mlt-7.38.0/000775 000000 000000 00000000000 15172202314 012362 5ustar00rootroot000000 000000 mlt-7.38.0/.clang-format000664 000000 000000 00000006265 15172202314 014746 0ustar00rootroot000000 000000 # .clang-format for MLT # # Adapted from https://github.com/qt-creator/qt-creator/blob/master/.clang-format # # The configuration below follows the Qt Creator Coding Rules [1] as closely as # possible. For documentation of the options, see [2]. # # # [1] https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html # [2] https://clang.llvm.org/docs/ClangFormatStyleOptions.html # --- AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: DontAlign AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false BinPackParameters: false BraceWrapping: AfterClass: true AfterControlStatement: Never AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: true AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakBeforeBinaryOperators: All BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeComma BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 100 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - forever # avoids { wrapped to next line - foreach - Q_FOREACH - BOOST_FOREACH IncludeCategories: - Regex: '^ /etc/sudoers.d/$USERNAME \ && chmod 0440 /etc/sudoers.d/$USERNAME # Environment setup ENV PATH="/usr/lib/qt6/bin:${PATH}" ENV MLT_REPOSITORY=/usr/local/lib/mlt-7 ENV CPATH=/usr/local/include/mlt-7:$CPATH ENV LIBRARY_PATH=/usr/local/lib:$LIBRARY_PATH ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH # Force mold linker for CMake builds ENV LDFLAGS="-fuse-ld=mold" # Headless audio driver for CI/Tests ENV SDL_AUDIODRIVER=dummy # FIX FOR JNI: Explicitly set JAVA_HOME for CMake FindJNI ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ENV CPATH="$JAVA_HOME/include:$JAVA_HOME/include/linux:$CPATH" USER $USERNAME WORKDIR /workspacesmlt-7.38.0/.devcontainer/devcontainer.json000664 000000 000000 00000002261 15172202314 020476 0ustar00rootroot000000 000000 { "name": "MLT Framework Dev 2026", "build": { "dockerfile": "Dockerfile", "args": { "USERNAME": "vscode" } }, "runArgs": [ "--device=/dev/dri:/dev/dri", "--group-add=video", "--group-add=render" // For audio and full hardware permissions, uncomment: // "--ipc=host", // "--privileged" ], "remoteUser": "vscode", "customizations": { "vscode": { "settings": { "terminal.integrated.defaultProfile.linux": "bash", "cmake.configureOnOpen": true, "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", "editor.formatOnSave": true, "cmake.buildDirectory": "${workspaceFolder}/build", "C_Cpp.clang_format_fallbackStyle": "Google" }, "extensions": [ "ms-vscode.cpptools", "ms-vscode.cmake-tools", "twxs.cmake", "xaver.clang-format", "ms-vscode.cpptools-extension-pack" ] } }, "postCreateCommand": "sudo chmod 666 /dev/dri/render* || true" }mlt-7.38.0/.github/000775 000000 000000 00000000000 15172202314 013722 5ustar00rootroot000000 000000 mlt-7.38.0/.github/ISSUE_TEMPLATE/000775 000000 000000 00000000000 15172202314 016105 5ustar00rootroot000000 000000 mlt-7.38.0/.github/ISSUE_TEMPLATE/bug_report.md000664 000000 000000 00000000776 15172202314 020611 0ustar00rootroot000000 000000 --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. **Expected behavior** A clear and concise description of what you expected to happen. **Desktop (please complete the following information):** - OS: [e.g. iOS] - OS Version [e.g. 22] - MLT/melt version: **Additional context** Add any other context about the problem here. mlt-7.38.0/.github/ISSUE_TEMPLATE/config.yml000664 000000 000000 00000000034 15172202314 020072 0ustar00rootroot000000 000000 blank_issues_enabled: false mlt-7.38.0/.github/copilot-instructions.md000664 000000 000000 00000020175 15172202314 020464 0ustar00rootroot000000 000000 # MLT Framework — Copilot Instructions MLT is a LGPL multimedia framework for video editing, written primarily in C with C++ for Qt-dependent modules. Version: 7.x (see `CMakeLists.txt`). Schema v7.0 metadata YAML files describe every plugin service. --- ## Build ```bash # Standard out-of-tree build # Configure (from repo root; output goes to build/cc-debug) cmake --preset cc-debug # Reformat code with clang-format cmake --build build/cc-debug --target clang-format # Build cmake --build build/cc-debug # Install (optional, default prefix is $HOME/opt) cmake --install build/cc-debug # Or install to system (also optional, requires sudo) cmake --install build/cc-debug --prefix /usr/local # With vcpkg preset (BUILD_TESTING=ON included) cmake --preset vcpkg # or vcpkg-ninja cmake --build build --parallel $(($(nproc) - 1)) ``` After building, source `setenv` from the repo root to configure runtime paths for the uninstalled build: ```bash ln -s build/cc-debug/out source setenv # bash only; sets MLT_REPOSITORY, LD_LIBRARY_PATH, etc. ``` Check formatting (CI enforces clang-format-14): ```bash cmake --build build/cc-debug --target clang-format-check # from build dir configured with -DCLANG_FORMAT=ON ``` ## Tests ```bash ln -s build/cc-debug/out source ../setenv cd build ctest --output-on-failure ``` Test suites live in `src/tests/` (one subdirectory per area: `test_filter`, `test_properties`, etc.). --- ## Architecture ``` src/ framework/ Pure-C core library: mlt_filter, mlt_frame, mlt_properties, mlt_producer, mlt_consumer, mlt_tractor, mlt_playlist, mlt_service modules/ Optional plugins (one shared library each, loaded at runtime) melt/ CLI tool mlt++/ C++ wrapper classes swig/ Language bindings (Python, etc.) tests/ Unit tests ``` Plugins are enabled at configure time via `MOD_*` CMake options (e.g. `MOD_QT6`, `MOD_PLUS`). --- ## Module / Plugin Conventions Each module in `src/modules//` contains: | File | Purpose | |------|---------| | `factory.c` | Registers all services with `mlt_register_*`; declares `extern` init functions | | `filter_.c/.cpp` | Filter implementation | | `filter_.yml` | Metadata descriptor (schema_version 7.0) | | `producer_.c/.yml`, `consumer_.c/.yml`, `transition_.c/.yml` | Same pattern for other service types | | `CMakeLists.txt` | Module-local build rules | **Language by module:** - `src/framework/` and most modules (core, avformat, plus majority) → pure C (`.c`) - Modules requiring Qt (`qt/`) → C++ (`.cpp`) - `plus/` is mixed: mostly C with a few `.cpp` files for subtitle/gradient helpers - `mlt++/` → C++ --- ## Filter Implementation Pattern ### Pixel-processing filter (push/pop) ```c static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); // read properties, call mlt_frame_get_image(), process pixels return mlt_frame_get_image(frame, image, format, width, height, writable); } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } ``` ### Delegating filter (pass-through to another filter) ```c static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_filter other = (mlt_filter) mlt_properties_get_data( MLT_FILTER_PROPERTIES(filter), "_other", NULL); mlt_properties text_props = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(other)); mlt_properties_pass_list(text_props, MLT_FILTER_PROPERTIES(filter), "key1 key2 key3"); return mlt_filter_process(other, frame); } ``` ### Init function ```c extern "C" // for .cpp files mlt_filter filter__init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { mlt_properties p = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_string(p, "key", "default"); mlt_properties_set_int(p, "_filter_private", 1); // suppress in serialization filter->process = filter_process; } return filter; } ``` --- ## Key API Reference | API | Use | |-----|-----| | `MLT_FILTER_PROPERTIES(f)` | Get `mlt_properties` from a filter | | `MLT_FILTER_SERVICE(f)` | Get `mlt_service` from a filter | | `MLT_FRAME_PROPERTIES(f)` | Get `mlt_properties` from a frame | | `mlt_properties_set/get` | String property access | | `mlt_properties_set_int/get_int` | Integer property access | | `mlt_properties_set_data/get_data` | Store owned C++ objects with destructor | | `mlt_properties_set_int64/get_int64` | 64-bit integer (e.g. timestamps) | | `mlt_properties_pass_list(dst, src, "k1 k2")` | Copy named properties between objects | | `mlt_properties_anim_get_double(p, "key", pos, len)` | Keyframed (animated) value | | `mlt_properties_exists(p, "key")` | Test presence before reading | | `mlt_frame_unique_properties(frame, service)` | Per-frame filter properties (thread-safe) | | `mlt_events_listen(p, owner, "property-changed", cb)` | React to property changes | | `mlt_slices_run_normal(n, proc, cookie)` | Multithreaded pixel loop | | `mlt_log_debug/info/error(service, fmt, ...)` | Structured logging | **Private properties** (not serialized to XML) must be prefixed with `_` (e.g. `"_subtitles"`, `"_mtime"`). --- ## YAML Metadata (.yml) Schema version: **7.0**. Validated against `src/framework/metaschema.yaml`. Required top-level fields: `schema_version`, `type`, `identifier`, `language`. Parameter `type` values: `string`, `integer`, `float`, `boolean`, `rect`, `color`, `binary`. Widget hints: `spinner`, `slider`, `combo`, `checkbox`, `color`, `text`. Add `animation: yes` for keyframeable parameters, `mutable: yes` for runtime-changeable ones. --- ## Naming Conventions - **Source files:** `filter_.c/.cpp`, `producer_.c`, `transition_.c`, `consumer_.c` - **Init function:** `filter__init(...)` — must be declared `extern "C"` in `.cpp` files - **Internal static functions:** `filter_process`, `filter_get_image`, `property_changed` — `snake_case` - **Structs:** `snake_case` - **C++ classes:** `PascalCase` (e.g. `Subtitles::SubtitleVector`) - **Private/internal properties:** prefix with `_` --- ## Code Style - 4-space indentation (no tabs) - Formatting enforced by `.clang-format` (derived from Qt Creator style) - Run `make clang-format-check` before committing; CI will fail on violations - `formatOnSave` is enabled in the dev container VS Code settings - Always update the copyright year in the header of any modified file --- ## Dev Container The repo includes `.devcontainer/` with a `devcontainer.json` and `Dockerfile`. - Default "Restricted Mode": audio routed to dummy driver, GPU via DRI passthrough only - For full hardware audio: uncomment `--privileged` / `--ipc=host` in `devcontainer.json` and set `SDL_AUDIODRIVER=alsa` or `pulseaudio` - After rebuilding: `Dev Containers: Rebuild Container` in VS Code --- ## Common Pitfalls - **Thread safety:** use `mlt_frame_unique_properties(frame, service)` when setting per-frame filter properties; never write directly to shared filter properties during `filter_get_image`. - **Missing `extern "C"`:** every `filter_*_init` in a `.cpp` file must be wrapped in `extern "C" { ... }` so the loader can find it. - **Property serialization:** properties starting with `_` are private and not written to XML. Use this for cached data, file modification times, sub-filter handles, and reset flags. - **`mlt_properties_pass_list` in `filter_process`:** pass properties to the sub-filter on `mlt_frame_unique_properties`, not on the sub-filter's own properties, to avoid data races. - **YAML `readonly`:** set `readonly: no` on any parameter the user is expected to set; omitting it defaults to read-only in some tooling. - **Delegating to `qtext` vs `text`:** `filter_subtitle` tries `qtext` first, then falls back to `text`. Properties specific to `qtext` (e.g. `typewriter.*`) are silently ignored by the `text` fallback. mlt-7.38.0/.github/instructions/000775 000000 000000 00000000000 15172202314 016466 5ustar00rootroot000000 000000 mlt-7.38.0/.github/instructions/mlt-modules.instructions.md000664 000000 000000 00000004054 15172202314 024020 0ustar00rootroot000000 000000 --- description: "Use when writing or editing MLT filter, producer, consumer, or transition plugins in src/modules/. Covers extern C for C++ init functions, private property naming, and thread-safe per-frame property access." applyTo: "src/modules/**" --- ## C++ init functions — mandatory `extern "C"` Every `filter__init` (or `producer_*`, `consumer_*`, `transition_*`) in a `.cpp` file **must** be wrapped: ```cpp extern "C" { mlt_filter filter__init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { ... } } // extern "C" ``` Without this, C++ name mangling prevents the dynamic loader from finding the symbol. The service silently fails to load — no error is logged. ## Private properties — `_` prefix Properties prefixed with `_` are **never serialized to XML**. Use this for: - Cached data (`"_subtitles"`, `"_mtime"`) - Owned C++ objects stored via `mlt_properties_set_data` - Sub-filter handles (`"_t"`) - One-shot reset flags (`"_reset"`) Do **not** prefix user-visible parameters with `_`. Do **not** list `_`-prefixed properties in `.yml` metadata files. ## Thread-safe per-frame state — `mlt_frame_unique_properties` `filter_get_image` runs on worker threads concurrently for different frames. Rules: - **Reading** from shared filter properties is safe. - **Writing** to shared filter properties is a data race — never do it. - Use `mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(filter))` to get a per-frame writable property bag. For delegating filters, pass properties to the **frame's** unique bag, not to the sub-filter's own properties: ```c // CORRECT — thread-safe mlt_properties fp = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(sub_filter)); mlt_properties_pass_list(fp, MLT_FILTER_PROPERTIES(filter), "key1 key2"); // WRONG — data race across concurrent frames mlt_properties_pass_list(MLT_FILTER_PROPERTIES(sub_filter), MLT_FILTER_PROPERTIES(filter), "key1 key2"); ``` mlt-7.38.0/.github/instructions/mlt-qt-module.instructions.md000664 000000 000000 00000010120 15172202314 024246 0ustar00rootroot000000 000000 --- description: "Use when writing or editing Qt module plugins in src/modules/qt/. Covers QApplication bootstrapping, QImage/QPainter pixel conversion, common.h utilities, Mlt C++ wrappers, animated property access, and Qt-version compatibility guards." applyTo: "src/modules/qt/**" --- ## Bootstrap: `createQApplicationIfNeeded` Every Qt filter init function must call `createQApplicationIfNeeded` before using any Qt class. If it returns `false`, close the filter and return `nullptr` — no display server is available. ```cpp #include "common.h" mlt_filter filter_foo_init(...) { mlt_filter filter = mlt_filter_new(); if (!filter) return nullptr; if (!createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_filter_close(filter); return nullptr; } // ... } ``` --- ## Image Format Selection: `choose_image_format` Use `choose_image_format` to respect the upstream format request (supports `mlt_image_rgba64`): ```cpp *image_format = choose_image_format(*image_format); int error = mlt_frame_get_image(frame, image, image_format, width, height, writable); ``` Never hard-code `mlt_image_rgba` in Qt filters — `rgba64` is supported and improves quality. --- ## MLT ↔ QImage Pixel Conversion Two helpers in `common.h` handle the buffer sharing between `mlt_frame` and `QImage`. The `QImage` wraps the existing MLT buffer — **no copy is made**: ```cpp // MLT buffer → QImage (wraps in-place, writable=1 required upstream) QImage qimg; convert_mlt_to_qimage(*image, &qimg, *width, *height, *image_format); // ... render into qimg with QPainter ... // QImage → MLT buffer (no-op: same pointer, just asserts) convert_qimage_to_mlt(&qimg, *image, *width, *height); ``` `convert_mlt_to_qimage` selects `QImage::Format_RGBA64` or `Format_RGBA8888` based on format. `convert_qimage_to_mlt` asserts that `*image == qimg.constBits()` — do not replace the buffer. --- ## Creating a Blank Frame: `create_image` For producer-style filters that generate content from scratch (no upstream image): ```cpp int error = create_image(frame, image, image_format, width, height, writable); // Returns a zeroed (transparent) mlt_pool_alloc'd buffer, registered on the frame. ``` This also reads `rescale_width`/`rescale_height` and `meta.media.width`/`meta.media.height` from the frame to choose the right dimensions. --- ## Mlt C++ Wrapper (`mlt++/`) Qt filters commonly use the `Mlt::Filter` and `Mlt::Frame` C++ wrappers for animated property access: ```cpp #include static int get_image(mlt_frame frame, ...) { auto filter = Mlt::Filter(mlt_filter(mlt_frame_pop_service(frame))); auto f = Mlt::Frame(frame); mlt_position pos = filter.get_position(f); mlt_position len = filter.get_length2(f); // Animated double (keyframeable) double radius = filter.anim_get_double("radius", pos, len); // Animated color — also convert TRC for display mlt_color color = filter.anim_get_color("color", pos, len); color = ::mlt_color_convert_trc(color, f.get("color_trc")); QColor qcolor(color.r, color.g, color.b, color.a); // Animated rect (geometry) mlt_rect rect = filter.anim_get_rect("geometry", pos, len); } ``` Use `Mlt::Properties` for convenient property access in init: ```cpp auto properties = Mlt::Properties(filter); properties.set("color", "#aa636363"); properties.set("radius", 5.0); ``` --- ## Thread Safety with QMutex Qt rendering (especially `QPainter`, `QTextDocument`) is not thread-safe. Protect with a file-scope mutex when the same static Qt object is accessed from multiple filter instances: ```cpp static QMutex g_mutex; static int get_image(mlt_frame frame, ...) { QMutexLocker locker(&g_mutex); // ... QPainter / QTextDocument work ... } ``` Use `QMutexLocker` (RAII) — never call `g_mutex.unlock()` manually. --- ## YAML Color Property Type For color parameters in Qt filter YAML, use `type: color` (not `type: string`) to enable the color picker widget and animation support: ```yaml - identifier: color title: Color type: color default: "#aa000000" mutable: yes widget: color animation: yes ``` mlt-7.38.0/.github/workflows/000775 000000 000000 00000000000 15172202314 015757 5ustar00rootroot000000 000000 mlt-7.38.0/.github/workflows/build-distros.yml000664 000000 000000 00000011675 15172202314 021300 0ustar00rootroot000000 000000 name: Test Building on Distros on: pull_request: branches: - master workflow_dispatch: inputs: target: description: 'Target distribution to build (or "all" for all targets)' required: false default: 'all' type: choice options: - all - ubuntu-24.04 - ubuntu-22.04 - debian-unstable - debian-testing - debian-stable - fedora-42 - fedora-38 jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: include: - name: ubuntu-24.04 image: ubuntu:24.04 setup_script: | sed -i 's/^Types: deb/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources DEBIAN_FRONTEND=noninteractive apt-get -qq update DEBIAN_FRONTEND=noninteractive apt-get -yqq build-dep mlt DEBIAN_FRONTEND=noninteractive apt-get -yqq install cmake qt6-base-dev libqt6svg6-dev - name: ubuntu-22.04 image: ubuntu:22.04 setup_script: | sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" DEBIAN_FRONTEND=noninteractive apt-get -qq update DEBIAN_FRONTEND=noninteractive apt-get -yqq build-dep mlt DEBIAN_FRONTEND=noninteractive apt-get -yqq install cmake qt6-base-dev libqt6svg6-dev libqt6core5compat6-dev - name: debian-unstable image: debian:unstable setup_script: | echo 'deb-src http://deb.debian.org/debian unstable main' >> /etc/apt/sources.list apt-get -qq update apt-get -yqq build-dep mlt - name: debian-testing image: debian:testing setup_script: | echo 'deb-src http://deb.debian.org/debian testing main' >> /etc/apt/sources.list apt-get -qq update apt-get -yqq build-dep mlt apt-get -yqq install cmake qt6-base-dev libqt6svg6-dev - name: debian-stable image: debian:stable setup_script: | echo 'deb-src http://deb.debian.org/debian stable main\ndeb-src http://deb.debian.org/debian stable-updates main' >> /etc/apt/sources.list echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list apt-get -qq update apt-get -yqq build-dep mlt apt-get -yqq install cmake qt6-base-dev libqt6svg6-dev - name: fedora-42 image: fedora:42 setup_script: | dnf --assumeyes group install development-tools dnf --assumeyes install \ yasm gavl-devel libsamplerate-devel libxml2-devel \ ladspa-devel jack-audio-connection-kit-devel sox-devel \ SDL2-devel fftw-devel frei0r-devel gtk2-devel libexif-devel \ qt6-qtbase-devel qt6-qtsvg-devel \ libtheora-devel libvorbis-devel libvdpau-devel \ libsoup-devel liboil-devel python-devel alsa-lib \ pulseaudio-libs-devel gcc-c++ cmake ffmpeg-free-devel \ movit-devel rubberband-devel vid.stab-devel - name: fedora-38 image: fedora:38 setup_script: | yum --assumeyes groupinstall "Development Tools" yum --assumeyes install \ yasm gavl-devel libsamplerate-devel libxml2-devel \ ladspa-devel jack-audio-connection-kit-devel sox-devel \ SDL2-devel fftw-devel frei0r-devel gtk2-devel libexif-devel \ qt6-qtbase-devel qt6-qtsvg-devel \ libtheora-devel libvorbis-devel libvdpau-devel \ libsoup-devel liboil-devel python-devel alsa-lib \ pulseaudio-libs-devel gcc-c++ cmake ffmpeg-free-devel \ movit-devel rubberband-devel vid.stab-devel container: image: ${{ matrix.image }} name: Build on ${{ matrix.name }} steps: - name: Check if job should run id: should_run run: | # For pull requests, always run all jobs # For workflow_dispatch, check the input parameter if [ "${{ github.event_name }}" = "pull_request" ]; then echo "run=true" >> $GITHUB_OUTPUT elif [ "${{ github.event.inputs.target }}" = "all" ] || [ "${{ github.event.inputs.target }}" = "${{ matrix.name }}" ]; then echo "run=true" >> $GITHUB_OUTPUT else echo "run=false" >> $GITHUB_OUTPUT fi - name: Checkout code if: steps.should_run.outputs.run == 'true' uses: actions/checkout@v4 - name: Setup dependencies if: steps.should_run.outputs.run == 'true' run: | ${{ matrix.setup_script }} - name: Build and install if: steps.should_run.outputs.run == 'true' run: | cmake -DCMAKE_BUILD_TYPE=Debug . && make -j -f Makefile install mlt-7.38.0/.github/workflows/build-docker.yml000664 000000 000000 00000001712 15172202314 021047 0ustar00rootroot000000 000000 name: build-docker on: workflow_dispatch: release: types: [published] jobs: build: if: ${{ github.repository_owner == 'mltframework' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Login to Docker Hub uses: docker/login-action@v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GitHub Packages uses: docker/login-action@v4 with: registry: docker.pkg.github.com username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v7 with: push: true tags: | mltframework/melt:${{ github.ref_name }} docker.pkg.github.com/mltframework/mlt/melt:${{ github.ref_name }} mlt-7.38.0/.github/workflows/build-linux.yml000664 000000 000000 00000003121 15172202314 020733 0ustar00rootroot000000 000000 name: build-test-linux-on-push on: [push, pull_request] jobs: python: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install flake8 - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics build-cmake: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Build C/C++ with CMake env: DEBIAN_FRONTEND: noninteractive run: | sudo sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" sudo apt-get -qq update sudo apt-get -yqq build-dep mlt sudo apt-get -yqq install qt6-base-dev libqt6svg6-dev libqt6core5compat6-dev sudo apt-get -yqq install cmake ninja-build kwalify cmake -D CMAKE_BUILD_TYPE=Debug -D BUILD_TESTING=ON -D SWIG_PYTHON=ON -S . -B build -G Ninja cmake --build build sudo cmake --install build - name: Run tests env: DEBIAN_FRONTEND: noninteractive LD_LIBRARY_PATH: /usr/local/lib run: | sudo locale-gen de_DE.UTF-8 cd build && ctest --output-on-failure mlt-7.38.0/.github/workflows/build-macos.yml000664 000000 000000 00000001645 15172202314 020707 0ustar00rootroot000000 000000 name: build-melt-macos on: workflow_dispatch: schedule: # nightly - cron: '0 14 * * *' jobs: build: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'mltframework' }} steps: - name: Download from S3 run: | VERSION=$(date +"%y%m%d") echo VERSION=$VERSION sudo apt install -yqq s3cmd s3cmd --access_key=${{ secrets.AWS_ACCESS_KEY }} --secret_key=${{ secrets.AWS_SECRET_KEY }} --stop-on-error get s3://builds.us.meltytech/melt-build-macos.txt s3cmd --access_key=${{ secrets.AWS_ACCESS_KEY }} --secret_key=${{ secrets.AWS_SECRET_KEY }} --stop-on-error get s3://builds.us.meltytech/melt/melt-macos-$VERSION.tar.bz2 - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: unsigned-tarball path: | *.tar.bz2 - name: Dump log run: cat melt-build-macos.txt mlt-7.38.0/.github/workflows/build-msys2-mingw64.yml000664 000000 000000 00000004763 15172202314 022157 0ustar00rootroot000000 000000 name: build-test-msys2-mingw64-on-push on: [push, pull_request, workflow_dispatch] jobs: build: if: ${{ github.repository_owner == 'mltframework' }} runs-on: windows-latest steps: - name: Setup MSYS2 uses: msys2/setup-msys2@v2 with: msystem: MINGW64 update: true install: >- autotools base-devel bzip2 coreutils git gzip tar xz zip perl-List-MoreUtils perl-XML-Parser mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-meson mingw-w64-x86_64-nasm mingw-w64-x86_64-ninja mingw-w64-x86_64-osslsigncode mingw-w64-x86_64-yasm mingw-w64-x86_64-cairo mingw-w64-x86_64-eigen3 mingw-w64-x86_64-dlfcn mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-fftw mingw-w64-x86_64-gcc-fortran mingw-w64-x86_64-gettext-tools mingw-w64-x86_64-harfbuzz mingw-w64-x86_64-lame mingw-w64-x86_64-libarchive mingw-w64-x86_64-libepoxy mingw-w64-x86_64-libexif mingw-w64-x86_64-libimagequant mingw-w64-x86_64-libsamplerate mingw-w64-x86_64-libtheora mingw-w64-x86_64-libvorbis mingw-w64-x86_64-libvpx mingw-w64-x86_64-libwebp mingw-w64-x86_64-libxml2 mingw-w64-x86_64-onevpl mingw-w64-x86_64-openblas mingw-w64-x86_64-openssl mingw-w64-x86_64-opus mingw-w64-x86_64-potrace mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt5-base mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-svg mingw-w64-x86_64-rubberband mingw-w64-x86_64-sox mingw-w64-x86_64-swig mingw-w64-x86_64-svt-av1 mingw-w64-x86_64-vid.stab mingw-w64-x86_64-x264 mingw-w64-x86_64-x265 mingw-w64-x86_64-zimg - uses: actions/checkout@v4 - name: Build C/C++ with CMake shell: msys2 {0} run: | cmake -D CMAKE_BUILD_TYPE=Debug -D MOD_MOVIT=OFF -D SWIG_PYTHON=ON -S . -B build -G Ninja cmake --build build cmake --install build - name: Run tests if: false # Temporarily disable tests due to missing kwalify shell: msys2 {0} run: | #locale-gen de_DE.UTF-8 cd build && ctest --output-on-failure mlt-7.38.0/.github/workflows/build-windows-msvc.yml000664 000000 000000 00000003064 15172202314 022242 0ustar00rootroot000000 000000 name: build-test-windows-msvc-on-push on: [push, pull_request] permissions: packages: write jobs: build-cmake-msvc: runs-on: windows-latest env: VCPKG_BINARY_SOURCES: "clear;nuget,https://nuget.pkg.github.com/mltframework/index.json,readwrite" steps: - uses: actions/checkout@v6 with: submodules: 'recursive' - name: Restore from cache and setup vcpkg executable and data files. uses: lukka/run-vcpkg@v11 with: vcpkgJsonGlob: 'vcpkg.json' - name: Add NuGet sources shell: pwsh env: USERNAME: mltframework VCPKG_EXE: ${{ env.VCPKG_ROOT }}/vcpkg FEED_URL: https://nuget.pkg.github.com/mltframework/index.json run: | .$(${{ env.VCPKG_EXE }} fetch nuget) ` sources add ` -Source "${{ env.FEED_URL }}" ` -StorePasswordInClearText ` -Name GitHubPackages ` -UserName "${{ env.USERNAME }}" ` -Password "${{ secrets.GITHUB_TOKEN }}" .$(${{ env.VCPKG_EXE }} fetch nuget) ` setapikey "${{ secrets.GITHUB_TOKEN }}" ` -Source "${{ env.FEED_URL }}" - name: Setup MSVC uses: ilammy/msvc-dev-cmd@v1 - name: Configure C/C++ with CMake run: | cmake --preset vcpkg-ninja - name: Build with Ninja run: | cmake --build build - name: Install MLT run: | cmake --install build - name: Run tests run: | ctest --test-dir build -C Debug mlt-7.38.0/.github/workflows/static-code-analysis.yml000664 000000 000000 00000003334 15172202314 022525 0ustar00rootroot000000 000000 name: static-code-analysis on: [push, pull_request] jobs: cppcheck: name: Cppcheck runs-on: ubuntu-latest env: extra-args: >- -i src/modules/decklink/darwin -i src/modules/decklink/linux -i src/modules/decklink/win -i src/modules/glaxnimate/glaxnimate/ -i src/modules/plus/ebur128/ -i src/modules/xml/common.c --include=src/framework/mlt_log.h --include=src/framework/mlt_types.h --library=cppcheck.cfg --suppress=ctuOneDefinitionRuleViolation --suppress=syntaxError:src/modules/xml/common.c steps: - uses: actions/checkout@v4 - name: Install Cppcheck run: sudo apt-get -qq -y install cppcheck - name: Run Cppcheck run: cppcheck src/ -j $(nproc) --force --inline-suppr --library=qt --error-exitcode=1 --template="::{severity} file={file},line={line},col={column}::{message}" ${{ env.extra-args }} clang-format: name: clang-format runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get -yqq install clang-format-14 - name: Run CMake run: | cmake -S . -B build -D CLANG_FORMAT=ON -D MOD_SDL2=OFF -D MOD_QT6=OFF -D MOD_AVFORMAT=OFF -D MOD_DECKLINK=OFF -D MOD_FREI0R=OFF -D MOD_GDK=OFF -D MOD_JACKRACK=OFF -D USE_LV2=OFF -D USE_VST2=OFF -D MOD_KDENLIVE=OFF -D MOD_NORMALIZE=OFF -D MOD_OLDFILM=OFF -D MOD_MOVIT=OFF -D MOD_PLUS=OFF -D MOD_PLUSGPL=OFF -D MOD_RESAMPLE=OFF -D MOD_RTAUDIO=OFF -D MOD_RUBBERBAND=OFF -D MOD_SOX=OFF -D MOD_VIDSTAB=OFF -D MOD_VORBIS=OFF -D MOD_XINE=OFF -D MOD_XML=OFF - name: Run clang-format run: | cd build make clang-format-check mlt-7.38.0/.gitignore000664 000000 000000 00000002102 15172202314 014345 0ustar00rootroot000000 000000 *.o *.so *.so.* *.dll *.def config.mak config.h config.log .depend *~ *.cxx demo/*.dv demo/*.mpeg demo/*.ogg demo/photos docs/html/* releases/* patches/* src/melt/melt src/modules/lumas/luma src/modules/lumas/NTSC/*.pgm src/modules/lumas/PAL/*.pgm src/modules/lumas/16_9/*.pgm src/modules/lumas/9_16/*.pgm src/modules/lumas/square/*.pgm src/modules/lumas/.executed src/swig/csharp/src_swig/* src/swig/perl/blib/* *.dylib src/swig/java/src_swig disable-* mlt.config mlt.creator mlt.files mlt.includes mlt-config *.pc packages.dat make.inc src/examples/play *.dot *.rej .project *.qmake.stash src/tests/Makefile src/tests/*/* !src/tests/*/*.cpp !src/tests/*/*.pro *CTestTestfile.cmake src/tests/test_*_autogen build* *cmake_install.cmake .ninja_* build.ninja rules.ninja CMakeCache.txt *CMakeFiles/ Mlt7Config*.cmake install_manifest.txt out/ src/modules/qt/mltqt_autogen/ src/modules/qt/mltqt6_autogen/ src/modules/glaxnimate/mltglaxnimate_autogen/ src/modules/glaxnimate/mltglaxnimate-qt6_autogen/ .qt/ ..clang-format.swp out src/swig/ruby/markdown/ CMakeLists.txt.user .cache/ *_export.h mlt-7.38.0/.gitmodules000664 000000 000000 00000000240 15172202314 014533 0ustar00rootroot000000 000000 [submodule "src/modules/glaxnimate/glaxnimate"] path = src/modules/glaxnimate/glaxnimate url = https://invent.kde.org/graphics/glaxnimate.git ignore = dirty mlt-7.38.0/AUTHORS000664 000000 000000 00000000767 15172202314 013444 0ustar00rootroot000000 000000 Charles Yates Dan Dennedy Stephane Fillod (effectv) Marco Gittler (frei0r, oldfilm, qimage/kdenlivetitle) Jean-Baptiste Mardelle (kdenlive, qimage) Zachary Drew (motion_est) Maksym Veremeyenko Brian Matherly Janne Liljeblad Steinar H. Gunderson Daniel F (gpstext, gpsgraphic) mr.fantastic (lv2, vst2) mlt-7.38.0/CMakeLists.txt000664 000000 000000 00000050257 15172202314 015133 0ustar00rootroot000000 000000 cmake_minimum_required(VERSION 3.14...3.31) project(MLT VERSION 7.38.0 DESCRIPTION "Multimedia Framework" HOMEPAGE_URL "https://www.mltframework.org" LANGUAGES C CXX ) # Enable compile_commands.json generation only for Debug builds to support LSP tools if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) endif() option(GPL "Enable GPLv2 components" ON) option(GPL3 "Enable GPLv3 components" ON) option(BUILD_TESTING "Enable tests" OFF) option(BUILD_DOCS "Enable Doxygen documentation" OFF) option(CLANG_FORMAT "Enable Clang Format" ON) option(BUILD_TESTS_WITH_QT6 "Build test against Qt 6" ON) option(MOD_AVFORMAT "Enable avformat module" ON) option(MOD_DECKLINK "Enable DeckLink module" ON) option(MOD_FREI0R "Enable Frei0r module" ON) option(MOD_GDK "Enable GDK module" ON) option(MOD_GLAXNIMATE_QT6 "Enable Glaxnimate module (Qt6)" OFF) option(MOD_JACKRACK "Enable JACK Rack module" ON) option(USE_LV2 "Enable LV2 features" ON) option(USE_VST2 "Enable LV2 features" ON) option(MOD_KDENLIVE "Enable Kdenlive module" ON) option(MOD_MOVIT "Enable OpenGL module" ON) option(MOD_NDI "Enable NDI module" OFF) option(MOD_NORMALIZE "Enable Normalize module (GPL)" ON) option(MOD_OLDFILM "Enable Oldfilm module" ON) option(MOD_OPENCV "Enable OpenCV module" OFF) option(MOD_OPENFX "Enable OpenFX module (GPL)" ON) option(MOD_PLUS "Enable Plus module" ON) option(MOD_PLUSGPL "Enable PlusGPL module (GPL)" ON) option(MOD_QT6 "Enable Qt6 module (GPL)" ON) option(MOD_RESAMPLE "Enable Resample module (GPL)" ON) option(MOD_RTAUDIO "Enable RtAudio module" ON) option(MOD_RUBBERBAND "Enable Rubberband module (GPL)" ON) option(MOD_SDL1 "Enable SDL1 module" OFF) option(MOD_SDL2 "Enable SDL2 module" ON) option(MOD_SOX "Enable SoX module" ON) option(MOD_SPATIALAUDIO "Enable SpatialAudio module" OFF) option(MOD_VIDSTAB "Enable vid.stab module (GPL)" ON) option(MOD_VORBIS "Enable Vorbis module" ON) option(MOD_XINE "Enable XINE module (GPL)" ON) option(MOD_XML "Enable XML module" ON) option(SWIG_CSHARP "Enable SWIG C# bindings" OFF) option(SWIG_JAVA "Enable SWIG Java bindings" OFF) option(SWIG_LUA "Enable SWIG Lua bindings" OFF) option(SWIG_NODEJS "Enable SWIG Node.js bindings" OFF) option(SWIG_PERL "Enable SWIG Perl bindings" OFF) option(SWIG_PHP "Enable SWIG PHP bindings" OFF) option(SWIG_PYTHON "Enable SWIG Python bindings" OFF) option(SWIG_RUBY "Enable SWIG Ruby bindings" OFF) option(SWIG_TCL "Enable SWIG Tcl bindings" OFF) if(WIN32) option(WINDOWS_DEPLOY "Install exes/libs directly to prefix (no subdir /bin)" ON) else() if(APPLE) option(RELOCATABLE "Use standard app bundle layout" ON) else() option(RELOCATABLE "Look for plugins relative to app" OFF) endif() endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(FeatureSummary) include(GNUInstallDirs) if(WINDOWS_DEPLOY) set(CMAKE_INSTALL_BINDIR .) endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/${CMAKE_INSTALL_BINDIR}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/lib") set(MLT_MODULE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/lib/mlt") set(MLT_DATA_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/share/mlt") # https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) if(NOT "${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "") endif() if(NOT EXISTS ${MLT_DATA_OUTPUT_DIRECTORY}) if(WIN32) # symlinks require admin rights on Windows file(COPY "${CMAKE_SOURCE_DIR}/src/modules" DESTINATION "${CMAKE_BINARY_DIR}/out/share" FILES_MATCHING REGEX yml|txt) file(RENAME "${CMAKE_BINARY_DIR}/out/share/modules" "${MLT_DATA_OUTPUT_DIRECTORY}") file(COPY "${CMAKE_SOURCE_DIR}/presets" DESTINATION "${MLT_DATA_OUTPUT_DIRECTORY}") file(COPY "${CMAKE_SOURCE_DIR}/profiles" DESTINATION "${MLT_DATA_OUTPUT_DIRECTORY}") else() file(MAKE_DIRECTORY "${MLT_DATA_OUTPUT_DIRECTORY}") file(GLOB MOD_SUBDIRS "${CMAKE_SOURCE_DIR}/src/modules/*") foreach(MOD_SUBDIR ${MOD_SUBDIRS}) file(RELATIVE_PATH MOD_NAME "${CMAKE_SOURCE_DIR}/src/modules" ${MOD_SUBDIR}) file(CREATE_LINK "${CMAKE_SOURCE_DIR}/src/modules/${MOD_NAME}" "${MLT_DATA_OUTPUT_DIRECTORY}/${MOD_NAME}" SYMBOLIC) endforeach() file(CREATE_LINK "${CMAKE_SOURCE_DIR}/presets" "${MLT_DATA_OUTPUT_DIRECTORY}/presets" SYMBOLIC) file(CREATE_LINK "${CMAKE_SOURCE_DIR}/profiles" "${MLT_DATA_OUTPUT_DIRECTORY}/profiles" SYMBOLIC) endif() endif() set(MLT_SUBDIR mlt) if(NOT (WIN32 OR APPLE)) set(MLT_SUBDIR mlt-${MLT_VERSION_MAJOR}) set(MLT_SUBDIR mlt-${MLT_VERSION_MAJOR}) endif() set(MLT_INSTALL_MODULE_DIR ${CMAKE_INSTALL_LIBDIR}/${MLT_SUBDIR}) set(MLT_INSTALL_DATA_DIR ${CMAKE_INSTALL_DATADIR}/${MLT_SUBDIR}) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) list(APPEND MLT_COMPILE_OPTIONS "") # MSVC cl doesn't support GNU inline assembly (but MSVC-compatible clang-cl does) if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") if(CMAKE_SYSTEM_PROCESSOR MATCHES "i686|x86|x86_64|AMD64") set(CPU_MMX ON) set(CPU_SSE ON) set(CPU_SSE2 ON) if(NOT MSVC) # also NOT clang-cl list(APPEND MLT_COMPILE_OPTIONS "-mmmx;-msse;-msse2") endif() endif() if(CMAKE_SYSTEM_PROCESSOR MATCHES "i686" OR (WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86")) set(CPU_X86_32 ON) endif() if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64") set(CPU_X86_64 ON) endif() endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_BUILD_TYPE STREQUAL "Debug") # Treat warnings as errors with some exceptions set(GCC_FLAGS "-Wall -Werror -Wno-deprecated-declarations -Werror=pointer-arith") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_FLAGS} -Wno-class-memaccess -Wno-array-compare -Wno-unused-result -Wno-maybe-uninitialized") if(SWIG_RUBY) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-enum-enum-conversion -Wno-volatile") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_FLAGS} -Wno-discarded-qualifiers") # The following showed up in GCC 14, which I am using via msys2 on Windows. # Remove the following when Glaxnimate is fixed. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++20-compat -Wno-dangling-reference") # The following showed up in GCC 13.3. # Remove the following when spatialaudio is fixed. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-overloaded-virtual") endif () if(MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /experimental:c11atomics") # Enable math defines for M_PI and M_PI_2 add_compile_definitions(_USE_MATH_DEFINES) endif() if(MSVC) list(APPEND MLT_COMPILE_OPTIONS "$<$:/fp:fast>") elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") list(APPEND MLT_COMPILE_OPTIONS "$<$:-ffast-math>") endif() if(NOT GPL) set(MOD_NORMALIZE OFF) set(MOD_OPENFX OFF) set(MOD_PLUSGPL OFF) set(MOD_QT6 OFF) set(MOD_RESAMPLE OFF) set(MOD_RUBBERBAND OFF) set(MOD_VIDSTAB OFF) set(MOD_XINE OFF) endif() if (MSVC) find_package(PThreads4W REQUIRED) find_package(Dirent 1.26 REQUIRED) endif () find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) if(WIN32) find_package(Iconv REQUIRED) if(NOT CMAKE_DL_LIBS) find_package(dlfcn-win32 REQUIRED) set(CMAKE_DL_LIBS dlfcn-win32::dl) endif() if(MINGW) string(REPLACE "iconv" "pthread" MLT_PTHREAD_LIBS "${Iconv_LIBRARY}") endif() endif() find_package(SDL2) if(MOD_QT6 OR MOD_PLUS) find_package(FFTW3) endif() if(MOD_QT6 OR MOD_GDK) pkg_check_modules(libexif IMPORTED_TARGET libexif) endif() if(MOD_GDK OR MOD_GTK) pkg_check_modules(fontconfig IMPORTED_TARGET fontconfig) endif() if(MOD_AVFORMAT) find_package(FFmpeg REQUIRED COMPONENTS AVFORMAT SWSCALE AVUTIL AVCODEC AVFILTER AVDEVICE SWRESAMPLE ) list(APPEND MLT_SUPPORTED_COMPONENTS avformat) endif() if(MOD_DECKLINK) list(APPEND MLT_SUPPORTED_COMPONENTS decklink) endif() if(MOD_FREI0R) pkg_check_modules(FREI0R REQUIRED frei0r) list(APPEND MLT_SUPPORTED_COMPONENTS frei0r) endif() if(MOD_GDK) pkg_check_modules(GdkPixbuf REQUIRED IMPORTED_TARGET gdk-pixbuf-2.0) # Optional deps pkg_check_modules(pango IMPORTED_TARGET pango) pkg_check_modules(pangoft2 IMPORTED_TARGET pangoft2) list(APPEND MLT_SUPPORTED_COMPONENTS gdk) endif() if(MOD_JACKRACK) find_package(JACK) pkg_check_modules(glib IMPORTED_TARGET glib-2.0) pkg_check_modules(xml IMPORTED_TARGET libxml-2.0) check_include_file(ladspa.h ladspa_h_FOUND) if(NOT ladspa_h_FOUND) set(USE_LV2 OFF) set(USE_VST2 OFF) endif() list(APPEND MLT_SUPPORTED_COMPONENTS jackrack) endif() if(USE_LV2) pkg_check_modules(lilv IMPORTED_TARGET lilv-0) if(NOT lilv_FOUND) set(USE_LV2 OFF) endif() endif() if(MOD_KDENLIVE) list(APPEND MLT_SUPPORTED_COMPONENTS kdenlive) endif() if(MOD_MOVIT) pkg_check_modules(movit REQUIRED IMPORTED_TARGET movit) find_package(OpenGL REQUIRED) if(UNIX AND NOT APPLE) find_package(X11 REQUIRED) endif() list(APPEND MLT_SUPPORTED_COMPONENTS movit) endif() if(MOD_NDI) find_package(NDI REQUIRED) list(APPEND MLT_SUPPORTED_COMPONENTS ndi) endif() if(MOD_NORMALIZE) list(APPEND MLT_SUPPORTED_COMPONENTS normalize) endif() if(MOD_OLDFILM) list(APPEND MLT_SUPPORTED_COMPONENTS oldfilm) endif() if(MOD_OPENCV) find_package(OpenCV REQUIRED COMPONENTS tracking) list(APPEND MLT_SUPPORTED_COMPONENTS opencv) endif() if(MOD_OPENFX) pkg_check_modules(glib IMPORTED_TARGET glib-2.0) endif() if(MOD_PLUS) pkg_check_modules(libebur128 IMPORTED_TARGET libebur128) list(APPEND MLT_SUPPORTED_COMPONENTS plus) endif() if(MOD_PLUSGPL) list(APPEND MLT_SUPPORTED_COMPONENTS plusgpl) endif() # It is necessary to look for Qt6 before Qt5, otherwise there will # be a conflict with the targets in case both are enabled if(MOD_QT6) find_package(Qt6 REQUIRED COMPONENTS Core Gui Xml SvgWidgets) if(Qt6_VERSION VERSION_LESS 6.4.0) find_package(Qt6 REQUIRED COMPONENTS Core5Compat) endif() list(APPEND MLT_SUPPORTED_COMPONENTS qt6) endif() if(MOD_GLAXNIMATE_QT6) find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets Xml) list(APPEND MLT_SUPPORTED_COMPONENTS glaxnimate-qt6) endif() if (BUILD_TESTS_WITH_QT6) set(QT_MAJOR_VERSION 6) else() set(QT_MAJOR_VERSION 5) endif() if(BUILD_TESTING) find_package(Qt${QT_MAJOR_VERSION} REQUIRED COMPONENTS Core Test) if(NOT MSVC) # Kwalify is not available for MSVC easily (eg. via vcpkg) find_package(Kwalify REQUIRED) endif() enable_testing() endif() if(MOD_RESAMPLE) pkg_check_modules(samplerate REQUIRED IMPORTED_TARGET samplerate) list(APPEND MLT_SUPPORTED_COMPONENTS resample) endif() if(MOD_RTAUDIO) pkg_check_modules(rtaudio IMPORTED_TARGET rtaudio) if(NOT TARGET PkgConfig::rtaudio AND UNIX AND NOT APPLE) pkg_check_modules(alsa IMPORTED_TARGET alsa) pkg_check_modules(libpulse-simple IMPORTED_TARGET libpulse-simple) endif() list(APPEND MLT_SUPPORTED_COMPONENTS rtaudio) endif() if(MOD_RUBBERBAND) pkg_check_modules(rubberband REQUIRED IMPORTED_TARGET rubberband) list(APPEND MLT_SUPPORTED_COMPONENTS rubberband) endif() if(MOD_SDL1) pkg_check_modules(sdl REQUIRED IMPORTED_TARGET sdl) list(APPEND MLT_SUPPORTED_COMPONENTS sdl) endif() if(MOD_SDL2) find_package(SDL2 REQUIRED) list(APPEND MLT_SUPPORTED_COMPONENTS sdl2) endif() if(MOD_SOX) pkg_check_modules(sox REQUIRED IMPORTED_TARGET sox) list(APPEND MLT_SUPPORTED_COMPONENTS sox) endif() if(MOD_SPATIALAUDIO) pkg_check_modules(spatialaudio REQUIRED IMPORTED_TARGET spatialaudio) list(APPEND MLT_SUPPORTED_COMPONENTS spatialaudio) endif() if(MOD_VIDSTAB) pkg_check_modules(vidstab REQUIRED IMPORTED_TARGET vidstab) list(APPEND MLT_SUPPORTED_COMPONENTS vidstab) endif() if(MOD_VORBIS) pkg_check_modules(vorbis REQUIRED IMPORTED_TARGET vorbis) pkg_check_modules(vorbisfile REQUIRED IMPORTED_TARGET vorbisfile) list(APPEND MLT_SUPPORTED_COMPONENTS vorbis) endif() if(MOD_XINE) list(APPEND MLT_SUPPORTED_COMPONENTS xine) endif() if(MOD_XML) pkg_check_modules(xml REQUIRED IMPORTED_TARGET libxml-2.0) list(APPEND MLT_SUPPORTED_COMPONENTS xml) endif() find_package(SWIG) if(SWIG_CSHARP) find_package(Mono REQUIRED) endif() if(SWIG_JAVA) find_package(JNI REQUIRED) endif() if(SWIG_LUA) find_package(Lua REQUIRED) endif() if(SWIG_NODEJS) find_package(Node REQUIRED) if(NODE_VERSION_MAJOR VERSION_GREATER_EQUAL 12 AND SWIG_VERSION VERSION_LESS 4.1) # https://github.com/swig/swig/issues/1520 set(SWIG_NODEJS OFF) endif() endif() if(SWIG_PERL) find_package(PerlLibs REQUIRED) endif() if(SWIG_PHP) find_package(PHP REQUIRED) endif() if(SWIG_PYTHON) find_package(Python3 REQUIRED COMPONENTS Interpreter Development) endif() if(SWIG_RUBY) find_package(Ruby REQUIRED) endif() if(SWIG_TCL) find_package(TCL REQUIRED) endif() if(BUILD_DOCS) find_package(Doxygen REQUIRED) set(DOXYGEN_OUTPUT_DIRECTORY "docs") set(DOXYGEN_ABBREVIATE_BRIEF "") set(DOXYGEN_STRIP_FROM_PATH "src") set(DOXYGEN_JAVADOC_AUTOBRIEF "YES") set(DOXYGEN_QT_AUTOBRIEF "YES") set(DOXYGEN_ALIASES "properties=\\xrefitem properties \\\"Property\\\" \\\"Properties Dictionary\\\"") list(APPEND DOXYGEN_ALIASES "event=\\xrefitem event \\\"Event\\\" \\\"Events Dictionary\\\"") list(APPEND DOXYGEN_ALIASES "envvar=\\xrefitem envvars \\\"Environment Variable\\\" \\\"Environment Variables\\\"") set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C "YES") set(DOXYGEN_SORT_BRIEF_DOCS "YES") set(DOXYGEN_EXTRACT_ALL "YES") set(DOXYGEN_EXTRACT_PRIVATE "YES") set(DOXYGEN_EXTRACT_STATIC "YES") set(DOXYGEN_FILE_PATTERNS "") set(DOXYGEN_RECURSIVE "NO") set(DOXYGEN_EXCLUDE_PATTERNS "") set(DOXYGEN_EXAMPLE_PATTERNS "") set(DOXYGEN_SEARCHENGINE "NO") set(DOXYGEN_INCLUDE_GRAPH "NO") set(DOXYGEN_EXTRA_SOURCES "") if(SWIG_PYTHON AND Python3_FOUND) list(APPEND DOXYGEN_EXTRA_SOURCES "${CMAKE_BINARY_DIR}/src/swig/python/mlt.py") list(APPEND DOXYGEN_EXTRA_SOURCES "${CMAKE_SOURCE_DIR}/src/swig/python/python_bindings.dox") endif() doxygen_add_docs(docs src/framework src/mlt++ ${DOXYGEN_EXTRA_SOURCES}) endif() if(CLANG_FORMAT) # Formatting may change with different versions of clang-format. # Test new versions before changing the allowed version here to avoid # accidental broad changes to formatting. find_package(ClangFormat 14 EXACT) if(CLANGFORMAT_FOUND) file(GLOB_RECURSE FORMAT_FILES "src/*.h" "src/*.c" "src/*.cpp") # exclude 3rd party source from format checking list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/glaxnimate/glaxnimate/") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*autogen") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/avformat/mmx.h") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/decklink/darwin") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/decklink/linux") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/decklink/win") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/gdk/pixops") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/jackrack/jack_rack.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/jackrack/lock_free_fifo.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/plugin_desc.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/plugin_mgr.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/plugin_settings.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/plugin.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/process.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/plus/ebur128") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/movit/optional_effect.h") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/opencv/filter_opencv_tracker.cpp") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/openfx/openfx") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/plusgpl/cJSON") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/plusgpl/image.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/plusgpl/utils.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/rtaudio/RtAudio.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/xine/attributes.h") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/xine/cpu_accel.c") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/xine/deinterlace.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/xine/vf_yadif_template.h") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/xine/xineutils.h") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/xine/yadif.*") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/win32") list(FILTER FORMAT_FILES EXCLUDE REGEX "/.*/msvc/libgen") add_custom_target(clang-format COMMAND ${CLANGFORMAT_EXECUTABLE} -style=file -i ${FORMAT_FILES} ) add_custom_target(clang-format-check COMMAND ${CLANGFORMAT_EXECUTABLE} --dry-run --Werror -style=file -i ${FORMAT_FILES} ) else() set(CLANG_FORMAT OFF) endif() endif() install(DIRECTORY presets profiles DESTINATION ${MLT_INSTALL_DATA_DIR}) if(UNIX AND NOT APPLE) install(FILES docs/melt.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 RENAME melt-${MLT_VERSION_MAJOR}.1) install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink melt-${MLT_VERSION_MAJOR}.1 melt.1 \ WORKING_DIRECTORY ${CMAKE_INSTALL_FULL_MANDIR}/man1)" ) endif() add_subdirectory(src) install(EXPORT MltTargets FILE Mlt${MLT_VERSION_MAJOR}Targets.cmake NAMESPACE Mlt${MLT_VERSION_MAJOR}:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Mlt${MLT_VERSION_MAJOR} ) include(CMakePackageConfigHelpers) configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/MltConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/Mlt${MLT_VERSION_MAJOR}Config.cmake" INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Mlt${MLT_VERSION_MAJOR} NO_CHECK_REQUIRED_COMPONENTS_MACRO ) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/Mlt${MLT_VERSION_MAJOR}ConfigVersion.cmake" COMPATIBILITY SameMajorVersion ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Mlt${MLT_VERSION_MAJOR}Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Mlt${MLT_VERSION_MAJOR}ConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Mlt${MLT_VERSION_MAJOR} ) add_feature_info("GPLv2" GPL "") add_feature_info("GPLv3" GPL3 "") add_feature_info("Tests" BUILD_TESTING "") add_feature_info("Doxygen" BUILD_DOCS "") add_feature_info("Clang Format" CLANG_FORMAT "") add_feature_info("Module: avformat" MOD_AVFORMAT "") add_feature_info("Module: DeckLink" MOD_DECKLINK "") add_feature_info("Module: Frei0r" MOD_FREI0R "") add_feature_info("Module: GDK" MOD_GDK "") add_feature_info("Module: Glaxnimate (Qt6)" MOD_GLAXNIMATE_QT6 "") add_feature_info("Module: JACKRack" MOD_JACKRACK "") add_feature_info("Module: Kdenlive" MOD_KDENLIVE "") add_feature_info("Module: Movit" MOD_MOVIT "") add_feature_info("Module: NDI" MOD_NDI "") add_feature_info("Module: Normalize" MOD_NORMALIZE "") add_feature_info("Module: Oldfilm" MOD_OLDFILM "") add_feature_info("Module: OpenCV" MOD_OPENCV "") add_feature_info("Module: OpenFX" MOD_OPENFX "") add_feature_info("Module: Plus" MOD_PLUS "") add_feature_info("Module: PlusGPL" MOD_PLUSGPL "") add_feature_info("Module: Qt6" MOD_QT6 "") add_feature_info("Module: Resample" MOD_RESAMPLE "") add_feature_info("Module: RtAudio" MOD_RTAUDIO "") add_feature_info("Module: Rubberband" MOD_RUBBERBAND "") add_feature_info("Module: SDL1" MOD_SDL1 "") add_feature_info("Module: SDL2" MOD_SDL2 "") add_feature_info("Module: SoX" MOD_SOX "") add_feature_info("Module: SpatialAudio" MOD_SPATIALAUDIO "") add_feature_info("Module: vid.stab" MOD_VIDSTAB "") add_feature_info("Module: Vorbis" MOD_VORBIS "") add_feature_info("Module: XINE" MOD_XINE "") add_feature_info("Module: XML" MOD_XML "") add_feature_info("SWIG: C#" SWIG_CSHARP "") add_feature_info("SWIG: Java" SWIG_JAVA "") add_feature_info("SWIG: Lua" SWIG_LUA "") add_feature_info("SWIG: Node.js" SWIG_NODEJS "") add_feature_info("SWIG: Perl" SWIG_PERL "") add_feature_info("SWIG: PHP" SWIG_PHP "") add_feature_info("SWIG: Python" SWIG_PYTHON "") add_feature_info("SWIG: Ruby" SWIG_RUBY "") add_feature_info("SWIG: Tcl" SWIG_TCL "") add_feature_info("lv2: LV2 Plugins support" USE_LV2 "") add_feature_info("vst2: VST2 Plugins support" USE_VST2 "") feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) mlt-7.38.0/CMakePresets.json000664 000000 000000 00000013607 15172202314 015612 0ustar00rootroot000000 000000 { "version": 6, "configurePresets": [ { "name": "vcpkg", "displayName": "vcpkg config", "description": "Configure with vcpkg toolchain", "binaryDir": "${sourceDir}/build", "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "cacheVariables": { "BUILD_TESTING": "ON", "SWIG_PYTHON": "OFF", "MOD_AVFORMAT": "ON", "MOD_DECKLINK": "OFF", "MOD_FREI0R": "OFF", "MOD_GDK": "ON", "MOD_JACKRACK": "OFF", "USE_VST2": "OFF", "USE_LV2": "OFF", "MOD_KDENLIVE": "ON", "MOD_NDI": "OFF", "MOD_NORMALIZE": "ON", "MOD_OLDFILM": "ON", "MOD_OPENCV": "ON", "MOD_OPENFX": "OFF", "MOD_MOVIT": "OFF", "MOD_PLUS": "ON", "MOD_PLUSGPL": "OFF", "MOD_QT6": "ON", "MOD_SOX": "OFF", "MOD_GLAXNIMATE_QT6": "ON", "MOD_RESAMPLE": "OFF", "MOD_RTAUDIO": "OFF", "MOD_RUBBERBAND": "ON", "MOD_SDL2": "ON", "MOD_SPATIALAUDIO": "OFF", "MOD_VIDSTAB": "OFF", "MOD_VORBIS": "ON", "MOD_XINE": "OFF", "MOD_XML": "ON" } }, { "name": "vcpkg-ninja", "inherits": "vcpkg", "generator": "Ninja" }, { "name": "_cc-debug-base", "hidden": true, "generator": "Ninja", "environment": { "QT_VERSION": "6.8.3", "PATH": "$env{HOME}/Qt/$env{QT_VERSION}/macos/bin:$env{HOME}/opt/bin:/usr/local/bin:/opt/local/bin:$penv{PATH}", "PKG_CONFIG_PATH": "$env{HOME}/.local/lib/pkgconfig:$env{HOME}/.local/lib64/pkgconfig:$env{HOME}/opt/lib/pkgconfig:$env{HOME}/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/lib64/pkgconfig:$penv{PKG_CONFIG_PATH}" }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_C_FLAGS": "-I/usr/local/include", "CMAKE_INSTALL_PREFIX": "$env{HOME}/opt", "CMAKE_PREFIX_PATH": "$env{HOME}/Qt/$env{QT_VERSION}/gcc_64;$env{HOME}/Qt/$env{QT_VERSION}/macos;$env{HOME}/.local;$env{HOME}/opt;/usr/local;/opt/local", "MOD_SDL1": "OFF", "RELOCATABLE": "OFF" } }, { "name": "cc-debug-linux", "displayName": "default system compiler (debug)", "binaryDir": "${sourceDir}/build/${presetName}", "inherits": "_cc-debug-base", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" }, "cacheVariables": { "BUILD_DOCS": "ON", "BUILD_TESTING": "ON", "SWIG_PYTHON": "ON" } }, { "name": "cc-debug-macos", "displayName": "default system compiler (debug)", "binaryDir": "${sourceDir}/build/${presetName}", "inherits": "_cc-debug-base", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" }, "cacheVariables": { "BUILD_TESTING": "OFF", "MOD_GDK": "OFF" } }, { "name": "msys2-debug", "displayName": "msys2 (debug)", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { "BUILD_TESTING": "OFF", "CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}", "CMAKE_BUILD_TYPE": "Debug", "CMAKE_PREFIX_PATH": "C:/Qt/$env{QT_VERSION}/mingw_64;C:/msys64/mingw64;C:/msys64/clangarm64", "MOD_GLAXNIMATE_QT6": "ON", "MOD_MOVIT": "OFF" }, "environment": { "PKG_CONFIG_PATH": "C:/Projects/Shotcut/lib/pkgconfig;C:/msys64/mingw64/lib/pkgconfig;C:/msys64/clangarm64/lib/pkgconfig;$penv{PKG_CONFIG_PATH}", "Path": "C:/msys64/usr/local/bin;C:/msys64/mingw64/bin;C:/msys64/clangarm64/bin;$penv{Path}", "QT_VERSION": "6.8.3" }, "generator": "Ninja" } ], "buildPresets": [ { "name": "clang-format-linux", "configurePreset": "cc-debug-linux", "targets": ["clang-format"] }, { "name": "install-linux", "configurePreset": "cc-debug-linux", "targets": ["install"] }, { "name": "clang-format-macos", "configurePreset": "cc-debug-macos", "targets": ["clang-format"] }, { "name": "install-macos", "configurePreset": "cc-debug-macos", "targets": ["install"] } ], "workflowPresets": [ { "name": "clang-format_install-linux", "displayName": "clang-format, build, install (Linux)", "steps": [ { "type": "configure", "name": "cc-debug-linux" }, { "type": "build", "name": "clang-format-linux" }, { "type": "build", "name": "install-linux" } ] }, { "name": "clang-format_install-macos", "displayName": "clang-format, build, install (macOS)", "steps": [ { "type": "configure", "name": "cc-debug-macos" }, { "type": "build", "name": "clang-format-macos" }, { "type": "build", "name": "install-macos" } ] } ] }mlt-7.38.0/COPYING000664 000000 000000 00000063631 15172202314 013426 0ustar00rootroot000000 000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! mlt-7.38.0/Dockerfile000664 000000 000000 00000003761 15172202314 014363 0ustar00rootroot000000 000000 FROM ubuntu:24.04 AS base ENV DEBIAN_FRONTEND noninteractive ENV HOME /tmp RUN apt-get update -qq && apt-get install -yqq apt-utils FROM base AS build # Install packages for building RUN apt-get install -yqq wget git automake autoconf libtool intltool g++ yasm nasm \ swig libgavl-dev libsamplerate0-dev libxml2-dev ladspa-sdk libjack-dev \ libsox-dev libsdl2-dev libgtk2.0-dev libsoup2.4-dev \ qt6-base-dev qt6-svg-dev libarchive-dev libmp3lame-dev \ libexif-dev libtheora-dev libvorbis-dev python3-dev cmake xutils-dev \ libegl1-mesa-dev libeigen3-dev libfftw3-dev libvdpau-dev meson ninja-build # Get and run the build script RUN wget --quiet -O /tmp/build-melt.sh https://raw.githubusercontent.com/mltframework/mlt-scripts/master/build/build-melt.sh && \ echo "INSTALL_DIR=\"/usr/local\"" > /tmp/build-melt.conf && \ echo "SOURCE_DIR=\"/tmp/melt\"" >> /tmp/build-melt.conf && \ echo "AUTO_APPEND_DATE=0" >> /tmp/build-melt.conf && \ bash /tmp/build-melt.sh -c /tmp/build-melt.conf FROM base # Install packages for running RUN apt-get install -yqq dumb-init \ libsamplerate0 libxml2 libjack0 \ libsdl2-2.0-0 libgtk2.0-0 libsoup2.4-1 \ libqt6svgwidgets6 libqt6xml6 qt6-image-formats-plugins libarchive13 \ libtheora0 libvorbis0a python3 \ libegl1 libfftw3-double3 libvdpau1 \ # Additional runtime libs \ libgavl2 libsox3 libexif12 xvfb libxkbcommon-x11-0 libhyphen0 libwebp7 \ # LADSPA plugins \ amb-plugins ambdec autotalent blepvco blop bs2b-ladspa caps cmt \ csladspa fil-plugins invada-studio-plugins-ladspa mcp-plugins \ omins rev-plugins ste-plugins swh-plugins tap-plugins vco-plugins wah-plugins \ lsp-plugins-ladspa dpf-plugins-ladspa \ # Fonts \ fonts-liberation 'ttf-.+' # Install the build COPY --from=build /usr/local/ /usr/local/ WORKDIR /mnt ENV LD_LIBRARY_PATH /usr/local/lib # Qt, Movit, and WebVfx require xvfb-run, which requires a PID 1 init provided by dumb-init ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/bin/xvfb-run", "-a", "/usr/local/bin/melt"] mlt-7.38.0/Doxyfile000664 000000 000000 00000166355 15172202314 014110 0ustar00rootroot000000 000000 # Doxyfile 1.5.7 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = MLT # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 7.38.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = docs # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, # Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = src/ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = YES # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = "properties=\xrefitem properties \"Property\" \"Properties Dictionary\"" ALIASES += "event=\xrefitem event \"Event\" \"Events Dictionary\"" ALIASES += "envvar=\xrefitem envvars \"Environment Variable\" \"Environment Variables\"" # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST = YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = src/framework \ src/mlt++ \ src/swig/python/mlt.py # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # Qt Help Project / Namespace. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # Qt Help Project / Virtual Folders. QHP_VIRTUAL_FOLDER = doc # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file . QHG_LOCATION = # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to FRAME, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. Other possible values # for this tag are: HIERARCHIES, which will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list; # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which # disables this behavior completely. For backwards compatibility with previous # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE # respectively. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO mlt-7.38.0/GPL000664 000000 000000 00000043254 15172202314 012737 0ustar00rootroot000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. mlt-7.38.0/GPLv3000664 000000 000000 00000104513 15172202314 013204 0ustar00rootroot000000 000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mlt-7.38.0/MltConfig.cmake.in000664 000000 000000 00000000622 15172202314 015653 0ustar00rootroot000000 000000 @PACKAGE_INIT@ set(_supported_components "@MLT_SUPPORTED_COMPONENTS@") foreach(_comp ${Mlt@MLT_VERSION_MAJOR@_FIND_COMPONENTS}) if (NOT _comp IN_LIST _supported_components) set(Mlt@MLT_VERSION_MAJOR@_FOUND False) set(Mlt@MLT_VERSION_MAJOR@_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}") endif() endforeach() include("${CMAKE_CURRENT_LIST_DIR}/Mlt@MLT_VERSION_MAJOR@Targets.cmake") mlt-7.38.0/NEWS000664 000000 000000 00000345210 15172202314 013066 0ustar00rootroot000000 000000 MLT Release Notes ----------------- Version 7.38.0 Framework - Added dynamic hash table for `mlt_properties` for better performance. - Fixed memory corruption in `mlt_pool`. - Added warning on file open failure when parsing in `mlt_properties`. Modules - Added `hwaccel_scale` filter with preview scaling (scale_vaapi, scale_vulkan, scale_d3d11, scale_vt (macOS)). This adds an environment variable `MLT_AVFORMAT_HWACCEL_PPS` to provide a limiter to hwaccel because transfering uncompressed video between memory CPU and GPU memory is heavy. PPS = maximum pixels per second. Anything over it skips hwaccel. - Added *basic* support for **OpenFX** *filter* plugins. OpenFX has many features. This is a summary of what is NOT yet included: - GPU rendering - exotic parameter types: Integer2D, Integer3D, Double3D, Custom, Bytes, Page, PushButton - plugin-driven keyframe animation; but MLT-driven does work - tile-, slice-, or frame-based multi-threading (only plugin internal threading) - multi-resolution - temporal clip access - overlays & dialogs - timeline queries (e.g. `getTime()`) - generator, transition, retimer - parametric curves - Added `lut` parameter to `avformat` producer. - Added `attached_pic` property to `avformat` consumer. - Added `movit.overlay_blend` video filter. - Added `compositing` property to `movit.overlay` transition. - Added constant-power crossfade to `mix` audio transition. - Added rounded corner support for `rect` in `kdenlivetitle`. - Added typewriter properties to `subtitle` filter. - Added underline and strikethrough to the `text` and `qtext` filters. - Changed `qtext` `style` property to support family named styles. - Added anchor point support for arbitrary rotations in `qtblend`. - Allow relative paths for images in `kdenlivetitle`. - Updated Glaxnimate module to version 0.6.0. - Switched to `QImage` scaling when downscaling in `qtblend`. - Deprecated `obscure` and `watermark` filters. - Install the jackrack module's blacklist files. - Fixed subtitle stream type metadata. - Fixed mutex initialization in `avformat` no-validate mode. - Fixed crash when using `linearblend` deinterlacer. - Fixed black bar with HEVC hwaccel decode and scale on Windows. - Fixed crash with hardware scaler and interlaced video. - Fixed converting 8-bit full to limited range with hwaccel. - Fixed alpha in 12-bit ProRes with alpha. - Fixed `movit` linear color not matching CPU filters. - Fixed `multi` consumer making extra linear color conversions. - Fixed regression writing raw FLAC. - Fixed `qtblend` filter returning unexpectedly large frames. - Fixed `qtext` outline encroaching on fill color. - Fixed wrong last+ frame when `eof=loop` in Glaxnimate. - Fixed `brightness` filter on track below a clip with transparency. Other - Fixed crash during exit cleanup in `melt`. - Continued build improvements for MSVC. - A lot of metadata fixes and improvements. - Many fixes to API documentation. - Added C++ and Python to the API docs. - Added two 10-bit VP9 encoding presets. Version 7.36.1 - Fixed construcing a new producer with a null profile. - Fixed constructing a `xml-clip` profile. Version 7.36.0 Framework - Added `mlt_image_default_colorspace()` - Added `mlt_image_default_trc()` - Added `mlt_image_default_primaries()` - Added `mlt_color_convert_trc()` - Added the `mlt_consumer` property `mlt_color_trc` to drive a new `color_transform` normalizer filter. This sets the color transfer function to use for image/video effects. It is beneficial and correct to use linear color for any pixel mixing whether for blending or interpolation. - Removed `atexit(mlt_factory_close)` in `mlt_factory_init()`. Close the factory yourself if you feel the need to (not generally needed when exiting a process). - Prevent possible crash in `mlt_service_connect_producer()`. - Added `mlt_profile_is_valid()` and `Mlt::Profile::is_valid()`. - Fixed crash on invalid profile (CVE-2025-65834) in: * `mlt_consumer_start()` * `mlt_factory_producer()` * `xml` producer Modules - Drop support for Qt 5. - Fixed bad color in `luma` transition's dissolve (regression in v7.34.0). - Fixed HLG HDR broken in `decklink` consumer (regression in v7.34.0). - Fixed warnings when flushing audio in the `avformat` consumer. - Added support for `hwaccel=vulkan` to the `avformat` producer. - Added a `color_transform` normalizer filter to the core module to convert all image/video producers to and from linear color. It is abstract in the core module and drives the following filters: * `avfilter.zscale` * `sws_colortransform` * `avfilter.scale` - Added filter `sws_colortransform` that is more efficient than `avfilter.swscale` but requires FFmpeg 8. - Changed the default property values of the `null` consumer: * `real_time`: -1 * `terminate_on_pause`: 1 - Changed the `luma` and `movit.luma` transitions to progress linearly with linear `color_trc`. - Fixed the alpha channel dropped by the `xml-clip` producer. - Added `mlt_image_rgba64` format to: * `avcolor_space` filter * `avdeinterlace` filter * `affine` filter and transition * `audiolevelgraph` filter * `audiospectrum` filter * `audiowaveform` filter * `autofade` filter * `avformat` producer and consumers * `box_blur` filter * `brightness` filter * `pillar_echo` filter * `color` producer * `dropshadow` filter * `dynamictext` filter (when used with `qtext`) * `frei0r.alpha0ps` filter * `frei0r.alphagrad` filter * `frei0r.alphaspot` filter * `frei0r.bluescreen0r` filter * `frei0r.select0r` filter * `frei0r.transparency` filter * `gpsgraphic` filter * `gpstext` filter * `gradientmap` filter * `hslprimaries` filter * `hslrange` filter * `kdenlivetitle` producer * `lift_gamma_gain` filter * `lightshow` filter * `mask_apply` (when used with `qtblend`) * `movit.convert` filter * `outline` filter * `qtblend` filter and transition * `qtcrop` filter * `qtext` producer * `shape` filter * `spot_remover` filter * `strobe` filter * `subtitle` filter (when used with `qtext`) * `swscale` filter * `text` filter (when used with `qtext`) * `timeremap` link * `timer` filter (when used with `qtext`) * `vqm` transition - Changed the `mask_apply` filter default transition to `qtblend`. - Removed the maximum for the `alpha` property on the `brightness` filter so that you can _increase_ the opacity of an existing alpha channel. - Fixed background image scaling in the `gpsgraphic` filter. - Fix audio artifacts with pcm_s24le in mkv in `avformat` producer. Other - Removed bitrate and quality limits in the vp9 and webm `avformat` presets. - Fixed melt on Windows not reading file path/name with extended characters (regression in v7.34.0). - Fixed building the ruby bindings with Ruby 3.0. - More fixes for MSVC. - Removed `win32/strptime.c` and replaced with Qt functions. - Stop showing `melt` usage on error exit. Version 7.34.1 - Fixed compiling with Qt 5. Version 7.34.0 Framework - Added `mlt_image_rgba64` format. This change touched core, avformat, and qt modules as well. - Added `mlt_color_trc`, `mlt_color_primaries`, and more `mlt_colorspace`s along with functions: * `mlt_image_color_trc_name` * `mlt_image_color_trc_id` * `mlt_image_colorspace_name` * `mlt_image_colorspace_id` * `mlt_image_color_pri_name` * `mlt_image_color_pri_id` - Fixed some minor memory leaks with `cppcheck`. Modules - Added rgba and rgba64 formats to the `luma` transition. - Added rgba64 format to `affine` filter and transition. - Added rgba64 format to the `qtblend` transition. - Added support for FFmpeg 8 to the `avformat` module. - Fixed audio `panner` filter for 5.1 channel layout. - Fixed converting 10-bit full to limited range in `avformat` producer (regression in v7.30.0). - Fixed shadow not working for multi-line template titles in `kdenlivetitle` producer. - Fixed `mask_apply` filter with custom transition. - Added `input_chmask` & `output_chmask` properties to `mono` filter. - Added `channel_mask` property to LADSPA/LV2/VST2 filters. - Added `channel_mask` to the `volume` filter. - Added an `outline` video filter. - Fixed a deadlock on image with a `%` in the name in `qimage` producer. - Added scrubbing to the `decklink` consumer. - Fixed `&` not decoded to `&` in the `xml` producer. - Fixed converting BT.709 to BT.2020 in the `avformat` module. - Fixed building a `ladspa` module without JACK as dependency. - Deprecate a JACK Rack XML file in the `jackrack` and `ladspa` modules. - Added filter `qtblend_mode`. - Fixed incorrect alpha channel breaking optimization of `qtblend`. - Fixed pixel format gbrap (Ut Video with alpha channel) in the `avformat` producer. - Fixed `opencv_tracker` filter on cut playlist clips. - Fixed warning about missing color range for `avfilter`s that use it (e.g. `avfilter.colorspace`). - Added typewriter properties to the `qtext` filter. - Fixed color distortion or a crash if using hwaccel with orientation rotation in `avformat` producer. Other - Enable Qt 6 and disable Qt 5 modules in CMake by default. - Disable the SDL1 module in CMake by default. - Changed `melt` now exits with 1 if the producer is invalid. - Various changes to work with MSVC compiler. - Changed SVT-AV1 encode presets to VBR for Opus audio. - Fixed initializing Qt on the main thread in `melt`. - Documented the `-progress2` option for `melt`. - Increased the minimum C++ version to C++20. - Converted many modules to use CMake `find_package()`. - Added `libgen.c` and `libgen.h` for MSVC. Version 7.32.0 Framework * Added `mlt_service_set_consumer()` and `Mlt::Service::set_consumer(). * Optimized `mlt_multitrack` to not request a frame from a producer that is both hidden and muted. Modules * Fixed pixel format for VP8/VP9 streams in `avformat` producer. * Fixed `yuva422p` pixel format in `avformat` producer. * Added `MLT_AVFORMAT_HWACCEL` & `MLT_AVFORMAT_HWACCEL_DEVICE` environment variables to `avformat` producer. * Updated `decklink` module for recent hardware and drivers. * Added `colorspace` and `color_trc` (including HDR) properties to the `decklink` consumer. * Fixed silencing extra audio channel (e.g. 6 => 8) in `decklink` consumer. * Fixed 5.1 C/LFE channels swapped over HDMI in `decklink` consumer. * Fixed text outline had sharp angles in `kdenlivetitle` producer. * Fixed default gamma to be Rec. 709 for 10-bit Y'CbCr from `movit` module. * Added support for HLG gamma to `movit` module (requires ddennedy/movit fork). * Fixed `opencv_tracker` filter should require multiple keyframes to be ready. * Fixed OpenCV crash on `rect` <= 1 pixel in `opencv_tracker` filter. * Fixed aspect ratio in `qtblend` filter and transition. * Fixed `QT_QPA_PLATFORM=offscreen` not working in qt and glaxnimate modules. * Improved preview scaling in `qtblend` filter. * Fixed requesting huge images on multiple `qtblend` filters or transitions. * Fixed `sdl2` consumer on macOS. * Fixed the `strobe` filter not working with `movit`. * Fixed color and gamma using `timeremap` link with `movit`. * Fixed consumer properties (e.g. `channels`) missing on `qglsl` consumer injected by the `xml` producer. Version 7.30.0 Framework * Fixed `Mlt::Producer::set_creation_time()` not exported on i686. * Fixed `Mlt::Properties::set(int64_t)` symbol version. * Fixed `mlt_factory_init()` on Linux/BSD may fail to initialize when compiled with `-DRELOCATABLE`. * Added generic non-drop-frame timecode in `mlt_property.c`. Previously, it was only done for 30000/1001 and 60000/1001 frame rates, but 24000/1001 is especially important as well. * Added support for MLT XML embedded in chains. This, along with `xml-clip` producer handles mismatching frame rate between parent and child producers and facilites time-affecting links on the child. * Added `mlt_image_full_range(const char *color_range)` where any of the following strings return true (1): `full`, `jpeg`, `pc`. Modules * Added `xml-clip` producer. * Fixed link `in` and `out` properties arenot serialized in the `xml` consumer. * Added `hslprimaries` and `hslrange` filters to the `plus` module. * Added a `gradientmap` filter to the `plus` module. * Fixed `avfilter` audio filters with FFmpeg 7. * Fixed incorrect frame rate for AVCHD (and possibly others) in `avformat`. * Fixed `window_id` property in consumer `sdl2` by using `SDL_CreateWindowFrom()`, especially important for embedding in GTK+ or Qt. * Fixed text not rendering in transition `vqm` since Qt 6. * Added `decimals` keyword to `gpstext` filter and move `RAW` keyword check. * Fixed `time_offset` property handling in `gpstext` filter. * Fixed reading and writing SRT files with unicode in the path. * Fixed `subtitle_feed` filter on producers with an in point > 0. * Fixed opaque alpha channels become translucent in `luma` transition. * Fixed a crash in the `mix` transition with `consumer` producer. * Fixed `freeze` filter freezing too much. * Fixed `avfilter.fillborders` with preview scaling. * Fixed non-proportional scaling in `qtblend` transition and filter. * Fixed `avfilter` color distortion with `mlt_image_rgb` and `mlt_image_rgba`. * Extended support for `colorspace=2020` consumer propeerty and the BT.2020 colorspace in the `avformat` producer and consumer. This does not imply HDR, which is signaled through a `color_trc` property. * Added support for `mlt_image_yuv420p10`, `mlt_image_yuv444p10`, and `mlt_image_yuv422p16` in `avfilter`, `swscale`, and `rescale` filters. This facilitates using these pixel formats end-to-end when using only FFmpeg producers, certain avfilters, and `avformat` consumer. This means it is possible to do 10-bit end-to-end on the CPU when being careful to select compatible components and options to avoid conversions. One can pass-through HDR; however, you must set the `color_trc` and `pix_fmt` properties on the `avformat` consumer (see `ffmpeg -h full` for these values). The `avformat` consumer automatically converts MLT `colorspace` (integer value) to FFmpeg's `colorspace` and `color_primaries` (unless explicit) options. Other * Cleaned up the examples in `src/swig`. * Changed the `x264-medium` preset to a higher quality. Version 7.28.0 This fixes a couple of major regressions in the previous version 7.26.0: * Fixed seeking and frozen video due by reverting "Improved performance with intra-only video and reducing the frame rate." * Fixed using `melt` to render MLT XML with OpenGL effects from `movit`. Other fixes and changes: * Deprecated the `composite` transition and the `sdl` and `ndi` modules. * Fixed Android camera video has the wrong frame rate (broken in v7.26.0). * Fixed audio not playing for audio-only DTS WMA. * Fixed using "https:" URLs in the `xml` producer. * Fixed crash on exit when running `melt -query` or `melt -query links`. * Added a `transition` property to the `watermark` filter. Now, it defaults to using `affine`. * Changed `affine` transition argument to set `rect` property if supplied. Version 7.26.0 Framework * Fixed a double-free crash in `Mlt::Service::profile()`. Modules * Fixes and improvements to the `avformat` producer: - Changed to prefer `r_frame_rate` over `avg_frame_rate`. - Fixed `lowres` if set too high. - Fixed `audio_index=all`. - Fixed `variable_frame_rate` incorrectly set true on 59.94 fps in Matroska. - Improved performance with intra-only video and reducing the frame rate. - Fixed mono audio handling regression on FFmpeg 7. - Fixed audio samples may be dropped unexpectly esp. with uncompressed. * Fixed `movit` transitions with a non-movit filter on one of its inputs. * Fixed duration in the `glaxnimate` producer off by one frame. * Added `dropshadow` filter to the `qt` module. * Fixed resetting animation in `kdenlivetitle` producer. * Added support for LV2 and VST2 plugins in the `jackrack` module. * Fixed crash using `av.declick` audio filter on FFmpeg 7. * Added `subtitle` filter and producer and `subtitle_feed` filter to the `plus` module. * Added subtitle encoding to the `avformat` consumer (new properties beginning with "subtitle."). * Fixed `alang` in the the `avformat` consumer. * Added `#gps_power` keyword to the `gpstext` filter. * Fixed tab handling in the `kdenlivetitle` producer. Other * Added 8- and 10-bit encode presets for SVT-AV1. * Fixed building on OpenBSD. * Fixed building on musl libc. * Fixed consumer properties not updating the automatic profile on the `melt` command line. * Added `-loglevel` command line option to `melt`. Version 7.24.0 Framework * Fixed a small memory leak in `mlt_repository`. * Fixed a small memory leak in `MltPushConsumer` C++ class. * Block connecting a null producer to a service. * Include `locale.h` on any GNU libc platform. Modules * Added a new `spatialaudio` module with filters: - `ambisonic-decoder` - `ambisonic-encoder` * Fixed building with FFmpeg 7. * Fixed text keywords do not work with non-ASCII filenames on Windows: - `pixbuf` producer - `opencv_tracker` filter - `dynamictext` filter - `qimage` producer * Added "meta.media.aspect_ratio" property to the `avformat` producer. * Fixed `distort` property not working in `movit.rect` filter. * Fixed frames dropping or repeating in the `multi` consumer. * Fixed the `dynamic_loudness` filter maximizing audio gain. * Fixed distortion in the `mono` filter. * Also check for `WAYLAND_DISPLAY` to detect a graphical session in the `qt` and `glaxnimate` modules. * Fixed the `wave` filter distorts if `wave` = 1 with preview scaling. * Added the read-only `meta.media.%u.codec.layout` property to `avformat` producer. * Set the `channel_layout` property on the frame for the `noise` and `tone` audio producers. * Fixed `outline` maximum for the `text` and `dynamictext` filters. Other * Fixed crash when using `-chain` from `melt`. * Fixed a small memory leak on Windows `fopen()`. Version 7.22.0 Framework * Added new functions: - `mlt_property_is_color()` - `mlt_property_is_numeric()` - `mlt_property_is_rect()` * Many new keyframe types: - `mlt_keyframe_smooth_loose` - `~=` (same as old `mlt_keyframe_smooth` - Unity Catmull-Rom spline) - `mlt_keyframe_smooth_natural` - `$=` (Centripetal Catmull-Rom spline with natural slope) - `mlt_keyframe_smooth_tight` - `-=` (Centripetal Catmull-Rom spline with 0 slope) - `mlt_keyframe_sinusoidal_in` - `a=` - `mlt_keyframe_sinusoidal_out` - `b=` - `mlt_keyframe_sinusoidal_in_out` - `c=` - `mlt_keyframe_quadratic_in` - `d=` - `mlt_keyframe_quadratic_out` - `e=` - `mlt_keyframe_quadratic_in_out` - `f=` - `mlt_keyframe_cubic_in` - `g=` - `mlt_keyframe_cubic_out` - `h=` - `mlt_keyframe_cubic_in_out` - `i=` - `mlt_keyframe_quartic_in` - `j=` - `mlt_keyframe_quartic_out` - `k=` - `mlt_keyframe_quartic_in_out` - `l=` - `mlt_keyframe_quintic_in` - `m=` - `mlt_keyframe_quintic_out` - `n=` - `mlt_keyframe_quintic_in_out` - `o=` - `mlt_keyframe_exponential_in` - `p=` - `mlt_keyframe_exponential_out` - `q=` - `mlt_keyframe_exponential_in_out` - `r=` - `mlt_keyframe_circular_in` - `s=` - `mlt_keyframe_circular_out` - `t=` - `mlt_keyframe_circular_in_out` - `u=` - `mlt_keyframe_back_in` - `v=` - `mlt_keyframe_back_out` - `w=` - `mlt_keyframe_back_in_out` - `x=` - `mlt_keyframe_elastic_in` - `y=` - `mlt_keyframe_elastic_out` - `z=` - `mlt_keyframe_elastic_in_out` - `A=` - `mlt_keyframe_bounce_in` - `B=` - `mlt_keyframe_bounce_out` - `C=` - `mlt_keyframe_bounce_in_out` - `D=` * Fixed missing support for `mlt_service_transition` in `Mlt::Producer()` C++ constructor. Modules * Fixed `rotoscoping` filter crash on image with height = 0. * Fixed crashed due to `qtblend` transition requesting an image of 0 width or height. * Added support for RtAudio 6 in the `rtaudio` consumer. * Fixed `createdate` keyword deletes preceeding text in `dynamictext` filter. * Added `opacity` property to filters that use `qtext`: - `dynamictext` - `gpstext` - `qtext` - `timer` * Added `fade_video`, `fade_audio`, and `fade_color` properties to `autofade` filter. * Added backwards compatibility for changed filter names in frei0r v2.3.1: - `frei0r.measure_pr0be` - `frei0r.measure_pr0file` - `frei0r.tehroxx0r` - `frei0r.alpha0ps_alpha0ps` - `frei0r.alpha0ps_alphagrad` - `frei0r.alpha0ps_alphaspot` - `frei0r.denoise_hqdn3d` * Fixed a memory leak in `avformat` producer with consumer deinterlacer=yadif. * Fixed `qimage` producer color if consumer color_range=pc pix_fmt=yuv444p. Other * Fixed `ten_bit/ProRes 422` avformat preset produced ProRes 444. * Fixed `YouTube` avformat preset did not output high profile with some hardware encoders. Version 7.20.0 Framework * Fixed "blank" in a playlist does not have audio normalization filters. * Fixed serializing `mlt_color` transparent black as "#00000000" when the property was set using an integer or `mlt_color`. * Fixed `mlt_chain_set_source()` would always fetch a frame from the producer even if it has "meta.media.frame_rate_num" and "meta.media.frame_rate_den" properties making things slow. * Fixed `Mlt::Chain` leaking memory. Modules * Added a `blank` producer to the `core` module. * Added keywords to `gpstext` filter: - `#gps_cadence#` - `#gps_grade_degrees#` - `#gps_grade_percentage#` - `#gps_temperature#` * Added some `color_style`s to the `gpsgraphic` filter: - 10 = color by speed (max 100 km/h) - 11 = color by grade (max 90 degrees) - 12 = color by grade (max 20 degrees) * Added more unit formats to `legend_unit` property of `gpsgraphic` filter: - `mmin` or `m/min` - `ftmin` or `ft/min` * Added keywords to `dynamictext` filter: - `#basename#` - `#filename#` * Fixed installing `filter_audioseam.yml`. * Added an `avlink` link to the `avformat` module for FFmpeg filters that can benefit from future frames such as `adeclick`. * Added the `preserve_alpha` property to the `box_blur` filter. * Fixed loading service metadata for the `qt6` and `glaxnimate-qt6` modules. * Fixed a crash when changing the `rotate` property in `avformat` producer with interlace video. * Add `astream` and `vstream` properties to avformat producer. Unlike `audio_index` and `video_index` are absolute indices across the entire array of streams regardless their type, these new 0-based properties are relative to the type audio or video. For example, astream=1 is the second audio stream. * Fixed a possible crash in the `avformat` producer's `mlt_producer_probe` virtual function. * Updated the `glaxnimate` module to version 0.5.4. * Fixed the `sdl2` consumer crashing with the Linux radeonsi_dri driver and showing only all black with the Linux `nvidia` driver. Other * Fix compiling on Android (not supported by the core developers). * Changed the `avformat` consumer `FLAC` preset to use the `flac` format. * Fixed the `melt` Shift+H and Shift+L keyboard shortcuts when the SDL2 window has focus. Version 7.18.0 Framework * Fixed `mlt_frame_get_audio` fails on `mlt_audio_none`. * Added `mlt_audio_free_data()`. * Added `meta.playlist.clip_position` and `meta.playlist.clip_length` properties to `mlt_playlist`. Modules * Added two audio filters to core module to be used on a playlist/track: - `audioseam` - `autofade` * Fixed a crash in `vidstab` filter on image format change. * Fixed font weight in `qtext` filter on Qt 6. * Fixed yuv420p not working in `rescale` filter. * Fixed text shadow outline in `kdenlivetitle` producer. * Fixed crash when changing the profile with `count` producer. * Fixed constructor corruption in `frei0r` module. * Fixed `deinterlace` link was added to invalid producer in `xml` producer. * Fixed producers not indicating progressive scan video: - `kdenlivetitle` - `pango` - `qimage` - `qtext` * Fixed video scan mode detection in `avformat` producers that only indicate on their container format and not on frames such as Ut Video in Matroska. * Fixed very large images in `qimage` producer on Qt 6. * Fixed seeking on clips that use `speed_map` in `timeremap` link. * Fixed a color level problem with sRGB inputs in the `movit` module. * Fixed `avformat` producer's deallocation function for `AVCodecContext`. * Fixed field order of `qtblend` and `frei0r.cairoblend` transitions. * Changed the `avformat` producer `seek_threshold` default to 64. * Updated `ebur128` filter to version 1.2.6. Version 7.16.0 Framework * Added a `chain_normalizers.ini` to the data directory. * Added New C functions to support deinterlacer links: - `mlt_deinterlacer_name()` - `mlt_deinterlacer_id()` - `mlt_link_filter_init()` - `mlt_link_filter_metadata()` - `mlt_cache_put_frame_audio()` - `mlt_cache_put_frame_image()` - `mlt_frame_clone_audio()` - `mlt_frame_clone_image()` * Added support for loading a filter as a link via `mlt_link_filter_init()`. * Added enum `mlt_deinterlacer` with: - `mlt_deinterlacer_none` - `mlt_deinterlacer_onefield` - `mlt_deinterlacer_linearblend` - `mlt_deinterlacer_weave` - `mlt_deinterlacer_bob` - `mlt_deinterlacer_greedy` - `mlt_deinterlacer_yadif_nospatial` - `mlt_deinterlacer_yadif` - `mlt_deinterlacer_bwdif` - `mlt_deinterlacer_estdif` - `mlt_deinterlacer_invalid` * Added new 10-bit YUV members to enum `mlt_image_format`: - `mlt_image_yuv420p10` - `mlt_image_yuv444p10` * Fixed a deadlock and improved quality of start of playback when `mlt_consumer` property `prefill` is greater than 1. * Fixed a couple of data races in `mlt_events` and `mlt_consumer`. * Fixed a crash in `mlt_frame_clone()` with movit and the `mask_start` filter. Modules * Fixed regressions in version 7.14.0: - memory and thread count usage in `swresample` and `resample` links - automatic profile support in `melt` - crash in `count` producer * Upgraded the `glaxnimate` git submodule to version 0.5.3. * Added avformat/`avdeinterlace` (default) and xine/`deinterlace` links. * Fixed deinterlacing in the `multi` and `qglsl` consumers. * Added 10-bit video support to `movit.convert` filter. * Several things in the `avformat` producer: - Fixed artifacts decoding raw FLAC audio. - Fixed a potential crash on `mlt_producer_probe()`. - Fixed seeking on music with album art. - Fixed possible infinite loop on end-of-file. - Fixed a potential deadlock. - Fixed chroma bleeding on interlaced yuv420p. - Fixed `color_range` or `force_full_range` sometimes not working. - Fixed `autorotate` property not working with a chain. - Added audio caching. - Deprecated the `mute_on_pause` property. * Fixed FFmpeg version 6 compilation error. * Fixed rendering the text outline in `kdenlivetitle` producer. * Fixed `'movit.rect` property animation. * Fixed corrupt video in `crop` filter when `mlt_image_yuv420p` requested. * Fixed possible null pointer crashes in some audio filters: - `audiolevel` - `volume` - `loudness` * Fixed a possible roi assert crash in `opencv.tracker` filter. * Added support for "Nano" `algo` to the `opencv.tracker` filter. * Added the property `fix_background_alpha` to the `luma` transition. Other * Added `-query links` to `melt` command line. * Added `avformat` consumer presets for 10-bit video: - AV1 - DNxHR-HQ - FFV1 - ProRes 422 - ProRes 444 - ProRes HQ - x264-high10 - x265-main10 * Added a `clang-format` target to CMake and reformatted all code. * Added warnings as errors with some exceptions to CMake with `Debug` build type and gcc. * Fixed numerous warnings throughout the code. Version 7.14.0 Framework * Added functions to get detailed info about a producer more directly (without having to get a frame and get its image in the case of avformat producer, for example): - `mlt_producer_probe()` - `Mlt::Producer::probe()` * Added functions to add normalizer links to chains (based on a `chain_loader.ini` configuration data file: - `mlt_chain_attach_normalizers()` - `Mlt::Chain::attach_normalizers()` * Changed `locale_t` to `mlt_locale_t` to avoid redefinition on some systems (e.g. clang/llvm on win32). * Fixed the value provided with event "consumer-thread-join" to be `mlt_event_data_thread` as documented. * Fixed `mlt_image_format_planes()` for `mlt_image_yuv420p`. Modules * Added a `swresample` link to the avformat module. * Added a `resample` link to the resample module. * Fixed compatibility of avformat module with FFmpeg version 6. * Fixed `rotoscoping` filter when request image size different than profile. * Fixed `timeremap` link breaking `crop` filter. * Fixed audio/video sync in `avformat` producer when the video start time is not 0. * Improved seeking on a WMA audio file in `avformat` producer. * Optimization to set `AVDISCARD_ALL` on disinterested streams in `avformat` producer. * Added separate demuxing thread in `avformat` producer. * Added `filtergraph` property to the `avformat` producer. * Fixed filter `movit.convert`'s CPU image converter in `mlt_tractor` and `mlt_frame_clone()`. * Fixed using `movit` module with mlt_chain. * Fixed 10-bit full range YUV color input with Movit. * Fixed the `movit.luma` transition. * Changed the `qglsl` consumer to use an OpenGL core profile version 3.2 context to make it compatible with recent Movit versions. * Fixed aspect ratio issues in `qtblend` filter transform. * Upgraded `glaxnimate` git submodule to version 0.5.2. * Fixed `xml` producer incorrectly adds a path prefix to a `consumer` producer. * Fixed using `opencv.tracker` filter with `mlt_chain`. * Added interlace-aware chroma conversion from mlt_image_yuv422 to yuv420p in the `avformat` consumer. * Added the `speed_map` property to the `timeremap` link. * Fixed the `loader` producer not injecting the `consumer` producer when a `xml` producer changes the frame rate. * Fixed 'loader' producer corrupts the profile colorspace and description when it injects a `consumer` producer. * Added a `loader-nogl` producer to the core module based on `loader` but prevents adding `movit`-based filters. * Changed `count` producer to take an optional string argument with the name of a loader producer. * Fixed `yadif` deinterlace not working in a mlt_chain. * Fixed the bob, weave, greedy, onefield `deinterlace` filter methods on x86-64 architecture. Other * Fixed SWIG python shadow functions for mlt7. * Added CMake build option `MOD_GLAXNIMATE_QT6`. Version 7.12.0 This version is released soon after 7.10.0 to fix a couple of major new bugs in the popular `qtblend` and `frei0r.cairoblend` transitions. It also includes new color animation APIs with sensible interpolation! Framework * Added new color animation APIs: - `mlt_property_set_color()` - `mlt_property_get_color()` - `mlt_property_anim_set_color()` - `mlt_property_anim_get_color()` - `mlt_properties_anim_set_color()` - `mlt_properties_anim_get_color()` - `Mlt::Properties::anim_get_color(char const*, int, int)` - `Mlt::Properties::anim_set(char const*, mlt_color, int, int, mlt_keyframe_type)` Modules * Updated the following services to support animation of color properties: - `frei0r` (any color parameter in any frei0r plugin) - `chroma` - `chroma_hold` - `audiolevelgraph` - `audiospectrum` - `audiowaveform` - `gpsgraphic` - `gpstext` - `qtcrop` - `qtext` * Added `discontinuity_reset` property to `dynamic_loudness` filter. * Fixed `qtblend` transition not blending with an opaque rgba image. * Added support for the "finer" engine in Rubberband version 3. * Fixed crash in `frei0r.cairoblend` when `threads` property not set. Other * Fixed leaking the xml producer in `melt` when the XML contains a `consumer` element but no profile information. * Fixed symbol not found error in `rtaudio` consumer. Version 7.10.0 The highlight of this version is support for Qt 6. Framework * Fixed some unguarded null pointers. * Added `MLT_REPOSITORY_DENY` environment variable to skip loading a module (colon delimited list of file names without extension, for example libmltqt). * Fixed frame corruption with one frame transition * Changed so-called test-card frame with audio to show a checkerboard: - Added `mlt_image_fill_checkerboard()` - Added `mlt_image_fill_white()` * Preserve the producer `creation_time` property when creating a chain. * Added `mlt_image_rgba_opaque()`. * Fixed getting a property as a timecode or clock value with 24 or 23.98 fps in `mlt_property.c`. Modules * Added support for Qt 6: - Added `MOD_QT6` and `BUILD_TESTS_WITH_QT6` CMake options. - Allow installing building and installing both Qt 5 & 6 modules. - Avoid loading both Qt 5 & 6 modules by preferring Qt 5 (use MLT_REPOSITORY_DENY=libmltqt to block Qt 5 and use Qt 6). - This is limited to the `qt` module for now and not `glaxnimate` (still a work-in-progress). * Added support for WebP animation to `qimage` producer. * Added `gps_graphic` filter to the `qt` module. * Added the `format` property in each producer's get_frame method to indicate the producer's default/preferred mlt_image_format to facilitate an optimization in the `qtblend` transition when the B frame is opaque and has the same aspect ratio. * Added property animation to all audio visualization filters in the `qt` module. * Improved TGA format detection in `qimage` filter. * Fixed `qtblend` transition has incorrect scaling with consumer scaling. * Fixed an case of incorrect alpha scaling in `qtblend` transition. * Fixed `luma` transition not updated when `resource` property changes. * Added the `alpha_operation` property to the `shape` filter. * Updated the `glaxnimate` git submodule to version 0.5.1. * Fixed `lines` filter in `oldfilm` regression in v7.6.0. * Added `dbpeak` property to the `audiolevel` filter in dB. * Fixed memory leak using some frei0r plugins in conjunction with an `affine` that animates the `rect` property. Other * Fixed building for musl. * Fixed underlinking iconv in `gdk` module on MinGW. * Fixed SWIG CMake options can overwrite each other. * Fixed SWIG 4 no longer generates a `mlt.php`. Version 7.8.0 This highlight of this version is a new glaxnimate producer to render 2D vector art and animation. Framework * Added `mlt_frame_get_alpha_size()` and refactored code to use it. * Fixed a possible null pointer crash in `mlt_service_apply_filters()`. Modules * Added a `glaxnimate` producer to the glaxnimate module. * Added new file extensions for `glaxnimate` producer: json, lottie, rawr, tgs. * Removed Qt4 compatibility from the qt module. * Added Qt6 compatibility to the qt module. * Added new file extensions for `qimage` producer: avif, heic, heif, jxl. * Fixed `color_range` when using the `multi` consumer. * Fixed reloading updated `results` in the `loudness` filter. * Fixed `image_mode=blend` in the `timeremap` link. * Fixed crash regression in `swscale` filter with odd size YUV image. * Fixed the `choppy` filter may result in black frames with transitions. * Prevent a crash in `avfilter` producer for a bug in glibc with `_FORTIFY_SOURCE=3`. Version 7.6.0 This version adds image slice-threading to many filters and full support for full range color. All inputs are normalized to and processed at the range specified by the consumer property `color_range` that defaults to tv/mpeg (limited). Framework * Added `Mlt::Animation::next_key()` and `previous_key()` with error checking. * Fixed the `moduledir` and `mltdatadir` variables in the pkg-config file. * Removed calling `setlocale()` in `mlt_factory_init()` (moved to `melt` option `-setlocale`). * Added `mlt_properties_copy()` and `Mlt::Properties::copy()`. * Changed some primarily internal property names to consolidate on "consumer." as a prefix convention for all consumer properties copied to `mlt_frame`s. * Added consumer property `deinterlacer` to replace deprecated `deinterlace_method`. * Fixed full range color from producer to consumer. * Added `mlt_slices_size_slice()` helper function. * Fixed choppy playback due to large values in `frame_rate_num` or `frame_rate_den` in `mlt_consumer`. * Added performance optimization for a single slice in `mlt_slices`. Modules * Added `audiolevelgraph` video filter to the `qt` module. * Added property `segment_gap` to the `audiospectrum` video filter. * Added `segments` property to the `audiolevelgraph` and `audiospectrum` filters. * Fixed loading image sequence with extended UTF-8 characters in the name of a folder for the `qimage` producer. * Fixed a crash in `avformat` producer if the `rotate` property is set after the first frame is fetched. * Added the `invert_mask` property to the `shape` video filter. * Changed `avformat` producer to normalize frame rates very close to non-integer broadcast frames 24/1.001, 30/1.001, and 60/1.001. * Converted the `chroma` and `chroma_hold` filters' `key` property to a proper color type. * Added slice threading to: - `avformat` producer (with FFmpeg v5) - `swsscale` (with FFmpeg v5) - `lift_gamma_gain` - `shape` - `charcoal` - `vignette` - `wave` - `threshold` - `tcolor` - `sepia` - `mirror` - `invert` - `grain` - `lines` - `spot_remover` * Improved the speed of the `oldfilm` filter. * Added a faster `box_blur` filter to the core module and deprecated the `boxblur` filter in the kdenlive module. * Fixed preview scaling for the `avfilter.gblur` filter. * Fixed incorrect text overlap in `kdenlivetitle` producer. * Improved audio synchronization in `avformat` when playing in reverse. * Added much more service metadata (documentation). * Fixed full range 10-bit video input in `avformat` producer. * Fixed full range color handling in: - `avformat` producer - `avcolor_space` - `brightness` - `resize` - `luma` transition - `movit.convert` - `charcoal` - `invert` - `shape` * Fixed identifying unsupported colorspaces in `avformat` producer. * Fixed preserving the alpha channel in the `avfilter.fspp` filter. Other - Some CMake fixes. - Added `dumb-init` to the docker (no need to remember `docker run --init`). Version 7.4.0 The main highlight of this version is property animation for avfilter! Framework * Added more constructors and assignment operators in C++ wrapper: - `Mlt::Filter::Filter(Mlt::Filter*)` - `Mlt::Link::Link(Mlt::Link*)` - `Mlt::Link::Link(Mlt::Service&)` - `Mlt::Link::Link(Mlt::Link&)` - `Mlt::Link::Link(Mlt::Link const&)` - `Mlt::Link::operator=(Mlt::Link const&)` - `Mlt::Service::Service(Mlt::Service*)` * Fixed serialized animation in `mlt_animation_serialize_cut_tf()` and `mlt_animation_serialize_cut()` to include a trailing keyframe value. Modules * Added property animation for `avfilter` filters. This only works for numeric parameters, but many libavfilter options that have a type string are actually numeric in nature but accept a string expression. * Added `rotate` property to `avformat` producer to override orientation. * Changed `jackrack` module to silence false LADSPA plugin loading errors. * Fixed a crash in the `oldfilm` filter when using preview scaling. * Fixed `timeremap` link distorts audio when speed is zero. * Added nautical mile and knot units of measure to the `gpstext` filter. * Fixed full range color handling with embedded tractor (e.g. same track transition). * Fixed device capture in `avformat` producer regression in version 7.2.0. * Fixed a crash in the `matte` transition. Version 7.2.0 This is the first major maintenance release for the new major version 7 rendering it much more production ready. Plus there are a few nice new features. Framework * Added support for `mlt_properties` as a child of `mlt_properties` including XML (de)serialization: - `mlt_property_set_properties()` - `mlt_property_get_properties()` - `mlt_properties_set_properties()` - `mlt_properties_get_properties()` - `mlt_properties_get_properties_at)(` - `Mlt::Properties::set()` - `Mlt::Properties::get_props()` - `Mlt::Properties::get_props_at()` Applications can use this to store structured data in its own namespace, for example "shotcut:markers". And modules could use this for hierarchical parameters. * Fixed crash in `mlt_transition` upon inserting or removing a track. * Stopped loading `mlt_profile` until needed in `mlt_chain` creation. Modules * Added filter `gpstext` that is similar to `dynamictext` based on data in a GPX file. * Added speed parameter to `timer` filter. * Added WebP presets for `avformat` consumer. * Added a pixelate option to the `opencv_tracker` filter's `blur` property. * Fixed `center_bias` of `crop` filter not working with `use_profile`. * Fixed some missing RGB `mlt_image_format` renames after change in v7.0.0. This primarily affected presets and service metadata. * Fixed a crash when changing preview scaling in `timeremap` link. * Fixes problems due to adding redundant normalize filters upon loading a producer from XML. * Ensure filters added by the `loader` producer always come first in list. * Fixed a crash using `shape` and `affine` filters together on `color` producer. * Fixed a crash when a `vidstab` file fails to open. * Changed `vidstab` filter to save its file in ASCII text mode. * Fixed a clang LTO error in the `decklink` module. * Fixed a video decoding regression on some videos in the `avformat` producer. * Fixed a crash in the `audiowaveform` filter. * Fixed loading a relative filename from XML for `mask_start` with `shape`. * Fixed "#filedate#" in `dynamictext` filter when used with `timeremap` link. * Fixed `timer` filter's new `speed` property interaction with `start` delay. * Fixed a crash with YUYV422 (YUY2) input in `avformat` producer. * Fixed data race condition in `timeremap` link. * Fixed compiling `avformat` module with FFmpeg git beyond v4.4 with many deprecations removed. * Fixed alpha channel size calculation in `brightness` filter. * Restore legacy tracker and the new DaSiam tracker for OpenCV >= 4.5.3 in the `opencv_tracker` filter. * Fixed a crash in `opencv_tracker` on `shape_width` = 0. * Fixed incorrect handling of in and out points and duration in the `opencv_tracker` filter. * Fixed the `composite` transition leaking left border of an image on the right side on uneven width. * Fixed a problem handling some UTF-8 in thhe `typerwriter` filter. Other * Added support for the `RELOCATABLE` CMake option for Linux or BSD build. Version 7.0.1 This version is just build fixes for the most immediate problems with the somewhat new but exclusive build system in v7. * Fixed docker image not working. * Fixed a system-installed build cannot finds its modules and data. * Fixed the python installation path for binaries. * Added support for the `DESTDIR` environment variable when creating melt symlink. * Increased the build constant for the maximum size of a line of a properties file. * Fixed the vid.stab metadata install path. Version 7.0.0 This is a major new version that breaks API to add a major new feature to the framework: retiming. This is accomplished through new classes `mlt_chain` and `mlt_link`. And since we are breaking API we decided to clean house by removing deprecations and switching the build system over entirely to CMake. For more information see our [migration guide](https://mltframework.org/docs/v7migration/). Framework * Added `mlt_chain` and `Mlt::Chain` classes. * Added `mlt_link` and `Mlt::Link` classes. * Added a `link` value to service `type` in the service metadata schema. * Added a boolean `animation` parameter attribute to the service metadata schema. * Added `mlt_animation_shift_frame()` and `Mlt::Animation::shift_frames()`. * Added `mlt_animation_get_string()`. * Fixed using a stale cached property animation string. * Added `mlt_image` and `Mlt::Image` classes. * Remove legacy "height + 1" workaround in image allocation. * Fixed a crash on setting `timewarp` speed higher than 23x. * Added `mlt_audio_silence()`. * Removed `mlt_image_opengl`. * Replaced variadic arguments in `mlt_events` with new `mlt_event_data` APIs. * Removed `mlt_geometry` APIs. * Renamed `mlt_image_rgb24a` as `mlt_image_rgba`. * Renamed `mlt_image_rgb24` to `mlt_image_rgb`. * Renamed `mlt_image_glsl` to `mlt_image_movit`. * Renamed `mlt_image_glsl_texture` to `mlt_image_opengl_texture`. * Removed virtual function `mlt_frame::get_alpha_mask()`. * Removed `mlt_frame_get_alpha_mask()`. * Removed deprecated functions: - `mlt_sample_calculator` - `mlt_sample_calculator_to_now` - `mlt_channel_layout_name` - `mlt_channel_layout_id` - `mlt_channel_layout_channels` - `mlt_channel_layout_default` - `mlt_slices_init` - `mlt_slices_close` - `mlt_slices_run` - `mlt_playlist_move_region` - `Mlt::Playlist::move_region` * Fixed a rounding error calculating display aspect ratio in `mlt_profile_from_producer()`. Modules * Added a `timeremap` link to the core module with animatable `map` property. (Speed can increase or decrease between keyframes including reverse.) * Added `chain` and `link` XML elements to `xml` module. * Added "meta.media.has_b_frames" property to `avformat` producer. * Removed deprecated modules: - `dv` - `gtk2` (not gdk) - `kino` - `linsys` - `lumas` - `motion_est` - `swfdec` - `videostab` * Removed the following services: - `data_feed` filter - `data_show` filter - `region` filter and transition - `sdl_image` * Converted filters to use new `mlt_image` class: - `brightness` - `imageconver` - `mirror` - `spot_remover` * Deprecated the `audiowave` filter. * Added the ability to build the `jackrack` module without JACK to get only LADSPA producers and filters. * Deprecated `start` and `end` properties for the following filters: - `brightness` - `panner` - `boxblur` - `wave` - `volume` * Removed deprecated `font` property from `pango` producer. * Improved album art (attached pic) detection in `avformat` producer. * Improved the `resample` filter to have less artifacts and use less memory. Other * CMake: nearly complete rewrite. * Removed the old configure bash scripts and Makefiles. * Added `-chain` and `-link` options to `melt` command line. Version 6.26.1 This version fixes a major regression in the avformat producer to read from network URLs. Version 6.26.0 This is the last planned release of major version 6. Version 7 will be released soon and introduce some minor API breakage while removing deprecations. The main new feature in this version is hardware-accellerated decoding! However, this is a basic implementation: It always returns the uncompressed video to the CPU memory with no pipelining to filters. Even when coupled with hardware encoding in the avformat consumer it must transfer the video. Also, there is no automatic software/CPU fallback and no resource management. Modules * Added support for `hwaccel` query string parameter to the `avformat` producer. It accepts the following values: vaapi (Linux/BSD), cuda (Linux), videotoolbox (macOS), d3d11va (Windows), dxva2 (Windows) * Added support for `hwaccel_device` query string parameter to the `avformat` producer. This is only used with vaapi (device path) and cuda, d3d11va, or dxva (number). * Improved the usage of image slice threading in `frei0r`. This only applies when `threads`=0 and only works with some frei0r plugins that you must decide yourself. * Added an ellipse item to `kdenlivetitle` producer. * Added support for PNG and GIF as album art in the `avformat` producer. * Added BT.2020 color space metadata to the `avformat` producer. * Resolved many FFmpeg deprecations in the `avformat` producer making it possible to support AV1 decoding. * Added a `strobe` fitler that periodically makes the alpha channel transparent. * Added a new `typewriter` text filter (currently only works with the kdenlivetitle producer). * Improved sound quality for lower pitch shifts in `rbpitch`. * Fixed speed of trick play in the `jack`, `rtaudio`, `sdl_audio`, and `sdl2_audio` consumers. * Fixed matrix for independent channels in `swresample` filter. * Fixed leading zeros for the `timer` filter. * Fixed flickering using `affine` with a `luma` transition. * Fixed a crash using RGBA images in the `qimage` producer (regression in v6.22.0). * Fixed `brightness` filter misbehaves on `alpha` > 1. * Fixed writing `flac` format file does not set its duration in the `avformat` consumer. * Fixed an infinite loop in `rbpitch` filter. * Fixed `ttl` in the `qimage` producer. * Fixed building with OpenCV 4.5 * Fixed artifacts with multiple HTML `qtext` filters and frame threading. * Deprecated the `start` and `end` properties on the following (use property animation instead): - brightness - panner - boxblur - wave - volume * Deprecated the following services: - data_show - region - transition filter - autotrack_rectangle - motion_est - slowmotion Other * CMake: - Fixed building without SWIG. - Added many "MOD_..." options to explictly disable modules. - Added src/tests and the option `BUILD_TESTING`, which defaults off. - All dependency checks moved to top level CMakeLists.txt. - Install melt man page. - Install oldfilm SVG files. - Added src/examples. - Install framework/metaschema.yaml. - Fixed `plusgpl` datadir. - Added all swwig/ languages. - Increased C++ standard to C++14. * Added an `AV1` encoding preset. * Improved documentation of the requirement for C11. * The minimum version of FFmpeg is v4.0 and Libav is no longer supported. Version 6.24.0 This version is mostly fixes plus a few new filters. Framework * Trigger a `property-changed` event on `mlt_properties_pass_list`. * Fixed using a video transition with a video clip on an audio track. * Reduce the amount of service caching to 2X #tracks to reduce memory usage. Modules * Added the `pillar_echo` filter to the plus module. * Added a `qtcrop` filter to the qt module. * Added `html`, `resource`, `overflow-y`, and `_hide` properties to the `qtext` filter for rich text. * Added the filter `choppy` to the core module. * Added slice threading to the `brightness` filter. * Fixed compiling with OpenCV 4. * Fixed the colors when using `mlt_image_format=rgb24a` with `avformat` consumer. * Fixed using WebVfx in a Docker container. * Fixed a possible crash in the `timewarp` producer on sources with non-integer frame rates. * Fixed a regression in version 6.22 with multiple affine filters at the same time. * Fixed possible abort or deadlock on recursive pthread mutexes in `avformat` producer. * Fixed a crash in `crop` filter with large `center_bias` value when `use_profile` is 1. * Fixed a white video frame appearing on threaded rendering in `freeze` filter. * Fixed MLT XML DRD to permit empty playlists, which may occur on empty tracks in a multitrack. * Fixed initializing QApplication in the `qimage` producer. * Fixed interpolation when scaling with the `affine` rect and geomety properties. * Fixed high memory usage with high factors of pitch shifting in the `rbpitch` filter. * Fixed a crash on files with more than 32 streams in the `avformat` producer. Other * Fixed CMake build on MSYS2 and Windows Craft. * Added the Python binding to the CMake build. * Added the `sdl` (v1) module to the CMake build. * Removed minrate and maxrate from the `webm` avformat consumer preset. Version 6.22.1 - July 30, 2020 This patch version only fixes the version reported in the CMake build. Version 6.22.0 - July 30, 2020 This version fixes bugs associated with the preview scaling introduced in the previous version. Framework * Added mlt_properties_exists() and Mlt::Properties::property_exists(). * Added mlt_audio C class with: - mlt_audio_new() - mlt_audio_close() - mlt_audio_set_values() - mlt_audio_get_values() - mlt_audio_alloc_data() - mlt_audio_calculate_size() - mlt_audio_plane_count() - mlt_audio_plane_size() - mlt_audio_get_planes() - mlt_audio_shrink() - mlt_audio_reverse() - mlt_audio_copy() - mlt_audio_calculate_frame_samples() - mlt_audio_calculate_samples_to_position() - mlt_audio_channel_layout_name() - mlt_audio_channel_layout_id() - mlt_audio_channel_layout_channels() - mlt_audio_channel_layout_default() * Added Mlt::Audio C++ class with: - Mlt::Audio::Audio() - Mlt::Audio::Audio(mlt_audio_s*) - Mlt::Audio::~Audio() - Mlt::Audio::data() - Mlt::Audio::set_data(void*) - Mlt::Audio::frequency() - Mlt::Audio::set_frequency(int) - Mlt::Audio::format() - Mlt::Audio::set_format(mlt_audio_format) - Mlt::Audio::samples() - Mlt::Audio::set_samples(int) - Mlt::Audio::channels() - Mlt::Audio::set_channels(int) - Mlt::Audio::layout() - Mlt::Audio::set_layout(mlt_channel_layout) * Fixed drop-frame timecode for 59.94 fps. * Fixed crash on null pointer passed to mlt_consumer_stop(). Modules * Fixed frei0r transitions with preview scaling. * Fixed affine ox and oy properties incorrect with preview scaling. * Fixed a crash and incorrect preview scaling with more than one affine filter active on the same frame. * Fixed preview scaling for the rotoscoping filter. * Added the sample_fmt property to the avformat consumer. * Fixed a possible segfault in the mix transition. * Removed support for text keyframes to the text and qtext filters to fix regression on strings containing '='. * Disable frame-threading with bigsh0t, distort0r, and medians frei0r plugins. * Added "meta.media.%d.stream.projection" property the avformat producer. * Fixed a crash with with filters not supporting preview scale in frei0r transitions. * Fix artifacts in luma transition and affine filter with frame-threading. * Stop including 'title="Anonymous Submission"' in xml consumer. * Fixed a crash in opencv.tracker filter. * Fixed a crash in composite transition if luma file fails to load. * Added validations in opengl module to prevent asserts in Movit. * Fixed building with OpenCV 4. * Moved some services from gtk2 module to new gdk module: - gtkrescale filter - pango producer - pixbuf producer * Deprecated the gtk2 module and no longer enabled by default. * Changed avformat producer to accept a '?' in argument/resource property by escaping it as '\?'. * Changed the background property of the affine filter to be mutable. * Deprecated the linsys (DVEO SDI) module. * Fixed changing the audio_index property in the avformat producer. * Changed resample filter to more resiliant to frequency changes. * Added a video_delay property to the sdl2_audio and rtaudio consumers. * Add millisecond options to the timer filter. * Fixed the in point handling for the timewarp producer. * Fixed some audio gaps and sync issues with the rbpitch filter and timewarp pitch compensation. * Fixed a possible crash caused by producer consumer. * Changed avformat consumer to set AVOption color_primaries based on the MLT colorspace if not already set as property. * Fixed crop right on image with odd width skews image in crop filter. * Fixed incorrect silence value for unsigned 8-bit audio in avformat producer. * Changed qimage to use Qt's internal orientation detection instead of libexif. * Reduced clicks in mix transition by silencing buffers on discontinuity. * Improved A/V synchronization in (sw)resample filters - also reduces audio clicks. * Improved speed of the qimage producer. * Fixed incorrect color using libx264rgb in avformat consumer. * Fixed relative paths for avfilters that have the "filename" option. * Fixed some avfilters dropping the alpha channel: smartblur, vaguedenoiser. * Improved performance of the resize filter. * Fixed an affine filter inside a transition was always nearest neighbor interpolation. * Changed the lift_gamma_gain filter to use round values up. Other * Fixed melt option "-group" applies to an implicit consumer. * Added "-quiet" option to melt (implies -silent but more so). * CMake build improvments adding modules: - gdk - jackrack - lumas - resample - sox - vorbis * Added avformat consumer presets: - Slide-Deck-H264 - Slide-Deck-HEVC * Removed intra=1 from some avformat presets (use g=1 for intra only): - intermediate/MPEG-2 - intermediate/MPEG-4 - lossless/H.264 * Fixed using Qt, Movit, and WebVfx in the official docker image: https://hub.docker.com/repository/docker/mltframework/melt IMPORTANT: it now requires `docker run` with the `--init` option. Version 6.20.0 - February 15, 2020 This version adds support for low resolution preview scaling and adds a module based on librubberband for audio pitch-shifting. An official docker image is now available at https://hub.docker.com/repository/docker/mltframework/melt Framework * Added consumer scaling: - mlt_profile_scale_width() - mlt_profile_scale_height() - Mlt::Profile::scale_width() - Mlt::Profile::scale_height() - support for a double "scale" property to melt and the xml producer * Fixed mlt_properties_set() with an invalid expression. * Added new functions that do not evaluate expressions: - mlt_properties_set_string() - Mlt::Properties::set_string() * Improved the service-caching heuristic in mlt_multitrack. * Fixed possible crashes in mlt_playlist get_frame() and mlt_filter_process(). Modules * Added the rubberband module with a rbpitch filter. * Added pitch compensation to timewarp producer. * Added the invert_scale property to the affine filter and transition. * Added the reverse property to shape filter. * Added support for text keyframes to the text and qtext filters. * Added support for the CSRT and MOSSE algorithms in opencv.tracker filter. * Fixed a crash on empty algo property in the opencv.tracker filter. * Changed vorbis module to no longer be deprecated. * Improved colorspace conversions in the avformat module. * Fixed audio artifacts on initial seek to in point in avformat producer. * Fixed the colorspace of the cached image in avformat producer. * Fixed white video flashes on property changes in the qtext filter. * Fixed a crash in the rotoscoping filter with large spline deviations. * Fixed a crash in the sdi consumer if the driver is not loaded. * Improved support for a video clip as luma producer to the luma transition. * Fixed a crash in the matte transition. * Fixed a crash when using invert property =1 in the composite transition. Other * Added a Dockerfile and integrated docker build into Travis CI. * Added more avformat consumer presets: - intermediate/DNxHR-HQ - intermediate/ProRes HQ - ALAC - FLAC * Fixed some parameters in the XDCAM and D10 avformat presets. * Fixed link failure on some CPU architectures. Version 6.18.0 - November 11, 2019 This version is a general maintenance release with a bunch of fixes, improvements, and additions. Framework * Fixed some data races in mlt_consumer, mlt_deque, and mlt_property. * Fixed the mlt_events listener incorrect owner argument. * Added support for the LC_ALL environmant variable on Windows. * Fixed the argument to mlt_factory_init() not working on Windows. * Fixed mlt_service_identify() not reliable in some use cases. * Added some default and copy constructors and assignment operators to mlt++ - Filter() - Filter( const Filter &filter ) - Filter& operator=( const Filter &filter ) - Producer( const Producer &producer ) - Producer& operator=( const Producer &producer ) - Properties( const Properties &properties ) - Properties& operator=( const Properties &properties ) - Service( const Service &service ) - Service& operator=( const Service &service ) - Transition() - Transition( const Transition &transition ) - Transition& operator=( const Transition &transition ) * Added mlt_luma_map: - mlt_luma_map_init - mlt_luma_map_new - mlt_luma_map_render - mlt_luma_map_from_pgm - mlt_luma_map_from_yuv422 * Fixed preset overrides depend on the XML attribute order. * Fixed serializing an animated property with a new length. Modules * Fixed interpolation in rotoscoping filter. * Fixed crop filter not working with color producer. * Fixed some data races in the sdl and sdl2 consumers. * Fixed some data races in the avformat producer. * Added a movit.flip filter to the opengl module. * Fixed using filters on frei0r producers. * Added support for in and out attributes on the "consumer" xml element. * Fixed using an in point with the multi consumer. * Fixed avfilter fails if the image size changes. * Fixed showing superfluous decimals for seconds in the timer filter. * Stop serializing an invalid producer as an "INVALID" text producer in xml. * Fixed an access violation crash in wave filter. * Added the meta.media.color_range property to the avformat producer. * Fixed full range yuv422p not converted correctly in the avformat producer. * Fixed the text filter not working with pango. * Fixed a regression using dynamictext with pango. * Added a position property to avfilter for filters that need position info. * Fixed avfilter.subtitles not using the source position. * Added an analyze property to vidstab filter. When set, analysis only starts and the results file written if true. * Fixed crash combining affine the affine filter with the shape filter. * Added interlace detection from AVCodecContext.field_order. * Changed the avformat producer to not use the rescale.interp frame property. Previously, when interp == nearest, it would relax seeking. Now, seek accuracy is reduced during trick play (rewind or fast forward). * Fixed sws flags for auto-inserted scalers in avfilter. * Fixed a double free crash in ladspa filter on channel count mismatch. * Refactored the composite and luma transitions to use mlt_luma_map. * Refactored the pgm producer and shape filter to use mlt_luma_map. * Refactored the lumas module to use mlt_luma_map. * The lumas module is now disabled by default and must be explicitly enabled. * Added property animation to the threshold filter. * Added a cairoblend_mode filter to the frei0r module to affect a frei0r.cairoblend transition used to composite/blend tracks. * Added support for new vaapi options to the avformat consumer: - connection_type: x11 or drm - driver - kernel_driver * Fixed the timewarp producer with a colon in the filename. * Fixed a relative file name with a colon in it in the xml producer. * Fixed defaulting to album or poster art if there is another video stream. * Fixed parameter animation in frei0r plugins when using frame threads. This change also enables frame-threading for more plugins. * Improved the qtblend filter to not process alpha if no transparency. * Added a background_color property to the qtblend filter. * Fixed the opencv.tracker incorrect behavior on cut clips. * Changed opencv.tracker to store absolute frame numbers. * Fixed incorrect frame offset on render in opencv.tracker. * Add an alpha_over property to luma transition. This addresses a behavior regression in version 6.14.0. * Fixed noimagecache not working in the avformat producer. Other * Mlt++ now requires C11 compiler support. * Fixed closing melt SDL2 window from window manager (i.e. close button). * Added -repository option to the melt command. * Added unit tests for Mlt::Event. * Fixed returning image data for Python 3. * Switch to python3 by default. * Updated the prores encoding presets to set vendor ID and colr atom. * Added a CMake build system. This is not yet prefered over the existing configure script and Makefiles and has less flexibility. It is a start and has limited support. Version 6.16.0 - May 7, 2019 This version is released to facilitate packaging the latest version of Shotcut, which is using new APIs. Framework Added functions to get/set a creation date to a producer: - mlt_producer_get_creation_time() - mlt_producer_set_creation_time() - Mlt::Producer::set_creation_time() - Mlt::Producer::get_creation_time() Modules * Fixed dance filter not showing when lower track is transparent. * Refactored dynamictext filter to use mlt_producer_get_creation_time(). * Marked frei0r rgsplit0r plugin version < 1.1 as not thread-safe. * Fixed possible null pointer crash in mlt_properties_serialise_yaml. Version 6.14.0 - March 30, 2019 This version is mostly fixes plus a few API additions and filters. Framework * Added mlt_profile_lumas_dir(). * Added mlt_frame_get_unique_properties(). * Added mlt_playlist_reorder() and Mlt::Playlist::reorder(). * Added some new convenience constructors to mlt++ - Producer(mlt_profile profile, const char *id, const char *service = NULL) - Consumer(mlt_profile profile, const char *id , const char *service = NULL) - Transition(mlt_profile profile, const char *id, const char *arg = NULL) - Filter(mlt_profile profile, const char *id, const char *service = NULL) - Tractor(mlt_profile profile, char *id, char *arg = NULL) * Added Mlt::Transition::connect(Service&). * Added unit tests for mlt_playlist. * Fixed a crash on invalid transition track values in mlt_transition. * Fixed a deadlock regression in v6.12.0 of mlt_consumer when starting from a paused state (producer speed=0). Modules * The avformat module now requires at least FFmpeg v2.4 or Libav 12. * Added mask_start and mask_apply filters to the core module. * Added qtext filter to qt module. * Changed dynamictext and timer filters to use qtext. * Fixed number of digits for seconds in timer filter. * Added mlt_image_format property to color producer. * Improved color accuracy of libswscale RGB->YUV conversion. * Fixed frei0r producer not working with tractor. * Fixed decklink consumer stalling on dropped frames. * Generate lumas for 16:9, 9:16 (vertical), and square aspect ratios. * Fixed crash in qimage when alpha_size is zero. * Fixed the mlt_consumer channels property not being passed to multi consumer. * Fixed the shape filter for full range color and crashes. * Converted the shape filter to use mlt_animation. * Added a use_mix property to the shape filter. * Fixed invert=1 and mix=100 gives wrong image in shape filter. * Fixed a possible free null pointer in the linsys sdi consumer. * Fixed using destroyed temporary object in qimage. * Fixed a possible null pointer dereference in the spot_remover filter. * Fixed memory leak on swr_convert() failure in swresample filter. * Fixed possible null pointer dereference in affine when not using rect. * Fixed loading image sequence on Windows in qimage. * Fixed some null pointer crashes using Movit opengl services. * Fixed sdl2 consumer crashes during initialization on Linux or BSD. * Fixed distorted image using melt_file. * Fixed qimage build on Qt version < 5.5. * Added offset property to the timer filter. * Changed the boxblur hori & vert properties' minimum to 0. * Fixed crash in duplicate frame on rotated videos. * Added automatic scaling and padding to avfilter. * Fixed field order when encoding progressive as interlace. * Fixed frei0r plugins to use the number of slices from the threads property. * Fixed over compositing with transparent clips in luma transition. * Added sliced processing to dissolve-with-alpha using the threads property. * Added createdate keyword to dynamictext filter. * Fixed possible crash changing audio_index in avformat producer. * Fixed small memory leaks in xml consumer, jackrack, and timewarp producer. * Fixed compiling opencv module with OpenCV > 3. Other * Added vertical video profiles: - vertical_hd_30 - vertical_hd_60 * Mlt++ now requires C++11 compiler support. * Added --disable-windeploy to configure to keep bin & lib folders on Windows. * Added support for consumer in & out to melt. * Fixed color accuracy of lossless/Ut Video preset and use pix_fmt yuv422p. * Fixed x264 lossless preset to use crf=0. * Fixed compiling with mingw32. * Fixed build with Python 3. Version 6.12.0 - November 26, 2018 This version has many important fixes plus a few new filters and support for encoding using VA-API. Framework * Changed buffer property to be mutable and adaptive to speed property in mlt_consumer. * Changed macOS RELOCATABLE build to use standard app bundle layout: - lib/mlt -> ../PlugIns/mlt - lib/frei0r-1 -> ../PlugIns/frei0r-1 - lib/ladspa -> ../PlugIns/ladspa - share/mlt -> ../Resources/mlt - share/movit -> ../Resources/movit * Fixed a_track of transitions matching deleted track in mlt_tractor_remove_track(). * Fixed multi-thread race crash in mlt_properties_clear(). * Fixed possiblle null pointer crash in mlt_property_get_rect() and mlt_property_get_time(). * Fixed non-animated strings containing ';' or '=' in mlt_animation_parse(). * Fixed crash in clear_property() with mlt_animation. Modules * Added a generic text filter to the plus module. * Added a timer filter to the plus module. * Added audio timeout handling to sdl2 consumers. * Added spot_remove filter to the plus module. * Added dds, ico, and webp filename extensions for qimage producer. * Added support for color_range property in avformat consumer: "pc" or "jpeg" for full range, otherwise limited range. * Added a window property to the audiowaveform filter. * Added MM:SS.SS to the timer filter. * Added query string param "multi" to the xml producer to force using the multi consumer. * Improved WebP image support in avformat producer. * Integrated hwupload filter in avformat consumer if using VAAPI codec. * Changed count producer to use pango if qtext not available. * Changed qt moduled to not call XInitThreads() * Changed color producer to only set alpha on frame if rgb24a requested or not opaque. * Changed the xml producer to pass quality and performance parameters to the multi consumer. * Fixed sdl2_audio distortion (regression in v6.10.0). * Fixed dynamictext filter to not error on empty text. * Fixed dynamictext aliased (regression in v6.10.0). * Fixed qimage outputs premultiplied if scaled internally. * Fixed crash in cbrts consumer if running property was never set. * Fixed rendering edges of some typefaces in qtext producer. * Fixed qimage fails to load with wrong filename extension. * Fixed affine dark right and bottom edge artifacts regression in (v6.10.0). * Fixed support for vp8 and vp9 with alpha channel in avformat producer. * Fixed interpolation mode selection in qimage producer. * Fixed crash in qimage with alpha channel. * Fixed some AAC MP4 files start playing from middle in avformat producer. * Fixed crash in avfilter if initialization fails. * Fixed crash in mix when frame rate is very low. * Fixed crash on missing luma file in composite transition. * Fixed A/V sync on some files in avformat producer. * Fixed seeking on audio filter with album art in avformat producer. * Fixed colorspace conversion in avformat consumer. Other * Added more avformat consumer presets: - alpha/Quicktime Animation - alpha/vp8 - alpha/vp9 - alpha/Ut Video - lossless/Ut Video * Added square video profiles: - square_1080p_30 - square_1080p_60 * Added support for nodejs to the swig bindings. * Changed configure script to require opencv module be explicitly enabled. * Numerous spelling fixes in source code and comments thanks to codespell. Version 6.10.0 - July 2, 2018 This version fixes bugs and supports serializing animation keyframes with a specified time format (previously only frame number). Framework * Reverted mlt_pool change in v6.8.0 pending further testing. (USE_MLT_POOL compiler define is now a 0/1 boolean, defaults to 1.) * Fixed crash regression in v6.8.0 "parsing non-animated string as an animation." * Added pointer checks to mlt_animation. * Changed producer cache size heuristic in mlt_multitrack to be more liberal. * Fixed handling reserved characters in names for YAML in mlt_properties. * Added clamping to prevent computing negative in and out points to mlt_producer. * Added functions to serialize animation with a time format: - mlt_animation_serialize_cut_tf() - mlt_animation_serialize_tf() - mlt_property_get_string_tf() - mlt_property_get_string_l_tf() - mlt_properties_get_value_tf() - Mlt::Properties::get(int, mlt_time_format) - Mlt::Animation::serialize_cut(mlt_time_format, int, int) * Added functions to clear a property to mlt_properties: - mlt_property_clear() - mlt_properties_clear() - Mlt::Properties::clear() Modules * Fixed enabling sliced pix_fmt conversion in avformat producer. * Fixed incorrect seek and sync on audio files with discard packets. * Added support for avcodec_send_frame() API to avformat consumer. * Fixed compile errors with Libav master. * Fixed a crash in affine transition. * Fixed a crash in ladspa filters when consumer frame rate is low (e.g. <= 8). * Fixed a crash in boxblur filter. * Added animation support to boxblur hori and vert properties. * Fixed a crash in movit.convert. * Fixed incorrect alpha in affine transition blending routine. * Converted frei0r from deprecated mlt_geometry to mlt_animation API. * Fixed tilde in text string for pango producer. * Fixed using more than one channelcopy filter. * Fixed the mono filter reducing volume level. * Fixed degraded audio scrubbing in sdl2_audio consumer. * Converted dynamictext filter to use affine transition for more correct alpha compositing and sub-pixel positioning. * Added time format support for animation keyframes to the xml consumer. * Added animation support to more affine transition properties: - fix_rotate_x - fix_rotate_y - fix_rotate_z - fix_shear_x - fix_shear_y - fix_shear_z - ox - oy - scale_x - scale_y * Fixed gaps in text when characters overlap in qtext and kdenlive producers. * Fixed a crash in pixbuf producer with multiple render threads. * Converted the oldfilm vignette filter from mlt_geometry to mlt_animation. Other * Numerous updates to mlt-xml.dtd. * Categorized many of the encode presets (using meta.preset.name). Version 6.8.0 - May 10, 2018 This version improves support for multi-channel audio and adds some new manipulation functions to the mlt_animation API. Framework * Added support for musl C library. * Added functions for audio channel layouts: - mlt_channel_layout_name() - mlt_channel_layout_id() - mlt_channel_layout_channels() - mlt_channel_layout_default() * Added channel_layout property to mlt_consumer. * Added mlt_channel_layout enum. * Disabled memory pooling by default and require compile macro USE_MLT_POOL to re-enable it. * Fixed reliability of keyframed properties serializing properly. * Fixed parsing non-animated string as an animation. * Added more functions to mlt_animation: - mlt_animation_key_set_type() - mlt_animation_key_set_frame() - Mlt::Animation::key_set_type() - Mlt::Animation::key_set_frame() Modules * Fixed some crashes in qimage producer especially with alpha channel. * Fixed >2 channel audio output in the SDL consumers. * Fixed >2 channel audio output in the rtaudio consumer on Windows. * Fixed vorbis encoding with FFmpeg v3.4+. * qimage and qtext are now higher priority than gtk2 pixbuf and pango by the loader producer. * Added support for more channel counts to decklink consumer. * Added swresample filter based on libswresample from FFmpeg. This is now the preferred channel count normalizing filter used by the loader producer. * Fixed the strange "Undefined constant" and "Unable to parse option value" log messages in the the avformat consumer. * Fixed GIF and DPX writing in avformat consumer. * Reduced the memory usage of the affine transition and filters. * Fixed a crash in kdenlivetitle producer. * Fixed a crash in the rotoscoping filter. * Fixed frame rate reported in Matroska and WebM files produced by the avformat consumer. * Added sdl2_audio consumer. * Fixed alpha channel support for more pixel formats in the avformat producer. * Converted the affine transition to use mlt_rect and mlt_animation. * Fixed LADSPA plugins with mono channel audio. Other * Fixed a melt command line parsing bug when argument supplied to -transition. * Fixed melt with SDL2 on Windows not using stdio and stderr. * Improved speed of the vp9 avformat consumer preset. Version 6.6.0 - January 22, 2018 This version builds upon the previous release with performance improvements using the sliced image processing framework. It also improves compatibility with dependencies (FFmpeg, Qt 5, SDL 2, NDI, OpenCV, libebur128). Framework * Added a thread pool to mlt_slices: - mlt_slices_run_normal() - mlt_slices_run_rr() - mlt_slices_run_fifo() - mlt_slices_count_normal() - mlt_slices_count_rr() - mlt_slices_count_fifo() - MLT_SLICES_COUNT environment variable * Added mlt_log_timings_now() to mlt_log. * Added mlt_image_yuv422p16 image format. * Added mlt_image_format_planes() and mlt_image_format_id() to mlt_frame. * Added mlt_service_disconnect_all_producers() and Mlt::Service::disconnect_all_producers() * Fixed accuracy of mlt_time_format conversions. * Fixed mlt_filter_get_progress() never reaching 1.0. * Fixed divide by zero in mlt_filter_get_progress(). Modules * Added sdl2 module! * Added sum property to mix transition for simple mixing without dynamics. * Added TLD and KCF tracking modes to opencv module. * Added CSV (comma-separated values) simple slideshow to pixbuf producer. * Added jack filter to jackrack module. * Added sliced processing to frei0r. * Added sliced processing to composite transition. * Added sliced processing to avformat producer pixel format conversion. * Added sliced processing to affine transition and filter. * Converted volume and pan filters to floating point. * Added rotate_center property to qtblend transition and filters. * Added meta.media.variable_frame_rate (VFR detection) to avformat producer. * Added support for external libebur128. * Updated internal libebur128 to latest. * Added 10-bit capture to decklink producer. * Added mlt_image_yuv422p16 to fieldorder filter, avcolour_space filter, and avformat consumer. * Added no_profile property to xml consumer. * Various ndi module fixes and improvements. * Fixed setting color_trc in multi consumer. * Fixed kdenlive title transparency. * Fixed incorrect alpha channel breaking freeze filter with qtblend transition. * Fixed transparency of pixbuf images with qtblend transition. * Fixed some crashes in new qtblend transition. * Fixed building qt module with C++11 for Qt 5.7+. * Fixed reporting accurage length for image sequences in qimage and pixbuf. * Fixed extra audio samples added to end of file in avformat consumer. * Fixed rawvideo export in avformat consumer. * Fixed multi-threading with FFmpeg 3.2+ in avformat module. * Fixed JPEG album art in avformat producer. * Removed filter_avresample (dropped by FFmpeg). * Stop flushing audio buffer in decklink consumer. * Fixed field order handling in decklink consumer. * Make composite transition field order aware. * Fixed nesting mlt_tractor with opengl module. * Fixed kdenlive titler crash on resized frame. Other * Various memory leak fixes. * Fixed opening files with extended chars on Windows (framework and modules). Version 6.4.1 - November 15, 2016 Hot fix for new C++ symbol Mlt::Profile::is_valid() not declare const in symbol versioning. This was breaking script bindings. Version 6.4.0 - November 11, 2016 This is both a bugfix and enhancement release: Framework * Added functions for multi-threaded slice-based image processing: mlt_slices_init, mlt_slices_close, and mlt_slices_run. * Added Mlt::Profile::is_valid(). * Added MLT_DIRLIST_DELIMITER to mlt_types.h. * Renamed mlt++/config.h to mlt++/MltConfig.h. * Fixed mlt_properties_set_lcnumeric() on macOS. * Fixed address of Free Software Foundation in comment headers. Modules * Added crop_to_fill property to composite transition. * Added sliced_composite property to composite transition. * Added peak and true peak properties to loudness_meter filter. * Added qtblend transition and filter to qt module. * Added ndi (NewTek NDI) module with producer and consumer. * Added opencv module with opencv_tracker filter. * Added line_spacing, stretch, wrap_width, and wrap_type properties to pango producer. * Added oblique value for style property to pango producer. * Added fontmap-reload event to pango producer. * Added support for pkg-config to sdl module. * Added .kra (Krita Image) file name extension to loader.dict. * Improved performance of kdenlivetitle producer. * Improved decklink producer and consumer. * Improved accuracy of seeking on lossy compressed audio in avformat producer. * Improved mix transition using 32-bit floating point. * Fixed avfilter when image format changes. * Fixed loading relative file name in vidstab filter. * Fixed crash on Windows with avfilter. * Fixed parsing LADSPA_PATH with semi-colon delimiter on Windows. * Fixed parsing FREI0R_PATH with semi-colon delimiter on Windows. * Fixed reading relative path with backslash (Windows) in xml producer. * Fixed loading relative file name for av.file (avfilter). * Fixed loading multiple LADSPA plugins on some systems. * Fixed compile error when not configured with --enable-gpl. * Fixed loading in avfilter.lut3d in locales with comma decimal point. * Fixed a possible crash in resample filter. * Fixed alpha channel in kdenlivetitle producer. * Fixed possible crash in pixbuf and qimage producers. * Fixed count when counting down in count producer. Other * Moved some avformat presets from lossless to new intermediate folder. * Added a YouTube avformat consumer preset. * Changed metadata.rb metadata publisher to output Markdown. Version 6.2.0 - April 20, 2016 There are no framework changes in this release. The major announcement is the introduction of support for libavfilter! This is still a work-in-progress. It is limited to FFmpeg 2.3 and up, and there are a number of filters that are black-listed because they are known to not integrate with MLT, which is not a full libav* environment or simple wrapper for it. There are likey avfilters that are not yet black-listed but might not work because they have not been completely tested. Also, they do not support MLT's keyframable property animation nor its frame-threaded parallelism due to architectural or integration limitations. However, some avfilters are slice-threaded (internal parallelism), and that works. Finally, libavfilter filtergraph syntax is not supported either. All of the supported libavfilters are exposed as MLT filters beginning with the prefix "avfilter." All of the avfilter parameters are exposed as MLT properties with the "av." prefix to prevent clashes with MLT properties. You can run `melt -query filters` to see the new avfilters, and `melt -query filter=avfilter.rotate`, for example, to view generated documentation for an individual filter. Here is a list of notable fixes and enhancements in this release: * Added support for libavfilter to avformat module. * Added auto-rotate support to avformat producer. * Added animated GIF preset for avformat consumer. * Prevent serializing and deserializing mlt_type property to xml module. * Fixed relative paths for WebVfx "plain:" resources in xml module. * Updated libebur128 to v1.1.0 in plus module. * Added dynamic_loudness filter to plus module. * Added loudness_meter filter to plus module. * Qt 5 fixes for kdenlivetitle producer. * Added gradients and text shadows to kdenlivetitle producer. * Added support for building rtaudio against external build of lib. * Upgraded bundled RtAudio to v4.1.2. * Added status parameters to ladspa producer and filters. * Added 5.1 surround to stereo downmix to audiochannels filter in core module. * Fixed compiling SWIG bindings for Ruby 2.0. Version 6.0.0 - February 17, 2016 This is a bugfix and minor enhancement release. Note that our release versioning scheme has changed. We were approaching 1.0 but decided to synchronize release version with the C library ABI version, which is currently at v6. Here are some of the notable changes and enhancements: Framework * Added unit tests for tractor, multitrack, and field. * Deprecate mlt_frame_get_alpha_mask(). * Added drop_count readable property to mlt_consumer. * Added mlt_factory_repository(). * Added mlt_properties_to_utf8(). * Define MIN, MAX, CLAMP in mlt_types.h in not already defined. * Switched to __APPLE__ and _WIN32 defines throughout codebase. Modules * Added UDP and SMPTE 2022-2 support to cbrts consumer. * Fixed build against latest FFmpeg versions - now requires v1.1 and up. * Added audiospectrum filter to qt module. * Added meta.media.0.codec.rotate property to avformat producer to let apps and other services get the media orientation. * Make the avformat producer handle animated images. * Added style property to dynamictext filter. * Added timewarp producer to core module. * Fixed slowly accumulating A/V sync drift in mix audio transition. * Added width_crop and width_fit properties to pango producer. Melt * Added -abort option to simply exit without full cleanup. * Fix key-press handling on Windows. Version 0.9.8 - July 29, 2015 Here is a list of the enhancements included in this release. There are many API additions to support the ability for apps to add and remove tracks and represent and manipulate property animations. There are still many services, however, that need updating to support animated properties. Framework * Added mlt_service_disconnect_producer() and Mlt::Service::disconnect_producer(). * Added mlt_multitrack_disconnect() and Mlt::Multitrack::disconnect(). * Added mlt_tractor_remove_track() and Mlt::Tractor::remove_track(). * Added mlt_service_insert_producer() and Mlt::Service::insert_producer(). * Added mlt_multitrack_insert() and Mlt::Multitrack::insert(). * Added mlt_tractor_insert_track() and Mlt::Tractor::insert_track(). * Added mlt_transition_set_tracks() and Mlt::Transition::set_tracks(). * Added Mlt::Properties::get_animation(). * Added Mlt::Properties::get_anim(). * Added Mlt:Animation class with methods: - length() - is_key() - keyframe_type() - get_item() - next_key() - previous_key() - set_length() - remove() - interpolate() - serialize_cut() * Added mlt_animation_key_count() and Mlt::Animation::key_count(). * Added mlt_animation_key_get() and Mlt::Animation::key_get(). Modules * Added audiowaveform video filter. * Added fft audio filter. * Added dance video filter (uses fft). * Added lighshow video filter (uses fft). * Added distort property to movit.rect video filter. * Added rotate property to pango video producer. * Added 2K DCI and 4K modes to decklink producer and consumer. * Added audiomap (channel remapping) filter. * Added property animation to all LADSPA audio filters and producers. Version 0.9.6 - March 1, 2015 A major regression slipped into the 0.9.4 release plus some other good fixes rolled in just after that release prompting this new release. Please discontinue using version 0.9.4 and upgrade. Version 0.9.4 - February 15, 2015 This is a bugfix and minor enhancement release. Here are some of the notable changes and enhancements. Framework * Added color_trc (transfer characteristic) property to mlt_consumer and mlt_frame. * Added Mlt::Profile::set_display_aspect(int, int). * Added mlt_pool_stat(). * Added mlt_smpte_df and mlt_smpte_ndf to mlt_time_format for non-drop-frame timecode support. * Added Mlt::Tractor::Tractor(Mlt::Profile&). * Added mlt_frame_get_alpha(). * Added default, copy, and assignment methods to Mlt::Frame. * Added Mlt::Filter::process(Mlt::Frame&). Modules * Added support for color_trc property to avformat and opengl modules. * Performance improvements for composite and matte transitions. * Added fill, halign, and valign properties to affine transition and filter. * Added producer.* and consumer.* properties to consumer producer. * Fixes for libavformat and libavcodec v56. * Dropped support for FFmpeg < v1.0 and Libav < v9. * Added a lumakey filter. * Added a localtime property to dynamictext filter. * Added date/time format string support to dynamictext filter. * Added no_root property to xml consumer. * Added audio-only tone producer. * Added drop property to count producer. * Added caching to pango producer to improve performance. Other * Added WMV and WMA avformat consumer presets. * Added a ProRes-Kostya avformat consumer preset. * Changed VP9 WebM preset to use Opus audio codec. * Added 4K UHD and 2.5K QHD profiles. * Added x265-medium and x265-medium-pass1 avformat consumer presets. * Added a unit test for Mlt::Frame. Version 0.9.2 - June 29, 2014 This is a bugfix and minor enhancement release. Framework * Added "boolean" parameter type and "argument" parameter attribute to service metadata schema. * Added mlt_properties_frames_to_time(). * Added mlt_properties_time_to_frames(). * Changed mlt_events_fire() to return the number of listeners. * Added consumer-thread-create and consumer-thread-join events. * Added LC_NUMERIC handling for Windows. * Added mlt_playlist_mix_out() and mlt_playlist_mix_in() * Added mlt_properties_from_utf8() Modules * Renamed "qimage" module to "qt". * Added support for Qt 5. * Added qtext producer, which is now preferred by dynamictext filter over pango. * Added xml-nogl consumer. * Consolidated dgraft, burningtv, and rotoscoping into new plusgpl module. * Added vid.stab module with vidstab and deshake filters. This depends on external vid.stab library unlike its predecessors. * Rewrote opengl module. * Added loudness filter based on EBU R128. * Added rgblut filter to plus module. * Added luma-only liftgammagain filter to plusgpl module. * Added lift_gamma_gain filter to plus module. * Added support new keyframable animated properties to brightness, volume, panner, boxblur, wave, sepia, charcoal, burn, gamma, grain, dust, lines, tcolor, oldfilm, * Added movit.luma transition to opengl module. * Added cbrts consumer to plusgpl module. * Removed the "ppm" PPM-pipe producer. * Added more VITC functionality to decklink module. * Added matte transition to core module. DEPRECATION WARNINGS * Deprecate videostab module with videostab and videostab filters. This will not be removed soon in order to keep old scripts and projects functional. * Deprecate the dv (libdv), kino, and vorbis modules. These are scheduled to be removed in next release. Version 0.9.0 - June 2, 2013 This is a significant enhancement release. Build (especially interesting for Linux packagers) * Added --rename-melt and --enable-extra-versioning configure options. * Added symbol versioning on Linux. Framework * Improved pause behavior when using buffered rendering in mlt_consumer. * Added mlt_animation API (exposed via Mlt::Properties in C++). * Added mlt_rect and mlt_color types. * Deprecated mlt_geometry API. Modules * Support for the latest versions of FFmpeg and Libav (but dropping support for 0.5 and 0.6 versions). * Added alpha channel output to avformat consumer. * Added reconnect and exit_on_disconnect properties to avformat producer. * Added opengl module that uses Movit for GLSL image processing. * Added qglsl consumer to use opengl with avformat, sdi, and decklink. * Added avsync module with blipflash producer and consumer for testing. * Added new "count" producer to gtk2 module. * Changed frei0r to use index-based property names making it impervious to param name changes (param name still accepted for compatibility). * Added default parameter values to frei0r metadata. Other * Added more python example web services. * Added the beginnings of unit test suite. Version 0.8.8 - January 20, 2013 This is purely a bugfix release. See the ChangeLog or git log. Version 0.8.6 - November 14, 2012 This is a re-issue of the 0.8.4 release with a fix for a performance regression on videos that use full-range colorspaces such as yuv420p. Version 0.8.4 - November 13, 2012 This is a bugfix and minor enhancement release. * Added playlist-next event and PlaylistNextListener to Ruby binding * FFmpeg 1.0 and libAV master compatibility * Improvements to motion_est filter to generate keyframes for apps * Added audiolevel (measurement) filter Version 0.8.2 - August 28, 2012 This is a bugfix and minor enhancement release. * Overhaul of A/V sync with libavformat-based inputs. * Fix a major memory leak introduced in previous release. * Fixes to problems revealed by Coverity Scan static analysis. * Improved encoding presets. * melt can now be built without SDL with define MELT_NOSDL, which is handy for running it as a child process on Windows and OS X. * melt can now be signaled to quit, which also makes it more useful as a child process. Special thanks to Mikko Rapeli who provided many of the Coverity fixes. Version 0.8.0 - June 1, 2012 The minor version is increased due to the addition of time properties! The soname version increased in the process because some mlt_property functions changed; however, very few if any apps actually directly use mlt_property preferring to use mlt_properties instead. In addition: * improve seek speed on AVCHD when using FFmpeg v0.9.1+ (NOT Libav!) * composite and dissolve speed improvements on x86-64 * improve performance of caching in image producers * add device enumeration to decklink producer and consumer Special thanks go to contributors Maksym Veremeyenko and Ed Rogalsky. Version 0.7.8 - February 13, 2012 This is a bugfix and minor enhancement release. * Improved support for v53 of libavcodec/libavformat * Added "multi" consumer - multiple, simultaneous outputs * Added framerate adaption to "consumer" producer and "multi" consumer * Can now use YADIF deinterlacer with decklink producer * Added "rtaudio" consumer for native audio support on multiple platforms * Added ability to request image format closest to source (mlt_image_none) * Added more audio formats * Added vqm (video quality measurement) transition Version 0.7.6 - October 31, 2011 This is a bugfix and minor enhancement release. * Improved support for v53 of libavcodec/libavformat (0.7 and 0.8 releases) * Major DeckLink consumer improvements * Much more metadata * Added audio-only JACK consumer * Added video stabilization filters * Added dual pass audio normalization to sox filter * Added VITC and VANC capture to DeckLink producer * Added support for writing timecode tracks * Added MLT, frei0r, and SoX version to xml serialization * Added D-10, XDCAM, DNxHD, and Sony-PSP encoding presets * Can now use rotoscoping for masking filters * Added dynamictext filter makes burned-in timecode and similar easier * Added support for consumer element in MLT XML * Added outlining, padding, and alignment to pango filter Special thanks go to contributors Maksym Veremeyenko, Brian Matherly, and Marco Gittler. Version 0.7.4 - July 16, 2011 This is a bugfix and minor enhancement release. Framework * Important: change consumer property profile to mlt_profile. * Improve frame-dropping and drop_max property to mlt_consumer. * Added support for presets for any service through special property named "properties" * Added mlt_profile_from_producer() for auto-profile. * Added mlt_properties_set_lcnumeric() and mlt_properties_get_lcnumeric(). * Added LC_NUMERIC to YAML Tiny metadata schema and parser. Modules * Added support for more than 2 channels of greater than 16-bit audio. * Added discrete filters for each SoX effect. * Added discrete filters for each LADSPA audio plugin. * Added automatic service metadata for SoX and LADSPA plugins. * Added at least basic metadata for nearly every service. * Added support for decklink on Windows (tested) and Mac OS X (untested). * Added support for JACK transport synchronization. * Added blacklist.txt to jackrack plugin (contains dssi-vst). * Rewrite of decklink consumer. * Added support for live network, multi-stream device, and pipe/fifo sources in avformat producer. * Added LC_NUMERIC attribute to root XML element. Melt * Added '-query presets' option. * Added -jack option for transport synchronization. * Send -help and -query output to stdout to make it convenient for pagers. Other * Added mlt.Frame.get_image() for Python. * Removed configure option --avformat-svn. * Fixes for locales that use comma for decimal point. * Added presets for DVD, DV, x264, and WebM encoding. Since FFmpeg forked and there were a few releases, there is no recommended version at this time. With that said, there were changes to accommodate the API changes with some moderate testing of the 0.6, 0.7, and 0.8 series releases of both FFmpeg and Libav. Version 0.7.2 - May 1, 2011 This is a minor release to fix a few things between the 0.7.0 release and the release of Kdenlive 0.8. I recommend Kdenlive v0.8 users to upgrade to this version of Mlt. Beyond that there are some exciting additions to the Blackmagic Design DeckLink plugin! Framework * Added mlt_profile_list(). Modules * Added decklink producer (i.e. capture, live encoding). * Added keyer output for decklink consumer. * Added AVOptions to the avformat service metadata. * Added support for new major API versions (53) of FFmpeg. Melt * Added '-query profile' option. * Added '-query formats', '-query audio_codecs' and '-query video_codecs'. The recommended version of FFmpeg for use with this release is 0.6.1. Version 0.7.0 - March 27, 2011 This is a major new release due to significant additions to API, framework, and build. Build * Added support for Windows via MinGW. * Enabled linsys module by default. * Disabled VDPAU by default and added --avformat-vdpau to enable it. * Added support for swfdec 0.7. Framework: * Added parallelism to mlt_consumer when 'real_time' > 1 or < -1. * Added mlt_deque_insert() and mlt_deque_peek(). * Added mlt_profile parameter to mlt_producer_new(). * Let transitions with no out point run forever. * Added mlt_frame_unique_properties(). * Added mlt_frame_set_image() and mlt_frame_set_alpha(). * Added mlt_image_format_size() and mlt_audio_format_size(). * Added mlt_filter_get_length() and mlt_transition_get_length(). * Added mlt_filter_get_progress(), mlt_transition_get_progress(), and mlt_transition_get_progress_delta(). * Added mlt_filter_get_position() and mlt_transition_get_position(). * Added mlt_properties_lock() and mlt_properties_unlock(). Modules * Added rotoscoping filter. * Improve libavdevice support (V4L2, ALSA, libdc1394). * Added support for new FFmpeg metadata API. * Various fixes, refactoring, and improvements. The recommended version of FFmpeg for use with this release is 0.6.1. Version 0.6.2 - January 23, 2011 This is just a minor release to address a few things prior to introducing major changes from other branches. * Added force_aspect_ratio property to pixbuf and qimage producers. * Added opacity handling in geometry property of the affine filter and transition. * Added use_normalised property to affine filter. * Added always_active property to affine transition. * Fix building on NetBSD. The recommended version of FFmpeg for use with this release is 0.6.1. Version 0.6.0 - January 1, 2011 The recommended version of FFmpeg for use with this release is 0.6.1. There were quite a few enhancements and changes including a minor interface change. Therefore, this release gets a jump in the versioning. Framework * mlt_profile - Added (Y'CbCr) colorspace attribute. - Added mlt_profile_clone(). * Added mlt_consumer_position() and Mlt::Consumer::position(). * Added Mlt::Properties::wait_for(string). * Added a version API: mlt_version_get_int() and others. * Added Mlt::Producer::pause(). * Added mlt_frame_write_ppm() for debugging. Melt * Added automatic detection of profile from first input when profile not specified. * Now exits with error result when consumer fails fatally. Modules * Added swfdec producer for Flash files including variables support. (Does not support audio.) * Added consumer for Blackmagic Design DeckLink SDI and Intensity HDMI. * Added Y'CbCr colorspace conversion and option to use full luma range. * Added (de)serialization of profile to XML. * Added support for #frame# variable to the data_show filter. * Added support for frei0r string parameters. * Make FFmpeg formats and codecs available as properties (not just stderr). * Try to load .xml file as MLT XML. * Added a consumer-sdl-paused event to sdl_preview. * Added a consumer-fatal-error event to avformat. * Change composite transition to default to progressive rendering; field- based rendering available only explicitly. Version 0.5.10 - September 13, 2010 This is a quick followup to the 0.5.8 release to address an issue I want to address immediately. I noticed an extra unconditional colorspace conversion to and from RGB was added for all YCbCr (YUV) video sources. In addition, I have enabled the avcolor_space filter on OS X since it works now. Version 0.5.8 - September 12, 2010 The recommended version of FFmpeg for use with this release is 0.6. This is a maintenance release to address some bugs that appeared thus far in the 0.5.x series. Beyond that is a few enhancements: * Added EXIF-based auto-rotation of images to pixbuf and qimage producers. * Improved quality of libswscale-based image conversion and scaling. * Added channelswap and panner audio filters; panner also does balance. * Improve audio waveform and add audiowave video filter. * Enhanced luma filter to work with animated filters such as affine. * Automatically crop 8 bottom lines of 1088 source in a 16:9 project (common in Canon EOS digital cameras). * Added support for inline images in kdenlivetitler. Version 0.5.6 - June 20, 2010 The recommended version of FFmpeg for use with this release is 0.6. This is a maintenance release to address some bugs that appeared this far in the 0.5.x series. Beyond that it a few enhancements: * Added interpolation to the affine transition and filter. * Added multi-track audio encoding to avformat consumer. * Added interlaced field rendering to kdenlivetitle producer. Version 0.5.4 - April 19, 2010 The recommended version of FFmpeg for use with this release is SVN r21322. This is another maintenance release to address some bugs that appeared this far in the 0.5.x series. Beyond that it adds two things that only very specific users will see: * Added C# (ECMA CLR) binding (not enabled by default). * Linsys SDI consumer now configures itself from MLT profile. Version 0.5.2 - March 10, 2010 The recommended version of FFmpeg for use with this release is SVN r21322. This is a minor maintenance release, but it is interesting because it now enables usage of libswscale as the default choice for image scaling, image format conversion, and color space conversion. That gives better quality and performance. In addition, there are some improvements in the sdl_preview consumer to make it suitable for use in OpenShot 1.1. Other things: * Fixed mlt++/MltFilteredProducer * Fixed playing to the end in Kdenlive (mantis bug 1207) * Fixed crash load uncompressed video * Fixed compiling yadif for non-sse2 builds Version 0.5.0 - February 15, 2010 The recommended version of FFmpeg for use with this release is SVN r21322. This is an enhancement release, confined mainly to the modules rather than the framework. In particular, this adds support for VDPAU, YADIF, and HD-SDI technologies! configure: added --disable-sse2 framework: * mlt_cache: added mlt_cache_set_size() * mlt_filter: added data property "service" - set when attached * mlt_frame: - added Doxygen docs - added "previous frame" and "next frame" data properties - available when its producer has _need_previous_next=1 * mlt_playlist: added support for negative out point same as length-1 * mlt_service: - added mlt_service_cache_purge() - added "_need_previous_next" handling in mlt_service_get_frame() - added firing event "service-changed" in mlt_service_attach() modules: * avformat producer: - added decoding H.264 with NVIDIA VDPAU Requires FFmpeg built with vdpau. This is automatically detected and enabled. You can disable this by setting environment variable MLT_NO_VDPAU=1 or property novdpau=1. - added caching of FFmpeg contexts and decoded images This allows large numbers of clips in a project avoiding limitations with number of threads and file descriptors permitted per process. You can disable image caching with property noimagecache=1. - added variant of producer named avformat-novalidate - restored support for video4linux(2) * avformat consumer: added apre, fpre, and vpre preset properties * crop filter: added center_bias integer property * deinterlace filter: added the excellent YADIF as a method * kdenlivetitle producer: added text outlining * linsys/sdi consumer: - added support for HD-SDI - changed name from "linsys_sdi" to just "sdi" * oldfilm filter: added "uneven development" effect * xml producer: add support for unspecified out points profiles: * added several missing ATSC (HD) profiles * change descriptions from using Hz to fps Version 0.4.10 - December 8, 2009 The recommended version of FFmpeg for use with this release is SVN r19873. This is "hotfix" for the 0.4.8 release that fixes a couple of regression bugs introduced just before the release. Version 0.4.8 - December 7, 2009 The recommended version of FFmpeg for use with this release is SVN r19873. This is mainly a maintenance release. Besides bug fixes here are other notable changes. modules: * avformat producer: - refactored producer to use much less properties - added support for audio_index=all for linsys_sdi consumer - added force_fps property (does yet not adjust duration) * core/crop: added "center" property to crop filter * linsys_sdi: - added support for >2 audio channels - added property meta.map.audio..channels= - added property meta.map.audio..start= * qimage/kdenlivetitle: add typewriter effect Version 0.4.6 - October 7, 2009 The recommended version of FFmpeg for use with this release is SVN r19873. This release is an enhancement release along with numerous build, A/V synch, concurrency, and other bug fixes. configure: new option --avformat-svn-version modules: * avformat: much improved seeking on H.264/MPEG2-TS (AVCHD) (Ivan Schreter) * core: new imageconvert and audioconvert filters (framework refactorization) * linsys: new SDI consumer (Broadcast Centre Europe) * qimage: new kdenlivetitle producer (J.B. Mardelle and Marco Gittler) * sdl: new audio_only consumer for OS X mlt++ and swig: update bindings framework: * refactored image format conversion mlt_frame.h: - added convert_image() virtual function - added mlt_image_format_name() - removed many mlt_convert_ and scaling/padding functions * refactored audio format conversion mlt_frame.h: - mlt_get_audio() virtual function parameters changed - added convert_audio() virtual function - mlt_frame_get_audio() parameters changed - added mlt_frame_set_audio() - added mlt_audio_format_name() mlt_types.h: - deprecated mlt_audio_pcm - added mlt_audio_s16 - added mlt_audio_s32 - added mlt_audio_float Version 0.4.4 - June 30, 2009 The recommended version of FFmpeg for use with this release is 0.5.0. This release is a minor maintenance update to the 0.4.2 - just build and bug fixes. * new configure script options: --swig-languages --rename-melt --mandir --datadir * added man page for melt (not installed) * added invert property to composite transition * added frei0r plugin blacklist, currently only contains facedetect * added Lua binding via SWIG Version 0.4.2 - May 30, 2009 The recommended version of FFmpeg for use with this release is 0.5.0. This release is a minor maintenance update to the 0.4.0 - just build and bug fixes. Version 0.4.0 - May 17, 2009 The recommended version of FFmpeg for use with this release is 0.5.0. This release is primarily a reorganization of the mlt and mlt++ projects. In brief: * "inigo" was renamed "melt" * "westley" is no longer the XML file extension and root element - they are simply "mlt" now * mlt++ is included with mlt and no longer a separate project * miracle, valerie, and humperdink were moved to a new, separate project For details: http://www.mltframework.org/twiki/bin/view/MLT/ExtremeMakeover avformat producer improvements: * improve audio synchronization * improve reliability of video playback with FFmpeg newer than v0.5.0 * improve seeking performance of DNxHD and HuffYUV Version 0.3.8 - April 15, 2009 The recommended version of FFmpeg for use with this release is SVN r17923. This almost entirely a bugfix release to coincide with the Kdenlive 0.7.3 release. See the ChangeLog (SVN log) for details. framework: * added mlt_cache API * improved doxygen documentation comments * added some 15 fps profiles * improved color property handling (support web-style '#' color value) * add const qualifier to many string parameters modules: * core: improved brightness filter * core: added image crop filter * frei0r: added support for producer/source plugins * frei0r: added support for color parameters * sdl: added window_background color property Version 0.3.6 - February 2, 2009 The recommended version of FFmpeg for use with this release is SVN r16849. This almost entirely a bugfix release to coincide with the Kdenlive 0.7.2 release. See the ChangeLog (SVN log) for details. framework: * added mlt_log logging API * improved doxygen documentation comments avformat module: * consumer: report list of muxers when f=list and codecs when acodec=list or vcodec=list * consumer: added support for an=1 or acodec=none and vn=1 or vcodec=none * producer: list available demuxers and decoders when constructor arg is like f-list[[,]acodec-list][[,]vcodec-list] Version 0.3.4 - December 29, 2008 The recommended version of FFmpeg for use with this release is SVN r16313. This almost entirely a bugfix release. See the ChangeLog (SVN log) for details. There are a few notes: framework: * improved doxygen documentation comments (work in progress) published docs are at http://mltframework.org/doxygen/ avformat module: * added support for AVOption parsing to producer * added filter_swscale as alternative rescaler * added recommended FFmpeg revision to configure option --help * use recommended FFmpeg revision with --avformat-svn on release versions * added configure option --avformat-no-codecs * added configure option --avformat-no-filters misc: * new profile atsc_1080i_50 * added --disable-sse option to configure script * improved build for OS X and x86-64 and improved handling of mmx/sse Version 0.3.2 - November 10, 2008 In addition to bug fixes detailed in the ChangeLog, here is a list of enhancements. framework: * deprecated mlt-config; use pkg-config instead * added more HD profiles modules: * sdl: added fullscreen property * sox: sox v14.1.0 compatibility * gtk: added force_reload property to producer_pixbuf * frei0r: added support for YAML Tiny metadata * frei0r: added keyframe support on double and boolean parameters * oldfilm: added keyframe support for filter_vignette * kdenlive: added filter_freeze inigo: * added -version, -silent, and -progress options * improved output of usage information * removed realtime process scheduling Version 0.3.0 - August 5, 2008 framework: * fix bugs with introduction of mlt_profile in v0.2.4 * added versioning to libs * remove module registry and add dynamic module loading: added mlt_repository_register, mlt_repository_consumers, mlt_repository_filters, mlt_repository_producers, mlt_repository_transitions * new module metadata system based on YAML Tiny: added mlt_repository_register_metadata, mlt_repository_metadata, mlt_repository_languages, mlt_properties_is_sequence, mlt_properties_parse_yaml, mlt_properties_serialise_yaml, and added metaschema.yaml Kwalify schema file * mlt_consumer: added threaded, non-lossy processing when real_time=-1 * added autoclose property to mlt_playlist for sequential processing of very large playlists (prevents resource exhaustion) * mlt_factory_init now returns the global mlt_repository * change mlt_repository_fetch to mlt_repository_create * change mlt_factory_prefix to mlt_factory_directory * added mlt_field_disconnect_service modules: * move all modules from $datadir to $libdir * new oldfilm module by Marco Gittler * new frei0r module by Marco Gittler * new dgraft module by Dan Dennedy for inverse telecine (not ready yet) * avformat: added support for multi-threaded encoding and decoding * consumer_avformat: added support for AVOption to support all ffmpeg options using ffmpeg-style property names * consumer_avformat: added support for dual pass encoding * qimage: added support for Qt4 * sox: added support for sox v14.0.0 * transition_composite: added animatable geometry-type "pan" property to crop and pan instead of automatic down-scale inigo: * added -query option to lookup module metadata * added -profile option and support for progress=1 for kdenlive Version 0.2.4 - August 4, 2007 * framework: new extensible profiles system to replace MLT_NORMALISATION * module avformat: interlaced coding support for ffmpeg/libavcodec * module avformat: build improvements for --avformat-svn * new effectv module with BurningTV video filter * module qimage: added support for psd, xcf and exr images * numerous bugfixes Version 0.2.3 - April 9, 2007 * Addition of kdenlive module * Support for ffmpeg from subversion * Support for ffmpeg libswscale * Copyright and license cleanup Version 0.2.2 - May 27, 2006 * Prepared specifically for the kdenlive 0.3 release. * Contains some patches to support rgb24a output for the gdk-pixbuf and qimage producers as well as some minor bugfixes. Version 0.2.1 - December 5, 2005 * Many improvements since initial releases due to development of Shotcut and Jahshaka editing interfaces. Version 0.1.1 - June 9, 2004 * Minor modifications and bug fixes from the previous release. Better ffmpeg/avformat integration and more reliable playback. Version 0.1.0 - May 6, 2004 * First official release mlt-7.38.0/README.md000664 000000 000000 00000010354 15172202314 013644 0ustar00rootroot000000 000000 # MLT FRAMEWORK MLT is a LGPL multimedia framework designed for video editing. This document provides a quick reference for the minimal configuration, build and installation of MLT. See the `docs/` directory for usage details. See the [website](https://www.mltframework.org/docs/) for development details and a [contributing](https://www.mltframework.org/docs/contributing/) guide. --- ## Development Environment (Dev Container) This repository provides a standardized development environment using the [Development Containers](https://containers.dev/) specification. It is compatible with **VS Code**, **CLion**, **DevPod**, and other IDEs supporting the `.devcontainer` standard. ### Prerequisites - Docker (or Podman) - An IDE with Dev Container support. ### Hardware Acceleration and Audio Support By default, the container runs in **Restricted Mode** (secure/headless). In this mode: * Audio is routed to a `dummy` driver (no sound). * GPU access might be limited to software rendering. #### Enabling Real Hardware Access To enable **Full GPU Acceleration (DRI)** and **Physical Audio (ALSA/PulseAudio/PipeWire)**, follow these steps: 1. **Modify `devcontainer.json`**: Uncomment the following lines inside the `runArgs` array: ```json "--ipc=host", "--privileged" ``` * **What this does**: `--ipc=host` allows shared memory access for high-performance video frames, and `--privileged` grants the container direct access to host devices (GPU nodes and Sound Cards). 2. **Trigger a Rebuild**: Changes to `runArgs` require a container recreation. * In VS Code: Run the command `Dev Containers: Rebuild Container`. * In CLion: Select `Restart and Rebuild Container`. 3. **Switch the Audio Driver**: The `Dockerfile` defaults to `SDL_AUDIODRIVER=dummy`. Once in privileged mode, you must tell the MLT consumer to use a real driver: * **Temporary (CLI)**: ```bash SDL_AUDIODRIVER=alsa melt video.mp4 ``` * **Permanent**: Edit the `ENV SDL_AUDIODRIVER` line in your `.devcontainer/Dockerfile` to `alsa` or `pulseaudio`. > **Warning**: Using `--privileged` mode reduces the security isolation between the container and your host machine. Use it only when real-time hardware playback is required for development. --- ## Configuration Configuration is triggered by running CMake **from inside a build directory**, not from the project root. ```bash mkdir -p build cd build cmake .. ``` Alternatively, you can configure CMake with Ninja: ```bash cmake -G Ninja .. ``` More information on usage is found by viewing `CMakeLists.txt` and the CMake manual page. --- ## Compilation Once configured, it should be sufficient to run: ```bash cmake --build . ``` Alternatively, you can compile with Ninja: ```bash ninja ``` Alternatively, you can compile using CMake or Ninja with one less CPU core (recommended only on systems with many CPU cores): ```bash cmake --build . --parallel $(($(nproc) - 1)) # or ninja -j $(($(nproc) - 1)) ``` All generated files (Makefiles, Ninja files, cache, object files, binaries) will remain inside the `build/` directory. --- ## Testing To execute the MLT tools without installation, or to test a new version on a system with an already installed MLT version, run: ```bash source ../setenv ctest --output-on-failure # -j $(($(nproc) - 1)) ``` NB: * This applies to the **current shell only** * It assumes a **bash or Bourne-compatible shell** is in use * The command must be executed from inside the `build/` directory --- ## Installation Installation is triggered by running: ```bash sudo cmake --install . ``` This installs the files generated in the `build/` directory, without affecting the project source tree. > **Note**: After installation, some Linux systems may require running `sudo ldconfig` > to refresh the shared library cache, especially if the install prefix is not already > part of the system linker configuration. --- ## Summary ```bash mkdir build cd build cmake .. cmake --build . source ../setenv ctest --output-on-failure sudo cmake --install . ``` This approach keeps the source tree clean and allows the entire build to be removed safely by deleting the `build/` directory. --- # More Information For more detailed information, please refer to https://mltframework.org/docs/install/.mlt-7.38.0/cmake/000775 000000 000000 00000000000 15172202314 013442 5ustar00rootroot000000 000000 mlt-7.38.0/cmake/FindClangFormat.cmake000664 000000 000000 00000005564 15172202314 017454 0ustar00rootroot000000 000000 # # .rst: FindClangFormat # --------------- # # The module defines the following variables # # ``CLANGFORMAT_EXECUTABLE`` Path to clang-format executable # ``CLANGFORMAT_FOUND`` True if the clang-format executable was found. # ``CLANGFORMAT_VERSION`` The version of clang-format found # # Example usage: # # .. code-block:: cmake # # find_package(ClangFormat) # if(CLANGFORMAT_FOUND) # message("clang-format executable found: ${CLANGFORMAT_EXECUTABLE}\n" "version: ${CLANGFORMAT_VERSION}") # endif() include(FindPackageHandleStandardArgs) function(_ClangFormat_get_version clangformat_version result_var clangformat_path) execute_process( COMMAND "${clangformat_path}" --version OUTPUT_VARIABLE full_clangformat_version OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE version_result ) # full_clangformat_version sample: "clang-format version 3.9.1-4ubuntu3~16.04.1 (tags/RELEASE_391/rc2)" # clean clangformat_version sample: "3.9.1" string(REGEX REPLACE "[^0-9]*([.0-9]+).*" "\\1" clean_clangformat_version "${full_clangformat_version}") set(${result_var} ${version_result} PARENT_SCOPE) set(${clangformat_version} ${clean_clangformat_version} PARENT_SCOPE) endfunction() function(_ClangFromat_version_validator version_match clangformat_path) if(NOT DEFINED ClangFormat_FIND_VERSION) set(${is_valid_version} TRUE PARENT_SCOPE) else() _ClangFormat_get_version(candidate_version version_result "${clangformat_path}") if(version_result) message(DEBUG "Unable to determine candidate clang-format version at ${clangformat_path}: ${version_result}") endif() find_package_check_version("${candidate_version}" valid_clangformat_version HANDLE_VERSION_RANGE ) set(${version_match} "${valid_clangformat_version}" PARENT_SCOPE) endif() endfunction() find_program(CLANGFORMAT_EXECUTABLE NAMES clang-format clang-format-16 clang-format-15 clang-format-14 clang-format-13 clang-format-12 clang-format-11 clang-format-10 DOC "clang-format executable" VALIDATOR _ClangFromat_version_validator ) mark_as_advanced(CLANGFORMAT_EXECUTABLE) if(CLANGFORMAT_EXECUTABLE) _ClangFormat_get_version(CLANGFORMAT_VERSION _Clangformat_version_result "${CLANGFORMAT_EXECUTABLE}") if(_Clangformat_version_result) set(CLANGFORMAT_FOUND FALSE) message(WARNING "Unable to determine clang-format version: ${_Clangformat_version_result}") else() set(CLANGFORMAT_FOUND TRUE) endif() endif() find_package_handle_standard_args(ClangFormat FOUND_VAR CLANGFORMAT_FOUND REQUIRED_VARS CLANGFORMAT_EXECUTABLE CLANGFORMAT_VERSION VERSION_VAR CLANGFORMAT_VERSION ) mlt-7.38.0/cmake/FindDirent.cmake000664 000000 000000 00000000747 15172202314 016502 0ustar00rootroot000000 000000 find_package(Dirent CONFIG) if(NOT Dirent_FOUND) include(FindPackageHandleStandardArgs) find_path(Dirent_INCLUDE_DIR NAMES dirent.h ) find_package_handle_standard_args(Dirent REQUIRED_VARS Dirent_INCLUDE_DIR ) if(Dirent_FOUND AND NOT TARGET dirent) add_library(dirent INTERFACE) set_target_properties(dirent PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Dirent_INCLUDE_DIR}" ) endif() endif() mlt-7.38.0/cmake/FindFFTW3.cmake000664 000000 000000 00000002427 15172202314 016103 0ustar00rootroot000000 000000 # For FFTW3 pkg-config is the most reliable option on most plattforms except MSVC if(NOT MSVC) find_package(PkgConfig) if(PkgConfig_FOUND) pkg_check_modules(FFTW3 IMPORTED_TARGET QUIET fftw3) endif() if(FFTW3_FOUND AND NOT TARGET FFTW3::fftw3) add_library(FFTW3::fftw3 INTERFACE IMPORTED) if(FFTW3_INCLUDE_DIRS) set_property(TARGET FFTW3::fftw3 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${FFTW3_INCLUDE_DIRS}") endif() if(FFTW3_LINK_LIBRARIES) set_property(TARGET FFTW3::fftw3 PROPERTY INTERFACE_LINK_LIBRARIES "${FFTW3_LINK_LIBRARIES}") endif() if(FFTW3_LDFLAGS_OTHER) set_property(TARGET FFTW3::fftw3 PROPERTY INTERFACE_LINK_OPTIONS "${FFTW3_LDFLAGS_OTHER}") endif() if(FFTW3_CFLAGS_OTHER) set_property(TARGET FFTW3::fftw3 PROPERTY INTERFACE_COMPILE_OPTIONS "${FFTW3_CFLAGS_OTHER}") endif() endif() endif() # If we didn't find FFTW3 yet, try find_package # Note: FFTW3 supports different build systems and the necessary bits for # find_package to work are only installed if CMake was used to build FFTW3 if(NOT TARGET FFTW3::fftw3) find_package(FFTW3 CONFIG) endif() mlt-7.38.0/cmake/FindFFmpeg.cmake000664 000000 000000 00000015620 15172202314 016415 0ustar00rootroot000000 000000 # SPDX-FileCopyrightText: 2006 Matthias Kretz # SPDX-FileCopyrightText: 2008 Alexander Neundorf # SPDX-FileCopyrightText: 2011 Michael Jansen # SPDX-FileCopyrightText: 2021 Stefan Brüns # # SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: FindFFmpeg ---------- Try to find FFmpeg components. The following components are available:: AVCODEC AVFILTER AVDEVICE AVFORMAT AVUTIL SWRESAMPLE SWSCALE POSTPROCESS If no components are specified in the find_module call, the following ones will be choosen as default:: AVFORMAT AVUTIL AVCODEC Once done this will define ``FFMPEG_FOUND`` System has the all required components. ``FFMPEG_INCLUDE_DIRS`` Include directory necessary for using the required components headers. ``FFMPEG_LIBRARIES`` Link these to use the required ffmpeg components. ``FFMPEG_DEFINITIONS`` Compiler switches required for using the required ffmpeg components. Additonally for each of the components, the following variables will be defined: ``_FOUND`` True if (the requestion version of) is available ``_INCLUDE_DIRS`` Include directory necessary for using the headers ``_LIBRARIES`` Link these to use ``_DEFINITIONS`` Compiler switches required for using ``_VERSION`` The components version As the versions of the various FFmpeg components differ for a given release, and CMake supports only one common version for all components, use the following to specify required versions for multiple components: .. code-block:: cmake find_package(FFmpeg 57.48 COMPONENTS AVCODEC) find_package(FFmpeg 57.40 COMPONENTS AVFORMAT) find_package(FFmpeg 55.27 COMPONENTS AVUTIL) Since 6.19.0. #]=======================================================================] include(FindPackageHandleStandardArgs) if (NOT FFmpeg_FIND_COMPONENTS) # The default components were taken from a survey over other FindFFMPEG.cmake files set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL) endif () list(LENGTH FFmpeg_FIND_COMPONENTS _numComponents) if ((${_numComponents} GREATER 1) AND DEFINED ${FFmpeg_FIND_VERSION}) message(WARNING "Using a required version in combination with multiple COMPONENTS is not supported") set(_FFmpeg_REQUIRED_VERSION 0) elseif (DEFINED FFmpeg_FIND_VERSION) set(_FFmpeg_REQUIRED_VERSION ${FFmpeg_FIND_VERSION}) else () set(_FFmpeg_REQUIRED_VERSION 0) endif () set(_FFmpeg_ALL_COMPONENTS AVCODEC AVFILTER AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWRESAMPLE SWSCALE) ### Macro: set_component_found # # Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present. macro(set_component_found _component ) if (${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS) set(${_component}_FOUND TRUE) set(FFmpeg_${_component}_FOUND TRUE) endif () endmacro() ### Macro: find_component # # Checks for the given component by invoking pkgconfig and then looking up the libraries and # include directories. macro(find_component _component _pkgconfig _library _header) if (NOT WIN32) # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_${_component} QUIET ${_pkgconfig}) endif () endif (NOT WIN32) find_path(${_component}_INCLUDE_DIRS ${_header} HINTS ${PC_LIB${_component}_INCLUDEDIR} ${PC_LIB${_component}_INCLUDE_DIRS} PATH_SUFFIXES ffmpeg ) find_library(${_component}_LIBRARIES NAMES ${_library} HINTS ${PC_LIB${_component}_LIBDIR} ${PC_LIB${_component}_LIBRARY_DIRS} ) set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") set_component_found(${_component}) mark_as_advanced( ${_component}_INCLUDE_DIRS ${_component}_LIBRARIES ${_component}_DEFINITIONS ${_component}_VERSION) endmacro() # Check for cached results. If there are skip the costly part. if (NOT FFMPEG_LIBRARIES) # Check for all possible component. find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) find_component(AVFILTER libavfilter avfilter libavfilter/avfilter.h) find_component(AVFORMAT libavformat avformat libavformat/avformat.h) find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) find_component(AVUTIL libavutil avutil libavutil/avutil.h) find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h) find_component(SWSCALE libswscale swscale libswscale/swscale.h) find_component(POSTPROCESS libpostproc postproc libpostproc/postprocess.h) # Check if the required components were found and add their stuff to the FFMPEG_* vars. foreach (_component ${_FFmpeg_ALL_COMPONENTS}) if (${_component}_FOUND) set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES}) set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS}) list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS}) endif () endforeach () # Build the include path with duplicates removed. if (FFMPEG_INCLUDE_DIRS) list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) endif () # cache the vars. set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES FFMPEG_DEFINITIONS) else () # Set the noncached _FOUND vars for the components. foreach (_component ${_FFmpeg_ALL_COMPONENTS}) set_component_found(${_component}) endforeach () endif () # Compile the list of required vars unset(_FFmpeg_REQUIRED_VARS) set(_FFmpeg_FOUND_LIBRARIES "") foreach (_component ${FFmpeg_FIND_COMPONENTS}) if (${_component}_FOUND) if (${_component}_VERSION VERSION_LESS _FFmpeg_REQUIRED_VERSION) message(STATUS "${_component}: ${${_component}_VERSION} < ${_FFmpeg_REQUIRED_VERSION}") unset(${_component}_FOUND) endif () list(APPEND _FFmpeg_FOUND_LIBRARIES ${${_component}_LIBRARIES}) endif () list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS ${_component}_FOUND) endforeach () list(INSERT _FFmpeg_REQUIRED_VARS 0 _FFmpeg_FOUND_LIBRARIES) # Give a nice error message if some of the required vars are missing. find_package_handle_standard_args(FFmpeg REQUIRED_VARS ${_FFmpeg_REQUIRED_VARS} HANDLE_COMPONENTS) mlt-7.38.0/cmake/FindJACK.cmake000664 000000 000000 00000001426 15172202314 015760 0ustar00rootroot000000 000000 find_package(PkgConfig) pkg_check_modules(PC_JACK QUIET jack) find_path(JACK_INCLUDE_DIR NAMES jack/jack.h PATHS ${PC_JACK_INCLUDE_DIRS} ) find_library(JACK_LIBRARY NAMES jack PATHS ${PC_JACK_LIBRARY_DIRS} ) set(JACK_VERSION ${PC_JACK_VERSION}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(JACK FOUND_VAR JACK_FOUND REQUIRED_VARS JACK_LIBRARY JACK_INCLUDE_DIR VERSION_VAR JACK_VERSION ) if(JACK_FOUND AND NOT TARGET JACK::JACK) add_library(JACK::JACK UNKNOWN IMPORTED) set_target_properties(JACK::JACK PROPERTIES IMPORTED_LOCATION "${JACK_LIBRARY}" INTERFACE_COMPILE_OPTIONS "${PC_JACK_CFLAGS_OTHER}" INTERFACE_INCLUDE_DIRECTORIES "${JACK_INCLUDE_DIR}" ) endif() mark_as_advanced( JACK_INCLUDE_DIR JACK_LIBRARY ) mlt-7.38.0/cmake/FindKwalify.cmake000664 000000 000000 00000000570 15172202314 016655 0ustar00rootroot000000 000000 find_program(Kwalify_EXECUTABLE kwalify) if(Kwalify_EXECUTABLE) execute_process(COMMAND ${Kwalify_EXECUTABLE} -v OUTPUT_VARIABLE Kwalify_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Kwalify FOUND_VAR Kwalify_FOUND REQUIRED_VARS Kwalify_EXECUTABLE VERSION_VAR Kwalify_VERSION ) mlt-7.38.0/cmake/FindMono.cmake000664 000000 000000 00000020216 15172202314 016156 0ustar00rootroot000000 000000 # https://github.com/SimpleITK/SimpleITK/blob/master/CMake/FindMono.cmake # # A CMake Module for finding Mono. # # The following variables are set: # CSHARP_MONO_FOUND # CSHARP_MONO_COMPILER_${version} eg. "CSHARP_MONO_COMPILER_2.10.2" # CSHARP_MONO_INTERPRETOR_${version} eg. "CSHARP_MONO_INTERPRETOR_2.10.2" # CSHARP_MONO_VERSION eg. "2.10.2" # CSHARP_MONO_VERSIONS eg. "2.10.2, 2.6.7" # # Additional references can be found here: # http://www.mono-project.com/Main_Page # http://www.mono-project.com/CSharp_Compiler # http://mono-project.com/FAQ:_Technical (How can I tell where the Mono runtime is installed) # # This file is based on the work of GDCM: # http://gdcm.svn.sf.net/viewvc/gdcm/trunk/CMake/FindMono.cmake # Copyright (c) 2006-2010 Mathieu Malaterre # set( csharp_mono_valid 1 ) if( DEFINED CSHARP_MONO_FOUND ) # The Mono compiler has already been found # It may have been reset by the user, verify it is correct if( NOT DEFINED CSHARP_MONO_COMPILER_${CSHARP_MONO_VERSION} ) set( csharp_mono_version_user ${CSHARP_MONO_VERSION} ) set( csharp_mono_valid 0 ) set( CSHARP_MONO_FOUND 0 ) set( CSHARP_MONO_VERSION "CSHARP_MONO_VERSION-NOTVALID" CACHE STRING "C# Mono compiler version, choices: ${CSHARP_MONO_VERSIONS}" FORCE ) message( FATAL_ERROR "The C# Mono version '${csharp_mono_version_user}' is not valid. Please enter one of the following: ${CSHARP_MONO_VERSIONS}" ) endif( NOT DEFINED CSHARP_MONO_COMPILER_${CSHARP_MONO_VERSION} ) endif( DEFINED CSHARP_MONO_FOUND ) unset( CSHARP_MONO_VERSIONS CACHE ) # Clear versions if( WIN32 ) # Search for Mono on Win32 systems # See http://mono-project.com/OldReleases and http://www.go-mono.com/mono-downloads/download.html set( csharp_mono_bin_dirs ) set( csharp_mono_search_hints "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.11.2;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.9;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.8;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.7;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.6;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.5;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.4;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.3;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.2;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10.1;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.10;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.8;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.6.7;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.6.4;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.6.3;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.6.1;SdkInstallRoot]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono\\2.6;SdkInstallRoot]/bin" ) foreach( csharp_mono_search_hint ${csharp_mono_search_hints} ) get_filename_component( csharp_mono_bin_dir "${csharp_mono_search_hint}" ABSOLUTE ) if ( EXISTS "${csharp_mono_bin_dir}" ) set( csharp_mono_bin_dirs ${csharp_mono_bin_dirs} ${csharp_mono_bin_dir} ) endif ( EXISTS "${csharp_mono_bin_dir}" ) endforeach( csharp_mono_search_hint ) # TODO: Use HKLM_LOCAL_MACHINE\Software\Novell\Mono\DefaultCLR to specify default version # get_filename_component( test "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Novell\\Mono;DefaultCLR]" NAME ) foreach ( csharp_mono_bin_dir ${csharp_mono_bin_dirs} ) string( REPLACE "\\" "/" csharp_mono_bin_dir ${csharp_mono_bin_dir} ) if (EXISTS "${csharp_mono_bin_dir}/dmcs.bat") set( csharp_mono_executable "${csharp_mono_bin_dir}/dmcs.bat") elseif (EXISTS "${csharp_mono_bin_dir}/gmcs.bat") set( csharp_mono_executable "${csharp_mono_bin_dir}/gmcs.bat") elseif (EXISTS "${csharp_mono_bin_dir}/mcs.bat") set( csharp_mono_executable "${csharp_mono_bin_dir}/mcs.bat") endif (EXISTS "${csharp_mono_bin_dir}/dmcs.bat") if( csharp_mono_valid ) # Extract version number (eg. 2.10.2) string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" csharp_mono_version_temp ${csharp_mono_bin_dir}) set( CSHARP_MONO_VERSION ${csharp_mono_version_temp} CACHE STRING "C# Mono compiler version" ) mark_as_advanced( CSHARP_MONO_VERSION ) # Add variable holding executable set( CSHARP_MONO_COMPILER_${csharp_mono_version_temp} ${csharp_mono_executable} CACHE STRING "C# Mono compiler ${csharp_mono_version_temp}" FORCE ) mark_as_advanced( CSHARP_MONO_COMPILER_${csharp_mono_version_temp} ) # Set interpreter if (EXISTS "${csharp_mono_bin_dir}/mono.exe") set( CSHARP_MONO_INTERPRETER_${csharp_mono_version_temp} "${csharp_mono_bin_dir}/mono.exe" CACHE STRING "C# Mono interpreter ${csharp_mono_version_temp}" FORCE ) mark_as_advanced( CSHARP_MONO_INTERPRETER_${csharp_mono_version_temp} ) endif (EXISTS "${csharp_mono_bin_dir}/mono.exe") endif( csharp_mono_valid ) # Create a list of supported compiler versions if( NOT DEFINED CSHARP_MONO_VERSIONS ) set( CSHARP_MONO_VERSIONS "${csharp_mono_version_temp}" CACHE STRING "Available C# Mono compiler versions" FORCE ) else( NOT DEFINED CSHARP_MONO_VERSIONS ) set( CSHARP_MONO_VERSIONS "${CSHARP_MONO_VERSIONS}, ${csharp_mono_version_temp}" CACHE STRING "Available C# Mono versions" FORCE ) endif( NOT DEFINED CSHARP_MONO_VERSIONS ) mark_as_advanced( CSHARP_MONO_VERSIONS ) # We found at least one Mono compiler version set( CSHARP_MONO_FOUND 1 CACHE INTERNAL "Boolean indicating if C# Mono was found" ) endforeach( csharp_mono_bin_dir ) else( UNIX ) # Search for Mono on non-Win32 systems set( chsarp_mono_names "mcs" "mcs.exe" "dmcs" "dmcs.exe" "smcs" "smcs.exe" "gmcs" "gmcs.exe" ) set( csharp_mono_paths "/usr/bin/" "/usr/local/bin/" "/usr/lib/mono/2.0" "/opt/novell/mono/bin" ) find_program( csharp_mono_compiler NAMES ${chsarp_mono_names} PATHS ${csharp_mono_paths} ) if( EXISTS ${csharp_mono_compiler} ) find_program( csharp_mono_interpreter NAMES mono PATHS ${csharp_mono_paths} ) if ( EXISTS ${csharp_mono_interpreter} ) execute_process( COMMAND ${csharp_mono_interpreter} -V OUTPUT_VARIABLE csharp_mono_version_string ) # REGEX FIX FOR UBUNTU 24.04 string( REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" csharp_mono_version_temp "${csharp_mono_version_string}" ) # IMMEDIATE VERSION DEFINITION set( CSHARP_MONO_VERSION ${csharp_mono_version_temp} CACHE STRING "C# Mono compiler version" FORCE ) set( CSHARP_MONO_INTERPRETER_${CSHARP_MONO_VERSION} ${csharp_mono_interpreter} CACHE STRING "C# Mono interpreter ${CSHARP_MONO_VERSION}" FORCE ) mark_as_advanced( CSHARP_MONO_INTERPRETER_${CSHARP_MONO_VERSION} ) endif ( EXISTS ${csharp_mono_interpreter} ) unset( csharp_mono_interpreter CACHE ) # COMPILER VARIABLES ASSIGNMENT mark_as_advanced( CSHARP_MONO_VERSION ) set( CSHARP_MONO_COMPILER_${CSHARP_MONO_VERSION} ${csharp_mono_compiler} CACHE STRING "C# Mono compiler ${CSHARP_MONO_VERSION}" FORCE ) mark_as_advanced( CSHARP_MONO_COMPILER_${CSHARP_MONO_VERSION} ) set( CSHARP_MONO_VERSIONS ${CSHARP_MONO_VERSION} CACHE STRING "Available C# Mono compiler versions" FORCE ) mark_as_advanced( CSHARP_MONO_VERSIONS ) # --- CRUCIAL CHANGE FOR FEATURE_SUMMARY --- set( CSHARP_MONO_FOUND 1 CACHE INTERNAL "Boolean indicating if C# Mono was found" ) set( Mono_FOUND 1 CACHE INTERNAL "Required by FeatureSummary" ) # ----------------------------------------------- endif( EXISTS ${csharp_mono_compiler} ) unset( csharp_mono_compiler CACHE ) endif( WIN32 ) if( CSHARP_MONO_FOUND ) message( STATUS "Found the following C# Mono versions: ${CSHARP_MONO_VERSIONS}" ) endif( CSHARP_MONO_FOUND ) get_filename_component( current_list_path ${CMAKE_CURRENT_LIST_FILE} PATH ) set( Mono_USE_FILE ${current_list_path}/UseMono.cmake )mlt-7.38.0/cmake/FindNDI.cmake000664 000000 000000 00000002621 15172202314 015660 0ustar00rootroot000000 000000 set(NDI_SDK_INCLUDE_PATH "" CACHE PATH "NDI SDK include path") set(NDI_SDK_LIBRARY_PATH "" CACHE PATH "NDI SDK library path") if(NOT (NDI_SDK_INCLUDE_PATH AND NDI_SDK_LIBRARY_PATH)) message(FATAL_ERROR "NDI SDK: Please provide NDI_SDK_INCLUDE_PATH and NDI_SDK_LIBRARY_PATH!") endif() find_path(NDI_INCLUDE_DIR NAMES Processing.NDI.compat.h Processing.NDI.deprecated.h Processing.NDI.DynamicLoad.h Processing.NDI.Find.h Processing.NDI.FrameSync.h Processing.NDI.Lib.cplusplus.h Processing.NDI.Lib.h Processing.NDI.Recv.ex.h Processing.NDI.Recv.h Processing.NDI.Routing.h Processing.NDI.Send.h Processing.NDI.structs.h Processing.NDI.utilities.h PATHS "${NDI_SDK_INCLUDE_PATH}" ) find_library(NDI_LIBRARY NAMES ndi PATHS "${NDI_SDK_LIBRARY_PATH}" ) if(NOT NDI_LIBRARY) message(FATAL_ERROR "NDI SDK: libndi.so / ndi.dll not found in:\n${NDI_SDK_LIBRARY_PATH}\nMaybe you have to create a symlink or rename the file.") endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(NDI FOUND_VAR NDI_FOUND REQUIRED_VARS NDI_LIBRARY NDI_INCLUDE_DIR ) if(NDI_FOUND AND NOT TARGET NDI::NDI) add_library(NDI::NDI SHARED IMPORTED) set_target_properties(NDI::NDI PROPERTIES IMPORTED_LOCATION "${NDI_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${NDI_INCLUDE_DIR}" ) endif() mark_as_advanced( NDI_INCLUDE_DIR NDI_LIBRARY ) mlt-7.38.0/cmake/FindNode.cmake000664 000000 000000 00000007603 15172202314 016140 0ustar00rootroot000000 000000 # https://github.com/eclipse/upm/blob/master/cmake/modules/FindNode.cmake # Macro to add directory to NODEJS_INCLUDE_DIRS if it exists and is not /usr/include macro(add_include_dir dir) if (IS_DIRECTORY ${dir} AND NOT ${dir} STREQUAL "/usr/include") set(NODEJS_INCLUDE_DIRS ${NODEJS_INCLUDE_DIRS} ${dir}) endif() endmacro() find_program (NODEJS_EXECUTABLE NAMES node nodejs HINTS $ENV{NODE_DIR} PATH_SUFFIXES bin DOC "Node.js interpreter") include (FindPackageHandleStandardArgs) # If compat-libuv package exists, it must be at start of include path find_path (UV_ROOT_DIR "uv.h" PATHS /usr/include/compat-libuv010 NO_DEFAULT_PATH) if (UV_ROOT_DIR) # set (NODEJS_INCLUDE_DIRS ${UV_ROOT_DIR}) add_include_dir(${UV_ROOT_DIR}) endif() # Now look for node. Flag an error if not found find_path (NODE_ROOT_DIR NAMES node.h src/node.h PATH_SUFFIXES node node4 node5 node6 node7 node8 nodejs PATHS /usr/include /usr/local/include) if (NODE_ROOT_DIR) add_include_dir(${NODE_ROOT_DIR}) add_include_dir(${NODE_ROOT_DIR}/deps/uv/include) add_include_dir(${NODE_ROOT_DIR}/deps/v8/include) add_include_dir(${NODE_ROOT_DIR}/include/deps/uv/include) add_include_dir(${NODE_ROOT_DIR}/include/deps/v8/include) add_include_dir(${NODE_ROOT_DIR}/include/node) add_include_dir(${NODE_ROOT_DIR}/include/src) add_include_dir(${NODE_ROOT_DIR}/src) else() unset(NODEJS_INCLUDE_DIRS) message(ERROR " - node.h not found") endif() # Check that v8.h is in NODEJS_INCLUDE_DIRS find_path (V8_ROOT_DIR "v8.h" PATHS ${NODEJS_INCLUDE_DIRS}) if (NOT V8_ROOT_DIR) unset(NODEJS_INCLUDE_DIRS) message(ERROR " - v8.h not found") endif() # Check that uv.h is in NODEJS_INCLUDE_DIRS find_path (UV_ROOT_DIR "uv.h" PATHS ${NODEJS_INCLUDE_DIRS}) if (NOT UV_ROOT_DIR) unset(NODEJS_INCLUDE_DIRS) message(ERROR " - uv.h not found") endif() if (NODEJS_EXECUTABLE) execute_process(COMMAND ${NODEJS_EXECUTABLE} --version OUTPUT_VARIABLE _VERSION RESULT_VARIABLE _NODE_VERSION_RESULT) execute_process(COMMAND ${NODEJS_EXECUTABLE} -e "console.log(process.versions.v8)" OUTPUT_VARIABLE _V8_VERSION RESULT_VARIABLE _V8_RESULT) if (NOT _NODE_VERSION_RESULT AND NOT _V8_RESULT) string (REPLACE "v" "" NODE_VERSION_STRING "${_VERSION}") string (REPLACE "." ";" _VERSION_LIST "${NODE_VERSION_STRING}") list (GET _VERSION_LIST 0 NODE_VERSION_MAJOR) list (GET _VERSION_LIST 1 NODE_VERSION_MINOR) list (GET _VERSION_LIST 2 NODE_VERSION_PATCH) set (V8_VERSION_STRING ${_V8_VERSION}) string (REPLACE "." ";" _V8_VERSION_LIST "${_V8_VERSION}") string (REPLACE "." "" V8_DEFINE_STRING "${_V8_VERSION}") string (STRIP ${V8_DEFINE_STRING} V8_DEFINE_STRING) list (GET _V8_VERSION_LIST 0 V8_VERSION_MAJOR) list (GET _V8_VERSION_LIST 1 V8_VERSION_MINOR) list (GET _V8_VERSION_LIST 2 V8_VERSION_PATCH) # we end up with a nasty newline so strip everything that isn't a number string (REGEX MATCH "^[0-9]*" V8_VERSION_PATCH ${V8_VERSION_PATCH}) else () set (NODE_VERSION_STRING "0.10.30") set (NODE_VERSION_MAJOR "0") set (NODE_VERSION_MINOR "10") set (NODE_VERSION_PATCH "30") set (V8_VERSION_MAJOR "3") set (V8_VERSION_MINOR "28") set (V8_VERSION_PATCH "72") set (V8_VERSION_STRING "3.28.72") message (STATUS "defaulted to node 0.10.30") endif () string (REGEX REPLACE "\n" "" NODE_VERSION_STRING ${NODE_VERSION_STRING}) string (REGEX REPLACE "\n" "" V8_VERSION_STRING ${V8_VERSION_STRING}) mark_as_advanced (NODEJS_EXECUTABLE) find_package_handle_standard_args (Node REQUIRED_VARS NODEJS_EXECUTABLE NODEJS_INCLUDE_DIRS VERSION_VAR NODE_VERSION_STRING) message(STATUS "Found v8: ${V8_ROOT_DIR}/v8.h (found version \"${V8_VERSION_STRING}\")") endif () mlt-7.38.0/cmake/FindPHP.cmake000664 000000 000000 00000001761 15172202314 015701 0ustar00rootroot000000 000000 find_program(PHP_CONFIG_EXECUTABLE php-config) if(PHP_CONFIG_EXECUTABLE) execute_process(COMMAND ${PHP_CONFIG_EXECUTABLE} --version OUTPUT_VARIABLE PHP_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process(COMMAND ${PHP_CONFIG_EXECUTABLE} --includes OUTPUT_VARIABLE PHP_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE ) string(REPLACE "-I" "" PHP_INCLUDE_DIRS ${PHP_INCLUDE_DIRS}) separate_arguments(PHP_INCLUDE_DIRS) execute_process(COMMAND ${PHP_CONFIG_EXECUTABLE} --extension-dir OUTPUT_VARIABLE PHP_EXTENSION_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PHP FOUND_VAR PHP_FOUND REQUIRED_VARS PHP_CONFIG_EXECUTABLE PHP_INCLUDE_DIRS VERSION_VAR PHP_VERSION ) if(PHP_FOUND AND NOT TARGET PHP::Extension) add_library(PHP::Extension INTERFACE IMPORTED) set_target_properties(PHP::Extension PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${PHP_INCLUDE_DIRS}" ) endif() mlt-7.38.0/cppcheck.cfg000664 000000 000000 00000000223 15172202314 014620 0ustar00rootroot000000 000000 mlt-7.38.0/demo/000775 000000 000000 00000000000 15172202314 013306 5ustar00rootroot000000 000000 mlt-7.38.0/demo/README000664 000000 000000 00000021742 15172202314 014174 0ustar00rootroot000000 000000 MLT Demo Notes Before running the demo script, make sure you '. setenv' from the parent directory. Also, please create clips clip1.dv, clip2.dv, clip3.dv, clip1.mpeg, clip2.mpeg, clip3.mpeg, and music1.ogg. Please make sure clips are at least 500 frames duration. These notes explain the the concepts presented in each demonstration and what details to look for. First, a note on consumers. When you start the script, the main menu asks you to choose a consumer. A consumer is like a viewer, but it could also write to a stream/file. The "SDL" consumer is the popular Simple DirectMedia Layer audio and video output. The "xml" consumer generates an XML representation of the service network. That can be played directly due to the XML producer plugin. See https://mltframework.org/docs/mltxml/ for more information. These examples assume the numeric locale LC_NUMERIC decimal separator is a period. Therefore, the demo script sets LC_NUMERIC=C for you, but if you are running these manually or learning from them, remember to use the appropriate separator for your locale. And now the demos... All clips Simply builds a playlist containing each video clip, and you can transport between them using j and k keys. Filter in/out A video filter can be applied to a portion of a producer (clip, playlist, or multitrack). This examples shows the greyscale filter. Watermark A graphic can overlay video in realtime with support for alpha channel. This example uses a PNG file with an alpha channel. Distortion is explicitly enabled here so the otherwise circular graphic is scaled to fill the compositing region. By default, compositing honours the aspect ratio of the overlay. My name is... Titles are very easy to overlay in realtime. The titler uses Pango with the FreeType2 rendering backend. This means it supports high quality scalable font rendering with anti-aliasing, unicode (UTF-8), and Pango markup capabilities. The compsiting here respects the aspect ratio of the rendered title in the first two title pieces but distorts the final one. This demo also shows the motion and scaling capabilities of the compositor in conjunction with honouring aspect. The compositor is doing field-based rendering. So, when displayed non-progressively with SDL, you can see motion artifacts during animation. An overlay transition The affine transition also handles video over video as demonstrated in this usage of affine to create a special transition. This demonstration also crossfades the audio during the transition! Progressive rendering is explicitly enabled on the compositor due to the poor results that would otherwise occur due to scaling an interleaved video frame and moving the video in a reverse direction horizontally. Fade in and out A simple series of transitions between 3 clips using dissolves and audio crossfades. This is easy :-). Clock in and out Wipe transitions are very easy and highly extensible as they are generated using a very convenient lookup table based upon the luma of an image. This image can be a 16 bit PGM (grayscale bitmap) or the luma channel of any video producer. A number of high quality wipes can be downloaded from http://mlt.sf.net/. It also performs field rendering. The second wipe demonstrates the ability to control the direction of the wipe as well. Audio Stuff A music bed sound track can be mixed with a video. The sound track of the video clip has a "floating" amplitude normalisation filter applied. Typically, audio normalisation applies a constant gain factor across the entire duration of an audio segment from a single source where the gain factor is automatically determined by analysing the maximum "power" or peak levels. However, in news production, a popular requirement is to to dynamically boost the amplitude in soft areas and reduce the amplitude in louder areas. Thus, the gain analysis is performed using a "sliding window" approach. This example also applies a constant gain factor of 0.5 (50%) to the normalised audio of the video clip (to get a nicer mix level). Audio and Video Levels Audio can be normalised by setting a target amplitude level in decibels. A gamma curve can be applied to the luma channel of video. Shadowed Title and Watermark Two instances of the titler are used to create a shadow effect. The aspect ratio of the watermark in this example is not distorted. Since the original image is a circle with square pixels--a computer-generated image--and ITU BT.601 video is not composed of square samples. Therefore, the compositor normalises the pixel aspect ratio of the overlay to the destination image, and the circular image remains circular on the analog video output. Finally, a greyscale filter is applied to the watermark while its opacity is set at 30%. Station Promo into Story? Here is fun demo that might show using a still graphic with some music to introduce a show. A luma wipe with an audio crossfade transitions from the show title or station promotional material. Voiceover 2 clips with title A common news production requirement to have a "voiceover" audio track to a clip or even multiple clips as demonstrated here. Likewise, it is common to place a title caption on the video at the same time! This demo has a little fun with the titler at the sake of practicality :-) The foreground of the title is transparent while the opacity of the background is reduced to blend with the video. Meanwhile, the compositor stretches the image to fill the bottom slice of the video--not suitable for overscan displays ;-) Also, pay close attention to the mixing levels of the audio tracks. The audio of the video fades out as the voiceover track (just music in this demo) fades in. Then, the voiceover remains mixed with the ambient audio at a 60% level. Finally, the voiceover fades out smoothly from the 60% level to nothing. GJ-TTAvantika title This demo requires a special TrueType font called Avantika. If you have the font, register it with fontconfig using the fc-cache utility. This demonstrates i18n capabilities of the titler and the alignment capabilities of both the titler and the compositor. The titler centre aligns the two lines of text, and the compositor centre aligns the title horizontally on the frame. Title over graphic You can superimpose a title over a graphic over video! Also, you can apply a luma wipe to the compositor! Slideshow This demo requires any number of JPEG images with the extension ".jpg" in a subdirectory named "photos." Bouncy, Bouncy The "watermark" filter encapsulates the compositor, and you have full control over the compositor properties. Who says a watermark can not also be a video?! Bouncy, Bouncy Ball A variation on the above Bouncy, Bouncy demo that applies a shape, or alpha producer, to the the compositing region. Breaking News This demonstrates layout capabilities of the compositor. Squeeze Transitions This demonstrates a distorting barndoor-like wipe. J Cut A J cut is an edit where the audio cuts before the video. It gets its name from the way it looks on a NLE timeline user interface. When the audio cuts over, it does an audio crossfade over the duration of one frame. This makes the audio cut slightly less abrupt and avoids any "click" due to mismatched sample levels at the edit point. The video edit is a hard cut. L Cut An L cut is an edit where the video cuts before the audio. It gets its name from the way it looks on a NLE timeline user interface. This demo shows a very quick dissolve over 5 frames for a soft video cut. Like the J Cut demo, an audio crossfade for the duration of one frame makes an audio edit nearly instantaneous while being slightly softened and avoiding aberrations. Fade from/to black/silence Of course, it is possible using MLT to fade from black on video and silence on audio as well fade to black and silence. Push wipe A push wipe is a somewhat fancier transition than most standard wipes because it involves motion. The new video clip "pushes" the old video clip off one edge. If you can preview on an analog monitor you will notice how smooth the motion is due to field-based rendering. Ticker tape A very minimal reverse crawling title neard the bottom of the screen. The goal of the demo is show fluid motion of the field-based rendering of the compositor when viewed on an analog monitor using a DV or BlueFish444 consumer. The demo also shows the potientional for using and extending the existing set of services for a full blown news ticker implementation. Pango Keyframed Markup You can create timed text and subtitles using a .mpl file, which is a properties format file. A properties file contains key=value pairs on separate lines. For .mpl the key is a frame number and the value is Pango Markup Language. A tilde is interpreted as a new line. This example also demonstrates using the watermark and the alignment properties of its encapsulated transition where halign is the horizontal, valign is the vertical, c is for center, and m is for middle. mlt-7.38.0/demo/circle.png000664 000000 000000 00000012167 15172202314 015264 0ustar00rootroot000000 000000 PNG  IHDR,,y}usBIT|d.IDATxa\ey$0Bb$(HDef ki;N?8Vvt3NVeT #&@'VB4$&!q~x&ݽgw99gg{G=ϼy%/" `0 ^ǁǀC~kK'N@IZ3S%6QIi`wu80*1aje[^R4vQR&bYg.`#p-p~l p/ \l+ V-6b#1vks+MGð`bN9f5M #g }j†^}c Dq#𦀭D܍@_Q|m7_FJScxG=k}+{~b҈9 10I 85&[-J9׀ä(lq] FK5!>ˋfp;։zz/,ؘj}j:bmOH6 UpQB h2&HՌmDE%3 QAyH%Hl$06$<7xqѦ\#+;BFn4+marq-Rm4;mMr(/6) A7`c4 ]"vL` chW!2,6Wp򡜆Qxha&ș1F8JYH cцG(EWND“ [S'RQVX-K4!n>OTEwHߕ7";DoQ^I.LT!⃩)Rcǀۀs % |sJ*Z`%.u(sHx1oVRs#.deDfyv *kHe%\K|ú،/"UIř"6RI.Xω+I0v`=$˂!^UE !8x9XQ QTZ`SǗT]F޼\DA[h%V<ZĤRq%RĒ:HټXI %jBnax$Qʫ`7@?ٞ"6>@y ob% 1Ct$ %zN-XKA*`qo)C"I=M- M$U=,Iq6LO3#I&gzXXI3ubHtHҜ' ֪H\VɂuA<$iN=,I`KRm\'5D|m(IUtX8CBIm Iv8YHRB2X2NaIR՝cKR],`I j!X2.uƁ X$łq`~,$),Iu1!pH(6%*o$Ɓ㩓 /B2x%6^`I jKR]B288O$ex1u$~ pH(6JYHR{Lj'?:Iұ/gDz8 ,q鄉H\q?$kw<$iNzxaISzXJ$W3& L$uvXvuAtHRWۉEM$t6G$"Is9QaIi\ +?Ii {X/&IG:{dgrsHROԤkSH\NIc~J_cvRӑvV %UiSr]RVfaAyx$ rf ڇ7BKJk;w*X<:֠ny,I)uA' KG:$m>0zXHx ,pX()WD$i.]kOV#eIR+W;WU"2.nK k"I=9Yny XO.vͽ`擋$4gZ  Y9AlIǒL|`'y> |>Hj3dn'n0Ij=P^O™nXVr(V~;9H:՝DmE;.xmǕT?.y1v]ǕT?~j-~I,}%σW01I5MԀ\S fAǖTmߤx9XT9$U!EIIBu Ijw糤:JV V%&AMD!a;NBi(uP5>B{S^NT` l&IJ!`p8u"3VRJ;}iWł x87u"vNdM|k+Ti"+n!%qmKH7U.XJDKbKH/UX8:7NDjH\P ^"b_LDu)X?H C$S XA1.i8_>LN$ib{p>|+_SbqۍTNJ& ZYVՕY |8#u"R #.$&fPu/XK/JTaE^g5&,ˁ὇R'{m*=,]v;轁j҄nb;R'"UC5шb*X{l vIF=ĵPմ;R'"%=Nyibxx/71D/q 4N&ݻ>US'R=H{x{DNFQt+w0Lj6=2FaH8:b:u"$[S'RQζxS'" Dbk(%mV# 7FOmUbYbc 0chfHH chW!0|8Hkf$`]wVˁ0(/6i@IߘfvIC| 8Bm4+mIu<7rqѦBmv]DJ3 QAJf u/h#.SPe7[HՈ-D]QT#Iib3ZYBL>H57E$/,#ߘR\ |K .>ˋn->?[㐱1lni}VZV=F ug'& I/|W rA[7m .Q|Q]=7¥`bxm4&HHbCeD)`kҦS9;9Y6ҦaXg.x] 4=K%F`5R2-mk3S&QR&bYFB"`Uu5)fG'ݭ]|FKC̅-dϋ3V,h{' ǀC~k?zK8eIENDB`mlt-7.38.0/demo/circle.svg000664 000000 000000 00000000125 15172202314 015266 0ustar00rootroot000000 000000 mlt-7.38.0/demo/consumers.ini000664 000000 000000 00000000376 15172202314 016033 0ustar00rootroot000000 000000 SDL Default sdl2 SDL Half D1 sdl2:360x288 rescale=nearest resize=1 SDL High Latency sdl2 buffer=12 rescale=none SDL Progressive sdl2 progressive=1 XML to Terminal xml XML to File xml: DeckLink decklink DeckLink Prog LL decklink progressive=1 buffer=1 mlt-7.38.0/demo/demo000775 000000 000000 00000004020 15172202314 014154 0ustar00rootroot000000 000000 #!/bin/bash export MLT_PROFILE=dv_ntsc export LC_NUMERIC=C function show_consumers( ) { awk -F '\t' '{ printf( "%d. %s\n", ++ i, $1 ); }' < consumers.ini } function get_consumer( ) { option=$1 [ "$option" != "" ] && [ $option -gt 0 ] && sed 's/\t\+/\t/g' < consumers.ini | cut -f 2 | head -n $option | tail -n -1 } function show_menu( ) { sed 's/\t\+/\t/g' < demo.ini | awk -F '\t' '{ printf( "%2d. %-30.30s", ++ i, $2 ); if ( i % 2 == 0 ) printf( "\n" ); } END { if ( i % 2 == 1 ) printf( "\n" ); }' } function check_dependencies( ) { option=$1 if [ $option -gt 0 ] then deps=`sed 's/\t\+/\t/g' < demo.ini | cut -f 3 | head -n $option | tail -n -1` if [ "$deps" != "" ] then echo "$deps" | tr ',' '\n' | while read dep do ls $dep > /dev/null 2>&1 val=$? [ $val != 0 ] && echo Failed to find $dep >&2 && echo $val done fi echo 0 fi } function get_demo( ) { option=$1 if [ $option -gt 0 ] then cut -f 1 demo.ini | head -n $option | tail -n -1 fi } while [ 1 ] do echo Select Consumer echo show_consumers echo echo 0. Exit echo echo -n "Option: " read option echo [ "$option" == "0" ] && break export MLT_CONSUMER=`get_consumer $option` while [ "$option" != "0" -a "$MLT_CONSUMER" != "" ] do echo Choose Demo echo show_menu echo echo -n "Option: " read option echo [ "$option" == "" ] && break demo=`get_demo $option` usable=`check_dependencies $option` if [ "$usable" = "0" -a "$demo" != "" ] then if [ "$MLT_CONSUMER" == "xml:" ] then export XML_CONSUMER="xml:$demo.mlt" bash $demo -consumer $XML_CONSUMER melt +$demo.txt out=100 $demo.mlt $demo.mlt -filter watermark:watermark1.png composite.fill=1 composite.geometry=85%/5%:10%x10% elif [ "$MLT_CONSUMER" == "xml" ] then bash $demo -consumer $MLT_CONSUMER | less else bash $demo -consumer $MLT_CONSUMER fi elif [ "$usable" != "" ] then echo echo Unable to locate suitable files for the demo - please provide them. read pause fi stty sane done done mlt-7.38.0/demo/demo.ini000664 000000 000000 00000002453 15172202314 014737 0ustar00rootroot000000 000000 mlt_all All clips clip* mlt_effect_in_middle Filter in/out clip1.mpeg mlt_watermark Watermark clip2.dv,watermark1.png mlt_my_name_is My name is... clip3.dv mlt_composite_transition A composite transition clip1.dv,clip2.mpeg mlt_fade_in_and_out Fade in and out clip1.dv,clip2.mpeg,clip3.dv mlt_clock_in_and_out Clock in and out clip2.dv,clip1.dv,clip3.mpeg mlt_audio_stuff Audio Stuff clip*.dv,music1.ogg mlt_levels Audio and Video Levels clip*.dv mlt_titleshadow_watermark Shadowed Title and Watermark clip3.dv mlt_intro Station Promo into Story? watermark1.png,clip3.mpeg,music1.ogg mlt_voiceover Voiceover 2 clips with title clip1.dv,clip2.mpeg,music1.ogg mlt_avantika_title GJ-TTAvantika title pango.mlt mlt_title_over_gfx Title over graphic watermark1.png,clip1.dv mlt_slideshow Slideshow photos mlt_bouncy Bouncy, Bouncy clip1.dv,clip3.dv mlt_news Breaking News clip1.dv,clip2.dv mlt_squeeze Squeeze Transitions clip1.dv,clip2.dv,clip3.dv mlt_squeeze_box Squeeze Box clip1.dv,clip2.dv,clip3.dv mlt_jcut J Cut clip1.dv,clip2.dv mlt_lcut L Cut clip1.dv,clip2.dv mlt_fade_black Fade from/to black/silence clip3.mpeg mlt_push Push wipe clip1.mpeg, clip2.mpeg mlt_ticker Ticker tape clip1.dv mlt_slideshow2 Ken Burns slideshow photos mlt_pango_keyframes Pango Keyframed Markup pango_keyframes.mpl mlt_typewriter Typewriter mlt-7.38.0/demo/demo.kino000664 000000 000000 00000000661 15172202314 015117 0ustar00rootroot000000 000000 mlt-7.38.0/demo/entity.mlt000664 000000 000000 00000000432 15172202314 015337 0ustar00rootroot000000 000000 ]> pango Hello &name;, My name is Inigo Montoya. mlt-7.38.0/demo/luma1.pgm000664 000000 000000 00001452017 15172202314 015044 0ustar00rootroot000000 000000 P5 720 576 255   !!!!!!!!"""""""########$$$$$$$$%%%%%%%%%&&&&&&&&''''''''''((((((((()))))))))))***********++++++++++,,,,,,,,,,,,,--------------.............///////////////00000000000000001111111111111111  !!!!!!!""""""""#######$$$$$$$$$%%%%%%%&&&&&&&&&&'''''''''(((((((((())))))))))**********++++++++++++,,,,,,,,,,,,-------------..............////////////////00000000000000001111111111111111  !!!!!!!""""""""########$$$$$$$%%%%%%%%%&&&&&&&&'''''''''''(((((((()))))))))))***********+++++++++++,,,,,,,,,,,,,------------.............///////////////00000000000000000111111111111111112  !!!!!!!!"""""""########$$$$$$$$%%%%%%%%&&&&&&&&&'''''''''(((((((((())))))))))***********+++++++++++,,,,,,,,,,,--------------..............///////////////00000000000000011111111111111111122  !!!!!!!"""""""########$$$$$$$$%%%%%%%%%&&&&&&&&''''''''''((((((((()))))))))))**********+++++++++++,,,,,,,,,,,,------------..............////////////////000000000000000011111111111111111122  !!!!!!!""""""""#######$$$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((((()))))))))***********+++++++++++,,,,,,,,,,,,-------------.............///////////////00000000000000001111111111111111122222  !!!!!!""""""""########$$$$$$$$%%%%%%%%%&&&&&&&''''''''''((((((((()))))))))))*********+++++++++++++,,,,,,,,,,,------------...............///////////////00000000000000011111111111111111222222  !!!!!!!"""""""########$$$$$$$$%%%%%%%%&&&&&&&&&'''''''''(((((((((()))))))))***********+++++++++++,,,,,,,,,,,,-------------.............///////////////0000000000000000011111111111111111222222  !!!!!!""""""""#######$$$$$$$$%%%%%%%%%&&&&&&&&''''''''''(((((((()))))))))))**********++++++++++++,,,,,,,,,,,-------------..............//////////////00000000000000011111111111111111122222222  !!!!!!!"""""""########$$$$$$$$%%%%%%%%&&&&&&&&&''''''''(((((((((()))))))))***********+++++++++++,,,,,,,,,,,,,-----------..............////////////////00000000000000011111111111111111222222222  !!!!!!!""""""""#######$$$$$$$$%%%%%%%%%&&&&&&&''''''''''((((((((())))))))))**********++++++++++++,,,,,,,,,,,-------------.............//////////////00000000000000000111111111111111112222222222  !!!!!!!"""""""########$$$$$$$%%%%%%%%&&&&&&&&&''''''''(((((((((())))))))))**********+++++++++++,,,,,,,,,,,,------------...............//////////////00000000000000011111111111111111222222222222  !!!!!!!""""""""######$$$$$$$$%%%%%%%%%&&&&&&&&'''''''''((((((((())))))))))***********++++++++++,,,,,,,,,,,,-------------............////////////////000000000000000011111111111111112222222222222  !!!!!!"""""""########$$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((()))))))))))*********++++++++++++,,,,,,,,,,,-------------.............//////////////00000000000000001111111111111111122222222222222  !!!!!!!"""""""#######$$$$$$$$%%%%%%%%%&&&&&&&''''''''''((((((((()))))))))***********++++++++++,,,,,,,,,,,,,-----------..............///////////////00000000000000111111111111111112222222222222222  !!!!!!!"""""""########$$$$$$$%%%%%%%%&&&&&&&&&''''''''(((((((((())))))))))*********++++++++++++,,,,,,,,,,,-------------............///////////////0000000000000000111111111111111122222222222222222  !!!!!!!!"""""""#######$$$$$$$$%%%%%%%%&&&&&&&&'''''''''((((((((()))))))))***********++++++++++,,,,,,,,,,,,,-----------..............//////////////00000000000000011111111111111111222222222222222222  !!!!!!!"""""""########$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((())))))))))**********+++++++++++,,,,,,,,,,,-------------............////////////////00000000000000111111111111111122222222222222222223  !!!!!!!!""""""#######$$$$$$$$%%%%%%%%%&&&&&&&'''''''''(((((((((())))))))***********+++++++++++,,,,,,,,,,,------------............../////////////00000000000000001111111111111111122222222222222222233  !!!!!!""""""""#######$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((())))))))))**********+++++++++++,,,,,,,,,,,,-----------..............//////////////00000000000000111111111111111112222222222222222222233  !!!!!!!"""""""#######$$$$$$$$%%%%%%%%&&&&&&&&'''''''''((((((((()))))))))**********+++++++++++,,,,,,,,,,,-------------............///////////////000000000000000111111111111111222222222222222222233333  !!!!!!!""""""""#######$$$$$$$%%%%%%%%&&&&&&&&'''''''''(((((((())))))))))***********++++++++++,,,,,,,,,,,,-----------............../////////////00000000000000011111111111111111222222222222222222333333  !!!!!!!""""""########$$$$$$$$%%%%%%%%&&&&&&&'''''''''((((((((()))))))))**********++++++++++++,,,,,,,,,,-------------............///////////////00000000000001111111111111111122222222222222222222333333  !!!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((())))))))))**********++++++++++,,,,,,,,,,,------------.............//////////////0000000000000001111111111111111222222222222222222333333333  !!!!!!"""""""########$$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((((()))))))))*********+++++++++++,,,,,,,,,,,------------.............//////////////00000000000000011111111111111111222222222222222223333333333  !!!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&&'''''''''(((((((())))))))))**********++++++++++,,,,,,,,,,,-------------...........///////////////000000000000001111111111111111222222222222222222223333333333  !!!!!!!"""""""#######$$$$$$$$%%%%%%%%&&&&&&&'''''''''((((((((()))))))))*********+++++++++++,,,,,,,,,,,,----------............../////////////00000000000000001111111111111112222222222222222223333333333333  !!!!!!!"""""""######$$$$$$$$%%%%%%%&&&&&&&&&''''''''((((((((()))))))))**********+++++++++++,,,,,,,,,,------------............///////////////00000000000000111111111111111112222222222222222233333333333333  !!!!!!!"""""""#######$$$$$$$$%%%%%%%&&&&&&&&''''''''((((((((())))))))))*********++++++++++,,,,,,,,,,,,-----------............./////////////0000000000000001111111111111112222222222222222222233333333333333  !!!!!!!""""""#######$$$$$$$%%%%%%%%&&&&&&&&'''''''''(((((((()))))))))**********+++++++++++,,,,,,,,,,------------............./////////////00000000000000011111111111111112222222222222222233333333333333333  !!!!!!!""""""########$$$$$$$%%%%%%%%&&&&&&&''''''''((((((((())))))))))*********++++++++++,,,,,,,,,,,-------------...........///////////////00000000000001111111111111111122222222222222222333333333333333333  !!!!!!"""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''((((((((())))))))**********+++++++++++,,,,,,,,,,,----------..............////////////00000000000000001111111111111122222222222222222222333333333333333333  !!!!!!"""""""#######$$$$$$$$%%%%%%%&&&&&&&&''''''''(((((((())))))))))**********+++++++++,,,,,,,,,,,------------............//////////////00000000000000111111111111111122222222222222222333333333333333333334  !!!!!!!""""""#######$$$$$$$%%%%%%%%&&&&&&&&'''''''''(((((((()))))))))*********+++++++++++,,,,,,,,,,,-----------............//////////////000000000000011111111111111111222222222222222223333333333333333334444  !!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&''''''''((((((((()))))))))**********++++++++++,,,,,,,,,,------------.............////////////00000000000000011111111111111222222222222222222223333333333333333334444  !!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&&''''''''((((((((())))))))*********++++++++++,,,,,,,,,,,,-----------...........///////////////00000000000001111111111111111222222222222222223333333333333333333344444  !!!!!!"""""""#######$$$$$$$%%%%%%%&&&&&&&&'''''''((((((((()))))))))**********++++++++++,,,,,,,,,,-----------............./////////////0000000000000011111111111111112222222222222222233333333333333333344444444  !!!!!!"""""""#######$$$$$$$%%%%%%%&&&&&&&&''''''''((((((((()))))))))*********+++++++++,,,,,,,,,,,------------............/////////////00000000000000011111111111111222222222222222222233333333333333333344444444  !!!!!!""""""#######$$$$$$$%%%%%%%%&&&&&&&''''''''(((((((()))))))))**********++++++++++,,,,,,,,,,,----------.............//////////////00000000000011111111111111111222222222222222233333333333333333333444444444  !!!!!!""""""#######$$$$$$$%%%%%%%%&&&&&&&'''''''''(((((((()))))))))*********++++++++++,,,,,,,,,,------------.............///////////00000000000000011111111111111122222222222222222333333333333333333444444444444  !!!!!!"""""""#######$$$$$$$%%%%%%%&&&&&&&&'''''''((((((((())))))))**********++++++++++,,,,,,,,,,,-----------...........//////////////00000000000000111111111111112222222222222222222333333333333333334444444444444  !!!!!!""""""#######$$$$$$$%%%%%%%&&&&&&&&''''''''(((((((()))))))))**********+++++++++,,,,,,,,,,-----------............./////////////000000000000011111111111111112222222222222222333333333333333333334444444444444  !!!!!!"""""""######$$$$$$$%%%%%%%%&&&&&&&''''''''(((((((()))))))))*********++++++++++,,,,,,,,,,------------............////////////00000000000000011111111111111222222222222222223333333333333333334444444444444444  !!!!!!!""""""#######$$$$$$$%%%%%%%&&&&&&&''''''''((((((((())))))))**********++++++++++,,,,,,,,,,----------............//////////////00000000000001111111111111112222222222222222223333333333333333344444444444444444  !!!!!!!""""""#######$$$$$$$%%%%%%%&&&&&&&&'''''''((((((((())))))))*********++++++++++,,,,,,,,,,------------............////////////0000000000000111111111111111112222222222222223333333333333333333344444444444444444  !!!!!!!""""""######$$$$$$$%%%%%%%&&&&&&&&''''''''(((((((())))))))**********++++++++++,,,,,,,,,,-----------.........../////////////00000000000000111111111111112222222222222222233333333333333333344444444444444444444  !!!!!!"""""""######$$$$$$$%%%%%%%%&&&&&&&''''''''(((((((()))))))))*********+++++++++,,,,,,,,,,-----------............//////////////00000000000011111111111111122222222222222222233333333333333333444444444444444444445  !!!!!!"""""""######$$$$$$$%%%%%%%&&&&&&&''''''''(((((((()))))))))*********+++++++++,,,,,,,,,,,-----------...........////////////00000000000000111111111111111122222222222222233333333333333333333444444444444444444555  !!!!!!""""""#######$$$$$$$%%%%%%%&&&&&&&&'''''''((((((((())))))))*********++++++++++,,,,,,,,,,----------...........//////////////00000000000000111111111111122222222222222222333333333333333333444444444444444444444555  !!!!!!""""""#######$$$$$$%%%%%%%&&&&&&&&'''''''(((((((()))))))))*********+++++++++,,,,,,,,,,-----------............/////////////000000000000111111111111111122222222222222222333333333333333344444444444444444444455555  !!!!!!""""""#######$$$$$$%%%%%%%%&&&&&&&''''''''(((((((()))))))))*********+++++++++,,,,,,,,,,-----------...........////////////00000000000000111111111111111222222222222222333333333333333333344444444444444444455555555  !!!!!!"""""""#######$$$$$$%%%%%%%&&&&&&&''''''''((((((()))))))))*********+++++++++,,,,,,,,,,-----------............/////////////00000000000001111111111111222222222222222223333333333333333334444444444444444444455555555  !!!!!!""""""######$$$$$$$%%%%%%%&&&&&&&&'''''''(((((((()))))))))*********+++++++++,,,,,,,,,,-----------...........////////////0000000000000111111111111111222222222222222223333333333333333444444444444444444444555555555  !!!!!!""""""#######$$$$$$$%%%%%%&&&&&&&''''''''(((((((())))))))*********+++++++++,,,,,,,,,,-----------.........../////////////00000000000000111111111111112222222222222223333333333333333333444444444444444444555555555555  !!!!!!""""""######$$$$$$%%%%%%%%&&&&&&&''''''''(((((((())))))))********++++++++++,,,,,,,,,,----------............/////////////00000000000011111111111112222222222222222223333333333333333344444444444444444444555555555555  !!!!!!""""""#######$$$$$$%%%%%%%&&&&&&&'''''''(((((((()))))))))*********+++++++++,,,,,,,,,,----------...........////////////00000000000001111111111111112222222222222222233333333333333334444444444444444444445555555555555  !!!!!!!""""""######$$$$$$%%%%%%%&&&&&&&&'''''''(((((((())))))))********++++++++++,,,,,,,,,-----------.........../////////////00000000000001111111111111122222222222222233333333333333333334444444444444444445555555555555555  !!!!!!"""""#######$$$$$$$%%%%%%%&&&&&&''''''''(((((((())))))))*********++++++++++,,,,,,,,,-----------...........////////////000000000000111111111111112222222222222222233333333333333333444444444444444444455555555555555555  !!!!!!""""""######$$$$$$$%%%%%%%&&&&&&&''''''''((((((())))))))********+++++++++,,,,,,,,,,----------.........../////////////00000000000001111111111111112222222222222222333333333333333444444444444444444444455555555555555555  !!!!!""""""#######$$$$$$$%%%%%%&&&&&&&'''''''(((((((()))))))))********+++++++++,,,,,,,,,,----------.........../////////////00000000000001111111111111222222222222222333333333333333333444444444444444444455555555555555555555  !!!!!!""""""######$$$$$$%%%%%%%&&&&&&&&'''''''((((((())))))))********++++++++++,,,,,,,,,,----------...........////////////0000000000001111111111111122222222222222222333333333333333334444444444444444445555555555555555555555  !!!!!""""""######$$$$$$$%%%%%%%&&&&&&'''''''((((((((())))))))********+++++++++,,,,,,,,,,----------...........////////////00000000000001111111111111122222222222222223333333333333334444444444444444444445555555555555555555566  !!!!!"""""""######$$$$$$%%%%%%%&&&&&&&'''''''((((((())))))))*********+++++++++,,,,,,,,,,-----------...........///////////000000000000011111111111112222222222222223333333333333333334444444444444444444555555555555555555555666  !!!!!!""""""######$$$$$$$%%%%%%&&&&&&&'''''''(((((((())))))))********+++++++++,,,,,,,,,----------...........////////////00000000000011111111111111122222222222222223333333333333333344444444444444444555555555555555555555555666  !!!!!""""""#######$$$$$%%%%%%%&&&&&&&&'''''''((((((()))))))*********++++++++++,,,,,,,,,----------...........////////////00000000000011111111111111122222222222222233333333333333344444444444444444444555555555555555555555666666  !!!!!!""""""#####$$$$$$$%%%%%%%&&&&&&'''''''((((((((()))))))********+++++++++,,,,,,,,,-----------...........////////////000000000000111111111111122222222222222233333333333333333344444444444444444445555555555555555555566666666  !!!!!""""""######$$$$$$%%%%%%%&&&&&&&'''''''((((((())))))))*********+++++++++,,,,,,,,,----------..........////////////00000000000011111111111111222222222222222233333333333333333444444444444444445555555555555555555555566666666  !!!!!!"""""######$$$$$$$%%%%%%&&&&&&&'''''''(((((((()))))))********+++++++++,,,,,,,,,,----------...........////////////00000000000011111111111111222222222222222333333333333333444444444444444444455555555555555555555556666666666  !!!!!"""""#######$$$$$$%%%%%%&&&&&&&&''''''((((((()))))))))********+++++++++,,,,,,,,----------...........////////////0000000000001111111111111222222222222222333333333333333334444444444444444444455555555555555555556666666666666  !!!!!""""""######$$$$$$%%%%%%%&&&&&&'''''''(((((((()))))))********++++++++++,,,,,,,,,----------..........////////////00000000000011111111111111222222222222222333333333333333334444444444444444455555555555555555555566666666666666  !!!!!!"""""#######$$$$$%%%%%%%&&&&&&&'''''''(((((()))))))))********++++++++,,,,,,,,,-----------...........///////////000000000000011111111111112222222222222223333333333333334444444444444444445555555555555555555555566666666666666  !!!!!!""""""#####$$$$$$$%%%%%%&&&&&&''''''''((((((())))))))********+++++++++,,,,,,,,,---------...........///////////00000000000011111111111112222222222222223333333333333333344444444444444444445555555555555555555566666666666666666  !!!!!!"""""######$$$$$$%%%%%%&&&&&&&'''''''((((((()))))))))*******++++++++,,,,,,,,,,----------...........///////////00000000000011111111111112222222222222223333333333333333344444444444444444555555555555555555556666666666666666666  !!!!!"""""""#####$$$$$$%%%%%%%&&&&&&'''''''(((((((()))))))********+++++++++,,,,,,,,,---------...........///////////0000000000000111111111111122222222222222233333333333333344444444444444444455555555555555555555556666666666666666666  !!!!!!""""""######$$$$$$%%%%%%&&&&&&&''''''((((((()))))))))*******++++++++,,,,,,,,,,----------..........///////////00000000000111111111111122222222222222233333333333333333444444444444444444455555555555555555555666666666666666666666  !!!!!""""""######$$$$$$%%%%%%&&&&&&''''''''((((((()))))))*********++++++++,,,,,,,,----------...........////////////00000000000111111111111122222222222222233333333333333333444444444444444445555555555555555556666666666666666666666666  !!!!!!"""""######$$$$$$%%%%%%&&&&&&&'''''''((((((())))))))*******+++++++++,,,,,,,,,---------..........///////////00000000000001111111111111222222222222222333333333333333444444444444444445555555555555555555556666666666666666666666667  !!!!!""""""######$$$$$%%%%%%%&&&&&&'''''''((((((()))))))*********++++++++,,,,,,,,-----------..........///////////00000000000111111111111122222222222222333333333333333344444444444444444445555555555555555555556666666666666666666677777  !!!!!!"""""######$$$$$$%%%%%%&&&&&&&''''''(((((((()))))))*******++++++++++,,,,,,,,---------..........////////////000000000000111111111111122222222222222333333333333333344444444444444444455555555555555555566666666666666666666666677777  !!!!!"""""######$$$$$$%%%%%%&&&&&&''''''''(((((())))))))********++++++++,,,,,,,,,----------.........///////////00000000000011111111111112222222222222223333333333333334444444444444444455555555555555555555666666666666666666666666677777  !!!!!""""""#####$$$$$$$%%%%%&&&&&&&''''''(((((((()))))))********+++++++++,,,,,,,,---------...........///////////00000000000111111111111222222222222223333333333333333444444444444444444555555555555555555555666666666666666666666677777777  !!!!!!"""""######$$$$$%%%%%%%&&&&&&'''''''(((((())))))))********++++++++,,,,,,,,,,--------..........///////////0000000000000111111111111222222222222223333333333333333444444444444444444555555555555555555566666666666666666666677777777777  !!!!!""""""#####$$$$$$%%%%%%&&&&&&&''''''((((((()))))))********++++++++,,,,,,,,-----------.........///////////00000000000111111111111122222222222222233333333333333344444444444444444555555555555555555566666666666666666666666677777777777  !!!!!!""""#######$$$$$%%%%%%%&&&&&''''''''(((((())))))))*******++++++++,,,,,,,,,---------..........////////////00000000000111111111111222222222222233333333333333334444444444444444455555555555555555555566666666666666666666666777777777777  !!!!!"""""#####$$$$$$$%%%%%&&&&&&&''''''(((((((())))))*********+++++++,,,,,,,,,----------.........//////////00000000000001111111111111222222222222233333333333333334444444444444444455555555555555555555666666666666666666667777777777777777  !!!!!"""""######$$$$$%%%%%%%&&&&&&'''''''(((((())))))))*******+++++++++,,,,,,,,---------...........//////////00000000001111111111111222222222222222333333333333333444444444444444445555555555555555556666666666666666666666677777777777777777  !!!!!""""""#####$$$$$$%%%%%%&&&&&&&'''''(((((((())))))*********+++++++,,,,,,,,,---------.........////////////000000000001111111111112222222222222333333333333333444444444444444445555555555555555555566666666666666666666666677777777777777777  !!!!!"""""######$$$$$%%%%%%%&&&&&'''''''(((((())))))))*******+++++++++,,,,,,,----------..........//////////00000000000011111111111112222222222222333333333333333444444444444444445555555555555555555566666666666666666666677777777777777777777  !!!!!""""""#####$$$$$$%%%%%&&&&&&&''''''((((((()))))))********+++++++,,,,,,,,,---------..........///////////00000000001111111111112222222222222223333333333333334444444444444444455555555555555555566666666666666666666677777777777777777777777  !!!!!"""""######$$$$$%%%%%%&&&&&&'''''''(((((()))))))********++++++++,,,,,,,,----------........///////////0000000000001111111111112222222222223333333333333334444444444444444455555555555555555556666666666666666666666677777777777777777777777  !!!!!"""""######$$$$$$%%%%%&&&&&&''''''((((((()))))))********++++++++,,,,,,,,---------..........//////////00000000000111111111111122222222222223333333333333334444444444444444455555555555555555556666666666666666666666677777777777777777777778  !!!!""""""#####$$$$$%%%%%%%&&&&&'''''''(((((()))))))********++++++++,,,,,,,,---------.........////////////00000000001111111111122222222222222233333333333333344444444444444444555555555555555555666666666666666666666777777777777777777777777788  !!!!!"""""#####$$$$$$%%%%%&&&&&&&''''''((((((()))))))*******++++++++,,,,,,,,---------.........//////////00000000000001111111111122222222222223333333333333344444444444444444555555555555555555666666666666666666666677777777777777777777777777788  !!!!!"""""######$$$$$%%%%%%&&&&&&''''''((((((())))))*********++++++,,,,,,,,,,-------...........//////////00000000001111111111111222222222222223333333333333344444444444444445555555555555555556666666666666666666666677777777777777777777777778888  !!!!!""""######$$$$$$%%%%%&&&&&&''''''((((((()))))))*******++++++++,,,,,,,----------.........///////////000000000011111111111222222222222222333333333333333444444444444444445555555555555555556666666666666666666667777777777777777777777788888888  !!!!!"""""#####$$$$$$%%%%%%&&&&&'''''''(((((()))))))********++++++,,,,,,,,,--------.........../////////00000000000011111111111222222222222233333333333333444444444444444445555555555555555556666666666666666666677777777777777777777777777888888888  !!!!"""""######$$$$$%%%%%&&&&&&&''''''(((((())))))))******++++++++,,,,,,,,---------.........///////////00000000011111111111112222222222222233333333333334444444444444444555555555555555555666666666666666666666677777777777777777777777777888888888  !!!!!!"""""#####$$$$$%%%%%%&&&&&&''''''((((((())))))********+++++++,,,,,,,,---------.........//////////0000000000011111111111222222222222223333333333333334444444444444444555555555555555555666666666666666666666677777777777777777777777888888888888  !!!!!"""""#####$$$$$$%%%%%&&&&&&''''''(((((())))))))******++++++++,,,,,,,,--------..........//////////00000000000111111111111222222222222333333333333334444444444444444455555555555555555566666666666666666666677777777777777777777778888888888888888  !!!!!!"""""####$$$$$$%%%%%%&&&&&''''''((((((())))))********++++++++,,,,,,,---------........////////////00000000011111111111122222222222222333333333333344444444444444455555555555555555566666666666666666666777777777777777777777777778888888888888888  !!!!""""""#####$$$$$%%%%%&&&&&&&''''''((((())))))))*******+++++++,,,,,,,,,-------........../////////00000000000011111111112222222222222233333333333333344444444444444455555555555555555566666666666666666666777777777777777777777777778888888888888888  !!!!!"""""#####$$$$$%%%%%%&&&&&&''''''(((((()))))))******+++++++++,,,,,,,---------.........//////////00000000001111111111112222222222223333333333333344444444444444444555555555555555555666666666666666666666777777777777777777777778888888888888888888  !!!!"""""######$$$$$%%%%%&&&&&&''''''(((((()))))))*******+++++++,,,,,,,,---------........///////////000000000011111111111222222222222223333333333333444444444444445555555555555555555666666666666666666667777777777777777777777788888888888888888888888  !!!!!"""""#####$$$$$%%%%%%&&&&&''''''((((((())))))******+++++++++,,,,,,,--------........../////////00000000000111111111112222222222222333333333333333444444444444445555555555555555566666666666666666666777777777777777777777777788888888888888888888888  !!!!!"""""######$$$$%%%%%&&&&&&'''''''((((()))))))*******+++++++,,,,,,,,---------........///////////00000000011111111111112222222222233333333333333444444444444444445555555555555555566666666666666666666777777777777777777777777788888888888888888888888  !!!!!""""#####$$$$$$%%%%%&&&&&&'''''((((((()))))))******+++++++,,,,,,,,,-------........../////////0000000000111111111112222222222222233333333333344444444444444445555555555555555556666666666666666666677777777777777777777777888888888888888888888888889  !!!!!"""""#####$$$$$%%%%%&&&&&&''''''(((((())))))*******++++++++,,,,,,,---------........//////////00000000001111111111122222222222223333333333333344444444444444555555555555555556666666666666666666677777777777777777777777888888888888888888888888888889  !!!!"""""#####$$$$$%%%%%%&&&&&''''''(((((()))))))******+++++++,,,,,,,,,-------.........//////////000000000111111111111122222222222333333333333334444444444444444555555555555555556666666666666666666777777777777777777777777888888888888888888888888888889  !!!!!"""""#####$$$$%%%%%%&&&&&'''''''((((())))))*******++++++++,,,,,,,--------..........////////00000000000111111111122222222222222333333333333444444444444444455555555555555555566666666666666666666777777777777777777777777888888888888888888888888889999  !!!!"""""#####$$$$$%%%%%%&&&&&'''''(((((()))))))*******++++++,,,,,,,,---------.......///////////00000000011111111111122222222222233333333333333444444444444445555555555555555566666666666666666666777777777777777777777778888888888888888888888888899999999  !!!!!"""""####$$$$$%%%%%&&&&&&''''''(((((())))))******++++++++,,,,,,,,-------........./////////0000000000111111111111222222222223333333333333344444444444444445555555555555556666666666666666666777777777777777777777778888888888888888888888888888999999999  !!!!!""""#####$$$$$$%%%%%&&&&&'''''(((((()))))))*******+++++++,,,,,,,---------......../////////00000000000111111111222222222222223333333333334444444444444444555555555555555556666666666666666666777777777777777777777778888888888888888888888888888999999999  !!!!!""""#####$$$$%%%%%%&&&&&''''''(((((())))))******++++++++,,,,,,,,-------........///////////00000000011111111111222222222222333333333333334444444444444555555555555555555666666666666666666667777777777777777777777788888888888888888888888888999999999999  !!!!"""""#####$$$$$%%%%%%&&&&&'''''(((((())))))*******+++++++,,,,,,,--------........./////////0000000000111111111112222222222233333333333333444444444444444555555555555555666666666666666666667777777777777777777777888888888888888888888888889999999999999999  !!!!"""""####$$$$$%%%%%&&&&&&''''''(((((())))))******+++++++,,,,,,,---------.......//////////00000000001111111112222222222222233333333333444444444444444445555555555555555666666666666666666777777777777777777777788888888888888888888888888899999999999999999  !!!!"""""#####$$$$$%%%%%&&&&&'''''(((((())))))*******++++++++,,,,,,-------........../////////000000000111111111112222222222223333333333333444444444444455555555555555555556666666666666666666777777777777777777777788888888888888888888888888899999999999999999  !!!!!""""#####$$$$%%%%%%&&&&&''''''(((((())))))******++++++,,,,,,,,--------.........////////00000000001111111111122222222222333333333333334444444444444455555555555555566666666666666666666677777777777777777777777888888888888888888888888889999999999999999999  !!!!"""""#####$$$$%%%%%%&&&&&'''''((((())))))*******+++++++,,,,,,,,-------........//////////00000000011111111112222222222222333333333334444444444444444455555555555555566666666666666666777777777777777777777778888888888888888888888888899999999999999999999999  !!!!"""""####$$$$$%%%%%&&&&&&'''''(((((())))))*******++++++,,,,,,,--------.........////////0000000001111111111112222222222233333333333334444444444444555555555555555555566666666666666666777777777777777777777888888888888888888888888889999999999999999999999999  !!!!"""""####$$$$$%%%%%&&&&&'''''(((((())))))******+++++++,,,,,,,,--------......./////////00000000001111111111222222222223333333333333344444444444445555555555555555666666666666666666666777777777777777777777888888888888888888888888889999999999999999999999999  !!!!""""#####$$$$$%%%%%&&&&&''''''((((()))))))******+++++++,,,,,,-------.........//////////00000000111111111122222222222223333333333344444444444444445555555555555566666666666666666667777777777777777777777788888888888888888888888888999999999999999999999999999  !!!!""""#####$$$$$%%%%%&&&&&'''''((((())))))******+++++++,,,,,,,--------.........///////0000000000111111111112222222222233333333333334444444444444555555555555555556666666666666666677777777777777777777788888888888888888888888888999999999999999999999999999999:  !!!!""""#####$$$$%%%%%&&&&&''''''(((((())))))******+++++++,,,,,,,-------.......//////////000000000011111111122222222222333333333333334444444444445555555555555555556666666666666666667777777777777777777788888888888888888888888899999999999999999999999999999999::  !!!!!""""####$$$$$%%%%%&&&&&'''''(((((())))))*****+++++++,,,,,,,--------......../////////0000000011111111111222222222222333333333334444444444444445555555555555566666666666666666666677777777777777777777788888888888888888888888899999999999999999999999999999999::  !!!!""""####$$$$$%%%%%&&&&&'''''(((((())))))******+++++++,,,,,,,-------........////////0000000000111111111112222222222333333333333444444444444445555555555555555666666666666666667777777777777777777777788888888888888888888888888999999999999999999999999999999::::  !!!!""""#####$$$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,-------........//////////000000000111111111222222222223333333333333344444444444555555555555555555566666666666666667777777777777777777788888888888888888888888888999999999999999999999999999999::::::::  !!!!!""""####$$$$$%%%%&&&&&''''''((((())))))******+++++++,,,,,,,-------........////////00000000111111111112222222222223333333333344444444444444555555555555555666666666666666666667777777777777777777888888888888888888888889999999999999999999999999999999:::::::::::  !!!!!""""####$$$$$%%%%%&&&&&'''''(((((())))))******++++++,,,,,,-------......../////////000000000111111111112222222222333333333333444444444444444555555555555566666666666666666667777777777777777777777888888888888888888888889999999999999999999999999999999:::::::::::  !!!!"""""####$$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,--------......../////////000000001111111112222222222233333333333333444444444445555555555555555566666666666666667777777777777777777777888888888888888888888888889999999999999999999999999999999:::::::::::  !!!!""""#####$$$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,-------.......////////0000000001111111111122222222222333333333334444444444444455555555555555566666666666666666677777777777777777788888888888888888888888888999999999999999999999999999999::::::::::::::::  !!!!""""#####$$$$%%%%&&&&&'''''((((())))))******++++++,,,,,,,-------......../////////000000000111111111122222222223333333333334444444444444445555555555556666666666666666666677777777777777777778888888888888888888888999999999999999999999999999999::::::::::::::::::::  !!!!""""####$$$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,-------........////////000000001111111112222222222233333333333333444444444445555555555555555666666666666666667777777777777777777777888888888888888888888899999999999999999999999999999:::::::::::::::::::::  !!!!""""####$$$$$%%%%&&&&&'''''((((()))))******+++++++,,,,,,-------......./////////00000000011111111112222222222233333333333444444444444455555555555555556666666666666667777777777777777777777888888888888888888888888899999999999999999999999999999:::::::::::::::::::::  !!!!""""####$$$$$%%%%%&&&&&'''''((((()))))******++++++,,,,,,--------......./////////000000000111111111222222222233333333333444444444444444555555555555566666666666666666677777777777777777788888888888888888888888888999999999999999999999999999999:::::::::::::::::::::::  !!!!!""""####$$$$%%%%%&&&&'''''((((())))))******++++++,,,,,,,-------.......////////000000001111111112222222222223333333333334444444444445555555555555556666666666666666667777777777777777777888888888888888888888999999999999999999999999999999::::::::::::::::::::::::::::  !!!!""""####$$$$%%%%%&&&&&'''''((((()))))******++++++,,,,,,-------......./////////0000000011111111112222222222223333333333444444444444555555555555555556666666666666677777777777777777777777888888888888888888888999999999999999999999999999:::::::::::::::::::::::::::::::  !!!!""""#####$$$$%%%%&&&&&''''(((((()))))******+++++++,,,,,,-------......./////////0000000011111111122222222223333333333344444444444444555555555555556666666666666666777777777777777777778888888888888888888888888999999999999999999999999999:::::::::::::::::::::::::::::::  !!!!"""####$$$$$%%%%%&&&&&''''((((()))))******++++++,,,,,,-------.......////////0000000011111111112222222222233333333333344444444444455555555555556666666666666666666777777777777777778888888888888888888888888999999999999999999999999999999:::::::::::::::::::::::::::::::  !!!!""""####$$$$%%%%%&&&&'''''((((())))))******++++++,,,,,,------........////////000000001111111111222222222223333333333444444444445555555555555555666666666666666677777777777777777777888888888888888888888999999999999999999999999999999:::::::::::::::::::::::::::::::::::  !!!!"""####$$$$%%%%%&&&&&'''''(((()))))******++++++,,,,,,,-------.......////////00000000111111111222222222233333333333444444444444455555555555555566666666666666777777777777777777777778888888888888888888899999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;  !!!""""#####$$$$%%%%&&&&'''''(((((()))))******+++++,,,,,,-------.......////////0000000011111111122222222222333333333333444444444444555555555555566666666666666666777777777777777777788888888888888888888888899999999999999999999999999:::::::::::::::::::::::::::::::::::;;;;;  !!!!""""####$$$$%%%%%&&&&'''''((((())))******+++++++,,,,,,-------.......////////0000000011111111122222222222333333333344444444444555555555555555666666666666666666777777777777777788888888888888888888888889999999999999999999999999999:::::::::::::::::::::::::::::::::::;;;;;  !!!""""####$$$$$%%%%&&&&'''''((((()))))******+++++,,,,,,-------.......////////0000000011111111122222222223333333333444444444444455555555555555556666666666666677777777777777777777888888888888888888888999999999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;  !!!!""""####$$$%%%%%&&&&&''''((((()))))******++++++,,,,,,------.......////////000000001111111112222222222333333333334444444444444555555555555566666666666666677777777777777777777788888888888888888888999999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;  !!!""""####$$$$%%%%&&&&'''''(((((()))))*****+++++,,,,,,-------........////////000000001111111112222222222333333333344444444444555555555555566666666666666666677777777777777777888888888888888888888888999999999999999999999999::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;  !!!"""""###$$$$%%%%%&&&&'''''(((()))))******++++++,,,,,,------......////////000000001111111112222222222333333333344444444444445555555555555566666666666666667777777777777777788888888888888888888888899999999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;  !!!"""####$$$$$%%%%&&&&'''''((((()))))*****+++++,,,,,,,-------......////////000000011111111122222222223333333333344444444444445555555555555666666666666667777777777777777777788888888888888888888999999999999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;  !!!""""####$$$$%%%%&&&&&''''(((())))))******+++++,,,,,------........////////000000001111111112222222222333333333344444444444555555555555666666666666666667777777777777777777788888888888888888889999999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;  !!!!"""####$$$$$%%%&&&&'''''((((()))))*****++++++,,,,,,------.......///////000000011111111122222222223333333333444444444444555555555555556666666666666666677777777777777778888888888888888888888899999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;  !!!""""####$$$%%%%%&&&&''''((((()))))*****++++++,,,,,-------.......////////00000001111111112222222223333333333344444444444455555555555555666666666666667777777777777777788888888888888888888888899999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!"""###$$$$$%%%%&&&&'''''(((()))))*****++++++,,,,,,------......////////0000000011111111122222222223333333333444444444445555555555555666666666666667777777777777777777788888888888888888888999999999999999999999999999:::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!"""####$$$$%%%%&&&&&''''(((())))))****+++++,,,,,,,-------......///////000000011111111222222222233333333334444444444455555555555555666666666666666677777777777777777788888888888888888899999999999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""###$$$$$%%%&&&&'''''((((())))******+++++,,,,,,-----.......////////000000001111111122222222233333333334444444444445555555555555566666666666666677777777777777788888888888888888888889999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!"""####$$$%%%%%&&&&''''((((()))))****++++++,,,,,,------......///////00000000111111111222222222233333333334444444444455555555555556666666666666777777777777777777888888888888888888888889999999999999999999999::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""###$$$$%%%%&&&&'''''(((())))******+++++,,,,,-------.......///////00000001111111222222222233333333334444444444455555555555566666666666666667777777777777777777888888888888888888899999999999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!"""#####$$$%%%%&&&&''''((((()))))****++++++,,,,,,------......///////0000000001111111222222222333333333344444444444555555555555566666666666666667777777777777777788888888888888888999999999999999999999999999999::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!""""####$$$$%%%&&&&'''''(((()))))*****+++++,,,,,-------......///////000000011111111122222222223333333333444444444445555555555555666666666666667777777777777778888888888888888888889999999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!"""####$$$$%%%%&&&&''''((((())))****+++++++,,,,,-----.......////////00000011111111222222222333333333344444444444555555555555666666666666667777777777777777778888888888888888888888999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!""""###$$$$%%%%&&&&'''''((()))))*****+++++,,,,,,------......//////0000000001111111122222222333333333344444444445555555555555666666666666666777777777777777777888888888888888888899999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<  !!!!"""####$$$%%%%&&&&''''((((())))*****++++++,,,,-------.......//////0000001111111112222222222333333333344444444445555555555555666666666666666777777777777777888888888888888889999999999999999999999999999:::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<  !!!""""###$$$$%%%%&&&'''''((())))))****+++++,,,,,,-----......////////000000011111112222222223333333333444444444445555555555556666666666666677777777777777788888888888888888888899999999999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<  !!!!"""###$$$$%%%%&&&&'''((((())))******+++++,,,,-------......//////0000000011111111222222223333333334444444444555555555555666666666666667777777777777777778888888888888888888889999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<  !!!"""####$$$%%%%&&&&''''(((()))))****++++++,,,,,-----.......///////000000111111112222222222333333333444444444455555555555566666666666666777777777777777777888888888888888888999999999999999999999:::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<  !!!""""###$$$$%%%&&&&''''((((()))******++++,,,,,,------.....///////00000000111111122222222333333333344444444444555555555555566666666666667777777777777777888888888888888889999999999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<  !!!!"""###$$$$%%%%&&&'''''((()))))****++++++,,,,,-----.......//////000000011111111122222223333333334444444444555555555555666666666666667777777777777778888888888888888888899999999999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<  !!!"""###$$$$%%%%&&&&'''((((())))*****++++,,,,,,-----......////////00000011111112222222222333333333444444444455555555555666666666666677777777777777777888888888888888888889999999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!!""####$$$%%%%&&&&''''(((())))****++++++,,,,-------.....//////0000000001111112222222233333333334444444444455555555555566666666666667777777777777777788888888888888888899999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""###$$$$%%%&&&&''''(((())))*****++++,,,,,,-----.......//////000000111111111222222233333333344444444445555555555556666666666666677777777777777788888888888888888999999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!"""####$$$%%%%&&&''''(((()))))****+++++,,,,,------....////////0000001111111222222222233333333344444444455555555555666666666666777777777777777788888888888888888889999999999999999999999999:::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""###$$$%%%%&&&&'''(((()))))****+++++,,,,,-----......//////0000000011111112222222333333333344444444444555555555556666666666667777777777777777888888888888888888899999999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!"""####$$%%%%&&&&''''(((())))****+++++,,,,,-----......///////00000111111111222222233333333444444444445555555555556666666666666677777777777777778888888888888888899999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!""""##$$$$%%%&&&&''''(((())))****+++++,,,,,------.....//////0000000111111222222222233333333444444444555555555556666666666666777777777777777788888888888888888999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!"""###$$$%%%%&&&''''(((())))*****++++,,,,,,----......./////00000001111111222222233333333334444444444555555555566666666666677777777777777788888888888888888899999999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!"""###$$$$%%%&&&''''(((()))))***++++++,,,-------....////////00000111111112222222233333334444444444455555555555566666666666677777777777777788888888888888888899999999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!"""####$$%%%%&&&&'''((((()))*****++++,,,,,-----....../////0000000011111222222222233333333444444445555555555556666666666666677777777777777788888888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!"""###$$$%%%&&&&''''((()))))***+++++,,,,,-----......//////00000011111112222222333333333344444444455555555556666666666667777777777777778888888888888888899999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!"""###$$$%%%%&&&''''(((()))*****++++,,,,,-----.....///////00000111111112222222233333334444444444455555555555666666666667777777777777788888888888888889999999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!""###$$$$%%%&&&''''((()))))****++++,,,,,-----....../////0000000111111222222222333333334444444455555555555566666666666667777777777777788888888888888889999999999999999999999::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======  !!!"""###$$$%%%&&&&'''(((())))****+++++,,,-------....///////000001111111122222233333333334444444455555555556666666666666777777777777777788888888888888888999999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<============  !!!""###$$$$%%&&&&''''(()))))****++++,,,,,-----.....//////000000111111122222222333333344444444444555555555666666666667777777777777778888888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============  !!!"""##$$$%%%%&&&'''(((())))****+++++,,,,-----.....//////0000000111112222222223333333444444444555555555555666666666667777777777777888888888888888999999999999999999999:::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============  !!!"""###$$$%%%&&&''''((())))*****+++,,,,,-----.....///////0000111111112222223333333333444444445555555555666666666666677777777777777888888888888888999999999999999999999:::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============  !!!""""##$$$%%%&&&&'''(((())))***+++++,,,,----.......////0000000111111222222223333333444444444445555555566666666666677777777777777788888888888888888999999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================  !!!""####$$$%%&&&&''''((()))*****++++,,,,-----....///////000000111111222222223333333444444444555555555556666666666777777777777788888888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================  !!!"""##$$$$%%%&&&'''(((())))***+++++,,,,,---......//////00000111111112222233333333334444444555555555556666666666667777777777778888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================  !!"""###$$$%%%&&&''''((()))*****++++,,,------...../////000000011111222222223333333444444444455555555666666666666677777777777777888888888888889999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================  !!!"""##$$$%%%%&&&''(((())))****+++,,,,,-----....///////000001111112222222233333334444444445555555555666666666777777777777777788888888888888889999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================  !!!"""###$$$%%&&&&'''(((())*****++++,,,,----....../////000001111111122222333333333344444455555555555566666666667777777777778888888888888888899999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================  !!!""###$$$%%%&&&'''((())))****+++,,,,,-----....//////000000111112222222233333334444444445555555566666666666666777777777778888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================  !!!"""###$$%%%&&&''''((()))****++++,,,,,---.....//////000001111111222222233333344444444445555555566666666666677777777777777888888888888899999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================  !!"""##$$$%%%%&&&''(((()))*****+++,,,,-----.....////00000011111112222233333333344444445555555555566666666677777777777777888888888888888899999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===============================================  !!!""###$$$%%&&&''''(((())****++++,,,,,----....//////00000111112222222233333334444444455555555556666666666677777777778888888888888888889999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================  !!""###$$$%%%&&&'''((())))****+++,,,,----....../////00001111111222222233333344444444445555555666666666666677777777777888888888888899999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================  !!!""###$$%%%&&&'''(((()))***++++,,,,-----..../////000000111111222223333333334444444555555555566666666677777777777777788888888888899999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================  !!"""###$$$%%%&&&''((())))****++++,,,----.....//////00001111122222222333333344444445555555555566666666777777777777778888888888888889999999999999999:::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================  !!"""##$$$%%&&&''''((()))***++++,,,,-----.....////0000011111122222223333334444444445555555666666666666777777777788888888888888888999999999999999999:::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================  !!"""###$$%%%&&&'''(())))***+++++,,,,---....//////000000111112222233333333344444445555555566666666666777777777777888888888888899999999999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================  !!"""##$$%%%&&&'''((())))***+++,,,,----...../////000011111122222223333333444444555555555556666666677777777777777788888888888999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================================================================  !!"""##$$$%%%&&&''((()))***++++,,,,,----....////0000011111112222223333344444444455555555666666666677777777777788888888888888899999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================  !!""##$$$%%&&&'''((())))***++++,,,----....//////0000011111222223333333344444444555555566666666666677777777788888888888888888999999999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================================>>>>>  !!""###$$$%%&&&'''(()))***++++,,,,-----....////000001111122222223333333444444555555555566666666677777777777788888888888899999999999999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================================>>>>>>>>>>>>>  !!"""##$$%%%&&&'''((()))***++++,,,,---..../////000001111112222223333344444444455555555666666667777777777777778888888888999999999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=========================================================>>>>>>>>>>>>>>>>>>  !!""###$$%%%&&&''((()))***+++,,,,----...../////00001111122222333333334444444455555566666666666777777777778888888888888899999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================>>>>>>>>>>>>>>>>>>  !!""##$$$%%&&&'''((()))***++++,,,,----...////000001111112222223333333444445555555555666666666677777777788888888888888889999999999999:::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================================>>>>>>>>>>>>>>>>>>  !!""##$$$%%&&&'''(()))***++++,,,---...../////000001111112222233333444444445555555555666666777777777777788888888888899999999999999999:::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================================================>>>>>>>>>>>>>>>>>>  !!""##$$%%%&&'''((()))***++++,,,----.....////000011111222223333333344444444555555666666666777777777777788888888899999999999999999999::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===========================================================================>>>>>>>>>>>>>>>>>>>>  !!""##$$%%%&&'''((()))***+++,,,----....////000001111122222233333334444455555555666666666667777777778888888888888999999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!!""##$$%%&&&'''(()))***++++,,,----..../////000001111122222333334444444555555555666666666777777777888888888888888999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""###$$%%&&&'''(()))***++++,,,----...////00001111122222333333344444444555555566666667777777777777888888888889999999999999999::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""###$$%%&&'''(()))***+++,,,,---..../////000011111122222333333344444455555566666666667777777777778888888899999999999999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$$%%&&'''(()))***+++,,,,---..../////00001111122222333334444445555555556666666666777777778888888888889999999999999999::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===============================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%%&&''((()))***+++,,,----...////000011111222222333333444444455555555666666677777777778888888888888899999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""###$$%%%&&''((())***+++,,,----.../////00001111122222233333344444455555566666666777777777777888888888889999999999999:::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%&&'''(()))***+++,,,,---....////000011111222223333344444555555556666666666777777777788888888899999999999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$$%%&&'''(())***+++,,,---....////0000111122222233333344444455555555666666667777777788888888888899999999999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%&&''((()))***+++,,,---....////00001111222222333333444444555555666666677777777778888888888888999999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%%&&''(()))**+++,,,---....////000011111222223333344444555555566666666677777777777888888888899999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""#$$%%&&'''(()))***+++,,,---...////0001111122222333334444444555555566666666677777777788888888999999999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!"##$$$%%&&''(()))**+++,,,----....////0001111122222333334444444555555666666677777777888888888889999999999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%&&''((()))**+++,,---...////0000111112222233333444445555555666666677777777778888888888889999999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!"##$$%%%&&''(())***+++,,,---...////00011112222233333444444555555566666666777777777788888888889999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$%%&&'''(()))**++,,,---....////00001111222223333344444455555556666666777777778888888889999999999999::::::::::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !""##$$%%&&''(())***+++,,,---..////0001111122222333334444455555556666666777777788888888888999999999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !""###$%%&&''((())**++,,,----...////00011112222333334444455555566666666777777777888888888889999999999999:::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""#$$%%%&''(()))***++,,---...////000011111222233333444445555556666666677777777788888888889999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<=================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !""##$$%&&'''(())**+++,,,---...///000111122222333334444455555556666666777777778888888899999999999::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""#$$%%&&''()))***++,,---....////000111222233334444455555566666667777777788888888889999999999999::::::::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !"###$%%&&''(())**+++,,,--...///000011111222233334444455555566666677777777788888888889999999999999::::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!"##$%%%&''(()))**++,,----...///00111122222333334444445555556666667777777778888888889999999999:::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??  !"##$$%&&'''())**++++,,--...////00011112223333444445555556666666777777778888888889999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<=========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????  !""#$$%%&&'(()))**++,,,---..///00011111222233334444555556666667777777788888888899999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<=====================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????  !!"##$$%&&''(()***+++,---...////00111222223333344444555556666667777777788888888899999999999::::::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????  !""#$%%%&''(())**++,,,--..////000011122233334444455555556666666777777778888888889999999999:::::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????  !!"#$$%&&'''())***+,,---...///001111122223333444555556666666777777778888888899999999999::::::::::::;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????  !"##$%%&&'(())**++,,,--..////000112222233333444455555666667777777888888899999999999:::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????? !!""#$$%&&''())***+,,----..//00001112223333444445555555666667777777888888899999999999:::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<==============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????  !""##$%%&'((()**+++,,-....///00111122223334444555556666666777777778888888899999999999:::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<====================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????? !!"#$$$%&''()))**+,,,--..///000011222233333444455556666777777778888888889999999999:::::::::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<===================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????  !"##$%%&&'(()**+++,---...//000111122333344444555555666677777788888889999999999::::::::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<===========================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????  !""#$$%&&''())**+,,,--..////0111122223334445555556666666777777888888999999999::::::::::::;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<===================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????  !"##$%%&'((()**++,----.///000112222333334445556666666777777778888888999999999::::::::::::;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????  !""#$$%&''()))*++,,-....//001111223333444445555666677777778888888899999999999:::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????? !!"##%%&''(()**++,,--.////00111222233344455555566666777778888889999999999:::::::::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<=======================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????  !""$$%&&'()))+++,,--..//000011222333334455556666666777777888889999999::::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????? !!##$%%&'(()**+,,,--..///011111233334444455566667777777788888889999999::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????  !"#$$%&''())*++,---..//00011222233444455555566677777888888888999999999::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<========================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????  !"##$%&''()**++,,-..../000112223333445555566666677778888889999999999:::::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<===============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????  !"#$%&&'())*++,---.////0111123333444455666666777777788889999999:::::::::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????  !"#$%%&'())*++,,--../000012222334444555566677777777888888999999:::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<===========================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????? !"##$%&'(()**+,,-...//001112233334555555666677778888888889999999:::::::;;;;;;;;;;;<<<<<<<<<<<<<<<<<<================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????  ""#$%&''()*+++---.///011112233344455566666667778888899999999999::::::::;;;;;;;;;;;<<<<<<<<<<<<<<<<<=================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !"#$%&&'()**+,,-.../000122222344445556666777777788899999999:::::::::::::;;;;;;;;;;;<<<<<<<<<<<<<<<<<=================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  ""$$%&'())+++---.///01112233334555555667777778888889999::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<=================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  !##$%&'((**+,,-..//0011122333444555666667788888888899999:::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<===============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !"#$%&''))*++,-..//00022222444445566666777788888999999999:::::;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<=============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  !##%&&((**+,,--////11122333445555566777777788999999999::::::::;;;;;;;;<<<<<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !"$$%''))*+,,-../000112233344555566677777888889999:::::::::::::;;;;;;;;<<<<<<<<<<<=========================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  "#$%&'()*++---.//0012222444446666666788888888899::::::::::;;;;;;;;;;;;;<<<<<<<<<<<=======================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  !"$%&'')**,,-..//11112333445556666777788889999999:::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<=======================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  "#%&&()*++,-../0011223334555557777777789999999999:::;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<=======================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  !#$%'')**,,--///01222244445566677778888899999::::::::;;;;;<<<<<<<<<<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !"#%&(()++,-../011123334455666667888888889::::::::::::;;;;;<<<<<<<<<<<<<<===============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !!#$&'(**+,--//0012233355555677777888899999:::::::;;;;;;;;;;<<<<<<<=============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? "#%%&()++,,.///1122244455567777777999999999::;;;;;;;;;;;;;;;<<<<<<=====================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !#$%'')*+--..0011133444666667778889999999::::;;;;;;;;;;;<<<<<<<<<<<=============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !"#%&())+,-///00223335556666788888899:::::::::;;;;;<<<<<<<<<<<<<<<<<============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !#$&'(*++-.//1122244555577777888999::::::::::;;<<<<<<<<<<<<<<<<<<<==============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !"$%&))*,--/001133444666777779999999:::::;;;;;;;<<<<<<<<<<<<=====================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? "#$'(*+,,.//022333556666788899999999;;;;;;;;;;;;<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !"%&()*--..111224555567788888999::::;;;;;;;;;;;<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? "$&'(+,-//01133445677777888:::::::::;;;;;<<<<<<<<<===================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !$%&)*+..//22333566677779999::::::::<<<<<<<<<<<<<<<==========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !#$'(+,-.01122455566688999999:::;;;;<<<<<<<<<<<<<<<<==>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !%&)*+.//013444557788889999;;;;;;;;;<<<<<<<<<<=======>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? !#'(),-./22334667777888::::;;;;;;;;;;<<<==============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? #%'*+,/01225566677999::::::;;;;;;=====================>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? "#')-.//03445568899999::::::<<<<<======================>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? $&*+-.1233477788899999;<<<<<<<<<<====================>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? "')*./012566777888;;;;;;<<<<<<<<<<=========>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? "%',-./3455667::::;;;;;;;<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????"(*,1233458999::::;;;;;;;<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????#&,./1267788999::::;=======>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? '*,.456677889<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? $'/134566;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? *-/189::;;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????&*56789::;;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????*/356789::;;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????*/356789::;;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`UPLJIHGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`UPLJIHGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@jUPLJIHGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@p`YUJIHGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@sf`URPNGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@uj`[XPNLKJIIDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@vmd`XUSQKJIIHHGGFCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@xphc\YSQPNMIHHGGFFFEEEEDBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@xqjc`]WUSNMLLKJGFFFEEEEDDDDDDDCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@yslfb]ZXSRQPLKJJIIHEEEEDDDDDDDCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ztnhe`]XVUQPONMJIIHHHGGGDDDDDDCCCCCCCCCCBBBBBBBBBAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@zupjgb`[YUTRQNMLLKHHHGGGFFFFFDCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@{vqlid`]\XVRQPPOLKKJJIGGFFFFFEEEEEECCCCCBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@{vrmifb`\ZXUTSPONMMJJIIIHHFFFEEEEEEDDDDDDBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@{wsnjhda^\XWVSRQPMMLLKIIHHHHGGGEEEEDDDDDDDDDDCCCBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|xtpliec`^ZYVUTQPPONLKKKJJHHGGGGFFFFDDDDDDDDDCCCCCCCCCCBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|xtpmjgca^\[XWTSRQONNMMKJJJIIIGGFFFFFFEEEDDDDCCCCCCCCCCCCCCCCBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|xuqnkhec`^[ZYVUTQQPPMMLLLJIIIHHHHFFFFEEEEEEEECCCCCCCCCCCCCCCBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|yurokifda`][YXWTSRPPONNLLKKJIHHHHHGGGEEEEEEEEEDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|yvspljgeb`^]ZYWVURRQQNNNMMKJJJJIHHGGGGGFFFEEEEDDDDDDDDDDDCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|yvspmkheda`]\[XWUTSSQPPOMMLLLJJIIIIHGGGFFFFFFFFDDDDDDDDDDDDCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}zwtqnlifeba^][ZYVVUSRRPOONNLLKKKIIIHHHHHFFFFFFFEEEEEDDDDDDDCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}zwtromjhfcb`^\[YXWUTTRQPPNNMMMKKJJJJHHHHHGGGFFFEEEEEEEEEEDDCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}zxurpnkhgdba^]\ZYWVVTSRPPPOOMMLLLJJJIIIIHGGGGGGFFEEEEEEEEEDDDDDCCCCCCCCCCCCCCCCCBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}zxuspnlihecb`^\[ZXXVUTRRQQOONNNLLKKKIIIIIHHHGGGFFFFFFFEEEEDDDDDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}{xvsqnmjhfdca`]\ZZYWVTTSSQPPPNNMMMKKKJJJIHHHHHHHFFFFFFFFFEEDDDDDDDDDDDDDDDCCCCCCBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}{xvtqomkigedba^^\[YXWUUTSRRPPOONMMLLLJJJJJIHHHHHGGGGFFFFFEEEEEEEDDDDDDDDDDCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}{yvtrpnlihfdca`^]\ZYWWVTTSRQQPONNNMLLLKKJJIIIIIHGGGGGGGGFEEEEEEEEEEEEDDDDDCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}{ywtrpnljigedb`_^\[ZXXVUUSSRRPPPONMMMMKKKKJJIIIHHHHGGGGGFFFFFEEEEEEEEDDDDDCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}{ywusqomkjhfdba`_]\ZYYWVUTTSRQQPOONNMMLLLKJJJJJHHHHHHHHGFFFFFFFFFFEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}{ywusqpnljhfecb`_^][ZYXXVUUSSRQQPPNNNNMLLLKKJJJIIIIHHHHGGGGFFFFFFFEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|zxvtrpnljigfdba`_]\[ZYXWVUTTRRRQPPOONMMMMKKKKKIIIIIIIHGGGGGGGGGFFEEEEEEEEEEDDDDDDDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|zxvtrpomkjhfecb``^][[ZXXVVUTSSRQQPOOONNMMLLLKKJJJJIIIHHHHHGGGGGFFFFEEEEEEEEEEEEEDDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|zxvtrpomkjhgfdca`_^\\ZYYWWUUTSSRRPPPPNNNMMLLLKKJJJJJIIHHHHHHHGGFFFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|zxvusqpnlkigfedb``^]\[ZYXXVVUTTSRQQPPOOOMMMMMKKKKKJJIIIIIHHHHGGGGGFFFFFFFFFEEEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|zxwusqpnlkjhgedca`_^\\[ZYXWWUUTSSRQQPPOONNNMMLLLKKKJJJIIIIIHHGGGGGGGGGFFFFFEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|zywusrpomkjihfdcba`_]][[ZYXWVVTTTRRRQPPPONNNMMLLLLKJJJJJJIIHHHHHHGGGGGGFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|zywutrqomlkihgedcb``^]\[ZYYXWVUUTSSRQQQPOOONMMMMMLKKKKJJJIIIIHHHHHHHGGGFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|{ywvtrqpnlkjhgfecba`_]]\[ZYXXWVUTTTRRRQPPPONNNNMMLLLKKKJJJIIIIIIIHHHGGGGGFFFFFFFFFFFEEEEEEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|{yxvtsqpnmljihfedbb``^]\\[ZYXWWVUUTSSRQQQPPOONNNMMLLLLKJJJJJJIIIIHHHHGGGGGGGGGFFFFFFFEEEEEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|{yxvusrpomlkihgedcba`_^]\[ZZYXWVVUTTSSRRQQPOOOONMMMMLLKKKKJJJJIIIHHHHHHHHGGGGGGFFFFFFEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~|{yxvusrqonmkjigfedcba`_^]\[ZYYXWVVUTTSRRRQPPPPONNNNMLLLLKKKKJJIIIIIIHHHHHHHGGGGFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}{zxwutrqpnmljihffdcba`_^]\\[ZYXXWVUUTTSSRQQQQPOOONNMMMLLLLKKJJJJJIIIIIIHHHHGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}{zxwutrqpnmlkihgfedcaa`_^]\[[ZYXXWVVUTTSRRRQQPPOOONNMMMMLLKKKKJJJJJJIIIHHHHHGGGGGGGGGFFFFFFFFFEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}{zxwvtsrpomlkjihfedcba``^^\\[ZZYXWWVUUTSSSRRQQPPPONNNNNMLLLLKKKKKJJJIIIIHHHHHHHHGGGGGGGFFFFFFFEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}{zywvtsrqonmljihgfedcba`_^]][[ZYYXWVVVTTTSSRRQQPPOOOONNMMMLLLLLKKJJJJIIIIIIIHHHHHHGGGGGFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}{zywvusrqpnmlkjhgfedcba``^^]\\ZZYXXWWVUUTTSSRRQPPPPOONNNMMMMLLLKKKJJJJJJIIIIIHHHHHGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|zyxvutrqponlkjihgfddcba`_^]]\[[ZYXXWVVVUTTSSRQQQQPPOONNNNMMLLLLKKKKKJJJJIIIIHHHHHHHGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|zyxvutsqponmkjihgfedcbaa`_^]\\[ZZYXWWWVUUTTSRRRRQPPPOOONNMMMMLLLLLKKKJJJIIIIIIIHHHHHHHHGGGGGGGFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|zyxwutsrponmlkjiggeedcba`_^]]\[[ZYYXXWVVUUTSSSRRQQPPPPONNNNMMMMLLLKKKJJJJJJIIIIIIIHHHHHHGGGGGGFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|zyxwutsrqpnmlkjihgfedcbaa`_^]\\[ZZYYXWWVUUTTTSRRRQQQPPOOONNNNMMLLLLKKKKKJJJJJJIIIIHHHHHHGGGGGGGFFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{yxwvtsrqponmkkihhfeeccba``^^]\[[[ZYXXWVVVUUTSSSRRQQPPPOOOONNMMMMLLLLLKKKKJJJJIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{yxwvusrqponmlkjihgfedcbba`_^]]\\[ZZYXWWWVUUTTTSSRQQQQPPPOONNNNMMMMLLLKKKKJJJJJIIIIIIIHHHHHHHHGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{zxwvutsrponmlkjihgfeddcba``^^]]\[[ZYYXXWVVUUUTSSRRRRQQPPOOOONNNMMMLLLLKKKKKJJJJJJJIIIIIHHHHHHHGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{zywvutsrqponlkkihhgfedcbba`__^]\\[ZZYYXWWVVUUTTSSSRRQQPPPPOOONNMMMMMLLLLLKKKKJJJJJIIIIIHHHHHHHGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{zyxvutsrqponmlkjihgfeddcbaa`_^^]\[[ZYYXXXWVVUUUTSSRRRQQQPPPOONNNNNMMMMLLLLKKKJJJJJIIIIIIIHHHHHHHHGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{zyxwutsrqponmlkjihgffedccba``_^]]\[ZZZYXXWWVVUUTTSSSRRQQPPPPOOOONNNMMMLLLLKKKKKJJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{zyxwvttrqponmllkiihgfeedcba``_^^]\\[[ZYYXXWWVUUUTTTSRRRQQQPPPPOONNNMMMMMLLLLLKKKKKJJJJJIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{zyxwvutsrqponmlkjihggfedcbaa``_^]]\[[ZZYYXWWVVVUUTTSSSRRRQQPPPOOONNNNNMMMMLLLLKKKKJJJJJIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@~}|{zyxwvutsrqponmlkjiihgfedccba``__^]\\[[ZYYXXXWVVUUTTTTSSRRQQQPPPPOOONNNNMMMLLLLKKKKKJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@~}|{zyxwvutsrqponmllkjihgfeedcbba``_^^]\\[ZZZYXXWWVVVUUTTSSRRRRQQQPPPOONNNNMMMMMLLLLLKKKKKKJJJJJJIIIIIIHHHHHHHHHGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@~}|{zyxwvutsrqpponmlkjihggfedccba``__^]\\\[ZZYYXXWWVVUUTTTSSSRRQQQPPPOOOONNNNNMMMMLLLLKKKKKJJJJJJIIIIIIHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvuttsrqponmlkjjihgffedcbba``_^^]]\[[ZZYYXXWVVVUUUTTSSRRRQQQQPPPPOOONNNMMMMLLLLKKKKKJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvutsrqponmlkjjihgffeddcbaa``_^]]\\[[ZYYXXXWWVVUUTTTSSSRRRQQQPPPOOONNNNMMMMMLLLLLKKKKKJJJJJJJIIIIIIIHHHHHHHHGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvutsrqponmmlkjihhgfeddccba``_^^]]\[[ZZZYXXWWVVVUUUTTSSRRRQQQPPPPOOOONNNNNMMMMLLLLLKKKKKJJJJJJIIIIIIIIHHHHHHHHHGGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvutsrqpponmlkjjihgffedccbaa``_^]]\\\[ZZYYXXWWWVVUUTTSSSRRRRQQQPPPPOOONNNNMMMMLLLLLKKKKKJJJJJJIIIIIIIIHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvutsrqpponmlkjjihhgfeddcbba``__^]]\\[[ZZYYXXWWVVUUUTTTSSSRRRQQPPPPOOONNNNNMMMMMLLLLLKKKKKJJJJJJJIIIIIIIHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvutssrqponmmlkjihhgffedccbba``_^^]]\\[ZZYYXXXWWVVVUUTTSSSRRRQQQQPPPPOOOONNNNMMMMMLLLLLKKKKKKJJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvuutsrqponmmlkjjihgffeedcbba``__^^]\\[[ZZZYYXXWWVVUUUTTTSSSRRRQQQPPPPOOONNNNMMMMMLLLLLKKKKKKJJJJJJJIIIIIIIIHHHHHHHHHHGGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvuutsrqpponmlkkjihhgfeedccbba``_^^]]\\[[ZZYYXXWWWVVVUUTTTSSRRRQQQPPPPOOOONNNNNMMMMMLLLLLKKKKKJJJJJJJIIIIIIIHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxwvvutsrqqponmllkjiihggfeddcbbaa``_^^]\\[[[ZZYYXXWWVVVUUTTTSSSRRRRQQQQPPPPOOONNNNNMMMMMLLLLLKKKKKKKJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxxwvutsrqqponnmlkjjihggfeeddcbaa``__^^]]\[[ZZYYXXXWWVVVUUUTTTSSSRRRQQQPPPPOOONNNNNMMMMMLLLLLKKKKKKKJJJJJJJIIIIIIIIIHHHHHHHHHGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyxxwvutssrqponnmlkkjiihgffeddcbbaa``_^^]]\\[[ZZZYYXXWWVVVUUTTTSSSRRRQQQQPPPPOOOONNNNNMMMMMLLLLLKKKKKJJJJJJJIIIIIIIIIHHHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zyyxwvuttsrqpponmllkjiihggfeedccbaa``__^^]]\\[[ZZYYXXWWWVVVUUUTTTSSSRRRQQQQPPPPOOOONNNNMMMMMMLLLLLLKKKKKKJJJJJJIIIIIIIHHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zzyxwvuutsrqqponmmlkjjihhgffeddccbaa``_^^]]\\[[[ZZYYXXXWWVVUUUTTTSSSRRRQQQQPPPPOOOONNNNMMMMMMLLLLLLKKKKKKJJJJJJJJIIIIIIIIHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{zzyxwvuutsrqqponnmllkjiihggfeedccbba``__^^]]\\[[ZZYYXXXWWVVVUUUTTTSSSSRRRQQQQPPPPOOOONNNNNMMMMMLLLLLKKKKKJJJJJJJJIIIIIIIIIIHHHHHHHHHHGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{{zyxwvvutsrrqponnmllkjiihhgffeddcbbaa``_^^]]\\\[[ZZZYYXXWWWVVUUUTTTSSSRRRRQQQPPPPPOOOONNNNNMMMMMMLLLLLLKKKKKKJJJJJJIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{{zyxwvvutssrqpponmmlkjjihhgffeedccbbaa``_^^]]\\[[ZZZYYXXWWWVVVUUUTTTSSSRRRRQQQPPPPOOOONNNNNMMMMMMLLLLLLKKKKKKKJJJJJJJJIIIIIIIHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}|{{zyxwwvuttsrqpponnmlkkjiihggfeeddcbbaa``__^^]]\\[[[ZZYYXXXWWVVVUUUTTTSSSSRRRQQQQPPPPPOOOONNNNNMMMMMLLLLLKKKKKKJJJJJJJJJIIIIIIIIIIHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}||{zyxxwvuttsrqqpoonmllkjjihhgffeedccbbaa``_^^]]\\\[[ZZYYXXXWWVVVUUUTTTSSSSRRRQQQQPPPPPOOOONNNNNNMMMMMLLLLLLLKKKKKKJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}||{zyxxwvuutsrrqpoonmllkjjihhgffeeddcbbaa``__^^]]\\\[[ZZYYYXXXWWVVVUUUTTTTSSSRRRRQQQPPPPOOOONNNNNMMMMMLLLLLLLKKKKKKKKJJJJJJJIIIIIIIHHHHHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}||{zyxxwvuutsrrqpponmmlkkjiihhgffeddccbbaa``_^^^]]\\[[ZZYYYXXXWWVVVUUUTTTTSSSRRRRQQQQPPPPPOOOOONNNNNMMMMMLLLLLKKKKKKKJJJJJJJJJIIIIIIIIIHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}||{zyyxwvvutssrqpponnmllkjjihhggfeeddccbbaa``__^^]]\\[[ZZZYYXXXWWWVVVUUUTTTSSSRRRRQQQQPPPPOOOOONNNNNMMMMMMLLLLLLLKKKKKJJJJJJJJIIIIIIIIIIIHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAA~}||{zyyxwvvutssrqqpoonmllkjjihhggfeeddccbbaa``__^^]]\\[[ZZZYYXXXWWWVVVUUUTTTTSSSRRRRQQQQQPPPPOOOONNNNNMMMMMLLLLLLLLKKKKKKKKJJJJJJIIIIIIIIIHHHHHHHHHHHHHGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAA~}||{zyyxwvvuttsrrqpoonmmlkkjiihhgffeeddccbba```__^^]]\\[[[ZZYYYXXXWWVVVUUUTTTTSSSRRRQQQQQPPPPPOOOOONNNNNNMMMMMLLLLLKKKKKKKKJJJJJJJJJJIIIIIIHHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAA~}}|{zzyxwwvuttsrrqpponnmllkjjiihggffeddccbbaa``__^^]]\\[[[ZZYYYXXXWWWVVVUUUTTTTSSSSRRRRQQQPPPPOOOOONNNNNNMMMMMMLLLLLLLKKKKKJJJJJJJJJJIIIIIIIIIIHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAA~}}|{zzyxwwvuutsrrqpponnmmlkkjiihggffeeddcbbaa```__^^]]\\\[[ZZZYYYXXWWWVVVUUUTTTSSSSRRRRQQQQQPPPPPOOOONNNNNMMMMMLLLLLLLLKKKKKKKKJJJJJJIIIIIIIIIIIHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAA~}}|{zzyxxwvuutssrqqpoonmmlkkjiihhggffeddccbbaa``__^^]]]\\[[ZZZYYYXXXWWWVVVUUUTTTTSSSSRRRQQQQPPPPPOOOOONNNNNNMMMMMMLLLLLKKKKKKKKKJJJJJJJJIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAA~}}|{zzyxxwvvutssrqqpoonmmllkjjiihhgffeedccbbaa```__^^]]\\\[[[ZZYYYXXXWWVVVUUUTTTTSSSSRRRRRQQQQPPPPOOOOONNNNNMMMMMMMLLLLLLLKKKKKKJJJJJJJJJJIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAA~}}|{{zyxxwvvuttsrrqpponnmllkkjiihhgffeeddccbbaa```__^^]]\\[[[ZZYYYXXXWWWVVVVUUUTTTTSSSRRRRQQQQPPPPPPOOOOONNNNNMMMMMLLLLLLLLKKKKKKKKJJJJJJJIIIIIIIIIIIIHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAA~}}|{{zyxxwvvuttsrrqpponnmmlkkjjihhggffeeddccbba```__^^]]]\\[[[ZZZYYYXXWWWVVVUUUTTTTSSSSSRRRRQQQQPPPPOOOOONNNNNNNMMMMMMLLLLLKKKKKKKKKJJJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~}}|{{zyyxwvvuttsrrqqpoonmmllkjjiihhggfeeddccbbaa```__^^]]]\\[[ZZZYYYXXXWWWVVVVUUUTTTSSSSRRRRQQQQQPPPPPOOOONNNNNNMMMMMMMLLLLLLLKKKKKKJJJJJJJJJJJIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~}}|{{zyyxwwvuutssrqqpponnmllkkjjihhggffeeddccbbaa``__^^]]]\\\[[ZZZYYYXXXWWVVVVUUUTTTTTSSSSRRRQQQQPPPPPPOOOOOONNNNNMMMMMLLLLLLLLLKKKKKKKJJJJJJJJIIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~}}|{{zyyxwwvuutssrrqpponnmmlkkjjihhggffeeddccbbaa```__^^]]]\\[[[ZZYYYXXXXWWWVVVUUUTTTTSSSSRRRRRQQQQQPPPPOOOOONNNNNNMMMMMMMLLLLLLKKKKKKKKKJJJJJJJIIIIIIIIIIIIHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~}}|{{zyyxwwvvuttsrrqppoonmmlkkjjiihhggfeeddcccbbaa``__^^]]]\\\[[[ZZZYYYXXWWWVVVVUUUUTTTTSSSRRRRQQQQQPPPPPPOOOONNNNNMMMMMMMMLLLLLLLKKKKKKKJJJJJJJJJJJIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{zzyxxwvvuttsrrqppoonnmllkkjjihhggffeeddccbbaa```__^^^]]\\\[[ZZZYYYXXXWWWWVVVUUUTTTTSSSSRRRRRQQQQPPPPPOOOOOONNNNNNMMMMMLLLLLLLLLKKKKKKKJJJJJJJJJJIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{zzyxxwvvuttssrqqpponnmllkkjjiihhggffeedccbbbaa```__^^]]\\\[[[ZZZYYYXXXWWWVVVVUUUUTTTSSSSRRRRQQQQQQPPPPPOOOONNNNNNNMMMMMMMLLLLLLKKKKKKKKKKJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{zzyxxwvvuutssrqqpponnmmllkjjiihhggffeeddccbbbaa``__^^^]]\\\[[[ZZYYYXXXXWWWWVVUUUUTTTTSSSSSRRRRQQQQPPPPPPOOOOONNNNNMMMMMMMMLLLLLLLKKKKKKKKJJJJJJJJJJIIIIIIIIIIIHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{zzyxxwvvuutssrrqppoonmmllkkjjiihggffeedddccbbaa```__^^]]]\\[[[ZZZZYYYXXWWWWVVVUUUUUTTTSSSSRRRRRQQQQQPPPPOOOOOONNNNNNNMMMMMLLLLLLLLLKKKKKKKJJJJJJJJJJJIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{zzyxxwwvuutssrrqppoonnmllkkjjiihhggffeeddccbbaaa``__^^^]]]\\\[[ZZZYYYXXXXWWWVVVUUUUTTTTSSSSSRRRQQQQQPPPPPPOOOOONNNNNNNMMMMMMMLLLLLLKKKKKKKKKKJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{zzyyxwwvuuttsrrqqpponnmmllkjjiihhggffeedddccbbaa```__^^^]]\\\[[[ZZZYYYXXXWWWVVVVUUUUTTTSSSSSRRRRRQQQQPPPPPPOOOOOONNNNNMMMMMMMMLLLLLLLKKKKKKKKKJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{zzyyxwwvvuttssrqqpponnmmllkkjjiihhgfffeeddccbbaaa``__^^^]]]\\\[[[ZZYYYYXXXWWWWVVUUUUUTTTTSSSSRRRRQQQQQQPPPPPOOOOONNNNNNNNMMMMMLLLLLLLLLLKKKKKKJJJJJJJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{{zyyxwwvvuttssrqqppoonnmllkkjjiihhggffeeddcccbbaa```__^^^]]]\\[[[ZZZZYYYXXWWWWVVVVUUUUTTTSSSSSRRRRRQQQQPPPPPPPOOOOONNNNNNMMMMMMMMLLLLLLLKKKKKKKKKJJJJJJJJJJIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{{zyyxxwvvuutssrrqppoonnmmllkkjiihhhggfeeeddccbbaaa```__^^]]]\\\[[[ZZZYYYXXXXWWWVVVUUUUUTTTTSSSRRRRRRQQQQQPPPPPOOOOOOONNNNNMMMMMMMMLLLLLLLKKKKKKKKKKJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{{zyyxxwvvuutssrrqqpponnmmllkkjjiihhggffeeddcccbbaaa``__^^^]]]\\[[[[ZZZYYYXXXWWWWVVVVUUUTTTTTSSSSSRRRQQQQQQPPPPPPOOOOONNNNNNNNMMMMMLLLLLLLLLLKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~}||{{zyyxxwwvuuttsrrqqpponnmmllkkjjiihhggfffedddccbbbaa```__^^^]]\\\\[[[ZZYYYYXXXXWWWVVVUUUUUTTTTSSSSRRRRRQQQQPPPPPPPOOOOOONNNNNNMMMMMMMMLLLLLLLKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBB~~}}|{{zzyxxwwvuuttssrqqppoonnmmllkkjiihhhggffeedddcbbbaaa``__^^^]]]]\\[[[ZZZYYYYXXXWWWWVVVVUUUTTTTTSSSSRRRRQQQQQQQPPPPOOOOOOONNNNNNMMMMMMMMLLLLLLLKKKKKKKKKKKJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBB~~}}|{{zzyxxwwvvuttssrrqqpoonnmmllkkjjiihhggffeeeddccbbbaa```__^^^]]]\\\[[[ZZZYYYXXXXWWWVVVVUUUUUTTTSSSSSRRRRRQQQQQPPPPPPPOOOONNNNNNNNMMMMMMLLLLLLLLLLKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBB~~}}|{{zzyxxwwvvuttssrrqqppoonmmllkkjjiihhgggffeeddcccbbaaa```__^^^]]]\\[[[ZZZZYYYXXXWWWWVVVVUUUUTTTTTSSSSRRRRRQQQQQPPPPPPOOOOOOONNNNNMMMMMMMMMLLLLLLLKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIHHHHHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBB~~}}|{{zzyyxwwvvuuttsrrqqppoonnmmlkkkjjiihhggffeeeddccbbbaa```___^^^]]\\\[[[[ZZYYYYXXXXWWVVVVVUUUUTTTTSSSSSRRRRRQQQQQPPPPPPOOOOOONNNNNNNMMMMMMMMLLLLLLLKKKKKKKKKKKJJJJJJJJJIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBB~~}}|{{zzyyxwwvvuuttsrrqqppoonnmmllkkjjiihhggfffeeddcccbbaaa```__^^^]]]\\\[[[ZZZYYYYXXXWWWWVVVVUUUUTTTTTSSSRRRRRRRQQQQPPPPPPPOOOOONNNNNNNNMMMMMMLLLLLLLLLLKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBB~~}}||{zzyyxxwvvuuttssrqqppoonnmmllkkjjiiihhggffeedddccbbbaaa``___^^^]]\\\[[[[ZZZYYYXXXXWWWVVVVVUUUUTTTTSSSSSRRRRRQQQQQQPPPPPOOOOOOONNNNNNMMMMMMMMMLLLLLLLLKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBB~~}}||{zzyyxxwwvuuttssrrqqppoonmmmlkkkjjiihhggffeeeddcccbbbaa```___^^]]]\\\[[[ZZZZYYYXXXXWWWWVVVUUUUUTTTTSSSSSRRRRRQQQQQPPPPPPPOOOOONNNNNNNNMMMMMMMMLLLLLLLKKKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{zzyyxxwwvuuttssrrqqppoonnmmllkkjjiihhhggffeedddcccbbaa```___^^^]]]\\\[[[ZZZYYYYXXXXWWWVVVVVUUUTTTTTSSSSSRRRRRQQQQQQPPPPPPOOOOOONNNNNNNMMMMMMMLLLLLLLLLLKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{zzyyxxwwvvuttssrrqqppoonnmmllkkjjiiihhggffeeedddccbbaaa```___^^]]]\\\\[[ZZZZYYYYXXXWWWWWVVVUUUUUTTTTSSSSSRRRRRQQQQQQPPPPPOOOOOOOONNNNNMMMMMMMMMMLLLLLLLLKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zyyxxwwvvuuttsrrqqppoonnmmlllkkjjiihhgggffeeeddccbbbaa````__^^^]]]\\\[[[[ZZZYYYXXXXWWWWVVVVVUUUTTTTTTSSSRRRRRRRQQQQPPPPPPPPOOOOONNNNNNNNMMMMMMMMLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zyyxxwwvvuuttssrrqqppoonnmmllkkjjiihhhggfffeeddcccbbaaa```___^^^]]]\\\[[[ZZZZYYYXXXXWWWWVVVVUUUUUTTTTSSSSSSRRRRQQQQQQQPPPPPOOOOOOONNNNNNNMMMMMMMLLLLLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyxxwwvvuuttssrrqqppoonnmmllkkjjiiihhgggffeedddccbbbaa````__^^^]]]]\\[[[[ZZZYYYYXXXXWWWWVVVVUUUUTTTTTSSSSSRRRRRRQQQQQPPPPPPOOOOOOONNNNNNMMMMMMMMMMLLLLLLLLKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyyxwwvvuuttssrrqqppoonnmmlllkkjjiihhhggffeeeddcccbbaaa```___^^^]]]\\\\[[ZZZZYYYYXXXXWWWWVVVVUUUUTTTTTSSSSSRRRRRQQQQQQPPPPPPPOOOOONNNNNNNNNMMMMMMMLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvuuttssrrqqppoonnnmmllkkjjiiihhggfffeedddccbbbaaa```___^^]]]]\\\[[[ZZZZYYYYXXXWWWWVVVVVUUUUTTTTTSSSSSRRRRRQQQQQQPPPPPPOOOOOOOONNNNNNNMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvutttsrrrqqppoonnmmllkkkjjiihhgggffeeeddcccbbbaa```___^^^]]]\\\\[[[ZZZZYYYXXXXWWWWVVVVVUUUUTTTTTSSSSSRRRRRRQQQQPPPPPPPPOOOOOONNNNNNNMMMMMMMMMMLLLLLLLLLKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmlllkkjjiihhhggfffeedddccbbbaaa```___^^]]]]\\\[[[[ZZZYYYYXXXXWWWWVVVVUUUUUTTTTSSSSSSRRRRRQQQQQQQPPPPPPOOOOOONNNNNNNNNMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvuuttssrrqqppoonnnmmllkkjjjiihhggfffeedddcccbbbaa````__^^^^]]\\\\[[[ZZZZYYYYXXXXWWWWVVVVUUUUTTTTTTSSSSRRRRRRRQQQQQPPPPPPOOOOOOOOONNNNNNMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvuuttssrrqqpppoonnmmllkkjjjiihhgggffeeeddcccbbbaaa```___^^^]]]\\\[[[[ZZZZYYYYXXXWWWWWVVVVUUUUUTTTTSSSSSSRRRRRQQQQQQPPPPPPPPOOOOOONNNNNNNMMMMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvuuttssrrrqqppoonnmmllkkkjjiihhhggfffeedddcccbbbaa````__^^^^]]]\\\[[[[ZZZZYYYXXXXXWWWVVVVVUUUUTTTTTTSSSSSRRRRRQQQQQQQPPPPPPOOOOOOONNNNNNNNNMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvuutttssrrqqppoonnmmlllkkjjjiihhhggffeeedddccbbbaaa```___^^^]]]]\\\[[[[ZZZYYYYXXXXWWWWWVVVUUUUUUTTTTSSSSSSRRRRRRQQQQQPPPPPPPOOOOOOOONNNNNNNMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCC~~}}||{{zzyyxxxwwvvuuttssrrqqppoonnnmmllkkjjjiihhhggfffeeeddcccbbbaaa```___^^^]]]\\\\[[[ZZZZYYYYXXXXWWWWVVVVVUUUUTTTTTTSSSSSRRRRRQQQQQQQPPPPPPPOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvuuttssrrqqpppoonnmmllkkkjjiiihhgggffeeedddccbbbaaa```___^^^^]]]\\\[[[[ZZZZYYYYXXXWWWWWVVVVUUUUUUTTTTTSSSSRRRRRRRQQQQQQPPPPPPOOOOOOOONNNNNNNNMMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvuuttssrrqqpppoonnmmmllkkjjjiihhhggfffeedddcccbbaaaa```___^^^]]]]\\\[[[[ZZZZYYYXXXXXWWWVVVVVVUUUUTTTTTSSSSSSRRRRRQQQQQQPPPPPPPPOOOOOOONNNNNNNMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC~~}}||{{zzyyxxwwvvuuttssrrrqqppoonnmmmllkkjjjiihhhgggffeeedddccbbbaaa````__^^^^]]]\\\\[[[ZZZZYYYYXXXXWWWWWVVVVUUUUUTTTTTTSSSSRRRRRRRQQQQQQQPPPPPPOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCC~~}}||{{zzyyxxwwvvuuttsssrrqqppoonnnmmllkkkjjiiihhgggfffeedddcccbbbaaa```___^^^]]]]\\\[[[[ZZZZYYYYXXXXXWWWVVVVVUUUUUTTTTTSSSSSSRRRRRRQQQQQQPPPPPPOOOOOOOOONNNNNNNNMMMMMMMMLLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCC~~}}||{{zzyyxxwwvvuuuttssrrqqpppoonnmmlllkkjjjiihhhggfffeeeddccccbbaaa````__^^^^]]]\\\\[[[[ZZZZYYYYXXXXWWWWWVVVVUUUUTTTTTTTSSSSSRRRRRQQQQQQQPPPPPPPPOOOOOONNNNNNNNMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvuuuttssrrqqpppoonnmmmllkkjjjiiihhgggffeeedddcccbbbaaa```___^^^]]]]\\\\[[[[ZZZYYYYXXXXXWWWWVVVVVUUUUUTTTTTSSSSSRRRRRRRQQQQQQPPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvvuuttssrrqqqppoonnnmmlllkkjjiiihhhggfffeeedddccbbbaaaa```___^^^]]]\\\\\[[[ZZZZYYYYXXXXWWWWWVVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPOOOOOOOOONNNNNNNMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxxwwvvuuttssrrrqqppooonnmmlllkkjjjiihhhgggffeeedddcccbbbaaa````__^^^^]]]]\\\[[[[ZZZZYYYYXXXXXWWWWVVVVUUUUUUTTTTTSSSSSRRRRRRRQQQQQQQPPPPPPPOOOOOOONNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxxwwvvuuttsssrrqqpppoonnmmmllkkjjjiiihhgggfffeeddddccbbbbaaa```___^^^^]]]\\\\[[[ZZZZZYYYYXXXXWWWWWVVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxxwwvvuutttssrrqqpppoonnmmmlllkkjjiiihhhggfffeeedddcccbbbaaa````__^^^^]]]]\\\[[[[[ZZZYYYYXXXXXWWWWWVVVVUUUUUUTTTTTSSSSSSRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyyxxwwvvuuuttssrrqqqppoonnnmmlllkkjjjiihhhgggffeeeeddcccbbbbaaa```____^^^]]]\\\\[[[[ZZZZYYYYYXXXXWWWWVVVVVVUUUUTTTTTSSSSSSSRRRRRRRQQQQQQPPPPPPPOOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzzyyxxwwvvuuuttssrrrqqppooonnmmmllkkkjjiiihhgggfffeeedddcccbbbaaaa```___^^^^]]]\\\[[[[[ZZZZYYYYXXXXXWWWWVVVVVUUUUUTTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzzyyxxwwvvvuuttssrrrqqpppoonnmmmllkkkjjjiihhhgggfffeedddccccbbbaaa```____^^^]]]]\\\\[[[ZZZZZYYYYXXXXWWWWWVVVVVUUUUUTTTTTTSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{{zzyyxxwwvvvuuttsssrrqqpppoonnnmmlllkkjjjiiihhhggfffeeedddcccbbbaaa````___^^^^]]]\\\\[[[[[ZZZYYYYXXXXXWWWWWVVVVVUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQPPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{{zzyyxxwwwvvuutttssrrqqqppooonnmmlllkkkjjiiihhhgggffeeedddccccbbbaaa````___^^^]]]]\\\\[[[[ZZZZYYYYYXXXXWWWWVVVVVVUUUUUUTTTTTSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDD~~}}||{{{zzyyxxxwwvvuutttssrrqqqppooonnmmmllkkkjjjiihhhgggfffeeedddcccbbbaaa````____^^^]]]\\\\[[[[[ZZZZYYYYXXXXXWWWWWVVVVVUUUUUTTTTTSSSSSSSRRRRRRRQQQQQQPPPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDD~~}}|||{{zzyyxxxwwvvuuuttssrrrqqpppoonnnmmlllkkjjjiiihhhggfffeeeddddccbbbaaaa````___^^^]]]]]\\\[[[[ZZZZZYYYYXXXXXWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDD~~}}|||{{zzyyxxxwwvvuuuttsssrrqqpppoonnnmmlllkkkjjiiihhhgggfffeeedddcccbbbaaa````____^^^]]]]\\\\[[[[ZZZZYYYYXXXXXWWWWWWVVVVVUUUUUTTTTTSSSSSSRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDD~~}}|||{{zzyyxxxwwvvuuuttsssrrqqqppooonnmmmllkkkjjjiiihhgggfffeeedddcccbbbbaaa````___^^^^]]]]\\\\[[[ZZZZZYYYYYXXXXWWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDD~~}}|||{{zzyyyxxwwvvvuuttsssrrqqqppooonnnmmlllkkjjjiiihhhgggfffeeedddcccbbbaaa````____^^^]]]]\\\\[[[[[ZZZZYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDD~~}}|||{{zzyyyxxwwvvvuutttssrrrqqpppoonnnmmmllkkkjjiiihhhgggfffeeedddccccbbbaaa````___^^^^]]]]\\\\[[[[ZZZZZYYYYXXXXXWWWWWVVVVUUUUUUTTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDD~~}}|||{{zzyyyxxwwwvvuutttssrrrqqpppoonnnmmmllkkkjjjiiihhhggffffeeedddcccbbbaaaa````___^^^]]]]\\\\\[[[[ZZZZYYYYXXXXXWWWWWWVVVVVUUUUUUTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDD~~}}}||{{zzzyyxxwwwvvuuuttsssrrqqqppooonnmmmlllkkjjjiiihhhgggfffeeedddccccbbbaaa````____^^^^]]]\\\\[[[[ZZZZZYYYYYXXXXXWWWWVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDD~~}}}||{{zzzyyxxxwwvvuuuttsssrrqqqppooonnnmmlllkkkjjjiihhhggggfffeeedddcccbbbbaaa````___^^^^]]]]\\\\[[[[ZZZZZYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~}}}||{{zzzyyxxxwwvvuuuttsssrrqqqpppoonnnmmmlllkkjjjiiihhhgggfffeeedddccccbbbaaa````____^^^^]]]\\\\[[[[[ZZZZZYYYYYXXXXWWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~}}}||{{zzzyyxxxwwvvvuutttssrrrqqpppoonnnmmmlllkkjjjiiihhhgggfffeeeddddcccbbbbaaa````___^^^^]]]]\\\\\[[[[ZZZZYYYYYXXXXWWWWWWVVVVVUUUUUUTTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~}}}||{{{zzyyxxxwwvvvuutttssrrrqqqppooonnnmmlllkkkjjjiihhhhgggfffeeedddcccbbbbaaaa````___^^^^]]]]\\\\[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~}}}||{{{zzyyxxxwwvvvuutttsssrrqqqpppoonnnmmmllkkkjjjiiihhhgggfffeeeedddcccbbbbaaa````___^^^^^]]]]\\\\[[[[ZZZZZYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~}}}||{{{zzyyyxxwwwvvuuuttsssrrqqqpppoonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbbaaaa````___^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEE~~}}}||{{{zzyyyxxwwwvvuuuttsssrrrqqpppooonnmmmlllkkkjjjiiihhhgggfffeeeddddcccbbbaaaa````____^^^^]]]]\\\\[[[[[ZZZZYYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEE~~}}}||{{{zzyyyxxwwwvvuuutttssrrrqqpppooonnnmmlllkkkjjjiiihhhgggfffeeeedddccccbbbaaaa````___^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEE~~}}}||{{{zzyyyxxxwwvvvuutttssrrrqqqpppoonnnmmmlllkkkjjjiiihhhgggfffeeeddddcccbbbaaaa````____^^^^]]]]\\\\\[[[[ZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEE~~~}}|||{{zzzyyxxxwwvvvuutttsssrrqqqpppoonnnmmmlllkkkjjjiiihhhgggffffeeedddccccbbbaaaa````___^^^^]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEE~~~}}|||{{zzzyyxxxwwvvvuuuttsssrrrqqpppooonnmmmlllkkkjjjiiihhhgggffffeeeddddcccbbbbaaaa````___^^^^]]]]]\\\\[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEE~~~}}|||{{zzzyyxxxwwvvvuuuttsssrrrqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeeedddccccbbbaaaa````___^^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUTTTTTTSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEE~~~}}|||{{zzzyyxxxwwwvvuuuttsssrrrqqqpppoonnnmmmlllkkkjjjiiihhhggggfffeeeddddcccbbbbaaaa````___^^^^^]]]]\\\\[[[[[ZZZZYYYYYXXXXXXWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEE~~~}}|||{{zzzyyxxxwwwvvuuutttssrrrqqqpppooonnmmmlllkkkjjjiiihhhhgggfffeeeddddcccbbbbaaaa````____^^^^]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEE~~~}}|||{{zzzyyyxxwwwvvvuutttsssrrqqqpppooonnnmmmlllkkkjjjiiihhhgggffffeeeddddcccbbbbaaaa````____^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEE~~~}}|||{{zzzyyyxxwwwvvvuutttsssrrrqqpppooonnnmmmlllkkkjjjiiihhhhgggfffeeeedddccccbbbbaaa````____^^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE~~~}}|||{{{zzyyyxxxwwvvvuuuttsssrrrqqqpppoonnnmmmlllkkkjjjiiihhhhgggfffeeeedddccccbbbbaaaa````____^^^^]]]]\\\\\[[[[ZZZZZYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE~~~}}|||{{{zzyyyxxxwwvvvuuutttssrrrqqqpppooonnnmmllllkkkjjjiiihhhggggfffeeeedddccccbbbbaaaa````____^^^^]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE~~~}}|||{{{zzyyyxxxwwvvvuuutttssrrrqqqpppooonnnmmmlllkkkjjjiiiihhhgggfffeeeedddccccbbbbaaaa````____^^^^]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXXWWWWWVVVVVVVUUUUUUTTTTTTTSSSSSSRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFF~~~}}|||{{{zzyyyxxxwwwvvuuutttsssrrrqqpppooonnnmmmlllkkkkjjjiiihhhgggffffeeeddddccccbbbbaaa`````____^^^^]]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFF~~~}}}||{{{zzzyyxxxwwwvvvuutttsssrrrqqqpppooonnnmmmllkkkkjjjiiihhhhgggfffeeeeddddcccbbbbaaaa````____^^^^]]]]]\\\\[[[[[ZZZZZYYYYYYXXXXXWWWWWWVVVVVVUUUUUUUTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQPPPPPPPPOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFF~~~}}}||{{{zzzyyxxxwwwvvvuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiiihhhgggffffeeeddddccccbbbaaaa`````____^^^^]]]]]\\\\\[[[[ZZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUUTTTTTTTSSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFF~~~}}}||{{{zzzyyyxxwwwvvvuuutttssrrrqqqpppooonnnmmmlllkkkkjjjiiihhhggggfffeeeeddddcccbbbbaaaa````____^^^^^]]]]\\\\\[[[[ZZZZZZYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFF~~~}}}||{{{zzzyyyxxwwwvvvuuutttssrrrqqqpppooonnnmmmmlllkkkjjjiiihhhhgggffffeeeedddccccbbbaaaa`````____^^^^]]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUUTTTTTTTTSSSSSSRRRRRRRQQQQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFF~~~}}}||{{{zzzyyyxxxwwvvvuuutttsssrrrqqppppooonnnmmmlllkkkjjjiiiihhhggggfffeeeedddccccbbbbaaaa`````____^^^^]]]]]\\\\[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFF~~~}}}|||{{zzzyyyxxxwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkkjjjiiihhhggggffffeeeddddccccbbbbaaaa````____^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUUTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFF~~~}}}|||{{zzzyyyxxxwwwvvuuutttsssrrrqqqpppooonnnmmmllllkkkjjjiiihhhhggggfffeeeedddccccbbbbaaaa`````____^^^^^]]]]\\\\\[[[[ZZZZZZYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFF~~~}}}|||{{zzzyyyxxxwwwvvvuutttsssrrrqqqpppooonnnnmmmlllkkkjjjiiiihhhhgggfffeeeeddddccccbbbbaaaa````____^^^^^]]]]\\\\\[[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVVUUUUUUUTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOOOOONNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFF~~~}}}|||{{zzzyyyxxxwwwvvvuuutttssrrrrqqqpppooonnnmmmlllkkkkjjjiiihhhhgggffffeeeeddddcccbbbbaaaa`````____^^^^^]]]]]\\\\[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRQQQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFF~~~}}}|||{{{zzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmllllkkkjjjiiiihhhggggffffeeeddddccccbbbbaaaa`````____^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYXXXXXWWWWWWVVVVVVVUUUUUUUTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFF~~~}}}|||{{{zzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmmlllkkkjjjjiiihhhhgggffffeeeddddccccbbbbbaaa`````____^^^^^]]]]]\\\\\[[[[ZZZZZZYYYYYXXXXXXXWWWWWVVVVVVUUUUUUUTTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPOOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFF~~~}}}|||{{{zzzyyyxxwwwvvvuuutttsssrrrqqqppppoonnnnmmmlllkkkkjjjiiiihhhgggffffeeeeddddccccbbbbaaaa`````____^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYXXXXXXWWWWWWVVVVVVVUUUUUUTTTTTTTSSSSSSSSSRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF~~~}}}|||{{{zzzyyyxxxwwvvvuuutttsssrrrqqqqpppooonnnmmmllllkkkjjjiiiihhhggggffffeeeddddccccbbbbbaaaa````____^^^^^]]]]]\\\\\[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUUTTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF~~~}}}|||{{{zzzyyyxxxwwwvvvuuttttssrrrrqqqpppooonnnmmmmlllkkkjjjjiiihhhhgggffffeeeeddddccccbbbbaaaa`````____^^^^^]]]]\\\\\[[[[[[ZZZZZYYYYYYXXXXXWWWWWWWVVVVVVVUUUUUUTTTTTTTSSSSSSSSSRRRRRRRQQQQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGF~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnnmmmlllkkkjjjjiiiihhhggggffffeeeedddccccbbbbaaaaa````____^^^^^]]]]]\\\\\\[[[[ZZZZZYYYYYYXXXXXXXWWWWWVVVVVVVUUUUUUUTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqppppooonnnmmmlllkkkkjjjjiiihhhhgggffffeeeeddddccccbbbbaaaa`````____^^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUTTTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqqpppooonnnmmmllllkkkjjjjiiihhhhggggfffeeeedddddcccbbbbaaaaa`````____^^^^]]]]]\\\\\\[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVUUUUUUUTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttssssrrrqqqpppooonnnmmmmlllkkkjjjjiiiihhhggggffffeeeeddddccccbbbbaaaa`````____^^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVUUUUUUTTTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuuttttsssrrrqqqpppoooonnnmmmlllkkkkjjjjiiihhhhggggfffeeeeddddcccccbbbaaaaa`````____^^^^]]]]]]\\\\\[[[[[ZZZZZYYYYYYYXXXXXXWWWWWVVVVVVVVUUUUUUUTTTTTTTSSSSSSSSSRRRRRRRQQQQQQQQQQPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvvuuutttsssrrrqqqppppooonnnmmmllllkkkjjjjiiihhhhggggffffeeeeddddccccbbbbaaaaa````____^^^^^^]]]]]\\\\[[[[[[ZZZZZZYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGG~~~}}}|||{{{zzzyyyyxxxwwwvvvuuutttsssrrrqqqppppooonnnmmmmlllkkkkjjjiiiihhhhgggffffeeeeddddccccbbbbbaaaa`````____^^^^^]]]]]\\\\\[[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqqpppooonnnnmmmllllkkkjjjjiiihhhhggggffffeeeeddddccccbbbbaaaaa````____^^^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUUUTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttssssrrrqqqpppoooonnnmmmllllkkkjjjjiiiihhhhgggffffeeeeddddccccbbbbbaaaa`````_____^^^^]]]]]\\\\\\[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttssssrrrqqqppppooonnnmmmmlllkkkkjjjiiiihhhhggggffffeeedddddccccbbbbaaaaa`````____^^^^^]]]]]\\\\\[[[[[[ZZZZZZYYYYYXXXXXXXWWWWWWVVVVVVVUUUUUUUUTTTTTTSSSSSSSSSRRRRRRRRQQQQQQQQQQQPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuuttttsssrrrqqqppppooonnnnmmmllllkkkjjjjiiihhhhggggffffeeeeddddccccbbbbbaaaaa````_____^^^^^]]]]\\\\\\[[[[[[ZZZZZYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUTTTTTTTTSSSSSSSRRRRRRRRRRQQQQQQQQQPPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvuuuutttsssrrrqqqqpppooonnnnmmmllllkkkjjjjiiiihhhhgggffffeeeeedddcccccbbbbbaaaa`````____^^^^^]]]]]\\\\\\[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUUUUTTTTTTSSSSSSSSSSRRRRRRRQQQQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHGGGG~~~}}}|||{{{zzzyyyxxxwwwvvvvuuutttsssrrrqqqqpppoooonnnmmmmlllkkkkjjjiiiihhhhggggffffeeeeddddcccccbbbbaaaa`````_____^^^^^]]]]]\\\\\[[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHGG~~~}}}|||{{{zzzyyyxxxwwwvvvvuuutttssssrrrqqqppppooonnnmmmmllllkkkjjjjiiiihhhggggffffeeeedddddccccbbbbbaaaa`````_____^^^^]]]]]]\\\\\[[[[[ZZZZZZZYYYYYXXXXXXXWWWWWWVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHGG~~~}}}|||{{{zzzyyyxxxxwwwvvvuuutttssssrrrqqqppppooonnnnmmmllllkkkjjjjiiiihhhhggggffffeeeeddddcccccbbbbaaaa`````_____^^^^^]]]]]\\\\\\[[[[[[ZZZZZYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHG~~~}}}|||{{{zzzyyyxxxxwwwvvvuuuttttsssrrrqqqqpppooonnnnmmmmlllkkkkjjjiiiihhhhggggffffeeeedddddccccbbbbaaaaa`````_____^^^^^]]]]]\\\\\\[[[[[ZZZZZZYYYYYYXXXXXXXWWWWWWVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHH~~~}}}|||{{{zzzyyyyxxxwwwvvvuuuutttsssrrrrqqqpppoooonnnmmmmlllkkkkjjjjiiiihhhhggggfffeeeeeddddccccbbbbbaaaa``````____^^^^^]]]]]]\\\\\[[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUUTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHH~~~}}}|||{{{zzzyyyyxxxwwwvvvuuuutttsssrrrrqqqppppooonnnnmmmllllkkkjjjjiiiihhhhggggffffeeeedddddccccbbbbaaaaa`````_____^^^^^]]]]]\\\\\\[[[[[ZZZZZZZYYYYYXXXXXXXWWWWWWVVVVVVVVUUUUUUUTTTTTTTTTSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHH~~~}}}|||{{{zzzzyyyxxxwwwvvvvuuutttssssrrrqqqppppooonnnnmmmllllkkkkjjjjiiihhhhhgggfffffeeeeddddccccbbbbbaaaa``````____^^^^^]]]]]]\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXWWWWWWWWVVVVVVUUUUUUUUUTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHH~~~}}}|||{{{zzzzyyyxxxwwwvvvvuuutttssssrrrqqqppppoooonnnmmmmlllkkkkjjjjiiiihhhhggggffffeeeeddddcccccbbbbaaaaa`````_____^^^^^]]]]]\\\\\\[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWVVVVVVVVUUUUUUUTTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHH~~~}}}|||{{{{zzzyyyxxxwwwwvvvuuuttttsssrrrqqqqpppoooonnnmmmmllllkkkkjjjiiiihhhhggggfffffeeeeddddccccbbbbbaaaaa`````_____^^^^^]]]]]\\\\\[[[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWWWVVVVVVUUUUUUUUUTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHH~~~}}}|||{{{{zzzyyyxxxwwwwvvvuuuttttsssrrrrqqqppppooonnnnmmmllllkkkkjjjjiiiihhhhggggffffeeeedddddccccbbbbbaaaa``````____^^^^^^]]]]\\\\\\\[[[[[ZZZZZZZYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHH~~~}}}|||{{{{zzzyyyxxxxwwwvvvuuuutttsssrrrrqqqppppooonnnnmmmmlllkkkkjjjjiiiihhhhggggffffeeeeeddddcccccbbbbaaaaa`````_____^^^^^]]]]]]\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRRQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHH~~~}}}||||{{{zzzyyyxxxxwwwvvvuuuutttssssrrrqqqppppoooonnnmmmmllllkkkkjjjiiiihhhhhgggfffffeeeedddddccccbbbbbaaaa``````____^^^^^^]]]]]\\\\\\[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHH~~~}}}||||{{{zzzyyyxxxxwwwvvvuuuutttssssrrrqqqqpppoooonnnnmmmllllkkkkjjjjiiiihhhhggggffffeeeeeddddcccccbbbbaaaaa`````_____^^^^^]]]]]]\\\\\[[[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHH~~~}}}||||{{{zzzyyyyxxxwwwvvvvuuutttssssrrrrqqqppppooonnnnmmmmllllkkkjjjjiiiihhhhgggggffffeeeeddddcccccbbbbbaaaaa`````_____^^^^^]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIHHHHH~~~}}}||||{{{zzzyyyyxxxwwwvvvvuuuttttsssrrrrqqqppppooonnnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeedddddccccbbbbbbaaaa`````_____^^^^^^]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXWWWWWWWWVVVVVVVUUUUUUUTTTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHH~~~}}}||||{{{zzzyyyyxxxwwwwvvvuuuttttsssrrrrqqqqpppoooonnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeeeddddcccccbbbbbaaaaa`````_____^^^^^]]]]]]\\\\\\[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWVVVVVVVUUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHH~~~}}}||||{{{zzzyyyyxxxwwwwvvvuuuutttssssrrrqqqqppppooonnnnmmmmllllkkkjjjjiiiihhhhgggggffffeeeedddddccccbbbbbaaaaa``````____^^^^^^]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIH~~~}}}}|||{{{zzzzyyyxxxwwwwvvvuuuutttssssrrrqqqqppppooonnnnmmmmllllkkkkjjjjiiiihhhhggggfffffeeeedddddccccbbbbbaaaaa`````_____^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWVVVVVVVUUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIII~~~}}}}|||{{{zzzzyyyxxxxwwwvvvuuuutttssssrrrrqqqppppooonnnnnmmmllllkkkkjjjjiiiihhhhhggggffffeeeedddddcccccbbbbaaaaa``````____^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXWWWWWWWVVVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIII~~~}}}}|||{{{zzzzyyyxxxxwwwvvvvuuuttttsssrrrrqqqqpppoooonnnnmmmmlllkkkkjjjjjiiiihhhhggggffffeeeeedddddccccbbbbbaaaaa`````_____^^^^^]]]]]]]\\\\\[[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIII~~~}}}}|||{{{zzzzyyyxxxxwwwvvvvuuuttttsssrrrrqqqqppppooonnnnmmmmllllkkkkjjjjiiiihhhhggggfffffeeeedddddcccccbbbbaaaaaa`````_____^^^^^^]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWVVVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIII~~~}}}}|||{{{{zzzyyyxxxxwwwvvvvuuuttttssssrrrqqqqppppoooonnnmmmmllllkkkkjjjjiiiihhhhhggggffffeeeeeddddcccccbbbbbaaaaa`````_____^^^^^^]]]]]]\\\\\[[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIII~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvuuuutttssssrrrrqqqppppoooonnnnmmmllllkkkkjjjjjiiiihhhhggggfffffeeeedddddcccccbbbbaaaaaa`````_____^^^^^^]]]]]\\\\\\\[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWVVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIII~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvuuuutttssssrrrrqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhggggfffffeeeeeddddcccccbbbbbaaaaa``````_____^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIII~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvuuuuttttsssrrrrqqqqppppooonnnnmmmmllllkkkkjjjjiiiihhhhhggggffffeeeeedddddccccbbbbbbaaaaa`````______^^^^^]]]]]\\\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXWWWWWWWVVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIII~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvvuuuttttsssrrrrqqqqppppoooonnnmmmmllllkkkkkjjjjiiiihhhhgggggffffeeeedddddcccccbbbbbaaaaa``````_____^^^^^^]]]]]\\\\\\[[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIII~~~}}}}|||{{{{zzzyyyyxxxxwwwvvvvuuuttttssssrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhhggggfffffeeeedddddcccccbbbbbaaaaa`````______^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJIIIIIIII~~~~}}}||||{{{zzzzyyyxxxxwwwvvvvuuuutttssssrrrrqqqppppoooonnnnmmmmllllkkkkjjjjiiiiihhhhggggfffffeeeeeddddcccccbbbbbaaaaaa`````_____^^^^^^]]]]]]\\\\\[[[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIII~~~~}}}||||{{{zzzzyyyxxxxwwwvvvvuuuutttssssrrrrqqqqppppooonnnnmmmmllllkkkkkjjjjiiiihhhhgggggffffeeeeedddddcccccbbbbbaaaaa`````______^^^^^]]]]]]\\\\\\\[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIII~~~~}}}||||{{{zzzzyyyxxxxwwwwvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhhggggfffffeeeeeddddcccccbbbbbbaaaa``````_____^^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIII~~~~}}}||||{{{zzzzyyyxxxxwwwwvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiihhhhgggggffffeeeeedddddcccccbbbbbaaaaa``````_____^^^^^]]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIII~~~~}}}||||{{{zzzzyyyyxxxwwwwvvvuuuuttttssssrrrqqqqppppoooonnnnmmmmlllllkkkkjjjjiiiihhhhgggggfffffeeeedddddcccccbbbbbaaaaa``````______^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJII~~~~}}}||||{{{zzzzyyyyxxxwwwwvvvvuuuttttssssrrrrqqqqppppooonnnnmmmmlllllkkkkjjjjiiiiihhhhggggfffffeeeeedddddcccccbbbbbaaaaa``````_____^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJ~~~~}}}||||{{{zzzzyyyyxxxwwwwvvvvuuuutttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiihhhhhggggffffeeeeedddddcccccbbbbbaaaaa``````______^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJ~~~~}}}||||{{{{zzzyyyyxxxxwwwvvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmllllkkkkkjjjjiiiihhhhhgggggffffeeeeedddddcccccbbbbbaaaaa``````_____^^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJ~~~~}}}||||{{{{zzzyyyyxxxxwwwvvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmlllllkkkkjjjjiiiiihhhhgggggffffeeeeeeddddcccccbbbbbaaaaaa``````_____^^^^^^]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJ~~~~}}}||||{{{{zzzyyyyxxxxwwwwvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjjiiiihhhhhggggfffffeeeeedddddcccccbbbbbaaaaa``````_____^^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJ~~~~}}}||||{{{{zzzzyyyxxxxwwwwvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkkjjjjiiiihhhhhgggggfffffeeeedddddcccccbbbbbaaaaaa``````_____^^^^^^]]]]]]\\\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJ~~~~}}}}|||{{{{zzzzyyyxxxxwwwwvvvvuuuttttssssrrrrqqqqppppoooonnnnmmmmmllllkkkkjjjjiiiiihhhhgggggfffffeeeeedddddcccccbbbbbaaaaa``````______^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJ~~~~}}}}|||{{{{zzzzyyyxxxxwwwwvvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmmllllkkkkjjjjjiiiihhhhhgggggffffeeeeedddddcccccbbbbbbaaaaa``````_____^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJ~~~~}}}}|||{{{{zzzzyyyyxxxwwwwvvvvuuuuttttssssrrrqqqqpppppoooonnnnmmmmllllkkkkkjjjjiiiihhhhhgggggfffffeeeeedddddcccccbbbbbaaaaa``````______^^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJ~~~~}}}}|||{{{{zzzzyyyyxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmlllllkkkkjjjjiiiiihhhhhggggfffffeeeeedddddcccccbbbbbbaaaaa``````_____^^^^^^]]]]]]]\\\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJ~~~~}}}}|||{{{{zzzzyyyyxxxxwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjiiiiihhhhgggggfffffeeeedddddcccccbbbbbbaaaaaa``````_____^^^^^^]]]]]]\\\\\\\[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJ~~~~}}}}||||{{{zzzzyyyyxxxxwwwwvvvuuuuttttssssrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjiiiiihhhhgggggfffffeeeeedddddcccccbbbbbbaaaaa``````______^^^^^^]]]]]]\\\\\\\[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJ~~~~}}}}||||{{{zzzzyyyyxxxxwwwwvvvuuuuttttssssrrrrrqqqpppppoooonnnnmmmmlllllkkkkjjjjjiiiihhhhhggggfffffeeeeeddddddcccccbbbbbaaaaaa``````_____^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJ~~~~}}}}||||{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmmllllkkkkkjjjjiiiiihhhhgggggfffffeeeeedddddcccccbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJ~~~~}}}}||||{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjiiiiihhhhhgggggfffffeeeeedddddcccccbbbbbaaaaaa``````_____^^^^^^]]]]]]]\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJ~~~~}}}}||||{{{{zzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqpppppoooonnnnmmmmlllllkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddcccccbbbbbbaaaaa``````______^^^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKK~~~~}}}}||||{{{{zzzzyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqqppppoooonnnnmmmmmllllkkkkjjjjjiiiiihhhhhggggfffffeeeeeddddddcccccbbbbbaaaaaa``````_____^^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxwwwwvvvvuuuuttttssssrrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddccccccbbbbbaaaaaa``````_____^^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuutttttssssrrrrqqqqppppooooonnnnmmmmllllkkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddccccccbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqpppppoooonnnnmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeedddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqqppppoooonnnnmmmmmllllkkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddccccccbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrrqqqqppppoooonnnnnmmmmlllllkkkkjjjjjiiiihhhhhgggggfffffeeeeeedddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttsssssrrrrqqqqppppooooonnnnmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeeddddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuutttttssssrrrrqqqqpppppoooonnnnmmmmmllllkkkkkjjjjjiiiiihhhhgggggfffffeeeeeddddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuuttttssssrrrrqqqqpppppoooonnnnmmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeedddddccccccbbbbbbaaaaa```````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuuttttssssrrrrqqqqqppppoooonnnnnmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeeedddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKK~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrrqqqqppppooooonnnnmmmmmllllkkkkkjjjjjiiiiihhhhhgggggfffffeeeeedddddccccccbbbbbbaaaaa```````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttsssssrrrrqqqqpppppoooonnnnmmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeeddddddccccccbbbbbaaaaaa``````______^^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttsssssrrrrqqqqpppppoooonnnnnmmmmlllllkkkkkjjjjiiiiihhhhhgggggffffffeeeeedddddccccccbbbbbaaaaaa```````______^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLK~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuutttttssssrrrrqqqqqppppoooonnnnnmmmmmllllkkkkkjjjjjiiiiihhhhhgggggfffffeeeeeddddddcccccbbbbbbaaaaaa```````______^^^^^^]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLL~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuuttttssssrrrrqqqqqppppooooonnnnmmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeeeddddddcccccbbbbbbaaaaa```````______^^^^^^^]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLL~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuuttttssssrrrrrqqqqpppppoooonnnnmmmmmlllllkkkkkjjjjiiiiihhhhhhgggggfffffeeeeeddddddcccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLL~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuuttttsssssrrrrqqqqpppppoooonnnnnmmmmmllllkkkkkjjjjjiiiiihhhhhgggggfffffeeeeeedddddccccccbbbbbbaaaaaa``````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLL~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuuttttsssssrrrrqqqqqppppoooonnnnnmmmmmllllkkkkkjjjjjiiiiihhhhhgggggffffffeeeeeddddddcccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]]\\\\\\[[[[[[[[ZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLL~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuutttttssssrrrrqqqqqppppooooonnnnnmmmmlllllkkkkkjjjjjiiiihhhhhhgggggfffffeeeeeedddddccccccbbbbbbaaaaaa``````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLL~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuutttttssssrrrrrqqqqpppppoooonnnnnmmmmlllllkkkkkjjjjjiiiiihhhhhgggggffffffeeeeeddddddccccccbbbbbaaaaaa```````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLL~~~~}}}}||||{{{{zzzzyyyyyxxxxwwwwvvvvuuuuuttttssssrrrrrqqqqpppppoooonnnnnmmmmmllllkkkkkjjjjjiiiiiihhhhggggggfffffeeeeeedddddccccccbbbbbbaaaaaa```````______^^^^^^]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLL~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwvvvvvuuuuttttsssssrrrrqqqqpppppooooonnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhgggggffffffeeeeeddddddccccccbbbbbbaaaaaa``````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLL~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwvvvvvuuuuttttsssssrrrrqqqqqppppooooonnnnnmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggfffffeeeeeedddddccccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLL~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwvvvvvuuuutttttssssrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkjjjjjiiiiiihhhhhgggggffffffeeeeedddddccccccbbbbbbbaaaaaa``````______^^^^^^^]]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLL~~~~}}}}||||{{{{{zzzzyyyyxxxxwwwwwvvvvuuuutttttssssrrrrrqqqqpppppooooonnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhggggggfffffeeeeeddddddccccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMLLLLL~~~~}}}}||||{{{{{zzzzyyyyxxxxxwwwwvvvvuuuuuttttssssrrrrrqqqqpppppooooonnnnnmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggfffffeeeeeeddddddcccccbbbbbbaaaaaaa``````_______^^^^^^]]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLL~~~~}}}}||||{{{{{zzzzyyyyxxxxxwwwwvvvvuuuuuttttsssssrrrrqqqqqppppooooonnnnnmmmmmlllllkkkkjjjjjjiiiiihhhhhgggggffffffeeeeeddddddccccccbbbbbbaaaaaa```````______^^^^^^^^]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLL~~~~}}}}|||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuuuttttsssssrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhggggggfffffeeeeeeddddddccccccbbbbbaaaaaaa```````______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMML~~~~}}}}|||||{{{{zzzzyyyyxxxxxwwwwvvvvvuuuuttttsssssrrrrrqqqqpppppooooonnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggffffffeeeeeddddddccccccbbbbbbaaaaaaa``````______^^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMM~~~~}}}}|||||{{{{zzzzyyyyyxxxxwwwwvvvvvuuuutttttssssrrrrrqqqqpppppooooonnnnnmmmmllllllkkkkkjjjjjiiiiihhhhhggggggfffffeeeeeeddddddccccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMM~~~~}}}}|||||{{{{zzzzyyyyyxxxxwwwwvvvvvuuuutttttssssrrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggfffffeeeeeeddddddccccccbbbbbbaaaaaaa``````_______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYXXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMM~~~~}}}}|||||{{{{zzzzyyyyyxxxxwwwwwvvvvuuuuuttttsssssrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkjjjjjiiiiiihhhhhgggggffffffeeeeeedddddcccccccbbbbbbaaaaaa```````_______^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVVUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMM~~~~}}}}|||||{{{{zzzzzyyyyxxxxwwwwwvvvvuuuuuttttsssssrrrrqqqqqpppppooooonnnnnmmmmmllllkkkkkkjjjjjiiiiihhhhhggggggffffffeeeeeddddddccccccbbbbbbaaaaaaa```````______^^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMM~~~~}}}}}||||{{{{zzzzzyyyyxxxxxwwwwvvvvuuuuutttttssssrrrrrqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggffffffeeeeeeddddddcccccbbbbbbbaaaaaa```````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMM~~~~}}}}}||||{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuutttttssssrrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkjjjjjjiiiiihhhhhggggggfffffeeeeeeddddddccccccbbbbbbaaaaaaa```````______^^^^^^^]]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYXXXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMM~~~~}}}}}||||{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuutttttssssrrrrrqqqqqpppppooooonnnnmmmmmlllllkkkkkkjjjjjiiiiihhhhhhgggggffffffeeeeedddddddccccccbbbbbbaaaaaaa``````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMM~~~~}}}}}||||{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuutttttsssssrrrrrqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjiiiiiihhhhhggggggfffffeeeeeeddddddccccccbbbbbbbaaaaaa```````_______^^^^^^]]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWVVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMM~~~~}}}}}||||{{{{{zzzzyyyyxxxxxwwwwvvvvvuuuuuttttsssssrrrrrqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjjiiiiihhhhhhgggggffffffeeeeeedddddcccccccbbbbbbaaaaaa```````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMM~~~~}}}}}||||{{{{{zzzzyyyyyxxxxwwwwwvvvvuuuuutttttssssrrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkkjjjjjiiiiihhhhhhgggggffffffeeeeeeddddddccccccbbbbbbbaaaaaa```````_______^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYXXXXXXXXXXWWWWWWWWWVVVVVVVVVVVUUUUUUUUUTTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMM~~~~}}}}}||||{{{{{zzzzyyyyyxxxxwwwwwvvvvuuuuutttttssssrrrrrqqqqqpppppooooonnnnmmmmmmlllllkkkkkjjjjjiiiiiihhhhhggggggfffffeeeeeeedddddcccccccbbbbbbaaaaaa````````______^^^^^^^]]]]]]]]\\\\\\\[[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMM~~~~}}}}}||||{{{{{zzzzyyyyyxxxxwwwwwvvvvvuuuutttttsssssrrrrqqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjjiiiiihhhhhhgggggffffffeeeeeeddddddccccccbbbbbbaaaaaaa```````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWVVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMM~~~~}}}}}||||{{{{{zzzzyyyyyxxxxxwwwwvvvvvuuuutttttsssssrrrrrqqqqpppppooooonnnnnmmmmmllllllkkkkkjjjjjiiiiihhhhhhggggggffffffeeeeedddddddccccccbbbbbbaaaaaa````````______^^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMM~~~~}}}}}||||{{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuuuttttsssssrrrrrqqqqqpppppoooonnnnnnmmmmmlllllkkkkkjjjjjiiiiiihhhhhggggggffffffeeeeeeddddddccccccbbbbbbaaaaaaa```````_______^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZZYYYYYYYYXXXXXXXXXXXWWWWWWWWVVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNM~~~~~}}}}|||||{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuuuttttsssssrrrrrqqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjjiiiiihhhhhhgggggffffffeeeeeedddddddccccccbbbbbbaaaaaaa```````_______^^^^^^^]]]]]]]]\\\\\\\[[[[[[[[[ZZZZZZZYYYYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNmlt-7.38.0/demo/mlt_all000664 000000 000000 00000000022 15172202314 014647 0ustar00rootroot000000 000000 melt \ clip* \ $* mlt-7.38.0/demo/mlt_audio_stuff000664 000000 000000 00000000200 15172202314 016405 0ustar00rootroot000000 000000 melt \ clip*.dv \ -track music1.ogg \ -filter volume:0.5 normalise= track=0 \ -transition mix out=9999 a_track=0 b_track=1 \ $* mlt-7.38.0/demo/mlt_avantika_title000664 000000 000000 00000000026 15172202314 017102 0ustar00rootroot000000 000000 melt \ pango.mlt \ $* mlt-7.38.0/demo/mlt_bouncy000664 000000 000000 00000000251 15172202314 015402 0ustar00rootroot000000 000000 melt \ clip3.dv \ -filter \ watermark:clip1.dv \ composite.rect="0=10%/10%:20%x20%; 33=30%/70%:25%x25%; 66=70%/30%:15%x15%; -1=70%/70%:20%x20%" \ composite.out=100 \ $* mlt-7.38.0/demo/mlt_clock_in_and_out000664 000000 000000 00000000417 15172202314 017401 0ustar00rootroot000000 000000 melt \ clip2.dv in=100 out=174 -blank 99 clip3.dv in=100 \ -track \ -blank 49 clip3.mpeg in=100 out=249 \ -transition luma:luma1.pgm softness=0.5 in=50 out=74 a_track=0 b_track=1 \ -transition luma:luma1.pgm softness=0.2 in=175 out=199 a_track=0 b_track=1 reverse=1 \ $* mlt-7.38.0/demo/mlt_composite_transition000664 000000 000000 00000000357 15172202314 020366 0ustar00rootroot000000 000000 melt \ clip1.dv out=74 \ -track \ -blank 49 clip2.mpeg \ -transition affine:"0=57%/10%:33%x33%; -1=0%/0%:100%x100%" progressive=1 fill=0 distort=1 in=50 out=74 a_track=0 b_track=1 \ -transition mix:-1 in=50 out=74 a_track=0 b_track=1 \ $* mlt-7.38.0/demo/mlt_effect_in_middle000664 000000 000000 00000000111 15172202314 017336 0ustar00rootroot000000 000000 melt \ clip1.mpeg in=100 out=500 \ -filter greyscale in=100 out=199 \ $* mlt-7.38.0/demo/mlt_fade_black000664 000000 000000 00000000444 15172202314 016142 0ustar00rootroot000000 000000 melt \ colour:black out=199 \ -track \ clip3.mpeg in=100 out=299 \ -transition luma in=0 out=49 a_track=0 b_track=1 \ -transition luma in=150 out=199 a_track=0 b_track=1 reverse=1 \ -filter volume in=0 out=49 track=1 gain=0 end=1.0 \ -filter volume in=150 out=199 track=1 gain=1.0 end=0 \ $* mlt-7.38.0/demo/mlt_fade_in_and_out000664 000000 000000 00000000511 15172202314 017200 0ustar00rootroot000000 000000 melt \ clip1.dv out=74 -blank 99 clip3.dv in=25 \ -track \ -blank 49 clip2.mpeg out=149 \ -transition luma in=50 out=74 a_track=0 b_track=1 \ -transition luma in=175 out=199 a_track=0 b_track=1 reverse=1 \ -transition mix:-1 in=50 out=74 a_track=0 b_track=1 \ -transition mix:-1 in=175 out=199 a_track=0 b_track=1 reverse=1 \ $* mlt-7.38.0/demo/mlt_intro000664 000000 000000 00000000261 15172202314 015237 0ustar00rootroot000000 000000 melt \ music1.ogg in=100 out=224 \ -track \ watermark1.png out=124 \ clip3.mpeg \ -mix 25 \ -mixer luma resource=luma1.pgm softness=0.2 \ -transition mix:-1 in=100 out=124 \ $* mlt-7.38.0/demo/mlt_jcut000664 000000 000000 00000000275 15172202314 015056 0ustar00rootroot000000 000000 melt \ -blank 49 \ clip2.dv in=100 \ -track \ clip1.dv out=99 \ -transition \ mix start=0 end=1 in=49 out=50 a_track=1 b_track=0 \ -transition \ mix:1 in=51 out=99 a_track=1 b_track=0 \ $* mlt-7.38.0/demo/mlt_lcut000664 000000 000000 00000000365 15172202314 015060 0ustar00rootroot000000 000000 melt \ clip1.dv out=100 \ -track \ -blank 49 \ clip2.dv in=100 \ -transition \ luma in=50 out=55 a_track=0 b_track=1 \ -transition \ mix:1 in=50 out=98 a_track=1 b_track=0 \ -transition \ mix start=1 end=0 in=99 out=100 a_track=1 b_track=0 \ $* mlt-7.38.0/demo/mlt_levels000664 000000 000000 00000000106 15172202314 015374 0ustar00rootroot000000 000000 melt \ *.dv \ -filter gamma:1.5 \ -filter volume normalise=-20db \ $* mlt-7.38.0/demo/mlt_my_name_is000664 000000 000000 00000000746 15172202314 016234 0ustar00rootroot000000 000000 melt \ clip3.dv \ -track \ "+My name is Inigo Montoya.txt" out=99 -blank 49 "+Prepare to die!.txt" out=99 \ -track \ -blank 74 "+You killed my father.txt" out=74 \ -transition affine:"0=50%/20%:5%x4%; -1=10%/20%:80%x12%" fill=0 distort=1 halign=centre valign=centre in=0 out=99 a_track=0 b_track=1 \ -transition affine:"0=0%/70%:100%x10%; -1=100%/70%:100%x10%" fill=0 in=75 out=149 a_track=0 b_track=2 \ -transition affine:25%/25%:50%x50% fill=0 in=150 out=249 a_track=0 b_track=1 \ $* mlt-7.38.0/demo/mlt_news000664 000000 000000 00000001421 15172202314 015057 0ustar00rootroot000000 000000 melt \ colour:black out=199 \ -track \ clip1.dv in=0 out=0 -repeat 99 clip1.dv \ -track \ clip2.dv out=199 \ -track \ pango: text=" Breaking News MLT Rocks India" bgcolour=#80ff0000 out=149 \ pango: text=" Breaking News MLT Rocks the World" bgcolour=#80ff0000 out=349 \ -transition mix:0.5 always_active=1 a_track=0 b_track=2 \ -transition affine rect=50%/15%:37.5%x40% fill=0 a_track=0 b_track=1 in=0 out=174 \ -transition affine rect=10%/15%:37.5%x40% fill=0 a_track=0 b_track=2 in=0 out=199 \ -transition affine rect="50%/15%:37.5%x40%; -1=0%/0%:100%x100%" fill=0 a_track=0 b_track=1 in=175 out=199 distort=1 \ -transition affine rect=10%/65%:90%x20% fill=0 a_track=0 b_track=3 in=0 out=199 \ -transition affine rect=10%/65%:90%x20% fill=0 a_track=1 b_track=3 in=200 out=499 \ $* mlt-7.38.0/demo/mlt_pango_keyframes000664 000000 000000 00000000204 15172202314 017253 0ustar00rootroot000000 000000 melt \ color:#03CF0 \ -filter watermark:pango_keyframes.mpl composite.halign=c composite.valign=m composite.sliced_composite=1 \ $* mlt-7.38.0/demo/mlt_push000664 000000 000000 00000000630 15172202314 015063 0ustar00rootroot000000 000000 melt \ -blank 49 colour:black out=25 -blank 999 \ -track \ clip3.dv in=200 out=275 \ -track \ -blank 49 \ clip2.dv in=200 \ -transition \ affine in=50 out=75 a_track=0 b_track=1 \ rect="0=0/0:100%x100%:100%; -1=100%/0:100%x100%:100%" \ -transition \ affine in=50 out=75 a_track=0 b_track=2 \ rect="0=-100%/0:100%x100%:100%; -1=0/0:100%x100%:100%" \ -transition \ mix:-1 in=50 out=75 a_track=1 b_track=2 \ $* mlt-7.38.0/demo/mlt_slideshow000664 000000 000000 00000000134 15172202314 016104 0ustar00rootroot000000 000000 melt \ photos/.all.jpg ttl=75 \ -filter luma:luma1.pgm luma.softness=0.1 luma.invert=0 \ $* mlt-7.38.0/demo/mlt_slideshow2000664 000000 000000 00000000467 15172202314 016177 0ustar00rootroot000000 000000 melt \ photos/.all.jpg ttl=75 \ -attach crop center=1 \ -attach affine transition.cycle=225 transition.rect="0=0/0:100%x100%;74=-20%/-20%:120%x120%;75=-10%/-10%:110%x110%;149=0/0:110%x110%;150=0/-10%:110%x110%;224=-10%/0:110%x110%" \ -filter luma cycle=75 duration=25 \ -track music1.ogg \ -transition mix \ $* mlt-7.38.0/demo/mlt_squeeze000664 000000 000000 00000001061 15172202314 015564 0ustar00rootroot000000 000000 melt \ clip1.dv out=124 clip2.dv out=149 clip3.dv in=75 out=224 clip1.dv \ -track \ -blank 99 colour:black out=49 -blank 99 colour:black out=49 -blank 99 colour:black out=49 \ -group progressive=1 distort=1 \ -transition affine rect="0%/0%:100%x100%;25=50%/0%:5%x100%;-1=0%/0%:100%x100%" a_track=1 b_track=0 in=100 out=149 \ -transition affine rect="0%/0%:100%x100%;25=0%/50%:100%x5%;-1=0%/0%:100%x100%" a_track=1 b_track=0 in=250 out=299 \ -transition affine rect="0%/0%:100%x100%;25=100%/0%:5%x100%;-1=0%/0%:100%x100%" a_track=1 b_track=0 in=400 out=449 \ $* mlt-7.38.0/demo/mlt_squeeze_box000664 000000 000000 00000001071 15172202314 016435 0ustar00rootroot000000 000000 melt \ clip1.dv out=124 clip2.dv out=149 clip3.dv in=75 out=224 clip1.dv \ -track \ -blank 99 colour:black out=49 -blank 99 colour:black out=49 -blank 99 colour:black out=49 \ -group progressive=1 \ -transition affine:"0=0%/0%:100%x100%; 25=50%/0%:5%x100%; -1=0%/0%:100%x100%" fill=0 a_track=1 b_track=0 in=100 out=149 \ -transition affine:"0=0%/0%:100%x100%; 25=0%/50%:100%x5%; -1=0%/0%:100%x100%" fill=0 a_track=1 b_track=0 in=250 out=299 \ -transition affine:"0=0%/0%:100%x100%; 25=100%/0%:5%x100%; -1=0%/0%:100%x100%" fill=0 a_track=1 b_track=0 in=400 out=449 \ $* mlt-7.38.0/demo/mlt_ticker000664 000000 000000 00000000565 15172202314 015374 0ustar00rootroot000000 000000 melt \ clip1.dv out=299 \ -track \ colour:0 out=299 \ -track \ "+The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog..txt" \ out=299 \ -transition \ affine a_track=0 b_track=1 out=299 distort=1 \ rect=0/70%:100%x11%:100% \ -transition \ affine a_track=0 b_track=2 out=299 fill=0 \ rect="0=100%/70%:999%x20%; -1=-299%/70%:999%x20%" \ $* mlt-7.38.0/demo/mlt_title_over_gfx000664 000000 000000 00000000650 15172202314 017126 0ustar00rootroot000000 000000 melt \ watermark1.png out=9999 \ -track \ "+title over gfx.txt" fgcolour=0x000000ff \ -track \ clip1.dv \ -transition \ affine:30%/20%:40%x60% \ in=50 \ out=199 \ a_track=0 \ b_track=1 \ distort=1 \ -transition \ affine:"0=0%/75%:100%x20%:0; 24=0%/75%:100%x20%:100%; -25=0%/75%:100%x20%:100%; -1=0%/75%:100%x20%:0" \ in=50 \ out=199 \ a_track=2 \ b_track=0 \ luma=luma1.pgm \ distort=1 \ $* mlt-7.38.0/demo/mlt_titleshadow_watermark000664 000000 000000 00000000722 15172202314 020512 0ustar00rootroot000000 000000 melt \ "+hello~world.txt" align=1 out=1000 \ -track "+hello~world.txt" align=1 out=1000 fgcolour=#000000 \ -track watermark1.png out=1000 \ -track clip3.dv \ -filter greyscale track=2 \ -transition affine:"0=21%/11%:100%x100%:50; -1=61%/41%:100%x100%" fill=0 out=99 a_track=3 b_track=1 \ -transition affine:"0=20%/10%:100%x100%; -1=60%/40%:100%x100%" fill=0 out=99 a_track=3 b_track=0 \ -transition affine:85%/80%:10%x10%:30 fill=0 out=1000 a_track=3 b_track=2 \ $* mlt-7.38.0/demo/mlt_typewriter000664 000000 000000 00000000746 15172202314 016332 0ustar00rootroot000000 000000 melt \ noise: out=1000 \ -filter qtext:"Welcome to the MLT Typewriter Text Effect" \ typewriter=1 \ typewriter.cursor=2 \ typewriter.cursor_blink_rate=30 \ typewriter.cursor_char='█' \ typewriter.step_length=15 \ typewriter.step_sigma=2 \ typewriter.macro_type=1 \ geometry="10%/20%:80%x60%:100%" \ fgcolour=#ffffffff \ bgcolour=#00000000 \ family="Monospace" \ size=36 \ halign=center \ valign=middle \ pad=20 \ $* mlt-7.38.0/demo/mlt_voiceover000664 000000 000000 00000001067 15172202314 016112 0ustar00rootroot000000 000000 melt \ "+voice over demo.txt" \ family="Sans" \ size="72" \ weight="700" \ fgcolour=#000000 \ bgcolour=#aaff9933 \ pad=10 \ -track music1.ogg \ -track clip1.dv out=149 clip2.mpeg \ -transition \ mix:0.0 \ end=0.6 \ in=75 \ out=99 \ a_track=2 \ b_track=1 \ -transition \ mix:0.6 \ in=100 \ out=299 \ a_track=2 \ b_track=1 \ -transition \ mix:0.6 \ end=0.0 \ in=300 \ out=324 \ a_track=2 \ b_track=1 \ -transition \ affine:0%/80%:100%x20% \ distort=1 \ in=100 \ out=299 \ a_track=2 \ b_track=0 \ $* mlt-7.38.0/demo/mlt_watermark000664 000000 000000 00000000214 15172202314 016077 0ustar00rootroot000000 000000 melt \ clip2.dv out=1000 \ -track \ watermark1.png out=1000 \ -transition affine in=0 out=1000 a_track=0 b_track=1 rect=85%/5%:10%x10% \ $* mlt-7.38.0/demo/new.mlt000664 000000 000000 00000002655 15172202314 014625 0ustar00rootroot000000 000000 clip2.mpeg clip3.mpeg greyscale luma luma 1 mlt-7.38.0/demo/pango.mlt000664 000000 000000 00000002475 15172202314 015140 0ustar00rootroot000000 000000 clip1.dv pango +.txt GJ-TTAvantika 36 1 0xffffddff 0x8c101080 8 composite 1 0 -70%/65%:100%x35%:0 0/65%:100%x35%:100 0/65%:100%x35%:100 0/65%:100%x35%:0 centre centre mlt-7.38.0/demo/pango_keyframes.mpl000664 000000 000000 00000000076 15172202314 017175 0ustar00rootroot000000 000000 0=Hello 40= 50=World~the end 90= mlt-7.38.0/demo/svg.mlt000664 000000 000000 00000005422 15172202314 014626 0ustar00rootroot000000 000000 ]> pixbuf mlt-7.38.0/demo/watermark1.png000664 000000 000000 00000002511 15172202314 016071 0ustar00rootroot000000 000000 PNG  IHDRr ߔsBIT|dIDATxŖklSe^]KؠSq QEB  #`DMH:&2A.M A~((l#( mu]s|iDQ9SlA8+Q9NT 1"4.g0")l!h>`i2(X97p.]QU ^YQ 6%E}Jq^% TWhdd S+-]]`^_0RT2"`{ʂyd=Nsbje"e3[Qq,\$b2 _.b~M.K o~-3iiT{qz詟: ϪL۹penݭ f4'MDY}3b&O0san 39x:w炄Z25Un^<08C`(-2R`-ڍTgQ^.7Q:#jyNIѬ u/|,MWpE^v=}DU,G~JɌ{#6RQj;y|Jw5QYffjgK]14 <͌4os{ Fcc$Yɚ+4rt/ec˖uyG?BEbKBL[.SC(51 nW fWhzOYZ meG%`J C.%zn 309-ʊl[Q5K}*^+9pWϨ&'#j`PcWi>w F>+!$F@O%qdG2y 2gAPdsl=( J dA~Ba#-ں|v'K@,yp~o&V8Q̯oVHޔ?]Vroj 192V Giw,R݇ID}Ūr#CA|No}˒F.W>OSQ<ȧ~ Rǩ,QY1|,f}{8KfaC}r-ZXn{SHxr$ 0B<&]|tpdTX'0r>L; /A/FA8B:ly3hRXQ@?Z V.[SY?wd|pIENDB`mlt-7.38.0/docs/000775 000000 000000 00000000000 15172202314 013312 5ustar00rootroot000000 000000 mlt-7.38.0/docs/melt.1000664 000000 000000 00000007203 15172202314 014337 0ustar00rootroot000000 000000 .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.38.4. .TH MELT "1" "April 2025" "melt 7.38.0" "User Commands" .SH NAME melt \- author, play, and encode multitrack audio/video compositions .SH SYNOPSIS .B melt [\fIoptions\fR] [\fIproducer \fR[\fIname=value\fR]\fI* \fR]\fI+\fR .SH OPTIONS .TP \fB\-attach\fR filter[:arg] [name=value]* Attach a filter to the output .TP \fB\-attach\-cut\fR filter[:arg] [name=value]* Attach a filter to a cut .HP \fB\-attach\-track\fR filter[:arg] [name=value]* Attach a filter to a track .TP \fB\-attach\-clip\fR filter[:arg] [name=value]* Attach a filter to a producer .TP \fB\-audio\-track\fR | \fB\-hide\-video\fR Add an audio\-only track .TP \fB\-blank\fR frames Add blank silence to a track .TP \fB\-chain\fR id[:arg] [name=value]* Add a producer as a chain .TP \fB\-consumer\fR id[:arg] [name=value]* Set the consumer (sink) .TP \fB\-debug\fR Set the logging level to debug .TP \fB\-filter\fR filter[:arg] [name=value]* Add a filter to the current track .TP \fB\-getc\fR Get keyboard input using getc .TP \fB\-group\fR [name=value]* Apply properties repeatedly .TP \fB\-help\fR Show this message .TP \fB\-jack\fR Enable JACK transport synchronization .TP \fB\-join\fR clips Join multiple clips into one cut .TP \fB\-link\fR id[:arg] [name=value]* Add a link to a chain .TP \fB\-loglevel\fR level Set the logging level (least to most): .br quiet panic, fatal, error, warning, info, verbose, debug, timings .TP \fB\-mix\fR length Add a mix between the last two cuts .TP \fB\-mixer\fR transition Add a transition to the mix .TP \fB\-null\-track\fR | \fB\-hide\-track\fR Add a hidden track .TP \fB\-profile\fR name Set the processing settings .TP \fB\-progress\fR Display progress along with position .TP \fB\-progress2\fR Display progress along with position on a new line .TP \fB\-query\fR List all of the registered services .TP \fB\-query\fR "consumers" | "consumer"=id List consumers or show info about one .TP \fB\-query\fR "filters" | "filter"=id List filters or show info about one .TP \fB\-query\fR "links" | "link"=id List links or show info about one .TP \fB\-query\fR "producers" | "producer"=id List producers or show info about one .TP \fB\-query\fR "transitions" | "transition"=id List transitions, show info about one .TP \fB\-query\fR "profiles" | "profile"=id List profiles, show info about one .TP \fB\-query\fR "presets" | "preset"=id List presets, show info about one .TP \fB\-query\fR "formats" List audio/video formats .TP \fB\-query\fR "audio_codecs" List audio codecs .TP \fB\-query\fR "video_codecs" List video codecs .TP \fB\-quiet\fR Set the logging level to quiet .TP \fB\-remove\fR Remove the most recent cut .TP \fB\-repeat\fR times Repeat the last cut .TP \fB\-repository\fR path Set the directory of MLT modules .TP \fB\-serialise\fR [filename] Write the commands to a text file .TP \fB\-setlocale\fR Make numeric strings locale-sensitive (legacy support) .TP \fB\-silent\fR Do not display position/transport .TP \fB\-split\fR relative\-frame Split the last cut into two cuts .TP \fB\-swap\fR Rearrange the last two cuts .TP \fB\-track\fR Add a track .TP \fB\-transition\fR id[:arg] [name=value]* Add a transition .TP \fB\-verbose\fR Set the logging level to verbose .TP \fB\-timings\fR Set the logging level to timings .TP \fB\-version\fR Show the version and copyright .TP \fB\-video\-track\fR | \fB\-hide\-audio\fR Add a video\-only track .PP For more help: .SH COPYRIGHT Copyright \(co 2002\-2026 Meltytech, LLC .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. mlt-7.38.0/makefile000664 000000 000000 00000002272 15172202314 014065 0ustar00rootroot000000 000000 default: @echo This Makefile is not used for building. Use CMake instead. @echo Rather, this makefile is purely for holding some maintenance routines. dist: git archive --format=tar --prefix=mlt-$(version)/ v$(version) | gzip >mlt-$(version).tar.gz validate-yml: for file in $$(find src/modules -maxdepth 2 -type f -name \*.yml \! -name resolution_scale.yml); do \ echo "validate: $$file"; \ kwalify -f src/framework/metaschema.yaml $$file || exit 1; \ done codespell: codespell -w -q 3 \ -L amin,boun,boundry,childs,hsi,indx,ith,mis,nast,parms,percentil,readded,sav,seeked,shotcut,sinc,slin,uint,writen \ -S ChangeLog,cJSON.c,cJSON.h,RtAudio.cpp,RtAudio.h,*.rej,mlt_wrap.* cppcheck: cppcheck src/ --force --quiet --inline-suppr --library=qt --error-exitcode=1 \ -j $(shell nproc) \ -i src/modules/decklink/darwin \ -i src/modules/decklink/linux \ -i src/modules/decklink/win \ -i src/modules/glaxnimate/glaxnimate/ \ -i src/modules/plus/ebur128/ \ -i src/modules/xml/common.c \ --include=src/framework/mlt_log.h \ --include=src/framework/mlt_types.h \ --library=cppcheck.cfg \ --suppress=ctuOneDefinitionRuleViolation \ --suppress=syntaxError:src/modules/xml/common.c mlt-7.38.0/presets/000775 000000 000000 00000000000 15172202314 014047 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/000775 000000 000000 00000000000 15172202314 015702 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/000775 000000 000000 00000000000 15172202314 017521 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/AAC000664 000000 000000 00000000173 15172202314 020031 0ustar00rootroot000000 000000 f=mp4 acodec=aac ab=256k vn=1 video_off=1 meta.preset.extension=m4a meta.preset.note=audio only meta.preset.name=audio/AAC mlt-7.38.0/presets/consumer/avformat/ALAC000664 000000 000000 00000000166 15172202314 020147 0ustar00rootroot000000 000000 f=mov acodec=alac vn=1 video_off=1 meta.preset.extension=mov meta.preset.note=audio only meta.preset.name=audio/ALAC mlt-7.38.0/presets/consumer/avformat/AV1000664 000000 000000 00000000456 15172202314 020040 0ustar00rootroot000000 000000 f=webm acodec=libopus ar=48000 ab=128k vcodec=libaom-av1 vb=0 crf=45 row-mt=1 tile-columns=2 tile-rows=1 cpu-used=4 strict=experimental meta.preset.name=AV1 WebM meta.preset.extension=webm meta.preset.note=AV1 video with Opus audio in Matroska container: Just say no to patents meta.preset.hidden=1 mlt-7.38.0/presets/consumer/avformat/FLAC000664 000000 000000 00000000170 15172202314 020147 0ustar00rootroot000000 000000 f=flac acodec=flac vn=1 video_off=1 meta.preset.extension=flac meta.preset.note=audio only meta.preset.name=audio/FLAC mlt-7.38.0/presets/consumer/avformat/Flash000664 000000 000000 00000000365 15172202314 020505 0ustar00rootroot000000 000000 f=flv acodec=libmp3lame ab=128k ar=44100 vcodec=flv minrate=0 vb=1M bf=0 progressive=1 meta.preset.extension=flv meta.preset.note=This is the old Sorenson H.263-based codec. Most people use H.264 with Flash now. meta.preset.name=legacy/Flash mlt-7.38.0/presets/consumer/avformat/GIF000664 000000 000000 00000000147 15172202314 020053 0ustar00rootroot000000 000000 progressive=1 f=gif vcodec=gif an=1 g=1 bf=0 meta.preset.extension=gif meta.preset.name=GIF Animation mlt-7.38.0/presets/consumer/avformat/MJPEG000664 000000 000000 00000000132 15172202314 020302 0ustar00rootroot000000 000000 f=avi vcodec=mjpeg qscale=4 acodec=libmp3lame ab=256k g=1 bf=0 meta.preset.extension=avi mlt-7.38.0/presets/consumer/avformat/MP3000664 000000 000000 00000000203 15172202314 020036 0ustar00rootroot000000 000000 f=mp3 acodec=libmp3lame ab=256k vn=1 video_off=1 meta.preset.extension=mp3 meta.preset.note=audio only meta.preset.name=audio/MP3 mlt-7.38.0/presets/consumer/avformat/MPEG-2000664 000000 000000 00000000313 15172202314 020330 0ustar00rootroot000000 000000 f=mpeg acodec=mp2 ab=256k ar=48000 vcodec=mpeg2video minrate=0 vb=8M trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=mpg meta.preset.note=a general purpose MPEG-2 preset mlt-7.38.0/presets/consumer/avformat/MPEG-4000664 000000 000000 00000000413 15172202314 020333 0ustar00rootroot000000 000000 f=mp4 acodec=libmp3lame ab=128k ar=44100 vcodec=mpeg4 minrate=0 vb=2M mbd=rd trellis=1 flags=+mv4+aic cmp=satd subcmp=satd progressive=1 movflags=+faststart meta.preset.extension=mp4 meta.preset.note=Part 2 Simple Profile meta.preset.name=legacy/MPEG-4 Part 2 SP mlt-7.38.0/presets/consumer/avformat/MPEG-4-ASP000664 000000 000000 00000000421 15172202314 020753 0ustar00rootroot000000 000000 f=mp4 acodec=libmp3lame ab=128k ar=44100 vcodec=mpeg4 minrate=0 vb=2M mbd=rd trellis=1 cmp=satd subcmp=satd bf=2 flags=+mv4+aic+qpel movflags=+faststart meta.preset.extension=mp4 meta.preset.note=Part 2 Advanced Simple Profile meta.preset.name=legacy/MPEG-4 Part 2 ASP mlt-7.38.0/presets/consumer/avformat/SVT-AV1000664 000000 000000 00000000647 15172202314 020514 0ustar00rootroot000000 000000 f=webm acodec=libopus ar=48000 ab=192k vcodec=libsvtav1 crf=32 bf=1 svtav1-params=preset=5:tile-columns=1:fast-decode=0:tune=0:film-grain=0:film-grain-denoise=0:enable-overlays=1:enable-variance-boost=1:variance-boost-strength=1:variance-octile=7:enable-qm=1:qm-min=0 progressive=1 meta.preset.name=AV1 WebM meta.preset.extension=webm meta.preset.note=AV1 video with Opus audio in WebM container: Just say no to patents mlt-7.38.0/presets/consumer/avformat/Slide-Deck-H264000664 000000 000000 00000000601 15172202314 021766 0ustar00rootroot000000 000000 pix_fmt=yuv420p vcodec=libx264 vprofile=high crf=27 g=249 bf=8 preset=veryfast b-pyramid=0 refs=8 weightb=1 b_strategy=2 rc-lookahead=25 channels=1 acodec=ac3 ar=44100 ab=80k f=mp4 movflags=+faststart meta.preset.extension=mp4 meta.preset.note=Small file size for slide decks, video conferences, and screencasts. Hardware encoding not recommended. meta.preset.name=Slide Deck (H.264) mlt-7.38.0/presets/consumer/avformat/Slide-Deck-HEVC000664 000000 000000 00000000525 15172202314 022075 0ustar00rootroot000000 000000 pix_fmt=yuv420p vcodec=libx265 crf=31 g=249 bf=8 preset=slow x265-params=weightb=1:open-gop=0 channels=1 acodec=ac3 ar=44100 ab=80k f=mp4 movflags=+faststart meta.preset.extension=mp4 meta.preset.note=Small file size for slide decks, video conferences, and screencasts. Hardware encoding not recommended. meta.preset.name=Slide Deck (HEVC) mlt-7.38.0/presets/consumer/avformat/Sony-PSP000664 000000 000000 00000000456 15172202314 021041 0ustar00rootroot000000 000000 width=480 height=272 aspect=@16/9 progressive=1 f=psp acodec=aac ar=32000 ab=96k vcodec=libx264 threads=0 preset=medium vprofile=main flags2=-dct8x8 vb=1M refs=1 bf=1 x264opts=ref=1:bframes=1 meta.preset.extension=mp4 meta.preset.note=for Sony PlayStation Portable meta.preset.name=device/Sony PSP mlt-7.38.0/presets/consumer/avformat/Vorbis000664 000000 000000 00000000206 15172202314 020706 0ustar00rootroot000000 000000 f=ogg acodec=vorbis ab=256k vn=1 video_off=1 meta.preset.name=audio/Ogg Vorbis meta.preset.extension=ogg meta.preset.note=audio only mlt-7.38.0/presets/consumer/avformat/WAV000664 000000 000000 00000000172 15172202314 020101 0ustar00rootroot000000 000000 f=wav acodec=pcm_s16le vn=1 video_off=1 meta.preset.extension=wav meta.preset.note=audio only meta.preset.name=audio/WAV mlt-7.38.0/presets/consumer/avformat/WMA000664 000000 000000 00000000207 15172202314 020067 0ustar00rootroot000000 000000 f=asf acodec=wmav2 ab=256k vn=1 video_off=1 meta.preset.extension=wma meta.preset.note=Windows Media Audio meta.preset.name=audio/WMA mlt-7.38.0/presets/consumer/avformat/WMV000664 000000 000000 00000000230 15172202314 020110 0ustar00rootroot000000 000000 f=asf acodec=wmav2 ab=160k vcodec=wmv2 g=100 vb=2M bf=0 meta.preset.extension=wmv meta.preset.note=Windows Media Video (Windows Media Player 8 and up) mlt-7.38.0/presets/consumer/avformat/XDCAM-HD422000664 000000 000000 00000000573 15172202314 021026 0ustar00rootroot000000 000000 f=mxf vcodec=mpeg2video top_field_first=1 pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M dc=10 intra_vlc=1 non_linear_quant=1 lmin=1*QP2LAMBDA rc_max_vbv_use=1 rc_min_vbv_use=1 qmin=1 qmax=12 bufsize=36408333 g=12 bf=2 vtag=xd5e acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Sony "workflow innovation". Set vtag as needed. meta.preset.name=camcorder/XDCAM-HD422 mlt-7.38.0/presets/consumer/avformat/YouTube000664 000000 000000 00000000320 15172202314 021033 0ustar00rootroot000000 000000 f=mp4 movflags=+faststart acodec=aac ar=48000 ab=384k vcodec=libx264 progressive=1 preset=fast threads=0 crf=23 g=15 bf=2 vprofile=high meta.preset.extension=mp4 meta.preset.note=H.264/AAC MP4 for YouTube mlt-7.38.0/presets/consumer/avformat/alpha/000775 000000 000000 00000000000 15172202314 020606 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/alpha/Quicktime Animation000664 000000 000000 00000000233 15172202314 024362 0ustar00rootroot000000 000000 f=mov vcodec=qtrle g=1 bf=0 progressive=1 mlt_image_format=rgba pix_fmt=argb meta.preset.extension=mov meta.preset.note=lossless video with alpha channel mlt-7.38.0/presets/consumer/avformat/alpha/Ut Video000664 000000 000000 00000000301 15172202314 022142 0ustar00rootroot000000 000000 f=avi vcodec=utvideo mlt_image_format=rgba pix_fmt=gbrap g=1 bf=0 acodec=pcm_s24le meta.preset.extension=avi meta.preset.note=Ut Video with alpha channel and 24-bit PCM audio in AVI container mlt-7.38.0/presets/consumer/avformat/alpha/vp8000664 000000 000000 00000000575 15172202314 021255 0ustar00rootroot000000 000000 f=webm acodec=vorbis ab=128k vcodec=libvpx g=120 rc_lookahead=16 quality=good speed=0 vprofile=0 slices=4 vb=2M arnr_max_frames=7 arnr_strength=5 arnr_type=3 auto-alt-ref=0 mlt_image_format=rgba pix_fmt=yuva420p meta.preset.name=alpha/WebM VP8 with alpha channel meta.preset.extension=webm meta.preset.note=VP8 video with Ogg Vorbis audio in Matroska container: "Don't be evil" mlt-7.38.0/presets/consumer/avformat/alpha/vp9000664 000000 000000 00000000640 15172202314 021247 0ustar00rootroot000000 000000 f=webm acodec=libopus ar=48000 ab=128k vcodec=libvpx-vp9 vb=2M g=120 bf=2 threads=0 rc_lookahead=16 quality=good speed=3 vprofile=0 slices=4 tile-columns=6 frame-parallel=1 lag-in-frames=25 row-mt=1 auto-alt-ref=0 mlt_image_format=rgba pix_fmt=yuva420p meta.preset.name=alpha/WebM VP9 with alpha channel meta.preset.extension=webm meta.preset.note=VP9 video with Opus audio in Matroska container: "Don't be evil" mlt-7.38.0/presets/consumer/avformat/atsc_1080i_50/000775 000000 000000 00000000000 15172202314 021600 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080i_50/DNxHD000664 000000 000000 00000000364 15172202314 022433 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=185M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080i 25 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080i_5994/000775 000000 000000 00000000000 15172202314 021766 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080i_5994/DNxHD000664 000000 000000 00000000367 15172202314 022624 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080i 29.97 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080p_2398/000775 000000 000000 00000000000 15172202314 021770 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080p_2398/DNxHD000664 000000 000000 00000000367 15172202314 022626 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=175M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 23.98 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080p_24/000775 000000 000000 00000000000 15172202314 021610 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080p_24/DNxHD000664 000000 000000 00000000364 15172202314 022443 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=175M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 24 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080p_25/000775 000000 000000 00000000000 15172202314 021611 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080p_25/DNxHD000664 000000 000000 00000000364 15172202314 022444 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=185M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 25 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080p_2997/000775 000000 000000 00000000000 15172202314 021775 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080p_2997/DNxHD000664 000000 000000 00000000367 15172202314 022633 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 29.97 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080p_30/000775 000000 000000 00000000000 15172202314 021605 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080p_30/DNxHD000664 000000 000000 00000000364 15172202314 022440 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 30 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080p_50/000775 000000 000000 00000000000 15172202314 021607 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080p_50/DNxHD000664 000000 000000 00000000364 15172202314 022442 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=185M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 50 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080p_5994/000775 000000 000000 00000000000 15172202314 021775 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080p_5994/DNxHD000664 000000 000000 00000000367 15172202314 022633 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 59.94 fps) mlt-7.38.0/presets/consumer/avformat/atsc_1080p_60/000775 000000 000000 00000000000 15172202314 021610 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_1080p_60/DNxHD000664 000000 000000 00000000364 15172202314 022443 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 60 fps) mlt-7.38.0/presets/consumer/avformat/atsc_720p_2398/000775 000000 000000 00000000000 15172202314 021710 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_720p_2398/DNxHD000664 000000 000000 00000000365 15172202314 022544 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=90M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 720p 23.98 fps) mlt-7.38.0/presets/consumer/avformat/atsc_720p_50/000775 000000 000000 00000000000 15172202314 021527 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_720p_50/DNxHD000664 000000 000000 00000000363 15172202314 022361 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=175M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 720p 50 fps) mlt-7.38.0/presets/consumer/avformat/atsc_720p_5994/000775 000000 000000 00000000000 15172202314 021715 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_720p_5994/DNxHD000664 000000 000000 00000000367 15172202314 022553 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 59.94 fps) mlt-7.38.0/presets/consumer/avformat/atsc_720p_60/000775 000000 000000 00000000000 15172202314 021530 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/atsc_720p_60/DNxHD000664 000000 000000 00000000363 15172202314 022362 0ustar00rootroot000000 000000 f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 720p 60 fps) mlt-7.38.0/presets/consumer/avformat/dv_ntsc/000775 000000 000000 00000000000 15172202314 021161 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/dv_ntsc/D10000664 000000 000000 00000000620 15172202314 021426 0ustar00rootroot000000 000000 f=mxf_d10 vcodec=mpeg2video pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M g=1 bf=0 flags=+ildct+low_delay dc=10 intra_vlc=1 non_linear_quant=1 ps=1 qmin=1 qmax=3 bufsize=1669k rc_init_occupancy=1669k rc_buf_aggressivity=0.25 acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Intra-frame only, 50 Mb/s MPEG-2 also known as Sony IMX or SMPTE 365M meta.preset.name=camcorder/D10 (SD NTSC) mlt-7.38.0/presets/consumer/avformat/dv_ntsc/DV000664 000000 000000 00000000320 15172202314 021410 0ustar00rootroot000000 000000 f=dv pix_fmt=yuv411p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format meta.preset.name=camcorder/DV (SD NTSC) mlt-7.38.0/presets/consumer/avformat/dv_ntsc/DVCPRO50000664 000000 000000 00000000300 15172202314 022237 0ustar00rootroot000000 000000 f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV meta.preset.name=camcorder/DVCPRO50 (SD NTSC) mlt-7.38.0/presets/consumer/avformat/dv_ntsc/DVD000664 000000 000000 00000000464 15172202314 021525 0ustar00rootroot000000 000000 f=dvd vcodec=mpeg2video acodec=ac3 vb=6000k maxrate=9000k minrate=0 bufsize=1835008 packetsize=2048 muxrate=10080000 ab=192k ar=48000 g=18 me_range=63 trellis=1 meta.preset.extension=vob meta.preset.note=Process the output with a DVD authoring tool such as dvdauthor. meta.preset.name=device/DVD (SD NTSC) mlt-7.38.0/presets/consumer/avformat/dv_ntsc_wide/000775 000000 000000 00000000000 15172202314 022171 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/dv_ntsc_wide/D10000664 000000 000000 00000000633 15172202314 022442 0ustar00rootroot000000 000000 f=mxf_d10 vcodec=mpeg2video pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M g=1 bf=0 flags=+ildct+low_delay dc=10 intra_vlc=1 non_linear_quant=1 ps=1 qmin=1 qmax=3 bufsize=1669k rc_init_occupancy=1669k rc_buf_aggressivity=0.25 acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Intra-frame only, 50 Mb/s MPEG-2 also known as Sony IMX or SMPTE 365M meta.preset.name=camcorder/D10 (SD Widescreen NTSC) mlt-7.38.0/presets/consumer/avformat/dv_ntsc_wide/DV000664 000000 000000 00000000333 15172202314 022424 0ustar00rootroot000000 000000 f=dv pix_fmt=yuv411p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format meta.preset.name=camcorder/DV (SD Widescreen NTSC) mlt-7.38.0/presets/consumer/avformat/dv_ntsc_wide/DVCPRO50000664 000000 000000 00000000313 15172202314 023253 0ustar00rootroot000000 000000 f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV meta.preset.name=camcorder/DVCPRO50 (SD Widescreen NTSC) mlt-7.38.0/presets/consumer/avformat/dv_ntsc_wide/DVD000664 000000 000000 00000000477 15172202314 022541 0ustar00rootroot000000 000000 f=dvd vcodec=mpeg2video acodec=ac3 vb=6000k maxrate=9000k minrate=0 bufsize=1835008 packetsize=2048 muxrate=10080000 ab=192k ar=48000 g=18 me_range=63 trellis=1 meta.preset.extension=vob meta.preset.note=Process the output with a DVD authoring tool such as dvdauthor. meta.preset.name=device/DVD (SD Widescreen NTSC) mlt-7.38.0/presets/consumer/avformat/dv_pal/000775 000000 000000 00000000000 15172202314 020766 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/dv_pal/D10000664 000000 000000 00000000640 15172202314 021235 0ustar00rootroot000000 000000 f=mxf_d10 top_field_first=1 vcodec=mpeg2video pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M g=1 bf=0 flags=+ildct+low_delay dc=10 intra_vlc=1 non_linear_quant=1 ps=1 qmin=1 qmax=3 bufsize=2000k rc_init_occupancy=2000k rc_buf_aggressivity=0.25 acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Intra-frame only, 50 Mb/s MPEG-2 also known as Sony IMX or SMPTE 365M meta.preset.name=camcorder/D10 (SD PAL) mlt-7.38.0/presets/consumer/avformat/dv_pal/DV000664 000000 000000 00000000317 15172202314 021223 0ustar00rootroot000000 000000 f=dv pix_fmt=yuv420p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format meta.preset.name=camcorder/DV (SD PAL) mlt-7.38.0/presets/consumer/avformat/dv_pal/DVCPRO50000664 000000 000000 00000000277 15172202314 022061 0ustar00rootroot000000 000000 f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV meta.preset.name=camcorder/DVCPRO50 (SD PAL) mlt-7.38.0/presets/consumer/avformat/dv_pal/DVD000664 000000 000000 00000000462 15172202314 021330 0ustar00rootroot000000 000000 f=dvd vcodec=mpeg2video acodec=ac3 vb=5000k maxrate=8000k minrate=0 bufsize=1835008 packetsize=2048 muxrate=10080000 ab=192k ar=48000 g=15 me_range=63 trellis=1 meta.preset.extension=vob meta.preset.note=Process the output with a DVD authoring tool such as dvdauthor. meta.preset.name=device/DVD (SD PAL) mlt-7.38.0/presets/consumer/avformat/dv_pal_wide/000775 000000 000000 00000000000 15172202314 021776 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/dv_pal_wide/D10000664 000000 000000 00000000653 15172202314 022251 0ustar00rootroot000000 000000 f=mxf_d10 top_field_first=1 vcodec=mpeg2video pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M g=1 bf=0 flags=+ildct+low_delay dc=10 intra_vlc=1 non_linear_quant=1 ps=1 qmin=1 qmax=3 bufsize=2000k rc_init_occupancy=2000k rc_buf_aggressivity=0.25 acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Intra-frame only, 50 Mb/s MPEG-2 also known as Sony IMX or SMPTE 365M meta.preset.name=camcorder/D10 (SD Widescreen PAL) mlt-7.38.0/presets/consumer/avformat/dv_pal_wide/DV000664 000000 000000 00000000332 15172202314 022230 0ustar00rootroot000000 000000 f=dv pix_fmt=yuv420p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format meta.preset.name=camcorder/DV (SD Widescreen PAL) mlt-7.38.0/presets/consumer/avformat/dv_pal_wide/DVCPRO50000664 000000 000000 00000000312 15172202314 023057 0ustar00rootroot000000 000000 f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV meta.preset.name=camcorder/DVCPRO50 (SD Widescreen PAL) mlt-7.38.0/presets/consumer/avformat/dv_pal_wide/DVD000664 000000 000000 00000000476 15172202314 022345 0ustar00rootroot000000 000000 f=dvd vcodec=mpeg2video acodec=ac3 vb=5000k maxrate=8000k minrate=0 bufsize=1835008 packetsize=2048 muxrate=10080000 ab=192k ar=48000 g=15 me_range=63 trellis=1 meta.preset.extension=vob meta.preset.note=Process the output with a DVD authoring tool such as dvdauthor. meta.preset.name=device/DVD (SD Widescreen PAL) mlt-7.38.0/presets/consumer/avformat/hdv_1080_25p/000775 000000 000000 00000000000 15172202314 021440 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/hdv_1080_25p/HDV000664 000000 000000 00000000372 15172202314 022006 0ustar00rootroot000000 000000 f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 1080p 25 fps) mlt-7.38.0/presets/consumer/avformat/hdv_1080_30p/000775 000000 000000 00000000000 15172202314 021434 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/hdv_1080_30p/HDV000664 000000 000000 00000000375 15172202314 022005 0ustar00rootroot000000 000000 f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 1080p 29.97 fps) mlt-7.38.0/presets/consumer/avformat/hdv_1080_50i/000775 000000 000000 00000000000 15172202314 021427 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/hdv_1080_50i/HDV000664 000000 000000 00000000372 15172202314 021775 0ustar00rootroot000000 000000 f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 1080i 25 fps) mlt-7.38.0/presets/consumer/avformat/hdv_1080_60i/000775 000000 000000 00000000000 15172202314 021430 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/hdv_1080_60i/HDV000664 000000 000000 00000000375 15172202314 022001 0ustar00rootroot000000 000000 f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 1080i 29.97 fps) mlt-7.38.0/presets/consumer/avformat/hdv_720_25p/000775 000000 000000 00000000000 15172202314 021360 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/hdv_720_25p/HDV000664 000000 000000 00000000371 15172202314 021725 0ustar00rootroot000000 000000 f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 720p 25 fps) mlt-7.38.0/presets/consumer/avformat/hdv_720_30p/000775 000000 000000 00000000000 15172202314 021354 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/hdv_720_30p/HDV000664 000000 000000 00000000374 15172202314 021724 0ustar00rootroot000000 000000 f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 720p 29.97 fps) mlt-7.38.0/presets/consumer/avformat/hdv_720_50p/000775 000000 000000 00000000000 15172202314 021356 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/hdv_720_50p/HDV000664 000000 000000 00000000371 15172202314 021723 0ustar00rootroot000000 000000 f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 720p 50 fps) mlt-7.38.0/presets/consumer/avformat/hdv_720_60p/000775 000000 000000 00000000000 15172202314 021357 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/hdv_720_60p/HDV000664 000000 000000 00000000374 15172202314 021727 0ustar00rootroot000000 000000 f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 720p 59.94 fps) mlt-7.38.0/presets/consumer/avformat/intermediate/000775 000000 000000 00000000000 15172202314 022173 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/intermediate/DNxHR-HQ000664 000000 000000 00000000425 15172202314 023350 0ustar00rootroot000000 000000 pix_fmt=yuv422p progressive=1 vcodec=dnxhd vprofile=dnxhr_hq qscale=1 vb=0 g=1 bf=0 acodec=pcm_s24le f=mov movflags=+write_colr+faststart meta.preset.extension=mov meta.preset.note=Avid's intermediate codec for resolutions up to UHD/C4K meta.preset.name=intermediate/DNxHR HQ mlt-7.38.0/presets/consumer/avformat/intermediate/MJPEG000664 000000 000000 00000000225 15172202314 022757 0ustar00rootroot000000 000000 progressive=1 f=avi acodec=pcm_s16le vcodec=mjpeg qscale=1 g=1 bf=0 meta.preset.extension=avi meta.preset.note=not lossless, but still high quality mlt-7.38.0/presets/consumer/avformat/intermediate/MPEG-2000664 000000 000000 00000000244 15172202314 023005 0ustar00rootroot000000 000000 f=mpeg acodec=ac3 ab=512k vcodec=mpeg2video vb=0 g=1 bf=0 qscale=1 meta.preset.extension=mpg meta.preset.note=a little lossy, but intra-frame only with AC-3 audio mlt-7.38.0/presets/consumer/avformat/intermediate/MPEG-4000664 000000 000000 00000000250 15172202314 023004 0ustar00rootroot000000 000000 f=avi acodec=pcm_s16le vcodec=mpeg4 qscale=1 g=1 vb=0 bf=0 meta.preset.extension=avi meta.preset.note=somewhat lossy, intra-frame only MPEG-4, with uncompressed audio mlt-7.38.0/presets/consumer/avformat/intermediate/ProRes000664 000000 000000 00000000345 15172202314 023332 0ustar00rootroot000000 000000 f=mov acodec=pcm_s16le vcodec=prores vb=0 g=1 bf=0 vprofile=2 vendor=apl0 movflags=+write_colr meta.preset.extension=mov meta.preset.note=Designed by Apple in California. Set vprofile=1 for LT or =3 for HQ. meta.preset.hidden=1 mlt-7.38.0/presets/consumer/avformat/intermediate/ProRes HQ000664 000000 000000 00000000405 15172202314 023620 0ustar00rootroot000000 000000 f=mov acodec=pcm_s16le vcodec=prores_ks vb=0 g=1 bf=0 vprofile=3 vendor=apl0 qscale=1 movflags=+write_colr meta.preset.extension=mov meta.preset.note=Designed by Apple in California. Set vprofile=1 for LT or =2 for 422. meta.preset.name=intermediate/ProRes HQ mlt-7.38.0/presets/consumer/avformat/intermediate/ProRes-Kostya000664 000000 000000 00000000405 15172202314 024577 0ustar00rootroot000000 000000 f=mov acodec=pcm_s16le vcodec=prores_ks vb=0 g=1 bf=0 vprofile=2 vendor=apl0 qscale=1 movflags=+write_colr meta.preset.extension=mov meta.preset.note=Designed by Apple in California. Set vprofile=1 for LT or =3 for HQ. meta.preset.name=intermediate/ProRes 422 mlt-7.38.0/presets/consumer/avformat/lossless/000775 000000 000000 00000000000 15172202314 021370 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/lossless/FFV1000664 000000 000000 00000000265 15172202314 022020 0ustar00rootroot000000 000000 f=matroska acodec=flac vcodec=ffv1 g=1 bf=0 coder=1 context=1 pix_fmt=yuv422p meta.preset.extension=mkv meta.preset.note=FFmpeg video codec 1 with FLAC audio in Matroska container mlt-7.38.0/presets/consumer/avformat/lossless/H.264000664 000000 000000 00000000300 15172202314 022005 0ustar00rootroot000000 000000 f=mp4 acodec=aac ab=384k vcodec=libx264 vb=0 g=25 bf=0 preset=medium crf=0 coder=ac meta.preset.extension=mp4 meta.preset.note=Intra-frame only, lossless compressed MPEG-4 AVC with AAC audio mlt-7.38.0/presets/consumer/avformat/lossless/HuffYUV000664 000000 000000 00000000201 15172202314 022600 0ustar00rootroot000000 000000 f=matroska acodec=flac vcodec=huffyuv g=1 bf=0 meta.preset.extension=mkv meta.preset.note=with FLAC audio in Matroska container mlt-7.38.0/presets/consumer/avformat/lossless/Ut Video000664 000000 000000 00000000306 15172202314 022731 0ustar00rootroot000000 000000 f=matroska vcodec=utvideo pix_fmt=yuv422p g=1 bf=0 color_range=mpeg pix_fmt=yuv422p acodec=pcm_s24le meta.preset.extension=mkv meta.preset.note=Ut Video with 24-bit PCM audio in Matroska container mlt-7.38.0/presets/consumer/avformat/stills/000775 000000 000000 00000000000 15172202314 021033 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/stills/BMP000664 000000 000000 00000000127 15172202314 021374 0ustar00rootroot000000 000000 progressive=1 f=image2 vcodec=bmp an=1 audio_off=1 g=1 bf=0 meta.preset.extension=bmp mlt-7.38.0/presets/consumer/avformat/stills/DPX000664 000000 000000 00000000127 15172202314 021411 0ustar00rootroot000000 000000 progressive=1 f=image2 vcodec=dpx an=1 audio_off=1 g=1 bf=0 meta.preset.extension=dpx mlt-7.38.0/presets/consumer/avformat/stills/JPEG000664 000000 000000 00000000222 15172202314 021477 0ustar00rootroot000000 000000 color_range=pc progressive=1 f=image2 vcodec=mjpeg qscale=1 an=1 audio_off=1 g=1 bf=0 pix_fmt=yuvj422p colorspace=srgb meta.preset.extension=jpg mlt-7.38.0/presets/consumer/avformat/stills/PNG000664 000000 000000 00000000127 15172202314 021402 0ustar00rootroot000000 000000 progressive=1 f=image2 vcodec=png an=1 audio_off=1 g=1 bf=0 meta.preset.extension=png mlt-7.38.0/presets/consumer/avformat/stills/PPM000664 000000 000000 00000000127 15172202314 021412 0ustar00rootroot000000 000000 progressive=1 f=image2 vcodec=ppm an=1 audio_off=1 g=1 bf=0 meta.preset.extension=ppm mlt-7.38.0/presets/consumer/avformat/stills/TGA000664 000000 000000 00000000131 15172202314 021364 0ustar00rootroot000000 000000 progressive=1 f=image2 vcodec=targa an=1 audio_off=1 g=1 bf=0 meta.preset.extension=tga mlt-7.38.0/presets/consumer/avformat/stills/TIFF000664 000000 000000 00000000130 15172202314 021500 0ustar00rootroot000000 000000 progressive=1 f=image2 vcodec=tiff an=1 audio_off=1 g=1 bf=0 meta.preset.extension=tif mlt-7.38.0/presets/consumer/avformat/stills/webp000664 000000 000000 00000000346 15172202314 021716 0ustar00rootroot000000 000000 progressive=1 f=image2 vcodec=libwebp an=1 audio_off=1 g=1 bf=0 qscale=75 preset=default meta.preset.extension=webp meta.preset.name=stills/WebP meta.preset.note=Change preset to photo, picture, drawing, icon, or text as needed. mlt-7.38.0/presets/consumer/avformat/ten_bit/000775 000000 000000 00000000000 15172202314 021145 5ustar00rootroot000000 000000 mlt-7.38.0/presets/consumer/avformat/ten_bit/AV1000664 000000 000000 00000000530 15172202314 021455 0ustar00rootroot000000 000000 f=webm acodec=libopus ar=48000 ab=128k vcodec=libaom-av1 vb=0 crf=45 row-mt=1 tile-columns=2 tile-rows=1 cpu-used=4 strict=experimental pix_fmt=yuv420p10le meta.preset.name=ten_bit/10-bit AV1 WebM meta.preset.extension=webm meta.preset.note=10-bit AV1 video with Opus audio in Matroska container: Just say no to patents meta.preset.hidden=1 mlt-7.38.0/presets/consumer/avformat/ten_bit/DNxHR-HQ000664 000000 000000 00000000434 15172202314 022322 0ustar00rootroot000000 000000 progressive=1 vcodec=dnxhd vprofile=dnxhr_hqx qscale=1 vb=0 g=1 bf=0 acodec=pcm_s24le f=mov movflags=+write_colr+faststart pix_fmt=yuv422p10le meta.preset.extension=mov meta.preset.note=Avid's intermediate codec for resolutions up to UHD/C4K meta.preset.name=ten_bit/10-bit DNxHR HQ mlt-7.38.0/presets/consumer/avformat/ten_bit/FFV1000664 000000 000000 00000000316 15172202314 021572 0ustar00rootroot000000 000000 f=matroska acodec=flac vcodec=ffv1 g=1 bf=0 coder=1 context=1 pix_fmt=yuv444p10le meta.preset.extension=mkv meta.preset.note=FFmpeg video codec 1, 10 bit, Y'CbCr 4:4:4 meta.preset.name=ten_bit/10-bit FFV1 mlt-7.38.0/presets/consumer/avformat/ten_bit/ProRes 422000664 000000 000000 00000000313 15172202314 022567 0ustar00rootroot000000 000000 f=mov acodec=pcm_s16le vcodec=prores_ks vb=0 g=1 bf=0 vprofile=2 vendor=apl0 qscale=1 movflags=+write_colr pix_fmt=yuv422p10le meta.preset.extension=mov meta.preset.note=Designed by Apple in California mlt-7.38.0/presets/consumer/avformat/ten_bit/ProRes 444000664 000000 000000 00000000313 15172202314 022573 0ustar00rootroot000000 000000 f=mov acodec=pcm_s16le vcodec=prores_ks vb=0 g=1 bf=0 vprofile=4 vendor=apl0 qscale=1 movflags=+write_colr pix_fmt=yuv444p10le meta.preset.extension=mov meta.preset.note=Designed by Apple in California mlt-7.38.0/presets/consumer/avformat/ten_bit/ProRes HQ000664 000000 000000 00000000313 15172202314 022570 0ustar00rootroot000000 000000 f=mov acodec=pcm_s16le vcodec=prores_ks vb=0 g=1 bf=0 vprofile=3 vendor=apl0 qscale=1 movflags=+write_colr pix_fmt=yuv422p10le meta.preset.extension=mov meta.preset.note=Designed by Apple in California mlt-7.38.0/presets/consumer/avformat/ten_bit/SVT-AV1000664 000000 000000 00000000721 15172202314 022131 0ustar00rootroot000000 000000 f=webm acodec=libopus ar=48000 ab=192k vcodec=libsvtav1 crf=32 bf=1 svtav1-params=preset=5:tile-columns=1:fast-decode=0:tune=0:film-grain=0:film-grain-denoise=0:enable-overlays=1:enable-variance-boost=1:variance-boost-strength=1:variance-octile=7:enable-qm=1:qm-min=0 pix_fmt=yuv420p10le progressive=1 meta.preset.name=ten_bit/10-bit AV1 WebM meta.preset.extension=webm meta.preset.note=10-bit AV1 video with Opus audio in WebM container: Just say no to patents mlt-7.38.0/presets/consumer/avformat/ten_bit/vp9-eac3-mp4000664 000000 000000 00000000573 15172202314 023122 0ustar00rootroot000000 000000 f=mp4 movflags=+faststart acodec=eac3 ar=48000 ab=192k vcodec=libvpx-vp9 vprofile=2 pix_fmt=yuv420p10le vb=0 crf=16 rc_lookahead=16 quality=good speed=2 slices=4 tile-columns=6 frame-parallel=1 auto-alt-ref=1 lag-in-frames=25 row-mt=1 meta.preset.name=ten_bit/10-bit VP9 MP4 (E-AC-3) meta.preset.extension=mp4 meta.preset.note=This version is better for TVs and Apple devices mlt-7.38.0/presets/consumer/avformat/ten_bit/vp9-opus-webm000664 000000 000000 00000000451 15172202314 023522 0ustar00rootroot000000 000000 f=webm acodec=libopus ar=48000 ab=128k vcodec=libvpx-vp9 vprofile=2 pix_fmt=yuv420p10le vb=0 crf=16 rc_lookahead=16 quality=good speed=2 slices=4 tile-columns=6 frame-parallel=1 auto-alt-ref=1 lag-in-frames=25 row-mt=1 meta.preset.name=ten_bit/10-bit VP9 WebM (Opus) meta.preset.extension=webm mlt-7.38.0/presets/consumer/avformat/ten_bit/x264-high10000664 000000 000000 00000000305 15172202314 022647 0ustar00rootroot000000 000000 f=mp4 acodec=aac ab=256k vcodec=libx264 threads=0 preset=medium vprofile=high10 pix_fmt=yuv420p10le movflags=+faststart meta.preset.extension=mp4 meta.preset.name=ten_bit/H.264 High 10 Profile mlt-7.38.0/presets/consumer/avformat/ten_bit/x265-main10000664 000000 000000 00000000304 15172202314 022654 0ustar00rootroot000000 000000 f=mp4 acodec=aac ab=256k vcodec=libx265 threads=0 preset=medium vprofile=main10 pix_fmt=yuv420p10le movflags=+faststart meta.preset.extension=mp4 meta.preset.name=ten_bit/HEVC Main 10 Profile mlt-7.38.0/presets/consumer/avformat/vp9000664 000000 000000 00000000540 15172202314 020161 0ustar00rootroot000000 000000 f=webm acodec=libopus ar=48000 ab=128k vcodec=libvpx-vp9 vb=2M g=120 bf=2 threads=0 rc_lookahead=16 quality=good speed=3 vprofile=0 slices=4 tile-columns=6 frame-parallel=1 auto-alt-ref=1 lag-in-frames=25 row-mt=1 meta.preset.name=WebM VP9 meta.preset.extension=webm meta.preset.note=VP9 video with Opus audio in Matroska container: "Don't be evil" mlt-7.38.0/presets/consumer/avformat/webm000664 000000 000000 00000000452 15172202314 020377 0ustar00rootroot000000 000000 f=webm acodec=vorbis ab=128k vcodec=libvpx g=120 rc_lookahead=16 quality=good speed=0 vprofile=0 slices=4 vb=2M arnr_max_frames=7 arnr_strength=5 arnr_type=3 meta.preset.name=WebM meta.preset.extension=webm meta.preset.note=VP8 video with Ogg Vorbis audio in Matroska container: "Don't be evil" mlt-7.38.0/presets/consumer/avformat/webm-pass1000664 000000 000000 00000000270 15172202314 021422 0ustar00rootroot000000 000000 f=webm an=1 audio_off=1 vcodec=libvpx g=120 rc_lookahead=16 quality=good speed=0 vprofile=0 slices=4 vb=2M arnr_max_frames=7 arnr_strength=5 arnr_type=3 pass=1 meta.preset.hidden=1 mlt-7.38.0/presets/consumer/avformat/webp000664 000000 000000 00000000354 15172202314 020403 0ustar00rootroot000000 000000 progressive=1 f=webp vcodec=libwebp_anim an=1 audio_off=1 g=1 bf=0 qscale=75 preset=default meta.preset.extension=webp meta.preset.name=WebP Animation meta.preset.note=Change preset to photo, picture, drawing, icon, or text as needed. mlt-7.38.0/presets/consumer/avformat/x264-medium000664 000000 000000 00000000253 15172202314 021425 0ustar00rootroot000000 000000 f=mp4 acodec=aac ab=256k vcodec=libx264 threads=0 preset=medium vprofile=high crf=18 movflags=+faststart meta.preset.extension=mp4 meta.preset.name=H.264 High Profile mlt-7.38.0/presets/consumer/avformat/x264-medium-baseline000664 000000 000000 00000000327 15172202314 023207 0ustar00rootroot000000 000000 f=mp4 acodec=aac ab=256k vcodec=libx264 threads=0 preset=medium vprofile=baseline coder=0 bf=0 flags2=-wpred-dct8x8 wpredp=0 movflags=+faststart meta.preset.extension=mp4 meta.preset.name=H.264 Baseline Profile mlt-7.38.0/presets/consumer/avformat/x264-medium-main000664 000000 000000 00000000263 15172202314 022350 0ustar00rootroot000000 000000 f=mp4 acodec=aac ab=256k vcodec=libx264 threads=0 preset=medium vprofile=main flags2=-dct8x8 movflags=+faststart meta.preset.extension=mp4 meta.preset.name=H.264 Main Profile mlt-7.38.0/presets/consumer/avformat/x264-medium-pass1000664 000000 000000 00000000430 15172202314 022447 0ustar00rootroot000000 000000 f=mp4 vcodec=libx264 threads=0 preset=medium vprofile=high fastfirstpass=1 partitions=-parti8x8-parti4x4-partp8x8-partb8x8 me_method=dia subq=2 refs=1 trellis=0 flags2=+bpyramid-mixed_refs+wpred-dct8x8+fastpskip pass=1 an=1 audio_off=1 movflags=+faststart meta.preset.hidden=1 mlt-7.38.0/presets/consumer/avformat/x265-medium000664 000000 000000 00000000225 15172202314 021425 0ustar00rootroot000000 000000 f=mp4 acodec=aac ab=256k vcodec=libx265 threads=0 preset=medium movflags=+faststart meta.preset.extension=mp4 meta.preset.name=HEVC Main Profile mlt-7.38.0/presets/consumer/avformat/x265-medium-pass1000664 000000 000000 00000000175 15172202314 022456 0ustar00rootroot000000 000000 f=mp4 vcodec=libx265 threads=0 preset=medium x265-params=pass=1 an=1 audio_off=1 movflags=+faststart meta.preset.hidden=1 mlt-7.38.0/presets/filter/000775 000000 000000 00000000000 15172202314 015334 5ustar00rootroot000000 000000 mlt-7.38.0/presets/filter/brightness/000775 000000 000000 00000000000 15172202314 017504 5ustar00rootroot000000 000000 mlt-7.38.0/presets/filter/brightness/from_black000664 000000 000000 00000000032 15172202314 021521 0ustar00rootroot000000 000000 out=50 in=0 start=0 end=1 mlt-7.38.0/presets/filter/brightness/to_black000664 000000 000000 00000000032 15172202314 021200 0ustar00rootroot000000 000000 out=50 in=0 start=1 end=0 mlt-7.38.0/presets/filter/movit.blur/000775 000000 000000 00000000000 15172202314 017435 5ustar00rootroot000000 000000 mlt-7.38.0/presets/filter/movit.blur/blur_in000664 000000 000000 00000000031 15172202314 021004 0ustar00rootroot000000 000000 radius=0=10.0; :1.0=0.0; mlt-7.38.0/presets/filter/movit.blur/blur_in_out000664 000000 000000 00000000053 15172202314 021677 0ustar00rootroot000000 000000 radius=0=10.0; :1.0=0; :-1.0=0.0; -1=10.0; mlt-7.38.0/presets/filter/movit.blur/blur_out000664 000000 000000 00000000040 15172202314 021205 0ustar00rootroot000000 000000 radius=0=0; :-1.0=0.0; -1=10.0; mlt-7.38.0/presets/filter/movit.opacity/000775 000000 000000 00000000000 15172202314 020141 5ustar00rootroot000000 000000 mlt-7.38.0/presets/filter/movit.opacity/fade_in000664 000000 000000 00000000035 15172202314 021447 0ustar00rootroot000000 000000 opacity=0=0;:2.0=1.0 alpha=1 mlt-7.38.0/presets/filter/movit.opacity/fade_in_out000664 000000 000000 00000000056 15172202314 022341 0ustar00rootroot000000 000000 opacity=0=0;:2.0=1.0; :-2.0=1.0; -1=0 alpha=1 mlt-7.38.0/presets/filter/movit.opacity/fade_out000664 000000 000000 00000000040 15172202314 021644 0ustar00rootroot000000 000000 opacity=:-2.0=1.0; -1=0 alpha=1 mlt-7.38.0/presets/filter/volume/000775 000000 000000 00000000000 15172202314 016643 5ustar00rootroot000000 000000 mlt-7.38.0/presets/filter/volume/fade_in000664 000000 000000 00000000031 15172202314 020145 0ustar00rootroot000000 000000 out=50 in=0 gain=0 end=1 mlt-7.38.0/presets/filter/volume/fade_out000664 000000 000000 00000000031 15172202314 020346 0ustar00rootroot000000 000000 out=50 in=0 gain=1 end=0 mlt-7.38.0/profiles/000775 000000 000000 00000000000 15172202314 014205 5ustar00rootroot000000 000000 mlt-7.38.0/profiles/atsc_1080i_50000664 000000 000000 00000000306 15172202314 016206 0ustar00rootroot000000 000000 description=HD 1080i 25 fps frame_rate_num=25 frame_rate_den=1 width=1920 height=1080 progressive=0 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080i_5994000664 000000 000000 00000000317 15172202314 016376 0ustar00rootroot000000 000000 description=HD 1080i 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=1920 height=1080 progressive=0 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080i_60000664 000000 000000 00000000306 15172202314 016207 0ustar00rootroot000000 000000 description=HD 1080i 30 fps frame_rate_num=30 frame_rate_den=1 width=1920 height=1080 progressive=0 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080p_2398000664 000000 000000 00000000317 15172202314 016400 0ustar00rootroot000000 000000 description=HD 1080p 23.98 fps frame_rate_num=24000 frame_rate_den=1001 width=1920 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080p_24000664 000000 000000 00000000306 15172202314 016216 0ustar00rootroot000000 000000 description=HD 1080p 24 fps frame_rate_num=24 frame_rate_den=1 width=1920 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080p_25000664 000000 000000 00000000306 15172202314 016217 0ustar00rootroot000000 000000 description=HD 1080p 25 fps frame_rate_num=25 frame_rate_den=1 width=1920 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080p_2997000664 000000 000000 00000000317 15172202314 016405 0ustar00rootroot000000 000000 description=HD 1080p 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=1920 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080p_30000664 000000 000000 00000000306 15172202314 016213 0ustar00rootroot000000 000000 description=HD 1080p 30 fps frame_rate_num=30 frame_rate_den=1 width=1920 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080p_50000664 000000 000000 00000000306 15172202314 016215 0ustar00rootroot000000 000000 description=HD 1080p 50 fps frame_rate_num=50 frame_rate_den=1 width=1920 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080p_5994000664 000000 000000 00000000317 15172202314 016405 0ustar00rootroot000000 000000 description=HD 1080p 59.94 fps frame_rate_num=60000 frame_rate_den=1001 width=1920 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_1080p_60000664 000000 000000 00000000306 15172202314 016216 0ustar00rootroot000000 000000 description=HD 1080p 60 fps frame_rate_num=60 frame_rate_den=1 width=1920 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_720p_2398000664 000000 000000 00000000315 15172202314 016316 0ustar00rootroot000000 000000 description=HD 720p 23.98 fps frame_rate_num=24000 frame_rate_den=1001 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_720p_24000664 000000 000000 00000000304 15172202314 016134 0ustar00rootroot000000 000000 description=HD 720p 24 fps frame_rate_num=24 frame_rate_den=1 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_720p_25000664 000000 000000 00000000304 15172202314 016135 0ustar00rootroot000000 000000 description=HD 720p 25 fps frame_rate_num=25 frame_rate_den=1 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_720p_2997000664 000000 000000 00000000315 15172202314 016323 0ustar00rootroot000000 000000 description=HD 720p 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_720p_30000664 000000 000000 00000000304 15172202314 016131 0ustar00rootroot000000 000000 description=HD 720p 30 fps frame_rate_num=30 frame_rate_den=1 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_720p_50000664 000000 000000 00000000304 15172202314 016133 0ustar00rootroot000000 000000 description=HD 720p 50 fps frame_rate_num=50 frame_rate_den=1 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_720p_5994000664 000000 000000 00000000315 15172202314 016323 0ustar00rootroot000000 000000 description=HD 720p 59.94 fps frame_rate_num=60000 frame_rate_den=1001 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/atsc_720p_60000664 000000 000000 00000000304 15172202314 016134 0ustar00rootroot000000 000000 description=HD 720p 60 fps frame_rate_num=60 frame_rate_den=1 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/cif_15000664 000000 000000 00000000300 15172202314 015167 0ustar00rootroot000000 000000 description=CIF 15 fps frame_rate_num=15 frame_rate_den=1 width=352 height=288 progressive=1 sample_aspect_num=59 sample_aspect_den=54 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/cif_ntsc000664 000000 000000 00000000304 15172202314 015715 0ustar00rootroot000000 000000 description=CIF NTSC frame_rate_num=30000 frame_rate_den=1001 width=352 height=288 progressive=1 sample_aspect_num=10 sample_aspect_den=11 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/cif_pal000664 000000 000000 00000000275 15172202314 015531 0ustar00rootroot000000 000000 description=CIF PAL frame_rate_num=25 frame_rate_den=1 width=352 height=288 progressive=1 sample_aspect_num=59 sample_aspect_den=54 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/cvd_ntsc000664 000000 000000 00000000304 15172202314 015730 0ustar00rootroot000000 000000 description=CVD NTSC frame_rate_num=30000 frame_rate_den=1001 width=352 height=480 progressive=0 sample_aspect_num=20 sample_aspect_den=11 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/cvd_pal000664 000000 000000 00000000275 15172202314 015544 0ustar00rootroot000000 000000 description=CVD PAL frame_rate_num=25 frame_rate_den=1 width=352 height=576 progressive=0 sample_aspect_num=59 sample_aspect_den=27 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/dv_ntsc000664 000000 000000 00000000305 15172202314 015566 0ustar00rootroot000000 000000 description=DV/DVD NTSC frame_rate_num=30000 frame_rate_den=1001 width=720 height=480 progressive=0 sample_aspect_num=8 sample_aspect_den=9 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/dv_ntsc_wide000664 000000 000000 00000000323 15172202314 016576 0ustar00rootroot000000 000000 description=DV/DVD Widescreen NTSC frame_rate_num=30000 frame_rate_den=1001 width=720 height=480 progressive=0 sample_aspect_num=32 sample_aspect_den=27 display_aspect_num=16 display_aspect_den=9 colorspace=601 mlt-7.38.0/profiles/dv_pal000664 000000 000000 00000000300 15172202314 015366 0ustar00rootroot000000 000000 description=DV/DVD PAL frame_rate_num=25 frame_rate_den=1 width=720 height=576 progressive=0 sample_aspect_num=16 sample_aspect_den=15 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/dv_pal_wide000664 000000 000000 00000000314 15172202314 016403 0ustar00rootroot000000 000000 description=DV/DVD Widescreen PAL frame_rate_num=25 frame_rate_den=1 width=720 height=576 progressive=0 sample_aspect_num=64 sample_aspect_den=45 display_aspect_num=16 display_aspect_den=9 colorspace=601 mlt-7.38.0/profiles/hdv_1080_25p000664 000000 000000 00000000314 15172202314 016045 0ustar00rootroot000000 000000 description=HDV 1440x1080p 25 fps frame_rate_num=25 frame_rate_den=1 width=1440 height=1080 progressive=1 sample_aspect_num=4 sample_aspect_den=3 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/hdv_1080_30p000664 000000 000000 00000000325 15172202314 016043 0ustar00rootroot000000 000000 description=HDV 1440x1080p 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=1440 height=1080 progressive=1 sample_aspect_num=4 sample_aspect_den=3 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/hdv_1080_50i000664 000000 000000 00000000314 15172202314 016034 0ustar00rootroot000000 000000 description=HDV 1440x1080i 25 fps frame_rate_num=25 frame_rate_den=1 width=1440 height=1080 progressive=0 sample_aspect_num=4 sample_aspect_den=3 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/hdv_1080_60i000664 000000 000000 00000000325 15172202314 016037 0ustar00rootroot000000 000000 description=HDV 1440x1080i 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=1440 height=1080 progressive=0 sample_aspect_num=4 sample_aspect_den=3 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/hdv_720_25p000664 000000 000000 00000000304 15172202314 015764 0ustar00rootroot000000 000000 description=HD 720p 25 fps frame_rate_num=25 frame_rate_den=1 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/hdv_720_30p000664 000000 000000 00000000315 15172202314 015762 0ustar00rootroot000000 000000 description=HD 720p 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/hdv_720_50p000664 000000 000000 00000000304 15172202314 015762 0ustar00rootroot000000 000000 description=HD 720p 50 fps frame_rate_num=50 frame_rate_den=1 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/hdv_720_60p000664 000000 000000 00000000315 15172202314 015765 0ustar00rootroot000000 000000 description=HD 720p 59.94 fps frame_rate_num=60000 frame_rate_den=1001 width=1280 height=720 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/qcif_15000664 000000 000000 00000000301 15172202314 015351 0ustar00rootroot000000 000000 description=QCIF 15 fps frame_rate_num=15 frame_rate_den=1 width=176 height=144 progressive=1 sample_aspect_num=59 sample_aspect_den=54 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/qcif_ntsc000664 000000 000000 00000000305 15172202314 016077 0ustar00rootroot000000 000000 description=QCIF NTSC frame_rate_num=30000 frame_rate_den=1001 width=176 height=144 progressive=1 sample_aspect_num=10 sample_aspect_den=11 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/qcif_pal000664 000000 000000 00000000276 15172202314 015713 0ustar00rootroot000000 000000 description=QCIF PAL frame_rate_num=25 frame_rate_den=1 width=176 height=144 progressive=1 sample_aspect_num=59 sample_aspect_den=54 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/qhd_1440p_2398000664 000000 000000 00000000325 15172202314 016221 0ustar00rootroot000000 000000 description=2.5K QHD 1440p 23.98 fps frame_rate_num=24000 frame_rate_den=1001 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/qhd_1440p_24000664 000000 000000 00000000314 15172202314 016037 0ustar00rootroot000000 000000 description=2.5K QHD 1440p 24 fps frame_rate_num=24 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/qhd_1440p_25000664 000000 000000 00000000314 15172202314 016040 0ustar00rootroot000000 000000 description=2.5K QHD 1440p 25 fps frame_rate_num=25 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/qhd_1440p_2997000664 000000 000000 00000000325 15172202314 016226 0ustar00rootroot000000 000000 description=2.5K QHD 1440p 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/qhd_1440p_30000664 000000 000000 00000000314 15172202314 016034 0ustar00rootroot000000 000000 description=2.5K QHD 1440p 30 fps frame_rate_num=30 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/qhd_1440p_50000664 000000 000000 00000000314 15172202314 016036 0ustar00rootroot000000 000000 description=2.5K QHD 1440p 50 fps frame_rate_num=50 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/qhd_1440p_5994000664 000000 000000 00000000325 15172202314 016226 0ustar00rootroot000000 000000 description=2.5K QHD 1440p 59.94 fps frame_rate_num=60000 frame_rate_den=1001 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/qhd_1440p_60000664 000000 000000 00000000314 15172202314 016037 0ustar00rootroot000000 000000 description=2.5K QHD 1440p 60 fps frame_rate_num=60 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/quarter_15000664 000000 000000 00000000277 15172202314 016126 0ustar00rootroot000000 000000 description=QVGA 15 fps frame_rate_num=15 frame_rate_den=1 width=320 height=240 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/quarter_ntsc000664 000000 000000 00000000310 15172202314 016634 0ustar00rootroot000000 000000 description=QVGA 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=320 height=240 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/quarter_ntsc_wide000664 000000 000000 00000000324 15172202314 017651 0ustar00rootroot000000 000000 description=QVGA Widescreen 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=426 height=240 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=601 mlt-7.38.0/profiles/quarter_pal000664 000000 000000 00000000303 15172202314 016443 0ustar00rootroot000000 000000 description=384x288 4:3 PAL frame_rate_num=25 frame_rate_den=1 width=384 height=288 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/quarter_pal_wide000664 000000 000000 00000000305 15172202314 017455 0ustar00rootroot000000 000000 description=512x288 16:9 PAL frame_rate_num=25 frame_rate_den=1 width=512 height=288 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=601 mlt-7.38.0/profiles/sdi_486i_5994000664 000000 000000 00000000310 15172202314 016145 0ustar00rootroot000000 000000 description=NTSC 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=720 height=486 progressive=0 sample_aspect_num=8 sample_aspect_den=9 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/sdi_486p_2398000664 000000 000000 00000000310 15172202314 016147 0ustar00rootroot000000 000000 description=NTSC 23.98 fps frame_rate_num=24000 frame_rate_den=1001 width=720 height=486 progressive=1 sample_aspect_num=8 sample_aspect_den=9 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/square_1080p_30000664 000000 000000 00000000311 15172202314 016555 0ustar00rootroot000000 000000 description=Square 1080p 30 fps frame_rate_num=30 frame_rate_den=1 width=1080 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=1 display_aspect_den=1 colorspace=709 mlt-7.38.0/profiles/square_1080p_60000664 000000 000000 00000000311 15172202314 016560 0ustar00rootroot000000 000000 description=Square 1080p 60 fps frame_rate_num=60 frame_rate_den=1 width=1080 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=1 display_aspect_den=1 colorspace=709 mlt-7.38.0/profiles/square_ntsc000664 000000 000000 00000000302 15172202314 016452 0ustar00rootroot000000 000000 description=VGA NTSC frame_rate_num=30000 frame_rate_den=1001 width=640 height=480 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/square_ntsc_wide000664 000000 000000 00000000316 15172202314 017467 0ustar00rootroot000000 000000 description=VGA Widescreen NTSC frame_rate_num=30000 frame_rate_den=1001 width=854 height=480 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=601 mlt-7.38.0/profiles/square_pal000664 000000 000000 00000000303 15172202314 016260 0ustar00rootroot000000 000000 description=768x576 4:3 PAL frame_rate_num=25 frame_rate_den=1 width=768 height=576 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/square_pal_wide000664 000000 000000 00000000307 15172202314 017274 0ustar00rootroot000000 000000 description=1024x576 16:9 PAL frame_rate_num=25 frame_rate_den=1 width=1024 height=576 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=601 mlt-7.38.0/profiles/svcd_ntsc000664 000000 000000 00000000305 15172202314 016114 0ustar00rootroot000000 000000 description=SVCD NTSC frame_rate_num=30000 frame_rate_den=1001 width=480 height=480 progressive=0 sample_aspect_num=15 sample_aspect_den=11 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/svcd_ntsc_wide000664 000000 000000 00000000321 15172202314 017122 0ustar00rootroot000000 000000 description=SVCD Widescreen NTSC frame_rate_num=30000 frame_rate_den=1001 width=480 height=480 progressive=0 sample_aspect_num=20 sample_aspect_den=11 display_aspect_num=16 display_aspect_den=9 colorspace=601 mlt-7.38.0/profiles/svcd_pal000664 000000 000000 00000000276 15172202314 015730 0ustar00rootroot000000 000000 description=SVCD PAL frame_rate_num=25 frame_rate_den=1 width=480 height=576 progressive=0 sample_aspect_num=59 sample_aspect_den=36 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/svcd_pal_wide000664 000000 000000 00000000312 15172202314 016727 0ustar00rootroot000000 000000 description=SVCD Widescreen PAL frame_rate_num=25 frame_rate_den=1 width=480 height=576 progressive=0 sample_aspect_num=59 sample_aspect_den=27 display_aspect_num=16 display_aspect_den=9 colorspace=601 mlt-7.38.0/profiles/uhd_2160p_2398000664 000000 000000 00000000323 15172202314 016223 0ustar00rootroot000000 000000 description=4K UHD 2160p 23.98 fps frame_rate_num=24000 frame_rate_den=1001 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/uhd_2160p_24000664 000000 000000 00000000312 15172202314 016041 0ustar00rootroot000000 000000 description=4K UHD 2160p 24 fps frame_rate_num=24 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/uhd_2160p_25000664 000000 000000 00000000312 15172202314 016042 0ustar00rootroot000000 000000 description=4K UHD 2160p 25 fps frame_rate_num=25 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/uhd_2160p_2997000664 000000 000000 00000000323 15172202314 016230 0ustar00rootroot000000 000000 description=4K UHD 2160p 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/uhd_2160p_30000664 000000 000000 00000000312 15172202314 016036 0ustar00rootroot000000 000000 description=4K UHD 2160p 30 fps frame_rate_num=30 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/uhd_2160p_50000664 000000 000000 00000000312 15172202314 016040 0ustar00rootroot000000 000000 description=4K UHD 2160p 50 fps frame_rate_num=50 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/uhd_2160p_5994000664 000000 000000 00000000323 15172202314 016230 0ustar00rootroot000000 000000 description=4K UHD 2160p 59.94 fps frame_rate_num=60000 frame_rate_den=1001 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/uhd_2160p_60000664 000000 000000 00000000312 15172202314 016041 0ustar00rootroot000000 000000 description=4K UHD 2160p 60 fps frame_rate_num=60 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-7.38.0/profiles/vcd_ntsc000664 000000 000000 00000000304 15172202314 015730 0ustar00rootroot000000 000000 description=VCD NTSC frame_rate_num=30000 frame_rate_den=1001 width=352 height=240 progressive=1 sample_aspect_num=10 sample_aspect_den=11 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/vcd_pal000664 000000 000000 00000000275 15172202314 015544 0ustar00rootroot000000 000000 description=VCD PAL frame_rate_num=25 frame_rate_den=1 width=352 height=288 progressive=1 sample_aspect_num=59 sample_aspect_den=54 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-7.38.0/profiles/vertical_hd_30000664 000000 000000 00000000311 15172202314 016711 0ustar00rootroot000000 000000 description=Vertical HD 30 fps frame_rate_num=30 frame_rate_den=1 width=1080 height=1920 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=9 display_aspect_den=16 colorspace=709 mlt-7.38.0/profiles/vertical_hd_60000664 000000 000000 00000000311 15172202314 016714 0ustar00rootroot000000 000000 description=Vertical HD 60 fps frame_rate_num=60 frame_rate_den=1 width=1080 height=1920 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=9 display_aspect_den=16 colorspace=709 mlt-7.38.0/setenv000664 000000 000000 00000000720 15172202314 013610 0ustar00rootroot000000 000000 # Environment variable settings to allow execution without install export MLT_REPOSITORY="`pwd`/out/lib/mlt" export MLT_DATA="`pwd`/src/modules" export MLT_PROFILES_PATH="`pwd`/out/share/mlt/profiles" export MLT_PRESETS_PATH="`pwd`/out/share/mlt/presets" if [ "$(uname -s)" = "Darwin" ]; then export DYLD_LIBRARY_PATH="`pwd`/out/lib:$DYLD_LIBRARY_PATH" else export LD_LIBRARY_PATH="`pwd`/out/lib:$LD_LIBRARY_PATH" fi export PATH="`pwd`/out/bin:$PATH" mlt-7.38.0/src/000775 000000 000000 00000000000 15172202314 013151 5ustar00rootroot000000 000000 mlt-7.38.0/src/CMakeLists.txt000664 000000 000000 00000000366 15172202314 015716 0ustar00rootroot000000 000000 add_subdirectory(framework) add_subdirectory(melt) add_subdirectory(mlt++) add_subdirectory(modules) if(SWIG_FOUND) add_subdirectory(swig) endif() if(BUILD_TESTING) add_subdirectory(tests) endif() if(MSVC) add_subdirectory(msvc) endif() mlt-7.38.0/src/examples/000775 000000 000000 00000000000 15172202314 014767 5ustar00rootroot000000 000000 mlt-7.38.0/src/examples/CMakeLists.txt000664 000000 000000 00000000674 15172202314 017536 0ustar00rootroot000000 000000 cmake_minimum_required(VERSION 3.14...3.31) project(MLT-examples) option(WITH_PKGCONFIG "Use pkg-config to find MLT" OFF) add_executable(play play.cpp) if(WITH_PKGCONFIG) find_package(PkgConfig REQUIRED) pkg_check_modules(mlt++ REQUIRED IMPORTED_TARGET mlt++-7) target_link_libraries(play PRIVATE PkgConfig::mlt++) else() find_package(Mlt7 REQUIRED COMPONENTS avformat sdl) target_link_libraries(play PRIVATE Mlt7::mlt++) endif() mlt-7.38.0/src/examples/play.cpp000664 000000 000000 00000001135 15172202314 016440 0ustar00rootroot000000 000000 #include using namespace Mlt; void play(const char *filename) { Profile profile; // defaults to dv_pal Producer producer(profile, filename); Consumer consumer(profile); // defaults to sdl // Prevent scaling to the profile size. // Let the sdl consumer do all scaling. consumer.set("rescale", "none"); // Automatically exit at end of file. consumer.set("terminate_on_pause", 1); consumer.connect(producer); consumer.run(); consumer.stop(); } int main(int, char **argv) { Factory::init(); play(argv[1]); Factory::close(); return 0; } mlt-7.38.0/src/framework/000775 000000 000000 00000000000 15172202314 015146 5ustar00rootroot000000 000000 mlt-7.38.0/src/framework/CMakeLists.txt000664 000000 000000 00000007537 15172202314 017722 0ustar00rootroot000000 000000 set(MLT_PUBLIC_HEADERS mlt.h mlt_animation.h mlt_audio.h mlt_cache.h mlt_chain.h mlt_consumer.h mlt_deque.h mlt_events.h mlt_factory.h mlt_field.h mlt_filter.h mlt_frame.h mlt_image.h mlt_link.h mlt_log.h mlt_luma_map.h mlt_multitrack.h mlt_parser.h mlt_playlist.h mlt_pool.h mlt_producer.h mlt_profile.h mlt_properties.h mlt_property.h mlt_repository.h mlt_service.h mlt_slices.h mlt_tokeniser.h mlt_tractor.h mlt_transition.h mlt_types.h mlt_version.h ) add_library(mlt SHARED mlt_animation.c mlt_audio.c mlt_cache.c mlt_chain.c mlt_consumer.c mlt_deque.c mlt_events.c mlt_factory.c mlt_field.c mlt_filter.c mlt_frame.c mlt_image.c mlt_link.c mlt_log.c mlt_luma_map.c mlt_multitrack.c mlt_parser.c mlt_playlist.c mlt_pool.c mlt_producer.c mlt_profile.c mlt_properties.c mlt_property.c mlt_repository.c mlt_service.c mlt_slices.c mlt_tokeniser.c mlt_tractor.c mlt_transition.c mlt_types.c mlt_version.c ) add_custom_target("Other_mlt_Files" SOURCES chain_normalizers.ini metaschema.yaml mlt-framework.pc.in mlt.vers ) add_library(Mlt${MLT_VERSION_MAJOR}::mlt ALIAS mlt) include(GenerateExportHeader) generate_export_header(mlt) target_sources(mlt PRIVATE ${MLT_PUBLIC_HEADERS}) target_compile_options(mlt PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mlt PRIVATE Threads::Threads ${CMAKE_DL_LIBS}) if(MSVC) target_link_libraries(mlt PRIVATE msvccompat PThreads4W::PThreads4W dirent) else() target_link_libraries(mlt PRIVATE m) endif() target_include_directories(mlt PUBLIC $ $ $ ) set_target_properties(mlt PROPERTIES VERSION ${MLT_VERSION} SOVERSION ${MLT_VERSION_MAJOR} OUTPUT_NAME mlt-${MLT_VERSION_MAJOR} PUBLIC_HEADER "${MLT_PUBLIC_HEADERS}" ) if(WIN32) if(MINGW) target_link_libraries(mlt PRIVATE ${MLT_PTHREAD_LIBS}) target_link_options(mlt PRIVATE -Wl,--output-def,${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt-${MLT_VERSION_MAJOR}.def) install(FILES "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt-${MLT_VERSION_MAJOR}.def" DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() target_sources(mlt PRIVATE ../win32/win32.c) target_link_libraries(mlt PRIVATE Iconv::Iconv) if(NOT WINDOWS_DEPLOY) target_compile_definitions(mlt PRIVATE NODEPLOY) endif() endif() if(NOT (WIN32 OR RELOCATABLE)) if(APPLE) target_compile_definitions(mlt PRIVATE PREFIX_DATA="${CMAKE_INSTALL_FULL_DATADIR}/mlt" PREFIX_LIB="${CMAKE_INSTALL_FULL_LIBDIR}/mlt") else() target_compile_definitions(mlt PRIVATE PREFIX_DATA="${CMAKE_INSTALL_FULL_DATADIR}/mlt-${MLT_VERSION_MAJOR}" PREFIX_LIB="${CMAKE_INSTALL_FULL_LIBDIR}/mlt-${MLT_VERSION_MAJOR}") target_link_options(mlt PRIVATE -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/mlt.vers) set_target_properties(mlt PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/mlt.vers) endif() elseif(RELOCATABLE) target_compile_definitions(mlt PRIVATE RELOCATABLE) endif() install(TARGETS mlt EXPORT MltTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mlt-${MLT_VERSION_MAJOR}/framework ) # Install the generated export header install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mlt_export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mlt-${MLT_VERSION_MAJOR}/framework ) install(FILES metaschema.yaml chain_normalizers.ini DESTINATION ${MLT_INSTALL_DATA_DIR}) configure_file(mlt-framework.pc.in mlt-framework-${MLT_VERSION_MAJOR}.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mlt-framework-${MLT_VERSION_MAJOR}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT Development ) mlt-7.38.0/src/framework/chain_normalizers.ini000664 000000 000000 00000001301 15172202314 021351 0ustar00rootroot000000 000000 # This file defines the chain normalizers, their fallbacks and the order they're attached # # The names on the left are arbitrary, but the order in which they occur is the # order in which they're applied. # # The names of the services on the right dictate the preference used (if unavailable # the second and third are applied as applicable). # image links color=color_transform deinterlace=avdeinterlace,deinterlace fieldorder=fieldorder crop=movit.crop,crop:1 rescaler=movit.resample,swscale,gtkrescale,rescale resizer=movit.resize,resize movitconvert=movit.convert imageconvert=avcolor_space,imageconvert # audio links audioconvert=audioconvert resampler=swresample,resample channels=audiochannels mlt-7.38.0/src/framework/metaschema.yaml000664 000000 000000 00000012615 15172202314 020146 0ustar00rootroot000000 000000 --- # A metadata schema in Kwalify: http://www.kuwata-lab.com/kwalify/ # Version: 7.1 type: map mapping: "schema_version": # This should match the version comment above type: float required: yes "LC_NUMERIC": # If not provided LC_NUMERIC=C is used, not the system's locale. type: str required: no "type": # A service type type: str required: yes enum: [consumer, filter, link, producer, transition] "identifier": # The same value used to register and create the service type: str required: yes unique: yes "title": # The UI can use this for a field label type: str "copyright": # Who owns the rights to the module and/or service? type: str "version": # The version of the service implementation type: text "license": # The software license for the service implementation type: str "language": # A 2 character ISO 639-1 language code type: str required: yes "url": # A hyperlink to a related website type: str "creator": # The name and/or e-mail address of the original author type: str "contributor": # The name and/or e-mail of all source code contributors type: seq sequence: - type: str "tags": # A set of categories, this might become an enum type: seq sequence: - type: str "description": # A slightly longer description than title type: str "icon": # A graphical representation of the effect type: map mapping: "filename": type: str "content-type": type: str "content-encoding": type: str "content": type: str "notes": # Details about the usage and/or implementation - can be long type: str "bugs": # A list of known problems that users can try to avoid type: seq sequence: - type: str # Can be a sentence or paragraph, preferably not a hyperlink "parameters": # A list of all of the options for the service type: seq sequence: - type: map mapping: "identifier": # The key that must be used to set the mlt_property type: str required: yes "type": # An mlt_property_type type: str enum: - boolean # 0 or 1; not 'true', 'false', 'yes', or 'no' strings at this time - color # 0xrrggbbaa | #rrggbb | #aarrggbb - float - geometry - group # for grouping parameters in a UI, this is not an mlt_property_type - integer - properties # for passing options to encapsulated services - rect # "X Y W H" or "X/Y:WxH" - string - time # time string values (clock, SMPTE) can be accepted in addition to frames "service-name": # for type: properties, a reference to another service type: str # format: type.service, e.g. transition.composite "title": # A UI can use this for a field label type: str "description": # A UI can use this for a tool tip or what's-this type: str "argument": # If this is also the service constructor argument. type: bool default: no "readonly": # If you set this property, it will be ignored type: bool default: no "required": # Is this property required? type: bool default: no "mutable": # The service will change behavior if this is set after # processing the first frame type: bool default: no "animation": # Does the property support the animation API for keyframes? type: bool default: no "widget": # A hint to the UI about how to let the user set this type: str enum: - checkbox - color - combo - curve - directory - fileopen - filesave - font - knob - listbox - dropdown # aka HTML select or GtkOptionMenu - point - radio - rectangle # for use with type: geometry - size - slider - spinner - text - textbox # multi-line - timecode "minimum": # For numeric types, the minimal value type: number "maximum": # For numeric types, the maximal value type: number "default": # The default value to be used in a UI type: scalar # If not specified, the UI might be able to display # this as blank "unit": # A UI can display this as a label after the widget (e.g. %) type: str "scale": # the number of digits after decimal point when type: float type: int "format": # A hint about a custom string encoding, possibly scanf "values": # A list of acceptable string values type: seq # A UI can allow something outside the list with # widget: combo or if "other" is in this sequence sequence: - type: scalar "group": # A UI can use this to group parameters together type: str "normalized_coordinates": # For types that represent coordinates, whether values are normalized to 0..1 type: bool default: no mlt-7.38.0/src/framework/mlt-framework.pc.in000664 000000 000000 00000000675 15172202314 020676 0ustar00rootroot000000 000000 prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ datadir=@CMAKE_INSTALL_FULL_DATADIR@ moduledir=${prefix}/@MLT_INSTALL_MODULE_DIR@ mltdatadir=${datadir}/@MLT_SUBDIR@ Name: mlt-framework Description: MLT multimedia framework Version: @MLT_VERSION@ Requires: Libs: -L${libdir} -lmlt-@MLT_VERSION_MAJOR@ Cflags: -I${includedir}/mlt-@MLT_VERSION_MAJOR@ mlt-7.38.0/src/framework/mlt.h000664 000000 000000 00000004271 15172202314 016117 0ustar00rootroot000000 000000 /** * \file mlt.h * \brief header file for lazy client and implementation code :-) * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_H #define MLT_H /** \mainpage MLT API Reference Documentation * \par * We recommend that you look in Classes * or Files. * \par * Additional documentation about MLT, in general, can be found on the * MLT website. */ /** \defgroup mlt MLT C Framework * \brief Core C library providing all MLT services, properties, and plugin infrastructure. * * Include this header to pull in all framework declarations. Plugin modules and * language bindings are built on top of these C interfaces. */ #ifdef __cplusplus extern "C" { #endif #include "mlt_animation.h" #include "mlt_audio.h" #include "mlt_cache.h" #include "mlt_chain.h" #include "mlt_consumer.h" #include "mlt_deque.h" #include "mlt_export.h" #include "mlt_factory.h" #include "mlt_field.h" #include "mlt_filter.h" #include "mlt_frame.h" #include "mlt_image.h" #include "mlt_link.h" #include "mlt_log.h" #include "mlt_multitrack.h" #include "mlt_parser.h" #include "mlt_playlist.h" #include "mlt_producer.h" #include "mlt_profile.h" #include "mlt_properties.h" #include "mlt_repository.h" #include "mlt_slices.h" #include "mlt_tokeniser.h" #include "mlt_tractor.h" #include "mlt_transition.h" #include "mlt_version.h" #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/framework/mlt.vers000664 000000 000000 00000040600 15172202314 016643 0ustar00rootroot000000 000000 MLT_0.8.8 { global: default_callback; mlt_audio_format_name; mlt_audio_format_size; mlt_cache_close; mlt_cache_get; mlt_cache_get_frame; mlt_cache_get_size; mlt_cache_init; mlt_cache_item_close; mlt_cache_item_data; mlt_cache_purge; mlt_cache_put; mlt_cache_put_frame; mlt_cache_set_size; mlt_consumer_close; mlt_consumer_connect; mlt_consumer_get_frame; mlt_consumer_init; mlt_consumer_is_stopped; mlt_consumer_new; mlt_consumer_position; mlt_consumer_properties; mlt_consumer_purge; mlt_consumer_put_frame; mlt_consumer_rt_frame; mlt_consumer_service; mlt_consumer_start; mlt_consumer_stop; mlt_consumer_stopped; mlt_deque_close; mlt_deque_count; mlt_deque_init; mlt_deque_insert; mlt_deque_peek; mlt_deque_peek_back; mlt_deque_peek_back_double; mlt_deque_peek_back_int; mlt_deque_peek_front; mlt_deque_peek_front_double; mlt_deque_peek_front_int; mlt_deque_pop_back; mlt_deque_pop_back_double; mlt_deque_pop_back_int; mlt_deque_pop_front; mlt_deque_pop_front_double; mlt_deque_pop_front_int; mlt_deque_push_back; mlt_deque_push_back_double; mlt_deque_push_back_int; mlt_deque_push_front; mlt_deque_push_front_double; mlt_deque_push_front_int; mlt_environment; mlt_environment_set; mlt_event_block; mlt_event_close; mlt_event_inc_ref; mlt_events_block; mlt_events_close_wait_for; mlt_events_disconnect; mlt_events_fire; mlt_events_init; mlt_events_listen; mlt_events_register; mlt_events_setup_wait_for; mlt_events_unblock; mlt_events_wait_for; mlt_event_unblock; mlt_factory_close; mlt_factory_consumer; mlt_factory_directory; mlt_factory_event_object; mlt_factory_filter; mlt_factory_init; mlt_factory_producer; mlt_factory_register_for_clean_up; mlt_factory_transition; mlt_field_close; mlt_field_disconnect_service; mlt_field_init; mlt_field_multitrack; mlt_field_new; mlt_field_plant_filter; mlt_field_plant_transition; mlt_field_properties; mlt_field_service; mlt_field_tractor; mlt_filter_close; mlt_filter_connect; mlt_filter_get_in; mlt_filter_get_length; mlt_filter_get_length2; mlt_filter_get_out; mlt_filter_get_position; mlt_filter_get_progress; mlt_filter_get_track; mlt_filter_init; mlt_filter_new; mlt_filter_process; mlt_filter_properties; mlt_filter_service; mlt_filter_set_in_and_out; mlt_frame_clone; mlt_frame_close; mlt_frame_get_aspect_ratio; mlt_frame_get_audio; mlt_frame_get_image; mlt_frame_get_original_producer; mlt_frame_get_position; mlt_frame_get_waveform; mlt_frame_init; mlt_frame_is_test_audio; mlt_frame_is_test_card; mlt_frame_original_position; mlt_frame_pop_audio; mlt_frame_pop_frame; mlt_frame_pop_get_image; mlt_frame_pop_service; mlt_frame_pop_service_int; mlt_frame_properties; mlt_frame_push_audio; mlt_frame_push_frame; mlt_frame_push_get_image; mlt_frame_push_service; mlt_frame_push_service_int; mlt_frame_replace_image; mlt_frame_service_stack; mlt_frame_set_alpha; mlt_frame_set_aspect_ratio; mlt_frame_set_audio; mlt_frame_set_image; mlt_frame_set_position; mlt_frame_unique_properties; mlt_frame_write_ppm; mlt_global_properties; mlt_image_format_name; mlt_image_format_size; mlt_log; mlt_log_get_level; mlt_log_set_callback; mlt_log_set_level; mlt_multitrack_clip; mlt_multitrack_close; mlt_multitrack_connect; mlt_multitrack_count; mlt_multitrack_init; mlt_multitrack_producer; mlt_multitrack_properties; mlt_multitrack_refresh; mlt_multitrack_service; mlt_multitrack_track; mlt_parser_close; mlt_parser_new; mlt_parser_properties; mlt_parser_start; mlt_playlist_append; mlt_playlist_append_io; mlt_playlist_blank; mlt_playlist_blanks_from; mlt_playlist_blank_time; mlt_playlist_clear; mlt_playlist_clip; mlt_playlist_clip_is_mix; mlt_playlist_clip_length; mlt_playlist_clip_start; mlt_playlist_close; mlt_playlist_consolidate_blanks; mlt_playlist_count; mlt_playlist_current; mlt_playlist_current_clip; mlt_playlist_get_clip; mlt_playlist_get_clip_at; mlt_playlist_get_clip_index_at; mlt_playlist_get_clip_info; mlt_playlist_init; mlt_playlist_insert; mlt_playlist_insert_at; mlt_playlist_insert_blank; mlt_playlist_is_blank; mlt_playlist_is_blank_at; mlt_playlist_join; mlt_playlist_mix; mlt_playlist_mix_add; mlt_playlist_move; mlt_playlist_new; mlt_playlist_pad_blanks; mlt_playlist_producer; mlt_playlist_properties; mlt_playlist_remove; mlt_playlist_remove_region; mlt_playlist_repeat_clip; mlt_playlist_replace_with_blank; mlt_playlist_resize_clip; mlt_playlist_service; mlt_playlist_split; mlt_playlist_split_at; mlt_pool_alloc; mlt_pool_close; mlt_pool_init; mlt_pool_purge; mlt_pool_realloc; mlt_pool_release; mlt_producer_attach; mlt_producer_clear; mlt_producer_close; mlt_producer_cut; mlt_producer_cut_parent; mlt_producer_detach; mlt_producer_filter; mlt_producer_frame; mlt_producer_frame_time; mlt_producer_get_fps; mlt_producer_get_in; mlt_producer_get_length; mlt_producer_get_length_time; mlt_producer_get_out; mlt_producer_get_playtime; mlt_producer_get_speed; mlt_producer_init; mlt_producer_is_blank; mlt_producer_is_cut; mlt_producer_is_mix; mlt_producer_new; mlt_producer_optimise; mlt_producer_position; mlt_producer_prepare_next; mlt_producer_properties; mlt_producer_seek; mlt_producer_seek_time; mlt_producer_service; mlt_producer_set_in_and_out; mlt_producer_set_speed; mlt_profile_clone; mlt_profile_close; mlt_profile_dar; mlt_profile_fps; mlt_profile_from_producer; mlt_profile_init; mlt_profile_list; mlt_profile_load_file; mlt_profile_load_properties; mlt_profile_load_string; mlt_profile_sar; mlt_properties_close; mlt_properties_count; mlt_properties_debug; mlt_properties_dec_ref; mlt_properties_dir_list; mlt_properties_dump; mlt_properties_get; mlt_properties_get_data; mlt_properties_get_data_at; mlt_properties_get_double; mlt_properties_get_int; mlt_properties_get_int64; mlt_properties_get_lcnumeric; mlt_properties_get_name; mlt_properties_get_position; mlt_properties_get_time; mlt_properties_get_value; mlt_properties_inc_ref; mlt_properties_inherit; mlt_properties_init; mlt_properties_is_sequence; mlt_properties_load; mlt_properties_lock; mlt_properties_mirror; mlt_properties_new; mlt_properties_parse; mlt_properties_parse_yaml; mlt_properties_pass; mlt_properties_pass_list; mlt_properties_pass_property; mlt_properties_preset; mlt_properties_ref_count; mlt_properties_rename; mlt_properties_save; mlt_properties_serialise_yaml; mlt_properties_set; mlt_properties_set_data; mlt_properties_set_double; mlt_properties_set_int; mlt_properties_set_int64; mlt_properties_set_lcnumeric; mlt_properties_set_or_default; mlt_properties_set_position; mlt_properties_unlock; mlt_property_close; mlt_property_get_data; mlt_property_get_double; mlt_property_get_int; mlt_property_get_int64; mlt_property_get_position; mlt_property_get_string; mlt_property_get_string_l; mlt_property_get_time; mlt_property_init; mlt_property_pass; mlt_property_set_data; mlt_property_set_double; mlt_property_set_int; mlt_property_set_int64; mlt_property_set_position; mlt_property_set_string; mlt_repository_close; mlt_repository_consumers; mlt_repository_create; mlt_repository_filters; mlt_repository_init; mlt_repository_languages; mlt_repository_metadata; mlt_repository_presets; mlt_repository_producers; mlt_repository_register; mlt_repository_register_metadata; mlt_repository_transitions; mlt_sdl_mutex; mlt_service_apply_filters; mlt_service_attach; mlt_service_cache_get; mlt_service_cache_get_size; mlt_service_cache_purge; mlt_service_cache_put; mlt_service_cache_set_size; mlt_service_close; mlt_service_connect_producer; mlt_service_consumer; mlt_service_detach; mlt_service_filter; mlt_service_get_frame; mlt_service_get_producer; mlt_service_identify; mlt_service_init; mlt_service_lock; mlt_service_producer; mlt_service_profile; mlt_service_properties; mlt_service_set_profile; mlt_service_unlock; mlt_tokeniser_close; mlt_tokeniser_count; mlt_tokeniser_get_input; mlt_tokeniser_get_string; mlt_tokeniser_init; mlt_tokeniser_parse_new; mlt_tractor_close; mlt_tractor_connect; mlt_tractor_field; mlt_tractor_get_track; mlt_tractor_init; mlt_tractor_multitrack; mlt_tractor_new; mlt_tractor_producer; mlt_tractor_properties; mlt_tractor_refresh; mlt_tractor_service; mlt_tractor_set_track; mlt_transition_close; mlt_transition_connect; mlt_transition_get_a_track; mlt_transition_get_b_track; mlt_transition_get_in; mlt_transition_get_length; mlt_transition_get_out; mlt_transition_get_position; mlt_transition_get_progress; mlt_transition_get_progress_delta; mlt_transition_init; mlt_transition_new; mlt_transition_process; mlt_transition_properties; mlt_transition_service; mlt_transition_set_in_and_out; mlt_version_get_int; mlt_version_get_major; mlt_version_get_minor; mlt_version_get_revision; mlt_version_get_string; mlt_vlog; local: *; }; MLT_0.9.0 { global: mlt_animation_new; mlt_animation_parse; mlt_animation_refresh; mlt_animation_get_length; mlt_animation_set_length; mlt_animation_parse_item; mlt_animation_get_item; mlt_animation_insert; mlt_animation_remove; mlt_animation_interpolate; mlt_animation_next_key; mlt_animation_prev_key; mlt_animation_serialize_cut; mlt_animation_serialize; mlt_animation_close; mlt_properties_get_color; mlt_properties_set_color; mlt_properties_anim_get; mlt_properties_anim_set; mlt_properties_anim_get_int; mlt_properties_anim_set_int; mlt_properties_anim_get_double; mlt_properties_anim_set_double; mlt_properties_get_animation; mlt_properties_set_rect; mlt_properties_get_rect; mlt_properties_anim_set_rect; mlt_properties_anim_get_rect; mlt_property_interpolate; mlt_property_anim_get_double; mlt_property_anim_get_int; mlt_property_anim_get_string; mlt_property_anim_set_double; mlt_property_anim_set_int; mlt_property_anim_set_string; mlt_property_get_animation; mlt_property_set_rect; mlt_property_get_rect; mlt_property_anim_set_rect; mlt_property_anim_get_rect; mlt_service_filter_count; mlt_service_move_filter; } MLT_0.8.8; MLT_0.9.2 { global: mlt_playlist_mix_in; mlt_playlist_mix_out; mlt_properties_frames_to_time; mlt_properties_time_to_frames; mlt_properties_from_utf8; } MLT_0.9.0; MLT_0.9.4 { global: mlt_pool_stat; mlt_frame_get_alpha; } MLT_0.9.2; MLT_0.9.8 { global: mlt_service_disconnect_producer; mlt_multitrack_disconnect; mlt_tractor_remove_track; mlt_service_insert_producer; mlt_multitrack_insert; mlt_tractor_insert_track; mlt_transition_set_tracks; mlt_animation_key_count; mlt_animation_key_get; } MLT_0.9.4; MLT_0.9.10 { global: mlt_factory_repository; } MLT_0.9.8; MLT_6.6.0 { global: mlt_image_format_planes; mlt_image_format_id; mlt_slices_count_normal; mlt_slices_count_rr; mlt_slices_count_fifo; mlt_slices_run_normal; mlt_slices_run_rr; mlt_slices_run_fifo; mlt_log_timings_now; mlt_service_disconnect_all_producers; } MLT_0.9.10; MLT_6.8.0 { global: mlt_animation_key_set_type; mlt_animation_key_set_frame; } MLT_6.6.0; MLT_6.10.0 { global: mlt_animation_serialize_cut_tf; mlt_animation_serialize_tf; mlt_property_clear; mlt_property_get_string_tf; mlt_property_get_string_l_tf; mlt_properties_clear; mlt_properties_get_value_tf; } MLT_6.8.0; MLT_6.12.0 { global: mlt_profile_lumas_dir; } MLT_6.10.0; MLT_6.14.0 { global: mlt_frame_get_unique_properties; mlt_playlist_reorder; mlt_producer_set_creation_time; mlt_producer_get_creation_time; } MLT_6.12.0; MLT_6.18.0 { global: mlt_luma_map_init; mlt_luma_map_new; mlt_luma_map_render; mlt_luma_map_from_pgm; mlt_luma_map_from_yuv422; } MLT_6.14.0; MLT_6.20.0 { global: mlt_profile_scale_width; mlt_profile_scale_height; mlt_properties_set_string; } MLT_6.18.0; MLT_6.22.0 { global: mlt_property_is_clear; mlt_properties_exists; mlt_audio_new; mlt_audio_close; mlt_audio_set_values; mlt_audio_get_values; mlt_audio_alloc_data; mlt_audio_calculate_size; mlt_audio_plane_count; mlt_audio_plane_size; mlt_audio_get_planes; mlt_audio_shrink; mlt_audio_reverse; mlt_audio_copy; mlt_audio_calculate_frame_samples; mlt_audio_calculate_samples_to_position; mlt_audio_channel_layout_name; mlt_audio_channel_layout_id; mlt_audio_channel_layout_channels; mlt_audio_channel_layout_default; } MLT_6.20.0; MLT_7.0.0 { global: mlt_factory_link; mlt_chain_init; mlt_chain_set_source; mlt_chain_get_source; mlt_chain_attach; mlt_chain_detach; mlt_chain_link_count; mlt_chain_move_link; mlt_chain_link; mlt_chain_close; mlt_link_init; mlt_link_connect_next; mlt_link_close; mlt_repository_links; mlt_animation_shift_frames; mlt_animation_get_string; mlt_image_new; mlt_image_close; mlt_image_set_values; mlt_image_get_values; mlt_image_alloc_data; mlt_image_alloc_alpha; mlt_image_calculate_size; mlt_image_fill_black; mlt_image_fill_opaque; mlt_audio_silence; mlt_event_data_none; mlt_event_data_from_int; mlt_event_data_to_int; mlt_event_data_from_string; mlt_event_data_to_string; mlt_event_data_from_frame; mlt_event_data_to_frame; mlt_event_data_from_object; mlt_event_data_to_object; } MLT_6.22.0; MLT_7.1.0 { global: mlt_property_set_properties; mlt_property_get_properties; mlt_properties_set_properties; mlt_properties_get_properties; mlt_properties_get_properties_at; } MLT_7.0.0; MLT_7.4.0 { global: mlt_property_is_anim; mlt_properties_is_anim; } MLT_7.1.0; MLT_7.6.0 { global: mlt_properties_copy; mlt_slices_size_slice; } MLT_7.4.0; MLT_7.8.0 { global: mlt_frame_get_alpha_size; } MLT_7.6.0; MLT_7.10.0 { global: mlt_image_fill_checkerboard; mlt_image_fill_white; mlt_image_rgba_opaque; } MLT_7.8.0; MLT_7.12.0 { global: mlt_property_set_color; mlt_property_get_color; mlt_property_anim_set_color; mlt_property_anim_get_color; mlt_properties_anim_set_color; mlt_properties_anim_get_color; } MLT_7.10.0; MLT_7.14.0 { global: mlt_producer_probe; mlt_chain_attach_normalizers; } MLT_7.12.0; MLT_7.16.0 { global: mlt_deinterlacer_name; mlt_deinterlacer_id; mlt_link_filter_init; mlt_link_filter_metadata; mlt_cache_put_frame_audio; mlt_cache_put_frame_image; mlt_frame_clone_audio; mlt_frame_clone_image; } MLT_7.14.0; MLT_7.18.0 { global: mlt_audio_free_data; } MLT_7.16.0; MLT_7.22.0 { global: mlt_property_is_color; mlt_property_is_numeric; mlt_property_is_rect; } MLT_7.18.0; MLT_7.30.0 { global: mlt_image_full_range; } MLT_7.22.0; MLT_7.32.0 { global: mlt_service_set_consumer; } MLT_7.30.0; MLT_7.34.0 { global: mlt_image_is_opaque; mlt_image_color_trc_name; mlt_image_color_trc_id; mlt_image_colorspace_name; mlt_image_colorspace_id; mlt_image_color_pri_name; mlt_image_color_pri_id; } MLT_7.32.0; MLT_7.36.0 { global: mlt_image_default_colorspace; mlt_image_default_trc; mlt_image_default_primaries; mlt_color_convert_trc; mlt_profile_is_valid; } MLT_7.34.0; mlt-7.38.0/src/framework/mlt_animation.c000664 000000 000000 00000147172 15172202314 020161 0ustar00rootroot000000 000000 /** * \file mlt_animation.c * \brief Property Animation class definition * \see mlt_animation_s * * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_animation.h" #include "mlt_factory.h" #include "mlt_profile.h" #include "mlt_properties.h" #include "mlt_tokeniser.h" #include #include #include #include #include /** \brief animation list node pointer */ typedef struct animation_node_s *animation_node; /** \brief private animation list node */ struct animation_node_s { struct mlt_animation_item_s item; animation_node next, prev; }; /** \brief Property Animation class * * This is the animation engine for a Property object. It is dependent upon * the mlt_property API and used by the various mlt_property_anim_* functions. */ struct mlt_animation_s { char *data; /**< the string representing the animation */ int length; /**< the maximum number of frames to use when interpreting negative keyframe positions */ double fps; /**< framerate to use when converting time clock strings to frame units */ mlt_locale_t locale; /**< pointer to a locale to use when converting strings to numeric values */ animation_node nodes; /**< a linked list of keyframes (and possibly non-keyframe values) */ }; /** \brief Keyframe type to string mapping * * Used for serialization and deserialization of keyframe types */ struct { mlt_keyframe_type t; const char *s; } keyframe_type_map[] = { // Map keyframe type to any single character except numeric values. {mlt_keyframe_discrete, "|"}, {mlt_keyframe_discrete, "!"}, {mlt_keyframe_linear, ""}, {mlt_keyframe_smooth, "~"}, {mlt_keyframe_smooth_loose, "~"}, {mlt_keyframe_smooth_natural, "$"}, {mlt_keyframe_smooth_tight, "-"}, {mlt_keyframe_sinusoidal_in, "a"}, {mlt_keyframe_sinusoidal_out, "b"}, {mlt_keyframe_sinusoidal_in_out, "c"}, {mlt_keyframe_quadratic_in, "d"}, {mlt_keyframe_quadratic_out, "e"}, {mlt_keyframe_quadratic_in_out, "f"}, {mlt_keyframe_cubic_in, "g"}, {mlt_keyframe_cubic_out, "h"}, {mlt_keyframe_cubic_in_out, "i"}, {mlt_keyframe_quartic_in, "j"}, {mlt_keyframe_quartic_out, "k"}, {mlt_keyframe_quartic_in_out, "l"}, {mlt_keyframe_quintic_in, "m"}, {mlt_keyframe_quintic_out, "n"}, {mlt_keyframe_quintic_in_out, "o"}, {mlt_keyframe_exponential_in, "p"}, {mlt_keyframe_exponential_out, "q"}, {mlt_keyframe_exponential_in_out, "r"}, {mlt_keyframe_circular_in, "s"}, {mlt_keyframe_circular_out, "t"}, {mlt_keyframe_circular_in_out, "u"}, {mlt_keyframe_back_in, "v"}, {mlt_keyframe_back_out, "w"}, {mlt_keyframe_back_in_out, "x"}, {mlt_keyframe_elastic_in, "y"}, {mlt_keyframe_elastic_out, "z"}, {mlt_keyframe_elastic_in_out, "A"}, {mlt_keyframe_bounce_in, "B"}, {mlt_keyframe_bounce_out, "C"}, {mlt_keyframe_bounce_in_out, "D"}, }; static void mlt_animation_clear_string(mlt_animation self); static int interpolate_item(mlt_animation_item item, mlt_animation_item p[], double fps, mlt_locale_t locale); static const char *keyframe_type_to_str(mlt_keyframe_type t) { int map_count = sizeof(keyframe_type_map) / sizeof(*keyframe_type_map); for (int i = 0; i < map_count; i++) { if (keyframe_type_map[i].t == t) { return keyframe_type_map[i].s; } } return ""; } static mlt_keyframe_type str_to_keyframe_type(const char *s) { if (s && (s[0] < '0' || s[0] > '9')) { int map_count = sizeof(keyframe_type_map) / sizeof(*keyframe_type_map); for (int i = 0; i < map_count; i++) { if (!strncmp(s, keyframe_type_map[i].s, 1)) { return keyframe_type_map[i].t; } } } return mlt_keyframe_linear; } /** Create a new animation object. * * \public \memberof mlt_animation_s * \return an animation object */ mlt_animation mlt_animation_new() { mlt_animation self = calloc(1, sizeof(*self)); return self; } /** Re-interpolate non-keyframe nodes after a series of insertions or removals. * * \public \memberof mlt_animation_s * \param self an animation */ void mlt_animation_interpolate(mlt_animation self) { // Parse all items to ensure non-keyframes are calculated correctly. if (self && self->nodes) { animation_node current = self->nodes; while (current) { if (!current->item.is_key) { mlt_animation_item points[4]; animation_node prev = current->prev; animation_node next = current->next; while (prev && !prev->item.is_key) prev = prev->prev; while (next && !next->item.is_key) next = next->next; if (!prev) { current->item.is_key = 1; prev = current; } if (!next) { next = current; } points[0] = prev->prev ? &prev->prev->item : &prev->item; points[1] = &prev->item; points[2] = &next->item; points[3] = next->next ? &next->next->item : &next->item; interpolate_item(¤t->item, points, self->fps, self->locale); } // Move to the next item current = current->next; } } } /** Remove a node from the linked list. * * \private \memberof mlt_animation_s * \param self an animation * \param node the node to remove * \return false */ static int mlt_animation_drop(mlt_animation self, animation_node node) { if (node == self->nodes) { self->nodes = node->next; if (self->nodes) { self->nodes->prev = NULL; self->nodes->item.is_key = 1; } } else if (node->next && node->prev) { node->prev->next = node->next; node->next->prev = node->prev; } else if (node->next) { node->next->prev = node->prev; } else if (node->prev) { node->prev->next = node->next; } mlt_property_close(node->item.property); free(node); return 0; } /** Reset an animation and free all strings and properties. * * \private \memberof mlt_animation_s * \param self an animation */ static void mlt_animation_clean(mlt_animation self) { if (!self) return; free(self->data); self->data = NULL; while (self->nodes) mlt_animation_drop(self, self->nodes); } /** Parse a string representing an animation. * * A semicolon is the delimiter between keyframe=value items in the string. * \public \memberof mlt_animation_s * \param self an animation * \param data the string representing an animation * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param fps the framerate to use when evaluating time strings * \param locale the locale to use when converting strings to numbers * \return true if there was an error */ int mlt_animation_parse( mlt_animation self, const char *data, int length, double fps, mlt_locale_t locale) { if (!self) return 1; int error = 0; int i = 0; struct mlt_animation_item_s item; mlt_tokeniser tokens = mlt_tokeniser_init(); // Clean the existing geometry mlt_animation_clean(self); // Update the info on the data if (data) self->data = strdup(data); self->length = length; self->fps = fps; self->locale = locale; item.property = mlt_property_init(); item.frame = item.is_key = 0; item.keyframe_type = mlt_keyframe_discrete; // Tokenise if (data) mlt_tokeniser_parse_new(tokens, (char *) data, ";"); // Iterate through each token for (i = 0; i < mlt_tokeniser_count(tokens); i++) { char *value = mlt_tokeniser_get_string(tokens, i); // If no data in keyframe, drop it (trailing semicolon) if (!value || !strcmp(value, "")) continue; // Reset item item.frame = item.is_key = 0; // Do not parse a string enclosed entirely within quotes as animation. // (mlt_tokeniser already skips splitting on delimiter inside quotes). if (value[0] == '\"' && value[strlen(value) - 1] == '\"') { // Remove the quotes. value[strlen(value) - 1] = '\0'; mlt_property_set_string(item.property, &value[1]); } else { // Now parse the item mlt_animation_parse_item(self, &item, value); } // Now insert into place mlt_animation_insert(self, &item); } mlt_animation_interpolate(self); // Cleanup mlt_tokeniser_close(tokens); mlt_property_close(item.property); return error; } /** Conditionally refresh the animation if it is modified. * * \public \memberof mlt_animation_s * \param self an animation * \param data the string representing an animation * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return true if there was an error */ int mlt_animation_refresh(mlt_animation self, const char *data, int length) { if (!self) return 1; if ((length != self->length) || (data && (!self->data || strcmp(data, self->data)))) return mlt_animation_parse(self, data, length, self->fps, self->locale); return 0; } /** Get the length of the animation. * * If the animation was initialized with a zero or negative value, then this * gets the maximum frame number from animation's list of nodes. * \public \memberof mlt_animation_s * \param self an animation * \return the number of frames */ int mlt_animation_get_length(mlt_animation self) { int length = 0; if (self) { if (self->length > 0) { length = self->length; } else if (self->nodes) { animation_node node = self->nodes; while (node) { if (node->item.frame > length) length = node->item.frame; node = node->next; } } } return length; } /** Set the length of the animation. * * The length is used for interpreting negative keyframe positions as relative * to the length. It is also used when serializing an animation as a string. * \public \memberof mlt_animation_s * \param self an animation * \param length the length of the animation in frame units */ void mlt_animation_set_length(mlt_animation self, int length) { if (self) { self->length = length; mlt_animation_clear_string(self); } } /** Parse a string representing an animation keyframe=value. * * This function does not affect the animation itself! But it will use some state * of the animation for the parsing (e.g. fps, locale). * It parses into a mlt_animation_item that you provide. * \p item->frame should be specified if the string does not have an equal sign and time field. * If an exclamation point (!) or vertical bar (|) character precedes the equal sign, then * the keyframe interpolation is set to discrete. If a tilde (~) precedes the equal sign, * then the keyframe interpolation is set to smooth (spline). * * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item * \param value the string representation of a single keyframe item (e.g. "50~=255") * \return true if there was an error */ int mlt_animation_parse_item(mlt_animation self, mlt_animation_item item, const char *value) { int error = 0; if (self && item && value && strcmp(value, "")) { // Determine if a position has been specified if (strchr(value, '=')) { // Parse an absolute time value. // Null terminate the string at the equal sign to prevent interpreting // a colon in the part to the right of the equal sign as indicative of a // a time value string. char *s = strdup(value); char *p = strchr(s, '='); p[0] = '\0'; mlt_property_set_string(item->property, s); item->frame = mlt_property_get_int(item->property, self->fps, self->locale); free(s); // The character preceding the equal sign indicates interpolation method. p = strchr(value, '=') - 1; item->keyframe_type = str_to_keyframe_type(p); value = &p[2]; // Check if the value is quoted. p = &p[2]; if (p && p[0] == '\"' && p[strlen(p) - 1] == '\"') { // Remove the quotes. p[strlen(p) - 1] = '\0'; value = &p[1]; } } // Special case - frame < 0 if (item->frame < 0) item->frame += mlt_animation_get_length(self); // Set remainder of string as item value. mlt_property_set_string(item->property, value); item->is_key = 1; } else { error = 1; } return error; } /** Load an animation item for an absolute position. * * This performs interpolation if there is no keyframe at the \p position. * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item that will be filled in * \param position the frame number for the point in time * \return true if there was an error */ int mlt_animation_get_item(mlt_animation self, mlt_animation_item item, int position) { if (!self || !item) return 1; int error = 0; // Need to find the nearest keyframe to the position specified animation_node node = self->nodes; // Iterate through the keyframes until we reach last or have while (node && node->next && position >= node->next->item.frame) node = node->next; if (node) { item->keyframe_type = node->item.keyframe_type; // Position is before the first keyframe. if (position < node->item.frame) { item->is_key = 0; if (item->property) mlt_property_pass(item->property, node->item.property); } // Item exists. else if (position == node->item.frame) { item->is_key = node->item.is_key; if (item->property) mlt_property_pass(item->property, node->item.property); } // Position is after the last keyframe. else if (!node->next) { item->is_key = 0; if (item->property) mlt_property_pass(item->property, node->item.property); } // Interpolation needed. else { if (item->property) { mlt_animation_item points[4]; points[0] = node->prev ? &node->prev->item : &node->item; points[1] = &node->item; points[2] = &node->next->item; points[3] = node->next->next ? &node->next->next->item : &node->next->item; item->frame = position; interpolate_item(item, points, self->fps, self->locale); } item->is_key = 0; } } else { item->frame = item->is_key = 0; error = 1; } item->frame = position; return error; } /** Insert an animation item. * * \public \memberof mlt_animation_s * \param self an animation * \param item an animation item * \return true if there was an error * \see mlt_animation_parse_item */ int mlt_animation_insert(mlt_animation self, mlt_animation_item item) { if (!self || !item) return 1; int error = 0; animation_node node = calloc(1, sizeof(*node)); node->item.frame = item->frame; node->item.is_key = 1; node->item.keyframe_type = item->keyframe_type; node->item.property = mlt_property_init(); if (item->property) mlt_property_pass(node->item.property, item->property); // Determine if we need to insert or append to the list, or if it's a new list if (self->nodes) { // Get the first item animation_node current = self->nodes; // Locate an existing nearby item while (current->next && item->frame > current->item.frame) current = current->next; if (item->frame < current->item.frame) { if (current == self->nodes) self->nodes = node; if (current->prev) current->prev->next = node; node->next = current; node->prev = current->prev; current->prev = node; } else if (item->frame > current->item.frame) { if (current->next) current->next->prev = node; node->next = current->next; node->prev = current; current->next = node; } else { // Update matching node. current->item.frame = item->frame; current->item.is_key = 1; current->item.keyframe_type = item->keyframe_type; mlt_property_close(current->item.property); current->item.property = node->item.property; free(node); } } else { // Set the first item self->nodes = node; } mlt_animation_clear_string(self); return error; } /** Remove the keyframe at the specified position. * * \public \memberof mlt_animation_s * \param self an animation * \param position the frame number of the animation node to remove * \return true if there was an error */ int mlt_animation_remove(mlt_animation self, int position) { if (!self) return 1; int error = 1; animation_node node = self->nodes; while (node && position != node->item.frame) node = node->next; if (node && position == node->item.frame) error = mlt_animation_drop(self, node); mlt_animation_clear_string(self); return error; } /** Get the keyfame at the position or the next following. * * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item which will be updated * \param position the frame number at which to start looking for the next animation node * \return true if there was an error */ int mlt_animation_next_key(mlt_animation self, mlt_animation_item item, int position) { if (!self || !item) return 1; animation_node node = self->nodes; while (node && position > node->item.frame) node = node->next; if (node) { item->frame = node->item.frame; item->is_key = node->item.is_key; item->keyframe_type = node->item.keyframe_type; if (item->property) mlt_property_pass(item->property, node->item.property); } return (node == NULL); } /** Get the keyfame at the position or the next preceding. * * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item which will be updated * \param position the frame number at which to start looking for the previous animation node * \return true if there was an error */ int mlt_animation_prev_key(mlt_animation self, mlt_animation_item item, int position) { if (!self || !item) return 1; animation_node node = self->nodes; while (node && node->next && position >= node->next->item.frame) node = node->next; if (position < node->item.frame) node = NULL; if (node) { item->frame = node->item.frame; item->is_key = node->item.is_key; item->keyframe_type = node->item.keyframe_type; if (item->property) mlt_property_pass(item->property, node->item.property); } return (node == NULL); } /** Serialize a cut of the animation (with time format). * * The caller is responsible for free-ing the returned string. * \public \memberof mlt_animation_s * \param self an animation * \param in the frame at which to start serializing animation nodes * \param out the frame at which to stop serializing nodes * \param time_format the time format to use for the key frames * \return a string representing the animation */ char *mlt_animation_serialize_cut_tf(mlt_animation self, int in, int out, mlt_time_format time_format) { struct mlt_animation_item_s item; char *ret = calloc(1, 1000); size_t used = 0; size_t size = 1000; mlt_property time_property = mlt_property_init(); item.property = mlt_property_init(); item.frame = item.is_key = 0; item.keyframe_type = mlt_keyframe_discrete; if (in == -1) in = 0; if (out == -1) out = mlt_animation_get_length(self); if (self && ret) { item.frame = in; while (1) { size_t item_len = 0; // If it's the first frame, then it's not necessarily a key if (item.frame == in) { if (mlt_animation_get_item(self, &item, item.frame)) break; // If the first keyframe is larger than the current position // then do nothing here if (self->nodes->item.frame > item.frame) { item.frame++; continue; } // To ensure correct seeding item.is_key = 1; } // Typically, we move from keyframe to keyframe else if (item.frame <= out) { if (mlt_animation_next_key(self, &item, item.frame)) break; // Special case - crop at the out point if (item.frame > out) { mlt_animation_get_item(self, &item, out); // To ensure correct seeding item.is_key = 1; } } // We've handled the last keyframe else { break; } // Determine length of string to be appended. item_len += 100; const char *value = mlt_property_get_string_l(item.property, self->locale); if (item.is_key && value) { item_len += strlen(value); // Check if the value must be quoted. if (strchr(value, ';') || strchr(value, '=')) item_len += 2; } // Reallocate return string to be long enough. while (used + item_len + 2 > size) // +2 for ';' and NULL { size += 1000; char *tmp = realloc(ret, size); if (!tmp) { free(ret); mlt_property_close(item.property); mlt_property_close(time_property); return NULL; } ret = tmp; } // Append item delimiter (;) if needed. if (ret && used > 0) { used++; strcat(ret, ";"); } if (ret) { // Append keyframe time and keyframe/value delimiter (=). const char *s = keyframe_type_to_str(item.keyframe_type); if (time_property && self->fps > 0.0) { mlt_property_set_int(time_property, item.frame - in); const char *time = mlt_property_get_time(time_property, time_format, self->fps, self->locale); sprintf(ret + used, "%s%s=", time, s); } else { sprintf(ret + used, "%d%s=", item.frame - in, s); } used = strlen(ret); // Append item value. if (item.is_key && value) { // Check if the value must be quoted. if (strchr(value, ';') || strchr(value, '=')) sprintf(ret + used, "\"%s\"", value); else strcat(ret, value); } used = strlen(ret); } item.frame++; } } mlt_property_close(item.property); mlt_property_close(time_property); return ret; } static mlt_time_format default_time_format() { const char *e = getenv("MLT_ANIMATION_TIME_FORMAT"); return e ? strtol(e, NULL, 10) : mlt_time_frames; } /** Serialize a cut of the animation. * * This version outputs the key frames' position as a frame number. * The caller is responsible for free-ing the returned string. * \public \memberof mlt_animation_s * \param self an animation * \param in the frame at which to start serializing animation nodes * \param out the frame at which to stop serializing nodes * \return a string representing the animation */ char *mlt_animation_serialize_cut(mlt_animation self, int in, int out) { return mlt_animation_serialize_cut_tf(self, in, out, default_time_format()); } /** Serialize the animation (with time format). * * The caller is responsible for free-ing the returned string. * \public \memberof mlt_animation_s * \param self an animation * \param time_format the time format to use for the key frames * \return a string representing the animation */ char *mlt_animation_serialize_tf(mlt_animation self, mlt_time_format time_format) { char *ret = mlt_animation_serialize_cut_tf(self, -1, -1, time_format); if (self && ret) { free(self->data); self->data = ret; ret = strdup(ret); } return ret; } /** Serialize the animation. * * This version outputs the key frames' position as a frame number. * The caller is responsible for free-ing the returned string. * \public \memberof mlt_animation_s * \param self an animation * \return a string representing the animation */ char *mlt_animation_serialize(mlt_animation self) { return mlt_animation_serialize_tf(self, default_time_format()); } /** Get the number of keyframes. * * \public \memberof mlt_animation_s * \param self an animation * \return the number of keyframes or -1 on error */ int mlt_animation_key_count(mlt_animation self) { int count = -1; if (self) { animation_node node = self->nodes; for (count = 0; node; ++count) node = node->next; } return count; } /** Get an animation item for the N-th keyframe. * * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item that will be filled in * \param index the N-th keyframe (0 based) in this animation * \return true if there was an error */ int mlt_animation_key_get(mlt_animation self, mlt_animation_item item, int index) { if (!self || !item) return 1; int error = 0; animation_node node = self->nodes; // Iterate through the keyframes. int i = index; while (i-- && node) node = node->next; if (node) { item->is_key = node->item.is_key; item->frame = node->item.frame; item->keyframe_type = node->item.keyframe_type; if (item->property) mlt_property_pass(item->property, node->item.property); } else { item->frame = item->is_key = 0; error = 1; } return error; } /** Close the animation and deallocate all of its resources. * * \public \memberof mlt_animation_s * \param self the animation to destroy */ void mlt_animation_close(mlt_animation self) { if (self) { mlt_animation_clean(self); free(self); } } /** Change the interpolation for the N-th keyframe. * * \public \memberof mlt_animation_s * \param self an animation * \param index the N-th keyframe (0 based) in this animation * \param type the method of interpolation for this key frame * \return true if there was an error */ int mlt_animation_key_set_type(mlt_animation self, int index, mlt_keyframe_type type) { if (!self) return 1; int error = 0; animation_node node = self->nodes; // Iterate through the keyframes. int i = index; while (i-- && node) node = node->next; if (node) { node->item.keyframe_type = type; mlt_animation_interpolate(self); mlt_animation_clear_string(self); } else { error = 1; } return error; } /** Change the frame number for the N-th keyframe. * * \public \memberof mlt_animation_s * \param self an animation * \param index the N-th keyframe (0 based) in this animation * \param frame the position of this keyframe in frame units * \return true if there was an error */ int mlt_animation_key_set_frame(mlt_animation self, int index, int frame) { if (!self) return 1; int error = 0; animation_node node = self->nodes; // Iterate through the keyframes. int i = index; while (i-- && node) node = node->next; if (node) { node->item.frame = frame; mlt_animation_interpolate(self); mlt_animation_clear_string(self); } else { error = 1; } return error; } /** Shift the frame value for all nodes. * * \public \memberof mlt_animation_s * \param self an animation * \param shift the value to add to all frame values */ void mlt_animation_shift_frames(mlt_animation self, int shift) { animation_node node = self->nodes; while (node) { node->item.frame += shift; node = node->next; } mlt_animation_clear_string(self); mlt_animation_interpolate(self); } /** Get the cached serialization string. * * This can be used to determine if the animation has been modified because the * string is cleared whenever the animation is changed. * \public \memberof mlt_animation_s * \param self an animation * \return the cached serialization string */ const char *mlt_animation_get_string(mlt_animation self) { if (!self) return NULL; return self->data; } /** Clear the cached serialization string. * * \private \memberof mlt_animation_s * \param self an animation */ void mlt_animation_clear_string(mlt_animation self) { if (!self) return; free(self->data); self->data = NULL; } /** A linear interpolation function. * * \private \memberof mlt_animation_s */ static inline double linear_interpolate(double y1, double y2, double t) { return y1 + (y2 - y1) * t; } /** Calculate the distance between two points. * * \private \memberof mlt_animation_s */ static inline double distance(double x0, double y0, double x1, double y1) { return sqrt(pow(x1 - x0, 2) + pow(y1 - y0, 2)); } /** A Catmull–Rom interpolation function. * * As described here: * https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline * And further reduced here with tension added: * https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html * * This implementation supports the alpha value which can be set to 0.5 to result in * centripetal Catmull–Rom splines. Centripetal Catmull–Rom splines are guaranteed * to not have any cusps or loops. These are not desirable because they result in the * value reversing direction when interpolation from one point to the next. * * To use this function for animation item interpolation, provide 4 points: two points preceding t * and two points following t. Use the item frame number as the x and the item value as y for each * point. t should represent the fractional progress between point 1 and point 2. * * If fewer than 2 points are available, then duplicate the first and/or last points as necessary to * meet the requirement for 4 points. * \private \memberof mlt_animation_s * \param x0 the x coordinate of the first control point * \param y0 the y coordinate of the first control point * \param x1 the x coordinate of the second control point (start of interpolated segment) * \param y1 the y coordinate of the second control point (start of interpolated segment) * \param x2 the x coordinate of the third control point (end of interpolated segment) * \param y2 the y coordinate of the third control point (end of interpolated segment) * \param x3 the x coordinate of the fourth control point * \param y3 the y coordinate of the fourth control point * \param t the fractional progress between x1,y1 and x2,y2 * \param alpha * 0.0 for the uniform spline * 0.5 for the centripetal spline (no cusps) * 1.0 for the chordal spline. * \param tension * 1.0 results in the most natural slope at x1,y1 and x2,y2 * 0.0 results in a horizontal tangent at x1,y1 and x2,y2 (slope of 0). * -1.0 results in the most natural slope at x1,y1 and x2,y2 unless x1 or x2 represents a peak. * In the case of peaks, a horizontal tangent will be used to avoid overshoot. */ static inline double catmull_rom_interpolate(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double t, double alpha, double tension) { // Correct first and last values. // If points are duplicated (e.g. for the first and last segments) assume the duplicated point // is far away to create a horizontal segment. if (x0 == x1) { x0 -= 10000; } if (x3 == x2) { x3 += 10000; } double m1 = 0; double m2 = 0; double t12 = pow(distance(x1, y1, x2, y2), alpha); if (tension > 0.0 || (y1 < y0 && y1 > y2) || (y1 > y0 && y1 < y2)) { double t01 = pow(distance(x0, y0, x1, y1), alpha); m1 = fabs(tension) * (y2 - y1 + t12 * ((y1 - y0) / t01 - (y2 - y0) / (t01 + t12))); } if (tension > 0.0 || (y2 < y1 && y2 > y3) || (y2 > y1 && y2 < y3)) { double t23 = pow(distance(x2, y2, x3, y3), alpha); m2 = fabs(tension) * (y2 - y1 + t12 * ((y3 - y2) / t23 - (y3 - y1) / (t12 + t23))); } double a = 2.0 * (y1 - y2) + m1 + m2; double b = -3.0 * (y1 - y2) - m1 - m1 - m2; double c = m1; double d = y1; return a * t * t * t + b * t * t + c * t + d; } /** Easing functions * * The following easing functions are based on Robert Penner's Easing Functions * http://robertpenner.com/easing/ */ typedef enum { ease_in, ease_out, ease_inout, } ease_type; static inline double sinusoidal_interpolate(double y1, double y2, double t, ease_type ease) { double factor = 0; if (ease == ease_in) { factor = sin((t - 1) * M_PI_2) + 1; } else if (ease == ease_out) { factor = sin(t * M_PI_2); } else { // ease_inout factor = 0.5 * (1 - cos(t * M_PI)); } return y1 + (y2 - y1) * factor; } static inline double power_interpolate(double y1, double y2, double t, double order, ease_type ease) { double factor = 0; if (ease == ease_in) { factor = pow(t, order); } else if (ease == ease_out) { factor = 1 - pow(1 - t, order); } else { // ease_inout if (t < 0.5) { factor = pow(2, order) * pow(t, order) / 2; } else { factor = 1.0 - pow(-2 * t + 2, order) / 2; } } return y1 + (y2 - y1) * factor; } static inline double exponential_interpolate(double y1, double y2, double t, ease_type ease) { double factor = 0; if (t == 0.0) { factor = 0; } else if (t == 1.0) { factor = 1.0; } else if (ease == ease_in) { factor = pow(2.0, 10 * t - 10); } else if (ease == ease_out) { factor = 1.0 - pow(2.0, -10 * t); } else { // ease_inout if (t < 0.5) { factor = pow(2, 20 * t - 10) / 2; } else { factor = (2 - pow(2, -20 * t + 10)) / 2; } } return y1 + (y2 - y1) * factor; } static inline double circular_interpolate(double y1, double y2, double t, ease_type ease) { double factor = 0; if (ease == ease_in) { factor = 1.0 - sqrt(1.0 - pow(t, 2.0)); } else if (ease == ease_out) { factor = sqrt(1.0 - pow(t - 1.0, 2.0)); } else { // ease_inout if (t < 0.5) { factor = 0.5 * (1 - sqrt(1 - 4 * (t * t))); } else { factor = 0.5 * (sqrt(-((2 * t) - 3) * ((2 * t) - 1)) + 1); } } return y1 + (y2 - y1) * factor; } static inline double back_interpolate(double y1, double y2, double t, ease_type ease) { double factor = 0; if (ease == ease_in) { factor = t * t * t - t * sin(t * M_PI); } else if (ease == ease_out) { double f = (1 - t); factor = 1 - (f * f * f - f * sin(f * M_PI)); } else { // ease_inout if (t < 0.5) { double f = 2 * t; factor = 0.5 * (f * f * f - f * sin(f * M_PI)); } else { double f = (1 - (2 * t - 1)); factor = 0.5 * (1 - (f * f * f - f * sin(f * M_PI))) + 0.5; } } return y1 + (y2 - y1) * factor; } static inline double elastic_interpolate(double y1, double y2, double t, ease_type ease) { double factor = 0; if (ease == ease_in) { factor = sin(13 * M_PI_2 * t) * pow(2, 10 * (t - 1)); } else if (ease == ease_out) { factor = sin(-13 * M_PI_2 * (t + 1)) * pow(2, -10 * t) + 1; } else { // ease_inout if (t < 0.5) { factor = 0.5 * sin(13 * M_PI_2 * (2 * t)) * pow(2, 10 * ((2 * t) - 1)); } else { factor = 0.5 * (sin(-13 * M_PI_2 * ((2 * t - 1) + 1)) * pow(2, -10 * (2 * t - 1)) + 2); } } return y1 + (y2 - y1) * factor; } static inline double bounce_interpolate(double y1, double y2, double t, ease_type ease) { double factor = 0; if (ease == ease_in) { factor = 1.0 - bounce_interpolate(0.0, 1.0, 1.0 - t, ease_out); } else if (ease == ease_out) { if (t < 4 / 11.0) { factor = (121 * t * t) / 16.0; } else if (t < 8 / 11.0) { factor = (363 / 40.0 * t * t) - (99 / 10.0 * t) + 17 / 5.0; } else if (t < 9 / 10.0) { factor = (4356 / 361.0 * t * t) - (35442 / 1805.0 * t) + 16061 / 1805.0; } else { factor = (54 / 5.0 * t * t) - (513 / 25.0 * t) + 268 / 25.0; } } else { // ease_inout if (t < 0.5) { factor = 0.5 * bounce_interpolate(0.0, 1.0, t * 2, ease_in); } else { factor = 0.5 * bounce_interpolate(0.0, 1.0, 2.0 * t - 1.0, ease_out) + 0.5; } } return y1 + (y2 - y1) * factor; } static inline double interpolate_value(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double t, mlt_keyframe_type type) { switch (type) { case mlt_keyframe_discrete: return y1; case mlt_keyframe_linear: return linear_interpolate(y1, y2, t); case mlt_keyframe_smooth_loose: return catmull_rom_interpolate(x0, y0, x1, y1, x2, y2, x3, y3, t, 0.0, 1.0); case mlt_keyframe_smooth_natural: return catmull_rom_interpolate(x0, y0, x1, y1, x2, y2, x3, y3, t, 0.5, -1.0); case mlt_keyframe_smooth_tight: return catmull_rom_interpolate(x0, y0, x1, y1, x2, y2, x3, y3, t, 0.5, 0.0); case mlt_keyframe_sinusoidal_in: return sinusoidal_interpolate(y1, y2, t, ease_in); case mlt_keyframe_sinusoidal_out: return sinusoidal_interpolate(y1, y2, t, ease_out); case mlt_keyframe_sinusoidal_in_out: return sinusoidal_interpolate(y1, y2, t, ease_inout); case mlt_keyframe_quadratic_in: return power_interpolate(y1, y2, t, 2, ease_in); case mlt_keyframe_quadratic_out: return power_interpolate(y1, y2, t, 2, ease_out); case mlt_keyframe_quadratic_in_out: return power_interpolate(y1, y2, t, 2, ease_inout); case mlt_keyframe_cubic_in: return power_interpolate(y1, y2, t, 3, ease_in); case mlt_keyframe_cubic_out: return power_interpolate(y1, y2, t, 3, ease_out); case mlt_keyframe_cubic_in_out: return power_interpolate(y1, y2, t, 3, ease_inout); case mlt_keyframe_quartic_in: return power_interpolate(y1, y2, t, 4, ease_in); case mlt_keyframe_quartic_out: return power_interpolate(y1, y2, t, 4, ease_out); case mlt_keyframe_quartic_in_out: return power_interpolate(y1, y2, t, 4, ease_inout); case mlt_keyframe_quintic_in: return power_interpolate(y1, y2, t, 5, ease_in); case mlt_keyframe_quintic_out: return power_interpolate(y1, y2, t, 5, ease_out); case mlt_keyframe_quintic_in_out: return power_interpolate(y1, y2, t, 5, ease_inout); case mlt_keyframe_exponential_in: return exponential_interpolate(y1, y2, t, ease_in); case mlt_keyframe_exponential_out: return exponential_interpolate(y1, y2, t, ease_out); case mlt_keyframe_exponential_in_out: return exponential_interpolate(y1, y2, t, ease_inout); case mlt_keyframe_circular_in: return circular_interpolate(y1, y2, t, ease_in); case mlt_keyframe_circular_out: return circular_interpolate(y1, y2, t, ease_out); case mlt_keyframe_circular_in_out: return circular_interpolate(y1, y2, t, ease_inout); case mlt_keyframe_back_in: return back_interpolate(y1, y2, t, ease_in); case mlt_keyframe_back_out: return back_interpolate(y1, y2, t, ease_out); case mlt_keyframe_back_in_out: return back_interpolate(y1, y2, t, ease_inout); case mlt_keyframe_elastic_in: return elastic_interpolate(y1, y2, t, ease_in); case mlt_keyframe_elastic_out: return elastic_interpolate(y1, y2, t, ease_out); case mlt_keyframe_elastic_in_out: return elastic_interpolate(y1, y2, t, ease_inout); case mlt_keyframe_bounce_in: return bounce_interpolate(y1, y2, t, ease_in); case mlt_keyframe_bounce_out: return bounce_interpolate(y1, y2, t, ease_out); case mlt_keyframe_bounce_in_out: return bounce_interpolate(y1, y2, t, ease_inout); } return y1; } /** Interpolate a new animation item given a set of other items. * * \private \memberof mlt_animation_s * * \param item an unpopulated animation item to be interpolated. * The frame and keyframe_type fields must already be set. The value for "frame" is the position * at which the value will be interpolated. The value for "keyframe_type" determines which * interpolation will be used. * \param p a sequential array of 4 animation items. The frame value for item must lie between the frame values for p[1] and p[2]. * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \return true if there was an error */ static int interpolate_item(mlt_animation_item item, mlt_animation_item p[], double fps, mlt_locale_t locale) { int error = 0; double progress = (double) (item->frame - p[1]->frame) / (double) (p[2]->frame - p[1]->frame); if (item->keyframe_type == mlt_keyframe_discrete) { mlt_property_pass(item->property, p[1]->property); } else if (mlt_property_is_color(p[1]->property)) { mlt_color value = {0xff, 0xff, 0xff, 0xff}; mlt_color colors[4]; mlt_color zero = {0xff, 0xff, 0xff, 0xff}; colors[1] = p[1] ? mlt_property_get_color(p[1]->property, fps, locale) : zero; if (p[2]) { colors[0] = p[0] ? mlt_property_get_color(p[0]->property, fps, locale) : zero; colors[2] = p[2] ? mlt_property_get_color(p[2]->property, fps, locale) : zero; colors[3] = p[3] ? mlt_property_get_color(p[3]->property, fps, locale) : zero; value.r = CLAMP(interpolate_value(p[0]->frame, colors[0].r, p[1]->frame, colors[1].r, p[2]->frame, colors[2].r, p[3]->frame, colors[3].r, progress, item->keyframe_type), 0, 255); value.g = CLAMP(interpolate_value(p[0]->frame, colors[0].g, p[1]->frame, colors[1].g, p[2]->frame, colors[2].g, p[3]->frame, colors[3].g, progress, item->keyframe_type), 0, 255); value.b = CLAMP(interpolate_value(p[0]->frame, colors[0].b, p[1]->frame, colors[1].b, p[2]->frame, colors[2].b, p[3]->frame, colors[3].b, progress, item->keyframe_type), 0, 255); value.a = CLAMP(interpolate_value(p[0]->frame, colors[0].a, p[1]->frame, colors[1].a, p[2]->frame, colors[2].a, p[3]->frame, colors[3].a, progress, item->keyframe_type), 0, 255); } else { value = colors[1]; } error = mlt_property_set_color(item->property, value); } else if (mlt_property_is_rect(item->property)) { mlt_rect value = {DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN}; mlt_rect points[4]; mlt_rect zero = {0, 0, 0, 0, 0}; points[1] = p[1] ? mlt_property_get_rect(p[1]->property, locale) : zero; if (p[2]) { points[0] = p[0] ? mlt_property_get_rect(p[0]->property, locale) : zero; points[2] = p[2] ? mlt_property_get_rect(p[2]->property, locale) : zero; points[3] = p[3] ? mlt_property_get_rect(p[3]->property, locale) : zero; value.x = interpolate_value(p[0]->frame, points[0].x, p[1]->frame, points[1].x, p[2]->frame, points[2].x, p[3]->frame, points[3].x, progress, item->keyframe_type); value.y = interpolate_value(p[0]->frame, points[0].y, p[1]->frame, points[1].y, p[2]->frame, points[2].y, p[3]->frame, points[3].y, progress, item->keyframe_type); value.w = interpolate_value(p[0]->frame, points[0].w, p[1]->frame, points[1].w, p[2]->frame, points[2].w, p[3]->frame, points[3].w, progress, item->keyframe_type); value.h = interpolate_value(p[0]->frame, points[0].h, p[1]->frame, points[1].h, p[2]->frame, points[2].h, p[3]->frame, points[3].h, progress, item->keyframe_type); value.o = interpolate_value(p[0]->frame, points[0].o, p[1]->frame, points[1].o, p[2]->frame, points[2].o, p[3]->frame, points[3].o, progress, item->keyframe_type); } else { value = points[1]; } error = mlt_property_set_rect(item->property, value); } else if (mlt_property_is_numeric(p[1]->property, locale)) { double value = 0.0; double points[4]; points[0] = p[0] ? mlt_property_get_double(p[0]->property, fps, locale) : 0; points[1] = p[1] ? mlt_property_get_double(p[1]->property, fps, locale) : 0; points[2] = p[2] ? mlt_property_get_double(p[2]->property, fps, locale) : 0; points[3] = p[3] ? mlt_property_get_double(p[3]->property, fps, locale) : 0; value = p[2] ? interpolate_value(p[0]->frame, points[0], p[1]->frame, points[1], p[2]->frame, points[2], p[3]->frame, points[3], progress, item->keyframe_type) : points[1]; error = mlt_property_set_double(item->property, value); } else { mlt_property_pass(item->property, p[1]->property); } return error; } mlt-7.38.0/src/framework/mlt_animation.h000664 000000 000000 00000010042 15172202314 020147 0ustar00rootroot000000 000000 /** * \file mlt_animation.h * \brief Property Animation class declaration * \see mlt_animation_s * * Copyright (C) 2004-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_ANIMATION_H #define MLT_ANIMATION_H #include "mlt_export.h" #include "mlt_property.h" #include "mlt_types.h" /** \brief Animation class * * Once an animation has been constructed using mlt_properties_s, this interface * provides a to query and manipulate the animation except for values. One must * use mlt_properties_s still to get, set, and change values. * * \envvar \em MLT_ANIMATION_TIME_FORMAT the time value string format to use, * defaults to mlt_time_frames. Use the numeric value of mlt_time_format as * the value of this variable. */ /** \brief An animation item that represents a keyframe-property combination. */ struct mlt_animation_item_s { int is_key; /**< a boolean of whether this is a key frame or an interpolated item */ int frame; /**< the frame number for this instance of the property */ mlt_property property; /**< the property for this point in time */ mlt_keyframe_type keyframe_type; /**< the method of interpolation for this key frame */ }; typedef struct mlt_animation_item_s *mlt_animation_item; /**< pointer to an animation item */ MLT_EXPORT mlt_animation mlt_animation_new(); MLT_EXPORT int mlt_animation_parse( mlt_animation self, const char *data, int length, double fps, mlt_locale_t locale); MLT_EXPORT int mlt_animation_refresh(mlt_animation self, const char *data, int length); MLT_EXPORT int mlt_animation_get_length(mlt_animation self); MLT_EXPORT void mlt_animation_set_length(mlt_animation self, int length); MLT_EXPORT int mlt_animation_parse_item(mlt_animation self, mlt_animation_item item, const char *data); MLT_EXPORT int mlt_animation_get_item(mlt_animation self, mlt_animation_item item, int position); MLT_EXPORT int mlt_animation_insert(mlt_animation self, mlt_animation_item item); MLT_EXPORT int mlt_animation_remove(mlt_animation self, int position); MLT_EXPORT void mlt_animation_interpolate(mlt_animation self); MLT_EXPORT int mlt_animation_next_key(mlt_animation self, mlt_animation_item item, int position); MLT_EXPORT int mlt_animation_prev_key(mlt_animation self, mlt_animation_item item, int position); MLT_EXPORT char *mlt_animation_serialize_cut_tf(mlt_animation self, int in, int out, mlt_time_format); MLT_EXPORT char *mlt_animation_serialize_cut(mlt_animation self, int in, int out); MLT_EXPORT char *mlt_animation_serialize_tf(mlt_animation self, mlt_time_format); MLT_EXPORT char *mlt_animation_serialize(mlt_animation self); MLT_EXPORT int mlt_animation_key_count(mlt_animation self); MLT_EXPORT int mlt_animation_key_get(mlt_animation self, mlt_animation_item item, int index); MLT_EXPORT void mlt_animation_close(mlt_animation self); MLT_EXPORT int mlt_animation_key_set_type(mlt_animation self, int index, mlt_keyframe_type type); MLT_EXPORT int mlt_animation_key_set_frame(mlt_animation self, int index, int frame); MLT_EXPORT void mlt_animation_shift_frames(mlt_animation self, int shift); MLT_EXPORT const char *mlt_animation_get_string(mlt_animation self); #endif mlt-7.38.0/src/framework/mlt_audio.c000664 000000 000000 00000052570 15172202314 017300 0ustar00rootroot000000 000000 /** * \file mlt_audio.c * \brief Audio class * \see mlt_mlt_audio_s * * Copyright (C) 2020-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_audio.h" #include "mlt_log.h" #include #include /** Allocate a new Audio object. * * \return a new audio object with default values set */ mlt_audio mlt_audio_new() { mlt_audio self = calloc(1, sizeof(struct mlt_audio_s)); self->close = free; return self; } /** Destroy an audio object created by mlt_audio_new(). * * \public \memberof mlt_audio_s * \param self the Audio object */ void mlt_audio_close(mlt_audio self) { if (self) { if (self->release_data) { self->release_data(self->data); } if (self->close) { self->close(self); } } } /** Set the most common values for the audio. * * Less common values will be set to reasonable defaults. * * You should use the \p mlt_audio_calculate_frame_samples to determine the number of samples you want. * \public \memberof mlt_audio_s * \param self the Audio object * \param data the buffer that contains the audio data * \param frequency the sample rate * \param format the audio format * \param samples the number of samples in the data * \param channels the number of audio channels */ void mlt_audio_set_values( mlt_audio self, void *data, int frequency, mlt_audio_format format, int samples, int channels) { self->data = data; self->frequency = frequency; self->format = format; self->samples = samples; self->channels = channels; self->layout = mlt_channel_auto; self->release_data = NULL; self->close = NULL; } /** Get the most common values for the audio. * * \public \memberof mlt_audio_s * \param self the Audio object * \param[out] data the buffer that contains the audio data * \param[out] frequency the sample rate * \param[out] format the audio format * \param[out] samples the number of samples in the data * \param[out] channels the number of audio channels */ void mlt_audio_get_values(mlt_audio self, void **data, int *frequency, mlt_audio_format *format, int *samples, int *channels) { *data = self->data; *frequency = self->frequency; *format = self->format; *samples = self->samples; *channels = self->channels; } /** Allocate the data field based on the other properties of the Audio. * * If the data field is already set, and a destructor function exists, the data * will be released. Else, the data pointer will be overwritten without being * released. * * After this function call, the release_data field will be set and can be used * to release the data when necessary. * * \public \memberof mlt_audio_s * \param self the Audio object */ void mlt_audio_alloc_data(mlt_audio self) { if (!self) return; mlt_audio_free_data(self); int size = mlt_audio_calculate_size(self); self->data = mlt_pool_alloc(size); self->release_data = mlt_pool_release; } /** Free the data field using the destructor function. * * If the constructor function does not exist, the value will be set to NULL * without being released. * * After this function call, the data and release_data fields will be NULL. * * \public \memberof mlt_audio_s * \param self the Audio object */ void mlt_audio_free_data(mlt_audio self) { if (!self) return; if (self->release_data) { self->release_data(self->data); } self->data = NULL; self->release_data = NULL; } /** Calculate the number of bytes needed for the Audio data. * * \public \memberof mlt_audio_s * \param self the Audio object * \return the number of bytes */ int mlt_audio_calculate_size(mlt_audio self) { if (!self) return 0; return mlt_audio_format_size(self->format, self->samples, self->channels); } /** Get the number of planes for the audio type * * \public \memberof mlt_audio_s * \param self the Audio object * \return the number of planes. */ int mlt_audio_plane_count(mlt_audio self) { switch (self->format) { case mlt_audio_none: return 0; case mlt_audio_s16: return 1; case mlt_audio_s32le: return 1; case mlt_audio_s32: return self->channels; case mlt_audio_f32le: return 1; case mlt_audio_float: return self->channels; case mlt_audio_u8: return 1; } return 0; } /** Get the size of an audio plane. * * \public \memberof mlt_audio_s * \param self the Audio object * \return the size of a plane. */ int mlt_audio_plane_size(mlt_audio self) { switch (self->format) { case mlt_audio_none: return 0; case mlt_audio_s16: return self->samples * self->channels * sizeof(int16_t); case mlt_audio_s32le: return self->samples * self->channels * sizeof(int32_t); case mlt_audio_s32: return self->samples * sizeof(int32_t); case mlt_audio_f32le: return self->samples * self->channels * sizeof(float); case mlt_audio_float: return self->samples * sizeof(float); case mlt_audio_u8: return self->samples * self->channels; } return 0; } /** Populate an array of pointers each pointing to the beginning of an audio plane. * * \public \memberof mlt_audio_s * \param self the Audio object * \param[out] planes the array of pointers to populate */ void mlt_audio_get_planes(mlt_audio self, uint8_t **planes) { int plane_count = mlt_audio_plane_count(self); int plane_size = mlt_audio_plane_size(self); int p = 0; for (p = 0; p < plane_count; p++) { planes[p] = (uint8_t *) self->data + (p * plane_size); } } /** Set a range of samples to silence. * * \public \memberof mlt_audio_s * \param self the Audio object * \param samples the number of samples to silence * \param start the sample at which to begin the silence * \return none */ void mlt_audio_silence(mlt_audio self, int samples, int start) { if ((start + samples) > self->samples) { mlt_log_error(NULL, "mlt_audio_silence: avoid buffer overrun\n"); return; } switch (self->format) { case mlt_audio_none: mlt_log_error(NULL, "mlt_audio_silence: mlt_audio_none\n"); return; // Interleaved 8bit formats case mlt_audio_u8: { int8_t *s = (int8_t *) self->data + (start * self->channels); int size = self->channels * samples * sizeof(int8_t); memset(s, 127, size); return; } // Interleaved 16bit formats case mlt_audio_s16: { int16_t *s = (int16_t *) self->data + (start * self->channels); int size = self->channels * samples * sizeof(int16_t); memset(s, 0, size); return; } // Interleaved 32bit formats case mlt_audio_s32le: case mlt_audio_f32le: { int32_t *s = (int32_t *) self->data + (start * self->channels); int size = self->channels * samples * sizeof(int32_t); memset(s, 0, size); return; } // Planer 32bit formats case mlt_audio_s32: case mlt_audio_float: { int p = 0; for (p = 0; p < self->channels; p++) { int32_t *s = (int32_t *) self->data + (p * self->samples) + start; int size = samples * sizeof(int32_t); memset(s, 0, size); } return; } } } /** Shrink the audio to the new number of samples. * * Existing samples will be moved as necessary to ensure that the audio planes * immediately follow each other. The samples field will be updated to match the * new number. * * \public \memberof mlt_audio_s * \param self the Audio object * \param samples the new number of samples to shrink to */ void mlt_audio_shrink(mlt_audio self, int samples) { int plane_count = mlt_audio_plane_count(self); if (samples >= self->samples || samples < 0) { // Nothing to do; } else if (plane_count == 1 || samples == 0) { // No need to move any data. self->samples = samples; } if (plane_count > 1) { // Move data to remove gaps between planes. size_t src_plane_size = mlt_audio_plane_size(self); self->samples = samples; size_t dst_plane_size = mlt_audio_plane_size(self); // The first channel will always be in the correct place (0). int p = 1; for (p = 1; p < plane_count; p++) { uint8_t *src = (uint8_t *) self->data + (p * src_plane_size); uint8_t *dst = (uint8_t *) self->data + (p * dst_plane_size); memmove(dst, src, dst_plane_size); } } } /** Reverse the audio samples. * * \public \memberof mlt_audio_s * \param self the Audio object */ void mlt_audio_reverse(mlt_audio self) { int c = 0; if (!self || !self->data || self->samples <= 0) return; switch (self->format) { // Interleaved 8bit formats case mlt_audio_u8: { int8_t tmp; for (c = 0; c < self->channels; c++) { // Pointer to first sample int8_t *a = (int8_t *) self->data + c; // Pointer to last sample int8_t *b = (int8_t *) self->data + ((self->samples - 1) * self->channels) + c; while (a < b) { tmp = *a; *a = *b; *b = tmp; a += self->channels; b -= self->channels; } } break; } // Interleaved 16bit formats case mlt_audio_s16: { int16_t tmp; for (c = 0; c < self->channels; c++) { // Pointer to first sample int16_t *a = (int16_t *) self->data + c; // Pointer to last sample int16_t *b = (int16_t *) self->data + ((self->samples - 1) * self->channels) + c; while (a < b) { tmp = *a; *a = *b; *b = tmp; a += self->channels; b -= self->channels; } } break; } // Interleaved 32bit formats case mlt_audio_s32le: case mlt_audio_f32le: { int32_t tmp; for (c = 0; c < self->channels; c++) { // Pointer to first sample int32_t *a = (int32_t *) self->data + c; // Pointer to last sample int32_t *b = (int32_t *) self->data + ((self->samples - 1) * self->channels) + c; while (a < b) { tmp = *a; *a = *b; *b = tmp; a += self->channels; b -= self->channels; } } break; } // Planer 32bit formats case mlt_audio_s32: case mlt_audio_float: { int32_t tmp; for (c = 0; c < self->channels; c++) { // Pointer to first sample int32_t *a = (int32_t *) self->data + (c * self->samples); // Pointer to last sample int32_t *b = (int32_t *) self->data + ((c + 1) * self->samples) - 1; while (a < b) { tmp = *a; *a = *b; *b = tmp; a++; b--; } } break; } case mlt_audio_none: break; } } /** Copy audio samples from src to dst. * * \public \memberof mlt_audio_s * \param dst the destination object * \param src the source object * \param samples the number of samples to copy * \param src_start the number of samples to skip from the source * \param dst_start the number of samples to skip from the destination * \return none */ void mlt_audio_copy(mlt_audio dst, mlt_audio src, int samples, int src_start, int dst_start) { if ((dst_start + samples) > dst->samples) { mlt_log_error(NULL, "mlt_audio_copy: avoid dst buffer overrun\n"); return; } if ((src_start + samples) > src->samples) { mlt_log_error(NULL, "mlt_audio_copy: avoid src buffer overrun\n"); return; } if (src->format != dst->format || src->channels != dst->channels) { mlt_log_error(NULL, "mlt_audio_copy: src/dst mismatch\n"); return; } switch (src->format) { case mlt_audio_none: mlt_log_error(NULL, "mlt_audio_copy: mlt_audio_none\n"); return; // Interleaved 8bit formats case mlt_audio_u8: { int8_t *s = (int8_t *) src->data + (src_start * src->channels); int8_t *d = (int8_t *) dst->data + (dst_start * dst->channels); int size = src->channels * samples * sizeof(int8_t); memmove(d, s, size); return; } // Interleaved 16bit formats case mlt_audio_s16: { int16_t *s = (int16_t *) src->data + (src_start * src->channels); int16_t *d = (int16_t *) dst->data + (dst_start * dst->channels); int size = src->channels * samples * sizeof(int16_t); memmove(d, s, size); return; } // Interleaved 32bit formats case mlt_audio_s32le: case mlt_audio_f32le: { int32_t *s = (int32_t *) src->data + (src_start * src->channels); int32_t *d = (int32_t *) dst->data + (dst_start * dst->channels); int size = src->channels * samples * sizeof(int32_t); memmove(d, s, size); return; } // Planer 32bit formats case mlt_audio_s32: case mlt_audio_float: { int p = 0; for (p = 0; p < src->channels; p++) { int32_t *s = (int32_t *) src->data + (p * src->samples) + src_start; int32_t *d = (int32_t *) dst->data + (p * dst->samples) + dst_start; int size = samples * sizeof(int32_t); memmove(d, s, size); } return; } } } /** Determine the number of samples that belong in a frame at a time position. * * \public \memberof mlt_audio_s * \param fps the frame rate * \param frequency the sample rate * \param position the time position * \return the number of samples per channel */ int mlt_audio_calculate_frame_samples(float fps, int frequency, int64_t position) { /* Compute the cumulative number of samples until the start of this frame and the cumulative number of samples until the start of the next frame. Round each to the nearest integer and take the difference to determine the number of samples in this frame. This approach should prevent rounding errors that can accumulate over a large number of frames causing A/V sync problems. */ return mlt_audio_calculate_samples_to_position(fps, frequency, position + 1) - mlt_audio_calculate_samples_to_position(fps, frequency, position); } /** Determine the number of samples that belong before a time position. * * \public \memberof mlt_audio_s * \param fps the frame rate * \param frequency the sample rate * \param position the time position * \return the number of samples per channel */ int64_t mlt_audio_calculate_samples_to_position(float fps, int frequency, int64_t position) { int64_t samples = 0; if (fps) { samples = (int64_t) ((double) position * (double) frequency / (double) fps + (position < 0 ? -0.5 : 0.5)); } return samples; } /** Get the short name for an audio format. * * You do not need to deallocate the returned string. * \public \memberof mlt_audio_s * \param format an audio format enum * \return a string for the name of the image format */ const char *mlt_audio_format_name(mlt_audio_format format) { switch (format) { case mlt_audio_none: return "none"; case mlt_audio_s16: return "s16"; case mlt_audio_s32: return "s32"; case mlt_audio_s32le: return "s32le"; case mlt_audio_float: return "float"; case mlt_audio_f32le: return "f32le"; case mlt_audio_u8: return "u8"; } return "invalid"; } /** Get the amount of bytes needed for a block of audio. * * \public \memberof mlt_audio_s * \param format an audio format enum * \param samples the number of samples per channel * \param channels the number of channels * \return the number of bytes */ int mlt_audio_format_size(mlt_audio_format format, int samples, int channels) { switch (format) { case mlt_audio_none: return 0; case mlt_audio_s16: return samples * channels * sizeof(int16_t); case mlt_audio_s32le: case mlt_audio_s32: return samples * channels * sizeof(int32_t); case mlt_audio_f32le: case mlt_audio_float: return samples * channels * sizeof(float); case mlt_audio_u8: return samples * channels; } return 0; } /** Get the short name for a channel layout. * * You do not need to deallocate the returned string. * \public \memberof mlt_audio_s * \param layout the channel layout * \return a string for the name of the channel layout */ const char *mlt_audio_channel_layout_name(mlt_channel_layout layout) { switch (layout) { case mlt_channel_auto: return "auto"; case mlt_channel_independent: return "independent"; case mlt_channel_mono: return "mono"; case mlt_channel_stereo: return "stereo"; case mlt_channel_2p1: return "2.1"; case mlt_channel_3p0: return "3.0"; case mlt_channel_3p0_back: return "3.0(back)"; case mlt_channel_4p0: return "4.0"; case mlt_channel_quad_back: return "quad"; case mlt_channel_quad_side: return "quad(side)"; case mlt_channel_3p1: return "3.1"; case mlt_channel_5p0_back: return "5.0"; case mlt_channel_5p0: return "5.0(side)"; case mlt_channel_4p1: return "4.1"; case mlt_channel_5p1_back: return "5.1"; case mlt_channel_5p1: return "5.1(side)"; case mlt_channel_6p0: return "6.0"; case mlt_channel_6p0_front: return "6.0(front)"; case mlt_channel_hexagonal: return "hexagonal"; case mlt_channel_6p1: return "6.1"; case mlt_channel_6p1_back: return "6.1(back)"; case mlt_channel_6p1_front: return "6.1(front)"; case mlt_channel_7p0: return "7.0"; case mlt_channel_7p0_front: return "7.0(front)"; case mlt_channel_7p1: return "7.1"; case mlt_channel_7p1_wide_side: return "7.1(wide-side)"; case mlt_channel_7p1_wide_back: return "7.1(wide)"; } return "invalid"; } /** Get the id of channel layout from short name. * * \public \memberof mlt_audio_s * \param name the channel layout short name * \return a channel layout */ mlt_channel_layout mlt_audio_channel_layout_id(const char *name) { if (name) { mlt_channel_layout c; for (c = mlt_channel_auto; c <= mlt_channel_7p1_wide_back; c++) { const char *v = mlt_audio_channel_layout_name(c); if (!strcmp(v, name)) return c; } } return mlt_channel_auto; } /** Get the number of channels for a channel layout. * * \public \memberof mlt_audio_s * \param layout the channel layout * \return the number of channels for the channel layout */ int mlt_audio_channel_layout_channels(mlt_channel_layout layout) { switch (layout) { case mlt_channel_auto: return 0; case mlt_channel_independent: return 0; case mlt_channel_mono: return 1; case mlt_channel_stereo: return 2; case mlt_channel_2p1: return 3; case mlt_channel_3p0: return 3; case mlt_channel_3p0_back: return 3; case mlt_channel_4p0: return 4; case mlt_channel_quad_back: return 4; case mlt_channel_quad_side: return 4; case mlt_channel_3p1: return 4; case mlt_channel_5p0_back: return 5; case mlt_channel_5p0: return 5; case mlt_channel_4p1: return 5; case mlt_channel_5p1_back: return 6; case mlt_channel_5p1: return 6; case mlt_channel_6p0: return 6; case mlt_channel_6p0_front: return 6; case mlt_channel_hexagonal: return 6; case mlt_channel_6p1: return 7; case mlt_channel_6p1_back: return 7; case mlt_channel_6p1_front: return 7; case mlt_channel_7p0: return 7; case mlt_channel_7p0_front: return 7; case mlt_channel_7p1: return 8; case mlt_channel_7p1_wide_back: return 8; case mlt_channel_7p1_wide_side: return 8; } return 0; } /** Get a default channel layout for a given number of channels. * * \public \memberof mlt_audio_s * \param channels the number of channels * \return the default channel layout */ mlt_channel_layout mlt_audio_channel_layout_default(int channels) { mlt_channel_layout c; for (c = mlt_channel_mono; c <= mlt_channel_7p1_wide_back; c++) { if (mlt_audio_channel_layout_channels(c) == channels) return c; } return mlt_channel_independent; } mlt-7.38.0/src/framework/mlt_audio.h000664 000000 000000 00000006262 15172202314 017302 0ustar00rootroot000000 000000 /** * \file mlt_audio.h * \brief Audio class * \see mlt_audio_s * * Copyright (C) 2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_AUDIO_H #define MLT_AUDIO_H #include "mlt_export.h" #include "mlt_types.h" /** \brief Audio class * * Audio is the data object that represents audio for a period of time. */ struct mlt_audio_s { void *data; int frequency; mlt_audio_format format; int samples; int channels; mlt_channel_layout layout; mlt_destructor release_data; mlt_destructor close; }; MLT_EXPORT mlt_audio mlt_audio_new(); MLT_EXPORT void mlt_audio_close(mlt_audio self); MLT_EXPORT void mlt_audio_set_values( mlt_audio self, void *data, int frequency, mlt_audio_format format, int samples, int channels); MLT_EXPORT void mlt_audio_get_values(mlt_audio self, void **data, int *frequency, mlt_audio_format *format, int *samples, int *channels); MLT_EXPORT void mlt_audio_alloc_data(mlt_audio self); MLT_EXPORT void mlt_audio_free_data(mlt_audio self); MLT_EXPORT int mlt_audio_calculate_size(mlt_audio self); MLT_EXPORT int mlt_audio_plane_count(mlt_audio self); MLT_EXPORT int mlt_audio_plane_size(mlt_audio self); MLT_EXPORT void mlt_audio_get_planes(mlt_audio self, uint8_t **planes); MLT_EXPORT void mlt_audio_silence(mlt_audio self, int samples, int start); MLT_EXPORT void mlt_audio_shrink(mlt_audio self, int samples); MLT_EXPORT void mlt_audio_reverse(mlt_audio self); MLT_EXPORT void mlt_audio_copy( mlt_audio dst, mlt_audio src, int samples, int src_start, int dst_start); MLT_EXPORT int mlt_audio_calculate_frame_samples(float fps, int frequency, int64_t position); MLT_EXPORT int64_t mlt_audio_calculate_samples_to_position(float fps, int frequency, int64_t position); MLT_EXPORT const char *mlt_audio_format_name(mlt_audio_format format); MLT_EXPORT int mlt_audio_format_size(mlt_audio_format format, int samples, int channels); MLT_EXPORT const char *mlt_audio_channel_layout_name(mlt_channel_layout layout); MLT_EXPORT mlt_channel_layout mlt_audio_channel_layout_id(const char *name); MLT_EXPORT int mlt_audio_channel_layout_channels(mlt_channel_layout layout); MLT_EXPORT mlt_channel_layout mlt_audio_channel_layout_default(int channels); #endif mlt-7.38.0/src/framework/mlt_cache.c000664 000000 000000 00000050614 15172202314 017237 0ustar00rootroot000000 000000 /** * \file mlt_cache.c * \brief least recently used cache * \see mlt_profile_s * * Copyright (C) 2007-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_cache.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_properties.h" #include "mlt_types.h" #include #include /** the maximum number of data objects to cache per line */ #define MAX_CACHE_SIZE (200) /** the default number of data objects to cache per line */ #define DEFAULT_CACHE_SIZE (4) /** \brief Cache item class * * A cache item is a structure holding information about a data object including * a reference count that is used to control its lifetime. When you get a * a cache item from the cache, you hold a reference that prevents the data * from being released when the cache is full and something new is added. * When you close the cache item, the reference count is decremented. * The data object is destroyed when all cache items are closed and the cache * releases its reference. */ typedef struct mlt_cache_item_s { mlt_cache cache; /**< a reference to the cache to which this belongs */ void *object; /**< a parent object to the cache data that uniquely identifies this cached item */ void *data; /**< the opaque pointer to the cached data */ int size; /**< the size of the cached data */ int refcount; /**< a reference counter to control when destructor is called */ mlt_destructor destructor; /**< a function to release or destroy the cached data */ } mlt_cache_item_s; /** \brief Cache class * * This is a utility class for implementing a Least Recently Used (LRU) cache * of data blobs indexed by the address of some other object (e.g., a service). * Instead of sorting and manipulating linked lists, it tries to be simple and * elegant by copying pointers between two arrays of fixed size to shuffle the * order of elements. * * This class is useful if you have a service that wants to cache something * somewhat large, but will not scale if there are many instances of the service. * Of course, the service will need to know how to recreate the cached element * if it gets flushed from the cache, * * The most obvious examples are the pixbuf and qimage producers that cache their * respective objects representing a picture read from a file. If the picture * is no longer in the cache, it can simply re-read it from file. However, a * picture is often repeated over many frames and makes sense to cache instead * of continually reading, parsing, and decoding. On the other hand, you might * want to load hundreds of pictures as individual producers, which would use * a lot of memory if every picture is held in memory! */ struct mlt_cache_s { int count; /**< the number of items currently in the cache */ int size; /**< the maximum number of items permitted in the cache <= \p MAX_CACHE_SIZE */ int is_frames; /**< indicates if this cache is used to cache frames */ void **current; /**< pointer to the current array of pointers */ void *A[MAX_CACHE_SIZE]; void *B[MAX_CACHE_SIZE]; pthread_mutex_t mutex; /**< a mutex to prevent multi-threaded race conditions */ mlt_properties active; /**< a list of cache items some of which may no longer be in \p current but to which there are outstanding references */ mlt_properties garbage; /**< a list cache items pending release. A cache item is copied to this list when it is updated but there are outstanding references to the old data object. */ }; /** Get the data pointer from the cache item. * * \public \memberof mlt_cache_s * \param item a cache item * \param[out] size the number of bytes pointed at, if supplied when putting the data into the cache * \return the data pointer */ void *mlt_cache_item_data(mlt_cache_item item, int *size) { if (size && item) *size = item->size; return item ? item->data : NULL; } /** Close a cache item given its parent object pointer. * * \private \memberof mlt_cache_s * \param cache a cache * \param object the object to which the data object belongs * \param data the data object, which might be in the garbage list (optional) */ static void cache_object_close(mlt_cache cache, void *object, void *data) { char key[19]; if (cache->is_frames) { // Frame caches are easy - just close the object as mlt_frame. mlt_frame_close(object); return; } // Fetch the cache item from the active list by its owner's address sprintf(key, "%p", object); mlt_cache_item item = mlt_properties_get_data(cache->active, key, NULL); if (item) { mlt_log(NULL, MLT_LOG_DEBUG, "%s: item %p object %p data %p refcount %d\n", __FUNCTION__, item, item->object, item->data, item->refcount); if (item->destructor && --item->refcount <= 0) { // Destroy the data object item->destructor(item->data); item->data = NULL; item->destructor = NULL; // Do not dispose of the cache item because it could likely be used // again. } } // Fetch the cache item from the garbage collection by its data address if (data) { sprintf(key, "%p", data); item = mlt_properties_get_data(cache->garbage, key, NULL); if (item) { mlt_log(NULL, MLT_LOG_DEBUG, "collecting garbage item %p object %p data %p refcount %d\n", item, item->object, item->data, item->refcount); if (item->destructor && --item->refcount <= 0) { item->destructor(item->data); item->data = NULL; item->destructor = NULL; // We do not need the garbage-collected cache item mlt_properties_set_data(cache->garbage, key, NULL, 0, NULL, NULL); } } } } /** Close a cache item. * * Release a reference and call the destructor on the data object when all * references are released. * * \public \memberof mlt_cache_item_s * \param item a cache item */ void mlt_cache_item_close(mlt_cache_item item) { if (item) { pthread_mutex_lock(&item->cache->mutex); cache_object_close(item->cache, item->object, item->data); pthread_mutex_unlock(&item->cache->mutex); } } /** Create a new cache. * * The default size is \p DEFAULT_CACHE_SIZE. * \public \memberof mlt_cache_s * \return a new cache or NULL if there was an error */ mlt_cache mlt_cache_init() { mlt_cache result = calloc(1, sizeof(struct mlt_cache_s)); if (result) { result->size = DEFAULT_CACHE_SIZE; result->current = result->A; pthread_mutex_init(&result->mutex, NULL); result->active = mlt_properties_new(); result->garbage = mlt_properties_new(); } return result; } /** Set the number of items to cache. * * This must be called before using the cache. The size can not be more * than \p MAX_CACHE_SIZE. * \public \memberof mlt_cache_s * \param cache the cache to adjust * \param size the new size of the cache */ void mlt_cache_set_size(mlt_cache cache, int size) { if (size <= MAX_CACHE_SIZE) cache->size = size; } /** Get the number of possible cache items. * * \public \memberof mlt_cache_s * \param cache the cache to check * \return the current maximum size of the cache */ int mlt_cache_get_size(mlt_cache cache) { return cache->size; } /** Destroy a cache. * * \public \memberof mlt_cache_s * \param cache the cache to destroy */ void mlt_cache_close(mlt_cache cache) { if (cache) { while (cache->count--) { void *object = cache->current[cache->count]; mlt_log(NULL, MLT_LOG_DEBUG, "%s: %d = %p\n", __FUNCTION__, cache->count, object); cache_object_close(cache, object, NULL); } mlt_properties_close(cache->active); mlt_properties_close(cache->garbage); pthread_mutex_destroy(&cache->mutex); free(cache); } } /** Remove cache entries for an object. * * \public \memberof mlt_cache_s * \param cache a cache * \param object the object that owns the cached data */ void mlt_cache_purge(mlt_cache cache, void *object) { if (!cache) return; pthread_mutex_lock(&cache->mutex); if (cache && object) { int i, j; void **alt = cache->current == cache->A ? cache->B : cache->A; for (i = 0, j = 0; i < cache->count; i++) { void *o = cache->current[i]; if (o == object) { cache_object_close(cache, o, NULL); } else { alt[j++] = o; } } cache->count = j; cache->current = alt; } pthread_mutex_unlock(&cache->mutex); } /** Shuffle the cache entries between the two arrays and return the cache entry for an object. * * \private \memberof mlt_cache_s * \param cache a cache object * \param object the object that owns the cached data * \return a cache entry if there was a hit or NULL for a miss */ static void **shuffle_get_hit(mlt_cache cache, void *object) { int i = cache->count; int j = cache->count - 1; void **hit = NULL; void **alt = cache->current == cache->A ? cache->B : cache->A; if (cache->count > 0 && cache->count < cache->size) { // first determine if we have a hit while (i-- && !hit) { void **o = &cache->current[i]; if (*o == object) hit = o; } // if there was no hit, we will not be shuffling out an entry // and are still filling the cache if (!hit) ++j; // reset these i = cache->count; hit = NULL; } // shuffle the existing entries to the alternate array while (i--) { void **o = &cache->current[i]; if (!hit && *o == object) { hit = o; } else if (j > 0) { alt[--j] = *o; // mlt_log( NULL, MLT_LOG_DEBUG, "%s: shuffle %d = %p\n", __FUNCTION__, j, alt[j] ); } } return hit; } /** Put a chunk of data in the cache. * * This function and mlt_cache_get() are not scalable with a large volume * of unique \p object parameter values. Therefore, it does not make sense * to use it for a frame/image cache using the frame position for \p object. * Instead, use mlt_cache_put_frame() for that. * * \public \memberof mlt_cache_s * \param cache a cache object * \param object the object to which this data belongs * \param data an opaque pointer to the data to cache * \param size the size of the data in bytes * \param destructor a pointer to a function that can destroy or release a reference to the data. */ void mlt_cache_put(mlt_cache cache, void *object, void *data, int size, mlt_destructor destructor) { pthread_mutex_lock(&cache->mutex); void **hit = shuffle_get_hit(cache, object); void **alt = cache->current == cache->A ? cache->B : cache->A; // add the object to the cache if (hit) { // release the old data cache_object_close(cache, *hit, NULL); // the MRU end gets the updated data hit = &alt[cache->count - 1]; } else if (cache->count < cache->size) { // more room in cache, add it to MRU end hit = &alt[cache->count++]; } else { // release the entry at the LRU end cache_object_close(cache, cache->current[0], NULL); // The MRU end gets the new item hit = &alt[cache->count - 1]; } *hit = object; mlt_log(NULL, MLT_LOG_DEBUG, "%s: put %d = %p, %p\n", __FUNCTION__, cache->count - 1, object, data); // Fetch the cache item char key[19]; sprintf(key, "%p", object); mlt_cache_item item = mlt_properties_get_data(cache->active, key, NULL); if (!item) { item = calloc(1, sizeof(mlt_cache_item_s)); if (item) mlt_properties_set_data(cache->active, key, item, 0, free, NULL); } if (item) { // If updating the cache item but not all references are released // copy the item to the garbage collection. if (item->refcount > 0 && item->data) { mlt_cache_item orphan = calloc(1, sizeof(mlt_cache_item_s)); if (orphan) { mlt_log(NULL, MLT_LOG_DEBUG, "adding to garbage collection object %p data %p\n", item->object, item->data); *orphan = *item; sprintf(key, "%p", orphan->data); // We store in the garbage collection by data address, not the owner's! mlt_properties_set_data(cache->garbage, key, orphan, 0, free, NULL); } } // Set/update the cache item item->cache = cache; item->object = object; item->data = data; item->size = size; item->destructor = destructor; item->refcount = 1; } // swap the current array cache->current = alt; pthread_mutex_unlock(&cache->mutex); } /** Get a chunk of data from the cache. * * \public \memberof mlt_cache_s * \param cache a cache object * \param object the object for which you are trying to locate the data * \return a mlt_cache_item if found or NULL if not found or has been flushed from the cache */ mlt_cache_item mlt_cache_get(mlt_cache cache, void *object) { mlt_cache_item result = NULL; pthread_mutex_lock(&cache->mutex); void **hit = shuffle_get_hit(cache, object); void **alt = cache->current == cache->A ? cache->B : cache->A; if (hit) { // copy the hit to the MRU end alt[cache->count - 1] = *hit; hit = &alt[cache->count - 1]; char key[19]; sprintf(key, "%p", *hit); result = mlt_properties_get_data(cache->active, key, NULL); if (result && result->data) { result->refcount++; mlt_log(NULL, MLT_LOG_DEBUG, "%s: get %d = %p, %p\n", __FUNCTION__, cache->count - 1, *hit, result->data); } // swap the current array cache->current = alt; } pthread_mutex_unlock(&cache->mutex); return result; } /** Shuffle the cache entries between the two arrays and return the frame for a position. * * \private \memberof mlt_cache_s * \param cache a cache object * \param position the position of the frame that you want * \return a frame if there was a hit or NULL for a miss */ static mlt_frame *shuffle_get_frame(mlt_cache cache, mlt_position position) { int i = cache->count; int j = cache->count - 1; mlt_frame *hit = NULL; mlt_frame *alt = (mlt_frame *) (cache->current == cache->A ? cache->B : cache->A); if (cache->count > 0 && cache->count < cache->size) { // first determine if we have a hit while (i-- && !hit) { mlt_frame *o = (mlt_frame *) &cache->current[i]; if (mlt_frame_original_position(*o) == position) hit = o; } // if there was no hit, we will not be shuffling out an entry // and are still filling the cache if (!hit) ++j; // reset these i = cache->count; hit = NULL; } // shuffle the existing entries to the alternate array while (i--) { mlt_frame *o = (mlt_frame *) &cache->current[i]; if (!hit && mlt_frame_original_position(*o) == position) { hit = o; } else if (j > 0) { alt[--j] = *o; // mlt_log( NULL, MLT_LOG_DEBUG, "%s: shuffle %d = %p\n", __FUNCTION__, j, alt[j] ); } } return hit; } static void cache_put_frame(mlt_cache cache, mlt_frame frame, int audio, int image) { pthread_mutex_lock(&cache->mutex); mlt_frame *hit = shuffle_get_frame(cache, mlt_frame_original_position(frame)); mlt_frame *alt = (mlt_frame *) (cache->current == cache->A ? cache->B : cache->A); // add the frame to the cache if (hit) { // release the old data mlt_frame_close(*hit); // the MRU end gets the updated data hit = &alt[cache->count - 1]; } else if (cache->count < cache->size) { // more room in cache, add it to MRU end hit = &alt[cache->count++]; } else { // release the entry at the LRU end mlt_frame_close(cache->current[0]); // The MRU end gets the new item hit = &alt[cache->count - 1]; } if (audio && image) { *hit = mlt_frame_clone(frame, 1); } else if (audio) { *hit = mlt_frame_clone_audio(frame, 1); } else if (image) { *hit = mlt_frame_clone_image(frame, 1); } mlt_log(NULL, MLT_LOG_DEBUG, "%s: put %d = %p\n", __FUNCTION__, cache->count - 1, frame); // swap the current array cache->current = (void **) alt; cache->is_frames = 1; pthread_mutex_unlock(&cache->mutex); } /** Put a frame in the cache with audio and video. * * Unlike mlt_cache_put() this version is more suitable for caching frames * and their data - like images. However, this version does not use reference * counting and garbage collection. Rather, frames are cloned with deep copy * to avoid those things. * * \public \memberof mlt_cache_s * \param cache a cache object * \param frame the frame to cache * \see mlt_frame_get_frame */ void mlt_cache_put_frame(mlt_cache cache, mlt_frame frame) { cache_put_frame(cache, frame, 1, 1); } /** Put a frame in the cache with audio. * * Unlike mlt_cache_put() this version is more suitable for caching frames * and their data - like images. However, this version does not use reference * counting and garbage collection. Rather, frames are cloned with deep copy * to avoid those things. * * \public \memberof mlt_cache_s * \param cache a cache object * \param frame the frame to cache * \see mlt_frame_get_frame */ void mlt_cache_put_frame_audio(mlt_cache cache, mlt_frame frame) { cache_put_frame(cache, frame, 1, 0); } /** Put a frame in the cache with image. * * Unlike mlt_cache_put() this version is more suitable for caching frames * and their data - like images. However, this version does not use reference * counting and garbage collection. Rather, frames are cloned with deep copy * to avoid those things. * * \public \memberof mlt_cache_s * \param cache a cache object * \param frame the frame to cache * \see mlt_frame_get_frame */ void mlt_cache_put_frame_image(mlt_cache cache, mlt_frame frame) { cache_put_frame(cache, frame, 0, 1); } /** Get a frame from the cache. * * You must call mlt_frame_close() on the frame you receive from this. * * \public \memberof mlt_cache_s * \param cache a cache object * \param position the position of the frame that you want * \return a frame if found or NULL if not found or has been flushed from the cache * \see mlt_frame_put_frame */ mlt_frame mlt_cache_get_frame(mlt_cache cache, mlt_position position) { mlt_frame result = NULL; pthread_mutex_lock(&cache->mutex); mlt_frame *hit = shuffle_get_frame(cache, position); mlt_frame *alt = (mlt_frame *) (cache->current == cache->A ? cache->B : cache->A); if (hit) { // copy the hit to the MRU end alt[cache->count - 1] = *hit; hit = &alt[cache->count - 1]; result = mlt_frame_clone(*hit, 1); mlt_log(NULL, MLT_LOG_DEBUG, "%s: get %d = %p\n", __FUNCTION__, cache->count - 1, *hit); // swap the current array cache->current = (void **) alt; } pthread_mutex_unlock(&cache->mutex); return result; } mlt-7.38.0/src/framework/mlt_cache.h000664 000000 000000 00000003511 15172202314 017236 0ustar00rootroot000000 000000 /** * \file mlt_cache.h * \brief least recently used cache * \see mlt_cache_s * * Copyright (C) 2007-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_CACHE_H #define MLT_CACHE_H #include "mlt_export.h" #include "mlt_types.h" MLT_EXPORT void *mlt_cache_item_data(mlt_cache_item item, int *size); MLT_EXPORT void mlt_cache_item_close(mlt_cache_item item); MLT_EXPORT mlt_cache mlt_cache_init(); MLT_EXPORT void mlt_cache_set_size(mlt_cache cache, int size); MLT_EXPORT int mlt_cache_get_size(mlt_cache cache); MLT_EXPORT void mlt_cache_close(mlt_cache cache); MLT_EXPORT void mlt_cache_purge(mlt_cache cache, void *object); MLT_EXPORT void mlt_cache_put( mlt_cache cache, void *object, void *data, int size, mlt_destructor destructor); MLT_EXPORT mlt_cache_item mlt_cache_get(mlt_cache cache, void *object); MLT_EXPORT void mlt_cache_put_frame(mlt_cache cache, mlt_frame frame); MLT_EXPORT void mlt_cache_put_frame_audio(mlt_cache cache, mlt_frame frame); MLT_EXPORT void mlt_cache_put_frame_image(mlt_cache cache, mlt_frame frame); MLT_EXPORT mlt_frame mlt_cache_get_frame(mlt_cache cache, mlt_position position); #endif mlt-7.38.0/src/framework/mlt_chain.c000664 000000 000000 00000051732 15172202314 017260 0ustar00rootroot000000 000000 /** * \file mlt_chain.c * \brief link service class * \see mlt_chain_s * * Copyright (C) 2020-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_chain.h" #include "mlt_factory.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_tokeniser.h" #include #include #include #include /** \brief private service definition */ typedef struct { int link_count; int link_size; mlt_link *links; mlt_producer source; mlt_profile source_profile; mlt_properties source_parameters; mlt_producer begin; mlt_link frc; int relink_required; } mlt_chain_base; /* Forward references to static methods. */ static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int index); static int producer_probe(mlt_producer parent); static void relink_chain(mlt_chain self); static void chain_property_changed(mlt_service owner, mlt_chain self, char *name); static void source_property_changed(mlt_service owner, mlt_chain self, char *name); /** Construct a chain. * * \public \memberof mlt_chain_s * \return the new chain */ mlt_chain mlt_chain_init(mlt_profile profile) { mlt_chain self = calloc(1, sizeof(struct mlt_chain_s)); if (self != NULL) { mlt_producer producer = &self->parent; if (mlt_producer_init(producer, self) == 0) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_set(properties, "mlt_type", "chain"); mlt_properties_clear(properties, "resource"); mlt_properties_clear(properties, "mlt_service"); mlt_properties_clear(properties, "in"); mlt_properties_clear(properties, "out"); mlt_properties_clear(properties, "length"); producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) mlt_chain_close; producer->close_object = self; mlt_properties_set_data(properties, "mlt_producer_probe", producer_probe, 0, NULL, NULL); mlt_service_set_profile(MLT_CHAIN_SERVICE(self), profile); // Generate local space self->local = calloc(1, sizeof(mlt_chain_base)); mlt_chain_base *base = self->local; base->source_profile = NULL; // Listen to property changes to pass along to the source mlt_events_listen(MLT_CHAIN_PROPERTIES(self), self, "property-changed", (mlt_listener) chain_property_changed); } else { free(self); self = NULL; } } return self; } /** Set the source producer. * * \public \memberof mlt_chain_s * \param self a chain * \param source the new source producer */ void mlt_chain_set_source(mlt_chain self, mlt_producer source) { int error = self == NULL || source == NULL; if (error == 0) { mlt_chain_base *base = self->local; mlt_properties source_properties = MLT_PRODUCER_PROPERTIES(source); int n = 0; int i = 0; // Clean up from previous source mlt_producer_close(base->source); mlt_properties_close(base->source_parameters); mlt_profile_close(base->source_profile); // Save the source producer base->source = source; mlt_properties_inc_ref(source_properties); // Save the native source producer frame rate base->source_profile = mlt_profile_clone(mlt_service_profile(MLT_CHAIN_SERVICE(self))); mlt_frame frame = NULL; if (!mlt_properties_exists(source_properties, "meta.media.frame_rate_num") || !mlt_properties_exists(source_properties, "meta.media.frame_rate_den")) { mlt_service_get_frame(MLT_PRODUCER_SERVICE(source), &frame, 0); mlt_frame_close(frame); } if (mlt_properties_get_int(source_properties, "meta.media.frame_rate_num") > 0 && mlt_properties_get_int(source_properties, "meta.media.frame_rate_den") > 0) { base->source_profile->frame_rate_num = mlt_properties_get_int(source_properties, "meta.media.frame_rate_num"); base->source_profile->frame_rate_den = mlt_properties_get_int(source_properties, "meta.media.frame_rate_den"); } // Create a list of all parameters used by the source producer so that // they can be passed between the source producer and this chain. base->source_parameters = mlt_properties_new(); mlt_repository repository = mlt_factory_repository(); char *source_metadata_name = strdup(mlt_properties_get(source_properties, "mlt_service")); // If the service name ends in "-novalidate", then drop the ending and search for the metadata of the main service. // E.g. "avformat-novalidate" -> "avformat" char *novalidate_ptr = strstr(source_metadata_name, "-novalidate"); if (novalidate_ptr) *novalidate_ptr = '\0'; mlt_properties source_metadata = mlt_repository_metadata(repository, mlt_service_producer_type, source_metadata_name); free(source_metadata_name); if (source_metadata) { mlt_properties params = (mlt_properties) mlt_properties_get_data(source_metadata, "parameters", NULL); if (params) { n = mlt_properties_count(params); for (i = 0; i < n; i++) { mlt_properties param = (mlt_properties) mlt_properties_get_data(params, mlt_properties_get_name(params, i), NULL); char *identifier = mlt_properties_get(param, "identifier"); if (identifier) { // Set the value to 1 to indicate the parameter exists. mlt_properties_set_int(base->source_parameters, identifier, 1); } } } } // Pass parameters and properties from the source producer to this chain. // Some properties may have been set during source initialization. n = mlt_properties_count(source_properties); mlt_events_block(MLT_CHAIN_PROPERTIES(self), self); for (i = 0; i < n; i++) { char *name = mlt_properties_get_name(source_properties, i); if (mlt_properties_get_int(base->source_parameters, name) || !strcmp(name, "mlt_service") || !strcmp(name, "_mlt_service_hidden") || !strcmp(name, "seekable") || !strcmp(name, "creation_time") || !strncmp(name, "meta.", 5)) { mlt_properties_pass_property(MLT_CHAIN_PROPERTIES(self), source_properties, name); } } // If a length has not been specified for this chain, copy in/out/length from the source producer if (!mlt_producer_get_length(MLT_CHAIN_PRODUCER(self))) { mlt_properties_set_position(MLT_CHAIN_PROPERTIES(self), "length", mlt_producer_get_length(base->source)); mlt_producer_set_in_and_out(MLT_CHAIN_PRODUCER(self), mlt_producer_get_in(base->source), mlt_producer_get_out(base->source)); } mlt_events_unblock(MLT_CHAIN_PROPERTIES(self), self); // Monitor property changes from the source to pass to the chain. mlt_events_listen(source_properties, self, "property-changed", (mlt_listener) source_property_changed); // This chain will control the speed and in/out mlt_producer_set_speed(base->source, 0.0); // Approximate infinite length mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(base->source), "length", 0x7fffffff); mlt_producer_set_in_and_out(base->source, 0, mlt_producer_get_length(base->source) - 1); // Reconfigure the chain base->relink_required = 1; mlt_events_fire(MLT_CHAIN_PROPERTIES(self), "chain-changed", mlt_event_data_none()); } } /** Get the source producer. * * \public \memberof mlt_chain_s * \param self a chain * \return the source producer */ extern mlt_producer mlt_chain_get_source(mlt_chain self) { mlt_producer source = NULL; if (self && self->local) { mlt_chain_base *base = self->local; source = base->source; } return source; } /** Attach a link. * * \public \memberof mlt_chain_s * \param self a chain * \param link the link to attach * \return true if there was an error */ int mlt_chain_attach(mlt_chain self, mlt_link link) { int error = self == NULL || link == NULL; if (error == 0) { int i = 0; mlt_chain_base *base = self->local; for (i = 0; error == 0 && i < base->link_count; i++) if (base->links[i] == link) error = 1; if (error == 0) { if (base->link_count == base->link_size) { base->link_size += 10; base->links = realloc(base->links, base->link_size * sizeof(mlt_link)); } if (base->links != NULL) { mlt_properties_inc_ref(MLT_LINK_PROPERTIES(link)); mlt_properties_set_data(MLT_LINK_PROPERTIES(link), "chain", self, 0, NULL, NULL); base->links[base->link_count++] = link; base->relink_required = 1; mlt_events_fire(MLT_CHAIN_PROPERTIES(self), "chain-changed", mlt_event_data_none()); } else { error = 2; } } } return error; } /** Detach a link. * * \public \memberof mlt_chain_s * \param self a chain * \param link the link to detach * \return true if there was an error */ int mlt_chain_detach(mlt_chain self, mlt_link link) { int error = self == NULL || link == NULL; if (error == 0) { int i = 0; mlt_chain_base *base = self->local; for (i = 0; i < base->link_count; i++) if (base->links[i] == link) break; if (i < base->link_count) { base->links[i] = NULL; for (i++; i < base->link_count; i++) base->links[i - 1] = base->links[i]; base->link_count--; mlt_link_close(link); base->relink_required = 1; mlt_events_fire(MLT_CHAIN_PROPERTIES(self), "chain-changed", mlt_event_data_none()); } } return error; } /** Get the number of links attached. * * \public \memberof mlt_chain_s * \param self a chain * \return the number of attached links or -1 if there was an error */ int mlt_chain_link_count(mlt_chain self) { int result = -1; if (self) { mlt_chain_base *base = self->local; result = base->link_count; } return result; } /** Reorder the attached links. * * \public \memberof mlt_chain_s * \param self a chain * \param from the current index value of the link to move * \param to the new index value for the link specified in \p from * \return true if there was an error */ int mlt_chain_move_link(mlt_chain self, int from, int to) { int error = -1; if (self) { mlt_chain_base *base = self->local; if (from < 0) from = 0; if (from >= base->link_count) from = base->link_count - 1; if (to < 0) to = 0; if (to >= base->link_count) to = base->link_count - 1; if (from != to && base->link_count > 1) { mlt_link link = base->links[from]; int i; if (from > to) { for (i = from; i > to; i--) base->links[i] = base->links[i - 1]; } else { for (i = from; i < to; i++) base->links[i] = base->links[i + 1]; } base->links[to] = link; base->relink_required = 1; mlt_events_fire(MLT_CHAIN_PROPERTIES(self), "chain-changed", mlt_event_data_none()); error = 0; } } return error; } /** Retrieve an attached link. * * \public \memberof mlt_chain_s * \param self a chain * \param index which one of potentially multiple links * \return the link or null if there was an error */ mlt_link mlt_chain_link(mlt_chain self, int index) { mlt_link link = NULL; if (self != NULL) { mlt_chain_base *base = self->local; if (index >= 0 && index < base->link_count) link = base->links[index]; } return link; } /** Close the chain and free its resources. * * \public \memberof mlt_chain_s * \param self a chain */ void mlt_chain_close(mlt_chain self) { if (self != NULL && mlt_properties_dec_ref(MLT_CHAIN_PROPERTIES(self)) <= 0) { int i = 0; mlt_chain_base *base = self->local; mlt_events_block(MLT_CHAIN_PROPERTIES(self), self); for (i = 0; i < base->link_count; i++) mlt_link_close(base->links[i]); free(base->links); mlt_producer_close(base->source); mlt_properties_close(base->source_parameters); mlt_profile_close(base->source_profile); mlt_link_close(base->frc); free(base); self->parent.close = NULL; mlt_producer_close(&self->parent); free(self); } } /** Attach normalizer links. * * \public \memberof mlt_chain_s * \param self a chain */ extern void mlt_chain_attach_normalizers(mlt_chain self) { if (!self) return; if (mlt_chain_link_count(self) && mlt_properties_get_int(MLT_LINK_PROPERTIES(mlt_chain_link(self, 0)), "_loader")) { // Chain already has normalizers return; } mlt_chain_base *base = self->local; // Remove any normalizer filters on the source for (int i = 0; i < mlt_service_filter_count(MLT_PRODUCER_SERVICE(base->source)); i++) { mlt_filter filter = mlt_service_filter(MLT_PRODUCER_SERVICE(base->source), i); if (filter && mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "_loader") == 1) { mlt_service_detach(MLT_PRODUCER_SERVICE(base->source), filter); i--; } } // Remove any normalizer filters on this chain for (int i = 0; i < mlt_service_filter_count(MLT_CHAIN_SERVICE(self)); i++) { mlt_filter filter = mlt_service_filter(MLT_CHAIN_SERVICE(self), i); if (filter && mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "_loader") == 1) { mlt_service_detach(MLT_CHAIN_SERVICE(self), filter); i--; } } static mlt_properties normalisers = NULL; mlt_tokeniser tokenizer = mlt_tokeniser_init(); // We only need to load the normalising properties once if (normalisers == NULL) { char temp[PATH_MAX]; snprintf(temp, PATH_MAX, "%s/chain_normalizers.ini", mlt_environment("MLT_DATA")); normalisers = mlt_properties_load(temp); if (normalisers) mlt_factory_register_for_clean_up(normalisers, (mlt_destructor) mlt_properties_close); else mlt_log_error(NULL, "Failed to load normalizers from %s\n", temp); } // Apply normalisers int norm_count = 0; for (int i = 0; i < mlt_properties_count(normalisers); i++) { char *value = mlt_properties_get_value(normalisers, i); mlt_tokeniser_parse_new(tokenizer, value, ","); for (int j = 0; j < mlt_tokeniser_count(tokenizer); j++) { char *id = strdup(mlt_tokeniser_get_string(tokenizer, j)); char *arg = strchr(id, ':'); if (arg != NULL) *arg++ = '\0'; mlt_link link = mlt_factory_link(id, arg); free(id); if (link) { mlt_properties_set_int(MLT_LINK_PROPERTIES(link), "_loader", 1); mlt_chain_attach(self, link); mlt_chain_move_link(self, mlt_chain_link_count(self) - 1, norm_count); norm_count++; break; } } } mlt_tokeniser_close(tokenizer); } static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int index) { int result = 1; if (parent && parent->child) { mlt_chain self = parent->child; if (self) { mlt_chain_base *base = self->local; if (base->relink_required) { relink_chain(self); base->relink_required = 0; } mlt_producer_seek(base->begin, mlt_producer_frame(parent)); result = mlt_service_get_frame(MLT_PRODUCER_SERVICE(base->begin), frame, index); mlt_producer_prepare_next(parent); } } return result; } static int producer_probe(mlt_producer parent) { if (parent && parent->child) { mlt_chain self = parent->child; mlt_chain_base *base = self->local; if (base && base->source) { return mlt_producer_probe(base->source); } } return 1; } static void relink_chain(mlt_chain self) { mlt_chain_base *base = self->local; mlt_profile profile = mlt_service_profile(MLT_CHAIN_SERVICE(self)); int i = 0; int frc_link = 0; if (!base->source) { return; } mlt_link_close(base->frc); base->frc = NULL; for (i = 0; i < base->link_count; i++) { if (mlt_properties_get_int(MLT_LINK_PROPERTIES(base->links[i]), "_frc")) { // A link will perform frame rate conversion. frc_link = 1; break; } } if (mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(base->source), "static_profile")) { // The producer does not support changing profile if (!frc_link) { // There are no links that can convert frame rate double chain_fps = mlt_producer_get_fps(MLT_CHAIN_PRODUCER(self)); double source_fps = mlt_producer_get_fps(base->source); if (fabs(chain_fps - source_fps) > 0.001) { // The link frame rate does not match the chain frame rate // A time remap link with no parameters can perform frame rate conversion base->frc = mlt_factory_link("timeremap", NULL); } } } else if (frc_link && base->source_profile) { // Set the producer to be in native frame rate mlt_service_set_profile(MLT_PRODUCER_SERVICE(base->source), base->source_profile); } else { // The producer can operate in the final frame rate. mlt_service_set_profile(MLT_PRODUCER_SERVICE(base->source), profile); } base->begin = base->source; if (base->frc) { mlt_link_connect_next(base->frc, base->begin, profile); base->begin = MLT_LINK_PRODUCER(base->frc); } for (i = 0; i < base->link_count; i++) { mlt_link_connect_next(base->links[i], base->begin, profile); base->begin = MLT_LINK_PRODUCER(base->links[i]); } } static void chain_property_changed(mlt_service owner, mlt_chain self, char *name) { mlt_chain_base *base = self->local; if (!base->source) return; if (mlt_properties_get_int(base->source_parameters, name) || !strncmp(name, "meta.", 5)) { // Pass parameter changes from this chain to the encapsulated source producer. mlt_properties chain_properties = MLT_CHAIN_PROPERTIES(self); mlt_properties source_properties = MLT_PRODUCER_PROPERTIES(base->source); mlt_events_block(source_properties, self); mlt_properties_pass_property(source_properties, chain_properties, name); mlt_events_unblock(source_properties, self); } } static void source_property_changed(mlt_service owner, mlt_chain self, char *name) { mlt_chain_base *base = self->local; if (mlt_properties_get_int(base->source_parameters, name) || !strncmp(name, "meta.", 5)) { // The source producer might change its own parameters. // Pass those changes to this producer. mlt_properties chain_properties = MLT_CHAIN_PROPERTIES(self); mlt_properties source_properties = MLT_PRODUCER_PROPERTIES(base->source); mlt_events_block(chain_properties, self); mlt_properties_pass_property(chain_properties, source_properties, name); mlt_events_unblock(chain_properties, self); } } mlt-7.38.0/src/framework/mlt_chain.h000664 000000 000000 00000004042 15172202314 017255 0ustar00rootroot000000 000000 /** * \file mlt_chain.h * \brief chain service class * \see mlt_chain_s * * Copyright (C) 2020-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_CHAIN_H #define MLT_CHAIN_H #include "mlt_export.h" #include "mlt_link.h" #include "mlt_producer.h" /** \brief Chain class * * The chain is a producer class that can connect multiple link producers in a sequence. * * \extends mlt_producer_s */ struct mlt_chain_s { struct mlt_producer_s parent; void *local; /**< \private instance object */ }; #define MLT_CHAIN_PRODUCER(chain) (&(chain)->parent) #define MLT_CHAIN_SERVICE(chain) MLT_PRODUCER_SERVICE(MLT_CHAIN_PRODUCER(chain)) #define MLT_CHAIN_PROPERTIES(chain) MLT_SERVICE_PROPERTIES(MLT_CHAIN_SERVICE(chain)) MLT_EXPORT mlt_chain mlt_chain_init(mlt_profile); MLT_EXPORT void mlt_chain_set_source(mlt_chain self, mlt_producer source); MLT_EXPORT mlt_producer mlt_chain_get_source(mlt_chain self); MLT_EXPORT int mlt_chain_attach(mlt_chain self, mlt_link link); MLT_EXPORT int mlt_chain_detach(mlt_chain self, mlt_link link); MLT_EXPORT int mlt_chain_link_count(mlt_chain self); MLT_EXPORT int mlt_chain_move_link(mlt_chain self, int from, int to); MLT_EXPORT mlt_link mlt_chain_link(mlt_chain self, int index); MLT_EXPORT void mlt_chain_close(mlt_chain self); MLT_EXPORT void mlt_chain_attach_normalizers(mlt_chain self); #endif mlt-7.38.0/src/framework/mlt_consumer.c000664 000000 000000 00000206512 15172202314 020027 0ustar00rootroot000000 000000 /** * \file mlt_consumer.c * \brief abstraction for all consumer services * \see mlt_consumer_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_consumer.h" #include "mlt_factory.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_producer.h" #include "mlt_profile.h" #include #include #include #include #ifdef _MSC_VER #include #else #include #endif /** Define this if you want an automatic deinterlace (if necessary) when the * consumer's producer is not running at normal speed. */ #undef DEINTERLACE_ON_NOT_NORMAL_SPEED /** This is not the ideal place for this, but it is needed by VDPAU as well. */ MLT_EXPORT pthread_mutex_t mlt_sdl_mutex = PTHREAD_MUTEX_INITIALIZER; /** mlt_frame_s::is_processing can not be made atomic, so protect it with a mutex. */ MLT_EXPORT pthread_mutex_t mlt_frame_processing_mutex = PTHREAD_MUTEX_INITIALIZER; /** \brief private members of mlt_consumer */ typedef struct { int real_time; atomic_int ahead; int preroll; mlt_image_format image_format; mlt_audio_format audio_format; mlt_deque queue; void *ahead_thread; pthread_mutex_t queue_mutex; pthread_cond_t queue_cond; pthread_mutex_t put_mutex; pthread_cond_t put_cond; mlt_frame put; int put_active; mlt_event event_listener; mlt_position position; pthread_mutex_t position_mutex; int is_purge; int aud_counter; double fps; int channels; int frequency; atomic_int speed; /* additional fields added for the parallel work queue */ mlt_deque worker_threads; pthread_mutex_t done_mutex; pthread_cond_t done_cond; int consecutive_dropped; int consecutive_rendered; int process_head; atomic_int started; pthread_t *threads; /**< used to deallocate all threads */ } consumer_private; static void mlt_consumer_property_changed(mlt_properties owner, mlt_consumer self, mlt_event_data); static void apply_profile_properties(mlt_consumer self, mlt_profile profile, mlt_properties properties); static void on_consumer_frame_show(mlt_properties owner, mlt_consumer self, mlt_event_data); static void mlt_thread_create(mlt_consumer self, mlt_thread_function_t function); static void mlt_thread_join(mlt_consumer self); static void consumer_read_ahead_start(mlt_consumer self); /** Initialize a consumer service. * * \public \memberof mlt_consumer_s * \param self the consumer to initialize * \param child a pointer to the object for the subclass * \param profile the \p mlt_profile_s to use (optional but recommended, * uses the environment variable MLT if self is NULL) * \return true if there was an error */ int mlt_consumer_init(mlt_consumer self, void *child, mlt_profile profile) { int error = 0; memset(self, 0, sizeof(struct mlt_consumer_s)); self->child = child; consumer_private *priv = self->local = calloc(1, sizeof(consumer_private)); error = mlt_service_init(&self->parent, self); if (error == 0) { // Get the properties from the service mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); // Apply profile to properties if (profile == NULL) { // Normally the application creates the profile and controls its lifetime // This is the fallback exception handling profile = mlt_profile_init(NULL); mlt_properties self_props = MLT_CONSUMER_PROPERTIES(self); mlt_properties_set_data(self_props, "_profile", profile, 0, (mlt_destructor) mlt_profile_close, NULL); } apply_profile_properties(self, profile, properties); mlt_properties_set(properties, "mlt_type", "consumer"); // Default rescaler for all consumers mlt_properties_set(properties, "rescale", "bilinear"); // Default read ahead buffer size mlt_properties_set_int(properties, "buffer", 25); mlt_properties_set_int(properties, "drop_max", 5); // Default audio frequency and channels mlt_properties_set_int(properties, "frequency", 48000); mlt_properties_set_int(properties, "channels", 2); // Default of all consumers is real time mlt_properties_set_int(properties, "real_time", 1); // Default to environment test card mlt_properties_set(properties, "test_card", mlt_environment("MLT_TEST_CARD")); // Hmm - default all consumers to yuv422 with s16 :-/ priv->image_format = mlt_image_yuv422; priv->audio_format = mlt_audio_s16; mlt_events_register(properties, "consumer-frame-show"); mlt_events_register(properties, "consumer-frame-render"); mlt_events_register(properties, "consumer-thread-started"); mlt_events_register(properties, "consumer-thread-stopped"); mlt_events_register(properties, "consumer-stopping"); mlt_events_register(properties, "consumer-stopped"); mlt_events_register(properties, "consumer-thread-create"); mlt_events_register(properties, "consumer-thread-join"); mlt_events_listen(properties, self, "consumer-frame-show", (mlt_listener) on_consumer_frame_show); // Register a property-changed listener to handle the profile property - // subsequent properties can override the profile priv->event_listener = mlt_events_listen(properties, self, "property-changed", (mlt_listener) mlt_consumer_property_changed); // Create the push mutex and condition pthread_mutex_init(&priv->put_mutex, NULL); pthread_cond_init(&priv->put_cond, NULL); pthread_mutex_init(&priv->position_mutex, NULL); } return error; } /** Convert the profile into properties on the consumer. * * \private \memberof mlt_consumer_s * \param self a consumer * \param profile a profile * \param properties a properties list (typically, the consumer's) */ static void apply_profile_properties(mlt_consumer self, mlt_profile profile, mlt_properties properties) { consumer_private *priv = self->local; mlt_event_block(priv->event_listener); mlt_properties_set_double(properties, "fps", mlt_profile_fps(profile)); mlt_properties_set_int(properties, "frame_rate_num", profile->frame_rate_num); mlt_properties_set_int(properties, "frame_rate_den", profile->frame_rate_den); mlt_properties_set_int(properties, "width", profile->width); mlt_properties_set_int(properties, "height", profile->height); mlt_properties_set_int(properties, "progressive", profile->progressive); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(properties, "sample_aspect_num", profile->sample_aspect_num); mlt_properties_set_int(properties, "sample_aspect_den", profile->sample_aspect_den); mlt_properties_set_double(properties, "display_ratio", mlt_profile_dar(profile)); mlt_properties_set_int(properties, "display_aspect_num", profile->display_aspect_num); mlt_properties_set_int(properties, "display_aspect_den", profile->display_aspect_den); mlt_properties_set_int(properties, "colorspace", profile->colorspace); mlt_event_unblock(priv->event_listener); } /** The property-changed event listener * * \private \memberof mlt_consumer_s * \param owner the events object * \param self the consumer * \param event_data the event data containing the name of the property that changed */ static void mlt_consumer_property_changed(mlt_properties owner, mlt_consumer self, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "mlt_profile")) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); // Get the current profile mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); // Load the new profile mlt_profile new_profile = mlt_profile_init(mlt_properties_get(properties, name)); if (new_profile) { // Copy the profile if (profile != NULL) { free(profile->description); memcpy(profile, new_profile, sizeof(struct mlt_profile_s)); profile->description = strdup(new_profile->description); } else { profile = new_profile; } // Apply to properties apply_profile_properties(self, profile, properties); mlt_profile_close(new_profile); } } else if (!strcmp(name, "frame_rate_num")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) { profile->frame_rate_num = mlt_properties_get_int(properties, "frame_rate_num"); mlt_properties_set_double(properties, "fps", mlt_profile_fps(profile)); } } else if (!strcmp(name, "frame_rate_den")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) { profile->frame_rate_den = mlt_properties_get_int(properties, "frame_rate_den"); mlt_properties_set_double(properties, "fps", mlt_profile_fps(profile)); } } else if (!strcmp(name, "width")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) profile->width = mlt_properties_get_int(properties, "width"); } else if (!strcmp(name, "height")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) profile->height = mlt_properties_get_int(properties, "height"); } else if (!strcmp(name, "progressive")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) profile->progressive = mlt_properties_get_int(properties, "progressive"); } else if (!strcmp(name, "sample_aspect_num")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) { profile->sample_aspect_num = mlt_properties_get_int(properties, "sample_aspect_num"); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); } } else if (!strcmp(name, "sample_aspect_den")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) { profile->sample_aspect_den = mlt_properties_get_int(properties, "sample_aspect_den"); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); } } else if (!strcmp(name, "display_aspect_num")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) { profile->display_aspect_num = mlt_properties_get_int(properties, "display_aspect_num"); mlt_properties_set_double(properties, "display_ratio", mlt_profile_dar(profile)); } } else if (!strcmp(name, "display_aspect_den")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) { profile->display_aspect_den = mlt_properties_get_int(properties, "display_aspect_den"); mlt_properties_set_double(properties, "display_ratio", mlt_profile_dar(profile)); } } else if (!strcmp(name, "colorspace")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (profile) profile->colorspace = mlt_properties_get_int(properties, "colorspace"); } else if (!strcmp(name, "mlt_color_trc")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); mlt_properties_clear(properties, "_ct_filter"); } } /** A listener on the consumer-frame-show event * * Saves the position of the frame shown. * * \private \memberof mlt_consumer_s * \param owner the events object * \param consumer the consumer on which this event occurred * \param event_data the event data containing the frame that was shown */ static void on_consumer_frame_show(mlt_properties owner, mlt_consumer consumer, mlt_event_data event_data) { mlt_frame frame = mlt_event_data_to_frame(event_data); if (frame) { consumer_private *priv = consumer->local; pthread_mutex_lock(&priv->position_mutex); priv->position = mlt_frame_get_position(frame); pthread_mutex_unlock(&priv->position_mutex); } } /** Create a new consumer. * * \public \memberof mlt_consumer_s * \param profile a profile (optional, but recommended) * \return a new consumer */ mlt_consumer mlt_consumer_new(mlt_profile profile) { // Create the memory for the structure mlt_consumer self = malloc(sizeof(struct mlt_consumer_s)); // Initialise it if (self != NULL && mlt_consumer_init(self, NULL, profile) == 0) { // Return it return self; } else { free(self); return NULL; } } /** Get the parent service object. * * \public \memberof mlt_consumer_s * \param self a consumer * \return the parent service class * \see MLT_CONSUMER_SERVICE */ mlt_service mlt_consumer_service(mlt_consumer self) { return self != NULL ? &self->parent : NULL; } /** Get the consumer properties. * * \public \memberof mlt_consumer_s * \param self a consumer * \return the consumer's properties list * \see MLT_CONSUMER_PROPERTIES */ mlt_properties mlt_consumer_properties(mlt_consumer self) { return self != NULL ? MLT_SERVICE_PROPERTIES(&self->parent) : NULL; } /** Connect the consumer to the producer. * * \public \memberof mlt_consumer_s * \param self a consumer * \param producer a producer * \return > 0 warning, == 0 success, < 0 serious error, * 1 = this service does not accept input, * 2 = the producer is invalid, * 3 = the producer is already registered with this consumer */ int mlt_consumer_connect(mlt_consumer self, mlt_service producer) { return mlt_service_connect_producer(&self->parent, producer, 0); } /** Set the audio format to use in the render thread. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void set_audio_format(mlt_consumer self) { // Get the audio format to use for rendering threads. consumer_private *priv = self->local; mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); const char *format = mlt_properties_get(properties, "mlt_audio_format"); if (format) { if (!strcmp(format, "none")) priv->audio_format = mlt_audio_none; else if (!strcmp(format, "s32")) priv->audio_format = mlt_audio_s32; else if (!strcmp(format, "s32le")) priv->audio_format = mlt_audio_s32le; else if (!strcmp(format, "float")) priv->audio_format = mlt_audio_float; else if (!strcmp(format, "f32le")) priv->audio_format = mlt_audio_f32le; else if (!strcmp(format, "u8")) priv->audio_format = mlt_audio_u8; } } /** Set the image format to use in render threads. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void set_image_format(mlt_consumer self) { // Get the image format to use for rendering threads. consumer_private *priv = self->local; mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); const char *format = mlt_properties_get(properties, "mlt_image_format"); if (format) { priv->image_format = mlt_image_format_id(format); if (mlt_image_invalid == priv->image_format) priv->image_format = mlt_image_yuv422; // mlt_image_movit is for internal use only. // Remapping it glsl_texture prevents breaking existing apps // using the legacy "glsl" name. else if (mlt_image_movit == priv->image_format) priv->image_format = mlt_image_opengl_texture; } } /** Start the consumer. * * \public \memberof mlt_consumer_s * \param self a consumer * \return true if there was an error */ int mlt_consumer_start(mlt_consumer self) { if (!self) { return 1; } int error = 0; if (!mlt_consumer_is_stopped(self)) return error; consumer_private *priv = self->local; mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(self)); if (!mlt_profile_is_valid(profile)) { return 1; } // Stop listening to the property-changed event mlt_event_block(priv->event_listener); // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); // Determine if there's a test card producer char *test_card = mlt_properties_get(properties, "test_card"); // Just to make sure nothing is hanging around... pthread_mutex_lock(&priv->put_mutex); priv->put = NULL; priv->put_active = 1; pthread_mutex_unlock(&priv->put_mutex); // Deal with it now. if (test_card != NULL) { if (mlt_properties_get_data(properties, "test_card_producer", NULL) == NULL) { // Create a test card producer mlt_producer producer = mlt_factory_producer(profile, NULL, test_card); // Do we have a producer if (producer != NULL) { // Test card should loop I guess... mlt_properties_set(MLT_PRODUCER_PROPERTIES(producer), "eof", "loop"); //mlt_producer_set_speed( producer, 0 ); //mlt_producer_set_in_and_out( producer, 0, 0 ); // Set the test card on the consumer mlt_properties_set_data(properties, "test_card_producer", producer, 0, (mlt_destructor) mlt_producer_close, NULL); } } } else { // Allow the hash table to speed things up mlt_properties_set_data(properties, "test_card_producer", NULL, 0, NULL, NULL); } // The profile could have changed between a stop and a restart. apply_profile_properties(self, profile, properties); // Set the frame duration in microseconds for the frame-dropping heuristic int frame_rate_num = mlt_properties_get_int(properties, "frame_rate_num"); int frame_rate_den = mlt_properties_get_int(properties, "frame_rate_den"); int frame_duration = 0; if (frame_rate_num && frame_rate_den) { frame_duration = 1000000.0 / frame_rate_num * frame_rate_den; } mlt_properties_set_int(properties, "frame_duration", frame_duration); mlt_properties_set_int(properties, "drop_count", 0); // Check and run an ante command if (mlt_properties_get(properties, "ante")) if (system(mlt_properties_get(properties, "ante")) == -1) mlt_log(MLT_CONSUMER_SERVICE(self), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get(properties, "ante")); // Set the real_time preference priv->real_time = mlt_properties_get_int(properties, "real_time"); // For worker threads implementation, buffer must be at least # threads if (abs(priv->real_time) > 1 && mlt_properties_get_int(properties, "buffer") <= abs(priv->real_time)) mlt_properties_set_int(properties, "_buffer", abs(priv->real_time) + 1); // Store the parameters for audio processing. priv->aud_counter = 0; priv->fps = mlt_properties_get_double(properties, "fps"); priv->channels = mlt_properties_get_int(properties, "channels"); priv->frequency = mlt_properties_get_int(properties, "frequency"); priv->preroll = 1; #ifdef _WIN32 if (priv->real_time == 1 || priv->real_time == -1) consumer_read_ahead_start(self); #endif // Start the service if (self->start != NULL) error = self->start(self); return error; } /** An alternative method to feed frames into the consumer. * * Only valid if the consumer itself is not connected. * * \public \memberof mlt_consumer_s * \param self a consumer * \param frame a frame * \return true (ignore self for now) */ int mlt_consumer_put_frame(mlt_consumer self, mlt_frame frame) { int error = 1; // Get the service associated to the consumer mlt_service service = MLT_CONSUMER_SERVICE(self); if (mlt_service_producer(service) == NULL) { struct timeval now; struct timespec tm; consumer_private *priv = self->local; mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(self), "put_pending", 1); pthread_mutex_lock(&priv->put_mutex); while (priv->put_active && priv->put != NULL) { gettimeofday(&now, NULL); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait(&priv->put_cond, &priv->put_mutex, &tm); } mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(self), "put_pending", 0); if (priv->put_active && priv->put == NULL) priv->put = frame; else mlt_frame_close(frame); pthread_cond_broadcast(&priv->put_cond); pthread_mutex_unlock(&priv->put_mutex); } else { mlt_frame_close(frame); } return error; } /** Protected method for consumer to get frames from connected service * * \public \memberof mlt_consumer_s * \param self a consumer * \return a frame */ mlt_frame mlt_consumer_get_frame(mlt_consumer self) { // Frame to return mlt_frame frame = NULL; // Get the service associated to the consumer mlt_service service = MLT_CONSUMER_SERVICE(self); // Get the consumer properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); // Get the frame if (mlt_service_producer(service) == NULL && mlt_properties_get_int(properties, "put_mode")) { struct timeval now; struct timespec tm; consumer_private *priv = self->local; pthread_mutex_lock(&priv->put_mutex); while (priv->put_active && priv->put == NULL) { gettimeofday(&now, NULL); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait(&priv->put_cond, &priv->put_mutex, &tm); } frame = priv->put; priv->put = NULL; pthread_cond_broadcast(&priv->put_cond); pthread_mutex_unlock(&priv->put_mutex); if (frame != NULL) mlt_service_apply_filters(service, frame, 0); } else if (mlt_service_producer(service) != NULL) { mlt_service_get_frame(service, &frame, 0); } else { frame = mlt_frame_init(service); } if (frame != NULL) { // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); // Get the test card producer mlt_producer test_card = mlt_properties_get_data(properties, "test_card_producer", NULL); // Attach the test frame producer to it. if (test_card != NULL) mlt_properties_set_data(frame_properties, "test_card_producer", test_card, 0, NULL, NULL); // Pass along the interpolation and deinterlace options // TODO: get rid of consumer_deinterlace and use profile.progressive mlt_properties_set(frame_properties, "consumer.rescale", mlt_properties_get(properties, "rescale")); mlt_properties_set_int(frame_properties, "consumer.progressive", mlt_properties_get_int(properties, "progressive") | mlt_properties_get_int(properties, "deinterlace")); mlt_properties_set(frame_properties, "consumer.deinterlacer", mlt_properties_get(properties, "deinterlacer") ? mlt_properties_get(properties, "deinterlacer") : mlt_properties_get(properties, "deinterlace_method")); mlt_properties_set_int(frame_properties, "consumer.top_field_first", mlt_properties_get_int(properties, "top_field_first")); mlt_properties_set(frame_properties, "consumer.color_trc", mlt_properties_get(properties, "color_trc")); mlt_properties_set(frame_properties, "consumer.mlt_color_trc", mlt_properties_get(properties, "mlt_color_trc")); mlt_properties_set(frame_properties, "consumer.channel_layout", mlt_properties_get(properties, "channel_layout")); mlt_properties_set(frame_properties, "consumer.color_range", mlt_properties_get(properties, "color_range")); mlt_properties_set(frame_properties, "consumer.scale", mlt_properties_get(properties, "scale")); if (mlt_properties_get(properties, "mlt_color_trc")) { // Add a normalize filter to convert the mlt_color_trc to color_trc mlt_filter ct_filter = (mlt_filter) mlt_properties_get_data(properties, "_ct_filter", NULL); if (!ct_filter) { mlt_profile profile = mlt_service_profile(service); ct_filter = mlt_factory_filter(profile, "color_transform", NULL); if (ct_filter) { mlt_properties cs_properties = MLT_FILTER_PROPERTIES(ct_filter); const char *color_trc_str = mlt_properties_get(properties, "color_trc"); mlt_color_trc trc = mlt_image_color_trc_id(color_trc_str); if (trc == mlt_color_trc_none) trc = mlt_image_default_trc(profile->colorspace); mlt_properties_set_int(cs_properties, "force_trc", trc); mlt_properties_set_data(properties, "_ct_filter", ct_filter, 0, (mlt_destructor) mlt_filter_close, NULL); } } if (ct_filter) { mlt_filter_process(ct_filter, frame); } } } // Return the frame return frame; } /** Compute the time difference between now and a time value. * * \private \memberof mlt_consumer_s * \param time1 a time value to be compared against now * \return the difference in microseconds */ static inline long time_difference(struct timeval *time1) { struct timeval time2; time2.tv_sec = time1->tv_sec; time2.tv_usec = time1->tv_usec; gettimeofday(time1, NULL); return time1->tv_sec * 1000000 + time1->tv_usec - time2.tv_sec * 1000000 - time2.tv_usec; } /** The thread procedure for asynchronously pulling frames through the service * network connected to a consumer. * * \private \memberof mlt_consumer_s * \param arg a consumer */ static void *consumer_read_ahead_thread(void *arg) { // The argument is the consumer mlt_consumer self = arg; consumer_private *priv = self->local; // Get the properties of the consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); // Get the width and height int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); // See if video is turned off int video_off = mlt_properties_get_int(properties, "video_off"); int preview_off = mlt_properties_get_int(properties, "preview_off"); int preview_format = mlt_properties_get_int(properties, "preview_format"); // Audio processing variables int samples = 0; void *audio = NULL; // See if audio is turned off int audio_off = mlt_properties_get_int(properties, "audio_off"); // General frame variable mlt_frame frame = NULL; uint8_t *image = NULL; // Time structures struct timeval ante; // Average time for get_frame and get_image int count = 0; int skipped = 0; int64_t time_process = 0; int skip_next = 0; mlt_position pos = 0; mlt_position start_pos = 0; mlt_position last_pos = 0; int frame_duration = mlt_properties_get_int(properties, "frame_duration"); int drop_max = mlt_properties_get_int(properties, "drop_max"); if (preview_off && preview_format != 0) priv->image_format = preview_format; set_audio_format(self); set_image_format(self); mlt_events_fire(properties, "consumer-thread-started", mlt_event_data_none()); // Get the first frame frame = mlt_consumer_get_frame(self); if (priv->speed != mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "_speed")) { priv->speed = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "_speed"); // get_frame might want to recalculate the minimum queue size if the speed has changed. pthread_cond_broadcast(&priv->queue_cond); } if (frame) { // Get the audio of the first frame if (!audio_off) { samples = mlt_audio_calculate_frame_samples(priv->fps, priv->frequency, priv->aud_counter++); mlt_frame_get_audio(frame, &audio, &priv->audio_format, &priv->frequency, &priv->channels, &samples); } // Get the image of the first frame if (!video_off) { mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-frame-render", mlt_event_data_from_frame(frame)); mlt_frame_get_image(frame, &image, &priv->image_format, &width, &height, 0); } // Mark as rendered mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "rendered", 1); last_pos = start_pos = pos = mlt_frame_get_position(frame); } // Get the starting time (can ignore the times above) gettimeofday(&ante, NULL); // Continue to read ahead while (priv->ahead) { // Get the maximum size of the buffer int buffer = (priv->speed == 0) ? 1 : MAX(mlt_properties_get_int(properties, "buffer"), 0) + 1; // Put the current frame into the queue pthread_mutex_lock(&priv->queue_mutex); while (priv->ahead && mlt_deque_count(priv->queue) >= buffer) pthread_cond_wait(&priv->queue_cond, &priv->queue_mutex); if (priv->is_purge) { mlt_frame_close(frame); priv->is_purge = 0; } else { mlt_deque_push_back(priv->queue, frame); } pthread_cond_broadcast(&priv->queue_cond); pthread_mutex_unlock(&priv->queue_mutex); mlt_log_timings_begin(); // Get the next frame frame = mlt_consumer_get_frame(self); mlt_log_timings_end(NULL, "mlt_consumer_get_frame"); // If there's no frame, we're probably stopped... if (frame == NULL) continue; pos = mlt_frame_get_position(frame); priv->speed = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "_speed"); // WebVfx uses this to setup a consumer-stopping event handler. mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "consumer", self, 0, NULL, NULL); // Increment the counter used for averaging processing cost count++; // Always process audio if (!audio_off) { samples = mlt_audio_calculate_frame_samples(priv->fps, priv->frequency, priv->aud_counter++); mlt_frame_get_audio(frame, &audio, &priv->audio_format, &priv->frequency, &priv->channels, &samples); } // All non-normal playback frames should be shown if (priv->speed != 1) { #ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "consumer.progressive", 1); #endif // Indicate seeking or trick-play start_pos = pos; } // If skip flag not set or frame-dropping disabled if (!skip_next || priv->real_time == -1) { if (!video_off) { // Reset width/height - could have been changed by previous mlt_frame_get_image width = mlt_properties_get_int(properties, "width"); height = mlt_properties_get_int(properties, "height"); // Get the image mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-frame-render", mlt_event_data_from_frame(frame)); mlt_log_timings_begin(); mlt_frame_get_image(frame, &image, &priv->image_format, &width, &height, 0); mlt_log_timings_end(NULL, "mlt_frame_get_image"); } // Indicate the rendered image is available. mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "rendered", 1); // Reset consecutively-skipped counter skipped = 0; } else // Skip image processing { // Increment the number of consecutively-skipped frames skipped++; // If too many (1 sec) consecutively-skipped frames if (skipped > drop_max) { // Reset cost tracker time_process = 0; count = 1; mlt_log_verbose(self, "too many frames dropped - forcing next frame\n"); } } // Get the time to process this frame int64_t time_current = time_difference(&ante); // If the current time is not suddenly some large amount if (time_current < time_process / count * 20 || !time_process || count < 5) { // Accumulate the cost for processing this frame time_process += time_current; } else { mlt_log_debug(self, "current %" PRId64 " threshold %" PRId64 " count %d\n", time_current, (int64_t) (time_process / count * 20), count); // Ignore the cost of this frame's time count--; } // Determine if we started, resumed, or seeked if (pos != last_pos + 1) { start_pos = pos; if (priv->speed) { priv->preroll = 1; } } last_pos = pos; // Do not skip the first 20% of buffer at start, resume, or seek if (pos - start_pos <= buffer / 5 + 1) { // Reset cost tracker time_process = 0; count = 1; } // Reset skip flag skip_next = 0; // Only consider skipping if the buffer level is low (or really small) if (mlt_deque_count(priv->queue) <= buffer / 5 + 1 && count > 1) { // Skip next frame if average cost exceeds frame duration. if (time_process / count > frame_duration) skip_next = 1; if (skip_next) mlt_log_debug(self, "avg usec %" PRId64 " (%" PRId64 "/%d) duration %d\n", time_process / count, time_process, count, frame_duration); } } // Remove the last frame mlt_frame_close(frame); // Wipe the queue pthread_mutex_lock(&priv->queue_mutex); while (mlt_deque_count(priv->queue)) mlt_frame_close(mlt_deque_pop_back(priv->queue)); // Close the queue mlt_deque_close(priv->queue); priv->queue = NULL; pthread_mutex_unlock(&priv->queue_mutex); mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-thread-stopped", mlt_event_data_none()); return NULL; } /** Locate the first unprocessed frame in the queue. * * When playing with realtime behavior, we do not use the true head, but * rather an adjusted process_head. The process_head is adjusted based on * the rate of frame-dropping or recovery from frame-dropping. The idea is * that as the level of frame-dropping increases to move the process_head * closer to the tail because the frames are not completing processing prior * to their playout! Then, as frames are not dropped the process_head moves * back closer to the head of the queue so that worker threads can work * ahead of the playout point (queue head). * * \private \memberof mlt_consumer_s * \param self a consumer * \return an index into the queue */ static inline int first_unprocessed_frame(mlt_consumer self) { consumer_private *priv = self->local; int index = priv->real_time <= 0 ? 0 : priv->process_head; pthread_mutex_lock(&mlt_frame_processing_mutex); while (index < mlt_deque_count(priv->queue) && MLT_FRAME(mlt_deque_peek(priv->queue, index))->is_processing) index++; pthread_mutex_unlock(&mlt_frame_processing_mutex); return index; } /** The worker thread procedure for parallel processing frames. * * \private \memberof mlt_consumer_s * \param arg a consumer */ static void *consumer_worker_thread(void *arg) { // The argument is the consumer mlt_consumer self = arg; consumer_private *priv = self->local; // Get the properties of the consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); // Get the width and height int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); mlt_image_format format = priv->image_format; // See if video is turned off int video_off = mlt_properties_get_int(properties, "video_off"); int preview_off = mlt_properties_get_int(properties, "preview_off"); int preview_format = mlt_properties_get_int(properties, "preview_format"); // General frame variable mlt_frame frame = NULL; uint8_t *image = NULL; if (preview_off && preview_format != 0) format = preview_format; mlt_events_fire(properties, "consumer-thread-started", mlt_event_data_none()); // Continue to read ahead while (priv->ahead) { // Get the next unprocessed frame from the work queue pthread_mutex_lock(&priv->queue_mutex); int index = first_unprocessed_frame(self); while (priv->ahead && index >= mlt_deque_count(priv->queue)) { mlt_log_debug(MLT_CONSUMER_SERVICE(self), "waiting in worker index = %d queue count = %d\n", index, mlt_deque_count(priv->queue)); pthread_cond_wait(&priv->queue_cond, &priv->queue_mutex); index = first_unprocessed_frame(self); } // Mark the frame for processing frame = mlt_deque_peek(priv->queue, index); if (frame) { mlt_log_debug(MLT_CONSUMER_SERVICE(self), "worker processing index = %d frame " MLT_POSITION_FMT " queue count = %d\n", index, mlt_frame_get_position(frame), mlt_deque_count(priv->queue)); pthread_mutex_lock(&mlt_frame_processing_mutex); frame->is_processing = 1; pthread_mutex_unlock(&mlt_frame_processing_mutex); mlt_properties_inc_ref(MLT_FRAME_PROPERTIES(frame)); } pthread_mutex_unlock(&priv->queue_mutex); // If there's no frame, we're probably stopped... if (frame == NULL) continue; // WebVfx uses this to setup a consumer-stopping event handler. mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "consumer", self, 0, NULL, NULL); #ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED // All non normal playback frames should be shown if (mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "_speed") != 1) mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "consumer.progressive", 1); #endif // Get the image if (!video_off) { // Fetch width/height again width = mlt_properties_get_int(properties, "width"); height = mlt_properties_get_int(properties, "height"); mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-frame-render", mlt_event_data_from_frame(frame)); mlt_frame_get_image(frame, &image, &format, &width, &height, 0); } mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "rendered", 1); mlt_frame_close(frame); // Tell a waiting thread (non-realtime main consumer thread) that we are done. pthread_mutex_lock(&priv->done_mutex); pthread_cond_broadcast(&priv->done_cond); pthread_mutex_unlock(&priv->done_mutex); } return NULL; } /** Start the read/render thread. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void consumer_read_ahead_start(mlt_consumer self) { consumer_private *priv = self->local; if (priv->started) return; // We're running now priv->ahead = 1; // Create the frame queue priv->queue = mlt_deque_init(); // Create the queue mutex pthread_mutex_init(&priv->queue_mutex, NULL); // Create the condition pthread_cond_init(&priv->queue_cond, NULL); // Create the read ahead mlt_thread_create(self, (mlt_thread_function_t) consumer_read_ahead_thread); priv->started = 1; } /** Start the worker threads. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void consumer_work_start(mlt_consumer self) { consumer_private *priv = self->local; int n = abs(priv->real_time); pthread_t *thread; if (priv->started) return; thread = calloc(1, sizeof(pthread_t) * n); // We're running now priv->ahead = 1; priv->threads = thread; // These keep track of the acceleration of frame dropping or recovery. priv->consecutive_dropped = 0; priv->consecutive_rendered = 0; // This is the position in the queue from which to look for a frame to process. // If we always start from the head, then we may likely not complete processing // before the frame is played out. priv->process_head = 0; // Create the queues priv->queue = mlt_deque_init(); priv->worker_threads = mlt_deque_init(); // Create the mutexes pthread_mutex_init(&priv->queue_mutex, NULL); pthread_mutex_init(&priv->done_mutex, NULL); // Create the conditions pthread_cond_init(&priv->queue_cond, NULL); pthread_cond_init(&priv->done_cond, NULL); // Create the read ahead if (mlt_properties_get(MLT_CONSUMER_PROPERTIES(self), "priority")) { struct sched_param priority; pthread_attr_t thread_attributes; priority.sched_priority = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(self), "priority"); pthread_attr_init(&thread_attributes); pthread_attr_setschedpolicy(&thread_attributes, SCHED_OTHER); pthread_attr_setschedparam(&thread_attributes, &priority); #if !defined(__ANDROID__) || (defined(__ANDROID__) && __ANDROID_API__ >= 28) pthread_attr_setinheritsched(&thread_attributes, PTHREAD_EXPLICIT_SCHED); #endif pthread_attr_setscope(&thread_attributes, PTHREAD_SCOPE_SYSTEM); while (n--) { if (pthread_create(thread, &thread_attributes, consumer_worker_thread, self) < 0) { if (pthread_create(thread, NULL, consumer_worker_thread, self) == 0) mlt_deque_push_back(priv->worker_threads, thread); } else { mlt_deque_push_back(priv->worker_threads, thread); } thread++; } pthread_attr_destroy(&thread_attributes); } else { while (n--) { if (pthread_create(thread, NULL, consumer_worker_thread, self) == 0) mlt_deque_push_back(priv->worker_threads, thread); thread++; } } priv->started = 1; } /** Stop the read/render thread. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void consumer_read_ahead_stop(mlt_consumer self) { consumer_private *priv = self->local; // Make sure we're running int expected = 1; if (atomic_compare_exchange_strong(&priv->started, &expected, 0)) { // Inform thread to stop priv->ahead = 0; mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-stopping", mlt_event_data_none()); // Broadcast to the condition in case it's waiting pthread_mutex_lock(&priv->queue_mutex); pthread_cond_broadcast(&priv->queue_cond); pthread_mutex_unlock(&priv->queue_mutex); // Broadcast to the put condition in case it's waiting pthread_mutex_lock(&priv->put_mutex); pthread_cond_broadcast(&priv->put_cond); pthread_mutex_unlock(&priv->put_mutex); // Join the thread mlt_thread_join(self); // Destroy the frame queue mutex pthread_mutex_destroy(&priv->queue_mutex); // Destroy the condition pthread_cond_destroy(&priv->queue_cond); } } /** Stop the worker threads. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void consumer_work_stop(mlt_consumer self) { consumer_private *priv = self->local; // Make sure we're running int expected = 1; if (atomic_compare_exchange_strong(&priv->started, &expected, 0)) { // Inform thread to stop priv->ahead = 0; mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-stopping", mlt_event_data_none()); // Broadcast to the queue condition in case it's waiting pthread_mutex_lock(&priv->queue_mutex); pthread_cond_broadcast(&priv->queue_cond); pthread_mutex_unlock(&priv->queue_mutex); // Broadcast to the put condition in case it's waiting pthread_mutex_lock(&priv->put_mutex); pthread_cond_broadcast(&priv->put_cond); pthread_mutex_unlock(&priv->put_mutex); // Broadcast to the done condition in case it's waiting pthread_mutex_lock(&priv->done_mutex); pthread_cond_broadcast(&priv->done_cond); pthread_mutex_unlock(&priv->done_mutex); // Join the threads pthread_t *thread; while ((thread = mlt_deque_pop_back(priv->worker_threads))) pthread_join(*thread, NULL); // Deallocate the array of threads free(priv->threads); // Destroy the mutexes pthread_mutex_destroy(&priv->queue_mutex); pthread_mutex_destroy(&priv->done_mutex); // Destroy the conditions pthread_cond_destroy(&priv->queue_cond); pthread_cond_destroy(&priv->done_cond); // Wipe the queues while (mlt_deque_count(priv->queue)) mlt_frame_close(mlt_deque_pop_back(priv->queue)); // Close the queues mlt_deque_close(priv->queue); mlt_deque_close(priv->worker_threads); mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-thread-stopped", mlt_event_data_none()); } } /** Flush the read/render thread's buffer. * * \public \memberof mlt_consumer_s * \param self a consumer */ void mlt_consumer_purge(mlt_consumer self) { if (self) { consumer_private *priv = self->local; pthread_mutex_lock(&priv->put_mutex); if (priv->put) { mlt_frame_close(priv->put); priv->put = NULL; } pthread_cond_broadcast(&priv->put_cond); pthread_mutex_unlock(&priv->put_mutex); if (self->purge) self->purge(self); if (priv->started && priv->real_time) pthread_mutex_lock(&priv->queue_mutex); while (priv->started && mlt_deque_count(priv->queue)) mlt_frame_close(mlt_deque_pop_back(priv->queue)); if (priv->started && priv->real_time) { priv->is_purge = 1; pthread_cond_broadcast(&priv->queue_cond); pthread_mutex_unlock(&priv->queue_mutex); if (abs(priv->real_time) > 1) { pthread_mutex_lock(&priv->done_mutex); pthread_cond_broadcast(&priv->done_cond); pthread_mutex_unlock(&priv->done_mutex); } } pthread_mutex_lock(&priv->put_mutex); if (priv->put) { mlt_frame_close(priv->put); priv->put = NULL; } pthread_cond_broadcast(&priv->put_cond); pthread_mutex_unlock(&priv->put_mutex); } } /** Use multiple worker threads and a work queue. */ static mlt_frame worker_get_frame(mlt_consumer self, mlt_properties properties) { // Frame to return mlt_frame frame = NULL; consumer_private *priv = self->local; int threads = abs(priv->real_time); int audio_off = mlt_properties_get_int(properties, "audio_off"); int samples = 0; void *audio = NULL; int buffer = mlt_properties_get_int(properties, "_buffer"); buffer = buffer > 0 ? buffer : mlt_properties_get_int(properties, "buffer"); // This is a heuristic to determine a suitable minimum buffer size for the number of threads. int headroom = (priv->real_time < 0) ? threads : (2 + threads * threads); buffer = MAX(buffer, headroom); // Start worker threads if not already started. if (!priv->ahead) { int prefill = mlt_properties_get_int(properties, "prefill"); prefill = prefill > 0 && prefill < buffer ? prefill : buffer; set_audio_format(self); set_image_format(self); consumer_work_start(self); // Fill the work queue. int i = buffer; while (priv->ahead && i--) { frame = mlt_consumer_get_frame(self); if (frame) { // Process the audio if (!audio_off) { samples = mlt_audio_calculate_frame_samples(priv->fps, priv->frequency, priv->aud_counter++); mlt_frame_get_audio(frame, &audio, &priv->audio_format, &priv->frequency, &priv->channels, &samples); } pthread_mutex_lock(&priv->queue_mutex); mlt_deque_push_back(priv->queue, frame); pthread_cond_signal(&priv->queue_cond); pthread_mutex_unlock(&priv->queue_mutex); priv->speed = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "_speed"); buffer = (priv->speed == 0) ? 1 : buffer; } } // Wait for prefill while (priv->ahead && first_unprocessed_frame(self) < prefill) { pthread_mutex_lock(&priv->done_mutex); pthread_cond_wait(&priv->done_cond, &priv->done_mutex); pthread_mutex_unlock(&priv->done_mutex); } priv->process_head = threads; } // mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "size %d done count %d work count %d process_head %d\n", // threads, first_unprocessed_frame( self ), mlt_deque_count( priv->queue ), priv->process_head ); // Feed the work queupriv->speede while (priv->ahead && mlt_deque_count(priv->queue) < buffer) { frame = mlt_consumer_get_frame(self); if (frame) { // Process the audio if (!audio_off) { samples = mlt_audio_calculate_frame_samples(priv->fps, priv->frequency, priv->aud_counter++); mlt_frame_get_audio(frame, &audio, &priv->audio_format, &priv->frequency, &priv->channels, &samples); } pthread_mutex_lock(&priv->queue_mutex); mlt_deque_push_back(priv->queue, frame); pthread_cond_signal(&priv->queue_cond); pthread_mutex_unlock(&priv->queue_mutex); priv->speed = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "_speed"); buffer = (priv->speed == 0) ? 1 : buffer; } } // Wait if not realtime. while (priv->ahead && priv->real_time < 0 && !priv->is_purge && !(mlt_properties_get_int(MLT_FRAME_PROPERTIES( MLT_FRAME(mlt_deque_peek_front(priv->queue))), "rendered"))) { pthread_mutex_lock(&priv->done_mutex); pthread_cond_wait(&priv->done_cond, &priv->done_mutex); pthread_mutex_unlock(&priv->done_mutex); } // Get the frame from the queue. pthread_mutex_lock(&priv->queue_mutex); frame = mlt_deque_pop_front(priv->queue); pthread_mutex_unlock(&priv->queue_mutex); if (!frame) { priv->is_purge = 0; return frame; } // Adapt the worker process head to the runtime conditions. if (priv->real_time > 0) { if (mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "rendered")) { priv->consecutive_dropped = 0; if (priv->process_head > threads && priv->consecutive_rendered >= priv->process_head) priv->process_head--; else priv->consecutive_rendered++; } else { priv->consecutive_rendered = 0; if (priv->process_head < buffer - threads && priv->consecutive_dropped > threads) priv->process_head++; else priv->consecutive_dropped++; } // mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "dropped %d rendered %d process_head %d\n", // priv->consecutive_dropped, priv->consecutive_rendered, priv->process_head ); // Check for too many consecutively dropped frames if (priv->consecutive_dropped > mlt_properties_get_int(properties, "drop_max")) { int orig_buffer = mlt_properties_get_int(properties, "buffer"); int prefill = mlt_properties_get_int(properties, "prefill"); mlt_log_verbose(self, "too many frames dropped - "); // If using a default low-latency buffer level (SDL) and below the limit if ((orig_buffer == 1 || prefill == 1) && buffer < (threads + 1) * 10) { // Auto-scale the buffer to compensate mlt_log_verbose(self, "increasing buffer to %d\n", buffer + threads); mlt_properties_set_int(properties, "_buffer", buffer + threads); priv->consecutive_dropped = priv->fps / 2; } else { // Tell the consumer to render it mlt_log_verbose(self, "forcing next frame\n"); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "rendered", 1); priv->consecutive_dropped = 0; } } if (!mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "rendered")) { int dropped = mlt_properties_get_int(properties, "drop_count"); mlt_properties_set_int(properties, "drop_count", ++dropped); mlt_log_verbose(MLT_CONSUMER_SERVICE(self), "dropped video frame %d\n", dropped); } } if (priv->is_purge) { priv->is_purge = 0; mlt_frame_close(frame); frame = NULL; } return frame; } /** Get the next frame from the producer connected to a consumer. * * Typically, one uses this instead of \p mlt_consumer_get_frame to make * the asynchronous/real-time behavior configurable at runtime. * You should close the frame returned from this when you are done with it. * * \public \memberof mlt_consumer_s * \param self a consumer * \return a frame */ mlt_frame mlt_consumer_rt_frame(mlt_consumer self) { // Frame to return mlt_frame frame = NULL; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); consumer_private *priv = self->local; // Check if the user has requested real time or not if (priv->real_time > 1 || priv->real_time < -1) { // see above return worker_get_frame(self, properties); } else if (priv->real_time == 1 || priv->real_time == -1) { int size = 1; int buffer = mlt_properties_get_int(properties, "buffer"); int prefill = mlt_properties_get_int(properties, "prefill"); int preroll_size = prefill > 0 && prefill < buffer ? prefill : buffer; if (priv->preroll) { #ifndef _WIN32 consumer_read_ahead_start(self); #endif if (buffer > 1 && priv->speed) size = preroll_size; priv->preroll = 0; } // Get frame from queue pthread_mutex_lock(&priv->queue_mutex); mlt_log_timings_begin(); while (priv->ahead && mlt_deque_count(priv->queue) < size) { pthread_cond_wait(&priv->queue_cond, &priv->queue_mutex); if (priv->speed == 0) { size = 1; } else if (priv->preroll) { if (buffer > 1 && priv->speed) size = preroll_size; priv->preroll = 0; } } frame = mlt_deque_pop_front(priv->queue); mlt_log_timings_end(NULL, "wait_for_frame_queue"); pthread_cond_broadcast(&priv->queue_cond); pthread_mutex_unlock(&priv->queue_mutex); if (priv->real_time == 1 && frame && !mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "rendered")) { int dropped = mlt_properties_get_int(properties, "drop_count"); mlt_properties_set_int(properties, "drop_count", ++dropped); mlt_log_verbose(MLT_CONSUMER_SERVICE(self), "dropped video frame %d\n", dropped); } } else // real_time == 0 { if (!priv->ahead) { priv->ahead = 1; mlt_events_fire(properties, "consumer-thread-started", mlt_event_data_none()); } // Get the frame in non real time frame = mlt_consumer_get_frame(self); // This isn't true, but from the consumers perspective it is if (frame != NULL) { mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "rendered", 1); // WebVfx uses this to setup a consumer-stopping event handler. mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "consumer", self, 0, NULL, NULL); } } return frame; } /** Callback for the implementation to indicate a stopped condition. * * \public \memberof mlt_consumer_s * \param self a consumer */ void mlt_consumer_stopped(mlt_consumer self) { mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(self), "running", 0); mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-stopped", mlt_event_data_none()); mlt_event_unblock(((consumer_private *) self->local)->event_listener); } /** Stop the consumer. * * \public \memberof mlt_consumer_s * \param self a consumer * \return true if there was an error */ int mlt_consumer_stop(mlt_consumer self) { if (!self) { return 1; } // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); consumer_private *priv = self->local; // Just in case... mlt_log(MLT_CONSUMER_SERVICE(self), MLT_LOG_DEBUG, "stopping put waiting\n"); pthread_mutex_lock(&priv->put_mutex); priv->put_active = 0; pthread_cond_broadcast(&priv->put_cond); pthread_mutex_unlock(&priv->put_mutex); // Stop the consumer mlt_log(MLT_CONSUMER_SERVICE(self), MLT_LOG_DEBUG, "stopping consumer\n"); // Cancel the read ahead threads if (priv->started) { // Unblock the consumer calling mlt_consumer_rt_frame pthread_mutex_lock(&priv->queue_mutex); pthread_cond_broadcast(&priv->queue_cond); pthread_mutex_unlock(&priv->queue_mutex); } // Invoke the child callback if (self->stop != NULL) self->stop(self); // Check if the user has requested real time or not and stop if necessary mlt_log(MLT_CONSUMER_SERVICE(self), MLT_LOG_DEBUG, "stopping read_ahead\n"); if (abs(priv->real_time) == 1) consumer_read_ahead_stop(self); else if (abs(priv->real_time) > 1) consumer_work_stop(self); // Kill the test card mlt_properties_set_data(properties, "test_card_producer", NULL, 0, NULL, NULL); // Check and run a post command if (mlt_properties_get(properties, "post")) if (system(mlt_properties_get(properties, "post")) == -1) mlt_log(MLT_CONSUMER_SERVICE(self), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get(properties, "post")); mlt_log(MLT_CONSUMER_SERVICE(self), MLT_LOG_DEBUG, "stopped\n"); return 0; } /** Determine if the consumer is stopped. * * \public \memberof mlt_consumer_s * \param self a consumer * \return true if the consumer is stopped */ int mlt_consumer_is_stopped(mlt_consumer self) { // Check if the consumer is stopped if (self && self->is_stopped) return self->is_stopped(self); return 0; } /** Close and destroy the consumer. * * \public \memberof mlt_consumer_s * \param self a consumer */ void mlt_consumer_close(mlt_consumer self) { if (self != NULL && mlt_properties_dec_ref(MLT_CONSUMER_PROPERTIES(self)) <= 0) { // Get the childs close function void (*consumer_close)() = self->close; if (consumer_close) { // Just in case... //mlt_consumer_stop( self ); self->close = NULL; consumer_close(self); } else { consumer_private *priv = self->local; // Make sure it only gets called once self->parent.close = NULL; // Destroy the push mutex and condition pthread_mutex_destroy(&priv->put_mutex); pthread_cond_destroy(&priv->put_cond); pthread_mutex_destroy(&priv->position_mutex); mlt_service_close(&self->parent); free(priv); } } } /** Get the position of the last frame shown. * * \public \memberof mlt_consumer_s * \param consumer a consumer * \return the position */ mlt_position mlt_consumer_position(mlt_consumer consumer) { consumer_private *priv = consumer->local; pthread_mutex_lock(&priv->position_mutex); mlt_position result = priv->position; pthread_mutex_unlock(&priv->position_mutex); return result; } static void mlt_thread_create(mlt_consumer self, mlt_thread_function_t function) { consumer_private *priv = self->local; mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); if (mlt_properties_get(MLT_CONSUMER_PROPERTIES(self), "priority")) { struct sched_param priority; priority.sched_priority = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(self), "priority"); mlt_event_data_thread data = {.thread = &priv->ahead_thread, .priority = &priority.sched_priority, .function = function, .data = self}; if (mlt_events_fire(properties, "consumer-thread-create", mlt_event_data_from_object(&data)) < 1) { pthread_attr_t thread_attributes; pthread_attr_init(&thread_attributes); pthread_attr_setschedpolicy(&thread_attributes, SCHED_OTHER); pthread_attr_setschedparam(&thread_attributes, &priority); #if !defined(__ANDROID__) \ || (defined(__ANDROID__) \ && __ANDROID_API__ \ >= 28) // pthread_attr_setinheritsched is not available until API level 28 pthread_attr_setinheritsched(&thread_attributes, PTHREAD_EXPLICIT_SCHED); #endif pthread_attr_setscope(&thread_attributes, PTHREAD_SCOPE_SYSTEM); priv->ahead_thread = malloc(sizeof(pthread_t)); pthread_t *handle = priv->ahead_thread; if (pthread_create((pthread_t *) &(*handle), &thread_attributes, function, self) < 0) pthread_create((pthread_t *) &(*handle), NULL, function, self); pthread_attr_destroy(&thread_attributes); } } else { int priority = -1; mlt_event_data_thread data = {.thread = &priv->ahead_thread, .priority = &priority, .function = function, .data = self}; if (mlt_events_fire(properties, "consumer-thread-create", mlt_event_data_from_object(&data)) < 1) { priv->ahead_thread = malloc(sizeof(pthread_t)); pthread_t *handle = priv->ahead_thread; pthread_create((pthread_t *) &(*handle), NULL, function, self); } } } static void mlt_thread_join(mlt_consumer self) { consumer_private *priv = self->local; mlt_event_data_thread data = {.thread = &priv->ahead_thread, .priority = NULL, .function = NULL, .data = self}; if (mlt_events_fire(MLT_CONSUMER_PROPERTIES(self), "consumer-thread-join", mlt_event_data_from_object(&data)) < 1) { pthread_t *handle = priv->ahead_thread; pthread_join(*handle, NULL); free(priv->ahead_thread); } priv->ahead_thread = NULL; } mlt-7.38.0/src/framework/mlt_consumer.h000664 000000 000000 00000020157 15172202314 020033 0ustar00rootroot000000 000000 /** * \file mlt_consumer.h * \brief abstraction for all consumer services * \see mlt_consumer_s * * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_CONSUMER_H #define MLT_CONSUMER_H #include "mlt_events.h" #include "mlt_export.h" #include "mlt_service.h" #include /** \brief Consumer abstract service class * * A consumer is a service that pulls audio and video from the connected * producers, filters, and transitions. Typically a consumer is used to * output audio and/or video to a device, file, or socket. * * \extends mlt_service_s * \properties \em rescale the scaling algorithm to pass on to all scaling * filters, defaults to "bilinear" * \properties \em buffer the number of frames to use in the asynchronous * render thread, defaults to 25 * \properties \em prefill the number of frames to render before commencing * output when real_time <> 0, defaults to the size of buffer * \properties \em drop_max the maximum number of consecutively dropped frames, defaults to 5 * \properties \em frequency the audio sample rate to use in Hertz, defaults to 48000 * \properties \em channels the number of audio channels to use, defaults to 2 * \properties \em channel_layout the layout of the audio channels, defaults to auto. * other options include: mono, stereo, 5.1, 7.1, etc. * \properties \em real_time the asynchronous behavior: 1 (default) for asynchronous * with frame dropping, -1 for asynchronous without frame dropping, 0 to disable (synchronous) * \properties \em test_card the name of a resource to use as the test card, defaults to * environment variable MLT_TEST_CARD. If undefined, the hard-coded default test card is * white silence. A test card is what appears when nothing is produced. * \event \em consumer-frame-show Subclass implementations fire this immediately after showing a frame * or when a frame should be shown (if audio-only consumer). The event data is a frame. * \event \em consumer-frame-render The base class fires this immediately before rendering a frame; * the event data is a frame. * \event \em consumer-thread-create Override the implementation of creating and * starting a thread by listening and responding to this (real_time 1 or -1 only). * The event data is a pointer to mlt_event_data_thread. * \event \em consumer-thread-join Override the implementation of waiting and * joining a terminated thread by listening and responding to this (real_time 1 or -1 only). * The event data is a pointer to mlt_event_data_thread. * \event \em consumer-thread-started The base class fires when beginning execution of a rendering thread. * \event \em consumer-thread-stopped The base class fires when a rendering thread has ended. * \event \em consumer-stopping This is fired when stop was requested, but before render threads are joined. * \event \em consumer-stopped This is fired when the subclass implementation calls mlt_consumer_stopped(). * \properties \em fps video frames per second as floating point (read only) * \properties \em frame_rate_num the numerator of the video frame rate, overrides \p mlt_profile_s * \properties \em frame_rate_den the denominator of the video frame rate, overrides \p mlt_profile_s * \properties \em width the horizontal video resolution, overrides \p mlt_profile_s * \properties \em height the vertical video resolution, overrides \p mlt_profile_s * \properties \em progressive a flag that indicates if the video is interlaced * or progressive, overrides \p mlt_profile_s * \properties \em aspect_ratio the video sample (pixel) aspect ratio as floating point (read only) * \properties \em sample_aspect_num the numerator of the sample aspect ratio, overrides \p mlt_profile_s * \properties \em sample_aspect_den the denominator of the sample aspect ratio, overrides \p mlt_profile_s * \properties \em display_ratio the video frame aspect ratio as floating point (read only) * \properties \em display_aspect_num the numerator of the video frame aspect ratio, overrides \p mlt_profile_s * \properties \em display_aspect_den the denominator of the video frame aspect ratio, overrides \p mlt_profile_s * \properties \em priority the OS scheduling priority for the render threads when real_time is not 0. * \properties \em top_field_first when not progressive, whether interlace field order is top-field-first, defaults to 0. * Set this to -1 if the consumer does not care about the field order. * \properties \em mlt_image_format the image format to request in rendering threads, defaults to yuv422 * \properties \em mlt_audio_format the audio format to request in rendering threads, defaults to S16 * \properties \em audio_off set non-zero to disable audio processing * \properties \em video_off set non-zero to disable video processing * \properties \em drop_count the number of video frames not rendered since starting consumer * \properties \em color_range the color range as tv/mpeg (limited) or pc/jpeg (full); default is unset, which implies tv/mpeg * \properties \em color_trc the color transfer characteristic (gamma), default is unset, use mlt_color_trc string values * \properties \em mlt_color_trc the color transfer to use for internal processing, default is unset; use mlt_color_trc string values but only "linear" is implemented * \properties \em deinterlacer the deinterlace algorithm to pass to deinterlace filters, defaults to "yadif" */ struct mlt_consumer_s { /** A consumer is a service. */ struct mlt_service_s parent; /** Start the consumer to pull frames (virtual function). * * \param mlt_consumer a consumer * \return true if there was an error */ int (*start)(mlt_consumer); /** Stop the consumer (virtual function). * * \param mlt_consumer a consumer * \return true if there was an error */ int (*stop)(mlt_consumer); /** Get whether the consumer is running or stopped (virtual function). * * \param mlt_consumer a consumer * \return true if the consumer is stopped */ int (*is_stopped)(mlt_consumer); /** Purge the consumer of buffered data (virtual function). * * \param mlt_consumer a consumer */ void (*purge)(mlt_consumer); /** The destructor virtual function * * \param mlt_consumer a consumer */ void (*close)(mlt_consumer); void *local; /**< \private instance object */ void *child; /**< \private the object of a subclass */ }; #define MLT_CONSUMER_SERVICE(consumer) (&(consumer)->parent) #define MLT_CONSUMER_PROPERTIES(consumer) MLT_SERVICE_PROPERTIES(MLT_CONSUMER_SERVICE(consumer)) MLT_EXPORT int mlt_consumer_init(mlt_consumer self, void *child, mlt_profile profile); MLT_EXPORT mlt_consumer mlt_consumer_new(mlt_profile profile); MLT_EXPORT mlt_service mlt_consumer_service(mlt_consumer self); MLT_EXPORT mlt_properties mlt_consumer_properties(mlt_consumer self); MLT_EXPORT int mlt_consumer_connect(mlt_consumer self, mlt_service producer); MLT_EXPORT int mlt_consumer_start(mlt_consumer self); MLT_EXPORT void mlt_consumer_purge(mlt_consumer self); MLT_EXPORT int mlt_consumer_put_frame(mlt_consumer self, mlt_frame frame); MLT_EXPORT mlt_frame mlt_consumer_get_frame(mlt_consumer self); MLT_EXPORT mlt_frame mlt_consumer_rt_frame(mlt_consumer self); MLT_EXPORT int mlt_consumer_stop(mlt_consumer self); MLT_EXPORT int mlt_consumer_is_stopped(mlt_consumer self); MLT_EXPORT void mlt_consumer_stopped(mlt_consumer self); MLT_EXPORT void mlt_consumer_close(mlt_consumer); MLT_EXPORT mlt_position mlt_consumer_position(mlt_consumer); #endif mlt-7.38.0/src/framework/mlt_deque.c000664 000000 000000 00000021712 15172202314 017274 0ustar00rootroot000000 000000 /** * \file mlt_deque.c * \brief double ended queue * \see mlt_deque_s * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // Local header files #include "mlt_deque.h" // System header files #include #include #include /** \brief Deque entry class * */ typedef union { void *addr; int value; double floating; } deque_entry; /** \brief Double-Ended Queue (deque) class * * The double-ended queue is a very versatile data structure. MLT uses it as * list, stack, and circular queue. */ struct mlt_deque_s { deque_entry *list; int size; atomic_int count; }; /** Create a deque. * * \public \memberof mlt_deque_s * \return a new deque */ mlt_deque mlt_deque_init() { mlt_deque self = calloc(1, sizeof(struct mlt_deque_s)); return self; } /** Return the number of items in the deque. * * \public \memberof mlt_deque_s * \param self a deque * \return the number of items */ int mlt_deque_count(mlt_deque self) { if (self) return self->count; else return 0; } /** Allocate space on the deque. * * \private \memberof mlt_deque_s * \param self a deque * \return true if there was an error */ static int mlt_deque_allocate(mlt_deque self) { if (self->count == self->size) { self->list = realloc(self->list, sizeof(deque_entry) * (self->size + 20)); self->size += 20; } return self->list == NULL; } /** Push an item to the end. * * \public \memberof mlt_deque_s * \param self a deque * \param item an opaque pointer * \return true if there was an error */ int mlt_deque_push_back(mlt_deque self, void *item) { int error = mlt_deque_allocate(self); if (error == 0) self->list[self->count++].addr = item; return error; } /** Pop an item. * * \public \memberof mlt_deque_s * \param self a pointer * \return an opaque pointer */ void *mlt_deque_pop_back(mlt_deque self) { return self->count > 0 ? self->list[--self->count].addr : NULL; } /** Queue an item at the start. * * \public \memberof mlt_deque_s * \param self a deque * \param item an opaque pointer * \return true if there was an error */ int mlt_deque_push_front(mlt_deque self, void *item) { int error = mlt_deque_allocate(self); if (error == 0) { memmove(&self->list[1], self->list, (self->count++) * sizeof(deque_entry)); self->list[0].addr = item; } return error; } /** Remove an item from the start. * * \public \memberof mlt_deque_s * \param self a pointer * \return an opaque pointer */ void *mlt_deque_pop_front(mlt_deque self) { void *item = NULL; if (self->count > 0) { item = self->list[0].addr; memmove(self->list, &self->list[1], (--self->count) * sizeof(deque_entry)); } return item; } /** Inquire on item at back of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return an opaque pointer */ void *mlt_deque_peek_back(mlt_deque self) { return self->count > 0 ? self->list[self->count - 1].addr : NULL; } /** Inquire on item at front of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return an opaque pointer */ void *mlt_deque_peek_front(mlt_deque self) { return self->count > 0 ? self->list[0].addr : NULL; } /** Inquire on item in deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \param index the position in the deque * \return an opaque pointer */ void *mlt_deque_peek(mlt_deque self, int index) { return self->count > index ? self->list[index].addr : NULL; } /** Insert an item in a sorted fashion. * * Optimized for the equivalent of \p mlt_deque_push_back. * * \public \memberof mlt_deque_s * \param self a deque * \param item an opaque pointer * \param cmp a function pointer to the comparison function * \return true if there was an error */ int mlt_deque_insert(mlt_deque self, void *item, mlt_deque_compare cmp) { int error = mlt_deque_allocate(self); if (error == 0) { int n = self->count + 1; while (--n) if (cmp(item, self->list[n - 1].addr) >= 0) break; memmove(&self->list[n + 1], &self->list[n], (self->count - n) * sizeof(deque_entry)); self->list[n].addr = item; self->count++; } return error; } /** Push an integer to the end. * * \public \memberof mlt_deque_s * \param self a deque * \param item an integer * \return true if there was an error */ int mlt_deque_push_back_int(mlt_deque self, int item) { int error = mlt_deque_allocate(self); if (error == 0) self->list[self->count++].value = item; return error; } /** Pop an integer. * * \public \memberof mlt_deque_s * \param self a deque * \return an integer */ int mlt_deque_pop_back_int(mlt_deque self) { return self->count > 0 ? self->list[--self->count].value : 0; } /** Queue an integer at the start. * * \public \memberof mlt_deque_s * \param self a deque * \param item an integer * \return true if there was an error */ int mlt_deque_push_front_int(mlt_deque self, int item) { int error = mlt_deque_allocate(self); if (error == 0) { memmove(&self->list[1], self->list, (self->count++) * sizeof(deque_entry)); self->list[0].value = item; } return error; } /** Remove an integer from the start. * * \public \memberof mlt_deque_s * \param self a deque * \return an integer */ int mlt_deque_pop_front_int(mlt_deque self) { int item = 0; if (self->count > 0) { item = self->list[0].value; memmove(self->list, &self->list[1], (--self->count) * sizeof(deque_entry)); } return item; } /** Inquire on an integer at back of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return an integer */ int mlt_deque_peek_back_int(mlt_deque self) { return self->count > 0 ? self->list[self->count - 1].value : 0; } /** Inquire on an integer at front of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return an integer */ int mlt_deque_peek_front_int(mlt_deque self) { return self->count > 0 ? self->list[0].value : 0; } /** Push a double float to the end. * * \public \memberof mlt_deque_s * \param self a deque * \param item a double float * \return true if there was an error */ int mlt_deque_push_back_double(mlt_deque self, double item) { int error = mlt_deque_allocate(self); if (error == 0) self->list[self->count++].floating = item; return error; } /** Pop a double float. * * \public \memberof mlt_deque_s * \param self a deque * \return a double float */ double mlt_deque_pop_back_double(mlt_deque self) { return self->count > 0 ? self->list[--self->count].floating : 0; } /** Queue a double float at the start. * * \public \memberof mlt_deque_s * \param self a deque * \param item a double float * \return true if there was an error */ int mlt_deque_push_front_double(mlt_deque self, double item) { int error = mlt_deque_allocate(self); if (error == 0) { memmove(&self->list[1], self->list, (self->count++) * sizeof(deque_entry)); self->list[0].floating = item; } return error; } /** Remove a double float from the start. * * \public \memberof mlt_deque_s * \param self a deque * \return a double float */ double mlt_deque_pop_front_double(mlt_deque self) { double item = 0; if (self->count > 0) { item = self->list[0].floating; memmove(self->list, &self->list[1], (--self->count) * sizeof(deque_entry)); } return item; } /** Inquire on a double float at back of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return a double float */ double mlt_deque_peek_back_double(mlt_deque self) { return self->count > 0 ? self->list[self->count - 1].floating : 0; } /** Inquire on a double float at front of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return a double float */ double mlt_deque_peek_front_double(mlt_deque self) { return self->count > 0 ? self->list[0].floating : 0; } /** Destroy the queue. * * \public \memberof mlt_deque_s * \param self a deque */ void mlt_deque_close(mlt_deque self) { free(self->list); free(self); } mlt-7.38.0/src/framework/mlt_deque.h000664 000000 000000 00000005061 15172202314 017300 0ustar00rootroot000000 000000 /** * \file mlt_deque.h * \brief double ended queue * \see mlt_deque_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_DEQUE_H #define MLT_DEQUE_H #include "mlt_export.h" #include "mlt_types.h" /** The callback function used to compare items for insert sort. * * \public \memberof mlt_deque_s * \param a the first object * \param b the second object * \returns 0 if equal, < 0 if a < b, or > 0 if a > b */ typedef int (*mlt_deque_compare)(void *a, void *b); MLT_EXPORT mlt_deque mlt_deque_init(); MLT_EXPORT int mlt_deque_count(mlt_deque self); MLT_EXPORT int mlt_deque_push_back(mlt_deque self, void *item); MLT_EXPORT void *mlt_deque_pop_back(mlt_deque self); MLT_EXPORT int mlt_deque_push_front(mlt_deque self, void *item); MLT_EXPORT void *mlt_deque_pop_front(mlt_deque self); MLT_EXPORT void *mlt_deque_peek_back(mlt_deque self); MLT_EXPORT void *mlt_deque_peek_front(mlt_deque self); MLT_EXPORT void *mlt_deque_peek(mlt_deque self, int index); MLT_EXPORT int mlt_deque_insert(mlt_deque self, void *item, mlt_deque_compare); MLT_EXPORT int mlt_deque_push_back_int(mlt_deque self, int item); MLT_EXPORT int mlt_deque_pop_back_int(mlt_deque self); MLT_EXPORT int mlt_deque_push_front_int(mlt_deque self, int item); MLT_EXPORT int mlt_deque_pop_front_int(mlt_deque self); MLT_EXPORT int mlt_deque_peek_back_int(mlt_deque self); MLT_EXPORT int mlt_deque_peek_front_int(mlt_deque self); MLT_EXPORT int mlt_deque_push_back_double(mlt_deque self, double item); MLT_EXPORT double mlt_deque_pop_back_double(mlt_deque self); MLT_EXPORT int mlt_deque_push_front_double(mlt_deque self, double item); MLT_EXPORT double mlt_deque_pop_front_double(mlt_deque self); MLT_EXPORT double mlt_deque_peek_back_double(mlt_deque self); MLT_EXPORT double mlt_deque_peek_front_double(mlt_deque self); MLT_EXPORT void mlt_deque_close(mlt_deque self); #endif mlt-7.38.0/src/framework/mlt_events.c000664 000000 000000 00000037712 15172202314 017504 0ustar00rootroot000000 000000 /** * \file mlt_events.c * \brief event handling * \see mlt_events_struct * * Copyright (C) 2004-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "mlt_events.h" #include "mlt_properties.h" /* Memory leak checks. */ #undef _MLT_EVENT_CHECKS_ #ifdef _MLT_EVENT_CHECKS_ static int events_created = 0; static int events_destroyed = 0; #endif /** \brief Events class * * Events provide messages and notifications between services and the application. * A service can register an event and fire/send it upon certain conditions or times. * Likewise, a service or an application can listen/receive specific events on specific * services. */ struct mlt_events_struct { mlt_properties owner; mlt_properties listeners; }; typedef struct mlt_events_struct *mlt_events; /** \brief Event class * */ struct mlt_event_struct { mlt_events parent; atomic_int_fast32_t ref_count; atomic_int_fast32_t block_count; mlt_listener listener; void *listener_data; }; /** Increment the reference count on self event. * * \public \memberof mlt_event_struct * \param self an event */ void mlt_event_inc_ref(mlt_event self) { if (self != NULL) self->ref_count++; } /** Increment the block count on self event. * * \public \memberof mlt_event_struct * \param self an event */ void mlt_event_block(mlt_event self) { if (self != NULL && self->parent != NULL) self->block_count++; } /** Decrement the block count on self event. * * \public \memberof mlt_event_struct * \param self an event */ void mlt_event_unblock(mlt_event self) { if (self != NULL && self->parent != NULL) self->block_count--; } /** Close self event. * * \public \memberof mlt_event_struct * \param self an event */ void mlt_event_close(mlt_event self) { if (self != NULL) { if (--self->ref_count == 1) self->parent = NULL; if (self->ref_count <= 0) { #ifdef _MLT_EVENT_CHECKS_ mlt_log(NULL, MLT_LOG_DEBUG, "Events created %d, destroyed %d\n", events_created, ++events_destroyed); #endif free(self); } } } /* Forward declaration to private functions. */ static mlt_events mlt_events_fetch(mlt_properties); static void mlt_events_close(mlt_events); /** Initialise the events structure. * * \public \memberof mlt_events_struct * \param self a properties list */ void mlt_events_init(mlt_properties self) { mlt_events events = mlt_events_fetch(self); if (!events && self) { events = calloc(1, sizeof(struct mlt_events_struct)); if (events) { events->listeners = mlt_properties_new(); events->owner = self; mlt_properties_set_data(self, "_events", events, 0, (mlt_destructor) mlt_events_close, NULL); } } } /** Register an event. * * \public \memberof mlt_events_struct * \param self a properties list * \param id the name of an event * \return true if there was an error */ int mlt_events_register(mlt_properties self, const char *id) { int error = 1; mlt_events events = mlt_events_fetch(self); if (events != NULL) { mlt_properties list = events->listeners; char temp[128]; sprintf(temp, "list:%s", id); if (mlt_properties_get_data(list, temp, NULL) == NULL) mlt_properties_set_data(list, temp, mlt_properties_new(), 0, (mlt_destructor) mlt_properties_close, NULL); } return error; } /** Fire an event. * * \public \memberof mlt_events_struct * \param self a properties list * \param id the name of an event * \param event_data an event data object * \return the number of listeners */ int mlt_events_fire(mlt_properties self, const char *id, mlt_event_data event_data) { int result = 0; mlt_events events = mlt_events_fetch(self); if (events != NULL) { mlt_properties list = events->listeners; mlt_properties listeners = NULL; char temp[128]; sprintf(temp, "list:%s", id); listeners = mlt_properties_get_data(list, temp, NULL); if (listeners != NULL) { for (int i = 0; i < mlt_properties_count(listeners); i++) { mlt_event event = mlt_properties_get_data_at(listeners, i, NULL); if (event != NULL && event->parent != NULL && event->block_count == 0) { event->listener(event->parent->owner, event->listener_data, event_data); ++result; } } } } return result; } /** Register a listener. * * \public \memberof mlt_events_struct * \param self a properties list * \param listener_data an opaque pointer * \param id the name of the event to listen for * \param listener the callback to receive an event message * \return an event */ mlt_event mlt_events_listen(mlt_properties self, void *listener_data, const char *id, mlt_listener listener) { mlt_event event = NULL; mlt_events events = mlt_events_fetch(self); if (events != NULL) { mlt_properties list = events->listeners; mlt_properties listeners = NULL; char temp[128]; sprintf(temp, "list:%s", id); listeners = mlt_properties_get_data(list, temp, NULL); if (listeners != NULL) { int first_null = -1; int i = 0; for (i = 0; event == NULL && i < mlt_properties_count(listeners); i++) { mlt_event entry = mlt_properties_get_data_at(listeners, i, NULL); if (entry != NULL && entry->parent != NULL) { if (entry->listener_data == listener_data && entry->listener == listener) event = entry; } else if ((entry == NULL || entry->parent == NULL) && first_null == -1) { first_null = i; } } if (event == NULL) { event = malloc(sizeof(struct mlt_event_struct)); if (event != NULL) { #ifdef _MLT_EVENT_CHECKS_ events_created++; #endif sprintf(temp, "%d", first_null == -1 ? mlt_properties_count(listeners) : first_null); event->parent = events; event->ref_count = 0; event->block_count = 0; event->listener = listener; event->listener_data = listener_data; mlt_properties_set_data(listeners, temp, event, 0, (mlt_destructor) mlt_event_close, NULL); mlt_event_inc_ref(event); } } } } return event; } /** Block all events for a given listener_data. * * \public \memberof mlt_events_struct * \param self a properties list * \param listener_data the listener's opaque data pointer */ void mlt_events_block(mlt_properties self, void *listener_data) { mlt_events events = mlt_events_fetch(self); if (events != NULL) { int i = 0, j = 0; mlt_properties list = events->listeners; for (j = 0; j < mlt_properties_count(list); j++) { char *temp = mlt_properties_get_name(list, j); if (!strncmp(temp, "list:", 5)) { mlt_properties listeners = mlt_properties_get_data(list, temp, NULL); for (i = 0; i < mlt_properties_count(listeners); i++) { mlt_event entry = mlt_properties_get_data_at(listeners, i, NULL); if (entry != NULL && entry->listener_data == listener_data) mlt_event_block(entry); } } } } } /** Unblock all events for a given listener_data. * * \public \memberof mlt_events_struct * \param self a properties list * \param listener_data the listener's opaque data pointer */ void mlt_events_unblock(mlt_properties self, void *listener_data) { mlt_events events = mlt_events_fetch(self); if (events != NULL) { int i = 0, j = 0; mlt_properties list = events->listeners; for (j = 0; j < mlt_properties_count(list); j++) { char *temp = mlt_properties_get_name(list, j); if (!strncmp(temp, "list:", 5)) { mlt_properties listeners = mlt_properties_get_data(list, temp, NULL); for (i = 0; i < mlt_properties_count(listeners); i++) { mlt_event entry = mlt_properties_get_data_at(listeners, i, NULL); if (entry != NULL && entry->listener_data == listener_data) mlt_event_unblock(entry); } } } } } /** Disconnect all events for a given listener_data. * * \public \memberof mlt_events_struct * \param self a properties list * \param listener_data the listener's opaque data pointer */ void mlt_events_disconnect(mlt_properties self, void *listener_data) { mlt_events events = mlt_events_fetch(self); if (events != NULL) { int i = 0, j = 0; mlt_properties list = events->listeners; for (j = 0; j < mlt_properties_count(list); j++) { char *temp = mlt_properties_get_name(list, j); if (!strncmp(temp, "list:", 5)) { mlt_properties listeners = mlt_properties_get_data(list, temp, NULL); for (i = 0; i < mlt_properties_count(listeners); i++) { mlt_event entry = mlt_properties_get_data_at(listeners, i, NULL); char *name = mlt_properties_get_name(listeners, i); if (entry != NULL && entry->listener_data == listener_data) mlt_properties_set_data(listeners, name, NULL, 0, NULL, NULL); } } } } } /** \brief private to mlt_events_struct, used by mlt_events_wait_for() */ typedef struct { pthread_cond_t cond; pthread_mutex_t mutex; } condition_pair; /** The event listener callback for the wait functions. * * \private \memberof mlt_events_struct * \param self a properties list * \param pair a condition pair */ static void mlt_events_listen_for(mlt_properties self, condition_pair *pair) { pthread_mutex_lock(&pair->mutex); pthread_cond_signal(&pair->cond); pthread_mutex_unlock(&pair->mutex); } /** Prepare to wait for an event. * * \public \memberof mlt_events_struct * \param self a properties list * \param id the name of the event to wait for * \return an event */ mlt_event mlt_events_setup_wait_for(mlt_properties self, const char *id) { condition_pair *pair = malloc(sizeof(condition_pair)); pthread_cond_init(&pair->cond, NULL); pthread_mutex_init(&pair->mutex, NULL); pthread_mutex_lock(&pair->mutex); return mlt_events_listen(self, pair, id, (mlt_listener) mlt_events_listen_for); } /** Wait for an event. * * \public \memberof mlt_events_struct * \param self a properties list * \param event an event */ void mlt_events_wait_for(mlt_properties self, mlt_event event) { if (event != NULL) { condition_pair *pair = event->listener_data; pthread_cond_wait(&pair->cond, &pair->mutex); } } /** Cleanup after waiting for an event. * * \public \memberof mlt_events_struct * \param self a properties list * \param event an event */ void mlt_events_close_wait_for(mlt_properties self, mlt_event event) { if (event != NULL) { condition_pair *pair = event->listener_data; event->parent = NULL; pthread_mutex_unlock(&pair->mutex); pthread_mutex_destroy(&pair->mutex); pthread_cond_destroy(&pair->cond); free(pair); } } /** Fetch the events object. * * \private \memberof mlt_events_struct * \param self a properties list * \return an events object */ static mlt_events mlt_events_fetch(mlt_properties self) { mlt_events events = NULL; if (self != NULL) events = mlt_properties_get_data(self, "_events", NULL); return events; } /** Close the events object. * * \private \memberof mlt_events_struct * \param events an events object */ static void mlt_events_close(mlt_events events) { if (events != NULL) { mlt_properties_close(events->listeners); free(events); } } /** Initialize an empty event data. * * \public \memberof mlt_event_data * \return an event data object */ mlt_event_data mlt_event_data_none() { mlt_event_data event_data; event_data.u.p = NULL; return event_data; } /** Initialize event data with an integer. * * \public \memberof mlt_event_data * \param value the integer with which to initialize the event data * \return an event data object */ mlt_event_data mlt_event_data_from_int(int value) { mlt_event_data event_data; event_data.u.i = value; return event_data; } /** Get an integer from the event data. * * \public \memberof mlt_event_data * \param event_data an event data object * \return an integer */ int mlt_event_data_to_int(mlt_event_data event_data) { return event_data.u.i; } /** Initialize event data with a string. * * \public \memberof mlt_event_data * \param value the string with which to initialize the event data * \return an event data object */ mlt_event_data mlt_event_data_from_string(const char *value) { mlt_event_data event_data; event_data.u.p = (void *) value; return event_data; } /** Get a string from the event data. * * \public \memberof mlt_event_data * \param event_data an event data object * \return a string */ const char *mlt_event_data_to_string(mlt_event_data event_data) { return event_data.u.p; } /** Initialize event data with a frame. * * \public \memberof mlt_event_data * \param frame the frame with which to initialize the event data * \return an event data object */ mlt_event_data mlt_event_data_from_frame(mlt_frame frame) { mlt_event_data event_data; event_data.u.p = frame; return event_data; } /** Get a frame from the event data. * * \public \memberof mlt_event_data * \param event_data an event data object * \return a frame */ mlt_frame mlt_event_data_to_frame(mlt_event_data event_data) { return (mlt_frame) event_data.u.p; } /** Initialize event data with opaque data. * * \public \memberof mlt_event_data * \param value the pointer with which to initialize the event data * \return an event data object */ mlt_event_data mlt_event_data_from_object(void *value) { mlt_event_data event_data; event_data.u.p = value; return event_data; } /** Get a pointer from the event data. * * \public \memberof mlt_event_data * \param event_data an event data object * \return a pointer */ void *mlt_event_data_to_object(mlt_event_data event_data) { return event_data.u.p; } mlt-7.38.0/src/framework/mlt_events.h000664 000000 000000 00000006732 15172202314 017507 0ustar00rootroot000000 000000 /** * \file mlt_events.h * \brief event handling * \see mlt_events_struct * * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_EVENTS_H #define MLT_EVENTS_H #include "mlt_export.h" #include "mlt_types.h" /** A container for data that may be supplied with an event */ typedef struct { union { int i; void *p; } u; } mlt_event_data; /** An event data structure to convey thread parameters */ typedef struct { void **thread; /**< a pointer to a thread object or handle as determined by you */ int *priority; /**< a priority level for the thread */ mlt_thread_function_t function; /**< a pointer to the function that thread will run */ void *data; /**< an opaque data pointer to pass along */ } mlt_event_data_thread; /** event handler when receiving an event message * * The first argument is the properties object on which the event was registered. * The second argument is an opaque pointer to the listener's data. * The third argument is an event data object. */ typedef void (*mlt_listener)(mlt_properties, void *, mlt_event_data); MLT_EXPORT void mlt_events_init(mlt_properties self); MLT_EXPORT int mlt_events_register(mlt_properties self, const char *id); MLT_EXPORT int mlt_events_fire(mlt_properties self, const char *id, mlt_event_data); MLT_EXPORT mlt_event mlt_events_listen(mlt_properties self, void *listener_data, const char *id, mlt_listener listener); MLT_EXPORT void mlt_events_block(mlt_properties self, void *listener_data); MLT_EXPORT void mlt_events_unblock(mlt_properties self, void *listener_data); MLT_EXPORT void mlt_events_disconnect(mlt_properties self, void *listener_data); MLT_EXPORT mlt_event mlt_events_setup_wait_for(mlt_properties self, const char *id); MLT_EXPORT void mlt_events_wait_for(mlt_properties self, mlt_event event); MLT_EXPORT void mlt_events_close_wait_for(mlt_properties self, mlt_event event); MLT_EXPORT void mlt_event_inc_ref(mlt_event self); MLT_EXPORT void mlt_event_block(mlt_event self); MLT_EXPORT void mlt_event_unblock(mlt_event self); MLT_EXPORT void mlt_event_close(mlt_event self); MLT_EXPORT mlt_event_data mlt_event_data_none(); MLT_EXPORT mlt_event_data mlt_event_data_from_int(int value); MLT_EXPORT int mlt_event_data_to_int(mlt_event_data); MLT_EXPORT mlt_event_data mlt_event_data_from_string(const char *value); MLT_EXPORT const char *mlt_event_data_to_string(mlt_event_data); MLT_EXPORT mlt_event_data mlt_event_data_from_frame(mlt_frame); MLT_EXPORT mlt_frame mlt_event_data_to_frame(mlt_event_data); MLT_EXPORT mlt_event_data mlt_event_data_from_object(void *); MLT_EXPORT void *mlt_event_data_to_object(mlt_event_data); #endif mlt-7.38.0/src/framework/mlt_factory.c000664 000000 000000 00000043752 15172202314 017650 0ustar00rootroot000000 000000 /** * \file mlt_factory.c * \brief the factory method interfaces * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt.h" #include "mlt_repository.h" #include #include #include #include /** the default subdirectory of the datadir for holding presets */ #define PRESETS_DIR "/presets" #ifdef _WIN32 #include #ifdef PREFIX_LIB #undef PREFIX_LIB #endif #ifdef PREFIX_DATA #undef PREFIX_DATA #endif /** the default subdirectory of the libdir for holding modules (plugins) */ #define PREFIX_LIB "\\lib\\mlt" /** the default subdirectory of the install prefix for holding module (plugin) data */ #define PREFIX_DATA "\\share\\mlt" #elif defined(RELOCATABLE) #ifdef PREFIX_LIB #undef PREFIX_LIB #endif #ifdef PREFIX_DATA #undef PREFIX_DATA #endif #ifdef __APPLE__ #include /** the default subdirectory of the libdir for holding modules (plugins) */ #define PREFIX_LIB "/PlugIns/mlt" /** the default subdirectory of the install prefix for holding module (plugin) data */ #define PREFIX_DATA "/Resources/mlt" #else #include #define PREFIX_LIB "/lib/mlt-7" /** the default subdirectory of the install prefix for holding module (plugin) data */ #define PREFIX_DATA "/share/mlt-7" #endif #endif /** holds the full path to the modules directory - initialized and retained for the entire session */ static char *mlt_directory = NULL; /** a global properties list for holding environment config data and things needing session-oriented cleanup */ static mlt_properties global_properties = NULL; /** the global repository singleton */ static mlt_repository repository = NULL; /** the events object for the factory events */ static mlt_properties event_object = NULL; /** for tracking the unique_id set on each constructed service */ static int unique_id = 0; #if defined(_WIN32) || defined(RELOCATABLE) // Replacement for buggy dirname() on some systems. // https://github.com/mltframework/mlt/issues/285 static char *mlt_dirname(char *path) { if (path && strlen(path)) { char *dirsep = strrchr(path, '/'); // Handle back slash on Windows. if (!dirsep) dirsep = strrchr(path, '\\'); // Handle trailing slash. if (dirsep == &path[strlen(path) - 1]) { dirsep[0] = '\0'; dirsep = strrchr(path, '/'); if (!dirsep) dirsep = strrchr(path, '\\'); } // Truncate string at last directory separator. if (dirsep) dirsep[0] = '\0'; } return path; } #endif /** Construct the repository and factories. * * \param directory an optional full path to a directory containing the modules that overrides the default and * the MLT_REPOSITORY environment variable * \return the repository */ mlt_repository mlt_factory_init(const char *directory) { if (!global_properties) global_properties = mlt_properties_new(); // Allow property refresh on a subsequent initialisation if (global_properties) { mlt_properties_set_or_default(global_properties, "MLT_NORMALISATION", getenv("MLT_NORMALISATION"), "PAL"); mlt_properties_set_or_default(global_properties, "MLT_PRODUCER", getenv("MLT_PRODUCER"), "loader"); mlt_properties_set_or_default(global_properties, "MLT_CONSUMER", getenv("MLT_CONSUMER"), "sdl2"); mlt_properties_set(global_properties, "MLT_TEST_CARD", getenv("MLT_TEST_CARD")); mlt_properties_set_or_default(global_properties, "MLT_PROFILE", getenv("MLT_PROFILE"), "dv_pal"); mlt_properties_set_or_default(global_properties, "MLT_DATA", getenv("MLT_DATA"), PREFIX_DATA); #if defined(_WIN32) char path[1024]; DWORD size = sizeof(path); GetModuleFileName(NULL, path, size); #ifndef NODEPLOY char *appdir = mlt_dirname(strdup(path)); #else char *appdir = mlt_dirname(mlt_dirname(strdup(path))); #endif mlt_properties_set(global_properties, "MLT_APPDIR", appdir); free(appdir); #elif defined(RELOCATABLE) char path[1024]; uint32_t size = sizeof(path); #ifdef __APPLE__ _NSGetExecutablePath(path, &size); #else ssize_t len = readlink("/proc/self/exe", path, size - 1); if (len != -1) { path[len] = '\0'; } #endif char *appdir = mlt_dirname(mlt_dirname(strdup(path))); mlt_properties_set(global_properties, "MLT_APPDIR", appdir); free(appdir); #endif } // Only initialise once if (mlt_directory == NULL) { #if !defined(_WIN32) && !defined(RELOCATABLE) // Allow user overrides if (directory == NULL || !strcmp(directory, "")) directory = getenv("MLT_REPOSITORY"); // If no directory is specified, default to install directory if (directory == NULL) directory = PREFIX_LIB; // Store the prefix for later retrieval mlt_directory = strdup(directory); #else if (directory) { mlt_directory = strdup(directory); } else { char *exedir = mlt_environment("MLT_APPDIR"); size_t size = strlen(exedir); if (global_properties && !getenv("MLT_DATA")) { mlt_directory = calloc(1, size + strlen(PREFIX_DATA) + 1); strcpy(mlt_directory, exedir); strcat(mlt_directory, PREFIX_DATA); mlt_properties_set(global_properties, "MLT_DATA", mlt_directory); free(mlt_directory); } mlt_directory = calloc(1, size + strlen(PREFIX_LIB) + 1); strcpy(mlt_directory, exedir); strcat(mlt_directory, PREFIX_LIB); } #endif // Initialise the pool mlt_pool_init(); // Create and set up the events object event_object = mlt_properties_new(); mlt_events_init(event_object); mlt_events_register(event_object, "producer-create-request"); mlt_events_register(event_object, "producer-create-done"); mlt_events_register(event_object, "filter-create-request"); mlt_events_register(event_object, "filter-create-done"); mlt_events_register(event_object, "transition-create-request"); mlt_events_register(event_object, "transition-create-done"); mlt_events_register(event_object, "consumer-create-request"); mlt_events_register(event_object, "consumer-create-done"); // Create the repository of services repository = mlt_repository_init(mlt_directory); } if (global_properties) { char *path = getenv("MLT_PRESETS_PATH"); if (path) { mlt_properties_set(global_properties, "MLT_PRESETS_PATH", path); } else { path = malloc(strlen(mlt_environment("MLT_DATA")) + strlen(PRESETS_DIR) + 1); strcpy(path, mlt_environment("MLT_DATA")); strcat(path, PRESETS_DIR); mlt_properties_set(global_properties, "MLT_PRESETS_PATH", path); free(path); } } return repository; } /** Fetch the repository. * * \return the global repository object */ mlt_repository mlt_factory_repository() { return repository; } /** Fetch the events object. * * \return the global factory event object */ mlt_properties mlt_factory_event_object() { return event_object; } /** Fetch the module directory used in this instance. * * \return the full path to the module directory that this session is using */ const char *mlt_factory_directory() { return mlt_directory; } /** Get a value from the environment. * * \param name the name of a MLT (runtime configuration) environment variable * \return the value of the variable */ char *mlt_environment(const char *name) { if (global_properties) return mlt_properties_get(global_properties, name); else return NULL; } /** Set a value in the environment. * * \param name the name of a MLT environment variable * \param value the value of the variable * \return true on error */ int mlt_environment_set(const char *name, const char *value) { if (global_properties) return mlt_properties_set(global_properties, name, value); else return -1; } /** Set some properties common to all services. * * This sets _unique_id, \p mlt_type, \p mlt_service (unless _mlt_service_hidden), and _profile. * * \param properties a service's properties list * \param profile the \p mlt_profile supplied to the factory function * \param type the MLT service class * \param service the name of the service */ static void set_common_properties(mlt_properties properties, mlt_profile profile, const char *type, const char *service) { mlt_properties_set_int(properties, "_unique_id", ++unique_id); mlt_properties_set(properties, "mlt_type", type); if (mlt_properties_get_int(properties, "_mlt_service_hidden") == 0) mlt_properties_set(properties, "mlt_service", service); if (profile != NULL && mlt_properties_get_data(properties, "_profile", NULL) == NULL) mlt_properties_set_data(properties, "_profile", profile, 0, NULL, NULL); } /** Fetch a producer from the repository. * * If you give NULL to \p service, then it will use core module's special * "loader"producer to load \p resource. One can override this default producer * by setting the environment variable MLT_PRODUCER. * * \param profile the \p mlt_profile to use * \param service the name of the producer (optional, defaults to MLT_PRODUCER) * \param resource an optional argument to the producer constructor, typically a string * \return a new producer */ mlt_producer mlt_factory_producer(mlt_profile profile, const char *service, const void *resource) { mlt_producer obj = NULL; if (profile && !mlt_profile_is_valid(profile)) return obj; // Pick up the default normalizing producer if necessary if (service == NULL) service = mlt_environment("MLT_PRODUCER"); // Offer the application the chance to 'create' mlt_factory_event_data data = {.name = service, .input = resource, .service = &obj}; mlt_events_fire(event_object, "producer-create-request", mlt_event_data_from_object(&data)); // Try to instantiate via the specified service if (obj == NULL) { obj = mlt_repository_create(repository, profile, mlt_service_producer_type, service, resource); mlt_events_fire(event_object, "producer-create-done", mlt_event_data_from_object(&data)); if (obj != NULL) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(obj); if (mlt_service_identify(MLT_PRODUCER_SERVICE(obj)) == mlt_service_chain_type) { // XML producer may return a chain set_common_properties(properties, profile, "chain", service); } else { set_common_properties(properties, profile, "producer", service); } } } return obj; } /** Fetch a filter from the repository. * * \param profile the \p mlt_profile to use * \param service the name of the filter * \param input an optional argument to the filter constructor, typically a string * \return a new filter */ mlt_filter mlt_factory_filter(mlt_profile profile, const char *service, const void *input) { mlt_filter obj = NULL; // Offer the application the chance to 'create' mlt_factory_event_data data = {.name = service, .input = input, .service = &obj}; mlt_events_fire(event_object, "filter-create-request", mlt_event_data_from_object(&data)); if (obj == NULL) { obj = mlt_repository_create(repository, profile, mlt_service_filter_type, service, input); mlt_events_fire(event_object, "filter-create-done", mlt_event_data_from_object(&data)); } if (obj != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(obj); set_common_properties(properties, profile, "filter", service); } return obj; } /** Fetch a link from the repository. * * \param service the name of the link * \param input an optional argument to the link constructor, typically a string * \return a new link */ mlt_link mlt_factory_link(const char *service, const void *input) { mlt_link obj = NULL; // Offer the application the chance to 'create' mlt_factory_event_data data = {.name = service, .input = input, .service = &obj}; mlt_events_fire(event_object, "link-create-request", mlt_event_data_from_object(&data)); if (obj == NULL) { obj = mlt_repository_create(repository, NULL, mlt_service_link_type, service, input); mlt_events_fire(event_object, "link-create-done", mlt_event_data_from_object(&data)); } if (obj != NULL) { mlt_properties properties = MLT_LINK_PROPERTIES(obj); set_common_properties(properties, NULL, "link", service); } return obj; } /** Fetch a transition from the repository. * * \param profile the \p mlt_profile to use * \param service the name of the transition * \param input an optional argument to the transition constructor, typically a string * \return a new transition */ mlt_transition mlt_factory_transition(mlt_profile profile, const char *service, const void *input) { mlt_transition obj = NULL; // Offer the application the chance to 'create' mlt_factory_event_data data = {.name = service, .input = input, .service = &obj}; mlt_events_fire(event_object, "transition-create-request", mlt_event_data_from_object(&data)); if (obj == NULL) { obj = mlt_repository_create(repository, profile, mlt_service_transition_type, service, input); mlt_events_fire(event_object, "transition-create-done", mlt_event_data_from_object(&data)); } if (obj != NULL) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(obj); set_common_properties(properties, profile, "transition", service); } return obj; } /** Fetch a consumer from the repository. * * \param profile the \p mlt_profile to use * \param service the name of the consumer (optional, defaults to MLT_CONSUMER) * \param input an optional argument to the consumer constructor, typically a string * \return a new consumer */ mlt_consumer mlt_factory_consumer(mlt_profile profile, const char *service, const void *input) { mlt_consumer obj = NULL; if (service == NULL) service = mlt_environment("MLT_CONSUMER"); // Offer the application the chance to 'create' mlt_factory_event_data data = {.name = service, .input = input, .service = &obj}; mlt_events_fire(event_object, "consumer-create-request", mlt_event_data_from_object(&data)); if (obj == NULL) { obj = mlt_repository_create(repository, profile, mlt_service_consumer_type, service, input); } if (obj == NULL) { if (!strcmp(service, "sdl2")) { service = "sdl"; obj = mlt_repository_create(repository, profile, mlt_service_consumer_type, service, input); } else if (!strcmp(service, "sdl_audio")) { service = "sdl2_audio"; obj = mlt_repository_create(repository, profile, mlt_service_consumer_type, service, input); } } if (obj != NULL) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(obj); mlt_events_fire(event_object, "consumer-create-done", mlt_event_data_from_object(&data)); set_common_properties(properties, profile, "consumer", service); } return obj; } /** Register an object for clean up. * * \param ptr an opaque pointer to anything allocated on the heap * \param destructor the function pointer of the deallocation subroutine (e.g., free or \p mlt_pool_release) */ void mlt_factory_register_for_clean_up(void *ptr, mlt_destructor destructor) { char unique[256]; sprintf(unique, "%08d", mlt_properties_count(global_properties)); mlt_properties_set_data(global_properties, unique, ptr, 0, destructor, NULL); } /** Close the factory. * * Cleanup all resources for the session. */ void mlt_factory_close() { if (mlt_directory != NULL) { mlt_properties_close(event_object); event_object = NULL; mlt_properties_close(global_properties); global_properties = NULL; if (repository) { mlt_repository_close(repository); repository = NULL; } free(mlt_directory); mlt_directory = NULL; mlt_pool_close(); } } mlt_properties mlt_global_properties() { return global_properties; } mlt-7.38.0/src/framework/mlt_factory.h000664 000000 000000 00000012067 15172202314 017650 0ustar00rootroot000000 000000 /** * \file mlt_factory.h * \brief the factory method interfaces * * Copyright (C) 2003-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_FACTORY_H #define MLT_FACTORY_H #include "mlt_export.h" #include "mlt_profile.h" #include "mlt_repository.h" #include "mlt_types.h" /** * \envvar \em MLT_PRODUCER the name of a default producer often used by other services, defaults to "loader" * \envvar \em MLT_CONSUMER the name of a default consumer, defaults to "sdl2" followed by "sdl" * \envvar \em MLT_TEST_CARD the name of a producer or file to be played when nothing is available (all tracks blank) * \envvar \em MLT_DATA overrides the default full path to the MLT and module supplemental data files, defaults to \p PREFIX_DATA * \envvar \em MLT_PROFILE selects the default mlt_profile_s, defaults to "dv_pal" * \envvar \em MLT_REPOSITORY overrides the default location of the plugin modules, defaults to \p PREFIX_LIB. * MLT_REPOSITORY is ignored on Windows and OS X relocatable builds. * \envvar \em MLT_PRESETS_PATH overrides the default full path to the properties preset files, defaults to \p MLT_DATA/presets * \envvar \em MLT_REPOSITORY_DENY colon separated list of modules to skip. Example: libmltplus:libmltavformat:libmltfrei0r * In case both qt5 and qt6 modules are found and none of both is blocked by MLT_REPOSITORY_DENY, qt6 will be blocked * \event \em producer-create-request fired when mlt_factory_producer is called; * the event data is a pointer to mlt_factory_event_data * \event \em producer-create-done fired when a producer registers itself; * the event data is a pointer to mlt_factory_event_data * \event \em filter-create-request fired when mlt_factory_filter is called; * the event data is a pointer to mlt_factory_event_data * \event \em filter-create-done fired when a filter registers itself; * the event data is a pointer to mlt_factory_event_data * \event \em transition-create-request fired when mlt_factory_transition is called; * the event data is a pointer to mlt_factory_event_data * \event \em transition-create-done fired when a transition registers itself; * the event data is a pointer to mlt_factory_event_data * \event \em consumer-create-request fired when mlt_factory_consumer is called; * the event data is a pointer to mlt_factory_event_data * \event \em consumer-create-done fired when a consumer registers itself; * the event data is a pointer to mlt_factory_event_data * \event \em link-create-request fired when mlt_factory_link is called; * the event data is a pointer to mlt_factory_event_data * \event \em link-create-done fired when a link registers itself; * the event data is a pointer to mlt_factory_event_data */ MLT_EXPORT mlt_repository mlt_factory_init(const char *directory); MLT_EXPORT mlt_repository mlt_factory_repository(); MLT_EXPORT const char *mlt_factory_directory(); MLT_EXPORT char *mlt_environment(const char *name); MLT_EXPORT int mlt_environment_set(const char *name, const char *value); MLT_EXPORT mlt_properties mlt_factory_event_object(); MLT_EXPORT mlt_producer mlt_factory_producer(mlt_profile profile, const char *service, const void *resource); MLT_EXPORT mlt_filter mlt_factory_filter(mlt_profile profile, const char *service, const void *input); MLT_EXPORT mlt_link mlt_factory_link(const char *service, const void *input); MLT_EXPORT mlt_transition mlt_factory_transition(mlt_profile profile, const char *service, const void *input); MLT_EXPORT mlt_consumer mlt_factory_consumer(mlt_profile profile, const char *service, const void *input); MLT_EXPORT void mlt_factory_register_for_clean_up(void *ptr, mlt_destructor destructor); MLT_EXPORT void mlt_factory_close(); MLT_EXPORT mlt_properties mlt_global_properties(); /** The event data for all factory-related events */ typedef struct { const char *name; /**< the name of the service requested */ const void *input; /**< an argument supplied to initialize the service, typically a string */ void *service; /**< the service being created */ } mlt_factory_event_data; #endif mlt-7.38.0/src/framework/mlt_field.c000664 000000 000000 00000015506 15172202314 017260 0ustar00rootroot000000 000000 /** * \file mlt_field.c * \brief a field for planting multiple transitions and filters * \see mlt_field_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_field.h" #include "mlt_filter.h" #include "mlt_multitrack.h" #include "mlt_service.h" #include "mlt_tractor.h" #include "mlt_transition.h" #include #include /** \brief Field class * * The field is a convenience class that works with the tractor and multitrack classes to manage track filters and transitions. */ struct mlt_field_s { /// This is the producer we're connected to mlt_service producer; /// Multitrack mlt_multitrack multitrack; /// Tractor mlt_tractor tractor; }; /** Construct a field, multitrack, and tractor. * * \public \memberof mlt_field_s * \return a new field */ mlt_field mlt_field_init() { // Initialise the field mlt_field self = calloc(1, sizeof(struct mlt_field_s)); // Initialise it if (self != NULL) { // Construct a multitrack self->multitrack = mlt_multitrack_init(); // Construct a tractor self->tractor = mlt_tractor_init(); // The first plant will be connected to the multitrack self->producer = MLT_MULTITRACK_SERVICE(self->multitrack); // Connect the tractor to the multitrack mlt_tractor_connect(self->tractor, self->producer); } // Return self return self; } /** Construct a field and initialize with supplied multitrack and tractor. * * \public \memberof mlt_field_s * \param multitrack a multitrack * \param tractor a tractor * \return a new field */ mlt_field mlt_field_new(mlt_multitrack multitrack, mlt_tractor tractor) { // Initialise the field mlt_field self = calloc(1, sizeof(struct mlt_field_s)); // Initialise it if (self != NULL) { // Construct a multitrack self->multitrack = multitrack; // Construct a tractor self->tractor = tractor; // The first plant will be connected to the multitrack self->producer = MLT_MULTITRACK_SERVICE(self->multitrack); // Connect the tractor to the multitrack mlt_tractor_connect(self->tractor, self->producer); } // Return self return self; } /** Get the service associated to this field. * * \public \memberof mlt_field_s * \param self a field * \return the tractor as a service */ mlt_service mlt_field_service(mlt_field self) { return self != NULL ? MLT_TRACTOR_SERVICE(self->tractor) : NULL; } /** Get the multitrack. * * \public \memberof mlt_field_s * \param self a field * \return the multitrack */ mlt_multitrack mlt_field_multitrack(mlt_field self) { return self != NULL ? self->multitrack : NULL; } /** Get the tractor. * * \public \memberof mlt_field_s * \param self a field * \return the tractor */ mlt_tractor mlt_field_tractor(mlt_field self) { return self != NULL ? self->tractor : NULL; } /** Get the properties associated to this field. * * \public \memberof mlt_field_s * \param self a field * \return a properties list */ mlt_properties mlt_field_properties(mlt_field self) { return MLT_SERVICE_PROPERTIES(mlt_field_service(self)); } /** Plant a filter. * * \public \memberof mlt_field_s * \param self a field * \param that a filter * \param track the track index * \return true if there was an error */ int mlt_field_plant_filter(mlt_field self, mlt_filter that, int track) { // Connect the filter to the last producer int result = mlt_filter_connect(that, self->producer, track); // If successful, then we'll use this for connecting in the future if (result == 0) { // This is now the new producer self->producer = MLT_FILTER_SERVICE(that); // Reconnect tractor to new producer mlt_tractor_connect(self->tractor, self->producer); // Fire an event mlt_events_fire(mlt_field_properties(self), "service-changed", mlt_event_data_none()); } return result; } /** Plant a transition. * * \public \memberof mlt_field_s * \param self a field * \param that a transition * \param a_track input A's track index * \param b_track input B's track index * \return true if there was an error */ int mlt_field_plant_transition(mlt_field self, mlt_transition that, int a_track, int b_track) { // Connect the transition to the last producer int result = mlt_transition_connect(that, self->producer, a_track, b_track); // If successful, then we'll use self for connecting in the future if (result == 0) { // This is now the new producer self->producer = MLT_TRANSITION_SERVICE(that); // Reconnect tractor to new producer mlt_tractor_connect(self->tractor, self->producer); // Fire an event mlt_events_fire(mlt_field_properties(self), "service-changed", mlt_event_data_none()); } return result; } /** Close the field. * * \public \memberof mlt_field_s * \param self a field */ void mlt_field_close(mlt_field self) { if (self != NULL && mlt_properties_dec_ref(mlt_field_properties(self)) <= 0) { //mlt_tractor_close( self->tractor ); //mlt_multitrack_close( self->multitrack ); free(self); } } /** Remove a filter or transition from the field. * * \public \memberof mlt_field_s * \param self a field * \param service the filter or transition to remove */ void mlt_field_disconnect_service(mlt_field self, mlt_service service) { mlt_service p = mlt_service_producer(service); mlt_service c = mlt_service_consumer(service); int i; switch (mlt_service_identify(c)) { case mlt_service_filter_type: i = mlt_filter_get_track(MLT_FILTER(c)); mlt_service_connect_producer(c, p, i); break; case mlt_service_transition_type: i = mlt_transition_get_a_track(MLT_TRANSITION(c)); mlt_service_connect_producer(c, p, i); MLT_TRANSITION(c)->producer = p; break; case mlt_service_tractor_type: self->producer = p; mlt_tractor_connect(MLT_TRACTOR(c), p); default: break; } mlt_events_fire(mlt_field_properties(self), "service-changed", mlt_event_data_none()); } mlt-7.38.0/src/framework/mlt_field.h000664 000000 000000 00000003427 15172202314 017264 0ustar00rootroot000000 000000 /** * \file mlt_field.h * \brief a field for planting multiple transitions and services * \see mlt_field_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_FIELD_H #define MLT_FIELD_H #include "mlt_export.h" #include "mlt_types.h" MLT_EXPORT mlt_field mlt_field_init(); MLT_EXPORT mlt_field mlt_field_new(mlt_multitrack multitrack, mlt_tractor tractor); MLT_EXPORT mlt_service mlt_field_service(mlt_field self); MLT_EXPORT mlt_tractor mlt_field_tractor(mlt_field self); MLT_EXPORT mlt_multitrack mlt_field_multitrack(mlt_field self); MLT_EXPORT mlt_properties mlt_field_properties(mlt_field self); MLT_EXPORT int mlt_field_plant_filter(mlt_field self, mlt_filter that, int track); MLT_EXPORT int mlt_field_plant_transition(mlt_field self, mlt_transition that, int a_track, int b_track); MLT_EXPORT void mlt_field_close(mlt_field self); MLT_EXPORT void mlt_field_disconnect_service(mlt_field self, mlt_service service); #endif mlt-7.38.0/src/framework/mlt_filter.c000664 000000 000000 00000025637 15172202314 017470 0ustar00rootroot000000 000000 /** * \file mlt_filter.c * \brief abstraction for all filter services * \see mlt_filter_s * * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_filter.h" #include "mlt_frame.h" #include "mlt_producer.h" #include #include #include static int filter_get_frame(mlt_service self, mlt_frame_ptr frame, int index); /** Initialize a new filter. * * \public \memberof mlt_filter_s * \param self a filter * \param child the object of a subclass * \return true if there was an error */ int mlt_filter_init(mlt_filter self, void *child) { mlt_service service = &self->parent; memset(self, 0, sizeof(struct mlt_filter_s)); self->child = child; if (mlt_service_init(service, self) == 0) { mlt_properties properties = MLT_SERVICE_PROPERTIES(service); // Override the get_frame method service->get_frame = filter_get_frame; // Define the destructor service->close = (mlt_destructor) mlt_filter_close; service->close_object = self; mlt_properties_set(properties, "mlt_type", "filter"); mlt_properties_set_position(properties, "in", 0); mlt_properties_set_position(properties, "out", 0); return 0; } return 1; } /** Create a new filter and initialize it. * * \public \memberof mlt_filter_s * \return a new filter */ mlt_filter mlt_filter_new() { mlt_filter self = calloc(1, sizeof(struct mlt_filter_s)); if (self != NULL && mlt_filter_init(self, NULL) == 0) { return self; } else { free(self); return NULL; } } /** Get the service class interface. * * \public \memberof mlt_filter_s * \param self a filter * \return the service parent class * \see MLT_FILTER_SERVICE */ mlt_service mlt_filter_service(mlt_filter self) { return self != NULL ? &self->parent : NULL; } /** Get the filter properties. * * \public \memberof mlt_filter_s * \param self a filter * \return the properties list for the filter * \see MLT_FILTER_PROPERTIES */ mlt_properties mlt_filter_properties(mlt_filter self) { return MLT_SERVICE_PROPERTIES(MLT_FILTER_SERVICE(self)); } /** Connect this filter to a producers track. Note that a filter only operates * on a single track, and by default it operates on the entirety of that track. * * \public \memberof mlt_filter_s * \param self a filter * \param producer the producer to which to connect this filter * \param index which of potentially multiple producers to this service (0 based) */ int mlt_filter_connect(mlt_filter self, mlt_service producer, int index) { int ret = mlt_service_connect_producer(&self->parent, producer, index); // If the connection was successful, grab the producer, track and reset in/out if (ret == 0) { mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); mlt_properties_set_position(properties, "in", 0); mlt_properties_set_position(properties, "out", 0); mlt_properties_set_int(properties, "track", index); } return ret; } /** Set the starting and ending time. * * \public \memberof mlt_filter_s * \param self a filter * \param in the time relative to the producer at which start applying the filter * \param out the time relative to the producer at which to stop applying the filter */ void mlt_filter_set_in_and_out(mlt_filter self, mlt_position in, mlt_position out) { mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); mlt_properties_set_position(properties, "in", in); mlt_properties_set_position(properties, "out", out); } /** Return the track that this filter is operating on. * * \public \memberof mlt_filter_s * \param self a filter * \return true on error */ int mlt_filter_get_track(mlt_filter self) { mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); return mlt_properties_get_int(properties, "track"); } /** Get the in point. * * \public \memberof mlt_filter_s * \param self a filter * \return the start time for the filter relative to the producer */ mlt_position mlt_filter_get_in(mlt_filter self) { mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); return mlt_properties_get_position(properties, "in"); } /** Get the out point. * * \public \memberof mlt_filter_s * \param self a filter * \return the ending time for the filter relative to the producer */ mlt_position mlt_filter_get_out(mlt_filter self) { mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); return mlt_properties_get_position(properties, "out"); } /** Get the duration. * * \public \memberof mlt_filter_s * \param self a filter * \return the duration or zero if unlimited */ mlt_position mlt_filter_get_length(mlt_filter self) { mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); mlt_position in = mlt_properties_get_position(properties, "in"); mlt_position out = mlt_properties_get_position(properties, "out"); return (out > 0) ? (out - in + 1) : 0; } /** Get the duration. * * This version works with filters with no explicit in and out by getting the * length of the frame's producer. * * \public \memberof mlt_filter_s * \param self a filter * \param frame a frame from which to get its producer * \return the duration or zero if unlimited */ mlt_position mlt_filter_get_length2(mlt_filter self, mlt_frame frame) { mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); mlt_position in = mlt_properties_get_position(properties, "in"); mlt_position out = mlt_properties_get_position(properties, "out"); if (out == 0 && frame) { // If always active, use the frame's producer mlt_producer producer = mlt_frame_get_original_producer(frame); if (producer) { producer = mlt_producer_cut_parent(producer); in = mlt_producer_get_in(producer); out = mlt_producer_get_out(producer); } } return (out > 0) ? (out - in + 1) : 0; } /** Get the position within the filter. * * The position is relative to the in point. * This will only be valid once mlt_filter_process is called. * * \public \memberof mlt_filter_s * \param self a filter * \param frame a frame * \return the position */ mlt_position mlt_filter_get_position(mlt_filter self, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(self); mlt_position in = mlt_properties_get_position(properties, "in"); const char *unique_id = mlt_properties_get(properties, "_unique_id"); char name[64]; // Make the properties key from unique id snprintf(name, sizeof(name), "pos.%s", unique_id); return mlt_properties_get_position(MLT_FRAME_PROPERTIES(frame), name) - in; } /** Get the percent complete. * * This will only be valid once mlt_filter_process is called. * * \public \memberof mlt_filter_s * \param self a filter * \param frame a frame * \return the progress in the range 0.0 to 1.0 */ double mlt_filter_get_progress(mlt_filter self, mlt_frame frame) { double position = mlt_filter_get_position(self, frame); double length = mlt_filter_get_length2(self, frame); if (length > 1) return position / (length - 1); else return 1.0; } /** Process the frame. * * When fetching the frame position in a subclass process method, the frame's * position is relative to the filter's producer - not the filter's in point * or timeline. * * \public \memberof mlt_filter_s * \param self a filter * \param frame a frame * \return a frame */ mlt_frame mlt_filter_process(mlt_filter self, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(self); int disable = mlt_properties_get_int(properties, "disable"); const char *unique_id = mlt_properties_get(properties, "_unique_id"); mlt_position position = mlt_frame_get_position(frame); char name[64]; // Make the properties key from unique id snprintf(name, sizeof(name), "pos.%s", unique_id); // Save the position on the frame mlt_properties_set_position(MLT_FRAME_PROPERTIES(frame), name, position); if (disable || !self || !self->process) { return frame; } else { // Add a reference to this filter on the frame mlt_properties_inc_ref(MLT_FILTER_PROPERTIES(self)); snprintf(name, sizeof(name), "filter.%s", unique_id); name[sizeof(name) - 1] = '\0'; mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), name, self, 0, (mlt_destructor) mlt_filter_close, NULL); return self->process(self, frame); } } /** Get a frame from this filter. * * \private \memberof mlt_filter_s * \param service a service * \param[out] frame a frame by reference * \param index as determined by the producer * \return true on error */ static int filter_get_frame(mlt_service service, mlt_frame_ptr frame, int index) { mlt_filter self = service->child; // Get coords in/out/track int track = mlt_filter_get_track(self); int in = mlt_filter_get_in(self); int out = mlt_filter_get_out(self); // Get the producer this is connected to mlt_service producer = mlt_service_producer(&self->parent); // If the frame request is for this filters track, we need to process it if (index == track || track == -1) { int ret = mlt_service_get_frame(producer, frame, index); if (ret == 0) { mlt_position position = mlt_frame_get_position(*frame); if (position >= in && (out == 0 || position <= out)) *frame = mlt_filter_process(self, *frame); return 0; } else { *frame = mlt_frame_init(service); return 0; } } else { return mlt_service_get_frame(producer, frame, index); } } /** Close and destroy the filter. * * \public \memberof mlt_filter_s * \param self a filter */ void mlt_filter_close(mlt_filter self) { if (self != NULL && mlt_properties_dec_ref(MLT_FILTER_PROPERTIES(self)) <= 0) { if (self->close != NULL) { self->close(self); } else { self->parent.close = NULL; mlt_service_close(&self->parent); } free(self); } } mlt-7.38.0/src/framework/mlt_filter.h000664 000000 000000 00000005547 15172202314 017473 0ustar00rootroot000000 000000 /** * \file mlt_filter.h * \brief abstraction for all filter services * \see mlt_filter_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_FILTER_H #define MLT_FILTER_H #include "mlt_export.h" #include "mlt_service.h" /** \brief Filter abstract service class * * A filter is a service that may modify the output of a single producer. * * \extends mlt_service_s * \properties \em track the index of the track of a multitrack on which the filter is applied * \properties \em service a reference to the service to which this filter is attached. * \properties \em disable Set this to disable the filter while keeping it in the object model. * Currently this is not cleared when the filter is detached. */ struct mlt_filter_s { /** We're implementing service here */ struct mlt_service_s parent; /** public virtual */ void (*close)(mlt_filter); /** protected filter method */ mlt_frame (*process)(mlt_filter, mlt_frame); /** Protected */ void *child; }; #define MLT_FILTER_SERVICE(filter) (&(filter)->parent) #define MLT_FILTER_PROPERTIES(filter) MLT_SERVICE_PROPERTIES(MLT_FILTER_SERVICE(filter)) MLT_EXPORT int mlt_filter_init(mlt_filter self, void *child); MLT_EXPORT mlt_filter mlt_filter_new(); MLT_EXPORT mlt_service mlt_filter_service(mlt_filter self); MLT_EXPORT mlt_properties mlt_filter_properties(mlt_filter self); MLT_EXPORT mlt_frame mlt_filter_process(mlt_filter self, mlt_frame that); MLT_EXPORT int mlt_filter_connect(mlt_filter self, mlt_service producer, int index); MLT_EXPORT void mlt_filter_set_in_and_out(mlt_filter self, mlt_position in, mlt_position out); MLT_EXPORT int mlt_filter_get_track(mlt_filter self); MLT_EXPORT mlt_position mlt_filter_get_in(mlt_filter self); MLT_EXPORT mlt_position mlt_filter_get_out(mlt_filter self); MLT_EXPORT mlt_position mlt_filter_get_length(mlt_filter self); MLT_EXPORT mlt_position mlt_filter_get_length2(mlt_filter self, mlt_frame frame); MLT_EXPORT mlt_position mlt_filter_get_position(mlt_filter self, mlt_frame frame); MLT_EXPORT double mlt_filter_get_progress(mlt_filter self, mlt_frame frame); MLT_EXPORT void mlt_filter_close(mlt_filter); #endif mlt-7.38.0/src/framework/mlt_frame.c000664 000000 000000 00000123221 15172202314 017261 0ustar00rootroot000000 000000 /** * \file mlt_frame.c * \brief interface for all frame classes * \see mlt_frame_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_frame.h" #include "mlt_factory.h" #include "mlt_image.h" #include "mlt_log.h" #include "mlt_producer.h" #include "mlt_profile.h" #include #include #include /** Construct a frame object. * * \public \memberof mlt_frame_s * \param service the pointer to any service that can provide access to the profile * \return a frame object on success or NULL if there was an allocation error */ mlt_frame mlt_frame_init(mlt_service service) { // Allocate a frame mlt_frame self = calloc(1, sizeof(struct mlt_frame_s)); if (self != NULL) { mlt_profile profile = mlt_service_profile(service); // Initialise the properties mlt_properties properties = &self->parent; mlt_properties_init(properties, self); // Set default properties on the frame mlt_properties_set_position(properties, "_position", 0.0); mlt_properties_set_data(properties, "image", NULL, 0, NULL, NULL); mlt_properties_set_int(properties, "width", profile ? profile->width : 720); mlt_properties_set_int(properties, "height", profile ? profile->height : 576); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(NULL)); mlt_properties_set_data(properties, "audio", NULL, 0, NULL, NULL); mlt_properties_set_data(properties, "alpha", NULL, 0, NULL, NULL); // Construct stacks for frames and methods self->stack_image = mlt_deque_init(); self->stack_audio = mlt_deque_init(); self->stack_service = mlt_deque_init(); } return self; } /** Get a frame's properties. * * \public \memberof mlt_frame_s * \param self a frame * \return the frame's properties or NULL if an invalid frame is supplied */ mlt_properties mlt_frame_properties(mlt_frame self) { return self != NULL ? &self->parent : NULL; } /** Determine if the frame will produce a test card image. * * \public \memberof mlt_frame_s * \param self a frame * \return true (non-zero) if this will produce from a test card */ int mlt_frame_is_test_card(mlt_frame self) { mlt_properties properties = MLT_FRAME_PROPERTIES(self); return (mlt_deque_count(self->stack_image) == 0 && !mlt_properties_get_data(properties, "image", NULL)) || mlt_properties_get_int(properties, "test_image"); } /** Determine if the frame will produce audio from a test card. * * \public \memberof mlt_frame_s * \param self a frame * \return true (non-zero) if this will produce from a test card */ int mlt_frame_is_test_audio(mlt_frame self) { mlt_properties properties = MLT_FRAME_PROPERTIES(self); return (mlt_deque_count(self->stack_audio) == 0 && !mlt_properties_get_data(properties, "audio", NULL)) || mlt_properties_get_int(properties, "test_audio"); } /** Get the sample aspect ratio of the frame. * * \public \memberof mlt_frame_s * \param self a frame * \return the aspect ratio */ double mlt_frame_get_aspect_ratio(mlt_frame self) { return mlt_properties_get_double(MLT_FRAME_PROPERTIES(self), "aspect_ratio"); } /** Set the sample aspect ratio of the frame. * * \public \memberof mlt_frame_s * \param self a frame * \param value the new image sample aspect ratio * \return true if error */ int mlt_frame_set_aspect_ratio(mlt_frame self, double value) { return mlt_properties_set_double(MLT_FRAME_PROPERTIES(self), "aspect_ratio", value); } /** Get the time position of this frame. * * This position is not necessarily the position as the original * producer knows it. It could be the position that the playlist, * multitrack, or tractor producer set. * * \public \memberof mlt_frame_s * \param self a frame * \return the position * \see mlt_frame_original_position */ mlt_position mlt_frame_get_position(mlt_frame self) { int pos = mlt_properties_get_position(MLT_FRAME_PROPERTIES(self), "_position"); return pos < 0 ? 0 : pos; } /** Get the original time position of this frame. * * This is the position that the original producer set on the frame. * * \public \memberof mlt_frame_s * \param self a frame * \return the position */ mlt_position mlt_frame_original_position(mlt_frame self) { int pos = mlt_properties_get_position(MLT_FRAME_PROPERTIES(self), "original_position"); return pos < 0 ? 0 : pos; } /** Set the time position of this frame. * * \public \memberof mlt_frame_s * \param self a frame * \param value the position * \return true if error */ int mlt_frame_set_position(mlt_frame self, mlt_position value) { // Only set the original_position the first time. if (!mlt_properties_get(MLT_FRAME_PROPERTIES(self), "original_position")) mlt_properties_set_position(MLT_FRAME_PROPERTIES(self), "original_position", value); return mlt_properties_set_position(MLT_FRAME_PROPERTIES(self), "_position", value); } /** Stack a get_image callback. * * \public \memberof mlt_frame_s * \param self a frame * \param get_image the get_image callback * \return true if error */ int mlt_frame_push_get_image(mlt_frame self, mlt_get_image get_image) { return mlt_deque_push_back(self->stack_image, get_image); } /** Pop a get_image callback. * * \public \memberof mlt_frame_s * \param self a frame * \return the get_image callback */ mlt_get_image mlt_frame_pop_get_image(mlt_frame self) { return mlt_deque_pop_back(self->stack_image); } /** Push a frame. * * \public \memberof mlt_frame_s * \param self a frame * \param that the frame to push onto \p self * \return true if error */ int mlt_frame_push_frame(mlt_frame self, mlt_frame that) { return mlt_deque_push_back(self->stack_image, that); } /** Pop a frame. * * \public \memberof mlt_frame_s * \param self a frame * \return a frame that was previously pushed */ mlt_frame mlt_frame_pop_frame(mlt_frame self) { return mlt_deque_pop_back(self->stack_image); } /** Push a service. * * \public \memberof mlt_frame_s * \param self a frame * \param that an opaque pointer * \return true if error */ int mlt_frame_push_service(mlt_frame self, void *that) { return mlt_deque_push_back(self->stack_image, that); } /** Pop a service. * * \public \memberof mlt_frame_s * \param self a frame * \return an opaque pointer to something previously pushed */ void *mlt_frame_pop_service(mlt_frame self) { return mlt_deque_pop_back(self->stack_image); } /** Push a number. * * \public \memberof mlt_frame_s * \param self a frame * \param that an integer * \return true if error */ int mlt_frame_push_service_int(mlt_frame self, int that) { return mlt_deque_push_back_int(self->stack_image, that); } /** Pop a number. * * \public \memberof mlt_frame_s * \param self a frame * \return an integer that was previously pushed */ int mlt_frame_pop_service_int(mlt_frame self) { return mlt_deque_pop_back_int(self->stack_image); } /** Push an audio item on the stack. * * \public \memberof mlt_frame_s * \param self a frame * \param that an opaque pointer * \return true if error */ int mlt_frame_push_audio(mlt_frame self, void *that) { return mlt_deque_push_back(self->stack_audio, that); } /** Pop an audio item from the stack * * \public \memberof mlt_frame_s * \param self a frame * \return an opaque pointer to something that was pushed onto the frame's audio stack */ void *mlt_frame_pop_audio(mlt_frame self) { return mlt_deque_pop_back(self->stack_audio); } /** Return the service stack * * \public \memberof mlt_frame_s * \param self a frame * \return the service stack */ mlt_deque mlt_frame_service_stack(mlt_frame self) { return self->stack_service; } /** Set a new image on the frame. * * \public \memberof mlt_frame_s * \param self a frame * \param image a pointer to the raw image data * \param size the size of the image data in bytes (optional) * \param destroy a function to deallocate \p image when the frame is closed (optional) * \return true if error */ int mlt_frame_set_image(mlt_frame self, uint8_t *image, int size, mlt_destructor destroy) { return mlt_properties_set_data(MLT_FRAME_PROPERTIES(self), "image", image, size, destroy, NULL); } /** Set a new alpha channel on the frame. * * \public \memberof mlt_frame_s * \param self a frame * \param alpha a pointer to the alpha channel * \param size the size of the alpha channel in bytes (optional) * \param destroy a function to deallocate \p alpha when the frame is closed (optional) * \return true if error */ int mlt_frame_set_alpha(mlt_frame self, uint8_t *alpha, int size, mlt_destructor destroy) { return mlt_properties_set_data(MLT_FRAME_PROPERTIES(self), "alpha", alpha, size, destroy, NULL); } /** Replace image stack with the information provided. * * This might prove to be unreliable and restrictive - the idea is that a transition * which normally uses two images may decide to only use the b frame (ie: in the case * of a composite where the b frame completely obscures the a frame). * * The image must be writable and the destructor for the image itself must be taken * care of on another frame and that frame cannot have a replace applied to it... * Further it assumes that no alpha mask is in use. * * For these reasons, it can only be used in a specific situation - when you have * multiple tracks each with their own transition and these transitions are applied * in a strictly reversed order (ie: highest numbered [lowest track] is processed * first). * * More reliable approach - the cases should be detected during the process phase * and the upper tracks should simply not be invited to stack... * * \public \memberof mlt_frame_s * \param self a frame * \param image a new image * \param format the image format * \param width the width of the new image * \param height the height of the new image */ void mlt_frame_replace_image( mlt_frame self, uint8_t *image, mlt_image_format format, int width, int height) { // Remove all items from the stack while (mlt_deque_pop_back(self->stack_image)) ; // Update the information mlt_properties_set_data(MLT_FRAME_PROPERTIES(self), "image", image, 0, NULL, NULL); mlt_properties_set_int(MLT_FRAME_PROPERTIES(self), "width", width); mlt_properties_set_int(MLT_FRAME_PROPERTIES(self), "height", height); mlt_properties_set_int(MLT_FRAME_PROPERTIES(self), "format", format); } static int generate_test_image(mlt_properties properties, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { mlt_producer producer = mlt_properties_get_data(properties, "test_card_producer", NULL); mlt_image_format requested_format = *format; int error = 1; if (producer) { mlt_frame test_frame = NULL; mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &test_frame, 0); if (test_frame) { mlt_properties test_properties = MLT_FRAME_PROPERTIES(test_frame); mlt_properties_set_data(properties, "test_card_frame", test_frame, 0, (mlt_destructor) mlt_frame_close, NULL); mlt_properties_set(test_properties, "consumer.rescale", mlt_properties_get(properties, "consumer.rescale")); error = mlt_frame_get_image(test_frame, buffer, format, width, height, writable); if (!error && buffer && *buffer) { mlt_properties_set_double(properties, "aspect_ratio", mlt_frame_get_aspect_ratio(test_frame)); mlt_properties_set_int(properties, "width", *width); mlt_properties_set_int(properties, "height", *height); if (test_frame->convert_image && requested_format != mlt_image_none) test_frame->convert_image(test_frame, buffer, format, requested_format); mlt_properties_set_int(properties, "format", *format); } } else { mlt_properties_set_data(properties, "test_card_producer", NULL, 0, NULL, NULL); } } if (error && buffer) { *width = *width == 0 ? 720 : *width; *height = *height == 0 ? 576 : *height; switch (*format) { case mlt_image_rgb: case mlt_image_rgba: case mlt_image_yuv422: case mlt_image_yuv420p: case mlt_image_yuv422p16: case mlt_image_yuv420p10: case mlt_image_yuv444p10: case mlt_image_rgba64: break; case mlt_image_none: case mlt_image_movit: case mlt_image_opengl_texture: *format = mlt_image_yuv422; break; case mlt_image_invalid: *format = mlt_image_invalid; break; } struct mlt_image_s img; mlt_image_set_values(&img, NULL, *format, *width, *height); mlt_image_alloc_data(&img); if (mlt_properties_get_int(properties, "test_audio")) { const char *color_range = mlt_properties_get(properties, "consumer.color_range"); mlt_image_fill_white(&img, mlt_image_full_range(color_range)); } else { mlt_image_fill_checkerboard(&img, mlt_properties_get_double(properties, "aspect_ratio")); } *buffer = img.data; mlt_properties_set_int(properties, "format", *format); mlt_properties_set_int(properties, "width", *width); mlt_properties_set_int(properties, "height", *height); mlt_properties_set_data(properties, "image", *buffer, 0, img.release_data, NULL); mlt_properties_set_int(properties, "test_image", 1); error = 0; } return error; } /** Get the image associated to the frame. * * You should express the desired format, width, and height as inputs. As long * as the loader producer was used to generate this or the imageconvert filter * was attached, then you will get the image back in the format you desire. * However, you do not always get the width and height you request depending * on properties and filters. You do not need to supply a pre-allocated * buffer, but you should always supply the desired image format. * * \public \memberof mlt_frame_s * \param self a frame * \param[out] buffer an image buffer * \param[in,out] format the image format * \param[in,out] width the horizontal size in pixels * \param[in,out] height the vertical size in pixels * \param writable whether or not you will need to be able to write to the memory returned in \p buffer * \return true if error * \todo Better describe the width and height as inputs. */ int mlt_frame_get_image(mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { mlt_properties properties = MLT_FRAME_PROPERTIES(self); mlt_get_image get_image = mlt_frame_pop_get_image(self); mlt_image_format requested_format = *format; int error = 0; if (get_image) { mlt_properties_set_int(properties, "image_count", mlt_properties_get_int(properties, "image_count") - 1); error = get_image(self, buffer, format, width, height, writable); if (!error && buffer && *buffer) { mlt_properties_set_int(properties, "width", *width); mlt_properties_set_int(properties, "height", *height); if (self->convert_image && requested_format != mlt_image_none) self->convert_image(self, buffer, format, requested_format); mlt_properties_set_int(properties, "format", *format); } else { error = generate_test_image(properties, buffer, format, width, height, writable); } } else if (mlt_properties_get_data(properties, "image", NULL) && buffer) { *format = mlt_properties_get_int(properties, "format"); *buffer = mlt_properties_get_data(properties, "image", NULL); *width = mlt_properties_get_int(properties, "width"); *height = mlt_properties_get_int(properties, "height"); if (self->convert_image && *buffer && requested_format != mlt_image_none) { self->convert_image(self, buffer, format, requested_format); mlt_properties_set_int(properties, "format", *format); } } else { error = generate_test_image(properties, buffer, format, width, height, writable); } return error; } /** Get the alpha channel associated to the frame (without creating if it has not). * * This returns NULL if the frame's image format is \p mlt_image_rgba or mlt_image_rgba64. * \public \memberof mlt_frame_s * \param self a frame * \return the alpha channel or NULL */ uint8_t *mlt_frame_get_alpha(mlt_frame self) { uint8_t *alpha = NULL; if (self != NULL) { alpha = mlt_properties_get_data(&self->parent, "alpha", NULL); if (alpha) { mlt_image_format format = mlt_properties_get_int(&self->parent, "format"); if (mlt_image_rgba == format || format == mlt_image_rgba64) { alpha = NULL; } } } return alpha; } /** Get the alpha channel associated to the frame and its size. * * This returns NULL and sets \p size to 0 if the frame's image format is * \p mlt_image_rgba or mlt_image_rgba64. * * \public \memberof mlt_frame_s * \param self a frame * \param[out] size the size of the alpha channel data in bytes * \return the alpha channel or NULL */ uint8_t *mlt_frame_get_alpha_size(mlt_frame self, int *size) { uint8_t *alpha = NULL; if (self) { alpha = mlt_properties_get_data(&self->parent, "alpha", size); if (alpha) { mlt_image_format format = mlt_properties_get_int(&self->parent, "format"); if (mlt_image_rgba == format || mlt_image_rgba64 == format) { alpha = NULL; if (size) { size = 0; } } } } return alpha; } /** Get the audio associated to the frame. * * You should express the desired format, frequency, channels, and samples as inputs. As long * as the loader producer was used to generate this or the audioconvert filter * was attached, then you will get the audio back in the format you desire. * However, you do not always get the channels and samples you request depending * on properties and filters. You do not need to supply a pre-allocated * buffer, but you should always supply the desired audio format. * The audio is always in interleaved format. * You should use the \p mlt_audio_sample_calculator to determine the number of samples you want. * * \public \memberof mlt_frame_s * \param self a frame * \param[out] buffer an audio buffer * \param[in,out] format the audio format * \param[in,out] frequency the sample rate * \param[in,out] channels the number of audio channels * \param[in,out] samples the number of samples per frame * \return true if error */ int mlt_frame_get_audio(mlt_frame self, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_get_audio get_audio = mlt_frame_pop_audio(self); mlt_properties properties = MLT_FRAME_PROPERTIES(self); int hide = mlt_properties_get_int(properties, "test_audio"); mlt_audio_format requested_format = *format; if (hide == 0 && get_audio != NULL) { get_audio(self, buffer, format, frequency, channels, samples); mlt_properties_set_int(properties, "audio_frequency", *frequency); mlt_properties_set_int(properties, "audio_channels", *channels); mlt_properties_set_int(properties, "audio_samples", *samples); mlt_properties_set_int(properties, "audio_format", *format); if (self->convert_audio && *buffer && requested_format != mlt_audio_none) self->convert_audio(self, buffer, format, requested_format); } else if (mlt_properties_get_data(properties, "audio", NULL)) { *buffer = mlt_properties_get_data(properties, "audio", NULL); *format = mlt_properties_get_int(properties, "audio_format"); *frequency = mlt_properties_get_int(properties, "audio_frequency"); *channels = mlt_properties_get_int(properties, "audio_channels"); *samples = mlt_properties_get_int(properties, "audio_samples"); if (self->convert_audio && *buffer && requested_format != mlt_audio_none) self->convert_audio(self, buffer, format, requested_format); } else { int size = 0; *samples = *samples <= 0 ? 1920 : *samples; *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; *format = *format == mlt_audio_none ? mlt_audio_s16 : *format; mlt_properties_set_int(properties, "audio_frequency", *frequency); mlt_properties_set_int(properties, "audio_channels", *channels); mlt_properties_set_int(properties, "audio_samples", *samples); mlt_properties_set_int(properties, "audio_format", *format); size = mlt_audio_format_size(*format, *samples, *channels); if (size) *buffer = mlt_pool_alloc(size); else *buffer = NULL; if (*buffer) memset(*buffer, 0, size); mlt_properties_set_data(properties, "audio", *buffer, size, (mlt_destructor) mlt_pool_release, NULL); mlt_properties_set_int(properties, "test_audio", 1); } // TODO: This does not belong here if (*format == mlt_audio_s16 && mlt_properties_get(properties, "meta.volume") && *buffer) { double value = mlt_properties_get_double(properties, "meta.volume"); if (value == 0.0) { memset(*buffer, 0, *samples * *channels * 2); } else if (value != 1.0) { int total = *samples * *channels; int16_t *p = *buffer; while (total--) { *p = *p * value; p++; } } mlt_properties_set(properties, "meta.volume", NULL); } return 0; } /** Set the audio on a frame. * * \public \memberof mlt_frame_s * \param self a frame * \param buffer an buffer containing audio samples * \param format the format of the audio in the \p buffer * \param size the total size of the buffer (optional) * \param destructor a function that releases or deallocates the \p buffer * \return true if error */ int mlt_frame_set_audio( mlt_frame self, void *buffer, mlt_audio_format format, int size, mlt_destructor destructor) { mlt_properties_set_int(MLT_FRAME_PROPERTIES(self), "audio_format", format); return mlt_properties_set_data(MLT_FRAME_PROPERTIES(self), "audio", buffer, size, destructor, NULL); } /** Get audio on a frame as a waveform image. * * This generates an 8-bit grayscale image representation of the audio in a * frame. Currently, this only really works for 2 channels. * This allocates the bitmap using mlt_pool so you should release the return * value with \p mlt_pool_release. * * \public \memberof mlt_frame_s * \param self a frame * \param w the width of the image * \param h the height of the image to create * \return a pointer to a new bitmap */ unsigned char *mlt_frame_get_waveform(mlt_frame self, int w, int h) { int16_t *pcm = NULL; mlt_properties properties = MLT_FRAME_PROPERTIES(self); mlt_audio_format format = mlt_audio_s16; int frequency = 16000; int channels = 2; mlt_producer producer = mlt_frame_get_original_producer(self); double fps = mlt_producer_get_fps(mlt_producer_cut_parent(producer)); int samples = mlt_audio_calculate_frame_samples(fps, frequency, mlt_frame_get_position(self)); // Increase audio resolution proportional to requested image size while (samples < w) { frequency += 16000; samples = mlt_audio_calculate_frame_samples(fps, frequency, mlt_frame_get_position(self)); } // Get the pcm data mlt_frame_get_audio(self, (void **) &pcm, &format, &frequency, &channels, &samples); // Make an 8-bit buffer large enough to hold rendering int size = w * h; if (size <= 0) return NULL; unsigned char *bitmap = (unsigned char *) mlt_pool_alloc(size); if (bitmap != NULL) memset(bitmap, 0, size); else return NULL; mlt_properties_set_data(properties, "waveform", bitmap, size, (mlt_destructor) mlt_pool_release, NULL); // Render vertical lines int16_t *ubound = pcm + samples * channels; int skip = samples / w; skip = !skip ? 1 : skip; unsigned char gray = 0xFF / skip; int i, j, k; // Iterate sample stream and along x coordinate for (i = 0; pcm < ubound; i++) { // pcm data has channels interleaved for (j = 0; j < channels; j++, pcm++) { // Determine sample's magnitude from 2s complement; int pcm_magnitude = *pcm < 0 ? ~(*pcm) + 1 : *pcm; // The height of a line is the ratio of the magnitude multiplied by // the vertical resolution of a single channel int height = h * pcm_magnitude / channels / 2 / 32768; // Determine the starting y coordinate - left top, right bottom int displacement = h * (j * 2 + 1) / channels / 2 - (*pcm < 0 ? 0 : height); // Position buffer pointer using y coordinate, stride, and x coordinate unsigned char *p = bitmap + i / skip + displacement * w; // Draw vertical line for (k = 0; k < height + 1; k++) if (*pcm < 0) p[w * k] = (k == 0) ? 0xFF : p[w * k] + gray; else p[w * k] = (k == height) ? 0xFF : p[w * k] + gray; } } return bitmap; } /** Get the end service that produced self frame. * * This fetches the first producer of the frame and not any producers that * encapsulate it. * * \public \memberof mlt_frame_s * \param self a frame * \return a producer */ mlt_producer mlt_frame_get_original_producer(mlt_frame self) { if (self != NULL) return mlt_properties_get_data(MLT_FRAME_PROPERTIES(self), "_producer", NULL); return NULL; } /** Destroy the frame. * * \public \memberof mlt_frame_s * \param self a frame */ void mlt_frame_close(mlt_frame self) { if (self != NULL && mlt_properties_dec_ref(MLT_FRAME_PROPERTIES(self)) <= 0) { mlt_deque_close(self->stack_image); mlt_deque_close(self->stack_audio); while (mlt_deque_peek_back(self->stack_service)) mlt_service_close(mlt_deque_pop_back(self->stack_service)); mlt_deque_close(self->stack_service); mlt_properties_close(&self->parent); free(self); } } /***** convenience functions *****/ void mlt_frame_write_ppm(mlt_frame frame) { int width = 0; int height = 0; mlt_image_format format = mlt_image_rgb; uint8_t *image; if (mlt_frame_get_image(frame, &image, &format, &width, &height, 0) == 0) { FILE *file; char filename[16]; sprintf(filename, "frame-%05d.ppm", (int) mlt_frame_get_position(frame)); file = mlt_fopen(filename, "wb"); if (!file) return; fprintf(file, "P6\n%d %d\n255\n", width, height); fwrite(image, width * height * 3, 1, file); fclose(file); } } /** Get or create a properties object unique to this service instance. * * Use this function to hold a service's processing parameters for this * particular frame. Set the parameters in the service's process function. * Then, get the parameters in the function it pushes to the frame's audio * or image stack. This makes the service more parallel by reducing race * conditions and less sensitive to multiple instances (by not setting a * non-unique property on the frame). Creation and destruction of the * properties object is handled automatically. * * \public \memberof mlt_frame_s * \param self a frame * \param service a service * \return a properties object */ mlt_properties mlt_frame_unique_properties(mlt_frame self, mlt_service service) { mlt_properties frame_props = MLT_FRAME_PROPERTIES(self); mlt_properties service_props = MLT_SERVICE_PROPERTIES(service); char *unique = mlt_properties_get(service_props, "_unique_id"); mlt_properties instance_props = mlt_properties_get_data(frame_props, unique, NULL); if (!instance_props) { instance_props = mlt_properties_new(); mlt_properties_set_data(frame_props, unique, instance_props, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set_lcnumeric(instance_props, mlt_properties_get_lcnumeric(service_props)); mlt_properties_set_data(instance_props, "_profile", mlt_service_profile(service), 0, NULL, NULL); } return instance_props; } /** Get a properties object unique to this service instance. * * Unlike \p mlt_frame_unique_properties, this function does not create the * service-unique properties object if it does not exist. * * \public \memberof mlt_frame_s * \param self a frame * \param service a service * \return a properties object or NULL if it does not exist */ mlt_properties mlt_frame_get_unique_properties(mlt_frame self, mlt_service service) { char *unique = mlt_properties_get(MLT_SERVICE_PROPERTIES(service), "_unique_id"); return mlt_properties_get_data(MLT_FRAME_PROPERTIES(self), unique, NULL); } /** Make a copy of a frame. * * This does not copy the get_image/get_audio processing stacks or any * data properties other than the audio and image. * * \public \memberof mlt_frame_s * \param self the frame to clone * \param is_deep a boolean to indicate whether to make a deep copy of the audio * and video data chunks or to make a shallow copy by pointing to the supplied frame * \return a almost-complete copy of the frame * \todo copy the processing deques */ mlt_frame mlt_frame_clone(mlt_frame self, int is_deep) { mlt_frame new_frame = mlt_frame_init(NULL); mlt_properties properties = MLT_FRAME_PROPERTIES(self); mlt_properties new_props = MLT_FRAME_PROPERTIES(new_frame); void *data, *copy; int size = 0; mlt_properties_inherit(new_props, properties); // Carry over some special data properties for the multi consumer. mlt_properties_set_data(new_props, "_producer", mlt_frame_get_original_producer(self), 0, NULL, NULL); mlt_properties_set_data(new_props, "movit.convert", mlt_properties_get_data(properties, "movit.convert", NULL), 0, NULL, NULL); mlt_properties_set_data(new_props, "_movit cpu_convert", mlt_properties_get_data(properties, "_movit cpu_convert", NULL), 0, NULL, NULL); if (is_deep) { data = mlt_properties_get_data(properties, "audio", &size); if (data) { if (!size) size = mlt_audio_format_size(mlt_properties_get_int(properties, "audio_format"), mlt_properties_get_int(properties, "audio_samples"), mlt_properties_get_int(properties, "audio_channels")); copy = mlt_pool_alloc(size); memcpy(copy, data, size); mlt_properties_set_data(new_props, "audio", copy, size, mlt_pool_release, NULL); } size = 0; data = mlt_properties_get_data(properties, "image", &size); if (data && mlt_image_movit != mlt_properties_get_int(properties, "format")) { int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); if (!size) size = mlt_image_format_size(mlt_properties_get_int(properties, "format"), width, height, NULL); copy = mlt_pool_alloc(size); memcpy(copy, data, size); mlt_properties_set_data(new_props, "image", copy, size, mlt_pool_release, NULL); size = 0; data = mlt_frame_get_alpha_size(self, &size); if (data) { if (!size) size = width * height; copy = mlt_pool_alloc(size); memcpy(copy, data, size); mlt_properties_set_data(new_props, "alpha", copy, size, mlt_pool_release, NULL); }; } } else { // This frame takes a reference on the original frame since the data is a shallow copy. mlt_properties_inc_ref(properties); mlt_properties_set_data(new_props, "_cloned_frame", self, 0, (mlt_destructor) mlt_frame_close, NULL); // Copy properties data = mlt_properties_get_data(properties, "audio", &size); mlt_properties_set_data(new_props, "audio", data, size, NULL, NULL); size = 0; data = mlt_properties_get_data(properties, "image", &size); mlt_properties_set_data(new_props, "image", data, size, NULL, NULL); size = 0; data = mlt_frame_get_alpha_size(self, &size); mlt_properties_set_data(new_props, "alpha", data, size, NULL, NULL); } return new_frame; } /** Make a copy of a frame and audio. * * This does not copy the get_image/get_audio processing stacks or any * data properties other than the audio. * * \public \memberof mlt_frame_s * \param self the frame to clone * \param is_deep a boolean to indicate whether to make a deep copy of the audio * data chunks or to make a shallow copy by pointing to the supplied frame * \return a almost-complete copy of the frame * \todo copy the processing deques */ mlt_frame mlt_frame_clone_audio(mlt_frame self, int is_deep) { mlt_frame new_frame = mlt_frame_init(NULL); mlt_properties properties = MLT_FRAME_PROPERTIES(self); mlt_properties new_props = MLT_FRAME_PROPERTIES(new_frame); void *data, *copy; int size = 0; mlt_properties_inherit(new_props, properties); // Carry over some special data properties for the multi consumer. mlt_properties_set_data(new_props, "_producer", mlt_frame_get_original_producer(self), 0, NULL, NULL); mlt_properties_set_data(new_props, "movit.convert", mlt_properties_get_data(properties, "movit.convert", NULL), 0, NULL, NULL); mlt_properties_set_data(new_props, "_movit cpu_convert", mlt_properties_get_data(properties, "_movit cpu_convert", NULL), 0, NULL, NULL); if (is_deep) { data = mlt_properties_get_data(properties, "audio", &size); if (data) { if (!size) size = mlt_audio_format_size(mlt_properties_get_int(properties, "audio_format"), mlt_properties_get_int(properties, "audio_samples"), mlt_properties_get_int(properties, "audio_channels")); copy = mlt_pool_alloc(size); memcpy(copy, data, size); mlt_properties_set_data(new_props, "audio", copy, size, mlt_pool_release, NULL); } } else { // This frame takes a reference on the original frame since the data is a shallow copy. mlt_properties_inc_ref(properties); mlt_properties_set_data(new_props, "_cloned_frame", self, 0, (mlt_destructor) mlt_frame_close, NULL); // Copy properties data = mlt_properties_get_data(properties, "audio", &size); mlt_properties_set_data(new_props, "audio", data, size, NULL, NULL); } return new_frame; } /** Make a copy of a frame and image. * * This does not copy the get_image/get_audio processing stacks or any * data properties other than the image. * * \public \memberof mlt_frame_s * \param self the frame to clone * \param is_deep a boolean to indicate whether to make a deep copy of the * video data chunks or to make a shallow copy by pointing to the supplied frame * \return a almost-complete copy of the frame * \todo copy the processing deques */ mlt_frame mlt_frame_clone_image(mlt_frame self, int is_deep) { mlt_frame new_frame = mlt_frame_init(NULL); mlt_properties properties = MLT_FRAME_PROPERTIES(self); mlt_properties new_props = MLT_FRAME_PROPERTIES(new_frame); void *data, *copy; int size = 0; mlt_properties_inherit(new_props, properties); // Carry over some special data properties for the multi consumer. mlt_properties_set_data(new_props, "_producer", mlt_frame_get_original_producer(self), 0, NULL, NULL); mlt_properties_set_data(new_props, "movit.convert", mlt_properties_get_data(properties, "movit.convert", NULL), 0, NULL, NULL); mlt_properties_set_data(new_props, "_movit cpu_convert", mlt_properties_get_data(properties, "_movit cpu_convert", NULL), 0, NULL, NULL); if (is_deep) { data = mlt_properties_get_data(properties, "image", &size); if (data && mlt_image_movit != mlt_properties_get_int(properties, "format")) { int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); if (!size) size = mlt_image_format_size(mlt_properties_get_int(properties, "format"), width, height, NULL); copy = mlt_pool_alloc(size); memcpy(copy, data, size); mlt_properties_set_data(new_props, "image", copy, size, mlt_pool_release, NULL); size = 0; data = mlt_frame_get_alpha_size(self, &size); if (data) { if (!size) size = width * height; copy = mlt_pool_alloc(size); memcpy(copy, data, size); mlt_properties_set_data(new_props, "alpha", copy, size, mlt_pool_release, NULL); }; } } else { // This frame takes a reference on the original frame since the data is a shallow copy. mlt_properties_inc_ref(properties); mlt_properties_set_data(new_props, "_cloned_frame", self, 0, (mlt_destructor) mlt_frame_close, NULL); // Copy properties size = 0; data = mlt_properties_get_data(properties, "image", &size); mlt_properties_set_data(new_props, "image", data, size, NULL, NULL); size = 0; data = mlt_frame_get_alpha_size(self, &size); mlt_properties_set_data(new_props, "alpha", data, size, NULL, NULL); } return new_frame; } mlt-7.38.0/src/framework/mlt_frame.h000664 000000 000000 00000022736 15172202314 017277 0ustar00rootroot000000 000000 /** * \file mlt_frame.h * \brief interface for all frame classes * \see mlt_frame_s * * Copyright (C) 2003-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_FRAME_H #define MLT_FRAME_H #include "mlt_audio.h" #include "mlt_deque.h" #include "mlt_export.h" #include "mlt_image.h" #include "mlt_properties.h" #include "mlt_service.h" /** Callback function to get video data. * */ typedef int (*mlt_get_image)(mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable); /** Callback function to get audio data. * */ typedef int (*mlt_get_audio)(mlt_frame self, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples); /** \brief Frame class * * The frame is the primary data object that gets passed around to and through services. * * \extends mlt_properties * \properties \em test_image set if the frame holds a "test card" image * \properties \em test_audio set if the frame holds "test card" audio * \properties \em _producer holds a reference to the frame's end producer * \properties \em _speed the current speed of the producer that generated the frame * \properties \em _position the position of the frame * \properties \em meta.* holds metadata * \properties \em hide set to 1 to hide the video, 2 to mute the audio * \properties \em last_track a flag to indicate an end-of-tracks frame * \properties \em previous \em frame a reference to the unfiltered preceding frame * (no speed factor applied, only available when \em _need_previous_next is set on the producer) * \properties \em next \em frame a reference to the unfiltered following frame * (no speed factor applied, only available when \em _need_previous_next is set on the producer) * \properties \em colorspace the standard for the YUV coefficients * \properties \em force_full_luma luma range handling: 1 for full range, 0 for scaling (DEPRECATED) * \properties \em color_trc the color transfer characteristic (gamma) * \properties \em audio_frequency the sample rate of the audio * \properties \em audio_channels the number of audio channels * \properties \em audio_samples the number of audio samples * \properties \em audio_format the mlt_audio_format for the audio on this frame * \properties \em format the mlt_image_format of the image on this frame * \properties \em width the horizontal resolution of the image * \properties \em height the vertical resolution of the image * \properties \em aspect_ratio the sample aspect ratio of the image * \properties \em full_range set if the video is full range - only applies to Y'CbCr * \properties \em meta.playlist.clip_position mlt_playlist sets this property * to the time position of this frame's clip in the playlist * \properties \em meta.playlist.clip_length mlt_playlist sets this property to * the playlist index of this frame's clip in the playlist */ struct mlt_frame_s { struct mlt_properties_s parent; /**< \private A frame extends properties. */ /** Convert the image format (callback function). * \param self a frame * \param[in,out] image a buffer of image data * \param[in,out] input the image format of supplied image data * \param output the image format to which to convert * \return true if error */ int (*convert_image)(mlt_frame self, uint8_t **image, mlt_image_format *input, mlt_image_format output); /** Convert the audio format (callback function). * \param self a frame * \param[in,out] audio a buffer of audio data * \param[in,out] input the audio format of supplied data * \param output the audio format to which to convert * \return true if error */ int (*convert_audio)(mlt_frame self, void **audio, mlt_audio_format *input, mlt_audio_format output); mlt_deque stack_image; /**< \private the image processing stack of operations and data */ mlt_deque stack_audio; /**< \private the audio processing stack of operations and data */ mlt_deque stack_service; /**< \private a general purpose data stack */ int is_processing; /**< \private indicates if a frame is or was processed by the parallel consumer */ }; #define MLT_FRAME_PROPERTIES(frame) (&(frame)->parent) #define MLT_FRAME_SERVICE_STACK(frame) ((frame)->stack_service) #define MLT_FRAME_IMAGE_STACK(frame) ((frame)->stack_image) #define MLT_FRAME_AUDIO_STACK(frame) ((frame)->stack_audio) MLT_EXPORT mlt_frame mlt_frame_init(mlt_service service); MLT_EXPORT mlt_properties mlt_frame_properties(mlt_frame self); MLT_EXPORT int mlt_frame_is_test_card(mlt_frame self); MLT_EXPORT int mlt_frame_is_test_audio(mlt_frame self); MLT_EXPORT double mlt_frame_get_aspect_ratio(mlt_frame self); MLT_EXPORT int mlt_frame_set_aspect_ratio(mlt_frame self, double value); MLT_EXPORT mlt_position mlt_frame_get_position(mlt_frame self); MLT_EXPORT mlt_position mlt_frame_original_position(mlt_frame self); MLT_EXPORT int mlt_frame_set_position(mlt_frame self, mlt_position value); MLT_EXPORT int mlt_frame_set_image(mlt_frame self, uint8_t *image, int size, mlt_destructor destroy); MLT_EXPORT int mlt_frame_set_alpha(mlt_frame self, uint8_t *alpha, int size, mlt_destructor destroy); MLT_EXPORT void mlt_frame_replace_image( mlt_frame self, uint8_t *image, mlt_image_format format, int width, int height); MLT_EXPORT int mlt_frame_get_image(mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable); MLT_EXPORT uint8_t *mlt_frame_get_alpha(mlt_frame self); MLT_EXPORT uint8_t *mlt_frame_get_alpha_size(mlt_frame self, int *size); MLT_EXPORT int mlt_frame_get_audio(mlt_frame self, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples); MLT_EXPORT int mlt_frame_set_audio( mlt_frame self, void *buffer, mlt_audio_format, int size, mlt_destructor); MLT_EXPORT unsigned char *mlt_frame_get_waveform(mlt_frame self, int w, int h); MLT_EXPORT int mlt_frame_push_get_image(mlt_frame self, mlt_get_image get_image); MLT_EXPORT mlt_get_image mlt_frame_pop_get_image(mlt_frame self); MLT_EXPORT int mlt_frame_push_frame(mlt_frame self, mlt_frame that); MLT_EXPORT mlt_frame mlt_frame_pop_frame(mlt_frame self); MLT_EXPORT int mlt_frame_push_service(mlt_frame self, void *that); MLT_EXPORT void *mlt_frame_pop_service(mlt_frame self); MLT_EXPORT int mlt_frame_push_service_int(mlt_frame self, int that); MLT_EXPORT int mlt_frame_pop_service_int(mlt_frame self); MLT_EXPORT int mlt_frame_push_audio(mlt_frame self, void *that); MLT_EXPORT void *mlt_frame_pop_audio(mlt_frame self); MLT_EXPORT mlt_deque mlt_frame_service_stack(mlt_frame self); MLT_EXPORT mlt_producer mlt_frame_get_original_producer(mlt_frame self); MLT_EXPORT void mlt_frame_close(mlt_frame self); MLT_EXPORT mlt_properties mlt_frame_unique_properties(mlt_frame self, mlt_service service); MLT_EXPORT mlt_properties mlt_frame_get_unique_properties(mlt_frame self, mlt_service service); MLT_EXPORT mlt_frame mlt_frame_clone(mlt_frame self, int is_deep); MLT_EXPORT mlt_frame mlt_frame_clone_audio(mlt_frame self, int is_deep); MLT_EXPORT mlt_frame mlt_frame_clone_image(mlt_frame self, int is_deep); /* convenience functions */ MLT_EXPORT void mlt_frame_write_ppm(mlt_frame frame); /** This macro scales RGB into the YUV gamut - y is scaled by 219/255 and uv by 224/255. */ #define RGB2YUV_601_SCALED(r, g, b, y, u, v) \ y = ((263 * r + 516 * g + 100 * b) >> 10) + 16; \ u = ((-152 * r - 300 * g + 450 * b) >> 10) + 128; \ v = ((450 * r - 377 * g - 73 * b) >> 10) + 128; /** This macro scales RGB into the YUV gamut - uv is scaled by 224/255 (y unused). */ #define RGB2UV_601_SCALED(r, g, b, u, v) \ u = ((-152 * r - 300 * g + 450 * b) >> 10) + 128; \ v = ((450 * r - 377 * g - 73 * b) >> 10) + 128; /** This macro scales YUV up into the full gamut of the RGB color space. */ #define YUV2RGB_601_SCALED(y, u, v, r, g, b) \ r = ((1192 * (y - 16) + 1634 * (v - 128)) >> 10); \ g = ((1192 * (y - 16) - 832 * (v - 128) - 401 * (u - 128)) >> 10); \ b = ((1192 * (y - 16) + 2066 * (u - 128)) >> 10); \ r = r < 0 ? 0 : r > 255 ? 255 : r; \ g = g < 0 ? 0 : g > 255 ? 255 : g; \ b = b < 0 ? 0 : b > 255 ? 255 : b; #endif mlt-7.38.0/src/framework/mlt_image.c000664 000000 000000 00000100043 15172202314 017246 0ustar00rootroot000000 000000 /** * \file mlt_image.c * \brief Image class * \see mlt_mlt_image_s * * Copyright (C) 2020-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_image.h" #include #include /** Allocate a new Image object. * * \return a new image object with default values set */ mlt_image mlt_image_new() { mlt_image self = calloc(1, sizeof(struct mlt_image_s)); self->close = free; return self; } /** Destroy an image object created by mlt_image_new(). * * \public \memberof mlt_image_s * \param self the Image object */ void mlt_image_close(mlt_image self) { if (self) { if (self->release_data) { self->release_data(self->data); } if (self->release_alpha) { self->release_alpha(self->alpha); } if (self->close) { self->close(self); } } } /** Set the most common values for the image. * * Less common values will be set to reasonable defaults. * * \public \memberof mlt_image_s * \param self the Image object * \param data the buffer that contains the image data * \param format the image format * \param width the width of the image * \param height the height of the image */ void mlt_image_set_values(mlt_image self, void *data, mlt_image_format format, int width, int height) { self->data = data; self->format = format; self->width = width; self->height = height; self->colorspace = mlt_colorspace_unspecified; self->release_data = NULL; self->release_alpha = NULL; self->close = NULL; mlt_image_format_planes(self->format, self->width, self->height, self->data, self->planes, self->strides); } /** Get the most common values for the image. * * \public \memberof mlt_image_s * \param self the Image object * \param[out] data the buffer that contains the image data * \param[out] format the image format * \param[out] width the width of the image * \param[out] height the height of the image */ void mlt_image_get_values( mlt_image self, void **data, mlt_image_format *format, int *width, int *height) { *data = self->data; *format = self->format; *width = self->width; *height = self->height; } /** Allocate the data field based on the other properties of the Image. * * If the data field is already set, and a destructor function exists, the data * will be released. Else, the data pointer will be overwritten without being * released. * * After this function call, the release_data field will be set and can be used * to release the data when necessary. * * \public \memberof mlt_image_s * \param self the Image object */ void mlt_image_alloc_data(mlt_image self) { if (!self) return; if (self->release_data) { self->release_data(self->data); } int size = mlt_image_calculate_size(self); self->data = mlt_pool_alloc(size); self->release_data = mlt_pool_release; mlt_image_format_planes(self->format, self->width, self->height, self->data, self->planes, self->strides); } /** Allocate the alpha field based on the other properties of the Image. * * If the alpha field is already set, and a destructor function exists, the data * will be released. Else, the data pointer will be overwritten without being * released. * * After this function call, the release_data field will be set and can be used * to release the data when necessary. * * \public \memberof mlt_image_s * \param self the Image object */ void mlt_image_alloc_alpha(mlt_image self) { if (!self) return; if (self->release_alpha) { self->release_alpha(self->alpha); } self->alpha = mlt_pool_alloc(self->width * self->height); self->release_alpha = mlt_pool_release; self->strides[3] = self->width; self->planes[3] = self->alpha; } /** Calculate the number of bytes needed for the Image data. * * \public \memberof mlt_image_s * \param self the Image object * \return the number of bytes */ int mlt_image_calculate_size(mlt_image self) { switch (self->format) { case mlt_image_rgb: return self->width * self->height * 3; case mlt_image_rgba: return self->width * self->height * 4; case mlt_image_yuv422: return self->width * self->height * 2; case mlt_image_yuv420p: return self->width * self->height * 3 / 2; case mlt_image_movit: case mlt_image_opengl_texture: return 4; case mlt_image_yuv422p16: return self->width * self->height * 4; case mlt_image_yuv420p10: return self->width * self->height * 3; case mlt_image_yuv444p10: return self->width * self->height * 6; case mlt_image_rgba64: return self->width * self->height * 4 * 2; case mlt_image_none: case mlt_image_invalid: return 0; } return 0; } /** Get the short name for an image format. * * \public \memberof mlt_image_s * \param format the image format * \return a string */ const char *mlt_image_format_name(mlt_image_format format) { switch (format) { case mlt_image_none: return "none"; case mlt_image_rgb: return "rgb"; case mlt_image_rgba: return "rgba"; case mlt_image_yuv422: return "yuv422"; case mlt_image_yuv420p: return "yuv420p"; case mlt_image_movit: return "glsl"; case mlt_image_opengl_texture: return "opengl_texture"; case mlt_image_yuv422p16: return "yuv422p16"; case mlt_image_yuv420p10: return "yuv420p10"; case mlt_image_yuv444p10: return "yuv444p10"; case mlt_image_rgba64: return "rgba64"; case mlt_image_invalid: return "invalid"; } return "invalid"; } /** Get the id of image format from short name. * * \public \memberof mlt_image_s * \param name the image format short name * \return a image format */ mlt_image_format mlt_image_format_id(const char *name) { mlt_image_format f; for (f = mlt_image_none; name && f < mlt_image_invalid; f++) { const char *v = mlt_image_format_name(f); if (!strcmp(v, name)) return f; } return mlt_image_invalid; } /** Get the short name for a color transfer characteristics. * * \public \memberof mlt_image_s * \param trc the color transfer characteristics * \return a string */ const char *mlt_image_color_trc_name(mlt_color_trc trc) { switch (trc) { case mlt_color_trc_none: case mlt_color_trc_unspecified: case mlt_color_trc_reserved: return "none"; case mlt_color_trc_bt709: return "bt709"; case mlt_color_trc_gamma22: return "bt470m"; case mlt_color_trc_gamma28: return "bt470bg"; case mlt_color_trc_smpte170m: return "smpte170m"; case mlt_color_trc_smpte240m: return "smpte240m"; case mlt_color_trc_linear: return "linear"; case mlt_color_trc_log: return "log100"; case mlt_color_trc_log_sqrt: return "log316"; case mlt_color_trc_iec61966_2_4: return "iec61966-2-4"; case mlt_color_trc_bt1361_ecg: return "bt1361e"; case mlt_color_trc_iec61966_2_1: return "iec61966-2-1"; case mlt_color_trc_bt2020_10: return "bt2020-10"; case mlt_color_trc_bt2020_12: return "bt2020-12"; case mlt_color_trc_smpte2084: return "smpte2084"; case mlt_color_trc_smpte428: return "smpte428"; case mlt_color_trc_arib_std_b67: return "arib-std-b67"; case mlt_color_trc_invalid: return "invalid"; } return "none"; } /** Get the id of color transfer characteristics from short name. * * \public \memberof mlt_image_s * \param name the color trc short name * \return a color trc */ mlt_color_trc mlt_image_color_trc_id(const char *name) { if (!name) { return mlt_color_trc_none; } mlt_color_trc c; for (c = mlt_color_trc_none; c <= mlt_color_trc_invalid; c++) { const char *s = mlt_image_color_trc_name(c); if (!strcmp(s, name)) return c; } // Fall back to see if it was specified as a number int value = strtol(name, NULL, 10); if (value && value < mlt_color_trc_invalid) return value; return mlt_color_trc_none; } const char *mlt_image_colorspace_name(mlt_colorspace colorspace) { switch (colorspace) { case mlt_colorspace_rgb: return "rgb"; case mlt_colorspace_bt709: return "bt709"; case mlt_colorspace_unspecified: return "unspecified"; case mlt_colorspace_reserved: return "reserved"; case mlt_colorspace_fcc: return "fcc"; case mlt_colorspace_bt470bg: return "bt470bg"; case mlt_colorspace_smpte170m: return "smpte170m"; case mlt_colorspace_smpte240m: return "smpte240m"; case mlt_colorspace_ycgco: return "ycgco"; case mlt_colorspace_bt2020_ncl: return "bt2020nc"; case mlt_colorspace_bt2020_cl: return "bt2020c"; case mlt_colorspace_smpte2085: return "smpte2085"; case mlt_colorspace_bt601: return "bt601"; case mlt_colorspace_invalid: return "invalid"; } return "invalid"; } mlt_colorspace mlt_image_colorspace_id(const char *name) { if (!name) { return mlt_colorspace_invalid; } const mlt_colorspace colorspaces[] = {mlt_colorspace_rgb, mlt_colorspace_bt709, mlt_colorspace_unspecified, mlt_colorspace_reserved, mlt_colorspace_fcc, mlt_colorspace_bt470bg, mlt_colorspace_smpte170m, mlt_colorspace_smpte240m, mlt_colorspace_ycgco, mlt_colorspace_bt2020_ncl, mlt_colorspace_bt2020_cl, mlt_colorspace_smpte2085, mlt_colorspace_bt601, mlt_colorspace_invalid}; // Fall back to see if it was specified as a number int value = strtol(name, NULL, 10); if (!value && strcmp(name, "0")) value = -1; // strtol returned an error; for (int i = 0; i < sizeof(colorspaces) / sizeof(colorspaces[0]); i++) { const char *s = mlt_image_colorspace_name(colorspaces[i]); if (value == colorspaces[i] || !strcmp(s, name)) return colorspaces[i]; } return mlt_colorspace_invalid; } const char *mlt_image_color_pri_name(mlt_color_primaries primaries) { switch (primaries) { case mlt_color_pri_none: return "none"; case mlt_color_pri_bt709: return "bt709"; case mlt_color_pri_bt470m: return "bt470m"; case mlt_color_pri_bt470bg: return "bt470bg"; case mlt_color_pri_smpte170m: return "smpte170m"; case mlt_color_pri_film: return "film"; case mlt_color_pri_bt2020: return "bt2020"; case mlt_color_pri_smpte428: return "smpte428"; case mlt_color_pri_smpte431: return "smpte431"; case mlt_color_pri_smpte432: return "smpte432"; case mlt_color_pri_invalid: return "invalid"; } return "invalid"; } mlt_color_primaries mlt_image_color_pri_id(const char *name) { if (!name) { return mlt_color_pri_none; } const mlt_color_primaries primaries[] = {mlt_color_pri_none, mlt_color_pri_bt709, mlt_color_pri_bt470m, mlt_color_pri_bt470bg, mlt_color_pri_smpte170m, mlt_color_pri_film, mlt_color_pri_bt2020, mlt_color_pri_smpte428, mlt_color_pri_smpte431, mlt_color_pri_smpte432, mlt_color_pri_invalid}; // Fall back to see if it was specified as a number int value = strtol(name, NULL, 10); if (!value && strcmp(name, "0")) value = -1; // strtol returned an error; for (int i = 0; name && i < sizeof(primaries) / sizeof(primaries[0]); i++) { const char *s = mlt_image_color_pri_name(primaries[i]); if (value == primaries[i] || !strcmp(s, name)) return primaries[i]; } return mlt_color_pri_none; } /** Get the default colorspace for a given image format. * * \public \memberof mlt_image_s * \param format the format * \param height the image height. Pass 0 if you do not know. * \return a colorspace */ mlt_colorspace mlt_image_default_colorspace(mlt_image_format format, int height) { mlt_colorspace colorspace = mlt_colorspace_bt709; switch (format) { case mlt_image_rgb: case mlt_image_rgba: case mlt_image_rgba64: case mlt_image_movit: case mlt_image_opengl_texture: colorspace = mlt_colorspace_rgb; break; case mlt_image_yuv422: case mlt_image_yuv420p: case mlt_image_yuv422p16: case mlt_image_yuv420p10: case mlt_image_yuv444p10: if (height < 576) { // Assume NTSC colorspace = mlt_colorspace_smpte170m; } else if (height < 720) { // Assume PAL colorspace = mlt_colorspace_bt470bg; } else { // Assume HDTV colorspace = mlt_colorspace_bt709; } break; case mlt_image_none: case mlt_image_invalid: break; } return colorspace; } /** Get the default color transfer characteristics for a given colorspace. * * \public \memberof mlt_image_s * \param colorspace the colorspace * \return a color trc */ mlt_color_trc mlt_image_default_trc(mlt_colorspace colorspace) { switch (colorspace) { case mlt_colorspace_rgb: return mlt_color_trc_iec61966_2_1; case mlt_colorspace_bt709: return mlt_color_trc_bt709; case mlt_colorspace_bt470bg: return mlt_color_trc_gamma28; case mlt_colorspace_smpte170m: return mlt_color_trc_smpte170m; case mlt_colorspace_smpte240m: return mlt_color_trc_smpte240m; case mlt_colorspace_bt2020_ncl: return mlt_color_trc_bt2020_10; case mlt_colorspace_bt2020_cl: return mlt_color_trc_bt2020_10; case mlt_colorspace_bt601: return mlt_color_trc_smpte170m; case mlt_colorspace_smpte2085: case mlt_colorspace_fcc: case mlt_colorspace_ycgco: case mlt_colorspace_unspecified: case mlt_colorspace_reserved: case mlt_colorspace_invalid: break; } return mlt_color_trc_bt709; } /** Get the default color primaries for a given colorspace. * * \public \memberof mlt_image_s * \param colorspace the colorspace * \param height the image height. Pass 0 if you do not know. * \return a color primaries */ mlt_color_primaries mlt_image_default_primaries(mlt_colorspace colorspace, int height) { switch (colorspace) { case mlt_colorspace_rgb: case mlt_colorspace_bt709: return mlt_color_pri_bt709; case mlt_colorspace_bt470bg: return mlt_color_pri_bt470bg; case mlt_colorspace_smpte170m: return mlt_color_pri_smpte170m; case mlt_colorspace_smpte240m: return mlt_color_pri_smpte170m; case mlt_colorspace_bt2020_ncl: return mlt_color_pri_bt2020; case mlt_colorspace_bt2020_cl: return mlt_color_pri_bt2020; case mlt_colorspace_bt601: return height >= 576 ? mlt_color_pri_bt470bg : mlt_color_pri_smpte170m; case mlt_colorspace_fcc: return mlt_color_pri_bt470m; case mlt_colorspace_smpte2085: case mlt_colorspace_ycgco: case mlt_colorspace_unspecified: case mlt_colorspace_reserved: case mlt_colorspace_invalid: break; } return mlt_color_pri_bt709; } /** Fill an image with black. * * \bug This does not respect full range YUV if needed. * \public \memberof mlt_image_s * \param self a mlt_image */ void mlt_image_fill_black(mlt_image self) { if (!self->data) return; switch (self->format) { case mlt_image_invalid: case mlt_image_none: case mlt_image_movit: case mlt_image_opengl_texture: return; case mlt_image_rgb: case mlt_image_rgba: case mlt_image_rgba64: { int size = mlt_image_calculate_size(self); memset(self->planes[0], 0, size); break; } case mlt_image_yuv422: { int size = mlt_image_calculate_size(self); register uint8_t *p = self->planes[0]; register uint8_t *q = p + size; while (p != NULL && p != q) { *p++ = 16; *p++ = 128; } } break; case mlt_image_yuv422p16: { for (int plane = 0; plane < 3; plane++) { uint16_t value = 16 << 8; size_t width = self->width; if (plane > 0) { value = 128 << 8; width /= 2; } uint16_t *p = (uint16_t *) self->planes[plane]; for (int i = 0; i < width * self->height; i++) { p[i] = value; } } } break; case mlt_image_yuv420p10: case mlt_image_yuv444p10: { for (int plane = 0; plane < 3; plane++) { uint16_t value = 16 << 2; size_t width = self->width; size_t height = self->height; if (plane > 0) { value = 128 << 2; if (self->format == mlt_image_yuv420p10) { width /= 2; height /= 2; } } uint16_t *p = (uint16_t *) self->planes[plane]; for (int i = 0; i < width * height; i++) { p[i] = value; } } } break; case mlt_image_yuv420p: { memset(self->planes[0], 16, self->height * self->strides[0]); memset(self->planes[1], 128, self->height * self->strides[1] / 2); memset(self->planes[2], 128, self->height * self->strides[2] / 2); } break; } } /** Fill an image with a checkerboard pattern. * * \public \memberof mlt_image_s * \param self a mlt_image * \param sample_aspect_ratio the pixel aspect ratio */ void mlt_image_fill_checkerboard(mlt_image self, double sample_aspect_ratio) { if (!self->data) return; if (sample_aspect_ratio == 0) sample_aspect_ratio = 1.0; int h = 0.025 * MAX(self->width * sample_aspect_ratio, self->height); int w = h / sample_aspect_ratio; if (w <= 0 || h <= 0) return; // compute center offsets int ox = w * 2 - (self->width / 2) % (w * 2); int oy = h * 2 - (self->height / 2) % (h * 2); int bpp = self->strides[0] / self->width; uint8_t color, gray1 = 0x7F, gray2 = 0xB2; switch (self->format) { case mlt_image_invalid: case mlt_image_none: case mlt_image_movit: case mlt_image_opengl_texture: return; case mlt_image_rgb: case mlt_image_rgba: { uint8_t *p = self->planes[0]; for (int i = 0; i < self->height; i++) { for (int j = 0; j < self->width; j++) { color = ((((i + oy) / h) % 2) ^ (((j + ox) / w) % 2)) ? gray1 : gray2; memset(&p[i * self->strides[0] + j * bpp], color, bpp); } } break; } case mlt_image_yuv422: { uint8_t *p = self->planes[0]; for (int i = 0; i < self->height; i++) { for (int j = 0; j < self->width; j++) { color = ((((i + oy) / h) % 2) ^ (((j + ox) / w) % 2)) ? gray1 : gray2; p[i * self->strides[0] + j * bpp] = color; p[i * self->strides[0] + j * bpp + 1] = 128; } } } break; case mlt_image_yuv422p16: { for (int plane = 0; plane < 3; plane++) { int width = plane > 0 ? self->width / 2 : self->width; uint16_t *p = (uint16_t *) self->planes[plane]; uint16_t color; for (int i = 0; i < self->height; i++) { for (int j = 0; j < width; j++) { color = plane > 0 ? 128 : ((((i + oy) / h) % 2) ^ (((j + ox) / w) % 2)) ? gray1 : gray2; p[i * width + j] = color << 8; } } } } break; case mlt_image_yuv420p10: case mlt_image_yuv444p10: { for (int plane = 0; plane < 3; plane++) { uint16_t *p = (uint16_t *) self->planes[plane]; uint16_t color; int width = self->width; int height = self->height; if (plane > 0 && self->format == mlt_image_yuv420p10) { width /= 2; height /= 2; } for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { color = plane > 0 ? 128 : ((((i + oy) / h) % 2) ^ (((j + ox) / w) % 2)) ? gray1 : gray2; p[i * width + j] = color << 2; } } } } break; case mlt_image_yuv420p: { uint8_t *p = self->planes[0]; for (int i = 0; i < self->height; i++) { for (int j = 0; j < self->width; j++) { color = ((((i + oy) / h) % 2) ^ (((j + ox) / w) % 2)) ? gray1 : gray2; p[i * self->width + j] = color; } } memset(self->planes[1], 128, self->height * self->strides[1] / 2); memset(self->planes[2], 128, self->height * self->strides[2] / 2); } break; case mlt_image_rgba64: { uint16_t *p = (uint16_t *) self->planes[0]; for (int i = 0; i < self->height; i++) { for (int j = 0; j < self->width; j++) { uint16_t color = ((((i + oy) / h) % 2) ^ (((j + ox) / w) % 2)) ? gray1 : gray2; color *= 256; p[0] = color; p[1] = color; p[2] = color; p[3] = 0xffff; p += 4; } } } break; } } /** Fill an image with white. * * \public \memberof mlt_image_s * \param self a mlt_image * \param full_range whether to use full color range */ void mlt_image_fill_white(mlt_image self, int full_range) { if (!self->data) return; uint8_t white = full_range ? 255 : 235; switch (self->format) { case mlt_image_invalid: case mlt_image_none: case mlt_image_movit: case mlt_image_opengl_texture: return; case mlt_image_rgb: case mlt_image_rgba: case mlt_image_rgba64: { int size = mlt_image_calculate_size(self); memset(self->planes[0], 255, size); break; } case mlt_image_yuv422: { int size = mlt_image_calculate_size(self); register uint8_t *p = self->planes[0]; register uint8_t *q = p + size; while (p != NULL && p != q) { *p++ = white; *p++ = 128; } } break; case mlt_image_yuv422p16: { for (int plane = 0; plane < 3; plane++) { uint16_t value = white << 8; size_t width = self->width; if (plane > 0) { value = 128 << 8; width /= 2; } uint16_t *p = (uint16_t *) self->planes[plane]; for (int i = 0; i < width * self->height; i++) { p[i] = value; } } } break; case mlt_image_yuv420p10: case mlt_image_yuv444p10: for (int plane = 0; plane < 3; plane++) { uint16_t value = white << 2; size_t width = self->width; size_t height = self->height; if (plane > 0) { value = 128 << 2; if (self->format == mlt_image_yuv420p10) { width /= 2; height /= 2; } } uint16_t *p = (uint16_t *) self->planes[plane]; for (int i = 0; i < width * height; i++) { p[i] = value; } } break; case mlt_image_yuv420p: { memset(self->planes[0], white, self->height * self->strides[0]); memset(self->planes[1], 128, self->height * self->strides[1] / 2); memset(self->planes[2], 128, self->height * self->strides[2] / 2); } break; } } /** Fill an image alpha channel with opaque if it exists. * * \public \memberof mlt_image_s */ void mlt_image_fill_opaque(mlt_image self) { if (!self->data) return; if (self->format == mlt_image_rgba && self->planes[0] != NULL) { for (int line = 0; line < self->height; line++) { uint8_t *pLine = self->planes[0] + (self->strides[0] * line) + 3; for (int pixel = 0; pixel < self->width; pixel++) { *pLine = 0xff; *pLine += 4; } } } else if (self->format == mlt_image_rgba64 && self->planes[0] != NULL) { for (int line = 0; line < self->height; line++) { uint16_t *pLine = (uint16_t *) self->planes[0] + (self->strides[0] * line) + 3; for (int pixel = 0; pixel < self->width; pixel++) { *pLine = 0xffff; *pLine += 4; } } } else if (self->planes[3] != NULL) { memset(self->planes[3], 255, self->height * self->strides[3]); } } /** Check if the alpha channel of an image is opaque * * \public \memberof mlt_image_s * \param self a mlt_image * \return true (1) or false (0) if the image is opaque */ extern int mlt_image_is_opaque(mlt_image self) { if (!self->data) return 0; int pixels = self->width * self->height; if (self->format == mlt_image_rgba && self->planes[0] != NULL) { int samples = pixels * 4; uint8_t *img = self->planes[0]; for (int i = 0; i < samples; i += 4) { if (img[i + 3] != 0xff) return 0; } } else if (self->format == mlt_image_rgba64 && self->planes[0] != NULL) { int samples = pixels * 4; uint16_t *img = (uint16_t *) self->planes[0]; for (int i = 0; i < samples; i += 4) { if (img[i + 3] != 0xffff) return 0; } } else if (self->planes[3] != NULL) { for (int i = 0; i < pixels; i++) { if (self->planes[3][i] != 0xff) return 0; } } return 1; } /** Get the number of bytes needed for an image. * * \public \memberof mlt_image_s * \param format the image format * \param width the width of the image in pixels * \param height the height of the image in pixels * \param[out] bpp the number of bytes per pixel (optional) * \return the number of bytes */ int mlt_image_format_size(mlt_image_format format, int width, int height, int *bpp) { switch (format) { case mlt_image_rgb: if (bpp) *bpp = 3; return width * height * 3; case mlt_image_rgba: if (bpp) *bpp = 4; return width * height * 4; case mlt_image_yuv422: if (bpp) *bpp = 2; return width * height * 2; case mlt_image_yuv420p: if (bpp) *bpp = 3 / 2; return width * height * 3 / 2; case mlt_image_movit: case mlt_image_opengl_texture: if (bpp) *bpp = 0; return 4; case mlt_image_yuv422p16: if (bpp) *bpp = 4; return 4 * height * width; case mlt_image_yuv420p10: if (bpp) *bpp = 3; return 3 * height * width; case mlt_image_yuv444p10: if (bpp) *bpp = 6; return 6 * height * width; case mlt_image_rgba64: if (bpp) *bpp = 8; return 8 * height * width; default: if (bpp) *bpp = 0; return 0; } return 0; } /** Build a planes pointers of image mapping * * For proper and unified planar image processing, plane sizes and plane pointers should * be provided to processing code. * * \public \memberof mlt_image_s * \param format the image format * \param width the width of the image in pixels * \param height the height of the image in pixels * \param[in] data pointer to allocated image * \param[out] planes pointers to plane's pointers will be set * \param[out] strides pointers to plane's strides will be set */ void mlt_image_format_planes( mlt_image_format format, int width, int height, void *data, uint8_t *planes[4], int strides[4]) { switch (format) { case mlt_image_yuv422p16: strides[0] = width * 2; strides[1] = width; strides[2] = width; strides[3] = 0; planes[0] = (unsigned char *) data; planes[1] = planes[0] + height * strides[0]; planes[2] = planes[1] + height * strides[1]; planes[3] = 0; break; case mlt_image_yuv420p10: strides[0] = width * 2; strides[1] = width; strides[2] = width; strides[3] = 0; planes[0] = (unsigned char *) data; planes[1] = planes[0] + height * strides[0]; planes[2] = planes[1] + (height >> 1) * strides[1]; planes[3] = 0; break; case mlt_image_yuv444p10: strides[0] = width * 2; strides[1] = width * 2; strides[2] = width * 2; strides[3] = 0; planes[0] = (unsigned char *) data; planes[1] = planes[0] + height * strides[0]; planes[2] = planes[1] + height * strides[1]; planes[3] = 0; break; case mlt_image_yuv420p: strides[0] = width; strides[1] = width >> 1; strides[2] = width >> 1; strides[3] = 0; planes[0] = (unsigned char *) data; planes[1] = planes[0] + width * height; planes[2] = planes[1] + (width >> 1) * (height >> 1); planes[3] = 0; break; default: planes[0] = data; planes[1] = 0; planes[2] = 0; planes[3] = 0; strides[0] = mlt_image_format_size(format, width, 1, NULL); strides[1] = 0; strides[2] = 0; strides[3] = 0; break; }; } /** Check if the alpha channel of an rgba image is opaque * * \public \memberof mlt_image_s * \param image the image buffer * \param width the width of the image in pixels * \param height the height of the image in pixels * \return true (1) or false (0) if the image is opaque */ int mlt_image_rgba_opaque(uint8_t *image, int width, int height) { for (int i = 0; i < width * height; ++i) { if (image[4 * i + 3] != 0xff) return 0; } return 1; } /** Check if the string indicates full range * * \public \memberof mlt_image_s * \param color_range the string whose values to test * \return true (1) or false (0) if the color range is full */ int mlt_image_full_range(const char *color_range) { return color_range && (!strcmp("pc", color_range) || !strcmp("full", color_range) || !strcmp("jpeg", color_range)); } mlt-7.38.0/src/framework/mlt_image.h000664 000000 000000 00000007053 15172202314 017262 0ustar00rootroot000000 000000 /** * \file mlt_image.h * \brief Image class * \see mlt_image_s * * Copyright (C) 2022-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_IMAGE_H #define MLT_IMAGE_H #include "mlt_export.h" #include "mlt_types.h" /** \brief Image class * * Image is the data object that represents image for a period of time. */ #define MLT_IMAGE_MAX_PLANES 4 struct mlt_image_s { mlt_image_format format; int width; int height; int colorspace; uint8_t *planes[MLT_IMAGE_MAX_PLANES]; int strides[MLT_IMAGE_MAX_PLANES]; void *data; mlt_destructor release_data; void *alpha; mlt_destructor release_alpha; mlt_destructor close; }; MLT_EXPORT mlt_image mlt_image_new(); MLT_EXPORT void mlt_image_close(mlt_image self); MLT_EXPORT void mlt_image_set_values( mlt_image self, void *data, mlt_image_format format, int width, int height); MLT_EXPORT void mlt_image_get_values( mlt_image self, void **data, mlt_image_format *format, int *width, int *height); MLT_EXPORT void mlt_image_alloc_data(mlt_image self); MLT_EXPORT void mlt_image_alloc_alpha(mlt_image self); MLT_EXPORT int mlt_image_calculate_size(mlt_image self); MLT_EXPORT void mlt_image_fill_black(mlt_image self); MLT_EXPORT void mlt_image_fill_checkerboard(mlt_image self, double sample_aspect_ratio); MLT_EXPORT void mlt_image_fill_white(mlt_image self, int full_range); MLT_EXPORT void mlt_image_fill_opaque(mlt_image self); MLT_EXPORT int mlt_image_is_opaque(mlt_image self); MLT_EXPORT const char *mlt_image_format_name(mlt_image_format format); MLT_EXPORT mlt_image_format mlt_image_format_id(const char *name); MLT_EXPORT const char *mlt_image_color_trc_name(mlt_color_trc trc); MLT_EXPORT mlt_color_trc mlt_image_color_trc_id(const char *name); MLT_EXPORT const char *mlt_image_colorspace_name(mlt_colorspace colorspace); MLT_EXPORT mlt_colorspace mlt_image_colorspace_id(const char *name); MLT_EXPORT const char *mlt_image_color_pri_name(mlt_color_primaries primaries); MLT_EXPORT mlt_color_primaries mlt_image_color_pri_id(const char *name); MLT_EXPORT mlt_colorspace mlt_image_default_colorspace(mlt_image_format format, int height); MLT_EXPORT mlt_color_trc mlt_image_default_trc(mlt_colorspace colorspace); MLT_EXPORT mlt_color_primaries mlt_image_default_primaries(mlt_colorspace colorspace, int height); MLT_EXPORT int mlt_image_rgba_opaque(uint8_t *image, int width, int height); MLT_EXPORT int mlt_image_full_range(const char *color_range); // Deprecated functions MLT_DEPRECATED_EXPORT int mlt_image_format_size(mlt_image_format format, int width, int height, int *bpp); MLT_DEPRECATED_EXPORT void mlt_image_format_planes( mlt_image_format format, int width, int height, void *data, uint8_t *planes[4], int strides[4]); #endif mlt-7.38.0/src/framework/mlt_link.c000664 000000 000000 00000017370 15172202314 017133 0ustar00rootroot000000 000000 /** * \file mlt_link.c * \brief link service class * \see mlt_link_s * * Copyright (C) 2020-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_link.h" #include "mlt_factory.h" #include "mlt_frame.h" #include "mlt_log.h" #include #include /* Forward references to static methods. */ static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int track); static int producer_seek(mlt_producer parent, mlt_position position); static int producer_set_in_and_out(mlt_producer, mlt_position, mlt_position); /** Construct a link. * * Sets the mlt_type to "link" * * \public \memberof mlt_link_s * \return the new link */ mlt_link mlt_link_init() { mlt_link self = calloc(1, sizeof(struct mlt_link_s)); if (self != NULL) { mlt_producer producer = &self->parent; if (mlt_producer_init(producer, self) == 0) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_set(properties, "mlt_type", "link"); mlt_properties_clear(properties, "mlt_service"); mlt_properties_clear(properties, "resource"); mlt_properties_clear(properties, "in"); mlt_properties_clear(properties, "out"); mlt_properties_clear(properties, "length"); mlt_properties_clear(properties, "eof"); producer->get_frame = producer_get_frame; producer->seek = producer_seek; producer->set_in_and_out = producer_set_in_and_out; producer->close = (mlt_destructor) mlt_link_close; producer->close_object = self; } else { free(self); self = NULL; } } return self; } /** Connect this link to the next producer. * * \public \memberof mlt_link_s * \param self a link * \param next the producer to get frames from * \param chain_profile a profile to use if needed (some links derive their frame rate from the next producer) * \return true on error */ int mlt_link_connect_next(mlt_link self, mlt_producer next, mlt_profile chain_profile) { self->next = next; if (self->configure) { self->configure(self, chain_profile); } return 0; } /** Close the link and free its resources. * * \public \memberof mlt_link_s * \param self a link */ void mlt_link_close(mlt_link self) { if (self != NULL && mlt_properties_dec_ref(MLT_LINK_PROPERTIES(self)) <= 0) { if (self->close) { self->close(self); } else { self->parent.close = NULL; mlt_producer_close(&self->parent); } } } static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int index) { if (parent && parent->child) { mlt_link self = parent->child; if (self->get_frame != NULL) { return self->get_frame(self, frame, index); } else { /* Default implementation: get a frame from the next producer */ return mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), frame, index); } } return 1; } int producer_seek(mlt_producer parent, mlt_position position) { // Unlike mlt_producer_seek(), a link does not do bounds checking when seeking if (parent && parent->child) { mlt_link self = parent->child; mlt_properties properties = MLT_LINK_PROPERTIES(self); int use_points = 1 - mlt_properties_get_int(properties, "ignore_points"); // Set the position mlt_properties_set_position(properties, "_position", position); // Calculate the absolute frame mlt_properties_set_position(properties, "_frame", use_points * mlt_producer_get_in(parent) + position); } return 0; } int producer_set_in_and_out(mlt_producer parent, mlt_position in, mlt_position out) { // Unlike mlt_producer_set_in_and_out(), a link does not do bounds checking against length if (parent && parent->child) { mlt_link self = parent->child; mlt_properties properties = MLT_LINK_PROPERTIES(self); mlt_events_block(properties, properties); mlt_properties_set_position(properties, "in", in); mlt_events_unblock(properties, properties); mlt_properties_set_position(properties, "out", out); } return 0; } // Link filter wrapper functions void link_filter_configure(mlt_link self, mlt_profile profile) { // Operate at the same frame rate as the next link if (self) { mlt_service_set_profile(MLT_LINK_SERVICE(self), mlt_service_profile(MLT_PRODUCER_SERVICE(self->next))); if (self->child) { mlt_service_set_profile(MLT_SERVICE(self->child), mlt_service_profile(MLT_PRODUCER_SERVICE(self->next))); } } } void link_filter_close(mlt_link self) { if (self) { mlt_filter_close((mlt_filter) self->child); self->close = NULL; self->child = NULL; mlt_link_close(self); free(self); } } int link_filter_get_frame(mlt_link self, mlt_frame_ptr frame, int index) { int error = 1; if (self && self->child) { // Get the frame from the next link and apply the filter to it. mlt_producer_seek(self->next, mlt_producer_position(MLT_LINK_PRODUCER(self))); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), frame, index); mlt_producer_prepare_next(MLT_LINK_PRODUCER(self)); mlt_filter_process((mlt_filter) self->child, *frame); } return error; } /** Construct a link as a wrapper for the specified filter * * The returned link will be the owner of the supplied filter * * \public \memberof mlt_link_s * \return the new link */ mlt_link mlt_link_filter_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_link self = mlt_link_init(); mlt_filter filter = mlt_factory_filter(profile, id, arg); if (self && filter) { self->child = filter; // Callback registration self->close = link_filter_close; self->configure = link_filter_configure; self->get_frame = link_filter_get_frame; } else { mlt_link_close(self); self = NULL; mlt_filter_close(filter); } return self; } /** Get the metadata about a link that is wrapping a filter. * * Returns NULL if link or its metadata are unavailable. * * \public \memberof mlt_link_s * \param type this must be mlt_service_type_link * \param id the name of the filter that this link is wrapping * \param data unused * \return the service metadata as a structured properties list */ extern mlt_properties mlt_link_filter_metadata(mlt_service_type type, const char *id, void *data) { mlt_repository repository = mlt_factory_repository(); mlt_properties filter_metadata = mlt_repository_metadata(repository, mlt_service_filter_type, id); mlt_properties_set(filter_metadata, "type", "link"); return filter_metadata; } mlt-7.38.0/src/framework/mlt_link.h000664 000000 000000 00000005323 15172202314 017133 0ustar00rootroot000000 000000 /** * \file mlt_link.h * \brief link service class * \see mlt_link_s * * Copyright (C) 2020-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_LINK_H #define MLT_LINK_H #include "mlt_export.h" #include "mlt_producer.h" /** \brief Link class * * The link is a producer class that can be connected to other link producers in a Chain. * * \extends mlt_producer_s * \properties \em next holds a reference to the next producer in the chain */ struct mlt_link_s { /** \publicsection */ struct mlt_producer_s parent; /** \protectedsection */ /** Get a frame of data (virtual function). * * \param mlt_link a link * \param mlt_frame_ptr a frame pointer by reference * \param int an index * \return true if there was an error */ int (*get_frame)(mlt_link, mlt_frame_ptr, int); /** Configure the link (virtual function). * * \param mlt_link a link * \param mlt_profile a default profile to use */ void (*configure)(mlt_link, mlt_profile); /** Virtual close function */ void (*close)(mlt_link); /** \privatesection */ mlt_producer next; /** the object of a subclass */ void *child; }; #define MLT_LINK_PRODUCER(link) (&(link)->parent) #define MLT_LINK_SERVICE(link) MLT_PRODUCER_SERVICE(MLT_LINK_PRODUCER(link)) #define MLT_LINK_PROPERTIES(link) MLT_SERVICE_PROPERTIES(MLT_LINK_SERVICE(link)) MLT_EXPORT mlt_link mlt_link_init(); MLT_EXPORT int mlt_link_connect_next(mlt_link self, mlt_producer next, mlt_profile chain_profile); MLT_EXPORT void mlt_link_close(mlt_link self); // Link filter wrapper functions MLT_EXPORT mlt_link mlt_link_filter_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); MLT_EXPORT mlt_properties mlt_link_filter_metadata(mlt_service_type type, const char *id, void *data); #endif mlt-7.38.0/src/framework/mlt_log.c000664 000000 000000 00000006321 15172202314 016751 0ustar00rootroot000000 000000 /** * \file mlt_log.c * \brief logging functions * * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_log.h" #include "mlt_service.h" #include #ifndef NDEBUG #ifdef _MSC_VER #include #else #include #include #endif #endif static int log_level = MLT_LOG_WARNING; void default_callback(void *ptr, int level, const char *fmt, va_list vl) { static int print_prefix = 1; mlt_properties properties = ptr ? MLT_SERVICE_PROPERTIES((mlt_service) ptr) : NULL; if (level > log_level) return; #ifndef NDEBUG if (print_prefix && level >= MLT_LOG_TIMINGS) { struct timeval tv; time_t ltime; struct tm *rtime; char buf[32]; gettimeofday(&tv, NULL); ltime = tv.tv_sec; rtime = localtime(<ime); strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", rtime); fprintf(stderr, "| %s.%.3d | ", buf, (int) (tv.tv_usec / 1000)); } #endif if (print_prefix && properties) { char *mlt_type = mlt_properties_get(properties, "mlt_type"); char *mlt_service = mlt_properties_get(properties, "mlt_service"); char *resource = mlt_properties_get(properties, "resource"); if (!(resource && *resource && resource[0] == '<' && resource[strlen(resource) - 1] == '>')) mlt_type = mlt_properties_get(properties, "mlt_type"); if (mlt_service) fprintf(stderr, "[%s %s] ", mlt_type, mlt_service); else fprintf(stderr, "[%s %p] ", mlt_type, ptr); if (resource) fprintf(stderr, "%s\n ", resource); } print_prefix = strstr(fmt, "\n") != NULL; vfprintf(stderr, fmt, vl); } static void (*callback)(void *, int, const char *, va_list) = default_callback; void mlt_log(void *service, int level, const char *fmt, ...) { va_list vl; va_start(vl, fmt); mlt_vlog(service, level, fmt, vl); va_end(vl); } void mlt_vlog(void *service, int level, const char *fmt, va_list vl) { if (callback) callback(service, level, fmt, vl); } int mlt_log_get_level(void) { return log_level; } void mlt_log_set_level(int level) { log_level = level; } void mlt_log_set_callback(void (*new_callback)(void *, int, const char *, va_list)) { callback = new_callback; } int64_t mlt_log_timings_now(void) { int64_t r = 0; #ifndef NDEBUG struct timeval tv; gettimeofday(&tv, NULL); r = tv.tv_sec; r *= 1000000LL; r += tv.tv_usec; #endif return r; } mlt-7.38.0/src/framework/mlt_log.h000664 000000 000000 00000012072 15172202314 016756 0ustar00rootroot000000 000000 /** * \file mlt_log.h * \brief logging functions * * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_LOG_H #define MLT_LOG_H #include "mlt_export.h" #include #include #define MLT_LOG_QUIET -8 /** * something went really wrong and we will crash now */ #define MLT_LOG_PANIC 0 /** * something went wrong and recovery is not possible * like no header in a format which depends on it or a combination * of parameters which are not allowed */ #define MLT_LOG_FATAL 8 /** * something went wrong and cannot losslessly be recovered * but not all future data is affected */ #define MLT_LOG_ERROR 16 /** * something somehow does not look correct / something which may or may not * lead to some problems */ #define MLT_LOG_WARNING 24 #define MLT_LOG_INFO 32 #define MLT_LOG_VERBOSE 40 #define MLT_LOG_TIMINGS 44 /** * stuff which is only useful for MLT developers */ #define MLT_LOG_DEBUG 48 /** * Send the specified message to the log if the level is less than or equal to * the current logging level. By default, all logging messages are sent to * stderr. This behavior can be altered by setting a different mlt_vlog callback * function. * * \param service An optional pointer to a \p mlt_service_s. * \param level The importance level of the message, lower values signifying * higher importance. * \param fmt The format string (printf-compatible) that specifies how * subsequent arguments are converted to output. * \see mlt_vlog */ #ifdef __GNUC__ MLT_EXPORT void mlt_log(void *service, int level, const char *fmt, ...) __attribute__((__format__(__printf__, 3, 4))); #else MLT_EXPORT void mlt_log(void *service, int level, const char *fmt, ...); #endif #ifdef _MSC_VER #define MLT_LOG_EXPAND_ARGS(...) , ##__VA_ARGS__ #define mlt_log_panic(service, format, ...) \ mlt_log((service), MLT_LOG_PANIC, format MLT_LOG_EXPAND_ARGS(__VA_ARGS__)) #define mlt_log_fatal(service, format, ...) \ mlt_log((service), MLT_LOG_FATAL, format MLT_LOG_EXPAND_ARGS(__VA_ARGS__)) #define mlt_log_error(service, format, ...) \ mlt_log((service), MLT_LOG_ERROR, format MLT_LOG_EXPAND_ARGS(__VA_ARGS__)) #define mlt_log_warning(service, format, ...) \ mlt_log((service), MLT_LOG_WARNING, format MLT_LOG_EXPAND_ARGS(__VA_ARGS__)) #define mlt_log_info(service, format, ...) \ mlt_log((service), MLT_LOG_INFO, format MLT_LOG_EXPAND_ARGS(__VA_ARGS__)) #define mlt_log_verbose(service, format, ...) \ mlt_log((service), MLT_LOG_VERBOSE, format MLT_LOG_EXPAND_ARGS(__VA_ARGS__)) #define mlt_log_timings(service, format, ...) \ mlt_log((service), MLT_LOG_TIMINGS, format MLT_LOG_EXPAND_ARGS(__VA_ARGS__)) #define mlt_log_debug(service, format, ...) \ mlt_log((service), MLT_LOG_DEBUG, format MLT_LOG_EXPAND_ARGS(__VA_ARGS__)) #else #define mlt_log_panic(service, format, args...) mlt_log((service), MLT_LOG_PANIC, (format), ##args) #define mlt_log_fatal(service, format, args...) mlt_log((service), MLT_LOG_FATAL, (format), ##args) #define mlt_log_error(service, format, args...) mlt_log((service), MLT_LOG_ERROR, (format), ##args) #define mlt_log_warning(service, format, args...) \ mlt_log((service), MLT_LOG_WARNING, (format), ##args) #define mlt_log_info(service, format, args...) mlt_log((service), MLT_LOG_INFO, (format), ##args) #define mlt_log_verbose(service, format, args...) \ mlt_log((service), MLT_LOG_VERBOSE, (format), ##args) #define mlt_log_timings(service, format, args...) \ mlt_log((service), MLT_LOG_TIMINGS, (format), ##args) #define mlt_log_debug(service, format, args...) mlt_log((service), MLT_LOG_DEBUG, (format), ##args) #endif MLT_EXPORT void mlt_vlog(void *service, int level, const char *fmt, va_list); MLT_EXPORT int mlt_log_get_level(void); MLT_EXPORT void mlt_log_set_level(int); MLT_EXPORT void mlt_log_set_callback(void (*)(void *, int, const char *, va_list)); #define mlt_log_timings_begin() \ { \ int64_t _mlt_log_timings_begin = mlt_log_timings_now(), _mlt_log_timings_end; #define mlt_log_timings_end(service, msg) \ _mlt_log_timings_end = mlt_log_timings_now(); \ mlt_log_timings(service, \ "%s:%d: T(%s)=%" PRId64 " us\n", \ __FILE__, \ __LINE__, \ msg, \ _mlt_log_timings_end - _mlt_log_timings_begin); \ } MLT_EXPORT int64_t mlt_log_timings_now(void); #endif /* MLT_LOG_H */ mlt-7.38.0/src/framework/mlt_luma_map.c000664 000000 000000 00000033340 15172202314 017764 0ustar00rootroot000000 000000 /** * \file mlt_luma_map.c * \brief functions to generate and read luma-wipe transition maps * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_luma_map.h" #include "mlt_pool.h" #include "mlt_types.h" #include #include #include #define HALF_USHRT_MAX (1 << 15) void mlt_luma_map_init(mlt_luma_map self) { memset(self, 0, sizeof(struct mlt_luma_map_s)); self->type = 0; self->w = 720; self->h = 576; self->bands = 1; self->rband = 0; self->vmirror = 0; self->hmirror = 0; self->dmirror = 0; self->invert = 0; self->offset = 0; self->flip = 0; self->flop = 0; self->quart = 0; self->pflop = 0; self->pflip = 0; } static inline int sqrti(int n) { int p = 0; int q = 1; int r = n; int h = 0; while (q <= n) q = 4 * q; while (q != 1) { q = q / 4; h = p + q; p = p / 2; if (r >= h) { p = p + q; r = r - h; } } return p; } uint16_t *mlt_luma_map_render(mlt_luma_map self) { int i = 0; int j = 0; int k = 0; if (self->quart) { self->w *= 2; self->h *= 2; } if (self->rotate) { int t = self->w; self->w = self->h; self->h = t; } if (self->bands < 0) self->bands = self->h; int max = (1 << 16) - 1; uint16_t *image = mlt_pool_alloc(self->w * self->h * sizeof(uint16_t)); uint16_t *end = image + self->w * self->h; uint16_t *p = image; uint16_t *r = image; int lower = 0; int lpb = self->h / self->bands; int rpb = max / self->bands; int direction = 1; int half_w = self->w / 2; int half_h = self->h / 2; if (!self->dmirror && (self->hmirror || self->vmirror)) rpb *= 2; for (i = 0; i < self->bands; i++) { lower = i * rpb; direction = 1; if (self->rband && i % 2 == 1) { direction = -1; lower += rpb; } switch (self->type) { case 1: { int length = sqrti(half_w * half_w + lpb * lpb / 4); int value; int x = 0; int y = 0; for (j = 0; j < lpb; j++) { y = j - lpb / 2; for (k = 0; k < self->w; k++) { x = k - half_w; value = sqrti(x * x + y * y); *p++ = lower + (direction * rpb * ((max * value) / length) / max) + (j * self->offset * 2 / lpb) + (j * self->offset / lpb); } } } break; case 2: { for (j = 0; j < lpb; j++) { int value = ((j * self->w) / lpb) - half_w; if (value > 0) value = -value; for (k = -half_w; k < value; k++) *p++ = lower + (direction * rpb * ((max * abs(k)) / half_w) / max); for (k = value; k < abs(value); k++) *p++ = lower + (direction * rpb * ((max * abs(value)) / half_w) / max) + (j * self->offset * 2 / lpb) + (j * self->offset / lpb); for (k = abs(value); k < half_w; k++) *p++ = lower + (direction * rpb * ((max * abs(k)) / half_w) / max); } } break; case 3: { int length; for (j = -half_h; j < half_h; j++) { if (j < 0) { for (k = -half_w; k < half_w; k++) { length = sqrti(k * k + j * j); *p++ = (max / 4 * k) / (length + 1); } } else { for (k = half_w; k > -half_w; k--) { length = sqrti(k * k + j * j); *p++ = (max / 2) + (max / 4 * k) / (length + 1); } } } } break; default: for (j = 0; j < lpb; j++) for (k = 0; k < self->w; k++) *p++ = lower + (direction * (rpb * ((k * max) / self->w) / max)) + (j * self->offset * 2 / lpb); break; } } if (self->quart) { self->w /= 2; self->h /= 2; for (i = 1; i < self->h; i++) { p = image + i * self->w; r = image + i * 2 * self->w; j = self->w; while (j-- > 0) *p++ = *r++; } } if (self->dmirror) { for (i = 0; i < self->h; i++) { p = image + i * self->w; r = end - i * self->w; j = (self->w * (self->h - i)) / self->h; while (j--) *(--r) = *p++; } } if (self->flip) { uint16_t t; for (i = 0; i < self->h; i++) { p = image + i * self->w; r = p + self->w; while (p != r) { t = *p; *p++ = *(--r); *r = t; } } } if (self->flop) { uint16_t t; r = end; for (i = 1; i < self->h / 2; i++) { p = image + i * self->w; j = self->w; while (j--) { t = *(--p); *p = *(--r); *r = t; } } } if (self->hmirror) { p = image; while (p < end) { r = p + self->w; while (p != r) *(--r) = *p++; p += self->w / 2; } } if (self->vmirror) { p = image; r = end; while (p != r) *(--r) = *p++; } if (self->invert) { p = image; r = image; while (p < end) *p++ = max - *r++; } if (self->pflip) { uint16_t t; for (i = 0; i < self->h; i++) { p = image + i * self->w; r = p + self->w; while (p != r) { t = *p; *p++ = *(--r); *r = t; } } } if (self->pflop) { uint16_t t; end = image + self->w * self->h; r = end; for (i = 1; i < self->h / 2; i++) { p = image + i * self->w; j = self->w; while (j--) { t = *(--p); *p = *(--r); *r = t; } } } if (self->rotate) { uint16_t *image2 = mlt_pool_alloc(self->w * self->h * sizeof(uint16_t)); for (i = 0; i < self->h; i++) { p = image + i * self->w; r = image2 + self->h - i - 1; for (j = 0; j < self->w; j++) { *r = *(p++); r += self->h; } } i = self->w; self->w = self->h; self->h = i; mlt_pool_release(image); image = image2; } return image; } /** Load the luma map from PGM stream. */ int mlt_luma_map_from_pgm(const char *filename, uint16_t **map, int *width, int *height) { uint8_t *data = NULL; FILE *f = mlt_fopen(filename, "rb"); int error = f == NULL; while (!error) { char line[128]; char comment[128]; int i = 2; int maxval; int bpp; uint16_t *p; line[127] = '\0'; // get the magic code if (fgets(line, 127, f) == NULL) break; // skip comments while (sscanf(line, " #%s", comment) > 0) if (fgets(line, 127, f) == NULL) break; if (line[0] != 'P' || line[1] != '5') break; // skip white space and see if a new line must be fetched for (i = 2; i < 127 && line[i] != '\0' && isspace(line[i]); i++) ; if ((line[i] == '\0' || line[i] == '#') && fgets(line, 127, f) == NULL) break; // skip comments while (sscanf(line, " #%s", comment) > 0) if (fgets(line, 127, f) == NULL) break; // get the dimensions if (line[0] == 'P') i = sscanf(line, "P5 %d %d %d", width, height, &maxval); else i = sscanf(line, "%d %d %d", width, height, &maxval); // get the height value, if not yet if (i < 2) { if (fgets(line, 127, f) == NULL) break; // skip comments while (sscanf(line, " #%s", comment) > 0) if (fgets(line, 127, f) == NULL) break; i = sscanf(line, "%d", height); if (i == 0) break; else i = 2; } // get the maximum gray value, if not yet if (i < 3) { if (fgets(line, 127, f) == NULL) break; // skip comments while (sscanf(line, " #%s", comment) > 0) if (fgets(line, 127, f) == NULL) break; i = sscanf(line, "%d", &maxval); if (i == 0) break; } // determine if this is one or two bytes per pixel bpp = maxval > 255 ? 2 : 1; // allocate temporary storage for the raw data data = mlt_pool_alloc(*width * *height * bpp); if (!data) { error = 1; break; } // read the raw data if (fread(data, *width * *height * bpp, 1, f) != 1) break; // allocate the luma bitmap *map = p = (uint16_t *) mlt_pool_alloc(*width * *height * sizeof(uint16_t)); if (!*map) { error = 1; break; } // process the raw data into the luma bitmap for (i = 0; i < *width * *height * bpp; i += bpp) { if (bpp == 1) *p++ = data[i] << 8; else *p++ = (data[i] << 8) + data[i + 1]; } break; } if (f) fclose(f); mlt_pool_release(data); return error; } mlt_luma_map mlt_luma_map_new(const char *path) { mlt_luma_map self = malloc(sizeof(struct mlt_luma_map_s)); if (self) { mlt_luma_map_init(self); if (strstr(path, "luma02.pgm")) { self->bands = -1; // use height } else if (strstr(path, "luma03.pgm")) { self->hmirror = 1; } else if (strstr(path, "luma04.pgm")) { self->bands = -1; // use height self->vmirror = 1; } else if (strstr(path, "luma05.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; } else if (strstr(path, "luma06.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; self->flip = 1; } else if (strstr(path, "luma07.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; self->quart = 1; } else if (strstr(path, "luma08.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; self->quart = 1; self->flip = 1; } else if (strstr(path, "luma09.pgm")) { self->bands = 12; } else if (strstr(path, "luma10.pgm")) { self->bands = 12; self->rotate = 1; } else if (strstr(path, "luma11.pgm")) { self->bands = 12; self->rband = 1; } else if (strstr(path, "luma12.pgm")) { self->bands = 12; self->rband = 1; self->vmirror = 1; } else if (strstr(path, "luma13.pgm")) { self->bands = 12; self->rband = 1; self->rotate = 1; self->flop = 1; } else if (strstr(path, "luma14.pgm")) { self->bands = 12; self->rband = 1; self->rotate = 1; self->vmirror = 1; } else if (strstr(path, "luma15.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; self->hmirror = 1; } else if (strstr(path, "luma16.pgm")) { self->type = 1; } else if (strstr(path, "luma17.pgm")) { self->type = 1; self->bands = 2; self->rband = 1; } else if (strstr(path, "luma18.pgm")) { self->type = 2; } else if (strstr(path, "luma19.pgm")) { self->type = 2; self->quart = 1; } else if (strstr(path, "luma20.pgm")) { self->type = 2; self->quart = 1; self->flip = 1; } else if (strstr(path, "luma21.pgm")) { self->type = 2; self->quart = 1; self->bands = 2; } else if (strstr(path, "luma22.pgm")) { self->type = 3; } } return self; } /** Generate a 16-bit luma map from an 8-bit image. */ void mlt_luma_map_from_yuv422(uint8_t *image, uint16_t **map, int width, int height) { int i; int size = width * height * 2; // allocate the luma bitmap uint16_t *p = *map = (uint16_t *) mlt_pool_alloc(width * height * sizeof(uint16_t)); if (*map == NULL) return; // process the image data into the luma bitmap for (i = 0; i < size; i += 2) *p++ = (image[i] - 16) * 299; // 299 = 65535 / 219 } mlt-7.38.0/src/framework/mlt_luma_map.h000664 000000 000000 00000003341 15172202314 017767 0ustar00rootroot000000 000000 /* * \file mlt_luma_map.h * \brief functions to generate and read luma-wipe transition maps * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_LUMA_MAP_H #define MLT_LUMA_MAP_H #include "mlt_export.h" #include #include #ifdef __cplusplus extern "C" { #endif struct mlt_luma_map_s { int type; int w; int h; int bands; int rband; int vmirror; int hmirror; int dmirror; int invert; int offset; int flip; int flop; int pflip; int pflop; int quart; int rotate; }; typedef struct mlt_luma_map_s *mlt_luma_map; MLT_EXPORT void mlt_luma_map_init(mlt_luma_map self); MLT_EXPORT mlt_luma_map mlt_luma_map_new(const char *path); MLT_EXPORT uint16_t *mlt_luma_map_render(mlt_luma_map self); MLT_EXPORT int mlt_luma_map_from_pgm(const char *filename, uint16_t **map, int *width, int *height); MLT_EXPORT void mlt_luma_map_from_yuv422(uint8_t *image, uint16_t **map, int width, int height); #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/framework/mlt_multitrack.c000664 000000 000000 00000051335 15172202314 020354 0ustar00rootroot000000 000000 /** * \file mlt_multitrack.c * \brief multitrack service class * \see mlt_multitrack_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_multitrack.h" #include "mlt_cache.h" #include "mlt_factory.h" #include "mlt_frame.h" #include "mlt_playlist.h" #include #include #include /* Forward reference. */ static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index); /** Construct and initialize a new multitrack. * * Sets the resource property to "". * * \public \memberof mlt_multitrack_s * \return a new multitrack */ mlt_multitrack mlt_multitrack_init() { // Allocate the multitrack object mlt_multitrack self = calloc(1, sizeof(struct mlt_multitrack_s)); if (self != NULL) { mlt_producer producer = &self->parent; if (mlt_producer_init(producer, self) == 0) { mlt_properties properties = MLT_MULTITRACK_PROPERTIES(self); producer->get_frame = producer_get_frame; mlt_properties_set_data(properties, "multitrack", self, 0, NULL, NULL); mlt_properties_set(properties, "log_id", "multitrack"); mlt_properties_set(properties, "resource", ""); mlt_properties_set_int(properties, "in", 0); mlt_properties_set_int(properties, "out", -1); mlt_properties_set_int(properties, "length", 0); producer->close = (mlt_destructor) mlt_multitrack_close; } else { free(self); self = NULL; } } return self; } /** Get the producer associated to this multitrack. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \return the producer object * \see MLT_MULTITRACK_PRODUCER */ mlt_producer mlt_multitrack_producer(mlt_multitrack self) { return self != NULL ? &self->parent : NULL; } /** Get the service associated this multitrack. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \return the service object * \see MLT_MULTITRACK_SERVICE */ mlt_service mlt_multitrack_service(mlt_multitrack self) { return MLT_MULTITRACK_SERVICE(self); } /** Get the properties associated this multitrack. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \return the multitrack's property list * \see MLT_MULTITRACK_PROPERTIES */ mlt_properties mlt_multitrack_properties(mlt_multitrack self) { return MLT_MULTITRACK_PROPERTIES(self); } /** Initialize position related information. * * \public \memberof mlt_multitrack_s * \param self a multitrack */ void mlt_multitrack_refresh(mlt_multitrack self) { int i = 0; // Obtain the properties of this multitrack mlt_properties properties = MLT_MULTITRACK_PROPERTIES(self); // We need to ensure that the multitrack reports the longest track as its length mlt_position length = 0; // Obtain stats on all connected services for (i = 0; i < self->count; i++) { // Get the producer from this index mlt_track track = self->list[i]; mlt_producer producer = track->producer; // If it's allocated then, update our stats if (producer != NULL) { // If we have more than 1 track, we must be in continue mode if (self->count > 1) mlt_properties_set(MLT_PRODUCER_PROPERTIES(producer), "eof", "continue"); // Determine the longest length //if ( !mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "hide" ) ) length = mlt_producer_get_playtime(producer) > length ? mlt_producer_get_playtime(producer) : length; } } // Update multitrack properties now - we'll not destroy the in point here mlt_events_block(properties, properties); mlt_properties_set_position(properties, "length", length); mlt_events_unblock(properties, properties); mlt_properties_set_position(properties, "out", length - 1); } /** Listener for producers on the playlist. * * \private \memberof mlt_multitrack_s * \param producer a producer * \param self a multitrack */ static void mlt_multitrack_listener(mlt_producer producer, mlt_multitrack self) { mlt_multitrack_refresh(self); } static void resize_service_caches(mlt_multitrack self) { mlt_properties caches = mlt_properties_get_data(mlt_global_properties(), "caches", NULL); if (caches) { int i; for (i = 0; i < mlt_properties_count(caches); ++i) { mlt_cache cache = mlt_properties_get_data_at(caches, i, NULL); if (self->count * 2 > mlt_cache_get_size(cache)) mlt_cache_set_size(cache, self->count * 2); } } } /** Connect a producer to a given track. * * Note that any producer can be connected here, but see special case treatment * of playlist in clip point determination below. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \param producer the producer to connect to the multitrack producer * \param track the 0-based index of the track on which to connect the multitrack * \return true on error */ int mlt_multitrack_connect(mlt_multitrack self, mlt_producer producer, int track) { // Connect to the producer to ourselves at the specified track int result = mlt_service_connect_producer(MLT_MULTITRACK_SERVICE(self), MLT_PRODUCER_SERVICE(producer), track); if (result == 0) { mlt_track current_track = (track < self->count) ? self->list[track] : NULL; // Resize the producer list if need be if (track >= self->size) { int i; self->list = realloc(self->list, (track + 10) * sizeof(mlt_track)); for (i = self->size; i < track + 10; i++) self->list[i] = NULL; self->size = track + 10; } if (current_track) { mlt_event_close(current_track->event); mlt_producer_close(current_track->producer); } else { self->list[track] = malloc(sizeof(struct mlt_track_s)); } // Assign the track in our list here self->list[track]->producer = producer; self->list[track]->event = mlt_events_listen(MLT_PRODUCER_PROPERTIES(producer), self, "producer-changed", (mlt_listener) mlt_multitrack_listener); mlt_properties_inc_ref(MLT_PRODUCER_PROPERTIES(producer)); mlt_event_inc_ref(self->list[track]->event); // Increment the track count if need be if (track >= self->count) { self->count = track + 1; resize_service_caches(self); } // Refresh our stats mlt_multitrack_refresh(self); } return result; } /** Insert a producer to a given track. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \param producer the producer to connect to the multitrack producer * \param track the 0-based index of the track on which to connect the multitrack * \return true on error */ int mlt_multitrack_insert(mlt_multitrack self, mlt_producer producer, int track) { if (track >= self->count) return mlt_multitrack_connect(self, producer, track); // Connect to the producer to ourselves at the specified track int result = mlt_service_insert_producer(MLT_MULTITRACK_SERVICE(self), MLT_PRODUCER_SERVICE(producer), track); if (result == 0) { // Resize the producer list if needed. if (self->count + 1 > self->size) { int new_size = self->size + 10; self->list = realloc(self->list, new_size * sizeof(mlt_track)); if (self->list) { memset(&self->list[self->size], 0, new_size - self->size); self->size = new_size; } } if (self->list) { // Move all of the list elements following track N down by 1. memmove(&self->list[track + 1], &self->list[track], (self->count - track) * sizeof(mlt_track)); self->count++; resize_service_caches(self); // Assign the track in our list. self->list[track] = malloc(sizeof(struct mlt_track_s)); self->list[track]->producer = producer; self->list[track]->event = mlt_events_listen(MLT_PRODUCER_PROPERTIES(producer), self, "producer-changed", (mlt_listener) mlt_multitrack_listener); mlt_properties_inc_ref(MLT_PRODUCER_PROPERTIES(producer)); mlt_event_inc_ref(self->list[track]->event); // Refresh our stats mlt_multitrack_refresh(self); } else { result = -1; } } return result; } /** Remove the N-th track. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \param track the index of the track to remove * \return true if there was an error */ int mlt_multitrack_disconnect(mlt_multitrack self, int track) { int error = -1; if (self && self->list && track >= 0 && track < self->count) { // Disconnect the track producer. error = mlt_service_disconnect_producer(MLT_MULTITRACK_SERVICE(self), track); if (!error) { // Release references on track. if (self->list[track]) { mlt_producer_close(self->list[track]->producer); mlt_event_close(self->list[track]->event); } // Contract the list of tracks. for (; track + 1 < self->count; track++) { if (self->list[track] && self->list[track + 1]) { self->list[track]->producer = self->list[track + 1]->producer; self->list[track]->event = self->list[track + 1]->event; } } if (self->list[self->count - 1]) { free(self->list[self->count - 1]); self->list[self->count - 1] = NULL; } self->count--; // Recalculate the duration. mlt_multitrack_refresh(self); } } return error; } /** Get the number of tracks. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \return the number of tracks */ int mlt_multitrack_count(mlt_multitrack self) { if (self == NULL) return 0; else return self->count; } /** Get an individual track as a producer. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \param track the 0-based index of the producer to get * \return the producer or NULL if not valid */ mlt_producer mlt_multitrack_track(mlt_multitrack self, int track) { mlt_producer producer = NULL; if (self->list != NULL && track >= 0 && track < self->count) producer = self->list[track]->producer; return producer; } /** Position comparison function for sorting. * * \private \memberof mlt_multitrack_s * \param p1 a position * \param p2 another position * \return <0 if \p p1 is less than \p p2, 0 if equal, >0 if greater */ static int position_compare(const void *p1, const void *p2) { return *(const mlt_position *) p1 - *(const mlt_position *) p2; } /** Add a position to a set. * * \private \memberof mlt_multitrack_s * \param array an array of positions (the set) * \param size the current number of positions in the array (not the capacity of the array) * \param position the position to add * \return the new size of the array */ static int add_unique(mlt_position *array, int size, mlt_position position) { int i = 0; for (i = 0; i < size; i++) if (array[i] == position) break; if (i == size) array[size++] = position; return size; } /** Increase the capacity of a set of mlt_position. * * \private \memberof mlt_multitrack_s * \param map an array of positions (the set) * \param count the current number of elements in the array (not the capacity) * \param[out] size the current capacity of the array * \return the new address of the array */ static mlt_position *resize_set(mlt_position *map, int count, int *size) { // Resize only if needed. if (count + 1 >= *size) { map = realloc(map, (*size + 1000) * sizeof(*map)); memset(map + *size, 0, 1000 * sizeof(*map)); *size += 1000; } return map; } /** Determine the clip point. * *
 * Special case here: a 'producer' has no concept of multiple clips - only the
 * playlist and multitrack producers have clip functionality. Further to that a
 * multitrack determines clip information from any connected tracks that happen
 * to be playlists.
 *
 * Additionally, it must locate clips in the correct order, for example, consider
 * the following track arrangement:
 *
 * playlist1 |0.0     |b0.0      |0.1          |0.1         |0.2           |
 * playlist2 |b1.0  |1.0           |b1.1     |1.1             |
 *
 * Note - b clips represent blanks. They are also reported as clip positions.
 *
 * When extracting clip positions from these playlists, we should get a sequence of:
 *
 * 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
 * 
* * \public \memberof mlt_multitrack_s * \param self a multitrack * \param whence from where to extract * \param index the 0-based index of which clip to extract * \return the position of clip \p index relative to \p whence */ mlt_position mlt_multitrack_clip(mlt_multitrack self, mlt_whence whence, int index) { mlt_position position = 0; int i = 0; int j = 0; int size = 1000; mlt_position *map = calloc(size, sizeof(*map)); int count = 0; for (i = 0; i < self->count; i++) { // Get the producer for this track mlt_producer producer = self->list[i]->producer; // If it's assigned and not a hidden track if (producer != NULL) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); // Determine if it's a playlist mlt_playlist playlist = mlt_properties_get_data(properties, "playlist", NULL); map = resize_set(map, count, &size); // Special case consideration of playlists if (playlist != NULL) { for (j = 0; j < mlt_playlist_count(playlist); j++) { count = add_unique(map, count, mlt_playlist_clip(playlist, mlt_whence_relative_start, j)); map = resize_set(map, count, &size); } count = add_unique(map, count, mlt_producer_get_out(producer) + 1); } else { count = add_unique(map, count, 0); count = add_unique(map, count, mlt_producer_get_out(producer) + 1); } } } // Now sort the map qsort(map, count, sizeof(mlt_position), position_compare); // Now locate the requested index switch (whence) { case mlt_whence_relative_start: if (index < count) position = map[index]; else position = map[count - 1]; break; case mlt_whence_relative_current: position = mlt_producer_position(MLT_MULTITRACK_PRODUCER(self)); for (i = 0; i < count - 2; i++) if (position >= map[i] && position < map[i + 1]) break; index += i; if (index >= 0 && index < count) position = map[index]; else if (index < 0) position = map[0]; else position = map[count - 1]; break; case mlt_whence_relative_end: if (index < count) position = map[count - index - 1]; else position = map[0]; break; } // Free the map free(map); return position; } /** Get frame method. * *
 * Special case here: The multitrack must be used in a conjunction with a downstream
 * tractor-type service, ie:
 *
 * Producer1 \
 * Producer2 - multitrack - { filters/transitions } - tractor - consumer
 * Producer3 /
 *
 * The get_frame of a tractor pulls frames from it's connected service on all tracks and
 * will terminate as soon as it receives a test card with a last_track property. The
 * important case here is that the multitrack does not move to the next frame until all
 * tracks have been pulled.
 *
 * Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
 * that all producers are positioned on the same frame. It uses the 'last track' logic
 * to determine when to move to the next frame.
 *
 * Flaw: if a transition is configured to read from a b-track which happens to trigger
 * the last frame logic (ie: it's configured incorrectly), then things are going to go
 * out of sync.
 *
 * See playlist logic too.
 * 
* * \private \memberof mlt_multitrack_s * \param parent the producer interface to a multitrack * \param[out] frame a frame by reference * \param index the 0-based track index * \return true if there was an error */ static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int index) { // Get the mutiltrack object mlt_multitrack self = parent->child; // Check if we have a track for this index if (index >= 0 && index < self->count && self->list[index] != NULL) { // Get the producer for this track mlt_producer producer = self->list[index]->producer; // Get the track hide property int hide = mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(mlt_producer_cut_parent(producer)), "hide"); // Obtain the current position mlt_position position = mlt_producer_frame(parent); // Get the parent properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(parent); // Get the speed double speed = mlt_properties_get_double(producer_properties, "_speed"); if (hide == 3) { *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); } else { // Make sure we're at the same point mlt_producer_seek(producer, position); // Get the frame from the producer mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), frame, 0); } // Indicate speed of this producer mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); mlt_properties_set_double(properties, "_speed", speed); mlt_frame_set_position(*frame, position); mlt_properties_set_int(properties, "hide", hide); } else { // Generate a test frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(parent)); // Update position on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(parent)); // Move on to the next frame if (index >= self->count) { // Let tractor know if we've reached the end mlt_properties_set_int(MLT_FRAME_PROPERTIES(*frame), "last_track", 1); // Move to the next frame mlt_producer_prepare_next(parent); } } return 0; } /** Close this instance and free its resources. * * \public \memberof mlt_multitrack_s * \param self a multitrack */ void mlt_multitrack_close(mlt_multitrack self) { if (self != NULL && mlt_properties_dec_ref(MLT_MULTITRACK_PROPERTIES(self)) <= 0) { int i = 0; for (i = 0; i < self->count; i++) { if (self->list[i] != NULL) { mlt_event_close(self->list[i]->event); mlt_producer_close(self->list[i]->producer); free(self->list[i]); } } // Close the producer self->parent.close = NULL; mlt_producer_close(&self->parent); // Free the list free(self->list); // Free the object free(self); } } mlt-7.38.0/src/framework/mlt_multitrack.h000664 000000 000000 00000005202 15172202314 020351 0ustar00rootroot000000 000000 /** * \file mlt_multitrack.h * \brief multitrack service class * \see mlt_multitrack_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_MULITRACK_H #define MLT_MULITRACK_H #include "mlt_export.h" #include "mlt_producer.h" /** \brief Track class used by mlt_multitrack_s */ struct mlt_track_s { mlt_producer producer; mlt_event event; }; typedef struct mlt_track_s *mlt_track; /** \brief Multitrack class * * A multitrack is a parallel container of producers that acts a single producer. * * \extends mlt_producer_s * \properties \em log_id not currently used, but sets it to "multitrack" */ struct mlt_multitrack_s { /** We're extending producer here */ struct mlt_producer_s parent; mlt_track *list; int size; int count; }; #define MLT_MULTITRACK_PRODUCER(multitrack) (&(multitrack)->parent) #define MLT_MULTITRACK_SERVICE(multitrack) MLT_PRODUCER_SERVICE(MLT_MULTITRACK_PRODUCER(multitrack)) #define MLT_MULTITRACK_PROPERTIES(multitrack) \ MLT_SERVICE_PROPERTIES(MLT_MULTITRACK_SERVICE(multitrack)) MLT_EXPORT mlt_multitrack mlt_multitrack_init(); MLT_EXPORT mlt_producer mlt_multitrack_producer(mlt_multitrack self); MLT_EXPORT mlt_service mlt_multitrack_service(mlt_multitrack self); MLT_EXPORT mlt_properties mlt_multitrack_properties(mlt_multitrack self); MLT_EXPORT int mlt_multitrack_connect(mlt_multitrack self, mlt_producer producer, int track); MLT_EXPORT int mlt_multitrack_insert(mlt_multitrack self, mlt_producer producer, int track); MLT_EXPORT int mlt_multitrack_disconnect(mlt_multitrack self, int track); MLT_EXPORT mlt_position mlt_multitrack_clip(mlt_multitrack self, mlt_whence whence, int index); MLT_EXPORT void mlt_multitrack_close(mlt_multitrack self); MLT_EXPORT int mlt_multitrack_count(mlt_multitrack self); MLT_EXPORT void mlt_multitrack_refresh(mlt_multitrack self); MLT_EXPORT mlt_producer mlt_multitrack_track(mlt_multitrack self, int track); #endif mlt-7.38.0/src/framework/mlt_parser.c000664 000000 000000 00000023113 15172202314 017462 0ustar00rootroot000000 000000 /** * \file mlt_parser.c * \brief service parsing functionality * \see mlt_parser_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt.h" #include static int on_invalid(mlt_parser self, mlt_service object) { return 0; } static int on_unknown(mlt_parser self, mlt_service object) { return 0; } static int on_start_producer(mlt_parser self, mlt_producer object) { return 0; } static int on_end_producer(mlt_parser self, mlt_producer object) { return 0; } static int on_start_playlist(mlt_parser self, mlt_playlist object) { return 0; } static int on_end_playlist(mlt_parser self, mlt_playlist object) { return 0; } static int on_start_tractor(mlt_parser self, mlt_tractor object) { return 0; } static int on_end_tractor(mlt_parser self, mlt_tractor object) { return 0; } static int on_start_multitrack(mlt_parser self, mlt_multitrack object) { return 0; } static int on_end_multitrack(mlt_parser self, mlt_multitrack object) { return 0; } static int on_start_track(mlt_parser self) { return 0; } static int on_end_track(mlt_parser self) { return 0; } static int on_start_filter(mlt_parser self, mlt_filter object) { return 0; } static int on_end_filter(mlt_parser self, mlt_filter object) { return 0; } static int on_start_transition(mlt_parser self, mlt_transition object) { return 0; } static int on_end_transition(mlt_parser self, mlt_transition object) { return 0; } static int on_start_chain(mlt_parser self, mlt_chain object) { return 0; } static int on_end_chain(mlt_parser self, mlt_chain object) { return 0; } static int on_start_link(mlt_parser self, mlt_link object) { return 0; } static int on_end_link(mlt_parser self, mlt_link object) { return 0; } mlt_parser mlt_parser_new() { mlt_parser self = calloc(1, sizeof(struct mlt_parser_s)); if (self != NULL && mlt_properties_init(&self->parent, self) == 0) { self->on_invalid = on_invalid; self->on_unknown = on_unknown; self->on_start_producer = on_start_producer; self->on_end_producer = on_end_producer; self->on_start_playlist = on_start_playlist; self->on_end_playlist = on_end_playlist; self->on_start_tractor = on_start_tractor; self->on_end_tractor = on_end_tractor; self->on_start_multitrack = on_start_multitrack; self->on_end_multitrack = on_end_multitrack; self->on_start_track = on_start_track; self->on_end_track = on_end_track; self->on_start_filter = on_start_filter; self->on_end_filter = on_end_filter; self->on_start_transition = on_start_transition; self->on_end_transition = on_end_transition; self->on_start_chain = on_start_chain; self->on_end_chain = on_end_chain; self->on_start_link = on_start_link; self->on_end_link = on_end_link; } return self; } mlt_properties mlt_parser_properties(mlt_parser self) { return &self->parent; } int mlt_parser_start(mlt_parser self, mlt_service object) { int error = 0; mlt_service_type type = mlt_service_identify(object); switch (type) { case mlt_service_invalid_type: error = self->on_invalid(self, object); break; case mlt_service_unknown_type: error = self->on_unknown(self, object); break; case mlt_service_producer_type: if (mlt_producer_is_cut((mlt_producer) object)) error = mlt_parser_start(self, (mlt_service) mlt_producer_cut_parent((mlt_producer) object)); error = self->on_start_producer(self, (mlt_producer) object); if (error == 0) { int i = 0; while (error == 0 && mlt_producer_filter((mlt_producer) object, i) != NULL) error = mlt_parser_start(self, (mlt_service) mlt_producer_filter((mlt_producer) object, i++)); } error = self->on_end_producer(self, (mlt_producer) object); break; case mlt_service_playlist_type: error = self->on_start_playlist(self, (mlt_playlist) object); if (error == 0) { int i = 0; while (error == 0 && i < mlt_playlist_count((mlt_playlist) object)) mlt_parser_start(self, (mlt_service) mlt_playlist_get_clip((mlt_playlist) object, i++)); i = 0; while (error == 0 && mlt_producer_filter((mlt_producer) object, i) != NULL) error = mlt_parser_start(self, (mlt_service) mlt_producer_filter((mlt_producer) object, i++)); } error = self->on_end_playlist(self, (mlt_playlist) object); break; case mlt_service_tractor_type: error = self->on_start_tractor(self, (mlt_tractor) object); if (error == 0) { int i = 0; mlt_service next = mlt_service_producer(object); mlt_parser_start(self, (mlt_service) mlt_tractor_multitrack((mlt_tractor) object)); while (next != (mlt_service) mlt_tractor_multitrack((mlt_tractor) object)) { mlt_parser_start(self, next); next = mlt_service_producer(next); } while (error == 0 && mlt_producer_filter((mlt_producer) object, i) != NULL) error = mlt_parser_start(self, (mlt_service) mlt_producer_filter((mlt_producer) object, i++)); } error = self->on_end_tractor(self, (mlt_tractor) object); break; case mlt_service_multitrack_type: error = self->on_start_multitrack(self, (mlt_multitrack) object); if (error == 0) { int i = 0; while (i < mlt_multitrack_count((mlt_multitrack) object)) { self->on_start_track(self); mlt_parser_start(self, (mlt_service) mlt_multitrack_track((mlt_multitrack) object, i++)); self->on_end_track(self); } i = 0; while (error == 0 && mlt_producer_filter((mlt_producer) object, i) != NULL) error = mlt_parser_start(self, (mlt_service) mlt_producer_filter((mlt_producer) object, i++)); } error = self->on_end_multitrack(self, (mlt_multitrack) object); break; case mlt_service_filter_type: error = self->on_start_filter(self, (mlt_filter) object); if (error == 0) { int i = 0; while (error == 0 && mlt_producer_filter((mlt_producer) object, i) != NULL) error = mlt_parser_start(self, (mlt_service) mlt_producer_filter((mlt_producer) object, i++)); } error = self->on_end_filter(self, (mlt_filter) object); break; case mlt_service_transition_type: error = self->on_start_transition(self, (mlt_transition) object); if (error == 0) { int i = 0; while (error == 0 && mlt_producer_filter((mlt_producer) object, i) != NULL) error = mlt_parser_start(self, (mlt_service) mlt_producer_filter((mlt_producer) object, i++)); } error = self->on_end_transition(self, (mlt_transition) object); break; case mlt_service_field_type: break; case mlt_service_consumer_type: break; case mlt_service_chain_type: error = self->on_start_chain(self, (mlt_chain) object); if (error == 0) { int i = 0; while (error == 0 && mlt_chain_link((mlt_chain) object, i) != NULL) mlt_parser_start(self, (mlt_service) mlt_chain_link((mlt_chain) object, i++)); i = 0; while (error == 0 && mlt_producer_filter((mlt_producer) object, i) != NULL) error = mlt_parser_start(self, (mlt_service) mlt_producer_filter((mlt_producer) object, i++)); } error = self->on_end_chain(self, (mlt_chain) object); break; case mlt_service_link_type: error = self->on_start_link(self, (mlt_link) object); error = self->on_end_link(self, (mlt_link) object); break; } return error; } void mlt_parser_close(mlt_parser self) { if (self != NULL) { mlt_properties_close(&self->parent); free(self); } } mlt-7.38.0/src/framework/mlt_parser.h000664 000000 000000 00000005030 15172202314 017465 0ustar00rootroot000000 000000 /** * \file mlt_parser.h * \brief service parsing functionality * \see mlt_parser_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PARSER_H #define MLT_PARSER_H #include "mlt_export.h" #include "mlt_types.h" /** \brief Parser class * * \extends mlt_properties_s */ struct mlt_parser_s { struct mlt_properties_s parent; int (*on_invalid)(mlt_parser self, mlt_service object); int (*on_unknown)(mlt_parser self, mlt_service object); int (*on_start_producer)(mlt_parser self, mlt_producer object); int (*on_end_producer)(mlt_parser self, mlt_producer object); int (*on_start_playlist)(mlt_parser self, mlt_playlist object); int (*on_end_playlist)(mlt_parser self, mlt_playlist object); int (*on_start_tractor)(mlt_parser self, mlt_tractor object); int (*on_end_tractor)(mlt_parser self, mlt_tractor object); int (*on_start_multitrack)(mlt_parser self, mlt_multitrack object); int (*on_end_multitrack)(mlt_parser self, mlt_multitrack object); int (*on_start_track)(mlt_parser self); int (*on_end_track)(mlt_parser self); int (*on_start_filter)(mlt_parser self, mlt_filter object); int (*on_end_filter)(mlt_parser self, mlt_filter object); int (*on_start_transition)(mlt_parser self, mlt_transition object); int (*on_end_transition)(mlt_parser self, mlt_transition object); int (*on_start_chain)(mlt_parser self, mlt_chain object); int (*on_end_chain)(mlt_parser self, mlt_chain object); int (*on_start_link)(mlt_parser self, mlt_link object); int (*on_end_link)(mlt_parser self, mlt_link object); }; MLT_EXPORT mlt_parser mlt_parser_new(); MLT_EXPORT mlt_properties mlt_parser_properties(mlt_parser self); MLT_EXPORT int mlt_parser_start(mlt_parser self, mlt_service object); MLT_EXPORT void mlt_parser_close(mlt_parser self); #endif mlt-7.38.0/src/framework/mlt_playlist.c000664 000000 000000 00000230364 15172202314 020037 0ustar00rootroot000000 000000 /** * \file mlt_playlist.c * \brief playlist service class * \see mlt_playlist_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_playlist.h" #include "mlt_factory.h" #include "mlt_field.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_multitrack.h" #include "mlt_tractor.h" #include "mlt_transition.h" #include #include #include /** \brief Virtual playlist entry used by mlt_playlist_s */ struct playlist_entry_s { mlt_producer producer; mlt_position frame_in; mlt_position frame_out; mlt_position frame_count; int repeat; mlt_position producer_length; mlt_event event; int preservation_hack; }; /* Forward declarations */ static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index); static int mlt_playlist_unmix(mlt_playlist self, int clip); static int mlt_playlist_resize_mix(mlt_playlist self, int clip, int in, int out); static mlt_producer blank_producer(mlt_playlist self); mlt_playlist mlt_playlist_alloc() { mlt_playlist self = calloc(1, sizeof(struct mlt_playlist_s)); if (self != NULL) { mlt_producer producer = &self->parent; // Construct the producer if (mlt_producer_init(producer, self) != 0) goto error1; // Override the producer get_frame producer->get_frame = producer_get_frame; // Define the destructor producer->close = (mlt_destructor) mlt_playlist_close; producer->close_object = self; // Indicate that this producer is a playlist mlt_properties_set_data(MLT_PLAYLIST_PROPERTIES(self), "playlist", self, 0, NULL, NULL); // Specify the eof condition mlt_properties_set(MLT_PLAYLIST_PROPERTIES(self), "eof", "pause"); mlt_properties_set(MLT_PLAYLIST_PROPERTIES(self), "resource", ""); mlt_properties_set(MLT_PLAYLIST_PROPERTIES(self), "mlt_type", "mlt_producer"); mlt_properties_set_position(MLT_PLAYLIST_PROPERTIES(self), "in", 0); mlt_properties_set_position(MLT_PLAYLIST_PROPERTIES(self), "out", -1); mlt_properties_set_position(MLT_PLAYLIST_PROPERTIES(self), "length", 0); self->size = 10; self->list = calloc(self->size, sizeof(playlist_entry *)); if (self->list == NULL) goto error2; mlt_events_register(MLT_PLAYLIST_PROPERTIES(self), "playlist-next"); } return self; error2: free(self->list); error1: free(self); return NULL; } /** Construct a playlist. * * Sets the resource property to "". * Set the mlt_type to property to "mlt_producer". * \deprecated use mlt_playlist_new() * \public \memberof mlt_playlist_s * \return a new playlist */ mlt_playlist mlt_playlist_init() { return mlt_playlist_alloc(); } /** Construct a playlist with a profile. * * Sets the resource property to "". * Set the mlt_type to property to "mlt_producer". * \public \memberof mlt_playlist_s * \param profile the profile to use with the profile * \return a new playlist */ mlt_playlist mlt_playlist_new(mlt_profile profile) { mlt_playlist self = mlt_playlist_alloc(); if (self) mlt_properties_set_data(MLT_PLAYLIST_PROPERTIES(self), "_profile", profile, 0, NULL, NULL); return self; } /** Get the producer associated to this playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the producer interface * \see MLT_PLAYLIST_PRODUCER */ mlt_producer mlt_playlist_producer(mlt_playlist self) { return self != NULL ? &self->parent : NULL; } /** Get the service associated to this playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the service interface * \see MLT_PLAYLIST_SERVICE */ mlt_service mlt_playlist_service(mlt_playlist self) { return MLT_PRODUCER_SERVICE(&self->parent); } /** Get the properties associated to this playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the playlist's properties list * \see MLT_PLAYLIST_PROPERTIES */ mlt_properties mlt_playlist_properties(mlt_playlist self) { return MLT_PRODUCER_PROPERTIES(&self->parent); } /** Refresh the playlist after a clip has been changed. * * \private \memberof mlt_playlist_s * \param self a playlist * \return false */ static int mlt_playlist_virtual_refresh(mlt_playlist self) { // Obtain the properties mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); int i = 0; mlt_position frame_count = 0; for (i = 0; i < self->count; i++) { // Get the producer mlt_producer producer = self->list[i]->producer; if (producer) { int current_length = mlt_producer_get_playtime(producer); // Check if the length of the producer has changed if (self->list[i]->frame_in != mlt_producer_get_in(producer) || self->list[i]->frame_out != mlt_producer_get_out(producer)) { // This clip should be removed... if (current_length < 1) { self->list[i]->frame_in = 0; self->list[i]->frame_out = -1; self->list[i]->frame_count = 0; } else { self->list[i]->frame_in = mlt_producer_get_in(producer); self->list[i]->frame_out = mlt_producer_get_out(producer); self->list[i]->frame_count = current_length; } // Update the producer_length self->list[i]->producer_length = current_length; } } // Calculate the frame_count self->list[i]->frame_count = (self->list[i]->frame_out - self->list[i]->frame_in + 1) * self->list[i]->repeat; // Update the frame_count for self clip frame_count += self->list[i]->frame_count; } // Refresh all properties mlt_events_block(properties, properties); mlt_properties_set_position(properties, "length", frame_count); mlt_events_unblock(properties, properties); mlt_properties_set_position(properties, "out", frame_count - 1); return 0; } /** Listener for producers on the playlist. * * Refreshes the playlist whenever an entry receives producer-changed. * \private \memberof mlt_playlist_s * \param producer a producer * \param self a playlist */ static void mlt_playlist_listener(mlt_producer producer, mlt_playlist self) { mlt_playlist_virtual_refresh(self); } /** Append to the virtual playlist. * * \private \memberof mlt_playlist_s * \param self a playlist * \param source a producer * \param in the producer's starting time * \param out the producer's ending time * \return true if there was an error */ static int mlt_playlist_virtual_append(mlt_playlist self, mlt_producer source, mlt_position in, mlt_position out) { mlt_producer producer = NULL; mlt_properties properties = NULL; mlt_properties parent = NULL; // If we have a cut, then use the in/out points from the cut if (mlt_producer_is_blank(source)) { mlt_position length = out - in + 1; mlt_producer blank = blank_producer(self); // Make sure the blank is long enough to accommodate the length specified if (length > mlt_producer_get_length(blank)) { mlt_properties blank_props = MLT_PRODUCER_PROPERTIES(blank); mlt_events_block(blank_props, blank_props); mlt_producer_set_in_and_out(blank, in, out); mlt_events_unblock(blank_props, blank_props); } // Now make sure the cut comes from this blank if (source == NULL) { producer = mlt_producer_cut(blank, in, out); } else if (!mlt_producer_is_cut(source) || mlt_producer_cut_parent(source) != blank) { producer = mlt_producer_cut(blank, in, out); } else { producer = source; mlt_properties_inc_ref(MLT_PRODUCER_PROPERTIES(producer)); } properties = MLT_PRODUCER_PROPERTIES(producer); // Make sure this cut of blank is long enough if (length > mlt_producer_get_length(producer)) mlt_properties_set_int(properties, "length", length); } else if (mlt_producer_is_cut(source)) { producer = source; if (in < 0) in = mlt_producer_get_in(producer); if (out < 0 || out > mlt_producer_get_out(producer)) out = mlt_producer_get_out(producer); properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_inc_ref(properties); } else { producer = mlt_producer_cut(source, in, out); if (in < 0 || in < mlt_producer_get_in(producer)) in = mlt_producer_get_in(producer); if (out < 0 || out > mlt_producer_get_out(producer)) out = mlt_producer_get_out(producer); properties = MLT_PRODUCER_PROPERTIES(producer); } // Fetch the cuts parent properties parent = MLT_PRODUCER_PROPERTIES(mlt_producer_cut_parent(producer)); // Remove loader normalizers for fx cuts if (mlt_properties_get_int(parent, "meta.fx_cut")) { mlt_service service = MLT_PRODUCER_SERVICE(mlt_producer_cut_parent(producer)); mlt_filter filter = mlt_service_filter(service, 0); while (filter != NULL && mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "_loader")) { mlt_service_detach(service, filter); filter = mlt_service_filter(service, 0); } mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(producer), "meta.fx_cut", 1); } // Check that we have room if (self->count >= self->size) { int i; self->list = realloc(self->list, (self->size + 10) * sizeof(playlist_entry *)); for (i = self->size; i < self->size + 10; i++) self->list[i] = NULL; self->size += 10; } // Create the entry self->list[self->count] = calloc(1, sizeof(playlist_entry)); if (self->list[self->count] != NULL) { self->list[self->count]->producer = producer; self->list[self->count]->frame_in = in; self->list[self->count]->frame_out = out; self->list[self->count]->frame_count = out - in + 1; self->list[self->count]->repeat = 1; self->list[self->count]->producer_length = mlt_producer_get_playtime(producer); self->list[self->count]->event = mlt_events_listen(parent, self, "producer-changed", (mlt_listener) mlt_playlist_listener); mlt_event_inc_ref(self->list[self->count]->event); mlt_properties_set(properties, "eof", "pause"); mlt_producer_set_speed(producer, 0); self->count++; } return mlt_playlist_virtual_refresh(self); } /** Locate a producer by index. * * \private \memberof mlt_playlist_s * \param self a playlist * \param[in, out] position the time at which to locate the producer, returns the time relative to the producer's starting point * \param[out] clip the index of the playlist entry * \param[out] total the duration of the playlist up to and including this producer * \return a producer or NULL if not found */ static mlt_producer mlt_playlist_locate(mlt_playlist self, mlt_position *position, int *clip, int *total) { // Default producer to NULL mlt_producer producer = NULL; // Loop for each producer until found for (*clip = 0; *clip < self->count; *clip += 1) { // Increment the total *total += self->list[*clip]->frame_count; // Check if the position indicates that we have found the clip // Note that 0 length clips get skipped automatically if (*position < self->list[*clip]->frame_count) { // Found it, now break producer = self->list[*clip]->producer; break; } else { // Decrement position by length of self entry *position -= self->list[*clip]->frame_count; } } return producer; } /** Seek in the virtual playlist. * * This gets the producer at the current position and seeks on the producer * while doing repeat and end-of-file handling. This is also responsible for * closing producers previous to the preceding playlist if the autoclose * property is set. * \private \memberof mlt_playlist_s * \param self a playlist * \param[out] progressive true if the producer should be displayed progressively * \param[out] clip_index the index of the returned service * \param[out] clip_position the position in the returned service relative to the beginning * \return the service interface of the producer at the play head * \see producer_get_frame */ static mlt_service mlt_playlist_virtual_seek(mlt_playlist self, int *progressive, int *clip_index, int *clip_position) { // Map playlist position to real producer in virtual playlist mlt_position position = mlt_producer_frame(&self->parent); // Keep the original position since we change it while iterating through the list mlt_position original = position; // Clip index and total int i = 0; int total = 0; // Locate the producer for the position mlt_producer producer = mlt_playlist_locate(self, &position, &i, &total); // Get the properties mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); // Automatically close previous producers if requested if (i > 1 // keep immediate previous in case app wants to get info about what just finished && position < 2 // tolerate off-by-one error on going to next clip && mlt_properties_get_int(properties, "autoclose")) { int j; // They might have jumped ahead! for (j = 0; j < i - 1; j++) { mlt_service_lock(MLT_PRODUCER_SERVICE(self->list[j]->producer)); mlt_producer p = self->list[j]->producer; if (p) { self->list[j]->producer = NULL; mlt_service_unlock(MLT_PRODUCER_SERVICE(p)); mlt_producer_close(p); } // If p is null, the lock will not have been "taken" } } // Get the eof handling char *eof = mlt_properties_get(properties, "eof"); // Seek in real producer to relative position if (producer != NULL) { int count = self->list[i]->frame_count / self->list[i]->repeat; *progressive = count == 1; mlt_producer_seek(producer, (int) position % count); } else if (!strcmp(eof, "pause") && total > 0) { playlist_entry *entry = self->list[self->count - 1]; int count = entry->frame_count / entry->repeat; mlt_producer self_producer = MLT_PLAYLIST_PRODUCER(self); mlt_producer_seek(self_producer, original - 1); producer = entry->producer; mlt_producer_seek(producer, (int) entry->frame_out % count); mlt_producer_set_speed(self_producer, 0); mlt_producer_set_speed(producer, 0); *progressive = count == 1; } else if (!strcmp(eof, "loop") && total > 0) { playlist_entry *entry = self->list[0]; mlt_producer self_producer = MLT_PLAYLIST_PRODUCER(self); mlt_producer_seek(self_producer, 0); producer = entry->producer; mlt_producer_seek(producer, 0); } else { producer = blank_producer(self); } if (i < self->count && clip_index && clip_position) { *clip_index = i; *clip_position = position; } // Determine if we have moved to the next entry in the playlist. if (original == total - 2) { mlt_events_fire(properties, "playlist-next", mlt_event_data_from_int(i)); } return MLT_PRODUCER_SERVICE(producer); } /** Invoked when a producer indicates that it has prematurely reached its end. * * \private \memberof mlt_playlist_s * \param self a playlist * \return a producer * \see producer_get_frame */ static mlt_producer mlt_playlist_virtual_set_out(mlt_playlist self) { mlt_producer producer = NULL; // Map playlist position to real producer in virtual playlist mlt_position position = mlt_producer_frame(&self->parent); // Loop through the virtual playlist int i = 0; for (i = 0; i < self->count; i++) { if (position < self->list[i]->frame_count) { // Found it, now break producer = self->list[i]->producer; break; } else { // Decrement position by length of this entry position -= self->list[i]->frame_count; } } if (!producer) { producer = blank_producer(self); } // Seek in real producer to relative position if (i < self->count && self->list[i]->frame_out != position) { // Update the frame_count for the changed clip (hmmm) self->list[i]->frame_out = position; self->list[i]->frame_count = self->list[i]->frame_out - self->list[i]->frame_in + 1; // Refresh the playlist mlt_playlist_virtual_refresh(self); } return producer; } /** Obtain the current clips index. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the index of the playlist entry at the current position */ int mlt_playlist_current_clip(mlt_playlist self) { // Map playlist position to real producer in virtual playlist mlt_position position = mlt_producer_frame(&self->parent); // Loop through the virtual playlist int i = 0; for (i = 0; i < self->count; i++) { if (position < self->list[i]->frame_count) { // Found it, now break break; } else { // Decrement position by length of this entry position -= self->list[i]->frame_count; } } return i; } /** Obtain the current clips producer. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the producer at the current position */ mlt_producer mlt_playlist_current(mlt_playlist self) { int i = mlt_playlist_current_clip(self); if (i < self->count) return self->list[i]->producer; else return blank_producer(self); } /** Get the position which corresponds to the start of the next clip. * * \public \memberof mlt_playlist_s * \param self a playlist * \param whence the location from which to make the index relative: * start of playlist, end of playlist, or current position * \param index the playlist entry index relative to whence * \return the time at which the referenced clip starts */ mlt_position mlt_playlist_clip(mlt_playlist self, mlt_whence whence, int index) { mlt_position position = 0; int absolute_clip = index; int i = 0; // Determine the absolute clip switch (whence) { case mlt_whence_relative_start: absolute_clip = index; break; case mlt_whence_relative_current: absolute_clip = mlt_playlist_current_clip(self) + index; break; case mlt_whence_relative_end: absolute_clip = self->count - index; break; } // Check that we're in a valid range if (absolute_clip < 0) absolute_clip = 0; else if (absolute_clip > self->count) absolute_clip = self->count; // Now determine the position for (i = 0; i < absolute_clip; i++) position += self->list[i]->frame_count; return position; } /** Get all the info about the clip specified. * * \public \memberof mlt_playlist_s * \param self a playlist * \param info a clip info struct * \param index a playlist entry index * \return true if there was an error */ int mlt_playlist_get_clip_info(mlt_playlist self, mlt_playlist_clip_info *info, int index) { int error = index < 0 || index >= self->count || self->list[index]->producer == NULL; memset(info, 0, sizeof(mlt_playlist_clip_info)); if (!error) { mlt_producer producer = mlt_producer_cut_parent(self->list[index]->producer); mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); info->clip = index; info->producer = producer; info->cut = self->list[index]->producer; info->start = mlt_playlist_clip(self, mlt_whence_relative_start, index); info->resource = mlt_properties_get(properties, "resource"); info->frame_in = self->list[index]->frame_in; info->frame_out = self->list[index]->frame_out; info->frame_count = self->list[index]->frame_count; info->repeat = self->list[index]->repeat; info->length = mlt_producer_get_length(producer); info->fps = mlt_producer_get_fps(producer); } return error; } /** Get number of clips in the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the number of playlist entries */ int mlt_playlist_count(mlt_playlist self) { return self->count; } /** Clear the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return true if there was an error */ int mlt_playlist_clear(mlt_playlist self) { int i; for (i = 0; i < self->count; i++) { mlt_event_close(self->list[i]->event); mlt_producer_close(self->list[i]->producer); } self->count = 0; return mlt_playlist_virtual_refresh(self); } /** Append a producer to the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param producer the producer to append * \return true if there was an error */ int mlt_playlist_append(mlt_playlist self, mlt_producer producer) { // Append to virtual list return mlt_playlist_virtual_append(self, producer, 0, mlt_producer_get_playtime(producer) - 1); } /** Append a producer to the playlist with in/out points. * * \public \memberof mlt_playlist_s * \param self a playlist * \param producer the producer to append * \param in the starting point on the producer; a negative value is the same as 0 * \param out the ending point on the producer; a negative value is the same as producer length - 1 * \return true if there was an error */ int mlt_playlist_append_io(mlt_playlist self, mlt_producer producer, mlt_position in, mlt_position out) { // Append to virtual list if (in < 0 && out < 0) return mlt_playlist_append(self, producer); else return mlt_playlist_virtual_append(self, producer, in, out); } /** Append a blank to the playlist of a given length. * * \public \memberof mlt_playlist_s * \param self a playlist * \param out the ending time of the blank entry, not its duration * \return true if there was an error */ int mlt_playlist_blank(mlt_playlist self, mlt_position out) { // Append to the virtual list if (out >= 0) { return mlt_playlist_virtual_append(self, blank_producer(self), 0, out); } else return 1; } /** Append a blank item to the playlist with duration as a time string. * * \public \memberof mlt_playlist_s * \param self a playlist * \param length the duration of the blank entry as a time string * \return true if there was an error */ int mlt_playlist_blank_time(mlt_playlist self, const char *length) { if (self && length) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); mlt_properties_set(properties, "_blank_time", length); mlt_position duration = mlt_properties_get_position(properties, "_blank_time"); return mlt_playlist_blank(self, duration - 1); } else return 1; } /** Insert a producer into the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param producer the producer to insert * \param where the producer's playlist entry index * \param in the starting point on the producer * \param out the ending point on the producer * \return true if there was an error */ int mlt_playlist_insert( mlt_playlist self, mlt_producer producer, int where, mlt_position in, mlt_position out) { // Append to end mlt_events_block(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_append_io(self, producer, in, out); // Move to the position specified mlt_playlist_move(self, self->count - 1, where); mlt_events_unblock(MLT_PLAYLIST_PROPERTIES(self), self); return mlt_playlist_virtual_refresh(self); } /** Remove an entry in the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param where the playlist entry index * \return true if there was an error */ int mlt_playlist_remove(mlt_playlist self, int where) { int error = where < 0 || where >= self->count; if (error == 0 && mlt_playlist_unmix(self, where) != 0) { // We need to know the current clip and the position within the playlist int current = mlt_playlist_current_clip(self); mlt_position position = mlt_producer_position(MLT_PLAYLIST_PRODUCER(self)); // We need all the details about the clip we're removing mlt_playlist_clip_info where_info; playlist_entry *entry = self->list[where]; mlt_properties properties = MLT_PRODUCER_PROPERTIES(entry->producer); // Loop variable int i = 0; // Get the clip info mlt_playlist_get_clip_info(self, &where_info, where); // Reorganise the list for (i = where + 1; i < self->count; i++) self->list[i - 1] = self->list[i]; self->count--; if (entry->preservation_hack == 0) { // Decouple from mix_in/out if necessary if (mlt_properties_get_data(properties, "mix_in", NULL) != NULL) { mlt_properties mix = mlt_properties_get_data(properties, "mix_in", NULL); mlt_properties_set_data(mix, "mix_out", NULL, 0, NULL, NULL); } if (mlt_properties_get_data(properties, "mix_out", NULL) != NULL) { mlt_properties mix = mlt_properties_get_data(properties, "mix_out", NULL); mlt_properties_set_data(mix, "mix_in", NULL, 0, NULL, NULL); } if (mlt_properties_ref_count(MLT_PRODUCER_PROPERTIES(entry->producer)) == 1) mlt_producer_clear(entry->producer); } // Close the producer associated to the clip info mlt_event_close(entry->event); mlt_producer_close(entry->producer); // Correct position if (where == current) mlt_producer_seek(MLT_PLAYLIST_PRODUCER(self), where_info.start); else if (where < current && self->count > 0) mlt_producer_seek(MLT_PLAYLIST_PRODUCER(self), position - where_info.frame_count); else if (self->count == 0) mlt_producer_seek(MLT_PLAYLIST_PRODUCER(self), 0); // Free the entry free(entry); // Refresh the playlist mlt_playlist_virtual_refresh(self); } return error; } /** Move an entry in the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param src an entry index * \param dest an entry index * \return false */ int mlt_playlist_move(mlt_playlist self, int src, int dest) { int i; /* We need to ensure that the requested indexes are valid and correct it as necessary */ if (src < 0) src = 0; if (src >= self->count) src = self->count - 1; if (dest < 0) dest = 0; if (dest >= self->count) dest = self->count - 1; if (src != dest && self->count > 1) { int current = mlt_playlist_current_clip(self); mlt_position position = mlt_producer_position(MLT_PLAYLIST_PRODUCER(self)); playlist_entry *src_entry = NULL; // We need all the details about the current clip mlt_playlist_clip_info current_info; mlt_playlist_get_clip_info(self, ¤t_info, current); position -= current_info.start; if (current == src) current = dest; else if (src < current && current < dest) current--; else if (dest < current && current < src) current++; else if (current == dest) current = src; src_entry = self->list[src]; if (src > dest) { for (i = src; i > dest; i--) self->list[i] = self->list[i - 1]; } else { for (i = src; i < dest; i++) self->list[i] = self->list[i + 1]; } self->list[dest] = src_entry; mlt_playlist_get_clip_info(self, ¤t_info, current); mlt_producer_seek(MLT_PLAYLIST_PRODUCER(self), current_info.start + position); mlt_playlist_virtual_refresh(self); } return 0; } /** Reorder the entries in the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param indices a list of current indices mapped to the new desired index * \return true if there was an error */ int mlt_playlist_reorder(mlt_playlist self, const int *indices) { // Check that the playlist is sortable. if (self->count < 2) return 1; // Sanity check the indices. The values must be in range and unique. int i, j; for (i = 0; i < self->count - 1; i++) for (j = i + 1; j < self->count; j++) if (indices[i] < 0 || indices[i] >= self->count || indices[j] < 0 || indices[j] >= self->count || indices[i] == indices[j]) return 1; // Create a new list to copy entries in a new order. playlist_entry **new_list = calloc(self->size, sizeof(playlist_entry *)); if (new_list == NULL) return 1; // Copy entries according to the new indices int new_index; for (new_index = 0; new_index < self->count; new_index++) { int old_index = indices[new_index]; new_list[new_index] = self->list[old_index]; } // Delete the old list and save the new list free(self->list); self->list = new_list; mlt_playlist_virtual_refresh(self); return 0; } /** Repeat the specified clip n times. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip a playlist entry index * \param repeat the number of times to repeat the clip * \return true if there was an error */ int mlt_playlist_repeat_clip(mlt_playlist self, int clip, int repeat) { int error = repeat < 1 || clip < 0 || clip >= self->count; if (error == 0) { playlist_entry *entry = self->list[clip]; entry->repeat = repeat; mlt_playlist_virtual_refresh(self); } return error; } /** Resize the specified clip. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param in the new starting time on the clip's producer; a negative value is the same as 0 * \param out the new ending time on the clip's producer; a negative value is the same as length - 1 * \return true if there was an error */ int mlt_playlist_resize_clip(mlt_playlist self, int clip, mlt_position in, mlt_position out) { int error = clip < 0 || clip >= self->count; if (error == 0 && mlt_playlist_resize_mix(self, clip, in, out) != 0) { playlist_entry *entry = self->list[clip]; mlt_producer producer = entry->producer; mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); mlt_events_block(properties, properties); if (mlt_producer_is_blank(producer)) { mlt_position length = out - in + 1; mlt_producer blank = blank_producer(self); // Make sure the parent blank is long enough to accommodate the length specified if (length > mlt_producer_get_length(blank)) { mlt_properties blank_props = MLT_PRODUCER_PROPERTIES(blank); mlt_properties_set_int(blank_props, "length", length); mlt_producer_set_in_and_out(blank, 0, out - in); } // Make sure this cut of blank is long enough if (length > mlt_producer_get_length(producer)) mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(producer), "length", length); } if (in < 0) in = 0; if (out < 0 || out >= mlt_producer_get_length(producer)) out = mlt_producer_get_length(producer) - 1; if (out < in) { mlt_position t = in; in = out; out = t; } mlt_producer_set_in_and_out(producer, in, out); mlt_events_unblock(properties, properties); mlt_playlist_virtual_refresh(self); } return error; } /** Split a clip on the playlist at the given position. * * This splits after the specified frame. * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param position the time at which to split relative to the beginning of the clip or its end if negative * \return true if there was an error */ int mlt_playlist_split(mlt_playlist self, int clip, mlt_position position) { int error = clip < 0 || clip >= self->count; if (error == 0) { playlist_entry *entry = self->list[clip]; position = position < 0 ? entry->frame_count + position - 1 : position; if (position >= 0 && position < entry->frame_count - 1) { int in = entry->frame_in; int out = entry->frame_out; mlt_events_block(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_resize_clip(self, clip, in, in + position); if (!mlt_producer_is_blank(entry->producer)) { mlt_properties entry_properties = MLT_PRODUCER_PROPERTIES(entry->producer); mlt_producer split = mlt_producer_cut(entry->producer, in + position + 1, out); mlt_properties split_properties = MLT_PRODUCER_PROPERTIES(split); mlt_playlist_insert(self, split, clip + 1, 0, -1); mlt_properties_lock(entry_properties); mlt_properties_copy(split_properties, entry_properties, "meta."); mlt_properties_unlock(entry_properties); mlt_producer_close(split); } else { mlt_playlist_insert(self, blank_producer(self), clip + 1, 0, out - position - 1); } mlt_events_unblock(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_virtual_refresh(self); } else { error = 1; } } return error; } /** Split the playlist at the absolute position. * * \public \memberof mlt_playlist_s * \param self a playlist * \param position the time at which to split relative to the beginning of the clip * \param left true to split before the frame starting at position * \return true if there was an error */ int mlt_playlist_split_at(mlt_playlist self, mlt_position position, int left) { int result = self == NULL ? -1 : 0; if (!result) { if (position >= 0 && position < mlt_producer_get_playtime(MLT_PLAYLIST_PRODUCER(self))) { int clip = mlt_playlist_get_clip_index_at(self, position); mlt_playlist_clip_info info; mlt_playlist_get_clip_info(self, &info, clip); if (left && position != info.start) mlt_playlist_split(self, clip, position - info.start - 1); else if (!left) mlt_playlist_split(self, clip, position - info.start); result = position; } else if (position <= 0) { result = 0; } else { result = mlt_producer_get_playtime(MLT_PLAYLIST_PRODUCER(self)); } } return result; } /** Join 1 or more consecutive clips. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the starting playlist entry index * \param count the number of entries to merge * \param merge ignored * \return true if there was an error */ int mlt_playlist_join(mlt_playlist self, int clip, int count, int merge) { int error = clip < 0 || clip >= self->count; if (error == 0) { int i = clip; mlt_playlist new_clip = mlt_playlist_new(mlt_service_profile(MLT_PLAYLIST_SERVICE(self))); mlt_properties_set_lcnumeric(MLT_PLAYLIST_PROPERTIES(new_clip), mlt_properties_get_lcnumeric(MLT_PLAYLIST_PROPERTIES(self))); mlt_events_block(MLT_PLAYLIST_PROPERTIES(self), self); if (clip + count >= self->count) count = self->count - clip - 1; for (i = 0; i <= count; i++) { playlist_entry *entry = self->list[clip]; mlt_playlist_append(new_clip, entry->producer); mlt_playlist_repeat_clip(new_clip, i, entry->repeat); entry->preservation_hack = 1; mlt_playlist_remove(self, clip); } mlt_events_unblock(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_insert(self, MLT_PLAYLIST_PRODUCER(new_clip), clip, 0, -1); mlt_playlist_close(new_clip); } return error; } /** Mix consecutive clips for a specified length and apply transition if specified. * * This version of the mix function does not utilize any frames beyond the out of * clip A or before the in point of clip B. It takes the frames needed for the length * of the transition by adjusting the duration of both clips - the out point for clip A * and the in point for clip B. * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param length the number of frames over which to create the mix * \param transition the transition to use for the mix * \return true if there was an error */ int mlt_playlist_mix(mlt_playlist self, int clip, int length, mlt_transition transition) { int error = (clip < 0 || clip + 1 >= self->count); if (error == 0) { playlist_entry *clip_a = self->list[clip]; playlist_entry *clip_b = self->list[clip + 1]; mlt_producer track_a = NULL; mlt_producer track_b = NULL; mlt_tractor tractor = mlt_tractor_new(); mlt_service_set_profile(MLT_TRACTOR_SERVICE(tractor), mlt_service_profile(MLT_PLAYLIST_SERVICE(self))); mlt_properties_set_lcnumeric(MLT_TRACTOR_PROPERTIES(tractor), mlt_properties_get_lcnumeric(MLT_PLAYLIST_PROPERTIES(self))); mlt_events_block(MLT_PLAYLIST_PROPERTIES(self), self); // Check length is valid for both clips and resize if necessary. int max_size = clip_a->frame_count > clip_b->frame_count ? clip_a->frame_count : clip_b->frame_count; length = length > max_size ? max_size : length; // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches if (length != clip_a->frame_count) track_a = mlt_producer_cut(clip_a->producer, clip_a->frame_out - length + 1, clip_a->frame_out); else track_a = clip_a->producer; if (length != clip_b->frame_count) track_b = mlt_producer_cut(clip_b->producer, clip_b->frame_in, clip_b->frame_in + length - 1); else track_b = clip_b->producer; // Set the tracks on the tractor mlt_tractor_set_track(tractor, track_a, 0); mlt_tractor_set_track(tractor, track_b, 1); // Insert the mix object into the playlist mlt_playlist_insert(self, MLT_TRACTOR_PRODUCER(tractor), clip + 1, -1, -1); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mlt_mix", tractor, 0, NULL, NULL); // Attach the transition if (transition != NULL) { mlt_field field = mlt_tractor_field(tractor); mlt_field_plant_transition(field, transition, 0, 1); mlt_transition_set_in_and_out(transition, 0, length - 1); } // Close our references to the tracks if we created new cuts above (the tracks can still be used here) if (track_a != clip_a->producer) mlt_producer_close(track_a); if (track_b != clip_b->producer) mlt_producer_close(track_b); // Check if we have anything left on the right hand clip if (track_b == clip_b->producer) { clip_b->preservation_hack = 1; mlt_playlist_remove(self, clip + 2); } else if (clip_b->frame_out - clip_b->frame_in >= length) { mlt_playlist_resize_clip(self, clip + 2, clip_b->frame_in + length, clip_b->frame_out); mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(clip_b->producer), "mix_in", tractor, 0, NULL, NULL); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mix_out", clip_b->producer, 0, NULL, NULL); } else { mlt_producer_clear(clip_b->producer); mlt_playlist_remove(self, clip + 2); } // Check if we have anything left on the left hand clip if (track_a == clip_a->producer) { clip_a->preservation_hack = 1; mlt_playlist_remove(self, clip); } else if (clip_a->frame_out - clip_a->frame_in >= length) { mlt_playlist_resize_clip(self, clip, clip_a->frame_in, clip_a->frame_out - length); mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(clip_a->producer), "mix_out", tractor, 0, NULL, NULL); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mix_in", clip_a->producer, 0, NULL, NULL); } else { mlt_producer_clear(clip_a->producer); mlt_playlist_remove(self, clip); } // Unblock and force a fire off of change events to listeners mlt_events_unblock(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_virtual_refresh(self); mlt_tractor_close(tractor); } return error; } /** Mix consecutive clips for a specified length. * * This version of the mix function maintains the out point of the clip A by occupying the * beginning of clip B before its current in point. Therefore, it ends up adjusting the in * point and duration of clip B without affecting the duration of clip A. * Also, therefore, there must be enough frames after the out point of clip A. * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param length the number of frames over which to create the mix * \return true if there was an error */ int mlt_playlist_mix_in(mlt_playlist self, int clip, int length) { int error = (clip < 0 || clip + 1 >= self->count); if (error == 0) { playlist_entry *clip_a = self->list[clip]; playlist_entry *clip_b = self->list[clip + 1]; mlt_producer track_a = NULL; mlt_producer track_b = NULL; mlt_tractor tractor = mlt_tractor_new(); mlt_service_set_profile(MLT_TRACTOR_SERVICE(tractor), mlt_service_profile(MLT_PLAYLIST_SERVICE(self))); mlt_properties_set_lcnumeric(MLT_TRACTOR_PROPERTIES(tractor), mlt_properties_get_lcnumeric(MLT_PLAYLIST_PROPERTIES(self))); mlt_events_block(MLT_PLAYLIST_PROPERTIES(self), self); // Check length is valid for both clips and resize if necessary. int max_size = (clip_a->frame_out + 1) > clip_b->frame_count ? (clip_a->frame_out + 1) : clip_b->frame_count; length = length > max_size ? max_size : length; // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches if (length != clip_a->frame_out + 1) track_a = mlt_producer_cut(clip_a->producer, clip_a->frame_out + 1, clip_a->frame_out + length); else track_a = clip_a->producer; if (length != clip_b->frame_count) track_b = mlt_producer_cut(clip_b->producer, clip_b->frame_in, clip_b->frame_in + length - 1); else track_b = clip_b->producer; // Set the tracks on the tractor mlt_tractor_set_track(tractor, track_a, 0); mlt_tractor_set_track(tractor, track_b, 1); // Insert the mix object into the playlist mlt_playlist_insert(self, MLT_TRACTOR_PRODUCER(tractor), clip + 1, -1, -1); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mlt_mix", tractor, 0, NULL, NULL); // Close our references to the tracks if we created new cuts above (the tracks can still be used here) if (track_a != clip_a->producer) mlt_producer_close(track_a); if (track_b != clip_b->producer) mlt_producer_close(track_b); // Check if we have anything left on the right hand clip if (track_b == clip_b->producer) { clip_b->preservation_hack = 1; mlt_playlist_remove(self, clip + 2); } else if (clip_b->frame_out - clip_b->frame_in >= length) { mlt_playlist_resize_clip(self, clip + 2, clip_b->frame_in + length, clip_b->frame_out); mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(clip_b->producer), "mix_in", tractor, 0, NULL, NULL); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mix_out", clip_b->producer, 0, NULL, NULL); } else { mlt_producer_clear(clip_b->producer); mlt_playlist_remove(self, clip + 2); } // Check if we have anything left on the left hand clip if (track_a == clip_a->producer) { clip_a->preservation_hack = 1; mlt_playlist_remove(self, clip); } else if (clip_a->frame_out - clip_a->frame_in > 0) { mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(clip_a->producer), "mix_out", tractor, 0, NULL, NULL); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mix_in", clip_a->producer, 0, NULL, NULL); } else { mlt_producer_clear(clip_a->producer); mlt_playlist_remove(self, clip); } // Unblock and force a fire off of change events to listeners mlt_events_unblock(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_virtual_refresh(self); mlt_tractor_close(tractor); } return error; } /** Mix consecutive clips for a specified length. * * This version of the mix function maintains the in point of the B clip by occupying the * end of clip A before its current out point. Therefore, it ends up adjusting the out * point and duration of clip A without affecting the duration or starting frame of clip B. * Also, therefore, there must be enough frames before the in point of clip B. * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param length the number of frames over which to create the mix * \return true if there was an error */ int mlt_playlist_mix_out(mlt_playlist self, int clip, int length) { int error = (clip < 0 || clip + 1 >= self->count); if (error == 0) { playlist_entry *clip_a = self->list[clip]; playlist_entry *clip_b = self->list[clip + 1]; mlt_producer track_a = NULL; mlt_producer track_b = NULL; mlt_tractor tractor = mlt_tractor_new(); mlt_service_set_profile(MLT_TRACTOR_SERVICE(tractor), mlt_service_profile(MLT_PLAYLIST_SERVICE(self))); mlt_properties_set_lcnumeric(MLT_TRACTOR_PROPERTIES(tractor), mlt_properties_get_lcnumeric(MLT_PLAYLIST_PROPERTIES(self))); mlt_events_block(MLT_PLAYLIST_PROPERTIES(self), self); // Check length is valid for both clips and resize if necessary. int max_size = clip_a->frame_count > clip_b->frame_in ? clip_a->frame_count : clip_b->frame_in; length = length > max_size ? max_size : length; // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches if (length != clip_a->frame_count) track_a = mlt_producer_cut(clip_a->producer, clip_a->frame_out - length + 1, clip_a->frame_out); else track_a = clip_a->producer; if (length != clip_b->frame_in) track_b = mlt_producer_cut(clip_b->producer, clip_b->frame_in - length, clip_b->frame_in - 1); else track_b = clip_b->producer; // Set the tracks on the tractor mlt_tractor_set_track(tractor, track_a, 0); mlt_tractor_set_track(tractor, track_b, 1); // Insert the mix object into the playlist mlt_playlist_insert(self, MLT_TRACTOR_PRODUCER(tractor), clip + 1, -1, -1); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mlt_mix", tractor, 0, NULL, NULL); // Close our references to the tracks if we created new cuts above (the tracks can still be used here) if (track_a != clip_a->producer) mlt_producer_close(track_a); if (track_b != clip_b->producer) mlt_producer_close(track_b); // Check if we have anything left on the right hand clip if (track_b == clip_b->producer) { clip_b->preservation_hack = 1; mlt_playlist_remove(self, clip + 2); } else if (clip_b->frame_out - clip_b->frame_in > 0) { mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(clip_b->producer), "mix_in", tractor, 0, NULL, NULL); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mix_out", clip_b->producer, 0, NULL, NULL); } else { mlt_producer_clear(clip_b->producer); mlt_playlist_remove(self, clip + 2); } // Check if we have anything left on the left hand clip if (track_a == clip_a->producer) { clip_a->preservation_hack = 1; mlt_playlist_remove(self, clip); } else if (clip_a->frame_out - clip_a->frame_in >= length) { mlt_playlist_resize_clip(self, clip, clip_a->frame_in, clip_a->frame_out - length); mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(clip_a->producer), "mix_out", tractor, 0, NULL, NULL); mlt_properties_set_data(MLT_TRACTOR_PROPERTIES(tractor), "mix_in", clip_a->producer, 0, NULL, NULL); } else { mlt_producer_clear(clip_a->producer); mlt_playlist_remove(self, clip); } // Unblock and force a fire off of change events to listeners mlt_events_unblock(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_virtual_refresh(self); mlt_tractor_close(tractor); } return error; } /** Add a transition to an existing mix. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param transition a transition * \return true if there was an error */ int mlt_playlist_mix_add(mlt_playlist self, int clip, mlt_transition transition) { mlt_producer producer = mlt_producer_cut_parent(mlt_playlist_get_clip(self, clip)); mlt_properties properties = producer != NULL ? MLT_PRODUCER_PROPERTIES(producer) : NULL; mlt_tractor tractor = properties != NULL ? mlt_properties_get_data(properties, "mlt_mix", NULL) : NULL; int error = transition == NULL || tractor == NULL; if (error == 0) { mlt_field field = mlt_tractor_field(tractor); mlt_field_plant_transition(field, transition, 0, 1); mlt_transition_set_in_and_out(transition, 0, self->list[clip]->frame_count - 1); } return error; } /** Return the clip at the clip index. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of a playlist entry * \return a producer or NULL if there was an error */ mlt_producer mlt_playlist_get_clip(mlt_playlist self, int clip) { if (clip >= 0 && clip < self->count) return self->list[clip]->producer; return NULL; } /** Return the clip at the specified position. * * \public \memberof mlt_playlist_s * \param self a playlist * \param position a time relative to the beginning of the playlist * \return a producer or NULL if not found */ mlt_producer mlt_playlist_get_clip_at(mlt_playlist self, mlt_position position) { int index = 0, total = 0; return mlt_playlist_locate(self, &position, &index, &total); } /** Return the clip index of the specified position. * * \public \memberof mlt_playlist_s * \param self a playlist * \param position a time relative to the beginning of the playlist * \return the index of the playlist entry */ int mlt_playlist_get_clip_index_at(mlt_playlist self, mlt_position position) { int index = 0, total = 0; mlt_playlist_locate(self, &position, &index, &total); return index; } /** Determine if the clip is a mix. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return true if the producer is a mix */ int mlt_playlist_clip_is_mix(mlt_playlist self, int clip) { mlt_producer producer = mlt_producer_cut_parent(mlt_playlist_get_clip(self, clip)); mlt_properties properties = producer != NULL ? MLT_PRODUCER_PROPERTIES(producer) : NULL; mlt_tractor tractor = properties != NULL ? mlt_properties_get_data(properties, "mlt_mix", NULL) : NULL; return tractor != NULL; } /** Remove a mixed clip - ensure that the cuts included in the mix find their way * back correctly on to the playlist. * * \private \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return true if there was an error */ static int mlt_playlist_unmix(mlt_playlist self, int clip) { int error = (clip < 0 || clip >= self->count); // Ensure that the clip request is actually a mix if (error == 0) { mlt_producer producer = mlt_producer_cut_parent(self->list[clip]->producer); mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); error = mlt_properties_get_data(properties, "mlt_mix", NULL) == NULL || self->list[clip]->preservation_hack; } if (error == 0) { playlist_entry *mix = self->list[clip]; mlt_tractor tractor = (mlt_tractor) mlt_producer_cut_parent(mix->producer); mlt_properties properties = MLT_TRACTOR_PROPERTIES(tractor); mlt_producer clip_a = mlt_properties_get_data(properties, "mix_in", NULL); mlt_producer clip_b = mlt_properties_get_data(properties, "mix_out", NULL); int length = mlt_producer_get_playtime(MLT_TRACTOR_PRODUCER(tractor)); mlt_events_block(MLT_PLAYLIST_PROPERTIES(self), self); if (clip_a != NULL) { mlt_producer_set_in_and_out(clip_a, mlt_producer_get_in(clip_a), mlt_producer_get_out(clip_a) + length); } else { mlt_producer cut = mlt_tractor_get_track(tractor, 0); mlt_playlist_insert(self, cut, clip, -1, -1); clip++; } if (clip_b != NULL) { mlt_producer_set_in_and_out(clip_b, mlt_producer_get_in(clip_b) - length, mlt_producer_get_out(clip_b)); } else { mlt_producer cut = mlt_tractor_get_track(tractor, 1); mlt_playlist_insert(self, cut, clip + 1, -1, -1); } mlt_properties_set_data(properties, "mlt_mix", NULL, 0, NULL, NULL); mlt_playlist_remove(self, clip); mlt_events_unblock(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_virtual_refresh(self); } return error; } /** Resize a mix clip. * * \private \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param in the new starting point * \param out the new ending point * \return true if there was an error */ static int mlt_playlist_resize_mix(mlt_playlist self, int clip, int in, int out) { int error = (clip < 0 || clip >= self->count); // Ensure that the clip request is actually a mix if (error == 0) { mlt_producer producer = mlt_producer_cut_parent(self->list[clip]->producer); mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); error = mlt_properties_get_data(properties, "mlt_mix", NULL) == NULL; } if (error == 0) { playlist_entry *mix = self->list[clip]; mlt_tractor tractor = (mlt_tractor) mlt_producer_cut_parent(mix->producer); mlt_properties properties = MLT_TRACTOR_PROPERTIES(tractor); mlt_producer clip_a = mlt_properties_get_data(properties, "mix_in", NULL); mlt_producer clip_b = mlt_properties_get_data(properties, "mix_out", NULL); mlt_producer track_a = mlt_tractor_get_track(tractor, 0); mlt_producer track_b = mlt_tractor_get_track(tractor, 1); int length = out - in + 1; int length_diff = length - mlt_producer_get_playtime(MLT_TRACTOR_PRODUCER(tractor)); mlt_events_block(MLT_PLAYLIST_PROPERTIES(self), self); if (clip_a != NULL) mlt_producer_set_in_and_out(clip_a, mlt_producer_get_in(clip_a), mlt_producer_get_out(clip_a) - length_diff); if (clip_b != NULL) mlt_producer_set_in_and_out(clip_b, mlt_producer_get_in(clip_b) + length_diff, mlt_producer_get_out(clip_b)); mlt_producer_set_in_and_out(track_a, mlt_producer_get_in(track_a) - length_diff, mlt_producer_get_out(track_a)); mlt_producer_set_in_and_out(track_b, mlt_producer_get_in(track_b), mlt_producer_get_out(track_b) + length_diff); mlt_producer_set_in_and_out(MLT_MULTITRACK_PRODUCER(mlt_tractor_multitrack(tractor)), in, out); mlt_producer_set_in_and_out(MLT_TRACTOR_PRODUCER(tractor), in, out); mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(mix->producer), "length", out - in + 1); mlt_producer_set_in_and_out(mix->producer, in, out); mlt_events_unblock(MLT_PLAYLIST_PROPERTIES(self), self); mlt_playlist_virtual_refresh(self); } return error; } /** Consolidate adjacent blank producers. * * \public \memberof mlt_playlist_s * \param self a playlist * \param keep_length set false to remove the last entry if it is blank */ void mlt_playlist_consolidate_blanks(mlt_playlist self, int keep_length) { if (self != NULL) { int i = 0; mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); mlt_events_block(properties, properties); for (i = 1; i < self->count; i++) { playlist_entry *left = self->list[i - 1]; playlist_entry *right = self->list[i]; if (mlt_producer_is_blank(left->producer) && mlt_producer_is_blank(right->producer)) { mlt_playlist_resize_clip(self, i - 1, 0, left->frame_count + right->frame_count - 1); mlt_playlist_remove(self, i--); } } if (!keep_length && self->count > 0) { playlist_entry *last = self->list[self->count - 1]; if (mlt_producer_is_blank(last->producer)) mlt_playlist_remove(self, self->count - 1); } mlt_events_unblock(properties, properties); mlt_playlist_virtual_refresh(self); } } /** Determine if the specified clip index is a blank. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return true if \p clip is a "blank" producer */ int mlt_playlist_is_blank(mlt_playlist self, int clip) { return self == NULL || mlt_producer_is_blank(mlt_playlist_get_clip(self, clip)); } /** Determine if the specified position is a blank. * * \public \memberof mlt_playlist_s * \param self a playlist * \param position a time relative to the start or end (negative) of the playlist * \return true if there was an error */ int mlt_playlist_is_blank_at(mlt_playlist self, mlt_position position) { return self == NULL || mlt_producer_is_blank(mlt_playlist_get_clip_at(self, position)); } /** Replace the specified clip with a blank and return the clip. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return a producer or NULL if there was an error */ mlt_producer mlt_playlist_replace_with_blank(mlt_playlist self, int clip) { mlt_producer producer = NULL; if (!mlt_playlist_is_blank(self, clip)) { playlist_entry *entry = self->list[clip]; int in = entry->frame_in; int out = entry->frame_out; mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); producer = entry->producer; mlt_properties_inc_ref(MLT_PRODUCER_PROPERTIES(producer)); mlt_events_block(properties, properties); mlt_playlist_remove(self, clip); mlt_playlist_blank(self, out - in); mlt_playlist_move(self, self->count - 1, clip); mlt_events_unblock(properties, properties); mlt_playlist_virtual_refresh(self); mlt_producer_set_in_and_out(producer, in, out); } return producer; } /** Insert blank space. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the new blank section * \param out the ending time of the new blank section (duration - 1) */ void mlt_playlist_insert_blank(mlt_playlist self, int clip, int out) { if (self != NULL && out >= 0) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); mlt_events_block(properties, properties); mlt_playlist_blank(self, out); mlt_playlist_move(self, self->count - 1, clip); mlt_events_unblock(properties, properties); mlt_playlist_virtual_refresh(self); } } /** Resize a blank entry. * * \public \memberof mlt_playlist_s * \param self a playlist * \param position the time at which the blank entry exists relative to the start or end (negative) of the playlist. * \param length the additional amount of blank frames to add * \param find true to fist locate the blank after the clip at position */ void mlt_playlist_pad_blanks(mlt_playlist self, mlt_position position, int length, int find) { if (self != NULL && length != 0) { int clip = mlt_playlist_get_clip_index_at(self, position); mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); mlt_events_block(properties, properties); if (find && clip < self->count && !mlt_playlist_is_blank(self, clip)) clip++; if (clip < self->count && mlt_playlist_is_blank(self, clip)) { mlt_playlist_clip_info info; mlt_playlist_get_clip_info(self, &info, clip); if (info.frame_out + length > info.frame_in) mlt_playlist_resize_clip(self, clip, info.frame_in, info.frame_out + length); else mlt_playlist_remove(self, clip); } else if (find && clip < self->count && length > 0) { mlt_playlist_insert_blank(self, clip, length); } mlt_events_unblock(properties, properties); mlt_playlist_virtual_refresh(self); } } /** Insert a clip at a specific time. * * \public \memberof mlt_playlist_s * \param self a playlist * \param position the time at which to insert * \param producer the producer to insert * \param mode true if you want to overwrite any blank section * \return true if there was an error */ int mlt_playlist_insert_at(mlt_playlist self, mlt_position position, mlt_producer producer, int mode) { int ret = self == NULL || position < 0 || producer == NULL; if (ret == 0) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); int length = mlt_producer_get_playtime(producer); int clip = mlt_playlist_get_clip_index_at(self, position); mlt_playlist_clip_info info; mlt_playlist_get_clip_info(self, &info, clip); mlt_events_block(properties, self); if (clip < self->count && mlt_playlist_is_blank(self, clip)) { // Split and move to new clip if need be if (position != info.start && mlt_playlist_split(self, clip, position - info.start - 1) == 0) mlt_playlist_get_clip_info(self, &info, ++clip); // Split again if need be if (length < info.frame_count) mlt_playlist_split(self, clip, length - 1); // Remove mlt_playlist_remove(self, clip); // Insert mlt_playlist_insert(self, producer, clip, -1, -1); ret = clip; } else if (clip < self->count) { if (position > info.start + info.frame_count / 2) clip++; if (mode == 1 && clip < self->count && mlt_playlist_is_blank(self, clip)) { mlt_playlist_get_clip_info(self, &info, clip); if (length < info.frame_count) mlt_playlist_split(self, clip, length); mlt_playlist_remove(self, clip); } mlt_playlist_insert(self, producer, clip, -1, -1); ret = clip; } else { if (mode == 1) { if (position == info.start) mlt_playlist_remove(self, clip); else mlt_playlist_blank(self, position - mlt_properties_get_int(properties, "length") - 1); } mlt_playlist_append(self, producer); ret = self->count - 1; } mlt_events_unblock(properties, self); mlt_playlist_virtual_refresh(self); } else { ret = -1; } return ret; } /** Get the time at which the clip starts relative to the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return the starting time */ int mlt_playlist_clip_start(mlt_playlist self, int clip) { mlt_playlist_clip_info info; if (mlt_playlist_get_clip_info(self, &info, clip) == 0) return info.start; return clip < 0 ? 0 : mlt_producer_get_playtime(MLT_PLAYLIST_PRODUCER(self)); } /** Get the playable duration of the clip. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return the duration of the playlist entry */ int mlt_playlist_clip_length(mlt_playlist self, int clip) { mlt_playlist_clip_info info; if (mlt_playlist_get_clip_info(self, &info, clip) == 0) return info.frame_count; return 0; } /** Get the duration of a blank space. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param bounded the maximum number of blank entries or 0 for all * \return the duration of a blank section */ int mlt_playlist_blanks_from(mlt_playlist self, int clip, int bounded) { int count = 0; mlt_playlist_clip_info info; if (self != NULL && clip < self->count) { mlt_playlist_get_clip_info(self, &info, clip); if (mlt_playlist_is_blank(self, clip)) count += info.frame_count; if (bounded == 0) bounded = self->count; for (clip++; clip < self->count && bounded >= 0; clip++) { mlt_playlist_get_clip_info(self, &info, clip); if (mlt_playlist_is_blank(self, clip)) count += info.frame_count; else bounded--; } } return count; } /** Remove a portion of the playlist by time. * * \public \memberof mlt_playlist_s * \param self a playlist * \param position the starting time * \param length the duration of time to remove * \return the new entry index at the position */ int mlt_playlist_remove_region(mlt_playlist self, mlt_position position, int length) { int index = mlt_playlist_get_clip_index_at(self, position); if (index >= 0 && index < self->count) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES(self); int clip_start = mlt_playlist_clip_start(self, index); int list_length = mlt_producer_get_playtime(MLT_PLAYLIST_PRODUCER(self)); mlt_events_block(properties, self); if (position + length > list_length) length -= (position + length - list_length); if (clip_start < position) { mlt_playlist_split(self, index++, position - clip_start - 1); } while (length > 0) { if (mlt_playlist_clip_length(self, index) > length) mlt_playlist_split(self, index, length - 1); length -= mlt_playlist_clip_length(self, index); mlt_playlist_remove(self, index); } mlt_playlist_consolidate_blanks(self, 0); mlt_events_unblock(properties, self); mlt_playlist_virtual_refresh(self); // Just to be sure, we'll get the clip index again... index = mlt_playlist_get_clip_index_at(self, position); } return index; } /** Get the current frame. * * The implementation of the get_frame virtual function. * \private \memberof mlt_playlist_s * \param producer a producer * \param frame a frame by reference * \param index the time at which to get the frame * \return 0 for success, -1 if producer is NULL */ static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Check that we have a producer if (producer == NULL) { *frame = NULL; return -1; } // Get this mlt_playlist mlt_playlist self = producer->child; // Need to ensure the frame is deinterlaced when repeating 1 frame int progressive = 0; int clip_index = -1; int clip_position = -1; // Get the real producer mlt_service real = mlt_playlist_virtual_seek(self, &progressive, &clip_index, &clip_position); // Check that we have a producer if (real == NULL) { *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); return 0; } // Get the frame mlt_properties_inc_ref(MLT_SERVICE_PROPERTIES(real)); if (!mlt_properties_get_int(MLT_SERVICE_PROPERTIES(real), "meta.fx_cut")) { mlt_service_get_frame(real, frame, index); } else { mlt_producer parent = mlt_producer_cut_parent((mlt_producer) real); *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(parent)); mlt_properties_set_int(MLT_FRAME_PROPERTIES(*frame), "fx_cut", 1); mlt_frame_push_service(*frame, NULL); mlt_frame_push_audio(*frame, NULL); mlt_service_apply_filters(MLT_PRODUCER_SERVICE(parent), *frame, 0); mlt_service_apply_filters(real, *frame, 0); mlt_deque_pop_front(MLT_FRAME_IMAGE_STACK(*frame)); mlt_deque_pop_front(MLT_FRAME_AUDIO_STACK(*frame)); } mlt_properties_dec_ref(MLT_SERVICE_PROPERTIES(real)); // Check if we're at the end of the clip mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); if (mlt_properties_get_int(properties, "end_of_clip")) mlt_playlist_virtual_set_out(self); // Set the consumer progressive property if (progressive) { mlt_properties_set_int(properties, "consumer.progressive", progressive); mlt_properties_set_int(properties, "test_audio", 1); } if (clip_index >= 0 && clip_index < self->size) { mlt_properties_set_int(properties, "meta.playlist.clip_position", clip_position); mlt_properties_set_int(properties, "meta.playlist.clip_length", self->list[clip_index]->frame_count); } // Check for notifier and call with appropriate argument mlt_properties playlist_properties = MLT_PRODUCER_PROPERTIES(producer); void (*notifier)(void *) = mlt_properties_get_data(playlist_properties, "notifier", NULL); if (notifier != NULL) { void *argument = mlt_properties_get_data(playlist_properties, "notifier_arg", NULL); notifier(argument); } // Update position on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_frame(producer)); // Position ourselves on the next frame mlt_producer_prepare_next(producer); return 0; } /** Close the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist */ void mlt_playlist_close(mlt_playlist self) { if (self != NULL && mlt_properties_dec_ref(MLT_PLAYLIST_PROPERTIES(self)) <= 0) { int i = 0; self->parent.close = NULL; for (i = 0; i < self->count; i++) { mlt_event_close(self->list[i]->event); mlt_producer_close(self->list[i]->producer); free(self->list[i]); } mlt_producer_close(&self->parent); free(self->list); free(self); } } mlt_producer blank_producer(mlt_playlist self) { mlt_producer blank = (mlt_producer) mlt_properties_get_data(MLT_PLAYLIST_PROPERTIES(self), "_blank", NULL); if (!blank) { mlt_profile profile = mlt_service_profile(MLT_PLAYLIST_SERVICE(self)); if (!profile) { mlt_log_error(self, "Playlist can not create blank producer without profile\n"); } else { blank = mlt_factory_producer(profile, NULL, "blank"); mlt_properties_set_data(MLT_PLAYLIST_PROPERTIES(self), "_blank", blank, 0, (mlt_destructor) mlt_producer_close, NULL); } } return blank; } mlt-7.38.0/src/framework/mlt_playlist.h000664 000000 000000 00000016704 15172202314 020044 0ustar00rootroot000000 000000 /** * \file mlt_playlist.h * \brief playlist service class * \see mlt_playlist_s * * Copyright (C) 2003-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PLAYLIST_H #define MLT_PLAYLIST_H #include "mlt_export.h" #include "mlt_producer.h" /** \brief structure for returning clip information from a playlist entry */ typedef struct { int clip; /**< the index of the clip within the playlist */ mlt_producer producer; /**< the clip's producer (or parent producer of a cut) */ mlt_producer cut; /**< the clips' cut producer */ mlt_position start; /**< the time this begins relative to the beginning of the playlist */ char *resource; /**< the file name or address of the clip */ mlt_position frame_in; /**< the clip's in point */ mlt_position frame_out; /**< the clip's out point */ mlt_position frame_count; /**< the duration of the clip */ mlt_position length; /**< the unedited duration of the clip */ float fps; /**< the frame rate of the clip */ int repeat; /**< the number of times the clip is repeated */ } mlt_playlist_clip_info; /** Playlist Entry */ typedef struct playlist_entry_s playlist_entry; /** \brief Playlist class * * A playlist is a sequential container of producers and blank spaces. The class provides all * sorts of playlist assembly and manipulation routines. A playlist is also a producer within * the framework. * * \extends mlt_producer_s * \properties \em autoclose Set this true if you are doing sequential processing and want to * automatically close producers as they are finished being used to free resources. * \properties \em meta.fx_cut Set true on a producer to indicate that it is a "fx_cut," * which is a way to add filters as a playlist entry - useful only in a multitrack. See FxCut in the docs. * \properties \em mix_in * \properties \em mix_out * \properties \em hide Set to 1 to hide the video (make it an audio-only track), * 2 to hide the audio (make it a video-only track), or 3 to hide audio and video (hidden track). * This property only applies when using a multitrack or transition. * \event \em playlist-next The playlist fires this when it moves to the next item in the list. * The event data is an integer of the index of the entry that just completed. */ struct mlt_playlist_s { struct mlt_producer_s parent; struct mlt_producer_s blank; /// Deprecated int size; int count; playlist_entry **list; }; #define MLT_PLAYLIST_PRODUCER(playlist) (&(playlist)->parent) #define MLT_PLAYLIST_SERVICE(playlist) MLT_PRODUCER_SERVICE(MLT_PLAYLIST_PRODUCER(playlist)) #define MLT_PLAYLIST_PROPERTIES(playlist) MLT_SERVICE_PROPERTIES(MLT_PLAYLIST_SERVICE(playlist)) MLT_EXPORT mlt_playlist mlt_playlist_init(); MLT_EXPORT mlt_playlist mlt_playlist_new(mlt_profile profile); MLT_EXPORT mlt_producer mlt_playlist_producer(mlt_playlist self); MLT_EXPORT mlt_service mlt_playlist_service(mlt_playlist self); MLT_EXPORT mlt_properties mlt_playlist_properties(mlt_playlist self); MLT_EXPORT int mlt_playlist_count(mlt_playlist self); MLT_EXPORT int mlt_playlist_clear(mlt_playlist self); MLT_EXPORT int mlt_playlist_append(mlt_playlist self, mlt_producer producer); MLT_EXPORT int mlt_playlist_append_io(mlt_playlist self, mlt_producer producer, mlt_position in, mlt_position out); MLT_EXPORT int mlt_playlist_blank(mlt_playlist self, mlt_position out); MLT_EXPORT int mlt_playlist_blank_time(mlt_playlist self, const char *length); MLT_EXPORT mlt_position mlt_playlist_clip(mlt_playlist self, mlt_whence whence, int index); MLT_EXPORT int mlt_playlist_current_clip(mlt_playlist self); MLT_EXPORT mlt_producer mlt_playlist_current(mlt_playlist self); MLT_EXPORT int mlt_playlist_get_clip_info(mlt_playlist self, mlt_playlist_clip_info *info, int index); MLT_EXPORT int mlt_playlist_insert( mlt_playlist self, mlt_producer producer, int where, mlt_position in, mlt_position out); MLT_EXPORT int mlt_playlist_remove(mlt_playlist self, int where); MLT_EXPORT int mlt_playlist_move(mlt_playlist self, int from, int to); MLT_EXPORT int mlt_playlist_reorder(mlt_playlist self, const int *indices); MLT_EXPORT int mlt_playlist_resize_clip(mlt_playlist self, int clip, mlt_position in, mlt_position out); MLT_EXPORT int mlt_playlist_repeat_clip(mlt_playlist self, int clip, int repeat); MLT_EXPORT int mlt_playlist_split(mlt_playlist self, int clip, mlt_position position); MLT_EXPORT int mlt_playlist_split_at(mlt_playlist self, mlt_position position, int left); MLT_EXPORT int mlt_playlist_join(mlt_playlist self, int clip, int count, int merge); MLT_EXPORT int mlt_playlist_mix(mlt_playlist self, int clip, int length, mlt_transition transition); MLT_EXPORT int mlt_playlist_mix_in(mlt_playlist self, int clip, int length); MLT_EXPORT int mlt_playlist_mix_out(mlt_playlist self, int clip, int length); MLT_EXPORT int mlt_playlist_mix_add(mlt_playlist self, int clip, mlt_transition transition); MLT_EXPORT mlt_producer mlt_playlist_get_clip(mlt_playlist self, int clip); MLT_EXPORT mlt_producer mlt_playlist_get_clip_at(mlt_playlist self, mlt_position position); MLT_EXPORT int mlt_playlist_get_clip_index_at(mlt_playlist self, mlt_position position); MLT_EXPORT int mlt_playlist_clip_is_mix(mlt_playlist self, int clip); MLT_EXPORT void mlt_playlist_consolidate_blanks(mlt_playlist self, int keep_length); MLT_EXPORT int mlt_playlist_is_blank(mlt_playlist self, int clip); MLT_EXPORT int mlt_playlist_is_blank_at(mlt_playlist self, mlt_position position); MLT_EXPORT void mlt_playlist_insert_blank(mlt_playlist self, int clip, int out); MLT_EXPORT void mlt_playlist_pad_blanks(mlt_playlist self, mlt_position position, int length, int find); MLT_EXPORT mlt_producer mlt_playlist_replace_with_blank(mlt_playlist self, int clip); MLT_EXPORT int mlt_playlist_insert_at(mlt_playlist self, mlt_position position, mlt_producer producer, int mode); MLT_EXPORT int mlt_playlist_clip_start(mlt_playlist self, int clip); MLT_EXPORT int mlt_playlist_clip_length(mlt_playlist self, int clip); MLT_EXPORT int mlt_playlist_blanks_from(mlt_playlist self, int clip, int bounded); MLT_EXPORT int mlt_playlist_remove_region(mlt_playlist self, mlt_position position, int length); MLT_EXPORT void mlt_playlist_close(mlt_playlist self); #endif mlt-7.38.0/src/framework/mlt_pool.c000664 000000 000000 00000025625 15172202314 017151 0ustar00rootroot000000 000000 /** * \file mlt_pool.c * \brief memory pooling functionality * \see mlt_pool_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_deque.h" #include "mlt_log.h" #include "mlt_properties.h" #include #include #include // Not nice - memalign is defined here apparently? #ifdef linux #include #endif // Macros to re-assign system functions. #ifdef _WIN32 #define mlt_free _aligned_free #define mlt_alloc(X) _aligned_malloc((X), 16) #define mlt_realloc(X, Y) _aligned_realloc((X), (Y), 16) #else #define mlt_free free #ifdef linux #define mlt_alloc(X) memalign(16, (X)) #else #define mlt_alloc(X) malloc((X)) #endif #define mlt_realloc realloc #endif // We now require a compile-time define to use mlt_pool. #ifndef USE_MLT_POOL #define USE_MLT_POOL 1 #endif #if !USE_MLT_POOL void mlt_pool_init() {} void *mlt_pool_alloc(int size) { return mlt_alloc(size); } void *mlt_pool_realloc(void *ptr, int size) { return mlt_realloc(ptr, size); } void mlt_pool_release(void *release) { return mlt_free(release); } void mlt_pool_purge() {} void mlt_pool_close() {} void mlt_pool_stat() {} #else /** global singleton for tracking pools */ static mlt_properties pools = NULL; /** \brief Pool (memory) class */ typedef struct mlt_pool_s { pthread_mutex_t lock; ///< lock to prevent race conditions mlt_deque stack; ///< a stack of addresses to memory blocks int size; ///< the size of the memory block as a power of 2 int count; ///< the number of blocks in the pool } * mlt_pool; /** \brief private to mlt_pool_s, for tracking items to release * * Aligned to 16 byte in case we toss buffers to external assembly * optimized libraries (sse/altivec). */ // In the future with C23 we should be able to use alignas // typedef struct alignas(16) mlt_release_s #ifdef _MSC_VER typedef struct __declspec(align(16)) mlt_release_s #else typedef struct __attribute__((aligned(16))) mlt_release_s #endif { mlt_pool pool; int references; } * mlt_release; /** Create a pool. * * \private \memberof mlt_pool_s * \param size the size of the memory blocks to hold as some power of two * \return a new pool object */ static mlt_pool pool_init(int size) { // Create the pool mlt_pool self = calloc(1, sizeof(struct mlt_pool_s)); // Initialise it if (self != NULL) { // Initialise the mutex pthread_mutex_init(&self->lock, NULL); // Create the stack self->stack = mlt_deque_init(); // Assign the size self->size = size; } // Return it return self; } /** Get an item from the pool. * * \private \memberof mlt_pool_s * \param self a pool * \return an opaque pointer */ static void *pool_fetch(mlt_pool self) { // We will generate a release object void *ptr = NULL; // Sanity check if (self != NULL) { // Lock the pool pthread_mutex_lock(&self->lock); // Check if the stack is empty if (mlt_deque_count(self->stack) != 0) { // Pop the top of the stack ptr = mlt_deque_pop_back(self->stack); // Assign the reference mlt_release release = (void *) ((char *) ptr - sizeof(struct mlt_release_s)); release->references = 1; } else { // We need to generate a release item mlt_release release = mlt_alloc(self->size); // If out of memory, log it, reclaim memory, and try again. if (!release && self->size > 0) { mlt_log_fatal(NULL, "[mlt_pool] out of memory\n"); mlt_pool_purge(); release = mlt_alloc(self->size); } // Initialise it if (release != NULL) { // Increment the number of items allocated to this pool self->count++; // Assign the pool release->pool = self; // Assign the reference release->references = 1; // Determine the ptr // The memory is not leaked - it's returned via ptr ptr = (char *) release + sizeof(struct mlt_release_s); } // cppcheck-suppress memleak } // Unlock the pool pthread_mutex_unlock(&self->lock); } // Return the generated release object return ptr; } /** Return an item to the pool. * * \private \memberof mlt_pool_s * \param ptr an opaque pointer */ static void pool_return(void *ptr) { // Sanity checks if (ptr != NULL) { // Get the release pointer mlt_release that = (void *) ((char *) ptr - sizeof(struct mlt_release_s)); // Get the pool mlt_pool self = that->pool; if (self != NULL) { // Lock the pool pthread_mutex_lock(&self->lock); // Push it back onto the stack mlt_deque_push_back(self->stack, ptr); // Unlock the pool pthread_mutex_unlock(&self->lock); return; } // Free the release itself mlt_free((char *) ptr - sizeof(struct mlt_release_s)); } } /** Destroy a pool. * * \private \memberof mlt_pool_s * \param self a pool */ static void pool_close(mlt_pool self) { if (self != NULL) { // We need to free up all items in the pool void *release = NULL; // Iterate through the stack until depleted while ((release = mlt_deque_pop_back(self->stack)) != NULL) { // We'll free this item now mlt_free((char *) release - sizeof(struct mlt_release_s)); } // We can now close the stack mlt_deque_close(self->stack); // Destroy the mutex pthread_mutex_destroy(&self->lock); // Close the pool free(self); } } /** Initialise the global pool. * * \public \memberof mlt_pool_s */ void mlt_pool_init() { // Loop variable used to create the pools int i = 0; // Create the pools pools = mlt_properties_new(); // Create the pools for (i = 8; i < 31; i++) { // Each properties item needs a name char name[32]; // Construct a pool mlt_pool pool = pool_init(1 << i); // Generate a name sprintf(name, "%d", i); // Register with properties mlt_properties_set_data(pools, name, pool, 0, (mlt_destructor) pool_close, NULL); } } /** Allocate size bytes from the pool. * * \public \memberof mlt_pool_s * \param size the number of bytes */ void *mlt_pool_alloc(int size) { // This will be used to obtain the pool to use mlt_pool pool = NULL; // Determines the index of the pool to use int index = 8; // Minimum size pooled is 256 bytes size += sizeof(struct mlt_release_s); while ((1 << index) < size) index++; // Now get the pool at the index pool = mlt_properties_get_data_at(pools, index - 8, NULL); // Now get the real item return pool_fetch(pool); } /** Allocate size bytes from the pool. * * \public \memberof mlt_pool_s * \param ptr an opaque pointer - can be in the pool or a new block to allocate * \param size the number of bytes */ void *mlt_pool_realloc(void *ptr, int size) { // Result to return void *result = NULL; // Check if we actually have an address if (ptr != NULL) { // Get the release pointer mlt_release that = (void *) ((char *) ptr - sizeof(struct mlt_release_s)); // If the current pool this ptr belongs to is big enough if (size > that->pool->size - sizeof(struct mlt_release_s)) { // Allocate result = mlt_pool_alloc(size); // Copy memcpy(result, ptr, that->pool->size - sizeof(struct mlt_release_s)); // Release mlt_pool_release(ptr); } else { // Nothing to do result = ptr; } } else { // Simply allocate result = mlt_pool_alloc(size); } return result; } /** Purge unused items in the pool. * * A form of garbage collection. * \public \memberof mlt_pool_s */ void mlt_pool_purge() { int i = 0; // For each pool for (i = 0; i < mlt_properties_count(pools); i++) { // Get the pool mlt_pool self = mlt_properties_get_data_at(pools, i, NULL); // Pointer to unused memory void *release = NULL; // Lock the pool pthread_mutex_lock(&self->lock); // We'll free all unused items now while ((release = mlt_deque_pop_back(self->stack)) != NULL) { mlt_free((char *) release - sizeof(struct mlt_release_s)); self->count--; } // Unlock the pool pthread_mutex_unlock(&self->lock); } } /** Release the allocated memory. * * \public \memberof mlt_pool_s * \param release an opaque pointer of a block in the pool */ void mlt_pool_release(void *release) { // Return to the pool pool_return(release); } /** Close the pool. * * \public \memberof mlt_pool_s */ void mlt_pool_close() { #ifdef _MLT_POOL_CHECKS_ mlt_pool_stat(); #endif // Close the properties mlt_properties_close(pools); } void mlt_pool_stat() { // Stats dump uint64_t allocated = 0, used = 0, s; int i = 0, c = mlt_properties_count(pools); mlt_log(NULL, MLT_LOG_VERBOSE, "%s: count %d\n", __FUNCTION__, c); for (i = 0; i < c; i++) { mlt_pool pool = mlt_properties_get_data_at(pools, i, NULL); if (pool->count) mlt_log_verbose(NULL, "%s: size %d allocated %d returned %d %c\n", __FUNCTION__, pool->size, pool->count, mlt_deque_count(pool->stack), pool->count != mlt_deque_count(pool->stack) ? '*' : ' '); s = pool->size; s *= pool->count; allocated += s; s = pool->count - mlt_deque_count(pool->stack); s *= pool->size; used += s; } mlt_log_verbose(NULL, "%s: allocated %" PRIu64 " bytes, used %" PRIu64 " bytes \n", __FUNCTION__, allocated, used); } #endif // NO_MLT_POOL mlt-7.38.0/src/framework/mlt_pool.h000664 000000 000000 00000002315 15172202314 017145 0ustar00rootroot000000 000000 /** * \file mlt_pool.h * \brief memory pooling functionality * \see mlt_pool_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_POOL_H #define MLT_POOL_H #include "mlt_export.h" MLT_EXPORT void mlt_pool_init(); MLT_EXPORT void *mlt_pool_alloc(int size); MLT_EXPORT void *mlt_pool_realloc(void *ptr, int size); MLT_EXPORT void mlt_pool_release(void *release); MLT_EXPORT void mlt_pool_purge(); MLT_EXPORT void mlt_pool_close(); MLT_EXPORT void mlt_pool_stat(); #endif mlt-7.38.0/src/framework/mlt_producer.c000664 000000 000000 00000125533 15172202314 020022 0ustar00rootroot000000 000000 /** * \file mlt_producer.c * \brief abstraction for all producer services * \see mlt_producer_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_producer.h" #include "mlt_factory.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_parser.h" #include "mlt_profile.h" #include #include #include #include // for stat() #include // for stat() #include // for strftime() and gtime() /* Forward references. */ static int producer_get_frame(mlt_service self, mlt_frame_ptr frame, int index); static void mlt_producer_property_changed(mlt_service owner, mlt_producer self, mlt_event_data); static void mlt_producer_service_changed(mlt_service owner, mlt_producer self); /* for debugging */ //#define _MLT_PRODUCER_CHECKS_ 1 #ifdef _MLT_PRODUCER_CHECKS_ static int producers_created = 0; static int producers_destroyed = 0; #endif /** Initialize a producer service. * * \public \memberof mlt_producer_s * \param self the producer structure to initialize * \param child a pointer to the child object for the subclass * \return true if there was an error */ int mlt_producer_init(mlt_producer self, void *child) { // Check that we haven't received NULL int error = self == NULL; // Continue if no error if (error == 0) { #ifdef _MLT_PRODUCER_CHECKS_ producers_created++; #endif // Initialise the producer memset(self, 0, sizeof(struct mlt_producer_s)); // Associate with the child self->child = child; // Initialise the service if (mlt_service_init(&self->parent, self) == 0) { // The parent is the service mlt_service parent = &self->parent; // Define the parent close parent->close = (mlt_destructor) mlt_producer_close; parent->close_object = self; // For convenience, we'll assume the close_object is self self->close_object = self; // Get the properties of the parent mlt_properties properties = MLT_SERVICE_PROPERTIES(parent); // Set the default properties mlt_properties_set(properties, "mlt_type", "mlt_producer"); mlt_properties_set_position(properties, "_position", 0.0); mlt_properties_set_double(properties, "_frame", 0); mlt_properties_set_double(properties, "_speed", 1.0); mlt_properties_set_position(properties, "in", 0); char *e = getenv("MLT_DEFAULT_PRODUCER_LENGTH"); int p = e ? atoi(e) : 15000; mlt_properties_set_position(properties, "out", MAX(0, p - 1)); mlt_properties_set_position(properties, "length", p); mlt_properties_set(properties, "eof", "pause"); mlt_properties_set(properties, "resource", ""); // Override service get_frame parent->get_frame = producer_get_frame; mlt_events_listen(properties, self, "service-changed", (mlt_listener) mlt_producer_service_changed); mlt_events_listen(properties, self, "property-changed", (mlt_listener) mlt_producer_property_changed); mlt_events_register(properties, "producer-changed"); } } return error; } /** Listener for property changes. * * If the in, out, or length properties changed, fire a "producer-changed" event. * * \private \memberof mlt_producer_s * \param owner a service (ignored) * \param self the producer * \param event_data the event data containing the name of the property that changed */ static void mlt_producer_property_changed(mlt_service owner, mlt_producer self, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (!name) return; if (!strcmp(name, "in") || !strcmp(name, "out") || !strcmp(name, "length")) mlt_events_fire(MLT_PRODUCER_PROPERTIES(mlt_producer_cut_parent(self)), "producer-changed", mlt_event_data_none()); } /** Listener for service changes. * * Fires the "producer-changed" event. * * \private \memberof mlt_producer_s * \param owner a service (ignored) * \param self the producer */ static void mlt_producer_service_changed(mlt_service owner, mlt_producer self) { mlt_events_fire(MLT_PRODUCER_PROPERTIES(mlt_producer_cut_parent(self)), "producer-changed", mlt_event_data_none()); } /** Create and initialize a new producer. * * \public \memberof mlt_producer_s * \return the new producer */ mlt_producer mlt_producer_new(mlt_profile profile) { mlt_producer self = malloc(sizeof(struct mlt_producer_s)); if (self) { if (mlt_producer_init(self, NULL) == 0) { mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(self), "_profile", profile, 0, NULL, NULL); mlt_properties_set_double(MLT_PRODUCER_PROPERTIES(self), "aspect_ratio", mlt_profile_sar(profile)); } else { free(self); return NULL; } } return self; } /** Determine if producer is a cut. * * \public \memberof mlt_producer_s * \param self a producer * \return true if \p self is a "cut" producer * \see mlt_producer_cut */ int mlt_producer_is_cut(mlt_producer self) { return mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(self), "_cut"); } /** Determine if producer is a mix. * * \public \memberof mlt_producer_s * \param self a producer * \return true if \p self is a "mix" producer * \todo Define a mix producer. */ int mlt_producer_is_mix(mlt_producer self) { mlt_properties properties = self != NULL ? MLT_PRODUCER_PROPERTIES(self) : NULL; mlt_tractor tractor = properties != NULL ? mlt_properties_get_data(properties, "mlt_mix", NULL) : NULL; return tractor != NULL; } /** Determine if the producer is a blank. * * Blank producers should only appear as an item in a playlist. * \public \memberof mlt_producer_s * \param self a producer * \return true if \p self is a "blank" producer * \see mlt_playlist_insert_blank */ int mlt_producer_is_blank(mlt_producer self) { if (self) { const char *resource = mlt_properties_get(MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent(self)), "resource"); return (resource && !strcmp("blank", resource)); } return (self == NULL); } /** Obtain the parent producer. * * \public \memberof mlt_producer_s * \param self a producer * \return either the parent producer if \p self is a "cut" producer or \p self otherwise. */ mlt_producer mlt_producer_cut_parent(mlt_producer self) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); if (mlt_producer_is_cut(self)) return mlt_properties_get_data(properties, "_cut_parent", NULL); else return self; } /** Create a cut of this producer. * * A "cut" is a portion of another (parent) producer. * * \public \memberof mlt_producer_s * \param self a producer * \param in the beginning * \param out the end * \return the new producer * \todo Expand on the value of a cut. */ mlt_producer mlt_producer_cut(mlt_producer self, int in, int out) { mlt_producer result = mlt_producer_new(mlt_service_profile(MLT_PRODUCER_SERVICE(self))); mlt_producer parent = mlt_producer_cut_parent(self); mlt_properties properties = MLT_PRODUCER_PROPERTIES(result); mlt_properties parent_props = MLT_PRODUCER_PROPERTIES(parent); mlt_properties_set_lcnumeric(properties, mlt_properties_get_lcnumeric(MLT_PRODUCER_PROPERTIES(self))); mlt_events_block(MLT_PRODUCER_PROPERTIES(result), MLT_PRODUCER_PROPERTIES(result)); // Special case - allow for a cut of the entire producer (this will squeeze all other cuts to 0) if (in <= 0) in = 0; if ((out < 0 || out >= mlt_producer_get_length(parent)) && !mlt_producer_is_blank(self)) out = MAX(0, mlt_producer_get_length(parent) - 1); mlt_properties_inc_ref(parent_props); mlt_properties_set_int(properties, "_cut", 1); mlt_properties_set_data(properties, "_cut_parent", parent, 0, (mlt_destructor) mlt_producer_close, NULL); mlt_properties_set_position(properties, "length", mlt_properties_get_position(parent_props, "length")); mlt_properties_set_double(properties, "aspect_ratio", mlt_properties_get_double(parent_props, "aspect_ratio")); mlt_producer_set_in_and_out(result, in, out); return result; } /** Get the parent service object. * * \public \memberof mlt_producer_s * \param self a producer * \return the service parent class * \see MLT_PRODUCER_SERVICE */ mlt_service mlt_producer_service(mlt_producer self) { return self != NULL ? &self->parent : NULL; } /** Get the producer properties. * * \public \memberof mlt_producer_s * \param self a producer * \return the producer's property list * \see MLT_PRODUCER_PROPERTIES */ mlt_properties mlt_producer_properties(mlt_producer self) { return MLT_SERVICE_PROPERTIES(&self->parent); } /** Seek to a specified position. * * \public \memberof mlt_producer_s * \param self a producer * \param position set the "play head" position of the producer * \return true if error * \todo Document how the properties affect behavior. * \see mlt_producer_seek_time */ int mlt_producer_seek(mlt_producer self, mlt_position position) { if (self->seek) { return self->seek(self, position); } // Determine eof handling mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); char *eof = mlt_properties_get(properties, "eof"); int use_points = 1 - mlt_properties_get_int(properties, "ignore_points"); // Recursive behaviour for cuts - repositions parent and then repositions cut // hence no return on this condition if (mlt_producer_is_cut(self)) mlt_producer_seek(mlt_producer_cut_parent(self), position + mlt_producer_get_in(self)); // Check bounds if (mlt_service_identify(MLT_PRODUCER_SERVICE(self)) == mlt_service_link_type) { // Do not bounds check a link. } else if (position < 0 || mlt_producer_get_playtime(self) == 0) { position = 0; } else if (use_points && (eof == NULL || !strcmp(eof, "pause")) && position >= mlt_producer_get_playtime(self)) { mlt_producer_set_speed(self, 0); position = mlt_producer_get_playtime(self) - 1; } else if (use_points && eof && !strcmp(eof, "loop") && position >= mlt_producer_get_playtime(self)) { position = (int) position % (int) mlt_producer_get_playtime(self); } // Set the position mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(self), "_position", position); // Calculate the absolute frame mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(self), "_frame", use_points * mlt_producer_get_in(self) + position); return 0; } /** Seek to a specified time string. * * \public \memberof mlt_producer_s * \param self a producer * \param time set the "play head" position of the producer to the time string * \return true if error * \see mlt_producer_seek */ int mlt_producer_seek_time(mlt_producer self, const char *time) { mlt_properties_set(MLT_PRODUCER_PROPERTIES(self), "_seek_time", time); mlt_position position = mlt_properties_get_position(MLT_PRODUCER_PROPERTIES(self), "_seek_time"); return mlt_producer_seek(self, position); } /** Get the current position (relative to in point). * * \public \memberof mlt_producer_s * \param self a producer * \return the position of the "play head" relative to its beginning */ mlt_position mlt_producer_position(mlt_producer self) { return mlt_properties_get_position(MLT_PRODUCER_PROPERTIES(self), "_position"); } /** Get the current position (relative to start of producer). * * \public \memberof mlt_producer_s * \param self a producer * \return the position of the "play head" regardless of the in point */ mlt_position mlt_producer_frame(mlt_producer self) { return mlt_properties_get_position(MLT_PRODUCER_PROPERTIES(self), "_frame"); } /** Get the current position (relative to start of producer) as a time string. * * \public \memberof mlt_producer_s * \param self a producer * \param format the time value format * \return the position of the "play head" regardless of the in point */ char *mlt_producer_frame_time(mlt_producer self, mlt_time_format format) { return mlt_properties_get_time(MLT_PRODUCER_PROPERTIES(self), "_frame", format); } /** Set the playing speed. * * \public \memberof mlt_producer_s * \param self a producer * \param speed the new speed as a relative factor (1.0 = normal) * \return true if error */ int mlt_producer_set_speed(mlt_producer self, double speed) { return mlt_properties_set_double(MLT_PRODUCER_PROPERTIES(self), "_speed", speed); } /** Get the playing speed. * * \public \memberof mlt_producer_s * \param self a producer * \return the speed as a relative factor (1.0 = normal) */ double mlt_producer_get_speed(mlt_producer self) { return mlt_properties_get_double(MLT_PRODUCER_PROPERTIES(self), "_speed"); } /** Get the frames per second. * * This is determined by the producer's profile. * * \public \memberof mlt_producer_s * \param self a producer * \return the video refresh rate */ double mlt_producer_get_fps(mlt_producer self) { mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(self)); return mlt_profile_fps(profile); } /** Set the in and out points. * * The in point is where play out should start relative to the natural start * of the underlying file. The out point is where play out should end, also * relative to the start of the underlying file. If the underlying resource is * a live stream, then the in point is an offset relative to first usable * sample. * * \public \memberof mlt_producer_s * \param self a producer * \param in the relative starting time; a negative value is the same as 0 * \param out the relative ending time; a negative value is the same as length - 1 * \return true if error */ int mlt_producer_set_in_and_out(mlt_producer self, mlt_position in, mlt_position out) { if (self->set_in_and_out) { return self->set_in_and_out(self, in, out); } mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); // Correct ins and outs if necessary if (in < 0) in = 0; else if (in >= mlt_producer_get_length(self)) in = MAX(0, mlt_producer_get_length(self) - 1); if (mlt_producer_is_blank(self) && out >= mlt_producer_get_length(self)) // Extend the blank producer if needed. mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(self), "length", out + 1); else if (out < 0 || out >= mlt_producer_get_length(self)) // Get the out point from the length. out = MAX(0, mlt_producer_get_length(self) - 1); // Swap ins and outs if wrong if (out < in) { mlt_position t = in; in = out; out = t; } // Set the values mlt_events_block(properties, properties); mlt_properties_set_position(properties, "in", in); mlt_events_unblock(properties, properties); mlt_properties_set_position(properties, "out", out); return 0; } /** Physically reduce the producer (typically a cut) to a 0 length. * Essentially, all 0 length cuts should be immediately removed by containers. * * \public \memberof mlt_producer_s * \param self a producer * \return false */ int mlt_producer_clear(mlt_producer self) { if (self != NULL) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); mlt_events_block(properties, properties); mlt_properties_set_position(properties, "in", 0); mlt_events_unblock(properties, properties); mlt_properties_set_position(properties, "out", -1); } return 0; } /** Get the in point. * * \public \memberof mlt_producer_s * \param self a producer * \return the in point */ mlt_position mlt_producer_get_in(mlt_producer self) { return mlt_properties_get_position(MLT_PRODUCER_PROPERTIES(self), "in"); } /** Get the out point. * * \public \memberof mlt_producer_s * \param self a producer * \return the out point */ mlt_position mlt_producer_get_out(mlt_producer self) { return mlt_properties_get_position(MLT_PRODUCER_PROPERTIES(self), "out"); } /** Get the total play time. * * \public \memberof mlt_producer_s * \param self a producer * \return the playable (based on in and out points) duration */ mlt_position mlt_producer_get_playtime(mlt_producer self) { return mlt_producer_get_out(self) - mlt_producer_get_in(self) + 1; } /** Get the total, unedited length of the producer. * * The value returned by a live streaming producer is unknown. * * \public \memberof mlt_producer_s * \param self a producer * \return the duration of the producer regardless of in and out points */ mlt_position mlt_producer_get_length(mlt_producer self) { return mlt_properties_get_position(MLT_PRODUCER_PROPERTIES(self), "length"); } /** Get the total, unedited length of the producer as a time string. * * The value returned by a live streaming producer is unknown. * * \public \memberof mlt_producer_s * \param self a producer * \param format the time value format * \return the duration of the producer regardless of in and out points */ char *mlt_producer_get_length_time(mlt_producer self, mlt_time_format format) { return mlt_properties_get_time(MLT_PRODUCER_PROPERTIES(self), "length", format); } /** Prepare for next frame. * * Advance the play out position. If the speed is less than zero, it will * move the play out position in the reverse direction. * * \public \memberof mlt_producer_s * \param self a producer */ void mlt_producer_prepare_next(mlt_producer self) { if (mlt_producer_get_speed(self) != 0) mlt_producer_seek(self, mlt_producer_position(self) + mlt_producer_get_speed(self)); } /** Get a frame. * * This is the implementation of the \p get_frame virtual function. * It requests a new frame object from the actual producer for the current * play out position. The producer and its filters can add information and * operations to the frame object in their get_frame handlers. * * \private \memberof mlt_producer_s * \param service a service * \param[out] frame a frame by reference * \param index as determined by the actual producer * \return true if there was an error * \todo Learn more about the details and document how certain properties affect * its behavior. */ static int producer_get_frame(mlt_service service, mlt_frame_ptr frame, int index) { int result = 1; mlt_producer self = service != NULL ? service->child : NULL; if (self != NULL && !mlt_producer_is_cut(self)) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); // Determine eof handling char *eof = mlt_properties_get(MLT_PRODUCER_PROPERTIES(self), "eof"); // Get the speed of the producer double speed = mlt_producer_get_speed(self); // We need to use the clone if it's specified mlt_producer clone = mlt_properties_get_data(properties, "use_clone", NULL); // If no clone is specified, use self clone = clone == NULL ? self : clone; // A properly instatiated producer will have a get_frame method... if (self->get_frame == NULL || (eof && !strcmp(eof, "continue") && mlt_producer_position(self) > mlt_producer_get_out(self))) { // Generate a test frame *frame = mlt_frame_init(service); // Set the position result = mlt_frame_set_position(*frame, mlt_producer_position(self)); // Mark as a test card mlt_properties_set_int(MLT_FRAME_PROPERTIES(*frame), "test_image", 1); mlt_properties_set_int(MLT_FRAME_PROPERTIES(*frame), "test_audio", 1); // Calculate the next position mlt_producer_prepare_next(self); } else { // Get the frame from the implementation result = self->get_frame(clone, frame, index); } // Copy the fps and speed of the producer onto the frame properties = MLT_FRAME_PROPERTIES(*frame); mlt_properties_set_double(properties, "_speed", speed); mlt_properties_set_int(properties, "test_audio", mlt_frame_is_test_audio(*frame)); mlt_properties_set_int(properties, "test_image", mlt_frame_is_test_card(*frame)); if (mlt_properties_get_data(properties, "_producer", NULL) == NULL) mlt_properties_set_data(properties, "_producer", service, 0, NULL, NULL); } else if (self != NULL) { // Get the speed of the cut double speed = mlt_producer_get_speed(self); // Get the parent of the cut mlt_producer parent = mlt_producer_cut_parent(self); // Get the properties of the parent mlt_properties parent_properties = MLT_PRODUCER_PROPERTIES(parent); // Get the properties of the cut mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); // Determine the clone index int clone_index = mlt_properties_get_int(properties, "_clone"); // Determine the clone to use mlt_producer clone = self; if (clone_index > 0) { char key[25]; sprintf(key, "_clone.%d", clone_index - 1); clone = mlt_properties_get_data(MLT_PRODUCER_PROPERTIES(mlt_producer_cut_parent(self)), key, NULL); if (clone == NULL) mlt_log(service, MLT_LOG_ERROR, "requested clone doesn't exist %d\n", clone_index); clone = clone == NULL ? self : clone; } else { clone = parent; } // We need to seek to the correct position in the clone mlt_producer_seek(clone, mlt_producer_get_in(self) + mlt_properties_get_int(properties, "_position")); // Assign the clone property to the parent mlt_properties_set_data(parent_properties, "use_clone", clone, 0, NULL, NULL); // Now get the frame from the parents service result = mlt_service_get_frame(MLT_PRODUCER_SERVICE(parent), frame, index); // We're done with the clone now mlt_properties_set_data(parent_properties, "use_clone", NULL, 0, NULL, NULL); // This is useful and required by always_active transitions to determine in/out points of the cut if (mlt_properties_get_data(MLT_FRAME_PROPERTIES(*frame), "_producer", NULL) == MLT_PRODUCER_SERVICE(parent)) mlt_properties_set_data(MLT_FRAME_PROPERTIES(*frame), "_producer", self, 0, NULL, NULL); mlt_properties_set_double(MLT_FRAME_PROPERTIES(*frame), "_speed", speed); mlt_producer_prepare_next(self); } else { *frame = mlt_frame_init(service); result = 0; } // Pass on all meta properties from the producer/cut on to the frame if (*frame != NULL && self != NULL) { mlt_properties p_props = MLT_PRODUCER_PROPERTIES(self); mlt_properties f_props = MLT_FRAME_PROPERTIES(*frame); mlt_properties_lock(p_props); mlt_properties_copy(f_props, p_props, "meta."); mlt_properties_pass(f_props, p_props, "set."); mlt_properties_unlock(p_props); } return result; } /** Attach a filter. * * \public \memberof mlt_producer_s * \param self a producer * \param filter the filter to attach * \return true if there was an error */ int mlt_producer_attach(mlt_producer self, mlt_filter filter) { return mlt_service_attach(MLT_PRODUCER_SERVICE(self), filter); } /** Detach a filter. * * \public \memberof mlt_producer_s * \param self a service * \param filter the filter to detach * \return true if there was an error */ int mlt_producer_detach(mlt_producer self, mlt_filter filter) { return mlt_service_detach(MLT_PRODUCER_SERVICE(self), filter); } /** Retrieve a filter. * * \public \memberof mlt_producer_s * \param self a service * \param index which filter to retrieve * \return the filter or null if there was an error */ mlt_filter mlt_producer_filter(mlt_producer self, int index) { return mlt_service_filter(MLT_PRODUCER_SERVICE(self), index); } /** Clone a producer. * * \private \memberof mlt_producer_s * \param self a producer * \return a new producer that is a copy of \p self * \see mlt_producer_set_clones */ static mlt_producer mlt_producer_clone(mlt_producer self) { mlt_producer clone = NULL; mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); char *resource = mlt_properties_get(properties, "resource"); char *service = mlt_properties_get(properties, "mlt_service"); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(self)); mlt_events_block(mlt_factory_event_object(), mlt_factory_event_object()); if (service != NULL) clone = mlt_factory_producer(profile, service, resource); if (clone == NULL && resource != NULL) clone = mlt_factory_producer(profile, NULL, resource); if (clone != NULL) mlt_properties_inherit(MLT_PRODUCER_PROPERTIES(clone), properties); mlt_events_unblock(mlt_factory_event_object(), mlt_factory_event_object()); return clone; } /** Create clones. * * \private \memberof mlt_producer_s * \param self a producer * \param clones the number of copies to make * \see mlt_producer_optimise */ static void mlt_producer_set_clones(mlt_producer self, int clones) { mlt_producer parent = mlt_producer_cut_parent(self); mlt_properties properties = MLT_PRODUCER_PROPERTIES(parent); int existing = mlt_properties_get_int(properties, "_clones"); int i = 0; char key[25]; // If the number of existing clones is different, then create/remove as necessary if (existing != clones) { if (existing < clones) { for (i = existing; i < clones; i++) { mlt_producer clone = mlt_producer_clone(parent); sprintf(key, "_clone.%d", i); mlt_properties_set_data(properties, key, clone, 0, (mlt_destructor) mlt_producer_close, NULL); } } else { for (i = clones; i < existing; i++) { sprintf(key, "_clone.%d", i); mlt_properties_set_data(properties, key, NULL, 0, NULL, NULL); } } } // Ensure all properties on the parent are passed to the clones for (i = 0; i < clones; i++) { mlt_producer clone = NULL; sprintf(key, "_clone.%d", i); clone = mlt_properties_get_data(properties, key, NULL); if (clone != NULL) mlt_properties_pass(MLT_PRODUCER_PROPERTIES(clone), properties, ""); } // Update the number of clones on the properties mlt_properties_set_int(properties, "_clones", clones); } /** \brief private to mlt_producer_s, used by mlt_producer_optimise() */ typedef struct { int multitrack; int track; int position; int length; int offset; } track_info; /** \brief private to mlt_producer_s, used by mlt_producer_optimise() */ typedef struct { mlt_producer cut; int start; int end; } clip_references; static int intersect(clip_references *a, clip_references *b) { int diff = (a->start - b->start) + (a->end - b->end); return diff >= 0 && diff < (a->end - a->start + 1); } static int push(mlt_parser self, int multitrack, int track, int position) { mlt_properties properties = mlt_parser_properties(self); mlt_deque stack = mlt_properties_get_data(properties, "stack", NULL); track_info *info = malloc(sizeof(track_info)); info->multitrack = multitrack; info->track = track; info->position = position; info->length = 0; info->offset = 0; return mlt_deque_push_back(stack, info); } static track_info *pop(mlt_parser self) { mlt_properties properties = mlt_parser_properties(self); mlt_deque stack = mlt_properties_get_data(properties, "stack", NULL); return mlt_deque_pop_back(stack); } static track_info *peek(mlt_parser self) { mlt_properties properties = mlt_parser_properties(self); mlt_deque stack = mlt_properties_get_data(properties, "stack", NULL); return mlt_deque_peek_back(stack); } static int on_start_multitrack(mlt_parser self, mlt_multitrack object) { track_info *info = peek(self); return push(self, info->multitrack++, info->track, info->position); } static int on_start_track(mlt_parser self) { track_info *info = peek(self); info->position -= info->offset; info->length -= info->offset; return push(self, info->multitrack, info->track++, info->position); } static int on_start_producer(mlt_parser self, mlt_producer object) { mlt_properties properties = mlt_parser_properties(self); mlt_properties producers = mlt_properties_get_data(properties, "producers", NULL); mlt_producer parent = mlt_producer_cut_parent(object); if (mlt_service_identify((mlt_service) mlt_producer_cut_parent(object)) == mlt_service_producer_type && mlt_producer_is_cut(object)) { int ref_count = 0; clip_references *old_refs = NULL; clip_references *refs = NULL; char key[50]; int count = 0; track_info *info = peek(self); sprintf(key, "%p", parent); mlt_properties_get_data(producers, key, &count); mlt_properties_set_data(producers, key, parent, ++count, NULL, NULL); old_refs = mlt_properties_get_data(properties, key, &ref_count); refs = malloc((ref_count + 1) * sizeof(clip_references)); if (old_refs != NULL) memcpy(refs, old_refs, ref_count * sizeof(clip_references)); mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(object), "_clone", -1); refs[ref_count].cut = object; refs[ref_count].start = info->position; refs[ref_count].end = info->position + mlt_producer_get_playtime(object) - 1; mlt_properties_set_data(properties, key, refs, ++ref_count, free, NULL); info->position += mlt_producer_get_playtime(object); info->length += mlt_producer_get_playtime(object); } return 0; } static int on_end_track(mlt_parser self) { track_info *track = pop(self); track_info *multi = peek(self); multi->length += track->length; multi->position += track->length; multi->offset = track->length; free(track); return 0; } static int on_end_multitrack(mlt_parser self, mlt_multitrack object) { track_info *multi = pop(self); track_info *track = peek(self); track->position += multi->length; track->length += multi->length; free(multi); return 0; } /** Optimise for overlapping cuts from the same clip. * * \todo learn more about this * \public \memberof mlt_producer_s * \param self a producer * \return true if there was an error */ int mlt_producer_optimise(mlt_producer self) { int error = 1; mlt_parser parser = mlt_parser_new(); if (parser != NULL) { int i = 0, j = 0, k = 0; mlt_properties properties = mlt_parser_properties(parser); mlt_properties producers = mlt_properties_new(); mlt_deque stack = mlt_deque_init(); mlt_properties_set_data(properties, "producers", producers, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set_data(properties, "stack", stack, 0, (mlt_destructor) mlt_deque_close, NULL); parser->on_start_producer = on_start_producer; parser->on_start_track = on_start_track; parser->on_end_track = on_end_track; parser->on_start_multitrack = on_start_multitrack; parser->on_end_multitrack = on_end_multitrack; push(parser, 0, 0, 0); mlt_parser_start(parser, MLT_PRODUCER_SERVICE(self)); free(pop(parser)); for (k = 0; k < mlt_properties_count(producers); k++) { char *name = mlt_properties_get_name(producers, k); int count = 0; int clones = 0; int max_clones = 0; mlt_producer producer = mlt_properties_get_data_at(producers, k, &count); if (producer != NULL && count > 1) { clip_references *refs = mlt_properties_get_data(properties, name, &count); for (i = 0; i < count; i++) { clones = 0; for (j = i + 1; j < count; j++) { if (intersect(&refs[i], &refs[j])) { clones++; mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(refs[j].cut), "_clone", clones); } } if (clones > max_clones) max_clones = clones; } for (i = 0; i < count; i++) { mlt_producer cut = refs[i].cut; if (mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(cut), "_clone") == -1) mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(cut), "_clone", 0); } mlt_producer_set_clones(producer, max_clones); } else if (producer != NULL) { clip_references *refs = mlt_properties_get_data(properties, name, &count); for (i = 0; i < count; i++) { mlt_producer cut = refs[i].cut; mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(cut), "_clone", 0); } mlt_producer_set_clones(producer, 0); } } mlt_parser_close(parser); } return error; } /** Close the producer. * * Destroys the producer and deallocates its resources managed by its * properties list. This will call the close virtual function. Therefore, a * subclass that defines its own close function should set its virtual close * function to NULL prior to calling this to avoid circular calls. * * \public \memberof mlt_producer_s * \param self a producer */ void mlt_producer_close(mlt_producer self) { if (self != NULL && mlt_properties_dec_ref(MLT_PRODUCER_PROPERTIES(self)) <= 0) { self->parent.close = NULL; if (self->close != NULL) { self->close(self->close_object); } else { int destroy = mlt_producer_is_cut(self); #if _MLT_PRODUCER_CHECKS_ == 1 // Show debug info mlt_properties_debug(MLT_PRODUCER_PROPERTIES(self), "Producer closing", stderr); #endif #ifdef _MLT_PRODUCER_CHECKS_ // Show current stats - these should match when the app is closed mlt_log(MLT_PRODUCER_SERVICE(self), MLT_LOG_DEBUG, "Producers created %d, destroyed %d\n", producers_created, ++producers_destroyed); #endif mlt_service_close(&self->parent); if (destroy) free(self); } } } /* * Boost implementation of timegm() * (C) Copyright Howard Hinnant * (C) Copyright 2010-2011 Vicente J. Botet Escriba */ static inline int32_t is_leap(int32_t year) { if (year % 400 == 0) return 1; if (year % 100 == 0) return 0; if (year % 4 == 0) return 1; return 0; } static inline int32_t days_from_0(int32_t year) { year--; return 365 * year + (year / 400) - (year / 100) + (year / 4); } static inline int32_t days_from_1970(int32_t year) { const int days_from_0_to_1970 = days_from_0(1970); return days_from_0(year) - days_from_0_to_1970; } static inline int32_t days_from_1jan(int32_t year, int32_t month, int32_t day) { static const int32_t days[2][12] = {{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}}; return days[is_leap(year)][month - 1] + day - 1; } static inline time_t internal_timegm(struct tm const *t) { int year = t->tm_year + 1900; int month = t->tm_mon; if (month > 11) { year += month / 12; month %= 12; } else if (month < 0) { int years_diff = (-month + 11) / 12; year -= years_diff; month += 12 * years_diff; } month++; int day = t->tm_mday; int day_of_year = days_from_1jan(year, month, day); int days_since_epoch = days_from_1970(year) + day_of_year; time_t seconds_in_day = 3600 * 24; time_t result = seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec; return result; } /* End of Boost implementation of timegm(). */ /** Get the creation time for the producer. * * The creation_time value is searched in the following order: * - A "creation_time" property in ISO 8601 format (yyyy-mm-ddThh:mm:ss) * - A "meta.attr.com.apple.quicktime.creationdate.markup" property in ISO 8601 format * - A "meta.attr.creation_time.markup" property in ISO 8601 format * - If the producer has a resource that is a file, the mtime of the file * * \public \memberof mlt_producer_s * \param self a producer * \return the creation time of the producer in seconds since the epoch */ int64_t mlt_producer_get_creation_time(mlt_producer self) { mlt_producer producer = mlt_producer_cut_parent(self); // Prefer creation_time producer property if present char *datestr = mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "creation_time"); if (!datestr) { // Fall back to quicktime creationdate metadata (common for .mov files) // creationdate is preferred over creation_time metadata because // creation_time may be recalculated if the device re-encodes the file. datestr = mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "meta.attr.com.apple.quicktime.creationdate.markup"); } if (!datestr) { // Fall back to creation_time metadata (common for most media handled by ffmpeg) datestr = mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "meta.attr.creation_time.markup"); } if (datestr) { struct tm time_info = {0}; double seconds; char offset_indicator = 0; int hour_offset = 0; int min_offset = 0; int ret = sscanf(datestr, "%04d-%02d-%02dT%02d:%02d:%lf%c%02d%02d", &time_info.tm_year, &time_info.tm_mon, &time_info.tm_mday, &time_info.tm_hour, &time_info.tm_min, &seconds, &offset_indicator, &hour_offset, &min_offset); if (ret >= 6) { time_info.tm_sec = (int) seconds; time_info.tm_mon -= 1; time_info.tm_year -= 1900; time_info.tm_isdst = -1; int64_t milliseconds = (int64_t) internal_timegm(&time_info) * 1000; milliseconds += (seconds - (double) time_info.tm_sec) * 1000.0; // Apply time zone offset if present. if (ret == 9 && offset_indicator == '-') { milliseconds += ((hour_offset * 60) + min_offset) * 60000; } else if (ret == 9 && offset_indicator == '+') { milliseconds -= ((hour_offset * 60) + min_offset) * 60000; } return milliseconds; } } // Fall back to file modification time. char *resource = mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "resource"); if (!resource) { resource = mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "warp_resource"); } if (resource) { struct stat file_info; if (!mlt_stat(resource, &file_info)) { return (int64_t) file_info.st_mtime * 1000; } } return 0; } /** Set the creation time for the producer. * * A "creation_time" property in ISO 8601 format (yyyy-mm-ddThh:mm:ss) will be * applied to the producer. * * \public \memberof mlt_producer_s * \param self a producer * \param creation_time the creation time of the producer in seconds since the epoch */ void mlt_producer_set_creation_time(mlt_producer self, int64_t creation_time) { time_t time = creation_time / 1000; mlt_producer parent = mlt_producer_cut_parent(self); char *datestr = calloc(1, 20); strftime(datestr, 20, "%Y-%m-%dT%H:%M:%S", gmtime(&time)); mlt_properties_set(MLT_PRODUCER_PROPERTIES(parent), "creation_time", datestr); free(datestr); } /** Probe the producer to publish metadata properties. * * After this call the producer will publish meta.media properties * * \public \memberof mlt_producer_s * \param self a producer * \return true on error */ int mlt_producer_probe(mlt_producer self) { if (self) { int (*probe)(mlt_producer) = mlt_properties_get_data(MLT_PRODUCER_PROPERTIES(self), "mlt_producer_probe", NULL); if (probe) return probe(self); } return 0; } mlt-7.38.0/src/framework/mlt_producer.h000664 000000 000000 00000016374 15172202314 020031 0ustar00rootroot000000 000000 /** * \file mlt_producer.h * \brief abstraction for all producer services * \see mlt_producer_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PRODUCER_H #define MLT_PRODUCER_H #include "mlt_export.h" #include "mlt_filter.h" #include "mlt_profile.h" #include "mlt_service.h" /** \brief Producer abstract service class * * A producer is a service that generates audio, video, and metadata. * Some day it may also generate text (subtitles). This is not to say * a producer "synthesizes," rather that is an origin of data within the * service network - that could be through synthesis or reading a stream. * * \extends mlt_service * \event \em producer-changed either service-changed was fired or the timing of the producer changed * \properties \em mlt_type the name of the service subclass, e.g. mlt_producer * \properties \em mlt_service the name of a producer subclass * \properties \em _position the current position of the play head, relative to the in point * \properties \em _frame the current position of the play head, relative to the beginning of the resource * \properties \em _speed the current speed factor, where 1.0 is normal * \properties \em aspect_ratio sample aspect ratio * \properties \em length the duration of the cut in frames * \properties \em eof the end-of-file behavior, one of: pause, continue, loop * \properties \em resource the file name, stream address, or the class name in angle brackets * \properties \em _cut set if this producer is a "cut" producer * \properties \em mlt_mix stores the data for a "mix" producer * \properties \em _cut_parent holds a reference to the cut's parent producer * \properties \em ignore_points Set this to temporarily disable the in and out points. * \properties \em use_clone holds a reference to a clone's producer, as created by mlt_producer_optimise * \properties \em _clone is the index of the clone in the list of clones stored on the clone's producer * \properties \em _clones is the number of clones of the producer, as created by mlt_producer_optimise * \properties \em _clone.{N} holds a reference to the N'th clone of the producer, as created by mlt_producer_optimise * \properties \em meta.* holds metadata - there is a loose taxonomy to be defined * \properties \em set.* holds properties to set on a frame produced * \envvar \em MLT_DEFAULT_PRODUCER_LENGTH - the default duration of the producer in frames, defaults to 15000. * Most producers will set the producer length to something appropriate * like the real duration of an audio or video clip. However, some other things * like still images and generators do not have an intrinsic length besides one * or infinity. Those producers tend to not override the default length and one * expect the app or user to set the length. The default value of 15000 was chosen * to provide something useful - not too long or short and convenient to simply * set an out point without necessarily needing to extend the length. * \todo define the media metadata taxonomy */ struct mlt_producer_s { /** A producer is a service. */ struct mlt_service_s parent; /** Get a frame of data (virtual function). * * \param mlt_producer a producer * \param mlt_frame_ptr a frame pointer by reference * \param int an index * \return true if there was an error */ int (*get_frame)(mlt_producer, mlt_frame_ptr, int); /** Seek to a specified position (virtual function). * * \param mlt_producer a producer * \param position set the "play head" position of the producer * \return false */ int (*seek)(mlt_producer, mlt_position); /** Set the in and out points. * * \param mlt_producer a producer * \param mlt_position the relative starting time; a negative value is the same as 0 * \param mlt_position the relative ending time; a negative value is the same as length - 1 * \return false */ int (*set_in_and_out)(mlt_producer, mlt_position, mlt_position); /** the destructor virtual function */ mlt_destructor close; void *close_object; /**< the object supplied to the close virtual function */ void *local; /**< \private instance object */ void *child; /**< \private the object of a subclass */ }; /* * Public final methods */ #define MLT_PRODUCER_SERVICE(producer) (&(producer)->parent) #define MLT_PRODUCER_PROPERTIES(producer) MLT_SERVICE_PROPERTIES(MLT_PRODUCER_SERVICE(producer)) MLT_EXPORT int mlt_producer_init(mlt_producer self, void *child); MLT_EXPORT mlt_producer mlt_producer_new(mlt_profile); MLT_EXPORT mlt_service mlt_producer_service(mlt_producer self); MLT_EXPORT mlt_properties mlt_producer_properties(mlt_producer self); MLT_EXPORT int mlt_producer_seek(mlt_producer self, mlt_position position); MLT_EXPORT int mlt_producer_seek_time(mlt_producer self, const char *time); MLT_EXPORT mlt_position mlt_producer_position(mlt_producer self); MLT_EXPORT mlt_position mlt_producer_frame(mlt_producer self); MLT_EXPORT char *mlt_producer_frame_time(mlt_producer self, mlt_time_format); MLT_EXPORT int mlt_producer_set_speed(mlt_producer self, double speed); MLT_EXPORT double mlt_producer_get_speed(mlt_producer self); MLT_EXPORT double mlt_producer_get_fps(mlt_producer self); MLT_EXPORT int mlt_producer_set_in_and_out(mlt_producer self, mlt_position in, mlt_position out); MLT_EXPORT int mlt_producer_clear(mlt_producer self); MLT_EXPORT mlt_position mlt_producer_get_in(mlt_producer self); MLT_EXPORT mlt_position mlt_producer_get_out(mlt_producer self); MLT_EXPORT mlt_position mlt_producer_get_playtime(mlt_producer self); MLT_EXPORT mlt_position mlt_producer_get_length(mlt_producer self); MLT_EXPORT char *mlt_producer_get_length_time(mlt_producer self, mlt_time_format); MLT_EXPORT void mlt_producer_prepare_next(mlt_producer self); MLT_EXPORT int mlt_producer_attach(mlt_producer self, mlt_filter filter); MLT_EXPORT int mlt_producer_detach(mlt_producer self, mlt_filter filter); MLT_EXPORT mlt_filter mlt_producer_filter(mlt_producer self, int index); MLT_EXPORT mlt_producer mlt_producer_cut(mlt_producer self, int in, int out); MLT_EXPORT int mlt_producer_is_cut(mlt_producer self); MLT_EXPORT int mlt_producer_is_mix(mlt_producer self); MLT_EXPORT int mlt_producer_is_blank(mlt_producer self); MLT_EXPORT mlt_producer mlt_producer_cut_parent(mlt_producer self); MLT_EXPORT int mlt_producer_optimise(mlt_producer self); MLT_EXPORT void mlt_producer_close(mlt_producer self); MLT_EXPORT int64_t mlt_producer_get_creation_time(mlt_producer self); MLT_EXPORT void mlt_producer_set_creation_time(mlt_producer self, int64_t creation_time); MLT_EXPORT int mlt_producer_probe(mlt_producer self); #endif mlt-7.38.0/src/framework/mlt_profile.c000664 000000 000000 00000042455 15172202314 017640 0ustar00rootroot000000 000000 /** * \file mlt_profile.c * \brief video output definition * \see mlt_profile_s * * Copyright (C) 2007-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt.h" #include #include #include #include #include /** the default subdirectory of the datadir for holding profiles */ #define PROFILES_DIR "/profiles/" /** Load a profile from the system folder. * * The environment variable MLT_PROFILES_PATH overrides the default \p PROFILES_DIR. * * \private \memberof mlt_profile_s * \param name the name of a profile settings file located in the standard location or * the full path name to a profile settings file * \return a profile or NULL on error */ static mlt_profile mlt_profile_select(const char *name) { char *filename = NULL; const char *prefix = getenv("MLT_PROFILES_PATH"); mlt_properties properties = NULL; mlt_profile profile = NULL; struct stat file_info; // Try to load from file specification if (name && !mlt_stat(name, &file_info)) properties = mlt_properties_load(name); if (properties && mlt_properties_get_int(properties, "width")) { filename = calloc(1, strlen(name) + 1); } // Load from $datadir/mlt/profiles else if (!prefix && mlt_environment("MLT_DATA")) { prefix = mlt_environment("MLT_DATA"); filename = calloc(1, strlen(prefix) + strlen(PROFILES_DIR) + strlen(name) + 1); strcpy(filename, prefix); strcat(filename, PROFILES_DIR); } // Use environment variable instead else if (prefix) { filename = calloc(1, strlen(prefix) + strlen(name) + 2); strcpy(filename, prefix); if (filename[strlen(filename) - 1] != '/') filename[strlen(filename)] = '/'; } else { mlt_properties_close(properties); return profile; } // Finish loading strcat(filename, name); profile = mlt_profile_load_file(filename); // Cleanup mlt_properties_close(properties); free(filename); return profile; } /** Construct a profile. * * This will never return NULL as it uses the dv_pal settings as hard-coded fallback default. * * \public \memberof mlt_profile_s * \param name the name of a profile settings file located in the standard location or * the full path name to a profile settings file * \return a profile */ mlt_profile mlt_profile_init(const char *name) { mlt_profile profile = NULL; // Explicit profile by name gets priority over environment variables if (name) profile = mlt_profile_select(name); // Try to load by environment variable if (profile == NULL) { // MLT_PROFILE is preferred environment variable if (getenv("MLT_PROFILE")) profile = mlt_profile_select(getenv("MLT_PROFILE")); // MLT_NORMALISATION backwards compatibility else if (getenv("MLT_NORMALISATION") && strcmp(getenv("MLT_NORMALISATION"), "PAL")) profile = mlt_profile_select("dv_ntsc"); else profile = mlt_profile_select("dv_pal"); // If still not loaded (no profile files), default to PAL if (profile == NULL) { profile = calloc(1, sizeof(struct mlt_profile_s)); if (profile) { mlt_environment_set("MLT_PROFILE", "dv_pal"); profile->description = strdup("PAL 4:3 DV or DVD"); profile->frame_rate_num = 25; profile->frame_rate_den = 1; profile->width = 720; profile->height = 576; profile->progressive = 0; profile->sample_aspect_num = 16; profile->sample_aspect_den = 15; profile->display_aspect_num = 4; profile->display_aspect_den = 3; profile->colorspace = 601; } } } return profile; } static void set_mlt_normalization(const char *profile_name) { if (profile_name) { if (strstr(profile_name, "_ntsc") || strstr(profile_name, "_60") || strstr(profile_name, "_5994") || strstr(profile_name, "_2997") || strstr(profile_name, "_30")) { mlt_environment_set("MLT_NORMALISATION", "NTSC"); } else if (strstr(profile_name, "_pal") || strstr(profile_name, "_50") || strstr(profile_name, "_25")) { mlt_environment_set("MLT_NORMALISATION", "PAL"); } } } /** Load a profile from specific file. * * \public \memberof mlt_profile_s * \param file the full path name to a properties file * \return a profile or NULL on error */ mlt_profile mlt_profile_load_file(const char *file) { mlt_profile profile = NULL; // Load the profile as properties mlt_properties properties = mlt_properties_load(file); if (properties) { // Simple check if the profile is valid if (mlt_properties_get_int(properties, "width")) { profile = mlt_profile_load_properties(properties); // Set MLT_PROFILE to basename char *filename = strdup(file); mlt_environment_set("MLT_PROFILE", basename(filename)); set_mlt_normalization(basename(filename)); free(filename); } mlt_properties_close(properties); } // Set MLT_NORMALISATION to appease legacy modules char *profile_name = mlt_environment("MLT_PROFILE"); set_mlt_normalization(profile_name); return profile; } /** Load a profile from a properties object. * * \public \memberof mlt_profile_s * \param properties a properties list * \return a profile or NULL if out of memory */ mlt_profile mlt_profile_load_properties(mlt_properties properties) { mlt_profile profile = calloc(1, sizeof(struct mlt_profile_s)); if (profile) { if (mlt_properties_get(properties, "name")) mlt_environment_set("MLT_PROFILE", mlt_properties_get(properties, "name")); if (mlt_properties_get(properties, "description")) profile->description = strdup(mlt_properties_get(properties, "description")); profile->frame_rate_num = mlt_properties_get_int(properties, "frame_rate_num"); profile->frame_rate_den = mlt_properties_get_int(properties, "frame_rate_den"); profile->width = mlt_properties_get_int(properties, "width"); profile->height = mlt_properties_get_int(properties, "height"); profile->progressive = mlt_properties_get_int(properties, "progressive"); profile->sample_aspect_num = mlt_properties_get_int(properties, "sample_aspect_num"); profile->sample_aspect_den = mlt_properties_get_int(properties, "sample_aspect_den"); profile->display_aspect_num = mlt_properties_get_int(properties, "display_aspect_num"); profile->display_aspect_den = mlt_properties_get_int(properties, "display_aspect_den"); const char *colorspace_str = mlt_properties_get(properties, "colorspace"); profile->colorspace = mlt_image_colorspace_id(colorspace_str); } return profile; } /** Load an anonymous profile from string. * * \public \memberof mlt_profile_s * \param string a newline-delimited list of properties as name=value pairs * \return a profile or NULL if out of memory */ mlt_profile mlt_profile_load_string(const char *string) { mlt_properties properties = mlt_properties_new(); mlt_profile profile = NULL; if (properties) { const char *p = string; while (p) { if (strcmp(p, "") && p[0] != '#') mlt_properties_parse(properties, p); p = strchr(p, '\n'); if (p) p++; } profile = mlt_profile_load_properties(properties); mlt_properties_close(properties); } return profile; } /** Get the video frame rate as a floating point value. * * \public \memberof mlt_profile_s * @param profile a profile * @return the frame rate */ double mlt_profile_fps(mlt_profile profile) { if (profile) return (double) profile->frame_rate_num / profile->frame_rate_den; else return 0; } /** Get the sample aspect ratio as a floating point value. * * \public \memberof mlt_profile_s * \param profile a profile * \return the pixel aspect ratio */ double mlt_profile_sar(mlt_profile profile) { if (profile) return (double) profile->sample_aspect_num / profile->sample_aspect_den; else return 0; } /** Get the display aspect ratio as floating point value. * * \public \memberof mlt_profile_s * \param profile a profile * \return the image aspect ratio */ double mlt_profile_dar(mlt_profile profile) { if (profile) return (double) profile->display_aspect_num / profile->display_aspect_den; else return 0; } /** Free up the global profile resources. * * \public \memberof mlt_profile_s * \param profile a profile */ void mlt_profile_close(mlt_profile profile) { if (profile) { free(profile->description); profile->description = NULL; free(profile); profile = NULL; } } /** Make a copy of a profile. * * \public \memberof mlt_profile_s * \param profile the profile to clone * \return a copy of the profile */ mlt_profile mlt_profile_clone(mlt_profile profile) { mlt_profile clone = NULL; if (profile) { clone = calloc(1, sizeof(*profile)); if (clone) { memcpy(clone, profile, sizeof(*profile)); clone->description = strdup(profile->description); } } return clone; } /** Get the list of profiles. * * The caller MUST close the returned properties object! * Each entry in the list is keyed on its name, and its value is another * properties object that contains the attributes of the profile. * \public \memberof mlt_profile_s * \return a list of profiles */ mlt_properties mlt_profile_list() { char *filename = NULL; const char *prefix = getenv("MLT_PROFILES_PATH"); mlt_properties properties = mlt_properties_new(); mlt_properties dir = mlt_properties_new(); int sort = 1; const char *wildcard = NULL; int i; // Load from $datadir/mlt/profiles if no env var if (prefix == NULL) { prefix = mlt_environment("MLT_DATA"); if (prefix) { filename = calloc(1, strlen(prefix) + strlen(PROFILES_DIR) + 1); strcpy(filename, prefix); strcat(filename, PROFILES_DIR); } else { filename = calloc(1, strlen(PROFILES_DIR) + 1); strcpy(filename, PROFILES_DIR); } prefix = filename; } mlt_properties_dir_list(dir, prefix, wildcard, sort); for (i = 0; i < mlt_properties_count(dir); i++) { char *filename = mlt_properties_get_value(dir, i); char *profile_name = basename(filename); if (profile_name[0] != '.' && strcmp(profile_name, "Makefile") && profile_name[strlen(profile_name) - 1] != '~') { mlt_properties profile = mlt_properties_load(filename); if (profile) { mlt_properties_set_data(properties, profile_name, profile, 0, (mlt_destructor) mlt_properties_close, NULL); } } } mlt_properties_close(dir); free(filename); return properties; } /** Update the profile using the attributes of a producer. * * Use this to make an "auto-profile." Typically, you need to re-open the producer * after you use this because some producers (e.g. avformat) adjust their framerate * to that of the profile used when you created it. * \public \memberof mlt_profile_s * \param profile the profile to update * \param producer the producer to inspect */ void mlt_profile_from_producer(mlt_profile profile, mlt_producer producer) { mlt_producer_probe(producer); mlt_properties p = MLT_PRODUCER_PROPERTIES(producer); // mlt_properties_dump(p, stderr); if (mlt_properties_get_int(p, "meta.media.frame_rate_den") && mlt_properties_get_int(p, "meta.media.sample_aspect_den")) { profile->width = mlt_properties_get_int(p, "meta.media.width"); profile->height = mlt_properties_get_int(p, "meta.media.height"); profile->progressive = mlt_properties_get_int(p, "meta.media.progressive"); if (1000 > mlt_properties_get_double(p, "meta.media.frame_rate_num") / mlt_properties_get_double(p, "meta.media.frame_rate_den")) { profile->frame_rate_num = mlt_properties_get_int(p, "meta.media.frame_rate_num"); profile->frame_rate_den = mlt_properties_get_int(p, "meta.media.frame_rate_den"); } else { profile->frame_rate_num = 60; profile->frame_rate_den = 1; } // AVCHD is mis-reported as double frame rate. if (profile->progressive == 0 && (profile->frame_rate_num / profile->frame_rate_den == 50 || profile->frame_rate_num / profile->frame_rate_den == 59)) profile->frame_rate_num /= 2; profile->sample_aspect_num = mlt_properties_get_int(p, "meta.media.sample_aspect_num"); profile->sample_aspect_den = mlt_properties_get_int(p, "meta.media.sample_aspect_den"); profile->colorspace = mlt_properties_get_int(p, "meta.media.colorspace"); int n = profile->display_aspect_num = profile->sample_aspect_num * profile->width; int m = profile->display_aspect_den = profile->sample_aspect_den * profile->height; int gcd, remainder; while (n) { remainder = m % n; m = n; n = remainder; } gcd = m; profile->display_aspect_num /= gcd; profile->display_aspect_den /= gcd; free(profile->description); profile->description = strdup("automatic"); profile->is_explicit = 0; } } /** Get the lumas subdirectory to use for the aspect ratio. * * \public \memberof mlt_profile_s * \param profile the profile to update * \return the name of a subdirectory generated by the lumas module */ char *mlt_profile_lumas_dir(mlt_profile profile) { if (profile) { if (profile->display_aspect_num == profile->display_aspect_den) { mlt_environment_set("MLT_LUMAS_DIR", "square"); // [-inf, 0.8) => 9:16 } else if (mlt_profile_dar(profile) < 0.8) { mlt_environment_set("MLT_LUMAS_DIR", "9_16"); // 0.5625 // [0.8, 1.3) => 1:1 } else if (mlt_profile_dar(profile) < 1.3) { mlt_environment_set("MLT_LUMAS_DIR", "square"); // [1.3, 1.5) => 4:3 } else if (mlt_profile_dar(profile) < 1.5) { if (profile->frame_rate_num == 30000 && profile->frame_rate_den == 1001) mlt_environment_set("MLT_LUMAS_DIR", "NTSC"); // 1.5 else mlt_environment_set("MLT_LUMAS_DIR", "PAL"); // 1.25 // [1.5, inf] => 16:9 } else { mlt_environment_set("MLT_LUMAS_DIR", "16_9"); } } else { mlt_environment_set("MLT_LUMAS_DIR", "16_9"); } return mlt_environment("MLT_LUMAS_DIR"); } /** Get the width scale factor. * * \public \memberof mlt_profile_s * \param profile the profile to reference * \param width the number of pixels the consumer requested * \return the scale factor for the width */ double mlt_profile_scale_width(mlt_profile profile, int width) { return (profile && width && profile->width) ? (double) width / profile->width : 1.0; } /** Get the height scale factor. * * \public \memberof mlt_profile_s * \param profile the profile to reference * \param height the number of pixels the consumer requested * \return the scale factor for the height */ double mlt_profile_scale_height(mlt_profile profile, int height) { return (profile && profile->width) ? (double) height / profile->height : 1.0; } /** Check if the profile values are acceptable. * * This checks that the resolution, aspect ratio, and frame rate are not * negative, zero, or too high. * \public \memberof mlt_profile_s * \param profile the profile to reference * \return true if the profile is valid */ int mlt_profile_is_valid(mlt_profile profile) { int valid = profile && (profile->width > 0 && profile->height > 0 && profile->sample_aspect_num > 0 && profile->sample_aspect_den > 0 && profile->frame_rate_num > 0 && profile->frame_rate_den > 0); if (!valid) { mlt_log_error(NULL, "[profile] a value is negative or zero\n"); } else if (profile->width * profile->height > 16384 * 16384) { valid = 0; mlt_log_error(NULL, "[profile] resolution exceeds the maximum 16384x16384\n"); } return valid; } mlt-7.38.0/src/framework/mlt_profile.h000664 000000 000000 00000006335 15172202314 017642 0ustar00rootroot000000 000000 /** * \file mlt_profile.h * \brief video output definition * \see mlt_profile_s * * Copyright (C) 2007-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PROFILE_H #define MLT_PROFILE_H #include "mlt_export.h" #include "mlt_types.h" /** \brief Profile class * * \envvar \em MLT_PROFILES_PATH overrides the default full path to the profile preset files, defaults to \p MLT_DATA/profiles * \envvar \em MLT_PROFILE the profile preset to use, defaults to "dv_pal" */ struct mlt_profile_s { char *description; /**< a brief description suitable as a label in UI menu */ int frame_rate_num; /**< the numerator of the video frame rate */ int frame_rate_den; /**< the denominator of the video frame rate */ int width; /**< the horizontal resolution of the video */ int height; /**< the vertical resolution of the video */ int progressive; /**< a flag to indicate if the video is progressive scan, interlace if not set */ int sample_aspect_num; /**< the numerator of the pixel aspect ratio */ int sample_aspect_den; /**< the denominator of the pixel aspect ratio */ int display_aspect_num; /**< the numerator of the image aspect ratio in case it can not be simply derived (e.g. ITU-R 601) */ int display_aspect_den; /**< the denominator of the image aspect ratio in case it can not be simply derived (e.g. ITU-R 601) */ int colorspace; /**< the Y'CbCr colorspace standard: see mlt_colorspace */ int is_explicit; /**< used internally to indicate if the profile was requested explicitly or computed or defaulted */ }; MLT_EXPORT mlt_profile mlt_profile_init(const char *name); MLT_EXPORT mlt_profile mlt_profile_load_file(const char *file); MLT_EXPORT mlt_profile mlt_profile_load_properties(mlt_properties properties); MLT_EXPORT mlt_profile mlt_profile_load_string(const char *string); MLT_EXPORT double mlt_profile_fps(mlt_profile profile); MLT_EXPORT double mlt_profile_sar(mlt_profile profile); MLT_EXPORT double mlt_profile_dar(mlt_profile profile); MLT_EXPORT void mlt_profile_close(mlt_profile profile); MLT_EXPORT mlt_profile mlt_profile_clone(mlt_profile profile); MLT_EXPORT mlt_properties mlt_profile_list(); MLT_EXPORT void mlt_profile_from_producer(mlt_profile profile, mlt_producer producer); MLT_EXPORT char *mlt_profile_lumas_dir(mlt_profile profile); MLT_EXPORT double mlt_profile_scale_width(mlt_profile profile, int width); MLT_EXPORT double mlt_profile_scale_height(mlt_profile profile, int height); MLT_EXPORT int mlt_profile_is_valid(mlt_profile profile); #endif mlt-7.38.0/src/framework/mlt_properties.c000664 000000 000000 00000272434 15172202314 020376 0ustar00rootroot000000 000000 /** * \file mlt_properties.c * \brief Properties class definition * \see mlt_properties_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // For strtod_l #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "mlt_properties.h" #include "mlt_deque.h" #include "mlt_factory.h" #include "mlt_log.h" #include "mlt_property.h" #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_LOAD_LINE_SIZE 4096 /** \brief private implementation of the property list */ typedef struct { int *buckets; unsigned int capacity; unsigned int mask; unsigned int used; char **name; mlt_property *value; int count; int size; mlt_properties mirror; int ref_count; pthread_mutex_t mutex; mlt_locale_t locale; mlt_properties *children_properties; char **children_names; int children_count; } property_list; /* Memory leak checks */ //#define _MLT_PROPERTY_CHECKS_ 2 #ifdef _MLT_PROPERTY_CHECKS_ static int properties_created = 0; static int properties_destroyed = 0; #endif /** Initialize a properties object that was already allocated. * * This does allocate its ::property_list, and it adds a reference count. * \public \memberof mlt_properties_s * \param self the properties structure to initialize * \param child an opaque pointer to a subclass object * \return true if failed */ int mlt_properties_init(mlt_properties self, void *child) { if (self != NULL) { #ifdef _MLT_PROPERTY_CHECKS_ // Increment number of properties created properties_created++; #endif // NULL all methods memset(self, 0, sizeof(struct mlt_properties_s)); // Assign the child of the object self->child = child; // Allocate the local structure self->local = calloc(1, sizeof(property_list)); ((property_list *) self->local)->buckets = NULL; ((property_list *) self->local)->capacity = 0; ((property_list *) self->local)->mask = 0; ((property_list *) self->local)->used = 0; // Increment the ref count ((property_list *) self->local)->ref_count = 1; pthread_mutex_init(&((property_list *) self->local)->mutex, NULL); } // Check that initialisation was successful return self != NULL && self->local == NULL; } /** Create a properties object. * * This allocates the properties structure and calls mlt_properties_init() on it. * Free the properties object with mlt_properties_close(). * \public \memberof mlt_properties_s * \return a new properties object */ mlt_properties mlt_properties_new() { // Construct a standalone properties object mlt_properties self = calloc(1, sizeof(struct mlt_properties_s)); // Initialise self mlt_properties_init(self, NULL); // Return the pointer return self; } /** Set the numeric locale used for string/double conversions. * * \public \memberof mlt_properties_s * \param self a properties list * \param locale the locale name * \return true if error */ int mlt_properties_set_lcnumeric(mlt_properties self, const char *locale) { int error = 0; #if !defined(_WIN32) if (self && locale) { property_list *list = self->local; #if defined(__GLIBC__) || defined(__APPLE__) if (list->locale) freelocale(list->locale); list->locale = newlocale(LC_NUMERIC_MASK, locale, NULL); #else free(list->locale); list->locale = strdup(locale); #endif } else error = 1; #endif // _WIN32 return error; } /** Get the numeric locale for this properties object. * * Do not free the result. * \public \memberof mlt_properties_s * \param self a properties list * \return the locale name if this properties has a specific locale it is using, NULL otherwise */ const char *mlt_properties_get_lcnumeric(mlt_properties self) { if (!self) return NULL; const char *result = NULL; #if !defined(_WIN32) property_list *list = self->local; if (list->locale) { #if defined(__APPLE__) result = querylocale(LC_NUMERIC_MASK, list->locale); #elif defined(__GLIBC__) result = list->locale->__names[LC_NUMERIC]; #else result = list->locale; #endif #if defined(_WIN32) if (result) { // Convert the string from ANSI code page to UTF-8. mlt_properties_set_string(self, "_lcnumeric_in", result); mlt_properties_to_utf8(self, "_lcnumeric_in", "_lcnumeric_out"); result = mlt_properties_get(self, "_lcnumeric_out"); } #endif } #endif // _WIN32 return result; } static int load_properties(mlt_properties self, const char *filename) { // Open the file FILE *file = mlt_fopen(filename, "r"); // Load contents of file if (file != NULL) { // Temp string char temp[MAX_LOAD_LINE_SIZE]; char last[MAX_LOAD_LINE_SIZE] = ""; // Read each string from the file while (fgets(temp, MAX_LOAD_LINE_SIZE, file)) { // Chomp the new line character from the string int x = strlen(temp) - 1; if (temp[x] == '\n' || temp[x] == '\r') temp[x] = '\0'; // Check if the line starts with a . if (temp[0] == '.') { char temp2[MAX_LOAD_LINE_SIZE]; strcpy(temp2, last); strncat(temp2, temp, sizeof(temp2) - strlen(temp2) - 1); temp2[sizeof(temp2) - 1] = '\0'; strcpy(temp, temp2); } else if (strchr(temp, '=')) { strcpy(last, temp); *(strchr(last, '=')) = '\0'; } // Parse and set the property if (strcmp(temp, "") && temp[0] != '#') mlt_properties_parse(self, temp); } // Close the file fclose(file); } else { mlt_log_warning(NULL, "Failed to open properties file %s for parsing!\n", filename); } return file ? 0 : errno; } /** Create a properties object by reading a .properties text file. * * Free the properties object with mlt_properties_close(). * \deprecated Please start using mlt_properties_parse_yaml(). * \public \memberof mlt_properties_s * \param filename the absolute file name * \return a new properties object */ mlt_properties mlt_properties_load(const char *filename) { // Construct a standalone properties object mlt_properties self = mlt_properties_new(); if (self != NULL) load_properties(self, filename); // Return the pointer return self; } /** Set properties from a preset. * * Presets are typically installed to $prefix/share/mlt/presets/{type}/{service}/[{profile}/]{name}. * For example, "/usr/share/mlt/presets/consumer/avformat/dv_ntsc_wide/DVD" * could be an encoding preset for a widescreen NTSC DVD Video. * Do not specify the type and service in the preset name parameter; these are * inferred automatically from the service to which you are applying the preset. * Using the example above and assuming you are calling this function on the * avformat consumer, the name passed to the function should simply be DVD. * Note that the profile portion of the path is optional, but a profile-specific * preset with the same name as a more generic one is given a higher priority. * \todo Look in a user-specific location - somewhere in the home directory. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the name of a preset in a well-known location or the explicit path * \return true if error */ int mlt_properties_preset(mlt_properties self, const char *name) { struct stat stat_buff; // validate input if (!(self && name && strlen(name))) return 1; // See if name is an explicit file if (!mlt_stat(name, &stat_buff)) { return load_properties(self, name); } else { // Look for profile-specific preset before a generic one. const char *data = mlt_environment("MLT_PRESETS_PATH"); const char *type = mlt_properties_get(self, "mlt_type"); const char *service = mlt_properties_get(self, "mlt_service"); const char *profile = mlt_environment("MLT_PROFILE"); int error = 0; if (data && type && service) { char *path = malloc(5 + strlen(name) + strlen(data) + strlen(type) + strlen(service) + (profile ? strlen(profile) : 0)); sprintf(path, "%s/%s/%s/%s/%s", data, type, service, profile, name); if (load_properties(self, path)) { sprintf(path, "%s/%s/%s/%s", data, type, service, name); error = load_properties(self, path); } free(path); } else { error = 1; } return error; } } /** Generate a hash key. * * \private \memberof mlt_properties_s * \param name a string * \return an unsigned integer */ static inline unsigned int generate_hash(const char *name) { // Initial magic constant 5381. This has been proven to result in fewer collisions. unsigned int hash = 5381; int c; while ((c = (unsigned char) *name++)) { hash = ((hash << 5) + hash) + (unsigned int) c; } return hash; } /** Insert an index into the hash bucket array using linear probing. * * This internal helper handles collisions by searching for the next * available slot in the bucket array. * \private \memberof mlt_properties_s * \param buckets pointer to the array of hash buckets * \param mask bitmask for fast modulo operation (capacity - 1) * \param name the string key to hash * \param index the index value to store in the bucket */ static void hash_insert(int *buckets, unsigned int mask, const char *name, int index) { unsigned int hash = generate_hash(name); unsigned int i = hash & mask; while (buckets[i] != -1) { i = (i + 1) & mask; } buckets[i] = index; } /** Check and trigger a rehash of the properties bucket list. * * Maintains a load factor below 0.75 to ensure O(1) performance. * If memory allocation fails during expansion, the hash table is * gracefully disabled, falling back to safe linear searches. * \private \memberof mlt_properties_s * \param list the property_list object to be resized or initialized */ static void check_rehash(property_list *list) { //(used * 4 >= capacity * 3) avoids slow floating-point division. if (list->buckets == NULL || (list->used * 4 >= list->capacity * 3)) { // Start with 64 to reduce early reallocations for medium-sized objects unsigned int new_capacity = list->capacity == 0 ? 64 : list->capacity * 2; unsigned int new_mask = new_capacity - 1; int *new_buckets = malloc(new_capacity * sizeof(int)); if (new_buckets) { // Initialize buckets to -1 (empty) for (unsigned int i = 0; i < new_capacity; i++) new_buckets[i] = -1; if (list->buckets) { // Rehash existing items into the new bucket array for (int i = 0; i < list->count; i++) { if (list->name[i]) hash_insert(new_buckets, new_mask, list->name[i], i); } free(list->buckets); } list->buckets = new_buckets; list->capacity = new_capacity; list->mask = new_mask; } else if (list->buckets) { // Memory failure: invalidate hash table and fallback to linear search free(list->buckets); list->buckets = NULL; list->capacity = 0; list->mask = 0; list->used = 0; } } } /** Copy a serializable property to a properties list that is mirroring this one. * * Special case - when a container (such as loader) is protecting another * producer, we need to ensure that properties are passed through to the * real producer. * \private \memberof mlt_properties_s * \param self a properties list * \param name the name of the property to copy */ static inline void mlt_properties_do_mirror(mlt_properties self, const char *name) { if (!self) return; property_list *list = self->local; if (list->mirror != NULL) { char *value = mlt_properties_get(self, name); if (value != NULL) mlt_properties_set_string(list->mirror, name, value); } } /** Increment the reference count. * * \public \memberof mlt_properties_s * \param self a properties list * \return the new reference count */ int mlt_properties_inc_ref(mlt_properties self) { int result = 0; if (self != NULL) { property_list *list = self->local; pthread_mutex_lock(&list->mutex); result = ++list->ref_count; pthread_mutex_unlock(&list->mutex); } return result; } /** Decrement the reference count. * * \public \memberof mlt_properties_s * \param self a properties list * \return the new reference count */ int mlt_properties_dec_ref(mlt_properties self) { int result = 0; if (self != NULL) { property_list *list = self->local; pthread_mutex_lock(&list->mutex); result = --list->ref_count; pthread_mutex_unlock(&list->mutex); } return result; } /** Get the reference count. * * \public \memberof mlt_properties_s * \param self a properties list * \return the current reference count */ int mlt_properties_ref_count(mlt_properties self) { if (self != NULL) { property_list *list = self->local; return list->ref_count; } return 0; } /** Set a properties list to be a mirror copy of another. * * Note that this does not copy all existing properties. Rather, you must * call this before setting the properties that you wish to copy. * \public \memberof mlt_properties_s * \param that the properties which will receive copies of the properties as they are set. * \param self the properties to mirror */ void mlt_properties_mirror(mlt_properties self, mlt_properties that) { if (!self) return; property_list *list = self->local; list->mirror = that; } /** Copy all serializable properties to another properties list. * * \public \memberof mlt_properties_s * \param self The properties to copy to * \param that The properties to copy from * \return true if error */ int mlt_properties_inherit(mlt_properties self, mlt_properties that) { if (!self || !that) return 1; // Set "properties" first so preset overrides are reliable. char *value = mlt_properties_get(that, "properties"); if (value) mlt_properties_set_string(self, "properties", value); mlt_properties_lock(that); int count = mlt_properties_count(that); int i = 0; for (i = 0; i < count; i++) { char *name = mlt_properties_get_name(that, i); if (name && strcmp("properties", name)) { char *value = mlt_properties_get_value(that, i); if (value != NULL) { mlt_properties_set_string(self, name, value); } else { mlt_properties that_child_props = mlt_properties_get_properties_at(that, i); if (that_child_props != NULL) { mlt_properties child_props = mlt_properties_new(); mlt_properties_set_properties(self, name, child_props); mlt_properties_inherit(child_props, that_child_props); } } } } mlt_properties_unlock(that); return 0; } /** Copy all serializable properties that match a prefix to another properties object * * \public \memberof mlt_properties_s * \param self the properties to copy to * \param that The properties to copy from * \param prefix the property names to match (required) * \return true if error */ int mlt_properties_copy(mlt_properties self, mlt_properties that, const char *prefix) { if (!self || !that) return 1; int count = mlt_properties_count(that); int length = strlen(prefix); int i = 0; for (i = 0; i < count; i++) { char *name = mlt_properties_get_name(that, i); if (!strncmp(name, prefix, length)) { char *value = mlt_properties_get_value(that, i); if (value != NULL) mlt_properties_set_string(self, name, value); } } return 0; } /** Pass all serializable properties that match a prefix to another properties object * * \warning The prefix is stripped from the name when it is set on the \p self properties list! * For example a property named "foo.bar" will match prefix "foo.", but the property * will be named simply "bar" on the receiving properties object. * \public \memberof mlt_properties_s * \param self the properties to copy to * \param that The properties to copy from * \param prefix the property names to match (required) * \return true if error */ int mlt_properties_pass(mlt_properties self, mlt_properties that, const char *prefix) { if (!self || !that) return 1; int count = mlt_properties_count(that); int length = strlen(prefix); int i = 0; for (i = 0; i < count; i++) { char *name = mlt_properties_get_name(that, i); if (!strncmp(name, prefix, length)) { char *value = mlt_properties_get_value(that, i); if (value != NULL) mlt_properties_set_string(self, name + length, value); } } return 0; } /** Locate a property by name. * * \private \memberof mlt_properties_s * \param self a properties list * \param name the property to lookup by name * \return the property or NULL for failure */ static inline mlt_property mlt_properties_find(mlt_properties self, const char *name) { if (!self || !name) return NULL; property_list *list = self->local; mlt_property value = NULL; mlt_properties_lock(self); // If the hash table is active, it is the authoritative source for O(1) lookups. if (list->buckets) { unsigned int hash = generate_hash(name); unsigned int i = hash & list->mask; // Linear probing: traverse the bucket array until an empty slot (-1) is found while (list->buckets[i] != -1) { int index = list->buckets[i]; if (list->name[index] && !strcmp(list->name[index], name)) { value = list->value[index]; mlt_properties_unlock(self); return value; } i = (i + 1) & list->mask; } // If we reached an empty slot (-1) in the hash table, the property definitely does not exist. mlt_properties_unlock(self); return NULL; } // Fallback Linear Search for (int i = list->count - 1; i >= 0; i--) { if (list->name[i] && !strcmp(list->name[i], name)) { value = list->value[i]; break; } } mlt_properties_unlock(self); return value; } /** Add a new property. * * \private \memberof mlt_properties_s * \param self a properties list * \param name the name of the new property * \return the new property or NULL for failure */ static mlt_property mlt_properties_add(mlt_properties self, const char *name) { if (!self || !name) return NULL; property_list *list = self->local; mlt_property result; mlt_properties_lock(self); // Doubling capacity instead of fixed increments reduces realloc overhead. if (list->count == list->size) { int new_size = list->size == 0 ? 16 : list->size * 2; // Use temporary pointers to prevent memory leaks on realloc failure char **new_names = realloc(list->name, new_size * sizeof(const char *)); mlt_property *new_values = realloc(list->value, new_size * sizeof(mlt_property)); if (new_names && new_values) { list->name = new_names; list->value = new_values; list->size = new_size; } else { // Memory allocation failed; rollback or handle error if (new_names) list->name = new_names; if (new_values) list->value = new_values; mlt_properties_unlock(self); return NULL; } } // Initialize new property entry list->name[list->count] = strdup(name); list->value[list->count] = mlt_property_init(); // Update internal hash table for O(1) lookups check_rehash(list); if (list->buckets) { hash_insert(list->buckets, list->mask, name, list->count); list->used++; } result = list->value[list->count++]; mlt_properties_unlock(self); return result; } /** Fetch a property by name and add one if not found. * * \private \memberof mlt_properties_s * \param self a properties list * \param name the property to lookup or add * \return the property */ static mlt_property mlt_properties_fetch(mlt_properties self, const char *name) { // Try to find an existing property first mlt_property property = mlt_properties_find(self, name); // If it wasn't found, create one if (property == NULL) property = mlt_properties_add(self, name); // Return the property return property; } static void fire_property_changed(mlt_properties self, const char *name) { mlt_events_fire(self, "property-changed", mlt_event_data_from_string(name)); } /** Copy a property to another properties list. * * \public \memberof mlt_properties_s * \author Zach * \param self the properties to copy to * \param that the properties to copy from * \param name the name of the property to copy */ void mlt_properties_pass_property(mlt_properties self, mlt_properties that, const char *name) { // Make sure the source property isn't null. mlt_property that_prop = mlt_properties_find(that, name); if (that_prop == NULL) return; mlt_property_pass(mlt_properties_fetch(self, name), that_prop); fire_property_changed(self, name); } /** Copy all properties specified in a comma-separated list to another properties list. * * White space is also a delimiter. * \public \memberof mlt_properties_s * \author Zach * \param self the properties to copy to * \param that the properties to copy from * \param list a delimited list of property names * \return true if error */ int mlt_properties_pass_list(mlt_properties self, mlt_properties that, const char *list) { if (!self || !that || !list) return 1; char *props = strdup(list); char *ptr = props; const char *delim = " ,\t\n"; // Any combination of spaces, commas, tabs, and newlines int count, done = 0; while (!done) { count = strcspn(ptr, delim); if (ptr[count] == '\0') done = 1; else ptr[count] = '\0'; // Make it a real string mlt_properties_pass_property(self, that, ptr); ptr += count + 1; if (!done) ptr += strspn(ptr, delim); } free(props); return 0; } static int is_valid_expression(mlt_properties self, const char *value) { int result = *value != '\0'; char id[255]; while (*value != '\0') { size_t length = strcspn(value, "+-*/"); // Get the identifier length = MIN(sizeof(id) - 1, length); strncpy(id, value, length); id[length] = '\0'; value += length; // Determine if the property exists if (!isdigit(id[0]) && !mlt_properties_get(self, id)) { result = 0; break; } // Get the next op if (value[0] != '\0') ++value; } return result; } /** Set a property to a string. * * The property name "properties" is reserved to load the preset in \p value. * When the value begins with '@' then it is interpreted as a very simple math * expression containing only the +, -, *, and / operators. * The event "property-changed" is fired after the property has been set. * * This makes a copy of the string value you supply. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the property's new value * \return true if error */ int mlt_properties_set(mlt_properties self, const char *name, const char *value) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property == NULL) { mlt_log(NULL, MLT_LOG_FATAL, "Whoops - %s not found (should never occur)\n", name); } else if (value == NULL) { error = mlt_property_set_string(property, value); mlt_properties_do_mirror(self, name); } else if (value[0] == '@' && is_valid_expression(self, &value[1])) { double total = 0; double current = 0; char id[255]; char op = '+'; value++; while (*value != '\0') { size_t length = strcspn(value, "+-*/"); // Get the identifier length = MIN(sizeof(id) - 1, length); strncpy(id, value, length); id[length] = '\0'; value += length; // Determine the value if (isdigit(id[0])) { #if defined(__GLIBC__) || defined(__APPLE__) || HAVE_STRTOD_L && !defined(__OpenBSD__) property_list *list = self->local; if (list->locale) current = strtod_l(id, NULL, list->locale); else #endif current = strtod(id, NULL); } else { current = mlt_properties_get_double(self, id); } // Apply the operation switch (op) { case '+': total += current; break; case '-': total -= current; break; case '*': total *= current; break; case '/': total = total / current; break; } // Get the next op op = *value != '\0' ? *value++ : ' '; } error = mlt_property_set_double(property, total); mlt_properties_do_mirror(self, name); } else { error = mlt_property_set_string(property, value); mlt_properties_do_mirror(self, name); if (!strcmp(name, "properties")) mlt_properties_preset(self, value); } fire_property_changed(self, name); return error; } /** Set or default a property to a string. * * This makes a copy of the string value you supply. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the string value to set or NULL to use the default * \param def the default string if value is NULL * \return true if error */ int mlt_properties_set_or_default(mlt_properties self, const char *name, const char *value, const char *def) { return mlt_properties_set(self, name, value == NULL ? def : value); } /** Set a property to a string. * * Unlike mlt_properties_set() this function does not attempt to interpret an expression. * The property name "properties" is reserved to load the preset in \p value. * The event "property-changed" is fired after the property has been set. * * This makes a copy of the string value you supply. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the property's new value * \return true if error */ int mlt_properties_set_string(mlt_properties self, const char *name, const char *value) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property == NULL) { mlt_log(NULL, MLT_LOG_FATAL, "Whoops - %s not found (should never occur)\n", name); } else if (value == NULL) { error = mlt_property_set_string(property, value); mlt_properties_do_mirror(self, name); } else { error = mlt_property_set_string(property, value); mlt_properties_do_mirror(self, name); if (!strcmp(name, "properties")) mlt_properties_preset(self, value); } fire_property_changed(self, name); return error; } /** Get a string value by name. * * Do not free the returned string. It's lifetime is controlled by the property * and this properties object. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return the property's string value or NULL if it does not exist */ char *mlt_properties_get(mlt_properties self, const char *name) { char *result = NULL; mlt_property value = mlt_properties_find(self, name); if (value) { property_list *list = self->local; result = mlt_property_get_string_l(value, list->locale); } return result; } /** Get a property name by index. * * Do not free the returned string. * \public \memberof mlt_properties_s * \param self a properties list * \param index the numeric index of the property * \return the name of the property or NULL if index is out of range */ char *mlt_properties_get_name(mlt_properties self, int index) { if (!self) return NULL; property_list *list = self->local; if (index >= 0 && index < list->count) return list->name[index]; return NULL; } /** Get a property's string value by index (with time format). * * Do not free the returned string. * \public \memberof mlt_properties_s * \param self a properties list * \param index the numeric index of the property * \param time_format the time format to use for animation * \return the property value as a string or NULL if the index is out of range */ char *mlt_properties_get_value_tf(mlt_properties self, int index, mlt_time_format time_format) { if (!self) return NULL; property_list *list = self->local; if (index >= 0 && index < list->count) return mlt_property_get_string_l_tf(list->value[index], list->locale, time_format); return NULL; } /** Get a property's string value by index. * * Do not free the returned string. * \public \memberof mlt_properties_s * \param self a properties list * \param index the numeric index of the property * \return the property value as a string or NULL if the index is out of range */ char *mlt_properties_get_value(mlt_properties self, int index) { return mlt_properties_get_value_tf(self, index, mlt_time_frames); } /** Get a data value by index. * * Do not free the returned pointer if you supplied a destructor function when you * set this property. * \public \memberof mlt_properties_s * \param self a properties list * \param index the numeric index of the property * \param[out] size the size of the binary data in bytes or NULL if the index is out of range */ void *mlt_properties_get_data_at(mlt_properties self, int index, int *size) { if (!self) return NULL; property_list *list = self->local; if (index >= 0 && index < list->count) return mlt_property_get_data(list->value[index], size); return NULL; } /** Return the number of items in the list. * * \public \memberof mlt_properties_s * \param self a properties list * \return the number of property objects or -1 if error */ int mlt_properties_count(mlt_properties self) { if (!self) return -1; property_list *list = self->local; return list->count; } /** Set a value by parsing a name=value string. * * \public \memberof mlt_properties_s * \param self a properties list * \param namevalue a string containing name and value delimited by '=' * \return true if there was an error */ int mlt_properties_parse(mlt_properties self, const char *namevalue) { if (!self) return 1; char *name = strdup(namevalue); char *value = NULL; int error = 0; char *ptr = strchr(name, '='); if (ptr) { *(ptr++) = '\0'; if (*ptr != '\"') { value = strdup(ptr); } else { ptr++; value = strdup(ptr); if (value != NULL && value[strlen(value) - 1] == '\"') value[strlen(value) - 1] = '\0'; } } else { value = strdup(""); } error = mlt_properties_set(self, name, value); free(name); free(value); return error; } /** Get an integer associated to the name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return The integer value, 0 if not found (which may also be a legitimate value) */ int mlt_properties_get_int(mlt_properties self, const char *name) { int result = 0; mlt_property value = mlt_properties_find(self, name); if (value) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; result = mlt_property_get_int(value, fps, list->locale); } return result; } /** Set a property to an integer value. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the integer * \return true if error */ int mlt_properties_set_int(mlt_properties self, const char *name, int value) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { error = mlt_property_set_int(property, value); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a 64-bit integer associated to the name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return the integer value, 0 if not found (which may also be a legitimate value) */ int64_t mlt_properties_get_int64(mlt_properties self, const char *name) { mlt_property value = mlt_properties_find(self, name); return value == NULL ? 0 : mlt_property_get_int64(value); } /** Set a property to a 64-bit integer value. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the integer * \return true if error */ int mlt_properties_set_int64(mlt_properties self, const char *name, int64_t value) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { error = mlt_property_set_int64(property, value); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a floating point value associated to the name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return the floating point, 0 if not found (which may also be a legitimate value) */ double mlt_properties_get_double(mlt_properties self, const char *name) { double result = 0; mlt_property value = mlt_properties_find(self, name); if (value) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; result = mlt_property_get_double(value, fps, list->locale); } return result; } /** Set a property to a floating point value. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the floating point value * \return true if error */ int mlt_properties_set_double(mlt_properties self, const char *name, double value) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { error = mlt_property_set_double(property, value); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a position value associated to the name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return the position, 0 if not found (which may also be a legitimate value) */ mlt_position mlt_properties_get_position(mlt_properties self, const char *name) { mlt_position result = 0; mlt_property value = mlt_properties_find(self, name); if (value) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; result = mlt_property_get_position(value, fps, list->locale); } return result; } /** Set a property to a position value. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the position * \return true if error */ int mlt_properties_set_position(mlt_properties self, const char *name, mlt_position value) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { error = mlt_property_set_position(property, value); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a binary data value associated to the name. * * Do not free the returned pointer if you supplied a destructor function * when you set this property. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param[out] length The size of the binary data in bytes, if available (often it is not, you should know) */ void *mlt_properties_get_data(mlt_properties self, const char *name, int *length) { mlt_property value = mlt_properties_find(self, name); return value == NULL ? NULL : mlt_property_get_data(value, length); } /** Store binary data as a property. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value an opaque pointer to binary data * \param length the size of the binary data in bytes (optional) * \param destroy a function to deallocate the binary data when the property is closed (optional) * \param serialise a function that can serialize the binary data as text (optional) * \return true if error */ int mlt_properties_set_data(mlt_properties self, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) error = mlt_property_set_data(property, value, length, destroy, serialise); fire_property_changed(self, name); return error; } /** Rename a property. * * \public \memberof mlt_properties_s * \param self a properties list * \param source the property to rename * \param dest the new name * \return true if the name is already in use */ int mlt_properties_rename(mlt_properties self, const char *source, const char *dest) { mlt_property value = mlt_properties_find(self, dest); if (value == NULL) { property_list *list = self->local; int found = 0; mlt_properties_lock(self); for (int i = 0; i < list->count; i++) { // Check if the property name matches the source. if (list->name[i] && !strcmp(list->name[i], source)) { free(list->name[i]); list->name[i] = strdup(dest); found = 1; break; } } // Rebuild the hash table if a property was renamed. if (found && list->buckets) { for (unsigned int j = 0; j < list->capacity; j++) list->buckets[j] = -1; for (int j = 0; j < list->count; j++) { if (list->name[j]) { hash_insert(list->buckets, list->mask, list->name[j], j); } } } mlt_properties_unlock(self); } return value != NULL; } /** Dump the properties to a file handle. * * \public \memberof mlt_properties_s * \param self a properties list * \param output a file handle */ void mlt_properties_dump(mlt_properties self, FILE *output) { if (!self || !output) return; property_list *list = self->local; int i = 0; for (i = 0; i < list->count; i++) if (mlt_properties_get(self, list->name[i]) != NULL) fprintf(output, "%s=%s\n", list->name[i], mlt_properties_get(self, list->name[i])); } /** Output the properties to a file handle. * * This version includes reference counts and does not put each property on a new line. * \public \memberof mlt_properties_s * \param self a properties pointer * \param title a string to preface the output * \param output a file handle */ void mlt_properties_debug(mlt_properties self, const char *title, FILE *output) { if (!self || !output) return; if (output == NULL) output = stderr; fprintf(output, "%s: ", title); if (self != NULL) { property_list *list = self->local; int i = 0; fprintf(output, "[ ref=%d", list->ref_count); for (i = 0; i < list->count; i++) if (mlt_properties_get(self, list->name[i]) != NULL) fprintf(output, ", %s=%s", list->name[i], mlt_properties_get(self, list->name[i])); else if (mlt_properties_get_data(self, list->name[i], NULL) != NULL) fprintf(output, ", %s=%p", list->name[i], mlt_properties_get_data(self, list->name[i], NULL)); else fprintf(output, ", %s=%p", list->name[i], mlt_properties_get_properties(self, list->name[i])); fprintf(output, " ]"); } fprintf(output, "\n"); } /** Save the properties to a file by name. * * This uses the dump format - one line per property. * \public \memberof mlt_properties_s * \param self a properties list * \param filename the name of a file to create or overwrite * \return true if there was an error */ int mlt_properties_save(mlt_properties self, const char *filename) { int error = 1; if (!self || !filename) return error; FILE *f = mlt_fopen(filename, "w"); if (f != NULL) { mlt_properties_dump(self, f); fclose(f); error = 0; } return error; } /* This is a very basic cross platform fnmatch replacement - it will fail in * many cases, but for the basic *.XXX and YYY*.XXX, it will work ok. */ /** Test whether a filename or pathname matches a shell-style pattern. * * \private \memberof mlt_properties_s * \param wild a string containing a wildcard pattern * \param file the name of a file to test against * \return true if the file name matches the wildcard pattern */ static int mlt_fnmatch(const char *wild, const char *file) { int f = 0; int w = 0; while (f < strlen(file) && w < strlen(wild)) { if (wild[w] == '*') { w++; if (w == strlen(wild)) f = strlen(file); while (f != strlen(file) && tolower(file[f]) != tolower(wild[w])) f++; } else if (wild[w] == '?' || tolower(file[f]) == tolower(wild[w])) { f++; w++; } else if (wild[0] == '*') { w = 0; } else { return 0; } } return strlen(file) == f && strlen(wild) == w; } /** Compare the string or serialized value of two properties. * * \private \memberof mlt_properties_s * \param self a property * \param that a property * \return < 0 if \p self less than \p that, 0 if equal, or > 0 if \p self is greater than \p that */ static int mlt_compare(const void *self, const void *that) { return strcmp(mlt_property_get_string(*(const mlt_property *) self), mlt_property_get_string(*(const mlt_property *) that)); } /** Get the contents of a directory. * * Obtains an optionally sorted list of the files found in a directory with a specific wild card. * Entries in the list have a numeric name (running from 0 to count - 1). Only values change * position if sort is enabled. Designed to be posix compatible (linux, os/x, mingw etc). * \public \memberof mlt_properties_s * \param self a properties list * \param dirname the name of the directory * \param pattern a wildcard pattern to filter the directory listing * \param sort Do you want to sort the directory listing? * \return the number of items in the directory listing */ int mlt_properties_dir_list(mlt_properties self, const char *dirname, const char *pattern, int sort) { DIR *dir = opendir(dirname); if (dir) { char key[20]; struct dirent *de = readdir(dir); char fullname[1024]; while (de != NULL) { sprintf(key, "%d", mlt_properties_count(self)); snprintf(fullname, 1024, "%s/%s", dirname, de->d_name); if (pattern == NULL) mlt_properties_set_string(self, key, fullname); else if (de->d_name[0] != '.' && mlt_fnmatch(pattern, de->d_name)) mlt_properties_set_string(self, key, fullname); de = readdir(dir); } closedir(dir); } if (sort && mlt_properties_count(self)) { property_list *list = self->local; mlt_properties_lock(self); qsort(list->value, mlt_properties_count(self), sizeof(mlt_property), mlt_compare); mlt_properties_unlock(self); } return mlt_properties_count(self); } /** Close a properties object. * * Deallocates the properties object and everything it contains. * \public \memberof mlt_properties_s * \param self a properties object */ void mlt_properties_close(mlt_properties self) { if (self != NULL && mlt_properties_dec_ref(self) <= 0) { if (self->close != NULL) { self->close(self->close_object); } else { property_list *list = self->local; int index = 0; #if _MLT_PROPERTY_CHECKS_ == 1 // Show debug info mlt_properties_debug(self, "Closing", stderr); #endif #ifdef _MLT_PROPERTY_CHECKS_ // Increment destroyed count properties_destroyed++; // Show current stats - these should match when the app is closed mlt_log(NULL, MLT_LOG_DEBUG, "Created %d, destroyed %d\n", properties_created, properties_destroyed); #endif // Clean up names and values for (index = list->count - 1; index >= 0; index--) { mlt_property_close(list->value[index]); free(list->name[index]); } #if defined(__GLIBC__) || defined(__APPLE__) // Cleanup locale if (list->locale) freelocale(list->locale); #else free(list->locale); #endif // Clear up the list pthread_mutex_destroy(&list->mutex); free(list->name); free(list->value); if (list->buckets) { free(list->buckets); list->buckets = NULL; } free(list); // Free self now if self has no child if (self->child == NULL) free(self); } } } /** Determine if the properties list is really just a sequence or ordered list. * * \public \memberof mlt_properties_s * \param properties a properties list * \return true if all of the property names are numeric (a sequence) */ int mlt_properties_is_sequence(mlt_properties properties) { int i; int n = mlt_properties_count(properties); for (i = 0; i < n; i++) if (!isdigit(mlt_properties_get_name(properties, i)[0])) return 0; return 1; } /** \brief YAML Tiny Parser context structure * * YAML is a nifty text format popular in the Ruby world as a cleaner, * less verbose alternative to XML. See this Wikipedia topic for an overview: * http://en.wikipedia.org/wiki/YAML * The YAML specification is at: * http://yaml.org/ * YAML::Tiny is a Perl module that specifies a subset of YAML that we are * using here (for the same reasons): * http://search.cpan.org/~adamk/YAML-Tiny-1.25/lib/YAML/Tiny.pm * \private */ struct yaml_parser_context { mlt_deque stack; unsigned int level; int index; mlt_deque index_stack; char block; char *block_name; unsigned int block_indent; }; typedef struct yaml_parser_context *yaml_parser; /** Remove spaces from the left side of a string. * * \param s the string to trim * \return the number of characters removed */ static unsigned int ltrim(char **s) { unsigned int i = 0; char *c = *s; int n = strlen(c); for (i = 0; i < n && *c == ' '; i++, c++) ; *s = c; return i; } /** Remove spaces from the right side of a string. * * \param s the string to trim * \return the number of characters removed */ static unsigned int rtrim(char *s) { int n = strlen(s); int i; for (i = n; i > 0 && s[i - 1] == ' '; --i) s[i - 1] = 0; return n - i; } /** Parse a line of YAML Tiny. * * Adds a property if needed. * \private \memberof yaml_parser_context * \param context a YAML Tiny Parser context * \param namevalue a line of YAML Tiny * \return true if there was an error */ static int parse_yaml(yaml_parser context, const char *namevalue) { char *name_ = strdup(namevalue); char *name = name_; char *value = NULL; int error = 0; char *ptr = strchr(name, ':'); unsigned int indent = ltrim(&name); mlt_properties properties = mlt_deque_peek_back(context->stack); // If name is quoted if (ptr && name[0] == '\"') { // Look for ending quote. ptr = strchr(ptr + 1, '\"'); if (ptr) // Locate delimiter after ending quote. ptr = strchr(ptr + 1, ':'); else // No ending quote! ptr = strchr(name, ':'); } // Ascending one more levels in the tree if (indent < context->level) { unsigned int i; unsigned int n = (context->level - indent) / 2; for (i = 0; i < n; i++) { mlt_deque_pop_back(context->stack); context->index = mlt_deque_pop_back_int(context->index_stack); } properties = mlt_deque_peek_back(context->stack); context->level = indent; } // Descending a level in the tree else if (indent > context->level && context->block == 0) { context->level = indent; } // If there is a colon that is not part of a block if (ptr && (indent == context->level)) { // Reset block processing if (context->block_name) { free(context->block_name); context->block_name = NULL; context->block = 0; } // Terminate the name and setup the value pointer *(ptr++) = 0; // Trim comment char *comment = strchr(ptr, '#'); if (comment) { const char *quote = strchr(ptr, '\"'); if (!quote || quote > comment) *comment = 0; } // Trim leading and trailing spaces from bare value ltrim(&ptr); rtrim(ptr); // No value means a child if (strcmp(ptr, "") == 0) { mlt_properties child = mlt_properties_new(); mlt_properties_set_lcnumeric(child, mlt_properties_get_lcnumeric(properties)); mlt_properties_set_data(properties, name, child, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_deque_push_back(context->stack, child); mlt_deque_push_back_int(context->index_stack, context->index); context->index = 0; free(name_); return error; } // A dash indicates a sequence item if (name[0] == '-') { mlt_properties child = mlt_properties_new(); char key[20]; mlt_properties_set_lcnumeric(child, mlt_properties_get_lcnumeric(properties)); snprintf(key, sizeof(key), "%d", context->index++); mlt_properties_set_data(properties, key, child, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_deque_push_back(context->stack, child); mlt_deque_push_back_int(context->index_stack, context->index); name++; context->level += ltrim(&name) + 1; properties = child; } // Value is quoted if (*ptr == '\"') { ptr++; value = strdup(ptr); if (value && value[strlen(value) - 1] == '\"') value[strlen(value) - 1] = 0; } // Value is folded or unfolded block else if (*ptr == '|' || *ptr == '>') { context->block = *ptr; context->block_name = strdup(name); context->block_indent = 0; value = strdup(""); } // Bare value else { value = strdup(ptr); } } // A list of scalars else if (name[0] == '-') { // Reset block processing if (context->block_name) { free(context->block_name); context->block_name = NULL; context->block = 0; } char key[20]; snprintf(key, sizeof(key), "%d", context->index++); ptr = name + 1; // Trim comment char *comment = strchr(ptr, '#'); if (comment) { const char *quote = strchr(ptr, '\"'); if (!quote || quote > comment) *comment = 0; } // Trim leading and trailing spaces from bare value ltrim(&ptr); rtrim(ptr); // Value is quoted if (*ptr == '\"') { ptr++; value = strdup(ptr); if (value && value[strlen(value) - 1] == '\"') value[strlen(value) - 1] = 0; } // Value is folded or unfolded block else if (*ptr == '|' || *ptr == '>') { context->block = *ptr; context->block_name = strdup(key); context->block_indent = 0; value = strdup(""); } // Bare value else { value = strdup(ptr); } free(name_); name = name_ = strdup(key); } // Non-folded block else if (context->block == '|') { if (context->block_indent == 0) context->block_indent = indent; if (indent > context->block_indent) name = &name_[context->block_indent]; rtrim(name); char *old_value = mlt_properties_get(properties, context->block_name); value = calloc(1, strlen(old_value) + strlen(name) + 2); strcpy(value, old_value); if (strcmp(old_value, "")) strcat(value, "\n"); strcat(value, name); name = context->block_name; } // Folded block else if (context->block == '>') { ltrim(&name); rtrim(name); char *old_value = mlt_properties_get(properties, context->block_name); // Blank line (prepended with spaces) is new line if (strcmp(name, "") == 0) { value = calloc(1, strlen(old_value) + 2); strcat(value, old_value); strcat(value, "\n"); } // Concatenate with space else { value = calloc(1, strlen(old_value) + strlen(name) + 2); strcat(value, old_value); if (strcmp(old_value, "") && old_value[strlen(old_value) - 1] != '\n') strcat(value, " "); strcat(value, name); } name = context->block_name; } else { value = strdup(""); } // Remove quotes around the name. if (name && name[0] == '"' && name[strlen(name) - 1] == '"') { name++; name[strlen(name) - 1] = '\0'; } error = mlt_properties_set_string(properties, name, value); if (!strcmp(name, "LC_NUMERIC")) mlt_properties_set_lcnumeric(properties, value); free(name_); free(value); return error; } /** Parse a YAML Tiny file by name. * * \public \memberof mlt_properties_s * \param filename the name of a text file containing YAML Tiny * \return a new properties list */ mlt_properties mlt_properties_parse_yaml(const char *filename) { // Construct a standalone properties object mlt_properties self = mlt_properties_new(); if (self) { // Open the file FILE *file = mlt_fopen(filename, "r"); // Load contents of file if (file) { // Temp string char temp[MAX_LOAD_LINE_SIZE]; char *ptemp = &temp[0]; // Default to LC_NUMERIC = C mlt_properties_set_lcnumeric(self, "C"); // Parser context yaml_parser context = calloc(1, sizeof(struct yaml_parser_context)); context->stack = mlt_deque_init(); context->index_stack = mlt_deque_init(); mlt_deque_push_back(context->stack, self); mlt_deque_push_back_int(context->index_stack, 0); // Read each string from the file while (fgets(temp, MAX_LOAD_LINE_SIZE, file)) { // Check for end-of-stream if (strncmp(ptemp, "...", 3) == 0) break; // Chomp any trailing line ending characters, but only if present // (last line may lack one). int len = strlen(temp); while (len > 0 && (temp[len - 1] == '\n' || temp[len - 1] == '\r')) { temp[len - 1] = '\0'; len--; } // Skip blank lines, comment lines, and document separator if (strcmp(ptemp, "") && ptemp[0] != '#' && strncmp(ptemp, "---", 3) && strncmp(ptemp, "%YAML", 5) && strncmp(ptemp, "% YAML", 6)) parse_yaml(context, temp); } // Close the file fclose(file); mlt_deque_close(context->stack); mlt_deque_close(context->index_stack); free(context->block_name); free(context); } else { mlt_log_warning(NULL, "Failed to open file %s for yaml parsing!\n", filename); } } // Return the pointer return self; } /* * YAML Tiny Serializer */ /** How many bytes to grow at a time */ #define STRBUF_GROWTH (1024) /** \brief Private to mlt_properties_s, a self-growing buffer for building strings * \private */ struct strbuf_s { size_t size; char *string; }; typedef struct strbuf_s *strbuf; /** Create a new string buffer * * \private \memberof strbuf_s * \return a new string buffer */ static strbuf strbuf_new() { strbuf buffer = calloc(1, sizeof(struct strbuf_s)); buffer->size = STRBUF_GROWTH; buffer->string = calloc(1, buffer->size); return buffer; } /** Destroy a string buffer * * \private \memberof strbuf_s * \param buffer the string buffer to close */ static void strbuf_close(strbuf buffer) { // We do not free buffer->string; strbuf user must save that pointer // and free it. free(buffer); } /** Format a string into a string buffer * * A variable number of arguments follows the format string - one for each * format specifier. * \private \memberof strbuf_s * \param buffer the string buffer to write into * \param format a string that contains text and formatting instructions * \return the formatted string */ static char *strbuf_printf(strbuf buffer, const char *format, ...) { while (buffer->string) { va_list ap; va_start(ap, format); size_t len = strlen(buffer->string); size_t remain = buffer->size - len - 1; int need = vsnprintf(buffer->string + len, remain, format, ap); va_end(ap); if (need > -1 && need < remain) break; buffer->string[len] = 0; buffer->size += need + STRBUF_GROWTH; buffer->string = realloc(buffer->string, buffer->size); } return buffer->string; } /** Indent a line of YAML Tiny. * * \private \memberof strbuf_s * \param output a string buffer * \param indent the number of spaces to indent */ static inline void indent_yaml(strbuf output, int indent) { int j; for (j = 0; j < indent; j++) strbuf_printf(output, " "); } static void strbuf_escape(strbuf output, const char *value, char c) { char *v = strdup(value); char *s = v; char *found = strchr(s, c); while (found) { *found = '\0'; strbuf_printf(output, "%s\\%c", s, c); s = found + 1; found = strchr(s, c); } strbuf_printf(output, "%s", s); free(v); } static inline int has_reserved_char(const char *string) { return strchr(string, ':') || strchr(string, '[') || strchr(string, ']') || strchr(string, '{') || strchr(string, '}') || strchr(string, '\'') || strchr(string, '#') || strchr(string, ',') || strchr(string, '*'); } static inline int is_numeric_identifier(const char *name, const char *s) { if (strcmp(name, "identifier")) return 0; while (*s) { if (!isdigit((unsigned char) *s)) return 0; s++; } return 1; } static inline int is_yaml_keyword(const char *s) { return !strcmp(s, "null") || !strcmp(s, "~"); } static inline int is_scientific_notation(const char *s) { char *end = NULL; if (!s || !*s || !strpbrk(s, "eE")) return 0; strtod(s, &end); return end != s && *end == '\0'; } /** Write a scientific notation value as fixed-point decimal for kwalify compatibility. * * \private \memberof strbuf_s * \param output a string buffer * \param value a string in scientific notation (e.g. "1e-08") */ static void strbuf_write_fixed_point(strbuf output, const char *value) { double d; const char *ep = strpbrk(value, "eE"); int exp = atoi(ep + 1); int prec = (exp < 0 ? -exp : 0) + 7; char buf[64]; #if defined(__GLIBC__) || defined(__APPLE__) \ || (defined(__FreeBSD_version) && __FreeBSD_version >= 900506) mlt_locale_t c_locale = newlocale(LC_NUMERIC_MASK, "C", NULL); mlt_locale_t orig_locale = c_locale ? uselocale(c_locale) : (mlt_locale_t) 0; d = strtod(value, NULL); snprintf(buf, sizeof(buf), "%.*f", prec, d); if (c_locale) { uselocale(orig_locale); freelocale(c_locale); } #else char *orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); d = strtod(value, NULL); snprintf(buf, sizeof(buf), "%.*f", prec, d); if (orig_localename) { setlocale(LC_NUMERIC, orig_localename); free(orig_localename); } #endif // Strip trailing zeros after decimal point if (strchr(buf, '.')) { char *end = buf + strlen(buf) - 1; while (*end == '0') *end-- = '\0'; if (*end == '.') *end = '\0'; } strbuf_printf(output, "%s\n", buf); } /** Convert a line string into a YAML block literal. * * \private \memberof strbuf_s * \param output a string buffer * \param value the string to format as a block literal * \param indent the number of spaces to indent */ static void output_yaml_block_literal(strbuf output, const char *value, int indent) { char *v = strdup(value); char *sol = v; char *eol = strchr(sol, '\n'); while (eol) { indent_yaml(output, indent); *eol = '\0'; strbuf_printf(output, "%s\n", sol); sol = eol + 1; eol = strchr(sol, '\n'); } indent_yaml(output, indent); strbuf_printf(output, "%s\n", sol); free(v); } /** Recursively serialize a properties list into a string buffer as YAML Tiny. * * \private \memberof mlt_properties_s * \param self a properties list * \param output a string buffer to hold the serialized YAML Tiny * \param indent the number of spaces to indent (for recursion, initialize to 0) * \param is_parent_sequence Is this properties list really just a sequence (for recursion, initialize to 0)? */ static void serialise_yaml(mlt_properties self, strbuf output, int indent, int is_parent_sequence) { property_list *list = self->local; int i = 0; int is_sequence = mlt_properties_is_sequence(self); for (i = 0; i < list->count; i++) { // This implementation assumes that all data elements are property lists. // Unfortunately, we do not have run time type identification. mlt_properties child = mlt_property_get_data(list->value[i], NULL); const char *name = list->name[i]; const char *value = mlt_properties_get(self, name); if (is_sequence) { // Ignore hidden/non-serialisable items if (name[0] != '_') { // Indicate a sequence item indent_yaml(output, indent); strbuf_printf(output, "- "); // If the value can be represented as a string if (value && strcmp(value, "")) { // Determine if this is an unfolded block literal if (strchr(value, '\n')) { strbuf_printf(output, "|\n"); output_yaml_block_literal(output, value, indent + strlen(name) + strlen("|")); } else if (is_scientific_notation(value)) { strbuf_write_fixed_point(output, value); } else if (has_reserved_char(value) || is_yaml_keyword(value) || is_numeric_identifier(name, value)) { strbuf_printf(output, "\""); strbuf_escape(output, value, '"'); strbuf_printf(output, "\"\n"); } else if (strchr(value, '"')) { strbuf_printf(output, "'%s'\n", value); } else { strbuf_printf(output, "%s\n", value); } } } // Recurse on child if (child && child->local) serialise_yaml(child, output, indent + 2, 1); } else { // Assume this is a normal map-oriented properties list // Ignore hidden/non-serialisable items // If the value can be represented as a string if (name[0] != '_' && value && strcmp(value, "")) { if (is_parent_sequence == 0) indent_yaml(output, indent); else is_parent_sequence = 0; // Output the name. if (has_reserved_char(name)) { strbuf_printf(output, "\""); strbuf_escape(output, name, '"'); strbuf_printf(output, "\": "); } else { strbuf_printf(output, "%s: ", name); } // Determine if this is an unfolded block literal if (strchr(value, '\n')) { strbuf_printf(output, "|\n"); output_yaml_block_literal(output, value, indent + strlen(name) + strlen(": ")); } else if (is_scientific_notation(value)) { strbuf_write_fixed_point(output, value); } else if (has_reserved_char(value) || is_yaml_keyword(value) || is_numeric_identifier(name, value)) { strbuf_printf(output, "\""); strbuf_escape(output, value, '"'); strbuf_printf(output, "\"\n"); } else if (strchr(value, '"')) { strbuf_printf(output, "'%s'\n", value); } else { strbuf_printf(output, "%s\n", value); } } // Output a child as a map item if (child && child->local) { indent_yaml(output, indent); if (has_reserved_char(name)) { strbuf_printf(output, "\""); strbuf_escape(output, name, '"'); strbuf_printf(output, "\":\n"); } else { strbuf_printf(output, "%s:\n", name); } // Recurse on child serialise_yaml(child, output, indent + 2, 0); } } } } /** Serialize a properties list as a string of YAML Tiny. * * The caller MUST free the returned string! * This operates on properties containing properties as a hierarchical data * structure. * \public \memberof mlt_properties_s * \param self a properties list * \return a string containing YAML Tiny that represents the properties list */ char *mlt_properties_serialise_yaml(mlt_properties self) { if (!self) return NULL; const char *lc_numeric = mlt_properties_get_lcnumeric(self); strbuf b = strbuf_new(); strbuf_printf(b, "---\n"); mlt_properties_set_lcnumeric(self, "C"); serialise_yaml(self, b, 0, 0); mlt_properties_set_lcnumeric(self, lc_numeric); strbuf_printf(b, "...\n"); char *ret = b->string; strbuf_close(b); return ret; } /** Protect a properties list against concurrent access. * * \public \memberof mlt_properties_s * \param self a properties list */ void mlt_properties_lock(mlt_properties self) { if (self) pthread_mutex_lock(&((property_list *) (self->local))->mutex); } /** End protecting a properties list against concurrent access. * * \public \memberof mlt_properties_s * \param self a properties list */ void mlt_properties_unlock(mlt_properties self) { if (self) pthread_mutex_unlock(&((property_list *) (self->local))->mutex); } /** Remove the value for a property. * * This initializes the value to zero and removes any string, data, or animation. * This is especially useful when you want to reset an animation. * \public \memberof mlt_properties_s * \param self a properties list * \param name the name of the property to clear */ void mlt_properties_clear(mlt_properties self, const char *name) { if (!self || !name) return; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property) mlt_property_clear(property); fire_property_changed(self, name); } /** Check if a property exists. * * This function is not a substitute for checking for NULL as a property could * be set to NULL. * * This function will return false immediately after a call to * mlt_properties_clear() or if the property has never been created. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the name of the property to query * \return true if the property exists */ int mlt_properties_exists(mlt_properties self, const char *name) { return !mlt_property_is_clear(mlt_properties_find(self, name)); } /** Get a time string associated to the name. * * Do not free the returned string. It's lifetime is controlled by the property. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param format the time format that you want * \return the property's time value or NULL if \p name does not exist or there is no profile */ char *mlt_properties_get_time(mlt_properties self, const char *name, mlt_time_format format) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); if (profile) { double fps = mlt_profile_fps(profile); mlt_property value = mlt_properties_find(self, name); property_list *list = self->local; return value == NULL ? NULL : mlt_property_get_time(value, format, fps, list->locale); } return NULL; } /** Convert a frame count to a time string. * * Do not free the returned string. It's lifetime is controlled by the property. * \public \memberof mlt_properties_s * \param self a properties list * \param frames the frame count to convert * \param format the time format that you want * \return the time string or NULL if error, e.g. there is no profile */ char *mlt_properties_frames_to_time(mlt_properties self, mlt_position frames, mlt_time_format format) { const char *name = "_mlt_properties_time"; mlt_properties_set_position(self, name, frames); return mlt_properties_get_time(self, name, format); } /** Convert a time string to a frame count. * * \public \memberof mlt_properties_s * \param self a properties list * \param time the time string to convert * \return a frame count or a negative value if error, e.g. there is no profile */ mlt_position mlt_properties_time_to_frames(mlt_properties self, const char *time) { const char *name = "_mlt_properties_time"; mlt_properties_set_string(self, name, time); return mlt_properties_get_position(self, name); } /** Set a property to an integer value by color. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param color the color * \return true if error */ int mlt_properties_set_color(mlt_properties self, const char *name, mlt_color color) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { error = mlt_property_set_color(property, color); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Convert a numeric property to a tuple of color components. * * If the property's string is red, green, blue, white, or black, then it * is converted to the corresponding opaque color tuple. Otherwise, the property * is fetched as an integer and then converted. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return a color structure */ mlt_color mlt_properties_get_color(mlt_properties self, const char *name) { mlt_property value = mlt_properties_find(self, name); mlt_color result = {0xff, 0xff, 0xff, 0xff}; if (value) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; result = mlt_property_get_color(value, fps, list->locale); } return result; } /** Set a property to an integer value by color at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the color * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return true if error */ extern int mlt_properties_anim_set_color(mlt_properties self, const char *name, mlt_color value, int position, int length, mlt_keyframe_type keyframe_type) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; error = mlt_property_anim_set_color(property, value, fps, list->locale, position, length, keyframe_type); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a color associated to the name at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return a color structure */ mlt_color mlt_properties_anim_get_color(mlt_properties self, const char *name, int position, int length) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; mlt_property value = mlt_properties_find(self, name); mlt_color color = {0xff, 0xff, 0xff, 0xff}; return value == NULL ? color : mlt_property_anim_get_color(value, fps, list->locale, position, length); } /** Get a string value by name at a frame position. * * Do not free the returned string. It's lifetime is controlled by the property * and this properties object. * Enclose a string property value in double quotation marks to prevent * mlt_properties_anim_get() from interpreting the string as animation. The * double-quotes are removed when retrieved through mlt_properties_anim_get(). * The same rule applies to string keyframe values: to protect a keyframed-string * value containing a semicolon or equal sign, enclose it in double-quotes. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the property's string value or NULL if it does not exist */ char *mlt_properties_anim_get(mlt_properties self, const char *name, int position, int length) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); mlt_property value = mlt_properties_find(self, name); property_list *list = self->local; return value == NULL ? NULL : mlt_property_anim_get_string(value, fps, list->locale, position, length); } /** Set a property to a string at a frame position. * * The event "property-changed" is fired after the property has been set. * * This makes a copy of the string value you supply. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the property's new value * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return true if error */ int mlt_properties_anim_set( mlt_properties self, const char *name, const char *value, int position, int length) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; error = mlt_property_anim_set_string(property, value, fps, list->locale, position, length); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get an integer associated to the name at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the integer value, 0 if not found (which may also be a legitimate value) */ int mlt_properties_anim_get_int(mlt_properties self, const char *name, int position, int length) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; mlt_property value = mlt_properties_find(self, name); return value == NULL ? 0 : mlt_property_anim_get_int(value, fps, list->locale, position, length); } /** Set a property to an integer value at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the integer * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return true if error */ int mlt_properties_anim_set_int(mlt_properties self, const char *name, int value, int position, int length, mlt_keyframe_type keyframe_type) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; error = mlt_property_anim_set_int(property, value, fps, list->locale, position, length, keyframe_type); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a real number associated to the name at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the real number, 0 if not found (which may also be a legitimate value) */ double mlt_properties_anim_get_double(mlt_properties self, const char *name, int position, int length) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; mlt_property value = mlt_properties_find(self, name); return value == NULL ? 0.0 : mlt_property_anim_get_double(value, fps, list->locale, position, length); } /** Set a property to a real number at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return true if error */ int mlt_properties_anim_set_double(mlt_properties self, const char *name, double value, int position, int length, mlt_keyframe_type keyframe_type) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; error = mlt_property_anim_set_double(property, value, fps, list->locale, position, length, keyframe_type); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get the animation associated to the name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return The animation object or NULL if the property has no animation */ mlt_animation mlt_properties_get_animation(mlt_properties self, const char *name) { mlt_property value = mlt_properties_find(self, name); return value == NULL ? NULL : mlt_property_get_animation(value); } /** Set a property to a rectangle value. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the rectangle * \return true if error */ extern int mlt_properties_set_rect(mlt_properties self, const char *name, mlt_rect value) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { error = mlt_property_set_rect(property, value); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a rectangle associated to the name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return the rectangle value, the rectangle fields will be DBL_MIN if not found */ extern mlt_rect mlt_properties_get_rect(mlt_properties self, const char *name) { property_list *list = self->local; mlt_property value = mlt_properties_find(self, name); mlt_rect rect = {DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN}; return value == NULL ? rect : mlt_property_get_rect(value, list->locale); } /** Set a property to a rectangle value at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the rectangle * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return true if error */ extern int mlt_properties_anim_set_rect(mlt_properties self, const char *name, mlt_rect value, int position, int length, mlt_keyframe_type keyframe_type) { int error = 1; if (!self || !name) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; error = mlt_property_anim_set_rect(property, value, fps, list->locale, position, length, keyframe_type); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a rectangle associated to the name at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the rectangle value, the rectangle fields will be DBL_MIN if not found */ extern mlt_rect mlt_properties_anim_get_rect(mlt_properties self, const char *name, int position, int length) { mlt_profile profile = mlt_properties_get_data(self, "_profile", NULL); double fps = mlt_profile_fps(profile); property_list *list = self->local; mlt_property value = mlt_properties_find(self, name); mlt_rect rect = {DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN}; return value == NULL ? rect : mlt_property_anim_get_rect(value, fps, list->locale, position, length); } #ifndef _WIN32 // See win32/win32.c for win32 implementation. /** Convert UTF-8 property to the locale-defined encoding. * * MLT uses UTF-8 for strings, but Windows cannot accept UTF-8 for a filename. * Windows uses code pages for the locale encoding. * \public \memberof mlt_properties_s * \param properties a properties list * \param name_from the property to read whose value is a UTF-8 string * \param name_to the name of the new property that will contain converted string * \return true if error */ int mlt_properties_from_utf8(mlt_properties properties, const char *name_from, const char *name_to) { // On non-Windows platforms, assume UTF-8 will always work and does not need conversion. // This function just becomes a pass-through operation. // This was largely chosen to prevent adding a libiconv dependency to the framework per policy. // However, for file open operations on Windows, especially when processing XML, a text codec // dependency is hardly avoidable. return mlt_properties_set_string(properties, name_to, mlt_properties_get(properties, name_from)); } #endif /** Set a property to a nested properties object. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param properties the properties list to nest into \p self with \p name * \return true if error */ int mlt_properties_set_properties(mlt_properties self, const char *name, mlt_properties properties) { int error = 1; if (!self || !name || !properties) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch(self, name); // Set it if not NULL if (property != NULL) { error = mlt_property_set_properties(property, properties); mlt_properties_do_mirror(self, name); } fire_property_changed(self, name); return error; } /** Get a nested properties object by name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return the nested properties list */ mlt_properties mlt_properties_get_properties(mlt_properties self, const char *name) { mlt_property value = mlt_properties_find(self, name); return value == NULL ? NULL : mlt_property_get_properties(value); } /** Get a nested properties object by index. * * \public \memberof mlt_properties_s * \param self a properties list * \param index the 0-based index value of the list item * \return the nested properties list */ mlt_properties mlt_properties_get_properties_at(mlt_properties self, int index) { if (!self) return NULL; property_list *list = self->local; if (index >= 0 && index < list->count) return mlt_property_get_properties(list->value[index]); return NULL; } /** Check if a property is animated. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return true if the property is animated */ int mlt_properties_is_anim(mlt_properties self, const char *name) { mlt_property property = mlt_properties_find(self, name); if (!property) return 0; property_list *list = self->local; pthread_mutex_lock(&list->mutex); int result = mlt_property_is_anim(property); pthread_mutex_unlock(&list->mutex); return result; } mlt-7.38.0/src/framework/mlt_properties.h000664 000000 000000 00000024156 15172202314 020377 0ustar00rootroot000000 000000 /** * \file mlt_properties.h * \brief Properties class declaration * \see mlt_properties_s * * Copyright (C) 2003-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PROPERTIES_H #define MLT_PROPERTIES_H #include "mlt_events.h" #include "mlt_export.h" #include "mlt_types.h" #include /** \brief Properties class * * Properties is a combination list/dictionary of name/::mlt_property pairs. * It is also a base class for many of the other MLT classes. * * \event \em property-changed a property's value changed; * the event data is a string for the name of the property */ struct mlt_properties_s { void *child; /**< \private the object of a subclass */ void *local; /**< \private instance object */ /** the destructor virtual function */ mlt_destructor close; void *close_object; /**< the object supplied to the close virtual function */ }; MLT_EXPORT int mlt_properties_init(mlt_properties, void *child); MLT_EXPORT mlt_properties mlt_properties_new(); MLT_EXPORT int mlt_properties_set_lcnumeric(mlt_properties, const char *locale); MLT_EXPORT const char *mlt_properties_get_lcnumeric(mlt_properties self); MLT_EXPORT mlt_properties mlt_properties_load(const char *file); MLT_EXPORT int mlt_properties_preset(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_inc_ref(mlt_properties self); MLT_EXPORT int mlt_properties_dec_ref(mlt_properties self); MLT_EXPORT int mlt_properties_ref_count(mlt_properties self); MLT_EXPORT void mlt_properties_mirror(mlt_properties self, mlt_properties that); MLT_EXPORT int mlt_properties_inherit(mlt_properties self, mlt_properties that); MLT_EXPORT int mlt_properties_copy(mlt_properties self, mlt_properties that, const char *prefix); MLT_EXPORT int mlt_properties_pass(mlt_properties self, mlt_properties that, const char *prefix); MLT_EXPORT void mlt_properties_pass_property(mlt_properties self, mlt_properties that, const char *name); MLT_EXPORT int mlt_properties_pass_list(mlt_properties self, mlt_properties that, const char *list); MLT_EXPORT int mlt_properties_set(mlt_properties self, const char *name, const char *value); MLT_EXPORT int mlt_properties_set_or_default(mlt_properties self, const char *name, const char *value, const char *def); MLT_EXPORT int mlt_properties_set_string(mlt_properties self, const char *name, const char *value); MLT_EXPORT int mlt_properties_parse(mlt_properties self, const char *namevalue); MLT_EXPORT char *mlt_properties_get(mlt_properties self, const char *name); MLT_EXPORT char *mlt_properties_get_name(mlt_properties self, int index); MLT_EXPORT char *mlt_properties_get_value_tf(mlt_properties self, int index, mlt_time_format); MLT_EXPORT char *mlt_properties_get_value(mlt_properties self, int index); MLT_EXPORT void *mlt_properties_get_data_at(mlt_properties self, int index, int *size); MLT_EXPORT int mlt_properties_get_int(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_set_int(mlt_properties self, const char *name, int value); MLT_EXPORT int64_t mlt_properties_get_int64(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_set_int64(mlt_properties self, const char *name, int64_t value); MLT_EXPORT double mlt_properties_get_double(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_set_double(mlt_properties self, const char *name, double value); MLT_EXPORT mlt_position mlt_properties_get_position(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_set_position(mlt_properties self, const char *name, mlt_position value); MLT_EXPORT int mlt_properties_set_data( mlt_properties self, const char *name, void *value, int length, mlt_destructor, mlt_serialiser); MLT_EXPORT void *mlt_properties_get_data(mlt_properties self, const char *name, int *length); MLT_EXPORT int mlt_properties_rename(mlt_properties self, const char *source, const char *dest); MLT_EXPORT int mlt_properties_count(mlt_properties self); MLT_EXPORT void mlt_properties_dump(mlt_properties self, FILE *output); MLT_EXPORT void mlt_properties_debug(mlt_properties self, const char *title, FILE *output); MLT_EXPORT int mlt_properties_save(mlt_properties, const char *); MLT_EXPORT int mlt_properties_dir_list(mlt_properties, const char *, const char *, int); MLT_EXPORT void mlt_properties_close(mlt_properties self); MLT_EXPORT int mlt_properties_is_sequence(mlt_properties self); MLT_EXPORT mlt_properties mlt_properties_parse_yaml(const char *file); MLT_EXPORT char *mlt_properties_serialise_yaml(mlt_properties self); MLT_EXPORT void mlt_properties_lock(mlt_properties self); MLT_EXPORT void mlt_properties_unlock(mlt_properties self); MLT_EXPORT void mlt_properties_clear(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_exists(mlt_properties self, const char *name); MLT_EXPORT char *mlt_properties_get_time(mlt_properties, const char *name, mlt_time_format); MLT_EXPORT char *mlt_properties_frames_to_time(mlt_properties, mlt_position, mlt_time_format); MLT_EXPORT mlt_position mlt_properties_time_to_frames(mlt_properties, const char *time); MLT_EXPORT int mlt_properties_set_color(mlt_properties, const char *name, mlt_color value); MLT_EXPORT mlt_color mlt_properties_get_color(mlt_properties, const char *name); MLT_EXPORT int mlt_properties_anim_set_color(mlt_properties self, const char *name, mlt_color value, int position, int length, mlt_keyframe_type keyframe_type); MLT_EXPORT mlt_color mlt_properties_anim_get_color(mlt_properties self, const char *name, int position, int length); MLT_EXPORT char *mlt_properties_anim_get(mlt_properties self, const char *name, int position, int length); MLT_EXPORT int mlt_properties_anim_set( mlt_properties self, const char *name, const char *value, int position, int length); MLT_EXPORT int mlt_properties_anim_get_int(mlt_properties self, const char *name, int position, int length); MLT_EXPORT int mlt_properties_anim_set_int(mlt_properties self, const char *name, int value, int position, int length, mlt_keyframe_type keyframe_type); MLT_EXPORT double mlt_properties_anim_get_double(mlt_properties self, const char *name, int position, int length); MLT_EXPORT int mlt_properties_anim_set_double(mlt_properties self, const char *name, double value, int position, int length, mlt_keyframe_type keyframe_type); MLT_EXPORT mlt_animation mlt_properties_get_animation(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_is_anim(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_set_rect(mlt_properties self, const char *name, mlt_rect value); MLT_EXPORT mlt_rect mlt_properties_get_rect(mlt_properties self, const char *name); MLT_EXPORT int mlt_properties_anim_set_rect(mlt_properties self, const char *name, mlt_rect value, int position, int length, mlt_keyframe_type keyframe_type); MLT_EXPORT mlt_rect mlt_properties_anim_get_rect(mlt_properties self, const char *name, int position, int length); MLT_EXPORT int mlt_properties_from_utf8(mlt_properties properties, const char *name_from, const char *name_to); MLT_EXPORT int mlt_properties_to_utf8(mlt_properties properties, const char *name_from, const char *name_to); MLT_EXPORT int mlt_properties_set_properties(mlt_properties self, const char *name, mlt_properties properties); MLT_EXPORT mlt_properties mlt_properties_get_properties(mlt_properties self, const char *name); MLT_EXPORT mlt_properties mlt_properties_get_properties_at(mlt_properties self, int index); #endif mlt-7.38.0/src/framework/mlt_property.c000664 000000 000000 00000220225 15172202314 020055 0ustar00rootroot000000 000000 /** * \file mlt_property.c * \brief Property class definition * \see mlt_property_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // For strtod_l #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "mlt_property.h" #include "mlt_animation.h" #include "mlt_properties.h" #include #include #include #include #include #include #include // Platforms with native strtod_l support #if defined(__GLIBC__) || defined(__APPLE__) || (defined(HAVE_STRTOD_L) && !defined(__OpenBSD__)) #define HAVE_LOCALE_STRTOD_L 1 #endif // Platforms requiring manual locale handling (excluding Windows) #if !defined(__GLIBC__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(HAVE_STRTOD_L) \ && !defined(__OpenBSD__) #define NEED_LOCALE_SAVE_RESTORE 1 #endif /** Bit pattern used internally to indicated representations available. */ typedef enum { mlt_prop_none = 0, //!< not set mlt_prop_int = 1, //!< set as an integer mlt_prop_string = 2, //!< set as string or already converted to string mlt_prop_position = 4, //!< set as a position mlt_prop_double = 8, //!< set as a floating point mlt_prop_data = 16, //!< set as opaque binary mlt_prop_int64 = 32, //!< set as a 64-bit integer mlt_prop_rect = 64, //!< set as a mlt_rect mlt_prop_color = 128 //!< set as a mlt_color } mlt_property_type; /** \brief Property class * * A property is like a variant or dynamic type. They are used for many things * in MLT, but in particular they are the parameter mechanism for the plugins. */ struct mlt_property_s { /// Stores a bit pattern of types available for this property mlt_property_type types; /// Atomic type handling int prop_int; mlt_position prop_position; double prop_double; int64_t prop_int64; /// String handling char *prop_string; /// Generic type handling void *data; int length; mlt_destructor destructor; mlt_serialiser serialiser; pthread_mutex_t mutex; mlt_animation animation; mlt_properties properties; }; /** Construct a property and initialize it * \public \memberof mlt_property_s */ mlt_property mlt_property_init() { mlt_property self = calloc(1, sizeof(*self)); if (self) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&self->mutex, &attr); } return self; } /** Clear (0/null) a property. * * Frees up any associated resources in the process. * \private \memberof mlt_property_s * \param self a property */ static void clear_property(mlt_property self) { // Special case data handling if (self->types & mlt_prop_data && self->destructor != NULL) self->destructor(self->data); // Special case string handling if (self->prop_string) free(self->prop_string); mlt_animation_close(self->animation); mlt_properties_close(self->properties); // Wipe stuff self->types = 0; self->prop_int = 0; self->prop_position = 0; self->prop_double = 0; self->prop_int64 = 0; self->prop_string = NULL; self->data = NULL; self->length = 0; self->destructor = NULL; self->serialiser = NULL; self->animation = NULL; self->properties = NULL; } /** Clear (0/null) a property. * * Frees up any associated resources in the process. * \public \memberof mlt_property_s * \param self a property */ void mlt_property_clear(mlt_property self) { pthread_mutex_lock(&self->mutex); clear_property(self); pthread_mutex_unlock(&self->mutex); } /** Check if a property is cleared. * * \public \memberof mlt_property_s * \param self a property * \return true if a property is clear. false if it has been set. */ int mlt_property_is_clear(mlt_property self) { int result = 1; if (self) { pthread_mutex_lock(&self->mutex); result = self->types == 0 && self->animation == NULL && self->properties == NULL; pthread_mutex_unlock(&self->mutex); } return result; } /** Set the property to an integer value. * * \public \memberof mlt_property_s * \param self a property * \param value an integer * \return false */ int mlt_property_set_int(mlt_property self, int value) { pthread_mutex_lock(&self->mutex); clear_property(self); self->types = mlt_prop_int; self->prop_int = value; pthread_mutex_unlock(&self->mutex); return 0; } /** Set the property to a floating point value. * * \public \memberof mlt_property_s * \param self a property * \param value a double precision floating point value * \return false */ int mlt_property_set_double(mlt_property self, double value) { pthread_mutex_lock(&self->mutex); clear_property(self); self->types = mlt_prop_double; self->prop_double = value; pthread_mutex_unlock(&self->mutex); return 0; } /** Set the property to a position value. * * Position is a relative time value in frame units. * \public \memberof mlt_property_s * \param self a property * \param value a position value * \return false */ int mlt_property_set_position(mlt_property self, mlt_position value) { pthread_mutex_lock(&self->mutex); clear_property(self); self->types = mlt_prop_position; self->prop_position = value; pthread_mutex_unlock(&self->mutex); return 0; } /** Set the property to a string value. * * This makes a copy of the string you supply so you do not need to track * a new reference to it. * \public \memberof mlt_property_s * \param self a property * \param value the string to copy to the property * \return true if it failed */ int mlt_property_set_string(mlt_property self, const char *value) { pthread_mutex_lock(&self->mutex); if (value != self->prop_string) { clear_property(self); self->types = mlt_prop_string; if (value != NULL) self->prop_string = strdup(value); } else { self->types = mlt_prop_string; } pthread_mutex_unlock(&self->mutex); return self->prop_string == NULL; } /** Set the property to a 64-bit integer value. * * \public \memberof mlt_property_s * \param self a property * \param value a 64-bit integer * \return false */ int mlt_property_set_int64(mlt_property self, int64_t value) { pthread_mutex_lock(&self->mutex); clear_property(self); self->types = mlt_prop_int64; self->prop_int64 = value; pthread_mutex_unlock(&self->mutex); return 0; } /** Set a property to an opaque binary value. * * This does not make a copy of the data. You can use a Properties object * with its reference tracking and the destructor function to control * the lifetime of the data. Otherwise, pass NULL for the destructor * function and control the lifetime yourself. * \public \memberof mlt_property_s * \param self a property * \param value an opaque pointer * \param length the number of bytes pointed to by value (optional) * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource) * \param serialiser a function to use to convert this binary data to a string (optional) * \return false */ int mlt_property_set_data(mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser) { pthread_mutex_lock(&self->mutex); if (self->data == value) self->destructor = NULL; clear_property(self); self->types = mlt_prop_data; self->data = value; self->length = length; self->destructor = destructor; self->serialiser = serialiser; pthread_mutex_unlock(&self->mutex); return 0; } /** Parse a SMIL clock value. * * \private \memberof mlt_property_s * \param self a property * \param s the string to parse * \param fps frames per second * \param locale the locale to use for parsing a real number value * \return position in frames */ static int time_clock_to_frames(mlt_property self, const char *s, double fps, mlt_locale_t locale) { char *pos, *copy = strdup(s); int hours = 0, minutes = 0; double seconds; s = copy; pos = strrchr(s, ':'); #ifdef NEED_LOCALE_SAVE_RESTORE char *orig_localename = NULL; if (locale) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock(&self->mutex); // Get the current locale orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); // Set the new locale setlocale(LC_NUMERIC, locale); } #endif if (pos) { #ifdef HAVE_LOCALE_STRTOD_L if (locale) seconds = strtod_l(pos + 1, NULL, locale); else #endif seconds = strtod(pos + 1, NULL); *pos = 0; pos = strrchr(s, ':'); if (pos) { minutes = atoi(pos + 1); *pos = 0; hours = atoi(s); } else { minutes = atoi(s); } } else { #ifdef HAVE_LOCALE_STRTOD_L if (locale) seconds = strtod_l(s, NULL, locale); else #endif seconds = strtod(s, NULL); } #ifdef NEED_LOCALE_SAVE_RESTORE if (locale) { // Restore the current locale setlocale(LC_NUMERIC, orig_localename); free(orig_localename); pthread_mutex_unlock(&self->mutex); } #endif free(copy); return floor(fps * hours * 3600) + floor(fps * minutes * 60) + lrint(fps * seconds); } /** Parse a SMPTE timecode string. * * \private \memberof mlt_property_s * \param self a property * \param s the string to parse * \param fps frames per second * \return position in frames */ static int time_code_to_frames(mlt_property self, const char *s, double fps) { char *pos, *copy = strdup(s); int hours = 0, minutes = 0, seconds = 0, frames; s = copy; pos = strrchr(s, ';'); if (!pos) pos = strrchr(s, ':'); if (pos) { frames = atoi(pos + 1); *pos = 0; pos = strrchr(s, ':'); if (pos) { seconds = atoi(pos + 1); *pos = 0; pos = strrchr(s, ':'); if (pos) { minutes = atoi(pos + 1); *pos = 0; hours = atoi(s); } else { minutes = atoi(s); } } else { seconds = atoi(s); } } else { frames = atoi(s); } free(copy); return floor(fps * hours * 3600) + floor(fps * minutes * 60) + ceil(fps * seconds) + frames; } /** Convert a string to an integer. * * The string must begin with '0x' to be interpreted as hexadecimal. * Otherwise, it is interpreted as base 10. * * If the string begins with '#' it is interpreted as a hexadecimal color value * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are * always in the form RRGGBBAA where the alpha components are not optional. * Applications and services should expect the binary color value in bytes to * be in the following order: RGBA. This means they will have to cast the int * to an unsigned int. This is especially important when they need to shift * right to obtain RGB without alpha in order to make it do a logical instead * of arithmetic shift. * * If the string contains a colon it is interpreted as a time value. If it also * contains a period or comma character, the string is parsed as a clock value: * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF. * \private \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return the resultant integer */ static int mlt_property_atoi(mlt_property self, double fps, mlt_locale_t locale) { const char *value = self->prop_string; // Parse a hex color value as #RRGGBB or #AARRGGBB. if (value[0] == '#') { unsigned int rgb = strtoul(value + 1, NULL, 16); unsigned int alpha = (strlen(value) > 7) ? (rgb >> 24) : 0xff; return (rgb << 8) | alpha; } // Do hex and decimal explicitly to avoid decimal value with leading zeros // interpreted as octal. else if (value[0] == '0' && value[1] == 'x') { return strtoul(value + 2, NULL, 16); } else if (fps > 0 && strchr(value, ':')) { if (strchr(value, '.') || strchr(value, ',')) return time_clock_to_frames(self, value, fps, locale); else return time_code_to_frames(self, value, fps); } else { return strtol(value, NULL, 10); } } /** Get the property as an integer. * * \public \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return an integer value */ int mlt_property_get_int(mlt_property self, double fps, mlt_locale_t locale) { pthread_mutex_lock(&self->mutex); int result = 0; if (self->types & mlt_prop_int || self->types & mlt_prop_color) result = self->prop_int; else if (self->types & mlt_prop_double) result = (int) self->prop_double; else if (self->types & mlt_prop_position) result = (int) self->prop_position; else if (self->types & mlt_prop_int64) result = (int) self->prop_int64; else if (self->types & mlt_prop_rect && self->data) result = (int) ((mlt_rect *) self->data)->x; else { if (self->animation && !mlt_animation_get_string(self->animation)) mlt_property_get_string(self); if ((self->types & mlt_prop_string) && self->prop_string) result = mlt_property_atoi(self, fps, locale); } pthread_mutex_unlock(&self->mutex); return result; } /** Convert a string to a floating point number. * * If the string contains a colon it is interpreted as a time value. If it also * contains a period or comma character, the string is parsed as a clock value: * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF. * If the numeric string ends with '%' then the value is divided by 100 to convert * it into a ratio. * \private \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return the resultant real number */ static double mlt_property_atof(mlt_property self, double fps, mlt_locale_t locale) { const char *value = self->prop_string; #ifdef NEED_LOCALE_SAVE_RESTORE char *orig_localename = NULL; #endif if (fps > 0 && strchr(value, ':')) { #ifdef NEED_LOCALE_SAVE_RESTORE if (locale) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock(&self->mutex); // Get the current locale orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); // Set the new locale setlocale(LC_NUMERIC, locale); } #endif double result; if (strchr(value, '.') || strchr(value, ',')) result = time_clock_to_frames(self, value, fps, locale); else result = time_code_to_frames(self, value, fps); #ifdef NEED_LOCALE_SAVE_RESTORE if (locale) { // Restore the current locale setlocale(LC_NUMERIC, orig_localename); free(orig_localename); pthread_mutex_unlock(&self->mutex); } #endif return result; } else { char *end = NULL; double result; #ifdef HAVE_LOCALE_STRTOD_L if (locale) result = strtod_l(value, &end, locale); else #elif !defined(_WIN32) if (locale) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock(&self->mutex); // Get the current locale orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); // Set the new locale setlocale(LC_NUMERIC, locale); } #endif result = strtod(value, &end); if (end && end[0] == '%') result /= 100.0; #ifdef NEED_LOCALE_SAVE_RESTORE if (locale) { // Restore the current locale setlocale(LC_NUMERIC, orig_localename); free(orig_localename); pthread_mutex_unlock(&self->mutex); } #endif return result; } } /** Get the property as a floating point. * * \public \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use for this conversion * \return a floating point value */ double mlt_property_get_double(mlt_property self, double fps, mlt_locale_t locale) { double result = 0.0; pthread_mutex_lock(&self->mutex); if (self->types & mlt_prop_double) result = self->prop_double; else if (self->types & mlt_prop_int || self->types & mlt_prop_color) result = (double) self->prop_int; else if (self->types & mlt_prop_position) result = (double) self->prop_position; else if (self->types & mlt_prop_int64) result = (double) self->prop_int64; else if (self->types & mlt_prop_rect && self->data) result = ((mlt_rect *) self->data)->x; else { if (self->animation && !mlt_animation_get_string(self->animation)) mlt_property_get_string(self); if ((self->types & mlt_prop_string) && self->prop_string) result = mlt_property_atof(self, fps, locale); } pthread_mutex_unlock(&self->mutex); return result; } /** Get the property as a position. * * A position is an offset time in terms of frame units. * \public \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return the position in frames */ mlt_position mlt_property_get_position(mlt_property self, double fps, mlt_locale_t locale) { mlt_position result = 0; pthread_mutex_lock(&self->mutex); if (self->types & mlt_prop_position) result = self->prop_position; else if (self->types & mlt_prop_int || self->types & mlt_prop_color) result = (mlt_position) self->prop_int; else if (self->types & mlt_prop_double) result = (mlt_position) self->prop_double; else if (self->types & mlt_prop_int64) result = (mlt_position) self->prop_int64; else if (self->types & mlt_prop_rect && self->data) result = (mlt_position) ((mlt_rect *) self->data)->x; else { if (self->animation && !mlt_animation_get_string(self->animation)) mlt_property_get_string(self); if ((self->types & mlt_prop_string) && self->prop_string) result = (mlt_position) mlt_property_atoi(self, fps, locale); } pthread_mutex_unlock(&self->mutex); return result; } /** Convert a string to a 64-bit integer. * * If the string begins with '0x' it is interpreted as a hexadecimal value. * \private \memberof mlt_property_s * \param value a string * \return a 64-bit integer */ static inline int64_t mlt_property_atoll(const char *value) { if (value == NULL) return 0; else if (value[0] == '0' && value[1] == 'x') return strtoll(value + 2, NULL, 16); else return strtoll(value, NULL, 10); } /** Get the property as a signed integer. * * \public \memberof mlt_property_s * \param self a property * \return a 64-bit integer */ int64_t mlt_property_get_int64(mlt_property self) { int64_t result = 0; pthread_mutex_lock(&self->mutex); if (self->types & mlt_prop_int64) result = self->prop_int64; else if (self->types & mlt_prop_int || self->types & mlt_prop_color) result = (int64_t) self->prop_int; else if (self->types & mlt_prop_double) result = (int64_t) self->prop_double; else if (self->types & mlt_prop_position) result = (int64_t) self->prop_position; else if (self->types & mlt_prop_rect && self->data) result = (int64_t) ((mlt_rect *) self->data)->x; else { if (self->animation && !mlt_animation_get_string(self->animation)) mlt_property_get_string(self); if ((self->types & mlt_prop_string) && self->prop_string) result = mlt_property_atoll(self->prop_string); } pthread_mutex_unlock(&self->mutex); return result; } /** Get the property as a string (with time format). * * The caller is not responsible for deallocating the returned string! * The string is deallocated when the Property is closed. * This tries its hardest to convert the property to string including using * a serialization function for binary data, if supplied. * \public \memberof mlt_property_s * \param self a property * \param time_format the time format to use for animation * \return a string representation of the property or NULL if failed */ char *mlt_property_get_string_tf(mlt_property self, mlt_time_format time_format) { // Construct a string if need be pthread_mutex_lock(&self->mutex); if (self->animation && self->serialiser) { free(self->prop_string); self->prop_string = self->serialiser(self->animation, time_format); } else if (!(self->types & mlt_prop_string)) { if (self->types & mlt_prop_int) { self->types |= mlt_prop_string; self->prop_string = malloc(32); sprintf(self->prop_string, "%d", self->prop_int); } else if (self->types & mlt_prop_color) { self->types |= mlt_prop_string; self->prop_string = malloc(10); uint32_t int_value = ((self->prop_int & 0xff) << 24) | ((self->prop_int >> 8) & 0xffffff); sprintf(self->prop_string, "#%08x", int_value); } else if (self->types & mlt_prop_double) { self->types |= mlt_prop_string; self->prop_string = malloc(32); sprintf(self->prop_string, "%g", self->prop_double); } else if (self->types & mlt_prop_position) { self->types |= mlt_prop_string; self->prop_string = malloc(32); sprintf(self->prop_string, "%d", (int) self->prop_position); } else if (self->types & mlt_prop_int64) { self->types |= mlt_prop_string; self->prop_string = malloc(32); sprintf(self->prop_string, "%" PRId64, self->prop_int64); } else if (self->types & mlt_prop_data && self->data && self->serialiser) { self->types |= mlt_prop_string; self->prop_string = self->serialiser(self->data, self->length); } } pthread_mutex_unlock(&self->mutex); // Return the string (may be NULL) return self->prop_string; } static mlt_time_format default_time_format() { const char *e = getenv("MLT_ANIMATION_TIME_FORMAT"); return e ? strtol(e, NULL, 10) : mlt_time_frames; } /** Get the property as a string. * * The caller is not responsible for deallocating the returned string! * The string is deallocated when the Property is closed. * This tries its hardest to convert the property to string including using * a serialization function for binary data, if supplied. * \public \memberof mlt_property_s * \param self a property * \return a string representation of the property or NULL if failed */ char *mlt_property_get_string(mlt_property self) { return mlt_property_get_string_tf(self, default_time_format()); } /** Get the property as a string (with locale and time format). * * The caller is not responsible for deallocating the returned string! * The string is deallocated when the Property is closed. * This tries its hardest to convert the property to string including using * a serialization function for binary data, if supplied. * \public \memberof mlt_property_s * \param self a property * \param locale the locale to use for this conversion * \param time_format the time format to use for animation * \return a string representation of the property or NULL if failed */ char *mlt_property_get_string_l_tf(mlt_property self, mlt_locale_t locale, mlt_time_format time_format) { // Optimization for no locale if (!locale) return mlt_property_get_string_tf(self, time_format); // Construct a string if need be pthread_mutex_lock(&self->mutex); if (self->animation && self->serialiser) { free(self->prop_string); self->prop_string = self->serialiser(self->animation, time_format); } else if (!(self->types & mlt_prop_string)) { #if !defined(_WIN32) // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale. // Save the current locale #if defined(__APPLE__) const char *localename = querylocale(LC_NUMERIC_MASK, locale); #elif defined(__GLIBC__) const char *localename = locale->__names[LC_NUMERIC]; #else const char *localename = locale; #endif // Get the current locale char *orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); // Set the new locale setlocale(LC_NUMERIC, localename); #endif // _WIN32 if (self->types & mlt_prop_int) { self->types |= mlt_prop_string; self->prop_string = malloc(32); sprintf(self->prop_string, "%d", self->prop_int); } else if (self->types & mlt_prop_color) { self->types |= mlt_prop_string; self->prop_string = malloc(10); uint32_t int_value = ((self->prop_int & 0xff) << 24) | ((self->prop_int >> 8) & 0xffffff); sprintf(self->prop_string, "#%08x", int_value); } else if (self->types & mlt_prop_double) { self->types |= mlt_prop_string; self->prop_string = malloc(32); sprintf(self->prop_string, "%g", self->prop_double); } else if (self->types & mlt_prop_position) { self->types |= mlt_prop_string; self->prop_string = malloc(32); sprintf(self->prop_string, "%d", (int) self->prop_position); } else if (self->types & mlt_prop_int64) { self->types |= mlt_prop_string; self->prop_string = malloc(32); sprintf(self->prop_string, "%" PRId64, self->prop_int64); } else if (self->types & mlt_prop_data && self->data && self->serialiser) { self->types |= mlt_prop_string; self->prop_string = self->serialiser(self->data, self->length); } #if !defined(_WIN32) // Restore the current locale setlocale(LC_NUMERIC, orig_localename); free(orig_localename); #endif } pthread_mutex_unlock(&self->mutex); // Return the string (may be NULL) return self->prop_string; } /** Get the property as a string (with locale). * * The caller is not responsible for deallocating the returned string! * The string is deallocated when the Property is closed. * This tries its hardest to convert the property to string including using * a serialization function for binary data, if supplied. * \public \memberof mlt_property_s * \param self a property * \param locale the locale to use for this conversion * \return a string representation of the property or NULL if failed */ char *mlt_property_get_string_l(mlt_property self, mlt_locale_t locale) { return mlt_property_get_string_l_tf(self, locale, default_time_format()); } /** Get the binary data from a property. * * This only works if you previously put binary data into the property. * This does not return a copy of the data; it returns a pointer to it. * If you supplied a destructor function when setting the binary data, * the destructor is used when the Property is closed to free the memory. * Therefore, only free the returned pointer if you did not supply a * destructor function. * \public \memberof mlt_property_s * \param self a property * \param[out] length the size of the binary object in bytes (optional) * \return an opaque data pointer or NULL if not available */ void *mlt_property_get_data(mlt_property self, int *length) { // Assign length if not NULL if (length != NULL) *length = self->length; // Return the data (note: there is no conversion here) pthread_mutex_lock(&self->mutex); void *result = self->data; pthread_mutex_unlock(&self->mutex); return result; } /** Destroy a property and free all related resources. * * \public \memberof mlt_property_s * \param self a property */ void mlt_property_close(mlt_property self) { clear_property(self); pthread_mutex_destroy(&self->mutex); free(self); } /** Copy a property. * * A Property holding binary data only copies the data if a serialiser * function was supplied when you set the Property. * \public \memberof mlt_property_s * \author Zach * \param self a property * \param that another property */ void mlt_property_pass(mlt_property self, mlt_property that) { pthread_mutex_lock(&self->mutex); clear_property(self); self->types = that->types; if (self->types & mlt_prop_int64) self->prop_int64 = that->prop_int64; else if (self->types & mlt_prop_int || self->types & mlt_prop_color) self->prop_int = that->prop_int; else if (self->types & mlt_prop_double) self->prop_double = that->prop_double; else if (self->types & mlt_prop_position) self->prop_position = that->prop_position; if (self->types & mlt_prop_string) { if (that->prop_string != NULL) self->prop_string = strdup(that->prop_string); } else if (that->types & mlt_prop_rect) { clear_property(self); self->types = mlt_prop_rect | mlt_prop_data; self->length = that->length; self->data = calloc(1, self->length); memcpy(self->data, that->data, self->length); self->destructor = free; self->serialiser = that->serialiser; } else if (that->animation && that->serialiser) { self->types = mlt_prop_string; self->prop_string = that->serialiser(that->animation, default_time_format()); } else if (that->types & mlt_prop_data && that->serialiser) { self->types = mlt_prop_string; self->prop_string = that->serialiser(that->data, that->length); } pthread_mutex_unlock(&self->mutex); } /** Convert frame count to a SMPTE timecode string. * * \private \memberof mlt_property_s * \param frames a frame count * \param fps frames per second * \param[out] s the string to write into - must have enough space to hold largest time string * \param drop whether to use drop-frame timecode for applicable frame rates */ static void time_smpte_from_frames(int frames, double fps, char *s, int drop) { int hours, mins, secs; char frame_sep = ':'; int save_frames = frames; if (fps == 30000.0 / 1001.0) { fps = 30.0; if (drop) { int i; for (i = 1800; i <= frames; i += 1800) { if (i % 18000) frames += 2; } frame_sep = ';'; } } else if (fps == 60000.0 / 1001.0) { fps = 60.0; if (drop) { int i; for (i = 3600; i <= frames; i += 3600) { if (i % 36000) frames += 4; } frame_sep = ';'; } } else if (!drop) { fps = lrint(fps); } else if (fps != lrint(fps)) { frame_sep = ';'; } hours = frames / (fps * 3600); frames -= floor(hours * 3600 * fps); mins = frames / (fps * 60); if (mins == 60) { // floating point error ++hours; frames = save_frames - floor(hours * 3600 * fps); mins = 0; } save_frames = frames; frames -= floor(mins * 60 * fps); secs = frames / fps; if (secs == 60) { // floating point error ++mins; frames = save_frames - floor(mins * 60 * fps); secs = 0; } frames -= ceil(secs * fps); sprintf(s, "%02d:%02d:%02d%c%0*d", hours, mins, secs, frame_sep, (fps > 999 ? 4 : fps > 99 ? 3 : 2), frames); } /** Convert frame count to a SMIL clock value string. * * \private \memberof mlt_property_s * \param frames a frame count * \param fps frames per second * \param[out] s the string to write into - must have enough space to hold largest time string */ static void time_clock_from_frames(int frames, double fps, char *s) { int hours, mins; double secs; int save_frames = frames; hours = frames / (fps * 3600); frames -= floor(hours * 3600 * fps); mins = frames / (fps * 60); if (mins == 60) { // floating point error ++hours; frames = save_frames - floor(hours * 3600 * fps); mins = 0; } save_frames = frames; frames -= floor(mins * 60 * fps); secs = frames / fps; if (secs >= 60.0) { // floating point error ++mins; frames = save_frames - floor(mins * 60 * fps); secs = frames / fps; } sprintf(s, "%02d:%02d:%06.3f", hours, mins, secs); } /** Get the property as a time string. * * The time value can be either a SMPTE timecode or SMIL clock value. * The caller is not responsible for deallocating the returned string! * The string is deallocated when the property is closed. * \public \memberof mlt_property_s * \param self a property * \param format the time format that you want * \param fps frames per second * \param locale the locale to use for this conversion * \return a string representation of the property or NULL if failed */ char *mlt_property_get_time(mlt_property self, mlt_time_format format, double fps, mlt_locale_t locale) { #if !defined(_WIN32) char *orig_localename = NULL; #endif int frames = 0; // Remove existing string if (self->prop_string) mlt_property_set_int(self, mlt_property_get_int(self, fps, locale)); // Optimization for mlt_time_frames if (format == mlt_time_frames) return mlt_property_get_string_l(self, locale); #if !defined(_WIN32) // Use the specified locale if (locale) { // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale. // Save the current locale #if defined(__APPLE__) const char *localename = querylocale(LC_NUMERIC_MASK, locale); #elif defined(__GLIBC__) const char *localename = locale->__names[LC_NUMERIC]; #else // TODO: not yet sure what to do on other platforms const char *localename = locale; #endif // _WIN32 // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock(&self->mutex); // Get the current locale orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); // Set the new locale setlocale(LC_NUMERIC, localename); } else #endif // _WIN32 { // Make sure we have a lock before accessing self->types pthread_mutex_lock(&self->mutex); } // Convert number to string if (self->types & mlt_prop_int) { frames = self->prop_int; } else if (self->types & mlt_prop_position) { frames = (int) self->prop_position; } else if (self->types & mlt_prop_double) { frames = self->prop_double; } else if (self->types & mlt_prop_int64) { frames = (int) self->prop_int64; } self->types |= mlt_prop_string; self->prop_string = malloc(32); if (format == mlt_time_clock) time_clock_from_frames(frames, fps, self->prop_string); else if (format == mlt_time_smpte_ndf) time_smpte_from_frames(frames, fps, self->prop_string, 0); else // Use smpte drop frame by default time_smpte_from_frames(frames, fps, self->prop_string, 1); #if !defined(_WIN32) // Restore the current locale if (locale) { setlocale(LC_NUMERIC, orig_localename); free(orig_localename); pthread_mutex_unlock(&self->mutex); } else #endif // _WIN32 { // Make sure we have a lock before accessing self->types pthread_mutex_unlock(&self->mutex); } // Return the string (may be NULL) return self->prop_string; } /** Determine if the property holds a numeric or numeric string value. * * \public \memberof mlt_property_s * \param self a property * \param locale the locale to use for string evaluation * \return true if it is numeric */ int mlt_property_is_numeric(mlt_property self, mlt_locale_t locale) { int result = (self->types & mlt_prop_int) || (self->types & mlt_prop_color) || (self->types & mlt_prop_int64) || (self->types & mlt_prop_double) || (self->types & mlt_prop_position) || (self->types & mlt_prop_rect); // If not already numeric but string is numeric. if ((!result && self->types & mlt_prop_string) && self->prop_string) { char *p = NULL; #ifdef NEED_LOCALE_SAVE_RESTORE char *orig_localename = NULL; #endif #ifdef HAVE_LOCALE_STRTOD_L if (locale) strtod_l(self->prop_string, &p, locale); else #elif !defined(_WIN32) if (locale) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock(&self->mutex); // Get the current locale orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); // Set the new locale setlocale(LC_NUMERIC, locale); } #endif strtod(self->prop_string, &p); #ifdef NEED_LOCALE_SAVE_RESTORE if (locale) { // Restore the current locale setlocale(LC_NUMERIC, orig_localename); free(orig_localename); pthread_mutex_unlock(&self->mutex); } #endif result = (p != self->prop_string); } return result; } /** A linear interpolation function for animation. * * \deprecated * \private \memberof mlt_property_s */ static inline double linear_interpolate(double y1, double y2, double t) { return y1 + (y2 - y1) * t; } /** A smooth spline interpolation for animation. * * \deprecated * For non-closed curves, you need to also supply the tangent vector at the first and last control point. * This is commonly done: T(P[0]) = P[1] - P[0] and T(P[n]) = P[n] - P[n-1]. * \private \memberof mlt_property_s */ static inline double catmull_rom_interpolate(double y0, double y1, double y2, double y3, double t) { double t2 = t * t; double a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3; double a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3; double a2 = -0.5 * y0 + 0.5 * y2; double a3 = y1; return a0 * t * t2 + a1 * t2 + a2 * t + a3; } /** Interpolate a new property value given a set of other properties. * * \deprecated * \public \memberof mlt_property_s * \param self the property onto which to set the computed value * \param p an array of at least 1 value in p[1] if \p interp is discrete, * 2 values in p[1] and p[2] if \p interp is linear, or * 4 values in p[0] - p[3] if \p interp is smooth * \param progress a ratio in the range [0, 1] to indicate how far between p[1] and p[2] * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param interp the interpolation method to use * \return true if there was an error */ int mlt_property_interpolate(mlt_property self, mlt_property p[], double progress, double fps, mlt_locale_t locale, mlt_keyframe_type interp) { int error = 0; int colorstring = 0; const char *value = self->prop_string; if (value && ((strlen(value) > 6 && value[0] == '#') || (strlen(value) > 7 && value[0] == '0' && value[1] == 'x'))) { colorstring = 1; } if (interp != mlt_keyframe_discrete && (self->types & mlt_prop_color || colorstring)) { mlt_color value = {0xff, 0xff, 0xff, 0xff}; if (interp == mlt_keyframe_linear) { mlt_color colors[2]; mlt_color zero = {0xff, 0xff, 0xff, 0xff}; colors[0] = p[1] ? mlt_property_get_color(p[1], fps, locale) : zero; if (p[2]) { colors[1] = mlt_property_get_color(p[2], fps, locale); value.r = linear_interpolate(colors[0].r, colors[1].r, progress); value.g = linear_interpolate(colors[0].g, colors[1].g, progress); value.b = linear_interpolate(colors[0].b, colors[1].b, progress); value.a = linear_interpolate(colors[0].a, colors[1].a, progress); } else { value = colors[0]; } } else if (interp == mlt_keyframe_smooth) { mlt_color colors[4]; mlt_color zero = {0xff, 0xff, 0xff, 0xff}; colors[1] = p[1] ? mlt_property_get_color(p[1], fps, locale) : zero; if (p[2]) { colors[0] = p[0] ? mlt_property_get_color(p[0], fps, locale) : zero; colors[2] = p[2] ? mlt_property_get_color(p[2], fps, locale) : zero; colors[3] = p[3] ? mlt_property_get_color(p[3], fps, locale) : zero; value.r = CLAMP(catmull_rom_interpolate(colors[0].r, colors[1].r, colors[2].r, colors[3].r, progress), 0, 255); value.g = CLAMP(catmull_rom_interpolate(colors[0].g, colors[1].g, colors[2].g, colors[3].g, progress), 0, 255); value.b = CLAMP(catmull_rom_interpolate(colors[0].b, colors[1].b, colors[2].b, colors[3].b, progress), 0, 255); value.a = CLAMP(catmull_rom_interpolate(colors[0].a, colors[1].a, colors[2].a, colors[3].a, progress), 0, 255); } else { value = colors[1]; } } error = mlt_property_set_color(self, value); } else if (interp != mlt_keyframe_discrete && mlt_property_is_numeric(p[1], locale) && mlt_property_is_numeric(p[2], locale)) { if (self->types & mlt_prop_rect) { mlt_rect value = {DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN}; if (interp == mlt_keyframe_linear) { mlt_rect points[2]; mlt_rect zero = {0, 0, 0, 0, 0}; points[0] = p[1] ? mlt_property_get_rect(p[1], locale) : zero; if (p[2]) { points[1] = mlt_property_get_rect(p[2], locale); value.x = linear_interpolate(points[0].x, points[1].x, progress); value.y = linear_interpolate(points[0].y, points[1].y, progress); value.w = linear_interpolate(points[0].w, points[1].w, progress); value.h = linear_interpolate(points[0].h, points[1].h, progress); value.o = linear_interpolate(points[0].o, points[1].o, progress); } else { value = points[0]; } } else if (interp == mlt_keyframe_smooth) { mlt_rect points[4]; mlt_rect zero = {0, 0, 0, 0, 0}; points[1] = p[1] ? mlt_property_get_rect(p[1], locale) : zero; if (p[2]) { points[0] = p[0] ? mlt_property_get_rect(p[0], locale) : zero; points[2] = p[2] ? mlt_property_get_rect(p[2], locale) : zero; points[3] = p[3] ? mlt_property_get_rect(p[3], locale) : zero; value.x = catmull_rom_interpolate(points[0].x, points[1].x, points[2].x, points[3].x, progress); value.y = catmull_rom_interpolate(points[0].y, points[1].y, points[2].y, points[3].y, progress); value.w = catmull_rom_interpolate(points[0].w, points[1].w, points[2].w, points[3].w, progress); value.h = catmull_rom_interpolate(points[0].h, points[1].h, points[2].h, points[3].h, progress); value.o = catmull_rom_interpolate(points[0].o, points[1].o, points[2].o, points[3].o, progress); } else { value = points[1]; } } error = mlt_property_set_rect(self, value); } else { double value = 0.0; if (interp == mlt_keyframe_linear) { double points[2]; points[0] = p[1] ? mlt_property_get_double(p[1], fps, locale) : 0; points[1] = p[2] ? mlt_property_get_double(p[2], fps, locale) : 0; value = p[2] ? linear_interpolate(points[0], points[1], progress) : points[0]; } else if (interp == mlt_keyframe_smooth) { double points[4]; points[0] = p[0] ? mlt_property_get_double(p[0], fps, locale) : 0; points[1] = p[1] ? mlt_property_get_double(p[1], fps, locale) : 0; points[2] = p[2] ? mlt_property_get_double(p[2], fps, locale) : 0; points[3] = p[3] ? mlt_property_get_double(p[3], fps, locale) : 0; value = p[2] ? catmull_rom_interpolate(points[0], points[1], points[2], points[3], progress) : points[1]; } error = mlt_property_set_double(self, value); } } else { mlt_property_pass(self, p[1]); } return error; } /** Create a new animation or refresh an existing one. * * \private \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that */ static void refresh_animation(mlt_property self, double fps, mlt_locale_t locale, int length) { if (!self->animation) { self->animation = mlt_animation_new(); self->serialiser = (mlt_serialiser) mlt_animation_serialize_tf; mlt_animation_parse(self->animation, self->prop_string, length, fps, locale); } else if (!mlt_animation_get_string(self->animation)) { // The animation clears its string if it is modified. // Do not use a property string that is out of sync. self->types &= ~mlt_prop_string; free(self->prop_string); self->prop_string = NULL; } else if ((self->types & mlt_prop_string) && self->prop_string) { mlt_animation_refresh(self->animation, self->prop_string, length); } else if (length >= 0) { mlt_animation_set_length(self->animation, length); } } /** Get the real number at a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the real number */ double mlt_property_anim_get_double( mlt_property self, double fps, mlt_locale_t locale, int position, int length) { double result; pthread_mutex_lock(&self->mutex); if (mlt_property_is_anim(self)) { struct mlt_animation_item_s item; item.property = mlt_property_init(); refresh_animation(self, fps, locale, length); mlt_animation_get_item(self->animation, &item, position); pthread_mutex_unlock(&self->mutex); result = mlt_property_get_double(item.property, fps, locale); mlt_property_close(item.property); } else { pthread_mutex_unlock(&self->mutex); result = mlt_property_get_double(self, fps, locale); } return result; } /** Get the property as an integer number at a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return an integer value */ int mlt_property_anim_get_int( mlt_property self, double fps, mlt_locale_t locale, int position, int length) { int result; pthread_mutex_lock(&self->mutex); if (mlt_property_is_anim(self)) { struct mlt_animation_item_s item; item.property = mlt_property_init(); refresh_animation(self, fps, locale, length); mlt_animation_get_item(self->animation, &item, position); pthread_mutex_unlock(&self->mutex); result = mlt_property_get_int(item.property, fps, locale); mlt_property_close(item.property); } else { pthread_mutex_unlock(&self->mutex); result = mlt_property_get_int(self, fps, locale); } return result; } /** Get the string at certain a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the string representation of the property or NULL if failed */ char *mlt_property_anim_get_string( mlt_property self, double fps, mlt_locale_t locale, int position, int length) { char *result; pthread_mutex_lock(&self->mutex); if (mlt_property_is_anim(self)) { struct mlt_animation_item_s item; item.property = mlt_property_init(); if (!self->animation) refresh_animation(self, fps, locale, length); mlt_animation_get_item(self->animation, &item, position); free(self->prop_string); pthread_mutex_unlock(&self->mutex); self->prop_string = mlt_property_get_string_l(item.property, locale); pthread_mutex_lock(&self->mutex); if (self->prop_string) self->prop_string = strdup(self->prop_string); self->types |= mlt_prop_string; result = self->prop_string; mlt_property_close(item.property); pthread_mutex_unlock(&self->mutex); } else { pthread_mutex_unlock(&self->mutex); result = mlt_property_get_string_l(self, locale); } return result; } /** Set a property animation keyframe to a real number. * * \public \memberof mlt_property_s * \param self a property * \param value a double precision floating point value * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return false if successful, true to indicate error */ int mlt_property_anim_set_double(mlt_property self, double value, double fps, mlt_locale_t locale, int position, int length, mlt_keyframe_type keyframe_type) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = keyframe_type; mlt_property_set_double(item.property, value); pthread_mutex_lock(&self->mutex); refresh_animation(self, fps, locale, length); result = mlt_animation_insert(self->animation, &item); mlt_animation_interpolate(self->animation); pthread_mutex_unlock(&self->mutex); mlt_property_close(item.property); return result; } /** Set a property animation keyframe to an integer value. * * \public \memberof mlt_property_s * \param self a property * \param value an integer * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return false if successful, true to indicate error */ int mlt_property_anim_set_int(mlt_property self, int value, double fps, mlt_locale_t locale, int position, int length, mlt_keyframe_type keyframe_type) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = keyframe_type; mlt_property_set_int(item.property, value); pthread_mutex_lock(&self->mutex); refresh_animation(self, fps, locale, length); result = mlt_animation_insert(self->animation, &item); mlt_animation_interpolate(self->animation); pthread_mutex_unlock(&self->mutex); mlt_property_close(item.property); return result; } /** Set a property animation keyframe to a string. * * Strings only support discrete animation. Do not use this to set a property's * animation string that contains a semicolon-delimited set of values; use * mlt_property_set() for that. * \public \memberof mlt_property_s * \param self a property * \param value a string * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return false if successful, true to indicate error */ int mlt_property_anim_set_string( mlt_property self, const char *value, double fps, mlt_locale_t locale, int position, int length) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = mlt_keyframe_discrete; mlt_property_set_string(item.property, value); pthread_mutex_lock(&self->mutex); refresh_animation(self, fps, locale, length); result = mlt_animation_insert(self->animation, &item); mlt_animation_interpolate(self->animation); pthread_mutex_unlock(&self->mutex); mlt_property_close(item.property); return result; } /** Get an object's animation object. * * You might need to call another mlt_property_anim_ function to actually construct * the animation, as this is a simple accessor function. * \public \memberof mlt_property_s * \param self a property * \return the animation object or NULL if there is no animation */ mlt_animation mlt_property_get_animation(mlt_property self) { pthread_mutex_lock(&self->mutex); mlt_animation result = self->animation; pthread_mutex_unlock(&self->mutex); return result; } /** Set the property to a color value. * * \public \memberof mlt_property_s * \param self a property * \param value an integer * \return false */ int mlt_property_set_color(mlt_property self, mlt_color value) { pthread_mutex_lock(&self->mutex); clear_property(self); self->types = mlt_prop_color; uint32_t int_value = (value.r << 24) | (value.g << 16) | (value.b << 8) | value.a; self->prop_int = int_value; pthread_mutex_unlock(&self->mutex); return 0; } /** Get the property as a color. * * \public \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use for when converting from a string * \return a color value */ mlt_color mlt_property_get_color(mlt_property self, double fps, mlt_locale_t locale) { mlt_color result = {0xff, 0xff, 0xff, 0xff}; int color_int = mlt_property_get_int(self, fps, locale); if ((self->types & mlt_prop_string) && self->prop_string) { const char *color = mlt_property_get_string_l(self, locale); if (!strcmp(color, "red")) { result.r = 0xff; result.g = 0x00; result.b = 0x00; return result; } if (!strcmp(color, "green")) { result.r = 0x00; result.g = 0xff; result.b = 0x00; return result; } if (!strcmp(color, "blue")) { result.r = 0x00; result.g = 0x00; result.b = 0xff; return result; } if (!strcmp(color, "black")) { result.r = 0x00; result.g = 0x00; result.b = 0x00; return result; } if (!strcmp(color, "white")) { return result; } } result.r = (color_int >> 24) & 0xff; result.g = (color_int >> 16) & 0xff; result.b = (color_int >> 8) & 0xff; result.a = (color_int) &0xff; return result; } /** Set a property animation keyframe to a color. * * \public \memberof mlt_property_s * \param self a property * \param value a color * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return false if successful, true to indicate error */ int mlt_property_anim_set_color(mlt_property self, mlt_color value, double fps, mlt_locale_t locale, int position, int length, mlt_keyframe_type keyframe_type) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = keyframe_type; mlt_property_set_color(item.property, value); pthread_mutex_lock(&self->mutex); refresh_animation(self, fps, locale, length); result = mlt_animation_insert(self->animation, &item); mlt_animation_interpolate(self->animation); pthread_mutex_unlock(&self->mutex); mlt_property_close(item.property); return result; } /** Get a color at a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the color */ mlt_color mlt_property_anim_get_color( mlt_property self, double fps, mlt_locale_t locale, int position, int length) { mlt_color result; pthread_mutex_lock(&self->mutex); if (mlt_property_is_anim(self)) { struct mlt_animation_item_s item; item.property = mlt_property_init(); item.property->types = mlt_prop_color; refresh_animation(self, fps, locale, length); mlt_animation_get_item(self->animation, &item, position); pthread_mutex_unlock(&self->mutex); result = mlt_property_get_color(item.property, fps, locale); mlt_property_close(item.property); } else { pthread_mutex_unlock(&self->mutex); result = mlt_property_get_color(self, fps, locale); } return result; } /** Convert a rectangle value into a string. * * The canonical form of a mlt_rect * is a space delimited "x y w h o" even though many kinds of field delimiters * may be used to convert a string to a rectangle. * \private \memberof mlt_property_s * \param rect the rectangle to convert * \param length not used * \return the string representation of a rectangle */ static char *serialise_mlt_rect(mlt_rect *rect, int length) { char *result = calloc(1, 100); if (rect->x != DBL_MIN) sprintf(result + strlen(result), "%g", rect->x); if (rect->y != DBL_MIN) sprintf(result + strlen(result), " %g", rect->y); if (rect->w != DBL_MIN) sprintf(result + strlen(result), " %g", rect->w); if (rect->h != DBL_MIN) sprintf(result + strlen(result), " %g", rect->h); if (rect->o != DBL_MIN) sprintf(result + strlen(result), " %g", rect->o); return result; } /** Set a property to a mlt_rect rectangle. * * \public \memberof mlt_property_s * \param self a property * \param value a rectangle * \return false */ int mlt_property_set_rect(mlt_property self, mlt_rect value) { pthread_mutex_lock(&self->mutex); clear_property(self); self->types = mlt_prop_rect | mlt_prop_data; self->length = sizeof(value); self->data = calloc(1, self->length); memcpy(self->data, &value, self->length); self->destructor = free; self->serialiser = (mlt_serialiser) serialise_mlt_rect; pthread_mutex_unlock(&self->mutex); return 0; } /** Get the property as a rectangle. * * You can use any non-numeric character(s) as a field delimiter. * If the number has a '%' immediately following it, the number is divided by * 100 to convert it into a real number. * \public \memberof mlt_property_s * \param self a property * \param locale the locale to use for when converting from a string * \return a rectangle value */ mlt_rect mlt_property_get_rect(mlt_property self, mlt_locale_t locale) { mlt_rect rect = {DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN}; if ((self->types & mlt_prop_rect) && self->data) rect = *((mlt_rect *) self->data); else if (self->types & mlt_prop_double) rect.x = self->prop_double; else if (self->types & mlt_prop_int || self->types & mlt_prop_color) rect.x = (double) self->prop_int; else if (self->types & mlt_prop_position) rect.x = (double) self->prop_position; else if (self->types & mlt_prop_int64) rect.x = (double) self->prop_int64; else if ((self->types & mlt_prop_string) && self->prop_string) { char *value = self->prop_string; char *p = NULL; int count = 0; #ifdef NEED_LOCALE_SAVE_RESTORE char *orig_localename = NULL; if (locale) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock(&self->mutex); // Get the current locale orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); // Set the new locale setlocale(LC_NUMERIC, locale); } #endif while (*value) { double temp; #ifdef HAVE_LOCALE_STRTOD_L if (locale) temp = strtod_l(value, &p, locale); else #endif temp = strtod(value, &p); if (p != value) { if (p[0] == '%') { temp /= 100.0; p++; } // Chomp the delimiter. if (*p) p++; // Assign the value to appropriate field. switch (count) { case 0: rect.x = temp; break; case 1: rect.y = temp; break; case 2: rect.w = temp; break; case 3: rect.h = temp; break; case 4: rect.o = temp; break; } } else { p++; } value = p; count++; } #ifdef NEED_LOCALE_SAVE_RESTORE if (locale) { // Restore the current locale setlocale(LC_NUMERIC, orig_localename); free(orig_localename); pthread_mutex_unlock(&self->mutex); } #endif } return rect; } /** Set a property animation keyframe to a rectangle. * * \public \memberof mlt_property_s * \param self a property * \param value a rectangle * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return false if successful, true to indicate error */ int mlt_property_anim_set_rect(mlt_property self, mlt_rect value, double fps, mlt_locale_t locale, int position, int length, mlt_keyframe_type keyframe_type) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = keyframe_type; mlt_property_set_rect(item.property, value); pthread_mutex_lock(&self->mutex); refresh_animation(self, fps, locale, length); result = mlt_animation_insert(self->animation, &item); mlt_animation_interpolate(self->animation); pthread_mutex_unlock(&self->mutex); mlt_property_close(item.property); return result; } /** Get a rectangle at a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the rectangle */ mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, mlt_locale_t locale, int position, int length) { mlt_rect result; pthread_mutex_lock(&self->mutex); if (mlt_property_is_anim(self)) { struct mlt_animation_item_s item; item.property = mlt_property_init(); item.property->types = mlt_prop_rect; refresh_animation(self, fps, locale, length); mlt_animation_get_item(self->animation, &item, position); pthread_mutex_unlock(&self->mutex); result = mlt_property_get_rect(item.property, locale); mlt_property_close(item.property); } else { pthread_mutex_unlock(&self->mutex); result = mlt_property_get_rect(self, locale); } return result; } /** Set a nested properties object. * * \public \memberof mlt_property_s * \param self a property * \param properties the properties list to nest into \p self with \p name * \return true if error */ int mlt_property_set_properties(mlt_property self, mlt_properties properties) { pthread_mutex_lock(&self->mutex); clear_property(self); self->properties = properties; mlt_properties_inc_ref(properties); pthread_mutex_unlock(&self->mutex); return 0; } /** Get a nested properties object. * * \public \memberof mlt_property_s * \param self a property * \return the nested properties list */ mlt_properties mlt_property_get_properties(mlt_property self) { mlt_properties properties = NULL; pthread_mutex_lock(&self->mutex); properties = self->properties; pthread_mutex_unlock(&self->mutex); return properties; } /** Check if a property is animated. * * This is not a thread-safe function because it is used internally by * mlt_property_s under a lock. However, external callers should protect it. * \public \memberof mlt_property_s * \param self a property * \return true if the property is animated */ int mlt_property_is_anim(mlt_property self) { return self->animation || (self->prop_string && strchr(self->prop_string, '=')); } /** Check if a property is a color. * \public \memberof mlt_property_s * \param self a property * \return true if the property is color */ extern int mlt_property_is_color(mlt_property self) { int result = 0; if (self) { pthread_mutex_lock(&self->mutex); if (self->types & mlt_prop_color) { result = 1; } else { const char *value = self->prop_string; if (value && ((strlen(value) > 6 && value[0] == '#') || (strlen(value) > 7 && value[0] == '0' && value[1] == 'x'))) { result = 1; } } pthread_mutex_unlock(&self->mutex); } return result; } /** Check if a property is a rect. * \public \memberof mlt_property_s * \param self a property * \return true if the property is a rect */ extern int mlt_property_is_rect(mlt_property self) { int result = 0; if (self) { result = self->types & mlt_prop_rect; } return result; } mlt-7.38.0/src/framework/mlt_property.h000664 000000 000000 00000015477 15172202314 020075 0ustar00rootroot000000 000000 /** * \file mlt_property.h * \brief Property class declaration * \see mlt_property_s * * Copyright (C) 2003-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PROPERTY_H #define MLT_PROPERTY_H #include "mlt_export.h" #include "mlt_types.h" #if defined(__FreeBSD__) /* This header has existed since 1994 and defines __FreeBSD_version below. */ #include #endif #if defined(__GLIBC__) #include typedef locale_t mlt_locale_t; #elif defined(__APPLE__) || (defined(__FreeBSD_version) && __FreeBSD_version >= 900506) #include typedef locale_t mlt_locale_t; #elif defined(__OpenBSD__) /* XXX matches __nop_locale glue in libc++ */ typedef void *mlt_locale_t; #elif (defined _WIN32 && defined _LIBCPP_VERSION) struct mlt_locale_t; #else typedef char *mlt_locale_t; #endif MLT_EXPORT mlt_property mlt_property_init(); MLT_EXPORT void mlt_property_clear(mlt_property self); MLT_EXPORT int mlt_property_is_clear(mlt_property self); MLT_EXPORT int mlt_property_set_int(mlt_property self, int value); MLT_EXPORT int mlt_property_set_double(mlt_property self, double value); MLT_EXPORT int mlt_property_set_position(mlt_property self, mlt_position value); MLT_EXPORT int mlt_property_set_int64(mlt_property self, int64_t value); MLT_EXPORT int mlt_property_set_string(mlt_property self, const char *value); MLT_EXPORT int mlt_property_set_data(mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser); MLT_EXPORT int mlt_property_get_int(mlt_property self, double fps, mlt_locale_t); MLT_EXPORT double mlt_property_get_double(mlt_property self, double fps, mlt_locale_t); MLT_EXPORT mlt_position mlt_property_get_position(mlt_property self, double fps, mlt_locale_t); MLT_EXPORT int64_t mlt_property_get_int64(mlt_property self); MLT_EXPORT char *mlt_property_get_string_tf(mlt_property self, mlt_time_format); MLT_EXPORT char *mlt_property_get_string(mlt_property self); MLT_EXPORT char *mlt_property_get_string_l_tf(mlt_property self, mlt_locale_t, mlt_time_format); MLT_EXPORT char *mlt_property_get_string_l(mlt_property self, mlt_locale_t); MLT_EXPORT void *mlt_property_get_data(mlt_property self, int *length); MLT_EXPORT void mlt_property_close(mlt_property self); MLT_EXPORT void mlt_property_pass(mlt_property self, mlt_property that); MLT_EXPORT char *mlt_property_get_time(mlt_property self, mlt_time_format, double fps, mlt_locale_t); MLT_EXPORT int mlt_property_interpolate(mlt_property self, mlt_property points[], double progress, double fps, mlt_locale_t locale, mlt_keyframe_type interp); MLT_EXPORT double mlt_property_anim_get_double( mlt_property self, double fps, mlt_locale_t locale, int position, int length); MLT_EXPORT int mlt_property_anim_get_int( mlt_property self, double fps, mlt_locale_t locale, int position, int length); MLT_EXPORT char *mlt_property_anim_get_string( mlt_property self, double fps, mlt_locale_t locale, int position, int length); MLT_EXPORT int mlt_property_anim_set_double(mlt_property self, double value, double fps, mlt_locale_t locale, int position, int length, mlt_keyframe_type keyframe_type); MLT_EXPORT int mlt_property_anim_set_int(mlt_property self, int value, double fps, mlt_locale_t locale, int position, int length, mlt_keyframe_type keyframe_type); MLT_EXPORT int mlt_property_anim_set_string( mlt_property self, const char *value, double fps, mlt_locale_t locale, int position, int length); MLT_EXPORT mlt_animation mlt_property_get_animation(mlt_property self); MLT_EXPORT int mlt_property_is_anim(mlt_property self); MLT_EXPORT int mlt_property_set_color(mlt_property self, mlt_color value); MLT_EXPORT mlt_color mlt_property_get_color(mlt_property self, double fps, mlt_locale_t locale); MLT_EXPORT int mlt_property_anim_set_color(mlt_property self, mlt_color value, double fps, mlt_locale_t locale, int position, int length, mlt_keyframe_type keyframe_type); MLT_EXPORT mlt_color mlt_property_anim_get_color( mlt_property self, double fps, mlt_locale_t locale, int position, int length); MLT_EXPORT int mlt_property_set_rect(mlt_property self, mlt_rect value); MLT_EXPORT mlt_rect mlt_property_get_rect(mlt_property self, mlt_locale_t locale); MLT_EXPORT int mlt_property_anim_set_rect(mlt_property self, mlt_rect value, double fps, mlt_locale_t locale, int position, int length, mlt_keyframe_type keyframe_type); MLT_EXPORT mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, mlt_locale_t locale, int position, int length); MLT_EXPORT int mlt_property_set_properties(mlt_property self, mlt_properties properties); MLT_EXPORT mlt_properties mlt_property_get_properties(mlt_property self); MLT_EXPORT int mlt_property_is_color(mlt_property self); MLT_EXPORT int mlt_property_is_numeric(mlt_property self, mlt_locale_t locale); MLT_EXPORT int mlt_property_is_rect(mlt_property self); #endif mlt-7.38.0/src/framework/mlt_repository.c000664 000000 000000 00000053300 15172202314 020406 0ustar00rootroot000000 000000 /** * \file mlt_repository.c * \brief provides a map between service and shared objects * \see mlt_repository_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_repository.h" #include "mlt_factory.h" #include "mlt_log.h" #include "mlt_properties.h" #include "mlt_tokeniser.h" #include #include #include #include #include #include #include /** \brief Repository class * * The Repository is a collection of plugin modules and their services and service metadata. * * \extends mlt_properties_s * \properties \p language a cached list of user locales */ struct mlt_repository_s { struct mlt_properties_s parent; /// a list of object files mlt_properties consumers; /// a list of entry points for consumers mlt_properties filters; /// a list of entry points for filters mlt_properties links; /// a list of entry points for links mlt_properties producers; /// a list of entry points for producers mlt_properties transitions; /// a list of entry points for transitions }; /** Construct a new repository. * * \public \memberof mlt_repository_s * \param directory the full path of a directory from which to read modules * \return a new repository or NULL if failed */ mlt_repository mlt_repository_init(const char *directory) { // Safety check if (directory == NULL || strcmp(directory, "") == 0) return NULL; // Construct the repository mlt_repository self = calloc(1, sizeof(struct mlt_repository_s)); mlt_properties_init(&self->parent, self); self->consumers = mlt_properties_new(); self->filters = mlt_properties_new(); self->links = mlt_properties_new(); self->producers = mlt_properties_new(); self->transitions = mlt_properties_new(); // Get the directory list mlt_properties dir = mlt_properties_new(); int count = mlt_properties_dir_list(dir, directory, NULL, 0); int i; int plugin_count = 0; #ifdef _WIN32 char *syspath = getenv("PATH"); char *exedir = mlt_environment("MLT_APPDIR"); #ifdef NODEPLOY char *sep = "\\bin;"; #else char *sep = ";"; #endif char *newpath; newpath = calloc(1, 5 + strlen(exedir) + strlen(sep) + strlen(syspath) + 1); strcat(newpath, "PATH="); // len=5 strcat(newpath, exedir); strcat(newpath, sep); strcat(newpath, syspath); putenv(newpath); free(newpath); #endif mlt_tokeniser tokeniser = mlt_tokeniser_init(); int dl_length = mlt_tokeniser_parse_new(tokeniser, getenv("MLT_REPOSITORY_DENY"), ":"); // check if both qt5 and qt6 modules are available… int qt_module_count = 0; int glaxnimate_module_count = 0; for (i = 0; i < count; i++) { const char *object_name = mlt_properties_get_value(dir, i); qt_module_count += !!strstr(object_name, "libmltqt"); glaxnimate_module_count += !!strstr(object_name, "libmltglaxnimate"); } // …and not blocked for (int j = 0; j < dl_length; j++) { char *denyfile = mlt_tokeniser_get_string(tokeniser, j); qt_module_count -= !strncmp("libmltqt", denyfile, strlen("libmltqt")); glaxnimate_module_count -= !strncmp("libmltglaxnimate", denyfile, strlen("libmltglaxnimate")); } // Iterate over files for (i = 0; i < count; i++) { int flags = RTLD_NOW; const char *object_name = mlt_properties_get_value(dir, i); // Skip invalid current & parent entries if (!object_name) continue; size_t object_name_len = strlen(object_name); if ((object_name_len >= 2 && strcmp(object_name + object_name_len - 2, "/.") == 0) || (object_name_len >= 3 && strcmp(object_name + object_name_len - 3, "/..") == 0)) continue; // check if the plugin was asked to be skipped through MLT_REPOSITORY_DENY int ignore = 0; for (int j = 0; j < dl_length; j++) { char *denyfile = calloc(1, strlen(directory) + strlen(mlt_tokeniser_get_string(tokeniser, j)) + 3); sprintf(denyfile, "%s/%s.", directory, mlt_tokeniser_get_string(tokeniser, j)); ignore += !strncmp(object_name, denyfile, strlen(denyfile)); free(denyfile); } // in case we have both qt modules, we block qt6 to avoid conflicts if ((qt_module_count == 2 && strstr(object_name, "libmltqt6")) || (glaxnimate_module_count == 2 && strstr(object_name, "libmltglaxnimate-qt6"))) { ignore = 1; } if (ignore) { mlt_log_info(NULL, "%s: skip plugin %s\n", __FUNCTION__, object_name); continue; } mlt_log_debug(NULL, "%s: processing plugin at %s\n", __FUNCTION__, object_name); // Open the shared object void *object = dlopen(object_name, flags); if (object != NULL) { // Get the registration function mlt_repository_callback symbol_ptr = dlsym(object, "mlt_register"); // Call the registration function if (symbol_ptr != NULL) { symbol_ptr(self); // Register the object file for closure mlt_properties_set_data(&self->parent, object_name, object, 0, (mlt_destructor) dlclose, NULL); ++plugin_count; } else { mlt_log_warning(NULL, "%s: failed to register %s\n (%s)\n", __FUNCTION__, object_name, dlerror()); dlclose(object); } } else if (strstr(object_name, "libmlt")) { mlt_log_warning(NULL, "%s: failed to dlopen %s\n (%s)\n", __FUNCTION__, object_name, dlerror()); } } if (!plugin_count) mlt_log_error(NULL, "%s: no plugins found in \"%s\"\n", __FUNCTION__, directory); mlt_properties_close(dir); mlt_tokeniser_close(tokeniser); return self; } /** Create a properties list for a service holding a function pointer to its constructor function. * * \private \memberof mlt_repository_s * \param symbol a pointer to a function that can create the service. * \return a properties list */ static mlt_properties new_service(void *symbol) { mlt_properties properties = mlt_properties_new(); mlt_properties_set_data(properties, "symbol", symbol, 0, NULL, NULL); return properties; } /** Register a service with the repository. * * Typically, this is invoked by a module within its mlt_register(). * * \public \memberof mlt_repository_s * \param self a repository * \param service_type a service class * \param service the name of a service * \param symbol a pointer to a function to create the service */ void mlt_repository_register(mlt_repository self, mlt_service_type service_type, const char *service, mlt_register_callback symbol) { // Add the entry point to the corresponding service list switch (service_type) { case mlt_service_consumer_type: mlt_properties_set_data(self->consumers, service, new_service(symbol), 0, (mlt_destructor) mlt_properties_close, NULL); break; case mlt_service_filter_type: mlt_properties_set_data(self->filters, service, new_service(symbol), 0, (mlt_destructor) mlt_properties_close, NULL); break; case mlt_service_link_type: mlt_properties_set_data(self->links, service, new_service(symbol), 0, (mlt_destructor) mlt_properties_close, NULL); break; case mlt_service_producer_type: mlt_properties_set_data(self->producers, service, new_service(symbol), 0, (mlt_destructor) mlt_properties_close, NULL); break; case mlt_service_transition_type: mlt_properties_set_data(self->transitions, service, new_service(symbol), 0, (mlt_destructor) mlt_properties_close, NULL); break; default: mlt_log_error(NULL, "%s: Unable to register \"%s\"\n", __FUNCTION__, service); break; } } /** Get the repository properties for particular service class. * * \private \memberof mlt_repository_s * \param self a repository * \param type a service class * \param service the name of a service * \return a properties list or NULL if error */ static mlt_properties get_service_properties(mlt_repository self, mlt_service_type type, const char *service) { mlt_properties service_properties = NULL; // Get the entry point from the corresponding service list switch (type) { case mlt_service_consumer_type: service_properties = mlt_properties_get_data(self->consumers, service, NULL); break; case mlt_service_filter_type: service_properties = mlt_properties_get_data(self->filters, service, NULL); break; case mlt_service_link_type: service_properties = mlt_properties_get_data(self->links, service, NULL); break; case mlt_service_producer_type: service_properties = mlt_properties_get_data(self->producers, service, NULL); break; case mlt_service_transition_type: service_properties = mlt_properties_get_data(self->transitions, service, NULL); break; default: break; } return service_properties; } /** Construct a new instance of a service. * * \public \memberof mlt_repository_s * \param self a repository * \param profile a \p mlt_profile to give the service * \param type a service class * \param service the name of the service * \param input an optional argument to the service constructor */ void *mlt_repository_create(mlt_repository self, mlt_profile profile, mlt_service_type type, const char *service, const void *input) { mlt_properties properties = get_service_properties(self, type, service); if (properties != NULL) { mlt_register_callback symbol_ptr = mlt_properties_get_data(properties, "symbol", NULL); // Construct the service return (symbol_ptr != NULL) ? symbol_ptr(profile, type, service, input) : NULL; } return NULL; } /** Destroy a repository and free its resources. * * \public \memberof mlt_repository_s * \param self a repository */ void mlt_repository_close(mlt_repository self) { mlt_properties_close(self->consumers); mlt_properties_close(self->filters); mlt_properties_close(self->producers); mlt_properties_close(self->links); mlt_properties_close(self->transitions); mlt_properties_close(&self->parent); free(self); } /** Get the list of registered consumers. * * \public \memberof mlt_repository_s * \param self a repository * \return a properties list containing all of the consumers */ mlt_properties mlt_repository_consumers(mlt_repository self) { return self->consumers; } /** Get the list of registered filters. * * \public \memberof mlt_repository_s * \param self a repository * \return a properties list of all of the filters */ mlt_properties mlt_repository_filters(mlt_repository self) { return self->filters; } /** Get the list of registered links. * * \public \memberof mlt_repository_s * \param self a repository * \return a properties list of all of the links */ mlt_properties mlt_repository_links(mlt_repository self) { return self->links; } /** Get the list of registered producers. * * \public \memberof mlt_repository_s * \param self a repository * \return a properties list of all of the producers */ mlt_properties mlt_repository_producers(mlt_repository self) { return self->producers; } /** Get the list of registered transitions. * * \public \memberof mlt_repository_s * \param self a repository * \return a properties list of all of the transitions */ mlt_properties mlt_repository_transitions(mlt_repository self) { return self->transitions; } /** Register the metadata for a service. * * IMPORTANT: mlt_repository will take responsibility for deallocating the metadata properties * that you supply! * * \public \memberof mlt_repository_s * \param self a repository * \param type a service class * \param service the name of a service * \param callback the pointer to a function that can supply metadata * \param callback_data an opaque user data pointer to be supplied on the callback */ void mlt_repository_register_metadata(mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback callback, void *callback_data) { mlt_properties service_properties = get_service_properties(self, type, service); mlt_properties_set_data(service_properties, "metadata_cb", callback, 0, NULL, NULL); mlt_properties_set_data(service_properties, "metadata_cb_data", callback_data, 0, NULL, NULL); } /** Get the metadata about a service. * * Returns NULL if service or its metadata are unavailable. * * \public \memberof mlt_repository_s * \param self a repository * \param type a service class * \param service the name of a service * \return the service metadata as a structured properties list */ mlt_properties mlt_repository_metadata(mlt_repository self, mlt_service_type type, const char *service) { mlt_properties metadata = NULL; mlt_properties properties = get_service_properties(self, type, service); // If this is a valid service if (properties) { // Lookup cached metadata metadata = mlt_properties_get_data(properties, "metadata", NULL); if (!metadata) { // Not cached, so get the registered metadata callback function mlt_metadata_callback callback = mlt_properties_get_data(properties, "metadata_cb", NULL); // If a metadata callback function is registered if (callback) { // Fetch the callback data arg void *data = mlt_properties_get_data(properties, "metadata_cb_data", NULL); // Fetch the metadata through the callback metadata = callback(type, service, data); // Cache the metadata if (metadata) { // Most links wrap a filter and references the filter's metadata mlt_destructor dtor = (type == mlt_service_link_type) ? NULL : (mlt_destructor) mlt_properties_close; // Include dellocation and serialisation mlt_properties_set_data(properties, "metadata", metadata, 0, dtor, (mlt_serialiser) mlt_properties_serialise_yaml); } } } } return metadata; } /** Try to determine the locale from some commonly used environment variables. * * \private \memberof mlt_repository_s * \return a string containing the locale id or NULL if unknown */ static char *getenv_locale() { char *s = getenv("LANGUAGE"); if (s && s[0]) return s; s = getenv("LC_ALL"); if (s && s[0]) return s; s = getenv("LC_MESSAGES"); if (s && s[0]) return s; s = getenv("LANG"); if (s && s[0]) return s; return NULL; } /** Return a list of user-preferred language codes taken from environment variables. * * A module should use this to locate a localized YAML Tiny file from which to build * its metadata structured properties. * * \public \memberof mlt_repository_s * \param self a repository * \return a properties list that is a list (not a map) of locales, defaults to "en" if not * overridden by environment variables, in order: LANGUAGE, LC_ALL, LC_MESSAGES, LANG */ mlt_properties mlt_repository_languages(mlt_repository self) { mlt_properties languages = mlt_properties_get_data(&self->parent, "languages", NULL); if (languages) return languages; languages = mlt_properties_new(); char *locale = getenv_locale(); if (locale) { locale = strdup(locale); mlt_tokeniser tokeniser = mlt_tokeniser_init(); int count = mlt_tokeniser_parse_new(tokeniser, locale, ":"); if (count) { int i; for (i = 0; i < count; i++) { char *locale = mlt_tokeniser_get_string(tokeniser, i); if (strcmp(locale, "C") == 0 || strcmp(locale, "POSIX") == 0) locale = "en"; else if (strlen(locale) > 2) locale[2] = 0; char string[21]; snprintf(string, sizeof(string), "%d", i); mlt_properties_set(languages, string, locale); } } else { mlt_properties_set(languages, "0", "en"); } free(locale); mlt_tokeniser_close(tokeniser); } else { mlt_properties_set(languages, "0", "en"); } mlt_properties_set_data(&self->parent, "languages", languages, 0, (mlt_destructor) mlt_properties_close, NULL); return languages; } static void list_presets(mlt_properties properties, const char *path, const char *dirname) { DIR *dir = opendir(dirname); if (dir) { struct dirent *de = readdir(dir); char fullname[PATH_MAX]; while (de != NULL) { if (de->d_name[0] != '.' && de->d_name[strlen(de->d_name) - 1] != '~') { struct stat info; int n = snprintf(fullname, sizeof(fullname), "%s/%s", dirname, de->d_name); if (n < 0 || n >= sizeof(fullname)) continue; mlt_stat(fullname, &info); if (S_ISDIR(info.st_mode)) { // recurse into subdirectories char sub[PATH_MAX]; if (path) { n = snprintf(sub, sizeof(sub), "%s/%s", path, de->d_name); if (n < 0 || n >= sizeof(sub)) continue; } else { strncpy(sub, de->d_name, sizeof(sub)); } list_presets(properties, sub, fullname); } else { // load the preset mlt_properties preset = mlt_properties_load(fullname); if (preset && mlt_properties_count(preset)) { n = snprintf(fullname, sizeof(fullname), "%s/%s", path, de->d_name); if (n < 0 || n >= sizeof(fullname)) continue; mlt_properties_set_data(properties, fullname, preset, 0, (mlt_destructor) mlt_properties_close, NULL); } } } de = readdir(dir); } closedir(dir); } } /** Get the list of presets. * * \public \memberof mlt_repository_s * \return a properties list of all the presets */ mlt_properties mlt_repository_presets() { mlt_properties result = mlt_properties_new(); list_presets(result, NULL, mlt_environment("MLT_PRESETS_PATH")); return result; } mlt-7.38.0/src/framework/mlt_repository.h000664 000000 000000 00000010570 15172202314 020415 0ustar00rootroot000000 000000 /** * \file mlt_repository.h * \brief provides a map between service and shared objects * \see mlt_repository_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_REPOSITORY_H #define MLT_REPOSITORY_H #include "mlt_export.h" #include "mlt_profile.h" #include "mlt_types.h" /** This callback is the main entry point into a module, which must be exported * with the symbol "mlt_register". * * Inside the callback, the module registers the additional callbacks below. */ typedef void (*mlt_repository_callback)(mlt_repository); /** The callback function that modules implement to construct a service. */ typedef void *(*mlt_register_callback)(mlt_profile, mlt_service_type, const char * /* service name */, const void * /* arg */); /** The callback function that modules implement to supply metadata as a properties list. */ typedef mlt_properties (*mlt_metadata_callback)(mlt_service_type, const char * /* service name */, void * /* callback_data */); /** A convenience macro to create an entry point for service registration. */ #define MLT_REPOSITORY void mlt_register(mlt_repository repository) /** A convenience macro to a register service in a more declarative manner. */ #define MLT_REGISTER(type, service, symbol) \ (mlt_repository_register(repository, (type), (service), (mlt_register_callback) (symbol))) /** A convenience macro to a register metadata in a more declarative manner. */ #define MLT_REGISTER_METADATA(type, service, callback, data) \ (mlt_repository_register_metadata(repository, \ (type), \ (service), \ (mlt_metadata_callback) (callback), \ (data))) MLT_EXPORT mlt_repository mlt_repository_init(const char *directory); MLT_EXPORT void mlt_repository_register(mlt_repository self, mlt_service_type service_type, const char *service, mlt_register_callback); MLT_EXPORT void *mlt_repository_create(mlt_repository self, mlt_profile profile, mlt_service_type type, const char *service, const void *arg); MLT_EXPORT void mlt_repository_close(mlt_repository self); MLT_EXPORT mlt_properties mlt_repository_consumers(mlt_repository self); MLT_EXPORT mlt_properties mlt_repository_filters(mlt_repository self); MLT_EXPORT mlt_properties mlt_repository_links(mlt_repository self); MLT_EXPORT mlt_properties mlt_repository_producers(mlt_repository self); MLT_EXPORT mlt_properties mlt_repository_transitions(mlt_repository self); MLT_EXPORT void mlt_repository_register_metadata(mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback, void *callback_data); MLT_EXPORT mlt_properties mlt_repository_metadata(mlt_repository self, mlt_service_type type, const char *service); MLT_EXPORT mlt_properties mlt_repository_languages(mlt_repository self); MLT_EXPORT mlt_properties mlt_repository_presets(); #endif mlt-7.38.0/src/framework/mlt_service.c000664 000000 000000 00000075530 15172202314 017640 0ustar00rootroot000000 000000 /** * \file mlt_service.c * \brief interface definition for all service classes * \see mlt_service_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_service.h" #include "mlt_cache.h" #include "mlt_factory.h" #include "mlt_filter.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_producer.h" #include #include #include #include /* IMPORTANT NOTES The base service implements a null frame producing service - as such, it is functional without extension and will produce test cards frames and PAL sized audio frames. PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT CONTROL THIS IN EXTENDING CLASSES. */ /** \brief private service definition */ typedef struct { int size; int count; mlt_service *in; mlt_service out; int filter_count; int filter_size; mlt_filter *filters; pthread_mutex_t mutex; } mlt_service_base; /* Private methods */ static void mlt_service_disconnect(mlt_service self); static void mlt_service_connect(mlt_service self, mlt_service that); static int service_get_frame(mlt_service self, mlt_frame_ptr frame, int index); /** Initialize a service. * * \public \memberof mlt_service_s * \param self the service structure to initialize * \param child pointer to the child object for the subclass * \return true if there was an error */ int mlt_service_init(mlt_service self, void *child) { int error = 0; // Initialise everything to NULL memset(self, 0, sizeof(struct mlt_service_s)); // Assign the child self->child = child; // Generate local space self->local = calloc(1, sizeof(mlt_service_base)); // Associate the methods self->get_frame = service_get_frame; // Initialise the properties error = mlt_properties_init(&self->parent, self); if (error == 0) { self->parent.close = (mlt_destructor) mlt_service_close; self->parent.close_object = self; mlt_events_init(&self->parent); mlt_events_register(&self->parent, "service-changed"); mlt_events_register(&self->parent, "property-changed"); pthread_mutex_init(&((mlt_service_base *) self->local)->mutex, NULL); } return error; } /** Acquire a mutual exclusion lock on this service. * * \public \memberof mlt_service_s * \param self the service to lock */ void mlt_service_lock(mlt_service self) { if (self != NULL) pthread_mutex_lock(&((mlt_service_base *) self->local)->mutex); } /** Release a mutual exclusion lock on this service. * * \public \memberof mlt_service_s * \param self the service to unlock */ void mlt_service_unlock(mlt_service self) { if (self != NULL) pthread_mutex_unlock(&((mlt_service_base *) self->local)->mutex); } /** Identify the subclass of the service. * * \public \memberof mlt_service_s * \param self a service * \return the subclass */ mlt_service_type mlt_service_identify(mlt_service self) { mlt_service_type type = mlt_service_invalid_type; if (self != NULL) { mlt_properties properties = MLT_SERVICE_PROPERTIES(self); char *mlt_type = mlt_properties_get(properties, "mlt_type"); char *resource = mlt_properties_get(properties, "resource"); if (mlt_type == NULL) type = mlt_service_unknown_type; else if (resource != NULL && !strcmp(resource, "")) type = mlt_service_playlist_type; else if (resource != NULL && !strcmp(resource, "")) type = mlt_service_tractor_type; else if (resource != NULL && !strcmp(resource, "")) type = mlt_service_multitrack_type; else if (!strcmp(mlt_type, "mlt_producer")) type = mlt_service_producer_type; else if (!strcmp(mlt_type, "producer")) type = mlt_service_producer_type; else if (!strcmp(mlt_type, "filter")) type = mlt_service_filter_type; else if (!strcmp(mlt_type, "transition")) type = mlt_service_transition_type; else if (!strcmp(mlt_type, "chain")) type = mlt_service_chain_type; else if (!strcmp(mlt_type, "consumer")) type = mlt_service_consumer_type; else if (!strcmp(mlt_type, "link")) type = mlt_service_link_type; else type = mlt_service_unknown_type; } return type; } /** Connect a producer to the service. * * \public \memberof mlt_service_s * \param self a service * \param producer a producer * \param index which of potentially multiple producers to this service (0 based) * \return 0 for success, -1 for error, or 3 if \p producer is already connected to \p self */ int mlt_service_connect_producer(mlt_service self, mlt_service producer, int index) { if (!self) return -1; int i = 0; // Get the service base mlt_service_base *base = self->local; // Special case 'track' index - only works for last filter(s) in a particular chain // but allows a filter to apply to the output frame regardless of which track it comes from if (index == -1) index = 0; // Check if the producer is already registered with this service for (i = 0; i < base->count; i++) if (base->in[i] == producer) return 3; // Allocate space if (index >= base->size) { int new_size = base->size + index + 10; base->in = realloc(base->in, new_size * sizeof(mlt_service)); if (base->in != NULL) { for (i = base->size; i < new_size; i++) base->in[i] = NULL; base->size = new_size; } } // If we have space, assign the input if (producer != NULL && base->in != NULL && index >= 0 && index < base->size) { // Get the current service mlt_service current = (index < base->count) ? base->in[index] : NULL; // Increment the reference count on this producer mlt_properties_inc_ref(MLT_SERVICE_PROPERTIES(producer)); // Now we disconnect the producer service from its consumer mlt_service_disconnect(producer); // Add the service to index specified base->in[index] = producer; // Determine the number of active tracks if (index >= base->count) base->count = index + 1; // Now we connect the producer to its connected consumer mlt_service_connect(producer, self); // Close the current service mlt_service_close(current); // Inform caller that all went well return 0; } else { return -1; } } /** Insert a producer connected to the service. * * mlt_service_connect_producer() appends or overwrites a producer at input * \p index whereas this function inserts pushing all of the inputs down by * 1 in the list of inputs. * * \public \memberof mlt_service_s * \param self a service * \param producer a producer * \param index which of potentially multiple producers to this service (0 based) * \return 0 for success, -1 for error, or 3 if \p producer is already connected to \p self */ int mlt_service_insert_producer(mlt_service self, mlt_service producer, int index) { // Get the service base mlt_service_base *base = self->local; if (index >= base->count) return mlt_service_connect_producer(self, producer, index); int i = 0; // Special case 'track' index - only works for last filter(s) in a particular chain // but allows a filter to apply to the output frame regardless of which track it comes from if (index == -1) index = 0; // Check if the producer is already registered with this service. for (i = 0; i < base->count; i++) if (base->in[i] == producer) return 3; // Allocate space if needed. if (base->count + 1 > base->size) { int new_size = base->size + 10; base->in = realloc(base->in, new_size * sizeof(mlt_service)); if (base->in != NULL) { memset(&base->in[base->size], 0, new_size - base->size); base->size = new_size; } } // If we have space, assign the input if (base->in && index >= 0 && index < base->size) { // Increment the reference count on this producer. if (producer != NULL) mlt_properties_inc_ref(MLT_SERVICE_PROPERTIES(producer)); // Disconnect the producer from its consumer. mlt_service_disconnect(producer); // Make room in the list for the producer. memmove(&base->in[index + 1], &base->in[index], (base->count - index) * sizeof(mlt_service)); // Add the service to index specified. base->in[index] = producer; // Increase the number of active tracks. base->count++; // Connect the producer to its connected consumer. mlt_service_connect(producer, self); // Inform caller that all went well return 0; } else { return -1; } } /** Remove the N-th producer. * * \public \memberof mlt_service_s * \param self a service * \param index which producer to remove (0-based) * \return true if there was an error */ int mlt_service_disconnect_producer(mlt_service self, int index) { mlt_service_base *base = self->local; if (base->in && index >= 0 && index < base->count) { mlt_service current = base->in[index]; if (current) { // Close the current producer. mlt_service_disconnect(current); mlt_service_close(current); base->in[index] = NULL; // Contract the list of producers. for (; index + 1 < base->count; index++) base->in[index] = base->in[index + 1]; base->count--; return 0; } } return -1; } /** Remove the all the attached producers * * \public \memberof mlt_service_s * \param self a service * \return the number of successfully disconnected producers */ int mlt_service_disconnect_all_producers(mlt_service self) { int disconnected = 0, i = 0; mlt_service_base *base = self->local; if (base->in) { for (i = 0; i < base->count; i++) { mlt_service current = base->in[i]; if (current) { mlt_service_close(current); disconnected++; } base->in[i] = NULL; } base->count = 0; } return disconnected; } /** Disconnect a service from its consumer. * * \private \memberof mlt_service_s * \param self a service */ static void mlt_service_disconnect(mlt_service self) { if (self != NULL) { // Get the service base mlt_service_base *base = self->local; // Disconnect base->out = NULL; } } /** Set the consumer this service is connected to. * * Set the value that is returned by mlt_service_consumer() * * \public \memberof mlt_service_s * \param self a service * \param consumer the consumer to set */ void mlt_service_set_consumer(mlt_service self, mlt_service consumer) { if (self) { // Get the service base mlt_service_base *base = self->local; // Return the connected consumer base->out = consumer; } } /** Obtain the consumer a service is connected to. * * \public \memberof mlt_service_s * \param self a service * \return the consumer */ mlt_service mlt_service_consumer(mlt_service self) { if (self) { // Get the service base mlt_service_base *base = self->local; // Return the connected consumer return base->out; } else return self; } /** Obtain the producer a service is connected to. * * \public \memberof mlt_service_s * \param self a service * \return the last-most producer */ mlt_service mlt_service_producer(mlt_service self) { if (self) { // Get the service base mlt_service_base *base = self->local; // Return the connected producer return base->count > 0 ? base->in[base->count - 1] : NULL; } else return self; } /** Associate a service to a consumer. * * Overwrites connection to any existing consumer. * \private \memberof mlt_service_s * \param self a service * \param that a consumer */ static void mlt_service_connect(mlt_service self, mlt_service that) { if (self != NULL) { // Get the service base mlt_service_base *base = self->local; // There's a bit more required here... base->out = that; } } /** Get the first connected producer. * * \public \memberof mlt_service_s * \param self a service * \return the first producer */ mlt_service mlt_service_get_producer(mlt_service self) { mlt_service producer = NULL; // Get the service base mlt_service_base *base = self->local; if (base->in != NULL) producer = base->in[0]; return producer; } /** Default implementation of the get_frame virtual function. * * \private \memberof mlt_service_s * \param self a service * \param[out] frame a frame by reference * \param index as determined by the producer * \return false */ static int service_get_frame(mlt_service self, mlt_frame_ptr frame, int index) { mlt_service_base *base = self->local; if (index < base->count) { mlt_service producer = base->in[index]; if (producer != NULL) return mlt_service_get_frame(producer, frame, index); } *frame = mlt_frame_init(self); return 0; } /** Return the properties object. * * \public \memberof mlt_service_s * \param self a service * \return the properties */ mlt_properties mlt_service_properties(mlt_service self) { return self != NULL ? &self->parent : NULL; } /** Recursively apply attached filters. * * \public \memberof mlt_service_s * \param self a service * \param frame a frame * \param index used to track depth of recursion, top caller should supply 0 */ void mlt_service_apply_filters(mlt_service self, mlt_frame frame, int index) { if (!self) return; int i; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties service_properties = MLT_SERVICE_PROPERTIES(self); mlt_service_base *base = self->local; mlt_position position = mlt_frame_get_position(frame); mlt_position self_in = mlt_properties_get_position(service_properties, "in"); mlt_position self_out = mlt_properties_get_position(service_properties, "out"); if (index == 0 || mlt_properties_get_int(service_properties, "_filter_private") == 0) { // Process the frame with the attached filters for (i = 0; i < base->filter_count; i++) { if (base->filters[i] != NULL) { mlt_position in = mlt_filter_get_in(base->filters[i]); mlt_position out = mlt_filter_get_out(base->filters[i]); int disable = mlt_properties_get_int(MLT_FILTER_PROPERTIES(base->filters[i]), "disable"); if (!disable && ((in == 0 && out == 0) || (position >= in && (position <= out || out == 0)))) { mlt_properties_set_position(frame_properties, "in", in == 0 ? self_in : in); mlt_properties_set_position(frame_properties, "out", out == 0 ? self_out : out); mlt_filter_process(base->filters[i], frame); mlt_service_apply_filters(MLT_FILTER_SERVICE(base->filters[i]), frame, index + 1); } } } } } /** Obtain a frame. * * \public \memberof mlt_service_s * \param self a service * \param[out] frame a frame by reference * \param index as determined by the producer * \return true if there was an error */ int mlt_service_get_frame(mlt_service self, mlt_frame_ptr frame, int index) { int result = 0; // Lock the service mlt_service_lock(self); // Ensure that the frame is NULL *frame = NULL; // Only process if we have a valid service if (self != NULL && self->get_frame != NULL) { mlt_properties properties = MLT_SERVICE_PROPERTIES(self); mlt_position in = mlt_properties_get_position(properties, "in"); mlt_position out = mlt_properties_get_position(properties, "out"); mlt_position position = -1; if (mlt_service_identify(self) == mlt_service_producer_type || mlt_service_identify(self) == mlt_service_chain_type) { position = mlt_producer_position(MLT_PRODUCER(self)); } result = self->get_frame(self, frame, index); if (result == 0) { mlt_properties_inc_ref(properties); properties = MLT_FRAME_PROPERTIES(*frame); if (in >= 0 && out > 0) { mlt_properties_set_position(properties, "in", in); mlt_properties_set_position(properties, "out", out); } mlt_service_apply_filters(self, *frame, 1); mlt_deque_push_back(MLT_FRAME_SERVICE_STACK(*frame), self); if (position > -1 && mlt_properties_get_int(MLT_SERVICE_PROPERTIES(self), "_need_previous_next")) { // Save the new position from self->get_frame mlt_position new_position = mlt_producer_position(MLT_PRODUCER(self)); // Get the preceding frame, unfiltered mlt_frame previous_frame; mlt_producer_seek(MLT_PRODUCER(self), position - 1); result = self->get_frame(self, &previous_frame, index); if (!result) mlt_properties_set_data(properties, "previous frame", previous_frame, 0, (mlt_destructor) mlt_frame_close, NULL); // Get the following frame, unfiltered mlt_frame next_frame; mlt_producer_seek(MLT_PRODUCER(self), position + 1); result = self->get_frame(self, &next_frame, index); if (!result) { mlt_properties_set_data(properties, "next frame", next_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } // Restore the new position mlt_producer_seek(MLT_PRODUCER(self), new_position); } } } // Make sure we return a frame if (*frame == NULL) *frame = mlt_frame_init(self); // Unlock the service mlt_service_unlock(self); return result; } /** The service-changed event handler. * * \private \memberof mlt_service_s * \param owner ignored * \param self the service on which the "service-changed" event is fired */ static void mlt_service_filter_changed(mlt_service owner, mlt_service self) { mlt_events_fire(MLT_SERVICE_PROPERTIES(self), "service-changed", mlt_event_data_none()); } /** The property-changed event handler. * * \private \memberof mlt_service_s * \param owner ignored * \param self the service on which the "property-changed" event is fired * \param event_data the event data containing the name of the property that changed */ static void mlt_service_filter_property_changed(mlt_service owner, mlt_service self, mlt_event_data event_data) { mlt_events_fire(MLT_SERVICE_PROPERTIES(self), "property-changed", event_data); } /** Attach a filter. * * \public \memberof mlt_service_s * \param self a service * \param filter the filter to attach * \return true if there was an error */ int mlt_service_attach(mlt_service self, mlt_filter filter) { int error = self == NULL || filter == NULL; if (error == 0) { int i = 0; mlt_properties properties = MLT_SERVICE_PROPERTIES(self); mlt_service_base *base = self->local; for (i = 0; error == 0 && i < base->filter_count; i++) if (base->filters[i] == filter) error = 1; if (error == 0) { if (base->filter_count == base->filter_size) { base->filter_size += 10; base->filters = realloc(base->filters, base->filter_size * sizeof(mlt_filter)); } if (base->filters != NULL) { mlt_properties props = MLT_FILTER_PROPERTIES(filter); mlt_properties_inc_ref(MLT_FILTER_PROPERTIES(filter)); base->filters[base->filter_count++] = filter; mlt_properties_set_data(props, "service", self, 0, NULL, NULL); mlt_events_fire(properties, "service-changed", mlt_event_data_none()); mlt_events_fire(props, "service-changed", mlt_event_data_none()); mlt_service cp = mlt_properties_get_data(properties, "_cut_parent", NULL); if (cp) mlt_events_fire(MLT_SERVICE_PROPERTIES(cp), "service-changed", mlt_event_data_none()); mlt_events_listen(props, self, "service-changed", (mlt_listener) mlt_service_filter_changed); mlt_events_listen(props, self, "property-changed", (mlt_listener) mlt_service_filter_property_changed); } else { error = 2; } } } return error; } /** Detach a filter. * * \public \memberof mlt_service_s * \param self a service * \param filter the filter to detach * \return true if there was an error */ int mlt_service_detach(mlt_service self, mlt_filter filter) { int error = self == NULL || filter == NULL; if (error == 0) { int i = 0; mlt_service_base *base = self->local; mlt_properties properties = MLT_SERVICE_PROPERTIES(self); for (i = 0; i < base->filter_count; i++) if (base->filters[i] == filter) break; if (i < base->filter_count) { base->filters[i] = NULL; for (i++; i < base->filter_count; i++) base->filters[i - 1] = base->filters[i]; base->filter_count--; mlt_events_disconnect(MLT_FILTER_PROPERTIES(filter), self); mlt_filter_close(filter); mlt_events_fire(properties, "service-changed", mlt_event_data_none()); } } return error; } /** Get the number of filters attached. * * \public \memberof mlt_service_s * \param self a service * \return the number of attached filters or -1 if there was an error */ int mlt_service_filter_count(mlt_service self) { int result = -1; if (self) { mlt_service_base *base = self->local; result = base->filter_count; } return result; } /** Reorder the attached filters. * * \public \memberof mlt_service_s * \param self a service * \param from the current index value of the filter to move * \param to the new index value for the filter specified in \p from * \return true if there was an error */ int mlt_service_move_filter(mlt_service self, int from, int to) { int error = -1; if (self) { mlt_service_base *base = self->local; if (from < 0) from = 0; if (from >= base->filter_count) from = base->filter_count - 1; if (to < 0) to = 0; if (to >= base->filter_count) to = base->filter_count - 1; if (from != to && base->filter_count > 1) { mlt_filter filter = base->filters[from]; int i; if (from > to) { for (i = from; i > to; i--) base->filters[i] = base->filters[i - 1]; } else { for (i = from; i < to; i++) base->filters[i] = base->filters[i + 1]; } base->filters[to] = filter; mlt_events_fire(MLT_SERVICE_PROPERTIES(self), "service-changed", mlt_event_data_none()); error = 0; } } return error; } /** Retrieve an attached filter. * * \public \memberof mlt_service_s * \param self a service * \param index which one of potentially multiple filters * \return the filter or null if there was an error */ mlt_filter mlt_service_filter(mlt_service self, int index) { mlt_filter filter = NULL; if (self != NULL) { mlt_service_base *base = self->local; if (index >= 0 && index < base->filter_count) filter = base->filters[index]; } return filter; } /** Retrieve the profile. * * \public \memberof mlt_service_s * \param self a service * \return the profile */ mlt_profile mlt_service_profile(mlt_service self) { return self ? mlt_properties_get_data(MLT_SERVICE_PROPERTIES(self), "_profile", NULL) : NULL; } /** Set the profile for a service. * * \public \memberof mlt_service_s * \param self a service * \param profile the profile to set onto the service */ void mlt_service_set_profile(mlt_service self, mlt_profile profile) { mlt_properties_set_data(MLT_SERVICE_PROPERTIES(self), "_profile", profile, 0, NULL, NULL); } /** Destroy a service. * * \public \memberof mlt_service_s * \param self the service to destroy */ void mlt_service_close(mlt_service self) { if (self != NULL && mlt_properties_dec_ref(MLT_SERVICE_PROPERTIES(self)) <= 0) { if (self->close != NULL) { self->close(self->close_object); } else { mlt_service_base *base = self->local; int i = 0; int count = base->filter_count; mlt_events_block(MLT_SERVICE_PROPERTIES(self), self); while (count--) mlt_service_detach(self, base->filters[0]); free(base->filters); for (i = 0; i < base->count; i++) if (base->in[i] != NULL) mlt_service_close(base->in[i]); self->parent.close = NULL; free(base->in); pthread_mutex_destroy(&base->mutex); free(base); mlt_properties_close(&self->parent); } } } /** Release a service's cache items. * * \private \memberof mlt_service_s * \param self a service */ void mlt_service_cache_purge(mlt_service self) { mlt_properties caches = mlt_properties_get_data(mlt_global_properties(), "caches", NULL); if (caches) { int i = mlt_properties_count(caches); while (i--) { mlt_cache_purge(mlt_properties_get_data_at(caches, i, NULL), self); mlt_properties_set_data(mlt_global_properties(), mlt_properties_get_name(caches, i), NULL, 0, NULL, NULL); } } } /** Lookup the cache object for a service. * * \private \memberof mlt_service_s * \param self a service * \param name a name for the object * \return a cache */ static mlt_cache get_cache(mlt_service self, const char *name) { mlt_cache result = NULL; mlt_properties caches = mlt_properties_get_data(mlt_global_properties(), "caches", NULL); if (!caches) { caches = mlt_properties_new(); mlt_properties_set_data(mlt_global_properties(), "caches", caches, 0, (mlt_destructor) mlt_properties_close, NULL); } if (caches) { result = mlt_properties_get_data(caches, name, NULL); if (!result) { result = mlt_cache_init(); mlt_properties_set_data(caches, name, result, 0, (mlt_destructor) mlt_cache_close, NULL); } } return result; } /** Put an object into a service's cache. * * \public \memberof mlt_service_s * \param self a service * \param name a name for the object that is unique to the service class, but not to the instance * \param data an opaque pointer to the object to put into the cache * \param size the number of bytes pointed to by data * \param destructor a function that releases the data */ void mlt_service_cache_put( mlt_service self, const char *name, void *data, int size, mlt_destructor destructor) { mlt_log(self, MLT_LOG_DEBUG, "%s: name %s object %p data %p\n", __FUNCTION__, name, self, data); mlt_cache cache = get_cache(self, name); if (cache) mlt_cache_put(cache, self, data, size, destructor); } /** Get an object from a service's cache. * * \public \memberof mlt_service_s * \param self a service * \param name a name for the object that is unique to the service class, but not to the instance * \return a cache item or NULL if an object is not found * \see mlt_cache_item_data */ mlt_cache_item mlt_service_cache_get(mlt_service self, const char *name) { mlt_log(self, MLT_LOG_DEBUG, "%s: name %s object %p\n", __FUNCTION__, name, self); mlt_cache_item result = NULL; mlt_cache cache = get_cache(self, name); if (cache) result = mlt_cache_get(cache, self); return result; } /** Set the number of items to cache for the named cache. * * \public \memberof mlt_service_s * \param self a service * \param name a name for the object that is unique to the service class, but not to the instance * \param size the number of items to cache */ void mlt_service_cache_set_size(mlt_service self, const char *name, int size) { mlt_cache cache = get_cache(self, name); if (cache) mlt_cache_set_size(cache, size); } /** Get the current maximum size of the named cache. * * \public \memberof mlt_service_s * \param self a service * \param name a name for the object that is unique to the service class, but not to the instance * \return the current maximum number of items to cache or zero if there is an error */ int mlt_service_cache_get_size(mlt_service self, const char *name) { mlt_cache cache = get_cache(self, name); if (cache) return mlt_cache_get_size(cache); else return 0; } mlt-7.38.0/src/framework/mlt_service.h000664 000000 000000 00000012666 15172202314 017646 0ustar00rootroot000000 000000 /** * \file mlt_service.h * \brief interface declaration for all service classes * \see mlt_service_s * * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_SERVICE_H #define MLT_SERVICE_H #include "mlt_export.h" #include "mlt_properties.h" #include "mlt_types.h" /** \brief Service abstract base class * * \extends mlt_properties * The service is the base class for all of the interesting classes and * plugins for MLT. A service can have multiple inputs connections to * other services called its "producers" but only a single output to another * service called its "consumer." A service that has both producer and * consumer connections is called a filter. Any service can have zero or more * filters "attached" to it. We call any collection of services and their * connections a "service network," which is similar to what DirectShow calls * a filter graph or what gstreamer calls an element pipeline. * * \event \em service-changed a filter was attached or detached or a transition was connected or disconnected * \event \em property-changed a property's value changed; the event data is a string for the name of the property * \properties \em mlt_type identifies the subclass * \properties \em _mlt_service_hidden a flag that indicates whether to hide the mlt_service * \properties \em mlt_service is the name of the implementation of the service * \properties \em resource is either the stream identifier or grandchild-class * \properties \em in when to start, what is started is service-specific * \properties \em out when to stop * \properties \em _filter_private Set this on a service to ensure that attached filters are handled privately. * See modules/core/filter_watermark.c for example. * \properties \em _profile stores the mlt_profile for a service * \properties \em _unique_id is a unique identifier * \properties \em _need_previous_next boolean that instructs producers to get * preceding and following frames inside of \p mlt_service_get_frame */ struct mlt_service_s { struct mlt_properties_s parent; /**< \private A service extends properties. */ /** Get a frame of data (virtual function). * * \param mlt_producer a producer * \param mlt_frame_ptr a frame pointer by reference * \param int an index * \return true if there was an error */ int (*get_frame)(mlt_service self, mlt_frame_ptr frame, int index); /** the destructor virtual function */ mlt_destructor close; void *close_object; /**< the object supplied to the close virtual function */ void *local; /**< \private instance object */ void *child; /**< \private the object of a subclass */ }; #define MLT_SERVICE_PROPERTIES(service) (&(service)->parent) MLT_EXPORT int mlt_service_init(mlt_service self, void *child); MLT_EXPORT void mlt_service_lock(mlt_service self); MLT_EXPORT void mlt_service_unlock(mlt_service self); MLT_EXPORT mlt_service_type mlt_service_identify(mlt_service self); MLT_EXPORT int mlt_service_connect_producer(mlt_service self, mlt_service producer, int index); MLT_EXPORT int mlt_service_insert_producer(mlt_service self, mlt_service producer, int index); MLT_EXPORT int mlt_service_disconnect_producer(mlt_service self, int index); MLT_EXPORT int mlt_service_disconnect_all_producers(mlt_service self); MLT_EXPORT mlt_service mlt_service_get_producer(mlt_service self); MLT_EXPORT int mlt_service_get_frame(mlt_service self, mlt_frame_ptr frame, int index); MLT_EXPORT mlt_properties mlt_service_properties(mlt_service self); MLT_EXPORT void mlt_service_set_consumer(mlt_service self, mlt_service consumer); MLT_EXPORT mlt_service mlt_service_consumer(mlt_service self); MLT_EXPORT mlt_service mlt_service_producer(mlt_service self); MLT_EXPORT int mlt_service_attach(mlt_service self, mlt_filter filter); MLT_EXPORT int mlt_service_detach(mlt_service self, mlt_filter filter); MLT_EXPORT void mlt_service_apply_filters(mlt_service self, mlt_frame frame, int index); MLT_EXPORT int mlt_service_filter_count(mlt_service self); MLT_EXPORT int mlt_service_move_filter(mlt_service self, int from, int to); MLT_EXPORT mlt_filter mlt_service_filter(mlt_service self, int index); MLT_EXPORT mlt_profile mlt_service_profile(mlt_service self); MLT_EXPORT void mlt_service_set_profile(mlt_service self, mlt_profile profile); MLT_EXPORT void mlt_service_close(mlt_service self); MLT_EXPORT void mlt_service_cache_put( mlt_service self, const char *name, void *data, int size, mlt_destructor destructor); MLT_EXPORT mlt_cache_item mlt_service_cache_get(mlt_service self, const char *name); MLT_EXPORT void mlt_service_cache_set_size(mlt_service self, const char *name, int size); MLT_EXPORT int mlt_service_cache_get_size(mlt_service self, const char *name); MLT_EXPORT void mlt_service_cache_purge(mlt_service self); #endif mlt-7.38.0/src/framework/mlt_slices.c000664 000000 000000 00000027250 15172202314 017456 0ustar00rootroot000000 000000 /** * \file mlt_slices.c * \brief sliced threading processing helper * \see mlt_slices_s * * Copyright (C) 2016-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_slices.h" #include "mlt_factory.h" #include "mlt_log.h" #include "mlt_properties.h" #include #include #include #ifdef _WIN32 #include #else // For _SC_NPROCESSORS_ONLN #include #endif #define MAX_SLICES 32 #define ENV_SLICES "MLT_SLICES_COUNT" typedef enum { mlt_policy_normal, mlt_policy_rr, mlt_policy_fifo, mlt_policy_nb } mlt_schedule_policy; static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; static mlt_slices globals[mlt_policy_nb] = {NULL, NULL, NULL}; struct mlt_slices_runtime_s { int jobs, done, curr; mlt_slices_proc proc; void *cookie; struct mlt_slices_runtime_s *next; }; struct mlt_slices_s { int f_exit; int count; int readys; int ref; pthread_mutex_t cond_mutex; pthread_cond_t cond_var_job; pthread_cond_t cond_var_ready; pthread_t threads[MAX_SLICES]; struct mlt_slices_runtime_s *head, *tail; const char *name; }; static void *mlt_slices_worker(void *p) { int id, idx; struct mlt_slices_runtime_s *r; mlt_slices ctx = (mlt_slices) p; mlt_log_debug(NULL, "%s:%d: ctx=[%p][%s] entering\n", __FUNCTION__, __LINE__, ctx, ctx->name); pthread_mutex_lock(&ctx->cond_mutex); id = ctx->readys; ctx->readys++; while (1) { mlt_log_debug(NULL, "%s:%d: ctx=[%p][%s] waiting\n", __FUNCTION__, __LINE__, ctx, ctx->name); /* wait for new jobs */ while (!ctx->f_exit && !(r = ctx->head)) pthread_cond_wait(&ctx->cond_var_job, &ctx->cond_mutex); if (ctx->f_exit) break; if (!r) continue; /* check if no new job */ if (r->curr == r->jobs) { ctx->head = ctx->head->next; if (!ctx->head) ctx->tail = NULL; mlt_log_debug(NULL, "%s:%d: new ctx->head=%p\n", __FUNCTION__, __LINE__, ctx->head); continue; }; /* new job id */ idx = r->curr; r->curr++; /* run job */ pthread_mutex_unlock(&ctx->cond_mutex); mlt_log_debug(NULL, "%s:%d: running job: id=%d, idx=%d/%d, pool=[%s]\n", __FUNCTION__, __LINE__, id, idx, r->jobs, ctx->name); r->proc(id, idx, r->jobs, r->cookie); pthread_mutex_lock(&ctx->cond_mutex); /* increase done jobs counter */ r->done++; /* notify we fininished last job */ if (r->done == r->jobs) { mlt_log_debug(NULL, "%s:%d: pthread_cond_signal( &ctx->cond_var_ready )\n", __FUNCTION__, __LINE__); pthread_cond_broadcast(&ctx->cond_var_ready); } } pthread_mutex_unlock(&ctx->cond_mutex); return NULL; } /** Initialize a sliced threading context * * \private \memberof mlt_slices_s * \param threads number of threads to use for job list, 0 for the number of CPUs * \param policy scheduling policy of processing threads, -1 for normal * \param priority priority value that can be used with the scheduling algorithm, -1 for maximum * \return the context pointer */ static mlt_slices mlt_slices_init(int threads, int policy, int priority) { pthread_attr_t tattr; struct sched_param param; mlt_slices ctx = (mlt_slices) calloc(1, sizeof(struct mlt_slices_s)); char *env = getenv(ENV_SLICES); #ifdef _WIN32 #if _WIN32_WINNT >= 0x0601 int cpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); #else SYSTEM_INFO info; GetSystemInfo(&info); int cpus = info.dwNumberOfProcessors; #endif #else int cpus = sysconf(_SC_NPROCESSORS_ONLN); #endif int i, env_val = env ? atoi(env) : 0; /* check given threads count */ if (!env || !env_val) { if (threads < 0) threads = -threads * cpus; else if (!threads) threads = cpus; } else if (env_val < 0) { if (threads < 0) threads = env_val * threads * cpus; else if (!threads) threads = -env_val * cpus; else threads = -env_val * threads; } else // env_val > 0 { if (threads < 0) threads = env_val * threads; else if (!threads) threads = env_val; } if (threads > MAX_SLICES) threads = MAX_SLICES; ctx->count = threads; /* init attributes */ pthread_mutex_init(&ctx->cond_mutex, NULL); pthread_cond_init(&ctx->cond_var_job, NULL); pthread_cond_init(&ctx->cond_var_ready, NULL); pthread_attr_init(&tattr); if (policy < 0) policy = SCHED_OTHER; if (priority < 0) priority = sched_get_priority_max(policy); pthread_attr_setschedpolicy(&tattr, policy); param.sched_priority = priority; pthread_attr_setschedparam(&tattr, ¶m); /* run worker threads */ for (i = 0; i < ctx->count; i++) { pthread_create(&ctx->threads[i], &tattr, mlt_slices_worker, ctx); pthread_setschedparam(ctx->threads[i], policy, ¶m); } pthread_attr_destroy(&tattr); /* return context */ return ctx; } /** Destroy sliced threading context * * \private \memberof mlt_slices_s * \param ctx context pointer */ static void mlt_slices_close(mlt_slices ctx) { int j; pthread_mutex_lock(&g_lock); mlt_log_debug(NULL, "%s:%d: ctx=[%p][%s] closing\n", __FUNCTION__, __LINE__, ctx, ctx->name); /* check reference count */ if (ctx->ref) { ctx->ref--; mlt_log_debug(NULL, "%s:%d: ctx=[%p][%s] new ref=%d\n", __FUNCTION__, __LINE__, ctx, ctx->name, ctx->ref); pthread_mutex_unlock(&g_lock); return; } pthread_mutex_unlock(&g_lock); /* notify to exit */ ctx->f_exit = 1; pthread_mutex_lock(&ctx->cond_mutex); pthread_cond_broadcast(&ctx->cond_var_job); pthread_cond_broadcast(&ctx->cond_var_ready); pthread_mutex_unlock(&ctx->cond_mutex); /* wait for threads exit */ for (j = 0; j < ctx->count; j++) pthread_join(ctx->threads[j], NULL); /* destroy vars */ pthread_cond_destroy(&ctx->cond_var_ready); pthread_cond_destroy(&ctx->cond_var_job); pthread_mutex_destroy(&ctx->cond_mutex); /* free context */ free(ctx); } /** Run sliced execution * * \private \memberof mlt_slices_s * \param ctx context pointer * \param jobs number of jobs to process * \param proc a pointer to the function that will be called * \param cookie an opaque data pointer passed to \p proc */ static void mlt_slices_run(mlt_slices ctx, int jobs, mlt_slices_proc proc, void *cookie) { if (jobs == 1) { proc(0, 0, 1, cookie); return; } struct mlt_slices_runtime_s runtime, *r = &runtime; /* lock */ pthread_mutex_lock(&ctx->cond_mutex); /* check jobs count */ if (jobs < 0) jobs = (-jobs) * ctx->count; if (!jobs) jobs = ctx->count; /* setup runtime args */ r->jobs = jobs; r->done = 0; r->curr = 0; r->proc = proc; r->cookie = cookie; r->next = NULL; /* attach job */ if (ctx->tail) { ctx->tail->next = r; ctx->tail = r; } else { ctx->head = ctx->tail = r; } /* notify workers */ pthread_cond_broadcast(&ctx->cond_var_job); /* wait for end of task */ while (!ctx->f_exit && (r->done < r->jobs)) { pthread_cond_wait(&ctx->cond_var_ready, &ctx->cond_mutex); mlt_log_debug(NULL, "%s:%d: ctx=[%p][%s] signalled\n", __FUNCTION__, __LINE__, ctx, ctx->name); } pthread_mutex_unlock(&ctx->cond_mutex); } /** Get a global shared sliced threading context. * * There are separate contexts for each scheduling policy. * * \private \memberof mlt_slices_s * \param policy the thread scheduling policy needed * \return the context pointer */ static mlt_slices mlt_slices_get_global(mlt_schedule_policy policy) { pthread_mutex_lock(&g_lock); if (!globals[policy]) { int posix_policy; switch (policy) { case mlt_policy_rr: posix_policy = SCHED_RR; break; case mlt_policy_fifo: posix_policy = SCHED_FIFO; break; default: posix_policy = SCHED_OTHER; } globals[policy] = mlt_slices_init(0, posix_policy, -1); mlt_factory_register_for_clean_up(globals[policy], (mlt_destructor) mlt_slices_close); } pthread_mutex_unlock(&g_lock); return globals[policy]; } /** Get the number of slices for the normal scheduling policy. * * \public \memberof mlt_slices_s * \return the number of slices */ int mlt_slices_count_normal() { mlt_slices slices = mlt_slices_get_global(mlt_policy_normal); if (slices) return slices->count; else return 0; } /** Get the number of slices for the round robin scheduling policy. * * \public \memberof mlt_slices_s * \return the number of slices */ int mlt_slices_count_rr() { mlt_slices slices = mlt_slices_get_global(mlt_policy_rr); if (slices) return slices->count; else return 0; } /** Get the number of slices for the fifo scheduling policy. * * \public \memberof mlt_slices_s * \return the number of slices */ int mlt_slices_count_fifo() { mlt_slices slices = mlt_slices_get_global(mlt_policy_fifo); if (slices) return slices->count; else return 0; } void mlt_slices_run_normal(int jobs, mlt_slices_proc proc, void *cookie) { return mlt_slices_run(mlt_slices_get_global(mlt_policy_normal), jobs, proc, cookie); } void mlt_slices_run_rr(int jobs, mlt_slices_proc proc, void *cookie) { return mlt_slices_run(mlt_slices_get_global(mlt_policy_rr), jobs, proc, cookie); } void mlt_slices_run_fifo(int jobs, mlt_slices_proc proc, void *cookie) { return mlt_slices_run(mlt_slices_get_global(mlt_policy_fifo), jobs, proc, cookie); } /** Compute size of a slice. * * This a helper function for use in a mlt_slices_proc() to get the number of * pixels over which to operate. * * \public \memberof mlt_slices_s * \param jobs the number of slices * \param index the zero-based index of the current slice * \param input_size the size of a dimension, usually in pixel units, for example height * \param[out] start the optional starting unit for this slice * \return the size of the slice, typically in pixel units */ int mlt_slices_size_slice(int jobs, int index, int input_size, int *start) { int size = (input_size + jobs - 1) / jobs; int my_start = index * size; if (start) { *start = my_start; } return CLAMP(input_size - my_start, 0, size); } mlt-7.38.0/src/framework/mlt_slices.h000664 000000 000000 00000003173 15172202314 017461 0ustar00rootroot000000 000000 /** * \file mlt_slices.h * \brief sliced threading processing helper * \see mlt_slices_s * * Copyright (C) 2016-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_SLICES_H #define MLT_SLICES_H #include "mlt_export.h" #include "mlt_types.h" /** * \envvar \em MLT_SLICES_COUNT Set the number of slices to use, which * defaults to number of CPUs found. */ struct mlt_slices_s; typedef int (*mlt_slices_proc)(int id, int idx, int jobs, void *cookie); MLT_EXPORT int mlt_slices_count_normal(); MLT_EXPORT int mlt_slices_count_rr(); MLT_EXPORT int mlt_slices_count_fifo(); MLT_EXPORT void mlt_slices_run_normal(int jobs, mlt_slices_proc proc, void *cookie); MLT_EXPORT void mlt_slices_run_rr(int jobs, mlt_slices_proc proc, void *cookie); MLT_EXPORT void mlt_slices_run_fifo(int jobs, mlt_slices_proc proc, void *cookie); MLT_EXPORT int mlt_slices_size_slice(int jobs, int index, int input_size, int *start); #endif mlt-7.38.0/src/framework/mlt_tokeniser.c000664 000000 000000 00000010331 15172202314 020167 0ustar00rootroot000000 000000 /** * \file mlt_tokeniser.c * \brief string tokeniser * \see mlt_tokeniser_s * * Copyright (C) 2002-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* System header files */ #include #include /* Application header files */ #include "mlt_tokeniser.h" /** Initialise a tokeniser. */ mlt_tokeniser mlt_tokeniser_init() { mlt_tokeniser tokeniser = calloc(1, sizeof(mlt_tokeniser_t)); tokeniser->input = NULL; tokeniser->tokens = NULL; tokeniser->count = 0; tokeniser->size = 0; return tokeniser; } /** Clear the tokeniser. */ static void mlt_tokeniser_clear(mlt_tokeniser tokeniser) { int index = 0; for (index = 0; index < tokeniser->count; index++) free(tokeniser->tokens[index]); tokeniser->count = 0; free(tokeniser->input); tokeniser->input = NULL; } /** Append a string to the tokeniser. */ static int mlt_tokeniser_append(mlt_tokeniser tokeniser, char *token) { int error = 0; if (tokeniser->count == tokeniser->size) { tokeniser->size += 20; tokeniser->tokens = realloc(tokeniser->tokens, tokeniser->size * sizeof(char *)); } if (tokeniser->tokens != NULL) { tokeniser->tokens[tokeniser->count++] = strdup(token); } else { tokeniser->count = 0; error = -1; } return error; } /** Parse a string by splitting on the delimiter provided. */ int mlt_tokeniser_parse_new(mlt_tokeniser tokeniser, char *string, const char *delimiter) { if (!string || !delimiter) return 0; int count = 0; int length = strlen(string); int delimiter_size = strlen(delimiter); int index = 0; char *token = strdup(string); int token_size = strlen(token); mlt_tokeniser_clear(tokeniser); tokeniser->input = strdup(string); strcpy(token, ""); for (index = 0; index < length;) { char *start = string + index; char *end = strstr(start, delimiter); if (end == NULL) { strcat(token, start); mlt_tokeniser_append(tokeniser, token); index = length; count++; } else if (start != end) { strncat(token, start, end - start); index += end - start; if (strchr(token, '\"') == NULL || token[strlen(token) - 1] == '\"') { mlt_tokeniser_append(tokeniser, token); strcpy(token, ""); count++; } else while (strncmp(string + index, delimiter, delimiter_size) == 0) { strncat(token, delimiter, token_size); token[token_size] = '\0'; index += delimiter_size; } } else { index += delimiter_size; } } /* Special case - malformed string condition */ if (!strcmp(token, "")) { count = 0 - (count - 1); mlt_tokeniser_append(tokeniser, token); } free(token); return count; } /** Get the original input. */ char *mlt_tokeniser_get_input(mlt_tokeniser tokeniser) { return tokeniser->input; } /** Get the number of tokens. */ int mlt_tokeniser_count(mlt_tokeniser tokeniser) { return tokeniser->count; } /** Get a token as a string. */ char *mlt_tokeniser_get_string(mlt_tokeniser tokeniser, int index) { if (index < tokeniser->count) return tokeniser->tokens[index]; else return NULL; } /** Close the tokeniser. */ void mlt_tokeniser_close(mlt_tokeniser tokeniser) { mlt_tokeniser_clear(tokeniser); free(tokeniser->tokens); free(tokeniser); } mlt-7.38.0/src/framework/mlt_tokeniser.h000664 000000 000000 00000003016 15172202314 020176 0ustar00rootroot000000 000000 /** * \file mlt_tokeniser.h * \brief string tokeniser * \see mlt_tokeniser_s * * Copyright (C) 2002-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_TOKENISER_H #define MLT_TOKENISER_H #include "mlt_export.h" /** \brief Tokeniser class * */ typedef struct { char *input; char **tokens; int count; int size; } * mlt_tokeniser, mlt_tokeniser_t; /* Remote parser API. */ MLT_EXPORT mlt_tokeniser mlt_tokeniser_init(); MLT_EXPORT int mlt_tokeniser_parse_new(mlt_tokeniser tokeniser, char *text, const char *delimiter); MLT_EXPORT char *mlt_tokeniser_get_input(mlt_tokeniser tokeniser); MLT_EXPORT int mlt_tokeniser_count(mlt_tokeniser tokeniser); MLT_EXPORT char *mlt_tokeniser_get_string(mlt_tokeniser tokeniser, int index); MLT_EXPORT void mlt_tokeniser_close(mlt_tokeniser tokeniser); #endif mlt-7.38.0/src/framework/mlt_tractor.c000664 000000 000000 00000062362 15172202314 017655 0ustar00rootroot000000 000000 /** * \file mlt_tractor.c * \brief tractor service class * \see mlt_tractor_s * * Copyright (C) 2003-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_tractor.h" #include "mlt_field.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_multitrack.h" #include "mlt_transition.h" #include #include #include #include /* Forward references to static methods. */ static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int track); static void mlt_tractor_listener(mlt_multitrack tracks, mlt_tractor self); /** Construct a tractor without a field or multitrack. * * Sets the resource property to "", the mlt_type to "mlt_producer", * and mlt_service to "tractor". * * \public \memberof mlt_tractor_s * \return the new tractor */ mlt_tractor mlt_tractor_init() { mlt_tractor self = calloc(1, sizeof(struct mlt_tractor_s)); if (self != NULL) { mlt_producer producer = &self->parent; if (mlt_producer_init(producer, self) == 0) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_set(properties, "resource", ""); mlt_properties_set(properties, "mlt_type", "mlt_producer"); mlt_properties_set(properties, "mlt_service", "tractor"); mlt_properties_set_int(properties, "in", 0); mlt_properties_set_int(properties, "out", -1); mlt_properties_set_int(properties, "length", 0); producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) mlt_tractor_close; producer->close_object = self; } else { free(self); self = NULL; } } return self; } /** Construct a tractor as well as a field and multitrack. * * Sets the resource property to "", the mlt_type to "mlt_producer", * and mlt_service to "tractor". * * \public \memberof mlt_tractor_s * \return the new tractor */ mlt_tractor mlt_tractor_new() { mlt_tractor self = calloc(1, sizeof(struct mlt_tractor_s)); if (self != NULL) { mlt_producer producer = &self->parent; if (mlt_producer_init(producer, self) == 0) { mlt_multitrack multitrack = mlt_multitrack_init(); mlt_field field = mlt_field_new(multitrack, self); mlt_properties props = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_set(props, "resource", ""); mlt_properties_set(props, "mlt_type", "mlt_producer"); mlt_properties_set(props, "mlt_service", "tractor"); mlt_properties_set_position(props, "in", 0); mlt_properties_set_position(props, "out", 0); mlt_properties_set_position(props, "length", 0); mlt_properties_set_data(props, "multitrack", multitrack, 0, (mlt_destructor) mlt_multitrack_close, NULL); mlt_properties_set_data(props, "field", field, 0, (mlt_destructor) mlt_field_close, NULL); mlt_events_listen(MLT_MULTITRACK_PROPERTIES(multitrack), self, "producer-changed", (mlt_listener) mlt_tractor_listener); producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) mlt_tractor_close; producer->close_object = self; } else { free(self); self = NULL; } } return self; } /** Get the service object associated to the tractor. * * \public \memberof mlt_tractor_s * \param self a tractor * \return the parent service object * \see MLT_TRACTOR_SERVICE */ mlt_service mlt_tractor_service(mlt_tractor self) { return MLT_PRODUCER_SERVICE(&self->parent); } /** Get the producer object associated to the tractor. * * \public \memberof mlt_tractor_s * \param self a tractor * \return the parent producer object * \see MLT_TRACTOR_PRODUCER */ mlt_producer mlt_tractor_producer(mlt_tractor self) { return self != NULL ? &self->parent : NULL; } /** Get the properties object associated to the tractor. * * \public \memberof mlt_tractor_s * \param self a tractor * \return the tractor's property list * \see MLT_TRACTOR_PROPERTIES */ mlt_properties mlt_tractor_properties(mlt_tractor self) { return MLT_PRODUCER_PROPERTIES(&self->parent); } /** Get the field self tractor is harvesting. * * \public \memberof mlt_tractor_s * \param self a tractor * \return a field or NULL if there is no field for this tractor */ mlt_field mlt_tractor_field(mlt_tractor self) { return mlt_properties_get_data(MLT_TRACTOR_PROPERTIES(self), "field", NULL); } /** Get the multitrack a tractor is pulling. * * \public \memberof mlt_tractor_s * \param self a tractor * \return a multitrack or NULL if there is none */ mlt_multitrack mlt_tractor_multitrack(mlt_tractor self) { return mlt_properties_get_data(MLT_TRACTOR_PROPERTIES(self), "multitrack", NULL); } /** Ensure the tractors in/out points match the multitrack. * * \public \memberof mlt_tractor_s * \param self a tractor */ void mlt_tractor_refresh(mlt_tractor self) { mlt_multitrack multitrack = mlt_tractor_multitrack(self); mlt_properties multitrack_props = MLT_MULTITRACK_PROPERTIES(multitrack); mlt_properties properties = MLT_TRACTOR_PROPERTIES(self); mlt_events_block(multitrack_props, properties); mlt_events_block(properties, properties); mlt_multitrack_refresh(multitrack); mlt_properties_set_position(properties, "in", 0); mlt_properties_set_position(properties, "out", mlt_properties_get_position(multitrack_props, "out")); mlt_events_unblock(properties, properties); mlt_events_unblock(multitrack_props, properties); mlt_properties_set_position(properties, "length", mlt_properties_get_position(multitrack_props, "length")); } static void mlt_tractor_listener(mlt_multitrack tracks, mlt_tractor self) { mlt_tractor_refresh(self); } /** Connect the tractor. * * \public \memberof mlt_tractor_s * \param self a tractor * \param producer a producer * \return true on error */ int mlt_tractor_connect(mlt_tractor self, mlt_service producer) { int ret = mlt_service_connect_producer(MLT_TRACTOR_SERVICE(self), producer, 0); // This is the producer we're going to connect to if (ret == 0) self->producer = producer; return ret; } /** Set the producer for a specific track. * * \public \memberof mlt_tractor_s * \param self a tractor * \param producer a producer * \param index the 0-based track index * \return true on error */ int mlt_tractor_set_track(mlt_tractor self, mlt_producer producer, int index) { return mlt_multitrack_connect(mlt_tractor_multitrack(self), producer, index); } /** Insert a producer before a specific track. * * This also adjusts the track indices on mlt_transition_s and mlt_filter_s, * * \public \memberof mlt_tractor_s * \param self a tractor * \param producer a producer * \param index the 0-based track index * \return true on error */ int mlt_tractor_insert_track(mlt_tractor self, mlt_producer producer, int index) { int error = mlt_multitrack_insert(mlt_tractor_multitrack(self), producer, index); if (!error) { // Update the track indices of transitions and track filters. mlt_service service = mlt_service_producer(MLT_TRACTOR_SERVICE(self)); while (service) { mlt_service_type type = mlt_service_identify(service); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); if (type == mlt_service_transition_type) { mlt_transition transition = MLT_TRANSITION(service); int a_track = mlt_transition_get_a_track(transition); int b_track = mlt_transition_get_b_track(transition); if (a_track >= index || b_track >= index) { a_track = a_track >= index ? a_track + 1 : a_track; b_track = b_track >= index ? b_track + 1 : b_track; mlt_transition_set_tracks(transition, a_track, b_track); } } else if (type == mlt_service_filter_type) { int current_track = mlt_properties_get_int(properties, "track"); if (current_track >= index) mlt_properties_set_int(properties, "track", current_track + 1); } service = mlt_service_producer(service); } } return error; } /** Remove a track by its index. * * \public \memberof mlt_tractor_s * \param self a tractor * \param index the 0-based track index * \return true on error */ int mlt_tractor_remove_track(mlt_tractor self, int index) { int error = mlt_multitrack_disconnect(mlt_tractor_multitrack(self), index); if (!error) { // Update the track indices of transitions and track filters. mlt_service service = mlt_service_producer(MLT_TRACTOR_SERVICE(self)); while (service) { mlt_service_type type = mlt_service_identify(service); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); int track_max = MAX(mlt_multitrack_count(mlt_tractor_multitrack(self)) - 1, 0); if (type == mlt_service_transition_type) { mlt_transition transition = MLT_TRANSITION(service); int a_track = mlt_transition_get_a_track(transition); int b_track = mlt_transition_get_b_track(transition); if (a_track > index || b_track >= index) { a_track = CLAMP(a_track > index ? a_track - 1 : a_track, 0, track_max); b_track = CLAMP(b_track >= index ? b_track - 1 : b_track, 0, track_max); mlt_transition_set_tracks(transition, a_track, b_track); } } else if (type == mlt_service_filter_type) { int current_track = mlt_properties_get_int(properties, "track"); if (current_track >= index) mlt_properties_set_int(properties, "track", CLAMP(current_track - 1, 0, track_max)); } service = mlt_service_producer(service); } } return error; } /** Get the producer for a specific track. * * \public \memberof mlt_tractor_s * \param self a tractor * \param index the 0-based track index * \return the producer for track \p index */ mlt_producer mlt_tractor_get_track(mlt_tractor self, int index) { return mlt_multitrack_track(mlt_tractor_multitrack(self), index); } static int producer_get_image(mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { uint8_t *data = NULL; int size = 0; mlt_properties properties = MLT_FRAME_PROPERTIES(self); mlt_frame frame = mlt_frame_pop_service(self); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set_int(frame_properties, "resize_alpha", mlt_properties_get_int(properties, "resize_alpha")); mlt_properties_set_int(frame_properties, "distort", mlt_properties_get_int(properties, "distort")); mlt_properties_copy(frame_properties, properties, "consumer."); // WebVfx uses this to setup a consumer-stopping event handler. mlt_properties_set_data(frame_properties, "consumer", mlt_properties_get_data(properties, "consumer", NULL), 0, NULL, NULL); mlt_frame_get_image(frame, buffer, format, width, height, writable); mlt_frame_set_image(self, *buffer, 0, NULL); mlt_properties_set_int(properties, "width", *width); mlt_properties_set_int(properties, "height", *height); mlt_properties_set_int(properties, "format", *format); mlt_properties_set_double(properties, "aspect_ratio", mlt_frame_get_aspect_ratio(frame)); // Pass all required frame properties mlt_properties_pass_list( properties, frame_properties, "progressive,distort,colorspace,full_range,force_full_luma,top_field_first,color_trc"); mlt_properties_set_data(properties, "movit.convert.fence", mlt_properties_get_data(frame_properties, "movit.convert.fence", NULL), 0, NULL, NULL); mlt_properties_set_data(properties, "movit.convert.texture", mlt_properties_get_data(frame_properties, "movit.convert.texture", NULL), 0, NULL, NULL); mlt_properties_set_int(properties, "movit.convert.use_texture", mlt_properties_get_int(frame_properties, "movit.convert.use_texture")); int i; for (i = 0; i < mlt_properties_count(frame_properties); i++) { char *name = mlt_properties_get_name(frame_properties, i); if (name && !strncmp(name, "_movit ", 7)) { mlt_properties_set_data(properties, name, mlt_properties_get_data_at(frame_properties, i, NULL), 0, NULL, NULL); } } data = mlt_frame_get_alpha_size(frame, &size); if (data) { mlt_frame_set_alpha(self, data, size, NULL); } self->convert_image = frame->convert_image; self->convert_audio = frame->convert_audio; return 0; } static int producer_get_audio(mlt_frame self, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_properties properties = MLT_FRAME_PROPERTIES(self); mlt_frame frame = mlt_frame_pop_audio(self); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set(frame_properties, "consumer.channel_layout", mlt_properties_get(properties, "consumer.channel_layout")); mlt_properties_set(frame_properties, "producer_consumer_fps", mlt_properties_get(properties, "producer_consumer_fps")); mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); mlt_frame_set_audio(self, *buffer, *format, mlt_audio_format_size(*format, *samples, *channels), NULL); mlt_properties_set_int(properties, "audio_frequency", *frequency); mlt_properties_set_int(properties, "audio_channels", *channels); mlt_properties_set_int(properties, "audio_samples", *samples); return 0; } /** Get the next frame. * * \private \memberof mlt_tractor_s * \param parent the producer interface to the tractor * \param[out] frame a frame by reference * \param track the 0-based track index * \return true on error */ static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int track) { mlt_tractor self = parent->child; // We only respond to the first track requests if (track == 0 && self->producer != NULL) { int i = 0; int done = 0; mlt_frame temp = NULL; int count = 0; int image_count = 0; // Get the properties of the parent producer mlt_properties properties = MLT_PRODUCER_PROPERTIES(parent); // Try to obtain the multitrack associated to the tractor mlt_multitrack multitrack = mlt_properties_get_data(properties, "multitrack", NULL); // Or a specific producer mlt_producer producer = mlt_properties_get_data(properties, "producer", NULL); // If we don't have one, we're in trouble... if (multitrack != NULL) { // Used to garbage collect all frames char label[64]; // Get the id of the tractor char *id = mlt_properties_get(properties, "_unique_id"); if (!id) { mlt_properties_set_int64(properties, "_unique_id", (int64_t) properties); id = mlt_properties_get(properties, "_unique_id"); } // Will be used to store the frame properties object mlt_properties frame_properties = NULL; // We'll store audio and video frames to use here mlt_frame audio = NULL; mlt_frame video = NULL; mlt_frame first_video = NULL; // Temporary properties mlt_properties temp_properties = NULL; // Get the multitrack's producer mlt_producer target = MLT_MULTITRACK_PRODUCER(multitrack); mlt_producer_seek(target, mlt_producer_frame(parent)); mlt_producer_set_speed(target, mlt_producer_get_speed(parent)); // We will create one frame and attach everything to it *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(parent)); // Get the properties of the frame frame_properties = MLT_FRAME_PROPERTIES(*frame); // Loop through each of the tracks we're harvesting for (i = 0; !done; i++) { // Get a frame from the producer mlt_service_get_frame(self->producer, &temp, i); // Get the temporary properties temp_properties = MLT_FRAME_PROPERTIES(temp); // Pass all unique meta properties from the producer's frame to the new frame mlt_properties_lock(temp_properties); mlt_properties_copy(frame_properties, temp_properties, "meta."); mlt_properties_unlock(temp_properties); // Pass subtitles if present mlt_properties subtitle_properties = mlt_properties_get_properties(temp_properties, "subtitles"); if (subtitle_properties) { mlt_properties_set_properties(frame_properties, "subtitles", subtitle_properties); } // Copy the format conversion virtual functions if (!(*frame)->convert_image && temp->convert_image) (*frame)->convert_image = temp->convert_image; if (!(*frame)->convert_audio && temp->convert_audio) (*frame)->convert_audio = temp->convert_audio; // Check for last track done = mlt_properties_get_int(temp_properties, "last_track"); // Handle fx only tracks if (mlt_properties_get_int(temp_properties, "fx_cut")) { int hide = (video == NULL ? 1 : 0) | (audio == NULL ? 2 : 0); mlt_properties_set_int(temp_properties, "hide", hide); } // We store all frames with a destructor on the output frame snprintf(label, sizeof(label), "mlt_tractor %s_%d", id, count++); mlt_properties_set_data(frame_properties, label, temp, 0, (mlt_destructor) mlt_frame_close, NULL); // Pick up first video and audio frames if (!done && !mlt_frame_is_test_audio(temp) && !(mlt_properties_get_int(temp_properties, "hide") & 2)) { // Order of frame creation is starting to get problematic if (audio != NULL) { mlt_deque_push_front(MLT_FRAME_AUDIO_STACK(temp), producer_get_audio); mlt_deque_push_front(MLT_FRAME_AUDIO_STACK(temp), audio); } audio = temp; } if (!done && !mlt_frame_is_test_card(temp) && !(mlt_properties_get_int(temp_properties, "hide") & 1)) { if (video != NULL) { mlt_deque_push_front(MLT_FRAME_IMAGE_STACK(temp), producer_get_image); mlt_deque_push_front(MLT_FRAME_IMAGE_STACK(temp), video); } video = temp; if (first_video == NULL) first_video = temp; mlt_properties_set_int(MLT_FRAME_PROPERTIES(temp), "image_count", ++image_count); image_count = 1; } } // Now stack callbacks if (audio != NULL) { mlt_frame_push_audio(*frame, audio); mlt_frame_push_audio(*frame, producer_get_audio); } if (video != NULL) { mlt_properties video_properties = MLT_FRAME_PROPERTIES(first_video); mlt_frame_push_service(*frame, video); mlt_frame_push_service(*frame, producer_get_image); mlt_properties_set_int(frame_properties, "width", mlt_properties_get_int(video_properties, "width")); mlt_properties_set_int(frame_properties, "height", mlt_properties_get_int(video_properties, "height")); mlt_properties_set_int(frame_properties, "format", mlt_properties_get_int(video_properties, "format")); mlt_properties_pass_list(frame_properties, video_properties, "meta.media.width, meta.media.height"); mlt_properties_set_int(frame_properties, "progressive", mlt_properties_get_int(video_properties, "progressive")); mlt_properties_set_double(frame_properties, "aspect_ratio", mlt_properties_get_double(video_properties, "aspect_ratio")); mlt_properties_set_int(frame_properties, "image_count", image_count); mlt_properties_set_data(frame_properties, "_producer", mlt_frame_get_original_producer(first_video), 0, NULL, NULL); } mlt_frame_set_position(*frame, mlt_producer_frame(parent)); mlt_properties_set_int(MLT_FRAME_PROPERTIES(*frame), "test_audio", audio == NULL); mlt_properties_set_int(MLT_FRAME_PROPERTIES(*frame), "test_image", video == NULL); } else if (producer != NULL) { mlt_producer_seek(producer, mlt_producer_frame(parent)); mlt_producer_set_speed(producer, mlt_producer_get_speed(parent)); mlt_service_get_frame(self->producer, frame, track); } else { mlt_log(MLT_PRODUCER_SERVICE(parent), MLT_LOG_ERROR, "tractor without a multitrack!!\n"); mlt_service_get_frame(self->producer, frame, track); } // Prepare the next frame mlt_producer_prepare_next(parent); // Indicate our found status return 0; } else { // Generate a test card *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(parent)); return 0; } } /** Close the tractor and free its resources. * * \public \memberof mlt_tractor_s * \param self a tractor */ void mlt_tractor_close(mlt_tractor self) { if (self != NULL && mlt_properties_dec_ref(MLT_TRACTOR_PROPERTIES(self)) <= 0) { self->parent.close = NULL; mlt_producer_close(&self->parent); free(self); } } mlt-7.38.0/src/framework/mlt_tractor.h000664 000000 000000 00000005160 15172202314 017653 0ustar00rootroot000000 000000 /** * \file mlt_tractor.h * \brief tractor service class * \see mlt_tractor_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_TRACTOR_H #define MLT_TRACTOR_H #include "mlt_export.h" #include "mlt_producer.h" /** \brief Tractor class * * The tractor is a convenience class that works with the field class * to manage a multitrack, track filters, and transitions. * * \extends mlt_producer_s * \properties \em multitrack holds a reference to the multitrack object that a tractor manages * \properties \em field holds a reference to the field object that a tractor manages * \properties \em producer holds a reference to an encapsulated producer */ struct mlt_tractor_s { struct mlt_producer_s parent; mlt_service producer; }; #define MLT_TRACTOR_PRODUCER(tractor) (&(tractor)->parent) #define MLT_TRACTOR_SERVICE(tractor) MLT_PRODUCER_SERVICE(MLT_TRACTOR_PRODUCER(tractor)) #define MLT_TRACTOR_PROPERTIES(tractor) MLT_SERVICE_PROPERTIES(MLT_TRACTOR_SERVICE(tractor)) MLT_EXPORT mlt_tractor mlt_tractor_init(); MLT_EXPORT mlt_tractor mlt_tractor_new(); MLT_EXPORT mlt_service mlt_tractor_service(mlt_tractor self); MLT_EXPORT mlt_producer mlt_tractor_producer(mlt_tractor self); MLT_EXPORT mlt_properties mlt_tractor_properties(mlt_tractor self); MLT_EXPORT mlt_field mlt_tractor_field(mlt_tractor self); MLT_EXPORT mlt_multitrack mlt_tractor_multitrack(mlt_tractor self); MLT_EXPORT int mlt_tractor_connect(mlt_tractor self, mlt_service service); MLT_EXPORT void mlt_tractor_refresh(mlt_tractor self); MLT_EXPORT int mlt_tractor_set_track(mlt_tractor self, mlt_producer producer, int index); MLT_EXPORT int mlt_tractor_insert_track(mlt_tractor self, mlt_producer producer, int index); MLT_EXPORT int mlt_tractor_remove_track(mlt_tractor self, int index); MLT_EXPORT mlt_producer mlt_tractor_get_track(mlt_tractor self, int index); MLT_EXPORT void mlt_tractor_close(mlt_tractor self); #endif mlt-7.38.0/src/framework/mlt_transition.c000664 000000 000000 00000046241 15172202314 020367 0ustar00rootroot000000 000000 /** * \file mlt_transition.c * \brief abstraction for all transition services * \see mlt_transition_s * * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_transition.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_producer.h" #include #include #include /* Forward references */ static int transition_get_frame(mlt_service self, mlt_frame_ptr frame, int index); /** Initialize a new transition. * * \public \memberof mlt_transition_s * \param self a transition * \param child the object of a subclass * \return true on error */ int mlt_transition_init(mlt_transition self, void *child) { mlt_service service = &self->parent; memset(self, 0, sizeof(struct mlt_transition_s)); self->child = child; if (mlt_service_init(service, self) == 0) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); service->get_frame = transition_get_frame; service->close = (mlt_destructor) mlt_transition_close; service->close_object = self; pthread_mutex_init(&self->mutex, NULL); mlt_properties_set(properties, "mlt_type", "transition"); mlt_properties_set_position(properties, "in", 0); mlt_properties_set_position(properties, "out", 0); mlt_properties_set_int(properties, "a_track", 0); mlt_properties_set_int(properties, "b_track", 1); return 0; } return 1; } /** Create and initialize a new transition. * * \public \memberof mlt_transition_s * \return a new transition */ mlt_transition mlt_transition_new() { mlt_transition self = calloc(1, sizeof(struct mlt_transition_s)); if (self != NULL) mlt_transition_init(self, NULL); return self; } /** Get the service class interface. * * \public \memberof mlt_transition_s * \param self a transition * \return the service class * \see MLT_TRANSITION_SERVICE */ mlt_service mlt_transition_service(mlt_transition self) { return self != NULL ? &self->parent : NULL; } /** Get the properties interface. * * \public \memberof mlt_transition_s * \param self a transition * \return the transition's properties * \see MLT_TRANSITION_PROPERTIES */ mlt_properties mlt_transition_properties(mlt_transition self) { return MLT_TRANSITION_PROPERTIES(self); } /** Connect a transition with a producer's a and b tracks. * * \public \memberof mlt_transition_s * \param self a transition * \param producer a producer * \param a_track the track index of the first input * \param b_track the track index of the second input * \return true on error */ int mlt_transition_connect(mlt_transition self, mlt_service producer, int a_track, int b_track) { int ret = mlt_service_connect_producer(&self->parent, producer, a_track); if (ret == 0) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); self->producer = producer; mlt_properties_set_int(properties, "a_track", a_track); mlt_properties_set_int(properties, "b_track", b_track); } return ret; } /** Set the starting and ending time for when the transition is active. * * \public \memberof mlt_transition_s * \param self a transition * \param in the starting time * \param out the ending time */ void mlt_transition_set_in_and_out(mlt_transition self, mlt_position in, mlt_position out) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); mlt_properties_set_position(properties, "in", in); mlt_properties_set_position(properties, "out", out); } /** Change the track indices of a transition. * * \public \memberof mlt_transition_s * \param self a transition * \param a_track the track index of the first input * \param b_track the track index of the second input */ void mlt_transition_set_tracks(mlt_transition self, int a_track, int b_track) { mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(self), "a_track", a_track); mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(self), "b_track", b_track); pthread_mutex_lock(&self->mutex); free(self->frames); self->frames = NULL; pthread_mutex_unlock(&self->mutex); } /** Get the index of the a track. * * \public \memberof mlt_transition_s * \param self a transition * \return the 0-based index of the track of the first producer */ int mlt_transition_get_a_track(mlt_transition self) { return mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(self), "a_track"); } /** Get the index of the b track. * * \public \memberof mlt_transition_s * \param self a transition * \return the 0-based index of the track of the second producer */ int mlt_transition_get_b_track(mlt_transition self) { return mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(self), "b_track"); } /** Get the in point. * * \public \memberof mlt_transition_s * \param self a transition * \return the starting time */ mlt_position mlt_transition_get_in(mlt_transition self) { return mlt_properties_get_position(MLT_TRANSITION_PROPERTIES(self), "in"); } /** Get the out point. * * \public \memberof mlt_transition_s * \param self a transition * \return the ending time */ mlt_position mlt_transition_get_out(mlt_transition self) { return mlt_properties_get_position(MLT_TRANSITION_PROPERTIES(self), "out"); } /** Get the duration. * * \public \memberof mlt_transition_s * \param self a transition * \return the duration or zero if unlimited */ mlt_position mlt_transition_get_length(mlt_transition self) { mlt_properties properties = MLT_SERVICE_PROPERTIES(&self->parent); mlt_position in = mlt_properties_get_position(properties, "in"); mlt_position out = mlt_properties_get_position(properties, "out"); return (out > 0) ? (out - in + 1) : 0; } /** Get the position within the transition. * * The position is relative to the in point. * * \public \memberof mlt_transition_s * \param self a transition * \param frame a frame * \return the position */ mlt_position mlt_transition_get_position(mlt_transition self, mlt_frame frame) { mlt_position in = mlt_transition_get_in(self); mlt_position position = mlt_frame_get_position(frame); return position - in; } /** Get the percent complete. * * \public \memberof mlt_transition_s * \param self a transition * \param frame a frame * \return the progress in the range 0.0 to 1.0 */ double mlt_transition_get_progress(mlt_transition self, mlt_frame frame) { double progress = 0; mlt_position in = mlt_transition_get_in(self); mlt_position out = mlt_transition_get_out(self); if (out == 0) { // If always active, use the frame's producer mlt_producer producer = mlt_frame_get_original_producer(frame); if (producer) { in = mlt_producer_get_in(producer); out = mlt_producer_get_out(producer); } } if (out != 0) { if (in == out) { // Special case for one frame transition progress = 0.5; } else { mlt_position position = mlt_frame_get_position(frame); progress = (double) (position - in) / (double) (out - in + 1); } } return progress; } /** Get the second field incremental progress. * * \public \memberof mlt_transition_s * \param self a transition * \param frame a frame * \return the progress increment in the range 0.0 to 1.0 */ double mlt_transition_get_progress_delta(mlt_transition self, mlt_frame frame) { double progress = 0; mlt_position in = mlt_transition_get_in(self); mlt_position out = mlt_transition_get_out(self); if (out == 0) { // If always active, use the frame's producer mlt_producer producer = mlt_frame_get_original_producer(frame); if (producer) { in = mlt_producer_get_in(producer); out = mlt_producer_get_out(producer); } } if (out != 0) { mlt_position position = mlt_frame_get_position(frame); double length = out - in + 1; double x = (double) (position - in) / length; double y = (double) (position + 1 - in) / length; progress = (y - x) / 2.0; } return progress; } /** Process the frame. * * If we have no process method (unlikely), we simply return the a_frame unmolested. * * \public \memberof mlt_transition_s * \param self a transition * \param a_frame a frame from the first producer * \param b_frame a frame from the second producer * \return a frame */ mlt_frame mlt_transition_process(mlt_transition self, mlt_frame a_frame, mlt_frame b_frame) { if (self->process == NULL) return a_frame; else return self->process(self, a_frame, b_frame); } static int get_image_a(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_transition self = mlt_frame_pop_service(a_frame); mlt_properties a_props = MLT_FRAME_PROPERTIES(a_frame); // All transitions get scaling const char *rescale = mlt_properties_get(a_props, "consumer.rescale"); if (!rescale || !strcmp(rescale, "none")) mlt_properties_set(a_props, "consumer.rescale", "nearest"); // Ensure sane aspect ratio if (mlt_frame_get_aspect_ratio(a_frame) == 0.0) mlt_frame_set_aspect_ratio(a_frame, mlt_profile_sar( mlt_service_profile(MLT_TRANSITION_SERVICE(self)))); return mlt_frame_get_image(a_frame, image, format, width, height, writable); } static int get_image_b(mlt_frame b_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_transition self = mlt_frame_pop_service(b_frame); mlt_frame a_frame = mlt_frame_pop_frame(b_frame); mlt_properties a_props = MLT_FRAME_PROPERTIES(a_frame); mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); // Set scaling from A frame if not already provided. if (!mlt_properties_get(b_props, "consumer.rescale")) { const char *rescale = mlt_properties_get(a_props, "consumer.rescale"); if (!rescale || !strcmp(rescale, "none")) rescale = "nearest"; mlt_properties_set(b_props, "consumer.rescale", rescale); } // Ensure sane aspect ratio if (mlt_frame_get_aspect_ratio(b_frame) == 0.0) mlt_frame_set_aspect_ratio(b_frame, mlt_profile_sar( mlt_service_profile(MLT_TRANSITION_SERVICE(self)))); mlt_properties_copy(b_props, a_props, "consumer."); return mlt_frame_get_image(b_frame, image, format, width, height, writable); } /** Get a frame from a transition. The logic is complex here. A transition is typically applied to frames on the a and b tracks specified in the connect method above and only if both contain valid info for the transition type (this is either audio or image). However, the fixed a_track may not always contain data of the correct type, eg:
	+---------+                               +-------+
	|c1       |                               |c5     | <-- A(0,1) <-- B(0,2) <-- get frame
	+---------+                     +---------+-+-----+        |          |
	                                |c4         |       <------+          |
	         +----------+-----------+-+---------+                         |
	         |c2        |c3           |                 <-----------------+
	         +----------+-------------+
During the overlap of c1 and c2, there is nothing for the A transition to do, so this results in a no operation, but B is triggered. During the overlap of c2 and c3, again, the A transition is inactive and because the B transition is pointing at track 0, it too would be inactive. This isn't an ideal situation - it's better if the B transition simply treats the frames from c3 as though they're the a track. For this to work, we cache all frames coming from all tracks between the a and b tracks. Before we process, we determine that the b frame contains something of the right type and then we determine which frame to use as the a frame (selecting a matching frame from a_track to b_track - 1). If both frames contain data of the correct type, we process the transition. This method is invoked for each track and we return the cached frames as needed. We clear the cache only when the requested frame is flagged as a 'last_track' frame. * \private \memberof mlt_transition_s * \param service a service * \param[out] frame a frame by reference * \param index 0-based track index * \return true on error */ static int transition_get_frame(mlt_service service, mlt_frame_ptr frame, int index) { int error = 0; mlt_transition self = service->child; mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); int accepts_blanks = mlt_properties_get_int(properties, "accepts_blanks"); int a_track = mlt_properties_get_int(properties, "a_track"); int b_track = mlt_properties_get_int(properties, "b_track"); mlt_position in = mlt_properties_get_position(properties, "in"); mlt_position out = mlt_properties_get_position(properties, "out"); int always_active = mlt_properties_get_int(properties, "always_active"); int type = mlt_properties_get_int(properties, "_transition_type"); int reverse_order = 0; // Ensure that we have the correct order if (a_track > b_track) { reverse_order = 1; a_track = b_track; b_track = mlt_properties_get_int(properties, "a_track"); } a_track = a_track < 0 ? 0 : a_track; b_track = b_track < 0 ? 0 : b_track; // Only act on this operation once per multitrack iteration from the tractor pthread_mutex_lock(&self->mutex); if (!self->held) { int active = 0; int i = 0; int a_frame = a_track; int b_frame = b_track; mlt_position position; int (*invalid)(mlt_frame) = type == 1 ? mlt_frame_is_test_card : mlt_frame_is_test_audio; // Initialise temporary store if (self->frames == NULL) self->frames = calloc(b_track + 1, sizeof(mlt_frame)); // Get all frames between a and b for (i = a_track; i <= b_track; i++) mlt_service_get_frame(self->producer, &self->frames[i], i); // We're holding these frames until the last_track frame property is received self->held = 1; // When we need to locate the a_frame switch (type) { case 1: case 2: // Some transitions (esp. audio) may accept blank frames active = accepts_blanks; // If we're not active then... if (!active) { // Hunt for the a_frame while (a_frame <= b_frame && (invalid(self->frames[a_frame]) || (mlt_properties_get_int(MLT_FRAME_PROPERTIES(self->frames[a_frame]), "hide") & type))) { a_frame++; } // Determine if we're active now active = a_frame != b_frame && !invalid(self->frames[b_frame]); } break; default: mlt_log(service, MLT_LOG_ERROR, "invalid transition type\n"); break; } // Now handle the non-always active case if (active && !always_active && a_frame <= b_track) { // For non-always-active transitions, we need the current position of the a frame position = mlt_frame_get_position(self->frames[a_frame]); // If a is in range, we're active active = position >= in && (out == 0 || position <= out); } // Finally, process the a and b frames if (active && !mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(self), "disable")) { int frame_nb = (!reverse_order && a_frame <= b_track) ? a_frame : b_frame; mlt_frame a_frame_ptr = self->frames[frame_nb]; frame_nb = (!reverse_order || a_frame > b_track) ? b_frame : a_frame; mlt_frame b_frame_ptr = self->frames[frame_nb]; if (a_frame_ptr && MLT_FRAME_PROPERTIES(a_frame_ptr)->local && b_frame_ptr && MLT_FRAME_PROPERTIES(b_frame_ptr)->local) { int a_hide = mlt_properties_get_int(MLT_FRAME_PROPERTIES(a_frame_ptr), "hide"); int b_hide = mlt_properties_get_int(MLT_FRAME_PROPERTIES(b_frame_ptr), "hide"); if (!(a_hide & type) && !(b_hide & type)) { // Add hooks for pre-processing frames mlt_frame_push_service(a_frame_ptr, self); mlt_frame_push_get_image(a_frame_ptr, get_image_a); mlt_frame_push_frame(b_frame_ptr, a_frame_ptr); mlt_frame_push_service(b_frame_ptr, self); mlt_frame_push_get_image(b_frame_ptr, get_image_b); // Process the transition *frame = mlt_transition_process(self, a_frame_ptr, b_frame_ptr); // We need to ensure that the tractor doesn't consider this frame for output if (*frame == a_frame_ptr) b_hide |= type; else a_hide |= type; mlt_properties_set_int(MLT_FRAME_PROPERTIES(a_frame_ptr), "hide", a_hide); mlt_properties_set_int(MLT_FRAME_PROPERTIES(b_frame_ptr), "hide", b_hide); } } } } // Obtain the frame from the cache or the producer we're attached to if (index >= a_track && index <= b_track && self->frames) *frame = self->frames[index]; else error = mlt_service_get_frame(self->producer, frame, index); // Determine if that was the last track if (!error && *frame) { self->held = !mlt_properties_get_int(MLT_FRAME_PROPERTIES(*frame), "last_track"); } pthread_mutex_unlock(&self->mutex); return error; } /** Close and destroy the transition. * * \public \memberof mlt_transition_s * \param self a transition */ void mlt_transition_close(mlt_transition self) { if (self != NULL && mlt_properties_dec_ref(MLT_TRANSITION_PROPERTIES(self)) <= 0) { self->parent.close = NULL; if (self->close != NULL) { self->close(self); } else { mlt_service_close(&self->parent); free(self->frames); pthread_mutex_destroy(&self->mutex); free(self); } } } mlt-7.38.0/src/framework/mlt_transition.h000664 000000 000000 00000007671 15172202314 020400 0ustar00rootroot000000 000000 /** * \file mlt_transition.h * \brief abstraction for all transition services * \see mlt_transition_s * * Copyright (C) 2003-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_TRANSITION_H #define MLT_TRANSITION_H #include "mlt_export.h" #include "mlt_service.h" #include /** \brief Transition abstract service class * * A transition may modify the output of a producer based on the output of a second producer. * * \extends mlt_service_s * \properties \em a_track the track index (0-based) of a multitrack of the first producer * \properties \em b_track the track index (0-based) of a multitrack of the second producer * \properties \em accepts_blanks a flag to indicate if the transition should accept blank frames * \properties \em always_active a flag to indicate that the in and out points do not apply * \properties \em _transition_type 1 for video, 2 for audio, 3 for both audio and video * \properties \em disable Set this to disable the transition while keeping it in the object model. */ struct mlt_transition_s { /** We're implementing service here */ struct mlt_service_s parent; /** public virtual */ void (*close)(mlt_transition); /** protected transition method */ mlt_frame (*process)(mlt_transition, mlt_frame, mlt_frame); /** Protected */ void *child; /** track and in/out points */ mlt_service producer; /** Private */ mlt_frame *frames; int held; pthread_mutex_t mutex; }; #define MLT_TRANSITION_SERVICE(transition) (&(transition)->parent) #define MLT_TRANSITION_PROPERTIES(transition) \ MLT_SERVICE_PROPERTIES(MLT_TRANSITION_SERVICE(transition)) MLT_EXPORT int mlt_transition_init(mlt_transition self, void *child); MLT_EXPORT mlt_transition mlt_transition_new(); MLT_EXPORT mlt_service mlt_transition_service(mlt_transition self); MLT_EXPORT mlt_properties mlt_transition_properties(mlt_transition self); MLT_EXPORT int mlt_transition_connect(mlt_transition self, mlt_service producer, int a_track, int b_track); MLT_EXPORT void mlt_transition_set_in_and_out(mlt_transition self, mlt_position in, mlt_position out); MLT_EXPORT void mlt_transition_set_tracks(mlt_transition self, int a_track, int b_track); MLT_EXPORT int mlt_transition_get_a_track(mlt_transition self); MLT_EXPORT int mlt_transition_get_b_track(mlt_transition self); MLT_EXPORT mlt_position mlt_transition_get_in(mlt_transition self); MLT_EXPORT mlt_position mlt_transition_get_out(mlt_transition self); MLT_EXPORT mlt_position mlt_transition_get_length(mlt_transition self); MLT_EXPORT mlt_position mlt_transition_get_position(mlt_transition self, mlt_frame frame); MLT_EXPORT double mlt_transition_get_progress(mlt_transition self, mlt_frame frame); MLT_EXPORT double mlt_transition_get_progress_delta(mlt_transition self, mlt_frame frame); MLT_EXPORT mlt_frame mlt_transition_process(mlt_transition self, mlt_frame a_frame, mlt_frame b_frame); MLT_EXPORT void mlt_transition_close(mlt_transition self); #endif mlt-7.38.0/src/framework/mlt_types.c000664 000000 000000 00000005771 15172202314 017344 0ustar00rootroot000000 000000 /** * \file mlt_types.c * \brief Mlt types helper functions * * Copyright (C) 2023-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_types.h" #include "mlt_image.h" #include #include #include const char *mlt_deinterlacer_name(mlt_deinterlacer method) { switch (method) { case mlt_deinterlacer_none: return "none"; case mlt_deinterlacer_onefield: return "onefield"; case mlt_deinterlacer_linearblend: return "linearblend"; case mlt_deinterlacer_bob: return "bob"; case mlt_deinterlacer_weave: return "weave"; case mlt_deinterlacer_greedy: return "greedy"; case mlt_deinterlacer_yadif_nospatial: return "yadif-nospatial"; case mlt_deinterlacer_yadif: return "yadif"; case mlt_deinterlacer_bwdif: return "bwdif"; case mlt_deinterlacer_estdif: return "estdif"; case mlt_deinterlacer_invalid: return "invalid"; } return "invalid"; } mlt_deinterlacer mlt_deinterlacer_id(const char *name) { mlt_deinterlacer m; for (m = mlt_deinterlacer_none; name && m < mlt_deinterlacer_invalid; m++) { if (!strcmp(mlt_deinterlacer_name(m), name)) return m; } return mlt_deinterlacer_invalid; } static uint8_t srgb_to_linear(uint8_t value) { // Normalize the 8-bit value to the [0.0, 1.0] range double factor = (double) value / 255.0; if (factor < 0.04045) { factor *= 0.0773993808; } else { factor = pow(factor * 0.9478672986 + 0.0521327014, 2.4); } // Map back to 0-255 range double linear_value = round((double) 255 * factor); return (uint8_t) CLAMP(linear_value, 0.0, 255.0); } /** Convert a standard sRGB color value to the specified TRC (Transfer Characteristics). * * \param color a standard sRGB color value * \param trc_name a string representing the TRC to convert to * \return the converted color */ mlt_color mlt_color_convert_trc(mlt_color color, const char *trc_name) { mlt_color_trc trc = mlt_image_color_trc_id(trc_name); // Only linear is supported for now. if (trc == mlt_color_trc_linear) { color.r = srgb_to_linear(color.r); color.g = srgb_to_linear(color.g); color.b = srgb_to_linear(color.b); } return color; } mlt-7.38.0/src/framework/mlt_types.h000664 000000 000000 00000037024 15172202314 017345 0ustar00rootroot000000 000000 /** * \file mlt_types.h * \brief Provides forward definitions of all public types * * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_TYPES_H #define MLT_TYPES_H #ifdef __cplusplus extern "C" { #endif #include "mlt_export.h" #include "mlt_pool.h" #include #include #include #ifndef PATH_MAX #define PATH_MAX 4096 #endif /** The set of supported image formats */ typedef enum { mlt_image_none = 0, /**< image not available */ mlt_image_rgb, /**< 8-bit RGB */ mlt_image_rgba, /**< 8-bit RGB with alpha channel */ mlt_image_yuv422, /**< 8-bit YUV 4:2:2 packed */ mlt_image_yuv420p, /**< 8-bit YUV 4:2:0 planar */ mlt_image_movit, /**< for movit module internal use only */ mlt_image_opengl_texture, /**< an OpenGL texture name */ mlt_image_yuv422p16, /**< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian */ mlt_image_yuv420p10, /**< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian */ mlt_image_yuv444p10, /**< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian */ mlt_image_rgba64, /**< 16-bit RGB with alpha channel */ mlt_image_invalid } mlt_image_format; /** The set of supported audio formats */ typedef enum { mlt_audio_none = 0, /**< audio not available */ mlt_audio_s16 = 1, /**< signed 16-bit interleaved PCM */ mlt_audio_s32, /**< signed 32-bit non-interleaved PCM */ mlt_audio_float, /**< 32-bit non-interleaved floating point */ mlt_audio_s32le, /**< signed 32-bit interleaved PCM */ mlt_audio_f32le, /**< 32-bit interleaved floating point */ mlt_audio_u8 /**< unsigned 8-bit interleaved PCM */ } mlt_audio_format; typedef enum { mlt_channel_auto = 0, /**< MLT will determine the default configuration based on channel number */ mlt_channel_independent, /**< channels are not related */ mlt_channel_mono, mlt_channel_stereo, mlt_channel_2p1, mlt_channel_3p0, mlt_channel_3p0_back, mlt_channel_4p0, mlt_channel_quad_back, mlt_channel_quad_side, mlt_channel_3p1, mlt_channel_5p0_back, mlt_channel_5p0, mlt_channel_4p1, mlt_channel_5p1_back, mlt_channel_5p1, mlt_channel_6p0, mlt_channel_6p0_front, mlt_channel_hexagonal, mlt_channel_6p1, mlt_channel_6p1_back, mlt_channel_6p1_front, mlt_channel_7p0, mlt_channel_7p0_front, mlt_channel_7p1, mlt_channel_7p1_wide_side, mlt_channel_7p1_wide_back, } mlt_channel_layout; /** Colorspace definitions */ typedef enum { mlt_colorspace_rgb = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB) mlt_colorspace_bt709 = 709, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B mlt_colorspace_unspecified = 2, mlt_colorspace_reserved = 3, mlt_colorspace_fcc = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20) mlt_colorspace_bt470bg = 470, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601 mlt_colorspace_smpte170m = 170, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC mlt_colorspace_smpte240m = 240, ///< functionally identical to above mlt_colorspace_ycgco = 8, ///< Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16 mlt_colorspace_bt2020_ncl = 2020, ///< ITU-R BT2020 non-constant luminance system mlt_colorspace_bt2020_cl = 2021, ///< ITU-R BT2020 constant luminance system mlt_colorspace_smpte2085 = 11, ///< SMPTE 2085, Y'D'zD'x mlt_colorspace_bt601 = 601, ///< BT.470 (625/PAL) or SMPTE170M (525/NTSC) mlt_colorspace_invalid } mlt_colorspace; typedef enum { mlt_color_trc_none = 0, mlt_color_trc_bt709 = 1, ///< also ITU-R BT1361 mlt_color_trc_unspecified = 2, mlt_color_trc_reserved = 3, mlt_color_trc_gamma22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM mlt_color_trc_gamma28 = 5, ///< also ITU-R BT470BG mlt_color_trc_smpte170m = 6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC mlt_color_trc_smpte240m = 7, mlt_color_trc_linear = 8, ///< "Linear transfer characteristics" mlt_color_trc_log = 9, ///< "Logarithmic transfer characteristic (100:1 range)" mlt_color_trc_log_sqrt = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)" mlt_color_trc_iec61966_2_4 = 11, ///< IEC 61966-2-4 mlt_color_trc_bt1361_ecg = 12, ///< ITU-R BT1361 Extended Colour Gamut mlt_color_trc_iec61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC) mlt_color_trc_bt2020_10 = 14, ///< ITU-R BT2020 for 10-bit system mlt_color_trc_bt2020_12 = 15, ///< ITU-R BT2020 for 12-bit system mlt_color_trc_smpte2084 = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems mlt_color_trc_smpte428 = 17, ///< SMPTE ST 428-1 mlt_color_trc_arib_std_b67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma" mlt_color_trc_invalid } mlt_color_trc; typedef enum { mlt_color_pri_none = 0, mlt_color_pri_bt709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B mlt_color_pri_bt470m = 4, ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20) mlt_color_pri_bt470bg = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM mlt_color_pri_smpte170m = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC mlt_color_pri_film = 8, ///< colour filters using Illuminant C mlt_color_pri_bt2020 = 9, ///< ITU-R BT2020 mlt_color_pri_smpte428 = 10, ///< SMPTE ST 428-1 (CIE 1931 XYZ) mlt_color_pri_smpte431 = 11, ///< SMPTE ST 431-2 (2011) / DCI P3 mlt_color_pri_smpte432 = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3 mlt_color_pri_invalid } mlt_color_primaries; typedef enum { mlt_deinterlacer_none, mlt_deinterlacer_onefield, mlt_deinterlacer_linearblend, mlt_deinterlacer_weave, mlt_deinterlacer_bob, mlt_deinterlacer_greedy, mlt_deinterlacer_yadif_nospatial, mlt_deinterlacer_yadif, mlt_deinterlacer_bwdif, mlt_deinterlacer_estdif, mlt_deinterlacer_invalid, } mlt_deinterlacer; /** The time string formats */ typedef enum { mlt_time_frames = 0, /**< frame count */ mlt_time_clock, /**< SMIL clock-value as [[hh:]mm:]ss[.fraction] */ mlt_time_smpte_df, /**< SMPTE timecode as [[[hh:]mm:]ss{:|;}]frames */ mlt_time_smpte_ndf /**< SMPTE NDF timecode as [[[hh:]mm:]ss:]frames */ } mlt_time_format; /** Interpolation methods for animation keyframes */ typedef enum { mlt_keyframe_discrete = 0, /**< non-interpolated; value changes instantaneously at the key frame */ mlt_keyframe_linear, /**< simple, constant pace from this key frame to the next */ mlt_keyframe_smooth, /**< deprecated use mlt_keyframe_smooth_loose */ mlt_keyframe_smooth_loose = mlt_keyframe_smooth, /**< Unity Catmull-Rom spline interpolation. May have cusps or overshoots*/ mlt_keyframe_smooth_natural, /**< Centripetal Catmull-Rom spline interpolation with natural slope at each keyframe. Will not have cusps or overshoots*/ mlt_keyframe_smooth_tight, /**< Centripetal Catmull-Rom spline interpolation with 0 slope at each keyframe. Will not have cusps or overshoots*/ mlt_keyframe_sinusoidal_in, mlt_keyframe_sinusoidal_out, mlt_keyframe_sinusoidal_in_out, mlt_keyframe_quadratic_in, mlt_keyframe_quadratic_out, mlt_keyframe_quadratic_in_out, mlt_keyframe_cubic_in, mlt_keyframe_cubic_out, mlt_keyframe_cubic_in_out, mlt_keyframe_quartic_in, mlt_keyframe_quartic_out, mlt_keyframe_quartic_in_out, mlt_keyframe_quintic_in, mlt_keyframe_quintic_out, mlt_keyframe_quintic_in_out, mlt_keyframe_exponential_in, mlt_keyframe_exponential_out, mlt_keyframe_exponential_in_out, mlt_keyframe_circular_in, mlt_keyframe_circular_out, mlt_keyframe_circular_in_out, mlt_keyframe_back_in, mlt_keyframe_back_out, mlt_keyframe_back_in_out, mlt_keyframe_elastic_in, mlt_keyframe_elastic_out, mlt_keyframe_elastic_in_out, mlt_keyframe_bounce_in, mlt_keyframe_bounce_out, mlt_keyframe_bounce_in_out, } mlt_keyframe_type; /** The relative time qualifiers */ typedef enum { mlt_whence_relative_start = 0, /**< relative to the beginning */ mlt_whence_relative_current, /**< relative to the current position */ mlt_whence_relative_end /**< relative to the end */ } mlt_whence; /** The recognized subclasses of mlt_service */ typedef enum { mlt_service_invalid_type = 0, /**< invalid service */ mlt_service_unknown_type, /**< unknown class */ mlt_service_producer_type, /**< Producer class */ mlt_service_tractor_type, /**< Tractor class */ mlt_service_playlist_type, /**< Playlist class */ mlt_service_multitrack_type, /**< Multitrack class */ mlt_service_filter_type, /**< Filter class */ mlt_service_transition_type, /**< Transition class */ mlt_service_consumer_type, /**< Consumer class */ mlt_service_field_type, /**< Field class */ mlt_service_link_type, /**< Link class */ mlt_service_chain_type /**< Chain class */ } mlt_service_type; /* I don't want to break anyone's applications without warning. -Zach */ #ifdef DOUBLE_MLT_POSITION #define MLT_POSITION_FMT "%f" #define MLT_POSITION_MOD(A, B) ((A) - (B) * ((int) ((A) / (B)))) typedef double mlt_position; #else #define MLT_POSITION_MOD(A, B) ((A) % (B)) #define MLT_POSITION_FMT "%d" typedef int32_t mlt_position; #endif /** A rectangle type with coordinates, size, and opacity */ typedef struct { double x; /**< X coordinate */ double y; /**< Y coordinate */ double w; /**< width */ double h; /**< height */ double o; /**< opacity / mix-level */ } mlt_rect; /** A tuple of color components */ typedef struct { uint8_t r; /**< red */ uint8_t g; /**< green */ uint8_t b; /**< blue */ uint8_t a; /**< alpha */ } mlt_color; typedef struct mlt_audio_s *mlt_audio; /**< pointer to Audio object */ typedef struct mlt_image_s *mlt_image; /**< pointer to Image object */ typedef struct mlt_frame_s *mlt_frame, **mlt_frame_ptr; /**< pointer to Frame object */ typedef struct mlt_property_s *mlt_property; /**< pointer to Property object */ typedef struct mlt_properties_s *mlt_properties; /**< pointer to Properties object */ typedef struct mlt_event_struct *mlt_event; /**< pointer to Event object */ typedef struct mlt_service_s *mlt_service; /**< pointer to Service object */ typedef struct mlt_producer_s *mlt_producer; /**< pointer to Producer object */ typedef struct mlt_playlist_s *mlt_playlist; /**< pointer to Playlist object */ typedef struct mlt_multitrack_s *mlt_multitrack; /**< pointer to Multitrack object */ typedef struct mlt_filter_s *mlt_filter; /**< pointer to Filter object */ typedef struct mlt_transition_s *mlt_transition; /**< pointer to Transition object */ typedef struct mlt_tractor_s *mlt_tractor; /**< pointer to Tractor object */ typedef struct mlt_field_s *mlt_field; /**< pointer to Field object */ typedef struct mlt_consumer_s *mlt_consumer; /**< pointer to Consumer object */ typedef struct mlt_parser_s *mlt_parser; /**< pointer to Properties object */ typedef struct mlt_deque_s *mlt_deque; /**< pointer to Deque object */ typedef struct mlt_geometry_s *mlt_geometry; /**< pointer to Geometry object */ typedef struct mlt_geometry_item_s *mlt_geometry_item; /**< pointer to Geometry Item object */ typedef struct mlt_profile_s *mlt_profile; /**< pointer to Profile object */ typedef struct mlt_repository_s *mlt_repository; /**< pointer to Repository object */ typedef struct mlt_cache_s *mlt_cache; /**< pointer to Cache object */ typedef struct mlt_cache_item_s *mlt_cache_item; /**< pointer to CacheItem object */ typedef struct mlt_animation_s *mlt_animation; /**< pointer to Property Animation object */ typedef struct mlt_slices_s *mlt_slices; /**< pointer to Sliced processing context object */ typedef struct mlt_link_s *mlt_link; /**< pointer to Link object */ typedef struct mlt_chain_s *mlt_chain; /**< pointer to Chain object */ typedef void (*mlt_destructor)(void *); /**< pointer to destructor function */ typedef char *(*mlt_serialiser)(void *, int length); /**< pointer to serialization function */ typedef void *(*mlt_thread_function_t)(void *); /**< generic thread function pointer */ #define MLT_SERVICE(x) ((mlt_service) (x)) /**< Cast to a Service pointer */ #define MLT_PRODUCER(x) ((mlt_producer) (x)) /**< Cast to a Producer pointer */ #define MLT_MULTITRACK(x) ((mlt_multitrack) (x)) /**< Cast to a Multitrack pointer */ #define MLT_PLAYLIST(x) ((mlt_playlist) (x)) /**< Cast to a Playlist pointer */ #define MLT_TRACTOR(x) ((mlt_tractor) (x)) /**< Cast to a Tractor pointer */ #define MLT_FILTER(x) ((mlt_filter) (x)) /**< Cast to a Filter pointer */ #define MLT_TRANSITION(x) ((mlt_transition) (x)) /**< Cast to a Transition pointer */ #define MLT_CONSUMER(x) ((mlt_consumer) (x)) /**< Cast to a Consumer pointer */ #define MLT_FRAME(x) ((mlt_frame) (x)) /**< Cast to a Frame pointer */ #define MLT_LINK(x) ((mlt_link) (x)) /**< Cast to a Link pointer */ #define MLT_CHAIN(x) ((mlt_chain) (x)) /**< Cast to a Chain pointer */ #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif #ifndef CLAMP #define CLAMP(x, min, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) #endif #ifdef _WIN32 #include /* Win32 compatibility function declarations */ #if !defined(__MINGW32__) MLT_EXPORT int usleep(unsigned int useconds); #endif #ifndef WIN_PTHREADS_TIME_H MLT_EXPORT int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); #endif MLT_EXPORT int setenv(const char *name, const char *value, int overwrite); MLT_EXPORT char *getlocale(); MLT_EXPORT FILE *win32_fopen(const char *filename_utf8, const char *mode_utf8); #include #include MLT_EXPORT int win32_stat(const char *filename_utf8, struct stat *buffer); #include #define mlt_fopen win32_fopen #define mlt_stat win32_stat #define MLT_DIRLIST_DELIMITER ";" #else #define mlt_fopen fopen #define mlt_stat stat #define MLT_DIRLIST_DELIMITER ":" #endif /* ifdef _WIN32 */ MLT_EXPORT const char *mlt_deinterlacer_name(mlt_deinterlacer method); MLT_EXPORT mlt_deinterlacer mlt_deinterlacer_id(const char *name); MLT_EXPORT mlt_color mlt_color_convert_trc(mlt_color color, const char *trc_name); #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/framework/mlt_version.c000664 000000 000000 00000002271 15172202314 017655 0ustar00rootroot000000 000000 /** * \file mlt_version.c * \brief contains version information * * Copyright (C) 2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_version.h" int mlt_version_get_int() { return LIBMLT_VERSION_INT; } char *mlt_version_get_string() { return LIBMLT_VERSION; } int mlt_version_get_major() { return LIBMLT_VERSION_MAJOR; } int mlt_version_get_minor() { return LIBMLT_VERSION_MINOR; } int mlt_version_get_revision() { return LIBMLT_VERSION_REVISION; } mlt-7.38.0/src/framework/mlt_version.h000664 000000 000000 00000003060 15172202314 017657 0ustar00rootroot000000 000000 /** * \file mlt_version.h * \brief contains version information * * Copyright (C) 2010-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_VERSION_H #define MLT_VERSION_H #include "mlt_export.h" // Add quotes around any #define variables #define MLT_STRINGIZE2(s) #s #define MLT_STRINGIZE(s) MLT_STRINGIZE2(s) #define LIBMLT_VERSION_MAJOR 7 #define LIBMLT_VERSION_MINOR 38 #define LIBMLT_VERSION_REVISION 0 #define LIBMLT_VERSION_INT \ ((LIBMLT_VERSION_MAJOR << 16) + (LIBMLT_VERSION_MINOR << 8) + LIBMLT_VERSION_REVISION) #define LIBMLT_VERSION \ MLT_STRINGIZE(LIBMLT_VERSION_MAJOR.LIBMLT_VERSION_MINOR.LIBMLT_VERSION_REVISION) MLT_EXPORT int mlt_version_get_int(); MLT_EXPORT int mlt_version_get_major(); MLT_EXPORT int mlt_version_get_minor(); MLT_EXPORT int mlt_version_get_revision(); MLT_EXPORT char *mlt_version_get_string(); #endif mlt-7.38.0/src/melt/000775 000000 000000 00000000000 15172202314 014112 5ustar00rootroot000000 000000 mlt-7.38.0/src/melt/CMakeLists.txt000664 000000 000000 00000002675 15172202314 016664 0ustar00rootroot000000 000000 add_executable(melt melt.c io.c io.h) target_compile_options(melt PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(melt PRIVATE mlt Threads::Threads) target_compile_definitions(melt PRIVATE VERSION="${MLT_VERSION}") if(SDL2_FOUND AND NOT ANDROID) target_link_libraries(melt PRIVATE SDL2::SDL2) target_compile_definitions(melt PRIVATE HAVE_SDL2) if(MINGW) target_link_libraries(melt PRIVATE ${MLT_PTHREAD_LIBS}) target_compile_definitions(melt PRIVATE main=SDL_main) target_link_libraries(melt PRIVATE mingw32 SDL2main SDL2) endif() endif() if(MSVC) target_link_libraries(melt PRIVATE msvccompat) endif() if(MINGW) target_link_options(melt PRIVATE -mconsole) endif() if(UNIX AND NOT APPLE) install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E remove melt melt-${MLT_VERSION_MAJOR} \ WORKING_DIRECTORY \$ENV\{DESTDIR\}${CMAKE_INSTALL_FULL_BINDIR})" ) endif() install(TARGETS melt RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) if(UNIX AND NOT APPLE) install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E rename melt melt-${MLT_VERSION_MAJOR} \ WORKING_DIRECTORY \$ENV\{DESTDIR\}${CMAKE_INSTALL_FULL_BINDIR})" ) install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink melt-${MLT_VERSION_MAJOR} melt \ WORKING_DIRECTORY \$ENV\{DESTDIR\}${CMAKE_INSTALL_FULL_BINDIR})" ) endif() mlt-7.38.0/src/melt/io.c000664 000000 000000 00000011622 15172202314 014667 0ustar00rootroot000000 000000 /* * io.c -- melt input/output * Copyright (C) 2002-2015 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif /* System header files */ #include #include #include #include #ifndef _WIN32 #include #include #else // MinGW defines struct timespec in pthread.h #include // for nanosleep() #include #include #endif /* Application header files */ #include "io.h" char *chomp(char *input) { if (input != NULL) { int length = strlen(input); if (length && input[length - 1] == '\n') input[length - 1] = '\0'; if (length > 1 && input[length - 2] == '\r') input[length - 2] = '\0'; } return input; } char *trim(char *input) { if (input != NULL) { int length = strlen(input); int first = 0; while (first < length && isspace(input[first])) first++; memmove(input, input + first, length - first + 1); length = length - first; while (length > 0 && isspace(input[length - 1])) input[--length] = '\0'; } return input; } char *strip_quotes(char *input) { if (input != NULL) { char *ptr = strrchr(input, '\"'); if (ptr != NULL) *ptr = '\0'; if (input[0] == '\"') strcpy(input, input + 1); } return input; } int *get_int(int *output, int use) { int *value = NULL; char temp[132]; *output = use; if (trim(chomp(fgets(temp, 132, stdin))) != NULL) { if (strcmp(temp, "")) *output = atoi(temp); value = output; } return value; } /** This stores the previous settings */ #ifndef _WIN32 static struct termios oldtty; #else static DWORD oldtty; #endif static int mode = 0; /** This is called automatically on application exit to restore the previous tty settings. */ void term_exit(void) { if (mode == 1) { #ifndef _WIN32 tcsetattr(0, TCSANOW, &oldtty); #else HANDLE h = GetStdHandle(STD_INPUT_HANDLE); if (h) { SetConsoleMode(h, oldtty); } #endif mode = 0; } } /** Init terminal so that we can grab keys without blocking. */ void term_init() { #ifndef _WIN32 struct termios tty; tcgetattr(0, &tty); oldtty = tty; tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); tty.c_oflag |= OPOST; tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); tty.c_cflag &= ~(CSIZE | PARENB); tty.c_cflag |= CS8; tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 0; tcsetattr(0, TCSANOW, &tty); #else HANDLE h = GetStdHandle(STD_INPUT_HANDLE); if (h) { DWORD tty; GetConsoleMode(h, &tty); oldtty = tty; SetConsoleMode(h, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)); } #endif mode = 1; atexit(term_exit); } /** Check for a keypress without blocking infinitely. Returns: ASCII value of keypress or -1 if no keypress detected. */ int term_read() { #ifndef _WIN32 int n = 1; unsigned char ch; struct timeval tv; fd_set rfds; FD_ZERO(&rfds); FD_SET(0, &rfds); tv.tv_sec = 0; tv.tv_usec = 40000; n = select(1, &rfds, NULL, NULL, &tv); if (n > 0) { n = read(0, &ch, 1); tcflush(0, TCIFLUSH); if (n == 1) return ch; return n; } #else HANDLE h = GetStdHandle(STD_INPUT_HANDLE); if (h && WaitForSingleObject(h, 0) == WAIT_OBJECT_0) { DWORD count; TCHAR c = 0; ReadConsole(h, &c, 1, &count, NULL); return (int) c; } else { struct timespec tm = {0, 40000000}; nanosleep(&tm, NULL); return 0; } #endif return -1; } char get_keypress() { char value = '\0'; int pressed = 0; fflush(stdout); term_init(); while ((pressed = term_read()) == -1) ; term_exit(); value = (char) pressed; return value; } void wait_for_any_key(char *message) { if (message == NULL) printf("Press any key to continue: "); else printf("%s", message); get_keypress(); printf("\n\n"); } void beep() { printf("%c", 7); fflush(stdout); } mlt-7.38.0/src/melt/io.h000664 000000 000000 00000002340 15172202314 014671 0ustar00rootroot000000 000000 /* * io.h -- melt input/output * Copyright (C) 2002-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _DEMO_IO_H_ #define _DEMO_IO_H_ #ifdef __cplusplus extern "C" { #endif extern char *chomp(char *); extern char *trim(char *); extern char *strip_quotes(char *); extern char *get_string(char *, int, char *); extern int *get_int(int *, int); extern void term_init(); extern int term_read(); extern void term_exit(); extern char get_keypress(); extern void wait_for_any_key(char *); extern void beep(); #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/melt/melt.c000664 000000 000000 00000130406 15172202314 015223 0ustar00rootroot000000 000000 /* * melt.c -- MLT command line utility * Copyright (C) 2002-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #ifdef _MSC_VER #ifndef STDIN_FILENO #define STDIN_FILENO _fileno(stdin) #endif #else #include #endif #include #if (defined(__APPLE__) || defined(_WIN32) || defined(HAVE_SDL2)) && !defined(MELT_NOSDL) #define SDL_MAIN_HANDLED #include #endif #include "io.h" static mlt_producer melt = NULL; static void stop_handler(int signum) { if (melt) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(melt); mlt_properties_set_int(properties, "done", 1); } } static void abnormal_exit_handler(int signum) { // The process is going down hard. Restore the terminal first. term_exit(); // Reset the default handler so the core gets dumped. signal(signum, SIG_DFL); raise(signum); } static void fire_jack_seek_event(mlt_properties jack, int position) { mlt_events_fire(jack, "jack-seek", mlt_event_data_from_int(position)); } static void transport_action(mlt_producer producer, char *value) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_multitrack multitrack = mlt_properties_get_data(properties, "multitrack", NULL); mlt_consumer consumer = mlt_properties_get_data(properties, "transport_consumer", NULL); mlt_properties jack = mlt_properties_get_data(MLT_CONSUMER_PROPERTIES(consumer), "jack_filter", NULL); mlt_position position = producer ? mlt_producer_position(producer) : 0; mlt_properties_set_int(properties, "stats_off", 1); if (strlen(value) == 1) { switch (value[0]) { case 'q': case 'Q': mlt_properties_set_int(properties, "done", 1); mlt_events_fire(jack, "jack-stop", mlt_event_data_none()); break; case '0': position = 0; mlt_producer_set_speed(producer, 1); mlt_producer_seek(producer, position); mlt_consumer_purge(consumer); fire_jack_seek_event(jack, position); break; case '1': mlt_producer_set_speed(producer, -10); break; case '2': mlt_producer_set_speed(producer, -5); break; case '3': mlt_producer_set_speed(producer, -2); break; case '4': mlt_producer_set_speed(producer, -1); break; case '5': mlt_producer_set_speed(producer, 0); mlt_consumer_purge(consumer); mlt_producer_seek(producer, mlt_consumer_position(consumer) + 1); mlt_events_fire(jack, "jack-stop", mlt_event_data_none()); break; case '6': case ' ': if (!jack || mlt_producer_get_speed(producer) != 0) mlt_producer_set_speed(producer, 1); mlt_consumer_purge(consumer); mlt_events_fire(jack, "jack-start", mlt_event_data_none()); break; case '7': mlt_producer_set_speed(producer, 2); break; case '8': mlt_producer_set_speed(producer, 5); break; case '9': mlt_producer_set_speed(producer, 10); break; case 'd': if (multitrack != NULL) { int i = 0; mlt_position last = -1; fprintf(stderr, "\n"); for (i = 0; 1; i++) { position = mlt_multitrack_clip(multitrack, mlt_whence_relative_start, i); if (position == last) break; last = position; fprintf(stderr, "%d: %d\n", i, (int) position); } } break; case 'g': if (multitrack != NULL) { position = mlt_multitrack_clip(multitrack, mlt_whence_relative_current, 0); mlt_producer_seek(producer, position); mlt_consumer_purge(consumer); fire_jack_seek_event(jack, position); } break; case 'H': if (producer != NULL) { position -= mlt_producer_get_fps(producer) * 60; mlt_consumer_purge(consumer); mlt_producer_seek(producer, position); fire_jack_seek_event(jack, position); } break; case 'h': if (producer != NULL) { position--; mlt_producer_set_speed(producer, 0); mlt_consumer_purge(consumer); mlt_producer_seek(producer, position); mlt_events_fire(jack, "jack-stop", mlt_event_data_none()); fire_jack_seek_event(jack, position); } break; case 'j': if (multitrack != NULL) { position = mlt_multitrack_clip(multitrack, mlt_whence_relative_current, 1); mlt_consumer_purge(consumer); mlt_producer_seek(producer, position); fire_jack_seek_event(jack, position); } break; case 'k': if (multitrack != NULL) { position = mlt_multitrack_clip(multitrack, mlt_whence_relative_current, -1); mlt_consumer_purge(consumer); mlt_producer_seek(producer, position); fire_jack_seek_event(jack, position); } break; case 'l': if (producer != NULL) { position++; mlt_consumer_purge(consumer); if (mlt_producer_get_speed(producer) != 0) { mlt_producer_set_speed(producer, 0); mlt_events_fire(jack, "jack-stop", mlt_event_data_none()); } else { mlt_producer_seek(producer, position); fire_jack_seek_event(jack, position); } } break; case 'L': if (producer != NULL) { position += mlt_producer_get_fps(producer) * 60; mlt_consumer_purge(consumer); mlt_producer_seek(producer, position); fire_jack_seek_event(jack, position); } break; } mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "refresh", 1); } mlt_properties_set_int(properties, "stats_off", 0); } static void on_jack_started(mlt_properties owner, mlt_consumer consumer, mlt_event_data event_data) { mlt_producer producer = mlt_properties_get_data(MLT_CONSUMER_PROPERTIES(consumer), "transport_producer", NULL); if (producer) { if (mlt_producer_get_speed(producer) != 0) { mlt_properties jack = mlt_properties_get_data(MLT_CONSUMER_PROPERTIES(consumer), "jack_filter", NULL); mlt_events_fire(jack, "jack-stop", mlt_event_data_none()); } else { mlt_position position = mlt_event_data_to_int(event_data); mlt_producer_set_speed(producer, 1); mlt_consumer_purge(consumer); mlt_producer_seek(producer, position); mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "refresh", 1); } } } static void on_jack_stopped(mlt_properties owner, mlt_consumer consumer, mlt_event_data event_data) { mlt_producer producer = mlt_properties_get_data(MLT_CONSUMER_PROPERTIES(consumer), "transport_producer", NULL); if (producer) { mlt_position position = mlt_event_data_to_int(event_data); mlt_producer_set_speed(producer, 0); mlt_consumer_purge(consumer); mlt_producer_seek(producer, position); mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "refresh", 1); } } static void setup_jack_transport(mlt_consumer consumer, mlt_profile profile) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_filter jack = mlt_factory_filter(profile, "jackrack", NULL); mlt_properties jack_properties = MLT_FILTER_PROPERTIES(jack); mlt_service_attach(MLT_CONSUMER_SERVICE(consumer), jack); mlt_properties_set_int(properties, "audio_off", 1); mlt_properties_set_data(properties, "jack_filter", jack, 0, (mlt_destructor) mlt_filter_close, NULL); // mlt_properties_set( jack_properties, "out_1", "system:playback_1" ); // mlt_properties_set( jack_properties, "out_2", "system:playback_2" ); mlt_events_listen(jack_properties, consumer, "jack-started", (mlt_listener) on_jack_started); mlt_events_listen(jack_properties, consumer, "jack-stopped", (mlt_listener) on_jack_stopped); } static mlt_consumer create_consumer(mlt_profile profile, char *id) { char *myid = id ? strdup(id) : NULL; char *arg = myid ? strchr(myid, ':') : NULL; if (arg != NULL) *arg++ = '\0'; mlt_consumer consumer = mlt_factory_consumer(profile, myid, arg); if (consumer != NULL) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_properties_set_data(properties, "transport_callback", transport_action, 0, NULL, NULL); } free(myid); return consumer; } static int load_consumer(mlt_consumer *consumer, mlt_profile profile, int argc, char **argv) { int i; int multi = 0; int qglsl = 0; for (i = 1; i < argc; i++) { // See if we need multi consumer. multi += !strcmp(argv[i], "-consumer"); // See if we need the qglsl variant of multi consumer. if (!strncmp(argv[i], "glsl.", 5) || !strncmp(argv[i], "movit.", 6)) qglsl = 1; #if SDL_MAJOR_VERSION == 2 if (!strcmp("sdl", argv[i]) || !strcmp("sdl_audio", argv[i]) || !strcmp("sdl_preview", argv[i]) || !strcmp("sdl_still", argv[i])) { fprintf(stderr, "Error: This program was linked against SDL2, which is incompatible with\nSDL1 " "consumers. Aborting.\n"); return EXIT_FAILURE; } #endif } // Disable qglsl if xgl is being used! for (i = 1; qglsl && i < argc; i++) if (!strcmp(argv[i], "xgl")) qglsl = 0; if (multi > 1 || qglsl) { // If there is more than one -consumer use the 'multi' consumer. int k = 0; char key[20]; if (*consumer) mlt_consumer_close(*consumer); *consumer = create_consumer(profile, (qglsl ? "qglsl" : "multi")); mlt_properties properties = MLT_CONSUMER_PROPERTIES(*consumer); for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-consumer") && argv[i + 1]) { // Create a properties object for each sub-consumer mlt_properties new_props = mlt_properties_new(); snprintf(key, sizeof(key), "%d", k++); mlt_properties_set_data(properties, key, new_props, 0, (mlt_destructor) mlt_properties_close, NULL); if (strchr(argv[i + 1], ':')) { char *temp = strdup(argv[++i]); char *service = temp; char *target = strchr(temp, ':'); *target++ = 0; mlt_properties_set(new_props, "mlt_service", service); mlt_properties_set(new_props, "target", target); } else { mlt_properties_set(new_props, "mlt_service", argv[++i]); } while (argv[i + 1] && strchr(argv[i + 1], '=')) mlt_properties_parse(new_props, argv[++i]); } } } else { for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-consumer")) { if (*consumer) mlt_consumer_close(*consumer); *consumer = create_consumer(profile, argv[++i]); if (*consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(*consumer); while (argv[i + 1] != NULL && strchr(argv[i + 1], '=')) mlt_properties_parse(properties, argv[++i]); } } } } return EXIT_SUCCESS; } #if defined(SDL_MAJOR_VERSION) static void event_handling(mlt_producer producer, mlt_consumer consumer) { SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(producer), "done", 1); break; case SDL_KEYDOWN: #if SDL_MAJOR_VERSION == 2 if (event.key.keysym.sym < 0x80 && event.key.keysym.sym > 0) { char keyboard[2] = {event.key.keysym.sym, 0}; if (event.key.keysym.mod & KMOD_SHIFT) keyboard[0] += 'A' - 'a'; transport_action(producer, keyboard); } break; case SDL_WINDOWEVENT: if (mlt_properties_get(MLT_CONSUMER_PROPERTIES(consumer), "mlt_service") && !strcmp("sdl2", mlt_properties_get(MLT_CONSUMER_PROPERTIES(consumer), "mlt_service"))) if (event.window.event == SDL_WINDOWEVENT_RESIZED || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "window_width", event.window.data1); mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "window_height", event.window.data2); } break; #else if (event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0) { char keyboard[2] = {event.key.keysym.unicode, 0}; transport_action(producer, keyboard); } break; #endif } } } #endif static void transport(mlt_producer producer, mlt_consumer consumer) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); int silent = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "silent"); int progress = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "progress"); int is_getc = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "melt_getc"); struct timespec tm = {0, 40000000}; int total_length = mlt_producer_get_playtime(producer); int last_position = 0; if (mlt_properties_get_int(properties, "done") == 0 && !mlt_consumer_is_stopped(consumer)) { if (!silent && !progress) { if (!is_getc) term_init(); fprintf(stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n"); fprintf(stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5= 0| |6= 1| |7= 2| |8= 5| |9= 10|\n"); fprintf(stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n"); fprintf(stderr, "+---------------------------------------------------------------------+\n"); fprintf(stderr, "| H = back 1 minute, L = forward 1 minute |\n"); fprintf(stderr, "| h = previous frame, l = next frame |\n"); fprintf(stderr, "| g = start of clip, j = next clip, k = previous clip |\n"); fprintf(stderr, "| 0 = restart, q = quit, space = play |\n"); fprintf(stderr, "+---------------------------------------------------------------------+\n"); } while (mlt_properties_get_int(properties, "done") == 0 && !mlt_consumer_is_stopped(consumer)) { int value = (silent || progress || is_getc) ? -1 : term_read(); if (is_getc) { value = getc(stdin); value = (value == EOF) ? 'q' : value; } if (value != -1) { char string[2] = {value, 0}; transport_action(producer, string); } #if defined(SDL_MAJOR_VERSION) event_handling(producer, consumer); #endif if (!silent && mlt_properties_get_int(properties, "stats_off") == 0) { if (progress) { int current_position = mlt_producer_position(producer); if (current_position > last_position) { fprintf(stderr, "Current Frame: %10d, percentage: %10d%c", current_position, 100 * current_position / total_length, progress == 2 ? '\n' : '\r'); last_position = current_position; } } else { fprintf(stderr, "Current Position: %10d\r", (int) mlt_consumer_position(consumer)); } fflush(stderr); } if (silent || progress) nanosleep(&tm, NULL); } if (!silent) fprintf(stderr, "\n"); } } static void show_usage(char *program_name) { fprintf( stdout, "Usage: %s [options] [producer [name=value]* ]+\n" "Options:\n" " -attach filter[:arg] [name=value]* Attach a filter to the output\n" " -attach-cut filter[:arg] [name=value]* Attach a filter to a cut\n" " -attach-track filter[:arg] [name=value]* Attach a filter to a track\n" " -attach-clip filter[:arg] [name=value]* Attach a filter to a producer\n" " -audio-track | -hide-video Add an audio-only track\n" " -blank frames Add blank silence to a track\n" " -chain id[:arg] [name=value]* Add a producer as a chain\n" " -consumer id[:arg] [name=value]* Set the consumer (sink)\n" " -debug Set the logging level to debug\n" " -filter filter[:arg] [name=value]* Add a filter to the current track\n" " -getc Get keyboard input using getc\n" " -group [name=value]* Apply properties repeatedly\n" " -help Show this message\n" " -jack Enable JACK transport synchronization\n" " -join clips Join multiple clips into one cut\n" " -link id[:arg] [name=value]* Add a link to a chain\n" " -loglevel \"quiet\" | \"panic\" | \"fatal\" | Set the logging level (least to most)\n" " \"error\" | \"warning\" | \"info\" | \"verbose\" | \"debug\" | \"timings\"\n" " -mix length Add a mix between the last two cuts\n" " -mixer transition Add a transition to the mix\n" " -null-track | -hide-track Add a hidden track\n" " -profile name Set the processing settings\n" " -progress Display progress along with position\n" " -progress2 Display progress along with position on a new " "line\n" " -query List all of the registered services\n" " -query \"consumers\" | \"consumer\"=id List consumers or show info about one\n" " -query \"filters\" | \"filter\"=id List filters or show info about one\n" " -query \"links\" | \"link\"=id List links or show info about one\n" " -query \"producers\" | \"producer\"=id List producers or show info about one\n" " -query \"transitions\" | \"transition\"=id List transitions, show info about one\n" " -query \"profiles\" | \"profile\"=id List profiles, show info about one\n" " -query \"presets\" | \"preset\"=id List presets, show info about one\n" " -query \"formats\" List audio/video formats\n" " -query \"audio_codecs\" List audio codecs\n" " -query \"video_codecs\" List video codecs\n" " -quiet Set the logging level to quiet\n" " -remove Remove the most recent cut\n" " -repeat times Repeat the last cut\n" " -repository path Set the directory of MLT modules\n" " -serialise [filename] Write the commands to a text file\n" " -setlocale Make numeric strings locale-sensitive\n" " -silent Do not display position/transport\n" " -split relative-frame Split the last cut into two cuts\n" " -swap Rearrange the last two cuts\n" " -track Add a track\n" " -transition id[:arg] [name=value]* Add a transition\n" " -verbose Set the logging level to verbose\n" " -timings Set the logging level to timings\n" " -version Show the version and copyright\n" " -video-track | -hide-audio Add a video-only track\n" "For more help: \n", basename(program_name)); } static void query_metadata(mlt_repository repo, mlt_service_type type, const char *typestr, char *id) { mlt_properties metadata = mlt_repository_metadata(repo, type, id); if (metadata) { char *s = mlt_properties_serialise_yaml(metadata); fprintf(stdout, "%s", s); free(s); } else { fprintf(stdout, "# No metadata for %s \"%s\"\n", typestr, id); } } static int is_service_hidden(mlt_repository repo, mlt_service_type type, const char *service_name) { mlt_properties metadata = NULL; mlt_properties tags = NULL; metadata = mlt_repository_metadata(repo, type, service_name); if (metadata) { tags = mlt_properties_get_data(metadata, "tags", NULL); if (tags) { int k; for (k = 0; k < mlt_properties_count(tags); k++) { const char *value = mlt_properties_get_value(tags, k); if (!strcmp("Hidden", value)) { return 1; } } } } return 0; } static void query_services(mlt_repository repo, mlt_service_type type) { mlt_properties services = NULL; const char *typestr = NULL; switch (type) { case mlt_service_consumer_type: services = mlt_repository_consumers(repo); typestr = "consumers"; break; case mlt_service_filter_type: services = mlt_repository_filters(repo); typestr = "filters"; break; case mlt_service_link_type: services = mlt_repository_links(repo); typestr = "links"; break; case mlt_service_producer_type: services = mlt_repository_producers(repo); typestr = "producers"; break; case mlt_service_transition_type: services = mlt_repository_transitions(repo); typestr = "transitions"; break; default: return; } fprintf(stdout, "---\n%s:\n", typestr); if (services) { int j; for (j = 0; j < mlt_properties_count(services); j++) { const char *service_name = mlt_properties_get_name(services, j); if (!is_service_hidden(repo, type, service_name)) fprintf(stdout, " - %s\n", service_name); } } fprintf(stdout, "...\n"); } static void query_profiles() { mlt_properties profiles = mlt_profile_list(); fprintf(stdout, "---\nprofiles:\n"); if (profiles) { int j; for (j = 0; j < mlt_properties_count(profiles); j++) fprintf(stdout, " - %s\n", mlt_properties_get_name(profiles, j)); } fprintf(stdout, "...\n"); mlt_properties_close(profiles); } static void query_profile(const char *id) { mlt_properties profiles = mlt_profile_list(); mlt_properties profile = mlt_properties_get_data(profiles, id, NULL); if (profile) { char *s = mlt_properties_serialise_yaml(profile); fprintf(stdout, "%s", s); free(s); } else { fprintf(stdout, "# No metadata for profile \"%s\"\n", id); } mlt_properties_close(profiles); } static void query_presets() { mlt_properties presets = mlt_repository_presets(); fprintf(stdout, "---\npresets:\n"); if (presets) { int j; for (j = 0; j < mlt_properties_count(presets); j++) fprintf(stdout, " - %s\n", mlt_properties_get_name(presets, j)); } fprintf(stdout, "...\n"); mlt_properties_close(presets); } static void query_preset(const char *id) { mlt_properties presets = mlt_repository_presets(); mlt_properties preset = mlt_properties_get_data(presets, id, NULL); if (preset) { char *s = mlt_properties_serialise_yaml(preset); fprintf(stdout, "%s", s); free(s); } else { fprintf(stdout, "# No metadata for preset \"%s\"\n", id); } mlt_properties_close(presets); } static void query_formats() { mlt_consumer consumer = mlt_factory_consumer(NULL, "avformat", NULL); if (consumer) { mlt_properties_set(MLT_CONSUMER_PROPERTIES(consumer), "f", "list"); mlt_consumer_start(consumer); mlt_consumer_close(consumer); } else { fprintf(stdout, "# No formats - failed to load avformat consumer\n"); } } static void query_acodecs() { mlt_consumer consumer = mlt_factory_consumer(NULL, "avformat", NULL); if (consumer) { mlt_properties_set(MLT_CONSUMER_PROPERTIES(consumer), "acodec", "list"); mlt_consumer_start(consumer); mlt_consumer_close(consumer); } else { fprintf(stdout, "# No audio codecs - failed to load avformat consumer\n"); } } static void query_vcodecs() { mlt_consumer consumer = mlt_factory_consumer(NULL, "avformat", NULL); if (consumer) { mlt_properties_set(MLT_CONSUMER_PROPERTIES(consumer), "vcodec", "list"); mlt_consumer_start(consumer); mlt_consumer_close(consumer); } else { fprintf(stdout, "# No video codecs - failed to load avformat consumer\n"); } } static void on_fatal_error(mlt_properties owner, mlt_consumer consumer) { mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "done", 1); mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "melt_error", 1); } static void set_preview_scale(mlt_profile *profile, mlt_profile *backup_profile, double scale) { *backup_profile = mlt_profile_clone(*profile); if (*backup_profile) { mlt_profile temp = *profile; *profile = *backup_profile; *backup_profile = temp; (*profile)->width *= scale; (*profile)->width -= (*profile)->width % 2; (*profile)->height *= scale; (*profile)->height -= (*profile)->height % 2; } } static mlt_repository setup_factory(const char *repo_path, int set_locale) { mlt_repository repo = mlt_factory_init(repo_path); if (repo && set_locale) { // Load the system locales const char *locale = ""; #if defined(_WIN32) if (getenv("LC_ALL")) locale = getenv("LC_ALL"); #endif setlocale(LC_ALL, locale); } return repo; } int main(int argc, char **argv) { int i; mlt_consumer consumer = NULL; FILE *store = NULL; char *name = NULL; mlt_profile profile = NULL; int is_progress = 0; int is_silent = 0; int is_getc = 0; int error = 0; mlt_profile backup_profile = NULL; mlt_repository repo = NULL; const char *repo_path = NULL; int is_consumer_explicit = 0; int is_setlocale = 0; // Handle abnormal exit situations. signal(SIGSEGV, abnormal_exit_handler); signal(SIGILL, abnormal_exit_handler); signal(SIGABRT, abnormal_exit_handler); for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-setlocale")) { is_setlocale = 1; break; } } for (i = 1; i < argc; i++) { // Check for serialisation switch if (!strcmp(argv[i], "-serialise")) { name = argv[++i]; if (name != NULL && strstr(name, ".melt")) store = fopen(name, "w"); else { if (name == NULL || name[0] == '-') store = stdout; name = NULL; } } // Look for the profile option else if (!strcmp(argv[i], "-profile")) { // Construct the factory if (!repo) repo = setup_factory(repo_path, is_setlocale); const char *pname = argv[++i]; if (pname && pname[0] != '-') profile = mlt_profile_init(pname); } else if (!strcmp(argv[i], "-progress")) { is_progress = 1; } else if (!strcmp(argv[i], "-progress2")) { is_progress = 2; } // Look for the query option else if (!strcmp(argv[i], "-query")) { // Construct the factory if (!repo) repo = setup_factory(repo_path, is_setlocale); const char *pname = argv[++i]; if (pname && pname[0] != '-') { if (!strcmp(pname, "consumers") || !strcmp(pname, "consumer")) query_services(repo, mlt_service_consumer_type); else if (!strcmp(pname, "filters") || !strcmp(pname, "filter")) query_services(repo, mlt_service_filter_type); else if (!strcmp(pname, "links") || !strcmp(pname, "link")) query_services(repo, mlt_service_link_type); else if (!strcmp(pname, "producers") || !strcmp(pname, "producer")) query_services(repo, mlt_service_producer_type); else if (!strcmp(pname, "transitions") || !strcmp(pname, "transition")) query_services(repo, mlt_service_transition_type); else if (!strcmp(pname, "profiles") || !strcmp(pname, "profile")) query_profiles(); else if (!strcmp(pname, "presets") || !strcmp(pname, "preset")) query_presets(); else if (!strncmp(pname, "format", 6)) query_formats(); else if (!strncmp(pname, "acodec", 6) || !strcmp(pname, "audio_codecs")) query_acodecs(); else if (!strncmp(pname, "vcodec", 6) || !strcmp(pname, "video_codecs")) query_vcodecs(); else if (!strncmp(pname, "consumer=", 9)) query_metadata(repo, mlt_service_consumer_type, "consumer", strchr(pname, '=') + 1); else if (!strncmp(pname, "filter=", 7)) query_metadata(repo, mlt_service_filter_type, "filter", strchr(pname, '=') + 1); else if (!strncmp(pname, "link=", 5)) query_metadata(repo, mlt_service_link_type, "link", strchr(pname, '=') + 1); else if (!strncmp(pname, "producer=", 9)) query_metadata(repo, mlt_service_producer_type, "producer", strchr(pname, '=') + 1); else if (!strncmp(pname, "transition=", 11)) query_metadata(repo, mlt_service_transition_type, "transition", strchr(pname, '=') + 1); else if (!strncmp(pname, "profile=", 8)) query_profile(strchr(pname, '=') + 1); else if (!strncmp(pname, "preset=", 7)) query_preset(strchr(pname, '=') + 1); else goto query_all; } else { query_all: query_services(repo, mlt_service_consumer_type); query_services(repo, mlt_service_filter_type); query_services(repo, mlt_service_link_type); query_services(repo, mlt_service_producer_type); query_services(repo, mlt_service_transition_type); fprintf(stdout, "# You can query the metadata for a specific service using:\n" "# -query =\n" "# where is one of: consumer, filter, producer, or transition.\n"); } return error; } else if (!strcmp(argv[i], "-silent")) { is_silent = 1; } else if (!strcmp(argv[i], "-quiet")) { is_silent = 1; mlt_log_set_level(MLT_LOG_QUIET); } else if (!strcmp(argv[i], "-verbose")) { mlt_log_set_level(MLT_LOG_VERBOSE); } else if (!strcmp(argv[i], "-timings")) { mlt_log_set_level(MLT_LOG_TIMINGS); } else if (!strncmp(argv[i], "-h", 2) || !strcmp(argv[i], "--help")) { show_usage(argv[0]); return error; } else if (!strcmp(argv[i], "-version") || !strcmp(argv[i], "--version")) { fprintf(stdout, "%s " VERSION "\n" "Copyright (C) 2002-2026 Meltytech, LLC\n" "\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", basename(argv[0])); return error; } else if (!strcmp(argv[i], "-debug")) { mlt_log_set_level(MLT_LOG_DEBUG); } else if (!strcmp(argv[i], "-loglevel") && argv[i + 1]) { switch (argv[++i][0]) { case 'q': mlt_log_set_level(MLT_LOG_QUIET); break; case 'p': mlt_log_set_level(MLT_LOG_PANIC); break; case 'f': mlt_log_set_level(MLT_LOG_FATAL); break; case 'e': mlt_log_set_level(MLT_LOG_ERROR); break; case 'w': mlt_log_set_level(MLT_LOG_WARNING); break; case 'i': mlt_log_set_level(MLT_LOG_INFO); break; case 'v': mlt_log_set_level(MLT_LOG_VERBOSE); break; case 'd': mlt_log_set_level(MLT_LOG_DEBUG); break; case 't': mlt_log_set_level(MLT_LOG_TIMINGS); break; default: break; } } else if (!strcmp(argv[i], "-getc")) { is_getc = 1; } else if (!repo && !strcmp(argv[i], "-repository")) { if (i + 1 < argc && argv[i + 1][0] != '-') repo_path = argv[++i]; } else if (!strcmp(argv[i], "-consumer")) { is_consumer_explicit = 1; } } if (!is_silent && !isatty(STDIN_FILENO) && !is_progress) is_progress = 1; // Construct the factory if (!repo) repo = setup_factory(repo_path, is_setlocale); // Create profile if not set explicitly if (getenv("MLT_PROFILE")) profile = mlt_profile_init(NULL); if (profile == NULL) profile = mlt_profile_init(NULL); else profile->is_explicit = 1; // Look for the consumer option to load profile settings from consumer properties backup_profile = mlt_profile_clone(profile); // Try to initialize QApplication on the main thread to prevent crash mlt_filter_close(mlt_factory_filter(profile, "qtcrop", NULL)); if (load_consumer(&consumer, profile, argc, argv) != EXIT_SUCCESS) return error; // If the consumer changed the profile, then it is explicit. if (backup_profile && !profile->is_explicit && (profile->width != backup_profile->width || profile->height != backup_profile->height || profile->sample_aspect_num != backup_profile->sample_aspect_num || profile->sample_aspect_den != backup_profile->sample_aspect_den || profile->frame_rate_den != backup_profile->frame_rate_den || profile->frame_rate_num != backup_profile->frame_rate_num || profile->colorspace != backup_profile->colorspace)) profile->is_explicit = 1; // Get melt producer if (argc > 1) melt = mlt_factory_producer(profile, "melt", &argv[1]); if (melt) { // Generate an automatic profile if needed. if ((consumer && backup_profile && !backup_profile->is_explicit) || (!consumer && !profile->is_explicit)) { mlt_producer first_producer = mlt_properties_get_data(MLT_PRODUCER_PROPERTIES(melt), "first_producer", NULL); mlt_profile_from_producer(profile, first_producer); mlt_consumer melt_consumer = MLT_CONSUMER( mlt_service_consumer(MLT_PRODUCER_SERVICE(melt))); if (melt_consumer) mlt_consumer_connect(melt_consumer, NULL); mlt_producer_close(melt); melt = mlt_factory_producer(profile, "melt", &argv[1]); } double scale = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "scale"); if (scale > 0.0) { mlt_profile_close(backup_profile); set_preview_scale(&profile, &backup_profile, scale); } // Reload the consumer with the fully qualified profile. // The producer or auto-profile could have changed the profile. load_consumer(&consumer, profile, argc, argv); // See if producer has consumer already attached if (!store && !consumer) { consumer = MLT_CONSUMER(mlt_service_consumer(MLT_PRODUCER_SERVICE(melt))); if (consumer) { mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES(consumer)); // because we explicitly close it mlt_properties_set_data(MLT_CONSUMER_PROPERTIES(consumer), "transport_callback", transport_action, 0, NULL, NULL); } } // If we have no consumer, default to sdl if (store == NULL && consumer == NULL) consumer = create_consumer(profile, NULL); } // Set transport properties on consumer and produder if (consumer != NULL && melt != NULL) { mlt_properties_set_data(MLT_CONSUMER_PROPERTIES(consumer), "transport_producer", melt, 0, NULL, NULL); mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(melt), "transport_consumer", consumer, 0, NULL, NULL); if (is_progress) mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "progress", is_progress); if (is_silent) mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "silent", is_silent); if (is_getc) mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "melt_getc", is_getc); } if (argc > 1 && melt != NULL && mlt_producer_get_length(melt) > 0) { // Parse the arguments for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-jack") && consumer) { setup_jack_transport(consumer, profile); } else if (!strcmp(argv[i], "-serialise")) { if (store != stdout) i++; } else { if (store != NULL) fprintf(store, "%s\n", argv[i]); i++; while (argv[i] != NULL && argv[i][0] != '-') { if (store != NULL) fprintf(store, "%s\n", argv[i]); i += 1; } i--; } } if (consumer != NULL && store == NULL) { // Get melt's properties mlt_properties melt_props = MLT_PRODUCER_PROPERTIES(melt); mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); if (is_consumer_explicit) { // Apply group settings mlt_properties group = mlt_properties_get_data(melt_props, "group", 0); mlt_properties_inherit(properties, group); } int in = mlt_properties_get_int(properties, "in"); int out = mlt_properties_get_int(properties, "out"); if (in > 0 || out > 0) { if (out == 0) { out = mlt_producer_get_length(melt) - 1; } mlt_producer_set_in_and_out(melt, in, out); mlt_producer_seek(melt, 0); } // Connect consumer to melt mlt_consumer_connect(consumer, MLT_PRODUCER_SERVICE(melt)); // Start the consumer mlt_events_listen(properties, consumer, "consumer-fatal-error", (mlt_listener) on_fatal_error); if (mlt_consumer_start(consumer) == 0) { // Try to exit gracefully upon these signals signal(SIGINT, stop_handler); signal(SIGTERM, stop_handler); #ifndef _WIN32 signal(SIGHUP, stop_handler); signal(SIGPIPE, stop_handler); #endif // Transport functionality transport(melt, consumer); // Stop the consumer mlt_consumer_stop(consumer); } } else if (store != NULL && store != stdout && name != NULL) { fprintf(stderr, "Project saved as %s.\n", name); fclose(store); } } else if (argc == 1) { show_usage(argv[0]); } else { error = EXIT_FAILURE; } // Disconnect producer from consumer to prevent ref cycles from closing services if (consumer && !error) { error = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "melt_error"); mlt_consumer_connect(consumer, NULL); } // Flush all open writable streams fflush(NULL); return error; } mlt-7.38.0/src/mlt++/000775 000000 000000 00000000000 15172202314 014073 5ustar00rootroot000000 000000 mlt-7.38.0/src/mlt++/CMakeLists.txt000664 000000 000000 00000005261 15172202314 016637 0ustar00rootroot000000 000000 set(MLTPP_PUBLIC_HEADERS Mlt.h MltAnimation.h MltAudio.h MltChain.h MltConfig.h MltConsumer.h MltDeque.h MltEvent.h MltFactory.h MltField.h MltFilter.h MltFilteredConsumer.h MltFilteredProducer.h MltFrame.h MltImage.h MltLink.h MltMultitrack.h MltParser.h MltPlaylist.h MltProducer.h MltProfile.h MltProperties.h MltPushConsumer.h MltRepository.h MltService.h MltTokeniser.h MltTractor.h MltTransition.h ) add_library(mlt++ SHARED MltAnimation.cpp MltAudio.cpp MltChain.cpp MltConsumer.cpp MltDeque.cpp MltEvent.cpp MltFactory.cpp MltField.cpp MltFilter.cpp MltFilteredConsumer.cpp MltFilteredProducer.cpp MltFrame.cpp MltImage.cpp MltLink.cpp MltMultitrack.cpp MltParser.cpp MltPlaylist.cpp MltProducer.cpp MltProfile.cpp MltProperties.cpp MltPushConsumer.cpp MltRepository.cpp MltService.cpp MltTokeniser.cpp MltTractor.cpp MltTransition.cpp ) add_custom_target("Other_mlt++_Files" SOURCES mlt++.pc.in mlt++.vers ) add_library(Mlt${MLT_VERSION_MAJOR}::mlt++ ALIAS mlt++) target_sources(mlt++ PRIVATE ${MLTPP_PUBLIC_HEADERS}) target_compile_options(mlt++ PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mlt++ PUBLIC mlt) if(MSVC) target_link_libraries(mlt++ PRIVATE PThreads4W::PThreads4W) endif() target_include_directories(mlt++ PUBLIC $ $ ) set_target_properties(mlt++ PROPERTIES VERSION ${MLT_VERSION} SOVERSION ${MLT_VERSION_MAJOR} OUTPUT_NAME mlt++-${MLT_VERSION_MAJOR} PUBLIC_HEADER "${MLTPP_PUBLIC_HEADERS}" ) if(WIN32) if(MINGW) target_link_options(mlt++ PRIVATE -Wl,--output-def,${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt++-${MLT_VERSION_MAJOR}.def) install(FILES "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt++-${MLT_VERSION_MAJOR}.def" DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() target_compile_definitions(mlt++ PRIVATE MLTPP_EXPORTS) endif() if(NOT (WIN32 OR APPLE)) target_link_options(mlt++ PRIVATE -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/mlt++.vers) set_target_properties(mlt++ PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/mlt++.vers) endif() install(TARGETS mlt++ EXPORT MltTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mlt-${MLT_VERSION_MAJOR}/mlt++ ) configure_file(mlt++.pc.in mlt++-${MLT_VERSION_MAJOR}.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mlt++-${MLT_VERSION_MAJOR}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT Development ) mlt-7.38.0/src/mlt++/Mlt.h000664 000000 000000 00000003355 15172202314 015006 0ustar00rootroot000000 000000 /** * Mlt.h - Convenience header file for all mlt++ objects * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** \defgroup mltpp MLT C++ Wrappers * \brief High-level C++ wrappers around the MLT framework C API. * * Include this header to pull in all mlt++ classes. The classes live in * the \c Mlt namespace and wrap the corresponding \c mlt_*_s C structs from * \c src/framework/. */ #ifndef MLTPP_H #define MLTPP_H #include "MltAnimation.h" #include "MltAudio.h" #include "MltChain.h" #include "MltConsumer.h" #include "MltDeque.h" #include "MltEvent.h" #include "MltFactory.h" #include "MltField.h" #include "MltFilter.h" #include "MltFilteredConsumer.h" #include "MltFrame.h" #include "MltImage.h" #include "MltMultitrack.h" #include "MltParser.h" #include "MltPlaylist.h" #include "MltProducer.h" #include "MltProfile.h" #include "MltProperties.h" #include "MltPushConsumer.h" #include "MltRepository.h" #include "MltService.h" #include "MltTokeniser.h" #include "MltTractor.h" #include "MltTransition.h" #endif mlt-7.38.0/src/mlt++/MltAnimation.cpp000664 000000 000000 00000014713 15172202314 017201 0ustar00rootroot000000 000000 /** * MltAnimation.cpp - MLT Wrapper * Copyright (C) 2015-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltAnimation.h" #include using namespace Mlt; Animation::Animation() : instance(0) {} Animation::Animation(mlt_animation animation) : instance(animation) {} Animation::Animation(const Animation &animation) : instance(animation.instance) {} Animation::~Animation() { // Do not call mlt_animation_close() because mlt_animation is not reference- // counted, and typically a mlt_properties owns it. instance = 0; } bool Animation::is_valid() const { return instance != 0; } mlt_animation Animation::get_animation() const { return instance; } Animation &Animation::operator=(const Animation &animation) { if (this != &animation) { instance = animation.instance; } return *this; } int Animation::length() { return mlt_animation_get_length(instance); } int Animation::get_item(int position, bool &is_key, mlt_keyframe_type &type) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_get_item(instance, &item, position); if (!error) { is_key = item.is_key; type = item.keyframe_type; } return error; } bool Animation::is_key(int position) { struct mlt_animation_item_s item; item.is_key = 0; item.property = NULL; mlt_animation_get_item(instance, &item, position); return item.is_key; } mlt_keyframe_type Animation::keyframe_type(int position) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_get_item(instance, &item, position); if (!error) return item.keyframe_type; else return (mlt_keyframe_type) -1; } /** Get the keyfame at the position or the next following. * * If no keyframe exists at or after the position, the return value is invalid * * \deprecated Prefer bool Animation::next_key( int position, int& key ) * \param position the frame number at which to start looking for the next keyframe * \return the position of the next keyframe */ int Animation::next_key(int position) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_next_key(instance, &item, position); if (!error) return item.frame; else return error; } /** Get the keyfame at the position or the next following. * * On error, key is not modified. * * \param position the frame number at which to start looking for the next keyframe * \param key the returned position of the next keyframe * \return true if there was an error */ bool Animation::next_key(int position, int &key) { struct mlt_animation_item_s item; item.property = NULL; bool error = mlt_animation_next_key(instance, &item, position); if (!error) { key = item.frame; } return error; } /** Get the keyfame at the position or the previous keyframe before. * * If no keyframe exists at or before the position, the return value is invalid * * \deprecated Prefer bool Animation::previous_key( int position, int& key ) * \param position the frame number at which to start looking for the previous keyframe * \return the position of the previous keyframe */ int Animation::previous_key(int position) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_prev_key(instance, &item, position); if (!error) return item.frame; else return error; } /** Get the keyfame at the position or the previous before. * * On error, key is not modified. * * \param position the frame number at which to start looking for the previous keyframe * \param key the returned position of the previous keyframe * \return true if there was an error */ bool Animation::previous_key(int position, int &key) { struct mlt_animation_item_s item; item.property = NULL; bool error = mlt_animation_prev_key(instance, &item, position); if (!error) { key = item.frame; } return error; } int Animation::key_count() { return mlt_animation_key_count(instance); } int Animation::key_get(int index, int &frame, mlt_keyframe_type &type) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_key_get(instance, &item, index); if (!error) { frame = item.frame; type = item.keyframe_type; } return error; } int Animation::key_get_frame(int index) { struct mlt_animation_item_s item; item.is_key = 0; item.property = NULL; int error = mlt_animation_key_get(instance, &item, index); if (!error) return item.frame; else return -1; } mlt_keyframe_type Animation::key_get_type(int index) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_key_get(instance, &item, index); if (!error) return item.keyframe_type; else return (mlt_keyframe_type) -1; } int Animation::key_set_type(int index, mlt_keyframe_type type) { return mlt_animation_key_set_type(instance, index, type); } int Animation::key_set_frame(int index, int frame) { return mlt_animation_key_set_frame(instance, index, frame); } void Animation::shift_frames(int shift) { return mlt_animation_shift_frames(instance, shift); } void Animation::set_length(int length) { return mlt_animation_set_length(instance, length); } int Animation::remove(int position) { return mlt_animation_remove(instance, position); } void Animation::interpolate() { mlt_animation_interpolate(instance); } char *Animation::serialize_cut(int in, int out) { return mlt_animation_serialize_cut(instance, in, out); } char *Animation::serialize_cut(mlt_time_format format, int in, int out) { return mlt_animation_serialize_cut_tf(instance, in, out, format); } mlt-7.38.0/src/mlt++/MltAnimation.h000664 000000 000000 00000007164 15172202314 016650 0ustar00rootroot000000 000000 /** * MLT C++ wrapper * Copyright (C) 2015-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_ANIMATION_H #define MLTPP_ANIMATION_H #include "MltConfig.h" #include namespace Mlt { /** \brief C++ wrapper for ::mlt_animation — keyframe animation data. * * An animation holds a set of keyframes that define how a property value * changes over time. Obtain one from Properties via the anim_get / anim_set * family of methods. * * \see mlt_animation_s */ class MLTPP_DECLSPEC Animation { private: mlt_animation instance; public: Animation(); /** Wrap an existing ::mlt_animation handle (does not take ownership). */ Animation(mlt_animation animation); Animation(const Animation &); ~Animation(); /** Return true if the underlying handle is non-null. */ bool is_valid() const; mlt_animation get_animation() const; Animation &operator=(const Animation &); /** Return the animation length in frames. */ int length(); /** Get the interpolated value and keyframe type at \p position. */ int get_item(int position, bool &is_key, mlt_keyframe_type &); /** Return true if \p position is exactly a keyframe. */ bool is_key(int position); /** Return the interpolation type of the keyframe at \p position. */ mlt_keyframe_type keyframe_type(int position); /** Return the next keyframe position at or after \p position. */ int next_key(int position); bool next_key(int position, int &key); /** Return the previous keyframe position at or before \p position. */ int previous_key(int position); bool previous_key(int position, int &key); /** Return the total number of keyframes. */ int key_count(); /** Get the frame and type of the keyframe at \p index. */ int key_get(int index, int &frame, mlt_keyframe_type &); /** Return the frame number of the keyframe at \p index. */ int key_get_frame(int index); /** Return the interpolation type of the keyframe at \p index. */ mlt_keyframe_type key_get_type(int index); /** Set the interpolation type of the keyframe at \p index. */ int key_set_type(int index, mlt_keyframe_type type); /** Move the keyframe at \p index to \p frame. */ int key_set_frame(int index, int frame); /** Shift all keyframe positions by \p shift frames. */ void shift_frames(int shift); /** Set a new total length, scaling keyframe positions proportionally. */ void set_length(int length); /** Remove the keyframe at \p position. */ int remove(int position); /** Recalculate intermediate values between keyframes. */ void interpolate(); /** Serialize the animation as a string, optionally clipped to [\p in, \p out]. * Caller must \c free() the returned string. */ char *serialize_cut(int in = -1, int out = -1); /** \overload */ char *serialize_cut(mlt_time_format format, int in = -1, int out = -1); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltAudio.cpp000664 000000 000000 00000003502 15172202314 016315 0ustar00rootroot000000 000000 /** * MltAudio.cpp - MLT Wrapper * Copyright (C) 2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltAudio.h" using namespace Mlt; Audio::Audio() { instance = mlt_audio_new(); } Audio::Audio(mlt_audio audio) : instance(audio) {} Audio::~Audio() { mlt_audio_close(instance); } void *Audio::data() { return instance->data; } void Audio::set_data(void *data) { instance->data = data; } int Audio::frequency() { return instance->frequency; } void Audio::set_frequency(int frequency) { instance->frequency = frequency; } mlt_audio_format Audio::format() { return instance->format; } void Audio::set_format(mlt_audio_format format) { instance->format = format; } int Audio::samples() { return instance->samples; } void Audio::set_samples(int samples) { instance->samples = samples; } int Audio::channels() { return instance->channels; } void Audio::set_channels(int channels) { instance->channels = channels; } mlt_channel_layout Audio::layout() { return instance->layout; } void Audio::set_layout(mlt_channel_layout layout) { instance->layout = layout; } mlt-7.38.0/src/mlt++/MltAudio.h000664 000000 000000 00000003371 15172202314 015766 0ustar00rootroot000000 000000 /** * MltAudio.h - MLT Wrapper * Copyright (C) 2020-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_AUDIO_H #define MLTPP_AUDIO_H #include "MltConfig.h" #include namespace Mlt { /** \brief C++ wrapper for ::mlt_audio — a block of decoded audio samples. * * Holds a raw audio buffer together with its format metadata (frequency, * channels, layout, sample count). Obtain one from Frame::get_audio() or * construct one manually for push-based workflows. * * \see mlt_audio_s */ class MLTPP_DECLSPEC Audio { private: mlt_audio instance; public: Audio(); Audio(mlt_audio audio); virtual ~Audio(); void *data(); void set_data(void *data); int frequency(); void set_frequency(int frequency); mlt_audio_format format(); void set_format(mlt_audio_format format); int samples(); void set_samples(int samples); int channels(); void set_channels(int channels); mlt_channel_layout layout(); void set_layout(mlt_channel_layout layout); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltChain.cpp000664 000000 000000 00000005763 15172202314 016311 0ustar00rootroot000000 000000 /** * MltChain.cpp - Chain wrapper * Copyright (C) 2020-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltChain.h" using namespace Mlt; Chain::Chain() : instance(nullptr) {} Chain::Chain(Profile &profile, const char *id, const char *service) : instance(nullptr) { if (!id || !service) { service = id ? id : service; id = nullptr; } mlt_producer source = mlt_factory_producer(profile.get_profile(), id, service); if (source) { instance = mlt_chain_init(profile.get_profile()); mlt_chain_set_source(instance, source); if (id == NULL) mlt_chain_attach_normalizers(instance); mlt_producer_close(source); } } Chain::Chain(Profile &profile) : instance(mlt_chain_init(profile.get_profile())) {} Chain::Chain(mlt_chain chain) : instance(chain) { inc_ref(); } Chain::Chain(Chain &chain) : Mlt::Producer(chain) , instance(chain.get_chain()) { inc_ref(); } Chain::Chain(Chain *chain) : Mlt::Producer(chain) , instance(chain != NULL ? chain->get_chain() : NULL) { if (is_valid()) inc_ref(); } Chain::Chain(Service &chain) : instance(NULL) { if (chain.type() == mlt_service_chain_type) { instance = (mlt_chain) chain.get_service(); inc_ref(); } } Chain::~Chain() { mlt_chain_close(instance); instance = nullptr; } mlt_chain Chain::get_chain() { return instance; } mlt_producer Chain::get_producer() { return MLT_CHAIN_PRODUCER(instance); } void Chain::set_source(Mlt::Producer &source) { mlt_chain_set_source(instance, source.get_producer()); } Mlt::Producer Chain::get_source() { return Mlt::Producer(mlt_chain_get_source(instance)); } int Chain::attach(Mlt::Link &link) { return mlt_chain_attach(instance, link.get_link()); } int Chain::detach(Mlt::Link &link) { return mlt_chain_detach(instance, link.get_link()); } int Chain::link_count() const { return mlt_chain_link_count(instance); } bool Chain::move_link(int from, int to) { return (bool) mlt_chain_move_link(instance, from, to); } Mlt::Link *Chain::link(int index) { mlt_link result = mlt_chain_link(instance, index); return result == NULL ? NULL : new Link(result); } void Chain::attach_normalizers() { mlt_chain_attach_normalizers(instance); } mlt-7.38.0/src/mlt++/MltChain.h000664 000000 000000 00000005067 15172202314 015753 0ustar00rootroot000000 000000 /** * MltChain.h - Chain wrapper * Copyright (C) 2020-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_CHAIN_H #define MLTPP_CHAIN_H #include "MltConfig.h" #include #include "MltLink.h" #include "MltProducer.h" #include "MltProfile.h" namespace Mlt { /** \brief C++ wrapper for ::mlt_chain — a linear processing pipeline. * * A chain is a producer that wraps another producer (the source) and applies * an ordered sequence of Link objects to it. Links are similar to filters * but designed for the chain architecture. * * \extends Producer * \see mlt_chain_s */ class MLTPP_DECLSPEC Chain : public Producer { private: mlt_chain instance; public: Chain(); /** Construct and instantiate chain \p id from the repository. */ Chain(Profile &profile, const char *id, const char *service = NULL); Chain(Mlt::Profile &profile); /** Wrap an existing ::mlt_chain handle. */ Chain(mlt_chain chain); Chain(Chain &chain); Chain(Chain *chain); Chain(Service &chain); virtual ~Chain(); virtual mlt_chain get_chain(); mlt_producer get_producer() override; /** Set the source producer that this chain wraps. */ void set_source(Mlt::Producer &source); /** Return the wrapped source producer. */ Mlt::Producer get_source(); /** Append \p link to the end of this chain. */ int attach(Mlt::Link &link); /** Remove \p link from this chain. */ int detach(Mlt::Link &link); /** Return the number of links in this chain. */ int link_count() const; /** Move the link at position \p from to position \p to. */ bool move_link(int from, int to); /** Return the link at \p index. Caller does not own the result. */ Mlt::Link *link(int index); /** Attach any normalizer links required by the source. */ void attach_normalizers(); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltConfig.h000664 000000 000000 00000002242 15172202314 016126 0ustar00rootroot000000 000000 /** * MltConfig.h - Convenience header file for all mlt++ objects * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_CONFIG_H #define MLTPP_CONFIG_H #if defined(_WIN32) #ifdef MLTPP_EXPORTS #define MLTPP_DECLSPEC __declspec(dllexport) #else #define MLTPP_DECLSPEC __declspec(dllimport) #endif #else #if __GNUC__ >= 4 #define MLTPP_DECLSPEC __attribute__((visibility("default"))) #else #define MLTPP_DECLSPEC #endif #endif #endif mlt-7.38.0/src/mlt++/MltConsumer.cpp000664 000000 000000 00000006113 15172202314 017050 0ustar00rootroot000000 000000 /** * MltConsumer.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltConsumer.h" #include "MltEvent.h" #include "MltProfile.h" #include #include using namespace Mlt; Consumer::Consumer() : instance(NULL) { instance = mlt_factory_consumer(NULL, NULL, NULL); } Consumer::Consumer(Profile &profile) : instance(NULL) { instance = mlt_factory_consumer(profile.get_profile(), NULL, NULL); } Consumer::Consumer(Profile &profile, const char *id, const char *arg) : Consumer(profile.get_profile(), id, arg) {} Consumer::Consumer(mlt_profile profile, const char *id, const char *arg) : instance(NULL) { if (id == NULL || arg != NULL) { instance = mlt_factory_consumer(profile, id, arg); } else { if (strchr(id, ':')) { char *temp = strdup(id); char *arg = strchr(temp, ':') + 1; *(arg - 1) = '\0'; instance = mlt_factory_consumer(profile, temp, arg); free(temp); } else { instance = mlt_factory_consumer(profile, id, NULL); } } } Consumer::Consumer(Service &consumer) : instance(NULL) { if (consumer.type() == mlt_service_consumer_type) { instance = (mlt_consumer) consumer.get_service(); inc_ref(); } } Consumer::Consumer(Consumer &consumer) : Mlt::Service(consumer) , instance(consumer.get_consumer()) { inc_ref(); } Consumer::Consumer(mlt_consumer consumer) : instance(consumer) { inc_ref(); } Consumer::~Consumer() { mlt_consumer_close(instance); } mlt_consumer Consumer::get_consumer() { return instance; } mlt_service Consumer::get_service() { return mlt_consumer_service(get_consumer()); } int Consumer::connect(Service &service) { return connect_producer(service); } int Consumer::start() { return mlt_consumer_start(get_consumer()); } void Consumer::purge() { mlt_consumer_purge(get_consumer()); } int Consumer::stop() { return mlt_consumer_stop(get_consumer()); } bool Consumer::is_stopped() { return mlt_consumer_is_stopped(get_consumer()) != 0; } int Consumer::run() { int ret = start(); if (!is_stopped()) { Event *e = setup_wait_for("consumer-stopped"); wait_for(e); delete e; } return ret; } int Consumer::position() { return mlt_consumer_position(get_consumer()); } mlt-7.38.0/src/mlt++/MltConsumer.h000664 000000 000000 00000005013 15172202314 016513 0ustar00rootroot000000 000000 /** * MltConsumer.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_CONSUMER_H #define MLTPP_CONSUMER_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Profile; /** \brief C++ wrapper for ::mlt_consumer — pulls and outputs audio/video. * * A consumer pulls frames from the service network and renders them to a * device, file, or socket. It drives the rendering pipeline. Connect a * producer (or tractor/playlist) with connect(), then call start(). * * \extends Service * \see mlt_consumer_s */ class MLTPP_DECLSPEC Consumer : public Service { private: mlt_consumer instance; public: Consumer(); Consumer(Profile &profile); /** Construct and instantiate consumer \p id from the repository. */ Consumer(Profile &profile, const char *id, const char *service = NULL); Consumer(mlt_profile profile, const char *id, const char *service = NULL); Consumer(Service &consumer); Consumer(Consumer &consumer); /** Wrap an existing ::mlt_consumer handle. */ Consumer(mlt_consumer consumer); virtual ~Consumer(); virtual mlt_consumer get_consumer(); mlt_service get_service() override; /** Connect \p service as the source for this consumer. */ virtual int connect(Service &service); /** Start rendering synchronously (returns when stopped). */ int run(); /** Start rendering asynchronously in a background thread. */ int start(); /** Flush buffered frames. */ void purge(); /** Stop the rendering thread and wait for it to finish. */ int stop(); /** Return true if the consumer is not currently rendering. */ bool is_stopped(); /** Return the current output frame position. */ int position(); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltDeque.cpp000664 000000 000000 00000003011 15172202314 016312 0ustar00rootroot000000 000000 /** * MltDeque.cpp - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltDeque.h" using namespace Mlt; Deque::Deque() { deque = mlt_deque_init(); } Deque::~Deque() { mlt_deque_close(deque); } int Deque::count() { return mlt_deque_count(deque); } int Deque::push_back(void *item) { return mlt_deque_push_back(deque, item); } void *Deque::pop_back() { return mlt_deque_pop_back(deque); } int Deque::push_front(void *item) { return mlt_deque_push_front(deque, item); } void *Deque::pop_front() { return mlt_deque_pop_front(deque); } void *Deque::peek_back() { return mlt_deque_peek_back(deque); } void *Deque::peek_front() { return mlt_deque_peek_front(deque); } void *Deque::peek(int index) { return mlt_deque_peek(deque, index); } mlt-7.38.0/src/mlt++/MltDeque.h000664 000000 000000 00000003023 15172202314 015762 0ustar00rootroot000000 000000 /** * MltDeque.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_DEQUE_H #define MLTPP_DEQUE_H #include "MltConfig.h" #include namespace Mlt { /** \brief C++ wrapper for ::mlt_deque — a double-ended queue of void pointers. * * A lightweight LIFO/FIFO container used internally by the MLT framework * for frame queuing. Items are untyped void pointers; the caller is * responsible for lifetime management. * * \see mlt_deque_s */ class MLTPP_DECLSPEC Deque { private: mlt_deque deque; public: Deque(); ~Deque(); int count(); int push_back(void *item); void *pop_back(); int push_front(void *item); void *pop_front(); void *peek_back(); void *peek_front(); void *peek(int index); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltEvent.cpp000664 000000 000000 00000004134 15172202314 016337 0ustar00rootroot000000 000000 /** * MltEvent.cpp - MLT Wrapper * Copyright (C) 2004-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltEvent.h" #include "MltFrame.h" using namespace Mlt; Event::Event(mlt_event event) : instance(event) { mlt_event_inc_ref(instance); } Event::Event(Event &event) : instance(event.get_event()) { mlt_event_inc_ref(instance); } Event::~Event() { mlt_event_close(instance); } mlt_event Event::get_event() { return instance; } bool Event::is_valid() { return instance != NULL; } void Event::block() { mlt_event_block(get_event()); } void Event::unblock() { mlt_event_unblock(get_event()); } EventData::EventData(mlt_event_data data) : instance(data) {} EventData::EventData(EventData &data) : instance(data.get_event_data()) {} EventData::EventData(const EventData &data) : instance(data.get_event_data()) {} EventData &EventData::operator=(const EventData &data) { instance = data.get_event_data(); return *this; } mlt_event_data EventData::get_event_data() const { return instance; } int EventData::to_int() const { return mlt_event_data_to_int(instance); } const char *EventData::to_string() const { return mlt_event_data_to_string(instance); } Frame EventData::to_frame() const { return Frame(mlt_event_data_to_frame(instance)); } void *EventData::to_object() const { return mlt_event_data_to_object(instance); } mlt-7.38.0/src/mlt++/MltEvent.h000664 000000 000000 00000004374 15172202314 016012 0ustar00rootroot000000 000000 /** * MltEvent.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_EVENT_H #define MLTPP_EVENT_H #include "MltConfig.h" #include namespace Mlt { class Frame; /** \brief C++ wrapper for ::mlt_event — a registered event listener handle. * * Returned by Properties::listen(). Call block()/unblock() to suppress or * resume event delivery. The owning Properties object manages the lifecycle; * delete_event() or Properties::wait_for() can be used to clean up. * * \see mlt_event_s */ class MLTPP_DECLSPEC Event { private: mlt_event instance; public: Event(mlt_event); Event(Event &); ~Event(); mlt_event get_event(); /** Return true if the underlying handle is non-null. */ bool is_valid(); /** Suppress delivery of this event. */ void block(); /** Resume delivery of this event. */ void unblock(); }; /** \brief C++ wrapper for ::mlt_event_data — typed payload for an MLT event. * * Passed to event listener callbacks. Use the to_*() accessors to extract * the payload in the appropriate type. * * \see mlt_event_data */ class MLTPP_DECLSPEC EventData { private: mlt_event_data instance; public: EventData(mlt_event_data); EventData(EventData &); EventData(const EventData &); EventData &operator=(const EventData &); ~EventData(){}; mlt_event_data get_event_data() const; int to_int() const; const char *to_string() const; Frame to_frame() const; void *to_object() const; }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltFactory.cpp000664 000000 000000 00000003265 15172202314 016671 0ustar00rootroot000000 000000 /** * MltFactory.cpp - MLT Wrapper * Copyright (C) 2004-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFactory.h" #include "MltConsumer.h" #include "MltFilter.h" #include "MltProducer.h" #include "MltRepository.h" #include "MltTransition.h" using namespace Mlt; Repository *Factory::init(const char *directory) { return new Repository(mlt_factory_init(directory)); } Properties *Factory::event_object() { return new Properties(mlt_factory_event_object()); } Producer *Factory::producer(Profile &profile, char *id, char *arg) { return new Producer(profile, id, arg); } Filter *Factory::filter(Profile &profile, char *id, char *arg) { return new Filter(profile, id, arg); } Transition *Factory::transition(Profile &profile, char *id, char *arg) { return new Transition(profile, id, arg); } Consumer *Factory::consumer(Profile &profile, char *id, char *arg) { return new Consumer(profile, id, arg); } void Factory::close() { mlt_factory_close(); } mlt-7.38.0/src/mlt++/MltFactory.h000664 000000 000000 00000004441 15172202314 016333 0ustar00rootroot000000 000000 /** * MltFactory.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FACTORY_H #define MLTPP_FACTORY_H #include "MltConfig.h" #ifdef SWIG #define MLTPP_DECLSPEC #endif #include namespace Mlt { class Properties; class Producer; class Filter; class Transition; class Consumer; class Profile; class Repository; /** \brief Static factory for creating MLT services. * * Call Factory::init() once at application start to load all plugins from * the MLT_REPOSITORY directory. Use the static factory methods to create * producers, filters, transitions, and consumers by service identifier. * * \see mlt_factory_s */ class MLTPP_DECLSPEC Factory { public: /** Initialise the factory from \p directory. Caller owns the returned Repository. */ static Repository *init(const char *directory = NULL); /** Return the global event object. Caller owns the result. */ static Properties *event_object(); /** Instantiate a producer by \p id. Caller owns the result. */ static Producer *producer(Profile &profile, char *id, char *arg = NULL); /** Instantiate a filter by \p id. Caller owns the result. */ static Filter *filter(Profile &profile, char *id, char *arg = NULL); /** Instantiate a transition by \p id. Caller owns the result. */ static Transition *transition(Profile &profile, char *id, char *arg = NULL); /** Instantiate a consumer by \p id. Caller owns the result. */ static Consumer *consumer(Profile &profile, char *id, char *arg = NULL); static void close(); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltField.cpp000664 000000 000000 00000003275 15172202314 016306 0ustar00rootroot000000 000000 /** * MltField.cpp - Field wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltField.h" #include "MltFilter.h" #include "MltTransition.h" using namespace Mlt; Field::Field(mlt_field field) : instance(field) { inc_ref(); } Field::Field(Field &field) : Mlt::Service(field) , instance(field.get_field()) { inc_ref(); } Field::~Field() { mlt_field_close(instance); } mlt_field Field::get_field() { return instance; } mlt_service Field::get_service() { return mlt_field_service(get_field()); } int Field::plant_filter(Filter &filter, int track) { return mlt_field_plant_filter(get_field(), filter.get_filter(), track); } int Field::plant_transition(Transition &transition, int a_track, int b_track) { return mlt_field_plant_transition(get_field(), transition.get_transition(), a_track, b_track); } void Field::disconnect_service(Service &service) { mlt_field_disconnect_service(get_field(), service.get_service()); } mlt-7.38.0/src/mlt++/MltField.h000664 000000 000000 00000003640 15172202314 015747 0ustar00rootroot000000 000000 /** * MltField.h - Field wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FIELD_H #define MLTPP_FIELD_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Filter; class Transition; /** \brief C++ wrapper for ::mlt_field — manages transitions and filters for a Tractor. * * A field maintains the ordered list of transitions and filters that are * applied across the tracks of a Tractor. Obtain it via Tractor::field(). * * \extends Service * \see mlt_field_s */ class MLTPP_DECLSPEC Field : public Service { private: mlt_field instance; public: /** Wrap an existing ::mlt_field handle. */ Field(mlt_field field); Field(Field &field); virtual ~Field(); mlt_field get_field(); mlt_service get_service() override; /** Attach \p filter to \p track. */ int plant_filter(Filter &filter, int track = 0); /** Attach \p transition between tracks \p a_track and \p b_track. */ int plant_transition(Transition &transition, int a_track = 0, int b_track = 1); /** Remove \p service from the field. */ void disconnect_service(Service &service); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltFilter.cpp000664 000000 000000 00000006721 15172202314 016507 0ustar00rootroot000000 000000 /** * MltFilter.cpp - MLT Wrapper * Copyright (C) 2004-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFilter.h" #include "MltProfile.h" #include #include using namespace Mlt; Filter::Filter() : Service() , instance(nullptr) {} Filter::Filter(Profile &profile, const char *id, const char *arg) : Filter(profile.get_profile(), id, arg) {} Filter::Filter(mlt_profile profile, const char *id, const char *arg) : instance(NULL) { if (arg != NULL) { instance = mlt_factory_filter(profile, id, arg); } else { if (strchr(id, ':')) { char *temp = strdup(id); char *arg = strchr(temp, ':') + 1; *(arg - 1) = '\0'; instance = mlt_factory_filter(profile, temp, arg); free(temp); } else { instance = mlt_factory_filter(profile, id, NULL); } } } Filter::Filter(Service &filter) : instance(NULL) { if (filter.type() == mlt_service_filter_type) { instance = (mlt_filter) filter.get_service(); inc_ref(); } } Filter::Filter(Filter &filter) : Mlt::Service(filter) , instance(filter.get_filter()) { inc_ref(); } Filter::Filter(const Filter &filter) : Filter(const_cast(filter)) {} Filter::Filter(mlt_filter filter) : instance(filter) { inc_ref(); } Filter::Filter(Filter *filter) : Filter(filter ? filter->get_filter() : nullptr) {} Filter::~Filter() { mlt_filter_close(instance); } Filter &Filter::operator=(const Filter &filter) { if (this != &filter) { mlt_filter_close(instance); instance = filter.instance; inc_ref(); } return *this; } mlt_filter Filter::get_filter() { return instance; } mlt_service Filter::get_service() { return mlt_filter_service(get_filter()); } int Filter::connect(Service &service, int index) { return mlt_filter_connect(get_filter(), service.get_service(), index); } void Filter::set_in_and_out(int in, int out) { mlt_filter_set_in_and_out(get_filter(), in, out); } int Filter::get_in() { return mlt_filter_get_in(get_filter()); } int Filter::get_out() { return mlt_filter_get_out(get_filter()); } int Filter::get_length() { return mlt_filter_get_length(get_filter()); } int Filter::get_length2(Frame &frame) { return mlt_filter_get_length2(get_filter(), frame.get_frame()); } int Filter::get_track() { return mlt_filter_get_track(get_filter()); } int Filter::get_position(Frame &frame) { return mlt_filter_get_position(get_filter(), frame.get_frame()); } double Filter::get_progress(Frame &frame) { return mlt_filter_get_progress(get_filter(), frame.get_frame()); } void Filter::process(Frame &frame) { mlt_filter_process(get_filter(), frame.get_frame()); } mlt-7.38.0/src/mlt++/MltFilter.h000664 000000 000000 00000005730 15172202314 016153 0ustar00rootroot000000 000000 /** * MltFilter.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FILTER_H #define MLTPP_FILTER_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Profile; class Frame; /** \brief C++ wrapper for ::mlt_filter — modifies a single producer's output. * * A filter is attached to a service (typically a producer or another filter) * and can inspect or modify each frame as it passes through the service * network. Set the \em disable property to 1 to bypass the filter. * * \extends Service * \see mlt_filter_s */ class MLTPP_DECLSPEC Filter : public Service { private: mlt_filter instance; public: Filter(); /** Construct and instantiate filter \p id from the repository. */ Filter(Profile &profile, const char *id, const char *service = NULL); Filter(mlt_profile profile, const char *id, const char *service = NULL); Filter(Service &filter); Filter(Filter &filter); Filter(const Filter &filter); /** Wrap an existing ::mlt_filter handle. */ Filter(mlt_filter filter); Filter(Filter *filter); virtual ~Filter(); Filter &operator=(const Filter &filter); virtual mlt_filter get_filter(); mlt_service get_service() override; /** Connect this filter to \p service at \p index. */ int connect(Service &service, int index = 0); /** Restrict filter activity to the frame range [\p in, \p out]. */ void set_in_and_out(int in, int out); /** Return the in-point frame number. */ int get_in(); /** Return the out-point frame number. */ int get_out(); /** Return the duration (out - in + 1). */ int get_length(); /** Return the duration relative to \p frame's producer length. */ int get_length2(Frame &frame); /** Return the multitrack track index on which this filter is applied. */ int get_track(); /** Return the current position of \p frame relative to the filter's in-point. */ int get_position(Frame &frame); /** Return the playback progress of \p frame in the range [0.0, 1.0]. */ double get_progress(Frame &frame); /** Run the filter's process function on \p frame (used by subclasses). */ void process(Frame &frame); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltFilteredConsumer.cpp000664 000000 000000 00000006012 15172202314 020525 0ustar00rootroot000000 000000 /** * MltFilteredConsumer.cpp - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFilteredConsumer.h" using namespace Mlt; FilteredConsumer::FilteredConsumer(Profile &profile, const char *id, const char *arg) : Consumer(profile, id, arg) { // Create a reference to the first service first = new Service(*this); } FilteredConsumer::FilteredConsumer(Consumer &consumer) : Consumer(consumer) { // Create a reference to the first service first = new Service(*this); } FilteredConsumer::~FilteredConsumer() { // Delete the reference to the first service delete first; } int FilteredConsumer::connect(Service &service) { // All producers must connect to the first service, hence the use of the virtual here return first->connect_producer(service); } int FilteredConsumer::attach(Filter &filter) { int error = 0; if (filter.is_valid()) { Service *producer = first->producer(); error = filter.connect(*producer); if (error == 0) { first->connect_producer(filter); delete first; first = new Service(filter); } delete producer; } else { error = 1; } return error; } int FilteredConsumer::last(Filter &filter) { int error = 0; if (filter.is_valid()) { Service *producer = this->producer(); error = filter.connect(*producer); if (error == 0) connect_producer(filter); delete producer; } else { error = 1; } return error; } int FilteredConsumer::detach(Filter &filter) { if (filter.is_valid()) { Service *it = new Service(*first); while (it->is_valid() && it->get_service() != filter.get_service()) { Service *consumer = it->consumer(); delete it; it = consumer; } if (it->get_service() == filter.get_service()) { Service *producer = it->producer(); Service *consumer = it->consumer(); consumer->connect_producer(*producer); Service dummy; it->connect_producer(dummy); if (first->get_service() == it->get_service()) { delete first; first = new Service(*consumer); } } delete it; } return 0; } mlt-7.38.0/src/mlt++/MltFilteredConsumer.h000664 000000 000000 00000003435 15172202314 020200 0ustar00rootroot000000 000000 /** * MltFilteredConsumer.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FILTERED_CONSUMER_H #define MLTPP_FILTERED_CONSUMER_H #include "MltConfig.h" #include "MltConsumer.h" #include "MltFilter.h" #include "MltService.h" namespace Mlt { class Consumer; class Service; class Filter; class Profile; /** \brief A Consumer with an internal filter chain between the source and output. * * FilteredConsumer inserts a private service chain between the connected * producer and the underlying consumer, allowing filters to be attached * (via attach()) without modifying the source service network. * * \extends Consumer */ class MLTPP_DECLSPEC FilteredConsumer : public Consumer { private: Service *first; public: FilteredConsumer(Profile &profile, const char *id, const char *arg = NULL); FilteredConsumer(Consumer &consumer); virtual ~FilteredConsumer(); int connect(Service &service) override; int attach(Filter &filter); int last(Filter &filter); int detach(Filter &filter); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltFilteredProducer.cpp000664 000000 000000 00000004651 15172202314 020524 0ustar00rootroot000000 000000 /** * MltFilteredProducer.cpp - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFilteredProducer.h" #include "MltProfile.h" using namespace Mlt; FilteredProducer::FilteredProducer(Profile &profile, const char *id, const char *arg) : Producer(profile, id, arg) { // Create a reference to the last service last = new Service(*this); } FilteredProducer::~FilteredProducer() { // Delete the reference to the last service delete last; } int FilteredProducer::attach(Filter &filter) { int error = 0; if (filter.is_valid()) { Service *consumer = last->consumer(); filter.connect_producer(*last); if (consumer->is_valid()) consumer->connect_producer(filter); delete consumer; delete last; last = new Service(filter); } else { error = 1; } return error; } int FilteredProducer::detach(Filter &filter) { if (filter.is_valid()) { Service *it = new Service(*last); while (it->is_valid() && it->get_service() != filter.get_service()) { Service *producer = it->producer(); delete it; it = producer; } if (it->get_service() == filter.get_service()) { Service *producer = it->producer(); Service *consumer = it->consumer(); if (consumer->is_valid()) consumer->connect_producer(*producer); Producer dummy(get_profile(), "colour"); dummy.connect_producer(*it); if (last->get_service() == it->get_service()) { delete last; last = new Service(*producer); } } delete it; } return 0; } mlt-7.38.0/src/mlt++/MltFilteredProducer.h000664 000000 000000 00000003207 15172202314 020165 0ustar00rootroot000000 000000 /** * MltFilteredProducer.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FILTERED_PRODUCER_H #define MLTPP_FILTERED_PRODUCER_H #include "MltConfig.h" #include "MltFilter.h" #include "MltProducer.h" #include "MltService.h" namespace Mlt { class Producer; class Service; class Filter; class Profile; /** \brief A Producer with an internal filter chain applied to its output. * * FilteredProducer wraps another producer and applies an ordered chain of * attached filters to each frame before delivery. Filters are added via * attach() and removed via detach(). * * \extends Producer */ class MLTPP_DECLSPEC FilteredProducer : public Producer { private: Service *last; public: FilteredProducer(Profile &profile, const char *id, const char *arg = NULL); virtual ~FilteredProducer(); int attach(Filter &filter); int detach(Filter &filter); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltFrame.cpp000664 000000 000000 00000006510 15172202314 016310 0ustar00rootroot000000 000000 /** * MltFrame.cpp - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFrame.h" #include "MltProducer.h" using namespace Mlt; Frame::Frame() : Mlt::Properties((mlt_properties) NULL) , instance(NULL) {} Frame::Frame(mlt_frame frame) : Mlt::Properties((mlt_properties) NULL) , instance(frame) { inc_ref(); } Frame::Frame(Frame &frame) : Mlt::Properties((mlt_properties) NULL) , instance(frame.instance) { inc_ref(); } Frame::Frame(const Frame &frame) : Mlt::Properties((mlt_properties) NULL) , instance(frame.instance) { inc_ref(); } Frame::~Frame() { mlt_frame_close(instance); } Frame &Frame::operator=(const Frame &frame) { if (this != &frame) { mlt_frame_close(instance); instance = frame.instance; inc_ref(); } return *this; } mlt_frame Frame::get_frame() { return instance; } mlt_properties Frame::get_properties() { return mlt_frame_properties(get_frame()); } uint8_t *Frame::get_image(mlt_image_format &format, int &w, int &h, int writable) { uint8_t *image = NULL; if (get_double("consumer_aspect_ratio") == 0.0) set("consumer_aspect_ratio", 1.0); mlt_frame_get_image(get_frame(), &image, &format, &w, &h, writable); set("format", format); set("writable", writable); return image; } unsigned char *Frame::fetch_image(mlt_image_format f, int w, int h, int writable) { uint8_t *image = NULL; if (get_double("consumer_aspect_ratio") == 0.0) set("consumer_aspect_ratio", 1.0); mlt_frame_get_image(get_frame(), &image, &f, &w, &h, writable); set("format", f); set("writable", writable); return image; } void *Frame::get_audio(mlt_audio_format &format, int &frequency, int &channels, int &samples) { void *audio = NULL; mlt_frame_get_audio(get_frame(), &audio, &format, &frequency, &channels, &samples); return audio; } unsigned char *Frame::get_waveform(int w, int h) { return mlt_frame_get_waveform(get_frame(), w, h); } Producer *Frame::get_original_producer() { return new Producer(mlt_frame_get_original_producer(get_frame())); } mlt_properties Frame::get_unique_properties(Service &service) { return mlt_frame_unique_properties(get_frame(), service.get_service()); } int Frame::get_position() { return mlt_frame_get_position(get_frame()); } int Frame::set_image(uint8_t *image, int size, mlt_destructor destroy) { return mlt_frame_set_image(get_frame(), image, size, destroy); } int Frame::set_alpha(uint8_t *alpha, int size, mlt_destructor destroy) { return mlt_frame_set_alpha(get_frame(), alpha, size, destroy); } mlt-7.38.0/src/mlt++/MltFrame.h000664 000000 000000 00000005472 15172202314 015763 0ustar00rootroot000000 000000 /** * MltFilter.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FRAME_H #define MLTPP_FRAME_H #include "MltConfig.h" #include "MltProperties.h" #include namespace Mlt { class Properties; class Producer; class Service; /** \brief C++ wrapper for ::mlt_frame — a single unit of audio/video data. * * A frame carries all data for one rendered moment: pixel buffer, audio * samples, and per-frame properties. Frames are obtained from a service via * Service::get_frame() and are owned by the caller. * * \extends Properties * \see mlt_frame_s */ class MLTPP_DECLSPEC Frame : public Properties { private: mlt_frame instance; public: Frame(); /** Wrap an existing ::mlt_frame handle. */ Frame(mlt_frame frame); Frame(Frame &frame); Frame(const Frame &frame); virtual ~Frame(); Frame &operator=(const Frame &frame); virtual mlt_frame get_frame(); mlt_properties get_properties() override; /** Decode and return the pixel buffer in the requested \p format. * On return \p w and \p h contain the actual dimensions. */ uint8_t *get_image(mlt_image_format &format, int &w, int &h, int writable = 0); /** Scale the image to exactly \p w x \p h before returning. */ unsigned char *fetch_image(mlt_image_format format, int w, int h, int writable = 0); /** Decode and return raw audio samples. */ void *get_audio(mlt_audio_format &format, int &frequency, int &channels, int &samples); /** Render a waveform image of size \p w x \p h. Caller owns result. */ unsigned char *get_waveform(int w, int h); /** Return the producer that originally created this frame. Caller owns the result. */ Producer *get_original_producer(); /** Return the absolute frame position. */ int get_position(); /** Return the per-frame property bag for \p service (thread-safe). */ mlt_properties get_unique_properties(Service &service); int set_image(uint8_t *image, int size, mlt_destructor destroy); int set_alpha(uint8_t *alpha, int size, mlt_destructor destroy); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltImage.cpp000664 000000 000000 00000003644 15172202314 016305 0ustar00rootroot000000 000000 /** * MltImage.cpp - MLT Wrapper * Copyright (C) 2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltImage.h" using namespace Mlt; Image::Image() { instance = mlt_image_new(); } Image::Image(mlt_image image) : instance(image) {} Image::Image(int width, int height, mlt_image_format format) { instance = mlt_image_new(); alloc(width, height, format); } Image::~Image() { mlt_image_close(instance); } mlt_image_format Image::format() { return instance->format; } int Image::width() { return instance->width; } int Image::height() { return instance->height; } void Image::set_colorspace(int colorspace) { instance->colorspace = colorspace; } int Image::colorspace() { return instance->colorspace; } void Image::alloc(int width, int height, mlt_image_format format, bool alpha) { instance->width = width; instance->height = height; instance->format = format; mlt_image_alloc_data(instance); if (alpha) mlt_image_alloc_alpha(instance); } void Image::init_alpha() { mlt_image_alloc_alpha(instance); } uint8_t *Image::plane(int plane) { return instance->planes[plane]; } int Image::stride(int plane) { return instance->strides[plane]; } mlt-7.38.0/src/mlt++/MltImage.h000664 000000 000000 00000003452 15172202314 015747 0ustar00rootroot000000 000000 /** * MltImage.h - MLT Wrapper * Copyright (C) 2021-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_IMAGE_H #define MLTPP_IMAGE_H #include "MltConfig.h" #include namespace Mlt { /** \brief C++ wrapper for ::mlt_image -- a decoded video frame buffer. * * Holds a raw pixel buffer together with its format metadata (width, height, * image format, colorspace). Supports multi-plane formats via plane() and * stride(). Use alloc() to allocate a new buffer, or construct from an * existing ::mlt_image handle obtained from a Frame. * * \see mlt_image_s */ class MLTPP_DECLSPEC Image { private: mlt_image instance; public: Image(); Image(mlt_image image); Image(int width, int height, mlt_image_format format); virtual ~Image(); mlt_image_format format(); int width(); int height(); void set_colorspace(int colorspace); int colorspace(); void alloc(int width, int height, mlt_image_format format, bool alpha = false); void init_alpha(); uint8_t *plane(int plane); int stride(int plane); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltLink.cpp000664 000000 000000 00000004713 15172202314 016156 0ustar00rootroot000000 000000 /** * MltLink.cpp - MLT Wrapper * Copyright (C) 2020-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltLink.h" #include "MltProfile.h" #include #include using namespace Mlt; Link::Link() : instance(nullptr) {} Link::Link(mlt_link link) : instance(link) { inc_ref(); } Link::Link(const char *id, const char *arg) : instance(NULL) { if (arg != NULL) { instance = mlt_factory_link(id, arg); } else { if (strchr(id, ':')) { char *temp = strdup(id); char *arg = strchr(temp, ':') + 1; *(arg - 1) = '\0'; instance = mlt_factory_link(temp, arg); free(temp); } else { instance = mlt_factory_link(id, NULL); } } } Link::Link(Link *link) : Link(link ? link->get_link() : nullptr) {} Link::Link(Service &link) : instance(nullptr) { if (link.type() == mlt_service_link_type) { instance = (mlt_link) link.get_service(); inc_ref(); } } Link::Link(Link &link) { if (link.type() == mlt_service_link_type) { instance = (mlt_link) link.get_service(); inc_ref(); } } Link::Link(const Link &link) : Link(const_cast(link)) {} Link &Link::operator=(const Link &link) { if (this != &link) { mlt_link_close(instance); instance = link.instance; inc_ref(); } return *this; } Link::~Link() { mlt_link_close(instance); } mlt_link Link::get_link() { return instance; } mlt_producer Link::get_producer() { return MLT_LINK_PRODUCER(instance); } int Link::connect_next(Mlt::Producer &next, Mlt::Profile &default_profile) { return mlt_link_connect_next(instance, next.get_producer(), default_profile.get_profile()); } mlt-7.38.0/src/mlt++/MltLink.h000664 000000 000000 00000003470 15172202314 015622 0ustar00rootroot000000 000000 /** * MltLink.h - MLT Wrapper * Copyright (C) 2020-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_LINK_H #define MLTPP_LINK_H #include "MltConfig.h" #include "MltProducer.h" #include namespace Mlt { class Producer; class Profile; /** \brief C++ wrapper for ::mlt_link — a processing stage in a Chain. * * A link is similar to a filter but designed for use within a Chain. Links * are attached with Chain::attach() and applied in order to the source * producer. Connect the next producer in the chain with connect_next(). * * \extends Producer * \see mlt_link_s */ class MLTPP_DECLSPEC Link : public Producer { private: mlt_link instance; public: Link(); Link(mlt_link link); Link(const char *id, const char *service = NULL); Link(Link *link); Link(Service &link); Link(Link &link); Link(const Link &link); Link &operator=(const Link &link); virtual ~Link(); virtual mlt_link get_link(); mlt_producer get_producer() override; int connect_next(Mlt::Producer &next, Mlt::Profile &default_profile); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltMultitrack.cpp000664 000000 000000 00000004535 15172202314 017402 0ustar00rootroot000000 000000 /** * MltMultitrack.h - Multitrack wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltMultitrack.h" #include "MltProducer.h" using namespace Mlt; Multitrack::Multitrack(mlt_multitrack multitrack) : instance(multitrack) { inc_ref(); } Multitrack::Multitrack(Service &multitrack) : instance(NULL) { if (multitrack.type() == mlt_service_multitrack_type) { instance = (mlt_multitrack) multitrack.get_service(); inc_ref(); } } Multitrack::Multitrack(Multitrack &multitrack) : Mlt::Producer(multitrack) , instance(multitrack.get_multitrack()) { inc_ref(); } Multitrack::~Multitrack() { mlt_multitrack_close(instance); } mlt_multitrack Multitrack::get_multitrack() { return instance; } mlt_producer Multitrack::get_producer() { return mlt_multitrack_producer(get_multitrack()); } int Multitrack::connect(Producer &producer, int index) { return mlt_multitrack_connect(get_multitrack(), producer.get_producer(), index); } int Multitrack::insert(Producer &producer, int index) { return mlt_multitrack_insert(get_multitrack(), producer.get_producer(), index); } int Multitrack::disconnect(int index) { return mlt_multitrack_disconnect(get_multitrack(), index); } int Multitrack::clip(mlt_whence whence, int index) { return mlt_multitrack_clip(get_multitrack(), whence, index); } int Multitrack::count() { return mlt_multitrack_count(get_multitrack()); } Producer *Multitrack::track(int index) { return new Producer(mlt_multitrack_track(get_multitrack(), index)); } void Multitrack::refresh() { return mlt_multitrack_refresh(get_multitrack()); } mlt-7.38.0/src/mlt++/MltMultitrack.h000664 000000 000000 00000004364 15172202314 017047 0ustar00rootroot000000 000000 /** * MltMultitrack.h - Multitrack wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_MULTITRACK_H #define MLTPP_MULTITRACK_H #include "MltConfig.h" #include #include "MltProducer.h" namespace Mlt { class Service; class Producer; /** \brief C++ wrapper for ::mlt_multitrack — parallel track container. * * A multitrack holds multiple producers on parallel tracks and delivers * one frame per track per pull. It is the track container inside a Tractor. * * \extends Producer * \see mlt_multitrack_s */ class MLTPP_DECLSPEC Multitrack : public Producer { private: mlt_multitrack instance; public: /** Wrap an existing ::mlt_multitrack handle. */ Multitrack(mlt_multitrack multitrack); Multitrack(Service &multitrack); Multitrack(Multitrack &multitrack); virtual ~Multitrack(); mlt_multitrack get_multitrack(); mlt_producer get_producer() override; /** Connect \p producer as track \p index. */ int connect(Producer &producer, int index); /** Insert \p producer at \p index, shifting higher tracks up. */ int insert(Producer &producer, int index); /** Disconnect the track at \p index. */ int disconnect(int index); int clip(mlt_whence whence, int index); /** Return the number of tracks. */ int count(); /** Return the producer on track \p index. Caller does not own the result. */ Producer *track(int index); /** Recalculate the multitrack length from its tracks. */ void refresh(); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltParser.cpp000664 000000 000000 00000023560 15172202314 016516 0ustar00rootroot000000 000000 /** * MltParser.cpp - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Mlt.h" using namespace Mlt; static int on_invalid_cb(mlt_parser self, mlt_service object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Service service(object); return parser->on_invalid(&service); } static int on_unknown_cb(mlt_parser self, mlt_service object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Service service(object); return parser->on_unknown(&service); } static int on_start_producer_cb(mlt_parser self, mlt_producer object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Producer producer(object); return parser->on_start_producer(&producer); } static int on_end_producer_cb(mlt_parser self, mlt_producer object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Producer producer(object); return parser->on_end_producer(&producer); } static int on_start_playlist_cb(mlt_parser self, mlt_playlist object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Playlist playlist(object); return parser->on_start_playlist(&playlist); } static int on_end_playlist_cb(mlt_parser self, mlt_playlist object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Playlist playlist(object); return parser->on_end_playlist(&playlist); } static int on_start_tractor_cb(mlt_parser self, mlt_tractor object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Tractor tractor(object); return parser->on_start_tractor(&tractor); } static int on_end_tractor_cb(mlt_parser self, mlt_tractor object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Tractor tractor(object); return parser->on_end_tractor(&tractor); } static int on_start_multitrack_cb(mlt_parser self, mlt_multitrack object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Multitrack multitrack(object); return parser->on_start_multitrack(&multitrack); } static int on_end_multitrack_cb(mlt_parser self, mlt_multitrack object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Multitrack multitrack(object); return parser->on_end_multitrack(&multitrack); } static int on_start_track_cb(mlt_parser self) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); return parser->on_start_track(); } static int on_end_track_cb(mlt_parser self) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); return parser->on_end_track(); } static int on_start_filter_cb(mlt_parser self, mlt_filter object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Filter filter(object); return parser->on_start_filter(&filter); } static int on_end_filter_cb(mlt_parser self, mlt_filter object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Filter filter(object); return parser->on_end_filter(&filter); } static int on_start_transition_cb(mlt_parser self, mlt_transition object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Transition transition(object); return parser->on_start_transition(&transition); } static int on_end_transition_cb(mlt_parser self, mlt_transition object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Transition transition(object); return parser->on_end_transition(&transition); } static int on_start_chain_cb(mlt_parser self, mlt_chain object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Chain chain(object); return parser->on_start_chain(&chain); } static int on_end_chain_cb(mlt_parser self, mlt_chain object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Chain chain(object); return parser->on_end_chain(&chain); } static int on_start_link_cb(mlt_parser self, mlt_link object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Link link(object); return parser->on_start_link(&link); } static int on_end_link_cb(mlt_parser self, mlt_link object) { mlt_properties properties = mlt_parser_properties(self); Parser *parser = (Parser *) mlt_properties_get_data(properties, "_parser_object", NULL); Link link(object); return parser->on_end_link(&link); } Parser::Parser() : Properties(false) { parser = mlt_parser_new(); set("_parser_object", this, 0); parser->on_invalid = on_invalid_cb; parser->on_unknown = on_unknown_cb; parser->on_start_producer = on_start_producer_cb; parser->on_end_producer = on_end_producer_cb; parser->on_start_playlist = on_start_playlist_cb; parser->on_end_playlist = on_end_playlist_cb; parser->on_start_tractor = on_start_tractor_cb; parser->on_end_tractor = on_end_tractor_cb; parser->on_start_multitrack = on_start_multitrack_cb; parser->on_end_multitrack = on_end_multitrack_cb; parser->on_start_track = on_start_track_cb; parser->on_end_track = on_end_track_cb; parser->on_start_filter = on_start_filter_cb; parser->on_end_filter = on_end_filter_cb; parser->on_start_transition = on_start_transition_cb; parser->on_end_transition = on_end_transition_cb; parser->on_start_chain = on_start_chain_cb; parser->on_end_chain = on_end_chain_cb; parser->on_start_link = on_start_link_cb; parser->on_end_link = on_end_link_cb; } Parser::~Parser() { mlt_parser_close(parser); } mlt_properties Parser::get_properties() { return mlt_parser_properties(parser); } int Parser::start(Service &service) { return mlt_parser_start(parser, service.get_service()); } int Parser::on_invalid(Service *object) { object->debug("Invalid"); return 0; } int Parser::on_unknown(Service *object) { object->debug("Unknown"); return 0; } int Parser::on_start_producer(Producer *object) { object->debug("on_start_producer"); return 0; } int Parser::on_end_producer(Producer *object) { object->debug("on_end_producer"); return 0; } int Parser::on_start_playlist(Playlist *object) { object->debug("on_start_playlist"); return 0; } int Parser::on_end_playlist(Playlist *object) { object->debug("on_end_playlist"); return 0; } int Parser::on_start_tractor(Tractor *object) { object->debug("on_start_tractor"); return 0; } int Parser::on_end_tractor(Tractor *object) { object->debug("on_end_tractor"); return 0; } int Parser::on_start_multitrack(Multitrack *object) { object->debug("on_start_multitrack"); return 0; } int Parser::on_end_multitrack(Multitrack *object) { object->debug("on_end_multitrack"); return 0; } int Parser::on_start_track() { fprintf(stderr, "on_start_track\n"); return 0; } int Parser::on_end_track() { fprintf(stderr, "on_end_track\n"); return 0; } int Parser::on_start_filter(Filter *object) { object->debug("on_start_filter"); return 0; } int Parser::on_end_filter(Filter *object) { object->debug("on_end_filter"); return 0; } int Parser::on_start_transition(Transition *object) { object->debug("on_start_transition"); return 0; } int Parser::on_end_transition(Transition *object) { object->debug("on_end_transition"); return 0; } int Parser::on_start_chain(Chain *object) { object->debug("on_start_chain"); return 0; } int Parser::on_end_chain(Chain *object) { object->debug("on_end_chain"); return 0; } int Parser::on_start_link(Link *object) { object->debug("on_start_link"); return 0; } int Parser::on_end_link(Link *object) { object->debug("on_end_link"); return 0; } mlt-7.38.0/src/mlt++/MltParser.h000664 000000 000000 00000005216 15172202314 016161 0ustar00rootroot000000 000000 /** * MltParser.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PARSER_H #define MLTPP_PARSER_H #include "MltConfig.h" #include "MltProperties.h" #include namespace Mlt { class Chain; class Filter; class Link; class Multitrack; class Playlist; class Producer; class Properties; class Service; class Tractor; class Transition; /** \brief C++ wrapper for ::mlt_parser — walks a service network and fires callbacks. * * Subclass Parser and override the virtual on_start and on_end methods to * inspect or transform every node in a service graph(producers, playlists, *tractors, filters, transitions, chains, links). * Call start() to begin traversal. * * \extends Properties * \see mlt_parser_s */ class MLTPP_DECLSPEC Parser : public Properties { private: mlt_parser parser; public: Parser(); ~Parser(); int start(Service &service); virtual mlt_properties get_properties() override; virtual int on_invalid(Service *object); virtual int on_unknown(Service *object); virtual int on_start_producer(Producer *object); virtual int on_end_producer(Producer *object); virtual int on_start_playlist(Playlist *object); virtual int on_end_playlist(Playlist *object); virtual int on_start_tractor(Tractor *object); virtual int on_end_tractor(Tractor *object); virtual int on_start_multitrack(Multitrack *object); virtual int on_end_multitrack(Multitrack *object); virtual int on_start_track(); virtual int on_end_track(); virtual int on_start_filter(Filter *object); virtual int on_end_filter(Filter *object); virtual int on_start_transition(Transition *object); virtual int on_end_transition(Transition *object); virtual int on_start_chain(Chain *object); virtual int on_end_chain(Chain *object); virtual int on_start_link(Link *object); virtual int on_end_link(Link *object); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltPlaylist.cpp000664 000000 000000 00000017752 15172202314 017071 0ustar00rootroot000000 000000 /** * MltPlaylist.cpp - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltPlaylist.h" #include "MltProfile.h" #include "MltTransition.h" #include #include using namespace Mlt; ClipInfo::ClipInfo() : clip(0) , producer(NULL) , cut(NULL) , start(0) , resource(NULL) , frame_in(0) , frame_out(0) , frame_count(0) , length(0) , fps(0) , repeat(0) {} ClipInfo::ClipInfo(mlt_playlist_clip_info *info) : clip(info->clip) , producer(new Producer(info->producer)) , cut(new Producer(info->cut)) , start(info->start) , resource(info->resource ? strdup(info->resource) : 0) , frame_in(info->frame_in) , frame_out(info->frame_out) , frame_count(info->frame_count) , length(info->length) , fps(info->fps) , repeat(info->repeat) {} ClipInfo::~ClipInfo() { delete producer; delete cut; free(resource); } void ClipInfo::update(mlt_playlist_clip_info *info) { delete producer; delete cut; free(resource); clip = info->clip; producer = new Producer(info->producer); cut = new Producer(info->cut); start = info->start; resource = info->resource ? strdup(info->resource) : 0; frame_in = info->frame_in; frame_out = info->frame_out; frame_count = info->frame_count; length = info->length; fps = info->fps; repeat = info->repeat; } Playlist::Playlist() : instance(NULL) { instance = mlt_playlist_new(nullptr); } Playlist::Playlist(Profile &profile) : instance(NULL) { instance = mlt_playlist_new(profile.get_profile()); } Playlist::Playlist(Service &producer) : instance(NULL) { if (producer.type() == mlt_service_playlist_type) { instance = (mlt_playlist) producer.get_service(); inc_ref(); } } Playlist::Playlist(Playlist &playlist) : Mlt::Producer(playlist) , instance(playlist.get_playlist()) { inc_ref(); } Playlist::Playlist(mlt_playlist playlist) : instance(playlist) { inc_ref(); } Playlist::~Playlist() { mlt_playlist_close(instance); } mlt_playlist Playlist::get_playlist() { return instance; } mlt_producer Playlist::get_producer() { return mlt_playlist_producer(get_playlist()); } int Playlist::count() { return mlt_playlist_count(get_playlist()); } int Playlist::clear() { return mlt_playlist_clear(get_playlist()); } int Playlist::append(Producer &producer, int in, int out) { return mlt_playlist_append_io(get_playlist(), producer.get_producer(), in, out); } int Playlist::blank(int out) { return mlt_playlist_blank(get_playlist(), out); } int Playlist::blank(const char *length) { return mlt_playlist_blank_time(get_playlist(), length); } int Playlist::clip(mlt_whence whence, int index) { return mlt_playlist_clip(get_playlist(), whence, index); } int Playlist::current_clip() { return mlt_playlist_current_clip(get_playlist()); } Producer *Playlist::current() { return new Producer(mlt_playlist_current(get_playlist())); } ClipInfo *Playlist::clip_info(int index, ClipInfo *info) { mlt_playlist_clip_info clip_info; if (mlt_playlist_get_clip_info(get_playlist(), &clip_info, index)) return NULL; if (info == NULL) return new ClipInfo(&clip_info); info->update(&clip_info); return info; } void Playlist::delete_clip_info(ClipInfo *info) { delete info; } int Playlist::insert(Producer &producer, int where, int in, int out) { return mlt_playlist_insert(get_playlist(), producer.get_producer(), where, in, out); } int Playlist::remove(int where) { return mlt_playlist_remove(get_playlist(), where); } int Playlist::move(int from, int to) { return mlt_playlist_move(get_playlist(), from, to); } int Playlist::reorder(const int *indices) { return mlt_playlist_reorder(get_playlist(), indices); } int Playlist::resize_clip(int clip, int in, int out) { return mlt_playlist_resize_clip(get_playlist(), clip, in, out); } int Playlist::split(int clip, int position) { return mlt_playlist_split(get_playlist(), clip, position); } int Playlist::split_at(int position, bool left) { return mlt_playlist_split_at(get_playlist(), position, left); } int Playlist::join(int clip, int count, int merge) { return mlt_playlist_join(get_playlist(), clip, count, merge); } int Playlist::mix(int clip, int length, Transition *transition) { return mlt_playlist_mix(get_playlist(), clip, length, transition == NULL ? NULL : transition->get_transition()); } int Playlist::mix_in(int clip, int length) { return mlt_playlist_mix_in(get_playlist(), clip, length); } int Playlist::mix_out(int clip, int length) { return mlt_playlist_mix_out(get_playlist(), clip, length); } int Playlist::mix_add(int clip, Transition *transition) { return mlt_playlist_mix_add(get_playlist(), clip, transition == NULL ? NULL : transition->get_transition()); } int Playlist::repeat(int clip, int count) { return mlt_playlist_repeat_clip(get_playlist(), clip, count); } Producer *Playlist::get_clip(int clip) { mlt_producer producer = mlt_playlist_get_clip(get_playlist(), clip); return producer != NULL ? new Producer(producer) : NULL; } Producer *Playlist::get_clip_at(int position) { mlt_producer producer = mlt_playlist_get_clip_at(get_playlist(), position); return producer != NULL ? new Producer(producer) : NULL; } int Playlist::get_clip_index_at(int position) { return mlt_playlist_get_clip_index_at(get_playlist(), position); } bool Playlist::is_mix(int clip) { return mlt_playlist_clip_is_mix(get_playlist(), clip) != 0; } bool Playlist::is_blank(int clip) { return mlt_playlist_is_blank(get_playlist(), clip) != 0; } bool Playlist::is_blank_at(int position) { return mlt_playlist_is_blank_at(get_playlist(), position) != 0; } Producer *Playlist::replace_with_blank(int clip) { mlt_producer producer = mlt_playlist_replace_with_blank(get_playlist(), clip); Producer *object = producer != NULL ? new Producer(producer) : NULL; mlt_producer_close(producer); return object; } void Playlist::consolidate_blanks(int keep_length) { return mlt_playlist_consolidate_blanks(get_playlist(), keep_length); } void Playlist::insert_blank(int clip, int out) { mlt_playlist_insert_blank(get_playlist(), clip, out); } void Playlist::pad_blanks(int position, int length, int find) { mlt_playlist_pad_blanks(get_playlist(), position, length, find); } int Playlist::insert_at(int position, Producer *producer, int mode) { return mlt_playlist_insert_at(get_playlist(), position, producer->get_producer(), mode); } int Playlist::insert_at(int position, Producer &producer, int mode) { return mlt_playlist_insert_at(get_playlist(), position, producer.get_producer(), mode); } int Playlist::clip_start(int clip) { return mlt_playlist_clip_start(get_playlist(), clip); } int Playlist::blanks_from(int clip, int bounded) { return mlt_playlist_blanks_from(get_playlist(), clip, bounded); } int Playlist::clip_length(int clip) { return mlt_playlist_clip_length(get_playlist(), clip); } int Playlist::remove_region(int position, int length) { return mlt_playlist_remove_region(get_playlist(), position, length); } mlt-7.38.0/src/mlt++/MltPlaylist.h000664 000000 000000 00000013640 15172202314 016526 0ustar00rootroot000000 000000 /** * MltPlaylist.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PLAYLIST_H #define MLTPP_PLAYLIST_H #include "MltConfig.h" #include #include "MltProducer.h" namespace Mlt { class Producer; class Service; class Playlist; class Transition; class Profile; /** \brief Metadata for a single clip within a Playlist. * * Populated by Playlist::clip_info(). The \p producer and \p cut fields * are owned by the ClipInfo object. */ class MLTPP_DECLSPEC ClipInfo { public: ClipInfo(); ClipInfo(mlt_playlist_clip_info *info); ~ClipInfo(); void update(mlt_playlist_clip_info *info); int clip; Producer *producer; Producer *cut; int start; char *resource; int frame_in; int frame_out; int frame_count; int length; float fps; int repeat; }; /** \brief C++ wrapper for ::mlt_playlist — a sequential list of clips. * * A playlist is a producer that plays its clips in order. Clips are * producers (or cut-producers) that may be appended, inserted, removed, * and trimmed. Blank gaps between clips are also supported. * * \extends Producer * \see mlt_playlist_s */ class MLTPP_DECLSPEC Playlist : public Producer { private: mlt_playlist instance; public: Playlist(); Playlist(Profile &profile); Playlist(Service &playlist); Playlist(Playlist &playlist); /** Wrap an existing ::mlt_playlist handle. */ Playlist(mlt_playlist playlist); virtual ~Playlist(); virtual mlt_playlist get_playlist(); mlt_producer get_producer() override; /** Return the number of entries (clips and blanks). */ int count(); /** Remove all entries from the playlist. */ int clear(); /** Append \p producer (optionally trimmed to [\p in, \p out]) to the end. */ int append(Producer &producer, int in = -1, int out = -1); /** Append a blank of \p out + 1 frames. */ int blank(int out); /** Append a blank of the given timecode \p length. */ int blank(const char *length); int clip(mlt_whence whence, int index); /** Return the index of the currently playing clip. */ int current_clip(); /** Return the currently playing producer. Caller owns the result. */ Producer *current(); /** Return metadata for the clip at \p index. When \p info is NULL a new ClipInfo is * returned and the caller owns it; otherwise \p info is updated in-place and returned. */ ClipInfo *clip_info(int index, ClipInfo *info = NULL); static void delete_clip_info(ClipInfo *info); /** Insert \p producer at position \p where, shifting later clips. */ int insert(Producer &producer, int where, int in = -1, int out = -1); /** Remove the clip at position \p where. */ int remove(int where); /** Move the clip at \p from to \p to. */ int move(int from, int to); /** Reorder the playlist using an array of old indices. */ int reorder(const int *indices); /** Retrim the clip at \p clip to [\p in, \p out]. */ int resize_clip(int clip, int in, int out); /** Split the clip at \p clip at frame \p position (relative to clip start). */ int split(int clip, int position); /** Split at absolute timeline \p position. */ int split_at(int position, bool left = true); /** Join \p count clips starting at \p clip into one. */ int join(int clip, int count = 1, int merge = 1); /** Create a mix (dissolve) of \p length frames at the \p clip boundary. */ int mix(int clip, int length, Transition *transition = NULL); int mix_in(int clip, int length); int mix_out(int clip, int length); /** Add a transition to an existing mix at \p clip. */ int mix_add(int clip, Transition *transition); /** Repeat the clip at \p clip \p count additional times. */ int repeat(int clip, int count); /** Return the producer for clip \p clip. Caller owns the result. */ Producer *get_clip(int clip); /** Return the producer at absolute timeline frame \p position. Caller owns the result. */ Producer *get_clip_at(int position); /** Return the clip index at absolute timeline frame \p position. */ int get_clip_index_at(int position); /** Return true if the clip at \p clip is a mix. */ bool is_mix(int clip); /** Return true if the clip at \p clip is a blank. */ bool is_blank(int clip); /** Return true if the absolute timeline \p position is in a blank. */ bool is_blank_at(int position); /** Merge adjacent blanks; if \p keep_length is 0, trim trailing blanks. */ void consolidate_blanks(int keep_length = 0); /** Replace the clip at \p clip with a blank of the same length. Caller owns the result. */ Producer *replace_with_blank(int clip); void insert_blank(int clip, int out); void pad_blanks(int position, int length, int find = 0); /** Insert \p producer at absolute timeline \p position. */ int insert_at(int position, Producer *producer, int mode = 0); int insert_at(int position, Producer &producer, int mode = 0); /** Return the start frame of clip \p clip in the timeline. */ int clip_start(int clip); int clip_length(int clip); int blanks_from(int clip, int bounded = 0); int remove_region(int position, int length); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltProducer.cpp000664 000000 000000 00000014135 15172202314 017043 0ustar00rootroot000000 000000 /** * MltProducer.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltProducer.h" #include "MltConsumer.h" #include "MltEvent.h" #include "MltFilter.h" #include "MltProfile.h" using namespace Mlt; Producer::Producer() : instance(NULL) , parent_(NULL) {} Producer::Producer(Profile &profile, const char *id, const char *service) : Producer(profile.get_profile(), id, service) {} Producer::Producer(mlt_profile profile, const char *id, const char *service) : instance(NULL) , parent_(NULL) { if (id != NULL && service != NULL) instance = mlt_factory_producer(profile, id, service); else instance = mlt_factory_producer(profile, NULL, id != NULL ? id : service); } Producer::Producer(Service &producer) : instance(NULL) , parent_(NULL) { mlt_service_type type = producer.type(); if (type == mlt_service_producer_type || type == mlt_service_playlist_type || type == mlt_service_tractor_type || type == mlt_service_multitrack_type || type == mlt_service_transition_type || type == mlt_service_chain_type || type == mlt_service_link_type) { instance = (mlt_producer) producer.get_service(); inc_ref(); } } Producer::Producer(mlt_producer producer) : instance(producer) , parent_(NULL) { inc_ref(); } Producer::Producer(Producer &producer) : Mlt::Service(producer) , instance(producer.get_producer()) , parent_(NULL) { inc_ref(); } Producer::Producer(const Producer &producer) : Producer(const_cast(producer)) {} Producer::Producer(Producer *producer) : instance(producer != NULL ? producer->get_producer() : NULL) , parent_(NULL) { if (is_valid()) inc_ref(); } Producer::~Producer() { delete parent_; mlt_producer_close(instance); instance = NULL; } Producer &Producer::operator=(const Producer &producer) { if (this != &producer) { delete parent_; parent_ = nullptr; mlt_producer_close(instance); instance = producer.instance; inc_ref(); } return *this; } mlt_producer Producer::get_producer() { return instance; } mlt_producer Producer::get_parent() { return get_producer() != NULL && mlt_producer_cut_parent(get_producer()) != NULL ? mlt_producer_cut_parent(get_producer()) : get_producer(); } Producer &Producer::parent() { if (is_cut() && parent_ == NULL) parent_ = new Producer(get_parent()); return parent_ == NULL ? *this : *parent_; } mlt_service Producer::get_service() { return mlt_producer_service(get_producer()); } int Producer::seek(int position) { return mlt_producer_seek(get_producer(), position); } int Producer::seek(const char *time) { return mlt_producer_seek_time(get_producer(), time); } int Producer::position() { return mlt_producer_position(get_producer()); } int Producer::frame() { return mlt_producer_frame(get_producer()); } char *Producer::frame_time(mlt_time_format format) { return mlt_producer_frame_time(get_producer(), format); } int Producer::set_speed(double speed) { return mlt_producer_set_speed(get_producer(), speed); } int Producer::pause() { int result = 0; if (get_speed() != 0) { Consumer consumer((mlt_consumer) mlt_service_consumer(get_service())); Event *event = consumer.setup_wait_for("consumer-sdl-paused"); result = mlt_producer_set_speed(get_producer(), 0); if (result == 0 && consumer.is_valid() && !consumer.is_stopped()) consumer.wait_for(event); delete event; } return result; } double Producer::get_speed() { return mlt_producer_get_speed(get_producer()); } double Producer::get_fps() { return mlt_producer_get_fps(get_producer()); } int Producer::set_in_and_out(int in, int out) { return mlt_producer_set_in_and_out(get_producer(), in, out); } int Producer::get_in() { return mlt_producer_get_in(get_producer()); } int Producer::get_out() { return mlt_producer_get_out(get_producer()); } int Producer::get_length() { return mlt_producer_get_length(get_producer()); } char *Producer::get_length_time(mlt_time_format format) { return mlt_producer_get_length_time(get_producer(), format); } int Producer::get_playtime() { return mlt_producer_get_playtime(get_producer()); } Producer *Producer::cut(int in, int out) { mlt_producer producer = mlt_producer_cut(get_producer(), in, out); Producer *result = new Producer(producer); mlt_producer_close(producer); return result; } bool Producer::is_cut() { return mlt_producer_is_cut(get_producer()) != 0; } bool Producer::is_blank() { return mlt_producer_is_blank(get_producer()) != 0; } bool Producer::same_clip(Producer &that) { return mlt_producer_cut_parent(get_producer()) == mlt_producer_cut_parent(that.get_producer()); } bool Producer::runs_into(Producer &that) { return same_clip(that) && get_out() == (that.get_in() - 1); } void Producer::optimise() { mlt_producer_optimise(get_producer()); } int Producer::clear() { return mlt_producer_clear(get_producer()); } int64_t Producer::get_creation_time() { return mlt_producer_get_creation_time(get_producer()); } void Producer::set_creation_time(int64_t creation_time) { mlt_producer_set_creation_time(get_producer(), creation_time); } bool Producer::probe() { return mlt_producer_probe(get_producer()); } mlt-7.38.0/src/mlt++/MltProducer.h000664 000000 000000 00000007726 15172202314 016520 0ustar00rootroot000000 000000 /** * MltProducer.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PRODUCER_H #define MLTPP_PRODUCER_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Filter; class Profile; class Frame; /** \brief C++ wrapper for ::mlt_producer — origin of audio/video data. * * A producer generates frames of audio and video data. It may read from a * file, network stream, or synthesize content. Producers support in/out * points, playback speed, and can be "cut" (sub-range) instances that share * the underlying resource. * * \extends Service * \see mlt_producer_s */ class MLTPP_DECLSPEC Producer : public Service { private: mlt_producer instance; Producer *parent_; public: Producer(); /** Construct and instantiate producer \p id from the repository. */ Producer(Profile &profile, const char *id, const char *service = NULL); Producer(mlt_profile profile, const char *id, const char *service = NULL); Producer(Service &producer); /** Wrap an existing ::mlt_producer handle. */ Producer(mlt_producer producer); Producer(Producer &producer); Producer(const Producer &producer); Producer(Producer *producer); virtual ~Producer(); Producer &operator=(const Producer &producer); virtual mlt_producer get_producer(); /** Return the parent producer (for cut producers, the shared root). */ Producer &parent(); mlt_producer get_parent(); mlt_service get_service() override; /** Seek to \p position (relative to in-point). */ int seek(int position); /** Seek to a timecode string (e.g. "00:01:23.04"). */ int seek(const char *time); /** Return the current position relative to in-point. */ int position(); /** Return the current absolute frame number. */ int frame(); /** Return the current timecode string. */ char *frame_time(mlt_time_format = mlt_time_smpte_df); /** Set the playback speed (1.0 = normal, 0.0 = paused, -1.0 = reverse). */ int set_speed(double speed); /** Pause playback (sets speed to 0). */ int pause(); /** Return the current playback speed. */ double get_speed(); /** Return the frames-per-second rate for this producer's profile. */ double get_fps(); /** Set the active in/out range in frame numbers. */ int set_in_and_out(int in, int out); /** Return the in-point frame number. */ int get_in(); /** Return the out-point frame number. */ int get_out(); /** Return the total length of the underlying resource in frames. */ int get_length(); char *get_length_time(mlt_time_format = mlt_time_smpte_df); /** Return the active play duration (out - in + 1). */ int get_playtime(); /** Create a cut (sub-range) producer sharing this producer's resource. Caller owns result. */ Producer *cut(int in = 0, int out = -1); /** Return true if this producer is a cut. */ bool is_cut(); /** Return true if this is a blank/gap clip. */ bool is_blank(); bool same_clip(Producer &that); bool runs_into(Producer &that); void optimise(); int clear(); int64_t get_creation_time(); void set_creation_time(int64_t creation_time); bool probe(); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltProfile.cpp000664 000000 000000 00000007665 15172202314 016672 0ustar00rootroot000000 000000 /** * MltProfile.cpp - MLT Wrapper * Copyright (C) 2008-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltProfile.h" #include "MltProducer.h" #include "MltProperties.h" using namespace Mlt; Profile::Profile() : instance(NULL) { instance = mlt_profile_init(NULL); } Profile::Profile(const char *name) : instance(NULL) { instance = mlt_profile_init(name); } Profile::Profile(Properties &properties) : instance(NULL) { instance = mlt_profile_load_properties(properties.get_properties()); } Profile::Profile(mlt_profile profile) : instance(profile) {} Profile::~Profile() { if (instance) mlt_profile_close(instance); instance = NULL; } bool Profile::is_valid() const { return instance != NULL; } mlt_profile Profile::get_profile() const { return instance; } char *Profile::description() const { return instance->description; } int Profile::frame_rate_num() const { return instance->frame_rate_num; } int Profile::frame_rate_den() const { return instance->frame_rate_den; } double Profile::fps() const { return mlt_profile_fps(instance); } int Profile::width() const { return instance->width; } int Profile::height() const { return instance->height; } bool Profile::progressive() const { return instance->progressive; } int Profile::sample_aspect_num() const { return instance->sample_aspect_num; } int Profile::sample_aspect_den() const { return instance->sample_aspect_den; } double Profile::sar() const { return mlt_profile_sar(instance); } int Profile::display_aspect_num() const { return instance->display_aspect_num; } int Profile::display_aspect_den() const { return instance->display_aspect_den; } double Profile::dar() const { return mlt_profile_dar(instance); } int Profile::is_explicit() const { return instance->is_explicit; } int Profile::colorspace() const { return instance->colorspace; } Properties *Profile::list() { return new Properties(mlt_profile_list()); } void Profile::from_producer(Producer &producer) { mlt_profile_from_producer(instance, producer.get_producer()); } void Profile::set_width(int width) { instance->width = width; } void Profile::set_height(int height) { instance->height = height; } void Profile::set_sample_aspect(int numerator, int denominator) { instance->sample_aspect_num = numerator; instance->sample_aspect_den = denominator; } void Profile::set_display_aspect(int numerator, int denominator) { instance->display_aspect_num = numerator; instance->display_aspect_den = denominator; } void Profile::set_progressive(int progressive) { instance->progressive = progressive; } void Profile::set_colorspace(int colorspace) { instance->colorspace = colorspace; } void Profile::set_frame_rate(int numerator, int denominator) { instance->frame_rate_num = numerator; instance->frame_rate_den = denominator; } void Profile::set_explicit(int boolean) { instance->is_explicit = boolean; } double Profile::scale_width(int width) { return mlt_profile_scale_width(instance, width); } double Profile::scale_height(int height) { return mlt_profile_scale_height(instance, height); } bool Profile::is_valid() { return mlt_profile_is_valid(instance); } mlt-7.38.0/src/mlt++/MltProfile.h000664 000000 000000 00000005216 15172202314 016325 0ustar00rootroot000000 000000 /** * MltProfile.h - MLT Wrapper * Copyright (C) 2008-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PROFILE_H #define MLTPP_PROFILE_H #include "MltConfig.h" #ifdef SWIG #define MLTPP_DECLSPEC #endif #include namespace Mlt { class Properties; class Producer; /** \brief C++ wrapper for ::mlt_profile — video format parameters. * * A profile describes the video format that services will produce and * consume: resolution, frame rate, aspect ratio, colorspace, and * interlace mode. Pass a profile to service constructors to configure * the format. * * \see mlt_profile_s */ class MLTPP_DECLSPEC Profile { private: mlt_profile instance; public: Profile(); Profile(const char *name); Profile(Properties &properties); Profile(mlt_profile profile); ~Profile(); bool is_valid() const; mlt_profile get_profile() const; char *description() const; int frame_rate_num() const; int frame_rate_den() const; double fps() const; int width() const; int height() const; bool progressive() const; int sample_aspect_num() const; int sample_aspect_den() const; double sar() const; int display_aspect_num() const; int display_aspect_den() const; double dar() const; int is_explicit() const; int colorspace() const; /** Return a list of all available profile names. Caller owns the result. */ static Properties *list(); void from_producer(Producer &producer); void set_width(int width); void set_height(int height); void set_sample_aspect(int numerator, int denominator); void set_display_aspect(int numerator, int denominator); void set_progressive(int progressive); void set_colorspace(int colorspace); void set_frame_rate(int numerator, int denominator); void set_explicit(int boolean); double scale_width(int width); double scale_height(int height); bool is_valid(); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltProperties.cpp000664 000000 000000 00000030747 15172202314 017423 0ustar00rootroot000000 000000 /** * MltProperties.cpp - MLT Wrapper * Copyright (C) 2004-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltProperties.h" #include "MltAnimation.h" #include "MltEvent.h" using namespace Mlt; Properties::Properties() : instance(NULL) { instance = mlt_properties_new(); } Properties::Properties(bool /*dummy*/) : instance(NULL) {} Properties::Properties(Properties &properties) : instance(properties.get_properties()) { inc_ref(); } Properties::Properties(const Properties &properties) : Properties(const_cast(properties)) {} Properties::Properties(mlt_properties properties) : instance(properties) { inc_ref(); } Properties::Properties(void *properties) : instance(mlt_properties(properties)) { inc_ref(); } Properties::Properties(const char *file) : instance(NULL) { instance = mlt_properties_load(file); } Properties::~Properties() { mlt_properties_close(instance); } Properties &Properties::operator=(const Properties &properties) { if (this != &properties) { mlt_properties_close(instance); instance = properties.instance; inc_ref(); } return *this; } mlt_properties Properties::get_properties() { return instance; } int Properties::inc_ref() { return mlt_properties_inc_ref(get_properties()); } int Properties::dec_ref() { return mlt_properties_dec_ref(get_properties()); } int Properties::ref_count() { return mlt_properties_ref_count(get_properties()); } void Properties::lock() { mlt_properties_lock(get_properties()); } void Properties::unlock() { mlt_properties_unlock(get_properties()); } void Properties::block(void *object) { mlt_events_block(get_properties(), object != NULL ? object : get_properties()); } void Properties::unblock(void *object) { mlt_events_unblock(get_properties(), object != NULL ? object : get_properties()); } int Properties::fire_event(const char *event) { return mlt_events_fire(get_properties(), event, mlt_event_data_none()); } bool Properties::is_valid() { return get_properties() != NULL; } int Properties::count() { return mlt_properties_count(get_properties()); } char *Properties::get(const char *name) { return mlt_properties_get(get_properties(), name); } int Properties::get_int(const char *name) { return mlt_properties_get_int(get_properties(), name); } int64_t Properties::get_int64(const char *name) { return mlt_properties_get_int64(get_properties(), name); } double Properties::get_double(const char *name) { return mlt_properties_get_double(get_properties(), name); } void *Properties::get_data(const char *name, int &size) { return mlt_properties_get_data(get_properties(), name, &size); } void *Properties::get_data(const char *name) { return mlt_properties_get_data(get_properties(), name, NULL); } int Properties::set(const char *name, const char *value) { return mlt_properties_set(get_properties(), name, value); } int Properties::set_string(const char *name, const char *value) { return mlt_properties_set_string(get_properties(), name, value); } int Properties::set(const char *name, int value) { return mlt_properties_set_int(get_properties(), name, value); } int Properties::set(const char *name, int64_t value) { return mlt_properties_set_int64(get_properties(), name, value); } int Properties::set(const char *name, double value) { return mlt_properties_set_double(get_properties(), name, value); } int Properties::set( const char *name, void *value, int size, mlt_destructor destructor, mlt_serialiser serialiser) { return mlt_properties_set_data(get_properties(), name, value, size, destructor, serialiser); } int Properties::copy(Properties &that, const char *prefix) { return mlt_properties_copy(get_properties(), that.get_properties(), prefix); } void Properties::pass_property(Properties &that, const char *name) { return mlt_properties_pass_property(get_properties(), that.get_properties(), name); } int Properties::pass_values(Properties &that, const char *prefix) { return mlt_properties_pass(get_properties(), that.get_properties(), prefix); } int Properties::pass_list(Properties &that, const char *list) { return mlt_properties_pass_list(get_properties(), that.get_properties(), list); } int Properties::parse(const char *namevalue) { return mlt_properties_parse(get_properties(), namevalue); } char *Properties::get_name(int index) { return mlt_properties_get_name(get_properties(), index); } char *Properties::get(int index) { return mlt_properties_get_value(get_properties(), index); } char *Properties::get(int index, mlt_time_format format) { return mlt_properties_get_value_tf(get_properties(), index, format); } void *Properties::get_data(int index, int &size) { return mlt_properties_get_data_at(get_properties(), index, &size); } void Properties::mirror(Properties &that) { mlt_properties_mirror(get_properties(), that.get_properties()); } int Properties::inherit(Properties &that) { return mlt_properties_inherit(get_properties(), that.get_properties()); } int Properties::rename(const char *source, const char *dest) { return mlt_properties_rename(get_properties(), source, dest); } void Properties::dump(FILE *output) { mlt_properties_dump(get_properties(), output); } void Properties::debug(const char *title, FILE *output) { mlt_properties_debug(get_properties(), title, output); } void Properties::load(const char *file) { mlt_properties properties = mlt_properties_load(file); if (properties != NULL) mlt_properties_pass(get_properties(), properties, ""); mlt_properties_close(properties); } int Properties::save(const char *file) { return mlt_properties_save(get_properties(), file); } Event *Properties::listen(const char *id, void *object, mlt_listener listener) { mlt_event event = mlt_events_listen(get_properties(), object, id, listener); return new Event(event); } Event *Properties::setup_wait_for(const char *id) { return new Event(mlt_events_setup_wait_for(get_properties(), id)); } void Properties::delete_event(Event *event) { delete event; } void Properties::wait_for(Event *event, bool destroy) { mlt_events_wait_for(get_properties(), event->get_event()); if (destroy) mlt_events_close_wait_for(get_properties(), event->get_event()); } void Properties::wait_for(const char *id) { Event *event = setup_wait_for(id); wait_for(event); delete event; } bool Properties::is_sequence() { return mlt_properties_is_sequence(get_properties()); } Properties *Properties::parse_yaml(const char *file) { return new Properties(mlt_properties_parse_yaml(file)); } char *Properties::serialise_yaml() { return mlt_properties_serialise_yaml(get_properties()); } int Properties::preset(const char *name) { return mlt_properties_preset(get_properties(), name); } int Properties::set_lcnumeric(const char *locale) { return mlt_properties_set_lcnumeric(get_properties(), locale); } const char *Properties::get_lcnumeric() { return mlt_properties_get_lcnumeric(get_properties()); } void Properties::clear(const char *name) { return mlt_properties_clear(get_properties(), name); } bool Properties::property_exists(const char *name) { return mlt_properties_exists(get_properties(), name); } char *Properties::get_time(const char *name, mlt_time_format format) { return mlt_properties_get_time(get_properties(), name, format); } char *Properties::frames_to_time(int frames, mlt_time_format format) { return mlt_properties_frames_to_time(get_properties(), frames, format); } int Properties::time_to_frames(const char *time) { return mlt_properties_time_to_frames(get_properties(), time); } mlt_color Properties::get_color(const char *name) { return mlt_properties_get_color(get_properties(), name); } int Properties::set(const char *name, mlt_color value) { return mlt_properties_set_color(get_properties(), name, value); } mlt_color Properties::anim_get_color(const char *name, int position, int length) { return mlt_properties_anim_get_color(get_properties(), name, position, length); } int Properties::anim_set( const char *name, mlt_color value, int position, int length, mlt_keyframe_type keyframe_type) { return mlt_properties_anim_set_color(get_properties(), name, value, position, length, keyframe_type); } char *Properties::anim_get(const char *name, int position, int length) { return mlt_properties_anim_get(get_properties(), name, position, length); } int Properties::anim_set(const char *name, const char *value, int position, int length) { return mlt_properties_anim_set(get_properties(), name, value, position, length); } int Properties::anim_get_int(const char *name, int position, int length) { return mlt_properties_anim_get_int(get_properties(), name, position, length); } int Properties::anim_set( const char *name, int value, int position, int length, mlt_keyframe_type keyframe_type) { return mlt_properties_anim_set_int(get_properties(), name, value, position, length, keyframe_type); } double Properties::anim_get_double(const char *name, int position, int length) { return mlt_properties_anim_get_double(get_properties(), name, position, length); } int Properties::anim_set( const char *name, double value, int position, int length, mlt_keyframe_type keyframe_type) { return mlt_properties_anim_set_double(get_properties(), name, value, position, length, keyframe_type); } int Properties::set(const char *name, mlt_rect value) { return mlt_properties_set_rect(get_properties(), name, value); } int Properties::set(const char *name, double x, double y, double w, double h, double opacity) { mlt_rect value = {x, y, w, h, opacity}; return mlt_properties_set_rect(get_properties(), name, value); } mlt_rect Properties::get_rect(const char *name) { return mlt_properties_get_rect(get_properties(), name); } int Properties::anim_set( const char *name, mlt_rect value, int position, int length, mlt_keyframe_type keyframe_type) { return mlt_properties_anim_set_rect(get_properties(), name, value, position, length, keyframe_type); } mlt_rect Properties::anim_get_rect(const char *name, int position, int length) { return mlt_properties_anim_get_rect(get_properties(), name, position, length); } mlt_animation Properties::get_animation(const char *name) { return mlt_properties_get_animation(get_properties(), name); } Animation *Properties::get_anim(const char *name) { return new Animation(mlt_properties_get_animation(get_properties(), name)); } bool Properties::is_anim(const char *name) { return mlt_properties_is_anim(get_properties(), name); } int Properties::set(const char *name, Properties &properties) { return mlt_properties_set_properties(get_properties(), name, properties.get_properties()); } Properties *Properties::get_props(const char *name) { return new Properties(mlt_properties_get_properties(get_properties(), name)); } Properties *Properties::get_props_at(int index) { return new Properties(mlt_properties_get_properties_at(get_properties(), index)); } mlt-7.38.0/src/mlt++/MltProperties.h000664 000000 000000 00000020047 15172202314 017060 0ustar00rootroot000000 000000 /** * MltProperties.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PROPERTIES_H #define MLTPP_PROPERTIES_H #include "MltConfig.h" #include #include namespace Mlt { class Event; class Animation; /** \brief C++ wrapper for ::mlt_properties. * * Properties is a combination list/dictionary of name/value pairs and the * base class for most MLT objects. It fires a \em property-changed event * when a value is modified. * * \see mlt_properties_s */ class MLTPP_DECLSPEC Properties { private: mlt_properties instance; public: Properties(); Properties(bool dummy); Properties(Properties &properties); Properties(const Properties &properties); /** Wrap an existing ::mlt_properties without taking ownership. */ Properties(mlt_properties properties); Properties(void *properties); /** Load properties from a file. */ Properties(const char *file); virtual ~Properties(); Properties &operator=(const Properties &properties); virtual mlt_properties get_properties(); int inc_ref(); int dec_ref(); int ref_count(); /** Acquire the recursive mutex protecting this object. */ void lock(); /** Release the recursive mutex. */ void unlock(); /** Suppress event delivery to \p object. */ void block(void *object = NULL); /** Resume event delivery to \p object. */ void unblock(void *object = NULL); /** Fire a named event manually. */ int fire_event(const char *event); /** Return true if the underlying ::mlt_properties handle is non-null. */ bool is_valid(); /** Return the number of name/value pairs stored. */ int count(); /** Get a string property value by name. */ char *get(const char *name); /** Get an integer property value by name. */ int get_int(const char *name); /** Get a 64-bit integer property value by name. */ int64_t get_int64(const char *name); /** Get a double property value by name. */ double get_double(const char *name); /** Get opaque data stored by name; \p size receives the byte count. */ void *get_data(const char *name, int &size); void *get_data(const char *name); /** Set a string property. */ int set(const char *name, const char *value); /** Set a string property (no environment-variable expansion). */ int set_string(const char *name, const char *value); /** Set an integer property. */ int set(const char *name, int value); /** Set a 64-bit integer property. */ int set(const char *name, int64_t value); /** Set a double property. */ int set(const char *name, double value); /** Store opaque data with an optional destructor and serialiser. */ int set(const char *name, void *value, int size, mlt_destructor destroy = NULL, mlt_serialiser serial = NULL); /** Copy all properties whose names begin with \p prefix from \p that. */ int copy(Properties &that, const char *prefix); /** Copy a single named property from \p that. */ void pass_property(Properties &that, const char *name); /** Copy all properties whose names begin with \p prefix from \p that. */ int pass_values(Properties &that, const char *prefix); /** Copy the space-delimited list of named properties from \p that. */ int pass_list(Properties &that, const char *list); /** Parse a "name=value" string and store the result. */ int parse(const char *namevalue); /** Return the property name at positional \p index. */ char *get_name(int index); /** Return the property value at positional \p index as a string. */ char *get(int index); /** Return the property value at positional \p index in the given time format. */ char *get(int index, mlt_time_format); void *get_data(int index, int &size); /** Mirror all property changes on this object to \p that. */ void mirror(Properties &that); /** Copy all properties from \p that into this object. */ int inherit(Properties &that); /** Rename property \p source to \p dest. */ int rename(const char *source, const char *dest); /** Write all properties to \p output as "name=value" lines. */ void dump(FILE *output = stderr); void debug(const char *title = "Object", FILE *output = stderr); /** Merge properties from a file into this object. */ void load(const char *file); /** Save properties to a file. */ int save(const char *file); /** Register a listener callback for the named event. Caller owns the returned Event. */ Event *listen(const char *id, void *object, mlt_listener); static void delete_event(Event *); /** Set up a one-shot wait handle for event \p id. Caller owns the returned Event. */ Event *setup_wait_for(const char *id); void wait_for(Event *, bool destroy = true); void wait_for(const char *id); /** Return true if all property names are numeric indices (i.e. a sequence). */ bool is_sequence(); /** Parse a YAML Tiny file and return a new Properties tree. Caller owns the result. */ static Properties *parse_yaml(const char *file); char *serialise_yaml(); /** Apply a named preset from the presets directory. */ int preset(const char *name); int set_lcnumeric(const char *locale); const char *get_lcnumeric(); void clear(const char *name); bool property_exists(const char *name); char *get_time(const char *name, mlt_time_format = mlt_time_smpte_df); char *frames_to_time(int, mlt_time_format = mlt_time_smpte_df); int time_to_frames(const char *time); mlt_color get_color(const char *name); int set(const char *name, mlt_color value); mlt_color anim_get_color(const char *name, int position, int length = 0); int anim_set(const char *name, mlt_color value, int position, int length = 0, mlt_keyframe_type keyframe_type = mlt_keyframe_linear); char *anim_get(const char *name, int position, int length = 0); int anim_set(const char *name, const char *value, int position, int length = 0); int anim_get_int(const char *name, int position, int length = 0); int anim_set(const char *name, int value, int position, int length = 0, mlt_keyframe_type keyframe_type = mlt_keyframe_linear); double anim_get_double(const char *name, int position, int length = 0); int anim_set(const char *name, double value, int position, int length = 0, mlt_keyframe_type keyframe_type = mlt_keyframe_linear); int set(const char *name, mlt_rect value); int set(const char *name, double x, double y, double w, double h, double opacity = 1.0); mlt_rect get_rect(const char *name); int anim_set(const char *name, mlt_rect value, int position, int length = 0, mlt_keyframe_type keyframe_type = mlt_keyframe_linear); mlt_rect anim_get_rect(const char *name, int position, int length = 0); mlt_animation get_animation(const char *name); Animation *get_anim(const char *name); bool is_anim(const char *name); int set(const char *name, Properties &properties); Properties *get_props(const char *name); Properties *get_props_at(int index); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltPushConsumer.cpp000664 000000 000000 00000010660 15172202314 017712 0ustar00rootroot000000 000000 /** * MltPushConsumer.cpp - MLT Wrapper * Copyright (C) 2004-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltPushConsumer.h" #include "MltFilter.h" using namespace Mlt; namespace Mlt { class PushPrivate { public: PushPrivate() {} }; } // namespace Mlt static void filter_destructor(void *arg) { Filter *filter = (Filter *) arg; delete filter; } PushConsumer::PushConsumer(Profile &profile, const char *id, const char *service) : Consumer(profile, id, service) , m_private(new PushPrivate()) { if (is_valid()) { // Set up push mode (known as put mode in mlt) set("real_time", 0); set("put_mode", 1); set("terminate_on_pause", 0); set("buffer", 0); // We might need resize and rescale filters so we'll create them now // NB: Try to use the best rescaler available here Filter *resize = new Filter(profile, "resize"); Filter *rescale = new Filter(profile, "mcrescale"); if (!rescale->is_valid()) { delete rescale; rescale = new Filter(profile, "gtkrescale"); } if (!rescale->is_valid()) { delete rescale; rescale = new Filter(profile, "rescale"); } Filter *convert = new Filter(profile, "avcolour_space"); set("filter_convert", convert, 0, filter_destructor); set("filter_resize", resize, 0, filter_destructor); set("filter_rescale", rescale, 0, filter_destructor); } } PushConsumer::~PushConsumer() { delete m_private; } void PushConsumer::set_render(int width, int height, double aspect_ratio) { set("render_width", width); set("render_height", height); set("render_aspect_ratio", aspect_ratio); } int PushConsumer::connect(Service & /*service*/) { return -1; } int PushConsumer::push(Frame *frame) { frame->inc_ref(); // Here we have the option to process the frame at a render resolution (this will // typically be PAL or NTSC) prior to scaling according to the consumers profile // This is done to optimise quality, esp. with regard to compositing positions if (get_int("render_width")) { // Process the projects render resolution first mlt_image_format format = mlt_image_yuv422; int w = get_int("render_width"); int h = get_int("render_height"); frame->set("consumer_aspect_ratio", get_double("render_aspect_ratio")); frame->set("consumer.progressive", get_int("progressive") | get_int("deinterlace")); frame->set("consumer.deinterlacer", get("deinterlacer") ? get("deinterlacer") : get("deinterlace_method")); frame->set("consumer.rescale", get("rescale")); // Render the frame frame->get_image(format, w, h); // Now set up the post image scaling Filter *convert = (Filter *) get_data("filter_convert"); mlt_filter_process(convert->get_filter(), frame->get_frame()); Filter *rescale = (Filter *) get_data("filter_rescale"); mlt_filter_process(rescale->get_filter(), frame->get_frame()); Filter *resize = (Filter *) get_data("filter_resize"); mlt_filter_process(resize->get_filter(), frame->get_frame()); } return mlt_consumer_put_frame((mlt_consumer) get_service(), frame->get_frame()); } int PushConsumer::push(Frame &frame) { return push(&frame); } int PushConsumer::drain() { return 0; } // Convenience function - generates a frame with an image of a given size Frame *PushConsumer::construct(int size) { mlt_frame f = mlt_frame_init(get_service()); Frame *frame = new Frame(f); uint8_t *buffer = (uint8_t *) mlt_pool_alloc(size); frame->set("image", buffer, size, mlt_pool_release); mlt_frame_close(f); return frame; } mlt-7.38.0/src/mlt++/MltPushConsumer.h000664 000000 000000 00000003616 15172202314 017362 0ustar00rootroot000000 000000 /** * MltPushConsumer.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PUSH_CONSUMER_H #define MLTPP_PUSH_CONSUMER_H #include "MltConfig.h" #include "MltConsumer.h" namespace Mlt { class Frame; class Service; class PushPrivate; class Profile; /** \brief A Consumer that accepts frames pushed in from external code. * * Unlike a pull-based Consumer (which drives its own render loop), * PushConsumer receives frames via push(). This is useful for integrating * MLT into an external render loop or for feeding decoded frames from a * third-party source. * * \extends Consumer */ class MLTPP_DECLSPEC PushConsumer : public Consumer { private: PushPrivate *m_private; public: PushConsumer(Profile &profile, const char *id, const char *service = NULL); virtual ~PushConsumer(); void set_render(int width, int height, double aspect_ratio); virtual int connect(Service &service) override; int push(Frame *frame); int push(Frame &frame); int drain(); /** Construct a blank frame with an allocated pixel buffer of \p size bytes. * Caller owns the result. */ Frame *construct(int); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltRepository.cpp000664 000000 000000 00000005465 15172202314 017445 0ustar00rootroot000000 000000 /** * MltRepository.cpp - MLT Wrapper * Copyright (C) 2008-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltRepository.h" #include "MltProfile.h" #include "MltProperties.h" using namespace Mlt; Repository::Repository(const char *directory) : instance(NULL) { instance = mlt_repository_init(directory); } Repository::Repository(mlt_repository repository) : instance(repository) {} Repository::~Repository() { instance = NULL; } void Repository::register_service(mlt_service_type service_type, const char *service, mlt_register_callback symbol) { mlt_repository_register(instance, service_type, service, symbol); } void *Repository::create(Profile &profile, mlt_service_type type, const char *service, void *arg) { return mlt_repository_create(instance, profile.get_profile(), type, service, arg); } Properties *Repository::consumers() const { return new Properties(mlt_repository_consumers(instance)); } Properties *Repository::filters() const { return new Properties(mlt_repository_filters(instance)); } Properties *Repository::links() const { return new Properties(mlt_repository_links(instance)); } Properties *Repository::producers() const { return new Properties(mlt_repository_producers(instance)); } Properties *Repository::transitions() const { return new Properties(mlt_repository_transitions(instance)); } void Repository::register_metadata(mlt_service_type type, const char *service, mlt_metadata_callback callback, void *callback_data) { mlt_repository_register_metadata(instance, type, service, callback, callback_data); } Properties *Repository::metadata(mlt_service_type type, const char *service) const { return new Properties(mlt_repository_metadata(instance, type, service)); } Properties *Repository::languages() const { return new Properties(mlt_repository_languages(instance)); } Properties *Repository::presets() { return new Properties(mlt_repository_presets()); } mlt-7.38.0/src/mlt++/MltRepository.h000664 000000 000000 00000005561 15172202314 017107 0ustar00rootroot000000 000000 /** * MltRepository.h - MLT Wrapper * Copyright (C) 2008-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_REPOSITORY_H #define MLTPP_REPOSITORY_H #include "MltConfig.h" #ifdef SWIG #define MLTPP_DECLSPEC #endif #include namespace Mlt { class Profile; class Properties; /** \brief C++ wrapper for ::mlt_repository — the plugin service registry. * * The repository is initialised once via Factory::init() and holds all * registered producer, filter, transition, and consumer services. It provides * metadata queries and service instantiation. * * \see mlt_repository_s */ class MLTPP_DECLSPEC Repository { private: mlt_repository instance; Repository() {} public: Repository(const char *directory); Repository(mlt_repository repository); ~Repository(); void register_service(mlt_service_type service_type, const char *service, mlt_register_callback symbol); void *create(Profile &profile, mlt_service_type type, const char *service, void *arg); /** Return registered consumer service names. Caller owns the result. */ Properties *consumers() const; /** Return registered filter service names. Caller owns the result. */ Properties *filters() const; /** Return registered link service names. Caller owns the result. */ Properties *links() const; /** Return registered producer service names. Caller owns the result. */ Properties *producers() const; /** Return registered transition service names. Caller owns the result. */ Properties *transitions() const; void register_metadata(mlt_service_type type, const char *service, mlt_metadata_callback, void *callback_data); /** Return the metadata Properties for \p service of \p type. Caller owns the result. */ Properties *metadata(mlt_service_type type, const char *service) const; /** Return available languages. Caller owns the result. */ Properties *languages() const; /** Return preset names. Caller owns the result. */ static Properties *presets(); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltService.cpp000664 000000 000000 00000007747 15172202314 016673 0ustar00rootroot000000 000000 /** * MltService.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltService.h" #include "MltFilter.h" #include "MltProfile.h" #include using namespace Mlt; Service::Service() : Properties(false) , instance(NULL) {} Service::Service(Service &service) : Properties(false) , instance(service.get_service()) { inc_ref(); } Service::Service(const Service &service) : Service(const_cast(service)) {} Service::Service(mlt_service service) : Properties(false) , instance(service) { inc_ref(); } Service::Service(Service *service) : Service(service ? service->get_service() : nullptr) {} Service::~Service() { mlt_service_close(instance); } Service &Service::operator=(const Service &service) { if (this != &service) { mlt_service_close(instance); instance = service.instance; inc_ref(); } return *this; } mlt_service Service::get_service() { return instance; } mlt_properties Service::get_properties() { return mlt_service_properties(get_service()); } void Service::lock() { mlt_service_lock(get_service()); } void Service::unlock() { mlt_service_unlock(get_service()); } int Service::connect_producer(Service &producer, int index) { return mlt_service_connect_producer(get_service(), producer.get_service(), index); } int Mlt::Service::insert_producer(Mlt::Service &producer, int index) { return mlt_service_insert_producer(get_service(), producer.get_service(), index); } int Service::disconnect_producer(int index) { return mlt_service_disconnect_producer(get_service(), index); } int Service::disconnect_all_producers() { return mlt_service_disconnect_all_producers(get_service()); } Service *Service::producer() { return new Service(mlt_service_producer(get_service())); } void Service::set_consumer(Service &service) { mlt_service_set_consumer(get_service(), service.get_service()); } Service *Service::consumer() { return new Service(mlt_service_consumer(get_service())); } Profile *Service::profile() { return new Profile(mlt_profile_clone(mlt_service_profile(get_service()))); } mlt_profile Service::get_profile() { return mlt_service_profile(get_service()); } Frame *Service::get_frame(int index) { mlt_frame frame = NULL; mlt_service_get_frame(get_service(), &frame, index); Frame *result = new Frame(frame); mlt_frame_close(frame); return result; } mlt_service_type Service::type() { return mlt_service_identify(get_service()); } int Service::attach(Filter &filter) { return mlt_service_attach(get_service(), filter.get_filter()); } int Service::detach(Filter &filter) { return mlt_service_detach(get_service(), filter.get_filter()); } int Service::filter_count() { return mlt_service_filter_count(get_service()); } int Service::move_filter(int from, int to) { return mlt_service_move_filter(get_service(), from, to); } Filter *Service::filter(int index) { mlt_filter result = mlt_service_filter(get_service(), index); return result == NULL ? NULL : new Filter(result); } void Service::set_profile(mlt_profile profile) { mlt_service_set_profile(get_service(), profile); } void Service::set_profile(Profile &profile) { set_profile(profile.get_profile()); } mlt-7.38.0/src/mlt++/MltService.h000664 000000 000000 00000007042 15172202314 016324 0ustar00rootroot000000 000000 /** * MltService.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_SERVICE_H #define MLTPP_SERVICE_H #include "MltConfig.h" #include #include "MltFrame.h" #include "MltProperties.h" namespace Mlt { class Properties; class Filter; class Frame; class Profile; /** \brief C++ wrapper for ::mlt_service — abstract base for all MLT services. * * A service can have multiple input connections (producers) and a single * output connection (consumer). Services that sit between producers and a * consumer modify data and are called filters. Any service can have zero or * more filters attached to it. * * \extends Properties * \see mlt_service_s */ class MLTPP_DECLSPEC Service : public Properties { private: mlt_service instance; public: Service(); Service(Service &service); Service(const Service &service); /** Wrap an existing ::mlt_service handle. */ Service(mlt_service service); Service(Service *service); virtual ~Service(); Service &operator=(const Service &service); virtual mlt_service get_service(); /** Acquire the service mutex (use around property reads in render threads). */ void lock(); /** Release the service mutex. */ void unlock(); virtual mlt_properties get_properties() override; /** Connect \p producer as the input at \p index. */ int connect_producer(Service &producer, int index = 0); /** Insert \p producer at \p index, shifting existing connections up. */ int insert_producer(Service &producer, int index = 0); /** Disconnect the producer at \p index. */ int disconnect_producer(int index = 0); /** Disconnect all producer inputs. */ int disconnect_all_producers(); void set_consumer(Service &service); /** Return the downstream consumer service, or null. */ Service *consumer(); /** Return the first upstream producer, or null. */ Service *producer(); /** Return the profile associated with this service. */ Profile *profile(); mlt_profile get_profile(); /** Pull and return frame \p index from this service. Caller owns the result. */ Frame *get_frame(int index = 0); /** Return the service sub-type (producer, filter, consumer, transition, etc.). */ mlt_service_type type(); /** Attach \p filter to this service. */ int attach(Filter &filter); /** Detach \p filter from this service. */ int detach(Filter &filter); /** Return the number of attached filters. */ int filter_count(); /** Move the attached filter at position \p from to position \p to. */ int move_filter(int from, int to); /** Return the attached filter at \p index. Caller does not own the result. */ Filter *filter(int index); void set_profile(mlt_profile profile); void set_profile(Profile &profile); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltTokeniser.cpp000664 000000 000000 00000002747 15172202314 017231 0ustar00rootroot000000 000000 /** * MltTokeniser.cpp - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltTokeniser.h" #include using namespace Mlt; Tokeniser::Tokeniser(char *text, char *delimiter) { tokens = mlt_tokeniser_init(); if (text != NULL) mlt_tokeniser_parse_new(tokens, text, delimiter ? delimiter : " "); } Tokeniser::~Tokeniser() { mlt_tokeniser_close(tokens); } int Tokeniser::parse(char *text, char *delimiter) { return mlt_tokeniser_parse_new(tokens, text, delimiter ? delimiter : " "); } int Tokeniser::count() { return mlt_tokeniser_count(tokens); } char *Tokeniser::get(int index) { return mlt_tokeniser_get_string(tokens, index); } char *Tokeniser::input() { return mlt_tokeniser_get_input(tokens); } mlt-7.38.0/src/mlt++/MltTokeniser.h000664 000000 000000 00000003012 15172202314 016660 0ustar00rootroot000000 000000 /** * MltTokeniser.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_TOKENISER_H #define MLTPP_TOKENISER_H #include "MltConfig.h" #include namespace Mlt { /** \brief C++ wrapper for ::mlt_tokeniser — splits a string by a delimiter. * * A simple string tokeniser used by the MLT framework to parse * delimited text (e.g. property lists). Call parse() to split a string, * then iterate the results with count() and get(). * * \see mlt_tokeniser_s */ class MLTPP_DECLSPEC Tokeniser { private: mlt_tokeniser tokens; public: Tokeniser(char *text = NULL, char *delimiter = NULL); ~Tokeniser(); int parse(char *text, char *delimiter = NULL); int count(); char *get(int index); char *input(); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltTractor.cpp000664 000000 000000 00000011357 15172202314 016701 0ustar00rootroot000000 000000 /** * MltTractor.cpp - Tractor wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltTractor.h" #include "MltField.h" #include "MltFilter.h" #include "MltMultitrack.h" #include "MltPlaylist.h" #include "MltProfile.h" #include "MltTransition.h" using namespace Mlt; Tractor::Tractor() : instance(mlt_tractor_new()) {} Tractor::Tractor(Profile &profile) : instance(mlt_tractor_new()) { set_profile(profile); } Tractor::Tractor(Service &tractor) : instance(NULL) { if (tractor.type() == mlt_service_tractor_type) { instance = (mlt_tractor) tractor.get_service(); inc_ref(); } } Tractor::Tractor(mlt_tractor tractor) : instance(tractor) { inc_ref(); } Tractor::Tractor(Tractor &tractor) : Mlt::Producer(tractor) , instance(tractor.get_tractor()) { inc_ref(); } Tractor::Tractor(Profile &profile, char *id, char *resource) : Tractor(profile.get_profile(), id, resource) {} Tractor::Tractor(mlt_profile profile, char *id, char *resource) : instance(NULL) { Producer producer(profile, id, resource); if (producer.is_valid() && producer.type() == mlt_service_tractor_type) { instance = (mlt_tractor) producer.get_producer(); inc_ref(); } else if (producer.is_valid()) { instance = mlt_tractor_new(); set_profile(profile); set_track(producer, 0); } } Tractor::~Tractor() { mlt_tractor_close(instance); } mlt_tractor Tractor::get_tractor() { return instance; } mlt_producer Tractor::get_producer() { return mlt_tractor_producer(get_tractor()); } Multitrack *Tractor::multitrack() { return new Multitrack(mlt_tractor_multitrack(get_tractor())); } Field *Tractor::field() { return new Field(mlt_tractor_field(get_tractor())); } void Tractor::refresh() { return mlt_tractor_refresh(get_tractor()); } int Tractor::set_track(Producer &producer, int index) { return mlt_tractor_set_track(get_tractor(), producer.get_producer(), index); } int Tractor::insert_track(Producer &producer, int index) { return mlt_tractor_insert_track(get_tractor(), producer.get_producer(), index); } int Tractor::remove_track(int index) { return mlt_tractor_remove_track(get_tractor(), index); } Producer *Tractor::track(int index) { mlt_producer producer = mlt_tractor_get_track(get_tractor(), index); return producer != NULL ? new Producer(producer) : NULL; } int Tractor::count() { return mlt_multitrack_count(mlt_tractor_multitrack(get_tractor())); } void Tractor::plant_transition(Transition &transition, int a_track, int b_track) { mlt_field_plant_transition(mlt_tractor_field(get_tractor()), transition.get_transition(), a_track, b_track); } void Tractor::plant_transition(Transition *transition, int a_track, int b_track) { if (transition != NULL) mlt_field_plant_transition(mlt_tractor_field(get_tractor()), transition->get_transition(), a_track, b_track); } void Tractor::plant_filter(Filter &filter, int track) { mlt_field_plant_filter(mlt_tractor_field(get_tractor()), filter.get_filter(), track); } void Tractor::plant_filter(Filter *filter, int track) { mlt_field_plant_filter(mlt_tractor_field(get_tractor()), filter->get_filter(), track); } bool Tractor::locate_cut(Producer *producer, int &track, int &cut) { bool found = false; for (track = 0; producer != NULL && !found && track < count(); track++) { Playlist playlist((mlt_playlist) mlt_tractor_get_track(get_tractor(), track)); for (cut = 0; !found && cut < playlist.count(); cut++) { Producer *clip = playlist.get_clip(cut); found = producer->get_producer() == clip->get_producer(); delete clip; } } track--; cut--; return found; } int Tractor::connect(Producer &producer) { return mlt_tractor_connect(get_tractor(), producer.get_service()); } mlt-7.38.0/src/mlt++/MltTractor.h000664 000000 000000 00000006323 15172202314 016343 0ustar00rootroot000000 000000 /** * MltTractor.h - Tractor wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_TRACTOR_H #define MLTPP_TRACTOR_H #include "MltConfig.h" #include #include "MltProducer.h" namespace Mlt { class Producer; class Field; class Multitrack; class Transition; class Filter; class Profile; /** \brief C++ wrapper for ::mlt_tractor — composites multiple tracks. * * A tractor owns a Multitrack (parallel track container) and a Field * (ordered list of transitions and filters). Together they form the * compositing graph for multi-track editing. * * \extends Producer * \see mlt_tractor_s */ class MLTPP_DECLSPEC Tractor : public Producer { private: mlt_tractor instance; public: Tractor(); Tractor(Profile &profile); Tractor(Service &tractor); /** Wrap an existing ::mlt_tractor handle. */ Tractor(mlt_tractor tractor); Tractor(Tractor &tractor); Tractor(Profile &profile, char *id, char *arg = NULL); Tractor(mlt_profile profile, char *id, char *arg = NULL); virtual ~Tractor(); virtual mlt_tractor get_tractor(); mlt_producer get_producer() override; /** Return the underlying Multitrack. Caller does not own the result. */ Multitrack *multitrack(); /** Return the Field managing transitions and filters. Caller does not own the result. */ Field *field(); /** Recalculate the tractor's length from its tracks. */ void refresh(); /** Assign \p producer to track \p index (replaces any existing track). */ int set_track(Producer &producer, int index); /** Insert \p producer at \p index, shifting higher tracks up. */ int insert_track(Producer &producer, int index); /** Remove the track at \p index. */ int remove_track(int index); /** Return the producer on track \p index. Caller does not own the result. */ Producer *track(int index); /** Return the number of tracks. */ int count(); /** Plant \p transition between tracks \p a_track and \p b_track. */ void plant_transition(Transition &transition, int a_track = 0, int b_track = 1); void plant_transition(Transition *transition, int a_track = 0, int b_track = 1); /** Attach \p filter to track \p track. */ void plant_filter(Filter &filter, int track = 0); void plant_filter(Filter *filter, int track = 0); /** Find which track and cut position \p producer occupies. */ bool locate_cut(Producer *producer, int &track, int &cut); int connect(Producer &producer); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/MltTransition.cpp000664 000000 000000 00000010047 15172202314 017410 0ustar00rootroot000000 000000 /** * MltTransition.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltTransition.h" #include "MltProducer.h" #include "MltProfile.h" #include #include using namespace Mlt; Transition::Transition() : Service() , instance(nullptr) {} Transition::Transition(Profile &profile, const char *id, const char *arg) : Transition(profile.get_profile(), id, arg) {} Transition::Transition(mlt_profile profile, const char *id, const char *arg) : instance(NULL) { if (arg != NULL) { instance = mlt_factory_transition(profile, id, arg); } else { if (strchr(id, ':')) { char *temp = strdup(id); char *arg = strchr(temp, ':') + 1; *(arg - 1) = '\0'; instance = mlt_factory_transition(profile, temp, arg); free(temp); } else { instance = mlt_factory_transition(profile, id, NULL); } } } Transition::Transition(Service &transition) : instance(NULL) { if (transition.type() == mlt_service_transition_type) { instance = (mlt_transition) transition.get_service(); inc_ref(); } } Transition::Transition(Transition &transition) : Mlt::Service(transition) , instance(transition.get_transition()) { inc_ref(); } Transition::Transition(const Transition &transition) : Transition(const_cast(transition)) {} Transition::Transition(mlt_transition transition) : instance(transition) { inc_ref(); } Transition::~Transition() { mlt_transition_close(instance); } Transition &Transition::operator=(const Transition &transition) { if (this != &transition) { mlt_transition_close(instance); instance = transition.instance; inc_ref(); } return *this; } mlt_transition Transition::get_transition() { return instance; } mlt_service Transition::get_service() { return mlt_transition_service(get_transition()); } void Transition::set_in_and_out(int in, int out) { mlt_transition_set_in_and_out(get_transition(), in, out); } void Transition::set_tracks(int a_track, int b_track) { mlt_transition_set_tracks(get_transition(), a_track, b_track); } int Transition::connect(Producer &producer, int a_track, int b_track) { return mlt_transition_connect(get_transition(), producer.get_service(), a_track, b_track); } int Transition::connect(Service &service, int a_track, int b_track) { return mlt_transition_connect(get_transition(), service.get_service(), a_track, b_track); } int Transition::get_a_track() { return mlt_transition_get_a_track(get_transition()); } int Transition::get_b_track() { return mlt_transition_get_b_track(get_transition()); } int Transition::get_in() { return mlt_transition_get_in(get_transition()); } int Transition::get_out() { return mlt_transition_get_out(get_transition()); } int Transition::get_length() { return mlt_transition_get_length(get_transition()); } int Transition::get_position(Frame &frame) { return mlt_transition_get_position(get_transition(), frame.get_frame()); } double Transition::get_progress(Frame &frame) { return mlt_transition_get_progress(get_transition(), frame.get_frame()); } double Transition::get_progress_delta(Frame &frame) { return mlt_transition_get_progress_delta(get_transition(), frame.get_frame()); } mlt-7.38.0/src/mlt++/MltTransition.h000664 000000 000000 00000006045 15172202314 017060 0ustar00rootroot000000 000000 /** * MltTransition.h - MLT Wrapper * Copyright (C) 2004-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_TRANSITION_H #define MLTPP_TRANSITION_H #include "MltConfig.h" #include "MltService.h" #include namespace Mlt { class Service; class Profile; class Frame; class Chain; class Link; /** \brief C++ wrapper for ::mlt_transition — blends two tracks together. * * A transition composites the B-track over the A-track for a specified frame * range and track pair. It is planted in a Tractor's Field via * Tractor::plant_transition(). * * \extends Service * \see mlt_transition_s */ class MLTPP_DECLSPEC Transition : public Service { private: mlt_transition instance; public: Transition(); /** Construct and instantiate transition \p id from the repository. */ Transition(Profile &profile, const char *id, const char *arg = NULL); Transition(mlt_profile profile, const char *id, const char *arg = NULL); Transition(Service &transition); Transition(Transition &transition); Transition(const Transition &transition); /** Wrap an existing ::mlt_transition handle. */ Transition(mlt_transition transition); virtual ~Transition(); Transition &operator=(const Transition &transition); virtual mlt_transition get_transition(); mlt_service get_service() override; /** Restrict this transition to the frame range [\p in, \p out]. */ void set_in_and_out(int in, int out); /** Set which tracks this transition blends (A = lower, B = upper). */ void set_tracks(int a_track, int b_track); int connect(Producer &producer, int a_track, int b_track); int connect(Service &service, int a_track, int b_track); /** Return the A-track index. */ int get_a_track(); /** Return the B-track index. */ int get_b_track(); /** Return the in-point frame number. */ int get_in(); /** Return the out-point frame number. */ int get_out(); /** Return the duration (out - in + 1). */ int get_length(); /** Return the current position of \p frame relative to the transition's in-point. */ int get_position(Frame &frame); /** Return the playback progress in the range [0.0, 1.0]. */ double get_progress(Frame &frame); /** Return the per-frame progress delta. */ double get_progress_delta(Frame &frame); }; } // namespace Mlt #endif mlt-7.38.0/src/mlt++/mlt++.pc.in000664 000000 000000 00000000632 15172202314 015747 0ustar00rootroot000000 000000 prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ datadir=@CMAKE_INSTALL_FULL_DATADIR@ Name: mlt++ Description: C++ API for MLT multimedia framework Version: @MLT_VERSION@ Requires: mlt-framework-@MLT_VERSION_MAJOR@ Libs: -L${libdir} -lmlt++-@MLT_VERSION_MAJOR@ Cflags: -I${includedir}/mlt-@MLT_VERSION_MAJOR@/mlt++ mlt-7.38.0/src/mlt++/mlt++.vers000664 000000 000000 00000070172 15172202314 015725 0ustar00rootroot000000 000000 MLTPP_0.8.8 { global: extern "C++" { "Mlt::ClipInfo::~ClipInfo()"; "Mlt::ClipInfo::ClipInfo()"; "Mlt::ClipInfo::ClipInfo(mlt_playlist_clip_info*)"; "Mlt::ClipInfo::update(mlt_playlist_clip_info*)"; "typeinfo for Mlt::Consumer"; "typeinfo name for Mlt::Consumer"; "vtable for Mlt::Consumer"; "Mlt::Consumer::connect(Mlt::Service&)"; "Mlt::Consumer::~Consumer()"; "Mlt::Consumer::Consumer()"; "Mlt::Consumer::Consumer(Mlt::Consumer&)"; "Mlt::Consumer::Consumer(mlt_consumer_s*)"; "Mlt::Consumer::Consumer(Mlt::Profile&)"; "Mlt::Consumer::Consumer(Mlt::Profile&, char const*, char const*)"; "Mlt::Consumer::Consumer(Mlt::Service&)"; "Mlt::Consumer::get_consumer()"; "Mlt::Consumer::get_service()"; "Mlt::Consumer::is_stopped()"; "Mlt::Consumer::position()"; "Mlt::Consumer::purge()"; "Mlt::Consumer::run()"; "Mlt::Consumer::start()"; "Mlt::Consumer::stop()"; "Mlt::Deque::count()"; "Mlt::Deque::~Deque()"; "Mlt::Deque::Deque()"; "Mlt::Deque::peek_back()"; "Mlt::Deque::peek_front()"; "Mlt::Deque::pop_back()"; "Mlt::Deque::pop_front()"; "Mlt::Deque::push_back(void*)"; "Mlt::Deque::push_front(void*)"; "Mlt::Event::block()"; "Mlt::Event::~Event()"; "Mlt::Event::Event(Mlt::Event&)"; "Mlt::Event::Event(mlt_event_struct*)"; "Mlt::Event::get_event()"; "Mlt::Event::is_valid()"; "Mlt::Event::unblock()"; "Mlt::Factory::close()"; "Mlt::Factory::consumer(Mlt::Profile&, char*, char*)"; "Mlt::Factory::event_object()"; "Mlt::Factory::filter(Mlt::Profile&, char*, char*)"; "Mlt::Factory::init(char const*)"; "Mlt::Factory::producer(Mlt::Profile&, char*, char*)"; "Mlt::Factory::transition(Mlt::Profile&, char*, char*)"; "typeinfo for Mlt::Field"; "typeinfo name for Mlt::Field"; "vtable for Mlt::Field"; "Mlt::Field::disconnect_service(Mlt::Service&)"; "Mlt::Field::~Field()"; "Mlt::Field::Field(Mlt::Field&)"; "Mlt::Field::Field(mlt_field_s*)"; "Mlt::Field::get_field()"; "Mlt::Field::get_service()"; "Mlt::Field::plant_filter(Mlt::Filter&, int)"; "Mlt::Field::plant_transition(Mlt::Transition&, int, int)"; "typeinfo for Mlt::FilteredConsumer"; "typeinfo name for Mlt::FilteredConsumer"; "vtable for Mlt::FilteredConsumer"; "Mlt::FilteredConsumer::attach(Mlt::Filter&)"; "Mlt::FilteredConsumer::connect(Mlt::Service&)"; "Mlt::FilteredConsumer::detach(Mlt::Filter&)"; "Mlt::FilteredConsumer::~FilteredConsumer()"; "Mlt::FilteredConsumer::FilteredConsumer(Mlt::Consumer&)"; "Mlt::FilteredConsumer::FilteredConsumer(Mlt::Profile&, char const*, char const*)"; "Mlt::FilteredConsumer::last(Mlt::Filter&)"; "typeinfo for Mlt::FilteredProducer"; "typeinfo name for Mlt::FilteredProducer"; "vtable for Mlt::FilteredProducer"; "Mlt::FilteredProducer::attach(Mlt::Filter&)"; "Mlt::FilteredProducer::detach(Mlt::Filter&)"; "Mlt::FilteredProducer::~FilteredProducer()"; "Mlt::FilteredProducer::FilteredProducer(Mlt::Profile&, char const*, char const*)"; "typeinfo for Mlt::Filter"; "typeinfo name for Mlt::Filter"; "vtable for Mlt::Filter"; "Mlt::Filter::connect(Mlt::Service&, int)"; "Mlt::Filter::~Filter()"; "Mlt::Filter::Filter(Mlt::Filter&)"; "Mlt::Filter::Filter(mlt_filter_s*)"; "Mlt::Filter::Filter(Mlt::Profile&, char const*, char const*)"; "Mlt::Filter::Filter(Mlt::Service&)"; "Mlt::Filter::get_filter()"; "Mlt::Filter::get_in()"; "Mlt::Filter::get_length()"; "Mlt::Filter::get_length2(Mlt::Frame&)"; "Mlt::Filter::get_out()"; "Mlt::Filter::get_position(Mlt::Frame&)"; "Mlt::Filter::get_progress(Mlt::Frame&)"; "Mlt::Filter::get_service()"; "Mlt::Filter::get_track()"; "Mlt::Filter::set_in_and_out(int, int)"; "typeinfo for Mlt::Frame"; "typeinfo name for Mlt::Frame"; "vtable for Mlt::Frame"; "Mlt::Frame::fetch_image(mlt_image_format, int, int, int)"; "Mlt::Frame::~Frame()"; "Mlt::Frame::Frame(Mlt::Frame&)"; "Mlt::Frame::Frame(mlt_frame_s*)"; "Mlt::Frame::get_audio(mlt_audio_format&, int&, int&, int&)"; "Mlt::Frame::get_frame()"; "Mlt::Frame::get_image(mlt_image_format&, int&, int&, int)"; "Mlt::Frame::get_original_producer()"; "Mlt::Frame::get_position()"; "Mlt::Frame::get_properties()"; "Mlt::Frame::get_unique_properties(Mlt::Service&)"; "Mlt::Frame::get_waveform(int, int)"; "Mlt::Frame::set_alpha(unsigned char*, int, void (*)(void*))"; "Mlt::Frame::set_image(unsigned char*, int, void (*)(void*))"; "typeinfo for Mlt::Multitrack"; "typeinfo name for Mlt::Multitrack"; "vtable for Mlt::Multitrack"; "Mlt::Multitrack::clip(mlt_whence, int)"; "Mlt::Multitrack::connect(Mlt::Producer&, int)"; "Mlt::Multitrack::count()"; "Mlt::Multitrack::get_multitrack()"; "Mlt::Multitrack::get_producer()"; "Mlt::Multitrack::~Multitrack()"; "Mlt::Multitrack::Multitrack(Mlt::Multitrack&)"; "Mlt::Multitrack::Multitrack(mlt_multitrack_s*)"; "Mlt::Multitrack::Multitrack(Mlt::Service&)"; "Mlt::Multitrack::refresh()"; "Mlt::Multitrack::track(int)"; "typeinfo for Mlt::Parser"; "typeinfo name for Mlt::Parser"; "vtable for Mlt::Parser"; "Mlt::Parser::get_properties()"; "Mlt::Parser::on_end_filter(Mlt::Filter*)"; "Mlt::Parser::on_end_multitrack(Mlt::Multitrack*)"; "Mlt::Parser::on_end_playlist(Mlt::Playlist*)"; "Mlt::Parser::on_end_producer(Mlt::Producer*)"; "Mlt::Parser::on_end_track()"; "Mlt::Parser::on_end_tractor(Mlt::Tractor*)"; "Mlt::Parser::on_end_transition(Mlt::Transition*)"; "Mlt::Parser::on_invalid(Mlt::Service*)"; "Mlt::Parser::on_start_filter(Mlt::Filter*)"; "Mlt::Parser::on_start_multitrack(Mlt::Multitrack*)"; "Mlt::Parser::on_start_playlist(Mlt::Playlist*)"; "Mlt::Parser::on_start_producer(Mlt::Producer*)"; "Mlt::Parser::on_start_track()"; "Mlt::Parser::on_start_tractor(Mlt::Tractor*)"; "Mlt::Parser::on_start_transition(Mlt::Transition*)"; "Mlt::Parser::on_unknown(Mlt::Service*)"; "Mlt::Parser::~Parser()"; "Mlt::Parser::Parser()"; "Mlt::Parser::start(Mlt::Service&)"; "typeinfo for Mlt::Playlist"; "typeinfo name for Mlt::Playlist"; "vtable for Mlt::Playlist"; "Mlt::Playlist::append(Mlt::Producer&, int, int)"; "Mlt::Playlist::blank(char const*)"; "Mlt::Playlist::blank(int)"; "Mlt::Playlist::blanks_from(int, int)"; "Mlt::Playlist::clear()"; "Mlt::Playlist::clip_info(int, Mlt::ClipInfo*)"; "Mlt::Playlist::clip_length(int)"; "Mlt::Playlist::clip(mlt_whence, int)"; "Mlt::Playlist::clip_start(int)"; "Mlt::Playlist::consolidate_blanks(int)"; "Mlt::Playlist::count()"; "Mlt::Playlist::current()"; "Mlt::Playlist::current_clip()"; "Mlt::Playlist::delete_clip_info(Mlt::ClipInfo*)"; "Mlt::Playlist::get_clip_at(int)"; "Mlt::Playlist::get_clip_index_at(int)"; "Mlt::Playlist::get_clip(int)"; "Mlt::Playlist::get_playlist()"; "Mlt::Playlist::get_producer()"; "Mlt::Playlist::insert_at(int, Mlt::Producer*, int)"; "Mlt::Playlist::insert_at(int, Mlt::Producer&, int)"; "Mlt::Playlist::insert_blank(int, int)"; "Mlt::Playlist::insert(Mlt::Producer&, int, int, int)"; "Mlt::Playlist::is_blank_at(int)"; "Mlt::Playlist::is_blank(int)"; "Mlt::Playlist::is_mix(int)"; "Mlt::Playlist::join(int, int, int)"; "Mlt::Playlist::mix_add(int, Mlt::Transition*)"; "Mlt::Playlist::mix(int, int, Mlt::Transition*)"; "Mlt::Playlist::move(int, int)"; "Mlt::Playlist::pad_blanks(int, int, int)"; "Mlt::Playlist::~Playlist()"; "Mlt::Playlist::Playlist()"; "Mlt::Playlist::Playlist(Mlt::Playlist&)"; "Mlt::Playlist::Playlist(mlt_playlist_s*)"; "Mlt::Playlist::Playlist(Mlt::Profile&)"; "Mlt::Playlist::Playlist(Mlt::Service&)"; "Mlt::Playlist::remove(int)"; "Mlt::Playlist::remove_region(int, int)"; "Mlt::Playlist::repeat(int, int)"; "Mlt::Playlist::replace_with_blank(int)"; "Mlt::Playlist::resize_clip(int, int, int)"; "Mlt::Playlist::split_at(int, bool)"; "Mlt::Playlist::split(int, int)"; "typeinfo for Mlt::Producer"; "typeinfo name for Mlt::Producer"; "vtable for Mlt::Producer"; "Mlt::Producer::clear()"; "Mlt::Producer::cut(int, int)"; "Mlt::Producer::frame()"; "Mlt::Producer::frame_time(mlt_time_format)"; "Mlt::Producer::get_fps()"; "Mlt::Producer::get_in()"; "Mlt::Producer::get_length()"; "Mlt::Producer::get_length_time(mlt_time_format)"; "Mlt::Producer::get_out()"; "Mlt::Producer::get_parent()"; "Mlt::Producer::get_playtime()"; "Mlt::Producer::get_producer()"; "Mlt::Producer::get_service()"; "Mlt::Producer::get_speed()"; "Mlt::Producer::is_blank()"; "Mlt::Producer::is_cut()"; "Mlt::Producer::optimise()"; "Mlt::Producer::parent()"; "Mlt::Producer::pause()"; "Mlt::Producer::position()"; "Mlt::Producer::~Producer()"; "Mlt::Producer::Producer()"; "Mlt::Producer::Producer(Mlt::Producer*)"; "Mlt::Producer::Producer(Mlt::Producer&)"; "Mlt::Producer::Producer(mlt_producer_s*)"; "Mlt::Producer::Producer(Mlt::Profile&, char const*, char const*)"; "Mlt::Producer::Producer(Mlt::Service&)"; "Mlt::Producer::runs_into(Mlt::Producer&)"; "Mlt::Producer::same_clip(Mlt::Producer&)"; "Mlt::Producer::seek(char const*)"; "Mlt::Producer::seek(int)"; "Mlt::Producer::set_in_and_out(int, int)"; "Mlt::Producer::set_speed(double)"; "Mlt::Profile::colorspace() const"; "Mlt::Profile::dar() const"; "Mlt::Profile::description() const"; "Mlt::Profile::display_aspect_den() const"; "Mlt::Profile::display_aspect_num() const"; "Mlt::Profile::fps() const"; "Mlt::Profile::frame_rate_den() const"; "Mlt::Profile::frame_rate_num() const"; "Mlt::Profile::from_producer(Mlt::Producer&)"; "Mlt::Profile::get_profile() const"; "Mlt::Profile::height() const"; "Mlt::Profile::is_explicit() const"; "Mlt::Profile::list()"; "Mlt::Profile::~Profile()"; "Mlt::Profile::Profile()"; "Mlt::Profile::Profile(char const*)"; "Mlt::Profile::Profile(mlt_profile_s*)"; "Mlt::Profile::Profile(Mlt::Properties&)"; "Mlt::Profile::progressive() const"; "Mlt::Profile::sample_aspect_den() const"; "Mlt::Profile::sample_aspect_num() const"; "Mlt::Profile::sar() const"; "Mlt::Profile::set_colorspace(int)"; "Mlt::Profile::set_explicit(int)"; "Mlt::Profile::set_frame_rate(int, int)"; "Mlt::Profile::set_height(int)"; "Mlt::Profile::set_progressive(int)"; "Mlt::Profile::set_sample_aspect(int, int)"; "Mlt::Profile::set_width(int)"; "Mlt::Profile::width() const"; "typeinfo for Mlt::Properties"; "typeinfo name for Mlt::Properties"; "vtable for Mlt::Properties"; "Mlt::Properties::block(void*)"; "Mlt::Properties::count()"; "Mlt::Properties::debug(char const*, _IO_FILE*)"; "Mlt::Properties::dec_ref()"; "Mlt::Properties::delete_event(Mlt::Event*)"; "Mlt::Properties::dump(_IO_FILE*)"; "Mlt::Properties::fire_event(char const*)"; "Mlt::Properties::get(char const*)"; "Mlt::Properties::get_data(char const*)"; "Mlt::Properties::get_data(char const*, int&)"; "Mlt::Properties::get_data(int, int&)"; "Mlt::Properties::get_double(char const*)"; "Mlt::Properties::get(int)"; "Mlt::Properties::get_int64(char const*)"; "Mlt::Properties::get_int(char const*)"; "Mlt::Properties::get_lcnumeric()"; "Mlt::Properties::get_name(int)"; "Mlt::Properties::get_properties()"; "Mlt::Properties::get_time(char const*, mlt_time_format)"; "Mlt::Properties::inc_ref()"; "Mlt::Properties::inherit(Mlt::Properties&)"; "Mlt::Properties::is_sequence()"; "Mlt::Properties::is_valid()"; "Mlt::Properties::load(char const*)"; "Mlt::Properties::lock()"; "Mlt::Properties::mirror(Mlt::Properties&)"; "Mlt::Properties::parse(char const*)"; "Mlt::Properties::parse_yaml(char const*)"; "Mlt::Properties::pass_list(Mlt::Properties&, char const*)"; "Mlt::Properties::pass_property(Mlt::Properties&, char const*)"; "Mlt::Properties::pass_values(Mlt::Properties&, char const*)"; "Mlt::Properties::preset(char const*)"; "Mlt::Properties::~Properties()"; "Mlt::Properties::Properties()"; "Mlt::Properties::Properties(bool)"; "Mlt::Properties::Properties(char const*)"; "Mlt::Properties::Properties(Mlt::Properties&)"; "Mlt::Properties::Properties(mlt_properties_s*)"; "Mlt::Properties::Properties(void*)"; "Mlt::Properties::ref_count()"; "Mlt::Properties::rename(char const*, char const*)"; "Mlt::Properties::save(char const*)"; "Mlt::Properties::serialise_yaml()"; "Mlt::Properties::set(char const*, char const*)"; "Mlt::Properties::set(char const*, double)"; "Mlt::Properties::set(char const*, int)"; "Mlt::Properties::set(char const*, void*, int, void (*)(void*), char* (*)(void*, int))"; "Mlt::Properties::set_lcnumeric(char const*)"; "Mlt::Properties::setup_wait_for(char const*)"; "Mlt::Properties::unblock(void*)"; "Mlt::Properties::unlock()"; "Mlt::Properties::wait_for(char const*)"; "Mlt::Properties::wait_for(Mlt::Event*, bool)"; "typeinfo for Mlt::PushConsumer"; "typeinfo name for Mlt::PushConsumer"; "vtable for Mlt::PushConsumer"; "Mlt::PushConsumer::connect(Mlt::Service&)"; "Mlt::PushConsumer::construct(int)"; "Mlt::PushConsumer::drain()"; "Mlt::PushConsumer::~PushConsumer()"; "Mlt::PushConsumer::PushConsumer(Mlt::Profile&, char const*, char const*)"; "Mlt::PushConsumer::push(Mlt::Frame*)"; "Mlt::PushConsumer::push(Mlt::Frame&)"; "Mlt::PushConsumer::set_render(int, int, double)"; "Mlt::Repository::consumers() const"; "Mlt::Repository::create(Mlt::Profile&, mlt_service_type, char const*, void*)"; "Mlt::Repository::filters() const"; "Mlt::Repository::languages() const"; "Mlt::Repository::metadata(mlt_service_type, char const*) const"; "Mlt::Repository::presets()"; "Mlt::Repository::producers() const"; "Mlt::Repository::register_metadata(mlt_service_type, char const*, mlt_properties_s* (*)(mlt_service_type, char const*, void*), void*)"; "Mlt::Repository::register_service(mlt_service_type, char const*, void* (*)(mlt_profile_s*, mlt_service_type, char const*, void const*))"; "Mlt::Repository::~Repository()"; "Mlt::Repository::Repository(char const*)"; "Mlt::Repository::Repository(mlt_repository_s*)"; "Mlt::Repository::transitions() const"; "typeinfo for Mlt::Service"; "typeinfo name for Mlt::Service"; "vtable for Mlt::Service"; "Mlt::Service::attach(Mlt::Filter&)"; "Mlt::Service::connect_producer(Mlt::Service&, int)"; "Mlt::Service::consumer()"; "Mlt::Service::detach(Mlt::Filter&)"; "Mlt::Service::filter(int)"; "Mlt::Service::get_frame(int)"; "Mlt::Service::get_profile()"; "Mlt::Service::get_properties()"; "Mlt::Service::get_service()"; "Mlt::Service::lock()"; "Mlt::Service::producer()"; "Mlt::Service::profile()"; "Mlt::Service::~Service()"; "Mlt::Service::Service()"; "Mlt::Service::Service(Mlt::Service&)"; "Mlt::Service::Service(mlt_service_s*)"; "Mlt::Service::set_profile(Mlt::Profile&)"; "Mlt::Service::type()"; "Mlt::Service::unlock()"; "Mlt::Tokeniser::count()"; "Mlt::Tokeniser::get(int)"; "Mlt::Tokeniser::input()"; "Mlt::Tokeniser::parse(char*, char*)"; "Mlt::Tokeniser::~Tokeniser()"; "Mlt::Tokeniser::Tokeniser(char*, char*)"; "typeinfo for Mlt::Tractor"; "typeinfo name for Mlt::Tractor"; "vtable for Mlt::Tractor"; "Mlt::Tractor::connect(Mlt::Producer&)"; "Mlt::Tractor::count()"; "Mlt::Tractor::field()"; "Mlt::Tractor::get_producer()"; "Mlt::Tractor::get_tractor()"; "Mlt::Tractor::locate_cut(Mlt::Producer*, int&, int&)"; "Mlt::Tractor::multitrack()"; "Mlt::Tractor::plant_filter(Mlt::Filter*, int)"; "Mlt::Tractor::plant_filter(Mlt::Filter&, int)"; "Mlt::Tractor::plant_transition(Mlt::Transition*, int, int)"; "Mlt::Tractor::plant_transition(Mlt::Transition&, int, int)"; "Mlt::Tractor::refresh()"; "Mlt::Tractor::set_track(Mlt::Producer&, int)"; "Mlt::Tractor::track(int)"; "Mlt::Tractor::~Tractor()"; "Mlt::Tractor::Tractor()"; "Mlt::Tractor::Tractor(Mlt::Profile&, char*, char*)"; "Mlt::Tractor::Tractor(Mlt::Service&)"; "Mlt::Tractor::Tractor(Mlt::Tractor&)"; "Mlt::Tractor::Tractor(mlt_tractor_s*)"; "typeinfo for Mlt::Transition"; "typeinfo name for Mlt::Transition"; "vtable for Mlt::Transition"; "Mlt::Transition::connect(Mlt::Producer&, int, int)"; "Mlt::Transition::get_a_track()"; "Mlt::Transition::get_b_track()"; "Mlt::Transition::get_in()"; "Mlt::Transition::get_length()"; "Mlt::Transition::get_out()"; "Mlt::Transition::get_position(Mlt::Frame&)"; "Mlt::Transition::get_progress_delta(Mlt::Frame&)"; "Mlt::Transition::get_progress(Mlt::Frame&)"; "Mlt::Transition::get_service()"; "Mlt::Transition::get_transition()"; "Mlt::Transition::set_in_and_out(int, int)"; "Mlt::Transition::~Transition()"; "Mlt::Transition::Transition(Mlt::Profile&, char const*, char const*)"; "Mlt::Transition::Transition(Mlt::Service&)"; "Mlt::Transition::Transition(Mlt::Transition&)"; "Mlt::Transition::Transition(mlt_transition_s*)"; }; # "Mlt::Properties::set(char const*, int64_t)"; _ZN3Mlt10Properties3setEPKc[lx]; local: *; }; MLTPP_0.9.0 { global: extern "C++" { "Mlt::Deque::peek(int)"; "Mlt::Properties::anim_get(char const*, int, int)"; "Mlt::Properties::anim_get_double(char const*, int, int)"; "Mlt::Properties::anim_get_int(char const*, int, int)"; "Mlt::Properties::anim_get_rect(char const*, int, int)"; "Mlt::Properties::anim_set(char const*, char const*, int, int)"; "Mlt::Properties::anim_set(char const*, double, int, int, mlt_keyframe_type)"; "Mlt::Properties::anim_set(char const*, int, int, int, mlt_keyframe_type)"; "Mlt::Properties::anim_set(char const*, mlt_rect, int, int, mlt_keyframe_type)"; "Mlt::Properties::get_color(char const*)"; "Mlt::Properties::get_rect(char const*)"; "Mlt::Properties::set(char const*, double, double, double, double, double)"; "Mlt::Properties::set(char const*, mlt_color)"; "Mlt::Properties::set(char const*, mlt_rect)"; "Mlt::Service::filter_count()"; "Mlt::Service::move_filter(int, int)"; }; } MLTPP_0.8.8; MLTPP_0.9.2 { global: extern "C++" { "Mlt::Playlist::mix_in(int, int)"; "Mlt::Playlist::mix_out(int, int)"; "Mlt::Properties::frames_to_time(int, mlt_time_format)"; "Mlt::Properties::time_to_frames(char const*)"; }; } MLTPP_0.9.0; MLTPP_0.9.4 { global: extern "C++" { "Mlt::Profile::set_display_aspect(int, int)"; "Mlt::Tractor::Tractor(Mlt::Profile&)"; "Mlt::Frame::Frame()"; "Mlt::Frame::Frame(Mlt::Frame const&)"; "Mlt::Frame::operator=(Mlt::Frame const&)"; "Mlt::Filter::process(Mlt::Frame&)"; }; } MLTPP_0.9.2; MLTPP_0.9.8 { global: extern "C++" { "Mlt::Multitrack::disconnect(int)"; "Mlt::Service::disconnect_producer(int)"; "Mlt::Tractor::remove_track(int)"; "Mlt::Service::insert_producer(Mlt::Service&, int)"; "Mlt::Multitrack::insert(Mlt::Producer&, int)"; "Mlt::Tractor::insert_track(Mlt::Producer&, int)"; "Mlt::Transition::set_tracks(int, int)"; "Mlt::Properties::get_animation(char const*)"; "Mlt::Properties::get_anim(char const*)"; "Mlt::Animation::Animation()"; "Mlt::Animation::Animation(mlt_animation_s*)"; "Mlt::Animation::Animation(Mlt::Animation const&)"; "Mlt::Animation::~Animation()"; "Mlt::Animation::is_valid() const"; "Mlt::Animation::get_animation() const"; "Mlt::Animation::operator=(Mlt::Animation const&)"; "Mlt::Animation::length()"; "Mlt::Animation::is_key(int)"; "Mlt::Animation::keyframe_type(int)"; "Mlt::Animation::get_item(int, bool&, mlt_keyframe_type&)"; "Mlt::Animation::next_key(int)"; "Mlt::Animation::previous_key(int)"; "Mlt::Animation::key_count()"; "Mlt::Animation::key_get(int, int&, mlt_keyframe_type&)"; "Mlt::Animation::key_get_frame(int)"; "Mlt::Animation::key_get_type(int)"; "Mlt::Animation::set_length(int)"; "Mlt::Animation::remove(int)"; "Mlt::Animation::interpolate()"; "Mlt::Animation::serialize_cut(int, int)"; }; } MLTPP_0.9.4; MLTPP_6.4.0 { global: extern "C++" { "Mlt::Profile::is_valid() const"; }; } MLTPP_0.9.8; MLTPP_6.6.0 { global: extern "C++" { "Mlt::Service::disconnect_all_producers()"; }; } MLTPP_6.4.0; MLTPP_6.8.0 { global: extern "C++" { "Mlt::Animation::key_set_type(int, mlt_keyframe_type)"; "Mlt::Animation::key_set_frame(int, int)"; }; } MLTPP_6.6.0; MLTPP_6.10.0 { global: extern "C++" { "Mlt::Properties::get(int, mlt_time_format)"; "Mlt::Properties::clear(char const*)"; "Mlt::Animation::serialize_cut(mlt_time_format, int, int)"; }; } MLTPP_6.8.0; MLTPP_6.14.0 { global: extern "C++" { "Mlt::Producer::Producer(mlt_profile_s*, char const*, char const*)"; "Mlt::Consumer::Consumer(mlt_profile_s*, char const*, char const*)"; "Mlt::Transition::Transition(mlt_profile_s*, char const*, char const*)"; "Mlt::Filter::Filter(mlt_profile_s*, char const*, char const*)"; "Mlt::Tractor::Tractor(mlt_profile_s*, char*, char*)"; "Mlt::Service::set_profile(mlt_profile_s*)"; "Mlt::Playlist::reorder(int const*)"; "Mlt::Transition::connect(Mlt::Service&, int, int)"; "Mlt::Producer::get_creation_time()"; }; # "Mlt::Producer::set_creation_time(int64_t)"; _ZN3Mlt8Producer17set_creation_timeE[lx]; } MLTPP_6.10.0; MLTPP_6.18.0 { global: extern "C++" { "Mlt::Filter::Filter()"; "Mlt::Filter::Filter(Mlt::Filter const&)"; "Mlt::Filter::operator=(Mlt::Filter const&)"; "Mlt::Producer::Producer(Mlt::Producer const&)"; "Mlt::Producer::operator=(Mlt::Producer const&)"; "Mlt::Properties::Properties(Mlt::Properties const&)"; "Mlt::Properties::operator=(Mlt::Properties const&)"; "Mlt::Service::Service(Mlt::Service const&)"; "Mlt::Service::operator=(Mlt::Service const&)"; "Mlt::Transition::Transition()"; "Mlt::Transition::Transition(Mlt::Transition const&)"; "Mlt::Transition::operator=(Mlt::Transition const&)"; }; } MLTPP_6.14.0; MLTPP_6.20.0 { global: extern "C++" { "Mlt::Profile::scale_width(int)"; "Mlt::Profile::scale_height(int)"; "Mlt::Properties::set_string(char const*, char const*)"; }; } MLTPP_6.18.0; MLTPP_6.22.0 { global: extern "C++" { "Mlt::Properties::property_exists(char const*)"; "Mlt::Audio::Audio()"; "Mlt::Audio::Audio(mlt_audio_s*)"; "Mlt::Audio::~Audio()"; "Mlt::Audio::data()"; "Mlt::Audio::set_data(void*)"; "Mlt::Audio::frequency()"; "Mlt::Audio::set_frequency(int)"; "Mlt::Audio::format()"; "Mlt::Audio::set_format(mlt_audio_format)"; "Mlt::Audio::samples()"; "Mlt::Audio::set_samples(int)"; "Mlt::Audio::channels()"; "Mlt::Audio::set_channels(int)"; "Mlt::Audio::layout()"; "Mlt::Audio::set_layout(mlt_channel_layout)"; }; } MLTPP_6.20.0; MLTPP_7.0.0 { global: extern "C++" { "typeinfo for Mlt::Audio"; "typeinfo name for Mlt::Audio"; "vtable for Mlt::Audio"; "typeinfo for Mlt::Image"; "typeinfo name for Mlt::Image"; "vtable for Mlt::Image"; "typeinfo for Mlt::Link"; "typeinfo name for Mlt::Link"; "vtable for Mlt::Link"; "typeinfo for Mlt::Chain"; "typeinfo name for Mlt::Chain"; "vtable for Mlt::Chain"; "Mlt::Link::Link()"; "Mlt::Link::Link(mlt_link_s*)"; "Mlt::Link::Link(char const*, char const*)"; "Mlt::Link::~Link()"; "Mlt::Link::get_link()"; "Mlt::Link::get_producer()"; "Mlt::Link::connect_next(Mlt::Producer&, Mlt::Profile&)"; "Mlt::Chain::Chain()"; "Mlt::Chain::Chain(Mlt::Profile&, char const*, char const*)"; "Mlt::Chain::Chain(Mlt::Profile&)"; "Mlt::Chain::Chain(mlt_chain_s*)"; "Mlt::Chain::Chain(Mlt::Chain&)"; "Mlt::Chain::Chain(Mlt::Chain*)"; "Mlt::Chain::Chain(Mlt::Service&)"; "Mlt::Chain::~Chain()"; "Mlt::Chain::get_chain()"; "Mlt::Chain::get_producer()"; "Mlt::Chain::set_source(Mlt::Producer&)"; "Mlt::Chain::get_source()"; "Mlt::Chain::attach(Mlt::Link&)"; "Mlt::Chain::detach(Mlt::Link&)"; "Mlt::Chain::link_count() const"; "Mlt::Chain::move_link(int, int)"; "Mlt::Chain::link(int)"; "Mlt::Repository::links() const"; "Mlt::Parser::on_start_chain(Mlt::Chain*)"; "Mlt::Parser::on_end_chain(Mlt::Chain*)"; "Mlt::Parser::on_start_link(Mlt::Link*)"; "Mlt::Parser::on_end_link(Mlt::Link*)"; "Mlt::Animation::shift_frames(int)"; "Mlt::Image::Image()"; "Mlt::Image::Image(mlt_image_s*)"; "Mlt::Image::Image(int, int, mlt_image_format)"; "Mlt::Image::~Image()"; "Mlt::Image::format()"; "Mlt::Image::width()"; "Mlt::Image::height()"; "Mlt::Image::set_colorspace(int)"; "Mlt::Image::colorspace()"; "Mlt::Image::alloc(int, int, mlt_image_format, bool)"; "Mlt::Image::init_alpha()"; "Mlt::Image::plane(int)"; "Mlt::Image::stride(int)"; "Mlt::Properties::listen(char const*, void*, void (*)(mlt_properties_s*, void*, mlt_event_data))"; "Mlt::EventData::EventData(mlt_event_data)"; "Mlt::EventData::EventData(Mlt::EventData&)"; "Mlt::EventData::EventData(Mlt::EventData const&)"; "Mlt::EventData::operator=(Mlt::EventData const&)"; "Mlt::EventData::get_event_data() const"; "Mlt::EventData::to_int() const"; "Mlt::EventData::to_string() const"; "Mlt::EventData::to_frame() const"; "Mlt::EventData::to_object() const"; }; } MLTPP_6.22.0; MLTPP_7.1.0 { global: extern "C++" { "Mlt::Properties::set(char const*, Mlt::Properties&)"; "Mlt::Properties::get_props(char const*)"; "Mlt::Properties::get_props_at(int)"; }; } MLTPP_7.0.0; MLT_7.4.0 { global: extern "C++" { "Mlt::Properties::is_anim(char const*)"; "Mlt::Filter::Filter(Mlt::Filter*)"; "Mlt::Link::Link(Mlt::Link*)"; "Mlt::Link::Link(Mlt::Service&)"; "Mlt::Link::Link(Mlt::Link&)"; "Mlt::Link::Link(Mlt::Link const&)"; "Mlt::Link::operator=(Mlt::Link const&)"; "Mlt::Service::Service(Mlt::Service*)"; }; } MLTPP_7.1.0; MLT_7.6.0 { global: extern "C++" { "Mlt::Animation::next_key(int, int&)"; "Mlt::Animation::previous_key(int, int&)"; "Mlt::Properties::copy(Mlt::Properties&, char const*)"; }; } MLT_7.4.0; MLT_7.12.0 { global: extern "C++" { "Mlt::Properties::anim_get_color(char const*, int, int)"; "Mlt::Properties::anim_set(char const*, mlt_color, int, int, mlt_keyframe_type)"; }; } MLT_7.6.0; MLT_7.14.0 { global: extern "C++" { "Mlt::Producer::probe()"; "Mlt::Chain::attach_normalizers()"; }; } MLT_7.12.0; MLT_7.32.0 { global: extern "C++" { "Mlt::Service::set_consumer(Mlt::Service&)"; }; } MLT_7.14.0; MLT_7.36.0 { global: extern "C++" { "Mlt::Profile::is_valid()"; }; } MLT_7.32.0; mlt-7.38.0/src/modules/000775 000000 000000 00000000000 15172202314 014621 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/CMakeLists.txt000664 000000 000000 00000002611 15172202314 017361 0ustar00rootroot000000 000000 add_subdirectory(core) if(MOD_AVFORMAT) add_subdirectory(avformat) endif() if(MOD_DECKLINK) add_subdirectory(decklink) endif() if(MOD_FREI0R) add_subdirectory(frei0r) endif() if(MOD_GDK) add_subdirectory(gdk) endif() if(MOD_GLAXNIMATE_QT6) add_subdirectory(glaxnimate) endif() if(MOD_JACKRACK) add_subdirectory(jackrack) endif() if(MOD_KDENLIVE) add_subdirectory(kdenlive) endif() if(MOD_NDI) add_subdirectory(ndi) endif() if(MOD_NORMALIZE) add_subdirectory(normalize) endif() if(MOD_OLDFILM) add_subdirectory(oldfilm) endif() if(MOD_OPENCV) add_subdirectory(opencv) endif() if(MOD_MOVIT) add_subdirectory(movit) endif() if(MOD_PLUS) add_subdirectory(plus) endif() if(MOD_PLUSGPL) add_subdirectory(plusgpl) endif() if(MOD_QT6) add_subdirectory(qt) endif() if(MOD_RESAMPLE) add_subdirectory(resample) endif() if(MOD_RTAUDIO) add_subdirectory(rtaudio) endif() if(MOD_RUBBERBAND) add_subdirectory(rubberband) endif() if(MOD_SDL1) add_subdirectory(sdl) endif() if(MOD_SDL2) add_subdirectory(sdl2) endif() if(MOD_SOX) add_subdirectory(sox) endif() if(MOD_SPATIALAUDIO) add_subdirectory(spatialaudio) endif() if(MOD_VIDSTAB) add_subdirectory(vid.stab) endif() if(MOD_OPENFX) add_subdirectory(openfx) endif() if(MOD_VORBIS) add_subdirectory(vorbis) endif() if(MOD_XINE) add_subdirectory(xine) endif() if(MOD_XML) add_subdirectory(xml) endif() mlt-7.38.0/src/modules/avformat/000775 000000 000000 00000000000 15172202314 016440 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/avformat/CMakeLists.txt000664 000000 000000 00000003474 15172202314 021210 0ustar00rootroot000000 000000 add_library(mltavformat MODULE common.c common.h factory.c filter_avcolour_space.c filter_avdeinterlace.c filter_swscale.c filter_sws_colortransform.c # avformat producer_avformat.c consumer_avformat.c # avfilter filter_avfilter.c link_avdeinterlace.c link_avfilter.c # swresample common_swr.c common_swr.h filter_swresample.c link_swresample.c ) file(GLOB YML "*.yml") add_custom_target(Other_avformat_Files SOURCES ${YML} blacklist.txt yuv_only.txt ) include(GenerateExportHeader) generate_export_header(mltavformat) target_compile_options(mltavformat PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltavformat SYSTEM PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_include_directories(mltavformat PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltavformat PRIVATE mlt Threads::Threads ${FFMPEG_LIBRARIES} ) if(MSVC) target_link_libraries(mltavformat PRIVATE PThreads4W::PThreads4W msvccompat) else() target_link_libraries(mltavformat PRIVATE m) endif() if(WIN32) target_compile_definitions(mltavformat PRIVATE AVDATADIR="share/ffmpeg/") if(MINGW) target_link_libraries(mltavformat PRIVATE ${MLT_PTHREAD_LIBS}) endif() endif() if(CPU_MMX) target_compile_definitions(mltavformat PRIVATE USE_MMX) endif() set_target_properties(mltavformat PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltavformat LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_avformat.yml filter_avcolour_space.yml filter_avdeinterlace.yml filter_swresample.yml filter_swscale.yml filter_sws_colortransform.yml link_avdeinterlace.yml link_swresample.yml producer_avformat.yml producer_avformat-novalidate.yml resolution_scale.yml blacklist.txt yuv_only.txt DESTINATION ${MLT_INSTALL_DATA_DIR}/avformat ) mlt-7.38.0/src/modules/avformat/blacklist.txt000664 000000 000000 00000001537 15172202314 021157 0ustar00rootroot000000 000000 acopy acrossfade afifo aformat amerge ametadata amix amultiply anull apad aperms aresample areverse asendcmd asetnsamples asetpts asetrate asettb astreamselect atempo atrim channelsplit codecview copy cover_rect dejudder detelecine displace extractplanes fifo format fps framerate framestep frei0r hysteresis join interlace ladspa maskedclamp maskedmerge mergeplanes mestimate metadata midequalizer minterpolate mix mpdecimate noformat null overlay palettegen paletteuse pan perms pixdesctest premultiply psnr pullup qp readeia608 readvitc repeatfields remap repeatfields replaygain resample reverse scale2ref sendcmd separatefields setdar setfield setparams setpts setsar settb showpalette sidechaincompress sidechaingate silenceremove ssim streamselect surround telecine thumbnail tile tinterlace trim unpremultiply vidstabdetect vidstabtransform vfrdet xstack mlt-7.38.0/src/modules/avformat/common.c000664 000000 000000 00000055747 15172202314 020116 0ustar00rootroot000000 000000 /* * common.h * Copyright (C) 2018-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include int mlt_get_sws_flags( int srcwidth, int srcheight, int srcformat, int dstwidth, int dstheight, int dstformat) { // Use default flags unless there is a reason to use something different. int flags = SWS_BICUBIC | SWS_FULL_CHR_H_INP | SWS_FULL_CHR_H_INT | SWS_ACCURATE_RND; if (srcwidth != dstwidth || srcheight != dstheight) { // Any resolution change should use default flags return flags; } const AVPixFmtDescriptor *srcDesc = av_pix_fmt_desc_get(srcformat); const AVPixFmtDescriptor *dstDesc = av_pix_fmt_desc_get(dstformat); if (!srcDesc || !dstDesc) { return flags; } if ((srcDesc->flags & AV_PIX_FMT_FLAG_RGB) != 0 && (dstDesc->flags & AV_PIX_FMT_FLAG_RGB) == 0) { // RGB -> YUV flags = SWS_BICUBIC; flags |= SWS_ACCURATE_RND; // Can improve precision by one bit flags |= SWS_FULL_CHR_H_INT; // Avoids luma reduction. Causes chroma bleeding when used with SWS_BICUBIC } else if ((srcDesc->flags & AV_PIX_FMT_FLAG_RGB) == 0 && (dstDesc->flags & AV_PIX_FMT_FLAG_RGB) != 0) { // YUV -> RGB // Going from lower sampling to full sampling - so pick the closest sample without interpolation flags = SWS_POINT; flags |= SWS_ACCURATE_RND; // Can improve precision by one bit flags |= SWS_FULL_CHR_H_INT; // Avoids luma reduction. Does not cause chroma bleeding when used with SWS_POINT } else if ((srcDesc->flags & AV_PIX_FMT_FLAG_RGB) == 0 && (dstDesc->flags & AV_PIX_FMT_FLAG_RGB) == 0) { // YUV -> YUV if (srcDesc->log2_chroma_w == dstDesc->log2_chroma_w && srcDesc->log2_chroma_h == dstDesc->log2_chroma_h) { // No chroma subsampling conversion. No interpolation required flags = SWS_POINT; flags |= SWS_ACCURATE_RND; // Can improve precision by one bit } else { // Chroma will be interpolated. Bilinear is suitable. flags = SWS_BILINEAR | SWS_ACCURATE_RND; } } return flags; } int mlt_to_av_sample_format(mlt_audio_format format) { switch (format) { case mlt_audio_none: return AV_SAMPLE_FMT_NONE; case mlt_audio_s16: return AV_SAMPLE_FMT_S16; case mlt_audio_s32: return AV_SAMPLE_FMT_S32P; case mlt_audio_float: return AV_SAMPLE_FMT_FLTP; case mlt_audio_s32le: return AV_SAMPLE_FMT_S32; case mlt_audio_f32le: return AV_SAMPLE_FMT_FLT; case mlt_audio_u8: return AV_SAMPLE_FMT_U8; } mlt_log_error(NULL, "[avformat] Unknown audio format: %d\n", format); return AV_SAMPLE_FMT_NONE; } int64_t mlt_to_av_channel_layout(mlt_channel_layout layout) { switch (layout) { case mlt_channel_auto: case mlt_channel_independent: mlt_log_error(NULL, "[avformat] No matching channel layout: %s\n", mlt_audio_channel_layout_name(layout)); return 0; case mlt_channel_mono: return AV_CH_LAYOUT_MONO; case mlt_channel_stereo: return AV_CH_LAYOUT_STEREO; case mlt_channel_2p1: return AV_CH_LAYOUT_2POINT1; case mlt_channel_3p0: return AV_CH_LAYOUT_SURROUND; case mlt_channel_3p0_back: return AV_CH_LAYOUT_2_1; case mlt_channel_3p1: return AV_CH_LAYOUT_3POINT1; case mlt_channel_4p0: return AV_CH_LAYOUT_4POINT0; case mlt_channel_quad_back: return AV_CH_LAYOUT_QUAD; case mlt_channel_quad_side: return AV_CH_LAYOUT_2_2; case mlt_channel_5p0: return AV_CH_LAYOUT_5POINT0; case mlt_channel_5p0_back: return AV_CH_LAYOUT_5POINT0_BACK; case mlt_channel_4p1: return AV_CH_LAYOUT_4POINT1; case mlt_channel_5p1: return AV_CH_LAYOUT_5POINT1; case mlt_channel_5p1_back: return AV_CH_LAYOUT_5POINT1_BACK; case mlt_channel_6p0: return AV_CH_LAYOUT_6POINT0; case mlt_channel_6p0_front: return AV_CH_LAYOUT_6POINT0_FRONT; case mlt_channel_hexagonal: return AV_CH_LAYOUT_HEXAGONAL; case mlt_channel_6p1: return AV_CH_LAYOUT_6POINT1; case mlt_channel_6p1_back: return AV_CH_LAYOUT_6POINT1_BACK; case mlt_channel_6p1_front: return AV_CH_LAYOUT_6POINT1_FRONT; case mlt_channel_7p0: return AV_CH_LAYOUT_7POINT0; case mlt_channel_7p0_front: return AV_CH_LAYOUT_7POINT0_FRONT; case mlt_channel_7p1: return AV_CH_LAYOUT_7POINT1; case mlt_channel_7p1_wide_side: return AV_CH_LAYOUT_7POINT1_WIDE; case mlt_channel_7p1_wide_back: return AV_CH_LAYOUT_7POINT1_WIDE_BACK; } mlt_log_error(NULL, "[avformat] Unknown channel configuration: %d\n", layout); return 0; } #if HAVE_FFMPEG_CH_LAYOUT mlt_channel_layout av_channel_layout_to_mlt(AVChannelLayout *layout) { if (layout->order != AV_CHANNEL_ORDER_NATIVE && layout->order != AV_CHANNEL_ORDER_AMBISONIC) { return (layout->nb_channels == 1) ? mlt_channel_mono : mlt_channel_independent; } unsigned long layout_id = layout->u.mask; #else mlt_channel_layout av_channel_layout_to_mlt(int64_t layout) { unsigned long layout_id = layout; #endif switch (layout_id) { case 0: return mlt_channel_independent; case AV_CH_LAYOUT_MONO: return mlt_channel_mono; case AV_CH_LAYOUT_STEREO: return mlt_channel_stereo; case AV_CH_LAYOUT_STEREO_DOWNMIX: return mlt_channel_stereo; case AV_CH_LAYOUT_2POINT1: return mlt_channel_2p1; case AV_CH_LAYOUT_SURROUND: return mlt_channel_3p0; case AV_CH_LAYOUT_2_1: return mlt_channel_3p0_back; case AV_CH_LAYOUT_3POINT1: return mlt_channel_3p1; case AV_CH_LAYOUT_4POINT0: return mlt_channel_4p0; case AV_CH_LAYOUT_QUAD: return mlt_channel_quad_back; case AV_CH_LAYOUT_2_2: return mlt_channel_quad_side; case AV_CH_LAYOUT_5POINT0: return mlt_channel_5p0; case AV_CH_LAYOUT_5POINT0_BACK: return mlt_channel_5p0_back; case AV_CH_LAYOUT_4POINT1: return mlt_channel_4p1; case AV_CH_LAYOUT_5POINT1: return mlt_channel_5p1; case AV_CH_LAYOUT_5POINT1_BACK: return mlt_channel_5p1_back; case AV_CH_LAYOUT_6POINT0: return mlt_channel_6p0; case AV_CH_LAYOUT_6POINT0_FRONT: return mlt_channel_6p0_front; case AV_CH_LAYOUT_HEXAGONAL: return mlt_channel_hexagonal; case AV_CH_LAYOUT_6POINT1: return mlt_channel_6p1; case AV_CH_LAYOUT_6POINT1_BACK: return mlt_channel_6p1_back; case AV_CH_LAYOUT_6POINT1_FRONT: return mlt_channel_6p1_front; case AV_CH_LAYOUT_7POINT0: return mlt_channel_7p0; case AV_CH_LAYOUT_7POINT0_FRONT: return mlt_channel_7p0_front; case AV_CH_LAYOUT_7POINT1: return mlt_channel_7p1; case AV_CH_LAYOUT_7POINT1_WIDE: return mlt_channel_7p1_wide_side; case AV_CH_LAYOUT_7POINT1_WIDE_BACK: return mlt_channel_7p1_wide_back; } mlt_log_error(NULL, "[avformat] Unknown channel layout: %lu\n", layout_id); return mlt_channel_independent; } mlt_channel_layout mlt_get_channel_layout_or_default(const char *name, int channels) { mlt_channel_layout layout = mlt_audio_channel_layout_id(name); if (layout == mlt_channel_auto || (layout != mlt_channel_independent && mlt_audio_channel_layout_channels(layout) != channels)) { layout = mlt_audio_channel_layout_default(channels); } return layout; } int mlt_set_luma_transfer(struct SwsContext *context, mlt_colorspace src_colorspace, mlt_colorspace dst_colorspace, int src_full_range, int dst_full_range) { const int *src_coefficients = sws_getCoefficients(SWS_CS_DEFAULT); const int *dst_coefficients = sws_getCoefficients(SWS_CS_DEFAULT); int brightness = 0; int contrast = 1 << 16; int saturation = 1 << 16; int src_range = src_full_range ? 1 : 0; int dst_range = dst_full_range ? 1 : 0; switch (src_colorspace) { case mlt_colorspace_smpte170m: case mlt_colorspace_bt470bg: case mlt_colorspace_bt601: src_coefficients = sws_getCoefficients(SWS_CS_ITU601); break; case mlt_colorspace_smpte240m: src_coefficients = sws_getCoefficients(SWS_CS_SMPTE240M); break; case mlt_colorspace_bt709: src_coefficients = sws_getCoefficients(SWS_CS_ITU709); break; case mlt_colorspace_bt2020_ncl: case mlt_colorspace_bt2020_cl: src_coefficients = sws_getCoefficients(SWS_CS_BT2020); default: break; } switch (dst_colorspace) { case mlt_colorspace_smpte170m: case mlt_colorspace_bt470bg: case mlt_colorspace_bt601: dst_coefficients = sws_getCoefficients(SWS_CS_ITU601); break; case mlt_colorspace_smpte240m: dst_coefficients = sws_getCoefficients(SWS_CS_SMPTE240M); break; case mlt_colorspace_bt709: dst_coefficients = sws_getCoefficients(SWS_CS_ITU709); break; case mlt_colorspace_bt2020_ncl: case mlt_colorspace_bt2020_cl: dst_coefficients = sws_getCoefficients(SWS_CS_BT2020); default: break; } return sws_setColorspaceDetails(context, src_coefficients, src_range, dst_coefficients, dst_range, brightness, contrast, saturation); } int mlt_to_av_image_format(mlt_image_format format) { switch (format) { case mlt_image_none: return AV_PIX_FMT_NONE; case mlt_image_rgb: return AV_PIX_FMT_RGB24; case mlt_image_rgba: return AV_PIX_FMT_RGBA; case mlt_image_yuv422: return AV_PIX_FMT_YUYV422; case mlt_image_yuv420p: return AV_PIX_FMT_YUV420P; case mlt_image_yuv420p10: return AV_PIX_FMT_YUV420P10LE; case mlt_image_yuv444p10: return AV_PIX_FMT_YUV444P10LE; case mlt_image_yuv422p16: return AV_PIX_FMT_YUV422P16LE; case mlt_image_rgba64: return AV_PIX_FMT_RGBA64LE; case mlt_image_movit: case mlt_image_opengl_texture: case mlt_image_invalid: mlt_log_error(NULL, "[filter_avfilter] Unexpected image format: %s\n", mlt_image_format_name(format)); return AV_PIX_FMT_NONE; } mlt_log_error(NULL, "[filter_avfilter] Unknown image format: %d\n", format); return AV_PIX_FMT_NONE; } mlt_image_format mlt_get_supported_image_format(mlt_image_format format) { switch (format) { case mlt_image_invalid: case mlt_image_none: case mlt_image_movit: case mlt_image_opengl_texture: case mlt_image_rgba: return mlt_image_rgba; case mlt_image_rgb: return mlt_image_rgb; case mlt_image_yuv420p: return mlt_image_yuv420p; case mlt_image_yuv422: return mlt_image_yuv422; case mlt_image_yuv420p10: return mlt_image_yuv420p10; case mlt_image_yuv444p10: return mlt_image_yuv444p10; case mlt_image_yuv422p16: return mlt_image_yuv422p16; case mlt_image_rgba64: return mlt_image_rgba64; } mlt_log_error(NULL, "[filter_avfilter] Unknown image format requested: %d\n", format); return mlt_image_rgba; } int mlt_to_av_color_trc(mlt_color_trc trc) { switch (trc) { case mlt_color_trc_none: return AVCOL_TRC_RESERVED0; case mlt_color_trc_bt709: return AVCOL_TRC_BT709; case mlt_color_trc_unspecified: return AVCOL_TRC_UNSPECIFIED; case mlt_color_trc_reserved: return AVCOL_TRC_RESERVED; case mlt_color_trc_gamma22: return AVCOL_TRC_GAMMA22; case mlt_color_trc_gamma28: return AVCOL_TRC_GAMMA28; case mlt_color_trc_smpte170m: return AVCOL_TRC_SMPTE170M; case mlt_color_trc_smpte240m: return AVCOL_TRC_SMPTE240M; case mlt_color_trc_linear: return AVCOL_TRC_LINEAR; case mlt_color_trc_log: return AVCOL_TRC_LOG; case mlt_color_trc_log_sqrt: return AVCOL_TRC_LOG_SQRT; case mlt_color_trc_iec61966_2_4: return AVCOL_TRC_IEC61966_2_4; case mlt_color_trc_bt1361_ecg: return AVCOL_TRC_BT1361_ECG; case mlt_color_trc_iec61966_2_1: return AVCOL_TRC_IEC61966_2_1; case mlt_color_trc_bt2020_10: return AVCOL_TRC_BT2020_10; case mlt_color_trc_bt2020_12: return AVCOL_TRC_BT2020_12; case mlt_color_trc_smpte2084: return AVCOL_TRC_SMPTE2084; case mlt_color_trc_smpte428: return AVCOL_TRC_SMPTE428; case mlt_color_trc_arib_std_b67: return AVCOL_TRC_ARIB_STD_B67; case mlt_color_trc_invalid: return AVCOL_TRC_UNSPECIFIED; } return AVCOL_TRC_UNSPECIFIED; } mlt_color_trc av_to_mlt_color_trc(int trc) { switch (trc) { case AVCOL_TRC_RESERVED0: return mlt_color_trc_none; case AVCOL_TRC_BT709: return mlt_color_trc_bt709; case AVCOL_TRC_UNSPECIFIED: return mlt_color_trc_unspecified; case AVCOL_TRC_RESERVED: return mlt_color_trc_reserved; case AVCOL_TRC_GAMMA22: return mlt_color_trc_gamma22; case AVCOL_TRC_GAMMA28: return mlt_color_trc_gamma28; case AVCOL_TRC_SMPTE170M: return mlt_color_trc_smpte170m; case AVCOL_TRC_SMPTE240M: return mlt_color_trc_smpte240m; case AVCOL_TRC_LINEAR: return mlt_color_trc_linear; case AVCOL_TRC_LOG: return mlt_color_trc_log; case AVCOL_TRC_LOG_SQRT: return mlt_color_trc_log_sqrt; case AVCOL_TRC_IEC61966_2_4: return mlt_color_trc_iec61966_2_4; case AVCOL_TRC_BT1361_ECG: return mlt_color_trc_bt1361_ecg; case AVCOL_TRC_IEC61966_2_1: return mlt_color_trc_iec61966_2_1; case AVCOL_TRC_BT2020_10: return mlt_color_trc_bt2020_10; case AVCOL_TRC_BT2020_12: return mlt_color_trc_bt2020_12; case AVCOL_TRC_SMPTE2084: return mlt_color_trc_smpte2084; case AVCOL_TRC_SMPTE428: return mlt_color_trc_smpte428; case AVCOL_TRC_ARIB_STD_B67: return mlt_color_trc_arib_std_b67; } return mlt_color_trc_unspecified; } int mlt_to_av_colorspace(mlt_colorspace colorspace, int height) { switch (colorspace) { case mlt_colorspace_rgb: return AVCOL_SPC_RGB; case mlt_colorspace_bt709: return AVCOL_SPC_BT709; case mlt_colorspace_unspecified: return AVCOL_SPC_UNSPECIFIED; case mlt_colorspace_reserved: return AVCOL_SPC_RESERVED; case mlt_colorspace_fcc: return AVCOL_SPC_FCC; case mlt_colorspace_bt470bg: return AVCOL_SPC_BT470BG; case mlt_colorspace_smpte170m: return AVCOL_SPC_SMPTE170M; case mlt_colorspace_smpte240m: return AVCOL_SPC_SMPTE240M; case mlt_colorspace_ycgco: return AVCOL_SPC_YCGCO; case mlt_colorspace_bt2020_ncl: return AVCOL_SPC_BT2020_NCL; case mlt_colorspace_bt2020_cl: return AVCOL_SPC_BT2020_CL; case mlt_colorspace_smpte2085: return AVCOL_SPC_SMPTE2085; case mlt_colorspace_bt601: return 576 % height ? AVCOL_SPC_SMPTE170M : AVCOL_SPC_BT470BG; case mlt_colorspace_invalid: return mlt_colorspace_unspecified; } return mlt_colorspace_unspecified; } mlt_colorspace av_to_mlt_colorspace(int colorspace, int width, int height) { switch (colorspace) { case AVCOL_SPC_RGB: return mlt_colorspace_rgb; case AVCOL_SPC_BT709: return mlt_colorspace_bt709; case AVCOL_SPC_UNSPECIFIED: case AVCOL_SPC_RESERVED: break; // use calculation at the end case AVCOL_SPC_FCC: return mlt_colorspace_fcc; case AVCOL_SPC_BT470BG: case AVCOL_SPC_SMPTE170M: return mlt_colorspace_bt601; case AVCOL_SPC_SMPTE240M: return mlt_colorspace_smpte240m; case AVCOL_SPC_YCGCO: return mlt_colorspace_ycgco; case AVCOL_SPC_BT2020_NCL: return mlt_colorspace_bt2020_ncl; case AVCOL_SPC_BT2020_CL: return mlt_colorspace_bt2020_cl; case AVCOL_SPC_SMPTE2085: return mlt_colorspace_smpte2085; } // This is a heuristic Charles Poynton suggests in "Digital Video and HDTV" if (width * height > 750000) { return mlt_colorspace_bt709; } return mlt_colorspace_bt601; } int mlt_to_av_color_primaries(mlt_color_primaries primaries) { switch (primaries) { case mlt_color_pri_none: return AVCOL_PRI_UNSPECIFIED; case mlt_color_pri_bt709: return AVCOL_PRI_BT709; case mlt_color_pri_bt470m: return AVCOL_PRI_BT470M; case mlt_color_pri_bt470bg: return AVCOL_PRI_BT470BG; case mlt_color_pri_smpte170m: return AVCOL_PRI_SMPTE170M; case mlt_color_pri_film: return AVCOL_PRI_FILM; case mlt_color_pri_bt2020: return AVCOL_PRI_BT2020; case mlt_color_pri_smpte428: return AVCOL_PRI_SMPTE428; case mlt_color_pri_smpte431: return AVCOL_PRI_SMPTE431; case mlt_color_pri_smpte432: return AVCOL_PRI_SMPTE432; case mlt_color_pri_invalid: return AVCOL_PRI_UNSPECIFIED; } return AVCOL_PRI_UNSPECIFIED; } mlt_color_primaries av_to_mlt_color_primaries(int primaries) { switch (primaries) { case AVCOL_PRI_RESERVED0: return mlt_color_pri_none; case AVCOL_PRI_BT709: return mlt_color_pri_bt709; case AVCOL_PRI_UNSPECIFIED: return mlt_color_pri_none; case AVCOL_PRI_RESERVED: return mlt_color_pri_none; case AVCOL_PRI_BT470M: return mlt_color_pri_bt470m; case AVCOL_PRI_BT470BG: return mlt_color_pri_bt470bg; case AVCOL_PRI_SMPTE170M: return mlt_color_pri_smpte170m; case AVCOL_PRI_SMPTE240M: return mlt_color_pri_smpte170m; case AVCOL_PRI_FILM: return mlt_color_pri_film; case AVCOL_PRI_BT2020: return mlt_color_pri_bt2020; case AVCOL_PRI_SMPTE428: return mlt_color_pri_smpte428; case AVCOL_PRI_SMPTE431: return mlt_color_pri_smpte431; case AVCOL_PRI_SMPTE432: return mlt_color_pri_smpte432; } return mlt_color_pri_none; } int mlt_to_av_color_range(int full_range) { return full_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; } int av_to_mlt_full_range(int color_range) { return color_range == AVCOL_RANGE_JPEG; } void mlt_image_to_avframe(mlt_image image, mlt_frame mltframe, AVFrame *avframe) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(mltframe); avframe->width = image->width; avframe->height = image->height; avframe->format = mlt_to_av_image_format(image->format); avframe->sample_aspect_ratio = av_d2q(mlt_frame_get_aspect_ratio(mltframe), 1024); avframe->pts = mlt_frame_get_position(mltframe); #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) if (!mlt_properties_get_int(frame_properties, "progressive")) avframe->flags |= AV_FRAME_FLAG_INTERLACED; else avframe->flags &= ~AV_FRAME_FLAG_INTERLACED; if (mlt_properties_get_int(frame_properties, "top_field_first")) avframe->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; else avframe->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; #else avframe->interlaced_frame = !mlt_properties_get_int(frame_properties, "progressive"); avframe->top_field_first = mlt_properties_get_int(frame_properties, "top_field_first"); #endif const char *primaries_str = mlt_properties_get(frame_properties, "color_primaries"); mlt_color_primaries primaries = mlt_image_color_pri_id(primaries_str); avframe->color_primaries = mlt_to_av_color_primaries(primaries); const char *color_trc_str = mlt_properties_get(frame_properties, "color_trc"); avframe->color_trc = mlt_to_av_color_trc(mlt_image_color_trc_id(color_trc_str)); avframe->color_range = mlt_to_av_color_range( mlt_properties_get_int(frame_properties, "full_range")); const char *colorspace_str = mlt_properties_get(frame_properties, "colorspace"); avframe->colorspace = mlt_to_av_colorspace(mlt_image_colorspace_id(colorspace_str), avframe->height); int ret = av_frame_get_buffer(avframe, 1); if (ret < 0) { mlt_log_error(NULL, "Cannot get frame buffer\n"); } // Set up the input frame if (image->format == mlt_image_yuv420p) { int i = 0; int p = 0; int widths[3] = {image->width, image->width / 2, image->width / 2}; int heights[3] = {image->height, image->height / 2, image->height / 2}; uint8_t *src = image->data; for (p = 0; p < 3; p++) { uint8_t *dst = avframe->data[p]; for (i = 0; i < heights[p]; i++) { memcpy(dst, src, widths[p]); src += widths[p]; dst += avframe->linesize[p]; } } } else { int i; uint8_t *src = image->data; uint8_t *dst = avframe->data[0]; int stride = mlt_image_format_size(image->format, image->width, 1, NULL); for (i = 0; i < image->height; i++) { memcpy(dst, src, stride); src += stride; dst += avframe->linesize[0]; } } } void avframe_to_mlt_image(AVFrame *avframe, mlt_image image) { if (image->format == mlt_image_yuv420p) { int i = 0; int p = 0; int widths[3] = {image->width, image->width / 2, image->width / 2}; int heights[3] = {image->height, image->height / 2, image->height / 2}; uint8_t *dst = image->data; for (p = 0; p < 3; p++) { uint8_t *src = avframe->data[p]; for (i = 0; i < heights[p]; i++) { memcpy(dst, src, widths[p]); dst += widths[p]; src += avframe->linesize[p]; } } } else { int i; uint8_t *dst = image->data; uint8_t *src = avframe->data[0]; int stride = mlt_image_format_size(image->format, image->width, 1, NULL); for (i = 0; i < image->height; i++) { memcpy(dst, src, stride); dst += stride; src += avframe->linesize[0]; } } } mlt-7.38.0/src/modules/avformat/common.h000664 000000 000000 00000005122 15172202314 020101 0ustar00rootroot000000 000000 /* * common.h * Copyright (C) 2018-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_H #define COMMON_H #include #include #include #define MLT_AVFILTER_SWS_FLAGS "bicubic+accurate_rnd+full_chroma_int+full_chroma_inp" #define HAVE_FFMPEG_CH_LAYOUT (LIBAVUTIL_VERSION_MAJOR >= 59) #define HAVE_FFMPEG_VULKAN (LIBAVUTIL_VERSION_INT >= ((56 << 16) + (39 << 8) + 100)) int mlt_to_av_sample_format(mlt_audio_format format); int64_t mlt_to_av_channel_layout(mlt_channel_layout layout); #if HAVE_FFMPEG_CH_LAYOUT mlt_channel_layout av_channel_layout_to_mlt(AVChannelLayout *layout); #else mlt_channel_layout av_channel_layout_to_mlt(int64_t layout); #endif mlt_channel_layout mlt_get_channel_layout_or_default(const char *name, int channels); int mlt_set_luma_transfer(struct SwsContext *context, mlt_colorspace src_colorspace, mlt_colorspace dst_colorspace, int src_full_range, int dst_full_range); int mlt_get_sws_flags( int srcwidth, int srcheight, int srcformat, int dstwidth, int dstheight, int dstformat); int mlt_to_av_image_format(mlt_image_format format); mlt_image_format mlt_get_supported_image_format(mlt_image_format format); int mlt_to_av_color_trc(mlt_color_trc trc); mlt_color_trc av_to_mlt_color_trc(int trc); int mlt_to_av_colorspace(mlt_colorspace colorspace, int height); mlt_colorspace av_to_mlt_colorspace(int colorspace, int width, int height); int mlt_to_av_color_primaries(mlt_color_primaries primaries); mlt_color_primaries av_to_mlt_color_primaries(int primaries); int mlt_to_av_color_range(int full_range); int av_to_mlt_full_range(int color_range); void mlt_image_to_avframe(mlt_image image, mlt_frame mltframe, AVFrame *avframe); void avframe_to_mlt_image(AVFrame *avframe, mlt_image image); #endif // COMMON_H mlt-7.38.0/src/modules/avformat/common_swr.c000664 000000 000000 00000012523 15172202314 020772 0ustar00rootroot000000 000000 /* * common_swr.c * Copyright (C) 2022-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common_swr.h" #include "common.h" #include #include #include int mlt_configure_swr_context(mlt_service service, mlt_swr_private_data *pdata) { int error = 0; mlt_log_verbose(service, "%d(%s) %s %dHz -> %d(%s) %s %dHz\n", pdata->in_channels, mlt_audio_channel_layout_name(pdata->in_layout), mlt_audio_format_name(pdata->in_format), pdata->in_frequency, pdata->out_channels, mlt_audio_channel_layout_name(pdata->out_layout), mlt_audio_format_name(pdata->out_format), pdata->out_frequency); mlt_free_swr_context(pdata); pdata->ctx = swr_alloc(); if (!pdata->ctx) { mlt_log_error(service, "Cannot allocate context\n"); return 1; } // Configure format, frequency and channels. av_opt_set_int(pdata->ctx, "osf", mlt_to_av_sample_format(pdata->out_format), 0); av_opt_set_int(pdata->ctx, "osr", pdata->out_frequency, 0); av_opt_set_int(pdata->ctx, "isf", mlt_to_av_sample_format(pdata->in_format), 0); av_opt_set_int(pdata->ctx, "isr", pdata->in_frequency, 0); #if !HAVE_FFMPEG_CH_LAYOUT av_opt_set_int(pdata->ctx, "och", pdata->out_channels, 0); av_opt_set_int(pdata->ctx, "ich", pdata->in_channels, 0); #endif if (pdata->in_layout != mlt_channel_independent && pdata->out_layout != mlt_channel_independent) { // Use standard channel layout and matrix for known channel configurations. #if HAVE_FFMPEG_CH_LAYOUT AVChannelLayout ochl = AV_CHANNEL_LAYOUT_MASK(pdata->out_channels, mlt_to_av_channel_layout(pdata->out_layout)); AVChannelLayout ichl = AV_CHANNEL_LAYOUT_MASK(pdata->in_channels, mlt_to_av_channel_layout(pdata->in_layout)); av_opt_set_chlayout(pdata->ctx, "ochl", &ochl, 0); av_opt_set_chlayout(pdata->ctx, "ichl", &ichl, 0); #else av_opt_set_int(pdata->ctx, "ocl", mlt_to_av_channel_layout(pdata->out_layout), 0); av_opt_set_int(pdata->ctx, "icl", mlt_to_av_channel_layout(pdata->in_layout), 0); #endif } else { // Use a custom channel layout and matrix for independent channels. // This matrix will simply map input channels to output channels in order. // If input channels > output channels, channels will be dropped. // If input channels < output channels, silent channels will be added. int64_t custom_in_layout = 0; int64_t custom_out_layout = 0; double *matrix = av_calloc(pdata->in_channels * pdata->out_channels, sizeof(double)); int stride = pdata->in_channels; int i = 0; for (i = 0; i < pdata->in_channels; i++) { custom_in_layout = (custom_in_layout << 1) | 0x01; } for (i = 0; i < pdata->out_channels; i++) { custom_out_layout = (custom_out_layout << 1) | 0x01; if (i < pdata->in_channels) { double *matrix_row = matrix + (stride * i); matrix_row[i] = 1.0; } } #if HAVE_FFMPEG_CH_LAYOUT AVChannelLayout ochl = {AV_CHANNEL_ORDER_UNSPEC, pdata->out_channels, {custom_out_layout}, NULL}; AVChannelLayout ichl = {AV_CHANNEL_ORDER_UNSPEC, pdata->in_channels, {custom_in_layout}, NULL}; av_opt_set_chlayout(pdata->ctx, "ochl", &ochl, 0); av_opt_set_chlayout(pdata->ctx, "ichl", &ichl, 0); #else av_opt_set_int(pdata->ctx, "ocl", custom_out_layout, 0); av_opt_set_int(pdata->ctx, "icl", custom_in_layout, 0); #endif error = swr_set_matrix(pdata->ctx, matrix, stride); av_free(matrix); if (error != 0) { swr_free(&pdata->ctx); mlt_log_error(service, "Unable to create custom matrix\n"); return error; } } error = swr_init(pdata->ctx); if (error != 0) { swr_free(&pdata->ctx); mlt_log_error(service, "Cannot initialize context\n"); return error; } // Allocate the channel buffer pointers pdata->in_buffers = av_calloc(pdata->in_channels, sizeof(uint8_t *)); pdata->out_buffers = av_calloc(pdata->out_channels, sizeof(uint8_t *)); return error; } void mlt_free_swr_context(mlt_swr_private_data *pdata) { if (pdata) { swr_free(&pdata->ctx); av_freep(&pdata->in_buffers); av_freep(&pdata->out_buffers); } } mlt-7.38.0/src/modules/avformat/common_swr.h000664 000000 000000 00000002575 15172202314 021005 0ustar00rootroot000000 000000 /* * common.h * Copyright (C) 2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_SWR_H #define COMMON_SWR_H #include #include typedef struct { SwrContext *ctx; uint8_t **in_buffers; uint8_t **out_buffers; mlt_audio_format in_format; mlt_audio_format out_format; int in_frequency; int out_frequency; int in_channels; int out_channels; mlt_channel_layout in_layout; mlt_channel_layout out_layout; } mlt_swr_private_data; int mlt_configure_swr_context(mlt_service service, mlt_swr_private_data *pdata); void mlt_free_swr_context(mlt_swr_private_data *pdata); #endif // COMMON_H mlt-7.38.0/src/modules/avformat/consumer_avformat.c000664 000000 000000 00000352033 15172202314 022344 0ustar00rootroot000000 000000 /* * consumer_avformat.c -- an encoder based on avformat * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" // mlt Header files #include #include #include #include #include // System header files #include #include #include #include #include #ifdef _MSC_VER #include #else #include #include #endif // avformat header files #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_AUDIO_STREAMS (8) #define MAX_SUBTITLE_STREAMS (8) #define AUDIO_ENCODE_BUFFER_SIZE (48000 * 2 * MAX_AUDIO_STREAMS) #define AUDIO_BUFFER_SIZE (1024 * 42) #define VIDEO_BUFFER_SIZE (8192 * 8192) #define IMAGE_ALIGN (4) // // This structure should be extended and made globally available in mlt // typedef struct { uint8_t *buffer; int size; int used; double time; int frequency; int channels; } * sample_fifo, sample_fifo_s; sample_fifo sample_fifo_init(int frequency, int channels) { sample_fifo fifo = calloc(1, sizeof(sample_fifo_s)); fifo->frequency = frequency; fifo->channels = channels; return fifo; } // count is the number of samples multiplied by the number of bytes per sample void sample_fifo_append(sample_fifo fifo, uint8_t *samples, int count) { if ((fifo->size - fifo->used) < count) { fifo->size += count * 5; fifo->buffer = realloc(fifo->buffer, fifo->size); } memcpy(&fifo->buffer[fifo->used], samples, count); fifo->used += count; } int sample_fifo_used(sample_fifo fifo) { return fifo->used; } int sample_fifo_fetch(sample_fifo fifo, uint8_t *samples, int count) { if (count > fifo->used) count = fifo->used; memcpy(samples, fifo->buffer, count); fifo->used -= count; memmove(fifo->buffer, &fifo->buffer[count], fifo->used); fifo->time += (double) count / fifo->channels / fifo->frequency; return count; } void sample_fifo_close(sample_fifo fifo) { free(fifo->buffer); free(fifo); } static AVFilterGraph *vfilter_graph; static int setup_hwupload_filter(mlt_properties properties, AVStream *stream, AVCodecContext *codec_context) { AVFilterContext *vfilter_in; AVFilterContext *vfilter_out; AVFilterContext *vfilter_hwupload; vfilter_graph = avfilter_graph_alloc(); mlt_properties_set_data(properties, "vfilter_graph", &vfilter_graph, 0, (mlt_destructor) avfilter_graph_free, NULL); // From ffplay.c:configure_video_filters(). char buffersrc_args[256]; snprintf(buffersrc_args, sizeof(buffersrc_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:frame_rate=%d/%d", codec_context->width, codec_context->height, AV_PIX_FMT_NV12, stream->time_base.num, stream->time_base.den, codec_context->sample_aspect_ratio.num, codec_context->sample_aspect_ratio.den, codec_context->time_base.den, codec_context->time_base.num); int result = avfilter_graph_create_filter(&vfilter_in, avfilter_get_by_name("buffer"), "mlt_buffer", buffersrc_args, NULL, vfilter_graph); if (result < 0) { mlt_log_error(MLT_CONSUMER(properties), "avfilter_graph_create_filter(buffer) failed with %d\n", result); return result; } vfilter_out = avfilter_graph_alloc_filter(vfilter_graph, avfilter_get_by_name("buffersink"), "mlt_buffersink"); if (!vfilter_out) { mlt_log_error(MLT_CONSUMER(properties), "avfilter_graph_alloc_filter(buffersink) failed\n"); return result; } enum AVPixelFormat pix_fmts[] = {codec_context->pix_fmt, AV_PIX_FMT_NONE}; #if LIBAVFILTER_VERSION_INT >= ((10 << 16) + (6 << 8) + 100) result = av_opt_set_array(vfilter_out, "pixel_formats", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_PIXEL_FMT, pix_fmts); #else result = av_opt_set_int_list(vfilter_out, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); #endif if (result < 0) { mlt_log_error(MLT_CONSUMER(properties), "av_opt_set_array() failed with %d\n", result); return result; } result = avfilter_init_dict(vfilter_out, NULL); if (result < 0) { mlt_log_error(MLT_CONSUMER(properties), "avfilter_init_dict() failed with %d\n", result); return result; } vfilter_hwupload = avfilter_graph_alloc_filter(vfilter_graph, avfilter_get_by_name("hwupload"), "mlt_hwupload"); if (!vfilter_hwupload) { mlt_log_error(MLT_CONSUMER(properties), "avfilter_graph_alloc_filter(hwupload) failed\n"); return result; } vfilter_hwupload->hw_device_ctx = av_buffer_ref(codec_context->hw_device_ctx); result = avfilter_init_dict(vfilter_hwupload, NULL); if (result < 0) { mlt_log_error(MLT_CONSUMER(properties), "avfilter_graph_create_filter(hwupload) failed with %d\n", result); return result; } result = avfilter_link(vfilter_in, 0, vfilter_hwupload, 0); if (result < 0) { mlt_log_error(MLT_CONSUMER(properties), "avfilter_link(vfilter_in, vfilter_hwupload) failed with %d\n", result); return result; } result = avfilter_link(vfilter_hwupload, 0, vfilter_out, 0); if (result < 0) { mlt_log_error(MLT_CONSUMER(properties), "avfilter_link(vfilter_hwupload, vfilter_out) failed with %d\n", result); return result; } result = avfilter_graph_config(vfilter_graph, NULL); if (result < 0) { mlt_log_error(MLT_CONSUMER(properties), "avfilter_graph_config() failed with %d\n", result); return result; } codec_context->hw_frames_ctx = av_buffer_ref(av_buffersink_get_hw_frames_ctx(vfilter_out)); mlt_properties_set_data(properties, "vfilter_in", vfilter_in, 0, NULL, NULL); mlt_properties_set_data(properties, "vfilter_out", vfilter_out, 0, NULL, NULL); return result; } static AVBufferRef *hw_device_ctx; static int init_vaapi(mlt_properties properties, AVCodecContext *codec_context) { int err = 0; const char *vaapi_device = mlt_properties_get(properties, "vaapi_device"); AVDictionary *opts = NULL; av_dict_set(&opts, "connection_type", mlt_properties_get(properties, "connection_type"), 0); av_dict_set(&opts, "driver", mlt_properties_get(properties, "driver"), 0); av_dict_set(&opts, "kernel_driver", mlt_properties_get(properties, "kernel_driver"), 0); if ((err = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, vaapi_device, opts, 0)) < 0) { mlt_log_warning(NULL, "Failed to create VAAPI device.\n"); } else { codec_context->hw_device_ctx = av_buffer_ref(hw_device_ctx); mlt_properties_set_data(properties, "hw_device_ctx", &hw_device_ctx, 0, (mlt_destructor) av_buffer_unref, NULL); } av_dict_free(&opts); return err; } #if HAVE_FFMPEG_VULKAN static int init_vulkan(mlt_properties properties, AVCodecContext *codec_context) { int err = 0; const char *vaapi_device = mlt_properties_get(properties, "vulkan_device"); AVDictionary *opts = NULL; if ((err = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VULKAN, vaapi_device, opts, 0)) < 0) { mlt_log_warning(NULL, "Failed to create Vulkan device.\n"); } else { codec_context->hw_device_ctx = av_buffer_ref(hw_device_ctx); mlt_properties_set_data(properties, "hw_device_ctx", &hw_device_ctx, 0, (mlt_destructor) av_buffer_unref, NULL); } av_dict_free(&opts); return err; } #endif // Forward references. static void property_changed(mlt_properties owner, mlt_consumer self, mlt_event_data); static int consumer_start(mlt_consumer consumer); static int consumer_stop(mlt_consumer consumer); static int consumer_is_stopped(mlt_consumer consumer); static void *consumer_thread(void *arg); static void consumer_close(mlt_consumer consumer); /** Initialise the consumer. */ mlt_consumer consumer_avformat_init(mlt_profile profile, char *arg) { // Allocate the consumer mlt_consumer consumer = mlt_consumer_new(profile); // If memory allocated and initialises without error if (consumer != NULL) { // Get properties from the consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Assign close callback consumer->close = consumer_close; // Interpret the argument if (arg != NULL) mlt_properties_set(properties, "target", arg); // sample and frame queue mlt_properties_set_data(properties, "frame_queue", mlt_deque_init(), 0, (mlt_destructor) mlt_deque_close, NULL); // Audio options not fully handled by AVOptions #define QSCALE_NONE (-99999) mlt_properties_set_int(properties, "aq", QSCALE_NONE); // Video options not fully handled by AVOptions mlt_properties_set_int(properties, "dc", 8); // Muxer options not fully handled by AVOptions mlt_properties_set_double(properties, "muxdelay", 0.7); mlt_properties_set_double(properties, "muxpreload", 0.5); // Ensure termination at end of the stream mlt_properties_set_int(properties, "terminate_on_pause", 1); // Default to separate processing threads for producer and consumer with no frame dropping! mlt_properties_set_int(properties, "real_time", -1); mlt_properties_set_int(properties, "prefill", 1); // Set up start/stop/terminated callbacks consumer->start = consumer_start; consumer->stop = consumer_stop; consumer->is_stopped = consumer_is_stopped; mlt_events_register(properties, "consumer-fatal-error"); mlt_event event = mlt_events_listen(properties, consumer, "property-changed", (mlt_listener) property_changed); mlt_properties_set_data(properties, "property-changed event", event, 0, NULL, NULL); } // Return consumer return consumer; } static void recompute_aspect_ratio(mlt_properties properties) { double ar = mlt_properties_get_double(properties, "aspect"); if (ar > 0.0) { AVRational rational = av_d2q(ar, 255); int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); // Update the profile and properties as well since this is an alias // for mlt properties that correspond to profile settings mlt_properties_set_int(properties, "display_aspect_num", rational.num); mlt_properties_set_int(properties, "display_aspect_den", rational.den); // Now compute the sample aspect ratio rational = av_d2q(ar * height / FFMAX(width, 1), 255); // Update the profile and properties as well since this is an alias // for mlt properties that correspond to profile settings mlt_properties_set_int(properties, "sample_aspect_num", rational.num); mlt_properties_set_int(properties, "sample_aspect_den", rational.den); } } static void color_trc_from_colorspace(mlt_properties properties) { // Default color transfer characteristic from MLT colorspace. const char *colorspace_str = mlt_properties_get(properties, "colorspace"); const mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); const mlt_color_trc trc = mlt_image_default_trc(colorspace); mlt_properties_set_int(properties, "color_trc", trc); } static void color_primaries_from_colorspace(mlt_properties properties) { // Default color_primaries from MLT colorspace. const char *colorspace_str = mlt_properties_get(properties, "colorspace"); mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); int height = mlt_properties_get_int(properties, "height"); mlt_color_primaries primaries = mlt_image_default_primaries(colorspace, height); if (primaries != mlt_color_pri_none) mlt_properties_set_int(properties, "color_primaries", primaries); } static void property_changed(mlt_properties owner, mlt_consumer self, mlt_event_data event_data) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(self); const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "s")) { // Obtain the size property char *size = mlt_properties_get(properties, "s"); int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); int tw, th; if (sscanf(size, "%dx%d", &tw, &th) == 2 && tw > 0 && th > 0) { width = tw; height = th; } else { mlt_log_warning(MLT_CONSUMER_SERVICE(self), "Invalid size property %s - ignoring.\n", size); } // Now ensure we honour the multiple of two requested by libavformat width = (width / 2) * 2; height = (height / 2) * 2; mlt_properties_set_int(properties, "width", width); mlt_properties_set_int(properties, "height", height); recompute_aspect_ratio(properties); } // "-aspect" on ffmpeg command line is display aspect ratio else if (!strcmp(name, "aspect") || !strcmp(name, "width") || !strcmp(name, "height")) { recompute_aspect_ratio(properties); } // Handle the ffmpeg command line "-r" property for frame rate else if (!strcmp(name, "r")) { double frame_rate = mlt_properties_get_double(properties, "r"); AVRational rational = av_d2q(frame_rate, 255); mlt_properties_set_int(properties, "frame_rate_num", rational.num); mlt_properties_set_int(properties, "frame_rate_den", rational.den); } // Apply AVOptions that are synonyms for standard mlt_consumer options else if (!strcmp(name, "ac")) { mlt_properties_set_int(properties, "channels", mlt_properties_get_int(properties, "ac")); } else if (!strcmp(name, "ar")) { mlt_properties_set_int(properties, "frequency", mlt_properties_get_int(properties, "ar")); } } /** Start the consumer. */ static int consumer_start(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); int error = 0; // Report information about available muxers and codecs as YAML Tiny char *s = mlt_properties_get(properties, "f"); if (s && strcmp(s, "list") == 0) { mlt_properties doc = mlt_properties_new(); mlt_properties formats = mlt_properties_new(); char key[20]; const AVOutputFormat *format = NULL; void *iterator = NULL; mlt_properties_set_data(properties, "f", formats, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set_data(doc, "formats", formats, 0, NULL, NULL); while ((format = av_muxer_iterate(&iterator))) { snprintf(key, sizeof(key), "%d", mlt_properties_count(formats)); mlt_properties_set(formats, key, format->name); } s = mlt_properties_serialise_yaml(doc); fprintf(stdout, "%s", s); free(s); mlt_properties_close(doc); error = 1; } s = mlt_properties_get(properties, "acodec"); if (s && strcmp(s, "list") == 0) { mlt_properties doc = mlt_properties_new(); mlt_properties codecs = mlt_properties_new(); char key[20]; const AVCodec *codec = NULL; mlt_properties_set_data(properties, "acodec", codecs, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set_data(doc, "audio_codecs", codecs, 0, NULL, NULL); void *iterator = NULL; while ((codec = av_codec_iterate(&iterator))) if (av_codec_is_encoder(codec) && codec->type == AVMEDIA_TYPE_AUDIO) { snprintf(key, sizeof(key), "%d", mlt_properties_count(codecs)); mlt_properties_set(codecs, key, codec->name); } s = mlt_properties_serialise_yaml(doc); fprintf(stdout, "%s", s); free(s); mlt_properties_close(doc); error = 1; } s = mlt_properties_get(properties, "vcodec"); if (s && strcmp(s, "list") == 0) { mlt_properties doc = mlt_properties_new(); mlt_properties codecs = mlt_properties_new(); char key[20]; const AVCodec *codec = NULL; void *iterator = NULL; mlt_properties_set_data(properties, "vcodec", codecs, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set_data(doc, "video_codecs", codecs, 0, NULL, NULL); while ((codec = av_codec_iterate(&iterator))) if (av_codec_is_encoder(codec) && codec->type == AVMEDIA_TYPE_VIDEO) { snprintf(key, sizeof(key), "%d", mlt_properties_count(codecs)); mlt_properties_set(codecs, key, codec->name); } s = mlt_properties_serialise_yaml(doc); fprintf(stdout, "%s", s); free(s); mlt_properties_close(doc); error = 1; } // Check that we're not already running if (!error && !mlt_properties_get_int(properties, "running")) { // Allocate a thread pthread_t *thread = calloc(1, sizeof(pthread_t)); mlt_event_block(mlt_properties_get_data(properties, "property-changed event", NULL)); // Because Movit only reads this on the first frame, // we must do this after properties have been set but before first frame request. if (!mlt_properties_get(properties, "color_trc")) color_trc_from_colorspace(properties); if (!mlt_properties_get(properties, "color_primaries")) color_primaries_from_colorspace(properties); // Assign the thread to properties mlt_properties_set_data(properties, "thread", thread, sizeof(pthread_t), free, NULL); // Create the thread pthread_create(thread, NULL, consumer_thread, consumer); // Set the running state mlt_properties_set_int(properties, "running", 1); } return error; } /** Stop the consumer. */ static int consumer_stop(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); pthread_t *thread = mlt_properties_get_data(properties, "thread", NULL); // Check that we're running if (thread) { // Stop the thread mlt_properties_set_int(properties, "running", 0); // Wait for termination pthread_join(*thread, NULL); mlt_properties_set_data(properties, "thread", NULL, 0, NULL, NULL); mlt_event_unblock(mlt_properties_get_data(properties, "property-changed event", NULL)); } return 0; } /** Determine if the consumer is stopped. */ static int consumer_is_stopped(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); return !mlt_properties_get_int(properties, "running"); } /** Process properties as AVOptions and apply to AV context obj */ static void apply_properties(void *obj, mlt_properties properties, int flags) { int i; int count = mlt_properties_count(properties); for (i = 0; i < count; i++) { const char *opt_name = mlt_properties_get_name(properties, i); int search_flags = AV_OPT_SEARCH_CHILDREN; const AVOption *opt = av_opt_find(obj, opt_name, NULL, flags, search_flags); // If option not found, see if it was prefixed with a or v (-vb) if (!opt && ((opt_name[0] == 'v' && (flags & AV_OPT_FLAG_VIDEO_PARAM)) || (opt_name[0] == 'a' && (flags & AV_OPT_FLAG_AUDIO_PARAM)))) opt = av_opt_find(obj, ++opt_name, NULL, flags, search_flags); // Apply option if found if (opt && strcmp(opt_name, "channel_layout")) av_opt_set(obj, opt_name, mlt_properties_get_value(properties, i), search_flags); } } static enum AVPixelFormat pick_pix_fmt(mlt_image_format img_fmt) { switch (img_fmt) { case mlt_image_rgb: return AV_PIX_FMT_RGB24; case mlt_image_rgba: return AV_PIX_FMT_RGBA; case mlt_image_yuv420p: return AV_PIX_FMT_YUV420P; case mlt_image_yuv422p16: return AV_PIX_FMT_YUV422P16LE; case mlt_image_yuv420p10: return AV_PIX_FMT_YUV420P10LE; case mlt_image_yuv444p10: return AV_PIX_FMT_YUV444P10LE; case mlt_image_rgba64: return AV_PIX_FMT_RGBA64LE; default: return AV_PIX_FMT_YUYV422; } } static int get_mlt_audio_format(int av_sample_fmt) { switch (av_sample_fmt) { case AV_SAMPLE_FMT_U8: return mlt_audio_u8; case AV_SAMPLE_FMT_S32: return mlt_audio_s32le; case AV_SAMPLE_FMT_FLT: return mlt_audio_f32le; case AV_SAMPLE_FMT_U8P: return mlt_audio_u8; case AV_SAMPLE_FMT_S32P: return mlt_audio_s32le; case AV_SAMPLE_FMT_FLTP: return mlt_audio_f32le; default: return mlt_audio_s16; } } static int pick_sample_fmt(mlt_properties properties, const AVCodec *codec) { int sample_fmt = AV_SAMPLE_FMT_S16; const char *format = mlt_properties_get(properties, "mlt_audio_format"); const int *p = codec->sample_fmts; const char *sample_fmt_str = mlt_properties_get(properties, "sample_fmt"); if (sample_fmt_str) sample_fmt = av_get_sample_fmt(sample_fmt_str); // get default av_sample_fmt from mlt_audio_format if (format && (!sample_fmt_str || sample_fmt == AV_SAMPLE_FMT_NONE)) { if (!strcmp(format, "s32le")) sample_fmt = AV_SAMPLE_FMT_S32; else if (!strcmp(format, "f32le")) sample_fmt = AV_SAMPLE_FMT_FLT; else if (!strcmp(format, "u8")) sample_fmt = AV_SAMPLE_FMT_U8; else if (!strcmp(format, "s32")) sample_fmt = AV_SAMPLE_FMT_S32P; else if (!strcmp(format, "float")) sample_fmt = AV_SAMPLE_FMT_FLTP; } // check if codec supports our mlt_audio_format for (; *p != -1; p++) { if (*p == sample_fmt) return sample_fmt; } // no match - pick first one we support for (p = codec->sample_fmts; *p != -1; p++) { switch (*p) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_FLTP: return *p; default: break; } } mlt_log_error(properties, "audio codec sample_fmt not compatible"); return AV_SAMPLE_FMT_NONE; } static uint8_t *interleaved_to_planar(int samples, int channels, uint8_t *audio, int bytes_per_sample) { uint8_t *buffer = mlt_pool_alloc(AUDIO_ENCODE_BUFFER_SIZE); uint8_t *p = buffer; int c; memset(buffer, 0, AUDIO_ENCODE_BUFFER_SIZE); for (c = 0; c < channels; c++) { uint8_t *q = audio + c * bytes_per_sample; int i = samples + 1; while (--i) { memcpy(p, q, bytes_per_sample); p += bytes_per_sample; q += channels * bytes_per_sample; } } return buffer; } /** Add an audio output stream */ static AVStream *add_audio_stream(mlt_consumer consumer, AVFormatContext *oc, const AVCodec *codec, AVCodecContext **codec_context, int channels, #if HAVE_FFMPEG_CH_LAYOUT AVChannelLayout *channel_layout #else int64_t channel_layout #endif ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Create a new stream AVStream *st = avformat_new_stream(oc, codec); // If created, then initialise from properties if (st != NULL) { AVCodecContext *c = *codec_context = avcodec_alloc_context3(codec); if (!c) { mlt_log_fatal(MLT_CONSUMER_SERVICE(consumer), "Failed to allocate the audio encoder context\n"); return NULL; } c->codec_id = codec->id; c->codec_type = AVMEDIA_TYPE_AUDIO; c->sample_fmt = pick_sample_fmt(properties, codec); #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_copy(&c->ch_layout, channel_layout); #else c->channel_layout = channel_layout; #endif // disabled until some audio codecs are multi-threaded #if 0 // Setup multi-threading int thread_count = mlt_properties_get_int( properties, "threads" ); if ( thread_count == 0 && getenv( "MLT_AVFORMAT_THREADS" ) ) thread_count = atoi( getenv( "MLT_AVFORMAT_THREADS" ) ); if ( thread_count >= 0 ) c->thread_count = thread_count; #endif if (oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // Allow the user to override the audio fourcc if (mlt_properties_get(properties, "atag")) { char *tail = NULL; char *arg = mlt_properties_get(properties, "atag"); int tag = strtol(arg, &tail, 0); if (!tail || *tail) tag = arg[0] + (arg[1] << 8) + (arg[2] << 16) + (arg[3] << 24); c->codec_tag = tag; } // Process properties as AVOptions char *apre = mlt_properties_get(properties, "apre"); if (apre) { mlt_properties p = mlt_properties_load(apre); apply_properties(c, p, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM); mlt_properties_close(p); } apply_properties(c, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM); int audio_qscale = mlt_properties_get_int(properties, "aq"); if (audio_qscale > QSCALE_NONE) { c->flags |= AV_CODEC_FLAG_QSCALE; c->global_quality = FF_QP2LAMBDA * audio_qscale; } // Set parameters controlled by MLT c->sample_rate = mlt_properties_get_int(properties, "frequency"); st->time_base = c->time_base = (AVRational){1, c->sample_rate}; #if HAVE_FFMPEG_CH_LAYOUT c->ch_layout.nb_channels = channels; #else c->channels = channels; #endif if (mlt_properties_get(properties, "alang") != NULL) { av_dict_set(&oc->metadata, "language", mlt_properties_get(properties, "alang"), 0); av_dict_set(&st->metadata, "language", mlt_properties_get(properties, "alang"), 0); } } else { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Could not allocate a stream for audio\n"); } return st; } static int open_audio(mlt_properties properties, AVFormatContext *oc, AVStream *st, const AVCodec *codec, AVCodecContext *c) { // We will return the audio input size from here int audio_input_frame_size = 0; // Process properties as AVOptions on the AVCodec if (codec && codec->priv_class) { char *apre = mlt_properties_get(properties, "apre"); if (apre) { mlt_properties p = mlt_properties_load(apre); apply_properties(c->priv_data, p, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM); mlt_properties_close(p); } apply_properties(c->priv_data, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM); } // Continue if codec found and we can open it if (codec && avcodec_open2(c, codec, NULL) >= 0) { #if LIBAVFORMAT_VERSION_INT < ((58 << 16) + (76 << 8) + 100) if (avcodec_copy_context(st->codec, c) < 0) { mlt_log_warning(NULL, "Failed to copy encoder parameters to output audio stream\n"); } #endif if (avcodec_parameters_from_context(st->codecpar, c) < 0) { mlt_log_warning(NULL, "Failed to copy encoder parameters to output audio stream\n"); return 0; } // ugly hack for PCM codecs (will be removed ASAP with new PCM // support to compute the input frame size in samples if (c->frame_size <= 1) audio_input_frame_size = 1; else audio_input_frame_size = c->frame_size; // Some formats want stream headers to be separate (hmm) if (!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp")) c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } else { mlt_log_warning(NULL, "%s: Unable to encode audio - disabling audio output.\n", __FILE__); audio_input_frame_size = 0; } return audio_input_frame_size; } /** Add a video output stream */ static AVStream *add_video_stream(mlt_consumer consumer, AVFormatContext *oc, const AVCodec *codec, AVCodecContext **codec_context) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Create a new stream AVStream *st = avformat_new_stream(oc, codec); if (st != NULL) { char *pix_fmt = mlt_properties_get(properties, "pix_fmt"); AVCodecContext *c = *codec_context = avcodec_alloc_context3(codec); if (!c) { mlt_log_fatal(MLT_CONSUMER_SERVICE(consumer), "Failed to allocate the video encoder context\n"); return NULL; } c->codec_id = codec->id; c->codec_type = AVMEDIA_TYPE_VIDEO; // Setup multi-threading int thread_count = mlt_properties_get_int(properties, "threads"); if (thread_count == 0 && getenv("MLT_AVFORMAT_THREADS")) thread_count = atoi(getenv("MLT_AVFORMAT_THREADS")); if (thread_count >= 0) c->thread_count = thread_count; // Process properties as AVOptions char *vpre = mlt_properties_get(properties, "vpre"); if (vpre) { mlt_properties p = mlt_properties_load(vpre); #ifdef AVDATADIR if (mlt_properties_count(p) < 1) { const AVCodec *codec = avcodec_find_encoder(c->codec_id); if (codec) { char *path = malloc(strlen(AVDATADIR) + strlen(codec->name) + strlen(vpre) + strlen(".ffpreset") + 2); strcpy(path, AVDATADIR); strcat(path, codec->name); strcat(path, "-"); strcat(path, vpre); strcat(path, ".ffpreset"); mlt_properties_close(p); p = mlt_properties_load(path); if (mlt_properties_count(p) > 0) mlt_properties_debug(p, path, stderr); free(path); } } else { mlt_properties_debug(p, vpre, stderr); } #endif apply_properties(c, p, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM); mlt_properties_close(p); } // Temporarily convert the mlt colorspace to av colorspace before applying the properties const char *colorspace_str = mlt_properties_get(properties, "colorspace"); mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); mlt_properties_clear(properties, "colorspace"); int av_colorspace = mlt_to_av_colorspace(colorspace, mlt_properties_get_int(properties, "height")); apply_properties(c, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM); mlt_properties_set_int(properties, "colorspace", colorspace); // Set options controlled by MLT c->width = mlt_properties_get_int(properties, "width"); c->height = mlt_properties_get_int(properties, "height"); c->time_base.num = mlt_properties_get_int(properties, "frame_rate_den"); c->time_base.den = mlt_properties_get_int(properties, "frame_rate_num"); st->time_base = c->time_base; st->avg_frame_rate = av_inv_q(c->time_base); c->framerate = av_inv_q(c->time_base); // Default to the codec's first pix_fmt if possible. c->pix_fmt = pix_fmt ? av_get_pix_fmt(pix_fmt) : codec ? (codec->pix_fmts ? codec->pix_fmts[0] : AV_PIX_FMT_YUV422P) : AV_PIX_FMT_YUV420P; if (AV_PIX_FMT_VAAPI == c->pix_fmt) { int result = init_vaapi(properties, c); if (result >= 0) { int result = setup_hwupload_filter(properties, st, c); if (result < 0) mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Failed to setup hwfilter: %d\n", result); } else { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Failed to initialize VA-API: %d\n", result); } } #if HAVE_FFMPEG_VULKAN else if (AV_PIX_FMT_VULKAN == c->pix_fmt) { int result = init_vulkan(properties, c); if (result >= 0) { int result = setup_hwupload_filter(properties, st, c); if (result < 0) mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Failed to setup hwfilter: %d\n", result); } else { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Failed to initialize Vulkan: %d\n", result); } } #endif c->colorspace = av_colorspace; if (mlt_properties_get(properties, "aspect")) { // "-aspect" on ffmpeg command line is display aspect ratio double ar = mlt_properties_get_double(properties, "aspect"); c->sample_aspect_ratio = av_d2q(ar * c->height / c->width, 255); } else { c->sample_aspect_ratio.num = mlt_properties_get_int(properties, "sample_aspect_num"); c->sample_aspect_ratio.den = mlt_properties_get_int(properties, "sample_aspect_den"); } st->sample_aspect_ratio = c->sample_aspect_ratio; if (mlt_properties_get_double(properties, "qscale") > 0) { c->flags |= AV_CODEC_FLAG_QSCALE; c->global_quality = FF_QP2LAMBDA * mlt_properties_get_double(properties, "qscale"); } // Allow the user to override the video fourcc if (mlt_properties_get(properties, "vtag")) { char *tail = NULL; const char *arg = mlt_properties_get(properties, "vtag"); int tag = strtol(arg, &tail, 0); if (!tail || *tail) tag = arg[0] + (arg[1] << 8) + (arg[2] << 16) + (arg[3] << 24); c->codec_tag = tag; } // Some formats want stream headers to be separate if (oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // Translate these standard mlt consumer properties to ffmpeg if (mlt_properties_get_int(properties, "progressive") == 0 && mlt_properties_get_int(properties, "deinterlace") == 0) { if (!mlt_properties_get(properties, "ildct") || mlt_properties_get_int(properties, "ildct")) c->flags |= AV_CODEC_FLAG_INTERLACED_DCT; if (!mlt_properties_get(properties, "ilme") || mlt_properties_get_int(properties, "ilme")) c->flags |= AV_CODEC_FLAG_INTERLACED_ME; } // parse the ratecontrol override string int i; char *rc_override = mlt_properties_get(properties, "rc_override"); for (i = 0; rc_override; i++) { int start, end, q; int e = sscanf(rc_override, "%d,%d,%d", &start, &end, &q); if (e != 3) mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Error parsing rc_override\n"); c->rc_override = av_realloc(c->rc_override, sizeof(RcOverride) * (i + 1)); c->rc_override[i].start_frame = start; c->rc_override[i].end_frame = end; if (q > 0) { c->rc_override[i].qscale = q; c->rc_override[i].quality_factor = 1.0; } else { c->rc_override[i].qscale = 0; c->rc_override[i].quality_factor = -q / 100.0; } rc_override = strchr(rc_override, '/'); if (rc_override) rc_override++; } c->rc_override_count = i; if (!c->rc_initial_buffer_occupancy) c->rc_initial_buffer_occupancy = c->rc_buffer_size * 3 / 4; c->intra_dc_precision = mlt_properties_get_int(properties, "dc") - 8; // Setup dual-pass i = mlt_properties_get_int(properties, "pass"); if (i == 1) c->flags |= AV_CODEC_FLAG_PASS1; else if (i == 2) c->flags |= AV_CODEC_FLAG_PASS2; #ifdef AV_CODEC_ID_H265 if (codec->id != AV_CODEC_ID_H265) #endif if (codec->id != AV_CODEC_ID_H264 && (c->flags & (AV_CODEC_FLAG_PASS1 | AV_CODEC_FLAG_PASS2))) { FILE *f; int size; char *logbuffer; char *filename; if (mlt_properties_get(properties, "passlogfile")) { filename = mlt_properties_get(properties, "passlogfile"); } else { char logfilename[1024]; snprintf(logfilename, sizeof(logfilename), "%s_2pass.log", mlt_properties_get(properties, "target")); mlt_properties_set(properties, "_logfilename", logfilename); filename = mlt_properties_get(properties, "_logfilename"); } if (c->flags & AV_CODEC_FLAG_PASS1) { f = mlt_fopen(filename, "w"); if (!f) perror(filename); else mlt_properties_set_data(properties, "_logfile", f, 0, (mlt_destructor) fclose, NULL); } else { /* read the log file */ f = mlt_fopen(filename, "r"); if (!f) { perror(filename); } else { fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); logbuffer = av_malloc(size + 1); if (!logbuffer) mlt_log_fatal(MLT_CONSUMER_SERVICE(consumer), "Could not allocate log buffer\n"); else { if (size >= 0) { size = fread(logbuffer, 1, size, f); logbuffer[size] = '\0'; c->stats_in = logbuffer; } } fclose(f); } } } } else { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Could not allocate a stream for video\n"); } return st; } static AVFrame *alloc_picture(int pix_fmt, int width, int height) { // Allocate a frame AVFrame *picture = av_frame_alloc(); if (picture) { int size = av_image_alloc(picture->data, picture->linesize, width, height, pix_fmt, IMAGE_ALIGN); if (size > 0) { picture->format = pix_fmt; picture->width = width; picture->height = height; } else { av_free(picture); picture = NULL; } } else { // Something failed - clean up what we can av_free(picture); picture = NULL; } return picture; } static int open_video(mlt_properties properties, AVFormatContext *oc, AVStream *st, const AVCodec *codec, AVCodecContext *video_enc) { // Process properties as AVOptions on the AVCodec if (codec && codec->priv_class) { char *vpre = mlt_properties_get(properties, "vpre"); if (vpre) { mlt_properties p = mlt_properties_load(vpre); apply_properties(video_enc->priv_data, p, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM); mlt_properties_close(p); } apply_properties(video_enc->priv_data, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM); } if (codec && codec->pix_fmts) { const enum AVPixelFormat *p = codec->pix_fmts; for (; *p != -1; p++) { if (*p == video_enc->pix_fmt) break; } if (*p == -1) video_enc->pix_fmt = codec->pix_fmts[0]; } const AVPixFmtDescriptor *srcDesc = av_pix_fmt_desc_get(video_enc->pix_fmt); if (srcDesc->flags & AV_PIX_FMT_FLAG_RGB) { video_enc->colorspace = AVCOL_SPC_RGB; } int result = codec && avcodec_open2(video_enc, codec, NULL) >= 0; if (result >= 0) { #if LIBAVFORMAT_VERSION_INT < ((58 << 16) + (76 << 8) + 100) result = avcodec_copy_context(st->codec, video_enc); if (!result) { mlt_log_warning(NULL, "Failed to copy encoder parameters to output video stream\n"); } #endif result = avcodec_parameters_from_context(st->codecpar, video_enc) >= 0; if (!result) { mlt_log_warning(NULL, "Failed to copy encoder parameters to output video stream\n"); } } else { mlt_log_warning(NULL, "%s: Unable to encode video - disabling video output.\n", __FILE__); } return result; } static inline long time_difference(struct timeval *time1) { struct timeval time2; gettimeofday(&time2, NULL); return time2.tv_sec * 1000000 + time2.tv_usec - time1->tv_sec * 1000000 - time1->tv_usec; } typedef struct { uint8_t *data; size_t size; } buffer_t; #if LIBAVFORMAT_VERSION_MAJOR >= 61 static int mlt_write(void *h, const uint8_t *buf, int size) #else static int mlt_write(void *h, uint8_t *buf, int size) #endif { mlt_properties properties = (mlt_properties) h; buffer_t buffer = {(uint8_t *) buf, size}; mlt_events_fire(properties, "avformat-write", mlt_event_data_from_object(&buffer)); return 0; } typedef struct encode_ctx_desc { mlt_consumer consumer; int audio_outbuf_size; int audio_input_frame_size; uint8_t audio_outbuf[AUDIO_BUFFER_SIZE], audio_buf_1[AUDIO_ENCODE_BUFFER_SIZE], audio_buf_2[AUDIO_ENCODE_BUFFER_SIZE]; int channels; int total_channels; int frequency; int sample_bytes; sample_fifo fifo; AVFormatContext *oc; AVStream *video_st; AVCodecContext *vcodec_ctx; AVStream *audio_st[MAX_AUDIO_STREAMS]; AVCodecContext *acodec_ctx[MAX_AUDIO_STREAMS]; int64_t sample_count[MAX_AUDIO_STREAMS]; // Used to store and override codec ids int video_codec_id; int audio_codec_id; int error_count; int frame_count; double audio_pts; double video_pts; int terminate_on_pause; int terminated; mlt_properties properties; mlt_properties frame_meta_properties; AVFrame *audio_avframe; mlt_position subtitle_prev_start[MAX_SUBTITLE_STREAMS]; AVStream *subtitle_st[MAX_SUBTITLE_STREAMS]; AVCodecContext *sdec_ctx[MAX_AUDIO_STREAMS]; AVCodecContext *senc_ctx[MAX_AUDIO_STREAMS]; AVStream *attached_pic_st; AVPacket *attached_pic_pkt; } encode_ctx_t; static int encode_audio(encode_ctx_t *ctx) { char key[27]; int i, j = 0, samples = ctx->audio_input_frame_size; int frame_length = ctx->audio_input_frame_size * ctx->channels * ctx->sample_bytes; // Get samples count to fetch from fifo if (sample_fifo_used(ctx->fifo) < frame_length) { samples = sample_fifo_used(ctx->fifo) / (ctx->channels * ctx->sample_bytes); } else if (ctx->audio_input_frame_size == 1) { // PCM consumes as much as possible. samples = FFMIN(sample_fifo_used(ctx->fifo), AUDIO_ENCODE_BUFFER_SIZE) / frame_length; } // Get the audio samples if (samples > 0) { sample_fifo_fetch(ctx->fifo, ctx->audio_buf_1, samples * ctx->sample_bytes * ctx->channels); } else if (samples == 0) { // Return done return 1; } else if (ctx->audio_codec_id == AV_CODEC_ID_VORBIS && ctx->terminated) { // This prevents an infinite loop when some versions of vorbis do not // increment pts when encoding silence. ctx->audio_pts = ctx->video_pts; return 1; } else { memset(ctx->audio_buf_1, 0, AUDIO_ENCODE_BUFFER_SIZE); } // For each output stream for (i = 0; i < MAX_AUDIO_STREAMS && ctx->audio_st[i] && j < ctx->total_channels; i++) { AVStream *stream = ctx->audio_st[i]; AVCodecContext *codec = ctx->acodec_ctx[i]; AVPacket pkt; av_init_packet(&pkt); pkt.data = ctx->audio_outbuf; pkt.size = ctx->audio_outbuf_size; // Optimized for single track and no channel remap if (!ctx->audio_st[1] && !mlt_properties_count(ctx->frame_meta_properties)) { void *p = ctx->audio_buf_1; if (codec->sample_fmt == AV_SAMPLE_FMT_FLTP) p = interleaved_to_planar(samples, ctx->channels, p, sizeof(float)); else if (codec->sample_fmt == AV_SAMPLE_FMT_S16P) p = interleaved_to_planar(samples, ctx->channels, p, sizeof(int16_t)); else if (codec->sample_fmt == AV_SAMPLE_FMT_S32P) p = interleaved_to_planar(samples, ctx->channels, p, sizeof(int32_t)); else if (codec->sample_fmt == AV_SAMPLE_FMT_U8P) p = interleaved_to_planar(samples, ctx->channels, p, sizeof(uint8_t)); ctx->audio_avframe->nb_samples = FFMAX(samples, ctx->audio_input_frame_size); ctx->audio_avframe->pts = ctx->sample_count[i]; ctx->sample_count[i] += ctx->audio_avframe->nb_samples; avcodec_fill_audio_frame(ctx->audio_avframe, #if HAVE_FFMPEG_CH_LAYOUT codec->ch_layout.nb_channels, #else codec->channels, #endif codec->sample_fmt, (const uint8_t *) p, AUDIO_ENCODE_BUFFER_SIZE, 0); int ret = avcodec_send_frame(codec, samples ? ctx->audio_avframe : NULL); if (ret < 0) { pkt.size = ret; } else { ret = avcodec_receive_packet(codec, &pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) pkt.size = 0; else if (ret < 0) pkt.size = ret; } if (p != ctx->audio_buf_1) mlt_pool_release(p); } else { // Extract the audio channels according to channel mapping int dest_offset = 0; // channel offset into interleaved dest buffer // Get the number of channels for this stream sprintf(key, "channels.%d", i); int current_channels = mlt_properties_get_int(ctx->properties, key); // Clear the destination audio buffer. memset(ctx->audio_buf_2, 0, AUDIO_ENCODE_BUFFER_SIZE); // For each output channel while (dest_offset < current_channels && j < ctx->total_channels) { int map_start = -1, map_channels = 0; int source_offset = 0; int k; // Look for a mapping that starts at j for (k = 0; k < (MAX_AUDIO_STREAMS * 2) && map_start != j; k++) { sprintf(key, "%d.channels", k); map_channels = mlt_properties_get_int(ctx->frame_meta_properties, key); sprintf(key, "%d.start", k); if (mlt_properties_get(ctx->frame_meta_properties, key)) map_start = mlt_properties_get_int(ctx->frame_meta_properties, key); if (map_start != j) source_offset += map_channels; } // If no mapping if (map_start != j) { map_channels = current_channels; source_offset = j; } // Copy samples if source offset valid if (source_offset < ctx->channels) { // Interleave the audio buffer with the # channels for this stream/mapping. for (k = 0; k < map_channels; k++, j++, source_offset++, dest_offset++) { uint8_t *src = ctx->audio_buf_1 + source_offset * ctx->sample_bytes; uint8_t *dest = ctx->audio_buf_2 + dest_offset * ctx->sample_bytes; int s = samples + 1; while (--s) { memcpy(dest, src, ctx->sample_bytes); dest += current_channels * ctx->sample_bytes; src += ctx->channels * ctx->sample_bytes; } } } // Otherwise silence else { j += current_channels; dest_offset += current_channels; } } ctx->audio_avframe->nb_samples = FFMAX(samples, ctx->audio_input_frame_size); ctx->audio_avframe->pts = ctx->sample_count[i]; ctx->sample_count[i] += ctx->audio_avframe->nb_samples; avcodec_fill_audio_frame(ctx->audio_avframe, #if HAVE_FFMPEG_CH_LAYOUT codec->ch_layout.nb_channels, #else codec->channels, #endif codec->sample_fmt, (const uint8_t *) ctx->audio_buf_2, AUDIO_ENCODE_BUFFER_SIZE, 0); int ret = avcodec_send_frame(codec, samples ? ctx->audio_avframe : NULL); if (ret < 0) { pkt.size = ret; } else { receive_audio_packet: ret = avcodec_receive_packet(codec, &pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) pkt.size = 0; else if (ret < 0) pkt.size = ret; } } if (pkt.size > 0) { // Write the compressed frame in the media file av_packet_rescale_ts(&pkt, codec->time_base, stream->time_base); pkt.stream_index = stream->index; if (av_interleaved_write_frame(ctx->oc, &pkt)) { mlt_log_fatal(MLT_CONSUMER_SERVICE(ctx->consumer), "error writing audio frame\n"); mlt_events_fire(ctx->properties, "consumer-fatal-error", mlt_event_data_none()); return -1; } ctx->error_count = 0; mlt_log_debug(MLT_CONSUMER_SERVICE(ctx->consumer), "audio stream %d pkt pts %" PRId64 " frame_size %d\n", stream->index, pkt.pts, codec->frame_size); goto receive_audio_packet; } else if (pkt.size < 0) { mlt_log_warning(MLT_CONSUMER_SERVICE(ctx->consumer), "error with audio encode: %d (frame %d)\n", pkt.size, ctx->frame_count); if (++ctx->error_count > 2) return -1; } if (i == 0) { ctx->audio_pts = (double) ctx->sample_count[0] * av_q2d(codec->time_base); } } return 0; } static int flush_audio_encoders(encode_ctx_t *ctx) { for (int i = 0; i < MAX_AUDIO_STREAMS && ctx->audio_st[i]; i++) { AVStream *stream = ctx->audio_st[i]; AVCodecContext *codec = ctx->acodec_ctx[i]; int ret = avcodec_send_frame(codec, NULL); if (ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) { mlt_log_warning(MLT_CONSUMER_SERVICE(ctx->consumer), "audio flush send_frame failed: %d\n", ret); return -1; } while (1) { AVPacket pkt; av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; ret = avcodec_receive_packet(codec, &pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { mlt_log_debug(MLT_CONSUMER_SERVICE(ctx->consumer), "audio flush complete for stream %d\n", stream->index); break; } else if (ret < 0) { mlt_log_warning(MLT_CONSUMER_SERVICE(ctx->consumer), "audio flush receive_packet failed: %d\n", ret); return -1; } mlt_log_debug(MLT_CONSUMER_SERVICE(ctx->consumer), "flushed audio stream %d pkt pts %" PRId64 " size %d\n", stream->index, pkt.pts, pkt.size); av_packet_rescale_ts(&pkt, codec->time_base, stream->time_base); pkt.stream_index = stream->index; if (av_interleaved_write_frame(ctx->oc, &pkt)) { av_packet_unref(&pkt); mlt_log_fatal(MLT_CONSUMER_SERVICE(ctx->consumer), "error writing flushed audio packet\n"); mlt_events_fire(ctx->properties, "consumer-fatal-error", mlt_event_data_none()); return -1; } av_packet_unref(&pkt); } } return 0; } static uint32_t read_be32(const uint8_t *p) { return ((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16) | ((uint32_t) p[2] << 8) | (uint32_t) p[3]; } static enum AVCodecID detect_attached_pic_codec_id(const uint8_t *data, size_t size) { if (!data || size < 4) return AV_CODEC_ID_NONE; if (size >= 3 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF) return AV_CODEC_ID_MJPEG; if (size >= 8 && data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 && data[4] == 0x0D && data[5] == 0x0A && data[6] == 0x1A && data[7] == 0x0A) return AV_CODEC_ID_PNG; return AV_CODEC_ID_NONE; } static int attached_pic_get_dimensions( enum AVCodecID codec_id, const uint8_t *data, size_t size, int *width, int *height) { if (!data || size < 4 || !width || !height) return 0; switch (codec_id) { case AV_CODEC_ID_MJPEG: if (data[0] != 0xFF || data[1] != 0xD8) return 0; size_t pos = 2; while (pos + 3 < size) { if (data[pos] != 0xFF) { pos++; continue; } while (pos < size && data[pos] == 0xFF) pos++; if (pos >= size) break; const uint8_t marker = data[pos++]; if (marker == 0xD8 || marker == 0xD9 || marker == 0x01 || (marker >= 0xD0 && marker <= 0xD7)) continue; if (pos + 1 >= size) break; const int segment_len = (data[pos] << 8) | data[pos + 1]; pos += 2; if (segment_len < 2 || pos + (size_t) (segment_len - 2) > size) break; if ((marker >= 0xC0 && marker <= 0xC3) || (marker >= 0xC5 && marker <= 0xC7) || (marker >= 0xC9 && marker <= 0xCB) || (marker >= 0xCD && marker <= 0xCF)) { if (segment_len < 7) return 0; *height = (data[pos + 1] << 8) | data[pos + 2]; *width = (data[pos + 3] << 8) | data[pos + 4]; return *width > 0 && *height > 0; } pos += (size_t) (segment_len - 2); } return 0; case AV_CODEC_ID_PNG: if (size >= 24 && !memcmp(data, "\x89PNG\r\n\x1a\n", 8)) { *width = (int) read_be32(data + 16); *height = (int) read_be32(data + 20); return *width > 0 && *height > 0; } return 0; default: return 0; } } /** Add an attached picture (cover art) stream from an image file path. */ static AVStream *add_attached_pic_stream(mlt_consumer consumer, encode_ctx_t *ctx, const char *pic_path) { if (!pic_path || !pic_path[0]) { mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "attached_pic: path is empty\n"); return NULL; } // Open the image file FILE *f = fopen(pic_path, "rb"); if (!f) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: could not open '%s'\n", pic_path); return NULL; } // Determine file size fseek(f, 0, SEEK_END); long file_size = ftell(f); fseek(f, 0, SEEK_SET); if (file_size <= 0) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: invalid or empty file '%s'\n", pic_path); fclose(f); return NULL; } // Allocate packet and read image data into it ctx->attached_pic_pkt = av_packet_alloc(); if (!ctx->attached_pic_pkt) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: could not allocate packet\n"); fclose(f); return NULL; } if (av_new_packet(ctx->attached_pic_pkt, (int) file_size) < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: could not allocate packet\n"); av_packet_free(&ctx->attached_pic_pkt); fclose(f); return NULL; } if (fread(ctx->attached_pic_pkt->data, 1, (size_t) file_size, f) != (size_t) file_size) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: could not read file '%s'\n", pic_path); av_packet_free(&ctx->attached_pic_pkt); fclose(f); return NULL; } fclose(f); enum AVCodecID pic_codec_id = detect_attached_pic_codec_id(ctx->attached_pic_pkt->data, (size_t) ctx->attached_pic_pkt->size); if (pic_codec_id != AV_CODEC_ID_MJPEG && pic_codec_id != AV_CODEC_ID_PNG) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: only JPEG and PNG are supported for '%s'\n", pic_path); av_packet_free(&ctx->attached_pic_pkt); return NULL; } int width = 0; int height = 0; if (attached_pic_get_dimensions(pic_codec_id, ctx->attached_pic_pkt->data, (size_t) ctx->attached_pic_pkt->size, &width, &height)) { mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "attached_pic: detected dimensions %dx%d for '%s'\n", width, height, pic_path); } else { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: failed to detect image dimensions in '%s'\n", pic_path); av_packet_free(&ctx->attached_pic_pkt); return NULL; } // For Matroska, pre-allocate the extradata buffer before creating the stream // so that if this allocation fails we have not yet modified ctx->oc. uint8_t *mkv_extradata = NULL; if (!strcmp(ctx->oc->oformat->name, "matroska")) { mkv_extradata = av_mallocz(ctx->attached_pic_pkt->size + AV_INPUT_BUFFER_PADDING_SIZE); if (!mkv_extradata) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: could not allocate extradata\n"); av_packet_free(&ctx->attached_pic_pkt); return NULL; } memcpy(mkv_extradata, ctx->attached_pic_pkt->data, ctx->attached_pic_pkt->size); } // Create new stream and mark it as an attached picture AVStream *st = avformat_new_stream(ctx->oc, NULL); if (!st) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "attached_pic: could not allocate stream\n"); av_packet_free(&ctx->attached_pic_pkt); av_free(mkv_extradata); return NULL; } st->codecpar->codec_id = pic_codec_id; // Matroska uses AVMEDIA_TYPE_ATTACHMENT with data in extradata and requires // "filename" and "mimetype" metadata. Other formats (MP3, M4A, FLAC, OGG) // use AVMEDIA_TYPE_VIDEO + AV_DISPOSITION_ATTACHED_PIC with a packet write. if (mkv_extradata) { st->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT; st->codecpar->extradata = mkv_extradata; st->codecpar->extradata_size = ctx->attached_pic_pkt->size; av_dict_set(&st->metadata, "filename", (pic_codec_id == AV_CODEC_ID_PNG) ? "cover.png" : "cover.jpg", 0); av_dict_set(&st->metadata, "mimetype", (pic_codec_id == AV_CODEC_ID_PNG) ? "image/png" : "image/jpeg", 0); av_dict_set(&st->metadata, "title", "Cover", 0); // The attachment is written by the muxer during avformat_write_header; // no packet needs to be sent. av_packet_free(&ctx->attached_pic_pkt); } else { st->disposition = AV_DISPOSITION_ATTACHED_PIC; st->time_base = (AVRational){1, 90000}; st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->width = width; st->codecpar->height = height; av_dict_set(&st->metadata, "title", "Cover", 0); av_dict_set(&st->metadata, "comment", "Cover (front)", 0); ctx->attached_pic_pkt->stream_index = st->index; ctx->attached_pic_pkt->flags |= AV_PKT_FLAG_KEY; ctx->attached_pic_pkt->pts = 0; ctx->attached_pic_pkt->dts = 0; ctx->attached_pic_pkt->duration = 0; ctx->attached_pic_pkt->pos = -1; } mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "attached_pic: added cover art from '%s'\n", pic_path); return st; } static void open_subtitles(encode_ctx_t *ctx) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(ctx->consumer); memset(ctx->subtitle_st, 0, sizeof(ctx->subtitle_st)); memset(ctx->sdec_ctx, 0, sizeof(ctx->sdec_ctx)); memset(ctx->senc_ctx, 0, sizeof(ctx->senc_ctx)); for (int p = 0; p < MAX_SUBTITLE_STREAMS; p++) { ctx->subtitle_prev_start[p] = -1; } if (!mlt_properties_get(properties, "subtitle.0.feed") || mlt_properties_get_int(properties, "sn")) { // No subtitles requested return; } enum AVCodecID codec_id = AV_CODEC_ID_SUBRIP; if (avformat_query_codec(ctx->oc->oformat, codec_id, FF_COMPLIANCE_UNOFFICIAL) != 1) { codec_id = AV_CODEC_ID_ASS; if (avformat_query_codec(ctx->oc->oformat, codec_id, FF_COMPLIANCE_UNOFFICIAL) != 1) { codec_id = AV_CODEC_ID_MOV_TEXT; if (avformat_query_codec(ctx->oc->oformat, codec_id, FF_COMPLIANCE_UNOFFICIAL) != 1) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Container not compatible with subtitles.\n"); return; } } } for (int i = 0; i < MAX_SUBTITLE_STREAMS; i++) { char key[20]; snprintf(key, sizeof(key), "subtitle.%d.feed", i); char *feed = mlt_properties_get(properties, key); if (!feed) { break; } // Set up the stream ctx->subtitle_st[i] = avformat_new_stream(ctx->oc, NULL); if (!ctx->subtitle_st[i]) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Failed to allocate the subtitle stream %d\n", i); break; } ctx->subtitle_st[i]->time_base = (AVRational){1, 1000}; ctx->subtitle_st[i]->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; ctx->subtitle_st[i]->codecpar->codec_id = codec_id; snprintf(key, sizeof(key), "subtitle.%d.lang", i); if (mlt_properties_get(properties, key) != NULL) av_dict_set(&ctx->subtitle_st[i]->metadata, "language", mlt_properties_get(properties, key), 0); // Set up transcoding for non-subrip subtitle types if (codec_id != AV_CODEC_ID_SUBRIP) { // Set up the SRT decoder const AVCodec *sdec = avcodec_find_decoder(AV_CODEC_ID_SUBRIP); if (!sdec) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Unable to find subrip decoder\n"); break; } ctx->sdec_ctx[i] = avcodec_alloc_context3(sdec); if (!ctx->sdec_ctx[i]) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Unable to create subrip decoder\n"); ctx->subtitle_st[i] = NULL; break; } if (avcodec_open2(ctx->sdec_ctx[i], sdec, NULL) < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Unable to open subrip decoder\n"); ctx->subtitle_st[i] = NULL; break; } // Set up the subtitle encoder const AVCodec *senc = avcodec_find_encoder(codec_id); if (!senc) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Unable to find subtitle encoder\n"); ctx->subtitle_st[i] = NULL; break; } ctx->senc_ctx[i] = avcodec_alloc_context3(senc); if (!ctx->senc_ctx[i]) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Unable to create subtitle encoder\n"); ctx->subtitle_st[i] = NULL; break; } ctx->senc_ctx[i]->time_base = ctx->subtitle_st[i]->time_base; const char *subtitle_header = "[Script Info]\n" "ScriptType: v4.00+\n" "\n" "[V4+ Styles]\n" "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, " "OutlineColour, " "BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, " "Angle, " "BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, " "Encoding\n" "Style: Default,Cataneo BT,16,16777215,16777215,16777215," "12632256,1,0,0,0,100,100,0,0," "1,1,0,2,10,10,10,1\n" "\n" "[Events]\n" "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, " "Text\n"; ctx->senc_ctx[i]->subtitle_header = (uint8_t *) av_strdup(subtitle_header); ctx->senc_ctx[i]->subtitle_header_size = strlen(subtitle_header); if (avcodec_open2(ctx->senc_ctx[i], senc, NULL) < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Unable to open subtitle encoder\n"); ctx->subtitle_st[i] = NULL; break; } } } } static void encode_subtitles(encode_ctx_t *ctx, mlt_frame frame) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties subtitle_properties = mlt_properties_get_properties(frame_properties, "subtitles"); if (!subtitle_properties) { return; } for (int i = 0; i < MAX_SUBTITLE_STREAMS; i++) { if (!ctx->subtitle_st[i]) { break; } // Get the subtitles from the feed char key[20]; snprintf(key, sizeof(key), "subtitle.%d.feed", i); char *feed = mlt_properties_get(MLT_CONSUMER_PROPERTIES(ctx->consumer), key); mlt_properties feed_properties = mlt_properties_get_properties(subtitle_properties, feed); if (!feed_properties) { break; } mlt_position start_frame = mlt_properties_get_position(feed_properties, "start"); if (ctx->subtitle_prev_start[i] >= start_frame) { // Skip duplicates. This is normal since subtitles are repeated on each frame for their duration. continue; } ctx->subtitle_prev_start[i] = start_frame; mlt_position end_frame = mlt_properties_get_position(feed_properties, "end"); char *text = mlt_properties_get(feed_properties, "text"); if (!text || !text[0]) { // Skip empty text. continue; } // Pack the SRT subtitles in a packet mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(ctx->consumer)); AVPacket pkt; if (av_new_packet(&pkt, strlen(text))) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "failed to allocate packet 1\n"); return; } strncpy((char *) pkt.data, text, pkt.size); // Transcode to another format if necessary if (ctx->subtitle_st[i]->codecpar->codec_id != AV_CODEC_ID_SUBRIP) { AVSubtitle avsubtitle; int got_sub; int ret = avcodec_decode_subtitle2(ctx->sdec_ctx[i], &avsubtitle, &got_sub, &pkt); av_packet_unref(&pkt); if (ret < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "failed to decode subtitles\n"); continue; } if (!got_sub) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "No subtitle received from decoder\n"); continue; } const int MAX_SUBTITLE_PACKET_SIZE = 1024 * 1024; if (av_new_packet(&pkt, MAX_SUBTITLE_PACKET_SIZE)) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "failed to allocate packet 2\n"); return; } int subtitle_out_size = avcodec_encode_subtitle(ctx->senc_ctx[i], pkt.data, pkt.size, &avsubtitle); avsubtitle_free(&avsubtitle); if (subtitle_out_size < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "Subtitle encoding failed\n"); av_packet_unref(&pkt); continue; } av_shrink_packet(&pkt, subtitle_out_size); } pkt.pts = (int64_t) start_frame * 1000 * profile->frame_rate_den / profile->frame_rate_num; pkt.dts = AV_NOPTS_VALUE; pkt.duration = (int64_t) (end_frame - start_frame) * 1000 * profile->frame_rate_den / profile->frame_rate_num; pkt.stream_index = ctx->subtitle_st[i]->index; // Send the packet to the output if (av_interleaved_write_frame(ctx->oc, &pkt)) { mlt_log_error(MLT_CONSUMER_SERVICE(ctx->consumer), "error writing subtitle frame\n"); av_packet_unref(&pkt); break; } } } static int encode_video(encode_ctx_t *enc_ctx, AVFrame *converted_avframe, uint8_t *video_outbuf, int video_outbuf_size, mlt_frame frame, mlt_image_format img_fmt) { int ret = 0; mlt_properties properties = enc_ctx->properties; const char *dst_colorspace_str = mlt_properties_get(properties, "colorspace"); mlt_colorspace dst_colorspace = mlt_image_colorspace_id(dst_colorspace_str); const char *color_range = mlt_properties_get(properties, "color_range"); int dst_full_range = mlt_image_full_range(color_range); AVCodecContext *c = enc_ctx->vcodec_ctx; AVFrame *avframe = NULL; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_service service = MLT_CONSUMER_SERVICE(enc_ctx->consumer); if (mlt_properties_get_int(frame_properties, "rendered")) { uint8_t *image; int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); int img_width = width; int img_height = height; AVFrame video_avframe; int is_interlaced_chroma_correction = 0; mlt_frame_get_image(frame, &image, &img_fmt, &img_width, &img_height, 0); // Interlaced 420 correction if (!mlt_properties_get_int(frame_properties, "progressive") && converted_avframe->format == AV_PIX_FMT_YUV420P // dst && img_fmt == mlt_image_yuv422 // src. It looks like rgb and 444 go as 422 too. && height % 4 == 0 // because reducing twice && width == converted_avframe->linesize[1] * 2) // if != things become too complicated { width *= 2; // substitute resolution, to appear each half-frame side-by-side height /= 2; for (int i = 0; i < 3; ++i) converted_avframe->linesize[i] *= 2; is_interlaced_chroma_correction = 1; mlt_log_debug(service, "interlaced chroma correction is activated\n"); } mlt_image_format_planes(img_fmt, width, height, image, video_avframe.data, video_avframe.linesize); // Do the colour space conversion int srcfmt = pick_pix_fmt(img_fmt); int flags = mlt_get_sws_flags(width, height, srcfmt, width, height, converted_avframe->format); struct SwsContext *context = sws_getContext(width, height, srcfmt, width, height, converted_avframe->format, flags, NULL, NULL, NULL); const char *src_colorspace_str = mlt_properties_get(frame_properties, "colorspace"); mlt_colorspace src_colorspace = mlt_image_colorspace_id(src_colorspace_str); int src_full_range = mlt_properties_get_int(frame_properties, "full_range"); mlt_set_luma_transfer(context, src_colorspace, dst_colorspace, src_full_range, dst_full_range); sws_scale(context, (const uint8_t *const *) video_avframe.data, video_avframe.linesize, 0, height, converted_avframe->data, converted_avframe->linesize); sws_freeContext(context); if (is_interlaced_chroma_correction) // restoring everything back { width /= 2; height *= 2; for (int i = 0; i < 3; ++i) converted_avframe->linesize[i] /= 2; } mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); // Apply the alpha if applicable if (!mlt_properties_get(properties, "mlt_image_format") || strcmp(mlt_properties_get(properties, "mlt_image_format"), "rgba")) if (c->pix_fmt == AV_PIX_FMT_RGBA || c->pix_fmt == AV_PIX_FMT_ARGB || c->pix_fmt == AV_PIX_FMT_BGRA) { uint8_t *p; uint8_t *alpha = mlt_frame_get_alpha(frame); if (alpha) { register int n; for (int i = 0; i < height; i++) { n = (width + 7) / 8; p = converted_avframe->data[0] + i * converted_avframe->linesize[0] + 3; switch (width % 8) { case 0: do { *p = *alpha++; p += 4; case 7: *p = *alpha++; p += 4; case 6: *p = *alpha++; p += 4; case 5: *p = *alpha++; p += 4; case 4: *p = *alpha++; p += 4; case 3: *p = *alpha++; p += 4; case 2: *p = *alpha++; p += 4; case 1: *p = *alpha++; p += 4; } while (--n); } } } else { for (int i = 0; i < height; i++) { int n = width; uint8_t *p = converted_avframe->data[0] + i * converted_avframe->linesize[0] + 3; while (n) { *p = 255; p += 4; n--; } } } } if (AV_PIX_FMT_VAAPI == c->pix_fmt #if HAVE_FFMPEG_VULKAN || AV_PIX_FMT_VULKAN == c->pix_fmt #endif ) { AVFilterContext *vfilter_in = mlt_properties_get_data(properties, "vfilter_in", NULL); AVFilterContext *vfilter_out = mlt_properties_get_data(properties, "vfilter_out", NULL); if (vfilter_in && vfilter_out) { if (!avframe) avframe = av_frame_alloc(); ret = av_buffersrc_add_frame(vfilter_in, converted_avframe); ret = av_buffersink_get_frame(vfilter_out, avframe); if (ret < 0) { mlt_log_warning(service, "error with hwupload: %d (frame %d)\n", ret, enc_ctx->frame_count); if (++enc_ctx->error_count > 2) return -1; ret = 0; } } } else { avframe = converted_avframe; } } #ifdef AVFMT_RAWPICTURE if (enc_ctx->oc->oformat->flags & AVFMT_RAWPICTURE) { // raw video case. The API will change slightly in the near future for that AVPacket pkt; av_init_packet(&pkt); // Set frame interlace hints if (mlt_properties_get_int(frame_properties, "progressive")) c->field_order = AV_FIELD_PROGRESSIVE; else c->field_order = (mlt_properties_get_int(frame_properties, "top_field_first")) ? AV_FIELD_TB : AV_FIELD_BT; pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = enc_ctx->video_st->index; pkt.data = (uint8_t *) avframe; pkt.size = sizeof(AVPicture); ret = av_write_frame(enc_ctx->oc, &pkt); } else #endif { AVPacket pkt; av_init_packet(&pkt); if (c->codec->id == AV_CODEC_ID_RAWVIDEO) { pkt.data = NULL; pkt.size = 0; } else { pkt.data = video_outbuf; pkt.size = video_outbuf_size; } // Set the quality avframe->quality = c->global_quality; avframe->pts = enc_ctx->frame_count; // Set frame interlace hints #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) if (!mlt_properties_get_int(frame_properties, "progressive")) avframe->flags |= AV_FRAME_FLAG_INTERLACED; else avframe->flags &= ~AV_FRAME_FLAG_INTERLACED; const int tff = mlt_properties_get_int(frame_properties, "top_field_first"); if (tff) avframe->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; else avframe->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; #else avframe->interlaced_frame = !mlt_properties_get_int(frame_properties, "progressive"); const int tff = avframe->top_field_first = mlt_properties_get_int(frame_properties, "top_field_first"); #endif if (mlt_properties_get_int(frame_properties, "progressive")) c->field_order = AV_FIELD_PROGRESSIVE; else if (c->codec_id == AV_CODEC_ID_MJPEG) c->field_order = tff ? AV_FIELD_TT : AV_FIELD_BB; else c->field_order = tff ? AV_FIELD_TB : AV_FIELD_BT; // Encode the image ret = avcodec_send_frame(c, avframe); if (ret < 0) { pkt.size = ret; } else { receive_video_packet: ret = avcodec_receive_packet(c, &pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) pkt.size = ret = 0; else if (ret < 0) pkt.size = ret; } // If zero size, it means the image was buffered if (pkt.size > 0) { av_packet_rescale_ts(&pkt, c->time_base, enc_ctx->video_st->time_base); pkt.stream_index = enc_ctx->video_st->index; // write the compressed frame in the media file ret = av_interleaved_write_frame(enc_ctx->oc, &pkt); mlt_log_debug(service, " frame_size %d\n", c->frame_size); // Dual pass logging if (mlt_properties_get_data(properties, "_logfile", NULL) && c->stats_out) fprintf(mlt_properties_get_data(properties, "_logfile", NULL), "%s", c->stats_out); enc_ctx->error_count = 0; if (!ret) goto receive_video_packet; } else if (pkt.size < 0) { mlt_log_warning(service, "error with video encode: %d (frame %d)\n", pkt.size, enc_ctx->frame_count); if (++enc_ctx->error_count > 2) return -1; ret = 0; } } enc_ctx->frame_count++; enc_ctx->video_pts = (double) enc_ctx->frame_count * av_q2d(enc_ctx->vcodec_ctx->time_base); if (ret) { mlt_log_fatal(service, "error writing video frame: %d\n", ret); mlt_events_fire(properties, "consumer-fatal-error", mlt_event_data_none()); return -1; } if (AV_PIX_FMT_VAAPI == c->pix_fmt #if HAVE_FFMPEG_VULKAN || AV_PIX_FMT_VULKAN == c->pix_fmt #endif ) av_frame_free(&avframe); return 0; } /** The main thread - the argument is simply the consumer. */ static void *consumer_thread(void *arg) { int i; // Encoding content encode_ctx_t *enc_ctx = mlt_pool_alloc(sizeof(encode_ctx_t)); memset(enc_ctx, 0, sizeof(encode_ctx_t)); // Map the argument to the object mlt_consumer consumer = enc_ctx->consumer = arg; // Get the properties mlt_properties properties = enc_ctx->properties = MLT_CONSUMER_PROPERTIES(consumer); // Get the terminate on pause property enc_ctx->terminate_on_pause = mlt_properties_get_int(enc_ctx->properties, "terminate_on_pause"); // Determine if feed is slow (for realtime stuff) int real_time_output = mlt_properties_get_int(properties, "real_time"); // Time structures struct timeval ante; // Get the frame rate double fps = mlt_properties_get_double(properties, "fps"); // Get width and height int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); // Get default audio properties enc_ctx->total_channels = enc_ctx->channels = mlt_properties_get_int(properties, "channels"); enc_ctx->frequency = mlt_properties_get_int(properties, "frequency"); void *pcm = NULL; int samples = 0; // AVFormat audio buffer and frame size enc_ctx->audio_outbuf_size = AUDIO_BUFFER_SIZE; // AVFormat video buffer and frame count int video_outbuf_size = VIDEO_BUFFER_SIZE; uint8_t *video_outbuf = av_malloc(video_outbuf_size); // Used for the frame properties mlt_frame frame = NULL; mlt_properties frame_properties = NULL; // Get the queues mlt_deque queue = mlt_properties_get_data(properties, "frame_queue", NULL); enc_ctx->fifo = mlt_properties_get_data(properties, "sample_fifo", NULL); AVFrame *converted_avframe = NULL; mlt_image_format img_fmt = mlt_image_yuv422; // For receiving audio samples back from the fifo int count = 0; // Frames dispatched long int frames = 0; long int total_time = 0; // Determine the format const AVOutputFormat *fmt = NULL; const char *filename = mlt_properties_get(properties, "target"); char *format = mlt_properties_get(properties, "f"); char *vcodec = mlt_properties_get(properties, "vcodec"); char *acodec = mlt_properties_get(properties, "acodec"); const AVCodec *audio_codec = NULL; const AVCodec *video_codec = NULL; // Misc char key[27]; enc_ctx->frame_meta_properties = mlt_properties_new(); int header_written = 0; // Check for user selected format first if (format != NULL) fmt = av_guess_format(format, NULL, NULL); // Otherwise check on the filename if (fmt == NULL && filename != NULL) fmt = av_guess_format(NULL, filename, NULL); // Otherwise default to mpeg if (fmt == NULL) fmt = av_guess_format("mpeg", NULL, NULL); // We need a filename - default to stdout? if (filename == NULL || !strcmp(filename, "")) filename = "pipe:"; avformat_alloc_output_context2(&enc_ctx->oc, fmt, format, filename); // Get the codec ids selected enc_ctx->audio_codec_id = fmt->audio_codec; enc_ctx->video_codec_id = fmt->video_codec; // Check for audio codec overrides if ((acodec && strcmp(acodec, "none") == 0) || mlt_properties_get_int(properties, "an")) enc_ctx->audio_codec_id = AV_CODEC_ID_NONE; else if (acodec) { audio_codec = avcodec_find_encoder_by_name(acodec); if (audio_codec) { enc_ctx->audio_codec_id = audio_codec->id; if (enc_ctx->audio_codec_id == AV_CODEC_ID_AC3 && avcodec_find_encoder_by_name("ac3_fixed")) { mlt_properties_set(enc_ctx->properties, "_acodec", "ac3_fixed"); acodec = mlt_properties_get(enc_ctx->properties, "_acodec"); audio_codec = avcodec_find_encoder_by_name(acodec); } else if (!strcmp(acodec, "aac") || !strcmp(acodec, "vorbis")) { mlt_properties_set(enc_ctx->properties, "astrict", "experimental"); } } else { enc_ctx->audio_codec_id = AV_CODEC_ID_NONE; mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "audio codec %s unrecognised - ignoring\n", acodec); } } else { audio_codec = avcodec_find_encoder(enc_ctx->audio_codec_id); } // Check for video codec overrides if ((vcodec && strcmp(vcodec, "none") == 0) || mlt_properties_get_int(properties, "vn")) enc_ctx->video_codec_id = AV_CODEC_ID_NONE; else if (vcodec) { video_codec = avcodec_find_encoder_by_name(vcodec); if (video_codec) { enc_ctx->video_codec_id = video_codec->id; } else { enc_ctx->video_codec_id = AV_CODEC_ID_NONE; mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "video codec %s unrecognised - ignoring\n", vcodec); } } else { video_codec = avcodec_find_encoder(enc_ctx->video_codec_id); } // Write metadata for (i = 0; i < mlt_properties_count(properties); i++) { char *name = mlt_properties_get_name(properties, i); if (name && !strncmp(name, "meta.attr.", 10)) { char *key = strdup(name + 10); char *markup = strrchr(key, '.'); if (markup && !strcmp(markup, ".markup")) { markup[0] = '\0'; if (!strstr(key, ".stream.")) av_dict_set(&enc_ctx->oc->metadata, key, mlt_properties_get_value(properties, i), 0); } free(key); } } // Add audio and video streams if (enc_ctx->video_codec_id != AV_CODEC_ID_NONE) { if ((enc_ctx->video_st = add_video_stream(consumer, enc_ctx->oc, video_codec, &enc_ctx->vcodec_ctx))) { const char *img_fmt_name = mlt_properties_get(properties, "mlt_image_format"); if (img_fmt_name) { // Set the mlt_image_format from explicit property. mlt_image_format f = mlt_image_format_id(img_fmt_name); if (mlt_image_invalid != f) img_fmt = f; } else { // Set the mlt_image_format from the selected pix_fmt. const char *pix_fmt_name = av_get_pix_fmt_name(enc_ctx->vcodec_ctx->pix_fmt); if (!strcmp(pix_fmt_name, "rgba") || !strcmp(pix_fmt_name, "argb") || !strcmp(pix_fmt_name, "bgra")) { mlt_properties_set(properties, "mlt_image_format", "rgba"); img_fmt = mlt_image_rgba; } else if (strstr(pix_fmt_name, "rgb") || strstr(pix_fmt_name, "bgr")) { mlt_properties_set(properties, "mlt_image_format", "rgb"); img_fmt = mlt_image_rgb; } else if (strstr(pix_fmt_name, "yuv420p10le")) { mlt_properties_set(properties, "mlt_image_format", "yuv420p10"); img_fmt = mlt_image_yuv420p10; } else if (strstr(pix_fmt_name, "yuv444p10le")) { mlt_properties_set(properties, "mlt_image_format", "yuv444p10"); img_fmt = mlt_image_yuv444p10; } else if (strstr(pix_fmt_name, "rgba64le")) { mlt_properties_set(properties, "mlt_image_format", "rgba64"); img_fmt = mlt_image_rgba64; } } } } if (enc_ctx->audio_codec_id != AV_CODEC_ID_NONE) { int is_multi = 0; #if HAVE_FFMPEG_CH_LAYOUT AVChannelLayout ch_layout; #else int64_t ch_layout; #endif enc_ctx->total_channels = 0; // multitrack audio for (i = 0; i < MAX_AUDIO_STREAMS; i++) { sprintf(key, "channels.%d", i); int j = mlt_properties_get_int(properties, key); if (j) { is_multi = 1; enc_ctx->total_channels += j; #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_default(&ch_layout, j); enc_ctx->audio_st[i] = add_audio_stream(consumer, enc_ctx->oc, audio_codec, &enc_ctx->acodec_ctx[i], j, &ch_layout); av_channel_layout_uninit(&ch_layout); #else ch_layout = av_get_default_channel_layout(j); enc_ctx->audio_st[i] = add_audio_stream(consumer, enc_ctx->oc, audio_codec, &enc_ctx->acodec_ctx[i], j, ch_layout); #endif } } // single track if (!is_multi) { mlt_channel_layout layout = mlt_audio_channel_layout_id( mlt_properties_get(properties, "channel_layout")); if (layout == mlt_channel_auto || layout == mlt_channel_independent || mlt_audio_channel_layout_channels(layout) != enc_ctx->channels) { layout = mlt_audio_channel_layout_default(enc_ctx->channels); } #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_from_mask(&ch_layout, mlt_to_av_channel_layout(layout)); enc_ctx->audio_st[0] = add_audio_stream(consumer, enc_ctx->oc, audio_codec, &enc_ctx->acodec_ctx[0], enc_ctx->channels, &ch_layout); av_channel_layout_uninit(&ch_layout); #else ch_layout = mlt_to_av_channel_layout(layout); enc_ctx->audio_st[0] = add_audio_stream(consumer, enc_ctx->oc, audio_codec, &enc_ctx->acodec_ctx[0], enc_ctx->channels, ch_layout); #endif enc_ctx->total_channels = enc_ctx->channels; } } mlt_properties_set_int(properties, "channels", enc_ctx->total_channels); // Audio format is determined when adding the audio stream mlt_audio_format aud_fmt = mlt_audio_none; if (enc_ctx->audio_st[0]) aud_fmt = get_mlt_audio_format(enc_ctx->acodec_ctx[0]->sample_fmt); enc_ctx->sample_bytes = mlt_audio_format_size(aud_fmt, 1, 1); enc_ctx->sample_bytes = enc_ctx->sample_bytes ? enc_ctx->sample_bytes : 1; // prevent divide by zero // Set the parameters (even though we have none...) { if (mlt_properties_get(properties, "muxpreload") && !mlt_properties_get(properties, "preload")) mlt_properties_set_double(properties, "preload", mlt_properties_get_double(properties, "muxpreload")); enc_ctx->oc->max_delay = (int) (mlt_properties_get_double(properties, "muxdelay") * AV_TIME_BASE); // Process properties as AVOptions char *fpre = mlt_properties_get(properties, "fpre"); if (fpre) { mlt_properties p = mlt_properties_load(fpre); apply_properties(enc_ctx->oc, p, AV_OPT_FLAG_ENCODING_PARAM); if (enc_ctx->oc->oformat && enc_ctx->oc->oformat->priv_class && enc_ctx->oc->priv_data) apply_properties(enc_ctx->oc->priv_data, p, AV_OPT_FLAG_ENCODING_PARAM); mlt_properties_close(p); } apply_properties(enc_ctx->oc, properties, AV_OPT_FLAG_ENCODING_PARAM); if (enc_ctx->oc->oformat && enc_ctx->oc->oformat->priv_class && enc_ctx->oc->priv_data) apply_properties(enc_ctx->oc->priv_data, properties, AV_OPT_FLAG_ENCODING_PARAM); if (enc_ctx->video_st && !open_video(properties, enc_ctx->oc, enc_ctx->video_st, video_codec, enc_ctx->vcodec_ctx)) enc_ctx->video_st = NULL; for (i = 0; i < MAX_AUDIO_STREAMS && enc_ctx->audio_st[i]; i++) { enc_ctx->audio_input_frame_size = open_audio(properties, enc_ctx->oc, enc_ctx->audio_st[i], audio_codec, enc_ctx->acodec_ctx[i]); if (!enc_ctx->audio_input_frame_size) { // Remove the audio stream from the output context unsigned int j; for (j = 0; j < enc_ctx->oc->nb_streams; j++) { if (enc_ctx->oc->streams[j] == enc_ctx->audio_st[i]) av_freep(&enc_ctx->oc->streams[j]); } --enc_ctx->oc->nb_streams; enc_ctx->audio_st[i] = NULL; } } open_subtitles(enc_ctx); // Add cover art (attached picture) stream if requested const char *attached_pic_path = mlt_properties_get(properties, "attached_pic"); if (attached_pic_path && attached_pic_path[0]) { enc_ctx->attached_pic_st = add_attached_pic_stream(consumer, enc_ctx, attached_pic_path); } // Setup custom I/O if redirecting if (mlt_properties_get_int(properties, "redirect")) { int buffer_size = 32768; unsigned char *buffer = av_malloc(buffer_size); AVIOContext *io = avio_alloc_context(buffer, buffer_size, 1, properties, NULL, mlt_write, NULL); if (buffer && io) { enc_ctx->oc->pb = io; enc_ctx->oc->flags |= AVFMT_FLAG_CUSTOM_IO; mlt_properties_set_data(properties, "avio_buffer", buffer, buffer_size, av_free, NULL); mlt_properties_set_data(properties, "avio_context", io, 0, av_free, NULL); mlt_events_register(properties, "avformat-write"); } else { av_free(buffer); mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "failed to setup output redirection\n"); } } // Open the output file, if needed else if (!(fmt->flags & AVFMT_NOFILE)) { if (avio_open(&enc_ctx->oc->pb, filename, AVIO_FLAG_WRITE) < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Could not open '%s'\n", filename); mlt_events_fire(properties, "consumer-fatal-error", mlt_event_data_none()); goto on_fatal_error; } } } // Last check - need at least one stream if (!enc_ctx->audio_st[0] && !enc_ctx->video_st) { mlt_events_fire(properties, "consumer-fatal-error", mlt_event_data_none()); goto on_fatal_error; } // Allocate picture enum AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P; if (enc_ctx->video_st) { const int need_nv12 = enc_ctx->vcodec_ctx->pix_fmt == AV_PIX_FMT_VAAPI #if HAVE_FFMPEG_VULKAN || enc_ctx->vcodec_ctx->pix_fmt == AV_PIX_FMT_VULKAN #endif ; pix_fmt = need_nv12 ? AV_PIX_FMT_NV12 : enc_ctx->vcodec_ctx->pix_fmt; converted_avframe = alloc_picture(pix_fmt, width, height); if (!converted_avframe) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "failed to allocate video AVFrame\n"); mlt_events_fire(properties, "consumer-fatal-error", mlt_event_data_none()); goto on_fatal_error; } } // Allocate audio AVFrame if (enc_ctx->audio_st[0]) { enc_ctx->audio_avframe = av_frame_alloc(); if (enc_ctx->audio_avframe) { AVCodecContext *c = enc_ctx->acodec_ctx[0]; enc_ctx->audio_avframe->format = c->sample_fmt; enc_ctx->audio_avframe->nb_samples = enc_ctx->audio_input_frame_size; #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_copy(&enc_ctx->audio_avframe->ch_layout, &c->ch_layout); #else enc_ctx->audio_avframe->channel_layout = c->channel_layout; enc_ctx->audio_avframe->channels = c->channels; #endif } else { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "failed to allocate audio AVFrame\n"); mlt_events_fire(properties, "consumer-fatal-error", mlt_event_data_none()); goto on_fatal_error; } } // Get the starting time (can ignore the times above) gettimeofday(&ante, NULL); // Loop while running while (mlt_properties_get_int(properties, "running") && (!enc_ctx->terminated || (enc_ctx->video_st && mlt_deque_count(queue)))) { if (!frame) frame = mlt_consumer_rt_frame(consumer); // Check that we have a frame to work with if (frame != NULL) { // Default audio args frame_properties = MLT_FRAME_PROPERTIES(frame); // Write the stream header. if (!header_written) { // set timecode from first frame if not been set from metadata if (!mlt_properties_get(properties, "timecode")) { char *vitc = mlt_properties_get(frame_properties, "meta.attr.vitc.markup"); if (vitc && vitc[0]) { mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "timecode=[%s]\n", vitc); av_dict_set(&enc_ctx->oc->metadata, "timecode", vitc, 0); if (enc_ctx->video_st) av_dict_set(&enc_ctx->video_st->metadata, "timecode", vitc, 0); }; }; if (avformat_write_header(enc_ctx->oc, NULL) < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Could not write header '%s'\n", filename); mlt_events_fire(properties, "consumer-fatal-error", mlt_event_data_none()); goto on_fatal_error; } // Write the attached picture (cover art) immediately after the header if (enc_ctx->attached_pic_st && enc_ctx->attached_pic_pkt) { if (av_interleaved_write_frame(enc_ctx->oc, enc_ctx->attached_pic_pkt) < 0) mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "attached_pic: failed to write cover art packet\n"); av_packet_free(&enc_ctx->attached_pic_pkt); } header_written = 1; } // Increment frames dispatched frames++; // Check for the terminated condition enc_ctx->terminated = enc_ctx->terminate_on_pause && mlt_properties_get_double(frame_properties, "_speed") == 0.0; // Encode subtitles if (!enc_ctx->terminated && enc_ctx->subtitle_st[0]) { encode_subtitles(enc_ctx, frame); } // Get audio and append to the fifo if (!enc_ctx->terminated && enc_ctx->audio_st[0]) { samples = mlt_audio_calculate_frame_samples(fps, enc_ctx->frequency, count++); enc_ctx->channels = enc_ctx->total_channels; mlt_frame_get_audio(frame, &pcm, &aud_fmt, &enc_ctx->frequency, &enc_ctx->channels, &samples); // Save the audio channel remap properties for later mlt_properties_pass(enc_ctx->frame_meta_properties, frame_properties, "meta.map.audio."); // Create the fifo if we don't have one if (enc_ctx->fifo == NULL) { enc_ctx->fifo = sample_fifo_init(enc_ctx->frequency, enc_ctx->channels); mlt_properties_set_data(properties, "sample_fifo", enc_ctx->fifo, 0, (mlt_destructor) sample_fifo_close, NULL); } if (pcm) { // Silence if not normal forward speed if (mlt_properties_get_double(frame_properties, "_speed") != 1.0) memset(pcm, 0, samples * enc_ctx->channels * enc_ctx->sample_bytes); // Append the samples sample_fifo_append(enc_ctx->fifo, pcm, samples * enc_ctx->channels * enc_ctx->sample_bytes); total_time += (samples * 1000000) / enc_ctx->frequency; } if (!enc_ctx->video_st) { mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } } // Encode the image if (!enc_ctx->terminated && enc_ctx->video_st) mlt_deque_push_back(queue, frame); else mlt_frame_close(frame); frame = NULL; } // While we have stuff to process, process... while (1) { // Write interleaved audio and video frames if (!enc_ctx->video_st || (enc_ctx->video_st && enc_ctx->audio_st[0] && enc_ctx->audio_pts < enc_ctx->video_pts)) { // Write audio int fifo_frames = sample_fifo_used(enc_ctx->fifo) / (enc_ctx->audio_input_frame_size * enc_ctx->channels * enc_ctx->sample_bytes); if ((enc_ctx->video_st && enc_ctx->terminated) || fifo_frames) { int r = encode_audio(enc_ctx); if (r > 0) break; else if (r < 0) goto on_fatal_error; } else { break; } } else if (enc_ctx->video_st) { // Write video if (mlt_deque_count(queue)) { frame = mlt_deque_pop_front(queue); if (encode_video(enc_ctx, converted_avframe, video_outbuf, video_outbuf_size, frame, img_fmt) < 0) goto on_fatal_error; mlt_frame_close(frame); frame = NULL; } else { break; } } if (enc_ctx->audio_st[0]) mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "audio pts %f ", enc_ctx->audio_pts); if (enc_ctx->video_st) mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "video pts %f ", enc_ctx->video_pts); mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "\n"); } if (real_time_output == 1 && frames % 2 == 0) { long passed = time_difference(&ante); if (enc_ctx->fifo != NULL) { long pending = (((long) sample_fifo_used(enc_ctx->fifo) / enc_ctx->sample_bytes * 1000) / enc_ctx->frequency) * 1000; passed -= pending; } if (passed < total_time) { long total = (total_time - passed); struct timespec t = {total / 1000000, (total % 1000000) * 1000}; nanosleep(&t, NULL); } } } // Flush the encoder buffers if (real_time_output <= 0) { // Flush audio fifo if (enc_ctx->fifo && enc_ctx->audio_st[0]) { for (;;) { int sz = sample_fifo_used(enc_ctx->fifo); int ret = encode_audio(enc_ctx); mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "flushing audio: sz=%d, ret=%d\n", sz, ret); if (!sz || ret < 0) break; } // After FIFO is empty, drain audio encoders to avoid writing untimestamped packets. if (flush_audio_encoders(enc_ctx) < 0) { goto on_fatal_error; } } // Flush video #ifdef AVFMT_RAWPICTURE if (enc_ctx->video_st && !(enc_ctx->oc->oformat->flags & AVFMT_RAWPICTURE)) for (;;) #else if (enc_ctx->video_st) for (;;) #endif { AVCodecContext *c = enc_ctx->vcodec_ctx; AVPacket pkt; av_init_packet(&pkt); if (c->codec->id == AV_CODEC_ID_RAWVIDEO) { pkt.data = NULL; pkt.size = 0; } else { pkt.data = video_outbuf; pkt.size = video_outbuf_size; } // Encode the image int ret; while ((ret = avcodec_receive_packet(c, &pkt)) == AVERROR(EAGAIN)) { ret = avcodec_send_frame(c, NULL); if (ret < 0) { mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "error with video encode: %d\n", ret); break; } } mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "flushing video size %d\n", pkt.size); if (pkt.size < 0) break; // Dual pass logging if (mlt_properties_get_data(properties, "_logfile", NULL) && c->stats_out) fprintf(mlt_properties_get_data(properties, "_logfile", NULL), "%s", c->stats_out); if (!pkt.size) break; av_packet_rescale_ts(&pkt, c->time_base, enc_ctx->video_st->time_base); pkt.stream_index = enc_ctx->video_st->index; // write the compressed frame in the media file if (av_interleaved_write_frame(enc_ctx->oc, &pkt) != 0) { mlt_log_fatal(MLT_CONSUMER_SERVICE(consumer), "error writing flushed video frame\n"); mlt_events_fire(properties, "consumer-fatal-error", mlt_event_data_none()); goto on_fatal_error; } } } on_fatal_error: if (frame) mlt_frame_close(frame); // Write the trailer, if any if (frames) av_write_trailer(enc_ctx->oc); // Clean up input and output frames if (converted_avframe) av_free(converted_avframe->data[0]); av_free(converted_avframe); av_free(video_outbuf); av_free(enc_ctx->audio_avframe); // Close subtitle transcoding codecs for (i = 0; i < MAX_SUBTITLE_STREAMS; i++) { avcodec_free_context(&enc_ctx->sdec_ctx[i]); avcodec_free_context(&enc_ctx->senc_ctx[i]); } // Free any unwritten attached picture packet if (enc_ctx->attached_pic_pkt) av_packet_free(&enc_ctx->attached_pic_pkt); // close each codec avcodec_free_context(&enc_ctx->vcodec_ctx); for (i = 0; i < MAX_AUDIO_STREAMS; i++) avcodec_free_context(&enc_ctx->acodec_ctx[i]); // Free the streams for (unsigned int i = 0; i < enc_ctx->oc->nb_streams; i++) av_freep(&enc_ctx->oc->streams[i]); // Close the output file if (!(fmt->flags & AVFMT_NOFILE) && !mlt_properties_get_int(properties, "redirect")) { if (enc_ctx->oc->pb) avio_close(enc_ctx->oc->pb); } // Free the stream av_free(enc_ctx->oc); // Just in case we terminated on pause mlt_consumer_stopped(consumer); mlt_properties_close(enc_ctx->frame_meta_properties); if (mlt_properties_get_int(properties, "pass") > 1) { // Remove the dual pass log file if (mlt_properties_get(properties, "_logfilename")) remove(mlt_properties_get(properties, "_logfilename")); // Remove the x264 dual pass logs char *cwd = getcwd(NULL, 0); const char *file = "x264_2pass.log"; char *full = malloc(strlen(cwd) + strlen(file) + 2); sprintf(full, "%s/%s", cwd, file); remove(full); free(full); file = "x264_2pass.log.temp"; full = malloc(strlen(cwd) + strlen(file) + 2); sprintf(full, "%s/%s", cwd, file); remove(full); free(full); file = "x264_2pass.log.mbtree"; full = malloc(strlen(cwd) + strlen(file) + 2); sprintf(full, "%s/%s", cwd, file); remove(full); free(full); free(cwd); remove("x264_2pass.log.temp"); // Recent versions of libavcodec/x264 support passlogfile and need cleanup if specified. if (!mlt_properties_get(properties, "_logfilename") && mlt_properties_get(properties, "passlogfile")) { mlt_properties_get(properties, "passlogfile"); file = mlt_properties_get(properties, "passlogfile"); remove(file); full = malloc(strlen(file) + strlen(".mbtree") + 1); sprintf(full, "%s.mbtree", file); remove(full); free(full); } } while ((frame = mlt_deque_pop_back(queue))) mlt_frame_close(frame); mlt_pool_release(enc_ctx); return NULL; } /** Close the consumer. */ static void consumer_close(mlt_consumer consumer) { // Stop the consumer mlt_consumer_stop(consumer); // Close the parent mlt_consumer_close(consumer); // Free the memory free(consumer); } mlt-7.38.0/src/modules/avformat/consumer_avformat.yml000664 000000 000000 00000034662 15172202314 022730 0ustar00rootroot000000 000000 schema_version: 0.3 type: consumer identifier: avformat title: FFmpeg Output version: 6 copyright: Copyright (C) 2003-2019 Meltytech, LLC license: LGPL language: en url: http://www.ffmpeg.org/ creator: Charles Yates contributor: - Dan Dennedy tags: - Audio - Video description: Write or stream audio and/or video using FFmpeg. notes: > The avformat consumer uses the FFmpeg libraries to encode to a file or network stream. You can get a lot of information about how to encode with FFmpeg all over the web including FFmpeg's web site. With melt, you simply need to add "-consumer avformat:output.file" to the command line followed by the encoding parameters by translating ffmpeg's '-option value' syntax to melt's 'option=value' syntax. Not all ffmpeg options are supported. Some are very specific to avconv/ffmpeg, the command line utility, and not an "AVOption" used in the libraries. In some cases, there are ffmpeg options that are not AVOptions but which closely resemble an existing MLT property. In that case, MLT supports the ffmpeg option name. For example, ffmpeg's "-ac" is equivalent to the MLT "channels" property. Therefore, the avformat consumer also supports the "ac" property. Complete details are below. Please note that the exact options depend on the version of libavformat and libavcodec on your system. parameters: - identifier: target argument: yes title: File/URL type: string description: > This is not the same thing as the ffmpeg -target option! If this is not supplied then it will output to stdout. widget: filesave - identifier: mlt_profile title: MLT Profile type: string description: > Choose a MLT basic video settings preset. This overrides a profile that may have been set elsewhere. - identifier: redirect title: Redirect I/O description: > This option allows other services to encapsulate the avformat consumer and do something different (not already available in a protocol) with its output by listening to the avformat-write event. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: attached_pic title: Cover Art type: string description: > Path to an image file to embed as cover art (attached picture) in the output file. The image type is detected from file content (magic bytes), and only JPEG and PNG are supported. The image is added as an attached picture stream with the AV_DISPOSITION_ATTACHED_PIC flag and is written immediately after the container header. Most audio container formats (e.g. MP3, M4A, OGG, FLAC) as well as Matroska support attached pictures. widget: fileopen # These override the MLT profile - identifier: width title: Width type: integer minimum: 0 unit: pixels - identifier: height title: Height type: integer minimum: 0 unit: pixels - identifier: display_aspect_num title: Display aspect ratio numerator type: integer minimum: 0 - identifier: display_aspect_den title: Display aspect ratio denominator type: integer minimum: 0 - identifier: display_ratio title: Display aspect ratio readonly: yes - identifier: sample_aspect_num title: Sample aspect ratio numerator type: integer minimum: 0 - identifier: sample_aspect_den title: Sample aspect ratio denominator type: integer minimum: 1 - identifier: progressive title: Progressive type: integer minimum: 0 maximum: 1 widget: checkbox - identifier: colorspace title: Colorspace type: integer description: Set the video colorspace (Y'CbCr only). values: - 240 # SMPTE 240M - 601 # ITU-R BT.601 - 709 # ITU-R BT.709 - identifier: frame_rate_num title: Frame rate numerator type: integer minimum: 0 unit: frames/second - identifier: frame_rate_den title: Frame rate denominator type: integer minimum: 1 unit: frames/second - identifier: fps title: Frame rate readonly: yes unit: frames/second # These are common to all consumers. - identifier: deinterlacer title: Deinterlacer type: string default: yadif values: - greedy - linearblend - onefield - yadif - yadif-nospatial - identifier: rescale title: Image scaler type: string description: Set the pixel interpolation mode. values: - nearest - bilinear - bicubic - bicublin - gauss - sinc - lanczos - spline - identifier: frequency title: Audio sample rate type: integer minimum: 0 maximum: 256000 default: 48000 unit: Hz - identifier: channels title: Audio channels type: integer minimum: 1 maximum: 16 default: 2 - identifier: channels.0 title: Channels on track 1 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.1 title: Channels on track 2 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.2 title: Channels on track 3 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.3 title: Channels on track 4 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.4 title: Channels on track 5 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.5 title: Channels on track 6 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.6 title: Channels on track 7 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.7 title: Channels on track 8 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: subtitle.0.feed title: Feed for subtitle stream 0 type: string description: Specify the name of the feed for subtitle stream 0. - identifier: subtitle.0.lang title: Language for subtitle stream 0 type: string description: Specify the 3 digit language code for subtitle stream 0. - identifier: subtitle.1.feed title: Feed for subtitle stream 1 type: string description: Specify the name of the feed for subtitle stream 1. - identifier: subtitle.1.lang title: Language for subtitle stream 1 type: string description: Specify the 3 digit language code for subtitle stream 1. - identifier: subtitle.2.feed title: Feed for subtitle stream 2 type: string description: Specify the name of the feed for subtitle stream 2. - identifier: subtitle.2.lang title: Language for subtitle stream 2 type: string description: Specify the 3 digit language code for subtitle stream 2. - identifier: subtitle.3.feed title: Feed for subtitle stream 3 type: string description: Specify the name of the feed for subtitle stream 3. - identifier: subtitle.3.lang title: Language for subtitle stream 3 type: string description: Specify the 3 digit language code for subtitle stream 3. - identifier: subtitle.4.feed title: Feed for subtitle stream 4 type: string description: Specify the name of the feed for subtitle stream 4. - identifier: subtitle.4.lang title: Language for subtitle stream 4 type: string description: Specify the 3 digit language code for subtitle stream 4. - identifier: subtitle.5.feed title: Feed for subtitle stream 5 type: string description: Specify the name of the feed for subtitle stream 5. - identifier: subtitle.5.lang title: Language for subtitle stream 5 type: string description: Specify the 3 digit language code for subtitle stream 5. - identifier: subtitle.6.feed title: Feed for subtitle stream 6 type: string description: Specify the name of the feed for subtitle stream 6. - identifier: subtitle.6.lang title: Language for subtitle stream 6 type: string description: Specify the 3 digit language code for subtitle stream 6. - identifier: subtitle.7.feed title: Feed for subtitle stream 7 type: string description: Specify the name of the feed for subtitle stream 7. - identifier: subtitle.7.lang title: Language for subtitle stream 7 type: string description: Specify the 3 digit language code for subtitle stream 7. # These are common to all consumers and affect runtime behavior - identifier: terminate_on_pause title: File output type: integer description: Disable this for streaming. minimum: 0 maximum: 1 default: 1 widget: checkbox - identifier: real_time title: Drop frames type: integer description: > Set the number of processing threads and enable frame-dropping (positive) or disable frame-dropping (negative). default: -1 widget: spinner unit: threads - identifier: prefill title: Pre-roll type: integer description: Set the number of frames to buffer before starting actual output. minimum: 1 default: 1 unit: frames - identifier: buffer title: Buffer type: integer description: > Set the maximum number of frames to buffer - process ahead of the output position. minimum: 1 default: 25 unit: frames # These are ffmpeg-compatible aliases to MLT properties - identifier: s title: Size type: string description: > This is a ffmpeg-compatible equivalent to the MLT profile and width and height parameters. format: WxH unit: pixels - identifier: aspect title: Aspect ratio type: string description: > This is a ffmpeg-compatible equivalent to the MLT profile and other aspect ratio parameters. format: numerator:denominator - identifier: deinterlace title: Deinterlace type: integer description: > This is a ffmpeg-compatible equivalent to the MLT profile and progressive parameter. minimum: 0 maximum: 1 - identifier: r title: Frame rate type: float description: > This is a ffmpeg-compatible equivalent to the MLT profile and frame rate parameters. minimum: 5.0 - identifier: ac title: Audio channels type: integer description: > This is a ffmpeg-compatible equivalent to the channels parameter. minimum: 1 maximum: 16 default: 2 - identifier: ar title: Audio sample rate type: integer description: > This is a ffmpeg-compatible equivalent to the frequency parameter. minimum: 0 maximum: 256000 default: 48000 unit: Hz # These are other non-AVOption parameters specific to FFmpeg. - identifier: threads title: Encoding threads type: integer minimum: 0 maximum: 16 default: 1 widget: spinner unit: threads - identifier: aq title: Audio quality type: integer description: The meaning depends upon the codec. - identifier: dc title: Intra DC precision type: integer default: 8 - identifier: muxdelay title: Muxer delay type: float description: Set the maximum demux-decode delay. default: 0.7 unit: seconds - identifier: muxpreload title: Muxer preload type: float description: Set the initial demux-decode delay. default: 0.5 unit: seconds - identifier: f title: Format type: string description: Use "list" to see the list of formats. default: mpeg - identifier: acodec title: Audio codec description: Use "list" to see the list of audio codecs. default: mp2 - identifier: vcodec title: Video codec description: Use "list" to see the list of video codecs. default: mpeg2video - identifier: atag title: Audio FourCC type: string - identifier: apre title: Audio codec preset type: string - identifier: vpre title: Video codec preset type: string - identifier: fpre title: Format preset type: string - identifier: alang title: Audio language type: string description: Set the 3-character ISO 639 language code of the current audio stream. - identifier: pix_fmt title: Pixel format type: string description: > See 'ffmpeg -pix_fmts' to see a list of values. Normally, this is not required, but some codecs support multiple pixel formats, especially chroma bit-depth. - identifier: sample_fmt title: Audio sample format type: string description: > See 'ffmpeg -sample_fmts' to see a list of values. Normally, this is not required, but some codecs support multiple sample formats, especially bit-depth and planar vs. interleaved. This is evaluated at a lower priority than mlt_audio_format. - identifier: qscale title: Video quantizer type: float description: Set a fixed video quantizer scale for constant quality VBR output. - identifier: vtag title: Video FourCC type: string - identifier: rc_override title: Rate control type: string format: start_frame,end_frame,qscale/... description: This is an override for specific intervals. - identifier: pass title: Pass type: integer description: Select the pass number for two-pass encoding. minimum: 1 maximum: 2 - identifier: passlogfile title: Two-pass log file type: string - identifier: vb title: Video bitrate type: string unit: bits/second description: > Normally this is an integer, but you can append a K suffix for convenience. minimum: 0 - identifier: ab title: Audio bitrate type: string unit: bits/second description: > Normally this is an integer, but you can append a K suffix for convenience. - identifier: an title: Disable audio type: integer minimum: 0 maximum: 1 widget: checkbox - identifier: vn title: Disable video type: integer minimum: 0 maximum: 1 widget: checkbox - identifier: sn title: Disable subtitles type: integer minimum: 0 maximum: 1 widget: checkbox mlt-7.38.0/src/modules/avformat/factory.c000664 000000 000000 00000055743 15172202314 020271 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include extern mlt_consumer consumer_avformat_init(mlt_profile profile, char *file); extern mlt_filter filter_avcolour_space_init(void *arg); extern mlt_filter filter_avdeinterlace_init(void *arg); extern mlt_filter filter_swresample_init(mlt_profile profile, char *arg); extern mlt_filter filter_swscale_init(mlt_profile profile, char *arg); #if LIBSWSCALE_VERSION_MAJOR >= 9 extern mlt_filter filter_sws_colortransform_init(mlt_profile, mlt_service_type, const char *, char *); #endif extern mlt_producer producer_avformat_init(mlt_profile profile, const char *service, char *file); extern mlt_filter filter_avfilter_init(mlt_profile, mlt_service_type, const char *, char *); extern mlt_link link_avdeinterlace_init(mlt_profile, mlt_service_type, const char *, char *); extern mlt_link link_avfilter_init(mlt_profile, mlt_service_type, const char *, char *); extern mlt_link link_swresample_init(mlt_profile profile, mlt_service_type, const char *, char *); // ffmpeg Header files #include "mltavformat_export.h" #include #include #include #include #include // A static flag used to determine if avformat has been initialised static int avformat_initialised = 0; static void avformat_init() { // Initialise avformat if necessary if (avformat_initialised == 0) { avformat_initialised = 1; avdevice_register_all(); avformat_network_init(); av_log_set_level(mlt_log_get_level()); if (getenv("MLT_AVFORMAT_PRODUCER_CACHE")) { int n = atoi(getenv("MLT_AVFORMAT_PRODUCER_CACHE")); mlt_service_cache_set_size(NULL, "producer_avformat", n); } } } static void *create_service(mlt_profile profile, mlt_service_type type, const char *id, void *arg) { avformat_init(); if (!strncmp(id, "avformat", 8)) { if (type == mlt_service_producer_type) return producer_avformat_init(profile, id, arg); else if (type == mlt_service_consumer_type) return consumer_avformat_init(profile, arg); } if (!strcmp(id, "avcolor_space")) return filter_avcolour_space_init(arg); if (!strcmp(id, "avcolour_space")) return filter_avcolour_space_init(arg); if (!strcmp(id, "avdeinterlace")) { if (type == mlt_service_filter_type) return filter_avdeinterlace_init(arg); else if (type == mlt_service_link_type) return link_avdeinterlace_init(profile, type, id, arg); } if (!strcmp(id, "swscale")) return filter_swscale_init(profile, arg); #if LIBSWSCALE_VERSION_MAJOR >= 9 if (!strcmp(id, "sws_colortransform")) return filter_sws_colortransform_init(profile, type, id, arg); #endif if (!strcmp(id, "swresample")) { if (type == mlt_service_filter_type) return filter_swresample_init(profile, arg); else if (type == mlt_service_link_type) return link_swresample_init(profile, type, id, arg); } return NULL; } static void add_parameters(mlt_properties params, const void *object, int req_flags, const char *unit, const char *subclass, const char *id_prefix) { const AVOption *opt = NULL; // For each AVOption on the AVClass object while ((opt = av_opt_next(object, opt))) { // If matches flags and not a binary option (not supported by Mlt) if (!(opt->flags & req_flags) || (opt->type == AV_OPT_TYPE_BINARY)) continue; // Ignore constants (keyword values) if (!unit && opt->type == AV_OPT_TYPE_CONST) continue; // When processing a groups of options (unit)... // ...ignore non-constants else if (unit && opt->type != AV_OPT_TYPE_CONST) continue; // ...ignore constants not in this group else if (unit && opt->type == AV_OPT_TYPE_CONST && strcmp(unit, opt->unit)) continue; // ..add constants to the 'values' sequence else if (unit && opt->type == AV_OPT_TYPE_CONST) { char key[20]; snprintf(key, 20, "%d", mlt_properties_count(params)); mlt_properties_set(params, key, opt->name); continue; } // Create a map for this option. mlt_properties p = mlt_properties_new(); char key[20]; snprintf(key, 20, "%d", mlt_properties_count(params)); // Add the map to the 'parameters' sequence. mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); // Add the parameter metadata for this AVOption. if (id_prefix) { char id[200]; snprintf(id, sizeof(id), "%s%s", id_prefix, opt->name); mlt_properties_set(p, "identifier", id); } else { mlt_properties_set(p, "identifier", opt->name); } if (opt->help) { if (subclass) { char *s = malloc(strlen(opt->help) + strlen(subclass) + 4); strcpy(s, opt->help); strcat(s, " ("); strcat(s, subclass); strcat(s, ")"); mlt_properties_set(p, "description", s); free(s); } else mlt_properties_set(p, "description", opt->help); } switch (opt->type) { case AV_OPT_TYPE_FLAGS: mlt_properties_set(p, "type", "string"); mlt_properties_set(p, "format", "flags"); break; case AV_OPT_TYPE_INT: if (!opt->unit) { mlt_properties_set(p, "type", "integer"); if (opt->min != INT_MIN) mlt_properties_set_int(p, "minimum", (int) opt->min); if (opt->max != INT_MAX) mlt_properties_set_int(p, "maximum", (int) opt->max); mlt_properties_set_int(p, "default", (int) opt->default_val.i64); } else { mlt_properties_set(p, "type", "string"); mlt_properties_set(p, "format", "integer or keyword"); } break; case AV_OPT_TYPE_INT64: mlt_properties_set(p, "type", "integer"); mlt_properties_set(p, "format", "64-bit"); if (opt->min != INT64_MIN) mlt_properties_set_int64(p, "minimum", (int64_t) opt->min); if (opt->max != INT64_MAX) mlt_properties_set_int64(p, "maximum", (int64_t) opt->max); mlt_properties_set_int64(p, "default", (int64_t) opt->default_val.i64); break; case AV_OPT_TYPE_FLOAT: mlt_properties_set(p, "type", "float"); if (opt->min != FLT_MIN && opt->min != -340282346638528859811704183484516925440.0) mlt_properties_set_double(p, "minimum", opt->min); if (opt->max != FLT_MAX) mlt_properties_set_double(p, "maximum", opt->max); mlt_properties_set_double(p, "default", opt->default_val.dbl); break; case AV_OPT_TYPE_DOUBLE: mlt_properties_set(p, "type", "float"); mlt_properties_set(p, "format", "double"); if (opt->min != DBL_MIN) mlt_properties_set_double(p, "minimum", opt->min); if (opt->max != DBL_MAX) mlt_properties_set_double(p, "maximum", opt->max); mlt_properties_set_double(p, "default", opt->default_val.dbl); break; case AV_OPT_TYPE_STRING: mlt_properties_set(p, "type", "string"); if (opt->default_val.str && strcmp(opt->default_val.str, "")) mlt_properties_set(p, "default", opt->default_val.str); break; case AV_OPT_TYPE_RATIONAL: mlt_properties_set(p, "type", "string"); mlt_properties_set(p, "format", "numerator/denominator"); break; case AV_OPT_TYPE_CONST: mlt_properties_set(p, "type", "integer"); mlt_properties_set(p, "format", "constant"); break; case AV_OPT_TYPE_COLOR: mlt_properties_set(p, "type", "color"); if (opt->default_val.str && strcmp(opt->default_val.str, "")) mlt_properties_set(p, "default", opt->default_val.str); default: mlt_properties_set(p, "type", "string"); break; } // Fix up minimum/maximum if serialized as scientific notation (invalid YAML number) const char *props[] = {"minimum", "maximum", NULL}; for (int pi = 0; props[pi]; pi++) { const char *v = mlt_properties_get(p, props[pi]); if (v && (strchr(v, 'e') || strchr(v, 'E'))) { char strbuf[32]; snprintf(strbuf, sizeof(strbuf), "%f", mlt_properties_get_double(p, props[pi])); mlt_properties_set(p, props[pi], strbuf); } } // If the option belongs to a group (unit) and is not a constant (keyword value) if (opt->unit && opt->type != AV_OPT_TYPE_CONST) { // Create a 'values' sequence. mlt_properties values = mlt_properties_new(); // Recurse to add constants in this group to the 'values' sequence. add_parameters(values, object, req_flags, opt->unit, NULL, NULL); if (mlt_properties_count(values)) mlt_properties_set_data(p, "values", values, 0, (mlt_destructor) mlt_properties_close, NULL); else mlt_properties_close(values); } } } static mlt_properties avformat_metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; const char *service_type = NULL; mlt_properties result = NULL; // Convert the service type to a string. switch (type) { case mlt_service_consumer_type: service_type = "consumer"; break; case mlt_service_filter_type: service_type = "filter"; break; case mlt_service_producer_type: service_type = "producer"; break; case mlt_service_transition_type: service_type = "transition"; break; default: return NULL; } if (type == mlt_service_producer_type && !strcmp(id, "avformat-novalidate")) { id = "avformat"; } // Load the yaml file snprintf(file, PATH_MAX, "%s/avformat/%s_%s.yml", mlt_environment("MLT_DATA"), service_type, id); result = mlt_properties_parse_yaml(file); if (result && (type == mlt_service_consumer_type || type == mlt_service_producer_type)) { // Annotate the yaml properties with AVOptions. mlt_properties params = (mlt_properties) mlt_properties_get_data(result, "parameters", NULL); AVFormatContext *avformat = avformat_alloc_context(); AVCodecContext *avcodec = avcodec_alloc_context3(NULL); int flags = (type == mlt_service_consumer_type) ? AV_OPT_FLAG_ENCODING_PARAM : AV_OPT_FLAG_DECODING_PARAM; add_parameters(params, avformat, flags, NULL, NULL, NULL); avformat_init(); if (type == mlt_service_producer_type) { const AVInputFormat *f = NULL; void *iterator = NULL; while ((f = av_demuxer_iterate(&iterator))) if (f->priv_class) add_parameters(params, &f->priv_class, flags, NULL, f->name, NULL); } else { const AVOutputFormat *f = NULL; void *iterator = NULL; while ((f = av_muxer_iterate(&iterator))) if (f->priv_class) add_parameters(params, &f->priv_class, flags, NULL, f->name, NULL); } add_parameters(params, avcodec, flags, NULL, NULL, NULL); const AVCodec *c = NULL; void *iterator = NULL; while ((c = av_codec_iterate(&iterator))) if (c->priv_class) add_parameters(params, &c->priv_class, flags, NULL, c->name, NULL); av_free(avformat); av_free(avcodec); } return result; } static mlt_properties avfilter_metadata(mlt_service_type type, const char *id, void *name) { AVFilter *f = (AVFilter *) avfilter_get_by_name(name); if (!f) return NULL; mlt_properties metadata = mlt_properties_new(); mlt_properties_set_double(metadata, "schema_version", 0.3); mlt_properties_set(metadata, "title", f->name); mlt_properties_set(metadata, "version", LIBAVFILTER_IDENT); mlt_properties_set(metadata, "identifier", id); mlt_properties_set(metadata, "description", f->description); mlt_properties_set(metadata, "language", "en"); mlt_properties_set( metadata, "notes", "Many parameters support animated values (keyframes) but only the numeric ones. Many " "numeric properties have type string because they accept an expression (see FFmpeg " "documentation) even though they evaluate to a numeric value."); mlt_properties_set(metadata, "creator", "libavfilter maintainers"); if (type == mlt_service_filter_type) { mlt_properties_set(metadata, "type", "filter"); } else { mlt_properties_set(metadata, "type", "link"); } mlt_properties tags = mlt_properties_new(); mlt_properties_set_data(metadata, "tags", tags, 0, (mlt_destructor) mlt_properties_close, NULL); if (avfilter_pad_get_type(f->inputs, 0) == AVMEDIA_TYPE_VIDEO) { mlt_properties_set(tags, "0", "Video"); } if (avfilter_pad_get_type(f->inputs, 0) == AVMEDIA_TYPE_AUDIO) { mlt_properties_set(tags, "0", "Audio"); } if (f->priv_class) { mlt_properties params = mlt_properties_new(); mlt_properties_set_data(metadata, "parameters", params, 0, (mlt_destructor) mlt_properties_close, NULL); add_parameters(params, &f->priv_class, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM, NULL, NULL, "av."); // Add the parameters common to all avfilters. if (f->flags & AVFILTER_FLAG_SLICE_THREADS) { mlt_properties p = mlt_properties_new(); char key[20]; snprintf(key, 20, "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "av.threads"); mlt_properties_set(p, "description", "Maximum number of threads"); mlt_properties_set(p, "type", "integer"); mlt_properties_set_int(p, "minimum", 0); mlt_properties_set_int(p, "default", 0); } { mlt_properties p = mlt_properties_new(); char key[20]; int i = 0; snprintf(key, 20, "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "position"); mlt_properties_set(p, "description", "The MLT position value to set on avfilter frames"); mlt_properties_set(p, "type", "string"); mlt_properties_set(p, "default", "frame"); mlt_properties values = mlt_properties_new(); mlt_properties_set_data(p, "values", values, 0, (mlt_destructor) mlt_properties_close, NULL); snprintf(key, 20, "%d", i++); mlt_properties_set(values, key, "frame"); snprintf(key, 20, "%d", i++); mlt_properties_set(values, key, "filter"); snprintf(key, 20, "%d", i++); mlt_properties_set(values, key, "source"); snprintf(key, 20, "%d", i++); mlt_properties_set(values, key, "producer"); } } return metadata; } static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/avformat/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTAVFORMAT_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "avformat", create_service); MLT_REGISTER(mlt_service_producer_type, "avformat", create_service); MLT_REGISTER(mlt_service_producer_type, "avformat-novalidate", create_service); MLT_REGISTER_METADATA(mlt_service_consumer_type, "avformat", avformat_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_producer_type, "avformat", avformat_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_producer_type, "avformat-novalidate", metadata, "producer_avformat-novalidate.yml"); MLT_REGISTER(mlt_service_filter_type, "avcolour_space", create_service); MLT_REGISTER(mlt_service_filter_type, "avcolor_space", create_service); MLT_REGISTER(mlt_service_filter_type, "avdeinterlace", create_service); MLT_REGISTER(mlt_service_filter_type, "swscale", create_service); #if LIBSWSCALE_VERSION_MAJOR >= 9 MLT_REGISTER(mlt_service_filter_type, "sws_colortransform", create_service); MLT_REGISTER_METADATA(mlt_service_filter_type, "sws_colortransform", metadata, "filter_sws_colortransform.yml"); #endif MLT_REGISTER(mlt_service_link_type, "avcolour_space", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "avcolor_space", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "avdeinterlace", create_service); MLT_REGISTER(mlt_service_link_type, "swscale", mlt_link_filter_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "avcolour_space", metadata, "filter_avcolour_space.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "avcolor_space", metadata, "filter_avcolour_space.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "avdeinterlace", metadata, "filter_avdeinterlace.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "swscale", metadata, "filter_swscale.yml"); MLT_REGISTER_METADATA(mlt_service_link_type, "avcolour_space", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "avcolor_space", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "avdeinterlace", metadata, "link_avdeinterlace.yml"); MLT_REGISTER_METADATA(mlt_service_link_type, "swscale", mlt_link_filter_metadata, NULL); char dirname[PATH_MAX]; snprintf(dirname, PATH_MAX, "%s/avformat/blacklist.txt", mlt_environment("MLT_DATA")); mlt_properties blacklist = mlt_properties_load(dirname); snprintf(dirname, PATH_MAX, "%s/avformat/yuv_only.txt", mlt_environment("MLT_DATA")); mlt_properties_set_data(mlt_global_properties(), "avfilter.yuv_only", mlt_properties_load(dirname), 0, (mlt_destructor) mlt_properties_close, NULL); // Load a list of parameters impacted by consumer scale into global properties. snprintf(dirname, PATH_MAX, "%s/avformat/resolution_scale.yml", mlt_environment("MLT_DATA")); mlt_properties_set_data(mlt_global_properties(), "avfilter.resolution_scale", mlt_properties_parse_yaml(dirname), 0, (mlt_destructor) mlt_properties_close, NULL); const AVFilter *f = NULL; void *iterator = NULL; while ((f = (AVFilter *) av_filter_iterate(&iterator))) { // Support filters that have one input and one output of the same type. #if LIBAVFILTER_VERSION_INT < ((8 << 16) + (3 << 8) + 101) if (avfilter_pad_count(f->inputs) == 1 && avfilter_pad_count(f->outputs) == 1 && #else if (avfilter_filter_pad_count(f, 0) == 1 && avfilter_filter_pad_count(f, 1) == 1 && #endif avfilter_pad_get_type(f->inputs, 0) == avfilter_pad_get_type(f->outputs, 0) && !mlt_properties_get(blacklist, f->name)) { char service_name[1024] = "avfilter."; strncat(service_name, f->name, sizeof(service_name) - strlen(service_name) - 1); MLT_REGISTER(mlt_service_filter_type, service_name, filter_avfilter_init); MLT_REGISTER_METADATA(mlt_service_filter_type, service_name, avfilter_metadata, (void *) f->name); MLT_REGISTER(mlt_service_link_type, service_name, link_avfilter_init); MLT_REGISTER_METADATA(mlt_service_link_type, service_name, avfilter_metadata, (void *) f->name); } } mlt_properties_close(blacklist); MLT_REGISTER(mlt_service_filter_type, "swresample", create_service); MLT_REGISTER_METADATA(mlt_service_filter_type, "swresample", metadata, "filter_swresample.yml"); MLT_REGISTER(mlt_service_link_type, "swresample", create_service); MLT_REGISTER_METADATA(mlt_service_link_type, "swresample", metadata, "link_swresample.yml"); } mlt-7.38.0/src/modules/avformat/filter_avcolour_space.c000664 000000 000000 00000033043 15172202314 023161 0ustar00rootroot000000 000000 /* * filter_avcolour_space.c -- Colour space filter * Copyright (C) 2004-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include // ffmpeg Header files #include #include #include #include #include #if 0 // This test might come in handy elsewhere someday. static int is_big_endian( ) { union { int i; char c[ 4 ]; } big_endian_test; big_endian_test.i = 1; return big_endian_test.c[ 0 ] != 1; } #endif #define IMAGE_ALIGN (1) static int convert_mlt_to_av_cs(mlt_image_format format) { int value = 0; switch (format) { case mlt_image_rgb: value = AV_PIX_FMT_RGB24; break; case mlt_image_rgba: value = AV_PIX_FMT_RGBA; break; case mlt_image_yuv422: value = AV_PIX_FMT_YUYV422; break; case mlt_image_yuv420p: value = AV_PIX_FMT_YUV420P; break; case mlt_image_yuv422p16: value = AV_PIX_FMT_YUV422P16LE; break; case mlt_image_yuv420p10: value = AV_PIX_FMT_YUV420P10LE; break; case mlt_image_yuv444p10: value = AV_PIX_FMT_YUV444P10LE; break; case mlt_image_rgba64: value = AV_PIX_FMT_RGBA64LE; break; default: mlt_log_error(NULL, "[filter avcolor_space] Invalid format %s\n", mlt_image_format_name(format)); break; } return value; } // returns set_lumage_transfer result static int av_convert_image(uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, int out_width, int out_height, int in_width, int in_height, mlt_colorspace src_colorspace, mlt_colorspace dst_colorspace, int src_full_range, int dst_full_range) { uint8_t *in_data[4]; int in_stride[4]; uint8_t *out_data[4]; int out_stride[4]; int flags = mlt_get_sws_flags(in_width, in_height, in_fmt, out_width, out_height, out_fmt); int error = -1; if (in_fmt == AV_PIX_FMT_YUV422P16LE) mlt_image_format_planes(in_fmt, in_width, in_height, in, in_data, in_stride); else av_image_fill_arrays(in_data, in_stride, in, in_fmt, in_width, in_height, IMAGE_ALIGN); if (out_fmt == AV_PIX_FMT_YUV422P16LE) mlt_image_format_planes(out_fmt, out_width, out_height, out, out_data, out_stride); else av_image_fill_arrays(out_data, out_stride, out, out_fmt, out_width, out_height, IMAGE_ALIGN); struct SwsContext *context = sws_getContext( in_width, in_height, in_fmt, out_width, out_height, out_fmt, flags, NULL, NULL, NULL); if (context) { // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. if (out_fmt == AV_PIX_FMT_RGB24 || out_fmt == AV_PIX_FMT_RGBA) dst_colorspace = mlt_colorspace_bt601; error = mlt_set_luma_transfer(context, src_colorspace, dst_colorspace, src_full_range, dst_full_range); sws_scale(context, (const uint8_t *const *) in_data, in_stride, 0, in_height, out_data, out_stride); sws_freeContext(context); } return error; } /** Do it :-). */ static int convert_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int error = 0; int out_width = mlt_properties_get_int(properties, "convert_image_width"); int out_height = mlt_properties_get_int(properties, "convert_image_height"); mlt_properties_clear(properties, "convert_image_width"); mlt_properties_clear(properties, "convert_image_height"); if (*format != output_format || out_width) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE(mlt_frame_get_original_producer(frame))); int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); mlt_colorspace dst_colorspace = profile ? profile->colorspace : mlt_colorspace_bt601; const char *src_colorspace_str = mlt_properties_get(properties, "colorspace"); mlt_colorspace src_colorspace = mlt_image_colorspace_id(src_colorspace_str); if (src_colorspace == mlt_colorspace_unspecified || src_colorspace == mlt_colorspace_reserved || src_colorspace == mlt_colorspace_invalid) { src_colorspace = mlt_image_default_colorspace(*format, height); } int src_full_range = mlt_properties_get_int(properties, "full_range"); const char *dst_color_range = mlt_properties_get(properties, "consumer.color_range"); int dst_full_range = mlt_image_full_range(dst_color_range); // Fix some producers that may not be setting properties correctly if (output_format == mlt_image_rgb || output_format == mlt_image_rgba || output_format == mlt_image_rgba64) { dst_full_range = 1; dst_colorspace = mlt_colorspace_rgb; } if (*format == mlt_image_rgb || *format == mlt_image_rgba || *format == mlt_image_rgba64) { src_full_range = 1; src_colorspace = mlt_colorspace_rgb; } if (out_width <= 0) out_width = width; if (out_height <= 0) out_height = height; mlt_log_debug( NULL, "[filter avcolor_space] %s @ %dx%d -> %s @ %dx%d space %d->%d full %d->%d (%d)\n", mlt_image_format_name(*format), width, height, mlt_image_format_name(output_format), out_width, out_height, src_colorspace, dst_colorspace, src_full_range, dst_full_range, mlt_frame_get_position(frame)); int in_fmt = convert_mlt_to_av_cs(*format); int out_fmt = convert_mlt_to_av_cs(output_format); int size = FFMAX(av_image_get_buffer_size(out_fmt, out_width, out_height, IMAGE_ALIGN), mlt_image_format_size(output_format, out_width, out_height, NULL)); uint8_t *output = mlt_pool_alloc(size); if (out_width == width && out_height == height) { if (*format == mlt_image_rgba && output_format != mlt_image_rgba64) { register int len = width * height; uint8_t *alpha = mlt_pool_alloc(len); if (alpha) { // Extract the alpha mask from the RGBA image using Duff's Device register uint8_t *s = *image + 3; // start on the alpha component register uint8_t *d = alpha; register int n = (len + 7) / 8; switch (len % 8) { case 0: do { *d++ = *s; s += 4; case 7: *d++ = *s; s += 4; case 6: *d++ = *s; s += 4; case 5: *d++ = *s; s += 4; case 4: *d++ = *s; s += 4; case 3: *d++ = *s; s += 4; case 2: *d++ = *s; s += 4; case 1: *d++ = *s; s += 4; } while (--n > 0); } mlt_frame_set_alpha(frame, alpha, len, mlt_pool_release); } } else if (*format == mlt_image_rgba64 && output_format != mlt_image_rgba) { // Extract the alpha mask from the RGBA image int len = width * height; uint8_t *alpha = mlt_pool_alloc(len); if (alpha) { const uint16_t *s = ((uint16_t *) *image) + 3; // start on the alpha component uint8_t *d = alpha; for (int i = 0; i < len; i++) { *d = lrint((float) *s / 256.0); d++; s += 4; } mlt_frame_set_alpha(frame, alpha, len, mlt_pool_release); } } } else { // Scaling mlt_properties_clear(properties, "alpha"); } // Update the output if (!av_convert_image(output, *image, out_fmt, in_fmt, out_width, out_height, width, height, src_colorspace, dst_colorspace, src_full_range, dst_full_range)) { mlt_properties_set_int(properties, "colorspace", dst_colorspace); mlt_properties_set_int(properties, "full_range", dst_full_range); } mlt_frame_set_image(frame, output, size, mlt_pool_release); *image = output; if (out_width == width && out_height == height) { if (output_format == mlt_image_rgba && *format != mlt_image_rgba64) { register int len = width * height; int alpha_size = 0; uint8_t *alpha = mlt_frame_get_alpha_size(frame, &alpha_size); if (alpha && alpha_size >= len) { // Merge the alpha mask from into the RGBA image using Duff's Device register uint8_t *s = alpha; register uint8_t *d = *image + 3; // start on the alpha component register int n = (len + 7) / 8; switch (len % 8) { case 0: do { *d = *s++; d += 4; case 7: *d = *s++; d += 4; case 6: *d = *s++; d += 4; case 5: *d = *s++; d += 4; case 4: *d = *s++; d += 4; case 3: *d = *s++; d += 4; case 2: *d = *s++; d += 4; case 1: *d = *s++; d += 4; } while (--n > 0); } } } else if (output_format == mlt_image_rgba64 && *format != mlt_image_rgba) { int len = width * height; int alpha_size = 0; uint8_t *alpha = mlt_frame_get_alpha_size(frame, &alpha_size); if (alpha && alpha_size >= len) { const uint8_t *s = alpha; uint16_t *d = ((uint16_t *) *image) + 3; // start on the alpha component for (int i = 0; i < len; i++) { *d = *s << 8; d += 4; s++; } } } } *format = output_format; mlt_properties_set_int(properties, "format", output_format); mlt_properties_set_int(properties, "width", out_width); mlt_properties_set_int(properties, "height", out_height); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { if (!frame->convert_image) frame->convert_image = convert_image; return frame; } /** Constructor for the filter. */ mlt_filter filter_avcolour_space_init(void *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) filter->process = filter_process; return filter; } mlt-7.38.0/src/modules/avformat/filter_avcolour_space.yml000664 000000 000000 00000000662 15172202314 023541 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: avcolor_space title: FFmpeg Image Converter version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Video - Hidden description: Converts the colorspace and pixel format. notes: > This is not intended to be created directly. Rather, the loader producer loads it if it is available to set the convert_image function pointer on frames. mlt-7.38.0/src/modules/avformat/filter_avdeinterlace.c000664 000000 000000 00000026215 15172202314 022765 0ustar00rootroot000000 000000 /* * filter_avdeinterlace.c -- deinterlace filter * Copyright (C) 2003-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include // ffmpeg Header files #include #include #ifdef USE_MMX #include "mmx.h" #else #define MAX_NEG_CROP 1024 static uint8_t ff_cropTbl[256 + 2 * MAX_NEG_CROP] = { 0, }; #endif #ifdef USE_MMX #define DEINT_INPLACE_LINE_LUM \ movd_m2r(lum_m4[0], mm0); \ movd_m2r(lum_m3[0], mm1); \ movd_m2r(lum_m2[0], mm2); \ movd_m2r(lum_m1[0], mm3); \ movd_m2r(lum[0], mm4); \ punpcklbw_r2r(mm7, mm0); \ movd_r2m(mm2, lum_m4[0]); \ punpcklbw_r2r(mm7, mm1); \ punpcklbw_r2r(mm7, mm2); \ punpcklbw_r2r(mm7, mm3); \ punpcklbw_r2r(mm7, mm4); \ paddw_r2r(mm3, mm1); \ psllw_i2r(1, mm2); \ paddw_r2r(mm4, mm0); \ psllw_i2r(2, mm1); \ paddw_r2r(mm6, mm2); \ paddw_r2r(mm2, mm1); \ psubusw_r2r(mm0, mm1); \ psrlw_i2r(3, mm1); \ packuswb_r2r(mm7, mm1); \ movd_r2m(mm1, lum_m2[0]); #define DEINT_LINE_LUM \ movd_m2r(lum_m4[0], mm0); \ movd_m2r(lum_m3[0], mm1); \ movd_m2r(lum_m2[0], mm2); \ movd_m2r(lum_m1[0], mm3); \ movd_m2r(lum[0], mm4); \ punpcklbw_r2r(mm7, mm0); \ punpcklbw_r2r(mm7, mm1); \ punpcklbw_r2r(mm7, mm2); \ punpcklbw_r2r(mm7, mm3); \ punpcklbw_r2r(mm7, mm4); \ paddw_r2r(mm3, mm1); \ psllw_i2r(1, mm2); \ paddw_r2r(mm4, mm0); \ psllw_i2r(2, mm1); \ paddw_r2r(mm6, mm2); \ paddw_r2r(mm2, mm1); \ psubusw_r2r(mm0, mm1); \ psrlw_i2r(3, mm1); \ packuswb_r2r(mm7, mm1); \ movd_r2m(mm1, dst[0]); #endif /* filter parameters: [-1 4 2 4 -1] // 8 */ static inline void deinterlace_line(uint8_t *dst, const uint8_t *lum_m4, const uint8_t *lum_m3, const uint8_t *lum_m2, const uint8_t *lum_m1, const uint8_t *lum, int size) { #ifndef USE_MMX uint8_t *cm = ff_cropTbl + MAX_NEG_CROP; int sum; for (; size > 0; size--) { sum = -lum_m4[0]; sum += lum_m3[0] << 2; sum += lum_m2[0] << 1; sum += lum_m1[0] << 2; sum += -lum[0]; dst[0] = cm[(sum + 4) >> 3]; lum_m4++; lum_m3++; lum_m2++; lum_m1++; lum++; dst++; } #else { mmx_t rounder; rounder.uw[0] = 4; rounder.uw[1] = 4; rounder.uw[2] = 4; rounder.uw[3] = 4; pxor_r2r(mm7, mm7); movq_m2r(rounder, mm6); } for (; size > 3; size -= 4) { DEINT_LINE_LUM lum_m4 += 4; lum_m3 += 4; lum_m2 += 4; lum_m1 += 4; lum += 4; dst += 4; } #endif } static inline void deinterlace_line_inplace( uint8_t *lum_m4, uint8_t *lum_m3, uint8_t *lum_m2, uint8_t *lum_m1, uint8_t *lum, int size) { #ifndef USE_MMX uint8_t *cm = ff_cropTbl + MAX_NEG_CROP; int sum; for (; size > 0; size--) { sum = -lum_m4[0]; sum += lum_m3[0] << 2; sum += lum_m2[0] << 1; lum_m4[0] = lum_m2[0]; sum += lum_m1[0] << 2; sum += -lum[0]; lum_m2[0] = cm[(sum + 4) >> 3]; lum_m4++; lum_m3++; lum_m2++; lum_m1++; lum++; } #else { mmx_t rounder; rounder.uw[0] = 4; rounder.uw[1] = 4; rounder.uw[2] = 4; rounder.uw[3] = 4; pxor_r2r(mm7, mm7); movq_m2r(rounder, mm6); } for (; size > 3; size -= 4) { DEINT_INPLACE_LINE_LUM lum_m4 += 4; lum_m3 += 4; lum_m2 += 4; lum_m1 += 4; lum += 4; } #endif } /* deinterlacing : 2 temporal taps, 3 spatial taps linear filter. The top field is copied as is, but the bottom field is deinterlaced against the top field. */ static inline void deinterlace_bottom_field( uint8_t *dst, int dst_wrap, const uint8_t *src1, int src_wrap, int width, int height) { const uint8_t *src_m2, *src_m1, *src_0, *src_p1, *src_p2; int y; src_m2 = src1; src_m1 = src1; src_0 = &src_m1[src_wrap]; src_p1 = &src_0[src_wrap]; src_p2 = &src_p1[src_wrap]; for (y = 0; y < (height - 2); y += 2) { memcpy(dst, src_m1, width); dst += dst_wrap; deinterlace_line(dst, src_m2, src_m1, src_0, src_p1, src_p2, width); src_m2 = src_0; src_m1 = src_p1; src_0 = src_p2; src_p1 += 2 * src_wrap; src_p2 += 2 * src_wrap; dst += dst_wrap; } memcpy(dst, src_m1, width); dst += dst_wrap; /* do last line */ deinterlace_line(dst, src_m2, src_m1, src_0, src_0, src_0, width); } static inline void deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap, int width, int height) { uint8_t *src_m1, *src_0, *src_p1, *src_p2; int y; uint8_t *buf; buf = (uint8_t *) av_malloc(width); src_m1 = src1; memcpy(buf, src_m1, width); src_0 = &src_m1[src_wrap]; src_p1 = &src_0[src_wrap]; src_p2 = &src_p1[src_wrap]; for (y = 0; y < (height - 2); y += 2) { deinterlace_line_inplace(buf, src_m1, src_0, src_p1, src_p2, width); src_m1 = src_p1; src_0 = src_p2; src_p1 += 2 * src_wrap; src_p2 += 2 * src_wrap; } /* do last line */ deinterlace_line_inplace(buf, src_m1, src_0, src_0, src_0, width); av_free(buf); } /* deinterlace - if not supported return -1 */ static int mlt_avpicture_deinterlace(uint8_t *dst_data[4], int dst_stride[4], uint8_t *src_data[4], int src_stride[4], int pix_fmt, int width, int height) { int i; if (pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUV422P && pix_fmt != AV_PIX_FMT_YUYV422 && pix_fmt != AV_PIX_FMT_YUV444P && pix_fmt != AV_PIX_FMT_YUV411P) return -1; if ((width & 3) != 0 || (height & 3) != 0) return -1; if (pix_fmt != AV_PIX_FMT_YUYV422) { for (i = 0; i < 3; i++) { if (i == 1) { switch (pix_fmt) { case AV_PIX_FMT_YUV420P: width >>= 1; height >>= 1; break; case AV_PIX_FMT_YUV422P: width >>= 1; break; case AV_PIX_FMT_YUV411P: width >>= 2; break; default: break; } } if (src_data[0] == dst_data[0]) { deinterlace_bottom_field_inplace(dst_data[i], dst_stride[i], width, height); } else { deinterlace_bottom_field(dst_data[i], dst_stride[i], src_data[i], src_stride[i], width, height); } } } else { if (src_data[0] == dst_data[0]) { deinterlace_bottom_field_inplace(dst_data[0], dst_stride[0], width << 1, height); } else { deinterlace_bottom_field(dst_data[0], dst_stride[0], src_data[0], src_stride[0], width << 1, height); } } #ifdef USE_MMX emms(); #endif return 0; } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; int deinterlace = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "consumer.progressive"); // Determine if we need a writable version or not if (deinterlace && !writable) writable = !mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "progressive"); // Get the input image *format = mlt_image_yuv422; error = mlt_frame_get_image(frame, image, format, width, height, 1); // Check that we want progressive and we aren't already progressive if (deinterlace && *format == mlt_image_yuv422 && *image != NULL && !mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "progressive")) { // Create a picture uint8_t *image_data[4]; int strides[4]; // Fill the picture av_image_fill_arrays(image_data, strides, *image, AV_PIX_FMT_YUYV422, *width, *height, 1); mlt_log_timings_begin(); mlt_avpicture_deinterlace(image_data, strides, image_data, strides, AV_PIX_FMT_YUYV422, *width, *height); mlt_log_timings_end(NULL, "mlt_avpicture_deinterlace"); // Make sure that others know the frame is deinterlaced mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "progressive", 1); } return error; } /** Deinterlace filter processing - this should be lazy evaluation here... */ static mlt_frame deinterlace_process(mlt_filter filter, mlt_frame frame) { // Push the get_image method on to the stack mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_avdeinterlace_init(void *arg) { #ifndef USE_MMX if (ff_cropTbl[MAX_NEG_CROP + 1] == 0) { int i; for (i = 0; i < 256; i++) ff_cropTbl[i + MAX_NEG_CROP] = i; for (i = 0; i < MAX_NEG_CROP; i++) { ff_cropTbl[i] = 0; ff_cropTbl[i + MAX_NEG_CROP + 256] = 255; } } #endif mlt_filter filter = mlt_filter_new(); if (filter != NULL) filter->process = deinterlace_process; return filter; } mlt-7.38.0/src/modules/avformat/filter_avdeinterlace.yml000664 000000 000000 00000000736 15172202314 023344 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: avdeinterlace title: Legacy FFmpeg Deinterlacer (*DEPRECATED*) version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Video - Hidden description: Deinterlace interlaced video. notes: > This is not intended to be created directly. Rather, the loader producer loads it if it is available to set deinterlace interlaced input when the consumer or profile is set to progressive. mlt-7.38.0/src/modules/avformat/filter_avfilter.c000664 000000 000000 00000126325 15172202314 021776 0ustar00rootroot000000 000000 /* * filter_avfilter.c -- provide various filters based on libavfilter * Copyright (C) 2016-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 700 #undef _XOPEN_SOURCE #define _XOPEN_SOURCE 700 #endif #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #define PARAM_PREFIX "av." #define PARAM_PREFIX_LEN (sizeof(PARAM_PREFIX) - 1) typedef struct { AVFilter *avfilter; AVFilterContext *avbuffsink_ctx; AVFilterContext *avbuffsrc_ctx; AVFilterContext *avfilter_ctx; AVFilterContext *scale_ctx; AVFilterContext *pad_ctx; AVFilterGraph *avfilter_graph; AVFrame *avinframe; AVFrame *avoutframe; int format; int width; int height; mlt_colorspace colorspace; int full_range; int reset; #if HAVE_FFMPEG_CH_LAYOUT AVChannelLayout ch_layout; #endif } private_data; #if LIBAVUTIL_VERSION_INT >= ((56 << 16) + (35 << 8) + 101) static int animatable_avoption(const AVOption *opt) { return opt && (opt->flags & AV_OPT_FLAG_RUNTIME_PARAM) && opt->type != AV_OPT_TYPE_COLOR; } #endif int is_rgb(int format) { if (format == mlt_image_rgb || format == mlt_image_rgba || format == mlt_image_rgba64) { return 1; } return 0; } static void property_changed(mlt_service owner, mlt_filter filter, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && strncmp(PARAM_PREFIX, name, PARAM_PREFIX_LEN) == 0) { private_data *pdata = (private_data *) filter->child; if (pdata->avfilter_ctx) { mlt_service_lock(MLT_FILTER_SERVICE(filter)); const AVOption *opt = av_opt_find(pdata->avfilter_ctx->priv, name + PARAM_PREFIX_LEN, 0, 0, 0); #if LIBAVUTIL_VERSION_INT >= ((56 << 16) + (35 << 8) + 101) pdata->reset = opt && !(animatable_avoption(opt) && mlt_properties_is_anim(MLT_FILTER_PROPERTIES(filter), name)); #else pdata->reset = opt && !mlt_properties_is_anim(MLT_FILTER_PROPERTIES(filter), name); #endif mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } } } static void set_avfilter_options(mlt_filter filter, double scale) { private_data *pdata = (private_data *) filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); int i; int count = mlt_properties_count(filter_properties); mlt_properties scale_map = mlt_properties_get_data(filter_properties, "_resolution_scale", NULL); for (i = 0; i < count; i++) { const char *param_name = mlt_properties_get_name(filter_properties, i); if (param_name && strncmp(PARAM_PREFIX, param_name, PARAM_PREFIX_LEN) == 0) { const AVOption *opt = av_opt_find(pdata->avfilter_ctx->priv, param_name + PARAM_PREFIX_LEN, 0, 0, 0); const char *value = mlt_properties_get_value(filter_properties, i); #if LIBAVUTIL_VERSION_INT >= ((56 << 16) + (35 << 8) + 101) if (opt && !(animatable_avoption(opt) && mlt_properties_is_anim(filter_properties, param_name))) #else if (opt && !mlt_properties_is_anim(filter_properties, param_name)) #endif { if (scale != 1.0) { double scale2 = mlt_properties_get_double(scale_map, opt->name); if (scale2 != 0.0) { double x = mlt_properties_get_double(filter_properties, param_name); x *= scale * scale2; mlt_properties_set_double(filter_properties, "_avfilter_temp", x); value = mlt_properties_get(filter_properties, "_avfilter_temp"); } } av_opt_set(pdata->avfilter_ctx->priv, opt->name, value, 0); } } } } static void send_avformat_commands(mlt_filter filter, mlt_frame frame, private_data *pdata, double scale) { #if LIBAVUTIL_VERSION_INT >= ((56 << 16) + (35 << 8) + 101) mlt_properties prop = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); int length = mlt_filter_get_length2(filter, frame); mlt_properties scale_map = mlt_properties_get_data(prop, "_resolution_scale", NULL); int count = mlt_properties_count(prop); int i; for (i = 0; i < count; i++) { char *name = mlt_properties_get_name(prop, i); if (!strncmp(name, PARAM_PREFIX, PARAM_PREFIX_LEN)) { const AVOption *opt = av_opt_find(pdata->avfilter_ctx->priv, name + PARAM_PREFIX_LEN, 0, 0, 0); if (animatable_avoption(opt) && mlt_properties_is_anim(prop, name)) { double x = mlt_properties_anim_get_double(prop, name, position, length); if (scale != 1.0) { double scale2 = mlt_properties_get_double(scale_map, opt->name); if (scale2 != 0.0) { x *= scale * scale2; } } mlt_properties_set_double(prop, "_avfilter_temp", x); char *new_val = mlt_properties_get(prop, "_avfilter_temp"); char *cur_val = NULL; av_opt_get(pdata->avfilter_ctx->priv, name + PARAM_PREFIX_LEN, AV_OPT_SEARCH_CHILDREN, (uint8_t **) &cur_val); if (new_val && cur_val && strcmp(new_val, cur_val)) { avfilter_graph_send_command(pdata->avfilter_graph, pdata->avfilter->name, name + PARAM_PREFIX_LEN, new_val, NULL, 0, 0); } av_free(cur_val); } } } #endif } static void init_audio_filtergraph(mlt_filter filter, mlt_audio_format format, int frequency, int channels) { private_data *pdata = (private_data *) filter->child; const AVFilter *abuffersrc = avfilter_get_by_name("abuffer"); const AVFilter *abuffersink = avfilter_get_by_name("abuffersink"); int sample_fmts[] = {-1, -1}; int sample_rates[] = {-1, -1}; int channel_counts[] = {-1, -1}; char channel_layout_str[64]; int ret; pdata->format = format; // Set up formats sample_fmts[0] = mlt_to_av_sample_format(format); sample_rates[0] = frequency; channel_counts[0] = channels; #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_default(&pdata->ch_layout, channels); av_channel_layout_describe(&pdata->ch_layout, channel_layout_str, sizeof(channel_layout_str)); #else int64_t channel_layouts[] = {-1, -1}; channel_layouts[0] = av_get_default_channel_layout(channels); av_get_channel_layout_string(channel_layout_str, sizeof(channel_layout_str), 0, channel_layouts[0]); #endif // Destroy the current filter graph avfilter_graph_free(&pdata->avfilter_graph); // Create the new filter graph pdata->avfilter_graph = avfilter_graph_alloc(); if (!pdata->avfilter_graph) { mlt_log_error(filter, "Cannot create filter graph\n"); goto fail; } // Set thread count if supported. if (pdata->avfilter->flags & AVFILTER_FLAG_SLICE_THREADS) { av_opt_set_int(pdata->avfilter_graph, "threads", FFMAX(0, mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "av.threads")), 0); } // Initialize the buffer source filter context pdata->avbuffsrc_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, abuffersrc, "in"); if (!pdata->avbuffsrc_ctx) { mlt_log_error(filter, "Cannot create audio buffer source\n"); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "sample_rate", sample_rates[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src sample rate %d\n", sample_rates[0]); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "sample_fmt", sample_fmts[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src sample format %d\n", sample_fmts[0]); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "channels", channel_counts[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src channels %d\n", channel_counts[0]); goto fail; } ret = av_opt_set(pdata->avbuffsrc_ctx, "channel_layout", channel_layout_str, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src channel layout %s\n", channel_layout_str); goto fail; } ret = avfilter_init_str(pdata->avbuffsrc_ctx, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot init buffer source\n"); goto fail; } // Initialize the buffer sink filter context pdata->avbuffsink_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, abuffersink, "out"); if (!pdata->avbuffsink_ctx) { mlt_log_error(filter, "Cannot create audio buffer sink\n"); goto fail; } #if LIBAVFILTER_VERSION_INT >= ((10 << 16) + (6 << 8) + 100) ret = av_opt_set_array(pdata->avbuffsink_ctx, "sample_formats", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_SAMPLE_FMT, sample_fmts); if (ret < 0) { mlt_log_error(filter, "Cannot set sink sample formats\n"); goto fail; } ret = av_opt_set_array(pdata->avbuffsink_ctx, "samplerates", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_INT, sample_rates); if (ret < 0) { mlt_log_error(filter, "Cannot set sink sample rates\n"); goto fail; } ret = av_opt_set_array(pdata->avbuffsink_ctx, "channel_layouts", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_CHLAYOUT, &pdata->ch_layout); if (ret < 0) { mlt_log_error(filter, "Cannot set sink channel layouts\n"); goto fail; } #else ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set sink sample formats\n"); goto fail; } ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set sink sample rates\n"); goto fail; } #if HAVE_FFMPEG_CH_LAYOUT ret = av_opt_set(pdata->avbuffsink_ctx, "ch_layouts", channel_layout_str, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set sink ch_layouts\n"); goto fail; } #else ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "channel_counts", channel_counts, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set sink channel counts\n"); goto fail; } ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set sink channel_layouts\n"); goto fail; } #endif #endif ret = avfilter_init_str(pdata->avbuffsink_ctx, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot init buffer sink\n"); goto fail; } // Initialize the filter context pdata->avfilter_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, pdata->avfilter, pdata->avfilter->name); if (!pdata->avfilter_ctx) { mlt_log_error(filter, "Cannot create audio filter\n"); goto fail; } set_avfilter_options(filter, 1.0); ret = avfilter_init_str(pdata->avfilter_ctx, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot init filter\n"); goto fail; } // Connect the filters ret = avfilter_link(pdata->avbuffsrc_ctx, 0, pdata->avfilter_ctx, 0); if (ret < 0) { mlt_log_error(filter, "Cannot link src to filter\n"); goto fail; } ret = avfilter_link(pdata->avfilter_ctx, 0, pdata->avbuffsink_ctx, 0); if (ret < 0) { mlt_log_error(filter, "Cannot link filter to sink\n"); goto fail; } // Configure the graph. ret = avfilter_graph_config(pdata->avfilter_graph, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot configure the filter graph\n"); goto fail; } return; fail: avfilter_graph_free(&pdata->avfilter_graph); } static void init_image_filtergraph(mlt_filter filter, mlt_image_format format, int width, int height, mlt_colorspace colorspace, int full_range, double resolution_scale) { private_data *pdata = (private_data *) filter->child; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); const AVFilter *buffersrc = avfilter_get_by_name("buffer"); const AVFilter *buffersink = avfilter_get_by_name("buffersink"); const AVFilter *scale = avfilter_get_by_name("scale"); const AVFilter *pad = avfilter_get_by_name("pad"); mlt_properties p = mlt_properties_new(); enum AVPixelFormat pixel_fmts[] = {-1, -1}; AVRational sar = (AVRational){profile->sample_aspect_num, profile->sample_aspect_den}; AVRational timebase = (AVRational){profile->frame_rate_den, profile->frame_rate_num}; AVRational framerate = (AVRational){profile->frame_rate_num, profile->frame_rate_den}; int avcolorspace = mlt_to_av_colorspace(colorspace, pdata->height); int color_range = mlt_to_av_color_range(full_range); int ret; pdata->format = format; pdata->width = width; pdata->height = height; pdata->colorspace = colorspace; pdata->full_range = full_range; // Set up formats pixel_fmts[0] = mlt_to_av_image_format(format); // Destroy the current filter graph avfilter_graph_free(&pdata->avfilter_graph); // Create the new filter graph pdata->avfilter_graph = avfilter_graph_alloc(); if (!pdata->avfilter_graph) { mlt_log_error(filter, "Cannot create filter graph\n"); goto fail; } pdata->avfilter_graph->scale_sws_opts = av_strdup("flags=" MLT_AVFILTER_SWS_FLAGS); // Set thread count if supported. if (pdata->avfilter->flags & AVFILTER_FLAG_SLICE_THREADS) { av_opt_set_int(pdata->avfilter_graph, "threads", FFMAX(0, mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "av.threads")), 0); } // Initialize the buffer source filter context pdata->avbuffsrc_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, buffersrc, "in"); if (!pdata->avbuffsrc_ctx) { mlt_log_error(filter, "Cannot create image buffer source\n"); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "width", width, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src width %d\n", width); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "height", height, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src height %d\n", height); goto fail; } ret = av_opt_set_pixel_fmt(pdata->avbuffsrc_ctx, "pix_fmt", pixel_fmts[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src pixel format %d\n", pixel_fmts[0]); goto fail; } ret = av_opt_set_q(pdata->avbuffsrc_ctx, "sar", sar, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src sar %d/%d\n", sar.num, sar.den); goto fail; } ret = av_opt_set_q(pdata->avbuffsrc_ctx, "time_base", timebase, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src time_base %d/%d\n", timebase.num, timebase.den); goto fail; } ret = av_opt_set_q(pdata->avbuffsrc_ctx, "frame_rate", framerate, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src frame_rate %d/%d\n", framerate.num, framerate.den); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "colorspace", avcolorspace, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src colorspace %d\n", avcolorspace); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "range", color_range, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set src range %d\n", color_range); goto fail; } ret = avfilter_init_str(pdata->avbuffsrc_ctx, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot init buffer source\n"); goto fail; } // Initialize the buffer sink filter context pdata->avbuffsink_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, buffersink, "out"); if (!pdata->avbuffsink_ctx) { mlt_log_error(filter, "Cannot create image buffer sink\n"); goto fail; } #if LIBAVFILTER_VERSION_INT >= ((10 << 16) + (6 << 8) + 100) ret = av_opt_set_array(pdata->avbuffsink_ctx, "pixel_formats", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_PIXEL_FMT, pixel_fmts); #else ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "pix_fmts", pixel_fmts, -1, AV_OPT_SEARCH_CHILDREN); #endif if (ret < 0) { mlt_log_error(filter, "Cannot set sink pixel formats\n"); goto fail; } ret = avfilter_init_str(pdata->avbuffsink_ctx, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot init buffer sink\n"); goto fail; } // Initialize the filter context pdata->avfilter_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, pdata->avfilter, pdata->avfilter->name); if (!pdata->avfilter_ctx) { mlt_log_error(filter, "Cannot create video filter\n"); goto fail; } set_avfilter_options(filter, resolution_scale); if (!strcmp("lut3d", pdata->avfilter->name)) { #if defined(__GLIBC__) || defined(__APPLE__) || (__FreeBSD__) // LUT data files use period for the decimal point regardless of LC_NUMERIC. mlt_locale_t posix_locale = newlocale(LC_NUMERIC_MASK, "POSIX", NULL); // Get the current locale and switch to POSIX local. mlt_locale_t orig_locale = uselocale(posix_locale); // Initialize the filter. ret = avfilter_init_str(pdata->avfilter_ctx, NULL); // Restore the original locale. uselocale(orig_locale); freelocale(posix_locale); #else // Get the current locale and switch to POSIX local. char *orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); // Initialize the filter. ret = avfilter_init_str(pdata->avfilter_ctx, NULL); // Restore the original locale. setlocale(LC_NUMERIC, orig_localename); free(orig_localename); #endif } else { ret = avfilter_init_str(pdata->avfilter_ctx, NULL); } if (ret < 0) { mlt_log_error(filter, "Cannot init scale filter: %s\n", av_err2str(ret)); goto fail; } // scale=w=1280:h=720:force_original_aspect_ratio=decrease, pad=w=1280:h=720:x=(ow-iw)/2:y=(oh-ih)/2 // Initialize the scale filter context pdata->scale_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, scale, "scale"); if (!pdata->scale_ctx) { mlt_log_error(filter, "Cannot create scale filer\n"); goto fail; } mlt_properties_set_int(p, "w", width); mlt_properties_set_int(p, "h", height); const AVOption *opt = av_opt_find(pdata->scale_ctx->priv, "w", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->scale_ctx->priv, opt->name, mlt_properties_get(p, "w"), 0); if (ret < 0) { mlt_log_error(filter, "Cannot set scale width\n"); goto fail; } } opt = av_opt_find(pdata->scale_ctx->priv, "h", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->scale_ctx->priv, opt->name, mlt_properties_get(p, "h"), 0); if (ret < 0) { mlt_log_error(filter, "Cannot set scale height\n"); goto fail; } } ret = av_opt_set_int(pdata->scale_ctx, "force_original_aspect_ratio", 1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(filter, "Cannot set scale force_original_aspect_ratio\n"); goto fail; } opt = av_opt_find(pdata->scale_ctx->priv, "flags", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->scale_ctx->priv, opt->name, MLT_AVFILTER_SWS_FLAGS, 0); if (ret < 0) { mlt_log_error(filter, "Cannot set scale flags\n"); goto fail; } } ret = avfilter_init_str(pdata->scale_ctx, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot init scale filter\n"); goto fail; } // Initialize the padding filter context pdata->pad_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, pad, "pad"); if (!pdata->pad_ctx) { mlt_log_error(filter, "Cannot create pad filter\n"); goto fail; } opt = av_opt_find(pdata->pad_ctx->priv, "w", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->pad_ctx->priv, opt->name, mlt_properties_get(p, "w"), 0); if (ret < 0) { mlt_log_error(filter, "Cannot set pad width\n"); goto fail; } } opt = av_opt_find(pdata->pad_ctx->priv, "h", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->pad_ctx->priv, opt->name, mlt_properties_get(p, "h"), 0); if (ret < 0) { mlt_log_error(filter, "Cannot pad scale height\n"); goto fail; } } opt = av_opt_find(pdata->pad_ctx->priv, "x", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->pad_ctx->priv, opt->name, "(ow-iw)/2", 0); if (ret < 0) { mlt_log_error(filter, "Cannot set pad x\n"); goto fail; } } opt = av_opt_find(pdata->pad_ctx->priv, "y", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->pad_ctx->priv, opt->name, "(oh-ih)/2", 0); if (ret < 0) { mlt_log_error(filter, "Cannot set pad y\n"); goto fail; } } ret = avfilter_init_str(pdata->pad_ctx, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot init pad filter\n"); goto fail; } // Connect the filters ret = avfilter_link(pdata->avbuffsrc_ctx, 0, pdata->avfilter_ctx, 0); if (ret < 0) { mlt_log_error(filter, "Cannot link src to filter\n"); goto fail; } ret = avfilter_link(pdata->avfilter_ctx, 0, pdata->scale_ctx, 0); if (ret < 0) { mlt_log_error(filter, "Cannot link filter to scale\n"); goto fail; } ret = avfilter_link(pdata->scale_ctx, 0, pdata->pad_ctx, 0); if (ret < 0) { mlt_log_error(filter, "Cannot link scale to pad\n"); goto fail; } ret = avfilter_link(pdata->pad_ctx, 0, pdata->avbuffsink_ctx, 0); if (ret < 0) { mlt_log_error(filter, "Cannot link pad to sink\n"); goto fail; } // Configure the graph. ret = avfilter_graph_config(pdata->avfilter_graph, NULL); if (ret < 0) { mlt_log_error(filter, "Cannot configure the filter graph\n"); goto fail; } return; fail: mlt_properties_close(p); avfilter_graph_free(&pdata->avfilter_graph); } static mlt_position get_position(mlt_filter filter, mlt_frame frame) { mlt_position position = mlt_frame_get_position(frame); const char *pos_type = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "position"); if (pos_type) { if (!strcmp("filter", pos_type)) { position = mlt_filter_get_position(filter, frame); } else if (!strcmp("source", pos_type)) { position = mlt_frame_original_position(frame); } else if (!strcmp("producer", pos_type)) { mlt_producer producer = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "service", NULL); if (producer) position = mlt_producer_position(producer); } } else { private_data *pdata = (private_data *) filter->child; if (!strcmp("subtitles", pdata->avfilter->name)) position = mlt_frame_original_position(frame); } return position; } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) filter->child; double fps = mlt_profile_fps(mlt_service_profile(MLT_FILTER_SERVICE(filter))); int64_t samplepos = mlt_audio_calculate_samples_to_position(fps, *frequency, get_position(filter, frame)); int bufsize = 0; int ret; // Get the producer's audio mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); bufsize = mlt_audio_format_size(*format, *samples, *channels); mlt_service_lock(MLT_FILTER_SERVICE(filter)); if (pdata->reset || pdata->format != *format) { init_audio_filtergraph(filter, *format, *frequency, *channels); pdata->reset = 0; } if (pdata->avfilter_graph) { // Set up the input frame mlt_channel_layout layout = mlt_get_channel_layout_or_default(mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "channel_layout"), *channels); pdata->avinframe->sample_rate = *frequency; pdata->avinframe->format = mlt_to_av_sample_format(*format); #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_from_mask(&pdata->avinframe->ch_layout, mlt_to_av_channel_layout(layout)); #else pdata->avinframe->channel_layout = mlt_to_av_channel_layout(layout); pdata->avinframe->channels = *channels; #endif pdata->avinframe->nb_samples = *samples; pdata->avinframe->pts = samplepos; ret = av_frame_get_buffer(pdata->avinframe, 1); if (ret < 0) { mlt_log_error(filter, "Cannot get in frame buffer\n"); } if (av_sample_fmt_is_planar(pdata->avinframe->format)) { int i = 0; int stride = bufsize / *channels; for (i = 0; i < *channels; i++) { memcpy(pdata->avinframe->extended_data[i], (uint8_t *) *buffer + stride * i, stride); } } else { memcpy(pdata->avinframe->extended_data[0], (uint8_t *) *buffer, bufsize); } send_avformat_commands(filter, frame, pdata, 1.0); // Run the frame through the filter graph ret = av_buffersrc_add_frame(pdata->avbuffsrc_ctx, pdata->avinframe); if (ret < 0) { mlt_log_error(filter, "Cannot add frame to buffer source\n"); } ret = av_buffersink_get_frame(pdata->avbuffsink_ctx, pdata->avoutframe); if (ret < 0) { mlt_log_error(filter, "Cannot get frame from buffer sink\n"); } // Sanity check the output frame #if HAVE_FFMPEG_CH_LAYOUT if (*channels != pdata->avoutframe->ch_layout.nb_channels #else if (*channels != pdata->avoutframe->channels #endif || *samples != pdata->avoutframe->nb_samples || *frequency != pdata->avoutframe->sample_rate) { mlt_log_error(filter, "Unexpected return format\n"); goto exit; } // Copy the filter output into the original buffer if (av_sample_fmt_is_planar(pdata->avoutframe->format)) { int stride = bufsize / *channels; int i = 0; for (i = 0; i < *channels; i++) { memcpy((uint8_t *) *buffer + stride * i, pdata->avoutframe->extended_data[i], stride); } } else { memcpy((uint8_t *) *buffer, pdata->avoutframe->extended_data[0], bufsize); } } exit: av_frame_unref(pdata->avinframe); av_frame_unref(pdata->avoutframe); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = mlt_frame_pop_service(frame); private_data *pdata = (private_data *) filter->child; int64_t pos = get_position(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); int ret; mlt_log_debug(MLT_FILTER_SERVICE(filter), "position %" PRId64 "\n", pos); if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "_yuv_only")) { if (is_rgb(*format)) *format = mlt_image_yuv422; } else { *format = mlt_get_supported_image_format(*format); } mlt_frame_get_image(frame, image, format, width, height, 0); mlt_service_lock(MLT_FILTER_SERVICE(filter)); double scale = mlt_profile_scale_width(profile, *width); const char *colorspace_str = mlt_properties_get(frame_properties, "colorspace"); mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); int full_range = mlt_properties_get_int(frame_properties, "full_range"); if (pdata->reset || pdata->format != *format || pdata->width != *width || pdata->height != *height || pdata->colorspace != colorspace || pdata->full_range != full_range) { init_image_filtergraph(filter, *format, *width, *height, colorspace, full_range, scale); pdata->reset = 0; } if (pdata->avfilter_graph) { pdata->avinframe->width = *width; pdata->avinframe->height = *height; pdata->avinframe->format = mlt_to_av_image_format(*format); pdata->avinframe->sample_aspect_ratio = (AVRational){profile->sample_aspect_num, profile->sample_aspect_den}; pdata->avinframe->pts = pos; #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) if (!mlt_properties_get_int(frame_properties, "progressive")) pdata->avinframe->flags |= AV_FRAME_FLAG_INTERLACED; else pdata->avinframe->flags &= ~AV_FRAME_FLAG_INTERLACED; if (mlt_properties_get_int(frame_properties, "top_field_first")) pdata->avinframe->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; else pdata->avinframe->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; #else pdata->avinframe->interlaced_frame = !mlt_properties_get_int(frame_properties, "progressive"); pdata->avinframe->top_field_first = mlt_properties_get_int(frame_properties, "top_field_first"); #endif const char *primaries_str = mlt_properties_get(frame_properties, "color_primaries"); mlt_color_primaries primaries = mlt_image_color_pri_id(primaries_str); if (primaries == mlt_color_pri_none) primaries = mlt_image_default_primaries(colorspace, *height); pdata->avinframe->color_primaries = mlt_to_av_color_primaries(primaries); const char *color_trc_str = mlt_properties_get(frame_properties, "color_trc"); mlt_color_trc trc = mlt_image_color_trc_id(color_trc_str); if (trc == mlt_color_trc_none) trc = mlt_image_default_trc(colorspace); pdata->avinframe->color_trc = mlt_to_av_color_trc(trc); pdata->avinframe->color_range = mlt_to_av_color_range(full_range); if (is_rgb(*format)) { pdata->avinframe->colorspace = AVCOL_SPC_RGB; } else { const char *colorspace_str = mlt_properties_get(frame_properties, "colorspace"); mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); pdata->avinframe->colorspace = mlt_to_av_colorspace(colorspace, pdata->avinframe->height); } ret = av_frame_get_buffer(pdata->avinframe, 1); if (ret < 0) { mlt_log_error(filter, "Cannot get in frame buffer\n"); } // Set up the input frame if (*format == mlt_image_yuv420p || *format == mlt_image_yuv420p10 || *format == mlt_image_yuv444p10 || *format == mlt_image_yuv422p16) { int i = 0; int p = 0; int strides[4]; uint8_t *planes[4]; int heights[3] = {*height, *height, *height}; if (*format == mlt_image_yuv420p || *format == mlt_image_yuv420p10) { heights[1] >>= 1; heights[2] >>= 1; } mlt_image_format_planes(*format, *width, *height, *image, planes, strides); for (p = 0; p < 3; p++) { uint8_t *const dst = pdata->avinframe->data[p]; for (i = 0; i < heights[p]; i++) { memcpy(&dst[i * pdata->avinframe->linesize[p]], &planes[p][i * strides[p]], strides[p]); } } } else { int i; uint8_t *src = *image; uint8_t *dst = pdata->avinframe->data[0]; int stride = mlt_image_format_size(*format, *width, 1, NULL); for (i = 0; i < *height; i++) { memcpy(dst, src, stride); src += stride; dst += pdata->avinframe->linesize[0]; } } send_avformat_commands(filter, frame, pdata, scale); // Run the frame through the filter graph ret = av_buffersrc_add_frame(pdata->avbuffsrc_ctx, pdata->avinframe); if (ret < 0) { mlt_log_error(filter, "Cannot add frame to buffer source\n"); } ret = av_buffersink_get_frame(pdata->avbuffsink_ctx, pdata->avoutframe); if (ret < 0) { mlt_log_error(filter, "Cannot get frame from buffer sink\n"); } // Sanity check the output frame if (*width != pdata->avoutframe->width || *height != pdata->avoutframe->height) { mlt_log_error(filter, "Unexpected return format\n"); goto exit; } // Copy the filter output into the original buffer if (*format == mlt_image_yuv420p || *format == mlt_image_yuv420p10 || *format == mlt_image_yuv444p10 || *format == mlt_image_yuv422p16) { int i = 0; int p = 0; int strides[4]; uint8_t *planes[4]; int heights[3] = {*height, *height, *height}; if (*format == mlt_image_yuv420p || *format == mlt_image_yuv420p10) { heights[1] >>= 1; heights[2] >>= 1; } mlt_image_format_planes(*format, *width, *height, *image, planes, strides); for (p = 0; p < 3; p++) { uint8_t *src = pdata->avoutframe->data[p]; for (i = 0; i < heights[p]; i++) { memcpy(&planes[p][i * strides[p]], &src[i * pdata->avoutframe->linesize[p]], strides[p]); } } } else { int i; uint8_t *dst = *image; uint8_t *src = pdata->avoutframe->data[0]; int stride = mlt_image_format_size(*format, *width, 1, NULL); for (i = 0; i < *height; i++) { memcpy(dst, src, stride); dst += stride; src += pdata->avoutframe->linesize[0]; } } mlt_properties_set_int(frame_properties, "color_trc", av_to_mlt_color_trc(pdata->avoutframe->color_trc)); mlt_properties_set_int(frame_properties, "colorspace", av_to_mlt_colorspace(pdata->avoutframe->colorspace, pdata->avoutframe->width, pdata->avoutframe->height)); mlt_properties_set_int(frame_properties, "color_primaries", av_to_mlt_color_primaries(pdata->avoutframe->color_primaries)); mlt_properties_set_int(frame_properties, "full_range", av_to_mlt_full_range(pdata->avoutframe->color_range)); } exit: av_frame_unref(pdata->avinframe); av_frame_unref(pdata->avoutframe); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { private_data *pdata = (private_data *) filter->child; if (avfilter_pad_get_type(pdata->avfilter->inputs, 0) == AVMEDIA_TYPE_VIDEO) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); } else if (avfilter_pad_get_type(pdata->avfilter->inputs, 0) == AVMEDIA_TYPE_AUDIO) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); } return frame; } /** Destructor for the filter. */ static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { avfilter_graph_free(&pdata->avfilter_graph); av_frame_free(&pdata->avinframe); av_frame_free(&pdata->avoutframe); #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_uninit(&pdata->ch_layout); #endif free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ mlt_filter filter_avfilter_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (pdata && id) { id += 9; // Move past "avfilter." pdata->avfilter = (AVFilter *) avfilter_get_by_name(id); } if (filter && pdata && pdata->avfilter) { pdata->avbuffsink_ctx = NULL; pdata->avbuffsrc_ctx = NULL; pdata->avfilter_ctx = NULL; pdata->avfilter_graph = NULL; pdata->avinframe = av_frame_alloc(); pdata->avoutframe = av_frame_alloc(); pdata->format = -1; pdata->width = -1; pdata->height = -1; pdata->reset = 1; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; mlt_events_listen(MLT_FILTER_PROPERTIES(filter), filter, "property-changed", (mlt_listener) property_changed); mlt_properties param_name_map = mlt_properties_get_data(mlt_global_properties(), "avfilter.resolution_scale", NULL); if (param_name_map) { // Lookup my plugin in the map param_name_map = mlt_properties_get_data(param_name_map, id, NULL); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "_resolution_scale", param_name_map, 0, NULL, NULL); } mlt_properties yuv_only = mlt_properties_get_data(mlt_global_properties(), "avfilter.yuv_only", NULL); if (yuv_only) { if (mlt_properties_get(yuv_only, id)) { mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "_yuv_only", 1); } } } else { mlt_filter_close(filter); free(pdata); } return filter; } mlt-7.38.0/src/modules/avformat/filter_swresample.c000664 000000 000000 00000016463 15172202314 022345 0ustar00rootroot000000 000000 /* * filter_swresample.c -- convert from one format/ configuration to another * Copyright (C) 2018-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "common_swr.h" #include #include #include #include #include static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int requested_samples = *samples; mlt_filter filter = mlt_frame_pop_audio(frame); mlt_swr_private_data *pdata = (mlt_swr_private_data *) filter->child; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); struct mlt_audio_s in; struct mlt_audio_s out; mlt_audio_set_values(&in, *buffer, *frequency, *format, *samples, *channels); mlt_audio_set_values(&out, NULL, *frequency, *format, *samples, *channels); // Get the producer's audio int error = mlt_frame_get_audio(frame, &in.data, &in.format, &in.frequency, &in.channels, &in.samples); if (error || in.format == mlt_audio_none || out.format == mlt_audio_none || in.frequency <= 0 || out.frequency <= 0 || in.channels <= 0 || out.channels <= 0) { // Error situation. Do not attempt to convert. mlt_audio_get_values(&in, buffer, frequency, format, samples, channels); mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid Parameters: %dS - %dHz %dC %s -> %dHz %dC %s\n", in.samples, in.frequency, in.channels, mlt_audio_format_name(in.format), out.frequency, out.channels, mlt_audio_format_name(out.format)); return error; } if (in.samples == 0) { // Nothing to convert. *samples = 0; return error; } // Determine the input/output channel layout. in.layout = mlt_get_channel_layout_or_default(mlt_properties_get(frame_properties, "channel_layout"), in.channels); out.layout = mlt_get_channel_layout_or_default(mlt_properties_get(frame_properties, "consumer.channel_layout"), out.channels); if (in.format == out.format && in.frequency == out.frequency && in.channels == out.channels && in.layout == out.layout) { // No change necessary mlt_audio_get_values(&in, buffer, frequency, format, samples, channels); return error; } mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Detect configuration change if (!pdata->ctx || pdata->in_format != in.format || pdata->out_format != out.format || pdata->in_frequency != in.frequency || pdata->out_frequency != out.frequency || pdata->in_channels != in.channels || pdata->out_channels != out.channels || pdata->in_layout != in.layout || pdata->out_layout != out.layout) { // Save the configuration pdata->in_format = in.format; pdata->out_format = out.format; pdata->in_frequency = in.frequency; pdata->out_frequency = out.frequency; pdata->in_channels = in.channels; pdata->out_channels = out.channels; pdata->in_layout = in.layout; pdata->out_layout = out.layout; // Reconfigure the context error = mlt_configure_swr_context(MLT_FILTER_SERVICE(filter), pdata); } if (!error) { out.samples = requested_samples; mlt_audio_alloc_data(&out); mlt_audio_get_planes(&in, pdata->in_buffers); mlt_audio_get_planes(&out, pdata->out_buffers); int received_samples = swr_convert(pdata->ctx, pdata->out_buffers, out.samples, (const uint8_t **) pdata->in_buffers, in.samples); if (received_samples >= 0) { if (received_samples == 0) { mlt_log_info(MLT_FILTER_SERVICE(filter), "Precharge required - return silence\n"); mlt_audio_silence(&out, out.samples, 0); } else if (received_samples < requested_samples) { // Duplicate samples to return the exact number requested. mlt_audio_copy(&out, &out, received_samples, 0, requested_samples - received_samples); } else if (received_samples > requested_samples) { // Discard samples to return the exact number requested. mlt_audio_shrink(&out, requested_samples); } mlt_frame_set_audio(frame, out.data, out.format, 0, out.release_data); mlt_audio_get_values(&out, buffer, frequency, format, samples, channels); mlt_properties_set(frame_properties, "channel_layout", mlt_audio_channel_layout_name(out.layout)); } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "swr_convert() failed. Alloc: %d\tIn: %d\tOut: %d\n", out.samples, in.samples, received_samples); out.release_data(out.data); error = 1; } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } static void filter_close(mlt_filter filter) { mlt_swr_private_data *pdata = (mlt_swr_private_data *) filter->child; if (pdata) { mlt_free_swr_context(pdata); free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } mlt_filter filter_swresample_init(mlt_profile profile, char *arg) { mlt_filter filter = mlt_filter_new(); mlt_swr_private_data *pdata = (mlt_swr_private_data *) calloc(1, sizeof(mlt_swr_private_data)); if (filter && pdata) { filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_filter_close(filter); free(pdata); } return filter; } mlt-7.38.0/src/modules/avformat/filter_swresample.yml000664 000000 000000 00000000702 15172202314 022711 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: swresample title: FFmpeg Audio Resampler version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Audio - Hidden description: Converts the audio sample rate. notes: > This is not intended to be created directly. Rather, the loader producer loads it if it is available to set the audio resampler to normalize inputs to the consumer audio frequency. mlt-7.38.0/src/modules/avformat/filter_sws_colortransform.c000664 000000 000000 00000022637 15172202314 024131 0ustar00rootroot000000 000000 /* * filter_sws_colortransform.c -- linear color transfer filter using libswscale * Copyright (C) 2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #if LIBSWSCALE_VERSION_MAJOR >= 9 #include "common.h" #include #include #include #include #include #include #include #include #include #include #include static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); // Get the image first int error = mlt_frame_get_image(frame, image, format, width, height, 0); if (error) return error; // Only process if we have a valid image if (!*image || *format == mlt_image_none || *format == mlt_image_movit || *format == mlt_image_opengl_texture) return 0; // Get the current color transfer characteristics const char *frame_trc_str = mlt_properties_get(properties, "color_trc"); if (!frame_trc_str) { mlt_log_info(MLT_FILTER_SERVICE(filter), "No color_trc property found\n"); return 0; } mlt_color_trc frame_trc = mlt_image_color_trc_id(frame_trc_str); // Get the target TRC from the transfer property const char *transfer_str = mlt_properties_get(filter_properties, "transfer"); if (!transfer_str) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "No transfer property set\n"); return 0; } mlt_color_trc target_trc = mlt_image_color_trc_id(transfer_str); // Check if conversion is needed if (target_trc == frame_trc) { // Already at target TRC mlt_log_verbose(MLT_FILTER_SERVICE(filter), "No TRC conversion needed: %s\n", mlt_image_color_trc_name(frame_trc)); return 0; } if (target_trc == mlt_color_trc_none || target_trc == mlt_color_trc_invalid || target_trc == mlt_color_trc_unspecified || target_trc == mlt_color_trc_reserved) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid transfer: %s\n", transfer_str); return 1; } // Get colorspace information const char *colorspace_str = mlt_properties_get(properties, "colorspace"); if (!colorspace_str) { colorspace_str = mlt_properties_get(properties, "consumer.colorspace"); } mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); const char *primaries_str = mlt_properties_get(properties, "color_primaries"); mlt_color_primaries primaries = mlt_image_color_pri_id(primaries_str); if (primaries == mlt_color_pri_none || primaries == mlt_color_pri_invalid) primaries = mlt_image_default_primaries(colorspace, *height); int full_range = mlt_properties_get_int(properties, "full_range"); mlt_log_debug(MLT_FILTER_SERVICE(filter), "Converting TRC: %s (%d) -> %s (%d) colorspace %s full_range %d format %s\n", mlt_image_color_trc_name(frame_trc), mlt_to_av_color_trc(frame_trc), mlt_image_color_trc_name(target_trc), mlt_to_av_color_trc(target_trc), mlt_image_colorspace_name(colorspace), full_range, mlt_image_format_name(*format)); // Convert the pixel format int avformat = mlt_to_av_image_format(*format); if (avformat == AV_PIX_FMT_NONE) return 1; AVFrame *avinframe = av_frame_alloc(); AVFrame *avoutframe = av_frame_alloc(); int result = 0; if (!avinframe || !avoutframe) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to allocate AVFrames\n"); result = 1; goto cleanup; } // Configure swscale context SwsContext *context = mlt_properties_get_data(filter_properties, "context", NULL); if (!context) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to get SwsScale context\n"); result = 1; goto cleanup; } context->threads = 0; // Setup the input frame avinframe->width = *width; avinframe->height = *height; avinframe->format = avformat; avinframe->color_trc = mlt_to_av_color_trc(frame_trc); avinframe->colorspace = mlt_to_av_colorspace(colorspace, *height); avinframe->color_primaries = mlt_to_av_color_primaries(primaries); avinframe->color_range = mlt_to_av_color_range(full_range); av_image_fill_arrays(avinframe->data, avinframe->linesize, *image, avinframe->format, *width, *height, 1); // Setup the output frame av_frame_copy_props(avoutframe, avinframe); avoutframe->width = *width; avoutframe->height = *height; avoutframe->format = avformat; avoutframe->color_trc = mlt_to_av_color_trc(target_trc); if (sws_is_noop(avoutframe, avinframe)) { mlt_log_debug(MLT_FILTER_SERVICE(filter), "swscale noop\n"); goto cleanup; } result = av_frame_get_buffer(avoutframe, 0); if (result < 0) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Cannot allocate output frame buffer\n"); result = 1; goto cleanup; } // Perform the conversion result = sws_scale_frame(context, avoutframe, avinframe); if (result < 0) { mlt_log_error(MLT_FILTER_SERVICE(filter), "sws_scale_frame failed with %d (%s)\n", result, av_err2str(result)); result = 1; goto cleanup; } assert(avoutframe->data[0] != avinframe->data[0]); // Copy the output to the buffer if (*format == mlt_image_yuv420p || *format == mlt_image_yuv420p10 || *format == mlt_image_yuv444p10 || *format == mlt_image_yuv422p16) { int i = 0; int p = 0; int strides[4]; uint8_t *planes[4]; int heights[3] = {*height, *height, *height}; if (*format == mlt_image_yuv420p || *format == mlt_image_yuv420p10) { heights[1] >>= 1; heights[2] >>= 1; } mlt_image_format_planes(*format, *width, *height, *image, planes, strides); for (p = 0; p < 3; p++) { uint8_t *src = avoutframe->data[p]; for (i = 0; i < heights[p]; i++) { memcpy(&planes[p][i * strides[p]], &src[i * avoutframe->linesize[p]], strides[p]); } } } else { int i; uint8_t *dst = *image; uint8_t *src = avoutframe->data[0]; int stride = mlt_image_format_size(*format, *width, 1, NULL); for (i = 0; i < *height; i++) { memcpy(dst, src, stride); dst += stride; src += avoutframe->linesize[0]; } } mlt_properties_set_int(properties, "color_trc", target_trc); result = 0; cleanup: av_frame_free(&avinframe); av_frame_free(&avoutframe); return result; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void release_context(SwsContext *context) { sws_free_context(&context); } mlt_filter filter_sws_colortransform_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); // Create the swscale context SwsContext *context = sws_alloc_context(); if (!context) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to allocate swscale context\n"); mlt_filter_close(filter); return NULL; } mlt_properties_set_data(properties, "context", context, sizeof(*context), (mlt_destructor) release_context, NULL); // Set default transfer (linear) mlt_properties_set(properties, "transfer", arg ? arg : "linear"); } return filter; } #endif mlt-7.38.0/src/modules/avformat/filter_sws_colortransform.yml000664 000000 000000 00000001361 15172202314 024477 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: sws_colortransform title: libswscale Color Transform version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Video description: > Convert image to and from linear color transfer characteristics using libswscale. This filter is designed to work with the color_transform filter to provide efficient linear color space conversion. parameters: - identifier: transfer title: Target Transfer Characteristic type: string description: > The target color transfer characteristic (TRC) to convert to. Can be any valid MLT color TRC name such as: linear, bt709, iec61966-2-1 (sRGB), bt2020_10, smpte170m, etc. default: linear mlt-7.38.0/src/modules/avformat/filter_swscale.c000664 000000 000000 00000035354 15172202314 021624 0ustar00rootroot000000 000000 /* * filter_swscale.c -- image scaling filter * Copyright (C) 2008-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include // ffmpeg Header files #include #include #include #include #include #include #include #define MAX_THREADS (6) static inline int convert_mlt_to_av_cs(mlt_image_format format) { int value = 0; switch (format) { case mlt_image_rgb: value = AV_PIX_FMT_RGB24; break; case mlt_image_rgba: value = AV_PIX_FMT_RGBA; break; case mlt_image_yuv422: value = AV_PIX_FMT_YUYV422; break; case mlt_image_yuv420p: value = AV_PIX_FMT_YUV420P; break; case mlt_image_yuv420p10: value = AV_PIX_FMT_YUV420P10LE; break; case mlt_image_yuv444p10: value = AV_PIX_FMT_YUV444P10LE; break; case mlt_image_yuv422p16: value = AV_PIX_FMT_YUV422P16LE; break; case mlt_image_rgba64: value = AV_PIX_FMT_RGBA64LE; break; default: mlt_log_error(NULL, "[filter swscale] Invalid format %s\n", mlt_image_format_name(format)); break; } return value; } static int filter_scale(mlt_frame frame, uint8_t **image, mlt_image_format *format, int iwidth, int iheight, int owidth, int oheight) { int result = 0; // Get the properties mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Get the requested interpolation method char *interps = mlt_properties_get(properties, "consumer.rescale"); // Convert to the SwScale flag int interp = SWS_BILINEAR; if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0) interp = SWS_POINT; else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0) interp = SWS_FAST_BILINEAR; else if (strcmp(interps, "bilinear") == 0) interp = SWS_BILINEAR; else if (strcmp(interps, "bicubic") == 0) interp = SWS_BICUBIC; else if (strcmp(interps, "bicublin") == 0) interp = SWS_BICUBLIN; else if (strcmp(interps, "gauss") == 0) interp = SWS_GAUSS; else if (strcmp(interps, "sinc") == 0) interp = SWS_SINC; else if (strcmp(interps, "hyper") == 0 || strcmp(interps, "lanczos") == 0) interp = SWS_LANCZOS; else if (strcmp(interps, "spline") == 0) interp = SWS_SPLINE; // Set swscale flags to get good quality interp |= SWS_FULL_CHR_H_INP | SWS_FULL_CHR_H_INT | SWS_ACCURATE_RND; // Convert the pixel formats int avformat = convert_mlt_to_av_cs(*format); // Determine the output image size. int out_size = mlt_image_format_size(*format, owidth, oheight, NULL); uint8_t *outbuf = mlt_pool_alloc(out_size); // Create the context struct SwsContext *context = sws_alloc_context(); if (outbuf && context) { AVFrame *avinframe = av_frame_alloc(); AVFrame *avoutframe = av_frame_alloc(); av_opt_set_int(context, "srcw", iwidth, 0); av_opt_set_int(context, "srch", iheight, 0); av_opt_set_int(context, "src_format", avformat, 0); av_opt_set_int(context, "dstw", owidth, 0); av_opt_set_int(context, "dsth", oheight, 0); av_opt_set_int(context, "dst_format", avformat, 0); av_opt_set_int(context, "sws_flags", interp, 0); #if LIBSWSCALE_VERSION_MAJOR >= 6 av_opt_set_int(context, "threads", MIN(mlt_slices_count_normal(), MAX_THREADS), 0); #endif result = sws_init_context(context, NULL, NULL); if (result < 0) { mlt_log_error(NULL, "[filter swscale] Initializing swscale failed with %d (%s)\n", result, av_err2str(result)); result = 1; goto exit; } mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE(mlt_frame_get_original_producer(frame))); mlt_colorspace dst_colorspace = profile ? profile->colorspace : mlt_colorspace_bt601; char *src_colorspace_str = mlt_properties_get(properties, "colorspace"); mlt_colorspace src_colorspace = mlt_image_colorspace_id(src_colorspace_str); int src_full_range = mlt_properties_get_int(properties, "full_range"); const char *dst_color_range = mlt_properties_get(properties, "consumer.color_range"); int dst_full_range = mlt_image_full_range(dst_color_range); result = mlt_set_luma_transfer(context, src_colorspace, dst_colorspace, src_full_range, dst_full_range); if (result < 0) { mlt_log_error(NULL, "[filter swscale] Setting swscale color options failed with %d (%s)\n", result, av_err2str(result)); result = 1; goto exit; } // Setup the input image avinframe->width = iwidth; avinframe->height = iheight; avinframe->format = avformat; avinframe->sample_aspect_ratio = av_d2q(mlt_frame_get_aspect_ratio(frame), 1024); #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) if (!mlt_properties_get_int(properties, "progressive")) avinframe->flags |= AV_FRAME_FLAG_INTERLACED; if (mlt_properties_get_int(properties, "top_field_first")) avinframe->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; #else avinframe->interlaced_frame = !mlt_properties_get_int(properties, "progressive"); avinframe->top_field_first = mlt_properties_get_int(properties, "top_field_first"); #endif av_image_fill_arrays(avinframe->data, avinframe->linesize, *image, avinframe->format, iwidth, iheight, 1); // Setup the output image av_frame_copy_props(avoutframe, avinframe); avoutframe->width = owidth; avoutframe->height = oheight; avoutframe->format = avformat; result = av_frame_get_buffer(avoutframe, 0); if (result < 0) { mlt_log_error(NULL, "[filter swscale] Cannot allocate output frame buffer\n"); result = 1; goto exit; } // Perform the scaling #if LIBSWSCALE_VERSION_MAJOR >= 6 result = sws_scale_frame(context, avoutframe, avinframe); #else result = sws_scale(context, (const uint8_t **) avinframe->data, avinframe->linesize, 0, iheight, avoutframe->data, avoutframe->linesize); #endif if (result < 0) { mlt_log_error(NULL, "[filter swscale] sws_scale_frame failed with %d (%s)\n", result, av_err2str(result)); result = 1; goto exit; } sws_freeContext(context); context = NULL; // Sanity check the output frame if (owidth != avoutframe->width || oheight != avoutframe->height) { mlt_log_error(NULL, "[filter swscale] Unexpected output size\n"); result = 1; goto exit; } // Copy the filter output into the output buffer if (*format == mlt_image_yuv420p || *format == mlt_image_yuv420p10 || *format == mlt_image_yuv444p10 || *format == mlt_image_yuv422p16) { int i = 0; int p = 0; int strides[4]; uint8_t *planes[4]; int heights[3] = {oheight, oheight, oheight}; if (*format == mlt_image_yuv420p || *format == mlt_image_yuv420p10) { heights[1] >>= 1; heights[2] >>= 1; } mlt_image_format_planes(*format, owidth, oheight, outbuf, planes, strides); for (p = 0; p < 3; p++) { uint8_t *src = avoutframe->data[p]; for (i = 0; i < heights[p]; i++) { memcpy(&planes[p][i * strides[p]], &src[i * avoutframe->linesize[p]], strides[p]); } } } else { int i; uint8_t *dst = outbuf; uint8_t *src = avoutframe->data[0]; int stride = mlt_image_format_size(*format, owidth, 1, NULL); for (i = 0; i < oheight; i++) { memcpy(dst, src, stride); dst += stride; src += avoutframe->linesize[0]; } } // Now update the MLT frame mlt_frame_set_image(frame, outbuf, out_size, mlt_pool_release); mlt_properties_set_int(properties, "full_range", dst_full_range); // Return the output image *image = outbuf; // Scale the alpha channel only if exists and not correct size int alpha_size = 0; uint8_t *alpha = mlt_frame_get_alpha_size(frame, &alpha_size); if (alpha && alpha_size > 0 && alpha_size != (owidth * oheight)) { // Create the context and output image outbuf = mlt_pool_alloc(owidth * oheight); context = sws_alloc_context(); if (outbuf && context) { av_frame_unref(avinframe); av_frame_unref(avoutframe); avformat = AV_PIX_FMT_GRAY8; av_opt_set_int(context, "srcw", iwidth, 0); av_opt_set_int(context, "srch", iheight, 0); av_opt_set_int(context, "src_format", avformat, 0); av_opt_set_int(context, "dstw", owidth, 0); av_opt_set_int(context, "dsth", oheight, 0); av_opt_set_int(context, "dst_format", avformat, 0); av_opt_set_int(context, "sws_flags", interp, 0); #if LIBSWSCALE_VERSION_MAJOR >= 6 av_opt_set_int(context, "threads", MIN(mlt_slices_count_normal(), MAX_THREADS), 0); #endif result = sws_init_context(context, NULL, NULL); if (result < 0) { mlt_log_error( NULL, "[filter swscale] Initializing swscale alpha failed with %d (%s)\n", result, av_err2str(result)); result = 1; goto exit; } // Setup the input image avinframe->width = iwidth; avinframe->height = iheight; avinframe->format = avformat; avinframe->data[0] = alpha; avinframe->linesize[0] = iwidth; // Setup the output image avoutframe->width = owidth; avoutframe->height = oheight; avoutframe->format = avformat; result = av_frame_get_buffer(avoutframe, 0); if (result < 0) { mlt_log_error(NULL, "[filter swscale] Cannot allocate alpha frame buffer\n"); result = 1; goto exit; } // Perform the scaling #if LIBSWSCALE_VERSION_MAJOR >= 6 result = sws_scale_frame(context, avoutframe, avinframe); #else result = sws_scale(context, (const uint8_t **) avinframe->data, avinframe->linesize, 0, iheight, avoutframe->data, avoutframe->linesize); #endif if (result < 0) { mlt_log_error( NULL, "[filter swscale] sws_scale_frame alpha failed with %d (%s) %d %d\n", result, av_err2str(result), avoutframe->width, avoutframe->height); result = 1; goto exit; } sws_freeContext(context); context = NULL; // Sanity check the output frame if (owidth != avoutframe->width || oheight != avoutframe->height) { mlt_log_error(NULL, "[filter swscale] Unexpected output alpha size\n"); result = 1; goto exit; } int i; uint8_t *dst = outbuf; uint8_t *src = avoutframe->data[0]; for (i = 0; i < oheight; i++) { memcpy(dst, src, owidth); dst += owidth; src += avoutframe->linesize[0]; } // Set it back on the frame mlt_frame_set_alpha(frame, outbuf, owidth * oheight, mlt_pool_release); } } exit: av_frame_free(&avinframe); av_frame_free(&avoutframe); sws_freeContext(context); } return result; } /** Constructor for the filter. */ mlt_filter filter_swscale_init(mlt_profile profile, void *arg) { // Create a new scaler mlt_filter filter = mlt_factory_filter(profile, "rescale", NULL); // If successful, then initialise it if (filter != NULL) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES(filter); // Set the inerpolation mlt_properties_set(properties, "interpolation", "bilinear"); // Set the method mlt_properties_set_data(properties, "method", filter_scale, 0, NULL, NULL); } return filter; } mlt-7.38.0/src/modules/avformat/filter_swscale.yml000664 000000 000000 00000000666 15172202314 022201 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: swscale title: FFmpeg Image Scaler version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Video - Hidden description: Change the resolution of an image. notes: > This is not intended to be created directly. Rather, the rescale filter loads it if it is available to normalize video and image inputs to the consumer/profile resolution. mlt-7.38.0/src/modules/avformat/link_avdeinterlace.c000664 000000 000000 00000061426 15172202314 022440 0ustar00rootroot000000 000000 /* * link_avdeinterlace.c * Copyright (C) 2023-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "common.h" #include #include #include #include #include #include #include #include // Private Types #define FUTURE_FRAMES 1 typedef struct { mlt_position expected_frame; mlt_position continuity_frame; mlt_deinterlacer method; int format; int width; int height; mlt_colorspace colorspace; int fullrange; int reset; } private_data; typedef struct { AVFilterContext *avbuffsink_ctx; AVFilterContext *avbuffsrc_ctx; AVFilterGraph *avfilter_graph; AVFrame *avinframe; AVFrame *avoutframe; } filter_data; static void destroy_filter_data(filter_data *fdata) { if (fdata) { avfilter_graph_free(&fdata->avfilter_graph); av_frame_free(&fdata->avinframe); av_frame_free(&fdata->avoutframe); free(fdata); } } static mlt_image_format validate_format(mlt_image_format format) { mlt_image_format ret_format = mlt_image_yuv422; switch (format) { case mlt_image_rgb: case mlt_image_rgba: case mlt_image_yuv422: case mlt_image_yuv420p: case mlt_image_yuv422p16: case mlt_image_yuv420p10: case mlt_image_yuv444p10: case mlt_image_rgba64: ret_format = format; break; case mlt_image_none: case mlt_image_movit: case mlt_image_opengl_texture: case mlt_image_invalid: ret_format = mlt_image_yuv422; break; } return ret_format; } static void init_image_filtergraph(mlt_link self, AVRational sar) { private_data *pdata = (private_data *) self->child; filter_data *fdata = (filter_data *) calloc(1, sizeof(filter_data)); mlt_profile profile = mlt_service_profile(MLT_LINK_SERVICE(self)); const AVFilter *buffersrc = avfilter_get_by_name("buffer"); const AVFilter *buffersink = avfilter_get_by_name("buffersink"); enum AVPixelFormat pixel_fmts[] = {-1, -1}; pixel_fmts[0] = mlt_to_av_image_format(pdata->format); AVRational timebase = (AVRational){profile->frame_rate_den, profile->frame_rate_num}; AVRational framerate = (AVRational){profile->frame_rate_num, profile->frame_rate_den}; int colorspace = mlt_to_av_colorspace(pdata->colorspace, pdata->height); int color_range = mlt_to_av_color_range(pdata->fullrange); AVFilterContext *prev_ctx = NULL; AVFilterContext *avfilter_ctx = NULL; int ret; fdata->avinframe = av_frame_alloc(); fdata->avoutframe = av_frame_alloc(); // Create the new filter graph fdata->avfilter_graph = avfilter_graph_alloc(); if (!fdata->avfilter_graph) { mlt_log_error(self, "Cannot create filter graph\n"); goto fail; } fdata->avfilter_graph->scale_sws_opts = av_strdup("flags=" MLT_AVFILTER_SWS_FLAGS); // Initialize the buffer source filter context fdata->avbuffsrc_ctx = avfilter_graph_alloc_filter(fdata->avfilter_graph, buffersrc, "in"); if (!fdata->avbuffsrc_ctx) { mlt_log_error(self, "Cannot create image buffer source\n"); goto fail; } ret = av_opt_set_int(fdata->avbuffsrc_ctx, "width", pdata->width, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src width %d\n", pdata->width); goto fail; } ret = av_opt_set_int(fdata->avbuffsrc_ctx, "height", pdata->height, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src height %d\n", pdata->height); goto fail; } ret = av_opt_set_pixel_fmt(fdata->avbuffsrc_ctx, "pix_fmt", pixel_fmts[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src pixel format %d\n", pixel_fmts[0]); goto fail; } ret = av_opt_set_q(fdata->avbuffsrc_ctx, "sar", sar, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src sar %d/%d\n", sar.num, sar.den); goto fail; } ret = av_opt_set_q(fdata->avbuffsrc_ctx, "time_base", timebase, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src time_base %d/%d\n", timebase.num, timebase.den); goto fail; } ret = av_opt_set_q(fdata->avbuffsrc_ctx, "frame_rate", framerate, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src frame_rate %d/%d\n", framerate.num, framerate.den); goto fail; } ret = av_opt_set_int(fdata->avbuffsrc_ctx, "colorspace", colorspace, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src colorspace %d\n", colorspace); goto fail; } ret = av_opt_set_int(fdata->avbuffsrc_ctx, "range", color_range, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src range %d\n", color_range); goto fail; } ret = avfilter_init_str(fdata->avbuffsrc_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init buffer source\n"); goto fail; } prev_ctx = fdata->avbuffsrc_ctx; // Initialize the deinterlace filter context if (pdata->method <= mlt_deinterlacer_onefield) { const AVFilter *deint = (AVFilter *) avfilter_get_by_name("field"); avfilter_ctx = avfilter_graph_alloc_filter(fdata->avfilter_graph, deint, deint->name); if (!avfilter_ctx) { mlt_log_error(self, "Cannot create field filter\n"); goto fail; } ret = avfilter_init_str(avfilter_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init filter: %s\n", av_err2str(ret)); goto fail; } ret = avfilter_link(prev_ctx, 0, avfilter_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link field filter\n"); goto fail; } prev_ctx = avfilter_ctx; const AVFilter *scale = (AVFilter *) avfilter_get_by_name("scale"); avfilter_ctx = avfilter_graph_alloc_filter(fdata->avfilter_graph, scale, scale->name); if (!avfilter_ctx) { mlt_log_error(self, "Cannot create scale filter\n"); goto fail; } ret = avfilter_init_str(avfilter_ctx, "w=iw:h=2*ih"); if (ret < 0) { mlt_log_error(self, "Cannot init scale filter: %s\n", av_err2str(ret)); goto fail; } ret = avfilter_link(prev_ctx, 0, avfilter_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link scale filter\n"); goto fail; } prev_ctx = avfilter_ctx; } else if (pdata->method <= mlt_deinterlacer_yadif_nospatial) { const AVFilter *deint = (AVFilter *) avfilter_get_by_name("yadif"); avfilter_ctx = avfilter_graph_alloc_filter(fdata->avfilter_graph, deint, deint->name); if (!avfilter_ctx) { mlt_log_error(self, "Cannot create yadif filter\n"); goto fail; } ret = avfilter_init_str(avfilter_ctx, "mode=send_frame_nospatial:parity=auto:deint=all"); if (ret < 0) { mlt_log_error(self, "Cannot init yadif filter: %s\n", av_err2str(ret)); goto fail; } ret = avfilter_link(prev_ctx, 0, avfilter_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link yadif filter\n"); goto fail; } prev_ctx = avfilter_ctx; } else if (pdata->method <= mlt_deinterlacer_yadif) { const AVFilter *deint = (AVFilter *) avfilter_get_by_name("yadif"); avfilter_ctx = avfilter_graph_alloc_filter(fdata->avfilter_graph, deint, deint->name); if (!avfilter_ctx) { mlt_log_error(self, "Cannot create yadif filter\n"); goto fail; } ret = avfilter_init_str(avfilter_ctx, "mode=send_frame:parity=auto:deint=all"); if (ret < 0) { mlt_log_error(self, "Cannot init yadif filter: %s\n", av_err2str(ret)); goto fail; } ret = avfilter_link(prev_ctx, 0, avfilter_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link yadif filter\n"); goto fail; } prev_ctx = avfilter_ctx; } else if (pdata->method <= mlt_deinterlacer_bwdif) { const AVFilter *deint = (AVFilter *) avfilter_get_by_name("bwdif"); avfilter_ctx = avfilter_graph_alloc_filter(fdata->avfilter_graph, deint, deint->name); if (!avfilter_ctx) { mlt_log_error(self, "Cannot create bwdif filter\n"); goto fail; } ret = avfilter_init_str(avfilter_ctx, "mode=send_frame:parity=auto:deint=all"); if (ret < 0) { mlt_log_error(self, "Cannot init bwdif filter: %s\n", av_err2str(ret)); goto fail; } ret = avfilter_link(prev_ctx, 0, avfilter_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link bwdif filter\n"); goto fail; } prev_ctx = avfilter_ctx; } else { const AVFilter *deint = (AVFilter *) avfilter_get_by_name("estdif"); avfilter_ctx = avfilter_graph_alloc_filter(fdata->avfilter_graph, deint, deint->name); if (!avfilter_ctx) { mlt_log_error(self, "Cannot create estdif filter\n"); goto fail; } ret = avfilter_init_str(avfilter_ctx, "mode=frame:parity=auto:deint=all"); if (ret < 0) { mlt_log_error(self, "Cannot init estdif filter: %s\n", av_err2str(ret)); goto fail; } ret = avfilter_link(prev_ctx, 0, avfilter_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link estdif filter\n"); goto fail; } prev_ctx = avfilter_ctx; } // Initialize the buffer sink filter context fdata->avbuffsink_ctx = avfilter_graph_alloc_filter(fdata->avfilter_graph, buffersink, "out"); if (!fdata->avbuffsink_ctx) { mlt_log_error(self, "Cannot create image buffer sink\n"); goto fail; } #if LIBAVFILTER_VERSION_INT >= ((10 << 16) + (6 << 8) + 100) ret = av_opt_set_array(fdata->avbuffsink_ctx, "pixel_formats", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_PIXEL_FMT, pixel_fmts); #else ret = av_opt_set_int_list(fdata->avbuffsink_ctx, "pix_fmts", pixel_fmts, -1, AV_OPT_SEARCH_CHILDREN); #endif if (ret < 0) { mlt_log_error(self, "Cannot set sink pixel formats %d\n", pixel_fmts[0]); goto fail; } ret = avfilter_init_str(fdata->avbuffsink_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init buffer sink\n"); goto fail; } ret = avfilter_link(prev_ctx, 0, fdata->avbuffsink_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link filter to sink\n"); goto fail; } // Configure the graph. ret = avfilter_graph_config(fdata->avfilter_graph, NULL); if (ret < 0) { mlt_log_error(self, "Cannot configure the filter graph\n"); goto fail; } mlt_service_cache_put(MLT_LINK_SERVICE(self), "link_avdeinterlace", fdata, 0, (mlt_destructor) destroy_filter_data); return; fail: destroy_filter_data(fdata); } static void link_configure(mlt_link self, mlt_profile chain_profile) { // Operate at the same frame rate as the next link mlt_service_set_profile(MLT_LINK_SERVICE(self), mlt_service_profile(MLT_PRODUCER_SERVICE(self->next))); } static int link_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_link self = (mlt_link) mlt_frame_pop_service(frame); private_data *pdata = (private_data *) self->child; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_deinterlacer method = mlt_deinterlacer_id( mlt_properties_get(frame_properties, "consumer.deinterlacer")); if (!mlt_properties_get_int(frame_properties, "consumer.progressive") || method == mlt_deinterlacer_none || mlt_frame_is_test_card(frame)) { mlt_log_debug(MLT_LINK_SERVICE(self), "Do not deinterlace - progressive=%d\tmethod=%s\ttest card=%d\n", mlt_properties_get_int(frame_properties, "consumer.progressive"), mlt_deinterlacer_name(pdata->method), mlt_frame_is_test_card(frame)); return mlt_frame_get_image(frame, image, format, width, height, writable); } // At this point, we know we need to deinterlace. mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_LINK_SERVICE(self)); struct mlt_image_s srcimg = {0}; struct mlt_image_s dstimg = {0}; int error = 0; int ret = 0; // Operate on the native image format/size; srcimg.width = mlt_properties_get_int(unique_properties, "width"); srcimg.height = mlt_properties_get_int(unique_properties, "height"); if (mlt_properties_exists(unique_properties, "format")) { srcimg.format = mlt_properties_get_int(unique_properties, "format"); } else { srcimg.format = *format; } // Sanitize the input if (srcimg.width <= 1 || srcimg.height <= 1) { mlt_profile profile = mlt_service_profile(MLT_LINK_SERVICE(self)); srcimg.width = profile->width; srcimg.height = profile->height; } srcimg.format = validate_format(srcimg.format); dstimg.format = srcimg.format; mlt_service_lock(MLT_LINK_SERVICE(self)); if (pdata->method != method || pdata->expected_frame != mlt_frame_get_position(frame)) { mlt_log_debug(MLT_LINK_SERVICE(self), "Reset: %s\n", mlt_deinterlacer_name(method)); pdata->reset = 1; pdata->continuity_frame = mlt_frame_get_position(frame); pdata->expected_frame = mlt_frame_get_position(frame); pdata->method = method; } filter_data *fdata = NULL; mlt_cache_item cache_item = NULL; pdata->expected_frame++; while (1) { mlt_frame src_frame = NULL; if (pdata->continuity_frame == mlt_frame_get_position(frame)) { src_frame = frame; pdata->continuity_frame++; } else { if (!unique_properties) { error = 1; break; } char key[19]; int frame_delta = mlt_frame_get_position(frame) - mlt_frame_original_position(frame); sprintf(key, "%d", pdata->continuity_frame - frame_delta); src_frame = (mlt_frame) mlt_properties_get_data(unique_properties, key, NULL); if (!src_frame) { mlt_log_error(MLT_LINK_SERVICE(self), "Frame not found: %s\n", key); error = 1; break; } pdata->continuity_frame++; } error = mlt_frame_get_image(src_frame, (uint8_t **) &srcimg.data, &srcimg.format, &srcimg.width, &srcimg.height, 0); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Failed to get image\n"); error = 1; break; } if (!fdata) { // Wait to initialize the filter data until after we have a frame so we know the colorspace, etc. const char *colorspace_str = mlt_properties_get(MLT_FRAME_PROPERTIES(src_frame), "colorspace"); mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); int fullrange = mlt_properties_get_int(MLT_FRAME_PROPERTIES(src_frame), "full_range"); if (pdata->reset || pdata->format != srcimg.format || pdata->width != srcimg.width || pdata->height != srcimg.height || pdata->colorspace != colorspace || pdata->fullrange != fullrange) { mlt_log_debug(MLT_LINK_SERVICE(self), "Init: %s\n", mlt_deinterlacer_name(method)); pdata->format = srcimg.format; pdata->width = srcimg.width; pdata->height = srcimg.height; pdata->colorspace = colorspace; pdata->fullrange = fullrange; init_image_filtergraph(self, av_d2q(mlt_frame_get_aspect_ratio(frame), 1024)); pdata->reset = 0; } cache_item = mlt_service_cache_get(MLT_LINK_SERVICE(self), "link_avdeinterlace"); if (!cache_item) { mlt_log_debug(MLT_LINK_SERVICE(self), "Cache miss\n"); init_image_filtergraph(self, av_d2q(mlt_frame_get_aspect_ratio(frame), 1024)); cache_item = mlt_service_cache_get(MLT_LINK_SERVICE(self), "link_avdeinterlace"); } fdata = mlt_cache_item_data(cache_item, NULL); } if (!fdata || !fdata->avfilter_graph) { mlt_log_error(MLT_LINK_SERVICE(self), "No Filtergraph\n"); error = 1; break; } mlt_image_to_avframe(&srcimg, src_frame, fdata->avinframe); // Run the frame through the filter graph ret = av_buffersrc_add_frame(fdata->avbuffsrc_ctx, fdata->avinframe); av_frame_unref(fdata->avinframe); if (ret < 0) { mlt_log_error(self, "Cannot add frame to buffer source\n"); error = 1; break; } ret = av_buffersink_get_frame(fdata->avbuffsink_ctx, fdata->avoutframe); if (ret >= 0) break; else if (ret == AVERROR(EAGAIN)) continue; else if (ret < 0) { mlt_log_error(self, "Cannot get frame from buffer sink\n"); error = 1; break; } srcimg.data = NULL; } if (!error) { // Allocate the output image mlt_image_set_values(&dstimg, NULL, pdata->format, fdata->avoutframe->width, fdata->avoutframe->height); mlt_image_alloc_data(&dstimg); avframe_to_mlt_image(fdata->avoutframe, &dstimg); mlt_properties_set_int(frame_properties, "color_trc", av_to_mlt_color_trc(fdata->avoutframe->color_trc)); mlt_properties_set_int(frame_properties, "colorspace", av_to_mlt_colorspace(fdata->avoutframe->colorspace, fdata->avoutframe->width, fdata->avoutframe->height)); mlt_properties_set_int(frame_properties, "color_primaries", av_to_mlt_color_primaries(fdata->avoutframe->color_primaries)); mlt_properties_set_int(frame_properties, "full_range", av_to_mlt_full_range(fdata->avoutframe->color_range)); } av_frame_unref(fdata->avoutframe); mlt_service_unlock(MLT_LINK_SERVICE(self)); mlt_image_get_values(&dstimg, (void **) image, format, width, height); mlt_frame_set_image(frame, dstimg.data, 0, dstimg.release_data); mlt_properties_set_int(frame_properties, "progressive", 1); mlt_cache_item_close(cache_item); return error; } static int link_get_frame(mlt_link self, mlt_frame_ptr frame, int index) { int error = 0; mlt_position frame_pos = mlt_producer_position(MLT_LINK_PRODUCER(self)); mlt_producer_seek(self->next, frame_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), frame, index); mlt_producer original_producer = mlt_frame_get_original_producer(*frame); mlt_producer_probe(original_producer); if (mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(original_producer), "meta.media.progressive") || mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(original_producer), "progressive")) { return error; } // Pass original producer dimensions with the frame mlt_properties unique_properties = mlt_frame_unique_properties(*frame, MLT_LINK_SERVICE(self)); mlt_properties original_producer_properties = MLT_PRODUCER_PROPERTIES(original_producer); if (mlt_properties_exists(original_producer_properties, "width")) { mlt_properties_set_int(unique_properties, "width", mlt_properties_get_int(original_producer_properties, "width")); } else if (mlt_properties_exists(original_producer_properties, "meta.media.width")) { mlt_properties_set_int(unique_properties, "width", mlt_properties_get_int(original_producer_properties, "meta.media.width")); } if (mlt_properties_exists(original_producer_properties, "height")) { mlt_properties_set_int(unique_properties, "height", mlt_properties_get_int(original_producer_properties, "height")); } else if (mlt_properties_exists(original_producer_properties, "meta.media.height")) { mlt_properties_set_int(unique_properties, "height", mlt_properties_get_int(original_producer_properties, "meta.media.height")); } if (mlt_properties_exists(original_producer_properties, "format")) { mlt_properties_set_int(unique_properties, "format", mlt_properties_get_int(original_producer_properties, "format")); } // Pass future frames int i = 0; for (i = 0; i < FUTURE_FRAMES; i++) { mlt_position future_pos = frame_pos + i + 1; mlt_frame future_frame = NULL; mlt_producer_seek(self->next, future_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), &future_frame, index); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Error getting frame: %d\n", (int) future_pos); } char key[19]; sprintf(key, "%d", (int) future_pos); mlt_properties_set_data(unique_properties, key, future_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } mlt_frame_push_service(*frame, self); mlt_frame_push_get_image(*frame, link_get_image); mlt_producer_prepare_next(MLT_LINK_PRODUCER(self)); return error; } static void link_close(mlt_link self) { if (self) { mlt_service_cache_purge(MLT_LINK_SERVICE(self)); free(self->child); self->close = NULL; self->child = NULL; mlt_link_close(self); free(self); } } mlt_link link_avdeinterlace_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_link self = mlt_link_init(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (self && pdata) { pdata->continuity_frame = -1; pdata->expected_frame = -1; pdata->reset = 1; pdata->method = mlt_deinterlacer_linearblend; self->child = pdata; // Callback registration self->configure = link_configure; self->get_frame = link_get_frame; self->close = link_close; } else { free(pdata); mlt_link_close(self); self = NULL; } return self; } mlt-7.38.0/src/modules/avformat/link_avdeinterlace.yml000664 000000 000000 00000000614 15172202314 023007 0ustar00rootroot000000 000000 schema_version: 7.0 type: link identifier: avdeinterlace title: FFmpeg Deinterlacer version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Video - Hidden description: > Deinterlace interlaced video. This link can be added to a chain to normalize video from the producer to provide deinterlaced images if requested by the consumer. mlt-7.38.0/src/modules/avformat/link_avfilter.c000664 000000 000000 00000136671 15172202314 021453 0ustar00rootroot000000 000000 /* * link_avfilter.c -- provide various links based on libavfilter * Copyright (C) 2023-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 700 #undef _XOPEN_SOURCE #define _XOPEN_SOURCE 700 #endif #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #define PARAM_PREFIX "av." #define PARAM_PREFIX_LEN (sizeof(PARAM_PREFIX) - 1) typedef struct { AVFilter *avfilter; AVFilterContext *avbuffsink_ctx; AVFilterContext *avbuffsrc_ctx; AVFilterContext *avfilter_ctx; AVFilterContext *scale_ctx; AVFilterContext *pad_ctx; AVFilterGraph *avfilter_graph; AVFrame *avinframe; AVFrame *avoutframe; int format; int width; int height; mlt_colorspace colorspace; int full_range; int channels; int frequency; int reset; mlt_position expected_frame; mlt_position continuity_frame; #if HAVE_FFMPEG_CH_LAYOUT AVChannelLayout ch_layout; #endif } private_data; #if LIBAVUTIL_VERSION_INT >= ((56 << 16) + (35 << 8) + 101) static int animatable_avoption(const AVOption *opt) { return opt && (opt->flags & AV_OPT_FLAG_RUNTIME_PARAM) && opt->type != AV_OPT_TYPE_COLOR; } #endif static void property_changed(mlt_service owner, mlt_link self, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && strncmp(PARAM_PREFIX, name, PARAM_PREFIX_LEN) == 0) { private_data *pdata = (private_data *) self->child; if (pdata->avfilter_ctx) { mlt_service_lock(MLT_LINK_SERVICE(self)); const AVOption *opt = av_opt_find(pdata->avfilter_ctx->priv, name + PARAM_PREFIX_LEN, 0, 0, 0); #if LIBAVUTIL_VERSION_INT >= ((56 << 16) + (35 << 8) + 101) pdata->reset = opt && !(animatable_avoption(opt) && mlt_properties_is_anim(MLT_LINK_PROPERTIES(self), name)); #else pdata->reset = opt && !mlt_properties_is_anim(MLT_LINK_PROPERTIES(self), name); #endif mlt_service_unlock(MLT_LINK_SERVICE(self)); } } } static int future_frames_needed(mlt_link self) { if (!self || !self->child) { return 0; } private_data *pdata = (private_data *) self->child; int future_frames = 0; if (strcmp(pdata->avfilter->name, "adeclick") == 0) { int windowms = mlt_properties_get_int(MLT_LINK_PROPERTIES(self), "av.window"); if (windowms <= 0) { // Default value is 100 windowms = 100; } double fps = mlt_profile_fps(mlt_service_profile(MLT_LINK_SERVICE(self))); // Provide future frames for 1.5x window duration (determined empirically) future_frames = ceil(1.5 * fps * windowms / 1000); } return future_frames; } static void set_avfilter_options(mlt_link self, double scale) { private_data *pdata = (private_data *) self->child; mlt_properties link_properties = MLT_LINK_PROPERTIES(self); int i; int count = mlt_properties_count(link_properties); mlt_properties scale_map = mlt_properties_get_data(link_properties, "_resolution_scale", NULL); for (i = 0; i < count; i++) { const char *param_name = mlt_properties_get_name(link_properties, i); if (param_name && strncmp(PARAM_PREFIX, param_name, PARAM_PREFIX_LEN) == 0) { const AVOption *opt = av_opt_find(pdata->avfilter_ctx->priv, param_name + PARAM_PREFIX_LEN, 0, 0, 0); const char *value = mlt_properties_get_value(link_properties, i); #if LIBAVUTIL_VERSION_INT >= ((56 << 16) + (35 << 8) + 101) if (opt && !(animatable_avoption(opt) && mlt_properties_is_anim(link_properties, param_name))) #else if (opt && !mlt_properties_is_anim(link_properties, param_name)) #endif { if (scale != 1.0) { double scale2 = mlt_properties_get_double(scale_map, opt->name); if (scale2 != 0.0) { double x = mlt_properties_get_double(link_properties, param_name); x *= scale * scale2; mlt_properties_set_double(link_properties, "_avfilter_temp", x); value = mlt_properties_get(link_properties, "_avfilter_temp"); } } av_opt_set(pdata->avfilter_ctx->priv, opt->name, value, 0); } } } } static void send_avformat_commands(mlt_link self, mlt_frame frame, private_data *pdata, double scale) { #if LIBAVUTIL_VERSION_INT >= ((56 << 16) + (35 << 8) + 101) mlt_properties prop = MLT_LINK_PROPERTIES(self); mlt_position position = mlt_producer_position(MLT_LINK_PRODUCER(self)) - mlt_producer_get_in(MLT_LINK_PRODUCER(self)); mlt_position length = mlt_producer_get_length(MLT_LINK_PRODUCER(self)); mlt_properties scale_map = mlt_properties_get_data(prop, "_resolution_scale", NULL); int count = mlt_properties_count(prop); int i; for (i = 0; i < count; i++) { char *name = mlt_properties_get_name(prop, i); if (!strncmp(name, PARAM_PREFIX, PARAM_PREFIX_LEN)) { const AVOption *opt = av_opt_find(pdata->avfilter_ctx->priv, name + PARAM_PREFIX_LEN, 0, 0, 0); if (animatable_avoption(opt) && mlt_properties_is_anim(prop, name)) { double x = mlt_properties_anim_get_double(prop, name, position, length); if (scale != 1.0) { double scale2 = mlt_properties_get_double(scale_map, opt->name); if (scale2 != 0.0) { x *= scale * scale2; } } mlt_properties_set_double(prop, "_avfilter_temp", x); char *new_val = mlt_properties_get(prop, "_avfilter_temp"); char *cur_val = NULL; av_opt_get(pdata->avfilter_ctx->priv, name + PARAM_PREFIX_LEN, AV_OPT_SEARCH_CHILDREN, (uint8_t **) &cur_val); if (new_val && cur_val && strcmp(new_val, cur_val)) { avfilter_graph_send_command(pdata->avfilter_graph, pdata->avfilter->name, name + PARAM_PREFIX_LEN, new_val, NULL, 0, 0); } av_free(cur_val); } } } #endif } static void init_audio_filtergraph(mlt_link self, mlt_audio_format format, int frequency, int channels) { private_data *pdata = (private_data *) self->child; const AVFilter *abuffersrc = avfilter_get_by_name("abuffer"); const AVFilter *abuffersink = avfilter_get_by_name("abuffersink"); int sample_fmts[] = {-1, -1}; int sample_rates[] = {-1, -1}; int channel_counts[] = {-1, -1}; char channel_layout_str[64]; int ret; pdata->format = format; // Set up formats sample_fmts[0] = mlt_to_av_sample_format(format); sample_rates[0] = frequency; channel_counts[0] = channels; #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_default(&pdata->ch_layout, channels); av_channel_layout_describe(&pdata->ch_layout, channel_layout_str, sizeof(channel_layout_str)); #else int64_t channel_layouts[] = {-1, -1}; channel_layouts[0] = av_get_default_channel_layout(channels); av_get_channel_layout_string(channel_layout_str, sizeof(channel_layout_str), 0, channel_layouts[0]); #endif // Destroy the current filter graph avfilter_graph_free(&pdata->avfilter_graph); // Create the new filter graph pdata->avfilter_graph = avfilter_graph_alloc(); if (!pdata->avfilter_graph) { mlt_log_error(self, "Cannot create filter graph\n"); goto fail; } // Set thread count if supported. if (pdata->avfilter->flags & AVFILTER_FLAG_SLICE_THREADS) { av_opt_set_int(pdata->avfilter_graph, "threads", FFMAX(0, mlt_properties_get_int(MLT_LINK_PROPERTIES(self), "av.threads")), 0); } // Initialize the buffer source filter context pdata->avbuffsrc_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, abuffersrc, "in"); if (!pdata->avbuffsrc_ctx) { mlt_log_error(self, "Cannot create audio buffer source\n"); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "sample_rate", sample_rates[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src sample rate %d\n", sample_rates[0]); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "sample_fmt", sample_fmts[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src sample format %d\n", sample_fmts[0]); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "channels", channel_counts[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src channels %d\n", channel_counts[0]); goto fail; } ret = av_opt_set(pdata->avbuffsrc_ctx, "channel_layout", channel_layout_str, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src channel layout %s\n", channel_layout_str); goto fail; } ret = avfilter_init_str(pdata->avbuffsrc_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init buffer source\n"); goto fail; } // Initialize the buffer sink filter context pdata->avbuffsink_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, abuffersink, "out"); if (!pdata->avbuffsink_ctx) { mlt_log_error(self, "Cannot create audio buffer sink\n"); goto fail; } #if LIBAVFILTER_VERSION_INT >= ((10 << 16) + (6 << 8) + 100) ret = av_opt_set_array(pdata->avbuffsink_ctx, "sample_formats", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_SAMPLE_FMT, sample_fmts); if (ret < 0) { mlt_log_error(self, "Cannot set sink sample formats\n"); goto fail; } ret = av_opt_set_array(pdata->avbuffsink_ctx, "samplerates", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_INT, sample_rates); if (ret < 0) { mlt_log_error(self, "Cannot set sink sample rates\n"); goto fail; } ret = av_opt_set_array(pdata->avbuffsink_ctx, "channel_layouts", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_CHLAYOUT, &pdata->ch_layout); if (ret < 0) { mlt_log_error(self, "Cannot set sink channel layouts\n"); goto fail; } #else ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set sink sample formats\n"); goto fail; } ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set sink sample rates\n"); goto fail; } #if HAVE_FFMPEG_CH_LAYOUT ret = av_opt_set(pdata->avbuffsink_ctx, "ch_layouts", channel_layout_str, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set sink ch_layouts\n"); goto fail; } #else ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "channel_counts", channel_counts, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set sink channel counts\n"); goto fail; } ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set sink channel_layouts\n"); goto fail; } #endif #endif ret = avfilter_init_str(pdata->avbuffsink_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init buffer sink\n"); goto fail; } // Initialize the filter context pdata->avfilter_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, pdata->avfilter, pdata->avfilter->name); if (!pdata->avfilter_ctx) { mlt_log_error(self, "Cannot create audio filter\n"); goto fail; } set_avfilter_options(self, 1.0); ret = avfilter_init_str(pdata->avfilter_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init filter\n"); goto fail; } // Connect the filters ret = avfilter_link(pdata->avbuffsrc_ctx, 0, pdata->avfilter_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link src to filter\n"); goto fail; } ret = avfilter_link(pdata->avfilter_ctx, 0, pdata->avbuffsink_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link filter to sink\n"); goto fail; } // Configure the graph. ret = avfilter_graph_config(pdata->avfilter_graph, NULL); if (ret < 0) { mlt_log_error(self, "Cannot configure the filter graph\n"); goto fail; } return; fail: avfilter_graph_free(&pdata->avfilter_graph); } static void init_image_filtergraph(mlt_link self, mlt_image_format format, int width, int height, mlt_colorspace colorspace, int full_range, double resolution_scale) { private_data *pdata = (private_data *) self->child; mlt_profile profile = mlt_service_profile(MLT_LINK_SERVICE(self)); const AVFilter *buffersrc = avfilter_get_by_name("buffer"); const AVFilter *buffersink = avfilter_get_by_name("buffersink"); const AVFilter *scale = avfilter_get_by_name("scale"); const AVFilter *pad = avfilter_get_by_name("pad"); mlt_properties p = mlt_properties_new(); enum AVPixelFormat pixel_fmts[] = {-1, -1}; AVRational sar = (AVRational){profile->sample_aspect_num, profile->sample_aspect_den}; AVRational timebase = (AVRational){profile->frame_rate_den, profile->frame_rate_num}; AVRational framerate = (AVRational){profile->frame_rate_num, profile->frame_rate_den}; int avcolorspace = mlt_to_av_colorspace(colorspace, height); int color_range = mlt_to_av_color_range(full_range); int ret; pdata->format = format; pdata->width = width; pdata->height = height; pdata->colorspace = colorspace; pdata->full_range = full_range; // Set up formats pixel_fmts[0] = mlt_to_av_image_format(format); // Destroy the current filter graph avfilter_graph_free(&pdata->avfilter_graph); // Create the new filter graph pdata->avfilter_graph = avfilter_graph_alloc(); if (!pdata->avfilter_graph) { mlt_log_error(self, "Cannot create filter graph\n"); goto fail; } pdata->avfilter_graph->scale_sws_opts = av_strdup("flags=" MLT_AVFILTER_SWS_FLAGS); // Set thread count if supported. if (pdata->avfilter->flags & AVFILTER_FLAG_SLICE_THREADS) { av_opt_set_int(pdata->avfilter_graph, "threads", FFMAX(0, mlt_properties_get_int(MLT_LINK_PROPERTIES(self), "av.threads")), 0); } // Initialize the buffer source filter context pdata->avbuffsrc_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, buffersrc, "in"); if (!pdata->avbuffsrc_ctx) { mlt_log_error(self, "Cannot create image buffer source\n"); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "width", width, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src width %d\n", width); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "height", height, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src height %d\n", height); goto fail; } ret = av_opt_set_pixel_fmt(pdata->avbuffsrc_ctx, "pix_fmt", pixel_fmts[0], AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src pixel format %d\n", pixel_fmts[0]); goto fail; } ret = av_opt_set_q(pdata->avbuffsrc_ctx, "sar", sar, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src sar %d/%d\n", sar.num, sar.den); goto fail; } ret = av_opt_set_q(pdata->avbuffsrc_ctx, "time_base", timebase, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src time_base %d/%d\n", timebase.num, timebase.den); goto fail; } ret = av_opt_set_q(pdata->avbuffsrc_ctx, "frame_rate", framerate, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src frame_rate %d/%d\n", framerate.num, framerate.den); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "colorspace", avcolorspace, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src colorspace %d\n", avcolorspace); goto fail; } ret = av_opt_set_int(pdata->avbuffsrc_ctx, "range", color_range, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set src range %d\n", color_range); goto fail; } ret = avfilter_init_str(pdata->avbuffsrc_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init buffer source\n"); goto fail; } // Initialize the buffer sink filter context pdata->avbuffsink_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, buffersink, "out"); if (!pdata->avbuffsink_ctx) { mlt_log_error(self, "Cannot create image buffer sink\n"); goto fail; } #if LIBAVFILTER_VERSION_INT >= ((10 << 16) + (6 << 8) + 100) ret = av_opt_set_array(pdata->avbuffsink_ctx, "pixel_formats", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_PIXEL_FMT, pixel_fmts); #else ret = av_opt_set_int_list(pdata->avbuffsink_ctx, "pix_fmts", pixel_fmts, -1, AV_OPT_SEARCH_CHILDREN); #endif if (ret < 0) { mlt_log_error(self, "Cannot set sink pixel formats\n"); goto fail; } ret = avfilter_init_str(pdata->avbuffsink_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init buffer sink\n"); goto fail; } // Initialize the filter context pdata->avfilter_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, pdata->avfilter, pdata->avfilter->name); if (!pdata->avfilter_ctx) { mlt_log_error(self, "Cannot create video filter\n"); goto fail; } set_avfilter_options(self, resolution_scale); if (!strcmp("lut3d", pdata->avfilter->name)) { #if defined(__GLIBC__) || defined(__APPLE__) || (__FreeBSD__) // LUT data files use period for the decimal point regardless of LC_NUMERIC. mlt_locale_t posix_locale = newlocale(LC_NUMERIC_MASK, "POSIX", NULL); // Get the current locale and switch to POSIX local. mlt_locale_t orig_locale = uselocale(posix_locale); // Initialize the filter. ret = avfilter_init_str(pdata->avfilter_ctx, NULL); // Restore the original locale. uselocale(orig_locale); freelocale(posix_locale); #else // Get the current locale and switch to POSIX local. char *orig_localename = strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); // Initialize the filter. ret = avfilter_init_str(pdata->avfilter_ctx, NULL); // Restore the original locale. setlocale(LC_NUMERIC, orig_localename); free(orig_localename); #endif } else { ret = avfilter_init_str(pdata->avfilter_ctx, NULL); } if (ret < 0) { mlt_log_error(self, "Cannot init scale filter: %s\n", av_err2str(ret)); goto fail; } // Initialize the scale filter context pdata->scale_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, scale, "scale"); if (!pdata->scale_ctx) { mlt_log_error(self, "Cannot create scale filer\n"); goto fail; } mlt_properties_set_int(p, "w", width); mlt_properties_set_int(p, "h", height); const AVOption *opt = av_opt_find(pdata->scale_ctx->priv, "w", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->scale_ctx->priv, opt->name, mlt_properties_get(p, "w"), 0); if (ret < 0) { mlt_log_error(self, "Cannot set scale width\n"); goto fail; } } opt = av_opt_find(pdata->scale_ctx->priv, "h", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->scale_ctx->priv, opt->name, mlt_properties_get(p, "h"), 0); if (ret < 0) { mlt_log_error(self, "Cannot set scale height\n"); goto fail; } } ret = av_opt_set_int(pdata->scale_ctx, "force_original_aspect_ratio", 1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(self, "Cannot set scale force_original_aspect_ratio\n"); goto fail; } opt = av_opt_find(pdata->scale_ctx->priv, "flags", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->scale_ctx->priv, opt->name, MLT_AVFILTER_SWS_FLAGS, 0); if (ret < 0) { mlt_log_error(self, "Cannot set scale flags\n"); goto fail; } } ret = avfilter_init_str(pdata->scale_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init scale filter\n"); goto fail; } // Initialize the padding filter context pdata->pad_ctx = avfilter_graph_alloc_filter(pdata->avfilter_graph, pad, "pad"); if (!pdata->pad_ctx) { mlt_log_error(self, "Cannot create pad filter\n"); goto fail; } opt = av_opt_find(pdata->pad_ctx->priv, "w", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->pad_ctx->priv, opt->name, mlt_properties_get(p, "w"), 0); if (ret < 0) { mlt_log_error(self, "Cannot set pad width\n"); goto fail; } } opt = av_opt_find(pdata->pad_ctx->priv, "h", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->pad_ctx->priv, opt->name, mlt_properties_get(p, "h"), 0); if (ret < 0) { mlt_log_error(self, "Cannot pad scale height\n"); goto fail; } } opt = av_opt_find(pdata->pad_ctx->priv, "x", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->pad_ctx->priv, opt->name, "(ow-iw)/2", 0); if (ret < 0) { mlt_log_error(self, "Cannot set pad x\n"); goto fail; } } opt = av_opt_find(pdata->pad_ctx->priv, "y", 0, 0, 0); if (opt) { ret = av_opt_set(pdata->pad_ctx->priv, opt->name, "(oh-ih)/2", 0); if (ret < 0) { mlt_log_error(self, "Cannot set pad y\n"); goto fail; } } ret = avfilter_init_str(pdata->pad_ctx, NULL); if (ret < 0) { mlt_log_error(self, "Cannot init pad filter\n"); goto fail; } // Connect the filters ret = avfilter_link(pdata->avbuffsrc_ctx, 0, pdata->avfilter_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link src to filter\n"); goto fail; } ret = avfilter_link(pdata->avfilter_ctx, 0, pdata->scale_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link filter to scale\n"); goto fail; } ret = avfilter_link(pdata->scale_ctx, 0, pdata->pad_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link scale to pad\n"); goto fail; } ret = avfilter_link(pdata->pad_ctx, 0, pdata->avbuffsink_ctx, 0); if (ret < 0) { mlt_log_error(self, "Cannot link pad to sink\n"); goto fail; } // Configure the graph. ret = avfilter_graph_config(pdata->avfilter_graph, NULL); if (ret < 0) { mlt_log_error(self, "Cannot configure the filter graph\n"); goto fail; } return; fail: mlt_properties_close(p); avfilter_graph_free(&pdata->avfilter_graph); } static mlt_position get_position(mlt_link self, mlt_frame frame) { mlt_position position = mlt_frame_get_position(frame); const char *pos_type = mlt_properties_get(MLT_LINK_PROPERTIES(self), "position"); if (pos_type) { if (!strcmp("link", pos_type)) { position = mlt_producer_position(MLT_LINK_PRODUCER(self)); } else if (!strcmp("source", pos_type)) { position = mlt_frame_original_position(frame); } } else { private_data *pdata = (private_data *) self->child; if (!strcmp("subtitles", pdata->avfilter->name)) position = mlt_frame_original_position(frame); } return position; } static void link_configure(mlt_link self, mlt_profile chain_profile) { // Operate at the same frame rate as the next link mlt_service_set_profile(MLT_LINK_SERVICE(self), mlt_service_profile(MLT_PRODUCER_SERVICE(self->next))); } static int link_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int error = 0; mlt_link self = mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) self->child; double fps = mlt_profile_fps(mlt_service_profile(MLT_LINK_SERVICE(self))); int ret; mlt_service_lock(MLT_LINK_SERVICE(self)); if (pdata->reset || pdata->format != *format || pdata->channels != *channels || pdata->frequency != *frequency || pdata->expected_frame != mlt_frame_get_position(frame)) { mlt_log_error(MLT_LINK_SERVICE(self), "Init: %s\t%dc\t%dHz\n", mlt_audio_format_name(*format), *channels, *frequency); init_audio_filtergraph(self, *format, *frequency, *channels); pdata->reset = 0; pdata->format = *format; pdata->channels = *channels; pdata->frequency = *frequency; pdata->continuity_frame = mlt_frame_get_position(frame); pdata->expected_frame = mlt_frame_get_position(frame); } pdata->expected_frame++; while (pdata->avfilter_graph) { // Choose the frame to use (maybe a future frame) mlt_frame src_frame = NULL; if (pdata->continuity_frame == mlt_frame_get_position(frame)) { src_frame = frame; pdata->continuity_frame++; } else { mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_LINK_SERVICE(self)); if (!unique_properties) { mlt_log_error(MLT_LINK_SERVICE(self), "Missing future frames\n"); error = 1; break; } char key[19]; int frame_delta = mlt_frame_get_position(frame) - mlt_frame_original_position(frame); sprintf(key, "%d", pdata->continuity_frame - frame_delta); src_frame = (mlt_frame) mlt_properties_get_data(unique_properties, key, NULL); if (!src_frame) { mlt_log_error(MLT_LINK_SERVICE(self), "Frame not found: %s\n", key); error = 1; break; } pdata->continuity_frame++; } // Get the producer's audio struct mlt_audio_s in; mlt_audio_set_values(&in, NULL, *frequency, *format, 0, *channels); in.samples = mlt_audio_calculate_frame_samples(mlt_producer_get_fps(MLT_LINK_PRODUCER(self)), in.frequency, mlt_frame_get_position(src_frame)); error = mlt_frame_get_audio(src_frame, &in.data, &in.format, &in.frequency, &in.channels, &in.samples); if (error || in.format != *format || in.frequency != *frequency || in.channels != *channels) { // Error situation. Do not attempt to process. mlt_log_error(MLT_LINK_SERVICE(self), "Invalid Return: E: %d %dS - %dHz %dC %s\n", error, in.samples, in.frequency, in.channels, mlt_audio_format_name(in.format)); error = 1; break; } // Set up the input frame mlt_channel_layout layout = mlt_get_channel_layout_or_default(mlt_properties_get(MLT_FRAME_PROPERTIES(src_frame), "channel_layout"), in.channels); int64_t samplepos = mlt_audio_calculate_samples_to_position(fps, *frequency, get_position(self, src_frame)); int inbufsize = mlt_audio_format_size(in.format, in.samples, in.channels); pdata->avinframe->sample_rate = in.frequency; pdata->avinframe->format = mlt_to_av_sample_format(in.format); #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_from_mask(&pdata->avinframe->ch_layout, mlt_to_av_channel_layout(layout)); #else pdata->avinframe->channel_layout = mlt_to_av_channel_layout(layout); pdata->avinframe->channels = in.channels; #endif pdata->avinframe->nb_samples = in.samples; pdata->avinframe->pts = samplepos; ret = av_frame_get_buffer(pdata->avinframe, 1); if (ret < 0) { mlt_log_error(self, "Cannot get in frame buffer\n"); } if (av_sample_fmt_is_planar(pdata->avinframe->format)) { int i = 0; int stride = inbufsize / *channels; for (i = 0; i < *channels; i++) { memcpy(pdata->avinframe->extended_data[i], (uint8_t *) in.data + stride * i, stride); } } else { memcpy(pdata->avinframe->extended_data[0], (uint8_t *) in.data, inbufsize); } send_avformat_commands(self, frame, pdata, 1.0); // Run the frame through the filter graph ret = av_buffersrc_add_frame(pdata->avbuffsrc_ctx, pdata->avinframe); if (ret < 0) { mlt_log_error(self, "Cannot add frame to buffer source\n"); } ret = av_buffersink_get_samples(pdata->avbuffsink_ctx, pdata->avoutframe, *samples); if (ret == AVERROR(EAGAIN)) { mlt_log_debug(self, "Need more samples from next future frame\n"); continue; } else if (ret < 0) { mlt_log_error(self, "Cannot get frame from buffer sink\n"); error = 1; break; } #if HAVE_FFMPEG_CH_LAYOUT int expected_channels = pdata->avoutframe->ch_layout.nb_channels; #else int expected_channels = pdata->avoutframe->channels; #endif // Sanity check the output frame if (*channels != expected_channels || *samples != pdata->avoutframe->nb_samples || *frequency != pdata->avoutframe->sample_rate) { mlt_log_error(self, "Unexpected return format c %d->%d\tf %d->%d\tf %d->%d\n", *channels, expected_channels, *samples, pdata->avoutframe->nb_samples, *frequency, pdata->avoutframe->sample_rate); error = 1; break; } // Copy the filter output into the frame int bufsize = mlt_audio_format_size(*format, *samples, *channels); *buffer = mlt_pool_alloc(bufsize); if (av_sample_fmt_is_planar(pdata->avoutframe->format)) { int stride = bufsize / *channels; int i = 0; for (i = 0; i < *channels; i++) { memcpy((uint8_t *) *buffer + stride * i, pdata->avoutframe->extended_data[i], stride); } } else { memcpy((uint8_t *) *buffer, pdata->avoutframe->extended_data[0], bufsize); } mlt_frame_set_audio(frame, *buffer, *format, bufsize, mlt_pool_release); break; } av_frame_unref(pdata->avinframe); av_frame_unref(pdata->avoutframe); if (error) { // Return unprocessed audio if an error occurs error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } else { // Clear the audio stack in case get_audio was not called on this frame. while (mlt_frame_pop_audio(frame)) { }; } mlt_service_unlock(MLT_LINK_SERVICE(self)); return error; } static int link_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_link self = mlt_frame_pop_service(frame); private_data *pdata = (private_data *) self->child; int64_t pos = get_position(self, frame); mlt_profile profile = mlt_service_profile(MLT_LINK_SERVICE(self)); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); int ret; mlt_log_debug(MLT_LINK_SERVICE(self), "position %" PRId64 "\n", pos); if (mlt_properties_get_int(MLT_LINK_PROPERTIES(self), "_yuv_only")) { *format = mlt_image_yuv422; } else { *format = mlt_get_supported_image_format(*format); } mlt_frame_get_image(frame, image, format, width, height, 0); mlt_service_lock(MLT_LINK_SERVICE(self)); double scale = mlt_profile_scale_width(profile, *width); const char *colorspace_str = mlt_properties_get(frame_properties, "colorspace"); mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); int full_range = mlt_properties_get_int(frame_properties, "full_range"); if (pdata->reset || pdata->format != *format || pdata->width != *width || pdata->height != *height || pdata->colorspace != colorspace || pdata->full_range != full_range) { init_image_filtergraph(self, *format, *width, *height, colorspace, full_range, scale); pdata->reset = 0; } if (pdata->avfilter_graph) { pdata->avinframe->width = *width; pdata->avinframe->height = *height; pdata->avinframe->format = mlt_to_av_image_format(*format); pdata->avinframe->sample_aspect_ratio = (AVRational){profile->sample_aspect_num, profile->sample_aspect_den}; pdata->avinframe->pts = pos; #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) if (!mlt_properties_get_int(frame_properties, "progressive")) pdata->avinframe->flags |= AV_FRAME_FLAG_INTERLACED; else pdata->avinframe->flags &= ~AV_FRAME_FLAG_INTERLACED; if (mlt_properties_get_int(frame_properties, "top_field_first")) pdata->avinframe->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; else pdata->avinframe->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; #else pdata->avinframe->interlaced_frame = !mlt_properties_get_int(frame_properties, "progressive"); pdata->avinframe->top_field_first = mlt_properties_get_int(frame_properties, "top_field_first"); #endif const char *primaries_str = mlt_properties_get(frame_properties, "color_primaries"); mlt_color_primaries primaries = mlt_image_color_pri_id(primaries_str); if (primaries == mlt_color_pri_none) primaries = mlt_image_default_primaries(colorspace, *height); pdata->avinframe->color_primaries = mlt_to_av_color_primaries(primaries); const char *color_trc_str = mlt_properties_get(frame_properties, "color_trc"); mlt_color_trc trc = mlt_image_color_trc_id(color_trc_str); pdata->avinframe->color_trc = mlt_to_av_color_trc(trc); if (trc == mlt_color_trc_none) trc = mlt_image_default_trc(colorspace); pdata->avinframe->color_range = mlt_to_av_color_range(full_range); const char *colorspace_str = mlt_properties_get(frame_properties, "colorspace"); mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); pdata->avinframe->colorspace = mlt_to_av_colorspace(colorspace, pdata->avinframe->height); ret = av_frame_get_buffer(pdata->avinframe, 1); if (ret < 0) { mlt_log_error(self, "Cannot get in frame buffer\n"); } // Set up the input frame if (*format == mlt_image_yuv420p) { int i = 0; int p = 0; int widths[3] = {*width, *width / 2, *width / 2}; int heights[3] = {*height, *height / 2, *height / 2}; uint8_t *src = *image; for (p = 0; p < 3; p++) { uint8_t *dst = pdata->avinframe->data[p]; for (i = 0; i < heights[p]; i++) { memcpy(dst, src, widths[p]); src += widths[p]; dst += pdata->avinframe->linesize[p]; } } } else { int i; uint8_t *src = *image; uint8_t *dst = pdata->avinframe->data[0]; int stride = mlt_image_format_size(*format, *width, 1, NULL); for (i = 0; i < *height; i++) { memcpy(dst, src, stride); src += stride; dst += pdata->avinframe->linesize[0]; } } send_avformat_commands(self, frame, pdata, scale); // Run the frame through the filter graph ret = av_buffersrc_add_frame(pdata->avbuffsrc_ctx, pdata->avinframe); if (ret < 0) { mlt_log_error(self, "Cannot add frame to buffer source\n"); } ret = av_buffersink_get_frame(pdata->avbuffsink_ctx, pdata->avoutframe); if (ret < 0) { mlt_log_error(self, "Cannot get frame from buffer sink\n"); } // Sanity check the output frame if (*width != pdata->avoutframe->width || *height != pdata->avoutframe->height) { mlt_log_error(self, "Unexpected return format\n"); goto exit; } // Copy the filter output into the original buffer if (*format == mlt_image_yuv420p) { int i = 0; int p = 0; int widths[3] = {*width, *width / 2, *width / 2}; int heights[3] = {*height, *height / 2, *height / 2}; uint8_t *dst = *image; for (p = 0; p < 3; p++) { uint8_t *src = pdata->avoutframe->data[p]; for (i = 0; i < heights[p]; i++) { memcpy(dst, src, widths[p]); dst += widths[p]; src += pdata->avoutframe->linesize[p]; } } } else { int i; uint8_t *dst = *image; uint8_t *src = pdata->avoutframe->data[0]; int stride = mlt_image_format_size(*format, *width, 1, NULL); for (i = 0; i < *height; i++) { memcpy(dst, src, stride); dst += stride; src += pdata->avoutframe->linesize[0]; } } mlt_properties_set_int(frame_properties, "color_trc", av_to_mlt_color_trc(pdata->avoutframe->color_trc)); mlt_properties_set_int(frame_properties, "colorspace", av_to_mlt_colorspace(pdata->avoutframe->colorspace, pdata->avoutframe->width, pdata->avoutframe->height)); mlt_properties_set_int(frame_properties, "color_primaries", av_to_mlt_color_primaries(pdata->avoutframe->color_primaries)); mlt_properties_set_int(frame_properties, "full_range", av_to_mlt_full_range(pdata->avoutframe->color_range)); } exit: av_frame_unref(pdata->avinframe); av_frame_unref(pdata->avoutframe); mlt_service_unlock(MLT_LINK_SERVICE(self)); return 0; } static int link_get_frame(mlt_link self, mlt_frame_ptr frame, int index) { int error = 0; mlt_position frame_pos = mlt_producer_position(MLT_LINK_PRODUCER(self)); mlt_producer_seek(self->next, frame_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), frame, index); mlt_properties unique_properties = mlt_frame_unique_properties(*frame, MLT_LINK_SERVICE(self)); // Pass future frames int future_frames = future_frames_needed(self); int i = 0; for (i = 0; i < future_frames; i++) { mlt_position future_pos = frame_pos + i + 1; mlt_frame future_frame = NULL; mlt_producer_seek(self->next, future_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), &future_frame, index); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Error getting frame: %d\n", (int) future_pos); } char key[19]; sprintf(key, "%d", (int) future_pos); mlt_properties_set_data(unique_properties, key, future_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } private_data *pdata = (private_data *) self->child; if (avfilter_pad_get_type(pdata->avfilter->inputs, 0) == AVMEDIA_TYPE_VIDEO) { mlt_frame_push_service(*frame, self); mlt_frame_push_get_image(*frame, link_get_image); } else if (avfilter_pad_get_type(pdata->avfilter->inputs, 0) == AVMEDIA_TYPE_AUDIO) { mlt_frame_push_audio(*frame, self); mlt_frame_push_audio(*frame, link_get_audio); } mlt_producer_prepare_next(MLT_LINK_PRODUCER(self)); return error; } static void link_close(mlt_link self) { if (self) { private_data *pdata = (private_data *) self->child; if (pdata) { avfilter_graph_free(&pdata->avfilter_graph); av_frame_free(&pdata->avinframe); av_frame_free(&pdata->avoutframe); #if HAVE_FFMPEG_CH_LAYOUT av_channel_layout_uninit(&pdata->ch_layout); #endif free(pdata); } self->close = NULL; mlt_link_close(self); free(self); } } mlt_link link_avfilter_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_link self = mlt_link_init(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (pdata && id) { id += 9; // Move past "avfilter." pdata->avfilter = (AVFilter *) avfilter_get_by_name(id); } if (self && pdata && pdata->avfilter) { pdata->avbuffsink_ctx = NULL; pdata->avbuffsrc_ctx = NULL; pdata->avfilter_ctx = NULL; pdata->avfilter_graph = NULL; pdata->avinframe = av_frame_alloc(); pdata->avoutframe = av_frame_alloc(); pdata->format = -1; pdata->width = -1; pdata->height = -1; pdata->reset = 1; self->child = pdata; // Callback registration self->configure = link_configure; self->get_frame = link_get_frame; self->close = link_close; mlt_events_listen(MLT_LINK_PROPERTIES(self), self, "property-changed", (mlt_listener) property_changed); mlt_properties param_name_map = mlt_properties_get_data(mlt_global_properties(), "avfilter.resolution_scale", NULL); if (param_name_map) { // Lookup my plugin in the map param_name_map = mlt_properties_get_data(param_name_map, id, NULL); mlt_properties_set_data(MLT_LINK_PROPERTIES(self), "_resolution_scale", param_name_map, 0, NULL, NULL); } mlt_properties yuv_only = mlt_properties_get_data(mlt_global_properties(), "avfilter.yuv_only", NULL); if (yuv_only) { if (mlt_properties_get(yuv_only, id)) { mlt_properties_set_int(MLT_LINK_PROPERTIES(self), "_yuv_only", 1); } } } else { free(pdata); mlt_link_close(self); self = NULL; } return self; } mlt-7.38.0/src/modules/avformat/link_swresample.c000664 000000 000000 00000034521 15172202314 022010 0ustar00rootroot000000 000000 /* * link_swresample.c * Copyright (C) 2022-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "common_swr.h" #include #include #include #include #include #include #include // Private Types #define FUTURE_FRAMES 1 typedef struct { mlt_position expected_frame; mlt_position continuity_frame; } private_data; static void destroy_swr_data(mlt_swr_private_data *swr) { if (swr) { mlt_free_swr_context(swr); free(swr); } } static void link_configure(mlt_link self, mlt_profile chain_profile) { // Operate at the same frame rate as the next link mlt_service_set_profile(MLT_LINK_SERVICE(self), mlt_service_profile(MLT_PRODUCER_SERVICE(self->next))); } static int link_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int requested_frequency = *frequency <= 0 ? 48000 : *frequency; int requested_samples = *samples; mlt_link self = (mlt_link) mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) self->child; // Validate the request *channels = *channels <= 0 ? 2 : *channels; int src_frequency = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "audio_frequency"); src_frequency = src_frequency <= 0 ? *frequency : src_frequency; int src_samples = mlt_audio_calculate_frame_samples(mlt_producer_get_fps( MLT_LINK_PRODUCER(self)), src_frequency, mlt_frame_get_position(frame)); int src_channels = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "audio_channels"); src_channels = src_channels <= 0 ? *channels : src_channels; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); struct mlt_audio_s in; struct mlt_audio_s out; mlt_audio_set_values(&in, *buffer, src_frequency, mlt_audio_none, src_samples, src_channels); mlt_audio_set_values(&out, NULL, requested_frequency, *format, requested_samples, *channels); // Get the producer's audio int error = mlt_frame_get_audio(frame, &in.data, &in.format, &in.frequency, &in.channels, &in.samples); if (out.format == mlt_audio_none) { out.format = in.format; } if (error || in.format == mlt_audio_none || out.format == mlt_audio_none || in.frequency <= 0 || out.frequency <= 0 || in.channels <= 0 || out.channels <= 0) { // Error situation. Do not attempt to convert. mlt_audio_get_values(&in, buffer, frequency, format, samples, channels); mlt_log_error(MLT_LINK_SERVICE(self), "Invalid Parameters: %dS - %dHz %dC %s -> %dHz %dC %s\n", in.samples, in.frequency, in.channels, mlt_audio_format_name(in.format), out.frequency, out.channels, mlt_audio_format_name(out.format)); return error; } if (in.samples == 0) { // Noting to convert. return error; } // Determine the input/output channel layout. in.layout = mlt_get_channel_layout_or_default(mlt_properties_get(frame_properties, "channel_layout"), in.channels); out.layout = mlt_get_channel_layout_or_default(mlt_properties_get(frame_properties, "consumer.channel_layout"), out.channels); if (in.format == out.format && in.frequency == out.frequency && in.channels == out.channels && in.layout == out.layout) { // No change necessary mlt_audio_get_values(&in, buffer, frequency, format, samples, channels); return error; } mlt_service_lock(MLT_LINK_SERVICE(self)); mlt_swr_private_data *swr = NULL; int cache_miss = 0; mlt_cache_item cache_item = mlt_service_cache_get(MLT_LINK_SERVICE(self), "link_swresample"); swr = mlt_cache_item_data(cache_item, NULL); if (!cache_item) { cache_miss = 1; } // Detect configuration change if (cache_miss || swr->in_format != in.format || swr->out_format != out.format || swr->in_frequency != in.frequency || swr->out_frequency != out.frequency || swr->in_channels != in.channels || swr->out_channels != out.channels || swr->in_layout != in.layout || swr->out_layout != out.layout || pdata->expected_frame != mlt_frame_get_position(frame)) { mlt_cache_item_close(cache_item); swr = calloc(1, sizeof(mlt_swr_private_data)); // Save the configuration swr->in_format = in.format; swr->out_format = out.format; swr->in_frequency = in.frequency; swr->out_frequency = out.frequency; swr->in_channels = in.channels; swr->out_channels = out.channels; swr->in_layout = in.layout; swr->out_layout = out.layout; // Reconfigure the context error = mlt_configure_swr_context(MLT_LINK_SERVICE(self), swr); mlt_service_cache_put(MLT_LINK_SERVICE(self), "link_swresample", swr, 0, (mlt_destructor) destroy_swr_data); cache_item = mlt_service_cache_get(MLT_LINK_SERVICE(self), "link_swresample"); swr = mlt_cache_item_data(cache_item, NULL); pdata->continuity_frame = mlt_frame_get_position(frame); pdata->expected_frame = mlt_frame_get_position(frame); } if (swr && !error) { int total_received_samples = 0; out.samples = requested_samples; mlt_audio_alloc_data(&out); if (pdata->continuity_frame == mlt_frame_get_position(frame)) { // This is the nominal case when sample rate is not changing mlt_audio_get_planes(&in, swr->in_buffers); mlt_audio_get_planes(&out, swr->out_buffers); total_received_samples = swr_convert(swr->ctx, swr->out_buffers, out.samples, (const uint8_t **) swr->in_buffers, in.samples); if (total_received_samples < 0) { mlt_log_error(MLT_LINK_SERVICE(self), "swr_convert() failed. Needed: %d\tIn: %d\tOut: %d\n", out.samples, in.samples, total_received_samples); error = 1; } pdata->continuity_frame++; } while (total_received_samples < requested_samples && !error) { // The input frame is insufficient to fill the output frame. // This happens when sample rate conversion is occurring. // Request data from future frames. mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_LINK_SERVICE(self)); if (!unique_properties) { error = 1; break; } char key[19]; int frame_delta = mlt_frame_get_position(frame) - mlt_frame_original_position(frame); sprintf(key, "%d", pdata->continuity_frame - frame_delta); mlt_frame src_frame = (mlt_frame) mlt_properties_get_data(unique_properties, key, NULL); if (!src_frame) { mlt_log_error(MLT_LINK_SERVICE(self), "Frame not found: %s\n", key); break; } // Get the audio from the in frame in.samples = mlt_audio_calculate_frame_samples(mlt_producer_get_fps( MLT_LINK_PRODUCER(self)), in.frequency, pdata->continuity_frame); in.format = mlt_audio_none; error = mlt_frame_get_audio(src_frame, &in.data, &in.format, &in.frequency, &in.channels, &in.samples); if (error) { break; } // Set up the SWR buffer for the audio from the in frame mlt_audio_get_planes(&in, swr->in_buffers); // Set up the SWR buffer for the audio from the out frame, // shifting according to what has already been received. int plane_count = mlt_audio_plane_count(&out); int plane_size = mlt_audio_plane_size(&out); int out_step_size = plane_size / out.samples; int p = 0; for (p = 0; p < plane_count; p++) { uint8_t *pAudio = (uint8_t *) out.data + (out_step_size * total_received_samples); swr->out_buffers[p] = pAudio + (p * plane_size); } int samples_needed = requested_samples - total_received_samples; int received_samples = swr_convert(swr->ctx, swr->out_buffers, samples_needed, (const uint8_t **) swr->in_buffers, in.samples); if (received_samples < 0) { mlt_log_error(MLT_LINK_SERVICE(self), "swr_convert() failed. Needed: %d\tIn: %d\tOut: %d\n", samples_needed, in.samples, received_samples); error = 1; } else { total_received_samples += received_samples; } pdata->continuity_frame++; } if (total_received_samples == 0) { mlt_log_info(MLT_LINK_SERVICE(self), "Failed to get any samples - return silence\n"); mlt_audio_silence(&out, out.samples, 0); } else if (total_received_samples < out.samples) { // Duplicate samples to return the exact number requested. mlt_audio_copy(&out, &out, total_received_samples, 0, out.samples - total_received_samples); } mlt_frame_set_audio(frame, out.data, out.format, 0, out.release_data); mlt_audio_get_values(&out, buffer, frequency, format, samples, channels); mlt_properties_set(frame_properties, "channel_layout", mlt_audio_channel_layout_name(out.layout)); pdata->expected_frame = mlt_frame_get_position(frame) + 1; } mlt_cache_item_close(cache_item); mlt_service_unlock(MLT_LINK_SERVICE(self)); return error; } static int link_get_frame(mlt_link self, mlt_frame_ptr frame, int index) { int error = 0; mlt_position frame_pos = mlt_producer_position(MLT_LINK_PRODUCER(self)); mlt_producer_seek(self->next, frame_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), frame, index); if (error) { return error; } mlt_properties unique_properties = mlt_frame_unique_properties(*frame, MLT_LINK_SERVICE(self)); // Pass future frames int i = 0; for (i = 0; i < FUTURE_FRAMES; i++) { mlt_position future_pos = frame_pos + i + 1; mlt_frame future_frame = NULL; mlt_producer_seek(self->next, future_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), &future_frame, index); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Error getting frame: %d\n", (int) future_pos); } char key[19]; sprintf(key, "%d", (int) future_pos); mlt_properties_set_data(unique_properties, key, future_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } mlt_frame_push_audio(*frame, (void *) self); mlt_frame_push_audio(*frame, link_get_audio); mlt_producer_prepare_next(MLT_LINK_PRODUCER(self)); return error; } static void link_close(mlt_link self) { if (self) { mlt_service_cache_purge(MLT_LINK_SERVICE(self)); free(self->child); self->close = NULL; self->child = NULL; mlt_link_close(self); free(self); } } mlt_link link_swresample_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_link self = mlt_link_init(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (self && pdata) { pdata->continuity_frame = -1; pdata->expected_frame = -1; self->child = pdata; // Callback registration self->configure = link_configure; self->get_frame = link_get_frame; self->close = link_close; } else { if (pdata) { free(pdata); } if (self) { mlt_link_close(self); self = NULL; } } return self; } mlt-7.38.0/src/modules/avformat/link_swresample.yml000664 000000 000000 00000000655 15172202314 022370 0ustar00rootroot000000 000000 schema_version: 7.0 type: link identifier: swresample title: FFmpeg Audio Resampler version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Audio - Hidden description: > Change the audio sampling rate, channel layout and format. This link can be added to a chain to normalize audio from the producer to provide the rate, channels and format requested by the consumer. mlt-7.38.0/src/modules/avformat/mmx.h000664 000000 000000 00000022534 15172202314 017420 0ustar00rootroot000000 000000 /* * mmx.h * Copyright (C) 1997-2001 H. Dietz and R. Fisher */ #ifndef AVCODEC_I386MMX_H #define AVCODEC_I386MMX_H /* * The type of an value that fits in an MMX register (note that long * long constant values MUST be suffixed by LL and unsigned long long * values by ULL, lest they be truncated by the compiler) */ typedef union { long long q; /* Quadword (64-bit) value */ unsigned long long uq; /* Unsigned Quadword */ int d[2]; /* 2 Doubleword (32-bit) values */ unsigned int ud[2]; /* 2 Unsigned Doubleword */ short w[4]; /* 4 Word (16-bit) values */ unsigned short uw[4]; /* 4 Unsigned Word */ char b[8]; /* 8 Byte (8-bit) values */ unsigned char ub[8]; /* 8 Unsigned Byte */ float s[2]; /* Single-precision (32-bit) value */ } mmx_t; /* On an 8-byte (64-bit) boundary */ #define mmx_i2r(op,imm,reg) \ __asm__ __volatile__ (#op " %0, %%" #reg \ : /* nothing */ \ : "i" (imm) ) #define mmx_m2r(op,mem,reg) \ __asm__ __volatile__ (#op " %0, %%" #reg \ : /* nothing */ \ : "m" (mem)) #define mmx_r2m(op,reg,mem) \ __asm__ __volatile__ (#op " %%" #reg ", %0" \ : "=m" (mem) \ : /* nothing */ ) #define mmx_r2r(op,regs,regd) \ __asm__ __volatile__ (#op " %" #regs ", %" #regd) #define emms() __asm__ __volatile__ ("emms") #define movd_m2r(var,reg) mmx_m2r (movd, var, reg) #define movd_r2m(reg,var) mmx_r2m (movd, reg, var) #define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd) #define movq_m2r(var,reg) mmx_m2r (movq, var, reg) #define movq_r2m(reg,var) mmx_r2m (movq, reg, var) #define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd) #define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg) #define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd) #define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg) #define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd) #define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg) #define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd) #define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg) #define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd) #define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg) #define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd) #define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg) #define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd) #define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg) #define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd) #define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg) #define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd) #define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg) #define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd) #define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg) #define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd) #define pand_m2r(var,reg) mmx_m2r (pand, var, reg) #define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd) #define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg) #define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd) #define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg) #define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd) #define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg) #define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd) #define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg) #define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd) #define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg) #define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd) #define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg) #define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd) #define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg) #define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd) #define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg) #define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd) #define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg) #define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd) #define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg) #define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd) #define por_m2r(var,reg) mmx_m2r (por, var, reg) #define por_r2r(regs,regd) mmx_r2r (por, regs, regd) #define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg) #define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg) #define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd) #define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg) #define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg) #define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd) #define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg) #define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg) #define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd) #define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg) #define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg) #define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd) #define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg) #define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg) #define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd) #define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg) #define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg) #define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd) #define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg) #define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg) #define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd) #define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg) #define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg) #define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd) #define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg) #define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd) #define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg) #define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd) #define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg) #define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd) #define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg) #define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd) #define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg) #define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd) #define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg) #define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd) #define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg) #define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd) #define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg) #define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd) #define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg) #define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd) #define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg) #define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd) #define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg) #define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd) #define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg) #define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd) #define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg) #define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd) #define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg) #define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd) /* 3DNOW extensions */ #define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg) #define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd) /* AMD MMX extensions - also available in intel SSE */ #define mmx_m2ri(op,mem,reg,imm) \ __asm__ __volatile__ (#op " %1, %0, %%" #reg \ : /* nothing */ \ : "X" (mem), "X" (imm)) #define mmx_r2ri(op,regs,regd,imm) \ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ : /* nothing */ \ : "X" (imm) ) #define mmx_fetch(mem,hint) \ __asm__ __volatile__ ("prefetch" #hint " %0" \ : /* nothing */ \ : "X" (mem)) #define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg) #define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var) #define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg) #define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd) #define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg) #define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd) #define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm) #define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm) #define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg) #define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd) #define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg) #define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd) #define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg) #define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd) #define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg) #define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd) #define pmovmskb(mmreg,reg) \ __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) #define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg) #define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd) #define prefetcht0(mem) mmx_fetch (mem, t0) #define prefetcht1(mem) mmx_fetch (mem, t1) #define prefetcht2(mem) mmx_fetch (mem, t2) #define prefetchnta(mem) mmx_fetch (mem, nta) #define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg) #define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd) #define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm) #define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm) #define sfence() __asm__ __volatile__ ("sfence\n\t") #endif /* AVCODEC_I386MMX_H */ mlt-7.38.0/src/modules/avformat/producer_avformat-novalidate.yml000664 000000 000000 00000001371 15172202314 025033 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: avformat-novalidate title: Non-validating FFmpeg Reader version: 3 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Audio - Video description: Read an audio and/or video file using FFmpeg. notes: > This is basically the same as the avformat producer, but it does not validate that FFmpeg can open and read the resource. This is primarily useful in a composition (e.g. XML) that was constructed after it was validated. Since validation also determines the length property, you should set that yourself on this producer after having learned it from the normal avformat producer. See the documentation for the normal avformat producer for more information. mlt-7.38.0/src/modules/avformat/producer_avformat.c000664 000000 000000 00000601570 15172202314 022337 0ustar00rootroot000000 000000 /* * producer_avformat.c -- avformat producer * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200809L #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #if !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 500 #undef _XOPEN_SOURCE #define _XOPEN_SOURCE 500 #endif #include "common.h" // MLT Header files #include #include #include #include #include #include #include #include // ffmpeg Header files #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // System header files #include #include #include #include #include #include #include #define POSITION_INITIAL (-2) #define POSITION_INVALID (-1) #define MAX_AUDIO_STREAMS (32) #define MAX_AUDIO_FRAME_SIZE (192000) // 1 second of 48khz 32bit audio #define IMAGE_ALIGN (1) #define VFR_THRESHOLD \ (3) // The minimum number of video frames with differing durations to be considered VFR. struct producer_avformat_s { mlt_producer parent; AVFormatContext *dummy_context; AVFormatContext *audio_format; AVFormatContext *video_format; AVCodecContext *audio_codec[MAX_AUDIO_STREAMS]; AVCodecContext *video_codec; AVFrame *video_frame; AVFrame *audio_frame; AVPacket pkt; mlt_position audio_expected; mlt_position video_expected; int audio_index; int video_index; int64_t first_pts; atomic_int_fast64_t last_position; int video_seekable; int seekable; /// This one is used for both audio and file level seekability. atomic_int_fast64_t current_position; mlt_position nonseek_position; atomic_int top_field_first; int progressive; uint8_t *audio_buffer[MAX_AUDIO_STREAMS]; int audio_buffer_size[MAX_AUDIO_STREAMS]; uint8_t *decode_buffer[MAX_AUDIO_STREAMS]; int audio_used[MAX_AUDIO_STREAMS]; int audio_streams; int audio_max_stream; int total_channels; int max_channel; int max_frequency; unsigned int invalid_pts_counter; unsigned int invalid_dts_counter; mlt_cache image_cache; mlt_cache audio_cache; mlt_colorspace yuv_colorspace; mlt_color_primaries color_primaries; mlt_color_trc color_trc; int full_range; pthread_mutex_t video_mutex; pthread_mutex_t audio_mutex; mlt_deque apackets; mlt_deque vpackets; pthread_mutex_t packets_mutex; pthread_mutex_t open_mutex; pthread_mutex_t close_mutex; int is_mutex_init; pthread_t packets_thread; pthread_cond_t packets_cond; int packets_thread_ret; // latest non-zero non-EGAIN return on av_read_frame() in packets_thread int packets_thread_stop; // non-zero when packets_thread is to stop int is_thread_init; AVRational video_time_base; mlt_frame last_good_frame; // for video error concealment int last_good_position; // for video error concealment AVFilterGraph *vfilter_graph; AVFilterContext *vfilter_in; AVFilterContext *vfilter_out; int autorotate; double rotation; int is_audio_synchronizing; int video_send_result; int reset_image_cache; struct { int pix_fmt; int device_type; char device[128]; AVBufferRef *device_ctx; int filters_initialized; AVFilterGraph *filter_graph; AVFilterContext *filter_in; AVFilterContext *filter_out; } hwaccel; }; typedef struct producer_avformat_s *producer_avformat; // Forward references. static int list_components(char *file); static int producer_open( producer_avformat self, mlt_profile profile, const char *URL, int take_lock, int test_open); static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index); static int producer_probe(mlt_producer producer); static void producer_avformat_close(producer_avformat); static void producer_close(mlt_producer parent); static void producer_set_up_video(producer_avformat self, mlt_frame frame); static void producer_set_up_audio(producer_avformat self, mlt_frame frame); static void apply_properties(void *obj, mlt_properties properties, int flags); static int video_codec_init(producer_avformat self, int index, mlt_properties properties); static void get_audio_streams_info(producer_avformat self); static mlt_audio_format pick_audio_format(int sample_fmt); static int pick_av_pixel_format(int *pix_fmt, int full_range); static void property_changed(mlt_service owner, producer_avformat self, char *name); static int absolute_stream_index(AVFormatContext *context, enum AVMediaType media_type, int relative) { if (context) { int n = -1; for (int i = 0; i < context->nb_streams; i++) { AVCodecParameters *codec_params = context->streams[i]->codecpar; if (codec_params->codec_type == media_type && ++n == relative) { return i; } } } return -1; } static int relative_stream_index(AVFormatContext *context, enum AVMediaType media_type, int absolute) { if (context) { int n = -1; for (int i = 0; i < context->nb_streams; i++) { AVCodecParameters *codec_params = context->streams[i]->codecpar; if (codec_params->codec_type == media_type) { ++n; if (absolute == i) return n; } } } return -1; } /** Constructor for libavformat. */ mlt_producer producer_avformat_init(mlt_profile profile, const char *service, char *file) { if (list_components(file)) return NULL; mlt_producer producer = NULL; // Check that we have a non-NULL argument if (file) { // Construct the producer producer_avformat self = calloc(1, sizeof(struct producer_avformat_s)); producer = calloc(1, sizeof(struct mlt_producer_s)); // Initialise it if (mlt_producer_init(producer, self) == 0) { self->parent = producer; // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); // Set the resource property (required for all producers) mlt_properties_set(properties, "resource", file); // Register transport implementation with the producer producer->close = (mlt_destructor) producer_close; // Register our get_frame implementation producer->get_frame = producer_get_frame; // Register our probe implementation mlt_properties_set_data(properties, "mlt_producer_probe", producer_probe, 0, NULL, NULL); // Force the duration to be computed unless explicitly provided. mlt_properties_set_position(properties, "length", 0); mlt_properties_set_position(properties, "out", 0); if (strcmp(service, "avformat-novalidate")) { // Open the file if (producer_open(self, profile, mlt_properties_get(properties, "resource"), 1, 1) != 0) { // Clean up producer_avformat_close(self); mlt_producer_close(producer); producer = NULL; } else if (self->seekable) { // Close the file to release resources for large playlists - reopen later as needed if (self->audio_format) avformat_close_input(&self->audio_format); if (self->video_format) avformat_close_input(&self->video_format); } } else if (!self->is_mutex_init) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&self->audio_mutex, &attr); pthread_mutex_init(&self->video_mutex, &attr); pthread_mutex_init(&self->packets_mutex, &attr); pthread_mutex_init(&self->open_mutex, &attr); pthread_mutex_init(&self->close_mutex, &attr); self->is_mutex_init = 1; } if (producer) { // Default the user-selectable indices from the auto-detected indices mlt_properties_set_int(properties, "audio_index", self->audio_index); mlt_properties_set_int(properties, "video_index", self->video_index); mlt_service_cache_put(MLT_PRODUCER_SERVICE(producer), "producer_avformat", self, 0, (mlt_destructor) producer_avformat_close); mlt_properties_set_int(properties, "mute_on_pause", 0); mlt_events_listen(properties, self, "property-changed", (mlt_listener) property_changed); } } } return producer; } int list_components(char *file) { int skip = 0; // Report information about available demuxers and codecs as YAML Tiny if (file && strstr(file, "f-list")) { fprintf(stderr, "---\nformats:\n"); void *state = NULL; const AVInputFormat *format = NULL; while ((format = av_demuxer_iterate(&state))) { fprintf(stderr, " - %s\n", format->name); } fprintf(stderr, "...\n"); skip = 1; } if (file && strstr(file, "acodec-list")) { fprintf(stderr, "---\naudio_codecs:\n"); void *state = NULL; const AVCodec *codec = NULL; while ((codec = av_codec_iterate(&state))) { if (av_codec_is_decoder(codec) && codec->type == AVMEDIA_TYPE_AUDIO) fprintf(stderr, " - %s\n", codec->name); } fprintf(stderr, "...\n"); skip = 1; } if (file && strstr(file, "vcodec-list")) { fprintf(stderr, "---\nvideo_codecs:\n"); void *state = NULL; const AVCodec *codec = NULL; while ((codec = av_codec_iterate(&state))) { if (av_codec_is_decoder(codec) && codec->type == AVMEDIA_TYPE_VIDEO) fprintf(stderr, " - %s\n", codec->name); } fprintf(stderr, "...\n"); skip = 1; } return skip; } static int first_video_index(producer_avformat self) { AVFormatContext *context = self->video_format ? self->video_format : self->audio_format; int result = -1; // not found if (context) { unsigned int i; for (i = 0; i < context->nb_streams; i++) { if (context->streams[i]->codecpar && context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) break; } if (i < context->nb_streams) { result = i; } } return result; } #include static const char *get_projection(AVStream *st) { #if LIBAVCODEC_VERSION_INT >= ((60 << 16) + (29 << 8) + 100) const AVPacketSideData *psd = av_packet_side_data_get(st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data, AV_PKT_DATA_SPHERICAL); if (psd) { const AVSphericalMapping *spherical = (const AVSphericalMapping *) psd->data; #else const AVSphericalMapping *spherical = (const AVSphericalMapping *) av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL, NULL); if (spherical) { #endif return av_spherical_projection_name(spherical->projection); } return NULL; } #include static double get_rotation(mlt_properties properties, AVStream *st) { AVDictionaryEntry *rotate_tag = av_dict_get(st->metadata, "rotate", NULL, 0); int has_rotate_metadata = rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0"); #if LIBAVCODEC_VERSION_INT >= ((60 << 16) + (29 << 8) + 100) int32_t *displaymatrix = NULL; const AVPacketSideData *psd = av_packet_side_data_get(st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data, AV_PKT_DATA_DISPLAYMATRIX); if (psd) displaymatrix = (int32_t *) psd->data; #else uint8_t *displaymatrix = av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL); #endif double theta = mlt_properties_get_double(properties, "rotate"); int has_mlt_rotate = mlt_properties_exists(properties, "rotate"); if (has_rotate_metadata && !has_mlt_rotate) { char *tail; theta = strtod(rotate_tag->value, &tail); if (*tail) { // invalid theta = 0; has_rotate_metadata = 0; } } if (displaymatrix && !has_rotate_metadata && !has_mlt_rotate) { theta = -av_display_rotation_get((int32_t *) displaymatrix); } theta -= 360 * floor(theta / 360 + 0.9 / 360); return theta; } static char *filter_restricted(const char *in) { if (!in) return NULL; size_t n = strlen(in); // https://github.com/bminor/glibc/commit/9bcd12d223a8990254b65e2dada54faa5d2742f3 char *out = calloc(n + MB_CUR_MAX, 1); char *p = out; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); while (*in) { wchar_t w; size_t c = mbrtowc(&w, in, n, &mbs); if (c <= 0 || c > n) break; n -= c; in += c; if (w == 0x9 || w == 0xA || w == 0xD || (w >= 0x20 && w <= 0xD7FF) || (w >= 0xE000 && w <= 0xFFFD) || (w >= 0x10000 && w <= 0x10FFFF)) { mbstate_t ps; memset(&ps, 0, sizeof(ps)); c = wcrtomb(p, w, &ps); if (c > 0) p += c; } } return out; } static AVRational guess_frame_rate(producer_avformat self, AVStream *stream) { AVRational frame_rate = av_guess_frame_rate(self->video_format, stream, NULL); double fps = av_q2d(frame_rate); // Verify and sanitize the frame rate. if (isnan(fps) || isinf(fps) || fps == 0) { frame_rate = stream->avg_frame_rate; fps = av_q2d(frame_rate); } // With my samples when r_frame_rate != 1000 but avg_frame_rate is valid, // avg_frame_rate gives some approximate value that does not well match the media. // Also, on my sample where r_frame_rate = 1000, using avg_frame_rate directly // results in some very choppy output, but some value slightly different works // great. if (av_q2d(stream->r_frame_rate) >= 1000 && av_q2d(stream->avg_frame_rate) > 0) { frame_rate = av_d2q(av_q2d(stream->avg_frame_rate), 1024); fps = av_q2d(frame_rate); } if (isnan(fps) || isinf(fps) || fps < 1.0) { // Use the profile frame rate if all else fails. mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(self->parent)); frame_rate.num = profile->frame_rate_num; frame_rate.den = profile->frame_rate_den; } // Normalize broadcast frame rates for Matroska if (self->video_format->iformat->name && strstr(self->video_format->iformat->name, "matroska")) { switch (lrint(100000.0 * frame_rate.num / frame_rate.den)) { case 2997003: frame_rate.num = 30000; frame_rate.den = 1001; break; case 5994006: frame_rate.num = 60000; frame_rate.den = 1001; break; case 2397602: frame_rate.num = 24000; frame_rate.den = 1001; break; case 4795204: frame_rate.num = 48000; frame_rate.den = 1001; break; default: break; } } fps = mlt_properties_get_double(MLT_PRODUCER_PROPERTIES(self->parent), "meta.attr.com.android.capture.fps.markup"); if (fps > 0.0 && isfinite(fps)) frame_rate = av_d2q(fps, 1024); return frame_rate; } /** Find the default streams. */ static mlt_properties find_default_streams(producer_avformat self) { unsigned int i; char key[200]; AVDictionaryEntry *tag = NULL; AVFormatContext *context = self->video_format; mlt_properties meta_media = MLT_PRODUCER_PROPERTIES(self->parent); // Default to the first audio and video streams found self->audio_index = -1; int first_video_index = self->video_index = -1; mlt_properties_set_int(meta_media, "meta.media.nb_streams", context->nb_streams); // Allow for multiple audio and video streams in the file and select first of each (if available) for (i = 0; i < context->nb_streams; i++) { // Get the codec context AVFormatContext *vpx_context = NULL; AVStream *stream = context->streams[i]; if (!stream) continue; AVCodecParameters *codec_params = stream->codecpar; const AVCodec *codec = avcodec_find_decoder(codec_params->codec_id); if (!codec) continue; int switch_to_vpx = 0; if (codec_params->codec_id == AV_CODEC_ID_VP9) { if (!(codec = avcodec_find_decoder_by_name("libvpx-vp9"))) { codec = avcodec_find_decoder(codec_params->codec_id); } else { switch_to_vpx = 1; } } else if (codec_params->codec_id == AV_CODEC_ID_VP8) { if (!(codec = avcodec_find_decoder_by_name("libvpx"))) { codec = avcodec_find_decoder(codec_params->codec_id); } else { switch_to_vpx = 1; } } if (switch_to_vpx) { // Use a temporary format context to get the real pixel format with the libvpx decoder, // since the native decoder incorreclty detects yuva420p as yuv420p int error = avformat_open_input(&vpx_context, mlt_properties_get(meta_media, "resource"), NULL, NULL); if (!error) { vpx_context->video_codec = codec; avformat_find_stream_info(vpx_context, NULL); AVStream *vpx_stream = vpx_context->streams[i]; if (vpx_stream) { codec_params = vpx_stream->codecpar; } } } snprintf(key, sizeof(key), "meta.media.%u.stream.type", i); // Determine the type and obtain the first index of each type switch (codec_params->codec_type) { case AVMEDIA_TYPE_VIDEO: // Save the first video stream if (first_video_index < 0) first_video_index = i; // Only set the video stream if not album art if (self->video_index < 0 && !(context->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC)) { self->video_index = i; } mlt_properties_set(meta_media, key, "video"); snprintf(key, sizeof(key), "meta.media.%u.stream.frame_rate", i); AVRational frame_rate = guess_frame_rate(self, context->streams[i]); mlt_properties_set_double(meta_media, key, av_q2d(frame_rate)); const char *projection = get_projection(context->streams[i]); if (projection) { snprintf(key, sizeof(key), "meta.media.%u.stream.projection", i); mlt_properties_set_string(meta_media, key, projection); } snprintf(key, sizeof(key), "meta.media.%u.stream.sample_aspect_ratio", i); mlt_properties_set_double(meta_media, key, av_q2d(context->streams[i]->sample_aspect_ratio)); snprintf(key, sizeof(key), "meta.media.%u.codec.width", i); mlt_properties_set_int(meta_media, key, codec_params->width); snprintf(key, sizeof(key), "meta.media.%u.codec.height", i); mlt_properties_set_int(meta_media, key, codec_params->height); snprintf(key, sizeof(key), "meta.media.%u.codec.rotate", i); mlt_properties_set_int(meta_media, key, get_rotation(NULL, context->streams[i])); // snprintf( key, sizeof(key), "meta.media.%u.codec.frame_rate", i ); // AVRational frame_rate = { codec_context->time_base.den, codec_context->time_base.num * codec_context->ticks_per_frame }; // mlt_properties_set_double( meta_media, key, av_q2d( frame_rate ) ); snprintf(key, sizeof(key), "meta.media.%u.codec.pix_fmt", i); mlt_properties_set(meta_media, key, av_get_pix_fmt_name(codec_params->format)); snprintf(key, sizeof(key), "meta.media.%u.codec.sample_aspect_ratio", i); mlt_properties_set_double(meta_media, key, av_q2d(codec_params->sample_aspect_ratio)); snprintf(key, sizeof(key), "meta.media.%u.codec.colorspace", i); mlt_colorspace colorspace = av_to_mlt_colorspace(codec_params->color_space, codec_params->width, codec_params->height); mlt_properties_set_int(meta_media, key, colorspace); if (codec_params->color_trc && codec_params->color_trc != AVCOL_TRC_UNSPECIFIED) { snprintf(key, sizeof(key), "meta.media.%u.codec.color_trc", i); mlt_color_trc trc = av_to_mlt_color_trc(codec_params->color_trc); mlt_properties_set_int(meta_media, key, trc); } break; case AVMEDIA_TYPE_AUDIO: #if HAVE_FFMPEG_CH_LAYOUT if (!codec_params->ch_layout.nb_channels) #else if (!codec_params->channels) #endif break; // Use first audio stream if (self->audio_index < 0 && pick_audio_format(codec_params->format) != mlt_audio_none) self->audio_index = i; mlt_properties_set(meta_media, key, "audio"); snprintf(key, sizeof(key), "meta.media.%u.codec.sample_fmt", i); mlt_properties_set(meta_media, key, av_get_sample_fmt_name(codec_params->format)); snprintf(key, sizeof(key), "meta.media.%u.codec.sample_rate", i); mlt_properties_set_int(meta_media, key, codec_params->sample_rate); snprintf(key, sizeof(key), "meta.media.%u.codec.channels", i); mlt_channel_layout mlt_layout = mlt_channel_independent; #if HAVE_FFMPEG_CH_LAYOUT mlt_properties_set_int(meta_media, key, codec_params->ch_layout.nb_channels); if (av_channel_layout_check(&codec_params->ch_layout)) { mlt_layout = av_channel_layout_to_mlt(&codec_params->ch_layout); } else { AVChannelLayout ch_layout; av_channel_layout_default(&ch_layout, codec_params->ch_layout.nb_channels); mlt_layout = av_channel_layout_to_mlt(&ch_layout); av_channel_layout_uninit(&ch_layout); } #else mlt_properties_set_int(meta_media, key, codec_params->channels); if (codec_params->channel_layout == 0) mlt_layout = av_channel_layout_to_mlt( av_get_default_channel_layout(codec_params->channels)); else mlt_layout = av_channel_layout_to_mlt(codec_params->channel_layout); #endif const char *layout = mlt_audio_channel_layout_name(mlt_layout); snprintf(key, sizeof(key), "meta.media.%u.codec.layout", i); mlt_properties_set(meta_media, key, layout); break; case AVMEDIA_TYPE_SUBTITLE: mlt_properties_set(meta_media, key, "subtitle"); break; default: break; } // snprintf( key, sizeof(key), "meta.media.%u.stream.time_base", i ); // mlt_properties_set_double( meta_media, key, av_q2d( context->streams[ i ]->time_base ) ); snprintf(key, sizeof(key), "meta.media.%u.codec.name", i); mlt_properties_set(meta_media, key, codec->name); snprintf(key, sizeof(key), "meta.media.%u.codec.long_name", i); mlt_properties_set(meta_media, key, codec->long_name); snprintf(key, sizeof(key), "meta.media.%u.codec.bit_rate", i); mlt_properties_set_int64(meta_media, key, codec_params->bit_rate); // snprintf( key, sizeof(key), "meta.media.%u.codec.time_base", i ); // mlt_properties_set_double( meta_media, key, av_q2d( codec_context->time_base ) ); // snprintf( key, sizeof(key), "meta.media.%u.codec.profile", i ); // mlt_properties_set_int( meta_media, key, codec_context->profile ); // snprintf( key, sizeof(key), "meta.media.%u.codec.level", i ); // mlt_properties_set_int( meta_media, key, codec_context->level ); // Read Metadata while ((tag = av_dict_get(stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { if (tag->value && strcmp(tag->value, "") && strcmp(tag->value, "und")) { snprintf(key, sizeof(key), "meta.attr.%u.stream.%s.markup", i, tag->key); char *value = filter_restricted(tag->value); mlt_properties_set(meta_media, key, value); free(value); } } if (vpx_context) { avformat_close_input(&vpx_context); vpx_context = NULL; } } // Use the album art if that is all we have if (self->video_index < 0 && first_video_index >= 0) self->video_index = first_video_index; while ((tag = av_dict_get(context->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { if (tag->value && strcmp(tag->value, "") && strcmp(tag->value, "und")) { snprintf(key, sizeof(key), "meta.attr.%s.markup", tag->key); char *value = filter_restricted(tag->value); mlt_properties_set(meta_media, key, value); free(value); } } return meta_media; } static void get_aspect_ratio(mlt_properties properties, AVStream *stream, AVCodecParameters *codec_params) { AVRational sar = stream->sample_aspect_ratio; if (sar.num <= 0 || sar.den <= 0) sar = codec_params->sample_aspect_ratio; if (sar.num <= 0 || sar.den <= 0) sar.num = sar.den = 1; mlt_properties_set_int(properties, "meta.media.sample_aspect_num", sar.num); mlt_properties_set_int(properties, "meta.media.sample_aspect_den", sar.den); mlt_properties_set_double(properties, "aspect_ratio", av_q2d(sar)); } static char *parse_url(mlt_profile profile, const char *URL, const AVInputFormat **format, AVDictionary **params) { (void) profile; // unused if (!URL) return NULL; char *protocol = strdup(URL); char *url = strchr(protocol, ':'); // Truncate protocol string if (url && (url - protocol) > 1 && avio_check(URL, 0) < 0) { // if defined and not a drive letter url[0] = '\0'; ++url; mlt_log_debug(NULL, "%s: protocol=%s resource=%s\n", __FUNCTION__, protocol, url); // Lookup the format *format = av_find_input_format(protocol); } else { url = protocol; } // Eat the format designator char *result = url; // support for legacy width and height parameters char *width = NULL; char *height = NULL; // Parse out params char *query = strchr(url, '?'); if (*format) { // Query string delimiter is '?' url = (query && query > url && query[-1] != '\\') ? query : NULL; } else { // Ignore unescaped question marks while (query && query > url && query[-1] != '\\') { query = strchr(query + 1, '?'); } // Query string delimiter is '\?' url = (query && query > url && query[-1] == '\\') ? query : NULL; if (url) url[-1] = '\0'; // null the backslash } while (url) { url[0] = '\0'; char *name = strdup(++url); char *value = strchr(name, '='); if (!value) // Also accept : as delimiter for backwards compatibility. value = strchr(name, ':'); if (value) { value[0] = '\0'; value++; char *t = strchr(value, '&'); if (t) t[0] = 0; // translate old parameters to new av_dict names if (!strcmp(name, "frame_rate")) av_dict_set(params, "framerate", value, 0); else if (!strcmp(name, "pix_fmt")) av_dict_set(params, "pixel_format", value, 0); else if (!strcmp(name, "width")) width = strdup(value); else if (!strcmp(name, "height")) height = strdup(value); else // generic demux/device option support av_dict_set(params, name, value, 0); } free(name); url = strchr(url, '&'); } // continued support for legacy width and height parameters if (width && height) { char *s = malloc(strlen(width) + strlen(height) + 2); strcpy(s, width); strcat(s, "x"); strcat(s, height); av_dict_set(params, "video_size", s, 0); free(s); } free(width); free(height); result = strdup(result); free(protocol); mlt_log_debug(NULL, "[producer avformat] %s filename = %s\n", __FUNCTION__, result); return result; } static enum AVPixelFormat pick_pix_fmt(enum AVPixelFormat pix_fmt) { switch (pix_fmt) { case AV_PIX_FMT_ARGB: case AV_PIX_FMT_RGBA: case AV_PIX_FMT_ABGR: case AV_PIX_FMT_BGRA: return AV_PIX_FMT_RGBA; case AV_PIX_FMT_BAYER_RGGB16LE: return AV_PIX_FMT_RGB24; case AV_PIX_FMT_VAAPI: case AV_PIX_FMT_CUDA: case AV_PIX_FMT_VIDEOTOOLBOX: case AV_PIX_FMT_DXVA2_VLD: case AV_PIX_FMT_D3D11: #if HAVE_FFMPEG_VULKAN case AV_PIX_FMT_VULKAN: #endif return AV_PIX_FMT_YUV420P; default: return AV_PIX_FMT_YUV422P; } } static mlt_image_format pick_image_format(enum AVPixelFormat pix_fmt, mlt_image_format current_format) { if (current_format == mlt_image_none || current_format == mlt_image_movit || pix_fmt == AV_PIX_FMT_ARGB || pix_fmt == AV_PIX_FMT_RGBA || pix_fmt == AV_PIX_FMT_ABGR || pix_fmt == AV_PIX_FMT_BGRA || pix_fmt == AV_PIX_FMT_GBRAP) { switch (pix_fmt) { case AV_PIX_FMT_ARGB: case AV_PIX_FMT_RGBA: case AV_PIX_FMT_ABGR: case AV_PIX_FMT_BGRA: case AV_PIX_FMT_GBRAP: return mlt_image_rgba; case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUVJ420P: case AV_PIX_FMT_YUVA420P: return mlt_image_yuv420p; case AV_PIX_FMT_RGB24: case AV_PIX_FMT_BGR24: case AV_PIX_FMT_GRAY8: case AV_PIX_FMT_MONOWHITE: case AV_PIX_FMT_MONOBLACK: case AV_PIX_FMT_RGB8: case AV_PIX_FMT_BGR8: case AV_PIX_FMT_BAYER_RGGB16LE: return mlt_image_rgb; case AV_PIX_FMT_YUV420P10LE: return mlt_image_yuv420p10; case AV_PIX_FMT_YUV422P10LE: case AV_PIX_FMT_YUV444P10LE: return mlt_image_yuv444p10; case AV_PIX_FMT_YUV422P16LE: return mlt_image_yuv422p16; case AV_PIX_FMT_RGBA64LE: return mlt_image_rgba64; default: current_format = mlt_image_yuv422; } } if (pix_fmt == AV_PIX_FMT_BAYER_RGGB16LE) { return mlt_image_rgb; } else if (pix_fmt == AV_PIX_FMT_YUVA444P10LE || pix_fmt == AV_PIX_FMT_YUVA444P12LE || pix_fmt == AV_PIX_FMT_GBRAP10LE || pix_fmt == AV_PIX_FMT_GBRAP12LE) { return mlt_image_rgba; } return current_format; } static int get_basic_info(producer_avformat self, mlt_profile profile, const char *filename) { int error = 0; // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES(self->parent); AVFormatContext *format = self->video_format; // Get the duration if (mlt_properties_get_position(properties, "length") <= 0 || mlt_properties_get_position(properties, "out") <= 0) { if (format->duration != AV_NOPTS_VALUE) { // This isn't going to be accurate for all formats // We will treat everything with the producer fps. mlt_position frames = (mlt_position) lrint(format->duration * mlt_profile_fps(profile) / AV_TIME_BASE); if (mlt_properties_get_position(properties, "out") <= 0) mlt_properties_set_position(properties, "out", frames - 1); if (mlt_properties_get_position(properties, "length") <= 0) mlt_properties_set_position(properties, "length", frames); } else if (format->nb_streams > 0 && format->streams[0]->codecpar && format->streams[0]->codecpar->codec_id == AV_CODEC_ID_WEBP) { char *e = getenv("MLT_DEFAULT_PRODUCER_LENGTH"); int p = e ? atoi(e) : 15000; mlt_properties_set_int(properties, "out", MAX(0, p - 1)); mlt_properties_set_int(properties, "length", p); } else { // Set live sources to run forever if (mlt_properties_get_position(properties, "length") <= 0) mlt_properties_set_position(properties, "length", INT_MAX); if (mlt_properties_get_position(properties, "out") <= 0) mlt_properties_set_position(properties, "out", INT_MAX - 1); mlt_properties_set(properties, "eof", "loop"); } } // Check if we're seekable // avdevices are typically AVFMT_NOFILE and not seekable self->seekable = !format->iformat || !(format->iformat->flags & AVFMT_NOFILE); if (format->pb) { // protocols can indicate if they support seeking self->seekable = format->pb->seekable; } if (self->seekable) { // Do a more rigorous test of seekable on a disposable context if (format->nb_streams > 0 && format->streams[0]->codecpar && format->streams[0]->codecpar->codec_id != AV_CODEC_ID_WEBP) self->seekable = av_seek_frame(format, -1, format->start_time, AVSEEK_FLAG_BACKWARD) >= 0; mlt_properties_set_int(properties, "seekable", self->seekable); self->dummy_context = format; self->video_format = NULL; avformat_open_input(&self->video_format, filename, NULL, NULL); avformat_find_stream_info(self->video_format, NULL); format = self->video_format; } self->video_seekable = self->seekable; // Fetch the width, height and aspect ratio if (self->video_index != -1) { AVCodecParameters *codec_params = format->streams[self->video_index]->codecpar; mlt_properties_set_int(properties, "width", codec_params->width); mlt_properties_set_int(properties, "height", codec_params->height); get_aspect_ratio(properties, format->streams[self->video_index], codec_params); int pix_fmt = self->vfilter_out ? av_buffersink_get_format(self->vfilter_out) : self->hwaccel.filter_out ? av_buffersink_get_format(self->hwaccel.filter_out) : codec_params->format; pick_av_pixel_format(&pix_fmt, self->full_range); if (pix_fmt != AV_PIX_FMT_NONE) { // Verify that we can convert this to one of our image formats. struct SwsContext *context = sws_getContext(codec_params->width, codec_params->height, pix_fmt, codec_params->width, codec_params->height, pick_pix_fmt(pix_fmt), SWS_BILINEAR, NULL, NULL, NULL); if (context) { sws_freeContext(context); mlt_image_format format = pick_image_format(pix_fmt, mlt_image_yuv422); mlt_properties_set_int(properties, "format", format); } else error = 1; } else { self->video_index = -1; } } return error; } static int setup_hwaccel_filters(producer_avformat self, mlt_producer producer, const char *filter_name, double scale_factor) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); AVStream *stream = self->video_format->streams[self->video_index]; AVRational frame_rate = guess_frame_rate(self, stream); // Get the actual hardware texture dimensions from hw_frames_ctx // hwaccel may pad these beyond the logical width/height for alignment int hw_width = self->video_frame->width; int hw_height = self->video_frame->height; if (self->video_frame->hw_frames_ctx) { AVHWFramesContext *hw_frames_ctx = (AVHWFramesContext *) self->video_frame->hw_frames_ctx->data; hw_width = hw_frames_ctx->width; hw_height = hw_frames_ctx->height; } int original_width = self->video_frame->width; int original_height = self->video_frame->height; int scaled_width = (int) (original_width * scale_factor); int scaled_height = (int) (original_height * scale_factor); char scale_args[64]; scaled_width += scaled_width % 2; scaled_height += scaled_height % 2; mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "hwaccel scale filter setup: input %dx%d (hw: %dx%d), scale %.3f, target %dx%d\n", original_width, original_height, hw_width, hw_height, scale_factor, scaled_width, scaled_height); if (self->hwaccel.pix_fmt == AV_PIX_FMT_D3D11) { snprintf(scale_args, sizeof(scale_args), "width=%d:height=%d:format=%s", scaled_width, scaled_height, av_get_pix_fmt_name(AV_PIX_FMT_NV12)); // scale_d3d11 source shows support for AV_PIX_FMT_P010 as well, but that // is generating an error for me: Could not create the texture (80070057) } else { snprintf(scale_args, sizeof(scale_args), "w=%d:h=%d", scaled_width, scaled_height); } mlt_log_verbose(MLT_PRODUCER_SERVICE(producer), "Attempting to set up hwaccel filter %s: %dx%d -> %dx%d\n", filter_name, self->video_frame->width, self->video_frame->height, scaled_width, scaled_height); self->hwaccel.filter_graph = avfilter_graph_alloc(); if (!self->hwaccel.filter_graph) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to allocate filter graph\n"); return -1; } // Create buffer source with hardware frames context const AVFilter *buffersrc = avfilter_get_by_name("buffer"); self->hwaccel.filter_in = avfilter_graph_alloc_filter(self->hwaccel.filter_graph, buffersrc, "hw_buffer"); if (!self->hwaccel.filter_in) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to allocate buffer source\n"); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; return -1; } // Set options on the filter context int ret = av_opt_set_int(self->hwaccel.filter_in, "width", hw_width, AV_OPT_SEARCH_CHILDREN); if (ret >= 0) ret = av_opt_set_int(self->hwaccel.filter_in, "height", hw_height, AV_OPT_SEARCH_CHILDREN); if (ret >= 0) ret = av_opt_set_pixel_fmt(self->hwaccel.filter_in, "pix_fmt", self->video_frame->format, AV_OPT_SEARCH_CHILDREN); if (ret >= 0) ret = av_opt_set_q(self->hwaccel.filter_in, "time_base", stream->time_base, AV_OPT_SEARCH_CHILDREN); if (ret >= 0) ret = av_opt_set_q(self->hwaccel.filter_in, "frame_rate", frame_rate, AV_OPT_SEARCH_CHILDREN); AVRational sar; sar.num = mlt_properties_get_int(properties, "meta.media.sample_aspect_num"); sar.den = FFMAX(mlt_properties_get_int(properties, "meta.media.sample_aspect_den"), 1); if (ret >= 0) ret = av_opt_set_q(self->hwaccel.filter_in, "sar", sar, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to set buffer source options: %d\n", ret); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; return ret; } // Set hw_frames_ctx before initializing AVBufferSrcParameters *params = av_buffersrc_parameters_alloc(); if (!params) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to allocate buffer src params\n"); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; return -1; } params->hw_frames_ctx = av_buffer_ref(self->video_frame->hw_frames_ctx); ret = av_buffersrc_parameters_set(self->hwaccel.filter_in, params); av_buffer_unref(¶ms->hw_frames_ctx); av_free(params); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to set hw_frames_ctx: %d\n", ret); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; return ret; } // Initialize the filter ret = avfilter_init_str(self->hwaccel.filter_in, NULL); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to initialize buffer source: %d\n", ret); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; return ret; } // Create scale filter const AVFilter *scale_filter = avfilter_get_by_name(filter_name); if (!scale_filter) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "%s filter not available\n", filter_name); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; return -1; } AVFilterContext *scale_ctx = NULL; ret = avfilter_graph_create_filter(&scale_ctx, scale_filter, filter_name, scale_args, NULL, self->hwaccel.filter_graph); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to create %s filter: %d\n", filter_name, ret); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; return ret; } // Create buffer sink const AVFilter *buffersink = avfilter_get_by_name("buffersink"); ret = avfilter_graph_create_filter(&self->hwaccel.filter_out, buffersink, "hw_buffersink", NULL, NULL, self->hwaccel.filter_graph); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to create buffer sink filter: %d\n", ret); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; self->hwaccel.filter_out = NULL; return ret; } // Link filters: buffer -> scale -> buffersink ret = avfilter_link(self->hwaccel.filter_in, 0, scale_ctx, 0); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to link buffer to scale: %d\n", ret); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; self->hwaccel.filter_out = NULL; return ret; } ret = avfilter_link(scale_ctx, 0, self->hwaccel.filter_out, 0); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to link scale to buffersink: %d\n", ret); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; self->hwaccel.filter_out = NULL; return ret; } // Configure the filter graph ret = avfilter_graph_config(self->hwaccel.filter_graph, NULL); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to configure filter graph: %d\n", ret); avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; self->hwaccel.filter_out = NULL; return ret; } mlt_log_info(MLT_PRODUCER_SERVICE(producer), "%s filter initialized: %dx%d -> %dx%d\n", filter_name, self->video_frame->width, self->video_frame->height, scaled_width, scaled_height); return 0; } static void try_setup_hwaccel_filters(producer_avformat self, mlt_producer producer, double consumer_scale) { if (self->hwaccel.filters_initialized || self->hwaccel.filter_graph) return; // Check for D3D11 texture padding with HEVC - this specific combination produces black bars if (self->hwaccel.pix_fmt == AV_PIX_FMT_D3D11 && self->video_codec && self->video_codec->codec_id == AV_CODEC_ID_HEVC && self->video_frame && self->video_frame->hw_frames_ctx) { AVHWFramesContext *hw_frames_ctx = (AVHWFramesContext *) self->video_frame->hw_frames_ctx->data; if (hw_frames_ctx->height > self->video_frame->height) { mlt_log_verbose(MLT_PRODUCER_SERVICE(producer), "D3D11 texture padding detected with HEVC (%dx%d vs logical %dx%d), " "skipping hwaccel filter\n", hw_frames_ctx->width, hw_frames_ctx->height, self->video_frame->width, self->video_frame->height); self->hwaccel.filters_initialized = 1; // Mark as "initialized" to skip future attempts return; } } const char *filter_name = NULL; switch (self->hwaccel.pix_fmt) { #if HAVE_FFMPEG_VULKAN case AV_PIX_FMT_VULKAN: filter_name = "scale_vulkan"; break; #endif #if LIBAVFILTER_VERSION_INT >= ((11 << 16) + (4 << 8) + 100) case AV_PIX_FMT_D3D11: filter_name = "scale_d3d11"; break; #endif case AV_PIX_FMT_VAAPI: filter_name = "scale_vaapi"; break; case AV_PIX_FMT_VIDEOTOOLBOX: filter_name = "scale_vt"; break; default: return; } if (filter_name) { setup_hwaccel_filters(self, producer, filter_name, consumer_scale); self->hwaccel.filters_initialized = 1; } } static int apply_hwaccel_filters(producer_avformat self, mlt_producer producer) { if (!self->hwaccel.filter_in || !self->hwaccel.filter_out) { return -1; } int ret = av_buffersrc_add_frame(self->hwaccel.filter_in, self->video_frame); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to add frame to filter: %d\n", ret); // Disable filter on error to prevent repeated failures avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; self->hwaccel.filter_out = NULL; return ret; } av_frame_unref(self->video_frame); ret = av_buffersink_get_frame(self->hwaccel.filter_out, self->video_frame); if (ret < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Failed to get filtered frame: %d\n", ret); // Disable filter on error to prevent repeated failures avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; self->hwaccel.filter_out = NULL; } return ret; } static int setup_video_filters(producer_avformat self) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self->parent); AVFormatContext *format = self->video_format; AVStream *stream = format->streams[self->video_index]; AVCodecParameters *codec_params = stream->codecpar; int width = codec_params->width; int height = codec_params->height; int pix_fmt = codec_params->format; if (self->video_frame) { width = self->video_frame->width; height = self->video_frame->height; pix_fmt = self->video_frame->format; } self->vfilter_graph = avfilter_graph_alloc(); // From ffplay.c:configure_video_filters(). char buffersrc_args[256]; AVRational frame_rate = guess_frame_rate(self, stream); snprintf(buffersrc_args, sizeof(buffersrc_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:frame_rate=%d/%d", width, height, pix_fmt, stream->time_base.num, stream->time_base.den, mlt_properties_get_int(properties, "meta.media.sample_aspect_num"), FFMAX(mlt_properties_get_int(properties, "meta.media.sample_aspect_den"), 1), frame_rate.num, FFMAX(frame_rate.den, 1)); int result = avfilter_graph_create_filter(&self->vfilter_in, avfilter_get_by_name("buffer"), "mlt_buffer", buffersrc_args, NULL, self->vfilter_graph); if (result >= 0) { result = avfilter_graph_create_filter(&self->vfilter_out, avfilter_get_by_name("buffersink"), "mlt_buffersink", NULL, NULL, self->vfilter_graph); } return result; } static int insert_filter(AVFilterGraph *graph, AVFilterContext **last_filter, const char *name, const char *args) { AVFilterContext *filt_ctx; int result = avfilter_graph_create_filter(&filt_ctx, avfilter_get_by_name(name), name, args, NULL, graph); if (result >= 0) { result = avfilter_link(filt_ctx, 0, *last_filter, 0); if (result >= 0) *last_filter = filt_ctx; } return result; } static int setup_filters(producer_avformat self) { int error = 0; mlt_properties properties = MLT_PRODUCER_PROPERTIES(self->parent); const char *filtergraph = mlt_properties_get(properties, "filtergraph"); const char *lut = mlt_properties_get(properties, "lut"); int use_lut = lut && *lut; double theta = 0.0; if (self->video_index != -1 && self->autorotate) { theta = get_rotation(properties, self->video_format->streams[self->video_index]); if (self->vfilter_graph && theta != self->rotation) { // The rotation has changed. Force the filter graph to be rebuilt avfilter_graph_free(&self->vfilter_graph); self->vfilter_in = NULL; self->vfilter_out = NULL; self->rotation = theta; } } if (!self->vfilter_graph && (self->autorotate || filtergraph || use_lut) && self->video_index != -1) { AVFilterContext *last_filter = NULL; if (self->autorotate) { if (fabs(theta - 90) < 1.0) { error = (setup_video_filters(self) < 0); last_filter = self->vfilter_out; if (!error) error = (insert_filter(self->vfilter_graph, &last_filter, "transpose", "clock") < 0); } else if (fabs(theta - 180) < 1.0) { error = (setup_video_filters(self) < 0); last_filter = self->vfilter_out; if (!error) error = (insert_filter(self->vfilter_graph, &last_filter, "hflip", NULL) < 0); if (!error) error = (insert_filter(self->vfilter_graph, &last_filter, "vflip", NULL) < 0); } else if (fabs(theta - 270) < 1.0) { error = (setup_video_filters(self) < 0); last_filter = self->vfilter_out; if (!error) error = (insert_filter(self->vfilter_graph, &last_filter, "transpose", "cclock") < 0); } } if (use_lut && !error) { if (!self->vfilter_graph) { error = (setup_video_filters(self) < 0); last_filter = self->vfilter_out; } else if (!last_filter) { last_filter = self->vfilter_out; } AVBPrint buf; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); av_bprintf(&buf, "file="); av_bprint_escape(&buf, lut, "\\:',;[]", AV_ESCAPE_MODE_BACKSLASH, 0); char *lut3d_args = NULL; av_bprint_finalize(&buf, &lut3d_args); if (!lut3d_args) error = 1; if (!error) error = (insert_filter(self->vfilter_graph, &last_filter, "lut3d", lut3d_args) < 0); av_free(lut3d_args); } if (filtergraph && !error) { if (!self->vfilter_graph) { error = (setup_video_filters(self) < 0); last_filter = self->vfilter_out; } else if (!last_filter) { last_filter = self->vfilter_out; } AVFilterInOut *outputs = avfilter_inout_alloc(); AVFilterInOut *inputs = avfilter_inout_alloc(); outputs->name = av_strdup("in"); outputs->filter_ctx = self->vfilter_in; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); inputs->filter_ctx = last_filter; inputs->pad_idx = 0; inputs->next = NULL; if (!error) error = (avfilter_graph_parse(self->vfilter_graph, filtergraph, inputs, outputs, NULL) < 0); } if (self->vfilter_graph) { if (!error && !filtergraph) error = (avfilter_link(self->vfilter_in, 0, last_filter, 0) < 0); if (!error) error = (avfilter_graph_config(self->vfilter_graph, NULL) < 0); } } if (error && self->vfilter_graph) { avfilter_graph_free(&self->vfilter_graph); self->vfilter_in = NULL; self->vfilter_out = NULL; } return error; } static void set_up_discard(producer_avformat self, int audio_index, int video_index) { // The open_mutex must be locked when this function is called if (self->audio_format) { for (int x = 0; x < self->audio_format->nb_streams; x++) { if (audio_index == INT_MAX || x == audio_index || (self->audio_format == self->video_format && x == video_index)) self->audio_format->streams[x]->discard = AVDISCARD_DEFAULT; else self->audio_format->streams[x]->discard = AVDISCARD_ALL; } } if (self->video_format && self->video_format != self->audio_format) { for (int x = 0; x < self->video_format->nb_streams; x++) { if (x == video_index) self->video_format->streams[x]->discard = AVDISCARD_DEFAULT; else self->video_format->streams[x]->discard = AVDISCARD_ALL; } } } /** Open the file. */ static int producer_open( producer_avformat self, mlt_profile profile, const char *URL, int take_lock, int test_open) { // Return an error code (0 == no error) int error = 0; mlt_properties properties = MLT_PRODUCER_PROPERTIES(self->parent); if (!self->is_mutex_init) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&self->audio_mutex, &attr); pthread_mutex_init(&self->video_mutex, &attr); pthread_mutex_init(&self->packets_mutex, &attr); pthread_mutex_init(&self->open_mutex, &attr); pthread_mutex_init(&self->close_mutex, &attr); self->is_mutex_init = 1; } // Lock the service if (take_lock) { pthread_mutex_lock(&self->audio_mutex); pthread_mutex_lock(&self->video_mutex); } mlt_events_block(properties, self->parent); // Parse URL const AVInputFormat *format = NULL; AVDictionary *params = NULL; char *filename = parse_url(profile, URL, &format, ¶ms); // Now attempt to open the file or device with filename error = avformat_open_input(&self->video_format, filename, format, ¶ms) < 0; if (error) // If the URL is a network stream URL, then we probably need to open with full URL error = avformat_open_input(&self->video_format, URL, format, ¶ms) < 0; // Set MLT properties onto video AVFormatContext if (!error && self->video_format) { apply_properties(self->video_format, properties, AV_OPT_FLAG_DECODING_PARAM); if (self->video_format->iformat && self->video_format->iformat->priv_class && self->video_format->priv_data) apply_properties(self->video_format->priv_data, properties, AV_OPT_FLAG_DECODING_PARAM); } // If successful, then try to get additional info if (!error && self->video_format) { // Get the stream info error = avformat_find_stream_info(self->video_format, NULL) < 0; // Continue if no error if (!error && self->video_format) { // Find default audio and video streams find_default_streams(self); error = get_basic_info(self, profile, filename); // Initialize position info self->first_pts = AV_NOPTS_VALUE; self->last_position = POSITION_INITIAL; AVDictionaryEntry *hwaccel = av_dict_get(params, "hwaccel", NULL, 0); AVDictionaryEntry *hwaccel_device = av_dict_get(params, "hwaccel_device", NULL, 0); const char *hwaccel_env = getenv("MLT_AVFORMAT_HWACCEL"); const char *pps_env = getenv("MLT_AVFORMAT_HWACCEL_PPS"); if (pps_env && self->video_index >= 0 && !test_open) { int64_t pps_threshold = strtoll(pps_env, NULL, 10); if (pps_threshold > 0) { // Calculate PPS while avoiding overflow AVStream *stream = self->video_format->streams[self->video_index]; int64_t width = stream->codecpar->width; int64_t height = stream->codecpar->height; double fps = av_q2d(guess_frame_rate(self, stream)); if (width > 0 && height > 0 && fps > 0.0) { // Calculate pixels per second using double to avoid overflow double pps = (double) width * (double) height * fps; if (pps > (double) pps_threshold) { mlt_log_verbose(MLT_PRODUCER_SERVICE(self->parent), "Disabling hwaccel: PPS %.0f exceeds threshold %lld\n", pps, (long long) pps_threshold); hwaccel = NULL; hwaccel_env = NULL; } } } } if (((hwaccel && hwaccel->value) || hwaccel_env) && !test_open) { // Leaving `device=NULL` will cause query string parameter `hwaccel_device` to be ignored char *device = getenv("MLT_AVFORMAT_HWACCEL_DEVICE"); if ((hwaccel && hwaccel->value && !strcmp(hwaccel->value, "vaapi")) || (hwaccel_env && !strcmp(hwaccel_env, "vaapi"))) { self->hwaccel.pix_fmt = AV_PIX_FMT_VAAPI; self->hwaccel.device_type = AV_HWDEVICE_TYPE_VAAPI; if (!device) device = "/dev/dri/renderD128"; } else if ((hwaccel && hwaccel->value && !strcmp(hwaccel->value, "cuda")) || (hwaccel && hwaccel->value && !strcmp(hwaccel->value, "nvdec")) || (hwaccel_env && !strcmp(hwaccel_env, "cuda")) || (hwaccel_env && !strcmp(hwaccel_env, "nvdec"))) { self->hwaccel.pix_fmt = AV_PIX_FMT_CUDA; self->hwaccel.device_type = AV_HWDEVICE_TYPE_CUDA; if (!device) device = "0"; } else if ((hwaccel && hwaccel->value && !strcmp(hwaccel->value, "videotoolbox")) || (hwaccel_env && !strcmp(hwaccel_env, "videotoolbox"))) { self->hwaccel.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX; self->hwaccel.device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX; } else if ((hwaccel && hwaccel->value && !strcmp(hwaccel->value, "d3d11va")) || (hwaccel_env && !strcmp(hwaccel_env, "d3d11va"))) { self->hwaccel.pix_fmt = AV_PIX_FMT_D3D11; self->hwaccel.device_type = AV_HWDEVICE_TYPE_D3D11VA; if (!device) device = "0"; } else if ((hwaccel && hwaccel->value && !strcmp(hwaccel->value, "dxva2")) || (hwaccel_env && !strcmp(hwaccel_env, "dxva2"))) { self->hwaccel.pix_fmt = AV_PIX_FMT_DXVA2_VLD; self->hwaccel.device_type = AV_HWDEVICE_TYPE_DXVA2; if (!device) device = "0"; #if HAVE_FFMPEG_VULKAN } else if ((hwaccel && hwaccel->value && !strcmp(hwaccel->value, "vulkan")) || (hwaccel_env && !strcmp(hwaccel_env, "vulkan"))) { self->hwaccel.pix_fmt = AV_PIX_FMT_VULKAN; self->hwaccel.device_type = AV_HWDEVICE_TYPE_VULKAN; if (!device) device = "0"; #endif } else { // TODO: init other hardware types } if (device) { if (hwaccel_device && hwaccel_device->value) device = hwaccel_device->value; memcpy(self->hwaccel.device, device, strlen(device)); } } if (!self->audio_format) { // We're going to cheat here - for seekable A/V files, we will have separate contexts // to support independent seeking of audio from video. // TODO: Is this really necessary? if (self->audio_index != -1 && self->video_index != -1) { if (self->seekable) { // And open again for our audio context avformat_open_input(&self->audio_format, filename, NULL, NULL); apply_properties(self->audio_format, properties, AV_OPT_FLAG_DECODING_PARAM); if (self->audio_format->iformat && self->audio_format->iformat->priv_class && self->audio_format->priv_data) apply_properties(self->audio_format->priv_data, properties, AV_OPT_FLAG_DECODING_PARAM); avformat_find_stream_info(self->audio_format, NULL); } else { self->audio_format = self->video_format; } } else if (self->audio_index != -1) { // We only have an audio context self->audio_format = self->video_format; self->video_format = NULL; } else if (self->video_index == -1) { // Something has gone wrong error = -1; } if (self->audio_format && !self->audio_streams) get_audio_streams_info(self); if (!test_open) { self->autorotate = !mlt_properties_get(properties, "autorotate") || mlt_properties_get_int(properties, "autorotate"); if (self->hwaccel.device_type == AV_HWDEVICE_TYPE_NONE) error = setup_filters(self); } } } } av_dict_free(¶ms); free(filename); if (!error) { self->apackets = mlt_deque_init(); self->vpackets = mlt_deque_init(); } if (self->dummy_context) { pthread_mutex_lock(&self->open_mutex); avformat_close_input(&self->dummy_context); self->dummy_context = NULL; pthread_mutex_unlock(&self->open_mutex); } // Unlock the service if (take_lock) { pthread_mutex_unlock(&self->audio_mutex); pthread_mutex_unlock(&self->video_mutex); } mlt_events_unblock(properties, self->parent); return error; } static void prepare_reopen(producer_avformat self) { mlt_service_lock(MLT_PRODUCER_SERVICE(self->parent)); pthread_mutex_lock(&self->audio_mutex); pthread_mutex_lock(&self->open_mutex); int i; for (i = 0; i < MAX_AUDIO_STREAMS; i++) { mlt_pool_release(self->audio_buffer[i]); self->audio_buffer[i] = NULL; av_free(self->decode_buffer[i]); self->decode_buffer[i] = NULL; avcodec_free_context(&self->audio_codec[i]); } avcodec_free_context(&self->video_codec); av_frame_unref(self->video_frame); av_buffer_unref(&self->hwaccel.device_ctx); self->hwaccel.device_ctx = NULL; if (self->seekable && self->audio_format) avformat_close_input(&self->audio_format); if (self->video_format) avformat_close_input(&self->video_format); self->audio_format = NULL; self->video_format = NULL; avfilter_graph_free(&self->vfilter_graph); self->vfilter_in = NULL; self->vfilter_out = NULL; avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; self->hwaccel.filter_out = NULL; self->hwaccel.filters_initialized = 0; pthread_mutex_unlock(&self->open_mutex); // Cleanup the packet queues AVPacket *pkt; if (self->apackets) { while ((pkt = mlt_deque_pop_back(self->apackets))) { av_packet_unref(pkt); free(pkt); } mlt_deque_close(self->apackets); self->apackets = NULL; } if (self->vpackets) { while ((pkt = mlt_deque_pop_back(self->vpackets))) { av_packet_unref(pkt); free(pkt); } mlt_deque_close(self->vpackets); self->vpackets = NULL; } pthread_mutex_unlock(&self->audio_mutex); mlt_service_unlock(MLT_PRODUCER_SERVICE(self->parent)); } static int64_t best_pts(producer_avformat self, int64_t pts, int64_t dts) { self->invalid_pts_counter += pts == AV_NOPTS_VALUE; self->invalid_dts_counter += dts == AV_NOPTS_VALUE; if ((self->invalid_pts_counter <= self->invalid_dts_counter || dts == AV_NOPTS_VALUE) && pts != AV_NOPTS_VALUE) return pts; else return dts; } static void find_first_pts(producer_avformat self, int video_index) { // find initial PTS AVFormatContext *context = self->video_format ? self->video_format : self->audio_format; int ret = 0; int pkt_countdown = 500; // check max 500 packets for first video keyframe PTS int vfr_countdown = 20; // check max 20 video frames for VFR int vfr_counter = 0; // counts the number of frame duration changes AVPacket pkt; int64_t prev_pkt_duration = AV_NOPTS_VALUE; av_init_packet(&pkt); if (video_index == -1 && self->audio_index >= 0) { // Special case for audio only files. while (ret >= 0 && pkt_countdown-- > 0 && (self->first_pts == AV_NOPTS_VALUE)) { ret = av_read_frame(context, &pkt); if (ret >= 0 && pkt.stream_index == self->audio_index) { mlt_log_debug(MLT_PRODUCER_SERVICE(self->parent), "first_pts %" PRId64 " dts %" PRId64 " pts_dts_delta %d\n", pkt.pts, pkt.dts, (int) (pkt.pts - pkt.dts)); self->first_pts = best_pts(self, pkt.pts, pkt.dts); } av_packet_unref(&pkt); } av_seek_frame(context, -1, 0, AVSEEK_FLAG_BACKWARD); return; } while (ret >= 0 && pkt_countdown-- > 0 && (self->first_pts == AV_NOPTS_VALUE || (vfr_counter < VFR_THRESHOLD && vfr_countdown > 0))) { ret = av_read_frame(context, &pkt); if (ret >= 0 && pkt.stream_index == video_index) { // Variable frame rate check if (pkt.duration != AV_NOPTS_VALUE && pkt.duration != prev_pkt_duration) { mlt_log_verbose(MLT_PRODUCER_SERVICE(self->parent), "checking VFR: pkt.duration %" PRId64 "\n", pkt.duration); if (prev_pkt_duration != AV_NOPTS_VALUE) ++vfr_counter; } prev_pkt_duration = pkt.duration; vfr_countdown--; // Finding PTS of first video key frame if ((pkt.flags & AV_PKT_FLAG_KEY) && self->first_pts == AV_NOPTS_VALUE) { mlt_log_debug(MLT_PRODUCER_SERVICE(self->parent), "first_pts %" PRId64 " dts %" PRId64 " pts_dts_delta %d\n", pkt.pts, pkt.dts, (int) (pkt.pts - pkt.dts)); if (pkt.dts != AV_NOPTS_VALUE && pkt.dts < 0) // Decoding Time Stamps with negative values are reported by ffmpeg code for // (at least) MP4 files containing h.264 video using b-frames. // For reasons not understood yet, the first PTS computed then is that of the // third frame, causing MLT to display the third frame as if it was the first. // This if-clause is meant to catch and work around this issue - if there is // a valid, but negative DTS value, we just guess that the first valid // Presentation Time Stamp is == 0. self->first_pts = 0; else self->first_pts = best_pts(self, pkt.pts, pkt.dts); } } av_packet_unref(&pkt); } // Determine if this is variable frame rate video int vfr = vfr_counter >= VFR_THRESHOLD; if (!vfr) { AVStream *stream = self->video_format ? self->video_format->streams[video_index] : NULL; if (stream) { int d = stream->avg_frame_rate.den; vfr = d != 0 && d != 1 && d != 2 && d != 125 && d != 1001 && av_cmp_q(stream->avg_frame_rate, stream->r_frame_rate); } } if (vfr) { mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(self->parent), "meta.media.variable_frame_rate", 1); } // Reset context position av_seek_frame(context, -1, 0, AVSEEK_FLAG_BACKWARD); } static int seek_video(producer_avformat self, mlt_position position, int64_t req_position, int preseek) { mlt_producer producer = self->parent; mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); int paused = 0; int seek_threshold = mlt_properties_get_int(properties, "seek_threshold"); if (seek_threshold <= 0) seek_threshold = 64; pthread_mutex_lock(&self->packets_mutex); if (self->video_seekable && (position != self->video_expected || self->last_position < 0)) { // Fetch the video format context AVFormatContext *context = self->video_format; // We may want to use the source fps if available double source_fps = mlt_properties_get_double(properties, "meta.media.frame_rate_num") / mlt_properties_get_double(properties, "meta.media.frame_rate_den"); if (self->first_pts == AV_NOPTS_VALUE && self->last_position == POSITION_INITIAL) find_first_pts(self, self->video_index); if (self->video_frame && position + 1 == self->video_expected) { // We're paused - use last image paused = 1; } else if (position < self->video_expected || position - self->video_expected >= seek_threshold || self->last_position < 0) { // Calculate the timestamp for the requested frame int64_t timestamp = req_position / (av_q2d(self->video_time_base) * source_fps); if (req_position <= 0) timestamp = 0; else if (self->first_pts != AV_NOPTS_VALUE) timestamp += self->first_pts; else if (context->start_time != AV_NOPTS_VALUE) timestamp += context->start_time; if (preseek && av_q2d(self->video_time_base) != 0) timestamp -= 2 / av_q2d(self->video_time_base); if (timestamp < 0) timestamp = 0; // cppcheck-suppress syntaxError mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "seeking timestamp %" PRId64 " position " MLT_POSITION_FMT " expected " MLT_POSITION_FMT " last_pos %" PRId64 "\n", timestamp, position, self->video_expected, self->last_position); // Seek to the timestamp self->video_codec->skip_loop_filter = AVDISCARD_NONREF; av_seek_frame(context, self->video_index, timestamp, AVSEEK_FLAG_BACKWARD); // flush any pictures still in decode buffer avcodec_flush_buffers(self->video_codec); self->video_send_result = 0; // let packets_worker know we handled EOF if (self->packets_thread_ret == AVERROR_EOF) { self->packets_thread_ret = 0; } // empty vpackets while (mlt_deque_count(self->vpackets) > 0) { AVPacket *tmp = (AVPacket *) mlt_deque_pop_front(self->vpackets); av_packet_free(&tmp); } pthread_cond_signal(&self->packets_cond); // Remove the cached info relating to the previous position self->current_position = POSITION_INVALID; self->last_position = POSITION_INVALID; av_frame_unref(self->video_frame); } } pthread_mutex_unlock(&self->packets_mutex); return paused; } /** Convert a frame position to a time code. */ static double producer_time_of_frame(mlt_producer producer, mlt_position position) { return (double) position / mlt_producer_get_fps(producer); } // Collect information about all audio streams static void get_audio_streams_info(producer_avformat self) { // Fetch the audio format context AVFormatContext *context = self->audio_format; unsigned int i; for (i = 0; i < context->nb_streams; i++) { if (context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { AVCodecParameters *codec_params = context->streams[i]->codecpar; const AVCodec *codec = avcodec_find_decoder(codec_params->codec_id); // Setup the codec context AVCodecContext *codec_context = avcodec_alloc_context3(codec); if (!codec_context) { mlt_log_info(MLT_PRODUCER_SERVICE(self->parent), "Failed to allocate the decoder context for stream #%d\n", i); continue; } int ret = avcodec_parameters_to_context(codec_context, codec_params); if (ret < 0) { mlt_log_info( MLT_PRODUCER_SERVICE(self->parent), "Failed to copy decoder parameters to input decoder context for stream #%d\n", i); continue; } // If we don't have a codec and we can't initialise it, we can't do much more... pthread_mutex_lock(&self->open_mutex); if (codec && avcodec_open2(codec_context, codec, NULL) >= 0) { self->audio_streams++; self->audio_max_stream = i; #if HAVE_FFMPEG_CH_LAYOUT self->total_channels += codec_params->ch_layout.nb_channels; if (codec_params->ch_layout.nb_channels > self->max_channel) self->max_channel = codec_params->ch_layout.nb_channels; #else self->total_channels += codec_params->channels; if (codec_params->channels > self->max_channel) self->max_channel = codec_params->channels; #endif if (codec_params->sample_rate > self->max_frequency) self->max_frequency = codec_params->sample_rate; avcodec_free_context(&codec_context); } pthread_mutex_unlock(&self->open_mutex); } } mlt_log_verbose(NULL, "[producer avformat] audio: total_streams %d max_stream %d total_channels %d " "max_channels %d\n", self->audio_streams, self->audio_max_stream, self->total_channels, self->max_channel); } static mlt_audio_format pick_audio_format(int sample_fmt) { switch (sample_fmt) { // interleaved case AV_SAMPLE_FMT_U8: return mlt_audio_u8; case AV_SAMPLE_FMT_S16: return mlt_audio_s16; case AV_SAMPLE_FMT_S32: return mlt_audio_s32le; case AV_SAMPLE_FMT_FLT: return mlt_audio_f32le; // planar - this producer converts planar to interleaved case AV_SAMPLE_FMT_U8P: return mlt_audio_u8; case AV_SAMPLE_FMT_S16P: return mlt_audio_s16; case AV_SAMPLE_FMT_S32P: return mlt_audio_s32le; case AV_SAMPLE_FMT_FLTP: return mlt_audio_f32le; default: return mlt_audio_none; } } /** * Handle deprecated pixel format (JPEG range in YUV420P for example). * * Replace pix_fmt with the official pixel format to use. * @return 0 if no pix_fmt replacement, 1 otherwise */ static int pick_av_pixel_format(int *pix_fmt, int full_range) { switch (*pix_fmt) { case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUVJ420P: *pix_fmt = full_range ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_YUV420P; return 1; case AV_PIX_FMT_YUV411P: case AV_PIX_FMT_YUVJ411P: *pix_fmt = full_range ? AV_PIX_FMT_YUVJ411P : AV_PIX_FMT_YUV411P; return 1; case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUVJ422P: *pix_fmt = full_range ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV422P; return 1; case AV_PIX_FMT_YUV444P: case AV_PIX_FMT_YUVJ444P: *pix_fmt = full_range ? AV_PIX_FMT_YUVJ444P : AV_PIX_FMT_YUV444P; return 1; case AV_PIX_FMT_YUV440P: case AV_PIX_FMT_YUVJ440P: *pix_fmt = full_range ? AV_PIX_FMT_YUVJ440P : AV_PIX_FMT_YUV440P; return 1; } return 0; } static void property_changed(mlt_service owner, producer_avformat self, char *name) { if (self && name && self->parent) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self->parent); if (!strcmp("color_range", name)) { if (self->video_codec && !av_opt_set(self->video_codec, name, mlt_properties_get(properties, name), AV_OPT_SEARCH_CHILDREN)) { if (self->full_range != (self->video_codec->color_range == AVCOL_RANGE_JPEG)) { self->full_range = self->video_codec->color_range == AVCOL_RANGE_JPEG; self->reset_image_cache = 1; } } } else if (!strcmp("force_full_range", name) || !strcmp("set.force_full_luma", name)) { if (self->full_range != mlt_properties_get_int(properties, name)) { self->full_range = mlt_properties_get_int(properties, name); self->reset_image_cache = 1; } } else if (!strcmp("force_progressive", name) || !strcmp("force_tff", name)) { self->reset_image_cache = 1; } else if (!strcmp("autorotate", name)) { self->autorotate = mlt_properties_get_int(properties, name); if (self->video_index != -1) { mlt_service_lock(MLT_PRODUCER_SERVICE(self->parent)); avfilter_graph_free(&self->vfilter_graph); self->vfilter_in = NULL; self->vfilter_out = NULL; self->rotation = 0.0; setup_filters(self); self->reset_image_cache = 1; mlt_service_unlock(MLT_PRODUCER_SERVICE(self->parent)); } } else if (!strcmp("lut", name)) { if (self->video_index != -1 && self->video_format) { mlt_service_lock(MLT_PRODUCER_SERVICE(self->parent)); avfilter_graph_free(&self->vfilter_graph); self->vfilter_in = NULL; self->vfilter_out = NULL; setup_filters(self); self->reset_image_cache = 1; mlt_service_unlock(MLT_PRODUCER_SERVICE(self->parent)); } } else if (!strcmp("video_index", name) || !strcmp("vstream", name)) { if (mlt_properties_get_int(properties, "_probe_complete")) { mlt_properties_set_int(properties, "_probe_complete", 0); } } } } struct sliced_pix_fmt_conv_t { int width, height, slice_w; AVFrame *frame; uint8_t *out_data[4]; int out_stride[4]; enum AVPixelFormat src_format, dst_format; const AVPixFmtDescriptor *src_desc, *dst_desc; int flags, src_full_range, dst_full_range; mlt_colorspace src_colorspace, dst_colorspace; }; static int sliced_h_pix_fmt_conv_proc(int id, int idx, int jobs, void *cookie) { uint8_t *out[4]; const uint8_t *in[4]; int in_stride[4], out_stride[4]; int src_v_chr_pos = -513, dst_v_chr_pos = -513, ret, i, slice_x, slice_w, h, mul, field, slices, interlaced = 0; struct SwsContext *sws; struct sliced_pix_fmt_conv_t *ctx = (struct sliced_pix_fmt_conv_t *) cookie; #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) interlaced = ctx->frame->flags & AV_FRAME_FLAG_INTERLACED; #else interlaced = ctx->frame->interlaced_frame; #endif field = (interlaced) ? (idx & 1) : 0; idx = (interlaced) ? (idx / 2) : idx; slices = (interlaced) ? (jobs / 2) : jobs; mul = (interlaced) ? 2 : 1; h = ctx->height >> !!interlaced; slice_w = ctx->slice_w; slice_x = slice_w * idx; slice_w = FFMIN(slice_w, ctx->width - slice_x); if (AV_PIX_FMT_YUV420P == ctx->src_format) src_v_chr_pos = (!interlaced) ? 128 : (!field) ? 64 : 192; if (AV_PIX_FMT_YUV420P == ctx->dst_format) dst_v_chr_pos = (!interlaced) ? 128 : (!field) ? 64 : 192; mlt_log_debug(NULL, "%s:%d: [id=%d, idx=%d, jobs=%d], interlaced=%d, field=%d, slices=%d, mul=%d, " "h=%d, slice_w=%d, slice_x=%d ctx->src_desc=[log2_chroma_h=%d, " "log2_chroma_w=%d], src_v_chr_pos=%d, dst_v_chr_pos=%d\n", __FUNCTION__, __LINE__, id, idx, jobs, interlaced, field, slices, mul, h, slice_w, slice_x, ctx->src_desc->log2_chroma_h, ctx->src_desc->log2_chroma_w, src_v_chr_pos, dst_v_chr_pos); if (slice_w <= 0) return 0; sws = sws_alloc_context(); av_opt_set_int(sws, "srcw", slice_w, 0); av_opt_set_int(sws, "srch", h, 0); av_opt_set_int(sws, "src_format", ctx->src_format, 0); av_opt_set_int(sws, "dstw", slice_w, 0); av_opt_set_int(sws, "dsth", h, 0); av_opt_set_int(sws, "dst_format", ctx->dst_format, 0); av_opt_set_int(sws, "sws_flags", ctx->flags, 0); av_opt_set_int(sws, "src_h_chr_pos", -513, 0); av_opt_set_int(sws, "src_v_chr_pos", src_v_chr_pos, 0); av_opt_set_int(sws, "dst_h_chr_pos", -513, 0); av_opt_set_int(sws, "dst_v_chr_pos", dst_v_chr_pos, 0); if ((ret = sws_init_context(sws, NULL, NULL)) < 0) { mlt_log_error(NULL, "%s:%d: sws_init_context failed, ret=%d\n", __FUNCTION__, __LINE__, ret); sws_freeContext(sws); return 0; } mlt_set_luma_transfer(sws, ctx->src_colorspace, ctx->dst_colorspace, ctx->src_full_range, ctx->dst_full_range); #define PIX_DESC_BPP(DESC) (DESC.step) for (i = 0; i < 4; i++) { int in_offset = (AV_PIX_FMT_FLAG_PLANAR & ctx->src_desc->flags) ? ((1 == i || 2 == i) ? (slice_x >> ctx->src_desc->log2_chroma_w) : slice_x) : ((0 == i) ? slice_x : 0); int out_offset = (AV_PIX_FMT_FLAG_PLANAR & ctx->dst_desc->flags) ? ((1 == i || 2 == i) ? (slice_x >> ctx->dst_desc->log2_chroma_w) : slice_x) : ((0 == i) ? slice_x : 0); in_offset *= PIX_DESC_BPP(ctx->src_desc->comp[i]); out_offset *= PIX_DESC_BPP(ctx->dst_desc->comp[i]); in_stride[i] = ctx->frame->linesize[i] * mul; out_stride[i] = ctx->out_stride[i] * mul; in[i] = ctx->frame->data[i] + ctx->frame->linesize[i] * field + in_offset; out[i] = ctx->out_data[i] + ctx->out_stride[i] * field + out_offset; } sws_scale(sws, in, in_stride, 0, h, out, out_stride); sws_freeContext(sws); return 0; } static mlt_colorspace convert_image_yuvp(producer_avformat self, mlt_profile profile, AVFrame *frame, uint8_t *buffer, mlt_image_format format, int width, int height, int src_pix_fmt, int dst_pix_fmt, int dst_colorspace, int dst_full_range) { mlt_colorspace result = self->yuv_colorspace; // Check if we need special handling for range conversion // Some formats don't have YUVJ variants or libswscale doesn't handle range // conversion reliably without an intermediate conversion step AVFrame *intermediate_frame = NULL; int intermediate_pix_fmt = AV_PIX_FMT_NONE; if (self->full_range != dst_full_range) { const AVPixFmtDescriptor *src_desc = av_pix_fmt_desc_get(src_pix_fmt); // For 8-bit NV12/NV21 -> YUV420P if (dst_pix_fmt == AV_PIX_FMT_YUV420P && (src_pix_fmt == AV_PIX_FMT_NV12 || src_pix_fmt == AV_PIX_FMT_NV21)) { // Use YUVJ420P to encode full range in the pixel format itself intermediate_pix_fmt = self->full_range ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_YUV420P; } // For 10-bit source else if (self->full_range && src_desc && src_desc->nb_components > 0 && src_desc->comp[0].depth == 10) { // For 8-bit output encode full range in pixel format if (dst_pix_fmt == AV_PIX_FMT_YUV420P) { intermediate_pix_fmt = AV_PIX_FMT_YUVJ420P; } else if (src_pix_fmt != AV_PIX_FMT_YUV422P10LE) { // For 10-bit output, use YUV 4:2:2 if source has sub-sampled chroma, otherwise 64-bit RGB if (src_desc->log2_chroma_w > 0 || src_desc->log2_chroma_h > 0) { intermediate_pix_fmt = AV_PIX_FMT_YUV422P10LE; } else { intermediate_pix_fmt = AV_PIX_FMT_RGBA64LE; } } } } if (intermediate_pix_fmt != AV_PIX_FMT_NONE) { mlt_log_debug(MLT_PRODUCER_SERVICE(self->parent), "Using intermediate conversion for %s range %d -> %d\n", av_get_pix_fmt_name(src_pix_fmt), self->full_range, dst_full_range); intermediate_frame = av_frame_alloc(); intermediate_frame->width = width; intermediate_frame->height = height; intermediate_frame->format = intermediate_pix_fmt; av_frame_get_buffer(intermediate_frame, 1); int flags = mlt_get_sws_flags(width, height, src_pix_fmt, width, height, intermediate_pix_fmt); struct SwsContext *context1 = sws_getContext(width, height, src_pix_fmt, width, height, intermediate_pix_fmt, flags, NULL, NULL, NULL); // Keep the intermediate in the source range mlt_set_luma_transfer(context1, self->yuv_colorspace, self->yuv_colorspace, self->full_range, self->full_range); sws_scale(context1, (const uint8_t *const *) frame->data, frame->linesize, 0, height, intermediate_frame->data, intermediate_frame->linesize); sws_freeContext(context1); // Now use the intermediate frame frame = intermediate_frame; src_pix_fmt = intermediate_pix_fmt; } // Main conversion int flags = mlt_get_sws_flags(width, height, src_pix_fmt, width, height, dst_pix_fmt); struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, width, height, dst_pix_fmt, flags, NULL, NULL, NULL); uint8_t *out_data[4]; int out_stride[4]; mlt_image_format_planes(format, width, height, buffer, out_data, out_stride); // For YUVJ formats, the range is encoded in the format int effective_src_range = (src_pix_fmt == AV_PIX_FMT_YUVJ420P || src_pix_fmt == AV_PIX_FMT_YUVJ422P || src_pix_fmt == AV_PIX_FMT_YUVJ444P) ? 1 : self->full_range; if (!mlt_set_luma_transfer(context, self->yuv_colorspace, dst_colorspace, effective_src_range, dst_full_range)) result = dst_colorspace; sws_scale(context, (const uint8_t *const *) frame->data, frame->linesize, 0, height, out_data, out_stride); sws_freeContext(context); av_frame_free(&intermediate_frame); return result; } static void convert_image_rgb(producer_avformat self, mlt_profile profile, AVFrame *frame, uint8_t *buffer, mlt_image_format format, int width, int height, int src_pix_fmt, int dst_pix_fmt, int dst_full_range) { int flags = mlt_get_sws_flags(width, height, src_pix_fmt, width, height, dst_pix_fmt); uint8_t *out_data[4]; int out_stride[4]; #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) if (src_pix_fmt == AV_PIX_FMT_YUV420P && (frame->flags & AV_FRAME_FLAG_INTERLACED)) { #else if (src_pix_fmt == AV_PIX_FMT_YUV420P && frame->interlaced_frame) { #endif // Perform field-aware conversion for 4:2:0 int field_height = height / 2; const uint8_t *in_data[4]; int in_stride[4]; struct SwsContext *context = sws_getContext(width, field_height, src_pix_fmt, width, field_height, dst_pix_fmt, flags, NULL, NULL, NULL); // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. mlt_set_luma_transfer(context, self->yuv_colorspace, mlt_colorspace_bt601, self->full_range, 1); av_image_fill_arrays(out_data, out_stride, buffer, dst_pix_fmt, width, height, IMAGE_ALIGN); // Copy the input frame arrays for (int i = 0; i < 4; i++) { in_data[i] = frame->data[i]; in_stride[i] = frame->linesize[i]; } // Modify the strides to skip every other line for (int i = 0; i < 4; i++) { in_stride[i] *= 2; out_stride[i] *= 2; } // Convert the first field sws_scale(context, in_data, in_stride, 0, field_height, out_data, out_stride); // Offset the data to point at the second field for (int i = 0; i < 4; i++) { in_data[i] += in_stride[i] / 2; out_data[i] += out_stride[i] / 2; } // Convert the second field sws_scale(context, in_data, in_stride, 0, field_height, out_data, out_stride); sws_freeContext(context); } else { struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, width, height, dst_pix_fmt, flags, NULL, NULL, NULL); av_image_fill_arrays(out_data, out_stride, buffer, dst_pix_fmt, width, height, IMAGE_ALIGN); // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. mlt_set_luma_transfer(context, self->yuv_colorspace, mlt_colorspace_bt601, self->full_range, 1); sws_scale(context, (const uint8_t *const *) frame->data, frame->linesize, 0, height, out_data, out_stride); sws_freeContext(context); } } // returns resulting YUV colorspace static void convert_image(producer_avformat self, mlt_properties frame_properties, AVFrame *frame, uint8_t *buffer, int pix_fmt, mlt_image_format *format, int width, int height, uint8_t **alpha, int dst_colorspace, int dst_full_range) { mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(self->parent)); mlt_colorspace colorspace = self->yuv_colorspace; mlt_log_timings_begin(); mlt_log_debug(MLT_PRODUCER_SERVICE(self->parent), "%s->%s @ %dx%d colorspace %d->%d range %d->%d\n", av_get_pix_fmt_name(pix_fmt), mlt_image_format_name(*format), width, height, self->yuv_colorspace, dst_colorspace, self->full_range, dst_full_range); // extract alpha from planar formats - only supports 8-bit if ((pix_fmt == AV_PIX_FMT_YUVA420P || pix_fmt == AV_PIX_FMT_YUVA422P || pix_fmt == AV_PIX_FMT_YUVA444P) && *format != mlt_image_rgba && *format != mlt_image_rgba64 && frame->data[3] && frame->linesize[3]) { int i; uint8_t *src, *dst; dst = *alpha = mlt_pool_alloc(width * height); src = frame->data[3]; for (i = 0; i < height; dst += width, src += frame->linesize[3], i++) memcpy(dst, src, FFMIN(width, frame->linesize[3])); } // Figure out source and destination pixel format int src_pix_fmt = pix_fmt; pick_av_pixel_format(&src_pix_fmt, self->full_range); int dst_pix_fmt = AV_PIX_FMT_NONE; switch (*format) { case mlt_image_yuv420p: dst_pix_fmt = AV_PIX_FMT_YUV420P; break; case mlt_image_yuv420p10: dst_pix_fmt = AV_PIX_FMT_YUV420P10LE; break; case mlt_image_yuv444p10: dst_pix_fmt = AV_PIX_FMT_YUV444P10LE; break; case mlt_image_yuv422p16: dst_pix_fmt = AV_PIX_FMT_YUV422P16LE; break; case mlt_image_rgb: dst_pix_fmt = AV_PIX_FMT_RGB24; break; case mlt_image_rgba: dst_pix_fmt = AV_PIX_FMT_RGBA; break; case mlt_image_rgba64: dst_pix_fmt = AV_PIX_FMT_RGBA64LE; break; case mlt_image_none: case mlt_image_yuv422: case mlt_image_movit: case mlt_image_opengl_texture: case mlt_image_invalid: break; } // Convert if (mlt_image_rgb == *format || mlt_image_rgba == *format || mlt_image_rgba64 == *format) { convert_image_rgb(self, profile, frame, buffer, *format, width, height, src_pix_fmt, dst_pix_fmt, dst_full_range); } else if (dst_pix_fmt != AV_PIX_FMT_NONE) { colorspace = convert_image_yuvp(self, profile, frame, buffer, *format, width, height, src_pix_fmt, dst_pix_fmt, dst_colorspace, dst_full_range); } else { int i, c; struct sliced_pix_fmt_conv_t ctx = { .width = width, .height = height, .frame = frame, .dst_format = AV_PIX_FMT_YUYV422, .src_colorspace = self->yuv_colorspace, .dst_colorspace = dst_colorspace, .src_full_range = self->full_range, .dst_full_range = dst_full_range, }; ctx.src_format = (self->full_range && src_pix_fmt == AV_PIX_FMT_YUV422P) ? AV_PIX_FMT_YUVJ422P : src_pix_fmt; ctx.src_desc = av_pix_fmt_desc_get(ctx.src_format); ctx.dst_desc = av_pix_fmt_desc_get(ctx.dst_format); ctx.flags = mlt_get_sws_flags(width, height, ctx.src_format, width, height, ctx.dst_format); av_image_fill_arrays(ctx.out_data, ctx.out_stride, buffer, ctx.dst_format, width, height, IMAGE_ALIGN); int sliced = !getenv("MLT_AVFORMAT_SLICED_PIXFMT_DISABLE") && src_pix_fmt != ctx.dst_format; if (sliced) { #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) ctx.slice_w = (width < 1000) ? (256 >> (frame->flags & AV_FRAME_FLAG_INTERLACED)) : (512 >> (frame->flags & AV_FRAME_FLAG_INTERLACED)); #else ctx.slice_w = (width < 1000) ? (256 >> frame->interlaced_frame) : (512 >> frame->interlaced_frame); #endif } else { ctx.slice_w = width; } c = (width + ctx.slice_w - 1) / ctx.slice_w; int last_slice_w = width - ctx.slice_w * (c - 1); if (sliced && (last_slice_w % 8) == 0 && !(ctx.src_format == AV_PIX_FMT_YUV422P && last_slice_w % 16)) { #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) c *= (frame->flags & AV_FRAME_FLAG_INTERLACED) ? 2 : 1; #else c *= frame->interlaced_frame ? 2 : 1; #endif mlt_slices_run_normal(c, sliced_h_pix_fmt_conv_proc, &ctx); } else { #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) c = (frame->flags & AV_FRAME_FLAG_INTERLACED) ? 2 : 1; #else c = frame->interlaced_frame ? 2 : 1; #endif ctx.slice_w = width; for (i = 0; i < c; i++) sliced_h_pix_fmt_conv_proc(i, i, c, &ctx); } colorspace = dst_colorspace; } mlt_log_timings_end(NULL, __FUNCTION__); mlt_color_primaries primaries = mlt_image_default_primaries(colorspace, height); if (primaries == mlt_color_pri_none) primaries = mlt_color_pri_bt709; mlt_properties_set_int(frame_properties, "color_primaries", primaries); mlt_properties_set_int(frame_properties, "colorspace", colorspace); mlt_properties_set_int(frame_properties, "full_range", dst_full_range); } static void set_image_size(producer_avformat self, int *width, int *height) { if (self->vfilter_out) { *width = av_buffersink_get_w(self->vfilter_out); *height = av_buffersink_get_h(self->vfilter_out); } else if (self->hwaccel.filter_out) { *width = av_buffersink_get_w(self->hwaccel.filter_out); *height = av_buffersink_get_h(self->hwaccel.filter_out); } else { double dar = mlt_profile_dar(mlt_service_profile(MLT_PRODUCER_SERVICE(self->parent))); *width = self->video_codec->width; // Workaround 1088 encodings missing cropping info. if (self->video_codec->height == 1088 && dar == 16.0 / 9.0) *height = 1080; else *height = self->video_codec->height; } } /** Allocate the image buffer and set it on the frame. */ static int allocate_buffer(mlt_frame frame, AVCodecParameters *codec_params, uint8_t **buffer, mlt_image_format format, int width, int height) { int size = 0; if (codec_params->width == 0 || codec_params->height == 0) return size; size = mlt_image_format_size(format, width, height, NULL); *buffer = mlt_pool_alloc(size); if (*buffer) mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); else size = 0; return size; } static int ignore_send_packet_result(int result) { return result >= 0 || result == AVERROR(EAGAIN) || result == AVERROR_EOF || result == AVERROR_INVALIDDATA || result == AVERROR(EINVAL); } static int is_album_art(producer_avformat self) { return self->video_index >= 0 && (self->video_format->streams[self->video_index]->disposition & AV_DISPOSITION_ATTACHED_PIC); } static void *packets_worker(void *param) { producer_avformat self = param; AVPacket *pkt = av_packet_alloc(); if (!pkt) { mlt_log_fatal(MLT_PRODUCER_SERVICE(self->parent), "av_packet_alloc failed\n"); exit(EXIT_FAILURE); } pthread_mutex_lock(&self->packets_mutex); for (;;) { check_stop: if (self->packets_thread_stop) { av_packet_free(&pkt); pthread_mutex_unlock(&self->packets_mutex); return NULL; } if (mlt_deque_count(self->vpackets) >= 1 || self->packets_thread_ret < 0) { pthread_cond_wait(&self->packets_cond, &self->packets_mutex); goto check_stop; } int ret = av_read_frame(self->video_format, pkt); // don't bother signalling on EAGAIN if (ret != AVERROR(EAGAIN)) { self->packets_thread_ret = ret; if (ret == 0) { if (pkt->stream_index == self->video_index) { mlt_deque_push_back(self->vpackets, av_packet_clone(pkt)); } else if (!self->video_seekable && pkt->stream_index == self->audio_index && !is_album_art(self)) { mlt_deque_push_back(self->apackets, av_packet_clone(pkt)); } av_packet_unref(pkt); } else if (ret != AVERROR_EOF) { mlt_log_verbose(MLT_PRODUCER_SERVICE(self->parent), "av_read_frame returned error %d inside packets_worker\n", ret); } pthread_cond_signal(&self->packets_cond); } } } static void init_cache(mlt_properties properties, mlt_cache *cache) { // if cache size supplied by environment variable int cache_supplied = getenv("MLT_AVFORMAT_CACHE") != NULL; int cache_size = cache_supplied ? atoi(getenv("MLT_AVFORMAT_CACHE")) : 0; // cache size supplied via property if (mlt_properties_get(properties, "cache")) { cache_supplied = 1; cache_size = mlt_properties_get_int(properties, "cache"); } if (mlt_properties_get_int(properties, "noimagecache")) { cache_supplied = 1; cache_size = 0; } // create cache if not disabled if (!cache_supplied || cache_size > 0) *cache = mlt_cache_init(); // set cache size if supplied if (*cache && cache_supplied) mlt_cache_set_size(*cache, cache_size); } /** Get an image from a frame. */ static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { // Get the producer (void) writable; // unused producer_avformat self = mlt_frame_pop_service(frame); mlt_producer producer = self->parent; // Get the properties from the frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); // Obtain the frame number of this frame mlt_position position = mlt_frame_original_position(frame); // Get the producer properties mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); uint8_t *alpha = NULL; int got_picture = 0; int image_size = 0; mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(self->parent)); int dst_colorspace = (*format == mlt_image_none) ? self->yuv_colorspace : profile->colorspace; const char *dst_color_range = mlt_properties_get(frame_properties, "consumer.color_range"); int dst_full_range = (*format == mlt_image_none) ? self->full_range : mlt_image_full_range(dst_color_range); mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); pthread_mutex_lock(&self->video_mutex); mlt_log_timings_begin(); if (self->autorotate && self->video_index != -1 && get_rotation(properties, self->video_format->streams[self->video_index]) != self->rotation) { // Rotation has changed. Clear any cached frames. self->reset_image_cache = 1; } if (self->reset_image_cache) { self->reset_image_cache = 0; mlt_cache_close(self->image_cache); self->image_cache = NULL; av_frame_free(&self->video_frame); } // Fetch the video format context AVFormatContext *context = self->video_format; AVCodecParameters *codec_params = NULL; if (!context) goto exit_get_image; // Get the video stream AVStream *stream = context->streams[self->video_index]; codec_params = stream->codecpar; // Always use the image cache for album art. if (is_album_art(self)) position = 0; // Get the image cache if (!self->image_cache) { init_cache(properties, &self->image_cache); } if (self->image_cache) { mlt_frame original = mlt_cache_get_frame(self->image_cache, position); if (original && (*format == mlt_image_none || *format == mlt_properties_get_int(MLT_FRAME_PROPERTIES(original), "format"))) { mlt_properties orig_props = MLT_FRAME_PROPERTIES(original); int size = 0; *buffer = mlt_frame_get_alpha_size(original, &size); if (*buffer) mlt_frame_set_alpha(frame, *buffer, size, NULL); *buffer = mlt_properties_get_data(orig_props, "image", &size); mlt_frame_set_image(frame, *buffer, size, NULL); mlt_properties_set_data(frame_properties, "avformat.image_cache", original, 0, (mlt_destructor) mlt_frame_close, NULL); *format = mlt_properties_get_int(orig_props, "format"); set_image_size(self, width, height); mlt_properties_pass_list(frame_properties, orig_props, "colorspace color_primaries"); mlt_properties_set_int(frame_properties, "full_range", dst_full_range); got_picture = 1; goto exit_get_image; } else { mlt_frame_close(original); } } // Cache miss // We may want to use the source fps if available double source_fps = mlt_properties_get_double(properties, "meta.media.frame_rate_num") / mlt_properties_get_double(properties, "meta.media.frame_rate_den"); // This is the physical frame position in the source int64_t req_position = (int64_t) (position / mlt_producer_get_fps(producer) * source_fps + 0.5); // Determines if we have to decode all frames in a sequence - when there temporal compression is used. const AVCodecDescriptor *descriptor = avcodec_descriptor_get(codec_params->codec_id); int must_decode = descriptor && !(descriptor->props & AV_CODEC_PROP_INTRA_ONLY); double delay = mlt_properties_get_double(properties, "video_delay"); // Seek if necessary double speed = mlt_producer_get_speed(producer); int preseek = must_decode && self->video_codec->has_b_frames && speed >= 0.0 && speed <= 1.0; int paused = seek_video(self, position, req_position, preseek); // Seek might have reopened the file context = self->video_format; stream = context->streams[self->video_index]; codec_params = stream->codecpar; // Only change the requested image format for special cases *format = pick_image_format(self->vfilter_out ? av_buffersink_get_format(self->vfilter_out) : codec_params->format, *format); // Duplicate the last image if necessary if (self->video_frame && self->video_frame->linesize[0] && (self->pkt.stream_index == self->video_index) && (paused || self->current_position >= req_position)) { // Duplicate it set_image_size(self, width, height); if ((image_size = allocate_buffer(frame, codec_params, buffer, *format, *width, *height))) { convert_image(self, frame_properties, self->video_frame, *buffer, self->video_frame->format, format, *width, *height, &alpha, dst_colorspace, dst_full_range); got_picture = 1; } } else { int64_t int_position = 0; int decode_errors = 0; // Construct an AVFrame for YUV422 conversion if (!self->video_frame) self->video_frame = av_frame_alloc(); else av_frame_unref(self->video_frame); if (!self->is_thread_init) { pthread_cond_init(&self->packets_cond, NULL); pthread_create(&self->packets_thread, NULL, packets_worker, self); self->is_thread_init = 1; } while (!got_picture && ignore_send_packet_result(self->video_send_result)) { if (self->video_send_result != AVERROR(EAGAIN)) { // Read a packet if (self->pkt.stream_index == self->video_index) av_packet_unref(&self->pkt); av_init_packet(&self->pkt); pthread_mutex_lock(&self->packets_mutex); while (mlt_deque_count(self->vpackets) == 0 && self->packets_thread_ret == 0) { pthread_cond_wait(&self->packets_cond, &self->packets_mutex); } if (self->packets_thread_ret == 0) { AVPacket *tmp = (AVPacket *) mlt_deque_pop_front(self->vpackets); av_packet_ref(&self->pkt, tmp); av_packet_free(&tmp); pthread_cond_signal(&self->packets_cond); } else { if (self->packets_thread_ret == AVERROR_EOF) { self->pkt.stream_index = self->video_index; } // notify packets_worker that we've seen the error self->packets_thread_ret = 0; pthread_cond_signal(&self->packets_cond); if (!self->video_seekable && mlt_properties_get_int(properties, "reconnect")) { // Try to reconnect to live sources by closing context and codecs, // and letting next call to get_frame() reopen. mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); prepare_reopen(self); mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); pthread_mutex_unlock(&self->packets_mutex); goto exit_get_image; } if (!self->video_seekable && mlt_properties_get_int(properties, "exit_on_disconnect")) { mlt_log_fatal(MLT_PRODUCER_SERVICE(producer), "Exiting with error due to disconnected source.\n"); exit(EXIT_FAILURE); } // Send null packets to drain decoder. self->pkt.size = 0; self->pkt.data = NULL; } pthread_mutex_unlock(&self->packets_mutex); } // We only deal with video from the selected video_index if (self->pkt.stream_index == self->video_index) { int64_t pts = best_pts(self, self->pkt.pts, self->pkt.dts); if (pts != AV_NOPTS_VALUE) { if (!self->video_seekable && self->first_pts == AV_NOPTS_VALUE) self->first_pts = pts; if (self->first_pts != AV_NOPTS_VALUE) pts -= self->first_pts; else if (context->start_time != AV_NOPTS_VALUE) pts -= context->start_time; int_position = (int64_t) ((av_q2d(self->video_time_base) * pts + delay) * source_fps + 0.5); if (int_position == self->last_position) int_position = self->last_position + 1; } mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "V pkt.pts %" PRId64 " pkt.dts %" PRId64 " req_pos %" PRId64 " cur_pos %" PRId64 " pkt_pos %" PRId64 "\n", self->pkt.pts, self->pkt.dts, req_position, self->current_position, int_position); // Make a dumb assumption on streams that contain wild timestamps if (llabs(req_position - int_position) > 999) { mlt_log_verbose(MLT_PRODUCER_SERVICE(producer), " WILD TIMESTAMP: " "pkt.pts=[%" PRId64 "], pkt.dts=[%" PRId64 "], req_position=[%" PRId64 "], " "current_position=[%" PRId64 "], int_position=[%" PRId64 "], pts=[%" PRId64 "] \n", self->pkt.pts, self->pkt.dts, req_position, self->current_position, int_position, pts); int_position = req_position; } self->last_position = int_position; // Decode the image if (must_decode || int_position >= req_position || !self->pkt.data) { #if LIBAVCODEC_VERSION_MAJOR < 61 self->video_codec->reordered_opaque = int_position; #endif if (int_position >= req_position) self->video_codec->skip_loop_filter = AVDISCARD_NONE; self->video_send_result = avcodec_send_packet(self->video_codec, &self->pkt); mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "decoded video packet with size %d => %d\n", self->pkt.size, self->video_send_result); // Note: decode may fail at the beginning of MPEGfile (B-frames referencing before first I-frame), so allow a few errors. if (!ignore_send_packet_result(self->video_send_result)) { mlt_log_warning(MLT_PRODUCER_SERVICE(producer), "video avcodec_send_packet failed with %d\n", self->video_send_result); } else { int error = avcodec_receive_frame(self->video_codec, self->video_frame); if (error < 0) { if (error != AVERROR(EAGAIN) && ++decode_errors > 10) { mlt_log_warning(MLT_PRODUCER_SERVICE(producer), "video decoding error %d\n", error); self->last_good_position = POSITION_INVALID; } } else { if (self->hwaccel.device_ctx && self->video_frame->format == self->hwaccel.pix_fmt) { // Check if we need to set up hardware scaling filter (only once) double consumer_scale = mlt_properties_get_double(frame_properties, "consumer.scale"); consumer_scale = consumer_scale * profile->height / self->video_frame->height; // Do not add hardware filters for interlaced video #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) int is_interlaced = !!(self->video_frame->flags & AV_FRAME_FLAG_INTERLACED); #else int is_interlaced = self->video_frame->interlaced_frame; #endif if (mlt_properties_get(properties, "force_progressive")) { is_interlaced = !mlt_properties_get_int(properties, "force_progressive"); } if (!is_interlaced && consumer_scale > 0.0 && consumer_scale < 1.0) { try_setup_hwaccel_filters(self, producer, consumer_scale); } // Apply hardware scale filter if initialized successfully // Only apply if frame is still in hardware format if ((self->video_frame->format == AV_PIX_FMT_D3D11 || self->video_frame->format == AV_PIX_FMT_VAAPI || self->video_frame->format == AV_PIX_FMT_VIDEOTOOLBOX || self->video_frame->format == AV_PIX_FMT_VULKAN) && self->hwaccel.filters_initialized && self->hwaccel.filter_graph && self->hwaccel.filter_in && self->hwaccel.filter_out) { apply_hwaccel_filters(self, producer); } // Transfer from hardware to software frame AVFrame *sw_video_frame = av_frame_alloc(); int transfer_data_result = av_hwframe_transfer_data(sw_video_frame, self->video_frame, 0); if (transfer_data_result < 0) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "av_hwframe_transfer_data() failed %d\n", transfer_data_result); av_frame_free(&sw_video_frame); av_frame_unref(self->video_frame); goto exit_get_image; } av_frame_copy_props(sw_video_frame, self->video_frame); sw_video_frame->width = self->video_frame->width; sw_video_frame->height = self->video_frame->height; av_frame_unref(self->video_frame); av_frame_move_ref(self->video_frame, sw_video_frame); av_frame_free(&sw_video_frame); } got_picture = 1; decode_errors = 0; } } } if (got_picture) { // Get position of reordered frame #if LIBAVCODEC_VERSION_MAJOR < 61 int_position = self->video_frame->reordered_opaque; #endif pts = best_pts(self, self->video_frame->pts, self->video_frame->pkt_dts); if (pts != AV_NOPTS_VALUE) { // Some streams are not marking their key frames even though // there are I frames, and find_first_pts() fails as a result. // Try to set first_pts here after getting pict_type. if (self->first_pts == AV_NOPTS_VALUE #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) && ((self->video_frame->flags & AV_FRAME_FLAG_KEY) #else && (self->video_frame->key_frame #endif || self->video_frame->pict_type == AV_PICTURE_TYPE_I)) self->first_pts = pts; if (self->first_pts != AV_NOPTS_VALUE) pts -= self->first_pts; else if (context->start_time != AV_NOPTS_VALUE) pts -= context->start_time; int_position = (int64_t) ((av_q2d(self->video_time_base) * pts + delay) * source_fps + 0.5); } if (int_position < req_position) got_picture = 0; else if (int_position >= req_position) self->video_codec->skip_loop_filter = AVDISCARD_NONE; } else if (!self->pkt.data) // draining decoder with null packets { self->video_send_result = -1; } mlt_log_debug(MLT_PRODUCER_SERVICE(producer), " got_pic %d key %d send_result %d pkt_pos %" PRId64 "\n", got_picture, self->pkt.flags & AV_PKT_FLAG_KEY, self->video_send_result, int_position); } // Now handle the picture if we have one if (got_picture) { // Detect and correct scan type if (mlt_properties_get(properties, "force_progressive")) { self->progressive = !!mlt_properties_get_int(properties, "force_progressive"); } else if (self->video_frame && codec_params) { #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) self->progressive = !(self->video_frame->flags & AV_FRAME_FLAG_INTERLACED) #else self->progressive = !self->video_frame->interlaced_frame #endif && (codec_params->field_order == AV_FIELD_PROGRESSIVE || codec_params->field_order == AV_FIELD_UNKNOWN); } else { self->progressive = 0; } #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) if (!self->progressive) self->video_frame->flags |= AV_FRAME_FLAG_INTERLACED; else self->video_frame->flags &= ~AV_FRAME_FLAG_INTERLACED; #else self->video_frame->interlaced_frame = !self->progressive; #endif // Detect and correct field order if (mlt_properties_get(properties, "force_tff")) { self->top_field_first = !!mlt_properties_get_int(properties, "force_tff"); } else { #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) self->top_field_first = (self->video_frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) #else self->top_field_first = self->video_frame->top_field_first #endif || codec_params->field_order == AV_FIELD_TT || codec_params->field_order == AV_FIELD_TB; } #if LIBAVUTIL_VERSION_INT >= ((58 << 16) + (7 << 8) + 100) if (self->top_field_first) self->video_frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; else self->video_frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; #else self->video_frame->top_field_first = self->top_field_first; #endif const char *lut = mlt_properties_get(properties, "lut"); if (self->autorotate || mlt_properties_exists(properties, "filtergraph") || (lut && *lut)) { if (!setup_filters(self) && self->vfilter_graph && self->vfilter_in && self->vfilter_out) { int ret = av_buffersrc_add_frame(self->vfilter_in, self->video_frame); if (ret < 0) { got_picture = 0; break; } while (ret >= 0) { ret = av_buffersink_get_frame_flags(self->vfilter_out, self->video_frame, 0); if (ret < 0) { ret = 0; break; } } } } set_image_size(self, width, height); if ((image_size = allocate_buffer(frame, codec_params, buffer, *format, *width, *height))) { convert_image(self, frame_properties, self->video_frame, *buffer, self->video_frame->format, format, *width, *height, &alpha, dst_colorspace, dst_full_range); self->current_position = int_position; } else { got_picture = 0; } } // Free packet data if not video and not live audio packet if (self->pkt.stream_index != self->video_index && !(!self->video_seekable && self->pkt.stream_index == self->audio_index)) av_packet_unref(&self->pkt); } } // set alpha if (alpha) mlt_frame_set_alpha(frame, alpha, (*width) * (*height), mlt_pool_release); if (image_size > 0) { mlt_properties_set_int(frame_properties, "format", *format); // Cache the image for rapid repeated access. if (self->image_cache) { if (is_album_art(self)) { mlt_position original_pos = mlt_frame_original_position(frame); mlt_properties_set_position(frame_properties, "original_position", 0); mlt_cache_put_frame_image(self->image_cache, frame); mlt_properties_set_position(frame_properties, "original_position", original_pos); } else { mlt_cache_put_frame_image(self->image_cache, frame); } } // Clone frame for error concealment. if (self->current_position >= self->last_good_position) { self->last_good_position = self->current_position; if (self->last_good_frame) mlt_frame_close(self->last_good_frame); self->last_good_frame = mlt_frame_clone(frame, 1); } } else if (self->last_good_frame) { // Use last known good frame if there was a decoding failure. mlt_frame original = mlt_frame_clone(self->last_good_frame, 1); mlt_properties orig_props = MLT_FRAME_PROPERTIES(original); int size = 0; *buffer = mlt_frame_get_alpha_size(original, &size); if (*buffer) mlt_frame_set_alpha(frame, *buffer, size, NULL); *buffer = mlt_properties_get_data(orig_props, "image", &size); mlt_frame_set_image(frame, *buffer, size, NULL); mlt_properties_set_data(frame_properties, "avformat.conceal_error", original, 0, (mlt_destructor) mlt_frame_close, NULL); *format = mlt_properties_get_int(orig_props, "format"); set_image_size(self, width, height); got_picture = 1; } // Regardless of speed, we expect to get the next frame (cos we ain't too bright) self->video_expected = position + 1; exit_get_image: pthread_mutex_unlock(&self->video_mutex); mlt_properties_set_int(frame_properties, "progressive", self->progressive); mlt_properties_set_int(frame_properties, "top_field_first", self->top_field_first); // Set immutable properties of the selected track's (or overridden) source attributes. mlt_properties_set_int(properties, "meta.media.top_field_first", self->top_field_first); mlt_properties_set_int(properties, "meta.media.progressive", self->progressive); mlt_properties_set_int(properties, "_probe_complete", 1); mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); mlt_log_timings_end(NULL, __FUNCTION__); return !got_picture; } /** Process properties as AVOptions and apply to AV context obj */ static void apply_properties(void *obj, mlt_properties properties, int flags) { int i; int count = mlt_properties_count(properties); for (i = 0; i < count; i++) { const char *opt_name = mlt_properties_get_name(properties, i); int search_flags = AV_OPT_SEARCH_CHILDREN; const AVOption *opt = av_opt_find(obj, opt_name, NULL, flags, search_flags); if (opt_name && mlt_properties_get(properties, opt_name) && strcmp(opt_name, "seekable")) { if (opt) av_opt_set(obj, opt_name, mlt_properties_get(properties, opt_name), search_flags); } } } /** Initialize the video codec context. */ static int video_codec_init(producer_avformat self, int index, mlt_properties properties) { // Initialise the codec if necessary if (!self->video_codec) { // Get the video stream AVStream *stream = self->video_format->streams[index]; // Get codec context AVCodecParameters *codec_params = stream->codecpar; // Find the codec const AVCodec *codec = avcodec_find_decoder(codec_params->codec_id); if (mlt_properties_get(properties, "vcodec")) { if (!(codec = avcodec_find_decoder_by_name(mlt_properties_get(properties, "vcodec")))) codec = avcodec_find_decoder(codec_params->codec_id); } else if (codec_params->codec_id == AV_CODEC_ID_VP9) { if (!(codec = avcodec_find_decoder_by_name("libvpx-vp9"))) codec = avcodec_find_decoder(codec_params->codec_id); } else if (codec_params->codec_id == AV_CODEC_ID_VP8) { if (!(codec = avcodec_find_decoder_by_name("libvpx"))) codec = avcodec_find_decoder(codec_params->codec_id); } // Setup the codec context AVCodecContext *codec_context = avcodec_alloc_context3(codec); if (!codec_context) { mlt_log_error(MLT_PRODUCER_SERVICE(self->parent), "Failed to allocate the decoder context for video stream #%d\n", index); self->video_index = -1; return 0; } int ret = avcodec_parameters_to_context(codec_context, codec_params); if (ret < 0) { mlt_log_error( MLT_PRODUCER_SERVICE(self->parent), "Failed to copy decoder parameters to input decoder context for video stream #%d\n", index); self->video_index = -1; return 0; } // Initialise multi-threading int thread_count = mlt_properties_get_int(properties, "threads"); if (thread_count == 0 && getenv("MLT_AVFORMAT_THREADS")) thread_count = atoi(getenv("MLT_AVFORMAT_THREADS")); if (thread_count >= 0) codec_context->thread_count = thread_count; // fix lowres if set too high int lowres = mlt_properties_get_int(properties, "lowres"); if (lowres > codec_context->codec->max_lowres) { mlt_log_debug(MLT_PRODUCER_SERVICE(self->parent), "clamping lowres=%i to max_lowres=%i\n", lowres, codec_context->codec->max_lowres); mlt_properties_set_int(properties, "lowres", codec_context->codec->max_lowres); } if (self->hwaccel.device_type == AV_HWDEVICE_TYPE_NONE || self->hwaccel.pix_fmt == AV_PIX_FMT_NONE) { mlt_log_debug(MLT_PRODUCER_SERVICE(self->parent), "missing hwaccel parameters. skipping hardware initialization\n"); goto skip_hwaccel; } int found_hw_pix_fmt = 0; for (int i = 0;; i++) { const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i); if (!config) break; if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == self->hwaccel.device_type && config->pix_fmt == self->hwaccel.pix_fmt) { found_hw_pix_fmt = 1; break; } } if (found_hw_pix_fmt) { av_buffer_unref(&self->hwaccel.device_ctx); int ret = av_hwdevice_ctx_create(&self->hwaccel.device_ctx, self->hwaccel.device_type, self->hwaccel.device, NULL, 0); if (ret >= 0) { codec_context->hw_device_ctx = av_buffer_ref(self->hwaccel.device_ctx); mlt_log_info(MLT_PRODUCER_SERVICE(self->parent), "hwaccel %s av_hwdevice_ctx_create() success\n", av_get_pix_fmt_name(self->hwaccel.pix_fmt)); } else { mlt_log_warning(MLT_PRODUCER_SERVICE(self->parent), "hwaccel %s av_hwdevice_ctx_create() failed %d\n", av_get_pix_fmt_name(self->hwaccel.pix_fmt), ret); } } else { mlt_log_warning(MLT_PRODUCER_SERVICE(self->parent), "failed to find hw_pix_fmt\n"); } skip_hwaccel: // If we don't have a codec and we can't initialise it, we can't do much more... pthread_mutex_lock(&self->open_mutex); if (codec && avcodec_open2(codec_context, codec, NULL) >= 0) { // Switch to the native vp8/vp9 decoder if not yuva420p if (codec_params->format != AV_PIX_FMT_YUVA420P && !mlt_properties_get(properties, "vcodec") && (!strcmp(codec->name, "libvpx") || !strcmp(codec->name, "libvpx-vp9"))) { codec = avcodec_find_decoder(codec_params->codec_id); if (codec && avcodec_open2(codec_context, codec, NULL) < 0) { self->video_index = -1; pthread_mutex_unlock(&self->open_mutex); return 0; } } // Now store the codec with its destructor self->video_codec = codec_context; } else { // Remember that we can't use this later self->video_index = -1; pthread_mutex_unlock(&self->open_mutex); return 0; } pthread_mutex_unlock(&self->open_mutex); // Process properties as AVOptions apply_properties(codec_context, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM); if (codec && codec->priv_class && codec_context->priv_data) apply_properties(codec_context->priv_data, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM); // Reset some image properties mlt_properties_set_int(properties, "width", codec_params->width); mlt_properties_set_int(properties, "height", codec_params->height); get_aspect_ratio(properties, stream, codec_params); AVRational frame_rate = guess_frame_rate(self, stream); self->video_time_base = stream->time_base; if (mlt_properties_get(properties, "force_fps")) { AVRational force_fps = av_d2q(mlt_properties_get_double(properties, "force_fps"), 1024); self->video_time_base = av_mul_q(stream->time_base, av_div_q(frame_rate, force_fps)); frame_rate = force_fps; } mlt_properties_set_int(properties, "meta.media.frame_rate_num", frame_rate.num); mlt_properties_set_int(properties, "meta.media.frame_rate_den", frame_rate.den); // Cover art is a single image at 90000 fps, which is not seekable. if (stream->disposition & AV_DISPOSITION_ATTACHED_PIC) self->video_seekable = 0; // Set the YUV colorspace from override or detect const char *force_colorspace_str = mlt_properties_get(properties, "force_colorspace"); if (force_colorspace_str) self->yuv_colorspace = mlt_image_colorspace_id(force_colorspace_str); if (!force_colorspace_str || self->yuv_colorspace == mlt_colorspace_invalid) self->yuv_colorspace = av_to_mlt_colorspace(self->video_codec->colorspace, self->video_codec->width, self->video_codec->height); mlt_properties_set_int(properties, "meta.media.colorspace", self->yuv_colorspace); // Get the color transfer characteristic (gamma). const char *force_color_trc_str = mlt_properties_get(properties, "force_color_trc"); if (force_color_trc_str) self->color_trc = mlt_image_color_trc_id(force_color_trc_str); if (!self->color_trc) self->color_trc = av_to_mlt_color_trc(self->video_codec->color_trc); mlt_properties_set_int(properties, "meta.media.color_trc", self->color_trc); // Get the RGB color primaries. self->color_primaries = av_to_mlt_color_primaries(self->video_codec->color_primaries); if (self->color_primaries == mlt_color_pri_none) self->color_primaries = mlt_color_pri_bt709; mlt_properties_set_int(properties, "meta.media.has_b_frames", self->video_codec->has_b_frames); self->full_range = codec_context->color_range == AVCOL_RANGE_JPEG; if (mlt_properties_get(properties, "force_full_range")) { self->full_range = mlt_properties_get_int(properties, "force_full_range"); } else if (mlt_properties_get(properties, "set.force_full_luma")) { // deprecated self->full_range = mlt_properties_get_int(properties, "set.force_full_luma"); } } return self->video_index > -1; } static int pick_video_stream(producer_avformat self) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self->parent); int absolute_index; if (self->video_format && mlt_properties_get(properties, "vstream")) { // Get the relative stream index absolute_index = absolute_stream_index(self->video_format, AVMEDIA_TYPE_VIDEO, mlt_properties_get_int(properties, "vstream")); } else { // Failover to the absolute index absolute_index = mlt_properties_get_int(properties, "video_index"); if (self->video_format) { // Compute the relative stream index mlt_properties_set_int(properties, "vstream", relative_stream_index(self->video_format, AVMEDIA_TYPE_VIDEO, absolute_index)); } } if (mlt_properties_get_int(properties, "video_index") != absolute_index) { // Update the absolute index mlt_properties_set_int(properties, "video_index", absolute_index); self->video_index = absolute_index; } return absolute_index; } /** Set up video handling. */ static void producer_set_up_video(producer_avformat self, mlt_frame frame) { // Get the producer mlt_producer producer = self->parent; // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); // Fetch the video format context AVFormatContext *context = self->video_format; // Get the video stream index int index = mlt_properties_get(properties, "vstream") ? mlt_properties_get_int(properties, "vstream") : mlt_properties_get_int(properties, "video_index"); int unlock_needed = 0; // Reopen the file if necessary if (!context && index > -1) { unlock_needed = 1; pthread_mutex_lock(&self->video_mutex); producer_open(self, mlt_service_profile(MLT_PRODUCER_SERVICE(producer)), mlt_properties_get(properties, "resource"), 0, 0); context = self->video_format; } index = pick_video_stream(self); // Exception handling for video_index if (context && index >= (int) context->nb_streams) { // Get the last video stream for (index = context->nb_streams - 1; index >= 0 && context->streams[index]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO; index--) ; mlt_properties_set_int(properties, "video_index", index); mlt_properties_set_int(properties, "vstream", relative_stream_index(context, AVMEDIA_TYPE_VIDEO, index)); } if (context && index > -1 && context->streams[index]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) { // Invalidate the video stream index = -1; mlt_properties_set_int(properties, "video_index", index); mlt_properties_set_int(properties, "vstream", relative_stream_index(context, AVMEDIA_TYPE_VIDEO, index)); } // Update the video properties if the index changed if (context && index > -1 && index != self->video_index) { // Reset the video properties if the index changed self->video_index = index; mlt_properties_set_int(properties, "_probe_complete", 0); pthread_mutex_lock(&self->open_mutex); avcodec_free_context(&self->video_codec); set_up_discard(self, self->audio_index, index); pthread_mutex_unlock(&self->open_mutex); } // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); // Get the codec if (context && index > -1 && video_codec_init(self, index, properties)) { // Set the frame properties double force_aspect_ratio = mlt_properties_get_double(properties, "force_aspect_ratio"); double aspect_ratio = (force_aspect_ratio > 0.0) ? force_aspect_ratio : mlt_properties_get_double(properties, "aspect_ratio"); // Set the width and height double dar = mlt_profile_dar(mlt_service_profile(MLT_PRODUCER_SERVICE(producer))); double theta = self->autorotate ? get_rotation(properties, self->video_format->streams[index]) : 0.0; if (fabs(theta - 90.0) < 1.0 || fabs(theta - 270.0) < 1.0) { // Workaround 1088 encodings missing cropping info. if (self->video_codec->height == 1088 && dar == 16.0 / 9.0) { mlt_properties_set_int(frame_properties, "width", 1080); mlt_properties_set_int(properties, "meta.media.width", 1080); mlt_properties_set_int(properties, "width", 1080); } else { mlt_properties_set_int(frame_properties, "width", self->video_codec->height); mlt_properties_set_int(properties, "meta.media.width", self->video_codec->height); mlt_properties_set_int(properties, "width", self->video_codec->height); } mlt_properties_set_int(frame_properties, "height", self->video_codec->width); mlt_properties_set_int(properties, "meta.media.height", self->video_codec->width); mlt_properties_set_int(properties, "height", self->video_codec->width); aspect_ratio = (force_aspect_ratio > 0.0) ? force_aspect_ratio : 1.0 / aspect_ratio; mlt_properties_set_double(frame_properties, "aspect_ratio", 1.0 / aspect_ratio); } else { mlt_properties_set_int(frame_properties, "width", self->video_codec->width); mlt_properties_set_int(properties, "meta.media.width", self->video_codec->width); mlt_properties_set_int(properties, "width", self->video_codec->width); // Workaround 1088 encodings missing cropping info. if (self->video_codec->height == 1088 && dar == 16.0 / 9.0) { mlt_properties_set_int(frame_properties, "height", 1080); mlt_properties_set_int(properties, "meta.media.height", 1080); mlt_properties_set_int(properties, "height", 1080); } else { mlt_properties_set_int(frame_properties, "height", self->video_codec->height); mlt_properties_set_int(properties, "meta.media.height", self->video_codec->height); mlt_properties_set_int(properties, "height", self->video_codec->height); } mlt_properties_set_double(frame_properties, "aspect_ratio", aspect_ratio); mlt_properties_set_double(properties, "meta.media.aspect_ratio", aspect_ratio); } mlt_properties_set_int(frame_properties, "colorspace", self->yuv_colorspace); mlt_properties_set_int(frame_properties, "color_trc", self->color_trc); mlt_properties_set_int(frame_properties, "color_primaries", self->color_primaries); // full_range is the current state of frame mlt_properties_set_int(frame_properties, "full_range", self->full_range); // "full_luma" is deprecated but keep this for backwards compatibility for kdenlive mlt_properties_set_int(frame_properties, "full_luma", self->full_range); // meta.media.color_range is the range of this producer per video_index regardless of override mlt_properties_set(properties, "meta.media.color_range", self->full_range ? "full" : "mpeg"); // Add our image operation mlt_frame_push_service(frame, self); mlt_frame_push_get_image(frame, producer_get_image); } else { // If something failed, use test card image mlt_properties_set_int(frame_properties, "test_image", 1); } if (unlock_needed) pthread_mutex_unlock(&self->video_mutex); } static int seek_audio(producer_avformat self, mlt_position position, double timecode) { int paused = 0; pthread_mutex_lock(&self->packets_mutex); // Seek if necessary if (self->seekable && (position != self->audio_expected || self->last_position < 0)) { if (self->last_position == POSITION_INITIAL) { int video_index = self->video_index; if (video_index == -1) video_index = first_video_index(self); if (self->first_pts == AV_NOPTS_VALUE) find_first_pts(self, video_index); } if (position + 1 == self->audio_expected && mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(self->parent), "mute_on_pause")) { // We're paused - silence required paused = 1; } else if (position < self->audio_expected || position - self->audio_expected >= 12) { AVFormatContext *context = self->audio_format; int64_t timestamp = llrint(timecode * AV_TIME_BASE); if (context->start_time != AV_NOPTS_VALUE) timestamp += context->start_time; if (timestamp < 0) timestamp = 0; // Set to the real timecode if (av_seek_frame(context, -1, timestamp, AVSEEK_FLAG_BACKWARD) != 0) paused = 1; // Clear the usage in the audio buffer int i = MAX_AUDIO_STREAMS + 1; while (--i) self->audio_used[i - 1] = 0; } } pthread_mutex_unlock(&self->packets_mutex); return paused; } static int sample_bytes(AVCodecContext *context) { return av_get_bytes_per_sample(context->sample_fmt); } static void planar_to_interleaved( uint8_t *dest, AVFrame *src, int samples, int channels, int bytes_per_sample) { int s, c; for (s = 0; s < samples; s++) { for (c = 0; c < channels; c++) { if (c < AV_NUM_DATA_POINTERS) memcpy(dest, &src->data[c][s * bytes_per_sample], bytes_per_sample); dest += bytes_per_sample; } } } static int decode_audio(producer_avformat self, int *ignore, const AVPacket *pkt, int samples, double timecode, double fps) { // Fetch the audio_format AVFormatContext *context = self->audio_format; // Get the current stream index int index = pkt->stream_index; // Get codec context AVCodecContext *codec_context = self->audio_codec[index]; // Obtain the audio buffers uint8_t *audio_buffer = self->audio_buffer[index]; #if HAVE_FFMPEG_CH_LAYOUT int channels = codec_context->ch_layout.nb_channels; #else int channels = codec_context->channels; #endif int audio_used = self->audio_used[index]; int audio_used_at_start = audio_used; int ret = 0; int discarded = 1; int sizeof_sample = sample_bytes(codec_context); // Decode the audio if (!self->audio_frame) self->audio_frame = av_frame_alloc(); else av_frame_unref(self->audio_frame); int error = avcodec_send_packet(codec_context, pkt); mlt_log_debug(MLT_PRODUCER_SERVICE(self->parent), "decoded audio packet with size %d => %d\n", pkt->size, error); if (!ignore_send_packet_result(error)) { mlt_log_warning(MLT_PRODUCER_SERVICE(self->parent), "audio avcodec_send_packet failed with %d\n", error); } else while (!error) { error = avcodec_receive_frame(codec_context, self->audio_frame); if (error) { if (error != AVERROR(EAGAIN)) { mlt_log_warning(MLT_PRODUCER_SERVICE(self->parent), "audio decoding error %d\n", error); } } else { // Figure out how many samples will be needed after resampling int convert_samples = self->audio_frame->nb_samples; #if HAVE_FFMPEG_CH_LAYOUT channels = codec_context->ch_layout.nb_channels; #else channels = codec_context->channels; #endif ret += convert_samples * channels * sizeof_sample; // Resize audio buffer to prevent overflow if ((audio_used + convert_samples) * channels * sizeof_sample > self->audio_buffer_size[index]) { self->audio_buffer_size[index] = (audio_used + convert_samples * 2) * channels * sizeof_sample; audio_buffer = self->audio_buffer[index] = mlt_pool_realloc(audio_buffer, self->audio_buffer_size[index]); } uint8_t *dest = &audio_buffer[audio_used * channels * sizeof_sample]; switch (codec_context->sample_fmt) { case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_FLTP: planar_to_interleaved(dest, self->audio_frame, convert_samples, channels, sizeof_sample); break; default: { int data_size = av_samples_get_buffer_size(NULL, channels, self->audio_frame->nb_samples, codec_context->sample_fmt, 1); // Straight copy to audio buffer memcpy(dest, self->audio_frame->data[0], data_size); } } audio_used += convert_samples; discarded = 0; } } // Handle ignore if (*ignore > 0 && audio_used) { int n = FFMIN(audio_used, *ignore); *ignore -= n; audio_used -= n; audio_used_at_start -= n; memmove(audio_buffer, &audio_buffer[n * channels * sizeof_sample], audio_used * channels * sizeof_sample); } // If we're behind, ignore this packet // Skip this on non-seekable, audio-only inputs. if (!discarded && pkt->pts >= 0 && (self->seekable || self->video_format) && *ignore == 0 && audio_used > samples / 2) { double timebase = av_q2d(context->streams[index]->time_base); int64_t pts_offset = lrint((double) audio_used_at_start / timebase / (double) codec_context->sample_rate); int64_t pts = pkt->pts - pts_offset; if (self->first_pts != AV_NOPTS_VALUE && self->video_index != -1) pts -= av_rescale_q(self->first_pts, context->streams[self->video_index]->time_base, context->streams[index]->time_base); else if (self->first_pts != AV_NOPTS_VALUE) pts -= self->first_pts; else if (context->start_time != AV_NOPTS_VALUE && self->video_index != -1) pts -= av_rescale_q(context->start_time, AV_TIME_BASE_Q, context->streams[index]->time_base); int64_t int_position = llrint(timebase * pts * fps); int64_t req_position = llrint(timecode * fps); int64_t req_pts = floor(timecode / timebase); mlt_log_debug(MLT_PRODUCER_SERVICE(self->parent), "A pkt.pts %" PRId64 " pkt->dts %" PRId64 " req_pos %" PRId64 " cur_pos %" PRId64 " pkt_pos %" PRId64 "\n", pkt->pts, pkt->dts, req_position, self->current_position, int_position); if (self->seekable || int_position > 0) { int64_t ahead_threshold = 2; if (codec_context->codec_id == AV_CODEC_ID_WMAPRO) { // WMAPro needs more tolerance for sync detection ahead_threshold = 4; } if (req_pts > pts) { // We are behind, so skip some int sample_error_margin = ceil(timebase * codec_context->sample_rate); int sample_delta = lrint(timebase * (req_pts - pts) * codec_context->sample_rate); if (sample_delta > sample_error_margin) { *ignore = sample_delta; } } else if (self->audio_index != INT_MAX && int_position > req_position + ahead_threshold && !self->is_audio_synchronizing) { // We are ahead, so seek backwards some more. // Supply -1 as the position to defeat the checks needed by for the other // call to seek_audio() at the beginning of producer_get_audio(). Otherwise, // more often than not, req_position will equal audio_expected. seek_audio(self, -1, timecode - 1.0); self->is_audio_synchronizing = 1; } } // Cancel the find_first_pts() in seek_audio() if (self->video_index == -1 && self->last_position == POSITION_INITIAL) self->last_position = int_position; } self->audio_used[index] = audio_used; return ret; } /** Get the audio from a frame. */ static int producer_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Get the producer producer_avformat self = mlt_frame_pop_audio(frame); pthread_mutex_lock(&self->audio_mutex); // Obtain the frame number of this frame mlt_position position = mlt_frame_original_position(frame); int paused = position + 1 == self->audio_expected && mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(self->parent), "mute_on_pause"); if (!paused) { // Check the audio cache if not paused if (!self->audio_cache) { init_cache(MLT_PRODUCER_PROPERTIES(self->parent), &self->audio_cache); } else { mlt_frame original = mlt_cache_get_frame(self->audio_cache, position); if (original) { mlt_frame_get_audio(original, buffer, format, frequency, channels, samples); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "avformat.audio_cache", original, 0, (mlt_destructor) mlt_frame_close, NULL); mlt_properties_pass_property(MLT_FRAME_PROPERTIES(frame), MLT_FRAME_PROPERTIES(original), "channel_layout"); goto done_get_audio; } } } // Calculate the real time code double real_timecode = producer_time_of_frame(self->parent, position); // Get the producer fps double fps = mlt_producer_get_fps(self->parent); if (mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "producer_consumer_fps")) fps = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "producer_consumer_fps"); // Number of frames to ignore (for ffwd) int ignore[MAX_AUDIO_STREAMS] = {0}; // Flag for paused (silence) double timecode = self->audio_expected > 0 ? real_timecode : FFMAX(real_timecode - 0.25, 0.0); paused = seek_audio(self, position, timecode); // Initialize ignore for all streams from the seek return value int i = MAX_AUDIO_STREAMS; while (i--) ignore[i] = ignore[0]; // Fetch the audio_format AVFormatContext *context = self->audio_format; if (!context) goto exit_get_audio; int sizeof_sample = sizeof(int16_t); // Determine the tracks to use int index = self->audio_index; int index_max = self->audio_index + 1; if (self->audio_index == INT_MAX) { index = 0; index_max = FFMIN(MAX_AUDIO_STREAMS, context->nb_streams); *channels = self->total_channels; *samples = mlt_audio_calculate_frame_samples(fps, self->max_frequency, position); *frequency = self->max_frequency; } // Initialize the buffers for (; index < index_max && index < MAX_AUDIO_STREAMS; index++) { // Get codec context AVCodecContext *codec_context = self->audio_codec[index]; if (codec_context && !self->audio_buffer[index]) { #if !HAVE_FFMPEG_CH_LAYOUT if (self->audio_index != INT_MAX && !mlt_properties_get(MLT_PRODUCER_PROPERTIES(self->parent), "request_channel_layout")) codec_context->request_channel_layout = av_get_default_channel_layout(*channels); #endif sizeof_sample = sample_bytes(codec_context); // Check for audio buffer and create if necessary self->audio_buffer_size[index] = MAX_AUDIO_FRAME_SIZE * sizeof_sample; self->audio_buffer[index] = mlt_pool_alloc(self->audio_buffer_size[index]); // Check for decoder buffer and create if necessary self->decode_buffer[index] = av_malloc(self->audio_buffer_size[index]); } } // Get the audio if required if (!paused && *frequency > 0) { int ret = 0; int got_audio = 0; AVPacket pkt; mlt_channel_layout mlt_layout = mlt_channel_auto; av_init_packet(&pkt); // Caller requested number samples based on requested sample rate. if (self->audio_index != INT_MAX) *samples = mlt_audio_calculate_frame_samples(fps, self->audio_codec[self->audio_index] ->sample_rate, position); while (ret >= 0 && !got_audio) { // Check if the buffer already contains the samples required if (self->audio_index != INT_MAX && self->audio_used[self->audio_index] >= *samples && ignore[self->audio_index] == 0) { got_audio = 1; break; } else if (self->audio_index == INT_MAX) { // Check if there is enough audio for all streams got_audio = 1; for (index = 0; got_audio && index < index_max; index++) if ((self->audio_codec[index] && self->audio_used[index] < *samples) || ignore[index]) got_audio = 0; if (got_audio) break; } // Read a packet pthread_mutex_lock(&self->packets_mutex); if (mlt_deque_count(self->apackets)) { AVPacket *tmp = (AVPacket *) mlt_deque_pop_front(self->apackets); av_packet_ref(&pkt, tmp); av_packet_free(&tmp); } else { ret = av_read_frame(context, &pkt); if (ret >= 0 && !self->seekable && pkt.stream_index == self->video_index) { mlt_deque_push_back(self->vpackets, av_packet_clone(&pkt)); } else if (ret == AVERROR(EAGAIN)) { ret = 0; pthread_mutex_unlock(&self->packets_mutex); continue; } else if (ret < 0) { mlt_producer producer = self->parent; mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); if (ret != AVERROR_EOF) mlt_log_verbose(MLT_PRODUCER_SERVICE(producer), "av_read_frame returned error %d inside get_audio\n", ret); if (!self->seekable && mlt_properties_get_int(properties, "reconnect")) { // Try to reconnect to live sources by closing context and codecs, // and letting next call to get_frame() reopen. prepare_reopen(self); pthread_mutex_unlock(&self->packets_mutex); goto exit_get_audio; } if (!self->seekable && mlt_properties_get_int(properties, "exit_on_disconnect")) { mlt_log_fatal(MLT_PRODUCER_SERVICE(producer), "Exiting with error due to disconnected source.\n"); exit(EXIT_FAILURE); } } } pthread_mutex_unlock(&self->packets_mutex); // We only deal with audio from the selected audio index index = pkt.stream_index; if (index < MAX_AUDIO_STREAMS && ret >= 0 && pkt.data && pkt.size > 0 && (index == self->audio_index || (self->audio_index == INT_MAX && context->streams[index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))) { ret = decode_audio(self, &ignore[index], &pkt, *samples, real_timecode, fps); } if (self->seekable || index != self->video_index) av_packet_unref(&pkt); } self->is_audio_synchronizing = 0; // Set some additional return values *format = mlt_audio_s16; if (self->audio_index != INT_MAX) { AVCodecContext *codec_ctx = self->audio_codec[self->audio_index]; index = self->audio_index; *frequency = codec_ctx->sample_rate; *format = pick_audio_format(codec_ctx->sample_fmt); sizeof_sample = sample_bytes(codec_ctx); #if HAVE_FFMPEG_CH_LAYOUT *channels = codec_ctx->ch_layout.nb_channels; if (av_channel_layout_check(&codec_ctx->ch_layout)) { mlt_layout = av_channel_layout_to_mlt(&codec_ctx->ch_layout); } else { AVChannelLayout ch_layout; av_channel_layout_default(&ch_layout, codec_ctx->ch_layout.nb_channels); mlt_layout = av_channel_layout_to_mlt(&ch_layout); av_channel_layout_uninit(&ch_layout); } #else *channels = codec_ctx->channels; if (codec_ctx->channel_layout == 0) mlt_layout = av_channel_layout_to_mlt( av_get_default_channel_layout(codec_ctx->channels)); else mlt_layout = av_channel_layout_to_mlt(codec_ctx->channel_layout); #endif } else if (self->audio_index == INT_MAX) { mlt_layout = mlt_channel_independent; for (index = 0; index < index_max; index++) if (self->audio_codec[index]) { // XXX: This only works if all audio tracks have the same sample format. *format = pick_audio_format(self->audio_codec[index]->sample_fmt); sizeof_sample = sample_bytes(self->audio_codec[index]); break; } } mlt_properties_set(MLT_FRAME_PROPERTIES(frame), "channel_layout", mlt_audio_channel_layout_name(mlt_layout)); // Allocate and set the frame's audio buffer int size = mlt_audio_format_size(*format, *samples, *channels); *buffer = mlt_pool_alloc(size); mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); // Interleave tracks if audio_index=all if (self->audio_index == INT_MAX) { uint8_t *dest = *buffer; int i; for (i = 0; i < *samples; i++) { for (index = 0; index < index_max; index++) if (self->audio_codec[index]) { #if HAVE_FFMPEG_CH_LAYOUT int current_channels = self->audio_codec[index]->ch_layout.nb_channels; #else int current_channels = self->audio_codec[index]->channels; #endif uint8_t *src = self->audio_buffer[index] + i * current_channels * sizeof_sample; memcpy(dest, src, current_channels * sizeof_sample); dest += current_channels * sizeof_sample; } } for (index = 0; index < index_max; index++) if (self->audio_codec[index] && self->audio_used[index] >= *samples) { #if HAVE_FFMPEG_CH_LAYOUT int current_channels = self->audio_codec[index]->ch_layout.nb_channels; #else int current_channels = self->audio_codec[index]->channels; #endif uint8_t *src = self->audio_buffer[index] + *samples * current_channels * sizeof_sample; self->audio_used[index] -= *samples; memmove(self->audio_buffer[index], src, self->audio_used[index] * current_channels * sizeof_sample); } } // Copy a single track to the output buffer else { index = self->audio_index; uint8_t silence = *format == mlt_audio_u8 ? 0x80 : 0; // Now handle the audio if we have enough if (self->audio_used[index] > 0) { uint8_t *src = self->audio_buffer[index]; // copy samples from audio_buffer size = self->audio_used[index] < *samples ? self->audio_used[index] : *samples; memcpy(*buffer, src, size * *channels * sizeof_sample); // supply the remaining requested samples as silence if (*samples > self->audio_used[index]) memset((char *) (*buffer) + size * *channels * sizeof_sample, silence, (*samples - self->audio_used[index]) * *channels * sizeof_sample); // reposition the samples within audio_buffer self->audio_used[index] -= size; memmove(src, src + size * *channels * sizeof_sample, self->audio_used[index] * *channels * sizeof_sample); } else { // Otherwise fill with silence memset(*buffer, silence, *samples * *channels * sizeof_sample); } } mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_samples", *samples); if (self->audio_cache) { mlt_cache_put_frame_audio(self->audio_cache, frame); } } else { exit_get_audio: if (*format == mlt_audio_none) { *format = mlt_audio_s16; } // Get silence and don't touch the context mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } done_get_audio: // Regardless of speed (other than paused), we expect to get the next frame if (!paused) self->audio_expected = position + 1; pthread_mutex_unlock(&self->audio_mutex); return 0; } /** Initialize the audio codec context. */ static int audio_codec_init(producer_avformat self, int index, mlt_properties properties) { // Initialise the codec if necessary if (!self->audio_codec[index]) { // Get the video stream AVStream *stream = self->audio_format->streams[index]; // Get codec context AVCodecParameters *codec_params = stream->codecpar; // Find the codec const AVCodec *codec = avcodec_find_decoder(codec_params->codec_id); if (mlt_properties_get(properties, "acodec")) { if (!(codec = avcodec_find_decoder_by_name(mlt_properties_get(properties, "acodec")))) codec = avcodec_find_decoder(codec_params->codec_id); } // Setup the codec context AVCodecContext *codec_context = avcodec_alloc_context3(codec); if (!codec_context) { mlt_log_error(MLT_PRODUCER_SERVICE(self->parent), "Failed to allocate the decoder context for audio stream #%d\n", index); self->audio_index = -1; return 0; } int ret = avcodec_parameters_to_context(codec_context, codec_params); if (ret < 0) { mlt_log_error( MLT_PRODUCER_SERVICE(self->parent), "Failed to copy decoder parameters to input decoder context for audio stream #%d\n", index); self->audio_index = -1; return 0; } // If we don't have a codec and we can't initialise it, we can't do much more... pthread_mutex_lock(&self->open_mutex); if (codec && avcodec_open2(codec_context, codec, NULL) >= 0) { // Now store the codec with its destructor if (self->audio_codec[index]) avcodec_free_context(&self->audio_codec[index]); self->audio_codec[index] = codec_context; self->audio_index = index; } else { // Remember that we can't use self later self->audio_index = -1; } pthread_mutex_unlock(&self->open_mutex); // Process properties as AVOptions apply_properties(codec_context, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM); if (codec && codec->priv_class && codec_context->priv_data) apply_properties(codec_context->priv_data, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM); } return self->audio_codec[index] && self->audio_index > -1; } static int pick_audio_stream(producer_avformat self) { AVFormatContext *context = self->audio_format; mlt_properties properties = MLT_PRODUCER_PROPERTIES(self->parent); int absolute_index = -1; // Handle all audio tracks if (self->audio_index > -1) { if (mlt_properties_get(properties, "audio_index") && !strcmp(mlt_properties_get(properties, "audio_index"), "all")) { absolute_index = INT_MAX; mlt_properties_set(properties, "astream", "all"); } if (mlt_properties_get(properties, "astream") && !strcmp(mlt_properties_get(properties, "astream"), "all")) { absolute_index = INT_MAX; mlt_properties_set(properties, "audio_index", "all"); } } // Are both audio_index != all and astream != all? if (absolute_index == -1) { if (context && mlt_properties_get(properties, "astream")) { // Get the relative stream index absolute_index = absolute_stream_index(context, AVMEDIA_TYPE_AUDIO, mlt_properties_get_int(properties, "astream")); } else { // Failover to the absolute index absolute_index = mlt_properties_get_int(properties, "audio_index"); if (context) { // Compute the relative stream index mlt_properties_set_int(properties, "astream", relative_stream_index(context, AVMEDIA_TYPE_AUDIO, absolute_index)); } } if (mlt_properties_get_int(properties, "audio_index") != absolute_index) { // Update the absolute index mlt_properties_set_int(properties, "audio_index", absolute_index); self->audio_index = absolute_index; } } // Exception handling for audio_index if (context && absolute_index >= (int) context->nb_streams && absolute_index < INT_MAX) { for (absolute_index = context->nb_streams - 1; absolute_index >= 0 && context->streams[absolute_index]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO; absolute_index--) ; mlt_properties_set_int(properties, "audio_index", absolute_index); mlt_properties_set_int(properties, "astream", relative_stream_index(context, AVMEDIA_TYPE_AUDIO, absolute_index)); } if (context && absolute_index > -1 && absolute_index < INT_MAX && context->streams[absolute_index]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) { absolute_index = self->audio_index; mlt_properties_set_int(properties, "audio_index", absolute_index); mlt_properties_set_int(properties, "astream", relative_stream_index(context, AVMEDIA_TYPE_AUDIO, absolute_index)); } if (context && absolute_index > -1 && absolute_index < INT_MAX && pick_audio_format(context->streams[absolute_index]->codecpar->format) == mlt_audio_none) { absolute_index = -1; } return absolute_index; } /** Set up audio handling. */ static void producer_set_up_audio(producer_avformat self, mlt_frame frame) { // Get the producer mlt_producer producer = self->parent; // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); // Fetch the audio format context AVFormatContext *context = self->audio_format; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); // Get the audio index int index = mlt_properties_get(properties, "astream") ? mlt_properties_get_int(properties, "astream") : mlt_properties_get_int(properties, "audio_index"); // Reopen the file if necessary if (!context && self->audio_index > -1 && index > -1) { producer_open(self, mlt_service_profile(MLT_PRODUCER_SERVICE(producer)), mlt_properties_get(properties, "resource"), 1, 0); context = self->audio_format; mlt_properties_set_int(properties, "_probe_complete", 0); } index = pick_audio_stream(self); // Update the audio properties if the index changed if (context && self->audio_index > -1 && index != self->audio_index) { self->audio_index = index; pthread_mutex_lock(&self->open_mutex); unsigned i = 0; int index_max = FFMIN(MAX_AUDIO_STREAMS, context->nb_streams); for (i = 0; i < index_max; i++) { avcodec_free_context(&self->audio_codec[i]); } set_up_discard(self, index, self->video_index); mlt_cache_close(self->audio_cache); self->audio_cache = NULL; pthread_mutex_unlock(&self->open_mutex); } // Get the codec(s) if (context && index == INT_MAX) { unsigned int index; mlt_properties_set_int(frame_properties, "audio_frequency", self->max_frequency); mlt_properties_set_int(frame_properties, "audio_channels", self->total_channels); for (index = 0; index < context->nb_streams && index < MAX_AUDIO_STREAMS; index++) { if (context->streams[index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) audio_codec_init(self, index, properties); } self->audio_index = INT_MAX; } else if (context && index > -1 && index < MAX_AUDIO_STREAMS && audio_codec_init(self, index, properties)) { mlt_properties_set_int(frame_properties, "audio_frequency", self->audio_codec[index]->sample_rate); mlt_properties_set_int(frame_properties, "audio_channels", #if HAVE_FFMPEG_CH_LAYOUT self->audio_codec[index]->ch_layout.nb_channels); #else self->audio_codec[index]->channels); #endif } if (context && index > -1) { // Add our audio operation mlt_frame_push_audio(frame, self); mlt_frame_push_audio(frame, producer_get_audio); } } /** Our get frame implementation. */ static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Access the private data (void) index; // unused mlt_service service = MLT_PRODUCER_SERVICE(producer); mlt_cache_item cache_item = mlt_service_cache_get(service, "producer_avformat"); producer_avformat self = mlt_cache_item_data(cache_item, NULL); // If cache miss if (!self) { self = calloc(1, sizeof(struct producer_avformat_s)); self->parent = producer; mlt_service_cache_put(service, "producer_avformat", self, 0, (mlt_destructor) producer_avformat_close); cache_item = mlt_service_cache_get(service, "producer_avformat"); } // Create an empty frame *frame = mlt_frame_init(service); if (*frame == NULL) { mlt_cache_item_close(cache_item); return 1; } mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); mlt_properties_set_data(frame_properties, "avformat_cache", cache_item, 0, (mlt_destructor) mlt_cache_item_close, NULL); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Set up the video producer_set_up_video(self, *frame); // Set up the audio producer_set_up_audio(self, *frame); mlt_properties_set_int(frame_properties, "format", mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(producer), "format")); // Set the position of this producer mlt_position position = mlt_producer_frame(producer); mlt_properties_set_position(frame_properties, "original_position", position); if (!mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(producer), "_probe_complete") && self->video_index < 0) { // If video index is valid, get_image() must be called before the probe is complete mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.width"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.height"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.aspect_ratio"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.color_range"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.aspect_ratio"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.progressive"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.sample_aspect_num"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.sample_aspect_den"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.top_field_first"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.variable_frame_rate"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.colorspace"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.color_trc"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "meta.media.has_b_frames"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "aspect_ratio"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "width"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "height"); mlt_properties_clear(MLT_PRODUCER_PROPERTIES(producer), "format"); mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(producer), "_probe_complete", 1); } // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } static int producer_probe(mlt_producer producer) { int error = 0; mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); if (mlt_properties_get_int(properties, "_probe_complete")) { return error; } if (!mlt_properties_exists(properties, "_probe_complete")) { // If metadata was loaded from XML, no need to probe int video_in_use = mlt_properties_get_int(properties, "vstream") > -1; if (video_in_use && mlt_properties_exists(properties, "meta.media.progressive")) { return error; } else if (!video_in_use && mlt_properties_exists(properties, "meta.media.nb_streams")) { return error; } } mlt_frame fr = NULL; mlt_position save_position = mlt_producer_position(producer); // Call producer_get_frame() directly so that the underlying service will not attach any normalizers mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); error = producer_get_frame(producer, &fr, 0); mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); if (!error && fr && mlt_properties_get_int(properties, "vstream") > -1) { // Some video metadata is not exposed until after the first get_image call. uint8_t *buffer = NULL; mlt_image_format fmt = mlt_image_none; int w = 0; int h = 0; error = mlt_frame_get_image(fr, &buffer, &fmt, &w, &h, 0); } mlt_frame_close(fr); mlt_producer_seek(producer, save_position); return error; } static void producer_avformat_close(producer_avformat self) { mlt_log_debug(NULL, "producer_avformat_close\n"); pthread_mutex_lock(&self->close_mutex); if (self->parent && self->parent->close) mlt_events_disconnect(MLT_PRODUCER_PROPERTIES(self->parent), self); pthread_mutex_unlock(&self->close_mutex); // Cleanup av contexts av_packet_unref(&self->pkt); av_frame_free(&self->video_frame); av_frame_free(&self->audio_frame); av_buffer_unref(&self->hwaccel.device_ctx); if (self->is_mutex_init) pthread_mutex_lock(&self->open_mutex); int i; for (i = 0; i < MAX_AUDIO_STREAMS; i++) { mlt_pool_release(self->audio_buffer[i]); av_free(self->decode_buffer[i]); avcodec_free_context(&self->audio_codec[i]); } avcodec_free_context(&self->video_codec); // Close the file if (self->is_thread_init) { pthread_mutex_lock(&self->packets_mutex); self->packets_thread_stop = 1; pthread_cond_signal(&self->packets_cond); pthread_mutex_unlock(&self->packets_mutex); pthread_join(self->packets_thread, NULL); pthread_cond_destroy(&self->packets_cond); } if (self->dummy_context) avformat_close_input(&self->dummy_context); if (self->seekable && self->audio_format) avformat_close_input(&self->audio_format); if (self->video_format) avformat_close_input(&self->video_format); if (self->is_mutex_init) pthread_mutex_unlock(&self->open_mutex); avfilter_graph_free(&self->vfilter_graph); self->vfilter_in = NULL; self->vfilter_out = NULL; avfilter_graph_free(&self->hwaccel.filter_graph); self->hwaccel.filter_in = NULL; self->hwaccel.filter_out = NULL; self->hwaccel.filters_initialized = 0; // Cleanup caches. mlt_cache_close(self->image_cache); mlt_cache_close(self->audio_cache); if (self->last_good_frame) mlt_frame_close(self->last_good_frame); // Cleanup the mutexes if (self->is_mutex_init) { pthread_mutex_destroy(&self->audio_mutex); pthread_mutex_destroy(&self->video_mutex); pthread_mutex_destroy(&self->packets_mutex); pthread_mutex_destroy(&self->open_mutex); pthread_mutex_destroy(&self->close_mutex); } // Cleanup the packet queues AVPacket *pkt; if (self->apackets) { while ((pkt = mlt_deque_pop_back(self->apackets))) { av_packet_free(&pkt); } mlt_deque_close(self->apackets); self->apackets = NULL; } if (self->vpackets) { while ((pkt = mlt_deque_pop_back(self->vpackets))) { av_packet_free(&pkt); } mlt_deque_close(self->vpackets); self->vpackets = NULL; } free(self); } static void producer_close(mlt_producer parent) { // Remove this instance from the cache mlt_service_cache_purge(MLT_PRODUCER_SERVICE(parent)); // Detach the producer_avformat struct mlt_cache_item cache_item = mlt_service_cache_get(MLT_PRODUCER_SERVICE(parent), "producer_avformat"); producer_avformat self = mlt_cache_item_data(cache_item, NULL); if (self) { pthread_mutex_lock(&self->close_mutex); self->parent = NULL; parent->close = NULL; pthread_mutex_unlock(&self->close_mutex); } else { parent->close = NULL; } mlt_cache_item_close(cache_item); // Close the parent mlt_producer_close(parent); // Free the memory free(parent); } mlt-7.38.0/src/modules/avformat/producer_avformat.yml000664 000000 000000 00000023317 15172202314 022713 0ustar00rootroot000000 000000 schema_version: 0.3 type: producer identifier: avformat title: FFmpeg Reader version: 6 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.ffmpeg.org/ tags: - Audio - Video description: Read an audio and/or video file using FFmpeg. notes: > This service uses mlt_cache to prevent many simultaneous, open instances of libavformat and libavcodec contexts, file handles, and threads in memory at the same time. Not only does it save on RAM usage, but kernels enforce maximum open file handles and threads per process. Without caching, large projects could easily run into these limits. The default producer cache size is in mlt_cache at size 4. When using mlt_multitrack, the size is adjusted automatically to be the number of tracks plus one if at least 4. This makes it rather dynamic and automatic; however, there are some service graph configurations or authoring scenarios that do not exclusively use the multitrack for multi-layer operations and may need a larger avformat producer cache size. One can set the environment variable MLT_AVFORMAT_PRODUCER_CACHE to a number to override and increase the size of this cache (or to lower it for limited use cases and seeking to minimize RAM). One can set the environment variables MLT_AVFORMAT_HWACCEL, MLT_AVFORMAT_HWACCEL_DEVICE, and MLT_AVFORMAT_HWACCEL_PPS to affect the usage of hwaccel decoding globally. MLT_AVFORMAT_HWACCEL_PPS sets a threshold for pixels per second (width * height * fps); hwaccel is only used when the video's PPS is less than or equal to this threshold. You should not use MLT_AVFORMAT_HWACCEL_PPS in conjunction with the consumer scale property. Hardware decoding gracefully falls back to software decoding. bugs: - Audio sync discrepancy with some content. - Not all libavformat supported formats are seekable. - > Seeking is not always accurate. Sometimes it doesn't seek to I-frames so you may get junk for a few frames. - > More than 2 channels of audio more than 16 bits is not supported. parameters: - identifier: resource argument: yes title: File/URL type: string description: | A file name specification or URL in the form: [{protocol}|{format}]:{resource}[?{format-parameter}[&{format-parameter}...]] For example, video4linux2:/dev/video1?width=320&height=240 Note: on the bash command line, '&' must be escaped as '\&'. If you need '?' in the resource name it must be escaped as '\?'. Use 'f-list' to see a list of supported file formats. Use 'vcodec-list' to see a list of supported video decoders. Use 'acodec-list' to see a list of supported audio decoders. readonly: no required: yes mutable: no widget: fileopen # could provide a button to use a file-open dialog - identifier: audio_index title: Audio index type: integer description: > Choose the absolute stream index of audio stream to use (-1 is off). When this value is equal to the maximum size of a 32-bit signed integer or the string "all" then all audio tracks are coalesced into a bundle of channels on one audio track. readonly: no mutable: no minimum: -1 default: 0 widget: spinner - identifier: astream title: Audio Stream type: integer description: > Choose the relative stream index (n-th) of audio to use (-1 is off). When this value is equal to the maximum size of a 32-bit signed integer or the string "all" then all audio tracks are coalesced into a bundle of channels on one audio track. This property has a higher priority than audio_index. readonly: no mutable: no minimum: -1 default: 0 widget: spinner - identifier: video_index title: Video index type: integer description: Choose the absolute index of video stream to use (-1 is off) readonly: no mutable: no minimum: -1 default: 0 widget: spinner - identifier: vstream title: Video Stream type: integer description: > Choose the relative stream index (n-th) of video to use (-1 is off). This property has a higher priority than video_index. readonly: no mutable: no minimum: -1 default: 0 widget: spinner - identifier: threads title: Decoding threads type: integer description: Choose the number of threads to use in the decoder(s) readonly: no mutable: no minimum: 0 maximum: 4 default: 1 widget: spinner unit: threads # the unit is a label that appears after the widget - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio readonly: no mutable: yes minimum: 0.001 # just a UI suggestion maximum: 9.999 # just a suggestion # no default property means it should be blank in the UI and not applied unless provided - identifier: source_fps title: Frame rate type: float scale: 2 # scale is the number of digits to display after the decimal point description: the framerate of the resource readonly: yes unit: frames/second - identifier: seekable title: Supports seeking type: boolean description: if the resource can seek readonly: yes - identifier: width title: Width type: integer unit: pixels readonly: yes - identifier: height title: Height type: integer unit: pixels readonly: yes - identifier: noimagecache title: Disable image caching type: boolean widget: checkbox - identifier: cache title: Number of images cache type: integer description: > By default, this producer caches images to facilitate YADIF deinterlace, which needs previous and next frames. Also, caching helps with frame- stepping within a player. The default number of images cached is supplied by the MLT framework, which is currently 4, but you can override it with this property. You can also disable caching by setting it to 0. If you are using parallel processing with YADIF deinterlacing, then you might need to increase caching to prevent inadvertent backward seeks. One can also set this value globally for all instances of avformat by setting the environment variable MLT_AVFORMAT_CACHE. - identifier: force_progressive title: Force progressive description: When provided, this overrides the detection of progressive video. type: boolean widget: checkbox - identifier: force_tff title: Force top field first description: When provided, this overrides the detected field order of interlaced video. type: boolean widget: checkbox - identifier: force_fps title: Force frame rate description: When provided, this attempts to override the detected frame rate of the video. type: float widget: checkbox - identifier: force_full_range title: Force Full Range Color description: When provided, this overrides the detected color range of the video (Y'CbCr only). type: boolean - identifier: force_colorspace title: Force colorspace description: When provided, this overrides the detected colorspace of the video (Y'CbCr only). type: integer values: - 240 # SMPTE 240M - 601 # ITU-R BT.601 - 709 # ITU-R BT.709 - identifier: force_color_trc title: Force transfer characteristic description: > When provided, this overrides the detected gamma transfer of the video. See libavcodec AVColorTransferCharacteristic for values. type: integer - identifier: video_delay title: Video delay description: > Manually adjust A/V synchronization. A negative value advances the video instead of delaying it. type: float default: 0 unit: seconds widget: timecode - identifier: reconnect title: Automatically reconnect? description: > Whether to attempt to automatically reconnect to a live source when a read failure occurs. type: boolean widget: checkbox - identifier: exit_on_disconnect title: Exit upon disconnection? description: > When this is set, the program will terminate with an error if a live source becomes disconnected. type: boolean widget: checkbox - identifier: mute_on_pause (*DEPRECATED*) title: Mute on Pause description: > Mute the audio when the same frame is requested twice in a row. type: boolean default: 0 widget: checkbox - identifier: seek_threshold title: Seek Threshold description: > Number of frames required to trigger a seek forward rather than continuous read when reading forward. This can be useful to optimize some applications which rely on accelerated reading of a media file or in cases where lack of I-frames cause libavformat to face issues in seeking and where user tries to minimize the number of seek calls. type: integer default: 64 unit: frames - identifier: autorotate title: Auto-rotate? type: boolean description: > Whether to automatically compensate image orientation if the file is tagged with appropriate metadata and this resource has video/images. default: 1 - identifier: rotate title: Rotation Override description: > While there is automatic rotation for orientation, some files are missing the metadata for it. This provides an override. It can only rotate by a multiple of 90 degrees. type: integer unit: degrees - identifier: filtergraph title: Filtergraph type: string description: Filtergraph to apply to resource. Uses libavfilter syntax. - identifier: lut title: LUT File type: string description: Path to a LUT file applied to the resource using the ffmpeg lut3d filter. mlt-7.38.0/src/modules/avformat/resolution_scale.yml000664 000000 000000 00000001431 15172202314 022534 0ustar00rootroot000000 000000 # This files contains a list of plugin parameters that are sensitive to the # image resolution. This only works for parameters with type F0R_PARAM_DOUBLE. # plugin: # parameter#: scale factor in addition to mlt_frame_resolution_scale delogo: x: 1.0 y: 1.0 w: 1.0 h: 1.0 drawbox: x: 1.0 y: 1.0 w: 1.0 h: 1.0 t: 1.0 drawgrid: x: 1.0 y: 1.0 w: 1.0 h: 1.0 t: 1.0 fillborders: left: 1.0 right: 1.0 top: 1.0 bottom: 1.0 floodfill: x: 1.0 y: 1.0 gblur: sigma: 1.0 sigmaV: 1.0 pad: x: 1.0 y: 1.0 width: 1.0 height: 1.0 perspective: x0: 1.0 y0: 1.0 x1: 1.0 y1: 1.0 x2: 1.0 y2: 1.0 x3: 1.0 y3: 1.0 swaprect: w: 1.0 h: 1.0 x1: 1.0 y1: 1.0 x2: 1.0 y2: 1.0 vignette: x0: 1.0 y0: 1.0 zoompan: x: 1.0 y: 1.0 mlt-7.38.0/src/modules/avformat/yuv_only.txt000664 000000 000000 00000000045 15172202314 021064 0ustar00rootroot000000 000000 fspp smartblur vaguedenoiser unsharp mlt-7.38.0/src/modules/core/000775 000000 000000 00000000000 15172202314 015551 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/core/CMakeLists.txt000664 000000 000000 00000004042 15172202314 020311 0ustar00rootroot000000 000000 add_library(mltcore MODULE consumer_multi.c consumer_null.c factory.c filter_audiochannels.c filter_audioconvert.c filter_audiomap.c filter_audioseam.c filter_audiowave.c filter_autofade.c filter_box_blur.c filter_brightness.c filter_channelcopy.c filter_choppy.c filter_color_transform.c filter_crop.c filter_fieldorder.c filter_gamma.c filter_greyscale.c filter_imageconvert.c filter_luma.c filter_mask_apply.c filter_mask_start.c filter_mirror.c filter_mono.c filter_obscure.c filter_panner.c filter_pillar_echo.c filter_rescale.c filter_resize.c filter_transition.c filter_watermark.c image_proc.c image_proc.h link_timeremap.c producer_blank.c producer_colour.c producer_consumer.c producer_hold.c producer_loader.c producer_melt.c producer_noise.c producer_timewarp.c producer_tone.c transition_composite.c transition_luma.c transition_matte.c transition_mix.c ) file(GLOB YML "*.yml") add_custom_target(Other_core_Files SOURCES ${YML} loader.dict loader.ini ) include(GenerateExportHeader) generate_export_header(mltcore) target_compile_options(mltcore PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltcore PRIVATE mlt Threads::Threads) target_include_directories(mltcore PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) if(MSVC) target_link_libraries(mltcore PRIVATE PThreads4W::PThreads4W) else() target_link_libraries(mltcore PRIVATE m) endif() if(WIN32) target_sources(mltcore PRIVATE ../../win32/fnmatch.c) target_include_directories(mltcore PRIVATE ../../win32) endif() if(CPU_SSE) target_compile_definitions(mltcore PRIVATE USE_SSE) endif() if(CPU_X86_64) target_sources(mltcore PRIVATE composite_line_yuv_sse2_simple.c) target_compile_definitions(mltcore PRIVATE ARCH_X86_64) endif() set_target_properties(mltcore PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltcore LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES ${YML} loader.dict loader.ini DESTINATION ${MLT_INSTALL_DATA_DIR}/core ) mlt-7.38.0/src/modules/core/composite_line_yuv_mmx.S000664 000000 000000 00000006666 15172202314 022510 0ustar00rootroot000000 000000 .file "composite_line_yuv_mmx" .version "01.01" gcc2_compiled.: .data .text .align 16 #if !defined(__MINGW32__) && !defined(__CYGWIN__) .globl composite_line_yuv_mmx .type composite_line_yuv_mmx,@function composite_line_yuv_mmx: #else .globl _composite_line_yuv_mmx _composite_line_yuv_mmx: #endif /* * Arguments * * dest: 8(%ebp) %esi * src: 12(%ebp) * width_src: 16(%ebp) * alpha: 20(%ebp) * weight: 24(%ebp) * luma: 28(%ebp) * softness: 32(%ebp) */ /* * Function call entry */ pushl %ebp movl %esp,%ebp subl $28,%esp pushl %edi pushl %esi pushl %ebx /* Initialise */ movl 8(%ebp), %esi # get dest movl $0, %edx # j = 0 .loop: movl $0xffff, %ecx # a = 255 cmpl $0, 20(%ebp) # if alpha == NULL je .noalpha movl 20(%ebp), %edi # a = alpha[ j ] movb (%edi,%edx), %cl .noalpha: movl %ecx, -24(%ebp) # save ecx movl 24(%ebp), %eax # mix = weight cmpl $0, 28(%ebp) # if luma == NULL je .noluma movl 28(%ebp), %edi # mix = ... movl %edx, %ebx sall $1, %ebx movw (%edi,%ebx), %bx # luma[ j*2 ] cmpl %ebx, %eax jl .luma0 movl %ebx, %ecx addl 32(%ebp), %ecx # + softness cmpl %ecx, %eax jge .luma1 /* TODO: linear interpolate between edges */ subw %bx, %ax sall $8, %eax subw %bx, %cx movl %edx, %ebx divw %cx movl %ebx, %edx jmp .noluma .luma0: movl $0, %eax jmp .noluma .luma1: movl $0xffff, %eax .noluma: shrl $8, %eax movl %edx, %ebx # edx will be destroyed by mulw movl -24(%ebp), %ecx # restore ecx mull %ecx # mix = mix * a... movl %ebx, %edx # restore edx shrl $8, %eax # >>8 andl $0xff, %eax /* put alpha and (1-alpha) into mm0 */ /* 0 aa 0 1-a 0 aa 0 1-a */ /* duplicate word */ movl %eax, %ecx shll $16, %ecx orl %eax, %ecx movd %ecx, %mm1 /* (1 << 16) - mix */ movl $0x000000ff, %ecx subl %eax, %ecx andl $0xff, %ecx /* duplicate word */ movl %ecx, %eax shll $16, %eax orl %eax, %ecx movd %ecx, %mm0 /* unpack words into double words */ punpcklwd %mm1, %mm0 /* put src yuv and dest yuv into mm1 */ /* 0 UVs 0 UVd 0 Ys 0 Yd */ movl 12(%ebp), %edi # get src movb (%edi), %cl shll $8, %ecx movb 1(%edi), %al shll $24, %eax orl %eax, %ecx movb (%esi), %al # get dest orl %eax, %ecx movb 1(%esi), %al shll $16, %eax orl %eax, %ecx movd %ecx, %mm1 punpcklbw %mm4, %mm1 /* alpha composite */ pmaddwd %mm1, %mm0 psrld $8, %mm0 /* store result */ movd %mm0, %eax movb %al, (%esi) pextrw $2, %mm0, %eax movl $128, %eax movb %al, 1(%esi) /* for..next */ addl $1, %edx # j++ cmpl 16(%ebp), %edx # if ( j == width_src ) jge .out addl $2, %esi addl $2, 12(%ebp) jmp .loop .out: emms leal -40(%ebp),%esp popl %ebx popl %esi popl %edi movl %ebp,%esp popl %ebp ret /********************************************/ .align 8 #if !defined(__MINGW32__) && !defined(__CYGWIN__) .globl composite_have_mmx .type composite_have_mmx,@function composite_have_mmx: #else .globl _composite_have_mmx _composite_have_mmx: #endif push %ebx # Check if bit 21 in flags word is writeable pushfl popl %eax movl %eax,%ebx xorl $0x00200000, %eax pushl %eax popfl pushfl popl %eax cmpl %eax, %ebx je .notfound # OK, we have CPUID movl $1, %eax cpuid test $0x00800000, %edx jz .notfound movl $1, %eax jmp .out2 .notfound: movl $0, %eax .out2: popl %ebx ret mlt-7.38.0/src/modules/core/composite_line_yuv_sse2_simple.c000664 000000 000000 00000026421 15172202314 024143 0ustar00rootroot000000 000000 /* * composite_line_yuv_sse2_simple.c * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include const static unsigned char const1[] = { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}; const static unsigned char const2[] = { 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00}; #define LOAD_CONSTS \ "pxor %%xmm0, %%xmm0 \n\t" /* clear zero register */ \ "movdqu (%[const1]), %%xmm9 \n\t" /* load const1 */ \ "movdqu (%[const2]), %%xmm10 \n\t" /* load const2 */ #define LOAD_WEIGHT \ "movd %[weight], %%xmm1 \n\t" /* load weight and decompose */ \ "movlhps %%xmm1, %%xmm1 \n\t" \ "pshuflw $0, %%xmm1, %%xmm1 \n\t" \ "pshufhw $0, %%xmm1, %%xmm1 \n\t" #define LOAD_SRC_A \ "movq (%[src_a]), %%xmm2 \n\t" /* load source alpha */ \ "punpcklbw %%xmm0, %%xmm2 \n\t" /* unpack alpha 8 8-bits alphas to 8 16-bits values */ #define SRC_A_PREMUL \ "pmullw %%xmm1, %%xmm2 \n\t" /* premultiply source alpha */ \ "psrlw $8, %%xmm2 \n\t" #define DST_A_CALC \ /* DSTa = DSTa + (SRCa * (0xFF - DSTa)) >> 8 */ \ "movq (%[dest_a]), %%xmm3 \n\t" /* load dst alpha */ \ "punpcklbw %%xmm0, %%xmm3 \n\t" /* unpack dst 8 8-bits alphas to 8 16-bits values */ \ "movdqa %%xmm9, %%xmm4 \n\t" \ "psubw %%xmm3, %%xmm4 \n\t" \ "pmullw %%xmm2, %%xmm4 \n\t" \ "movdqa %%xmm4, %%xmm5 \n\t" \ "psrlw $8, %%xmm4 \n\t" \ "paddw %%xmm5, %%xmm4 \n\t" \ "paddw %%xmm10, %%xmm4 \n\t" \ "psrlw $8, %%xmm4 \n\t" \ "paddw %%xmm4, %%xmm3 \n\t" \ "packuswb %%xmm0, %%xmm3 \n\t" \ "movq %%xmm3, (%[dest_a]) \n\t" /* save dst alpha */ #define DST_PIX_CALC \ "movdqu (%[src]), %%xmm3 \n\t" /* load src */ \ "movdqu (%[dest]), %%xmm4 \n\t" /* load dst */ \ "movdqa %%xmm3, %%xmm5 \n\t" /* dub src */ \ "movdqa %%xmm4, %%xmm6 \n\t" /* dub dst */ \ "punpcklbw %%xmm0, %%xmm5 \n\t" /* unpack src low */ \ "punpcklbw %%xmm0, %%xmm6 \n\t" /* unpack dst low */ \ "punpckhbw %%xmm0, %%xmm3 \n\t" /* unpack src high */ \ "punpckhbw %%xmm0, %%xmm4 \n\t" /* unpack dst high */ \ "movdqa %%xmm2, %%xmm7 \n\t" /* dub alpha */ \ "movdqa %%xmm2, %%xmm8 \n\t" /* dub alpha */ \ "movlhps %%xmm7, %%xmm7 \n\t" /* dub low */ \ "movhlps %%xmm8, %%xmm8 \n\t" /* dub high */ \ "pshuflw $0x50, %%xmm7, %%xmm7 \n\t" \ "pshuflw $0x50, %%xmm8, %%xmm8 \n\t" \ "pshufhw $0xFA, %%xmm7, %%xmm7 \n\t" \ "pshufhw $0xFA, %%xmm8, %%xmm8 \n\t" \ "psubw %%xmm4, %%xmm3 \n\t" /* src = src - dst */ \ "psubw %%xmm6, %%xmm5 \n\t" \ "pmullw %%xmm8, %%xmm3 \n\t" /* src = src * alpha */ \ "pmullw %%xmm7, %%xmm5 \n\t" \ "pmullw %%xmm9, %%xmm4 \n\t" /* dst = dst * 0xFF */ \ "pmullw %%xmm9, %%xmm6 \n\t" \ "paddw %%xmm3, %%xmm4 \n\t" /* dst = dst + src */ \ "paddw %%xmm5, %%xmm6 \n\t" \ "movdqa %%xmm4, %%xmm3 \n\t" /* dst = ((dst >> 8) + dst + 128) >> 8 */ \ "movdqa %%xmm6, %%xmm5 \n\t" \ "psrlw $8, %%xmm4 \n\t" \ "psrlw $8, %%xmm6 \n\t" \ "paddw %%xmm3, %%xmm4 \n\t" \ "paddw %%xmm5, %%xmm6 \n\t" \ "paddw %%xmm10, %%xmm4 \n\t" \ "paddw %%xmm10, %%xmm6 \n\t" \ "psrlw $8, %%xmm4 \n\t" \ "psrlw $8, %%xmm6 \n\t" \ "packuswb %%xmm4, %%xmm6 \n\t" \ "movdqu %%xmm6, (%[dest]) \n\t" /* store dst */ #define PIX_POINTER_INC \ "add $0x10, %[src] \n\t" \ "add $0x10, %[dest] \n\t" \ "dec %[width] \n\t" #define CLOBBER_XMM \ "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "%xmm9", \ "%xmm10" static void blend_case7( uint8_t *dest, uint8_t *src, int width, uint8_t *src_a, uint8_t *dest_a, int weight) { int w8 = width / 8; __asm__ volatile(LOAD_CONSTS LOAD_WEIGHT "loop_start7: \n\t" LOAD_SRC_A SRC_A_PREMUL DST_A_CALC DST_PIX_CALC "add $0x08, %[src_a] \n\t" "add $0x08, %[dest_a] \n\t" PIX_POINTER_INC "jnz loop_start7 \n\t" : [weight] "+r"(weight), [src_a] "+r"(src_a), [src] "+r"(src), [dest] "+r"(dest), [dest_a] "+r"(dest_a), [width] "+r"(w8) : [const1] "r"(const1), [const2] "r"(const2) : CLOBBER_XMM); }; // | 3 | dest_a == NULL | src_a != NULL | weight != 256 | blend: premultiply src alpha static void blend_case3(uint8_t *dest, uint8_t *src, int width, uint8_t *src_a, int weight) { int w8 = width / 8; __asm__ volatile( LOAD_CONSTS LOAD_WEIGHT "loop_start3: \n\t" LOAD_SRC_A SRC_A_PREMUL DST_PIX_CALC "add $0x08, %[src_a] \n\t" PIX_POINTER_INC "jnz loop_start3 \n\t" : [weight] "+r"(weight), [src_a] "+r"(src_a), [src] "+r"(src), [dest] "+r"(dest), [width] "+r"(w8) : [const1] "r"(const1), [const2] "r"(const2) : CLOBBER_XMM); }; // | 2 | dest_a == NULL | src_a != NULL | weight == 255 | blend: only src alpha static void blend_case2(uint8_t *dest, uint8_t *src, int width, uint8_t *src_a) { int w8 = width / 8; __asm__ volatile(LOAD_CONSTS "loop_start2: \n\t" LOAD_SRC_A DST_PIX_CALC "add $0x08, %[src_a] \n\t" PIX_POINTER_INC "jnz loop_start2 \n\t" : [src_a] "+r"(src_a), [src] "+r"(src), [dest] "+r"(dest), [width] "+r"(w8) : [const1] "r"(const1), [const2] "r"(const2) : CLOBBER_XMM); }; // | 1 | dest_a == NULL | src_a == NULL | weight != 256 | blend: with given alpha static void blend_case1(uint8_t *dest, uint8_t *src, int width, int weight) { int w8 = width / 8; __asm__ volatile(LOAD_CONSTS LOAD_WEIGHT "loop_start1: \n\t" "movdqa %%xmm1, %%xmm2 \n\t" /* src alpha cames from weight */ DST_PIX_CALC PIX_POINTER_INC "jnz loop_start1 \n\t" : [weight] "+r"(weight), [src] "+r"(src), [dest] "+r"(dest), [width] "+r"(w8) : [const1] "r"(const1), [const2] "r"(const2) : CLOBBER_XMM); }; // | 5 | dest_a != NULL | src_a == NULL | weight != 256 | blend: with given alpha static void blend_case5(uint8_t *dest, uint8_t *src, int width, uint8_t *dest_a, int weight) { int w8 = width / 8; __asm__ volatile( LOAD_CONSTS LOAD_WEIGHT "loop_start5: \n\t" "movdqa %%xmm1, %%xmm2 \n\t" /* source alpha comes from weight */ DST_A_CALC DST_PIX_CALC "add $0x08, %[dest_a] \n\t" PIX_POINTER_INC "jnz loop_start5 \n\t" : [weight] "+r"(weight), [src] "+r"(src), [dest] "+r"(dest), [dest_a] "+r"(dest_a), [width] "+r"(w8) : [const1] "r"(const1), [const2] "r"(const2) : CLOBBER_XMM); }; // | 6 | dest_a != NULL | src_a != NULL | weight == 256 | blend: full blend without src alpha premutiply static void blend_case6(uint8_t *dest, uint8_t *src, int width, uint8_t *src_a, uint8_t *dest_a) { int w8 = width / 8; __asm__ volatile( LOAD_CONSTS "loop_start6: \n\t" LOAD_SRC_A DST_A_CALC DST_PIX_CALC "add $0x08, %[src_a] \n\t" "add $0x08, %[dest_a] \n\t" PIX_POINTER_INC "jnz loop_start6 \n\t" : [src_a] "+r"(src_a), [src] "+r"(src), [dest] "+r"(dest), [dest_a] "+r"(dest_a), [width] "+r"(w8) : [const1] "r"(const1), [const2] "r"(const2) : CLOBBER_XMM); }; void composite_line_yuv_sse2_simple( uint8_t *dest, uint8_t *src, int width, uint8_t *src_a, uint8_t *dest_a, int weight) { weight >>= 8; /* | 0 | dest_a == NULL | src_a == NULL | weight == 256 | blit | 1 | dest_a == NULL | src_a == NULL | weight != 256 | blend: with given alpha | 2 | dest_a == NULL | src_a != NULL | weight == 256 | blend: only src alpha | 3 | dest_a == NULL | src_a != NULL | weight != 256 | blend: premultiply src alpha | 4 | dest_a != NULL | src_a == NULL | weight == 256 | blit: blit and set dst alpha to FF | 5 | dest_a != NULL | src_a == NULL | weight != 256 | blend: with given alpha | 6 | dest_a != NULL | src_a != NULL | weight == 256 | blend: full blend without src alpha premutiply | 7 | dest_a != NULL | src_a != NULL | weight != 256 | blend: full (origin version) */ int cond = ((dest_a != NULL) ? 4 : 0) + ((src_a != NULL) ? 2 : 0) + ((weight != 256) ? 1 : 0); switch (cond) { case 0: memcpy(dest, src, 2 * width); break; case 1: blend_case1(dest, src, width, weight); break; case 2: blend_case2(dest, src, width, src_a); break; case 3: blend_case3(dest, src, width, src_a, weight); break; case 4: memcpy(dest, src, 2 * width); memset(dest_a, 0xFF, width); break; case 5: blend_case5(dest, src, width, dest_a, weight); break; case 6: blend_case6(dest, src, width, src_a, dest_a); break; case 7: blend_case7(dest, src, width, src_a, dest_a, weight); break; }; }; mlt-7.38.0/src/modules/core/consumer_multi.c000664 000000 000000 00000060077 15172202314 020774 0ustar00rootroot000000 000000 /* * Copyright (C) 2011-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include // Forward references static int start(mlt_consumer consumer); static int stop(mlt_consumer consumer); static int is_stopped(mlt_consumer consumer); static void *consumer_thread(void *arg); static void consumer_close(mlt_consumer consumer); static void purge(mlt_consumer consumer); static mlt_properties normalizers = NULL; /** Initialise the consumer. */ mlt_consumer consumer_multi_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_consumer consumer = mlt_consumer_new(profile); if (consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Set defaults mlt_properties_set(properties, "resource", arg); mlt_properties_set_int(properties, "real_time", -1); mlt_properties_set_int(properties, "terminate_on_pause", 1); // Init state mlt_properties_set_int(properties, "joined", 1); // Assign callbacks consumer->close = consumer_close; consumer->start = start; consumer->stop = stop; consumer->is_stopped = is_stopped; consumer->purge = purge; } return consumer; } static mlt_consumer create_consumer(mlt_profile profile, char *id, char *arg) { char *myid = id ? strdup(id) : NULL; char *myarg = (myid && !arg) ? strchr(myid, ':') : NULL; if (myarg) *myarg++ = '\0'; else myarg = arg; mlt_consumer consumer = mlt_factory_consumer(profile, myid, myarg); free(myid); return consumer; } static void create_filter(mlt_profile profile, mlt_service service, char *effect, int *created) { char *id = strdup(effect); char *arg = strchr(id, ':'); if (arg != NULL) *arg++ = '\0'; // We cannot use GLSL-based filters here. if (strncmp(effect, "movit.", 6) && strncmp(effect, "glsl.", 5)) { mlt_filter filter; // The swscale and avcolor_space filters require resolution as arg to test compatibility if (strncmp(effect, "swscale", 7) == 0 || strncmp(effect, "avcolo", 6) == 0) { int width = mlt_properties_get_int(MLT_SERVICE_PROPERTIES(service), "meta.media.width"); filter = mlt_factory_filter(profile, id, &width); } else { filter = mlt_factory_filter(profile, id, arg); } if (filter) { mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "_loader", 1); mlt_service_attach(service, filter); mlt_filter_close(filter); *created = 1; } } free(id); } static void attach_normalizers(mlt_profile profile, mlt_service service) { // Loop variable int i; // Tokeniser mlt_tokeniser tokeniser = mlt_tokeniser_init(); // We only need to load the normalizing properties once if (normalizers == NULL) { char temp[PATH_MAX]; snprintf(temp, sizeof(temp), "%s/core/loader.ini", mlt_environment("MLT_DATA")); normalizers = mlt_properties_load(temp); mlt_factory_register_for_clean_up(normalizers, (mlt_destructor) mlt_properties_close); } // Apply normalizers for (i = 0; i < mlt_properties_count(normalizers); i++) { char *value = mlt_properties_get_value(normalizers, i); mlt_tokeniser_parse_new(tokeniser, value, ","); int created = 0; for (int j = 0; !created && j < mlt_tokeniser_count(tokeniser); j++) { char *name = mlt_tokeniser_get_string(tokeniser, j); if (name && strcmp(name, "color_transform")) { create_filter(profile, service, name, &created); } } } // Close the tokeniser mlt_tokeniser_close(tokeniser); // Attach the audio and video format converters int created = 0; // movit.convert skips setting the frame->convert_image pointer if GLSL cannot be used. mlt_filter filter = mlt_factory_filter(profile, "movit.convert", NULL); if (filter != NULL) { mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "_loader", 1); mlt_service_attach(service, filter); mlt_filter_close(filter); created = 1; } // avcolor_space and imageconvert only set frame->convert_image if it has not been set. create_filter(profile, service, "avcolor_space", &created); if (!created) create_filter(profile, service, "imageconvert", &created); create_filter(profile, service, "audioconvert", &created); } static void on_frame_show(void *dummy, mlt_properties properties, mlt_event_data event_data) { mlt_events_fire(properties, "consumer-frame-show", event_data); } static mlt_consumer generate_consumer(mlt_consumer consumer, mlt_properties props, int index) { mlt_profile profile = NULL; if (mlt_properties_get(props, "mlt_profile")) profile = mlt_profile_init(mlt_properties_get(props, "mlt_profile")); if (!profile) profile = mlt_profile_clone(mlt_service_profile(MLT_CONSUMER_SERVICE(consumer))); mlt_consumer nested = create_consumer(profile, mlt_properties_get(props, "mlt_service"), mlt_properties_get(props, "target")); if (nested) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested); char key[30]; snprintf(key, sizeof(key), "%d.consumer", index); mlt_properties_set_data(properties, key, nested, 0, (mlt_destructor) mlt_consumer_close, NULL); snprintf(key, sizeof(key), "%d.profile", index); mlt_properties_set_data(properties, key, profile, 0, (mlt_destructor) mlt_profile_close, NULL); mlt_properties_set_int(nested_props, "put_mode", 1); mlt_properties_pass_list(nested_props, properties, "terminate_on_pause"); mlt_properties_set(props, "consumer", NULL); // set mlt_profile before other properties to facilitate presets mlt_properties_pass_list(nested_props, props, "mlt_profile"); mlt_properties_inherit(nested_props, props); attach_normalizers(profile, MLT_CONSUMER_SERVICE(nested)); // Relay the first available consumer-frame-show event mlt_event event = mlt_properties_get_data(properties, "frame-show-event", NULL); if (!event) { event = mlt_events_listen(nested_props, properties, "consumer-frame-show", (mlt_listener) on_frame_show); mlt_properties_set_data(properties, "frame-show-event", event, 0, /*mlt_event_close*/ NULL, NULL); } } else { mlt_profile_close(profile); } return nested; } static void foreach_consumer_init(mlt_consumer consumer) { const char *resource = mlt_properties_get(MLT_CONSUMER_PROPERTIES(consumer), "resource"); mlt_properties properties = mlt_properties_parse_yaml(resource); char key[20]; int index = 0; if (mlt_properties_get_data(MLT_CONSUMER_PROPERTIES(consumer), "0", NULL)) { // Properties set directly by application mlt_properties p; if (properties) mlt_properties_close(properties); properties = MLT_CONSUMER_PROPERTIES(consumer); do { snprintf(key, sizeof(key), "%d", index); if ((p = mlt_properties_get_data(properties, key, NULL))) generate_consumer(consumer, p, index++); } while (p); } else if (properties && mlt_properties_get_data(properties, "0", NULL)) { // YAML file supplied mlt_properties p; do { snprintf(key, sizeof(key), "%d", index); if ((p = mlt_properties_get_data(properties, key, NULL))) generate_consumer(consumer, p, index++); } while (p); mlt_properties_close(properties); } else { // properties file supplied or properties on this consumer const char *s; if (properties) mlt_properties_close(properties); if (resource) properties = mlt_properties_load(resource); else properties = MLT_CONSUMER_PROPERTIES(consumer); do { snprintf(key, sizeof(key), "%d", index); if ((s = mlt_properties_get(properties, key))) { mlt_properties p = mlt_properties_new(); if (!p) break; // Terminate mlt_service value at the argument delimiter if supplied. // Needed here instead of just relying upon create_consumer() so that // a properties preset is picked up correctly. char *service = strdup(mlt_properties_get(properties, key)); char *arg = strchr(service, ':'); if (arg) { *arg++ = '\0'; mlt_properties_set(p, "target", arg); } mlt_properties_set(p, "mlt_service", service); free(service); snprintf(key, sizeof(key), "%d.", index); int i, count; count = mlt_properties_count(properties); for (i = 0; i < count; i++) { char *name = mlt_properties_get_name(properties, i); if (!strncmp(name, key, strlen(key))) mlt_properties_set(p, name + strlen(key), mlt_properties_get_value(properties, i)); } generate_consumer(consumer, p, index++); mlt_properties_close(p); } } while (s); if (resource) mlt_properties_close(properties); } } static void foreach_consumer_start(mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf(key, sizeof(key), "%d.consumer", index++); nested = mlt_properties_get_data(properties, key, NULL); if (nested) { mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested); mlt_properties_set_position(nested_props, "_multi_position", mlt_properties_get_position(properties, "in")); mlt_properties_set_data(nested_props, "_multi_audio", NULL, 0, NULL, NULL); mlt_properties_set_int(nested_props, "_multi_samples", 0); mlt_consumer_start(nested); } } while (nested); } static void foreach_consumer_refresh(mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf(key, sizeof(key), "%d.consumer", index++); nested = mlt_properties_get_data(properties, key, NULL); if (nested) mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(nested), "refresh", 1); } while (nested); } // Update certain properties on this consumer from the child consumers. static void foreach_consumer_update(mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf(key, sizeof(key), "%d.consumer", index++); nested = mlt_properties_get_data(properties, key, NULL); if (nested) { mlt_properties_pass_list( properties, MLT_CONSUMER_PROPERTIES(nested), "color_trc color_range progressive deinterlacer mlt_image_format mlt_color_trc"); } } while (nested); } static void foreach_consumer_put(mlt_consumer consumer, mlt_frame frame) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf(key, sizeof(key), "%d.consumer", index++); nested = mlt_properties_get_data(properties, key, NULL); if (nested) { mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested); double self_fps = mlt_properties_get_double(properties, "fps"); double nested_fps = mlt_properties_get_double(nested_props, "fps"); mlt_position nested_pos = mlt_properties_get_position(nested_props, "_multi_position"); mlt_position self_pos = mlt_frame_get_position(frame); double self_time = self_pos / self_fps; double nested_time = nested_pos / nested_fps; // get the audio for the current frame uint8_t *buffer = NULL; mlt_audio_format format = mlt_audio_s16; int channels = mlt_properties_get_int(properties, "channels"); int frequency = mlt_properties_get_int(properties, "frequency"); int current_samples = mlt_audio_calculate_frame_samples(self_fps, frequency, self_pos); mlt_frame_get_audio(frame, (void **) &buffer, &format, &frequency, &channels, ¤t_samples); int current_size = mlt_audio_format_size(format, current_samples, channels); // get any leftover audio int prev_size = 0; uint8_t *prev_buffer = mlt_properties_get_data(nested_props, "_multi_audio", &prev_size); uint8_t *new_buffer = NULL; if (prev_size > 0) { new_buffer = mlt_pool_alloc(prev_size + current_size); memcpy(new_buffer, prev_buffer, prev_size); memcpy(new_buffer + prev_size, buffer, current_size); buffer = new_buffer; } current_size += prev_size; current_samples += mlt_properties_get_int(nested_props, "_multi_samples"); // This log line somehow fixes a bug in release build on clang/macOS // https://forum.shotcut.org/t/shotcut-export-drops-frames/42676 mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%d: nested_time %g self_time %g\n", nested_pos, nested_time, self_time); while (nested_time <= self_time) { // put ideal number of samples into cloned frame int deeply = index > 1 ? 1 : 0; mlt_frame clone_frame = mlt_frame_clone(frame, deeply); mlt_properties clone_props = MLT_FRAME_PROPERTIES(clone_frame); int nested_samples = mlt_audio_calculate_frame_samples(nested_fps, frequency, nested_pos); // -10 is an optimization to avoid tiny amounts of leftover samples nested_samples = nested_samples > current_samples - 10 ? current_samples : nested_samples; int nested_size = mlt_audio_format_size(format, nested_samples, channels); if (nested_size > 0) { prev_buffer = mlt_pool_alloc(nested_size); memcpy(prev_buffer, buffer, nested_size); } else { prev_buffer = NULL; nested_size = 0; } mlt_frame_set_audio(clone_frame, prev_buffer, format, nested_size, mlt_pool_release); mlt_properties_set_int(clone_props, "audio_samples", nested_samples); mlt_properties_set_int(clone_props, "audio_frequency", frequency); mlt_properties_set_int(clone_props, "audio_channels", channels); // chomp the audio current_samples -= nested_samples; current_size -= nested_size; buffer += nested_size; // Fix some things mlt_properties_set_int(clone_props, "meta.media.width", mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "width")); mlt_properties_set_int(clone_props, "meta.media.height", mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "height")); // send frame to nested consumer mlt_consumer_put_frame(nested, clone_frame); mlt_properties_set_position(nested_props, "_multi_position", ++nested_pos); nested_time = nested_pos / nested_fps; } // save any remaining audio if (current_size > 0) { prev_buffer = mlt_pool_alloc(current_size); memcpy(prev_buffer, buffer, current_size); } else { prev_buffer = NULL; current_size = 0; } mlt_pool_release(new_buffer); mlt_properties_set_data(nested_props, "_multi_audio", prev_buffer, current_size, mlt_pool_release, NULL); mlt_properties_set_int(nested_props, "_multi_samples", current_samples); } } while (nested); } static void foreach_consumer_stop(mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_consumer nested = NULL; char key[30]; int index = 0; struct timespec tm = {0, 1000 * 1000}; do { snprintf(key, sizeof(key), "%d.consumer", index++); nested = mlt_properties_get_data(properties, key, NULL); if (nested) { // Let consumer with terminate_on_pause stop on their own if (mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(nested), "terminate_on_pause")) { // Send additional dummy frame to unlatch nested consumer's threads mlt_consumer_put_frame(nested, mlt_frame_init(MLT_CONSUMER_SERVICE(consumer))); // wait for stop while (!mlt_consumer_is_stopped(nested)) nanosleep(&tm, NULL); } else { mlt_consumer_stop(nested); } } } while (nested); } /** Start the consumer. */ static int start(mlt_consumer consumer) { // Check that we're not already running if (is_stopped(consumer)) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); pthread_t *thread = calloc(1, sizeof(pthread_t)); // Assign the thread to properties with automatic dealloc mlt_properties_set_data(properties, "thread", thread, sizeof(pthread_t), free, NULL); // Set the running state mlt_properties_set_int(properties, "running", 1); mlt_properties_set_int(properties, "joined", 0); // Construct and start nested consumers if (!mlt_properties_get_data(properties, "0.consumer", NULL)) foreach_consumer_init(consumer); foreach_consumer_start(consumer); // Create the thread pthread_create(thread, NULL, consumer_thread, consumer); } return 0; } /** Stop the consumer. */ static int stop(mlt_consumer consumer) { // Check that we're running if (!mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "joined")) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); pthread_t *thread = mlt_properties_get_data(properties, "thread", NULL); // Stop the thread mlt_properties_set_int(properties, "running", 0); // Wait for termination if (thread) { foreach_consumer_refresh(consumer); pthread_join(*thread, NULL); } mlt_properties_set_int(properties, "joined", 1); // Stop nested consumers foreach_consumer_stop(consumer); } return 0; } /** Determine if the consumer is stopped. */ static int is_stopped(mlt_consumer consumer) { return !mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "running"); } /** Purge each of the child consumers. */ static void purge(mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); if (mlt_properties_get_int(properties, "running")) { mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf(key, sizeof(key), "%d.consumer", index++); nested = mlt_properties_get_data(properties, key, NULL); mlt_consumer_purge(nested); } while (nested); } } /** The main thread - the argument is simply the consumer. */ static void *consumer_thread(void *arg) { mlt_consumer consumer = arg; mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_frame frame = NULL; // Determine whether to stop at end-of-media int terminate_on_pause = mlt_properties_get_int(properties, "terminate_on_pause"); int terminated = 0; foreach_consumer_update(consumer); // Loop while running while (!terminated && !is_stopped(consumer)) { // Get the next frame frame = mlt_consumer_rt_frame(consumer); // Check for termination if (terminate_on_pause && frame) terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0; // Check that we have a frame to work with if (frame && !terminated && !is_stopped(consumer)) { if (mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "rendered")) { if (mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "_speed") == 0) foreach_consumer_refresh(consumer); foreach_consumer_put(consumer, frame); } else { int dropped = mlt_properties_get_int(properties, "_dropped"); mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "dropped frame %d\n", ++dropped); mlt_properties_set_int(properties, "_dropped", dropped); } mlt_frame_close(frame); } else { if (frame && terminated) { // Send this termination frame to nested consumers for their cancellation foreach_consumer_put(consumer, frame); } if (frame) mlt_frame_close(frame); terminated = 1; } } // Indicate that the consumer is stopped mlt_consumer_stopped(consumer); return NULL; } /** Close the consumer. */ static void consumer_close(mlt_consumer consumer) { mlt_consumer_stop(consumer); // Close the parent mlt_consumer_close(consumer); free(consumer); } mlt-7.38.0/src/modules/core/consumer_multi.yml000664 000000 000000 00000002670 15172202314 021346 0ustar00rootroot000000 000000 schema_version: 7.0 type: consumer identifier: multi title: Multiple outputs version: 1 copyright: Copyright (C) 2011-2014 Meltytech, LLC license: LGPL language: en creator: Dan Dennedy tags: - Audio - Video description: Use multiple consumers with the same producer. notes: | There are a few ways of defining each of the outputs and their properties. One form is a flat set of properties on this consumer that follows the pattern: = [.=]* For example, 0=sdl2 0.rescale=bilinear 1=avformat 1.target=foo.dv ... To change the profile for a particular output set the property "mlt_profile." You can put these into a MLT properties file and supply that to this consumer. Another way is to create a separate properties list for each output and set that on the consumer with a numeric name starting with zero: = ... In this format, to specify the service, use the property name "mlt_service" and, again, to specify the profile, use "mlt_profile." You can put these into a YAML Tiny file and supply that to this consumer. This is also the recommended way for applications to interact with this consumer, which is how melt and the XML producer support multiple consumers. parameters: - identifier: resource argument: yes title: File type: string description: > A properties or YAML file specifying multiple consumers and their properties. required: no mlt-7.38.0/src/modules/core/consumer_null.c000664 000000 000000 00000012331 15172202314 020602 0ustar00rootroot000000 000000 /* * consumer_null.c -- a null consumer * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // mlt Header files #include #include // System header files #include #include #include // Forward references. static int consumer_start(mlt_consumer consumer); static int consumer_stop(mlt_consumer consumer); static int consumer_is_stopped(mlt_consumer consumer); static void *consumer_thread(void *arg); static void consumer_close(mlt_consumer consumer); /** Initialise the dv consumer. */ mlt_consumer consumer_null_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Allocate the consumer mlt_consumer consumer = mlt_consumer_new(profile); // If memory allocated and initialises without error if (consumer != NULL) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_properties_set_int(properties, "real_time", -1); mlt_properties_set_int(properties, "terminate_on_pause", 1); // Assign close callback consumer->close = consumer_close; // Set up start/stop/terminated callbacks consumer->start = consumer_start; consumer->stop = consumer_stop; consumer->is_stopped = consumer_is_stopped; } // Return consumer return consumer; } /** Start the consumer. */ static int consumer_start(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Check that we're not already running if (!mlt_properties_get_int(properties, "running")) { // Allocate a thread pthread_t *thread = calloc(1, sizeof(pthread_t)); // Assign the thread to properties mlt_properties_set_data(properties, "thread", thread, sizeof(pthread_t), free, NULL); // Set the running state mlt_properties_set_int(properties, "running", 1); mlt_properties_set_int(properties, "joined", 0); // Create the thread pthread_create(thread, NULL, consumer_thread, consumer); } return 0; } /** Stop the consumer. */ static int consumer_stop(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Check that we're running if (!mlt_properties_get_int(properties, "joined")) { // Get the thread pthread_t *thread = mlt_properties_get_data(properties, "thread", NULL); // Stop the thread mlt_properties_set_int(properties, "running", 0); mlt_properties_set_int(properties, "joined", 1); // Wait for termination if (thread) pthread_join(*thread, NULL); } return 0; } /** Determine if the consumer is stopped. */ static int consumer_is_stopped(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); return !mlt_properties_get_int(properties, "running"); } /** The main thread - the argument is simply the consumer. */ static void *consumer_thread(void *arg) { // Map the argument to the object mlt_consumer consumer = arg; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Convenience functionality int terminate_on_pause = mlt_properties_get_int(properties, "terminate_on_pause"); int terminated = 0; // Frame and size mlt_frame frame = NULL; // Loop while running while (!terminated && mlt_properties_get_int(properties, "running")) { // Get the frame frame = mlt_consumer_rt_frame(consumer); // Check for termination if (terminate_on_pause && frame != NULL) terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0; // Check that we have a frame to work with if (frame != NULL) { // Close the frame mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); mlt_frame_close(frame); } } // Indicate that the consumer is stopped mlt_properties_set_int(properties, "running", 0); mlt_consumer_stopped(consumer); return NULL; } /** Close the consumer. */ static void consumer_close(mlt_consumer consumer) { // Stop the consumer mlt_consumer_stop(consumer); // Close the parent mlt_consumer_close(consumer); // Free the memory free(consumer); } mlt-7.38.0/src/modules/core/consumer_null.yml000664 000000 000000 00000000674 15172202314 021170 0ustar00rootroot000000 000000 schema_version: 7.0 type: consumer identifier: "null" title: NULL version: 1 copyright: Meltytech, LLC license: LGPL language: en tags: - Audio - Video description: A consumer that does nothing but pull frames. notes: > This is intentionally minimal and does not even request image or audio from frames or fire events. It is handy for benchmarking, howevever, if you set the consumer properties terminate_on_pause=1 and real_time=-1. mlt-7.38.0/src/modules/core/factory.c000664 000000 000000 00000053620 15172202314 017372 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltcore_export.h" #include #include #include extern mlt_consumer consumer_multi_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_consumer consumer_null_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_audiochannels_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_audioconvert_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_audiomap_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_audiowave_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_autofade_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_box_blur_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_brightness_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_channelcopy_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_choppy_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_color_transform_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_crop_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_audioseam_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_fieldorder_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_gamma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_greyscale_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_imageconvert_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_luma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_mask_apply_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_mask_start_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_mirror_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_mono_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_obscure_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_panner_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_pillar_echo_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_rescale_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_resize_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_transition_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_watermark_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_link link_timeremap_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_blank_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_colour_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_consumer_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_hold_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_loader_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_melt_file_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_melt_init(mlt_profile profile, mlt_service_type type, const char *id, char **argv); extern mlt_producer producer_noise_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_timewarp_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_tone_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #include "transition_composite.h" extern mlt_transition transition_luma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_transition transition_mix_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_transition transition_matte_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/core/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTCORE_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "multi", consumer_multi_init); MLT_REGISTER(mlt_service_consumer_type, "null", consumer_null_init); MLT_REGISTER(mlt_service_link_type, "color_transform", mlt_link_filter_init); MLT_REGISTER(mlt_service_filter_type, "audiochannels", filter_audiochannels_init); MLT_REGISTER(mlt_service_filter_type, "audioconvert", filter_audioconvert_init); MLT_REGISTER(mlt_service_filter_type, "audiomap", filter_audiomap_init); MLT_REGISTER(mlt_service_filter_type, "audioseam", filter_audioseam_init); MLT_REGISTER(mlt_service_filter_type, "audiowave", filter_audiowave_init); MLT_REGISTER(mlt_service_filter_type, "autofade", filter_autofade_init); MLT_REGISTER(mlt_service_filter_type, "box_blur", filter_box_blur_init); MLT_REGISTER(mlt_service_filter_type, "brightness", filter_brightness_init); MLT_REGISTER(mlt_service_filter_type, "channelcopy", filter_channelcopy_init); MLT_REGISTER(mlt_service_filter_type, "channelswap", filter_channelcopy_init); MLT_REGISTER(mlt_service_filter_type, "choppy", filter_choppy_init); MLT_REGISTER(mlt_service_filter_type, "color_transform", filter_color_transform_init); MLT_REGISTER(mlt_service_filter_type, "crop", filter_crop_init); MLT_REGISTER(mlt_service_filter_type, "fieldorder", filter_fieldorder_init); MLT_REGISTER(mlt_service_filter_type, "gamma", filter_gamma_init); MLT_REGISTER(mlt_service_filter_type, "greyscale", filter_greyscale_init); MLT_REGISTER(mlt_service_filter_type, "grayscale", filter_greyscale_init); MLT_REGISTER(mlt_service_filter_type, "imageconvert", filter_imageconvert_init); MLT_REGISTER(mlt_service_filter_type, "luma", filter_luma_init); MLT_REGISTER(mlt_service_filter_type, "mask_apply", filter_mask_apply_init); MLT_REGISTER(mlt_service_filter_type, "mask_start", filter_mask_start_init); MLT_REGISTER(mlt_service_filter_type, "mirror", filter_mirror_init); MLT_REGISTER(mlt_service_filter_type, "mono", filter_mono_init); MLT_REGISTER(mlt_service_filter_type, "obscure", filter_obscure_init); MLT_REGISTER(mlt_service_filter_type, "panner", filter_panner_init); MLT_REGISTER(mlt_service_filter_type, "pillar_echo", filter_pillar_echo_init); MLT_REGISTER(mlt_service_filter_type, "rescale", filter_rescale_init); MLT_REGISTER(mlt_service_filter_type, "resize", filter_resize_init); MLT_REGISTER(mlt_service_filter_type, "transition", filter_transition_init); MLT_REGISTER(mlt_service_filter_type, "watermark", filter_watermark_init); MLT_REGISTER(mlt_service_link_type, "audiochannels", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "audioconvert", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "crop", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "fieldorder", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "imageconvert", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "rescale", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "resize", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "timeremap", link_timeremap_init); MLT_REGISTER(mlt_service_producer_type, "abnormal", producer_loader_init); MLT_REGISTER(mlt_service_producer_type, "blank", producer_blank_init); MLT_REGISTER(mlt_service_producer_type, "color", producer_colour_init); MLT_REGISTER(mlt_service_producer_type, "colour", producer_colour_init); MLT_REGISTER(mlt_service_producer_type, "consumer", producer_consumer_init); MLT_REGISTER(mlt_service_producer_type, "hold", producer_hold_init); MLT_REGISTER(mlt_service_producer_type, "loader", producer_loader_init); MLT_REGISTER(mlt_service_producer_type, "loader-nogl", producer_loader_init); MLT_REGISTER(mlt_service_producer_type, "melt", producer_melt_init); MLT_REGISTER(mlt_service_producer_type, "melt_file", producer_melt_file_init); MLT_REGISTER(mlt_service_producer_type, "noise", producer_noise_init); MLT_REGISTER(mlt_service_producer_type, "timewarp", producer_timewarp_init); MLT_REGISTER(mlt_service_producer_type, "tone", producer_tone_init); MLT_REGISTER(mlt_service_transition_type, "composite", transition_composite_init); MLT_REGISTER(mlt_service_transition_type, "luma", transition_luma_init); MLT_REGISTER(mlt_service_transition_type, "mix", transition_mix_init); MLT_REGISTER(mlt_service_transition_type, "matte", transition_matte_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "multi", metadata, "consumer_multi.yml"); MLT_REGISTER_METADATA(mlt_service_consumer_type, "null", metadata, "consumer_null.yml"); MLT_REGISTER_METADATA(mlt_service_link_type, "color_transform", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_filter_type, "audiochannels", metadata, "filter_audiochannels.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "audioconvert", metadata, "filter_audioconvert.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "audiomap", metadata, "filter_audiomap.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "audioseam", metadata, "filter_audioseam.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "audiowave", metadata, "filter_audiowave.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "autofade", metadata, "filter_autofade.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "box_blur", metadata, "filter_box_blur.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "brightness", metadata, "filter_brightness.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "channelcopy", metadata, "filter_channelcopy.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "channelswap", metadata, "filter_channelcopy.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "choppy", metadata, "filter_choppy.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "color_transform", metadata, "filter_color_transform.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "crop", metadata, "filter_crop.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "fieldorder", metadata, "filter_fieldorder.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "gamma", metadata, "filter_gamma.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "greyscale", metadata, "filter_greyscale.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "grayscale", metadata, "filter_greyscale.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "imageconvert", metadata, "filter_imageconvert.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "luma", metadata, "filter_luma.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "mask_apply", metadata, "filter_mask_apply.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "mask_start", metadata, "filter_mask_start.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "mirror", metadata, "filter_mirror.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "mono", metadata, "filter_mono.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "obscure", metadata, "filter_obscure.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "panner", metadata, "filter_panner.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "pillar_echo", metadata, "filter_pillar_echo.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "rescale", metadata, "filter_rescale.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "resize", metadata, "filter_resize.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "transition", metadata, "filter_transition.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "watermark", metadata, "filter_watermark.yml"); MLT_REGISTER_METADATA(mlt_service_link_type, "audiochannels", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "audioconvert", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "crop", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "fieldorder", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "imageconvert", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "rescale", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "resize", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "timeremap", metadata, "link_timeremap.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "abnormal", metadata, "producer_abnormal.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "blank", metadata, "producer_blank.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "colour", metadata, "producer_colour.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "color", metadata, "producer_colour.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "consumer", metadata, "producer_consumer.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "hold", metadata, "producer_hold.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "loader", metadata, "producer_loader.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "loader-nogl", metadata, "producer_loader-nogl.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "melt", metadata, "producer_melt.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "melt_file", metadata, "producer_melt_file.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "noise", metadata, "producer_noise.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "timewarp", metadata, "producer_timewarp.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "tone", metadata, "producer_tone.yml"); MLT_REGISTER_METADATA(mlt_service_transition_type, "composite", metadata, "transition_composite.yml"); MLT_REGISTER_METADATA(mlt_service_transition_type, "luma", metadata, "transition_luma.yml"); MLT_REGISTER_METADATA(mlt_service_transition_type, "mix", metadata, "transition_mix.yml"); MLT_REGISTER_METADATA(mlt_service_transition_type, "matte", metadata, "transition_matte.yml"); } mlt-7.38.0/src/modules/core/filter_audiochannels.c000664 000000 000000 00000022476 15172202314 022112 0ustar00rootroot000000 000000 /* * filter_audiochannels.c -- convert from one audio format to another * Copyright (C) 2009-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Used to return number of channels in the source int channels_avail = *channels; // Get the producer's audio int error = mlt_frame_get_audio(frame, buffer, format, frequency, &channels_avail, samples); if (error) return error; if (channels_avail < *channels) { int size = mlt_audio_format_size(*format, *samples, *channels); int16_t *new_buffer = mlt_pool_alloc(size); // Duplicate the existing channels if (*format == mlt_audio_s16) { int i, j, k = 0; for (i = 0; i < *samples; i++) { for (j = 0; j < *channels; j++) { new_buffer[(i * *channels) + j] = (( int16_t *) (*buffer))[(i * channels_avail) + k]; k = (k + 1) % channels_avail; } } } else if (*format == mlt_audio_s32le || *format == mlt_audio_f32le) { int32_t *p = (int32_t *) new_buffer; int i, j, k = 0; for (i = 0; i < *samples; i++) { for (j = 0; j < *channels; j++) { p[(i * *channels) + j] = ((int32_t *) (*buffer))[(i * channels_avail) + k]; k = (k + 1) % channels_avail; } } } else if (*format == mlt_audio_u8) { uint8_t *p = (uint8_t *) new_buffer; int i, j, k = 0; for (i = 0; i < *samples; i++) { for (j = 0; j < *channels; j++) { p[(i * *channels) + j] = ((uint8_t *) (*buffer))[(i * channels_avail) + k]; k = (k + 1) % channels_avail; } } } else { // non-interleaved - s32 or float int size_avail = mlt_audio_format_size(*format, *samples, channels_avail); int32_t *p = (int32_t *) new_buffer; int i = *channels / channels_avail; while (i--) { memcpy(p, *buffer, size_avail); p += size_avail / sizeof(*p); } i = *channels % channels_avail; if (i) { size_avail = mlt_audio_format_size(*format, *samples, i); memcpy(p, *buffer, size_avail); } } // Update the audio buffer now - destroys the old mlt_frame_set_audio(frame, new_buffer, *format, size, mlt_pool_release); *buffer = new_buffer; } else if (channels_avail == 6 && *channels == 2) { // Downmix 5.1 audio to stereo. // Mix levels taken from ATSC A/52 assuming maximum center and surround // mix levels. #define MIX(front, center, surr) (front + (0.707 * center) + (0.5 * surr)) // Convert to a supported format if necessary mlt_audio_format new_format = *format; switch (*format) { default: // Unknown. Try to convert to float anyway. mlt_log_error(NULL, "[audiochannels] Unknown format %d\n", *format); case mlt_audio_float: case mlt_audio_f32le: new_format = mlt_audio_float; break; case mlt_audio_s32le: case mlt_audio_s32: new_format = mlt_audio_s32; break; case mlt_audio_s16: case mlt_audio_u8: new_format = mlt_audio_s16; break; case mlt_audio_none: new_format = mlt_audio_none; break; } if (*format != new_format && frame->convert_audio) frame->convert_audio(frame, buffer, format, new_format); // Perform the downmix. Operate on the buffer in place to avoid realloc. if (*format == mlt_audio_s16) { int16_t *in = *buffer; int16_t *out = *buffer; int i; for (i = 0; i < *samples; i++) { float fl = in[0]; float fr = in[1]; float c = in[2]; // in[3] is LFE float sl = in[4]; float sr = in[5]; *out++ = CLAMP(MIX(fl, c, sl), INT16_MIN, INT16_MAX); // Left *out++ = CLAMP(MIX(fr, c, sr), INT16_MIN, INT16_MAX); // Right in += 6; } } else if (*format == mlt_audio_s32) { char *buffer_start = (char *) (*buffer); int32_t *flin = (int32_t *) (buffer_start); int32_t *frin = (int32_t *) (buffer_start + (*samples * sizeof(float))); int32_t *cin = (int32_t *) (buffer_start + (2 * *samples * sizeof(float))); int32_t *slin = (int32_t *) (buffer_start + (4 * *samples * sizeof(float))); int32_t *srin = (int32_t *) (buffer_start + (5 * *samples * sizeof(float))); int32_t *lout = (int32_t *) (buffer_start); int32_t *rout = (int32_t *) (buffer_start + (*samples * sizeof(float))); int i; for (i = 0; i < *samples; i++) { double fl = *flin++; double fr = *frin++; double c = *cin++; double sl = *slin++; double sr = *srin++; *lout++ = CLAMP(MIX(fl, c, sl), INT32_MIN, INT32_MAX); *rout++ = CLAMP(MIX(fr, c, sr), INT32_MIN, INT32_MAX); } } else if (*format == mlt_audio_float) { char *buffer_start = (char *) (*buffer); float *flin = (float *) (buffer_start); float *frin = (float *) (buffer_start + (*samples * sizeof(float))); float *cin = (float *) (buffer_start + (2 * *samples * sizeof(float))); float *slin = (float *) (buffer_start + (4 * *samples * sizeof(float))); float *srin = (float *) (buffer_start + (5 * *samples * sizeof(float))); float *lout = (float *) (buffer_start); float *rout = (float *) (buffer_start + (*samples * sizeof(float))); int i; for (i = 0; i < *samples; i++) { float fl = *flin++; float fr = *frin++; float c = *cin++; float sl = *slin++; float sr = *srin++; *lout++ = MIX(fl, c, sl); *rout++ = MIX(fr, c, sr); } } else { mlt_log_error(NULL, "[audiochannels] Unable to mix format %d\n", *format); } } else if (channels_avail > *channels) { int size = mlt_audio_format_size(*format, *samples, *channels); int16_t *new_buffer = mlt_pool_alloc(size); int i, j; // Drop all but the first *channels if (*format == mlt_audio_s16) { for (i = 0; i < *samples; i++) for (j = 0; j < *channels; j++) new_buffer[(i * *channels) + j] = (( int16_t *) (*buffer))[(i * channels_avail) + j]; } else if (*format == mlt_audio_s32le || *format == mlt_audio_f32le) { int32_t *p = (int32_t *) new_buffer; for (i = 0; i < *samples; i++) for (j = 0; j < *channels; j++) p[(i * *channels) + j] = ((int32_t *) (*buffer))[(i * channels_avail) + j]; } else if (*format == mlt_audio_u8) { uint8_t *p = (uint8_t *) new_buffer; for (i = 0; i < *samples; i++) for (j = 0; j < *channels; j++) p[(i * *channels) + j] = ((uint8_t *) (*buffer))[(i * channels_avail) + j]; } else { // non-interleaved - s32 or float memcpy(new_buffer, *buffer, size); } // Update the audio buffer now - destroys the old mlt_frame_set_audio(frame, new_buffer, *format, size, mlt_pool_release); *buffer = new_buffer; } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Constructor for the filter. */ mlt_filter filter_audiochannels_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) filter->process = filter_process; return filter; } mlt-7.38.0/src/modules/core/filter_audiochannels.yml000664 000000 000000 00000000666 15172202314 022466 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: audiochannels title: Convert Audio Channel Count version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: Converts the number of audio channels. notes: > This is not intended to be created directly. Rather, the loader producer loads it if it is available to automatically all inputs to have the number of audio channels requested by the consumer. mlt-7.38.0/src/modules/core/filter_audioconvert.c000664 000000 000000 00000044376 15172202314 022002 0ustar00rootroot000000 000000 /* * filter_audioconvert.c -- convert from one audio format to another * Copyright (C) 2009-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int convert_audio(mlt_frame frame, void **audio, mlt_audio_format *format, mlt_audio_format requested_format) { int error = 1; mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int channels = mlt_properties_get_int(properties, "audio_channels"); int samples = mlt_properties_get_int(properties, "audio_samples"); int size = mlt_audio_format_size(requested_format, samples, channels); if (*format != requested_format) { mlt_log_debug(NULL, "[filter audioconvert] %s -> %s %d channels %d samples\n", mlt_audio_format_name(*format), mlt_audio_format_name(requested_format), channels, samples); switch (*format) { case mlt_audio_s16: switch (requested_format) { case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; int c; for (c = 0; c < channels; c++) { int16_t *q = (int16_t *) *audio + c; int i = samples + 1; while (--i) { *p++ = (int32_t) *q << 16; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc(size); float *p = buffer; int c; for (c = 0; c < channels; c++) { int16_t *q = (int16_t *) *audio + c; int i = samples + 1; while (--i) { *p++ = (float) (*q) / 32768.0; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; int16_t *q = (int16_t *) *audio; int i = samples * channels + 1; while (--i) *p++ = (int32_t) *q++ << 16; *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc(size); float *p = buffer; int16_t *q = (int16_t *) *audio; int i = samples * channels + 1; while (--i) { float f = (float) (*q++) / 32768.0; *p++ = CLAMP(f, -1.0f, 1.0f); } *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc(size); uint8_t *p = buffer; int16_t *q = (int16_t *) *audio; int i = samples * channels + 1; while (--i) *p++ = (*q++ >> 8) + 128; *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_s32: switch (requested_format) { case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc(size); int16_t *p = buffer; int32_t *q = (int32_t *) *audio; int s, c; for (s = 0; s < samples; s++) for (c = 0; c < channels; c++) *p++ = *(q + c * samples + s) >> 16; *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc(size); float *p = buffer; int32_t *q = (int32_t *) *audio; int i = samples * channels + 1; while (--i) *p++ = (float) (*q++) / 2147483648.0; *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; int32_t *q = (int32_t *) *audio; int s, c; for (s = 0; s < samples; s++) for (c = 0; c < channels; c++) *p++ = *(q + c * samples + s); *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc(size); float *p = buffer; int32_t *q = (int32_t *) *audio; int s, c; for (s = 0; s < samples; s++) for (c = 0; c < channels; c++) { float f = (float) (*(q + c * samples + s)) / 2147483648.0; *p++ = CLAMP(f, -1.0f, 1.0f); } *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc(size); uint8_t *p = buffer; int32_t *q = (int32_t *) *audio; int s, c; for (s = 0; s < samples; s++) for (c = 0; c < channels; c++) *p++ = (q[c * samples + s] >> 24) + 128; *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_float: switch (requested_format) { case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc(size); int16_t *p = buffer; float *q = (float *) *audio; int s, c; for (s = 0; s < samples; s++) for (c = 0; c < channels; c++) { float f = *(q + c * samples + s); f = CLAMP(f, -1.0f, 1.0f); *p++ = 32767 * f; } *audio = buffer; error = 0; break; } case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; float *q = (float *) *audio; int i = samples * channels + 1; while (--i) { float f = *q++; f = CLAMP(f, -1.0f, 1.0f); int64_t pcm = (f > 0.0f ? 2147483647LL : 2147483648LL) * f; *p++ = CLAMP(pcm, -2147483648LL, 2147483647LL); } *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; float *q = (float *) *audio; int s, c; for (s = 0; s < samples; s++) for (c = 0; c < channels; c++) { float f = *(q + c * samples + s); f = CLAMP(f, -1.0f, 1.0f); int64_t pcm = (f > 0.0f ? 2147483647LL : 2147483648LL) * f; *p++ = CLAMP(pcm, -2147483648LL, 2147483647LL); } *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc(size); float *p = buffer; float *q = (float *) *audio; int s, c; for (s = 0; s < samples; s++) for (c = 0; c < channels; c++) *p++ = *(q + c * samples + s); *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc(size); uint8_t *p = buffer; float *q = (float *) *audio; int s, c; for (s = 0; s < samples; s++) for (c = 0; c < channels; c++) { float f = *(q + c * samples + s); f = CLAMP(f, -1.0f, 1.0f); *p++ = (127 * f) + 128; } *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_s32le: switch (requested_format) { case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc(size); int16_t *p = buffer; int32_t *q = (int32_t *) *audio; int i = samples * channels + 1; while (--i) *p++ = *q++ >> 16; *audio = buffer; error = 0; break; } case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; int c; for (c = 0; c < channels; c++) { int32_t *q = (int32_t *) *audio + c; int i = samples + 1; while (--i) { *p++ = *q; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc(size); float *p = buffer; int c; for (c = 0; c < channels; c++) { int32_t *q = (int32_t *) *audio + c; int i = samples + 1; while (--i) { *p++ = (float) (*q) / 2147483648.0; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc(size); float *p = buffer; int32_t *q = (int32_t *) *audio; int i = samples * channels + 1; while (--i) *p++ = (float) (*q++) / 2147483648.0; *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc(size); uint8_t *p = buffer; int32_t *q = (int32_t *) *audio; int i = samples * channels + 1; while (--i) *p++ = (*q++ >> 24) + 128; *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_f32le: switch (requested_format) { case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc(size); int16_t *p = buffer; float *q = (float *) *audio; int i = samples * channels + 1; while (--i) { float f = *q++; f = CLAMP(f, -1.0f, 1.0f); *p++ = 32767 * f; } *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc(size); float *p = buffer; int c; for (c = 0; c < channels; c++) { float *q = (float *) *audio + c; int i = samples + 1; while (--i) { *p++ = *q; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; int c; for (c = 0; c < channels; c++) { float *q = (float *) *audio + c; int i = samples + 1; while (--i) { float f = *q; f = CLAMP(f, -1.0f, 1.0f); int64_t pcm = (f > 0.0f ? 2147483647LL : 2147483648LL) * f; *p++ = CLAMP(pcm, -2147483648LL, 2147483647LL); q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; float *q = (float *) *audio; int i = samples * channels + 1; while (--i) { float f = *q++; f = CLAMP(f, -1.0f, 1.0f); int64_t pcm = (f > 0.0f ? 2147483647LL : 2147483648LL) * f; *p++ = CLAMP(pcm, -2147483648LL, 2147483647LL); } *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc(size); uint8_t *p = buffer; float *q = (float *) *audio; int i = samples * channels + 1; while (--i) { float f = *q++; f = CLAMP(f, -1.0f, 1.0f); *p++ = (127 * f) + 128; } *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_u8: switch (requested_format) { case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; int c; for (c = 0; c < channels; c++) { uint8_t *q = (uint8_t *) *audio + c; int i = samples + 1; while (--i) { *p++ = ((int32_t) *q - 128) << 24; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc(size); float *p = buffer; int c; for (c = 0; c < channels; c++) { uint8_t *q = (uint8_t *) *audio + c; int i = samples + 1; while (--i) { *p++ = ((float) *q - 128) / 256.0f; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc(size); int16_t *p = buffer; uint8_t *q = (uint8_t *) *audio; int i = samples * channels + 1; while (--i) *p++ = ((int16_t) *q++ - 128) << 8; *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc(size); int32_t *p = buffer; uint8_t *q = (uint8_t *) *audio; int i = samples * channels + 1; while (--i) *p++ = ((int32_t) *q++ - 128) << 24; *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc(size); float *p = buffer; uint8_t *q = (uint8_t *) *audio; int i = samples * channels + 1; while (--i) { float f = ((float) *q++ - 128) / 256.0f; *p++ = CLAMP(f, -1.0f, 1.0f); } *audio = buffer; error = 0; break; } default: break; } break; default: break; } } if (!error) { mlt_frame_set_audio(frame, *audio, requested_format, size, mlt_pool_release); *format = requested_format; } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { frame->convert_audio = convert_audio; return frame; } /** Constructor for the filter. */ mlt_filter filter_audioconvert_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = calloc(1, sizeof(struct mlt_filter_s)); if (mlt_filter_init(filter, filter) == 0) filter->process = filter_process; return filter; } mlt-7.38.0/src/modules/core/filter_audioconvert.yml000664 000000 000000 00000000566 15172202314 022352 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: audioconvert title: Audio Sample Format Converter version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: Converts the audio sample format. notes: > This is not intended to be created directly. Rather, the loader producer loads it to set the convert_audio function pointer on frames. mlt-7.38.0/src/modules/core/filter_audiomap.c000664 000000 000000 00000006065 15172202314 021070 0ustar00rootroot000000 000000 /* * filter_audiomap.c -- remap audio channels * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #define MAX_CHANNELS 32 static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { char prop_name[32], *prop_val; int i, j, l, m[MAX_CHANNELS]; mlt_filter filter = mlt_frame_pop_audio(frame); // Get the producer's audio int error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (error) return error; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); /* find samples length */ int len = mlt_audio_format_size(*format, 1, 1); /* pcm samples buffer */ uint8_t *pcm = *buffer; /* build matrix */ for (i = 0; i < MAX_CHANNELS; i++) { m[i] = i; snprintf(prop_name, sizeof(prop_name), "%d", i); if ((prop_val = mlt_properties_get(properties, prop_name))) { j = atoi(prop_val); if (j >= 0 && j < MAX_CHANNELS) m[i] = j; } } /* process samples */ for (i = 0; i < *samples; i++) { uint8_t tmp[MAX_CHANNELS * 4]; for (j = 0; j < MAX_CHANNELS && j < *channels; j++) for (l = 0; l < len; l++) tmp[j * len + l] = pcm[m[j] * len + l]; for (j = 0; j < MAX_CHANNELS && j < *channels; j++) for (l = 0; l < len; l++) pcm[j * len + l] = tmp[j * len + l]; pcm += len * (*channels); } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Constructor for the filter. */ mlt_filter filter_audiomap_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) filter->process = filter_process; return filter; } mlt-7.38.0/src/modules/core/filter_audiomap.yml000664 000000 000000 00000010745 15172202314 021447 0ustar00rootroot000000 000000 schema_version: 0.2 type: filter identifier: audiomap title: Remap Channels version: 1 copyright: Meltytech, LLC creator: Maksym Veremeyenko license: LGPLv2.1 language: en tags: - Audio description: Copy/swap channels according to given mapping. parameters: - identifier: '0' title: Source of channel 0 type: integer mutable: yes minimum: 0 maximum: 31 default: 0 - identifier: '1' title: Source of channel 1 type: integer mutable: yes minimum: 0 maximum: 31 default: 1 - identifier: '2' title: Source of channel 2 type: integer mutable: yes minimum: 0 maximum: 31 default: 2 - identifier: '3' title: Source of channel 3 type: integer mutable: yes minimum: 0 maximum: 31 default: 3 - identifier: '4' title: Source of channel 4 type: integer mutable: yes minimum: 0 maximum: 31 default: 4 - identifier: '5' title: Source of channel 5 type: integer mutable: yes minimum: 0 maximum: 31 default: 5 - identifier: '6' title: Source of channel 6 type: integer mutable: yes minimum: 0 maximum: 31 default: 6 - identifier: '7' title: Source of channel 7 type: integer mutable: yes minimum: 0 maximum: 31 default: 7 - identifier: '8' title: Source of channel 8 type: integer mutable: yes minimum: 0 maximum: 31 default: 8 - identifier: '9' title: Source of channel 9 type: integer mutable: yes minimum: 0 maximum: 31 default: 9 - identifier: '10' title: Source of channel 10 type: integer mutable: yes minimum: 0 maximum: 31 default: 10 - identifier: '11' title: Source of channel 11 type: integer mutable: yes minimum: 0 maximum: 31 default: 11 - identifier: '12' title: Source of channel 12 type: integer mutable: yes minimum: 0 maximum: 31 default: 12 - identifier: '13' title: Source of channel 13 type: integer mutable: yes minimum: 0 maximum: 31 default: 13 - identifier: '14' title: Source of channel 14 type: integer mutable: yes minimum: 0 maximum: 31 default: 14 - identifier: '15' title: Source of channel 15 type: integer mutable: yes minimum: 0 maximum: 31 default: 15 - identifier: '16' title: Source of channel 16 type: integer mutable: yes minimum: 0 maximum: 31 default: 16 - identifier: '17' title: Source of channel 17 type: integer mutable: yes minimum: 0 maximum: 31 default: 17 - identifier: '18' title: Source of channel 18 type: integer mutable: yes minimum: 0 maximum: 31 default: 18 - identifier: '19' title: Source of channel 19 type: integer mutable: yes minimum: 0 maximum: 31 default: 19 - identifier: '20' title: Source of channel 20 type: integer mutable: yes minimum: 0 maximum: 31 default: 20 - identifier: '21' title: Source of channel 21 type: integer mutable: yes minimum: 0 maximum: 31 default: 21 - identifier: '22' title: Source of channel 22 type: integer mutable: yes minimum: 0 maximum: 31 default: 22 - identifier: '23' title: Source of channel 23 type: integer mutable: yes minimum: 0 maximum: 31 default: 23 - identifier: '24' title: Source of channel 24 type: integer mutable: yes minimum: 0 maximum: 31 default: 24 - identifier: '25' title: Source of channel 25 type: integer mutable: yes minimum: 0 maximum: 31 default: 25 - identifier: '26' title: Source of channel 26 type: integer mutable: yes minimum: 0 maximum: 31 default: 26 - identifier: '27' title: Source of channel 27 type: integer mutable: yes minimum: 0 maximum: 31 default: 27 - identifier: '28' title: Source of channel 28 type: integer mutable: yes minimum: 0 maximum: 31 default: 28 - identifier: '29' title: Source of channel 29 type: integer mutable: yes minimum: 0 maximum: 31 default: 29 - identifier: '30' title: Source of channel 30 type: integer mutable: yes minimum: 0 maximum: 31 default: 30 - identifier: '31' title: Source of channel 31 type: integer mutable: yes minimum: 0 maximum: 31 default: 31 mlt-7.38.0/src/modules/core/filter_audioseam.c000664 000000 000000 00000016025 15172202314 021235 0ustar00rootroot000000 000000 /* * filter_audioseam.c -- smooth seams between clips in a playlist * Copyright (C) 2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include typedef struct { struct mlt_audio_s prev_audio; } private_data; static float db_delta(float a, float b) { float dba = 0; float dbb = 0; const float essentially_zero = 0.001; // Calculate db from zero if (fabs(a) > essentially_zero) { dba = log10(fabs(a)) * 20; } if (fabs(b) > essentially_zero) { dbb = log10(fabs(b)) * 20; } // Apply sign if (a < 0) { dba *= -1.0; } if (b < 0) { dba *= -1; } return dba - dbb; } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = mlt_frame_pop_audio(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; int clip_position = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.playlist.clip_position"); int clip_length = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.playlist.clip_length"); if (clip_length == 0 || (clip_position != 0 && clip_position != (clip_length - 1))) { // Only operate on the first and last frame of every clip return mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } *format = mlt_audio_f32le; int ret = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (ret != 0) { return ret; } struct mlt_audio_s curr_audio; mlt_audio_set_values(&curr_audio, *buffer, *frequency, *format, *samples, *channels); if (clip_position == 0) { if (!pdata->prev_audio.data) { mlt_log_debug(MLT_FILTER_SERVICE(filter), "Missing previous audio\n"); } else { float *prev_data = pdata->prev_audio.data; float *curr_data = curr_audio.data; float level_delta = db_delta(prev_data[pdata->prev_audio.samples - 1], curr_data[0]); double discontinuity_threshold = mlt_properties_get_double(filter_properties, "discontinuity_threshold"); if (fabs(level_delta) > discontinuity_threshold) { // We have decided to create a transition with the previous frame. // Reverse the prevous frame and use the reversed samples as faux // data that is continuous from the prevous frame. // Mix/fade the reversed previous samples with the new samples to create a transition. mlt_audio_reverse(&pdata->prev_audio); int fade_samples = 1000; if (fade_samples > curr_audio.samples) { fade_samples = curr_audio.samples; } if (fade_samples > pdata->prev_audio.samples) { fade_samples = pdata->prev_audio.samples; } for (int c = 0; c < curr_audio.channels; c++) { curr_data = (float *) curr_audio.data + c; prev_data = (float *) pdata->prev_audio.data + c; for (int i = 0; i < fade_samples; i++) { float mix = (1.0 / fade_samples) * (float) (fade_samples - i); *curr_data = (*prev_data * mix) + (*curr_data * (1.0 - mix)); curr_data += curr_audio.channels; prev_data += curr_audio.channels; } } // If this flag is set, it must be cleared so that other services will know it can't be ignored. mlt_properties_clear(MLT_FRAME_PROPERTIES(frame), "test_audio"); // Increment the counter mlt_properties_set_int(filter_properties, "seam_count", mlt_properties_get_int(filter_properties, "seam_count") + 1); } } mlt_audio_free_data(&pdata->prev_audio); } else if (clip_position == (clip_length - 1)) { // Save the samples of the last frame to be used to mix with the first frame of the next clip. mlt_audio_set_values(&pdata->prev_audio, NULL, *frequency, *format, *samples, *channels); mlt_audio_alloc_data(&pdata->prev_audio); mlt_audio_copy(&pdata->prev_audio, &curr_audio, *samples, 0, 0); } return 0; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); int clip_position = mlt_properties_get_int(frame_properties, "meta.playlist.clip_position"); int clip_length = mlt_properties_get_int(frame_properties, "meta.playlist.clip_length"); // Only operate on the first and last frame of every clip if (clip_length > 0 && (clip_position == 0 || clip_position == (clip_length - 1))) { // Be sure to process blanks in a playlist mlt_properties_clear(frame_properties, "test_audio"); mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); } return frame; } static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { mlt_audio_free_data(&pdata->prev_audio); } free(pdata); filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } mlt_filter filter_audioseam_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata) { filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_filter_close(filter); filter = NULL; free(pdata); } return filter; } mlt-7.38.0/src/modules/core/filter_audioseam.yml000664 000000 000000 00000002061 15172202314 021607 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: audioseam title: Audio Seam version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: > Seam audio splices between clips in a playlist. Only to be used as a filter on playlist producers. Uses the "meta.playlist.clip_position" and "meta.playlist.clip_length" properties that are added by the playlist producer to determine where the seams between clips occur. parameters: - identifier: discontinuity_threshold title: Discontinuity Threshold type: float description: > The delta between the last sample of one clip and the first sample of the following clip that are spliced. If the delta is above the discontinuity threshold, then smoothing will be applied. readonly: no mutable: yes default: 2 minimum: 0 maximum: 30 unit: dB - identifier: seam_count title: Seam Count type: integer description: > The number of splices that have exceeded the discontinuity threshold and have been seamed. readonly: yes mlt-7.38.0/src/modules/core/filter_audiowave.c000664 000000 000000 00000004326 15172202314 021253 0ustar00rootroot000000 000000 /* * filter_audiowave.c -- display audio waveform * Copyright (C) 2010-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int size = *width * *height * 2; *format = mlt_image_yuv422; *image = mlt_pool_alloc(size); mlt_frame_set_image(frame, *image, size, mlt_pool_release); uint8_t *wave = mlt_frame_get_waveform(frame, *width, *height); if (wave) { uint8_t *p = *image; uint8_t *q = *image + *width * *height * 2; uint8_t *s = wave; while (p != q) { *p++ = *s++; *p++ = 128; } } return (wave == NULL); } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_audiowave_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) filter->process = filter_process; return filter; } mlt-7.38.0/src/modules/core/filter_audiowave.yml000664 000000 000000 00000001130 15172202314 021620 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: audiowave title: Audio Waveform (*DEPRECATED*) version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en description: Generate audio waveforms. notes: > This filter is deprecated and will eventually be removed. Use the audiowaveform filter instead. tags: - Video bugs: - > This does not work alone on audio-only clips. It must have video to overwrite. A workaround is to apply this to a multitrack with a color generator. - The quality of the waveforms is not so good especially for high definition video. mlt-7.38.0/src/modules/core/filter_autofade.c000664 000000 000000 00000022275 15172202314 021062 0ustar00rootroot000000 000000 /* * filter_autofade.c -- Automatically fade audio between clips in a playlist. * Copyright (C) 2023-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include static float decay_factor(int position, int count) { float factor = (float) position / (float) (count - 1); if (factor < 0) { factor = 0; } else if (factor > 1.0) { factor = 1.0; } return factor; } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = mlt_frame_pop_audio(frame); *format = mlt_audio_f32le; int ret = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (ret != 0) { return ret; } mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); int clip_position = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.playlist.clip_position"); int clip_length = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.playlist.clip_length"); int fade_duration = mlt_properties_get_int(filter_properties, "fade_duration"); int fade_samples = fade_duration * *frequency / 1000; double fps = mlt_profile_fps(mlt_service_profile(MLT_FILTER_SERVICE(filter))); int64_t samples_to_frame_begin = mlt_audio_calculate_samples_to_position(fps, *frequency, clip_position); int64_t samples_in_clip = mlt_audio_calculate_samples_to_position(fps, *frequency, clip_length + 1); int64_t samples_to_clip_end = samples_in_clip - samples_to_frame_begin - *samples; struct mlt_audio_s audio; mlt_audio_set_values(&audio, *buffer, *frequency, *format, *samples, *channels); float *data = (float *) audio.data; if (samples_to_frame_begin <= fade_samples) { // Fade in for (int i = 0; i < audio.samples; i++) { float factor = decay_factor(samples_to_frame_begin + i, fade_samples); for (int c = 0; c < audio.channels; c++) { *data = *data * factor; data++; } } } else if ((samples_to_clip_end - *samples) <= fade_samples) { // Fade out for (int i = 0; i < audio.samples; i++) { float factor = decay_factor(samples_to_clip_end - i, fade_samples); for (int c = 0; c < audio.channels; c++) { *data = *data * factor; data++; } } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); // Get the current image if (*format != mlt_image_rgba64) { *format = mlt_image_rgba; } error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error) return error; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); int clip_position = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.playlist.clip_position"); int clip_length = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.playlist.clip_length"); double fade_duration = mlt_properties_get_int(filter_properties, "fade_duration"); double fps = mlt_profile_fps(mlt_service_profile(MLT_FILTER_SERVICE(filter))); int fade_duration_frames = lrint(fade_duration * fps / 1000.0); float image_factor = 1.0; int frames_from_begining = clip_position + 1; int frames_to_end = clip_length - clip_position - 1; if (frames_from_begining <= fade_duration_frames) { // Fade In image_factor = decay_factor(clip_position, fade_duration_frames); } else if (frames_to_end <= fade_duration_frames) { // Fade Out image_factor = decay_factor(frames_to_end, fade_duration_frames); } if (image_factor < 1.0) { mlt_color color = mlt_properties_get_color(filter_properties, "fade_color"); color = mlt_color_convert_trc(color, mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "color_trc")); float color_factor = 1.0 - image_factor; float r_value = color.r * color_factor; float g_value = color.g * color_factor; float b_value = color.b * color_factor; float a_value = color.a * color_factor; int pixels = *width * *height; if (*format == mlt_image_rgba64) { uint16_t *p = (uint16_t *) *image; for (int i = 0; i < pixels; i++) { p[0] = ((float) p[0] * image_factor) + r_value; p[1] = ((float) p[1] * image_factor) + g_value; p[2] = ((float) p[2] * image_factor) + b_value; p[3] = ((float) p[3] * image_factor) + a_value; p += 4; } } else { // mlt_image_rgba uint8_t *p = *image; for (int i = 0; i < pixels; i++) { p[0] = ((float) p[0] * image_factor) + r_value; p[1] = ((float) p[1] * image_factor) + g_value; p[2] = ((float) p[2] * image_factor) + b_value; p[3] = ((float) p[3] * image_factor) + a_value; p += 4; } } } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); int clip_position = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.playlist.clip_position"); int clip_length = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.playlist.clip_length"); int fade_duration = mlt_properties_get_int(filter_properties, "fade_duration"); double fps = mlt_profile_fps(mlt_service_profile(MLT_FILTER_SERVICE(filter))); int ms_from_begining = (double) clip_position * 1000.0 / fps; int ms_from_end = (double) (clip_length - clip_position - 1) * 1000.0 / fps; int fade = 0; if (ms_from_begining <= fade_duration) { fade = 1; mlt_properties_set_int(filter_properties, "fade_in_count", mlt_properties_get_int(filter_properties, "fade_in_count") + 1); } else if (ms_from_end <= fade_duration) { fade = 1; mlt_properties_set_int(filter_properties, "fade_out_count", mlt_properties_get_int(filter_properties, "fade_out_count") + 1); } if (fade && mlt_properties_get_int(filter_properties, "fade_audio")) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); } if (fade && mlt_properties_get_int(filter_properties, "fade_video")) { mlt_frame_push_get_image(frame, (void *) filter); mlt_frame_push_get_image(frame, filter_get_image); } return frame; } static void filter_close(mlt_filter filter) { filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } mlt_filter filter_autofade_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->close = filter_close; filter->process = filter_process; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(filter_properties, "fade_duration", 20); mlt_properties_set_int(filter_properties, "fade_audio", 1); mlt_properties_set_int(filter_properties, "fade_video", 0); mlt_properties_set_string(filter_properties, "fade_color", "0x000000ff"); } return filter; } mlt-7.38.0/src/modules/core/filter_autofade.yml000664 000000 000000 00000003500 15172202314 021427 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: autofade title: Auto Fade version: 3 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: > Automatically fade audio between clips in a playlist. This filter will fade the audio out at the end of a clip and fade the audio in at the beginning of a clip. Only to be used as a filter on playlist producers. Uses the "meta.playlist.clip_position" and "meta.playlist.clip_length" properties that are added by the playlist producer to determine where the splices between clips occur. parameters: - identifier: fade_duration title: Fade Duration type: integer description: > The duration of each fade in and fade out. readonly: no mutable: yes default: 20 minimum: 1 maximum: 1000 unit: ms - identifier: fade_video title: Fade Video type: boolean description: Fade the video to/from the fade color mutable: yes default: 0 - identifier: fade_audio title: Fade Audio type: boolean description: Fade the audio to/from silence mutable: yes default: 1 - identifier: fade_color title: Fade color type: color description: > The color to fade to. A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb default: 0x000000ff mutable: yes widget: color - identifier: fade_in_count title: Fade In Count type: integer description: > The number of time fade in has been applied. readonly: yes - identifier: fade_out_count title: Fade Out Count type: integer description: > The number of time fade out has been applied. readonly: yes mlt-7.38.0/src/modules/core/filter_box_blur.c000664 000000 000000 00000006661 15172202314 021107 0ustar00rootroot000000 000000 /* * filter_box_blur.c * Copyright (C) 2011-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "image_proc.h" #include #include #include #include #include static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double hradius = mlt_properties_anim_get_double(properties, "hradius", position, length); double vradius = mlt_properties_anim_get_double(properties, "vradius", position, length); int preserve_alpha = mlt_properties_get_int(properties, "preserve_alpha"); // Convert from percent to pixels as a factor of 10% image width. double pixelScale = (double) profile->width * mlt_profile_scale_width(profile, *width) / 1000.0; hradius = MAX(round(hradius * pixelScale), 0); vradius = MAX(round(vradius * pixelScale), 0); if (hradius == 0 && vradius == 0) { // Nothing to blur error = mlt_frame_get_image(frame, image, format, width, height, writable); } else { // Get the image if (*format != mlt_image_rgba64) { *format = mlt_image_rgba; } error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0) { struct mlt_image_s img; mlt_image_set_values(&img, *image, *format, *width, *height); mlt_image_box_blur(&img, hradius, vradius, preserve_alpha); } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_box_blur_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "hradius", "1"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "vradius", "1"); } return filter; } mlt-7.38.0/src/modules/core/filter_box_blur.yml000664 000000 000000 00000001653 15172202314 021462 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: box_blur title: Box Blur version: 3 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video parameters: - identifier: hradius title: Horizontal radius description: > The horizontal blur radius as a percent of the image size. 100% results in a blur radius of 10% of the image width. type: float unit: percent mutable: yes animation: yes minimum: 0 default: 1 - identifier: vradius title: Vertical radius description: > The vertical blur radius as a percent of the image size. 100% results in a blur radius of 10% of the image width. type: float unit: percent mutable: yes animation: yes minimum: 0 default: 1 - identifier: preserve_alpha title: Preserve Alpha type: boolean description: Exclude the alpha channel from the blur operation mutable: yes default: 0 mlt-7.38.0/src/modules/core/filter_brightness.c000664 000000 000000 00000025465 15172202314 021446 0ustar00rootroot000000 000000 /* * filter_brightness.c -- brightness, fade, and opacity filter * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include struct sliced_desc { mlt_image image; double level; double alpha_level; int full_range; }; static int sliced_proc(int id, int index, int jobs, void *cookie) { (void) id; // unused struct sliced_desc *ctx = ((struct sliced_desc *) cookie); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, ctx->image->height, &slice_line_start); // Only process if level is something other than 1 if (ctx->level != 1.0) { if (ctx->image->format == mlt_image_yuv422) { int32_t m = ctx->level * (1 << 16); int32_t n = 128 * ((1 << 16) - m); int min = ctx->full_range ? 0 : 16; int max_luma = ctx->full_range ? 255 : 235; int max_chroma = ctx->full_range ? 255 : 240; for (int line = 0; line < slice_height; line++) { uint8_t *p = ctx->image->planes[0] + ((slice_line_start + line) * ctx->image->strides[0]); for (int pixel = 0; pixel < ctx->image->width; pixel++) { p[0] = CLAMP((p[0] * m) >> 16, min, max_luma); p[1] = CLAMP((p[1] * m + n) >> 16, min, max_chroma); p += 2; } } } else if (ctx->image->format == mlt_image_rgba) { for (int line = 0; line < slice_height; line++) { uint8_t *p = ctx->image->planes[0] + ((slice_line_start + line) * ctx->image->strides[0]); for (int pixel = 0; pixel < ctx->image->width; pixel++) { p[0] = CLAMP(round((double) p[0] * ctx->level), 0, 255); p[1] = CLAMP(round((double) p[1] * ctx->level), 0, 255); p[2] = CLAMP(round((double) p[2] * ctx->level), 0, 255); p += 4; } } } else if (ctx->image->format == mlt_image_rgb) { for (int line = 0; line < slice_height; line++) { uint8_t *p = ctx->image->planes[0] + ((slice_line_start + line) * ctx->image->strides[0]); for (int pixel = 0; pixel < ctx->image->width; pixel++) { p[0] = CLAMP(round((double) p[0] * ctx->level), 0, 255); p[1] = CLAMP(round((double) p[1] * ctx->level), 0, 255); p[2] = CLAMP(round((double) p[2] * ctx->level), 0, 255); p += 3; } } } else if (ctx->image->format == mlt_image_rgba64) { for (int row = 0; row < slice_height; row++) { uint16_t *p = (uint16_t *) ctx->image->planes[0] + ((slice_line_start + row) * ctx->image->strides[0] / 2); for (int pixel = 0; pixel < ctx->image->width; pixel++) { p[0] = CLAMP(round((double) p[0] * ctx->level), 0, 65535); p[1] = CLAMP(round((double) p[1] * ctx->level), 0, 65535); p[2] = CLAMP(round((double) p[2] * ctx->level), 0, 65535); p += 4; } } } } // Process the alpha channel if requested. if (ctx->alpha_level != 1.0) { if (ctx->image->format == mlt_image_rgba) { for (int line = 0; line < slice_height; line++) { uint8_t *p = ctx->image->planes[0] + ((slice_line_start + line) * ctx->image->strides[0]) + 3; int components_in_row = ctx->image->width * 4; for (int col = 0; col < components_in_row; col += 4) { p[col] = CLAMP(round((double) p[col] * ctx->alpha_level), 0, 255); } } } else if (ctx->image->format == mlt_image_rgba64) { for (int row = 0; row < slice_height; row++) { uint16_t *p = (uint16_t *) ctx->image->planes[0] + ((slice_line_start + row) * ctx->image->strides[0] / 2) + 3; int components_in_row = ctx->image->width * 4; for (int col = 0; col < components_in_row; col += 4) { p[col] = CLAMP(round((double) p[col] * ctx->alpha_level), 0, 65535); } } } else if (ctx->image->planes[3]) { for (int line = 0; line < slice_height; line++) { uint8_t *p = ctx->image->planes[3] + ((slice_line_start + line) * ctx->image->strides[3]); for (int pixel = 0; pixel < ctx->image->width; pixel++) { p[pixel] = CLAMP(round((double) p[pixel] * ctx->alpha_level), 0, 255); } } } } return 0; } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); double level = 1.0; double alpha_level = 1.0; // Use animated "level" property only if it has been set since init char *level_property = mlt_properties_get(properties, "level"); if (level_property != NULL) { level = mlt_properties_anim_get_double(properties, "level", position, length); } else { // Get level using old "start,"end" mechanics // Get the starting brightness level level = fabs(mlt_properties_get_double(properties, "start")); // If there is an end adjust gain to the range if (mlt_properties_get(properties, "end") != NULL) { // Determine the time position of this frame in the transition duration double end = fabs(mlt_properties_get_double(properties, "end")); level += (end - level) * mlt_filter_get_progress(filter, frame); } } // Do not cause an image conversion unless there is real work to do. if (level != 1.0) { switch (*format) { case mlt_image_rgb: case mlt_image_rgba: case mlt_image_rgba64: break; case mlt_image_yuv422: if (mlt_properties_get_int(properties, "rgb_only")) { *format = mlt_image_rgba; } break; case mlt_image_movit: case mlt_image_opengl_texture: *format = mlt_image_rgba; break; case mlt_image_none: case mlt_image_invalid: *format = mlt_image_rgba; break; case mlt_image_yuv420p: if (mlt_properties_get_int(properties, "rgb_only")) { *format = mlt_image_rgba; } else { *format = mlt_image_yuv422; } break; case mlt_image_yuv422p16: case mlt_image_yuv420p10: case mlt_image_yuv444p10: *format = mlt_image_rgba64; break; } } // Get the image int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (*format != mlt_image_rgb && *format != mlt_image_rgba && *format != mlt_image_rgba64 && *format != mlt_image_yuv422) level = 1.0; alpha_level = mlt_properties_exists(properties, "alpha") ? mlt_properties_anim_get_double(properties, "alpha", position, length) : 1.0; if (alpha_level < 0.0) { alpha_level = level; } // Only process if we have no error. if (!error && (level != 1.0 || alpha_level != 1.0)) { int threads = mlt_properties_get_int(properties, "threads"); struct sliced_desc desc; struct mlt_image_s proc_image; mlt_image_set_values(&proc_image, *image, *format, *width, *height); if (alpha_level != 1.0 && proc_image.format != mlt_image_rgba && proc_image.format != mlt_image_rgba64) { proc_image.planes[3] = mlt_frame_get_alpha(frame); proc_image.strides[3] = proc_image.width; if (!proc_image.planes[3]) { // Alpha will be needed but it does not exist yet. Create opaque alpha. mlt_image_alloc_alpha(&proc_image); mlt_image_fill_opaque(&proc_image); mlt_frame_set_alpha(frame, proc_image.planes[3], proc_image.width * proc_image.height, proc_image.release_alpha); } } desc.level = level; desc.alpha_level = alpha_level; desc.image = &proc_image; desc.full_range = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_range"); threads = CLAMP(threads, 0, mlt_slices_count_normal()); if (threads == 1) { sliced_proc(0, 0, 1, &desc); } else { mlt_slices_run_normal(threads, sliced_proc, &desc); } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_brightness_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "start", arg == NULL ? "1" : arg); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "level", NULL); } return filter; } mlt-7.38.0/src/modules/core/filter_brightness.yml000664 000000 000000 00000003211 15172202314 022006 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: brightness title: Brightness version: 5 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en description: Adjust the brightness and opacity of the image. tags: - Video parameters: - identifier: start title: Start level (*DEPRECATED*) type: float argument: yes minimum: 0.0 maximum: 15.0 default: 1.0 - identifier: end (*DEPRECATED*) title: End level type: float minimum: 0.0 maximum: 15.0 default: 1.0 - identifier: level title: Level type: float minimum: 0.0 maximum: 15.0 mutable: yes animation: yes - identifier: alpha title: Alpha factor description: > When this is less than zero, the alpha factor follows the level property. Otherwise, you can set this to another value to adjust the alpha component independently. No alpha channel adjustment occurs if this is not set or it equals 1. type: float minimum: -1 mutable: yes animation: yes - identifier: threads title: Thread count description: > Use 0 to use the slice count, which defaults to the number of detected CPUs. Otherwise, set the number of threads to use up to the slice count. minimum: 0 default: 0 - identifier: rgb_only title: RGB Only description: > When enabled, the filter will only operate on RGB even if YUV is requested. Applying level changes to YUV does not result in the same brightness as applying the same level to RGB. This option ensures consistent brightness in case the image format changes. type: boolean default: 0mlt-7.38.0/src/modules/core/filter_channelcopy.c000664 000000 000000 00000013057 15172202314 021573 0ustar00rootroot000000 000000 /* * filter_channelcopy.c -- copy one audio channel to another * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /** Get the audio. */ static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Get the filter service mlt_filter filter = mlt_frame_pop_audio(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); int from = mlt_properties_get_int(properties, "from"); int to = mlt_properties_get_int(properties, "to"); int swap = mlt_properties_get_int(properties, "swap"); // Get the producer's audio mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); // Copy channels as necessary if (from != to) switch (*format) { case mlt_audio_u8: { uint8_t *f = (uint8_t *) *buffer + from; uint8_t *t = (uint8_t *) *buffer + to; uint8_t x; int i; if (swap) for (i = 0; i < *samples; i++, f += *channels, t += *channels) { x = *t; *t = *f; *f = x; } else for (i = 0; i < *samples; i++, f += *channels, t += *channels) *t = *f; break; } case mlt_audio_s16: { int16_t *f = (int16_t *) *buffer + from; int16_t *t = (int16_t *) *buffer + to; int16_t x; int i; if (swap) for (i = 0; i < *samples; i++, f += *channels, t += *channels) { x = *t; *t = *f; *f = x; } else for (i = 0; i < *samples; i++, f += *channels, t += *channels) *t = *f; break; } case mlt_audio_s32: { int32_t *f = (int32_t *) *buffer + from * *samples; int32_t *t = (int32_t *) *buffer + to * *samples; if (swap) { int32_t *x = malloc(*samples * sizeof(int32_t)); memcpy(x, t, *samples * sizeof(int32_t)); memcpy(t, f, *samples * sizeof(int32_t)); memcpy(f, x, *samples * sizeof(int32_t)); free(x); } else { memcpy(t, f, *samples * sizeof(int32_t)); } break; } case mlt_audio_s32le: case mlt_audio_f32le: { int32_t *f = (int32_t *) *buffer + from; int32_t *t = (int32_t *) *buffer + to; int32_t x; int i; if (swap) for (i = 0; i < *samples; i++, f += *channels, t += *channels) { x = *t; *t = *f; *f = x; } else for (i = 0; i < *samples; i++, f += *channels, t += *channels) *t = *f; break; } case mlt_audio_float: { float *f = (float *) *buffer + from * *samples; float *t = (float *) *buffer + to * *samples; if (swap) { float *x = malloc(*samples * sizeof(float)); memcpy(x, t, *samples * sizeof(float)); memcpy(t, f, *samples * sizeof(float)); memcpy(f, x, *samples * sizeof(float)); free(x); } else { memcpy(t, f, *samples * sizeof(float)); } break; } default: mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid audio format\n"); break; } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Override the get_audio method mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Constructor for the filter. */ mlt_filter filter_channelcopy_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; if (arg != NULL) mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "to", atoi(arg)); else mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "to", 1); if (strcmp(id, "channelswap") == 0) mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "swap", 1); } return filter; } mlt-7.38.0/src/modules/core/filter_channelcopy.yml000664 000000 000000 00000001227 15172202314 022146 0ustar00rootroot000000 000000 schema_version: 0.2 type: filter identifier: channelcopy title: Copy Channels version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: Copy one audio channel to another. parameters: - identifier: to title: To type: integer argument: true minimum: 0 maximum: 15 default: 1 - identifier: from title: From type: integer minimum: 0 maximum: 15 default: 0 - identifier: swap title: Swap description: Swap the two channels instead of duplicating the source channel. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-7.38.0/src/modules/core/filter_choppy.c000664 000000 000000 00000011001 15172202314 020555 0ustar00rootroot000000 000000 /* * filter_choppy.c -- simple frame repeating filter * Copyright (C) 2020-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error; mlt_filter filter = mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); int amount = mlt_properties_anim_get_int(properties, "amount", position, length) + 1; if (amount > 1) { mlt_service_lock(MLT_FILTER_SERVICE(filter)); mlt_frame cloned_frame = mlt_properties_get_data(properties, "cloned_frame", NULL); mlt_position cloned_pos = mlt_frame_get_position(cloned_frame); position = mlt_frame_get_position(frame); if (!cloned_frame || MLT_POSITION_MOD(position, amount) == 0 || abs(position - cloned_pos) > amount) { error = mlt_frame_get_image(frame, image, format, width, height, writable); cloned_frame = mlt_frame_clone(frame, 1); mlt_properties_set_data(properties, "cloned_frame", cloned_frame, 0, (mlt_destructor) mlt_frame_close, NULL); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } else { mlt_service_unlock(MLT_FILTER_SERVICE(filter)); error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error) { mlt_properties cloned_props = MLT_FRAME_PROPERTIES(cloned_frame); int size = 0; void *data = mlt_properties_get_data(cloned_props, "image", &size); if (data) { *width = mlt_properties_get_int(cloned_props, "width"); *height = mlt_properties_get_int(cloned_props, "height"); *format = mlt_properties_get_int(cloned_props, "format"); if (!size) { size = mlt_image_format_size(*format, *width, *height, NULL); } *image = mlt_pool_alloc(size); memcpy(*image, data, size); mlt_frame_set_image(frame, *image, size, mlt_pool_release); data = mlt_frame_get_alpha_size(cloned_frame, &size); if (data) { if (!size) { size = (*width) * (*height); } void *copy = mlt_pool_alloc(size); memcpy(copy, data, size); mlt_frame_set_alpha(frame, copy, size, mlt_pool_release); } } } } } else { error = mlt_frame_get_image(frame, image, format, width, height, writable); } return error; } /** Filter processing. */ static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_choppy_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "amount", arg ? arg : "0"); } return filter; } mlt-7.38.0/src/modules/core/filter_choppy.yml000664 000000 000000 00000000634 15172202314 021146 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: choppy title: Choppy version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video parameters: - identifier: amount title: Repeat type: integer description: The keyframable number of frames to repeat in a row argument: yes default: 0 minimum: 0 mutable: yes animation: yes unit: frames mlt-7.38.0/src/modules/core/filter_color_transform.c000664 000000 000000 00000027602 15172202314 022502 0ustar00rootroot000000 000000 /* * filter_color_transform.c -- color transform filter * Copyright (C) 2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include static const char *av_trc_str(mlt_color_trc trc) { switch (trc) { case mlt_color_trc_bt709: return "bt709"; case mlt_color_trc_gamma22: return "bt470m"; case mlt_color_trc_gamma28: return "bt470bg"; case mlt_color_trc_smpte170m: return "smpte170m"; case mlt_color_trc_smpte240m: return "smpte240m"; case mlt_color_trc_linear: return "linear"; case mlt_color_trc_log: return "log100"; case mlt_color_trc_log_sqrt: return "log316"; case mlt_color_trc_iec61966_2_4: return "iec61966-2-4"; case mlt_color_trc_iec61966_2_1: return "iec61966-2-1"; case mlt_color_trc_bt2020_10: return "bt2020_10"; case mlt_color_trc_bt2020_12: return "bt2020_12"; case mlt_color_trc_smpte2084: return "smpte2084"; case mlt_color_trc_arib_std_b67: return "arib-std-b67"; case mlt_color_trc_smpte428: return "smpte428"; case mlt_color_trc_bt1361_ecg: return "bt1361e"; case mlt_color_trc_none: case mlt_color_trc_unspecified: case mlt_color_trc_reserved: case mlt_color_trc_invalid: break; } return "unknown"; } static void create_t_filter(mlt_filter self, mlt_frame frame, mlt_color_trc out_trc) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(self)); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(self); const char *method = mlt_properties_get(filter_properties, "method"); mlt_filter t_filter = mlt_factory_filter(profile, method, NULL); if (!t_filter) { return; } mlt_properties cs_properties = MLT_FILTER_PROPERTIES(t_filter); if (!strcmp(method, "avfilter.zscale")) { mlt_properties_set(cs_properties, "av.t", av_trc_str(out_trc)); } else if (!strcmp(method, "sws_colortransform")) { mlt_properties_set(cs_properties, "transfer", mlt_image_color_trc_name(out_trc)); } else if (!strcmp(method, "avfilter.scale")) { mlt_properties_set(cs_properties, "av.out_transfer", av_trc_str(out_trc)); } mlt_service_cache_put(MLT_FILTER_SERVICE(self), "t_filter", t_filter, 0, (mlt_destructor) mlt_filter_close); } static void ensure_color_properties(mlt_filter self, mlt_frame frame) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_image_format format = mlt_properties_get_int(frame_properties, "format"); int height = mlt_properties_get_int(frame_properties, "height"); const char *colorspace_str = mlt_properties_get(frame_properties, "colorspace"); if (!colorspace_str) { mlt_colorspace colorspace = mlt_image_default_colorspace(format, height); mlt_properties_set_int(frame_properties, "colorspace", colorspace); colorspace_str = mlt_properties_get(frame_properties, "colorspace"); } else { mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); if (format == mlt_image_rgb || format == mlt_image_rgba || format == mlt_image_rgba64) { // Correct the colorspace. For RGB, only support rgb. if (colorspace != mlt_colorspace_rgb) { mlt_properties_set_int(frame_properties, "colorspace", mlt_colorspace_rgb); colorspace_str = mlt_properties_get(frame_properties, "colorspace"); } } } mlt_colorspace colorspace = mlt_image_colorspace_id(colorspace_str); const char *color_trc_str = mlt_properties_get(frame_properties, "color_trc"); if (!color_trc_str) { mlt_color_trc color_trc = mlt_image_default_trc(colorspace); mlt_properties_set_int(frame_properties, "color_trc", color_trc); } else { mlt_color_trc color_trc = mlt_image_color_trc_id(color_trc_str); if (format == mlt_image_rgb || format == mlt_image_rgba || format == mlt_image_rgba64) { // Correct the TRC. For RGB, only support linear or iec61966-2-1. if (color_trc != mlt_color_trc_linear && color_trc != mlt_color_trc_iec61966_2_1) { mlt_properties_set_int(frame_properties, "color_trc", mlt_color_trc_iec61966_2_1); } } } const char *color_primaries_str = mlt_properties_get(frame_properties, "color_primaries"); if (!color_primaries_str) { mlt_color_primaries primaries = mlt_image_default_primaries(colorspace, height); mlt_properties_set_int(frame_properties, "color_primaries", primaries); } const char *full_range_str = mlt_properties_get(frame_properties, "full_range"); if (!full_range_str) { int full_range = 0; switch (format) { case mlt_image_rgb: case mlt_image_rgba: case mlt_image_rgba64: case mlt_image_movit: case mlt_image_opengl_texture: full_range = 1; break; case mlt_image_yuv422: case mlt_image_yuv420p: case mlt_image_yuv422p16: case mlt_image_yuv420p10: case mlt_image_yuv444p10: case mlt_image_none: case mlt_image_invalid: break; } mlt_properties_set_int(frame_properties, "full_range", full_range); } else { int full_range = mlt_properties_get_int(frame_properties, "full_range"); if (format == mlt_image_rgb || format == mlt_image_rgba || format == mlt_image_rgba64) { // Correct full range if it is set incorrectly for RGB if (!full_range) { mlt_properties_set_int(frame_properties, "full_range", 1); } } } } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter self = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(self); mlt_image_format requested_format = *format; int ret = mlt_frame_get_image(frame, image, format, width, height, writable); if (ret || requested_format == mlt_image_movit || requested_format == mlt_image_none) return ret; const char *out_trc_str = mlt_properties_get(filter_properties, "force_trc"); if (!out_trc_str) out_trc_str = mlt_properties_get(frame_properties, "consumer.mlt_color_trc"); if (!out_trc_str) return 0; ensure_color_properties(self, frame); const char *frame_trc_str = mlt_properties_get(frame_properties, "color_trc"); mlt_color_trc frame_trc = mlt_image_color_trc_id(frame_trc_str); mlt_color_trc out_trc = mlt_image_color_trc_id(out_trc_str); if (*format == mlt_image_rgb || *format == mlt_image_rgba || *format == mlt_image_rgba64) { // Correct the TRC. For RGB, only support linear or iec61966-2-1. if (out_trc != mlt_color_trc_linear) { out_trc = mlt_color_trc_iec61966_2_1; } } if (out_trc == frame_trc) { // TRC matches. No conversion required return 0; } if (out_trc == mlt_color_trc_none || frame_trc == mlt_color_trc_none) { mlt_log_info(MLT_FILTER_SERVICE(self), "Missing TRC - Frame: %s\tOut: %s\n", mlt_image_color_trc_name(frame_trc), mlt_image_color_trc_name(out_trc)); return 0; } mlt_log_debug(MLT_FILTER_SERVICE(self), "color trc: %s -> %s\n", mlt_image_color_trc_name(frame_trc), mlt_image_color_trc_name(out_trc)); // Create a temporary frame to process the image mlt_frame clone_frame = mlt_frame_clone_image(frame, 0); mlt_service_lock(MLT_FILTER_SERVICE(self)); // Retrieve the saved filter mlt_cache_item cache_item = mlt_service_cache_get(MLT_FILTER_SERVICE(self), "t_filter"); if (!cache_item) { create_t_filter(self, clone_frame, out_trc); cache_item = mlt_service_cache_get(MLT_FILTER_SERVICE(self), "t_filter"); } if (!cache_item) { mlt_log_error(MLT_FILTER_SERVICE(self), "Unable to create colorspace filter\n"); return 1; } mlt_filter t_filter = mlt_cache_item_data(cache_item, NULL); // Process the cloned frame. The cloned frame references the same image // as the original frame. mlt_filter_process(t_filter, clone_frame); ret = mlt_frame_get_image(clone_frame, image, format, width, height, 0); mlt_cache_item_close(cache_item); mlt_service_unlock(MLT_FILTER_SERVICE(self)); mlt_properties_pass_list(frame_properties, MLT_FRAME_PROPERTIES(clone_frame), "colorspace color_trc color_primaries"); mlt_frame_close(clone_frame); return ret; } static mlt_frame filter_process(mlt_filter self, mlt_frame frame) { mlt_frame_push_service(frame, self); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter self) { mlt_service_cache_purge(MLT_FILTER_SERVICE(self)); self->child = NULL; self->close = NULL; self->parent.close = NULL; mlt_service_close(&self->parent); } mlt_filter filter_color_transform_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { const char *method = getenv("MLT_COLOR_TRANSFORM"); if (!method) method = arg ? arg : "auto"; // Test if the embedded filter is available. mlt_filter test_filter = NULL; if (!strcmp(method, "auto")) { if ((test_filter = mlt_factory_filter(profile, "avfilter.zscale", NULL))) { method = "avfilter.zscale"; } else if ((test_filter = mlt_factory_filter(profile, "sws_colortransform", NULL))) { method = "sws_colortransform"; } else if ((test_filter = mlt_factory_filter(profile, "avfilter.scale", NULL))) { method = "avfilter.scale"; } } else if (!strcmp(method, "avfilter.zscale")) { test_filter = mlt_factory_filter(profile, "avfilter.zscale", NULL); } else if (!strcmp(method, "sws_colortransform")) { test_filter = mlt_factory_filter(profile, "sws_colortransform", NULL); } else if (!strcmp(method, "avfilter.scale")) { test_filter = mlt_factory_filter(profile, "avfilter.scale", NULL); } if (!test_filter) { mlt_log_error(NULL, "[filter_color_transform] unable to create filter %s\n", method); return NULL; } mlt_filter_close(test_filter); mlt_filter self = mlt_filter_new(); if (self) { self->process = filter_process; self->close = filter_close; mlt_properties_set(MLT_FILTER_PROPERTIES(self), "method", method); } return self; } mlt-7.38.0/src/modules/core/filter_color_transform.yml000664 000000 000000 00000003060 15172202314 023051 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: color_transform title: Colorspace & Transfer Characteristics version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video - Hidden description: > Convert the image color to whatever was requested by the consumer. This filter is designed for use as a normalizer for the loader producer. If the "force_trc" property is set on this filter, the filter will force the output trc to be "force_trc"; Otherwise, if a property "consumer.mlt_color_trc" exists on the frame, then it normalizes the image to the trc requested by the consumer. parameters: - identifier: method argument: yes title: Method type: string values: - auto # Choose best available - avfilter.zscale - sws_colortransform - avfilter.scale description: | The underlying conversion method to use. The method is based on the encapsulated filter that is used. 'auto' will choose the best available in this order: * avfilter.zscale * sws_colortransform * avfilter.scale One can set the environment variable MLT_COLOR_TRANSFORM to override this. required: no readonly: no mutable: no default: auto - identifier: force_trc title: Force TRC type: string description: | Force the TRC to be as specified by this parameter instead of consumer.mlt_color_trc This property can be used to "undo" a TRC set by this filter when used as a normalizer required: no readonly: no mutable: no default: unset mlt-7.38.0/src/modules/core/filter_crop.c000664 000000 000000 00000022224 15172202314 020227 0ustar00rootroot000000 000000 /* * filter_crop.c -- cropping filter * Copyright (C) 2009-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include static void crop(uint8_t *src, uint8_t *dest, int bpp, int width, int height, int left, int right, int top, int bottom) { int src_stride = (width) *bpp; int dest_stride = (width - left - right) * bpp; int y = height - top - bottom + 1; src += top * src_stride + left * bpp; while (--y) { memcpy(dest, src, dest_stride); dest += dest_stride; src += src_stride; } } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_profile profile = mlt_frame_pop_service(frame); // Get the properties from the frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Correct Width/height if necessary if (*width == 0 || *height == 0) { *width = profile->width; *height = profile->height; } int left = mlt_properties_get_int(properties, "crop.left"); int right = mlt_properties_get_int(properties, "crop.right"); int top = mlt_properties_get_int(properties, "crop.top"); int bottom = mlt_properties_get_int(properties, "crop.bottom"); // Request the image at its original resolution if (left || right || top || bottom) { mlt_properties_set_int(properties, "rescale_width", mlt_properties_get_int(properties, "crop.original_width")); mlt_properties_set_int(properties, "rescale_height", mlt_properties_get_int(properties, "crop.original_height")); } // Now get the image error = mlt_frame_get_image(frame, image, format, width, height, writable); int owidth = *width - left - right; int oheight = *height - top - bottom; owidth = owidth < 0 ? 0 : owidth; oheight = oheight < 0 ? 0 : oheight; if ((owidth != *width || oheight != *height) && error == 0 && *image != NULL && owidth > 0 && oheight > 0) { int bpp; mlt_image_format requested_format = *format; if (requested_format == mlt_image_yuv420p) { // The crop function does not support planar. requested_format = mlt_image_yuv422; } if (requested_format == mlt_image_yuv422 && (left & 1 || right & 1)) { // Subsampled YUV requires even dimensions. Use RGB for odd dimensions. requested_format = mlt_image_rgb; } if (*format != requested_format && frame->convert_image) { frame->convert_image(frame, image, format, requested_format); } mlt_log_debug(NULL, "[filter crop] %s %dx%d -> %dx%d\n", mlt_image_format_name(*format), *width, *height, owidth, oheight); if (top % 2) mlt_properties_set_int(properties, "top_field_first", !mlt_properties_get_int(properties, "top_field_first")); // Create the output image int size = mlt_image_format_size(*format, owidth, oheight, &bpp); uint8_t *output = mlt_pool_alloc(size); if (output) { // Call the generic resize crop(*image, output, bpp, *width, *height, left, right, top, bottom); // Now update the frame mlt_frame_set_image(frame, output, size, mlt_pool_release); *image = output; } // We should resize the alpha too int alpha_size = 0; uint8_t *alpha = mlt_frame_get_alpha_size(frame, &alpha_size); if (alpha && alpha_size >= (*width * *height)) { uint8_t *newalpha = mlt_pool_alloc(owidth * oheight); if (newalpha) { crop(alpha, newalpha, 1, *width, *height, left, right, top, bottom); mlt_frame_set_alpha(frame, newalpha, owidth * oheight, mlt_pool_release); } } *width = owidth; *height = oheight; } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "active")) { // Push the get_image method on to the stack mlt_frame_push_service(frame, mlt_service_profile(MLT_FILTER_SERVICE(filter))); mlt_frame_push_get_image(frame, filter_get_image); } else { mlt_properties filter_props = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_props = MLT_FRAME_PROPERTIES(frame); int left = mlt_properties_get_int(filter_props, "left"); int right = mlt_properties_get_int(filter_props, "right"); int top = mlt_properties_get_int(filter_props, "top"); int bottom = mlt_properties_get_int(filter_props, "bottom"); int width = mlt_properties_get_int(frame_props, "meta.media.width"); int height = mlt_properties_get_int(frame_props, "meta.media.height"); int use_profile = mlt_properties_get_int(filter_props, "use_profile"); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); if (use_profile) { top = top * height / profile->height; bottom = bottom * height / profile->height; left = left * width / profile->width; right = right * width / profile->width; } if (mlt_properties_get_int(filter_props, "center")) { double aspect_ratio = mlt_frame_get_aspect_ratio(frame); if (aspect_ratio == 0.0) aspect_ratio = mlt_profile_sar(profile); double input_ar = aspect_ratio * width / height; double output_ar = mlt_profile_dar(mlt_service_profile(MLT_FILTER_SERVICE(filter))); int bias = mlt_properties_get_int(filter_props, "center_bias"); if (input_ar > output_ar) { left = right = (width - rint(output_ar * height / aspect_ratio)) / 2; if (use_profile) bias = bias * width / profile->width; if (abs(bias) > left) bias = bias < 0 ? -left : left; left -= bias; right += bias; } else { top = bottom = (height - rint(aspect_ratio * width / output_ar)) / 2; if (use_profile) bias = bias * height / profile->height; if (abs(bias) > top) bias = bias < 0 ? -top : top; top -= bias; bottom += bias; } } // Coerce the output to an even width because subsampled YUV with odd widths is too // risky for downstream processing to handle correctly. left += (width - left - right) & 1; if (width - left - right < 8) left = right = 0; if (height - top - bottom < 8) top = bottom = 0; mlt_properties_set_int(frame_props, "crop.left", left); mlt_properties_set_int(frame_props, "crop.right", right); mlt_properties_set_int(frame_props, "crop.top", top); mlt_properties_set_int(frame_props, "crop.bottom", bottom); mlt_properties_set_int(frame_props, "crop.original_width", width); mlt_properties_set_int(frame_props, "crop.original_height", height); mlt_properties_set_int(frame_props, "meta.media.width", width - left - right); mlt_properties_set_int(frame_props, "meta.media.height", height - top - bottom); } return frame; } /** Constructor for the filter. */ mlt_filter filter_crop_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = calloc(1, sizeof(struct mlt_filter_s)); if (mlt_filter_init(filter, filter) == 0) { filter->process = filter_process; if (arg) mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "active", atoi(arg)); } return filter; } mlt-7.38.0/src/modules/core/filter_crop.yml000664 000000 000000 00000003657 15172202314 020617 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: crop title: Crop version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en description: Remove pixels from the edges of the video. tags: - Video notes: > This filter is meant to be included as a normalizing filter attached automatically by the loader so that cropping occurs early in the processing stack and can request the full resolution of the source image. Then, a second instance of the filter may be applied to set the parameters of the crop operation. parameters: - identifier: active argument: yes title: Active description: Whether to do the processing (1) or simply set the parameters. type: integer minimum: 0 maximum: 1 default: 0 - identifier: left title: Left type: integer minimum: 0 default: 0 unit: pixels - identifier: right title: Right type: integer minimum: 0 default: 0 unit: pixels - identifier: top title: Top type: integer minimum: 0 default: 0 unit: pixels - identifier: bottom title: Bottom type: integer minimum: 0 default: 0 unit: pixels - identifier: center title: Center crop description: Whether to automatically crop whatever is needed to fill the output frame and prevent padding. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: center_bias title: Center balance description: When center crop is enabled, offset the center point. type: integer minimum: 0 default: 0 unit: pixels - identifier: use_profile title: Use profile resolution description: > This is useful for proxy editing. Normally all crop values are expressed in terms of pixels of the source footage, but this option makes them relative to the profile resolution. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-7.38.0/src/modules/core/filter_fieldorder.c000664 000000 000000 00000012261 15172202314 021403 0ustar00rootroot000000 000000 /* * filter_fieldorder.c -- change field dominance * Copyright (C) 2011-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the properties from the frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Get the input image, width and height int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error && *image) { int tff = mlt_properties_get_int(properties, "consumer.top_field_first"); // Provides a manual override for misreported field order if (mlt_properties_get(properties, "meta.top_field_first")) mlt_properties_set_int(properties, "top_field_first", mlt_properties_get_int(properties, "meta.top_field_first")); mlt_log_debug(NULL, "TFF in %d out %d\n", mlt_properties_get_int(properties, "top_field_first"), tff); if (mlt_properties_get_int(properties, "meta.swap_fields") && mlt_properties_get(properties, "progressive") && mlt_properties_get_int(properties, "progressive") == 0) { // We only work with non-planar formats if (*format == mlt_image_yuv420p && frame->convert_image) error = frame->convert_image(frame, image, format, mlt_image_yuv422); // Make a new image int bpp; int size = mlt_image_format_size(*format, *width, *height, &bpp); uint8_t *new_image = mlt_pool_alloc(size); int stride = *width * bpp; int i = *height + 1; uint8_t *src = *image; // Set the new image mlt_frame_set_image(frame, new_image, size, mlt_pool_release); *image = new_image; while (--i) { memcpy(new_image, src + stride * !(i % 2), stride); new_image += stride; src += stride * (i % 2) * 2; } } // Correct field order if needed if (tff != -1 && mlt_properties_get_int(properties, "top_field_first") != tff && mlt_properties_get(properties, "progressive") && mlt_properties_get_int(properties, "progressive") == 0) { mlt_log_timings_begin(); // We only work with non-vertical luma/chroma scaled if (*format == mlt_image_yuv420p) { *format = mlt_image_yuv422; mlt_frame_get_image(frame, image, format, width, height, writable); } // Shift the entire image down by one line int p, strides[4], size; uint8_t *new_planes[4], *old_planes[4], *new_image; size = mlt_image_format_size(*format, *width, *height, NULL); new_image = mlt_pool_alloc(size); mlt_image_format_planes(*format, *width, *height, new_image, new_planes, strides); mlt_image_format_planes(*format, *width, *height, *image, old_planes, strides); for (p = 0; p < 4; p++) { if (!new_planes[p]) continue; memcpy(new_planes[p], old_planes[p], strides[p]); memcpy(new_planes[p] + strides[p], old_planes[p], strides[p] * (*height - 1)); } // Set the new image mlt_frame_set_image(frame, new_image, size, mlt_pool_release); *image = new_image; mlt_log_timings_end(NULL, "shifting_fields"); } // Set the normalized field order mlt_properties_set_int(properties, "top_field_first", tff); mlt_properties_set_int(properties, "meta.top_field_first", tff); } return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_get_image(frame, get_image); return frame; } mlt_filter filter_fieldorder_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = calloc(1, sizeof(*filter)); if (mlt_filter_init(filter, NULL) == 0) { filter->process = process; } return filter; } mlt-7.38.0/src/modules/core/filter_fieldorder.yml000664 000000 000000 00000001371 15172202314 021762 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: fieldorder title: Field order description: Correct the field order of interlaced video. version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video - Hidden notes: > This filter is automatically invoked by the loader as part of image normalization. It compares the frame property top_field_first to the consumer property with the same name to determine if correction is needed. It performs field order correction by shifting the image down by one line. If you set the property meta.swap_fields=1 on the producer, then this filter swaps the fields of an interlaced frame in addition to any field order correction by shifting the image. mlt-7.38.0/src/modules/core/filter_gamma.c000664 000000 000000 00000005440 15172202314 020347 0ustar00rootroot000000 000000 /* * filter_gamma.c -- gamma filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0) { // Get the gamma value double gamma = mlt_properties_anim_get_double(properties, "gamma", position, length); if (gamma != 1.0) { uint8_t *p = *image; uint8_t *q = *image + *width * *height * 2; // Calculate the look up table double exp = 1 / gamma; uint8_t lookup[256]; int i; for (i = 0; i < 256; i++) lookup[i] = (uint8_t) (pow((double) i / 255.0, exp) * 255); while (p != q) { *p = lookup[*p]; p += 2; } } } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_gamma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "gamma", arg == NULL ? "1" : arg); } return filter; } mlt-7.38.0/src/modules/core/filter_gamma.yml000664 000000 000000 00000000667 15172202314 020734 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: gamma title: Gamma version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Adjust image luma using a non-linear power-law curve. parameters: - identifier: gamma argument: yes title: Gamma type: float description: The exponential factor of the power-law curve mutable: yes animation: yes default: 1.0 mlt-7.38.0/src/modules/core/filter_greyscale.c000664 000000 000000 00000004017 15172202314 021242 0ustar00rootroot000000 000000 /* * filter_greyscale.c -- greyscale filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0) { uint8_t *p = *image; uint8_t *q = *image + *width * *height * 2; while (p++ != q) *p++ = 128; } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_greyscale_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) filter->process = filter_process; return filter; } mlt-7.38.0/src/modules/core/filter_greyscale.yml000664 000000 000000 00000000342 15172202314 021616 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: greyscale title: Greyscale version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Convert colour image to greyscale mlt-7.38.0/src/modules/core/filter_imageconvert.c000664 000000 000000 00000040771 15172202314 021756 0ustar00rootroot000000 000000 /* * filter_imageconvert.c -- colorspace and pixel format converter * Copyright (C) 2009-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /** This macro converts a YUV value to the RGB color space. */ #define RGB2YUV_601_UNSCALED(r, g, b, y, u, v) \ y = (299 * r + 587 * g + 114 * b) >> 10; \ u = ((-169 * r - 331 * g + 500 * b) >> 10) + 128; \ v = ((500 * r - 419 * g - 81 * b) >> 10) + 128; \ y = y < 16 ? 16 : y; \ u = u < 16 ? 16 : u; \ v = v < 16 ? 16 : v; \ y = y > 235 ? 235 : y; \ u = u > 240 ? 240 : u; \ v = v > 240 ? 240 : v /** This macro converts a YUV value to the RGB color space. */ #define YUV2RGB_601_UNSCALED(y, u, v, r, g, b) \ r = ((1024 * y + 1404 * (v - 128)) >> 10); \ g = ((1024 * y - 715 * (v - 128) - 345 * (u - 128)) >> 10); \ b = ((1024 * y + 1774 * (u - 128)) >> 10); \ r = r < 0 ? 0 : r > 255 ? 255 : r; \ g = g < 0 ? 0 : g > 255 ? 255 : g; \ b = b < 0 ? 0 : b > 255 ? 255 : b; #define SCALED 1 #if SCALED #define RGB2YUV_601 RGB2YUV_601_SCALED #define YUV2RGB_601 YUV2RGB_601_SCALED #else #define RGB2YUV_601 RGB2YUV_601_UNSCALED #define YUV2RGB_601 YUV2RGB_601_UNSCALED #endif static void convert_yuv422_to_rgba(mlt_image src, mlt_image dst) { int yy, uu, vv; int r, g, b; mlt_image_set_values(dst, NULL, mlt_image_rgba, src->width, src->height); mlt_image_alloc_data(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line; uint8_t *pAlpha = src->planes[3] + src->strides[3] * line; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; int total = src->width / 2 + 1; if (pAlpha) while (--total) { yy = pSrc[0]; uu = pSrc[1]; vv = pSrc[3]; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[0] = r; pDst[1] = g; pDst[2] = b; pDst[3] = *pAlpha++; yy = pSrc[2]; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[4] = r; pDst[5] = g; pDst[6] = b; pDst[7] = *pAlpha++; pSrc += 4; pDst += 8; } else while (--total) { yy = pSrc[0]; uu = pSrc[1]; vv = pSrc[3]; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[0] = r; pDst[1] = g; pDst[2] = b; pDst[3] = 0xff; yy = pSrc[2]; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[4] = r; pDst[5] = g; pDst[6] = b; pDst[7] = 0xff; pSrc += 4; pDst += 8; } } } static void convert_yuv422_to_rgb(mlt_image src, mlt_image dst) { int yy, uu, vv; int r, g, b; mlt_image_set_values(dst, NULL, mlt_image_rgb, src->width, src->height); mlt_image_alloc_data(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; int total = src->width / 2 + 1; while (--total) { yy = pSrc[0]; uu = pSrc[1]; vv = pSrc[3]; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[0] = r; pDst[1] = g; pDst[2] = b; yy = pSrc[2]; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[3] = r; pDst[4] = g; pDst[5] = b; pSrc += 4; pDst += 6; } } } static void convert_rgba_to_yuv422(mlt_image src, mlt_image dst) { int y0, y1, u0, u1, v0, v1; int r, g, b; mlt_image_set_values(dst, NULL, mlt_image_yuv422, src->width, src->height); mlt_image_alloc_data(dst); mlt_image_alloc_alpha(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; uint8_t *pAlpha = dst->planes[3] + dst->strides[3] * line; int j = src->width / 2 + 1; while (--j) { r = *pSrc++; g = *pSrc++; b = *pSrc++; *pAlpha++ = *pSrc++; RGB2YUV_601(r, g, b, y0, u0, v0); r = *pSrc++; g = *pSrc++; b = *pSrc++; *pAlpha++ = *pSrc++; RGB2YUV_601(r, g, b, y1, u1, v1); *pDst++ = y0; *pDst++ = (u0 + u1) >> 1; *pDst++ = y1; *pDst++ = (v0 + v1) >> 1; } if (src->width % 2) { r = *pSrc++; g = *pSrc++; b = *pSrc++; *pAlpha++ = *pSrc++; RGB2YUV_601(r, g, b, y0, u0, v0); *pDst++ = y0; *pDst++ = u0; } } } static void convert_rgb_to_yuv422(mlt_image src, mlt_image dst) { int y0, y1, u0, u1, v0, v1; int r, g, b; mlt_image_set_values(dst, NULL, mlt_image_yuv422, src->width, src->height); mlt_image_alloc_data(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; int j = src->width / 2 + 1; while (--j) { r = *pSrc++; g = *pSrc++; b = *pSrc++; RGB2YUV_601(r, g, b, y0, u0, v0); r = *pSrc++; g = *pSrc++; b = *pSrc++; RGB2YUV_601(r, g, b, y1, u1, v1); *pDst++ = y0; *pDst++ = (u0 + u1) >> 1; *pDst++ = y1; *pDst++ = (v0 + v1) >> 1; } if (src->width % 2) { r = *pSrc++; g = *pSrc++; b = *pSrc++; RGB2YUV_601(r, g, b, y0, u0, v0); *pDst++ = y0; *pDst++ = u0; } } } static void convert_yuv420p_to_yuv422(mlt_image src, mlt_image dst) { mlt_image_set_values(dst, NULL, mlt_image_yuv422, src->width, src->height); mlt_image_alloc_data(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrcY = src->planes[0] + src->strides[0] * line; uint8_t *pSrcU = src->planes[1] + src->strides[1] * line / 2; uint8_t *pSrcV = src->planes[2] + src->strides[2] * line / 2; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; int j = src->width / 2 + 1; while (--j) { *pDst++ = *pSrcY++; *pDst++ = *pSrcU++; *pDst++ = *pSrcY++; *pDst++ = *pSrcV++; } } } static void convert_yuv420p_to_rgb(mlt_image src, mlt_image dst) { int yy, uu, vv; int r, g, b; mlt_image_set_values(dst, NULL, mlt_image_rgb, src->width, src->height); mlt_image_alloc_data(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrcY = src->planes[0] + src->strides[0] * line; uint8_t *pSrcU = src->planes[1] + src->strides[1] * line / 2; uint8_t *pSrcV = src->planes[2] + src->strides[2] * line / 2; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; int total = src->width / 2 + 1; while (--total) { yy = *pSrcY++; uu = *pSrcU++; vv = *pSrcV++; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[0] = r; pDst[1] = g; pDst[2] = b; yy = *pSrcY++; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[3] = r; pDst[4] = g; pDst[5] = b; pDst += 6; } } } static void convert_yuv420p_to_rgba(mlt_image src, mlt_image dst) { int yy, uu, vv; int r, g, b; mlt_image_set_values(dst, NULL, mlt_image_rgba, src->width, src->height); mlt_image_alloc_data(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrcY = src->planes[0] + src->strides[0] * line; uint8_t *pSrcU = src->planes[1] + src->strides[1] * line / 2; uint8_t *pSrcV = src->planes[2] + src->strides[2] * line / 2; uint8_t *pSrcA = src->planes[3] + src->strides[3] * line; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; int total = src->width / 2 + 1; if (pSrcA) while (--total) { yy = *pSrcY++; uu = *pSrcU++; vv = *pSrcV++; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[0] = r; pDst[1] = g; pDst[2] = b; pDst[3] = *pSrcA++; yy = *pSrcY++; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[4] = r; pDst[5] = g; pDst[6] = b; pDst[7] = *pSrcA++; pDst += 8; } else while (--total) { yy = *pSrcY++; uu = *pSrcU++; vv = *pSrcV++; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[0] = r; pDst[1] = g; pDst[2] = b; pDst[3] = 0xff; yy = *pSrcY++; YUV2RGB_601(yy, uu, vv, r, g, b); pDst[4] = r; pDst[5] = g; pDst[6] = b; pDst[7] = 0xff; pDst += 8; } } } static void convert_yuv422_to_yuv420p(mlt_image src, mlt_image dst) { int lines = src->height; int pixels = src->width; mlt_image_set_values(dst, NULL, mlt_image_yuv420p, src->width, src->height); mlt_image_alloc_data(dst); // Y for (int line = 0; line < lines; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; for (int pixel = 0; pixel < pixels; pixel++) { *pDst++ = *pSrc; pSrc += 2; } } lines = src->height / 2; pixels = src->width / 2; // U for (int line = 0; line < lines; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line * 2 + 1; uint8_t *pDst = dst->planes[1] + dst->strides[1] * line; for (int pixel = 0; pixel < pixels; pixel++) { *pDst++ = *pSrc; pSrc += 4; } } // V for (int line = 0; line < lines; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line * 2 + 3; uint8_t *pDst = dst->planes[2] + dst->strides[2] * line; for (int pixel = 0; pixel < pixels; pixel++) { *pDst++ = *pSrc; pSrc += 4; } } } static void convert_rgb_to_rgba(mlt_image src, mlt_image dst) { mlt_image_set_values(dst, NULL, mlt_image_rgba, src->width, src->height); mlt_image_alloc_data(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line; uint8_t *pAlpha = src->planes[3] + src->strides[3] * line; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; int total = src->width + 1; if (pAlpha) while (--total) { *pDst++ = pSrc[0]; *pDst++ = pSrc[1]; *pDst++ = pSrc[2]; *pDst++ = *pAlpha++; pSrc += 3; } else while (--total) { *pDst++ = pSrc[0]; *pDst++ = pSrc[1]; *pDst++ = pSrc[2]; *pDst++ = 0xff; pSrc += 3; } } } static void convert_rgba_to_rgb(mlt_image src, mlt_image dst) { mlt_image_set_values(dst, NULL, mlt_image_rgb, src->width, src->height); mlt_image_alloc_data(dst); mlt_image_alloc_alpha(dst); for (int line = 0; line < src->height; line++) { uint8_t *pSrc = src->planes[0] + src->strides[0] * line; uint8_t *pDst = dst->planes[0] + dst->strides[0] * line; uint8_t *pAlpha = dst->planes[3] + dst->strides[3] * line; int total = src->width + 1; while (--total) { *pDst++ = pSrc[0]; *pDst++ = pSrc[1]; *pDst++ = pSrc[2]; *pAlpha++ = pSrc[3]; pSrc += 4; } } } typedef void (*conversion_function)(mlt_image src, mlt_image dst); static conversion_function conversion_matrix[mlt_image_invalid - 1][mlt_image_invalid - 1] = { {NULL, convert_rgb_to_rgba, convert_rgb_to_yuv422, NULL, NULL, NULL, NULL}, {convert_rgba_to_rgb, NULL, convert_rgba_to_yuv422, NULL, NULL, NULL, NULL}, {convert_yuv422_to_rgb, convert_yuv422_to_rgba, NULL, convert_yuv422_to_yuv420p, NULL, NULL, NULL}, {convert_yuv420p_to_rgb, convert_yuv420p_to_rgba, convert_yuv420p_to_yuv422, NULL, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL, NULL, NULL}, }; static int convert_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, mlt_image_format requested_format) { int error = 0; mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); if (*format != requested_format) { conversion_function converter = conversion_matrix[*format - 1][requested_format - 1]; mlt_log_debug(NULL, "[filter imageconvert] %s -> %s @ %dx%d\n", mlt_image_format_name(*format), mlt_image_format_name(requested_format), width, height); if (converter) { struct mlt_image_s src; struct mlt_image_s dst; mlt_image_set_values(&src, *buffer, *format, width, height); if (requested_format == mlt_image_rgba && mlt_frame_get_alpha(frame)) { // imageconvert leaves the alpha buffer alone except in the case of rgba. // For rgba input, an alpha buffer will be created and added to the frame. // For rgba output, the alpha buffer will be copied to the rgba and the buffer is removed from the frame. src.planes[3] = mlt_frame_get_alpha(frame); src.strides[3] = src.width; } converter(&src, &dst); mlt_frame_set_image(frame, dst.data, 0, dst.release_data); if (requested_format == mlt_image_rgba) { // Clear the alpha buffer on the frame mlt_frame_set_alpha(frame, NULL, 0, NULL); } else if (dst.alpha) { mlt_frame_set_alpha(frame, dst.alpha, 0, dst.release_alpha); } *buffer = dst.data; *format = dst.format; } else { mlt_log_error(NULL, "imageconvert: no conversion from %s to %s\n", mlt_image_format_name(*format), mlt_image_format_name(requested_format)); error = 1; } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { if (!frame->convert_image) frame->convert_image = convert_image; return frame; } /** Constructor for the filter. */ mlt_filter filter_imageconvert_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = calloc(1, sizeof(struct mlt_filter_s)); if (mlt_filter_init(filter, filter) == 0) { filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/core/filter_imageconvert.yml000664 000000 000000 00000000763 15172202314 022332 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: imageconvert title: Basic Image Converter version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Converts the colorspace and pixel format. notes: > This is not intended to be created directly. Rather, the loader producer loads it if it is available to set the convert_image function pointer on frames. This implementation is old and naive by assuming all YCbCr video is ITU-R BT.601 and all RGB is sRGB. mlt-7.38.0/src/modules/core/filter_luma.c000664 000000 000000 00000014156 15172202314 020227 0ustar00rootroot000000 000000 /* * filter_luma.c -- luma filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_service_lock(MLT_FILTER_SERVICE(filter)); mlt_transition luma = mlt_properties_get_data(properties, "luma", NULL); mlt_frame b_frame = mlt_properties_get_data(properties, "frame", NULL); mlt_properties b_frame_props = b_frame ? MLT_FRAME_PROPERTIES(b_frame) : NULL; int out = mlt_properties_get_int(properties, "period"); int cycle = mlt_properties_get_int(properties, "cycle"); int duration = mlt_properties_get_int(properties, "duration"); mlt_position position = mlt_filter_get_position(filter, frame); out = out ? out + 1 : 25; if (cycle) out = cycle; if (duration < 1 || duration > out) duration = out; *format = mlt_image_yuv422; if (b_frame == NULL || mlt_properties_get_int(b_frame_props, "width") != *width || mlt_properties_get_int(b_frame_props, "height") != *height) { b_frame = mlt_frame_init(MLT_FILTER_SERVICE(filter)); mlt_properties_set_data(properties, "frame", b_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } if (luma == NULL) { char *resource = mlt_properties_get(properties, "resource"); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); luma = mlt_factory_transition(profile, "luma", resource); if (luma != NULL) { mlt_properties luma_properties = MLT_TRANSITION_PROPERTIES(luma); mlt_properties_set_int(luma_properties, "in", 0); mlt_properties_set_int(luma_properties, "out", duration - 1); mlt_properties_set_int(luma_properties, "reverse", 1); mlt_properties_set_data(properties, "luma", luma, 0, (mlt_destructor) mlt_transition_close, NULL); } } mlt_position modulo_pos = MLT_POSITION_MOD(position, out); mlt_log_debug(MLT_FILTER_SERVICE(filter), "pos " MLT_POSITION_FMT " mod period " MLT_POSITION_FMT "\n", position, modulo_pos); if (luma != NULL && (mlt_properties_get(properties, "blur") != NULL || (position >= duration && modulo_pos < duration - 1))) { mlt_properties luma_properties = MLT_TRANSITION_PROPERTIES(luma); mlt_properties_pass(luma_properties, properties, "luma."); int in = position / out * out + mlt_frame_get_position(frame) - position; mlt_properties_set_int(luma_properties, "in", in); mlt_properties_set_int(luma_properties, "out", in + duration - 1); mlt_transition_process(luma, frame, b_frame); } error = mlt_frame_get_image(frame, image, format, width, height, 1); // We only need a copy of the last frame in the cycle, but we could miss it // with realtime frame-dropping, so we copy the last several frames of the cycle. if (error == 0 && modulo_pos > out - duration) { mlt_properties a_props = MLT_FRAME_PROPERTIES(frame); int size = 0; uint8_t *src = mlt_properties_get_data(a_props, "image", &size); uint8_t *dst = mlt_pool_alloc(size); if (dst != NULL) { mlt_log_debug(MLT_FILTER_SERVICE(filter), "copying frame " MLT_POSITION_FMT "\n", modulo_pos); mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); memcpy(dst, src, size); mlt_frame_set_image(b_frame, dst, size, mlt_pool_release); mlt_properties_set_int(b_props, "width", *width); mlt_properties_set_int(b_props, "height", *height); mlt_properties_set_int(b_props, "format", *format); } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the filter on to the stack mlt_frame_push_service(frame, filter); // Push the get_image on to the stack mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_luma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); filter->process = filter_process; if (arg != NULL) mlt_properties_set(properties, "resource", arg); } return filter; } mlt-7.38.0/src/modules/core/filter_luma.yml000664 000000 000000 00000002111 15172202314 020572 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: luma title: Wipe version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Applies a luma transition between the current and next frames. Useful for transitions from a slideshow created using producer pixbuf. parameters: - identifier: resource argument: yes title: File type: string description: The luma map file to be used for the transition - identifier: cycle title: Period type: integer description: > The duration between iterations of the transition. For best results set this to a multiple of ttl used in pixbuf. mutable: yes default: 25 - identifier: duration title: Duration type: integer description: The length of the transition. mutable: yes default: 25 - identifier: luma.* title: Luma Properties description: > All properties beginning with "luma." are passed to the encapsulated luma transition. For example, luma.out controls the duration of the wipe. mutable: yes mlt-7.38.0/src/modules/core/filter_mask_apply.c000664 000000 000000 00000013443 15172202314 021427 0ustar00rootroot000000 000000 /* * filter_mask_apply.c -- composite atop a cloned frame made by mask_start * Copyright (C) 2018-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include static int dummy_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); *image = mlt_properties_get_data(properties, "image", NULL); *format = mlt_properties_get_int(properties, "format"); *width = mlt_properties_get_int(properties, "width"); *height = mlt_properties_get_int(properties, "height"); return 0; } static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_transition transition = mlt_frame_pop_service(frame); if (*format == mlt_image_rgba64) { mlt_frame_pop_service_int(frame); } else { *format = mlt_frame_pop_service_int(frame); } int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_frame clone = mlt_properties_get_data(properties, "mask frame", NULL); if (clone) { mlt_frame_push_get_image(frame, dummy_get_image); mlt_service_lock(MLT_TRANSITION_SERVICE(transition)); mlt_transition_process(transition, clone, frame); mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); error = mlt_frame_get_image(clone, image, format, width, height, writable); if (!error) { int size = mlt_image_format_size(*format, *width, *height, NULL); mlt_frame_set_image(frame, *image, size, NULL); } } } return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_transition transition = mlt_properties_get_data(properties, "instance", NULL); char *name = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "transition"); if (!name || !strcmp("", name)) return frame; // Create the transition if needed. if (!transition || !mlt_properties_get(MLT_FILTER_PROPERTIES(transition), "mlt_service") || strcmp(name, mlt_properties_get(MLT_FILTER_PROPERTIES(transition), "mlt_service"))) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); transition = mlt_factory_transition(profile, name, NULL); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "instance", transition, 0, (mlt_destructor) mlt_transition_close, NULL); } if (transition) { mlt_properties transition_props = MLT_TRANSITION_PROPERTIES(transition); int type = mlt_properties_get_int(transition_props, "_transition_type"); int hide = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "hide"); mlt_properties_pass_list(transition_props, properties, "in out"); mlt_properties_pass(transition_props, properties, "transition."); // Only if video transition on visible track. if ((type & 1) && !mlt_frame_is_test_card(frame) && !(hide & 1)) { mlt_frame_push_service_int(frame, mlt_image_format_id( mlt_properties_get(properties, "mlt_image_format"))); mlt_frame_push_service(frame, transition); mlt_frame_push_get_image(frame, get_image); } if (type == 0) mlt_properties_debug(transition_props, "unknown transition type", stderr); } else { mlt_properties_debug(properties, "mask_failed to create transition", stderr); } return frame; } mlt_filter filter_mask_apply_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { mlt_properties transitions = mlt_repository_transitions(mlt_factory_repository()); int qtblend_exists = mlt_properties_exists(transitions, "qtblend"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "transition", arg ? arg : qtblend_exists ? "qtblend" : "frei0r.cairoblend"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "mlt_image_format", "rgba"); filter->process = process; } return filter; } mlt-7.38.0/src/modules/core/filter_mask_apply.yml000664 000000 000000 00000001744 15172202314 022007 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: mask_apply title: Apply a filter mask version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > This filter works in conjunction with the mask_start filter, which makes a snapshot of the frame. There can be other filters between the two, which are masked by the alpha channel via this filter's compositing transition. parameters: - identifier: transition title: Transition description: The name of a compositing or blending transition type: string argument: yes default: qtblend mutable: yes - identifier: transition.* type: properties description: > Properties to set on the encapsulated transition - identifier: mlt_image_format title: MLT image format type: string description: Set to the same image format that the transition needs. mutable: yes default: rgba values: - yuv422 - rgb - rgba mlt-7.38.0/src/modules/core/filter_mask_start.c000664 000000 000000 00000007426 15172202314 021443 0ustar00rootroot000000 000000 /* * filter_mask_start.c -- clone a frame before invoking a filter * Copyright (C) 2018-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error && !mlt_properties_exists(properties, "mask frame")) { mlt_frame clone = mlt_frame_clone(frame, 1); clone->convert_audio = frame->convert_audio; clone->convert_image = frame->convert_image; mlt_properties_set_data(properties, "mask frame", clone, 0, (mlt_destructor) mlt_frame_close, NULL); } return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_filter instance = mlt_properties_get_data(properties, "instance", NULL); const char *name = mlt_properties_get(properties, "filter"); if (!name || !strcmp("", name) || !strcmp("0", name)) return frame; // Create the filter if needed. if (!instance || !mlt_properties_get(MLT_FILTER_PROPERTIES(instance), "mlt_service") || strcmp(name, mlt_properties_get(MLT_FILTER_PROPERTIES(instance), "mlt_service"))) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); instance = mlt_factory_filter(profile, name, NULL); mlt_properties_set_data(properties, "instance", instance, 0, (mlt_destructor) mlt_filter_close, NULL); } if (instance) { mlt_properties instance_props = MLT_FILTER_PROPERTIES(instance); mlt_properties_pass_list(instance_props, properties, "in out"); mlt_properties_pass(instance_props, properties, "filter."); mlt_properties_clear(properties, "filter.producer.refresh"); mlt_frame_push_get_image(frame, get_image); return mlt_filter_process(instance, frame); } else { mlt_properties_debug(properties, "failed to create filter", stderr); return frame; } } mlt_filter filter_mask_start_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "filter", arg ? arg : "frei0r.alphaspot"); filter->process = process; } return filter; } mlt-7.38.0/src/modules/core/filter_mask_start.yml000664 000000 000000 00000001656 15172202314 022021 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: mask_start title: Setup a filter mask version: 4 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > This filter works in conjunction with the mask_apply filter to make filter masking easier. This filter makes a snapshop of the frame before applying a filter, typically one to affect the alpha channel defining the mask. Then, the mask_apply filter uses a transition to composite the current frame's image over the snapshot. The typical use case is to add filters in the following sequence: mask_start, zero or more filters, mask_apply. parameters: - identifier: filter title: Filter description: The name of a filter type: string argument: yes default: frei0r.alphaspot mutable: yes - identifier: filter.* type: properties description: > Properties to set on the encapsulated filter mlt-7.38.0/src/modules/core/filter_mirror.c000664 000000 000000 00000030230 15172202314 020572 0ustar00rootroot000000 000000 /* * filter_mirror.c -- mirror filter * Copyright (C) 2003-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include typedef struct { mlt_image image; char *mirror; int reverse; } slice_desc; static int do_slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *desc = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->image->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; int slice_line_start_half, slice_height_half = mlt_slices_size_slice(jobs, index, desc->image->height / 2, &slice_line_start_half); int slice_line_end_half = slice_line_start_half + slice_height_half; int uneven_w = (desc->image->width % 2) * 2; int i; if (!strcmp(desc->mirror, "horizontal")) { for (i = slice_line_start; i < slice_line_end; i++) { uint8_t *p = desc->image->planes[0] + desc->image->strides[0] * i; uint8_t *q = p + desc->image->width * 2; if (!desc->reverse) { while (p < q) { *p++ = *(q - 2); *p++ = *(q - 3 - uneven_w); *p++ = *(q - 4); *p++ = *(q - 1 - uneven_w); q -= 4; } } else { while (p < q) { *(q - 2) = *p++; *(q - 3 - uneven_w) = *p++; *(q - 4) = *p++; *(q - 1 - uneven_w) = *p++; q -= 4; } } } if (desc->image->planes[3]) { for (i = slice_line_start; i < slice_line_end; i++) { uint8_t *a = desc->image->planes[3] + desc->image->strides[3] * i; uint8_t *b = a + desc->image->width - 1; if (!desc->reverse) { while (a < b) { *a++ = *b--; *a++ = *b--; } } else { while (a < b) { *b-- = *a++; *b-- = *a++; } } } } } else if (!strcmp(desc->mirror, "vertical")) { for (i = slice_line_start_half; i < slice_line_end_half; i++) { uint16_t *p = (uint16_t *) (desc->image->planes[0] + (desc->image->strides[0] * i)); uint16_t *q = (uint16_t *) (desc->image->planes[0] + (desc->image->strides[0] * (desc->image->height - i - 1))); int j = desc->image->width; if (!desc->reverse) { while (j--) { *p++ = *q++; } } else { while (j--) { *q++ = *p++; } } } if (desc->image->planes[3]) { for (i = slice_line_start_half; i < slice_line_end_half; i++) { int j = desc->image->width; uint8_t *a = desc->image->planes[3] + (desc->image->strides[3] * i); uint8_t *b = desc->image->planes[3] + (desc->image->strides[3] * (desc->image->height - i - 1)); if (!desc->reverse) while (j--) *a++ = *b++; else while (j--) *b++ = *a++; } } } else if (!strcmp(desc->mirror, "diagonal")) { for (i = slice_line_start; i < slice_line_end; i++) { uint8_t *p = desc->image->planes[0] + (desc->image->strides[0] * i); uint8_t *q = desc->image->planes[0] + (desc->image->strides[0] * (desc->image->height - i - 1)); int j = ((desc->image->width * (desc->image->height - i)) / desc->image->height) / 2; if (!desc->reverse) { while (j--) { *p++ = *(q - 2); *p++ = *(q - 3 - uneven_w); *p++ = *(q - 4); *p++ = *(q - 1 - uneven_w); q -= 4; } } else { while (j--) { *(q - 2) = *p++; *(q - 3 - uneven_w) = *p++; *(q - 4) = *p++; *(q - 1 - uneven_w) = *p++; q -= 4; } } } if (desc->image->planes[3]) { int i; for (i = slice_line_start; i < slice_line_end; i++) { int j = (desc->image->width * (desc->image->height - i)) / desc->image->height; uint8_t *a = desc->image->planes[3] + (desc->image->strides[3] * i); uint8_t *b = desc->image->planes[3] + (desc->image->strides[3] * (desc->image->height - i - 1)); if (!desc->reverse) while (j--) *a++ = *b--; else while (j--) *b-- = *a++; } } } else if (!strcmp(desc->mirror, "xdiagonal")) { for (i = slice_line_start; i < slice_line_end; i++) { uint8_t *p = desc->image->planes[0] + (desc->image->strides[0] * (i + 1)); uint8_t *q = desc->image->planes[0] + (desc->image->strides[0] * (desc->image->height - i)); int j = ((desc->image->width * (desc->image->height - i)) / desc->image->height) / 2; if (!desc->reverse) { while (j--) { *q++ = *(p - 2); *q++ = *(p - 3 - uneven_w); *q++ = *(p - 4); *q++ = *(p - 1 - uneven_w); p -= 4; } } else { while (j--) { *(p - 2) = *q++; *(p - 3 - uneven_w) = *q++; *(p - 4) = *q++; *(p - 1 - uneven_w) = *q++; p -= 4; } } } if (desc->image->planes[3]) { int i; for (i = slice_line_start; i < slice_line_end; i++) { int j = ((desc->image->width * (desc->image->height - i)) / desc->image->height); uint8_t *a = desc->image->planes[3] + (desc->image->strides[3] * i) + desc->image->width - 1; uint8_t *b = desc->image->planes[3] + (desc->image->strides[3] * (desc->image->height - i - 1)); if (!desc->reverse) while (j--) *b++ = *a--; else while (j--) *a-- = *b++; } } } else if (!strcmp(desc->mirror, "flip")) { uint8_t t[4]; for (i = slice_line_start; i < slice_line_end; i++) { uint8_t *p = desc->image->planes[0] + (desc->image->strides[0] * i); uint8_t *q = p + desc->image->width * 2; while (p < q) { t[0] = p[0]; t[1] = p[1 + uneven_w]; t[2] = p[2]; t[3] = p[3 + uneven_w]; *p++ = *(q - 2); *p++ = *(q - 3 - uneven_w); *p++ = *(q - 4); *p++ = *(q - 1 - uneven_w); *(--q) = t[3]; *(--q) = t[0]; *(--q) = t[1]; *(--q) = t[2]; } } if (desc->image->planes[3]) { uint8_t c; for (i = slice_line_start; i < slice_line_end; i++) { uint8_t *a = desc->image->planes[3] + (desc->image->strides[3] * i); uint8_t *b = a + desc->image->width - 1; while (a < b) { c = *a; *a++ = *b; *b-- = c; } } } } else if (!strcmp(desc->mirror, "flop")) { uint16_t t; for (i = slice_line_start_half; i < slice_line_end_half; i++) { uint16_t *p = (uint16_t *) (desc->image->planes[0] + (desc->image->strides[0] * i)); uint16_t *q = (uint16_t *) (desc->image->planes[0] + (desc->image->strides[0] * (desc->image->height - i - 1))); int j = desc->image->width; while (j--) { t = *p; *p++ = *q; *q++ = t; } } if (desc->image->planes[3]) { uint8_t c; for (i = slice_line_start_half; i < slice_line_end_half; i++) { uint8_t *a = desc->image->planes[3] + (desc->image->strides[3] * i); uint8_t *b = desc->image->planes[3] + (desc->image->strides[3] * (desc->image->height - i - 1)); while (a < b) { c = *a; *a++ = *b; *b-- = c; } } } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Pop the mirror filter from the stack mlt_filter filter = mlt_frame_pop_service(frame); // Get the image *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); // If we have an image of the right colour space if (error == 0 && *format == mlt_image_yuv422) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); slice_desc desc; struct mlt_image_s img; mlt_image_set_values(&img, *image, *format, *width, *height); if (mlt_frame_get_alpha(frame)) { img.planes[3] = mlt_frame_get_alpha(frame); img.strides[3] = img.width; } desc.image = &img; desc.mirror = mlt_properties_get(properties, "mirror"); desc.reverse = mlt_properties_get_int(properties, "reverse"); mlt_slices_run_normal(0, do_slice_proc, &desc); } // Return the error return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the service on to the stack mlt_frame_push_service(frame, filter); // Push the filter method on to the stack mlt_frame_push_service(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_mirror_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Construct a new filter mlt_filter filter = mlt_filter_new(); // If we have a filter, initialise it if (filter != NULL) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES(filter); // Set the default mirror type mlt_properties_set_or_default(properties, "mirror", arg, "horizontal"); // Assign the process method filter->process = filter_process; } // Return the filter return filter; } mlt-7.38.0/src/modules/core/filter_mirror.yml000664 000000 000000 00000001202 15172202314 021146 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: mirror title: Mirror version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Provides various mirror and image reversing effects. parameters: - identifier: mirror argument: yes title: Mirror Type type: string description: Choose the type of mirror operation. values: - horizontal - vertical - diagonal - xdiagonal - flip - flop - identifier: reverse title: Reverse type: integer mutable: no default: 0 minimum: 0 maximum: 1 widget: checkbox mlt-7.38.0/src/modules/core/filter_mono.c000664 000000 000000 00000015242 15172202314 020236 0ustar00rootroot000000 000000 /* * filter_mono.c -- mix all channels to a mono signal across n channels * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /** Get the audio. */ static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); mlt_properties properties = mlt_filter_properties(filter); int channels_out = mlt_properties_get_int(properties, "channels"); int input_chmask = mlt_properties_get_int64(properties, "input_chmask"); int output_chmask = mlt_properties_get_int64(properties, "output_chmask"); int i, j, size; // Get the producer's audio mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (channels_out == -1 || channels_out > *channels) channels_out = *channels; size = mlt_audio_format_size(*format, *samples, channels_out); switch (*format) { case mlt_audio_u8: { uint8_t *new_buffer = mlt_pool_alloc(size); memcpy(new_buffer, *buffer, size); for (i = 0; i < *samples; i++) { uint8_t mixdown = 0; for (j = 0; j < *channels; j++) { if (input_chmask & (1 << j)) mixdown += ((uint8_t *) *buffer)[(i * *channels) + j]; } for (j = 0; j < channels_out; j++) { if (output_chmask & (1 << j)) new_buffer[(i * channels_out) + j] = mixdown; } } *buffer = new_buffer; break; } case mlt_audio_s16: { int16_t *new_buffer = mlt_pool_alloc(size); memcpy(new_buffer, *buffer, size); for (i = 0; i < *samples; i++) { int16_t mixdown = 0; for (j = 0; j < *channels; j++) { if (input_chmask & (1 << j)) mixdown += ((int16_t *) *buffer)[(i * *channels) + j]; } for (j = 0; j < channels_out; j++) { if (output_chmask & (1 << j)) new_buffer[(i * channels_out) + j] = mixdown; } } *buffer = new_buffer; break; } case mlt_audio_s32le: { int32_t *new_buffer = mlt_pool_alloc(size); memcpy(new_buffer, *buffer, size); for (i = 0; i < *samples; i++) { int32_t mixdown = 0; for (j = 0; j < *channels; j++) { if (input_chmask & (1 << j)) mixdown += ((int32_t *) *buffer)[(i * *channels) + j]; } for (j = 0; j < channels_out; j++) { if (output_chmask & (1 << j)) new_buffer[(i * channels_out) + j] = mixdown; } } *buffer = new_buffer; break; } case mlt_audio_f32le: { float *new_buffer = mlt_pool_alloc(size); memcpy(new_buffer, *buffer, size); for (i = 0; i < *samples; i++) { float mixdown = 0; for (j = 0; j < *channels; j++) { if (input_chmask & (1 << j)) mixdown += ((float *) *buffer)[(i * *channels) + j]; } for (j = 0; j < channels_out; j++) { if (output_chmask & (1 << j)) new_buffer[(i * channels_out) + j] = mixdown; } } *buffer = new_buffer; break; } case mlt_audio_s32: { int32_t *new_buffer = mlt_pool_alloc(size); memcpy(new_buffer, *buffer, size); for (i = 0; i < *samples; i++) { int32_t mixdown = 0; for (j = 0; j < *channels; j++) { if (input_chmask & (1 << j)) mixdown += ((int32_t *) *buffer)[(j * *samples) + i]; } for (j = 0; j < channels_out; j++) { if (output_chmask & (1 << j)) new_buffer[(j * *samples) + i] = mixdown; } } *buffer = new_buffer; break; } case mlt_audio_float: { float *new_buffer = mlt_pool_alloc(size); memcpy(new_buffer, *buffer, size); for (i = 0; i < *samples; i++) { float mixdown = 0; for (j = 0; j < *channels; j++) { if (input_chmask & (1 << j)) mixdown += ((float *) *buffer)[(j * *samples) + i]; } for (j = 0; j < channels_out; j++) { if (output_chmask & (1 << j)) new_buffer[(j * *samples) + i] = mixdown; } } *buffer = new_buffer; break; } default: mlt_log_error(NULL, "[filter mono] Invalid audio format\n"); break; } if (size > *samples * channels_out) { mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); *channels = channels_out; } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Override the get_audio method mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Constructor for the filter. */ mlt_filter filter_mono_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; if (arg != NULL) mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "channels", atoi(arg)); else mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "channels", -1); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "input_chmask", 7); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "output_chmask", -1); } return filter; } mlt-7.38.0/src/modules/core/filter_mono.yml000664 000000 000000 00000002117 15172202314 020612 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: mono title: Mixdown version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: > Mix all channels of audio into a mono signal and output it as N channels. parameters: - identifier: channels argument: yes title: channels type: integer description: > Set the number of output channels. The default is automatic based on consumer request. required: no minimum: 1 - identifier: input_chmask title: Input Channel Mask type: integer description: > Which input channels to mix together as a bitmask. The default is for the front left and right and center channel, if any. required: no minimum: 0 default: 7 mutable: yes - identifier: output_chmask title: Output Channel Mask type: integer description: > Which output channels to overwrite with the mixed audio as a bitmask. The default is to overwrite all of the channels. required: no minimum: 0 default: 0xFFFFFFFF mutable: yes mlt-7.38.0/src/modules/core/filter_obscure.c000664 000000 000000 00000020267 15172202314 020733 0ustar00rootroot000000 000000 /* * filter_obscure.c -- obscure filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /** Geometry struct. */ struct geometry_s { int nw; int nh; float x; float y; float w; float h; int mask_w; int mask_h; }; /** Parse a value from a geometry string. */ static inline float parse_value(char **ptr, int normalization, char delim, float defaults) { float value = defaults; if (*ptr != NULL && **ptr != '\0') { char *end = NULL; value = strtod(*ptr, &end); if (end != NULL) { if (*end == '%') value = (value / 100.0) * normalization; while (*end == delim || *end == '%' || (delim == ',' && *end == '/')) end++; } *ptr = end; } return value; } /** Parse a geometry property string. */ static void geometry_parse( struct geometry_s *geometry, struct geometry_s *defaults, char *property, int nw, int nh) { // Assign normalized width and height geometry->nw = nw; geometry->nh = nh; // Assign from defaults if available if (defaults != NULL) { geometry->x = defaults->x; geometry->y = defaults->y; geometry->w = defaults->w; geometry->h = defaults->h; geometry->mask_w = defaults->mask_w; geometry->mask_h = defaults->mask_h; } else { geometry->x = 0; geometry->y = 0; geometry->w = nw; geometry->h = nh; geometry->mask_w = 20; geometry->mask_h = 20; } // Parse the geometry string if (property != NULL) { char *ptr = property; geometry->x = parse_value(&ptr, nw, ',', geometry->x); geometry->y = parse_value(&ptr, nh, ':', geometry->y); geometry->w = parse_value(&ptr, nw, 'x', geometry->w); geometry->h = parse_value(&ptr, nh, ':', geometry->h); geometry->mask_w = parse_value(&ptr, nw, 'x', geometry->mask_w); geometry->mask_h = parse_value(&ptr, nh, ' ', geometry->mask_h); } } /** A Timism but not as clean ;-). */ static float lerp(float value, float lower, float upper) { if (value < lower) return lower; else if (upper > lower && value > upper) return upper; return value; } /** Calculate real geometry. */ static void geometry_calculate(struct geometry_s *output, struct geometry_s *in, struct geometry_s *out, float position, int ow, int oh) { // Calculate this frames geometry output->x = lerp((in->x + (out->x - in->x) * position) / (float) out->nw * ow, 0, ow); output->y = lerp((in->y + (out->y - in->y) * position) / (float) out->nh * oh, 0, oh); output->w = lerp((in->w + (out->w - in->w) * position) / (float) out->nw * ow, 0, ow - output->x); output->h = lerp((in->h + (out->h - in->h) * position) / (float) out->nh * oh, 0, oh - output->y); output->mask_w = lerp(in->mask_w + (out->mask_w - in->mask_w) * position, 1, -1); output->mask_h = lerp(in->mask_h + (out->mask_h - in->mask_h) * position, 1, -1); } /** The averaging function... */ static inline void obscure_average(uint8_t *start, int width, int height, int stride) { register int y; register int x; register int Y = (*start + *(start + 2)) / 2; register int U = *(start + 1); register int V = *(start + 3); register uint8_t *p; register int components = width >> 1; y = height; while (y--) { p = start; x = components; while (x--) { Y = (Y + *p++) >> 1; U = (U + *p++) >> 1; Y = (Y + *p++) >> 1; V = (V + *p++) >> 1; } start += stride; } start -= height * stride; y = height; while (y--) { p = start; x = components; while (x--) { *p++ = Y; *p++ = U; *p++ = Y; *p++ = V; } start += stride; } } /** The obscurer rendering function... */ static void obscure_render(uint8_t *image, int width, int height, struct geometry_s result) { int area_x = result.x; int area_y = result.y; int area_w = result.w; int area_h = result.h; int mw = result.mask_w; int mh = result.mask_h; int w; int h; int aw; int ah; uint8_t *p = image + area_y * width * 2 + area_x * 2; for (w = 0; w < area_w; w += mw) { for (h = 0; h < area_h; h += mh) { aw = w + mw > area_w ? mw - (w + mw - area_w) : mw; ah = h + mh > area_h ? mh - (h + mh - area_h) : mh; if (aw > 1 && ah > 1) obscure_average(p + h * (width << 1) + (w << 1), aw, ah, width << 1); } } } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Pop the top of stack now mlt_filter filter = mlt_frame_pop_service(frame); // Get the image from the frame *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Get the image from the frame if (error == 0) { if (filter != NULL) { // Get the filter properties mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Structures for geometry struct geometry_s result; struct geometry_s start; struct geometry_s end; // Retrieve the position float position = mlt_filter_get_progress(filter, frame); // Now parse the geometries geometry_parse(&start, NULL, mlt_properties_get(properties, "start"), profile->width, profile->height); geometry_parse(&end, &start, mlt_properties_get(properties, "end"), profile->width, profile->height); // Do the calculation geometry_calculate(&result, &start, &end, position, *width, *height); // Now actually render it obscure_render(*image, *width, *height, result); } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push this on to the service stack mlt_frame_push_service(frame, filter); // Push the get image call mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_obscure_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); filter->process = filter_process; mlt_properties_set(properties, "start", arg != NULL ? arg : "0%/0%:100%x100%"); mlt_properties_set(properties, "end", ""); } return filter; } mlt-7.38.0/src/modules/core/filter_obscure.yml000664 000000 000000 00000001232 15172202314 021301 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: obscure title: Obscure (*DEPRECATED*) version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Obscuring filter. parameters: - identifier: start argument: yes title: Start type: string description: > The starting rectangle is given in the format X/Y:WxH[:PWxPY] where PWxPY is the size of the averaging region in pixels. - identifier: end title: End type: string description: > The ending rectangle is given in the format X/Y:WxH[:PWxPY] where PWxPY is the size of the averaging region in pixels. mlt-7.38.0/src/modules/core/filter_panner.c000664 000000 000000 00000027620 15172202314 020554 0ustar00rootroot000000 000000 /* * filter_panner.c --pan/balance audio channels * Copyright (C) 2010-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /** Get the audio. */ static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_properties properties = mlt_frame_pop_audio(frame); mlt_filter filter = mlt_frame_pop_audio(frame); mlt_properties filter_props = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_props = MLT_FRAME_PROPERTIES(frame); // We can only mix interleaved 32-bit float. *format = mlt_audio_f32le; mlt_frame_get_audio(frame, (void **) buffer, format, frequency, channels, samples); // Apply silence int silent = mlt_properties_get_int(frame_props, "silent_audio"); mlt_properties_set_int(frame_props, "silent_audio", 0); if (silent) memset(*buffer, 0, *samples * *channels * sizeof(float)); int src_size = 0; float *src = mlt_properties_get_data(filter_props, "scratch_buffer", &src_size); float *dest = *buffer; double v; // sample accumulator int i, out, in; double factors[6][6]; // mixing weights [in][out] double mix_start = 0.5, mix_end = 0.5; if (mlt_properties_get(properties, "previous_mix") != NULL) mix_start = mlt_properties_get_double(properties, "previous_mix"); if (mlt_properties_get(properties, "mix") != NULL) mix_end = mlt_properties_get_double(properties, "mix"); double weight = mix_start; double weight_step = (mix_end - mix_start) / *samples; int active_channel = mlt_properties_get_int(properties, "channel"); int gang = mlt_properties_get_int(properties, "gang") ? 2 : 1; // Setup or resize a scratch buffer if (!src || src_size < *samples * *channels * sizeof(*src)) { // We allocate 4 more samples than we need to deal with jitter in the sample count per frame. src_size = (*samples + 4) * *channels * sizeof(*src); src = mlt_pool_alloc(src_size); if (!src) return 0; mlt_properties_set_data(filter_props, "scratch_buffer", src, src_size, mlt_pool_release, NULL); } // We must use a pristine copy as the source memcpy(src, *buffer, *samples * *channels * sizeof(*src)); // Initialize the mix factors for (i = 0; i < 6; i++) for (out = 0; out < 6; out++) factors[i][out] = (out == i) ? 1.0 : 0.0; for (i = 0; i < *samples; i++) { // Recompute the mix factors switch (active_channel) { case -1: // Front L/R balance case -2: // Rear L/R balance { // Gang front/rear balance if requested int active = active_channel; for (int g = 0; g < gang; g++, active--) { int left = active == -1 ? 0 : (*channels == 6 ? 4 : 2); int right = left + 1; if (weight < 0.0) { factors[right][right] = MAX(weight + 1.0, 0.0); } else { factors[left][left] = MAX(1.0 - weight, 0.0); } } break; } case -3: // Left fade case -4: // right fade { // Gang left/right fade if requested int active = active_channel; for (int g = 0; g < gang; g++, active--) { int front = active == -3 ? 0 : 1; int rear = front + (*channels == 6 ? 4 : 2); if (weight < 0.0) { factors[rear][rear] = MAX(weight + 1.0, 0.0); } else { factors[front][front] = MAX(1.0 - weight, 0.0); } } break; } case 0: // left case 2: { int left = active_channel; int right = left + 1; factors[right][right] = 1.0; if (weight < 0.0) // output left toward left { factors[left][left] = 0.5 - weight * 0.5; factors[left][right] = (1.0 + weight) * 0.5; } else // output left toward right { factors[left][left] = (1.0 - weight) * 0.5; factors[left][right] = 0.5 + weight * 0.5; } break; } case 1: // right case 3: { int right = active_channel; int left = right - 1; factors[left][left] = 1.0; if (weight < 0.0) // output right toward left { factors[right][left] = 0.5 - weight * 0.5; factors[right][right] = (1.0 + weight) * 0.5; } else // output right toward right { factors[right][left] = (1.0 - weight) * 0.5; factors[right][right] = 0.5 + weight * 0.5; } break; } } // Do the mixing for (out = 0; out < *channels && out < 6; out++) { v = 0.0; for (in = 0; in < *channels && in < 6; in++) v += factors[in][out] * src[i * *channels + in]; dest[i * *channels + out] = v; } weight += weight_step; } return 0; } /** Pan filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_props = MLT_FRAME_PROPERTIES(frame); mlt_properties instance_props = mlt_properties_new(); // Only if mix is specified, otherwise a producer may set the mix if (mlt_properties_get(properties, "start") != NULL) { // Determine the time position of this frame in the filter duration mlt_properties props = mlt_properties_get_data(frame_props, "_producer", NULL); int always_active = mlt_properties_get_int(properties, "always_active"); mlt_position in = !always_active ? mlt_filter_get_in(filter) : mlt_properties_get_int(props, "in"); mlt_position out = !always_active ? mlt_filter_get_out(filter) : mlt_properties_get_int(props, "out"); int length = mlt_properties_get_int(properties, "length"); mlt_position time = !always_active ? mlt_frame_get_position(frame) : mlt_properties_get_int(props, "_frame"); double mix = (double) (time - in) / (double) (out - in + 1); if (length == 0) { // If there is an end mix level adjust mix to the range if (mlt_properties_get(properties, "end") != NULL) { double start = mlt_properties_get_double(properties, "start"); double end = mlt_properties_get_double(properties, "end"); mix = start + (end - start) * mix; } // Use constant mix level if only start else if (mlt_properties_get(properties, "start") != NULL) { mix = mlt_properties_get_double(properties, "start"); } // Use animated property "split" to get mix level if property is set char *split_property = mlt_properties_get(properties, "split"); if (split_property) { mlt_position pos = mlt_filter_get_position(filter, frame); mlt_position len = mlt_filter_get_length2(filter, frame); mix = mlt_properties_anim_get_double(properties, "split", pos, len); } // Convert it from [0, 1] to [-1, 1] mix = mix * 2.0 - 1.0; // Finally, set the mix property on the frame mlt_properties_set_double(instance_props, "mix", mix); // Initialise filter previous mix value to prevent an inadvertent jump from 0 mlt_position last_position = mlt_properties_get_position(properties, "_last_position"); mlt_position current_position = mlt_frame_get_position(frame); mlt_properties_set_position(properties, "_last_position", current_position); if (mlt_properties_get(properties, "_previous_mix") == NULL || current_position != last_position + 1) mlt_properties_set_double(properties, "_previous_mix", mix); // Tell the frame what the previous mix level was mlt_properties_set_double(instance_props, "previous_mix", mlt_properties_get_double(properties, "_previous_mix")); // Save the current mix level for the next iteration mlt_properties_set_double(properties, "_previous_mix", mix); } else { double level = mlt_properties_get_double(properties, "start"); double mix_start = level; double mix_end = mix_start; double mix_increment = 1.0 / length; if (time - in < length) { mix_start *= (double) (time - in) / length; mix_end = mix_start + mix_increment; } else if (time > out - length) { mix_end = mix_start * ((double) (out - time - in) / length); mix_start = mix_end - mix_increment; } mix_start = mix_start < 0 ? 0 : mix_start > level ? level : mix_start; mix_end = mix_end < 0 ? 0 : mix_end > level ? level : mix_end; mlt_properties_set_double(instance_props, "previous_mix", mix_start); mlt_properties_set_double(instance_props, "mix", mix_end); } mlt_properties_set_int(instance_props, "channel", mlt_properties_get_int(properties, "channel")); mlt_properties_set_int(instance_props, "gang", mlt_properties_get_int(properties, "gang")); } char label[64]; snprintf(label, sizeof(label), "panner %s", mlt_properties_get(properties, "_unique_id")); mlt_properties_set_data(frame_props, label, instance_props, 0, (mlt_destructor) mlt_properties_close, NULL); // Override the get_audio method mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, instance_props); mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Constructor for the filter. */ mlt_filter filter_panner_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = calloc(1, sizeof(struct mlt_filter_s)); if (filter != NULL && mlt_filter_init(filter, NULL) == 0) { filter->process = filter_process; if (arg != NULL) mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "start", atof(arg)); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "channel", -1); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "split", NULL); } return filter; } mlt-7.38.0/src/modules/core/filter_panner.yml000664 000000 000000 00000004140 15172202314 021123 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: panner title: Audio Pan version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: Pan an audio channel, adjust balance, or adjust fade. notes: Only handles up to 6 channels. Needs more work balance for surround and other channel layouts. parameters: - identifier: start (*deprecated*) title: Start description: > The position of the audio relative to its neighbor channel. For example, when channel is set to 0 for left, then start 0 is full left, 0.5 is center, and 1.0 is full right. If value for property "split" is set value of this property is discarded. type: float argument: yes mutable: yes minimum: 0 maximum: 1 - identifier: end (*deprecated*) title: End description: > The ending value of the audio position. It will be interpolated from start to end over the in-out range. If value for property "split" is set value of this property is discarded. type: float mutable: yes minimum: 0 maximum: 1 - identifier: channel title: Channel type: integer description: > For stereo: 0 for front left, 1 for front right, -1 for front balance. For quad: 2 for back left, 3 for back right, -2 for rear balance, -3 for left fade, -4 for right fade. minimum: -4 maximum: 5 default: -1 - identifier: gang title: Gang description: > Whether to gang together the front and back when using balance or to gang together the left and right when using fade. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: split title: Split description: > The animated position of the audio relative to its neighbor channel. For example, when channel is set to 0 for left, then 0 is full left, 0.5 is center, and 1.0 is full right. If this value is set, values for properties "start" and "end" are discarded. type: float mutable: yes animation: yes minimum: 0 maximum: 1 mlt-7.38.0/src/modules/core/filter_pillar_echo.c000664 000000 000000 00000033753 15172202314 021556 0ustar00rootroot000000 000000 /* * filter_pillar_echo.c -- filter to interpolate pixels outside an area of interest * Copyright (c) 2020-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "image_proc.h" #include #include #include #include /** Constrain a rect to be within the max dimensions */ static mlt_rect constrain_rect(mlt_rect rect, int max_x, int max_y) { if (rect.x < 0) { rect.w = rect.w + rect.x; rect.x = 0; } if (rect.y < 0) { rect.h = rect.h + rect.y; rect.y = 0; } if (rect.x + rect.w < 0) { rect.w = 0; } if (rect.y + rect.h < 0) { rect.h = 0; } if (rect.x + rect.w > max_x) { rect.w = max_x - rect.x; } if (rect.y + rect.h > max_y) { rect.h = max_y - rect.y; } return rect; } typedef struct { mlt_image src; mlt_image dst; mlt_rect rect; } scale_sliced_desc; static int scale_sliced_proc_rgba32(int id, int index, int jobs, void *data) { (void) id; // unused scale_sliced_desc *desc = ((scale_sliced_desc *) data); mlt_image src = desc->src; mlt_image dst = desc->dst; mlt_rect rect = desc->rect; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, src->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; double srcScale = rect.h / (double) src->height; int linesize = src->width * 4; uint8_t *d = dst->planes[0] + (slice_line_start * linesize); for (int y = slice_line_start; y < slice_line_end; y++) { double srcY = rect.y + (double) y * srcScale; int srcYindex = floor(srcY); double fbottom = srcY - srcYindex; double ftop = 1.0 - fbottom; int x = 0; for (x = 0; x < src->width; x++) { double srcX = rect.x + (double) x * srcScale; int srcXindex = floor(srcX); double fright = srcX - srcXindex; double fleft = 1.0 - fright; double valueSum[] = {0.0, 0.0, 0.0, 0.0}; double factorSum[] = {0.0, 0.0, 0.0, 0.0}; uint8_t *s = src->planes[0] + (srcYindex * linesize) + (srcXindex * 4); // Top Left double ftl = ftop * fleft; valueSum[0] += s[0] * ftl; factorSum[0] += ftl; valueSum[1] += s[1] * ftl; factorSum[1] += ftl; valueSum[2] += s[2] * ftl; factorSum[2] += ftl; valueSum[3] += s[3] * ftl; factorSum[3] += ftl; // Top Right if (x < src->width - 1) { double ftr = ftop * fright; valueSum[0] += s[4] * ftr; factorSum[0] += ftr; valueSum[1] += s[5] * ftr; factorSum[1] += ftr; valueSum[2] += s[6] * ftr; factorSum[2] += ftr; valueSum[3] += s[7] * ftr; factorSum[3] += ftr; } if (y < src->height - 1) { uint8_t *sb = s + linesize; // Bottom Left double fbl = fbottom * fleft; valueSum[0] += sb[0] * fbl; factorSum[0] += fbl; valueSum[1] += sb[1] * fbl; factorSum[1] += fbl; valueSum[2] += sb[2] * fbl; factorSum[2] += fbl; valueSum[3] += sb[3] * fbl; factorSum[3] += fbl; // Bottom Right if (x < src->width - 1) { double fbr = fbottom * fright; valueSum[0] += sb[4] * fbr; factorSum[0] += fbr; valueSum[1] += sb[5] * fbr; factorSum[1] += fbr; valueSum[2] += sb[6] * fbr; factorSum[2] += fbr; valueSum[3] += sb[7] * fbr; factorSum[3] += fbr; } } d[0] = (uint8_t) round(valueSum[0] / factorSum[0]); d[1] = (uint8_t) round(valueSum[1] / factorSum[1]); d[2] = (uint8_t) round(valueSum[2] / factorSum[2]); d[3] = (uint8_t) round(valueSum[3] / factorSum[3]); d += 4; } } return 0; } static int scale_sliced_proc_rgba64(int id, int index, int jobs, void *data) { (void) id; // unused scale_sliced_desc *desc = ((scale_sliced_desc *) data); mlt_image src = desc->src; mlt_image dst = desc->dst; mlt_rect rect = desc->rect; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, src->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; double srcScale = rect.h / (double) src->height; int linesize = src->width * 4; uint16_t *d = (uint16_t *) dst->planes[0] + (slice_line_start * linesize); for (int y = slice_line_start; y < slice_line_end; y++) { double srcY = rect.y + (double) y * srcScale; int srcYindex = floor(srcY); double fbottom = srcY - srcYindex; double ftop = 1.0 - fbottom; int x = 0; for (x = 0; x < src->width; x++) { double srcX = rect.x + (double) x * srcScale; int srcXindex = floor(srcX); double fright = srcX - srcXindex; double fleft = 1.0 - fright; double valueSum[] = {0.0, 0.0, 0.0, 0.0}; double factorSum[] = {0.0, 0.0, 0.0, 0.0}; uint16_t *s = (uint16_t *) src->planes[0] + (srcYindex * linesize) + (srcXindex * 4); // Top Left double ftl = ftop * fleft; valueSum[0] += s[0] * ftl; factorSum[0] += ftl; valueSum[1] += s[1] * ftl; factorSum[1] += ftl; valueSum[2] += s[2] * ftl; factorSum[2] += ftl; valueSum[3] += s[3] * ftl; factorSum[3] += ftl; // Top Right if (x < src->width - 1) { double ftr = ftop * fright; valueSum[0] += s[4] * ftr; factorSum[0] += ftr; valueSum[1] += s[5] * ftr; factorSum[1] += ftr; valueSum[2] += s[6] * ftr; factorSum[2] += ftr; valueSum[3] += s[7] * ftr; factorSum[3] += ftr; } if (y < src->height - 1) { uint16_t *sb = s + linesize; // Bottom Left double fbl = fbottom * fleft; valueSum[0] += sb[0] * fbl; factorSum[0] += fbl; valueSum[1] += sb[1] * fbl; factorSum[1] += fbl; valueSum[2] += sb[2] * fbl; factorSum[2] += fbl; valueSum[3] += sb[3] * fbl; factorSum[3] += fbl; // Bottom Right if (x < src->width - 1) { double fbr = fbottom * fright; valueSum[0] += sb[4] * fbr; factorSum[0] += fbr; valueSum[1] += sb[5] * fbr; factorSum[1] += fbr; valueSum[2] += sb[6] * fbr; factorSum[2] += fbr; valueSum[3] += sb[7] * fbr; factorSum[3] += fbr; } } d[0] = (uint16_t) round(valueSum[0] / factorSum[0]); d[1] = (uint16_t) round(valueSum[1] / factorSum[1]); d[2] = (uint16_t) round(valueSum[2] / factorSum[2]); d[3] = (uint16_t) round(valueSum[3] / factorSum[3]); d += 4; } } return 0; } /** Perform a bilinear scale from the rect inside the source to fill the destination * * \param src a pointer to the source image * \param dst a pointer to the destination image * \param rect the area of interest in the src to be scaled to fit the dst */ static void bilinear_scale_rgba(mlt_image src, mlt_image dst, mlt_rect rect) { scale_sliced_desc desc; desc.src = src; desc.dst = dst; desc.rect = rect; // Crop out a section of the rect that has the same aspect ratio as the image double destAr = (double) src->width / (double) src->height; double sourceAr = rect.w / rect.h; if (sourceAr > destAr) { // Crop sides to fit height desc.rect.w = rect.w * destAr / sourceAr; desc.rect.x = rect.x + (rect.w - desc.rect.w) / 2.0; } else if (destAr > sourceAr) { // Crop top and bottom to fit width. desc.rect.h = rect.h * sourceAr / destAr; desc.rect.y = rect.y + (rect.h - desc.rect.h) / 2.0; } if (src->format == mlt_image_rgba) { mlt_slices_run_normal(0, scale_sliced_proc_rgba32, &desc); } else { mlt_slices_run_normal(0, scale_sliced_proc_rgba64, &desc); } } /** Copy pixels from source to destination * * \param src a pointer to the source image * \param dst a pointer to the destination image * \param width the number of values in each row * \param rect the area of interest in the src to be copied to the dst */ static void blit_rect(mlt_image src, mlt_image dst, mlt_rect rect) { int blitHeight = rect.h; int blitWidth = rect.w * 4; int linesize = src->width * 4; if (src->format == mlt_image_rgba) { uint8_t *s = (uint8_t *) src->planes[0] + (int) rect.y * linesize + (int) rect.x * 4; uint8_t *d = (uint8_t *) dst->planes[0] + (int) rect.y * linesize + (int) rect.x * 4; while (blitHeight--) { memcpy(d, s, blitWidth); s += linesize; d += linesize; } } else { uint16_t *s = (uint16_t *) src->planes[0] + (int) rect.y * linesize + (int) rect.x * 4; uint16_t *d = (uint16_t *) dst->planes[0] + (int) rect.y * linesize + (int) rect.x * 4; while (blitHeight--) { memcpy(d, s, blitWidth * 2); s += linesize; d += linesize; } } } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); char *rect_str = mlt_properties_get(filter_properties, "rect"); if (!rect_str) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "rect property not set\n"); return mlt_frame_get_image(frame, image, format, width, height, writable); } mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_rect rect = mlt_properties_anim_get_rect(filter_properties, "rect", position, length); if (strchr(rect_str, '%')) { rect.x *= profile->width; rect.w *= profile->width; rect.y *= profile->height; rect.h *= profile->height; } double scale = mlt_profile_scale_width(profile, *width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); rect.y *= scale; rect.h *= scale; rect = constrain_rect(rect, profile->width * scale, profile->height * scale); if (rect.w < 1 || rect.h < 1) { mlt_log_info(MLT_FILTER_SERVICE(filter), "rect invalid\n"); return mlt_frame_get_image(frame, image, format, width, height, writable); } if (*format != mlt_image_rgba64) { *format = mlt_image_rgba; } error = mlt_frame_get_image(frame, image, format, width, height, 0); if (error) return error; if (rect.x <= 0 && rect.y <= 0 && rect.w >= *width && rect.h >= *height) { // Rect fills the image. Nothing to blur return error; } double blur = mlt_properties_anim_get_double(filter_properties, "blur", position, length); // Convert from percent to pixels. blur = blur * (double) profile->width * mlt_profile_scale_width(profile, *width) / 100.0; blur = MAX(round(blur), 0); struct mlt_image_s src; mlt_image_set_values(&src, *image, *format, *width, *height); struct mlt_image_s dst; mlt_image_set_values(&dst, NULL, *format, *width, *height); mlt_image_alloc_data(&dst); bilinear_scale_rgba(&src, &dst, rect); if (blur != 0) { mlt_image_box_blur(&dst, blur, blur, 0); } blit_rect(&src, &dst, rect); *image = dst.data; mlt_frame_set_image(frame, dst.data, 0, dst.release_data); return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_pillar_echo_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set(properties, "rect", "0% 0% 10% 10%"); mlt_properties_set_double(properties, "blur", 4.0); filter->process = filter_process; } else { mlt_log_error(NULL, "Filter pillar_echo initialization failed\n"); } return filter; } mlt-7.38.0/src/modules/core/filter_pillar_echo.yml000664 000000 000000 00000001602 15172202314 022121 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: pillar_echo title: Pillar Echo version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > Create an echo effect (blur) outside of an area of interest. The area of interest is scaled to fill the image, then blurred. Then the original image is replaced back on top. parameters: - identifier: rect title: Rectangle description: > Defines the rectangle of the area of interest. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 10% 10%" readonly: no mutable: yes animation: yes - identifier: blur title: Blur description: > The blur radius as a percent of the image width type: float default: 4.0 readonly: no mutable: yes animation: yes mlt-7.38.0/src/modules/core/filter_rescale.c000664 000000 000000 00000024646 15172202314 020714 0ustar00rootroot000000 000000 /* * filter_rescale.c -- scale the producer video frame size to match the consumer * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include /** virtual function declaration for an image scaler * * image scaler implementations are expected to support the following in and out formats: * yuv422 -> yuv422 * rgb -> rgb * rgba -> rgba * rgb -> yuv422 * rgba -> yuv422 */ typedef int (*image_scaler)(mlt_frame frame, uint8_t **image, mlt_image_format *format, int iwidth, int iheight, int owidth, int oheight); static int filter_scale(mlt_frame frame, uint8_t **image, mlt_image_format *format, int iwidth, int iheight, int owidth, int oheight) { // Create the output image uint8_t *output = mlt_pool_alloc(owidth * (oheight + 1) * 2); // Calculate strides int istride = iwidth * 2; int ostride = owidth * 2; iwidth = iwidth - (iwidth % 4); // Derived coordinates int dy, dx; // Calculate ranges int out_x_range = owidth / 2; int out_y_range = oheight / 2; int in_x_range = iwidth / 2; int in_y_range = iheight / 2; // Output pointers register uint8_t *out_line = output; register uint8_t *out_ptr; // Calculate a middle pointer uint8_t *in_middle = *image + istride * in_y_range + in_x_range * 2; uint8_t *in_line; // Generate the affine transform scaling values register int scale_width = (iwidth << 16) / owidth; register int scale_height = (iheight << 16) / oheight; register int base = 0; int outer = out_x_range * scale_width; int bottom = out_y_range * scale_height; // Loop for the entirety of our output height. for (dy = -bottom; dy < bottom; dy += scale_height) { // Start at the beginning of the line out_ptr = out_line; // Pointer to the middle of the input line in_line = in_middle + (dy >> 16) * istride; // Loop for the entirety of our output row. for (dx = -outer; dx < outer; dx += scale_width) { base = dx >> 15; base &= 0xfffffffe; *out_ptr++ = *(in_line + base); base &= 0xfffffffc; *out_ptr++ = *(in_line + base + 1); dx += scale_width; base = dx >> 15; base &= 0xfffffffe; *out_ptr++ = *(in_line + base); base &= 0xfffffffc; *out_ptr++ = *(in_line + base + 3); } // Move to next output line out_line += ostride; } // Now update the frame mlt_frame_set_image(frame, output, owidth * (oheight + 1) * 2, mlt_pool_release); *image = output; return 0; } static void scale_alpha(mlt_frame frame, int iwidth, int iheight, int owidth, int oheight) { // Scale the alpha uint8_t *output = NULL; uint8_t *input = mlt_frame_get_alpha(frame); if (input != NULL) { uint8_t *out_line, *in_line; register int i, j, x, y; register int ox = (iwidth << 16) / owidth; register int oy = (iheight << 16) / oheight; output = mlt_pool_alloc(owidth * oheight); out_line = output; // Loop for the entirety of our output height. for (i = 0, y = (oy >> 1); i < oheight; i++, y += oy) { in_line = &input[(y >> 16) * iwidth]; for (j = 0, x = (ox >> 1); j < owidth; j++, x += ox) *out_line++ = in_line[x >> 16]; } // Set it back on the frame mlt_frame_set_alpha(frame, output, owidth * oheight, mlt_pool_release); } } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; // Get the frame properties mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Get the filter from the stack mlt_filter filter = mlt_frame_pop_service(frame); // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); // Get the image scaler method image_scaler scaler_method = mlt_properties_get_data(filter_properties, "method", NULL); // Correct Width/height if necessary if (*width == 0 || *height == 0) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); *width = profile->width; *height = profile->height; } // There can be problems with small images - avoid them (by hacking - gah) if (*width >= 6 && *height >= 6) { int iwidth = *width; int iheight = *height; int owidth = *width; int oheight = *height; char *interps = mlt_properties_get(properties, "consumer.rescale"); if (mlt_properties_get(filter_properties, "factor")) { double factor = mlt_properties_get_double(filter_properties, "factor"); owidth *= factor; oheight *= factor; } // Default from the scaler if not specified on the frame if (interps == NULL) { interps = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "interpolation"); mlt_properties_set(properties, "consumer.rescale", interps); } // If meta.media.width/height exist, we want that as minimum information if (mlt_properties_get_int(properties, "meta.media.width")) { iwidth = mlt_properties_get_int(properties, "meta.media.width"); iheight = mlt_properties_get_int(properties, "meta.media.height"); } // Let the producer know what we are actually requested to obtain if (strcmp(interps, "none")) { mlt_properties_set_int(properties, "rescale_width", *width); mlt_properties_set_int(properties, "rescale_height", *height); } else { // When no scaling is requested, revert the requested dimensions if possible mlt_properties_set_int(properties, "rescale_width", iwidth); mlt_properties_set_int(properties, "rescale_height", iheight); } // Deinterlace if height is changing to prevent fields mixing on interpolation // One exception: non-interpolated, integral scaling if (iheight != oheight && (strcmp(interps, "nearest") || (iheight % oheight != 0))) mlt_properties_set_int(properties, "consumer.progressive", 1); // Convert the image to yuv422 when using the local scaler if (scaler_method == filter_scale) *format = mlt_image_yuv422; // Get the image as requested mlt_frame_get_image(frame, image, format, &iwidth, &iheight, writable); // Get rescale interpretation again, in case the producer wishes to override scaling interps = mlt_properties_get(properties, "consumer.rescale"); if (*image && strcmp(interps, "none") && (iwidth != owidth || iheight != oheight)) { mlt_log_debug(MLT_FILTER_SERVICE(filter), "%dx%d -> %dx%d (%s) %s\n", iwidth, iheight, owidth, oheight, mlt_image_format_name(*format), interps); // If valid colorspace if (*format == mlt_image_yuv422 || *format == mlt_image_rgb || *format == mlt_image_rgba || *format == mlt_image_yuv420p || *format == mlt_image_yuv420p10 || *format == mlt_image_yuv444p10 || *format == mlt_image_rgba64) { // Call the virtual function scaler_method(frame, image, format, iwidth, iheight, owidth, oheight); *width = owidth; *height = oheight; } else { *width = iwidth; *height = iheight; } // Scale the alpha channel only if exists and not correct size int alpha_size = 0; mlt_frame_get_alpha_size(frame, &alpha_size); if (alpha_size > 0 && alpha_size != (owidth * oheight) && alpha_size != (owidth * (oheight + 1))) scale_alpha(frame, iwidth, iheight, owidth, oheight); } else { *width = iwidth; *height = iheight; } } else { error = 1; } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the filter mlt_frame_push_service(frame, filter); // Push the get image method mlt_frame_push_service(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_rescale_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new scaler mlt_filter filter = mlt_filter_new(); // If successful, then initialise it if (filter != NULL) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES(filter); // Set the process method filter->process = filter_process; // Set the inerpolation mlt_properties_set(properties, "interpolation", arg == NULL ? "bilinear" : arg); // Set the method mlt_properties_set_data(properties, "method", filter_scale, 0, NULL, NULL); } return filter; } mlt-7.38.0/src/modules/core/filter_rescale.yml000664 000000 000000 00000001655 15172202314 021266 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: rescale title: Rescale version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video - Hidden description: > Scale the producer video frame size to match the consumer. This filter is designed for use as a normalizer for the loader producer. notes: > If a property "consumer_aspect_ratio" exists on the frame, then rescaler normalizes the producer's aspect ratio and maximises the size of the frame, but may not produce the consumer's requested dimension. Therefore, this option works best in conjunction with the resize filter. This behavior can be disabled by another service by either removing the property, setting it to zero, or setting frame property "distort" to 1. bugs: - > It only implements a nearest neighbour scaling - it is used as the base class for the gtkrescale and swscale filters. mlt-7.38.0/src/modules/core/filter_resize.c000664 000000 000000 00000025421 15172202314 020567 0ustar00rootroot000000 000000 /* * filter_resize.c -- resizing filter * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include static uint8_t *resize_alpha( uint8_t *input, int owidth, int oheight, int iwidth, int iheight, uint8_t alpha_value) { uint8_t *output = NULL; if (input != NULL && (iwidth != owidth || iheight != oheight) && (owidth > 6 && oheight > 6)) { uint8_t *out_line; int offset_x = (owidth - iwidth) / 2; int offset_y = (oheight - iheight) / 2; int iused = iwidth; output = mlt_pool_alloc(owidth * oheight); memset(output, alpha_value, owidth * oheight); offset_x -= offset_x % 2; out_line = output + offset_y * owidth; out_line += offset_x; // Loop for the entirety of our output height. while (iheight--) { // We're in the input range for this row. memcpy(out_line, input, iused); // Move to next input line input += iwidth; // Move to next output line out_line += owidth; } } return output; } static void resize_image(uint8_t *output, int owidth, int oheight, uint8_t *input, int iwidth, int iheight, int bpp, mlt_image_format format, uint8_t alpha_value) { // Calculate strides int istride = iwidth * bpp; int ostride = owidth * bpp; int offset_x = (owidth - iwidth) / 2 * bpp; int offset_y = (oheight - iheight) / 2; uint8_t *in_line = input; uint8_t *out_line; int size = owidth * oheight; uint8_t *p = output; // Optimisation point if (output == NULL || input == NULL || (owidth <= 6 || oheight <= 6 || iwidth <= 6 || iheight <= 6)) { return; } else if (iwidth == owidth && iheight == oheight) { memcpy(output, input, iheight * istride); return; } if (format == mlt_image_rgba) { memset(p, 0, size * bpp); if (alpha_value != 0) { while (size--) { p[3] = alpha_value; p += 4; } } } else if (format == mlt_image_rgba64) { uint16_t *p16 = (uint16_t *) p; uint16_t alpha_value_16 = alpha_value << 8; memset(p16, 0, size * bpp); if (alpha_value != 0) { while (size--) { p16[3] = alpha_value_16; p16 += 4; } } } else if (bpp == 2) { memset(p, 16, size * bpp); while (size--) { p[1] = 128; p += 2; } offset_x -= offset_x % 4; } else { memset(p, 0, size * bpp); } out_line = output + offset_y * ostride; out_line += offset_x; // Loop for the entirety of our output height. while (iheight--) { // We're in the input range for this row. memcpy(out_line, in_line, istride); // Move to next input line in_line += istride; // Move to next output line out_line += ostride; } } /** A padding function for frames - this does not rescale, but simply resizes. */ static uint8_t *frame_resize_image(mlt_frame frame, int owidth, int oheight, mlt_image_format format) { // Get properties mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Get the input image, width and height uint8_t *input = mlt_properties_get_data(properties, "image", NULL); uint8_t *alpha = mlt_frame_get_alpha(frame); int alpha_size = 0; mlt_frame_get_alpha_size(frame, &alpha_size); int bpp = 0; mlt_image_format_size(format, owidth, oheight, &bpp); int iwidth = mlt_properties_get_int(properties, "width"); int iheight = mlt_properties_get_int(properties, "height"); // If width and height are correct, don't do anything if (iwidth < owidth || iheight < oheight) { mlt_log_debug(NULL, "[filter_resize] %dx%d -> %dx%d (%s)\n", iwidth, iheight, owidth, oheight, mlt_image_format_name(format)); uint8_t alpha_value = mlt_properties_get_int(properties, "resize_alpha"); // Create the output image uint8_t *output = mlt_pool_alloc(owidth * (oheight + 1) * bpp); // Call the generic resize resize_image(output, owidth, oheight, input, iwidth, iheight, bpp, format, alpha_value); // Now update the frame mlt_frame_set_image(frame, output, owidth * (oheight + 1) * bpp, mlt_pool_release); // We should resize the alpha too if (format != mlt_image_rgba && alpha && alpha_size >= iwidth * iheight) { alpha = resize_alpha(alpha, owidth, oheight, iwidth, iheight, alpha_value); if (alpha) mlt_frame_set_alpha(frame, alpha, owidth * oheight, mlt_pool_release); } // Return the output return output; } // No change, return input return input; } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; // Get the properties from the frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Pop the top of stack now mlt_filter filter = mlt_frame_pop_service(frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Retrieve the aspect ratio double aspect_ratio = mlt_deque_pop_back_double(MLT_FRAME_IMAGE_STACK(frame)); double consumer_aspect = mlt_profile_sar(mlt_service_profile(MLT_FILTER_SERVICE(filter))); // Correct Width/height if necessary if (*width == 0 || *height == 0) { *width = profile->width; *height = profile->height; } // Assign requested width/height from our subordinate int owidth = *width; int oheight = *height; // Check for the special case - no aspect ratio means no problem :-) if (aspect_ratio == 0.0) aspect_ratio = consumer_aspect; // Reset the aspect ratio mlt_properties_set_double(properties, "aspect_ratio", aspect_ratio); // Skip scaling if requested char *rescale = mlt_properties_get(properties, "consumer.rescale"); if (rescale != NULL && !strcmp(rescale, "none")) return mlt_frame_get_image(frame, image, format, width, height, writable); if (mlt_properties_get_int(properties, "distort") == 0 && profile) { // Normalize the input and out display aspect int normalized_width = profile->width; int normalized_height = profile->height; int real_width = mlt_properties_get_int(properties, "meta.media.width"); int real_height = mlt_properties_get_int(properties, "meta.media.height"); if (real_width == 0) real_width = mlt_properties_get_int(properties, "width"); if (real_height == 0) real_height = mlt_properties_get_int(properties, "height"); double input_ar = aspect_ratio * real_width / real_height; double output_ar = consumer_aspect * owidth / oheight; // fprintf( stderr, "real %dx%d normalized %dx%d output %dx%d sar %f in-dar %f out-dar %f\n", // real_width, real_height, normalized_width, normalized_height, owidth, oheight, aspect_ratio, input_ar, output_ar); // Optimised for the input_ar > output_ar case (e.g. widescreen on standard) int scaled_width = rint((input_ar * normalized_width) / output_ar); int scaled_height = normalized_height; // Now ensure that our images fit in the output frame if (scaled_width > normalized_width) { scaled_width = normalized_width; scaled_height = rint((output_ar * normalized_height) / input_ar); } // Now calculate the actual image size that we want owidth = rint(scaled_width * owidth / normalized_width); oheight = rint(scaled_height * oheight / normalized_height); // Tell frame we have conformed the aspect to the consumer mlt_frame_set_aspect_ratio(frame, consumer_aspect); } mlt_properties_set_int(properties, "distort", 0); // Now pass on the calculations down the line mlt_properties_set_int(properties, "resize_width", *width); mlt_properties_set_int(properties, "resize_height", *height); // If there will be padding, then we need packed image format. if (*format == mlt_image_yuv420p && (owidth < *width || oheight < *height)) { *format = mlt_image_yuv422; } // Now get the image if (*format == mlt_image_yuv422) { owidth -= owidth % 2; *width -= *width % 2; } error = mlt_frame_get_image(frame, image, format, &owidth, &oheight, writable); if (error == 0 && *image && *format != mlt_image_yuv420p) { *image = frame_resize_image(frame, *width, *height, *format); } else { *width = owidth; *height = oheight; } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Store the aspect ratio reported by the source mlt_deque_push_back_double(MLT_FRAME_IMAGE_STACK(frame), mlt_frame_get_aspect_ratio(frame)); // Push this on to the service stack mlt_frame_push_service(frame, filter); // Push the get_image method on to the stack mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_resize_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = calloc(1, sizeof(struct mlt_filter_s)); if (mlt_filter_init(filter, filter) == 0) { filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/core/filter_resize.yml000664 000000 000000 00000001065 15172202314 021144 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: resize title: Pad version: 2 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video - Hidden description: Pad an image with black to fulfill the requested image size. notes: > Normally resize is used to pad the producer's output to what the consumer has requested after an upstream rescale filter first scales the image to maximise usage of the image area. This filter is automatically invoked by the loader as part of image normalization. mlt-7.38.0/src/modules/core/filter_transition.c000664 000000 000000 00000012473 15172202314 021463 0ustar00rootroot000000 000000 /* * filter_transition.c -- Convert any transition into a filter * Copyright (C) 2005-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include /** Get the image via the transition. NB: Not all transitions will accept a and b frames being the same... */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_transition transition = mlt_frame_pop_service(frame); if (mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "image_count") >= 1) mlt_transition_process(transition, frame, frame); return mlt_frame_get_image(frame, image, format, width, height, writable); } /** Get the audio via the transition. NB: Not all transitions will accept a and b frames being the same... */ static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Obtain the transition instance mlt_transition transition = mlt_frame_pop_audio(frame); mlt_transition_process(transition, frame, frame); return mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Obtain the transition instance mlt_transition transition = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "instance", NULL); // If we haven't created the instance, do it now if (transition == NULL) { char *name = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "transition"); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); transition = mlt_factory_transition(profile, name, NULL); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "instance", transition, 0, (mlt_destructor) mlt_transition_close, NULL); } // We may still not have a transition... if (transition != NULL) { // Get the transition type int type = mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(transition), "_transition_type"); // Set the basic info mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "in", mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "in")); mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "out", mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "out")); // Refresh with current user values mlt_properties_pass(MLT_TRANSITION_PROPERTIES(transition), MLT_FILTER_PROPERTIES(filter), "transition."); if (type & 1 && !mlt_frame_is_test_card(frame) && !(mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "hide") & 1)) { mlt_frame_push_service(frame, transition); mlt_frame_push_get_image(frame, filter_get_image); } if (type & 2 && !mlt_frame_is_test_audio(frame) && !(mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "hide") & 2)) { mlt_frame_push_audio(frame, transition); mlt_frame_push_audio(frame, filter_get_audio); } if (type == 0) mlt_properties_debug(MLT_TRANSITION_PROPERTIES(transition), "unknown transition type", stderr); } else { mlt_properties_debug(MLT_FILTER_PROPERTIES(filter), "no transition", stderr); } return frame; } /** Constructor for the filter. */ mlt_filter filter_transition_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "transition", arg); filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/core/filter_transition.yml000664 000000 000000 00000001247 15172202314 022037 0ustar00rootroot000000 000000 schema_version: 0.2 type: filter identifier: transition title: Transition as Filter (*DEPRECATED*) version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video description: > Use a transition as a filter. The filters supplies the same frame as both the A and B clip to the transition. parameters: - identifier: transition argument: yes type: string title: Transition description: The MLT name of a transition. required: yes - identifier: transition.* type: properties description: > Properties may be set on the encapsulated composite transition. e.g.: transition.valign=c mlt-7.38.0/src/modules/core/filter_watermark.c000664 000000 000000 00000026303 15172202314 021263 0ustar00rootroot000000 000000 /* * filter_watermark.c -- watermark filter * Copyright (C) 2003-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Error we will return int error = 0; // Get the watermark filter object mlt_filter filter = mlt_frame_pop_service(frame); // Get the properties of the filter mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Get the producer from the filter mlt_producer producer = mlt_properties_get_data(properties, "producer", NULL); // Get the composite from the filter mlt_transition composite = mlt_properties_get_data(properties, "composite", NULL); // Get the resource to use char *resource = mlt_properties_get(properties, "resource"); char *old_resource = mlt_properties_get(properties, "_old_resource"); const char *transition = mlt_properties_get(properties, "transition"); // Create a composite if we don't have one if (composite == NULL) { // Create composite via the factory mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); composite = mlt_factory_transition(profile, transition, NULL); // Register the composite for reuse/destruction if (composite != NULL) mlt_properties_set_data(properties, "composite", composite, 0, (mlt_destructor) mlt_transition_close, NULL); } // If we have one if (composite != NULL) { // Get the properties mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES(composite); // Pass all the composite. properties on the filter down mlt_properties_set_int(composite_properties, "fill", 0); mlt_properties_pass(composite_properties, properties, "composite."); if (mlt_properties_get(properties, "composite.out") == NULL) mlt_properties_set_int(composite_properties, "out", mlt_properties_get_int(properties, "_out")); // Force a refresh mlt_properties_set_int(composite_properties, "refresh", 1); } // Create a producer if don't have one if (producer == NULL || (old_resource != NULL && strcmp(resource, old_resource))) { // Get the factory producer service char *factory = mlt_properties_get(properties, "factory"); // Create the producer mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); producer = mlt_factory_producer(profile, factory, resource); // If we have one if (producer != NULL) { // Register the producer for reuse/destruction mlt_properties_set_data(properties, "producer", producer, 0, (mlt_destructor) mlt_producer_close, NULL); // Ensure that we loop mlt_properties_set(MLT_PRODUCER_PROPERTIES(producer), "eof", "loop"); // Set the old resource mlt_properties_set(properties, "_old_resource", resource); } } if (producer != NULL) { // Get the producer properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Now pass all producer. properties on the filter down mlt_properties_pass(producer_properties, properties, "producer."); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Process all remaining filters first mlt_image_format img_format = mlt_image_rgba; if (transition && !strcmp("composite", transition)) img_format = mlt_image_yuv422; *format = img_format; error = mlt_frame_get_image(frame, image, format, width, height, 0); // Only continue if we have both producer and composite if (!error && composite != NULL && producer != NULL) { // Get the service of the producer mlt_service service = MLT_PRODUCER_SERVICE(producer); // Create a temporary frame so the original stays in tact. mlt_frame a_frame = mlt_frame_clone(frame, 0); // We will get the 'b frame' from the producer mlt_frame b_frame = NULL; // Get the original producer position mlt_position position = mlt_filter_get_position(filter, frame); // Make sure the producer is in the correct position mlt_producer_seek(producer, position); // Resetting position to appease the composite transition mlt_frame_set_position(a_frame, position); // Get the b frame and process with composite if successful if (mlt_service_get_frame(service, &b_frame, 0) == 0) { // Get the a and b frame properties mlt_properties a_props = MLT_FRAME_PROPERTIES(a_frame); mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); mlt_profile profile = mlt_service_profile(service); // Set the b frame to be in the same position and have same consumer requirements mlt_frame_set_position(b_frame, position); mlt_properties_set_int(b_props, "consumer.progressive", mlt_properties_get_int(a_props, "consumer.progressive") || mlt_properties_get_int(properties, "deinterlace")); // Check for the special case - no aspect ratio means no problem :-) if (mlt_frame_get_aspect_ratio(b_frame) == 0) mlt_frame_set_aspect_ratio(b_frame, mlt_profile_sar(profile)); if (mlt_frame_get_aspect_ratio(a_frame) == 0) mlt_frame_set_aspect_ratio(a_frame, mlt_profile_sar(profile)); if (mlt_properties_get_int(properties, "distort")) { mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(composite), "distort", 1); mlt_properties_set_int(a_props, "distort", 1); mlt_properties_set_int(b_props, "distort", 1); } *format = img_format; if (mlt_properties_get_int(properties, "reverse") == 0) { // Apply all filters that are attached to this filter to the b frame mlt_service_apply_filters(MLT_FILTER_SERVICE(filter), b_frame, 0); // Process the frame mlt_transition_process(composite, a_frame, b_frame); // Get the image error = mlt_frame_get_image(a_frame, image, format, width, height, 1); } else { char temp[132]; int count = 0; uint8_t *alpha = NULL; const char *rescale = mlt_properties_get(a_props, "consumer.rescale"); if (rescale == NULL || !strcmp(rescale, "none")) rescale = "hyper"; mlt_transition_process(composite, b_frame, a_frame); mlt_properties_set_int(a_props, "consumer.progressive", 1); mlt_properties_set_int(b_props, "consumer.progressive", 1); mlt_properties_set(a_props, "consumer.rescale", rescale); mlt_properties_set(b_props, "consumer.rescale", rescale); mlt_service_apply_filters(MLT_FILTER_SERVICE(filter), b_frame, 0); error = mlt_frame_get_image(b_frame, image, format, width, height, 1); alpha = mlt_frame_get_alpha(b_frame); mlt_frame_set_image(frame, *image, *width * *height * 2, NULL); if (alpha) mlt_frame_set_alpha(frame, alpha, *width * *height, NULL); mlt_properties_set_int(a_props, "width", *width); mlt_properties_set_int(a_props, "height", *height); mlt_properties_set_int(a_props, "progressive", 1); mlt_properties_inc_ref(b_props); strcpy(temp, "_b_frame"); while (mlt_properties_get_data(a_props, temp, NULL) != NULL) sprintf(temp, "_b_frame%d", count++); mlt_properties_set_data(a_props, temp, b_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } } // Close the temporary frames mlt_frame_close(a_frame); mlt_frame_close(b_frame); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Get the properties of the frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Assign the frame out point to the filter (just in case we need it later) mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "_out", mlt_properties_get_int(properties, "out")); // Push the filter on to the stack mlt_frame_push_service(frame, filter); // Push the get_image on to the stack mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_watermark_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); filter->process = filter_process; mlt_properties_set(properties, "factory", mlt_environment("MLT_PRODUCER")); if (arg != NULL) mlt_properties_set(properties, "resource", arg); mlt_properties_set(properties, "transition", "affine"); // Ensure that attached filters are handled privately mlt_properties_set_int(properties, "_filter_private", 1); } return filter; } mlt-7.38.0/src/modules/core/filter_watermark.yml000664 000000 000000 00000004543 15172202314 021644 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: watermark title: Overlay (*DEPRECATED*) version: 2 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Overlay text or images onto the video notes: > The watermark filter combines a frame producer and a transition to overlay the specified text or image onto the video. Supplying a filename with extension ".txt" causes the loader to load a pango producer. Supplying a file name with an extension supported by gtk-pixbuf causes the loader to load a pixbuf producer. See the pango and pixbuf producers for details. Note: If the filename begins with "+" the pango producer interprets the filename as pango text. Text Example: melt colour:red -filter watermark:"+First Line~Second Line.txt" composite.progressive=1 producer.align=centre composite.valign=c composite.halign=c Image Example: melt clip.dv -filter watermark:logo.png parameters: - identifier: resource argument: yes title: File/URL type: string description: The file to overlay. required: no readonly: no widget: fileopen - identifier: transition title: Composite Service type: string description: The name of the MLT transition service to use for compositing. default: affine required: yes readonly: no - identifier: distort title: Allow distorted scaling type: integer default: 0 minimum: 0 maximum: 1 widget: checkbox - identifier: producer.* title: Producer description: | Properties may be set on the encapsulated producer. e.g.: producer.align=centre See "pango" and "pixbuf" producers for details. readonly: no - identifier: composite.* title: Composite Service Property description: | Properties may be set on the encapsulated transition. e.g.: composite.valign=c See "composite" transition for details. readonly: no - identifier: reverse title: Reverse type: integer description: Overlay the video to which the filter is applied atop the supplied file. minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: deinterlace description: Force the supplied file to be be deinterlaced if it is interlaced. type: integer minimum: 0 maximum: 1 mutable: yes widget: checkbox mlt-7.38.0/src/modules/core/image_proc.c000664 000000 000000 00000062500 15172202314 020025 0ustar00rootroot000000 000000 /* * Copyright (c) 2022-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "image_proc.h" #include #include #include typedef struct { mlt_image src; mlt_image dst; int radius; } blur_slice_desc; static int blur_h_proc_rgba(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->src->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; int accumulator[] = {0, 0, 0, 0}; int x = 0; int y = 0; int step = 4; int linesize = step * desc->src->width; int radius = desc->radius; if (desc->radius > (desc->src->width / 2)) { radius = desc->src->width / 2; } double diameter = (radius * 2) + 1; for (y = slice_line_start; y < slice_line_end; y++) { uint8_t *first = desc->src->planes[0] + (y * linesize); uint8_t *last = first + linesize - step; uint8_t *s1 = first; uint8_t *s2 = first; uint8_t *d = desc->dst->planes[0] + (y * linesize); accumulator[0] = first[0] * (radius + 1); accumulator[1] = first[1] * (radius + 1); accumulator[2] = first[2] * (radius + 1); accumulator[3] = first[3] * (radius + 1); for (x = 0; x < radius; x++) { accumulator[0] += s1[0]; accumulator[1] += s1[1]; accumulator[2] += s1[2]; accumulator[3] += s1[3]; s1 += step; } for (x = 0; x <= radius; x++) { accumulator[0] += s1[0] - first[0]; accumulator[1] += s1[1] - first[1]; accumulator[2] += s1[2] - first[2]; accumulator[3] += s1[3] - first[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s1 += step; d += step; } for (x = radius + 1; x < desc->src->width - radius; x++) { accumulator[0] += s1[0] - s2[0]; accumulator[1] += s1[1] - s2[1]; accumulator[2] += s1[2] - s2[2]; accumulator[3] += s1[3] - s2[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s1 += step; s2 += step; d += step; } for (x = desc->src->width - radius; x < desc->src->width; x++) { accumulator[0] += last[0] - s2[0]; accumulator[1] += last[1] - s2[1]; accumulator[2] += last[2] - s2[2]; accumulator[3] += last[3] - s2[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s2 += step; d += step; } } return 0; } static int blur_v_proc_rgba(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); int slice_row_start, slice_width = mlt_slices_size_slice(jobs, index, desc->src->width, &slice_row_start); int slice_row_end = slice_row_start + slice_width; int accumulator[] = {0, 0, 0, 0}; int x = 0; int y = 0; int step = 4; int linesize = step * desc->src->width; int radius = desc->radius; if (desc->radius > (desc->src->height / 2)) { radius = desc->src->height / 2; } double diameter = (radius * 2) + 1; for (x = slice_row_start; x < slice_row_end; x++) { uint8_t *first = desc->src->planes[0] + (x * step); uint8_t *last = first + (linesize * (desc->src->height - 1)); uint8_t *s1 = first; uint8_t *s2 = first; uint8_t *d = desc->dst->planes[0] + (x * step); accumulator[0] = first[0] * (radius + 1); accumulator[1] = first[1] * (radius + 1); accumulator[2] = first[2] * (radius + 1); accumulator[3] = first[3] * (radius + 1); for (y = 0; y < radius; y++) { accumulator[0] += s1[0]; accumulator[1] += s1[1]; accumulator[2] += s1[2]; accumulator[3] += s1[3]; s1 += linesize; } for (y = 0; y <= radius; y++) { accumulator[0] += s1[0] - first[0]; accumulator[1] += s1[1] - first[1]; accumulator[2] += s1[2] - first[2]; accumulator[3] += s1[3] - first[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s1 += linesize; d += linesize; } for (y = radius + 1; y < desc->src->height - radius; y++) { accumulator[0] += s1[0] - s2[0]; accumulator[1] += s1[1] - s2[1]; accumulator[2] += s1[2] - s2[2]; accumulator[3] += s1[3] - s2[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s1 += linesize; s2 += linesize; d += linesize; } for (y = desc->src->height - radius; y < desc->src->height; y++) { accumulator[0] += last[0] - s2[0]; accumulator[1] += last[1] - s2[1]; accumulator[2] += last[2] - s2[2]; accumulator[3] += last[3] - s2[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s2 += linesize; d += linesize; } } return 0; } static int blur_h_proc_rgbx(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->src->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; int accumulator[] = {0, 0, 0}; int x = 0; int y = 0; int step = 4; int linesize = step * desc->src->width; int radius = desc->radius; if (desc->radius > (desc->src->width / 2)) { radius = desc->src->width / 2; } double diameter = (radius * 2) + 1; for (y = slice_line_start; y < slice_line_end; y++) { uint8_t *first = desc->src->planes[0] + (y * linesize); uint8_t *last = first + linesize - step; uint8_t *s1 = first; uint8_t *s2 = first; uint8_t *d = desc->dst->planes[0] + (y * linesize); accumulator[0] = first[0] * (radius + 1); accumulator[1] = first[1] * (radius + 1); accumulator[2] = first[2] * (radius + 1); for (x = 0; x < radius; x++) { accumulator[0] += s1[0]; accumulator[1] += s1[1]; accumulator[2] += s1[2]; s1 += step; } for (x = 0; x <= radius; x++) { accumulator[0] += s1[0] - first[0]; accumulator[1] += s1[1] - first[1]; accumulator[2] += s1[2] - first[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s1 += step; d += step; } for (x = radius + 1; x < desc->src->width - radius; x++) { accumulator[0] += s1[0] - s2[0]; accumulator[1] += s1[1] - s2[1]; accumulator[2] += s1[2] - s2[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s1 += step; s2 += step; d += step; } for (x = desc->src->width - radius; x < desc->src->width; x++) { accumulator[0] += last[0] - s2[0]; accumulator[1] += last[1] - s2[1]; accumulator[2] += last[2] - s2[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s2 += step; d += step; } } return 0; } static int blur_v_proc_rgbx(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); int slice_row_start, slice_width = mlt_slices_size_slice(jobs, index, desc->src->width, &slice_row_start); int slice_row_end = slice_row_start + slice_width; int accumulator[] = {0, 0, 0}; int x = 0; int y = 0; int step = 4; int linesize = step * desc->src->width; int radius = desc->radius; if (desc->radius > (desc->src->height / 2)) { radius = desc->src->height / 2; } double diameter = (radius * 2) + 1; for (x = slice_row_start; x < slice_row_end; x++) { uint8_t *first = desc->src->planes[0] + (x * step); uint8_t *last = first + (linesize * (desc->src->height - 1)); uint8_t *s1 = first; uint8_t *s2 = first; uint8_t *d = desc->dst->planes[0] + (x * step); accumulator[0] = first[0] * (radius + 1); accumulator[1] = first[1] * (radius + 1); accumulator[2] = first[2] * (radius + 1); for (y = 0; y < radius; y++) { accumulator[0] += s1[0]; accumulator[1] += s1[1]; accumulator[2] += s1[2]; s1 += linesize; } for (y = 0; y <= radius; y++) { accumulator[0] += s1[0] - first[0]; accumulator[1] += s1[1] - first[1]; accumulator[2] += s1[2] - first[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s1 += linesize; d += linesize; } for (y = radius + 1; y < desc->src->height - radius; y++) { accumulator[0] += s1[0] - s2[0]; accumulator[1] += s1[1] - s2[1]; accumulator[2] += s1[2] - s2[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s1 += linesize; s2 += linesize; d += linesize; } for (y = desc->src->height - radius; y < desc->src->height; y++) { accumulator[0] += last[0] - s2[0]; accumulator[1] += last[1] - s2[1]; accumulator[2] += last[2] - s2[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s2 += linesize; d += linesize; } } return 0; } static int blur_h_proc_rgba64(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->src->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; int accumulator[] = {0, 0, 0, 0}; int x = 0; int y = 0; int step = 4; int linesize = step * desc->src->width; int radius = desc->radius; if (desc->radius > (desc->src->width / 2)) { radius = desc->src->width / 2; } double diameter = (radius * 2) + 1; for (y = slice_line_start; y < slice_line_end; y++) { uint16_t *first = (uint16_t *) desc->src->planes[0] + (y * linesize); uint16_t *last = first + linesize - step; uint16_t *s1 = first; uint16_t *s2 = first; uint16_t *d = (uint16_t *) desc->dst->planes[0] + (y * linesize); accumulator[0] = first[0] * (radius + 1); accumulator[1] = first[1] * (radius + 1); accumulator[2] = first[2] * (radius + 1); accumulator[3] = first[3] * (radius + 1); for (x = 0; x < radius; x++) { accumulator[0] += s1[0]; accumulator[1] += s1[1]; accumulator[2] += s1[2]; accumulator[3] += s1[3]; s1 += step; } for (x = 0; x <= radius; x++) { accumulator[0] += s1[0] - first[0]; accumulator[1] += s1[1] - first[1]; accumulator[2] += s1[2] - first[2]; accumulator[3] += s1[3] - first[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s1 += step; d += step; } for (x = radius + 1; x < desc->src->width - radius; x++) { accumulator[0] += s1[0] - s2[0]; accumulator[1] += s1[1] - s2[1]; accumulator[2] += s1[2] - s2[2]; accumulator[3] += s1[3] - s2[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s1 += step; s2 += step; d += step; } for (x = desc->src->width - radius; x < desc->src->width; x++) { accumulator[0] += last[0] - s2[0]; accumulator[1] += last[1] - s2[1]; accumulator[2] += last[2] - s2[2]; accumulator[3] += last[3] - s2[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s2 += step; d += step; } } return 0; } static int blur_v_proc_rgba64(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); int slice_row_start, slice_width = mlt_slices_size_slice(jobs, index, desc->src->width, &slice_row_start); int slice_row_end = slice_row_start + slice_width; int accumulator[] = {0, 0, 0, 0}; int x = 0; int y = 0; int step = 4; int linesize = step * desc->src->width; int radius = desc->radius; if (desc->radius > (desc->src->height / 2)) { radius = desc->src->height / 2; } double diameter = (radius * 2) + 1; for (x = slice_row_start; x < slice_row_end; x++) { uint16_t *first = (uint16_t *) desc->src->planes[0] + (x * step); uint16_t *last = first + (linesize * (desc->src->height - 1)); uint16_t *s1 = first; uint16_t *s2 = first; uint16_t *d = (uint16_t *) desc->dst->planes[0] + (x * step); accumulator[0] = first[0] * (radius + 1); accumulator[1] = first[1] * (radius + 1); accumulator[2] = first[2] * (radius + 1); accumulator[3] = first[3] * (radius + 1); for (y = 0; y < radius; y++) { accumulator[0] += s1[0]; accumulator[1] += s1[1]; accumulator[2] += s1[2]; accumulator[3] += s1[3]; s1 += linesize; } for (y = 0; y <= radius; y++) { accumulator[0] += s1[0] - first[0]; accumulator[1] += s1[1] - first[1]; accumulator[2] += s1[2] - first[2]; accumulator[3] += s1[3] - first[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s1 += linesize; d += linesize; } for (y = radius + 1; y < desc->src->height - radius; y++) { accumulator[0] += s1[0] - s2[0]; accumulator[1] += s1[1] - s2[1]; accumulator[2] += s1[2] - s2[2]; accumulator[3] += s1[3] - s2[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s1 += linesize; s2 += linesize; d += linesize; } for (y = desc->src->height - radius; y < desc->src->height; y++) { accumulator[0] += last[0] - s2[0]; accumulator[1] += last[1] - s2[1]; accumulator[2] += last[2] - s2[2]; accumulator[3] += last[3] - s2[3]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); d[3] = lrint((double) accumulator[3] / diameter); s2 += linesize; d += linesize; } } return 0; } static int blur_h_proc_rgbx64(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->src->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; int accumulator[] = {0, 0, 0}; int x = 0; int y = 0; int step = 4; int linesize = step * desc->src->width; int radius = desc->radius; if (desc->radius > (desc->src->width / 2)) { radius = desc->src->width / 2; } double diameter = (radius * 2) + 1; for (y = slice_line_start; y < slice_line_end; y++) { uint16_t *first = (uint16_t *) desc->src->planes[0] + (y * linesize); uint16_t *last = first + linesize - step; uint16_t *s1 = first; uint16_t *s2 = first; uint16_t *d = (uint16_t *) desc->dst->planes[0] + (y * linesize); accumulator[0] = first[0] * (radius + 1); accumulator[1] = first[1] * (radius + 1); accumulator[2] = first[2] * (radius + 1); for (x = 0; x < radius; x++) { accumulator[0] += s1[0]; accumulator[1] += s1[1]; accumulator[2] += s1[2]; s1 += step; } for (x = 0; x <= radius; x++) { accumulator[0] += s1[0] - first[0]; accumulator[1] += s1[1] - first[1]; accumulator[2] += s1[2] - first[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s1 += step; d += step; } for (x = radius + 1; x < desc->src->width - radius; x++) { accumulator[0] += s1[0] - s2[0]; accumulator[1] += s1[1] - s2[1]; accumulator[2] += s1[2] - s2[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s1 += step; s2 += step; d += step; } for (x = desc->src->width - radius; x < desc->src->width; x++) { accumulator[0] += last[0] - s2[0]; accumulator[1] += last[1] - s2[1]; accumulator[2] += last[2] - s2[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s2 += step; d += step; } } return 0; } static int blur_v_proc_rgbx64(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); int slice_row_start, slice_width = mlt_slices_size_slice(jobs, index, desc->src->width, &slice_row_start); int slice_row_end = slice_row_start + slice_width; int accumulator[] = {0, 0, 0}; int x = 0; int y = 0; int step = 4; int linesize = step * desc->src->width * 2; int radius = desc->radius; if (desc->radius > (desc->src->height / 2)) { radius = desc->src->height / 2; } double diameter = (radius * 2) + 1; for (x = slice_row_start; x < slice_row_end; x++) { uint16_t *first = (uint16_t *) desc->src->planes[0] + (x * step); uint16_t *last = first + (linesize * (desc->src->height - 1)); uint16_t *s1 = first; uint16_t *s2 = first; uint16_t *d = (uint16_t *) desc->dst->planes[0] + (x * step); accumulator[0] = first[0] * (radius + 1); accumulator[1] = first[1] * (radius + 1); accumulator[2] = first[2] * (radius + 1); for (y = 0; y < radius; y++) { accumulator[0] += s1[0]; accumulator[1] += s1[1]; accumulator[2] += s1[2]; s1 += linesize; } for (y = 0; y <= radius; y++) { accumulator[0] += s1[0] - first[0]; accumulator[1] += s1[1] - first[1]; accumulator[2] += s1[2] - first[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s1 += linesize; d += linesize; } for (y = radius + 1; y < desc->src->height - radius; y++) { accumulator[0] += s1[0] - s2[0]; accumulator[1] += s1[1] - s2[1]; accumulator[2] += s1[2] - s2[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s1 += linesize; s2 += linesize; d += linesize; } for (y = desc->src->height - radius; y < desc->src->height; y++) { accumulator[0] += last[0] - s2[0]; accumulator[1] += last[1] - s2[1]; accumulator[2] += last[2] - s2[2]; d[0] = lrint((double) accumulator[0] / diameter); d[1] = lrint((double) accumulator[1] / diameter); d[2] = lrint((double) accumulator[2] / diameter); s2 += linesize; d += linesize; } } return 0; } /** Perform a box blur * * This function uses a sliding window accumulator method - applied * horizontally first and then vertically. * * \param self the Image object * \param hradius the radius of the horizontal blur in pixels * \param vradius radius of the vertical blur in pixels * \param preserve_alpha exclude the alpha channel from the blur operation */ void mlt_image_box_blur(mlt_image self, int hradius, int vradius, int preserve_alpha) { if (self->format != mlt_image_rgba && self->format != mlt_image_rgba64) { mlt_log(NULL, MLT_LOG_ERROR, "Image type %s not supported by box blur\n", mlt_image_format_name(self->format)); return; } // The horizontal blur is performed into a temporary image. // The vertical blur is performed back into the original image. struct mlt_image_s tmpimage; mlt_image_set_values(&tmpimage, NULL, self->format, self->width, self->height); mlt_image_alloc_data(&tmpimage); if (self->alpha) { mlt_image_alloc_alpha(&tmpimage); } mlt_slices_proc h_proc; mlt_slices_proc v_proc; if (preserve_alpha && self->format == mlt_image_rgba) { h_proc = blur_h_proc_rgbx; v_proc = blur_v_proc_rgbx; } else if (self->format == mlt_image_rgba) { h_proc = blur_h_proc_rgba; v_proc = blur_v_proc_rgba; } else if (preserve_alpha && self->format == mlt_image_rgba64) { h_proc = blur_h_proc_rgbx64; v_proc = blur_v_proc_rgbx64; } else { // self->format == mlt_image_rgba64 h_proc = blur_h_proc_rgba64; v_proc = blur_v_proc_rgba64; } blur_slice_desc desc; desc.src = self, desc.dst = &tmpimage, desc.radius = hradius, mlt_slices_run_normal(0, h_proc, &desc); desc.src = &tmpimage, desc.dst = self, desc.radius = vradius, mlt_slices_run_normal(0, v_proc, &desc); mlt_image_close(&tmpimage); } mlt-7.38.0/src/modules/core/image_proc.h000664 000000 000000 00000001771 15172202314 020035 0ustar00rootroot000000 000000 /* * Copyright (c) 2022-20023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IMAGE_PROC_H #define IMAGE_PROC_H #include #include void mlt_image_box_blur(mlt_image self, int hradius, int vradius, int preserve_alpha); #endif // IMAGE_PROC_H mlt-7.38.0/src/modules/core/link_timeremap.c000664 000000 000000 00000074007 15172202314 020725 0ustar00rootroot000000 000000 /* * link_timeremap.c * Copyright (C) 2020-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include // Private Types typedef struct { mlt_position prev_integration_position; double prev_integration_time; mlt_frame prev_frame; mlt_filter resample_filter; mlt_filter pitch_filter; } private_data; static void property_changed(mlt_service owner, mlt_link self, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (!name) return; if (strcmp("map", name) == 0) { // Copy the deprecated "map" parameter to the new "time_map" parameter. const char *value = mlt_properties_get(MLT_LINK_PROPERTIES(self), "map"); mlt_properties_set(MLT_LINK_PROPERTIES(self), "time_map", value); } else if (strcmp("speed_map", name) == 0) { // speed_map changed. Need to re-integrate from the beginning. private_data *pdata = (private_data *) self->child; pdata->prev_integration_position = 0; pdata->prev_integration_time = 0.0; } } static double integrate_source_time(mlt_link self, mlt_position position) { private_data *pdata = (private_data *) self->child; mlt_properties properties = MLT_LINK_PROPERTIES(self); mlt_position length = mlt_producer_get_length(MLT_LINK_PRODUCER(self)); mlt_position in = mlt_producer_get_in(MLT_LINK_PRODUCER(self)); double link_fps = mlt_producer_get_fps(MLT_LINK_PRODUCER(self)); mlt_position integration_distance = abs(pdata->prev_integration_position - position); if (pdata->prev_integration_position < in || integration_distance > (position - in)) { // Restart integration from the beginning pdata->prev_integration_position = in; pdata->prev_integration_time = 0.0; } double source_time = pdata->prev_integration_time; if (pdata->prev_integration_position < position) { for (mlt_position p = pdata->prev_integration_position; p < position; p++) { double speed = mlt_properties_anim_get_double(properties, "speed_map", p - in, length); double time_delta = speed / link_fps; source_time += time_delta; } } else if (pdata->prev_integration_position > position) { for (mlt_position p = position; p < pdata->prev_integration_position; p++) { double speed = mlt_properties_anim_get_double(properties, "speed_map", p - in, length); double time_delta = speed / link_fps; source_time -= time_delta; } } // Remember the integration results so that the next frame can start // integration from this position instead of from the beginning. pdata->prev_integration_position = position; pdata->prev_integration_time = source_time; return source_time; } static void link_configure(mlt_link self, mlt_profile chain_profile) { // Timeremap must always work with the same profile as the chain so that // animation, in and out will work. mlt_service_set_profile(MLT_LINK_SERVICE(self), chain_profile); } static void change_movit_format(mlt_frame frame, mlt_frame src_frame, mlt_image_format *format) { if (*format == mlt_image_movit) { const mlt_image_format src_format = mlt_properties_get_int(MLT_FRAME_PROPERTIES(src_frame), "format"); // First priority, preserve the alpha channel in the source if (src_format == mlt_image_rgba) { // Producers typically set frame property "format" to rgba in their get_frame callback *format = mlt_image_rgba; } else { // Use a 10-bit output if the end consumer wants it. // TODO add a "consumer.format" property to find other criteria for 10-bit const char *trc_str = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "consumer.color_trc"); mlt_color_trc trc = mlt_image_color_trc_id(trc_str); *format = trc == mlt_color_trc_arib_std_b67 ? mlt_image_yuv444p10 : mlt_image_rgba; } } } static int link_get_audio(mlt_frame frame, void **audio, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int requested_frequency = *frequency; int requested_samples = *samples; mlt_link self = (mlt_link) mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) self->child; mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_LINK_SERVICE(self)); if (!unique_properties) { return 1; } double source_time = mlt_properties_get_double(unique_properties, "source_time"); double source_duration = mlt_properties_get_double(unique_properties, "source_duration"); double source_fps = mlt_properties_get_double(unique_properties, "source_fps"); double source_speed = mlt_properties_get_double(unique_properties, "source_speed"); source_speed = fabs(source_speed); double link_fps = mlt_producer_get_fps(MLT_LINK_PRODUCER(self)); // Validate the request *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; *format = *format == mlt_audio_none ? mlt_audio_float : *format; if (source_speed < 0.1 || source_speed > 10) { // Return silent samples for speeds less than 0.1 or > 10 mlt_position position = mlt_frame_original_position(frame); *samples = mlt_audio_calculate_frame_samples(link_fps, *frequency, position); int size = mlt_audio_format_size(*format, *samples, *channels); *audio = mlt_pool_alloc(size); memset(*audio, 0, size); mlt_frame_set_audio(frame, *audio, *format, size, mlt_pool_release); return 0; } int src_frequency = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "audio_frequency"); if (src_frequency > 0) { *frequency = src_frequency; } // Calculate the samples to get from the input frames int link_sample_count = mlt_audio_calculate_frame_samples(link_fps, *frequency, mlt_frame_get_position(frame)); int sample_count = lrint((double) link_sample_count * source_speed); mlt_position in_frame_pos = floor(source_time * source_fps); int64_t first_out_sample = llrint(source_time * (double) *frequency); // Attempt to maintain sample continuity with the previous frame static const int64_t SAMPLE_CONTINUITY_ERROR_MARGIN = 4; int64_t continuity_sample = mlt_properties_get_int64(MLT_LINK_PROPERTIES(self), "_continuity_sample"); int64_t continuity_delta = continuity_sample - first_out_sample; if (source_duration > 0.0 && continuity_delta != 0) { // Forward: Continue from where the previous frame left off if within the margin of error if (continuity_delta > -SAMPLE_CONTINUITY_ERROR_MARGIN && continuity_delta < SAMPLE_CONTINUITY_ERROR_MARGIN) { sample_count = sample_count - continuity_delta; first_out_sample = continuity_sample; mlt_log_debug(MLT_LINK_SERVICE(self), "Maintain Forward Continuity: %d\n", (int) continuity_delta); } } else if (source_duration < 0.0 && continuity_delta != sample_count) { // Reverse: End where the previous frame left off if within the margin of error continuity_delta -= sample_count; if (continuity_delta > -SAMPLE_CONTINUITY_ERROR_MARGIN && continuity_delta < SAMPLE_CONTINUITY_ERROR_MARGIN) { sample_count = sample_count + continuity_delta; mlt_log_debug(MLT_LINK_SERVICE(self), "Maintain Reverse Continuity: %d\n", (int) continuity_delta); } } int64_t first_in_sample = mlt_audio_calculate_samples_to_position(source_fps, *frequency, in_frame_pos); int samples_to_skip = first_out_sample - first_in_sample; if (samples_to_skip < 0) { mlt_log_error(MLT_LINK_SERVICE(self), "Audio too late: %d\t%d\n", (int) first_out_sample, (int) first_in_sample); samples_to_skip = 0; } // Allocate the out buffer struct mlt_audio_s out; mlt_audio_set_values(&out, NULL, *frequency, *format, sample_count, *channels); mlt_audio_alloc_data(&out); // Copy audio from the input frames to the output buffer int samples_copied = 0; int samples_needed = sample_count; while (samples_needed > 0) { char key[19]; sprintf(key, "%d", in_frame_pos); mlt_frame src_frame = (mlt_frame) mlt_properties_get_data(unique_properties, key, NULL); if (!src_frame) { mlt_log_error(MLT_LINK_SERVICE(self), "Frame not found: %d\n", in_frame_pos); break; } int in_samples = mlt_audio_calculate_frame_samples(source_fps, *frequency, in_frame_pos); struct mlt_audio_s in; mlt_audio_set_values(&in, NULL, *frequency, *format, in_samples, *channels); int error = mlt_frame_get_audio(src_frame, &in.data, &in.format, &in.frequency, &in.channels, &in.samples); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "No audio: %d\n", in_frame_pos); break; } if (in.format == mlt_audio_none) { mlt_log_error(MLT_LINK_SERVICE(self), "Audio none: %d\n", in_frame_pos); error = 1; break; } int samples_to_copy = in.samples - samples_to_skip; if (samples_to_copy > samples_needed) { samples_to_copy = samples_needed; } mlt_log_debug(MLT_LINK_SERVICE(self), "Copy: %d\t%d\t%d\t%d\n", samples_to_skip, samples_to_skip + samples_to_copy - 1, samples_to_copy, in.samples); if (samples_to_copy > 0) { mlt_audio_copy(&out, &in, samples_to_copy, samples_to_skip, samples_copied); samples_copied += samples_to_copy; samples_needed -= samples_to_copy; } samples_to_skip = 0; in_frame_pos++; } if (samples_copied != sample_count) { mlt_log_error(MLT_LINK_SERVICE(self), "Sample under run: %d\t%d\n", samples_copied, sample_count); mlt_audio_shrink(&out, samples_copied); } if (source_duration < 0.0) { // Going backwards mlt_audio_reverse(&out); mlt_properties_set_int64(MLT_LINK_PROPERTIES(self), "_continuity_sample", first_out_sample); } else { mlt_properties_set_int64(MLT_LINK_PROPERTIES(self), "_continuity_sample", first_out_sample + sample_count); } int in_frequency = *frequency; out.frequency = lrint((double) out.frequency * (double) sample_count / (double) link_sample_count); mlt_frame_set_audio(frame, out.data, out.format, 0, out.release_data); mlt_audio_get_values(&out, audio, frequency, format, samples, channels); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_frequency", *frequency); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_channels", *channels); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_samples", *samples); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_format", *format); // Apply pitch compensation if requested int pitch_compensate = mlt_properties_get_int(MLT_LINK_PROPERTIES(self), "pitch"); if (pitch_compensate) { if (!pdata->pitch_filter) { pdata->pitch_filter = mlt_factory_filter(mlt_service_profile(MLT_LINK_SERVICE(self)), "rbpitch", NULL); } if (pdata->pitch_filter) { mlt_properties_set_int(MLT_FILTER_PROPERTIES(pdata->pitch_filter), "stretch", 1); // Set the pitchscale to compensate for the difference between the input sampling frequency and the requested sampling frequency. double pitchscale = (double) in_frequency / (double) requested_frequency; mlt_properties_set_double(MLT_FILTER_PROPERTIES(pdata->pitch_filter), "pitchscale", pitchscale); mlt_filter_process(pdata->pitch_filter, frame); } } // Apply a resampler if rbpitch is not stretching to provide if (!pitch_compensate || !pdata->pitch_filter) { if (!pdata->resample_filter) { pdata->resample_filter = mlt_factory_filter(mlt_service_profile(MLT_LINK_SERVICE(self)), "resample", NULL); if (!pdata->resample_filter) { pdata->resample_filter = mlt_factory_filter(mlt_service_profile( MLT_LINK_SERVICE(self)), "swresample", NULL); } } if (pdata->resample_filter) { mlt_filter_process(pdata->resample_filter, frame); } } // Final call to get_audio() to apply the pitch or resample filter *frequency = requested_frequency; *samples = requested_samples; int error = mlt_frame_get_audio(frame, audio, format, frequency, channels, samples); return error; } #define MAX_BLEND_IMAGES 10 static int link_get_image_blend(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_link self = (mlt_link) mlt_frame_pop_get_image(frame); mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_LINK_SERVICE(self)); if (!unique_properties) { return 1; } int requested_width = *width; int requested_height = *height; double source_time = mlt_properties_get_double(unique_properties, "source_time"); double source_fps = mlt_properties_get_double(unique_properties, "source_fps"); // Get pointers to all the images for this frame uint8_t *images[MAX_BLEND_IMAGES]; int image_count = 0; mlt_position in_frame_pos = floor(source_time * source_fps); char key[19]; sprintf(key, "%d", in_frame_pos); mlt_frame src_frame = mlt_properties_get_data(unique_properties, key, NULL); while (src_frame && image_count < MAX_BLEND_IMAGES) { mlt_service_lock(MLT_LINK_SERVICE(self)); mlt_properties_pass_list(MLT_FRAME_PROPERTIES(src_frame), MLT_FRAME_PROPERTIES(frame), "crop.left crop.right crop.top crop.bottom crop.original_width " "crop.original_height meta.media.width meta.media.height"); mlt_properties_copy(MLT_FRAME_PROPERTIES(src_frame), MLT_FRAME_PROPERTIES(frame), "consumer."); change_movit_format(frame, src_frame, format); int error = mlt_frame_get_image(src_frame, &images[image_count], format, &requested_width, &requested_height, 0); mlt_service_unlock(MLT_LINK_SERVICE(self)); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Failed to get image %s\n", key); break; } if (*width != requested_width || *height != requested_height) { mlt_log_error(MLT_LINK_SERVICE(self), "Dimension Mismatch (%s): %dx%d != %dx%d\n", key, requested_width, requested_height, *width, *height); break; } image_count++; in_frame_pos++; sprintf(key, "%d", in_frame_pos); mlt_frame next_frame = mlt_properties_get_data(unique_properties, key, NULL); if (next_frame) { src_frame = next_frame; } else { break; } } if (image_count <= 0) { mlt_log_error(MLT_LINK_SERVICE(self), "No images to blend\n"); return 1; } // Sum all the images into one image with 16 bit components int size = mlt_image_format_size(*format, *width, *height, NULL); *image = mlt_pool_alloc(size); int s = 0; if (*format == mlt_image_rgba64) { uint16_t *p = (uint16_t *) *image; size /= 2; for (s = 0; s < size; s++) { int32_t sum = 0; for (int i = 0; i < image_count; i++) { uint16_t *image16 = (uint16_t *) images[i]; sum += image16[s]; } p[s] = sum / image_count; } } else { uint8_t *p = *image; for (s = 0; s < size; s++) { int16_t sum = 0; int i = 0; for (i = 0; i < image_count; i++) { sum += *(images[i]); images[i]++; } *p = sum / image_count; p++; } } mlt_frame_set_image(frame, *image, size, mlt_pool_release); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "format", *format); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "width", *width); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "height", *height); mlt_properties_pass_list(MLT_FRAME_PROPERTIES(frame), MLT_FRAME_PROPERTIES(src_frame), "colorspace color_primaries color_trc full_range"); return 0; } static int link_get_image_nearest(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_link self = (mlt_link) mlt_frame_pop_get_image(frame); mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_LINK_SERVICE(self)); if (!unique_properties) { return 1; } double source_time = mlt_properties_get_double(unique_properties, "source_time"); double source_fps = mlt_properties_get_double(unique_properties, "source_fps"); mlt_position in_frame_pos = floor(source_time * source_fps); char key[19]; sprintf(key, "%d", in_frame_pos); mlt_frame src_frame = (mlt_frame) mlt_properties_get_data(unique_properties, key, NULL); if (src_frame) { uint8_t *in_image; mlt_service_lock(MLT_LINK_SERVICE(self)); mlt_properties_pass_list(MLT_FRAME_PROPERTIES(src_frame), MLT_FRAME_PROPERTIES(frame), "crop.left crop.right crop.top crop.bottom crop.original_width " "crop.original_height meta.media.width meta.media.height"); mlt_properties_copy(MLT_FRAME_PROPERTIES(src_frame), MLT_FRAME_PROPERTIES(frame), "consumer."); change_movit_format(frame, src_frame, format); int error = mlt_frame_get_image(src_frame, &in_image, format, width, height, 0); mlt_service_unlock(MLT_LINK_SERVICE(self)); if (!error) { int size = mlt_image_format_size(*format, *width, *height, NULL); *image = mlt_pool_alloc(size); memcpy(*image, in_image, size); mlt_frame_set_image(frame, *image, size, mlt_pool_release); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "format", *format); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "width", *width); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "height", *height); mlt_properties_pass_list(MLT_FRAME_PROPERTIES(frame), MLT_FRAME_PROPERTIES(src_frame), "colorspace color_primaries color_trc full_range"); uint8_t *in_alpha = mlt_frame_get_alpha(src_frame); if (in_alpha) { size = *width * *height; uint8_t *out_alpha = mlt_pool_alloc(size); memcpy(out_alpha, in_alpha, size); mlt_frame_set_alpha(frame, out_alpha, size, mlt_pool_release); }; return 0; } } return 1; } static int link_get_frame(mlt_link self, mlt_frame_ptr frame, int index) { mlt_properties properties = MLT_LINK_PROPERTIES(self); private_data *pdata = (private_data *) self->child; mlt_position position = mlt_producer_position(MLT_LINK_PRODUCER(self)); mlt_position length = mlt_producer_get_length(MLT_LINK_PRODUCER(self)); double source_time = 0.0; double source_duration = 0.0; double source_fps = mlt_producer_get_fps(self->next); double link_fps = mlt_producer_get_fps(MLT_LINK_PRODUCER(self)); // Assume that the user wants normal speed before the in point. mlt_position in = mlt_producer_get_in(MLT_LINK_PRODUCER(self)); double in_time = (double) in / link_fps; int result = 0; // Create a frame *frame = mlt_frame_init(MLT_LINK_SERVICE(self)); mlt_frame_set_position(*frame, mlt_producer_position(MLT_LINK_PRODUCER(self))); mlt_properties unique_properties = mlt_frame_unique_properties(*frame, MLT_LINK_SERVICE(self)); // Calculate the frames from the next link to be used if (mlt_properties_exists(properties, "speed_map")) { // Speed mapping source_time = integrate_source_time(self, position) + in_time; double next_source_time = integrate_source_time(self, position + 1) + in_time; source_duration = next_source_time - source_time; } else if (mlt_properties_exists(properties, "time_map")) { source_time = mlt_properties_anim_get_double(properties, "time_map", position - in, length) + in_time; double next_source_time = mlt_properties_anim_get_double(properties, "time_map", position - in + 1, length) + in_time; source_duration = next_source_time - source_time; } else { // No mapping specified. Assume 1.0 speed. // This can be used as a frame rate converter. source_time = (double) position / link_fps; source_duration = 1.0 / link_fps; } double frame_duration = 1.0 / link_fps; double source_speed = 0.0; if (source_duration != 0.0) { source_speed = source_duration / frame_duration; } mlt_properties_set_double(unique_properties, "source_fps", source_fps); mlt_properties_set_double(unique_properties, "source_time", source_time); mlt_properties_set_double(unique_properties, "source_duration", source_duration); mlt_properties_set_double(unique_properties, "source_speed", source_speed); mlt_log_debug(MLT_LINK_SERVICE(self), "Get Frame: %f -> %f\t%d\t%d\n", source_fps, link_fps, position, mlt_producer_get_in(MLT_LINK_PRODUCER(self))); // Get frames from the next link and pass them along with the new frame int in_frame_count = 0; mlt_frame src_frame = NULL; mlt_position prev_frame_position = pdata->prev_frame ? mlt_frame_get_position(pdata->prev_frame) : -1; mlt_position in_frame_pos = floor(source_time * source_fps); double frame_time = (double) in_frame_pos / source_fps; double source_end_time = source_time + fabs(source_duration); if (frame_time == source_end_time) { // Force one frame to be sent. source_end_time += 0.0000000001; } while (frame_time < source_end_time) { if (in_frame_pos == prev_frame_position) { // Reuse the previous frame to avoid seeking. src_frame = pdata->prev_frame; mlt_properties_inc_ref(MLT_FRAME_PROPERTIES(src_frame)); } else { mlt_producer_seek(self->next, in_frame_pos); result = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), &src_frame, index); if (result) { break; } } // Save the source frame on the output frame char key[19]; sprintf(key, "%d", in_frame_pos); mlt_properties_set_data(unique_properties, key, src_frame, 0, (mlt_destructor) mlt_frame_close, NULL); // Calculate the next frame in_frame_pos++; frame_time = (double) in_frame_pos / source_fps; in_frame_count++; } if (!src_frame) { mlt_frame_close(*frame); *frame = NULL; return 1; } // Copy some useful properties from one of the source frames. (*frame)->convert_image = src_frame->convert_image; (*frame)->convert_audio = src_frame->convert_audio; mlt_filter cpu_csc = (mlt_filter) mlt_properties_get_data(MLT_FRAME_PROPERTIES(src_frame), "_movit cpu_convert", NULL); if (cpu_csc) { mlt_properties_inc_ref(MLT_FILTER_PROPERTIES(cpu_csc)); mlt_properties_set_data(MLT_FRAME_PROPERTIES(*frame), "_movit cpu_convert", cpu_csc, 0, (mlt_destructor) mlt_filter_close, NULL); } mlt_properties_pass_list(MLT_FRAME_PROPERTIES(*frame), MLT_FRAME_PROPERTIES(src_frame), "audio_frequency"); mlt_properties_set_data(MLT_FRAME_PROPERTIES(*frame), "_producer", mlt_frame_get_original_producer(src_frame), 0, NULL, NULL); if (src_frame != pdata->prev_frame) { // Save the last source frame because it might be requested for the next frame. mlt_frame_close(pdata->prev_frame); mlt_properties_inc_ref(MLT_FRAME_PROPERTIES(src_frame)); pdata->prev_frame = src_frame; } // Setup callbacks char *mode = mlt_properties_get(properties, "image_mode"); mlt_frame_push_get_image(*frame, (void *) self); if (in_frame_count == 1 || !mode || !strcmp(mode, "nearest")) { mlt_frame_push_get_image(*frame, link_get_image_nearest); } else { mlt_frame_push_get_image(*frame, link_get_image_blend); } mlt_frame_push_audio(*frame, (void *) self); mlt_frame_push_audio(*frame, link_get_audio); mlt_producer_prepare_next(MLT_LINK_PRODUCER(self)); mlt_properties_set_double(properties, "speed", source_speed); return result; } static void link_close(mlt_link self) { if (self) { private_data *pdata = (private_data *) self->child; if (pdata) { mlt_frame_close(pdata->prev_frame); mlt_filter_close(pdata->resample_filter); mlt_filter_close(pdata->pitch_filter); free(pdata); } self->close = NULL; mlt_link_close(self); free(self); } } mlt_link link_timeremap_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_link self = mlt_link_init(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (self && pdata) { self->child = pdata; // Callback registration self->configure = link_configure; self->get_frame = link_get_frame; self->close = link_close; // Signal that this link performs frame rate conversion mlt_properties_set_int(MLT_LINK_PROPERTIES(self), "_frc", 1); mlt_events_listen(MLT_LINK_PROPERTIES(self), self, "property-changed", (mlt_listener) property_changed); } else { free(pdata); mlt_link_close(self); self = NULL; } return self; } mlt-7.38.0/src/modules/core/link_timeremap.yml000664 000000 000000 00000002310 15172202314 021270 0ustar00rootroot000000 000000 schema_version: 7.0 type: link identifier: timeremap title: Time Remap version: 3 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio - Video description: Remap frames in time. parameters: - identifier: time_map title: Time Map type: float description: > A map of input frame times to output frame times. Ignored if speed_map is set. mutable: yes animation: yes - identifier: speed_map title: Speed Map type: float description: > A map of input speed to output frame times. Overrides time_map mutable: yes animation: yes - identifier: map (*deprecated*) description: > This parameter is deprecated. Use time_map instead. - identifier: image_mode title: Image Mode type: string description: The image mode to use. values: - nearest # Output the nearest frame - blend # Blend the frames that make up the output default: nearest mutable: yes - identifier: speed title: Speed type: float description: The instantaneous speed of the last frame that was processed. readonly: yes - identifier: pitch title: Pitch Compensation type: boolean default: 0 mlt-7.38.0/src/modules/core/loader.dict000664 000000 000000 00000002004 15172202314 017660 0ustar00rootroot000000 000000 http://*=avformat,webvfx:plain: https://*=webvfx:plain: plain:http://*=webvfx:plain: plain:https://*=webvfx:plain: This is just like the loader producer except it does not add normalizing filters. parameters: - identifier: resource title: File/URL type: string description: The file for the producer to be based on. argument: yes required: yes readonly: yes widget: fileopen mlt-7.38.0/src/modules/core/producer_blank.c000664 000000 000000 00000004343 15172202314 020713 0ustar00rootroot000000 000000 /* * producer_blank.c * Copyright (C) 2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include static int producer_get_frame(mlt_producer self, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(self)); if (*frame) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); mlt_frame_set_position(*frame, mlt_producer_position(self)); // Set producer-specific frame properties mlt_properties_set_int(frame_properties, "progressive", 1); } // Calculate the next timecode mlt_producer_prepare_next(self); return 0; } static void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } mlt_producer producer_blank_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_producer self = calloc(1, sizeof(struct mlt_producer_s)); if (self && !mlt_producer_init(self, NULL)) { mlt_properties_set(MLT_PRODUCER_PROPERTIES(self), "mlt_service", "blank"); mlt_properties_set(MLT_PRODUCER_PROPERTIES(self), "resource", "blank"); // Callback registration self->get_frame = producer_get_frame; self->close = (mlt_destructor) producer_close; } else { free(self); self = NULL; } return self; } mlt-7.38.0/src/modules/core/producer_blank.yml000664 000000 000000 00000000555 15172202314 021273 0ustar00rootroot000000 000000 schema_version: 0.3 type: producer identifier: blank title: Blank version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video - Audio description: > A "Blank" producer returns frames that are white and silent. These frames are suitable for creating blank spaces in a playlist. mlt_producer_is_blank() will return 1 on this producer. mlt-7.38.0/src/modules/core/producer_colour.c000664 000000 000000 00000031745 15172202314 021135 0ustar00rootroot000000 000000 /* * producer_colour.c * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "framework/mlt_image.h" #include #include #include #include #include #include #include static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int index); static void producer_close(mlt_producer parent); mlt_producer producer_colour_init(mlt_profile profile, mlt_service_type type, const char *id, char *colour) { mlt_producer producer = calloc(1, sizeof(struct mlt_producer_s)); if (producer != NULL && mlt_producer_init(producer, NULL) == 0) { // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; // Set the default properties mlt_properties_set(properties, "resource", (!colour || !strcmp(colour, "")) ? "0x000000ff" : colour); mlt_properties_set(properties, "_resource", ""); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); return producer; } free(producer); return NULL; } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { mlt_image_format requested_format = *format; // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Obtain the producer for this frame mlt_producer producer = mlt_frame_pop_service(frame); mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); // Obtain properties of producer mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); // Get the current and previous colour strings char *now = mlt_properties_get(producer_props, "resource"); char *then = mlt_properties_get(producer_props, "_resource"); // Get the current image and dimensions cached in the producer int size = 0; uint8_t *image = mlt_properties_get_data(producer_props, "image", &size); int current_width = mlt_properties_get_int(producer_props, "_width"); int current_height = mlt_properties_get_int(producer_props, "_height"); mlt_image_format current_format = mlt_properties_get_int(producer_props, "_format"); // Parse the colour mlt_color color = mlt_properties_get_color(producer_props, "resource"); if (mlt_properties_get(producer_props, "mlt_image_format")) *format = mlt_image_format_id(mlt_properties_get(producer_props, "mlt_image_format")); // Choose suitable out values if nothing specific requested if (*format == mlt_image_none || *format == mlt_image_movit) *format = mlt_image_rgba; // Optimize the format to avoid unnecessary conversion if ((requested_format == mlt_image_rgba && *format == mlt_image_rgba64) || (requested_format == mlt_image_rgba64 && *format == mlt_image_rgba)) *format = requested_format; if (*width <= 0) *width = mlt_service_profile(MLT_PRODUCER_SERVICE(producer))->width; if (*height <= 0) *height = mlt_service_profile(MLT_PRODUCER_SERVICE(producer))->height; // Choose default image format if specific request is unsupported if (*format != mlt_image_yuv420p && *format != mlt_image_yuv422 && *format != mlt_image_rgb && *format != mlt_image_movit && *format != mlt_image_opengl_texture && *format != mlt_image_rgba64) *format = mlt_image_rgba; // See if we need to regenerate if (!now || (then && strcmp(now, then)) || *width != current_width || *height != current_height || *format != current_format) { // Color the image int i = *width * *height + 1; int bpp; // Allocate the image size = mlt_image_format_size(*format, *width, *height, &bpp); uint8_t *p = image = mlt_pool_alloc(size); // Update the producer mlt_properties_set_data(producer_props, "image", image, size, mlt_pool_release, NULL); mlt_properties_set_int(producer_props, "_width", *width); mlt_properties_set_int(producer_props, "_height", *height); mlt_properties_set_int(producer_props, "_format", *format); mlt_properties_set(producer_props, "_resource", now); mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); if (!strcmp(now, "checkerboard")) { struct mlt_image_s img; mlt_image_set_values(&img, NULL, *format, *width, *height); mlt_image_alloc_data(&img); mlt_image_fill_checkerboard(&img, 1.0); memcpy(image, img.data, size); } else { switch (*format) { case mlt_image_yuv420p: { int plane_size = *width * *height; uint8_t y, u, v; RGB2YUV_601_SCALED(color.r, color.g, color.b, y, u, v); memset(p + 0, y, plane_size); memset(p + plane_size, u, plane_size / 4); memset(p + plane_size + plane_size / 4, v, plane_size / 4); break; } case mlt_image_yuv422: { int uneven = *width % 2; int count = (*width - uneven) / 2 + 1; uint8_t y, u, v; RGB2YUV_601_SCALED(color.r, color.g, color.b, y, u, v); i = *height + 1; while (--i) { int j = count; while (--j) { *p++ = y; *p++ = u; *p++ = y; *p++ = v; } if (uneven) { *p++ = y; *p++ = u; } } break; } case mlt_image_rgb: while (--i) { *p++ = color.r; *p++ = color.g; *p++ = color.b; } break; case mlt_image_movit: case mlt_image_opengl_texture: memset(p, 0, size); break; case mlt_image_rgba: while (--i) { *p++ = color.r; *p++ = color.g; *p++ = color.b; *p++ = color.a; } break; case mlt_image_rgba64: { uint16_t *p16 = (uint16_t *) p; const int component_count = *width * *height * 4; for (int j = 0; j < component_count; j += 4) { p16[j] = (color.r << 8) + color.r; p16[j + 1] = (color.g << 8) + color.g; p16[j + 2] = (color.b << 8) + color.b; p16[j + 3] = (color.a << 8) + color.a; } break; } default: mlt_log_error(MLT_PRODUCER_SERVICE(producer), "invalid image format %s\n", mlt_image_format_name(*format)); } } } else { mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); } // Create the alpha channel int alpha_size = 0; uint8_t *alpha = NULL; // Initialise the alpha if (color.a < 255 || *format == mlt_image_rgba) { alpha_size = *width * *height; alpha = mlt_pool_alloc(alpha_size); if (alpha) memset(alpha, color.a, alpha_size); else alpha_size = 0; } // Clone our image if (buffer && image && size > 0) { *buffer = mlt_pool_alloc(size); memcpy(*buffer, image, size); } // Now update properties so we free the copy after mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); mlt_frame_set_alpha(frame, alpha, alpha_size, mlt_pool_release); mlt_properties_set_double(properties, "aspect_ratio", mlt_properties_get_double(producer_props, "aspect_ratio")); mlt_properties_set_int(properties, "meta.media.width", *width); mlt_properties_set_int(properties, "meta.media.height", *height); mlt_colorspace colorspace = mlt_colorspace_rgb; mlt_color_trc color_trc = mlt_color_trc_iec61966_2_1; mlt_color_primaries primaries = mlt_color_pri_bt709; int full_range = 1; switch (*format) { case mlt_image_yuv420p: case mlt_image_yuv422: colorspace = mlt_colorspace_bt601; color_trc = mlt_color_trc_smpte170m; primaries = mlt_color_pri_smpte170m; full_range = 0; break; case mlt_image_rgb: case mlt_image_rgba: case mlt_image_rgba64: break; default: mlt_log_error(MLT_PRODUCER_SERVICE(producer), "invalid image format %s\n", mlt_image_format_name(*format)); } mlt_properties_set_int(properties, "colorspace", colorspace); mlt_properties_set_int(properties, "color_trc", color_trc); mlt_properties_set_int(properties, "color_primaries", primaries); mlt_properties_set_int(properties, "full_range", full_range); return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); // Obtain properties of producer mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Set producer-specific frame properties mlt_properties_set_int(properties, "progressive", 1); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(properties, "meta.media.width", profile->width); mlt_properties_set_int(properties, "meta.media.height", profile->height); // colour is an alias for resource if (mlt_properties_get(producer_props, "colour") != NULL) mlt_properties_set(producer_props, "resource", mlt_properties_get(producer_props, "colour")); char *colorstring = mlt_properties_get(producer_props, "resource"); if (colorstring && strchr(colorstring, '/')) { colorstring = strdup(strrchr(colorstring, '/') + 1); mlt_properties_set(producer_props, "resource", colorstring); free(colorstring); } // Check if we have a predefined image format if (mlt_properties_exists(producer_props, "mlt_image_format")) { int image_format = mlt_image_format_id( mlt_properties_get(producer_props, "mlt_image_format")); mlt_properties_set_int(properties, "format", image_format); } else { mlt_color color = mlt_properties_get_color(producer_props, "resource"); // Inform framework of the default frame format for this producer mlt_properties_set_int(properties, "format", color.a < 255 ? mlt_image_rgba : mlt_image_yuv422); } // Push the get_image method mlt_frame_push_service(*frame, producer); mlt_frame_push_get_image(*frame, producer_get_image); // A hint to scalers and affine transition that this producer does not // benefit from interpolation. mlt_properties_set_int(properties, "interpolation_not_required", 1); } // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } mlt-7.38.0/src/modules/core/producer_colour.yml000664 000000 000000 00000001672 15172202314 021510 0ustar00rootroot000000 000000 schema_version: 0.3 type: producer identifier: colour title: Color version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: A simple color generator. parameters: - identifier: resource argument: yes title: Color description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. type: string required: no readonly: no default: black widget: color - identifier: mlt_image_format title: MLT image format type: string description: > Force to generate image in the specified format. The default behavior is to supply whatever was requested by the connected consumer. mutable: yes values: - yuv420p - yuv422 - rgb - rgba mlt-7.38.0/src/modules/core/producer_consumer.c000664 000000 000000 00000027347 15172202314 021470 0ustar00rootroot000000 000000 /* * producer_consumer.c -- produce as a consumer of an encapsulated producer * Copyright (C) 2008-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #define CONSUMER_PROPERTIES_PREFIX "consumer." #define PRODUCER_PROPERTIES_PREFIX "producer." #include #include #include #include struct context_s { mlt_producer self; mlt_producer producer; mlt_consumer consumer; mlt_profile profile; int64_t audio_counter; mlt_position audio_position; }; typedef struct context_s *context; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { context cx = mlt_frame_pop_service(frame); mlt_frame nested_frame = mlt_frame_pop_service(frame); *width = cx->profile->width; *height = cx->profile->height; int result = mlt_frame_get_image(nested_frame, image, format, width, height, writable); // Allocate the image int size = mlt_image_format_size(*format, *width, *height, NULL); uint8_t *new_image = mlt_pool_alloc(size); // Update the frame mlt_properties properties = mlt_frame_properties(frame); mlt_frame_set_image(frame, new_image, size, mlt_pool_release); memcpy(new_image, *image, size); mlt_properties_set(properties, "progressive", mlt_properties_get(MLT_FRAME_PROPERTIES(nested_frame), "progressive")); *image = new_image; // Copy the alpha channel uint8_t *alpha = mlt_frame_get_alpha_size(nested_frame, &size); if (alpha && size > 0) { new_image = mlt_pool_alloc(size); memcpy(new_image, alpha, size); mlt_frame_set_alpha(frame, new_image, size, mlt_pool_release); } return result; } static int get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { context cx = mlt_frame_pop_audio(frame); mlt_frame nested_frame = mlt_frame_pop_audio(frame); int result = 0; // if not repeating last frame if (mlt_frame_get_position(nested_frame) != cx->audio_position) { double fps = mlt_profile_fps(cx->profile); if (mlt_producer_get_fps(cx->self) < fps) { fps = mlt_producer_get_fps(cx->self); mlt_properties_set_double(MLT_FRAME_PROPERTIES(nested_frame), "producer_consumer_fps", fps); } *samples = mlt_audio_calculate_frame_samples(fps, *frequency, cx->audio_counter++); result = mlt_frame_get_audio(nested_frame, buffer, format, frequency, channels, samples); int size = mlt_audio_format_size(*format, *samples, *channels); int16_t *new_buffer = mlt_pool_alloc(size); mlt_frame_set_audio(frame, new_buffer, *format, size, mlt_pool_release); memcpy(new_buffer, *buffer, size); *buffer = new_buffer; cx->audio_position = mlt_frame_get_position(nested_frame); } else { // otherwise return no samples *samples = 0; } return result; } static void property_changed(mlt_properties owner, mlt_consumer self, mlt_event_data event_data) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); context cx = mlt_properties_get_data(properties, "context", NULL); if (!cx) return; const char *name = mlt_event_data_to_string(event_data); if (name && name == strstr(name, CONSUMER_PROPERTIES_PREFIX)) mlt_properties_set(MLT_CONSUMER_PROPERTIES(cx->consumer), name + strlen(CONSUMER_PROPERTIES_PREFIX), mlt_properties_get(properties, name)); if (name && name == strstr(name, PRODUCER_PROPERTIES_PREFIX)) mlt_properties_set(MLT_PRODUCER_PROPERTIES(cx->producer), name + strlen(PRODUCER_PROPERTIES_PREFIX), mlt_properties_get(properties, name)); } static int get_frame(mlt_producer self, mlt_frame_ptr frame, int index) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); context cx = mlt_properties_get_data(properties, "context", NULL); if (!cx) { // Allocate and initialize our context cx = mlt_pool_alloc(sizeof(struct context_s)); memset(cx, 0, sizeof(*cx)); mlt_properties_set_data(properties, "context", cx, 0, mlt_pool_release, NULL); cx->self = self; char *profile_name = mlt_properties_get(properties, "profile"); if (!profile_name) profile_name = mlt_properties_get(properties, "mlt_profile"); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(self)); if (profile_name) { cx->profile = mlt_profile_init(profile_name); cx->profile->is_explicit = 1; } else { cx->profile = mlt_profile_clone(profile); cx->profile->is_explicit = 0; } // Encapsulate a real producer for the resource cx->producer = mlt_factory_producer(cx->profile, NULL, mlt_properties_get(properties, "resource")); if ((profile_name && !strcmp(profile_name, "auto")) || mlt_properties_get_int(properties, "autoprofile")) { mlt_profile_from_producer(cx->profile, cx->producer); mlt_producer_close(cx->producer); cx->producer = mlt_factory_producer(cx->profile, NULL, mlt_properties_get(properties, "resource")); } // Since we control the seeking, prevent it from seeking on its own mlt_producer_set_speed(cx->producer, 0); cx->audio_position = -1; // We will encapsulate a consumer cx->consumer = mlt_consumer_new(cx->profile); // Do not use _pass_list on real_time so that it defaults to 0 in the absence of // an explicit real_time property. mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(cx->consumer), "real_time", mlt_properties_get_int(properties, "real_time")); mlt_properties_pass_list(MLT_CONSUMER_PROPERTIES(cx->consumer), properties, "buffer, prefill, deinterlacer, deinterlace_method, rescale"); mlt_properties_pass(MLT_CONSUMER_PROPERTIES(cx->consumer), properties, CONSUMER_PROPERTIES_PREFIX); mlt_properties_pass(MLT_PRODUCER_PROPERTIES(cx->producer), properties, PRODUCER_PROPERTIES_PREFIX); mlt_events_listen(properties, self, "property-changed", (mlt_listener) property_changed); // Connect it all together mlt_consumer_connect(cx->consumer, MLT_PRODUCER_SERVICE(cx->producer)); mlt_consumer_start(cx->consumer); } // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(self)); if (*frame) { // Seek the producer to the correct place // Calculate our positions double actual_position = (double) mlt_producer_frame(self); if (mlt_producer_get_speed(self) != 0) actual_position *= mlt_producer_get_speed(self); mlt_position need_first = floor(actual_position); mlt_producer_seek(cx->producer, lrint(need_first * mlt_profile_fps(cx->profile) / mlt_producer_get_fps(self))); // Get the nested frame mlt_frame nested_frame = mlt_consumer_rt_frame(cx->consumer); // Stack the producer and our methods on the nested frame mlt_frame_push_service(*frame, nested_frame); mlt_frame_push_service(*frame, cx); mlt_frame_push_get_image(*frame, get_image); mlt_frame_push_audio(*frame, nested_frame); mlt_frame_push_audio(*frame, cx); mlt_frame_push_audio(*frame, get_audio); // Give the returned frame temporal identity mlt_frame_set_position(*frame, mlt_producer_position(self)); // Store the nested frame on the produced frame for destruction mlt_properties frame_props = MLT_FRAME_PROPERTIES(*frame); mlt_properties_set_data(frame_props, "_producer_consumer.frame", nested_frame, 0, (mlt_destructor) mlt_frame_close, NULL); // Inform the normalizers about our video properties mlt_properties_set_double(frame_props, "aspect_ratio", mlt_profile_sar(cx->profile)); mlt_properties_set_int(frame_props, "width", cx->profile->width); mlt_properties_set_int(frame_props, "height", cx->profile->height); mlt_properties_set_int(frame_props, "meta.media.width", cx->profile->width); mlt_properties_set_int(frame_props, "meta.media.height", cx->profile->height); mlt_properties_set_int(frame_props, "progressive", cx->profile->progressive); } // Calculate the next timecode mlt_producer_prepare_next(self); return 0; } static void producer_close(mlt_producer self) { context cx = mlt_properties_get_data(MLT_PRODUCER_PROPERTIES(self), "context", NULL); // Shut down all the encapsulated services if (cx) { mlt_consumer_stop(cx->consumer); mlt_consumer_close(cx->consumer); mlt_producer_close(cx->producer); mlt_profile_close(cx->profile); } self->close = NULL; mlt_producer_close(self); free(self); } mlt_producer producer_consumer_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_producer self = mlt_producer_new(profile); // Encapsulate the real producer mlt_profile temp_profile = mlt_profile_clone(profile); temp_profile->is_explicit = 0; mlt_producer real_producer = mlt_factory_producer(temp_profile, NULL, arg); if (self && real_producer) { // Override some producer methods self->close = (mlt_destructor) producer_close; self->get_frame = get_frame; // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); mlt_properties_set(properties, "resource", arg); mlt_properties_pass_list(properties, MLT_PRODUCER_PROPERTIES(real_producer), "out, length"); // Done with the producer - will re-open later when we have the profile property mlt_producer_close(real_producer); } else { if (self) mlt_producer_close(self); if (real_producer) mlt_producer_close(real_producer); self = NULL; } mlt_profile_close(temp_profile); return self; } mlt-7.38.0/src/modules/core/producer_consumer.yml000664 000000 000000 00000002301 15172202314 022026 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: consumer title: Consumer as Producer version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video parameters: - identifier: resource argument: yes title: File/URL type: string description: A file name, URL, or producer name. required: yes - identifier: profile title: Profile type: string description: > The name of a MLT profile with which to load the resource. This defaults to the composition's profile, but could be overridden by the profile in MLT XML. Also, the value "auto" triggers profile detection. - identifier: autoprofile title: Auto-profile type: integer description: Generate a new, custom profile from the encapsulated resource. minimum: 0 maximum: 1 default: 0 - identifier: producer.* title: Producer properties description: A property and its value to apply to the encapsulated producer. type: properties mutable: yes - identifier: consumer.* title: Consumer properties description: A property and its value to apply to the encapsulated consumer. type: properties mutable: yes mlt-7.38.0/src/modules/core/producer_hold.c000664 000000 000000 00000017256 15172202314 020561 0ustar00rootroot000000 000000 /* * producer_hold.c -- frame holding producer * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include // Forward references static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index); static void producer_close(mlt_producer producer); /** Constructor for the frame holding producer. Basically, all this producer does is provide a producer wrapper for the requested producer, allows the specification of the frame required and will then repeatedly obtain that frame for each get_frame and get_image requested. */ mlt_producer producer_hold_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Construct a new holding producer mlt_producer self = mlt_producer_new(profile); // Construct the requested producer via loader mlt_producer producer = mlt_factory_producer(profile, NULL, arg); // Initialise the frame holding capabilities if (self != NULL && producer != NULL) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); // Store the producer mlt_properties_set_data(properties, "producer", producer, 0, (mlt_destructor) mlt_producer_close, NULL); // Set frame, in, out and length for this producer mlt_properties_set_position(properties, "frame", 0); mlt_properties_set_position(properties, "out", 25); mlt_properties_set(properties, "resource", arg); mlt_properties_set(properties, "method", "onefield"); // Override the get_frame method self->get_frame = producer_get_frame; self->close = (mlt_destructor) producer_close; } else { // Clean up (not sure which one failed, can't be bothered to find out, so close both) if (self) mlt_producer_close(self); if (producer) mlt_producer_close(producer); // Make sure we return NULL self = NULL; } // Return this producer return self; } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { // Get the properties of the frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Obtain the real frame mlt_frame real_frame = mlt_frame_pop_service(frame); // Get the image from the real frame int size = 0; *buffer = mlt_properties_get_data(MLT_FRAME_PROPERTIES(real_frame), "image", &size); *width = mlt_properties_get_int(MLT_FRAME_PROPERTIES(real_frame), "width"); *height = mlt_properties_get_int(MLT_FRAME_PROPERTIES(real_frame), "height"); // If this is the first time, get it from the producer if (*buffer == NULL) { mlt_properties_pass(MLT_FRAME_PROPERTIES(real_frame), properties, ""); // We'll deinterlace on the downstream deinterlacer mlt_properties_set_int(MLT_FRAME_PROPERTIES(real_frame), "consumer.progressive", 1); // We want distorted to ensure we don't hit the resize filter twice mlt_properties_set_int(MLT_FRAME_PROPERTIES(real_frame), "distort", 1); // Get the image mlt_frame_get_image(real_frame, buffer, format, width, height, writable); // Make sure we get the size *buffer = mlt_properties_get_data(MLT_FRAME_PROPERTIES(real_frame), "image", &size); } mlt_properties_pass(properties, MLT_FRAME_PROPERTIES(real_frame), ""); // Set the values obtained on the frame if (*buffer != NULL) { uint8_t *image = mlt_pool_alloc(size); memcpy(image, *buffer, size); *buffer = image; mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); } else { // Pass the current image as is mlt_frame_set_image(frame, *buffer, size, NULL); } // Make sure that no further scaling is done mlt_properties_set(properties, "consumer.rescale", "none"); mlt_properties_set(properties, "scale", "off"); // All done return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); // Construct a new frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // If we have a frame, then stack the producer itself and the get_image method if (*frame) { // Define the real frame mlt_frame real_frame = mlt_properties_get_data(properties, "real_frame", NULL); // Obtain real frame if we don't have it if (real_frame == NULL) { // Get the producer mlt_producer producer = mlt_properties_get_data(properties, "producer", NULL); // Get the frame position requested mlt_position position = mlt_properties_get_position(properties, "frame"); // Seek the producer to the correct place mlt_producer_seek(producer, position); // Get the real frame mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &real_frame, index); // Ensure that the real frame gets wiped eventually mlt_properties_set_data(properties, "real_frame", real_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } else { // Temporary fix - ensure that we aren't seen as a test frame uint8_t *image = mlt_properties_get_data(MLT_FRAME_PROPERTIES(real_frame), "image", NULL); mlt_frame_set_image(*frame, image, 0, NULL); mlt_properties_set_int(MLT_FRAME_PROPERTIES(*frame), "test_image", 0); } // Stack the real frame and method mlt_frame_push_service(*frame, real_frame); mlt_frame_push_service(*frame, producer_get_image); // Ensure that the consumer sees what the real frame has mlt_properties_pass(MLT_FRAME_PROPERTIES(*frame), MLT_FRAME_PROPERTIES(real_frame), ""); mlt_properties_set(MLT_FRAME_PROPERTIES(real_frame), "consumer.deinterlacer", mlt_properties_get(properties, "method")); } // Move to the next position mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } mlt-7.38.0/src/modules/core/producer_hold.yml000664 000000 000000 00000001100 15172202314 021115 0ustar00rootroot000000 000000 schema_version: 0.2 type: producer identifier: hold title: Still version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Repeat the same frame for the entirety of the clip. parameters: - identifier: resource argument: yes type: string title: File/URL description: A file name specification, URL, or producer name:argument. required: yes - identifier: frame type: integer title: Frame number description: The frame number of the frame to repeat default: 0 minimum: 0 mlt-7.38.0/src/modules/core/producer_loader-nogl.yml000664 000000 000000 00000001020 15172202314 022373 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: loader-nogl title: Loader Without OpenGL Normalization Filters version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio - Video - Hidden description: > This is just like the loader producer except it does not add OpenGL normalizing filters. parameters: - identifier: resource title: File/URL type: string description: The file for the producer to be based on. argument: yes required: yes readonly: yes widget: fileopen mlt-7.38.0/src/modules/core/producer_loader.c000664 000000 000000 00000024452 15172202314 021075 0ustar00rootroot000000 000000 /* * producer_loader.c -- auto-load producer by file name extension * Copyright (C) 2003-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include static mlt_properties dictionary = NULL; static mlt_properties normalizers = NULL; static mlt_producer create_from(mlt_profile profile, char *file, char *services) { mlt_producer producer = NULL; char *temp = strdup(services); char *service = temp; do { char *p = strchr(service, ','); if (p != NULL) *p++ = '\0'; // If the service name has a colon as field delimiter, then treat the // second field as a prefix for the file/url. char *prefix = strchr(service, ':'); if (prefix) { *prefix++ = '\0'; char *prefix_file = calloc(1, strlen(file) + strlen(prefix) + 1); strcpy(prefix_file, prefix); strcat(prefix_file, file); producer = mlt_factory_producer(profile, service, prefix_file); free(prefix_file); } else { producer = mlt_factory_producer(profile, service, file); } service = p; } while (producer == NULL && service != NULL); free(temp); return producer; } static mlt_producer create_producer(mlt_profile profile, char *file) { mlt_producer result = NULL; // 1st Line - check for service:resource handling // And ignore drive letters on Win32 - no single char services supported. if (strchr(file, ':') > file + 1) { char *temp = strdup(file); char *service = temp; char *resource = strchr(temp, ':'); *resource++ = '\0'; result = mlt_factory_producer(profile, service, resource); free(temp); } // 2nd Line preferences if (result == NULL) { int i = 0; char *lookup = strdup(file); char *p = lookup; // Make backup of profile for determining if we need to use 'consumer' producer. mlt_profile backup_profile = mlt_profile_clone(profile); // We only need to load the dictionary once if (dictionary == NULL) { char temp[PATH_MAX]; snprintf(temp, sizeof(temp), "%s/core/loader.dict", mlt_environment("MLT_DATA")); dictionary = mlt_properties_load(temp); mlt_factory_register_for_clean_up(dictionary, (mlt_destructor) mlt_properties_close); } // Convert the lookup string to lower case while (*p) { *p = tolower(*p); p++; } // Chop off the query string p = strrchr(lookup, '?'); if (p && p > lookup && p[-1] == '\\') p[-1] = '\0'; // Strip file:// prefix p = lookup; if (strncmp(lookup, "file://", 7) == 0) p += 7; // Iterate through the dictionary for (i = 0; result == NULL && i < mlt_properties_count(dictionary); i++) { char *name = mlt_properties_get_name(dictionary, i); if (fnmatch(name, p, 0) == 0) result = create_from(profile, file, mlt_properties_get_value(dictionary, i)); } // Check if the producer changed the profile - xml does this. if (result && backup_profile && backup_profile->is_explicit && (profile->width != backup_profile->width || profile->height != backup_profile->height || profile->sample_aspect_num != backup_profile->sample_aspect_num || profile->sample_aspect_den != backup_profile->sample_aspect_den || profile->frame_rate_num != backup_profile->frame_rate_num || profile->frame_rate_den != backup_profile->frame_rate_den || profile->colorspace != backup_profile->colorspace)) { // Restore the original profile attributes. profile->display_aspect_den = backup_profile->display_aspect_den; profile->display_aspect_num = backup_profile->display_aspect_num; profile->frame_rate_den = backup_profile->frame_rate_den; profile->frame_rate_num = backup_profile->frame_rate_num; profile->height = backup_profile->height; profile->progressive = backup_profile->progressive; profile->sample_aspect_den = backup_profile->sample_aspect_den; profile->sample_aspect_num = backup_profile->sample_aspect_num; profile->width = backup_profile->width; profile->colorspace = backup_profile->colorspace; free(profile->description); profile->description = strdup(backup_profile->description); // Use the 'consumer' producer. mlt_producer_close(result); result = mlt_factory_producer(profile, "consumer", file); } mlt_profile_close(backup_profile); free(lookup); } // Finally, try just loading as service if (result == NULL) result = mlt_factory_producer(profile, file, NULL); return result; } static void create_filter(mlt_profile profile, mlt_producer producer, const char *effect, int *created) { mlt_filter filter = NULL; int i = 0; int exists = 0; char *id = strdup(effect); char *arg = strchr(id, ':'); if (arg != NULL) *arg++ = '\0'; for (i = 0; (filter = mlt_service_filter(MLT_PRODUCER_SERVICE(producer), i)) != NULL; i++) { // Check if this filter already exists char *filter_id = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "mlt_service"); if (filter_id && strcmp(id, filter_id) == 0) { exists = 1; *created = 1; break; } else if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "_loader") == 0) { // Stop at the first non-loader filter. This will be the insertion point for the new filter. break; } } if (!exists) { filter = mlt_factory_filter(profile, id, arg); if (filter) { mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "_loader", 1); mlt_producer_attach(producer, filter); int last_filter_index = mlt_service_filter_count(MLT_PRODUCER_SERVICE(producer)) - 1; if (i != last_filter_index) { // Move the filter to be before any non-loader filters; mlt_service_move_filter(MLT_PRODUCER_SERVICE(producer), last_filter_index, i); } mlt_filter_close(filter); *created = 1; } } free(id); } static void attach_normalizers(mlt_profile profile, mlt_producer producer, int nogl) { // Loop variable int i; // Tokeniser mlt_tokeniser tokeniser = mlt_tokeniser_init(); // We only need to load the normalizing properties once if (normalizers == NULL) { char temp[PATH_MAX]; snprintf(temp, sizeof(temp), "%s/core/loader.ini", mlt_environment("MLT_DATA")); normalizers = mlt_properties_load(temp); mlt_factory_register_for_clean_up(normalizers, (mlt_destructor) mlt_properties_close); } // Apply normalizers for (i = 0; i < mlt_properties_count(normalizers); i++) { int j = 0; int created = 0; char *value = mlt_properties_get_value(normalizers, i); mlt_tokeniser_parse_new(tokeniser, value, ","); for (j = 0; !created && j < mlt_tokeniser_count(tokeniser); j++) { const char *filter_name = mlt_tokeniser_get_string(tokeniser, j); if (!nogl || (filter_name && strncmp(filter_name, "movit.", 6))) create_filter(profile, producer, filter_name, &created); } } // Close the tokeniser mlt_tokeniser_close(tokeniser); } mlt_producer producer_loader_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the producer mlt_producer producer = NULL; mlt_properties properties = NULL; int nogl = !strcmp(id, "loader-nogl"); if (arg != NULL) producer = create_producer(profile, arg); if (producer != NULL) properties = MLT_PRODUCER_PROPERTIES(producer); // Attach filters if we have a producer and it isn't already xml'd :-) if (producer && strcmp(id, "abnormal") && strncmp(arg, "abnormal:", 9) && mlt_properties_get(properties, "xml") == NULL && mlt_properties_get(properties, "_xml") == NULL && mlt_service_identify(MLT_PRODUCER_SERVICE(producer)) != mlt_service_chain_type && mlt_properties_get(properties, "loader_normalized") == NULL) attach_normalizers(profile, producer, nogl); if (producer && mlt_service_identify(MLT_PRODUCER_SERVICE(producer)) != mlt_service_chain_type) { // Always let the image and audio be converted int created = 0; // movit.convert skips setting the frame->convert_image pointer if GLSL cannot be used. if (!nogl) create_filter(profile, producer, "movit.convert", &created); // avcolor_space and imageconvert only set frame->convert_image if it has not been set. create_filter(profile, producer, "avcolor_space", &created); if (!created) create_filter(profile, producer, "imageconvert", &created); create_filter(profile, producer, "audioconvert", &created); } // Now make sure we don't lose our identity if (properties != NULL) mlt_properties_set_int(properties, "_mlt_service_hidden", 1); // Return the producer return producer; } mlt-7.38.0/src/modules/core/producer_loader.yml000664 000000 000000 00000001426 15172202314 021450 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: loader title: Loader version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video - Hidden description: > This producer has two roles: 1. it handles the mappings of all file names to the other producers; 2. it attaches normalizing filters (rescale, resize and resample) to the producers (when necessary). This producer simplifies many aspects of use. Essentially, it ensures that a consumer will receive images and audio precisely as they request them. parameters: - identifier: resource title: File/URL type: string description: The file for the producer to be based on. argument: yes required: yes readonly: yes widget: fileopen mlt-7.38.0/src/modules/core/producer_melt.c000664 000000 000000 00000057321 15172202314 020571 0ustar00rootroot000000 000000 /* * producer_melt.c -- load from melt command line syntax * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #define MELT_FILE_MAX_LINES (100000) #define MELT_FILE_MAX_LENGTH (2048) mlt_producer producer_melt_init(mlt_profile profile, mlt_service_type type, const char *id, char **argv); mlt_producer producer_melt_file_init(mlt_profile profile, mlt_service_type type, const char *id, char *file) { FILE *input = mlt_fopen(file, "r"); char **args = calloc(sizeof(char *), MELT_FILE_MAX_LINES); int count = 0; char temp[MELT_FILE_MAX_LENGTH]; if (input != NULL) { while (fgets(temp, MELT_FILE_MAX_LENGTH, input) && count < MELT_FILE_MAX_LINES) { if (temp[strlen(temp) - 1] != '\n') mlt_log_warning(NULL, "Exceeded maximum line length (%d) while reading a melt file.\n", MELT_FILE_MAX_LENGTH); temp[strlen(temp) - 1] = '\0'; if (strcmp(temp, "")) args[count++] = strdup(temp); } fclose(input); if (count == MELT_FILE_MAX_LINES) mlt_log_warning(NULL, "Reached the maximum number of lines (%d) while reading a melt file.\n" "Consider using MLT XML.\n", MELT_FILE_MAX_LINES); } mlt_producer result = producer_melt_init(profile, type, id, args); if (result != NULL) { mlt_properties_set(MLT_PRODUCER_PROPERTIES(result), "resource", file); mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(result), "loader_normalized", 1); } while (count--) free(args[count]); free(args); return result; } static void track_service(mlt_field field, void *service, mlt_destructor destructor) { mlt_properties properties = mlt_field_properties(field); int registered = mlt_properties_get_int(properties, "registered"); char *key = mlt_properties_get(properties, "registered"); mlt_properties_set_data(properties, key, service, 0, destructor, NULL); mlt_properties_set_int(properties, "registered", ++registered); } static mlt_producer create_producer(mlt_profile profile, mlt_field field, char *file) { mlt_producer result = mlt_factory_producer(profile, NULL, file); if (result != NULL) track_service(field, result, (mlt_destructor) mlt_producer_close); return result; } static mlt_filter create_attach(mlt_profile profile, mlt_field field, char *id, int track) { char *temp = strdup(id); char *arg = strchr(temp, ':'); if (arg != NULL) *arg++ = '\0'; mlt_filter filter = mlt_factory_filter(profile, temp, arg); if (filter != NULL) track_service(field, filter, (mlt_destructor) mlt_filter_close); free(temp); return filter; } static mlt_filter create_filter(mlt_profile profile, mlt_field field, char *id, int track) { char *temp = strdup(id); char *arg = strchr(temp, ':'); if (arg != NULL) *arg++ = '\0'; mlt_filter filter = mlt_factory_filter(profile, temp, arg); if (filter != NULL) { mlt_field_plant_filter(field, filter, track); track_service(field, filter, (mlt_destructor) mlt_filter_close); } free(temp); return filter; } static mlt_transition create_transition(mlt_profile profile, mlt_field field, char *id, int track) { char *temp = strdup(id); char *arg = strchr(temp, ':'); if (arg != NULL) *arg++ = '\0'; mlt_transition transition = mlt_factory_transition(profile, temp, arg); if (transition != NULL) { mlt_field_plant_transition(field, transition, track, track + 1); track_service(field, transition, (mlt_destructor) mlt_transition_close); } free(temp); return transition; } static mlt_link create_link(mlt_field field, char *id) { char *temp = strdup(id); char *arg = strchr(temp, ':'); if (arg != NULL) *arg++ = '\0'; mlt_link link = mlt_factory_link(temp, arg); if (link != NULL) track_service(field, link, (mlt_destructor) mlt_link_close); free(temp); return link; } mlt_producer producer_melt_init(mlt_profile profile, mlt_service_type type, const char *id, char **argv) { int i; int track = 0; mlt_producer producer = NULL; mlt_producer first_producer = NULL; mlt_tractor mix = NULL; mlt_playlist playlist = mlt_playlist_new(profile); mlt_properties group = mlt_properties_new(); mlt_tractor tractor = mlt_tractor_new(); mlt_properties properties = MLT_TRACTOR_PROPERTIES(tractor); mlt_field field = mlt_tractor_field(tractor); mlt_properties field_properties = mlt_field_properties(field); mlt_multitrack multitrack = mlt_tractor_multitrack(tractor); mlt_chain chain = NULL; char *title = NULL; // Assistance for template construction (allows -track usage to specify the first track) mlt_properties_set_int(MLT_PLAYLIST_PROPERTIES(playlist), "_melt_first", 1); // We need to track the number of registered filters mlt_properties_set_int(field_properties, "registered", 0); // Parse the arguments if (argv) for (i = 0; argv[i] != NULL; i++) { if (argv[i + 1] == NULL && (!strcmp(argv[i], "-attach") || !strcmp(argv[i], "-attach-cut") || !strcmp(argv[i], "-attach-track") || !strcmp(argv[i], "-attach-clip") || !strcmp(argv[i], "-repeat") || !strcmp(argv[i], "-split") || !strcmp(argv[i], "-join") || !strcmp(argv[i], "-mixer") || !strcmp(argv[i], "-mix") || !strcmp(argv[i], "-filter") || !strcmp(argv[i], "-transition") || !strcmp(argv[i], "-blank"))) { fprintf(stderr, "Argument missing for %s.\n", argv[i]); break; } if (!strcmp(argv[i], "-group")) { if (mlt_properties_count(group) != 0) { mlt_properties_close(group); group = mlt_properties_new(); } if (group != NULL) properties = group; } else if (!strcmp(argv[i], "-attach") || !strcmp(argv[i], "-attach-cut") || !strcmp(argv[i], "-attach-track") || !strcmp(argv[i], "-attach-clip")) { int type = !strcmp(argv[i], "-attach") ? 0 : !strcmp(argv[i], "-attach-cut") ? 1 : !strcmp(argv[i], "-attach-track") ? 2 : 3; mlt_filter filter = create_attach(profile, field, argv[++i], track); if (producer != NULL && !mlt_producer_is_cut(producer)) { mlt_playlist_clip_info info; mlt_playlist_append(playlist, producer); mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); producer = info.cut; } if (type == 1 || type == 2) { mlt_playlist_clip_info info; mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); producer = info.cut; } if (filter != NULL && mlt_playlist_count(playlist) > 0) { if (type == 0) mlt_service_attach((mlt_service) properties, filter); else if (type == 1) mlt_service_attach((mlt_service) producer, filter); else if (type == 2) mlt_service_attach((mlt_service) playlist, filter); else if (type == 3) mlt_service_attach((mlt_service) mlt_producer_cut_parent(producer), filter); properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_inherit(properties, group); } else if (filter != NULL) { mlt_service_attach((mlt_service) playlist, filter); properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_inherit(properties, group); } } else if (!strcmp(argv[i], "-repeat")) { int repeat = atoi(argv[++i]); if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); producer = NULL; if (mlt_playlist_count(playlist) > 0) { mlt_playlist_clip_info info; mlt_playlist_repeat_clip(playlist, mlt_playlist_count(playlist) - 1, repeat); mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); producer = info.cut; properties = MLT_PRODUCER_PROPERTIES(producer); } } else if (!strcmp(argv[i], "-split")) { int split = atoi(argv[++i]); if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); producer = NULL; if (mlt_playlist_count(playlist) > 0) { mlt_playlist_clip_info info; mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); split = split < 0 ? info.frame_out + split : split; mlt_playlist_split(playlist, mlt_playlist_count(playlist) - 1, split); mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); producer = info.cut; properties = MLT_PRODUCER_PROPERTIES(producer); } } else if (!strcmp(argv[i], "-swap")) { if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); producer = NULL; if (mlt_playlist_count(playlist) >= 2) { mlt_playlist_clip_info info; mlt_playlist_move(playlist, mlt_playlist_count(playlist) - 2, mlt_playlist_count(playlist) - 1); mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); producer = info.cut; properties = MLT_PRODUCER_PROPERTIES(producer); } } else if (!strcmp(argv[i], "-join")) { int clips = atoi(argv[++i]); if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); producer = NULL; if (mlt_playlist_count(playlist) > 0) { mlt_playlist_clip_info info; int clip = clips <= 0 ? 0 : mlt_playlist_count(playlist) - clips - 1; if (clip < 0) clip = 0; if (clip >= mlt_playlist_count(playlist)) clip = mlt_playlist_count(playlist) - 2; if (clips < 0) clips = mlt_playlist_count(playlist) - 1; mlt_playlist_join(playlist, clip, clips, 0); mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); producer = info.cut; properties = MLT_PRODUCER_PROPERTIES(producer); } } else if (!strcmp(argv[i], "-remove")) { if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); producer = NULL; if (mlt_playlist_count(playlist) > 0) { mlt_playlist_clip_info info; mlt_playlist_remove(playlist, mlt_playlist_count(playlist) - 1); mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); producer = info.cut; properties = MLT_PRODUCER_PROPERTIES(producer); } } else if (!strcmp(argv[i], "-mix")) { int length = atoi(argv[++i]); if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); producer = NULL; if (mlt_playlist_count(playlist) >= 2) { if (mlt_playlist_mix(playlist, mlt_playlist_count(playlist) - 2, length, NULL) == 0) { mlt_playlist_clip_info info; mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 1); if (mlt_properties_get_data((mlt_properties) info.producer, "mlt_mix", NULL) == NULL) mlt_playlist_get_clip_info(playlist, &info, mlt_playlist_count(playlist) - 2); mix = (mlt_tractor) mlt_properties_get_data((mlt_properties) info.producer, "mlt_mix", NULL); properties = NULL; } else { fprintf(stderr, "Mix failed?\n"); } } else { fprintf(stderr, "Invalid position for a mix...\n"); } } else if (!strcmp(argv[i], "-mixer")) { if (mix != NULL) { char *id = strdup(argv[++i]); char *arg = strchr(id, ':'); mlt_field field = mlt_tractor_field(mix); mlt_transition transition = NULL; if (arg != NULL) *arg++ = '\0'; transition = mlt_factory_transition(profile, id, arg); if (transition != NULL) { properties = MLT_TRANSITION_PROPERTIES(transition); mlt_properties_inherit(properties, group); mlt_field_plant_transition(field, transition, 0, 1); mlt_properties_set_position(properties, "in", 0); mlt_properties_set_position(properties, "out", mlt_producer_get_out((mlt_producer) mix)); mlt_transition_close(transition); } free(id); } else { fprintf(stderr, "Invalid mixer...\n"); } } else if (!strcmp(argv[i], "-filter")) { mlt_filter filter = create_filter(profile, field, argv[++i], track); if (filter != NULL) { properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_inherit(properties, group); } } else if (!strcmp(argv[i], "-transition")) { mlt_transition transition = create_transition(profile, field, argv[++i], track - 1); if (transition != NULL) { properties = MLT_TRANSITION_PROPERTIES(transition); mlt_properties_inherit(properties, group); } } else if (!strcmp(argv[i], "-blank")) { if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); producer = NULL; if (strchr(argv[i + 1], ':')) mlt_playlist_blank_time(playlist, argv[++i]); else // support for legacy where plain int is an out point instead of length mlt_playlist_blank(playlist, atof(argv[++i])); } else if (!strcmp(argv[i], "-track") || !strcmp(argv[i], "-null-track") || !strcmp(argv[i], "-video-track") || !strcmp(argv[i], "-audio-track") || !strcmp(argv[i], "-hide-track") || !strcmp(argv[i], "-hide-video") || !strcmp(argv[i], "-hide-audio")) { if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); producer = NULL; if (!mlt_properties_get_int(MLT_PLAYLIST_PROPERTIES(playlist), "_melt_first") || mlt_producer_get_playtime(MLT_PLAYLIST_PRODUCER(playlist)) > 0) { mlt_multitrack_connect(multitrack, MLT_PLAYLIST_PRODUCER(playlist), track++); track_service(field, playlist, (mlt_destructor) mlt_playlist_close); playlist = mlt_playlist_new(profile); } if (playlist != NULL) { properties = MLT_PLAYLIST_PROPERTIES(playlist); if (!strcmp(argv[i], "-null-track") || !strcmp(argv[i], "-hide-track")) mlt_properties_set_int(properties, "hide", 3); else if (!strcmp(argv[i], "-audio-track") || !strcmp(argv[i], "-hide-video")) mlt_properties_set_int(properties, "hide", 1); else if (!strcmp(argv[i], "-video-track") || !strcmp(argv[i], "-hide-audio")) mlt_properties_set_int(properties, "hide", 2); } } else if (!strcmp(argv[i], "-chain")) { if (chain == NULL && producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); else if (chain != NULL && mlt_chain_get_source(chain) != NULL) { mlt_playlist_append(playlist, producer); } chain = mlt_chain_init(profile); if (chain != NULL) { producer = MLT_CHAIN_PRODUCER(chain); properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_inherit(properties, group); track_service(field, chain, (mlt_destructor) mlt_chain_close); } } else if (!strcmp(argv[i], "-link")) { if (chain == NULL || mlt_chain_get_source(chain) == NULL) { fprintf(stderr, "A link can only be added to a chain with a producer.\n"); } else { mlt_link link = create_link(field, argv[++i]); if (link != NULL) { mlt_chain_attach(chain, link); properties = MLT_LINK_PROPERTIES(link); mlt_properties_inherit(properties, group); } } } else if (strchr(argv[i], '=') && strstr(argv[i], " strchr(argv[i], '='))) { mlt_properties_parse(properties, argv[i]); } else if (argv[i][0] != '-') { if (chain == NULL && producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); else if (chain != NULL && mlt_chain_get_source(chain) != NULL) { mlt_playlist_append(playlist, producer); chain = NULL; } if (title == NULL && strstr(argv[i], " strchr(argv[i], '='))) { i++; backtrack = 1; } if (backtrack) i--; } } // Connect last producer to playlist if (producer != NULL && !mlt_producer_is_cut(producer)) mlt_playlist_append(playlist, producer); // Track the last playlist too track_service(field, playlist, (mlt_destructor) mlt_playlist_close); // We must have a playlist to connect if (playlist && (!mlt_properties_get_int(MLT_PLAYLIST_PROPERTIES(playlist), "_melt_first") || mlt_producer_get_playtime(MLT_PLAYLIST_PRODUCER(playlist)) > 0)) mlt_multitrack_connect(multitrack, MLT_PLAYLIST_PRODUCER(playlist), track); mlt_producer prod = MLT_TRACTOR_PRODUCER(tractor); mlt_producer_optimise(prod); mlt_properties props = MLT_TRACTOR_PROPERTIES(tractor); mlt_properties_set_data(props, "group", group, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set_data(props, "first_producer", first_producer, 0, NULL, NULL); mlt_properties_set_position(props, "length", mlt_producer_get_out(MLT_MULTITRACK_PRODUCER(multitrack)) + 1); mlt_producer_set_in_and_out(prod, 0, mlt_producer_get_out(MLT_MULTITRACK_PRODUCER(multitrack))); if (title != NULL) mlt_properties_set(props, "title", strchr(title, '/') ? strrchr(title, '/') + 1 : title); // If the last producer has a consumer property connect it tractor if (producer) { mlt_consumer consumer = mlt_properties_get_data(MLT_PRODUCER_PROPERTIES(producer), "consumer", NULL); if (consumer) mlt_consumer_connect(consumer, MLT_PRODUCER_SERVICE(prod)); } return prod; } mlt-7.38.0/src/modules/core/producer_melt.yml000664 000000 000000 00000000335 15172202314 021141 0ustar00rootroot000000 000000 schema_version: 0.1 type: producer identifier: melt title: Melt description: Process melt command line. version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video mlt-7.38.0/src/modules/core/producer_melt_file.yml000664 000000 000000 00000000733 15172202314 022142 0ustar00rootroot000000 000000 schema_version: 0.2 type: producer identifier: melt_file title: Melt description: Process file containing melt command line syntax. version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video notes: Limited to files with 100000 or less lines and maximum line size of 2048 bytes. parameters: - identifier: resource argument: yes title: File description: The name of the melt text file. required: yes mlt-7.38.0/src/modules/core/producer_noise.c000664 000000 000000 00000015125 15172202314 020741 0ustar00rootroot000000 000000 /* * producer_noise.c -- noise generating producer * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /** Random number generator */ typedef struct { unsigned int x; unsigned int y; } rand_seed; static void init_seed(rand_seed *seed, int init) { // Use the initial value to initialize the seed to arbitrary values. // This causes the algorithm to produce consistent results each time for the same frame number. seed->x = 521288629 + init - (init << 16); seed->y = 362436069 - init + (init << 16); } static inline unsigned int fast_rand(rand_seed *seed) { static unsigned int a = 18000, b = 30903; seed->x = a * (seed->x & 65535) + (seed->x >> 16); seed->y = b * (seed->y & 65535) + (seed->y >> 16); return ((seed->x << 16) + (seed->y & 65535)); } // Forward declarations static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index); static void producer_close(mlt_producer producer); /** Initialise. */ mlt_producer producer_noise_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); // Initialise the producer if (producer != NULL) { // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; } return producer; } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { // Choose suitable out values if nothing specific requested if (*width <= 0) *width = mlt_service_profile(MLT_PRODUCER_SERVICE(mlt_frame_get_original_producer(frame))) ->width; if (*height <= 0) *height = mlt_service_profile(MLT_PRODUCER_SERVICE(mlt_frame_get_original_producer(frame))) ->height; // Calculate the size of the image int size = *width * *height * 2; // Set the format being returned *format = mlt_image_yuv422; // Allocate the image *buffer = mlt_pool_alloc(size); // Update the frame mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); // Before we write to the image, make sure we have one if (*buffer != NULL) { // Calculate the end of the buffer uint8_t *p = *buffer + *width * *height * 2; // Value to hold a random number uint32_t value; // Initialize seed from the frame number. rand_seed seed; init_seed(&seed, mlt_frame_get_position(frame)); // Generate random noise while (p != *buffer) { value = fast_rand(&seed) & 0xff; *(--p) = 128; *(--p) = value < 16 ? 16 : value > 240 ? 240 : value; } } return 0; } static int producer_get_audio(mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int size = 0; // Correct the returns if necessary *samples = *samples <= 0 ? 1920 : *samples; *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; *format = mlt_audio_s16; // Calculate the size of the buffer size = *samples * *channels * sizeof(int16_t); // Allocate the buffer *buffer = mlt_pool_alloc(size); // Make sure we got one and fill it if (*buffer != NULL) { int16_t *p = *buffer + size / 2; rand_seed seed; init_seed(&seed, mlt_frame_get_position(frame)); while (p != *buffer) { int16_t val = (int16_t) fast_rand(&seed); *(--p) = val; } } // Set the buffer for destruction mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set(frame_properties, "channel_layout", mlt_properties_get(frame_properties, "consumer.channel_layout")); return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // Check that we created a frame and initialise it if (*frame != NULL) { // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); // Aspect ratio is whatever it needs to be mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); // Set producer-specific frame properties mlt_properties_set_int(properties, "progressive", 1); // Inform framework that this producer creates yuv frames by default mlt_properties_set_int(properties, "format", mlt_image_yuv422); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Push the get_image method mlt_frame_push_get_image(*frame, producer_get_image); // Specify the audio mlt_frame_push_audio(*frame, producer_get_audio); } // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } mlt-7.38.0/src/modules/core/producer_noise.yml000664 000000 000000 00000000331 15172202314 021311 0ustar00rootroot000000 000000 schema_version: 0.1 type: producer identifier: noise title: Noise version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video description: White noise producer mlt-7.38.0/src/modules/core/producer_timewarp.c000664 000000 000000 00000036437 15172202314 021465 0ustar00rootroot000000 000000 /* * producer_timewarp.c -- modify speed and direction of a clip * Copyright (C) 2015-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include // Private Types typedef struct { int first_frame; double speed; int reverse; mlt_producer clip_producer; mlt_profile clip_profile; mlt_properties clip_parameters; mlt_filter pitch_filter; } private_data; // Private Functions static void timewarp_property_changed(mlt_service owner, mlt_producer producer, mlt_event_data event_data) { private_data *pdata = (private_data *) producer->child; const char *name = mlt_event_data_to_string(event_data); if (mlt_properties_get_int(pdata->clip_parameters, name) || !strcmp(name, "length") || !strcmp(name, "in") || !strcmp(name, "out") || !strcmp(name, "ignore_points") || !strcmp(name, "eof") || !strncmp(name, "meta.", 5)) { // Pass parameter changes from this producer to the encapsulated clip // producer. mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES(pdata->clip_producer); mlt_events_block(clip_properties, producer); mlt_properties_pass_property(clip_properties, producer_properties, name); mlt_events_unblock(clip_properties, producer); } } static void clip_property_changed(mlt_service owner, mlt_producer producer, mlt_event_data event_data) { private_data *pdata = (private_data *) producer->child; const char *name = mlt_event_data_to_string(event_data); if (mlt_properties_get_int(pdata->clip_parameters, name) || !strcmp(name, "length") || !strcmp(name, "in") || !strcmp(name, "out") || !strcmp(name, "ignore_points") || !strcmp(name, "eof") || !strncmp(name, "meta.", 5)) { // The encapsulated clip producer might change its own parameters. // Pass those changes to this producer. mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES(pdata->clip_producer); mlt_events_block(producer_properties, producer); mlt_properties_pass_property(producer_properties, clip_properties, name); mlt_events_unblock(producer_properties, producer); } } static int producer_probe(mlt_producer parent) { if (parent && parent->child) { private_data *pdata = (private_data *) parent->child; if (pdata->clip_producer) { return mlt_producer_probe(pdata->clip_producer); } } return 1; } static int producer_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_producer producer = mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) producer->child; struct mlt_audio_s audio; mlt_audio_set_values(&audio, *buffer, *frequency, *format, *samples, *channels); int error = mlt_frame_get_audio(frame, &audio.data, &audio.format, &audio.frequency, &audio.channels, &audio.samples); // Scale the frequency to account for the speed change. // The resample normalizer will convert it to the requested frequency audio.frequency = (double) audio.frequency * fabs(pdata->speed); if (pdata->speed < 0.0) { mlt_audio_reverse(&audio); } mlt_audio_get_values(&audio, buffer, frequency, format, samples, channels); return error; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { private_data *pdata = (private_data *) producer->child; mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); if (pdata->first_frame && pdata->clip_producer) { // Pass parameters from this producer to the clip producer // Properties that are set after initialization are not always caught by // the property_changed event - so do this once after init. int n = mlt_properties_count(pdata->clip_parameters); int i = 0; mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES(pdata->clip_producer); mlt_events_block(clip_properties, producer); for (i = 0; i < n; i++) { char *name = mlt_properties_get_name(pdata->clip_parameters, i); if (mlt_properties_get_int(clip_properties, name) && mlt_properties_get(producer_properties, name) && strcmp("resource", name)) { mlt_properties_pass_property(clip_properties, producer_properties, name); } } mlt_events_unblock(clip_properties, producer); pdata->first_frame = 0; } if (pdata->clip_producer) { // Seek the clip producer to the appropriate position mlt_position clip_position = mlt_producer_position(producer); if (pdata->speed < 0.0) { clip_position = mlt_properties_get_int(producer_properties, "out") - clip_position; } if (!mlt_properties_get_int(producer_properties, "ignore_points")) { clip_position += mlt_producer_get_in(producer); } mlt_producer_seek(pdata->clip_producer, clip_position); // Get the frame from the clip producer mlt_service_get_frame(MLT_PRODUCER_SERVICE(pdata->clip_producer), frame, index); // Configure callbacks if (!mlt_frame_is_test_audio(*frame)) { mlt_frame_push_audio(*frame, producer); mlt_frame_push_audio(*frame, producer_get_audio); // Pitch compensation does not work well for speed *reduction* of 1/10th or less. if (mlt_properties_get_int(producer_properties, "warp_pitch") && fabs(pdata->speed) >= 0.1) { if (!pdata->pitch_filter) { pdata->pitch_filter = mlt_factory_filter(mlt_service_profile( MLT_PRODUCER_SERVICE(producer)), "rbpitch", NULL); } if (pdata->pitch_filter) { mlt_properties_set_double(MLT_FILTER_PROPERTIES(pdata->pitch_filter), "pitchscale", 1.0 / fabs(pdata->speed)); mlt_filter_process(pdata->pitch_filter, *frame); } } } } else { *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); } // Set the correct position on the frame mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer producer) { private_data *pdata = (private_data *) producer->child; if (pdata) { mlt_producer_close(pdata->clip_producer); mlt_profile_close(pdata->clip_profile); mlt_properties_close(pdata->clip_parameters); mlt_filter_close(pdata->pitch_filter); free(pdata); } producer->child = NULL; producer->close = NULL; mlt_producer_close(producer); free(producer); } mlt_producer producer_timewarp_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (arg && producer && pdata) { double frame_rate_num_scaled; mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Initialize the producer mlt_properties_set(producer_properties, "resource", arg); producer->child = pdata; producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; mlt_properties_set_data(producer_properties, "mlt_producer_probe", producer_probe, 0, NULL, NULL); // Get the resource to be passed to the clip producer char *resource = strchr(arg, ':'); if (resource == NULL) resource = arg; // Apparently speed was not specified. else resource++; // move past the delimiter. // Initialize private data pdata->first_frame = 1; pdata->speed = atof(arg); if (pdata->speed == 0.0) { pdata->speed = 1.0; } pdata->clip_profile = NULL; pdata->clip_parameters = NULL; pdata->clip_producer = NULL; pdata->pitch_filter = NULL; // Create a false profile to be used by the clip producer. pdata->clip_profile = mlt_profile_clone(mlt_service_profile(MLT_PRODUCER_SERVICE(producer))); // Frame rate must be recalculated for the clip profile to change the time base. if (pdata->clip_profile->frame_rate_num < 1000) { // Scale the frame rate fraction so we keep more accuracy when // the speed is factored in. pdata->clip_profile->frame_rate_num *= 1000; pdata->clip_profile->frame_rate_den *= 1000; } frame_rate_num_scaled = (double) pdata->clip_profile->frame_rate_num / fabs(pdata->speed); if (frame_rate_num_scaled > INT_MAX) // Check for overflow in case speed < 1.0 { //scale by denominator to avoid overflow. pdata->clip_profile->frame_rate_den = (double) pdata->clip_profile->frame_rate_den * fabs(pdata->speed); } else { //scale by numerator pdata->clip_profile->frame_rate_num = frame_rate_num_scaled; } // Create a producer for the clip using the false profile. pdata->clip_producer = mlt_factory_producer(pdata->clip_profile, "abnormal", resource); if (pdata->clip_producer) { mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES(pdata->clip_producer); int n = 0; int i = 0; // Set the speed to 0 since we will control the seeking mlt_producer_set_speed(pdata->clip_producer, 0); // Create a list of all parameters used by the clip producer so that // they can be passed between the clip producer and this producer. pdata->clip_parameters = mlt_properties_new(); mlt_repository repository = mlt_factory_repository(); mlt_properties clip_metadata = mlt_repository_metadata(repository, mlt_service_producer_type, mlt_properties_get(clip_properties, "mlt_service")); if (clip_metadata) { mlt_properties params = (mlt_properties) mlt_properties_get_data(clip_metadata, "parameters", NULL); if (params) { n = mlt_properties_count(params); for (i = 0; i < n; i++) { mlt_properties param = (mlt_properties) mlt_properties_get_data(params, mlt_properties_get_name(params, i), NULL); char *identifier = mlt_properties_get(param, "identifier"); if (identifier) { // Set the value to 1 to indicate the parameter exists. mlt_properties_set_int(pdata->clip_parameters, identifier, 1); } } // Explicitly exclude the "resource" parameter since it needs to be different. mlt_properties_set_int(pdata->clip_parameters, "resource", 0); } } // Pass parameters and properties from the clip producer to this producer. // Some properties may have been set during initialization. n = mlt_properties_count(clip_properties); for (i = 0; i < n; i++) { char *name = mlt_properties_get_name(clip_properties, i); if (mlt_properties_get_int(pdata->clip_parameters, name) || !strcmp(name, "length") || !strcmp(name, "in") || !strcmp(name, "out") || !strncmp(name, "meta.", 5)) { mlt_properties_pass_property(producer_properties, clip_properties, name); } } // Initialize warp producer properties mlt_properties_set_double(producer_properties, "warp_speed", pdata->speed); mlt_properties_set(producer_properties, "warp_resource", mlt_properties_get(clip_properties, "resource")); // Monitor property changes from both producers so that the clip // parameters can be passed back and forth. mlt_events_listen(clip_properties, producer, "property-changed", (mlt_listener) clip_property_changed); mlt_events_listen(producer_properties, producer, "property-changed", (mlt_listener) timewarp_property_changed); } } if (!producer || !pdata || !pdata->clip_producer) { if (pdata) { mlt_producer_close(pdata->clip_producer); mlt_profile_close(pdata->clip_profile); mlt_properties_close(pdata->clip_parameters); free(pdata); } if (producer) { producer->child = NULL; producer->close = NULL; mlt_producer_close(producer); free(producer); producer = NULL; } } return producer; } mlt-7.38.0/src/modules/core/producer_timewarp.yml000664 000000 000000 00000004612 15172202314 022032 0ustar00rootroot000000 000000 schema_version: 0.3 type: producer identifier: timewarp title: Time Warp version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio - Video description: > Timewarp is a wrapper producer that allows temporal effects on an encapsulated producer. The encapsulated producer can be modified to change the speed (faster/slower) or direction (forward/reverse) In addition to the parameters listed below, this producer inherits all parameters of the encapsulated producer. The encapsulated producer parameters can be accessed directly (without any prefix) by getting and setting those parameters on the timewarp producer. In addition to modifying the speed of the video, the audio is also slowed down or sped up to match the video. parameters: - identifier: resource title: Speed and Resource type: string argument: yes description: | The speed factor and the producer resource in the form: [speed:resource] The speed can be any decimal number between 20 and 0.01. Negative speed values cause the file to be played backwards. The resource can be a file name or any producer service name. The resource will be passed to the loader to create the encapsulated producer. Examples: File opened for 2x speed forward: "2.0:example.mp4" File opened for 2x speed reverse: "-2.0:example.mp4" File opened for 1x speed reverse: "-1.0:example.mp4" File opened for 0.25x speed (slow motion) forward: "0.25:example.mp4" The most common use for this producer is to change the speed of a file. However, any arbitrary producer can be specified. E.g.: "2.0:colour:red" readonly: no required: yes mutable: no - identifier: warp_pitch title: Pitch Compensation type: boolean description: Enable or disable pitch compensation readonly: no mutable: no default: 0 - identifier: warp_speed title: Warp Speed type: float description: > A convenience parameter to access the speed that was passed as part of the argument. readonly: yes - identifier: warp_resource title: Warp Producer type: string description: > A convenience parameter to access the resource that was passed as part of the argument readonly: yes mlt-7.38.0/src/modules/core/producer_tone.c000664 000000 000000 00000011146 15172202314 020570 0ustar00rootroot000000 000000 /* * producer_tone.c -- audio tone generating producer * Copyright (C) 2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int producer_get_audio(mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_producer producer = mlt_frame_pop_audio(frame); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); double fps = mlt_producer_get_fps(producer); mlt_position position = mlt_frame_get_position(frame); mlt_position length = mlt_producer_get_length(producer); // Correct the returns if necessary *format = mlt_audio_float; *frequency = *frequency <= 0 ? 48000 : *frequency; *channels = *channels <= 0 ? 2 : *channels; *samples = *samples <= 0 ? mlt_audio_calculate_frame_samples(fps, *frequency, position) : *samples; // Allocate the buffer int size = *samples * *channels * sizeof(float); *buffer = mlt_pool_alloc(size); // Fill the buffer int s = 0; int c = 0; long double first_sample = mlt_audio_calculate_samples_to_position(fps, *frequency, position); float a = mlt_properties_anim_get_double(producer_properties, "level", position, length); long double f = mlt_properties_anim_get_double(producer_properties, "frequency", position, length); long double p = mlt_properties_anim_get_double(producer_properties, "phase", position, length); p = (M_PI / 180) * p; // Convert from degrees to radians a = pow(10, a / 20.0); // Convert from dB to amplitude for (s = 0; s < *samples; s++) { long double t = (first_sample + s) / *frequency; float value = a * sin(2 * M_PI * f * t + p); for (c = 0; c < *channels; c++) { float *sample_ptr = ((float *) *buffer) + (c * *samples) + s; *sample_ptr = value; } } // Set the buffer for destruction mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set(frame_properties, "channel_layout", mlt_properties_get(frame_properties, "consumer.channel_layout")); return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL) { // Update time code on the frame mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Configure callbacks mlt_frame_push_audio(*frame, producer); mlt_frame_push_audio(*frame, producer_get_audio); } // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer this) { this->close = NULL; mlt_producer_close(this); free(this); } mlt_producer producer_tone_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Initialize the producer if (producer) { mlt_properties_set_double(producer_properties, "frequency", 1000.0); mlt_properties_set_double(producer_properties, "phase", 0.0); mlt_properties_set_double(producer_properties, "level", 0.0); // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; } return producer; } mlt-7.38.0/src/modules/core/producer_tone.yml000664 000000 000000 00000001421 15172202314 021142 0ustar00rootroot000000 000000 schema_version: 0.1 type: producer identifier: tone title: Tone version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: > Generate audio tones. parameters: - identifier: frequency title: Frequency type: float description: > The frequency of the tone. unit: Hz default: 1000.0 readonly: no mutable: yes widget: spinner - identifier: phase title: Phase type: float description: > The phase of the tone. unit: degrees default: 0.0 readonly: no mutable: yes widget: spinner - identifier: level title: Level type: float description: > The peak level of the tone. unit: dB default: 0.0 readonly: no mutable: yes widget: spinner mlt-7.38.0/src/modules/core/transition_composite.c000664 000000 000000 00000135136 15172202314 022202 0ustar00rootroot000000 000000 /* * transition_composite.c -- compose one image over another using alpha channel * Copyright (C) 2003-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "transition_composite.h" #include #include #include #include #include #include #include #include #include typedef void (*composite_line_fn)(uint8_t *dest, uint8_t *src, int width_src, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int softness, uint32_t step); /** Geometry struct. */ struct geometry_s { mlt_rect item; int nw; // normalized width int nh; // normalized height int sw; // scaled width, not including consumer scale based upon w/nw int sh; // scaled height, not including consumer scale based upon h/nh int halign; // horizontal alignment: 0=left, 1=center, 2=right int valign; // vertical alignment: 0=top, 1=middle, 2=bottom int x_src; int y_src; }; /** Parse the alignment properties into the geometry. */ static int alignment_parse(char *align) { int ret = 0; if (align == NULL) ; else if (isdigit(align[0])) ret = atoi(align); else if (align[0] == 'c' || align[0] == 'm') ret = 1; else if (align[0] == 'r' || align[0] == 'b') ret = 2; return ret; } /** Adjust position according to scaled size and alignment properties. */ static void alignment_calculate(struct geometry_s *geometry) { geometry->item.x += (geometry->item.w - geometry->sw) * geometry->halign / 2; geometry->item.y += (geometry->item.h - geometry->sh) * geometry->valign / 2; } /** Calculate the position for this frame. */ static int position_calculate(mlt_transition self, mlt_position position) { // Get the in and out position mlt_position in = mlt_transition_get_in(self); // Now do the calcs return position - in; } /** Calculate the field delta for this frame - position between two frames. */ static int get_value(mlt_properties properties, const char *preferred, const char *fallback) { int value = mlt_properties_get_int(properties, preferred); if (value == 0) value = mlt_properties_get_int(properties, fallback); return value; } /** A smoother, non-linear threshold determination function. */ static inline int32_t smoothstep(int32_t edge1, int32_t edge2, uint32_t a) { if (a < edge1) return 0; if (a >= edge2) return 0x10000; a = ((a - edge1) << 16) / (edge2 - edge1); return (((a * a) >> 16) * ((3 << 16) - (2 * a))) >> 16; } static inline int calculate_mix( uint16_t *luma, int j, int softness, int weight, int alpha, uint32_t step) { return ((luma ? smoothstep(luma[j], luma[j] + softness, step) : weight) * (alpha + 1)) >> 8; } static inline uint8_t sample_mix(uint8_t dest, uint8_t src, int mix) { return (src * mix + dest * ((1 << 16) - mix)) >> 16; } /** Composite a source line over a destination line */ #if defined(USE_SSE) && defined(ARCH_X86_64) void composite_line_yuv_sse2_simple( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight); #endif void composite_line_yuv(uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft, uint32_t step) { register int j = 0; register int mix; #if defined(USE_SSE) && defined(ARCH_X86_64) if (!luma && width > 7) { composite_line_yuv_sse2_simple(dest, src, width, alpha_b, alpha_a, weight); j = width - width % 8; dest += j * 2; src += j * 2; if (alpha_a) alpha_a += j; if (alpha_b) alpha_b += j; } #endif for (; j < width; j++) { mix = calculate_mix(luma, j, soft, weight, alpha_b ? *alpha_b : 255, step); *dest = sample_mix(*dest, *src++, mix); dest++; *dest = sample_mix(*dest, *src++, mix); dest++; if (alpha_a) { *alpha_a = (mix >> 8) | *alpha_a; alpha_a++; } if (alpha_b) alpha_b++; } } static void composite_line_yuv_or(uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft, uint32_t step) { register int j; register int mix; for (j = 0; j < width; j++) { mix = calculate_mix(luma, j, soft, weight, (alpha_b ? *alpha_b : 255) | (alpha_a ? *alpha_a : 255), step); *dest = sample_mix(*dest, *src++, mix); dest++; *dest = sample_mix(*dest, *src++, mix); dest++; if (alpha_a) *alpha_a++ = mix >> 8; if (alpha_b) alpha_b++; } } static void composite_line_yuv_and(uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft, uint32_t step) { register int j; register int mix; for (j = 0; j < width; j++) { mix = calculate_mix(luma, j, soft, weight, (alpha_b ? *alpha_b : 255) & (alpha_a ? *alpha_a : 255), step); *dest = sample_mix(*dest, *src++, mix); dest++; *dest = sample_mix(*dest, *src++, mix); dest++; if (alpha_a) *alpha_a++ = mix >> 8; if (alpha_b) alpha_b++; } } static void composite_line_yuv_xor(uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft, uint32_t step) { register int j; register int mix; for (j = 0; j < width; j++) { mix = calculate_mix(luma, j, soft, weight, (alpha_b ? *alpha_b : 255) ^ (alpha_a ? *alpha_a : 255), step); *dest = sample_mix(*dest, *src++, mix); dest++; *dest = sample_mix(*dest, *src++, mix); dest++; if (alpha_a) *alpha_a++ = mix >> 8; if (alpha_b) alpha_b++; } } struct sliced_composite_desc { int height_src; int step; uint8_t *p_dest; uint8_t *p_src; int width_src; uint8_t *alpha_b; uint8_t *alpha_a; int weight; uint16_t *p_luma; int i_softness; uint32_t luma_step; int stride_src; int stride_dest; int alpha_b_stride; int alpha_a_stride; composite_line_fn line_fn; }; static int sliced_composite_proc(int id, int idx, int jobs, void *cookie) { struct sliced_composite_desc ctx = *((struct sliced_composite_desc *) cookie); int i, ho, hs = mlt_slices_size_slice(jobs, idx, ctx.height_src, &ho); for (i = 0; i < ctx.height_src; i += ctx.step) { if (i >= ho && i < (ho + hs)) ctx.line_fn(ctx.p_dest, ctx.p_src, ctx.width_src, ctx.alpha_b, ctx.alpha_a, ctx.weight, ctx.p_luma, ctx.i_softness, ctx.luma_step); ctx.p_src += ctx.stride_src; ctx.p_dest += ctx.stride_dest; if (ctx.alpha_b) ctx.alpha_b += ctx.alpha_b_stride; if (ctx.alpha_a) ctx.alpha_a += ctx.alpha_a_stride; if (ctx.p_luma) ctx.p_luma += ctx.alpha_b_stride; } return 0; } /** Composite function. */ static int composite_yuv(uint8_t *p_dest, int width_dest, int height_dest, uint8_t *p_src, int width_src, int height_src, uint8_t *alpha_b, uint8_t *alpha_a, const struct geometry_s *geometry, int field, uint16_t *p_luma, double softness, composite_line_fn line_fn, int sliced) { int ret = 0; int i; int x_src = -geometry->x_src, y_src = -geometry->y_src; int uneven_x_src = (x_src % 2); int step = (field > -1) ? 2 : 1; int bpp = 2; int stride_src = geometry->sw * bpp; int stride_dest = width_dest * bpp; int i_softness = (1 << 16) * softness; int weight = ((1 << 16) * geometry->item.o + 50) / 100; uint32_t luma_step = (((1 << 16) - 1) * geometry->item.o + 50) / 100 * (1.0 + softness); // Adjust to consumer scale int x = rint(geometry->item.x * width_dest / geometry->nw); int y = rint(geometry->item.y * height_dest / geometry->nh); int uneven_x = (x % 2); // optimization points - no work to do if (width_src <= 0 || height_src <= 0 || y_src >= height_src || x_src >= width_src) return ret; if ((x < 0 && -x >= width_src) || (y < 0 && -y >= height_src)) return ret; // cropping affects the source width if (x_src > 0) { width_src -= x_src; // and it implies cropping if (width_src > geometry->item.w) width_src = geometry->item.w; } // cropping affects the source height if (y_src > 0) { height_src -= y_src; // and it implies cropping if (height_src > geometry->item.h) height_src = geometry->item.h; } // crop overlay off the left edge of frame if (x < 0) { x_src = -x; width_src -= x_src; x = 0; } // crop overlay beyond right edge of frame if (x + width_src > width_dest) width_src = width_dest - x; // crop overlay off the top edge of the frame if (y < 0) { y_src = -y; height_src -= y_src; y = 0; } // crop overlay below bottom edge of frame if (y + height_src > height_dest) height_src = height_dest - y; // offset pointer into overlay buffer based on cropping p_src += x_src * bpp + y_src * stride_src; // offset pointer into frame buffer based upon positive coordinates only! p_dest += x * bpp + y * stride_dest; // offset pointer into alpha channel based upon cropping if (alpha_b) alpha_b += x_src + y_src * stride_src / bpp; if (alpha_a) alpha_a += x + y * stride_dest / bpp; // offset pointer into luma channel based upon cropping if (p_luma) p_luma += x_src + y_src * stride_src / bpp; // Assuming lower field first // Special care is taken to make sure the b_frame is aligned to the correct field. // field 0 = lower field and y should be odd (y is 0-based). // field 1 = upper field and y should be even. if ((field > -1) && (y % 2 == field)) { if ((field == 1 && y < height_dest - 1) || (field == 0 && y == 0)) p_dest += stride_dest; else p_dest -= stride_dest; } // On the second field, use the other lines from b_frame if (field == 1) { p_src += stride_src; if (alpha_b) alpha_b += stride_src / bpp; if (alpha_a) alpha_a += stride_dest / bpp; height_src--; } stride_src *= step; stride_dest *= step; int alpha_b_stride = stride_src / bpp; int alpha_a_stride = stride_dest / bpp; // Align chroma of source and destination if (uneven_x != uneven_x_src) { p_src += 2; } // now do the compositing only to cropped extents if (!sliced) { for (i = 0; i < height_src; i += step) { line_fn(p_dest, p_src, width_src, alpha_b, alpha_a, weight, p_luma, i_softness, luma_step); p_src += stride_src; p_dest += stride_dest; if (alpha_b) alpha_b += alpha_b_stride; if (alpha_a) alpha_a += alpha_a_stride; if (p_luma) p_luma += alpha_b_stride; } } else { struct sliced_composite_desc s = { .height_src = height_src, .step = step, .p_dest = p_dest, .p_src = p_src, .width_src = width_src, .alpha_b = alpha_b, .alpha_a = alpha_a, .weight = weight, .p_luma = p_luma, .i_softness = i_softness, .luma_step = luma_step, .stride_src = stride_src, .stride_dest = stride_dest, .alpha_b_stride = alpha_b_stride, .alpha_a_stride = alpha_a_stride, .line_fn = line_fn, }; mlt_slices_run_normal(0, sliced_composite_proc, &s); } return ret; } /** Scale 16bit greyscale luma map using nearest neighbor. */ static inline void scale_luma(uint16_t *dest_buf, int dest_width, int dest_height, const uint16_t *src_buf, int src_width, int src_height, int invert) { register int i, j; register int x_step = (src_width << 16) / dest_width; register int y_step = (src_height << 16) / dest_height; register int x, y = 0; for (i = 0; i < dest_height; i++) { const uint16_t *src = src_buf + (y >> 16) * src_width; x = 0; for (j = 0; j < dest_width; j++) { *dest_buf++ = src[x >> 16] ^ invert; x += x_step; } y += y_step; } } static uint16_t *get_luma(mlt_transition self, mlt_properties properties, int width, int height) { // The cached luma map information int luma_width = mlt_properties_get_int(properties, "_luma.width"); int luma_height = mlt_properties_get_int(properties, "_luma.height"); uint16_t *luma_bitmap = mlt_properties_get_data(properties, "_luma.bitmap", NULL); int invert = mlt_properties_get_int(properties, "luma_invert"); // If the filename property changed, reload the map char *resource = mlt_properties_get(properties, "luma"); char *orig_resource = resource; mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(self)); char temp[PATH_MAX]; if (luma_width == 0 || luma_height == 0) { luma_width = width; luma_height = height; } if (resource && resource[0] && strchr(resource, '%')) { snprintf(temp, sizeof(temp), "%s/lumas/%s/%s", mlt_environment("MLT_DATA"), mlt_profile_lumas_dir(profile), strchr(resource, '%') + 1); FILE *test = mlt_fopen(temp, "r"); if (!test) { strcat(temp, ".png"); test = mlt_fopen(temp, "r"); } if (test) { fclose(test); resource = temp; } } if (resource && resource[0]) { char *old_luma = mlt_properties_get(properties, "_luma"); int old_invert = mlt_properties_get_int(properties, "_luma_invert"); if (invert != old_invert || (old_luma && old_luma[0] && strcmp(resource, old_luma))) { mlt_properties_set_data(properties, "_luma.orig_bitmap", NULL, 0, NULL, NULL); luma_bitmap = NULL; } } else { char *old_luma = mlt_properties_get(properties, "_luma"); if (old_luma && old_luma[0]) { mlt_properties_set_data(properties, "_luma.orig_bitmap", NULL, 0, NULL, NULL); mlt_properties_set_data(properties, "_luma.bitmap", NULL, 0, NULL, NULL); luma_bitmap = NULL; mlt_properties_set(properties, "_luma", NULL); } } if (resource && resource[0] && (luma_bitmap == NULL || luma_width != width || luma_height != height)) { uint16_t *orig_bitmap = mlt_properties_get_data(properties, "_luma.orig_bitmap", NULL); luma_width = mlt_properties_get_int(properties, "_luma.orig_width"); luma_height = mlt_properties_get_int(properties, "_luma.orig_height"); // Load the original luma once if (orig_bitmap == NULL) { char *extension = strrchr(resource, '.'); // See if it is a PGM int lumaLoaded = 0; if (extension != NULL && strcmp(extension, ".pgm") == 0) { // Load from PGM if (mlt_luma_map_from_pgm(resource, &orig_bitmap, &luma_width, &luma_height)) { // Failed to read file; generate it. mlt_luma_map luma = mlt_luma_map_new(orig_resource); if (profile) { luma->w = profile->width; luma->h = profile->height; } luma_bitmap = mlt_luma_map_render(luma); luma_width = luma->w; luma_height = luma->h; free(luma); } if (luma_width > 0 && luma_height > 0) { // Remember the original size for subsequent scaling mlt_properties_set_data(properties, "_luma.orig_bitmap", orig_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL); mlt_properties_set_int(properties, "_luma.orig_width", luma_width); mlt_properties_set_int(properties, "_luma.orig_height", luma_height); lumaLoaded = 1; } } if (!lumaLoaded) { // Get the factory producer service char *factory = mlt_properties_get(properties, "factory"); // Create the producer mlt_producer producer = mlt_factory_producer(profile, factory, resource); // If we have one if (producer != NULL) { // Get the producer properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Ensure that we loop mlt_properties_set(producer_properties, "eof", "loop"); // Now pass all producer. properties on the transition down mlt_properties_pass(producer_properties, properties, "luma."); // We will get the alpha frame from the producer mlt_frame luma_frame = NULL; // Get the luma frame if (mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &luma_frame, 0) == 0) { uint8_t *luma_image; mlt_image_format luma_format = mlt_image_yuv422; // Get image from the luma producer mlt_properties_set(MLT_FRAME_PROPERTIES(luma_frame), "consumer.rescale", "none"); mlt_frame_get_image(luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0); // Generate the luma map if (luma_image != NULL && luma_format == mlt_image_yuv422) mlt_luma_map_from_yuv422(luma_image, &orig_bitmap, luma_width, luma_height); // Remember the original size for subsequent scaling mlt_properties_set_data(properties, "_luma.orig_bitmap", orig_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL); mlt_properties_set_int(properties, "_luma.orig_width", luma_width); mlt_properties_set_int(properties, "_luma.orig_height", luma_height); // Cleanup the luma frame mlt_frame_close(luma_frame); } // Cleanup the luma producer mlt_producer_close(producer); } else { luma_width = 0; luma_height = 0; } } } if (orig_bitmap && luma_width > 0 && luma_height > 0) { // Scale luma map luma_bitmap = mlt_pool_alloc(width * height * sizeof(uint16_t)); scale_luma(luma_bitmap, width, height, orig_bitmap, luma_width, luma_height, invert * ((1 << 16) - 1)); // Remember the scaled luma size to prevent unnecessary scaling mlt_properties_set_int(properties, "_luma.width", width); mlt_properties_set_int(properties, "_luma.height", height); mlt_properties_set_data(properties, "_luma.bitmap", luma_bitmap, width * height * 2, mlt_pool_release, NULL); mlt_properties_set(properties, "_luma", resource); mlt_properties_set_int(properties, "_luma_invert", invert); } } return luma_bitmap; } /** Get the properly sized image from b_frame. */ static int get_b_frame_image(mlt_transition self, mlt_frame b_frame, uint8_t **image, int *width, int *height, struct geometry_s *geometry) { int error = 0; mlt_image_format format = mlt_image_yuv422; // Get the properties objects mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); uint8_t resize_alpha = mlt_properties_get_int(b_props, "resize_alpha"); double output_ar = mlt_profile_sar(mlt_service_profile(MLT_TRANSITION_SERVICE(self))); // Do not scale if we are cropping - the compositing rectangle can crop the b image // TODO: Use the animatable w and h of the crop geometry to scale independently of crop rectangle if (mlt_properties_get(properties, "crop")) { int real_width = get_value(b_props, "meta.media.width", "width"); int real_height = get_value(b_props, "meta.media.height", "height"); double input_ar = mlt_properties_get_double(b_props, "aspect_ratio"); int scaled_width = rint((input_ar == 0.0 ? output_ar : input_ar) / output_ar * real_width); int scaled_height = real_height; geometry->sw = scaled_width; geometry->sh = scaled_height; } else if (mlt_properties_get_int(properties, "crop_to_fill")) { int real_width = get_value(b_props, "meta.media.width", "width"); int real_height = get_value(b_props, "meta.media.height", "height"); double input_ar = mlt_properties_get_double(b_props, "aspect_ratio"); int scaled_width = rint((input_ar == 0.0 ? output_ar : input_ar) / output_ar * real_width); int scaled_height = real_height; int normalized_width = geometry->item.w; int normalized_height = geometry->item.h; if (scaled_height > 0 && scaled_width * normalized_height / scaled_height >= normalized_width) { // crop left/right edges scaled_width = rint(scaled_width * normalized_height / scaled_height); scaled_height = normalized_height; } else if (scaled_width > 0) { // crop top/bottom edges scaled_height = rint(scaled_height * normalized_width / scaled_width); scaled_width = normalized_width; } geometry->sw = scaled_width; geometry->sh = scaled_height; } // Normalize aspect ratios and scale preserving aspect ratio else if (mlt_properties_get_int(properties, "aligned") && mlt_properties_get_int(properties, "distort") == 0 && mlt_properties_get_int(b_props, "distort") == 0) { // Adjust b_frame pixel aspect int normalized_width = geometry->item.w; int normalized_height = geometry->item.h; int real_width = get_value(b_props, "meta.media.width", "width"); int real_height = get_value(b_props, "meta.media.height", "height"); double input_ar = mlt_properties_get_double(b_props, "aspect_ratio"); int scaled_width = rint((input_ar == 0.0 ? output_ar : input_ar) / output_ar * real_width); int scaled_height = real_height; // fprintf(stderr, "%s: scaled %dx%d norm %dx%d real %dx%d output_ar %f\n", __FILE__, // scaled_width, scaled_height, normalized_width, normalized_height, real_width, real_height, // output_ar); // Now ensure that our images fit in the normalized frame if (scaled_width > normalized_width) { scaled_height = rint(scaled_height * normalized_width / scaled_width); scaled_width = normalized_width; } if (scaled_height > normalized_height) { scaled_width = rint(scaled_width * normalized_height / scaled_height); scaled_height = normalized_height; } // Honour the fill request - this will scale the image to fill width or height while maintaining a/r // ????: Shouldn't this be the default behaviour? if (mlt_properties_get_int(properties, "fill") && scaled_width > 0 && scaled_height > 0) { if (scaled_height < normalized_height && scaled_width * normalized_height / scaled_height <= normalized_width) { scaled_width = rint(scaled_width * normalized_height / scaled_height); scaled_height = normalized_height; } else if (scaled_width < normalized_width && scaled_height * normalized_width / scaled_width < normalized_height) { scaled_height = rint(scaled_height * normalized_width / scaled_width); scaled_width = normalized_width; } } // Save the new scaled dimensions geometry->sw = scaled_width; geometry->sh = scaled_height; } else { geometry->sw = geometry->item.w; geometry->sh = geometry->item.h; } // We want to ensure that we bypass resize now... if (resize_alpha == 0) mlt_properties_set_int(b_props, "distort", mlt_properties_get_int(properties, "distort")); // If we're not aligned, we want a non-transparent background if (mlt_properties_get_int(properties, "aligned") == 0) mlt_properties_set_int(b_props, "resize_alpha", 255); // Take into consideration alignment for optimisation (titles are a special case) if (!mlt_properties_get_int(properties, "titles") && mlt_properties_get(properties, "crop") == NULL) alignment_calculate(geometry); // Adjust to consumer scale *width = rint(geometry->sw * *width / geometry->nw); *width -= *width % 2; // coerce to even width for yuv422 *height = rint(geometry->sh * *height / geometry->nh); // fprintf(stderr, "%s: scaled %dx%d norm %dx%d resize %dx%d\n", __FILE__, // geometry->sw, geometry->sh, geometry->nw, geometry->nh, *width, *height); error = mlt_frame_get_image(b_frame, image, &format, width, height, 1); // composite_yuv uses geometry->sw to determine source stride, which // should equal the image width if not using crop property. if (!mlt_properties_get(properties, "crop")) geometry->sw = *width; // Set the frame back mlt_properties_set_int(b_props, "resize_alpha", resize_alpha); return !error && image; } static void crop_calculate(mlt_transition self, struct geometry_s *result, double position) { // Get the properties from the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); // Initialize panning info result->x_src = 0; result->y_src = 0; if (mlt_properties_get(properties, "crop")) { mlt_position length = mlt_transition_get_length(self); double cycle = mlt_properties_get_double(properties, "cycle"); // Allow a geometry repeat cycle if (cycle >= 1) length = cycle; else if (cycle > 0) length *= cycle; mlt_rect crop = mlt_properties_anim_get_rect(properties, "crop", position, length); // Repeat processing mlt_animation animation = mlt_properties_get_animation(properties, "crop"); int anim_length = mlt_animation_get_length(animation); int mirror_off = mlt_properties_get_int(properties, "mirror_off"); int repeat_off = mlt_properties_get_int(properties, "repeat_off"); if (!repeat_off && position >= anim_length && anim_length != 0) { int section = position / anim_length; position -= section * anim_length; if (!mirror_off && section % 2 == 1) position = anim_length - position; } // Compute the pan crop = mlt_properties_anim_get_rect(properties, "crop", position, length); if (mlt_properties_get(properties, "crop") && strchr(mlt_properties_get(properties, "crop"), '%')) { mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(self)); crop.x *= profile->width; crop.y *= profile->height; } result->x_src = rint(crop.x); result->y_src = rint(crop.y); } } static void composite_calculate(mlt_transition self, struct geometry_s *result, double position) { // Get the properties from the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); // Obtain the normalized width and height from the a_frame mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(self)); int normalized_width = profile->width; int normalized_height = profile->height; // Now parse the geometries mlt_position length = mlt_transition_get_length(self); double cycle = mlt_properties_get_double(properties, "cycle"); // Allow a geometry repeat cycle if (cycle >= 1) length = cycle; else if (cycle > 0) length *= cycle; result->item = mlt_properties_anim_get_rect(properties, "geometry", position, length); // Repeat processing mlt_animation animation = mlt_properties_get_animation(properties, "geometry"); int anim_length = mlt_animation_get_length(animation); int mirror_off = mlt_properties_get_int(properties, "mirror_off"); int repeat_off = mlt_properties_get_int(properties, "repeat_off"); if (!repeat_off && position >= anim_length && anim_length != 0) { int section = position / anim_length; position -= section * anim_length; if (!mirror_off && section % 2 == 1) position = anim_length - position; } result->item = mlt_properties_anim_get_rect(properties, "geometry", position, length); if (mlt_properties_get(properties, "geometry") && strchr(mlt_properties_get(properties, "geometry"), '%')) { result->item.x *= normalized_width; result->item.y *= normalized_height; result->item.w *= normalized_width; result->item.h *= normalized_height; } result->item.o = 100.0 * (result->item.o == DBL_MIN ? 1.0 : MIN(result->item.o, 1.0)); // Assign normalized info result->nw = normalized_width; result->nh = normalized_height; // Now parse the alignment result->halign = alignment_parse(mlt_properties_get(properties, "halign")); result->valign = alignment_parse(mlt_properties_get(properties, "valign")); crop_calculate(self, result, position); } /** Get the image. */ static int transition_get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the b frame from the stack mlt_frame b_frame = mlt_frame_pop_frame(a_frame); // Get the transition from the a frame mlt_transition self = mlt_frame_pop_service(a_frame); // Get in and out double position = mlt_deque_pop_back_double(MLT_FRAME_IMAGE_STACK(a_frame)); int out = mlt_frame_pop_service_int(a_frame); int in = mlt_frame_pop_service_int(a_frame); // Get the properties from the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); // TODO: clean up always_active behaviour if (mlt_properties_get_int(properties, "always_active")) { mlt_events_block(properties, properties); mlt_properties_set_int(properties, "in", in); mlt_properties_set_int(properties, "out", out); mlt_events_unblock(properties, properties); } if (mlt_properties_get_int(properties, "invert")) { mlt_frame c = a_frame; a_frame = b_frame; b_frame = c; } // This compositer is yuv422 only *format = mlt_image_yuv422; if (b_frame != NULL) { // Get the properties of the a frame mlt_properties a_props = MLT_FRAME_PROPERTIES(a_frame); // Get the properties of the b frame mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); // Structures for geometry struct geometry_s result; // Calculate the position double delta = mlt_transition_get_progress_delta(self, a_frame); mlt_position length = mlt_transition_get_length(self); // Get the image from the b frame uint8_t *image_b = NULL; mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(self)); int width_b = *width > 0 ? *width : profile->width; int height_b = *height > 0 ? *height : profile->height; // Vars for alphas uint8_t *alpha_a = NULL; uint8_t *alpha_b = NULL; // Do the calculation // NB: Locks needed here since the properties are being modified mlt_service_lock(MLT_TRANSITION_SERVICE(self)); composite_calculate(self, &result, position); mlt_service_unlock(MLT_TRANSITION_SERVICE(self)); // Manual option to deinterlace if (mlt_properties_get_int(properties, "deinterlace")) { mlt_properties_set_int(a_props, "consumer.progressive", 1); mlt_properties_set_int(b_props, "consumer.progressive", 1); } // TODO: Dangerous/temporary optimisation - if nothing to do, then do nothing if (mlt_properties_get_int(properties, "no_alpha") && result.item.x == 0 && result.item.y == 0 && result.item.w == *width && result.item.h == *height && result.item.o == 100) { mlt_frame_get_image(b_frame, image, format, width, height, 1); if (!mlt_frame_is_test_card(a_frame)) mlt_frame_replace_image(a_frame, *image, *format, *width, *height); return 0; } if (a_frame == b_frame) { double aspect_ratio = mlt_frame_get_aspect_ratio(b_frame); get_b_frame_image(self, b_frame, &image_b, &width_b, &height_b, &result); alpha_b = mlt_frame_get_alpha(b_frame); mlt_properties_set_double(a_props, "aspect_ratio", aspect_ratio); } // Get the image from the a frame mlt_frame_get_image(a_frame, image, format, width, height, 1); alpha_a = mlt_frame_get_alpha(a_frame); // Optimisation - no compositing required if (result.item.o == 0 || (result.item.w == 0 && result.item.h == 0)) return 0; // Need to keep the width/height of the a_frame on the b_frame for titling if (mlt_properties_get(a_props, "dest_width") == NULL) { mlt_properties_set_int(a_props, "dest_width", *width); mlt_properties_set_int(a_props, "dest_height", *height); mlt_properties_set_int(b_props, "dest_width", *width); mlt_properties_set_int(b_props, "dest_height", *height); } else { mlt_properties_set_int(b_props, "dest_width", mlt_properties_get_int(a_props, "dest_width")); mlt_properties_set_int(b_props, "dest_height", mlt_properties_get_int(a_props, "dest_height")); } // Special case for titling... if (mlt_properties_get_int(properties, "titles")) { if (mlt_properties_get(b_props, "consumer.rescale") == NULL) mlt_properties_set(b_props, "consumer.rescale", "hyper"); width_b = mlt_properties_get_int(a_props, "dest_width"); height_b = mlt_properties_get_int(a_props, "dest_height"); } if (*image != image_b && (image_b || get_b_frame_image(self, b_frame, &image_b, &width_b, &height_b, &result))) { int progressive = mlt_properties_get_int(a_props, "consumer.progressive") || mlt_properties_get_int(properties, "progressive"); int top_field_first = mlt_properties_get_int(a_props, "top_field_first"); int field; int sliced = mlt_properties_get_int(properties, "sliced_composite"); double luma_softness = mlt_properties_get_double(properties, "softness"); mlt_service_lock(MLT_TRANSITION_SERVICE(self)); uint16_t *luma_bitmap = get_luma(self, properties, width_b, height_b); mlt_service_unlock(MLT_TRANSITION_SERVICE(self)); char *operator= mlt_properties_get(properties, "operator"); alpha_b = alpha_b == NULL ? mlt_frame_get_alpha(b_frame) : alpha_b; composite_line_fn line_fn = composite_line_yuv; // Replacement and override if (operator!= NULL) { if (!strcmp(operator, "or")) line_fn = composite_line_yuv_or; if (!strcmp(operator, "and")) line_fn = composite_line_yuv_and; if (!strcmp(operator, "xor")) line_fn = composite_line_yuv_xor; } // Allow the user to completely obliterate the alpha channels from both frames if (mlt_properties_get(properties, "alpha_a") && alpha_a) memset(alpha_a, mlt_properties_get_int(properties, "alpha_a"), *width * *height); if (mlt_properties_get(properties, "alpha_b") && alpha_b) memset(alpha_b, mlt_properties_get_int(properties, "alpha_b"), width_b * height_b); for (field = 0; field < (progressive ? 1 : 2); field++) { // Assume lower field (0) first double field_position = position + field * delta * length; int field_id = progressive ? -1 : (top_field_first ? (1 - field) : field); // Do the calculation if we need to // NB: Locks needed here since the properties are being modified mlt_service_lock(MLT_TRANSITION_SERVICE(self)); composite_calculate(self, &result, field_position); mlt_service_unlock(MLT_TRANSITION_SERVICE(self)); if (mlt_properties_get_int(properties, "titles")) { result.item.w = rint(*width * (result.item.w / result.nw)); result.nw = result.item.w; result.item.h = rint(*height * (result.item.h / result.nh)); result.nh = *height; result.sw = width_b; result.sh = height_b; } // Enforce cropping if (mlt_properties_get(properties, "crop")) { if (result.x_src == 0) width_b = width_b > result.item.w ? result.item.w : width_b; if (result.y_src == 0) height_b = height_b > result.item.h ? result.item.h : height_b; } else if (mlt_properties_get_int(properties, "crop_to_fill")) { if (result.item.w < result.sw) result.x_src = rint((result.item.w - result.sw) * result.halign / 2); if (result.item.h < result.sh) result.y_src = rint((result.item.h - result.sh) * result.valign / 2); // same as crop if (result.x_src == 0) width_b = width_b > result.item.w ? result.item.w : width_b; if (result.y_src == 0) height_b = height_b > result.item.h ? result.item.h : height_b; } else { // Otherwise, align alignment_calculate(&result); } // Composite the b_frame on the a_frame mlt_log_timings_begin() composite_yuv(*image, *width, *height, image_b, width_b, height_b, alpha_b, alpha_a, &result, field_id, luma_bitmap, luma_softness, line_fn, sliced); mlt_log_timings_end(NULL, "composite_yuv") } } } else { mlt_frame_get_image(a_frame, image, format, width, height, 1); } return 0; } /** Composition transition processing. */ static mlt_frame composite_process(mlt_transition self, mlt_frame a_frame, mlt_frame b_frame) { // UGH - this is a TODO - find a more reliable means of obtaining in/out for the always_active case if (mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(self), "always_active") == 0) { mlt_frame_push_service_int(a_frame, mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(self), "in")); mlt_frame_push_service_int(a_frame, mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(self), "out")); mlt_deque_push_back_double(MLT_FRAME_IMAGE_STACK(a_frame), position_calculate(self, mlt_frame_get_position(a_frame))); } else { mlt_properties props = mlt_properties_get_data(MLT_FRAME_PROPERTIES(b_frame), "_producer", NULL); mlt_frame_push_service_int(a_frame, mlt_properties_get_int(props, "in")); mlt_frame_push_service_int(a_frame, mlt_properties_get_int(props, "out")); mlt_deque_push_back_double(MLT_FRAME_IMAGE_STACK(a_frame), mlt_properties_get_int(props, "_frame") - mlt_properties_get_int(props, "in")); } mlt_frame_push_service(a_frame, self); mlt_frame_push_frame(a_frame, b_frame); mlt_frame_push_get_image(a_frame, transition_get_image); return a_frame; } /** Constructor for the filter. */ mlt_transition transition_composite_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_transition self = calloc(1, sizeof(struct mlt_transition_s)); if (self != NULL && mlt_transition_init(self, NULL) == 0) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(self); self->process = composite_process; // Default starting motion and zoom mlt_properties_set(properties, "geometry", arg != NULL ? arg : "0/0:100%x100%"); // Default factory mlt_properties_set(properties, "factory", mlt_environment("MLT_PRODUCER")); // Use alignment (and hence alpha of b frame) mlt_properties_set_int(properties, "aligned", 1); // Default to progressive rendering mlt_properties_set_int(properties, "progressive", 1); // Inform apps and framework that this is a video only transition mlt_properties_set_int(properties, "_transition_type", 1); } return self; } mlt-7.38.0/src/modules/core/transition_composite.h000664 000000 000000 00000003176 15172202314 022205 0ustar00rootroot000000 000000 /* * transition_composite.h -- compose one image over another using alpha channel * Copyright (C) 2003-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _TRANSITION_COMPOSITE_H_ #define _TRANSITION_COMPOSITE_H_ #include extern mlt_transition transition_composite_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern void composite_line_yuv(uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft, uint32_t step); #endif mlt-7.38.0/src/modules/core/transition_composite.yml000664 000000 000000 00000010126 15172202314 022550 0ustar00rootroot000000 000000 schema_version: 7.0 type: transition identifier: composite title: Composite (*DEPRECATED*) version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > A key-framable alpha-channel compositor for two frames. notes: > Deprecated: use "affine" or "qtblend" instead. Performs dissolves and luma wipes in addition to alpha compositing. By default, the aspect ratio of the B frame is respected and the size portion of the geometry specification simply defines a bounding rectangle. This performs field-based rendering unless the A frame property "progressive" or "consumer_progressive" or the transition property "progressive" is set to 1. bugs: - Assumes lower field first during field rendering. parameters: - identifier: factory title: Factory type: string description: > The name of a factory service used as a non-PGM producer loader. default: loader - identifier: geometry argument: yes title: Geometry type: rect description: A possibly keyframed rectangle mutable: yes animation: yes - identifier: progressive title: Progressive description: > Enable or disable field-based rendering. type: integer minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: distort title: Allow distorted scaling description: > When set, causes the B frame image to fill the WxH completely with no regard to B's aspect ratio. type: integer default: 0 minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: crop_to_fill title: Fill by cropping description: > When set, causes the B frame image to fill the WxH completely by cropping edges in order to maintain B's aspect ratio. type: integer default: 0 minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: halign title: Horizontal alignment description: > When not distorting, set the horizontal alignment of B within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > When not distorting, set the vertical alignment of B within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo - identifier: luma title: Luma map description: > The luma map file name. type: string mutable: yes widget: fileopen - identifier: softness title: Softness description: > Only when using a luma map, how soft to make the edges between A and B. type: float default: 0.0 minimum: 0.0 maximum: 1.0 mutable: yes - identifier: luma.* title: Luma producer description: > Properties may be set on the encapsulated producer. Any property starting with "luma." is passed to the non-PGM luma producer. readonly: no mutable: yes - identifier: sliced_composite title: Use sliced compositing description: > Enabling this option will start sliced processing of picture compositing, i.e. some parts of picture processed in different thread type: boolean default: 0 mutable: yes widget: checkbox - identifier: fill title: Fill geometry description: > Determines whether the image will be scaled up to fill the geometry. Otherwise, if the B frame image fits within the geometry, it will not be scaled. If 0, and the B frame image exceeds the geometry, then it is scaled down to fit within the geometry. type: boolean default: 1 mutable: yes widget: checkbox - identifier: invert title: Invert description: Whether to swap the A and B clips type: boolean default: 1 mutable: yes widget: checkbox - identifier: crop title: Crop Rectangle type: rect description: Defines a cropping rectangle for the second input mutable: yes animation: yes mlt-7.38.0/src/modules/core/transition_luma.c000664 000000 000000 00000125032 15172202314 021130 0ustar00rootroot000000 000000 /* * transition_luma.c -- a generic dissolve/wipe processor * Copyright (C) 2003-2025 Meltytech, LLC * * Adapted from Kino Plugin Timfx, which is * Copyright (C) 2002 Timothy M. Shead * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "transition_composite.h" #include #include #include #include #include #include static inline int is_opaque(uint8_t *alpha_channel, int width, int height) { int n = width * height + 1; while (--n) if (*alpha_channel++ != 0xff) return 0; return 1; } static inline float calculate_mix(float weight, float alpha) { return weight * alpha / 255.f; } static inline uint8_t sample_mix(uint8_t dest, uint8_t src, float mix) { return src * mix + dest * (1.f - mix); } static inline float calculate_mix_16(float weight, float alpha) { return weight * alpha / 65535.f; } static inline uint16_t sample_mix_16(uint16_t dest, uint16_t src, float mix) { return src * mix + dest * (1.f - mix); } static void composite_line_yuv_float( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, float weight) { register int j = 0; float mix_a, mix_b; for (; j < width; j++) { mix_a = calculate_mix(1.0f - weight, alpha_a ? *alpha_a : 255); mix_b = calculate_mix(weight, alpha_b ? *alpha_b : 255); if (alpha_a) { float mix2 = mix_b + mix_a; *alpha_a = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } *dest = sample_mix(*dest, *src++, mix_b); dest++; *dest = sample_mix(*dest, *src++, mix_b); dest++; if (alpha_a) alpha_a++; if (alpha_b) alpha_b++; } } struct dissolve_slice_context { uint8_t *dst_image; uint8_t *src_image; uint8_t *dst_alpha; uint8_t *src_alpha; int width; int height; float weight; }; static int dissolve_slice_yuv422(int id, int index, int count, void *context) { struct dissolve_slice_context ctx = *((struct dissolve_slice_context *) context); int stride = ctx.width * 2; int slice_start, slice_height = mlt_slices_size_slice(count, index, ctx.height, &slice_start); int i; ctx.dst_image += slice_start * stride; ctx.src_image += slice_start * stride; if (ctx.dst_alpha) ctx.dst_alpha += slice_start * ctx.width; if (ctx.src_alpha) ctx.src_alpha += slice_start * ctx.width; for (i = 0; i < slice_height; i++) { composite_line_yuv_float(ctx.dst_image, ctx.src_image, ctx.width, ctx.src_alpha, ctx.dst_alpha, ctx.weight); ctx.dst_image += stride; ctx.src_image += stride; if (ctx.dst_alpha) ctx.dst_alpha += ctx.width; if (ctx.src_alpha) ctx.src_alpha += ctx.width; } return 0; } static inline int dissolve_yuv422(mlt_frame frame, mlt_frame that, float weight, int width, int height, int threads, int alpha_over, int fix_background_alpha) { int ret = 0; int i = height + 1; int width_src = width, height_src = height; mlt_image_format format = (fix_background_alpha && frame->convert_image) ? mlt_image_rgba : mlt_image_yuv422; uint8_t *p_src, *p_dest; uint8_t *alpha_src; uint8_t *alpha_dst; int mix = weight * (1 << 16); mlt_frame_get_image(frame, &p_dest, &format, &width, &height, 1); if (fix_background_alpha && frame->convert_image) frame->convert_image(frame, &p_dest, &format, mlt_image_yuv422); alpha_dst = mlt_frame_get_alpha(frame); if (fix_background_alpha && that->convert_image) format = mlt_image_rgba; mlt_frame_get_image(that, &p_src, &format, &width_src, &height_src, 0); if (that->convert_image) that->convert_image(that, &p_src, &format, mlt_image_yuv422); alpha_src = mlt_frame_get_alpha(that); int is_translucent = (alpha_dst && !is_opaque(alpha_dst, width, height)) || (alpha_src && !is_opaque(alpha_src, width_src, height_src)); // Pick the lesser of two evils ;-) width_src = width_src > width ? width : width_src; height_src = height_src > height ? height : height_src; if (is_translucent && alpha_over) { struct dissolve_slice_context context = {.dst_image = p_dest, .src_image = p_src, .dst_alpha = alpha_dst, .src_alpha = alpha_src, .width = width_src, .height = height_src, .weight = weight}; mlt_slices_run_normal(threads, dissolve_slice_yuv422, &context); } else { while (--i) { composite_line_yuv(p_dest, p_src, width_src, alpha_src, alpha_dst, mix, NULL, 0, 0); p_src += width_src << 1; p_dest += width << 1; if (alpha_src) alpha_src += width_src; if (alpha_dst) alpha_dst += width; } } return ret; } static int dissolve_slice_rgba32(int id, int index, int count, void *context) { struct dissolve_slice_context ctx = *((struct dissolve_slice_context *) context); int slice_start, slice_height = mlt_slices_size_slice(count, index, ctx.height, &slice_start); int stride = ctx.width * 4; uint8_t *dst = ctx.dst_image + slice_start * stride; uint8_t *src = ctx.src_image + slice_start * stride; for (int i = 0; i < slice_height; i++) { for (int j = 0; j < ctx.width; j++) { float mix_a = calculate_mix(1.0f - ctx.weight, dst[3]); float mix_b = calculate_mix(ctx.weight, src[3]); float mix2 = mix_b + mix_a; if (mix2 != 0.f) mix_b /= mix2; dst[0] = sample_mix(dst[0], src[0], mix_b); dst[1] = sample_mix(dst[1], src[1], mix_b); dst[2] = sample_mix(dst[2], src[2], mix_b); dst[3] = 255 * mix2; dst += 4; src += 4; } } return 0; } static inline int dissolve_rgba32( mlt_frame frame, mlt_frame that, float weight, int width, int height, int threads) { struct mlt_image_s dimg; struct mlt_image_s simg; mlt_image_set_values(&dimg, NULL, mlt_image_rgba, width, height); mlt_frame_get_image(frame, (uint8_t **) &dimg.data, &dimg.format, &dimg.width, &dimg.height, 1); mlt_image_set_values(&dimg, dimg.data, dimg.format, dimg.width, dimg.height); mlt_image_set_values(&simg, NULL, mlt_image_rgba, width, height); mlt_frame_get_image(that, (uint8_t **) &simg.data, &simg.format, &simg.width, &simg.height, 0); mlt_image_set_values(&simg, simg.data, simg.format, simg.width, simg.height); if (simg.width != dimg.width || simg.height != dimg.height) { return 1; } struct dissolve_slice_context context = {.dst_image = dimg.data, .src_image = simg.data, .width = dimg.width, .height = dimg.height, .weight = weight}; mlt_slices_run_normal(threads, dissolve_slice_rgba32, &context); return 0; } static int dissolve_slice_rgba64(int id, int index, int count, void *context) { struct dissolve_slice_context ctx = *((struct dissolve_slice_context *) context); int slice_start, slice_height = mlt_slices_size_slice(count, index, ctx.height, &slice_start); int stride = ctx.width * 4; uint16_t *dst = (uint16_t *) ctx.dst_image + slice_start * stride; uint16_t *src = (uint16_t *) ctx.src_image + slice_start * stride; for (int i = 0; i < slice_height; i++) { for (int j = 0; j < ctx.width; j++) { float mix_a = calculate_mix_16(1.0f - ctx.weight, dst[3]); float mix_b = calculate_mix_16(ctx.weight, src[3]); float mix2 = mix_b + mix_a; if (mix2 != 0.f) mix_b /= mix2; dst[0] = sample_mix_16(dst[0], src[0], mix_b); dst[1] = sample_mix_16(dst[1], src[1], mix_b); dst[2] = sample_mix_16(dst[2], src[2], mix_b); dst[3] = 255 * mix2; dst += 4; src += 4; } } return 0; } static inline int dissolve_rgba64( mlt_frame frame, mlt_frame that, float weight, int width, int height, int threads) { struct mlt_image_s dimg; struct mlt_image_s simg; mlt_image_set_values(&dimg, NULL, mlt_image_rgba64, width, height); mlt_frame_get_image(frame, (uint8_t **) &dimg.data, &dimg.format, &dimg.width, &dimg.height, 1); mlt_image_set_values(&dimg, dimg.data, dimg.format, dimg.width, dimg.height); mlt_image_set_values(&simg, NULL, mlt_image_rgba64, width, height); mlt_frame_get_image(that, (uint8_t **) &simg.data, &simg.format, &simg.width, &simg.height, 0); mlt_image_set_values(&simg, simg.data, simg.format, simg.width, simg.height); if (simg.width != dimg.width || simg.height != dimg.height) { return 1; } struct dissolve_slice_context context = {.dst_image = dimg.data, .src_image = simg.data, .width = dimg.width, .height = dimg.height, .weight = weight}; mlt_slices_run_normal(threads, dissolve_slice_rgba64, &context); return 0; } /** A smoother, non-linear threshold determination function. */ static inline int32_t smoothstep(int32_t edge1, int32_t edge2, uint32_t a) { if (a < edge1) return 0; if (a >= edge2) return 0x10000; a = ((a - edge1) << 16) / (edge2 - edge1); return (((a * a) >> 16) * ((3 << 16) - (2 * a))) >> 16; } static float smoothstep_float(float edge1, float edge2, float a) { if (a < edge1) return 0.f; if (a >= edge2) return 1.f; a = (a - edge1) / (edge2 - edge1); return (a * a) * (3 - (2 * a)); } /** powerful stuff \param field_order -1 = progressive, 0 = lower field first, 1 = top field first */ static void luma_composite_yuv422(mlt_frame a_frame, mlt_frame b_frame, int luma_width, int luma_height, uint16_t *luma_bitmap, float pos, float frame_delta, float softness, int field_order, int *width, int *height, int invert, int fix_background_alpha) { int width_src = *width, height_src = *height; int width_dest = *width, height_dest = *height; mlt_image_format format_src = (fix_background_alpha && a_frame->convert_image) ? mlt_image_rgba : mlt_image_yuv422; mlt_image_format format_dest = (fix_background_alpha && b_frame->convert_image) ? mlt_image_rgba : mlt_image_yuv422; uint8_t *p_src, *p_dest; uint8_t *alpha_src, *alpha_dest; int i, j; int stride_src; int stride_dest; if (mlt_properties_get(&a_frame->parent, "distort")) mlt_properties_set(&b_frame->parent, "distort", mlt_properties_get(&a_frame->parent, "distort")); mlt_frame_get_image(a_frame, &p_dest, &format_dest, &width_dest, &height_dest, 1); if (fix_background_alpha && a_frame->convert_image) a_frame->convert_image(a_frame, &p_dest, &format_dest, mlt_image_yuv422); alpha_dest = mlt_frame_get_alpha(a_frame); mlt_frame_get_image(b_frame, &p_src, &format_src, &width_src, &height_src, 0); if (fix_background_alpha && b_frame->convert_image) b_frame->convert_image(b_frame, &p_src, &format_src, mlt_image_yuv422); alpha_src = mlt_frame_get_alpha(b_frame); if (*width == 0 || *height == 0) return; int is_translucent = (alpha_dest && !is_opaque(alpha_dest, width_dest, height_dest)) || (alpha_src && !is_opaque(alpha_src, width_src, height_src)); // Pick the lesser of two evils ;-) width_src = width_src > width_dest ? width_dest : width_src; height_src = height_src > height_dest ? height_dest : height_src; stride_src = width_src * 2; stride_dest = width_dest * 2; // Offset the position based on which field we're looking at ... float field_pos[2]; field_pos[0] = (pos + ((field_order == 0 ? 1 : 0) * frame_delta * 0.5f)) * (1.f + softness); field_pos[1] = (pos + ((field_order == 0 ? 0 : 1) * frame_delta * 0.5f)) * (1.f + softness); register uint8_t *p; register uint8_t *q; uint16_t *l; int32_t x_diff = (luma_width << 16) / *width; int32_t y_diff = (luma_height << 16) / *height; int32_t x_offset = 0; int32_t y_offset = 0; uint8_t *p_row; uint8_t *q_row; uint32_t i_softness = softness * (1 << 16); int field_count = field_order < 0 ? 1 : 2; int field_stride_src = field_count * stride_src; int field_stride_dest = field_count * stride_dest; int field = 0; float mix_a, mix_b; // composite using luma map while (field < field_count) { p_row = p_src + field * stride_src; q_row = p_dest + field * stride_dest; y_offset = field << 16; i = field; while (i < height_src) { p = p_row; q = q_row; l = luma_bitmap + (y_offset >> 16) * (luma_width * field_count); x_offset = 0; j = width_src; if (is_translucent) { while (j--) { float weight = l[x_offset >> 16] / 65535.f; float value = smoothstep_float(weight, softness + weight, field_pos[field]); mix_a = calculate_mix(1.0f - value, alpha_dest ? *alpha_dest : 255); mix_b = calculate_mix(value, alpha_src ? *alpha_src : 255); if (invert && alpha_src) { float mix2 = mix_b + mix_a; *alpha_src = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } else if (!invert && alpha_dest) { float mix2 = mix_b + mix_a; *alpha_dest = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } *q = sample_mix(*q, *p++, mix_b); q++; *q = sample_mix(*q, *p++, mix_b); q++; if (alpha_dest) alpha_dest++; if (alpha_src) alpha_src++; x_offset += x_diff; } } else { while (j--) { uint16_t weight = l[x_offset >> 16]; uint32_t value = smoothstep(weight, i_softness + weight, (1 << 16) * field_pos[field]); *q = (*p++ * value + *q * ((1 << 16) - value)) >> 16; q++; *q = (*p++ * value + *q * ((1 << 16) - value)) >> 16; q++; x_offset += x_diff; } } y_offset += y_diff; i += field_count; p_row += field_stride_src; q_row += field_stride_dest; } field++; } } static int luma_composite_rgba32(mlt_frame a_frame, mlt_frame b_frame, int luma_width, int luma_height, uint16_t *luma_bitmap, float pos, float frame_delta, float softness, int field_order, int width, int height, int invert) { if (mlt_properties_get(&a_frame->parent, "distort")) mlt_properties_set(&b_frame->parent, "distort", mlt_properties_get(&a_frame->parent, "distort")); struct mlt_image_s dimg; struct mlt_image_s simg; mlt_image_set_values(&dimg, NULL, mlt_image_rgba, width, height); mlt_frame_get_image(a_frame, (uint8_t **) &dimg.data, &dimg.format, &dimg.width, &dimg.height, 1); mlt_image_set_values(&dimg, dimg.data, dimg.format, dimg.width, dimg.height); mlt_image_set_values(&simg, NULL, mlt_image_rgba, width, height); mlt_frame_get_image(b_frame, (uint8_t **) &simg.data, &simg.format, &simg.width, &simg.height, 0); mlt_image_set_values(&simg, simg.data, simg.format, simg.width, simg.height); if (simg.width != dimg.width || simg.height != dimg.height || simg.width == 0 || dimg.width == 0 || simg.height == 0 || dimg.height == 0) { return 1; } int is_translucent = (!mlt_image_is_opaque(&dimg) || !mlt_image_is_opaque(&simg)); // Offset the position based on which field we're looking at ... float field_pos[2]; field_pos[0] = (pos + ((field_order == 0 ? 1 : 0) * frame_delta * 0.5f)) * (1.f + softness); field_pos[1] = (pos + ((field_order == 0 ? 0 : 1) * frame_delta * 0.5f)) * (1.f + softness); register uint8_t *p; register uint8_t *q; uint16_t *l; int32_t x_diff = (luma_width << 16) / dimg.width; int32_t y_diff = (luma_height << 16) / dimg.height; int32_t x_offset = 0; int32_t y_offset = 0; uint8_t *p_row; uint8_t *q_row; uint32_t i_softness = softness * (1 << 16); int field_count = field_order < 0 ? 1 : 2; int field_stride_src = field_count * simg.strides[0]; int field_stride_dest = field_count * dimg.strides[0]; int field = 0; float mix_a, mix_b; // composite using luma map while (field < field_count) { p_row = simg.planes[0] + field * simg.strides[0]; q_row = dimg.planes[0] + field * dimg.strides[0]; y_offset = field << 16; int i = field; while (i < simg.height) { p = p_row; q = q_row; l = luma_bitmap + (y_offset >> 16) * (luma_width * field_count); x_offset = 0; int j = simg.width; if (is_translucent) { while (j--) { float weight = l[x_offset >> 16] / 65535.f; float value = smoothstep_float(weight, softness + weight, field_pos[field]); mix_a = calculate_mix(1.0f - value, q[3]); mix_b = calculate_mix(value, p[3]); if (invert) { float mix2 = mix_b + mix_a; q[3] = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } else { float mix2 = mix_b + mix_a; q[3] = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } q[0] = sample_mix(q[0], p[0], mix_b); q[1] = sample_mix(q[1], p[1], mix_b); q[2] = sample_mix(q[2], p[2], mix_b); p += 4; q += 4; x_offset += x_diff; } } else { while (j--) { uint16_t weight = l[x_offset >> 16]; uint32_t value = smoothstep(weight, i_softness + weight, (1 << 16) * field_pos[field]); q[0] = (p[0] * value + q[0] * ((1 << 16) - value)) >> 16; q[1] = (p[1] * value + q[1] * ((1 << 16) - value)) >> 16; q[2] = (p[2] * value + q[2] * ((1 << 16) - value)) >> 16; p += 4; q += 4; x_offset += x_diff; } } y_offset += y_diff; i += field_count; p_row += field_stride_src; q_row += field_stride_dest; } field++; } return 0; } static int luma_composite_rgba64(mlt_frame a_frame, mlt_frame b_frame, int luma_width, int luma_height, uint16_t *luma_bitmap, float pos, float frame_delta, float softness, int field_order, int width, int height, int invert) { if (mlt_properties_get(&a_frame->parent, "distort")) mlt_properties_set(&b_frame->parent, "distort", mlt_properties_get(&a_frame->parent, "distort")); struct mlt_image_s dimg; struct mlt_image_s simg; mlt_image_set_values(&dimg, NULL, mlt_image_rgba64, width, height); mlt_frame_get_image(a_frame, (uint8_t **) &dimg.data, &dimg.format, &dimg.width, &dimg.height, 1); mlt_image_set_values(&dimg, dimg.data, dimg.format, dimg.width, dimg.height); mlt_image_set_values(&simg, NULL, mlt_image_rgba64, width, height); mlt_frame_get_image(b_frame, (uint8_t **) &simg.data, &simg.format, &simg.width, &simg.height, 0); mlt_image_set_values(&simg, simg.data, simg.format, simg.width, simg.height); if (simg.width != dimg.width || simg.height != dimg.height || simg.width == 0 || dimg.width == 0 || simg.height == 0 || dimg.height == 0) { return 1; } int is_translucent = (!mlt_image_is_opaque(&dimg) || !mlt_image_is_opaque(&simg)); // Offset the position based on which field we're looking at ... float field_pos[2]; field_pos[0] = (pos + ((field_order == 0 ? 1 : 0) * frame_delta * 0.5f)) * (1.f + softness); field_pos[1] = (pos + ((field_order == 0 ? 0 : 1) * frame_delta * 0.5f)) * (1.f + softness); register uint16_t *p; register uint16_t *q; uint16_t *l; int32_t x_diff = (luma_width << 16) / dimg.width; int32_t y_diff = (luma_height << 16) / dimg.height; int32_t x_offset = 0; int32_t y_offset = 0; uint16_t *p_row; uint16_t *q_row; uint32_t i_softness = softness * (1 << 16); int field_count = field_order < 0 ? 1 : 2; int field_stride_src = field_count * simg.strides[0] / 2; int field_stride_dest = field_count * dimg.strides[0] / 2; int field = 0; float mix_a, mix_b; // composite using luma map while (field < field_count) { p_row = (uint16_t *) simg.planes[0] + field * simg.strides[0] / 2; q_row = (uint16_t *) dimg.planes[0] + field * dimg.strides[0] / 2; y_offset = field << 16; int i = field; while (i < simg.height) { p = p_row; q = q_row; l = luma_bitmap + (y_offset >> 16) * (luma_width * field_count); x_offset = 0; int j = simg.width; if (is_translucent) { while (j--) { float weight = l[x_offset >> 16] / 65535.f; float value = smoothstep_float(weight, softness + weight, field_pos[field]); mix_a = calculate_mix_16(1.0f - value, q[3]); mix_b = calculate_mix_16(value, p[3]); if (invert) { float mix2 = mix_b + mix_a; q[3] = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } else { float mix2 = mix_b + mix_a; q[3] = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } q[0] = sample_mix_16(q[0], p[0], mix_b); q[1] = sample_mix_16(q[1], p[1], mix_b); q[2] = sample_mix_16(q[2], p[2], mix_b); p += 4; q += 4; x_offset += x_diff; } } else { while (j--) { uint16_t weight = l[x_offset >> 16]; uint32_t value = smoothstep(weight, i_softness + weight, (1 << 16) * field_pos[field]); q[0] = (p[0] * value + q[0] * ((1 << 16) - value)) >> 16; q[1] = (p[1] * value + q[1] * ((1 << 16) - value)) >> 16; q[2] = (p[2] * value + q[2] * ((1 << 16) - value)) >> 16; p += 4; q += 4; x_offset += x_diff; } } y_offset += y_diff; i += field_count; p_row += field_stride_src; q_row += field_stride_dest; } field++; } return 0; } void yuv422_to_luma16(uint8_t *image, uint16_t **map, int width, int height, int full_range) { // allocate the luma bitmap *map = (uint16_t *) mlt_pool_alloc(width * height * sizeof(uint16_t)); if (!*map) return; int i; int n = width * height; uint8_t offset = full_range ? 0 : 16; uint8_t max = full_range ? 255 : 235 - offset; int factor = full_range ? 256 : 299; // 299 = 65535 / 219 // process the image data into the luma bitmap for (i = 0; i < n; i++) { (*map)[i] = CLAMP(image[i << 1] - offset, 0, max) * factor; } } static int transition_get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the b frame from the stack mlt_frame b_frame = mlt_frame_pop_frame(a_frame); // Get the transition object mlt_transition transition = mlt_frame_pop_service(a_frame); // Get the properties of the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); // Get the properties of the a frame mlt_properties a_props = MLT_FRAME_PROPERTIES(a_frame); // Get the properties of the b frame mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); mlt_service_lock(MLT_TRANSITION_SERVICE(transition)); // The cached luma map information int luma_width = mlt_properties_get_int(properties, "width"); int luma_height = mlt_properties_get_int(properties, "height"); uint16_t *luma_bitmap = mlt_properties_get_data(properties, "bitmap", NULL); char *current_resource = mlt_properties_get(properties, "_resource"); mlt_producer producer = mlt_properties_get_data(properties, "producer", NULL); // If the filename property changed, reload the map char *resource = mlt_properties_get(properties, "resource"); // Correct width/height if not specified if (luma_width == 0 || luma_height == 0) { luma_width = *width; luma_height = *height; } if (resource && (producer || !current_resource || strcmp(resource, current_resource))) { char temp[PATH_MAX]; char *extension = strrchr(resource, '.'); char *orig_resource = resource; mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(transition)); if (strchr(resource, '%')) { snprintf(temp, sizeof(temp), "%s/lumas/%s/%s", mlt_environment("MLT_DATA"), mlt_profile_lumas_dir(profile), strchr(resource, '%') + 1); FILE *test = mlt_fopen(temp, "r"); if (!test) { strcat(temp, ".png"); test = mlt_fopen(temp, "r"); } if (test) { fclose(test); resource = temp; } extension = strrchr(resource, '.'); } // See if it is a PGM if (extension != NULL && strcmp(extension, ".pgm") == 0) { // Load from PGM luma_bitmap = NULL; if (mlt_luma_map_from_pgm(resource, &luma_bitmap, &luma_width, &luma_height)) { // Failed to read file; generate it. mlt_luma_map luma = mlt_luma_map_new(orig_resource); if (profile) { luma->w = profile->width; luma->h = profile->height; } luma_bitmap = mlt_luma_map_render(luma); luma_width = luma->w; luma_height = luma->h; free(luma); } // Set the transition properties mlt_properties_set_int(properties, "width", luma_width); mlt_properties_set_int(properties, "height", luma_height); mlt_properties_set(properties, "_resource", orig_resource); mlt_properties_set_data(properties, "bitmap", luma_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL); mlt_properties_clear(properties, "producer"); } else if (!*resource) { luma_bitmap = NULL; mlt_properties_set(properties, "_resource", NULL); mlt_properties_set_data(properties, "bitmap", luma_bitmap, 0, mlt_pool_release, NULL); mlt_properties_clear(properties, "producer"); } else { if (!producer || !current_resource || strcmp(resource, current_resource)) { // Get the factory producer service char *factory = mlt_properties_get(properties, "factory"); // Create the producer producer = mlt_factory_producer(profile, factory, resource); if (producer) mlt_properties_set(properties, "_resource", resource); } // If we have one if (producer) { // Get the producer properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Ensure that we loop mlt_properties_set(producer_properties, "eof", "loop"); // Now pass all producer. properties on the transition down mlt_properties_pass(producer_properties, properties, "producer."); // We will get the alpha frame from the producer mlt_frame luma_frame = NULL; // Determine if producer is a video clip or still image const char *service_name = mlt_properties_get(producer_properties, "mlt_service"); int is_clip = mlt_producer_get_length(producer) > 1 && mlt_properties_get_int(producer_properties, "video_index") >= 0 && service_name && !strncmp("avformat", service_name, 8); if (is_clip) { if (!mlt_properties_get(producer_properties, "producer.eof")) mlt_properties_set(producer_properties, "eof", "pause"); mlt_producer_seek(producer, mlt_transition_get_position(transition, a_frame)); } // Get the luma frame if (mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &luma_frame, 0) == 0) { uint8_t *luma_image = NULL; mlt_image_format luma_format = mlt_image_yuv422; // Get image from the luma producer mlt_frame_get_image(luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0); // Generate the luma map if (luma_image) { if (is_clip) { yuv422_to_luma16(luma_image, &luma_bitmap, luma_width, luma_height, mlt_properties_get_int(MLT_FRAME_PROPERTIES(luma_frame), "full_range")); } else { mlt_luma_map_from_yuv422(luma_image, &luma_bitmap, luma_width, luma_height); } } // Set the transition properties mlt_properties_set_int(properties, "width", luma_width); mlt_properties_set_int(properties, "height", luma_height); mlt_properties_set_data(properties, "bitmap", luma_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL); // Cleanup the luma frame mlt_frame_close(luma_frame); } if (is_clip) { // Save the producer for getting next frame mlt_properties_set_data(properties, "producer", producer, 0, (mlt_destructor) mlt_producer_close, NULL); } else { // Cleanup the luma producer mlt_properties_clear(properties, "producer"); mlt_producer_close(producer); producer = NULL; } } } } // Arbitrary composite defaults float mix = mlt_transition_get_progress(transition, a_frame); float frame_delta = mlt_transition_get_progress_delta(transition, a_frame); float luma_softness = mlt_properties_get_double(properties, "softness"); int progressive = mlt_properties_get_int(a_props, "consumer.progressive") || mlt_properties_get_int(properties, "progressive") || mlt_properties_get_int(b_props, "luma.progressive"); int top_field_first = mlt_properties_get_int(b_props, "top_field_first"); int reverse = mlt_properties_get_int(properties, "reverse"); int invert = mlt_properties_get_int(properties, "invert"); int threads = CLAMP(mlt_properties_get_int(properties, "threads"), 0, mlt_slices_count_normal()); int alpha_over = mlt_properties_get_int(properties, "alpha_over"); int ret = 0; // Honour the reverse here if (mix >= 1.0) mix -= floor(mix); if (mlt_properties_get(properties, "fixed")) mix = mlt_properties_get_double(properties, "fixed"); if (producer) { invert = !invert; mix = 0.5f; } else { mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); } int fix_background_alpha = mlt_properties_get_int(properties, "fix_background_alpha"); if (luma_width > 0 && luma_height > 0 && luma_bitmap != NULL) { reverse = invert ? !reverse : reverse; mix = reverse ? 1 - mix : mix; frame_delta *= reverse ? -1.0 : 1.0; // Composite the frames using a luma map if (*format == mlt_image_rgba) { ret = luma_composite_rgba32(!invert ? a_frame : b_frame, !invert ? b_frame : a_frame, luma_width, luma_height, luma_bitmap, mix, frame_delta, luma_softness, progressive ? -1 : top_field_first, *width, *height, invert); } else if (*format == mlt_image_rgba64) { ret = luma_composite_rgba64(!invert ? a_frame : b_frame, !invert ? b_frame : a_frame, luma_width, luma_height, luma_bitmap, mix, frame_delta, luma_softness, progressive ? -1 : top_field_first, *width, *height, invert); } else { *format = mlt_image_yuv422; luma_composite_yuv422(!invert ? a_frame : b_frame, !invert ? b_frame : a_frame, luma_width, luma_height, luma_bitmap, mix, frame_delta, luma_softness, progressive ? -1 : top_field_first, width, height, invert, fix_background_alpha); } } else { if (mlt_properties_get(&a_frame->parent, "distort")) mlt_properties_set(&b_frame->parent, "distort", mlt_properties_get(&a_frame->parent, "distort")); if (mlt_color_trc_linear == mlt_image_color_trc_id(mlt_properties_get(a_props, "consumer.mlt_color_trc"))) { // Make progress appear linear using a gamma curve similar to sRGB or Rec. 709 if (reverse || invert) mix = 1.0 - pow(1.0 - mix, 2.0); else mix = pow(mix, 2.0); } mix = (reverse || invert) ? 1 - mix : mix; invert = 0; // Dissolve the frames using the time offset for mix value if (alpha_over && *format == mlt_image_rgba) { ret = dissolve_rgba32(a_frame, b_frame, mix, *width, *height, threads); } else if (alpha_over && *format == mlt_image_rgba64) { ret = dissolve_rgba64(a_frame, b_frame, mix, *width, *height, threads); } else { *format = mlt_image_yuv422; ret = dissolve_yuv422(a_frame, b_frame, mix, *width, *height, threads, alpha_over, fix_background_alpha); } } if (ret) { mlt_log_error(MLT_TRANSITION_SERVICE(transition), "Unable to dissolve incompatible images"); } if (producer) { mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); } // Extract the a_frame image info *width = mlt_properties_get_int(!invert ? a_props : b_props, "width"); *height = mlt_properties_get_int(!invert ? a_props : b_props, "height"); *image = mlt_properties_get_data(!invert ? a_props : b_props, "image", NULL); return ret; } /** Luma transition processing. */ static mlt_frame transition_process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { // Push the transition on to the frame mlt_frame_push_service(a_frame, transition); // Push the b_frame on to the stack mlt_frame_push_frame(a_frame, b_frame); // Push the transition method mlt_frame_push_get_image(a_frame, transition_get_image); return a_frame; } /** Constructor for the filter. */ mlt_transition transition_luma_init(mlt_profile profile, mlt_service_type type, const char *id, char *lumafile) { mlt_transition transition = mlt_transition_new(); if (transition != NULL) { // Set the methods transition->process = transition_process; // Default factory mlt_properties_set(MLT_TRANSITION_PROPERTIES(transition), "factory", mlt_environment("MLT_PRODUCER")); // Set the main property mlt_properties_set(MLT_TRANSITION_PROPERTIES(transition), "resource", lumafile); // Inform apps and framework that this is a video only transition mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "_transition_type", 1); return transition; } return NULL; } mlt-7.38.0/src/modules/core/transition_luma.yml000664 000000 000000 00000004630 15172202314 021507 0ustar00rootroot000000 000000 schema_version: 0.3 type: transition identifier: luma title: Wipe version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > A generic dissolve and wipe transition processor. "luma" gets its name from how it uses a grayscale "map" file. As the luma value varies over time, a threshold filter is applied to the map to determine what parts of frame A vs. frame B to show. It reads PGM files up to 16 bits! Alternatively, it can use the first frame from any producer that outputs yuv, but it will be limited to the luma gamut of 220 values. This performs field-based rendering unless the A frame property "progressive" or "consumer_progressive" or the transition property "progressive" is set to 1. bugs: - Assumes lower field first output. parameters: - identifier: resource title: Luma map file type: string description: > Either PGM or any other producible video. If not supplied, performs a dissolve. argument: yes - identifier: factory title: Factory type: string description: > The name of a factory service used as a non-PGM producer loader. default: loader - identifier: softness title: Softness type: float mutable: yes description: > Only when using a luma map, how soft to make the edges between A and B. 0.0 = no softness. 1.0 = too soft. - identifier: reverse title: Reverse type: integer mutable: yes description: > Reverse the direction of the transition. default: 0 - identifier: producer.* title: Producer mutable: yes description: > Properties may be set on the encapsulated producer. Any property starting with "producer." is passed to the non-PGM luma producer. readonly: no - identifier: alpha_over title: Use over-blending on the alpha channel type: boolean default: 0 mutable: yes - identifier: fix_background_alpha title: Ensure padding is transparent description: > This is a fix for version 2 that ensures the background of sources without an alpha channel and aspect ratio that does not match the profile get padding that includes an alpha channel. Basically, this should be the new default behavior, but a property is needed to not unexpectedly change the result of existing projects and scripts. type: boolean default: 0 mutable: yes mlt-7.38.0/src/modules/core/transition_matte.c000664 000000 000000 00000016741 15172202314 021312 0ustar00rootroot000000 000000 /* * transition_matte.c -- replace alpha channel of track * * Copyright (C) 2003-2014 Meltytech, LLC * Author: Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #if defined(USE_SSE) && defined(ARCH_X86_64) static void __attribute__((noinline)) copy_Y_to_A_scaled_luma_sse(uint8_t *alpha_a, uint8_t *image_b, int cnt) { const static unsigned char const1[] = {43, 0, 43, 0, 43, 0, 43, 0, 43, 0, 43, 0, 43, 0, 43, 0}; const static unsigned char const2[] = {16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0}; const static unsigned char const3[] = {235, 0, 235, 0, 235, 0, 235, 0, 235, 0, 235, 0, 235, 0, 235, 0}; const static unsigned char const4[] = {255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0}; __asm__ volatile( "movdqu (%[equ43]), %%xmm7 \n\t" /* load multiplier 43 */ "movdqu (%[equ16]), %%xmm6 \n\t" /* load bottom value 16 */ "movdqu (%[equ235]), %%xmm5 \n\t" /* load bottom value 235 */ "movdqu (%[equ255]), %%xmm4 \n\t" /* load bottom value 0xff */ "loop_start: \n\t" /* load pixels block 1 */ "movdqu 0(%[image_b]), %%xmm0 \n\t" "add $0x10, %[image_b] \n\t" /* load pixels block 2 */ "movdqu 0(%[image_b]), %%xmm1 \n\t" "add $0x10, %[image_b] \n\t" /* leave only Y */ "pand %%xmm4, %%xmm0 \n\t" "pand %%xmm4, %%xmm1 \n\t" /* upper range clip */ "pminsw %%xmm5, %%xmm0 \n\t" "pminsw %%xmm5, %%xmm1 \n\t" /* upper range clip */ "pmaxsw %%xmm6, %%xmm0 \n\t" "pmaxsw %%xmm6, %%xmm1 \n\t" /* upper range clip */ "psubw %%xmm6, %%xmm0 \n\t" "psubw %%xmm6, %%xmm1 \n\t" /* duplicate values */ "movdqa %%xmm0,%%xmm2 \n\t" "movdqa %%xmm1,%%xmm3 \n\t" /* regA = regA << 8 */ "psllw $8, %%xmm0 \n\t" "psllw $8, %%xmm1 \n\t" /* regB = regB * 47 */ "pmullw %%xmm7, %%xmm2 \n\t" "pmullw %%xmm7, %%xmm3 \n\t" /* regA = regA + regB */ "paddw %%xmm2, %%xmm0 \n\t" "paddw %%xmm3, %%xmm1 \n\t" /* regA = regA >> 8 */ "psrlw $8, %%xmm0 \n\t" "psrlw $8, %%xmm1 \n\t" /* pack to 8 bit value */ "packuswb %%xmm1, %%xmm0 \n\t" /* store */ "movdqu %%xmm0, (%[alpha_a]) \n\t" "add $0x10, %[alpha_a] \n\t" /* loop if we done */ "dec %[cnt] \n\t" "jnz loop_start \n\t" : [cnt] "+r"(cnt), [alpha_a] "+r"(alpha_a), [image_b] "+r"(image_b) : [equ43] "r"(const1), [equ16] "r"(const2), [equ235] "r"(const3), [equ255] "r"(const4) : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7"); }; #endif static void copy_Y_to_A_scaled_luma( uint8_t *alpha_a, int stride_a, uint8_t *image_b, int stride_b, int width, int height) { int i, j; for (j = 0; j < height; j++) { i = 0; #if defined(USE_SSE) && defined(ARCH_X86_64) if (width >= 16) { copy_Y_to_A_scaled_luma_sse(alpha_a, image_b, width >> 4); i = (width >> 4) << 4; } #endif for (; i < width; i++) { unsigned int p = image_b[2 * i]; if (p < 16) p = 16; if (p > 235) p = 235; /* p = (p - 16) * 255 / 219; */ p -= 16; p = ((p << 8) + (p * 43)) >> 8; alpha_a[i] = p; }; alpha_a += stride_a; image_b += stride_b; }; }; /** Get the image. */ static int transition_get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the b frame from the stack mlt_frame b_frame = mlt_frame_pop_frame(a_frame); mlt_frame_get_image(a_frame, image, format, width, height, 1); // Get the properties of the a frame mlt_properties a_props = MLT_FRAME_PROPERTIES(a_frame); int width_a = mlt_properties_get_int(a_props, "width"), width_b = width_a, height_a = mlt_properties_get_int(a_props, "height"), height_b = height_a; uint8_t *alpha_a, *image_b; // This transition is yuv422 only *format = mlt_image_yuv422; // Get the image from the a frame mlt_frame_get_image(b_frame, &image_b, format, &width_b, &height_b, 1); // Create the alpha channel based on the b_image dimensions int alpha_width = MIN(width_a, width_b); int alpha_height = MIN(height_a, height_b); int size = alpha_width * alpha_height; alpha_a = mlt_pool_alloc(size); memset(alpha_a, 255, size); mlt_frame_set_alpha(a_frame, alpha_a, size, mlt_pool_release); // Copy luma from image_b to a_frame's new alpha channel copy_Y_to_A_scaled_luma(alpha_a, width_a, image_b, width_b * 2, alpha_width, alpha_height); // Extract the a_frame image info *width = mlt_properties_get_int(a_props, "width"); *height = mlt_properties_get_int(a_props, "height"); *image = mlt_properties_get_data(a_props, "image", NULL); return 0; } /** Matte transition processing. */ static mlt_frame transition_process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { // Push the b_frame on to the stack mlt_frame_push_frame(a_frame, b_frame); // Push the transition method mlt_frame_push_get_image(a_frame, transition_get_image); return a_frame; } /** Constructor for the filter. */ mlt_transition transition_matte_init(mlt_profile profile, mlt_service_type type, const char *id, char *lumafile) { mlt_transition transition = mlt_transition_new(); if (transition != NULL) { // Set the methods transition->process = transition_process; // Inform apps and framework that this is a video only transition mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "_transition_type", 1); return transition; } return NULL; } mlt-7.38.0/src/modules/core/transition_matte.yml000664 000000 000000 00000003551 15172202314 021664 0ustar00rootroot000000 000000 schema_version: 0.1 type: transition identifier: matte title: Matte version: 1 copyright: Meltytech, LLC creator: Maksym Veremeyenko license: LGPLv2.1 language: en tags: - Video description: > Replace the alpha channel of track A with the luma channel from track B. notes: | Please note that the transition automatically detects if the frame from track B has scaled or non-scaled luma. The frame property "full_range" should indicate it. If you need to prepare fill and key (matte) files, you can use melt or ffmpeg to extract alpha channel from existing video: melt sg_gm_2013_clip_title.avi -attach frei0r.alpha0ps 0=0.21 -consumer \ avformat:sg_gm_2013_clip_title.matte_scaled.mp4 crf=10 preset=placebo an=1 ffmpeg -i sg_gm_2013_clip_title.avi -vf "alphaextract" -pix_fmt \ yuv422p -preset placebo -crf 10 -y sg_gm_2013_clip_title.matte_scaled.mp4 Because the example above provides a scaled luma output, the transition performs scaling from [16,235] -> [0, 255]. It is possible to create unscaled (full) range: melt sg_gm_2013_clip_title.avi -attach frei0r.alpha0ps 0=0.21 -consumer \ avformat:sg_gm_2013_clip_title.matte_full.mp4 crf=10 preset=placebo an=1 \ mlt_image_format=rgba pix_fmt=yuvj422p ffmpeg -i sg_gm_2013_clip_title.avi -vf "alphaextract" -pix_fmt \ yuvj422p -preset placebo -crf 10 -y sg_gm_2013_clip_title.matte_full.mp4 The fill can be converted from rgba to yuv422: melt sg_gm_2013_clip_title.avi -consumer avformat:sg_gm_2013_clip_title.fill.mp4 \ crf=10 preset=placebo an=1 ffmpeg -i sg_gm_2013_clip_title.avi -pix_fmt yuv422p -preset placebo -crf 10 -y \ sg_gm_2013_clip_title.fill.mp4 Putting it all together: melt sg_gm_2013_clip_title.matte_full.mp4 -track noise: -track \ sg_gm_2013_clip_title.fill.mp4 -transition matte a_track=2 \ b_track=0 -transition qtblend a_track=1 b_track=2 mlt-7.38.0/src/modules/core/transition_mix.c000664 000000 000000 00000046556 15172202314 021004 0ustar00rootroot000000 000000 /* * transition_mix.c -- mix two audio streams * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #define MAX_CHANNELS (6) #define MAX_SAMPLES (192000) #define SAMPLE_BYTES(samples, channels) ((samples) * (channels) * sizeof(float)) #define MAX_BYTES SAMPLE_BYTES(MAX_SAMPLES, MAX_CHANNELS) typedef struct transition_mix_s { mlt_transition parent; float src_buffer[MAX_SAMPLES * MAX_CHANNELS]; float dest_buffer[MAX_SAMPLES * MAX_CHANNELS]; int src_buffer_count; int dest_buffer_count; mlt_position previous_frame_a; mlt_position previous_frame_b; } * transition_mix; static void mix_audio(double weight_start, double weight_end, float *buffer_a, float *buffer_b, int channels_a, int channels_b, int channels_out, int samples, int power) { int i, j; double a, b, v; // Compute a smooth ramp over start to end double mix = weight_start; double mix_step = (weight_end - weight_start) / samples; for (i = 0; i < samples; i++) { double gain_b = power ? sin(mix * M_PI_2) : mix; double gain_a = power ? cos(mix * M_PI_2) : 1.0 - mix; for (j = 0; j < channels_out; j++) { a = (double) buffer_a[i * channels_a + j]; b = (double) buffer_b[i * channels_b + j]; v = gain_b * b + gain_a * a; buffer_a[i * channels_a + j] = v; } mix += mix_step; } } static void sum_audio(double weight_start, double weight_end, float *buffer_a, float *buffer_b, int channels_a, int channels_b, int channels_out, int samples) { int i, j; double a, b; // Compute a smooth ramp over start to end double mix = weight_start; double mix_step = (weight_end - weight_start) / samples; for (i = 0; i < samples; i++) { for (j = 0; j < channels_out; j++) { a = (double) buffer_a[i * channels_a + j]; b = (double) buffer_b[i * channels_b + j]; buffer_a[i * channels_a + j] = mix * b + a; } mix += mix_step; } } // This filter uses an inline low pass filter to allow mixing without volume hacking. static void combine_audio(double weight, float *buffer_a, float *buffer_b, int channels_a, int channels_b, int channels_out, int samples) { int i, j; double Fc = 0.5; double B = exp(-2.0 * M_PI * Fc); double A = 1.0 - B; double a, b, v; double v_prev[MAX_CHANNELS]; for (j = 0; j < channels_out; j++) v_prev[j] = (double) buffer_a[j]; for (i = 0; i < samples; i++) { for (j = 0; j < channels_out; j++) { a = (double) buffer_a[i * channels_a + j]; b = (double) buffer_b[i * channels_b + j]; v = weight * a + b; v_prev[j] = buffer_a[i * channels_a + j] = v * A + v_prev[j] * B; } } } /** Get the audio. */ static int transition_get_audio(mlt_frame frame_a, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int error = 0; // Get the b frame from the stack mlt_frame frame_b = mlt_frame_pop_audio(frame_a); // Get the effect mlt_transition transition = mlt_frame_pop_audio(frame_a); // Get the properties of the b frame mlt_properties b_props = MLT_FRAME_PROPERTIES(frame_b); transition_mix self = transition->child; float *buffer_b, *buffer_a; int frequency_b = *frequency, frequency_a = *frequency; int channels_b = *channels, channels_a = *channels; int samples_b = *samples, samples_a = *samples; // We can only mix interleaved 32-bit float. *format = mlt_audio_f32le; // Get the audio from our producers mlt_frame_get_audio(frame_b, (void **) &buffer_b, format, &frequency_b, &channels_b, &samples_b); mlt_frame_get_audio(frame_a, (void **) &buffer_a, format, &frequency_a, &channels_a, &samples_a); // Prevent dividing by zero. if (!channels_a || !channels_b || !buffer_a || !buffer_b) return 1; if (buffer_b == buffer_a) { *samples = samples_b; *channels = channels_b; *buffer = buffer_b; *frequency = frequency_b; return error; } // I do not recall what these silent_audio properties are about. int silent = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame_a), "silent_audio"); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame_a), "silent_audio", 0); if (silent) memset(buffer_a, 0, samples_a * channels_a * sizeof(float)); silent = mlt_properties_get_int(b_props, "silent_audio"); mlt_properties_set_int(b_props, "silent_audio", 0); if (silent) memset(buffer_b, 0, samples_b * channels_b * sizeof(float)); // At this point we have two frames of audio with possibly differing sample // counts. How to reconcile this? #ifdef KEEP_IT_SIMPLE_AND_STUPID // The simple and stupid way to deal with different sample counts was to // use the lesser of the two. This sounds good. You can #define SIMPLE_AND_STUPID // and hear what it sounds like. *samples = MIN(samples_a, samples_b); *channels = MIN(MIN(channels_b, channels_a), MAX_CHANNELS); *frequency = frequency_a; // Note this direct call to sum_audio() skips ramping and the alternative // mixing methods. sum_audio(1, 1, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples); *buffer = buffer_a; return error; #endif // However, the simple and stupid approach drops samples. Over time, this // can accumulate and cause an A/V sync drift, which addressed in b2640656 // by saving the unused samples in a buffer and then using them first on the // next iteration. // determine number of samples to process *samples = MIN(self->src_buffer_count + samples_b, self->dest_buffer_count + samples_a); *channels = MIN(MIN(channels_b, channels_a), MAX_CHANNELS); *frequency = frequency_a; // Prevent src buffer overflow by discarding oldest samples. samples_b = MIN(samples_b, MAX_SAMPLES * MAX_CHANNELS / channels_b); size_t bytes = SAMPLE_BYTES(samples_b, channels_b); if (SAMPLE_BYTES(self->src_buffer_count + samples_b, channels_b) > MAX_BYTES) { mlt_log_verbose(MLT_TRANSITION_SERVICE(transition), "buffer overflow: src_buffer_count %d\n", self->src_buffer_count); self->src_buffer_count = MAX_SAMPLES * MAX_CHANNELS / channels_b - samples_b; memmove(self->src_buffer, &self->src_buffer[MAX_SAMPLES * MAX_CHANNELS - samples_b * channels_b], SAMPLE_BYTES(samples_b, channels_b)); } // Silence src buffer if discontinuity if (self->src_buffer_count > 0 && mlt_frame_get_position(frame_b) != self->previous_frame_b + 1) memset(self->src_buffer, 0, SAMPLE_BYTES(self->src_buffer_count, channels_b)); self->previous_frame_b = mlt_frame_get_position(frame_b); // Append the new samples from frame B to the src buffer memcpy(&self->src_buffer[self->src_buffer_count * channels_b], buffer_b, bytes); self->src_buffer_count += samples_b; buffer_b = self->src_buffer; // Prevent dest buffer overflow by discarding oldest samples. samples_a = MIN(samples_a, MAX_SAMPLES * MAX_CHANNELS / channels_a); bytes = SAMPLE_BYTES(samples_a, channels_a); if (SAMPLE_BYTES(self->dest_buffer_count + samples_a, channels_a) > MAX_BYTES) { mlt_log_verbose(MLT_TRANSITION_SERVICE(transition), "buffer overflow: dest_buffer_count %d\n", self->dest_buffer_count); self->dest_buffer_count = MAX_SAMPLES * MAX_CHANNELS / channels_a - samples_a; memmove(self->dest_buffer, &self->dest_buffer[MAX_SAMPLES * MAX_CHANNELS - samples_a * channels_a], SAMPLE_BYTES(samples_a, channels_a)); } // Silence dest buffer if discontinuity if (self->dest_buffer_count > 0 && mlt_frame_get_position(frame_a) != self->previous_frame_a + 1) memset(self->dest_buffer, 0, SAMPLE_BYTES(self->dest_buffer_count, channels_a)); self->previous_frame_a = mlt_frame_get_position(frame_a); // Append the new samples from frame A to the dest buffer memcpy(&self->dest_buffer[self->dest_buffer_count * channels_a], buffer_a, bytes); self->dest_buffer_count += samples_a; buffer_a = self->dest_buffer; // Do the mixing. if (mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(transition), "sum")) { double mix_start = 1.0, mix_end = 1.0; if (mlt_properties_get(b_props, "audio.previous_mix")) mix_start = mlt_properties_get_double(b_props, "audio.previous_mix"); if (mlt_properties_get(b_props, "audio.mix")) mix_end = mlt_properties_get_double(b_props, "audio.mix"); if (mlt_properties_get_int(b_props, "audio.reverse")) { mix_start = 1.0 - mix_start; mix_end = 1.0 - mix_end; } sum_audio(mix_start, mix_end, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples); } else if (mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(transition), "combine")) { double weight = 1.0; if (mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame_a), "meta.mixdown")) weight = 1.0 - mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame_a), "meta.volume"); combine_audio(weight, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples); } else { double mix_start = 0.5, mix_end = 0.5; if (mlt_properties_get(b_props, "audio.previous_mix")) mix_start = mlt_properties_get_double(b_props, "audio.previous_mix"); if (mlt_properties_get(b_props, "audio.mix")) mix_end = mlt_properties_get_double(b_props, "audio.mix"); if (mlt_properties_get_int(b_props, "audio.reverse")) { mix_start = 1.0 - mix_start; mix_end = 1.0 - mix_end; } int power = mlt_properties_get_double(MLT_TRANSITION_PROPERTIES(transition), "start") < -1.0; mix_audio(mix_start, mix_end, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples, power); } // Copy the audio from the dest buffer into the frame. bytes = SAMPLE_BYTES(*samples, *channels); *buffer = mlt_pool_alloc(bytes); memcpy(*buffer, buffer_a, bytes); mlt_frame_set_audio(frame_a, *buffer, *format, bytes, mlt_pool_release); if (mlt_properties_get_int(b_props, "_speed") == 0) { // Flush the buffer when paused and scrubbing. samples_b = self->src_buffer_count; samples_a = self->dest_buffer_count; } else { // It is also not good for A/V sync to let many samples accumulate in // the buffer. This part provides a time-based buffer limit. // Determine the maximum amount of latency permitted in the buffer. int max_latency = CLAMP(*frequency / 1000, 0, MAX_SAMPLES); // samples in 1ms // samples_b becomes the new target src buffer count. samples_b = CLAMP(self->src_buffer_count - *samples, 0, max_latency); // samples_b becomes the number of samples to consume: difference between actual and the target. samples_b = self->src_buffer_count - samples_b; // samples_a becomes the new target dest buffer count. samples_a = CLAMP(self->dest_buffer_count - *samples, 0, max_latency); // samples_a becomes the number of samples to consume: difference between actual and the target. samples_a = self->dest_buffer_count - samples_a; } // Consume the src buffer. self->src_buffer_count -= samples_b; if (self->src_buffer_count) { memmove(self->src_buffer, &self->src_buffer[samples_b * channels_b], SAMPLE_BYTES(self->src_buffer_count, channels_b)); } // Consume the dest buffer. self->dest_buffer_count -= samples_a; if (self->dest_buffer_count > 0) { memmove(self->dest_buffer, &self->dest_buffer[samples_a * channels_a], SAMPLE_BYTES(self->dest_buffer_count, channels_a)); } return error; } /** Mix transition processing. */ static mlt_frame transition_process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); // Only if mix is specified, otherwise a producer may set the mix if (mlt_properties_get(properties, "start")) { // Determine the time position of this frame in the transition duration mlt_properties props = mlt_properties_get_data(MLT_FRAME_PROPERTIES(b_frame), "_producer", NULL); mlt_position in = mlt_properties_get_int(props, "in"); mlt_position out = mlt_properties_get_int(props, "out"); int length = mlt_properties_get_int(properties, "length"); mlt_position time = mlt_properties_get_int(props, "_frame"); double mix = mlt_transition_get_progress(transition, b_frame); if (mlt_properties_get_int(properties, "always_active")) mix = (double) (time - in) / (double) (out - in + 1); // TODO: Check the logic here - shouldn't we be computing current and next mixing levels in all cases? if (length == 0) { // If there is an end mix level adjust mix to the range if (mlt_properties_get(properties, "end")) { double start = mlt_properties_get_double(properties, "start"); double end = mlt_properties_get_double(properties, "end"); mix = start + (end - start) * mix; } // A negative means total crossfade (uses position) else if (mlt_properties_get_double(properties, "start") >= 0) { // Otherwise, start/constructor is a constant mix level mix = mlt_properties_get_double(properties, "start"); } // Finally, set the mix property on the frame mlt_properties_set_double(b_props, "audio.mix", mix); // Initialise transition previous mix value to prevent an inadvertent jump from 0 mlt_position last_position = mlt_properties_get_position(properties, "_last_position"); mlt_position current_position = mlt_frame_get_position(b_frame); mlt_properties_set_position(properties, "_last_position", current_position); if (!mlt_properties_get(properties, "_previous_mix") || current_position != last_position + 1) mlt_properties_set_double(properties, "_previous_mix", mix); // Tell b frame what the previous mix level was mlt_properties_set_double(b_props, "audio.previous_mix", mlt_properties_get_double(properties, "_previous_mix")); // Save the current mix level for the next iteration mlt_properties_set_double(properties, "_previous_mix", mlt_properties_get_double(b_props, "audio.mix")); mlt_properties_set_double(b_props, "audio.reverse", mlt_properties_get_double(properties, "reverse")); } else { double level = mlt_properties_get_double(properties, "start"); double mix_start = level; double mix_end = mix_start; double mix_increment = 1.0 / length; if (time - in < length) { mix_start = mix_start * ((double) (time - in) / length); mix_end = mix_start + mix_increment; } else if (time > out - length) { mix_end = mix_start * ((double) (out - time - in) / length); mix_start = mix_end - mix_increment; } mix_start = mix_start < 0 ? 0 : mix_start > level ? level : mix_start; mix_end = mix_end < 0 ? 0 : mix_end > level ? level : mix_end; mlt_properties_set_double(b_props, "audio.previous_mix", mix_start); mlt_properties_set_double(b_props, "audio.mix", mix_end); } } // Override the get_audio method mlt_frame_push_audio(a_frame, transition); mlt_frame_push_audio(a_frame, b_frame); mlt_frame_push_audio(a_frame, transition_get_audio); // Ensure transition_get_audio is called if test_audio=1. if (mlt_properties_get_int(properties, "accepts_blanks")) mlt_properties_set_int(MLT_FRAME_PROPERTIES(a_frame), "test_audio", 0); return a_frame; } static void transition_close(mlt_transition transition) { free(transition->child); transition->close = NULL; mlt_transition_close(transition); } /** Constructor for the transition. */ mlt_transition transition_mix_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { transition_mix mix = calloc(1, sizeof(struct transition_mix_s)); mlt_transition transition = calloc(1, sizeof(struct mlt_transition_s)); if (mix && transition && !mlt_transition_init(transition, mix)) { mix->parent = transition; transition->close = transition_close; transition->process = transition_process; if (arg) { mlt_properties_set_double(MLT_TRANSITION_PROPERTIES(transition), "start", atof(arg)); if (atof(arg) < 0) mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "accepts_blanks", 1); } // Inform apps and framework that this is an audio only transition mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "_transition_type", 2); } else { if (transition) mlt_transition_close(transition); if (mix) free(mix); } return transition; } mlt-7.38.0/src/modules/core/transition_mix.yml000664 000000 000000 00000003640 15172202314 021346 0ustar00rootroot000000 000000 schema_version: 7.0 type: transition identifier: mix title: Mix version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: Mix two audio tracks. bugs: - Samples from the longer of the two frames are discarded. parameters: - identifier: start title: Start argument: yes type: float mutable: yes description: > The mix level to apply to the second frame. -1 causes an automatic linear crossfade from 0 to 1. -2 causes an automatic constant-power (equal-power) crossfade from 0 to 1. - identifier: end title: End type: float mutable: yes description: > The ending value of the mix level. Mix level will be interpolated from start to end over the in-out range. - identifier: reverse title: Reverse type: boolean mutable: yes description: > Set to 1 to reverse the direction of the mix. default: 0 widget: checkbox - identifier: combine title: Use an alternative mixing algorithm description: > Mix using a low pass filter to prevent affecting audio levels. However, this may introduce slight artifacts. This is incompatible with start < 0. type: boolean default: 0 mutable: yes - identifier: sum title: Mix by simply adding samples description: > The default mixing algorithm halves the sample values before adding them to absolutely prevent clipping. However, that affects levels. This algorithm simply adds samples and may clip. In many real world scenarios, the signals being mixed typically have headroom in their level and are rarely correlated and thus often will not clip. Also, one can reduce the gain and add a limiter on the mixed output prior to integer quantization to prevent clipping. This mode is incompatible with start < 0. type: boolean default: 0 mutable: yes mlt-7.38.0/src/modules/decklink/000775 000000 000000 00000000000 15172202314 016405 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/decklink/CMakeLists.txt000664 000000 000000 00000002435 15172202314 021151 0ustar00rootroot000000 000000 add_library(mltdecklink MODULE common.cpp common.h consumer_decklink.cpp producer_decklink.cpp ) file(GLOB YML "*.yml") add_custom_target(Other_decklink_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltdecklink) target_compile_options(mltdecklink PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltdecklink PRIVATE mlt Threads::Threads) target_include_directories(mltdecklink PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) if(WIN32) target_sources(mltdecklink PRIVATE win/DeckLinkAPI_i.cpp) target_include_directories(mltdecklink PRIVATE win) elseif(APPLE) target_sources(mltdecklink PRIVATE darwin/DeckLinkAPIDispatch.cpp) target_include_directories(mltdecklink PRIVATE darwin) target_link_libraries(mltdecklink PRIVATE "-framework CoreFoundation") else() target_sources(mltdecklink PRIVATE linux/DeckLinkAPIDispatch.cpp) target_include_directories(mltdecklink PRIVATE linux) endif() if(CPU_SSE) target_compile_definitions(mltdecklink PRIVATE USE_SSE) endif() set_target_properties(mltdecklink PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltdecklink LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_decklink.yml producer_decklink.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/decklink ) mlt-7.38.0/src/modules/decklink/common.cpp000664 000000 000000 00000006646 15172202314 020415 0ustar00rootroot000000 000000 /* * common.cpp -- Blackmagic Design DeckLink common functions * Copyright (C) 2012-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #ifdef __APPLE__ char *getCString(DLString aDLString) { char *CString = (char *) malloc(64); CFStringGetCString(aDLString, CString, 64, kCFStringEncodingMacRoman); return CString; } void freeCString(char *aCString) { if (aCString) free(aCString); } void freeDLString(DLString aDLString) { if (aDLString) CFRelease(aDLString); } #elif defined(_WIN32) char *getCString(DLString aDLString) { char *CString = NULL; if (aDLString) { int size = WideCharToMultiByte(CP_UTF8, 0, aDLString, -1, NULL, 0, NULL, NULL); if (size) { CString = new char[size]; size = WideCharToMultiByte(CP_UTF8, 0, aDLString, -1, CString, size, NULL, NULL); if (!size) { delete[] CString; CString = NULL; } } } return CString; } void freeCString(char *aCString) { delete[] aCString; } void freeDLString(DLString aDLString) { SysFreeString(aDLString); } #else char *getCString(DLString aDLString) { return aDLString ? (char *) aDLString : NULL; } void freeCString(char *aCString) {} void freeDLString(DLString aDLString) { if (aDLString) free((void *) aDLString); } #endif void swab2(const void *from, void *to, int n) { #if defined(USE_SSE) #define SWAB_STEP 16 int cnt = n / SWAB_STEP; __asm__ volatile( /* "loop_start: \n\t" */ "1: \n\t" /* load */ "movdqa 0(%[from]), %%xmm0 \n\t" "add $0x10, %[from] \n\t" /* duplicate to temp registers */ "movdqa %%xmm0, %%xmm1 \n\t" /* shift right temp register */ "psrlw $8, %%xmm1 \n\t" /* shift left main register */ "psllw $8, %%xmm0 \n\t" /* compose them back */ "por %%xmm0, %%xmm1 \n\t" /* save */ "movdqa %%xmm1, 0(%[to]) \n\t" "add $0x10, %[to] \n\t" "dec %[cnt] \n\t" "jnz 1b \n\t" /* "jnz loop_start \n\t" */ : [from] "+r"(from), [to] "+r"(to), [cnt] "+r"(cnt) : : "xmm0", "xmm1"); from = (unsigned char *) from + n - (n % SWAB_STEP); to = (unsigned char *) to + n - (n % SWAB_STEP); n = (n % SWAB_STEP); #endif swab((char *) from, (char *) to, n); }; mlt-7.38.0/src/modules/decklink/common.h000664 000000 000000 00000002573 15172202314 020055 0ustar00rootroot000000 000000 /* * common.h -- Blackmagic Design DeckLink common functions * Copyright (C) 2012-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DECKLINK_COMMON_H #define DECKLINK_COMMON_H #ifdef _WIN32 #include "DeckLinkAPI_h.h" #include typedef BSTR DLString; #else #include "DeckLinkAPI.h" #ifdef __APPLE__ typedef CFStringRef DLString; #else typedef const char *DLString; #endif #endif #define SAFE_RELEASE(V) \ if (V) { \ V->Release(); \ V = NULL; \ } char *getCString(DLString aDLString); void freeCString(char *aCString); void freeDLString(DLString aDLString); void swab2(const void *from, void *to, int n); #endif // DECKLINK_COMMON_H mlt-7.38.0/src/modules/decklink/consumer_decklink.cpp000664 000000 000000 00000135510 15172202314 022615 0ustar00rootroot000000 000000 /* * consumer_decklink.cpp -- output through Blackmagic Design DeckLink * Copyright (C) 2010-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_FORMAT_MACROS /* see inttypes.h */ #include "common.h" #include "mltdecklink_export.h" #include #include #include #include #include #include #include #define SWAB_SLICED_ALIGN_POW 5 static int swab_sliced(int id, int idx, int jobs, void *cookie) { unsigned char **args = (unsigned char **) cookie; ssize_t sz = (ssize_t) args[2]; ssize_t bsz = ((sz / jobs + (1 << SWAB_SLICED_ALIGN_POW) - 1) >> SWAB_SLICED_ALIGN_POW) << SWAB_SLICED_ALIGN_POW; ssize_t offset = bsz * idx; if (offset < sz) { if ((offset + bsz) > sz) bsz = sz - offset; swab2(args[0] + offset, args[1] + offset, bsz); } return 0; }; static const unsigned PREROLL_MINIMUM = 3; enum { OP_NONE = 0, OP_OPEN, OP_START, OP_STOP, OP_EXIT }; enum { EOTF_SDR = 0, EOTF_HDR = 1, EOTF_PQ = 2, EOTF_HLG = 3 }; ///< CEA 861.3 class DeckLinkConsumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback { private: mlt_consumer_s m_consumer; IDeckLink *m_deckLink; IDeckLinkOutput *m_deckLinkOutput; IDeckLinkDisplayMode *m_displayMode; int m_width; int m_height; BMDTimeValue m_duration; BMDTimeScale m_timescale; double m_fps; uint64_t m_count; int m_outChannels; int m_inChannels; bool m_fix5p1{false}; bool m_isAudio; int m_isKeyer; IDeckLinkKeyer *m_deckLinkKeyer; bool m_terminate_on_pause; uint32_t m_preroll; uint32_t m_reprio; mlt_deque m_aqueue; pthread_mutex_t m_aqueue_lock; mlt_deque m_frames; pthread_mutex_t m_op_lock; pthread_mutex_t m_op_arg_mutex; pthread_cond_t m_op_arg_cond; int m_op_id; int m_op_res; int m_op_arg; pthread_t m_op_thread; bool m_sliced_swab; uint8_t *m_buffer; bool m_running{false}; pthread_cond_t m_refresh_cond; pthread_mutex_t m_refresh_mutex; int m_refresh{0}; bool m_purge{false}; int m_previousSpeed{0}; int m_currentSpeed{0}; IDeckLinkDisplayMode *getDisplayMode() { mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(getConsumer())); IDeckLinkDisplayModeIterator *iter = nullptr; IDeckLinkDisplayMode *mode = nullptr; IDeckLinkDisplayMode *result = 0; if (m_deckLinkOutput->GetDisplayModeIterator(&iter) == S_OK) { while (!result && iter->Next(&mode) == S_OK) { m_width = mode->GetWidth(); m_height = mode->GetHeight(); mode->GetFrameRate(&m_duration, &m_timescale); m_fps = (double) m_timescale / m_duration; int p = mode->GetFieldDominance() == bmdProgressiveFrame; mlt_log_verbose(getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p); if (m_width == profile->width && p == profile->progressive && (int) m_fps == (int) mlt_profile_fps(profile) && (m_height == profile->height || (m_height == 486 && profile->height == 480))) result = mode; else SAFE_RELEASE(mode); } SAFE_RELEASE(iter); } return result; } public: mlt_consumer getConsumer() { return &m_consumer; } bool isRunning() const { return m_running; } DeckLinkConsumer() { pthread_mutexattr_t mta; m_displayMode = nullptr; m_deckLinkKeyer = nullptr; m_deckLinkOutput = nullptr; m_deckLink = nullptr; m_aqueue = mlt_deque_init(); m_frames = mlt_deque_init(); m_buffer = nullptr; // operation locks m_op_id = OP_NONE; m_op_arg = 0; pthread_mutexattr_init(&mta); pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&m_op_lock, &mta); pthread_mutex_init(&m_op_arg_mutex, &mta); pthread_mutex_init(&m_aqueue_lock, &mta); pthread_mutexattr_destroy(&mta); pthread_cond_init(&m_op_arg_cond, nullptr); pthread_create(&m_op_thread, nullptr, op_main, this); pthread_cond_init(&m_refresh_cond, nullptr); pthread_mutex_init(&m_refresh_mutex, nullptr); } virtual ~DeckLinkConsumer() { mlt_log_debug(getConsumer(), "%s: entering\n", __FUNCTION__); SAFE_RELEASE(m_displayMode); SAFE_RELEASE(m_deckLinkKeyer); SAFE_RELEASE(m_deckLinkOutput); SAFE_RELEASE(m_deckLink); mlt_deque_close(m_aqueue); mlt_deque_close(m_frames); op(OP_EXIT, 0); mlt_log_debug(getConsumer(), "%s: waiting for op thread\n", __FUNCTION__); pthread_join(m_op_thread, nullptr); mlt_log_debug(getConsumer(), "%s: finished op thread\n", __FUNCTION__); pthread_mutex_destroy(&m_aqueue_lock); pthread_mutex_destroy(&m_op_lock); pthread_mutex_destroy(&m_op_arg_mutex); pthread_cond_destroy(&m_op_arg_cond); pthread_mutex_lock(&m_refresh_mutex); if (m_refresh < 2) m_refresh = m_refresh <= 0 ? 1 : m_refresh + 1; pthread_cond_broadcast(&m_refresh_cond); pthread_mutex_unlock(&m_refresh_mutex); pthread_mutex_destroy(&m_refresh_mutex); pthread_cond_destroy(&m_refresh_cond); mlt_log_debug(getConsumer(), "%s: exiting\n", __FUNCTION__); } int op(int op_id, int arg) { int r; // lock operation mutex pthread_mutex_lock(&m_op_lock); mlt_log_debug(getConsumer(), "%s: op_id=%d\n", __FUNCTION__, op_id); // notify op id pthread_mutex_lock(&m_op_arg_mutex); m_op_id = op_id; m_op_arg = arg; pthread_cond_signal(&m_op_arg_cond); pthread_mutex_unlock(&m_op_arg_mutex); // wait op done pthread_mutex_lock(&m_op_arg_mutex); while (OP_NONE != m_op_id) pthread_cond_wait(&m_op_arg_cond, &m_op_arg_mutex); pthread_mutex_unlock(&m_op_arg_mutex); // save result r = m_op_res; mlt_log_debug(getConsumer(), "%s: r=%d\n", __FUNCTION__, r); // unlock operation mutex pthread_mutex_unlock(&m_op_lock); return r; } void refresh() { pthread_mutex_lock(&m_refresh_mutex); m_refresh = CLAMP(m_refresh + 1, 0, 2); pthread_cond_broadcast(&m_refresh_cond); pthread_mutex_unlock(&m_refresh_mutex); } void purge() { if (!m_running) return; pthread_mutex_lock(&m_aqueue_lock); // When playing rewind or fast forward then we need to keep one // frame in the queue to prevent playback stalling. int n = (m_currentSpeed == 0 || m_currentSpeed == 1) ? 0 : 1; while (mlt_deque_count(m_aqueue) > n) mlt_frame_close(mlt_frame(mlt_deque_pop_back(m_aqueue))); m_purge = true; m_deckLinkOutput->FlushBufferedAudioSamples(); pthread_mutex_unlock(&m_aqueue_lock); } protected: static void *op_main(void *thisptr) { DeckLinkConsumer *d = static_cast(thisptr); mlt_log_debug(d->getConsumer(), "%s: entering\n", __FUNCTION__); for (;;) { int o, r = 0; // wait op command pthread_mutex_lock(&d->m_op_arg_mutex); while (OP_NONE == d->m_op_id) pthread_cond_wait(&d->m_op_arg_cond, &d->m_op_arg_mutex); pthread_mutex_unlock(&d->m_op_arg_mutex); o = d->m_op_id; mlt_log_debug(d->getConsumer(), "%s:%d d->m_op_id=%d\n", __FUNCTION__, __LINE__, d->m_op_id); switch (d->m_op_id) { case OP_OPEN: r = d->m_op_res = d->open(d->m_op_arg); break; case OP_START: r = d->m_op_res = d->start(d->m_op_arg); break; case OP_STOP: r = d->m_op_res = d->stop(); break; }; // notify op done pthread_mutex_lock(&d->m_op_arg_mutex); d->m_op_id = OP_NONE; pthread_cond_signal(&d->m_op_arg_cond); pthread_mutex_unlock(&d->m_op_arg_mutex); // post for async if (OP_START == o && r) d->preroll(); if (OP_EXIT == o) { mlt_log_debug(d->getConsumer(), "%s: exiting\n", __FUNCTION__); return nullptr; } }; return nullptr; } bool open(unsigned card = 0) { unsigned i = 0; #ifdef _WIN32 IDeckLinkIterator *deckLinkIterator = nullptr; HRESULT result = CoInitialize(nullptr); if (FAILED(result)) { mlt_log_error(getConsumer(), "COM initialization failed\n"); return false; } result = CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, (void **) &deckLinkIterator); if (FAILED(result)) { mlt_log_warning(getConsumer(), "The DeckLink drivers not installed.\n"); return false; } #else IDeckLinkIterator *deckLinkIterator = CreateDeckLinkIteratorInstance(); if (!deckLinkIterator) { mlt_log_warning(getConsumer(), "The DeckLink drivers not installed.\n"); return false; } #endif // Connect to the Nth DeckLink instance for (i = 0; deckLinkIterator->Next(&m_deckLink) == S_OK; i++) { if (i == card) break; else SAFE_RELEASE(m_deckLink); } SAFE_RELEASE(deckLinkIterator); if (!m_deckLink) { mlt_log_error(getConsumer(), "DeckLink card not found\n"); return false; } // Obtain the audio/video output interface (IDeckLinkOutput) if (m_deckLink->QueryInterface(IID_IDeckLinkOutput, (void **) &m_deckLinkOutput) != S_OK) { mlt_log_error(getConsumer(), "No DeckLink cards support output\n"); SAFE_RELEASE(m_deckLink); return false; } // Get the keyer interface IDeckLinkProfileAttributes *deckLinkAttributes = 0; if (m_deckLink->QueryInterface(IID_IDeckLinkProfileAttributes, (void **) &deckLinkAttributes) == S_OK) { #ifdef _WIN32 BOOL flag = FALSE; #else bool flag = false; #endif if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &flag) == S_OK && flag) { if (m_deckLink->QueryInterface(IID_IDeckLinkKeyer, (void **) &m_deckLinkKeyer) != S_OK) { mlt_log_error(getConsumer(), "Failed to get keyer\n"); SAFE_RELEASE(m_deckLinkOutput); SAFE_RELEASE(m_deckLink); return false; } } SAFE_RELEASE(deckLinkAttributes); } // Provide this class as a delegate to the audio and video output interfaces m_deckLinkOutput->SetScheduledFrameCompletionCallback(this); m_deckLinkOutput->SetAudioCallback(this); return true; } int preroll() { mlt_log_debug(getConsumer(), "%s: starting\n", __FUNCTION__); if (!m_running) return 0; mlt_log_debug(getConsumer(), "preroll %u frames\n", m_preroll); // preroll frames for (unsigned i = 0; i < m_preroll; i++) ScheduleNextFrame(true); // start audio preroll if (m_isAudio) m_deckLinkOutput->BeginAudioPreroll(); else m_deckLinkOutput->StartScheduledPlayback(0, m_timescale, 1.0); mlt_log_debug(getConsumer(), "%s: exiting\n", __FUNCTION__); return 0; } bool start(unsigned preroll) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(getConsumer()); // Initialize members m_count = 0; m_buffer = nullptr; preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll; m_inChannels = mlt_properties_get_int(properties, "channels"); if (m_inChannels <= 2) { m_outChannels = 2; } else if (m_inChannels <= 8) { m_outChannels = 8; } else { m_outChannels = 16; } auto channelLayout = mlt_audio_channel_layout_id( mlt_properties_get(properties, "channel_layout")); m_fix5p1 = (channelLayout == mlt_channel_auto && m_inChannels == 6) || channelLayout == mlt_channel_5p1 || channelLayout == mlt_channel_5p1_back; m_isAudio = !mlt_properties_get_int(properties, "audio_off"); m_terminate_on_pause = mlt_properties_get_int(properties, "terminate_on_pause"); m_displayMode = getDisplayMode(); if (!m_displayMode) { mlt_log_error(getConsumer(), "Profile is not compatible with decklink.\n"); return false; } mlt_properties_set_int(properties, "top_field_first", m_displayMode->GetFieldDominance() == bmdUpperFieldFirst); // Set the keyer if (m_deckLinkKeyer && (m_isKeyer = mlt_properties_get_int(properties, "keyer"))) { bool external = (m_isKeyer == 2); double level = mlt_properties_get_double(properties, "keyer_level"); if (m_deckLinkKeyer->Enable(external) != S_OK) mlt_log_error(getConsumer(), "Failed to enable %s keyer\n", external ? "external" : "internal"); m_deckLinkKeyer->SetLevel(level <= 1 ? (level > 0 ? 255 * level : 255) : 255); } else if (m_deckLinkKeyer) { m_deckLinkKeyer->Disable(); } // Set the video output mode if (S_OK != m_deckLinkOutput->EnableVideoOutput(m_displayMode->GetDisplayMode(), (BMDVideoOutputFlags) (bmdVideoOutputFlagDefault | bmdVideoOutputRP188 | bmdVideoOutputVITC))) { mlt_log_error(getConsumer(), "Failed to enable video output\n"); return false; } // Set the audio output mode if (m_isAudio && S_OK != m_deckLinkOutput->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, m_outChannels, bmdAudioOutputStreamContinuous)) { mlt_log_error(getConsumer(), "Failed to enable audio output\n"); stop(); return false; } m_preroll = preroll; m_reprio = 2; for (unsigned i = 0; i < (m_preroll + 2); i++) { IDeckLinkMutableVideoFrame *frame; // Generate a DeckLink video frame if (S_OK != m_deckLinkOutput->CreateVideoFrame(m_width, m_height, m_width * (m_isKeyer ? 4 : 2), m_isKeyer ? bmdFormat8BitARGB : bmdFormat8BitYUV, bmdFrameFlagDefault, &frame)) { mlt_log_error(getConsumer(), "%s: CreateVideoFrame (%d) failed\n", __FUNCTION__, i); return false; } mlt_deque_push_back(m_frames, frame); } pthread_mutex_lock(&m_refresh_mutex); m_refresh = 0; pthread_mutex_unlock(&m_refresh_mutex); // Set the running state m_running = true; return true; } bool stop() { if (!m_running) return false; m_running = false; // Unlatch the consumer thread pthread_mutex_lock(&m_refresh_mutex); pthread_cond_broadcast(&m_refresh_cond); pthread_mutex_unlock(&m_refresh_mutex); mlt_log_debug(getConsumer(), "%s: starting\n", __FUNCTION__); // Stop the audio and video output streams immediately if (m_deckLinkOutput) { m_deckLinkOutput->StopScheduledPlayback(0, 0, 0); m_deckLinkOutput->DisableAudioOutput(); m_deckLinkOutput->DisableVideoOutput(); } pthread_mutex_lock(&m_aqueue_lock); while (mlt_frame frame = (mlt_frame) mlt_deque_pop_back(m_aqueue)) mlt_frame_close(frame); pthread_mutex_unlock(&m_aqueue_lock); m_buffer = nullptr; while (IDeckLinkMutableVideoFrame *frame = (IDeckLinkMutableVideoFrame *) mlt_deque_pop_back(m_frames)) SAFE_RELEASE(frame); mlt_consumer_stopped(getConsumer()); mlt_log_debug(getConsumer(), "%s: exiting\n", __FUNCTION__); return true; } void renderAudio(mlt_frame frame) { mlt_properties properties; properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set_int64(properties, "m_count", m_count); mlt_properties_inc_ref(properties); pthread_mutex_lock(&m_aqueue_lock); mlt_deque_push_back(m_aqueue, frame); mlt_log_debug(getConsumer(), "%s:%d frame=%p, len=%d\n", __FUNCTION__, __LINE__, frame, mlt_deque_count(m_aqueue)); pthread_mutex_unlock(&m_aqueue_lock); } void renderVideo(mlt_frame frame) { HRESULT hr; mlt_image_format format = m_isKeyer ? mlt_image_rgba : mlt_image_yuv422; uint8_t *image = 0; int rendered = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "rendered"); mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES(getConsumer()); int stride = m_width * (m_isKeyer ? 4 : 2); int height = m_height; IDeckLinkMutableVideoFrame *decklinkFrame = static_cast( mlt_deque_pop_front(m_frames)); mlt_log_debug(getConsumer(), "%s: entering\n", __FUNCTION__); m_sliced_swab = mlt_properties_get_int(consumer_properties, "sliced_swab"); if (rendered && !mlt_frame_get_image(frame, &image, &format, &m_width, &height, 0)) { if (decklinkFrame) { IDeckLinkVideoBuffer *videoBuffer = nullptr; if (decklinkFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void **) &videoBuffer) == S_OK) { if (videoBuffer->StartAccess(bmdBufferAccessWrite) == S_OK) { videoBuffer->GetBytes((void **) &m_buffer); videoBuffer->EndAccess(bmdBufferAccessWrite); } videoBuffer->Release(); videoBuffer = nullptr; } } if (m_buffer) { // NTSC SDI is always 486 lines if (m_height == 486 && height == 480) { // blank first 6 lines if (m_isKeyer) { memset(m_buffer, 0, stride * 6); m_buffer += stride * 6; } else for (int i = 0; i < m_width * 6; i++) { *m_buffer++ = 128; *m_buffer++ = 16; } } if (!m_isKeyer) { unsigned char *arg[3] = {image, m_buffer}; ssize_t size = stride * height; // Normal non-keyer playout - needs byte swapping if (!m_sliced_swab) swab2(arg[0], arg[1], size); else { arg[2] = (unsigned char *) size; mlt_slices_run_fifo(0, swab_sliced, arg); } } else if (!mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "test_image")) { // Normal keyer output int y = height + 1; uint32_t *s = (uint32_t *) image; uint32_t *d = (uint32_t *) m_buffer; // Need to relocate alpha channel RGBA => ARGB while (--y) { int x = m_width + 1; while (--x) { *d++ = (*s << 8) | (*s >> 24); s++; } } } else { // Keying blank frames - nullify alpha memset(m_buffer, 0, stride * height); } } } else if (decklinkFrame) { uint8_t *buffer = nullptr; IDeckLinkVideoBuffer *videoBuffer = nullptr; if (decklinkFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void **) &videoBuffer) == S_OK) { if (videoBuffer->StartAccess(bmdBufferAccessWrite) == S_OK) { videoBuffer->GetBytes((void **) &buffer); if (buffer) memcpy(buffer, m_buffer, stride * height); videoBuffer->EndAccess(bmdBufferAccessWrite); } videoBuffer->Release(); videoBuffer = nullptr; } } if (decklinkFrame) { char *vitc; // set timecode vitc = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "meta.attr.vitc.markup"); if (vitc) { int h, m, s, f; if (4 == sscanf(vitc, "%d:%d:%d:%d", &h, &m, &s, &f)) decklinkFrame->SetTimecodeFromComponents(bmdTimecodeVITC, h, m, s, f, bmdTimecodeFlagDefault); } // set userbits vitc = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "meta.attr.vitc.userbits"); if (vitc) decklinkFrame ->SetTimecodeUserBits(bmdTimecodeVITC, mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "meta.attr.vitc.userbits")); // Set color space IDeckLinkVideoFrameMutableMetadataExtensions *frameMeta = nullptr; if (decklinkFrame->QueryInterface(IID_IDeckLinkVideoFrameMutableMetadataExtensions, (void **) &frameMeta) == S_OK) { const char *colorspace_str = mlt_properties_get(consumer_properties, "colorspace"); auto colorspace = bmdColorspaceRec709; switch (mlt_image_colorspace_id(colorspace_str)) { case mlt_colorspace_bt601: colorspace = bmdColorspaceRec601; break; case mlt_colorspace_bt2020_ncl: colorspace = bmdColorspaceRec2020; break; default: break; } frameMeta->SetInt(bmdDeckLinkFrameMetadataColorspace, colorspace); // Set HDR a transfer function const char *trc_str = mlt_properties_get(consumer_properties, "color_trc"); mlt_color_trc trc = mlt_image_color_trc_id(trc_str); if (trc == mlt_color_trc_arib_std_b67) { frameMeta->SetInt(bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc, EOTF_HLG); decklinkFrame->SetFlags(decklinkFrame->GetFlags() & ~bmdFrameContainsHDRMetadata); } else if (trc == mlt_color_trc_smpte2084) { frameMeta->SetInt(bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc, EOTF_PQ); // CEA/SMPTE HDR metadata // TODO: document and provide defaults for these decklinkFrame->SetFlags(decklinkFrame->GetFlags() | bmdFrameContainsHDRMetadata); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX, mlt_properties_get_double(consumer_properties, "hdr_red_x")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY, mlt_properties_get_double(consumer_properties, "hdr_red_y")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX, mlt_properties_get_double(consumer_properties, "hdr_green_x")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY, mlt_properties_get_double(consumer_properties, "hdr.green_y")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX, mlt_properties_get_double(consumer_properties, "hdr_blue_x")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY, mlt_properties_get_double(consumer_properties, "hdr_blue_y")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRWhitePointX, mlt_properties_get_double(consumer_properties, "hdr_white_x")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRWhitePointY, mlt_properties_get_double(consumer_properties, "hdr_white_y")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance, mlt_properties_get_double(consumer_properties, "hdr_max_luminance")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance, mlt_properties_get_double(consumer_properties, "hdr_min_luminance")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel, mlt_properties_get_double(consumer_properties, "hdr_max_cll")); frameMeta->SetFloat(bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel, mlt_properties_get_double(consumer_properties, "hdr_max_fall")); } } else { mlt_log_debug(getConsumer(), "Unable to get IDeckLinkVideoFrameMutableMetadataExtensions\n"); } hr = m_deckLinkOutput->ScheduleVideoFrame(decklinkFrame, m_count * m_duration, m_duration, m_timescale); if (S_OK != hr) mlt_log_error(getConsumer(), "%s:%d: ScheduleVideoFrame failed, hr=%.8X \n", __FUNCTION__, __LINE__, unsigned(hr)); else mlt_log_debug(getConsumer(), "%s: ScheduleVideoFrame SUCCESS\n", __FUNCTION__); } } HRESULT render(mlt_frame frame) { HRESULT result = S_OK; // Get the audio auto scrub = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(getConsumer()), "scrub_audio"); if (m_isAudio && (m_currentSpeed == 1 || (scrub && m_currentSpeed == 0))) renderAudio(frame); // Get the video renderVideo(frame); ++m_count; return result; } void reprio(int target) { int r; pthread_t thread; pthread_attr_t tattr; struct sched_param param; mlt_properties properties; if (m_reprio & target) return; m_reprio |= target; properties = MLT_CONSUMER_PROPERTIES(getConsumer()); if (!mlt_properties_get(properties, "priority")) return; pthread_attr_init(&tattr); pthread_attr_setschedpolicy(&tattr, SCHED_FIFO); if (!strcmp("max", mlt_properties_get(properties, "priority"))) param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1; else if (!strcmp("min", mlt_properties_get(properties, "priority"))) param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1; else param.sched_priority = mlt_properties_get_int(properties, "priority"); pthread_attr_setschedparam(&tattr, ¶m); thread = pthread_self(); r = pthread_setschedparam(thread, SCHED_FIFO, ¶m); if (r) mlt_log_error(getConsumer(), "%s: [%d] pthread_setschedparam returned %d\n", __FUNCTION__, target, r); else mlt_log_verbose(getConsumer(), "%s: [%d] param.sched_priority=%d\n", __FUNCTION__, target, param.sched_priority); } // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** // // IUnknown needs only a dummy implementation virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; } virtual ULONG STDMETHODCALLTYPE AddRef() { return 1; } virtual ULONG STDMETHODCALLTYPE Release() { return 1; } /************************* DeckLink API Delegate Methods *****************************/ #ifdef _WIN32 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll) #else virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(bool preroll) #endif { pthread_mutex_lock(&m_aqueue_lock); mlt_log_debug(getConsumer(), "%s: ENTERING preroll=%d, len=%d\n", __FUNCTION__, (int) preroll, mlt_deque_count(m_aqueue)); mlt_frame frame = (mlt_frame) mlt_deque_pop_front(m_aqueue); pthread_mutex_unlock(&m_aqueue_lock); reprio(2); if (frame) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); uint64_t m_count = mlt_properties_get_int64(properties, "m_count"); mlt_audio_format format = mlt_audio_s16; int frequency = bmdAudioSampleRate48kHz; int samples = mlt_audio_calculate_frame_samples(m_fps, frequency, m_count); int16_t *pcm = 0; if (!mlt_frame_get_audio(frame, (void **) &pcm, &format, &frequency, &m_inChannels, &samples)) { HRESULT hr; int16_t *outBuff = nullptr; mlt_log_debug(getConsumer(), "%s:%d, samples=%d, channels=%d, freq=%d\n", __FUNCTION__, __LINE__, samples, m_inChannels, frequency); if (m_inChannels != m_outChannels) { int size = mlt_audio_format_size(format, samples, m_outChannels); int16_t *src = pcm; int16_t *dst = outBuff = (int16_t *) mlt_pool_alloc(size); for (int s = 0; s < samples; s++) { for (int c = 0; c < m_outChannels; c++) { int cOut = m_fix5p1 ? c == 2 ? 3 : c == 3 ? 2 : c : c; // Fill silence if there are more out channels than in channels. dst[s * m_outChannels + cOut] = (c < m_inChannels) ? src[s * m_inChannels + c] : 0; } } pcm = outBuff; } #ifdef _WIN32 #define DECKLINK_UNSIGNED_FORMAT "%lu" unsigned long written = 0; #else #define DECKLINK_UNSIGNED_FORMAT "%u" uint32_t written = 0; #endif BMDTimeValue streamTime = m_count * frequency * m_duration / m_timescale; #ifdef _WIN32 hr = m_deckLinkOutput->ScheduleAudioSamples(pcm, samples, streamTime, frequency, (unsigned int *) &written); #else hr = m_deckLinkOutput->ScheduleAudioSamples(pcm, samples, streamTime, frequency, &written); #endif if (S_OK != hr) mlt_log_error(getConsumer(), "%s:%d ScheduleAudioSamples failed, hr=%.8X \n", __FUNCTION__, __LINE__, unsigned(hr)); else mlt_log_debug(getConsumer(), "%s:%d ScheduleAudioSamples success " DECKLINK_UNSIGNED_FORMAT " samples\n", __FUNCTION__, __LINE__, written); if (written != (uint32_t) samples) mlt_log_verbose(getConsumer(), "renderAudio: samples=%d, written=" DECKLINK_UNSIGNED_FORMAT "\n", samples, written); mlt_pool_release(outBuff); } else mlt_log_error(getConsumer(), "%s:%d mlt_frame_get_audio failed\n", __FUNCTION__, __LINE__); mlt_frame_close(frame); if (!preroll) RenderAudioSamples(preroll); } if (preroll) m_deckLinkOutput->StartScheduledPlayback(0, m_timescale, 1.0); return S_OK; } virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame *completedFrame, BMDOutputFrameCompletionResult completed) { mlt_log_debug(getConsumer(), "%s: ENTERING\n", __FUNCTION__); mlt_deque_push_back(m_frames, completedFrame); // change priority of video callback thread reprio(1); // When a video frame has been released by the API, schedule another video frame to be output // ignore handler if frame was flushed if (bmdOutputFrameFlushed == completed) return S_OK; // schedule next frame ScheduleNextFrame(false); // step forward frames counter if underrun if (bmdOutputFrameDisplayedLate == completed) { mlt_log_debug(getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate\n"); } if (bmdOutputFrameDropped == completed) { mlt_log_debug(getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped\n"); m_count++; ScheduleNextFrame(false); } return S_OK; } virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped() { return mlt_consumer_is_stopped(getConsumer()) ? S_FALSE : S_OK; } void ScheduleNextFrame(bool preroll) { // get the consumer mlt_consumer consumer = getConsumer(); // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Frame and size mlt_frame frame = nullptr; mlt_log_debug(getConsumer(), "%s:%d: preroll=%d\n", __FUNCTION__, __LINE__, preroll); while (!frame && (m_running || preroll)) { mlt_log_timings_begin(); frame = mlt_consumer_rt_frame(consumer); mlt_log_timings_end(nullptr, "mlt_consumer_rt_frame"); if (frame) { m_currentSpeed = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "_speed"); if (m_running) { if (m_purge && m_currentSpeed == 1.0) { mlt_frame_close(frame); frame = nullptr; m_purge = false; continue; } else { mlt_log_timings_begin(); render(frame); mlt_log_timings_end(nullptr, "render"); mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } if (m_currentSpeed || preroll) { if (!preroll && m_previousSpeed != 1 && m_currentSpeed == 1) { // Resume mlt_log_verbose(getConsumer(), "Resuming forward 1x playback\n"); m_deckLinkOutput->StopScheduledPlayback(0, nullptr, 0); m_count = 0; if (m_isAudio) m_deckLinkOutput->BeginAudioPreroll(); else m_deckLinkOutput->StartScheduledPlayback(0, m_timescale, 1.0); } } else { pthread_mutex_lock(&m_refresh_mutex); if (--m_refresh <= 0) pthread_cond_wait(&m_refresh_cond, &m_refresh_mutex); pthread_mutex_unlock(&m_refresh_mutex); mlt_consumer_purge(consumer); } } // terminate on pause if (m_terminate_on_pause && !m_currentSpeed) stop(); mlt_frame_close(frame); m_previousSpeed = m_currentSpeed; } else mlt_log_warning(getConsumer(), "%s: mlt_consumer_rt_frame return nullptr\n", __FUNCTION__); } } }; /** Start the consumer. */ static int start(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); DeckLinkConsumer *decklink = (DeckLinkConsumer *) consumer->child; return decklink->op(OP_START, mlt_properties_get_int(properties, "preroll")) ? 0 : 1; } /** Stop the consumer. */ static int stop(mlt_consumer consumer) { int r; mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__); // Get the properties DeckLinkConsumer *decklink = (DeckLinkConsumer *) consumer->child; r = decklink->op(OP_STOP, 0); mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__); return r; } /** Determine if the consumer is stopped. */ static int is_stopped(mlt_consumer consumer) { auto *decklink = (DeckLinkConsumer *) consumer->child; return !decklink->isRunning(); } /** Close the consumer. */ static void close(mlt_consumer consumer) { mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__); // Stop the consumer mlt_consumer_stop(consumer); // Close the parent consumer->close = nullptr; mlt_consumer_close(consumer); // Free the memory delete (DeckLinkConsumer *) consumer->child; mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__); } extern "C" { // Listen for the list_devices property to be set static void on_property_changed(void *, mlt_consumer consumer, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); auto properties = MLT_CONSUMER_PROPERTIES(consumer); IDeckLinkIterator *decklinkIterator = nullptr; IDeckLink *decklink = nullptr; IDeckLinkInput *decklinkOutput = nullptr; int i = 0; if (name) { if (!strcmp(name, "list_devices")) { mlt_event_block( (mlt_event) mlt_properties_get_data(properties, "list-devices-event", nullptr)); } else if (!strcmp(name, "refresh")) { auto *decklink = (DeckLinkConsumer *) consumer->child; decklink->refresh(); return; } else { return; } } #ifdef _WIN32 if (FAILED(CoInitialize(nullptr))) return; if (FAILED(CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, (void **) &decklinkIterator))) return; #else if (!(decklinkIterator = CreateDeckLinkIteratorInstance())) return; #endif for (; decklinkIterator->Next(&decklink) == S_OK; i++) { if (decklink->QueryInterface(IID_IDeckLinkOutput, (void **) &decklinkOutput) == S_OK) { DLString name = nullptr; if (decklink->GetModelName(&name) == S_OK) { char *name_cstr = getCString(name); const char *format = "device.%d"; char *key = (char *) calloc(1, strlen(format) + 10); sprintf(key, format, i); mlt_properties_set(properties, key, name_cstr); free(key); freeDLString(name); freeCString(name_cstr); } SAFE_RELEASE(decklinkOutput); } SAFE_RELEASE(decklink); } SAFE_RELEASE(decklinkIterator); mlt_properties_set_int(properties, "devices", i); } void purge(mlt_consumer consumer) { DeckLinkConsumer *decklink = (DeckLinkConsumer *) consumer->child; decklink->purge(); } /** Initialise the consumer. */ mlt_consumer consumer_decklink_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Allocate the consumer DeckLinkConsumer *decklink = new DeckLinkConsumer(); mlt_consumer consumer = nullptr; // If allocated if (!mlt_consumer_init(decklink->getConsumer(), decklink, profile)) { // If initialises without error if (decklink->op(OP_OPEN, arg ? atoi(arg) : 0)) { consumer = decklink->getConsumer(); mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Setup callbacks consumer->close = close; consumer->start = start; consumer->stop = stop; consumer->is_stopped = is_stopped; consumer->purge = purge; mlt_properties_set(properties, "consumer.deinterlacer", "onefield"); mlt_event event = mlt_events_listen(properties, consumer, "property-changed", (mlt_listener) on_property_changed); mlt_properties_set_data(properties, "list-devices-event", event, 0, nullptr, nullptr); } } // Return consumer return consumer; } extern mlt_producer producer_decklink_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; const char *service_type = nullptr; switch (type) { case mlt_service_consumer_type: service_type = "consumer"; break; case mlt_service_producer_type: service_type = "producer"; break; default: return nullptr; } snprintf(file, PATH_MAX, "%s/decklink/%s_%s.yml", mlt_environment("MLT_DATA"), service_type, id); return mlt_properties_parse_yaml(file); } MLTDECKLINK_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "decklink", consumer_decklink_init); MLT_REGISTER(mlt_service_producer_type, "decklink", producer_decklink_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "decklink", metadata, nullptr); MLT_REGISTER_METADATA(mlt_service_producer_type, "decklink", metadata, nullptr); } } // extern C mlt-7.38.0/src/modules/decklink/consumer_decklink.yml000664 000000 000000 00000006162 15172202314 022634 0ustar00rootroot000000 000000 schema_version: 7.0 type: consumer identifier: decklink title: Blackmagic Design DeckLink Output version: 3 copyright: Copyright (C) 2010-2025 Meltytech, LLC license: LGPL language: en creator: Dan Dennedy tags: - Audio - Video description: > Output audio and video using Blackmagic Design DeckLink SDI or Intensity HDMI cards. notes: > Please ensure that you use a MLT profile that is compatible with a broadcast standard which the card you are using supports. bugs: - Only internal keying is supported at this time. - Only 8-bit Y'CbCr or RGBA (key) is supported at this time. parameters: - identifier: resource argument: yes title: Card type: integer readonly: no required: no mutable: no default: 0 minimum: 0 widget: spinner - identifier: preroll title: Pre-roll Count type: integer description: > This controls the amount of buffering in the DeckLink driver/library. Increase this if you get video tearing or choppy audio. However, as you increase the amount, you increase the risk of audio and video becoming out of synchronization. readonly: no required: no mutable: no default: 3 minimum: 2 unit: frames widget: spinner - identifier: keyer title: Enable Keyer type: integer description: > Keying is the process of compositing MLT output over a live SDI input. The alpha channel of the MLT video controls the transparent areas, and the keyer supports alpha-blending. You can not control the compositing rectangle. Rather, the entire MLT output overlays the entire video input. Therefore, you must use MLT's compositing services to control the size and position. The value 1 enables the internal keyer, the value 2 enables the external keyer, and the value 0 disables it. readonly: no required: no mutable: no default: 0 minimum: 0 maximum: 2 - identifier: keyer_level title: Key Opacity type: float description: > This controls the level of blending between the key and the input video. 1 is fully opaque and something near 0 is transparent. However, absolute 0 is considered as "not supplied" and also fully opaque. 0.5 is an evenly balanced blending of the key and input video. readonly: no required: no mutable: no minimum: 0 maximum: 1 default: 1 widget: slider - identifier: devices title: Number of devices type: integer readonly: yes minimum: 0 - identifier: device.* title: Device model description: The model name of each device that provides output. type: string readonly: yes - identifier: sliced_swab title: Use sliced swab operation description: This option enables multithreaded parallel swab frame data operation type: boolean readonly: no minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: terminate_on_pause title: Stop automatically type: boolean description: > Whether to stop playback at the end of the producer or when playback is paused. default: 0 widget: checkbox mlt-7.38.0/src/modules/decklink/darwin/000775 000000 000000 00000000000 15172202314 017671 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/decklink/darwin/DeckLinkAPI.h000664 000000 000000 00000255346 15172202314 022077 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation (the ** "Software") to use, reproduce, display, distribute, sub-license, execute, ** and transmit the Software, and to prepare derivative works of the Software, ** and to permit third-parties to whom the Software is furnished to do so, in ** accordance with: ** ** (1) if the Software is obtained from Blackmagic Design, the End User License ** Agreement for the Software Development Kit ("EULA") available at ** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or ** ** (2) if the Software is obtained from any third party, such licensing terms ** as notified by that third party, ** ** and all subject to the following: ** ** (3) the copyright notices in the Software and this entire statement, ** including the above license grant, this restriction and the following ** disclaimer, must be included in all copies of the Software, in whole or in ** part, and all derivative works of the Software, unless such copies or ** derivative works are solely in the form of machine-executable object code ** generated by a source language processor. ** ** (4) 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** ** A copy of the Software is available free of charge at ** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. ** ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPI_H #define BMD_DECKLINKAPI_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif /* DeckLink API */ #include #include #include #include "DeckLinkAPITypes.h" #include "DeckLinkAPIModes.h" #include "DeckLinkAPIDiscovery.h" #include "DeckLinkAPIConfiguration.h" #include "DeckLinkAPIDeckControl.h" #include "DeckLinkAPIStreaming.h" #define BLACKMAGIC_DECKLINK_API_MAGIC 1 // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkVideoOutputCallback = /* 5BE6DF26-02CE-433E-99D9-9A87C3AC171F */ { 0x5B,0xE6,0xDF,0x26,0x02,0xCE,0x43,0x3E,0x99,0xD9,0x9A,0x87,0xC3,0xAC,0x17,0x1F }; BMD_CONST REFIID IID_IDeckLinkInputCallback = /* 3A94F075-C37D-4BA8-BCC0-1D778C8F881B */ { 0x3A,0x94,0xF0,0x75,0xC3,0x7D,0x4B,0xA8,0xBC,0xC0,0x1D,0x77,0x8C,0x8F,0x88,0x1B }; BMD_CONST REFIID IID_IDeckLinkEncoderInputCallback = /* ACF13E61-F4A0-4974-A6A7-59AFF6268B31 */ { 0xAC,0xF1,0x3E,0x61,0xF4,0xA0,0x49,0x74,0xA6,0xA7,0x59,0xAF,0xF6,0x26,0x8B,0x31 }; BMD_CONST REFIID IID_IDeckLinkVideoBufferAllocator = /* 3481A4DF-2B11-4E55-AC61-836B87985E9A */ { 0x34,0x81,0xA4,0xDF,0x2B,0x11,0x4E,0x55,0xAC,0x61,0x83,0x6B,0x87,0x98,0x5E,0x9A }; BMD_CONST REFIID IID_IDeckLinkVideoBufferAllocatorProvider = /* 08B80403-BFF2-49D0-B448-8C908B9E9FC9 */ { 0x08,0xB8,0x04,0x03,0xBF,0xF2,0x49,0xD0,0xB4,0x48,0x8C,0x90,0x8B,0x9E,0x9F,0xC9 }; BMD_CONST REFIID IID_IDeckLinkAudioOutputCallback = /* 403C681B-7F46-4A12-B993-2BB127084EE6 */ { 0x40,0x3C,0x68,0x1B,0x7F,0x46,0x4A,0x12,0xB9,0x93,0x2B,0xB1,0x27,0x08,0x4E,0xE6 }; BMD_CONST REFIID IID_IDeckLinkIterator = /* 50FB36CD-3063-4B73-BDBB-958087F2D8BA */ { 0x50,0xFB,0x36,0xCD,0x30,0x63,0x4B,0x73,0xBD,0xBB,0x95,0x80,0x87,0xF2,0xD8,0xBA }; BMD_CONST REFIID IID_IDeckLinkAPIInformation = /* 7BEA3C68-730D-4322-AF34-8A7152B532A4 */ { 0x7B,0xEA,0x3C,0x68,0x73,0x0D,0x43,0x22,0xAF,0x34,0x8A,0x71,0x52,0xB5,0x32,0xA4 }; BMD_CONST REFIID IID_IDeckLinkIPFlowAttributes = /* CDA938DA-6479-40C6-B2EC-A3579B3AEECD */ { 0xCD,0xA9,0x38,0xDA,0x64,0x79,0x40,0xC6,0xB2,0xEC,0xA3,0x57,0x9B,0x3A,0xEE,0xCD }; BMD_CONST REFIID IID_IDeckLinkIPFlowStatus = /* 31C41656-4992-4396-BBE9-5F8406AAB5AF */ { 0x31,0xC4,0x16,0x56,0x49,0x92,0x43,0x96,0xBB,0xE9,0x5F,0x84,0x06,0xAA,0xB5,0xAF }; BMD_CONST REFIID IID_IDeckLinkIPFlowSetting = /* 86DD9174-27D3-4032-B2AD-6067C3BB2424 */ { 0x86,0xDD,0x91,0x74,0x27,0xD3,0x40,0x32,0xB2,0xAD,0x60,0x67,0xC3,0xBB,0x24,0x24 }; BMD_CONST REFIID IID_IDeckLinkIPFlow = /* C5FC83C7-5B8E-42A7-9A40-7C065955D4E1 */ { 0xC5,0xFC,0x83,0xC7,0x5B,0x8E,0x42,0xA7,0x9A,0x40,0x7C,0x06,0x59,0x55,0xD4,0xE1 }; BMD_CONST REFIID IID_IDeckLinkIPFlowIterator = /* BD296AB2-A5C5-4153-888F-AAB1FDBD8A5C */ { 0xBD,0x29,0x6A,0xB2,0xA5,0xC5,0x41,0x53,0x88,0x8F,0xAA,0xB1,0xFD,0xBD,0x8A,0x5C }; BMD_CONST REFIID IID_IDeckLinkOutput = /* 1A8077F1-9FE2-4533-8147-2294305E253F */ { 0x1A,0x80,0x77,0xF1,0x9F,0xE2,0x45,0x33,0x81,0x47,0x22,0x94,0x30,0x5E,0x25,0x3F }; BMD_CONST REFIID IID_IDeckLinkMacOutput = /* 22EFC84B-6981-4D35-9F9A-9CEC5790107E */ { 0x22,0xEF,0xC8,0x4B,0x69,0x81,0x4D,0x35,0x9F,0x9A,0x9C,0xEC,0x57,0x90,0x10,0x7E }; BMD_CONST REFIID IID_IDeckLinkInput = /* 4095DB82-E294-4B8C-AAA8-3B9E80C49336 */ { 0x40,0x95,0xDB,0x82,0xE2,0x94,0x4B,0x8C,0xAA,0xA8,0x3B,0x9E,0x80,0xC4,0x93,0x36 }; BMD_CONST REFIID IID_IDeckLinkIPExtensions = /* 46CF7903-A9FD-4D0B-8FFC-0103722AB442 */ { 0x46,0xCF,0x79,0x03,0xA9,0xFD,0x4D,0x0B,0x8F,0xFC,0x01,0x03,0x72,0x2A,0xB4,0x42 }; BMD_CONST REFIID IID_IDeckLinkHDMIInputEDID = /* ABBBACBC-45BC-4665-9D92-ACE6E5A97902 */ { 0xAB,0xBB,0xAC,0xBC,0x45,0xBC,0x46,0x65,0x9D,0x92,0xAC,0xE6,0xE5,0xA9,0x79,0x02 }; BMD_CONST REFIID IID_IDeckLinkEncoderInput = /* 46C1332E-6FD9-472A-8591-FE59C22192E1 */ { 0x46,0xC1,0x33,0x2E,0x6F,0xD9,0x47,0x2A,0x85,0x91,0xFE,0x59,0xC2,0x21,0x92,0xE1 }; BMD_CONST REFIID IID_IDeckLinkVideoBuffer = /* CCB4B64A-5C86-4E02-B778-885D352709FE */ { 0xCC,0xB4,0xB6,0x4A,0x5C,0x86,0x4E,0x02,0xB7,0x78,0x88,0x5D,0x35,0x27,0x09,0xFE }; BMD_CONST REFIID IID_IDeckLinkMacVideoBuffer = /* 3FBEDC55-AA43-4EAB-B12F-FA1299C7C324 */ { 0x3F,0xBE,0xDC,0x55,0xAA,0x43,0x4E,0xAB,0xB1,0x2F,0xFA,0x12,0x99,0xC7,0xC3,0x24 }; BMD_CONST REFIID IID_IDeckLinkVideoFrame = /* 6502091C-615F-4F51-BAF6-45C4256DD5B0 */ { 0x65,0x02,0x09,0x1C,0x61,0x5F,0x4F,0x51,0xBA,0xF6,0x45,0xC4,0x25,0x6D,0xD5,0xB0 }; BMD_CONST REFIID IID_IDeckLinkMutableVideoFrame = /* CF9EB134-0374-4C5B-95FA-1EC14819FF62 */ { 0xCF,0x9E,0xB1,0x34,0x03,0x74,0x4C,0x5B,0x95,0xFA,0x1E,0xC1,0x48,0x19,0xFF,0x62 }; BMD_CONST REFIID IID_IDeckLinkVideoFrame3DExtensions = /* D4DBE9C6-B4D2-49D3-ABF2-B4E86C7391B0 */ { 0xD4,0xDB,0xE9,0xC6,0xB4,0xD2,0x49,0xD3,0xAB,0xF2,0xB4,0xE8,0x6C,0x73,0x91,0xB0 }; BMD_CONST REFIID IID_IDeckLinkVideoFrameMetadataExtensions = /* E232A5B7-4DB4-44C9-9152-F47C12E5F051 */ { 0xE2,0x32,0xA5,0xB7,0x4D,0xB4,0x44,0xC9,0x91,0x52,0xF4,0x7C,0x12,0xE5,0xF0,0x51 }; BMD_CONST REFIID IID_IDeckLinkVideoFrameMutableMetadataExtensions = /* CC198FC6-8298-4419-942D-8357EC355E58 */ { 0xCC,0x19,0x8F,0xC6,0x82,0x98,0x44,0x19,0x94,0x2D,0x83,0x57,0xEC,0x35,0x5E,0x58 }; BMD_CONST REFIID IID_IDeckLinkVideoInputFrame = /* C9ADD3D2-BE52-488D-AB2D-7FDEF7AF0C95 */ { 0xC9,0xAD,0xD3,0xD2,0xBE,0x52,0x48,0x8D,0xAB,0x2D,0x7F,0xDE,0xF7,0xAF,0x0C,0x95 }; BMD_CONST REFIID IID_IDeckLinkAncillaryPacket = /* CC5BBF7E-029C-4D3B-9158-6000EF5E3670 */ { 0xCC,0x5B,0xBF,0x7E,0x02,0x9C,0x4D,0x3B,0x91,0x58,0x60,0x00,0xEF,0x5E,0x36,0x70 }; BMD_CONST REFIID IID_IDeckLinkAncillaryPacketIterator = /* 3FC8994B-88FB-4C17-968F-9AAB69D964A7 */ { 0x3F,0xC8,0x99,0x4B,0x88,0xFB,0x4C,0x17,0x96,0x8F,0x9A,0xAB,0x69,0xD9,0x64,0xA7 }; BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillaryPackets = /* 6C186C0F-459E-41D8-AEE2-4812D81AEE68 */ { 0x6C,0x18,0x6C,0x0F,0x45,0x9E,0x41,0xD8,0xAE,0xE2,0x48,0x12,0xD8,0x1A,0xEE,0x68 }; BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillary = /* 732E723C-D1A4-4E29-9E8E-4A88797A0004 */ { 0x73,0x2E,0x72,0x3C,0xD1,0xA4,0x4E,0x29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04 }; BMD_CONST REFIID IID_IDeckLinkEncoderPacket = /* B693F36C-316E-4AF1-B6C2-F389A4BCA620 */ { 0xB6,0x93,0xF3,0x6C,0x31,0x6E,0x4A,0xF1,0xB6,0xC2,0xF3,0x89,0xA4,0xBC,0xA6,0x20 }; BMD_CONST REFIID IID_IDeckLinkEncoderVideoPacket = /* 4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0 */ { 0x4E,0x7F,0xD9,0x44,0xE8,0xC7,0x4E,0xAC,0xB8,0xC0,0x7B,0x77,0xF8,0x0F,0x5A,0xE0 }; BMD_CONST REFIID IID_IDeckLinkEncoderAudioPacket = /* 49E8EDC8-693B-4E14-8EF6-12C658F5A07A */ { 0x49,0xE8,0xED,0xC8,0x69,0x3B,0x4E,0x14,0x8E,0xF6,0x12,0xC6,0x58,0xF5,0xA0,0x7A }; BMD_CONST REFIID IID_IDeckLinkH265NALPacket = /* 639C8E0B-68D5-4BDE-A6D4-95F3AEAFF2E7 */ { 0x63,0x9C,0x8E,0x0B,0x68,0xD5,0x4B,0xDE,0xA6,0xD4,0x95,0xF3,0xAE,0xAF,0xF2,0xE7 }; BMD_CONST REFIID IID_IDeckLinkAudioInputPacket = /* E43D5870-2894-11DE-8C30-0800200C9A66 */ { 0xE4,0x3D,0x58,0x70,0x28,0x94,0x11,0xDE,0x8C,0x30,0x08,0x00,0x20,0x0C,0x9A,0x66 }; BMD_CONST REFIID IID_IDeckLinkScreenPreviewCallback = /* D4FA2345-9FBA-4497-95C3-C0C3CED3CDA8 */ { 0xD4,0xFA,0x23,0x45,0x9F,0xBA,0x44,0x97,0x95,0xC3,0xC0,0xC3,0xCE,0xD3,0xCD,0xA8 }; BMD_CONST REFIID IID_IDeckLinkCocoaScreenPreviewCallback = /* 91945B73-70DE-40A9-A25D-EA00397D0D3A */ { 0x91,0x94,0x5B,0x73,0x70,0xDE,0x40,0xA9,0xA2,0x5D,0xEA,0x00,0x39,0x7D,0x0D,0x3A }; BMD_CONST REFIID IID_IDeckLinkGLScreenPreviewHelper = /* CEB778E2-C202-4EC8-9085-0CD285CC5522 */ { 0xCE,0xB7,0x78,0xE2,0xC2,0x02,0x4E,0xC8,0x90,0x85,0x0C,0xD2,0x85,0xCC,0x55,0x22 }; BMD_CONST REFIID IID_IDeckLinkMetalScreenPreviewHelper = /* C15739C7-A5EF-4F0E-A7CB-6806CB87E032 */ { 0xC1,0x57,0x39,0xC7,0xA5,0xEF,0x4F,0x0E,0xA7,0xCB,0x68,0x06,0xCB,0x87,0xE0,0x32 }; BMD_CONST REFIID IID_IDeckLinkNotificationCallback = /* B002A1EC-070D-4288-8289-BD5D36E5FF0D */ { 0xB0,0x02,0xA1,0xEC,0x07,0x0D,0x42,0x88,0x82,0x89,0xBD,0x5D,0x36,0xE5,0xFF,0x0D }; BMD_CONST REFIID IID_IDeckLinkNotification = /* B85DF4C8-BDF5-47C1-8064-28162EBDD4EB */ { 0xB8,0x5D,0xF4,0xC8,0xBD,0xF5,0x47,0xC1,0x80,0x64,0x28,0x16,0x2E,0xBD,0xD4,0xEB }; BMD_CONST REFIID IID_IDeckLinkProfileAttributes = /* 17D4BF8E-4911-473A-80A0-731CF6FF345B */ { 0x17,0xD4,0xBF,0x8E,0x49,0x11,0x47,0x3A,0x80,0xA0,0x73,0x1C,0xF6,0xFF,0x34,0x5B }; BMD_CONST REFIID IID_IDeckLinkProfileIterator = /* 29E5A8C0-8BE4-46EB-93AC-31DAAB5B7BF2 */ { 0x29,0xE5,0xA8,0xC0,0x8B,0xE4,0x46,0xEB,0x93,0xAC,0x31,0xDA,0xAB,0x5B,0x7B,0xF2 }; BMD_CONST REFIID IID_IDeckLinkProfile = /* 16093466-674A-432B-9DA0-1AC2C5A8241C */ { 0x16,0x09,0x34,0x66,0x67,0x4A,0x43,0x2B,0x9D,0xA0,0x1A,0xC2,0xC5,0xA8,0x24,0x1C }; BMD_CONST REFIID IID_IDeckLinkProfileCallback = /* A4F9341E-97AA-4E04-8935-15F809898CEA */ { 0xA4,0xF9,0x34,0x1E,0x97,0xAA,0x4E,0x04,0x89,0x35,0x15,0xF8,0x09,0x89,0x8C,0xEA }; BMD_CONST REFIID IID_IDeckLinkProfileManager = /* 30D41429-3998-4B6D-84F8-78C94A797C6E */ { 0x30,0xD4,0x14,0x29,0x39,0x98,0x4B,0x6D,0x84,0xF8,0x78,0xC9,0x4A,0x79,0x7C,0x6E }; BMD_CONST REFIID IID_IDeckLinkStatus = /* 5F558200-4028-49BC-BEAC-DB3FA4A96E46 */ { 0x5F,0x55,0x82,0x00,0x40,0x28,0x49,0xBC,0xBE,0xAC,0xDB,0x3F,0xA4,0xA9,0x6E,0x46 }; BMD_CONST REFIID IID_IDeckLinkKeyer = /* 89AFCAF5-65F8-421E-98F7-96FE5F5BFBA3 */ { 0x89,0xAF,0xCA,0xF5,0x65,0xF8,0x42,0x1E,0x98,0xF7,0x96,0xFE,0x5F,0x5B,0xFB,0xA3 }; BMD_CONST REFIID IID_IDeckLinkVideoConversion = /* A48755D9-8BD5-4727-A1E9-069FDEDBA6E9 */ { 0xA4,0x87,0x55,0xD9,0x8B,0xD5,0x47,0x27,0xA1,0xE9,0x06,0x9F,0xDE,0xDB,0xA6,0xE9 }; BMD_CONST REFIID IID_IDeckLinkDeviceNotificationCallback = /* 4997053B-0ADF-4CC8-AC70-7A50C4BE728F */ { 0x49,0x97,0x05,0x3B,0x0A,0xDF,0x4C,0xC8,0xAC,0x70,0x7A,0x50,0xC4,0xBE,0x72,0x8F }; BMD_CONST REFIID IID_IDeckLinkDiscovery = /* CDBF631C-BC76-45FA-B44D-C55059BC6101 */ { 0xCD,0xBF,0x63,0x1C,0xBC,0x76,0x45,0xFA,0xB4,0x4D,0xC5,0x50,0x59,0xBC,0x61,0x01 }; /* Enum BMDBufferAccessFlags - Flags to describe access requirements to a video frame buffer */ typedef uint32_t BMDBufferAccessFlags; enum _BMDBufferAccessFlags { bmdBufferAccessReadAndWrite = 1 << 0 | 1 << 1, bmdBufferAccessRead = 1 << 0, bmdBufferAccessWrite = 1 << 1 }; /* Enum BMDVideoOutputFlags - Flags to control the output of ancillary data along with video. */ typedef uint32_t BMDVideoOutputFlags; enum _BMDVideoOutputFlags { bmdVideoOutputFlagDefault = 0, bmdVideoOutputVANC = 1 << 0, bmdVideoOutputVITC = 1 << 1, bmdVideoOutputRP188 = 1 << 2, bmdVideoOutputDualStream3D = 1 << 4, bmdVideoOutputSynchronizeToPlaybackGroup = 1 << 6, bmdVideoOutputDolbyVision = 1 << 7 }; /* Enum BMDSupportedVideoModeFlags - Flags to describe supported video modes */ typedef uint32_t BMDSupportedVideoModeFlags; enum _BMDSupportedVideoModeFlags { bmdSupportedVideoModeDefault = 0, bmdSupportedVideoModeKeying = 1 << 0, bmdSupportedVideoModeDualStream3D = 1 << 1, bmdSupportedVideoModeSDISingleLink = 1 << 2, bmdSupportedVideoModeSDIDualLink = 1 << 3, bmdSupportedVideoModeSDIQuadLink = 1 << 4, bmdSupportedVideoModeInAnyProfile = 1 << 5, bmdSupportedVideoModePsF = 1 << 6, bmdSupportedVideoModeDolbyVision = 1 << 7 }; /* Enum BMDPacketType - Type of packet */ typedef uint32_t BMDPacketType; enum _BMDPacketType { bmdPacketTypeStreamInterruptedMarker = /* 'sint' */ 0x73696E74, // A packet of this type marks the time when a video stream was interrupted, for example by a disconnected cable bmdPacketTypeStreamData = /* 'sdat' */ 0x73646174 // Regular stream data }; /* Enum BMDFrameFlags - Frame flags */ typedef uint32_t BMDFrameFlags; enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, bmdFrameFlagMonitorOutOnly = 1 << 3, bmdFrameContainsHDRMetadata = 1 << 1, bmdFrameContainsDolbyVisionMetadata = 1 << 4, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ bmdFrameCapturedAsPsF = 1 << 30, bmdFrameHasNoInputSource = 1 << 31 }; /* Enum BMDVideoInputFlags - Flags applicable to video input */ typedef uint32_t BMDVideoInputFlags; enum _BMDVideoInputFlags { bmdVideoInputFlagDefault = 0, bmdVideoInputEnableFormatDetection = 1 << 0, bmdVideoInputDualStream3D = 1 << 1, bmdVideoInputSynchronizeToCaptureGroup = 1 << 2 }; /* Enum BMDVideoInputFormatChangedEvents - Bitmask passed to the VideoInputFormatChanged notification to identify the properties of the input signal that have changed */ typedef uint32_t BMDVideoInputFormatChangedEvents; enum _BMDVideoInputFormatChangedEvents { bmdVideoInputDisplayModeChanged = 1 << 0, bmdVideoInputFieldDominanceChanged = 1 << 1, bmdVideoInputColorspaceChanged = 1 << 2 }; /* Enum BMDDetectedVideoInputFormatFlags - Flags passed to the VideoInputFormatChanged notification to describe the detected video input signal */ typedef uint32_t BMDDetectedVideoInputFormatFlags; enum _BMDDetectedVideoInputFormatFlags { bmdDetectedVideoInputYCbCr422 = 1 << 0, bmdDetectedVideoInputRGB444 = 1 << 1, bmdDetectedVideoInputDualStream3D = 1 << 2, bmdDetectedVideoInput12BitDepth = 1 << 3, bmdDetectedVideoInput10BitDepth = 1 << 4, bmdDetectedVideoInput8BitDepth = 1 << 5 }; /* Enum BMDDeckLinkCapturePassthroughMode - Enumerates whether the video output is electrically connected to the video input or if the clean switching mode is enabled */ typedef uint32_t BMDDeckLinkCapturePassthroughMode; enum _BMDDeckLinkCapturePassthroughMode { bmdDeckLinkCapturePassthroughModeDisabled = /* 'pdis' */ 0x70646973, bmdDeckLinkCapturePassthroughModeDirect = /* 'pdir' */ 0x70646972, bmdDeckLinkCapturePassthroughModeCleanSwitch = /* 'pcln' */ 0x70636C6E }; /* Enum BMDOutputFrameCompletionResult - Frame Completion Callback */ typedef uint32_t BMDOutputFrameCompletionResult; enum _BMDOutputFrameCompletionResult { bmdOutputFrameCompleted, bmdOutputFrameDisplayedLate, bmdOutputFrameDropped, bmdOutputFrameFlushed }; /* Enum BMDReferenceStatus - GenLock input status */ typedef uint32_t BMDReferenceStatus; enum _BMDReferenceStatus { bmdReferenceUnlocked = 0, bmdReferenceNotSupportedByHardware = 1 << 0, bmdReferenceLocked = 1 << 1 }; /* Enum BMDAudioFormat - Audio Format */ typedef uint32_t BMDAudioFormat; enum _BMDAudioFormat { bmdAudioFormatPCM = /* 'lpcm' */ 0x6C70636D // Linear signed PCM samples }; /* Enum BMDAudioSampleRate - Audio sample rates supported for output/input */ typedef uint32_t BMDAudioSampleRate; enum _BMDAudioSampleRate { bmdAudioSampleRate48kHz = 48000 }; /* Enum BMDAudioSampleType - Audio sample sizes supported for output/input */ typedef uint32_t BMDAudioSampleType; enum _BMDAudioSampleType { bmdAudioSampleType16bitInteger = 16, bmdAudioSampleType32bitInteger = 32 }; /* Enum BMDAudioOutputStreamType - Audio output stream type */ typedef uint32_t BMDAudioOutputStreamType; enum _BMDAudioOutputStreamType { bmdAudioOutputStreamContinuous, bmdAudioOutputStreamContinuousDontResample, bmdAudioOutputStreamTimestamped }; /* Enum BMDAncillaryPacketFormat - Ancillary packet format */ typedef uint32_t BMDAncillaryPacketFormat; enum _BMDAncillaryPacketFormat { bmdAncillaryPacketFormatUInt8 = /* 'ui08' */ 0x75693038, bmdAncillaryPacketFormatUInt16 = /* 'ui16' */ 0x75693136, bmdAncillaryPacketFormatYCbCr10 = /* 'v210' */ 0x76323130 }; /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ typedef uint32_t BMDTimecodeFormat; enum _BMDTimecodeFormat { bmdTimecodeRP188VITC1 = /* 'rpv1' */ 0x72707631, // RP188 timecode where DBB1 equals VITC1 (line 9) bmdTimecodeRP188VITC2 = /* 'rp12' */ 0x72703132, // RP188 timecode where DBB1 equals VITC2 (line 9 for progressive or line 571 for interlaced/PsF) bmdTimecodeRP188LTC = /* 'rplt' */ 0x72706C74, // RP188 timecode where DBB1 equals LTC (line 10) bmdTimecodeRP188HighFrameRate = /* 'rphr' */ 0x72706872, // RP188 timecode where DBB1 is an HFRTC (SMPTE ST 12-3), the only timecode allowing the frame value to go above 30 bmdTimecodeRP188Any = /* 'rp18' */ 0x72703138, // Convenience for capture, returning the first valid timecode in {HFRTC (if supported), VITC1, VITC2, LTC } bmdTimecodeVITC = /* 'vitc' */ 0x76697463, bmdTimecodeVITCField2 = /* 'vit2' */ 0x76697432, bmdTimecodeSerial = /* 'seri' */ 0x73657269 }; /* Enum BMDAnalogVideoFlags - Analog video display flags */ typedef uint32_t BMDAnalogVideoFlags; enum _BMDAnalogVideoFlags { bmdAnalogVideoFlagCompositeSetup75 = 1 << 0, bmdAnalogVideoFlagComponentBetacamLevels = 1 << 1 }; /* Enum BMDAudioOutputAnalogAESSwitch - Audio output Analog/AESEBU switch */ typedef uint32_t BMDAudioOutputAnalogAESSwitch; enum _BMDAudioOutputAnalogAESSwitch { bmdAudioOutputSwitchAESEBU = /* 'aes ' */ 0x61657320, bmdAudioOutputSwitchAnalog = /* 'anlg' */ 0x616E6C67 }; /* Enum BMDVideoOutputConversionMode - Video/audio conversion mode */ typedef uint32_t BMDVideoOutputConversionMode; enum _BMDVideoOutputConversionMode { bmdNoVideoOutputConversion = /* 'none' */ 0x6E6F6E65, bmdVideoOutputLetterboxDownconversion = /* 'ltbx' */ 0x6C746278, bmdVideoOutputAnamorphicDownconversion = /* 'amph' */ 0x616D7068, bmdVideoOutputHD720toHD1080Conversion = /* '720c' */ 0x37323063, bmdVideoOutputHardwareLetterboxDownconversion = /* 'HWlb' */ 0x48576C62, bmdVideoOutputHardwareAnamorphicDownconversion = /* 'HWam' */ 0x4857616D, bmdVideoOutputHardwareCenterCutDownconversion = /* 'HWcc' */ 0x48576363, bmdVideoOutputHardware720p1080pCrossconversion = /* 'xcap' */ 0x78636170, bmdVideoOutputHardwareAnamorphic720pUpconversion = /* 'ua7p' */ 0x75613770, bmdVideoOutputHardwareAnamorphic1080iUpconversion = /* 'ua1i' */ 0x75613169, bmdVideoOutputHardwareAnamorphic149To720pUpconversion = /* 'u47p' */ 0x75343770, bmdVideoOutputHardwareAnamorphic149To1080iUpconversion = /* 'u41i' */ 0x75343169, bmdVideoOutputHardwarePillarbox720pUpconversion = /* 'up7p' */ 0x75703770, bmdVideoOutputHardwarePillarbox1080iUpconversion = /* 'up1i' */ 0x75703169 }; /* Enum BMDVideoInputConversionMode - Video input conversion mode */ typedef uint32_t BMDVideoInputConversionMode; enum _BMDVideoInputConversionMode { bmdNoVideoInputConversion = /* 'none' */ 0x6E6F6E65, bmdVideoInputLetterboxDownconversionFromHD1080 = /* '10lb' */ 0x31306C62, bmdVideoInputAnamorphicDownconversionFromHD1080 = /* '10am' */ 0x3130616D, bmdVideoInputLetterboxDownconversionFromHD720 = /* '72lb' */ 0x37326C62, bmdVideoInputAnamorphicDownconversionFromHD720 = /* '72am' */ 0x3732616D, bmdVideoInputLetterboxUpconversion = /* 'lbup' */ 0x6C627570, bmdVideoInputAnamorphicUpconversion = /* 'amup' */ 0x616D7570 }; /* Enum BMDVideo3DPackingFormat - Video 3D packing format */ typedef uint32_t BMDVideo3DPackingFormat; enum _BMDVideo3DPackingFormat { bmdVideo3DPackingSidebySideHalf = /* 'sbsh' */ 0x73627368, bmdVideo3DPackingLinebyLine = /* 'lbyl' */ 0x6C62796C, bmdVideo3DPackingTopAndBottom = /* 'tabo' */ 0x7461626F, bmdVideo3DPackingFramePacking = /* 'frpk' */ 0x6672706B, bmdVideo3DPackingLeftOnly = /* 'left' */ 0x6C656674, bmdVideo3DPackingRightOnly = /* 'righ' */ 0x72696768 }; /* Enum BMDIdleVideoOutputOperation - Video output operation when not playing video */ typedef uint32_t BMDIdleVideoOutputOperation; enum _BMDIdleVideoOutputOperation { bmdIdleVideoOutputBlack = /* 'blac' */ 0x626C6163, bmdIdleVideoOutputLastFrame = /* 'lafa' */ 0x6C616661 }; /* Enum BMDVideoEncoderFrameCodingMode - Video frame coding mode */ typedef uint32_t BMDVideoEncoderFrameCodingMode; enum _BMDVideoEncoderFrameCodingMode { bmdVideoEncoderFrameCodingModeInter = /* 'inte' */ 0x696E7465, bmdVideoEncoderFrameCodingModeIntra = /* 'intr' */ 0x696E7472 }; /* Enum BMDDNxHRLevel - DNxHR Levels */ typedef uint32_t BMDDNxHRLevel; enum _BMDDNxHRLevel { bmdDNxHRLevelSQ = /* 'dnsq' */ 0x646E7371, bmdDNxHRLevelLB = /* 'dnlb' */ 0x646E6C62, bmdDNxHRLevelHQ = /* 'dnhq' */ 0x646E6871, bmdDNxHRLevelHQX = /* 'dhqx' */ 0x64687178, bmdDNxHRLevel444 = /* 'd444' */ 0x64343434 }; /* Enum BMDLinkConfiguration - Video link configuration */ typedef uint32_t BMDLinkConfiguration; enum _BMDLinkConfiguration { bmdLinkConfigurationSingleLink = /* 'lcsl' */ 0x6C63736C, bmdLinkConfigurationDualLink = /* 'lcdl' */ 0x6C63646C, bmdLinkConfigurationQuadLink = /* 'lcql' */ 0x6C63716C }; /* Enum BMDDeviceInterface - Device interface type */ typedef uint32_t BMDDeviceInterface; enum _BMDDeviceInterface { bmdDeviceInterfacePCI = /* 'pci ' */ 0x70636920, bmdDeviceInterfaceUSB = /* 'usb ' */ 0x75736220, bmdDeviceInterfaceThunderbolt = /* 'thun' */ 0x7468756E }; /* Enum BMDColorspace - Colorspace */ typedef uint32_t BMDColorspace; enum _BMDColorspace { bmdColorspaceRec601 = /* 'r601' */ 0x72363031, bmdColorspaceRec709 = /* 'r709' */ 0x72373039, bmdColorspaceRec2020 = /* '2020' */ 0x32303230, bmdColorspaceDolbyVisionNative = /* 'DoVi' */ 0x446F5669, // For bmdDeckLinkConfigVideoOutputConversionColorspaceDestination with 12-bit RGB bmdColorspaceP3D65 = /* 'P3D6' */ 0x50334436, // For bmdDeckLinkConfigVideoOutputConversionColorspaceSource only bmdColorspaceUnknown = /* 'Ncol' */ 0x4E636F6C // For disabling bmdDeckLinkConfigVideoOutputConversionColorspaceDestination }; /* Enum BMDDynamicRange - SDR or HDR */ typedef uint32_t BMDDynamicRange; enum _BMDDynamicRange { bmdDynamicRangeSDR = 0, // Standard Dynamic Range in accordance with SMPTE ST 2036-1 bmdDynamicRangeHDRStaticPQ = 1 << 29, // High Dynamic Range PQ in accordance with SMPTE ST 2084 bmdDynamicRangeHDRStaticHLG = 1 << 30 // High Dynamic Range HLG in accordance with ITU-R BT.2100-0 }; /* Enum BMDMezzanineType - */ typedef uint32_t BMDMezzanineType; enum _BMDMezzanineType { bmdMezzanineTypeNone = 0, // No mezzanine board bmdMezzanineTypeHDMI14OpticalSDI = /* 'mza1' */ 0x6D7A6131, // Mezzanine board with HDMI 1.4 and Optical SDI bmdMezzanineTypeQuadSDI = /* 'mz4s' */ 0x6D7A3473, // Mezzanine board with four SDI connectors bmdMezzanineTypeHDMI20OpticalSDI = /* 'mza2' */ 0x6D7A6132, // Mezzanine board with HDMI 2.0 and Optical SDI bmdMezzanineTypeHDMI21RS422 = /* 'mzhr' */ 0x6D7A6872 // Mezzanine boards with HDMI 2.1 and RS422 }; /* Enum BMDDeckLinkHDMIInputEDIDID - DeckLink HDMI Input EDID ID */ typedef uint32_t BMDDeckLinkHDMIInputEDIDID; enum _BMDDeckLinkHDMIInputEDIDID { /* Integers */ bmdDeckLinkHDMIInputEDIDDynamicRange = /* 'HIDy' */ 0x48494479 // Parameter is of type BMDDynamicRange. Default is (bmdDynamicRangeSDR|bmdDynamicRangeHDRStaticPQ) }; /* Enum BMDDeckLinkFrameMetadataID - DeckLink Frame Metadata ID */ typedef uint32_t BMDDeckLinkFrameMetadataID; enum _BMDDeckLinkFrameMetadataID { /* Integers */ bmdDeckLinkFrameMetadataColorspace = /* 'cspc' */ 0x63737063, // Colorspace of video frame (see BMDColorspace) bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = /* 'eotf' */ 0x656F7466, // EOTF in range 0-7 as per CEA 861.3 /* Dolby Vision only - Bytes */ bmdDeckLinkFrameMetadataDolbyVision = /* 'dovi' */ 0x646F7669, // Dolby Vision Metadata /* CEA/SMPTE only - HDR Metadata Floats */ bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = /* 'hdrx' */ 0x68647278, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = /* 'hdry' */ 0x68647279, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = /* 'hdgx' */ 0x68646778, // Green display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY = /* 'hdgy' */ 0x68646779, // Green display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX = /* 'hdbx' */ 0x68646278, // Blue display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY = /* 'hdby' */ 0x68646279, // Blue display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRWhitePointX = /* 'hdwx' */ 0x68647778, // White point in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRWhitePointY = /* 'hdwy' */ 0x68647779, // White point in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = /* 'hdml' */ 0x68646D6C, // Max display mastering luminance in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = /* 'hmil' */ 0x686D696C, // Min display mastering luminance in range 0.0001 cd/m2 - 6.5535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = /* 'mcll' */ 0x6D636C6C, // Maximum Content Light Level in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 }; /* Enum BMDEthernetLinkState - The state of the Ethernet link */ typedef uint32_t BMDEthernetLinkState; enum _BMDEthernetLinkState { bmdEthernetLinkStateDisconnected = /* 'elds' */ 0x656C6473, bmdEthernetLinkStateConnectedUnbound = /* 'elcu' */ 0x656C6375, bmdEthernetLinkStateConnectedBound = /* 'elcb' */ 0x656C6362 }; /* Enum BMDProfileID - Identifies a profile */ typedef uint32_t BMDProfileID; enum _BMDProfileID { bmdProfileOneSubDeviceFullDuplex = /* '1dfd' */ 0x31646664, bmdProfileOneSubDeviceHalfDuplex = /* '1dhd' */ 0x31646864, bmdProfileTwoSubDevicesFullDuplex = /* '2dfd' */ 0x32646664, bmdProfileTwoSubDevicesHalfDuplex = /* '2dhd' */ 0x32646864, bmdProfileFourSubDevicesHalfDuplex = /* '4dhd' */ 0x34646864 }; /* Enum BMDHDMITimecodePacking - Packing form of timecode on HDMI */ typedef uint32_t BMDHDMITimecodePacking; enum _BMDHDMITimecodePacking { bmdHDMITimecodePackingIEEEOUI000085 = 0x00008500, bmdHDMITimecodePackingIEEEOUI080046 = 0x08004601, bmdHDMITimecodePackingIEEEOUI5CF9F0 = 0x5CF9F003 }; /* Enum BMDInternalKeyingAncillaryDataSource - Source for VANC and timecode data when performing internal keying */ typedef uint32_t BMDInternalKeyingAncillaryDataSource; enum _BMDInternalKeyingAncillaryDataSource { bmdInternalKeyingUsesAncillaryDataFromInputSignal = /* 'ikai' */ 0x696B6169, bmdInternalKeyingUsesAncillaryDataFromKeyFrame = /* 'ikak' */ 0x696B616B }; /* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */ typedef uint32_t BMDDeckLinkAttributeID; enum _BMDDeckLinkAttributeID { /* Flags */ BMDDeckLinkSupportsInternalKeying = /* 'keyi' */ 0x6B657969, BMDDeckLinkSupportsExternalKeying = /* 'keye' */ 0x6B657965, BMDDeckLinkSupportsInputFormatDetection = /* 'infd' */ 0x696E6664, BMDDeckLinkHasReferenceInput = /* 'hrin' */ 0x6872696E, BMDDeckLinkHasSerialPort = /* 'hspt' */ 0x68737074, BMDDeckLinkHasAnalogVideoOutputGain = /* 'avog' */ 0x61766F67, BMDDeckLinkCanOnlyAdjustOverallVideoOutputGain = /* 'ovog' */ 0x6F766F67, BMDDeckLinkHasVideoInputAntiAliasingFilter = /* 'aafl' */ 0x6161666C, BMDDeckLinkHasBypass = /* 'byps' */ 0x62797073, BMDDeckLinkSupportsClockTimingAdjustment = /* 'ctad' */ 0x63746164, BMDDeckLinkSupportsFullFrameReferenceInputTimingOffset = /* 'frin' */ 0x6672696E, BMDDeckLinkSupportsSMPTELevelAOutput = /* 'lvla' */ 0x6C766C61, BMDDeckLinkSupportsAutoSwitchingPPsFOnInput = /* 'apsf' */ 0x61707366, BMDDeckLinkSupportsDualLinkSDI = /* 'sdls' */ 0x73646C73, BMDDeckLinkSupportsQuadLinkSDI = /* 'sqls' */ 0x73716C73, BMDDeckLinkSupportsIdleOutput = /* 'idou' */ 0x69646F75, BMDDeckLinkVANCRequires10BitYUVVideoFrames = /* 'vioY' */ 0x76696F59, // Legacy product requires v210 active picture for IDeckLinkVideoFrameAncillaryPackets or 10-bit VANC BMDDeckLinkHasLTCTimecodeInput = /* 'hltc' */ 0x686C7463, BMDDeckLinkSupportsHDRMetadata = /* 'hdrm' */ 0x6864726D, BMDDeckLinkSupportsColorspaceMetadata = /* 'cmet' */ 0x636D6574, BMDDeckLinkSupportsHDMITimecode = /* 'htim' */ 0x6874696D, BMDDeckLinkSupportsHighFrameRateTimecode = /* 'HFRT' */ 0x48465254, BMDDeckLinkSupportsSynchronizeToCaptureGroup = /* 'stcg' */ 0x73746367, BMDDeckLinkSupportsSynchronizeToPlaybackGroup = /* 'stpg' */ 0x73747067, BMDDeckLinkHasMonitorOut = /* 'fmoo' */ 0x666D6F6F, /* Integers */ BMDDeckLinkMaximumAudioChannels = /* 'mach' */ 0x6D616368, BMDDeckLinkMaximumHDMIAudioChannels = /* 'mhch' */ 0x6D686368, BMDDeckLinkMaximumAnalogAudioInputChannels = /* 'iach' */ 0x69616368, BMDDeckLinkMaximumAnalogAudioOutputChannels = /* 'aach' */ 0x61616368, BMDDeckLinkNumberOfSubDevices = /* 'nsbd' */ 0x6E736264, BMDDeckLinkSubDeviceIndex = /* 'subi' */ 0x73756269, BMDDeckLinkPersistentID = /* 'peid' */ 0x70656964, BMDDeckLinkDeviceGroupID = /* 'dgid' */ 0x64676964, BMDDeckLinkTopologicalID = /* 'toid' */ 0x746F6964, BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, // Returns a BMDVideoConnection bit field BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, // Returns a BMDVideoConnection bit field BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, // Returns a BMDAudioConnection bit field BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, // Returns a BMDAudioConnection bit field BMDDeckLinkVideoIOSupport = /* 'vios' */ 0x76696F73, // Returns a BMDVideoIOSupport bit field BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, // Returns a BMDDeckControlConnection bit field BMDDeckLinkDeviceInterface = /* 'dbus' */ 0x64627573, // Returns a BMDDeviceInterface BMDDeckLinkAudioInputRCAChannelCount = /* 'airc' */ 0x61697263, BMDDeckLinkAudioInputXLRChannelCount = /* 'aixc' */ 0x61697863, BMDDeckLinkAudioOutputRCAChannelCount = /* 'aorc' */ 0x616F7263, BMDDeckLinkAudioOutputXLRChannelCount = /* 'aoxc' */ 0x616F7863, BMDDeckLinkProfileID = /* 'prid' */ 0x70726964, // Returns a BMDProfileID BMDDeckLinkDuplex = /* 'dupx' */ 0x64757078, BMDDeckLinkMinimumPrerollFrames = /* 'mprf' */ 0x6D707266, BMDDeckLinkSupportedDynamicRange = /* 'sudr' */ 0x73756472, BMDDeckLinkMezzanineType = /* 'mezt' */ 0x6D657A74, /* Floats */ BMDDeckLinkVideoInputGainMinimum = /* 'vigm' */ 0x7669676D, BMDDeckLinkVideoInputGainMaximum = /* 'vigx' */ 0x76696778, BMDDeckLinkVideoOutputGainMinimum = /* 'vogm' */ 0x766F676D, BMDDeckLinkVideoOutputGainMaximum = /* 'vogx' */ 0x766F6778, BMDDeckLinkMicrophoneInputGainMinimum = /* 'migm' */ 0x6D69676D, BMDDeckLinkMicrophoneInputGainMaximum = /* 'migx' */ 0x6D696778, /* Strings */ BMDDeckLinkSerialPortDeviceName = /* 'slpn' */ 0x736C706E, BMDDeckLinkVendorName = /* 'vndr' */ 0x766E6472, BMDDeckLinkDisplayName = /* 'dspn' */ 0x6473706E, BMDDeckLinkModelName = /* 'mdln' */ 0x6D646C6E, BMDDeckLinkDeviceHandle = /* 'devh' */ 0x64657668, BMDDeckLinkEthernetMACAddress = /* 'eMAC' */ 0x654D4143 }; /* Enum BMDDeckLinkAPIInformationID - DeckLinkAPI information ID */ typedef uint32_t BMDDeckLinkAPIInformationID; enum _BMDDeckLinkAPIInformationID { /* Integer or String */ BMDDeckLinkAPIVersion = /* 'vers' */ 0x76657273 }; /* Enum BMDDeckLinkStatusID - DeckLink Status ID */ typedef uint32_t BMDDeckLinkStatusID; enum _BMDDeckLinkStatusID { /* Integers */ bmdDeckLinkStatusDetectedVideoInputMode = /* 'dvim' */ 0x6476696D, bmdDeckLinkStatusDetectedVideoInputFormatFlags = /* 'dvff' */ 0x64766666, bmdDeckLinkStatusDetectedVideoInputFieldDominance = /* 'dvfd' */ 0x64766664, bmdDeckLinkStatusDetectedVideoInputColorspace = /* 'dscl' */ 0x6473636C, bmdDeckLinkStatusDetectedVideoInputDynamicRange = /* 'dsdr' */ 0x64736472, bmdDeckLinkStatusDetectedSDILinkConfiguration = /* 'dslc' */ 0x64736C63, bmdDeckLinkStatusCurrentVideoInputMode = /* 'cvim' */ 0x6376696D, bmdDeckLinkStatusCurrentVideoInputPixelFormat = /* 'cvip' */ 0x63766970, bmdDeckLinkStatusCurrentVideoInputFlags = /* 'cvif' */ 0x63766966, bmdDeckLinkStatusCurrentVideoOutputMode = /* 'cvom' */ 0x63766F6D, bmdDeckLinkStatusCurrentVideoOutputFlags = /* 'cvof' */ 0x63766F66, bmdDeckLinkStatusEthernetLink = /* 'sels' */ 0x73656C73, bmdDeckLinkStatusEthernetLinkMbps = /* 'sesp' */ 0x73657370, bmdDeckLinkStatusPCIExpressLinkWidth = /* 'pwid' */ 0x70776964, bmdDeckLinkStatusPCIExpressLinkSpeed = /* 'plnk' */ 0x706C6E6B, bmdDeckLinkStatusLastVideoOutputPixelFormat = /* 'opix' */ 0x6F706978, bmdDeckLinkStatusReferenceSignalMode = /* 'refm' */ 0x7265666D, bmdDeckLinkStatusReferenceSignalFlags = /* 'reff' */ 0x72656666, bmdDeckLinkStatusBusy = /* 'busy' */ 0x62757379, bmdDeckLinkStatusInterchangeablePanelType = /* 'icpt' */ 0x69637074, bmdDeckLinkStatusDeviceTemperature = /* 'dtmp' */ 0x64746D70, bmdDeckLinkStatusHDMIOutputActualMode = /* 'hiam' */ 0x6869616D, bmdDeckLinkStatusHDMIOutputActualFormatFlags = /* 'hiaf' */ 0x68696166, bmdDeckLinkStatusHDMIOutputFRLRate = /* 'hiof' */ 0x68696F66, bmdDeckLinkStatusHDMIInputFRLRate = /* 'hiif' */ 0x68696966, bmdDeckLinkStatusHDMIOutputTMDSLineRate = /* 'hilr' */ 0x68696C72, /* Floats */ bmdDeckLinkStatusSinkSupportsDolbyVision = /* 'dvvr' */ 0x64767672, /* Flags */ bmdDeckLinkStatusVideoInputSignalLocked = /* 'visl' */ 0x7669736C, bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C, /* Strings */ bmdDeckLinkStatusEthernetLocalIPAddress = /* 'seip' */ 0x73656970, bmdDeckLinkStatusEthernetSubnetMask = /* 'sesm' */ 0x7365736D, bmdDeckLinkStatusEthernetGatewayIPAddress = /* 'segw' */ 0x73656777, bmdDeckLinkStatusEthernetPrimaryDNS = /* 'sepd' */ 0x73657064, bmdDeckLinkStatusEthernetSecondaryDNS = /* 'sesd' */ 0x73657364, bmdDeckLinkStatusEthernetPTPGrandmasterIdentity = /* 'spid' */ 0x73706964, bmdDeckLinkStatusEthernetVideoOutputAddress = /* 'soav' */ 0x736F6176, bmdDeckLinkStatusEthernetAudioOutputAddress = /* 'soaa' */ 0x736F6161, bmdDeckLinkStatusEthernetAncillaryOutputAddress = /* 'soaA' */ 0x736F6141, bmdDeckLinkStatusEthernetAudioInputChannelOrder = /* 'saco' */ 0x7361636F, /* Bytes */ bmdDeckLinkStatusReceivedEDID = /* 'edid' */ 0x65646964 }; /* Enum BMDDeckLinkVideoStatusFlags - */ typedef uint32_t BMDDeckLinkVideoStatusFlags; enum _BMDDeckLinkVideoStatusFlags { bmdDeckLinkVideoStatusPsF = 1 << 0, bmdDeckLinkVideoStatusDualStream3D = 1 << 1 }; /* Enum BMDDuplexMode - Duplex of the device */ typedef uint32_t BMDDuplexMode; enum _BMDDuplexMode { bmdDuplexFull = /* 'dxfu' */ 0x64786675, bmdDuplexHalf = /* 'dxha' */ 0x64786861, bmdDuplexSimplex = /* 'dxsp' */ 0x64787370, bmdDuplexInactive = /* 'dxin' */ 0x6478696E }; /* Enum BMDPanelType - The type of interchangeable panel */ typedef uint32_t BMDPanelType; enum _BMDPanelType { bmdPanelNotDetected = /* 'npnl' */ 0x6E706E6C, bmdPanelTeranexMiniSmartPanel = /* 'tmsm' */ 0x746D736D }; /* Enum BMDFormatFlags - Flags to describe the video signal */ typedef uint32_t BMDFormatFlags; enum _BMDFormatFlags { bmdFormatRGB444 = 1 << 0, bmdFormatYUV444 = 1 << 1, bmdFormatYUV422 = 1 << 2, bmdFormatYUV420 = 1 << 3, bmdFormat8BitDepth = 1 << 4, bmdFormat10BitDepth = 1 << 5, bmdFormat12BitDepth = 1 << 6 }; /* Enum BMDDeviceBusyState - Current device busy state */ typedef uint32_t BMDDeviceBusyState; enum _BMDDeviceBusyState { bmdDeviceCaptureBusy = 1 << 0, bmdDevicePlaybackBusy = 1 << 1, bmdDeviceSerialPortBusy = 1 << 2 }; /* Enum BMDVideoIOSupport - Device video input/output support */ typedef uint32_t BMDVideoIOSupport; enum _BMDVideoIOSupport { bmdDeviceSupportsCapture = 1 << 0, bmdDeviceSupportsPlayback = 1 << 1 }; /* Enum BMD3DPreviewFormat - Linked Frame preview format */ typedef uint32_t BMD3DPreviewFormat; enum _BMD3DPreviewFormat { bmd3DPreviewFormatDefault = /* 'defa' */ 0x64656661, bmd3DPreviewFormatLeftOnly = /* 'left' */ 0x6C656674, bmd3DPreviewFormatRightOnly = /* 'righ' */ 0x72696768, bmd3DPreviewFormatSideBySide = /* 'side' */ 0x73696465, bmd3DPreviewFormatTopBottom = /* 'topb' */ 0x746F7062 }; /* Enum BMDIPFlowDirection - BMDIPFlowDirection enumerates the direction of the IP flow. */ enum BMDIPFlowDirection { bmdDeckLinkIPFlowDirectionOutput = 0, bmdDeckLinkIPFlowDirectionInput = 1 }; /* Enum BMDIPFlowType - BMDIPFlowDirection enumerates the IP flow type. */ enum BMDIPFlowType { bmdDeckLinkIPFlowTypeVideo = 0, bmdDeckLinkIPFlowTypeAudio = 1, bmdDeckLinkIPFlowTypeAncillary = 2 }; /* Enum BMDDeckLinkIPFlowAttributeID - DeckLink IP Flow Attribute ID */ enum BMDDeckLinkIPFlowAttributeID { /* DeckLink IP Flow Attribute Integers */ bmdDeckLinkIPFlowID = /* '2fai' */ 0x32666169, bmdDeckLinkIPFlowDirection = /* '2fad' */ 0x32666164, bmdDeckLinkIPFlowType = /* '2fat' */ 0x32666174 }; /* Enum BMDDeckLinkIPFlowStatusID - DeckLink IP Flow Attribute ID */ enum BMDDeckLinkIPFlowStatusID { /* DeckLink IP Flow Status Strings */ bmdDeckLinkIPFlowSDP = /* '2fas' */ 0x32666173 }; /* Enum BMDDeckLinkIPFlowSettingID - DeckLink IP Flow Setting ID */ enum BMDDeckLinkIPFlowSettingID { /* DeckLink IP Flow Setting Strings */ bmdDeckLinkIPFlowPeerSDP = /* '2fps' */ 0x32667073 // The peer's SDP. Must not be over 1000 bytes large. }; /* Enum BMDNotifications - Events that can be subscribed through IDeckLinkNotification */ typedef uint32_t BMDNotifications; enum _BMDNotifications { bmdPreferencesChanged = /* 'pref' */ 0x70726566, bmdStatusChanged = /* 'stat' */ 0x73746174, bmdIPFlowStatusChanged = /* 'bfsc' */ 0x62667363, bmdIPFlowSettingChanged = /* 'bfcc' */ 0x62666363 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkVideoOutputCallback; class IDeckLinkInputCallback; class IDeckLinkEncoderInputCallback; class IDeckLinkVideoBufferAllocator; class IDeckLinkVideoBufferAllocatorProvider; class IDeckLinkAudioOutputCallback; class IDeckLinkIterator; class IDeckLinkAPIInformation; class IDeckLinkIPFlowAttributes; class IDeckLinkIPFlowStatus; class IDeckLinkIPFlowSetting; class IDeckLinkIPFlow; class IDeckLinkIPFlowIterator; class IDeckLinkOutput; class IDeckLinkMacOutput; class IDeckLinkInput; class IDeckLinkIPExtensions; class IDeckLinkHDMIInputEDID; class IDeckLinkEncoderInput; class IDeckLinkVideoBuffer; class IDeckLinkMacVideoBuffer; class IDeckLinkVideoFrame; class IDeckLinkMutableVideoFrame; class IDeckLinkVideoFrame3DExtensions; class IDeckLinkVideoFrameMetadataExtensions; class IDeckLinkVideoFrameMutableMetadataExtensions; class IDeckLinkVideoInputFrame; class IDeckLinkAncillaryPacket; class IDeckLinkAncillaryPacketIterator; class IDeckLinkVideoFrameAncillaryPackets; class IDeckLinkVideoFrameAncillary; class IDeckLinkEncoderPacket; class IDeckLinkEncoderVideoPacket; class IDeckLinkEncoderAudioPacket; class IDeckLinkH265NALPacket; class IDeckLinkAudioInputPacket; class IDeckLinkScreenPreviewCallback; class IDeckLinkCocoaScreenPreviewCallback; class IDeckLinkGLScreenPreviewHelper; class IDeckLinkMetalScreenPreviewHelper; class IDeckLinkNotificationCallback; class IDeckLinkNotification; class IDeckLinkProfileAttributes; class IDeckLinkProfileIterator; class IDeckLinkProfile; class IDeckLinkProfileCallback; class IDeckLinkProfileManager; class IDeckLinkStatus; class IDeckLinkKeyer; class IDeckLinkVideoConversion; class IDeckLinkDeviceNotificationCallback; class IDeckLinkDiscovery; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ class BMD_PUBLIC IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame* completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; virtual HRESULT ScheduledPlaybackHasStopped (void) = 0; protected: virtual ~IDeckLinkVideoOutputCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkInputCallback - Frame arrival callback. */ class BMD_PUBLIC IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode* newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT VideoInputFrameArrived (/* in */ IDeckLinkVideoInputFrame* videoFrame, /* in */ IDeckLinkAudioInputPacket* audioPacket) = 0; protected: virtual ~IDeckLinkInputCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderInputCallback - Frame arrival callback. */ class BMD_PUBLIC IDeckLinkEncoderInputCallback : public IUnknown { public: virtual HRESULT VideoInputSignalChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode* newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT VideoPacketArrived (/* in */ IDeckLinkEncoderVideoPacket* videoPacket) = 0; virtual HRESULT AudioPacketArrived (/* in */ IDeckLinkEncoderAudioPacket* audioPacket) = 0; protected: virtual ~IDeckLinkEncoderInputCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoBufferAllocator - Buffer allocator for video. */ class BMD_PUBLIC IDeckLinkVideoBufferAllocator : public IUnknown { public: virtual HRESULT AllocateVideoBuffer (/* out */ IDeckLinkVideoBuffer** allocatedBuffer) = 0; protected: virtual ~IDeckLinkVideoBufferAllocator () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoBufferAllocatorProvider - Allows EnableVideoInputWithAllocatorProvider to obtain allocators */ class BMD_PUBLIC IDeckLinkVideoBufferAllocatorProvider : public IUnknown { public: virtual HRESULT GetVideoBufferAllocator (/* in */ uint32_t bufferSize, /* in */ uint32_t width, /* in */ uint32_t height, /* in */ uint32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoBufferAllocator** allocator) = 0; protected: virtual ~IDeckLinkVideoBufferAllocatorProvider () {} // call Release method to drop reference count }; /* Interface IDeckLinkAudioOutputCallback - Optional callback to allow audio samples to be pulled as required. */ class BMD_PUBLIC IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT RenderAudioSamples (/* in */ bool preroll) = 0; }; /* Interface IDeckLinkIterator - Enumerates installed DeckLink hardware */ class BMD_PUBLIC IDeckLinkIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink** deckLinkInstance) = 0; }; /* Interface IDeckLinkAPIInformation - DeckLinkAPI attribute interface */ class BMD_PUBLIC IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ bool* value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ CFStringRef* value) = 0; protected: virtual ~IDeckLinkAPIInformation () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlowAttributes - */ class BMD_PUBLIC IDeckLinkIPFlowAttributes : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkIPFlowAttributeID attrID, /* out */ int64_t* value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkIPFlowAttributeID attrID, /* out */ bool* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkIPFlowAttributeID attrID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkIPFlowAttributeID attrID, /* out */ CFStringRef* value) = 0; protected: virtual ~IDeckLinkIPFlowAttributes () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlowStatus - */ class BMD_PUBLIC IDeckLinkIPFlowStatus : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkIPFlowStatusID statusID, /* out */ int64_t* value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkIPFlowStatusID statusID, /* out */ bool* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkIPFlowStatusID statusID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkIPFlowStatusID statusID, /* out */ CFStringRef* value) = 0; protected: virtual ~IDeckLinkIPFlowStatus () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlowSetting - */ class BMD_PUBLIC IDeckLinkIPFlowSetting : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* out */ int64_t* value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* out */ bool* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* out */ CFStringRef* value) = 0; virtual HRESULT SetInt (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* in */ int64_t value) = 0; virtual HRESULT SetFlag (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* in */ bool value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* in */ double value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* in */ CFStringRef value) = 0; protected: virtual ~IDeckLinkIPFlowSetting () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlow - */ class BMD_PUBLIC IDeckLinkIPFlow : public IUnknown { public: virtual HRESULT Enable (void) = 0; // Enables an IP flow to start sending or receiving. virtual HRESULT Disable (void) = 0; // Disables an IP flow to stop sending or receiving. protected: virtual ~IDeckLinkIPFlow () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlowIterator - Enumerates DeckLink IP Flows */ class BMD_PUBLIC IDeckLinkIPFlowIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkIPFlow** deckLinkIPFlowInstance) = 0; protected: virtual ~IDeckLinkIPFlowIterator () {} // call Release method to drop reference count }; /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ class BMD_PUBLIC IDeckLinkOutput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoOutputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0; virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0; virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback* previewCallback) = 0; /* Video Output */ virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0; virtual HRESULT DisableVideoOutput (void) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame** outFrame) = 0; virtual HRESULT CreateVideoFrameWithBuffer (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* in */ IDeckLinkVideoBuffer* buffer, /* out */ IDeckLinkMutableVideoFrame** outFrame) = 0; virtual HRESULT RowBytesForPixelFormat (/* in */ BMDPixelFormat pixelFormat, /* in */ int32_t width, /* out */ int32_t* rowBytes) = 0; virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary** outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame* theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame* theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback* theCallback) = 0; virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t* bufferedFrameCount) = 0; /* Audio Output */ virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT DisableAudioOutput (void) = 0; virtual HRESULT WriteAudioSamplesSync (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t* sampleFramesWritten) = 0; virtual HRESULT BeginAudioPreroll (void) = 0; virtual HRESULT EndAudioPreroll (void) = 0; virtual HRESULT ScheduleAudioSamples (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t* sampleFramesWritten) = 0; virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t* bufferedSampleFrameCount) = 0; virtual HRESULT FlushBufferedAudioSamples (void) = 0; virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback* theCallback) = 0; /* Output Control */ virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0; virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue* actualStopTime, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool* active) = 0; virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* streamTime, /* out */ double* playbackSpeed) = 0; virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus* referenceStatus) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0; virtual HRESULT GetFrameCompletionReferenceTimestamp (/* in */ IDeckLinkVideoFrame* theFrame, /* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* frameCompletionTimestamp) = 0; protected: virtual ~IDeckLinkOutput () {} // call Release method to drop reference count }; /* Interface IDeckLinkMacOutput - Created by QueryInterface from IDeckLinkOutput. */ class BMD_PUBLIC IDeckLinkMacOutput : public IUnknown { public: virtual HRESULT CreateVideoFrameFromCVPixelBufferRef (/* in */ void* cvPixelBuffer, /* out */ IDeckLinkMutableVideoFrame** outFrame) = 0; protected: virtual ~IDeckLinkMacOutput () {} // call Release method to drop reference count }; /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ class BMD_PUBLIC IDeckLinkInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoInputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0; virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0; virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback* previewCallback) = 0; /* Video Input */ virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0; virtual HRESULT EnableVideoInputWithAllocatorProvider (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* in */ IDeckLinkVideoBufferAllocatorProvider* allocatorProvider) = 0; virtual HRESULT DisableVideoInput (void) = 0; virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t* availableFrameCount) = 0; /* Audio Input */ virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0; virtual HRESULT DisableAudioInput (void) = 0; virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0; /* Input Control */ virtual HRESULT StartStreams (void) = 0; virtual HRESULT StopStreams (void) = 0; virtual HRESULT PauseStreams (void) = 0; virtual HRESULT FlushStreams (void) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback* theCallback) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0; protected: virtual ~IDeckLinkInput () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPExtensions - */ class BMD_PUBLIC IDeckLinkIPExtensions : public IUnknown { public: virtual HRESULT GetDeckLinkIPFlowIterator (/* out */ IDeckLinkIPFlowIterator** iterator) = 0; virtual HRESULT GetIPFlowByID (/* in */ BMDIPFlowID id, /* out */ IDeckLinkIPFlow** flow) = 0; protected: virtual ~IDeckLinkIPExtensions () {} // call Release method to drop reference count }; /* Interface IDeckLinkHDMIInputEDID - Created by QueryInterface from IDeckLink. Releasing all references will restore EDID to default */ class BMD_PUBLIC IDeckLinkHDMIInputEDID : public IUnknown { public: virtual HRESULT SetInt (/* in */ BMDDeckLinkHDMIInputEDIDID cfgID, /* in */ int64_t value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkHDMIInputEDIDID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT WriteToEDID (void) = 0; protected: virtual ~IDeckLinkHDMIInputEDID () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderInput - Created by QueryInterface from IDeckLink. */ class BMD_PUBLIC IDeckLinkEncoderInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedCodec, /* in */ uint32_t requestedCodecProfile, /* in */ BMDSupportedVideoModeFlags flags, /* out */ bool* supported) = 0; virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0; /* Video Input */ virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0; virtual HRESULT DisableVideoInput (void) = 0; virtual HRESULT GetAvailablePacketsCount (/* out */ uint32_t* availablePacketsCount) = 0; /* Audio Input */ virtual HRESULT EnableAudioInput (/* in */ BMDAudioFormat audioFormat, /* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0; virtual HRESULT DisableAudioInput (void) = 0; virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0; /* Input Control */ virtual HRESULT StartStreams (void) = 0; virtual HRESULT StopStreams (void) = 0; virtual HRESULT PauseStreams (void) = 0; virtual HRESULT FlushStreams (void) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkEncoderInputCallback* theCallback) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0; protected: virtual ~IDeckLinkEncoderInput () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoBuffer - Interface to encapsulate a video frame buffer; can be caller-implemented. */ class BMD_PUBLIC IDeckLinkVideoBuffer : public IUnknown { public: virtual HRESULT GetBytes (/* out */ void** buffer) = 0; virtual HRESULT StartAccess (/* in */ BMDBufferAccessFlags flags) = 0; virtual HRESULT EndAccess (/* in */ BMDBufferAccessFlags flags) = 0; protected: virtual ~IDeckLinkVideoBuffer () {} // call Release method to drop reference count }; /* Interface IDeckLinkMacVideoBuffer - Video buffer helper functions for macOS; can be caller-implemented. */ class BMD_PUBLIC IDeckLinkMacVideoBuffer : public IUnknown { public: virtual HRESULT CreateCVPixelBufferRef (/* out */ void** cvPixelBuffer) = 0; protected: virtual ~IDeckLinkMacVideoBuffer () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ class BMD_PUBLIC IDeckLinkVideoFrame : public IUnknown { public: virtual long GetWidth (void) = 0; virtual long GetHeight (void) = 0; virtual long GetRowBytes (void) = 0; virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDFrameFlags GetFlags (void) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode** timecode) = 0; virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary** ancillary) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred protected: virtual ~IDeckLinkVideoFrame () {} // call Release method to drop reference count }; /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ class BMD_PUBLIC IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0; virtual HRESULT SetTimecode (/* in */ BMDTimecodeFormat format, /* in */ IDeckLinkTimecode* timecode) = 0; virtual HRESULT SetTimecodeFromComponents (/* in */ BMDTimecodeFormat format, /* in */ uint8_t hours, /* in */ uint8_t minutes, /* in */ uint8_t seconds, /* in */ uint8_t frames, /* in */ BMDTimecodeFlags flags) = 0; virtual HRESULT SetAncillaryData (/* in */ IDeckLinkVideoFrameAncillary* ancillary) = 0; virtual HRESULT SetTimecodeUserBits (/* in */ BMDTimecodeFormat format, /* in */ BMDTimecodeUserBits userBits) = 0; virtual HRESULT SetInterfaceProvider (/* in */ REFIID iid, /* in */ IUnknown* iface) = 0; protected: virtual ~IDeckLinkMutableVideoFrame () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrame3DExtensions - Optional interface to support 3D frames. */ class BMD_PUBLIC IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0; virtual HRESULT GetFrameForRightEye (/* out */ IDeckLinkVideoFrame** rightEyeFrame) = 0; protected: virtual ~IDeckLinkVideoFrame3DExtensions () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameMetadataExtensions - Get frame metadata */ class BMD_PUBLIC IDeckLinkVideoFrameMetadataExtensions : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ double* value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ bool* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ CFStringRef* value) = 0; virtual HRESULT GetBytes (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ void* buffer /* optional */, /* in, out */ uint32_t* bufferSize) = 0; protected: virtual ~IDeckLinkVideoFrameMetadataExtensions () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameMutableMetadataExtensions - Set frame metadata */ class BMD_PUBLIC IDeckLinkVideoFrameMutableMetadataExtensions : public IDeckLinkVideoFrameMetadataExtensions { public: virtual HRESULT SetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ int64_t value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ double value) = 0; virtual HRESULT SetFlag (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ bool value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ CFStringRef value) = 0; virtual HRESULT SetBytes (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ void* buffer, /* in */ uint32_t bufferSize) = 0; protected: virtual ~IDeckLinkVideoFrameMutableMetadataExtensions () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ class BMD_PUBLIC IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue* frameTime, /* out */ BMDTimeValue* frameDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT GetHardwareReferenceTimestamp (/* in */ BMDTimeScale timeScale, /* out */ BMDTimeValue* frameTime, /* out */ BMDTimeValue* frameDuration) = 0; protected: virtual ~IDeckLinkVideoInputFrame () {} // call Release method to drop reference count }; /* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */ class BMD_PUBLIC IDeckLinkAncillaryPacket : public IUnknown { public: virtual HRESULT GetBytes (/* in */ BMDAncillaryPacketFormat format /* For output, only one format need be offered */, /* out */ const void** data /* Optional */, /* out */ uint32_t* size /* Optional */) = 0; virtual uint8_t GetDID (void) = 0; virtual uint8_t GetSDID (void) = 0; virtual uint32_t GetLineNumber (void) = 0; // On output, zero is auto virtual uint8_t GetDataStreamIndex (void) = 0; // Usually zero. Can only be 1 if non-SD and the first data stream is completely full protected: virtual ~IDeckLinkAncillaryPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */ class BMD_PUBLIC IDeckLinkAncillaryPacketIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkAncillaryPacket** packet) = 0; protected: virtual ~IDeckLinkAncillaryPacketIterator () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface on an IDeckLinkVideoFrame object. */ class BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets : public IUnknown { public: virtual HRESULT GetPacketIterator (/* out */ IDeckLinkAncillaryPacketIterator** iterator) = 0; virtual HRESULT GetFirstPacketByID (/* in */ uint8_t DID, /* in */ uint8_t SDID, /* out */ IDeckLinkAncillaryPacket** packet) = 0; virtual HRESULT AttachPacket (/* in */ IDeckLinkAncillaryPacket* packet) = 0; // Implement IDeckLinkAncillaryPacket to output your own virtual HRESULT DetachPacket (/* in */ IDeckLinkAncillaryPacket* packet) = 0; virtual HRESULT DetachAllPackets (void) = 0; protected: virtual ~IDeckLinkVideoFrameAncillaryPackets () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameAncillary - Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface on an IDeckLinkVideoFrame object. */ class BMD_PUBLIC IDeckLinkVideoFrameAncillary : public IUnknown { public: virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void** buffer) = 0; // Pixels/rowbytes is same as display mode, except for above HD where it's 1920 pixels for UHD modes and 2048 pixels for DCI modes virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; protected: virtual ~IDeckLinkVideoFrameAncillary () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderPacket - Interface to encapsulate an encoded packet. */ class BMD_PUBLIC IDeckLinkEncoderPacket : public IUnknown { public: virtual HRESULT GetBytes (/* out */ void** buffer) = 0; virtual long GetSize (void) = 0; virtual HRESULT GetStreamTime (/* out */ BMDTimeValue* frameTime, /* in */ BMDTimeScale timeScale) = 0; virtual BMDPacketType GetPacketType (void) = 0; protected: virtual ~IDeckLinkEncoderPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderVideoPacket - Provided by the IDeckLinkEncoderInput video packet arrival callback. */ class BMD_PUBLIC IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket { public: virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual HRESULT GetHardwareReferenceTimestamp (/* in */ BMDTimeScale timeScale, /* out */ BMDTimeValue* frameTime, /* out */ BMDTimeValue* frameDuration) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode** timecode) = 0; protected: virtual ~IDeckLinkEncoderVideoPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderAudioPacket - Provided by the IDeckLinkEncoderInput audio packet arrival callback. */ class BMD_PUBLIC IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket { public: virtual BMDAudioFormat GetAudioFormat (void) = 0; protected: virtual ~IDeckLinkEncoderAudioPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkH265NALPacket - Obtained through QueryInterface on an IDeckLinkEncoderVideoPacket object */ class BMD_PUBLIC IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket { public: virtual HRESULT GetUnitType (/* out */ uint8_t* unitType) = 0; virtual HRESULT GetBytesNoPrefix (/* out */ void** buffer) = 0; virtual long GetSizeNoPrefix (void) = 0; protected: virtual ~IDeckLinkH265NALPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkAudioInputPacket - Provided by the IDeckLinkInput callback. */ class BMD_PUBLIC IDeckLinkAudioInputPacket : public IUnknown { public: virtual long GetSampleFrameCount (void) = 0; virtual HRESULT GetBytes (/* out */ void** buffer) = 0; virtual HRESULT GetPacketTime (/* out */ BMDTimeValue* packetTime, /* in */ BMDTimeScale timeScale) = 0; protected: virtual ~IDeckLinkAudioInputPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ class BMD_PUBLIC IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame* theFrame) = 0; protected: virtual ~IDeckLinkScreenPreviewCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkCocoaScreenPreviewCallback - Screen preview callback for Cocoa-based applications. Created with CreateCocoaScreenPreview */ class BMD_PUBLIC IDeckLinkCocoaScreenPreviewCallback : public IDeckLinkScreenPreviewCallback { public: protected: virtual ~IDeckLinkCocoaScreenPreviewCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance on platforms with native COM support or from CreateOpenGLScreenPreviewHelper/CreateOpenGL3ScreenPreviewHelper on other platforms. */ class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper : public IUnknown { public: /* Methods must be called with OpenGL context set */ virtual HRESULT InitializeGL (void) = 0; virtual HRESULT PaintGL (void) = 0; virtual HRESULT SetFrame (/* in */ IDeckLinkVideoFrame* theFrame) = 0; virtual HRESULT Set3DPreviewFormat (/* in */ BMD3DPreviewFormat previewFormat) = 0; protected: virtual ~IDeckLinkGLScreenPreviewHelper () {} // call Release method to drop reference count }; /* Interface IDeckLinkMetalScreenPreviewHelper - Created with CreateMetalScreenPreviewHelper(). */ class BMD_PUBLIC IDeckLinkMetalScreenPreviewHelper : public IUnknown { public: virtual HRESULT Initialize (/* in */ void* device) = 0; virtual HRESULT Draw (/* in */ void* cmdBuffer, /* in */ void* renderPassDescriptor, /* in */ void* viewport) = 0; virtual HRESULT SetFrame (/* in */ IDeckLinkVideoFrame* theFrame) = 0; virtual HRESULT Set3DPreviewFormat (/* in */ BMD3DPreviewFormat previewFormat) = 0; protected: virtual ~IDeckLinkMetalScreenPreviewHelper () {} // call Release method to drop reference count }; /* Interface IDeckLinkNotificationCallback - DeckLink Notification Callback Interface */ class BMD_PUBLIC IDeckLinkNotificationCallback : public IUnknown { public: virtual HRESULT Notify (/* in */ BMDNotifications topic, /* in */ uint64_t param1, /* in */ uint64_t param2) = 0; }; /* Interface IDeckLinkNotification - DeckLink Notification interface */ class BMD_PUBLIC IDeckLinkNotification : public IUnknown { public: virtual HRESULT Subscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback* theCallback) = 0; virtual HRESULT Unsubscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback* theCallback) = 0; protected: virtual ~IDeckLinkNotification () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfileAttributes - Created by QueryInterface from an IDeckLinkProfile, or from IDeckLink. When queried from IDeckLink, interrogates the active profile */ class BMD_PUBLIC IDeckLinkProfileAttributes : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool* value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ CFStringRef* value) = 0; protected: virtual ~IDeckLinkProfileAttributes () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfileIterator - Enumerates IDeckLinkProfile interfaces */ class BMD_PUBLIC IDeckLinkProfileIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkProfile** profile) = 0; protected: virtual ~IDeckLinkProfileIterator () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfile - Represents the active profile when queried from IDeckLink */ class BMD_PUBLIC IDeckLinkProfile : public IUnknown { public: virtual HRESULT GetDevice (/* out */ IDeckLink** device) = 0; // Device affected when this profile becomes active virtual HRESULT IsActive (/* out */ bool* isActive) = 0; virtual HRESULT SetActive (void) = 0; // Activating a profile will also change the profile on all devices enumerated by GetPeers. Activation is not complete until IDeckLinkProfileCallback::ProfileActivated is called virtual HRESULT GetPeers (/* out */ IDeckLinkProfileIterator** profileIterator) = 0; // Profiles of other devices activated with this profile protected: virtual ~IDeckLinkProfile () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfileCallback - Receive notifications about profiles related to this device */ class BMD_PUBLIC IDeckLinkProfileCallback : public IUnknown { public: virtual HRESULT ProfileChanging (/* in */ IDeckLinkProfile* profileToBeActivated, /* in */ bool streamsWillBeForcedToStop) = 0; // Called before this device changes profile. User has an opportunity for teardown if streamsWillBeForcedToStop virtual HRESULT ProfileActivated (/* in */ IDeckLinkProfile* activatedProfile) = 0; // Called after this device has been activated with a new profile protected: virtual ~IDeckLinkProfileCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfileManager - Created by QueryInterface from IDeckLink when a device has multiple optional profiles */ class BMD_PUBLIC IDeckLinkProfileManager : public IUnknown { public: virtual HRESULT GetProfiles (/* out */ IDeckLinkProfileIterator** profileIterator) = 0; // All available profiles for this device virtual HRESULT GetProfile (/* in */ BMDProfileID profileID, /* out */ IDeckLinkProfile** profile) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkProfileCallback* callback) = 0; protected: virtual ~IDeckLinkProfileManager () {} // call Release method to drop reference count }; /* Interface IDeckLinkStatus - DeckLink Status interface */ class BMD_PUBLIC IDeckLinkStatus : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkStatusID statusID, /* out */ bool* value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkStatusID statusID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkStatusID statusID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkStatusID statusID, /* out */ CFStringRef* value) = 0; virtual HRESULT GetBytes (/* in */ BMDDeckLinkStatusID statusID, /* out */ void* buffer, /* in, out */ uint32_t* bufferSize) = 0; protected: virtual ~IDeckLinkStatus () {} // call Release method to drop reference count }; /* Interface IDeckLinkKeyer - DeckLink Keyer interface */ class BMD_PUBLIC IDeckLinkKeyer : public IUnknown { public: virtual HRESULT Enable (/* in */ bool isExternal) = 0; virtual HRESULT SetLevel (/* in */ uint8_t level) = 0; virtual HRESULT RampUp (/* in */ uint32_t numberOfFrames) = 0; virtual HRESULT RampDown (/* in */ uint32_t numberOfFrames) = 0; virtual HRESULT Disable (void) = 0; protected: virtual ~IDeckLinkKeyer () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance. */ class BMD_PUBLIC IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0; virtual HRESULT ConvertNewFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ BMDPixelFormat dstPixelFormat, /* in */ BMDColorspace dstColorspace, /* in */ IDeckLinkVideoBuffer* dstBuffer, /* out */ IDeckLinkVideoFrame** dstFrame) = 0; protected: virtual ~IDeckLinkVideoConversion () {} // call Release method to drop reference count }; /* Interface IDeckLinkDeviceNotificationCallback - DeckLink device arrival/removal notification callbacks */ class BMD_PUBLIC IDeckLinkDeviceNotificationCallback : public IUnknown { public: virtual HRESULT DeckLinkDeviceArrived (/* in */ IDeckLink* deckLinkDevice) = 0; virtual HRESULT DeckLinkDeviceRemoved (/* in */ IDeckLink* deckLinkDevice) = 0; protected: virtual ~IDeckLinkDeviceNotificationCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkDiscovery - DeckLink device discovery */ class BMD_PUBLIC IDeckLinkDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IDeckLinkDeviceNotificationCallback* deviceNotificationCallback) = 0; virtual HRESULT UninstallDeviceNotifications (void) = 0; protected: virtual ~IDeckLinkDiscovery () {} // call Release method to drop reference count }; /* Functions */ extern "C" { BMD_PUBLIC IDeckLinkIterator* CreateDeckLinkIteratorInstance(void); BMD_PUBLIC IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance(void); BMD_PUBLIC IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance(void); BMD_PUBLIC IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper(void); BMD_PUBLIC IDeckLinkGLScreenPreviewHelper* CreateOpenGL3ScreenPreviewHelper(void); // Requires OpenGL 3.2 support and provides improved performance and color handling BMD_PUBLIC IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview(/* in */ void* /* (NSView*)*/ parentView); BMD_PUBLIC IDeckLinkMetalScreenPreviewHelper* CreateMetalScreenPreviewHelper(void); BMD_PUBLIC IDeckLinkVideoConversion* CreateVideoConversionInstance(void); BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance(void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPI_H) */ mlt-7.38.0/src/modules/decklink/darwin/DeckLinkAPIConfiguration.h000664 000000 000000 00000040047 15172202314 024615 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPICONFIGURATION_H #define BMD_DECKLINKAPICONFIGURATION_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkConfiguration = /* 912F634B-2D4E-40A4-8AAB-8D80B73F1289 */ { 0x91,0x2F,0x63,0x4B,0x2D,0x4E,0x40,0xA4,0x8A,0xAB,0x8D,0x80,0xB7,0x3F,0x12,0x89 }; BMD_CONST REFIID IID_IDeckLinkEncoderConfiguration = /* 138050E5-C60A-4552-BF3F-0F358049327E */ { 0x13,0x80,0x50,0xE5,0xC6,0x0A,0x45,0x52,0xBF,0x3F,0x0F,0x35,0x80,0x49,0x32,0x7E }; /* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ typedef uint32_t BMDDeckLinkConfigurationID; enum _BMDDeckLinkConfigurationID { /* Serial port Flags */ bmdDeckLinkConfigSwapSerialRxTx = /* 'ssrt' */ 0x73737274, /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = /* '3dpf' */ 0x33647066, bmdDeckLinkConfigBypass = /* 'byps' */ 0x62797073, bmdDeckLinkConfigClockTimingAdjustment = /* 'ctad' */ 0x63746164, /* Audio Input/Output Flags */ bmdDeckLinkConfigAnalogAudioConsumerLevels = /* 'aacl' */ 0x6161636C, bmdDeckLinkConfigSwapHDMICh3AndCh4OnInput = /* 'hi34' */ 0x68693334, bmdDeckLinkConfigSwapHDMICh3AndCh4OnOutput = /* 'ho34' */ 0x686F3334, /* Video Output Flags */ bmdDeckLinkConfigFieldFlickerRemoval = /* 'fdfr' */ 0x66646672, bmdDeckLinkConfigHD1080p24ToHD1080i5994Conversion = /* 'to59' */ 0x746F3539, bmdDeckLinkConfig444SDIVideoOutput = /* '444o' */ 0x3434346F, bmdDeckLinkConfigBlackVideoOutputDuringCapture = /* 'bvoc' */ 0x62766F63, bmdDeckLinkConfigLowLatencyVideoOutput = /* 'llvo' */ 0x6C6C766F, bmdDeckLinkConfigDownConversionOnAllAnalogOutput = /* 'caao' */ 0x6361616F, bmdDeckLinkConfigSMPTELevelAOutput = /* 'smta' */ 0x736D7461, bmdDeckLinkConfigRec2020Output = /* 'rec2' */ 0x72656332, // Ensure output is Rec.2020 colorspace bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = /* 'SDQS' */ 0x53445153, bmdDeckLinkConfigOutput1080pAsPsF = /* 'pfpr' */ 0x70667072, bmdDeckLinkConfigOutputValidateEDIDForDolbyVision = /* 'pred' */ 0x70726564, /* Video Output Integers */ bmdDeckLinkConfigVideoOutputConnection = /* 'vocn' */ 0x766F636E, bmdDeckLinkConfigVideoOutputConversionMode = /* 'vocm' */ 0x766F636D, bmdDeckLinkConfigVideoOutputConversionColorspaceDestination = /* 'vccd' */ 0x76636364, // Parameter is of type BMDColorspace bmdDeckLinkConfigVideoOutputConversionColorspaceSource = /* 'vccs' */ 0x76636373, // Parameter is of type BMDColorspace bmdDeckLinkConfigAnalogVideoOutputFlags = /* 'avof' */ 0x61766F66, bmdDeckLinkConfigReferenceInputTimingOffset = /* 'glot' */ 0x676C6F74, bmdDeckLinkConfigReferenceOutputMode = /* 'glOm' */ 0x676C4F6D, bmdDeckLinkConfigVideoOutputIdleOperation = /* 'voio' */ 0x766F696F, bmdDeckLinkConfigDefaultVideoOutputMode = /* 'dvom' */ 0x64766F6D, bmdDeckLinkConfigDefaultVideoOutputModeFlags = /* 'dvof' */ 0x64766F66, bmdDeckLinkConfigSDIOutputLinkConfiguration = /* 'solc' */ 0x736F6C63, bmdDeckLinkConfigHDMITimecodePacking = /* 'htpk' */ 0x6874706B, bmdDeckLinkConfigPlaybackGroup = /* 'plgr' */ 0x706C6772, /* Video Output Floats */ bmdDeckLinkConfigVideoOutputComponentLumaGain = /* 'oclg' */ 0x6F636C67, bmdDeckLinkConfigVideoOutputComponentChromaBlueGain = /* 'occb' */ 0x6F636362, bmdDeckLinkConfigVideoOutputComponentChromaRedGain = /* 'occr' */ 0x6F636372, bmdDeckLinkConfigVideoOutputCompositeLumaGain = /* 'oilg' */ 0x6F696C67, bmdDeckLinkConfigVideoOutputCompositeChromaGain = /* 'oicg' */ 0x6F696367, bmdDeckLinkConfigVideoOutputSVideoLumaGain = /* 'oslg' */ 0x6F736C67, bmdDeckLinkConfigVideoOutputSVideoChromaGain = /* 'oscg' */ 0x6F736367, bmdDeckLinkConfigDolbyVisionCMVersion = /* 'dvvr' */ 0x64767672, bmdDeckLinkConfigDolbyVisionMasterMinimumNits = /* 'mnnt' */ 0x6D6E6E74, bmdDeckLinkConfigDolbyVisionMasterMaximumNits = /* 'mxnt' */ 0x6D786E74, /* Video Input Flags */ bmdDeckLinkConfigVideoInputScanning = /* 'visc' */ 0x76697363, // Applicable to H264 Pro Recorder only bmdDeckLinkConfigUseDedicatedLTCInput = /* 'dltc' */ 0x646C7463, // Use timecode from LTC input instead of SDI stream bmdDeckLinkConfigSDIInput3DPayloadOverride = /* '3dds' */ 0x33646473, bmdDeckLinkConfigCapture1080pAsPsF = /* 'cfpr' */ 0x63667072, /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = /* 'vicn' */ 0x7669636E, bmdDeckLinkConfigAnalogVideoInputFlags = /* 'avif' */ 0x61766966, bmdDeckLinkConfigVideoInputConversionMode = /* 'vicm' */ 0x7669636D, bmdDeckLinkConfig32PulldownSequenceInitialTimecodeFrame = /* 'pdif' */ 0x70646966, bmdDeckLinkConfigVANCSourceLine1Mapping = /* 'vsl1' */ 0x76736C31, bmdDeckLinkConfigVANCSourceLine2Mapping = /* 'vsl2' */ 0x76736C32, bmdDeckLinkConfigVANCSourceLine3Mapping = /* 'vsl3' */ 0x76736C33, bmdDeckLinkConfigCapturePassThroughMode = /* 'cptm' */ 0x6370746D, bmdDeckLinkConfigCaptureGroup = /* 'cpgr' */ 0x63706772, /* Video Input Floats */ bmdDeckLinkConfigVideoInputComponentLumaGain = /* 'iclg' */ 0x69636C67, bmdDeckLinkConfigVideoInputComponentChromaBlueGain = /* 'iccb' */ 0x69636362, bmdDeckLinkConfigVideoInputComponentChromaRedGain = /* 'iccr' */ 0x69636372, bmdDeckLinkConfigVideoInputCompositeLumaGain = /* 'iilg' */ 0x69696C67, bmdDeckLinkConfigVideoInputCompositeChromaGain = /* 'iicg' */ 0x69696367, bmdDeckLinkConfigVideoInputSVideoLumaGain = /* 'islg' */ 0x69736C67, bmdDeckLinkConfigVideoInputSVideoChromaGain = /* 'iscg' */ 0x69736367, /* Keying Integers */ bmdDeckLinkConfigInternalKeyingAncillaryDataSource = /* 'ikas' */ 0x696B6173, /* Audio Input Flags */ bmdDeckLinkConfigMicrophonePhantomPower = /* 'mphp' */ 0x6D706870, /* Audio Input Integers */ bmdDeckLinkConfigAudioInputConnection = /* 'aicn' */ 0x6169636E, /* Audio Input Floats */ bmdDeckLinkConfigAnalogAudioInputScaleChannel1 = /* 'ais1' */ 0x61697331, bmdDeckLinkConfigAnalogAudioInputScaleChannel2 = /* 'ais2' */ 0x61697332, bmdDeckLinkConfigAnalogAudioInputScaleChannel3 = /* 'ais3' */ 0x61697333, bmdDeckLinkConfigAnalogAudioInputScaleChannel4 = /* 'ais4' */ 0x61697334, bmdDeckLinkConfigDigitalAudioInputScale = /* 'dais' */ 0x64616973, bmdDeckLinkConfigMicrophoneInputGain = /* 'micg' */ 0x6D696367, /* Audio Output Integers */ bmdDeckLinkConfigAudioOutputAESAnalogSwitch = /* 'aoaa' */ 0x616F6161, /* Audio Output Floats */ bmdDeckLinkConfigAnalogAudioOutputScaleChannel1 = /* 'aos1' */ 0x616F7331, bmdDeckLinkConfigAnalogAudioOutputScaleChannel2 = /* 'aos2' */ 0x616F7332, bmdDeckLinkConfigAnalogAudioOutputScaleChannel3 = /* 'aos3' */ 0x616F7333, bmdDeckLinkConfigAnalogAudioOutputScaleChannel4 = /* 'aos4' */ 0x616F7334, bmdDeckLinkConfigDigitalAudioOutputScale = /* 'daos' */ 0x64616F73, bmdDeckLinkConfigHeadphoneVolume = /* 'hvol' */ 0x68766F6C, /* Network Flags */ bmdDeckLinkConfigEthernetUseDHCP = /* 'DHCP' */ 0x44484350, bmdDeckLinkConfigEthernetPTPFollowerOnly = /* 'PTPf' */ 0x50545066, bmdDeckLinkConfigEthernetPTPUseUDPEncapsulation = /* 'PTPU' */ 0x50545055, /* Network Integers */ bmdDeckLinkConfigEthernetPTPPriority1 = /* 'PTP1' */ 0x50545031, bmdDeckLinkConfigEthernetPTPPriority2 = /* 'PTP2' */ 0x50545032, bmdDeckLinkConfigEthernetPTPDomain = /* 'PTPD' */ 0x50545044, bmdDeckLinkConfigEthernetPTPLogAnnounceInterval = /* 'PTPA' */ 0x50545041, /* Network Strings */ bmdDeckLinkConfigEthernetStaticLocalIPAddress = /* 'nsip' */ 0x6E736970, bmdDeckLinkConfigEthernetStaticSubnetMask = /* 'nssm' */ 0x6E73736D, bmdDeckLinkConfigEthernetStaticGatewayIPAddress = /* 'nsgw' */ 0x6E736777, bmdDeckLinkConfigEthernetStaticPrimaryDNS = /* 'nspd' */ 0x6E737064, bmdDeckLinkConfigEthernetStaticSecondaryDNS = /* 'nssd' */ 0x6E737364, bmdDeckLinkConfigEthernetVideoOutputAddress = /* 'noav' */ 0x6E6F6176, bmdDeckLinkConfigEthernetAudioOutputAddress = /* 'noaa' */ 0x6E6F6161, bmdDeckLinkConfigEthernetAncillaryOutputAddress = /* 'noaA' */ 0x6E6F6141, bmdDeckLinkConfigEthernetAudioOutputChannelOrder = /* 'caco' */ 0x6361636F, /* Device Information Strings */ bmdDeckLinkConfigDeviceInformationLabel = /* 'dila' */ 0x64696C61, bmdDeckLinkConfigDeviceInformationSerialNumber = /* 'disn' */ 0x6469736E, bmdDeckLinkConfigDeviceInformationCompany = /* 'dico' */ 0x6469636F, bmdDeckLinkConfigDeviceInformationPhone = /* 'diph' */ 0x64697068, bmdDeckLinkConfigDeviceInformationEmail = /* 'diem' */ 0x6469656D, bmdDeckLinkConfigDeviceInformationDate = /* 'dida' */ 0x64696461, /* Deck Control Integers */ bmdDeckLinkConfigDeckControlConnection = /* 'dcco' */ 0x6463636F }; /* Enum BMDDeckLinkEncoderConfigurationID - DeckLink Encoder Configuration ID */ typedef uint32_t BMDDeckLinkEncoderConfigurationID; enum _BMDDeckLinkEncoderConfigurationID { /* Video Encoder Integers */ bmdDeckLinkEncoderConfigPreferredBitDepth = /* 'epbr' */ 0x65706272, bmdDeckLinkEncoderConfigFrameCodingMode = /* 'efcm' */ 0x6566636D, /* HEVC/H.265 Encoder Integers */ bmdDeckLinkEncoderConfigH265TargetBitrate = /* 'htbr' */ 0x68746272, /* DNxHR/DNxHD Compression ID */ bmdDeckLinkEncoderConfigDNxHRCompressionID = /* 'dcid' */ 0x64636964, /* DNxHR/DNxHD Level */ bmdDeckLinkEncoderConfigDNxHRLevel = /* 'dlev' */ 0x646C6576, /* Encoded Sample Decriptions */ bmdDeckLinkEncoderConfigMPEG4SampleDescription = /* 'stsE' */ 0x73747345, // Full MPEG4 sample description (aka SampleEntry of an 'stsd' atom-box). Useful for MediaFoundation, QuickTime, MKV and more bmdDeckLinkEncoderConfigMPEG4CodecSpecificDesc = /* 'esds' */ 0x65736473 // Sample description extensions only (atom stream, each with size and fourCC header). Useful for AVFoundation, VideoToolbox, MKV and more }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkConfiguration; class IDeckLinkEncoderConfiguration; /* Interface IDeckLinkConfiguration - DeckLink Configuration interface */ class BMD_PUBLIC IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool* value) = 0; virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double* value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ CFStringRef value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ CFStringRef* value) = 0; virtual HRESULT WriteConfigurationToPreferences (void) = 0; protected: virtual ~IDeckLinkConfiguration () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderConfiguration - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */ class BMD_PUBLIC IDeckLinkEncoderConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ bool* value) = 0; virtual HRESULT SetInt (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ int64_t value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ double value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ double* value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ CFStringRef value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ CFStringRef* value) = 0; virtual HRESULT GetBytes (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ void* buffer /* optional */, /* in, out */ uint32_t* bufferSize) = 0; protected: virtual ~IDeckLinkEncoderConfiguration () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPICONFIGURATION_H) */ mlt-7.38.0/src/modules/decklink/darwin/DeckLinkAPIDeckControl.h000664 000000 000000 00000031472 15172202314 024217 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPIDECKCONTROL_H #define BMD_DECKLINKAPIDECKCONTROL_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkDeckControlStatusCallback = /* 53436FFB-B434-4906-BADC-AE3060FFE8EF */ { 0x53,0x43,0x6F,0xFB,0xB4,0x34,0x49,0x06,0xBA,0xDC,0xAE,0x30,0x60,0xFF,0xE8,0xEF }; BMD_CONST REFIID IID_IDeckLinkDeckControl = /* 8E1C3ACE-19C7-4E00-8B92-D80431D958BE */ { 0x8E,0x1C,0x3A,0xCE,0x19,0xC7,0x4E,0x00,0x8B,0x92,0xD8,0x04,0x31,0xD9,0x58,0xBE }; /* Enum BMDDeckControlMode - DeckControl mode */ typedef uint32_t BMDDeckControlMode; enum _BMDDeckControlMode { bmdDeckControlNotOpened = /* 'ntop' */ 0x6E746F70, bmdDeckControlVTRControlMode = /* 'vtrc' */ 0x76747263, bmdDeckControlExportMode = /* 'expm' */ 0x6578706D, bmdDeckControlCaptureMode = /* 'capm' */ 0x6361706D }; /* Enum BMDDeckControlEvent - DeckControl event */ typedef uint32_t BMDDeckControlEvent; enum _BMDDeckControlEvent { bmdDeckControlAbortedEvent = /* 'abte' */ 0x61627465, // This event is triggered when a capture or edit-to-tape operation is aborted. /* Export-To-Tape events */ bmdDeckControlPrepareForExportEvent = /* 'pfee' */ 0x70666565, // This event is triggered a few frames before reaching the in-point. IDeckLinkInput::StartScheduledPlayback should be called at this point. bmdDeckControlExportCompleteEvent = /* 'exce' */ 0x65786365, // This event is triggered a few frames after reaching the out-point. At this point, it is safe to stop playback. Upon reception of this event the deck's control mode is set back to bmdDeckControlVTRControlMode. /* Capture events */ bmdDeckControlPrepareForCaptureEvent = /* 'pfce' */ 0x70666365, // This event is triggered a few frames before reaching the in-point. The serial timecode attached to IDeckLinkVideoInputFrames is now valid. bmdDeckControlCaptureCompleteEvent = /* 'ccev' */ 0x63636576 // This event is triggered a few frames after reaching the out-point. Upon reception of this event the deck's control mode is set back to bmdDeckControlVTRControlMode. }; /* Enum BMDDeckControlVTRControlState - VTR Control state */ typedef uint32_t BMDDeckControlVTRControlState; enum _BMDDeckControlVTRControlState { bmdDeckControlNotInVTRControlMode = /* 'nvcm' */ 0x6E76636D, bmdDeckControlVTRControlPlaying = /* 'vtrp' */ 0x76747270, bmdDeckControlVTRControlRecording = /* 'vtrr' */ 0x76747272, bmdDeckControlVTRControlStill = /* 'vtra' */ 0x76747261, bmdDeckControlVTRControlShuttleForward = /* 'vtsf' */ 0x76747366, bmdDeckControlVTRControlShuttleReverse = /* 'vtsr' */ 0x76747372, bmdDeckControlVTRControlJogForward = /* 'vtjf' */ 0x76746A66, bmdDeckControlVTRControlJogReverse = /* 'vtjr' */ 0x76746A72, bmdDeckControlVTRControlStopped = /* 'vtro' */ 0x7674726F }; /* Enum BMDDeckControlStatusFlags - Deck Control status flags */ typedef uint32_t BMDDeckControlStatusFlags; enum _BMDDeckControlStatusFlags { bmdDeckControlStatusDeckConnected = 1 << 0, bmdDeckControlStatusRemoteMode = 1 << 1, bmdDeckControlStatusRecordInhibited = 1 << 2, bmdDeckControlStatusCassetteOut = 1 << 3 }; /* Enum BMDDeckControlExportModeOpsFlags - Export mode flags */ typedef uint32_t BMDDeckControlExportModeOpsFlags; enum _BMDDeckControlExportModeOpsFlags { bmdDeckControlExportModeInsertVideo = 1 << 0, bmdDeckControlExportModeInsertAudio1 = 1 << 1, bmdDeckControlExportModeInsertAudio2 = 1 << 2, bmdDeckControlExportModeInsertAudio3 = 1 << 3, bmdDeckControlExportModeInsertAudio4 = 1 << 4, bmdDeckControlExportModeInsertAudio5 = 1 << 5, bmdDeckControlExportModeInsertAudio6 = 1 << 6, bmdDeckControlExportModeInsertAudio7 = 1 << 7, bmdDeckControlExportModeInsertAudio8 = 1 << 8, bmdDeckControlExportModeInsertAudio9 = 1 << 9, bmdDeckControlExportModeInsertAudio10 = 1 << 10, bmdDeckControlExportModeInsertAudio11 = 1 << 11, bmdDeckControlExportModeInsertAudio12 = 1 << 12, bmdDeckControlExportModeInsertTimeCode = 1 << 13, bmdDeckControlExportModeInsertAssemble = 1 << 14, bmdDeckControlExportModeInsertPreview = 1 << 15, bmdDeckControlUseManualExport = 1 << 16 }; /* Enum BMDDeckControlError - Deck Control error */ typedef uint32_t BMDDeckControlError; enum _BMDDeckControlError { bmdDeckControlNoError = /* 'noer' */ 0x6E6F6572, bmdDeckControlModeError = /* 'moer' */ 0x6D6F6572, bmdDeckControlMissedInPointError = /* 'mier' */ 0x6D696572, bmdDeckControlDeckTimeoutError = /* 'dter' */ 0x64746572, bmdDeckControlCommandFailedError = /* 'cfer' */ 0x63666572, bmdDeckControlDeviceAlreadyOpenedError = /* 'dalo' */ 0x64616C6F, bmdDeckControlFailedToOpenDeviceError = /* 'fder' */ 0x66646572, bmdDeckControlInLocalModeError = /* 'lmer' */ 0x6C6D6572, bmdDeckControlEndOfTapeError = /* 'eter' */ 0x65746572, bmdDeckControlUserAbortError = /* 'uaer' */ 0x75616572, bmdDeckControlNoTapeInDeckError = /* 'nter' */ 0x6E746572, bmdDeckControlNoVideoFromCardError = /* 'nvfc' */ 0x6E766663, bmdDeckControlNoCommunicationError = /* 'ncom' */ 0x6E636F6D, bmdDeckControlBufferTooSmallError = /* 'btsm' */ 0x6274736D, bmdDeckControlBadChecksumError = /* 'chks' */ 0x63686B73, bmdDeckControlUnknownError = /* 'uner' */ 0x756E6572 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkDeckControlStatusCallback; class IDeckLinkDeckControl; /* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */ class BMD_PUBLIC IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState newState, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0; protected: virtual ~IDeckLinkDeckControlStatusCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkDeckControl - Deck Control main interface */ class BMD_PUBLIC IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Close (/* in */ bool standbyOn) = 0; virtual HRESULT GetCurrentState (/* out */ BMDDeckControlMode* mode, /* out */ BMDDeckControlVTRControlState* vtrControlState, /* out */ BMDDeckControlStatusFlags* flags) = 0; virtual HRESULT SetStandby (/* in */ bool standbyOn) = 0; virtual HRESULT SendCommand (/* in */ uint8_t* inBuffer, /* in */ uint32_t inBufferSize, /* out */ uint8_t* outBuffer, /* out */ uint32_t* outDataSize, /* in */ uint32_t outBufferSize, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Play (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT Stop (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT TogglePlayStop (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT Eject (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT GoToTimecode (/* in */ BMDTimecodeBCD timecode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT FastForward (/* in */ bool viewTape, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Rewind (/* in */ bool viewTape, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT StepForward (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT StepBack (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT Jog (/* in */ double rate, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Shuttle (/* in */ double rate, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT GetTimecodeString (/* out */ CFStringRef* currentTimeCode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT GetTimecode (/* out */ IDeckLinkTimecode** currentTimecode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT GetTimecodeBCD (/* out */ BMDTimecodeBCD* currentTimecode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT SetPreroll (/* in */ uint32_t prerollSeconds) = 0; virtual HRESULT GetPreroll (/* out */ uint32_t* prerollSeconds) = 0; virtual HRESULT SetExportOffset (/* in */ int32_t exportOffsetFields) = 0; virtual HRESULT GetExportOffset (/* out */ int32_t* exportOffsetFields) = 0; virtual HRESULT GetManualExportOffset (/* out */ int32_t* deckManualExportOffsetFields) = 0; virtual HRESULT SetCaptureOffset (/* in */ int32_t captureOffsetFields) = 0; virtual HRESULT GetCaptureOffset (/* out */ int32_t* captureOffsetFields) = 0; virtual HRESULT StartExport (/* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* in */ BMDDeckControlExportModeOpsFlags exportModeOps, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT StartCapture (/* in */ bool useVITC, /* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT GetDeviceID (/* out */ uint16_t* deviceId, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Abort (void) = 0; virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback* callback) = 0; protected: virtual ~IDeckLinkDeckControl () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPIDECKCONTROL_H) */ mlt-7.38.0/src/modules/decklink/darwin/DeckLinkAPIDiscovery.h000664 000000 000000 00000005006 15172202314 023751 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPIDISCOVERY_H #define BMD_DECKLINKAPIDISCOVERY_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLink = /* C418FBDD-0587-48ED-8FE5-640F0A14AF91 */ { 0xC4,0x18,0xFB,0xDD,0x05,0x87,0x48,0xED,0x8F,0xE5,0x64,0x0F,0x0A,0x14,0xAF,0x91 }; #if defined(__cplusplus) // Forward Declarations class IDeckLink; /* Interface IDeckLink - Represents a DeckLink device */ class BMD_PUBLIC IDeckLink : public IUnknown { public: virtual HRESULT GetModelName (/* out */ CFStringRef* modelName) = 0; virtual HRESULT GetDisplayName (/* out */ CFStringRef* displayName) = 0; protected: virtual ~IDeckLink () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPIDISCOVERY_H) */ mlt-7.38.0/src/modules/decklink/darwin/DeckLinkAPIDispatch.cpp000664 000000 000000 00000022544 15172202314 024102 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2009 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation (the ** "Software") to use, reproduce, display, distribute, sub-license, execute, ** and transmit the Software, and to prepare derivative works of the Software, ** and to permit third-parties to whom the Software is furnished to do so, in ** accordance with: ** ** (1) if the Software is obtained from Blackmagic Design, the End User License ** Agreement for the Software Development Kit ("EULA") available at ** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or ** ** (2) if the Software is obtained from any third party, such licensing terms ** as notified by that third party, ** ** and all subject to the following: ** ** (3) the copyright notices in the Software and this entire statement, ** including the above license grant, this restriction and the following ** disclaimer, must be included in all copies of the Software, in whole or in ** part, and all derivative works of the Software, unless such copies or ** derivative works are solely in the form of machine-executable object code ** generated by a source language processor. ** ** (4) 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** ** A copy of the Software is available free of charge at ** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. ** ** -LICENSE-END- */ /* DeckLinkAPIDispatch.cpp */ #include "DeckLinkAPI.h" #include #if BLACKMAGIC_DECKLINK_API_MAGIC != 1 #error The DeckLink API version of DeckLinkAPIDispatch.cpp is not the same version as DeckLinkAPI.h #endif #define kDeckLinkAPI_BundlePath "/Library/Frameworks/DeckLinkAPI.framework" typedef IDeckLinkIterator* (*CreateIteratorFunc)(void); typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGL3ScreenPreviewHelperFunc)(void); typedef IDeckLinkMetalScreenPreviewHelper* (*CreateMetalScreenPreviewHelperFunc)(void); typedef IDeckLinkCocoaScreenPreviewCallback* (*CreateCocoaScreenPreviewFunc)(void*); typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void); static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; static CFBundleRef gDeckLinkAPIBundleRef = NULL; static CreateIteratorFunc gCreateIteratorFunc = NULL; static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateOpenGL3ScreenPreviewHelperFunc gCreateOpenGL3PreviewFunc = NULL; static CreateMetalScreenPreviewHelperFunc gCreateMetalPreviewFunc = NULL; static CreateCocoaScreenPreviewFunc gCreateCocoaPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc= NULL; static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL; static void InitDeckLinkAPI (void) { CFURLRef bundleURL; bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(kDeckLinkAPI_BundlePath), kCFURLPOSIXPathStyle, true); if (bundleURL != NULL) { gDeckLinkAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); if (gDeckLinkAPIBundleRef != NULL) { gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0004")); gCreateAPIInformationFunc = (CreateAPIInformationFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkAPIInformationInstance_0001")); gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateOpenGLScreenPreviewHelper_0002")); gCreateOpenGL3PreviewFunc = (CreateOpenGL3ScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateOpenGL3ScreenPreviewHelper_0002")); gCreateMetalPreviewFunc = (CreateMetalScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateMetalScreenPreviewHelper_0002")); gCreateCocoaPreviewFunc = (CreateCocoaScreenPreviewFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateCocoaScreenPreview_0002")); gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoConversionInstance_0002")); gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkDiscoveryInstance_0003")); gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoFrameAncillaryPacketsInstance_0001")); } CFRelease(bundleURL); } } bool IsDeckLinkAPIPresent (void) { // If the DeckLink API bundle was successfully loaded, return this knowledge to the caller if (gDeckLinkAPIBundleRef != NULL) return true; return false; } IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); } IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateAPIInformationFunc == NULL) return NULL; return gCreateAPIInformationFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGL3ScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateOpenGL3PreviewFunc == NULL) return NULL; return gCreateOpenGL3PreviewFunc(); } IDeckLinkMetalScreenPreviewHelper* CreateMetalScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateMetalPreviewFunc == NULL) return NULL; return gCreateMetalPreviewFunc(); } IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* parentView) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateCocoaPreviewFunc == NULL) return NULL; return gCreateCocoaPreviewFunc(parentView); } IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); } IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateDeckLinkDiscoveryFunc == NULL) return NULL; return gCreateDeckLinkDiscoveryFunc(); } IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateVideoFrameAncillaryPacketsFunc == NULL) return NULL; return gCreateVideoFrameAncillaryPacketsFunc(); } #define kBMDStreamingAPI_BundlePath "/Library/Application Support/Blackmagic Design/Streaming/BMDStreamingAPI.bundle" typedef IBMDStreamingDiscovery* (*CreateDiscoveryFunc)(void); typedef IBMDStreamingH264NALParser* (*CreateNALParserFunc)(void); static pthread_once_t gBMDStreamingOnceControl = PTHREAD_ONCE_INIT; static CFBundleRef gBMDStreamingAPIBundleRef = NULL; static CreateDiscoveryFunc gCreateDiscoveryFunc = NULL; static CreateNALParserFunc gCreateNALParserFunc = NULL; static void InitBMDStreamingAPI(void) { CFURLRef bundleURL; bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(kBMDStreamingAPI_BundlePath), kCFURLPOSIXPathStyle, true); if (bundleURL != NULL) { gBMDStreamingAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); if (gBMDStreamingAPIBundleRef != NULL) { gCreateDiscoveryFunc = (CreateDiscoveryFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingDiscoveryInstance_0002")); gCreateNALParserFunc = (CreateNALParserFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingH264NALParser_0001")); } CFRelease(bundleURL); } } IBMDStreamingDiscovery* CreateBMDStreamingDiscoveryInstance() { pthread_once(&gBMDStreamingOnceControl, InitBMDStreamingAPI); if (gCreateDiscoveryFunc == NULL) return NULL; return gCreateDiscoveryFunc(); } IBMDStreamingH264NALParser* CreateBMDStreamingH264NALParser() { pthread_once(&gBMDStreamingOnceControl, InitBMDStreamingAPI); if (gCreateNALParserFunc == NULL) return NULL; return gCreateNALParserFunc(); } mlt-7.38.0/src/modules/decklink/darwin/DeckLinkAPIModes.h000664 000000 000000 00000041325 15172202314 023055 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPIMODES_H #define BMD_DECKLINKAPIMODES_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkDisplayModeIterator = /* 9C88499F-F601-4021-B80B-032E4EB41C35 */ { 0x9C,0x88,0x49,0x9F,0xF6,0x01,0x40,0x21,0xB8,0x0B,0x03,0x2E,0x4E,0xB4,0x1C,0x35 }; BMD_CONST REFIID IID_IDeckLinkDisplayMode = /* 3EB2C1AB-0A3D-4523-A3AD-F40D7FB14E78 */ { 0x3E,0xB2,0xC1,0xAB,0x0A,0x3D,0x45,0x23,0xA3,0xAD,0xF4,0x0D,0x7F,0xB1,0x4E,0x78 }; /* Enum BMDDisplayMode - BMDDisplayMode enumerates the video modes supported. */ typedef uint32_t BMDDisplayMode; enum _BMDDisplayMode { /* SD Modes */ bmdModeNTSC = /* 'ntsc' */ 0x6E747363, bmdModeNTSC2398 = /* 'nt23' */ 0x6E743233, // 3:2 pulldown bmdModePAL = /* 'pal ' */ 0x70616C20, bmdModeNTSCp = /* 'ntsp' */ 0x6E747370, bmdModePALp = /* 'palp' */ 0x70616C70, /* HD 1080 Modes */ bmdModeHD1080p2398 = /* '23ps' */ 0x32337073, bmdModeHD1080p24 = /* '24ps' */ 0x32347073, bmdModeHD1080p25 = /* 'Hp25' */ 0x48703235, bmdModeHD1080p2997 = /* 'Hp29' */ 0x48703239, bmdModeHD1080p30 = /* 'Hp30' */ 0x48703330, bmdModeHD1080p4795 = /* 'Hp47' */ 0x48703437, bmdModeHD1080p48 = /* 'Hp48' */ 0x48703438, bmdModeHD1080p50 = /* 'Hp50' */ 0x48703530, bmdModeHD1080p5994 = /* 'Hp59' */ 0x48703539, bmdModeHD1080p6000 = /* 'Hp60' */ 0x48703630, // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p9590 = /* 'Hp95' */ 0x48703935, bmdModeHD1080p96 = /* 'Hp96' */ 0x48703936, bmdModeHD1080p100 = /* 'Hp10' */ 0x48703130, bmdModeHD1080p11988 = /* 'Hp11' */ 0x48703131, bmdModeHD1080p120 = /* 'Hp12' */ 0x48703132, bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ bmdModeHD720p50 = /* 'hp50' */ 0x68703530, bmdModeHD720p5994 = /* 'hp59' */ 0x68703539, bmdModeHD720p60 = /* 'hp60' */ 0x68703630, /* 2K Modes */ bmdMode2k2398 = /* '2k23' */ 0x326B3233, bmdMode2k24 = /* '2k24' */ 0x326B3234, bmdMode2k25 = /* '2k25' */ 0x326B3235, /* 2K DCI Modes */ bmdMode2kDCI2398 = /* '2d23' */ 0x32643233, bmdMode2kDCI24 = /* '2d24' */ 0x32643234, bmdMode2kDCI25 = /* '2d25' */ 0x32643235, bmdMode2kDCI2997 = /* '2d29' */ 0x32643239, bmdMode2kDCI30 = /* '2d30' */ 0x32643330, bmdMode2kDCI4795 = /* '2d47' */ 0x32643437, bmdMode2kDCI48 = /* '2d48' */ 0x32643438, bmdMode2kDCI50 = /* '2d50' */ 0x32643530, bmdMode2kDCI5994 = /* '2d59' */ 0x32643539, bmdMode2kDCI60 = /* '2d60' */ 0x32643630, bmdMode2kDCI9590 = /* '2d95' */ 0x32643935, bmdMode2kDCI96 = /* '2d96' */ 0x32643936, bmdMode2kDCI100 = /* '2d10' */ 0x32643130, bmdMode2kDCI11988 = /* '2d11' */ 0x32643131, bmdMode2kDCI120 = /* '2d12' */ 0x32643132, /* 4K UHD Modes */ bmdMode4K2160p2398 = /* '4k23' */ 0x346B3233, bmdMode4K2160p24 = /* '4k24' */ 0x346B3234, bmdMode4K2160p25 = /* '4k25' */ 0x346B3235, bmdMode4K2160p2997 = /* '4k29' */ 0x346B3239, bmdMode4K2160p30 = /* '4k30' */ 0x346B3330, bmdMode4K2160p4795 = /* '4k47' */ 0x346B3437, bmdMode4K2160p48 = /* '4k48' */ 0x346B3438, bmdMode4K2160p50 = /* '4k50' */ 0x346B3530, bmdMode4K2160p5994 = /* '4k59' */ 0x346B3539, bmdMode4K2160p60 = /* '4k60' */ 0x346B3630, bmdMode4K2160p9590 = /* '4k95' */ 0x346B3935, bmdMode4K2160p96 = /* '4k96' */ 0x346B3936, bmdMode4K2160p100 = /* '4k10' */ 0x346B3130, bmdMode4K2160p11988 = /* '4k11' */ 0x346B3131, bmdMode4K2160p120 = /* '4k12' */ 0x346B3132, /* 4K DCI Modes */ bmdMode4kDCI2398 = /* '4d23' */ 0x34643233, bmdMode4kDCI24 = /* '4d24' */ 0x34643234, bmdMode4kDCI25 = /* '4d25' */ 0x34643235, bmdMode4kDCI2997 = /* '4d29' */ 0x34643239, bmdMode4kDCI30 = /* '4d30' */ 0x34643330, bmdMode4kDCI4795 = /* '4d47' */ 0x34643437, bmdMode4kDCI48 = /* '4d48' */ 0x34643438, bmdMode4kDCI50 = /* '4d50' */ 0x34643530, bmdMode4kDCI5994 = /* '4d59' */ 0x34643539, bmdMode4kDCI60 = /* '4d60' */ 0x34643630, bmdMode4kDCI9590 = /* '4d95' */ 0x34643935, bmdMode4kDCI96 = /* '4d96' */ 0x34643936, bmdMode4kDCI100 = /* '4d10' */ 0x34643130, bmdMode4kDCI11988 = /* '4d11' */ 0x34643131, bmdMode4kDCI120 = /* '4d12' */ 0x34643132, /* 8K UHD Modes */ bmdMode8K4320p2398 = /* '8k23' */ 0x386B3233, bmdMode8K4320p24 = /* '8k24' */ 0x386B3234, bmdMode8K4320p25 = /* '8k25' */ 0x386B3235, bmdMode8K4320p2997 = /* '8k29' */ 0x386B3239, bmdMode8K4320p30 = /* '8k30' */ 0x386B3330, bmdMode8K4320p4795 = /* '8k47' */ 0x386B3437, bmdMode8K4320p48 = /* '8k48' */ 0x386B3438, bmdMode8K4320p50 = /* '8k50' */ 0x386B3530, bmdMode8K4320p5994 = /* '8k59' */ 0x386B3539, bmdMode8K4320p60 = /* '8k60' */ 0x386B3630, /* 8K DCI Modes */ bmdMode8kDCI2398 = /* '8d23' */ 0x38643233, bmdMode8kDCI24 = /* '8d24' */ 0x38643234, bmdMode8kDCI25 = /* '8d25' */ 0x38643235, bmdMode8kDCI2997 = /* '8d29' */ 0x38643239, bmdMode8kDCI30 = /* '8d30' */ 0x38643330, bmdMode8kDCI4795 = /* '8d47' */ 0x38643437, bmdMode8kDCI48 = /* '8d48' */ 0x38643438, bmdMode8kDCI50 = /* '8d50' */ 0x38643530, bmdMode8kDCI5994 = /* '8d59' */ 0x38643539, bmdMode8kDCI60 = /* '8d60' */ 0x38643630, /* PC Modes */ bmdMode640x480p60 = /* 'vga6' */ 0x76676136, bmdMode800x600p60 = /* 'svg6' */ 0x73766736, bmdMode1440x900p50 = /* 'wxg5' */ 0x77786735, bmdMode1440x900p60 = /* 'wxg6' */ 0x77786736, bmdMode1440x1080p50 = /* 'sxg5' */ 0x73786735, bmdMode1440x1080p60 = /* 'sxg6' */ 0x73786736, bmdMode1600x1200p50 = /* 'uxg5' */ 0x75786735, bmdMode1600x1200p60 = /* 'uxg6' */ 0x75786736, bmdMode1920x1200p50 = /* 'wux5' */ 0x77757835, bmdMode1920x1200p60 = /* 'wux6' */ 0x77757836, bmdMode1920x1440p50 = /* '1945' */ 0x31393435, bmdMode1920x1440p60 = /* '1946' */ 0x31393436, bmdMode2560x1440p50 = /* 'wqh5' */ 0x77716835, bmdMode2560x1440p60 = /* 'wqh6' */ 0x77716836, bmdMode2560x1600p50 = /* 'wqx5' */ 0x77717835, bmdMode2560x1600p60 = /* 'wqx6' */ 0x77717836, /* Special Modes */ bmdModeUnknown = /* 'iunk' */ 0x69756E6B }; /* Enum BMDFieldDominance - BMDFieldDominance enumerates settings applicable to video fields. */ typedef uint32_t BMDFieldDominance; enum _BMDFieldDominance { bmdUnknownFieldDominance = 0, bmdLowerFieldFirst = /* 'lowr' */ 0x6C6F7772, bmdUpperFieldFirst = /* 'uppr' */ 0x75707072, bmdProgressiveFrame = /* 'prog' */ 0x70726F67, bmdProgressiveSegmentedFrame = /* 'psf ' */ 0x70736620 }; /* Enum BMDPixelFormat - Video pixel formats supported for output/input */ typedef uint32_t BMDPixelFormat; enum _BMDPixelFormat { bmdFormatUnspecified = 0, bmdFormat8BitYUV = /* '2vuy' */ 0x32767579, bmdFormat10BitYUV = /* 'v210' */ 0x76323130, bmdFormat10BitYUVA = /* 'Ay10' */ 0x41793130, // Big-endian YUVA 10 bit per component with SMPTE video levels (64-940) for YUV but full range alpha bmdFormat8BitARGB = 32, bmdFormat8BitBGRA = /* 'BGRA' */ 0x42475241, bmdFormat10BitRGB = /* 'r210' */ 0x72323130, // Big-endian RGB 10-bit per component with SMPTE video levels (64-940). Packed as 2:10:10:10 bmdFormat12BitRGB = /* 'R12B' */ 0x52313242, // Big-endian RGB 12-bit per component with full range (0-4095). Packed as 12-bit per component bmdFormat12BitRGBLE = /* 'R12L' */ 0x5231324C, // Little-endian RGB 12-bit per component with full range (0-4095). Packed as 12-bit per component bmdFormat10BitRGBXLE = /* 'R10l' */ 0x5231306C, // Little-endian 10-bit RGB with SMPTE video levels (64-940) bmdFormat10BitRGBX = /* 'R10b' */ 0x52313062, // Big-endian 10-bit RGB with SMPTE video levels (64-940) bmdFormatH265 = /* 'hev1' */ 0x68657631, // High Efficiency Video Coding (HEVC/h.265) /* AVID DNxHR */ bmdFormatDNxHR = /* 'AVdh' */ 0x41566468 }; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ typedef uint32_t BMDDisplayModeFlags; enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = 1 << 0, bmdDisplayModeColorspaceRec601 = 1 << 1, bmdDisplayModeColorspaceRec709 = 1 << 2, bmdDisplayModeColorspaceRec2020 = 1 << 3 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkDisplayModeIterator; class IDeckLinkDisplayMode; /* Interface IDeckLinkDisplayModeIterator - Enumerates over supported input/output display modes. */ class BMD_PUBLIC IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode** deckLinkDisplayMode) = 0; protected: virtual ~IDeckLinkDisplayModeIterator () {} // call Release method to drop reference count }; /* Interface IDeckLinkDisplayMode - Represents a display mode */ class BMD_PUBLIC IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT GetName (/* out */ CFStringRef* name) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; virtual long GetWidth (void) = 0; virtual long GetHeight (void) = 0; virtual HRESULT GetFrameRate (/* out */ BMDTimeValue* frameDuration, /* out */ BMDTimeScale* timeScale) = 0; virtual BMDFieldDominance GetFieldDominance (void) = 0; virtual BMDDisplayModeFlags GetFlags (void) = 0; protected: virtual ~IDeckLinkDisplayMode () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPIMODES_H) */ mlt-7.38.0/src/modules/decklink/darwin/DeckLinkAPIStreaming.h000664 000000 000000 00000044344 15172202314 023743 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPISTREAMING_H #define BMD_DECKLINKAPISTREAMING_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IBMDStreamingDeviceNotificationCallback = /* F9531D64-3305-4B29-A387-7F74BB0D0E84 */ { 0xF9,0x53,0x1D,0x64,0x33,0x05,0x4B,0x29,0xA3,0x87,0x7F,0x74,0xBB,0x0D,0x0E,0x84 }; BMD_CONST REFIID IID_IBMDStreamingH264InputCallback = /* 823C475F-55AE-46F9-890C-537CC5CEDCCA */ { 0x82,0x3C,0x47,0x5F,0x55,0xAE,0x46,0xF9,0x89,0x0C,0x53,0x7C,0xC5,0xCE,0xDC,0xCA }; BMD_CONST REFIID IID_IBMDStreamingDiscovery = /* 2C837444-F989-4D87-901A-47C8A36D096D */ { 0x2C,0x83,0x74,0x44,0xF9,0x89,0x4D,0x87,0x90,0x1A,0x47,0xC8,0xA3,0x6D,0x09,0x6D }; BMD_CONST REFIID IID_IBMDStreamingVideoEncodingMode = /* 1AB8035B-CD13-458D-B6DF-5E8F7C2141D9 */ { 0x1A,0xB8,0x03,0x5B,0xCD,0x13,0x45,0x8D,0xB6,0xDF,0x5E,0x8F,0x7C,0x21,0x41,0xD9 }; BMD_CONST REFIID IID_IBMDStreamingMutableVideoEncodingMode = /* 19BF7D90-1E0A-400D-B2C6-FFC4E78AD49D */ { 0x19,0xBF,0x7D,0x90,0x1E,0x0A,0x40,0x0D,0xB2,0xC6,0xFF,0xC4,0xE7,0x8A,0xD4,0x9D }; BMD_CONST REFIID IID_IBMDStreamingVideoEncodingModePresetIterator = /* 7AC731A3-C950-4AD0-804A-8377AA51C6C4 */ { 0x7A,0xC7,0x31,0xA3,0xC9,0x50,0x4A,0xD0,0x80,0x4A,0x83,0x77,0xAA,0x51,0xC6,0xC4 }; BMD_CONST REFIID IID_IBMDStreamingDeviceInput = /* 24B6B6EC-1727-44BB-9818-34FF086ACF98 */ { 0x24,0xB6,0xB6,0xEC,0x17,0x27,0x44,0xBB,0x98,0x18,0x34,0xFF,0x08,0x6A,0xCF,0x98 }; BMD_CONST REFIID IID_IBMDStreamingH264NALPacket = /* E260E955-14BE-4395-9775-9F02CC0A9D89 */ { 0xE2,0x60,0xE9,0x55,0x14,0xBE,0x43,0x95,0x97,0x75,0x9F,0x02,0xCC,0x0A,0x9D,0x89 }; BMD_CONST REFIID IID_IBMDStreamingAudioPacket = /* D9EB5902-1AD2-43F4-9E2C-3CFA50B5EE19 */ { 0xD9,0xEB,0x59,0x02,0x1A,0xD2,0x43,0xF4,0x9E,0x2C,0x3C,0xFA,0x50,0xB5,0xEE,0x19 }; BMD_CONST REFIID IID_IBMDStreamingMPEG2TSPacket = /* 91810D1C-4FB3-4AAA-AE56-FA301D3DFA4C */ { 0x91,0x81,0x0D,0x1C,0x4F,0xB3,0x4A,0xAA,0xAE,0x56,0xFA,0x30,0x1D,0x3D,0xFA,0x4C }; BMD_CONST REFIID IID_IBMDStreamingH264NALParser = /* 5867F18C-5BFA-4CCC-B2A7-9DFD140417D2 */ { 0x58,0x67,0xF1,0x8C,0x5B,0xFA,0x4C,0xCC,0xB2,0xA7,0x9D,0xFD,0x14,0x04,0x17,0xD2 }; /* Enum BMDStreamingDeviceMode - Device modes */ typedef uint32_t BMDStreamingDeviceMode; enum _BMDStreamingDeviceMode { bmdStreamingDeviceIdle = /* 'idle' */ 0x69646C65, bmdStreamingDeviceEncoding = /* 'enco' */ 0x656E636F, bmdStreamingDeviceStopping = /* 'stop' */ 0x73746F70, bmdStreamingDeviceUnknown = /* 'munk' */ 0x6D756E6B }; /* Enum BMDStreamingEncodingFrameRate - Encoded frame rates */ typedef uint32_t BMDStreamingEncodingFrameRate; enum _BMDStreamingEncodingFrameRate { /* Interlaced rates */ bmdStreamingEncodedFrameRate50i = /* 'e50i' */ 0x65353069, bmdStreamingEncodedFrameRate5994i = /* 'e59i' */ 0x65353969, bmdStreamingEncodedFrameRate60i = /* 'e60i' */ 0x65363069, /* Progressive rates */ bmdStreamingEncodedFrameRate2398p = /* 'e23p' */ 0x65323370, bmdStreamingEncodedFrameRate24p = /* 'e24p' */ 0x65323470, bmdStreamingEncodedFrameRate25p = /* 'e25p' */ 0x65323570, bmdStreamingEncodedFrameRate2997p = /* 'e29p' */ 0x65323970, bmdStreamingEncodedFrameRate30p = /* 'e30p' */ 0x65333070, bmdStreamingEncodedFrameRate50p = /* 'e50p' */ 0x65353070, bmdStreamingEncodedFrameRate5994p = /* 'e59p' */ 0x65353970, bmdStreamingEncodedFrameRate60p = /* 'e60p' */ 0x65363070 }; /* Enum BMDStreamingEncodingSupport - Output encoding mode supported flag */ typedef uint32_t BMDStreamingEncodingSupport; enum _BMDStreamingEncodingSupport { bmdStreamingEncodingModeNotSupported = 0, bmdStreamingEncodingModeSupported, bmdStreamingEncodingModeSupportedWithChanges }; /* Enum BMDStreamingVideoCodec - Video codecs */ typedef uint32_t BMDStreamingVideoCodec; enum _BMDStreamingVideoCodec { bmdStreamingVideoCodecH264 = /* 'H264' */ 0x48323634 }; /* Enum BMDStreamingH264Profile - H264 encoding profile */ typedef uint32_t BMDStreamingH264Profile; enum _BMDStreamingH264Profile { bmdStreamingH264ProfileHigh = /* 'high' */ 0x68696768, bmdStreamingH264ProfileMain = /* 'main' */ 0x6D61696E, bmdStreamingH264ProfileBaseline = /* 'base' */ 0x62617365 }; /* Enum BMDStreamingH264Level - H264 encoding level */ typedef uint32_t BMDStreamingH264Level; enum _BMDStreamingH264Level { bmdStreamingH264Level12 = /* 'lv12' */ 0x6C763132, bmdStreamingH264Level13 = /* 'lv13' */ 0x6C763133, bmdStreamingH264Level2 = /* 'lv2 ' */ 0x6C763220, bmdStreamingH264Level21 = /* 'lv21' */ 0x6C763231, bmdStreamingH264Level22 = /* 'lv22' */ 0x6C763232, bmdStreamingH264Level3 = /* 'lv3 ' */ 0x6C763320, bmdStreamingH264Level31 = /* 'lv31' */ 0x6C763331, bmdStreamingH264Level32 = /* 'lv32' */ 0x6C763332, bmdStreamingH264Level4 = /* 'lv4 ' */ 0x6C763420, bmdStreamingH264Level41 = /* 'lv41' */ 0x6C763431, bmdStreamingH264Level42 = /* 'lv42' */ 0x6C763432 }; /* Enum BMDStreamingH264EntropyCoding - H264 entropy coding */ typedef uint32_t BMDStreamingH264EntropyCoding; enum _BMDStreamingH264EntropyCoding { bmdStreamingH264EntropyCodingCAVLC = /* 'EVLC' */ 0x45564C43, bmdStreamingH264EntropyCodingCABAC = /* 'EBAC' */ 0x45424143 }; /* Enum BMDStreamingAudioCodec - Audio codecs */ typedef uint32_t BMDStreamingAudioCodec; enum _BMDStreamingAudioCodec { bmdStreamingAudioCodecAAC = /* 'AAC ' */ 0x41414320 }; /* Enum BMDStreamingEncodingModePropertyID - Encoding mode properties */ typedef uint32_t BMDStreamingEncodingModePropertyID; enum _BMDStreamingEncodingModePropertyID { /* Integers, Video Properties */ bmdStreamingEncodingPropertyVideoFrameRate = /* 'vfrt' */ 0x76667274, // Uses values of type BMDStreamingEncodingFrameRate bmdStreamingEncodingPropertyVideoBitRateKbps = /* 'vbrt' */ 0x76627274, /* Integers, H264 Properties */ bmdStreamingEncodingPropertyH264Profile = /* 'hprf' */ 0x68707266, bmdStreamingEncodingPropertyH264Level = /* 'hlvl' */ 0x686C766C, bmdStreamingEncodingPropertyH264EntropyCoding = /* 'hent' */ 0x68656E74, /* Flags, H264 Properties */ bmdStreamingEncodingPropertyH264HasBFrames = /* 'hBfr' */ 0x68426672, /* Integers, Audio Properties */ bmdStreamingEncodingPropertyAudioCodec = /* 'acdc' */ 0x61636463, bmdStreamingEncodingPropertyAudioSampleRate = /* 'asrt' */ 0x61737274, bmdStreamingEncodingPropertyAudioChannelCount = /* 'achc' */ 0x61636863, bmdStreamingEncodingPropertyAudioBitRateKbps = /* 'abrt' */ 0x61627274 }; #if defined(__cplusplus) // Forward Declarations class IBMDStreamingDeviceNotificationCallback; class IBMDStreamingH264InputCallback; class IBMDStreamingDiscovery; class IBMDStreamingVideoEncodingMode; class IBMDStreamingMutableVideoEncodingMode; class IBMDStreamingVideoEncodingModePresetIterator; class IBMDStreamingDeviceInput; class IBMDStreamingH264NALPacket; class IBMDStreamingAudioPacket; class IBMDStreamingMPEG2TSPacket; class IBMDStreamingH264NALParser; /* Interface IBMDStreamingDeviceNotificationCallback - Device notification callbacks. */ class BMD_PUBLIC IBMDStreamingDeviceNotificationCallback : public IUnknown { public: virtual HRESULT StreamingDeviceArrived (/* in */ IDeckLink* device) = 0; virtual HRESULT StreamingDeviceRemoved (/* in */ IDeckLink* device) = 0; virtual HRESULT StreamingDeviceModeChanged (/* in */ IDeckLink* device, /* in */ BMDStreamingDeviceMode mode) = 0; protected: virtual ~IBMDStreamingDeviceNotificationCallback () {} // call Release method to drop reference count }; /* Interface IBMDStreamingH264InputCallback - H264 input callbacks. */ class BMD_PUBLIC IBMDStreamingH264InputCallback : public IUnknown { public: virtual HRESULT H264NALPacketArrived (/* in */ IBMDStreamingH264NALPacket* nalPacket) = 0; virtual HRESULT H264AudioPacketArrived (/* in */ IBMDStreamingAudioPacket* audioPacket) = 0; virtual HRESULT MPEG2TSPacketArrived (/* in */ IBMDStreamingMPEG2TSPacket* tsPacket) = 0; virtual HRESULT H264VideoInputConnectorScanningChanged (void) = 0; virtual HRESULT H264VideoInputConnectorChanged (void) = 0; virtual HRESULT H264VideoInputModeChanged (void) = 0; protected: virtual ~IBMDStreamingH264InputCallback () {} // call Release method to drop reference count }; /* Interface IBMDStreamingDiscovery - Installs device notifications */ class BMD_PUBLIC IBMDStreamingDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IBMDStreamingDeviceNotificationCallback* theCallback) = 0; virtual HRESULT UninstallDeviceNotifications (void) = 0; protected: virtual ~IBMDStreamingDiscovery () {} // call Release method to drop reference count }; /* Interface IBMDStreamingVideoEncodingMode - Represents an encoded video mode. */ class BMD_PUBLIC IBMDStreamingVideoEncodingMode : public IUnknown { public: virtual HRESULT GetName (/* out */ CFStringRef* name) = 0; virtual unsigned int GetPresetID (void) = 0; virtual unsigned int GetSourcePositionX (void) = 0; virtual unsigned int GetSourcePositionY (void) = 0; virtual unsigned int GetSourceWidth (void) = 0; virtual unsigned int GetSourceHeight (void) = 0; virtual unsigned int GetDestWidth (void) = 0; virtual unsigned int GetDestHeight (void) = 0; virtual HRESULT GetFlag (/* in */ BMDStreamingEncodingModePropertyID cfgID, /* out */ bool* value) = 0; virtual HRESULT GetInt (/* in */ BMDStreamingEncodingModePropertyID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDStreamingEncodingModePropertyID cfgID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDStreamingEncodingModePropertyID cfgID, /* out */ CFStringRef* value) = 0; virtual HRESULT CreateMutableVideoEncodingMode (/* out */ IBMDStreamingMutableVideoEncodingMode** newEncodingMode) = 0; // Creates a mutable copy of the encoding mode protected: virtual ~IBMDStreamingVideoEncodingMode () {} // call Release method to drop reference count }; /* Interface IBMDStreamingMutableVideoEncodingMode - Represents a mutable encoded video mode. */ class BMD_PUBLIC IBMDStreamingMutableVideoEncodingMode : public IBMDStreamingVideoEncodingMode { public: virtual HRESULT SetSourceRect (/* in */ uint32_t posX, /* in */ uint32_t posY, /* in */ uint32_t width, /* in */ uint32_t height) = 0; virtual HRESULT SetDestSize (/* in */ uint32_t width, /* in */ uint32_t height) = 0; virtual HRESULT SetFlag (/* in */ BMDStreamingEncodingModePropertyID cfgID, /* in */ bool value) = 0; virtual HRESULT SetInt (/* in */ BMDStreamingEncodingModePropertyID cfgID, /* in */ int64_t value) = 0; virtual HRESULT SetFloat (/* in */ BMDStreamingEncodingModePropertyID cfgID, /* in */ double value) = 0; virtual HRESULT SetString (/* in */ BMDStreamingEncodingModePropertyID cfgID, /* in */ CFStringRef value) = 0; protected: virtual ~IBMDStreamingMutableVideoEncodingMode () {} // call Release method to drop reference count }; /* Interface IBMDStreamingVideoEncodingModePresetIterator - Enumerates encoding mode presets */ class BMD_PUBLIC IBMDStreamingVideoEncodingModePresetIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IBMDStreamingVideoEncodingMode** videoEncodingMode) = 0; protected: virtual ~IBMDStreamingVideoEncodingModePresetIterator () {} // call Release method to drop reference count }; /* Interface IBMDStreamingDeviceInput - Created by QueryInterface from IDeckLink */ class BMD_PUBLIC IBMDStreamingDeviceInput : public IUnknown { public: /* Input modes */ virtual HRESULT DoesSupportVideoInputMode (/* in */ BMDDisplayMode inputMode, /* out */ bool* result) = 0; virtual HRESULT GetVideoInputModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0; virtual HRESULT SetVideoInputMode (/* in */ BMDDisplayMode inputMode) = 0; virtual HRESULT GetCurrentDetectedVideoInputMode (/* out */ BMDDisplayMode* detectedMode) = 0; /* Capture modes */ virtual HRESULT GetVideoEncodingMode (/* out */ IBMDStreamingVideoEncodingMode** encodingMode) = 0; virtual HRESULT GetVideoEncodingModePresetIterator (/* in */ BMDDisplayMode inputMode, /* out */ IBMDStreamingVideoEncodingModePresetIterator** iterator) = 0; virtual HRESULT DoesSupportVideoEncodingMode (/* in */ BMDDisplayMode inputMode, /* in */ IBMDStreamingVideoEncodingMode* encodingMode, /* out */ BMDStreamingEncodingSupport* result, /* out */ IBMDStreamingVideoEncodingMode** changedEncodingMode) = 0; virtual HRESULT SetVideoEncodingMode (/* in */ IBMDStreamingVideoEncodingMode* encodingMode) = 0; /* Input control */ virtual HRESULT StartCapture (void) = 0; virtual HRESULT StopCapture (void) = 0; virtual HRESULT SetCallback (/* in */ IUnknown* theCallback) = 0; protected: virtual ~IBMDStreamingDeviceInput () {} // call Release method to drop reference count }; /* Interface IBMDStreamingH264NALPacket - Represent an H.264 NAL packet */ class BMD_PUBLIC IBMDStreamingH264NALPacket : public IUnknown { public: virtual long GetPayloadSize (void) = 0; virtual HRESULT GetBytes (/* out */ void** buffer) = 0; virtual HRESULT GetBytesWithSizePrefix (/* out */ void** buffer) = 0; // Contains a 32-bit unsigned big endian size prefix virtual HRESULT GetDisplayTime (/* in */ uint64_t requestedTimeScale, /* out */ uint64_t* displayTime) = 0; virtual HRESULT GetPacketIndex (/* out */ uint32_t* packetIndex) = 0; // Deprecated protected: virtual ~IBMDStreamingH264NALPacket () {} // call Release method to drop reference count }; /* Interface IBMDStreamingAudioPacket - Represents a chunk of audio data */ class BMD_PUBLIC IBMDStreamingAudioPacket : public IUnknown { public: virtual BMDStreamingAudioCodec GetCodec (void) = 0; virtual long GetPayloadSize (void) = 0; virtual HRESULT GetBytes (/* out */ void** buffer) = 0; virtual HRESULT GetPlayTime (/* in */ uint64_t requestedTimeScale, /* out */ uint64_t* playTime) = 0; virtual HRESULT GetPacketIndex (/* out */ uint32_t* packetIndex) = 0; // Deprecated protected: virtual ~IBMDStreamingAudioPacket () {} // call Release method to drop reference count }; /* Interface IBMDStreamingMPEG2TSPacket - Represent an MPEG2 Transport Stream packet */ class BMD_PUBLIC IBMDStreamingMPEG2TSPacket : public IUnknown { public: virtual long GetPayloadSize (void) = 0; virtual HRESULT GetBytes (/* out */ void** buffer) = 0; protected: virtual ~IBMDStreamingMPEG2TSPacket () {} // call Release method to drop reference count }; /* Interface IBMDStreamingH264NALParser - For basic NAL parsing */ class BMD_PUBLIC IBMDStreamingH264NALParser : public IUnknown { public: virtual HRESULT IsNALSequenceParameterSet (/* in */ IBMDStreamingH264NALPacket* nal) = 0; virtual HRESULT IsNALPictureParameterSet (/* in */ IBMDStreamingH264NALPacket* nal) = 0; virtual HRESULT GetProfileAndLevelFromSPS (/* in */ IBMDStreamingH264NALPacket* nal, /* out */ uint32_t* profileIdc, /* out */ uint32_t* profileCompatability, /* out */ uint32_t* levelIdc) = 0; protected: virtual ~IBMDStreamingH264NALParser () {} // call Release method to drop reference count }; /* Functions */ extern "C" { BMD_PUBLIC IBMDStreamingDiscovery* CreateBMDStreamingDiscoveryInstance(void); BMD_PUBLIC IBMDStreamingH264NALParser* CreateBMDStreamingH264NALParser(void); } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPISTREAMING_H) */ mlt-7.38.0/src/modules/decklink/darwin/DeckLinkAPITypes.h000664 000000 000000 00000012347 15172202314 023114 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPITYPES_H #define BMD_DECKLINKAPITYPES_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations typedef int64_t BMDTimeValue; typedef int64_t BMDTimeScale; typedef uint32_t BMDTimecodeBCD; typedef uint32_t BMDTimecodeUserBits; typedef int64_t BMDIPFlowID; // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkTimecode = /* BC6CFBD3-8317-4325-AC1C-1216391E9340 */ { 0xBC,0x6C,0xFB,0xD3,0x83,0x17,0x43,0x25,0xAC,0x1C,0x12,0x16,0x39,0x1E,0x93,0x40 }; /* Enum BMDTimecodeFlags - Timecode flags */ typedef uint32_t BMDTimecodeFlags; enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0, bmdTimecodeFieldMark = 1 << 1, bmdTimecodeColorFrame = 1 << 2, bmdTimecodeEmbedRecordingTrigger = 1 << 3, // On SDI recording trigger utilises a user-bit. bmdTimecodeRecordingTriggered = 1 << 4 }; /* Enum BMDVideoConnection - Video connection types */ typedef uint32_t BMDVideoConnection; enum _BMDVideoConnection { bmdVideoConnectionUnspecified = 0, bmdVideoConnectionSDI = 1 << 0, bmdVideoConnectionHDMI = 1 << 1, bmdVideoConnectionOpticalSDI = 1 << 2, bmdVideoConnectionComponent = 1 << 3, bmdVideoConnectionComposite = 1 << 4, bmdVideoConnectionSVideo = 1 << 5, bmdVideoConnectionEthernet = 1 << 6, bmdVideoConnectionOpticalEthernet = 1 << 7 }; /* Enum BMDAudioConnection - Audio connection types */ typedef uint32_t BMDAudioConnection; enum _BMDAudioConnection { bmdAudioConnectionEmbedded = 1 << 0, bmdAudioConnectionAESEBU = 1 << 1, bmdAudioConnectionAnalog = 1 << 2, bmdAudioConnectionAnalogXLR = 1 << 3, bmdAudioConnectionAnalogRCA = 1 << 4, bmdAudioConnectionMicrophone = 1 << 5, bmdAudioConnectionHeadphones = 1 << 6 }; /* Enum BMDDeckControlConnection - Deck control connections */ typedef uint32_t BMDDeckControlConnection; enum _BMDDeckControlConnection { bmdDeckControlConnectionRS422Remote1 = 1 << 0, bmdDeckControlConnectionRS422Remote2 = 1 << 1 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkTimecode; /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ class BMD_PUBLIC IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; virtual HRESULT GetComponents (/* out */ uint8_t* hours, /* out */ uint8_t* minutes, /* out */ uint8_t* seconds, /* out */ uint8_t* frames) = 0; virtual HRESULT GetString (/* out */ CFStringRef* timecode) = 0; virtual BMDTimecodeFlags GetFlags (void) = 0; virtual HRESULT GetTimecodeUserBits (/* out */ BMDTimecodeUserBits* userBits) = 0; protected: virtual ~IDeckLinkTimecode () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPITYPES_H) */ mlt-7.38.0/src/modules/decklink/linux/000775 000000 000000 00000000000 15172202314 017544 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/decklink/linux/DeckLinkAPI.h000664 000000 000000 00000247464 15172202314 021754 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation (the ** "Software") to use, reproduce, display, distribute, sub-license, execute, ** and transmit the Software, and to prepare derivative works of the Software, ** and to permit third-parties to whom the Software is furnished to do so, in ** accordance with: ** ** (1) if the Software is obtained from Blackmagic Design, the End User License ** Agreement for the Software Development Kit ("EULA") available at ** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or ** ** (2) if the Software is obtained from any third party, such licensing terms ** as notified by that third party, ** ** and all subject to the following: ** ** (3) the copyright notices in the Software and this entire statement, ** including the above license grant, this restriction and the following ** disclaimer, must be included in all copies of the Software, in whole or in ** part, and all derivative works of the Software, unless such copies or ** derivative works are solely in the form of machine-executable object code ** generated by a source language processor. ** ** (4) 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** ** A copy of the Software is available free of charge at ** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. ** ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPI_H #define BMD_DECKLINKAPI_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif /* DeckLink API */ #include #include "LinuxCOM.h" #include "DeckLinkAPITypes.h" #include "DeckLinkAPIModes.h" #include "DeckLinkAPIDiscovery.h" #include "DeckLinkAPIConfiguration.h" #include "DeckLinkAPIDeckControl.h" #define BLACKMAGIC_DECKLINK_API_MAGIC 1 // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkVideoOutputCallback = /* 5BE6DF26-02CE-433E-99D9-9A87C3AC171F */ { 0x5B,0xE6,0xDF,0x26,0x02,0xCE,0x43,0x3E,0x99,0xD9,0x9A,0x87,0xC3,0xAC,0x17,0x1F }; BMD_CONST REFIID IID_IDeckLinkInputCallback = /* 3A94F075-C37D-4BA8-BCC0-1D778C8F881B */ { 0x3A,0x94,0xF0,0x75,0xC3,0x7D,0x4B,0xA8,0xBC,0xC0,0x1D,0x77,0x8C,0x8F,0x88,0x1B }; BMD_CONST REFIID IID_IDeckLinkEncoderInputCallback = /* ACF13E61-F4A0-4974-A6A7-59AFF6268B31 */ { 0xAC,0xF1,0x3E,0x61,0xF4,0xA0,0x49,0x74,0xA6,0xA7,0x59,0xAF,0xF6,0x26,0x8B,0x31 }; BMD_CONST REFIID IID_IDeckLinkVideoBufferAllocator = /* 3481A4DF-2B11-4E55-AC61-836B87985E9A */ { 0x34,0x81,0xA4,0xDF,0x2B,0x11,0x4E,0x55,0xAC,0x61,0x83,0x6B,0x87,0x98,0x5E,0x9A }; BMD_CONST REFIID IID_IDeckLinkVideoBufferAllocatorProvider = /* 08B80403-BFF2-49D0-B448-8C908B9E9FC9 */ { 0x08,0xB8,0x04,0x03,0xBF,0xF2,0x49,0xD0,0xB4,0x48,0x8C,0x90,0x8B,0x9E,0x9F,0xC9 }; BMD_CONST REFIID IID_IDeckLinkAudioOutputCallback = /* 403C681B-7F46-4A12-B993-2BB127084EE6 */ { 0x40,0x3C,0x68,0x1B,0x7F,0x46,0x4A,0x12,0xB9,0x93,0x2B,0xB1,0x27,0x08,0x4E,0xE6 }; BMD_CONST REFIID IID_IDeckLinkIterator = /* 50FB36CD-3063-4B73-BDBB-958087F2D8BA */ { 0x50,0xFB,0x36,0xCD,0x30,0x63,0x4B,0x73,0xBD,0xBB,0x95,0x80,0x87,0xF2,0xD8,0xBA }; BMD_CONST REFIID IID_IDeckLinkAPIInformation = /* 7BEA3C68-730D-4322-AF34-8A7152B532A4 */ { 0x7B,0xEA,0x3C,0x68,0x73,0x0D,0x43,0x22,0xAF,0x34,0x8A,0x71,0x52,0xB5,0x32,0xA4 }; BMD_CONST REFIID IID_IDeckLinkIPFlowAttributes = /* CDA938DA-6479-40C6-B2EC-A3579B3AEECD */ { 0xCD,0xA9,0x38,0xDA,0x64,0x79,0x40,0xC6,0xB2,0xEC,0xA3,0x57,0x9B,0x3A,0xEE,0xCD }; BMD_CONST REFIID IID_IDeckLinkIPFlowStatus = /* 31C41656-4992-4396-BBE9-5F8406AAB5AF */ { 0x31,0xC4,0x16,0x56,0x49,0x92,0x43,0x96,0xBB,0xE9,0x5F,0x84,0x06,0xAA,0xB5,0xAF }; BMD_CONST REFIID IID_IDeckLinkIPFlowSetting = /* 86DD9174-27D3-4032-B2AD-6067C3BB2424 */ { 0x86,0xDD,0x91,0x74,0x27,0xD3,0x40,0x32,0xB2,0xAD,0x60,0x67,0xC3,0xBB,0x24,0x24 }; BMD_CONST REFIID IID_IDeckLinkIPFlow = /* C5FC83C7-5B8E-42A7-9A40-7C065955D4E1 */ { 0xC5,0xFC,0x83,0xC7,0x5B,0x8E,0x42,0xA7,0x9A,0x40,0x7C,0x06,0x59,0x55,0xD4,0xE1 }; BMD_CONST REFIID IID_IDeckLinkIPFlowIterator = /* BD296AB2-A5C5-4153-888F-AAB1FDBD8A5C */ { 0xBD,0x29,0x6A,0xB2,0xA5,0xC5,0x41,0x53,0x88,0x8F,0xAA,0xB1,0xFD,0xBD,0x8A,0x5C }; BMD_CONST REFIID IID_IDeckLinkOutput = /* 1A8077F1-9FE2-4533-8147-2294305E253F */ { 0x1A,0x80,0x77,0xF1,0x9F,0xE2,0x45,0x33,0x81,0x47,0x22,0x94,0x30,0x5E,0x25,0x3F }; BMD_CONST REFIID IID_IDeckLinkInput = /* 4095DB82-E294-4B8C-AAA8-3B9E80C49336 */ { 0x40,0x95,0xDB,0x82,0xE2,0x94,0x4B,0x8C,0xAA,0xA8,0x3B,0x9E,0x80,0xC4,0x93,0x36 }; BMD_CONST REFIID IID_IDeckLinkIPExtensions = /* 46CF7903-A9FD-4D0B-8FFC-0103722AB442 */ { 0x46,0xCF,0x79,0x03,0xA9,0xFD,0x4D,0x0B,0x8F,0xFC,0x01,0x03,0x72,0x2A,0xB4,0x42 }; BMD_CONST REFIID IID_IDeckLinkHDMIInputEDID = /* ABBBACBC-45BC-4665-9D92-ACE6E5A97902 */ { 0xAB,0xBB,0xAC,0xBC,0x45,0xBC,0x46,0x65,0x9D,0x92,0xAC,0xE6,0xE5,0xA9,0x79,0x02 }; BMD_CONST REFIID IID_IDeckLinkEncoderInput = /* 46C1332E-6FD9-472A-8591-FE59C22192E1 */ { 0x46,0xC1,0x33,0x2E,0x6F,0xD9,0x47,0x2A,0x85,0x91,0xFE,0x59,0xC2,0x21,0x92,0xE1 }; BMD_CONST REFIID IID_IDeckLinkVideoBuffer = /* CCB4B64A-5C86-4E02-B778-885D352709FE */ { 0xCC,0xB4,0xB6,0x4A,0x5C,0x86,0x4E,0x02,0xB7,0x78,0x88,0x5D,0x35,0x27,0x09,0xFE }; BMD_CONST REFIID IID_IDeckLinkVideoFrame = /* 6502091C-615F-4F51-BAF6-45C4256DD5B0 */ { 0x65,0x02,0x09,0x1C,0x61,0x5F,0x4F,0x51,0xBA,0xF6,0x45,0xC4,0x25,0x6D,0xD5,0xB0 }; BMD_CONST REFIID IID_IDeckLinkMutableVideoFrame = /* CF9EB134-0374-4C5B-95FA-1EC14819FF62 */ { 0xCF,0x9E,0xB1,0x34,0x03,0x74,0x4C,0x5B,0x95,0xFA,0x1E,0xC1,0x48,0x19,0xFF,0x62 }; BMD_CONST REFIID IID_IDeckLinkVideoFrame3DExtensions = /* D4DBE9C6-B4D2-49D3-ABF2-B4E86C7391B0 */ { 0xD4,0xDB,0xE9,0xC6,0xB4,0xD2,0x49,0xD3,0xAB,0xF2,0xB4,0xE8,0x6C,0x73,0x91,0xB0 }; BMD_CONST REFIID IID_IDeckLinkVideoFrameMetadataExtensions = /* E232A5B7-4DB4-44C9-9152-F47C12E5F051 */ { 0xE2,0x32,0xA5,0xB7,0x4D,0xB4,0x44,0xC9,0x91,0x52,0xF4,0x7C,0x12,0xE5,0xF0,0x51 }; BMD_CONST REFIID IID_IDeckLinkVideoFrameMutableMetadataExtensions = /* CC198FC6-8298-4419-942D-8357EC355E58 */ { 0xCC,0x19,0x8F,0xC6,0x82,0x98,0x44,0x19,0x94,0x2D,0x83,0x57,0xEC,0x35,0x5E,0x58 }; BMD_CONST REFIID IID_IDeckLinkVideoInputFrame = /* C9ADD3D2-BE52-488D-AB2D-7FDEF7AF0C95 */ { 0xC9,0xAD,0xD3,0xD2,0xBE,0x52,0x48,0x8D,0xAB,0x2D,0x7F,0xDE,0xF7,0xAF,0x0C,0x95 }; BMD_CONST REFIID IID_IDeckLinkAncillaryPacket = /* CC5BBF7E-029C-4D3B-9158-6000EF5E3670 */ { 0xCC,0x5B,0xBF,0x7E,0x02,0x9C,0x4D,0x3B,0x91,0x58,0x60,0x00,0xEF,0x5E,0x36,0x70 }; BMD_CONST REFIID IID_IDeckLinkAncillaryPacketIterator = /* 3FC8994B-88FB-4C17-968F-9AAB69D964A7 */ { 0x3F,0xC8,0x99,0x4B,0x88,0xFB,0x4C,0x17,0x96,0x8F,0x9A,0xAB,0x69,0xD9,0x64,0xA7 }; BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillaryPackets = /* 6C186C0F-459E-41D8-AEE2-4812D81AEE68 */ { 0x6C,0x18,0x6C,0x0F,0x45,0x9E,0x41,0xD8,0xAE,0xE2,0x48,0x12,0xD8,0x1A,0xEE,0x68 }; BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillary = /* 732E723C-D1A4-4E29-9E8E-4A88797A0004 */ { 0x73,0x2E,0x72,0x3C,0xD1,0xA4,0x4E,0x29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04 }; BMD_CONST REFIID IID_IDeckLinkEncoderPacket = /* B693F36C-316E-4AF1-B6C2-F389A4BCA620 */ { 0xB6,0x93,0xF3,0x6C,0x31,0x6E,0x4A,0xF1,0xB6,0xC2,0xF3,0x89,0xA4,0xBC,0xA6,0x20 }; BMD_CONST REFIID IID_IDeckLinkEncoderVideoPacket = /* 4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0 */ { 0x4E,0x7F,0xD9,0x44,0xE8,0xC7,0x4E,0xAC,0xB8,0xC0,0x7B,0x77,0xF8,0x0F,0x5A,0xE0 }; BMD_CONST REFIID IID_IDeckLinkEncoderAudioPacket = /* 49E8EDC8-693B-4E14-8EF6-12C658F5A07A */ { 0x49,0xE8,0xED,0xC8,0x69,0x3B,0x4E,0x14,0x8E,0xF6,0x12,0xC6,0x58,0xF5,0xA0,0x7A }; BMD_CONST REFIID IID_IDeckLinkH265NALPacket = /* 639C8E0B-68D5-4BDE-A6D4-95F3AEAFF2E7 */ { 0x63,0x9C,0x8E,0x0B,0x68,0xD5,0x4B,0xDE,0xA6,0xD4,0x95,0xF3,0xAE,0xAF,0xF2,0xE7 }; BMD_CONST REFIID IID_IDeckLinkAudioInputPacket = /* E43D5870-2894-11DE-8C30-0800200C9A66 */ { 0xE4,0x3D,0x58,0x70,0x28,0x94,0x11,0xDE,0x8C,0x30,0x08,0x00,0x20,0x0C,0x9A,0x66 }; BMD_CONST REFIID IID_IDeckLinkScreenPreviewCallback = /* D4FA2345-9FBA-4497-95C3-C0C3CED3CDA8 */ { 0xD4,0xFA,0x23,0x45,0x9F,0xBA,0x44,0x97,0x95,0xC3,0xC0,0xC3,0xCE,0xD3,0xCD,0xA8 }; BMD_CONST REFIID IID_IDeckLinkGLScreenPreviewHelper = /* CEB778E2-C202-4EC8-9085-0CD285CC5522 */ { 0xCE,0xB7,0x78,0xE2,0xC2,0x02,0x4E,0xC8,0x90,0x85,0x0C,0xD2,0x85,0xCC,0x55,0x22 }; BMD_CONST REFIID IID_IDeckLinkNotificationCallback = /* B002A1EC-070D-4288-8289-BD5D36E5FF0D */ { 0xB0,0x02,0xA1,0xEC,0x07,0x0D,0x42,0x88,0x82,0x89,0xBD,0x5D,0x36,0xE5,0xFF,0x0D }; BMD_CONST REFIID IID_IDeckLinkNotification = /* B85DF4C8-BDF5-47C1-8064-28162EBDD4EB */ { 0xB8,0x5D,0xF4,0xC8,0xBD,0xF5,0x47,0xC1,0x80,0x64,0x28,0x16,0x2E,0xBD,0xD4,0xEB }; BMD_CONST REFIID IID_IDeckLinkProfileAttributes = /* 17D4BF8E-4911-473A-80A0-731CF6FF345B */ { 0x17,0xD4,0xBF,0x8E,0x49,0x11,0x47,0x3A,0x80,0xA0,0x73,0x1C,0xF6,0xFF,0x34,0x5B }; BMD_CONST REFIID IID_IDeckLinkProfileIterator = /* 29E5A8C0-8BE4-46EB-93AC-31DAAB5B7BF2 */ { 0x29,0xE5,0xA8,0xC0,0x8B,0xE4,0x46,0xEB,0x93,0xAC,0x31,0xDA,0xAB,0x5B,0x7B,0xF2 }; BMD_CONST REFIID IID_IDeckLinkProfile = /* 16093466-674A-432B-9DA0-1AC2C5A8241C */ { 0x16,0x09,0x34,0x66,0x67,0x4A,0x43,0x2B,0x9D,0xA0,0x1A,0xC2,0xC5,0xA8,0x24,0x1C }; BMD_CONST REFIID IID_IDeckLinkProfileCallback = /* A4F9341E-97AA-4E04-8935-15F809898CEA */ { 0xA4,0xF9,0x34,0x1E,0x97,0xAA,0x4E,0x04,0x89,0x35,0x15,0xF8,0x09,0x89,0x8C,0xEA }; BMD_CONST REFIID IID_IDeckLinkProfileManager = /* 30D41429-3998-4B6D-84F8-78C94A797C6E */ { 0x30,0xD4,0x14,0x29,0x39,0x98,0x4B,0x6D,0x84,0xF8,0x78,0xC9,0x4A,0x79,0x7C,0x6E }; BMD_CONST REFIID IID_IDeckLinkStatus = /* 5F558200-4028-49BC-BEAC-DB3FA4A96E46 */ { 0x5F,0x55,0x82,0x00,0x40,0x28,0x49,0xBC,0xBE,0xAC,0xDB,0x3F,0xA4,0xA9,0x6E,0x46 }; BMD_CONST REFIID IID_IDeckLinkKeyer = /* 89AFCAF5-65F8-421E-98F7-96FE5F5BFBA3 */ { 0x89,0xAF,0xCA,0xF5,0x65,0xF8,0x42,0x1E,0x98,0xF7,0x96,0xFE,0x5F,0x5B,0xFB,0xA3 }; BMD_CONST REFIID IID_IDeckLinkVideoConversion = /* A48755D9-8BD5-4727-A1E9-069FDEDBA6E9 */ { 0xA4,0x87,0x55,0xD9,0x8B,0xD5,0x47,0x27,0xA1,0xE9,0x06,0x9F,0xDE,0xDB,0xA6,0xE9 }; BMD_CONST REFIID IID_IDeckLinkDeviceNotificationCallback = /* 4997053B-0ADF-4CC8-AC70-7A50C4BE728F */ { 0x49,0x97,0x05,0x3B,0x0A,0xDF,0x4C,0xC8,0xAC,0x70,0x7A,0x50,0xC4,0xBE,0x72,0x8F }; BMD_CONST REFIID IID_IDeckLinkDiscovery = /* CDBF631C-BC76-45FA-B44D-C55059BC6101 */ { 0xCD,0xBF,0x63,0x1C,0xBC,0x76,0x45,0xFA,0xB4,0x4D,0xC5,0x50,0x59,0xBC,0x61,0x01 }; /* Enum BMDBufferAccessFlags - Flags to describe access requirements to a video frame buffer */ typedef uint32_t BMDBufferAccessFlags; enum _BMDBufferAccessFlags { bmdBufferAccessReadAndWrite = 1 << 0 | 1 << 1, bmdBufferAccessRead = 1 << 0, bmdBufferAccessWrite = 1 << 1 }; /* Enum BMDVideoOutputFlags - Flags to control the output of ancillary data along with video. */ typedef uint32_t BMDVideoOutputFlags; enum _BMDVideoOutputFlags { bmdVideoOutputFlagDefault = 0, bmdVideoOutputVANC = 1 << 0, bmdVideoOutputVITC = 1 << 1, bmdVideoOutputRP188 = 1 << 2, bmdVideoOutputDualStream3D = 1 << 4, bmdVideoOutputSynchronizeToPlaybackGroup = 1 << 6, bmdVideoOutputDolbyVision = 1 << 7 }; /* Enum BMDSupportedVideoModeFlags - Flags to describe supported video modes */ typedef uint32_t BMDSupportedVideoModeFlags; enum _BMDSupportedVideoModeFlags { bmdSupportedVideoModeDefault = 0, bmdSupportedVideoModeKeying = 1 << 0, bmdSupportedVideoModeDualStream3D = 1 << 1, bmdSupportedVideoModeSDISingleLink = 1 << 2, bmdSupportedVideoModeSDIDualLink = 1 << 3, bmdSupportedVideoModeSDIQuadLink = 1 << 4, bmdSupportedVideoModeInAnyProfile = 1 << 5, bmdSupportedVideoModePsF = 1 << 6, bmdSupportedVideoModeDolbyVision = 1 << 7 }; /* Enum BMDPacketType - Type of packet */ typedef uint32_t BMDPacketType; enum _BMDPacketType { bmdPacketTypeStreamInterruptedMarker = /* 'sint' */ 0x73696E74, // A packet of this type marks the time when a video stream was interrupted, for example by a disconnected cable bmdPacketTypeStreamData = /* 'sdat' */ 0x73646174 // Regular stream data }; /* Enum BMDFrameFlags - Frame flags */ typedef uint32_t BMDFrameFlags; enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, bmdFrameFlagMonitorOutOnly = 1 << 3, bmdFrameContainsHDRMetadata = 1 << 1, bmdFrameContainsDolbyVisionMetadata = 1 << 4, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ bmdFrameCapturedAsPsF = 1 << 30, bmdFrameHasNoInputSource = 1 << 31 }; /* Enum BMDVideoInputFlags - Flags applicable to video input */ typedef uint32_t BMDVideoInputFlags; enum _BMDVideoInputFlags { bmdVideoInputFlagDefault = 0, bmdVideoInputEnableFormatDetection = 1 << 0, bmdVideoInputDualStream3D = 1 << 1, bmdVideoInputSynchronizeToCaptureGroup = 1 << 2 }; /* Enum BMDVideoInputFormatChangedEvents - Bitmask passed to the VideoInputFormatChanged notification to identify the properties of the input signal that have changed */ typedef uint32_t BMDVideoInputFormatChangedEvents; enum _BMDVideoInputFormatChangedEvents { bmdVideoInputDisplayModeChanged = 1 << 0, bmdVideoInputFieldDominanceChanged = 1 << 1, bmdVideoInputColorspaceChanged = 1 << 2 }; /* Enum BMDDetectedVideoInputFormatFlags - Flags passed to the VideoInputFormatChanged notification to describe the detected video input signal */ typedef uint32_t BMDDetectedVideoInputFormatFlags; enum _BMDDetectedVideoInputFormatFlags { bmdDetectedVideoInputYCbCr422 = 1 << 0, bmdDetectedVideoInputRGB444 = 1 << 1, bmdDetectedVideoInputDualStream3D = 1 << 2, bmdDetectedVideoInput12BitDepth = 1 << 3, bmdDetectedVideoInput10BitDepth = 1 << 4, bmdDetectedVideoInput8BitDepth = 1 << 5 }; /* Enum BMDDeckLinkCapturePassthroughMode - Enumerates whether the video output is electrically connected to the video input or if the clean switching mode is enabled */ typedef uint32_t BMDDeckLinkCapturePassthroughMode; enum _BMDDeckLinkCapturePassthroughMode { bmdDeckLinkCapturePassthroughModeDisabled = /* 'pdis' */ 0x70646973, bmdDeckLinkCapturePassthroughModeDirect = /* 'pdir' */ 0x70646972, bmdDeckLinkCapturePassthroughModeCleanSwitch = /* 'pcln' */ 0x70636C6E }; /* Enum BMDOutputFrameCompletionResult - Frame Completion Callback */ typedef uint32_t BMDOutputFrameCompletionResult; enum _BMDOutputFrameCompletionResult { bmdOutputFrameCompleted, bmdOutputFrameDisplayedLate, bmdOutputFrameDropped, bmdOutputFrameFlushed }; /* Enum BMDReferenceStatus - GenLock input status */ typedef uint32_t BMDReferenceStatus; enum _BMDReferenceStatus { bmdReferenceUnlocked = 0, bmdReferenceNotSupportedByHardware = 1 << 0, bmdReferenceLocked = 1 << 1 }; /* Enum BMDAudioFormat - Audio Format */ typedef uint32_t BMDAudioFormat; enum _BMDAudioFormat { bmdAudioFormatPCM = /* 'lpcm' */ 0x6C70636D // Linear signed PCM samples }; /* Enum BMDAudioSampleRate - Audio sample rates supported for output/input */ typedef uint32_t BMDAudioSampleRate; enum _BMDAudioSampleRate { bmdAudioSampleRate48kHz = 48000 }; /* Enum BMDAudioSampleType - Audio sample sizes supported for output/input */ typedef uint32_t BMDAudioSampleType; enum _BMDAudioSampleType { bmdAudioSampleType16bitInteger = 16, bmdAudioSampleType32bitInteger = 32 }; /* Enum BMDAudioOutputStreamType - Audio output stream type */ typedef uint32_t BMDAudioOutputStreamType; enum _BMDAudioOutputStreamType { bmdAudioOutputStreamContinuous, bmdAudioOutputStreamContinuousDontResample, bmdAudioOutputStreamTimestamped }; /* Enum BMDAncillaryPacketFormat - Ancillary packet format */ typedef uint32_t BMDAncillaryPacketFormat; enum _BMDAncillaryPacketFormat { bmdAncillaryPacketFormatUInt8 = /* 'ui08' */ 0x75693038, bmdAncillaryPacketFormatUInt16 = /* 'ui16' */ 0x75693136, bmdAncillaryPacketFormatYCbCr10 = /* 'v210' */ 0x76323130 }; /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ typedef uint32_t BMDTimecodeFormat; enum _BMDTimecodeFormat { bmdTimecodeRP188VITC1 = /* 'rpv1' */ 0x72707631, // RP188 timecode where DBB1 equals VITC1 (line 9) bmdTimecodeRP188VITC2 = /* 'rp12' */ 0x72703132, // RP188 timecode where DBB1 equals VITC2 (line 9 for progressive or line 571 for interlaced/PsF) bmdTimecodeRP188LTC = /* 'rplt' */ 0x72706C74, // RP188 timecode where DBB1 equals LTC (line 10) bmdTimecodeRP188HighFrameRate = /* 'rphr' */ 0x72706872, // RP188 timecode where DBB1 is an HFRTC (SMPTE ST 12-3), the only timecode allowing the frame value to go above 30 bmdTimecodeRP188Any = /* 'rp18' */ 0x72703138, // Convenience for capture, returning the first valid timecode in {HFRTC (if supported), VITC1, VITC2, LTC } bmdTimecodeVITC = /* 'vitc' */ 0x76697463, bmdTimecodeVITCField2 = /* 'vit2' */ 0x76697432, bmdTimecodeSerial = /* 'seri' */ 0x73657269 }; /* Enum BMDAnalogVideoFlags - Analog video display flags */ typedef uint32_t BMDAnalogVideoFlags; enum _BMDAnalogVideoFlags { bmdAnalogVideoFlagCompositeSetup75 = 1 << 0, bmdAnalogVideoFlagComponentBetacamLevels = 1 << 1 }; /* Enum BMDAudioOutputAnalogAESSwitch - Audio output Analog/AESEBU switch */ typedef uint32_t BMDAudioOutputAnalogAESSwitch; enum _BMDAudioOutputAnalogAESSwitch { bmdAudioOutputSwitchAESEBU = /* 'aes ' */ 0x61657320, bmdAudioOutputSwitchAnalog = /* 'anlg' */ 0x616E6C67 }; /* Enum BMDVideoOutputConversionMode - Video/audio conversion mode */ typedef uint32_t BMDVideoOutputConversionMode; enum _BMDVideoOutputConversionMode { bmdNoVideoOutputConversion = /* 'none' */ 0x6E6F6E65, bmdVideoOutputLetterboxDownconversion = /* 'ltbx' */ 0x6C746278, bmdVideoOutputAnamorphicDownconversion = /* 'amph' */ 0x616D7068, bmdVideoOutputHD720toHD1080Conversion = /* '720c' */ 0x37323063, bmdVideoOutputHardwareLetterboxDownconversion = /* 'HWlb' */ 0x48576C62, bmdVideoOutputHardwareAnamorphicDownconversion = /* 'HWam' */ 0x4857616D, bmdVideoOutputHardwareCenterCutDownconversion = /* 'HWcc' */ 0x48576363, bmdVideoOutputHardware720p1080pCrossconversion = /* 'xcap' */ 0x78636170, bmdVideoOutputHardwareAnamorphic720pUpconversion = /* 'ua7p' */ 0x75613770, bmdVideoOutputHardwareAnamorphic1080iUpconversion = /* 'ua1i' */ 0x75613169, bmdVideoOutputHardwareAnamorphic149To720pUpconversion = /* 'u47p' */ 0x75343770, bmdVideoOutputHardwareAnamorphic149To1080iUpconversion = /* 'u41i' */ 0x75343169, bmdVideoOutputHardwarePillarbox720pUpconversion = /* 'up7p' */ 0x75703770, bmdVideoOutputHardwarePillarbox1080iUpconversion = /* 'up1i' */ 0x75703169 }; /* Enum BMDVideoInputConversionMode - Video input conversion mode */ typedef uint32_t BMDVideoInputConversionMode; enum _BMDVideoInputConversionMode { bmdNoVideoInputConversion = /* 'none' */ 0x6E6F6E65, bmdVideoInputLetterboxDownconversionFromHD1080 = /* '10lb' */ 0x31306C62, bmdVideoInputAnamorphicDownconversionFromHD1080 = /* '10am' */ 0x3130616D, bmdVideoInputLetterboxDownconversionFromHD720 = /* '72lb' */ 0x37326C62, bmdVideoInputAnamorphicDownconversionFromHD720 = /* '72am' */ 0x3732616D, bmdVideoInputLetterboxUpconversion = /* 'lbup' */ 0x6C627570, bmdVideoInputAnamorphicUpconversion = /* 'amup' */ 0x616D7570 }; /* Enum BMDVideo3DPackingFormat - Video 3D packing format */ typedef uint32_t BMDVideo3DPackingFormat; enum _BMDVideo3DPackingFormat { bmdVideo3DPackingSidebySideHalf = /* 'sbsh' */ 0x73627368, bmdVideo3DPackingLinebyLine = /* 'lbyl' */ 0x6C62796C, bmdVideo3DPackingTopAndBottom = /* 'tabo' */ 0x7461626F, bmdVideo3DPackingFramePacking = /* 'frpk' */ 0x6672706B, bmdVideo3DPackingLeftOnly = /* 'left' */ 0x6C656674, bmdVideo3DPackingRightOnly = /* 'righ' */ 0x72696768 }; /* Enum BMDIdleVideoOutputOperation - Video output operation when not playing video */ typedef uint32_t BMDIdleVideoOutputOperation; enum _BMDIdleVideoOutputOperation { bmdIdleVideoOutputBlack = /* 'blac' */ 0x626C6163, bmdIdleVideoOutputLastFrame = /* 'lafa' */ 0x6C616661 }; /* Enum BMDVideoEncoderFrameCodingMode - Video frame coding mode */ typedef uint32_t BMDVideoEncoderFrameCodingMode; enum _BMDVideoEncoderFrameCodingMode { bmdVideoEncoderFrameCodingModeInter = /* 'inte' */ 0x696E7465, bmdVideoEncoderFrameCodingModeIntra = /* 'intr' */ 0x696E7472 }; /* Enum BMDDNxHRLevel - DNxHR Levels */ typedef uint32_t BMDDNxHRLevel; enum _BMDDNxHRLevel { bmdDNxHRLevelSQ = /* 'dnsq' */ 0x646E7371, bmdDNxHRLevelLB = /* 'dnlb' */ 0x646E6C62, bmdDNxHRLevelHQ = /* 'dnhq' */ 0x646E6871, bmdDNxHRLevelHQX = /* 'dhqx' */ 0x64687178, bmdDNxHRLevel444 = /* 'd444' */ 0x64343434 }; /* Enum BMDLinkConfiguration - Video link configuration */ typedef uint32_t BMDLinkConfiguration; enum _BMDLinkConfiguration { bmdLinkConfigurationSingleLink = /* 'lcsl' */ 0x6C63736C, bmdLinkConfigurationDualLink = /* 'lcdl' */ 0x6C63646C, bmdLinkConfigurationQuadLink = /* 'lcql' */ 0x6C63716C }; /* Enum BMDDeviceInterface - Device interface type */ typedef uint32_t BMDDeviceInterface; enum _BMDDeviceInterface { bmdDeviceInterfacePCI = /* 'pci ' */ 0x70636920, bmdDeviceInterfaceUSB = /* 'usb ' */ 0x75736220, bmdDeviceInterfaceThunderbolt = /* 'thun' */ 0x7468756E }; /* Enum BMDColorspace - Colorspace */ typedef uint32_t BMDColorspace; enum _BMDColorspace { bmdColorspaceRec601 = /* 'r601' */ 0x72363031, bmdColorspaceRec709 = /* 'r709' */ 0x72373039, bmdColorspaceRec2020 = /* '2020' */ 0x32303230, bmdColorspaceDolbyVisionNative = /* 'DoVi' */ 0x446F5669, // For bmdDeckLinkConfigVideoOutputConversionColorspaceDestination with 12-bit RGB bmdColorspaceP3D65 = /* 'P3D6' */ 0x50334436, // For bmdDeckLinkConfigVideoOutputConversionColorspaceSource only bmdColorspaceUnknown = /* 'Ncol' */ 0x4E636F6C // For disabling bmdDeckLinkConfigVideoOutputConversionColorspaceDestination }; /* Enum BMDDynamicRange - SDR or HDR */ typedef uint32_t BMDDynamicRange; enum _BMDDynamicRange { bmdDynamicRangeSDR = 0, // Standard Dynamic Range in accordance with SMPTE ST 2036-1 bmdDynamicRangeHDRStaticPQ = 1 << 29, // High Dynamic Range PQ in accordance with SMPTE ST 2084 bmdDynamicRangeHDRStaticHLG = 1 << 30 // High Dynamic Range HLG in accordance with ITU-R BT.2100-0 }; /* Enum BMDMezzanineType - */ typedef uint32_t BMDMezzanineType; enum _BMDMezzanineType { bmdMezzanineTypeNone = 0, // No mezzanine board bmdMezzanineTypeHDMI14OpticalSDI = /* 'mza1' */ 0x6D7A6131, // Mezzanine board with HDMI 1.4 and Optical SDI bmdMezzanineTypeQuadSDI = /* 'mz4s' */ 0x6D7A3473, // Mezzanine board with four SDI connectors bmdMezzanineTypeHDMI20OpticalSDI = /* 'mza2' */ 0x6D7A6132, // Mezzanine board with HDMI 2.0 and Optical SDI bmdMezzanineTypeHDMI21RS422 = /* 'mzhr' */ 0x6D7A6872 // Mezzanine boards with HDMI 2.1 and RS422 }; /* Enum BMDDeckLinkHDMIInputEDIDID - DeckLink HDMI Input EDID ID */ typedef uint32_t BMDDeckLinkHDMIInputEDIDID; enum _BMDDeckLinkHDMIInputEDIDID { /* Integers */ bmdDeckLinkHDMIInputEDIDDynamicRange = /* 'HIDy' */ 0x48494479 // Parameter is of type BMDDynamicRange. Default is (bmdDynamicRangeSDR|bmdDynamicRangeHDRStaticPQ) }; /* Enum BMDDeckLinkFrameMetadataID - DeckLink Frame Metadata ID */ typedef uint32_t BMDDeckLinkFrameMetadataID; enum _BMDDeckLinkFrameMetadataID { /* Integers */ bmdDeckLinkFrameMetadataColorspace = /* 'cspc' */ 0x63737063, // Colorspace of video frame (see BMDColorspace) bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = /* 'eotf' */ 0x656F7466, // EOTF in range 0-7 as per CEA 861.3 /* Dolby Vision only - Bytes */ bmdDeckLinkFrameMetadataDolbyVision = /* 'dovi' */ 0x646F7669, // Dolby Vision Metadata /* CEA/SMPTE only - HDR Metadata Floats */ bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = /* 'hdrx' */ 0x68647278, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = /* 'hdry' */ 0x68647279, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = /* 'hdgx' */ 0x68646778, // Green display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY = /* 'hdgy' */ 0x68646779, // Green display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX = /* 'hdbx' */ 0x68646278, // Blue display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY = /* 'hdby' */ 0x68646279, // Blue display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRWhitePointX = /* 'hdwx' */ 0x68647778, // White point in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRWhitePointY = /* 'hdwy' */ 0x68647779, // White point in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = /* 'hdml' */ 0x68646D6C, // Max display mastering luminance in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = /* 'hmil' */ 0x686D696C, // Min display mastering luminance in range 0.0001 cd/m2 - 6.5535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = /* 'mcll' */ 0x6D636C6C, // Maximum Content Light Level in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 }; /* Enum BMDEthernetLinkState - The state of the Ethernet link */ typedef uint32_t BMDEthernetLinkState; enum _BMDEthernetLinkState { bmdEthernetLinkStateDisconnected = /* 'elds' */ 0x656C6473, bmdEthernetLinkStateConnectedUnbound = /* 'elcu' */ 0x656C6375, bmdEthernetLinkStateConnectedBound = /* 'elcb' */ 0x656C6362 }; /* Enum BMDProfileID - Identifies a profile */ typedef uint32_t BMDProfileID; enum _BMDProfileID { bmdProfileOneSubDeviceFullDuplex = /* '1dfd' */ 0x31646664, bmdProfileOneSubDeviceHalfDuplex = /* '1dhd' */ 0x31646864, bmdProfileTwoSubDevicesFullDuplex = /* '2dfd' */ 0x32646664, bmdProfileTwoSubDevicesHalfDuplex = /* '2dhd' */ 0x32646864, bmdProfileFourSubDevicesHalfDuplex = /* '4dhd' */ 0x34646864 }; /* Enum BMDHDMITimecodePacking - Packing form of timecode on HDMI */ typedef uint32_t BMDHDMITimecodePacking; enum _BMDHDMITimecodePacking { bmdHDMITimecodePackingIEEEOUI000085 = 0x00008500, bmdHDMITimecodePackingIEEEOUI080046 = 0x08004601, bmdHDMITimecodePackingIEEEOUI5CF9F0 = 0x5CF9F003 }; /* Enum BMDInternalKeyingAncillaryDataSource - Source for VANC and timecode data when performing internal keying */ typedef uint32_t BMDInternalKeyingAncillaryDataSource; enum _BMDInternalKeyingAncillaryDataSource { bmdInternalKeyingUsesAncillaryDataFromInputSignal = /* 'ikai' */ 0x696B6169, bmdInternalKeyingUsesAncillaryDataFromKeyFrame = /* 'ikak' */ 0x696B616B }; /* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */ typedef uint32_t BMDDeckLinkAttributeID; enum _BMDDeckLinkAttributeID { /* Flags */ BMDDeckLinkSupportsInternalKeying = /* 'keyi' */ 0x6B657969, BMDDeckLinkSupportsExternalKeying = /* 'keye' */ 0x6B657965, BMDDeckLinkSupportsInputFormatDetection = /* 'infd' */ 0x696E6664, BMDDeckLinkHasReferenceInput = /* 'hrin' */ 0x6872696E, BMDDeckLinkHasSerialPort = /* 'hspt' */ 0x68737074, BMDDeckLinkHasAnalogVideoOutputGain = /* 'avog' */ 0x61766F67, BMDDeckLinkCanOnlyAdjustOverallVideoOutputGain = /* 'ovog' */ 0x6F766F67, BMDDeckLinkHasVideoInputAntiAliasingFilter = /* 'aafl' */ 0x6161666C, BMDDeckLinkHasBypass = /* 'byps' */ 0x62797073, BMDDeckLinkSupportsClockTimingAdjustment = /* 'ctad' */ 0x63746164, BMDDeckLinkSupportsFullFrameReferenceInputTimingOffset = /* 'frin' */ 0x6672696E, BMDDeckLinkSupportsSMPTELevelAOutput = /* 'lvla' */ 0x6C766C61, BMDDeckLinkSupportsAutoSwitchingPPsFOnInput = /* 'apsf' */ 0x61707366, BMDDeckLinkSupportsDualLinkSDI = /* 'sdls' */ 0x73646C73, BMDDeckLinkSupportsQuadLinkSDI = /* 'sqls' */ 0x73716C73, BMDDeckLinkSupportsIdleOutput = /* 'idou' */ 0x69646F75, BMDDeckLinkVANCRequires10BitYUVVideoFrames = /* 'vioY' */ 0x76696F59, // Legacy product requires v210 active picture for IDeckLinkVideoFrameAncillaryPackets or 10-bit VANC BMDDeckLinkHasLTCTimecodeInput = /* 'hltc' */ 0x686C7463, BMDDeckLinkSupportsHDRMetadata = /* 'hdrm' */ 0x6864726D, BMDDeckLinkSupportsColorspaceMetadata = /* 'cmet' */ 0x636D6574, BMDDeckLinkSupportsHDMITimecode = /* 'htim' */ 0x6874696D, BMDDeckLinkSupportsHighFrameRateTimecode = /* 'HFRT' */ 0x48465254, BMDDeckLinkSupportsSynchronizeToCaptureGroup = /* 'stcg' */ 0x73746367, BMDDeckLinkSupportsSynchronizeToPlaybackGroup = /* 'stpg' */ 0x73747067, BMDDeckLinkHasMonitorOut = /* 'fmoo' */ 0x666D6F6F, /* Integers */ BMDDeckLinkMaximumAudioChannels = /* 'mach' */ 0x6D616368, BMDDeckLinkMaximumHDMIAudioChannels = /* 'mhch' */ 0x6D686368, BMDDeckLinkMaximumAnalogAudioInputChannels = /* 'iach' */ 0x69616368, BMDDeckLinkMaximumAnalogAudioOutputChannels = /* 'aach' */ 0x61616368, BMDDeckLinkNumberOfSubDevices = /* 'nsbd' */ 0x6E736264, BMDDeckLinkSubDeviceIndex = /* 'subi' */ 0x73756269, BMDDeckLinkPersistentID = /* 'peid' */ 0x70656964, BMDDeckLinkDeviceGroupID = /* 'dgid' */ 0x64676964, BMDDeckLinkTopologicalID = /* 'toid' */ 0x746F6964, BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, // Returns a BMDVideoConnection bit field BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, // Returns a BMDVideoConnection bit field BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, // Returns a BMDAudioConnection bit field BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, // Returns a BMDAudioConnection bit field BMDDeckLinkVideoIOSupport = /* 'vios' */ 0x76696F73, // Returns a BMDVideoIOSupport bit field BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, // Returns a BMDDeckControlConnection bit field BMDDeckLinkDeviceInterface = /* 'dbus' */ 0x64627573, // Returns a BMDDeviceInterface BMDDeckLinkAudioInputRCAChannelCount = /* 'airc' */ 0x61697263, BMDDeckLinkAudioInputXLRChannelCount = /* 'aixc' */ 0x61697863, BMDDeckLinkAudioOutputRCAChannelCount = /* 'aorc' */ 0x616F7263, BMDDeckLinkAudioOutputXLRChannelCount = /* 'aoxc' */ 0x616F7863, BMDDeckLinkProfileID = /* 'prid' */ 0x70726964, // Returns a BMDProfileID BMDDeckLinkDuplex = /* 'dupx' */ 0x64757078, BMDDeckLinkMinimumPrerollFrames = /* 'mprf' */ 0x6D707266, BMDDeckLinkSupportedDynamicRange = /* 'sudr' */ 0x73756472, BMDDeckLinkMezzanineType = /* 'mezt' */ 0x6D657A74, /* Floats */ BMDDeckLinkVideoInputGainMinimum = /* 'vigm' */ 0x7669676D, BMDDeckLinkVideoInputGainMaximum = /* 'vigx' */ 0x76696778, BMDDeckLinkVideoOutputGainMinimum = /* 'vogm' */ 0x766F676D, BMDDeckLinkVideoOutputGainMaximum = /* 'vogx' */ 0x766F6778, BMDDeckLinkMicrophoneInputGainMinimum = /* 'migm' */ 0x6D69676D, BMDDeckLinkMicrophoneInputGainMaximum = /* 'migx' */ 0x6D696778, /* Strings */ BMDDeckLinkSerialPortDeviceName = /* 'slpn' */ 0x736C706E, BMDDeckLinkVendorName = /* 'vndr' */ 0x766E6472, BMDDeckLinkDisplayName = /* 'dspn' */ 0x6473706E, BMDDeckLinkModelName = /* 'mdln' */ 0x6D646C6E, BMDDeckLinkDeviceHandle = /* 'devh' */ 0x64657668, BMDDeckLinkEthernetMACAddress = /* 'eMAC' */ 0x654D4143 }; /* Enum BMDDeckLinkAPIInformationID - DeckLinkAPI information ID */ typedef uint32_t BMDDeckLinkAPIInformationID; enum _BMDDeckLinkAPIInformationID { /* Integer or String */ BMDDeckLinkAPIVersion = /* 'vers' */ 0x76657273 }; /* Enum BMDDeckLinkStatusID - DeckLink Status ID */ typedef uint32_t BMDDeckLinkStatusID; enum _BMDDeckLinkStatusID { /* Integers */ bmdDeckLinkStatusDetectedVideoInputMode = /* 'dvim' */ 0x6476696D, bmdDeckLinkStatusDetectedVideoInputFormatFlags = /* 'dvff' */ 0x64766666, bmdDeckLinkStatusDetectedVideoInputFieldDominance = /* 'dvfd' */ 0x64766664, bmdDeckLinkStatusDetectedVideoInputColorspace = /* 'dscl' */ 0x6473636C, bmdDeckLinkStatusDetectedVideoInputDynamicRange = /* 'dsdr' */ 0x64736472, bmdDeckLinkStatusDetectedSDILinkConfiguration = /* 'dslc' */ 0x64736C63, bmdDeckLinkStatusCurrentVideoInputMode = /* 'cvim' */ 0x6376696D, bmdDeckLinkStatusCurrentVideoInputPixelFormat = /* 'cvip' */ 0x63766970, bmdDeckLinkStatusCurrentVideoInputFlags = /* 'cvif' */ 0x63766966, bmdDeckLinkStatusCurrentVideoOutputMode = /* 'cvom' */ 0x63766F6D, bmdDeckLinkStatusCurrentVideoOutputFlags = /* 'cvof' */ 0x63766F66, bmdDeckLinkStatusEthernetLink = /* 'sels' */ 0x73656C73, bmdDeckLinkStatusEthernetLinkMbps = /* 'sesp' */ 0x73657370, bmdDeckLinkStatusPCIExpressLinkWidth = /* 'pwid' */ 0x70776964, bmdDeckLinkStatusPCIExpressLinkSpeed = /* 'plnk' */ 0x706C6E6B, bmdDeckLinkStatusLastVideoOutputPixelFormat = /* 'opix' */ 0x6F706978, bmdDeckLinkStatusReferenceSignalMode = /* 'refm' */ 0x7265666D, bmdDeckLinkStatusReferenceSignalFlags = /* 'reff' */ 0x72656666, bmdDeckLinkStatusBusy = /* 'busy' */ 0x62757379, bmdDeckLinkStatusInterchangeablePanelType = /* 'icpt' */ 0x69637074, bmdDeckLinkStatusDeviceTemperature = /* 'dtmp' */ 0x64746D70, bmdDeckLinkStatusHDMIOutputActualMode = /* 'hiam' */ 0x6869616D, bmdDeckLinkStatusHDMIOutputActualFormatFlags = /* 'hiaf' */ 0x68696166, bmdDeckLinkStatusHDMIOutputFRLRate = /* 'hiof' */ 0x68696F66, bmdDeckLinkStatusHDMIInputFRLRate = /* 'hiif' */ 0x68696966, bmdDeckLinkStatusHDMIOutputTMDSLineRate = /* 'hilr' */ 0x68696C72, /* Floats */ bmdDeckLinkStatusSinkSupportsDolbyVision = /* 'dvvr' */ 0x64767672, /* Flags */ bmdDeckLinkStatusVideoInputSignalLocked = /* 'visl' */ 0x7669736C, bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C, /* Strings */ bmdDeckLinkStatusEthernetLocalIPAddress = /* 'seip' */ 0x73656970, bmdDeckLinkStatusEthernetSubnetMask = /* 'sesm' */ 0x7365736D, bmdDeckLinkStatusEthernetGatewayIPAddress = /* 'segw' */ 0x73656777, bmdDeckLinkStatusEthernetPrimaryDNS = /* 'sepd' */ 0x73657064, bmdDeckLinkStatusEthernetSecondaryDNS = /* 'sesd' */ 0x73657364, bmdDeckLinkStatusEthernetPTPGrandmasterIdentity = /* 'spid' */ 0x73706964, bmdDeckLinkStatusEthernetVideoOutputAddress = /* 'soav' */ 0x736F6176, bmdDeckLinkStatusEthernetAudioOutputAddress = /* 'soaa' */ 0x736F6161, bmdDeckLinkStatusEthernetAncillaryOutputAddress = /* 'soaA' */ 0x736F6141, bmdDeckLinkStatusEthernetAudioInputChannelOrder = /* 'saco' */ 0x7361636F, /* Bytes */ bmdDeckLinkStatusReceivedEDID = /* 'edid' */ 0x65646964 }; /* Enum BMDDeckLinkVideoStatusFlags - */ typedef uint32_t BMDDeckLinkVideoStatusFlags; enum _BMDDeckLinkVideoStatusFlags { bmdDeckLinkVideoStatusPsF = 1 << 0, bmdDeckLinkVideoStatusDualStream3D = 1 << 1 }; /* Enum BMDDuplexMode - Duplex of the device */ typedef uint32_t BMDDuplexMode; enum _BMDDuplexMode { bmdDuplexFull = /* 'dxfu' */ 0x64786675, bmdDuplexHalf = /* 'dxha' */ 0x64786861, bmdDuplexSimplex = /* 'dxsp' */ 0x64787370, bmdDuplexInactive = /* 'dxin' */ 0x6478696E }; /* Enum BMDPanelType - The type of interchangeable panel */ typedef uint32_t BMDPanelType; enum _BMDPanelType { bmdPanelNotDetected = /* 'npnl' */ 0x6E706E6C, bmdPanelTeranexMiniSmartPanel = /* 'tmsm' */ 0x746D736D }; /* Enum BMDFormatFlags - Flags to describe the video signal */ typedef uint32_t BMDFormatFlags; enum _BMDFormatFlags { bmdFormatRGB444 = 1 << 0, bmdFormatYUV444 = 1 << 1, bmdFormatYUV422 = 1 << 2, bmdFormatYUV420 = 1 << 3, bmdFormat8BitDepth = 1 << 4, bmdFormat10BitDepth = 1 << 5, bmdFormat12BitDepth = 1 << 6 }; /* Enum BMDDeviceBusyState - Current device busy state */ typedef uint32_t BMDDeviceBusyState; enum _BMDDeviceBusyState { bmdDeviceCaptureBusy = 1 << 0, bmdDevicePlaybackBusy = 1 << 1, bmdDeviceSerialPortBusy = 1 << 2 }; /* Enum BMDVideoIOSupport - Device video input/output support */ typedef uint32_t BMDVideoIOSupport; enum _BMDVideoIOSupport { bmdDeviceSupportsCapture = 1 << 0, bmdDeviceSupportsPlayback = 1 << 1 }; /* Enum BMD3DPreviewFormat - Linked Frame preview format */ typedef uint32_t BMD3DPreviewFormat; enum _BMD3DPreviewFormat { bmd3DPreviewFormatDefault = /* 'defa' */ 0x64656661, bmd3DPreviewFormatLeftOnly = /* 'left' */ 0x6C656674, bmd3DPreviewFormatRightOnly = /* 'righ' */ 0x72696768, bmd3DPreviewFormatSideBySide = /* 'side' */ 0x73696465, bmd3DPreviewFormatTopBottom = /* 'topb' */ 0x746F7062 }; /* Enum BMDIPFlowDirection - BMDIPFlowDirection enumerates the direction of the IP flow. */ enum BMDIPFlowDirection { bmdDeckLinkIPFlowDirectionOutput = 0, bmdDeckLinkIPFlowDirectionInput = 1 }; /* Enum BMDIPFlowType - BMDIPFlowDirection enumerates the IP flow type. */ enum BMDIPFlowType { bmdDeckLinkIPFlowTypeVideo = 0, bmdDeckLinkIPFlowTypeAudio = 1, bmdDeckLinkIPFlowTypeAncillary = 2 }; /* Enum BMDDeckLinkIPFlowAttributeID - DeckLink IP Flow Attribute ID */ enum BMDDeckLinkIPFlowAttributeID { /* DeckLink IP Flow Attribute Integers */ bmdDeckLinkIPFlowID = /* '2fai' */ 0x32666169, bmdDeckLinkIPFlowDirection = /* '2fad' */ 0x32666164, bmdDeckLinkIPFlowType = /* '2fat' */ 0x32666174 }; /* Enum BMDDeckLinkIPFlowStatusID - DeckLink IP Flow Attribute ID */ enum BMDDeckLinkIPFlowStatusID { /* DeckLink IP Flow Status Strings */ bmdDeckLinkIPFlowSDP = /* '2fas' */ 0x32666173 }; /* Enum BMDDeckLinkIPFlowSettingID - DeckLink IP Flow Setting ID */ enum BMDDeckLinkIPFlowSettingID { /* DeckLink IP Flow Setting Strings */ bmdDeckLinkIPFlowPeerSDP = /* '2fps' */ 0x32667073 // The peer's SDP. Must not be over 1000 bytes large. }; /* Enum BMDNotifications - Events that can be subscribed through IDeckLinkNotification */ typedef uint32_t BMDNotifications; enum _BMDNotifications { bmdPreferencesChanged = /* 'pref' */ 0x70726566, bmdStatusChanged = /* 'stat' */ 0x73746174, bmdIPFlowStatusChanged = /* 'bfsc' */ 0x62667363, bmdIPFlowSettingChanged = /* 'bfcc' */ 0x62666363 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkVideoOutputCallback; class IDeckLinkInputCallback; class IDeckLinkEncoderInputCallback; class IDeckLinkVideoBufferAllocator; class IDeckLinkVideoBufferAllocatorProvider; class IDeckLinkAudioOutputCallback; class IDeckLinkIterator; class IDeckLinkAPIInformation; class IDeckLinkIPFlowAttributes; class IDeckLinkIPFlowStatus; class IDeckLinkIPFlowSetting; class IDeckLinkIPFlow; class IDeckLinkIPFlowIterator; class IDeckLinkOutput; class IDeckLinkInput; class IDeckLinkIPExtensions; class IDeckLinkHDMIInputEDID; class IDeckLinkEncoderInput; class IDeckLinkVideoBuffer; class IDeckLinkVideoFrame; class IDeckLinkMutableVideoFrame; class IDeckLinkVideoFrame3DExtensions; class IDeckLinkVideoFrameMetadataExtensions; class IDeckLinkVideoFrameMutableMetadataExtensions; class IDeckLinkVideoInputFrame; class IDeckLinkAncillaryPacket; class IDeckLinkAncillaryPacketIterator; class IDeckLinkVideoFrameAncillaryPackets; class IDeckLinkVideoFrameAncillary; class IDeckLinkEncoderPacket; class IDeckLinkEncoderVideoPacket; class IDeckLinkEncoderAudioPacket; class IDeckLinkH265NALPacket; class IDeckLinkAudioInputPacket; class IDeckLinkScreenPreviewCallback; class IDeckLinkGLScreenPreviewHelper; class IDeckLinkNotificationCallback; class IDeckLinkNotification; class IDeckLinkProfileAttributes; class IDeckLinkProfileIterator; class IDeckLinkProfile; class IDeckLinkProfileCallback; class IDeckLinkProfileManager; class IDeckLinkStatus; class IDeckLinkKeyer; class IDeckLinkVideoConversion; class IDeckLinkDeviceNotificationCallback; class IDeckLinkDiscovery; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ class BMD_PUBLIC IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame* completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; virtual HRESULT ScheduledPlaybackHasStopped (void) = 0; protected: virtual ~IDeckLinkVideoOutputCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkInputCallback - Frame arrival callback. */ class BMD_PUBLIC IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode* newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT VideoInputFrameArrived (/* in */ IDeckLinkVideoInputFrame* videoFrame, /* in */ IDeckLinkAudioInputPacket* audioPacket) = 0; protected: virtual ~IDeckLinkInputCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderInputCallback - Frame arrival callback. */ class BMD_PUBLIC IDeckLinkEncoderInputCallback : public IUnknown { public: virtual HRESULT VideoInputSignalChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode* newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT VideoPacketArrived (/* in */ IDeckLinkEncoderVideoPacket* videoPacket) = 0; virtual HRESULT AudioPacketArrived (/* in */ IDeckLinkEncoderAudioPacket* audioPacket) = 0; protected: virtual ~IDeckLinkEncoderInputCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoBufferAllocator - Buffer allocator for video. */ class BMD_PUBLIC IDeckLinkVideoBufferAllocator : public IUnknown { public: virtual HRESULT AllocateVideoBuffer (/* out */ IDeckLinkVideoBuffer** allocatedBuffer) = 0; protected: virtual ~IDeckLinkVideoBufferAllocator () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoBufferAllocatorProvider - Allows EnableVideoInputWithAllocatorProvider to obtain allocators */ class BMD_PUBLIC IDeckLinkVideoBufferAllocatorProvider : public IUnknown { public: virtual HRESULT GetVideoBufferAllocator (/* in */ uint32_t bufferSize, /* in */ uint32_t width, /* in */ uint32_t height, /* in */ uint32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoBufferAllocator** allocator) = 0; protected: virtual ~IDeckLinkVideoBufferAllocatorProvider () {} // call Release method to drop reference count }; /* Interface IDeckLinkAudioOutputCallback - Optional callback to allow audio samples to be pulled as required. */ class BMD_PUBLIC IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT RenderAudioSamples (/* in */ bool preroll) = 0; }; /* Interface IDeckLinkIterator - Enumerates installed DeckLink hardware */ class BMD_PUBLIC IDeckLinkIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink** deckLinkInstance) = 0; }; /* Interface IDeckLinkAPIInformation - DeckLinkAPI attribute interface */ class BMD_PUBLIC IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ bool* value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ const char** value) = 0; protected: virtual ~IDeckLinkAPIInformation () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlowAttributes - */ class BMD_PUBLIC IDeckLinkIPFlowAttributes : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkIPFlowAttributeID attrID, /* out */ int64_t* value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkIPFlowAttributeID attrID, /* out */ bool* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkIPFlowAttributeID attrID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkIPFlowAttributeID attrID, /* out */ const char** value) = 0; protected: virtual ~IDeckLinkIPFlowAttributes () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlowStatus - */ class BMD_PUBLIC IDeckLinkIPFlowStatus : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkIPFlowStatusID statusID, /* out */ int64_t* value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkIPFlowStatusID statusID, /* out */ bool* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkIPFlowStatusID statusID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkIPFlowStatusID statusID, /* out */ const char** value) = 0; protected: virtual ~IDeckLinkIPFlowStatus () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlowSetting - */ class BMD_PUBLIC IDeckLinkIPFlowSetting : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* out */ int64_t* value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* out */ bool* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* out */ const char** value) = 0; virtual HRESULT SetInt (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* in */ int64_t value) = 0; virtual HRESULT SetFlag (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* in */ bool value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* in */ double value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkIPFlowSettingID settingID, /* in */ const char* value) = 0; protected: virtual ~IDeckLinkIPFlowSetting () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlow - */ class BMD_PUBLIC IDeckLinkIPFlow : public IUnknown { public: virtual HRESULT Enable (void) = 0; // Enables an IP flow to start sending or receiving. virtual HRESULT Disable (void) = 0; // Disables an IP flow to stop sending or receiving. protected: virtual ~IDeckLinkIPFlow () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPFlowIterator - Enumerates DeckLink IP Flows */ class BMD_PUBLIC IDeckLinkIPFlowIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkIPFlow** deckLinkIPFlowInstance) = 0; protected: virtual ~IDeckLinkIPFlowIterator () {} // call Release method to drop reference count }; /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ class BMD_PUBLIC IDeckLinkOutput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoOutputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0; virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0; virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback* previewCallback) = 0; /* Video Output */ virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0; virtual HRESULT DisableVideoOutput (void) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame** outFrame) = 0; virtual HRESULT CreateVideoFrameWithBuffer (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* in */ IDeckLinkVideoBuffer* buffer, /* out */ IDeckLinkMutableVideoFrame** outFrame) = 0; virtual HRESULT RowBytesForPixelFormat (/* in */ BMDPixelFormat pixelFormat, /* in */ int32_t width, /* out */ int32_t* rowBytes) = 0; virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary** outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame* theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame* theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback* theCallback) = 0; virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t* bufferedFrameCount) = 0; /* Audio Output */ virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT DisableAudioOutput (void) = 0; virtual HRESULT WriteAudioSamplesSync (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t* sampleFramesWritten) = 0; virtual HRESULT BeginAudioPreroll (void) = 0; virtual HRESULT EndAudioPreroll (void) = 0; virtual HRESULT ScheduleAudioSamples (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t* sampleFramesWritten) = 0; virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t* bufferedSampleFrameCount) = 0; virtual HRESULT FlushBufferedAudioSamples (void) = 0; virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback* theCallback) = 0; /* Output Control */ virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0; virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue* actualStopTime, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool* active) = 0; virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* streamTime, /* out */ double* playbackSpeed) = 0; virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus* referenceStatus) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0; virtual HRESULT GetFrameCompletionReferenceTimestamp (/* in */ IDeckLinkVideoFrame* theFrame, /* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* frameCompletionTimestamp) = 0; protected: virtual ~IDeckLinkOutput () {} // call Release method to drop reference count }; /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ class BMD_PUBLIC IDeckLinkInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoInputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0; virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0; virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback* previewCallback) = 0; /* Video Input */ virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0; virtual HRESULT EnableVideoInputWithAllocatorProvider (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* in */ IDeckLinkVideoBufferAllocatorProvider* allocatorProvider) = 0; virtual HRESULT DisableVideoInput (void) = 0; virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t* availableFrameCount) = 0; /* Audio Input */ virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0; virtual HRESULT DisableAudioInput (void) = 0; virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0; /* Input Control */ virtual HRESULT StartStreams (void) = 0; virtual HRESULT StopStreams (void) = 0; virtual HRESULT PauseStreams (void) = 0; virtual HRESULT FlushStreams (void) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback* theCallback) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0; protected: virtual ~IDeckLinkInput () {} // call Release method to drop reference count }; /* Interface IDeckLinkIPExtensions - */ class BMD_PUBLIC IDeckLinkIPExtensions : public IUnknown { public: virtual HRESULT GetDeckLinkIPFlowIterator (/* out */ IDeckLinkIPFlowIterator** iterator) = 0; virtual HRESULT GetIPFlowByID (/* in */ BMDIPFlowID id, /* out */ IDeckLinkIPFlow** flow) = 0; protected: virtual ~IDeckLinkIPExtensions () {} // call Release method to drop reference count }; /* Interface IDeckLinkHDMIInputEDID - Created by QueryInterface from IDeckLink. Releasing all references will restore EDID to default */ class BMD_PUBLIC IDeckLinkHDMIInputEDID : public IUnknown { public: virtual HRESULT SetInt (/* in */ BMDDeckLinkHDMIInputEDIDID cfgID, /* in */ int64_t value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkHDMIInputEDIDID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT WriteToEDID (void) = 0; protected: virtual ~IDeckLinkHDMIInputEDID () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderInput - Created by QueryInterface from IDeckLink. */ class BMD_PUBLIC IDeckLinkEncoderInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedCodec, /* in */ uint32_t requestedCodecProfile, /* in */ BMDSupportedVideoModeFlags flags, /* out */ bool* supported) = 0; virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0; /* Video Input */ virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0; virtual HRESULT DisableVideoInput (void) = 0; virtual HRESULT GetAvailablePacketsCount (/* out */ uint32_t* availablePacketsCount) = 0; /* Audio Input */ virtual HRESULT EnableAudioInput (/* in */ BMDAudioFormat audioFormat, /* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0; virtual HRESULT DisableAudioInput (void) = 0; virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0; /* Input Control */ virtual HRESULT StartStreams (void) = 0; virtual HRESULT StopStreams (void) = 0; virtual HRESULT PauseStreams (void) = 0; virtual HRESULT FlushStreams (void) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkEncoderInputCallback* theCallback) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0; protected: virtual ~IDeckLinkEncoderInput () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoBuffer - Interface to encapsulate a video frame buffer; can be caller-implemented. */ class BMD_PUBLIC IDeckLinkVideoBuffer : public IUnknown { public: virtual HRESULT GetBytes (/* out */ void** buffer) = 0; virtual HRESULT StartAccess (/* in */ BMDBufferAccessFlags flags) = 0; virtual HRESULT EndAccess (/* in */ BMDBufferAccessFlags flags) = 0; protected: virtual ~IDeckLinkVideoBuffer () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ class BMD_PUBLIC IDeckLinkVideoFrame : public IUnknown { public: virtual long GetWidth (void) = 0; virtual long GetHeight (void) = 0; virtual long GetRowBytes (void) = 0; virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDFrameFlags GetFlags (void) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode** timecode) = 0; virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary** ancillary) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred protected: virtual ~IDeckLinkVideoFrame () {} // call Release method to drop reference count }; /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ class BMD_PUBLIC IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0; virtual HRESULT SetTimecode (/* in */ BMDTimecodeFormat format, /* in */ IDeckLinkTimecode* timecode) = 0; virtual HRESULT SetTimecodeFromComponents (/* in */ BMDTimecodeFormat format, /* in */ uint8_t hours, /* in */ uint8_t minutes, /* in */ uint8_t seconds, /* in */ uint8_t frames, /* in */ BMDTimecodeFlags flags) = 0; virtual HRESULT SetAncillaryData (/* in */ IDeckLinkVideoFrameAncillary* ancillary) = 0; virtual HRESULT SetTimecodeUserBits (/* in */ BMDTimecodeFormat format, /* in */ BMDTimecodeUserBits userBits) = 0; virtual HRESULT SetInterfaceProvider (/* in */ REFIID iid, /* in */ IUnknown* iface) = 0; protected: virtual ~IDeckLinkMutableVideoFrame () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrame3DExtensions - Optional interface to support 3D frames. */ class BMD_PUBLIC IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0; virtual HRESULT GetFrameForRightEye (/* out */ IDeckLinkVideoFrame** rightEyeFrame) = 0; protected: virtual ~IDeckLinkVideoFrame3DExtensions () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameMetadataExtensions - Get frame metadata */ class BMD_PUBLIC IDeckLinkVideoFrameMetadataExtensions : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ double* value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ bool* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ const char** value) = 0; virtual HRESULT GetBytes (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ void* buffer /* optional */, /* in, out */ uint32_t* bufferSize) = 0; protected: virtual ~IDeckLinkVideoFrameMetadataExtensions () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameMutableMetadataExtensions - Set frame metadata */ class BMD_PUBLIC IDeckLinkVideoFrameMutableMetadataExtensions : public IDeckLinkVideoFrameMetadataExtensions { public: virtual HRESULT SetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ int64_t value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ double value) = 0; virtual HRESULT SetFlag (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ bool value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ const char* value) = 0; virtual HRESULT SetBytes (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* in */ void* buffer, /* in */ uint32_t bufferSize) = 0; protected: virtual ~IDeckLinkVideoFrameMutableMetadataExtensions () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ class BMD_PUBLIC IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue* frameTime, /* out */ BMDTimeValue* frameDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT GetHardwareReferenceTimestamp (/* in */ BMDTimeScale timeScale, /* out */ BMDTimeValue* frameTime, /* out */ BMDTimeValue* frameDuration) = 0; protected: virtual ~IDeckLinkVideoInputFrame () {} // call Release method to drop reference count }; /* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */ class BMD_PUBLIC IDeckLinkAncillaryPacket : public IUnknown { public: virtual HRESULT GetBytes (/* in */ BMDAncillaryPacketFormat format /* For output, only one format need be offered */, /* out */ const void** data /* Optional */, /* out */ uint32_t* size /* Optional */) = 0; virtual uint8_t GetDID (void) = 0; virtual uint8_t GetSDID (void) = 0; virtual uint32_t GetLineNumber (void) = 0; // On output, zero is auto virtual uint8_t GetDataStreamIndex (void) = 0; // Usually zero. Can only be 1 if non-SD and the first data stream is completely full protected: virtual ~IDeckLinkAncillaryPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */ class BMD_PUBLIC IDeckLinkAncillaryPacketIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkAncillaryPacket** packet) = 0; protected: virtual ~IDeckLinkAncillaryPacketIterator () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface on an IDeckLinkVideoFrame object. */ class BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets : public IUnknown { public: virtual HRESULT GetPacketIterator (/* out */ IDeckLinkAncillaryPacketIterator** iterator) = 0; virtual HRESULT GetFirstPacketByID (/* in */ uint8_t DID, /* in */ uint8_t SDID, /* out */ IDeckLinkAncillaryPacket** packet) = 0; virtual HRESULT AttachPacket (/* in */ IDeckLinkAncillaryPacket* packet) = 0; // Implement IDeckLinkAncillaryPacket to output your own virtual HRESULT DetachPacket (/* in */ IDeckLinkAncillaryPacket* packet) = 0; virtual HRESULT DetachAllPackets (void) = 0; protected: virtual ~IDeckLinkVideoFrameAncillaryPackets () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameAncillary - Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface on an IDeckLinkVideoFrame object. */ class BMD_PUBLIC IDeckLinkVideoFrameAncillary : public IUnknown { public: virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void** buffer) = 0; // Pixels/rowbytes is same as display mode, except for above HD where it's 1920 pixels for UHD modes and 2048 pixels for DCI modes virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; protected: virtual ~IDeckLinkVideoFrameAncillary () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderPacket - Interface to encapsulate an encoded packet. */ class BMD_PUBLIC IDeckLinkEncoderPacket : public IUnknown { public: virtual HRESULT GetBytes (/* out */ void** buffer) = 0; virtual long GetSize (void) = 0; virtual HRESULT GetStreamTime (/* out */ BMDTimeValue* frameTime, /* in */ BMDTimeScale timeScale) = 0; virtual BMDPacketType GetPacketType (void) = 0; protected: virtual ~IDeckLinkEncoderPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderVideoPacket - Provided by the IDeckLinkEncoderInput video packet arrival callback. */ class BMD_PUBLIC IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket { public: virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual HRESULT GetHardwareReferenceTimestamp (/* in */ BMDTimeScale timeScale, /* out */ BMDTimeValue* frameTime, /* out */ BMDTimeValue* frameDuration) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode** timecode) = 0; protected: virtual ~IDeckLinkEncoderVideoPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderAudioPacket - Provided by the IDeckLinkEncoderInput audio packet arrival callback. */ class BMD_PUBLIC IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket { public: virtual BMDAudioFormat GetAudioFormat (void) = 0; protected: virtual ~IDeckLinkEncoderAudioPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkH265NALPacket - Obtained through QueryInterface on an IDeckLinkEncoderVideoPacket object */ class BMD_PUBLIC IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket { public: virtual HRESULT GetUnitType (/* out */ uint8_t* unitType) = 0; virtual HRESULT GetBytesNoPrefix (/* out */ void** buffer) = 0; virtual long GetSizeNoPrefix (void) = 0; protected: virtual ~IDeckLinkH265NALPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkAudioInputPacket - Provided by the IDeckLinkInput callback. */ class BMD_PUBLIC IDeckLinkAudioInputPacket : public IUnknown { public: virtual long GetSampleFrameCount (void) = 0; virtual HRESULT GetBytes (/* out */ void** buffer) = 0; virtual HRESULT GetPacketTime (/* out */ BMDTimeValue* packetTime, /* in */ BMDTimeScale timeScale) = 0; protected: virtual ~IDeckLinkAudioInputPacket () {} // call Release method to drop reference count }; /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ class BMD_PUBLIC IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame* theFrame) = 0; protected: virtual ~IDeckLinkScreenPreviewCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance on platforms with native COM support or from CreateOpenGLScreenPreviewHelper/CreateOpenGL3ScreenPreviewHelper on other platforms. */ class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper : public IUnknown { public: /* Methods must be called with OpenGL context set */ virtual HRESULT InitializeGL (void) = 0; virtual HRESULT PaintGL (void) = 0; virtual HRESULT SetFrame (/* in */ IDeckLinkVideoFrame* theFrame) = 0; virtual HRESULT Set3DPreviewFormat (/* in */ BMD3DPreviewFormat previewFormat) = 0; protected: virtual ~IDeckLinkGLScreenPreviewHelper () {} // call Release method to drop reference count }; /* Interface IDeckLinkNotificationCallback - DeckLink Notification Callback Interface */ class BMD_PUBLIC IDeckLinkNotificationCallback : public IUnknown { public: virtual HRESULT Notify (/* in */ BMDNotifications topic, /* in */ uint64_t param1, /* in */ uint64_t param2) = 0; }; /* Interface IDeckLinkNotification - DeckLink Notification interface */ class BMD_PUBLIC IDeckLinkNotification : public IUnknown { public: virtual HRESULT Subscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback* theCallback) = 0; virtual HRESULT Unsubscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback* theCallback) = 0; protected: virtual ~IDeckLinkNotification () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfileAttributes - Created by QueryInterface from an IDeckLinkProfile, or from IDeckLink. When queried from IDeckLink, interrogates the active profile */ class BMD_PUBLIC IDeckLinkProfileAttributes : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool* value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ const char** value) = 0; protected: virtual ~IDeckLinkProfileAttributes () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfileIterator - Enumerates IDeckLinkProfile interfaces */ class BMD_PUBLIC IDeckLinkProfileIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkProfile** profile) = 0; protected: virtual ~IDeckLinkProfileIterator () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfile - Represents the active profile when queried from IDeckLink */ class BMD_PUBLIC IDeckLinkProfile : public IUnknown { public: virtual HRESULT GetDevice (/* out */ IDeckLink** device) = 0; // Device affected when this profile becomes active virtual HRESULT IsActive (/* out */ bool* isActive) = 0; virtual HRESULT SetActive (void) = 0; // Activating a profile will also change the profile on all devices enumerated by GetPeers. Activation is not complete until IDeckLinkProfileCallback::ProfileActivated is called virtual HRESULT GetPeers (/* out */ IDeckLinkProfileIterator** profileIterator) = 0; // Profiles of other devices activated with this profile protected: virtual ~IDeckLinkProfile () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfileCallback - Receive notifications about profiles related to this device */ class BMD_PUBLIC IDeckLinkProfileCallback : public IUnknown { public: virtual HRESULT ProfileChanging (/* in */ IDeckLinkProfile* profileToBeActivated, /* in */ bool streamsWillBeForcedToStop) = 0; // Called before this device changes profile. User has an opportunity for teardown if streamsWillBeForcedToStop virtual HRESULT ProfileActivated (/* in */ IDeckLinkProfile* activatedProfile) = 0; // Called after this device has been activated with a new profile protected: virtual ~IDeckLinkProfileCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkProfileManager - Created by QueryInterface from IDeckLink when a device has multiple optional profiles */ class BMD_PUBLIC IDeckLinkProfileManager : public IUnknown { public: virtual HRESULT GetProfiles (/* out */ IDeckLinkProfileIterator** profileIterator) = 0; // All available profiles for this device virtual HRESULT GetProfile (/* in */ BMDProfileID profileID, /* out */ IDeckLinkProfile** profile) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkProfileCallback* callback) = 0; protected: virtual ~IDeckLinkProfileManager () {} // call Release method to drop reference count }; /* Interface IDeckLinkStatus - DeckLink Status interface */ class BMD_PUBLIC IDeckLinkStatus : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkStatusID statusID, /* out */ bool* value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkStatusID statusID, /* out */ int64_t* value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkStatusID statusID, /* out */ double* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkStatusID statusID, /* out */ const char** value) = 0; virtual HRESULT GetBytes (/* in */ BMDDeckLinkStatusID statusID, /* out */ void* buffer, /* in, out */ uint32_t* bufferSize) = 0; protected: virtual ~IDeckLinkStatus () {} // call Release method to drop reference count }; /* Interface IDeckLinkKeyer - DeckLink Keyer interface */ class BMD_PUBLIC IDeckLinkKeyer : public IUnknown { public: virtual HRESULT Enable (/* in */ bool isExternal) = 0; virtual HRESULT SetLevel (/* in */ uint8_t level) = 0; virtual HRESULT RampUp (/* in */ uint32_t numberOfFrames) = 0; virtual HRESULT RampDown (/* in */ uint32_t numberOfFrames) = 0; virtual HRESULT Disable (void) = 0; protected: virtual ~IDeckLinkKeyer () {} // call Release method to drop reference count }; /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance. */ class BMD_PUBLIC IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0; virtual HRESULT ConvertNewFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ BMDPixelFormat dstPixelFormat, /* in */ BMDColorspace dstColorspace, /* in */ IDeckLinkVideoBuffer* dstBuffer, /* out */ IDeckLinkVideoFrame** dstFrame) = 0; protected: virtual ~IDeckLinkVideoConversion () {} // call Release method to drop reference count }; /* Interface IDeckLinkDeviceNotificationCallback - DeckLink device arrival/removal notification callbacks */ class BMD_PUBLIC IDeckLinkDeviceNotificationCallback : public IUnknown { public: virtual HRESULT DeckLinkDeviceArrived (/* in */ IDeckLink* deckLinkDevice) = 0; virtual HRESULT DeckLinkDeviceRemoved (/* in */ IDeckLink* deckLinkDevice) = 0; protected: virtual ~IDeckLinkDeviceNotificationCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkDiscovery - DeckLink device discovery */ class BMD_PUBLIC IDeckLinkDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IDeckLinkDeviceNotificationCallback* deviceNotificationCallback) = 0; virtual HRESULT UninstallDeviceNotifications (void) = 0; protected: virtual ~IDeckLinkDiscovery () {} // call Release method to drop reference count }; /* Functions */ extern "C" { BMD_PUBLIC IDeckLinkIterator* CreateDeckLinkIteratorInstance(void); BMD_PUBLIC IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance(void); BMD_PUBLIC IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance(void); BMD_PUBLIC IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper(void); BMD_PUBLIC IDeckLinkGLScreenPreviewHelper* CreateOpenGL3ScreenPreviewHelper(void); // Requires OpenGL 3.2 support and provides improved performance and color handling BMD_PUBLIC IDeckLinkVideoConversion* CreateVideoConversionInstance(void); BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance(void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPI_H) */ mlt-7.38.0/src/modules/decklink/linux/DeckLinkAPIConfiguration.h000664 000000 000000 00000040047 15172202314 024470 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPICONFIGURATION_H #define BMD_DECKLINKAPICONFIGURATION_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkConfiguration = /* 912F634B-2D4E-40A4-8AAB-8D80B73F1289 */ { 0x91,0x2F,0x63,0x4B,0x2D,0x4E,0x40,0xA4,0x8A,0xAB,0x8D,0x80,0xB7,0x3F,0x12,0x89 }; BMD_CONST REFIID IID_IDeckLinkEncoderConfiguration = /* 138050E5-C60A-4552-BF3F-0F358049327E */ { 0x13,0x80,0x50,0xE5,0xC6,0x0A,0x45,0x52,0xBF,0x3F,0x0F,0x35,0x80,0x49,0x32,0x7E }; /* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ typedef uint32_t BMDDeckLinkConfigurationID; enum _BMDDeckLinkConfigurationID { /* Serial port Flags */ bmdDeckLinkConfigSwapSerialRxTx = /* 'ssrt' */ 0x73737274, /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = /* '3dpf' */ 0x33647066, bmdDeckLinkConfigBypass = /* 'byps' */ 0x62797073, bmdDeckLinkConfigClockTimingAdjustment = /* 'ctad' */ 0x63746164, /* Audio Input/Output Flags */ bmdDeckLinkConfigAnalogAudioConsumerLevels = /* 'aacl' */ 0x6161636C, bmdDeckLinkConfigSwapHDMICh3AndCh4OnInput = /* 'hi34' */ 0x68693334, bmdDeckLinkConfigSwapHDMICh3AndCh4OnOutput = /* 'ho34' */ 0x686F3334, /* Video Output Flags */ bmdDeckLinkConfigFieldFlickerRemoval = /* 'fdfr' */ 0x66646672, bmdDeckLinkConfigHD1080p24ToHD1080i5994Conversion = /* 'to59' */ 0x746F3539, bmdDeckLinkConfig444SDIVideoOutput = /* '444o' */ 0x3434346F, bmdDeckLinkConfigBlackVideoOutputDuringCapture = /* 'bvoc' */ 0x62766F63, bmdDeckLinkConfigLowLatencyVideoOutput = /* 'llvo' */ 0x6C6C766F, bmdDeckLinkConfigDownConversionOnAllAnalogOutput = /* 'caao' */ 0x6361616F, bmdDeckLinkConfigSMPTELevelAOutput = /* 'smta' */ 0x736D7461, bmdDeckLinkConfigRec2020Output = /* 'rec2' */ 0x72656332, // Ensure output is Rec.2020 colorspace bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = /* 'SDQS' */ 0x53445153, bmdDeckLinkConfigOutput1080pAsPsF = /* 'pfpr' */ 0x70667072, bmdDeckLinkConfigOutputValidateEDIDForDolbyVision = /* 'pred' */ 0x70726564, /* Video Output Integers */ bmdDeckLinkConfigVideoOutputConnection = /* 'vocn' */ 0x766F636E, bmdDeckLinkConfigVideoOutputConversionMode = /* 'vocm' */ 0x766F636D, bmdDeckLinkConfigVideoOutputConversionColorspaceDestination = /* 'vccd' */ 0x76636364, // Parameter is of type BMDColorspace bmdDeckLinkConfigVideoOutputConversionColorspaceSource = /* 'vccs' */ 0x76636373, // Parameter is of type BMDColorspace bmdDeckLinkConfigAnalogVideoOutputFlags = /* 'avof' */ 0x61766F66, bmdDeckLinkConfigReferenceInputTimingOffset = /* 'glot' */ 0x676C6F74, bmdDeckLinkConfigReferenceOutputMode = /* 'glOm' */ 0x676C4F6D, bmdDeckLinkConfigVideoOutputIdleOperation = /* 'voio' */ 0x766F696F, bmdDeckLinkConfigDefaultVideoOutputMode = /* 'dvom' */ 0x64766F6D, bmdDeckLinkConfigDefaultVideoOutputModeFlags = /* 'dvof' */ 0x64766F66, bmdDeckLinkConfigSDIOutputLinkConfiguration = /* 'solc' */ 0x736F6C63, bmdDeckLinkConfigHDMITimecodePacking = /* 'htpk' */ 0x6874706B, bmdDeckLinkConfigPlaybackGroup = /* 'plgr' */ 0x706C6772, /* Video Output Floats */ bmdDeckLinkConfigVideoOutputComponentLumaGain = /* 'oclg' */ 0x6F636C67, bmdDeckLinkConfigVideoOutputComponentChromaBlueGain = /* 'occb' */ 0x6F636362, bmdDeckLinkConfigVideoOutputComponentChromaRedGain = /* 'occr' */ 0x6F636372, bmdDeckLinkConfigVideoOutputCompositeLumaGain = /* 'oilg' */ 0x6F696C67, bmdDeckLinkConfigVideoOutputCompositeChromaGain = /* 'oicg' */ 0x6F696367, bmdDeckLinkConfigVideoOutputSVideoLumaGain = /* 'oslg' */ 0x6F736C67, bmdDeckLinkConfigVideoOutputSVideoChromaGain = /* 'oscg' */ 0x6F736367, bmdDeckLinkConfigDolbyVisionCMVersion = /* 'dvvr' */ 0x64767672, bmdDeckLinkConfigDolbyVisionMasterMinimumNits = /* 'mnnt' */ 0x6D6E6E74, bmdDeckLinkConfigDolbyVisionMasterMaximumNits = /* 'mxnt' */ 0x6D786E74, /* Video Input Flags */ bmdDeckLinkConfigVideoInputScanning = /* 'visc' */ 0x76697363, // Applicable to H264 Pro Recorder only bmdDeckLinkConfigUseDedicatedLTCInput = /* 'dltc' */ 0x646C7463, // Use timecode from LTC input instead of SDI stream bmdDeckLinkConfigSDIInput3DPayloadOverride = /* '3dds' */ 0x33646473, bmdDeckLinkConfigCapture1080pAsPsF = /* 'cfpr' */ 0x63667072, /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = /* 'vicn' */ 0x7669636E, bmdDeckLinkConfigAnalogVideoInputFlags = /* 'avif' */ 0x61766966, bmdDeckLinkConfigVideoInputConversionMode = /* 'vicm' */ 0x7669636D, bmdDeckLinkConfig32PulldownSequenceInitialTimecodeFrame = /* 'pdif' */ 0x70646966, bmdDeckLinkConfigVANCSourceLine1Mapping = /* 'vsl1' */ 0x76736C31, bmdDeckLinkConfigVANCSourceLine2Mapping = /* 'vsl2' */ 0x76736C32, bmdDeckLinkConfigVANCSourceLine3Mapping = /* 'vsl3' */ 0x76736C33, bmdDeckLinkConfigCapturePassThroughMode = /* 'cptm' */ 0x6370746D, bmdDeckLinkConfigCaptureGroup = /* 'cpgr' */ 0x63706772, /* Video Input Floats */ bmdDeckLinkConfigVideoInputComponentLumaGain = /* 'iclg' */ 0x69636C67, bmdDeckLinkConfigVideoInputComponentChromaBlueGain = /* 'iccb' */ 0x69636362, bmdDeckLinkConfigVideoInputComponentChromaRedGain = /* 'iccr' */ 0x69636372, bmdDeckLinkConfigVideoInputCompositeLumaGain = /* 'iilg' */ 0x69696C67, bmdDeckLinkConfigVideoInputCompositeChromaGain = /* 'iicg' */ 0x69696367, bmdDeckLinkConfigVideoInputSVideoLumaGain = /* 'islg' */ 0x69736C67, bmdDeckLinkConfigVideoInputSVideoChromaGain = /* 'iscg' */ 0x69736367, /* Keying Integers */ bmdDeckLinkConfigInternalKeyingAncillaryDataSource = /* 'ikas' */ 0x696B6173, /* Audio Input Flags */ bmdDeckLinkConfigMicrophonePhantomPower = /* 'mphp' */ 0x6D706870, /* Audio Input Integers */ bmdDeckLinkConfigAudioInputConnection = /* 'aicn' */ 0x6169636E, /* Audio Input Floats */ bmdDeckLinkConfigAnalogAudioInputScaleChannel1 = /* 'ais1' */ 0x61697331, bmdDeckLinkConfigAnalogAudioInputScaleChannel2 = /* 'ais2' */ 0x61697332, bmdDeckLinkConfigAnalogAudioInputScaleChannel3 = /* 'ais3' */ 0x61697333, bmdDeckLinkConfigAnalogAudioInputScaleChannel4 = /* 'ais4' */ 0x61697334, bmdDeckLinkConfigDigitalAudioInputScale = /* 'dais' */ 0x64616973, bmdDeckLinkConfigMicrophoneInputGain = /* 'micg' */ 0x6D696367, /* Audio Output Integers */ bmdDeckLinkConfigAudioOutputAESAnalogSwitch = /* 'aoaa' */ 0x616F6161, /* Audio Output Floats */ bmdDeckLinkConfigAnalogAudioOutputScaleChannel1 = /* 'aos1' */ 0x616F7331, bmdDeckLinkConfigAnalogAudioOutputScaleChannel2 = /* 'aos2' */ 0x616F7332, bmdDeckLinkConfigAnalogAudioOutputScaleChannel3 = /* 'aos3' */ 0x616F7333, bmdDeckLinkConfigAnalogAudioOutputScaleChannel4 = /* 'aos4' */ 0x616F7334, bmdDeckLinkConfigDigitalAudioOutputScale = /* 'daos' */ 0x64616F73, bmdDeckLinkConfigHeadphoneVolume = /* 'hvol' */ 0x68766F6C, /* Network Flags */ bmdDeckLinkConfigEthernetUseDHCP = /* 'DHCP' */ 0x44484350, bmdDeckLinkConfigEthernetPTPFollowerOnly = /* 'PTPf' */ 0x50545066, bmdDeckLinkConfigEthernetPTPUseUDPEncapsulation = /* 'PTPU' */ 0x50545055, /* Network Integers */ bmdDeckLinkConfigEthernetPTPPriority1 = /* 'PTP1' */ 0x50545031, bmdDeckLinkConfigEthernetPTPPriority2 = /* 'PTP2' */ 0x50545032, bmdDeckLinkConfigEthernetPTPDomain = /* 'PTPD' */ 0x50545044, bmdDeckLinkConfigEthernetPTPLogAnnounceInterval = /* 'PTPA' */ 0x50545041, /* Network Strings */ bmdDeckLinkConfigEthernetStaticLocalIPAddress = /* 'nsip' */ 0x6E736970, bmdDeckLinkConfigEthernetStaticSubnetMask = /* 'nssm' */ 0x6E73736D, bmdDeckLinkConfigEthernetStaticGatewayIPAddress = /* 'nsgw' */ 0x6E736777, bmdDeckLinkConfigEthernetStaticPrimaryDNS = /* 'nspd' */ 0x6E737064, bmdDeckLinkConfigEthernetStaticSecondaryDNS = /* 'nssd' */ 0x6E737364, bmdDeckLinkConfigEthernetVideoOutputAddress = /* 'noav' */ 0x6E6F6176, bmdDeckLinkConfigEthernetAudioOutputAddress = /* 'noaa' */ 0x6E6F6161, bmdDeckLinkConfigEthernetAncillaryOutputAddress = /* 'noaA' */ 0x6E6F6141, bmdDeckLinkConfigEthernetAudioOutputChannelOrder = /* 'caco' */ 0x6361636F, /* Device Information Strings */ bmdDeckLinkConfigDeviceInformationLabel = /* 'dila' */ 0x64696C61, bmdDeckLinkConfigDeviceInformationSerialNumber = /* 'disn' */ 0x6469736E, bmdDeckLinkConfigDeviceInformationCompany = /* 'dico' */ 0x6469636F, bmdDeckLinkConfigDeviceInformationPhone = /* 'diph' */ 0x64697068, bmdDeckLinkConfigDeviceInformationEmail = /* 'diem' */ 0x6469656D, bmdDeckLinkConfigDeviceInformationDate = /* 'dida' */ 0x64696461, /* Deck Control Integers */ bmdDeckLinkConfigDeckControlConnection = /* 'dcco' */ 0x6463636F }; /* Enum BMDDeckLinkEncoderConfigurationID - DeckLink Encoder Configuration ID */ typedef uint32_t BMDDeckLinkEncoderConfigurationID; enum _BMDDeckLinkEncoderConfigurationID { /* Video Encoder Integers */ bmdDeckLinkEncoderConfigPreferredBitDepth = /* 'epbr' */ 0x65706272, bmdDeckLinkEncoderConfigFrameCodingMode = /* 'efcm' */ 0x6566636D, /* HEVC/H.265 Encoder Integers */ bmdDeckLinkEncoderConfigH265TargetBitrate = /* 'htbr' */ 0x68746272, /* DNxHR/DNxHD Compression ID */ bmdDeckLinkEncoderConfigDNxHRCompressionID = /* 'dcid' */ 0x64636964, /* DNxHR/DNxHD Level */ bmdDeckLinkEncoderConfigDNxHRLevel = /* 'dlev' */ 0x646C6576, /* Encoded Sample Decriptions */ bmdDeckLinkEncoderConfigMPEG4SampleDescription = /* 'stsE' */ 0x73747345, // Full MPEG4 sample description (aka SampleEntry of an 'stsd' atom-box). Useful for MediaFoundation, QuickTime, MKV and more bmdDeckLinkEncoderConfigMPEG4CodecSpecificDesc = /* 'esds' */ 0x65736473 // Sample description extensions only (atom stream, each with size and fourCC header). Useful for AVFoundation, VideoToolbox, MKV and more }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkConfiguration; class IDeckLinkEncoderConfiguration; /* Interface IDeckLinkConfiguration - DeckLink Configuration interface */ class BMD_PUBLIC IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool* value) = 0; virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double* value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char** value) = 0; virtual HRESULT WriteConfigurationToPreferences (void) = 0; protected: virtual ~IDeckLinkConfiguration () {} // call Release method to drop reference count }; /* Interface IDeckLinkEncoderConfiguration - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */ class BMD_PUBLIC IDeckLinkEncoderConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ bool* value) = 0; virtual HRESULT SetInt (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ int64_t value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ int64_t* value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ double value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ double* value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ const char* value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ const char** value) = 0; virtual HRESULT GetBytes (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ void* buffer /* optional */, /* in, out */ uint32_t* bufferSize) = 0; protected: virtual ~IDeckLinkEncoderConfiguration () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPICONFIGURATION_H) */ mlt-7.38.0/src/modules/decklink/linux/DeckLinkAPIDeckControl.h000664 000000 000000 00000031472 15172202314 024072 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPIDECKCONTROL_H #define BMD_DECKLINKAPIDECKCONTROL_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkDeckControlStatusCallback = /* 53436FFB-B434-4906-BADC-AE3060FFE8EF */ { 0x53,0x43,0x6F,0xFB,0xB4,0x34,0x49,0x06,0xBA,0xDC,0xAE,0x30,0x60,0xFF,0xE8,0xEF }; BMD_CONST REFIID IID_IDeckLinkDeckControl = /* 8E1C3ACE-19C7-4E00-8B92-D80431D958BE */ { 0x8E,0x1C,0x3A,0xCE,0x19,0xC7,0x4E,0x00,0x8B,0x92,0xD8,0x04,0x31,0xD9,0x58,0xBE }; /* Enum BMDDeckControlMode - DeckControl mode */ typedef uint32_t BMDDeckControlMode; enum _BMDDeckControlMode { bmdDeckControlNotOpened = /* 'ntop' */ 0x6E746F70, bmdDeckControlVTRControlMode = /* 'vtrc' */ 0x76747263, bmdDeckControlExportMode = /* 'expm' */ 0x6578706D, bmdDeckControlCaptureMode = /* 'capm' */ 0x6361706D }; /* Enum BMDDeckControlEvent - DeckControl event */ typedef uint32_t BMDDeckControlEvent; enum _BMDDeckControlEvent { bmdDeckControlAbortedEvent = /* 'abte' */ 0x61627465, // This event is triggered when a capture or edit-to-tape operation is aborted. /* Export-To-Tape events */ bmdDeckControlPrepareForExportEvent = /* 'pfee' */ 0x70666565, // This event is triggered a few frames before reaching the in-point. IDeckLinkInput::StartScheduledPlayback should be called at this point. bmdDeckControlExportCompleteEvent = /* 'exce' */ 0x65786365, // This event is triggered a few frames after reaching the out-point. At this point, it is safe to stop playback. Upon reception of this event the deck's control mode is set back to bmdDeckControlVTRControlMode. /* Capture events */ bmdDeckControlPrepareForCaptureEvent = /* 'pfce' */ 0x70666365, // This event is triggered a few frames before reaching the in-point. The serial timecode attached to IDeckLinkVideoInputFrames is now valid. bmdDeckControlCaptureCompleteEvent = /* 'ccev' */ 0x63636576 // This event is triggered a few frames after reaching the out-point. Upon reception of this event the deck's control mode is set back to bmdDeckControlVTRControlMode. }; /* Enum BMDDeckControlVTRControlState - VTR Control state */ typedef uint32_t BMDDeckControlVTRControlState; enum _BMDDeckControlVTRControlState { bmdDeckControlNotInVTRControlMode = /* 'nvcm' */ 0x6E76636D, bmdDeckControlVTRControlPlaying = /* 'vtrp' */ 0x76747270, bmdDeckControlVTRControlRecording = /* 'vtrr' */ 0x76747272, bmdDeckControlVTRControlStill = /* 'vtra' */ 0x76747261, bmdDeckControlVTRControlShuttleForward = /* 'vtsf' */ 0x76747366, bmdDeckControlVTRControlShuttleReverse = /* 'vtsr' */ 0x76747372, bmdDeckControlVTRControlJogForward = /* 'vtjf' */ 0x76746A66, bmdDeckControlVTRControlJogReverse = /* 'vtjr' */ 0x76746A72, bmdDeckControlVTRControlStopped = /* 'vtro' */ 0x7674726F }; /* Enum BMDDeckControlStatusFlags - Deck Control status flags */ typedef uint32_t BMDDeckControlStatusFlags; enum _BMDDeckControlStatusFlags { bmdDeckControlStatusDeckConnected = 1 << 0, bmdDeckControlStatusRemoteMode = 1 << 1, bmdDeckControlStatusRecordInhibited = 1 << 2, bmdDeckControlStatusCassetteOut = 1 << 3 }; /* Enum BMDDeckControlExportModeOpsFlags - Export mode flags */ typedef uint32_t BMDDeckControlExportModeOpsFlags; enum _BMDDeckControlExportModeOpsFlags { bmdDeckControlExportModeInsertVideo = 1 << 0, bmdDeckControlExportModeInsertAudio1 = 1 << 1, bmdDeckControlExportModeInsertAudio2 = 1 << 2, bmdDeckControlExportModeInsertAudio3 = 1 << 3, bmdDeckControlExportModeInsertAudio4 = 1 << 4, bmdDeckControlExportModeInsertAudio5 = 1 << 5, bmdDeckControlExportModeInsertAudio6 = 1 << 6, bmdDeckControlExportModeInsertAudio7 = 1 << 7, bmdDeckControlExportModeInsertAudio8 = 1 << 8, bmdDeckControlExportModeInsertAudio9 = 1 << 9, bmdDeckControlExportModeInsertAudio10 = 1 << 10, bmdDeckControlExportModeInsertAudio11 = 1 << 11, bmdDeckControlExportModeInsertAudio12 = 1 << 12, bmdDeckControlExportModeInsertTimeCode = 1 << 13, bmdDeckControlExportModeInsertAssemble = 1 << 14, bmdDeckControlExportModeInsertPreview = 1 << 15, bmdDeckControlUseManualExport = 1 << 16 }; /* Enum BMDDeckControlError - Deck Control error */ typedef uint32_t BMDDeckControlError; enum _BMDDeckControlError { bmdDeckControlNoError = /* 'noer' */ 0x6E6F6572, bmdDeckControlModeError = /* 'moer' */ 0x6D6F6572, bmdDeckControlMissedInPointError = /* 'mier' */ 0x6D696572, bmdDeckControlDeckTimeoutError = /* 'dter' */ 0x64746572, bmdDeckControlCommandFailedError = /* 'cfer' */ 0x63666572, bmdDeckControlDeviceAlreadyOpenedError = /* 'dalo' */ 0x64616C6F, bmdDeckControlFailedToOpenDeviceError = /* 'fder' */ 0x66646572, bmdDeckControlInLocalModeError = /* 'lmer' */ 0x6C6D6572, bmdDeckControlEndOfTapeError = /* 'eter' */ 0x65746572, bmdDeckControlUserAbortError = /* 'uaer' */ 0x75616572, bmdDeckControlNoTapeInDeckError = /* 'nter' */ 0x6E746572, bmdDeckControlNoVideoFromCardError = /* 'nvfc' */ 0x6E766663, bmdDeckControlNoCommunicationError = /* 'ncom' */ 0x6E636F6D, bmdDeckControlBufferTooSmallError = /* 'btsm' */ 0x6274736D, bmdDeckControlBadChecksumError = /* 'chks' */ 0x63686B73, bmdDeckControlUnknownError = /* 'uner' */ 0x756E6572 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkDeckControlStatusCallback; class IDeckLinkDeckControl; /* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */ class BMD_PUBLIC IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState newState, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0; protected: virtual ~IDeckLinkDeckControlStatusCallback () {} // call Release method to drop reference count }; /* Interface IDeckLinkDeckControl - Deck Control main interface */ class BMD_PUBLIC IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Close (/* in */ bool standbyOn) = 0; virtual HRESULT GetCurrentState (/* out */ BMDDeckControlMode* mode, /* out */ BMDDeckControlVTRControlState* vtrControlState, /* out */ BMDDeckControlStatusFlags* flags) = 0; virtual HRESULT SetStandby (/* in */ bool standbyOn) = 0; virtual HRESULT SendCommand (/* in */ uint8_t* inBuffer, /* in */ uint32_t inBufferSize, /* out */ uint8_t* outBuffer, /* out */ uint32_t* outDataSize, /* in */ uint32_t outBufferSize, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Play (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT Stop (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT TogglePlayStop (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT Eject (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT GoToTimecode (/* in */ BMDTimecodeBCD timecode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT FastForward (/* in */ bool viewTape, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Rewind (/* in */ bool viewTape, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT StepForward (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT StepBack (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT Jog (/* in */ double rate, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Shuttle (/* in */ double rate, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT GetTimecodeString (/* out */ const char** currentTimeCode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT GetTimecode (/* out */ IDeckLinkTimecode** currentTimecode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT GetTimecodeBCD (/* out */ BMDTimecodeBCD* currentTimecode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT SetPreroll (/* in */ uint32_t prerollSeconds) = 0; virtual HRESULT GetPreroll (/* out */ uint32_t* prerollSeconds) = 0; virtual HRESULT SetExportOffset (/* in */ int32_t exportOffsetFields) = 0; virtual HRESULT GetExportOffset (/* out */ int32_t* exportOffsetFields) = 0; virtual HRESULT GetManualExportOffset (/* out */ int32_t* deckManualExportOffsetFields) = 0; virtual HRESULT SetCaptureOffset (/* in */ int32_t captureOffsetFields) = 0; virtual HRESULT GetCaptureOffset (/* out */ int32_t* captureOffsetFields) = 0; virtual HRESULT StartExport (/* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* in */ BMDDeckControlExportModeOpsFlags exportModeOps, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT StartCapture (/* in */ bool useVITC, /* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT GetDeviceID (/* out */ uint16_t* deviceId, /* out */ BMDDeckControlError* error) = 0; virtual HRESULT Abort (void) = 0; virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError* error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback* callback) = 0; protected: virtual ~IDeckLinkDeckControl () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPIDECKCONTROL_H) */ mlt-7.38.0/src/modules/decklink/linux/DeckLinkAPIDiscovery.h000664 000000 000000 00000005006 15172202314 023624 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPIDISCOVERY_H #define BMD_DECKLINKAPIDISCOVERY_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLink = /* C418FBDD-0587-48ED-8FE5-640F0A14AF91 */ { 0xC4,0x18,0xFB,0xDD,0x05,0x87,0x48,0xED,0x8F,0xE5,0x64,0x0F,0x0A,0x14,0xAF,0x91 }; #if defined(__cplusplus) // Forward Declarations class IDeckLink; /* Interface IDeckLink - Represents a DeckLink device */ class BMD_PUBLIC IDeckLink : public IUnknown { public: virtual HRESULT GetModelName (/* out */ const char** modelName) = 0; virtual HRESULT GetDisplayName (/* out */ const char** displayName) = 0; protected: virtual ~IDeckLink () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPIDISCOVERY_H) */ mlt-7.38.0/src/modules/decklink/linux/DeckLinkAPIDispatch.cpp000664 000000 000000 00000015723 15172202314 023756 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2009 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation (the ** "Software") to use, reproduce, display, distribute, sub-license, execute, ** and transmit the Software, and to prepare derivative works of the Software, ** and to permit third-parties to whom the Software is furnished to do so, in ** accordance with: ** ** (1) if the Software is obtained from Blackmagic Design, the End User License ** Agreement for the Software Development Kit ("EULA") available at ** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or ** ** (2) if the Software is obtained from any third party, such licensing terms ** as notified by that third party, ** ** and all subject to the following: ** ** (3) the copyright notices in the Software and this entire statement, ** including the above license grant, this restriction and the following ** disclaimer, must be included in all copies of the Software, in whole or in ** part, and all derivative works of the Software, unless such copies or ** derivative works are solely in the form of machine-executable object code ** generated by a source language processor. ** ** (4) 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** ** A copy of the Software is available free of charge at ** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. ** ** -LICENSE-END- **/ #include #include #include #include "DeckLinkAPI.h" #define kDeckLinkAPI_Name "libDeckLinkAPI.so" #define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so" typedef IDeckLinkIterator* (*CreateIteratorFunc)(void); typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGL3ScreenPreviewHelperFunc)(void); typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void); static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT; static bool gLoadedDeckLinkAPI = false; static CreateIteratorFunc gCreateIteratorFunc = NULL; static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateOpenGL3ScreenPreviewHelperFunc gCreateOpenGL3PreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL; static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL; static void InitDeckLinkAPI (void) { void *libraryHandle; libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } gLoadedDeckLinkAPI = true; gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0004"); if (!gCreateIteratorFunc) fprintf(stderr, "%s\n", dlerror()); gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001"); if (!gCreateAPIInformationFunc) fprintf(stderr, "%s\n", dlerror()); gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0002"); if (!gCreateVideoConversionFunc) fprintf(stderr, "%s\n", dlerror()); gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0003"); if (!gCreateDeckLinkDiscoveryFunc) fprintf(stderr, "%s\n", dlerror()); gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)dlsym(libraryHandle, "CreateVideoFrameAncillaryPacketsInstance_0001"); if (!gCreateVideoFrameAncillaryPacketsFunc) fprintf(stderr, "%s\n", dlerror()); } static void InitDeckLinkPreviewAPI (void) { void *libraryHandle; libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0002"); if (!gCreateOpenGLPreviewFunc) fprintf(stderr, "%s\n", dlerror()); gCreateOpenGL3PreviewFunc = (CreateOpenGL3ScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGL3ScreenPreviewHelper_0002"); if (!gCreateOpenGL3PreviewFunc) fprintf(stderr, "%s\n", dlerror()); } bool IsDeckLinkAPIPresent (void) { // If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller return gLoadedDeckLinkAPI; } IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); } IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateAPIInformationFunc == NULL) return NULL; return gCreateAPIInformationFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGL3ScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); if (gCreateOpenGL3PreviewFunc == NULL) return NULL; return gCreateOpenGL3PreviewFunc(); } IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); } IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateDeckLinkDiscoveryFunc == NULL) return NULL; return gCreateDeckLinkDiscoveryFunc(); } IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateVideoFrameAncillaryPacketsFunc == NULL) return NULL; return gCreateVideoFrameAncillaryPacketsFunc(); } mlt-7.38.0/src/modules/decklink/linux/DeckLinkAPIModes.h000664 000000 000000 00000041325 15172202314 022730 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPIMODES_H #define BMD_DECKLINKAPIMODES_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkDisplayModeIterator = /* 9C88499F-F601-4021-B80B-032E4EB41C35 */ { 0x9C,0x88,0x49,0x9F,0xF6,0x01,0x40,0x21,0xB8,0x0B,0x03,0x2E,0x4E,0xB4,0x1C,0x35 }; BMD_CONST REFIID IID_IDeckLinkDisplayMode = /* 3EB2C1AB-0A3D-4523-A3AD-F40D7FB14E78 */ { 0x3E,0xB2,0xC1,0xAB,0x0A,0x3D,0x45,0x23,0xA3,0xAD,0xF4,0x0D,0x7F,0xB1,0x4E,0x78 }; /* Enum BMDDisplayMode - BMDDisplayMode enumerates the video modes supported. */ typedef uint32_t BMDDisplayMode; enum _BMDDisplayMode { /* SD Modes */ bmdModeNTSC = /* 'ntsc' */ 0x6E747363, bmdModeNTSC2398 = /* 'nt23' */ 0x6E743233, // 3:2 pulldown bmdModePAL = /* 'pal ' */ 0x70616C20, bmdModeNTSCp = /* 'ntsp' */ 0x6E747370, bmdModePALp = /* 'palp' */ 0x70616C70, /* HD 1080 Modes */ bmdModeHD1080p2398 = /* '23ps' */ 0x32337073, bmdModeHD1080p24 = /* '24ps' */ 0x32347073, bmdModeHD1080p25 = /* 'Hp25' */ 0x48703235, bmdModeHD1080p2997 = /* 'Hp29' */ 0x48703239, bmdModeHD1080p30 = /* 'Hp30' */ 0x48703330, bmdModeHD1080p4795 = /* 'Hp47' */ 0x48703437, bmdModeHD1080p48 = /* 'Hp48' */ 0x48703438, bmdModeHD1080p50 = /* 'Hp50' */ 0x48703530, bmdModeHD1080p5994 = /* 'Hp59' */ 0x48703539, bmdModeHD1080p6000 = /* 'Hp60' */ 0x48703630, // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p9590 = /* 'Hp95' */ 0x48703935, bmdModeHD1080p96 = /* 'Hp96' */ 0x48703936, bmdModeHD1080p100 = /* 'Hp10' */ 0x48703130, bmdModeHD1080p11988 = /* 'Hp11' */ 0x48703131, bmdModeHD1080p120 = /* 'Hp12' */ 0x48703132, bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ bmdModeHD720p50 = /* 'hp50' */ 0x68703530, bmdModeHD720p5994 = /* 'hp59' */ 0x68703539, bmdModeHD720p60 = /* 'hp60' */ 0x68703630, /* 2K Modes */ bmdMode2k2398 = /* '2k23' */ 0x326B3233, bmdMode2k24 = /* '2k24' */ 0x326B3234, bmdMode2k25 = /* '2k25' */ 0x326B3235, /* 2K DCI Modes */ bmdMode2kDCI2398 = /* '2d23' */ 0x32643233, bmdMode2kDCI24 = /* '2d24' */ 0x32643234, bmdMode2kDCI25 = /* '2d25' */ 0x32643235, bmdMode2kDCI2997 = /* '2d29' */ 0x32643239, bmdMode2kDCI30 = /* '2d30' */ 0x32643330, bmdMode2kDCI4795 = /* '2d47' */ 0x32643437, bmdMode2kDCI48 = /* '2d48' */ 0x32643438, bmdMode2kDCI50 = /* '2d50' */ 0x32643530, bmdMode2kDCI5994 = /* '2d59' */ 0x32643539, bmdMode2kDCI60 = /* '2d60' */ 0x32643630, bmdMode2kDCI9590 = /* '2d95' */ 0x32643935, bmdMode2kDCI96 = /* '2d96' */ 0x32643936, bmdMode2kDCI100 = /* '2d10' */ 0x32643130, bmdMode2kDCI11988 = /* '2d11' */ 0x32643131, bmdMode2kDCI120 = /* '2d12' */ 0x32643132, /* 4K UHD Modes */ bmdMode4K2160p2398 = /* '4k23' */ 0x346B3233, bmdMode4K2160p24 = /* '4k24' */ 0x346B3234, bmdMode4K2160p25 = /* '4k25' */ 0x346B3235, bmdMode4K2160p2997 = /* '4k29' */ 0x346B3239, bmdMode4K2160p30 = /* '4k30' */ 0x346B3330, bmdMode4K2160p4795 = /* '4k47' */ 0x346B3437, bmdMode4K2160p48 = /* '4k48' */ 0x346B3438, bmdMode4K2160p50 = /* '4k50' */ 0x346B3530, bmdMode4K2160p5994 = /* '4k59' */ 0x346B3539, bmdMode4K2160p60 = /* '4k60' */ 0x346B3630, bmdMode4K2160p9590 = /* '4k95' */ 0x346B3935, bmdMode4K2160p96 = /* '4k96' */ 0x346B3936, bmdMode4K2160p100 = /* '4k10' */ 0x346B3130, bmdMode4K2160p11988 = /* '4k11' */ 0x346B3131, bmdMode4K2160p120 = /* '4k12' */ 0x346B3132, /* 4K DCI Modes */ bmdMode4kDCI2398 = /* '4d23' */ 0x34643233, bmdMode4kDCI24 = /* '4d24' */ 0x34643234, bmdMode4kDCI25 = /* '4d25' */ 0x34643235, bmdMode4kDCI2997 = /* '4d29' */ 0x34643239, bmdMode4kDCI30 = /* '4d30' */ 0x34643330, bmdMode4kDCI4795 = /* '4d47' */ 0x34643437, bmdMode4kDCI48 = /* '4d48' */ 0x34643438, bmdMode4kDCI50 = /* '4d50' */ 0x34643530, bmdMode4kDCI5994 = /* '4d59' */ 0x34643539, bmdMode4kDCI60 = /* '4d60' */ 0x34643630, bmdMode4kDCI9590 = /* '4d95' */ 0x34643935, bmdMode4kDCI96 = /* '4d96' */ 0x34643936, bmdMode4kDCI100 = /* '4d10' */ 0x34643130, bmdMode4kDCI11988 = /* '4d11' */ 0x34643131, bmdMode4kDCI120 = /* '4d12' */ 0x34643132, /* 8K UHD Modes */ bmdMode8K4320p2398 = /* '8k23' */ 0x386B3233, bmdMode8K4320p24 = /* '8k24' */ 0x386B3234, bmdMode8K4320p25 = /* '8k25' */ 0x386B3235, bmdMode8K4320p2997 = /* '8k29' */ 0x386B3239, bmdMode8K4320p30 = /* '8k30' */ 0x386B3330, bmdMode8K4320p4795 = /* '8k47' */ 0x386B3437, bmdMode8K4320p48 = /* '8k48' */ 0x386B3438, bmdMode8K4320p50 = /* '8k50' */ 0x386B3530, bmdMode8K4320p5994 = /* '8k59' */ 0x386B3539, bmdMode8K4320p60 = /* '8k60' */ 0x386B3630, /* 8K DCI Modes */ bmdMode8kDCI2398 = /* '8d23' */ 0x38643233, bmdMode8kDCI24 = /* '8d24' */ 0x38643234, bmdMode8kDCI25 = /* '8d25' */ 0x38643235, bmdMode8kDCI2997 = /* '8d29' */ 0x38643239, bmdMode8kDCI30 = /* '8d30' */ 0x38643330, bmdMode8kDCI4795 = /* '8d47' */ 0x38643437, bmdMode8kDCI48 = /* '8d48' */ 0x38643438, bmdMode8kDCI50 = /* '8d50' */ 0x38643530, bmdMode8kDCI5994 = /* '8d59' */ 0x38643539, bmdMode8kDCI60 = /* '8d60' */ 0x38643630, /* PC Modes */ bmdMode640x480p60 = /* 'vga6' */ 0x76676136, bmdMode800x600p60 = /* 'svg6' */ 0x73766736, bmdMode1440x900p50 = /* 'wxg5' */ 0x77786735, bmdMode1440x900p60 = /* 'wxg6' */ 0x77786736, bmdMode1440x1080p50 = /* 'sxg5' */ 0x73786735, bmdMode1440x1080p60 = /* 'sxg6' */ 0x73786736, bmdMode1600x1200p50 = /* 'uxg5' */ 0x75786735, bmdMode1600x1200p60 = /* 'uxg6' */ 0x75786736, bmdMode1920x1200p50 = /* 'wux5' */ 0x77757835, bmdMode1920x1200p60 = /* 'wux6' */ 0x77757836, bmdMode1920x1440p50 = /* '1945' */ 0x31393435, bmdMode1920x1440p60 = /* '1946' */ 0x31393436, bmdMode2560x1440p50 = /* 'wqh5' */ 0x77716835, bmdMode2560x1440p60 = /* 'wqh6' */ 0x77716836, bmdMode2560x1600p50 = /* 'wqx5' */ 0x77717835, bmdMode2560x1600p60 = /* 'wqx6' */ 0x77717836, /* Special Modes */ bmdModeUnknown = /* 'iunk' */ 0x69756E6B }; /* Enum BMDFieldDominance - BMDFieldDominance enumerates settings applicable to video fields. */ typedef uint32_t BMDFieldDominance; enum _BMDFieldDominance { bmdUnknownFieldDominance = 0, bmdLowerFieldFirst = /* 'lowr' */ 0x6C6F7772, bmdUpperFieldFirst = /* 'uppr' */ 0x75707072, bmdProgressiveFrame = /* 'prog' */ 0x70726F67, bmdProgressiveSegmentedFrame = /* 'psf ' */ 0x70736620 }; /* Enum BMDPixelFormat - Video pixel formats supported for output/input */ typedef uint32_t BMDPixelFormat; enum _BMDPixelFormat { bmdFormatUnspecified = 0, bmdFormat8BitYUV = /* '2vuy' */ 0x32767579, bmdFormat10BitYUV = /* 'v210' */ 0x76323130, bmdFormat10BitYUVA = /* 'Ay10' */ 0x41793130, // Big-endian YUVA 10 bit per component with SMPTE video levels (64-940) for YUV but full range alpha bmdFormat8BitARGB = 32, bmdFormat8BitBGRA = /* 'BGRA' */ 0x42475241, bmdFormat10BitRGB = /* 'r210' */ 0x72323130, // Big-endian RGB 10-bit per component with SMPTE video levels (64-940). Packed as 2:10:10:10 bmdFormat12BitRGB = /* 'R12B' */ 0x52313242, // Big-endian RGB 12-bit per component with full range (0-4095). Packed as 12-bit per component bmdFormat12BitRGBLE = /* 'R12L' */ 0x5231324C, // Little-endian RGB 12-bit per component with full range (0-4095). Packed as 12-bit per component bmdFormat10BitRGBXLE = /* 'R10l' */ 0x5231306C, // Little-endian 10-bit RGB with SMPTE video levels (64-940) bmdFormat10BitRGBX = /* 'R10b' */ 0x52313062, // Big-endian 10-bit RGB with SMPTE video levels (64-940) bmdFormatH265 = /* 'hev1' */ 0x68657631, // High Efficiency Video Coding (HEVC/h.265) /* AVID DNxHR */ bmdFormatDNxHR = /* 'AVdh' */ 0x41566468 }; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ typedef uint32_t BMDDisplayModeFlags; enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = 1 << 0, bmdDisplayModeColorspaceRec601 = 1 << 1, bmdDisplayModeColorspaceRec709 = 1 << 2, bmdDisplayModeColorspaceRec2020 = 1 << 3 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkDisplayModeIterator; class IDeckLinkDisplayMode; /* Interface IDeckLinkDisplayModeIterator - Enumerates over supported input/output display modes. */ class BMD_PUBLIC IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode** deckLinkDisplayMode) = 0; protected: virtual ~IDeckLinkDisplayModeIterator () {} // call Release method to drop reference count }; /* Interface IDeckLinkDisplayMode - Represents a display mode */ class BMD_PUBLIC IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT GetName (/* out */ const char** name) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; virtual long GetWidth (void) = 0; virtual long GetHeight (void) = 0; virtual HRESULT GetFrameRate (/* out */ BMDTimeValue* frameDuration, /* out */ BMDTimeScale* timeScale) = 0; virtual BMDFieldDominance GetFieldDominance (void) = 0; virtual BMDDisplayModeFlags GetFlags (void) = 0; protected: virtual ~IDeckLinkDisplayMode () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPIMODES_H) */ mlt-7.38.0/src/modules/decklink/linux/DeckLinkAPITypes.h000664 000000 000000 00000012347 15172202314 022767 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2024 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* * -- AUTOMATICALLY GENERATED - DO NOT EDIT --- */ #ifndef BMD_DECKLINKAPITYPES_H #define BMD_DECKLINKAPITYPES_H #ifndef BMD_CONST #if defined(_MSC_VER) #define BMD_CONST __declspec(selectany) static const #else #define BMD_CONST static const #endif #endif #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif // Type Declarations typedef int64_t BMDTimeValue; typedef int64_t BMDTimeScale; typedef uint32_t BMDTimecodeBCD; typedef uint32_t BMDTimecodeUserBits; typedef int64_t BMDIPFlowID; // Interface ID Declarations BMD_CONST REFIID IID_IDeckLinkTimecode = /* BC6CFBD3-8317-4325-AC1C-1216391E9340 */ { 0xBC,0x6C,0xFB,0xD3,0x83,0x17,0x43,0x25,0xAC,0x1C,0x12,0x16,0x39,0x1E,0x93,0x40 }; /* Enum BMDTimecodeFlags - Timecode flags */ typedef uint32_t BMDTimecodeFlags; enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0, bmdTimecodeFieldMark = 1 << 1, bmdTimecodeColorFrame = 1 << 2, bmdTimecodeEmbedRecordingTrigger = 1 << 3, // On SDI recording trigger utilises a user-bit. bmdTimecodeRecordingTriggered = 1 << 4 }; /* Enum BMDVideoConnection - Video connection types */ typedef uint32_t BMDVideoConnection; enum _BMDVideoConnection { bmdVideoConnectionUnspecified = 0, bmdVideoConnectionSDI = 1 << 0, bmdVideoConnectionHDMI = 1 << 1, bmdVideoConnectionOpticalSDI = 1 << 2, bmdVideoConnectionComponent = 1 << 3, bmdVideoConnectionComposite = 1 << 4, bmdVideoConnectionSVideo = 1 << 5, bmdVideoConnectionEthernet = 1 << 6, bmdVideoConnectionOpticalEthernet = 1 << 7 }; /* Enum BMDAudioConnection - Audio connection types */ typedef uint32_t BMDAudioConnection; enum _BMDAudioConnection { bmdAudioConnectionEmbedded = 1 << 0, bmdAudioConnectionAESEBU = 1 << 1, bmdAudioConnectionAnalog = 1 << 2, bmdAudioConnectionAnalogXLR = 1 << 3, bmdAudioConnectionAnalogRCA = 1 << 4, bmdAudioConnectionMicrophone = 1 << 5, bmdAudioConnectionHeadphones = 1 << 6 }; /* Enum BMDDeckControlConnection - Deck control connections */ typedef uint32_t BMDDeckControlConnection; enum _BMDDeckControlConnection { bmdDeckControlConnectionRS422Remote1 = 1 << 0, bmdDeckControlConnectionRS422Remote2 = 1 << 1 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkTimecode; /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ class BMD_PUBLIC IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; virtual HRESULT GetComponents (/* out */ uint8_t* hours, /* out */ uint8_t* minutes, /* out */ uint8_t* seconds, /* out */ uint8_t* frames) = 0; virtual HRESULT GetString (/* out */ const char** timecode) = 0; virtual BMDTimecodeFlags GetFlags (void) = 0; virtual HRESULT GetTimecodeUserBits (/* out */ BMDTimecodeUserBits* userBits) = 0; protected: virtual ~IDeckLinkTimecode () {} // call Release method to drop reference count }; /* Functions */ extern "C" { } #endif /* defined(__cplusplus) */ #endif /* defined(BMD_DECKLINKAPITYPES_H) */ mlt-7.38.0/src/modules/decklink/linux/LinuxCOM.h000664 000000 000000 00000007632 15172202314 021363 0ustar00rootroot000000 000000 /* -LICENSE-START- ** Copyright (c) 2009 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation (the ** "Software") to use, reproduce, display, distribute, sub-license, execute, ** and transmit the Software, and to prepare derivative works of the Software, ** and to permit third-parties to whom the Software is furnished to do so, in ** accordance with: ** ** (1) if the Software is obtained from Blackmagic Design, the End User License ** Agreement for the Software Development Kit ("EULA") available at ** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or ** ** (2) if the Software is obtained from any third party, such licensing terms ** as notified by that third party, ** ** and all subject to the following: ** ** (3) the copyright notices in the Software and this entire statement, ** including the above license grant, this restriction and the following ** disclaimer, must be included in all copies of the Software, in whole or in ** part, and all derivative works of the Software, unless such copies or ** derivative works are solely in the form of machine-executable object code ** generated by a source language processor. ** ** (4) 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** ** A copy of the Software is available free of charge at ** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. ** ** -LICENSE-END- */ #ifndef __LINUX_COM_H_ #define __LINUX_COM_H_ struct REFIID { unsigned char byte0; unsigned char byte1; unsigned char byte2; unsigned char byte3; unsigned char byte4; unsigned char byte5; unsigned char byte6; unsigned char byte7; unsigned char byte8; unsigned char byte9; unsigned char byte10; unsigned char byte11; unsigned char byte12; unsigned char byte13; unsigned char byte14; unsigned char byte15; }; typedef REFIID CFUUIDBytes; #define CFUUIDGetUUIDBytes(x) x typedef int HRESULT; typedef unsigned long ULONG; typedef void *LPVOID; #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) #define FAILED(Status) ((HRESULT)(Status)<0) #define IS_ERROR(Status) ((unsigned long)(Status) >> 31 == SEVERITY_ERROR) #define HRESULT_CODE(hr) ((hr) & 0xFFFF) #define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff) #define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1) #define SEVERITY_SUCCESS 0 #define SEVERITY_ERROR 1 #define MAKE_HRESULT(sev,fac,code) ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) #define S_OK ((HRESULT)0x00000000L) #define S_FALSE ((HRESULT)0x00000001L) #define E_UNEXPECTED ((HRESULT)0x8000FFFFL) #define E_NOTIMPL ((HRESULT)0x80000001L) #define E_OUTOFMEMORY ((HRESULT)0x80000002L) #define E_INVALIDARG ((HRESULT)0x80000003L) #define E_NOINTERFACE ((HRESULT)0x80000004L) #define E_POINTER ((HRESULT)0x80000005L) #define E_HANDLE ((HRESULT)0x80000006L) #define E_ABORT ((HRESULT)0x80000007L) #define E_FAIL ((HRESULT)0x80000008L) #define E_ACCESSDENIED ((HRESULT)0x80000009L) #define STDMETHODCALLTYPE #define IID_IUnknown (REFIID){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} #define IUnknownUUID IID_IUnknown #ifndef BMD_PUBLIC #define BMD_PUBLIC #endif #ifdef __cplusplus class BMD_PUBLIC IUnknown { public: virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0; virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; virtual ULONG STDMETHODCALLTYPE Release(void) = 0; }; #endif #endif mlt-7.38.0/src/modules/decklink/producer_decklink.cpp000664 000000 000000 00000111012 15172202314 022574 0ustar00rootroot000000 000000 /* * producer_decklink.c -- input from Blackmagic Design DeckLink * Copyright (C) 2011-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include struct copy_lines_sliced_desc { BMDPixelFormat in_fmt; mlt_image_format out_fmt; unsigned char *in_buffer, **out_buffers; int in_stride, *out_strides, w, h; }; #define READ_PIXELS(a, b, c) \ val = *v210++; \ *a++ = (val & 0x3FF) << 6; \ *b++ = ((val >> 10) & 0x3FF) << 6; \ *c++ = ((val >> 20) & 0x3FF) << 6; static int copy_lines_sliced_proc(int id, int idx, int jobs, void *cookie) { int c, H, Y, i; struct copy_lines_sliced_desc *ctx = (struct copy_lines_sliced_desc *) cookie; H = mlt_slices_size_slice(jobs, idx, ctx->h, &Y); if (ctx->in_fmt == bmdFormat10BitYUV) // bmdFormat10BitYUV -> mlt_image_yuv422p16 { for (i = 0; i < H; i++) { uint32_t val, *v210 = (uint32_t *) (ctx->in_buffer + (Y + i) * ctx->in_stride); uint16_t *y = (uint16_t *) (ctx->out_buffers[0] + (Y + i) * ctx->out_strides[0]), *u = (uint16_t *) (ctx->out_buffers[1] + (Y + i) * ctx->out_strides[1]), *v = (uint16_t *) (ctx->out_buffers[2] + (Y + i) * ctx->out_strides[2]); for (c = 0; c < ctx->w / 6; c++) { READ_PIXELS(u, y, v); READ_PIXELS(y, u, y); READ_PIXELS(v, y, u); READ_PIXELS(y, v, y); } } } else // bmdFormat8BitYUV -> mlt_image_yuv422 { if (ctx->out_strides[0] == ctx->in_stride) swab2(ctx->in_buffer + Y * ctx->in_stride, ctx->out_buffers[0] + Y * ctx->out_strides[0], H * ctx->in_stride); else for (i = 0; i < H; i++) swab2(ctx->in_buffer + (Y + i) * ctx->in_stride, ctx->out_buffers[0] + (Y + i) * ctx->out_strides[0], MIN(ctx->in_stride, ctx->out_strides[0])); } return 0; } static void copy_lines(BMDPixelFormat in_fmt, unsigned char *in_buffer, int in_stride, mlt_image_format out_fmt, unsigned char *out_buffers[4], int out_strides[4], int w, int h) { struct copy_lines_sliced_desc ctx = {in_fmt, out_fmt, in_buffer, out_buffers, in_stride, out_strides, w, h}; if (h == 1) copy_lines_sliced_proc(0, 0, 1, &ctx); else mlt_slices_run_normal(mlt_slices_count_normal(), copy_lines_sliced_proc, &ctx); } static void fill_line(mlt_image_format out_fmt, unsigned char *in[4], int strides[4], int pattern) { // TODO } class DeckLinkProducer : public IDeckLinkInputCallback { private: mlt_producer m_producer; IDeckLink *m_decklink; IDeckLinkInput *m_decklinkInput; mlt_deque m_queue; pthread_mutex_t m_mutex; pthread_cond_t m_condition; bool m_started; int m_dropped; bool m_isBuffering; int m_topFieldFirst; BMDPixelFormat m_pixel_format; mlt_colorspace m_colorspace; int m_vancLines; mlt_cache m_cache; bool m_reprio; BMDDisplayMode getDisplayMode(mlt_profile profile, int vancLines) { IDeckLinkDisplayModeIterator *iter = NULL; IDeckLinkDisplayMode *mode = NULL; BMDDisplayMode result = (BMDDisplayMode) bmdModeUnknown; if (m_decklinkInput->GetDisplayModeIterator(&iter) == S_OK) { while (!result && iter->Next(&mode) == S_OK) { int width = mode->GetWidth(); int height = mode->GetHeight(); BMDTimeValue duration; BMDTimeScale timescale; mode->GetFrameRate(&duration, ×cale); double fps = (double) timescale / duration; int p = mode->GetFieldDominance() == bmdProgressiveFrame; m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst; m_colorspace = (mode->GetFlags() & bmdDisplayModeColorspaceRec709) ? mlt_colorspace_bt709 : mlt_colorspace_bt601; mlt_log_verbose(getProducer(), "BMD mode %dx%d %.3f fps prog %d tff %d\n", width, height, fps, p, m_topFieldFirst); if (width == profile->width && p == profile->progressive && (height + vancLines == profile->height || (height == 486 && profile->height == 480 + vancLines)) && (int) fps == (int) mlt_profile_fps(profile)) result = mode->GetDisplayMode(); SAFE_RELEASE(mode); } SAFE_RELEASE(iter); } return result; } public: mlt_profile m_new_input; void setProducer(mlt_producer producer) { m_producer = producer; } mlt_producer getProducer() const { return m_producer; } DeckLinkProducer() { m_producer = NULL; m_decklink = NULL; m_decklinkInput = NULL; m_new_input = NULL; } virtual ~DeckLinkProducer() { if (m_queue) { stop(); mlt_deque_close(m_queue); pthread_mutex_destroy(&m_mutex); pthread_cond_destroy(&m_condition); mlt_cache_close(m_cache); } SAFE_RELEASE(m_decklinkInput); SAFE_RELEASE(m_decklink); } bool open(unsigned card = 0) { IDeckLinkIterator *decklinkIterator = NULL; try { #ifdef _WIN32 HRESULT result = CoInitialize(NULL); if (FAILED(result)) throw "COM initialization failed"; result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void **) &decklinkIterator); if (FAILED(result)) throw "The DeckLink drivers are not installed."; #else decklinkIterator = CreateDeckLinkIteratorInstance(); if (!decklinkIterator) throw "The DeckLink drivers are not installed."; #endif // Connect to the Nth DeckLink instance for (unsigned i = 0; decklinkIterator->Next(&m_decklink) == S_OK; i++) { if (i == card) break; else SAFE_RELEASE(m_decklink); } SAFE_RELEASE(decklinkIterator); if (!m_decklink) throw "DeckLink card not found."; // Get the input interface if (m_decklink->QueryInterface(IID_IDeckLinkInput, (void **) &m_decklinkInput) != S_OK) throw "No DeckLink cards support input."; // Provide this class as a delegate to the input callback m_decklinkInput->SetCallback(this); // Initialize other members pthread_mutex_init(&m_mutex, NULL); pthread_cond_init(&m_condition, NULL); m_queue = mlt_deque_init(); m_started = false; m_dropped = 0; m_isBuffering = true; m_cache = mlt_cache_init(); // 3 covers YADIF and increasing framerate use cases mlt_cache_set_size(m_cache, 3); } catch (const char *error) { SAFE_RELEASE(m_decklinkInput); SAFE_RELEASE(m_decklink); mlt_log_error(getProducer(), "%s\n", error); return false; } return true; } bool start(mlt_profile profile = 0) { if (m_started) return false; try { // Initialize some members m_vancLines = mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "vanc"); if (m_vancLines == -1) m_vancLines = profile->height <= 512 ? 26 : 32; if (!profile) profile = mlt_service_profile(MLT_PRODUCER_SERVICE(getProducer())); // Get the display mode BMDDisplayMode displayMode = getDisplayMode(profile, m_vancLines); if (displayMode == (BMDDisplayMode) bmdModeUnknown) { mlt_log_info(getProducer(), "profile = %dx%d %f fps %s\n", profile->width, profile->height, mlt_profile_fps(profile), profile->progressive ? "progressive" : "interlace"); throw "Profile is not compatible with decklink."; } // Determine if supports input format detection #ifdef _WIN32 BOOL doesDetectFormat = FALSE; #else bool doesDetectFormat = false; #endif IDeckLinkProfileAttributes *decklinkAttributes = 0; if (m_decklink->QueryInterface(IID_IDeckLinkProfileAttributes, (void **) &decklinkAttributes) == S_OK) { if (decklinkAttributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &doesDetectFormat) != S_OK) doesDetectFormat = false; SAFE_RELEASE(decklinkAttributes); } mlt_log_verbose(getProducer(), "%s format detection\n", doesDetectFormat ? "supports" : "does not support"); // Enable video capture m_pixel_format = (10 == mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "bitdepth")) ? bmdFormat10BitYUV : bmdFormat8BitYUV; BMDVideoInputFlags flags = doesDetectFormat ? bmdVideoInputEnableFormatDetection : bmdVideoInputFlagDefault; if (S_OK != m_decklinkInput->EnableVideoInput(displayMode, m_pixel_format, flags)) throw "Failed to enable video capture."; // Enable audio capture BMDAudioSampleRate sampleRate = bmdAudioSampleRate48kHz; BMDAudioSampleType sampleType = bmdAudioSampleType16bitInteger; int channels = mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "channels"); if (S_OK != m_decklinkInput->EnableAudioInput(sampleRate, sampleType, channels)) throw "Failed to enable audio capture."; // Start capture m_dropped = 0; mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(getProducer()), "dropped", m_dropped); m_started = m_decklinkInput->StartStreams() == S_OK; if (!m_started) throw "Failed to start capture."; } catch (const char *error) { m_decklinkInput->DisableVideoInput(); mlt_log_error(getProducer(), "%s\n", error); return false; } return true; } void stop() { if (!m_started) return; m_started = false; // Release the wait in getFrame pthread_mutex_lock(&m_mutex); pthread_cond_broadcast(&m_condition); pthread_mutex_unlock(&m_mutex); m_decklinkInput->StopStreams(); m_decklinkInput->DisableVideoInput(); m_decklinkInput->DisableAudioInput(); // Cleanup queue pthread_mutex_lock(&m_mutex); while (mlt_frame frame = (mlt_frame) mlt_deque_pop_back(m_queue)) mlt_frame_close(frame); pthread_mutex_unlock(&m_mutex); } mlt_frame getFrame() { struct timeval now; struct timespec tm; double fps = mlt_producer_get_fps(getProducer()); mlt_position position = mlt_producer_position(getProducer()); mlt_frame frame = mlt_cache_get_frame(m_cache, position); // Allow the buffer to fill to the requested initial buffer level. if (m_isBuffering) { int prefill = mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "prefill"); int buffer = mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "buffer"); m_isBuffering = false; prefill = prefill > buffer ? buffer : prefill; pthread_mutex_lock(&m_mutex); while (mlt_deque_count(m_queue) < prefill) { // Wait up to buffer/fps seconds gettimeofday(&now, NULL); long usec = now.tv_sec * 1000000 + now.tv_usec; usec += 1000000 * buffer / fps; tm.tv_sec = usec / 1000000; tm.tv_nsec = (usec % 1000000) * 1000; if (pthread_cond_timedwait(&m_condition, &m_mutex, &tm)) break; } pthread_mutex_unlock(&m_mutex); } if (!frame) { // Wait if queue is empty pthread_mutex_lock(&m_mutex); while (mlt_deque_count(m_queue) < 1) { // Wait up to twice frame duration gettimeofday(&now, NULL); long usec = now.tv_sec * 1000000 + now.tv_usec; usec += 2000000 / fps; tm.tv_sec = usec / 1000000; tm.tv_nsec = (usec % 1000000) * 1000; if (pthread_cond_timedwait(&m_condition, &m_mutex, &tm)) // Stop waiting if error (timed out) break; } frame = (mlt_frame) mlt_deque_pop_front(m_queue); pthread_mutex_unlock(&m_mutex); // add to cache if (frame) { mlt_frame_set_position(frame, position); mlt_cache_put_frame(m_cache, frame); } } // Set frame timestamp and properties if (frame) { mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(getProducer())); mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set_int(properties, "progressive", profile->progressive); mlt_properties_set_int(properties, "meta.media.progressive", profile->progressive); mlt_properties_set_int(properties, "top_field_first", m_topFieldFirst); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(properties, "meta.media.sample_aspect_num", profile->sample_aspect_num); mlt_properties_set_int(properties, "meta.media.sample_aspect_den", profile->sample_aspect_den); mlt_properties_set_int(properties, "meta.media.frame_rate_num", profile->frame_rate_num); mlt_properties_set_int(properties, "meta.media.frame_rate_den", profile->frame_rate_den); mlt_properties_set_int(properties, "width", profile->width); mlt_properties_set_int(properties, "meta.media.width", profile->width); mlt_properties_set_int(properties, "height", profile->height); mlt_properties_set_int(properties, "meta.media.height", profile->height); mlt_properties_set_int(properties, "format", (m_pixel_format == bmdFormat8BitYUV) ? mlt_image_yuv422 : mlt_image_yuv422p16); mlt_properties_set_int(properties, "colorspace", m_colorspace); mlt_properties_set_int(properties, "meta.media.colorspace", m_colorspace); mlt_properties_set_int(properties, "audio_frequency", 48000); mlt_properties_set_int(properties, "audio_channels", mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "channels")); } else mlt_log_warning(getProducer(), "buffer underrun\n"); return frame; } // *** DeckLink API implementation of IDeckLinkInputCallback *** // // IUnknown needs only a dummy implementation virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; } virtual ULONG STDMETHODCALLTYPE AddRef() { return 1; } virtual ULONG STDMETHODCALLTYPE Release() { return 1; } /************************* DeckLink API Delegate Methods *****************************/ virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame *video, IDeckLinkAudioInputPacket *audio) { mlt_frame frame = NULL; struct timeval arrived; gettimeofday(&arrived, NULL); if (!m_reprio) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(getProducer()); if (mlt_properties_get(properties, "priority")) { int r; pthread_t thread; pthread_attr_t tattr; struct sched_param param; pthread_attr_init(&tattr); pthread_attr_setschedpolicy(&tattr, SCHED_FIFO); if (!strcmp("max", mlt_properties_get(properties, "priority"))) param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1; else if (!strcmp("min", mlt_properties_get(properties, "priority"))) param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1; else param.sched_priority = mlt_properties_get_int(properties, "priority"); pthread_attr_setschedparam(&tattr, ¶m); thread = pthread_self(); r = pthread_setschedparam(thread, SCHED_FIFO, ¶m); if (r) mlt_log_verbose(getProducer(), "VideoInputFrameArrived: pthread_setschedparam returned %d\n", r); else mlt_log_verbose(getProducer(), "VideoInputFrameArrived: param.sched_priority=%d\n", param.sched_priority); }; m_reprio = true; }; if (mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "preview") && mlt_producer_get_speed(getProducer()) == 0.0 && !mlt_deque_count(m_queue)) { pthread_cond_broadcast(&m_condition); return S_OK; } // Copy video if (video) { IDeckLinkTimecode *timecode = 0; if (!(video->GetFlags() & bmdFrameHasNoInputSource)) { int vitc_in = mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "vitc_in"); if (vitc_in && (S_OK == video->GetTimecode(bmdTimecodeRP188Any, &timecode) || S_OK == video->GetTimecode(bmdTimecodeVITC, &timecode)) && timecode) { int vitc = timecode->GetBCD(); SAFE_RELEASE(timecode); mlt_log_verbose(getProducer(), "VideoInputFrameArrived: vitc=%.8X vitc_in=%.8X\n", vitc, vitc_in); if (vitc < vitc_in) { pthread_cond_broadcast(&m_condition); return S_OK; } mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(getProducer()), "vitc_in", 0); } void *buffer; int image_strides[4]; unsigned char *image_buffers[4]; mlt_image_format fmt = (m_pixel_format == bmdFormat8BitYUV) ? mlt_image_yuv422 : mlt_image_yuv422p16; int size = mlt_image_format_size(fmt, video->GetWidth(), video->GetHeight() + m_vancLines, NULL); void *image = mlt_pool_alloc(size); mlt_image_format_planes(fmt, video->GetWidth(), video->GetHeight() + m_vancLines, image, image_buffers, image_strides); // Capture VANC if (m_vancLines > 0) { IDeckLinkVideoFrameAncillary *vanc = 0; if (video->GetAncillaryData(&vanc) == S_OK && vanc) { for (int i = 1; i < m_vancLines + 1; i++) { unsigned char *out[4] = {image_buffers[0] + (i - 1) * image_strides[0], image_buffers[1] + (i - 1) * image_strides[1], image_buffers[2] + (i - 1) * image_strides[2], image_buffers[3] + (i - 1) * image_strides[3]}; if (vanc->GetBufferForVerticalBlankingLine(i, &buffer) == S_OK) copy_lines(m_pixel_format, (unsigned char *) buffer, video->GetRowBytes(), fmt, out, image_strides, video->GetWidth(), 1); else { fill_line(fmt, out, image_strides, 0); mlt_log_debug(getProducer(), "failed capture vanc line %d\n", i); } } SAFE_RELEASE(vanc); } } // Capture image IDeckLinkVideoBuffer *videoBuffer = NULL; if (video->QueryInterface(IID_IDeckLinkVideoBuffer, (void **) &videoBuffer) == S_OK) { if (videoBuffer->StartAccess(bmdBufferAccessRead) == S_OK) { videoBuffer->GetBytes(&buffer); videoBuffer->EndAccess(bmdBufferAccessRead); } videoBuffer->Release(); videoBuffer = NULL; } if (image && buffer) { unsigned char *out[4] = {image_buffers[0] + m_vancLines * image_strides[0], image_buffers[1] + m_vancLines * image_strides[1], image_buffers[2] + m_vancLines * image_strides[2], image_buffers[3] + m_vancLines * image_strides[3]}; copy_lines(m_pixel_format, (unsigned char *) buffer, video->GetRowBytes(), fmt, (unsigned char **) out, image_strides, video->GetWidth(), video->GetHeight()); frame = mlt_frame_init(MLT_PRODUCER_SERVICE(getProducer())); mlt_frame_set_image(frame, (uint8_t *) image, size, mlt_pool_release); } else if (image) { mlt_log_verbose(getProducer(), "no video image\n"); mlt_pool_release(image); } } else { mlt_log_verbose(getProducer(), "no signal\n"); } // Get timecode if ((S_OK == video->GetTimecode(bmdTimecodeRP188Any, &timecode) || S_OK == video->GetTimecode(bmdTimecodeVITC, &timecode)) && timecode) { DLString timecodeString = 0; if (timecode->GetString(&timecodeString) == S_OK) { char *s = getCString(timecodeString); mlt_properties_set(MLT_FRAME_PROPERTIES(frame), "meta.attr.vitc.markup", s); mlt_log_debug(getProducer(), "timecode %s\n", s); freeCString(s); } freeDLString(timecodeString); SAFE_RELEASE(timecode); } } else { mlt_log_verbose(getProducer(), "no video\n"); } // Copy audio if (frame && audio) { int channels = mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "channels"); int size = audio->GetSampleFrameCount() * channels * sizeof(int16_t); mlt_audio_format format = mlt_audio_s16; void *pcm = mlt_pool_alloc(size); void *buffer = 0; audio->GetBytes(&buffer); if (buffer) { memcpy(pcm, buffer, size); mlt_frame_set_audio(frame, pcm, format, size, mlt_pool_release); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_samples", audio->GetSampleFrameCount()); } else { mlt_log_verbose(getProducer(), "no audio samples\n"); mlt_pool_release(pcm); } } else { mlt_log_verbose(getProducer(), "no audio\n"); } // Put frame in queue if (frame) { mlt_properties_set_int64(MLT_FRAME_PROPERTIES(frame), "arrived", arrived.tv_sec * 1000000LL + arrived.tv_usec); int queueMax = mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(getProducer()), "buffer"); pthread_mutex_lock(&m_mutex); if (mlt_deque_count(m_queue) < queueMax) { mlt_deque_push_back(m_queue, frame); pthread_cond_broadcast(&m_condition); } else { mlt_frame_close(frame); mlt_properties_set_int(MLT_PRODUCER_PROPERTIES(getProducer()), "dropped", ++m_dropped); mlt_log_warning(getProducer(), "buffer overrun, frame dropped %d\n", m_dropped); } pthread_mutex_unlock(&m_mutex); } return S_OK; } virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode, BMDDetectedVideoInputFormatFlags flags) { mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(getProducer())); if (events & bmdVideoInputDisplayModeChanged) { BMDTimeValue duration; BMDTimeScale timescale; mode->GetFrameRate(&duration, ×cale); profile->width = mode->GetWidth(); profile->height = mode->GetHeight() + m_vancLines; profile->frame_rate_num = timescale; profile->frame_rate_den = duration; if (profile->width == 720) { if (profile->height == 576) { profile->sample_aspect_num = 16; profile->sample_aspect_den = 15; } else { profile->sample_aspect_num = 8; profile->sample_aspect_den = 9; } profile->display_aspect_num = 4; profile->display_aspect_den = 3; } else { profile->sample_aspect_num = 1; profile->sample_aspect_den = 1; profile->display_aspect_num = 16; profile->display_aspect_den = 9; } free(profile->description); profile->description = strdup("decklink"); mlt_log_verbose(getProducer(), "format changed %dx%d %.3f fps\n", profile->width, profile->height, (double) profile->frame_rate_num / profile->frame_rate_den); m_new_input = profile; } if (events & bmdVideoInputFieldDominanceChanged) { profile->progressive = mode->GetFieldDominance() == bmdProgressiveFrame; m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst; mlt_log_verbose(getProducer(), "field dominance changed prog %d tff %d\n", profile->progressive, m_topFieldFirst); } if (events & bmdVideoInputColorspaceChanged) { profile->colorspace = m_colorspace = (mode->GetFlags() & bmdDisplayModeColorspaceRec709) ? mlt_colorspace_bt709 : mlt_colorspace_bt601; mlt_log_verbose(getProducer(), "colorspace changed %d\n", profile->colorspace); } return S_OK; } }; static int get_audio(mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { return mlt_frame_get_audio(frame, (void **) buffer, format, frequency, channels, samples); } static int get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { return mlt_frame_get_image(frame, buffer, format, width, height, writable); } static int get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { DeckLinkProducer *decklink = (DeckLinkProducer *) producer->child; mlt_position pos = mlt_producer_position(producer); mlt_position end = mlt_producer_get_playtime(producer); end = (mlt_producer_get_length(producer) < end ? mlt_producer_get_length(producer) : end) - 1; if (decklink && decklink->m_new_input) { decklink->m_new_input = NULL; decklink->stop(); decklink->start(decklink->m_new_input); } // Re-open if needed if (!decklink && pos < end) { producer->child = decklink = new DeckLinkProducer(); decklink->setProducer(producer); decklink->open(mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(producer), "resource")); } // Start if needed if (decklink) { decklink->start(mlt_service_profile(MLT_PRODUCER_SERVICE(producer))); // Get the next frame from the decklink object if ((*frame = decklink->getFrame())) { // Add audio and video getters mlt_frame_push_audio(*frame, (void *) get_audio); mlt_frame_push_get_image(*frame, get_image); } } if (!*frame) *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // Calculate the next timecode mlt_producer_prepare_next(producer); // Close DeckLink if at end if (pos >= end && decklink) { decklink->stop(); delete decklink; producer->child = NULL; } return 0; } static void producer_close(mlt_producer producer) { delete (DeckLinkProducer *) producer->child; producer->close = NULL; mlt_producer_close(producer); } extern "C" { // Listen for the list_devices property to be set static void on_property_changed(void *, mlt_properties properties, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); IDeckLinkIterator *decklinkIterator = NULL; IDeckLink *decklink = NULL; IDeckLinkInput *decklinkInput = NULL; int i = 0; if (name && !strcmp(name, "list_devices")) mlt_event_block((mlt_event) mlt_properties_get_data(properties, "list-devices-event", NULL)); else return; #ifdef _WIN32 if (FAILED(CoInitialize(NULL))) return; if (FAILED(CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void **) &decklinkIterator))) return; #else if (!(decklinkIterator = CreateDeckLinkIteratorInstance())) return; #endif for (; decklinkIterator->Next(&decklink) == S_OK; i++) { if (decklink->QueryInterface(IID_IDeckLinkInput, (void **) &decklinkInput) == S_OK) { DLString name = NULL; if (decklink->GetModelName(&name) == S_OK) { char *name_cstr = getCString(name); const char *format = "device.%d"; char *key = (char *) calloc(1, strlen(format) + 17); sprintf(key, format, i); mlt_properties_set(properties, key, name_cstr); free(key); freeDLString(name); freeCString(name_cstr); } SAFE_RELEASE(decklinkInput); } SAFE_RELEASE(decklink); } SAFE_RELEASE(decklinkIterator); mlt_properties_set_int(properties, "devices", i); } /** Initialise the producer. */ mlt_producer producer_decklink_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Allocate the producer DeckLinkProducer *decklink = new DeckLinkProducer(); mlt_producer producer = (mlt_producer) calloc(1, sizeof(*producer)); // If allocated and initializes if (decklink && !mlt_producer_init(producer, decklink)) { // Extract resource (card) from arg, removing path prefix, if any. // (modules such as melted may pass arg with root_dir prefix) char *arg_dup = strdup(arg ? arg : ""); const char *resource = strchr(arg_dup, '/') ? strrchr(arg_dup, '/') + 1 : arg_dup; // Handle empty string resource (arg supplied as "" or "/some/path/") resource = strlen(resource) ? resource : "0"; if (decklink->open(atoi(resource))) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); // Close DeckLink and defer re-open to get_frame delete decklink; producer->child = NULL; // Set callbacks producer->close = (mlt_destructor) producer_close; producer->get_frame = get_frame; // Set properties mlt_properties_set(properties, "resource", resource); mlt_properties_set_int(properties, "channels", 2); mlt_properties_set_int(properties, "buffer", 25); mlt_properties_set_int(properties, "prefill", 25); // These properties effectively make it infinite. mlt_properties_set_int(properties, "length", INT_MAX); mlt_properties_set_int(properties, "out", INT_MAX - 1); mlt_properties_set(properties, "eof", "loop"); mlt_event event = mlt_events_listen(properties, properties, "property-changed", (mlt_listener) on_property_changed); mlt_properties_set_data(properties, "list-devices-event", event, 0, NULL, NULL); } free(arg_dup); } return producer; } } mlt-7.38.0/src/modules/decklink/producer_decklink.yml000664 000000 000000 00000006602 15172202314 022623 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: decklink title: Blackmagic Design DeckLink Capture version: 3 copyright: Copyright (C) 2011-2025 Meltytech, LLC license: LGPL language: en creator: Dan Dennedy tags: - Audio - Video description: > Capture video and audio using Blackmagic Design DeckLink SDI or Intensity HDMI cards. notes: > Please ensure that you use a MLT profile that is compatible with a broadcast standard which the card you are using supports. If you must use an interlaced profile but wish to deinterlace or scale the input, then you must use the consumer producer, e.g.: melt -profile square_pal consumer:decklink: profile=dv_pal bugs: - It is incompatible with the yadif deinterlacer. - Transport controls such as seeking has no affect. - External deck control is not implemented. - Only 8-bit Y'CbCr is supported at this time. parameters: - identifier: resource argument: yes title: Card type: integer default: 0 minimum: 0 widget: spinner - identifier: channels title: Audio channels type: integer default: 2 minimum: 2 maximum: 16 widget: spinner - identifier: buffer title: Maximum buffer description: > There is a queue of frames between this plugin and its consumer. If the consumer has a little, intermittent delay then it reduces the risk of dropping a frame. However, this provides a maximum number of frames that can be buffered to prevent consuming memory unbounded in the case of frequent or sustained delays. type: integer default: 25 minimum: 0 unit: frames widget: spinner - identifier: prefill title: Initial buffer description: Initially fill the buffer with a number of frames. type: integer default: 25 minimum: 0 unit: frames widget: spinner - identifier: vanc title: Vertical ancillary capture description: > Captures vertical ancillary data as image data and places it at the top of the visible/active image. You can either set the number of lines to capture or use -1 for automatic (32 lines) mode. type: integer minimum: -1 default: 0 unit: lines widget: spinner - identifier: preview title: Enable preview description: Support preview monitoring when paused (speed = 0). type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: devices title: Number of devices type: integer readonly: yes minimum: 0 - identifier: device.* title: Device model description: The model name of each device that accepts input. type: string readonly: yes - identifier: priority title: Thread priority description: Set the DeckLink thread's scheduling class to realtime and its priority. type: integer minimum: 1 maximum: 99 default: 20 - identifier: vitc_in title: Start timecode type: integer description: > The vertical interval timecode (VITC) in binary-coded decimal (BCD) format. It skips frames that has VITC timecode less then specified. After reaching first frame with timecode greater or equal then specified this property is reset to zero. - identifier: bitdepth title: Bitdepth for capturing description: Enable capturing in 10-bit native SDI signal type: integer values: - 8 # 8-bit data - 10 # 10-bit data mlt-7.38.0/src/modules/decklink/win/000775 000000 000000 00000000000 15172202314 017202 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/decklink/win/DeckLinkAPI_h.h000664 000000 000000 00002335666 15172202314 021725 0ustar00rootroot000000 000000 /* this ALWAYS GENERATED file contains the definitions for the interfaces */ /* File created by MIDL compiler version 8.01.0628 */ /* at Mon Jan 18 19:14:07 2038 */ /* Compiler settings for ..\..\..\Win\include\DeckLinkAPI.idl: Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0628 protocol : all , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: __declspec(uuid()), __declspec(selectany), __declspec(novtable) DECLSPEC_UUID(), MIDL_INTERFACE() */ /* @@MIDL_FILE_HEADING( ) */ /* verify that the version is high enough to compile this file*/ #ifndef __REQUIRED_RPCNDR_H_VERSION__ #define __REQUIRED_RPCNDR_H_VERSION__ 475 #endif #include "rpc.h" #include "rpcndr.h" #ifndef __RPCNDR_H_VERSION__ #error this stub requires an updated version of #endif /* __RPCNDR_H_VERSION__ */ #ifndef __DeckLinkAPI_h__ #define __DeckLinkAPI_h__ #if defined(_MSC_VER) && (_MSC_VER >= 1020) #pragma once #endif #ifndef DECLSPEC_XFGVIRT #if defined(_CONTROL_FLOW_GUARD_XFG) #define DECLSPEC_XFGVIRT(base, func) __declspec(xfg_virtual(base, func)) #else #define DECLSPEC_XFGVIRT(base, func) #endif #endif /* Forward Declarations */ #ifndef __IDeckLinkTimecode_FWD_DEFINED__ #define __IDeckLinkTimecode_FWD_DEFINED__ typedef interface IDeckLinkTimecode IDeckLinkTimecode; #endif /* __IDeckLinkTimecode_FWD_DEFINED__ */ #ifndef __IDeckLinkDisplayModeIterator_FWD_DEFINED__ #define __IDeckLinkDisplayModeIterator_FWD_DEFINED__ typedef interface IDeckLinkDisplayModeIterator IDeckLinkDisplayModeIterator; #endif /* __IDeckLinkDisplayModeIterator_FWD_DEFINED__ */ #ifndef __IDeckLinkDisplayMode_FWD_DEFINED__ #define __IDeckLinkDisplayMode_FWD_DEFINED__ typedef interface IDeckLinkDisplayMode IDeckLinkDisplayMode; #endif /* __IDeckLinkDisplayMode_FWD_DEFINED__ */ #ifndef __IDeckLink_FWD_DEFINED__ #define __IDeckLink_FWD_DEFINED__ typedef interface IDeckLink IDeckLink; #endif /* __IDeckLink_FWD_DEFINED__ */ #ifndef __IDeckLinkConfiguration_FWD_DEFINED__ #define __IDeckLinkConfiguration_FWD_DEFINED__ typedef interface IDeckLinkConfiguration IDeckLinkConfiguration; #endif /* __IDeckLinkConfiguration_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderConfiguration_FWD_DEFINED__ #define __IDeckLinkEncoderConfiguration_FWD_DEFINED__ typedef interface IDeckLinkEncoderConfiguration IDeckLinkEncoderConfiguration; #endif /* __IDeckLinkEncoderConfiguration_FWD_DEFINED__ */ #ifndef __IDeckLinkDeckControlStatusCallback_FWD_DEFINED__ #define __IDeckLinkDeckControlStatusCallback_FWD_DEFINED__ typedef interface IDeckLinkDeckControlStatusCallback IDeckLinkDeckControlStatusCallback; #endif /* __IDeckLinkDeckControlStatusCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkDeckControl_FWD_DEFINED__ #define __IDeckLinkDeckControl_FWD_DEFINED__ typedef interface IDeckLinkDeckControl IDeckLinkDeckControl; #endif /* __IDeckLinkDeckControl_FWD_DEFINED__ */ #ifndef __IBMDStreamingDeviceNotificationCallback_FWD_DEFINED__ #define __IBMDStreamingDeviceNotificationCallback_FWD_DEFINED__ typedef interface IBMDStreamingDeviceNotificationCallback IBMDStreamingDeviceNotificationCallback; #endif /* __IBMDStreamingDeviceNotificationCallback_FWD_DEFINED__ */ #ifndef __IBMDStreamingH264InputCallback_FWD_DEFINED__ #define __IBMDStreamingH264InputCallback_FWD_DEFINED__ typedef interface IBMDStreamingH264InputCallback IBMDStreamingH264InputCallback; #endif /* __IBMDStreamingH264InputCallback_FWD_DEFINED__ */ #ifndef __IBMDStreamingDiscovery_FWD_DEFINED__ #define __IBMDStreamingDiscovery_FWD_DEFINED__ typedef interface IBMDStreamingDiscovery IBMDStreamingDiscovery; #endif /* __IBMDStreamingDiscovery_FWD_DEFINED__ */ #ifndef __IBMDStreamingVideoEncodingMode_FWD_DEFINED__ #define __IBMDStreamingVideoEncodingMode_FWD_DEFINED__ typedef interface IBMDStreamingVideoEncodingMode IBMDStreamingVideoEncodingMode; #endif /* __IBMDStreamingVideoEncodingMode_FWD_DEFINED__ */ #ifndef __IBMDStreamingMutableVideoEncodingMode_FWD_DEFINED__ #define __IBMDStreamingMutableVideoEncodingMode_FWD_DEFINED__ typedef interface IBMDStreamingMutableVideoEncodingMode IBMDStreamingMutableVideoEncodingMode; #endif /* __IBMDStreamingMutableVideoEncodingMode_FWD_DEFINED__ */ #ifndef __IBMDStreamingVideoEncodingModePresetIterator_FWD_DEFINED__ #define __IBMDStreamingVideoEncodingModePresetIterator_FWD_DEFINED__ typedef interface IBMDStreamingVideoEncodingModePresetIterator IBMDStreamingVideoEncodingModePresetIterator; #endif /* __IBMDStreamingVideoEncodingModePresetIterator_FWD_DEFINED__ */ #ifndef __IBMDStreamingDeviceInput_FWD_DEFINED__ #define __IBMDStreamingDeviceInput_FWD_DEFINED__ typedef interface IBMDStreamingDeviceInput IBMDStreamingDeviceInput; #endif /* __IBMDStreamingDeviceInput_FWD_DEFINED__ */ #ifndef __IBMDStreamingH264NALPacket_FWD_DEFINED__ #define __IBMDStreamingH264NALPacket_FWD_DEFINED__ typedef interface IBMDStreamingH264NALPacket IBMDStreamingH264NALPacket; #endif /* __IBMDStreamingH264NALPacket_FWD_DEFINED__ */ #ifndef __IBMDStreamingAudioPacket_FWD_DEFINED__ #define __IBMDStreamingAudioPacket_FWD_DEFINED__ typedef interface IBMDStreamingAudioPacket IBMDStreamingAudioPacket; #endif /* __IBMDStreamingAudioPacket_FWD_DEFINED__ */ #ifndef __IBMDStreamingMPEG2TSPacket_FWD_DEFINED__ #define __IBMDStreamingMPEG2TSPacket_FWD_DEFINED__ typedef interface IBMDStreamingMPEG2TSPacket IBMDStreamingMPEG2TSPacket; #endif /* __IBMDStreamingMPEG2TSPacket_FWD_DEFINED__ */ #ifndef __IBMDStreamingH264NALParser_FWD_DEFINED__ #define __IBMDStreamingH264NALParser_FWD_DEFINED__ typedef interface IBMDStreamingH264NALParser IBMDStreamingH264NALParser; #endif /* __IBMDStreamingH264NALParser_FWD_DEFINED__ */ #ifndef __CBMDStreamingDiscovery_FWD_DEFINED__ #define __CBMDStreamingDiscovery_FWD_DEFINED__ #ifdef __cplusplus typedef class CBMDStreamingDiscovery CBMDStreamingDiscovery; #else typedef struct CBMDStreamingDiscovery CBMDStreamingDiscovery; #endif /* __cplusplus */ #endif /* __CBMDStreamingDiscovery_FWD_DEFINED__ */ #ifndef __CBMDStreamingH264NALParser_FWD_DEFINED__ #define __CBMDStreamingH264NALParser_FWD_DEFINED__ #ifdef __cplusplus typedef class CBMDStreamingH264NALParser CBMDStreamingH264NALParser; #else typedef struct CBMDStreamingH264NALParser CBMDStreamingH264NALParser; #endif /* __cplusplus */ #endif /* __CBMDStreamingH264NALParser_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoOutputCallback_FWD_DEFINED__ #define __IDeckLinkVideoOutputCallback_FWD_DEFINED__ typedef interface IDeckLinkVideoOutputCallback IDeckLinkVideoOutputCallback; #endif /* __IDeckLinkVideoOutputCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkInputCallback_FWD_DEFINED__ #define __IDeckLinkInputCallback_FWD_DEFINED__ typedef interface IDeckLinkInputCallback IDeckLinkInputCallback; #endif /* __IDeckLinkInputCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderInputCallback_FWD_DEFINED__ #define __IDeckLinkEncoderInputCallback_FWD_DEFINED__ typedef interface IDeckLinkEncoderInputCallback IDeckLinkEncoderInputCallback; #endif /* __IDeckLinkEncoderInputCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoBufferAllocator_FWD_DEFINED__ #define __IDeckLinkVideoBufferAllocator_FWD_DEFINED__ typedef interface IDeckLinkVideoBufferAllocator IDeckLinkVideoBufferAllocator; #endif /* __IDeckLinkVideoBufferAllocator_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoBufferAllocatorProvider_FWD_DEFINED__ #define __IDeckLinkVideoBufferAllocatorProvider_FWD_DEFINED__ typedef interface IDeckLinkVideoBufferAllocatorProvider IDeckLinkVideoBufferAllocatorProvider; #endif /* __IDeckLinkVideoBufferAllocatorProvider_FWD_DEFINED__ */ #ifndef __IDeckLinkAudioOutputCallback_FWD_DEFINED__ #define __IDeckLinkAudioOutputCallback_FWD_DEFINED__ typedef interface IDeckLinkAudioOutputCallback IDeckLinkAudioOutputCallback; #endif /* __IDeckLinkAudioOutputCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkIterator_FWD_DEFINED__ #define __IDeckLinkIterator_FWD_DEFINED__ typedef interface IDeckLinkIterator IDeckLinkIterator; #endif /* __IDeckLinkIterator_FWD_DEFINED__ */ #ifndef __IDeckLinkAPIInformation_FWD_DEFINED__ #define __IDeckLinkAPIInformation_FWD_DEFINED__ typedef interface IDeckLinkAPIInformation IDeckLinkAPIInformation; #endif /* __IDeckLinkAPIInformation_FWD_DEFINED__ */ #ifndef __IDeckLinkIPFlowAttributes_FWD_DEFINED__ #define __IDeckLinkIPFlowAttributes_FWD_DEFINED__ typedef interface IDeckLinkIPFlowAttributes IDeckLinkIPFlowAttributes; #endif /* __IDeckLinkIPFlowAttributes_FWD_DEFINED__ */ #ifndef __IDeckLinkIPFlowStatus_FWD_DEFINED__ #define __IDeckLinkIPFlowStatus_FWD_DEFINED__ typedef interface IDeckLinkIPFlowStatus IDeckLinkIPFlowStatus; #endif /* __IDeckLinkIPFlowStatus_FWD_DEFINED__ */ #ifndef __IDeckLinkIPFlowSetting_FWD_DEFINED__ #define __IDeckLinkIPFlowSetting_FWD_DEFINED__ typedef interface IDeckLinkIPFlowSetting IDeckLinkIPFlowSetting; #endif /* __IDeckLinkIPFlowSetting_FWD_DEFINED__ */ #ifndef __IDeckLinkIPFlow_FWD_DEFINED__ #define __IDeckLinkIPFlow_FWD_DEFINED__ typedef interface IDeckLinkIPFlow IDeckLinkIPFlow; #endif /* __IDeckLinkIPFlow_FWD_DEFINED__ */ #ifndef __IDeckLinkIPFlowIterator_FWD_DEFINED__ #define __IDeckLinkIPFlowIterator_FWD_DEFINED__ typedef interface IDeckLinkIPFlowIterator IDeckLinkIPFlowIterator; #endif /* __IDeckLinkIPFlowIterator_FWD_DEFINED__ */ #ifndef __IDeckLinkOutput_FWD_DEFINED__ #define __IDeckLinkOutput_FWD_DEFINED__ typedef interface IDeckLinkOutput IDeckLinkOutput; #endif /* __IDeckLinkOutput_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_FWD_DEFINED__ #define __IDeckLinkInput_FWD_DEFINED__ typedef interface IDeckLinkInput IDeckLinkInput; #endif /* __IDeckLinkInput_FWD_DEFINED__ */ #ifndef __IDeckLinkIPExtensions_FWD_DEFINED__ #define __IDeckLinkIPExtensions_FWD_DEFINED__ typedef interface IDeckLinkIPExtensions IDeckLinkIPExtensions; #endif /* __IDeckLinkIPExtensions_FWD_DEFINED__ */ #ifndef __IDeckLinkHDMIInputEDID_FWD_DEFINED__ #define __IDeckLinkHDMIInputEDID_FWD_DEFINED__ typedef interface IDeckLinkHDMIInputEDID IDeckLinkHDMIInputEDID; #endif /* __IDeckLinkHDMIInputEDID_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderInput_FWD_DEFINED__ #define __IDeckLinkEncoderInput_FWD_DEFINED__ typedef interface IDeckLinkEncoderInput IDeckLinkEncoderInput; #endif /* __IDeckLinkEncoderInput_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoBuffer_FWD_DEFINED__ #define __IDeckLinkVideoBuffer_FWD_DEFINED__ typedef interface IDeckLinkVideoBuffer IDeckLinkVideoBuffer; #endif /* __IDeckLinkVideoBuffer_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_FWD_DEFINED__ #define __IDeckLinkVideoFrame_FWD_DEFINED__ typedef interface IDeckLinkVideoFrame IDeckLinkVideoFrame; #endif /* __IDeckLinkVideoFrame_FWD_DEFINED__ */ #ifndef __IDeckLinkMutableVideoFrame_FWD_DEFINED__ #define __IDeckLinkMutableVideoFrame_FWD_DEFINED__ typedef interface IDeckLinkMutableVideoFrame IDeckLinkMutableVideoFrame; #endif /* __IDeckLinkMutableVideoFrame_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrame3DExtensions_FWD_DEFINED__ #define __IDeckLinkVideoFrame3DExtensions_FWD_DEFINED__ typedef interface IDeckLinkVideoFrame3DExtensions IDeckLinkVideoFrame3DExtensions; #endif /* __IDeckLinkVideoFrame3DExtensions_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrameMetadataExtensions_FWD_DEFINED__ #define __IDeckLinkVideoFrameMetadataExtensions_FWD_DEFINED__ typedef interface IDeckLinkVideoFrameMetadataExtensions IDeckLinkVideoFrameMetadataExtensions; #endif /* __IDeckLinkVideoFrameMetadataExtensions_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrameMutableMetadataExtensions_FWD_DEFINED__ #define __IDeckLinkVideoFrameMutableMetadataExtensions_FWD_DEFINED__ typedef interface IDeckLinkVideoFrameMutableMetadataExtensions IDeckLinkVideoFrameMutableMetadataExtensions; #endif /* __IDeckLinkVideoFrameMutableMetadataExtensions_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_FWD_DEFINED__ #define __IDeckLinkVideoInputFrame_FWD_DEFINED__ typedef interface IDeckLinkVideoInputFrame IDeckLinkVideoInputFrame; #endif /* __IDeckLinkVideoInputFrame_FWD_DEFINED__ */ #ifndef __IDeckLinkAncillaryPacket_FWD_DEFINED__ #define __IDeckLinkAncillaryPacket_FWD_DEFINED__ typedef interface IDeckLinkAncillaryPacket IDeckLinkAncillaryPacket; #endif /* __IDeckLinkAncillaryPacket_FWD_DEFINED__ */ #ifndef __IDeckLinkAncillaryPacketIterator_FWD_DEFINED__ #define __IDeckLinkAncillaryPacketIterator_FWD_DEFINED__ typedef interface IDeckLinkAncillaryPacketIterator IDeckLinkAncillaryPacketIterator; #endif /* __IDeckLinkAncillaryPacketIterator_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrameAncillaryPackets_FWD_DEFINED__ #define __IDeckLinkVideoFrameAncillaryPackets_FWD_DEFINED__ typedef interface IDeckLinkVideoFrameAncillaryPackets IDeckLinkVideoFrameAncillaryPackets; #endif /* __IDeckLinkVideoFrameAncillaryPackets_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrameAncillary_FWD_DEFINED__ #define __IDeckLinkVideoFrameAncillary_FWD_DEFINED__ typedef interface IDeckLinkVideoFrameAncillary IDeckLinkVideoFrameAncillary; #endif /* __IDeckLinkVideoFrameAncillary_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderPacket_FWD_DEFINED__ #define __IDeckLinkEncoderPacket_FWD_DEFINED__ typedef interface IDeckLinkEncoderPacket IDeckLinkEncoderPacket; #endif /* __IDeckLinkEncoderPacket_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderVideoPacket_FWD_DEFINED__ #define __IDeckLinkEncoderVideoPacket_FWD_DEFINED__ typedef interface IDeckLinkEncoderVideoPacket IDeckLinkEncoderVideoPacket; #endif /* __IDeckLinkEncoderVideoPacket_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderAudioPacket_FWD_DEFINED__ #define __IDeckLinkEncoderAudioPacket_FWD_DEFINED__ typedef interface IDeckLinkEncoderAudioPacket IDeckLinkEncoderAudioPacket; #endif /* __IDeckLinkEncoderAudioPacket_FWD_DEFINED__ */ #ifndef __IDeckLinkH265NALPacket_FWD_DEFINED__ #define __IDeckLinkH265NALPacket_FWD_DEFINED__ typedef interface IDeckLinkH265NALPacket IDeckLinkH265NALPacket; #endif /* __IDeckLinkH265NALPacket_FWD_DEFINED__ */ #ifndef __IDeckLinkAudioInputPacket_FWD_DEFINED__ #define __IDeckLinkAudioInputPacket_FWD_DEFINED__ typedef interface IDeckLinkAudioInputPacket IDeckLinkAudioInputPacket; #endif /* __IDeckLinkAudioInputPacket_FWD_DEFINED__ */ #ifndef __IDeckLinkScreenPreviewCallback_FWD_DEFINED__ #define __IDeckLinkScreenPreviewCallback_FWD_DEFINED__ typedef interface IDeckLinkScreenPreviewCallback IDeckLinkScreenPreviewCallback; #endif /* __IDeckLinkScreenPreviewCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ #define __IDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ typedef interface IDeckLinkGLScreenPreviewHelper IDeckLinkGLScreenPreviewHelper; #endif /* __IDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __IDeckLinkDX9ScreenPreviewHelper_FWD_DEFINED__ #define __IDeckLinkDX9ScreenPreviewHelper_FWD_DEFINED__ typedef interface IDeckLinkDX9ScreenPreviewHelper IDeckLinkDX9ScreenPreviewHelper; #endif /* __IDeckLinkDX9ScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __IDeckLinkWPFDX9ScreenPreviewHelper_FWD_DEFINED__ #define __IDeckLinkWPFDX9ScreenPreviewHelper_FWD_DEFINED__ typedef interface IDeckLinkWPFDX9ScreenPreviewHelper IDeckLinkWPFDX9ScreenPreviewHelper; #endif /* __IDeckLinkWPFDX9ScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __IDeckLinkNotificationCallback_FWD_DEFINED__ #define __IDeckLinkNotificationCallback_FWD_DEFINED__ typedef interface IDeckLinkNotificationCallback IDeckLinkNotificationCallback; #endif /* __IDeckLinkNotificationCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkNotification_FWD_DEFINED__ #define __IDeckLinkNotification_FWD_DEFINED__ typedef interface IDeckLinkNotification IDeckLinkNotification; #endif /* __IDeckLinkNotification_FWD_DEFINED__ */ #ifndef __IDeckLinkProfileAttributes_FWD_DEFINED__ #define __IDeckLinkProfileAttributes_FWD_DEFINED__ typedef interface IDeckLinkProfileAttributes IDeckLinkProfileAttributes; #endif /* __IDeckLinkProfileAttributes_FWD_DEFINED__ */ #ifndef __IDeckLinkProfileIterator_FWD_DEFINED__ #define __IDeckLinkProfileIterator_FWD_DEFINED__ typedef interface IDeckLinkProfileIterator IDeckLinkProfileIterator; #endif /* __IDeckLinkProfileIterator_FWD_DEFINED__ */ #ifndef __IDeckLinkProfile_FWD_DEFINED__ #define __IDeckLinkProfile_FWD_DEFINED__ typedef interface IDeckLinkProfile IDeckLinkProfile; #endif /* __IDeckLinkProfile_FWD_DEFINED__ */ #ifndef __IDeckLinkProfileCallback_FWD_DEFINED__ #define __IDeckLinkProfileCallback_FWD_DEFINED__ typedef interface IDeckLinkProfileCallback IDeckLinkProfileCallback; #endif /* __IDeckLinkProfileCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkProfileManager_FWD_DEFINED__ #define __IDeckLinkProfileManager_FWD_DEFINED__ typedef interface IDeckLinkProfileManager IDeckLinkProfileManager; #endif /* __IDeckLinkProfileManager_FWD_DEFINED__ */ #ifndef __IDeckLinkStatus_FWD_DEFINED__ #define __IDeckLinkStatus_FWD_DEFINED__ typedef interface IDeckLinkStatus IDeckLinkStatus; #endif /* __IDeckLinkStatus_FWD_DEFINED__ */ #ifndef __IDeckLinkKeyer_FWD_DEFINED__ #define __IDeckLinkKeyer_FWD_DEFINED__ typedef interface IDeckLinkKeyer IDeckLinkKeyer; #endif /* __IDeckLinkKeyer_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoConversion_FWD_DEFINED__ #define __IDeckLinkVideoConversion_FWD_DEFINED__ typedef interface IDeckLinkVideoConversion IDeckLinkVideoConversion; #endif /* __IDeckLinkVideoConversion_FWD_DEFINED__ */ #ifndef __IDeckLinkDeviceNotificationCallback_FWD_DEFINED__ #define __IDeckLinkDeviceNotificationCallback_FWD_DEFINED__ typedef interface IDeckLinkDeviceNotificationCallback IDeckLinkDeviceNotificationCallback; #endif /* __IDeckLinkDeviceNotificationCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkDiscovery_FWD_DEFINED__ #define __IDeckLinkDiscovery_FWD_DEFINED__ typedef interface IDeckLinkDiscovery IDeckLinkDiscovery; #endif /* __IDeckLinkDiscovery_FWD_DEFINED__ */ #ifndef __CDeckLinkIterator_FWD_DEFINED__ #define __CDeckLinkIterator_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkIterator CDeckLinkIterator; #else typedef struct CDeckLinkIterator CDeckLinkIterator; #endif /* __cplusplus */ #endif /* __CDeckLinkIterator_FWD_DEFINED__ */ #ifndef __CDeckLinkAPIInformation_FWD_DEFINED__ #define __CDeckLinkAPIInformation_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkAPIInformation CDeckLinkAPIInformation; #else typedef struct CDeckLinkAPIInformation CDeckLinkAPIInformation; #endif /* __cplusplus */ #endif /* __CDeckLinkAPIInformation_FWD_DEFINED__ */ #ifndef __CDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ #define __CDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkGLScreenPreviewHelper CDeckLinkGLScreenPreviewHelper; #else typedef struct CDeckLinkGLScreenPreviewHelper CDeckLinkGLScreenPreviewHelper; #endif /* __cplusplus */ #endif /* __CDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __CDeckLinkGL3ScreenPreviewHelper_FWD_DEFINED__ #define __CDeckLinkGL3ScreenPreviewHelper_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkGL3ScreenPreviewHelper CDeckLinkGL3ScreenPreviewHelper; #else typedef struct CDeckLinkGL3ScreenPreviewHelper CDeckLinkGL3ScreenPreviewHelper; #endif /* __cplusplus */ #endif /* __CDeckLinkGL3ScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __CDeckLinkDX9ScreenPreviewHelper_FWD_DEFINED__ #define __CDeckLinkDX9ScreenPreviewHelper_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkDX9ScreenPreviewHelper CDeckLinkDX9ScreenPreviewHelper; #else typedef struct CDeckLinkDX9ScreenPreviewHelper CDeckLinkDX9ScreenPreviewHelper; #endif /* __cplusplus */ #endif /* __CDeckLinkDX9ScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __CDeckLinkWPFDX9ScreenPreviewHelper_FWD_DEFINED__ #define __CDeckLinkWPFDX9ScreenPreviewHelper_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkWPFDX9ScreenPreviewHelper CDeckLinkWPFDX9ScreenPreviewHelper; #else typedef struct CDeckLinkWPFDX9ScreenPreviewHelper CDeckLinkWPFDX9ScreenPreviewHelper; #endif /* __cplusplus */ #endif /* __CDeckLinkWPFDX9ScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __CDeckLinkVideoConversion_FWD_DEFINED__ #define __CDeckLinkVideoConversion_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkVideoConversion CDeckLinkVideoConversion; #else typedef struct CDeckLinkVideoConversion CDeckLinkVideoConversion; #endif /* __cplusplus */ #endif /* __CDeckLinkVideoConversion_FWD_DEFINED__ */ #ifndef __CDeckLinkDiscovery_FWD_DEFINED__ #define __CDeckLinkDiscovery_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkDiscovery CDeckLinkDiscovery; #else typedef struct CDeckLinkDiscovery CDeckLinkDiscovery; #endif /* __cplusplus */ #endif /* __CDeckLinkDiscovery_FWD_DEFINED__ */ #ifndef __CDeckLinkVideoFrameAncillaryPackets_FWD_DEFINED__ #define __CDeckLinkVideoFrameAncillaryPackets_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkVideoFrameAncillaryPackets CDeckLinkVideoFrameAncillaryPackets; #else typedef struct CDeckLinkVideoFrameAncillaryPackets CDeckLinkVideoFrameAncillaryPackets; #endif /* __cplusplus */ #endif /* __CDeckLinkVideoFrameAncillaryPackets_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoOutputCallback_v14_2_1_FWD_DEFINED__ #define __IDeckLinkVideoOutputCallback_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkVideoOutputCallback_v14_2_1 IDeckLinkVideoOutputCallback_v14_2_1; #endif /* __IDeckLinkVideoOutputCallback_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkInputCallback_v14_2_1_FWD_DEFINED__ #define __IDeckLinkInputCallback_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkInputCallback_v14_2_1 IDeckLinkInputCallback_v14_2_1; #endif /* __IDeckLinkInputCallback_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkMemoryAllocator_v14_2_1_FWD_DEFINED__ #define __IDeckLinkMemoryAllocator_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkMemoryAllocator_v14_2_1 IDeckLinkMemoryAllocator_v14_2_1; #endif /* __IDeckLinkMemoryAllocator_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkOutput_v14_2_1_FWD_DEFINED__ #define __IDeckLinkOutput_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkOutput_v14_2_1 IDeckLinkOutput_v14_2_1; #endif /* __IDeckLinkOutput_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_v14_2_1_FWD_DEFINED__ #define __IDeckLinkInput_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkInput_v14_2_1 IDeckLinkInput_v14_2_1; #endif /* __IDeckLinkInput_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderInput_v14_2_1_FWD_DEFINED__ #define __IDeckLinkEncoderInput_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkEncoderInput_v14_2_1 IDeckLinkEncoderInput_v14_2_1; #endif /* __IDeckLinkEncoderInput_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_v14_2_1_FWD_DEFINED__ #define __IDeckLinkVideoFrame_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkVideoFrame_v14_2_1 IDeckLinkVideoFrame_v14_2_1; #endif /* __IDeckLinkVideoFrame_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkMutableVideoFrame_v14_2_1_FWD_DEFINED__ #define __IDeckLinkMutableVideoFrame_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkMutableVideoFrame_v14_2_1 IDeckLinkMutableVideoFrame_v14_2_1; #endif /* __IDeckLinkMutableVideoFrame_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrame3DExtensions_v14_2_1_FWD_DEFINED__ #define __IDeckLinkVideoFrame3DExtensions_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkVideoFrame3DExtensions_v14_2_1 IDeckLinkVideoFrame3DExtensions_v14_2_1; #endif /* __IDeckLinkVideoFrame3DExtensions_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_v14_2_1_FWD_DEFINED__ #define __IDeckLinkVideoInputFrame_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkVideoInputFrame_v14_2_1 IDeckLinkVideoInputFrame_v14_2_1; #endif /* __IDeckLinkVideoInputFrame_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkScreenPreviewCallback_v14_2_1_FWD_DEFINED__ #define __IDeckLinkScreenPreviewCallback_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkScreenPreviewCallback_v14_2_1 IDeckLinkScreenPreviewCallback_v14_2_1; #endif /* __IDeckLinkScreenPreviewCallback_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkGLScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #define __IDeckLinkGLScreenPreviewHelper_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkGLScreenPreviewHelper_v14_2_1 IDeckLinkGLScreenPreviewHelper_v14_2_1; #endif /* __IDeckLinkGLScreenPreviewHelper_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #define __IDeckLinkDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkDX9ScreenPreviewHelper_v14_2_1 IDeckLinkDX9ScreenPreviewHelper_v14_2_1; #endif /* __IDeckLinkDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #define __IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1; #endif /* __IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoConversion_v14_2_1_FWD_DEFINED__ #define __IDeckLinkVideoConversion_v14_2_1_FWD_DEFINED__ typedef interface IDeckLinkVideoConversion_v14_2_1 IDeckLinkVideoConversion_v14_2_1; #endif /* __IDeckLinkVideoConversion_v14_2_1_FWD_DEFINED__ */ #ifndef __CDeckLinkGLScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #define __CDeckLinkGLScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkGLScreenPreviewHelper_v14_2_1 CDeckLinkGLScreenPreviewHelper_v14_2_1; #else typedef struct CDeckLinkGLScreenPreviewHelper_v14_2_1 CDeckLinkGLScreenPreviewHelper_v14_2_1; #endif /* __cplusplus */ #endif /* __CDeckLinkGLScreenPreviewHelper_v14_2_1_FWD_DEFINED__ */ #ifndef __CDeckLinkGL3ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #define __CDeckLinkGL3ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkGL3ScreenPreviewHelper_v14_2_1 CDeckLinkGL3ScreenPreviewHelper_v14_2_1; #else typedef struct CDeckLinkGL3ScreenPreviewHelper_v14_2_1 CDeckLinkGL3ScreenPreviewHelper_v14_2_1; #endif /* __cplusplus */ #endif /* __CDeckLinkGL3ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ */ #ifndef __CDeckLinkDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #define __CDeckLinkDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkDX9ScreenPreviewHelper_v14_2_1 CDeckLinkDX9ScreenPreviewHelper_v14_2_1; #else typedef struct CDeckLinkDX9ScreenPreviewHelper_v14_2_1 CDeckLinkDX9ScreenPreviewHelper_v14_2_1; #endif /* __cplusplus */ #endif /* __CDeckLinkDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ */ #ifndef __CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #define __CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1; #else typedef struct CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1; #endif /* __cplusplus */ #endif /* __CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_FWD_DEFINED__ */ #ifndef __CDeckLinkVideoConversion_v14_2_1_FWD_DEFINED__ #define __CDeckLinkVideoConversion_v14_2_1_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkVideoConversion_v14_2_1 CDeckLinkVideoConversion_v14_2_1; #else typedef struct CDeckLinkVideoConversion_v14_2_1 CDeckLinkVideoConversion_v14_2_1; #endif /* __cplusplus */ #endif /* __CDeckLinkVideoConversion_v14_2_1_FWD_DEFINED__ */ #ifndef __IDeckLinkInputCallback_v11_5_1_FWD_DEFINED__ #define __IDeckLinkInputCallback_v11_5_1_FWD_DEFINED__ typedef interface IDeckLinkInputCallback_v11_5_1 IDeckLinkInputCallback_v11_5_1; #endif /* __IDeckLinkInputCallback_v11_5_1_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_v11_5_1_FWD_DEFINED__ #define __IDeckLinkInput_v11_5_1_FWD_DEFINED__ typedef interface IDeckLinkInput_v11_5_1 IDeckLinkInput_v11_5_1; #endif /* __IDeckLinkInput_v11_5_1_FWD_DEFINED__ */ #ifndef __IDeckLinkConfiguration_v10_11_FWD_DEFINED__ #define __IDeckLinkConfiguration_v10_11_FWD_DEFINED__ typedef interface IDeckLinkConfiguration_v10_11 IDeckLinkConfiguration_v10_11; #endif /* __IDeckLinkConfiguration_v10_11_FWD_DEFINED__ */ #ifndef __IDeckLinkAttributes_v10_11_FWD_DEFINED__ #define __IDeckLinkAttributes_v10_11_FWD_DEFINED__ typedef interface IDeckLinkAttributes_v10_11 IDeckLinkAttributes_v10_11; #endif /* __IDeckLinkAttributes_v10_11_FWD_DEFINED__ */ #ifndef __IDeckLinkNotification_v10_11_FWD_DEFINED__ #define __IDeckLinkNotification_v10_11_FWD_DEFINED__ typedef interface IDeckLinkNotification_v10_11 IDeckLinkNotification_v10_11; #endif /* __IDeckLinkNotification_v10_11_FWD_DEFINED__ */ #ifndef __IDeckLinkOutput_v10_11_FWD_DEFINED__ #define __IDeckLinkOutput_v10_11_FWD_DEFINED__ typedef interface IDeckLinkOutput_v10_11 IDeckLinkOutput_v10_11; #endif /* __IDeckLinkOutput_v10_11_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_v10_11_FWD_DEFINED__ #define __IDeckLinkInput_v10_11_FWD_DEFINED__ typedef interface IDeckLinkInput_v10_11 IDeckLinkInput_v10_11; #endif /* __IDeckLinkInput_v10_11_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderInput_v10_11_FWD_DEFINED__ #define __IDeckLinkEncoderInput_v10_11_FWD_DEFINED__ typedef interface IDeckLinkEncoderInput_v10_11 IDeckLinkEncoderInput_v10_11; #endif /* __IDeckLinkEncoderInput_v10_11_FWD_DEFINED__ */ #ifndef __CDeckLinkIterator_v10_11_FWD_DEFINED__ #define __CDeckLinkIterator_v10_11_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkIterator_v10_11 CDeckLinkIterator_v10_11; #else typedef struct CDeckLinkIterator_v10_11 CDeckLinkIterator_v10_11; #endif /* __cplusplus */ #endif /* __CDeckLinkIterator_v10_11_FWD_DEFINED__ */ #ifndef __CDeckLinkDiscovery_v10_11_FWD_DEFINED__ #define __CDeckLinkDiscovery_v10_11_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkDiscovery_v10_11 CDeckLinkDiscovery_v10_11; #else typedef struct CDeckLinkDiscovery_v10_11 CDeckLinkDiscovery_v10_11; #endif /* __cplusplus */ #endif /* __CDeckLinkDiscovery_v10_11_FWD_DEFINED__ */ #ifndef __IDeckLinkConfiguration_v10_9_FWD_DEFINED__ #define __IDeckLinkConfiguration_v10_9_FWD_DEFINED__ typedef interface IDeckLinkConfiguration_v10_9 IDeckLinkConfiguration_v10_9; #endif /* __IDeckLinkConfiguration_v10_9_FWD_DEFINED__ */ #ifndef __CBMDStreamingDiscovery_v10_8_FWD_DEFINED__ #define __CBMDStreamingDiscovery_v10_8_FWD_DEFINED__ #ifdef __cplusplus typedef class CBMDStreamingDiscovery_v10_8 CBMDStreamingDiscovery_v10_8; #else typedef struct CBMDStreamingDiscovery_v10_8 CBMDStreamingDiscovery_v10_8; #endif /* __cplusplus */ #endif /* __CBMDStreamingDiscovery_v10_8_FWD_DEFINED__ */ #ifndef __IDeckLinkConfiguration_v10_4_FWD_DEFINED__ #define __IDeckLinkConfiguration_v10_4_FWD_DEFINED__ typedef interface IDeckLinkConfiguration_v10_4 IDeckLinkConfiguration_v10_4; #endif /* __IDeckLinkConfiguration_v10_4_FWD_DEFINED__ */ #ifndef __IDeckLinkConfiguration_v10_2_FWD_DEFINED__ #define __IDeckLinkConfiguration_v10_2_FWD_DEFINED__ typedef interface IDeckLinkConfiguration_v10_2 IDeckLinkConfiguration_v10_2; #endif /* __IDeckLinkConfiguration_v10_2_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrameMetadataExtensions_v11_5_FWD_DEFINED__ #define __IDeckLinkVideoFrameMetadataExtensions_v11_5_FWD_DEFINED__ typedef interface IDeckLinkVideoFrameMetadataExtensions_v11_5 IDeckLinkVideoFrameMetadataExtensions_v11_5; #endif /* __IDeckLinkVideoFrameMetadataExtensions_v11_5_FWD_DEFINED__ */ #ifndef __IDeckLinkOutput_v11_4_FWD_DEFINED__ #define __IDeckLinkOutput_v11_4_FWD_DEFINED__ typedef interface IDeckLinkOutput_v11_4 IDeckLinkOutput_v11_4; #endif /* __IDeckLinkOutput_v11_4_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_v11_4_FWD_DEFINED__ #define __IDeckLinkInput_v11_4_FWD_DEFINED__ typedef interface IDeckLinkInput_v11_4 IDeckLinkInput_v11_4; #endif /* __IDeckLinkInput_v11_4_FWD_DEFINED__ */ #ifndef __CDeckLinkIterator_v10_8_FWD_DEFINED__ #define __CDeckLinkIterator_v10_8_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkIterator_v10_8 CDeckLinkIterator_v10_8; #else typedef struct CDeckLinkIterator_v10_8 CDeckLinkIterator_v10_8; #endif /* __cplusplus */ #endif /* __CDeckLinkIterator_v10_8_FWD_DEFINED__ */ #ifndef __CDeckLinkDiscovery_v10_8_FWD_DEFINED__ #define __CDeckLinkDiscovery_v10_8_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkDiscovery_v10_8 CDeckLinkDiscovery_v10_8; #else typedef struct CDeckLinkDiscovery_v10_8 CDeckLinkDiscovery_v10_8; #endif /* __cplusplus */ #endif /* __CDeckLinkDiscovery_v10_8_FWD_DEFINED__ */ #ifndef __IDeckLinkEncoderConfiguration_v10_5_FWD_DEFINED__ #define __IDeckLinkEncoderConfiguration_v10_5_FWD_DEFINED__ typedef interface IDeckLinkEncoderConfiguration_v10_5 IDeckLinkEncoderConfiguration_v10_5; #endif /* __IDeckLinkEncoderConfiguration_v10_5_FWD_DEFINED__ */ /* header files for imported files */ #include "unknwn.h" #ifdef __cplusplus extern "C"{ #endif #ifndef __DeckLinkAPI_LIBRARY_DEFINED__ #define __DeckLinkAPI_LIBRARY_DEFINED__ /* library DeckLinkAPI */ /* [helpstring][version][uuid] */ typedef LONGLONG BMDTimeValue; typedef LONGLONG BMDTimeScale; typedef unsigned int BMDTimecodeBCD; typedef unsigned int BMDTimecodeUserBits; typedef LONGLONG BMDIPFlowID; typedef unsigned int BMDTimecodeFlags; #if 0 typedef enum _BMDTimecodeFlags BMDTimecodeFlags; #endif /* [v1_enum] */ enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = ( 1 << 0 ) , bmdTimecodeFieldMark = ( 1 << 1 ) , bmdTimecodeColorFrame = ( 1 << 2 ) , bmdTimecodeEmbedRecordingTrigger = ( 1 << 3 ) , bmdTimecodeRecordingTriggered = ( 1 << 4 ) } ; typedef /* [v1_enum] */ enum _BMDVideoConnection { bmdVideoConnectionUnspecified = 0, bmdVideoConnectionSDI = ( 1 << 0 ) , bmdVideoConnectionHDMI = ( 1 << 1 ) , bmdVideoConnectionOpticalSDI = ( 1 << 2 ) , bmdVideoConnectionComponent = ( 1 << 3 ) , bmdVideoConnectionComposite = ( 1 << 4 ) , bmdVideoConnectionSVideo = ( 1 << 5 ) , bmdVideoConnectionEthernet = ( 1 << 6 ) , bmdVideoConnectionOpticalEthernet = ( 1 << 7 ) } BMDVideoConnection; typedef /* [v1_enum] */ enum _BMDAudioConnection { bmdAudioConnectionEmbedded = ( 1 << 0 ) , bmdAudioConnectionAESEBU = ( 1 << 1 ) , bmdAudioConnectionAnalog = ( 1 << 2 ) , bmdAudioConnectionAnalogXLR = ( 1 << 3 ) , bmdAudioConnectionAnalogRCA = ( 1 << 4 ) , bmdAudioConnectionMicrophone = ( 1 << 5 ) , bmdAudioConnectionHeadphones = ( 1 << 6 ) } BMDAudioConnection; typedef /* [v1_enum] */ enum _BMDDeckControlConnection { bmdDeckControlConnectionRS422Remote1 = ( 1 << 0 ) , bmdDeckControlConnectionRS422Remote2 = ( 1 << 1 ) } BMDDeckControlConnection; typedef unsigned int BMDDisplayModeFlags; #if 0 typedef enum _BMDDisplayModeFlags BMDDisplayModeFlags; #endif typedef /* [v1_enum] */ enum _BMDDisplayMode { bmdModeNTSC = 0x6e747363, bmdModeNTSC2398 = 0x6e743233, bmdModePAL = 0x70616c20, bmdModeNTSCp = 0x6e747370, bmdModePALp = 0x70616c70, bmdModeHD1080p2398 = 0x32337073, bmdModeHD1080p24 = 0x32347073, bmdModeHD1080p25 = 0x48703235, bmdModeHD1080p2997 = 0x48703239, bmdModeHD1080p30 = 0x48703330, bmdModeHD1080p4795 = 0x48703437, bmdModeHD1080p48 = 0x48703438, bmdModeHD1080p50 = 0x48703530, bmdModeHD1080p5994 = 0x48703539, bmdModeHD1080p6000 = 0x48703630, bmdModeHD1080p9590 = 0x48703935, bmdModeHD1080p96 = 0x48703936, bmdModeHD1080p100 = 0x48703130, bmdModeHD1080p11988 = 0x48703131, bmdModeHD1080p120 = 0x48703132, bmdModeHD1080i50 = 0x48693530, bmdModeHD1080i5994 = 0x48693539, bmdModeHD1080i6000 = 0x48693630, bmdModeHD720p50 = 0x68703530, bmdModeHD720p5994 = 0x68703539, bmdModeHD720p60 = 0x68703630, bmdMode2k2398 = 0x326b3233, bmdMode2k24 = 0x326b3234, bmdMode2k25 = 0x326b3235, bmdMode2kDCI2398 = 0x32643233, bmdMode2kDCI24 = 0x32643234, bmdMode2kDCI25 = 0x32643235, bmdMode2kDCI2997 = 0x32643239, bmdMode2kDCI30 = 0x32643330, bmdMode2kDCI4795 = 0x32643437, bmdMode2kDCI48 = 0x32643438, bmdMode2kDCI50 = 0x32643530, bmdMode2kDCI5994 = 0x32643539, bmdMode2kDCI60 = 0x32643630, bmdMode2kDCI9590 = 0x32643935, bmdMode2kDCI96 = 0x32643936, bmdMode2kDCI100 = 0x32643130, bmdMode2kDCI11988 = 0x32643131, bmdMode2kDCI120 = 0x32643132, bmdMode4K2160p2398 = 0x346b3233, bmdMode4K2160p24 = 0x346b3234, bmdMode4K2160p25 = 0x346b3235, bmdMode4K2160p2997 = 0x346b3239, bmdMode4K2160p30 = 0x346b3330, bmdMode4K2160p4795 = 0x346b3437, bmdMode4K2160p48 = 0x346b3438, bmdMode4K2160p50 = 0x346b3530, bmdMode4K2160p5994 = 0x346b3539, bmdMode4K2160p60 = 0x346b3630, bmdMode4K2160p9590 = 0x346b3935, bmdMode4K2160p96 = 0x346b3936, bmdMode4K2160p100 = 0x346b3130, bmdMode4K2160p11988 = 0x346b3131, bmdMode4K2160p120 = 0x346b3132, bmdMode4kDCI2398 = 0x34643233, bmdMode4kDCI24 = 0x34643234, bmdMode4kDCI25 = 0x34643235, bmdMode4kDCI2997 = 0x34643239, bmdMode4kDCI30 = 0x34643330, bmdMode4kDCI4795 = 0x34643437, bmdMode4kDCI48 = 0x34643438, bmdMode4kDCI50 = 0x34643530, bmdMode4kDCI5994 = 0x34643539, bmdMode4kDCI60 = 0x34643630, bmdMode4kDCI9590 = 0x34643935, bmdMode4kDCI96 = 0x34643936, bmdMode4kDCI100 = 0x34643130, bmdMode4kDCI11988 = 0x34643131, bmdMode4kDCI120 = 0x34643132, bmdMode8K4320p2398 = 0x386b3233, bmdMode8K4320p24 = 0x386b3234, bmdMode8K4320p25 = 0x386b3235, bmdMode8K4320p2997 = 0x386b3239, bmdMode8K4320p30 = 0x386b3330, bmdMode8K4320p4795 = 0x386b3437, bmdMode8K4320p48 = 0x386b3438, bmdMode8K4320p50 = 0x386b3530, bmdMode8K4320p5994 = 0x386b3539, bmdMode8K4320p60 = 0x386b3630, bmdMode8kDCI2398 = 0x38643233, bmdMode8kDCI24 = 0x38643234, bmdMode8kDCI25 = 0x38643235, bmdMode8kDCI2997 = 0x38643239, bmdMode8kDCI30 = 0x38643330, bmdMode8kDCI4795 = 0x38643437, bmdMode8kDCI48 = 0x38643438, bmdMode8kDCI50 = 0x38643530, bmdMode8kDCI5994 = 0x38643539, bmdMode8kDCI60 = 0x38643630, bmdMode640x480p60 = 0x76676136, bmdMode800x600p60 = 0x73766736, bmdMode1440x900p50 = 0x77786735, bmdMode1440x900p60 = 0x77786736, bmdMode1440x1080p50 = 0x73786735, bmdMode1440x1080p60 = 0x73786736, bmdMode1600x1200p50 = 0x75786735, bmdMode1600x1200p60 = 0x75786736, bmdMode1920x1200p50 = 0x77757835, bmdMode1920x1200p60 = 0x77757836, bmdMode1920x1440p50 = 0x31393435, bmdMode1920x1440p60 = 0x31393436, bmdMode2560x1440p50 = 0x77716835, bmdMode2560x1440p60 = 0x77716836, bmdMode2560x1600p50 = 0x77717835, bmdMode2560x1600p60 = 0x77717836, bmdModeUnknown = 0x69756e6b } BMDDisplayMode; typedef /* [v1_enum] */ enum _BMDFieldDominance { bmdUnknownFieldDominance = 0, bmdLowerFieldFirst = 0x6c6f7772, bmdUpperFieldFirst = 0x75707072, bmdProgressiveFrame = 0x70726f67, bmdProgressiveSegmentedFrame = 0x70736620 } BMDFieldDominance; typedef /* [v1_enum] */ enum _BMDPixelFormat { bmdFormatUnspecified = 0, bmdFormat8BitYUV = 0x32767579, bmdFormat10BitYUV = 0x76323130, bmdFormat10BitYUVA = 0x41793130, bmdFormat8BitARGB = 32, bmdFormat8BitBGRA = 0x42475241, bmdFormat10BitRGB = 0x72323130, bmdFormat12BitRGB = 0x52313242, bmdFormat12BitRGBLE = 0x5231324c, bmdFormat10BitRGBXLE = 0x5231306c, bmdFormat10BitRGBX = 0x52313062, bmdFormatH265 = 0x68657631, bmdFormatDNxHR = 0x41566468 } BMDPixelFormat; /* [v1_enum] */ enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = ( 1 << 0 ) , bmdDisplayModeColorspaceRec601 = ( 1 << 1 ) , bmdDisplayModeColorspaceRec709 = ( 1 << 2 ) , bmdDisplayModeColorspaceRec2020 = ( 1 << 3 ) } ; #if 0 #endif #if 0 #endif typedef /* [v1_enum] */ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigSwapSerialRxTx = 0x73737274, bmdDeckLinkConfigHDMI3DPackingFormat = 0x33647066, bmdDeckLinkConfigBypass = 0x62797073, bmdDeckLinkConfigClockTimingAdjustment = 0x63746164, bmdDeckLinkConfigAnalogAudioConsumerLevels = 0x6161636c, bmdDeckLinkConfigSwapHDMICh3AndCh4OnInput = 0x68693334, bmdDeckLinkConfigSwapHDMICh3AndCh4OnOutput = 0x686f3334, bmdDeckLinkConfigFieldFlickerRemoval = 0x66646672, bmdDeckLinkConfigHD1080p24ToHD1080i5994Conversion = 0x746f3539, bmdDeckLinkConfig444SDIVideoOutput = 0x3434346f, bmdDeckLinkConfigBlackVideoOutputDuringCapture = 0x62766f63, bmdDeckLinkConfigLowLatencyVideoOutput = 0x6c6c766f, bmdDeckLinkConfigDownConversionOnAllAnalogOutput = 0x6361616f, bmdDeckLinkConfigSMPTELevelAOutput = 0x736d7461, bmdDeckLinkConfigRec2020Output = 0x72656332, bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = 0x53445153, bmdDeckLinkConfigOutput1080pAsPsF = 0x70667072, bmdDeckLinkConfigOutputValidateEDIDForDolbyVision = 0x70726564, bmdDeckLinkConfigVideoOutputConnection = 0x766f636e, bmdDeckLinkConfigVideoOutputConversionMode = 0x766f636d, bmdDeckLinkConfigVideoOutputConversionColorspaceDestination = 0x76636364, bmdDeckLinkConfigVideoOutputConversionColorspaceSource = 0x76636373, bmdDeckLinkConfigAnalogVideoOutputFlags = 0x61766f66, bmdDeckLinkConfigReferenceInputTimingOffset = 0x676c6f74, bmdDeckLinkConfigReferenceOutputMode = 0x676c4f6d, bmdDeckLinkConfigVideoOutputIdleOperation = 0x766f696f, bmdDeckLinkConfigDefaultVideoOutputMode = 0x64766f6d, bmdDeckLinkConfigDefaultVideoOutputModeFlags = 0x64766f66, bmdDeckLinkConfigSDIOutputLinkConfiguration = 0x736f6c63, bmdDeckLinkConfigHDMITimecodePacking = 0x6874706b, bmdDeckLinkConfigPlaybackGroup = 0x706c6772, bmdDeckLinkConfigVideoOutputComponentLumaGain = 0x6f636c67, bmdDeckLinkConfigVideoOutputComponentChromaBlueGain = 0x6f636362, bmdDeckLinkConfigVideoOutputComponentChromaRedGain = 0x6f636372, bmdDeckLinkConfigVideoOutputCompositeLumaGain = 0x6f696c67, bmdDeckLinkConfigVideoOutputCompositeChromaGain = 0x6f696367, bmdDeckLinkConfigVideoOutputSVideoLumaGain = 0x6f736c67, bmdDeckLinkConfigVideoOutputSVideoChromaGain = 0x6f736367, bmdDeckLinkConfigDolbyVisionCMVersion = 0x64767672, bmdDeckLinkConfigDolbyVisionMasterMinimumNits = 0x6d6e6e74, bmdDeckLinkConfigDolbyVisionMasterMaximumNits = 0x6d786e74, bmdDeckLinkConfigVideoInputScanning = 0x76697363, bmdDeckLinkConfigUseDedicatedLTCInput = 0x646c7463, bmdDeckLinkConfigSDIInput3DPayloadOverride = 0x33646473, bmdDeckLinkConfigCapture1080pAsPsF = 0x63667072, bmdDeckLinkConfigVideoInputConnection = 0x7669636e, bmdDeckLinkConfigAnalogVideoInputFlags = 0x61766966, bmdDeckLinkConfigVideoInputConversionMode = 0x7669636d, bmdDeckLinkConfig32PulldownSequenceInitialTimecodeFrame = 0x70646966, bmdDeckLinkConfigVANCSourceLine1Mapping = 0x76736c31, bmdDeckLinkConfigVANCSourceLine2Mapping = 0x76736c32, bmdDeckLinkConfigVANCSourceLine3Mapping = 0x76736c33, bmdDeckLinkConfigCapturePassThroughMode = 0x6370746d, bmdDeckLinkConfigCaptureGroup = 0x63706772, bmdDeckLinkConfigVideoInputComponentLumaGain = 0x69636c67, bmdDeckLinkConfigVideoInputComponentChromaBlueGain = 0x69636362, bmdDeckLinkConfigVideoInputComponentChromaRedGain = 0x69636372, bmdDeckLinkConfigVideoInputCompositeLumaGain = 0x69696c67, bmdDeckLinkConfigVideoInputCompositeChromaGain = 0x69696367, bmdDeckLinkConfigVideoInputSVideoLumaGain = 0x69736c67, bmdDeckLinkConfigVideoInputSVideoChromaGain = 0x69736367, bmdDeckLinkConfigInternalKeyingAncillaryDataSource = 0x696b6173, bmdDeckLinkConfigMicrophonePhantomPower = 0x6d706870, bmdDeckLinkConfigAudioInputConnection = 0x6169636e, bmdDeckLinkConfigAnalogAudioInputScaleChannel1 = 0x61697331, bmdDeckLinkConfigAnalogAudioInputScaleChannel2 = 0x61697332, bmdDeckLinkConfigAnalogAudioInputScaleChannel3 = 0x61697333, bmdDeckLinkConfigAnalogAudioInputScaleChannel4 = 0x61697334, bmdDeckLinkConfigDigitalAudioInputScale = 0x64616973, bmdDeckLinkConfigMicrophoneInputGain = 0x6d696367, bmdDeckLinkConfigAudioOutputAESAnalogSwitch = 0x616f6161, bmdDeckLinkConfigAnalogAudioOutputScaleChannel1 = 0x616f7331, bmdDeckLinkConfigAnalogAudioOutputScaleChannel2 = 0x616f7332, bmdDeckLinkConfigAnalogAudioOutputScaleChannel3 = 0x616f7333, bmdDeckLinkConfigAnalogAudioOutputScaleChannel4 = 0x616f7334, bmdDeckLinkConfigDigitalAudioOutputScale = 0x64616f73, bmdDeckLinkConfigHeadphoneVolume = 0x68766f6c, bmdDeckLinkConfigEthernetUseDHCP = 0x44484350, bmdDeckLinkConfigEthernetPTPFollowerOnly = 0x50545066, bmdDeckLinkConfigEthernetPTPUseUDPEncapsulation = 0x50545055, bmdDeckLinkConfigEthernetPTPPriority1 = 0x50545031, bmdDeckLinkConfigEthernetPTPPriority2 = 0x50545032, bmdDeckLinkConfigEthernetPTPDomain = 0x50545044, bmdDeckLinkConfigEthernetPTPLogAnnounceInterval = 0x50545041, bmdDeckLinkConfigEthernetStaticLocalIPAddress = 0x6e736970, bmdDeckLinkConfigEthernetStaticSubnetMask = 0x6e73736d, bmdDeckLinkConfigEthernetStaticGatewayIPAddress = 0x6e736777, bmdDeckLinkConfigEthernetStaticPrimaryDNS = 0x6e737064, bmdDeckLinkConfigEthernetStaticSecondaryDNS = 0x6e737364, bmdDeckLinkConfigEthernetVideoOutputAddress = 0x6e6f6176, bmdDeckLinkConfigEthernetAudioOutputAddress = 0x6e6f6161, bmdDeckLinkConfigEthernetAncillaryOutputAddress = 0x6e6f6141, bmdDeckLinkConfigEthernetAudioOutputChannelOrder = 0x6361636f, bmdDeckLinkConfigDeviceInformationLabel = 0x64696c61, bmdDeckLinkConfigDeviceInformationSerialNumber = 0x6469736e, bmdDeckLinkConfigDeviceInformationCompany = 0x6469636f, bmdDeckLinkConfigDeviceInformationPhone = 0x64697068, bmdDeckLinkConfigDeviceInformationEmail = 0x6469656d, bmdDeckLinkConfigDeviceInformationDate = 0x64696461, bmdDeckLinkConfigDeckControlConnection = 0x6463636f } BMDDeckLinkConfigurationID; typedef /* [v1_enum] */ enum _BMDDeckLinkEncoderConfigurationID { bmdDeckLinkEncoderConfigPreferredBitDepth = 0x65706272, bmdDeckLinkEncoderConfigFrameCodingMode = 0x6566636d, bmdDeckLinkEncoderConfigH265TargetBitrate = 0x68746272, bmdDeckLinkEncoderConfigDNxHRCompressionID = 0x64636964, bmdDeckLinkEncoderConfigDNxHRLevel = 0x646c6576, bmdDeckLinkEncoderConfigMPEG4SampleDescription = 0x73747345, bmdDeckLinkEncoderConfigMPEG4CodecSpecificDesc = 0x65736473 } BMDDeckLinkEncoderConfigurationID; typedef unsigned int BMDDeckControlStatusFlags; typedef unsigned int BMDDeckControlExportModeOpsFlags; #if 0 typedef enum _BMDDeckControlStatusFlags BMDDeckControlStatusFlags; typedef enum _BMDDeckControlExportModeOpsFlags BMDDeckControlExportModeOpsFlags; #endif typedef /* [v1_enum] */ enum _BMDDeckControlMode { bmdDeckControlNotOpened = 0x6e746f70, bmdDeckControlVTRControlMode = 0x76747263, bmdDeckControlExportMode = 0x6578706d, bmdDeckControlCaptureMode = 0x6361706d } BMDDeckControlMode; typedef /* [v1_enum] */ enum _BMDDeckControlEvent { bmdDeckControlAbortedEvent = 0x61627465, bmdDeckControlPrepareForExportEvent = 0x70666565, bmdDeckControlExportCompleteEvent = 0x65786365, bmdDeckControlPrepareForCaptureEvent = 0x70666365, bmdDeckControlCaptureCompleteEvent = 0x63636576 } BMDDeckControlEvent; typedef /* [v1_enum] */ enum _BMDDeckControlVTRControlState { bmdDeckControlNotInVTRControlMode = 0x6e76636d, bmdDeckControlVTRControlPlaying = 0x76747270, bmdDeckControlVTRControlRecording = 0x76747272, bmdDeckControlVTRControlStill = 0x76747261, bmdDeckControlVTRControlShuttleForward = 0x76747366, bmdDeckControlVTRControlShuttleReverse = 0x76747372, bmdDeckControlVTRControlJogForward = 0x76746a66, bmdDeckControlVTRControlJogReverse = 0x76746a72, bmdDeckControlVTRControlStopped = 0x7674726f } BMDDeckControlVTRControlState; /* [v1_enum] */ enum _BMDDeckControlStatusFlags { bmdDeckControlStatusDeckConnected = ( 1 << 0 ) , bmdDeckControlStatusRemoteMode = ( 1 << 1 ) , bmdDeckControlStatusRecordInhibited = ( 1 << 2 ) , bmdDeckControlStatusCassetteOut = ( 1 << 3 ) } ; /* [v1_enum] */ enum _BMDDeckControlExportModeOpsFlags { bmdDeckControlExportModeInsertVideo = ( 1 << 0 ) , bmdDeckControlExportModeInsertAudio1 = ( 1 << 1 ) , bmdDeckControlExportModeInsertAudio2 = ( 1 << 2 ) , bmdDeckControlExportModeInsertAudio3 = ( 1 << 3 ) , bmdDeckControlExportModeInsertAudio4 = ( 1 << 4 ) , bmdDeckControlExportModeInsertAudio5 = ( 1 << 5 ) , bmdDeckControlExportModeInsertAudio6 = ( 1 << 6 ) , bmdDeckControlExportModeInsertAudio7 = ( 1 << 7 ) , bmdDeckControlExportModeInsertAudio8 = ( 1 << 8 ) , bmdDeckControlExportModeInsertAudio9 = ( 1 << 9 ) , bmdDeckControlExportModeInsertAudio10 = ( 1 << 10 ) , bmdDeckControlExportModeInsertAudio11 = ( 1 << 11 ) , bmdDeckControlExportModeInsertAudio12 = ( 1 << 12 ) , bmdDeckControlExportModeInsertTimeCode = ( 1 << 13 ) , bmdDeckControlExportModeInsertAssemble = ( 1 << 14 ) , bmdDeckControlExportModeInsertPreview = ( 1 << 15 ) , bmdDeckControlUseManualExport = ( 1 << 16 ) } ; typedef /* [v1_enum] */ enum _BMDDeckControlError { bmdDeckControlNoError = 0x6e6f6572, bmdDeckControlModeError = 0x6d6f6572, bmdDeckControlMissedInPointError = 0x6d696572, bmdDeckControlDeckTimeoutError = 0x64746572, bmdDeckControlCommandFailedError = 0x63666572, bmdDeckControlDeviceAlreadyOpenedError = 0x64616c6f, bmdDeckControlFailedToOpenDeviceError = 0x66646572, bmdDeckControlInLocalModeError = 0x6c6d6572, bmdDeckControlEndOfTapeError = 0x65746572, bmdDeckControlUserAbortError = 0x75616572, bmdDeckControlNoTapeInDeckError = 0x6e746572, bmdDeckControlNoVideoFromCardError = 0x6e766663, bmdDeckControlNoCommunicationError = 0x6e636f6d, bmdDeckControlBufferTooSmallError = 0x6274736d, bmdDeckControlBadChecksumError = 0x63686b73, bmdDeckControlUnknownError = 0x756e6572 } BMDDeckControlError; #if 0 #endif typedef /* [v1_enum] */ enum _BMDStreamingDeviceMode { bmdStreamingDeviceIdle = 0x69646c65, bmdStreamingDeviceEncoding = 0x656e636f, bmdStreamingDeviceStopping = 0x73746f70, bmdStreamingDeviceUnknown = 0x6d756e6b } BMDStreamingDeviceMode; typedef /* [v1_enum] */ enum _BMDStreamingEncodingFrameRate { bmdStreamingEncodedFrameRate50i = 0x65353069, bmdStreamingEncodedFrameRate5994i = 0x65353969, bmdStreamingEncodedFrameRate60i = 0x65363069, bmdStreamingEncodedFrameRate2398p = 0x65323370, bmdStreamingEncodedFrameRate24p = 0x65323470, bmdStreamingEncodedFrameRate25p = 0x65323570, bmdStreamingEncodedFrameRate2997p = 0x65323970, bmdStreamingEncodedFrameRate30p = 0x65333070, bmdStreamingEncodedFrameRate50p = 0x65353070, bmdStreamingEncodedFrameRate5994p = 0x65353970, bmdStreamingEncodedFrameRate60p = 0x65363070 } BMDStreamingEncodingFrameRate; typedef /* [v1_enum] */ enum _BMDStreamingEncodingSupport { bmdStreamingEncodingModeNotSupported = 0, bmdStreamingEncodingModeSupported = ( bmdStreamingEncodingModeNotSupported + 1 ) , bmdStreamingEncodingModeSupportedWithChanges = ( bmdStreamingEncodingModeSupported + 1 ) } BMDStreamingEncodingSupport; typedef /* [v1_enum] */ enum _BMDStreamingVideoCodec { bmdStreamingVideoCodecH264 = 0x48323634 } BMDStreamingVideoCodec; typedef /* [v1_enum] */ enum _BMDStreamingH264Profile { bmdStreamingH264ProfileHigh = 0x68696768, bmdStreamingH264ProfileMain = 0x6d61696e, bmdStreamingH264ProfileBaseline = 0x62617365 } BMDStreamingH264Profile; typedef /* [v1_enum] */ enum _BMDStreamingH264Level { bmdStreamingH264Level12 = 0x6c763132, bmdStreamingH264Level13 = 0x6c763133, bmdStreamingH264Level2 = 0x6c763220, bmdStreamingH264Level21 = 0x6c763231, bmdStreamingH264Level22 = 0x6c763232, bmdStreamingH264Level3 = 0x6c763320, bmdStreamingH264Level31 = 0x6c763331, bmdStreamingH264Level32 = 0x6c763332, bmdStreamingH264Level4 = 0x6c763420, bmdStreamingH264Level41 = 0x6c763431, bmdStreamingH264Level42 = 0x6c763432 } BMDStreamingH264Level; typedef /* [v1_enum] */ enum _BMDStreamingH264EntropyCoding { bmdStreamingH264EntropyCodingCAVLC = 0x45564c43, bmdStreamingH264EntropyCodingCABAC = 0x45424143 } BMDStreamingH264EntropyCoding; typedef /* [v1_enum] */ enum _BMDStreamingAudioCodec { bmdStreamingAudioCodecAAC = 0x41414320 } BMDStreamingAudioCodec; typedef /* [v1_enum] */ enum _BMDStreamingEncodingModePropertyID { bmdStreamingEncodingPropertyVideoFrameRate = 0x76667274, bmdStreamingEncodingPropertyVideoBitRateKbps = 0x76627274, bmdStreamingEncodingPropertyH264Profile = 0x68707266, bmdStreamingEncodingPropertyH264Level = 0x686c766c, bmdStreamingEncodingPropertyH264EntropyCoding = 0x68656e74, bmdStreamingEncodingPropertyH264HasBFrames = 0x68426672, bmdStreamingEncodingPropertyAudioCodec = 0x61636463, bmdStreamingEncodingPropertyAudioSampleRate = 0x61737274, bmdStreamingEncodingPropertyAudioChannelCount = 0x61636863, bmdStreamingEncodingPropertyAudioBitRateKbps = 0x61627274 } BMDStreamingEncodingModePropertyID; typedef unsigned int BMDBufferAccessFlags; typedef unsigned int BMDFrameFlags; typedef unsigned int BMDVideoInputFlags; typedef unsigned int BMDVideoInputFormatChangedEvents; typedef unsigned int BMDDetectedVideoInputFormatFlags; typedef unsigned int BMDDeckLinkCapturePassthroughMode; typedef unsigned int BMDAnalogVideoFlags; typedef unsigned int BMDFormatFlags; typedef unsigned int BMDDeviceBusyState; #if 0 typedef enum _BMDBufferAccessFlags BMDBufferAccessFlags; typedef enum _BMDFrameFlags BMDFrameFlags; typedef enum _BMDVideoInputFlags BMDVideoInputFlags; typedef enum _BMDVideoInputFormatChangedEvents BMDVideoInputFormatChangedEvents; typedef enum _BMDDetectedVideoInputFormatFlags BMDDetectedVideoInputFormatFlags; typedef enum _BMDDeckLinkCapturePassthroughMode BMDDeckLinkCapturePassthroughMode; typedef enum _BMDAnalogVideoFlags BMDAnalogVideoFlags; typedef enum _BMDFormatFlags BMDFormatFlags; typedef enum _BMDDeviceBusyState BMDDeviceBusyState; #endif /* [v1_enum] */ enum _BMDBufferAccessFlags { bmdBufferAccessReadAndWrite = ( ( 1 << 0 ) | ( 1 << 1 ) ) , bmdBufferAccessRead = ( 1 << 0 ) , bmdBufferAccessWrite = ( 1 << 1 ) } ; typedef /* [v1_enum] */ enum _BMDVideoOutputFlags { bmdVideoOutputFlagDefault = 0, bmdVideoOutputVANC = ( 1 << 0 ) , bmdVideoOutputVITC = ( 1 << 1 ) , bmdVideoOutputRP188 = ( 1 << 2 ) , bmdVideoOutputDualStream3D = ( 1 << 4 ) , bmdVideoOutputSynchronizeToPlaybackGroup = ( 1 << 6 ) , bmdVideoOutputDolbyVision = ( 1 << 7 ) } BMDVideoOutputFlags; typedef /* [v1_enum] */ enum _BMDSupportedVideoModeFlags { bmdSupportedVideoModeDefault = 0, bmdSupportedVideoModeKeying = ( 1 << 0 ) , bmdSupportedVideoModeDualStream3D = ( 1 << 1 ) , bmdSupportedVideoModeSDISingleLink = ( 1 << 2 ) , bmdSupportedVideoModeSDIDualLink = ( 1 << 3 ) , bmdSupportedVideoModeSDIQuadLink = ( 1 << 4 ) , bmdSupportedVideoModeInAnyProfile = ( 1 << 5 ) , bmdSupportedVideoModePsF = ( 1 << 6 ) , bmdSupportedVideoModeDolbyVision = ( 1 << 7 ) } BMDSupportedVideoModeFlags; typedef /* [v1_enum] */ enum _BMDPacketType { bmdPacketTypeStreamInterruptedMarker = 0x73696e74, bmdPacketTypeStreamData = 0x73646174 } BMDPacketType; /* [v1_enum] */ enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = ( 1 << 0 ) , bmdFrameFlagMonitorOutOnly = ( 1 << 3 ) , bmdFrameContainsHDRMetadata = ( 1 << 1 ) , bmdFrameContainsDolbyVisionMetadata = ( 1 << 4 ) , bmdFrameCapturedAsPsF = ( 1 << 30 ) , bmdFrameHasNoInputSource = ( 1 << 31 ) } ; /* [v1_enum] */ enum _BMDVideoInputFlags { bmdVideoInputFlagDefault = 0, bmdVideoInputEnableFormatDetection = ( 1 << 0 ) , bmdVideoInputDualStream3D = ( 1 << 1 ) , bmdVideoInputSynchronizeToCaptureGroup = ( 1 << 2 ) } ; /* [v1_enum] */ enum _BMDVideoInputFormatChangedEvents { bmdVideoInputDisplayModeChanged = ( 1 << 0 ) , bmdVideoInputFieldDominanceChanged = ( 1 << 1 ) , bmdVideoInputColorspaceChanged = ( 1 << 2 ) } ; /* [v1_enum] */ enum _BMDDetectedVideoInputFormatFlags { bmdDetectedVideoInputYCbCr422 = ( 1 << 0 ) , bmdDetectedVideoInputRGB444 = ( 1 << 1 ) , bmdDetectedVideoInputDualStream3D = ( 1 << 2 ) , bmdDetectedVideoInput12BitDepth = ( 1 << 3 ) , bmdDetectedVideoInput10BitDepth = ( 1 << 4 ) , bmdDetectedVideoInput8BitDepth = ( 1 << 5 ) } ; /* [v1_enum] */ enum _BMDDeckLinkCapturePassthroughMode { bmdDeckLinkCapturePassthroughModeDisabled = 0x70646973, bmdDeckLinkCapturePassthroughModeDirect = 0x70646972, bmdDeckLinkCapturePassthroughModeCleanSwitch = 0x70636c6e } ; typedef /* [v1_enum] */ enum _BMDOutputFrameCompletionResult { bmdOutputFrameCompleted = 0, bmdOutputFrameDisplayedLate = ( bmdOutputFrameCompleted + 1 ) , bmdOutputFrameDropped = ( bmdOutputFrameDisplayedLate + 1 ) , bmdOutputFrameFlushed = ( bmdOutputFrameDropped + 1 ) } BMDOutputFrameCompletionResult; typedef /* [v1_enum] */ enum _BMDReferenceStatus { bmdReferenceUnlocked = 0, bmdReferenceNotSupportedByHardware = ( 1 << 0 ) , bmdReferenceLocked = ( 1 << 1 ) } BMDReferenceStatus; typedef /* [v1_enum] */ enum _BMDAudioFormat { bmdAudioFormatPCM = 0x6c70636d } BMDAudioFormat; typedef /* [v1_enum] */ enum _BMDAudioSampleRate { bmdAudioSampleRate48kHz = 48000 } BMDAudioSampleRate; typedef /* [v1_enum] */ enum _BMDAudioSampleType { bmdAudioSampleType16bitInteger = 16, bmdAudioSampleType32bitInteger = 32 } BMDAudioSampleType; typedef /* [v1_enum] */ enum _BMDAudioOutputStreamType { bmdAudioOutputStreamContinuous = 0, bmdAudioOutputStreamContinuousDontResample = ( bmdAudioOutputStreamContinuous + 1 ) , bmdAudioOutputStreamTimestamped = ( bmdAudioOutputStreamContinuousDontResample + 1 ) } BMDAudioOutputStreamType; typedef /* [v1_enum] */ enum _BMDAncillaryPacketFormat { bmdAncillaryPacketFormatUInt8 = 0x75693038, bmdAncillaryPacketFormatUInt16 = 0x75693136, bmdAncillaryPacketFormatYCbCr10 = 0x76323130 } BMDAncillaryPacketFormat; typedef /* [v1_enum] */ enum _BMDTimecodeFormat { bmdTimecodeRP188VITC1 = 0x72707631, bmdTimecodeRP188VITC2 = 0x72703132, bmdTimecodeRP188LTC = 0x72706c74, bmdTimecodeRP188HighFrameRate = 0x72706872, bmdTimecodeRP188Any = 0x72703138, bmdTimecodeVITC = 0x76697463, bmdTimecodeVITCField2 = 0x76697432, bmdTimecodeSerial = 0x73657269 } BMDTimecodeFormat; /* [v1_enum] */ enum _BMDAnalogVideoFlags { bmdAnalogVideoFlagCompositeSetup75 = ( 1 << 0 ) , bmdAnalogVideoFlagComponentBetacamLevels = ( 1 << 1 ) } ; typedef /* [v1_enum] */ enum _BMDAudioOutputAnalogAESSwitch { bmdAudioOutputSwitchAESEBU = 0x61657320, bmdAudioOutputSwitchAnalog = 0x616e6c67 } BMDAudioOutputAnalogAESSwitch; typedef /* [v1_enum] */ enum _BMDVideoOutputConversionMode { bmdNoVideoOutputConversion = 0x6e6f6e65, bmdVideoOutputLetterboxDownconversion = 0x6c746278, bmdVideoOutputAnamorphicDownconversion = 0x616d7068, bmdVideoOutputHD720toHD1080Conversion = 0x37323063, bmdVideoOutputHardwareLetterboxDownconversion = 0x48576c62, bmdVideoOutputHardwareAnamorphicDownconversion = 0x4857616d, bmdVideoOutputHardwareCenterCutDownconversion = 0x48576363, bmdVideoOutputHardware720p1080pCrossconversion = 0x78636170, bmdVideoOutputHardwareAnamorphic720pUpconversion = 0x75613770, bmdVideoOutputHardwareAnamorphic1080iUpconversion = 0x75613169, bmdVideoOutputHardwareAnamorphic149To720pUpconversion = 0x75343770, bmdVideoOutputHardwareAnamorphic149To1080iUpconversion = 0x75343169, bmdVideoOutputHardwarePillarbox720pUpconversion = 0x75703770, bmdVideoOutputHardwarePillarbox1080iUpconversion = 0x75703169 } BMDVideoOutputConversionMode; typedef /* [v1_enum] */ enum _BMDVideoInputConversionMode { bmdNoVideoInputConversion = 0x6e6f6e65, bmdVideoInputLetterboxDownconversionFromHD1080 = 0x31306c62, bmdVideoInputAnamorphicDownconversionFromHD1080 = 0x3130616d, bmdVideoInputLetterboxDownconversionFromHD720 = 0x37326c62, bmdVideoInputAnamorphicDownconversionFromHD720 = 0x3732616d, bmdVideoInputLetterboxUpconversion = 0x6c627570, bmdVideoInputAnamorphicUpconversion = 0x616d7570 } BMDVideoInputConversionMode; typedef /* [v1_enum] */ enum _BMDVideo3DPackingFormat { bmdVideo3DPackingSidebySideHalf = 0x73627368, bmdVideo3DPackingLinebyLine = 0x6c62796c, bmdVideo3DPackingTopAndBottom = 0x7461626f, bmdVideo3DPackingFramePacking = 0x6672706b, bmdVideo3DPackingLeftOnly = 0x6c656674, bmdVideo3DPackingRightOnly = 0x72696768 } BMDVideo3DPackingFormat; typedef /* [v1_enum] */ enum _BMDIdleVideoOutputOperation { bmdIdleVideoOutputBlack = 0x626c6163, bmdIdleVideoOutputLastFrame = 0x6c616661 } BMDIdleVideoOutputOperation; typedef /* [v1_enum] */ enum _BMDVideoEncoderFrameCodingMode { bmdVideoEncoderFrameCodingModeInter = 0x696e7465, bmdVideoEncoderFrameCodingModeIntra = 0x696e7472 } BMDVideoEncoderFrameCodingMode; typedef /* [v1_enum] */ enum _BMDDNxHRLevel { bmdDNxHRLevelSQ = 0x646e7371, bmdDNxHRLevelLB = 0x646e6c62, bmdDNxHRLevelHQ = 0x646e6871, bmdDNxHRLevelHQX = 0x64687178, bmdDNxHRLevel444 = 0x64343434 } BMDDNxHRLevel; typedef /* [v1_enum] */ enum _BMDLinkConfiguration { bmdLinkConfigurationSingleLink = 0x6c63736c, bmdLinkConfigurationDualLink = 0x6c63646c, bmdLinkConfigurationQuadLink = 0x6c63716c } BMDLinkConfiguration; typedef /* [v1_enum] */ enum _BMDDeviceInterface { bmdDeviceInterfacePCI = 0x70636920, bmdDeviceInterfaceUSB = 0x75736220, bmdDeviceInterfaceThunderbolt = 0x7468756e } BMDDeviceInterface; typedef /* [v1_enum] */ enum _BMDColorspace { bmdColorspaceRec601 = 0x72363031, bmdColorspaceRec709 = 0x72373039, bmdColorspaceRec2020 = 0x32303230, bmdColorspaceDolbyVisionNative = 0x446f5669, bmdColorspaceP3D65 = 0x50334436, bmdColorspaceUnknown = 0x4e636f6c } BMDColorspace; typedef /* [v1_enum] */ enum _BMDDynamicRange { bmdDynamicRangeSDR = 0, bmdDynamicRangeHDRStaticPQ = ( 1 << 29 ) , bmdDynamicRangeHDRStaticHLG = ( 1 << 30 ) } BMDDynamicRange; typedef /* [v1_enum] */ enum _BMDMezzanineType { bmdMezzanineTypeNone = 0, bmdMezzanineTypeHDMI14OpticalSDI = 0x6d7a6131, bmdMezzanineTypeQuadSDI = 0x6d7a3473, bmdMezzanineTypeHDMI20OpticalSDI = 0x6d7a6132, bmdMezzanineTypeHDMI21RS422 = 0x6d7a6872 } BMDMezzanineType; typedef /* [v1_enum] */ enum _BMDDeckLinkHDMIInputEDIDID { bmdDeckLinkHDMIInputEDIDDynamicRange = 0x48494479 } BMDDeckLinkHDMIInputEDIDID; typedef /* [v1_enum] */ enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataColorspace = 0x63737063, bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = 0x656f7466, bmdDeckLinkFrameMetadataDolbyVision = 0x646f7669, bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = 0x68647278, bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = 0x68647279, bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = 0x68646778, bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY = 0x68646779, bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX = 0x68646278, bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY = 0x68646279, bmdDeckLinkFrameMetadataHDRWhitePointX = 0x68647778, bmdDeckLinkFrameMetadataHDRWhitePointY = 0x68647779, bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = 0x68646d6c, bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = 0x686d696c, bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = 0x6d636c6c, bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = 0x66616c6c } BMDDeckLinkFrameMetadataID; typedef /* [v1_enum] */ enum _BMDEthernetLinkState { bmdEthernetLinkStateDisconnected = 0x656c6473, bmdEthernetLinkStateConnectedUnbound = 0x656c6375, bmdEthernetLinkStateConnectedBound = 0x656c6362 } BMDEthernetLinkState; typedef /* [v1_enum] */ enum _BMDProfileID { bmdProfileOneSubDeviceFullDuplex = 0x31646664, bmdProfileOneSubDeviceHalfDuplex = 0x31646864, bmdProfileTwoSubDevicesFullDuplex = 0x32646664, bmdProfileTwoSubDevicesHalfDuplex = 0x32646864, bmdProfileFourSubDevicesHalfDuplex = 0x34646864 } BMDProfileID; typedef /* [v1_enum] */ enum _BMDHDMITimecodePacking { bmdHDMITimecodePackingIEEEOUI000085 = 0x8500, bmdHDMITimecodePackingIEEEOUI080046 = 0x8004601, bmdHDMITimecodePackingIEEEOUI5CF9F0 = 0x5cf9f003 } BMDHDMITimecodePacking; typedef /* [v1_enum] */ enum _BMDInternalKeyingAncillaryDataSource { bmdInternalKeyingUsesAncillaryDataFromInputSignal = 0x696b6169, bmdInternalKeyingUsesAncillaryDataFromKeyFrame = 0x696b616b } BMDInternalKeyingAncillaryDataSource; typedef /* [v1_enum] */ enum _BMDDeckLinkAttributeID { BMDDeckLinkSupportsInternalKeying = 0x6b657969, BMDDeckLinkSupportsExternalKeying = 0x6b657965, BMDDeckLinkSupportsInputFormatDetection = 0x696e6664, BMDDeckLinkHasReferenceInput = 0x6872696e, BMDDeckLinkHasSerialPort = 0x68737074, BMDDeckLinkHasAnalogVideoOutputGain = 0x61766f67, BMDDeckLinkCanOnlyAdjustOverallVideoOutputGain = 0x6f766f67, BMDDeckLinkHasVideoInputAntiAliasingFilter = 0x6161666c, BMDDeckLinkHasBypass = 0x62797073, BMDDeckLinkSupportsClockTimingAdjustment = 0x63746164, BMDDeckLinkSupportsFullFrameReferenceInputTimingOffset = 0x6672696e, BMDDeckLinkSupportsSMPTELevelAOutput = 0x6c766c61, BMDDeckLinkSupportsAutoSwitchingPPsFOnInput = 0x61707366, BMDDeckLinkSupportsDualLinkSDI = 0x73646c73, BMDDeckLinkSupportsQuadLinkSDI = 0x73716c73, BMDDeckLinkSupportsIdleOutput = 0x69646f75, BMDDeckLinkVANCRequires10BitYUVVideoFrames = 0x76696f59, BMDDeckLinkHasLTCTimecodeInput = 0x686c7463, BMDDeckLinkSupportsHDRMetadata = 0x6864726d, BMDDeckLinkSupportsColorspaceMetadata = 0x636d6574, BMDDeckLinkSupportsHDMITimecode = 0x6874696d, BMDDeckLinkSupportsHighFrameRateTimecode = 0x48465254, BMDDeckLinkSupportsSynchronizeToCaptureGroup = 0x73746367, BMDDeckLinkSupportsSynchronizeToPlaybackGroup = 0x73747067, BMDDeckLinkHasMonitorOut = 0x666d6f6f, BMDDeckLinkMaximumAudioChannels = 0x6d616368, BMDDeckLinkMaximumHDMIAudioChannels = 0x6d686368, BMDDeckLinkMaximumAnalogAudioInputChannels = 0x69616368, BMDDeckLinkMaximumAnalogAudioOutputChannels = 0x61616368, BMDDeckLinkNumberOfSubDevices = 0x6e736264, BMDDeckLinkSubDeviceIndex = 0x73756269, BMDDeckLinkPersistentID = 0x70656964, BMDDeckLinkDeviceGroupID = 0x64676964, BMDDeckLinkTopologicalID = 0x746f6964, BMDDeckLinkVideoOutputConnections = 0x766f636e, BMDDeckLinkVideoInputConnections = 0x7669636e, BMDDeckLinkAudioOutputConnections = 0x616f636e, BMDDeckLinkAudioInputConnections = 0x6169636e, BMDDeckLinkVideoIOSupport = 0x76696f73, BMDDeckLinkDeckControlConnections = 0x6463636e, BMDDeckLinkDeviceInterface = 0x64627573, BMDDeckLinkAudioInputRCAChannelCount = 0x61697263, BMDDeckLinkAudioInputXLRChannelCount = 0x61697863, BMDDeckLinkAudioOutputRCAChannelCount = 0x616f7263, BMDDeckLinkAudioOutputXLRChannelCount = 0x616f7863, BMDDeckLinkProfileID = 0x70726964, BMDDeckLinkDuplex = 0x64757078, BMDDeckLinkMinimumPrerollFrames = 0x6d707266, BMDDeckLinkSupportedDynamicRange = 0x73756472, BMDDeckLinkMezzanineType = 0x6d657a74, BMDDeckLinkVideoInputGainMinimum = 0x7669676d, BMDDeckLinkVideoInputGainMaximum = 0x76696778, BMDDeckLinkVideoOutputGainMinimum = 0x766f676d, BMDDeckLinkVideoOutputGainMaximum = 0x766f6778, BMDDeckLinkMicrophoneInputGainMinimum = 0x6d69676d, BMDDeckLinkMicrophoneInputGainMaximum = 0x6d696778, BMDDeckLinkSerialPortDeviceName = 0x736c706e, BMDDeckLinkVendorName = 0x766e6472, BMDDeckLinkDisplayName = 0x6473706e, BMDDeckLinkModelName = 0x6d646c6e, BMDDeckLinkDeviceHandle = 0x64657668, BMDDeckLinkEthernetMACAddress = 0x654d4143 } BMDDeckLinkAttributeID; typedef /* [v1_enum] */ enum _BMDDeckLinkAPIInformationID { BMDDeckLinkAPIVersion = 0x76657273 } BMDDeckLinkAPIInformationID; typedef /* [v1_enum] */ enum _BMDDeckLinkStatusID { bmdDeckLinkStatusDetectedVideoInputMode = 0x6476696d, bmdDeckLinkStatusDetectedVideoInputFormatFlags = 0x64766666, bmdDeckLinkStatusDetectedVideoInputFieldDominance = 0x64766664, bmdDeckLinkStatusDetectedVideoInputColorspace = 0x6473636c, bmdDeckLinkStatusDetectedVideoInputDynamicRange = 0x64736472, bmdDeckLinkStatusDetectedSDILinkConfiguration = 0x64736c63, bmdDeckLinkStatusCurrentVideoInputMode = 0x6376696d, bmdDeckLinkStatusCurrentVideoInputPixelFormat = 0x63766970, bmdDeckLinkStatusCurrentVideoInputFlags = 0x63766966, bmdDeckLinkStatusCurrentVideoOutputMode = 0x63766f6d, bmdDeckLinkStatusCurrentVideoOutputFlags = 0x63766f66, bmdDeckLinkStatusEthernetLink = 0x73656c73, bmdDeckLinkStatusEthernetLinkMbps = 0x73657370, bmdDeckLinkStatusPCIExpressLinkWidth = 0x70776964, bmdDeckLinkStatusPCIExpressLinkSpeed = 0x706c6e6b, bmdDeckLinkStatusLastVideoOutputPixelFormat = 0x6f706978, bmdDeckLinkStatusReferenceSignalMode = 0x7265666d, bmdDeckLinkStatusReferenceSignalFlags = 0x72656666, bmdDeckLinkStatusBusy = 0x62757379, bmdDeckLinkStatusInterchangeablePanelType = 0x69637074, bmdDeckLinkStatusDeviceTemperature = 0x64746d70, bmdDeckLinkStatusHDMIOutputActualMode = 0x6869616d, bmdDeckLinkStatusHDMIOutputActualFormatFlags = 0x68696166, bmdDeckLinkStatusHDMIOutputFRLRate = 0x68696f66, bmdDeckLinkStatusHDMIInputFRLRate = 0x68696966, bmdDeckLinkStatusHDMIOutputTMDSLineRate = 0x68696c72, bmdDeckLinkStatusSinkSupportsDolbyVision = 0x64767672, bmdDeckLinkStatusVideoInputSignalLocked = 0x7669736c, bmdDeckLinkStatusReferenceSignalLocked = 0x7265666c, bmdDeckLinkStatusEthernetLocalIPAddress = 0x73656970, bmdDeckLinkStatusEthernetSubnetMask = 0x7365736d, bmdDeckLinkStatusEthernetGatewayIPAddress = 0x73656777, bmdDeckLinkStatusEthernetPrimaryDNS = 0x73657064, bmdDeckLinkStatusEthernetSecondaryDNS = 0x73657364, bmdDeckLinkStatusEthernetPTPGrandmasterIdentity = 0x73706964, bmdDeckLinkStatusEthernetVideoOutputAddress = 0x736f6176, bmdDeckLinkStatusEthernetAudioOutputAddress = 0x736f6161, bmdDeckLinkStatusEthernetAncillaryOutputAddress = 0x736f6141, bmdDeckLinkStatusEthernetAudioInputChannelOrder = 0x7361636f, bmdDeckLinkStatusReceivedEDID = 0x65646964 } BMDDeckLinkStatusID; typedef /* [v1_enum] */ enum _BMDDeckLinkVideoStatusFlags { bmdDeckLinkVideoStatusPsF = ( 1 << 0 ) , bmdDeckLinkVideoStatusDualStream3D = ( 1 << 1 ) } BMDDeckLinkVideoStatusFlags; typedef /* [v1_enum] */ enum _BMDDuplexMode { bmdDuplexFull = 0x64786675, bmdDuplexHalf = 0x64786861, bmdDuplexSimplex = 0x64787370, bmdDuplexInactive = 0x6478696e } BMDDuplexMode; typedef /* [v1_enum] */ enum _BMDPanelType { bmdPanelNotDetected = 0x6e706e6c, bmdPanelTeranexMiniSmartPanel = 0x746d736d } BMDPanelType; /* [v1_enum] */ enum _BMDFormatFlags { bmdFormatRGB444 = ( 1 << 0 ) , bmdFormatYUV444 = ( 1 << 1 ) , bmdFormatYUV422 = ( 1 << 2 ) , bmdFormatYUV420 = ( 1 << 3 ) , bmdFormat8BitDepth = ( 1 << 4 ) , bmdFormat10BitDepth = ( 1 << 5 ) , bmdFormat12BitDepth = ( 1 << 6 ) } ; /* [v1_enum] */ enum _BMDDeviceBusyState { bmdDeviceCaptureBusy = ( 1 << 0 ) , bmdDevicePlaybackBusy = ( 1 << 1 ) , bmdDeviceSerialPortBusy = ( 1 << 2 ) } ; typedef /* [v1_enum] */ enum _BMDVideoIOSupport { bmdDeviceSupportsCapture = ( 1 << 0 ) , bmdDeviceSupportsPlayback = ( 1 << 1 ) } BMDVideoIOSupport; typedef /* [v1_enum] */ enum _BMD3DPreviewFormat { bmd3DPreviewFormatDefault = 0x64656661, bmd3DPreviewFormatLeftOnly = 0x6c656674, bmd3DPreviewFormatRightOnly = 0x72696768, bmd3DPreviewFormatSideBySide = 0x73696465, bmd3DPreviewFormatTopBottom = 0x746f7062 } BMD3DPreviewFormat; typedef /* [v1_enum] */ enum _BMDIPFlowDirection { bmdDeckLinkIPFlowDirectionOutput = 0, bmdDeckLinkIPFlowDirectionInput = 1 } BMDIPFlowDirection; typedef /* [v1_enum] */ enum _BMDIPFlowType { bmdDeckLinkIPFlowTypeVideo = 0, bmdDeckLinkIPFlowTypeAudio = 1, bmdDeckLinkIPFlowTypeAncillary = 2 } BMDIPFlowType; typedef /* [v1_enum] */ enum _BMDDeckLinkIPFlowAttributeID { bmdDeckLinkIPFlowID = 0x32666169, bmdDeckLinkIPFlowDirection = 0x32666164, bmdDeckLinkIPFlowType = 0x32666174 } BMDDeckLinkIPFlowAttributeID; typedef /* [v1_enum] */ enum _BMDDeckLinkIPFlowStatusID { bmdDeckLinkIPFlowSDP = 0x32666173 } BMDDeckLinkIPFlowStatusID; typedef /* [v1_enum] */ enum _BMDDeckLinkIPFlowSettingID { bmdDeckLinkIPFlowPeerSDP = 0x32667073 } BMDDeckLinkIPFlowSettingID; typedef /* [v1_enum] */ enum _BMDNotifications { bmdPreferencesChanged = 0x70726566, bmdStatusChanged = 0x73746174, bmdIPFlowStatusChanged = 0x62667363, bmdIPFlowSettingChanged = 0x62666363 } BMDNotifications; typedef /* [v1_enum] */ enum _BMDDeckLinkStatusID_v11_5_1 { bmdDeckLinkStatusDetectedVideoInputFlags_v11_5_1 = 0x64766966 } BMDDeckLinkStatusID_v11_5_1; typedef /* [v1_enum] */ enum _BMDDisplayModeSupport_v10_11 { bmdDisplayModeNotSupported_v10_11 = 0, bmdDisplayModeSupported_v10_11 = ( bmdDisplayModeNotSupported_v10_11 + 1 ) , bmdDisplayModeSupportedWithConversion_v10_11 = ( bmdDisplayModeSupported_v10_11 + 1 ) } BMDDisplayModeSupport_v10_11; typedef /* [v1_enum] */ enum _BMDDuplexMode_v10_11 { bmdDuplexModeFull_v10_11 = 0x66647570, bmdDuplexModeHalf_v10_11 = 0x68647570 } BMDDuplexMode_v10_11; typedef /* [v1_enum] */ enum _BMDDeckLinkConfigurationID_v10_11 { bmdDeckLinkConfigDuplexMode_v10_11 = 0x64757078 } BMDDeckLinkConfigurationID_v10_11; typedef /* [v1_enum] */ enum _BMDDeckLinkAttributeID_v10_11 { BMDDeckLinkSupportsDuplexModeConfiguration_v10_11 = 0x64757078, BMDDeckLinkSupportsHDKeying_v10_11 = 0x6b657968, BMDDeckLinkPairedDevicePersistentID_v10_11 = 0x70706964, BMDDeckLinkSupportsFullDuplex_v10_11 = 0x66647570 } BMDDeckLinkAttributeID_v10_11; typedef /* [v1_enum] */ enum _BMDDeckLinkStatusID_v10_11 { bmdDeckLinkStatusDuplexMode_v10_11 = 0x64757078 } BMDDeckLinkStatusID_v10_11; typedef /* [v1_enum] */ enum _BMDDuplexStatus_v10_11 { bmdDuplexFullDuplex_v10_11 = 0x66647570, bmdDuplexHalfDuplex_v10_11 = 0x68647570, bmdDuplexSimplex_v10_11 = 0x73706c78, bmdDuplexInactive_v10_11 = 0x696e6163 } BMDDuplexStatus_v10_11; typedef /* [v1_enum] */ enum _BMDDeckLinkConfigurationID_v10_9 { bmdDeckLinkConfig1080pNotPsF_v10_9 = 0x6670726f } BMDDeckLinkConfigurationID_v10_9; typedef /* [v1_enum] */ enum _BMDDeckLinkConfigurationID_v10_4 { bmdDeckLinkConfigSingleLinkVideoOutput_v10_4 = 0x73676c6f } BMDDeckLinkConfigurationID_v10_4; typedef /* [v1_enum] */ enum _BMDDeckLinkConfigurationID_v10_2 { bmdDeckLinkConfig3GBpsVideoOutput_v10_2 = 0x33676273 } BMDDeckLinkConfigurationID_v10_2; typedef /* [v1_enum] */ enum _BMDAudioConnection_v10_2 { bmdAudioConnectionEmbedded_v10_2 = 0x656d6264, bmdAudioConnectionAESEBU_v10_2 = 0x61657320, bmdAudioConnectionAnalog_v10_2 = 0x616e6c67, bmdAudioConnectionAnalogXLR_v10_2 = 0x61786c72, bmdAudioConnectionAnalogRCA_v10_2 = 0x61726361 } BMDAudioConnection_v10_2; typedef /* [v1_enum] */ enum _BMDDeckLinkFrameMetadataID_v11_5 { bmdDeckLinkFrameMetadataCintelFilmType_v11_5 = 0x63667479, bmdDeckLinkFrameMetadataCintelFilmGauge_v11_5 = 0x63666761, bmdDeckLinkFrameMetadataCintelKeykodeLow_v11_5 = 0x636b6b6c, bmdDeckLinkFrameMetadataCintelKeykodeHigh_v11_5 = 0x636b6b68, bmdDeckLinkFrameMetadataCintelTile1Size_v11_5 = 0x63743173, bmdDeckLinkFrameMetadataCintelTile2Size_v11_5 = 0x63743273, bmdDeckLinkFrameMetadataCintelTile3Size_v11_5 = 0x63743373, bmdDeckLinkFrameMetadataCintelTile4Size_v11_5 = 0x63743473, bmdDeckLinkFrameMetadataCintelImageWidth_v11_5 = 0x49575078, bmdDeckLinkFrameMetadataCintelImageHeight_v11_5 = 0x49485078, bmdDeckLinkFrameMetadataCintelLinearMaskingRedInRed_v11_5 = 0x6d726972, bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInRed_v11_5 = 0x6d676972, bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInRed_v11_5 = 0x6d626972, bmdDeckLinkFrameMetadataCintelLinearMaskingRedInGreen_v11_5 = 0x6d726967, bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInGreen_v11_5 = 0x6d676967, bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInGreen_v11_5 = 0x6d626967, bmdDeckLinkFrameMetadataCintelLinearMaskingRedInBlue_v11_5 = 0x6d726962, bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInBlue_v11_5 = 0x6d676962, bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInBlue_v11_5 = 0x6d626962, bmdDeckLinkFrameMetadataCintelLogMaskingRedInRed_v11_5 = 0x6d6c7272, bmdDeckLinkFrameMetadataCintelLogMaskingGreenInRed_v11_5 = 0x6d6c6772, bmdDeckLinkFrameMetadataCintelLogMaskingBlueInRed_v11_5 = 0x6d6c6272, bmdDeckLinkFrameMetadataCintelLogMaskingRedInGreen_v11_5 = 0x6d6c7267, bmdDeckLinkFrameMetadataCintelLogMaskingGreenInGreen_v11_5 = 0x6d6c6767, bmdDeckLinkFrameMetadataCintelLogMaskingBlueInGreen_v11_5 = 0x6d6c6267, bmdDeckLinkFrameMetadataCintelLogMaskingRedInBlue_v11_5 = 0x6d6c7262, bmdDeckLinkFrameMetadataCintelLogMaskingGreenInBlue_v11_5 = 0x6d6c6762, bmdDeckLinkFrameMetadataCintelLogMaskingBlueInBlue_v11_5 = 0x6d6c6262, bmdDeckLinkFrameMetadataCintelFilmFrameRate_v11_5 = 0x63666672, bmdDeckLinkFrameMetadataCintelOffsetToApplyHorizontal_v11_5 = 0x6f746168, bmdDeckLinkFrameMetadataCintelOffsetToApplyVertical_v11_5 = 0x6f746176, bmdDeckLinkFrameMetadataCintelGainRed_v11_5 = 0x4c665264, bmdDeckLinkFrameMetadataCintelGainGreen_v11_5 = 0x4c664772, bmdDeckLinkFrameMetadataCintelGainBlue_v11_5 = 0x4c66426c, bmdDeckLinkFrameMetadataCintelLiftRed_v11_5 = 0x476e5264, bmdDeckLinkFrameMetadataCintelLiftGreen_v11_5 = 0x476e4772, bmdDeckLinkFrameMetadataCintelLiftBlue_v11_5 = 0x476e426c, bmdDeckLinkFrameMetadataCintelHDRGainRed_v11_5 = 0x48475264, bmdDeckLinkFrameMetadataCintelHDRGainGreen_v11_5 = 0x48474772, bmdDeckLinkFrameMetadataCintelHDRGainBlue_v11_5 = 0x4847426c, bmdDeckLinkFrameMetadataCintel16mmCropRequired_v11_5 = 0x63313663, bmdDeckLinkFrameMetadataCintelInversionRequired_v11_5 = 0x63696e76, bmdDeckLinkFrameMetadataCintelFlipRequired_v11_5 = 0x63666c72, bmdDeckLinkFrameMetadataCintelFocusAssistEnabled_v11_5 = 0x63666165, bmdDeckLinkFrameMetadataCintelKeykodeIsInterpolated_v11_5 = 0x6b6b6969 } BMDDeckLinkFrameMetadataID_v11_5; typedef /* [v1_enum] */ enum _BMDDeckLinkAttributeID_v10_6 { BMDDeckLinkSupportsDesktopDisplay_v10_6 = 0x65787464 } BMDDeckLinkAttributeID_v10_6; typedef /* [v1_enum] */ enum _BMDIdleVideoOutputOperation_v10_6 { bmdIdleVideoOutputDesktop_v10_6 = 0x6465736b } BMDIdleVideoOutputOperation_v10_6; typedef /* [v1_enum] */ enum _BMDDeckLinkAttributeID_v10_5 { BMDDeckLinkDeviceBusyState_v10_5 = 0x64627374 } BMDDeckLinkAttributeID_v10_5; EXTERN_C const IID LIBID_DeckLinkAPI; #ifndef __IDeckLinkTimecode_INTERFACE_DEFINED__ #define __IDeckLinkTimecode_INTERFACE_DEFINED__ /* interface IDeckLinkTimecode */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkTimecode; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("BC6CFBD3-8317-4325-AC1C-1216391E9340") IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD STDMETHODCALLTYPE GetBCD( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetComponents( /* [out] */ unsigned char *hours, /* [out] */ unsigned char *minutes, /* [out] */ unsigned char *seconds, /* [out] */ unsigned char *frames) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [out] */ BSTR *timecode) = 0; virtual BMDTimecodeFlags STDMETHODCALLTYPE GetFlags( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecodeUserBits( /* [out] */ BMDTimecodeUserBits *userBits) = 0; }; #else /* C style interface */ typedef struct IDeckLinkTimecodeVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkTimecode * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkTimecode * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkTimecode * This); DECLSPEC_XFGVIRT(IDeckLinkTimecode, GetBCD) BMDTimecodeBCD ( STDMETHODCALLTYPE *GetBCD )( IDeckLinkTimecode * This); DECLSPEC_XFGVIRT(IDeckLinkTimecode, GetComponents) HRESULT ( STDMETHODCALLTYPE *GetComponents )( IDeckLinkTimecode * This, /* [out] */ unsigned char *hours, /* [out] */ unsigned char *minutes, /* [out] */ unsigned char *seconds, /* [out] */ unsigned char *frames); DECLSPEC_XFGVIRT(IDeckLinkTimecode, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkTimecode * This, /* [out] */ BSTR *timecode); DECLSPEC_XFGVIRT(IDeckLinkTimecode, GetFlags) BMDTimecodeFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkTimecode * This); DECLSPEC_XFGVIRT(IDeckLinkTimecode, GetTimecodeUserBits) HRESULT ( STDMETHODCALLTYPE *GetTimecodeUserBits )( IDeckLinkTimecode * This, /* [out] */ BMDTimecodeUserBits *userBits); END_INTERFACE } IDeckLinkTimecodeVtbl; interface IDeckLinkTimecode { CONST_VTBL struct IDeckLinkTimecodeVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkTimecode_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkTimecode_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkTimecode_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkTimecode_GetBCD(This) \ ( (This)->lpVtbl -> GetBCD(This) ) #define IDeckLinkTimecode_GetComponents(This,hours,minutes,seconds,frames) \ ( (This)->lpVtbl -> GetComponents(This,hours,minutes,seconds,frames) ) #define IDeckLinkTimecode_GetString(This,timecode) \ ( (This)->lpVtbl -> GetString(This,timecode) ) #define IDeckLinkTimecode_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkTimecode_GetTimecodeUserBits(This,userBits) \ ( (This)->lpVtbl -> GetTimecodeUserBits(This,userBits) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkTimecode_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDisplayModeIterator_INTERFACE_DEFINED__ #define __IDeckLinkDisplayModeIterator_INTERFACE_DEFINED__ /* interface IDeckLinkDisplayModeIterator */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDisplayModeIterator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("9C88499F-F601-4021-B80B-032E4EB41C35") IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLinkDisplayMode **deckLinkDisplayMode) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDisplayModeIteratorVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDisplayModeIterator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDisplayModeIterator * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDisplayModeIterator * This); DECLSPEC_XFGVIRT(IDeckLinkDisplayModeIterator, Next) HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkDisplayModeIterator * This, /* [out] */ IDeckLinkDisplayMode **deckLinkDisplayMode); END_INTERFACE } IDeckLinkDisplayModeIteratorVtbl; interface IDeckLinkDisplayModeIterator { CONST_VTBL struct IDeckLinkDisplayModeIteratorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDisplayModeIterator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDisplayModeIterator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDisplayModeIterator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDisplayModeIterator_Next(This,deckLinkDisplayMode) \ ( (This)->lpVtbl -> Next(This,deckLinkDisplayMode) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDisplayModeIterator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDisplayMode_INTERFACE_DEFINED__ #define __IDeckLinkDisplayMode_INTERFACE_DEFINED__ /* interface IDeckLinkDisplayMode */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDisplayMode; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3EB2C1AB-0A3D-4523-A3AD-F40D7FB14E78") IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetName( /* [out] */ BSTR *name) = 0; virtual BMDDisplayMode STDMETHODCALLTYPE GetDisplayMode( void) = 0; virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameRate( /* [out] */ BMDTimeValue *frameDuration, /* [out] */ BMDTimeScale *timeScale) = 0; virtual BMDFieldDominance STDMETHODCALLTYPE GetFieldDominance( void) = 0; virtual BMDDisplayModeFlags STDMETHODCALLTYPE GetFlags( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDisplayModeVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDisplayMode * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDisplayMode * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDisplayMode * This); DECLSPEC_XFGVIRT(IDeckLinkDisplayMode, GetName) HRESULT ( STDMETHODCALLTYPE *GetName )( IDeckLinkDisplayMode * This, /* [out] */ BSTR *name); DECLSPEC_XFGVIRT(IDeckLinkDisplayMode, GetDisplayMode) BMDDisplayMode ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkDisplayMode * This); DECLSPEC_XFGVIRT(IDeckLinkDisplayMode, GetWidth) long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkDisplayMode * This); DECLSPEC_XFGVIRT(IDeckLinkDisplayMode, GetHeight) long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkDisplayMode * This); DECLSPEC_XFGVIRT(IDeckLinkDisplayMode, GetFrameRate) HRESULT ( STDMETHODCALLTYPE *GetFrameRate )( IDeckLinkDisplayMode * This, /* [out] */ BMDTimeValue *frameDuration, /* [out] */ BMDTimeScale *timeScale); DECLSPEC_XFGVIRT(IDeckLinkDisplayMode, GetFieldDominance) BMDFieldDominance ( STDMETHODCALLTYPE *GetFieldDominance )( IDeckLinkDisplayMode * This); DECLSPEC_XFGVIRT(IDeckLinkDisplayMode, GetFlags) BMDDisplayModeFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkDisplayMode * This); END_INTERFACE } IDeckLinkDisplayModeVtbl; interface IDeckLinkDisplayMode { CONST_VTBL struct IDeckLinkDisplayModeVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDisplayMode_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDisplayMode_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDisplayMode_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDisplayMode_GetName(This,name) \ ( (This)->lpVtbl -> GetName(This,name) ) #define IDeckLinkDisplayMode_GetDisplayMode(This) \ ( (This)->lpVtbl -> GetDisplayMode(This) ) #define IDeckLinkDisplayMode_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkDisplayMode_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkDisplayMode_GetFrameRate(This,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetFrameRate(This,frameDuration,timeScale) ) #define IDeckLinkDisplayMode_GetFieldDominance(This) \ ( (This)->lpVtbl -> GetFieldDominance(This) ) #define IDeckLinkDisplayMode_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDisplayMode_INTERFACE_DEFINED__ */ #ifndef __IDeckLink_INTERFACE_DEFINED__ #define __IDeckLink_INTERFACE_DEFINED__ /* interface IDeckLink */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLink; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C418FBDD-0587-48ED-8FE5-640F0A14AF91") IDeckLink : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetModelName( /* [out] */ BSTR *modelName) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayName( /* [out] */ BSTR *displayName) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLink * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLink * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLink * This); DECLSPEC_XFGVIRT(IDeckLink, GetModelName) HRESULT ( STDMETHODCALLTYPE *GetModelName )( IDeckLink * This, /* [out] */ BSTR *modelName); DECLSPEC_XFGVIRT(IDeckLink, GetDisplayName) HRESULT ( STDMETHODCALLTYPE *GetDisplayName )( IDeckLink * This, /* [out] */ BSTR *displayName); END_INTERFACE } IDeckLinkVtbl; interface IDeckLink { CONST_VTBL struct IDeckLinkVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLink_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLink_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLink_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLink_GetModelName(This,modelName) \ ( (This)->lpVtbl -> GetModelName(This,modelName) ) #define IDeckLink_GetDisplayName(This,displayName) \ ( (This)->lpVtbl -> GetDisplayName(This,displayName) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLink_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkConfiguration_INTERFACE_DEFINED__ #define __IDeckLinkConfiguration_INTERFACE_DEFINED__ /* interface IDeckLinkConfiguration */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkConfiguration; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("912F634B-2D4E-40A4-8AAB-8D80B73F1289") IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE WriteConfigurationToPreferences( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkConfigurationVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkConfiguration * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkConfiguration * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkConfiguration * This); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration, WriteConfigurationToPreferences) HRESULT ( STDMETHODCALLTYPE *WriteConfigurationToPreferences )( IDeckLinkConfiguration * This); END_INTERFACE } IDeckLinkConfigurationVtbl; interface IDeckLinkConfiguration { CONST_VTBL struct IDeckLinkConfigurationVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkConfiguration_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkConfiguration_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkConfiguration_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkConfiguration_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #define IDeckLinkConfiguration_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IDeckLinkConfiguration_WriteConfigurationToPreferences(This) \ ( (This)->lpVtbl -> WriteConfigurationToPreferences(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkConfiguration_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkEncoderConfiguration_INTERFACE_DEFINED__ #define __IDeckLinkEncoderConfiguration_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderConfiguration */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderConfiguration; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("138050E5-C60A-4552-BF3F-0F358049327E") IDeckLinkEncoderConfiguration : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ void *buffer, /* [out][in] */ unsigned int *bufferSize) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderConfigurationVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderConfiguration * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderConfiguration * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderConfiguration * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ BSTR value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkEncoderConfiguration * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ void *buffer, /* [out][in] */ unsigned int *bufferSize); END_INTERFACE } IDeckLinkEncoderConfigurationVtbl; interface IDeckLinkEncoderConfiguration { CONST_VTBL struct IDeckLinkEncoderConfigurationVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderConfiguration_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderConfiguration_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderConfiguration_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderConfiguration_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_GetBytes(This,cfgID,buffer,bufferSize) \ ( (This)->lpVtbl -> GetBytes(This,cfgID,buffer,bufferSize) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderConfiguration_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDeckControlStatusCallback_INTERFACE_DEFINED__ #define __IDeckLinkDeckControlStatusCallback_INTERFACE_DEFINED__ /* interface IDeckLinkDeckControlStatusCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDeckControlStatusCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("53436FFB-B434-4906-BADC-AE3060FFE8EF") IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE TimecodeUpdate( /* [in] */ BMDTimecodeBCD currentTimecode) = 0; virtual HRESULT STDMETHODCALLTYPE VTRControlStateChanged( /* [in] */ BMDDeckControlVTRControlState newState, /* [in] */ BMDDeckControlError error) = 0; virtual HRESULT STDMETHODCALLTYPE DeckControlEventReceived( /* [in] */ BMDDeckControlEvent event, /* [in] */ BMDDeckControlError error) = 0; virtual HRESULT STDMETHODCALLTYPE DeckControlStatusChanged( /* [in] */ BMDDeckControlStatusFlags flags, /* [in] */ unsigned int mask) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDeckControlStatusCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDeckControlStatusCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDeckControlStatusCallback * This); DECLSPEC_XFGVIRT(IDeckLinkDeckControlStatusCallback, TimecodeUpdate) HRESULT ( STDMETHODCALLTYPE *TimecodeUpdate )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ BMDTimecodeBCD currentTimecode); DECLSPEC_XFGVIRT(IDeckLinkDeckControlStatusCallback, VTRControlStateChanged) HRESULT ( STDMETHODCALLTYPE *VTRControlStateChanged )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ BMDDeckControlVTRControlState newState, /* [in] */ BMDDeckControlError error); DECLSPEC_XFGVIRT(IDeckLinkDeckControlStatusCallback, DeckControlEventReceived) HRESULT ( STDMETHODCALLTYPE *DeckControlEventReceived )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ BMDDeckControlEvent event, /* [in] */ BMDDeckControlError error); DECLSPEC_XFGVIRT(IDeckLinkDeckControlStatusCallback, DeckControlStatusChanged) HRESULT ( STDMETHODCALLTYPE *DeckControlStatusChanged )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ BMDDeckControlStatusFlags flags, /* [in] */ unsigned int mask); END_INTERFACE } IDeckLinkDeckControlStatusCallbackVtbl; interface IDeckLinkDeckControlStatusCallback { CONST_VTBL struct IDeckLinkDeckControlStatusCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDeckControlStatusCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDeckControlStatusCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDeckControlStatusCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDeckControlStatusCallback_TimecodeUpdate(This,currentTimecode) \ ( (This)->lpVtbl -> TimecodeUpdate(This,currentTimecode) ) #define IDeckLinkDeckControlStatusCallback_VTRControlStateChanged(This,newState,error) \ ( (This)->lpVtbl -> VTRControlStateChanged(This,newState,error) ) #define IDeckLinkDeckControlStatusCallback_DeckControlEventReceived(This,event,error) \ ( (This)->lpVtbl -> DeckControlEventReceived(This,event,error) ) #define IDeckLinkDeckControlStatusCallback_DeckControlStatusChanged(This,flags,mask) \ ( (This)->lpVtbl -> DeckControlStatusChanged(This,flags,mask) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDeckControlStatusCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDeckControl_INTERFACE_DEFINED__ #define __IDeckLinkDeckControl_INTERFACE_DEFINED__ /* interface IDeckLinkDeckControl */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDeckControl; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("8E1C3ACE-19C7-4E00-8B92-D80431D958BE") IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Open( /* [in] */ BMDTimeScale timeScale, /* [in] */ BMDTimeValue timeValue, /* [in] */ BOOL timecodeIsDropFrame, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Close( /* [in] */ BOOL standbyOn) = 0; virtual HRESULT STDMETHODCALLTYPE GetCurrentState( /* [out] */ BMDDeckControlMode *mode, /* [out] */ BMDDeckControlVTRControlState *vtrControlState, /* [out] */ BMDDeckControlStatusFlags *flags) = 0; virtual HRESULT STDMETHODCALLTYPE SetStandby( /* [in] */ BOOL standbyOn) = 0; virtual HRESULT STDMETHODCALLTYPE SendCommand( /* [in] */ unsigned char *inBuffer, /* [in] */ unsigned int inBufferSize, /* [out] */ unsigned char *outBuffer, /* [out] */ unsigned int *outDataSize, /* [in] */ unsigned int outBufferSize, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Play( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Stop( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE TogglePlayStop( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Eject( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GoToTimecode( /* [in] */ BMDTimecodeBCD timecode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE FastForward( /* [in] */ BOOL viewTape, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Rewind( /* [in] */ BOOL viewTape, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE StepForward( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE StepBack( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Jog( /* [in] */ double rate, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Shuttle( /* [in] */ double rate, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecodeString( /* [out] */ BSTR *currentTimeCode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecode( /* [out] */ IDeckLinkTimecode **currentTimecode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecodeBCD( /* [out] */ BMDTimecodeBCD *currentTimecode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE SetPreroll( /* [in] */ unsigned int prerollSeconds) = 0; virtual HRESULT STDMETHODCALLTYPE GetPreroll( /* [out] */ unsigned int *prerollSeconds) = 0; virtual HRESULT STDMETHODCALLTYPE SetExportOffset( /* [in] */ int exportOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE GetExportOffset( /* [out] */ int *exportOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE GetManualExportOffset( /* [out] */ int *deckManualExportOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE SetCaptureOffset( /* [in] */ int captureOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE GetCaptureOffset( /* [out] */ int *captureOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE StartExport( /* [in] */ BMDTimecodeBCD inTimecode, /* [in] */ BMDTimecodeBCD outTimecode, /* [in] */ BMDDeckControlExportModeOpsFlags exportModeOps, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE StartCapture( /* [in] */ BOOL useVITC, /* [in] */ BMDTimecodeBCD inTimecode, /* [in] */ BMDTimecodeBCD outTimecode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GetDeviceID( /* [out] */ unsigned short *deviceId, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Abort( void) = 0; virtual HRESULT STDMETHODCALLTYPE CrashRecordStart( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE CrashRecordStop( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkDeckControlStatusCallback *callback) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDeckControlVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDeckControl * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDeckControl * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDeckControl * This); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Open) HRESULT ( STDMETHODCALLTYPE *Open )( IDeckLinkDeckControl * This, /* [in] */ BMDTimeScale timeScale, /* [in] */ BMDTimeValue timeValue, /* [in] */ BOOL timecodeIsDropFrame, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Close) HRESULT ( STDMETHODCALLTYPE *Close )( IDeckLinkDeckControl * This, /* [in] */ BOOL standbyOn); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetCurrentState) HRESULT ( STDMETHODCALLTYPE *GetCurrentState )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlMode *mode, /* [out] */ BMDDeckControlVTRControlState *vtrControlState, /* [out] */ BMDDeckControlStatusFlags *flags); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, SetStandby) HRESULT ( STDMETHODCALLTYPE *SetStandby )( IDeckLinkDeckControl * This, /* [in] */ BOOL standbyOn); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, SendCommand) HRESULT ( STDMETHODCALLTYPE *SendCommand )( IDeckLinkDeckControl * This, /* [in] */ unsigned char *inBuffer, /* [in] */ unsigned int inBufferSize, /* [out] */ unsigned char *outBuffer, /* [out] */ unsigned int *outDataSize, /* [in] */ unsigned int outBufferSize, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Play) HRESULT ( STDMETHODCALLTYPE *Play )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Stop) HRESULT ( STDMETHODCALLTYPE *Stop )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, TogglePlayStop) HRESULT ( STDMETHODCALLTYPE *TogglePlayStop )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Eject) HRESULT ( STDMETHODCALLTYPE *Eject )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GoToTimecode) HRESULT ( STDMETHODCALLTYPE *GoToTimecode )( IDeckLinkDeckControl * This, /* [in] */ BMDTimecodeBCD timecode, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, FastForward) HRESULT ( STDMETHODCALLTYPE *FastForward )( IDeckLinkDeckControl * This, /* [in] */ BOOL viewTape, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Rewind) HRESULT ( STDMETHODCALLTYPE *Rewind )( IDeckLinkDeckControl * This, /* [in] */ BOOL viewTape, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, StepForward) HRESULT ( STDMETHODCALLTYPE *StepForward )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, StepBack) HRESULT ( STDMETHODCALLTYPE *StepBack )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Jog) HRESULT ( STDMETHODCALLTYPE *Jog )( IDeckLinkDeckControl * This, /* [in] */ double rate, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Shuttle) HRESULT ( STDMETHODCALLTYPE *Shuttle )( IDeckLinkDeckControl * This, /* [in] */ double rate, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetTimecodeString) HRESULT ( STDMETHODCALLTYPE *GetTimecodeString )( IDeckLinkDeckControl * This, /* [out] */ BSTR *currentTimeCode, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkDeckControl * This, /* [out] */ IDeckLinkTimecode **currentTimecode, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetTimecodeBCD) HRESULT ( STDMETHODCALLTYPE *GetTimecodeBCD )( IDeckLinkDeckControl * This, /* [out] */ BMDTimecodeBCD *currentTimecode, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, SetPreroll) HRESULT ( STDMETHODCALLTYPE *SetPreroll )( IDeckLinkDeckControl * This, /* [in] */ unsigned int prerollSeconds); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetPreroll) HRESULT ( STDMETHODCALLTYPE *GetPreroll )( IDeckLinkDeckControl * This, /* [out] */ unsigned int *prerollSeconds); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, SetExportOffset) HRESULT ( STDMETHODCALLTYPE *SetExportOffset )( IDeckLinkDeckControl * This, /* [in] */ int exportOffsetFields); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetExportOffset) HRESULT ( STDMETHODCALLTYPE *GetExportOffset )( IDeckLinkDeckControl * This, /* [out] */ int *exportOffsetFields); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetManualExportOffset) HRESULT ( STDMETHODCALLTYPE *GetManualExportOffset )( IDeckLinkDeckControl * This, /* [out] */ int *deckManualExportOffsetFields); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, SetCaptureOffset) HRESULT ( STDMETHODCALLTYPE *SetCaptureOffset )( IDeckLinkDeckControl * This, /* [in] */ int captureOffsetFields); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetCaptureOffset) HRESULT ( STDMETHODCALLTYPE *GetCaptureOffset )( IDeckLinkDeckControl * This, /* [out] */ int *captureOffsetFields); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, StartExport) HRESULT ( STDMETHODCALLTYPE *StartExport )( IDeckLinkDeckControl * This, /* [in] */ BMDTimecodeBCD inTimecode, /* [in] */ BMDTimecodeBCD outTimecode, /* [in] */ BMDDeckControlExportModeOpsFlags exportModeOps, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, StartCapture) HRESULT ( STDMETHODCALLTYPE *StartCapture )( IDeckLinkDeckControl * This, /* [in] */ BOOL useVITC, /* [in] */ BMDTimecodeBCD inTimecode, /* [in] */ BMDTimecodeBCD outTimecode, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, GetDeviceID) HRESULT ( STDMETHODCALLTYPE *GetDeviceID )( IDeckLinkDeckControl * This, /* [out] */ unsigned short *deviceId, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, Abort) HRESULT ( STDMETHODCALLTYPE *Abort )( IDeckLinkDeckControl * This); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, CrashRecordStart) HRESULT ( STDMETHODCALLTYPE *CrashRecordStart )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, CrashRecordStop) HRESULT ( STDMETHODCALLTYPE *CrashRecordStop )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); DECLSPEC_XFGVIRT(IDeckLinkDeckControl, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkDeckControl * This, /* [in] */ IDeckLinkDeckControlStatusCallback *callback); END_INTERFACE } IDeckLinkDeckControlVtbl; interface IDeckLinkDeckControl { CONST_VTBL struct IDeckLinkDeckControlVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDeckControl_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDeckControl_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDeckControl_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDeckControl_Open(This,timeScale,timeValue,timecodeIsDropFrame,error) \ ( (This)->lpVtbl -> Open(This,timeScale,timeValue,timecodeIsDropFrame,error) ) #define IDeckLinkDeckControl_Close(This,standbyOn) \ ( (This)->lpVtbl -> Close(This,standbyOn) ) #define IDeckLinkDeckControl_GetCurrentState(This,mode,vtrControlState,flags) \ ( (This)->lpVtbl -> GetCurrentState(This,mode,vtrControlState,flags) ) #define IDeckLinkDeckControl_SetStandby(This,standbyOn) \ ( (This)->lpVtbl -> SetStandby(This,standbyOn) ) #define IDeckLinkDeckControl_SendCommand(This,inBuffer,inBufferSize,outBuffer,outDataSize,outBufferSize,error) \ ( (This)->lpVtbl -> SendCommand(This,inBuffer,inBufferSize,outBuffer,outDataSize,outBufferSize,error) ) #define IDeckLinkDeckControl_Play(This,error) \ ( (This)->lpVtbl -> Play(This,error) ) #define IDeckLinkDeckControl_Stop(This,error) \ ( (This)->lpVtbl -> Stop(This,error) ) #define IDeckLinkDeckControl_TogglePlayStop(This,error) \ ( (This)->lpVtbl -> TogglePlayStop(This,error) ) #define IDeckLinkDeckControl_Eject(This,error) \ ( (This)->lpVtbl -> Eject(This,error) ) #define IDeckLinkDeckControl_GoToTimecode(This,timecode,error) \ ( (This)->lpVtbl -> GoToTimecode(This,timecode,error) ) #define IDeckLinkDeckControl_FastForward(This,viewTape,error) \ ( (This)->lpVtbl -> FastForward(This,viewTape,error) ) #define IDeckLinkDeckControl_Rewind(This,viewTape,error) \ ( (This)->lpVtbl -> Rewind(This,viewTape,error) ) #define IDeckLinkDeckControl_StepForward(This,error) \ ( (This)->lpVtbl -> StepForward(This,error) ) #define IDeckLinkDeckControl_StepBack(This,error) \ ( (This)->lpVtbl -> StepBack(This,error) ) #define IDeckLinkDeckControl_Jog(This,rate,error) \ ( (This)->lpVtbl -> Jog(This,rate,error) ) #define IDeckLinkDeckControl_Shuttle(This,rate,error) \ ( (This)->lpVtbl -> Shuttle(This,rate,error) ) #define IDeckLinkDeckControl_GetTimecodeString(This,currentTimeCode,error) \ ( (This)->lpVtbl -> GetTimecodeString(This,currentTimeCode,error) ) #define IDeckLinkDeckControl_GetTimecode(This,currentTimecode,error) \ ( (This)->lpVtbl -> GetTimecode(This,currentTimecode,error) ) #define IDeckLinkDeckControl_GetTimecodeBCD(This,currentTimecode,error) \ ( (This)->lpVtbl -> GetTimecodeBCD(This,currentTimecode,error) ) #define IDeckLinkDeckControl_SetPreroll(This,prerollSeconds) \ ( (This)->lpVtbl -> SetPreroll(This,prerollSeconds) ) #define IDeckLinkDeckControl_GetPreroll(This,prerollSeconds) \ ( (This)->lpVtbl -> GetPreroll(This,prerollSeconds) ) #define IDeckLinkDeckControl_SetExportOffset(This,exportOffsetFields) \ ( (This)->lpVtbl -> SetExportOffset(This,exportOffsetFields) ) #define IDeckLinkDeckControl_GetExportOffset(This,exportOffsetFields) \ ( (This)->lpVtbl -> GetExportOffset(This,exportOffsetFields) ) #define IDeckLinkDeckControl_GetManualExportOffset(This,deckManualExportOffsetFields) \ ( (This)->lpVtbl -> GetManualExportOffset(This,deckManualExportOffsetFields) ) #define IDeckLinkDeckControl_SetCaptureOffset(This,captureOffsetFields) \ ( (This)->lpVtbl -> SetCaptureOffset(This,captureOffsetFields) ) #define IDeckLinkDeckControl_GetCaptureOffset(This,captureOffsetFields) \ ( (This)->lpVtbl -> GetCaptureOffset(This,captureOffsetFields) ) #define IDeckLinkDeckControl_StartExport(This,inTimecode,outTimecode,exportModeOps,error) \ ( (This)->lpVtbl -> StartExport(This,inTimecode,outTimecode,exportModeOps,error) ) #define IDeckLinkDeckControl_StartCapture(This,useVITC,inTimecode,outTimecode,error) \ ( (This)->lpVtbl -> StartCapture(This,useVITC,inTimecode,outTimecode,error) ) #define IDeckLinkDeckControl_GetDeviceID(This,deviceId,error) \ ( (This)->lpVtbl -> GetDeviceID(This,deviceId,error) ) #define IDeckLinkDeckControl_Abort(This) \ ( (This)->lpVtbl -> Abort(This) ) #define IDeckLinkDeckControl_CrashRecordStart(This,error) \ ( (This)->lpVtbl -> CrashRecordStart(This,error) ) #define IDeckLinkDeckControl_CrashRecordStop(This,error) \ ( (This)->lpVtbl -> CrashRecordStop(This,error) ) #define IDeckLinkDeckControl_SetCallback(This,callback) \ ( (This)->lpVtbl -> SetCallback(This,callback) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDeckControl_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingDeviceNotificationCallback_INTERFACE_DEFINED__ #define __IBMDStreamingDeviceNotificationCallback_INTERFACE_DEFINED__ /* interface IBMDStreamingDeviceNotificationCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingDeviceNotificationCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("F9531D64-3305-4B29-A387-7F74BB0D0E84") IBMDStreamingDeviceNotificationCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE StreamingDeviceArrived( /* [in] */ IDeckLink *device) = 0; virtual HRESULT STDMETHODCALLTYPE StreamingDeviceRemoved( /* [in] */ IDeckLink *device) = 0; virtual HRESULT STDMETHODCALLTYPE StreamingDeviceModeChanged( /* [in] */ IDeckLink *device, /* [in] */ BMDStreamingDeviceMode mode) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingDeviceNotificationCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingDeviceNotificationCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingDeviceNotificationCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingDeviceNotificationCallback * This); DECLSPEC_XFGVIRT(IBMDStreamingDeviceNotificationCallback, StreamingDeviceArrived) HRESULT ( STDMETHODCALLTYPE *StreamingDeviceArrived )( IBMDStreamingDeviceNotificationCallback * This, /* [in] */ IDeckLink *device); DECLSPEC_XFGVIRT(IBMDStreamingDeviceNotificationCallback, StreamingDeviceRemoved) HRESULT ( STDMETHODCALLTYPE *StreamingDeviceRemoved )( IBMDStreamingDeviceNotificationCallback * This, /* [in] */ IDeckLink *device); DECLSPEC_XFGVIRT(IBMDStreamingDeviceNotificationCallback, StreamingDeviceModeChanged) HRESULT ( STDMETHODCALLTYPE *StreamingDeviceModeChanged )( IBMDStreamingDeviceNotificationCallback * This, /* [in] */ IDeckLink *device, /* [in] */ BMDStreamingDeviceMode mode); END_INTERFACE } IBMDStreamingDeviceNotificationCallbackVtbl; interface IBMDStreamingDeviceNotificationCallback { CONST_VTBL struct IBMDStreamingDeviceNotificationCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingDeviceNotificationCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingDeviceNotificationCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingDeviceNotificationCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingDeviceNotificationCallback_StreamingDeviceArrived(This,device) \ ( (This)->lpVtbl -> StreamingDeviceArrived(This,device) ) #define IBMDStreamingDeviceNotificationCallback_StreamingDeviceRemoved(This,device) \ ( (This)->lpVtbl -> StreamingDeviceRemoved(This,device) ) #define IBMDStreamingDeviceNotificationCallback_StreamingDeviceModeChanged(This,device,mode) \ ( (This)->lpVtbl -> StreamingDeviceModeChanged(This,device,mode) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingDeviceNotificationCallback_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingH264InputCallback_INTERFACE_DEFINED__ #define __IBMDStreamingH264InputCallback_INTERFACE_DEFINED__ /* interface IBMDStreamingH264InputCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingH264InputCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("823C475F-55AE-46F9-890C-537CC5CEDCCA") IBMDStreamingH264InputCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE H264NALPacketArrived( /* [in] */ IBMDStreamingH264NALPacket *nalPacket) = 0; virtual HRESULT STDMETHODCALLTYPE H264AudioPacketArrived( /* [in] */ IBMDStreamingAudioPacket *audioPacket) = 0; virtual HRESULT STDMETHODCALLTYPE MPEG2TSPacketArrived( /* [in] */ IBMDStreamingMPEG2TSPacket *tsPacket) = 0; virtual HRESULT STDMETHODCALLTYPE H264VideoInputConnectorScanningChanged( void) = 0; virtual HRESULT STDMETHODCALLTYPE H264VideoInputConnectorChanged( void) = 0; virtual HRESULT STDMETHODCALLTYPE H264VideoInputModeChanged( void) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingH264InputCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingH264InputCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingH264InputCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingH264InputCallback * This); DECLSPEC_XFGVIRT(IBMDStreamingH264InputCallback, H264NALPacketArrived) HRESULT ( STDMETHODCALLTYPE *H264NALPacketArrived )( IBMDStreamingH264InputCallback * This, /* [in] */ IBMDStreamingH264NALPacket *nalPacket); DECLSPEC_XFGVIRT(IBMDStreamingH264InputCallback, H264AudioPacketArrived) HRESULT ( STDMETHODCALLTYPE *H264AudioPacketArrived )( IBMDStreamingH264InputCallback * This, /* [in] */ IBMDStreamingAudioPacket *audioPacket); DECLSPEC_XFGVIRT(IBMDStreamingH264InputCallback, MPEG2TSPacketArrived) HRESULT ( STDMETHODCALLTYPE *MPEG2TSPacketArrived )( IBMDStreamingH264InputCallback * This, /* [in] */ IBMDStreamingMPEG2TSPacket *tsPacket); DECLSPEC_XFGVIRT(IBMDStreamingH264InputCallback, H264VideoInputConnectorScanningChanged) HRESULT ( STDMETHODCALLTYPE *H264VideoInputConnectorScanningChanged )( IBMDStreamingH264InputCallback * This); DECLSPEC_XFGVIRT(IBMDStreamingH264InputCallback, H264VideoInputConnectorChanged) HRESULT ( STDMETHODCALLTYPE *H264VideoInputConnectorChanged )( IBMDStreamingH264InputCallback * This); DECLSPEC_XFGVIRT(IBMDStreamingH264InputCallback, H264VideoInputModeChanged) HRESULT ( STDMETHODCALLTYPE *H264VideoInputModeChanged )( IBMDStreamingH264InputCallback * This); END_INTERFACE } IBMDStreamingH264InputCallbackVtbl; interface IBMDStreamingH264InputCallback { CONST_VTBL struct IBMDStreamingH264InputCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingH264InputCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingH264InputCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingH264InputCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingH264InputCallback_H264NALPacketArrived(This,nalPacket) \ ( (This)->lpVtbl -> H264NALPacketArrived(This,nalPacket) ) #define IBMDStreamingH264InputCallback_H264AudioPacketArrived(This,audioPacket) \ ( (This)->lpVtbl -> H264AudioPacketArrived(This,audioPacket) ) #define IBMDStreamingH264InputCallback_MPEG2TSPacketArrived(This,tsPacket) \ ( (This)->lpVtbl -> MPEG2TSPacketArrived(This,tsPacket) ) #define IBMDStreamingH264InputCallback_H264VideoInputConnectorScanningChanged(This) \ ( (This)->lpVtbl -> H264VideoInputConnectorScanningChanged(This) ) #define IBMDStreamingH264InputCallback_H264VideoInputConnectorChanged(This) \ ( (This)->lpVtbl -> H264VideoInputConnectorChanged(This) ) #define IBMDStreamingH264InputCallback_H264VideoInputModeChanged(This) \ ( (This)->lpVtbl -> H264VideoInputModeChanged(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingH264InputCallback_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingDiscovery_INTERFACE_DEFINED__ #define __IBMDStreamingDiscovery_INTERFACE_DEFINED__ /* interface IBMDStreamingDiscovery */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingDiscovery; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("2C837444-F989-4D87-901A-47C8A36D096D") IBMDStreamingDiscovery : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE InstallDeviceNotifications( /* [in] */ IBMDStreamingDeviceNotificationCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE UninstallDeviceNotifications( void) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingDiscoveryVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingDiscovery * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingDiscovery * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingDiscovery * This); DECLSPEC_XFGVIRT(IBMDStreamingDiscovery, InstallDeviceNotifications) HRESULT ( STDMETHODCALLTYPE *InstallDeviceNotifications )( IBMDStreamingDiscovery * This, /* [in] */ IBMDStreamingDeviceNotificationCallback *theCallback); DECLSPEC_XFGVIRT(IBMDStreamingDiscovery, UninstallDeviceNotifications) HRESULT ( STDMETHODCALLTYPE *UninstallDeviceNotifications )( IBMDStreamingDiscovery * This); END_INTERFACE } IBMDStreamingDiscoveryVtbl; interface IBMDStreamingDiscovery { CONST_VTBL struct IBMDStreamingDiscoveryVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingDiscovery_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingDiscovery_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingDiscovery_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingDiscovery_InstallDeviceNotifications(This,theCallback) \ ( (This)->lpVtbl -> InstallDeviceNotifications(This,theCallback) ) #define IBMDStreamingDiscovery_UninstallDeviceNotifications(This) \ ( (This)->lpVtbl -> UninstallDeviceNotifications(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingDiscovery_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingVideoEncodingMode_INTERFACE_DEFINED__ #define __IBMDStreamingVideoEncodingMode_INTERFACE_DEFINED__ /* interface IBMDStreamingVideoEncodingMode */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingVideoEncodingMode; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("1AB8035B-CD13-458D-B6DF-5E8F7C2141D9") IBMDStreamingVideoEncodingMode : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetName( /* [out] */ BSTR *name) = 0; virtual unsigned int STDMETHODCALLTYPE GetPresetID( void) = 0; virtual unsigned int STDMETHODCALLTYPE GetSourcePositionX( void) = 0; virtual unsigned int STDMETHODCALLTYPE GetSourcePositionY( void) = 0; virtual unsigned int STDMETHODCALLTYPE GetSourceWidth( void) = 0; virtual unsigned int STDMETHODCALLTYPE GetSourceHeight( void) = 0; virtual unsigned int STDMETHODCALLTYPE GetDestWidth( void) = 0; virtual unsigned int STDMETHODCALLTYPE GetDestHeight( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE CreateMutableVideoEncodingMode( /* [out] */ IBMDStreamingMutableVideoEncodingMode **newEncodingMode) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingVideoEncodingModeVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingVideoEncodingMode * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetName) HRESULT ( STDMETHODCALLTYPE *GetName )( IBMDStreamingVideoEncodingMode * This, /* [out] */ BSTR *name); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetPresetID) unsigned int ( STDMETHODCALLTYPE *GetPresetID )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetSourcePositionX) unsigned int ( STDMETHODCALLTYPE *GetSourcePositionX )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetSourcePositionY) unsigned int ( STDMETHODCALLTYPE *GetSourcePositionY )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetSourceWidth) unsigned int ( STDMETHODCALLTYPE *GetSourceWidth )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetSourceHeight) unsigned int ( STDMETHODCALLTYPE *GetSourceHeight )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetDestWidth) unsigned int ( STDMETHODCALLTYPE *GetDestWidth )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetDestHeight) unsigned int ( STDMETHODCALLTYPE *GetDestHeight )( IBMDStreamingVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IBMDStreamingVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IBMDStreamingVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IBMDStreamingVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IBMDStreamingVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, CreateMutableVideoEncodingMode) HRESULT ( STDMETHODCALLTYPE *CreateMutableVideoEncodingMode )( IBMDStreamingVideoEncodingMode * This, /* [out] */ IBMDStreamingMutableVideoEncodingMode **newEncodingMode); END_INTERFACE } IBMDStreamingVideoEncodingModeVtbl; interface IBMDStreamingVideoEncodingMode { CONST_VTBL struct IBMDStreamingVideoEncodingModeVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingVideoEncodingMode_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingVideoEncodingMode_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingVideoEncodingMode_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingVideoEncodingMode_GetName(This,name) \ ( (This)->lpVtbl -> GetName(This,name) ) #define IBMDStreamingVideoEncodingMode_GetPresetID(This) \ ( (This)->lpVtbl -> GetPresetID(This) ) #define IBMDStreamingVideoEncodingMode_GetSourcePositionX(This) \ ( (This)->lpVtbl -> GetSourcePositionX(This) ) #define IBMDStreamingVideoEncodingMode_GetSourcePositionY(This) \ ( (This)->lpVtbl -> GetSourcePositionY(This) ) #define IBMDStreamingVideoEncodingMode_GetSourceWidth(This) \ ( (This)->lpVtbl -> GetSourceWidth(This) ) #define IBMDStreamingVideoEncodingMode_GetSourceHeight(This) \ ( (This)->lpVtbl -> GetSourceHeight(This) ) #define IBMDStreamingVideoEncodingMode_GetDestWidth(This) \ ( (This)->lpVtbl -> GetDestWidth(This) ) #define IBMDStreamingVideoEncodingMode_GetDestHeight(This) \ ( (This)->lpVtbl -> GetDestHeight(This) ) #define IBMDStreamingVideoEncodingMode_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IBMDStreamingVideoEncodingMode_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IBMDStreamingVideoEncodingMode_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IBMDStreamingVideoEncodingMode_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IBMDStreamingVideoEncodingMode_CreateMutableVideoEncodingMode(This,newEncodingMode) \ ( (This)->lpVtbl -> CreateMutableVideoEncodingMode(This,newEncodingMode) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingVideoEncodingMode_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingMutableVideoEncodingMode_INTERFACE_DEFINED__ #define __IBMDStreamingMutableVideoEncodingMode_INTERFACE_DEFINED__ /* interface IBMDStreamingMutableVideoEncodingMode */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingMutableVideoEncodingMode; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("19BF7D90-1E0A-400D-B2C6-FFC4E78AD49D") IBMDStreamingMutableVideoEncodingMode : public IBMDStreamingVideoEncodingMode { public: virtual HRESULT STDMETHODCALLTYPE SetSourceRect( /* [in] */ unsigned int posX, /* [in] */ unsigned int posY, /* [in] */ unsigned int width, /* [in] */ unsigned int height) = 0; virtual HRESULT STDMETHODCALLTYPE SetDestSize( /* [in] */ unsigned int width, /* [in] */ unsigned int height) = 0; virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [in] */ BSTR value) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingMutableVideoEncodingModeVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetName) HRESULT ( STDMETHODCALLTYPE *GetName )( IBMDStreamingMutableVideoEncodingMode * This, /* [out] */ BSTR *name); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetPresetID) unsigned int ( STDMETHODCALLTYPE *GetPresetID )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetSourcePositionX) unsigned int ( STDMETHODCALLTYPE *GetSourcePositionX )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetSourcePositionY) unsigned int ( STDMETHODCALLTYPE *GetSourcePositionY )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetSourceWidth) unsigned int ( STDMETHODCALLTYPE *GetSourceWidth )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetSourceHeight) unsigned int ( STDMETHODCALLTYPE *GetSourceHeight )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetDestWidth) unsigned int ( STDMETHODCALLTYPE *GetDestWidth )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetDestHeight) unsigned int ( STDMETHODCALLTYPE *GetDestHeight )( IBMDStreamingMutableVideoEncodingMode * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingMode, CreateMutableVideoEncodingMode) HRESULT ( STDMETHODCALLTYPE *CreateMutableVideoEncodingMode )( IBMDStreamingMutableVideoEncodingMode * This, /* [out] */ IBMDStreamingMutableVideoEncodingMode **newEncodingMode); DECLSPEC_XFGVIRT(IBMDStreamingMutableVideoEncodingMode, SetSourceRect) HRESULT ( STDMETHODCALLTYPE *SetSourceRect )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ unsigned int posX, /* [in] */ unsigned int posY, /* [in] */ unsigned int width, /* [in] */ unsigned int height); DECLSPEC_XFGVIRT(IBMDStreamingMutableVideoEncodingMode, SetDestSize) HRESULT ( STDMETHODCALLTYPE *SetDestSize )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ unsigned int width, /* [in] */ unsigned int height); DECLSPEC_XFGVIRT(IBMDStreamingMutableVideoEncodingMode, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IBMDStreamingMutableVideoEncodingMode, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IBMDStreamingMutableVideoEncodingMode, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [in] */ double value); DECLSPEC_XFGVIRT(IBMDStreamingMutableVideoEncodingMode, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IBMDStreamingMutableVideoEncodingMode * This, /* [in] */ BMDStreamingEncodingModePropertyID cfgID, /* [in] */ BSTR value); END_INTERFACE } IBMDStreamingMutableVideoEncodingModeVtbl; interface IBMDStreamingMutableVideoEncodingMode { CONST_VTBL struct IBMDStreamingMutableVideoEncodingModeVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingMutableVideoEncodingMode_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingMutableVideoEncodingMode_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingMutableVideoEncodingMode_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingMutableVideoEncodingMode_GetName(This,name) \ ( (This)->lpVtbl -> GetName(This,name) ) #define IBMDStreamingMutableVideoEncodingMode_GetPresetID(This) \ ( (This)->lpVtbl -> GetPresetID(This) ) #define IBMDStreamingMutableVideoEncodingMode_GetSourcePositionX(This) \ ( (This)->lpVtbl -> GetSourcePositionX(This) ) #define IBMDStreamingMutableVideoEncodingMode_GetSourcePositionY(This) \ ( (This)->lpVtbl -> GetSourcePositionY(This) ) #define IBMDStreamingMutableVideoEncodingMode_GetSourceWidth(This) \ ( (This)->lpVtbl -> GetSourceWidth(This) ) #define IBMDStreamingMutableVideoEncodingMode_GetSourceHeight(This) \ ( (This)->lpVtbl -> GetSourceHeight(This) ) #define IBMDStreamingMutableVideoEncodingMode_GetDestWidth(This) \ ( (This)->lpVtbl -> GetDestWidth(This) ) #define IBMDStreamingMutableVideoEncodingMode_GetDestHeight(This) \ ( (This)->lpVtbl -> GetDestHeight(This) ) #define IBMDStreamingMutableVideoEncodingMode_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IBMDStreamingMutableVideoEncodingMode_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IBMDStreamingMutableVideoEncodingMode_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IBMDStreamingMutableVideoEncodingMode_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IBMDStreamingMutableVideoEncodingMode_CreateMutableVideoEncodingMode(This,newEncodingMode) \ ( (This)->lpVtbl -> CreateMutableVideoEncodingMode(This,newEncodingMode) ) #define IBMDStreamingMutableVideoEncodingMode_SetSourceRect(This,posX,posY,width,height) \ ( (This)->lpVtbl -> SetSourceRect(This,posX,posY,width,height) ) #define IBMDStreamingMutableVideoEncodingMode_SetDestSize(This,width,height) \ ( (This)->lpVtbl -> SetDestSize(This,width,height) ) #define IBMDStreamingMutableVideoEncodingMode_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IBMDStreamingMutableVideoEncodingMode_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IBMDStreamingMutableVideoEncodingMode_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IBMDStreamingMutableVideoEncodingMode_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingMutableVideoEncodingMode_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingVideoEncodingModePresetIterator_INTERFACE_DEFINED__ #define __IBMDStreamingVideoEncodingModePresetIterator_INTERFACE_DEFINED__ /* interface IBMDStreamingVideoEncodingModePresetIterator */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingVideoEncodingModePresetIterator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("7AC731A3-C950-4AD0-804A-8377AA51C6C4") IBMDStreamingVideoEncodingModePresetIterator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IBMDStreamingVideoEncodingMode **videoEncodingMode) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingVideoEncodingModePresetIteratorVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingVideoEncodingModePresetIterator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingVideoEncodingModePresetIterator * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingVideoEncodingModePresetIterator * This); DECLSPEC_XFGVIRT(IBMDStreamingVideoEncodingModePresetIterator, Next) HRESULT ( STDMETHODCALLTYPE *Next )( IBMDStreamingVideoEncodingModePresetIterator * This, /* [out] */ IBMDStreamingVideoEncodingMode **videoEncodingMode); END_INTERFACE } IBMDStreamingVideoEncodingModePresetIteratorVtbl; interface IBMDStreamingVideoEncodingModePresetIterator { CONST_VTBL struct IBMDStreamingVideoEncodingModePresetIteratorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingVideoEncodingModePresetIterator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingVideoEncodingModePresetIterator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingVideoEncodingModePresetIterator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingVideoEncodingModePresetIterator_Next(This,videoEncodingMode) \ ( (This)->lpVtbl -> Next(This,videoEncodingMode) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingVideoEncodingModePresetIterator_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingDeviceInput_INTERFACE_DEFINED__ #define __IBMDStreamingDeviceInput_INTERFACE_DEFINED__ /* interface IBMDStreamingDeviceInput */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingDeviceInput; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("24B6B6EC-1727-44BB-9818-34FF086ACF98") IBMDStreamingDeviceInput : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoInputMode( /* [in] */ BMDDisplayMode inputMode, /* [out] */ BOOL *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetVideoInputModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoInputMode( /* [in] */ BMDDisplayMode inputMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetCurrentDetectedVideoInputMode( /* [out] */ BMDDisplayMode *detectedMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetVideoEncodingMode( /* [out] */ IBMDStreamingVideoEncodingMode **encodingMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetVideoEncodingModePresetIterator( /* [in] */ BMDDisplayMode inputMode, /* [out] */ IBMDStreamingVideoEncodingModePresetIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoEncodingMode( /* [in] */ BMDDisplayMode inputMode, /* [in] */ IBMDStreamingVideoEncodingMode *encodingMode, /* [out] */ BMDStreamingEncodingSupport *result, /* [out] */ IBMDStreamingVideoEncodingMode **changedEncodingMode) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoEncodingMode( /* [in] */ IBMDStreamingVideoEncodingMode *encodingMode) = 0; virtual HRESULT STDMETHODCALLTYPE StartCapture( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopCapture( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IUnknown *theCallback) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingDeviceInputVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingDeviceInput * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingDeviceInput * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingDeviceInput * This); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, DoesSupportVideoInputMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoInputMode )( IBMDStreamingDeviceInput * This, /* [in] */ BMDDisplayMode inputMode, /* [out] */ BOOL *result); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, GetVideoInputModeIterator) HRESULT ( STDMETHODCALLTYPE *GetVideoInputModeIterator )( IBMDStreamingDeviceInput * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, SetVideoInputMode) HRESULT ( STDMETHODCALLTYPE *SetVideoInputMode )( IBMDStreamingDeviceInput * This, /* [in] */ BMDDisplayMode inputMode); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, GetCurrentDetectedVideoInputMode) HRESULT ( STDMETHODCALLTYPE *GetCurrentDetectedVideoInputMode )( IBMDStreamingDeviceInput * This, /* [out] */ BMDDisplayMode *detectedMode); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, GetVideoEncodingMode) HRESULT ( STDMETHODCALLTYPE *GetVideoEncodingMode )( IBMDStreamingDeviceInput * This, /* [out] */ IBMDStreamingVideoEncodingMode **encodingMode); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, GetVideoEncodingModePresetIterator) HRESULT ( STDMETHODCALLTYPE *GetVideoEncodingModePresetIterator )( IBMDStreamingDeviceInput * This, /* [in] */ BMDDisplayMode inputMode, /* [out] */ IBMDStreamingVideoEncodingModePresetIterator **iterator); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, DoesSupportVideoEncodingMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoEncodingMode )( IBMDStreamingDeviceInput * This, /* [in] */ BMDDisplayMode inputMode, /* [in] */ IBMDStreamingVideoEncodingMode *encodingMode, /* [out] */ BMDStreamingEncodingSupport *result, /* [out] */ IBMDStreamingVideoEncodingMode **changedEncodingMode); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, SetVideoEncodingMode) HRESULT ( STDMETHODCALLTYPE *SetVideoEncodingMode )( IBMDStreamingDeviceInput * This, /* [in] */ IBMDStreamingVideoEncodingMode *encodingMode); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, StartCapture) HRESULT ( STDMETHODCALLTYPE *StartCapture )( IBMDStreamingDeviceInput * This); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, StopCapture) HRESULT ( STDMETHODCALLTYPE *StopCapture )( IBMDStreamingDeviceInput * This); DECLSPEC_XFGVIRT(IBMDStreamingDeviceInput, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IBMDStreamingDeviceInput * This, /* [in] */ IUnknown *theCallback); END_INTERFACE } IBMDStreamingDeviceInputVtbl; interface IBMDStreamingDeviceInput { CONST_VTBL struct IBMDStreamingDeviceInputVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingDeviceInput_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingDeviceInput_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingDeviceInput_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingDeviceInput_DoesSupportVideoInputMode(This,inputMode,result) \ ( (This)->lpVtbl -> DoesSupportVideoInputMode(This,inputMode,result) ) #define IBMDStreamingDeviceInput_GetVideoInputModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetVideoInputModeIterator(This,iterator) ) #define IBMDStreamingDeviceInput_SetVideoInputMode(This,inputMode) \ ( (This)->lpVtbl -> SetVideoInputMode(This,inputMode) ) #define IBMDStreamingDeviceInput_GetCurrentDetectedVideoInputMode(This,detectedMode) \ ( (This)->lpVtbl -> GetCurrentDetectedVideoInputMode(This,detectedMode) ) #define IBMDStreamingDeviceInput_GetVideoEncodingMode(This,encodingMode) \ ( (This)->lpVtbl -> GetVideoEncodingMode(This,encodingMode) ) #define IBMDStreamingDeviceInput_GetVideoEncodingModePresetIterator(This,inputMode,iterator) \ ( (This)->lpVtbl -> GetVideoEncodingModePresetIterator(This,inputMode,iterator) ) #define IBMDStreamingDeviceInput_DoesSupportVideoEncodingMode(This,inputMode,encodingMode,result,changedEncodingMode) \ ( (This)->lpVtbl -> DoesSupportVideoEncodingMode(This,inputMode,encodingMode,result,changedEncodingMode) ) #define IBMDStreamingDeviceInput_SetVideoEncodingMode(This,encodingMode) \ ( (This)->lpVtbl -> SetVideoEncodingMode(This,encodingMode) ) #define IBMDStreamingDeviceInput_StartCapture(This) \ ( (This)->lpVtbl -> StartCapture(This) ) #define IBMDStreamingDeviceInput_StopCapture(This) \ ( (This)->lpVtbl -> StopCapture(This) ) #define IBMDStreamingDeviceInput_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingDeviceInput_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingH264NALPacket_INTERFACE_DEFINED__ #define __IBMDStreamingH264NALPacket_INTERFACE_DEFINED__ /* interface IBMDStreamingH264NALPacket */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingH264NALPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("E260E955-14BE-4395-9775-9F02CC0A9D89") IBMDStreamingH264NALPacket : public IUnknown { public: virtual long STDMETHODCALLTYPE GetPayloadSize( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytesWithSizePrefix( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayTime( /* [in] */ ULONGLONG requestedTimeScale, /* [out] */ ULONGLONG *displayTime) = 0; virtual HRESULT STDMETHODCALLTYPE GetPacketIndex( /* [out] */ unsigned int *packetIndex) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingH264NALPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingH264NALPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingH264NALPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingH264NALPacket * This); DECLSPEC_XFGVIRT(IBMDStreamingH264NALPacket, GetPayloadSize) long ( STDMETHODCALLTYPE *GetPayloadSize )( IBMDStreamingH264NALPacket * This); DECLSPEC_XFGVIRT(IBMDStreamingH264NALPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IBMDStreamingH264NALPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IBMDStreamingH264NALPacket, GetBytesWithSizePrefix) HRESULT ( STDMETHODCALLTYPE *GetBytesWithSizePrefix )( IBMDStreamingH264NALPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IBMDStreamingH264NALPacket, GetDisplayTime) HRESULT ( STDMETHODCALLTYPE *GetDisplayTime )( IBMDStreamingH264NALPacket * This, /* [in] */ ULONGLONG requestedTimeScale, /* [out] */ ULONGLONG *displayTime); DECLSPEC_XFGVIRT(IBMDStreamingH264NALPacket, GetPacketIndex) HRESULT ( STDMETHODCALLTYPE *GetPacketIndex )( IBMDStreamingH264NALPacket * This, /* [out] */ unsigned int *packetIndex); END_INTERFACE } IBMDStreamingH264NALPacketVtbl; interface IBMDStreamingH264NALPacket { CONST_VTBL struct IBMDStreamingH264NALPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingH264NALPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingH264NALPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingH264NALPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingH264NALPacket_GetPayloadSize(This) \ ( (This)->lpVtbl -> GetPayloadSize(This) ) #define IBMDStreamingH264NALPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IBMDStreamingH264NALPacket_GetBytesWithSizePrefix(This,buffer) \ ( (This)->lpVtbl -> GetBytesWithSizePrefix(This,buffer) ) #define IBMDStreamingH264NALPacket_GetDisplayTime(This,requestedTimeScale,displayTime) \ ( (This)->lpVtbl -> GetDisplayTime(This,requestedTimeScale,displayTime) ) #define IBMDStreamingH264NALPacket_GetPacketIndex(This,packetIndex) \ ( (This)->lpVtbl -> GetPacketIndex(This,packetIndex) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingH264NALPacket_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingAudioPacket_INTERFACE_DEFINED__ #define __IBMDStreamingAudioPacket_INTERFACE_DEFINED__ /* interface IBMDStreamingAudioPacket */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingAudioPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("D9EB5902-1AD2-43F4-9E2C-3CFA50B5EE19") IBMDStreamingAudioPacket : public IUnknown { public: virtual BMDStreamingAudioCodec STDMETHODCALLTYPE GetCodec( void) = 0; virtual long STDMETHODCALLTYPE GetPayloadSize( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetPlayTime( /* [in] */ ULONGLONG requestedTimeScale, /* [out] */ ULONGLONG *playTime) = 0; virtual HRESULT STDMETHODCALLTYPE GetPacketIndex( /* [out] */ unsigned int *packetIndex) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingAudioPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingAudioPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingAudioPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingAudioPacket * This); DECLSPEC_XFGVIRT(IBMDStreamingAudioPacket, GetCodec) BMDStreamingAudioCodec ( STDMETHODCALLTYPE *GetCodec )( IBMDStreamingAudioPacket * This); DECLSPEC_XFGVIRT(IBMDStreamingAudioPacket, GetPayloadSize) long ( STDMETHODCALLTYPE *GetPayloadSize )( IBMDStreamingAudioPacket * This); DECLSPEC_XFGVIRT(IBMDStreamingAudioPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IBMDStreamingAudioPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IBMDStreamingAudioPacket, GetPlayTime) HRESULT ( STDMETHODCALLTYPE *GetPlayTime )( IBMDStreamingAudioPacket * This, /* [in] */ ULONGLONG requestedTimeScale, /* [out] */ ULONGLONG *playTime); DECLSPEC_XFGVIRT(IBMDStreamingAudioPacket, GetPacketIndex) HRESULT ( STDMETHODCALLTYPE *GetPacketIndex )( IBMDStreamingAudioPacket * This, /* [out] */ unsigned int *packetIndex); END_INTERFACE } IBMDStreamingAudioPacketVtbl; interface IBMDStreamingAudioPacket { CONST_VTBL struct IBMDStreamingAudioPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingAudioPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingAudioPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingAudioPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingAudioPacket_GetCodec(This) \ ( (This)->lpVtbl -> GetCodec(This) ) #define IBMDStreamingAudioPacket_GetPayloadSize(This) \ ( (This)->lpVtbl -> GetPayloadSize(This) ) #define IBMDStreamingAudioPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IBMDStreamingAudioPacket_GetPlayTime(This,requestedTimeScale,playTime) \ ( (This)->lpVtbl -> GetPlayTime(This,requestedTimeScale,playTime) ) #define IBMDStreamingAudioPacket_GetPacketIndex(This,packetIndex) \ ( (This)->lpVtbl -> GetPacketIndex(This,packetIndex) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingAudioPacket_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingMPEG2TSPacket_INTERFACE_DEFINED__ #define __IBMDStreamingMPEG2TSPacket_INTERFACE_DEFINED__ /* interface IBMDStreamingMPEG2TSPacket */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingMPEG2TSPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("91810D1C-4FB3-4AAA-AE56-FA301D3DFA4C") IBMDStreamingMPEG2TSPacket : public IUnknown { public: virtual long STDMETHODCALLTYPE GetPayloadSize( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingMPEG2TSPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingMPEG2TSPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingMPEG2TSPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingMPEG2TSPacket * This); DECLSPEC_XFGVIRT(IBMDStreamingMPEG2TSPacket, GetPayloadSize) long ( STDMETHODCALLTYPE *GetPayloadSize )( IBMDStreamingMPEG2TSPacket * This); DECLSPEC_XFGVIRT(IBMDStreamingMPEG2TSPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IBMDStreamingMPEG2TSPacket * This, /* [out] */ void **buffer); END_INTERFACE } IBMDStreamingMPEG2TSPacketVtbl; interface IBMDStreamingMPEG2TSPacket { CONST_VTBL struct IBMDStreamingMPEG2TSPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingMPEG2TSPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingMPEG2TSPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingMPEG2TSPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingMPEG2TSPacket_GetPayloadSize(This) \ ( (This)->lpVtbl -> GetPayloadSize(This) ) #define IBMDStreamingMPEG2TSPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingMPEG2TSPacket_INTERFACE_DEFINED__ */ #ifndef __IBMDStreamingH264NALParser_INTERFACE_DEFINED__ #define __IBMDStreamingH264NALParser_INTERFACE_DEFINED__ /* interface IBMDStreamingH264NALParser */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IBMDStreamingH264NALParser; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("5867F18C-5BFA-4CCC-B2A7-9DFD140417D2") IBMDStreamingH264NALParser : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE IsNALSequenceParameterSet( /* [in] */ IBMDStreamingH264NALPacket *nal) = 0; virtual HRESULT STDMETHODCALLTYPE IsNALPictureParameterSet( /* [in] */ IBMDStreamingH264NALPacket *nal) = 0; virtual HRESULT STDMETHODCALLTYPE GetProfileAndLevelFromSPS( /* [in] */ IBMDStreamingH264NALPacket *nal, /* [out] */ unsigned int *profileIdc, /* [out] */ unsigned int *profileCompatability, /* [out] */ unsigned int *levelIdc) = 0; }; #else /* C style interface */ typedef struct IBMDStreamingH264NALParserVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IBMDStreamingH264NALParser * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IBMDStreamingH264NALParser * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IBMDStreamingH264NALParser * This); DECLSPEC_XFGVIRT(IBMDStreamingH264NALParser, IsNALSequenceParameterSet) HRESULT ( STDMETHODCALLTYPE *IsNALSequenceParameterSet )( IBMDStreamingH264NALParser * This, /* [in] */ IBMDStreamingH264NALPacket *nal); DECLSPEC_XFGVIRT(IBMDStreamingH264NALParser, IsNALPictureParameterSet) HRESULT ( STDMETHODCALLTYPE *IsNALPictureParameterSet )( IBMDStreamingH264NALParser * This, /* [in] */ IBMDStreamingH264NALPacket *nal); DECLSPEC_XFGVIRT(IBMDStreamingH264NALParser, GetProfileAndLevelFromSPS) HRESULT ( STDMETHODCALLTYPE *GetProfileAndLevelFromSPS )( IBMDStreamingH264NALParser * This, /* [in] */ IBMDStreamingH264NALPacket *nal, /* [out] */ unsigned int *profileIdc, /* [out] */ unsigned int *profileCompatability, /* [out] */ unsigned int *levelIdc); END_INTERFACE } IBMDStreamingH264NALParserVtbl; interface IBMDStreamingH264NALParser { CONST_VTBL struct IBMDStreamingH264NALParserVtbl *lpVtbl; }; #ifdef COBJMACROS #define IBMDStreamingH264NALParser_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IBMDStreamingH264NALParser_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IBMDStreamingH264NALParser_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IBMDStreamingH264NALParser_IsNALSequenceParameterSet(This,nal) \ ( (This)->lpVtbl -> IsNALSequenceParameterSet(This,nal) ) #define IBMDStreamingH264NALParser_IsNALPictureParameterSet(This,nal) \ ( (This)->lpVtbl -> IsNALPictureParameterSet(This,nal) ) #define IBMDStreamingH264NALParser_GetProfileAndLevelFromSPS(This,nal,profileIdc,profileCompatability,levelIdc) \ ( (This)->lpVtbl -> GetProfileAndLevelFromSPS(This,nal,profileIdc,profileCompatability,levelIdc) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IBMDStreamingH264NALParser_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_CBMDStreamingDiscovery; #ifdef __cplusplus class DECLSPEC_UUID("23A4EDF5-A0E5-432C-94EF-3BABB5F81C82") CBMDStreamingDiscovery; #endif EXTERN_C const CLSID CLSID_CBMDStreamingH264NALParser; #ifdef __cplusplus class DECLSPEC_UUID("7753EFBD-951C-407C-97A5-23C737B73B52") CBMDStreamingH264NALParser; #endif #ifndef __IDeckLinkVideoOutputCallback_INTERFACE_DEFINED__ #define __IDeckLinkVideoOutputCallback_INTERFACE_DEFINED__ /* interface IDeckLinkVideoOutputCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoOutputCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("5BE6DF26-02CE-433E-99D9-9A87C3AC171F") IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( /* [in] */ IDeckLinkVideoFrame *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoOutputCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoOutputCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoOutputCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoOutputCallback * This); DECLSPEC_XFGVIRT(IDeckLinkVideoOutputCallback, ScheduledFrameCompleted) HRESULT ( STDMETHODCALLTYPE *ScheduledFrameCompleted )( IDeckLinkVideoOutputCallback * This, /* [in] */ IDeckLinkVideoFrame *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result); DECLSPEC_XFGVIRT(IDeckLinkVideoOutputCallback, ScheduledPlaybackHasStopped) HRESULT ( STDMETHODCALLTYPE *ScheduledPlaybackHasStopped )( IDeckLinkVideoOutputCallback * This); END_INTERFACE } IDeckLinkVideoOutputCallbackVtbl; interface IDeckLinkVideoOutputCallback { CONST_VTBL struct IDeckLinkVideoOutputCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoOutputCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoOutputCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoOutputCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoOutputCallback_ScheduledFrameCompleted(This,completedFrame,result) \ ( (This)->lpVtbl -> ScheduledFrameCompleted(This,completedFrame,result) ) #define IDeckLinkVideoOutputCallback_ScheduledPlaybackHasStopped(This) \ ( (This)->lpVtbl -> ScheduledPlaybackHasStopped(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoOutputCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInputCallback_INTERFACE_DEFINED__ #define __IDeckLinkInputCallback_INTERFACE_DEFINED__ /* interface IDeckLinkInputCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInputCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3A94F075-C37D-4BA8-BCC0-1D778C8F881B") IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged( /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( /* [in] */ IDeckLinkVideoInputFrame *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInputCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInputCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInputCallback * This); DECLSPEC_XFGVIRT(IDeckLinkInputCallback, VideoInputFormatChanged) HRESULT ( STDMETHODCALLTYPE *VideoInputFormatChanged )( IDeckLinkInputCallback * This, /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags); DECLSPEC_XFGVIRT(IDeckLinkInputCallback, VideoInputFrameArrived) HRESULT ( STDMETHODCALLTYPE *VideoInputFrameArrived )( IDeckLinkInputCallback * This, /* [in] */ IDeckLinkVideoInputFrame *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket); END_INTERFACE } IDeckLinkInputCallbackVtbl; interface IDeckLinkInputCallback { CONST_VTBL struct IDeckLinkInputCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInputCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInputCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInputCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInputCallback_VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) \ ( (This)->lpVtbl -> VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) ) #define IDeckLinkInputCallback_VideoInputFrameArrived(This,videoFrame,audioPacket) \ ( (This)->lpVtbl -> VideoInputFrameArrived(This,videoFrame,audioPacket) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInputCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkEncoderInputCallback_INTERFACE_DEFINED__ #define __IDeckLinkEncoderInputCallback_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderInputCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderInputCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("ACF13E61-F4A0-4974-A6A7-59AFF6268B31") IDeckLinkEncoderInputCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputSignalChanged( /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT STDMETHODCALLTYPE VideoPacketArrived( /* [in] */ IDeckLinkEncoderVideoPacket *videoPacket) = 0; virtual HRESULT STDMETHODCALLTYPE AudioPacketArrived( /* [in] */ IDeckLinkEncoderAudioPacket *audioPacket) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderInputCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderInputCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderInputCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderInputCallback * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInputCallback, VideoInputSignalChanged) HRESULT ( STDMETHODCALLTYPE *VideoInputSignalChanged )( IDeckLinkEncoderInputCallback * This, /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags); DECLSPEC_XFGVIRT(IDeckLinkEncoderInputCallback, VideoPacketArrived) HRESULT ( STDMETHODCALLTYPE *VideoPacketArrived )( IDeckLinkEncoderInputCallback * This, /* [in] */ IDeckLinkEncoderVideoPacket *videoPacket); DECLSPEC_XFGVIRT(IDeckLinkEncoderInputCallback, AudioPacketArrived) HRESULT ( STDMETHODCALLTYPE *AudioPacketArrived )( IDeckLinkEncoderInputCallback * This, /* [in] */ IDeckLinkEncoderAudioPacket *audioPacket); END_INTERFACE } IDeckLinkEncoderInputCallbackVtbl; interface IDeckLinkEncoderInputCallback { CONST_VTBL struct IDeckLinkEncoderInputCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderInputCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderInputCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderInputCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderInputCallback_VideoInputSignalChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) \ ( (This)->lpVtbl -> VideoInputSignalChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) ) #define IDeckLinkEncoderInputCallback_VideoPacketArrived(This,videoPacket) \ ( (This)->lpVtbl -> VideoPacketArrived(This,videoPacket) ) #define IDeckLinkEncoderInputCallback_AudioPacketArrived(This,audioPacket) \ ( (This)->lpVtbl -> AudioPacketArrived(This,audioPacket) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderInputCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoBufferAllocator_INTERFACE_DEFINED__ #define __IDeckLinkVideoBufferAllocator_INTERFACE_DEFINED__ /* interface IDeckLinkVideoBufferAllocator */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoBufferAllocator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3481A4DF-2B11-4E55-AC61-836B87985E9A") IDeckLinkVideoBufferAllocator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE AllocateVideoBuffer( /* [out] */ IDeckLinkVideoBuffer **allocatedBuffer) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoBufferAllocatorVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoBufferAllocator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoBufferAllocator * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoBufferAllocator * This); DECLSPEC_XFGVIRT(IDeckLinkVideoBufferAllocator, AllocateVideoBuffer) HRESULT ( STDMETHODCALLTYPE *AllocateVideoBuffer )( IDeckLinkVideoBufferAllocator * This, /* [out] */ IDeckLinkVideoBuffer **allocatedBuffer); END_INTERFACE } IDeckLinkVideoBufferAllocatorVtbl; interface IDeckLinkVideoBufferAllocator { CONST_VTBL struct IDeckLinkVideoBufferAllocatorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoBufferAllocator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoBufferAllocator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoBufferAllocator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoBufferAllocator_AllocateVideoBuffer(This,allocatedBuffer) \ ( (This)->lpVtbl -> AllocateVideoBuffer(This,allocatedBuffer) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoBufferAllocator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoBufferAllocatorProvider_INTERFACE_DEFINED__ #define __IDeckLinkVideoBufferAllocatorProvider_INTERFACE_DEFINED__ /* interface IDeckLinkVideoBufferAllocatorProvider */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoBufferAllocatorProvider; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("08B80403-BFF2-49D0-B448-8C908B9E9FC9") IDeckLinkVideoBufferAllocatorProvider : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetVideoBufferAllocator( /* [in] */ unsigned int bufferSize, /* [in] */ unsigned int width, /* [in] */ unsigned int height, /* [in] */ unsigned int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoBufferAllocator **allocator) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoBufferAllocatorProviderVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoBufferAllocatorProvider * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoBufferAllocatorProvider * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoBufferAllocatorProvider * This); DECLSPEC_XFGVIRT(IDeckLinkVideoBufferAllocatorProvider, GetVideoBufferAllocator) HRESULT ( STDMETHODCALLTYPE *GetVideoBufferAllocator )( IDeckLinkVideoBufferAllocatorProvider * This, /* [in] */ unsigned int bufferSize, /* [in] */ unsigned int width, /* [in] */ unsigned int height, /* [in] */ unsigned int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoBufferAllocator **allocator); END_INTERFACE } IDeckLinkVideoBufferAllocatorProviderVtbl; interface IDeckLinkVideoBufferAllocatorProvider { CONST_VTBL struct IDeckLinkVideoBufferAllocatorProviderVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoBufferAllocatorProvider_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoBufferAllocatorProvider_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoBufferAllocatorProvider_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoBufferAllocatorProvider_GetVideoBufferAllocator(This,bufferSize,width,height,rowBytes,pixelFormat,allocator) \ ( (This)->lpVtbl -> GetVideoBufferAllocator(This,bufferSize,width,height,rowBytes,pixelFormat,allocator) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoBufferAllocatorProvider_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAudioOutputCallback_INTERFACE_DEFINED__ #define __IDeckLinkAudioOutputCallback_INTERFACE_DEFINED__ /* interface IDeckLinkAudioOutputCallback */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAudioOutputCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("403C681B-7F46-4A12-B993-2BB127084EE6") IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples( /* [in] */ BOOL preroll) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAudioOutputCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAudioOutputCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAudioOutputCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAudioOutputCallback * This); DECLSPEC_XFGVIRT(IDeckLinkAudioOutputCallback, RenderAudioSamples) HRESULT ( STDMETHODCALLTYPE *RenderAudioSamples )( IDeckLinkAudioOutputCallback * This, /* [in] */ BOOL preroll); END_INTERFACE } IDeckLinkAudioOutputCallbackVtbl; interface IDeckLinkAudioOutputCallback { CONST_VTBL struct IDeckLinkAudioOutputCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAudioOutputCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAudioOutputCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAudioOutputCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAudioOutputCallback_RenderAudioSamples(This,preroll) \ ( (This)->lpVtbl -> RenderAudioSamples(This,preroll) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAudioOutputCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkIterator_INTERFACE_DEFINED__ #define __IDeckLinkIterator_INTERFACE_DEFINED__ /* interface IDeckLinkIterator */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkIterator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("50FB36CD-3063-4B73-BDBB-958087F2D8BA") IDeckLinkIterator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLink **deckLinkInstance) = 0; }; #else /* C style interface */ typedef struct IDeckLinkIteratorVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkIterator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkIterator * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkIterator * This); DECLSPEC_XFGVIRT(IDeckLinkIterator, Next) HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkIterator * This, /* [out] */ IDeckLink **deckLinkInstance); END_INTERFACE } IDeckLinkIteratorVtbl; interface IDeckLinkIterator { CONST_VTBL struct IDeckLinkIteratorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkIterator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkIterator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkIterator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkIterator_Next(This,deckLinkInstance) \ ( (This)->lpVtbl -> Next(This,deckLinkInstance) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkIterator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAPIInformation_INTERFACE_DEFINED__ #define __IDeckLinkAPIInformation_INTERFACE_DEFINED__ /* interface IDeckLinkAPIInformation */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAPIInformation; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("7BEA3C68-730D-4322-AF34-8A7152B532A4") IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ BSTR *value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAPIInformationVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAPIInformation * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAPIInformation * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAPIInformation * This); DECLSPEC_XFGVIRT(IDeckLinkAPIInformation, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkAPIInformation * This, /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkAPIInformation, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkAPIInformation * This, /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkAPIInformation, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkAPIInformation * This, /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkAPIInformation, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkAPIInformation * This, /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ BSTR *value); END_INTERFACE } IDeckLinkAPIInformationVtbl; interface IDeckLinkAPIInformation { CONST_VTBL struct IDeckLinkAPIInformationVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAPIInformation_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAPIInformation_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAPIInformation_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAPIInformation_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkAPIInformation_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkAPIInformation_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkAPIInformation_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAPIInformation_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkIPFlowAttributes_INTERFACE_DEFINED__ #define __IDeckLinkIPFlowAttributes_INTERFACE_DEFINED__ /* interface IDeckLinkIPFlowAttributes */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkIPFlowAttributes; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CDA938DA-6479-40C6-B2EC-A3579B3AEECD") IDeckLinkIPFlowAttributes : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkIPFlowAttributeID attrID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkIPFlowAttributeID attrID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkIPFlowAttributeID attrID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkIPFlowAttributeID attrID, /* [out] */ BSTR *value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkIPFlowAttributesVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkIPFlowAttributes * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkIPFlowAttributes * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkIPFlowAttributes * This); DECLSPEC_XFGVIRT(IDeckLinkIPFlowAttributes, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkIPFlowAttributes * This, /* [in] */ BMDDeckLinkIPFlowAttributeID attrID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowAttributes, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkIPFlowAttributes * This, /* [in] */ BMDDeckLinkIPFlowAttributeID attrID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowAttributes, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkIPFlowAttributes * This, /* [in] */ BMDDeckLinkIPFlowAttributeID attrID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowAttributes, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkIPFlowAttributes * This, /* [in] */ BMDDeckLinkIPFlowAttributeID attrID, /* [out] */ BSTR *value); END_INTERFACE } IDeckLinkIPFlowAttributesVtbl; interface IDeckLinkIPFlowAttributes { CONST_VTBL struct IDeckLinkIPFlowAttributesVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkIPFlowAttributes_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkIPFlowAttributes_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkIPFlowAttributes_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkIPFlowAttributes_GetInt(This,attrID,value) \ ( (This)->lpVtbl -> GetInt(This,attrID,value) ) #define IDeckLinkIPFlowAttributes_GetFlag(This,attrID,value) \ ( (This)->lpVtbl -> GetFlag(This,attrID,value) ) #define IDeckLinkIPFlowAttributes_GetFloat(This,attrID,value) \ ( (This)->lpVtbl -> GetFloat(This,attrID,value) ) #define IDeckLinkIPFlowAttributes_GetString(This,attrID,value) \ ( (This)->lpVtbl -> GetString(This,attrID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkIPFlowAttributes_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkIPFlowStatus_INTERFACE_DEFINED__ #define __IDeckLinkIPFlowStatus_INTERFACE_DEFINED__ /* interface IDeckLinkIPFlowStatus */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkIPFlowStatus; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("31C41656-4992-4396-BBE9-5F8406AAB5AF") IDeckLinkIPFlowStatus : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkIPFlowStatusID statusID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkIPFlowStatusID statusID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkIPFlowStatusID statusID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkIPFlowStatusID statusID, /* [out] */ BSTR *value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkIPFlowStatusVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkIPFlowStatus * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkIPFlowStatus * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkIPFlowStatus * This); DECLSPEC_XFGVIRT(IDeckLinkIPFlowStatus, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkIPFlowStatus * This, /* [in] */ BMDDeckLinkIPFlowStatusID statusID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowStatus, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkIPFlowStatus * This, /* [in] */ BMDDeckLinkIPFlowStatusID statusID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowStatus, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkIPFlowStatus * This, /* [in] */ BMDDeckLinkIPFlowStatusID statusID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowStatus, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkIPFlowStatus * This, /* [in] */ BMDDeckLinkIPFlowStatusID statusID, /* [out] */ BSTR *value); END_INTERFACE } IDeckLinkIPFlowStatusVtbl; interface IDeckLinkIPFlowStatus { CONST_VTBL struct IDeckLinkIPFlowStatusVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkIPFlowStatus_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkIPFlowStatus_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkIPFlowStatus_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkIPFlowStatus_GetInt(This,statusID,value) \ ( (This)->lpVtbl -> GetInt(This,statusID,value) ) #define IDeckLinkIPFlowStatus_GetFlag(This,statusID,value) \ ( (This)->lpVtbl -> GetFlag(This,statusID,value) ) #define IDeckLinkIPFlowStatus_GetFloat(This,statusID,value) \ ( (This)->lpVtbl -> GetFloat(This,statusID,value) ) #define IDeckLinkIPFlowStatus_GetString(This,statusID,value) \ ( (This)->lpVtbl -> GetString(This,statusID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkIPFlowStatus_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkIPFlowSetting_INTERFACE_DEFINED__ #define __IDeckLinkIPFlowSetting_INTERFACE_DEFINED__ /* interface IDeckLinkIPFlowSetting */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkIPFlowSetting; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("86DD9174-27D3-4032-B2AD-6067C3BB2424") IDeckLinkIPFlowSetting : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [in] */ BSTR value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkIPFlowSettingVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkIPFlowSetting * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkIPFlowSetting * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkIPFlowSetting * This); DECLSPEC_XFGVIRT(IDeckLinkIPFlowSetting, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkIPFlowSetting * This, /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowSetting, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkIPFlowSetting * This, /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowSetting, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkIPFlowSetting * This, /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowSetting, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkIPFlowSetting * This, /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowSetting, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkIPFlowSetting * This, /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowSetting, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkIPFlowSetting * This, /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowSetting, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkIPFlowSetting * This, /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkIPFlowSetting, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkIPFlowSetting * This, /* [in] */ BMDDeckLinkIPFlowSettingID settingID, /* [in] */ BSTR value); END_INTERFACE } IDeckLinkIPFlowSettingVtbl; interface IDeckLinkIPFlowSetting { CONST_VTBL struct IDeckLinkIPFlowSettingVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkIPFlowSetting_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkIPFlowSetting_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkIPFlowSetting_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkIPFlowSetting_GetInt(This,settingID,value) \ ( (This)->lpVtbl -> GetInt(This,settingID,value) ) #define IDeckLinkIPFlowSetting_GetFlag(This,settingID,value) \ ( (This)->lpVtbl -> GetFlag(This,settingID,value) ) #define IDeckLinkIPFlowSetting_GetFloat(This,settingID,value) \ ( (This)->lpVtbl -> GetFloat(This,settingID,value) ) #define IDeckLinkIPFlowSetting_GetString(This,settingID,value) \ ( (This)->lpVtbl -> GetString(This,settingID,value) ) #define IDeckLinkIPFlowSetting_SetInt(This,settingID,value) \ ( (This)->lpVtbl -> SetInt(This,settingID,value) ) #define IDeckLinkIPFlowSetting_SetFlag(This,settingID,value) \ ( (This)->lpVtbl -> SetFlag(This,settingID,value) ) #define IDeckLinkIPFlowSetting_SetFloat(This,settingID,value) \ ( (This)->lpVtbl -> SetFloat(This,settingID,value) ) #define IDeckLinkIPFlowSetting_SetString(This,settingID,value) \ ( (This)->lpVtbl -> SetString(This,settingID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkIPFlowSetting_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkIPFlow_INTERFACE_DEFINED__ #define __IDeckLinkIPFlow_INTERFACE_DEFINED__ /* interface IDeckLinkIPFlow */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkIPFlow; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C5FC83C7-5B8E-42A7-9A40-7C065955D4E1") IDeckLinkIPFlow : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Enable( void) = 0; virtual HRESULT STDMETHODCALLTYPE Disable( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkIPFlowVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkIPFlow * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkIPFlow * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkIPFlow * This); DECLSPEC_XFGVIRT(IDeckLinkIPFlow, Enable) HRESULT ( STDMETHODCALLTYPE *Enable )( IDeckLinkIPFlow * This); DECLSPEC_XFGVIRT(IDeckLinkIPFlow, Disable) HRESULT ( STDMETHODCALLTYPE *Disable )( IDeckLinkIPFlow * This); END_INTERFACE } IDeckLinkIPFlowVtbl; interface IDeckLinkIPFlow { CONST_VTBL struct IDeckLinkIPFlowVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkIPFlow_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkIPFlow_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkIPFlow_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkIPFlow_Enable(This) \ ( (This)->lpVtbl -> Enable(This) ) #define IDeckLinkIPFlow_Disable(This) \ ( (This)->lpVtbl -> Disable(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkIPFlow_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkIPFlowIterator_INTERFACE_DEFINED__ #define __IDeckLinkIPFlowIterator_INTERFACE_DEFINED__ /* interface IDeckLinkIPFlowIterator */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkIPFlowIterator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("BD296AB2-A5C5-4153-888F-AAB1FDBD8A5C") IDeckLinkIPFlowIterator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLinkIPFlow **deckLinkIPFlowInstance) = 0; }; #else /* C style interface */ typedef struct IDeckLinkIPFlowIteratorVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkIPFlowIterator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkIPFlowIterator * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkIPFlowIterator * This); DECLSPEC_XFGVIRT(IDeckLinkIPFlowIterator, Next) HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkIPFlowIterator * This, /* [out] */ IDeckLinkIPFlow **deckLinkIPFlowInstance); END_INTERFACE } IDeckLinkIPFlowIteratorVtbl; interface IDeckLinkIPFlowIterator { CONST_VTBL struct IDeckLinkIPFlowIteratorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkIPFlowIterator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkIPFlowIterator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkIPFlowIterator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkIPFlowIterator_Next(This,deckLinkIPFlowInstance) \ ( (This)->lpVtbl -> Next(This,deckLinkIPFlowInstance) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkIPFlowIterator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkOutput_INTERFACE_DEFINED__ #define __IDeckLinkOutput_INTERFACE_DEFINED__ /* interface IDeckLinkOutput */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkOutput; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("1A8077F1-9FE2-4533-8147-2294305E253F") IDeckLinkOutput : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoOutputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame( /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrameWithBuffer( /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [in] */ IDeckLinkVideoBuffer *buffer, /* [out] */ IDeckLinkMutableVideoFrame **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE RowBytesForPixelFormat( /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ int width, /* [out] */ int *rowBytes) = 0; virtual HRESULT STDMETHODCALLTYPE CreateAncillaryData( /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync( /* [in] */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame( /* [in] */ IDeckLinkVideoFrame *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback( /* [in] */ IDeckLinkVideoOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedVideoFrameCount( /* [out] */ unsigned int *bufferedFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount, /* [in] */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync( /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [out] */ unsigned int *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples( /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned int *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned int *bufferedSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioCallback( /* [in] */ IDeckLinkAudioOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback( /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback( /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE IsScheduledPlaybackRunning( /* [out] */ BOOL *active) = 0; virtual HRESULT STDMETHODCALLTYPE GetScheduledStreamTime( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE GetReferenceStatus( /* [out] */ BMDReferenceStatus *referenceStatus) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameCompletionReferenceTimestamp( /* [in] */ IDeckLinkVideoFrame *theFrame, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *frameCompletionTimestamp) = 0; }; #else /* C style interface */ typedef struct IDeckLinkOutputVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkOutput * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkOutput * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkOutput * This); DECLSPEC_XFGVIRT(IDeckLinkOutput, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkOutput * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoOutputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkOutput, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkOutput * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkOutput, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkOutput * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkOutput, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkOutput * This, /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput, EnableVideoOutput) HRESULT ( STDMETHODCALLTYPE *EnableVideoOutput )( IDeckLinkOutput * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkOutput, DisableVideoOutput) HRESULT ( STDMETHODCALLTYPE *DisableVideoOutput )( IDeckLinkOutput * This); DECLSPEC_XFGVIRT(IDeckLinkOutput, CreateVideoFrame) HRESULT ( STDMETHODCALLTYPE *CreateVideoFrame )( IDeckLinkOutput * This, /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame **outFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput, CreateVideoFrameWithBuffer) HRESULT ( STDMETHODCALLTYPE *CreateVideoFrameWithBuffer )( IDeckLinkOutput * This, /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [in] */ IDeckLinkVideoBuffer *buffer, /* [out] */ IDeckLinkMutableVideoFrame **outFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput, RowBytesForPixelFormat) HRESULT ( STDMETHODCALLTYPE *RowBytesForPixelFormat )( IDeckLinkOutput * This, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ int width, /* [out] */ int *rowBytes); DECLSPEC_XFGVIRT(IDeckLinkOutput, CreateAncillaryData) HRESULT ( STDMETHODCALLTYPE *CreateAncillaryData )( IDeckLinkOutput * This, /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer); DECLSPEC_XFGVIRT(IDeckLinkOutput, DisplayVideoFrameSync) HRESULT ( STDMETHODCALLTYPE *DisplayVideoFrameSync )( IDeckLinkOutput * This, /* [in] */ IDeckLinkVideoFrame *theFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput, ScheduleVideoFrame) HRESULT ( STDMETHODCALLTYPE *ScheduleVideoFrame )( IDeckLinkOutput * This, /* [in] */ IDeckLinkVideoFrame *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkOutput, SetScheduledFrameCompletionCallback) HRESULT ( STDMETHODCALLTYPE *SetScheduledFrameCompletionCallback )( IDeckLinkOutput * This, /* [in] */ IDeckLinkVideoOutputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput, GetBufferedVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetBufferedVideoFrameCount )( IDeckLinkOutput * This, /* [out] */ unsigned int *bufferedFrameCount); DECLSPEC_XFGVIRT(IDeckLinkOutput, EnableAudioOutput) HRESULT ( STDMETHODCALLTYPE *EnableAudioOutput )( IDeckLinkOutput * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount, /* [in] */ BMDAudioOutputStreamType streamType); DECLSPEC_XFGVIRT(IDeckLinkOutput, DisableAudioOutput) HRESULT ( STDMETHODCALLTYPE *DisableAudioOutput )( IDeckLinkOutput * This); DECLSPEC_XFGVIRT(IDeckLinkOutput, WriteAudioSamplesSync) HRESULT ( STDMETHODCALLTYPE *WriteAudioSamplesSync )( IDeckLinkOutput * This, /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [out] */ unsigned int *sampleFramesWritten); DECLSPEC_XFGVIRT(IDeckLinkOutput, BeginAudioPreroll) HRESULT ( STDMETHODCALLTYPE *BeginAudioPreroll )( IDeckLinkOutput * This); DECLSPEC_XFGVIRT(IDeckLinkOutput, EndAudioPreroll) HRESULT ( STDMETHODCALLTYPE *EndAudioPreroll )( IDeckLinkOutput * This); DECLSPEC_XFGVIRT(IDeckLinkOutput, ScheduleAudioSamples) HRESULT ( STDMETHODCALLTYPE *ScheduleAudioSamples )( IDeckLinkOutput * This, /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned int *sampleFramesWritten); DECLSPEC_XFGVIRT(IDeckLinkOutput, GetBufferedAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkOutput * This, /* [out] */ unsigned int *bufferedSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkOutput, FlushBufferedAudioSamples) HRESULT ( STDMETHODCALLTYPE *FlushBufferedAudioSamples )( IDeckLinkOutput * This); DECLSPEC_XFGVIRT(IDeckLinkOutput, SetAudioCallback) HRESULT ( STDMETHODCALLTYPE *SetAudioCallback )( IDeckLinkOutput * This, /* [in] */ IDeckLinkAudioOutputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput, StartScheduledPlayback) HRESULT ( STDMETHODCALLTYPE *StartScheduledPlayback )( IDeckLinkOutput * This, /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed); DECLSPEC_XFGVIRT(IDeckLinkOutput, StopScheduledPlayback) HRESULT ( STDMETHODCALLTYPE *StopScheduledPlayback )( IDeckLinkOutput * This, /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkOutput, IsScheduledPlaybackRunning) HRESULT ( STDMETHODCALLTYPE *IsScheduledPlaybackRunning )( IDeckLinkOutput * This, /* [out] */ BOOL *active); DECLSPEC_XFGVIRT(IDeckLinkOutput, GetScheduledStreamTime) HRESULT ( STDMETHODCALLTYPE *GetScheduledStreamTime )( IDeckLinkOutput * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed); DECLSPEC_XFGVIRT(IDeckLinkOutput, GetReferenceStatus) HRESULT ( STDMETHODCALLTYPE *GetReferenceStatus )( IDeckLinkOutput * This, /* [out] */ BMDReferenceStatus *referenceStatus); DECLSPEC_XFGVIRT(IDeckLinkOutput, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkOutput * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput, GetFrameCompletionReferenceTimestamp) HRESULT ( STDMETHODCALLTYPE *GetFrameCompletionReferenceTimestamp )( IDeckLinkOutput * This, /* [in] */ IDeckLinkVideoFrame *theFrame, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *frameCompletionTimestamp); END_INTERFACE } IDeckLinkOutputVtbl; interface IDeckLinkOutput { CONST_VTBL struct IDeckLinkOutputVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkOutput_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkOutput_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkOutput_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkOutput_DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) ) #define IDeckLinkOutput_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkOutput_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkOutput_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkOutput_EnableVideoOutput(This,displayMode,flags) \ ( (This)->lpVtbl -> EnableVideoOutput(This,displayMode,flags) ) #define IDeckLinkOutput_DisableVideoOutput(This) \ ( (This)->lpVtbl -> DisableVideoOutput(This) ) #define IDeckLinkOutput_CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_CreateVideoFrameWithBuffer(This,width,height,rowBytes,pixelFormat,flags,buffer,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrameWithBuffer(This,width,height,rowBytes,pixelFormat,flags,buffer,outFrame) ) #define IDeckLinkOutput_RowBytesForPixelFormat(This,pixelFormat,width,rowBytes) \ ( (This)->lpVtbl -> RowBytesForPixelFormat(This,pixelFormat,width,rowBytes) ) #define IDeckLinkOutput_CreateAncillaryData(This,pixelFormat,outBuffer) \ ( (This)->lpVtbl -> CreateAncillaryData(This,pixelFormat,outBuffer) ) #define IDeckLinkOutput_DisplayVideoFrameSync(This,theFrame) \ ( (This)->lpVtbl -> DisplayVideoFrameSync(This,theFrame) ) #define IDeckLinkOutput_ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) \ ( (This)->lpVtbl -> ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) ) #define IDeckLinkOutput_SetScheduledFrameCompletionCallback(This,theCallback) \ ( (This)->lpVtbl -> SetScheduledFrameCompletionCallback(This,theCallback) ) #define IDeckLinkOutput_GetBufferedVideoFrameCount(This,bufferedFrameCount) \ ( (This)->lpVtbl -> GetBufferedVideoFrameCount(This,bufferedFrameCount) ) #define IDeckLinkOutput_EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) \ ( (This)->lpVtbl -> EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) ) #define IDeckLinkOutput_DisableAudioOutput(This) \ ( (This)->lpVtbl -> DisableAudioOutput(This) ) #define IDeckLinkOutput_WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) \ ( (This)->lpVtbl -> WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) ) #define IDeckLinkOutput_BeginAudioPreroll(This) \ ( (This)->lpVtbl -> BeginAudioPreroll(This) ) #define IDeckLinkOutput_EndAudioPreroll(This) \ ( (This)->lpVtbl -> EndAudioPreroll(This) ) #define IDeckLinkOutput_ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) \ ( (This)->lpVtbl -> ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) ) #define IDeckLinkOutput_GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) ) #define IDeckLinkOutput_FlushBufferedAudioSamples(This) \ ( (This)->lpVtbl -> FlushBufferedAudioSamples(This) ) #define IDeckLinkOutput_SetAudioCallback(This,theCallback) \ ( (This)->lpVtbl -> SetAudioCallback(This,theCallback) ) #define IDeckLinkOutput_StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) \ ( (This)->lpVtbl -> StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) ) #define IDeckLinkOutput_StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) \ ( (This)->lpVtbl -> StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) ) #define IDeckLinkOutput_IsScheduledPlaybackRunning(This,active) \ ( (This)->lpVtbl -> IsScheduledPlaybackRunning(This,active) ) #define IDeckLinkOutput_GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) \ ( (This)->lpVtbl -> GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) ) #define IDeckLinkOutput_GetReferenceStatus(This,referenceStatus) \ ( (This)->lpVtbl -> GetReferenceStatus(This,referenceStatus) ) #define IDeckLinkOutput_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #define IDeckLinkOutput_GetFrameCompletionReferenceTimestamp(This,theFrame,desiredTimeScale,frameCompletionTimestamp) \ ( (This)->lpVtbl -> GetFrameCompletionReferenceTimestamp(This,theFrame,desiredTimeScale,frameCompletionTimestamp) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkOutput_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_INTERFACE_DEFINED__ #define __IDeckLinkInput_INTERFACE_DEFINED__ /* interface IDeckLinkInput */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("4095DB82-E294-4B8C-AAA8-3B9E80C49336") IDeckLinkInput : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoInputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInputWithAllocatorProvider( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags, /* [in] */ IDeckLinkVideoBufferAllocatorProvider *allocatorProvider) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoFrameCount( /* [out] */ unsigned int *availableFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned int *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput * This); DECLSPEC_XFGVIRT(IDeckLinkInput, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoInputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkInput, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkInput * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkInput, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkInput, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkInput * This, /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkInput, EnableVideoInput) HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkInput, EnableVideoInputWithAllocatorProvider) HRESULT ( STDMETHODCALLTYPE *EnableVideoInputWithAllocatorProvider )( IDeckLinkInput * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags, /* [in] */ IDeckLinkVideoBufferAllocatorProvider *allocatorProvider); DECLSPEC_XFGVIRT(IDeckLinkInput, DisableVideoInput) HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput * This); DECLSPEC_XFGVIRT(IDeckLinkInput, GetAvailableVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableVideoFrameCount )( IDeckLinkInput * This, /* [out] */ unsigned int *availableFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput, EnableAudioInput) HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount); DECLSPEC_XFGVIRT(IDeckLinkInput, DisableAudioInput) HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput * This); DECLSPEC_XFGVIRT(IDeckLinkInput, GetAvailableAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkInput * This, /* [out] */ unsigned int *availableSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput, StartStreams) HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput * This); DECLSPEC_XFGVIRT(IDeckLinkInput, StopStreams) HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput * This); DECLSPEC_XFGVIRT(IDeckLinkInput, PauseStreams) HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput * This); DECLSPEC_XFGVIRT(IDeckLinkInput, FlushStreams) HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkInput * This); DECLSPEC_XFGVIRT(IDeckLinkInput, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput * This, /* [in] */ IDeckLinkInputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkInput, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkInput * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkInputVtbl; interface IDeckLinkInput { CONST_VTBL struct IDeckLinkInputVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) ) #define IDeckLinkInput_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkInput_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkInput_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_EnableVideoInputWithAllocatorProvider(This,displayMode,pixelFormat,flags,allocatorProvider) \ ( (This)->lpVtbl -> EnableVideoInputWithAllocatorProvider(This,displayMode,pixelFormat,flags,allocatorProvider) ) #define IDeckLinkInput_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_GetAvailableVideoFrameCount(This,availableFrameCount) \ ( (This)->lpVtbl -> GetAvailableVideoFrameCount(This,availableFrameCount) ) #define IDeckLinkInput_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkInput_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkInput_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkInput_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkIPExtensions_INTERFACE_DEFINED__ #define __IDeckLinkIPExtensions_INTERFACE_DEFINED__ /* interface IDeckLinkIPExtensions */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkIPExtensions; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("46CF7903-A9FD-4D0B-8FFC-0103722AB442") IDeckLinkIPExtensions : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetDeckLinkIPFlowIterator( /* [out] */ IDeckLinkIPFlowIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE GetIPFlowByID( /* [in] */ BMDIPFlowID id, /* [out] */ IDeckLinkIPFlow **flow) = 0; }; #else /* C style interface */ typedef struct IDeckLinkIPExtensionsVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkIPExtensions * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkIPExtensions * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkIPExtensions * This); DECLSPEC_XFGVIRT(IDeckLinkIPExtensions, GetDeckLinkIPFlowIterator) HRESULT ( STDMETHODCALLTYPE *GetDeckLinkIPFlowIterator )( IDeckLinkIPExtensions * This, /* [out] */ IDeckLinkIPFlowIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkIPExtensions, GetIPFlowByID) HRESULT ( STDMETHODCALLTYPE *GetIPFlowByID )( IDeckLinkIPExtensions * This, /* [in] */ BMDIPFlowID id, /* [out] */ IDeckLinkIPFlow **flow); END_INTERFACE } IDeckLinkIPExtensionsVtbl; interface IDeckLinkIPExtensions { CONST_VTBL struct IDeckLinkIPExtensionsVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkIPExtensions_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkIPExtensions_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkIPExtensions_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkIPExtensions_GetDeckLinkIPFlowIterator(This,iterator) \ ( (This)->lpVtbl -> GetDeckLinkIPFlowIterator(This,iterator) ) #define IDeckLinkIPExtensions_GetIPFlowByID(This,id,flow) \ ( (This)->lpVtbl -> GetIPFlowByID(This,id,flow) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkIPExtensions_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkHDMIInputEDID_INTERFACE_DEFINED__ #define __IDeckLinkHDMIInputEDID_INTERFACE_DEFINED__ /* interface IDeckLinkHDMIInputEDID */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkHDMIInputEDID; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("ABBBACBC-45BC-4665-9D92-ACE6E5A97902") IDeckLinkHDMIInputEDID : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkHDMIInputEDIDID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkHDMIInputEDIDID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE WriteToEDID( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkHDMIInputEDIDVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkHDMIInputEDID * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkHDMIInputEDID * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkHDMIInputEDID * This); DECLSPEC_XFGVIRT(IDeckLinkHDMIInputEDID, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkHDMIInputEDID * This, /* [in] */ BMDDeckLinkHDMIInputEDIDID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkHDMIInputEDID, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkHDMIInputEDID * This, /* [in] */ BMDDeckLinkHDMIInputEDIDID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkHDMIInputEDID, WriteToEDID) HRESULT ( STDMETHODCALLTYPE *WriteToEDID )( IDeckLinkHDMIInputEDID * This); END_INTERFACE } IDeckLinkHDMIInputEDIDVtbl; interface IDeckLinkHDMIInputEDID { CONST_VTBL struct IDeckLinkHDMIInputEDIDVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkHDMIInputEDID_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkHDMIInputEDID_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkHDMIInputEDID_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkHDMIInputEDID_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkHDMIInputEDID_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkHDMIInputEDID_WriteToEDID(This) \ ( (This)->lpVtbl -> WriteToEDID(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkHDMIInputEDID_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkEncoderInput_INTERFACE_DEFINED__ #define __IDeckLinkEncoderInput_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderInput */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderInput; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("46C1332E-6FD9-472A-8591-FE59C22192E1") IDeckLinkEncoderInput : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedCodec, /* [in] */ unsigned int requestedCodecProfile, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailablePacketsCount( /* [out] */ unsigned int *availablePacketsCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioFormat audioFormat, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned int *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkEncoderInputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderInputVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderInput * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderInput * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderInput * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkEncoderInput * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedCodec, /* [in] */ unsigned int requestedCodecProfile, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkEncoderInput * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkEncoderInput * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, EnableVideoInput) HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkEncoderInput * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, DisableVideoInput) HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkEncoderInput * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, GetAvailablePacketsCount) HRESULT ( STDMETHODCALLTYPE *GetAvailablePacketsCount )( IDeckLinkEncoderInput * This, /* [out] */ unsigned int *availablePacketsCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, EnableAudioInput) HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkEncoderInput * This, /* [in] */ BMDAudioFormat audioFormat, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, DisableAudioInput) HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkEncoderInput * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, GetAvailableAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkEncoderInput * This, /* [out] */ unsigned int *availableSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, StartStreams) HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkEncoderInput * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, StopStreams) HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkEncoderInput * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, PauseStreams) HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkEncoderInput * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, FlushStreams) HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkEncoderInput * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkEncoderInput * This, /* [in] */ IDeckLinkEncoderInputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkEncoderInput * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkEncoderInputVtbl; interface IDeckLinkEncoderInput { CONST_VTBL struct IDeckLinkEncoderInputVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderInput_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderInput_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderInput_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderInput_DoesSupportVideoMode(This,connection,requestedMode,requestedCodec,requestedCodecProfile,flags,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedCodec,requestedCodecProfile,flags,supported) ) #define IDeckLinkEncoderInput_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkEncoderInput_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkEncoderInput_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkEncoderInput_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkEncoderInput_GetAvailablePacketsCount(This,availablePacketsCount) \ ( (This)->lpVtbl -> GetAvailablePacketsCount(This,availablePacketsCount) ) #define IDeckLinkEncoderInput_EnableAudioInput(This,audioFormat,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,audioFormat,sampleRate,sampleType,channelCount) ) #define IDeckLinkEncoderInput_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkEncoderInput_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkEncoderInput_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkEncoderInput_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkEncoderInput_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkEncoderInput_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkEncoderInput_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkEncoderInput_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderInput_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoBuffer_INTERFACE_DEFINED__ #define __IDeckLinkVideoBuffer_INTERFACE_DEFINED__ /* interface IDeckLinkVideoBuffer */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoBuffer; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CCB4B64A-5C86-4E02-B778-885D352709FE") IDeckLinkVideoBuffer : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE StartAccess( /* [in] */ BMDBufferAccessFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE EndAccess( /* [in] */ BMDBufferAccessFlags flags) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoBufferVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoBuffer * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoBuffer * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoBuffer * This); DECLSPEC_XFGVIRT(IDeckLinkVideoBuffer, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoBuffer * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkVideoBuffer, StartAccess) HRESULT ( STDMETHODCALLTYPE *StartAccess )( IDeckLinkVideoBuffer * This, /* [in] */ BMDBufferAccessFlags flags); DECLSPEC_XFGVIRT(IDeckLinkVideoBuffer, EndAccess) HRESULT ( STDMETHODCALLTYPE *EndAccess )( IDeckLinkVideoBuffer * This, /* [in] */ BMDBufferAccessFlags flags); END_INTERFACE } IDeckLinkVideoBufferVtbl; interface IDeckLinkVideoBuffer { CONST_VTBL struct IDeckLinkVideoBufferVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoBuffer_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoBuffer_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoBuffer_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoBuffer_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoBuffer_StartAccess(This,flags) \ ( (This)->lpVtbl -> StartAccess(This,flags) ) #define IDeckLinkVideoBuffer_EndAccess(This,flags) \ ( (This)->lpVtbl -> EndAccess(This,flags) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoBuffer_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrame_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrame */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrame; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("6502091C-615F-4F51-BAF6-45C4256DD5B0") IDeckLinkVideoFrame : public IUnknown { public: virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual long STDMETHODCALLTYPE GetRowBytes( void) = 0; virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat( void) = 0; virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecode( /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode) = 0; virtual HRESULT STDMETHODCALLTYPE GetAncillaryData( /* [out] */ IDeckLinkVideoFrameAncillary **ancillary) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrameVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrame * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrame * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetWidth) long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetHeight) long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetRowBytes) long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetFlags) BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetAncillaryData) HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoFrame * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); END_INTERFACE } IDeckLinkVideoFrameVtbl; interface IDeckLinkVideoFrame { CONST_VTBL struct IDeckLinkVideoFrameVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrame_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrame_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrame_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrame_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoFrame_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoFrame_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoFrame_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoFrame_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoFrame_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoFrame_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrame_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkMutableVideoFrame_INTERFACE_DEFINED__ #define __IDeckLinkMutableVideoFrame_INTERFACE_DEFINED__ /* interface IDeckLinkMutableVideoFrame */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkMutableVideoFrame; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CF9EB134-0374-4C5B-95FA-1EC14819FF62") IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT STDMETHODCALLTYPE SetFlags( /* [in] */ BMDFrameFlags newFlags) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecode( /* [in] */ BMDTimecodeFormat format, /* [in] */ IDeckLinkTimecode *timecode) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecodeFromComponents( /* [in] */ BMDTimecodeFormat format, /* [in] */ unsigned char hours, /* [in] */ unsigned char minutes, /* [in] */ unsigned char seconds, /* [in] */ unsigned char frames, /* [in] */ BMDTimecodeFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE SetAncillaryData( /* [in] */ IDeckLinkVideoFrameAncillary *ancillary) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecodeUserBits( /* [in] */ BMDTimecodeFormat format, /* [in] */ BMDTimecodeUserBits userBits) = 0; virtual HRESULT STDMETHODCALLTYPE SetInterfaceProvider( /* [in] */ REFIID iid, /* [in] */ IUnknown *iface) = 0; }; #else /* C style interface */ typedef struct IDeckLinkMutableVideoFrameVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkMutableVideoFrame * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkMutableVideoFrame * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkMutableVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetWidth) long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkMutableVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetHeight) long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkMutableVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetRowBytes) long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkMutableVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkMutableVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetFlags) BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkMutableVideoFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetAncillaryData) HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkMutableVideoFrame * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame, SetFlags) HRESULT ( STDMETHODCALLTYPE *SetFlags )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDFrameFlags newFlags); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame, SetTimecode) HRESULT ( STDMETHODCALLTYPE *SetTimecode )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ IDeckLinkTimecode *timecode); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame, SetTimecodeFromComponents) HRESULT ( STDMETHODCALLTYPE *SetTimecodeFromComponents )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ unsigned char hours, /* [in] */ unsigned char minutes, /* [in] */ unsigned char seconds, /* [in] */ unsigned char frames, /* [in] */ BMDTimecodeFlags flags); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame, SetAncillaryData) HRESULT ( STDMETHODCALLTYPE *SetAncillaryData )( IDeckLinkMutableVideoFrame * This, /* [in] */ IDeckLinkVideoFrameAncillary *ancillary); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame, SetTimecodeUserBits) HRESULT ( STDMETHODCALLTYPE *SetTimecodeUserBits )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ BMDTimecodeUserBits userBits); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame, SetInterfaceProvider) HRESULT ( STDMETHODCALLTYPE *SetInterfaceProvider )( IDeckLinkMutableVideoFrame * This, /* [in] */ REFIID iid, /* [in] */ IUnknown *iface); END_INTERFACE } IDeckLinkMutableVideoFrameVtbl; interface IDeckLinkMutableVideoFrame { CONST_VTBL struct IDeckLinkMutableVideoFrameVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkMutableVideoFrame_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkMutableVideoFrame_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkMutableVideoFrame_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkMutableVideoFrame_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkMutableVideoFrame_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkMutableVideoFrame_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkMutableVideoFrame_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkMutableVideoFrame_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkMutableVideoFrame_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkMutableVideoFrame_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkMutableVideoFrame_SetFlags(This,newFlags) \ ( (This)->lpVtbl -> SetFlags(This,newFlags) ) #define IDeckLinkMutableVideoFrame_SetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> SetTimecode(This,format,timecode) ) #define IDeckLinkMutableVideoFrame_SetTimecodeFromComponents(This,format,hours,minutes,seconds,frames,flags) \ ( (This)->lpVtbl -> SetTimecodeFromComponents(This,format,hours,minutes,seconds,frames,flags) ) #define IDeckLinkMutableVideoFrame_SetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> SetAncillaryData(This,ancillary) ) #define IDeckLinkMutableVideoFrame_SetTimecodeUserBits(This,format,userBits) \ ( (This)->lpVtbl -> SetTimecodeUserBits(This,format,userBits) ) #define IDeckLinkMutableVideoFrame_SetInterfaceProvider(This,iid,iface) \ ( (This)->lpVtbl -> SetInterfaceProvider(This,iid,iface) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkMutableVideoFrame_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrame3DExtensions_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrame3DExtensions_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrame3DExtensions */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrame3DExtensions; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("D4DBE9C6-B4D2-49D3-ABF2-B4E86C7391B0") IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat STDMETHODCALLTYPE Get3DPackingFormat( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameForRightEye( /* [out] */ IDeckLinkVideoFrame **rightEyeFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrame3DExtensionsVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrame3DExtensions * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrame3DExtensions * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrame3DExtensions * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame3DExtensions, Get3DPackingFormat) BMDVideo3DPackingFormat ( STDMETHODCALLTYPE *Get3DPackingFormat )( IDeckLinkVideoFrame3DExtensions * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame3DExtensions, GetFrameForRightEye) HRESULT ( STDMETHODCALLTYPE *GetFrameForRightEye )( IDeckLinkVideoFrame3DExtensions * This, /* [out] */ IDeckLinkVideoFrame **rightEyeFrame); END_INTERFACE } IDeckLinkVideoFrame3DExtensionsVtbl; interface IDeckLinkVideoFrame3DExtensions { CONST_VTBL struct IDeckLinkVideoFrame3DExtensionsVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrame3DExtensions_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrame3DExtensions_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrame3DExtensions_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrame3DExtensions_Get3DPackingFormat(This) \ ( (This)->lpVtbl -> Get3DPackingFormat(This) ) #define IDeckLinkVideoFrame3DExtensions_GetFrameForRightEye(This,rightEyeFrame) \ ( (This)->lpVtbl -> GetFrameForRightEye(This,rightEyeFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrame3DExtensions_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrameMetadataExtensions_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrameMetadataExtensions_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrameMetadataExtensions */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrameMetadataExtensions; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("E232A5B7-4DB4-44C9-9152-F47C12E5F051") IDeckLinkVideoFrameMetadataExtensions : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ void *buffer, /* [out][in] */ unsigned int *bufferSize) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrameMetadataExtensionsVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrameMetadataExtensions * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrameMetadataExtensions * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrameMetadataExtensions * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkVideoFrameMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkVideoFrameMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkVideoFrameMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkVideoFrameMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoFrameMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ void *buffer, /* [out][in] */ unsigned int *bufferSize); END_INTERFACE } IDeckLinkVideoFrameMetadataExtensionsVtbl; interface IDeckLinkVideoFrameMetadataExtensions { CONST_VTBL struct IDeckLinkVideoFrameMetadataExtensionsVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrameMetadataExtensions_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrameMetadataExtensions_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrameMetadataExtensions_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrameMetadataExtensions_GetInt(This,metadataID,value) \ ( (This)->lpVtbl -> GetInt(This,metadataID,value) ) #define IDeckLinkVideoFrameMetadataExtensions_GetFloat(This,metadataID,value) \ ( (This)->lpVtbl -> GetFloat(This,metadataID,value) ) #define IDeckLinkVideoFrameMetadataExtensions_GetFlag(This,metadataID,value) \ ( (This)->lpVtbl -> GetFlag(This,metadataID,value) ) #define IDeckLinkVideoFrameMetadataExtensions_GetString(This,metadataID,value) \ ( (This)->lpVtbl -> GetString(This,metadataID,value) ) #define IDeckLinkVideoFrameMetadataExtensions_GetBytes(This,metadataID,buffer,bufferSize) \ ( (This)->lpVtbl -> GetBytes(This,metadataID,buffer,bufferSize) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrameMetadataExtensions_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrameMutableMetadataExtensions_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrameMutableMetadataExtensions_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrameMutableMetadataExtensions */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrameMutableMetadataExtensions; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CC198FC6-8298-4419-942D-8357EC355E58") IDeckLinkVideoFrameMutableMetadataExtensions : public IDeckLinkVideoFrameMetadataExtensions { public: virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE SetBytes( /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ void *buffer, /* [in] */ unsigned int bufferSize) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrameMutableMetadataExtensionsVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrameMutableMetadataExtensions * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrameMutableMetadataExtensions * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [out] */ void *buffer, /* [out][in] */ unsigned int *bufferSize); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMutableMetadataExtensions, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMutableMetadataExtensions, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMutableMetadataExtensions, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMutableMetadataExtensions, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ BSTR value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMutableMetadataExtensions, SetBytes) HRESULT ( STDMETHODCALLTYPE *SetBytes )( IDeckLinkVideoFrameMutableMetadataExtensions * This, /* [in] */ BMDDeckLinkFrameMetadataID metadataID, /* [in] */ void *buffer, /* [in] */ unsigned int bufferSize); END_INTERFACE } IDeckLinkVideoFrameMutableMetadataExtensionsVtbl; interface IDeckLinkVideoFrameMutableMetadataExtensions { CONST_VTBL struct IDeckLinkVideoFrameMutableMetadataExtensionsVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrameMutableMetadataExtensions_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_GetInt(This,metadataID,value) \ ( (This)->lpVtbl -> GetInt(This,metadataID,value) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_GetFloat(This,metadataID,value) \ ( (This)->lpVtbl -> GetFloat(This,metadataID,value) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_GetFlag(This,metadataID,value) \ ( (This)->lpVtbl -> GetFlag(This,metadataID,value) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_GetString(This,metadataID,value) \ ( (This)->lpVtbl -> GetString(This,metadataID,value) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_GetBytes(This,metadataID,buffer,bufferSize) \ ( (This)->lpVtbl -> GetBytes(This,metadataID,buffer,bufferSize) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_SetInt(This,metadataID,value) \ ( (This)->lpVtbl -> SetInt(This,metadataID,value) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_SetFloat(This,metadataID,value) \ ( (This)->lpVtbl -> SetFloat(This,metadataID,value) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_SetFlag(This,metadataID,value) \ ( (This)->lpVtbl -> SetFlag(This,metadataID,value) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_SetString(This,metadataID,value) \ ( (This)->lpVtbl -> SetString(This,metadataID,value) ) #define IDeckLinkVideoFrameMutableMetadataExtensions_SetBytes(This,metadataID,buffer,bufferSize) \ ( (This)->lpVtbl -> SetBytes(This,metadataID,buffer,bufferSize) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrameMutableMetadataExtensions_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_INTERFACE_DEFINED__ #define __IDeckLinkVideoInputFrame_INTERFACE_DEFINED__ /* interface IDeckLinkVideoInputFrame */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoInputFrame; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C9ADD3D2-BE52-488D-AB2D-7FDEF7AF0C95") IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT STDMETHODCALLTYPE GetStreamTime( /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceTimestamp( /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoInputFrameVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoInputFrame * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoInputFrame * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoInputFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetWidth) long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoInputFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetHeight) long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoInputFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetRowBytes) long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoInputFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoInputFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetFlags) BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoInputFrame * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoInputFrame * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame, GetAncillaryData) HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoInputFrame * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); DECLSPEC_XFGVIRT(IDeckLinkVideoInputFrame, GetStreamTime) HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkVideoInputFrame * This, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkVideoInputFrame, GetHardwareReferenceTimestamp) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceTimestamp )( IDeckLinkVideoInputFrame * This, /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration); END_INTERFACE } IDeckLinkVideoInputFrameVtbl; interface IDeckLinkVideoInputFrame { CONST_VTBL struct IDeckLinkVideoInputFrameVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoInputFrame_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoInputFrame_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoInputFrame_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoInputFrame_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoInputFrame_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoInputFrame_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoInputFrame_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoInputFrame_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoInputFrame_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoInputFrame_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkVideoInputFrame_GetStreamTime(This,frameTime,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,frameDuration,timeScale) ) #define IDeckLinkVideoInputFrame_GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) \ ( (This)->lpVtbl -> GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoInputFrame_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAncillaryPacket_INTERFACE_DEFINED__ #define __IDeckLinkAncillaryPacket_INTERFACE_DEFINED__ /* interface IDeckLinkAncillaryPacket */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAncillaryPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CC5BBF7E-029C-4D3B-9158-6000EF5E3670") IDeckLinkAncillaryPacket : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [in] */ BMDAncillaryPacketFormat format, /* [out] */ const void **data, /* [out] */ unsigned int *size) = 0; virtual unsigned char STDMETHODCALLTYPE GetDID( void) = 0; virtual unsigned char STDMETHODCALLTYPE GetSDID( void) = 0; virtual unsigned int STDMETHODCALLTYPE GetLineNumber( void) = 0; virtual unsigned char STDMETHODCALLTYPE GetDataStreamIndex( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAncillaryPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAncillaryPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAncillaryPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAncillaryPacket * This); DECLSPEC_XFGVIRT(IDeckLinkAncillaryPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkAncillaryPacket * This, /* [in] */ BMDAncillaryPacketFormat format, /* [out] */ const void **data, /* [out] */ unsigned int *size); DECLSPEC_XFGVIRT(IDeckLinkAncillaryPacket, GetDID) unsigned char ( STDMETHODCALLTYPE *GetDID )( IDeckLinkAncillaryPacket * This); DECLSPEC_XFGVIRT(IDeckLinkAncillaryPacket, GetSDID) unsigned char ( STDMETHODCALLTYPE *GetSDID )( IDeckLinkAncillaryPacket * This); DECLSPEC_XFGVIRT(IDeckLinkAncillaryPacket, GetLineNumber) unsigned int ( STDMETHODCALLTYPE *GetLineNumber )( IDeckLinkAncillaryPacket * This); DECLSPEC_XFGVIRT(IDeckLinkAncillaryPacket, GetDataStreamIndex) unsigned char ( STDMETHODCALLTYPE *GetDataStreamIndex )( IDeckLinkAncillaryPacket * This); END_INTERFACE } IDeckLinkAncillaryPacketVtbl; interface IDeckLinkAncillaryPacket { CONST_VTBL struct IDeckLinkAncillaryPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAncillaryPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAncillaryPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAncillaryPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAncillaryPacket_GetBytes(This,format,data,size) \ ( (This)->lpVtbl -> GetBytes(This,format,data,size) ) #define IDeckLinkAncillaryPacket_GetDID(This) \ ( (This)->lpVtbl -> GetDID(This) ) #define IDeckLinkAncillaryPacket_GetSDID(This) \ ( (This)->lpVtbl -> GetSDID(This) ) #define IDeckLinkAncillaryPacket_GetLineNumber(This) \ ( (This)->lpVtbl -> GetLineNumber(This) ) #define IDeckLinkAncillaryPacket_GetDataStreamIndex(This) \ ( (This)->lpVtbl -> GetDataStreamIndex(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAncillaryPacket_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAncillaryPacketIterator_INTERFACE_DEFINED__ #define __IDeckLinkAncillaryPacketIterator_INTERFACE_DEFINED__ /* interface IDeckLinkAncillaryPacketIterator */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAncillaryPacketIterator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3FC8994B-88FB-4C17-968F-9AAB69D964A7") IDeckLinkAncillaryPacketIterator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLinkAncillaryPacket **packet) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAncillaryPacketIteratorVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAncillaryPacketIterator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAncillaryPacketIterator * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAncillaryPacketIterator * This); DECLSPEC_XFGVIRT(IDeckLinkAncillaryPacketIterator, Next) HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkAncillaryPacketIterator * This, /* [out] */ IDeckLinkAncillaryPacket **packet); END_INTERFACE } IDeckLinkAncillaryPacketIteratorVtbl; interface IDeckLinkAncillaryPacketIterator { CONST_VTBL struct IDeckLinkAncillaryPacketIteratorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAncillaryPacketIterator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAncillaryPacketIterator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAncillaryPacketIterator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAncillaryPacketIterator_Next(This,packet) \ ( (This)->lpVtbl -> Next(This,packet) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAncillaryPacketIterator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrameAncillaryPackets_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrameAncillaryPackets_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrameAncillaryPackets */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrameAncillaryPackets; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("6C186C0F-459E-41D8-AEE2-4812D81AEE68") IDeckLinkVideoFrameAncillaryPackets : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetPacketIterator( /* [out] */ IDeckLinkAncillaryPacketIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE GetFirstPacketByID( /* [in] */ unsigned char DID, /* [in] */ unsigned char SDID, /* [out] */ IDeckLinkAncillaryPacket **packet) = 0; virtual HRESULT STDMETHODCALLTYPE AttachPacket( /* [in] */ IDeckLinkAncillaryPacket *packet) = 0; virtual HRESULT STDMETHODCALLTYPE DetachPacket( /* [in] */ IDeckLinkAncillaryPacket *packet) = 0; virtual HRESULT STDMETHODCALLTYPE DetachAllPackets( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrameAncillaryPacketsVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrameAncillaryPackets * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrameAncillaryPackets * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrameAncillaryPackets * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameAncillaryPackets, GetPacketIterator) HRESULT ( STDMETHODCALLTYPE *GetPacketIterator )( IDeckLinkVideoFrameAncillaryPackets * This, /* [out] */ IDeckLinkAncillaryPacketIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameAncillaryPackets, GetFirstPacketByID) HRESULT ( STDMETHODCALLTYPE *GetFirstPacketByID )( IDeckLinkVideoFrameAncillaryPackets * This, /* [in] */ unsigned char DID, /* [in] */ unsigned char SDID, /* [out] */ IDeckLinkAncillaryPacket **packet); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameAncillaryPackets, AttachPacket) HRESULT ( STDMETHODCALLTYPE *AttachPacket )( IDeckLinkVideoFrameAncillaryPackets * This, /* [in] */ IDeckLinkAncillaryPacket *packet); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameAncillaryPackets, DetachPacket) HRESULT ( STDMETHODCALLTYPE *DetachPacket )( IDeckLinkVideoFrameAncillaryPackets * This, /* [in] */ IDeckLinkAncillaryPacket *packet); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameAncillaryPackets, DetachAllPackets) HRESULT ( STDMETHODCALLTYPE *DetachAllPackets )( IDeckLinkVideoFrameAncillaryPackets * This); END_INTERFACE } IDeckLinkVideoFrameAncillaryPacketsVtbl; interface IDeckLinkVideoFrameAncillaryPackets { CONST_VTBL struct IDeckLinkVideoFrameAncillaryPacketsVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrameAncillaryPackets_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrameAncillaryPackets_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrameAncillaryPackets_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrameAncillaryPackets_GetPacketIterator(This,iterator) \ ( (This)->lpVtbl -> GetPacketIterator(This,iterator) ) #define IDeckLinkVideoFrameAncillaryPackets_GetFirstPacketByID(This,DID,SDID,packet) \ ( (This)->lpVtbl -> GetFirstPacketByID(This,DID,SDID,packet) ) #define IDeckLinkVideoFrameAncillaryPackets_AttachPacket(This,packet) \ ( (This)->lpVtbl -> AttachPacket(This,packet) ) #define IDeckLinkVideoFrameAncillaryPackets_DetachPacket(This,packet) \ ( (This)->lpVtbl -> DetachPacket(This,packet) ) #define IDeckLinkVideoFrameAncillaryPackets_DetachAllPackets(This) \ ( (This)->lpVtbl -> DetachAllPackets(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrameAncillaryPackets_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrameAncillary_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrameAncillary_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrameAncillary */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrameAncillary; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("732E723C-D1A4-4E29-9E8E-4A88797A0004") IDeckLinkVideoFrameAncillary : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetBufferForVerticalBlankingLine( /* [in] */ unsigned int lineNumber, /* [out] */ void **buffer) = 0; virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat( void) = 0; virtual BMDDisplayMode STDMETHODCALLTYPE GetDisplayMode( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrameAncillaryVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrameAncillary * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrameAncillary * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrameAncillary * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameAncillary, GetBufferForVerticalBlankingLine) HRESULT ( STDMETHODCALLTYPE *GetBufferForVerticalBlankingLine )( IDeckLinkVideoFrameAncillary * This, /* [in] */ unsigned int lineNumber, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameAncillary, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoFrameAncillary * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameAncillary, GetDisplayMode) BMDDisplayMode ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkVideoFrameAncillary * This); END_INTERFACE } IDeckLinkVideoFrameAncillaryVtbl; interface IDeckLinkVideoFrameAncillary { CONST_VTBL struct IDeckLinkVideoFrameAncillaryVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrameAncillary_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrameAncillary_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrameAncillary_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrameAncillary_GetBufferForVerticalBlankingLine(This,lineNumber,buffer) \ ( (This)->lpVtbl -> GetBufferForVerticalBlankingLine(This,lineNumber,buffer) ) #define IDeckLinkVideoFrameAncillary_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoFrameAncillary_GetDisplayMode(This) \ ( (This)->lpVtbl -> GetDisplayMode(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrameAncillary_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkEncoderPacket_INTERFACE_DEFINED__ #define __IDeckLinkEncoderPacket_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderPacket */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("B693F36C-316E-4AF1-B6C2-F389A4BCA620") IDeckLinkEncoderPacket : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual long STDMETHODCALLTYPE GetSize( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetStreamTime( /* [out] */ BMDTimeValue *frameTime, /* [in] */ BMDTimeScale timeScale) = 0; virtual BMDPacketType STDMETHODCALLTYPE GetPacketType( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkEncoderPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetSize) long ( STDMETHODCALLTYPE *GetSize )( IDeckLinkEncoderPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetStreamTime) HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkEncoderPacket * This, /* [out] */ BMDTimeValue *frameTime, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetPacketType) BMDPacketType ( STDMETHODCALLTYPE *GetPacketType )( IDeckLinkEncoderPacket * This); END_INTERFACE } IDeckLinkEncoderPacketVtbl; interface IDeckLinkEncoderPacket { CONST_VTBL struct IDeckLinkEncoderPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkEncoderPacket_GetSize(This) \ ( (This)->lpVtbl -> GetSize(This) ) #define IDeckLinkEncoderPacket_GetStreamTime(This,frameTime,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,timeScale) ) #define IDeckLinkEncoderPacket_GetPacketType(This) \ ( (This)->lpVtbl -> GetPacketType(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderPacket_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkEncoderVideoPacket_INTERFACE_DEFINED__ #define __IDeckLinkEncoderVideoPacket_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderVideoPacket */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderVideoPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0") IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket { public: virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceTimestamp( /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecode( /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderVideoPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderVideoPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderVideoPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderVideoPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkEncoderVideoPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetSize) long ( STDMETHODCALLTYPE *GetSize )( IDeckLinkEncoderVideoPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetStreamTime) HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkEncoderVideoPacket * This, /* [out] */ BMDTimeValue *frameTime, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetPacketType) BMDPacketType ( STDMETHODCALLTYPE *GetPacketType )( IDeckLinkEncoderVideoPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderVideoPacket, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkEncoderVideoPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderVideoPacket, GetHardwareReferenceTimestamp) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceTimestamp )( IDeckLinkEncoderVideoPacket * This, /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration); DECLSPEC_XFGVIRT(IDeckLinkEncoderVideoPacket, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkEncoderVideoPacket * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); END_INTERFACE } IDeckLinkEncoderVideoPacketVtbl; interface IDeckLinkEncoderVideoPacket { CONST_VTBL struct IDeckLinkEncoderVideoPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderVideoPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderVideoPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderVideoPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderVideoPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkEncoderVideoPacket_GetSize(This) \ ( (This)->lpVtbl -> GetSize(This) ) #define IDeckLinkEncoderVideoPacket_GetStreamTime(This,frameTime,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,timeScale) ) #define IDeckLinkEncoderVideoPacket_GetPacketType(This) \ ( (This)->lpVtbl -> GetPacketType(This) ) #define IDeckLinkEncoderVideoPacket_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkEncoderVideoPacket_GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) \ ( (This)->lpVtbl -> GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) ) #define IDeckLinkEncoderVideoPacket_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderVideoPacket_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkEncoderAudioPacket_INTERFACE_DEFINED__ #define __IDeckLinkEncoderAudioPacket_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderAudioPacket */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderAudioPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("49E8EDC8-693B-4E14-8EF6-12C658F5A07A") IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket { public: virtual BMDAudioFormat STDMETHODCALLTYPE GetAudioFormat( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderAudioPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderAudioPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderAudioPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderAudioPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkEncoderAudioPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetSize) long ( STDMETHODCALLTYPE *GetSize )( IDeckLinkEncoderAudioPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetStreamTime) HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkEncoderAudioPacket * This, /* [out] */ BMDTimeValue *frameTime, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetPacketType) BMDPacketType ( STDMETHODCALLTYPE *GetPacketType )( IDeckLinkEncoderAudioPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderAudioPacket, GetAudioFormat) BMDAudioFormat ( STDMETHODCALLTYPE *GetAudioFormat )( IDeckLinkEncoderAudioPacket * This); END_INTERFACE } IDeckLinkEncoderAudioPacketVtbl; interface IDeckLinkEncoderAudioPacket { CONST_VTBL struct IDeckLinkEncoderAudioPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderAudioPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderAudioPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderAudioPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderAudioPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkEncoderAudioPacket_GetSize(This) \ ( (This)->lpVtbl -> GetSize(This) ) #define IDeckLinkEncoderAudioPacket_GetStreamTime(This,frameTime,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,timeScale) ) #define IDeckLinkEncoderAudioPacket_GetPacketType(This) \ ( (This)->lpVtbl -> GetPacketType(This) ) #define IDeckLinkEncoderAudioPacket_GetAudioFormat(This) \ ( (This)->lpVtbl -> GetAudioFormat(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderAudioPacket_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkH265NALPacket_INTERFACE_DEFINED__ #define __IDeckLinkH265NALPacket_INTERFACE_DEFINED__ /* interface IDeckLinkH265NALPacket */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkH265NALPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("639C8E0B-68D5-4BDE-A6D4-95F3AEAFF2E7") IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket { public: virtual HRESULT STDMETHODCALLTYPE GetUnitType( /* [out] */ unsigned char *unitType) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytesNoPrefix( /* [out] */ void **buffer) = 0; virtual long STDMETHODCALLTYPE GetSizeNoPrefix( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkH265NALPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkH265NALPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkH265NALPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkH265NALPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkH265NALPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetSize) long ( STDMETHODCALLTYPE *GetSize )( IDeckLinkH265NALPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetStreamTime) HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkH265NALPacket * This, /* [out] */ BMDTimeValue *frameTime, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkEncoderPacket, GetPacketType) BMDPacketType ( STDMETHODCALLTYPE *GetPacketType )( IDeckLinkH265NALPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderVideoPacket, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkH265NALPacket * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderVideoPacket, GetHardwareReferenceTimestamp) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceTimestamp )( IDeckLinkH265NALPacket * This, /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration); DECLSPEC_XFGVIRT(IDeckLinkEncoderVideoPacket, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkH265NALPacket * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); DECLSPEC_XFGVIRT(IDeckLinkH265NALPacket, GetUnitType) HRESULT ( STDMETHODCALLTYPE *GetUnitType )( IDeckLinkH265NALPacket * This, /* [out] */ unsigned char *unitType); DECLSPEC_XFGVIRT(IDeckLinkH265NALPacket, GetBytesNoPrefix) HRESULT ( STDMETHODCALLTYPE *GetBytesNoPrefix )( IDeckLinkH265NALPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkH265NALPacket, GetSizeNoPrefix) long ( STDMETHODCALLTYPE *GetSizeNoPrefix )( IDeckLinkH265NALPacket * This); END_INTERFACE } IDeckLinkH265NALPacketVtbl; interface IDeckLinkH265NALPacket { CONST_VTBL struct IDeckLinkH265NALPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkH265NALPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkH265NALPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkH265NALPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkH265NALPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkH265NALPacket_GetSize(This) \ ( (This)->lpVtbl -> GetSize(This) ) #define IDeckLinkH265NALPacket_GetStreamTime(This,frameTime,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,timeScale) ) #define IDeckLinkH265NALPacket_GetPacketType(This) \ ( (This)->lpVtbl -> GetPacketType(This) ) #define IDeckLinkH265NALPacket_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkH265NALPacket_GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) \ ( (This)->lpVtbl -> GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) ) #define IDeckLinkH265NALPacket_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkH265NALPacket_GetUnitType(This,unitType) \ ( (This)->lpVtbl -> GetUnitType(This,unitType) ) #define IDeckLinkH265NALPacket_GetBytesNoPrefix(This,buffer) \ ( (This)->lpVtbl -> GetBytesNoPrefix(This,buffer) ) #define IDeckLinkH265NALPacket_GetSizeNoPrefix(This) \ ( (This)->lpVtbl -> GetSizeNoPrefix(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkH265NALPacket_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAudioInputPacket_INTERFACE_DEFINED__ #define __IDeckLinkAudioInputPacket_INTERFACE_DEFINED__ /* interface IDeckLinkAudioInputPacket */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAudioInputPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("E43D5870-2894-11DE-8C30-0800200C9A66") IDeckLinkAudioInputPacket : public IUnknown { public: virtual long STDMETHODCALLTYPE GetSampleFrameCount( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetPacketTime( /* [out] */ BMDTimeValue *packetTime, /* [in] */ BMDTimeScale timeScale) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAudioInputPacketVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAudioInputPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAudioInputPacket * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAudioInputPacket * This); DECLSPEC_XFGVIRT(IDeckLinkAudioInputPacket, GetSampleFrameCount) long ( STDMETHODCALLTYPE *GetSampleFrameCount )( IDeckLinkAudioInputPacket * This); DECLSPEC_XFGVIRT(IDeckLinkAudioInputPacket, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkAudioInputPacket * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkAudioInputPacket, GetPacketTime) HRESULT ( STDMETHODCALLTYPE *GetPacketTime )( IDeckLinkAudioInputPacket * This, /* [out] */ BMDTimeValue *packetTime, /* [in] */ BMDTimeScale timeScale); END_INTERFACE } IDeckLinkAudioInputPacketVtbl; interface IDeckLinkAudioInputPacket { CONST_VTBL struct IDeckLinkAudioInputPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAudioInputPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAudioInputPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAudioInputPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAudioInputPacket_GetSampleFrameCount(This) \ ( (This)->lpVtbl -> GetSampleFrameCount(This) ) #define IDeckLinkAudioInputPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkAudioInputPacket_GetPacketTime(This,packetTime,timeScale) \ ( (This)->lpVtbl -> GetPacketTime(This,packetTime,timeScale) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAudioInputPacket_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkScreenPreviewCallback_INTERFACE_DEFINED__ #define __IDeckLinkScreenPreviewCallback_INTERFACE_DEFINED__ /* interface IDeckLinkScreenPreviewCallback */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkScreenPreviewCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("D4FA2345-9FBA-4497-95C3-C0C3CED3CDA8") IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DrawFrame( /* [in] */ IDeckLinkVideoFrame *theFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkScreenPreviewCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkScreenPreviewCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkScreenPreviewCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkScreenPreviewCallback * This); DECLSPEC_XFGVIRT(IDeckLinkScreenPreviewCallback, DrawFrame) HRESULT ( STDMETHODCALLTYPE *DrawFrame )( IDeckLinkScreenPreviewCallback * This, /* [in] */ IDeckLinkVideoFrame *theFrame); END_INTERFACE } IDeckLinkScreenPreviewCallbackVtbl; interface IDeckLinkScreenPreviewCallback { CONST_VTBL struct IDeckLinkScreenPreviewCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkScreenPreviewCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkScreenPreviewCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkScreenPreviewCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkScreenPreviewCallback_DrawFrame(This,theFrame) \ ( (This)->lpVtbl -> DrawFrame(This,theFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkScreenPreviewCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkGLScreenPreviewHelper_INTERFACE_DEFINED__ #define __IDeckLinkGLScreenPreviewHelper_INTERFACE_DEFINED__ /* interface IDeckLinkGLScreenPreviewHelper */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkGLScreenPreviewHelper; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CEB778E2-C202-4EC8-9085-0CD285CC5522") IDeckLinkGLScreenPreviewHelper : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE InitializeGL( void) = 0; virtual HRESULT STDMETHODCALLTYPE PaintGL( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetFrame( /* [in] */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE Set3DPreviewFormat( /* [in] */ BMD3DPreviewFormat previewFormat) = 0; }; #else /* C style interface */ typedef struct IDeckLinkGLScreenPreviewHelperVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkGLScreenPreviewHelper * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkGLScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkGLScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IDeckLinkGLScreenPreviewHelper, InitializeGL) HRESULT ( STDMETHODCALLTYPE *InitializeGL )( IDeckLinkGLScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IDeckLinkGLScreenPreviewHelper, PaintGL) HRESULT ( STDMETHODCALLTYPE *PaintGL )( IDeckLinkGLScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IDeckLinkGLScreenPreviewHelper, SetFrame) HRESULT ( STDMETHODCALLTYPE *SetFrame )( IDeckLinkGLScreenPreviewHelper * This, /* [in] */ IDeckLinkVideoFrame *theFrame); DECLSPEC_XFGVIRT(IDeckLinkGLScreenPreviewHelper, Set3DPreviewFormat) HRESULT ( STDMETHODCALLTYPE *Set3DPreviewFormat )( IDeckLinkGLScreenPreviewHelper * This, /* [in] */ BMD3DPreviewFormat previewFormat); END_INTERFACE } IDeckLinkGLScreenPreviewHelperVtbl; interface IDeckLinkGLScreenPreviewHelper { CONST_VTBL struct IDeckLinkGLScreenPreviewHelperVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkGLScreenPreviewHelper_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkGLScreenPreviewHelper_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkGLScreenPreviewHelper_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkGLScreenPreviewHelper_InitializeGL(This) \ ( (This)->lpVtbl -> InitializeGL(This) ) #define IDeckLinkGLScreenPreviewHelper_PaintGL(This) \ ( (This)->lpVtbl -> PaintGL(This) ) #define IDeckLinkGLScreenPreviewHelper_SetFrame(This,theFrame) \ ( (This)->lpVtbl -> SetFrame(This,theFrame) ) #define IDeckLinkGLScreenPreviewHelper_Set3DPreviewFormat(This,previewFormat) \ ( (This)->lpVtbl -> Set3DPreviewFormat(This,previewFormat) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkGLScreenPreviewHelper_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDX9ScreenPreviewHelper_INTERFACE_DEFINED__ #define __IDeckLinkDX9ScreenPreviewHelper_INTERFACE_DEFINED__ /* interface IDeckLinkDX9ScreenPreviewHelper */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDX9ScreenPreviewHelper; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("F2DD78CA-2921-4AC2-B5BC-BFDCC2035A1F") IDeckLinkDX9ScreenPreviewHelper : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Initialize( /* [in] */ void *device) = 0; virtual HRESULT STDMETHODCALLTYPE Render( /* [in] */ RECT *rc) = 0; virtual HRESULT STDMETHODCALLTYPE SetFrame( /* [in] */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE Set3DPreviewFormat( /* [in] */ BMD3DPreviewFormat previewFormat) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDX9ScreenPreviewHelperVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDX9ScreenPreviewHelper * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDX9ScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDX9ScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IDeckLinkDX9ScreenPreviewHelper, Initialize) HRESULT ( STDMETHODCALLTYPE *Initialize )( IDeckLinkDX9ScreenPreviewHelper * This, /* [in] */ void *device); DECLSPEC_XFGVIRT(IDeckLinkDX9ScreenPreviewHelper, Render) HRESULT ( STDMETHODCALLTYPE *Render )( IDeckLinkDX9ScreenPreviewHelper * This, /* [in] */ RECT *rc); DECLSPEC_XFGVIRT(IDeckLinkDX9ScreenPreviewHelper, SetFrame) HRESULT ( STDMETHODCALLTYPE *SetFrame )( IDeckLinkDX9ScreenPreviewHelper * This, /* [in] */ IDeckLinkVideoFrame *theFrame); DECLSPEC_XFGVIRT(IDeckLinkDX9ScreenPreviewHelper, Set3DPreviewFormat) HRESULT ( STDMETHODCALLTYPE *Set3DPreviewFormat )( IDeckLinkDX9ScreenPreviewHelper * This, /* [in] */ BMD3DPreviewFormat previewFormat); END_INTERFACE } IDeckLinkDX9ScreenPreviewHelperVtbl; interface IDeckLinkDX9ScreenPreviewHelper { CONST_VTBL struct IDeckLinkDX9ScreenPreviewHelperVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDX9ScreenPreviewHelper_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDX9ScreenPreviewHelper_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDX9ScreenPreviewHelper_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDX9ScreenPreviewHelper_Initialize(This,device) \ ( (This)->lpVtbl -> Initialize(This,device) ) #define IDeckLinkDX9ScreenPreviewHelper_Render(This,rc) \ ( (This)->lpVtbl -> Render(This,rc) ) #define IDeckLinkDX9ScreenPreviewHelper_SetFrame(This,theFrame) \ ( (This)->lpVtbl -> SetFrame(This,theFrame) ) #define IDeckLinkDX9ScreenPreviewHelper_Set3DPreviewFormat(This,previewFormat) \ ( (This)->lpVtbl -> Set3DPreviewFormat(This,previewFormat) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDX9ScreenPreviewHelper_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkWPFDX9ScreenPreviewHelper_INTERFACE_DEFINED__ #define __IDeckLinkWPFDX9ScreenPreviewHelper_INTERFACE_DEFINED__ /* interface IDeckLinkWPFDX9ScreenPreviewHelper */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkWPFDX9ScreenPreviewHelper; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C59346CD-9326-4266-AC2D-5C190F5799EE") IDeckLinkWPFDX9ScreenPreviewHelper : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Initialize( void) = 0; virtual HRESULT STDMETHODCALLTYPE Render( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetSurfaceSize( /* [in] */ unsigned int width, /* [in] */ unsigned int height) = 0; virtual HRESULT STDMETHODCALLTYPE SetFrame( /* [in] */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE Set3DPreviewFormat( /* [in] */ BMD3DPreviewFormat previewFormat) = 0; virtual HRESULT STDMETHODCALLTYPE GetBackBuffer( /* [out] */ void **backBuffer) = 0; }; #else /* C style interface */ typedef struct IDeckLinkWPFDX9ScreenPreviewHelperVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkWPFDX9ScreenPreviewHelper * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkWPFDX9ScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkWPFDX9ScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper, Initialize) HRESULT ( STDMETHODCALLTYPE *Initialize )( IDeckLinkWPFDX9ScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper, Render) HRESULT ( STDMETHODCALLTYPE *Render )( IDeckLinkWPFDX9ScreenPreviewHelper * This); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper, SetSurfaceSize) HRESULT ( STDMETHODCALLTYPE *SetSurfaceSize )( IDeckLinkWPFDX9ScreenPreviewHelper * This, /* [in] */ unsigned int width, /* [in] */ unsigned int height); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper, SetFrame) HRESULT ( STDMETHODCALLTYPE *SetFrame )( IDeckLinkWPFDX9ScreenPreviewHelper * This, /* [in] */ IDeckLinkVideoFrame *theFrame); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper, Set3DPreviewFormat) HRESULT ( STDMETHODCALLTYPE *Set3DPreviewFormat )( IDeckLinkWPFDX9ScreenPreviewHelper * This, /* [in] */ BMD3DPreviewFormat previewFormat); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper, GetBackBuffer) HRESULT ( STDMETHODCALLTYPE *GetBackBuffer )( IDeckLinkWPFDX9ScreenPreviewHelper * This, /* [out] */ void **backBuffer); END_INTERFACE } IDeckLinkWPFDX9ScreenPreviewHelperVtbl; interface IDeckLinkWPFDX9ScreenPreviewHelper { CONST_VTBL struct IDeckLinkWPFDX9ScreenPreviewHelperVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkWPFDX9ScreenPreviewHelper_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_Initialize(This) \ ( (This)->lpVtbl -> Initialize(This) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_Render(This) \ ( (This)->lpVtbl -> Render(This) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_SetSurfaceSize(This,width,height) \ ( (This)->lpVtbl -> SetSurfaceSize(This,width,height) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_SetFrame(This,theFrame) \ ( (This)->lpVtbl -> SetFrame(This,theFrame) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_Set3DPreviewFormat(This,previewFormat) \ ( (This)->lpVtbl -> Set3DPreviewFormat(This,previewFormat) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_GetBackBuffer(This,backBuffer) \ ( (This)->lpVtbl -> GetBackBuffer(This,backBuffer) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkWPFDX9ScreenPreviewHelper_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkNotificationCallback_INTERFACE_DEFINED__ #define __IDeckLinkNotificationCallback_INTERFACE_DEFINED__ /* interface IDeckLinkNotificationCallback */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkNotificationCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("b002a1ec-070d-4288-8289-bd5d36e5ff0d") IDeckLinkNotificationCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Notify( /* [in] */ BMDNotifications topic, /* [in] */ ULONGLONG param1, /* [in] */ ULONGLONG param2) = 0; }; #else /* C style interface */ typedef struct IDeckLinkNotificationCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkNotificationCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkNotificationCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkNotificationCallback * This); DECLSPEC_XFGVIRT(IDeckLinkNotificationCallback, Notify) HRESULT ( STDMETHODCALLTYPE *Notify )( IDeckLinkNotificationCallback * This, /* [in] */ BMDNotifications topic, /* [in] */ ULONGLONG param1, /* [in] */ ULONGLONG param2); END_INTERFACE } IDeckLinkNotificationCallbackVtbl; interface IDeckLinkNotificationCallback { CONST_VTBL struct IDeckLinkNotificationCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkNotificationCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkNotificationCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkNotificationCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkNotificationCallback_Notify(This,topic,param1,param2) \ ( (This)->lpVtbl -> Notify(This,topic,param1,param2) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkNotificationCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkNotification_INTERFACE_DEFINED__ #define __IDeckLinkNotification_INTERFACE_DEFINED__ /* interface IDeckLinkNotification */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkNotification; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("b85df4c8-bdf5-47c1-8064-28162ebdd4eb") IDeckLinkNotification : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Subscribe( /* [in] */ BMDNotifications topic, /* [in] */ IDeckLinkNotificationCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE Unsubscribe( /* [in] */ BMDNotifications topic, /* [in] */ IDeckLinkNotificationCallback *theCallback) = 0; }; #else /* C style interface */ typedef struct IDeckLinkNotificationVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkNotification * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkNotification * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkNotification * This); DECLSPEC_XFGVIRT(IDeckLinkNotification, Subscribe) HRESULT ( STDMETHODCALLTYPE *Subscribe )( IDeckLinkNotification * This, /* [in] */ BMDNotifications topic, /* [in] */ IDeckLinkNotificationCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkNotification, Unsubscribe) HRESULT ( STDMETHODCALLTYPE *Unsubscribe )( IDeckLinkNotification * This, /* [in] */ BMDNotifications topic, /* [in] */ IDeckLinkNotificationCallback *theCallback); END_INTERFACE } IDeckLinkNotificationVtbl; interface IDeckLinkNotification { CONST_VTBL struct IDeckLinkNotificationVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkNotification_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkNotification_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkNotification_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkNotification_Subscribe(This,topic,theCallback) \ ( (This)->lpVtbl -> Subscribe(This,topic,theCallback) ) #define IDeckLinkNotification_Unsubscribe(This,topic,theCallback) \ ( (This)->lpVtbl -> Unsubscribe(This,topic,theCallback) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkNotification_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkProfileAttributes_INTERFACE_DEFINED__ #define __IDeckLinkProfileAttributes_INTERFACE_DEFINED__ /* interface IDeckLinkProfileAttributes */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkProfileAttributes; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("17D4BF8E-4911-473A-80A0-731CF6FF345B") IDeckLinkProfileAttributes : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BSTR *value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkProfileAttributesVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkProfileAttributes * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkProfileAttributes * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkProfileAttributes * This); DECLSPEC_XFGVIRT(IDeckLinkProfileAttributes, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkProfileAttributes * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkProfileAttributes, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkProfileAttributes * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkProfileAttributes, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkProfileAttributes * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkProfileAttributes, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkProfileAttributes * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BSTR *value); END_INTERFACE } IDeckLinkProfileAttributesVtbl; interface IDeckLinkProfileAttributes { CONST_VTBL struct IDeckLinkProfileAttributesVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkProfileAttributes_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkProfileAttributes_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkProfileAttributes_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkProfileAttributes_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkProfileAttributes_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkProfileAttributes_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkProfileAttributes_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkProfileAttributes_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkProfileIterator_INTERFACE_DEFINED__ #define __IDeckLinkProfileIterator_INTERFACE_DEFINED__ /* interface IDeckLinkProfileIterator */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkProfileIterator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("29E5A8C0-8BE4-46EB-93AC-31DAAB5B7BF2") IDeckLinkProfileIterator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLinkProfile **profile) = 0; }; #else /* C style interface */ typedef struct IDeckLinkProfileIteratorVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkProfileIterator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkProfileIterator * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkProfileIterator * This); DECLSPEC_XFGVIRT(IDeckLinkProfileIterator, Next) HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkProfileIterator * This, /* [out] */ IDeckLinkProfile **profile); END_INTERFACE } IDeckLinkProfileIteratorVtbl; interface IDeckLinkProfileIterator { CONST_VTBL struct IDeckLinkProfileIteratorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkProfileIterator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkProfileIterator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkProfileIterator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkProfileIterator_Next(This,profile) \ ( (This)->lpVtbl -> Next(This,profile) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkProfileIterator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkProfile_INTERFACE_DEFINED__ #define __IDeckLinkProfile_INTERFACE_DEFINED__ /* interface IDeckLinkProfile */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkProfile; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("16093466-674A-432B-9DA0-1AC2C5A8241C") IDeckLinkProfile : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetDevice( /* [out] */ IDeckLink **device) = 0; virtual HRESULT STDMETHODCALLTYPE IsActive( /* [out] */ BOOL *isActive) = 0; virtual HRESULT STDMETHODCALLTYPE SetActive( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetPeers( /* [out] */ IDeckLinkProfileIterator **profileIterator) = 0; }; #else /* C style interface */ typedef struct IDeckLinkProfileVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkProfile * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkProfile * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkProfile * This); DECLSPEC_XFGVIRT(IDeckLinkProfile, GetDevice) HRESULT ( STDMETHODCALLTYPE *GetDevice )( IDeckLinkProfile * This, /* [out] */ IDeckLink **device); DECLSPEC_XFGVIRT(IDeckLinkProfile, IsActive) HRESULT ( STDMETHODCALLTYPE *IsActive )( IDeckLinkProfile * This, /* [out] */ BOOL *isActive); DECLSPEC_XFGVIRT(IDeckLinkProfile, SetActive) HRESULT ( STDMETHODCALLTYPE *SetActive )( IDeckLinkProfile * This); DECLSPEC_XFGVIRT(IDeckLinkProfile, GetPeers) HRESULT ( STDMETHODCALLTYPE *GetPeers )( IDeckLinkProfile * This, /* [out] */ IDeckLinkProfileIterator **profileIterator); END_INTERFACE } IDeckLinkProfileVtbl; interface IDeckLinkProfile { CONST_VTBL struct IDeckLinkProfileVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkProfile_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkProfile_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkProfile_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkProfile_GetDevice(This,device) \ ( (This)->lpVtbl -> GetDevice(This,device) ) #define IDeckLinkProfile_IsActive(This,isActive) \ ( (This)->lpVtbl -> IsActive(This,isActive) ) #define IDeckLinkProfile_SetActive(This) \ ( (This)->lpVtbl -> SetActive(This) ) #define IDeckLinkProfile_GetPeers(This,profileIterator) \ ( (This)->lpVtbl -> GetPeers(This,profileIterator) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkProfile_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkProfileCallback_INTERFACE_DEFINED__ #define __IDeckLinkProfileCallback_INTERFACE_DEFINED__ /* interface IDeckLinkProfileCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkProfileCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("A4F9341E-97AA-4E04-8935-15F809898CEA") IDeckLinkProfileCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ProfileChanging( /* [in] */ IDeckLinkProfile *profileToBeActivated, /* [in] */ BOOL streamsWillBeForcedToStop) = 0; virtual HRESULT STDMETHODCALLTYPE ProfileActivated( /* [in] */ IDeckLinkProfile *activatedProfile) = 0; }; #else /* C style interface */ typedef struct IDeckLinkProfileCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkProfileCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkProfileCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkProfileCallback * This); DECLSPEC_XFGVIRT(IDeckLinkProfileCallback, ProfileChanging) HRESULT ( STDMETHODCALLTYPE *ProfileChanging )( IDeckLinkProfileCallback * This, /* [in] */ IDeckLinkProfile *profileToBeActivated, /* [in] */ BOOL streamsWillBeForcedToStop); DECLSPEC_XFGVIRT(IDeckLinkProfileCallback, ProfileActivated) HRESULT ( STDMETHODCALLTYPE *ProfileActivated )( IDeckLinkProfileCallback * This, /* [in] */ IDeckLinkProfile *activatedProfile); END_INTERFACE } IDeckLinkProfileCallbackVtbl; interface IDeckLinkProfileCallback { CONST_VTBL struct IDeckLinkProfileCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkProfileCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkProfileCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkProfileCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkProfileCallback_ProfileChanging(This,profileToBeActivated,streamsWillBeForcedToStop) \ ( (This)->lpVtbl -> ProfileChanging(This,profileToBeActivated,streamsWillBeForcedToStop) ) #define IDeckLinkProfileCallback_ProfileActivated(This,activatedProfile) \ ( (This)->lpVtbl -> ProfileActivated(This,activatedProfile) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkProfileCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkProfileManager_INTERFACE_DEFINED__ #define __IDeckLinkProfileManager_INTERFACE_DEFINED__ /* interface IDeckLinkProfileManager */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkProfileManager; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("30D41429-3998-4B6D-84F8-78C94A797C6E") IDeckLinkProfileManager : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetProfiles( /* [out] */ IDeckLinkProfileIterator **profileIterator) = 0; virtual HRESULT STDMETHODCALLTYPE GetProfile( /* [in] */ BMDProfileID profileID, /* [out] */ IDeckLinkProfile **profile) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkProfileCallback *callback) = 0; }; #else /* C style interface */ typedef struct IDeckLinkProfileManagerVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkProfileManager * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkProfileManager * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkProfileManager * This); DECLSPEC_XFGVIRT(IDeckLinkProfileManager, GetProfiles) HRESULT ( STDMETHODCALLTYPE *GetProfiles )( IDeckLinkProfileManager * This, /* [out] */ IDeckLinkProfileIterator **profileIterator); DECLSPEC_XFGVIRT(IDeckLinkProfileManager, GetProfile) HRESULT ( STDMETHODCALLTYPE *GetProfile )( IDeckLinkProfileManager * This, /* [in] */ BMDProfileID profileID, /* [out] */ IDeckLinkProfile **profile); DECLSPEC_XFGVIRT(IDeckLinkProfileManager, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkProfileManager * This, /* [in] */ IDeckLinkProfileCallback *callback); END_INTERFACE } IDeckLinkProfileManagerVtbl; interface IDeckLinkProfileManager { CONST_VTBL struct IDeckLinkProfileManagerVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkProfileManager_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkProfileManager_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkProfileManager_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkProfileManager_GetProfiles(This,profileIterator) \ ( (This)->lpVtbl -> GetProfiles(This,profileIterator) ) #define IDeckLinkProfileManager_GetProfile(This,profileID,profile) \ ( (This)->lpVtbl -> GetProfile(This,profileID,profile) ) #define IDeckLinkProfileManager_SetCallback(This,callback) \ ( (This)->lpVtbl -> SetCallback(This,callback) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkProfileManager_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkStatus_INTERFACE_DEFINED__ #define __IDeckLinkStatus_INTERFACE_DEFINED__ /* interface IDeckLinkStatus */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkStatus; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("5F558200-4028-49BC-BEAC-DB3FA4A96E46") IDeckLinkStatus : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ void *buffer, /* [out][in] */ unsigned int *bufferSize) = 0; }; #else /* C style interface */ typedef struct IDeckLinkStatusVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkStatus * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkStatus * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkStatus * This); DECLSPEC_XFGVIRT(IDeckLinkStatus, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkStatus * This, /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkStatus, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkStatus * This, /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkStatus, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkStatus * This, /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkStatus, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkStatus * This, /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkStatus, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkStatus * This, /* [in] */ BMDDeckLinkStatusID statusID, /* [out] */ void *buffer, /* [out][in] */ unsigned int *bufferSize); END_INTERFACE } IDeckLinkStatusVtbl; interface IDeckLinkStatus { CONST_VTBL struct IDeckLinkStatusVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkStatus_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkStatus_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkStatus_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkStatus_GetFlag(This,statusID,value) \ ( (This)->lpVtbl -> GetFlag(This,statusID,value) ) #define IDeckLinkStatus_GetInt(This,statusID,value) \ ( (This)->lpVtbl -> GetInt(This,statusID,value) ) #define IDeckLinkStatus_GetFloat(This,statusID,value) \ ( (This)->lpVtbl -> GetFloat(This,statusID,value) ) #define IDeckLinkStatus_GetString(This,statusID,value) \ ( (This)->lpVtbl -> GetString(This,statusID,value) ) #define IDeckLinkStatus_GetBytes(This,statusID,buffer,bufferSize) \ ( (This)->lpVtbl -> GetBytes(This,statusID,buffer,bufferSize) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkStatus_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkKeyer_INTERFACE_DEFINED__ #define __IDeckLinkKeyer_INTERFACE_DEFINED__ /* interface IDeckLinkKeyer */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkKeyer; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("89AFCAF5-65F8-421E-98F7-96FE5F5BFBA3") IDeckLinkKeyer : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Enable( /* [in] */ BOOL isExternal) = 0; virtual HRESULT STDMETHODCALLTYPE SetLevel( /* [in] */ unsigned char level) = 0; virtual HRESULT STDMETHODCALLTYPE RampUp( /* [in] */ unsigned int numberOfFrames) = 0; virtual HRESULT STDMETHODCALLTYPE RampDown( /* [in] */ unsigned int numberOfFrames) = 0; virtual HRESULT STDMETHODCALLTYPE Disable( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkKeyerVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkKeyer * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkKeyer * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkKeyer * This); DECLSPEC_XFGVIRT(IDeckLinkKeyer, Enable) HRESULT ( STDMETHODCALLTYPE *Enable )( IDeckLinkKeyer * This, /* [in] */ BOOL isExternal); DECLSPEC_XFGVIRT(IDeckLinkKeyer, SetLevel) HRESULT ( STDMETHODCALLTYPE *SetLevel )( IDeckLinkKeyer * This, /* [in] */ unsigned char level); DECLSPEC_XFGVIRT(IDeckLinkKeyer, RampUp) HRESULT ( STDMETHODCALLTYPE *RampUp )( IDeckLinkKeyer * This, /* [in] */ unsigned int numberOfFrames); DECLSPEC_XFGVIRT(IDeckLinkKeyer, RampDown) HRESULT ( STDMETHODCALLTYPE *RampDown )( IDeckLinkKeyer * This, /* [in] */ unsigned int numberOfFrames); DECLSPEC_XFGVIRT(IDeckLinkKeyer, Disable) HRESULT ( STDMETHODCALLTYPE *Disable )( IDeckLinkKeyer * This); END_INTERFACE } IDeckLinkKeyerVtbl; interface IDeckLinkKeyer { CONST_VTBL struct IDeckLinkKeyerVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkKeyer_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkKeyer_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkKeyer_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkKeyer_Enable(This,isExternal) \ ( (This)->lpVtbl -> Enable(This,isExternal) ) #define IDeckLinkKeyer_SetLevel(This,level) \ ( (This)->lpVtbl -> SetLevel(This,level) ) #define IDeckLinkKeyer_RampUp(This,numberOfFrames) \ ( (This)->lpVtbl -> RampUp(This,numberOfFrames) ) #define IDeckLinkKeyer_RampDown(This,numberOfFrames) \ ( (This)->lpVtbl -> RampDown(This,numberOfFrames) ) #define IDeckLinkKeyer_Disable(This) \ ( (This)->lpVtbl -> Disable(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkKeyer_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoConversion_INTERFACE_DEFINED__ #define __IDeckLinkVideoConversion_INTERFACE_DEFINED__ /* interface IDeckLinkVideoConversion */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoConversion; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("A48755D9-8BD5-4727-A1E9-069FDEDBA6E9") IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ConvertFrame( /* [in] */ IDeckLinkVideoFrame *srcFrame, /* [in] */ IDeckLinkVideoFrame *dstFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ConvertNewFrame( /* [in] */ IDeckLinkVideoFrame *srcFrame, /* [in] */ BMDPixelFormat dstPixelFormat, /* [in] */ BMDColorspace dstColorspace, /* [in] */ IDeckLinkVideoBuffer *dstBuffer, /* [out] */ IDeckLinkVideoFrame **dstFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoConversionVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoConversion * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoConversion * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoConversion * This); DECLSPEC_XFGVIRT(IDeckLinkVideoConversion, ConvertFrame) HRESULT ( STDMETHODCALLTYPE *ConvertFrame )( IDeckLinkVideoConversion * This, /* [in] */ IDeckLinkVideoFrame *srcFrame, /* [in] */ IDeckLinkVideoFrame *dstFrame); DECLSPEC_XFGVIRT(IDeckLinkVideoConversion, ConvertNewFrame) HRESULT ( STDMETHODCALLTYPE *ConvertNewFrame )( IDeckLinkVideoConversion * This, /* [in] */ IDeckLinkVideoFrame *srcFrame, /* [in] */ BMDPixelFormat dstPixelFormat, /* [in] */ BMDColorspace dstColorspace, /* [in] */ IDeckLinkVideoBuffer *dstBuffer, /* [out] */ IDeckLinkVideoFrame **dstFrame); END_INTERFACE } IDeckLinkVideoConversionVtbl; interface IDeckLinkVideoConversion { CONST_VTBL struct IDeckLinkVideoConversionVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoConversion_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoConversion_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoConversion_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoConversion_ConvertFrame(This,srcFrame,dstFrame) \ ( (This)->lpVtbl -> ConvertFrame(This,srcFrame,dstFrame) ) #define IDeckLinkVideoConversion_ConvertNewFrame(This,srcFrame,dstPixelFormat,dstColorspace,dstBuffer,dstFrame) \ ( (This)->lpVtbl -> ConvertNewFrame(This,srcFrame,dstPixelFormat,dstColorspace,dstBuffer,dstFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoConversion_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDeviceNotificationCallback_INTERFACE_DEFINED__ #define __IDeckLinkDeviceNotificationCallback_INTERFACE_DEFINED__ /* interface IDeckLinkDeviceNotificationCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDeviceNotificationCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("4997053B-0ADF-4CC8-AC70-7A50C4BE728F") IDeckLinkDeviceNotificationCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DeckLinkDeviceArrived( /* [in] */ IDeckLink *deckLinkDevice) = 0; virtual HRESULT STDMETHODCALLTYPE DeckLinkDeviceRemoved( /* [in] */ IDeckLink *deckLinkDevice) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDeviceNotificationCallbackVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDeviceNotificationCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDeviceNotificationCallback * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDeviceNotificationCallback * This); DECLSPEC_XFGVIRT(IDeckLinkDeviceNotificationCallback, DeckLinkDeviceArrived) HRESULT ( STDMETHODCALLTYPE *DeckLinkDeviceArrived )( IDeckLinkDeviceNotificationCallback * This, /* [in] */ IDeckLink *deckLinkDevice); DECLSPEC_XFGVIRT(IDeckLinkDeviceNotificationCallback, DeckLinkDeviceRemoved) HRESULT ( STDMETHODCALLTYPE *DeckLinkDeviceRemoved )( IDeckLinkDeviceNotificationCallback * This, /* [in] */ IDeckLink *deckLinkDevice); END_INTERFACE } IDeckLinkDeviceNotificationCallbackVtbl; interface IDeckLinkDeviceNotificationCallback { CONST_VTBL struct IDeckLinkDeviceNotificationCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDeviceNotificationCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDeviceNotificationCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDeviceNotificationCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDeviceNotificationCallback_DeckLinkDeviceArrived(This,deckLinkDevice) \ ( (This)->lpVtbl -> DeckLinkDeviceArrived(This,deckLinkDevice) ) #define IDeckLinkDeviceNotificationCallback_DeckLinkDeviceRemoved(This,deckLinkDevice) \ ( (This)->lpVtbl -> DeckLinkDeviceRemoved(This,deckLinkDevice) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDeviceNotificationCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDiscovery_INTERFACE_DEFINED__ #define __IDeckLinkDiscovery_INTERFACE_DEFINED__ /* interface IDeckLinkDiscovery */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDiscovery; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CDBF631C-BC76-45FA-B44D-C55059BC6101") IDeckLinkDiscovery : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE InstallDeviceNotifications( /* [in] */ IDeckLinkDeviceNotificationCallback *deviceNotificationCallback) = 0; virtual HRESULT STDMETHODCALLTYPE UninstallDeviceNotifications( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDiscoveryVtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDiscovery * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDiscovery * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDiscovery * This); DECLSPEC_XFGVIRT(IDeckLinkDiscovery, InstallDeviceNotifications) HRESULT ( STDMETHODCALLTYPE *InstallDeviceNotifications )( IDeckLinkDiscovery * This, /* [in] */ IDeckLinkDeviceNotificationCallback *deviceNotificationCallback); DECLSPEC_XFGVIRT(IDeckLinkDiscovery, UninstallDeviceNotifications) HRESULT ( STDMETHODCALLTYPE *UninstallDeviceNotifications )( IDeckLinkDiscovery * This); END_INTERFACE } IDeckLinkDiscoveryVtbl; interface IDeckLinkDiscovery { CONST_VTBL struct IDeckLinkDiscoveryVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDiscovery_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDiscovery_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDiscovery_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDiscovery_InstallDeviceNotifications(This,deviceNotificationCallback) \ ( (This)->lpVtbl -> InstallDeviceNotifications(This,deviceNotificationCallback) ) #define IDeckLinkDiscovery_UninstallDeviceNotifications(This) \ ( (This)->lpVtbl -> UninstallDeviceNotifications(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDiscovery_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_CDeckLinkIterator; #ifdef __cplusplus class DECLSPEC_UUID("BA6C6F44-6DA5-4DCE-94AA-EE2D1372A676") CDeckLinkIterator; #endif EXTERN_C const CLSID CLSID_CDeckLinkAPIInformation; #ifdef __cplusplus class DECLSPEC_UUID("263CA19F-ED09-482E-9F9D-84005783A237") CDeckLinkAPIInformation; #endif EXTERN_C const CLSID CLSID_CDeckLinkGLScreenPreviewHelper; #ifdef __cplusplus class DECLSPEC_UUID("1E332DAE-0D04-49EB-B8A1-B6E00B2B6BD0") CDeckLinkGLScreenPreviewHelper; #endif EXTERN_C const CLSID CLSID_CDeckLinkGL3ScreenPreviewHelper; #ifdef __cplusplus class DECLSPEC_UUID("166804E4-15EF-4BFD-B623-B5BA921667C5") CDeckLinkGL3ScreenPreviewHelper; #endif EXTERN_C const CLSID CLSID_CDeckLinkDX9ScreenPreviewHelper; #ifdef __cplusplus class DECLSPEC_UUID("0EB111ED-ADA6-43A6-8B16-CA5D27EEA15E") CDeckLinkDX9ScreenPreviewHelper; #endif EXTERN_C const CLSID CLSID_CDeckLinkWPFDX9ScreenPreviewHelper; #ifdef __cplusplus class DECLSPEC_UUID("5E64496D-4BB2-45D5-9B63-BF1B463B18AF") CDeckLinkWPFDX9ScreenPreviewHelper; #endif EXTERN_C const CLSID CLSID_CDeckLinkVideoConversion; #ifdef __cplusplus class DECLSPEC_UUID("89BA47BD-1FE2-4D76-9BFE-DE85049C4987") CDeckLinkVideoConversion; #endif EXTERN_C const CLSID CLSID_CDeckLinkDiscovery; #ifdef __cplusplus class DECLSPEC_UUID("22FBFC33-8D07-495C-A5BF-DAB5EA9B82DB") CDeckLinkDiscovery; #endif EXTERN_C const CLSID CLSID_CDeckLinkVideoFrameAncillaryPackets; #ifdef __cplusplus class DECLSPEC_UUID("F891AD29-D0C2-46E9-A926-4E2D0DD8CFAD") CDeckLinkVideoFrameAncillaryPackets; #endif #ifndef __IDeckLinkVideoOutputCallback_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkVideoOutputCallback_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkVideoOutputCallback_v14_2_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoOutputCallback_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("20AA5225-1958-47CB-820B-80A8D521A6EE") IDeckLinkVideoOutputCallback_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoOutputCallback_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoOutputCallback_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoOutputCallback_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoOutputCallback_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoOutputCallback_v14_2_1, ScheduledFrameCompleted) HRESULT ( STDMETHODCALLTYPE *ScheduledFrameCompleted )( IDeckLinkVideoOutputCallback_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result); DECLSPEC_XFGVIRT(IDeckLinkVideoOutputCallback_v14_2_1, ScheduledPlaybackHasStopped) HRESULT ( STDMETHODCALLTYPE *ScheduledPlaybackHasStopped )( IDeckLinkVideoOutputCallback_v14_2_1 * This); END_INTERFACE } IDeckLinkVideoOutputCallback_v14_2_1Vtbl; interface IDeckLinkVideoOutputCallback_v14_2_1 { CONST_VTBL struct IDeckLinkVideoOutputCallback_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoOutputCallback_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoOutputCallback_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoOutputCallback_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoOutputCallback_v14_2_1_ScheduledFrameCompleted(This,completedFrame,result) \ ( (This)->lpVtbl -> ScheduledFrameCompleted(This,completedFrame,result) ) #define IDeckLinkVideoOutputCallback_v14_2_1_ScheduledPlaybackHasStopped(This) \ ( (This)->lpVtbl -> ScheduledPlaybackHasStopped(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoOutputCallback_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInputCallback_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkInputCallback_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkInputCallback_v14_2_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInputCallback_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C6FCE4C9-C4E4-4047-82FB-5D238232A902") IDeckLinkInputCallback_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged( /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( /* [in] */ IDeckLinkVideoInputFrame_v14_2_1 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputCallback_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInputCallback_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInputCallback_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInputCallback_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInputCallback_v14_2_1, VideoInputFormatChanged) HRESULT ( STDMETHODCALLTYPE *VideoInputFormatChanged )( IDeckLinkInputCallback_v14_2_1 * This, /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags); DECLSPEC_XFGVIRT(IDeckLinkInputCallback_v14_2_1, VideoInputFrameArrived) HRESULT ( STDMETHODCALLTYPE *VideoInputFrameArrived )( IDeckLinkInputCallback_v14_2_1 * This, /* [in] */ IDeckLinkVideoInputFrame_v14_2_1 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket); END_INTERFACE } IDeckLinkInputCallback_v14_2_1Vtbl; interface IDeckLinkInputCallback_v14_2_1 { CONST_VTBL struct IDeckLinkInputCallback_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInputCallback_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInputCallback_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInputCallback_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInputCallback_v14_2_1_VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) \ ( (This)->lpVtbl -> VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) ) #define IDeckLinkInputCallback_v14_2_1_VideoInputFrameArrived(This,videoFrame,audioPacket) \ ( (This)->lpVtbl -> VideoInputFrameArrived(This,videoFrame,audioPacket) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInputCallback_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkMemoryAllocator_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkMemoryAllocator_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkMemoryAllocator_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkMemoryAllocator_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("B36EB6E7-9D29-4AA8-92EF-843B87A289E8") IDeckLinkMemoryAllocator_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE AllocateBuffer( /* [in] */ unsigned int bufferSize, /* [out] */ void **allocatedBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer( /* [in] */ void *buffer) = 0; virtual HRESULT STDMETHODCALLTYPE Commit( void) = 0; virtual HRESULT STDMETHODCALLTYPE Decommit( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkMemoryAllocator_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkMemoryAllocator_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkMemoryAllocator_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkMemoryAllocator_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkMemoryAllocator_v14_2_1, AllocateBuffer) HRESULT ( STDMETHODCALLTYPE *AllocateBuffer )( IDeckLinkMemoryAllocator_v14_2_1 * This, /* [in] */ unsigned int bufferSize, /* [out] */ void **allocatedBuffer); DECLSPEC_XFGVIRT(IDeckLinkMemoryAllocator_v14_2_1, ReleaseBuffer) HRESULT ( STDMETHODCALLTYPE *ReleaseBuffer )( IDeckLinkMemoryAllocator_v14_2_1 * This, /* [in] */ void *buffer); DECLSPEC_XFGVIRT(IDeckLinkMemoryAllocator_v14_2_1, Commit) HRESULT ( STDMETHODCALLTYPE *Commit )( IDeckLinkMemoryAllocator_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkMemoryAllocator_v14_2_1, Decommit) HRESULT ( STDMETHODCALLTYPE *Decommit )( IDeckLinkMemoryAllocator_v14_2_1 * This); END_INTERFACE } IDeckLinkMemoryAllocator_v14_2_1Vtbl; interface IDeckLinkMemoryAllocator_v14_2_1 { CONST_VTBL struct IDeckLinkMemoryAllocator_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkMemoryAllocator_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkMemoryAllocator_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkMemoryAllocator_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkMemoryAllocator_v14_2_1_AllocateBuffer(This,bufferSize,allocatedBuffer) \ ( (This)->lpVtbl -> AllocateBuffer(This,bufferSize,allocatedBuffer) ) #define IDeckLinkMemoryAllocator_v14_2_1_ReleaseBuffer(This,buffer) \ ( (This)->lpVtbl -> ReleaseBuffer(This,buffer) ) #define IDeckLinkMemoryAllocator_v14_2_1_Commit(This) \ ( (This)->lpVtbl -> Commit(This) ) #define IDeckLinkMemoryAllocator_v14_2_1_Decommit(This) \ ( (This)->lpVtbl -> Decommit(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkMemoryAllocator_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkOutput_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkOutput_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkOutput_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkOutput_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("BE2D9020-461E-442F-84B7-E949CB953B9D") IDeckLinkOutput_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoOutputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame( /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v14_2_1 **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateAncillaryData( /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback( /* [in] */ IDeckLinkVideoOutputCallback_v14_2_1 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedVideoFrameCount( /* [out] */ unsigned int *bufferedFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount, /* [in] */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync( /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [out] */ unsigned int *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples( /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned int *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned int *bufferedSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioCallback( /* [in] */ IDeckLinkAudioOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback( /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback( /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE IsScheduledPlaybackRunning( /* [out] */ BOOL *active) = 0; virtual HRESULT STDMETHODCALLTYPE GetScheduledStreamTime( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE GetReferenceStatus( /* [out] */ BMDReferenceStatus *referenceStatus) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameCompletionReferenceTimestamp( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *frameCompletionTimestamp) = 0; }; #else /* C style interface */ typedef struct IDeckLinkOutput_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkOutput_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkOutput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoOutputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkOutput_v14_2_1 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, EnableVideoOutput) HRESULT ( STDMETHODCALLTYPE *EnableVideoOutput )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, DisableVideoOutput) HRESULT ( STDMETHODCALLTYPE *DisableVideoOutput )( IDeckLinkOutput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, SetVideoOutputFrameMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetVideoOutputFrameMemoryAllocator )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, CreateVideoFrame) HRESULT ( STDMETHODCALLTYPE *CreateVideoFrame )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v14_2_1 **outFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, CreateAncillaryData) HRESULT ( STDMETHODCALLTYPE *CreateAncillaryData )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, DisplayVideoFrameSync) HRESULT ( STDMETHODCALLTYPE *DisplayVideoFrameSync )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, ScheduleVideoFrame) HRESULT ( STDMETHODCALLTYPE *ScheduleVideoFrame )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, SetScheduledFrameCompletionCallback) HRESULT ( STDMETHODCALLTYPE *SetScheduledFrameCompletionCallback )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ IDeckLinkVideoOutputCallback_v14_2_1 *theCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, GetBufferedVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetBufferedVideoFrameCount )( IDeckLinkOutput_v14_2_1 * This, /* [out] */ unsigned int *bufferedFrameCount); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, EnableAudioOutput) HRESULT ( STDMETHODCALLTYPE *EnableAudioOutput )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount, /* [in] */ BMDAudioOutputStreamType streamType); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, DisableAudioOutput) HRESULT ( STDMETHODCALLTYPE *DisableAudioOutput )( IDeckLinkOutput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, WriteAudioSamplesSync) HRESULT ( STDMETHODCALLTYPE *WriteAudioSamplesSync )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [out] */ unsigned int *sampleFramesWritten); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, BeginAudioPreroll) HRESULT ( STDMETHODCALLTYPE *BeginAudioPreroll )( IDeckLinkOutput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, EndAudioPreroll) HRESULT ( STDMETHODCALLTYPE *EndAudioPreroll )( IDeckLinkOutput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, ScheduleAudioSamples) HRESULT ( STDMETHODCALLTYPE *ScheduleAudioSamples )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned int *sampleFramesWritten); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, GetBufferedAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkOutput_v14_2_1 * This, /* [out] */ unsigned int *bufferedSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, FlushBufferedAudioSamples) HRESULT ( STDMETHODCALLTYPE *FlushBufferedAudioSamples )( IDeckLinkOutput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, SetAudioCallback) HRESULT ( STDMETHODCALLTYPE *SetAudioCallback )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ IDeckLinkAudioOutputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, StartScheduledPlayback) HRESULT ( STDMETHODCALLTYPE *StartScheduledPlayback )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, StopScheduledPlayback) HRESULT ( STDMETHODCALLTYPE *StopScheduledPlayback )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, IsScheduledPlaybackRunning) HRESULT ( STDMETHODCALLTYPE *IsScheduledPlaybackRunning )( IDeckLinkOutput_v14_2_1 * This, /* [out] */ BOOL *active); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, GetScheduledStreamTime) HRESULT ( STDMETHODCALLTYPE *GetScheduledStreamTime )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, GetReferenceStatus) HRESULT ( STDMETHODCALLTYPE *GetReferenceStatus )( IDeckLinkOutput_v14_2_1 * This, /* [out] */ BMDReferenceStatus *referenceStatus); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v14_2_1, GetFrameCompletionReferenceTimestamp) HRESULT ( STDMETHODCALLTYPE *GetFrameCompletionReferenceTimestamp )( IDeckLinkOutput_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *frameCompletionTimestamp); END_INTERFACE } IDeckLinkOutput_v14_2_1Vtbl; interface IDeckLinkOutput_v14_2_1 { CONST_VTBL struct IDeckLinkOutput_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkOutput_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkOutput_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkOutput_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkOutput_v14_2_1_DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) ) #define IDeckLinkOutput_v14_2_1_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkOutput_v14_2_1_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkOutput_v14_2_1_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkOutput_v14_2_1_EnableVideoOutput(This,displayMode,flags) \ ( (This)->lpVtbl -> EnableVideoOutput(This,displayMode,flags) ) #define IDeckLinkOutput_v14_2_1_DisableVideoOutput(This) \ ( (This)->lpVtbl -> DisableVideoOutput(This) ) #define IDeckLinkOutput_v14_2_1_SetVideoOutputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoOutputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkOutput_v14_2_1_CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_v14_2_1_CreateAncillaryData(This,pixelFormat,outBuffer) \ ( (This)->lpVtbl -> CreateAncillaryData(This,pixelFormat,outBuffer) ) #define IDeckLinkOutput_v14_2_1_DisplayVideoFrameSync(This,theFrame) \ ( (This)->lpVtbl -> DisplayVideoFrameSync(This,theFrame) ) #define IDeckLinkOutput_v14_2_1_ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) \ ( (This)->lpVtbl -> ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) ) #define IDeckLinkOutput_v14_2_1_SetScheduledFrameCompletionCallback(This,theCallback) \ ( (This)->lpVtbl -> SetScheduledFrameCompletionCallback(This,theCallback) ) #define IDeckLinkOutput_v14_2_1_GetBufferedVideoFrameCount(This,bufferedFrameCount) \ ( (This)->lpVtbl -> GetBufferedVideoFrameCount(This,bufferedFrameCount) ) #define IDeckLinkOutput_v14_2_1_EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) \ ( (This)->lpVtbl -> EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) ) #define IDeckLinkOutput_v14_2_1_DisableAudioOutput(This) \ ( (This)->lpVtbl -> DisableAudioOutput(This) ) #define IDeckLinkOutput_v14_2_1_WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) \ ( (This)->lpVtbl -> WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) ) #define IDeckLinkOutput_v14_2_1_BeginAudioPreroll(This) \ ( (This)->lpVtbl -> BeginAudioPreroll(This) ) #define IDeckLinkOutput_v14_2_1_EndAudioPreroll(This) \ ( (This)->lpVtbl -> EndAudioPreroll(This) ) #define IDeckLinkOutput_v14_2_1_ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) \ ( (This)->lpVtbl -> ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) ) #define IDeckLinkOutput_v14_2_1_GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) ) #define IDeckLinkOutput_v14_2_1_FlushBufferedAudioSamples(This) \ ( (This)->lpVtbl -> FlushBufferedAudioSamples(This) ) #define IDeckLinkOutput_v14_2_1_SetAudioCallback(This,theCallback) \ ( (This)->lpVtbl -> SetAudioCallback(This,theCallback) ) #define IDeckLinkOutput_v14_2_1_StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) \ ( (This)->lpVtbl -> StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) ) #define IDeckLinkOutput_v14_2_1_StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) \ ( (This)->lpVtbl -> StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) ) #define IDeckLinkOutput_v14_2_1_IsScheduledPlaybackRunning(This,active) \ ( (This)->lpVtbl -> IsScheduledPlaybackRunning(This,active) ) #define IDeckLinkOutput_v14_2_1_GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) \ ( (This)->lpVtbl -> GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) ) #define IDeckLinkOutput_v14_2_1_GetReferenceStatus(This,referenceStatus) \ ( (This)->lpVtbl -> GetReferenceStatus(This,referenceStatus) ) #define IDeckLinkOutput_v14_2_1_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #define IDeckLinkOutput_v14_2_1_GetFrameCompletionReferenceTimestamp(This,theFrame,desiredTimeScale,frameCompletionTimestamp) \ ( (This)->lpVtbl -> GetFrameCompletionReferenceTimestamp(This,theFrame,desiredTimeScale,frameCompletionTimestamp) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkOutput_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkInput_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkInput_v14_2_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C21CDB6E-F414-46E4-A636-80A566E0ED37") IDeckLinkInput_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoInputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoFrameCount( /* [out] */ unsigned int *availableFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoInputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned int *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback_v14_2_1 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInput_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput_v14_2_1 * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoInputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkInput_v14_2_1 * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput_v14_2_1 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkInput_v14_2_1 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, EnableVideoInput) HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput_v14_2_1 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, DisableVideoInput) HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, GetAvailableVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableVideoFrameCount )( IDeckLinkInput_v14_2_1 * This, /* [out] */ unsigned int *availableFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, SetVideoInputFrameMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetVideoInputFrameMemoryAllocator )( IDeckLinkInput_v14_2_1 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, EnableAudioInput) HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput_v14_2_1 * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, DisableAudioInput) HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, GetAvailableAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkInput_v14_2_1 * This, /* [out] */ unsigned int *availableSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, StartStreams) HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, StopStreams) HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, PauseStreams) HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, FlushStreams) HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput_v14_2_1 * This, /* [in] */ IDeckLinkInputCallback_v14_2_1 *theCallback); DECLSPEC_XFGVIRT(IDeckLinkInput_v14_2_1, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkInput_v14_2_1 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkInput_v14_2_1Vtbl; interface IDeckLinkInput_v14_2_1 { CONST_VTBL struct IDeckLinkInput_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_v14_2_1_DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) ) #define IDeckLinkInput_v14_2_1_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkInput_v14_2_1_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_v14_2_1_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkInput_v14_2_1_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_v14_2_1_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_v14_2_1_GetAvailableVideoFrameCount(This,availableFrameCount) \ ( (This)->lpVtbl -> GetAvailableVideoFrameCount(This,availableFrameCount) ) #define IDeckLinkInput_v14_2_1_SetVideoInputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoInputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkInput_v14_2_1_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_v14_2_1_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_v14_2_1_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkInput_v14_2_1_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_v14_2_1_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_v14_2_1_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_v14_2_1_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkInput_v14_2_1_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkInput_v14_2_1_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkEncoderInput_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkEncoderInput_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderInput_v14_2_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderInput_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("F222551D-13DF-4FD8-B587-9D4F19EC12C9") IDeckLinkEncoderInput_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedCodec, /* [in] */ unsigned int requestedCodecProfile, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailablePacketsCount( /* [out] */ unsigned int *availablePacketsCount) = 0; virtual HRESULT STDMETHODCALLTYPE SetMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioFormat audioFormat, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned int *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkEncoderInputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderInput_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderInput_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkEncoderInput_v14_2_1 * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedCodec, /* [in] */ unsigned int requestedCodecProfile, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkEncoderInput_v14_2_1 * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkEncoderInput_v14_2_1 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, EnableVideoInput) HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkEncoderInput_v14_2_1 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, DisableVideoInput) HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkEncoderInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, GetAvailablePacketsCount) HRESULT ( STDMETHODCALLTYPE *GetAvailablePacketsCount )( IDeckLinkEncoderInput_v14_2_1 * This, /* [out] */ unsigned int *availablePacketsCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, SetMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetMemoryAllocator )( IDeckLinkEncoderInput_v14_2_1 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, EnableAudioInput) HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkEncoderInput_v14_2_1 * This, /* [in] */ BMDAudioFormat audioFormat, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, DisableAudioInput) HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkEncoderInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, GetAvailableAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkEncoderInput_v14_2_1 * This, /* [out] */ unsigned int *availableSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, StartStreams) HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkEncoderInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, StopStreams) HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkEncoderInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, PauseStreams) HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkEncoderInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, FlushStreams) HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkEncoderInput_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkEncoderInput_v14_2_1 * This, /* [in] */ IDeckLinkEncoderInputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v14_2_1, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkEncoderInput_v14_2_1 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkEncoderInput_v14_2_1Vtbl; interface IDeckLinkEncoderInput_v14_2_1 { CONST_VTBL struct IDeckLinkEncoderInput_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderInput_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderInput_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderInput_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderInput_v14_2_1_DoesSupportVideoMode(This,connection,requestedMode,requestedCodec,requestedCodecProfile,flags,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedCodec,requestedCodecProfile,flags,supported) ) #define IDeckLinkEncoderInput_v14_2_1_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkEncoderInput_v14_2_1_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkEncoderInput_v14_2_1_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkEncoderInput_v14_2_1_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkEncoderInput_v14_2_1_GetAvailablePacketsCount(This,availablePacketsCount) \ ( (This)->lpVtbl -> GetAvailablePacketsCount(This,availablePacketsCount) ) #define IDeckLinkEncoderInput_v14_2_1_SetMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetMemoryAllocator(This,theAllocator) ) #define IDeckLinkEncoderInput_v14_2_1_EnableAudioInput(This,audioFormat,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,audioFormat,sampleRate,sampleType,channelCount) ) #define IDeckLinkEncoderInput_v14_2_1_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkEncoderInput_v14_2_1_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkEncoderInput_v14_2_1_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkEncoderInput_v14_2_1_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkEncoderInput_v14_2_1_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkEncoderInput_v14_2_1_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkEncoderInput_v14_2_1_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkEncoderInput_v14_2_1_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderInput_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrame_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrame_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrame_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3F716FE0-F023-4111-BE5D-EF4414C05B17") IDeckLinkVideoFrame_v14_2_1 : public IUnknown { public: virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual long STDMETHODCALLTYPE GetRowBytes( void) = 0; virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat( void) = 0; virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecode( /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode) = 0; virtual HRESULT STDMETHODCALLTYPE GetAncillaryData( /* [out] */ IDeckLinkVideoFrameAncillary **ancillary) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrame_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrame_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetWidth) long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetHeight) long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetRowBytes) long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetFlags) BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoFrame_v14_2_1 * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoFrame_v14_2_1 * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetAncillaryData) HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoFrame_v14_2_1 * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); END_INTERFACE } IDeckLinkVideoFrame_v14_2_1Vtbl; interface IDeckLinkVideoFrame_v14_2_1 { CONST_VTBL struct IDeckLinkVideoFrame_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrame_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrame_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrame_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrame_v14_2_1_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoFrame_v14_2_1_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoFrame_v14_2_1_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoFrame_v14_2_1_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoFrame_v14_2_1_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoFrame_v14_2_1_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoFrame_v14_2_1_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoFrame_v14_2_1_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrame_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkMutableVideoFrame_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkMutableVideoFrame_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkMutableVideoFrame_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkMutableVideoFrame_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("69E2639F-40DA-4E19-B6F2-20ACE815C390") IDeckLinkMutableVideoFrame_v14_2_1 : public IDeckLinkVideoFrame_v14_2_1 { public: virtual HRESULT STDMETHODCALLTYPE SetFlags( /* [in] */ BMDFrameFlags newFlags) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecode( /* [in] */ BMDTimecodeFormat format, /* [in] */ IDeckLinkTimecode *timecode) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecodeFromComponents( /* [in] */ BMDTimecodeFormat format, /* [in] */ unsigned char hours, /* [in] */ unsigned char minutes, /* [in] */ unsigned char seconds, /* [in] */ unsigned char frames, /* [in] */ BMDTimecodeFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE SetAncillaryData( /* [in] */ IDeckLinkVideoFrameAncillary *ancillary) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecodeUserBits( /* [in] */ BMDTimecodeFormat format, /* [in] */ BMDTimecodeUserBits userBits) = 0; }; #else /* C style interface */ typedef struct IDeckLinkMutableVideoFrame_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkMutableVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkMutableVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetWidth) long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkMutableVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetHeight) long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkMutableVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetRowBytes) long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkMutableVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkMutableVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetFlags) BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkMutableVideoFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetAncillaryData) HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame_v14_2_1, SetFlags) HRESULT ( STDMETHODCALLTYPE *SetFlags )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [in] */ BMDFrameFlags newFlags); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame_v14_2_1, SetTimecode) HRESULT ( STDMETHODCALLTYPE *SetTimecode )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ IDeckLinkTimecode *timecode); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame_v14_2_1, SetTimecodeFromComponents) HRESULT ( STDMETHODCALLTYPE *SetTimecodeFromComponents )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ unsigned char hours, /* [in] */ unsigned char minutes, /* [in] */ unsigned char seconds, /* [in] */ unsigned char frames, /* [in] */ BMDTimecodeFlags flags); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame_v14_2_1, SetAncillaryData) HRESULT ( STDMETHODCALLTYPE *SetAncillaryData )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrameAncillary *ancillary); DECLSPEC_XFGVIRT(IDeckLinkMutableVideoFrame_v14_2_1, SetTimecodeUserBits) HRESULT ( STDMETHODCALLTYPE *SetTimecodeUserBits )( IDeckLinkMutableVideoFrame_v14_2_1 * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ BMDTimecodeUserBits userBits); END_INTERFACE } IDeckLinkMutableVideoFrame_v14_2_1Vtbl; interface IDeckLinkMutableVideoFrame_v14_2_1 { CONST_VTBL struct IDeckLinkMutableVideoFrame_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkMutableVideoFrame_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkMutableVideoFrame_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkMutableVideoFrame_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkMutableVideoFrame_v14_2_1_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkMutableVideoFrame_v14_2_1_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkMutableVideoFrame_v14_2_1_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkMutableVideoFrame_v14_2_1_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkMutableVideoFrame_v14_2_1_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkMutableVideoFrame_v14_2_1_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkMutableVideoFrame_v14_2_1_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkMutableVideoFrame_v14_2_1_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkMutableVideoFrame_v14_2_1_SetFlags(This,newFlags) \ ( (This)->lpVtbl -> SetFlags(This,newFlags) ) #define IDeckLinkMutableVideoFrame_v14_2_1_SetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> SetTimecode(This,format,timecode) ) #define IDeckLinkMutableVideoFrame_v14_2_1_SetTimecodeFromComponents(This,format,hours,minutes,seconds,frames,flags) \ ( (This)->lpVtbl -> SetTimecodeFromComponents(This,format,hours,minutes,seconds,frames,flags) ) #define IDeckLinkMutableVideoFrame_v14_2_1_SetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> SetAncillaryData(This,ancillary) ) #define IDeckLinkMutableVideoFrame_v14_2_1_SetTimecodeUserBits(This,format,userBits) \ ( (This)->lpVtbl -> SetTimecodeUserBits(This,format,userBits) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkMutableVideoFrame_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrame3DExtensions_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrame3DExtensions_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrame3DExtensions_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrame3DExtensions_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7") IDeckLinkVideoFrame3DExtensions_v14_2_1 : public IUnknown { public: virtual BMDVideo3DPackingFormat STDMETHODCALLTYPE Get3DPackingFormat( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameForRightEye( /* [out] */ IDeckLinkVideoFrame_v14_2_1 **rightEyeFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrame3DExtensions_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrame3DExtensions_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrame3DExtensions_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrame3DExtensions_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame3DExtensions_v14_2_1, Get3DPackingFormat) BMDVideo3DPackingFormat ( STDMETHODCALLTYPE *Get3DPackingFormat )( IDeckLinkVideoFrame3DExtensions_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame3DExtensions_v14_2_1, GetFrameForRightEye) HRESULT ( STDMETHODCALLTYPE *GetFrameForRightEye )( IDeckLinkVideoFrame3DExtensions_v14_2_1 * This, /* [out] */ IDeckLinkVideoFrame_v14_2_1 **rightEyeFrame); END_INTERFACE } IDeckLinkVideoFrame3DExtensions_v14_2_1Vtbl; interface IDeckLinkVideoFrame3DExtensions_v14_2_1 { CONST_VTBL struct IDeckLinkVideoFrame3DExtensions_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrame3DExtensions_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrame3DExtensions_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrame3DExtensions_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrame3DExtensions_v14_2_1_Get3DPackingFormat(This) \ ( (This)->lpVtbl -> Get3DPackingFormat(This) ) #define IDeckLinkVideoFrame3DExtensions_v14_2_1_GetFrameForRightEye(This,rightEyeFrame) \ ( (This)->lpVtbl -> GetFrameForRightEye(This,rightEyeFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrame3DExtensions_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkVideoInputFrame_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkVideoInputFrame_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoInputFrame_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("05CFE374-537C-4094-9A57-680525118F44") IDeckLinkVideoInputFrame_v14_2_1 : public IDeckLinkVideoFrame_v14_2_1 { public: virtual HRESULT STDMETHODCALLTYPE GetStreamTime( /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceTimestamp( /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoInputFrame_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoInputFrame_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoInputFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoInputFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetWidth) long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoInputFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetHeight) long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoInputFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetRowBytes) long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoInputFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetPixelFormat) BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoInputFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetFlags) BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoInputFrame_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetBytes) HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoInputFrame_v14_2_1 * This, /* [out] */ void **buffer); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetTimecode) HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoInputFrame_v14_2_1 * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); DECLSPEC_XFGVIRT(IDeckLinkVideoFrame_v14_2_1, GetAncillaryData) HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoInputFrame_v14_2_1 * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); DECLSPEC_XFGVIRT(IDeckLinkVideoInputFrame_v14_2_1, GetStreamTime) HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkVideoInputFrame_v14_2_1 * This, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkVideoInputFrame_v14_2_1, GetHardwareReferenceTimestamp) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceTimestamp )( IDeckLinkVideoInputFrame_v14_2_1 * This, /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration); END_INTERFACE } IDeckLinkVideoInputFrame_v14_2_1Vtbl; interface IDeckLinkVideoInputFrame_v14_2_1 { CONST_VTBL struct IDeckLinkVideoInputFrame_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoInputFrame_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoInputFrame_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoInputFrame_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetStreamTime(This,frameTime,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,frameDuration,timeScale) ) #define IDeckLinkVideoInputFrame_v14_2_1_GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) \ ( (This)->lpVtbl -> GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoInputFrame_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkScreenPreviewCallback_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkScreenPreviewCallback_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkScreenPreviewCallback_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkScreenPreviewCallback_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("B1D3F49A-85FE-4C5D-95C8-0B5D5DCCD438") IDeckLinkScreenPreviewCallback_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DrawFrame( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkScreenPreviewCallback_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkScreenPreviewCallback_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkScreenPreviewCallback_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkScreenPreviewCallback_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkScreenPreviewCallback_v14_2_1, DrawFrame) HRESULT ( STDMETHODCALLTYPE *DrawFrame )( IDeckLinkScreenPreviewCallback_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame); END_INTERFACE } IDeckLinkScreenPreviewCallback_v14_2_1Vtbl; interface IDeckLinkScreenPreviewCallback_v14_2_1 { CONST_VTBL struct IDeckLinkScreenPreviewCallback_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkScreenPreviewCallback_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkScreenPreviewCallback_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkScreenPreviewCallback_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkScreenPreviewCallback_v14_2_1_DrawFrame(This,theFrame) \ ( (This)->lpVtbl -> DrawFrame(This,theFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkScreenPreviewCallback_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkGLScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkGLScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkGLScreenPreviewHelper_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkGLScreenPreviewHelper_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("504E2209-CAC7-4C1A-9FB4-C5BB6274D22F") IDeckLinkGLScreenPreviewHelper_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE InitializeGL( void) = 0; virtual HRESULT STDMETHODCALLTYPE PaintGL( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetFrame( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE Set3DPreviewFormat( /* [in] */ BMD3DPreviewFormat previewFormat) = 0; }; #else /* C style interface */ typedef struct IDeckLinkGLScreenPreviewHelper_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkGLScreenPreviewHelper_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkGLScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkGLScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkGLScreenPreviewHelper_v14_2_1, InitializeGL) HRESULT ( STDMETHODCALLTYPE *InitializeGL )( IDeckLinkGLScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkGLScreenPreviewHelper_v14_2_1, PaintGL) HRESULT ( STDMETHODCALLTYPE *PaintGL )( IDeckLinkGLScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkGLScreenPreviewHelper_v14_2_1, SetFrame) HRESULT ( STDMETHODCALLTYPE *SetFrame )( IDeckLinkGLScreenPreviewHelper_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame); DECLSPEC_XFGVIRT(IDeckLinkGLScreenPreviewHelper_v14_2_1, Set3DPreviewFormat) HRESULT ( STDMETHODCALLTYPE *Set3DPreviewFormat )( IDeckLinkGLScreenPreviewHelper_v14_2_1 * This, /* [in] */ BMD3DPreviewFormat previewFormat); END_INTERFACE } IDeckLinkGLScreenPreviewHelper_v14_2_1Vtbl; interface IDeckLinkGLScreenPreviewHelper_v14_2_1 { CONST_VTBL struct IDeckLinkGLScreenPreviewHelper_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkGLScreenPreviewHelper_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkGLScreenPreviewHelper_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkGLScreenPreviewHelper_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkGLScreenPreviewHelper_v14_2_1_InitializeGL(This) \ ( (This)->lpVtbl -> InitializeGL(This) ) #define IDeckLinkGLScreenPreviewHelper_v14_2_1_PaintGL(This) \ ( (This)->lpVtbl -> PaintGL(This) ) #define IDeckLinkGLScreenPreviewHelper_v14_2_1_SetFrame(This,theFrame) \ ( (This)->lpVtbl -> SetFrame(This,theFrame) ) #define IDeckLinkGLScreenPreviewHelper_v14_2_1_Set3DPreviewFormat(This,previewFormat) \ ( (This)->lpVtbl -> Set3DPreviewFormat(This,previewFormat) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkGLScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDX9ScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkDX9ScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkDX9ScreenPreviewHelper_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDX9ScreenPreviewHelper_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("2094B522-D1A1-40C0-9AC7-1C012218EF02") IDeckLinkDX9ScreenPreviewHelper_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Initialize( /* [in] */ void *device) = 0; virtual HRESULT STDMETHODCALLTYPE Render( /* [in] */ RECT *rc) = 0; virtual HRESULT STDMETHODCALLTYPE SetFrame( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE Set3DPreviewFormat( /* [in] */ BMD3DPreviewFormat previewFormat) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDX9ScreenPreviewHelper_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDX9ScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDX9ScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkDX9ScreenPreviewHelper_v14_2_1, Initialize) HRESULT ( STDMETHODCALLTYPE *Initialize )( IDeckLinkDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ void *device); DECLSPEC_XFGVIRT(IDeckLinkDX9ScreenPreviewHelper_v14_2_1, Render) HRESULT ( STDMETHODCALLTYPE *Render )( IDeckLinkDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ RECT *rc); DECLSPEC_XFGVIRT(IDeckLinkDX9ScreenPreviewHelper_v14_2_1, SetFrame) HRESULT ( STDMETHODCALLTYPE *SetFrame )( IDeckLinkDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame); DECLSPEC_XFGVIRT(IDeckLinkDX9ScreenPreviewHelper_v14_2_1, Set3DPreviewFormat) HRESULT ( STDMETHODCALLTYPE *Set3DPreviewFormat )( IDeckLinkDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ BMD3DPreviewFormat previewFormat); END_INTERFACE } IDeckLinkDX9ScreenPreviewHelper_v14_2_1Vtbl; interface IDeckLinkDX9ScreenPreviewHelper_v14_2_1 { CONST_VTBL struct IDeckLinkDX9ScreenPreviewHelper_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDX9ScreenPreviewHelper_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDX9ScreenPreviewHelper_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDX9ScreenPreviewHelper_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDX9ScreenPreviewHelper_v14_2_1_Initialize(This,device) \ ( (This)->lpVtbl -> Initialize(This,device) ) #define IDeckLinkDX9ScreenPreviewHelper_v14_2_1_Render(This,rc) \ ( (This)->lpVtbl -> Render(This,rc) ) #define IDeckLinkDX9ScreenPreviewHelper_v14_2_1_SetFrame(This,theFrame) \ ( (This)->lpVtbl -> SetFrame(This,theFrame) ) #define IDeckLinkDX9ScreenPreviewHelper_v14_2_1_Set3DPreviewFormat(This,previewFormat) \ ( (This)->lpVtbl -> Set3DPreviewFormat(This,previewFormat) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDX9ScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("AD8EC84A-7DDE-11E9-8F9E-2A86E4085A59") IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Initialize( void) = 0; virtual HRESULT STDMETHODCALLTYPE Render( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetSurfaceSize( /* [in] */ unsigned int width, /* [in] */ unsigned int height) = 0; virtual HRESULT STDMETHODCALLTYPE SetFrame( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE Set3DPreviewFormat( /* [in] */ BMD3DPreviewFormat previewFormat) = 0; virtual HRESULT STDMETHODCALLTYPE GetBackBuffer( /* [out] */ void **backBuffer) = 0; }; #else /* C style interface */ typedef struct IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1, Initialize) HRESULT ( STDMETHODCALLTYPE *Initialize )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1, Render) HRESULT ( STDMETHODCALLTYPE *Render )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1, SetSurfaceSize) HRESULT ( STDMETHODCALLTYPE *SetSurfaceSize )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ unsigned int width, /* [in] */ unsigned int height); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1, SetFrame) HRESULT ( STDMETHODCALLTYPE *SetFrame )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1, Set3DPreviewFormat) HRESULT ( STDMETHODCALLTYPE *Set3DPreviewFormat )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This, /* [in] */ BMD3DPreviewFormat previewFormat); DECLSPEC_XFGVIRT(IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1, GetBackBuffer) HRESULT ( STDMETHODCALLTYPE *GetBackBuffer )( IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 * This, /* [out] */ void **backBuffer); END_INTERFACE } IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1Vtbl; interface IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1 { CONST_VTBL struct IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_Initialize(This) \ ( (This)->lpVtbl -> Initialize(This) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_Render(This) \ ( (This)->lpVtbl -> Render(This) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_SetSurfaceSize(This,width,height) \ ( (This)->lpVtbl -> SetSurfaceSize(This,width,height) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_SetFrame(This,theFrame) \ ( (This)->lpVtbl -> SetFrame(This,theFrame) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_Set3DPreviewFormat(This,previewFormat) \ ( (This)->lpVtbl -> Set3DPreviewFormat(This,previewFormat) ) #define IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_GetBackBuffer(This,backBuffer) \ ( (This)->lpVtbl -> GetBackBuffer(This,backBuffer) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoConversion_v14_2_1_INTERFACE_DEFINED__ #define __IDeckLinkVideoConversion_v14_2_1_INTERFACE_DEFINED__ /* interface IDeckLinkVideoConversion_v14_2_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoConversion_v14_2_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3BBCB8A2-DA2C-42D9-B5D8-88083644E99A") IDeckLinkVideoConversion_v14_2_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ConvertFrame( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *srcFrame, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *dstFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoConversion_v14_2_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoConversion_v14_2_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoConversion_v14_2_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoConversion_v14_2_1 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoConversion_v14_2_1, ConvertFrame) HRESULT ( STDMETHODCALLTYPE *ConvertFrame )( IDeckLinkVideoConversion_v14_2_1 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *srcFrame, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *dstFrame); END_INTERFACE } IDeckLinkVideoConversion_v14_2_1Vtbl; interface IDeckLinkVideoConversion_v14_2_1 { CONST_VTBL struct IDeckLinkVideoConversion_v14_2_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoConversion_v14_2_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoConversion_v14_2_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoConversion_v14_2_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoConversion_v14_2_1_ConvertFrame(This,srcFrame,dstFrame) \ ( (This)->lpVtbl -> ConvertFrame(This,srcFrame,dstFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoConversion_v14_2_1_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_CDeckLinkGLScreenPreviewHelper_v14_2_1; #ifdef __cplusplus class DECLSPEC_UUID("F63E77C7-B655-4A4A-9AD0-3CA85D394343") CDeckLinkGLScreenPreviewHelper_v14_2_1; #endif EXTERN_C const CLSID CLSID_CDeckLinkGL3ScreenPreviewHelper_v14_2_1; #ifdef __cplusplus class DECLSPEC_UUID("00696A71-EBC7-491F-AC02-18D3393F33F0") CDeckLinkGL3ScreenPreviewHelper_v14_2_1; #endif EXTERN_C const CLSID CLSID_CDeckLinkDX9ScreenPreviewHelper_v14_2_1; #ifdef __cplusplus class DECLSPEC_UUID("CC010023-E01D-4525-9D59-80C8AB3DC7A0") CDeckLinkDX9ScreenPreviewHelper_v14_2_1; #endif EXTERN_C const CLSID CLSID_CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1; #ifdef __cplusplus class DECLSPEC_UUID("EF2A8478-7DDF-11E9-8F9E-2A86E4085A59") CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1; #endif EXTERN_C const CLSID CLSID_CDeckLinkVideoConversion_v14_2_1; #ifdef __cplusplus class DECLSPEC_UUID("7DBBBB11-5B7B-467D-AEA4-CEA468FD368C") CDeckLinkVideoConversion_v14_2_1; #endif #ifndef __IDeckLinkInputCallback_v11_5_1_INTERFACE_DEFINED__ #define __IDeckLinkInputCallback_v11_5_1_INTERFACE_DEFINED__ /* interface IDeckLinkInputCallback_v11_5_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInputCallback_v11_5_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("DD04E5EC-7415-42AB-AE4A-E80C4DFC044A") IDeckLinkInputCallback_v11_5_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged( /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( /* [in] */ IDeckLinkVideoInputFrame_v14_2_1 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputCallback_v11_5_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInputCallback_v11_5_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInputCallback_v11_5_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInputCallback_v11_5_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInputCallback_v11_5_1, VideoInputFormatChanged) HRESULT ( STDMETHODCALLTYPE *VideoInputFormatChanged )( IDeckLinkInputCallback_v11_5_1 * This, /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags); DECLSPEC_XFGVIRT(IDeckLinkInputCallback_v11_5_1, VideoInputFrameArrived) HRESULT ( STDMETHODCALLTYPE *VideoInputFrameArrived )( IDeckLinkInputCallback_v11_5_1 * This, /* [in] */ IDeckLinkVideoInputFrame_v14_2_1 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket); END_INTERFACE } IDeckLinkInputCallback_v11_5_1Vtbl; interface IDeckLinkInputCallback_v11_5_1 { CONST_VTBL struct IDeckLinkInputCallback_v11_5_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInputCallback_v11_5_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInputCallback_v11_5_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInputCallback_v11_5_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInputCallback_v11_5_1_VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) \ ( (This)->lpVtbl -> VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) ) #define IDeckLinkInputCallback_v11_5_1_VideoInputFrameArrived(This,videoFrame,audioPacket) \ ( (This)->lpVtbl -> VideoInputFrameArrived(This,videoFrame,audioPacket) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInputCallback_v11_5_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_v11_5_1_INTERFACE_DEFINED__ #define __IDeckLinkInput_v11_5_1_INTERFACE_DEFINED__ /* interface IDeckLinkInput_v11_5_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput_v11_5_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("9434C6E4-B15D-4B1C-979E-661E3DDCB4B9") IDeckLinkInput_v11_5_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoInputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoFrameCount( /* [out] */ unsigned int *availableFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoInputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned int *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback_v11_5_1 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInput_v11_5_1Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput_v11_5_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput_v11_5_1 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput_v11_5_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput_v11_5_1 * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDVideoInputConversionMode conversionMode, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkInput_v11_5_1 * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput_v11_5_1 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkInput_v11_5_1 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, EnableVideoInput) HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput_v11_5_1 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, DisableVideoInput) HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput_v11_5_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, GetAvailableVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableVideoFrameCount )( IDeckLinkInput_v11_5_1 * This, /* [out] */ unsigned int *availableFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, SetVideoInputFrameMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetVideoInputFrameMemoryAllocator )( IDeckLinkInput_v11_5_1 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, EnableAudioInput) HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput_v11_5_1 * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, DisableAudioInput) HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput_v11_5_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, GetAvailableAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkInput_v11_5_1 * This, /* [out] */ unsigned int *availableSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, StartStreams) HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput_v11_5_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, StopStreams) HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput_v11_5_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, PauseStreams) HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput_v11_5_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, FlushStreams) HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkInput_v11_5_1 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput_v11_5_1 * This, /* [in] */ IDeckLinkInputCallback_v11_5_1 *theCallback); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_5_1, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkInput_v11_5_1 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkInput_v11_5_1Vtbl; interface IDeckLinkInput_v11_5_1 { CONST_VTBL struct IDeckLinkInput_v11_5_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_v11_5_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_v11_5_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_v11_5_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_v11_5_1_DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,conversionMode,flags,actualMode,supported) ) #define IDeckLinkInput_v11_5_1_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkInput_v11_5_1_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_v11_5_1_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkInput_v11_5_1_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_v11_5_1_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_v11_5_1_GetAvailableVideoFrameCount(This,availableFrameCount) \ ( (This)->lpVtbl -> GetAvailableVideoFrameCount(This,availableFrameCount) ) #define IDeckLinkInput_v11_5_1_SetVideoInputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoInputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkInput_v11_5_1_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_v11_5_1_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_v11_5_1_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkInput_v11_5_1_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_v11_5_1_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_v11_5_1_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_v11_5_1_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkInput_v11_5_1_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkInput_v11_5_1_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_v11_5_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkConfiguration_v10_11_INTERFACE_DEFINED__ #define __IDeckLinkConfiguration_v10_11_INTERFACE_DEFINED__ /* interface IDeckLinkConfiguration_v10_11 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkConfiguration_v10_11; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("EF90380B-4AE5-4346-9077-E288E149F129") IDeckLinkConfiguration_v10_11 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE WriteConfigurationToPreferences( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkConfiguration_v10_11Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkConfiguration_v10_11 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkConfiguration_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkConfiguration_v10_11 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_11, WriteConfigurationToPreferences) HRESULT ( STDMETHODCALLTYPE *WriteConfigurationToPreferences )( IDeckLinkConfiguration_v10_11 * This); END_INTERFACE } IDeckLinkConfiguration_v10_11Vtbl; interface IDeckLinkConfiguration_v10_11 { CONST_VTBL struct IDeckLinkConfiguration_v10_11Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkConfiguration_v10_11_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkConfiguration_v10_11_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkConfiguration_v10_11_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkConfiguration_v10_11_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_11_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_11_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_11_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_11_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_11_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_11_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_11_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_11_WriteConfigurationToPreferences(This) \ ( (This)->lpVtbl -> WriteConfigurationToPreferences(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkConfiguration_v10_11_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAttributes_v10_11_INTERFACE_DEFINED__ #define __IDeckLinkAttributes_v10_11_INTERFACE_DEFINED__ /* interface IDeckLinkAttributes_v10_11 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAttributes_v10_11; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("ABC11843-D966-44CB-96E2-A1CB5D3135C4") IDeckLinkAttributes_v10_11 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BSTR *value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAttributes_v10_11Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAttributes_v10_11 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAttributes_v10_11 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAttributes_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkAttributes_v10_11, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkAttributes_v10_11 * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkAttributes_v10_11, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkAttributes_v10_11 * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkAttributes_v10_11, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkAttributes_v10_11 * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkAttributes_v10_11, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkAttributes_v10_11 * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BSTR *value); END_INTERFACE } IDeckLinkAttributes_v10_11Vtbl; interface IDeckLinkAttributes_v10_11 { CONST_VTBL struct IDeckLinkAttributes_v10_11Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAttributes_v10_11_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAttributes_v10_11_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAttributes_v10_11_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAttributes_v10_11_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkAttributes_v10_11_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkAttributes_v10_11_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkAttributes_v10_11_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAttributes_v10_11_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkNotification_v10_11_INTERFACE_DEFINED__ #define __IDeckLinkNotification_v10_11_INTERFACE_DEFINED__ /* interface IDeckLinkNotification_v10_11 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkNotification_v10_11; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("0A1FB207-E215-441B-9B19-6FA1575946C5") IDeckLinkNotification_v10_11 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Subscribe( /* [in] */ BMDNotifications topic, /* [in] */ IDeckLinkNotificationCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE Unsubscribe( /* [in] */ BMDNotifications topic, /* [in] */ IDeckLinkNotificationCallback *theCallback) = 0; }; #else /* C style interface */ typedef struct IDeckLinkNotification_v10_11Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkNotification_v10_11 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkNotification_v10_11 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkNotification_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkNotification_v10_11, Subscribe) HRESULT ( STDMETHODCALLTYPE *Subscribe )( IDeckLinkNotification_v10_11 * This, /* [in] */ BMDNotifications topic, /* [in] */ IDeckLinkNotificationCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkNotification_v10_11, Unsubscribe) HRESULT ( STDMETHODCALLTYPE *Unsubscribe )( IDeckLinkNotification_v10_11 * This, /* [in] */ BMDNotifications topic, /* [in] */ IDeckLinkNotificationCallback *theCallback); END_INTERFACE } IDeckLinkNotification_v10_11Vtbl; interface IDeckLinkNotification_v10_11 { CONST_VTBL struct IDeckLinkNotification_v10_11Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkNotification_v10_11_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkNotification_v10_11_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkNotification_v10_11_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkNotification_v10_11_Subscribe(This,topic,theCallback) \ ( (This)->lpVtbl -> Subscribe(This,topic,theCallback) ) #define IDeckLinkNotification_v10_11_Unsubscribe(This,topic,theCallback) \ ( (This)->lpVtbl -> Unsubscribe(This,topic,theCallback) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkNotification_v10_11_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkOutput_v10_11_INTERFACE_DEFINED__ #define __IDeckLinkOutput_v10_11_INTERFACE_DEFINED__ /* interface IDeckLinkOutput_v10_11 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkOutput_v10_11; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CC5C8A6E-3F2F-4B3A-87EA-FD78AF300564") IDeckLinkOutput_v10_11 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoOutputFlags flags, /* [out] */ BMDDisplayModeSupport_v10_11 *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame( /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v14_2_1 **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateAncillaryData( /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback( /* [in] */ IDeckLinkVideoOutputCallback_v14_2_1 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedVideoFrameCount( /* [out] */ unsigned int *bufferedFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount, /* [in] */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync( /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [out] */ unsigned int *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples( /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned int *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned int *bufferedSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioCallback( /* [in] */ IDeckLinkAudioOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback( /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback( /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE IsScheduledPlaybackRunning( /* [out] */ BOOL *active) = 0; virtual HRESULT STDMETHODCALLTYPE GetScheduledStreamTime( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE GetReferenceStatus( /* [out] */ BMDReferenceStatus *referenceStatus) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameCompletionReferenceTimestamp( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *frameCompletionTimestamp) = 0; }; #else /* C style interface */ typedef struct IDeckLinkOutput_v10_11Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkOutput_v10_11 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkOutput_v10_11 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkOutput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkOutput_v10_11 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoOutputFlags flags, /* [out] */ BMDDisplayModeSupport_v10_11 *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkOutput_v10_11 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkOutput_v10_11 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, EnableVideoOutput) HRESULT ( STDMETHODCALLTYPE *EnableVideoOutput )( IDeckLinkOutput_v10_11 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, DisableVideoOutput) HRESULT ( STDMETHODCALLTYPE *DisableVideoOutput )( IDeckLinkOutput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, SetVideoOutputFrameMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetVideoOutputFrameMemoryAllocator )( IDeckLinkOutput_v10_11 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, CreateVideoFrame) HRESULT ( STDMETHODCALLTYPE *CreateVideoFrame )( IDeckLinkOutput_v10_11 * This, /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v14_2_1 **outFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, CreateAncillaryData) HRESULT ( STDMETHODCALLTYPE *CreateAncillaryData )( IDeckLinkOutput_v10_11 * This, /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, DisplayVideoFrameSync) HRESULT ( STDMETHODCALLTYPE *DisplayVideoFrameSync )( IDeckLinkOutput_v10_11 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, ScheduleVideoFrame) HRESULT ( STDMETHODCALLTYPE *ScheduleVideoFrame )( IDeckLinkOutput_v10_11 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, SetScheduledFrameCompletionCallback) HRESULT ( STDMETHODCALLTYPE *SetScheduledFrameCompletionCallback )( IDeckLinkOutput_v10_11 * This, /* [in] */ IDeckLinkVideoOutputCallback_v14_2_1 *theCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, GetBufferedVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetBufferedVideoFrameCount )( IDeckLinkOutput_v10_11 * This, /* [out] */ unsigned int *bufferedFrameCount); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, EnableAudioOutput) HRESULT ( STDMETHODCALLTYPE *EnableAudioOutput )( IDeckLinkOutput_v10_11 * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount, /* [in] */ BMDAudioOutputStreamType streamType); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, DisableAudioOutput) HRESULT ( STDMETHODCALLTYPE *DisableAudioOutput )( IDeckLinkOutput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, WriteAudioSamplesSync) HRESULT ( STDMETHODCALLTYPE *WriteAudioSamplesSync )( IDeckLinkOutput_v10_11 * This, /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [out] */ unsigned int *sampleFramesWritten); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, BeginAudioPreroll) HRESULT ( STDMETHODCALLTYPE *BeginAudioPreroll )( IDeckLinkOutput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, EndAudioPreroll) HRESULT ( STDMETHODCALLTYPE *EndAudioPreroll )( IDeckLinkOutput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, ScheduleAudioSamples) HRESULT ( STDMETHODCALLTYPE *ScheduleAudioSamples )( IDeckLinkOutput_v10_11 * This, /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned int *sampleFramesWritten); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, GetBufferedAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkOutput_v10_11 * This, /* [out] */ unsigned int *bufferedSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, FlushBufferedAudioSamples) HRESULT ( STDMETHODCALLTYPE *FlushBufferedAudioSamples )( IDeckLinkOutput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, SetAudioCallback) HRESULT ( STDMETHODCALLTYPE *SetAudioCallback )( IDeckLinkOutput_v10_11 * This, /* [in] */ IDeckLinkAudioOutputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, StartScheduledPlayback) HRESULT ( STDMETHODCALLTYPE *StartScheduledPlayback )( IDeckLinkOutput_v10_11 * This, /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, StopScheduledPlayback) HRESULT ( STDMETHODCALLTYPE *StopScheduledPlayback )( IDeckLinkOutput_v10_11 * This, /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, IsScheduledPlaybackRunning) HRESULT ( STDMETHODCALLTYPE *IsScheduledPlaybackRunning )( IDeckLinkOutput_v10_11 * This, /* [out] */ BOOL *active); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, GetScheduledStreamTime) HRESULT ( STDMETHODCALLTYPE *GetScheduledStreamTime )( IDeckLinkOutput_v10_11 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, GetReferenceStatus) HRESULT ( STDMETHODCALLTYPE *GetReferenceStatus )( IDeckLinkOutput_v10_11 * This, /* [out] */ BMDReferenceStatus *referenceStatus); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkOutput_v10_11 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v10_11, GetFrameCompletionReferenceTimestamp) HRESULT ( STDMETHODCALLTYPE *GetFrameCompletionReferenceTimestamp )( IDeckLinkOutput_v10_11 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *frameCompletionTimestamp); END_INTERFACE } IDeckLinkOutput_v10_11Vtbl; interface IDeckLinkOutput_v10_11 { CONST_VTBL struct IDeckLinkOutput_v10_11Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkOutput_v10_11_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkOutput_v10_11_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkOutput_v10_11_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkOutput_v10_11_DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) ) #define IDeckLinkOutput_v10_11_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkOutput_v10_11_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkOutput_v10_11_EnableVideoOutput(This,displayMode,flags) \ ( (This)->lpVtbl -> EnableVideoOutput(This,displayMode,flags) ) #define IDeckLinkOutput_v10_11_DisableVideoOutput(This) \ ( (This)->lpVtbl -> DisableVideoOutput(This) ) #define IDeckLinkOutput_v10_11_SetVideoOutputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoOutputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkOutput_v10_11_CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_v10_11_CreateAncillaryData(This,pixelFormat,outBuffer) \ ( (This)->lpVtbl -> CreateAncillaryData(This,pixelFormat,outBuffer) ) #define IDeckLinkOutput_v10_11_DisplayVideoFrameSync(This,theFrame) \ ( (This)->lpVtbl -> DisplayVideoFrameSync(This,theFrame) ) #define IDeckLinkOutput_v10_11_ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) \ ( (This)->lpVtbl -> ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) ) #define IDeckLinkOutput_v10_11_SetScheduledFrameCompletionCallback(This,theCallback) \ ( (This)->lpVtbl -> SetScheduledFrameCompletionCallback(This,theCallback) ) #define IDeckLinkOutput_v10_11_GetBufferedVideoFrameCount(This,bufferedFrameCount) \ ( (This)->lpVtbl -> GetBufferedVideoFrameCount(This,bufferedFrameCount) ) #define IDeckLinkOutput_v10_11_EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) \ ( (This)->lpVtbl -> EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) ) #define IDeckLinkOutput_v10_11_DisableAudioOutput(This) \ ( (This)->lpVtbl -> DisableAudioOutput(This) ) #define IDeckLinkOutput_v10_11_WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) \ ( (This)->lpVtbl -> WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) ) #define IDeckLinkOutput_v10_11_BeginAudioPreroll(This) \ ( (This)->lpVtbl -> BeginAudioPreroll(This) ) #define IDeckLinkOutput_v10_11_EndAudioPreroll(This) \ ( (This)->lpVtbl -> EndAudioPreroll(This) ) #define IDeckLinkOutput_v10_11_ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) \ ( (This)->lpVtbl -> ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) ) #define IDeckLinkOutput_v10_11_GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) ) #define IDeckLinkOutput_v10_11_FlushBufferedAudioSamples(This) \ ( (This)->lpVtbl -> FlushBufferedAudioSamples(This) ) #define IDeckLinkOutput_v10_11_SetAudioCallback(This,theCallback) \ ( (This)->lpVtbl -> SetAudioCallback(This,theCallback) ) #define IDeckLinkOutput_v10_11_StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) \ ( (This)->lpVtbl -> StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) ) #define IDeckLinkOutput_v10_11_StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) \ ( (This)->lpVtbl -> StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) ) #define IDeckLinkOutput_v10_11_IsScheduledPlaybackRunning(This,active) \ ( (This)->lpVtbl -> IsScheduledPlaybackRunning(This,active) ) #define IDeckLinkOutput_v10_11_GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) \ ( (This)->lpVtbl -> GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) ) #define IDeckLinkOutput_v10_11_GetReferenceStatus(This,referenceStatus) \ ( (This)->lpVtbl -> GetReferenceStatus(This,referenceStatus) ) #define IDeckLinkOutput_v10_11_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #define IDeckLinkOutput_v10_11_GetFrameCompletionReferenceTimestamp(This,theFrame,desiredTimeScale,frameCompletionTimestamp) \ ( (This)->lpVtbl -> GetFrameCompletionReferenceTimestamp(This,theFrame,desiredTimeScale,frameCompletionTimestamp) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkOutput_v10_11_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_v10_11_INTERFACE_DEFINED__ #define __IDeckLinkInput_v10_11_INTERFACE_DEFINED__ /* interface IDeckLinkInput_v10_11 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput_v10_11; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("AF22762B-DFAC-4846-AA79-FA8883560995") IDeckLinkInput_v10_11 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags, /* [out] */ BMDDisplayModeSupport_v10_11 *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoFrameCount( /* [out] */ unsigned int *availableFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoInputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned int *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback_v11_5_1 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInput_v10_11Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput_v10_11 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput_v10_11 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput_v10_11 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags, /* [out] */ BMDDisplayModeSupport_v10_11 *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput_v10_11 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkInput_v10_11 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, EnableVideoInput) HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput_v10_11 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, DisableVideoInput) HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, GetAvailableVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableVideoFrameCount )( IDeckLinkInput_v10_11 * This, /* [out] */ unsigned int *availableFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, SetVideoInputFrameMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetVideoInputFrameMemoryAllocator )( IDeckLinkInput_v10_11 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, EnableAudioInput) HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput_v10_11 * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, DisableAudioInput) HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, GetAvailableAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkInput_v10_11 * This, /* [out] */ unsigned int *availableSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, StartStreams) HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, StopStreams) HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, PauseStreams) HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, FlushStreams) HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput_v10_11 * This, /* [in] */ IDeckLinkInputCallback_v11_5_1 *theCallback); DECLSPEC_XFGVIRT(IDeckLinkInput_v10_11, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkInput_v10_11 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkInput_v10_11Vtbl; interface IDeckLinkInput_v10_11 { CONST_VTBL struct IDeckLinkInput_v10_11Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_v10_11_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_v10_11_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_v10_11_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_v10_11_DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) ) #define IDeckLinkInput_v10_11_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_v10_11_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkInput_v10_11_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_v10_11_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_v10_11_GetAvailableVideoFrameCount(This,availableFrameCount) \ ( (This)->lpVtbl -> GetAvailableVideoFrameCount(This,availableFrameCount) ) #define IDeckLinkInput_v10_11_SetVideoInputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoInputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkInput_v10_11_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_v10_11_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_v10_11_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkInput_v10_11_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_v10_11_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_v10_11_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_v10_11_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkInput_v10_11_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkInput_v10_11_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_v10_11_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkEncoderInput_v10_11_INTERFACE_DEFINED__ #define __IDeckLinkEncoderInput_v10_11_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderInput_v10_11 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderInput_v10_11; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("270587DA-6B7D-42E7-A1F0-6D853F581185") IDeckLinkEncoderInput_v10_11 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags, /* [out] */ BMDDisplayModeSupport_v10_11 *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailablePacketsCount( /* [out] */ unsigned int *availablePacketsCount) = 0; virtual HRESULT STDMETHODCALLTYPE SetMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioFormat audioFormat, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned int *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkEncoderInputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderInput_v10_11Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderInput_v10_11 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderInput_v10_11 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkEncoderInput_v10_11 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags, /* [out] */ BMDDisplayModeSupport_v10_11 *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkEncoderInput_v10_11 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, EnableVideoInput) HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkEncoderInput_v10_11 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, DisableVideoInput) HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkEncoderInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, GetAvailablePacketsCount) HRESULT ( STDMETHODCALLTYPE *GetAvailablePacketsCount )( IDeckLinkEncoderInput_v10_11 * This, /* [out] */ unsigned int *availablePacketsCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, SetMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetMemoryAllocator )( IDeckLinkEncoderInput_v10_11 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, EnableAudioInput) HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkEncoderInput_v10_11 * This, /* [in] */ BMDAudioFormat audioFormat, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, DisableAudioInput) HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkEncoderInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, GetAvailableAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkEncoderInput_v10_11 * This, /* [out] */ unsigned int *availableSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, StartStreams) HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkEncoderInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, StopStreams) HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkEncoderInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, PauseStreams) HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkEncoderInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, FlushStreams) HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkEncoderInput_v10_11 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkEncoderInput_v10_11 * This, /* [in] */ IDeckLinkEncoderInputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkEncoderInput_v10_11, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkEncoderInput_v10_11 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkEncoderInput_v10_11Vtbl; interface IDeckLinkEncoderInput_v10_11 { CONST_VTBL struct IDeckLinkEncoderInput_v10_11Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderInput_v10_11_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderInput_v10_11_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderInput_v10_11_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderInput_v10_11_DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) ) #define IDeckLinkEncoderInput_v10_11_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkEncoderInput_v10_11_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkEncoderInput_v10_11_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkEncoderInput_v10_11_GetAvailablePacketsCount(This,availablePacketsCount) \ ( (This)->lpVtbl -> GetAvailablePacketsCount(This,availablePacketsCount) ) #define IDeckLinkEncoderInput_v10_11_SetMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetMemoryAllocator(This,theAllocator) ) #define IDeckLinkEncoderInput_v10_11_EnableAudioInput(This,audioFormat,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,audioFormat,sampleRate,sampleType,channelCount) ) #define IDeckLinkEncoderInput_v10_11_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkEncoderInput_v10_11_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkEncoderInput_v10_11_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkEncoderInput_v10_11_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkEncoderInput_v10_11_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkEncoderInput_v10_11_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkEncoderInput_v10_11_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkEncoderInput_v10_11_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderInput_v10_11_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_CDeckLinkIterator_v10_11; #ifdef __cplusplus class DECLSPEC_UUID("87D2693F-8D4A-45C7-B43F-10ACBA25E68F") CDeckLinkIterator_v10_11; #endif EXTERN_C const CLSID CLSID_CDeckLinkDiscovery_v10_11; #ifdef __cplusplus class DECLSPEC_UUID("652615D4-26CD-4514-B161-2FD5072ED008") CDeckLinkDiscovery_v10_11; #endif #ifndef __IDeckLinkConfiguration_v10_9_INTERFACE_DEFINED__ #define __IDeckLinkConfiguration_v10_9_INTERFACE_DEFINED__ /* interface IDeckLinkConfiguration_v10_9 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkConfiguration_v10_9; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CB71734A-FE37-4E8D-8E13-802133A1C3F2") IDeckLinkConfiguration_v10_9 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE WriteConfigurationToPreferences( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkConfiguration_v10_9Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkConfiguration_v10_9 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkConfiguration_v10_9 * This); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkConfiguration_v10_9 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_9, WriteConfigurationToPreferences) HRESULT ( STDMETHODCALLTYPE *WriteConfigurationToPreferences )( IDeckLinkConfiguration_v10_9 * This); END_INTERFACE } IDeckLinkConfiguration_v10_9Vtbl; interface IDeckLinkConfiguration_v10_9 { CONST_VTBL struct IDeckLinkConfiguration_v10_9Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkConfiguration_v10_9_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkConfiguration_v10_9_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkConfiguration_v10_9_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkConfiguration_v10_9_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_9_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_9_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_9_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_9_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_9_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_9_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_9_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_9_WriteConfigurationToPreferences(This) \ ( (This)->lpVtbl -> WriteConfigurationToPreferences(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkConfiguration_v10_9_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_CBMDStreamingDiscovery_v10_8; #ifdef __cplusplus class DECLSPEC_UUID("0CAA31F6-8A26-40B0-86A4-BF58DCCA710C") CBMDStreamingDiscovery_v10_8; #endif #ifndef __IDeckLinkConfiguration_v10_4_INTERFACE_DEFINED__ #define __IDeckLinkConfiguration_v10_4_INTERFACE_DEFINED__ /* interface IDeckLinkConfiguration_v10_4 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkConfiguration_v10_4; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("1E69FCF6-4203-4936-8076-2A9F4CFD50CB") IDeckLinkConfiguration_v10_4 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE WriteConfigurationToPreferences( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkConfiguration_v10_4Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkConfiguration_v10_4 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkConfiguration_v10_4 * This); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkConfiguration_v10_4 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_4, WriteConfigurationToPreferences) HRESULT ( STDMETHODCALLTYPE *WriteConfigurationToPreferences )( IDeckLinkConfiguration_v10_4 * This); END_INTERFACE } IDeckLinkConfiguration_v10_4Vtbl; interface IDeckLinkConfiguration_v10_4 { CONST_VTBL struct IDeckLinkConfiguration_v10_4Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkConfiguration_v10_4_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkConfiguration_v10_4_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkConfiguration_v10_4_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkConfiguration_v10_4_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_4_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_4_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_4_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_4_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_4_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_4_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_4_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_4_WriteConfigurationToPreferences(This) \ ( (This)->lpVtbl -> WriteConfigurationToPreferences(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkConfiguration_v10_4_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkConfiguration_v10_2_INTERFACE_DEFINED__ #define __IDeckLinkConfiguration_v10_2_INTERFACE_DEFINED__ /* interface IDeckLinkConfiguration_v10_2 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkConfiguration_v10_2; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C679A35B-610C-4D09-B748-1D0478100FC0") IDeckLinkConfiguration_v10_2 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE WriteConfigurationToPreferences( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkConfiguration_v10_2Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkConfiguration_v10_2 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkConfiguration_v10_2 * This); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkConfiguration_v10_2 * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkConfiguration_v10_2, WriteConfigurationToPreferences) HRESULT ( STDMETHODCALLTYPE *WriteConfigurationToPreferences )( IDeckLinkConfiguration_v10_2 * This); END_INTERFACE } IDeckLinkConfiguration_v10_2Vtbl; interface IDeckLinkConfiguration_v10_2 { CONST_VTBL struct IDeckLinkConfiguration_v10_2Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkConfiguration_v10_2_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkConfiguration_v10_2_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkConfiguration_v10_2_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkConfiguration_v10_2_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_2_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_2_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_2_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_2_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_2_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_2_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_2_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IDeckLinkConfiguration_v10_2_WriteConfigurationToPreferences(This) \ ( (This)->lpVtbl -> WriteConfigurationToPreferences(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkConfiguration_v10_2_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrameMetadataExtensions_v11_5_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrameMetadataExtensions_v11_5_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrameMetadataExtensions_v11_5 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrameMetadataExtensions_v11_5; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("D5973DC9-6432-46D0-8F0B-2496F8A1238F") IDeckLinkVideoFrameMetadataExtensions_v11_5 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* [out] */ BSTR *value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrameMetadataExtensions_v11_5Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrameMetadataExtensions_v11_5 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrameMetadataExtensions_v11_5 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrameMetadataExtensions_v11_5 * This); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions_v11_5, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkVideoFrameMetadataExtensions_v11_5 * This, /* [in] */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions_v11_5, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkVideoFrameMetadataExtensions_v11_5 * This, /* [in] */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions_v11_5, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkVideoFrameMetadataExtensions_v11_5 * This, /* [in] */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkVideoFrameMetadataExtensions_v11_5, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkVideoFrameMetadataExtensions_v11_5 * This, /* [in] */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* [out] */ BSTR *value); END_INTERFACE } IDeckLinkVideoFrameMetadataExtensions_v11_5Vtbl; interface IDeckLinkVideoFrameMetadataExtensions_v11_5 { CONST_VTBL struct IDeckLinkVideoFrameMetadataExtensions_v11_5Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrameMetadataExtensions_v11_5_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrameMetadataExtensions_v11_5_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrameMetadataExtensions_v11_5_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrameMetadataExtensions_v11_5_GetInt(This,metadataID,value) \ ( (This)->lpVtbl -> GetInt(This,metadataID,value) ) #define IDeckLinkVideoFrameMetadataExtensions_v11_5_GetFloat(This,metadataID,value) \ ( (This)->lpVtbl -> GetFloat(This,metadataID,value) ) #define IDeckLinkVideoFrameMetadataExtensions_v11_5_GetFlag(This,metadataID,value) \ ( (This)->lpVtbl -> GetFlag(This,metadataID,value) ) #define IDeckLinkVideoFrameMetadataExtensions_v11_5_GetString(This,metadataID,value) \ ( (This)->lpVtbl -> GetString(This,metadataID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrameMetadataExtensions_v11_5_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkOutput_v11_4_INTERFACE_DEFINED__ #define __IDeckLinkOutput_v11_4_INTERFACE_DEFINED__ /* interface IDeckLinkOutput_v11_4 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkOutput_v11_4; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("065A0F6C-C508-4D0D-B919-F5EB0EBFC96B") IDeckLinkOutput_v11_4 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame( /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v14_2_1 **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateAncillaryData( /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback( /* [in] */ IDeckLinkVideoOutputCallback_v14_2_1 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedVideoFrameCount( /* [out] */ unsigned int *bufferedFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount, /* [in] */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync( /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [out] */ unsigned int *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples( /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned int *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned int *bufferedSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioCallback( /* [in] */ IDeckLinkAudioOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback( /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback( /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE IsScheduledPlaybackRunning( /* [out] */ BOOL *active) = 0; virtual HRESULT STDMETHODCALLTYPE GetScheduledStreamTime( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE GetReferenceStatus( /* [out] */ BMDReferenceStatus *referenceStatus) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameCompletionReferenceTimestamp( /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *frameCompletionTimestamp) = 0; }; #else /* C style interface */ typedef struct IDeckLinkOutput_v11_4Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkOutput_v11_4 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkOutput_v11_4 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkOutput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BMDDisplayMode *actualMode, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkOutput_v11_4 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkOutput_v11_4 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, EnableVideoOutput) HRESULT ( STDMETHODCALLTYPE *EnableVideoOutput )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, DisableVideoOutput) HRESULT ( STDMETHODCALLTYPE *DisableVideoOutput )( IDeckLinkOutput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, SetVideoOutputFrameMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetVideoOutputFrameMemoryAllocator )( IDeckLinkOutput_v11_4 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, CreateVideoFrame) HRESULT ( STDMETHODCALLTYPE *CreateVideoFrame )( IDeckLinkOutput_v11_4 * This, /* [in] */ int width, /* [in] */ int height, /* [in] */ int rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v14_2_1 **outFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, CreateAncillaryData) HRESULT ( STDMETHODCALLTYPE *CreateAncillaryData )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, DisplayVideoFrameSync) HRESULT ( STDMETHODCALLTYPE *DisplayVideoFrameSync )( IDeckLinkOutput_v11_4 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, ScheduleVideoFrame) HRESULT ( STDMETHODCALLTYPE *ScheduleVideoFrame )( IDeckLinkOutput_v11_4 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, SetScheduledFrameCompletionCallback) HRESULT ( STDMETHODCALLTYPE *SetScheduledFrameCompletionCallback )( IDeckLinkOutput_v11_4 * This, /* [in] */ IDeckLinkVideoOutputCallback_v14_2_1 *theCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, GetBufferedVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetBufferedVideoFrameCount )( IDeckLinkOutput_v11_4 * This, /* [out] */ unsigned int *bufferedFrameCount); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, EnableAudioOutput) HRESULT ( STDMETHODCALLTYPE *EnableAudioOutput )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount, /* [in] */ BMDAudioOutputStreamType streamType); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, DisableAudioOutput) HRESULT ( STDMETHODCALLTYPE *DisableAudioOutput )( IDeckLinkOutput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, WriteAudioSamplesSync) HRESULT ( STDMETHODCALLTYPE *WriteAudioSamplesSync )( IDeckLinkOutput_v11_4 * This, /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [out] */ unsigned int *sampleFramesWritten); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, BeginAudioPreroll) HRESULT ( STDMETHODCALLTYPE *BeginAudioPreroll )( IDeckLinkOutput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, EndAudioPreroll) HRESULT ( STDMETHODCALLTYPE *EndAudioPreroll )( IDeckLinkOutput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, ScheduleAudioSamples) HRESULT ( STDMETHODCALLTYPE *ScheduleAudioSamples )( IDeckLinkOutput_v11_4 * This, /* [in] */ void *buffer, /* [in] */ unsigned int sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned int *sampleFramesWritten); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, GetBufferedAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkOutput_v11_4 * This, /* [out] */ unsigned int *bufferedSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, FlushBufferedAudioSamples) HRESULT ( STDMETHODCALLTYPE *FlushBufferedAudioSamples )( IDeckLinkOutput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, SetAudioCallback) HRESULT ( STDMETHODCALLTYPE *SetAudioCallback )( IDeckLinkOutput_v11_4 * This, /* [in] */ IDeckLinkAudioOutputCallback *theCallback); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, StartScheduledPlayback) HRESULT ( STDMETHODCALLTYPE *StartScheduledPlayback )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, StopScheduledPlayback) HRESULT ( STDMETHODCALLTYPE *StopScheduledPlayback )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, IsScheduledPlaybackRunning) HRESULT ( STDMETHODCALLTYPE *IsScheduledPlaybackRunning )( IDeckLinkOutput_v11_4 * This, /* [out] */ BOOL *active); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, GetScheduledStreamTime) HRESULT ( STDMETHODCALLTYPE *GetScheduledStreamTime )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, GetReferenceStatus) HRESULT ( STDMETHODCALLTYPE *GetReferenceStatus )( IDeckLinkOutput_v11_4 * This, /* [out] */ BMDReferenceStatus *referenceStatus); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkOutput_v11_4 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); DECLSPEC_XFGVIRT(IDeckLinkOutput_v11_4, GetFrameCompletionReferenceTimestamp) HRESULT ( STDMETHODCALLTYPE *GetFrameCompletionReferenceTimestamp )( IDeckLinkOutput_v11_4 * This, /* [in] */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *frameCompletionTimestamp); END_INTERFACE } IDeckLinkOutput_v11_4Vtbl; interface IDeckLinkOutput_v11_4 { CONST_VTBL struct IDeckLinkOutput_v11_4Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkOutput_v11_4_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkOutput_v11_4_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkOutput_v11_4_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkOutput_v11_4_DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,flags,actualMode,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,flags,actualMode,supported) ) #define IDeckLinkOutput_v11_4_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkOutput_v11_4_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkOutput_v11_4_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkOutput_v11_4_EnableVideoOutput(This,displayMode,flags) \ ( (This)->lpVtbl -> EnableVideoOutput(This,displayMode,flags) ) #define IDeckLinkOutput_v11_4_DisableVideoOutput(This) \ ( (This)->lpVtbl -> DisableVideoOutput(This) ) #define IDeckLinkOutput_v11_4_SetVideoOutputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoOutputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkOutput_v11_4_CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_v11_4_CreateAncillaryData(This,pixelFormat,outBuffer) \ ( (This)->lpVtbl -> CreateAncillaryData(This,pixelFormat,outBuffer) ) #define IDeckLinkOutput_v11_4_DisplayVideoFrameSync(This,theFrame) \ ( (This)->lpVtbl -> DisplayVideoFrameSync(This,theFrame) ) #define IDeckLinkOutput_v11_4_ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) \ ( (This)->lpVtbl -> ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) ) #define IDeckLinkOutput_v11_4_SetScheduledFrameCompletionCallback(This,theCallback) \ ( (This)->lpVtbl -> SetScheduledFrameCompletionCallback(This,theCallback) ) #define IDeckLinkOutput_v11_4_GetBufferedVideoFrameCount(This,bufferedFrameCount) \ ( (This)->lpVtbl -> GetBufferedVideoFrameCount(This,bufferedFrameCount) ) #define IDeckLinkOutput_v11_4_EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) \ ( (This)->lpVtbl -> EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) ) #define IDeckLinkOutput_v11_4_DisableAudioOutput(This) \ ( (This)->lpVtbl -> DisableAudioOutput(This) ) #define IDeckLinkOutput_v11_4_WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) \ ( (This)->lpVtbl -> WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) ) #define IDeckLinkOutput_v11_4_BeginAudioPreroll(This) \ ( (This)->lpVtbl -> BeginAudioPreroll(This) ) #define IDeckLinkOutput_v11_4_EndAudioPreroll(This) \ ( (This)->lpVtbl -> EndAudioPreroll(This) ) #define IDeckLinkOutput_v11_4_ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) \ ( (This)->lpVtbl -> ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) ) #define IDeckLinkOutput_v11_4_GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) ) #define IDeckLinkOutput_v11_4_FlushBufferedAudioSamples(This) \ ( (This)->lpVtbl -> FlushBufferedAudioSamples(This) ) #define IDeckLinkOutput_v11_4_SetAudioCallback(This,theCallback) \ ( (This)->lpVtbl -> SetAudioCallback(This,theCallback) ) #define IDeckLinkOutput_v11_4_StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) \ ( (This)->lpVtbl -> StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) ) #define IDeckLinkOutput_v11_4_StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) \ ( (This)->lpVtbl -> StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) ) #define IDeckLinkOutput_v11_4_IsScheduledPlaybackRunning(This,active) \ ( (This)->lpVtbl -> IsScheduledPlaybackRunning(This,active) ) #define IDeckLinkOutput_v11_4_GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) \ ( (This)->lpVtbl -> GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) ) #define IDeckLinkOutput_v11_4_GetReferenceStatus(This,referenceStatus) \ ( (This)->lpVtbl -> GetReferenceStatus(This,referenceStatus) ) #define IDeckLinkOutput_v11_4_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #define IDeckLinkOutput_v11_4_GetFrameCompletionReferenceTimestamp(This,theFrame,desiredTimeScale,frameCompletionTimestamp) \ ( (This)->lpVtbl -> GetFrameCompletionReferenceTimestamp(This,theFrame,desiredTimeScale,frameCompletionTimestamp) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkOutput_v11_4_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_v11_4_INTERFACE_DEFINED__ #define __IDeckLinkInput_v11_4_INTERFACE_DEFINED__ /* interface IDeckLinkInput_v11_4 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput_v11_4; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("2A88CF76-F494-4216-A7EF-DC74EEB83882") IDeckLinkInput_v11_4 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BOOL *supported) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayMode( /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoFrameCount( /* [out] */ unsigned int *availableFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoInputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned int *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback_v11_5_1 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInput_v11_4Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput_v11_4 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput_v11_4 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, DoesSupportVideoMode) HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput_v11_4 * This, /* [in] */ BMDVideoConnection connection, /* [in] */ BMDDisplayMode requestedMode, /* [in] */ BMDPixelFormat requestedPixelFormat, /* [in] */ BMDSupportedVideoModeFlags flags, /* [out] */ BOOL *supported); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, GetDisplayMode) HRESULT ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkInput_v11_4 * This, /* [in] */ BMDDisplayMode displayMode, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, GetDisplayModeIterator) HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput_v11_4 * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, SetScreenPreviewCallback) HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkInput_v11_4 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, EnableVideoInput) HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput_v11_4 * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, DisableVideoInput) HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, GetAvailableVideoFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableVideoFrameCount )( IDeckLinkInput_v11_4 * This, /* [out] */ unsigned int *availableFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, SetVideoInputFrameMemoryAllocator) HRESULT ( STDMETHODCALLTYPE *SetVideoInputFrameMemoryAllocator )( IDeckLinkInput_v11_4 * This, /* [in] */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, EnableAudioInput) HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput_v11_4 * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned int channelCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, DisableAudioInput) HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, GetAvailableAudioSampleFrameCount) HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkInput_v11_4 * This, /* [out] */ unsigned int *availableSampleFrameCount); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, StartStreams) HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, StopStreams) HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, PauseStreams) HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, FlushStreams) HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkInput_v11_4 * This); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, SetCallback) HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput_v11_4 * This, /* [in] */ IDeckLinkInputCallback_v11_5_1 *theCallback); DECLSPEC_XFGVIRT(IDeckLinkInput_v11_4, GetHardwareReferenceClock) HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkInput_v11_4 * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkInput_v11_4Vtbl; interface IDeckLinkInput_v11_4 { CONST_VTBL struct IDeckLinkInput_v11_4Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_v11_4_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_v11_4_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_v11_4_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_v11_4_DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,flags,supported) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,connection,requestedMode,requestedPixelFormat,flags,supported) ) #define IDeckLinkInput_v11_4_GetDisplayMode(This,displayMode,resultDisplayMode) \ ( (This)->lpVtbl -> GetDisplayMode(This,displayMode,resultDisplayMode) ) #define IDeckLinkInput_v11_4_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_v11_4_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkInput_v11_4_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_v11_4_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_v11_4_GetAvailableVideoFrameCount(This,availableFrameCount) \ ( (This)->lpVtbl -> GetAvailableVideoFrameCount(This,availableFrameCount) ) #define IDeckLinkInput_v11_4_SetVideoInputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoInputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkInput_v11_4_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_v11_4_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_v11_4_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkInput_v11_4_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_v11_4_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_v11_4_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_v11_4_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkInput_v11_4_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkInput_v11_4_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_v11_4_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_CDeckLinkIterator_v10_8; #ifdef __cplusplus class DECLSPEC_UUID("1F2E109A-8F4F-49E4-9203-135595CB6FA5") CDeckLinkIterator_v10_8; #endif EXTERN_C const CLSID CLSID_CDeckLinkDiscovery_v10_8; #ifdef __cplusplus class DECLSPEC_UUID("1073A05C-D885-47E9-B3C6-129B3F9F648B") CDeckLinkDiscovery_v10_8; #endif #ifndef __IDeckLinkEncoderConfiguration_v10_5_INTERFACE_DEFINED__ #define __IDeckLinkEncoderConfiguration_v10_5_INTERFACE_DEFINED__ /* interface IDeckLinkEncoderConfiguration_v10_5 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkEncoderConfiguration_v10_5; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("67455668-0848-45DF-8D8E-350A77C9A028") IDeckLinkEncoderConfiguration_v10_5 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetDecoderConfigurationInfo( /* [out] */ void *buffer, /* [in] */ long bufferSize, /* [out] */ long *returnedSize) = 0; }; #else /* C style interface */ typedef struct IDeckLinkEncoderConfiguration_v10_5Vtbl { BEGIN_INTERFACE DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkEncoderConfiguration_v10_5 * This); DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkEncoderConfiguration_v10_5 * This); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, SetFlag) HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ BOOL value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, GetFlag) HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ BOOL *value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, SetInt) HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ LONGLONG value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, GetInt) HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ LONGLONG *value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, SetFloat) HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ double value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, GetFloat) HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ double *value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, SetString) HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [in] */ BSTR value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, GetString) HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [in] */ BMDDeckLinkEncoderConfigurationID cfgID, /* [out] */ BSTR *value); DECLSPEC_XFGVIRT(IDeckLinkEncoderConfiguration_v10_5, GetDecoderConfigurationInfo) HRESULT ( STDMETHODCALLTYPE *GetDecoderConfigurationInfo )( IDeckLinkEncoderConfiguration_v10_5 * This, /* [out] */ void *buffer, /* [in] */ long bufferSize, /* [out] */ long *returnedSize); END_INTERFACE } IDeckLinkEncoderConfiguration_v10_5Vtbl; interface IDeckLinkEncoderConfiguration_v10_5 { CONST_VTBL struct IDeckLinkEncoderConfiguration_v10_5Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkEncoderConfiguration_v10_5_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkEncoderConfiguration_v10_5_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkEncoderConfiguration_v10_5_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkEncoderConfiguration_v10_5_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_v10_5_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_v10_5_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_v10_5_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_v10_5_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_v10_5_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_v10_5_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_v10_5_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IDeckLinkEncoderConfiguration_v10_5_GetDecoderConfigurationInfo(This,buffer,bufferSize,returnedSize) \ ( (This)->lpVtbl -> GetDecoderConfigurationInfo(This,buffer,bufferSize,returnedSize) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkEncoderConfiguration_v10_5_INTERFACE_DEFINED__ */ #endif /* __DeckLinkAPI_LIBRARY_DEFINED__ */ /* Additional Prototypes for ALL interfaces */ /* end of Additional Prototypes */ #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/decklink/win/DeckLinkAPI_i.cpp000664 000000 000000 00000040214 15172202314 022235 0ustar00rootroot000000 000000 /* this ALWAYS GENERATED file contains the IIDs and CLSIDs */ /* link this file in with the server and any clients */ /* File created by MIDL compiler version 8.01.0628 */ /* at Mon Jan 18 19:14:07 2038 */ /* Compiler settings for ..\..\..\Win\include\DeckLinkAPI.idl: Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0628 protocol : all , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: __declspec(uuid()), __declspec(selectany), __declspec(novtable) DECLSPEC_UUID(), MIDL_INTERFACE() */ /* @@MIDL_FILE_HEADING( ) */ #ifdef __cplusplus extern "C"{ #endif #include #include #ifdef _MIDL_USE_GUIDDEF_ #ifndef INITGUID #define INITGUID #include #undef INITGUID #else #include #endif #define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) #else // !_MIDL_USE_GUIDDEF_ #ifndef __IID_DEFINED__ #define __IID_DEFINED__ typedef struct _IID { unsigned long x; unsigned short s1; unsigned short s2; unsigned char c[8]; } IID; #endif // __IID_DEFINED__ #ifndef CLSID_DEFINED #define CLSID_DEFINED typedef IID CLSID; #endif // CLSID_DEFINED #define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ EXTERN_C __declspec(selectany) const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} #endif // !_MIDL_USE_GUIDDEF_ MIDL_DEFINE_GUID(IID, LIBID_DeckLinkAPI,0xD864517A,0xEDD5,0x466D,0x86,0x7D,0xC8,0x19,0xF1,0xC0,0x52,0xBB); MIDL_DEFINE_GUID(IID, IID_IDeckLinkTimecode,0xBC6CFBD3,0x8317,0x4325,0xAC,0x1C,0x12,0x16,0x39,0x1E,0x93,0x40); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDisplayModeIterator,0x9C88499F,0xF601,0x4021,0xB8,0x0B,0x03,0x2E,0x4E,0xB4,0x1C,0x35); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDisplayMode,0x3EB2C1AB,0x0A3D,0x4523,0xA3,0xAD,0xF4,0x0D,0x7F,0xB1,0x4E,0x78); MIDL_DEFINE_GUID(IID, IID_IDeckLink,0xC418FBDD,0x0587,0x48ED,0x8F,0xE5,0x64,0x0F,0x0A,0x14,0xAF,0x91); MIDL_DEFINE_GUID(IID, IID_IDeckLinkConfiguration,0x912F634B,0x2D4E,0x40A4,0x8A,0xAB,0x8D,0x80,0xB7,0x3F,0x12,0x89); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderConfiguration,0x138050E5,0xC60A,0x4552,0xBF,0x3F,0x0F,0x35,0x80,0x49,0x32,0x7E); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDeckControlStatusCallback,0x53436FFB,0xB434,0x4906,0xBA,0xDC,0xAE,0x30,0x60,0xFF,0xE8,0xEF); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDeckControl,0x8E1C3ACE,0x19C7,0x4E00,0x8B,0x92,0xD8,0x04,0x31,0xD9,0x58,0xBE); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingDeviceNotificationCallback,0xF9531D64,0x3305,0x4B29,0xA3,0x87,0x7F,0x74,0xBB,0x0D,0x0E,0x84); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingH264InputCallback,0x823C475F,0x55AE,0x46F9,0x89,0x0C,0x53,0x7C,0xC5,0xCE,0xDC,0xCA); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingDiscovery,0x2C837444,0xF989,0x4D87,0x90,0x1A,0x47,0xC8,0xA3,0x6D,0x09,0x6D); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingVideoEncodingMode,0x1AB8035B,0xCD13,0x458D,0xB6,0xDF,0x5E,0x8F,0x7C,0x21,0x41,0xD9); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingMutableVideoEncodingMode,0x19BF7D90,0x1E0A,0x400D,0xB2,0xC6,0xFF,0xC4,0xE7,0x8A,0xD4,0x9D); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingVideoEncodingModePresetIterator,0x7AC731A3,0xC950,0x4AD0,0x80,0x4A,0x83,0x77,0xAA,0x51,0xC6,0xC4); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingDeviceInput,0x24B6B6EC,0x1727,0x44BB,0x98,0x18,0x34,0xFF,0x08,0x6A,0xCF,0x98); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingH264NALPacket,0xE260E955,0x14BE,0x4395,0x97,0x75,0x9F,0x02,0xCC,0x0A,0x9D,0x89); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingAudioPacket,0xD9EB5902,0x1AD2,0x43F4,0x9E,0x2C,0x3C,0xFA,0x50,0xB5,0xEE,0x19); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingMPEG2TSPacket,0x91810D1C,0x4FB3,0x4AAA,0xAE,0x56,0xFA,0x30,0x1D,0x3D,0xFA,0x4C); MIDL_DEFINE_GUID(IID, IID_IBMDStreamingH264NALParser,0x5867F18C,0x5BFA,0x4CCC,0xB2,0xA7,0x9D,0xFD,0x14,0x04,0x17,0xD2); MIDL_DEFINE_GUID(CLSID, CLSID_CBMDStreamingDiscovery,0x23A4EDF5,0xA0E5,0x432C,0x94,0xEF,0x3B,0xAB,0xB5,0xF8,0x1C,0x82); MIDL_DEFINE_GUID(CLSID, CLSID_CBMDStreamingH264NALParser,0x7753EFBD,0x951C,0x407C,0x97,0xA5,0x23,0xC7,0x37,0xB7,0x3B,0x52); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoOutputCallback,0x5BE6DF26,0x02CE,0x433E,0x99,0xD9,0x9A,0x87,0xC3,0xAC,0x17,0x1F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInputCallback,0x3A94F075,0xC37D,0x4BA8,0xBC,0xC0,0x1D,0x77,0x8C,0x8F,0x88,0x1B); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderInputCallback,0xACF13E61,0xF4A0,0x4974,0xA6,0xA7,0x59,0xAF,0xF6,0x26,0x8B,0x31); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoBufferAllocator,0x3481A4DF,0x2B11,0x4E55,0xAC,0x61,0x83,0x6B,0x87,0x98,0x5E,0x9A); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoBufferAllocatorProvider,0x08B80403,0xBFF2,0x49D0,0xB4,0x48,0x8C,0x90,0x8B,0x9E,0x9F,0xC9); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAudioOutputCallback,0x403C681B,0x7F46,0x4A12,0xB9,0x93,0x2B,0xB1,0x27,0x08,0x4E,0xE6); MIDL_DEFINE_GUID(IID, IID_IDeckLinkIterator,0x50FB36CD,0x3063,0x4B73,0xBD,0xBB,0x95,0x80,0x87,0xF2,0xD8,0xBA); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAPIInformation,0x7BEA3C68,0x730D,0x4322,0xAF,0x34,0x8A,0x71,0x52,0xB5,0x32,0xA4); MIDL_DEFINE_GUID(IID, IID_IDeckLinkIPFlowAttributes,0xCDA938DA,0x6479,0x40C6,0xB2,0xEC,0xA3,0x57,0x9B,0x3A,0xEE,0xCD); MIDL_DEFINE_GUID(IID, IID_IDeckLinkIPFlowStatus,0x31C41656,0x4992,0x4396,0xBB,0xE9,0x5F,0x84,0x06,0xAA,0xB5,0xAF); MIDL_DEFINE_GUID(IID, IID_IDeckLinkIPFlowSetting,0x86DD9174,0x27D3,0x4032,0xB2,0xAD,0x60,0x67,0xC3,0xBB,0x24,0x24); MIDL_DEFINE_GUID(IID, IID_IDeckLinkIPFlow,0xC5FC83C7,0x5B8E,0x42A7,0x9A,0x40,0x7C,0x06,0x59,0x55,0xD4,0xE1); MIDL_DEFINE_GUID(IID, IID_IDeckLinkIPFlowIterator,0xBD296AB2,0xA5C5,0x4153,0x88,0x8F,0xAA,0xB1,0xFD,0xBD,0x8A,0x5C); MIDL_DEFINE_GUID(IID, IID_IDeckLinkOutput,0x1A8077F1,0x9FE2,0x4533,0x81,0x47,0x22,0x94,0x30,0x5E,0x25,0x3F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput,0x4095DB82,0xE294,0x4B8C,0xAA,0xA8,0x3B,0x9E,0x80,0xC4,0x93,0x36); MIDL_DEFINE_GUID(IID, IID_IDeckLinkIPExtensions,0x46CF7903,0xA9FD,0x4D0B,0x8F,0xFC,0x01,0x03,0x72,0x2A,0xB4,0x42); MIDL_DEFINE_GUID(IID, IID_IDeckLinkHDMIInputEDID,0xABBBACBC,0x45BC,0x4665,0x9D,0x92,0xAC,0xE6,0xE5,0xA9,0x79,0x02); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderInput,0x46C1332E,0x6FD9,0x472A,0x85,0x91,0xFE,0x59,0xC2,0x21,0x92,0xE1); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoBuffer,0xCCB4B64A,0x5C86,0x4E02,0xB7,0x78,0x88,0x5D,0x35,0x27,0x09,0xFE); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrame,0x6502091C,0x615F,0x4F51,0xBA,0xF6,0x45,0xC4,0x25,0x6D,0xD5,0xB0); MIDL_DEFINE_GUID(IID, IID_IDeckLinkMutableVideoFrame,0xCF9EB134,0x0374,0x4C5B,0x95,0xFA,0x1E,0xC1,0x48,0x19,0xFF,0x62); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrame3DExtensions,0xD4DBE9C6,0xB4D2,0x49D3,0xAB,0xF2,0xB4,0xE8,0x6C,0x73,0x91,0xB0); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrameMetadataExtensions,0xE232A5B7,0x4DB4,0x44C9,0x91,0x52,0xF4,0x7C,0x12,0xE5,0xF0,0x51); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrameMutableMetadataExtensions,0xCC198FC6,0x8298,0x4419,0x94,0x2D,0x83,0x57,0xEC,0x35,0x5E,0x58); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoInputFrame,0xC9ADD3D2,0xBE52,0x488D,0xAB,0x2D,0x7F,0xDE,0xF7,0xAF,0x0C,0x95); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAncillaryPacket,0xCC5BBF7E,0x029C,0x4D3B,0x91,0x58,0x60,0x00,0xEF,0x5E,0x36,0x70); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAncillaryPacketIterator,0x3FC8994B,0x88FB,0x4C17,0x96,0x8F,0x9A,0xAB,0x69,0xD9,0x64,0xA7); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrameAncillaryPackets,0x6C186C0F,0x459E,0x41D8,0xAE,0xE2,0x48,0x12,0xD8,0x1A,0xEE,0x68); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrameAncillary,0x732E723C,0xD1A4,0x4E29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderPacket,0xB693F36C,0x316E,0x4AF1,0xB6,0xC2,0xF3,0x89,0xA4,0xBC,0xA6,0x20); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderVideoPacket,0x4E7FD944,0xE8C7,0x4EAC,0xB8,0xC0,0x7B,0x77,0xF8,0x0F,0x5A,0xE0); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderAudioPacket,0x49E8EDC8,0x693B,0x4E14,0x8E,0xF6,0x12,0xC6,0x58,0xF5,0xA0,0x7A); MIDL_DEFINE_GUID(IID, IID_IDeckLinkH265NALPacket,0x639C8E0B,0x68D5,0x4BDE,0xA6,0xD4,0x95,0xF3,0xAE,0xAF,0xF2,0xE7); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAudioInputPacket,0xE43D5870,0x2894,0x11DE,0x8C,0x30,0x08,0x00,0x20,0x0C,0x9A,0x66); MIDL_DEFINE_GUID(IID, IID_IDeckLinkScreenPreviewCallback,0xD4FA2345,0x9FBA,0x4497,0x95,0xC3,0xC0,0xC3,0xCE,0xD3,0xCD,0xA8); MIDL_DEFINE_GUID(IID, IID_IDeckLinkGLScreenPreviewHelper,0xCEB778E2,0xC202,0x4EC8,0x90,0x85,0x0C,0xD2,0x85,0xCC,0x55,0x22); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDX9ScreenPreviewHelper,0xF2DD78CA,0x2921,0x4AC2,0xB5,0xBC,0xBF,0xDC,0xC2,0x03,0x5A,0x1F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkWPFDX9ScreenPreviewHelper,0xC59346CD,0x9326,0x4266,0xAC,0x2D,0x5C,0x19,0x0F,0x57,0x99,0xEE); MIDL_DEFINE_GUID(IID, IID_IDeckLinkNotificationCallback,0xb002a1ec,0x070d,0x4288,0x82,0x89,0xbd,0x5d,0x36,0xe5,0xff,0x0d); MIDL_DEFINE_GUID(IID, IID_IDeckLinkNotification,0xb85df4c8,0xbdf5,0x47c1,0x80,0x64,0x28,0x16,0x2e,0xbd,0xd4,0xeb); MIDL_DEFINE_GUID(IID, IID_IDeckLinkProfileAttributes,0x17D4BF8E,0x4911,0x473A,0x80,0xA0,0x73,0x1C,0xF6,0xFF,0x34,0x5B); MIDL_DEFINE_GUID(IID, IID_IDeckLinkProfileIterator,0x29E5A8C0,0x8BE4,0x46EB,0x93,0xAC,0x31,0xDA,0xAB,0x5B,0x7B,0xF2); MIDL_DEFINE_GUID(IID, IID_IDeckLinkProfile,0x16093466,0x674A,0x432B,0x9D,0xA0,0x1A,0xC2,0xC5,0xA8,0x24,0x1C); MIDL_DEFINE_GUID(IID, IID_IDeckLinkProfileCallback,0xA4F9341E,0x97AA,0x4E04,0x89,0x35,0x15,0xF8,0x09,0x89,0x8C,0xEA); MIDL_DEFINE_GUID(IID, IID_IDeckLinkProfileManager,0x30D41429,0x3998,0x4B6D,0x84,0xF8,0x78,0xC9,0x4A,0x79,0x7C,0x6E); MIDL_DEFINE_GUID(IID, IID_IDeckLinkStatus,0x5F558200,0x4028,0x49BC,0xBE,0xAC,0xDB,0x3F,0xA4,0xA9,0x6E,0x46); MIDL_DEFINE_GUID(IID, IID_IDeckLinkKeyer,0x89AFCAF5,0x65F8,0x421E,0x98,0xF7,0x96,0xFE,0x5F,0x5B,0xFB,0xA3); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoConversion,0xA48755D9,0x8BD5,0x4727,0xA1,0xE9,0x06,0x9F,0xDE,0xDB,0xA6,0xE9); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDeviceNotificationCallback,0x4997053B,0x0ADF,0x4CC8,0xAC,0x70,0x7A,0x50,0xC4,0xBE,0x72,0x8F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDiscovery,0xCDBF631C,0xBC76,0x45FA,0xB4,0x4D,0xC5,0x50,0x59,0xBC,0x61,0x01); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkIterator,0xBA6C6F44,0x6DA5,0x4DCE,0x94,0xAA,0xEE,0x2D,0x13,0x72,0xA6,0x76); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkAPIInformation,0x263CA19F,0xED09,0x482E,0x9F,0x9D,0x84,0x00,0x57,0x83,0xA2,0x37); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkGLScreenPreviewHelper,0x1E332DAE,0x0D04,0x49EB,0xB8,0xA1,0xB6,0xE0,0x0B,0x2B,0x6B,0xD0); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkGL3ScreenPreviewHelper,0x166804E4,0x15EF,0x4BFD,0xB6,0x23,0xB5,0xBA,0x92,0x16,0x67,0xC5); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkDX9ScreenPreviewHelper,0x0EB111ED,0xADA6,0x43A6,0x8B,0x16,0xCA,0x5D,0x27,0xEE,0xA1,0x5E); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkWPFDX9ScreenPreviewHelper,0x5E64496D,0x4BB2,0x45D5,0x9B,0x63,0xBF,0x1B,0x46,0x3B,0x18,0xAF); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkVideoConversion,0x89BA47BD,0x1FE2,0x4D76,0x9B,0xFE,0xDE,0x85,0x04,0x9C,0x49,0x87); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkDiscovery,0x22FBFC33,0x8D07,0x495C,0xA5,0xBF,0xDA,0xB5,0xEA,0x9B,0x82,0xDB); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkVideoFrameAncillaryPackets,0xF891AD29,0xD0C2,0x46E9,0xA9,0x26,0x4E,0x2D,0x0D,0xD8,0xCF,0xAD); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoOutputCallback_v14_2_1,0x20AA5225,0x1958,0x47CB,0x82,0x0B,0x80,0xA8,0xD5,0x21,0xA6,0xEE); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInputCallback_v14_2_1,0xC6FCE4C9,0xC4E4,0x4047,0x82,0xFB,0x5D,0x23,0x82,0x32,0xA9,0x02); MIDL_DEFINE_GUID(IID, IID_IDeckLinkMemoryAllocator_v14_2_1,0xB36EB6E7,0x9D29,0x4AA8,0x92,0xEF,0x84,0x3B,0x87,0xA2,0x89,0xE8); MIDL_DEFINE_GUID(IID, IID_IDeckLinkOutput_v14_2_1,0xBE2D9020,0x461E,0x442F,0x84,0xB7,0xE9,0x49,0xCB,0x95,0x3B,0x9D); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput_v14_2_1,0xC21CDB6E,0xF414,0x46E4,0xA6,0x36,0x80,0xA5,0x66,0xE0,0xED,0x37); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderInput_v14_2_1,0xF222551D,0x13DF,0x4FD8,0xB5,0x87,0x9D,0x4F,0x19,0xEC,0x12,0xC9); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrame_v14_2_1,0x3F716FE0,0xF023,0x4111,0xBE,0x5D,0xEF,0x44,0x14,0xC0,0x5B,0x17); MIDL_DEFINE_GUID(IID, IID_IDeckLinkMutableVideoFrame_v14_2_1,0x69E2639F,0x40DA,0x4E19,0xB6,0xF2,0x20,0xAC,0xE8,0x15,0xC3,0x90); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrame3DExtensions_v14_2_1,0xDA0F7E4A,0xEDC7,0x48A8,0x9C,0xDD,0x2D,0xB5,0x1C,0x72,0x9C,0xD7); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoInputFrame_v14_2_1,0x05CFE374,0x537C,0x4094,0x9A,0x57,0x68,0x05,0x25,0x11,0x8F,0x44); MIDL_DEFINE_GUID(IID, IID_IDeckLinkScreenPreviewCallback_v14_2_1,0xB1D3F49A,0x85FE,0x4C5D,0x95,0xC8,0x0B,0x5D,0x5D,0xCC,0xD4,0x38); MIDL_DEFINE_GUID(IID, IID_IDeckLinkGLScreenPreviewHelper_v14_2_1,0x504E2209,0xCAC7,0x4C1A,0x9F,0xB4,0xC5,0xBB,0x62,0x74,0xD2,0x2F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDX9ScreenPreviewHelper_v14_2_1,0x2094B522,0xD1A1,0x40C0,0x9A,0xC7,0x1C,0x01,0x22,0x18,0xEF,0x02); MIDL_DEFINE_GUID(IID, IID_IDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1,0xAD8EC84A,0x7DDE,0x11E9,0x8F,0x9E,0x2A,0x86,0xE4,0x08,0x5A,0x59); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoConversion_v14_2_1,0x3BBCB8A2,0xDA2C,0x42D9,0xB5,0xD8,0x88,0x08,0x36,0x44,0xE9,0x9A); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkGLScreenPreviewHelper_v14_2_1,0xF63E77C7,0xB655,0x4A4A,0x9A,0xD0,0x3C,0xA8,0x5D,0x39,0x43,0x43); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkGL3ScreenPreviewHelper_v14_2_1,0x00696A71,0xEBC7,0x491F,0xAC,0x02,0x18,0xD3,0x39,0x3F,0x33,0xF0); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkDX9ScreenPreviewHelper_v14_2_1,0xCC010023,0xE01D,0x4525,0x9D,0x59,0x80,0xC8,0xAB,0x3D,0xC7,0xA0); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkWPFDX9ScreenPreviewHelper_v14_2_1,0xEF2A8478,0x7DDF,0x11E9,0x8F,0x9E,0x2A,0x86,0xE4,0x08,0x5A,0x59); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkVideoConversion_v14_2_1,0x7DBBBB11,0x5B7B,0x467D,0xAE,0xA4,0xCE,0xA4,0x68,0xFD,0x36,0x8C); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInputCallback_v11_5_1,0xDD04E5EC,0x7415,0x42AB,0xAE,0x4A,0xE8,0x0C,0x4D,0xFC,0x04,0x4A); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput_v11_5_1,0x9434C6E4,0xB15D,0x4B1C,0x97,0x9E,0x66,0x1E,0x3D,0xDC,0xB4,0xB9); MIDL_DEFINE_GUID(IID, IID_IDeckLinkConfiguration_v10_11,0xEF90380B,0x4AE5,0x4346,0x90,0x77,0xE2,0x88,0xE1,0x49,0xF1,0x29); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAttributes_v10_11,0xABC11843,0xD966,0x44CB,0x96,0xE2,0xA1,0xCB,0x5D,0x31,0x35,0xC4); MIDL_DEFINE_GUID(IID, IID_IDeckLinkNotification_v10_11,0x0A1FB207,0xE215,0x441B,0x9B,0x19,0x6F,0xA1,0x57,0x59,0x46,0xC5); MIDL_DEFINE_GUID(IID, IID_IDeckLinkOutput_v10_11,0xCC5C8A6E,0x3F2F,0x4B3A,0x87,0xEA,0xFD,0x78,0xAF,0x30,0x05,0x64); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput_v10_11,0xAF22762B,0xDFAC,0x4846,0xAA,0x79,0xFA,0x88,0x83,0x56,0x09,0x95); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderInput_v10_11,0x270587DA,0x6B7D,0x42E7,0xA1,0xF0,0x6D,0x85,0x3F,0x58,0x11,0x85); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkIterator_v10_11,0x87D2693F,0x8D4A,0x45C7,0xB4,0x3F,0x10,0xAC,0xBA,0x25,0xE6,0x8F); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkDiscovery_v10_11,0x652615D4,0x26CD,0x4514,0xB1,0x61,0x2F,0xD5,0x07,0x2E,0xD0,0x08); MIDL_DEFINE_GUID(IID, IID_IDeckLinkConfiguration_v10_9,0xCB71734A,0xFE37,0x4E8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2); MIDL_DEFINE_GUID(CLSID, CLSID_CBMDStreamingDiscovery_v10_8,0x0CAA31F6,0x8A26,0x40B0,0x86,0xA4,0xBF,0x58,0xDC,0xCA,0x71,0x0C); MIDL_DEFINE_GUID(IID, IID_IDeckLinkConfiguration_v10_4,0x1E69FCF6,0x4203,0x4936,0x80,0x76,0x2A,0x9F,0x4C,0xFD,0x50,0xCB); MIDL_DEFINE_GUID(IID, IID_IDeckLinkConfiguration_v10_2,0xC679A35B,0x610C,0x4D09,0xB7,0x48,0x1D,0x04,0x78,0x10,0x0F,0xC0); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrameMetadataExtensions_v11_5,0xD5973DC9,0x6432,0x46D0,0x8F,0x0B,0x24,0x96,0xF8,0xA1,0x23,0x8F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkOutput_v11_4,0x065A0F6C,0xC508,0x4D0D,0xB9,0x19,0xF5,0xEB,0x0E,0xBF,0xC9,0x6B); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput_v11_4,0x2A88CF76,0xF494,0x4216,0xA7,0xEF,0xDC,0x74,0xEE,0xB8,0x38,0x82); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkIterator_v10_8,0x1F2E109A,0x8F4F,0x49E4,0x92,0x03,0x13,0x55,0x95,0xCB,0x6F,0xA5); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkDiscovery_v10_8,0x1073A05C,0xD885,0x47E9,0xB3,0xC6,0x12,0x9B,0x3F,0x9F,0x64,0x8B); MIDL_DEFINE_GUID(IID, IID_IDeckLinkEncoderConfiguration_v10_5,0x67455668,0x0848,0x45DF,0x8D,0x8E,0x35,0x0A,0x77,0xC9,0xA0,0x28); #undef MIDL_DEFINE_GUID #ifdef __cplusplus } #endif mlt-7.38.0/src/modules/frei0r/000775 000000 000000 00000000000 15172202314 016010 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/frei0r/CMakeLists.txt000664 000000 000000 00000002342 15172202314 020551 0ustar00rootroot000000 000000 add_library(mltfrei0r MODULE factory.c filter_cairoblend_mode.c filter_frei0r.c frei0r_helper.c producer_frei0r.c transition_frei0r.c ) file(GLOB YML "*.yml") add_custom_target(Other_frei0r_Files SOURCES ${YML} aliases.yaml alpha_only.txt blacklist.txt filter_cairoblend_mode.yml not_thread_safe.txt param_name_map.yaml resolution_scale.yml ) include(GenerateExportHeader) generate_export_header(mltfrei0r) target_compile_options(mltfrei0r PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltfrei0r PRIVATE ${FREI0R_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) if(RELOCATABLE) target_compile_definitions(mltfrei0r PRIVATE RELOCATABLE) endif() target_link_libraries(mltfrei0r PRIVATE mlt ${CMAKE_DL_LIBS}) if(MSVC) target_link_libraries(mltfrei0r PRIVATE dirent) else() target_link_libraries(mltfrei0r PRIVATE m) endif() set_target_properties(mltfrei0r PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltfrei0r LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES aliases.yaml alpha_only.txt blacklist.txt filter_cairoblend_mode.yml not_thread_safe.txt param_name_map.yaml resolution_scale.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/frei0r ) mlt-7.38.0/src/modules/frei0r/aliases.yaml000664 000000 000000 00000000573 15172202314 020322 0ustar00rootroot000000 000000 # MLT frei0r plugin name mapping from new name to old ones # new: # - old frei0r.measure_pr0be: - frei0r.pr0be frei0r.measure_pr0file: - frei0r.pr0file frei0r.tehroxx0r: - frei0r.tehRoxx0r frei0r.alpha0ps_alpha0ps: - frei0r.alpha0ps frei0r.alpha0ps_alphagrad: - frei0r.alphagrad frei0r.alpha0ps_alphaspot: - frei0r.alphaspot frei0r.denoise_hqdn3d: - frei0r.hqdn3d mlt-7.38.0/src/modules/frei0r/alpha_only.txt000664 000000 000000 00000000100 15172202314 020666 0ustar00rootroot000000 000000 alpha0ps alphagrad alphaspot bluescreen0r select0r transparency mlt-7.38.0/src/modules/frei0r/blacklist.txt000664 000000 000000 00000000014 15172202314 020514 0ustar00rootroot000000 000000 perspective mlt-7.38.0/src/modules/frei0r/factory.c000664 000000 000000 00000067404 15172202314 017636 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "mltfrei0r_export.h" #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #define LIBSUF ".dll" #define FREI0R_PLUGIN_PATH "\\lib\\frei0r-1" #else #define LIBSUF ".so" #ifdef RELOCATABLE #ifdef __APPLE__ #define FREI0R_PLUGIN_PATH "/PlugIns/frei0r-1" #else #define FREI0R_PLUGIN_PATH "/lib/frei0r-1" #endif #else #define FREI0R_PLUGIN_PATH \ "/usr/lib/frei0r-1:/usr/lib64/frei0r-1:/opt/local/lib/frei0r-1:/usr/local/lib/frei0r-1:$HOME/" \ ".frei0r-1/lib" #endif #endif extern mlt_filter filter_frei0r_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_cairoblend_mode_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_frame filter_process(mlt_filter filter, mlt_frame frame); extern void filter_close(mlt_filter filter); extern int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index); extern void producer_close(mlt_producer producer); extern void transition_close(mlt_transition transition); extern mlt_frame transition_process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame); static char *get_frei0r_path() { #if defined(_WIN32) || defined(RELOCATABLE) char *dirname = malloc(strlen(mlt_environment("MLT_APPDIR")) + strlen(FREI0R_PLUGIN_PATH) + 1); strcpy(dirname, mlt_environment("MLT_APPDIR")); strcat(dirname, FREI0R_PLUGIN_PATH); return dirname; #else return strdup(getenv("FREI0R_PATH") ? getenv("FREI0R_PATH") : getenv("MLT_FREI0R_PLUGIN_PATH") ? getenv("MLT_FREI0R_PLUGIN_PATH") : FREI0R_PLUGIN_PATH); #endif } static void check_thread_safe(mlt_properties properties, const char *name) { char dirname[PATH_MAX]; snprintf(dirname, PATH_MAX, "%s/frei0r/not_thread_safe.txt", mlt_environment("MLT_DATA")); mlt_properties not_thread_safe = mlt_properties_load(dirname); double version = mlt_properties_get_double(properties, "version"); int i; for (i = 0; i < mlt_properties_count(not_thread_safe); i++) { if (!strcmp(name, mlt_properties_get_name(not_thread_safe, i))) { double thread_safe_version = mlt_properties_get_double(not_thread_safe, name); if (thread_safe_version == 0.0 || version < thread_safe_version) mlt_properties_set_int(properties, "_not_thread_safe", 1); break; } } mlt_properties_close(not_thread_safe); } static mlt_properties fill_param_info(mlt_service_type type, const char *service_name, char *name) { char file[PATH_MAX]; char servicetype[1024] = ""; struct stat stat_buff; switch (type) { case mlt_service_producer_type: strcpy(servicetype, "producer"); break; case mlt_service_filter_type: strcpy(servicetype, "filter"); break; case mlt_service_transition_type: strcpy(servicetype, "transition"); break; default: strcpy(servicetype, ""); } int n = snprintf(file, PATH_MAX, "%s/frei0r/%s_%s.yml", mlt_environment("MLT_DATA"), servicetype, service_name); if (n < 0 || n >= sizeof(file)) return NULL; memset(&stat_buff, 0, sizeof(stat_buff)); mlt_stat(file, &stat_buff); if (S_ISREG(stat_buff.st_mode)) { return mlt_properties_parse_yaml(file); } void *handle = dlopen(name, RTLD_LAZY); if (!handle) return NULL; void (*plginfo)(f0r_plugin_info_t *) = dlsym(handle, "f0r_get_plugin_info"); void (*param_info)(f0r_param_info_t *, int param_index) = dlsym(handle, "f0r_get_param_info"); void (*f0r_init)(void) = dlsym(handle, "f0r_init"); void (*f0r_deinit)(void) = dlsym(handle, "f0r_deinit"); f0r_instance_t (*f0r_construct)(unsigned int, unsigned int) = dlsym(handle, "f0r_construct"); void (*f0r_destruct)(f0r_instance_t) = dlsym(handle, "f0r_destruct"); void (*f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index) = dlsym(handle, "f0r_get_param_value"); if (!plginfo || !param_info) { dlclose(handle); return NULL; } mlt_properties metadata = mlt_properties_new(); f0r_plugin_info_t info; char string[48]; int j = 0; f0r_init(); f0r_instance_t instance = f0r_construct(720, 576); if (!instance) { f0r_deinit(); dlclose(handle); mlt_properties_close(metadata); return NULL; } plginfo(&info); snprintf(string, sizeof(string), "%d", info.minor_version); mlt_properties_set(metadata, "schema_version", "7.0"); mlt_properties_set(metadata, "title", info.name); mlt_properties_set_double(metadata, "version", info.major_version + info.minor_version / pow(10, strlen(string))); mlt_properties_set(metadata, "identifier", service_name); mlt_properties_set(metadata, "description", info.explanation); mlt_properties_set(metadata, "creator", info.author); mlt_properties_set(metadata, "language", "en"); switch (type) { case mlt_service_producer_type: mlt_properties_set(metadata, "type", "producer"); break; case mlt_service_filter_type: mlt_properties_set(metadata, "type", "filter"); break; case mlt_service_transition_type: mlt_properties_set(metadata, "type", "transition"); break; default: break; } mlt_properties tags = mlt_properties_new(); mlt_properties_set_data(metadata, "tags", tags, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(tags, "0", "Video"); mlt_properties parameter = mlt_properties_new(); mlt_properties_set_data(metadata, "parameters", parameter, 0, (mlt_destructor) mlt_properties_close, NULL); for (j = 0; j < info.num_params; j++) { snprintf(string, sizeof(string), "%d", j); mlt_properties pnum = mlt_properties_new(); mlt_properties_set_data(parameter, string, pnum, 0, (mlt_destructor) mlt_properties_close, NULL); f0r_param_info_t paraminfo = {NULL, 0, NULL}; param_info(¶minfo, j); mlt_properties_set(pnum, "identifier", string); mlt_properties_set(pnum, "title", paraminfo.name); mlt_properties_set(pnum, "description", paraminfo.explanation); if (paraminfo.type == F0R_PARAM_DOUBLE) { double deflt = 0; mlt_properties_set(pnum, "type", "float"); mlt_properties_set(pnum, "minimum", "0"); mlt_properties_set(pnum, "maximum", "1"); f0r_get_param_value(instance, &deflt, j); mlt_properties_set_double(pnum, "default", CLAMP(deflt, 0.0, 1.0)); mlt_properties_set(pnum, "mutable", "yes"); mlt_properties_set(pnum, "animation", "yes"); mlt_properties_set(pnum, "widget", "spinner"); } else if (paraminfo.type == F0R_PARAM_BOOL) { double deflt = 0; mlt_properties_set(pnum, "type", "boolean"); mlt_properties_set(pnum, "minimum", "0"); mlt_properties_set(pnum, "maximum", "1"); f0r_get_param_value(instance, &deflt, j); mlt_properties_set_int(pnum, "default", deflt != 0.0); mlt_properties_set(pnum, "mutable", "yes"); mlt_properties_set(pnum, "animation", "yes"); mlt_properties_set(pnum, "widget", "checkbox"); } else if (paraminfo.type == F0R_PARAM_COLOR) { char colorstr[8]; f0r_param_color_t deflt = {0, 0, 0}; mlt_properties_set(pnum, "type", "color"); f0r_get_param_value(instance, &deflt, j); sprintf(colorstr, "#%02x%02x%02x", (unsigned) CLAMP(deflt.r * 255, 0, 255), (unsigned) CLAMP(deflt.g * 255, 0, 255), (unsigned) CLAMP(deflt.b * 255, 0, 255)); colorstr[7] = 0; mlt_properties_set(pnum, "default", colorstr); mlt_properties_set(pnum, "mutable", "yes"); mlt_properties_set(pnum, "animation", "yes"); mlt_properties_set(pnum, "widget", "color"); } else if (paraminfo.type == F0R_PARAM_STRING) { char *deflt = NULL; mlt_properties_set(pnum, "type", "string"); f0r_get_param_value(instance, &deflt, j); mlt_properties_set(pnum, "default", deflt); mlt_properties_set(pnum, "mutable", "yes"); mlt_properties_set(pnum, "widget", "text"); } } f0r_destruct(instance); f0r_deinit(); dlclose(handle); return metadata; } static void *load_lib(mlt_profile profile, mlt_service_type type, void *handle, const char *name) { int i = 0; void (*f0r_get_plugin_info)(f0r_plugin_info_t *), *f0r_construct, *f0r_update, *f0r_destruct, (*f0r_get_param_info)(f0r_param_info_t * info, int param_index), (*f0r_init)(void), *f0r_deinit, (*f0r_set_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index), (*f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index), (*f0r_update2)(f0r_instance_t instance, double time, const uint32_t *inframe1, const uint32_t *inframe2, const uint32_t *inframe3, uint32_t *outframe); if ((f0r_construct = dlsym(handle, "f0r_construct")) && (f0r_destruct = dlsym(handle, "f0r_destruct")) && (f0r_get_plugin_info = dlsym(handle, "f0r_get_plugin_info")) && (f0r_get_param_info = dlsym(handle, "f0r_get_param_info")) && (f0r_set_param_value = dlsym(handle, "f0r_set_param_value")) && (f0r_get_param_value = dlsym(handle, "f0r_get_param_value")) && (f0r_init = dlsym(handle, "f0r_init")) && (f0r_deinit = dlsym(handle, "f0r_deinit"))) { f0r_update = dlsym(handle, "f0r_update"); f0r_update2 = dlsym(handle, "f0r_update2"); f0r_plugin_info_t info; f0r_get_plugin_info(&info); void *ret = NULL; mlt_properties properties = NULL; char minor[12]; if (type == mlt_service_producer_type && info.plugin_type == F0R_PLUGIN_TYPE_SOURCE) { mlt_producer producer = mlt_producer_new(profile); if (producer) { producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; f0r_init(); properties = MLT_PRODUCER_PROPERTIES(producer); for (i = 0; i < info.num_params; i++) { f0r_param_info_t pinfo; f0r_get_param_info(&pinfo, i); } ret = producer; } } else if (type == mlt_service_filter_type && info.plugin_type == F0R_PLUGIN_TYPE_FILTER) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; filter->close = filter_close; f0r_init(); properties = MLT_FILTER_PROPERTIES(filter); for (i = 0; i < info.num_params; i++) { f0r_param_info_t pinfo; f0r_get_param_info(&pinfo, i); } ret = filter; } } else if (type == mlt_service_transition_type && info.plugin_type == F0R_PLUGIN_TYPE_MIXER2) { mlt_transition transition = mlt_transition_new(); if (transition) { transition->process = transition_process; transition->close = transition_close; f0r_init(); properties = MLT_TRANSITION_PROPERTIES(transition); mlt_properties_set_int(properties, "_transition_type", 1); ret = transition; } } mlt_properties_set_data(properties, "_dlclose_handle", handle, sizeof(handle), NULL, NULL); mlt_properties_set_data(properties, "_dlclose", dlclose, sizeof(void *), NULL, NULL); mlt_properties_set_data(properties, "f0r_construct", f0r_construct, sizeof(f0r_construct), NULL, NULL); mlt_properties_set_data(properties, "f0r_update", f0r_update, sizeof(f0r_update), NULL, NULL); if (f0r_update2) mlt_properties_set_data(properties, "f0r_update2", f0r_update2, sizeof(f0r_update2), NULL, NULL); mlt_properties_set_data(properties, "f0r_destruct", f0r_destruct, sizeof(f0r_destruct), NULL, NULL); mlt_properties_set_data(properties, "f0r_get_plugin_info", f0r_get_plugin_info, sizeof(void *), NULL, NULL); mlt_properties_set_data(properties, "f0r_get_param_info", f0r_get_param_info, sizeof(void *), NULL, NULL); mlt_properties_set_data(properties, "f0r_set_param_value", f0r_set_param_value, sizeof(void *), NULL, NULL); mlt_properties_set_data(properties, "f0r_get_param_value", f0r_get_param_value, sizeof(void *), NULL, NULL); // Let frei0r plugin version be serialized using same format as metadata snprintf(minor, sizeof(minor), "%d", info.minor_version); mlt_properties_set_double(properties, "version", info.major_version + info.minor_version / pow(10, strlen(minor))); check_thread_safe(properties, name); // Use the global param name map for backwards compatibility when // param names change and setting frei0r params by name instead of index. mlt_properties param_name_map = mlt_properties_get_data(mlt_global_properties(), "frei0r.param_name_map", NULL); if (param_name_map) { // Lookup my plugin in the map param_name_map = mlt_properties_get_data(param_name_map, name, NULL); mlt_properties_set_data(properties, "_param_name_map", param_name_map, 0, NULL, NULL); } param_name_map = mlt_properties_get_data(mlt_global_properties(), "frei0r.resolution_scale", NULL); if (param_name_map) { // Lookup my plugin in the map param_name_map = mlt_properties_get_data(param_name_map, name, NULL); mlt_properties_set_data(properties, "_resolution_scale", param_name_map, 0, NULL, NULL); } param_name_map = mlt_properties_get_data(mlt_global_properties(), "frei0r.alpha_only", NULL); if (param_name_map && mlt_properties_exists(param_name_map, name)) { mlt_properties_set_int(properties, "_alpha_only", 1); } return ret; } else { mlt_log_error(NULL, "frei0r plugin \"%s\" is missing a function\n", name); dlerror(); } return NULL; } static void *create_frei0r_item(mlt_profile profile, mlt_service_type type, const char *id, void *arg) { mlt_tokeniser tokeniser = mlt_tokeniser_init(); char *frei0r_path = get_frei0r_path(); int dircount = mlt_tokeniser_parse_new(tokeniser, frei0r_path, MLT_DIRLIST_DELIMITER); void *ret = NULL; while (dircount-- && !ret) { char soname[PATH_MAX]; char *myid = strdup(id); #ifdef _WIN32 char *firstname = strtok(myid, "."); #else char *save_firstptr = NULL; char *firstname = strtok_r(myid, ".", &save_firstptr); #endif char *directory = mlt_tokeniser_get_string(tokeniser, dircount); #ifdef _WIN32 firstname = strtok(NULL, "."); #else firstname = strtok_r(NULL, ".", &save_firstptr); #endif if (strncmp(directory, "$HOME", 5)) snprintf(soname, PATH_MAX, "%s/%s" LIBSUF, directory, firstname); else snprintf(soname, PATH_MAX, "%s%s/%s" LIBSUF, getenv("HOME"), strchr(directory, '/'), firstname); if (firstname) { const char *alias = mlt_properties_get(mlt_properties_get_data(mlt_global_properties(), "frei0r.aliases", NULL), id); void *handle = dlopen(alias ? alias : soname, RTLD_LAZY); if (handle) { ret = load_lib(profile, type, handle, firstname); } else { dlerror(); } } free(myid); } mlt_tokeniser_close(tokeniser); free(frei0r_path); return ret; } static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/frei0r/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTFREI0R_EXPORT MLT_REPOSITORY { mlt_tokeniser tokeniser = mlt_tokeniser_init(); char *frei0r_path = get_frei0r_path(); int dircount = mlt_tokeniser_parse_new(tokeniser, frei0r_path, MLT_DIRLIST_DELIMITER); char dirname[PATH_MAX]; snprintf(dirname, PATH_MAX, "%s/frei0r/blacklist.txt", mlt_environment("MLT_DATA")); mlt_properties blacklist = mlt_properties_load(dirname); // Load a param name map into global properties for backwards compatibility when // param names change and setting frei0r params by name instead of index. snprintf(dirname, PATH_MAX, "%s/frei0r/param_name_map.yaml", mlt_environment("MLT_DATA")); mlt_properties_set_data(mlt_global_properties(), "frei0r.param_name_map", mlt_properties_parse_yaml(dirname), 0, (mlt_destructor) mlt_properties_close, NULL); // Load a list of parameters impacted by consumer scale into global properties. snprintf(dirname, PATH_MAX, "%s/frei0r/resolution_scale.yml", mlt_environment("MLT_DATA")); mlt_properties_set_data(mlt_global_properties(), "frei0r.resolution_scale", mlt_properties_parse_yaml(dirname), 0, (mlt_destructor) mlt_properties_close, NULL); // Load a list of plugin alias names. snprintf(dirname, PATH_MAX, "%s/frei0r/aliases.yaml", mlt_environment("MLT_DATA")); mlt_properties aliases = mlt_properties_parse_yaml(dirname); mlt_properties reverse_aliases = mlt_properties_new(); mlt_properties_set_data(mlt_global_properties(), "frei0r.aliases", reverse_aliases, 0, (mlt_destructor) mlt_properties_close, NULL); // Load a list of plugin alias names. snprintf(dirname, PATH_MAX, "%s/frei0r/alpha_only.txt", mlt_environment("MLT_DATA")); mlt_properties_set_data(mlt_global_properties(), "frei0r.alpha_only", mlt_properties_load(dirname), 0, (mlt_destructor) mlt_properties_close, NULL); while (dircount--) { mlt_properties direntries = mlt_properties_new(); char *directory = mlt_tokeniser_get_string(tokeniser, dircount); if (strncmp(directory, "$HOME", 5)) snprintf(dirname, PATH_MAX, "%s", directory); else snprintf(dirname, PATH_MAX, "%s%s", getenv("HOME"), strchr(directory, '/')); mlt_properties_dir_list(direntries, dirname, "*" LIBSUF, 1); for (int i = 0; i < mlt_properties_count(direntries); i++) { char *name = mlt_properties_get_value(direntries, i); char *shortname = name + strlen(dirname) + 1; #ifdef _WIN32 char *firstname = strtok(shortname, "."); #else char *save_firstptr = NULL; char *firstname = strtok_r(shortname, ".", &save_firstptr); #endif char pluginname[1024] = "frei0r."; if (firstname) strncat(pluginname, firstname, sizeof(pluginname) - strlen(pluginname) - 1); if (firstname && mlt_properties_exists(blacklist, firstname)) continue; mlt_properties plugin_aliases = mlt_properties_get_data(aliases, pluginname, NULL); void *handle = dlopen(strcat(name, LIBSUF), RTLD_LAZY); if (handle) { void (*plginfo)(f0r_plugin_info_t *) = dlsym(handle, "f0r_get_plugin_info"); if (plginfo) { f0r_plugin_info_t info; plginfo(&info); if (firstname && info.plugin_type == F0R_PLUGIN_TYPE_SOURCE) { if (mlt_properties_get(mlt_repository_producers(repository), pluginname)) { dlclose(handle); continue; } MLT_REGISTER(mlt_service_producer_type, pluginname, create_frei0r_item); MLT_REGISTER_METADATA(mlt_service_producer_type, pluginname, fill_param_info, name); for (int j = 0; j < mlt_properties_count(plugin_aliases); j++) { const char *alias = mlt_properties_get_value(plugin_aliases, j); mlt_properties_set(reverse_aliases, alias, name); MLT_REGISTER(mlt_service_producer_type, alias, create_frei0r_item); MLT_REGISTER_METADATA(mlt_service_producer_type, alias, fill_param_info, name); } } else if (firstname && info.plugin_type == F0R_PLUGIN_TYPE_FILTER) { if (mlt_properties_get(mlt_repository_filters(repository), pluginname)) { dlclose(handle); continue; } MLT_REGISTER(mlt_service_filter_type, pluginname, create_frei0r_item); MLT_REGISTER_METADATA(mlt_service_filter_type, pluginname, fill_param_info, name); for (int j = 0; j < mlt_properties_count(plugin_aliases); j++) { const char *alias = mlt_properties_get_value(plugin_aliases, j); mlt_properties_set(reverse_aliases, alias, name); MLT_REGISTER(mlt_service_filter_type, alias, create_frei0r_item); MLT_REGISTER_METADATA(mlt_service_filter_type, alias, fill_param_info, name); } } else if (firstname && info.plugin_type == F0R_PLUGIN_TYPE_MIXER2) { if (mlt_properties_get(mlt_repository_transitions(repository), pluginname)) { dlclose(handle); continue; } MLT_REGISTER(mlt_service_transition_type, pluginname, create_frei0r_item); MLT_REGISTER_METADATA(mlt_service_transition_type, pluginname, fill_param_info, name); for (int j = 0; j < mlt_properties_count(plugin_aliases); j++) { const char *alias = mlt_properties_get_value(plugin_aliases, j); mlt_properties_set(reverse_aliases, alias, name); MLT_REGISTER(mlt_service_transition_type, alias, create_frei0r_item); MLT_REGISTER_METADATA(mlt_service_transition_type, alias, fill_param_info, name); } } } dlclose(handle); } } mlt_factory_register_for_clean_up(direntries, (mlt_destructor) mlt_properties_close); } mlt_tokeniser_close(tokeniser); mlt_properties_close(blacklist); free(frei0r_path); MLT_REGISTER(mlt_service_filter_type, "cairoblend_mode", filter_cairoblend_mode_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "cairoblend_mode", metadata, "filter_cairoblend_mode.yml"); } mlt-7.38.0/src/modules/frei0r/filter_cairoblend_mode.c000664 000000 000000 00000003212 15172202314 022625 0ustar00rootroot000000 000000 /* * filter_cairoblend_mode.c * Copyright (C) 2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frei0r_helper.h" #include #include #include #include static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties_set(MLT_FRAME_PROPERTIES(frame), CAIROBLEND_MODE_PROPERTY, mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "mode")); return frame; } mlt_filter filter_cairoblend_mode_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "mode", arg ? arg : "normal"); } return filter; } mlt-7.38.0/src/modules/frei0r/filter_cairoblend_mode.yml000664 000000 000000 00000001045 15172202314 023206 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: cairoblend_mode title: Set Cairographics Blend Mode version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: Change the blend mode used by the frei0r.cairoblend transition. parameters: - identifier: mode argument: yes title: Blend mode type: string description: > This filter can be used to change the blend mode for a single clip when using the cairoblend transition to composite tracks. default: normal mlt-7.38.0/src/modules/frei0r/filter_frei0r.c000664 000000 000000 00000014775 15172202314 020726 0ustar00rootroot000000 000000 /* * filter_frei0r.c -- frei0r filter * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "frei0r_helper.h" #include typedef struct { uint8_t *rgba_image; uint16_t *rgba64_image; int width; int height; } alpha_copy_desc; static int copy_alpha_rgba_to_rgba64_slice(int id, int index, int jobs, void *data) { (void) id; // unused const alpha_copy_desc *desc = (const alpha_copy_desc *) data; int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int slice_pixels = desc->width * slice_height; const uint8_t *src = desc->rgba_image + slice_line_start * desc->width * 4; uint16_t *dst = desc->rgba64_image + slice_line_start * desc->width * 4; for (int i = 0; i < slice_pixels; i++) { // Convert 8-bit to 16-bit dst[i * 4 + 3] = ((uint16_t) src[i * 4 + 3]) * 257; // 257 = 65535 / 255 } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = mlt_frame_pop_service(frame); mlt_properties filter_props = MLT_FILTER_PROPERTIES(filter); int alpha_only = mlt_properties_get_int(filter_props, "_alpha_only"); if (alpha_only) { // Except when using alpha0ps to view the alpha channel const char *service_name = mlt_properties_get(filter_props, "mlt_service"); if (service_name && !strcmp(service_name, "frei0r.alpha0ps")) alpha_only = mlt_properties_get_double(filter_props, "0") < 0.2; } mlt_log_debug(MLT_FILTER_SERVICE(filter), "frei0r filter_get_image called for format %d (%s), alpha_only=%d\n", *format, mlt_image_format_name(*format), alpha_only); // Check if we need special rgba64 handling for alpha-only filters if (alpha_only && *format == mlt_image_rgba64) { // First get the rgba64 image int error = mlt_frame_get_image(frame, image, format, width, height, 0); // Now request the same image as rgba using convert_image if (!error && *image && *format == mlt_image_rgba64 && frame->convert_image) { // Convert to rgba mlt_frame temp_frame = mlt_frame_clone(frame, 0); uint8_t *rgba64_image = *image; error = frame->convert_image(temp_frame, image, format, mlt_image_rgba); if (!error && *image && *format == mlt_image_rgba) { // Process with frei0r using rgba image mlt_position position = mlt_filter_get_position(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double time = (double) position / mlt_profile_fps(profile); int length = mlt_filter_get_length2(filter, frame); error = process_frei0r_item(MLT_FILTER_SERVICE(filter), position, time, length, temp_frame, image, width, height); if (!error) { // Copy alpha channel from rgba to rgba64 alpha_copy_desc desc = {.rgba_image = *image, .rgba64_image = (uint16_t *) rgba64_image, .width = *width, .height = *height}; mlt_slices_run_normal(0, copy_alpha_rgba_to_rgba64_slice, &desc); } } else { mlt_log_warning(MLT_FILTER_SERVICE(filter), "alpha-only frei0r filter failed to convert frame to rgba\n"); } mlt_frame_close(temp_frame); *image = rgba64_image; *format = mlt_image_rgba64; } else { mlt_log_warning(MLT_FILTER_SERVICE(filter), "alpha-only frei0r filter failed to get rgba64 image from frame\n"); error = 1; } return error; } // Normal processing for non-rgba64 or non-alpha-only filters *format = mlt_image_rgba; mlt_log_debug(MLT_FILTER_SERVICE(filter), "frei0r %dx%d\n", *width, *height); int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0 && *image) { mlt_position position = mlt_filter_get_position(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double time = (double) position / mlt_profile_fps(profile); int length = mlt_filter_get_length2(filter, frame); process_frei0r_item(MLT_FILTER_SERVICE(filter), position, time, length, frame, image, width, height); } return error; } mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } void filter_close(mlt_filter filter) { destruct(MLT_FILTER_PROPERTIES(filter)); filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } mlt-7.38.0/src/modules/frei0r/frei0r_helper.c000664 000000 000000 00000034733 15172202314 020714 0ustar00rootroot000000 000000 /* * frei0r_helper.c -- frei0r helper * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frei0r_helper.h" #include #include #ifdef _WIN32 #include #endif const char *CAIROBLEND_MODE_PROPERTY = "frei0r.cairoblend.mode"; static void rgba_bgra(uint8_t *src, uint8_t *dst, int width, int height) { int n = width * height + 1; while (--n) { *dst++ = src[2]; *dst++ = src[1]; *dst++ = src[0]; *dst++ = src[3]; src += 4; } } struct update_context { f0r_instance_t frei0r; int width; int height; double time; uint32_t *inputs[2]; uint32_t *output; void (*f0r_update)(f0r_instance_t instance, double time, const uint32_t *inframe, uint32_t *outframe); void (*f0r_update2)(f0r_instance_t instance, double time, const uint32_t *inframe1, const uint32_t *inframe2, const uint32_t *inframe3, uint32_t *outframe); }; static int f0r_update_slice(int id, int index, int count, void *context) { struct update_context *ctx = context; int slice_height = ctx->height / count; int slice_offset = index * slice_height * ctx->width; uint32_t *input = ctx->inputs[0] + slice_offset; uint32_t *output = ctx->output + slice_offset; ctx->f0r_update(ctx->frei0r, ctx->time, input, output); return 0; } static int f0r_update2_slice(int id, int index, int count, void *context) { struct update_context *ctx = context; int slice_height = ctx->height / count; int slice_offset = index * slice_height * ctx->width; uint32_t *inputs[2] = {ctx->inputs[0] + slice_offset, ctx->inputs[1] + slice_offset}; uint32_t *output = ctx->output + slice_offset; ctx->f0r_update2(ctx->frei0r, ctx->time, inputs[0], inputs[1], NULL, output); return 0; } int process_frei0r_item(mlt_service service, mlt_position position, double time, int length, mlt_frame frame, uint8_t **image, int *width, int *height) { int i = 0; mlt_properties prop = MLT_SERVICE_PROPERTIES(service); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); f0r_instance_t (*f0r_construct)(unsigned int, unsigned int) = mlt_properties_get_data(prop, "f0r_construct", NULL); if (!f0r_construct) { return -1; } void (*f0r_update)(f0r_instance_t instance, double time, const uint32_t *inframe, uint32_t *outframe) = mlt_properties_get_data(prop, "f0r_update", NULL); void (*f0r_get_plugin_info)(f0r_plugin_info_t *) = mlt_properties_get_data(prop, "f0r_get_plugin_info", NULL); void (*f0r_get_param_info)(f0r_param_info_t * info, int param_index) = mlt_properties_get_data(prop, "f0r_get_param_info", NULL); void (*f0r_set_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index) = mlt_properties_get_data(prop, "f0r_set_param_value", NULL); void (*f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index) = mlt_properties_get_data(prop, "f0r_get_param_value", NULL); void (*f0r_update2)(f0r_instance_t instance, double time, const uint32_t *inframe1, const uint32_t *inframe2, const uint32_t *inframe3, uint32_t *outframe) = mlt_properties_get_data(prop, "f0r_update2", NULL); mlt_service_type type = mlt_service_identify(service); int not_thread_safe = mlt_properties_get_int(prop, "_not_thread_safe"); int slice_count = mlt_properties_get(prop, "threads") ? mlt_properties_get_int(prop, "threads") : -1; const char *service_name = mlt_properties_get(prop, "mlt_service"); int is_cairoblend = service_name && !strcmp("frei0r.cairoblend", service_name); double scale = mlt_profile_scale_width(mlt_service_profile(service), *width); mlt_properties scale_map = mlt_properties_get_data(prop, "_resolution_scale", NULL); // Determine the number of slices and slice height if (slice_count == 0) { slice_count = mlt_slices_count_normal(); } else { slice_count = CLAMP(slice_count, 1, mlt_slices_count_normal()); } // Reduce the slice count until the height is a multiple of slices while (slice_count > 1 && (*height % slice_count)) { --slice_count; } int slice_height = *height / slice_count; // Use width and height in the frei0r instance key char ctorname[1024] = ""; if (not_thread_safe) sprintf(ctorname, "_ctor-"); else #ifdef _WIN32 sprintf(ctorname, "_ctor-%lu", GetCurrentThreadId()); #else sprintf(ctorname, "_ctor-%p", (void *) pthread_self()); #endif mlt_service_lock(service); mlt_properties ctor = mlt_properties_get_data(prop, ctorname, NULL); if (!ctor || mlt_properties_get_int(ctor, "width") != *width || mlt_properties_get_int(ctor, "height") != slice_height) { mlt_properties_clear(prop, ctorname); ctor = mlt_properties_new(); f0r_instance_t new_inst = f0r_construct(*width, slice_height); mlt_properties_set_int(ctor, "width", *width); mlt_properties_set_int(ctor, "height", slice_height); mlt_properties_set_data(ctor, "inst", new_inst, 0, mlt_properties_get_data(prop, "f0r_destruct", NULL), NULL); mlt_properties_set_data(prop, ctorname, ctor, 0, (mlt_destructor) mlt_properties_close, NULL); } f0r_instance_t inst = mlt_properties_get_data(ctor, "inst", NULL); if (!inst) { mlt_service_unlock(service); return -1; } if (!not_thread_safe && slice_count == 1) mlt_service_unlock(service); f0r_plugin_info_t info; memset(&info, 0, sizeof(info)); if (f0r_get_plugin_info) { f0r_get_plugin_info(&info); for (i = 0; i < info.num_params; i++) { prop = MLT_SERVICE_PROPERTIES(service); f0r_param_info_t pinfo; f0r_get_param_info(&pinfo, i); char index[20]; snprintf(index, sizeof(index), "%d", i); const char *name = index; char *val = mlt_properties_get(prop, name); // Special cairoblend handling for an override from the cairoblend_mode filter. if (is_cairoblend && i == 1) { if (mlt_properties_get(frame_properties, CAIROBLEND_MODE_PROPERTY)) { name = CAIROBLEND_MODE_PROPERTY; prop = frame_properties; val = mlt_properties_get(prop, name); } else if (!val && !mlt_properties_get(frame_properties, name)) { // Reset plugin back to its default value. char *default_val = "normal"; char *plugin_val = NULL; f0r_get_param_value(inst, &plugin_val, i); if (plugin_val && strcmp(default_val, plugin_val)) { f0r_set_param_value(inst, &default_val, i); continue; } } } if (!val) { name = pinfo.name; val = mlt_properties_get(prop, name); } if (!val) { // Use the backwards-compatibility param name map. mlt_properties map = mlt_properties_get_data(prop, "_param_name_map", NULL); if (map) { int j; for (j = 0; !val && j < mlt_properties_count(map); j++) { if (!strcmp(mlt_properties_get_value(map, j), index)) { name = mlt_properties_get_name(map, j); val = mlt_properties_get(prop, name); } } } } if (val) { switch (pinfo.type) { case F0R_PARAM_DOUBLE: case F0R_PARAM_BOOL: { double t = mlt_properties_anim_get_double(prop, name, position, length); if (scale != 1.0) { double scale2 = mlt_properties_get_double(scale_map, name); if (scale2 != 0.0) t *= scale * scale2; } f0r_set_param_value(inst, &t, i); break; } case F0R_PARAM_COLOR: { f0r_param_color_t f_color; mlt_color m_color = mlt_properties_get(prop, index) ? mlt_properties_anim_get_color(prop, index, position, length) : mlt_properties_anim_get_color(prop, pinfo.name, position, length); if (type != mlt_service_producer_type) { const char *trc = mlt_properties_get(frame_properties, "color_trc"); m_color = mlt_color_convert_trc(m_color, trc); } f_color.r = (float) m_color.r / 255.0f; f_color.g = (float) m_color.g / 255.0f; f_color.b = (float) m_color.b / 255.0f; f0r_set_param_value(inst, &f_color, i); break; } case F0R_PARAM_STRING: { val = mlt_properties_anim_get(prop, name, position, length); f0r_set_param_value(inst, &val, i); break; } } } } } int video_area = *width * *height; uint32_t *result = mlt_pool_alloc(video_area * sizeof(uint32_t)); uint32_t *extra = NULL; uint32_t *source[2] = {(uint32_t *) image[0], (uint32_t *) image[1]}; uint32_t *dest = result; if (info.color_model == F0R_COLOR_MODEL_BGRA8888) { if (type == mlt_service_producer_type) { dest = source[0]; } else { rgba_bgra(image[0], (uint8_t *) result, *width, *height); source[0] = result; dest = (uint32_t *) image[0]; if (type == mlt_service_transition_type && f0r_update2) { extra = mlt_pool_alloc(video_area * sizeof(uint32_t)); rgba_bgra(image[1], (uint8_t *) extra, *width, *height); source[1] = extra; } } } if (type == mlt_service_producer_type) { if (slice_count > 1) { struct update_context ctx = {.frei0r = inst, .width = *width, .height = *height, .time = time, .inputs = {NULL, NULL}, .output = dest, .f0r_update = f0r_update}; mlt_slices_run_normal(slice_count, f0r_update_slice, &ctx); } else { f0r_update(inst, time, NULL, dest); } } else if (type == mlt_service_filter_type) { if (slice_count > 1) { struct update_context ctx = {.frei0r = inst, .width = *width, .height = *height, .time = time, .inputs = {source[0], NULL}, .output = dest, .f0r_update = f0r_update}; mlt_slices_run_normal(slice_count, f0r_update_slice, &ctx); } else { f0r_update(inst, time, source[0], dest); } } else if (type == mlt_service_transition_type && f0r_update2) { if (slice_count > 1) { struct update_context ctx = {.frei0r = inst, .width = *width, .height = *height, .time = time, .inputs = {source[0], source[1]}, .output = dest, .f0r_update2 = f0r_update2}; mlt_slices_run_normal(slice_count, f0r_update2_slice, &ctx); } else { f0r_update2(inst, time, source[0], source[1], NULL, dest); } } if (not_thread_safe || slice_count != 1) mlt_service_unlock(service); if (info.color_model == F0R_COLOR_MODEL_BGRA8888) { rgba_bgra((uint8_t *) dest, (uint8_t *) result, *width, *height); } *image = (uint8_t *) result; mlt_frame_set_image(frame, (uint8_t *) result, video_area * sizeof(uint32_t), mlt_pool_release); if (extra) mlt_pool_release(extra); return 0; } void destruct(mlt_properties prop) { void (*f0r_deinit)(void) = mlt_properties_get_data(prop, "f0r_deinit", NULL); int i = 0; if (f0r_deinit) f0r_deinit(); for (i = 0; i < mlt_properties_count(prop); i++) { if (strstr(mlt_properties_get_name(prop, i), "_ctor-")) { mlt_properties_clear(prop, mlt_properties_get_name(prop, i)); } } void (*dlclose)(void *) = mlt_properties_get_data(prop, "_dlclose", NULL); void *handle = mlt_properties_get_data(prop, "_dlclose_handle", NULL); if (handle && dlclose) dlclose(handle); } mlt-7.38.0/src/modules/frei0r/frei0r_helper.h000664 000000 000000 00000002426 15172202314 020713 0ustar00rootroot000000 000000 /* * frei0r_helper.h -- frei0r helper * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include int process_frei0r_item(mlt_service, mlt_position position, double time, int length, mlt_frame, uint8_t **image, int *width, int *height); void destruct(mlt_properties prop); extern const char *CAIROBLEND_MODE_PROPERTY; mlt-7.38.0/src/modules/frei0r/not_thread_safe.txt000664 000000 000000 00000000510 15172202314 021672 0ustar00rootroot000000 000000 # plugin name = lowest version that is thread safe or empty if not yet thread safe baltan bigsh0t_eq_mask bigsh0t_eq_to_rect bigsh0t_eq_to_stereo bigsh0t_hemi_to_eq bigsh0t_rect_to_eq bigsh0t_stabilize_360 bigsh0t_transform_360 colorhalftone delay0r delaygrab distort0r ising0r medians nervous partik0l plasma tehRoxx0r vertigo mlt-7.38.0/src/modules/frei0r/param_name_map.yaml000664 000000 000000 00000000370 15172202314 021631 0ustar00rootroot000000 000000 # MLT frei0r param name mapping from old name to current index # plugin: # param_name: index lenscorrection: xcenter: 0 ycenter: 1 correctionnearcenter: 2 correctionnearedges: 3 brightness: 4 pixeliz0r: BlockSizeX: 0 BlockSizeY: 1 mlt-7.38.0/src/modules/frei0r/producer_frei0r.c000664 000000 000000 00000007526 15172202314 021260 0ustar00rootroot000000 000000 /* * producer_frei0r.c -- frei0r producer * Copyright (c) 2009 Jean-Baptiste Mardelle * Copyright (C) 2009-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "frei0r_helper.h" #include #include static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { mlt_producer producer = mlt_frame_pop_service(frame); // Choose suitable out values if nothing specific requested if (*width <= 0) *width = mlt_service_profile(MLT_PRODUCER_SERVICE(producer))->width; if (*height <= 0) *height = mlt_service_profile(MLT_PRODUCER_SERVICE(producer))->height; // Allocate the image *format = mlt_image_rgba; int size = mlt_image_format_size(*format, *width, *height, NULL); // Allocate the image *buffer = mlt_pool_alloc(size); // Update the frame mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); if (*buffer != NULL) { mlt_position position = mlt_frame_get_position(frame); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); double time = (double) position / mlt_profile_fps(profile); int length = mlt_producer_get_playtime(producer); process_frei0r_item(MLT_PRODUCER_SERVICE(producer), position, time, length, frame, buffer, width, height); } return 0; } int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Set producer-specific frame properties mlt_properties_set_int(properties, "progressive", 1); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(properties, "meta.media.width", profile->width); mlt_properties_set_int(properties, "meta.media.height", profile->height); // Inform framework that this producer creates rgba frames by default mlt_properties_set_int(properties, "format", mlt_image_rgba); // Push the get_image method mlt_frame_push_service(*frame, producer); mlt_frame_push_get_image(*frame, producer_get_image); } // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } mlt-7.38.0/src/modules/frei0r/resolution_scale.yml000664 000000 000000 00000000426 15172202314 022107 0ustar00rootroot000000 000000 # This files contains a list of plugin parameters that are sensitive to the # image resolution. This only works for parameters with type F0R_PARAM_DOUBLE. # plugin: # parameter#: scale factor in addition to mlt_frame_resolution_scale colorhalftone: 0: 1.0 IIRblur: 0: 1.7 mlt-7.38.0/src/modules/frei0r/transition_frei0r.c000664 000000 000000 00000013102 15172202314 021612 0ustar00rootroot000000 000000 /* * transition_frei0r.c -- frei0r transition * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frei0r_helper.h" #include #include static int transition_get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_frame b_frame = mlt_frame_pop_frame(a_frame); mlt_transition transition = mlt_frame_pop_service(a_frame); mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); mlt_properties a_props = MLT_FRAME_PROPERTIES(a_frame); mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); int invert = mlt_properties_get_int(properties, "invert"); uint8_t *images[] = {NULL, NULL, NULL}; int request_width = *width; int request_height = *height; int error = 0; // Get the B-frame. *format = mlt_image_rgba; error = mlt_frame_get_image(b_frame, &images[1], format, width, height, 0); if (error) return error; if (b_frame->convert_image && (*width != request_width || *height != request_height)) { mlt_properties_set_int(b_props, "convert_image_width", request_width); mlt_properties_set_int(b_props, "convert_image_height", request_height); b_frame->convert_image(b_frame, &images[1], format, *format); *width = request_width; *height = request_height; } const char *service_name = mlt_properties_get(properties, "mlt_service"); int is_cairoblend = service_name && !strcmp("frei0r.cairoblend", service_name); const char *blend_mode = mlt_properties_get(b_props, CAIROBLEND_MODE_PROPERTY); // An optimization for cairoblend in normal (over) mode and opaque B frame. if (is_cairoblend && (!mlt_properties_get(properties, "0") || mlt_properties_get_double(properties, "0") == 1.0) && (!mlt_properties_get(properties, "1") || !strcmp("normal", mlt_properties_get(properties, "1"))) && (!blend_mode || !strcmp("normal", blend_mode)) // Check if the alpha channel is entirely opaque. && mlt_image_rgba_opaque(images[1], *width, *height)) { if (invert) { error = mlt_frame_get_image(a_frame, image, format, width, height, 0); } else { // Pass all required frame properties mlt_properties_pass_list(a_props, b_props, "progressive,distort,colorspace,full_range,force_full_luma," "top_field_first,color_trc"); *image = images[1]; } } else { error = mlt_frame_get_image(a_frame, &images[0], format, width, height, 0); if (error) return error; if (a_frame->convert_image && (*width != request_width || *height != request_height)) { mlt_properties_set_int(a_props, "convert_image_width", request_width); mlt_properties_set_int(a_props, "convert_image_height", request_height); a_frame->convert_image(a_frame, &images[0], format, *format); *width = request_width; *height = request_height; } mlt_position position = mlt_transition_get_position(transition, a_frame); mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(transition)); double time = (double) position / mlt_profile_fps(profile); int length = mlt_transition_get_length(transition); // Special cairoblend handling for an override from the cairoblend_mode filter. if (is_cairoblend) { mlt_properties_set(a_props, CAIROBLEND_MODE_PROPERTY, blend_mode); } process_frei0r_item(MLT_TRANSITION_SERVICE(transition), position, time, length, !invert ? a_frame : b_frame, images, width, height); *width = mlt_properties_get_int(!invert ? a_props : b_props, "width"); *height = mlt_properties_get_int(!invert ? a_props : b_props, "height"); *image = mlt_properties_get_data(!invert ? a_props : b_props, "image", NULL); } return error; } mlt_frame transition_process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { mlt_frame_push_service(a_frame, transition); mlt_frame_push_frame(a_frame, b_frame); mlt_frame_push_get_image(a_frame, transition_get_image); return a_frame; } void transition_close(mlt_transition transition) { destruct(MLT_TRANSITION_PROPERTIES(transition)); transition->close = NULL; mlt_transition_close(transition); } mlt-7.38.0/src/modules/gdk/000775 000000 000000 00000000000 15172202314 015366 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/gdk/CMakeLists.txt000664 000000 000000 00000003241 15172202314 020126 0ustar00rootroot000000 000000 add_library(mltgdk MODULE factory.c filter_rescale.c pixops.c producer_pixbuf.c ) file(GLOB YML "*.yml") add_custom_target(Other_gdk_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltgdk) target_compile_options(mltgdk PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltgdk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltgdk PRIVATE mlt Threads::Threads PkgConfig::GdkPixbuf) if(MSVC) target_link_libraries(mltgdk PRIVATE PThreads4W::PThreads4W) else() target_link_libraries(mltgdk PRIVATE m) endif() target_compile_definitions(mltgdk PRIVATE USE_PIXBUF) if(TARGET PkgConfig::libexif) target_link_libraries(mltgdk PRIVATE PkgConfig::libexif) target_compile_definitions(mltgdk PRIVATE USE_EXIF) endif() if(TARGET PkgConfig::pango AND TARGET PkgConfig::fontconfig) target_sources(mltgdk PRIVATE producer_pango.c) target_link_libraries(mltgdk PRIVATE PkgConfig::pango PkgConfig::fontconfig PkgConfig::pangoft2) if(APPLE OR MINGW) target_link_libraries(mltgdk PRIVATE iconv) endif() target_compile_definitions(mltgdk PRIVATE USE_PANGO) install(FILES producer_pango.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/gdk) endif() if(CPU_MMX) target_sources(mltgdk PRIVATE have_mmx.S scale_line_22_yuv_mmx.S) target_compile_definitions(mltgdk PRIVATE USE_MMX) endif() if(CPU_X86_64) target_compile_definitions(mltgdk PRIVATE ARCH_X86_64) endif() set_target_properties(mltgdk PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltgdk LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_rescale.yml producer_pixbuf.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/gdk) mlt-7.38.0/src/modules/gdk/factory.c000664 000000 000000 00000006315 15172202314 017206 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltgdk_export.h" #include #include #include #include #ifdef USE_PIXBUF extern mlt_producer producer_pixbuf_init(char *filename); extern mlt_filter filter_rescale_init(mlt_profile profile, char *arg); #endif #ifdef USE_PANGO extern mlt_producer producer_pango_init(const char *filename); #endif static void initialise() { static int init = 0; if (init == 0) { init = 1; #if !GLIB_CHECK_VERSION(2, 35, 0) g_type_init(); #endif if (getenv("MLT_PIXBUF_PRODUCER_CACHE")) { int n = atoi(getenv("MLT_PIXBUF_PRODUCER_CACHE")); mlt_service_cache_set_size(NULL, "pixbuf.image", n); mlt_service_cache_set_size(NULL, "pixbuf.alpha", n); mlt_service_cache_set_size(NULL, "pixbuf.pixbuf", n); } if (getenv("MLT_PANGO_PRODUCER_CACHE")) { int n = atoi(getenv("MLT_PANGO_PRODUCER_CACHE")); mlt_service_cache_set_size(NULL, "pango.image", n); } } } void *create_service(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { initialise(); #ifdef USE_PIXBUF if (!strcmp(id, "pixbuf")) return producer_pixbuf_init(arg); #endif #ifdef USE_PANGO if (!strcmp(id, "pango")) return producer_pango_init(arg); #endif #ifdef USE_PIXBUF if (!strcmp(id, "gtkrescale")) return filter_rescale_init(profile, arg); #endif return NULL; } static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/gdk/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTGDK_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "gtkrescale", create_service); MLT_REGISTER(mlt_service_link_type, "gtkrescale", mlt_link_filter_init); MLT_REGISTER(mlt_service_producer_type, "pango", create_service); MLT_REGISTER(mlt_service_producer_type, "pixbuf", create_service); MLT_REGISTER_METADATA(mlt_service_filter_type, "gtkrescale", metadata, "filter_rescale.yml"); MLT_REGISTER_METADATA(mlt_service_link_type, "gtkrescale", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_producer_type, "pango", metadata, "producer_pango.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "pixbuf", metadata, "producer_pixbuf.yml"); } mlt-7.38.0/src/modules/gdk/filter_rescale.c000664 000000 000000 00000012174 15172202314 020522 0ustar00rootroot000000 000000 /* * filter_rescale.c -- scale the producer video frame size to match the consumer * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pixops.h" #include #include #include #include #include #include #include static int filter_scale(mlt_frame this, uint8_t **image, mlt_image_format *format, int iwidth, int iheight, int owidth, int oheight) { // Get the properties mlt_properties properties = MLT_FRAME_PROPERTIES(this); // Get the requested interpolation method char *interps = mlt_properties_get(properties, "consumer.rescale"); // Convert to the GTK flag int interp = PIXOPS_INTERP_BILINEAR; if (strcmp(interps, "nearest") == 0) interp = PIXOPS_INTERP_NEAREST; else if (strcmp(interps, "tiles") == 0) interp = PIXOPS_INTERP_TILES; else if (strcmp(interps, "hyper") == 0 || strcmp(interps, "bicubic") == 0) interp = PIXOPS_INTERP_HYPER; int bpp; int size = mlt_image_format_size(*format, owidth, oheight, &bpp); // Carry out the rescaling switch (*format) { case mlt_image_yuv422: { // Create the output image uint8_t *output = mlt_pool_alloc(size); // Calculate strides int istride = iwidth * 2; int ostride = owidth * 2; yuv422_scale_simple(output, owidth, oheight, ostride, *image, iwidth, iheight, istride, interp); // Now update the frame mlt_frame_set_image(this, output, size, mlt_pool_release); // Return the output *image = output; break; } case mlt_image_rgb: case mlt_image_rgba: { if (strcmp(interps, "none") && (iwidth != owidth || iheight != oheight)) { // Create the output image uint8_t *output = mlt_pool_alloc(size); GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(*image, GDK_COLORSPACE_RGB, (*format == mlt_image_rgba), 8, iwidth, iheight, iwidth * bpp, NULL, NULL); GdkPixbuf *scaled = gdk_pixbuf_scale_simple(pixbuf, owidth, oheight, interp); g_object_unref(pixbuf); int src_stride = gdk_pixbuf_get_rowstride(scaled); int dst_stride = owidth * bpp; if (src_stride != dst_stride) { int y = oheight; uint8_t *src = gdk_pixbuf_get_pixels(scaled); uint8_t *dst = output; while (y--) { memcpy(dst, src, dst_stride); dst += dst_stride; src += src_stride; } } else { memcpy(output, gdk_pixbuf_get_pixels(scaled), owidth * oheight * bpp); } g_object_unref(scaled); // Now update the frame mlt_frame_set_image(this, output, size, mlt_pool_release); // Return the output *image = output; } break; } default: break; } return 0; } /** Constructor for the filter. */ mlt_filter filter_rescale_init(mlt_profile profile, char *arg) { // Create a new scaler mlt_filter this = mlt_factory_filter(profile, "rescale", arg); // If successful, then initialise it if (this != NULL) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES(this); // Set the inerpolation mlt_properties_set(properties, "interpolation", arg == NULL ? "bilinear" : arg); // Set the method mlt_properties_set_data(properties, "method", filter_scale, 0, NULL, NULL); } return this; } mlt-7.38.0/src/modules/gdk/filter_rescale.yml000664 000000 000000 00000002232 15172202314 021073 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: gtkrescale title: Gtk Rescale version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video - Hidden description: > Scale the producer video frame size to match the consumer. This filter is designed for use as a normalizer for the loader producer. notes: > If a property "consumer_aspect_ratio" exists on the frame, then rescaler normalizes the producer's aspect ratio and maximises the size of the frame, but may not produce the consumer's requested dimension. Therefore, this option works best in conjunction with the resize filter. This behavior can be disabled by another service by either removing the property, setting it to zero, or setting frame property "distort" to 1. parameters: - identifier: interpolation argument: yes title: Interpolation type: string description: The rescaling method. values: - nearest (lowest quality, fastest) - tiles - bilinear (good quality, moderate speed) - hyper (best quality, slowest) required: no readonly: no default: bilinear widget: combo mlt-7.38.0/src/modules/gdk/have_mmx.S000664 000000 000000 00000003045 15172202314 017320 0ustar00rootroot000000 000000 /* * Copyright (C) 2000 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ .file "have_mmx.S" .version "01.01" gcc2_compiled.: .text .align 16 #if !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(__INTERIX) /* Magic indicating no need for an executable stack */ #if !defined __powerpc64__ && !defined __ia64__ .section .note.GNU-stack; .previous #endif .globl _pixops_have_mmx .type _pixops_have_mmx,@function _pixops_have_mmx: #else .globl __pixops_have_mmx __pixops_have_mmx: #endif push %ebx # Check if bit 21 in flags word is writeable pushfl popl %eax movl %eax,%ebx xorl $0x00200000, %eax pushl %eax popfl pushfl popl %eax cmpl %eax, %ebx je .notfound # OK, we have CPUID movl $1, %eax cpuid test $0x00800000, %edx jz .notfound movl $1, %eax jmp .out .notfound: movl $0, %eax .out: popl %ebx ret mlt-7.38.0/src/modules/gdk/pixops.c000664 000000 000000 00000047202 15172202314 017061 0ustar00rootroot000000 000000 /* GdkPixbuf library - Scaling and compositing functions * * Original: * Copyright (C) 2000 Red Hat, Inc * Author: Owen Taylor * * Modification for MLT: * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include "pixops.h" #define SUBSAMPLE_BITS 4 #define SUBSAMPLE (1 << SUBSAMPLE_BITS) #define SUBSAMPLE_MASK ((1 << SUBSAMPLE_BITS)-1) #define SCALE_SHIFT 16 typedef struct _PixopsFilter PixopsFilter; typedef struct _PixopsFilterDimension PixopsFilterDimension; struct _PixopsFilterDimension { int n; double offset; double *weights; }; struct _PixopsFilter { PixopsFilterDimension x; PixopsFilterDimension y; double overall_alpha; }; typedef guchar *( *PixopsLineFunc ) ( int *weights, int n_x, int n_y, guchar *dest, int dest_x, guchar *dest_end, guchar **src, int x_init, int x_step, int src_width ); typedef void ( *PixopsPixelFunc ) ( guchar *dest, guint y1, guint cr, guint y2, guint cb ); /* mmx function declarations */ #if defined(USE_MMX) && !defined(ARCH_X86_64) guchar *pixops_scale_line_22_yuv_mmx ( guint32 weights[ 16 ][ 8 ], guchar *p, guchar *q1, guchar *q2, int x_step, guchar *p_stop, int x_init, int destx ); int _pixops_have_mmx ( void ); #endif static inline int get_check_shift ( int check_size ) { int check_shift = 0; g_return_val_if_fail ( check_size >= 0, 4 ); while ( !( check_size & 1 ) ) { check_shift++; check_size >>= 1; } return check_shift; } static inline void pixops_scale_nearest ( guchar *dest_buf, int render_x0, int render_y0, int render_x1, int render_y1, int dest_rowstride, const guchar *src_buf, int src_width, int src_height, int src_rowstride, double scale_x, double scale_y ) { register int i, j; register int x_step = ( 1 << SCALE_SHIFT ) / scale_x; register int y_step = ( 1 << SCALE_SHIFT ) / scale_y; register int x, x_scaled; for ( i = 0; i < ( render_y1 - render_y0 ); i++ ) { const guchar *src = src_buf + ( ( ( i + render_y0 ) * y_step + ( y_step >> 1 ) ) >> SCALE_SHIFT ) * src_rowstride; guchar *dest = dest_buf + i * dest_rowstride; x = render_x0 * x_step + ( x_step >> 1 ); for ( j = 0; j < ( render_x1 - render_x0 ); j++ ) { x_scaled = x >> SCALE_SHIFT; *dest++ = src[ x_scaled << 1 ]; *dest++ = src[ ( ( x_scaled >> 1 ) << 2 ) + ( ( j & 1 ) << 1 ) + 1 ]; x += x_step; } } } static inline guchar * scale_line ( int *weights, int n_x, int n_y, guchar *dest, int dest_x, guchar *dest_end, guchar **src, int x_init, int x_step, int src_width ) { register int x = x_init; register int i, j, x_scaled, y_index, uv_index; while ( dest < dest_end ) { unsigned int y = 0, uv = 0; int *pixel_weights = weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * n_x * n_y; x_scaled = x >> SCALE_SHIFT; y_index = x_scaled << 1; uv_index = ( ( x_scaled >> 1 ) << 2 ) + ( ( dest_x & 1 ) << 1 ) + 1; for ( i = 0; i < n_y; i++ ) { int *line_weights = pixel_weights + n_x * i; guchar *q = src[ i ]; for ( j = 0; j < n_x; j ++ ) { unsigned int ta = line_weights[ j ]; y += ta * q[ y_index ]; uv += ta * q[ uv_index ]; } } *dest++ = ( y + 0xffff ) >> SCALE_SHIFT; *dest++ = ( uv + 0xffff ) >> SCALE_SHIFT; x += x_step; dest_x++; } return dest; } #if defined(USE_MMX) && !defined(ARCH_X86_64) static inline guchar * scale_line_22_yuv_mmx_stub ( int *weights, int n_x, int n_y, guchar *dest, int dest_x, guchar *dest_end, guchar **src, int x_init, int x_step, int src_width ) { guint32 mmx_weights[ 16 ][ 8 ]; int j; for ( j = 0; j < 16; j++ ) { mmx_weights[ j ][ 0 ] = 0x00010001 * ( weights[ 4 * j ] >> 8 ); mmx_weights[ j ][ 1 ] = 0x00010001 * ( weights[ 4 * j ] >> 8 ); mmx_weights[ j ][ 2 ] = 0x00010001 * ( weights[ 4 * j + 1 ] >> 8 ); mmx_weights[ j ][ 3 ] = 0x00010001 * ( weights[ 4 * j + 1 ] >> 8 ); mmx_weights[ j ][ 4 ] = 0x00010001 * ( weights[ 4 * j + 2 ] >> 8 ); mmx_weights[ j ][ 5 ] = 0x00010001 * ( weights[ 4 * j + 2 ] >> 8 ); mmx_weights[ j ][ 6 ] = 0x00010001 * ( weights[ 4 * j + 3 ] >> 8 ); mmx_weights[ j ][ 7 ] = 0x00010001 * ( weights[ 4 * j + 3 ] >> 8 ); } return pixops_scale_line_22_yuv_mmx ( mmx_weights, dest, src[ 0 ], src[ 1 ], x_step, dest_end, x_init, dest_x ); } #endif /* USE_MMX */ static inline guchar * scale_line_22_yuv ( int *weights, int n_x, int n_y, guchar *dest, int dest_x, guchar *dest_end, guchar **src, int x_init, int x_step, int src_width ) { register int x = x_init; register guchar *src0 = src[ 0 ]; register guchar *src1 = src[ 1 ]; register unsigned int p; register guchar *q0, *q1; register int w1, w2, w3, w4; register int x_scaled, x_aligned, uv_index; while ( dest < dest_end ) { int *pixel_weights = weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * 4; x_scaled = x >> SCALE_SHIFT; w1 = pixel_weights[ 0 ]; w2 = pixel_weights[ 1 ]; w3 = pixel_weights[ 2 ]; w4 = pixel_weights[ 3 ]; /* process Y */ q0 = src0 + ( x_scaled << 1 ); q1 = src1 + ( x_scaled << 1 ); p = w1 * q0[ 0 ]; p += w2 * q0[ 2 ]; p += w3 * q1[ 0 ]; p += w4 * q1[ 2 ]; *dest++ = ( p + 0x8000 ) >> SCALE_SHIFT; /* process U/V */ x_aligned = ( ( x_scaled >> 1 ) << 2 ); uv_index = ( ( dest_x & 1 ) << 1 ) + 1; q0 = src0 + x_aligned; q1 = src1 + x_aligned; p = w1 * q0[ uv_index ]; p += w3 * q1[ uv_index ]; p += w2 * q0[ uv_index ]; p += w4 * q1[ uv_index ]; x += x_step; dest_x ++; *dest++ = ( p + 0x8000 ) >> SCALE_SHIFT; } return dest; } static inline void process_pixel ( int *weights, int n_x, int n_y, guchar *dest, int dest_x, int dest_channels, guchar **src, int src_channels, int x_start, int src_width ) { register unsigned int y = 0, uv = 0; register int i, j; int uv_index = ( ( dest_x & 1 ) << 1 ) + 1; for ( i = 0; i < n_y; i++ ) { int *line_weights = weights + n_x * i; for ( j = 0; j < n_x; j++ ) { unsigned int ta = 0xff * line_weights[ j ]; if ( x_start + j < 0 ) { y += ta * src[ i ][ 0 ]; uv += ta * src[ i ][ uv_index ]; } else if ( x_start + j < src_width ) { y += ta * src[ i ][ ( x_start + j ) << 1 ]; uv += ta * src[ i ][ ( ( ( x_start + j ) >> 1 ) << 2) + uv_index ]; } else { y += ta * src[ i ][ ( src_width - 1 ) << 1 ]; uv += ta * src[ i ][ ( ( ( src_width - 1 ) >> 1 ) << 2) + uv_index ]; } } } *dest++ = ( y + 0xffffff ) >> 24; *dest++ = ( uv + 0xffffff ) >> 24; } static inline void correct_total ( int *weights, int n_x, int n_y, int total, double overall_alpha ) { int correction = ( int ) ( 0.5 + 65536 * overall_alpha ) - total; int remaining, c, d, i; if ( correction != 0 ) { remaining = correction; for ( d = 1, c = correction; c != 0 && remaining != 0; d++, c = correction / d ) for ( i = n_x * n_y - 1; i >= 0 && c != 0 && remaining != 0; i-- ) if ( *( weights + i ) + c >= 0 ) { *( weights + i ) += c; remaining -= c; if ( ( 0 < remaining && remaining < c ) || ( 0 > remaining && remaining > c ) ) c = remaining; } } } static inline int * make_filter_table ( PixopsFilter *filter ) { int i_offset, j_offset; int n_x = filter->x.n; int n_y = filter->y.n; int *weights = g_new ( int, SUBSAMPLE * SUBSAMPLE * n_x * n_y ); for ( i_offset = 0; i_offset < SUBSAMPLE; i_offset++ ) for ( j_offset = 0; j_offset < SUBSAMPLE; j_offset++ ) { double weight; int *pixel_weights = weights + ( ( i_offset * SUBSAMPLE ) + j_offset ) * n_x * n_y; int total = 0; int i, j; for ( i = 0; i < n_y; i++ ) for ( j = 0; j < n_x; j++ ) { weight = filter->x.weights[ ( j_offset * n_x ) + j ] * filter->y.weights[ ( i_offset * n_y ) + i ] * filter->overall_alpha * 65536 + 0.5; total += ( int ) weight; *( pixel_weights + n_x * i + j ) = weight; } correct_total ( pixel_weights, n_x, n_y, total, filter->overall_alpha ); } return weights; } static inline void pixops_process ( guchar *dest_buf, int render_x0, int render_y0, int render_x1, int render_y1, int dest_rowstride, int dest_channels, gboolean dest_has_alpha, const guchar *src_buf, int src_width, int src_height, int src_rowstride, int src_channels, gboolean src_has_alpha, double scale_x, double scale_y, int check_x, int check_y, int check_size, guint32 color1, guint32 color2, PixopsFilter *filter, PixopsLineFunc line_func ) { int i, j; int x, y; /* X and Y position in source (fixed_point) */ guchar **line_bufs = g_new ( guchar *, filter->y.n ); int *filter_weights = make_filter_table ( filter ); int x_step = ( 1 << SCALE_SHIFT ) / scale_x; /* X step in source (fixed point) */ int y_step = ( 1 << SCALE_SHIFT ) / scale_y; /* Y step in source (fixed point) */ int scaled_x_offset = floor ( filter->x.offset * ( 1 << SCALE_SHIFT ) ); /* Compute the index where we run off the end of the source buffer. The furthest * source pixel we access at index i is: * * ((render_x0 + i) * x_step + scaled_x_offset) >> SCALE_SHIFT + filter->x.n - 1 * * So, run_end_index is the smallest i for which this pixel is src_width, i.e, for which: * * (i + render_x0) * x_step >= ((src_width - filter->x.n + 1) << SCALE_SHIFT) - scaled_x_offset * */ #define MYDIV(a,b) ((a) > 0 ? (a) / (b) : ((a) - (b) + 1) / (b)) /* Division so that -1/5 = -1 */ int run_end_x = ( ( ( src_width - filter->x.n + 1 ) << SCALE_SHIFT ) - scaled_x_offset ); int run_end_index = MYDIV ( run_end_x + x_step - 1, x_step ) - render_x0; run_end_index = MIN ( run_end_index, render_x1 - render_x0 ); y = render_y0 * y_step + floor ( filter->y.offset * ( 1 << SCALE_SHIFT ) ); for ( i = 0; i < ( render_y1 - render_y0 ); i++ ) { int dest_x; int y_start = y >> SCALE_SHIFT; int x_start; int *run_weights = filter_weights + ( ( y >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * filter->x.n * filter->y.n * SUBSAMPLE; guchar *new_outbuf; guchar *outbuf = dest_buf + dest_rowstride * i; guchar *outbuf_end = outbuf + dest_channels * ( render_x1 - render_x0 ); for ( j = 0; j < filter->y.n; j++ ) { if ( y_start < 0 ) line_bufs[ j ] = ( guchar * ) src_buf; else if ( y_start < src_height ) line_bufs[ j ] = ( guchar * ) src_buf + src_rowstride * y_start; else line_bufs[ j ] = ( guchar * ) src_buf + src_rowstride * ( src_height - 1 ); y_start++; } dest_x = check_x; x = render_x0 * x_step + scaled_x_offset; x_start = x >> SCALE_SHIFT; while ( x_start < 0 && outbuf < outbuf_end ) { process_pixel ( run_weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * ( filter->x.n * filter->y.n ), filter->x.n, filter->y.n, outbuf, dest_x, dest_channels, line_bufs, src_channels, x >> SCALE_SHIFT, src_width ); x += x_step; x_start = x >> SCALE_SHIFT; dest_x++; outbuf += dest_channels; } new_outbuf = ( *line_func ) ( run_weights, filter->x.n, filter->y.n, outbuf, dest_x, dest_buf + dest_rowstride * i + run_end_index * dest_channels, line_bufs, x, x_step, src_width ); dest_x += ( new_outbuf - outbuf ) / dest_channels; x = ( dest_x - check_x + render_x0 ) * x_step + scaled_x_offset; outbuf = new_outbuf; while ( outbuf < outbuf_end ) { process_pixel ( run_weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * ( filter->x.n * filter->y.n ), filter->x.n, filter->y.n, outbuf, dest_x, dest_channels, line_bufs, src_channels, x >> SCALE_SHIFT, src_width ); x += x_step; dest_x++; outbuf += dest_channels; } y += y_step; } g_free ( line_bufs ); g_free ( filter_weights ); } /* Compute weights for reconstruction by replication followed by * sampling with a box filter */ static inline void tile_make_weights ( PixopsFilterDimension *dim, double scale ) { int n = ceil ( 1 / scale + 1 ); double *pixel_weights = g_new ( double, SUBSAMPLE * n ); int offset; int i; dim->n = n; dim->offset = 0; dim->weights = pixel_weights; for ( offset = 0; offset < SUBSAMPLE; offset++ ) { double x = ( double ) offset / SUBSAMPLE; double a = x + 1 / scale; for ( i = 0; i < n; i++ ) { if ( i < x ) { if ( i + 1 > x ) * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - x ) * scale; else *( pixel_weights++ ) = 0; } else { if ( a > i ) * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - i ) * scale; else *( pixel_weights++ ) = 0; } } } } /* Compute weights for a filter that, for minification * is the same as 'tiles', and for magnification, is bilinear * reconstruction followed by a sampling with a delta function. */ static inline void bilinear_magnify_make_weights ( PixopsFilterDimension *dim, double scale ) { double * pixel_weights; int n; int offset; int i; if ( scale > 1.0 ) /* Linear */ { n = 2; dim->offset = 0.5 * ( 1 / scale - 1 ); } else /* Tile */ { n = ceil ( 1.0 + 1.0 / scale ); dim->offset = 0.0; } dim->n = n; dim->weights = g_new ( double, SUBSAMPLE * n ); pixel_weights = dim->weights; for ( offset = 0; offset < SUBSAMPLE; offset++ ) { double x = ( double ) offset / SUBSAMPLE; if ( scale > 1.0 ) /* Linear */ { for ( i = 0; i < n; i++ ) *( pixel_weights++ ) = ( ( ( i == 0 ) ? ( 1 - x ) : x ) / scale ) * scale; } else /* Tile */ { double a = x + 1 / scale; /* x * ---------|--.-|----|--.-|------- SRC * ------------|---------|--------- DEST */ for ( i = 0; i < n; i++ ) { if ( i < x ) { if ( i + 1 > x ) * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - x ) * scale; else *( pixel_weights++ ) = 0; } else { if ( a > i ) * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - i ) * scale; else *( pixel_weights++ ) = 0; } } } } } /* Computes the integral from b0 to b1 of * * f(x) = x; 0 <= x < 1 * f(x) = 0; otherwise * * We combine two of these to compute the convolution of * a box filter with a triangular spike. */ static inline double linear_box_half ( double b0, double b1 ) { double a0, a1; double x0, x1; a0 = 0.; a1 = 1.; if ( a0 < b0 ) { if ( a1 > b0 ) { x0 = b0; x1 = MIN ( a1, b1 ); } else return 0; } else { if ( b1 > a0 ) { x0 = a0; x1 = MIN ( a1, b1 ); } else return 0; } return 0.5 * ( x1 * x1 - x0 * x0 ); } /* Compute weights for reconstructing with bilinear * interpolation, then sampling with a box filter */ static inline void bilinear_box_make_weights ( PixopsFilterDimension *dim, double scale ) { int n = ceil ( 1 / scale + 2.0 ); double *pixel_weights = g_new ( double, SUBSAMPLE * n ); double w; int offset, i; dim->offset = -1.0; dim->n = n; dim->weights = pixel_weights; for ( offset = 0 ; offset < SUBSAMPLE; offset++ ) { double x = ( double ) offset / SUBSAMPLE; double a = x + 1 / scale; for ( i = 0; i < n; i++ ) { w = linear_box_half ( 0.5 + i - a, 0.5 + i - x ); w += linear_box_half ( 1.5 + x - i, 1.5 + a - i ); *( pixel_weights++ ) = w * scale; } } } static inline void make_weights ( PixopsFilter *filter, PixopsInterpType interp_type, double scale_x, double scale_y ) { switch ( interp_type ) { case PIXOPS_INTERP_NEAREST: g_assert_not_reached (); break; case PIXOPS_INTERP_TILES: tile_make_weights ( &filter->x, scale_x ); tile_make_weights ( &filter->y, scale_y ); break; case PIXOPS_INTERP_BILINEAR: bilinear_magnify_make_weights ( &filter->x, scale_x ); bilinear_magnify_make_weights ( &filter->y, scale_y ); break; case PIXOPS_INTERP_HYPER: bilinear_box_make_weights ( &filter->x, scale_x ); bilinear_box_make_weights ( &filter->y, scale_y ); break; } } void yuv422_scale ( guchar *dest_buf, int render_x0, int render_y0, int render_x1, int render_y1, int dest_rowstride, int dest_channels, gboolean dest_has_alpha, const guchar *src_buf, int src_width, int src_height, int src_rowstride, int src_channels, gboolean src_has_alpha, double scale_x, double scale_y, PixopsInterpType interp_type ) { PixopsFilter filter = { { 0, 0, 0}, { 0, 0, 0 }, 0 }; PixopsLineFunc line_func; #if defined(USE_MMX) && !defined(ARCH_X86_64) gboolean found_mmx = _pixops_have_mmx(); #endif //g_return_if_fail ( !( dest_channels == 3 && dest_has_alpha ) ); //g_return_if_fail ( !( src_channels == 3 && src_has_alpha ) ); //g_return_if_fail ( !( src_has_alpha && !dest_has_alpha ) ); if ( scale_x == 0 || scale_y == 0 ) return ; if ( interp_type == PIXOPS_INTERP_NEAREST ) { pixops_scale_nearest ( dest_buf, render_x0, render_y0, render_x1, render_y1, dest_rowstride, src_buf, src_width, src_height, src_rowstride, scale_x, scale_y ); return; } filter.overall_alpha = 1.0; make_weights ( &filter, interp_type, scale_x, scale_y ); if ( filter.x.n == 2 && filter.y.n == 2 ) { #if defined(USE_MMX) && !defined(ARCH_X86_64) if ( found_mmx ) { //fprintf( stderr, "rescale: using mmx\n" ); line_func = scale_line_22_yuv_mmx_stub; } else #endif line_func = scale_line_22_yuv; } else line_func = scale_line; pixops_process ( dest_buf, render_x0, render_y0, render_x1, render_y1, dest_rowstride, dest_channels, dest_has_alpha, src_buf, src_width, src_height, src_rowstride, src_channels, src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0, &filter, line_func ); g_free ( filter.x.weights ); g_free ( filter.y.weights ); } mlt-7.38.0/src/modules/gdk/pixops.h000664 000000 000000 00000004735 15172202314 017072 0ustar00rootroot000000 000000 /* GdkPixbuf library - Scaling and compositing functions * * Original: * Copyright (C) 2000 Red Hat, Inc * Author: Owen Taylor * * Modification for MLT: * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef PIXOPS_H #define PIXOPS_H #include /* Interpolation modes; must match GdkInterpType */ typedef enum { PIXOPS_INTERP_NEAREST, PIXOPS_INTERP_TILES, PIXOPS_INTERP_BILINEAR, PIXOPS_INTERP_HYPER } PixopsInterpType; /* Scale src_buf from src_width / src_height by factors scale_x, scale_y * and composite the portion corresponding to * render_x, render_y, render_width, render_height in the new * coordinate system into dest_buf starting at 0, 0 */ void yuv422_scale (guchar *dest_buf, int render_x0, int render_y0, int render_x1, int render_y1, int dest_rowstride, int dest_channels, int dest_has_alpha, const guchar *src_buf, int src_width, int src_height, int src_rowstride, int src_channels, int src_has_alpha, double scale_x, double scale_y, PixopsInterpType interp_type); #define yuv422_scale_simple( dest_buf, dest_width, dest_height, dest_rowstride, src_buf, src_width, src_height, src_rowstride, interp_type ) \ yuv422_scale( (dest_buf), 0, 0, \ (dest_width), (dest_height), \ (dest_rowstride), 2, 0, \ (src_buf), (src_width), (src_height), \ (src_rowstride), 2, 0, \ (double) (dest_width) / (src_width), (double) (dest_height) / (src_height), \ (PixopsInterpType) interp_type ); #endif mlt-7.38.0/src/modules/gdk/producer_pango.c000664 000000 000000 00000131427 15172202314 020551 0ustar00rootroot000000 000000 /* * producer_pango.c -- a pango-based titler * Copyright (C) 2003-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #include #include #include typedef struct producer_pango_s *producer_pango; typedef enum { pango_align_left = 0, pango_align_center, pango_align_right } pango_align; static pthread_mutex_t pango_mutex = PTHREAD_MUTEX_INITIALIZER; struct pango_cached_image_s { uint8_t *image, *alpha; mlt_image_format format; int width, height; }; static void pango_cached_image_destroy(void *p) { struct pango_cached_image_s *i = p; if (!i) return; if (i->image) mlt_pool_release(i->image); if (i->alpha) mlt_pool_release(i->alpha); mlt_pool_release(i); }; struct producer_pango_s { struct mlt_producer_s parent; int width; int height; GdkPixbuf *pixbuf; char *fgcolor; char *bgcolor; char *olcolor; int align; int pad; int outline; int underline; int strikethrough; char *markup; char *text; char *font; char *family; int size; int style; int weight; int stretch; int rotate; int width_crop; int width_fit; int wrap_type; int wrap_width; int line_spacing; double aspect_ratio; }; static void clean_cached(producer_pango self) { mlt_service_cache_put(MLT_PRODUCER_SERVICE(&self->parent), "pango.image", NULL, 0, NULL); } // special color type used by internal pango routines typedef struct { uint8_t r, g, b, a; } rgba_color; // Forward declarations static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int index); static void producer_close(mlt_producer parent); static void pango_draw_background(GdkPixbuf *pixbuf, rgba_color bg); static GdkPixbuf *pango_get_pixbuf(const char *markup, const char *text, const char *font, rgba_color fg, rgba_color bg, rgba_color ol, int pad, int align, char *family, int style, int weight, int stretch, int size, int outline, int rotate, int width_crop, int width_fit, int wrap_type, int wrap_width, int line_spacing, double aspect_ratio, int underline, int strikethrough); static void fill_pixbuf(GdkPixbuf *pixbuf, FT_Bitmap *bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg); static void fill_pixbuf_with_outline(GdkPixbuf *pixbuf, FT_Bitmap *bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg, rgba_color ol, int outline); /** Return nonzero if the two strings are equal, ignoring case, up to the first n characters. */ int strncaseeq(const char *s1, const char *s2, size_t n) { for (; n > 0; n--) { if (tolower(*s1++) != tolower(*s2++)) return 0; } return 1; } /** Parse the alignment property. */ static int parse_alignment(char *align) { int ret = pango_align_left; if (align == NULL) ; else if (isdigit(align[0])) ret = atoi(align); else if (align[0] == 'c' || align[0] == 'm') ret = pango_align_center; else if (align[0] == 'r') ret = pango_align_right; return ret; } /** Parse the style property. */ static int parse_style(char *style) { int ret = PANGO_STYLE_NORMAL; if (!strncmp(style, "italic", 6)) ret = PANGO_STYLE_ITALIC; if (!strncmp(style, "oblique", 7)) ret = PANGO_STYLE_OBLIQUE; return ret; } static PangoFT2FontMap *fontmap = NULL; static void on_fontmap_reload(); mlt_producer producer_pango_init(const char *filename) { producer_pango self = calloc(1, sizeof(struct producer_pango_s)); if (self != NULL && mlt_producer_init(&self->parent, self) == 0) { mlt_producer producer = &self->parent; pthread_mutex_lock(&pango_mutex); if (fontmap == NULL) fontmap = (PangoFT2FontMap *) pango_ft2_font_map_new(); pthread_mutex_unlock(&pango_mutex); producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES(&self->parent); mlt_events_register(properties, "fontmap-reload"); mlt_events_listen(properties, producer, "fontmap-reload", (mlt_listener) on_fontmap_reload); // Set the default properties mlt_properties_set_string(properties, "fgcolour", "0xffffffff"); mlt_properties_set_string(properties, "bgcolour", "0x00000000"); mlt_properties_set_string(properties, "olcolour", "0x00000000"); mlt_properties_set_int(properties, "align", pango_align_left); mlt_properties_set_int(properties, "pad", 0); mlt_properties_set_int(properties, "outline", 0); mlt_properties_set_int(properties, "underline", 0); mlt_properties_set_int(properties, "strikethrough", 0); mlt_properties_set_string(properties, "text", ""); mlt_properties_set_string(properties, "font", NULL); mlt_properties_set_string(properties, "family", "Sans"); mlt_properties_set_int(properties, "size", 48); mlt_properties_set_string(properties, "style", "normal"); mlt_properties_set_string(properties, "encoding", "UTF-8"); mlt_properties_set_int(properties, "weight", PANGO_WEIGHT_NORMAL); mlt_properties_set_int(properties, "stretch", PANGO_STRETCH_NORMAL + 1); mlt_properties_set_int(properties, "rotate", 0); mlt_properties_set_int(properties, "seekable", 1); mlt_properties_set_int(properties, "meta.media.progressive", 1); if (filename == NULL || (filename && (!strcmp(filename, "") || strstr(filename, "") // workaround for old kdenlive countdown generator || strstr(filename, "<producer>")))) { mlt_properties_set_string(properties, "markup", ""); } else if (filename[0] == '+' || strstr(filename, "/+")) { char *copy = strdup(filename + 1); char *markup = copy; if (strstr(markup, "/+")) markup = strstr(markup, "/+") + 2; if (strrchr(markup, '.')) (*strrchr(markup, '.')) = '\0'; while (strchr(markup, '~')) (*strchr(markup, '~')) = '\n'; mlt_properties_set_string(properties, "resource", filename); mlt_properties_set_string(properties, "markup", markup); free(copy); } else if (strstr(filename, ".mpl")) { int i = 0; mlt_position out_point = 0; mlt_properties contents = mlt_properties_load(filename); mlt_animation key_frames = mlt_animation_new(); struct mlt_animation_item_s item; item.property = NULL; item.keyframe_type = mlt_keyframe_discrete; mlt_properties_set_string(properties, "resource", filename); mlt_properties_set_data(properties, "contents", contents, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set_data(properties, "key_frames", key_frames, 0, (mlt_destructor) mlt_animation_close, NULL); // Make sure we have at least one entry if (mlt_properties_get(contents, "0") == NULL) mlt_properties_set_string(contents, "0", ""); for (i = 0; i < mlt_properties_count(contents); i++) { char *name = mlt_properties_get_name(contents, i); char *value = mlt_properties_get_value(contents, i); while (value != NULL && strchr(value, '~')) (*strchr(value, '~')) = '\n'; item.frame = atoi(name); mlt_animation_insert(key_frames, &item); out_point = MAX(out_point, item.frame); } mlt_animation_interpolate(key_frames); mlt_properties_set_position(properties, "length", out_point + 1); mlt_properties_set_position(properties, "out", out_point); } else { mlt_properties_set_string(properties, "resource", filename); FILE *f = mlt_fopen(filename, "r"); if (f != NULL) { char line[81]; char *markup = NULL; size_t size = 0; line[80] = '\0'; while (fgets(line, 80, f)) { size += strlen(line) + 1; if (markup) { char *new_markup = realloc(markup, size); if (new_markup) { markup = new_markup; strcat(markup, line); } else { // Allocation failed: keep existing content and stop appending break; } } else { markup = strdup(line); } } fclose(f); if (markup && markup[strlen(markup) - 1] == '\n') markup[strlen(markup) - 1] = '\0'; if (markup) mlt_properties_set_string(properties, "markup", markup); else mlt_properties_set_string(properties, "markup", ""); free(markup); } else { producer->close = NULL; mlt_producer_close(producer); producer = NULL; free(self); } } return producer; } free(self); return NULL; } static void set_string(char **string, const char *value, const char *fallback) { if (value != NULL) { free(*string); *string = strdup(value); } else if (*string == NULL && fallback != NULL) { *string = strdup(fallback); } else if (*string != NULL && fallback == NULL) { free(*string); *string = NULL; } } rgba_color parse_color(char *color, unsigned int color_int) { rgba_color result = {0xff, 0xff, 0xff, 0xff}; if (!strcmp(color, "red")) { result.r = 0xff; result.g = 0x00; result.b = 0x00; } else if (!strcmp(color, "green")) { result.r = 0x00; result.g = 0xff; result.b = 0x00; } else if (!strcmp(color, "blue")) { result.r = 0x00; result.g = 0x00; result.b = 0xff; } else if (strcmp(color, "white")) { result.r = (color_int >> 24) & 0xff; result.g = (color_int >> 16) & 0xff; result.b = (color_int >> 8) & 0xff; result.a = (color_int) &0xff; } return result; } /** Convert a string property to UTF-8 */ static int iconv_utf8(mlt_properties properties, const char *prop_name, const char *encoding) { char *text = mlt_properties_get(properties, prop_name); int result = -1; iconv_t cd = iconv_open("UTF-8", encoding); if (text && (cd != (iconv_t) -1)) { char *inbuf_p = text; size_t inbuf_n = strlen(text); size_t outbuf_n = inbuf_n * 6; char *outbuf = mlt_pool_alloc(outbuf_n); char *outbuf_p = outbuf; memset(outbuf, 0, outbuf_n); if (text != NULL && strcmp(text, "") && iconv(cd, &inbuf_p, &inbuf_n, &outbuf_p, &outbuf_n) != -1) mlt_properties_set_string(properties, prop_name, outbuf); else mlt_properties_set_string(properties, prop_name, ""); mlt_pool_release(outbuf); result = 0; } iconv_close(cd); return result; } static void refresh_image(producer_pango self, mlt_frame frame, int width, int height) { // Pixbuf GdkPixbuf *pixbuf = mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "pixbuf", NULL); // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); // Obtain the producer mlt_producer producer = &self->parent; // Obtain the producer properties mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); // Get producer properties char *fg = mlt_properties_get(producer_props, "fgcolour"); char *bg = mlt_properties_get(producer_props, "bgcolour"); char *ol = mlt_properties_get(producer_props, "olcolour"); int align = parse_alignment(mlt_properties_get(producer_props, "align")); int pad = mlt_properties_get_int(producer_props, "pad"); int outline = mlt_properties_get_int(producer_props, "outline"); char *markup = mlt_properties_get(producer_props, "markup"); char *text = mlt_properties_get(producer_props, "text"); char *font = mlt_properties_get(producer_props, "font"); char *family = mlt_properties_get(producer_props, "family"); int style = parse_style(mlt_properties_get(producer_props, "style")); char *encoding = mlt_properties_get(producer_props, "encoding"); int weight = mlt_properties_get_int(producer_props, "weight"); int stretch = mlt_properties_get_int(producer_props, "stretch"); int rotate = mlt_properties_get_int(producer_props, "rotate"); int size = mlt_properties_get_int(producer_props, "size"); int width_crop = mlt_properties_get_int(producer_props, "width_crop"); int width_fit = mlt_properties_get_int(producer_props, "width_fit"); int wrap_type = mlt_properties_get_int(producer_props, "wrap_type"); int wrap_width = mlt_properties_get_int(producer_props, "wrap_width"); int line_spacing = mlt_properties_get_int(properties, "line_spacing"); double aspect_ratio = mlt_properties_get_double(properties, "aspect_ratio"); int underline = mlt_properties_get_int(producer_props, "underline"); int strikethrough = mlt_properties_get_int(producer_props, "strikethrough"); int property_changed = 0; if (pixbuf == NULL) { // Check for file support mlt_properties contents = mlt_properties_get_data(producer_props, "contents", NULL); mlt_animation key_frames = mlt_properties_get_data(producer_props, "key_frames", NULL); if (contents != NULL) { char temp[20]; struct mlt_animation_item_s item; item.property = NULL; mlt_animation_prev_key(key_frames, &item, mlt_frame_original_position(frame)); sprintf(temp, "%d", item.frame); markup = mlt_properties_get(contents, temp); } // See if any properties changed property_changed = (align != self->align); property_changed = property_changed || (self->fgcolor == NULL || (fg && strcmp(fg, self->fgcolor))); property_changed = property_changed || (self->bgcolor == NULL || (bg && strcmp(bg, self->bgcolor))); property_changed = property_changed || (self->olcolor == NULL || (ol && strcmp(ol, self->olcolor))); property_changed = property_changed || (pad != self->pad); property_changed = property_changed || (outline != self->outline); property_changed = property_changed || (underline != self->underline); property_changed = property_changed || (strikethrough != self->strikethrough); property_changed = property_changed || (markup && self->markup && strcmp(markup, self->markup)); property_changed = property_changed || (text && self->text && strcmp(text, self->text)); property_changed = property_changed || (font && self->font && strcmp(font, self->font)); property_changed = property_changed || (family && self->family && strcmp(family, self->family)); property_changed = property_changed || (weight != self->weight); property_changed = property_changed || (stretch != self->stretch); property_changed = property_changed || (rotate != self->rotate); property_changed = property_changed || (style != self->style); property_changed = property_changed || (size != self->size); property_changed = property_changed || (width_crop != self->width_crop); property_changed = property_changed || (width_fit != self->width_fit); property_changed = property_changed || (wrap_type != self->wrap_type); property_changed = property_changed || (wrap_width != self->wrap_width); property_changed = property_changed || (line_spacing != self->line_spacing); property_changed = property_changed || (aspect_ratio != self->aspect_ratio); // Save the properties for next comparison self->align = align; self->pad = pad; self->outline = outline; self->underline = underline; self->strikethrough = strikethrough; set_string(&self->fgcolor, fg, "0xffffffff"); set_string(&self->bgcolor, bg, "0x00000000"); set_string(&self->olcolor, ol, "0x00000000"); set_string(&self->markup, markup, NULL); set_string(&self->text, text, NULL); set_string(&self->font, font, NULL); set_string(&self->family, family, "Sans"); self->weight = weight; self->stretch = stretch; self->rotate = rotate; self->style = style; self->size = size; self->width_crop = width_crop; self->width_fit = width_fit; self->wrap_type = wrap_type; self->wrap_width = wrap_width; self->line_spacing = line_spacing; self->aspect_ratio = aspect_ratio; } if (pixbuf == NULL && property_changed) { rgba_color fgcolor = parse_color(self->fgcolor, mlt_properties_get_int(producer_props, "fgcolour")); rgba_color bgcolor = parse_color(self->bgcolor, mlt_properties_get_int(producer_props, "bgcolour")); rgba_color olcolor = parse_color(self->olcolor, mlt_properties_get_int(producer_props, "olcolour")); if (self->pixbuf) g_object_unref(self->pixbuf); self->pixbuf = NULL; clean_cached(self); // Convert from specified encoding to UTF-8 if (encoding != NULL && !strncaseeq(encoding, "utf-8", 5) && !strncaseeq(encoding, "utf8", 4)) { if (markup != NULL && iconv_utf8(producer_props, "markup", encoding) != -1) { markup = mlt_properties_get(producer_props, "markup"); set_string(&self->markup, markup, NULL); } if (text != NULL && iconv_utf8(producer_props, "text", encoding) != -1) { text = mlt_properties_get(producer_props, "text"); set_string(&self->text, text, NULL); } } // Render the title pixbuf = pango_get_pixbuf(markup, text, font, fgcolor, bgcolor, olcolor, pad, align, family, style, weight, stretch, size, outline, rotate, width_crop, width_fit, wrap_type, wrap_width, line_spacing, aspect_ratio, underline, strikethrough); if (pixbuf != NULL) { // Register self pixbuf for destruction and reuse mlt_properties_set_data(producer_props, "pixbuf", pixbuf, 0, (mlt_destructor) g_object_unref, NULL); g_object_ref(pixbuf); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "pixbuf", pixbuf, 0, (mlt_destructor) g_object_unref, NULL); mlt_properties_set_int(producer_props, "meta.media.width", gdk_pixbuf_get_width(pixbuf)); mlt_properties_set_int(producer_props, "meta.media.height", gdk_pixbuf_get_height(pixbuf)); // Store the width/height of the pixbuf temporarily self->width = gdk_pixbuf_get_width(pixbuf); self->height = gdk_pixbuf_get_height(pixbuf); } } else if (pixbuf == NULL && width > 0 && (self->pixbuf == NULL || width != self->width || height != self->height)) { if (self->pixbuf) g_object_unref(self->pixbuf); self->pixbuf = NULL; clean_cached(self); pixbuf = mlt_properties_get_data(producer_props, "pixbuf", NULL); } // If we have a pixbuf and a valid width if (pixbuf && width > 0) { char *interps = mlt_properties_get(properties, "consumer.rescale"); int interp = GDK_INTERP_BILINEAR; if (strcmp(interps, "nearest") == 0) interp = GDK_INTERP_NEAREST; else if (strcmp(interps, "tiles") == 0) interp = GDK_INTERP_TILES; else if (strcmp(interps, "hyper") == 0 || strcmp(interps, "bicubic") == 0) interp = GDK_INTERP_HYPER; // fprintf(stderr,"%s: scaling from %dx%d to %dx%d\n", __FILE__, self->width, self->height, width, height); // Note - the original pixbuf is already safe and ready for destruction self->pixbuf = gdk_pixbuf_scale_simple(pixbuf, width, height, interp); clean_cached(self); // Store width and height self->width = width; self->height = height; int has_alpha = gdk_pixbuf_get_has_alpha(self->pixbuf); mlt_properties_set_int(properties, "format", has_alpha ? mlt_image_rgba : mlt_image_rgb); } // Set width/height mlt_properties_set_int(properties, "width", self->width); mlt_properties_set_int(properties, "height", self->height); } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; producer_pango self = (producer_pango) mlt_frame_pop_service(frame); // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); *width = mlt_properties_get_int(properties, "rescale_width"); *height = mlt_properties_get_int(properties, "rescale_height"); mlt_service_lock(MLT_PRODUCER_SERVICE(&self->parent)); // Refresh the image pthread_mutex_lock(&pango_mutex); refresh_image(self, frame, *width, *height); // Get width and height *width = self->width; *height = self->height; // Always clone here to allow 'animated' text if (self->pixbuf) { int size, bpp; uint8_t *buf; mlt_cache_item cached_item = mlt_service_cache_get(MLT_PRODUCER_SERVICE(&self->parent), "pango.image"); struct pango_cached_image_s *cached = mlt_cache_item_data(cached_item, NULL); // destroy cached data if request is differ if (!cached || (cached && (cached->format != *format || cached->width != *width || cached->height != *height))) { mlt_cache_item_close(cached_item); cached_item = NULL; cached = NULL; clean_cached(self); } // create cached image if (!cached) { int dst_stride, src_stride; cached = mlt_pool_alloc(sizeof(struct pango_cached_image_s)); cached->width = self->width; cached->height = self->height; cached->format = gdk_pixbuf_get_has_alpha(self->pixbuf) ? mlt_image_rgba : mlt_image_rgb; cached->alpha = NULL; cached->image = NULL; src_stride = gdk_pixbuf_get_rowstride(self->pixbuf); dst_stride = self->width * (mlt_image_rgba == cached->format ? 4 : 3); size = mlt_image_format_size(cached->format, cached->width, cached->height, &bpp); buf = mlt_pool_alloc(size); uint8_t *buf_save = buf; if (src_stride != dst_stride) { int y = self->height; uint8_t *src = gdk_pixbuf_get_pixels(self->pixbuf); uint8_t *dst = buf; while (y--) { memcpy(dst, src, dst_stride); dst += dst_stride; src += src_stride; } } else { memcpy(buf, gdk_pixbuf_get_pixels(self->pixbuf), src_stride * self->height); } // convert image if (frame->convert_image && cached->format != *format) { frame->convert_image(frame, &buf, &cached->format, *format); *format = cached->format; if (buf != buf_save) mlt_pool_release(buf_save); } size = mlt_image_format_size(cached->format, cached->width, cached->height, &bpp); cached->image = mlt_pool_alloc(size); memcpy(cached->image, buf, size); if ((buf = mlt_frame_get_alpha(frame))) { size = cached->width * cached->height; cached->alpha = mlt_pool_alloc(size); memcpy(cached->alpha, buf, size); } } if (cached) { // clone image surface size = mlt_image_format_size(cached->format, cached->width, cached->height, &bpp); buf = mlt_pool_alloc(size); memcpy(buf, cached->image, size); // set image surface mlt_frame_set_image(frame, buf, size, mlt_pool_release); *buffer = buf; // set alpha if (cached->alpha) { size = cached->width * cached->height; buf = mlt_pool_alloc(size); memcpy(buf, cached->alpha, size); mlt_frame_set_alpha(frame, buf, size, mlt_pool_release); } } if (cached_item) mlt_cache_item_close(cached_item); else mlt_service_cache_put(MLT_PRODUCER_SERVICE(&self->parent), "pango.image", cached, sizeof(struct pango_cached_image_s), pango_cached_image_destroy); } else { error = 1; } pthread_mutex_unlock(&pango_mutex); mlt_service_unlock(MLT_PRODUCER_SERVICE(&self->parent)); return error; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { producer_pango self = producer->child; // Fetch the producers properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Set producer-specific frame properties mlt_properties_set_int(properties, "progressive", 1); double force_ratio = mlt_properties_get_double(producer_properties, "force_aspect_ratio"); if (force_ratio > 0.0) mlt_properties_set_double(properties, "aspect_ratio", force_ratio); else { mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); } // Refresh the pango image pthread_mutex_lock(&pango_mutex); refresh_image(self, *frame, 0, 0); pthread_mutex_unlock(&pango_mutex); // Stack the get image callback mlt_frame_push_service(*frame, self); mlt_frame_push_get_image(*frame, producer_get_image); // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer parent) { producer_pango self = parent->child; if (self->pixbuf) g_object_unref(self->pixbuf); mlt_service_cache_purge(MLT_PRODUCER_SERVICE(parent)); free(self->fgcolor); free(self->bgcolor); free(self->olcolor); free(self->markup); free(self->text); free(self->font); free(self->family); parent->close = NULL; mlt_producer_close(parent); free(self); } static void pango_draw_background(GdkPixbuf *pixbuf, rgba_color bg) { int ww = gdk_pixbuf_get_width(pixbuf); int hh = gdk_pixbuf_get_height(pixbuf); uint8_t *p = gdk_pixbuf_get_pixels(pixbuf); int i, j; for (j = 0; j < hh; j++) { for (i = 0; i < ww; i++) { *p++ = bg.r; *p++ = bg.g; *p++ = bg.b; *p++ = bg.a; } } } static GdkPixbuf *pango_get_pixbuf(const char *markup, const char *text, const char *font, rgba_color fg, rgba_color bg, rgba_color ol, int pad, int align, char *family, int style, int weight, int stretch, int size, int outline, int rotate, int width_crop, int width_fit, int wrap_type, int wrap_width, int line_spacing, double aspect_ratio, int underline, int strikethrough) { PangoContext *context = pango_ft2_font_map_create_context(fontmap); PangoLayout *layout = pango_layout_new(context); int w, h; int x = 0, y = 0; GdkPixbuf *pixbuf = NULL; FT_Bitmap bitmap; PangoFontDescription *desc = NULL; desc = pango_font_description_from_string(family); pango_font_description_set_size(desc, PANGO_SCALE * size); pango_font_description_set_style(desc, (PangoStyle) style); pango_font_description_set_weight(desc, (PangoWeight) weight); if (stretch) pango_font_description_set_stretch(desc, (PangoStretch) (stretch - 1)); // set line_spacing if (line_spacing) pango_layout_set_spacing(layout, PANGO_SCALE * line_spacing); // set wrapping constraints if (wrap_width <= 0) pango_layout_set_width(layout, -1); else { pango_layout_set_width(layout, PANGO_SCALE * wrap_width); pango_layout_set_wrap(layout, (PangoWrapMode) wrap_type); } pango_layout_set_font_description(layout, desc); pango_layout_set_alignment(layout, (PangoAlignment) align); if (markup != NULL && strcmp(markup, "") != 0) { pango_layout_set_markup(layout, markup, strlen(markup)); } else if (text != NULL && strcmp(text, "") != 0) { pango_layout_set_text(layout, text, strlen(text)); } else { // Pango doesn't like empty strings pango_layout_set_text(layout, " ", 2); } // Apply text decoration attributes (underline and strikethrough) if (underline || strikethrough) { PangoAttrList *attrs = pango_attr_list_new(); if (underline) pango_attr_list_insert(attrs, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE)); if (strikethrough) pango_attr_list_insert(attrs, pango_attr_strikethrough_new(TRUE)); pango_layout_set_attributes(layout, attrs); pango_attr_list_unref(attrs); } if (rotate) { double n_x, n_y; PangoRectangle rect; PangoMatrix m_layout = PANGO_MATRIX_INIT, m_offset = PANGO_MATRIX_INIT; pango_matrix_rotate(&m_layout, rotate); pango_matrix_rotate(&m_offset, -rotate); pango_context_set_base_gravity(context, PANGO_GRAVITY_AUTO); pango_context_set_matrix(context, &m_layout); pango_layout_context_changed(layout); pango_layout_get_extents(layout, NULL, &rect); pango_matrix_transform_rectangle(pango_context_get_matrix(context), &rect); n_x = -rect.x; n_y = -rect.y; pango_matrix_transform_point(&m_offset, &n_x, &n_y); rect.x = n_x; rect.y = n_y; pango_extents_to_pixels(&rect, NULL); w = rect.width; h = rect.height; x = rect.x; y = rect.y; } else pango_layout_get_pixel_size(layout, &w, &h); // respect aspect ratio if (0.0 < aspect_ratio && aspect_ratio != 1.0) { double n_x, n_y; PangoRectangle rect; PangoMatrix m_layout = PANGO_MATRIX_INIT, m_offset = PANGO_MATRIX_INIT; #if 1 pango_matrix_scale(&m_layout, 1.0 / aspect_ratio, 1.0); pango_matrix_scale(&m_offset, 1.0 / aspect_ratio, 1.0); #else pango_matrix_scale(&m_layout, 1.0, aspect_ratio); pango_matrix_scale(&m_offset, 1.0, aspect_ratio); #endif pango_context_set_base_gravity(context, PANGO_GRAVITY_AUTO); pango_context_set_matrix(context, &m_layout); pango_layout_context_changed(layout); pango_layout_get_extents(layout, NULL, &rect); pango_matrix_transform_rectangle(pango_context_get_matrix(context), &rect); n_x = -rect.x; n_y = -rect.y; pango_matrix_transform_point(&m_offset, &n_x, &n_y); rect.x = n_x; rect.y = n_y; pango_extents_to_pixels(&rect, NULL); w = rect.width; h = rect.height; x = rect.x; y = rect.y; } // limit width if (width_crop && w > width_crop) w = width_crop; else if (width_fit && w > width_fit) { double n_x, n_y; PangoRectangle rect; PangoMatrix m_layout = PANGO_MATRIX_INIT, m_offset = PANGO_MATRIX_INIT; pango_matrix_scale(&m_layout, width_fit / (double) w, 1.0); pango_matrix_scale(&m_offset, width_fit / (double) w, 1.0); pango_context_set_base_gravity(context, PANGO_GRAVITY_AUTO); pango_context_set_matrix(context, &m_layout); pango_layout_context_changed(layout); pango_layout_get_extents(layout, NULL, &rect); pango_matrix_transform_rectangle(pango_context_get_matrix(context), &rect); n_x = -rect.x; n_y = -rect.y; pango_matrix_transform_point(&m_offset, &n_x, &n_y); rect.x = n_x; rect.y = n_y; pango_extents_to_pixels(&rect, NULL); w = rect.width; h = rect.height; x = rect.x; y = rect.y; } if (pad == 0) pad = 1; pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE /* has alpha */, 8, w + 2 * pad, h + 2 * pad); pango_draw_background(pixbuf, bg); bitmap.width = w; bitmap.pitch = 32 * ((w + 31) / 31); bitmap.rows = h; bitmap.buffer = mlt_pool_alloc(h * bitmap.pitch); bitmap.num_grays = 256; bitmap.pixel_mode = ft_pixel_mode_grays; memset(bitmap.buffer, 0, h * bitmap.pitch); pango_ft2_render_layout(&bitmap, layout, x, y); if (outline) { fill_pixbuf_with_outline(pixbuf, &bitmap, w, h, pad, align, fg, bg, ol, outline); } else { fill_pixbuf(pixbuf, &bitmap, w, h, pad, align, fg, bg); } mlt_pool_release(bitmap.buffer); pango_font_description_free(desc); g_object_unref(layout); g_object_unref(context); return pixbuf; } static void fill_pixbuf(GdkPixbuf *pixbuf, FT_Bitmap *bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg) { int stride = gdk_pixbuf_get_rowstride(pixbuf); uint8_t *src = bitmap->buffer; int x = (gdk_pixbuf_get_width(pixbuf) - w - 2 * pad) * align / 2 + pad; uint8_t *dest = gdk_pixbuf_get_pixels(pixbuf) + 4 * x + pad * stride; int j = h; int i = 0; uint8_t *d, *s, a; while (j--) { d = dest; s = src; i = w; while (i--) { a = *s++; *d++ = (a * fg.r + (255 - a) * bg.r) >> 8; *d++ = (a * fg.g + (255 - a) * bg.g) >> 8; *d++ = (a * fg.b + (255 - a) * bg.b) >> 8; *d++ = (a * fg.a + (255 - a) * bg.a) >> 8; } dest += stride; src += bitmap->pitch; } } static void fill_pixbuf_with_outline(GdkPixbuf *pixbuf, FT_Bitmap *bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg, rgba_color ol, int outline) { int stride = gdk_pixbuf_get_rowstride(pixbuf); int x = (gdk_pixbuf_get_width(pixbuf) - w - 2 * pad) * align / 2 + pad; uint8_t *dest = gdk_pixbuf_get_pixels(pixbuf) + 4 * x + pad * stride; int j, i; uint8_t *d = NULL; float a_ol = 0; float a_fg = 0; for (j = 0; j < h; j++) { d = dest; for (i = 0; i < w; i++) { #define geta(x, y) (float) bitmap->buffer[(y) *bitmap->pitch + (x)] / 255.0 a_ol = geta(i, j); // One pixel fake circle if (i > 0) a_ol = MAX(a_ol, geta(i - 1, j)); if (i < w - 1) a_ol = MAX(a_ol, geta(i + 1, j)); if (j > 0) a_ol = MAX(a_ol, geta(i, j - 1)); if (j < h - 1) a_ol = MAX(a_ol, geta(i, j + 1)); if (outline >= 2) { // Two pixels fake circle if (i > 1) { a_ol = MAX(a_ol, geta(i - 2, j)); if (j > 0) a_ol = MAX(a_ol, geta(i - 2, j - 1)); if (j < h - 1) a_ol = MAX(a_ol, geta(i - 2, j + 1)); } if (i > 0) { if (j > 0) a_ol = MAX(a_ol, geta(i - 1, j - 1)); if (j > 1) a_ol = MAX(a_ol, geta(i - 1, j - 2)); if (j < h - 1) a_ol = MAX(a_ol, geta(i - 1, j + 1)); if (j < h - 2) a_ol = MAX(a_ol, geta(i - 1, j + 2)); } if (j > 1) a_ol = MAX(a_ol, geta(i, j - 2)); if (j < h - 2) a_ol = MAX(a_ol, geta(i, j + 2)); if (i < w - 1) { if (j > 0) a_ol = MAX(a_ol, geta(i + 1, j - 1)); if (j > 1) a_ol = MAX(a_ol, geta(i + 1, j - 2)); if (j < h - 1) a_ol = MAX(a_ol, geta(i + 1, j + 1)); if (j < h - 2) a_ol = MAX(a_ol, geta(i + 1, j + 2)); } if (i < w - 2) { a_ol = MAX(a_ol, geta(i + 2, j)); if (j > 0) a_ol = MAX(a_ol, geta(i + 2, j - 1)); if (j < h - 1) a_ol = MAX(a_ol, geta(i + 2, j + 1)); } } if (outline >= 3) { // Three pixels fake circle if (i > 2) { a_ol = MAX(a_ol, geta(i - 3, j)); if (j > 0) a_ol = MAX(a_ol, geta(i - 3, j - 1)); if (j < h - 1) a_ol = MAX(a_ol, geta(i - 3, j + 1)); } if (i > 1) { if (j > 1) a_ol = MAX(a_ol, geta(i - 2, j - 2)); if (j < h - 2) a_ol = MAX(a_ol, geta(i - 2, j + 2)); } if (i > 0) { if (j > 2) a_ol = MAX(a_ol, geta(i - 1, j - 3)); if (j < h - 3) a_ol = MAX(a_ol, geta(i - 1, j + 3)); } if (j > 2) a_ol = MAX(a_ol, geta(i, j - 3)); if (j < h - 3) a_ol = MAX(a_ol, geta(i, j + 3)); if (i < w - 1) { if (j > 2) a_ol = MAX(a_ol, geta(i + 1, j - 3)); if (j < h - 3) a_ol = MAX(a_ol, geta(i + 1, j + 3)); } if (i < w - 2) { if (j > 1) a_ol = MAX(a_ol, geta(i + 2, j - 2)); if (j < h - 2) a_ol = MAX(a_ol, geta(i + 2, j + 2)); } if (i < w - 3) { a_ol = MAX(a_ol, geta(i + 3, j)); if (j > 0) a_ol = MAX(a_ol, geta(i + 3, j - 1)); if (j < h - 1) a_ol = MAX(a_ol, geta(i + 3, j + 1)); } } a_fg = (float) bitmap->buffer[j * bitmap->pitch + i] / 255.0; *d++ = (int) (a_fg * fg.r + (1 - a_fg) * (a_ol * ol.r + (1 - a_ol) * bg.r)); *d++ = (int) (a_fg * fg.g + (1 - a_fg) * (a_ol * ol.g + (1 - a_ol) * bg.g)); *d++ = (int) (a_fg * fg.b + (1 - a_fg) * (a_ol * ol.b + (1 - a_ol) * bg.b)); *d++ = (int) (a_fg * fg.a + (1 - a_fg) * (a_ol * ol.a + (1 - a_ol) * bg.a)); } dest += stride; } } static void on_fontmap_reload() { PangoFT2FontMap *new_fontmap = NULL, *old_fontmap = NULL; FcInitReinitialize(); new_fontmap = (PangoFT2FontMap *) pango_ft2_font_map_new(); pthread_mutex_lock(&pango_mutex); old_fontmap = fontmap; fontmap = new_fontmap; pthread_mutex_unlock(&pango_mutex); if (old_fontmap) g_object_unref(old_fontmap); } mlt-7.38.0/src/modules/gdk/producer_pango.yml000664 000000 000000 00000021463 15172202314 021126 0ustar00rootroot000000 000000 schema_version: 0.3 type: producer identifier: pango title: Pango version: 3 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > A title generator that uses the Pango international text layout and Freetype2 font renderer. notes: > Supplying a filename with extension ".txt" causes the loader producer to load with pango. If the filename begins with "+" the pango producer interprets the filename as pango text. This is a shortcut to embed titles in melt commands. For MLT XML, it is recommended that you embed the title text in the property value. Pango has builtin scaling. It will rescale the originally rendered title to whatever the consumer requests. Therefore, it will lose its aspect ratio if so requested, and it is up to the consumer to request a proper width and height that maintains the image aspect. Environment variable MLT_PANGO_PRODUCER_CACHE could be used to override and increase the size of cached converted images of simultaneous use. Fontset used by pango producer loaded once. That behavior prevents using new fonts till process used pango producer been restarted. To force fontmap reload you need to send signal "fontmap-reload" to pango producer: { mlt_profile profile = mlt_profile_init("dv_pal"); mlt_producer producer = mlt_factory_producer(profile, "pango", NULL); mlt_events_fire(mlt_producer_properties(producer), "fontmap-reload", NULL ); mlt_producer_close(producer); mlt_profile_close(profile); }; parameters: - identifier: resource title: File type: string description: | A text file containing Pango markup, see: https://developer.gnome.org/pango/stable/PangoMarkupFormat.html requires xml-like encoding special chars from: <, >, & -to- <, >, & readonly: no argument: yes mutable: no widget: fileopen - identifier: markup title: Markup type: string description: | A string containing Pango markup see: http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html requires xml-like encoding special chars from: <, >, & -to- <, >, & readonly: no argument: yes mutable: yes widget: textbox - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner unit: pixels - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: align title: Paragraph alignment type: string description: > left, centre, right (also, numbers 0, 1 and 2 can be used respectively) readonly: no default: left mutable: yes widget: combo - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner unit: pixels - identifier: text title: Text type: string description: | A non-markup string in UTF-8 encoding (can contain markup chars un-encoded) readonly: no mutable: yes widget: textbox - identifier: family title: Font family type: string description: > The default typeface to use when not using markup. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font to use when not using markup. default: 48 readonly: no mutable: yes widget: spinner unit: pixels - identifier: style title: Font style type: string description: > The style of the font to use when not using markup. values: - normal - italic - oblique default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: encoding title: Encoding type: string description: > The text encoding type of the input if not UTF-8. See 'iconv --list' for a list of possible inputs. default: UTF-8 readonly: no mutable: yes widget: combo - identifier: real_width title: Real width type: integer description: The original, unscaled width of the rendered title. readonly: yes unit: pixels - identifier: real_height title: Real height type: integer description: The original, unscaled height of the rendered title. readonly: yes unit: pixels - identifier: width title: Width type: integer description: The last requested scaled image width. readonly: yes unit: pixels - identifier: height title: Height type: integer description: The last requested scaled image height. readonly: yes unit: pixels - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio mutable: yes - identifier: rotate title: Rotation angle type: integer description: > The angle of text rotation in degrees. Positive value is clockwise. default: 0 readonly: no mutable: yes widget: spinner unit: degrees - identifier: width_crop title: Width to crop type: integer description: > Limit width of rendered image. default: 0 readonly: no mutable: yes widget: spinner unit: pixels - identifier: width_fit title: Fit width type: integer description: > Scale pango layout to fit specified width. default: 0 readonly: no mutable: yes widget: spinner unit: pixels - identifier: line_spacing title: Sets lines spacing type: integer description: > Sets the amount of spacing between the lines of the layout. default: 0 readonly: no mutable: yes widget: spinner - identifier: stretch title: Font stretch type: integer description: > The stretch feature of pango's font description. Possible values: 1 - ULTRA_CONDENSED 2 - EXTRA_CONDENSED 3 - CONDENSED 4 - SEMI_CONDENSED 5 - NORMAL 6 - SEMI_EXPANDED 7 - EXPANDED 8 - EXTRA_EXPANDED 9 - ULTRA_EXPANDED minimum: 0 maximum: 9 default: 4 readonly: no mutable: yes widget: spinner - identifier: wrap_width title: Sets the width to wrap to type: integer description: > Sets the width to which the lines of the PangoLayout should wrap. default: 0 readonly: no mutable: yes widget: spinner unit: pixels - identifier: wrap_type title: Sets the wrap mode type: integer description: > Sets the wrap mode; the wrap mode only has effect if a 'wrap_width' is set. Possible values: 0 - wrap lines at word boundaries 1 - wrap lines at character boundaries 2 - wrap lines at word boundaries, but fall back to character boundaries if there is not enough space for a full word default: 0 readonly: no mutable: yes widget: spinner mlt-7.38.0/src/modules/gdk/producer_pixbuf.c000664 000000 000000 00000073773 15172202314 020753 0ustar00rootroot000000 000000 /* * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf * Copyright (C) 2003-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #ifdef USE_EXIF #include #endif #include #include #include #include #include #include #include #include #include #ifndef _MSC_VER #include #endif #ifndef ssize_t #ifdef _WIN32 #include #define ssize_t SSIZE_T #else #define ssize_t ptrdiff_t #endif #endif // this protects concurrent access to gdk_pixbuf static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct producer_pixbuf_s *producer_pixbuf; struct producer_pixbuf_s { struct mlt_producer_s parent; // File name list mlt_properties filenames; mlt_position *outs; int count; int image_idx; int pixbuf_idx; int width; int height; uint8_t *alpha; uint8_t *image; mlt_cache_item image_cache; mlt_cache_item alpha_cache; mlt_cache_item pixbuf_cache; GdkPixbuf *pixbuf; mlt_image_format format; }; static void load_filenames(producer_pixbuf self, mlt_properties producer_properties); static int refresh_pixbuf(producer_pixbuf self, mlt_frame frame); static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int index); static void producer_close(mlt_producer parent); static void refresh_length(mlt_properties properties, producer_pixbuf self) { if (self->count > mlt_properties_get_int(properties, "length") || mlt_properties_get_int(properties, "autolength")) { int ttl = mlt_properties_get_int(properties, "ttl"); mlt_position length = self->count * ttl; mlt_properties_set_position(properties, "length", length); mlt_properties_set_position(properties, "out", length - 1); } } static void on_property_changed(mlt_service owner, mlt_producer producer, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "ttl")) refresh_length(MLT_PRODUCER_PROPERTIES(producer), producer->child); } mlt_producer producer_pixbuf_init(char *filename) { producer_pixbuf self = calloc(1, sizeof(struct producer_pixbuf_s)); if (self != NULL && mlt_producer_init(&self->parent, self) == 0) { mlt_producer producer = &self->parent; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES(&self->parent); // Reject if animation. GError *error = NULL; pthread_mutex_lock(&g_mutex); GdkPixbufAnimation *anim = gdk_pixbuf_animation_new_from_file(filename, &error); if (anim) { gboolean is_anim = !gdk_pixbuf_animation_is_static_image(anim); g_object_unref(anim); if (is_anim) { pthread_mutex_unlock(&g_mutex); mlt_producer_close(&self->parent); free(self); return NULL; } } pthread_mutex_unlock(&g_mutex); // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; // Set the default properties mlt_properties_set(properties, "resource", filename); mlt_properties_set_int(properties, "ttl", 25); mlt_properties_set_int(properties, "aspect_ratio", 1); mlt_properties_set_int(properties, "progressive", 1); mlt_properties_set_int(properties, "seekable", 1); mlt_properties_set_int(properties, "loop", 1); mlt_properties_set_int(properties, "meta.media.progressive", 1); // Validate the resource if (filename) load_filenames(self, properties); if (self->count) { mlt_frame frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (frame) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set_data(frame_properties, "producer_pixbuf", self, 0, NULL, NULL); mlt_frame_set_position(frame, mlt_producer_position(producer)); refresh_pixbuf(self, frame); mlt_cache_item_close(self->pixbuf_cache); mlt_frame_close(frame); } } if (self->width == 0) { producer_close(producer); producer = NULL; } else { mlt_events_listen(properties, self, "property-changed", (mlt_listener) on_property_changed); } return producer; } free(self); return NULL; } static int load_svg(producer_pixbuf self, mlt_properties properties, const char *filename) { int result = 0; // Read xml string if (strstr(filename, " -1) { // Write the svg into the temp file ssize_t remaining_bytes; const char *xml = filename; // Strip leading crap while (xml[0] != '<') xml++; remaining_bytes = strlen(xml); while (remaining_bytes > 0) remaining_bytes -= write(fd, xml + strlen(xml) - remaining_bytes, remaining_bytes); close(fd); mlt_properties_set(self->filenames, "0", fullname); // Teehe - when the producer closes, delete the temp file and the space allo mlt_properties_set_data(properties, "__temporary_file__", fullname, 0, (mlt_destructor) unlink, NULL); result = 1; } } return result; } static int load_sequence_sprintf(producer_pixbuf self, mlt_properties properties, const char *filename) { int result = 0; // Obtain filenames with pattern if (strchr(filename, '%') != NULL) { // handle picture sequences int i = mlt_properties_get_int(properties, "begin"); int gap = 0; char full[1024]; int keyvalue = 0; char key[50]; while (gap < 100) { struct stat buf; snprintf(full, 1023, filename, i++); if (mlt_stat(full, &buf) == 0) { sprintf(key, "%d", keyvalue++); mlt_properties_set(self->filenames, key, full); gap = 0; } else { gap++; } } if (mlt_properties_count(self->filenames) > 0) { mlt_properties_set_int(properties, "ttl", 1); result = 1; } } return result; } static int load_sequence_deprecated(producer_pixbuf self, mlt_properties properties, const char *filename) { int result = 0; const char *start; // This approach is deprecated in favor of the begin querystring parameter. // Obtain filenames with pattern containing a begin value, e.g. foo%1234d.png if ((start = strchr(filename, '%'))) { const char *end = ++start; while (isdigit(*end)) end++; if (end > start && (end[0] == 'd' || end[0] == 'i' || end[0] == 'u')) { int n = end - start; char *s = calloc(1, n + 1); strncpy(s, start, n); mlt_properties_set(properties, "begin", s); free(s); s = calloc(1, strlen(filename) + 2); strncpy(s, filename, start - filename); sprintf(s + (start - filename), ".%d%s", n, end); result = load_sequence_sprintf(self, properties, s); free(s); } } return result; } static int load_sequence_querystring(producer_pixbuf self, mlt_properties properties, const char *filename) { int result = 0; // Obtain filenames with pattern and begin value in query string if (strchr(filename, '%') && strchr(filename, '?')) { // Split filename into pattern and query string char *s = strdup(filename); char *querystring = strrchr(s, '?'); *querystring++ = '\0'; if (strstr(filename, "begin=")) mlt_properties_set(properties, "begin", strstr(querystring, "begin=") + 6); else if (strstr(filename, "begin:")) mlt_properties_set(properties, "begin", strstr(querystring, "begin:") + 6); // Coerce to an int value so serialization does not have any extra query string cruft mlt_properties_set_int(properties, "begin", mlt_properties_get_int(properties, "begin")); result = load_sequence_sprintf(self, properties, s); free(s); } return result; } static int load_folder(producer_pixbuf self, mlt_properties properties, const char *filename) { int result = 0; // Obtain filenames with folder if (strstr(filename, "/.all.") != NULL) { char wildcard[1024]; char *dir_name = strdup(filename); char *extension = strrchr(dir_name, '.'); *(strstr(dir_name, "/.all.") + 1) = '\0'; sprintf(wildcard, "*%s", extension); mlt_properties_dir_list(self->filenames, dir_name, wildcard, 1); free(dir_name); result = 1; } return result; } static int load_sequence_csv(producer_pixbuf self, mlt_properties properties, const char *filename) { int result = 0; const char *csv_extension = strstr(filename, ".csv"); if (csv_extension != NULL && csv_extension[4] == '\0') { FILE *csv = fopen(filename, "r"); if (csv != NULL) { int keyvalue = 0; int out = 0; int nlines = 0; while (!feof(csv)) { char line[1024]; if (fgets(line, 1024, csv) != NULL) { nlines++; } } self->outs = (mlt_position *) malloc(nlines * sizeof(mlt_position)); fseek(csv, 0, SEEK_SET); int index = 0; while (!feof(csv)) { char line[1024]; if (fgets(line, 1024, csv) != NULL) { char *sep = strchr(line, ';'); if (sep != NULL) { int ttl = 0; int n = 0; char key[50]; struct stat buf; *sep++ = '\0'; n = sscanf(sep, "%d", &ttl); if (n == 0) { break; } if (mlt_stat(line, &buf) != 0) { break; } out += ttl; mlt_log_debug(MLT_PRODUCER_SERVICE(&self->parent), "file:'%s' ttl=%d out=%d\n", line, ttl, out); sprintf(key, "%d", keyvalue++); mlt_properties_set(self->filenames, key, line); self->outs[index++] = out; } } } fclose(csv); result = 1; } } return result; } static void load_filenames(producer_pixbuf self, mlt_properties properties) { char *filename = mlt_properties_get(properties, "resource"); self->filenames = mlt_properties_new(); self->outs = NULL; if (!load_svg(self, properties, filename) && !load_sequence_querystring(self, properties, filename) && !load_sequence_sprintf(self, properties, filename) && !load_sequence_deprecated(self, properties, filename) && !load_sequence_csv(self, properties, filename) && !load_folder(self, properties, filename)) { mlt_properties_set(self->filenames, "0", filename); } self->count = mlt_properties_count(self->filenames); refresh_length(properties, self); } static GdkPixbuf *reorient_with_exif(producer_pixbuf self, int image_idx, GdkPixbuf *pixbuf) { #ifdef USE_EXIF mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(&self->parent); ExifData *d = exif_data_new_from_file(mlt_properties_get_value(self->filenames, image_idx)); ExifEntry *entry; int exif_orientation = 0; /* get orientation and rotate image accordingly if necessary */ if (d) { if ((entry = exif_content_get_entry(d->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION))) exif_orientation = exif_get_short(entry->data, exif_data_get_byte_order(d)); /* Free the EXIF data */ exif_data_unref(d); } // Remember EXIF value, might be useful for someone mlt_properties_set_int(producer_props, "_exif_orientation", exif_orientation); if (exif_orientation > 1) { GdkPixbuf *processed = NULL; GdkPixbufRotation matrix = GDK_PIXBUF_ROTATE_NONE; // Rotate image according to exif data switch (exif_orientation) { case 2: processed = gdk_pixbuf_flip(pixbuf, TRUE); break; case 3: matrix = GDK_PIXBUF_ROTATE_UPSIDEDOWN; processed = pixbuf; break; case 4: processed = gdk_pixbuf_flip(pixbuf, FALSE); break; case 5: matrix = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE; processed = gdk_pixbuf_flip(pixbuf, TRUE); break; case 6: matrix = GDK_PIXBUF_ROTATE_CLOCKWISE; processed = pixbuf; break; case 7: matrix = GDK_PIXBUF_ROTATE_CLOCKWISE; processed = gdk_pixbuf_flip(pixbuf, TRUE); break; case 8: matrix = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE; processed = pixbuf; break; } if (processed) { pixbuf = gdk_pixbuf_rotate_simple(processed, matrix); g_object_unref(processed); } } #endif return pixbuf; } static int refresh_pixbuf(producer_pixbuf self, mlt_frame frame) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); // Check if user wants us to reload the image if (mlt_properties_get_int(producer_props, "force_reload")) { self->pixbuf = NULL; self->image = NULL; mlt_properties_set_int(producer_props, "force_reload", 0); } // Get the original position of this frame mlt_position position = mlt_frame_original_position(frame); position += mlt_producer_get_in(producer); int loop = mlt_properties_get_int(producer_props, "loop"); // Image index int current_idx = 0; if (!self->outs) { // Get the time to live for each frame double ttl = mlt_properties_get_int(producer_props, "ttl"); if (loop) { current_idx = (int) floor((double) position / ttl) % self->count; } else { current_idx = MIN((double) position / ttl, self->count - 1); } } else { int total_ttl = (int) floor(self->outs[self->count - 1]); mlt_position looped_pos = loop ? (int) floor(position) % total_ttl : position; while (current_idx < self->count) { if (self->outs[current_idx] > looped_pos) { break; } current_idx++; } if (current_idx >= self->count) { current_idx = self->count - 1; } mlt_log_debug(MLT_PRODUCER_SERVICE(&self->parent), "position=%d current_idx=%d\n", position, current_idx); } int disable_exif = mlt_properties_get_int(producer_props, "disable_exif"); if (current_idx != self->pixbuf_idx) self->pixbuf = NULL; if (!self->pixbuf || mlt_properties_get_int(producer_props, "_disable_exif") != disable_exif) { GError *error = NULL; self->image = NULL; pthread_mutex_lock(&g_mutex); self->pixbuf = gdk_pixbuf_new_from_file(mlt_properties_get_value(self->filenames, current_idx), &error); if (self->pixbuf) { // Read the exif value for this file if (!disable_exif) self->pixbuf = reorient_with_exif(self, current_idx, self->pixbuf); // Register this pixbuf for destruction and reuse mlt_cache_item_close(self->pixbuf_cache); mlt_service_cache_put(MLT_PRODUCER_SERVICE(producer), "pixbuf.pixbuf", self->pixbuf, 0, (mlt_destructor) g_object_unref); self->pixbuf_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "pixbuf.pixbuf"); self->pixbuf_idx = current_idx; // Store the width/height of the pixbuf temporarily self->width = gdk_pixbuf_get_width(self->pixbuf); self->height = gdk_pixbuf_get_height(self->pixbuf); mlt_events_block(producer_props, NULL); mlt_properties_set_int(producer_props, "meta.media.width", self->width); mlt_properties_set_int(producer_props, "meta.media.height", self->height); mlt_properties_set_int(producer_props, "_disable_exif", disable_exif); int has_alpha = gdk_pixbuf_get_has_alpha(self->pixbuf); mlt_properties_set_int(properties, "format", has_alpha ? mlt_image_rgba : mlt_image_rgb); mlt_events_unblock(producer_props, NULL); } pthread_mutex_unlock(&g_mutex); } // Set width/height of frame mlt_properties_set_int(properties, "width", self->width); mlt_properties_set_int(properties, "height", self->height); return current_idx; } static void refresh_image( producer_pixbuf self, mlt_frame frame, mlt_image_format format, int width, int height) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_producer producer = &self->parent; // Get index and pixbuf int current_idx = refresh_pixbuf(self, frame); // optimization for subsequent iterations on single picture if (current_idx != self->image_idx || width != self->width || height != self->height) self->image = NULL; mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "image %p pixbuf %p idx %d current_idx %d pixbuf_idx %d width %d\n", self->image, self->pixbuf, current_idx, self->image_idx, self->pixbuf_idx, width); // If we have a pixbuf and we need an image if (self->pixbuf && (!self->image || (format != mlt_image_none && format != mlt_image_movit && format != self->format))) { char *interps = mlt_properties_get(properties, "consumer.rescale"); if (interps) interps = strdup(interps); int interp = GDK_INTERP_BILINEAR; if (!interps) { // Keep bilinear by default } else if (strcmp(interps, "nearest") == 0) interp = GDK_INTERP_NEAREST; else if (strcmp(interps, "tiles") == 0) interp = GDK_INTERP_TILES; else if (strcmp(interps, "hyper") == 0 || strcmp(interps, "bicubic") == 0) interp = GDK_INTERP_HYPER; free(interps); // Note - the original pixbuf is already safe and ready for destruction pthread_mutex_lock(&g_mutex); GdkPixbuf *pixbuf = gdk_pixbuf_scale_simple(self->pixbuf, width, height, interp); // Store width and height self->width = width; self->height = height; // Allocate/define image int has_alpha = gdk_pixbuf_get_has_alpha(pixbuf); int src_stride = gdk_pixbuf_get_rowstride(pixbuf); int dst_stride = self->width * (has_alpha ? 4 : 3); self->format = has_alpha ? mlt_image_rgba : mlt_image_rgb; int image_size = mlt_image_format_size(self->format, width, height, NULL); self->image = mlt_pool_alloc(image_size); self->alpha = NULL; if (src_stride != dst_stride) { int y = self->height; uint8_t *src = gdk_pixbuf_get_pixels(pixbuf); uint8_t *dst = self->image; while (y--) { memcpy(dst, src, dst_stride); dst += dst_stride; src += src_stride; } } else { memcpy(self->image, gdk_pixbuf_get_pixels(pixbuf), src_stride * height); } pthread_mutex_unlock(&g_mutex); // Convert image to requested format if (format != mlt_image_none && format != mlt_image_movit && format != self->format && frame->convert_image) { // cache copies of the image and alpha buffers uint8_t *buffer = self->image; if (buffer) { mlt_frame_set_image(frame, self->image, image_size, mlt_pool_release); mlt_properties_set_int(properties, "width", self->width); mlt_properties_set_int(properties, "height", self->height); mlt_properties_set_int(properties, "format", self->format); if (!frame->convert_image(frame, &self->image, &self->format, format)) { buffer = self->image; image_size = mlt_image_format_size(self->format, self->width, self->height, NULL); self->image = mlt_pool_alloc(image_size); memcpy(self->image, buffer, mlt_image_format_size(self->format, self->width, self->height, NULL)); } } if ((buffer = mlt_frame_get_alpha(frame))) { self->alpha = mlt_pool_alloc(width * height); memcpy(self->alpha, buffer, width * height); } } // Update the cache mlt_cache_item_close(self->image_cache); mlt_service_cache_put(MLT_PRODUCER_SERVICE(producer), "pixbuf.image", self->image, image_size, mlt_pool_release); self->image_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "pixbuf.image"); self->image_idx = current_idx; mlt_cache_item_close(self->alpha_cache); self->alpha_cache = NULL; if (self->alpha) { mlt_service_cache_put(MLT_PRODUCER_SERVICE(producer), "pixbuf.alpha", self->alpha, width * height, mlt_pool_release); self->alpha_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "pixbuf.alpha"); } // Finished with pixbuf now g_object_unref(pixbuf); } // Set width/height of frame mlt_properties_set_int(properties, "width", self->width); mlt_properties_set_int(properties, "height", self->height); } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(frame); producer_pixbuf self = mlt_properties_get_data(properties, "producer_pixbuf", NULL); mlt_producer producer = &self->parent; // Use the width and height suggested by the rescale filter because we can do our own scaling. if (mlt_properties_get_int(properties, "rescale_width") > 0) *width = mlt_properties_get_int(properties, "rescale_width"); if (mlt_properties_get_int(properties, "rescale_height") > 0) *height = mlt_properties_get_int(properties, "rescale_height"); // Restore pixbuf and image mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); self->pixbuf_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "pixbuf.pixbuf"); self->pixbuf = mlt_cache_item_data(self->pixbuf_cache, NULL); self->image_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "pixbuf.image"); self->image = mlt_cache_item_data(self->image_cache, NULL); self->alpha_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "pixbuf.alpha"); self->alpha = mlt_cache_item_data(self->alpha_cache, NULL); // Refresh the image refresh_image(self, frame, *format, *width, *height); // Get width and height (may have changed during the refresh) *width = self->width; *height = self->height; *format = self->format; // NB: Cloning is necessary with this producer (due to processing of images ahead of use) // The fault is not in the design of mlt, but in the implementation of the pixbuf producer... if (self->image) { // Clone the image int image_size = mlt_image_format_size(self->format, self->width, self->height, NULL); uint8_t *image_copy = mlt_pool_alloc(image_size); memcpy(image_copy, self->image, mlt_image_format_size(self->format, self->width, self->height, NULL)); // Now update properties so we free the copy after mlt_frame_set_image(frame, image_copy, image_size, mlt_pool_release); // We're going to pass the copy on *buffer = image_copy; mlt_log_debug(MLT_PRODUCER_SERVICE(&self->parent), "%dx%d (%s)\n", self->width, self->height, mlt_image_format_name(*format)); // Clone the alpha channel if (self->alpha) { image_copy = mlt_pool_alloc(self->width * self->height); memcpy(image_copy, self->alpha, self->width * self->height); mlt_frame_set_alpha(frame, image_copy, self->width * self->height, mlt_pool_release); } } else { error = 1; } // Release references and locks mlt_cache_item_close(self->pixbuf_cache); mlt_cache_item_close(self->image_cache); mlt_cache_item_close(self->alpha_cache); mlt_service_unlock(MLT_PRODUCER_SERVICE(&self->parent)); return error; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Get the real structure for this producer producer_pixbuf self = producer->child; // Fetch the producers properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); if (self->filenames == NULL && mlt_properties_get(producer_properties, "resource") != NULL) load_filenames(self, producer_properties); // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL && self->count > 0) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); // Set the producer on the frame properties mlt_properties_set_data(properties, "producer_pixbuf", self, 0, NULL, NULL); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Refresh the pixbuf self->pixbuf_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "pixbuf.pixbuf"); self->pixbuf = mlt_cache_item_data(self->pixbuf_cache, NULL); refresh_pixbuf(self, *frame); mlt_cache_item_close(self->pixbuf_cache); // Set producer-specific frame properties mlt_properties_set_int(properties, "progressive", mlt_properties_get_int(producer_properties, "progressive")); double force_ratio = mlt_properties_get_double(producer_properties, "force_aspect_ratio"); if (force_ratio > 0.0) mlt_properties_set_double(properties, "aspect_ratio", force_ratio); else mlt_properties_set_double(properties, "aspect_ratio", mlt_properties_get_double(producer_properties, "aspect_ratio")); // Push the get_image method mlt_frame_push_get_image(*frame, producer_get_image); } // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer parent) { producer_pixbuf self = parent->child; parent->close = NULL; mlt_service_cache_purge(MLT_PRODUCER_SERVICE(parent)); mlt_producer_close(parent); free(self->outs); self->outs = NULL; mlt_properties_close(self->filenames); free(self); } mlt-7.38.0/src/modules/gdk/producer_pixbuf.yml000664 000000 000000 00000007736 15172202314 021326 0ustar00rootroot000000 000000 schema_version: 0.3 type: producer identifier: pixbuf title: GDK-PixBuf version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > A still graphics to video generator using gdk-pixbuf notes: > Pixbuf has builtin scaling. It will rescale the originally rendered title to whatever the consumer requests. Therefore, it will lose its aspect ratio if so requested, and it is up to the consumer to request a proper width and height that maintains the image aspect. Environment variable MLT_PIXBUF_PRODUCER_CACHE could be used to to override /increase the number of cached converted images for simultaneous use. parameters: - identifier: resource title: File type: string description: > The name of a graphics file loadable by a gdk-pixbuf loader. See the output of gdk-pixbuf-query-loaders. Definitely png, jpeg, tiff, pnm, and xpm will work. If "%" in filename, the filename is used with sprintf to generate a filename from a counter for multi-file/flipbook animation. The file sequence ends when numeric discontinuity >100. If the file sequence does not begin within the count of 100 you can pass the begin property like a query string parameter, for example: anim-%04d.png?begin=1000. If filename contains "/.all.", suffix with an extension to load all pictures with matching extension from a directory. If filename contains the string " Reload the file instead of using its cached image. This property automatically resets itself once it has been set 1 and processed. minimum: 0 maximum: 1 mutable: yes - identifier: disable_exif title: Disable auto-rotation type: boolean default: 0 widget: checkbox - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio mutable: yes - identifier: loop title: Loop sequence of images indefinitely description: when 1 (default) loop sequences of images, when 0, play them only once type: boolean default: 1 widget: checkbox - identifier: autolength title: Automatically compute length description: Whether to automatically compute the length and out point for an image sequence. type: boolean default: 0 widget: checkbox mlt-7.38.0/src/modules/gdk/scale_line_22_yuv_mmx.S000664 000000 000000 00000012602 15172202314 021700 0ustar00rootroot000000 000000 /* * scale_line_22_yuv_mmx.S -- scale line in YUY2 format * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ .file "scale_line_22_yuv_mmx.S" .version "01.01" #if !defined(__MINGW32__) && !defined(__CYGWIN__) .section .note.GNU-stack,"",%progbits #endif .extern printf gcc2_compiled.: .data MSG: .ascii "scale_line_22_yuv_mmx: %d %d\n" .text .align 16 #if !defined(__MINGW32__) && !defined(__CYGWIN__) .globl pixops_scale_line_22_yuv_mmx .type pixops_scale_line_22_yuv_mmx,@function pixops_scale_line_22_yuv_mmx: #else .globl _pixops_scale_line_22_yuv_mmx _pixops_scale_line_22_yuv_mmx: #endif /* * Arguments * * weights: 8(%ebp) * p (dest): 12(%ebp) %esi * q1 (src0): 16(%ebp) * q2 (src1): 20(%ebp) * xstep: 24(%ebp) * p_end: 28(%ebp) * xinit: 32(%ebp) * dest_x: 36(%ebp) * */ /* * Function call entry */ pushl %ebp movl %esp,%ebp subl $28,%esp pushl %edi pushl %esi pushl %ebx /* Locals: * int x %ebx * int x_scaled -24(%ebp) * int dest_x 36(%ebp) */ /* * Setup */ /* Initialize variables */ movl 36(%ebp),%eax # destx movl %eax,36(%ebp) movl 32(%ebp),%ebx # x movl 12(%ebp),%esi # dest cmpl 28(%ebp),%esi # dest == dest_end ? jnb .out /* For the body of this loop, %mm0, %mm1, %mm2, %mm3 hold the 4 adjoining * points we are interpolating between, as: * * 00VV00Y200UU00Y1 */ pxor %mm4, %mm4 /* * Load next component values into mm1 (src0) and mm3 (src1) */ movl %ebx, %eax # x_scaled sarl $15, %eax andl $0xfffffffe, %eax movl %eax, %edx # x_aligned andl $0xfffffffc, %edx movl 16(%ebp), %edi # get src0 movl (%edi,%eax), %ecx # get y andl $0x00ff00ff, %ecx # mask off y movl (%edi,%edx), %eax # get uv andl $0xff00ff00, %eax # mask off uv orl %eax, %ecx # composite y, uv movd %ecx, %mm1 # move to mmx1 punpcklbw %mm4, %mm1 movl 20(%ebp), %edi # get src1 movl (%edi,%edx), %ecx # get y andl $0x00ff00ff, %ecx # mask off y movl (%edi,%edx), %eax # get uv andl $0xff00ff00, %eax # mask off uv orl %eax, %ecx # composite y, uv movd %ecx, %mm3 # move to mmx3 punpcklbw %mm4, %mm3 jmp .newx .p2align 4,,7 .loop: /* short *pixel_weights = weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y * 16 4 0xf 2 2 */ movl 8(%ebp), %edi # get weights pointer movl %ebx, %eax andl $0xf000, %eax shrl $7, %eax /* At this point, %edi holds weights. Load the 4 weights into * %mm4,%mm5,%mm6,%mm7, multiply and accumulate. */ movq (%edi,%eax), %mm4 pmullw %mm0, %mm4 movq 8(%edi,%eax), %mm5 pmullw %mm1, %mm5 movq 16(%edi,%eax), %mm6 pmullw %mm2,%mm6 movq 24(%edi,%eax), %mm7 pmullw %mm3,%mm7 paddw %mm4, %mm5 paddw %mm6, %mm7 paddw %mm5, %mm7 /* %mm7 holds the accumulated sum. Compute (C + 0x80) / 256 */ pxor %mm4, %mm4 movl $0x80808080, %eax movd %eax, %mm6 punpcklbw %mm4, %mm6 paddw %mm6, %mm7 psrlw $8, %mm7 /* Pack into %eax and store result */ packuswb %mm7, %mm7 movd %mm7, %eax movb %al, (%esi) # *dest = y movl 36(%ebp), %ecx # get dest_x andl $1, %ecx # select u or v sall $1, %ecx # determine offset addl $1, %ecx # relative to x_aligned sall $3, %ecx # offset * 8 bits/byte movd %mm7, %eax shrl %cl, %eax movb %al, 1(%esi) # *dest = uv addl $2, %esi # dest += 2 cmpl %esi,28(%ebp) # if dest == dest_end je .out # then exit addl $1, 36(%ebp) # dest_x++ .newx: addl 24(%ebp), %ebx # x += x_step /* * Load current component values into mm0 (src0) and mm2 (src1) */ movq %mm1, %mm0 movq %mm3, %mm2 /* * Load next component values into mm1 (src0) and mm3 (src1) */ movl %ebx, %eax # x_scaled sarl $15, %eax andl $0xfffffffe, %eax movl %eax, %edx # x_aligned andl $0xfffffffc, %edx movl 16(%ebp), %edi # get src0 movl (%edi,%eax), %ecx # get y andl $0x00ff00ff, %ecx # mask off y movl (%edi,%edx), %eax # get uv andl $0xff00ff00, %eax # mask off uv orl %eax, %ecx # composite y, uv movd %ecx, %mm1 # move to mmx1 punpcklbw %mm4, %mm1 movl 20(%ebp), %edi # get src1 movl (%edi,%edx), %ecx # get y andl $0x00ff00ff, %ecx # mask off y movl (%edi,%edx), %eax # get uv andl $0xff00ff00, %eax # mask off uv orl %eax, %ecx # composite y, uv movd %ecx, %mm3 # move to mmx3 punpcklbw %mm4, %mm3 jmp .loop .out: movl %esi,%eax emms leal -40(%ebp),%esp popl %ebx popl %esi popl %edi movl %ebp,%esp popl %ebp ret mlt-7.38.0/src/modules/glaxnimate/000775 000000 000000 00000000000 15172202314 016752 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/glaxnimate/CMakeLists.txt000664 000000 000000 00000004104 15172202314 021511 0ustar00rootroot000000 000000 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) file(GLOB YML "*.yml") add_custom_target(Other_glaxnimate_Files SOURCES ${YML} ) include(GenerateExportHeader) function(mlt_add_glaxnimate_module ARG_TARGET) cmake_parse_arguments(PARSE_ARGV 1 ARG "" "QT_VERSION;DATADIR" "") if ("${ARG_TARGET}" STREQUAL "") message(FATAL_ERROR "mlt_add_glaxnimate_module called without a valid target name.") endif() if (NOT ("${ARG_QT_VERSION}" STREQUAL "6")) message(FATAL_ERROR "mlt_add_glaxnimate_module called without a valid Qt Version (allowed is 6).") endif() if ("${ARG_DATADIR}" STREQUAL "") message(FATAL_ERROR "mlt_add_glaxnimate_module called without a valid data dir name.") endif() set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_subdirectory(glaxnimate/src/core/) add_library(${ARG_TARGET} MODULE producer_glaxnimate.cpp) string(MAKE_C_IDENTIFIER ${ARG_TARGET} EXPORT_MACRO_PREFIX) string(TOUPPER "${EXPORT_MACRO_PREFIX}" EXPORT_MACRO_PREFIX_UPPERCASE) generate_export_header(${ARG_TARGET} EXPORT_FILE_NAME "${EXPORT_MACRO_PREFIX}_export.h" EXPORT_MACRO_NAME "${EXPORT_MACRO_PREFIX_UPPERCASE}_EXPORT" ) target_compile_options(${ARG_TARGET} PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(${ARG_TARGET} PRIVATE mlt++ mlt Qt${ARG_QT_VERSION}::Core Qt${ARG_QT_VERSION}::Widgets Glaxnimate::Core ) target_include_directories(${ARG_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) if(NOT WINDOWS_DEPLOY) target_compile_definitions(${ARG_TARGET} PRIVATE NODEPLOY) endif() set_target_properties(${ARG_TARGET} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS ${ARG_TARGET} LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES producer_glaxnimate.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/${ARG_DATADIR} ) endfunction() if(MOD_GLAXNIMATE_QT6) mlt_add_glaxnimate_module(mltglaxnimate-qt6 QT_VERSION 6 DATADIR glaxnimate-qt6) endif() mlt-7.38.0/src/modules/glaxnimate/glaxnimate/000775 000000 000000 00000000000 15172202314 021103 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/glaxnimate/gpl000664 000000 000000 00000000000 15172202314 017445 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/glaxnimate/producer_glaxnimate.cpp000664 000000 000000 00000026330 15172202314 023516 0ustar00rootroot000000 000000 /* * producer_glaxnimate.cpp -- a Glaxnimate/Qt based producer for MLT * Copyright (C) 2022-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see */ #include #include #include #include #include #include #include #include #include "glaxnimate/io/io_registry.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/module/module.hpp" #include "glaxnimate/renderer/renderer.hpp" #if defined(mltglaxnimate_qt6_EXPORTS) #include "mltglaxnimate_qt6_export.h" #define MLT_GLAXNIMATE_MODULE_EXPORT MLTGLAXNIMATE_QT6_EXPORT #endif using namespace glaxnimate; class Glaxnimate { private: mlt_producer m_producer = nullptr; std::unique_ptr m_document; public: Glaxnimate() { // Initialize default format loaders and Qt meta types glaxnimate::module::initialize(); } mlt_profile m_profile = nullptr; void setProducer(mlt_producer producer) { m_producer = producer; } mlt_producer producer() const { return m_producer; } mlt_service service() const { return MLT_PRODUCER_SERVICE(m_producer); } mlt_properties properties() const { return MLT_PRODUCER_PROPERTIES(m_producer); } glaxnimate::model::Composition *composition() const { auto comps = m_document->assets()->compositions.get(); if (comps->values.empty()) { mlt_log_error(service(), "No compositions in Glaxnimate document\n"); } return comps->values[0]; } QSizeF size() const { return composition()->size(); } int duration() const { auto frames = composition()->animation->last_frame.get() - composition()->animation->first_frame.get(); return toMltFps(frames); } int toMltFps(float frame) const { return qRound(frame / fps() * m_profile->frame_rate_num / m_profile->frame_rate_den); } float toGlaxnimateFps(float frame) const { return frame * fps() * m_profile->frame_rate_den / m_profile->frame_rate_num; } int firstFrame() const { return toMltFps(composition()->animation->first_frame.get()); } float fps() const { return composition()->get_fps(); } int getImage(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; auto pos = mlt_frame_original_position(frame); if (!::qstrcmp("loop", mlt_properties_get(properties(), "eof"))) { pos %= std::max(1, duration()); } pos += toMltFps(composition()->animation->first_frame.get()); auto bg = mlt_properties_get_color(properties(), "background"); auto background = QColor(bg.r, bg.g, bg.b, bg.a); auto image = composition()->render_image(toGlaxnimateFps(pos), {*width, *height}, background); // workaround for Glaxnimate 0.6.0 not using background if (bg.a > 0) { QPainter painter(&image); painter.setCompositionMode(QPainter::CompositionMode_DestinationOver); painter.fillRect(image.rect(), background); } *format = mlt_image_rgba; image = image.convertToFormat(QImage::Format_RGBA8888); int size = mlt_image_format_size(*format, *width, *height, NULL); *buffer = static_cast(mlt_pool_alloc(size)); memcpy(*buffer, image.constBits(), size); error = mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); return error; } bool open(const char *fileName) { auto filename = QString::fromUtf8(fileName); auto importer = io::IoRegistry::instance().from_filename(filename, io::ImportExport::Import); if (!importer) { mlt_log_error(service(), "No suitable importer for %s\n", fileName); return false; } QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { mlt_log_error(service(), "Could not open input file %s for reading\n", fileName); return false; } m_document.reset(new model::Document(filename)); if (!importer->load(m_document.get(), file.readAll(), {}, filename)) { mlt_log_error(service(), "Error loading input file %s\n", fileName); return false; } return true; } }; static bool createQApplicationIfNeeded(mlt_service service) { if (!qApp) { #if defined(Q_OS_WIN) && defined(NODEPLOY) QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR")) + QStringLiteral("/bin")); QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR")) + QStringLiteral("/plugins")); #endif #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) if (getenv("DISPLAY") == 0 && getenv("WAYLAND_DISPLAY") == 0) { const char *qt_qpa = getenv("QT_QPA_PLATFORM"); if (!qt_qpa || strcmp(qt_qpa, "offscreen")) { mlt_log_error( service, "The MLT Glaxnimate module requires a X11 or Wayland environment.\n" "Please either run melt from a session with a display server or use a " "fake X server like xvfb:\n" "xvfb-run -a melt (...)\n"); return false; } } #endif if (!mlt_properties_get(mlt_global_properties(), "qt_argv")) mlt_properties_set(mlt_global_properties(), "qt_argv", "MLT"); static int argc = 1; static char *argv[] = {mlt_properties_get(mlt_global_properties(), "qt_argv")}; new QApplication(argc, argv); const char *localename = mlt_properties_get_lcnumeric(MLT_SERVICE_PROPERTIES(service)); QLocale::setDefault(QLocale(localename)); } return true; } extern "C" { static int get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { auto producer = static_cast(mlt_frame_pop_service(frame)); auto glax = static_cast(producer->child); if (mlt_properties_get_int(glax->properties(), "refresh")) { mlt_properties_clear(glax->properties(), "refresh"); glax->open(mlt_properties_get(glax->properties(), "resource")); if (glax->duration() > mlt_properties_get_int(glax->properties(), "length")) { mlt_properties_set_int(glax->properties(), "length", glax->duration()); } } return glax->getImage(frame, buffer, format, width, height, writable); } static int get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Set frame properties mlt_properties_set_int(frame_properties, "progressive", 1); // Inform framework that this producer creates rgba frames by default mlt_properties_set_int(frame_properties, "format", mlt_image_rgba); double force_ratio = mlt_properties_get_double(MLT_PRODUCER_PROPERTIES(producer), "force_aspect_ratio"); if (force_ratio > 0.0) mlt_properties_set_double(frame_properties, "aspect_ratio", force_ratio); else mlt_properties_set_double(frame_properties, "aspect_ratio", 1.0); mlt_frame_set_position(*frame, mlt_producer_position(producer)); mlt_frame_push_service(*frame, producer); mlt_frame_push_get_image(*frame, get_image); mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer producer) { delete static_cast(producer->child); producer->close = nullptr; mlt_producer_close(producer); } mlt_producer producer_glaxnimate_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Allocate the producer Glaxnimate *glax = new Glaxnimate(); mlt_producer producer = (mlt_producer) calloc(1, sizeof(*producer)); if (!glax || mlt_producer_init(producer, glax) || !createQApplicationIfNeeded(MLT_PRODUCER_SERVICE(producer))) { mlt_producer_close(producer); return NULL; } // If allocated and initializes if (glax->open(arg)) { glax->setProducer(producer); glax->m_profile = profile; producer->close = (mlt_destructor) producer_close; producer->get_frame = get_frame; auto properties = glax->properties(); mlt_properties_set(properties, "resource", arg); mlt_properties_set(properties, "background", "#00000000"); mlt_properties_set_int(properties, "aspect_ratio", 1); mlt_properties_set_int(properties, "progressive", 1); mlt_properties_set_int(properties, "seekable", 1); mlt_properties_set_int(properties, "meta.media.width", glax->size().width()); mlt_properties_set_int(properties, "meta.media.height", glax->size().height()); mlt_properties_set_int(properties, "meta.media.sample_aspect_num", 1); mlt_properties_set_int(properties, "meta.media.sample_aspect_den", 1); mlt_properties_set_double(properties, "meta.media.frame_rate", glax->fps()); mlt_properties_set_int(properties, "out", glax->duration() - 1); mlt_properties_set_int(properties, "length", glax->duration()); mlt_properties_set_int(properties, "first_frame", glax->firstFrame()); mlt_properties_set(properties, "eof", "loop"); } return producer; } static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; const char *service_type = NULL; switch (type) { case mlt_service_producer_type: service_type = "producer"; break; default: return NULL; } snprintf(file, PATH_MAX, "%s/glaxnimate-qt6/%s_%s.yml", mlt_environment("MLT_DATA"), service_type, id); return mlt_properties_parse_yaml(file); } MLT_GLAXNIMATE_MODULE_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_producer_type, "glaxnimate", producer_glaxnimate_init); MLT_REGISTER_METADATA(mlt_service_producer_type, "glaxnimate", metadata, NULL); } } // extern C mlt-7.38.0/src/modules/glaxnimate/producer_glaxnimate.yml000664 000000 000000 00000002105 15172202314 023527 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: glaxnimate title: Glaxnimate Animation version: 1 copyright: Meltytech, LLC license: GPLv3 language: en tags: - Video description: Reads a variety of 2D animations parameters: - identifier: resource argument: yes title: File type: string required: yes mutable: no - identifier: background title: Background color type: color mutable: yes default: '#00000000' - identifier: refresh title: Refresh type: boolean description: > Set this to reload the animation from the resource the next an image is requested. This property is cleared when it has been refreshed. mutable: yes - identifier: meta.media..frame_rate title: Animation frame rate type: float readonly: yes - identifier: first_frame title: First frame number type: integer description: > This returns the first frame number in the animation file as they do not always start with 0. This value is converted to the associated MLT profile frame rate. readonly: yes mlt-7.38.0/src/modules/jackrack/000775 000000 000000 00000000000 15172202314 016372 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/jackrack/CMakeLists.txt000664 000000 000000 00000010200 15172202314 021123 0ustar00rootroot000000 000000 add_library(mltjackrack MODULE factory.c) file(GLOB YML "*.yml") add_custom_target(Other_jackrack_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltjackrack) target_compile_options(mltjackrack PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltjackrack PRIVATE mlt Threads::Threads) target_include_directories(mltjackrack PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) if(TARGET JACK::JACK) target_sources(mltjackrack PRIVATE consumer_jack.c) target_link_libraries(mltjackrack PRIVATE JACK::JACK) target_compile_definitions(mltjackrack PRIVATE WITH_JACK) install(FILES consumer_jack.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack) endif() if(GPL AND TARGET PkgConfig::xml AND TARGET PkgConfig::glib AND ladspa_h_FOUND) target_sources(mltjackrack PRIVATE jack_rack.c jack_rack.h lock_free_fifo.c lock_free_fifo.h plugin.c plugin.h plugin_desc.c plugin_desc.h plugin_mgr.c plugin_mgr.h plugin_settings.c plugin_settings.h process.c process.h producer_ladspa.c filter_ladspa.c ) target_link_libraries(mltjackrack PRIVATE ${CMAKE_DL_LIBS} PkgConfig::xml PkgConfig::glib) if(NOT MSVC) target_link_libraries(mltjackrack PRIVATE m) endif() target_compile_definitions(mltjackrack PRIVATE GPL) if(RELOCATABLE) target_compile_definitions(mltjackrack PRIVATE RELOCATABLE) endif() install(FILES filter_jack.yml blacklist.txt DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack) if(TARGET JACK::JACK) target_sources(mltjackrack PRIVATE filter_jackrack.c) install(FILES filter_jackrack.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack) endif() endif() set_target_properties(mltjackrack PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") add_library(mltladspa MODULE factory.c) generate_export_header(mltladspa) target_compile_options(mltladspa PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltladspa PRIVATE mlt Threads::Threads) if(USE_LV2 AND GPL) target_compile_definitions(mltladspa PRIVATE WITH_LV2) install(FILES filter_lv2.yml producer_lv2.yml lv2blacklist.txt DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack) endif() if(USE_VST2 AND GPL) target_compile_definitions(mltladspa PRIVATE WITH_VST2) install(FILES filter_vst2.yml producer_vst2.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack) endif() if(GPL AND TARGET PkgConfig::xml AND TARGET PkgConfig::glib AND ladspa_h_FOUND) target_sources(mltladspa PRIVATE jack_rack.c jack_rack.h lock_free_fifo.c lock_free_fifo.h plugin.c plugin.h plugin_desc.c plugin_desc.h plugin_mgr.c plugin_mgr.h plugin_settings.c plugin_settings.h process.c process.h producer_ladspa.c filter_ladspa.c ) target_link_libraries(mltladspa PRIVATE ${CMAKE_DL_LIBS} PkgConfig::xml PkgConfig::glib) if(NOT MSVC) target_link_libraries(mltladspa PRIVATE m) endif() target_compile_definitions(mltladspa PRIVATE GPL) if(RELOCATABLE) target_compile_definitions(mltladspa PRIVATE RELOCATABLE) endif() install(FILES filter_ladspa.yml producer_ladspa.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack) if(USE_LV2 AND TARGET PkgConfig::lilv) target_link_libraries(mltladspa PRIVATE PkgConfig::lilv) target_sources(mltladspa PRIVATE filter_lv2.c producer_lv2.c lv2_context.c lv2_context.h lv2_plugin.c lv2_plugin.h lv2_process.c lv2_process.h lv2_plugin_settings.c lv2_plugin_settings.h lv2_urid_helper.h) install(FILES filter_lv2.yml producer_lv2.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack) endif() if(USE_VST2) target_sources(mltladspa PRIVATE filter_vst2.c producer_vst2.c vst2_context.c vst2_context.h vst2_plugin.c vst2_plugin.h vst2_process.c vst2_process.h vst2_plugin_settings.c vst2_plugin_settings.h vestige.h) install(FILES filter_vst2.yml producer_vst2.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack) endif() target_include_directories(mltladspa PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) endif() set_target_properties(mltladspa PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltjackrack mltladspa LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) mlt-7.38.0/src/modules/jackrack/blacklist.txt000664 000000 000000 00000000014 15172202314 021076 0ustar00rootroot000000 000000 dssi-vst.so mlt-7.38.0/src/modules/jackrack/consumer_jack.c000664 000000 000000 00000047206 15172202314 021372 0ustar00rootroot000000 000000 /* * consumer_jack.c -- a JACK audio consumer * Copyright (C) 2011-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #define BUFFER_LEN (204800 * 6) pthread_mutex_t g_activate_mutex = PTHREAD_MUTEX_INITIALIZER; /** This classes definition. */ typedef struct consumer_jack_s *consumer_jack; struct consumer_jack_s { struct mlt_consumer_s parent; jack_client_t *jack; mlt_deque queue; pthread_t thread; int joined; int running; pthread_mutex_t video_mutex; pthread_cond_t video_cond; int playing; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; int counter; jack_ringbuffer_t **ringbuffers; jack_port_t **ports; }; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer parent, mlt_event_data); static int jack_process(jack_nframes_t frames, void *data); /** Constructor */ mlt_consumer consumer_jack_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object consumer_jack self = calloc(1, sizeof(struct consumer_jack_s)); // If no malloc'd and consumer init ok if (self != NULL && mlt_consumer_init(&self->parent, self, profile) == 0) { char name[14]; snprintf(name, sizeof(name), "mlt%d", getpid()); if ((self->jack = jack_client_open(name, JackNullOption, NULL))) { jack_set_process_callback(self->jack, jack_process, self); // Create the queue self->queue = mlt_deque_init(); // Get the parent consumer object mlt_consumer parent = &self->parent; // We have stuff to clean up, so override the close method parent->close = consumer_close; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE(parent); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); // This is the initialisation of the consumer pthread_mutex_init(&self->video_mutex, NULL); pthread_cond_init(&self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set(properties, "rescale", "nearest"); mlt_properties_set(properties, "consumer.deinterlacer", "onefield"); // Default buffer for low latency mlt_properties_set_int(properties, "buffer", 1); // Set frequency from JACK mlt_properties_set_int(properties, "frequency", (int) jack_get_sample_rate(self->jack)); // Set default volume mlt_properties_set_double(properties, "volume", 1.0); // Ensure we don't join on a non-running object self->joined = 1; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; // Initialize the refresh handler pthread_cond_init(&self->refresh_cond, NULL); pthread_mutex_init(&self->refresh_mutex, NULL); mlt_events_listen(MLT_CONSUMER_PROPERTIES(parent), self, "property-changed", (mlt_listener) consumer_refresh_cb); // Return the consumer produced return parent; } } // malloc or consumer init failed free(self); // Indicate failure return NULL; } static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer parent, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (!strcmp(name, "refresh")) { consumer_jack self = parent->child; pthread_mutex_lock(&self->refresh_mutex); self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1; pthread_cond_broadcast(&self->refresh_cond); pthread_mutex_unlock(&self->refresh_mutex); } } static int consumer_start(mlt_consumer parent) { consumer_jack self = parent->child; if (!self->running) { consumer_stop(parent); self->running = 1; self->joined = 0; pthread_create(&self->thread, NULL, consumer_thread, self); } return 0; } static int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_jack self = parent->child; if (self->running && !self->joined) { // Kill the thread and clean up self->joined = 1; self->running = 0; // Unlatch the consumer thread pthread_mutex_lock(&self->refresh_mutex); pthread_cond_broadcast(&self->refresh_cond); pthread_mutex_unlock(&self->refresh_mutex); // Cleanup the main thread #ifndef _WIN32 if (self->thread) #endif pthread_join(self->thread, NULL); // Unlatch the video thread pthread_mutex_lock(&self->video_mutex); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); // Cleanup JACK if (self->playing) jack_deactivate(self->jack); if (self->ringbuffers) { int n = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "channels"); while (n--) { jack_ringbuffer_free(self->ringbuffers[n]); jack_port_unregister(self->jack, self->ports[n]); } mlt_pool_release(self->ringbuffers); } self->ringbuffers = NULL; if (self->ports) mlt_pool_release(self->ports); self->ports = NULL; } return 0; } static int consumer_is_stopped(mlt_consumer parent) { consumer_jack self = parent->child; return !self->running; } static int jack_process(jack_nframes_t frames, void *data) { int error = 0; consumer_jack self = (consumer_jack) data; mlt_properties properties = MLT_CONSUMER_PROPERTIES(&self->parent); int channels = mlt_properties_get_int(properties, "channels"); int i; if (!self->ringbuffers) return 1; for (i = 0; i < channels; i++) { size_t jack_size = (frames * sizeof(float)); size_t ring_size = jack_ringbuffer_read_space(self->ringbuffers[i]); char *dest = jack_port_get_buffer(self->ports[i], frames); jack_ringbuffer_read(self->ringbuffers[i], dest, ring_size < jack_size ? ring_size : jack_size); if (ring_size < jack_size) memset(dest + ring_size, 0, jack_size - ring_size); } return error; } static void initialise_jack_ports(consumer_jack self) { int i; char mlt_name[20], con_name[30]; mlt_properties properties = MLT_CONSUMER_PROPERTIES(&self->parent); const char **ports = NULL; // Propagate these for the Jack processing callback int channels = mlt_properties_get_int(properties, "channels"); // Allocate buffers and ports self->ringbuffers = mlt_pool_alloc(sizeof(jack_ringbuffer_t *) * channels); self->ports = mlt_pool_alloc(sizeof(jack_port_t *) * channels); // Start Jack processing - required before registering ports pthread_mutex_lock(&g_activate_mutex); jack_activate(self->jack); pthread_mutex_unlock(&g_activate_mutex); self->playing = 1; // Register Jack ports for (i = 0; i < channels; i++) { self->ringbuffers[i] = jack_ringbuffer_create(BUFFER_LEN * sizeof(float)); snprintf(mlt_name, sizeof(mlt_name), "out_%d", i + 1); self->ports[i] = jack_port_register(self->jack, mlt_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0); } // Establish connections for (i = 0; i < channels; i++) { snprintf(mlt_name, sizeof(mlt_name), "%s", jack_port_name(self->ports[i])); if (mlt_properties_get(properties, con_name)) snprintf(con_name, sizeof(con_name), "%s", mlt_properties_get(properties, con_name)); else { if (!ports) ports = jack_get_ports(self->jack, NULL, NULL, JackPortIsPhysical | JackPortIsInput); if (ports) strncpy(con_name, ports[i], sizeof(con_name)); else snprintf(con_name, sizeof(con_name), "system:playback_%d", i + 1); con_name[sizeof(con_name) - 1] = '\0'; } mlt_log_verbose(NULL, "JACK connect %s to %s\n", mlt_name, con_name); jack_connect(self->jack, mlt_name, con_name); } if (ports) jack_free(ports); } static int consumer_play_audio(consumer_jack self, mlt_frame frame, int init_audio, int *duration) { // Get the properties of this consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES(&self->parent); mlt_audio_format afmt = mlt_audio_float; // Set the preferred params of the test card signal double speed = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed"); int channels = mlt_properties_get_int(properties, "channels"); int frequency = mlt_properties_get_int(properties, "frequency"); int scrub = mlt_properties_get_int(properties, "scrub_audio"); int samples = mlt_audio_calculate_frame_samples(mlt_properties_get_double(properties, "fps"), frequency, self->counter++); float *buffer; mlt_frame_get_audio(frame, (void **) &buffer, &afmt, &frequency, &channels, &samples); *duration = ((samples * 1000) / frequency); if (mlt_properties_get_int(properties, "audio_off")) { init_audio = 1; return init_audio; } if (init_audio == 1) { self->playing = 0; initialise_jack_ports(self); init_audio = 0; } if (init_audio == 0 && (speed == 1.0 || speed == 0.0)) { int i; size_t mlt_size = samples * sizeof(float); float volume = mlt_properties_get_double(properties, "volume"); if (!scrub && speed == 0.0) volume = 0.0; if (volume != 1.0) { float *p = buffer; i = samples * channels + 1; while (--i) *p++ *= volume; } // Write into output ringbuffer for (i = 0; i < channels; i++) { size_t ring_size = jack_ringbuffer_write_space(self->ringbuffers[i]); if (ring_size >= mlt_size) jack_ringbuffer_write(self->ringbuffers[i], (char *) (buffer + i * samples), mlt_size); } } return init_audio; } static int consumer_play_video(consumer_jack self, mlt_frame frame) { // Get the properties of this consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES(&self->parent); if (self->running && !mlt_consumer_is_stopped(&self->parent)) { mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } return 0; } static void *video_thread(void *arg) { // Identify the arg consumer_jack self = arg; // Obtain time of thread start struct timeval now; int64_t start = 0; int64_t elapsed = 0; struct timespec tm; mlt_frame next = NULL; mlt_properties properties = NULL; double speed = 0; // Get real time flag int real_time = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(&self->parent), "real_time"); // Get the current time gettimeofday(&now, NULL); // Determine start time start = (int64_t) now.tv_sec * 1000000 + now.tv_usec; while (self->running) { // Pop the next frame pthread_mutex_lock(&self->video_mutex); next = mlt_deque_pop_front(self->queue); while (next == NULL && self->running) { pthread_cond_wait(&self->video_cond, &self->video_mutex); next = mlt_deque_pop_front(self->queue); } pthread_mutex_unlock(&self->video_mutex); if (!self->running || next == NULL) break; // Get the properties properties = MLT_FRAME_PROPERTIES(next); // Get the speed of the frame speed = mlt_properties_get_double(properties, "_speed"); // Get the current time gettimeofday(&now, NULL); // Get the elapsed time elapsed = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - start; // See if we have to delay the display of the current frame if (mlt_properties_get_int(properties, "rendered") == 1 && self->running) { // Obtain the scheduled playout time int64_t scheduled = mlt_properties_get_int(properties, "playtime"); // Determine the difference between the elapsed time and the scheduled playout time int64_t difference = scheduled - elapsed; // Smooth playback a bit if (real_time && (difference > 20000 && speed == 1.0)) { tm.tv_sec = difference / 1000000; tm.tv_nsec = (difference % 1000000) * 500; nanosleep(&tm, NULL); } // Show current frame if not too old if (!real_time || (difference > -10000 || speed != 1.0 || mlt_deque_count(self->queue) < 2)) consumer_play_video(self, next); // If the queue is empty, recalculate start to allow build up again if (real_time && (mlt_deque_count(self->queue) == 0 && speed == 1.0)) { gettimeofday(&now, NULL); start = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - scheduled + 20000; } } // This frame can now be closed mlt_frame_close(next); next = NULL; } if (next != NULL) mlt_frame_close(next); mlt_consumer_stopped(&self->parent); return NULL; } /** Threaded wrapper for pipe. */ static void *consumer_thread(void *arg) { // Identify the arg consumer_jack self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES(consumer); // Video thread pthread_t thread; // internal initialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; mlt_properties properties = NULL; int duration = 0; int64_t playtime = 0; struct timespec tm = {0, 100000}; // int last_position = -1; pthread_mutex_lock(&self->refresh_mutex); self->refresh_count = 0; pthread_mutex_unlock(&self->refresh_mutex); // Loop until told not to while (self->running) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame(consumer); // Ensure that we have a frame if (frame) { // Get the frame properties properties = MLT_FRAME_PROPERTIES(frame); // Get the speed of the frame double speed = mlt_properties_get_double(properties, "_speed"); // Get refresh request for the current frame int refresh = mlt_properties_get_int(consumer_props, "refresh"); // Clear refresh mlt_events_block(consumer_props, consumer_props); mlt_properties_set_int(consumer_props, "refresh", 0); mlt_events_unblock(consumer_props, consumer_props); // Play audio init_audio = consumer_play_audio(self, frame, init_audio, &duration); // Determine the start time now if (self->playing && init_video) { // Create the video thread pthread_create(&thread, NULL, video_thread, self); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame mlt_properties_set_int(properties, "playtime", playtime); while (self->running && speed != 0 && mlt_deque_count(self->queue) > 15) nanosleep(&tm, NULL); // Push this frame to the back of the video queue if (self->running && speed) { pthread_mutex_lock(&self->video_mutex); mlt_deque_push_back(self->queue, frame); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); // Calculate the next playtime playtime += (duration * 1000); } else if (self->running) { pthread_mutex_lock(&self->refresh_mutex); if (refresh == 0 && self->refresh_count <= 0) { consumer_play_video(self, frame); pthread_cond_wait(&self->refresh_cond, &self->refresh_mutex); } mlt_frame_close(frame); self->refresh_count--; pthread_mutex_unlock(&self->refresh_mutex); } else { mlt_frame_close(frame); frame = NULL; } // Optimisation to reduce latency if (frame && speed == 1.0) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else if (speed == 0.0) { mlt_consumer_purge(consumer); // last_position = -1; } } } // Kill the video thread if (init_video == 0) { pthread_mutex_lock(&self->video_mutex); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); pthread_join(thread, NULL); } while (mlt_deque_count(self->queue)) mlt_frame_close(mlt_deque_pop_back(self->queue)); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_jack self = parent->child; // Stop the consumer mlt_consumer_stop(parent); // Now clean up the rest mlt_consumer_close(parent); // Close the queue mlt_deque_close(self->queue); // Destroy mutexes pthread_mutex_destroy(&self->video_mutex); pthread_cond_destroy(&self->video_cond); pthread_mutex_destroy(&self->refresh_mutex); pthread_cond_destroy(&self->refresh_cond); // Disconnect from JACK jack_client_close(self->jack); // Finally deallocate self free(self); } mlt-7.38.0/src/modules/jackrack/consumer_jack.yml000664 000000 000000 00000002066 15172202314 021744 0ustar00rootroot000000 000000 schema_version: 0.1 type: consumer identifier: jack title: JACK version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio parameters: - identifier: channels title: Channels type: integer minimum: 1 default: 2 - identifier: out_1 title: Send L type: string - identifier: out_2 title: Send R type: string - identifier: volume title: Volume type: float minimum: 0.0 default: 1.0 - identifier: refresh description: > Applications should set this to update the video frame when paused. type: integer minimum: 0 maximum: 1 - identifier: audio_off title: Audio off type: integer description: If 1, disable audio output mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: scrub_audio title: Audio scrubbing type: integer description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-7.38.0/src/modules/jackrack/factory.c000664 000000 000000 00000102623 15172202314 020211 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #if defined(mltjackrack_EXPORTS) #include "mltjackrack_export.h" #define JACKRACK_MODULE_EXPORT MLTJACKRACK_EXPORT #elif defined(mltladspa_EXPORTS) #include "mltladspa_export.h" #define JACKRACK_MODULE_EXPORT MLTLADSPA_EXPORT #else #define JACKRACK_MODULE_EXPORT #endif extern mlt_consumer consumer_jack_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #ifdef GPL #include "plugin_mgr.h" #include #ifdef WITH_JACK extern mlt_filter filter_jackrack_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #endif extern mlt_filter filter_ladspa_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_ladspa_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #ifdef WITH_LV2 #include /* lv2 extenstions */ #include #include #include #include #include #include #include #include #include extern mlt_filter filter_lv2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_lv2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #endif #ifdef WITH_VST2 #include "vestige.h" extern mlt_filter filter_vst2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_vst2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #endif plugin_mgr_t *g_jackrack_plugin_mgr = NULL; #ifdef WITH_LV2 lv2_mgr_t *g_lv2_plugin_mgr = NULL; #endif #ifdef WITH_VST2 vst2_mgr_t *g_vst2_plugin_mgr = NULL; #endif static void add_port_to_metadata(mlt_properties p, plugin_desc_t *desc, int j) { LADSPA_Data sample_rate = 48000; LADSPA_PortRangeHintDescriptor hint_descriptor = desc->port_range_hints[j].HintDescriptor; mlt_properties_set(p, "title", desc->port_names[j]); if (LADSPA_IS_HINT_INTEGER(hint_descriptor)) { mlt_properties_set(p, "type", "integer"); mlt_properties_set_int(p, "default", plugin_desc_get_default_control_value(desc, j, sample_rate)); } else if (LADSPA_IS_HINT_TOGGLED(hint_descriptor)) { mlt_properties_set(p, "type", "boolean"); mlt_properties_set_int(p, "default", plugin_desc_get_default_control_value(desc, j, sample_rate)); } else { mlt_properties_set(p, "type", "float"); mlt_properties_set_double(p, "default", plugin_desc_get_default_control_value(desc, j, sample_rate)); } /* set upper and lower, possibly adjusted to the sample rate */ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint_descriptor)) { LADSPA_Data lower = desc->port_range_hints[j].LowerBound; if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) lower *= sample_rate; if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { if (lower < FLT_EPSILON) lower = FLT_EPSILON; } mlt_properties_set_double(p, "minimum", lower); } if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) { LADSPA_Data upper = desc->port_range_hints[j].UpperBound; if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) upper *= sample_rate; mlt_properties_set_double(p, "maximum", upper); } if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) mlt_properties_set(p, "description", "logarithmic scale recommended"); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "yes"); } #endif static mlt_properties metadata(mlt_service_type type, const char *id, char *data) { char file[PATH_MAX]; if (type == mlt_service_filter_type) { snprintf(file, PATH_MAX, "%s/jackrack/%s", mlt_environment("MLT_DATA"), strncmp(id, "ladspa.", 7) ? data : "filter_ladspa.yml"); } else { snprintf(file, PATH_MAX, "%s/jackrack/%s", mlt_environment("MLT_DATA"), strncmp(id, "ladspa.", 7) ? data : "producer_ladspa.yml"); } mlt_properties result = mlt_properties_parse_yaml(file); #ifdef GPL if (!strncmp(id, "ladspa.", 7)) { // Annotate the yaml properties with ladspa control port info. plugin_desc_t *desc = plugin_mgr_get_any_desc(g_jackrack_plugin_mgr, strtol(id + 7, NULL, 10)); if (desc) { mlt_properties params = mlt_properties_new(); mlt_properties p; char key[20]; int i; mlt_properties_set(result, "identifier", id); mlt_properties_set(result, "title", desc->name); mlt_properties_set(result, "creator", desc->maker ? desc->maker : "unknown"); mlt_properties_set(result, "description", "LADSPA plugin"); mlt_properties_set_data(result, "parameters", params, 0, (mlt_destructor) mlt_properties_close, NULL); for (i = 0; i < desc->control_port_count; i++) { int j = desc->control_port_indicies[i]; p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); snprintf(key, sizeof(key), "%d", j); mlt_properties_set(p, "identifier", key); add_port_to_metadata(p, desc, j); mlt_properties_set(p, "mutable", "yes"); } for (i = 0; i < desc->status_port_count; i++) { int j = desc->status_port_indicies[i]; p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); snprintf(key, sizeof(key), "%d[*]", j); mlt_properties_set(p, "identifier", key); add_port_to_metadata(p, desc, j); mlt_properties_set(p, "readonly", "yes"); } p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "instances"); mlt_properties_set(p, "title", "Instances"); mlt_properties_set(p, "description", "The number of instances of the plugin that are in use.\n" "MLT will create the number of plugins that are required " "to support the number of audio channels.\n" "Status parameters (readonly) are provided for each instance " "and are accessed by specifying the instance number after the " "identifier (starting at zero).\n" "e.g. 9[0] provides the value of status 9 for the first instance."); mlt_properties_set(p, "type", "integer"); mlt_properties_set(p, "readonly", "yes"); if (type == mlt_service_filter_type) { p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "wetness"); mlt_properties_set(p, "title", "Wet/Dry"); mlt_properties_set(p, "type", "float"); mlt_properties_set_double(p, "default", 1); mlt_properties_set_double(p, "minimum", 0); mlt_properties_set_double(p, "maximum", 1); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "yes"); p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "channel_mask"); mlt_properties_set(p, "title", "Channel Mask"); mlt_properties_set(p, "description", "A bitmask inidicating which channels to affect."); mlt_properties_set(p, "type", "integer"); mlt_properties_set(p, "default", "0xFFFFFFFF"); mlt_properties_set_int(p, "minimum", 0); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "no"); } } } #endif return result; } #ifdef WITH_LV2 static void lv2_add_port_to_metadata(mlt_properties p, lv2_plugin_desc_t *desc, int j) { LADSPA_PortRangeHintDescriptor hint_descriptor = desc->port_range_hints[j].HintDescriptor; mlt_properties_set(p, "title", desc->port_names[j]); if (LADSPA_IS_HINT_INTEGER(hint_descriptor)) { mlt_properties_set(p, "type", "integer"); mlt_properties_set_int(p, "default", (int) desc->def_values[j]); mlt_properties_set_double(p, "minimum", (int) desc->min_values[j]); mlt_properties_set_double(p, "maximum", (int) desc->max_values[j]); } else if (LADSPA_IS_HINT_TOGGLED(hint_descriptor)) { mlt_properties_set(p, "type", "boolean"); mlt_properties_set_int(p, "default", desc->def_values[j]); } else { mlt_properties_set(p, "type", "float"); mlt_properties_set_double(p, "default", desc->def_values[j]); mlt_properties_set_double(p, "minimum", desc->min_values[j]); mlt_properties_set_double(p, "maximum", desc->max_values[j]); } if (LADSPA_IS_HINT_ENUMERATION(hint_descriptor)) { mlt_properties_set(p, "type", "string"); char *str_ptr = strchr(desc->uri, '^'); while (str_ptr != NULL) { *str_ptr++ = ':'; str_ptr = strchr(str_ptr, '^'); } LilvNode *puri_temp = lilv_new_uri(g_lv2_plugin_mgr->lv2_world, desc->uri); str_ptr = strchr(desc->uri, ':'); while (str_ptr != NULL) { *str_ptr++ = '^'; str_ptr = strchr(str_ptr, ':'); } const LilvPlugin *p_temp = lilv_plugins_get_by_uri(g_lv2_plugin_mgr->plugin_list, puri_temp); const LilvPort *port_temp = lilv_plugin_get_port_by_index(p_temp, j); lilv_node_free(puri_temp); mlt_properties values_temp = mlt_properties_new(); mlt_properties_set_data(p, "values", values_temp, 0, (mlt_destructor) mlt_properties_close, NULL); // Fill scalePoints Map LilvScalePoints *sp = lilv_port_get_scale_points(p_temp, port_temp); if (sp) { LILV_FOREACH(scale_points, s, sp) { const LilvScalePoint *p = lilv_scale_points_get(sp, s); const LilvNode *val = lilv_scale_point_get_value(p); if (!lilv_node_is_float(val) && !lilv_node_is_int(val)) { continue; } const float f = lilv_node_as_float(val); char key_temp[20]; if (lilv_node_is_float(val)) { snprintf(key_temp, 20, "%f", f); } else if (lilv_node_is_int(val)) { snprintf(key_temp, 20, "%d", (int) f); } mlt_properties_set(values_temp, key_temp, lilv_node_as_string(lilv_scale_point_get_label(p))); } lilv_scale_points_free(sp); } } if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) mlt_properties_set(p, "scale", "log"); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "yes"); } static mlt_properties lv2_metadata(mlt_service_type type, const char *id, char *data) { char file[PATH_MAX]; if (type == mlt_service_filter_type) { snprintf(file, PATH_MAX, "%s/jackrack/%s", mlt_environment("MLT_DATA"), strncmp(id, "lv2.", 4) ? data : "filter_lv2.yml"); } else { snprintf(file, PATH_MAX, "%s/jackrack/%s", mlt_environment("MLT_DATA"), strncmp(id, "lv2.", 4) ? data : "producer_lv2.yml"); } mlt_properties result = mlt_properties_parse_yaml(file); if (!strncmp(id, "lv2.", 4)) { // Annotate the yaml properties with lv2 control port info. lv2_plugin_desc_t *desc = lv2_mgr_get_any_desc(g_lv2_plugin_mgr, (char *) &id[4]); if (desc) { mlt_properties params = mlt_properties_new(); mlt_properties p; char key[20]; int i; mlt_properties_set(result, "identifier", id); mlt_properties_set(result, "title", desc->name); mlt_properties_set(result, "creator", desc->maker ? desc->maker : "unknown"); mlt_properties_set(result, "description", "LV2 plugin"); mlt_properties_set_data(result, "parameters", params, 0, (mlt_destructor) mlt_properties_close, NULL); for (i = 0; i < desc->control_port_count; i++) { int j = desc->control_port_indicies[i]; p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); snprintf(key, sizeof(key), "%d", j); mlt_properties_set(p, "identifier", key); lv2_add_port_to_metadata(p, desc, j); mlt_properties_set(p, "mutable", "yes"); } for (i = 0; i < desc->status_port_count; i++) { int j = desc->status_port_indicies[i]; p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); snprintf(key, sizeof(key), "%d[*]", j); mlt_properties_set(p, "identifier", key); lv2_add_port_to_metadata(p, desc, j); mlt_properties_set(p, "readonly", "yes"); } p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "instances"); mlt_properties_set(p, "title", "Instances"); mlt_properties_set(p, "description", "The number of instances of the plugin that are in use.\n" "MLT will create the number of plugins that are required " "to support the number of audio channels.\n" "Status parameters (readonly) are provided for each instance " "and are accessed by specifying the instance number after the " "identifier (starting at zero).\n" "e.g. 9[0] provides the value of status 9 for the first instance."); mlt_properties_set(p, "type", "integer"); mlt_properties_set(p, "readonly", "yes"); if (type == mlt_service_filter_type) { p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "wetness"); mlt_properties_set(p, "title", "Wet/Dry"); mlt_properties_set(p, "type", "float"); mlt_properties_set_double(p, "default", 1); mlt_properties_set_double(p, "minimum", 0); mlt_properties_set_double(p, "maximum", 1); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "yes"); p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "channel_mask"); mlt_properties_set(p, "title", "Channel Mask"); mlt_properties_set(p, "description", "A bitmask inidicating which channels to affect."); mlt_properties_set(p, "type", "integer"); mlt_properties_set(p, "default", "0xFFFFFFFF"); mlt_properties_set_int(p, "minimum", 0); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "no"); } } } return result; } #endif #ifdef WITH_VST2 static void vst2_add_port_to_metadata(mlt_properties p, vst2_plugin_desc_t *desc, int j) { LADSPA_Data sample_rate = 48000; LADSPA_PortRangeHintDescriptor hint_descriptor = desc->port_range_hints[j].HintDescriptor; mlt_properties_set(p, "title", desc->port_names[j]); if (LADSPA_IS_HINT_INTEGER(hint_descriptor)) { mlt_properties_set(p, "type", "integer"); mlt_properties_set_int(p, "default", vst2_plugin_desc_get_default_control_value( desc, j - (desc->effect->numInputs + desc->effect->numOutputs), sample_rate)); } else if (LADSPA_IS_HINT_TOGGLED(hint_descriptor)) { mlt_properties_set(p, "type", "boolean"); mlt_properties_set_int(p, "default", vst2_plugin_desc_get_default_control_value( desc, j - (desc->effect->numInputs + desc->effect->numOutputs), sample_rate)); } else { mlt_properties_set(p, "type", "float"); mlt_properties_set_double(p, "default", vst2_plugin_desc_get_default_control_value( desc, j - (desc->effect->numInputs + desc->effect->numOutputs), sample_rate)); mlt_properties_set_double(p, "minimum", 0.0); mlt_properties_set_double(p, "maximum", 1.0); } /* set upper and lower, possibly adjusted to the sample rate */ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint_descriptor)) { LADSPA_Data lower = desc->port_range_hints[j].LowerBound; if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) lower *= sample_rate; if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { if (lower < FLT_EPSILON) lower = FLT_EPSILON; } mlt_properties_set_double(p, "minimum", lower); } if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) { LADSPA_Data upper = desc->port_range_hints[j].UpperBound; if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) upper *= sample_rate; mlt_properties_set_double(p, "maximum", upper); } if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) mlt_properties_set(p, "scale", "log"); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "yes"); } static mlt_properties vst2_metadata(mlt_service_type type, const char *id, char *data) { char file[PATH_MAX]; if (type == mlt_service_filter_type) { snprintf(file, PATH_MAX, "%s/jackrack/%s", mlt_environment("MLT_DATA"), strncmp(id, "vst2.", 5) ? data : "filter_vst2.yml"); } else { snprintf(file, PATH_MAX, "%s/jackrack/%s", mlt_environment("MLT_DATA"), strncmp(id, "vst2.", 5) ? data : "producer_vst2.yml"); } mlt_properties result = mlt_properties_parse_yaml(file); if (!strncmp(id, "vst2.", 5)) { // Annotate the yaml properties with ladspa control port info. vst2_plugin_desc_t *desc = vst2_mgr_get_any_desc(g_vst2_plugin_mgr, strtol(id + 5, NULL, 10)); if (desc) { mlt_properties params = mlt_properties_new(); mlt_properties p; char key[20]; int i; mlt_properties_set(result, "identifier", id); mlt_properties_set(result, "title", desc->name); mlt_properties_set(result, "creator", desc->maker ? desc->maker : "unknown"); mlt_properties_set(result, "description", "VST2 plugin"); mlt_properties_set_data(result, "parameters", params, 0, (mlt_destructor) mlt_properties_close, NULL); for (i = 0; i < desc->control_port_count; i++) { int j = desc->control_port_indicies[i]; p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); snprintf(key, sizeof(key), "%d", j - (desc->effect->numInputs + desc->effect->numOutputs)); mlt_properties_set(p, "identifier", key); vst2_add_port_to_metadata(p, desc, j); mlt_properties_set(p, "mutable", "yes"); } /* for (i = 0; i < desc->status_port_count; i++) { int j = desc->status_port_indicies[i]; p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); snprintf(key, sizeof(key), "%d[*]", j); mlt_properties_set(p, "identifier", key); vst2_add_port_to_metadata(p, desc, j); mlt_properties_set(p, "readonly", "yes"); } */ p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "instances"); mlt_properties_set(p, "title", "Instances"); mlt_properties_set(p, "description", "The number of instances of the plugin that are in use.\n" "MLT will create the number of plugins that are required " "to support the number of audio channels.\n" "Status parameters (readonly) are provided for each instance " "and are accessed by specifying the instance number after the " "identifier (starting at zero).\n" "e.g. 9[0] provides the value of status 9 for the first instance."); mlt_properties_set(p, "type", "integer"); mlt_properties_set(p, "readonly", "yes"); if (type == mlt_service_filter_type) { p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "wetness"); mlt_properties_set(p, "title", "Wet/Dry"); mlt_properties_set(p, "type", "float"); mlt_properties_set_double(p, "default", 1); mlt_properties_set_double(p, "minimum", 0); mlt_properties_set_double(p, "maximum", 1); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "yes"); p = mlt_properties_new(); snprintf(key, sizeof(key), "%d", mlt_properties_count(params)); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", "channel_mask"); mlt_properties_set(p, "title", "Channel Mask"); mlt_properties_set(p, "description", "A bitmask inidicating which channels to affect."); mlt_properties_set(p, "type", "integer"); mlt_properties_set(p, "default", "0xFFFFFFFF"); mlt_properties_set_int(p, "minimum", 0); mlt_properties_set(p, "mutable", "yes"); mlt_properties_set(p, "animation", "no"); } } } return result; } #endif JACKRACK_MODULE_EXPORT MLT_REPOSITORY { #ifdef GPL GSList *list; g_jackrack_plugin_mgr = plugin_mgr_new(); for (list = g_jackrack_plugin_mgr->all_plugins; list; list = g_slist_next(list)) { plugin_desc_t *desc = (plugin_desc_t *) list->data; char *s = malloc(strlen("ladpsa.") + 21); sprintf(s, "ladspa.%lu", desc->id); if (desc->has_input) { MLT_REGISTER(mlt_service_filter_type, s, filter_ladspa_init); MLT_REGISTER_METADATA(mlt_service_filter_type, s, metadata, NULL); } else { MLT_REGISTER(mlt_service_producer_type, s, producer_ladspa_init); MLT_REGISTER_METADATA(mlt_service_producer_type, s, metadata, NULL); } free(s); } mlt_factory_register_for_clean_up(g_jackrack_plugin_mgr, (mlt_destructor) plugin_mgr_destroy); #ifdef WITH_LV2 g_lv2_plugin_mgr = lv2_mgr_new(); char global_lv2_world[20]; snprintf(global_lv2_world, 20, "%p", g_lv2_plugin_mgr->lv2_world); mlt_environment_set("global_lv2_world", global_lv2_world); for (list = g_lv2_plugin_mgr->all_plugins; list; list = g_slist_next(list)) { lv2_plugin_desc_t *desc = (lv2_plugin_desc_t *) list->data; char *s = NULL; s = calloc(1, strlen("lv2.") + strlen(desc->uri) + 1); sprintf(s, "lv2.%s", desc->uri); char *str_ptr = strchr(s, ':'); while (str_ptr != NULL) { *str_ptr++ = '^'; str_ptr = strchr(str_ptr, ':'); } if (desc->has_input) { MLT_REGISTER(mlt_service_filter_type, s, filter_lv2_init); MLT_REGISTER_METADATA(mlt_service_filter_type, s, lv2_metadata, NULL); } else { MLT_REGISTER(mlt_service_producer_type, s, producer_lv2_init); MLT_REGISTER_METADATA(mlt_service_producer_type, s, lv2_metadata, NULL); } if (s) { free(s); } } #endif #ifdef WITH_VST2 g_vst2_plugin_mgr = vst2_mgr_new(); for (list = g_vst2_plugin_mgr->all_plugins; list; list = g_slist_next(list)) { vst2_plugin_desc_t *desc = (vst2_plugin_desc_t *) list->data; char *s = malloc(strlen("vst2.") + 21); sprintf(s, "vst2.%lu", desc->id); if (desc->has_input) { MLT_REGISTER(mlt_service_filter_type, s, filter_vst2_init); MLT_REGISTER_METADATA(mlt_service_filter_type, s, vst2_metadata, NULL); } else { MLT_REGISTER(mlt_service_producer_type, s, producer_vst2_init); MLT_REGISTER_METADATA(mlt_service_producer_type, s, vst2_metadata, NULL); } free(s); } mlt_factory_register_for_clean_up(g_vst2_plugin_mgr, (mlt_destructor) vst2_mgr_destroy); #endif #ifdef WITH_JACK MLT_REGISTER(mlt_service_filter_type, "jack", filter_jackrack_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "jack", metadata, "filter_jack.yml"); MLT_REGISTER(mlt_service_filter_type, "jackrack", filter_jackrack_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "jackrack", metadata, "filter_jackrack.yml"); #endif MLT_REGISTER(mlt_service_filter_type, "ladspa", filter_ladspa_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "ladspa", metadata, "filter_ladspa.yml"); #endif #ifdef WITH_JACK MLT_REGISTER(mlt_service_consumer_type, "jack", consumer_jack_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "jack", metadata, "consumer_jack.yml"); #endif } mlt-7.38.0/src/modules/jackrack/filter_jack.yml000664 000000 000000 00000003267 15172202314 021402 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: jack title: JACK version: 1 copyright: Copyright (C) 2004-2018 Meltytech, LLC license: GPLv2 language: en url: http://www.ladspa.org/ creator: Dan Dennedy tags: - Audio description: Process audio using JACK. notes: > This can be used to receive audio from JACK by connecting only input ports. It can be used to output audio to JACK by connecting only the output ports. Or, you can use it as a filter with something like JACK Rack by connecting both output and input ports to send and receive. You can configure as many channels as you need and repeat the in_1/out_1 pattern for as many channels as you have configured. If you are using a MLT consumer that uses ALSA, then you should start jackd with the dummy driver, e.g.: jackd -ddummy -r48000 -p2048. bugs: - > MLT cannot automatically adapt to the sample rate at which JACK is configured. Please make sure they are configured the same. - Does not automatically reconfigure to the number of channels requested by consumer. - Some effects have a temporal side-effect that may not work well. parameters: - identifier: client_name title: JACK client name type: string argument: yes required: yes description: > Creates a JACK client with the specified name with input and output ports. The name must be 60 characters or less. - identifier: channels title: Channels type: integer minimum: 1 default: 2 - identifier: in_1 title: Receive L type: string - identifier: in_2 title: Receive R type: string - identifier: out_1 title: Send L type: string - identifier: out_2 title: Send R type: string mlt-7.38.0/src/modules/jackrack/filter_jackrack.c000664 000000 000000 00000053441 15172202314 021663 0ustar00rootroot000000 000000 /* * filter_jackrack.c -- filter audio through Jack and/or LADSPA plugins * Copyright (C) 2004-2021 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "jack_rack.h" extern pthread_mutex_t g_activate_mutex; #define BUFFER_LEN 204800 * 6 #define JACKSTATE(x) \ (x == JackTransportStopped ? "stopped" \ : x == JackTransportStarting ? "starting" \ : x == JackTransportRolling ? "rolling" \ : "unknown") static int jack_sync(jack_transport_state_t state, jack_position_t *jack_pos, void *arg) { mlt_filter filter = (mlt_filter) arg; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_position position = mlt_profile_fps(profile) * jack_pos->frame / jack_pos->frame_rate + 0.5; int result = 1; mlt_log_debug(MLT_FILTER_SERVICE(filter), "%s frame %u rate %u pos %d last_pos %d\n", JACKSTATE(state), jack_pos->frame, jack_pos->frame_rate, position, mlt_properties_get_position(properties, "_last_pos")); if (state == JackTransportStopped) { mlt_events_fire(properties, "jack-stopped", mlt_event_data_from_int(position)); mlt_properties_set_int(properties, "_sync_guard", 0); } else if (state == JackTransportStarting) { result = 0; if (!mlt_properties_get_int(properties, "_sync_guard")) { mlt_properties_set_int(properties, "_sync_guard", 1); mlt_events_fire(properties, "jack-started", mlt_event_data_from_int(position)); } else if (position >= mlt_properties_get_position(properties, "_last_pos") - 2) { mlt_properties_set_int(properties, "_sync_guard", 0); result = 1; } } else { mlt_properties_set_int(properties, "_sync_guard", 0); } return result; } static void on_jack_start(mlt_properties owner, mlt_properties properties) { mlt_log_verbose(NULL, "%s\n", __FUNCTION__); jack_client_t *jack_client = mlt_properties_get_data(properties, "jack_client", NULL); jack_transport_start(jack_client); } static void on_jack_stop(mlt_properties owner, mlt_properties properties) { mlt_log_verbose(NULL, "%s\n", __FUNCTION__); jack_client_t *jack_client = mlt_properties_get_data(properties, "jack_client", NULL); jack_transport_stop(jack_client); } static void on_jack_seek(mlt_properties owner, mlt_filter filter, mlt_event_data event_data) { mlt_position position = mlt_event_data_to_int(event_data); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_log_verbose(MLT_FILTER_SERVICE(filter), "%s: %d\n", __FUNCTION__, position); mlt_properties_set_int(properties, "_sync_guard", 1); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); jack_client_t *jack_client = mlt_properties_get_data(properties, "jack_client", NULL); jack_nframes_t jack_frame = jack_get_sample_rate(jack_client); jack_frame *= position / mlt_profile_fps(profile); jack_transport_locate(jack_client, jack_frame); } static void initialise_jack_ports(mlt_properties properties) { int i; char mlt_name[67], rack_name[30]; jack_port_t **port = NULL; jack_client_t *jack_client = mlt_properties_get_data(properties, "jack_client", NULL); jack_nframes_t jack_buffer_size = jack_get_buffer_size(jack_client); // Propagate these for the Jack processing callback int channels = mlt_properties_get_int(properties, "channels"); // Start JackRack if (mlt_properties_get(properties, "src")) { snprintf(rack_name, sizeof(rack_name), "jackrack%d", getpid()); jack_rack_t *jackrack = jack_rack_new(rack_name, mlt_properties_get_int(properties, "channels")); jack_rack_open_file(jackrack, mlt_properties_get(properties, "src")); mlt_properties_set_data(properties, "jackrack", jackrack, 0, (mlt_destructor) jack_rack_destroy, NULL); mlt_properties_set(properties, "_rack_client_name", rack_name); } else { // We have to set this to something to prevent re-initialization. mlt_properties_set_data(properties, "jackrack", jack_client, 0, NULL, NULL); } // Allocate buffers and ports jack_ringbuffer_t **output_buffers = mlt_pool_alloc(sizeof(jack_ringbuffer_t *) * channels); jack_ringbuffer_t **input_buffers = mlt_pool_alloc(sizeof(jack_ringbuffer_t *) * channels); jack_port_t **jack_output_ports = mlt_pool_alloc(sizeof(jack_port_t *) * channels); jack_port_t **jack_input_ports = mlt_pool_alloc(sizeof(jack_port_t *) * channels); float **jack_output_buffers = mlt_pool_alloc(sizeof(float *) * jack_buffer_size); float **jack_input_buffers = mlt_pool_alloc(sizeof(float *) * jack_buffer_size); // Set properties - released inside filter_close mlt_properties_set_data(properties, "output_buffers", output_buffers, sizeof(jack_ringbuffer_t *) * channels, mlt_pool_release, NULL); mlt_properties_set_data(properties, "input_buffers", input_buffers, sizeof(jack_ringbuffer_t *) * channels, mlt_pool_release, NULL); mlt_properties_set_data(properties, "jack_output_ports", jack_output_ports, sizeof(jack_port_t *) * channels, mlt_pool_release, NULL); mlt_properties_set_data(properties, "jack_input_ports", jack_input_ports, sizeof(jack_port_t *) * channels, mlt_pool_release, NULL); mlt_properties_set_data(properties, "jack_output_buffers", jack_output_buffers, sizeof(float *) * channels, mlt_pool_release, NULL); mlt_properties_set_data(properties, "jack_input_buffers", jack_input_buffers, sizeof(float *) * channels, mlt_pool_release, NULL); // Register Jack ports for (i = 0; i < channels; i++) { int in; output_buffers[i] = jack_ringbuffer_create(BUFFER_LEN * sizeof(float)); input_buffers[i] = jack_ringbuffer_create(BUFFER_LEN * sizeof(float)); snprintf(mlt_name, sizeof(mlt_name), "obuf%d", i); mlt_properties_set_data(properties, mlt_name, output_buffers[i], BUFFER_LEN * sizeof(float), (mlt_destructor) jack_ringbuffer_free, NULL); snprintf(mlt_name, sizeof(mlt_name), "ibuf%d", i); mlt_properties_set_data(properties, mlt_name, input_buffers[i], BUFFER_LEN * sizeof(float), (mlt_destructor) jack_ringbuffer_free, NULL); for (in = 0; in < 2; in++) { snprintf(mlt_name, sizeof(mlt_name), "%s_%d", in ? "in" : "out", i + 1); port = (in ? &jack_input_ports[i] : &jack_output_ports[i]); *port = jack_port_register(jack_client, mlt_name, JACK_DEFAULT_AUDIO_TYPE, (in ? JackPortIsInput : JackPortIsOutput) | JackPortIsTerminal, 0); } } // Start Jack processing pthread_mutex_lock(&g_activate_mutex); jack_activate(jack_client); pthread_mutex_unlock(&g_activate_mutex); // Establish connections for (i = 0; i < channels; i++) { int in; for (in = 0; in < 2; in++) { port = (in ? &jack_input_ports[i] : &jack_output_ports[i]); snprintf(mlt_name, sizeof(mlt_name), "%s", jack_port_name(*port)); snprintf(rack_name, sizeof(rack_name), "%s_%d", in ? "in" : "out", i + 1); if (mlt_properties_get(properties, "_rack_client_name")) snprintf(rack_name, sizeof(rack_name), "%s:%s_%d", mlt_properties_get(properties, "_rack_client_name"), in ? "out" : "in", i + 1); else if (mlt_properties_get(properties, rack_name)) snprintf(rack_name, sizeof(rack_name), "%s", mlt_properties_get(properties, rack_name)); else snprintf(rack_name, sizeof(rack_name), "%s:%s_%d", mlt_properties_get(properties, "client_name"), in ? "out" : "in", i + 1); if (in) { mlt_log_verbose(NULL, "JACK connect %s to %s\n", rack_name, mlt_name); jack_connect(jack_client, rack_name, mlt_name); } else { mlt_log_verbose(NULL, "JACK connect %s to %s\n", mlt_name, rack_name); jack_connect(jack_client, mlt_name, rack_name); } } } } static int jack_process(jack_nframes_t frames, void *data) { mlt_filter filter = (mlt_filter) data; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); int channels = mlt_properties_get_int(properties, "channels"); int frame_size = mlt_properties_get_int(properties, "_samples") * sizeof(float); int sync = mlt_properties_get_int(properties, "_sync"); int err = 0; int i; static int total_size = 0; jack_ringbuffer_t **output_buffers = mlt_properties_get_data(properties, "output_buffers", NULL); if (output_buffers == NULL) return 0; jack_ringbuffer_t **input_buffers = mlt_properties_get_data(properties, "input_buffers", NULL); jack_port_t **jack_output_ports = mlt_properties_get_data(properties, "jack_output_ports", NULL); jack_port_t **jack_input_ports = mlt_properties_get_data(properties, "jack_input_ports", NULL); float **jack_output_buffers = mlt_properties_get_data(properties, "jack_output_buffers", NULL); float **jack_input_buffers = mlt_properties_get_data(properties, "jack_input_buffers", NULL); pthread_mutex_t *output_lock = mlt_properties_get_data(properties, "output_lock", NULL); pthread_cond_t *output_ready = mlt_properties_get_data(properties, "output_ready", NULL); for (i = 0; i < channels; i++) { size_t jack_size = (frames * sizeof(float)); size_t ring_size; // Send audio through out port jack_output_buffers[i] = jack_port_get_buffer(jack_output_ports[i], frames); if (!jack_output_buffers[i]) { mlt_log_error(MLT_FILTER_SERVICE(filter), "no buffer for output port %d\n", i); err = 1; break; } ring_size = jack_ringbuffer_read_space(output_buffers[i]); jack_ringbuffer_read(output_buffers[i], (char *) jack_output_buffers[i], ring_size < jack_size ? ring_size : jack_size); if (ring_size < jack_size) memset(&jack_output_buffers[i][ring_size], 0, jack_size - ring_size); // Return audio through in port jack_input_buffers[i] = jack_port_get_buffer(jack_input_ports[i], frames); if (!jack_input_buffers[i]) { mlt_log_error(MLT_FILTER_SERVICE(filter), "no buffer for input port %d\n", i); err = 1; break; } // Do not start returning audio until we have sent first mlt frame if (sync && i == 0 && frame_size > 0) total_size += ring_size; mlt_log_debug(MLT_FILTER_SERVICE(filter), "sync %d frame_size %d ring_size %zu jack_size %zu\n", sync, frame_size, ring_size, jack_size); if (!sync || (frame_size > 0 && total_size >= frame_size)) { ring_size = jack_ringbuffer_write_space(input_buffers[i]); jack_ringbuffer_write(input_buffers[i], (char *) jack_input_buffers[i], ring_size < jack_size ? ring_size : jack_size); if (sync) { // Tell mlt that audio is available pthread_mutex_lock(output_lock); pthread_cond_signal(output_ready); pthread_mutex_unlock(output_lock); // Clear sync phase mlt_properties_set_int(properties, "_sync", 0); } } } // Often jackd does not send the stopped event through the JackSyncCallback jack_client_t *jack_client = mlt_properties_get_data(properties, "jack_client", NULL); jack_position_t jack_pos; jack_transport_state_t state = jack_transport_query(jack_client, &jack_pos); int transport_state = mlt_properties_get_int(properties, "_transport_state"); if (state != transport_state) { mlt_properties_set_int(properties, "_transport_state", state); if (state == JackTransportStopped) jack_sync(state, &jack_pos, filter); } return err; } /** Get the audio. */ static int jackrack_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Get the filter service mlt_filter filter = mlt_frame_pop_audio(frame); // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); int jack_frequency = mlt_properties_get_int(filter_properties, "_sample_rate"); // Get the producer's audio *format = mlt_audio_float; mlt_frame_get_audio(frame, buffer, format, &jack_frequency, channels, samples); // TODO: Deal with sample rate differences if (*frequency != jack_frequency) mlt_log_error(MLT_FILTER_SERVICE(filter), "mismatching frequencies JACK = %d actual = %d\n", jack_frequency, *frequency); *frequency = jack_frequency; // Initialise Jack ports and connections if needed if (mlt_properties_get_int(filter_properties, "_samples") == 0) mlt_properties_set_int(filter_properties, "_samples", *samples); // Get the filter-specific properties jack_ringbuffer_t **output_buffers = mlt_properties_get_data(filter_properties, "output_buffers", NULL); jack_ringbuffer_t **input_buffers = mlt_properties_get_data(filter_properties, "input_buffers", NULL); // pthread_mutex_t *output_lock = mlt_properties_get_data( filter_properties, "output_lock", NULL ); // pthread_cond_t *output_ready = mlt_properties_get_data( filter_properties, "output_ready", NULL ); // Process the audio float *q = (float *) *buffer; size_t size = *samples * sizeof(float); int j; // struct timespec tm = { 0, 0 }; // Write into output ringbuffer for (j = 0; j < *channels; j++) { if (jack_ringbuffer_write_space(output_buffers[j]) >= size) jack_ringbuffer_write(output_buffers[j], (char *) (q + j * *samples), size); } // Synchronization phase - wait for signal from Jack process while (jack_ringbuffer_read_space(input_buffers[*channels - 1]) < size) ; //pthread_cond_wait( output_ready, output_lock ); // Read from input ringbuffer for (j = 0; j < *channels; j++, q++) { if (jack_ringbuffer_read_space(input_buffers[j]) >= size) jack_ringbuffer_read(input_buffers[j], (char *) (q + j * *samples), size); } // help jack_sync() indicate when we are rolling mlt_position pos = mlt_frame_get_position(frame); mlt_properties_set_position(filter_properties, "_last_pos", pos); return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter this, mlt_frame frame) { { mlt_properties properties = MLT_FILTER_PROPERTIES(this); mlt_frame_push_audio(frame, this); mlt_frame_push_audio(frame, jackrack_get_audio); if (!mlt_properties_get_data(properties, "jackrack", NULL)) initialise_jack_ports(properties); } return frame; } static void filter_close(mlt_filter this) { mlt_properties properties = MLT_FILTER_PROPERTIES(this); jack_client_t *jack_client = mlt_properties_get_data(properties, "jack_client", NULL); jack_deactivate(jack_client); jack_client_close(jack_client); this->parent.close = NULL; mlt_service_close(&this->parent); } /** Constructor for the filter. */ mlt_filter filter_jackrack_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter this = mlt_filter_new(); if (this != NULL) { char name[61]; char *jack_client_name; const char *src; jack_status_t status = 0; if (id && arg && !strcmp(id, "jack")) { snprintf(name, sizeof(name), "%s", arg); src = NULL; } else { snprintf(name, sizeof(name), "mlt%d", getpid()); src = arg; } jack_client_t *jack_client = jack_client_open(arg, JackNullOption, &status, NULL); if (jack_client) { if (status & JackNameNotUnique) { jack_client_name = jack_get_client_name(jack_client); strcpy(name, jack_client_name); } mlt_properties properties = MLT_FILTER_PROPERTIES(this); pthread_mutex_t *output_lock = mlt_pool_alloc(sizeof(pthread_mutex_t)); pthread_cond_t *output_ready = mlt_pool_alloc(sizeof(pthread_cond_t)); jack_set_process_callback(jack_client, jack_process, this); jack_set_sync_callback(jack_client, jack_sync, this); jack_set_sync_timeout(jack_client, 5000000); //TODO: jack_on_shutdown( jack_client, jack_shutdown_cb, this ); this->process = filter_process; this->close = filter_close; pthread_mutex_init(output_lock, NULL); pthread_cond_init(output_ready, NULL); mlt_properties_set(properties, "src", src); mlt_properties_set(properties, "client_name", name); mlt_properties_set_data(properties, "jack_client", jack_client, 0, NULL, NULL); mlt_properties_set_int(properties, "_sample_rate", jack_get_sample_rate(jack_client)); mlt_properties_set_data(properties, "output_lock", output_lock, 0, mlt_pool_release, NULL); mlt_properties_set_data(properties, "output_ready", output_ready, 0, mlt_pool_release, NULL); mlt_properties_set_int(properties, "_sync", 1); mlt_properties_set_int(properties, "channels", 2); mlt_events_register(properties, "jack-started"); mlt_events_register(properties, "jack-stopped"); mlt_events_register(properties, "jack-start"); mlt_events_register(properties, "jack-stop"); mlt_events_register(properties, "jack-seek"); mlt_events_listen(properties, properties, "jack-start", (mlt_listener) on_jack_start); mlt_events_listen(properties, properties, "jack-stop", (mlt_listener) on_jack_stop); mlt_events_listen(properties, this, "jack-seek", (mlt_listener) on_jack_seek); mlt_properties_set_position(properties, "_jack_seek", -1); } else { mlt_log_error(NULL, "Failed to connect to JACK server\n"); mlt_filter_close(this); this = NULL; } } return this; } mlt-7.38.0/src/modules/jackrack/filter_jackrack.yml000664 000000 000000 00000003603 15172202314 022235 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: jackrack title: JACK Rack XML (*DEPRECATED*) version: 1 copyright: Copyright (C) 2004-2018 Meltytech, LLC license: GPLv2 language: en url: http://www.ladspa.org/ creator: Dan Dennedy tags: - Audio description: Process audio using JACK. notes: > This can be used to receive audio from JACK by connecting only input ports. It can be used to output audio to JACK by connecting only the output ports. Or, you can use it as a filter with something like JACK Rack by connecting both output and input ports to send and receive. You can configure as many channels as you need and repeat the in_1/out_1 pattern for as many channels as you have configured. If you are using a MLT consumer that uses ALSA, then you should start jackd with the dummy driver, e.g.: jackd -ddummy -r48000 -p2048. The MLT JACK client name uses the format: mlt{pid}. bugs: - > MLT cannot automatically adapt to the sample rate at which JACK is configured. Please make sure they are configured the same. - Does not automatically reconfigure to the number of channels requested by consumer. - Some effects have a temporal side-effect that may not work well. parameters: - identifier: src title: JACK Rack file type: string argument: yes description: > Creates JACK ports and runs a JACK Rack project to process audio through a stack of LADSPA filters. - identifier: client_name title: JACK client name type: string argument: yes readonly: yes description: The generated name of the JACK client. - identifier: channels title: Channels type: integer minimum: 1 default: 2 - identifier: in_1 title: Receive L type: string - identifier: in_2 title: Receive R type: string - identifier: out_1 title: Send L type: string - identifier: out_2 title: Send R type: string mlt-7.38.0/src/modules/jackrack/filter_ladspa.c000664 000000 000000 00000024553 15172202314 021360 0ustar00rootroot000000 000000 /* * filter_ladspa.c -- filter audio through LADSPA plugins * Copyright (C) 2004-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "jack_rack.h" #define BUFFER_LEN (10000) #define MAX_SAMPLE_COUNT (4096) static jack_rack_t *initialise_jack_rack(mlt_properties properties, int channels) { jack_rack_t *jackrack = NULL; char *resource = mlt_properties_get(properties, "resource"); if (!resource && mlt_properties_get(properties, "src")) resource = mlt_properties_get(properties, "src"); // Start JackRack if (resource || mlt_properties_get_int64(properties, "_pluginid")) { // Create JackRack without Jack client name so that it only uses LADSPA jackrack = jack_rack_new(NULL, channels); mlt_properties_set_data(properties, "jackrack", jackrack, 0, (mlt_destructor) jack_rack_destroy, NULL); if (resource) // Load JACK Rack XML file jack_rack_open_file(jackrack, resource); else if (mlt_properties_get_int64(properties, "_pluginid")) { // Load one LADSPA plugin by its UniqueID unsigned long id = mlt_properties_get_int64(properties, "_pluginid"); plugin_desc_t *desc = plugin_mgr_get_any_desc(jackrack->plugin_mgr, id); plugin_t *plugin; if (desc && (plugin = jack_rack_instantiate_plugin(jackrack, desc))) { plugin->enabled = TRUE; process_add_plugin(jackrack->procinfo, plugin); mlt_properties_set_int(properties, "instances", plugin->copies); } else { mlt_log_error(properties, "failed to load plugin %lu\n", id); return jackrack; } if (plugin && plugin->desc && plugin->copies == 0) { // Calculate the number of channels that will work with this plugin int request_channels = plugin->desc->channels; while (request_channels < channels) request_channels += plugin->desc->channels; if (request_channels != channels) { // Try to load again with a compatible number of channels. mlt_log_warning( properties, "Not compatible with %d channels. Requesting %d channels instead.\n", channels, request_channels); jackrack = initialise_jack_rack(properties, request_channels); } else { mlt_log_error(properties, "Invalid plugin configuration: %lu\n", id); return jackrack; } } if (plugin && plugin->desc && plugin->copies) mlt_log_debug(properties, "Plugin Initialized. Channels: %lu\tCopies: %d\tTotal: %lu\n", plugin->desc->channels, plugin->copies, jackrack->channels); } } return jackrack; } /** Get the audio. */ static int ladspa_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int error = 0; // Get the filter service mlt_filter filter = mlt_frame_pop_audio(frame); // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); // Check if the channel configuration has changed int prev_channels = mlt_properties_get_int(filter_properties, "_prev_channels"); if (prev_channels != *channels) { if (prev_channels) { mlt_log_info(MLT_FILTER_SERVICE(filter), "Channel configuration changed. Old: %d New: %d.\n", prev_channels, *channels); mlt_properties_set_data(filter_properties, "jackrack", NULL, 0, (mlt_destructor) NULL, NULL); } mlt_properties_set_int(filter_properties, "_prev_channels", *channels); } // Initialise LADSPA if needed jack_rack_t *jackrack = mlt_properties_get_data(filter_properties, "jackrack", NULL); if (jackrack == NULL) { sample_rate = *frequency; // global inside jack_rack jackrack = initialise_jack_rack(filter_properties, *channels); } if (jackrack && jackrack->procinfo && jackrack->procinfo->chain && mlt_properties_get_int64(filter_properties, "_pluginid")) { plugin_t *plugin = jackrack->procinfo->chain; LADSPA_Data value; int i, c; mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); jackrack->procinfo->channel_mask = (unsigned long) mlt_properties_get_int64(filter_properties, "channel_mask"); // Get the producer's audio *format = mlt_audio_float; mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); // Resize the buffer if necessary. if (*channels < jackrack->channels) { // Add extra channels to satisfy the plugin. // Extra channels in the buffer will be ignored by downstream services. int old_size = mlt_audio_format_size(*format, *samples, *channels); int new_size = mlt_audio_format_size(*format, *samples, jackrack->channels); uint8_t *new_buffer = mlt_pool_alloc(new_size); memcpy(new_buffer, *buffer, old_size); // Put silence in extra channels. memset(new_buffer + old_size, 0, new_size - old_size); mlt_frame_set_audio(frame, new_buffer, *format, new_size, mlt_pool_release); *buffer = new_buffer; } for (i = 0; i < plugin->desc->control_port_count; i++) { // Apply the control port values char key[20]; value = plugin_desc_get_default_control_value(plugin->desc, i, sample_rate); snprintf(key, sizeof(key), "%d", i); if (mlt_properties_get(filter_properties, key)) value = mlt_properties_anim_get_double(filter_properties, key, position, length); for (c = 0; c < plugin->copies; c++) plugin->holders[c].control_memory[i] = value; } plugin->wet_dry_enabled = mlt_properties_get(filter_properties, "wetness") != NULL; if (plugin->wet_dry_enabled) { value = mlt_properties_anim_get_double(filter_properties, "wetness", position, length); for (c = 0; c < jackrack->channels; c++) plugin->wet_dry_values[c] = value; } // Configure the buffers LADSPA_Data **input_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * jackrack->channels); LADSPA_Data **output_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * jackrack->channels); // Some plugins crash with too many frames (samples). // So, feed the plugin with N samples per loop iteration. int samples_offset = 0; int sample_count = MIN(*samples, MAX_SAMPLE_COUNT); for (i = 0; samples_offset < *samples; i++) { int j = 0; for (; j < jackrack->channels; j++) output_buffers[j] = input_buffers[j] = (LADSPA_Data *) *buffer + j * (*samples) + samples_offset; sample_count = MIN(*samples - samples_offset, MAX_SAMPLE_COUNT); // Do LADSPA processing error = process_ladspa(jackrack->procinfo, sample_count, input_buffers, output_buffers); samples_offset += MAX_SAMPLE_COUNT; } mlt_pool_release(input_buffers); mlt_pool_release(output_buffers); // read the status port values for (i = 0; i < plugin->desc->status_port_count; i++) { char key[20]; int p = plugin->desc->status_port_indicies[i]; for (c = 0; c < plugin->copies; c++) { snprintf(key, sizeof(key), "%d[%d]", p, c); value = plugin->holders[c].status_memory[i]; mlt_properties_set_double(filter_properties, key, value); } } } else { // Nothing to do. error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter this, mlt_frame frame) { if (mlt_frame_is_test_audio(frame) == 0) { mlt_frame_push_audio(frame, this); mlt_frame_push_audio(frame, ladspa_get_audio); } return frame; } /** Constructor for the filter. */ mlt_filter filter_ladspa_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter this = mlt_filter_new(); if (this != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(this); this->process = filter_process; mlt_properties_set(properties, "resource", arg); if (!strncmp(id, "ladspa.", 7)) mlt_properties_set(properties, "_pluginid", id + 7); mlt_properties_set_int(properties, "channel_mask", -1); } return this; } mlt-7.38.0/src/modules/jackrack/filter_ladspa.yml000664 000000 000000 00000001772 15172202314 021735 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: ladspa title: LADSPA version: 2 license: GPLv2 language: en url: http://www.ladspa.org/ creator: Dan Dennedy tags: - Audio description: Process audio using LADSPA plugins. notes: > Automatically adapts to the number of channels and sampling rate of the consumer. bugs: - Some effects have a temporal side-effect that may not work well. parameters: - identifier: resource argument: yes title: JACK Rack XML file (*DEPRECATED*) type: string description: > Runs a JACK Rack project to process audio through a stack of LADSPA filters without using JACK. - identifier: channel_mask title: Channel Mask type: integer description: > Which channels to affect. Each bit represents a channel; for example, 1 = channel 0 (left), 2 = channel 1 (right), 3 = both left and right channels. The default is to overwrite all of the channels. required: no minimum: 0 default: 0xFFFFFFFF mutable: yes mlt-7.38.0/src/modules/jackrack/filter_lv2.c000664 000000 000000 00000025053 15172202314 020613 0ustar00rootroot000000 000000 /* * filter_lv2.c -- filter audio through LV2 plugins * Copyright (C) 2024-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "lv2_context.h" #define BUFFER_LEN (10000) #define MAX_SAMPLE_COUNT (4096) static lv2_context_t *initialise_lv2_context(mlt_properties properties, int channels) { lv2_context_t *lv2context = NULL; char *resource = mlt_properties_get(properties, "resource"); if (!resource && mlt_properties_get(properties, "src")) resource = mlt_properties_get(properties, "src"); char *plugin_id = NULL; plugin_id = mlt_properties_get(properties, "_pluginid"); // Start Lv2context if (resource || plugin_id) { // Create Lv2context without Jack client name so that it only uses LV2 lv2context = lv2_context_new(NULL, channels); mlt_properties_set_data(properties, "lv2context", lv2context, 0, (mlt_destructor) lv2_context_destroy, NULL); if (plugin_id) { // Load one LV2 plugin by its URI char *id = plugin_id; lv2_plugin_desc_t *desc = lv2_mgr_get_any_desc(lv2context->plugin_mgr, id); lv2_plugin_t *plugin; if (desc && (plugin = lv2_context_instantiate_plugin(lv2context, desc))) { plugin->enabled = TRUE; lv2_process_add_plugin(lv2context->procinfo, plugin); mlt_properties_set_int(properties, "instances", plugin->copies); } else { //mlt_log_error(properties, "failed to load plugin %lu\n", id); mlt_log_error(properties, "failed to load plugin `%s`\n", id); return lv2context; } if (plugin && plugin->desc && plugin->copies == 0) { // Calculate the number of channels that will work with this plugin int request_channels = plugin->desc->channels; while (request_channels < channels) request_channels += plugin->desc->channels; if (request_channels != channels) { // Try to load again with a compatible number of channels. mlt_log_warning( properties, "Not compatible with %d channels. Requesting %d channels instead.\n", channels, request_channels); lv2context = initialise_lv2_context(properties, request_channels); } else { mlt_log_error(properties, "Invalid plugin configuration: `%s`\n", id); return lv2context; } } if (plugin && plugin->desc && plugin->copies) mlt_log_debug(properties, "Plugin Initialized. Channels: %lu\tCopies: %d\tTotal: %lu\n", plugin->desc->channels, plugin->copies, lv2context->channels); } } return lv2context; } /** Get the audio. */ static int lv2_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int error = 0; // Get the filter service mlt_filter filter = mlt_frame_pop_audio(frame); // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); // Check if the channel configuration has changed int prev_channels = mlt_properties_get_int(filter_properties, "_prev_channels"); if (prev_channels != *channels) { if (prev_channels) { mlt_log_info(MLT_FILTER_SERVICE(filter), "Channel configuration changed. Old: %d New: %d.\n", prev_channels, *channels); mlt_properties_set_data(filter_properties, "lv2context", NULL, 0, (mlt_destructor) NULL, NULL); } mlt_properties_set_int(filter_properties, "_prev_channels", *channels); } // Initialise LV2 if needed lv2_context_t *lv2context = mlt_properties_get_data(filter_properties, "lv2context", NULL); if (lv2context == NULL) { lv2_sample_rate = *frequency; // global inside lv2_context lv2context = initialise_lv2_context(filter_properties, *channels); } char *plugin_id = NULL; plugin_id = mlt_properties_get(filter_properties, "_pluginid"); if (lv2context && lv2context->procinfo && lv2context->procinfo->chain && plugin_id) { lv2_plugin_t *plugin = lv2context->procinfo->chain; LADSPA_Data value; int i, c; mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); lv2context->procinfo->channel_mask = (unsigned long) mlt_properties_get_int64(filter_properties, "channel_mask"); // Get the producer's audio *format = mlt_audio_float; mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); // Resize the buffer if necessary. if (*channels < lv2context->channels) { // Add extra channels to satisfy the plugin. // Extra channels in the buffer will be ignored by downstream services. int old_size = mlt_audio_format_size(*format, *samples, *channels); int new_size = mlt_audio_format_size(*format, *samples, lv2context->channels); uint8_t *new_buffer = mlt_pool_alloc(new_size); memcpy(new_buffer, *buffer, old_size); // Put silence in extra channels. memset(new_buffer + old_size, 0, new_size - old_size); mlt_frame_set_audio(frame, new_buffer, *format, new_size, mlt_pool_release); *buffer = new_buffer; } for (i = 0; i < plugin->desc->control_port_count; i++) { // Apply the control port values char key[20]; value = plugin->desc->def_values[plugin->desc->control_port_indicies[i]]; snprintf(key, sizeof(key), "%d", (int) plugin->desc->control_port_indicies[i]); if (mlt_properties_get(filter_properties, key)) value = mlt_properties_anim_get_double(filter_properties, key, position, length); for (c = 0; c < plugin->copies; c++) { plugin->holders[c].control_memory[i] = value; } } plugin->wet_dry_enabled = mlt_properties_get(filter_properties, "wetness") != NULL; if (plugin->wet_dry_enabled) { value = mlt_properties_anim_get_double(filter_properties, "wetness", position, length); for (c = 0; c < lv2context->channels; c++) plugin->wet_dry_values[c] = value; } // Configure the buffers LADSPA_Data **input_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * lv2context->channels); LADSPA_Data **output_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * lv2context->channels); // Some plugins crash with too many frames (samples). // So, feed the plugin with N samples per loop iteration. int samples_offset = 0; int sample_count = MIN(*samples, MAX_SAMPLE_COUNT); for (i = 0; samples_offset < *samples; i++) { int j = 0; for (; j < lv2context->channels; j++) output_buffers[j] = input_buffers[j] = (LADSPA_Data *) *buffer + j * (*samples) + samples_offset; sample_count = MIN(*samples - samples_offset, MAX_SAMPLE_COUNT); // Do LV2 processing error = process_lv2(lv2context->procinfo, sample_count, input_buffers, output_buffers); samples_offset += MAX_SAMPLE_COUNT; } mlt_pool_release(input_buffers); mlt_pool_release(output_buffers); // read the status port values for (i = 0; i < plugin->desc->status_port_count; i++) { char key[20]; int p = plugin->desc->status_port_indicies[i]; for (c = 0; c < plugin->copies; c++) { snprintf(key, sizeof(key), "%d[%d]", p, c); value = plugin->holders[c].status_memory[i]; mlt_properties_set_double(filter_properties, key, value); } } } else { // Nothing to do. error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter this, mlt_frame frame) { if (mlt_frame_is_test_audio(frame) == 0) { mlt_frame_push_audio(frame, this); mlt_frame_push_audio(frame, lv2_get_audio); } return frame; } /** Constructor for the filter. */ mlt_filter filter_lv2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter this = mlt_filter_new(); /* mlt_filter this = mlt_factory_filter(profile, id, arga); */ if (this != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(this); this->process = filter_process; mlt_properties_set(properties, "resource", arg); if (!strncmp(id, "lv2.", 4)) { mlt_properties_set(properties, "_pluginid", id + 4); } mlt_properties_set_int(properties, "channel_mask", -1); } return this; } mlt-7.38.0/src/modules/jackrack/filter_lv2.yml000664 000000 000000 00000001304 15172202314 021163 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: lv2 title: LV2 version: 2 license: GPLv2 language: en url: http://www.lv2.org/ creator: mr.fantastic tags: - Audio description: Process audio using LV2 plugins. notes: > Automatically adapts to the number of channels and sampling rate of the consumer. parameters: - identifier: channel_mask title: Channel Mask type: integer description: > Which channels to affect. Each bit represents a channel; for example, 1 = channel 0 (left), 2 = channel 1 (right), 3 = both left and right channels. The default is to overwrite all of the channels. required: no minimum: 0 default: 0xFFFFFFFF mlt-7.38.0/src/modules/jackrack/filter_vst2.c000664 000000 000000 00000025661 15172202314 021013 0ustar00rootroot000000 000000 /* * filter_vst2.c -- filter audio through VST2 plugins * Copyright (C) 2024-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "vst2_context.h" #define BUFFER_LEN (10000) #define MAX_SAMPLE_COUNT (4096) static vst2_context_t *initialise_vst2_context(mlt_properties properties, int channels) { vst2_context_t *vst2context = NULL; char *resource = mlt_properties_get(properties, "resource"); if (!resource && mlt_properties_get(properties, "src")) resource = mlt_properties_get(properties, "src"); // Start Vst2context if (resource || mlt_properties_get_int64(properties, "_pluginid")) { // Create Vst2context without Jack client name so that it only uses VST2 vst2context = vst2_context_new(NULL, channels); mlt_properties_set_data(properties, "vst2context", vst2context, 0, (mlt_destructor) vst2_context_destroy, NULL); if (mlt_properties_get_int64(properties, "_pluginid")) { // Load one VST2 plugin by its UniqueID unsigned long id = mlt_properties_get_int64(properties, "_pluginid"); vst2_plugin_desc_t *desc = vst2_mgr_get_any_desc(vst2context->plugin_mgr, id); vst2_plugin_t *plugin; if (desc && (plugin = vst2_context_instantiate_plugin(vst2context, desc))) { plugin->enabled = TRUE; vst2_process_add_plugin(vst2context->procinfo, plugin); mlt_properties_set_int(properties, "instances", plugin->copies); } else { mlt_log_error(properties, "failed to load plugin %lu\n", id); return vst2context; } if (plugin && plugin->desc && plugin->copies == 0) { // Calculate the number of channels that will work with this plugin int request_channels = plugin->desc->channels; while (request_channels < channels) request_channels += plugin->desc->channels; if (request_channels != channels) { // Try to load again with a compatible number of channels. mlt_log_warning( properties, "Not compatible with %d channels. Requesting %d channels instead.\n", channels, request_channels); vst2context = initialise_vst2_context(properties, request_channels); } else { mlt_log_error(properties, "Invalid plugin configuration: %lu\n", id); return vst2context; } } if (plugin && plugin->desc && plugin->copies) mlt_log_debug(properties, "Plugin Initialized. Channels: %lu\tCopies: %d\tTotal: %lu\n", plugin->desc->channels, plugin->copies, vst2context->channels); } } return vst2context; } /** Get the audio. */ static int vst2_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int error = 0; // Get the filter service mlt_filter filter = mlt_frame_pop_audio(frame); // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); // Check if the channel configuration has changed int prev_channels = mlt_properties_get_int(filter_properties, "_prev_channels"); if (prev_channels != *channels) { if (prev_channels) { mlt_log_info(MLT_FILTER_SERVICE(filter), "Channel configuration changed. Old: %d New: %d.\n", prev_channels, *channels); mlt_properties_set_data(filter_properties, "vst2context", NULL, 0, (mlt_destructor) NULL, NULL); } mlt_properties_set_int(filter_properties, "_prev_channels", *channels); } // Initialise VST2 if needed vst2_context_t *vst2context = mlt_properties_get_data(filter_properties, "vst2context", NULL); if (vst2context == NULL) { vst2_sample_rate = *frequency; // global inside jack_rack vst2context = initialise_vst2_context(filter_properties, *channels); } if (vst2context && vst2context->procinfo && vst2context->procinfo->chain && mlt_properties_get_int64(filter_properties, "_pluginid")) { vst2_plugin_t *plugin = vst2context->procinfo->chain; LADSPA_Data value; int i, c; mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); vst2context->procinfo->channel_mask = (unsigned long) mlt_properties_get_int64(filter_properties, "channel_mask"); // Get the producer's audio *format = mlt_audio_float; mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); // Resize the buffer if necessary. if (*channels < vst2context->channels) { // Add extra channels to satisfy the plugin. // Extra channels in the buffer will be ignored by downstream services. int old_size = mlt_audio_format_size(*format, *samples, *channels); int new_size = mlt_audio_format_size(*format, *samples, vst2context->channels); uint8_t *new_buffer = mlt_pool_alloc(new_size); memcpy(new_buffer, *buffer, old_size); // Put silence in extra channels. memset(new_buffer + old_size, 0, new_size - old_size); mlt_frame_set_audio(frame, new_buffer, *format, new_size, mlt_pool_release); *buffer = new_buffer; } for (i = 0; i < plugin->desc->control_port_count; i++) { // Apply the control port values char key[20]; value = vst2_plugin_desc_get_default_control_value(plugin->desc, i, vst2_sample_rate); snprintf(key, sizeof(key), "%d", i); if (mlt_properties_get(filter_properties, key)) value = mlt_properties_anim_get_double(filter_properties, key, position, length); for (c = 0; c < plugin->copies; c++) { if (plugin->holders[c].control_memory[i] != value) { plugin->holders[c].control_memory[i] = value; plugin->holders[c] .effect->setParameter(plugin->holders[c].effect, plugin->desc->control_port_indicies[i] - (plugin->holders[c].effect->numInputs + plugin->holders[c].effect->numOutputs), plugin->holders[c].control_memory[i]); } } } plugin->wet_dry_enabled = mlt_properties_get(filter_properties, "wetness") != NULL; if (plugin->wet_dry_enabled) { value = mlt_properties_anim_get_double(filter_properties, "wetness", position, length); for (c = 0; c < vst2context->channels; c++) plugin->wet_dry_values[c] = value; } // Configure the buffers LADSPA_Data **input_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * vst2context->channels); LADSPA_Data **output_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * vst2context->channels); // Some plugins crash with too many frames (samples). // So, feed the plugin with N samples per loop iteration. int samples_offset = 0; int sample_count = MIN(*samples, MAX_SAMPLE_COUNT); for (i = 0; samples_offset < *samples; i++) { int j = 0; for (; j < vst2context->channels; j++) output_buffers[j] = input_buffers[j] = (LADSPA_Data *) *buffer + j * (*samples) + samples_offset; sample_count = MIN(*samples - samples_offset, MAX_SAMPLE_COUNT); // Do VST2 processing error = process_vst2(vst2context->procinfo, sample_count, input_buffers, output_buffers); samples_offset += MAX_SAMPLE_COUNT; } mlt_pool_release(input_buffers); mlt_pool_release(output_buffers); // read the status port values for (i = 0; i < plugin->desc->status_port_count; i++) { char key[20]; int p = plugin->desc->status_port_indicies[i]; for (c = 0; c < plugin->copies; c++) { snprintf(key, sizeof(key), "%d[%d]", p, c); value = plugin->holders[c].status_memory[i]; mlt_properties_set_double(filter_properties, key, value); } } } else { // Nothing to do. error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter this, mlt_frame frame) { if (mlt_frame_is_test_audio(frame) == 0) { mlt_frame_push_audio(frame, this); mlt_frame_push_audio(frame, vst2_get_audio); } return frame; } /** Constructor for the filter. */ mlt_filter filter_vst2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter this = mlt_filter_new(); if (this != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(this); this->process = filter_process; mlt_properties_set(properties, "resource", arg); if (!strncmp(id, "vst2.", 5)) mlt_properties_set(properties, "_pluginid", id + 5); mlt_properties_set_int(properties, "channel_mask", -1); } return this; } mlt-7.38.0/src/modules/jackrack/filter_vst2.yml000664 000000 000000 00000001352 15172202314 021361 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: vst2 title: VST2 version: 2 license: GPLv2 language: en url: https://www.steinberg.net/en/company/technologies.html creator: mr.fantastic tags: - Audio description: Process audio using VST2 plugins. notes: > Automatically adapts to the number of channels and sampling rate of the consumer. parameters: - identifier: channel_mask title: Channel Mask type: integer description: > Which channels to affect. Each bit represents a channel; for example, 1 = channel 0 (left), 2 = channel 1 (right), 3 = both left and right channels. The default is to overwrite all of the channels. required: no minimum: 0 default: 0xFFFFFFFF mlt-7.38.0/src/modules/jackrack/jack_rack.c000664 000000 000000 00000026714 15172202314 020460 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include "jack_rack.h" #include "lock_free_fifo.h" #include "plugin_settings.h" #include "framework/mlt_log.h" #ifndef _ #define _(x) x #endif #define _x (const xmlChar*) #define _s (const char*) extern plugin_mgr_t *g_jackrack_plugin_mgr; jack_rack_t * jack_rack_new (const char * client_name, unsigned long channels) { jack_rack_t *rack; rack = g_malloc (sizeof (jack_rack_t)); rack->saved_plugins = NULL; rack->channels = channels; rack->procinfo = process_info_new (client_name, channels, FALSE, FALSE); if (!rack->procinfo) { g_free (rack); return NULL; } rack->plugin_mgr = g_jackrack_plugin_mgr; plugin_mgr_set_plugins (rack->plugin_mgr, channels); return rack; } void jack_rack_destroy (jack_rack_t * jack_rack) { process_quit (jack_rack->procinfo); // plugin_mgr is shared and global now, so we do not destroy it with each instance // plugin_mgr_destroy (jack_rack->plugin_mgr); process_info_destroy (jack_rack->procinfo); g_slist_free (jack_rack->saved_plugins); g_free (jack_rack); } plugin_t * jack_rack_instantiate_plugin (jack_rack_t * jack_rack, plugin_desc_t * desc) { plugin_t * plugin; /* check whether or not the plugin is RT capable and confirm with the user if it isn't */ if (!LADSPA_IS_HARD_RT_CAPABLE(desc->properties)) { mlt_log_info( NULL, "Plugin not RT capable. The plugin '%s' does not describe itself as being capable of real-time operation. You may experience drop outs or jack may even kick us out if you use it.\n", desc->name); } /* create the plugin */ plugin = plugin_new (desc, jack_rack); if (!plugin) { mlt_log_error( NULL, "Error loading file plugin '%s' from file '%s'\n", desc->name, desc->object_file); } return plugin; } void jack_rack_add_saved_plugin (jack_rack_t * jack_rack, saved_plugin_t * saved_plugin) { plugin_t * plugin = jack_rack_instantiate_plugin (jack_rack, saved_plugin->settings->desc); if (!plugin) { mlt_log_warning( NULL, "%s: could not instantiate object file '%s'\n", __FUNCTION__, saved_plugin->settings->desc->object_file); return; } jack_rack->saved_plugins = g_slist_append (jack_rack->saved_plugins, saved_plugin); process_add_plugin (jack_rack->procinfo, plugin); jack_rack_add_plugin (jack_rack, plugin); } void jack_rack_add_plugin (jack_rack_t * jack_rack, plugin_t * plugin) { saved_plugin_t * saved_plugin = NULL; GSList * list; unsigned long control, channel; LADSPA_Data value; guint copy; /* see if there's any saved settings that match the plugin id */ for (list = jack_rack->saved_plugins; list; list = g_slist_next (list)) { saved_plugin = list->data; if (saved_plugin->settings->desc->id == plugin->desc->id) { /* process the settings! */ jack_rack->saved_plugins = g_slist_remove (jack_rack->saved_plugins, saved_plugin); break; } saved_plugin = NULL; } if ( !saved_plugin ) return; /* initialize plugin parameters */ plugin->enabled = settings_get_enabled (saved_plugin->settings); plugin->wet_dry_enabled = settings_get_wet_dry_enabled (saved_plugin->settings); for (control = 0; control < saved_plugin->settings->desc->control_port_count; control++) for (copy = 0; copy < plugin->copies; copy++) { value = settings_get_control_value (saved_plugin->settings, copy, control); plugin->holders[copy].control_memory[control] = value; //mlt_log_debug( NULL, "setting control value %s (%d) = %f\n", saved_plugin->settings->desc->port_names[control], copy, value); // lff_write (plugin->holders[copy].ui_control_fifos + control, &value); } if (plugin->wet_dry_enabled) for (channel = 0; channel < jack_rack->channels; channel++) { value = settings_get_wet_dry_value (saved_plugin->settings, channel); plugin->wet_dry_values[channel] = value; //mlt_log_debug( NULL, "setting wet/dry value %d = %f\n", channel, value); // lff_write (plugin->wet_dry_fifos + channel, &value); } } static void saved_rack_parse_plugin (jack_rack_t * jack_rack, saved_rack_t * saved_rack, saved_plugin_t * saved_plugin, const char * filename, xmlNodePtr plugin) { plugin_desc_t * desc; settings_t * settings = NULL; xmlNodePtr node; xmlNodePtr sub_node; xmlChar *content; unsigned long num; unsigned long control = 0; #ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif for (node = plugin->children; node; node = node->next) { if (xmlStrcmp (node->name, _x("id")) == 0) { content = xmlNodeGetContent (node); num = strtoul (_s(content), NULL, 10); xmlFree (content); desc = plugin_mgr_get_any_desc (jack_rack->plugin_mgr, num); if (!desc) { mlt_log_verbose( NULL, _("The file '%s' contains an unknown plugin with ID '%ld'; skipping\n"), filename, num); return; } settings = settings_new (desc, saved_rack->channels, saved_rack->sample_rate); } else if (xmlStrcmp (node->name, _x("enabled")) == 0) { content = xmlNodeGetContent (node); settings_set_enabled (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE); xmlFree (content); } else if (xmlStrcmp (node->name, _x("wet_dry_enabled")) == 0) { content = xmlNodeGetContent (node); settings_set_wet_dry_enabled (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE); xmlFree (content); } else if (xmlStrcmp (node->name, _x("wet_dry_locked")) == 0) { content = xmlNodeGetContent (node); settings_set_wet_dry_locked (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE); xmlFree (content); } else if (xmlStrcmp (node->name, _x("wet_dry_values")) == 0) { unsigned long channel = 0; for (sub_node = node->children; sub_node; sub_node = sub_node->next) { if (xmlStrcmp (sub_node->name, _x("value")) == 0) { content = xmlNodeGetContent (sub_node); settings_set_wet_dry_value (settings, channel, strtod (_s(content), NULL)); xmlFree (content); channel++; } } } else if (xmlStrcmp (node->name, _x("lockall")) == 0) { content = xmlNodeGetContent (node); settings_set_lock_all (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE); xmlFree (content); } else if (xmlStrcmp (node->name, _x("controlrow")) == 0) { gint copy = 0; for (sub_node = node->children; sub_node; sub_node = sub_node->next) { if (xmlStrcmp (sub_node->name, _x("lock")) == 0) { content = xmlNodeGetContent (sub_node); settings_set_lock (settings, control, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE); xmlFree (content); } else if (xmlStrcmp (sub_node->name, _x("value")) == 0) { content = xmlNodeGetContent (sub_node); settings_set_control_value (settings, copy, control, strtod (_s(content), NULL)); xmlFree (content); copy++; } } control++; } } if (settings) saved_plugin->settings = settings; } static void saved_rack_parse_jackrack (jack_rack_t * jack_rack, saved_rack_t * saved_rack, const char * filename, xmlNodePtr jackrack) { xmlNodePtr node; xmlChar *content; saved_plugin_t * saved_plugin; #ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif for (node = jackrack->children; node; node = node->next) { if (xmlStrcmp (node->name, _x("channels")) == 0) { content = xmlNodeGetContent (node); saved_rack->channels = strtoul (_s(content), NULL, 10); xmlFree (content); } else if (xmlStrcmp (node->name, _x("samplerate")) == 0) { content = xmlNodeGetContent (node); saved_rack->sample_rate = strtoul (_s(content), NULL, 10); xmlFree (content); } else if (xmlStrcmp (node->name, _x("plugin")) == 0) { saved_plugin = g_malloc0 (sizeof (saved_plugin_t)); saved_rack->plugins = g_slist_append (saved_rack->plugins, saved_plugin); saved_rack_parse_plugin (jack_rack, saved_rack, saved_plugin, filename, node); } } } static saved_rack_t * saved_rack_new (jack_rack_t * jack_rack, const char * filename, xmlDocPtr doc) { xmlNodePtr node; saved_rack_t *saved_rack; /* create the saved rack */ saved_rack = g_malloc (sizeof (saved_rack_t)); saved_rack->plugins = NULL; saved_rack->sample_rate = 48000; saved_rack->channels = 2; for (node = doc->children; node; node = node->next) { if (xmlStrcmp (node->name, _x("jackrack")) == 0) saved_rack_parse_jackrack (jack_rack, saved_rack, filename, node); } return saved_rack; } static void saved_rack_destroy (saved_rack_t * saved_rack) { GSList * list; for (list = saved_rack->plugins; list; list = g_slist_next (list)) settings_destroy (((saved_plugin_t *) list->data)->settings); g_slist_free (saved_rack->plugins); g_free (saved_rack); } int jack_rack_open_file (jack_rack_t * jack_rack, const char * filename) { xmlDocPtr doc; saved_rack_t * saved_rack; GSList * list; saved_plugin_t * saved_plugin; doc = xmlParseFile (filename); if (!doc) { mlt_log_error( NULL, _("Could not parse file '%s'\n"), filename); return 1; } if (xmlStrcmp ( ((xmlDtdPtr)doc->children)->name, _x("jackrack")) != 0) { mlt_log_error( NULL, _("The file '%s' is not a JACK Rack settings file\n"), filename); return 1; } saved_rack = saved_rack_new (jack_rack, filename, doc); xmlFreeDoc (doc); if (!saved_rack) return 1; for (list = saved_rack->plugins; list; list = g_slist_next (list)) { saved_plugin = list->data; settings_set_sample_rate (saved_plugin->settings, sample_rate); jack_rack_add_saved_plugin (jack_rack, saved_plugin); } saved_rack_destroy (saved_rack); return 0; } /* EOF */ mlt-7.38.0/src/modules/jackrack/jack_rack.h000664 000000 000000 00000003741 15172202314 020460 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JR_JACK_RACK_H__ #define __JR_JACK_RACK_H__ #include #include #include "plugin.h" #include "plugin_mgr.h" #include "plugin_settings.h" #include "process.h" typedef struct _saved_plugin saved_plugin_t; struct _saved_plugin { settings_t *settings; }; typedef struct _saved_rack saved_rack_t; struct _saved_rack { unsigned long channels; jack_nframes_t sample_rate; GSList * plugins; }; typedef struct _jack_rack jack_rack_t; struct _jack_rack { plugin_mgr_t * plugin_mgr; process_info_t * procinfo; unsigned long channels; GSList * saved_plugins; }; jack_rack_t * jack_rack_new (const char * client_name, unsigned long channels); void jack_rack_destroy (jack_rack_t * jack_rack); int jack_rack_open_file (jack_rack_t * jack_rack, const char * filename); void jack_rack_add_plugin (jack_rack_t * jack_rack, plugin_t * plugin); void jack_rack_add_saved_plugin (jack_rack_t * jack_rack, struct _saved_plugin * saved_plugin); plugin_t * jack_rack_instantiate_plugin (jack_rack_t * jack_rack, plugin_desc_t * desc); #endif /* __JR_JACK_RACK_H__ */ mlt-7.38.0/src/modules/jackrack/lock_free_fifo.c000664 000000 000000 00000005753 15172202314 021504 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include "lock_free_fifo.h" /** initialise a lock free fifo */ void lff_init (lff_t * lff, unsigned int size, size_t object_size) { lff->size = size; lff->object_size = object_size; lff->read_index = 0; lff->write_index = 0; lff->data = g_malloc (object_size * size); } lff_t * lff_new (unsigned int size, size_t object_size) { lff_t * lff; lff = g_malloc (sizeof (lff_t)); lff_init (lff, size, object_size); return lff; } void lff_free (lff_t * lff) { g_free (lff->data); } void lff_destroy (lff_t * lff) { lff_free (lff); g_free (lff); } /** read an element from the fifo into data. returns 0 on success, non-zero if there were no elements to read */ int lff_read (lff_t * lff, void * data) { if (lff->read_index == lff->write_index) { return -1; } else { memcpy (data, ((char *)lff->data) + (lff->read_index * lff->object_size), lff->object_size); lff->read_index++; if (lff->read_index >= lff->size) { lff->read_index = 0; } return 0; } } /** write an element from data to the fifo. returns 0 on success, non-zero if there was no space */ int lff_write (lff_t * lff, void * data) { static unsigned int ri; /* got to read read_index only once for safety */ ri = lff->read_index; /* lots of logic for when we're allowed to write to the fifo which basically boils down to "don't write if we're one element behind the read index" */ if ((ri > lff->write_index && ri - lff->write_index > 1) || (lff->write_index >= ri && lff->write_index != lff->size - 1) || (lff->write_index >= ri && lff->write_index == lff->size - 1 && ri != 0)) { /* if ((ri > lff->write_index && ri - lff->write_index > 1) || (lff->write_index >= ri && (lff->write_index != lff->size - 1 || ri != 0))) { */ memcpy (((char *)lff->data) + (lff->write_index * lff->object_size), data, lff->object_size); /* FIXME: is this safe? */ lff->write_index++; if (lff->write_index >= lff->size) { lff->write_index = 0; } return 0; } else { return -1; } } mlt-7.38.0/src/modules/jackrack/lock_free_fifo.h000664 000000 000000 00000003267 15172202314 021507 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JLH_LOCK_FREE_FIFO_H__ #define __JLH_LOCK_FREE_FIFO_H__ /** lock free fifo ring buffer structure */ typedef struct lock_free_fifo { /** Size of the ringbuffer (in elements) */ unsigned int size; /** the memory containing the ringbuffer */ void * data; /** the size of an element */ size_t object_size; /** the current position of the reader */ unsigned int read_index; /** the current position of the writer */ unsigned int write_index; } lff_t; void lff_init (lff_t * lff, unsigned int size, size_t object_size); void lff_free (lff_t * lff); lff_t * lff_new (unsigned int size, size_t object_size); void lff_destroy (lff_t * lock_free_fifo); int lff_read (lff_t * lock_free_fifo, void * data); int lff_write (lff_t * lock_free_fifo, void * data); #endif /* __JLH_LOCK_FREE_FIFO_H__ */ mlt-7.38.0/src/modules/jackrack/lv2_context.c000664 000000 000000 00000012666 15172202314 021020 0ustar00rootroot000000 000000 /* * LV2 Context * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include "framework/mlt_log.h" #include "lock_free_fifo.h" #include "lv2_context.h" #include "lv2_plugin_settings.h" #ifndef _ #define _(x) x #endif #define _x (const xmlChar *) #define _s (const char *) extern lv2_mgr_t *g_lv2_plugin_mgr; lv2_context_t *lv2_context_new(const char *client_name, unsigned long channels) { lv2_context_t *rack; rack = g_malloc(sizeof(lv2_context_t)); rack->saved_plugins = NULL; rack->channels = channels; rack->procinfo = lv2_process_info_new(client_name, channels, FALSE, FALSE); if (!rack->procinfo) { g_free(rack); return NULL; } rack->plugin_mgr = g_lv2_plugin_mgr; lv2_mgr_set_plugins(rack->plugin_mgr, channels); return rack; } void lv2_context_destroy(lv2_context_t *lv2_context) { lv2_process_quit(lv2_context->procinfo); // plugin_mgr is shared and global now, so we do not destroy it with each instance // lv2_mgr_destroy (lv2_context->plugin_mgr); lv2_process_info_destroy(lv2_context->procinfo); g_slist_free(lv2_context->saved_plugins); g_free(lv2_context); } lv2_plugin_t *lv2_context_instantiate_plugin(lv2_context_t *lv2_context, lv2_plugin_desc_t *desc) { lv2_plugin_t *plugin; /* check whether or not the plugin is RT capablex and confirm with the user if it isn't */ if (!LADSPA_IS_HARD_RT_CAPABLE(desc->properties)) { mlt_log_info(NULL, "Plugin not RT capable. The plugin '%s' does not describe itself as being " "capable of real-time operation. You may experience drop outs or jack may " "even kick us out if you use it.\n", desc->name); } /* create the plugin */ plugin = lv2_plugin_new(desc, lv2_context); if (!plugin) { mlt_log_error(NULL, "Error loading file plugin '%s' from file '%s'\n", desc->name, desc->uri); } return plugin; } void lv2_context_add_saved_plugin(lv2_context_t *lv2_context, saved_plugin_t *saved_plugin) { lv2_plugin_t *plugin = lv2_context_instantiate_plugin(lv2_context, saved_plugin->settings->desc); if (!plugin) { mlt_log_warning(NULL, "%s: could not instantiate object file '%s'\n", __FUNCTION__, saved_plugin->settings->desc->uri); return; } lv2_context->saved_plugins = g_slist_append(lv2_context->saved_plugins, saved_plugin); lv2_process_add_plugin(lv2_context->procinfo, plugin); lv2_context_add_plugin(lv2_context, plugin); } void lv2_context_add_plugin(lv2_context_t *lv2_context, lv2_plugin_t *plugin) { saved_plugin_t *saved_plugin = NULL; GSList *list; unsigned long control, channel; LADSPA_Data value; guint copy; /* see if there's any saved settings that match the plugin id */ for (list = lv2_context->saved_plugins; list; list = g_slist_next(list)) { saved_plugin = list->data; if (saved_plugin->settings->desc->id == plugin->desc->id) { /* process the settings! */ lv2_context->saved_plugins = g_slist_remove(lv2_context->saved_plugins, saved_plugin); break; } saved_plugin = NULL; } if (!saved_plugin) return; /* initialize plugin parameters */ plugin->enabled = lv2_settings_get_enabled(saved_plugin->settings); plugin->wet_dry_enabled = lv2_settings_get_wet_dry_enabled(saved_plugin->settings); for (control = 0; control < saved_plugin->settings->desc->control_port_count; control++) for (copy = 0; copy < plugin->copies; copy++) { value = lv2_settings_get_control_value(saved_plugin->settings, copy, control); plugin->holders[copy].control_memory[control] = value; //mlt_log_debug( NULL, "setting control value %s (%d) = %f\n", saved_plugin->settings->desc->port_names[control], copy, value); // lff_write (plugin->holders[copy].ui_control_fifos + control, &value); } if (plugin->wet_dry_enabled) for (channel = 0; channel < lv2_context->channels; channel++) { value = lv2_settings_get_wet_dry_value(saved_plugin->settings, channel); plugin->wet_dry_values[channel] = value; //mlt_log_debug( NULL, "setting wet/dry value %d = %f\n", channel, value); // lff_write (plugin->wet_dry_fifos + channel, &value); } } /* EOF */ mlt-7.38.0/src/modules/jackrack/lv2_context.h000664 000000 000000 00000003713 15172202314 021016 0ustar00rootroot000000 000000 /* * LV2 Context * * Based on the Jack Rack module * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __LV2_CONTEXT_H__ #define __LV2_CONTEXT_H__ #include #include #include "lv2_plugin.h" #include "lv2_plugin_settings.h" #include "lv2_process.h" #include "plugin_mgr.h" typedef struct _saved_plugin saved_plugin_t; struct _saved_plugin { lv2_settings_t *settings; }; typedef struct _saved_rack saved_rack_t; struct _saved_rack { unsigned long channels; jack_nframes_t sample_rate; GSList *plugins; }; typedef struct _lv2_context lv2_context_t; struct _lv2_context { lv2_mgr_t *plugin_mgr; lv2_process_info_t *procinfo; unsigned long channels; GSList *saved_plugins; }; lv2_context_t *lv2_context_new(const char *client_name, unsigned long channels); void lv2_context_destroy(lv2_context_t *lv2_context); void lv2_context_add_plugin(lv2_context_t *lv2_context, lv2_plugin_t *plugin); void lv2_context_add_saved_plugin(lv2_context_t *lv2_context, struct _saved_plugin *saved_plugin); lv2_plugin_t *lv2_context_instantiate_plugin(lv2_context_t *lv2_context, lv2_plugin_desc_t *desc); #endif /* __LV2_CONTEXT_H__ */ mlt-7.38.0/src/modules/jackrack/lv2_plugin.c000664 000000 000000 00000040163 15172202314 020623 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include "framework/mlt_log.h" #include "lv2_context.h" #include "lv2_plugin.h" #include "lv2_process.h" #define CONTROL_FIFO_SIZE 128 extern char *mlt_environment(const char *name); extern const LV2_Feature *features[]; #ifdef WITH_JACK /* swap over the jack ports in two plugins */ static void plugin_swap_aux_ports(lv2_plugin_t *plugin, lv2_plugin_t *other) { guint copy; jack_port_t **aux_ports_tmp; for (copy = 0; copy < plugin->copies; copy++) { aux_ports_tmp = other->holders[copy].aux_ports; other->holders[copy].aux_ports = plugin->holders[copy].aux_ports; plugin->holders[copy].aux_ports = aux_ports_tmp; } } #endif /** connect up the LV2 instance's input buffers to the previous plugin's audio memory. make sure to check that plugin->prev exists. */ void lv2_plugin_connect_input_ports(lv2_plugin_t *plugin, LADSPA_Data **inputs) { gint copy; unsigned long channel; unsigned long rack_channel; if (!plugin || !inputs) return; rack_channel = 0; for (copy = 0; copy < plugin->copies; copy++) { for (channel = 0; channel < plugin->desc->channels; channel++) { lilv_instance_connect_port(plugin->holders[copy].instance, plugin->desc->audio_input_port_indicies[channel], inputs[rack_channel]); rack_channel++; } } plugin->audio_input_memory = inputs; } /** connect up a plugin's output ports to its own audio_output_memory output memory */ void lv2_plugin_connect_output_ports(lv2_plugin_t *plugin) { gint copy; unsigned long channel; unsigned long rack_channel = 0; if (!plugin) return; for (copy = 0; copy < plugin->copies; copy++) { for (channel = 0; channel < plugin->desc->channels; channel++) { lilv_instance_connect_port(plugin->holders[copy].instance, plugin->desc->audio_output_port_indicies[channel], plugin->audio_output_memory[rack_channel]); rack_channel++; } } } void lv2_process_add_plugin(lv2_process_info_t *procinfo, lv2_plugin_t *plugin) { /* sort out list pointers */ plugin->next = NULL; plugin->prev = procinfo->chain_end; if (procinfo->chain_end) procinfo->chain_end->next = plugin; else procinfo->chain = plugin; procinfo->chain_end = plugin; } /** remove a plugin from the chain */ lv2_plugin_t *lv2_process_remove_plugin(lv2_process_info_t *procinfo, lv2_plugin_t *plugin) { /* sort out chain pointers */ if (plugin->prev) plugin->prev->next = plugin->next; else procinfo->chain = plugin->next; if (plugin->next) plugin->next->prev = plugin->prev; else procinfo->chain_end = plugin->prev; #ifdef WITH_JACK /* sort out the aux ports */ if (procinfo->jack_client && plugin->desc->aux_channels > 0) { lv2_plugin_t *other; for (other = plugin->next; other; other = other->next) if (other->desc->id == plugin->desc->id) plugin_swap_aux_ports(plugin, other); } #endif return plugin; } /** enable/disable a plugin */ void lv2_process_ablise_plugin(lv2_process_info_t *procinfo, lv2_plugin_t *plugin, gboolean enable) { plugin->enabled = enable; } /** enable/disable a plugin */ void lv2_process_ablise_plugin_wet_dry(lv2_process_info_t *procinfo, lv2_plugin_t *plugin, gboolean enable) { plugin->wet_dry_enabled = enable; } /** move a plugin up or down one place in the chain */ void lv2_process_move_plugin(lv2_process_info_t *procinfo, lv2_plugin_t *plugin, gint up) { /* other plugins in the chain */ lv2_plugin_t *pp = NULL, *p, *n, *nn = NULL; /* note that we should never receive an illogical move request ie, there will always be at least 1 plugin before for an up request or 1 plugin after for a down request */ /* these are pointers to the plugins surrounding the specified one: { pp, p, plugin, n, nn } which makes things much clearer than tptr, tptr2 etc */ p = plugin->prev; if (p) pp = p->prev; n = plugin->next; if (n) nn = n->next; if (up) { if (!p) return; if (pp) pp->next = plugin; else procinfo->chain = plugin; p->next = n; p->prev = plugin; plugin->prev = pp; plugin->next = p; if (n) n->prev = p; else procinfo->chain_end = p; } else { if (!n) return; if (p) p->next = n; else procinfo->chain = n; n->prev = p; n->next = plugin; plugin->prev = n; plugin->next = nn; if (nn) nn->prev = plugin; else procinfo->chain_end = plugin; } #ifdef WITH_JACK if (procinfo->jack_client && plugin->desc->aux_channels > 0) { lv2_plugin_t *other; other = up ? plugin->next : plugin->prev; /* swap around the jack ports */ if (other->desc->id == plugin->desc->id) plugin_swap_aux_ports(plugin, other); } #endif } /** exchange an existing plugin for a newly created one */ lv2_plugin_t *lv2_process_change_plugin(lv2_process_info_t *procinfo, lv2_plugin_t *plugin, lv2_plugin_t *new_plugin) { new_plugin->next = plugin->next; new_plugin->prev = plugin->prev; if (plugin->prev) plugin->prev->next = new_plugin; else procinfo->chain = new_plugin; if (plugin->next) plugin->next->prev = new_plugin; else procinfo->chain_end = new_plugin; #ifdef WITH_JACK /* sort out the aux ports */ if (procinfo->jack_client && plugin->desc->aux_channels > 0) { lv2_plugin_t *other; for (other = plugin->next; other; other = other->next) if (other->desc->id == plugin->desc->id) plugin_swap_aux_ports(plugin, other); } #endif return plugin; } /****************************************** ************* non RT stuff *************** ******************************************/ static int lv2_plugin_instantiate(const LilvPlugin *plugin, unsigned long plugin_index, gint copies, LilvInstance **instances) { char *lv2context_can_ui = mlt_environment("lv2context_can_ui"); if (lv2context_can_ui != NULL) { /* Video editors and other hosts that support custom GUI should use mlt_environment_set ("lv2context_can_ui", "1") to inform mlt lv2 plugin manager and set UI features and extensions if not set. */ if (lv2context_can_ui[0] == '1') { //WIP: if support UI } } gint i; for (i = 0; i < copies; i++) { instances[i] = lilv_plugin_instantiate(plugin, lv2_sample_rate, features); if (!instances[i]) { unsigned long d; for (d = 0; d < i; d++) lilv_instance_free(instances[d]); return 1; } } return 0; } #ifdef WITH_JACK static void lv2_plugin_create_aux_ports(lv2_plugin_t *plugin, guint copy, lv2_context_t *lv2_context) { lv2_plugin_desc_t *desc; unsigned long aux_channel = 1; unsigned long plugin_index = 1; unsigned long i; char port_name[64]; char *plugin_name; char *ptr; lv2_holder_t *holder; desc = plugin->desc; holder = plugin->holders + copy; holder->aux_ports = g_malloc(sizeof(jack_port_t *) * desc->aux_channels); /* make the plugin name jack worthy */ ptr = plugin_name = g_strndup(plugin->desc->name, 7); while (*ptr != '\0') { if (*ptr == ' ') *ptr = '_'; else *ptr = tolower(*ptr); ptr++; } /* for (list = lv2_context->slots; list; list = g_list_next (list)) { slot = (plugin_slot_t *) list->data; if (slot->plugin->desc->id == plugin->desc->id) plugin_index++; } */ for (i = 0; i < desc->aux_channels; i++, aux_channel++) { sprintf(port_name, "%s_%ld-%d_%c%ld", plugin_name, plugin_index, copy + 1, desc->aux_are_input ? 'i' : 'o', aux_channel); holder->aux_ports[i] = jack_port_register(lv2_context->procinfo->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, desc->aux_are_input ? JackPortIsInput : JackPortIsOutput, 0); if (!holder->aux_ports[i]) { mlt_log_panic(NULL, "Could not register jack port '%s'; aborting\n", port_name); } } g_free(plugin_name); } #endif static void lv2_plugin_init_holder(const lv2_plugin_t *plugin, guint copy, const LilvInstance *instance, lv2_context_t *lv2_context) { unsigned long i; lv2_plugin_desc_t *desc; lv2_holder_t *holder; desc = plugin->desc; holder = plugin->holders + copy; holder->instance = instance; if (desc->control_port_count > 0) { holder->ui_control_fifos = g_malloc(sizeof(lff_t) * desc->control_port_count); holder->control_memory = g_malloc(sizeof(LADSPA_Data) * desc->control_port_count); } else { holder->ui_control_fifos = NULL; holder->control_memory = NULL; } for (i = 0; i < desc->control_port_count; i++) { lff_init(holder->ui_control_fifos + i, CONTROL_FIFO_SIZE, sizeof(LADSPA_Data)); if (!isnan(plugin->desc->def_values[desc->control_port_indicies[i]])) { holder->control_memory[i] = plugin->desc->def_values[desc->control_port_indicies[i]]; } else if (!isnan(plugin->desc->min_values[desc->control_port_indicies[i]])) { holder->control_memory[i] = plugin->desc->min_values[desc->control_port_indicies[i]]; } else if (!isnan(plugin->desc->max_values[desc->control_port_indicies[i]])) { holder->control_memory[i] = plugin->desc->max_values[desc->control_port_indicies[i]]; } else { holder->control_memory[i] = 0.0; } lilv_instance_connect_port(instance, desc->control_port_indicies[i], &holder->control_memory[i]); } if (desc->status_port_count > 0) { holder->status_memory = g_malloc(sizeof(LADSPA_Data) * desc->status_port_count); } else { holder->status_memory = NULL; } for (i = 0; i < desc->status_port_count; i++) { lilv_instance_connect_port(instance, desc->status_port_indicies[i], holder->status_memory + i); } #ifdef WITH_JACK if (lv2_context->procinfo->jack_client && plugin->desc->aux_channels > 0) lv2_plugin_create_aux_ports(plugin, copy, lv2_context); #endif lilv_instance_activate(instance); } lv2_plugin_t *lv2_plugin_new(lv2_plugin_desc_t *desc, lv2_context_t *lv2_context) { LilvInstance **instances; gint copies; unsigned long i; int err; lv2_plugin_t *plugin; /* open the plugin */ plugin = g_malloc(sizeof(lv2_plugin_t)); char *str_ptr = strchr(desc->uri, '^'); while (str_ptr != NULL) { *str_ptr++ = ':'; str_ptr = strchr(str_ptr, '^'); } plugin->lv2_plugin_uri = lilv_new_uri(lv2_context->plugin_mgr->lv2_world, desc->uri); plugin->lv2_plugin = lilv_plugins_get_by_uri(lv2_context->plugin_mgr->plugin_list, plugin->lv2_plugin_uri); str_ptr = strchr(desc->uri, ':'); while (str_ptr != NULL) { *str_ptr++ = '^'; str_ptr = strchr(str_ptr, ':'); } /* create the instances */ copies = lv2_plugin_desc_get_copies(desc, lv2_context->channels); instances = g_malloc(sizeof(LADSPA_Handle) * copies); err = lv2_plugin_instantiate(plugin->lv2_plugin, desc->index, copies, instances); if (err) { g_free(instances); return NULL; } plugin->desc = desc; plugin->copies = copies; plugin->enabled = FALSE; plugin->next = NULL; plugin->prev = NULL; plugin->wet_dry_enabled = FALSE; plugin->lv2_context = lv2_context; /* create audio memory and wet/dry stuff */ plugin->audio_output_memory = g_malloc(sizeof(LADSPA_Data *) * lv2_context->channels); plugin->wet_dry_fifos = g_malloc(sizeof(lff_t) * lv2_context->channels); plugin->wet_dry_values = g_malloc(sizeof(LADSPA_Data) * lv2_context->channels); for (i = 0; i < lv2_context->channels; i++) { plugin->audio_output_memory[i] = g_malloc(sizeof(LADSPA_Data) * lv2_buffer_size); lff_init(plugin->wet_dry_fifos + i, CONTROL_FIFO_SIZE, sizeof(LADSPA_Data)); plugin->wet_dry_values[i] = 1.0; } /* create holders and fill them out */ plugin->holders = g_malloc(sizeof(lv2_holder_t) * copies); for (i = 0; i < copies; i++) lv2_plugin_init_holder(plugin, i, instances[i], lv2_context); return plugin; } void lv2_plugin_destroy(lv2_plugin_t *plugin) { unsigned long i, j; int err = 0; /* destroy holders */ for (i = 0; i < plugin->copies; i++) { lilv_instance_deactivate(plugin->holders[i].instance); if (plugin->desc->control_port_count > 0) { for (j = 0; j < plugin->desc->control_port_count; j++) { lff_free(plugin->holders[i].ui_control_fifos + j); } g_free(plugin->holders[i].ui_control_fifos); g_free(plugin->holders[i].control_memory); } if (plugin->desc->status_port_count > 0) { g_free(plugin->holders[i].status_memory); } #ifdef WITH_JACK /* aux ports */ if (plugin->lv2_context->procinfo->jack_client && plugin->desc->aux_channels > 0) { for (j = 0; j < plugin->desc->aux_channels; j++) { err = jack_port_unregister(plugin->lv2_context->procinfo->jack_client, plugin->holders[i].aux_ports[j]); if (err) mlt_log_warning(NULL, "%s: could not unregister jack port\n", __FUNCTION__); } g_free(plugin->holders[i].aux_ports); } #endif } g_free(plugin->holders); for (i = 0; i < plugin->lv2_context->channels; i++) { g_free(plugin->audio_output_memory[i]); lff_free(plugin->wet_dry_fifos + i); } g_free(plugin->audio_output_memory); g_free(plugin->wet_dry_fifos); g_free(plugin->wet_dry_values); if (err) { mlt_log_warning(NULL, "%s: error closing shared object '%s': %s\n", __FUNCTION__, plugin->desc->uri, dlerror()); } g_free(plugin); } /* EOF */ mlt-7.38.0/src/modules/jackrack/lv2_plugin.h000664 000000 000000 00000005416 15172202314 020632 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __LV2_PLUGIN_H__ #define __LV2_PLUGIN_H__ #include #include #include #ifdef WITH_JACK #include #endif #include "lv2_process.h" #include "plugin_desc.h" typedef struct _lv2_holder lv2_holder_t; typedef struct _lv2_plugin lv2_plugin_t; struct _lv2_holder { LilvInstance *instance; lff_t *ui_control_fifos; LADSPA_Data *control_memory; LADSPA_Data *status_memory; #ifdef WITH_JACK jack_port_t **aux_ports; #endif }; struct _lv2_plugin { lv2_plugin_desc_t *desc; gint enabled; gint copies; lv2_holder_t *holders; LADSPA_Data **audio_input_memory; LADSPA_Data **audio_output_memory; gboolean wet_dry_enabled; /* 1.0 = all wet, 0.0 = all dry, 0.5 = 50% wet/50% dry */ LADSPA_Data *wet_dry_values; lff_t *wet_dry_fifos; lv2_plugin_t *next; lv2_plugin_t *prev; LilvNode *lv2_plugin_uri; LilvPlugin *lv2_plugin; struct _lv2_context *lv2_context; }; void lv2_process_add_plugin(lv2_process_info_t *, lv2_plugin_t *plugin); lv2_plugin_t *lv2_process_remove_plugin(lv2_process_info_t *, lv2_plugin_t *plugin); void lv2_process_ablise_plugin(lv2_process_info_t *, lv2_plugin_t *plugin, gboolean able); void lv2_process_ablise_plugin_wet_dry(lv2_process_info_t *, lv2_plugin_t *plugin, gboolean enable); void lv2_process_move_plugin(lv2_process_info_t *, lv2_plugin_t *plugin, gint up); lv2_plugin_t *lv2_process_change_plugin(lv2_process_info_t *, lv2_plugin_t *plugin, lv2_plugin_t *new_plugin); struct _lv2_context; struct _ui; lv2_plugin_t *lv2_plugin_new(lv2_plugin_desc_t *plugin_desc, struct _lv2_context *lv2_context); void lv2_plugin_destroy(lv2_plugin_t *plugin); void lv2_plugin_connect_input_ports(lv2_plugin_t *plugin, LADSPA_Data **inputs); void lv2_plugin_connect_output_ports(lv2_plugin_t *plugin); #endif /* __LV2_PLUGIN_H__ */ mlt-7.38.0/src/modules/jackrack/lv2_plugin_settings.c000664 000000 000000 00000026033 15172202314 022543 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include "lv2_plugin_settings.h" static void lv2_settings_set_to_default(lv2_settings_t *settings, guint32 sample_rate) { unsigned long control; guint copy; LADSPA_Data value; for (control = 0; control < settings->desc->control_port_count; control++) { value = settings->desc->def_values[settings->desc->control_port_indicies[control]]; for (copy = 0; copy < settings->copies; copy++) { settings->control_values[copy][control] = value; } settings->locks[control] = TRUE; } } lv2_settings_t *lv2_settings_new(lv2_plugin_desc_t *desc, unsigned long channels, guint32 sample_rate) { lv2_settings_t *settings; unsigned long channel; guint copies; settings = g_malloc(sizeof(lv2_settings_t)); copies = lv2_plugin_desc_get_copies(desc, channels); settings->sample_rate = sample_rate; settings->desc = desc; settings->copies = copies; settings->channels = channels; settings->lock_all = TRUE; settings->enabled = FALSE; settings->locks = NULL; settings->control_values = NULL; settings->wet_dry_enabled = FALSE; settings->wet_dry_locked = TRUE; /* control settings */ if (desc->control_port_count > 0) { guint copy; settings->locks = g_malloc(sizeof(gboolean) * desc->control_port_count); settings->control_values = g_malloc(sizeof(LADSPA_Data *) * copies); for (copy = 0; copy < copies; copy++) { settings->control_values[copy] = g_malloc(sizeof(LADSPA_Data) * desc->control_port_count); } lv2_settings_set_to_default(settings, sample_rate); } /* wet/dry settings */ settings->wet_dry_values = g_malloc(sizeof(LADSPA_Data) * channels); for (channel = 0; channel < channels; channel++) settings->wet_dry_values[channel] = 1.0; return settings; } lv2_settings_t *lv2_settings_dup(lv2_settings_t *other) { lv2_settings_t *settings; lv2_plugin_desc_t *desc; unsigned long channel; settings = g_malloc(sizeof(lv2_settings_t)); settings->sample_rate = other->sample_rate; settings->desc = other->desc; settings->copies = lv2_settings_get_copies(other); settings->channels = lv2_settings_get_channels(other); settings->wet_dry_enabled = lv2_settings_get_wet_dry_enabled(other); settings->wet_dry_locked = lv2_settings_get_wet_dry_locked(other); settings->lock_all = lv2_settings_get_lock_all(other); settings->enabled = lv2_settings_get_enabled(other); settings->locks = NULL; settings->control_values = NULL; desc = other->desc; if (desc->control_port_count > 0) { guint copy; unsigned long control; settings->locks = g_malloc(sizeof(gboolean) * desc->control_port_count); for (control = 0; control < desc->control_port_count; control++) lv2_settings_set_lock(settings, control, lv2_settings_get_lock(other, control)); settings->control_values = g_malloc(sizeof(LADSPA_Data *) * settings->copies); for (copy = 0; copy < settings->copies; copy++) { settings->control_values[copy] = g_malloc(sizeof(LADSPA_Data) * desc->control_port_count); for (control = 0; control < desc->control_port_count; control++) { settings->control_values[copy][control] = lv2_settings_get_control_value(other, copy, control); } } } settings->wet_dry_values = g_malloc(sizeof(LADSPA_Data) * settings->channels); for (channel = 0; channel < settings->channels; channel++) settings->wet_dry_values[channel] = lv2_settings_get_wet_dry_value(other, channel); return settings; } void lv2_settings_destroy(lv2_settings_t *settings) { if (settings->desc->control_port_count > 0) { guint i; for (i = 0; i < settings->copies; i++) g_free(settings->control_values[i]); g_free(settings->control_values); g_free(settings->locks); } g_free(settings->wet_dry_values); g_free(settings); } static void lv2_settings_set_copies(lv2_settings_t *settings, guint copies) { guint copy; guint last_copy; unsigned long control; if (copies <= settings->copies) return; last_copy = settings->copies - 1; settings->control_values = g_realloc(settings->control_values, sizeof(LADSPA_Data *) * copies); /* copy over the last settings to the new copies */ for (copy = settings->copies; copy < copies; copy++) { for (control = 0; control < settings->desc->control_port_count; control++) { settings->control_values[copy][control] = settings->control_values[last_copy][control]; } } settings->copies = copies; } static void lv2_settings_set_channels(lv2_settings_t *settings, unsigned long channels) { unsigned long channel; LADSPA_Data last_value; if (channels <= settings->channels) return; settings->wet_dry_values = g_realloc(settings->wet_dry_values, sizeof(LADSPA_Data) * channels); last_value = settings->wet_dry_values[settings->channels - 1]; for (channel = settings->channels; channel < channels; channel++) settings->wet_dry_values[channel] = last_value; settings->channels = channels; } void lv2_settings_set_sample_rate(lv2_settings_t *settings, guint32 sample_rate) { LADSPA_Data old_sample_rate; LADSPA_Data new_sample_rate; g_return_if_fail(settings != NULL); if (settings->sample_rate == sample_rate) return; if (settings->desc->control_port_count > 0) { unsigned long control; guint copy; new_sample_rate = (LADSPA_Data) sample_rate; old_sample_rate = (LADSPA_Data) settings->sample_rate; for (control = 0; control < settings->desc->control_port_count; control++) { for (copy = 0; copy < settings->copies; copy++) { if (LADSPA_IS_HINT_SAMPLE_RATE( settings->desc->port_range_hints[control].HintDescriptor)) { settings->control_values[copy][control] = (settings->control_values[copy][control] / old_sample_rate) * new_sample_rate; } } } } settings->sample_rate = sample_rate; } void lv2_settings_set_control_value(lv2_settings_t *settings, guint copy, unsigned long control_index, LADSPA_Data value) { g_return_if_fail(settings != NULL); g_return_if_fail(control_index < settings->desc->control_port_count); if (copy >= settings->copies) lv2_settings_set_copies(settings, copy + 1); settings->control_values[copy][control_index] = value; } void lv2_settings_set_lock(lv2_settings_t *settings, unsigned long control_index, gboolean locked) { g_return_if_fail(settings != NULL); g_return_if_fail(control_index < settings->desc->control_port_count); settings->locks[control_index] = locked; } void lv2_settings_set_lock_all(lv2_settings_t *settings, gboolean lock_all) { g_return_if_fail(settings != NULL); settings->lock_all = lock_all; } void lv2_settings_set_enabled(lv2_settings_t *settings, gboolean enabled) { g_return_if_fail(settings != NULL); settings->enabled = enabled; } void lv2_settings_set_wet_dry_enabled(lv2_settings_t *settings, gboolean enabled) { g_return_if_fail(settings != NULL); settings->wet_dry_enabled = enabled; } void lv2_settings_set_wet_dry_locked(lv2_settings_t *settings, gboolean locked) { g_return_if_fail(settings != NULL); settings->wet_dry_locked = locked; } void lv2_settings_set_wet_dry_value(lv2_settings_t *settings, unsigned long channel, LADSPA_Data value) { g_return_if_fail(settings != NULL); if (channel >= settings->channels) lv2_settings_set_channels(settings, channel + 1); settings->wet_dry_values[channel] = value; } LADSPA_Data lv2_settings_get_control_value(lv2_settings_t *settings, guint copy, unsigned long control_index) { g_return_val_if_fail(settings != NULL, NAN); g_return_val_if_fail(control_index < settings->desc->control_port_count, NAN); if (copy >= settings->copies) lv2_settings_set_copies(settings, copy - 1); return settings->control_values[copy][control_index]; } gboolean lv2_settings_get_lock(const lv2_settings_t *settings, unsigned long control_index) { g_return_val_if_fail(settings != NULL, FALSE); return settings->locks[control_index]; } gboolean lv2_settings_get_lock_all(const lv2_settings_t *settings) { g_return_val_if_fail(settings != NULL, FALSE); return settings->lock_all; } gboolean lv2_settings_get_enabled(const lv2_settings_t *settings) { g_return_val_if_fail(settings != NULL, FALSE); return settings->enabled; } guint lv2_settings_get_copies(const lv2_settings_t *settings) { g_return_val_if_fail(settings != NULL, 0); return settings->copies; } unsigned long lv2_settings_get_channels(const lv2_settings_t *settings) { g_return_val_if_fail(settings != NULL, 0); return settings->channels; } gboolean lv2_settings_get_wet_dry_enabled(const lv2_settings_t *settings) { g_return_val_if_fail(settings != NULL, FALSE); return settings->wet_dry_enabled; } gboolean lv2_settings_get_wet_dry_locked(const lv2_settings_t *settings) { g_return_val_if_fail(settings != NULL, FALSE); return settings->wet_dry_locked; } LADSPA_Data lv2_settings_get_wet_dry_value(lv2_settings_t *settings, unsigned long channel) { g_return_val_if_fail(settings != NULL, NAN); if (channel >= settings->channels) lv2_settings_set_channels(settings, channel + 1); return settings->wet_dry_values[channel]; } /* EOF */ mlt-7.38.0/src/modules/jackrack/lv2_plugin_settings.h000664 000000 000000 00000006544 15172202314 022555 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __LV2_PLUGIN_SETTINGS_H__ #define __LV2_PLUGIN_SETTINGS_H__ #include #include #include "plugin_desc.h" #include "plugin_mgr.h" typedef struct _lv2_settings lv2_settings_t; struct _lv2_settings { guint32 sample_rate; lv2_plugin_desc_t *desc; guint copies; LADSPA_Data **control_values; gboolean *locks; gboolean lock_all; gboolean enabled; unsigned long channels; gboolean wet_dry_enabled; gboolean wet_dry_locked; LADSPA_Data *wet_dry_values; }; lv2_settings_t *lv2_settings_new(lv2_plugin_desc_t *desc, unsigned long channels, guint32 sample_rate); lv2_settings_t *lv2_settings_dup(lv2_settings_t *settings); void lv2_settings_destroy(lv2_settings_t *settings); void lv2_settings_set_control_value(lv2_settings_t *settings, guint copy, unsigned long control_index, LADSPA_Data value); void lv2_settings_set_lock(lv2_settings_t *settings, unsigned long control_index, gboolean locked); void lv2_settings_set_lock_all(lv2_settings_t *settings, gboolean lock_all); void lv2_settings_set_enabled(lv2_settings_t *settings, gboolean enabled); void lv2_settings_set_wet_dry_enabled(lv2_settings_t *settings, gboolean enabled); void lv2_settings_set_wet_dry_locked(lv2_settings_t *settings, gboolean locked); void lv2_settings_set_wet_dry_value(lv2_settings_t *settings, unsigned long channel, LADSPA_Data value); LADSPA_Data lv2_settings_get_control_value(lv2_settings_t *settings, guint copy, unsigned long control_index); gboolean lv2_settings_get_lock(const lv2_settings_t *settings, unsigned long control_index); gboolean lv2_settings_get_lock_all(const lv2_settings_t *settings); gboolean lv2_settings_get_enabled(const lv2_settings_t *settings); guint lv2_settings_get_copies(const lv2_settings_t *settings); unsigned long lv2_settings_get_channels(const lv2_settings_t *settings); gboolean lv2_settings_get_wet_dry_enabled(const lv2_settings_t *settings); gboolean lv2_settings_get_wet_dry_locked(const lv2_settings_t *settings); LADSPA_Data lv2_settings_get_wet_dry_value(lv2_settings_t *settings, unsigned long channel); void lv2_settings_set_sample_rate(lv2_settings_t *settings, guint32 sample_rate); #endif /* __LV2_PLUGIN_SETTINGS_H__ */ mlt-7.38.0/src/modules/jackrack/lv2_process.c000664 000000 000000 00000052164 15172202314 021007 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include /* #ifdef WITH_JACK #include #endif */ #include #include #include #include #include #include #include #include "framework/mlt_log.h" #include "lock_free_fifo.h" #include "lv2_context.h" #include "lv2_plugin.h" #include "lv2_process.h" #ifndef _ #define _(x) x #endif extern pthread_mutex_t g_activate_mutex; #define USEC_PER_SEC 1000000 #define MSEC_PER_SEC 1000 #define TIME_RUN_SKIP_COUNT 5 #define MAX_BUFFER_SIZE 4096 jack_nframes_t lv2_sample_rate; jack_nframes_t lv2_buffer_size; /* #ifdef WITH_JACK static void jack_shutdown_cb (void * data) { lv2_process_info_t * procinfo = data; procinfo->quit = TRUE; } #endif */ /** process messages for plugins' control ports */ void lv2_process_control_port_messages(lv2_process_info_t *procinfo) { lv2_plugin_t *plugin; unsigned long control; unsigned long channel; gint copy; if (!procinfo->chain) return; for (plugin = procinfo->chain; plugin; plugin = plugin->next) { if (plugin->desc->control_port_count > 0) for (control = 0; control < plugin->desc->control_port_count; control++) for (copy = 0; copy < plugin->copies; copy++) { while (lff_read(plugin->holders[copy].ui_control_fifos + control, plugin->holders[copy].control_memory + control) == 0) ; } if (plugin->wet_dry_enabled) for (channel = 0; channel < procinfo->channels; channel++) { while (lff_read(plugin->wet_dry_fifos + channel, plugin->wet_dry_values + channel) == 0) ; } } } /* #ifdef WITH_JACK static int get_jack_buffers (lv2_process_info_t * procinfo, jack_nframes_t frames) { unsigned long channel; for (channel = 0; channel < procinfo->channels; channel++) { procinfo->jack_input_buffers[channel] = jack_port_get_buffer (procinfo->jack_input_ports[channel], frames); if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel); return 1; } procinfo->jack_output_buffers[channel] = jack_port_get_buffer (procinfo->jack_output_ports[channel], frames); if (!procinfo->jack_output_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel); return 1; } } return 0; } #endif */ lv2_plugin_t *lv2_get_first_enabled_plugin(lv2_process_info_t *procinfo) { lv2_plugin_t *first_enabled; if (!procinfo->chain) return NULL; for (first_enabled = procinfo->chain; first_enabled; first_enabled = first_enabled->next) { if (first_enabled->enabled) return first_enabled; } return NULL; } lv2_plugin_t *lv2_get_last_enabled_plugin(lv2_process_info_t *procinfo) { lv2_plugin_t *last_enabled; if (!procinfo->chain) return NULL; for (last_enabled = procinfo->chain_end; last_enabled; last_enabled = last_enabled->prev) { if (last_enabled->enabled) return last_enabled; } return NULL; } void lv2_connect_chain(lv2_process_info_t *procinfo, jack_nframes_t frames) { lv2_plugin_t *first_enabled, *last_enabled, *plugin; gint copy; unsigned long channel; if (!procinfo->chain) return; first_enabled = lv2_get_first_enabled_plugin(procinfo); if (!first_enabled) return; last_enabled = lv2_get_last_enabled_plugin(procinfo); /* sort out the aux ports */ plugin = first_enabled; do { if (plugin->desc->aux_channels > 0 && plugin->enabled) { #ifdef WITH_JACK if (procinfo->jack_client) { for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) lilv_instance_connect_port(plugin->holders[copy].instance, plugin->desc->audio_aux_port_indicies[channel], jack_port_get_buffer(plugin->holders[copy] .aux_ports[channel], frames)); } else #endif { for (copy = 0; copy < frames; copy++) procinfo->silent_buffer[copy] = 0.0; for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) lilv_instance_connect_port(plugin->holders[copy].instance, plugin->desc->audio_aux_port_indicies[channel], procinfo->silent_buffer); } } } while ((plugin != last_enabled) && (plugin = plugin->next)); /* ensure that all the of the enabled plugins are connected to their memory */ lv2_plugin_connect_output_ports(first_enabled); if (first_enabled != last_enabled) { lv2_plugin_connect_input_ports(last_enabled, last_enabled->prev->audio_output_memory); for (plugin = first_enabled->next; plugin; plugin = plugin->next) { if (plugin->enabled) { lv2_plugin_connect_input_ports(plugin, plugin->prev->audio_output_memory); lv2_plugin_connect_output_ports(plugin); } } } /* input buffers for first plugin */ if (plugin->desc->has_input) lv2_plugin_connect_input_ports(first_enabled, procinfo->jack_input_buffers); } void lv2_process_chain(lv2_process_info_t *procinfo, jack_nframes_t frames) { lv2_plugin_t *first_enabled; lv2_plugin_t *last_enabled = NULL; lv2_plugin_t *plugin; unsigned long channel; unsigned long i; #ifdef WITH_JACK if (procinfo->jack_client) { LADSPA_Data zero_signal[frames]; guint copy; /* set the zero signal to zero */ for (channel = 0; channel < frames; channel++) zero_signal[channel] = 0.0; /* possibly set aux output channels to zero if they're not enabled */ for (plugin = procinfo->chain; plugin; plugin = plugin->next) if (!plugin->enabled && plugin->desc->aux_channels > 0 && !plugin->desc->aux_are_input) for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) memcpy(jack_port_get_buffer(plugin->holders[copy].aux_ports[channel], frames), zero_signal, sizeof(LADSPA_Data) * frames); } #endif first_enabled = lv2_get_first_enabled_plugin(procinfo); /* no chain; just copy input to output */ if (!procinfo->chain || !first_enabled) { unsigned long channel; for (channel = 0; channel < procinfo->channels; channel++) { memcpy(procinfo->jack_output_buffers[channel], procinfo->jack_input_buffers[channel], sizeof(LADSPA_Data) * frames); } return; } /* all past here is guaranteed to have at least 1 enabled plugin */ last_enabled = lv2_get_last_enabled_plugin(procinfo); for (plugin = first_enabled; plugin; plugin = plugin->next) { if (plugin->enabled) { for (i = 0; i < plugin->copies; i++) lilv_instance_run(plugin->holders[i].instance, frames); if (plugin->wet_dry_enabled) for (channel = 0; channel < procinfo->channels; channel++) { if (procinfo->channel_mask & (1 << channel)) for (i = 0; i < frames; i++) { plugin->audio_output_memory[channel][i] *= plugin->wet_dry_values[channel]; plugin->audio_output_memory[channel][i] += plugin->audio_input_memory[channel][i] * (1.0 - plugin->wet_dry_values[channel]); } else memcpy(plugin->audio_output_memory[channel], plugin->audio_input_memory[channel], sizeof(LADSPA_Data) * frames); } if (plugin == last_enabled) break; } else { /* copy the data through */ for (i = 0; i < procinfo->channels; i++) memcpy(plugin->audio_output_memory[i], plugin->prev->audio_output_memory[i], sizeof(LADSPA_Data) * frames); } } /* copy the last enabled data to the jack ports */ for (i = 0; i < procinfo->channels; i++) memcpy(procinfo->jack_output_buffers[i], last_enabled->audio_output_memory[i], sizeof(LADSPA_Data) * frames); } int process_lv2(lv2_process_info_t *procinfo, jack_nframes_t frames, LADSPA_Data **inputs, LADSPA_Data **outputs) { unsigned long channel; if (!procinfo) { mlt_log_error(NULL, "%s: no process_info from jack!\n", __FUNCTION__); return 1; } if (procinfo->quit == TRUE) return 1; lv2_process_control_port_messages(procinfo); for (channel = 0; channel < procinfo->channels; channel++) { if (lv2_get_first_enabled_plugin(procinfo)->desc->has_input) { procinfo->jack_input_buffers[channel] = inputs[channel]; if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose(NULL, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel); return 1; } } procinfo->jack_output_buffers[channel] = outputs[channel]; if (!procinfo->jack_output_buffers[channel]) { mlt_log_verbose(NULL, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel); return 1; } } lv2_connect_chain(procinfo, frames); lv2_process_chain(procinfo, frames); return 0; } #ifdef WITH_JACK /* int process_jack (jack_nframes_t frames, void * data) { int err; lv2_process_info_t * procinfo; procinfo = (lv2_process_info_t *) data; if (!procinfo) { mlt_log_error( NULL, "%s: no process_info from jack!\n", __FUNCTION__); return 1; } if (procinfo->port_count == 0) return 0; if (procinfo->quit == TRUE) return 1; lv2_process_control_port_messages (procinfo); err = get_jack_buffers (procinfo, frames); if (err) { mlt_log_warning( NULL, "%s: failed to get jack ports, not processing\n", __FUNCTION__); return 0; } lv2_connect_chain (procinfo, frames); lv2_process_chain (procinfo, frames); return 0; } */ /******************************************* ************** non RT stuff *************** *******************************************/ /* static int lv2_process_info_connect_jack (lv2_process_info_t * procinfo) { mlt_log_info( NULL, _("Connecting to JACK server with client name '%s'\n"), procinfo->jack_client_name); procinfo->jack_client = jack_client_open (procinfo->jack_client_name, JackNullOption, NULL); if (!procinfo->jack_client) { mlt_log_warning( NULL, "%s: could not create jack client; is the jackd server running?\n", __FUNCTION__); return 1; } mlt_log_verbose( NULL, _("Connected to JACK server\n")); jack_set_process_callback (procinfo->jack_client, process_jack, procinfo); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); return 0; } */ static void lv2_process_info_connect_port(lv2_process_info_t *procinfo, gshort in, unsigned long port_index, const char *port_name) { const char **jack_ports; unsigned long jack_port_index; int err; char *full_port_name; jack_ports = jack_get_ports(procinfo->jack_client, NULL, NULL, JackPortIsPhysical | (in ? JackPortIsOutput : JackPortIsInput)); if (!jack_ports) return; for (jack_port_index = 0; jack_ports[jack_port_index] && jack_port_index <= port_index; jack_port_index++) { if (jack_port_index != port_index) continue; full_port_name = g_strdup_printf("%s:%s", procinfo->jack_client_name, port_name); mlt_log_debug(NULL, _("Connecting ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]); err = jack_connect(procinfo->jack_client, in ? jack_ports[jack_port_index] : full_port_name, in ? full_port_name : jack_ports[jack_port_index]); if (err) mlt_log_warning(NULL, "%s: error connecting ports '%s' and '%s'\n", __FUNCTION__, full_port_name, jack_ports[jack_port_index]); else mlt_log_info(NULL, _("Connected ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]); free(full_port_name); } free(jack_ports); } static int lv2_process_info_set_port_count(lv2_process_info_t *procinfo, unsigned long port_count, gboolean connect_inputs, gboolean connect_outputs) { unsigned long i; char *port_name; jack_port_t **port_ptr; gshort in; if (procinfo->port_count >= port_count) return -1; if (procinfo->port_count == 0) { procinfo->jack_input_ports = g_malloc(sizeof(jack_port_t *) * port_count); procinfo->jack_output_ports = g_malloc(sizeof(jack_port_t *) * port_count); procinfo->jack_input_buffers = g_malloc(sizeof(LADSPA_Data *) * port_count); procinfo->jack_output_buffers = g_malloc(sizeof(LADSPA_Data *) * port_count); } else { procinfo->jack_input_ports = g_realloc(procinfo->jack_input_ports, sizeof(jack_port_t *) * port_count); procinfo->jack_output_ports = g_realloc(procinfo->jack_output_ports, sizeof(jack_port_t *) * port_count); procinfo->jack_input_buffers = g_realloc(procinfo->jack_input_buffers, sizeof(LADSPA_Data *) * port_count); procinfo->jack_output_buffers = g_realloc(procinfo->jack_output_buffers, sizeof(LADSPA_Data *) * port_count); } for (i = procinfo->port_count; i < port_count; i++) { for (in = 0; in < 2; in++) { port_name = g_strdup_printf("%s_%ld", in ? "in" : "out", i + 1); //mlt_log_debug( NULL, _("Creating %s port %s\n"), in ? "input" : "output", port_name); port_ptr = (in ? &procinfo->jack_input_ports[i] : &procinfo->jack_output_ports[i]); *port_ptr = jack_port_register(procinfo->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, in ? JackPortIsInput : JackPortIsOutput, 0); if (!*port_ptr) { mlt_log_error(NULL, "%s: could not register port '%s'; aborting\n", __FUNCTION__, port_name); return 1; } //mlt_log_debug( NULL, _("Created %s port %s\n"), in ? "input" : "output", port_name); if ((in && connect_inputs) || (!in && connect_outputs)) lv2_process_info_connect_port(procinfo, in, i, port_name); g_free(port_name); } } procinfo->port_count = port_count; return 0; } #endif void lv2_process_info_set_channels(lv2_process_info_t *procinfo, unsigned long channels, gboolean connect_inputs, gboolean connect_outputs) { #ifdef WITH_JACK lv2_process_info_set_port_count(procinfo, channels, connect_inputs, connect_outputs); #endif procinfo->channels = channels; } lv2_process_info_t *lv2_process_info_new(const char *client_name, unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs) { lv2_process_info_t *procinfo; char *jack_client_name; int err; procinfo = g_malloc(sizeof(lv2_process_info_t)); procinfo->chain = NULL; procinfo->chain_end = NULL; #ifdef WITH_JACK procinfo->jack_client = NULL; procinfo->port_count = 0; procinfo->jack_input_ports = NULL; procinfo->jack_output_ports = NULL; #endif procinfo->channels = rack_channels; procinfo->channel_mask = 0xFFFFFFFF; procinfo->quit = FALSE; if (client_name == NULL) { lv2_sample_rate = 48000; // should be set externally before calling process_lv2 lv2_buffer_size = MAX_BUFFER_SIZE; procinfo->silent_buffer = g_malloc(sizeof(LADSPA_Data) * lv2_buffer_size); procinfo->jack_input_buffers = g_malloc(sizeof(LADSPA_Data *) * rack_channels); procinfo->jack_output_buffers = g_malloc(sizeof(LADSPA_Data *) * rack_channels); return procinfo; } /* sort out the client name */ procinfo->jack_client_name = jack_client_name = strdup(client_name); for (err = 0; jack_client_name[err] != '\0'; err++) { if (jack_client_name[err] == ' ') jack_client_name[err] = '_'; else if (!isalnum( jack_client_name [err])) { /* shift all the chars up one (to remove the non-alphanumeric char) */ int i; for (i = err; jack_client_name[i] != '\0'; i++) jack_client_name[i] = jack_client_name[i + 1]; } else if (isupper(jack_client_name[err])) jack_client_name[err] = tolower(jack_client_name[err]); } /* #ifdef WITH_JACK err = lv2_process_info_connect_jack (procinfo); if (err) { /\* g_free (procinfo); *\/ return NULL; /\* abort (); *\/ } lv2_sample_rate = jack_get_sample_rate (procinfo->jack_client); lv2_buffer_size = jack_get_sample_rate (procinfo->jack_client); jack_set_process_callback (procinfo->jack_client, process_jack, procinfo); pthread_mutex_lock( &g_activate_mutex ); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); pthread_mutex_unlock( &g_activate_mutex ); jack_activate (procinfo->jack_client); err = lv2_process_info_set_port_count (procinfo, rack_channels, connect_inputs, connect_outputs); if (err) return NULL; #endif */ return procinfo; } void lv2_process_info_destroy(lv2_process_info_t *procinfo) { #ifdef WITH_JACK if (procinfo->jack_client) { jack_deactivate(procinfo->jack_client); jack_client_close(procinfo->jack_client); } g_free(procinfo->jack_input_ports); g_free(procinfo->jack_output_ports); #endif g_free(procinfo->jack_input_buffers); g_free(procinfo->jack_output_buffers); g_free(procinfo->silent_buffer); g_free(procinfo); } void lv2_process_quit(lv2_process_info_t *procinfo) { procinfo->quit = TRUE; } mlt-7.38.0/src/modules/jackrack/lv2_process.h000664 000000 000000 00000005333 15172202314 021010 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JLH_PROCESS_H__ #define __JLH_PROCESS_H__ #include #ifdef WITH_JACK #include #endif #include #include "lock_free_fifo.h" typedef struct _lv2_process_info lv2_process_info_t; /** this is what gets passed to the process() callback and contains all the data the process callback will need */ struct _lv2_process_info { /** the plugin instance chain */ struct _lv2_plugin *chain; struct _lv2_plugin *chain_end; #ifdef WITH_JACK jack_client_t *jack_client; unsigned long port_count; jack_port_t **jack_input_ports; jack_port_t **jack_output_ports; #endif unsigned long channels; unsigned long channel_mask; LADSPA_Data **jack_input_buffers; LADSPA_Data **jack_output_buffers; LADSPA_Data *silent_buffer; char *jack_client_name; int quit; }; #ifndef WITH_JACK typedef guint32 jack_nframes_t; #endif extern jack_nframes_t lv2_sample_rate; extern jack_nframes_t lv2_buffer_size; lv2_process_info_t *lv2_process_info_new(const char *client_name, unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs); void lv2_process_info_destroy(lv2_process_info_t *procinfo); void lv2_process_info_set_channels(lv2_process_info_t *procinfo, unsigned long channels, gboolean connect_inputs, gboolean connect_outputs); int process_lv2(lv2_process_info_t *procinfo, jack_nframes_t frames, LADSPA_Data **inputs, LADSPA_Data **outputs); /* #ifdef WITH_JACK int process_jack (jack_nframes_t frames, void * data); #endif */ void lv2_process_quit(lv2_process_info_t *procinfo); #endif /* __JLH_PROCESS_H__ */ mlt-7.38.0/src/modules/jackrack/lv2_urid_helper.h000664 000000 000000 00000004047 15172202314 021635 0ustar00rootroot000000 000000 /* * LV2 URID Helpers * * Adapted from lv2bench.c from lilv project https://drobilla.net/software/lilv.html - http://gitlab.com/lv2/lilv.git * * Copyright (C) David Robillard 2012-2019 (d@drobilla.net) * Copyright (C) mr.fantastic (mrfantastic@firemail.cc) and MLT project 2024 * * ISC License * SPDX-License-Identifier: ISC * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * 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. */ #ifndef __LV2_URID_HELPER_H__ #define __LV2_URID_HELPER_H__ typedef struct { char **uris; size_t n_uris; } URITable; static void uri_table_init(URITable *table) { table->uris = NULL; table->n_uris = 0; } static LV2_URID uri_table_map(LV2_URID_Map_Handle handle, const char *uri) { URITable *table = (URITable *) handle; for (size_t i = 0; i < table->n_uris; ++i) { if (!strcmp(table->uris[i], uri)) { return i + 1; } } const size_t len = strlen(uri); table->uris = (char **) realloc(table->uris, ++table->n_uris * sizeof(char *)); table->uris[table->n_uris - 1] = (char *) malloc(len + 1); memcpy(table->uris[table->n_uris - 1], uri, len + 1); return table->n_uris; } static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid) { URITable *table = (URITable *) handle; if (urid > 0 && urid <= table->n_uris) { return table->uris[urid - 1]; } return NULL; } #endif /* __LV2_URID_HELPER_H__ */ mlt-7.38.0/src/modules/jackrack/lv2blacklist.txt000664 000000 000000 00000000000 15172202314 021515 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/jackrack/plugin.c000664 000000 000000 00000037314 15172202314 020044 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2021 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include "plugin.h" #include "jack_rack.h" #include "process.h" #include "framework/mlt_log.h" #define CONTROL_FIFO_SIZE 128 #ifdef WITH_JACK /* swap over the jack ports in two plugins */ static void plugin_swap_aux_ports (plugin_t * plugin, plugin_t * other) { guint copy; jack_port_t ** aux_ports_tmp; for (copy = 0; copy < plugin->copies; copy++) { aux_ports_tmp = other->holders[copy].aux_ports; other->holders[copy].aux_ports = plugin->holders[copy].aux_ports; plugin->holders[copy].aux_ports = aux_ports_tmp; } } #endif /** connect up the ladspa instance's input buffers to the previous plugin's audio memory. make sure to check that plugin->prev exists. */ void plugin_connect_input_ports (plugin_t * plugin, LADSPA_Data ** inputs) { gint copy; unsigned long channel; unsigned long rack_channel; if (!plugin || !inputs) return; rack_channel = 0; for (copy = 0; copy < plugin->copies; copy++) { for (channel = 0; channel < plugin->desc->channels; channel++) { plugin->descriptor-> connect_port (plugin->holders[copy].instance, plugin->desc->audio_input_port_indicies[channel], inputs[rack_channel]); rack_channel++; } } plugin->audio_input_memory = inputs; } /** connect up a plugin's output ports to its own audio_output_memory output memory */ void plugin_connect_output_ports (plugin_t * plugin) { gint copy; unsigned long channel; unsigned long rack_channel = 0; if (!plugin) return; for (copy = 0; copy < plugin->copies; copy++) { for (channel = 0; channel < plugin->desc->channels; channel++) { plugin->descriptor-> connect_port (plugin->holders[copy].instance, plugin->desc->audio_output_port_indicies[channel], plugin->audio_output_memory[rack_channel]); rack_channel++; } } } void process_add_plugin (process_info_t * procinfo, plugin_t * plugin) { /* sort out list pointers */ plugin->next = NULL; plugin->prev = procinfo->chain_end; if (procinfo->chain_end) procinfo->chain_end->next = plugin; else procinfo->chain = plugin; procinfo->chain_end = plugin; } /** remove a plugin from the chain */ plugin_t * process_remove_plugin (process_info_t * procinfo, plugin_t *plugin) { /* sort out chain pointers */ if (plugin->prev) plugin->prev->next = plugin->next; else procinfo->chain = plugin->next; if (plugin->next) plugin->next->prev = plugin->prev; else procinfo->chain_end = plugin->prev; #ifdef WITH_JACK /* sort out the aux ports */ if (procinfo->jack_client && plugin->desc->aux_channels > 0) { plugin_t * other; for (other = plugin->next; other; other = other->next) if (other->desc->id == plugin->desc->id) plugin_swap_aux_ports (plugin, other); } #endif return plugin; } /** enable/disable a plugin */ void process_ablise_plugin (process_info_t * procinfo, plugin_t *plugin, gboolean enable) { plugin->enabled = enable; } /** enable/disable a plugin */ void process_ablise_plugin_wet_dry (process_info_t * procinfo, plugin_t *plugin, gboolean enable) { plugin->wet_dry_enabled = enable; } /** move a plugin up or down one place in the chain */ void process_move_plugin (process_info_t * procinfo, plugin_t *plugin, gint up) { /* other plugins in the chain */ plugin_t *pp = NULL, *p, *n, *nn = NULL; /* note that we should never receive an illogical move request ie, there will always be at least 1 plugin before for an up request or 1 plugin after for a down request */ /* these are pointers to the plugins surrounding the specified one: { pp, p, plugin, n, nn } which makes things much clearer than tptr, tptr2 etc */ p = plugin->prev; if (p) pp = p->prev; n = plugin->next; if (n) nn = n->next; if (up) { if (!p) return; if (pp) pp->next = plugin; else procinfo->chain = plugin; p->next = n; p->prev = plugin; plugin->prev = pp; plugin->next = p; if (n) n->prev = p; else procinfo->chain_end = p; } else { if (!n) return; if (p) p->next = n; else procinfo->chain = n; n->prev = p; n->next = plugin; plugin->prev = n; plugin->next = nn; if (nn) nn->prev = plugin; else procinfo->chain_end = plugin; } #ifdef WITH_JACK if (procinfo->jack_client && plugin->desc->aux_channels > 0) { plugin_t * other; other = up ? plugin->next : plugin->prev; /* swap around the jack ports */ if (other->desc->id == plugin->desc->id) plugin_swap_aux_ports (plugin, other); } #endif } /** exchange an existing plugin for a newly created one */ plugin_t * process_change_plugin (process_info_t * procinfo, plugin_t *plugin, plugin_t * new_plugin) { new_plugin->next = plugin->next; new_plugin->prev = plugin->prev; if (plugin->prev) plugin->prev->next = new_plugin; else procinfo->chain = new_plugin; if (plugin->next) plugin->next->prev = new_plugin; else procinfo->chain_end = new_plugin; #ifdef WITH_JACK /* sort out the aux ports */ if (procinfo->jack_client && plugin->desc->aux_channels > 0) { plugin_t * other; for (other = plugin->next; other; other = other->next) if (other->desc->id == plugin->desc->id) plugin_swap_aux_ports (plugin, other); } #endif return plugin; } /****************************************** ************* non RT stuff *************** ******************************************/ static int plugin_open_plugin (plugin_desc_t * desc, void ** dl_handle_ptr, const LADSPA_Descriptor ** descriptor_ptr) { void * dl_handle; const char * dlerr; LADSPA_Descriptor_Function get_descriptor; /* clear the error report */ dlerror (); /* open the object file */ dl_handle = dlopen (desc->object_file, RTLD_NOW); dlerr = dlerror (); if (!dl_handle || dlerr) { if (!dlerr) dlerr = "unknown error"; mlt_log_warning( NULL, "%s: error opening shared object file '%s': %s\n", __FUNCTION__, desc->object_file, dlerr); return 1; } /* get the get_descriptor function */ get_descriptor = (LADSPA_Descriptor_Function) dlsym (dl_handle, "ladspa_descriptor"); dlerr = dlerror(); if (dlerr) { if (!dlerr) dlerr = "unknown error"; mlt_log_warning( NULL, "%s: error finding descriptor symbol in object file '%s': %s\n", __FUNCTION__, desc->object_file, dlerr); dlclose (dl_handle); return 1; } #ifdef __APPLE__ if (!get_descriptor (desc->index)) { void (*constructor)(void) = dlsym (dl_handle, "_init"); if (constructor) constructor(); } #endif *descriptor_ptr = get_descriptor (desc->index); if (!*descriptor_ptr) { mlt_log_warning( NULL, "%s: error finding index %lu in object file '%s'\n", __FUNCTION__, desc->index, desc->object_file); dlclose (dl_handle); return 1; } *dl_handle_ptr = dl_handle; return 0; } static int plugin_instantiate (const LADSPA_Descriptor * descriptor, unsigned long plugin_index, gint copies, LADSPA_Handle * instances) { gint i; for (i = 0; i < copies; i++) { instances[i] = descriptor->instantiate (descriptor, sample_rate); if (!instances[i]) { unsigned long d; for (d = 0; d < i; d++) descriptor->cleanup (instances[d]); return 1; } } return 0; } #ifdef WITH_JACK static void plugin_create_aux_ports (plugin_t * plugin, guint copy, jack_rack_t * jack_rack) { plugin_desc_t * desc; // plugin_slot_t * slot; unsigned long aux_channel = 1; unsigned long plugin_index = 1; unsigned long i; char port_name[64]; char * plugin_name; char * ptr; // GList * list; ladspa_holder_t * holder; desc = plugin->desc; holder = plugin->holders + copy; holder->aux_ports = g_malloc (sizeof (jack_port_t *) * desc->aux_channels); /* make the plugin name jack worthy */ ptr = plugin_name = g_strndup (plugin->desc->name, 7); while (*ptr != '\0') { if (*ptr == ' ') *ptr = '_'; else *ptr = tolower (*ptr); ptr++; } /* for (list = jack_rack->slots; list; list = g_list_next (list)) { slot = (plugin_slot_t *) list->data; if (slot->plugin->desc->id == plugin->desc->id) plugin_index++; } */ for (i = 0; i < desc->aux_channels; i++, aux_channel++) { sprintf (port_name, "%s_%ld-%d_%c%ld", plugin_name, plugin_index, copy + 1, desc->aux_are_input ? 'i' : 'o', aux_channel); holder->aux_ports[i] = jack_port_register (jack_rack->procinfo->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, desc->aux_are_input ? JackPortIsInput : JackPortIsOutput, 0); if (!holder->aux_ports[i]) { mlt_log_panic( NULL, "Could not register jack port '%s'; aborting\n", port_name); } } g_free (plugin_name); } #endif static void plugin_init_holder (plugin_t * plugin, guint copy, LADSPA_Handle instance, jack_rack_t * jack_rack) { unsigned long i; plugin_desc_t * desc; ladspa_holder_t * holder; desc = plugin->desc; holder = plugin->holders + copy; holder->instance = instance; if (desc->control_port_count > 0) { holder->ui_control_fifos = g_malloc (sizeof (lff_t) * desc->control_port_count); holder->control_memory = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count); } else { holder->ui_control_fifos = NULL; holder->control_memory = NULL; } for (i = 0; i < desc->control_port_count; i++) { lff_init (holder->ui_control_fifos + i, CONTROL_FIFO_SIZE, sizeof (LADSPA_Data)); holder->control_memory[i] = plugin_desc_get_default_control_value (desc, desc->control_port_indicies[i], sample_rate); plugin->descriptor-> connect_port (instance, desc->control_port_indicies[i], holder->control_memory + i); } if (desc->status_port_count > 0) { holder->status_memory = g_malloc (sizeof (LADSPA_Data) * desc->status_port_count); } else { holder->status_memory = NULL; } for (i = 0; i < desc->status_port_count; i++) { plugin->descriptor-> connect_port (instance, desc->status_port_indicies[i], holder->status_memory + i); } #ifdef WITH_JACK if (jack_rack->procinfo->jack_client && plugin->desc->aux_channels > 0) plugin_create_aux_ports (plugin, copy, jack_rack); #endif if (plugin->descriptor->activate) plugin->descriptor->activate (instance); } plugin_t * plugin_new (plugin_desc_t * desc, jack_rack_t * jack_rack) { void * dl_handle; const LADSPA_Descriptor * descriptor; LADSPA_Handle * instances; gint copies; unsigned long i; int err; plugin_t * plugin; /* open the plugin */ err = plugin_open_plugin (desc, &dl_handle, &descriptor); if (err) return NULL; /* create the instances */ copies = plugin_desc_get_copies (desc, jack_rack->channels); instances = g_malloc (sizeof (LADSPA_Handle) * copies); err = plugin_instantiate (descriptor, desc->index, copies, instances); if (err) { g_free (instances); dlclose (dl_handle); return NULL; } plugin = g_malloc (sizeof (plugin_t)); plugin->descriptor = descriptor; plugin->dl_handle = dl_handle; plugin->desc = desc; plugin->copies = copies; plugin->enabled = FALSE; plugin->next = NULL; plugin->prev = NULL; plugin->wet_dry_enabled = FALSE; plugin->jack_rack = jack_rack; /* create audio memory and wet/dry stuff */ plugin->audio_output_memory = g_malloc (sizeof (LADSPA_Data *) * jack_rack->channels); plugin->wet_dry_fifos = g_malloc (sizeof (lff_t) * jack_rack->channels); plugin->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * jack_rack->channels); for (i = 0; i < jack_rack->channels; i++) { plugin->audio_output_memory[i] = g_malloc (sizeof (LADSPA_Data) * buffer_size); lff_init (plugin->wet_dry_fifos + i, CONTROL_FIFO_SIZE, sizeof (LADSPA_Data)); plugin->wet_dry_values[i] = 1.0; } /* create holders and fill them out */ plugin->holders = g_malloc (sizeof (ladspa_holder_t) * copies); for (i = 0; i < copies; i++) plugin_init_holder (plugin, i, instances[i], jack_rack); return plugin; } void plugin_destroy (plugin_t * plugin) { unsigned long i, j; int err; /* destroy holders */ for (i = 0; i < plugin->copies; i++) { if (plugin->descriptor->deactivate) plugin->descriptor->deactivate (plugin->holders[i].instance); /* if (plugin->descriptor->cleanup) plugin->descriptor->cleanup (plugin->holders[i].instance); */ if (plugin->desc->control_port_count > 0) { for (j = 0; j < plugin->desc->control_port_count; j++) { lff_free (plugin->holders[i].ui_control_fifos + j); } g_free (plugin->holders[i].ui_control_fifos); g_free (plugin->holders[i].control_memory); } if (plugin->desc->status_port_count > 0) { g_free (plugin->holders[i].status_memory); } #ifdef WITH_JACK /* aux ports */ if (plugin->jack_rack->procinfo->jack_client && plugin->desc->aux_channels > 0) { for (j = 0; j < plugin->desc->aux_channels; j++) { err = jack_port_unregister (plugin->jack_rack->procinfo->jack_client, plugin->holders[i].aux_ports[j]); if (err) mlt_log_warning( NULL, "%s: could not unregister jack port\n", __FUNCTION__); } g_free (plugin->holders[i].aux_ports); } #endif } g_free (plugin->holders); for (i = 0; i < plugin->jack_rack->channels; i++) { g_free (plugin->audio_output_memory[i]); lff_free (plugin->wet_dry_fifos + i); } g_free (plugin->audio_output_memory); g_free (plugin->wet_dry_fifos); g_free (plugin->wet_dry_values); err = dlclose (plugin->dl_handle); if (err) { mlt_log_warning( NULL, "%s: error closing shared object '%s': %s\n", __FUNCTION__, plugin->desc->object_file, dlerror ()); } g_free (plugin); } /* EOF */ mlt-7.38.0/src/modules/jackrack/plugin.h000664 000000 000000 00000005521 15172202314 020044 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2021 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JR_PLUGIN_H__ #define __JR_PLUGIN_H__ #include #include #include #ifdef WITH_JACK #include #endif #include "process.h" #include "plugin_desc.h" typedef struct _ladspa_holder ladspa_holder_t; typedef struct _plugin plugin_t; struct _ladspa_holder { LADSPA_Handle instance; lff_t * ui_control_fifos; LADSPA_Data * control_memory; LADSPA_Data * status_memory; #ifdef WITH_JACK jack_port_t ** aux_ports; #endif }; struct _plugin { plugin_desc_t * desc; gint enabled; gint copies; ladspa_holder_t * holders; LADSPA_Data ** audio_input_memory; LADSPA_Data ** audio_output_memory; gboolean wet_dry_enabled; /* 1.0 = all wet, 0.0 = all dry, 0.5 = 50% wet/50% dry */ LADSPA_Data * wet_dry_values; lff_t * wet_dry_fifos; plugin_t * next; plugin_t * prev; const LADSPA_Descriptor * descriptor; void * dl_handle; struct _jack_rack * jack_rack; }; void process_add_plugin (process_info_t *, plugin_t *plugin); plugin_t * process_remove_plugin (process_info_t *, plugin_t *plugin); void process_ablise_plugin (process_info_t *, plugin_t *plugin, gboolean able); void process_ablise_plugin_wet_dry (process_info_t *, plugin_t *plugin, gboolean enable); void process_move_plugin (process_info_t *, plugin_t *plugin, gint up); plugin_t * process_change_plugin (process_info_t *, plugin_t *plugin, plugin_t * new_plugin); struct _jack_rack; struct _ui; plugin_t * plugin_new (plugin_desc_t * plugin_desc, struct _jack_rack * jack_rack); void plugin_destroy (plugin_t * plugin); void plugin_connect_input_ports (plugin_t * plugin, LADSPA_Data ** inputs); void plugin_connect_output_ports (plugin_t * plugin); #endif /* __JR_PLUGIN_H__ */ mlt-7.38.0/src/modules/jackrack/plugin_desc.c000664 000000 000000 00000105403 15172202314 021035 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include "plugin_desc.h" #define set_string_property(property, value) \ \ if (property) \ g_free(property); \ \ if (value) \ (property) = g_strdup(value); \ else \ (property) = NULL; #ifdef WITH_LV2 extern LilvNode *lv2_input_class; extern LilvNode *lv2_output_class; extern LilvNode *lv2_audio_class; extern LilvNode *lv2_control_class; extern LilvNode *lv2_atom_class; extern LilvNode *lv2_integer_property; extern LilvNode *lv2_logarithmic_property; extern LilvNode *lv2_toggled_property; extern LilvNode *lv2_enumeration_property; #endif void plugin_desc_set_ports(plugin_desc_t *pd, unsigned long port_count, const LADSPA_PortDescriptor *port_descriptors, const LADSPA_PortRangeHint *port_range_hints, const char *const *port_names); static void plugin_desc_init(plugin_desc_t *pd) { pd->object_file = NULL; pd->id = 0; pd->name = NULL; pd->maker = NULL; pd->properties = 0; pd->channels = 0; pd->port_count = 0; pd->port_descriptors = NULL; pd->port_range_hints = NULL; pd->audio_input_port_indicies = NULL; pd->audio_output_port_indicies = NULL; pd->audio_aux_port_indicies = NULL; pd->control_port_count = 0; pd->control_port_indicies = NULL; pd->status_port_count = 0; pd->status_port_indicies = NULL; pd->aux_channels = 0; pd->aux_are_input = TRUE; pd->has_input = TRUE; } static void plugin_desc_free_ports(plugin_desc_t *pd) { if (pd->port_count) { g_free(pd->port_descriptors); g_free(pd->port_range_hints); g_free(pd->audio_input_port_indicies); g_free(pd->audio_output_port_indicies); g_free(pd->port_names); g_free(pd->control_port_indicies); g_free(pd->status_port_indicies); g_free(pd->audio_aux_port_indicies); pd->port_descriptors = NULL; pd->port_range_hints = NULL; pd->audio_input_port_indicies = NULL; pd->audio_output_port_indicies = NULL; pd->port_names = NULL; pd->control_port_indicies = NULL; pd->status_port_indicies = NULL; pd->audio_aux_port_indicies = NULL; pd->port_count = 0; } } static void plugin_desc_free(plugin_desc_t *pd) { plugin_desc_set_object_file(pd, NULL); plugin_desc_set_name(pd, NULL); plugin_desc_set_maker(pd, NULL); plugin_desc_free_ports(pd); } plugin_desc_t *plugin_desc_new() { plugin_desc_t *pd; pd = g_malloc(sizeof(plugin_desc_t)); plugin_desc_init(pd); return pd; } plugin_desc_t *plugin_desc_new_with_descriptor(const char *object_file, unsigned long index, const LADSPA_Descriptor *descriptor) { plugin_desc_t *pd; pd = plugin_desc_new(); plugin_desc_set_object_file(pd, object_file); plugin_desc_set_index(pd, index); plugin_desc_set_id(pd, descriptor->UniqueID); plugin_desc_set_name(pd, descriptor->Name); plugin_desc_set_maker(pd, descriptor->Maker); plugin_desc_set_properties(pd, descriptor->Properties); plugin_desc_set_ports(pd, descriptor->PortCount, descriptor->PortDescriptors, descriptor->PortRangeHints, descriptor->PortNames); pd->rt = LADSPA_IS_HARD_RT_CAPABLE(pd->properties) ? TRUE : FALSE; return pd; } void plugin_desc_destroy(plugin_desc_t *pd) { plugin_desc_free(pd); g_free(pd); } void plugin_desc_set_object_file(plugin_desc_t *pd, const char *object_file) { set_string_property(pd->object_file, object_file); } void plugin_desc_set_index(plugin_desc_t *pd, unsigned long index) { pd->index = index; } void plugin_desc_set_id(plugin_desc_t *pd, unsigned long id) { pd->id = id; } void plugin_desc_set_name(plugin_desc_t *pd, const char *name) { set_string_property(pd->name, name); } void plugin_desc_set_maker(plugin_desc_t *pd, const char *maker) { set_string_property(pd->maker, maker); } void plugin_desc_set_properties(plugin_desc_t *pd, LADSPA_Properties properties) { pd->properties = properties; } static void plugin_desc_add_audio_port_index(unsigned long **indices, unsigned long *current_port_count, unsigned long index) { (*current_port_count)++; if (*current_port_count == 0) *indices = g_malloc(sizeof(unsigned long) * *current_port_count); else *indices = g_realloc(*indices, sizeof(unsigned long) * *current_port_count); (*indices)[*current_port_count - 1] = index; } static void plugin_desc_set_port_counts(plugin_desc_t *pd) { unsigned long i; unsigned long icount = 0; unsigned long ocount = 0; for (i = 0; i < pd->port_count; i++) { if (LADSPA_IS_PORT_AUDIO(pd->port_descriptors[i])) { if (LADSPA_IS_PORT_INPUT(pd->port_descriptors[i])) plugin_desc_add_audio_port_index(&pd->audio_input_port_indicies, &icount, i); else plugin_desc_add_audio_port_index(&pd->audio_output_port_indicies, &ocount, i); } else { if (LADSPA_IS_PORT_OUTPUT(pd->port_descriptors[i])) { pd->status_port_count++; if (pd->status_port_count == 0) pd->status_port_indicies = g_malloc(sizeof(unsigned long) * pd->status_port_count); else pd->status_port_indicies = g_realloc(pd->status_port_indicies, sizeof(unsigned long) * pd->status_port_count); pd->status_port_indicies[pd->status_port_count - 1] = i; } else { pd->control_port_count++; if (pd->control_port_count == 0) pd->control_port_indicies = g_malloc(sizeof(unsigned long) * pd->control_port_count); else pd->control_port_indicies = g_realloc(pd->control_port_indicies, sizeof(unsigned long) * pd->control_port_count); pd->control_port_indicies[pd->control_port_count - 1] = i; } } } if (icount == ocount) pd->channels = icount; else if (icount == 0) { pd->channels = ocount; pd->has_input = FALSE; } else { /* deal with auxiliary ports */ unsigned long **port_indicies; unsigned long port_count; unsigned long i, j; if (icount > ocount) { pd->channels = ocount; pd->aux_channels = icount - ocount; pd->aux_are_input = TRUE; port_indicies = &pd->audio_input_port_indicies; port_count = icount; } else { pd->channels = icount; pd->aux_channels = ocount - icount; pd->aux_are_input = FALSE; port_indicies = &pd->audio_output_port_indicies; port_count = ocount; } /* allocate indices */ pd->audio_aux_port_indicies = g_malloc(sizeof(unsigned long) * pd->aux_channels); /* copy indices */ for (i = pd->channels, j = 0; i < port_count; i++, j++) pd->audio_aux_port_indicies[j] = (*port_indicies)[i]; /* shrink the main indices to only have channels indices */ *port_indicies = g_realloc(*port_indicies, sizeof(unsigned long) * pd->channels); } } void plugin_desc_set_ports(plugin_desc_t *pd, unsigned long port_count, const LADSPA_PortDescriptor *port_descriptors, const LADSPA_PortRangeHint *port_range_hints, const char *const *port_names) { unsigned long i; plugin_desc_free_ports(pd); if (!port_count) return; pd->port_count = port_count; pd->port_descriptors = g_malloc(sizeof(LADSPA_PortDescriptor) * port_count); pd->port_range_hints = g_malloc(sizeof(LADSPA_PortRangeHint) * port_count); pd->port_names = g_malloc(sizeof(char *) * port_count); memcpy(pd->port_descriptors, port_descriptors, sizeof(LADSPA_PortDescriptor) * port_count); memcpy(pd->port_range_hints, port_range_hints, sizeof(LADSPA_PortRangeHint) * port_count); for (i = 0; i < port_count; i++) pd->port_names[i] = g_strdup(port_names[i]); plugin_desc_set_port_counts(pd); } LADSPA_Data plugin_desc_get_default_control_value(plugin_desc_t *pd, unsigned long port_index, guint32 sample_rate) { LADSPA_Data upper, lower; LADSPA_PortRangeHintDescriptor hint_descriptor; hint_descriptor = pd->port_range_hints[port_index].HintDescriptor; /* set upper and lower, possibly adjusted to the sample rate */ if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) { upper = pd->port_range_hints[port_index].UpperBound * (LADSPA_Data) sample_rate; lower = pd->port_range_hints[port_index].LowerBound * (LADSPA_Data) sample_rate; } else { upper = pd->port_range_hints[port_index].UpperBound; lower = pd->port_range_hints[port_index].LowerBound; } if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { if (lower < FLT_EPSILON) lower = FLT_EPSILON; } if (LADSPA_IS_HINT_HAS_DEFAULT(hint_descriptor)) { if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint_descriptor)) { return lower; } else if (LADSPA_IS_HINT_DEFAULT_LOW(hint_descriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { return exp(log(lower) * 0.75 + log(upper) * 0.25); } else { return lower * 0.75 + upper * 0.25; } } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint_descriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { return exp(log(lower) * 0.5 + log(upper) * 0.5); } else { return lower * 0.5 + upper * 0.5; } } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint_descriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { return exp(log(lower) * 0.25 + log(upper) * 0.75); } else { return lower * 0.25 + upper * 0.75; } } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint_descriptor)) { return upper; } else if (LADSPA_IS_HINT_DEFAULT_0(hint_descriptor)) { return 0.0; } else if (LADSPA_IS_HINT_DEFAULT_1(hint_descriptor)) { if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) { return (LADSPA_Data) sample_rate; } else { return 1.0; } } else if (LADSPA_IS_HINT_DEFAULT_100(hint_descriptor)) { if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) { return 100.0 * (LADSPA_Data) sample_rate; } else { return 100.0; } } else if (LADSPA_IS_HINT_DEFAULT_440(hint_descriptor)) { if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) { return 440.0 * (LADSPA_Data) sample_rate; } else { return 440.0; } } } else { /* try and find a reasonable default */ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint_descriptor)) { return lower; } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) { return upper; } } return 0.0; } LADSPA_Data plugin_desc_change_control_value(plugin_desc_t *pd, unsigned long control_index, LADSPA_Data value, guint32 old_sample_rate, guint32 new_sample_rate) { if (LADSPA_IS_HINT_SAMPLE_RATE(pd->port_range_hints[control_index].HintDescriptor)) { LADSPA_Data old_sr, new_sr; old_sr = (LADSPA_Data) old_sample_rate; new_sr = (LADSPA_Data) new_sample_rate; value /= old_sr; value *= new_sr; } return value; } gint plugin_desc_get_copies(plugin_desc_t *pd, unsigned long rack_channels) { gint copies = 1; if (pd->channels > rack_channels) return 0; while (pd->channels * copies < rack_channels) copies++; if (pd->channels * copies > rack_channels) return 0; return copies; } #ifdef WITH_LV2 static void lv2_plugin_desc_init(lv2_plugin_desc_t *pd) { pd->uri = NULL; pd->id = 0; pd->name = NULL; pd->maker = NULL; pd->properties = 0; pd->channels = 0; pd->port_count = 0; pd->port_descriptors = NULL; pd->port_range_hints = NULL; pd->audio_input_port_indicies = NULL; pd->audio_output_port_indicies = NULL; pd->audio_aux_port_indicies = NULL; pd->control_port_count = 0; pd->control_port_indicies = NULL; pd->status_port_count = 0; pd->status_port_indicies = NULL; pd->aux_channels = 0; pd->aux_are_input = TRUE; pd->has_input = TRUE; } void lv2_plugin_desc_set_uri(lv2_plugin_desc_t *pd, const char *uri) { set_string_property(pd->uri, uri); } void lv2_plugin_desc_set_index(lv2_plugin_desc_t *pd, unsigned long index) { pd->index = index; } void lv2_plugin_desc_set_id(lv2_plugin_desc_t *pd, unsigned long id) { pd->id = id; } void lv2_plugin_desc_set_name(lv2_plugin_desc_t *pd, const char *name) { set_string_property(pd->name, name); } void lv2_plugin_desc_set_maker(lv2_plugin_desc_t *pd, const char *maker) { set_string_property(pd->maker, maker); } void lv2_plugin_desc_set_properties(lv2_plugin_desc_t *pd, LADSPA_Properties properties) { pd->properties = properties; } static void lv2_plugin_desc_set_port_counts(lv2_plugin_desc_t *pd) { unsigned long i; unsigned long icount = 0; unsigned long ocount = 0; for (i = 0; i < pd->port_count; i++) { /* if (LADSPA_IS_PORT_ATOM (pd->port_descriptors[i])) { if(yes) printf("%li atom port\n", i); } else */ if (LADSPA_IS_PORT_AUDIO(pd->port_descriptors[i])) { if (LADSPA_IS_PORT_INPUT(pd->port_descriptors[i])) { plugin_desc_add_audio_port_index(&pd->audio_input_port_indicies, &icount, i); } else { plugin_desc_add_audio_port_index(&pd->audio_output_port_indicies, &ocount, i); } } else { if (LADSPA_IS_PORT_OUTPUT(pd->port_descriptors[i])) { pd->status_port_count++; if (pd->status_port_count == 0) pd->status_port_indicies = g_malloc(sizeof(unsigned long) * pd->status_port_count); else pd->status_port_indicies = g_realloc(pd->status_port_indicies, sizeof(unsigned long) * pd->status_port_count); pd->status_port_indicies[pd->status_port_count - 1] = i; } else { pd->control_port_count++; if (pd->control_port_count == 0) pd->control_port_indicies = g_malloc(sizeof(unsigned long) * pd->control_port_count); else pd->control_port_indicies = g_realloc(pd->control_port_indicies, sizeof(unsigned long) * pd->control_port_count); pd->control_port_indicies[pd->control_port_count - 1] = i; } } } if (icount == ocount) pd->channels = icount; else if (icount == 0) { pd->channels = ocount; pd->has_input = FALSE; } else { /* deal with auxiliary ports */ unsigned long **port_indicies; unsigned long port_count; unsigned long i, j; if (icount > ocount) { pd->channels = ocount; pd->aux_channels = icount - ocount; pd->aux_are_input = TRUE; port_indicies = &pd->audio_input_port_indicies; port_count = icount; } else { pd->channels = icount; pd->aux_channels = ocount - icount; pd->aux_are_input = FALSE; port_indicies = &pd->audio_output_port_indicies; port_count = ocount; } /* allocate indices */ pd->audio_aux_port_indicies = g_malloc(sizeof(unsigned long) * pd->aux_channels); /* copy indices */ for (i = pd->channels, j = 0; i < port_count; i++, j++) pd->audio_aux_port_indicies[j] = (*port_indicies)[i]; /* shrink the main indices to only have channels indices */ *port_indicies = g_realloc(*port_indicies, sizeof(unsigned long) * pd->channels); } } static void lv2_plugin_desc_free_ports(lv2_plugin_desc_t *pd) { if (pd->port_count) { g_free(pd->port_descriptors); g_free(pd->port_range_hints); g_free(pd->audio_input_port_indicies); g_free(pd->audio_output_port_indicies); g_free(pd->port_names); g_free(pd->control_port_indicies); g_free(pd->status_port_indicies); g_free(pd->audio_aux_port_indicies); pd->port_descriptors = NULL; pd->port_range_hints = NULL; pd->audio_input_port_indicies = NULL; pd->audio_output_port_indicies = NULL; pd->port_names = NULL; pd->control_port_indicies = NULL; pd->status_port_indicies = NULL; pd->audio_aux_port_indicies = NULL; pd->port_count = 0; } } static void lv2_plugin_desc_free(lv2_plugin_desc_t *pd) { g_free(pd->def_values); g_free(pd->min_values); g_free(pd->max_values); lv2_plugin_desc_set_uri(pd, NULL); lv2_plugin_desc_set_name(pd, NULL); lv2_plugin_desc_set_maker(pd, NULL); lv2_plugin_desc_free_ports(pd); } void lv2_plugin_desc_destroy(lv2_plugin_desc_t *pd) { lv2_plugin_desc_free(pd); g_free(pd); } lv2_plugin_desc_t *lv2_plugin_desc_new() { lv2_plugin_desc_t *pd; pd = g_malloc(sizeof(lv2_plugin_desc_t)); lv2_plugin_desc_init(pd); return pd; } void lv2_plugin_desc_set_ports(lv2_plugin_desc_t *pd, unsigned long port_count, const LADSPA_PortDescriptor *port_descriptors, const LADSPA_PortRangeHint *port_range_hints, const char *const *port_names) { unsigned long i; lv2_plugin_desc_free_ports(pd); if (!port_count) return; pd->port_count = port_count; pd->port_descriptors = g_malloc(sizeof(LADSPA_PortDescriptor) * port_count); pd->port_range_hints = g_malloc(sizeof(LADSPA_PortRangeHint) * port_count); pd->port_names = g_malloc(sizeof(char *) * port_count); memcpy(pd->port_descriptors, port_descriptors, sizeof(LADSPA_PortDescriptor) * port_count); memcpy(pd->port_range_hints, port_range_hints, sizeof(LADSPA_PortRangeHint) * port_count); for (i = 0; i < port_count; i++) pd->port_names[i] = g_strdup(port_names[i]); lv2_plugin_desc_set_port_counts(pd); } lv2_plugin_desc_t *lv2_plugin_desc_new_with_descriptor(const char *uri, unsigned long index, const LilvPlugin *plugin) { lv2_plugin_desc_t *pd; pd = lv2_plugin_desc_new(); LilvNode *val = NULL; char *str_ptr = strchr(uri, ':'); while (str_ptr != NULL) { *str_ptr++ = '^'; str_ptr = strchr(str_ptr, ':'); } lv2_plugin_desc_set_uri(pd, uri); str_ptr = strchr(uri, '^'); while (str_ptr != NULL) { *str_ptr++ = ':'; str_ptr = strchr(str_ptr, '^'); } lv2_plugin_desc_set_index(pd, index); val = lilv_plugin_get_name(plugin); lv2_plugin_desc_set_name(pd, lilv_node_as_string(val)); lv2_plugin_desc_set_maker(pd, lilv_node_as_string(lilv_plugin_get_author_name(plugin))); int PortCount = lilv_plugin_get_num_ports(plugin); char **PortNames = calloc(PortCount, sizeof(char *)); LADSPA_PortDescriptor *port_descriptors = calloc(PortCount, sizeof(LADSPA_PortDescriptor)); LADSPA_PortRangeHint *PortRangeHints = calloc(PortCount, sizeof(LADSPA_PortRangeHint)); pd->min_values = calloc(PortCount, sizeof(LADSPA_Data)), pd->max_values = calloc(PortCount, sizeof(LADSPA_Data)), pd->def_values = calloc(PortCount, sizeof(LADSPA_Data)); lilv_plugin_get_port_ranges_float(plugin, pd->min_values, pd->max_values, pd->def_values); int i; for (i = 0; i < PortCount; ++i) { const LilvPort *port = lilv_plugin_get_port_by_index(plugin, i); if (lilv_port_is_a(plugin, port, lv2_audio_class)) { port_descriptors[i] |= LADSPA_PORT_AUDIO; } if (lilv_port_is_a(plugin, port, lv2_input_class)) { port_descriptors[i] |= LADSPA_PORT_INPUT; } if (lilv_port_is_a(plugin, port, lv2_output_class)) { port_descriptors[i] |= LADSPA_PORT_OUTPUT; } if (lilv_port_is_a(plugin, port, lv2_control_class)) { port_descriptors[i] |= LADSPA_PORT_CONTROL; } if (lilv_port_is_a(plugin, port, lv2_atom_class)) { port_descriptors[i] |= LADSPA_PORT_ATOM; } if (lilv_port_has_property(plugin, port, lv2_integer_property)) { PortRangeHints[i].HintDescriptor |= LADSPA_HINT_INTEGER; } if (lilv_port_has_property(plugin, port, lv2_logarithmic_property)) { PortRangeHints[i].HintDescriptor |= LADSPA_HINT_LOGARITHMIC; } if (lilv_port_has_property(plugin, port, lv2_toggled_property)) { PortRangeHints[i].HintDescriptor |= LADSPA_HINT_TOGGLED; } if (lilv_port_has_property(plugin, port, lv2_enumeration_property)) { PortRangeHints[i].HintDescriptor |= LADSPA_HINT_ENUMERATION; } PortRangeHints[i].LowerBound = pd->min_values[i]; PortRangeHints[i].UpperBound = pd->max_values[i]; PortNames[i] = (char *) lilv_node_as_string(lilv_port_get_name(plugin, port)); } lv2_plugin_desc_set_ports(pd, PortCount, port_descriptors, PortRangeHints, (const char *const *) PortNames); free(PortNames); free(port_descriptors); return pd; } gint lv2_plugin_desc_get_copies(lv2_plugin_desc_t *pd, unsigned long rack_channels) { gint copies = 1; if (pd->channels > rack_channels) return 0; while (pd->channels * copies < rack_channels) copies++; if (pd->channels * copies > rack_channels) return 0; return copies; } #endif #ifdef WITH_VST2 void vst2_plugin_desc_set_ports(vst2_plugin_desc_t *pd, unsigned long port_count, const LADSPA_PortDescriptor *port_descriptors, const LADSPA_PortRangeHint *port_range_hints, const char *const *port_names); static void vst2_plugin_desc_init(vst2_plugin_desc_t *pd) { pd->object_file = NULL; pd->id = 0; pd->name = NULL; pd->maker = NULL; pd->properties = 0; pd->channels = 0; pd->port_count = 0; pd->port_descriptors = NULL; pd->port_range_hints = NULL; pd->audio_input_port_indicies = NULL; pd->audio_output_port_indicies = NULL; pd->audio_aux_port_indicies = NULL; pd->control_port_count = 0; pd->control_port_indicies = NULL; pd->status_port_count = 0; pd->status_port_indicies = NULL; pd->aux_channels = 0; pd->aux_are_input = TRUE; pd->has_input = TRUE; } static void vst2_plugin_desc_free_ports(vst2_plugin_desc_t *pd) { if (pd->port_count) { g_free(pd->port_descriptors); g_free(pd->port_range_hints); g_free(pd->audio_input_port_indicies); g_free(pd->audio_output_port_indicies); g_free(pd->port_names); g_free(pd->control_port_indicies); g_free(pd->status_port_indicies); g_free(pd->audio_aux_port_indicies); pd->port_descriptors = NULL; pd->port_range_hints = NULL; pd->audio_input_port_indicies = NULL; pd->audio_output_port_indicies = NULL; pd->port_names = NULL; pd->control_port_indicies = NULL; pd->status_port_indicies = NULL; pd->audio_aux_port_indicies = NULL; pd->port_count = 0; } } static void vst2_plugin_desc_free(vst2_plugin_desc_t *pd) { vst2_plugin_desc_set_object_file(pd, NULL); vst2_plugin_desc_set_name(pd, NULL); vst2_plugin_desc_set_maker(pd, NULL); vst2_plugin_desc_free_ports(pd); } vst2_plugin_desc_t *vst2_plugin_desc_new() { vst2_plugin_desc_t *pd; pd = g_malloc(sizeof(vst2_plugin_desc_t)); vst2_plugin_desc_init(pd); return pd; } vst2_plugin_desc_t *vst2_plugin_desc_new_with_descriptor(const char *object_file, unsigned long index, AEffect *effect) { vst2_plugin_desc_t *pd; pd = vst2_plugin_desc_new(); vst2_plugin_desc_set_object_file(pd, object_file); vst2_plugin_desc_set_index(pd, index); vst2_plugin_desc_set_id(pd, effect->uniqueID); static char strBuf[1024]; effect->dispatcher (effect, effGetEffectName, 0, 0, strBuf, 0); vst2_plugin_desc_set_name(pd, strBuf); effect->dispatcher (effect, effGetVendorString, 0, 0, strBuf, 0); vst2_plugin_desc_set_maker(pd, strBuf); int PortCount = effect->numInputs + effect->numOutputs + effect->numParams; char **PortNames = calloc(PortCount, sizeof(char *)); LADSPA_PortDescriptor *port_descriptors = calloc(PortCount, sizeof(LADSPA_PortDescriptor)); LADSPA_PortRangeHint *PortRangeHints = calloc(PortCount, sizeof(LADSPA_PortRangeHint)); pd->def_values = calloc(PortCount, sizeof(LADSPA_Data)); int j; for (j = 0; j < effect->numInputs; ++j) { strBuf[0] = '\0'; sprintf (strBuf, "Input %d", j); PortNames[j] = strdup(strBuf); port_descriptors[j] |= LADSPA_PORT_AUDIO; port_descriptors[j] |= LADSPA_PORT_INPUT; PortRangeHints[j].LowerBound = 0.0f; PortRangeHints[j].UpperBound = 1.0f; } for (; j < effect->numInputs+effect->numOutputs; ++j) { strBuf[0] = '\0'; sprintf (strBuf, "Output %d", j); PortNames[j] = strdup(strBuf); port_descriptors[j] |= LADSPA_PORT_AUDIO; port_descriptors[j] |= LADSPA_PORT_OUTPUT; PortRangeHints[j].LowerBound = 0.0f; PortRangeHints[j].UpperBound = 1.0f; } int i; for (i = j; i < PortCount; ++i) { strBuf[0] = '\0'; effect->dispatcher(effect, effGetParamName, i-(effect->numInputs+effect->numOutputs), 0, strBuf, 0); PortNames[i] = strdup(strBuf); port_descriptors[i] |= LADSPA_PORT_CONTROL; PortRangeHints[i].LowerBound = 0.0f; PortRangeHints[i].UpperBound = 1.0f; } vst2_plugin_desc_set_ports(pd, PortCount, port_descriptors, PortRangeHints, (const char *const *) PortNames); pd->effect = effect; pd->rt = TRUE; return pd; } void vst2_plugin_desc_destroy(vst2_plugin_desc_t *pd) { vst2_plugin_desc_free(pd); g_free(pd); } void vst2_plugin_desc_set_object_file(vst2_plugin_desc_t *pd, const char *object_file) { set_string_property(pd->object_file, object_file); } void vst2_plugin_desc_set_index(vst2_plugin_desc_t *pd, unsigned long index) { pd->index = index; } void vst2_plugin_desc_set_id(vst2_plugin_desc_t *pd, unsigned long id) { pd->id = id; } void vst2_plugin_desc_set_name(vst2_plugin_desc_t *pd, const char *name) { set_string_property(pd->name, name); } void vst2_plugin_desc_set_maker(vst2_plugin_desc_t *pd, const char *maker) { set_string_property(pd->maker, maker); } void vst2_plugin_desc_set_properties(vst2_plugin_desc_t *pd, LADSPA_Properties properties) { pd->properties = properties; } static void vst2_plugin_desc_add_audio_port_index(unsigned long **indices, unsigned long *current_port_count, unsigned long index) { (*current_port_count)++; if (*current_port_count == 0) *indices = g_malloc(sizeof(unsigned long) * *current_port_count); else *indices = g_realloc(*indices, sizeof(unsigned long) * *current_port_count); (*indices)[*current_port_count - 1] = index; } static void vst2_plugin_desc_set_port_counts(vst2_plugin_desc_t *pd) { unsigned long i; unsigned long icount = 0; unsigned long ocount = 0; for (i = 0; i < pd->port_count; i++) { if (LADSPA_IS_PORT_AUDIO(pd->port_descriptors[i])) { if (LADSPA_IS_PORT_INPUT(pd->port_descriptors[i])) { vst2_plugin_desc_add_audio_port_index(&pd->audio_input_port_indicies, &icount, i); } else { vst2_plugin_desc_add_audio_port_index(&pd->audio_output_port_indicies, &ocount, i); } } else { if (LADSPA_IS_PORT_OUTPUT(pd->port_descriptors[i])) { pd->status_port_count++; if (pd->status_port_count == 0) pd->status_port_indicies = g_malloc(sizeof(unsigned long) * pd->status_port_count); else pd->status_port_indicies = g_realloc(pd->status_port_indicies, sizeof(unsigned long) * pd->status_port_count); pd->status_port_indicies[pd->status_port_count - 1] = i; } else { pd->control_port_count++; if (pd->control_port_count == 0) pd->control_port_indicies = g_malloc(sizeof(unsigned long) * pd->control_port_count); else pd->control_port_indicies = g_realloc(pd->control_port_indicies, sizeof(unsigned long) * pd->control_port_count); pd->control_port_indicies[pd->control_port_count - 1] = i; } } } if (icount == ocount) pd->channels = icount; else if (icount == 0) { pd->channels = ocount; pd->has_input = FALSE; } else { /* deal with auxiliary ports */ unsigned long **port_indicies; unsigned long port_count; unsigned long i, j; if (icount > ocount) { pd->channels = ocount; pd->aux_channels = icount - ocount; pd->aux_are_input = TRUE; port_indicies = &pd->audio_input_port_indicies; port_count = icount; } else { pd->channels = icount; pd->aux_channels = ocount - icount; pd->aux_are_input = FALSE; port_indicies = &pd->audio_output_port_indicies; port_count = ocount; } /* allocate indices */ pd->audio_aux_port_indicies = g_malloc(sizeof(unsigned long) * pd->aux_channels); /* copy indices */ for (i = pd->channels, j = 0; i < port_count; i++, j++) pd->audio_aux_port_indicies[j] = (*port_indicies)[i]; /* shrink the main indices to only have channels indices */ *port_indicies = g_realloc(*port_indicies, sizeof(unsigned long) * pd->channels); } } void vst2_plugin_desc_set_ports(vst2_plugin_desc_t *pd, unsigned long port_count, const LADSPA_PortDescriptor *port_descriptors, const LADSPA_PortRangeHint *port_range_hints, const char *const *port_names) { unsigned long i; vst2_plugin_desc_free_ports(pd); if (!port_count) return; pd->port_count = port_count; pd->port_descriptors = g_malloc(sizeof(LADSPA_PortDescriptor) * port_count); pd->port_range_hints = g_malloc(sizeof(LADSPA_PortRangeHint) * port_count); pd->port_names = g_malloc(sizeof(char *) * port_count); memcpy(pd->port_descriptors, port_descriptors, sizeof(LADSPA_PortDescriptor) * port_count); memcpy(pd->port_range_hints, port_range_hints, sizeof(LADSPA_PortRangeHint) * port_count); for (i = 0; i < port_count; i++) pd->port_names[i] = g_strdup(port_names[i]); vst2_plugin_desc_set_port_counts(pd); } LADSPA_Data vst2_plugin_desc_get_default_control_value(vst2_plugin_desc_t *pd, unsigned long port_index, guint32 sample_rate) { return pd->effect->getParameter(pd->effect, port_index); } LADSPA_Data vst2_plugin_desc_change_control_value(vst2_plugin_desc_t *pd, unsigned long control_index, LADSPA_Data value, guint32 old_sample_rate, guint32 new_sample_rate) { if (LADSPA_IS_HINT_SAMPLE_RATE(pd->port_range_hints[control_index].HintDescriptor)) { LADSPA_Data old_sr, new_sr; old_sr = (LADSPA_Data) old_sample_rate; new_sr = (LADSPA_Data) new_sample_rate; value /= old_sr; value *= new_sr; } return value; } gint vst2_plugin_desc_get_copies(vst2_plugin_desc_t *pd, unsigned long rack_channels) { gint copies = 1; if (pd->channels > rack_channels) return 0; while (pd->channels * copies < rack_channels) copies++; if (pd->channels * copies > rack_channels) return 0; return copies; } #endif /* EOF */ mlt-7.38.0/src/modules/jackrack/plugin_desc.h000664 000000 000000 00000017055 15172202314 021047 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JR_PLUGIN_DESC_H__ #define __JR_PLUGIN_DESC_H__ #include #include #ifdef WITH_LV2 #define LADSPA_PORT_ATOM 10 #define LADSPA_IS_PORT_ATOM(x) ((x) & LADSPA_PORT_ATOM) #define LADSPA_HINT_ENUMERATION LADSPA_HINT_DEFAULT_LOW #define LADSPA_IS_HINT_ENUMERATION(x) ((x) & LADSPA_HINT_ENUMERATION) #include /* lv2 extenstions */ #include #include #include #include #include #include #include #include #include #endif #ifdef WITH_VST2 #include "vestige.h" #endif typedef struct _plugin_desc plugin_desc_t; struct _plugin_desc { char *object_file; unsigned long index; unsigned long id; char *name; char *maker; LADSPA_Properties properties; gboolean rt; unsigned long channels; gboolean aux_are_input; unsigned long aux_channels; unsigned long port_count; LADSPA_PortDescriptor *port_descriptors; LADSPA_PortRangeHint *port_range_hints; char **port_names; unsigned long *audio_input_port_indicies; unsigned long *audio_output_port_indicies; unsigned long *audio_aux_port_indicies; unsigned long control_port_count; unsigned long *control_port_indicies; unsigned long status_port_count; unsigned long *status_port_indicies; gboolean has_input; }; plugin_desc_t *plugin_desc_new(); plugin_desc_t *plugin_desc_new_with_descriptor(const char *object_file, unsigned long index, const LADSPA_Descriptor *descriptor); void plugin_desc_destroy(plugin_desc_t *pd); void plugin_desc_set_object_file(plugin_desc_t *pd, const char *object_file); void plugin_desc_set_index(plugin_desc_t *pd, unsigned long index); void plugin_desc_set_id(plugin_desc_t *pd, unsigned long id); void plugin_desc_set_name(plugin_desc_t *pd, const char *name); void plugin_desc_set_maker(plugin_desc_t *pd, const char *maker); void plugin_desc_set_properties(plugin_desc_t *pd, LADSPA_Properties properties); struct _plugin *plugin_desc_instantiate(plugin_desc_t *pd); LADSPA_Data plugin_desc_get_default_control_value(plugin_desc_t *pd, unsigned long port_index, guint32 sample_rate); LADSPA_Data plugin_desc_change_control_value( plugin_desc_t *, unsigned long, LADSPA_Data, guint32, guint32); gint plugin_desc_get_copies(plugin_desc_t *pd, unsigned long rack_channels); #ifdef WITH_LV2 typedef struct _lv2_plugin_desc lv2_plugin_desc_t; struct _lv2_plugin_desc { char *uri; /* file name */ unsigned long index; unsigned long id; char *name; char *maker; LADSPA_Properties properties; gboolean rt; unsigned long channels; gboolean aux_are_input; unsigned long aux_channels; unsigned long port_count; LADSPA_PortDescriptor *port_descriptors; LADSPA_PortRangeHint *port_range_hints; char **port_names; unsigned long *audio_input_port_indicies; unsigned long *audio_output_port_indicies; unsigned long *audio_aux_port_indicies; unsigned long control_port_count; unsigned long *control_port_indicies; unsigned long status_port_count; unsigned long *status_port_indicies; float *def_values, *min_values, *max_values; gboolean has_input; }; lv2_plugin_desc_t *lv2_plugin_desc_new_with_descriptor(const char *uri, unsigned long index, const LilvPlugin *plugin); void lv2_plugin_desc_destroy(lv2_plugin_desc_t *pd); lv2_plugin_desc_t *lv2_plugin_desc_new(); void lv2_plugin_desc_set_uri(lv2_plugin_desc_t *pd, const char *uri); void lv2_plugin_desc_set_index(lv2_plugin_desc_t *pd, unsigned long index); void lv2_plugin_desc_set_id(lv2_plugin_desc_t *pd, unsigned long id); void lv2_plugin_desc_set_name(lv2_plugin_desc_t *pd, const char *name); void lv2_plugin_desc_set_maker(lv2_plugin_desc_t *pd, const char *maker); void lv2_plugin_desc_set_properties(lv2_plugin_desc_t *pd, LADSPA_Properties properties); struct _lv2_plugin *lv2_plugin_desc_instantiate(lv2_plugin_desc_t *pd); LADSPA_Data lv2_plugin_desc_change_control_value( lv2_plugin_desc_t *, unsigned long, LADSPA_Data, guint32, guint32); gint lv2_plugin_desc_get_copies(lv2_plugin_desc_t *pd, unsigned long rack_channels); #endif #ifdef WITH_VST2 typedef struct _vst2_plugin_desc vst2_plugin_desc_t; struct _vst2_plugin_desc { char *object_file; unsigned long index; unsigned long id; char *name; char *maker; LADSPA_Properties properties; AEffect *effect; gboolean rt; unsigned long channels; gboolean aux_are_input; unsigned long aux_channels; unsigned long port_count; LADSPA_PortDescriptor *port_descriptors; LADSPA_PortRangeHint *port_range_hints; char **port_names; unsigned long *audio_input_port_indicies; unsigned long *audio_output_port_indicies; unsigned long *audio_aux_port_indicies; unsigned long control_port_count; unsigned long *control_port_indicies; unsigned long status_port_count; unsigned long *status_port_indicies; float *def_values; gboolean has_input; }; vst2_plugin_desc_t *vst2_plugin_desc_new(); vst2_plugin_desc_t *vst2_plugin_desc_new_with_descriptor(const char *object_file, unsigned long index, AEffect *descriptor); void vst2_plugin_desc_destroy(vst2_plugin_desc_t *pd); void vst2_plugin_desc_set_object_file(vst2_plugin_desc_t *pd, const char *object_file); void vst2_plugin_desc_set_index(vst2_plugin_desc_t *pd, unsigned long index); void vst2_plugin_desc_set_id(vst2_plugin_desc_t *pd, unsigned long id); void vst2_plugin_desc_set_name(vst2_plugin_desc_t *pd, const char *name); void vst2_plugin_desc_set_maker(vst2_plugin_desc_t *pd, const char *maker); void vst2_plugin_desc_set_properties(vst2_plugin_desc_t *pd, LADSPA_Properties properties); struct _vst2 *vst2_plugin_desc_instantiate(vst2_plugin_desc_t *pd); LADSPA_Data vst2_plugin_desc_get_default_control_value(vst2_plugin_desc_t *pd, unsigned long port_index, guint32 sample_rate); LADSPA_Data vst2_plugin_desc_change_control_value( vst2_plugin_desc_t *, unsigned long, LADSPA_Data, guint32, guint32); gint vst2_plugin_desc_get_copies(vst2_plugin_desc_t *pd, unsigned long rack_channels); #endif #endif /* __JR_PLUGIN_DESC_H__ */ mlt-7.38.0/src/modules/jackrack/plugin_mgr.c000664 000000 000000 00000076543 15172202314 020720 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modifications for MLT: * Copyright (C) 2004-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "framework/mlt_factory.h" #include "framework/mlt_log.h" #include "plugin_desc.h" #include "plugin_mgr.h" #ifdef WITH_LV2 #include /* lv2 extenstions */ #include "lv2/buf-size/buf-size.h" #include "lv2/parameters/parameters.h" #include #include #include #include #include #include #include #include #include #include #include "lv2_urid_helper.h" LilvNode *lv2_input_class; LilvNode *lv2_output_class; LilvNode *lv2_audio_class; LilvNode *lv2_control_class; LilvNode *lv2_atom_class; LilvNode *lv2_integer_property; LilvNode *lv2_logarithmic_property; LilvNode *lv2_toggled_property; LilvNode *lv2_enumeration_property; static LV2_URID urid_atom_Float; static LV2_URID urid_atom_Int; static LV2_URID urid_bufsz_minBlockLength; static LV2_URID urid_bufsz_maxBlockLength; static LV2_URID urid_param_sampleRate; static LV2_URID urid_bufsz_sequenceSize; static LV2_URID urid_ui_updateRate; static LV2_URID urid_ui_scaleFactor; static URITable uri_table; static float lv2opt_sample_rate = 48000.000000; static uint32_t lv2opt_block_length = 4096; static size_t lv2opt_midi_buf_size = 32768; static float lv2opt_ui_update_hz = 60.000000; static float lv2opt_ui_scale_factor = 1.000000; static LV2_URID_Map map = {&uri_table, uri_table_map}; static LV2_Feature map_feature = {LV2_URID_MAP_URI, &map}; static LV2_URID_Unmap unmap = {&uri_table, uri_table_unmap}; static LV2_Feature unmap_feature = {LV2_URID_UNMAP_URI, &unmap}; static LV2_Feature boundedBlockLength_feature = {LV2_BUF_SIZE__boundedBlockLength, NULL}; static LV2_Feature fixedBlockLength_feature = {LV2_BUF_SIZE__fixedBlockLength, NULL}; static LV2_Feature powerOf2BlockLength_feature = {LV2_BUF_SIZE__powerOf2BlockLength, NULL}; static LV2_Feature loadDefaultState_feature = {LV2_STATE__loadDefaultState, NULL}; static LV2_Options_Option lv2_options_features[7]; static LV2_Feature options_feature = {LV2_OPTIONS__options, (void *) lv2_options_features}; const LV2_Feature *features[] = { &map_feature, &unmap_feature, &options_feature, &boundedBlockLength_feature, &fixedBlockLength_feature, &powerOf2BlockLength_feature, &loadDefaultState_feature, NULL }; #endif #ifdef WITH_VST2 #include "vestige.h" #define audioMasterGetOutputSpeakerArrangement audioMasterGetSpeakerArrangement #define effFlagsProgramChunks (1 << 5) #define effSetProgramName 4 #define effGetParamLabel 6 #define effGetParamDisplay 7 #define effGetVu 9 #define effEditDraw 16 #define effEditMouse 17 #define effEditKey 18 #define effEditSleep 21 #define effIdentify 22 #define effGetChunk 23 #define effSetChunk 24 #define effCanBeAutomated 26 #define effString2Parameter 27 #define effGetNumProgramCategories 28 #define effGetProgramNameIndexed 29 #define effCopyProgram 30 #define effConnectInput 31 #define effConnectOutput 32 #define effGetInputProperties 33 #define effGetOutputProperties 34 #define effGetCurrentPosition 36 #define effGetDestinationBuffer 37 #define effOfflineNotify 38 #define effOfflinePrepare 39 #define effOfflineRun 40 #define effProcessVarIo 41 #define effSetSpeakerArrangement 42 #define effSetBlockSizeAndSampleRate 43 #define effSetBypass 44 #define effGetErrorText 46 #define effVendorSpecific 50 #define effGetTailSize 52 #define effGetIcon 54 #define effSetViewPosition 55 #define effKeysRequired 57 #define effEditKeyDown 59 #define effEditKeyUp 60 #define effSetEditKnobMode 61 #define effGetMidiProgramName 62 #define effGetCurrentMidiProgram 63 #define effGetMidiProgramCategory 64 #define effHasMidiProgramsChanged 65 #define effGetMidiKeyName 66 #define effGetSpeakerArrangement 69 #define effSetTotalSampleToProcess 73 #define effSetPanLaw 74 #define effBeginLoadBank 75 #define effBeginLoadProgram 76 #define effSetProcessPrecision 77 #define effGetNumMidiInputChannels 78 #define effGetNumMidiOutputChannels 79 #define kVstAutomationOff 1 #define kVstAutomationReadWrite 4 #define kVstProcessLevelUnknown 0 #define kVstProcessLevelUser 1 #define kVstProcessLevelRealtime 2 #define kVstProcessLevelOffline 4 #define kVstProcessPrecision32 0 #define kVstVersion 2400 #if defined(CARLA_OS_WIN) && defined(__cdecl) # define VSTCALLBACK __cdecl #else # define VSTCALLBACK #endif typedef AEffect* (*VST_Function)(audioMasterCallback); #define kVstVersion 2400 #endif static gboolean plugin_is_valid(const LADSPA_Descriptor *descriptor) { unsigned long i; unsigned long icount = 0; unsigned long ocount = 0; for (i = 0; i < descriptor->PortCount; i++) { if (!LADSPA_IS_PORT_AUDIO(descriptor->PortDescriptors[i])) continue; if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) icount++; else ocount++; } if (ocount == 0) return FALSE; return TRUE; } static void plugin_mgr_get_object_file_plugins(plugin_mgr_t *plugin_mgr, const char *filename) { const char *dlerr; void *dl_handle; LADSPA_Descriptor_Function get_descriptor; const LADSPA_Descriptor *descriptor; unsigned long plugin_index; plugin_desc_t *desc, *other_desc = NULL; GSList *list; gboolean exists; int err; /* open the object file */ dl_handle = dlopen(filename, RTLD_LAZY); if (!dl_handle) { mlt_log_info(NULL, "%s: error opening shared object file '%s': %s\n", __FUNCTION__, filename, dlerror()); return; } /* get the get_descriptor function */ dlerror(); /* clear the error report */ get_descriptor = (LADSPA_Descriptor_Function) dlsym(dl_handle, "ladspa_descriptor"); dlerr = dlerror(); if (dlerr) { mlt_log_info(NULL, "%s: error finding ladspa_descriptor symbol in object file '%s': %s\n", __FUNCTION__, filename, dlerr); dlclose(dl_handle); return; } #ifdef __APPLE__ if (!get_descriptor(0)) { void (*constructor)(void) = dlsym(dl_handle, "_init"); if (constructor) constructor(); } #endif plugin_index = 0; while ((descriptor = get_descriptor(plugin_index))) { if (!plugin_is_valid(descriptor)) { plugin_index++; continue; } /* check it doesn't already exist */ exists = FALSE; for (list = plugin_mgr->all_plugins; list; list = g_slist_next(list)) { other_desc = (plugin_desc_t *) list->data; if (other_desc->id == descriptor->UniqueID) { exists = TRUE; break; } } if (exists) { mlt_log_info(NULL, "Plugin %ld exists in both '%s' and '%s'; using version in '%s'\n", descriptor->UniqueID, other_desc->object_file, filename, other_desc->object_file); plugin_index++; continue; } desc = plugin_desc_new_with_descriptor(filename, plugin_index, descriptor); plugin_mgr->all_plugins = g_slist_append(plugin_mgr->all_plugins, desc); plugin_index++; plugin_mgr->plugin_count++; /* print in the splash screen */ /* mlt_log_verbose( NULL, "Loaded plugin '%s'\n", desc->name); */ } err = dlclose(dl_handle); if (err) { mlt_log_warning(NULL, "%s: error closing object file '%s': %s\n", __FUNCTION__, filename, dlerror()); } } static void plugin_mgr_get_dir_plugins(plugin_mgr_t *plugin_mgr, const char *dir) { DIR *dir_stream; struct dirent *dir_entry; char *file_name; int err; size_t dirlen; dir_stream = opendir(dir); if (!dir_stream) { /* mlt_log_warning( NULL, "%s: error opening directory '%s': %s\n", __FUNCTION__, dir, strerror (errno)); */ return; } dirlen = strlen(dir); while ((dir_entry = readdir(dir_stream))) { struct stat info; if (strcmp(dir_entry->d_name, ".") == 0 || mlt_properties_get(plugin_mgr->blacklist, dir_entry->d_name) || strcmp(dir_entry->d_name, "..") == 0) continue; file_name = g_malloc(dirlen + 1 + strlen(dir_entry->d_name) + 1); strcpy(file_name, dir); if (file_name[dirlen - 1] == '/') strcpy(file_name + dirlen, dir_entry->d_name); else { file_name[dirlen] = '/'; strcpy(file_name + dirlen + 1, dir_entry->d_name); } stat(file_name, &info); if (S_ISDIR(info.st_mode)) plugin_mgr_get_dir_plugins(plugin_mgr, file_name); else { char *ext = strrchr(file_name, '.'); if (ext && (strcmp(ext, ".so") == 0 || strcmp(ext, ".dll") == 0 || strcmp(ext, ".dylib") == 0)) { plugin_mgr_get_object_file_plugins(plugin_mgr, file_name); } } g_free(file_name); } err = closedir(dir_stream); if (err) mlt_log_warning(NULL, "%s: error closing directory '%s': %s\n", __FUNCTION__, dir, strerror(errno)); } static void plugin_mgr_get_path_plugins(plugin_mgr_t *plugin_mgr) { char *ladspa_path, *dir; ladspa_path = g_strdup(getenv("LADSPA_PATH")); #ifdef _WIN32 if (!ladspa_path) { ladspa_path = malloc(strlen(mlt_environment("MLT_APPDIR")) + strlen("\\lib\\ladspa") + 1); strcpy(ladspa_path, mlt_environment("MLT_APPDIR")); strcat(ladspa_path, "\\lib\\ladspa"); } #elif defined(RELOCATABLE) #ifdef __APPLE__ #define LADSPA_SUBDIR "/PlugIns/ladspa" #else #define LADSPA_SUBDIR "/lib/ladspa" #endif { ladspa_path = malloc(strlen(mlt_environment("MLT_APPDIR")) + strlen(LADSPA_SUBDIR) + 1); strcpy(ladspa_path, mlt_environment("MLT_APPDIR")); strcat(ladspa_path, LADSPA_SUBDIR); } #else if (!ladspa_path) ladspa_path = g_strdup("/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/lib64/ladspa"); #endif for (dir = strtok(ladspa_path, MLT_DIRLIST_DELIMITER); dir; dir = strtok(NULL, MLT_DIRLIST_DELIMITER)) plugin_mgr_get_dir_plugins(plugin_mgr, dir); g_free(ladspa_path); } static gint plugin_mgr_sort(gconstpointer a, gconstpointer b) { const plugin_desc_t *da; const plugin_desc_t *db; da = (const plugin_desc_t *) a; db = (const plugin_desc_t *) b; return strcasecmp(da->name, db->name); } plugin_mgr_t *plugin_mgr_new() { plugin_mgr_t *pm; char dirname[PATH_MAX]; pm = g_malloc(sizeof(plugin_mgr_t)); pm->all_plugins = NULL; pm->plugins = NULL; pm->plugin_count = 0; snprintf(dirname, PATH_MAX, "%s/jackrack/blacklist.txt", mlt_environment("MLT_DATA")); pm->blacklist = mlt_properties_load(dirname); plugin_mgr_get_path_plugins(pm); if (!pm->all_plugins) mlt_log_info( NULL, "No LADSPA plugins were found! Check your LADSPA_PATH environment variable.\n"); else pm->all_plugins = g_slist_sort(pm->all_plugins, plugin_mgr_sort); return pm; } void plugin_mgr_destroy(plugin_mgr_t *plugin_mgr) { GSList *list; for (list = plugin_mgr->all_plugins; list; list = g_slist_next(list)) plugin_desc_destroy((plugin_desc_t *) list->data); g_slist_free(plugin_mgr->plugins); g_slist_free(plugin_mgr->all_plugins); mlt_properties_close(plugin_mgr->blacklist); free(plugin_mgr); } void plugin_mgr_set_plugins(plugin_mgr_t *plugin_mgr, unsigned long rack_channels) { GSList *list; plugin_desc_t *desc; /* clear the current plugins */ g_slist_free(plugin_mgr->plugins); plugin_mgr->plugins = NULL; for (list = plugin_mgr->all_plugins; list; list = g_slist_next(list)) { desc = (plugin_desc_t *) list->data; if (plugin_desc_get_copies(desc, rack_channels) != 0) plugin_mgr->plugins = g_slist_append(plugin_mgr->plugins, desc); } } static plugin_desc_t *plugin_mgr_find_desc(plugin_mgr_t *plugin_mgr, GSList *plugins, unsigned long id) { GSList *list; plugin_desc_t *desc; for (list = plugins; list; list = g_slist_next(list)) { desc = (plugin_desc_t *) list->data; if (desc->id == id) return desc; } return NULL; } plugin_desc_t *plugin_mgr_get_desc(plugin_mgr_t *plugin_mgr, unsigned long id) { return plugin_mgr_find_desc(plugin_mgr, plugin_mgr->plugins, id); } plugin_desc_t *plugin_mgr_get_any_desc(plugin_mgr_t *plugin_mgr, unsigned long id) { return plugin_mgr_find_desc(plugin_mgr, plugin_mgr->all_plugins, id); } #ifdef WITH_LV2 static gint lv2_mgr_sort(gconstpointer a, gconstpointer b) { const lv2_plugin_desc_t *da; const lv2_plugin_desc_t *db; da = (const lv2_plugin_desc_t *) a; db = (const lv2_plugin_desc_t *) b; return strcasecmp(da->name, db->name); } static void lv2_mgr_get_uri_plugins(lv2_mgr_t *plugin_mgr, const LilvPlugin *plugin) { unsigned long plugin_index = 0; lv2_plugin_desc_t *desc; desc = lv2_plugin_desc_new_with_descriptor(lilv_node_as_uri(lilv_plugin_get_uri(plugin)), plugin_index, plugin); plugin_mgr->all_plugins = g_slist_append(plugin_mgr->all_plugins, desc); plugin_index++; plugin_mgr->plugin_count++; } static void lv2_mgr_get_dir_plugins(lv2_mgr_t *plugin_mgr) { LILV_FOREACH(plugins, i, plugin_mgr->plugin_list) { const LilvPlugin *p = lilv_plugins_get(plugin_mgr->plugin_list, i); lv2_mgr_get_uri_plugins(plugin_mgr, p); } } static void lv2_mgr_get_path_plugins(lv2_mgr_t *plugin_mgr) { lv2_mgr_get_dir_plugins(plugin_mgr); } lv2_mgr_t *lv2_mgr_new() { lv2_mgr_t *pm; char dirname[PATH_MAX]; pm = g_malloc(sizeof(lv2_mgr_t)); pm->all_plugins = NULL; pm->plugins = NULL; pm->plugin_count = 0; pm->lv2_world = lilv_world_new(); lilv_world_load_all(pm->lv2_world); pm->plugin_list = (LilvPlugins *) lilv_world_get_all_plugins(pm->lv2_world); lv2_input_class = lilv_new_uri(pm->lv2_world, LILV_URI_INPUT_PORT); lv2_output_class = lilv_new_uri(pm->lv2_world, LILV_URI_OUTPUT_PORT); lv2_audio_class = lilv_new_uri(pm->lv2_world, LILV_URI_AUDIO_PORT); lv2_control_class = lilv_new_uri(pm->lv2_world, LILV_URI_CONTROL_PORT); lv2_atom_class = lilv_new_uri(pm->lv2_world, LV2_ATOM__AtomPort); lv2_integer_property = lilv_new_uri(pm->lv2_world, LV2_CORE__integer); lv2_logarithmic_property = lilv_new_uri(pm->lv2_world, LV2_PORT_PROPS__logarithmic); lv2_toggled_property = lilv_new_uri(pm->lv2_world, LV2_CORE__toggled); lv2_enumeration_property = lilv_new_uri(pm->lv2_world, LV2_CORE__enumeration); uri_table_init(&uri_table); urid_atom_Float = uri_table_map(&uri_table, LV2_ATOM__Float); urid_atom_Int = uri_table_map(&uri_table, LV2_ATOM__Int); urid_bufsz_minBlockLength = uri_table_map(&uri_table, LV2_BUF_SIZE__minBlockLength); urid_bufsz_maxBlockLength = uri_table_map(&uri_table, LV2_BUF_SIZE__maxBlockLength); urid_bufsz_sequenceSize = uri_table_map(&uri_table, LV2_BUF_SIZE__sequenceSize); urid_ui_updateRate = uri_table_map(&uri_table, LV2_UI__updateRate); urid_ui_scaleFactor = uri_table_map(&uri_table, LV2_UI__scaleFactor); urid_param_sampleRate = uri_table_map(&uri_table, LV2_PARAMETERS__sampleRate); lv2_options_features[0].context = LV2_OPTIONS_INSTANCE; lv2_options_features[0].subject = 0; lv2_options_features[0].key = urid_param_sampleRate; lv2_options_features[0].size = sizeof(float); lv2_options_features[0].type = urid_atom_Float; lv2_options_features[0].value = &lv2opt_sample_rate; lv2_options_features[1].context = LV2_OPTIONS_INSTANCE; lv2_options_features[1].subject = 0; lv2_options_features[1].key = urid_bufsz_minBlockLength; lv2_options_features[1].size = sizeof(int32_t); lv2_options_features[1].type = urid_atom_Int; lv2_options_features[1].value = &lv2opt_block_length; lv2_options_features[2].context = LV2_OPTIONS_INSTANCE; lv2_options_features[2].subject = 0; lv2_options_features[2].key = urid_bufsz_maxBlockLength; lv2_options_features[2].size = sizeof(int32_t); lv2_options_features[2].type = urid_atom_Int; lv2_options_features[2].value = &lv2opt_block_length; lv2_options_features[3].context = LV2_OPTIONS_INSTANCE; lv2_options_features[3].subject = 0; lv2_options_features[3].key = urid_bufsz_sequenceSize; lv2_options_features[3].size = sizeof(int32_t); lv2_options_features[3].type = urid_atom_Int; lv2_options_features[3].value = &lv2opt_midi_buf_size; lv2_options_features[4].context = LV2_OPTIONS_INSTANCE; lv2_options_features[4].subject = 0; lv2_options_features[4].key = urid_ui_updateRate; lv2_options_features[4].size = sizeof(float); lv2_options_features[4].type = urid_atom_Float; lv2_options_features[4].value = &lv2opt_ui_update_hz; lv2_options_features[5].context = LV2_OPTIONS_INSTANCE; lv2_options_features[5].subject = 0; lv2_options_features[5].key = urid_ui_scaleFactor; lv2_options_features[5].size = sizeof(float); lv2_options_features[5].type = urid_atom_Float; lv2_options_features[5].value = &lv2opt_ui_scale_factor; lv2_options_features[6].context = LV2_OPTIONS_INSTANCE; lv2_options_features[6].subject = 0; lv2_options_features[6].key = 0; lv2_options_features[6].size = 0; lv2_options_features[6].type = 0; lv2_options_features[6].value = NULL; snprintf(dirname, PATH_MAX, "%s/jackrack/lv2blacklist.txt", mlt_environment("MLT_DATA")); pm->blacklist = mlt_properties_load(dirname); lv2_mgr_get_path_plugins(pm); if (!pm->all_plugins) mlt_log_info( NULL, "No LV2 plugins were found! Check your LV2_PATH environment variable.\n"); else pm->all_plugins = g_slist_sort(pm->all_plugins, lv2_mgr_sort); return pm; } void lv2_mgr_destroy(lv2_mgr_t *plugin_mgr) { GSList *list; for (list = plugin_mgr->all_plugins; list; list = g_slist_next(list)) lv2_plugin_desc_destroy((lv2_plugin_desc_t *) list->data); g_slist_free(plugin_mgr->plugins); g_slist_free(plugin_mgr->all_plugins); mlt_properties_close(plugin_mgr->blacklist); lilv_node_free(lv2_input_class); lilv_node_free(lv2_output_class); lilv_node_free(lv2_audio_class); lilv_node_free(lv2_control_class); lilv_node_free(lv2_atom_class); lilv_node_free(lv2_integer_property); lilv_node_free(lv2_logarithmic_property); lilv_node_free(lv2_toggled_property); lilv_node_free(lv2_enumeration_property); lilv_world_free(plugin_mgr->lv2_world); free(plugin_mgr); } void lv2_mgr_set_plugins(lv2_mgr_t *plugin_mgr, unsigned long rack_channels) { GSList *list; lv2_plugin_desc_t *desc; /* clear the current plugins */ g_slist_free(plugin_mgr->plugins); plugin_mgr->plugins = NULL; for (list = plugin_mgr->all_plugins; list; list = g_slist_next(list)) { desc = (lv2_plugin_desc_t *) list->data; if (desc->channels > 0 && lv2_plugin_desc_get_copies(desc, rack_channels) != 0) plugin_mgr->plugins = g_slist_append(plugin_mgr->plugins, desc); } } static lv2_plugin_desc_t *lv2_mgr_find_desc(lv2_mgr_t *plugin_mgr, GSList *plugins, char *id) { GSList *list; lv2_plugin_desc_t *desc; for (list = plugins; list; list = g_slist_next(list)) { desc = (lv2_plugin_desc_t *) list->data; if (!strcmp(desc->uri, id)) return desc; } return NULL; } lv2_plugin_desc_t *lv2_mgr_get_desc(lv2_mgr_t *plugin_mgr, char *id) { return lv2_mgr_find_desc(plugin_mgr, plugin_mgr->plugins, id); } lv2_plugin_desc_t *lv2_mgr_get_any_desc(lv2_mgr_t *plugin_mgr, char *id) { return lv2_mgr_find_desc(plugin_mgr, plugin_mgr->all_plugins, id); } #endif #ifdef WITH_VST2 static gboolean vst2_is_valid(const AEffect *effect) { /* unsigned long icount = 0; */ unsigned long ocount = 0; /* icount = effect->numInputs; */ ocount = effect->numOutputs; if (effect->magic == kEffectMagic) return FALSE; if (ocount == 0) return FALSE; return TRUE; } static intptr_t mlt_vst_hostCanDo(const char* const feature) { mlt_log_info(NULL, "mlt_vst_hostCanDo(\"%s\")", feature); if (strcmp(feature, "supplyIdle") == 0) return 1; if (strcmp(feature, "sendVstEvents") == 0) return 1; if (strcmp(feature, "sendVstMidiEvent") == 0) return 1; if (strcmp(feature, "sendVstMidiEventFlagIsRealtime") == 0) return 1; if (strcmp(feature, "sendVstTimeInfo") == 0) return 1; if (strcmp(feature, "receiveVstEvents") == 0) return 1; if (strcmp(feature, "receiveVstMidiEvent") == 0) return 1; if (strcmp(feature, "receiveVstTimeInfo") == 0) return -1; if (strcmp(feature, "reportConnectionChanges") == 0) return -1; if (strcmp(feature, "acceptIOChanges") == 0) return 1; if (strcmp(feature, "sizeWindow") == 0) return 1; if (strcmp(feature, "offline") == 0) return -1; if (strcmp(feature, "openFileSelector") == 0) return -1; if (strcmp(feature, "closeFileSelector") == 0) return -1; if (strcmp(feature, "startStopProcess") == 0) return 1; if (strcmp(feature, "supportShell") == 0) return 1; if (strcmp(feature, "shellCategory") == 0) return 1; if (strcmp(feature, "NIMKPIVendorSpecificCallbacks") == 0) return -1; // unimplemented mlt_log_error(NULL, "mlt_vst_hostCanDo(\"%s\") - unknown feature", feature); return 0; } static intptr_t VSTCALLBACK mlt_vst_audioMasterCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) { switch (opcode) { case audioMasterAutomate: return 1; case audioMasterVersion: return kVstVersion; case audioMasterCurrentId: return 0; case audioMasterGetVendorString: strcpy((char*)ptr, "MRF"); return 1; case audioMasterGetProductString: strcpy((char*)ptr, "No Organization"); return 1; case audioMasterGetVendorVersion: return 0x01; case audioMasterCanDo: return mlt_vst_hostCanDo((const char*)ptr); case audioMasterGetLanguage: return kVstLangEnglish; } return 0; } static void vst2_mgr_get_object_file_plugins(vst2_mgr_t *vst2_mgr, const char *filename) { const char *dlerr; void *dl_handle; VST_Function vstFn = NULL; AEffect *effect = NULL; unsigned long vst2_index; vst2_plugin_desc_t *desc, *other_desc = NULL; GSList *list; gboolean exists; /* int err; */ /* open the object file */ dl_handle = dlopen(filename, RTLD_LAZY); if (!dl_handle) { mlt_log_info(NULL, "%s: error opening shared object file '%s': %s\n", __FUNCTION__, filename, dlerror()); return; } /* get the get_descriptor function */ dlerror(); /* clear the error report */ vstFn = (VST_Function) dlsym (dl_handle, "VSTPluginMain"); if (vstFn == NULL) vstFn = (VST_Function) dlsym (dl_handle, "main_macho"); if (vstFn == NULL) vstFn = (VST_Function) dlsym (dl_handle, "main"); if (vstFn == NULL) return; effect = vstFn(mlt_vst_audioMasterCallback); dlerr = dlerror(); if (dlerr) { mlt_log_info(NULL, "%s: error finding {VSTPluginMain, main_macho, main} symbol in object file '%s': %s\n", __FUNCTION__, filename, dlerr); dlclose(dl_handle); return; } vst2_index = 0; /* while ((descriptor = get_descriptor(vst2_index))) */ if (effect != NULL) { if (!vst2_is_valid(effect)) { vst2_index++; //continue; } /* check it doesn't already exist */ exists = FALSE; for (list = vst2_mgr->all_plugins; list; list = g_slist_next(list)) { other_desc = (vst2_plugin_desc_t *) list->data; if (other_desc->id == effect->uniqueID) { exists = TRUE; break; } } if (exists) { mlt_log_info(NULL, "Plugin %d exists in both '%s' and '%s'; using version in '%s'\n", effect->uniqueID, other_desc->object_file, filename, other_desc->object_file); vst2_index++; //continue; } desc = vst2_plugin_desc_new_with_descriptor(filename, vst2_index, effect); vst2_mgr->all_plugins = g_slist_append(vst2_mgr->all_plugins, desc); vst2_index++; vst2_mgr->plugin_count++; /* print in the splash screen */ /* mlt_log_verbose( NULL, "Loaded plugin '%s'\n", desc->name); */ } /* WIP temporarily disabled */ /* err = dlclose(dl_handle); */ /* if (err) { mlt_log_warning(NULL, "%s: error closing object file '%s': %s\n", __FUNCTION__, filename, dlerror()); } */ } static gint vst2_mgr_sort(gconstpointer a, gconstpointer b) { const vst2_plugin_desc_t *da; const vst2_plugin_desc_t *db; da = (const vst2_plugin_desc_t *) a; db = (const vst2_plugin_desc_t *) b; return strcasecmp(da->name, db->name); } static void vst2_mgr_get_dir_plugins(vst2_mgr_t *vst2_mgr, const char *dir) { DIR *dir_stream; struct dirent *dir_entry; char *file_name; int err; size_t dirlen; dir_stream = opendir(dir); if (!dir_stream) { /* mlt_log_warning( NULL, "%s: error opening directory '%s': %s\n", __FUNCTION__, dir, strerror (errno)); */ return; } dirlen = strlen(dir); while ((dir_entry = readdir(dir_stream))) { struct stat info; if (strcmp(dir_entry->d_name, ".") == 0 || mlt_properties_get(vst2_mgr->blacklist, dir_entry->d_name) || strcmp(dir_entry->d_name, "..") == 0) continue; file_name = g_malloc(dirlen + 1 + strlen(dir_entry->d_name) + 1); strcpy(file_name, dir); if (file_name[dirlen - 1] == '/') strcpy(file_name + dirlen, dir_entry->d_name); else { file_name[dirlen] = '/'; strcpy(file_name + dirlen + 1, dir_entry->d_name); } stat(file_name, &info); if (S_ISDIR(info.st_mode)) { vst2_mgr_get_dir_plugins(vst2_mgr, file_name); } else { char *ext = strrchr(file_name, '.'); if (ext && (strcmp(ext, ".so") == 0 || strcasecmp(ext, ".dll") == 0 || strcmp(ext, ".dylib") == 0 || strcasecmp(ext, ".vst") == 0)) { vst2_mgr_get_object_file_plugins(vst2_mgr, file_name); } } g_free(file_name); } err = closedir(dir_stream); if (err) mlt_log_warning(NULL, "%s: error closing directory '%s': %s\n", __FUNCTION__, dir, strerror(errno)); } static void vst2_mgr_get_path_plugins(vst2_mgr_t *vst2_mgr) { char *vst_path, *dir; vst_path = g_strdup(getenv("VST_PATH")); if (!vst_path) { #ifdef _WIN32 vst_path = g_strdup("C:/Program Files/Common Files/VST2;C:/Program Files/Steinberg/VSTPlugins;C:/Program Files/VSTPlugins;C:/Program Files (x86)/Common Files/VST2;C:/Program Files (x86)/Steinberg/VSTPlugins;C:/Program Files (x86)/VSTPlugins"); #elif __APPLE__ vst_path = g_strdup("/Library/Audio/Plug-Ins/VST:~/Library/Audio/Plug-Ins/VST"); #else vst_path = g_strdup("/usr/local/lib/vst:/usr/lib/vst:/usr/lib64/vst"); #endif } for (dir = strtok(vst_path, MLT_DIRLIST_DELIMITER); dir; dir = strtok(NULL, MLT_DIRLIST_DELIMITER)) { vst2_mgr_get_dir_plugins(vst2_mgr, dir); } g_free(vst_path); } vst2_mgr_t *vst2_mgr_new() { vst2_mgr_t *pm; char dirname[PATH_MAX]; pm = g_malloc(sizeof(vst2_mgr_t)); pm->all_plugins = NULL; pm->plugins = NULL; pm->plugin_count = 0; snprintf(dirname, PATH_MAX, "%s/jackrack/blacklist.txt", mlt_environment("MLT_DATA")); pm->blacklist = mlt_properties_load(dirname); vst2_mgr_get_path_plugins(pm); if (!pm->all_plugins) mlt_log_info( NULL, "No VST2 plugins were found! Check your VST_PATH environment variable.\n"); else pm->all_plugins = g_slist_sort(pm->all_plugins, vst2_mgr_sort); return pm; } void vst2_mgr_destroy(vst2_mgr_t *vst2_mgr) { GSList *list; for (list = vst2_mgr->all_plugins; list; list = g_slist_next(list)) vst2_plugin_desc_destroy((vst2_plugin_desc_t *) list->data); g_slist_free(vst2_mgr->plugins); g_slist_free(vst2_mgr->all_plugins); mlt_properties_close(vst2_mgr->blacklist); free(vst2_mgr); } void vst2_mgr_set_plugins(vst2_mgr_t *vst2_mgr, unsigned long rack_channels) { GSList *list; vst2_plugin_desc_t *desc; /* clear the current plugins */ g_slist_free(vst2_mgr->plugins); vst2_mgr->plugins = NULL; for (list = vst2_mgr->all_plugins; list; list = g_slist_next(list)) { desc = (vst2_plugin_desc_t *) list->data; if (vst2_plugin_desc_get_copies(desc, rack_channels) != 0) vst2_mgr->plugins = g_slist_append(vst2_mgr->plugins, desc); } } static vst2_plugin_desc_t *vst2_mgr_find_desc(vst2_mgr_t *vst2_mgr, GSList *plugins, unsigned long id) { GSList *list; vst2_plugin_desc_t *desc; for (list = plugins; list; list = g_slist_next(list)) { desc = (vst2_plugin_desc_t *) list->data; if (desc->id == id) return desc; } return NULL; } vst2_plugin_desc_t *vst2_mgr_get_desc(vst2_mgr_t *vst2_mgr, unsigned long id) { return vst2_mgr_find_desc(vst2_mgr, vst2_mgr->plugins, id); } vst2_plugin_desc_t *vst2_mgr_get_any_desc(vst2_mgr_t *vst2_mgr, unsigned long id) { return vst2_mgr_find_desc(vst2_mgr, vst2_mgr->all_plugins, id); } #endif /* EOF */ mlt-7.38.0/src/modules/jackrack/plugin_mgr.h000664 000000 000000 00000005337 15172202314 020716 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JR_PLUGIN_MANAGER_H__ #define __JR_PLUGIN_MANAGER_H__ #include #include "framework/mlt_properties.h" #include "plugin_desc.h" #ifdef WITH_LV2 #include #endif typedef struct _plugin_mgr plugin_mgr_t; struct _plugin_mgr { GSList *all_plugins; GSList *plugins; unsigned long plugin_count; mlt_properties blacklist; }; #ifdef WITH_LV2 typedef struct _lv2_mgr lv2_mgr_t; struct _lv2_mgr { LilvWorld *lv2_world; LilvPlugins *plugin_list; GSList *all_plugins; /* this contain instances of lv2_plugin_desc_t */ GSList *plugins; unsigned long plugin_count; mlt_properties blacklist; }; #endif #ifdef WITH_VST2 typedef struct _vst2_mgr vst2_mgr_t; struct _vst2_mgr { GSList *all_plugins; GSList *plugins; unsigned long plugin_count; mlt_properties blacklist; }; #endif struct _ui; plugin_mgr_t *plugin_mgr_new(); void plugin_mgr_destroy(plugin_mgr_t *plugin_mgr); void plugin_mgr_set_plugins(plugin_mgr_t *plugin_mgr, unsigned long rack_channels); plugin_desc_t *plugin_mgr_get_desc(plugin_mgr_t *plugin_mgr, unsigned long id); plugin_desc_t *plugin_mgr_get_any_desc(plugin_mgr_t *plugin_mgr, unsigned long id); #ifdef WITH_LV2 lv2_mgr_t *lv2_mgr_new(); void lv2_mgr_destroy(lv2_mgr_t *plugin_mgr); void lv2_mgr_set_plugins(lv2_mgr_t *plugin_mgr, unsigned long rack_channels); lv2_plugin_desc_t *lv2_mgr_get_desc(lv2_mgr_t *plugin_mgr, char *id); lv2_plugin_desc_t *lv2_mgr_get_any_desc(lv2_mgr_t *plugin_mgr, char *id); #endif #ifdef WITH_VST2 vst2_mgr_t *vst2_mgr_new(); void vst2_mgr_destroy(vst2_mgr_t *plugin_mgr); void vst2_mgr_set_plugins(vst2_mgr_t *vst2_mgr, unsigned long rack_channels); vst2_plugin_desc_t *vst2_mgr_get_desc(vst2_mgr_t *vst2_mgr, unsigned long id); vst2_plugin_desc_t *vst2_mgr_get_any_desc(vst2_mgr_t *vst2_mgr, unsigned long id);; #endif #endif /* __JR_PLUGIN_MANAGER_H__ */ mlt-7.38.0/src/modules/jackrack/plugin_settings.c000664 000000 000000 00000024441 15172202314 021761 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include "plugin_settings.h" static void settings_set_to_default (settings_t * settings, guint32 sample_rate) { unsigned long control; guint copy; LADSPA_Data value; for (control = 0; control < settings->desc->control_port_count; control++) { value = plugin_desc_get_default_control_value (settings->desc, control, sample_rate); for (copy = 0; copy < settings->copies; copy++) { settings->control_values[copy][control] = value; } settings->locks[control] = TRUE; } } settings_t * settings_new (plugin_desc_t * desc, unsigned long channels, guint32 sample_rate) { settings_t * settings; unsigned long channel; guint copies; settings = g_malloc (sizeof (settings_t)); copies = plugin_desc_get_copies (desc, channels); settings->sample_rate = sample_rate; settings->desc = desc; settings->copies = copies; settings->channels = channels; settings->lock_all = TRUE; settings->enabled = FALSE; settings->locks = NULL; settings->control_values = NULL; settings->wet_dry_enabled = FALSE; settings->wet_dry_locked = TRUE; /* control settings */ if (desc->control_port_count > 0) { guint copy; settings->locks = g_malloc (sizeof (gboolean) * desc->control_port_count); settings->control_values = g_malloc (sizeof (LADSPA_Data *) * copies); for (copy = 0; copy < copies; copy++) { settings->control_values[copy] = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count); } settings_set_to_default (settings, sample_rate); } /* wet/dry settings */ settings->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * channels); for (channel = 0; channel < channels; channel++) settings->wet_dry_values[channel] = 1.0; return settings; } settings_t * settings_dup (settings_t * other) { settings_t * settings; plugin_desc_t * desc; unsigned long channel; settings = g_malloc (sizeof (settings_t)); settings->sample_rate = other->sample_rate; settings->desc = other->desc; settings->copies = settings_get_copies (other); settings->channels = settings_get_channels (other); settings->wet_dry_enabled = settings_get_wet_dry_enabled (other); settings->wet_dry_locked = settings_get_wet_dry_locked (other); settings->lock_all = settings_get_lock_all (other); settings->enabled = settings_get_enabled (other); settings->locks = NULL; settings->control_values = NULL; desc = other->desc; if (desc->control_port_count > 0) { guint copy; unsigned long control; settings->locks = g_malloc (sizeof (gboolean) * desc->control_port_count); for (control = 0; control < desc->control_port_count; control++) settings_set_lock (settings, control, settings_get_lock (other, control)); settings->control_values = g_malloc (sizeof (LADSPA_Data *) * settings->copies); for (copy = 0; copy < settings->copies; copy++) { settings->control_values[copy] = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count); for (control = 0; control < desc->control_port_count; control++) { settings->control_values[copy][control] = settings_get_control_value (other, copy, control); } } } settings->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * settings->channels); for (channel = 0; channel < settings->channels; channel++) settings->wet_dry_values[channel] = settings_get_wet_dry_value (other, channel); return settings; } void settings_destroy (settings_t * settings) { if (settings->desc->control_port_count > 0) { guint i; for (i = 0; i < settings->copies; i++) g_free (settings->control_values[i]); g_free (settings->control_values); g_free (settings->locks); } g_free (settings->wet_dry_values); g_free (settings); } static void settings_set_copies (settings_t * settings, guint copies) { guint copy; guint last_copy; unsigned long control; if (copies <= settings->copies) return; last_copy = settings->copies - 1; settings->control_values = g_realloc (settings->control_values, sizeof (LADSPA_Data *) * copies); /* copy over the last settings to the new copies */ for (copy = settings->copies; copy < copies; copy++) { for (control = 0; control < settings->desc->control_port_count; control++) { settings->control_values[copy][control] = settings->control_values[last_copy][control]; } } settings->copies = copies; } static void settings_set_channels (settings_t * settings, unsigned long channels) { unsigned long channel; LADSPA_Data last_value; if (channels <= settings->channels) return; settings->wet_dry_values = g_realloc (settings->wet_dry_values, sizeof (LADSPA_Data) * channels); last_value = settings->wet_dry_values[settings->channels - 1]; for (channel = settings->channels; channel < channels; channel++) settings->wet_dry_values[channel] = last_value; settings->channels = channels; } void settings_set_sample_rate (settings_t * settings, guint32 sample_rate) { LADSPA_Data old_sample_rate; LADSPA_Data new_sample_rate; g_return_if_fail (settings != NULL); if (settings->sample_rate == sample_rate) return; if (settings->desc->control_port_count > 0) { unsigned long control; guint copy; new_sample_rate = (LADSPA_Data) sample_rate; old_sample_rate = (LADSPA_Data) settings->sample_rate; for (control = 0; control < settings->desc->control_port_count; control++) { for (copy = 0; copy < settings->copies; copy++) { if (LADSPA_IS_HINT_SAMPLE_RATE (settings->desc->port_range_hints[control].HintDescriptor)) { settings->control_values[copy][control] = (settings->control_values[copy][control] / old_sample_rate) * new_sample_rate; } } } } settings->sample_rate = sample_rate; } void settings_set_control_value (settings_t * settings, guint copy, unsigned long control_index, LADSPA_Data value) { g_return_if_fail (settings != NULL); g_return_if_fail (control_index < settings->desc->control_port_count); if (copy >= settings->copies) settings_set_copies (settings, copy + 1); settings->control_values[copy][control_index] = value; } void settings_set_lock (settings_t * settings, unsigned long control_index, gboolean locked) { g_return_if_fail (settings != NULL); g_return_if_fail (control_index < settings->desc->control_port_count); settings->locks[control_index] = locked; } void settings_set_lock_all (settings_t * settings, gboolean lock_all) { g_return_if_fail (settings != NULL); settings->lock_all = lock_all; } void settings_set_enabled (settings_t * settings, gboolean enabled) { g_return_if_fail (settings != NULL); settings->enabled = enabled; } void settings_set_wet_dry_enabled (settings_t * settings, gboolean enabled) { g_return_if_fail (settings != NULL); settings->wet_dry_enabled = enabled; } void settings_set_wet_dry_locked (settings_t * settings, gboolean locked) { g_return_if_fail (settings != NULL); settings->wet_dry_locked = locked; } void settings_set_wet_dry_value (settings_t * settings, unsigned long channel, LADSPA_Data value) { g_return_if_fail (settings != NULL); if (channel >= settings->channels) settings_set_channels (settings, channel + 1); settings->wet_dry_values[channel] = value; } LADSPA_Data settings_get_control_value (settings_t * settings, guint copy, unsigned long control_index) { g_return_val_if_fail (settings != NULL, NAN); g_return_val_if_fail (control_index < settings->desc->control_port_count, NAN); if (copy >= settings->copies) settings_set_copies (settings, copy - 1); return settings->control_values[copy][control_index]; } gboolean settings_get_lock (const settings_t * settings, unsigned long control_index) { g_return_val_if_fail (settings != NULL, FALSE); return settings->locks[control_index]; } gboolean settings_get_lock_all (const settings_t * settings) { g_return_val_if_fail (settings != NULL, FALSE); return settings->lock_all; } gboolean settings_get_enabled (const settings_t * settings) { g_return_val_if_fail (settings != NULL, FALSE); return settings->enabled; } guint settings_get_copies (const settings_t * settings) { g_return_val_if_fail (settings != NULL, 0); return settings->copies; } unsigned long settings_get_channels (const settings_t * settings) { g_return_val_if_fail (settings != NULL, 0); return settings->channels; } gboolean settings_get_wet_dry_enabled (const settings_t * settings) { g_return_val_if_fail (settings != NULL, FALSE); return settings->wet_dry_enabled; } gboolean settings_get_wet_dry_locked (const settings_t * settings) { g_return_val_if_fail (settings != NULL, FALSE); return settings->wet_dry_locked; } LADSPA_Data settings_get_wet_dry_value (settings_t * settings, unsigned long channel) { g_return_val_if_fail (settings != NULL, NAN); if (channel >= settings->channels) settings_set_channels (settings, channel + 1); return settings->wet_dry_values[channel]; } /* EOF */ mlt-7.38.0/src/modules/jackrack/plugin_settings.h000664 000000 000000 00000006071 15172202314 021765 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JR_PLUGIN_SETTINGS_H__ #define __JR_PLUGIN_SETTINGS_H__ #include #include #include "plugin_mgr.h" #include "plugin_desc.h" typedef struct _settings settings_t; struct _settings { guint32 sample_rate; plugin_desc_t * desc; guint copies; LADSPA_Data ** control_values; gboolean * locks; gboolean lock_all; gboolean enabled; unsigned long channels; gboolean wet_dry_enabled; gboolean wet_dry_locked; LADSPA_Data * wet_dry_values; }; settings_t * settings_new (plugin_desc_t * desc, unsigned long channels, guint32 sample_rate); settings_t * settings_dup (settings_t * settings); void settings_destroy (settings_t * settings); void settings_set_control_value (settings_t * settings, guint copy, unsigned long control_index, LADSPA_Data value); void settings_set_lock (settings_t * settings, unsigned long control_index, gboolean locked); void settings_set_lock_all (settings_t * settings, gboolean lock_all); void settings_set_enabled (settings_t * settings, gboolean enabled); void settings_set_wet_dry_enabled (settings_t * settings, gboolean enabled); void settings_set_wet_dry_locked (settings_t * settings, gboolean locked); void settings_set_wet_dry_value (settings_t * settings, unsigned long channel, LADSPA_Data value); LADSPA_Data settings_get_control_value (settings_t * settings, guint copy, unsigned long control_index); gboolean settings_get_lock (const settings_t * settings, unsigned long control_index); gboolean settings_get_lock_all (const settings_t * settings); gboolean settings_get_enabled (const settings_t * settings); guint settings_get_copies (const settings_t * settings); unsigned long settings_get_channels (const settings_t * settings); gboolean settings_get_wet_dry_enabled (const settings_t * settings); gboolean settings_get_wet_dry_locked (const settings_t * settings); LADSPA_Data settings_get_wet_dry_value (settings_t * settings, unsigned long channel); void settings_set_sample_rate (settings_t * settings, guint32 sample_rate); #endif /* __JR_PLUGIN_SETTINGS_H__ */ mlt-7.38.0/src/modules/jackrack/process.c000664 000000 000000 00000045735 15172202314 020232 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #ifdef WITH_JACK #include #endif #include #include #include #include #include #include #include #include "process.h" #include "lock_free_fifo.h" #include "plugin.h" #include "jack_rack.h" #include "framework/mlt_log.h" #ifndef _ #define _(x) x #endif extern pthread_mutex_t g_activate_mutex; #define USEC_PER_SEC 1000000 #define MSEC_PER_SEC 1000 #define TIME_RUN_SKIP_COUNT 5 #define MAX_BUFFER_SIZE 4096 jack_nframes_t sample_rate; jack_nframes_t buffer_size; #ifdef WITH_JACK static void jack_shutdown_cb (void * data) { process_info_t * procinfo = data; procinfo->quit = TRUE; } #endif /** process messages for plugins' control ports */ void process_control_port_messages (process_info_t * procinfo) { plugin_t * plugin; unsigned long control; unsigned long channel; gint copy; if (!procinfo->chain) return; for (plugin = procinfo->chain; plugin; plugin = plugin->next) { if (plugin->desc->control_port_count > 0) for (control = 0; control < plugin->desc->control_port_count; control++) for (copy = 0; copy < plugin->copies; copy++) { while (lff_read (plugin->holders[copy].ui_control_fifos + control, plugin->holders[copy].control_memory + control) == 0); } if (plugin->wet_dry_enabled) for (channel = 0; channel < procinfo->channels; channel++) { while (lff_read (plugin->wet_dry_fifos + channel, plugin->wet_dry_values + channel) == 0); } } } #ifdef WITH_JACK static int get_jack_buffers (process_info_t * procinfo, jack_nframes_t frames) { unsigned long channel; for (channel = 0; channel < procinfo->channels; channel++) { procinfo->jack_input_buffers[channel] = jack_port_get_buffer (procinfo->jack_input_ports[channel], frames); if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel); return 1; } procinfo->jack_output_buffers[channel] = jack_port_get_buffer (procinfo->jack_output_ports[channel], frames); if (!procinfo->jack_output_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel); return 1; } } return 0; } #endif plugin_t * get_first_enabled_plugin (process_info_t * procinfo) { plugin_t * first_enabled; if (!procinfo->chain) return NULL; for (first_enabled = procinfo->chain; first_enabled; first_enabled = first_enabled->next) { if (first_enabled->enabled) return first_enabled; } return NULL; } plugin_t * get_last_enabled_plugin (process_info_t * procinfo) { plugin_t * last_enabled; if (!procinfo->chain) return NULL; for (last_enabled = procinfo->chain_end; last_enabled; last_enabled = last_enabled->prev) { if (last_enabled->enabled) return last_enabled; } return NULL; } void connect_chain (process_info_t * procinfo, jack_nframes_t frames) { plugin_t * first_enabled, * last_enabled, * plugin; gint copy; unsigned long channel; if (!procinfo->chain) return; first_enabled = get_first_enabled_plugin (procinfo); if (!first_enabled) return; last_enabled = get_last_enabled_plugin (procinfo); /* sort out the aux ports */ plugin = first_enabled; do { if (plugin->desc->aux_channels > 0 && plugin->enabled) { #ifdef WITH_JACK if (procinfo->jack_client) { for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) plugin->descriptor-> connect_port (plugin->holders[copy].instance, plugin->desc->audio_aux_port_indicies[channel], jack_port_get_buffer (plugin->holders[copy].aux_ports[channel], frames)); } else #endif { for (copy = 0; copy < frames; copy++) procinfo->silent_buffer[copy] = 0.0; for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) plugin->descriptor-> connect_port (plugin->holders[copy].instance, plugin->desc->audio_aux_port_indicies[channel], procinfo->silent_buffer); } } } while ( (plugin != last_enabled) && (plugin = plugin->next) ); /* ensure that all the of the enabled plugins are connected to their memory */ plugin_connect_output_ports (first_enabled); if (first_enabled != last_enabled) { plugin_connect_input_ports (last_enabled, last_enabled->prev->audio_output_memory); for (plugin = first_enabled->next; plugin; plugin = plugin->next) { if (plugin->enabled) { plugin_connect_input_ports (plugin, plugin->prev->audio_output_memory); plugin_connect_output_ports (plugin); } } } /* input buffers for first plugin */ if( plugin->desc->has_input ) plugin_connect_input_ports (first_enabled, procinfo->jack_input_buffers); } void process_chain (process_info_t * procinfo, jack_nframes_t frames) { plugin_t * first_enabled; plugin_t * last_enabled = NULL; plugin_t * plugin; unsigned long channel; unsigned long i; #ifdef WITH_JACK if (procinfo->jack_client) { LADSPA_Data zero_signal[frames]; guint copy; /* set the zero signal to zero */ for (channel = 0; channel < frames; channel++) zero_signal[channel] = 0.0; /* possibly set aux output channels to zero if they're not enabled */ for (plugin = procinfo->chain; plugin; plugin = plugin->next) if (!plugin->enabled && plugin->desc->aux_channels > 0 && !plugin->desc->aux_are_input) for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) memcpy (jack_port_get_buffer (plugin->holders[copy].aux_ports[channel], frames), zero_signal, sizeof (LADSPA_Data) * frames); } #endif first_enabled = get_first_enabled_plugin (procinfo); /* no chain; just copy input to output */ if (!procinfo->chain || !first_enabled) { unsigned long channel; for (channel = 0; channel < procinfo->channels; channel++) { memcpy (procinfo->jack_output_buffers[channel], procinfo->jack_input_buffers[channel], sizeof(LADSPA_Data) * frames); } return; } /* all past here is guaranteed to have at least 1 enabled plugin */ last_enabled = get_last_enabled_plugin (procinfo); for (plugin = first_enabled; plugin; plugin = plugin->next) { if (plugin->enabled) { for (i = 0; i < plugin->copies; i++) plugin->descriptor->run (plugin->holders[i].instance, frames); if (plugin->wet_dry_enabled) for (channel = 0; channel < procinfo->channels; channel++) { if (procinfo->channel_mask & (1 << channel)) for (i = 0; i < frames; i++) { plugin->audio_output_memory[channel][i] *= plugin->wet_dry_values[channel]; plugin->audio_output_memory[channel][i] += plugin->audio_input_memory[channel][i] * (1.0 - plugin->wet_dry_values[channel]); } else memcpy (plugin->audio_output_memory[channel], plugin->audio_input_memory[channel], sizeof(LADSPA_Data) * frames); } if (plugin == last_enabled) break; } else { /* copy the data through */ for (i = 0; i < procinfo->channels; i++) memcpy (plugin->audio_output_memory[i], plugin->prev->audio_output_memory[i], sizeof(LADSPA_Data) * frames); } } /* copy the last enabled data to the jack ports */ for (i = 0; i < procinfo->channels; i++) memcpy (procinfo->jack_output_buffers[i], last_enabled->audio_output_memory[i], sizeof(LADSPA_Data) * frames); } int process_ladspa (process_info_t * procinfo, jack_nframes_t frames, LADSPA_Data ** inputs, LADSPA_Data ** outputs) { unsigned long channel; if (!procinfo) { mlt_log_error( NULL, "%s: no process_info from jack!\n", __FUNCTION__); return 1; } if (procinfo->quit == TRUE) return 1; process_control_port_messages (procinfo); for (channel = 0; channel < procinfo->channels; channel++) { if(get_first_enabled_plugin (procinfo)->desc->has_input) { procinfo->jack_input_buffers[channel] = inputs[channel]; if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel); return 1; } } procinfo->jack_output_buffers[channel] = outputs[channel]; if (!procinfo->jack_output_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel); return 1; } } connect_chain (procinfo, frames); process_chain (procinfo, frames); return 0; } #ifdef WITH_JACK int process_jack (jack_nframes_t frames, void * data) { int err; process_info_t * procinfo; procinfo = (process_info_t *) data; if (!procinfo) { mlt_log_error( NULL, "%s: no process_info from jack!\n", __FUNCTION__); return 1; } if (procinfo->port_count == 0) return 0; if (procinfo->quit == TRUE) return 1; process_control_port_messages (procinfo); err = get_jack_buffers (procinfo, frames); if (err) { mlt_log_warning( NULL, "%s: failed to get jack ports, not processing\n", __FUNCTION__); return 0; } connect_chain (procinfo, frames); process_chain (procinfo, frames); return 0; } /******************************************* ************** non RT stuff *************** *******************************************/ static int process_info_connect_jack (process_info_t * procinfo) { mlt_log_info( NULL, _("Connecting to JACK server with client name '%s'\n"), procinfo->jack_client_name); procinfo->jack_client = jack_client_open (procinfo->jack_client_name, JackNullOption, NULL); if (!procinfo->jack_client) { mlt_log_warning( NULL, "%s: could not create jack client; is the jackd server running?\n", __FUNCTION__); return 1; } mlt_log_verbose( NULL, _("Connected to JACK server\n")); jack_set_process_callback (procinfo->jack_client, process_jack, procinfo); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); return 0; } static void process_info_connect_port (process_info_t * procinfo, gshort in, unsigned long port_index, const char * port_name) { const char ** jack_ports; unsigned long jack_port_index; int err; char * full_port_name; jack_ports = jack_get_ports (procinfo->jack_client, NULL, NULL, JackPortIsPhysical | (in ? JackPortIsOutput : JackPortIsInput)); if (!jack_ports) return; for (jack_port_index = 0; jack_ports[jack_port_index] && jack_port_index <= port_index; jack_port_index++) { if (jack_port_index != port_index) continue; full_port_name = g_strdup_printf ("%s:%s", procinfo->jack_client_name, port_name); mlt_log_debug( NULL, _("Connecting ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]); err = jack_connect (procinfo->jack_client, in ? jack_ports[jack_port_index] : full_port_name, in ? full_port_name : jack_ports[jack_port_index]); if (err) mlt_log_warning( NULL, "%s: error connecting ports '%s' and '%s'\n", __FUNCTION__, full_port_name, jack_ports[jack_port_index]); else mlt_log_info( NULL, _("Connected ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]); free (full_port_name); } free (jack_ports); } static int process_info_set_port_count (process_info_t * procinfo, unsigned long port_count, gboolean connect_inputs, gboolean connect_outputs) { unsigned long i; char * port_name; jack_port_t ** port_ptr; gshort in; if (procinfo->port_count >= port_count) return -1; if (procinfo->port_count == 0) { procinfo->jack_input_ports = g_malloc (sizeof (jack_port_t *) * port_count); procinfo->jack_output_ports = g_malloc (sizeof (jack_port_t *) * port_count); procinfo->jack_input_buffers = g_malloc (sizeof (LADSPA_Data *) * port_count); procinfo->jack_output_buffers = g_malloc (sizeof (LADSPA_Data *) * port_count); } else { procinfo->jack_input_ports = g_realloc (procinfo->jack_input_ports, sizeof (jack_port_t *) * port_count); procinfo->jack_output_ports = g_realloc (procinfo->jack_output_ports, sizeof (jack_port_t *) * port_count); procinfo->jack_input_buffers = g_realloc (procinfo->jack_input_buffers, sizeof (LADSPA_Data *) * port_count); procinfo->jack_output_buffers = g_realloc (procinfo->jack_output_buffers, sizeof (LADSPA_Data *) * port_count); } for (i = procinfo->port_count; i < port_count; i++) { for (in = 0; in < 2; in++) { port_name = g_strdup_printf ("%s_%ld", in ? "in" : "out", i + 1); //mlt_log_debug( NULL, _("Creating %s port %s\n"), in ? "input" : "output", port_name); port_ptr = (in ? &procinfo->jack_input_ports[i] : &procinfo->jack_output_ports[i]); *port_ptr = jack_port_register (procinfo->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, in ? JackPortIsInput : JackPortIsOutput, 0); if (!*port_ptr) { mlt_log_error( NULL, "%s: could not register port '%s'; aborting\n", __FUNCTION__, port_name); return 1; } //mlt_log_debug( NULL, _("Created %s port %s\n"), in ? "input" : "output", port_name); if ((in && connect_inputs) || (!in && connect_outputs)) process_info_connect_port (procinfo, in, i, port_name); g_free (port_name); } } procinfo->port_count = port_count; return 0; } #endif void process_info_set_channels (process_info_t * procinfo, unsigned long channels, gboolean connect_inputs, gboolean connect_outputs) { #ifdef WITH_JACK process_info_set_port_count (procinfo, channels, connect_inputs, connect_outputs); #endif procinfo->channels = channels; } process_info_t * process_info_new (const char * client_name, unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs) { process_info_t * procinfo; char * jack_client_name; int err; procinfo = g_malloc (sizeof (process_info_t)); procinfo->chain = NULL; procinfo->chain_end = NULL; #ifdef WITH_JACK procinfo->jack_client = NULL; procinfo->port_count = 0; procinfo->jack_input_ports = NULL; procinfo->jack_output_ports = NULL; #endif procinfo->channels = rack_channels; procinfo->channel_mask = 0xFFFFFFFF; procinfo->quit = FALSE; if ( client_name == NULL ) { sample_rate = 48000; // should be set externally before calling process_ladspa buffer_size = MAX_BUFFER_SIZE; procinfo->silent_buffer = g_malloc (sizeof (LADSPA_Data) * buffer_size ); procinfo->jack_input_buffers = g_malloc (sizeof (LADSPA_Data *) * rack_channels); procinfo->jack_output_buffers = g_malloc (sizeof (LADSPA_Data *) * rack_channels); return procinfo; } /* sort out the client name */ procinfo->jack_client_name = jack_client_name = strdup (client_name); for (err = 0; jack_client_name[err] != '\0'; err++) { if (jack_client_name[err] == ' ') jack_client_name[err] = '_'; else if (!isalnum (jack_client_name[err])) { /* shift all the chars up one (to remove the non-alphanumeric char) */ int i; for (i = err; jack_client_name[i] != '\0'; i++) jack_client_name[i] = jack_client_name[i + 1]; } else if (isupper (jack_client_name[err])) jack_client_name[err] = tolower (jack_client_name[err]); } #ifdef WITH_JACK err = process_info_connect_jack (procinfo); if (err) { /* g_free (procinfo); */ return NULL; /* abort (); */ } sample_rate = jack_get_sample_rate (procinfo->jack_client); buffer_size = jack_get_sample_rate (procinfo->jack_client); jack_set_process_callback (procinfo->jack_client, process_jack, procinfo); pthread_mutex_lock( &g_activate_mutex ); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); pthread_mutex_unlock( &g_activate_mutex ); jack_activate (procinfo->jack_client); err = process_info_set_port_count (procinfo, rack_channels, connect_inputs, connect_outputs); if (err) return NULL; #endif return procinfo; } void process_info_destroy (process_info_t * procinfo) { #ifdef WITH_JACK if (procinfo->jack_client) { jack_deactivate (procinfo->jack_client); jack_client_close (procinfo->jack_client); } g_free (procinfo->jack_input_ports); g_free (procinfo->jack_output_ports); #endif g_free (procinfo->jack_input_buffers); g_free (procinfo->jack_output_buffers); g_free (procinfo->silent_buffer); g_free (procinfo); } void process_quit (process_info_t * procinfo) { procinfo->quit = TRUE; } mlt-7.38.0/src/modules/jackrack/process.h000664 000000 000000 00000004624 15172202314 020227 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JLH_PROCESS_H__ #define __JLH_PROCESS_H__ #include #ifdef WITH_JACK #include #endif #include #include "lock_free_fifo.h" typedef struct _process_info process_info_t; /** this is what gets passed to the process() callback and contains all the data the process callback will need */ struct _process_info { /** the plugin instance chain */ struct _plugin * chain; struct _plugin * chain_end; #ifdef WITH_JACK jack_client_t * jack_client; unsigned long port_count; jack_port_t ** jack_input_ports; jack_port_t ** jack_output_ports; #endif unsigned long channels; unsigned long channel_mask; LADSPA_Data ** jack_input_buffers; LADSPA_Data ** jack_output_buffers; LADSPA_Data * silent_buffer; char * jack_client_name; int quit; }; #ifndef WITH_JACK typedef guint32 jack_nframes_t; #endif extern jack_nframes_t sample_rate; extern jack_nframes_t buffer_size; process_info_t * process_info_new (const char * client_name, unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs); void process_info_destroy (process_info_t * procinfo); void process_info_set_channels (process_info_t * procinfo, unsigned long channels, gboolean connect_inputs, gboolean connect_outputs); int process_ladspa (process_info_t * procinfo, jack_nframes_t frames, LADSPA_Data ** inputs, LADSPA_Data ** outputs); #ifdef WITH_JACK int process_jack (jack_nframes_t frames, void * data); #endif void process_quit (process_info_t * procinfo); #endif /* __JLH_PROCESS_H__ */ mlt-7.38.0/src/modules/jackrack/producer_ladspa.c000664 000000 000000 00000020035 15172202314 021705 0ustar00rootroot000000 000000 /* * producer_ladspa.c -- LADSPA plugin producer * Copyright (C) 2013-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "jack_rack.h" #define BUFFER_LEN 10000 /** One-time initialization of jack rack. */ static jack_rack_t *initialise_jack_rack(mlt_properties properties, int channels) { jack_rack_t *jackrack = NULL; unsigned long plugin_id = mlt_properties_get_int64(properties, "_pluginid"); // Start JackRack if (plugin_id) { // Create JackRack without Jack client name so that it only uses LADSPA jackrack = jack_rack_new(NULL, channels); mlt_properties_set_data(properties, "_jackrack", jackrack, 0, (mlt_destructor) jack_rack_destroy, NULL); // Load one LADSPA plugin by its UniqueID plugin_desc_t *desc = plugin_mgr_get_any_desc(jackrack->plugin_mgr, plugin_id); plugin_t *plugin; if (desc && (plugin = jack_rack_instantiate_plugin(jackrack, desc))) { plugin->enabled = TRUE; plugin->wet_dry_enabled = FALSE; process_add_plugin(jackrack->procinfo, plugin); mlt_properties_set_int(properties, "instances", plugin->copies); } else { mlt_log_error(properties, "failed to load plugin %lu\n", plugin_id); } } return jackrack; } static int producer_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Get the producer service mlt_producer producer = mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "_producer_ladspa", NULL); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); int size = 0; LADSPA_Data **output_buffers = NULL; int i = 0; // Initialize LADSPA if needed jack_rack_t *jackrack = mlt_properties_get_data(producer_properties, "_jackrack", NULL); if (!jackrack) { sample_rate = *frequency; // global inside jack_rack jackrack = initialise_jack_rack(producer_properties, *channels); } if (jackrack) { // Correct the returns if necessary *samples = *samples <= 0 ? 1920 : *samples; *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; *format = mlt_audio_float; if (jackrack->procinfo && jackrack->procinfo->chain) { plugin_t *plugin = jackrack->procinfo->chain; LADSPA_Data value; int index, c; mlt_position position = mlt_frame_get_position(frame); mlt_position length = mlt_producer_get_length(producer); for (index = 0; index < plugin->desc->control_port_count; index++) { // Apply the control port values char key[20]; value = plugin_desc_get_default_control_value(plugin->desc, index, sample_rate); snprintf(key, sizeof(key), "%d", index); if (mlt_properties_get(producer_properties, key)) value = mlt_properties_anim_get_double(producer_properties, key, position, length); for (c = 0; c < plugin->copies; c++) plugin->holders[c].control_memory[index] = value; } } // Calculate the size of the buffer size = *samples * *channels * sizeof(float); // Allocate the buffer *buffer = mlt_pool_alloc(size); // Initialize the LADSPA output buffer. output_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * *channels); for (i = 0; i < *channels; i++) { output_buffers[i] = (LADSPA_Data *) *buffer + i * *samples; } // Do LADSPA processing process_ladspa(jackrack->procinfo, *samples, NULL, output_buffers); mlt_pool_release(output_buffers); // Set the buffer for destruction mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); if (jackrack && jackrack->procinfo && jackrack->procinfo->chain && mlt_properties_get_int64(producer_properties, "_pluginid")) { plugin_t *plugin = jackrack->procinfo->chain; LADSPA_Data value; int i, c; for (i = 0; i < plugin->desc->status_port_count; i++) { // read the status port values char key[20]; int p = plugin->desc->status_port_indicies[i]; for (c = 0; c < plugin->copies; c++) { snprintf(key, sizeof(key), "%d[%d]", p, c); value = plugin->holders[c].status_memory[i]; mlt_properties_set_double(producer_properties, key, value); } } } } return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // Check that we created a frame and initialize it if (*frame != NULL) { // Obtain properties of frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Save the producer to be used in get_audio mlt_properties_set_data(frame_properties, "_producer_ladspa", producer, 0, NULL, NULL); // Push the get_audio method mlt_frame_push_audio(*frame, producer_get_audio); } // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } /** Destructor for the producer. */ static void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } /** Constructor for the producer. */ mlt_producer producer_ladspa_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); if (producer != NULL) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; // Save the plugin ID. if (!strncmp(id, "ladspa.", 7)) { mlt_properties_set(properties, "_pluginid", id + 7); } // Make sure the plugin ID is valid. unsigned long plugin_id = mlt_properties_get_int64(properties, "_pluginid"); if (plugin_id < 1000 || plugin_id > 0x00FFFFFF) { producer_close(producer); producer = NULL; } } return producer; } mlt-7.38.0/src/modules/jackrack/producer_ladspa.yml000664 000000 000000 00000000413 15172202314 022262 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: ladspa title: LADSPA version: 1 license: GPLv2 language: en tags: - Audio description: Generate audio using LADSPA plugins. notes: > Automatically adapts to the number of channels and sampling rate of the consumer. mlt-7.38.0/src/modules/jackrack/producer_lv2.c000664 000000 000000 00000017606 15172202314 021156 0ustar00rootroot000000 000000 /* * producer_lv2.c -- LV2 plugin producer * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "lv2_context.h" #define BUFFER_LEN 10000 /** One-time initialization of lv2 rack. */ static lv2_context_t *initialise_lv2_context(mlt_properties properties, int channels) { lv2_context_t *lv2context = NULL; //unsigned long plugin_id = mlt_properties_get_int64(properties, "_pluginid"); char *plugin_id = NULL; plugin_id = mlt_properties_get(properties, "_pluginid"); // Start Lv2context if (plugin_id) { // Create Lv2context without Jack client name so that it only uses LV2 lv2context = lv2_context_new(NULL, channels); mlt_properties_set_data(properties, "_lv2context", lv2context, 0, (mlt_destructor) lv2_context_destroy, NULL); // Load one LV2 plugin by its URI lv2_plugin_desc_t *desc = lv2_mgr_get_any_desc(lv2context->plugin_mgr, plugin_id); lv2_plugin_t *plugin; if (desc && (plugin = lv2_context_instantiate_plugin(lv2context, desc))) { plugin->enabled = TRUE; plugin->wet_dry_enabled = FALSE; lv2_process_add_plugin(lv2context->procinfo, plugin); mlt_properties_set_int(properties, "instances", plugin->copies); } else { mlt_log_error(properties, "failed to load plugin %s\n", plugin_id); } } return lv2context; } static int producer_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Get the producer service mlt_producer producer = mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "_producer_lv2", NULL); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); int size = 0; LADSPA_Data **output_buffers = NULL; int i = 0; // Initialize LV2 if needed lv2_context_t *lv2context = mlt_properties_get_data(producer_properties, "_lv2context", NULL); if (!lv2context) { lv2_sample_rate = *frequency; // global inside lv2_context lv2context = initialise_lv2_context(producer_properties, *channels); } if (lv2context) { // Correct the returns if necessary *samples = *samples <= 0 ? 1920 : *samples; *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; *format = mlt_audio_float; if (lv2context->procinfo && lv2context->procinfo->chain) { lv2_plugin_t *plugin = lv2context->procinfo->chain; LADSPA_Data value; int index, c; mlt_position position = mlt_frame_get_position(frame); mlt_position length = mlt_producer_get_length(producer); for (index = 0; index < plugin->desc->control_port_count; index++) { // Apply the control port values char key[20]; value = plugin->desc->def_values[plugin->desc->control_port_indicies[index]]; snprintf(key, sizeof(key), "%d", (int) plugin->desc->control_port_indicies[index]); if (mlt_properties_get(producer_properties, key)) value = mlt_properties_anim_get_double(producer_properties, key, position, length); for (c = 0; c < plugin->copies; c++) plugin->holders[c].control_memory[index] = value; } } // Calculate the size of the buffer size = *samples * *channels * sizeof(float); // Allocate the buffer *buffer = mlt_pool_alloc(size); // Initialize the LV2 output buffer. output_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * *channels); for (i = 0; i < *channels; i++) { output_buffers[i] = (LADSPA_Data *) *buffer + i * *samples; } // Do LV2 processing process_lv2(lv2context->procinfo, *samples, NULL, output_buffers); mlt_pool_release(output_buffers); // Set the buffer for destruction mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); char *plugin_id = NULL; plugin_id = mlt_properties_get(producer_properties, "_pluginid"); if (lv2context && lv2context->procinfo && lv2context->procinfo->chain && plugin_id) { lv2_plugin_t *plugin = lv2context->procinfo->chain; LADSPA_Data value; int i, c; for (i = 0; i < plugin->desc->status_port_count; i++) { // read the status port values char key[20]; int p = plugin->desc->status_port_indicies[i]; for (c = 0; c < plugin->copies; c++) { snprintf(key, sizeof(key), "%d[%d]", p, c); value = plugin->holders[c].status_memory[i]; mlt_properties_set_double(producer_properties, key, value); } } } } return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // Check that we created a frame and initialize it if (*frame != NULL) { // Obtain properties of frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Save the producer to be used in get_audio mlt_properties_set_data(frame_properties, "_producer_lv2", producer, 0, NULL, NULL); // Push the get_audio method mlt_frame_push_audio(*frame, producer_get_audio); } // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } /** Destructor for the producer. */ static void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } /** Constructor for the producer. */ mlt_producer producer_lv2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); if (producer != NULL) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; // Save the plugin ID. if (!strncmp(id, "lv2.", 4)) { mlt_properties_set(properties, "_pluginid", id + 4); } } return producer; } mlt-7.38.0/src/modules/jackrack/producer_lv2.yml000664 000000 000000 00000000402 15172202314 021517 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: lv2 title: LV2 version: 1 license: GPLv2 language: en tags: - Audio description: Generate audio using LV2 plugins. notes: > Automatically adapts to the number of channels and sampling rate of the consumer. mlt-7.38.0/src/modules/jackrack/producer_vst2.c000664 000000 000000 00000021572 15172202314 021346 0ustar00rootroot000000 000000 /* * producer_vst2.c -- VST2 plugin producer * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "vst2_context.h" #define BUFFER_LEN 10000 /** One-time initialization of jack rack. */ static vst2_context_t *initialise_vst2_context(mlt_properties properties, int channels) { vst2_context_t *vst2context = NULL; unsigned long plugin_id = mlt_properties_get_int64(properties, "_pluginid"); // Start Vst2context if (plugin_id) { // Create Vst2context without Jack client name so that it only uses LADSPA vst2context = vst2_context_new(NULL, channels); mlt_properties_set_data(properties, "_vst2context", vst2context, 0, (mlt_destructor) vst2_context_destroy, NULL); // Load one LADSPA plugin by its UniqueID vst2_plugin_desc_t *desc = vst2_mgr_get_any_desc(vst2context->plugin_mgr, plugin_id); vst2_plugin_t *plugin; if (desc && (plugin = vst2_context_instantiate_plugin(vst2context, desc))) { plugin->enabled = TRUE; plugin->wet_dry_enabled = FALSE; vst2_process_add_plugin(vst2context->procinfo, plugin); mlt_properties_set_int(properties, "instances", plugin->copies); } else { mlt_log_error(properties, "failed to load plugin %lu\n", plugin_id); } } return vst2context; } static int producer_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Get the producer service mlt_producer producer = mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "_producer_vst2", NULL); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); int size = 0; LADSPA_Data **output_buffers = NULL; int i = 0; // Initialize VST2 if needed vst2_context_t *vst2context = mlt_properties_get_data(producer_properties, "_vst2context", NULL); if (!vst2context) { vst2_sample_rate = *frequency; // global inside jack_rack vst2context = initialise_vst2_context(producer_properties, *channels); } if (vst2context) { // Correct the returns if necessary *samples = *samples <= 0 ? 1920 : *samples; *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; *format = mlt_audio_float; if (vst2context->procinfo && vst2context->procinfo->chain) { vst2_plugin_t *plugin = vst2context->procinfo->chain; LADSPA_Data value; int index, c; mlt_position position = mlt_frame_get_position(frame); mlt_position length = mlt_producer_get_length(producer); for (index = 0; index < plugin->desc->control_port_count; index++) { // Apply the control port values char key[20]; value = vst2_plugin_desc_get_default_control_value(plugin->desc, index, vst2_sample_rate); snprintf(key, sizeof(key), "%d", index); if (mlt_properties_get(producer_properties, key)) value = mlt_properties_anim_get_double(producer_properties, key, position, length); for (c = 0; c < plugin->copies; c++) { if (plugin->holders[c].control_memory[index] != value) { plugin->holders[c].control_memory[index] = value; plugin->holders[c] .effect->setParameter(plugin->holders[c].effect, plugin->desc->control_port_indicies[index] - (plugin->holders[c].effect->numInputs + plugin->holders[c].effect->numOutputs), plugin->holders[c].control_memory[index]); } } } } // Calculate the size of the buffer size = *samples * *channels * sizeof(float); // Allocate the buffer *buffer = mlt_pool_alloc(size); // Initialize the LADSPA output buffer. output_buffers = mlt_pool_alloc(sizeof(LADSPA_Data *) * *channels); for (i = 0; i < *channels; i++) { output_buffers[i] = (LADSPA_Data *) *buffer + i * *samples; } // Do VST2 processing process_vst2(vst2context->procinfo, *samples, NULL, output_buffers); mlt_pool_release(output_buffers); // Set the buffer for destruction mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); if (vst2context && vst2context->procinfo && vst2context->procinfo->chain && mlt_properties_get_int64(producer_properties, "_pluginid")) { vst2_plugin_t *plugin = vst2context->procinfo->chain; LADSPA_Data value; int i, c; for (i = 0; i < plugin->desc->status_port_count; i++) { // read the status port values char key[20]; int p = plugin->desc->status_port_indicies[i]; for (c = 0; c < plugin->copies; c++) { snprintf(key, sizeof(key), "%d[%d]", p, c); value = plugin->holders[c].status_memory[i]; mlt_properties_set_double(producer_properties, key, value); } } } } return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // Check that we created a frame and initialize it if (*frame != NULL) { // Obtain properties of frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Save the producer to be used in get_audio mlt_properties_set_data(frame_properties, "_producer_vst2", producer, 0, NULL, NULL); // Push the get_audio method mlt_frame_push_audio(*frame, producer_get_audio); } // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } /** Destructor for the producer. */ static void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } /** Constructor for the producer. */ mlt_producer producer_vst2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); if (producer != NULL) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; // Save the plugin ID. if (!strncmp(id, "vst2.", 5)) { mlt_properties_set(properties, "_pluginid", id + 5); } // Make sure the plugin ID is valid. unsigned long plugin_id = mlt_properties_get_int64(properties, "_pluginid"); if (plugin_id < 1000 || plugin_id > 0x00FFFFFF) { producer_close(producer); producer = NULL; } } return producer; } mlt-7.38.0/src/modules/jackrack/producer_vst2.yml000664 000000 000000 00000000405 15172202314 021715 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: vst2 title: VST2 version: 1 license: GPLv2 language: en tags: - Audio description: Generate audio using VST2 plugins. notes: > Automatically adapts to the number of channels and sampling rate of the consumer. mlt-7.38.0/src/modules/jackrack/vestige.h000664 000000 000000 00000023366 15172202314 020223 0ustar00rootroot000000 000000 /* * IMPORTANT: The author of Carla has no connection with the * author of the VeSTige VST-compatibility header, has had no * involvement in its creation. * * The VeSTige header is included in this package in the good-faith * belief that it has been cleanly and legally reverse engineered * without reference to the official VST SDK and without its * developer(s) having agreed to the VST SDK license agreement. */ /* * simple header to allow VeSTige compilation and eventually work * * Copyright (c) 2006 Javier Serrano Polo * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program (see COPYING); if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include #ifndef _VESTIGE_H #define _VESTIGE_H #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #define __cdecl #endif #define CCONST(a, b, c, d) \ ((((int) a) << 24) | (((int) b) << 16) | (((int) c) << 8) | (((int) d) << 0)) #define audioMasterAutomate 0 #define audioMasterVersion 1 #define audioMasterCurrentId 2 #define audioMasterIdle 3 #define audioMasterPinConnected 4 // unsupported? 5 #define audioMasterWantMidi 6 #define audioMasterGetTime 7 #define audioMasterProcessEvents 8 #define audioMasterSetTime 9 #define audioMasterTempoAt 10 #define audioMasterGetNumAutomatableParameters 11 #define audioMasterGetParameterQuantization 12 #define audioMasterIOChanged 13 #define audioMasterNeedIdle 14 #define audioMasterSizeWindow 15 #define audioMasterGetSampleRate 16 #define audioMasterGetBlockSize 17 #define audioMasterGetInputLatency 18 #define audioMasterGetOutputLatency 19 #define audioMasterGetPreviousPlug 20 #define audioMasterGetNextPlug 21 #define audioMasterWillReplaceOrAccumulate 22 #define audioMasterGetCurrentProcessLevel 23 #define audioMasterGetAutomationState 24 #define audioMasterOfflineStart 25 #define audioMasterOfflineRead 26 #define audioMasterOfflineWrite 27 #define audioMasterOfflineGetCurrentPass 28 #define audioMasterOfflineGetCurrentMetaPass 29 #define audioMasterSetOutputSampleRate 30 // unsupported? 31 #define audioMasterGetSpeakerArrangement 31 // deprecated in 2.4? #define audioMasterGetVendorString 32 #define audioMasterGetProductString 33 #define audioMasterGetVendorVersion 34 #define audioMasterVendorSpecific 35 #define audioMasterSetIcon 36 #define audioMasterCanDo 37 #define audioMasterGetLanguage 38 #define audioMasterOpenWindow 39 #define audioMasterCloseWindow 40 #define audioMasterGetDirectory 41 #define audioMasterUpdateDisplay 42 #define audioMasterBeginEdit 43 #define audioMasterEndEdit 44 #define audioMasterOpenFileSelector 45 #define audioMasterCloseFileSelector 46 // currently unused #define audioMasterEditFile 47 // currently unused #define audioMasterGetChunkFile 48 // currently unused #define audioMasterGetInputSpeakerArrangement 49 // currently unused #define effFlagsHasEditor 1 #define effFlagsCanReplacing (1 << 4) // very likely #define effFlagsIsSynth (1 << 8) // currently unused #define effOpen 0 #define effClose 1 #define effSetProgram 2 #define effGetProgram 3 #define effGetProgramName 5 #define effGetParamName 8 #define effSetSampleRate 10 #define effSetBlockSize 11 #define effMainsChanged 12 #define effEditGetRect 13 #define effEditOpen 14 #define effEditClose 15 #define effEditIdle 19 #define effEditTop 20 #define effProcessEvents 25 #define effGetPlugCategory 35 #define effGetEffectName 45 #define effGetVendorString 47 #define effGetProductString 48 #define effGetVendorVersion 49 #define effCanDo 51 #define effIdle 53 #define effGetParameterProperties 56 #define effGetVstVersion 58 #define effShellGetNextPlugin 70 #define effStartProcess 71 #define effStopProcess 72 #define effBeginSetProgram 67 #define effEndSetProgram 68 #ifdef WORDS_BIGENDIAN // "VstP" #define kEffectMagic 0x50747356 #else // "PtsV" #define kEffectMagic 0x56737450 #endif #define kVstLangEnglish 1 #define kVstMidiType 1 struct RemoteVstPlugin; #define kVstTransportChanged 1 #define kVstTransportPlaying (1 << 1) #define kVstTransportCycleActive (1 << 2) #define kVstTransportRecording (1 << 3) #define kVstAutomationWriting (1 << 6) #define kVstAutomationReading (1 << 7) #define kVstNanosValid (1 << 8) #define kVstPpqPosValid (1 << 9) #define kVstTempoValid (1 << 10) #define kVstBarsValid (1 << 11) #define kVstCyclePosValid (1 << 12) #define kVstTimeSigValid (1 << 13) #define kVstSmpteValid (1 << 14) #define kVstClockValid (1 << 15) struct _VstMidiEvent { // 00 int type; // 04 int byteSize; // 08 int deltaFrames; // 0c? int flags; // 10? int noteLength; // 14? int noteOffset; // 18 char midiData[4]; // 1c? char detune; // 1d? char noteOffVelocity; // 1e? char reserved1; // 1f? char reserved2; }; typedef struct _VstMidiEvent VstMidiEvent; struct _VstEvent { char dump[sizeof(VstMidiEvent)]; }; typedef struct _VstEvent VstEvent; struct _VstEvents { // 00 int numEvents; // 04 void *reserved; // 08 VstEvent *events[2]; }; enum Vestige2StringConstants { VestigeMaxNameLen = 64, VestigeMaxLabelLen = 64, VestigeMaxShortLabelLen = 8, VestigeMaxCategLabelLen = 24, VestigeMaxFileNameLen = 100 }; enum VstPlugCategory { kPlugCategUnknown = 0, kPlugCategEffect, kPlugCategSynth, kPlugCategAnalysis, kPlugCategMastering, kPlugCategSpacializer, kPlugCategRoomFx, kPlugSurroundFx, kPlugCategRestoration, kPlugCategOfflineProcess, kPlugCategShell, kPlugCategGenerator, kPlugCategMaxCount }; typedef struct _VstEvents VstEvents; struct _VstParameterProperties { float stepFloat; /* float step */ float smallStepFloat; /* small float step */ float largeStepFloat; /* large float step */ char label[VestigeMaxLabelLen]; /* parameter label */ int32_t flags; /* @see VstParameterFlags */ int32_t minInteger; /* integer minimum */ int32_t maxInteger; /* integer maximum */ int32_t stepInteger; /* integer step */ int32_t largeStepInteger; /* large integer step */ char shortLabel[VestigeMaxShortLabelLen]; /* short label, recommended: 6 + delimiter */ int16_t displayIndex; /* index where this parameter should be displayed (starting with 0) */ int16_t category; /* 0: no category, else group index + 1 */ int16_t numParametersInCategory; /* number of parameters in category */ int16_t reserved; /* zero */ char categoryLabel[VestigeMaxCategLabelLen]; /* category label, e.g. "Osc 1" */ char future[16]; /* reserved for future use */ }; typedef struct _VstParameterProperties VstParameterProperties; enum VstParameterFlags { kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */ kVstParameterUsesIntegerMinMax = 1 << 1, /* minInteger, maxInteger valid */ kVstParameterUsesFloatStep = 1 << 2, /* stepFloat, smallStepFloat, largeStepFloat valid */ kVstParameterUsesIntStep = 1 << 3, /* stepInteger, largeStepInteger valid */ kVstParameterSupportsDisplayIndex = 1 << 4, /* displayIndex valid */ kVstParameterSupportsDisplayCategory = 1 << 5, /* category, etc. valid */ kVstParameterCanRamp = 1 << 6 /* set if parameter value can ramp up/down */ }; struct _AEffect { // Never use virtual functions!!! // 00-03 int magic; // dispatcher 04-07 intptr_t(__cdecl *dispatcher)(struct _AEffect *, int, int, intptr_t, void *, float); // process, quite sure 08-0b void(__cdecl *process)(struct _AEffect *, float **, float **, int); // setParameter 0c-0f void(__cdecl *setParameter)(struct _AEffect *, int, float); // getParameter 10-13 float(__cdecl *getParameter)(struct _AEffect *, int); // programs 14-17 int numPrograms; // Params 18-1b int numParams; // Input 1c-1f int numInputs; // Output 20-23 int numOutputs; // flags 24-27 int flags; // Fill somewhere 28-2b void *ptr1; void *ptr2; int initialDelay; // Zeroes 30-33 34-37 38-3b char empty2[4 + 4]; // 1.0f 3c-3f float unkown_float; // An object? pointer 40-43 void *object; // Zeroes 44-47 void *user; // Id 48-4b int32_t uniqueID; // plugin version 4c-4f int32_t version; // processReplacing 50-53 void(__cdecl *processReplacing)(struct _AEffect *, float **, float **, int); }; typedef struct _AEffect AEffect; typedef struct _VstTimeInfo { /* info from online documentation of VST provided by Steinberg */ double samplePos; double sampleRate; double nanoSeconds; double ppqPos; double tempo; double barStartPos; double cycleStartPos; double cycleEndPos; int32_t timeSigNumerator; int32_t timeSigDenominator; int32_t smpteOffset; int32_t smpteFrameRate; int32_t samplesToNextClock; int32_t flags; } VstTimeInfo; typedef intptr_t(__cdecl *audioMasterCallback)(AEffect *, int32_t, int32_t, intptr_t, void *, float); #endif mlt-7.38.0/src/modules/jackrack/vst2_context.c000664 000000 000000 00000013676 15172202314 021215 0ustar00rootroot000000 000000 /* * VST2 Context * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include "framework/mlt_log.h" #include "vst2_context.h" #include "vst2_plugin_settings.h" #ifndef _ #define _(x) x #endif #define _x (const xmlChar *) #define _s (const char *) extern vst2_mgr_t *g_vst2_plugin_mgr; vst2_context_t *vst2_context_new(const char *client_name, unsigned long channels) { vst2_context_t *rack; rack = g_malloc(sizeof(vst2_context_t)); rack->saved_plugins = NULL; rack->channels = channels; rack->procinfo = vst2_process_info_new(client_name, channels, FALSE, FALSE); if (!rack->procinfo) { g_free(rack); return NULL; } rack->plugin_mgr = g_vst2_plugin_mgr; vst2_mgr_set_plugins(rack->plugin_mgr, channels); return rack; } #include extern vst2_plugin_t *vst2_get_first_enabled_plugin(vst2_process_info_t *procinfo); extern vst2_plugin_t *vst2_get_last_enabled_plugin(vst2_process_info_t *procinfo); void vst2_context_destroy(vst2_context_t *vst2_context) { vst2_plugin_t *first_enabled = vst2_get_first_enabled_plugin(vst2_context->procinfo); vst2_plugin_t *last_enabled = vst2_get_last_enabled_plugin(vst2_context->procinfo); vst2_plugin_t *plugin = first_enabled; do { dlclose(plugin->dl_handle); } while ((plugin != last_enabled) && (plugin = plugin->next)); vst2_process_quit(vst2_context->procinfo); vst2_process_info_destroy(vst2_context->procinfo); g_slist_free(vst2_context->saved_plugins); g_free(vst2_context); } vst2_plugin_t *vst2_context_instantiate_plugin(vst2_context_t *vst2_context, vst2_plugin_desc_t *desc) { vst2_plugin_t *plugin; /* check whether or not the plugin is RT capable and confirm with the user if it isn't */ if (!LADSPA_IS_HARD_RT_CAPABLE(desc->properties)) { mlt_log_info(NULL, "Plugin not RT capable. The plugin '%s' does not describe itself as being " "capable of real-time operation. You may experience drop outs or jack may " "even kick us out if you use it.\n", desc->name); } /* create the plugin */ plugin = vst2_plugin_new(desc, vst2_context); if (!plugin) { mlt_log_error(NULL, "Error loading file plugin '%s' from file '%s'\n", desc->name, desc->object_file); } return plugin; } void vst2_context_add_saved_plugin(vst2_context_t *vst2_context, vst2_saved_plugin_t *saved_plugin) { vst2_plugin_t *plugin = vst2_context_instantiate_plugin(vst2_context, saved_plugin->settings->desc); if (!plugin) { mlt_log_warning(NULL, "%s: could not instantiate object file '%s'\n", __FUNCTION__, saved_plugin->settings->desc->object_file); return; } vst2_context->saved_plugins = g_slist_append(vst2_context->saved_plugins, saved_plugin); vst2_process_add_plugin(vst2_context->procinfo, plugin); vst2_context_add_plugin(vst2_context, plugin); } void vst2_context_add_plugin(vst2_context_t *vst2_context, vst2_plugin_t *plugin) { vst2_saved_plugin_t *saved_plugin = NULL; GSList *list; unsigned long control, channel; LADSPA_Data value; guint copy; /* see if there's any saved settings that match the plugin id */ for (list = vst2_context->saved_plugins; list; list = g_slist_next(list)) { saved_plugin = list->data; if (saved_plugin->settings->desc->id == plugin->desc->id) { /* process the settings! */ vst2_context->saved_plugins = g_slist_remove(vst2_context->saved_plugins, saved_plugin); break; } saved_plugin = NULL; } if (!saved_plugin) return; /* initialize plugin parameters */ plugin->enabled = vst2_settings_get_enabled(saved_plugin->settings); plugin->wet_dry_enabled = vst2_settings_get_wet_dry_enabled(saved_plugin->settings); for (control = 0; control < saved_plugin->settings->desc->control_port_count; control++) for (copy = 0; copy < plugin->copies; copy++) { value = vst2_settings_get_control_value(saved_plugin->settings, copy, control); plugin->holders[copy].control_memory[control] = value; //mlt_log_debug( NULL, "setting control value %s (%d) = %f\n", saved_plugin->settings->desc->port_names[control], copy, value); // lff_write (plugin->holders[copy].ui_control_fifos + control, &value); } if (plugin->wet_dry_enabled) for (channel = 0; channel < vst2_context->channels; channel++) { value = vst2_settings_get_wet_dry_value(saved_plugin->settings, channel); plugin->wet_dry_values[channel] = value; //mlt_log_debug( NULL, "setting wet/dry value %d = %f\n", channel, value); // lff_write (plugin->wet_dry_fifos + channel, &value); } } /* EOF */ mlt-7.38.0/src/modules/jackrack/vst2_context.h000664 000000 000000 00000004130 15172202314 021203 0ustar00rootroot000000 000000 /* * VST2 Context * * Based on the Jack Rack module * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JR_VST2_CONTEXT_H__ #define __JR_VST2_CONTEXT_H__ #include #include #include "plugin_mgr.h" #include "vst2_plugin.h" #include "vst2_plugin_settings.h" #include "vst2_process.h" typedef struct _vst2_saved_plugin vst2_saved_plugin_t; struct _vst2_saved_plugin { vst2_settings_t *settings; }; typedef struct _saved_rack saved_rack_t; struct _saved_rack { unsigned long channels; jack_nframes_t sample_rate; GSList *plugins; }; typedef struct _vst2_context vst2_context_t; struct _vst2_context { vst2_mgr_t *plugin_mgr; vst2_process_info_t *procinfo; unsigned long channels; GSList *saved_plugins; }; vst2_context_t *vst2_context_new(const char *client_name, unsigned long channels); void vst2_context_destroy(vst2_context_t *vst2_context); void vst2_context_add_plugin(vst2_context_t *vst2_context, vst2_plugin_t *plugin); void vst2_context_add_saved_plugin(vst2_context_t *vst2_context, struct _vst2_saved_plugin *saved_plugin); vst2_plugin_t *vst2_context_instantiate_plugin(vst2_context_t *vst2_context, vst2_plugin_desc_t *desc); #endif /* __JR_VST2_CONTEXT_H__ */ mlt-7.38.0/src/modules/jackrack/vst2_plugin.c000664 000000 000000 00000043731 15172202314 021022 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include "framework/mlt_log.h" #include "modules/jackrack/lock_free_fifo.h" #include "vst2_context.h" #include "vst2_plugin.h" #include "vst2_process.h" #define CONTROL_FIFO_SIZE 128 #ifdef WITH_JACK /* swap over the jack ports in two plugins */ static void vst2_plugin_swap_aux_ports(vst2_plugin_t *plugin, vst2_plugin_t *other) { guint copy; jack_port_t **aux_ports_tmp; for (copy = 0; copy < plugin->copies; copy++) { aux_ports_tmp = other->holders[copy].aux_ports; other->holders[copy].aux_ports = plugin->holders[copy].aux_ports; plugin->holders[copy].aux_ports = aux_ports_tmp; } } #endif /** connect up the ladspa instance's input buffers to the previous plugin's audio memory. make sure to check that plugin->prev exists. */ void vst2_plugin_connect_input_ports(vst2_plugin_t *plugin, LADSPA_Data **inputs) { gint copy; unsigned long channel; unsigned long rack_channel; if (!plugin || !inputs) return; rack_channel = 0; for (copy = 0; copy < plugin->copies; copy++) { for (channel = 0; channel < plugin->desc->channels; channel++) { plugin->holders[copy] .effect->setParameter(plugin->holders[copy].effect, plugin->desc->audio_input_port_indicies[channel] - (plugin->holders[copy].effect->numInputs + plugin->holders[copy].effect->numOutputs), *inputs[rack_channel]); rack_channel++; } } plugin->audio_input_memory = inputs; } /** connect up a plugin's output ports to its own audio_output_memory output memory */ void vst2_plugin_connect_output_ports(vst2_plugin_t *plugin) { gint copy; unsigned long channel; unsigned long rack_channel = 0; if (!plugin) return; for (copy = 0; copy < plugin->copies; copy++) { for (channel = 0; channel < plugin->desc->channels; channel++) { /* WIP: It might be not used */ plugin->holders[copy] .effect->setParameter(plugin->holders[copy].effect, plugin->desc->audio_input_port_indicies[channel] - (plugin->holders[copy].effect->numInputs + plugin->holders[copy].effect->numOutputs), *plugin->audio_output_memory[rack_channel]); rack_channel++; } } } void vst2_process_add_plugin(vst2_process_info_t *procinfo, vst2_plugin_t *plugin) { /* sort out list pointers */ plugin->next = NULL; plugin->prev = procinfo->chain_end; if (procinfo->chain_end) procinfo->chain_end->next = plugin; else procinfo->chain = plugin; procinfo->chain_end = plugin; } /** remove a plugin from the chain */ vst2_plugin_t *vst2_process_remove_plugin(vst2_process_info_t *procinfo, vst2_plugin_t *plugin) { /* sort out chain pointers */ if (plugin->prev) plugin->prev->next = plugin->next; else procinfo->chain = plugin->next; if (plugin->next) plugin->next->prev = plugin->prev; else procinfo->chain_end = plugin->prev; #ifdef WITH_JACK /* sort out the aux ports */ if (procinfo->jack_client && plugin->desc->aux_channels > 0) { vst2_plugin_t *other; for (other = plugin->next; other; other = other->next) if (other->desc->id == plugin->desc->id) vst2_plugin_swap_aux_ports(plugin, other); } #endif return plugin; } /** enable/disable a plugin */ void vst2_process_ablise_plugin(vst2_process_info_t *procinfo, vst2_plugin_t *plugin, gboolean enable) { plugin->enabled = enable; } /** enable/disable a plugin */ void vst2_process_ablise_vst2_plugin_wet_dry(vst2_process_info_t *procinfo, vst2_plugin_t *plugin, gboolean enable) { plugin->wet_dry_enabled = enable; } /** move a plugin up or down one place in the chain */ void vst2_process_move_plugin(vst2_process_info_t *procinfo, vst2_plugin_t *plugin, gint up) { /* other plugins in the chain */ vst2_plugin_t *pp = NULL, *p, *n, *nn = NULL; /* note that we should never receive an illogical move request ie, there will always be at least 1 plugin before for an up request or 1 plugin after for a down request */ /* these are pointers to the plugins surrounding the specified one: { pp, p, plugin, n, nn } which makes things much clearer than tptr, tptr2 etc */ p = plugin->prev; if (p) pp = p->prev; n = plugin->next; if (n) nn = n->next; if (up) { if (!p) return; if (pp) pp->next = plugin; else procinfo->chain = plugin; p->next = n; p->prev = plugin; plugin->prev = pp; plugin->next = p; if (n) n->prev = p; else procinfo->chain_end = p; } else { if (!n) return; if (p) p->next = n; else procinfo->chain = n; n->prev = p; n->next = plugin; plugin->prev = n; plugin->next = nn; if (nn) nn->prev = plugin; else procinfo->chain_end = plugin; } #ifdef WITH_JACK if (procinfo->jack_client && plugin->desc->aux_channels > 0) { vst2_plugin_t *other; other = up ? plugin->next : plugin->prev; /* swap around the jack ports */ if (other->desc->id == plugin->desc->id) vst2_plugin_swap_aux_ports(plugin, other); } #endif } /** exchange an existing plugin for a newly created one */ vst2_plugin_t *vst2_process_change_plugin(vst2_process_info_t *procinfo, vst2_plugin_t *plugin, vst2_plugin_t *new_plugin) { new_plugin->next = plugin->next; new_plugin->prev = plugin->prev; if (plugin->prev) plugin->prev->next = new_plugin; else procinfo->chain = new_plugin; if (plugin->next) plugin->next->prev = new_plugin; else procinfo->chain_end = new_plugin; #ifdef WITH_JACK /* sort out the aux ports */ if (procinfo->jack_client && plugin->desc->aux_channels > 0) { vst2_plugin_t *other; for (other = plugin->next; other; other = other->next) if (other->desc->id == plugin->desc->id) vst2_plugin_swap_aux_ports(plugin, other); } #endif return plugin; } /****************************************** ************* non RT stuff *************** ******************************************/ static int vst2_plugin_open_plugin(vst2_plugin_desc_t *desc, void **dl_handle_ptr, const AEffect **effect_ptr) { /* void * dl_handle; */ /* const char * dlerr; */ //LADSPA_Descriptor_Function get_descriptor; /* clear the error report */ //dlerror (); /* open the object file */ //dl_handle = dlopen (desc->object_file, RTLD_NOW); /* dlerr = dlerror (); if (!dl_handle || dlerr) { if (!dlerr) dlerr = "unknown error"; mlt_log_warning( NULL, "%s: error opening shared object file '%s': %s\n", __FUNCTION__, desc->object_file, dlerr); return 1; } */ /* get the get_descriptor function */ /* get_descriptor = (LADSPA_Descriptor_Function) dlsym (dl_handle, "ladspa_descriptor"); dlerr = dlerror(); if (dlerr) { if (!dlerr) dlerr = "unknown error"; mlt_log_warning( NULL, "%s: error finding descriptor symbol in object file '%s': %s\n", __FUNCTION__, desc->object_file, dlerr); dlclose (dl_handle); return 1; } */ /* #ifdef __APPLE__ if (!get_descriptor (desc->index)) { void (*constructor)(void) = dlsym (dl_handle, "_init"); if (constructor) constructor(); } #endif */ *effect_ptr = desc->effect; if (!*effect_ptr) { mlt_log_warning(NULL, "%s: error finding index %lu in object file '%s'\n", __FUNCTION__, desc->index, desc->object_file); /* dlclose (dl_handle); */ return 1; } /* *dl_handle_ptr = dl_handle; */ return 0; } static int vst2_plugin_instantiate(AEffect *effect, unsigned long vst2_plugin_index, gint copies, AEffect **effects) { gint i; for (i = 0; i < copies; i++) { effects[i] = effect; effect->dispatcher(effect, effSetSampleRate, 0, 0, NULL, (float) vst2_sample_rate); /* if (!effects[i]) { unsigned long d; for (d = 0; d < i; d++) descriptor->cleanup (effects[d]); return 1; } */ } return 0; } #ifdef WITH_JACK static void vst2_plugin_create_aux_ports(vst2_plugin_t *plugin, guint copy, vst2_context_t *vst2_context) { vst2_plugin_desc_t *desc; // vst2_plugin_slot_t * slot; unsigned long aux_channel = 1; unsigned long vst2_plugin_index = 1; unsigned long i; char port_name[64]; char *vst2_plugin_name; char *ptr; // GList * list; vst2_holder_t *holder; desc = plugin->desc; holder = plugin->holders + copy; holder->aux_ports = g_malloc(sizeof(jack_port_t *) * desc->aux_channels); /* make the plugin name jack worthy */ ptr = vst2_plugin_name = g_strndup(plugin->desc->name, 7); while (*ptr != '\0') { if (*ptr == ' ') *ptr = '_'; else *ptr = tolower(*ptr); ptr++; } /* for (list = vst2_context->slots; list; list = g_list_next (list)) { slot = (vst2_plugin_slot_t *) list->data; if (slot->plugin->desc->id == plugin->desc->id) vst2_plugin_index++; } */ for (i = 0; i < desc->aux_channels; i++, aux_channel++) { sprintf(port_name, "%s_%ld-%d_%c%ld", vst2_plugin_name, vst2_plugin_index, copy + 1, desc->aux_are_input ? 'i' : 'o', aux_channel); holder->aux_ports[i] = jack_port_register(vst2_context->procinfo->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, desc->aux_are_input ? JackPortIsInput : JackPortIsOutput, 0); if (!holder->aux_ports[i]) { mlt_log_panic(NULL, "Could not register jack port '%s'; aborting\n", port_name); } } g_free(vst2_plugin_name); } #endif static void vst2_plugin_init_holder(vst2_plugin_t *plugin, guint copy, AEffect *effect, vst2_context_t *vst2_context) { unsigned long i; vst2_plugin_desc_t *desc; vst2_holder_t *holder; desc = plugin->desc; holder = plugin->holders + copy; holder->effect = effect; if (desc->control_port_count > 0) { holder->ui_control_fifos = g_malloc(sizeof(lff_t) * desc->control_port_count); holder->control_memory = g_malloc(sizeof(LADSPA_Data) * desc->control_port_count); } else { holder->ui_control_fifos = NULL; holder->control_memory = NULL; } for (i = 0; i < desc->control_port_count; i++) { lff_init(holder->ui_control_fifos + i, CONTROL_FIFO_SIZE, sizeof(LADSPA_Data)); holder->control_memory[i] = vst2_plugin_desc_get_default_control_value(desc, desc->control_port_indicies[i], vst2_sample_rate); holder->effect->setParameter(holder->effect, desc->control_port_indicies[i] - (holder->effect->numInputs + holder->effect->numOutputs), *(holder->control_memory + i)); } if (desc->status_port_count > 0) { holder->status_memory = g_malloc(sizeof(LADSPA_Data) * desc->status_port_count); } else { holder->status_memory = NULL; } if (holder->control_memory) { for (i = 0; i < desc->status_port_count; i++) { holder->effect->setParameter(holder->effect, desc->control_port_indicies[i] - (holder->effect->numInputs + holder->effect->numOutputs), *(holder->control_memory + i)); } } #ifdef WITH_JACK if (vst2_context->procinfo->jack_client && plugin->desc->aux_channels > 0) vst2_plugin_create_aux_ports(plugin, copy, vst2_context); #endif /* if (plugin->descriptor->activate) plugin->descriptor->activate (effects); */ } vst2_plugin_t *vst2_plugin_new(vst2_plugin_desc_t *desc, vst2_context_t *vst2_context) { void *dl_handle; //const LADSPA_Descriptor * descriptor; const AEffect *effect; AEffect **effects; gint copies; unsigned long i; int err; vst2_plugin_t *plugin; /* open the plugin */ err = vst2_plugin_open_plugin(desc, &dl_handle, &effect); if (err) return NULL; /* create the effects */ copies = vst2_plugin_desc_get_copies(desc, vst2_context->channels); effects = g_malloc(sizeof(AEffect) * copies); err = vst2_plugin_instantiate(desc->effect, desc->index, copies, effects); if (err) { g_free(effects); dlclose(dl_handle); return NULL; } plugin = g_malloc(sizeof(vst2_plugin_t)); plugin->dl_handle = dl_handle; plugin->desc = desc; plugin->copies = copies; plugin->enabled = FALSE; plugin->next = NULL; plugin->prev = NULL; plugin->wet_dry_enabled = FALSE; plugin->vst2_context = vst2_context; /* create audio memory and wet/dry stuff */ plugin->audio_output_memory = g_malloc(sizeof(LADSPA_Data *) * vst2_context->channels); plugin->wet_dry_fifos = g_malloc(sizeof(lff_t) * vst2_context->channels); plugin->wet_dry_values = g_malloc(sizeof(LADSPA_Data) * vst2_context->channels); for (i = 0; i < vst2_context->channels; i++) { plugin->audio_output_memory[i] = g_malloc(sizeof(LADSPA_Data) * vst2_buffer_size); lff_init(plugin->wet_dry_fifos + i, CONTROL_FIFO_SIZE, sizeof(LADSPA_Data)); plugin->wet_dry_values[i] = 1.0; } /* create holders and fill them out */ plugin->holders = g_malloc(sizeof(vst2_holder_t) * copies); for (i = 0; i < copies; i++) vst2_plugin_init_holder(plugin, i, effects[i], vst2_context); return plugin; } void vst2_plugin_destroy(vst2_plugin_t *plugin) { unsigned long i, j; int err; /* destroy holders */ for (i = 0; i < plugin->copies; i++) { if (plugin->desc->control_port_count > 0) { for (j = 0; j < plugin->desc->control_port_count; j++) { lff_free(plugin->holders[i].ui_control_fifos + j); } g_free(plugin->holders[i].ui_control_fifos); g_free(plugin->holders[i].control_memory); } if (plugin->desc->status_port_count > 0) { g_free(plugin->holders[i].status_memory); } #ifdef WITH_JACK /* aux ports */ if (plugin->vst2_context->procinfo->jack_client && plugin->desc->aux_channels > 0) { for (j = 0; j < plugin->desc->aux_channels; j++) { err = jack_port_unregister(plugin->vst2_context->procinfo->jack_client, plugin->holders[i].aux_ports[j]); if (err) mlt_log_warning(NULL, "%s: could not unregister jack port\n", __FUNCTION__); } g_free(plugin->holders[i].aux_ports); } #endif } g_free(plugin->holders); for (i = 0; i < plugin->vst2_context->channels; i++) { g_free(plugin->audio_output_memory[i]); lff_free(plugin->wet_dry_fifos + i); } g_free(plugin->audio_output_memory); g_free(plugin->wet_dry_fifos); g_free(plugin->wet_dry_values); err = dlclose(plugin->dl_handle); if (err) { mlt_log_warning(NULL, "%s: error closing shared object '%s': %s\n", __FUNCTION__, plugin->desc->object_file, dlerror()); } g_free(plugin); } /* EOF */ mlt-7.38.0/src/modules/jackrack/vst2_plugin.h000664 000000 000000 00000005766 15172202314 021035 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JR_VST2_PLUGIN_H__ #define __JR_VST2_PLUGIN_H__ #include #include #include #ifdef WITH_JACK #include #endif #include "lock_free_fifo.h" #include "plugin_desc.h" #include "vst2_process.h" typedef struct _vst2_holder vst2_holder_t; typedef struct _vst2_plugin vst2_plugin_t; struct _vst2_holder { //LADSPA_Handle instance; AEffect *effect; lff_t *ui_control_fifos; LADSPA_Data *control_memory; LADSPA_Data *status_memory; #ifdef WITH_JACK jack_port_t **aux_ports; #endif }; struct _vst2_plugin { vst2_plugin_desc_t *desc; gint enabled; gint copies; vst2_holder_t *holders; LADSPA_Data **audio_input_memory; LADSPA_Data **audio_output_memory; gboolean wet_dry_enabled; /* 1.0 = all wet, 0.0 = all dry, 0.5 = 50% wet/50% dry */ LADSPA_Data *wet_dry_values; lff_t *wet_dry_fifos; vst2_plugin_t *next; vst2_plugin_t *prev; //const LADSPA_Descriptor * descriptor; //const AEffect * effect; void *dl_handle; struct _vst2_context *vst2_context; }; void vst2_process_add_plugin(vst2_process_info_t *, vst2_plugin_t *plugin); vst2_plugin_t *vst2_process_remove_plugin(vst2_process_info_t *, vst2_plugin_t *plugin); void vst2_process_ablise_plugin(vst2_process_info_t *, vst2_plugin_t *plugin, gboolean able); void vst2_process_ablise_plugin_wet_dry(vst2_process_info_t *, vst2_plugin_t *plugin, gboolean enable); void vst2_process_move_plugin(vst2_process_info_t *, vst2_plugin_t *plugin, gint up); vst2_plugin_t *vst2_process_change_plugin(vst2_process_info_t *, vst2_plugin_t *plugin, vst2_plugin_t *new_plugin); struct _vst2_context; struct _ui; vst2_plugin_t *vst2_plugin_new(vst2_plugin_desc_t *plugin_desc, struct _vst2_context *vst2_context); void vst2_plugin_destroy(vst2_plugin_t *plugin); void vst2_plugin_connect_input_ports(vst2_plugin_t *plugin, LADSPA_Data **inputs); void vst2_plugin_connect_output_ports(vst2_plugin_t *plugin); #endif /* __JR_VST2_PLUGIN_H__ */ mlt-7.38.0/src/modules/jackrack/vst2_plugin_settings.c000664 000000 000000 00000026164 15172202314 022743 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include "vst2_plugin_settings.h" static void vst2_settings_set_to_default(vst2_settings_t *settings, guint32 sample_rate) { unsigned long control; guint copy; LADSPA_Data value; for (control = 0; control < settings->desc->control_port_count; control++) { value = vst2_plugin_desc_get_default_control_value(settings->desc, control, sample_rate); for (copy = 0; copy < settings->copies; copy++) { settings->control_values[copy][control] = value; } settings->locks[control] = TRUE; } } vst2_settings_t *vst2_settings_new(vst2_plugin_desc_t *desc, unsigned long channels, guint32 sample_rate) { vst2_settings_t *settings; unsigned long channel; guint copies; settings = g_malloc(sizeof(vst2_settings_t)); copies = vst2_plugin_desc_get_copies(desc, channels); settings->sample_rate = sample_rate; settings->desc = desc; settings->copies = copies; settings->channels = channels; settings->lock_all = TRUE; settings->enabled = FALSE; settings->locks = NULL; settings->control_values = NULL; settings->wet_dry_enabled = FALSE; settings->wet_dry_locked = TRUE; /* control settings */ if (desc->control_port_count > 0) { guint copy; settings->locks = g_malloc(sizeof(gboolean) * desc->control_port_count); settings->control_values = g_malloc(sizeof(LADSPA_Data *) * copies); for (copy = 0; copy < copies; copy++) { settings->control_values[copy] = g_malloc(sizeof(LADSPA_Data) * desc->control_port_count); } vst2_settings_set_to_default(settings, sample_rate); } /* wet/dry settings */ settings->wet_dry_values = g_malloc(sizeof(LADSPA_Data) * channels); for (channel = 0; channel < channels; channel++) settings->wet_dry_values[channel] = 1.0; return settings; } vst2_settings_t *vst2_settings_dup(vst2_settings_t *other) { vst2_settings_t *settings; vst2_plugin_desc_t *desc; unsigned long channel; settings = g_malloc(sizeof(vst2_settings_t)); settings->sample_rate = other->sample_rate; settings->desc = other->desc; settings->copies = vst2_settings_get_copies(other); settings->channels = vst2_settings_get_channels(other); settings->wet_dry_enabled = vst2_settings_get_wet_dry_enabled(other); settings->wet_dry_locked = vst2_settings_get_wet_dry_locked(other); settings->lock_all = vst2_settings_get_lock_all(other); settings->enabled = vst2_settings_get_enabled(other); settings->locks = NULL; settings->control_values = NULL; desc = other->desc; if (desc->control_port_count > 0) { guint copy; unsigned long control; settings->locks = g_malloc(sizeof(gboolean) * desc->control_port_count); for (control = 0; control < desc->control_port_count; control++) vst2_settings_set_lock(settings, control, vst2_settings_get_lock(other, control)); settings->control_values = g_malloc(sizeof(LADSPA_Data *) * settings->copies); for (copy = 0; copy < settings->copies; copy++) { settings->control_values[copy] = g_malloc(sizeof(LADSPA_Data) * desc->control_port_count); for (control = 0; control < desc->control_port_count; control++) { settings->control_values[copy][control] = vst2_settings_get_control_value(other, copy, control); } } } settings->wet_dry_values = g_malloc(sizeof(LADSPA_Data) * settings->channels); for (channel = 0; channel < settings->channels; channel++) settings->wet_dry_values[channel] = vst2_settings_get_wet_dry_value(other, channel); return settings; } void vst2_settings_destroy(vst2_settings_t *settings) { if (settings->desc->control_port_count > 0) { guint i; for (i = 0; i < settings->copies; i++) g_free(settings->control_values[i]); g_free(settings->control_values); g_free(settings->locks); } g_free(settings->wet_dry_values); g_free(settings); } static void vst2_settings_set_copies(vst2_settings_t *settings, guint copies) { guint copy; guint last_copy; unsigned long control; if (copies <= settings->copies) return; last_copy = settings->copies - 1; settings->control_values = g_realloc(settings->control_values, sizeof(LADSPA_Data *) * copies); /* copy over the last settings to the new copies */ for (copy = settings->copies; copy < copies; copy++) { for (control = 0; control < settings->desc->control_port_count; control++) { settings->control_values[copy][control] = settings->control_values[last_copy][control]; } } settings->copies = copies; } static void vst2_settings_set_channels(vst2_settings_t *settings, unsigned long channels) { unsigned long channel; LADSPA_Data last_value; if (channels <= settings->channels) return; settings->wet_dry_values = g_realloc(settings->wet_dry_values, sizeof(LADSPA_Data) * channels); last_value = settings->wet_dry_values[settings->channels - 1]; for (channel = settings->channels; channel < channels; channel++) settings->wet_dry_values[channel] = last_value; settings->channels = channels; } void vst2_settings_set_sample_rate(vst2_settings_t *settings, guint32 sample_rate) { LADSPA_Data old_sample_rate; LADSPA_Data new_sample_rate; g_return_if_fail(settings != NULL); if (settings->sample_rate == sample_rate) return; if (settings->desc->control_port_count > 0) { unsigned long control; guint copy; new_sample_rate = (LADSPA_Data) sample_rate; old_sample_rate = (LADSPA_Data) settings->sample_rate; for (control = 0; control < settings->desc->control_port_count; control++) { for (copy = 0; copy < settings->copies; copy++) { if (LADSPA_IS_HINT_SAMPLE_RATE( settings->desc->port_range_hints[control].HintDescriptor)) { settings->control_values[copy][control] = (settings->control_values[copy][control] / old_sample_rate) * new_sample_rate; } } } } settings->sample_rate = sample_rate; } void vst2_settings_set_control_value(vst2_settings_t *settings, guint copy, unsigned long control_index, LADSPA_Data value) { g_return_if_fail(settings != NULL); g_return_if_fail(control_index < settings->desc->control_port_count); if (copy >= settings->copies) vst2_settings_set_copies(settings, copy + 1); settings->control_values[copy][control_index] = value; } void vst2_settings_set_lock(vst2_settings_t *settings, unsigned long control_index, gboolean locked) { g_return_if_fail(settings != NULL); g_return_if_fail(control_index < settings->desc->control_port_count); settings->locks[control_index] = locked; } void vst2_settings_set_lock_all(vst2_settings_t *settings, gboolean lock_all) { g_return_if_fail(settings != NULL); settings->lock_all = lock_all; } void vst2_settings_set_enabled(vst2_settings_t *settings, gboolean enabled) { g_return_if_fail(settings != NULL); settings->enabled = enabled; } void vst2_settings_set_wet_dry_enabled(vst2_settings_t *settings, gboolean enabled) { g_return_if_fail(settings != NULL); settings->wet_dry_enabled = enabled; } void vst2_settings_set_wet_dry_locked(vst2_settings_t *settings, gboolean locked) { g_return_if_fail(settings != NULL); settings->wet_dry_locked = locked; } void vst2_settings_set_wet_dry_value(vst2_settings_t *settings, unsigned long channel, LADSPA_Data value) { g_return_if_fail(settings != NULL); if (channel >= settings->channels) vst2_settings_set_channels(settings, channel + 1); settings->wet_dry_values[channel] = value; } LADSPA_Data vst2_settings_get_control_value(vst2_settings_t *settings, guint copy, unsigned long control_index) { g_return_val_if_fail(settings != NULL, NAN); g_return_val_if_fail(control_index < settings->desc->control_port_count, NAN); if (copy >= settings->copies) vst2_settings_set_copies(settings, copy - 1); return settings->control_values[copy][control_index]; } gboolean vst2_settings_get_lock(const vst2_settings_t *settings, unsigned long control_index) { g_return_val_if_fail(settings != NULL, FALSE); return settings->locks[control_index]; } gboolean vst2_settings_get_lock_all(const vst2_settings_t *settings) { g_return_val_if_fail(settings != NULL, FALSE); return settings->lock_all; } gboolean vst2_settings_get_enabled(const vst2_settings_t *settings) { g_return_val_if_fail(settings != NULL, FALSE); return settings->enabled; } guint vst2_settings_get_copies(const vst2_settings_t *settings) { g_return_val_if_fail(settings != NULL, 0); return settings->copies; } unsigned long vst2_settings_get_channels(const vst2_settings_t *settings) { g_return_val_if_fail(settings != NULL, 0); return settings->channels; } gboolean vst2_settings_get_wet_dry_enabled(const vst2_settings_t *settings) { g_return_val_if_fail(settings != NULL, FALSE); return settings->wet_dry_enabled; } gboolean vst2_settings_get_wet_dry_locked(const vst2_settings_t *settings) { g_return_val_if_fail(settings != NULL, FALSE); return settings->wet_dry_locked; } LADSPA_Data vst2_settings_get_wet_dry_value(vst2_settings_t *settings, unsigned long channel) { g_return_val_if_fail(settings != NULL, NAN); if (channel >= settings->channels) vst2_settings_set_channels(settings, channel + 1); return settings->wet_dry_values[channel]; } /* EOF */ mlt-7.38.0/src/modules/jackrack/vst2_plugin_settings.h000664 000000 000000 00000006621 15172202314 022744 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JR_VST2_PLUGIN_SETTINGS_H__ #define __JR_VST2_PLUGIN_SETTINGS_H__ #include #include #include "plugin_desc.h" typedef struct _vst2_settings vst2_settings_t; struct _vst2_settings { guint32 sample_rate; vst2_plugin_desc_t *desc; guint copies; LADSPA_Data **control_values; gboolean *locks; gboolean lock_all; gboolean enabled; unsigned long channels; gboolean wet_dry_enabled; gboolean wet_dry_locked; LADSPA_Data *wet_dry_values; }; vst2_settings_t *vst2_settings_new(vst2_plugin_desc_t *desc, unsigned long channels, guint32 sample_rate); vst2_settings_t *vst2_settings_dup(vst2_settings_t *settings); void vst2_settings_destroy(vst2_settings_t *settings); void vst2_settings_set_control_value(vst2_settings_t *settings, guint copy, unsigned long control_index, LADSPA_Data value); void vst2_settings_set_lock(vst2_settings_t *settings, unsigned long control_index, gboolean locked); void vst2_settings_set_lock_all(vst2_settings_t *settings, gboolean lock_all); void vst2_settings_set_enabled(vst2_settings_t *settings, gboolean enabled); void vst2_settings_set_wet_dry_enabled(vst2_settings_t *settings, gboolean enabled); void vst2_settings_set_wet_dry_locked(vst2_settings_t *settings, gboolean locked); void vst2_settings_set_wet_dry_value(vst2_settings_t *settings, unsigned long channel, LADSPA_Data value); LADSPA_Data vst2_settings_get_control_value(vst2_settings_t *settings, guint copy, unsigned long control_index); gboolean vst2_settings_get_lock(const vst2_settings_t *settings, unsigned long control_index); gboolean vst2_settings_get_lock_all(const vst2_settings_t *settings); gboolean vst2_settings_get_enabled(const vst2_settings_t *settings); guint vst2_settings_get_copies(const vst2_settings_t *settings); unsigned long vst2_settings_get_channels(const vst2_settings_t *settings); gboolean vst2_settings_get_wet_dry_enabled(const vst2_settings_t *settings); gboolean vst2_settings_get_wet_dry_locked(const vst2_settings_t *settings); LADSPA_Data vst2_settings_get_wet_dry_value(vst2_settings_t *settings, unsigned long channel); void vst2_settings_set_sample_rate(vst2_settings_t *settings, guint32 sample_rate); #endif /* __JR_VST2_PLUGIN_SETTINGS_H__ */ mlt-7.38.0/src/modules/jackrack/vst2_process.c000664 000000 000000 00000054257 15172202314 021207 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #ifdef WITH_JACK #include #endif #include #include #include #include #include #include #include #include "framework/mlt_log.h" #include "lock_free_fifo.h" #include "vst2_plugin.h" #include "vst2_process.h" #ifndef _ #define _(x) x #endif extern pthread_mutex_t g_activate_mutex; #define USEC_PER_SEC 1000000 #define MSEC_PER_SEC 1000 #define TIME_RUN_SKIP_COUNT 5 #define MAX_BUFFER_SIZE 4096 jack_nframes_t vst2_sample_rate; jack_nframes_t vst2_buffer_size; /* #ifdef WITH_JACK static void jack_shutdown_cb (void * data) { vst2_process_info_t * procinfo = data; procinfo->quit = TRUE; } #endif */ /** process messages for plugins' control ports */ void vst2_process_control_port_messages(vst2_process_info_t *procinfo) { vst2_plugin_t *plugin; unsigned long control; unsigned long channel; gint copy; if (!procinfo->chain) return; for (plugin = procinfo->chain; plugin; plugin = plugin->next) { if (plugin->desc->control_port_count > 0) for (control = 0; control < plugin->desc->control_port_count; control++) for (copy = 0; copy < plugin->copies; copy++) { while (lff_read(plugin->holders[copy].ui_control_fifos + control, plugin->holders[copy].control_memory + control) == 0) ; } if (plugin->wet_dry_enabled) for (channel = 0; channel < procinfo->channels; channel++) { while (lff_read(plugin->wet_dry_fifos + channel, plugin->wet_dry_values + channel) == 0) ; } } } #ifdef WITH_JACK static int get_jack_buffers(vst2_process_info_t *procinfo, jack_nframes_t frames) { unsigned long channel; for (channel = 0; channel < procinfo->channels; channel++) { procinfo->jack_input_buffers[channel] = jack_port_get_buffer(procinfo->jack_input_ports[channel], frames); if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose(NULL, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel); return 1; } procinfo->jack_output_buffers[channel] = jack_port_get_buffer(procinfo->jack_output_ports[channel], frames); if (!procinfo->jack_output_buffers[channel]) { mlt_log_verbose(NULL, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel); return 1; } } return 0; } #endif vst2_plugin_t *vst2_get_first_enabled_plugin(vst2_process_info_t *procinfo) { vst2_plugin_t *first_enabled; if (!procinfo->chain) return NULL; for (first_enabled = procinfo->chain; first_enabled; first_enabled = first_enabled->next) { if (first_enabled->enabled) return first_enabled; } return NULL; } vst2_plugin_t *vst2_get_last_enabled_plugin(vst2_process_info_t *procinfo) { vst2_plugin_t *last_enabled; if (!procinfo->chain) return NULL; for (last_enabled = procinfo->chain_end; last_enabled; last_enabled = last_enabled->prev) { if (last_enabled->enabled) return last_enabled; } return NULL; } void vst2_connect_chain(vst2_process_info_t *procinfo, jack_nframes_t frames) { vst2_plugin_t *first_enabled, *last_enabled, *plugin; gint copy; unsigned long channel; if (!procinfo->chain) return; first_enabled = vst2_get_first_enabled_plugin(procinfo); if (!first_enabled) return; last_enabled = vst2_get_last_enabled_plugin(procinfo); /* sort out the aux ports */ plugin = first_enabled; do { if (plugin->desc->aux_channels > 0 && plugin->enabled) { #ifdef WITH_JACK if (procinfo->jack_client) { for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) { plugin->holders[copy].effect->setParameter( plugin->holders[copy].effect, plugin->desc->audio_aux_port_indicies[channel] - (plugin->holders[copy].effect->numInputs + plugin->holders[copy].effect->numOutputs), *((LADSPA_Data *) jack_port_get_buffer(plugin->holders[copy].aux_ports[channel], frames))); } } else #endif { for (copy = 0; copy < frames; copy++) procinfo->silent_buffer[copy] = 0.0; for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) { plugin->holders[copy] .effect->setParameter(plugin->holders[copy].effect, plugin->desc->audio_aux_port_indicies[channel] - (plugin->holders[copy].effect->numInputs + plugin->holders[copy].effect->numOutputs), *(procinfo->silent_buffer)); } } } } while ((plugin != last_enabled) && (plugin = plugin->next)); /* ensure that all the of the enabled plugins are connected to their memory */ vst2_plugin_connect_output_ports(first_enabled); if (first_enabled != last_enabled) { vst2_plugin_connect_input_ports(last_enabled, last_enabled->prev->audio_output_memory); for (plugin = first_enabled->next; plugin; plugin = plugin->next) { if (plugin->enabled) { vst2_plugin_connect_input_ports(plugin, plugin->prev->audio_output_memory); vst2_plugin_connect_output_ports(plugin); } } } /* input buffers for first plugin */ if (plugin && plugin->desc->has_input) vst2_plugin_connect_input_ports(first_enabled, procinfo->jack_input_buffers); } void vst2_process_chain(vst2_process_info_t *procinfo, jack_nframes_t frames) { vst2_plugin_t *first_enabled; vst2_plugin_t *last_enabled = NULL; vst2_plugin_t *plugin; unsigned long channel; unsigned long i; #ifdef WITH_JACK if (procinfo->jack_client) { LADSPA_Data zero_signal[frames]; guint copy; /* set the zero signal to zero */ for (channel = 0; channel < frames; channel++) zero_signal[channel] = 0.0; /* possibly set aux output channels to zero if they're not enabled */ for (plugin = procinfo->chain; plugin; plugin = plugin->next) if (!plugin->enabled && plugin->desc->aux_channels > 0 && !plugin->desc->aux_are_input) for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) memcpy(jack_port_get_buffer(plugin->holders[copy].aux_ports[channel], frames), zero_signal, sizeof(LADSPA_Data) * frames); } #endif first_enabled = vst2_get_first_enabled_plugin(procinfo); /* no chain; just copy input to output */ if (!procinfo->chain || !first_enabled) { unsigned long channel; for (channel = 0; channel < procinfo->channels; channel++) { memcpy(procinfo->jack_output_buffers[channel], procinfo->jack_input_buffers[channel], sizeof(LADSPA_Data) * frames); } return; } /* all past here is guaranteed to have at least 1 enabled plugin */ last_enabled = vst2_get_last_enabled_plugin(procinfo); for (plugin = first_enabled; plugin; plugin = plugin->next) { if (plugin->enabled) { for (i = 0; i < plugin->copies; i++) { if (plugin->holders[i].effect->processReplacing != NULL) { plugin->holders[i].effect->processReplacing(plugin->holders[i].effect, plugin->audio_input_memory, plugin->audio_output_memory, frames); } else { plugin->holders[i] .effect->process(plugin->holders[i].effect, plugin->audio_input_memory, plugin->audio_output_memory, frames); // deprecated only few plugins support it } } if (plugin->wet_dry_enabled) for (channel = 0; channel < procinfo->channels; channel++) { if (procinfo->channel_mask & (1 << channel)) for (i = 0; i < frames; i++) { plugin->audio_output_memory[channel][i] *= plugin->wet_dry_values[channel]; plugin->audio_output_memory[channel][i] += plugin->audio_input_memory[channel][i] * (1.0 - plugin->wet_dry_values[channel]); } else memcpy(plugin->audio_output_memory[channel], plugin->audio_input_memory[channel], sizeof(LADSPA_Data) * frames); } if (plugin == last_enabled) break; } else { /* copy the data through */ for (i = 0; i < procinfo->channels; i++) memcpy(plugin->audio_output_memory[i], plugin->prev->audio_output_memory[i], sizeof(LADSPA_Data) * frames); } } /* copy the last enabled data to the jack ports */ for (i = 0; i < procinfo->channels; i++) memcpy(procinfo->jack_output_buffers[i], last_enabled->audio_output_memory[i], sizeof(LADSPA_Data) * frames); } int process_vst2(vst2_process_info_t *procinfo, jack_nframes_t frames, LADSPA_Data **inputs, LADSPA_Data **outputs) { unsigned long channel; if (!procinfo) { mlt_log_error(NULL, "%s: no vst2_process_info from jack!\n", __FUNCTION__); return 1; } if (procinfo->quit == TRUE) return 1; vst2_process_control_port_messages(procinfo); for (channel = 0; channel < procinfo->channels; channel++) { if (vst2_get_first_enabled_plugin(procinfo)->desc->has_input) { procinfo->jack_input_buffers[channel] = inputs[channel]; if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose(NULL, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel); return 1; } } procinfo->jack_output_buffers[channel] = outputs[channel]; if (!procinfo->jack_output_buffers[channel]) { mlt_log_verbose(NULL, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel); return 1; } } vst2_connect_chain(procinfo, frames); vst2_process_chain(procinfo, frames); return 0; } #ifdef WITH_JACK int vst2_process_jack(jack_nframes_t frames, void *data) { int err; vst2_process_info_t *procinfo; procinfo = (vst2_process_info_t *) data; if (!procinfo) { mlt_log_error(NULL, "%s: no vst2_process_info from jack!\n", __FUNCTION__); return 1; } if (procinfo->port_count == 0) return 0; if (procinfo->quit == TRUE) return 1; vst2_process_control_port_messages(procinfo); err = get_jack_buffers(procinfo, frames); if (err) { mlt_log_warning(NULL, "%s: failed to get jack ports, not processing\n", __FUNCTION__); return 0; } vst2_connect_chain(procinfo, frames); vst2_process_chain(procinfo, frames); return 0; } /******************************************* ************** non RT stuff *************** *******************************************/ /* static int vst2_process_info_connect_jack (vst2_process_info_t * procinfo) { mlt_log_info( NULL, _("Connecting to JACK server with client name '%s'\n"), procinfo->jack_client_name); procinfo->jack_client = jack_client_open (procinfo->jack_client_name, JackNullOption, NULL); if (!procinfo->jack_client) { mlt_log_warning( NULL, "%s: could not create jack client; is the jackd server running?\n", __FUNCTION__); return 1; } mlt_log_verbose( NULL, _("Connected to JACK server\n")); jack_set_vst2_process_callback (procinfo->jack_client, vst2_process_jack, procinfo); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); return 0; } */ static void vst2_process_info_connect_port(vst2_process_info_t *procinfo, gshort in, unsigned long port_index, const char *port_name) { const char **jack_ports; unsigned long jack_port_index; int err; char *full_port_name; jack_ports = jack_get_ports(procinfo->jack_client, NULL, NULL, JackPortIsPhysical | (in ? JackPortIsOutput : JackPortIsInput)); if (!jack_ports) return; for (jack_port_index = 0; jack_ports[jack_port_index] && jack_port_index <= port_index; jack_port_index++) { if (jack_port_index != port_index) continue; full_port_name = g_strdup_printf("%s:%s", procinfo->jack_client_name, port_name); mlt_log_debug(NULL, _("Connecting ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]); err = jack_connect(procinfo->jack_client, in ? jack_ports[jack_port_index] : full_port_name, in ? full_port_name : jack_ports[jack_port_index]); if (err) mlt_log_warning(NULL, "%s: error connecting ports '%s' and '%s'\n", __FUNCTION__, full_port_name, jack_ports[jack_port_index]); else mlt_log_info(NULL, _("Connected ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]); free(full_port_name); } free(jack_ports); } static int vst2_process_info_set_port_count(vst2_process_info_t *procinfo, unsigned long port_count, gboolean connect_inputs, gboolean connect_outputs) { unsigned long i; char *port_name; jack_port_t **port_ptr; gshort in; if (procinfo->port_count >= port_count) return -1; if (procinfo->port_count == 0) { procinfo->jack_input_ports = g_malloc(sizeof(jack_port_t *) * port_count); procinfo->jack_output_ports = g_malloc(sizeof(jack_port_t *) * port_count); procinfo->jack_input_buffers = g_malloc(sizeof(LADSPA_Data *) * port_count); procinfo->jack_output_buffers = g_malloc(sizeof(LADSPA_Data *) * port_count); } else { procinfo->jack_input_ports = g_realloc(procinfo->jack_input_ports, sizeof(jack_port_t *) * port_count); procinfo->jack_output_ports = g_realloc(procinfo->jack_output_ports, sizeof(jack_port_t *) * port_count); procinfo->jack_input_buffers = g_realloc(procinfo->jack_input_buffers, sizeof(LADSPA_Data *) * port_count); procinfo->jack_output_buffers = g_realloc(procinfo->jack_output_buffers, sizeof(LADSPA_Data *) * port_count); } for (i = procinfo->port_count; i < port_count; i++) { for (in = 0; in < 2; in++) { port_name = g_strdup_printf("%s_%ld", in ? "in" : "out", i + 1); //mlt_log_debug( NULL, _("Creating %s port %s\n"), in ? "input" : "output", port_name); port_ptr = (in ? &procinfo->jack_input_ports[i] : &procinfo->jack_output_ports[i]); *port_ptr = jack_port_register(procinfo->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, in ? JackPortIsInput : JackPortIsOutput, 0); if (!*port_ptr) { mlt_log_error(NULL, "%s: could not register port '%s'; aborting\n", __FUNCTION__, port_name); return 1; } //mlt_log_debug( NULL, _("Created %s port %s\n"), in ? "input" : "output", port_name); if ((in && connect_inputs) || (!in && connect_outputs)) vst2_process_info_connect_port(procinfo, in, i, port_name); g_free(port_name); } } procinfo->port_count = port_count; return 0; } #endif void vst2_process_info_set_channels(vst2_process_info_t *procinfo, unsigned long channels, gboolean connect_inputs, gboolean connect_outputs) { #ifdef WITH_JACK vst2_process_info_set_port_count(procinfo, channels, connect_inputs, connect_outputs); #endif procinfo->channels = channels; } vst2_process_info_t *vst2_process_info_new(const char *client_name, unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs) { vst2_process_info_t *procinfo; char *jack_client_name; int err; procinfo = g_malloc(sizeof(vst2_process_info_t)); procinfo->chain = NULL; procinfo->chain_end = NULL; #ifdef WITH_JACK procinfo->jack_client = NULL; procinfo->port_count = 0; procinfo->jack_input_ports = NULL; procinfo->jack_output_ports = NULL; #endif procinfo->channels = rack_channels; procinfo->channel_mask = 0xFFFFFFFF; procinfo->quit = FALSE; if (client_name == NULL) { vst2_sample_rate = 48000; // should be set externally before calling process_vst2 vst2_buffer_size = MAX_BUFFER_SIZE; procinfo->silent_buffer = g_malloc(sizeof(LADSPA_Data) * vst2_buffer_size); procinfo->jack_input_buffers = g_malloc(sizeof(LADSPA_Data *) * rack_channels); procinfo->jack_output_buffers = g_malloc(sizeof(LADSPA_Data *) * rack_channels); return procinfo; } /* sort out the client name */ procinfo->jack_client_name = jack_client_name = strdup(client_name); for (err = 0; jack_client_name[err] != '\0'; err++) { if (jack_client_name[err] == ' ') jack_client_name[err] = '_'; else if (!isalnum( jack_client_name [err])) { /* shift all the chars up one (to remove the non-alphanumeric char) */ int i; for (i = err; jack_client_name[i] != '\0'; i++) jack_client_name[i] = jack_client_name[i + 1]; } else if (isupper(jack_client_name[err])) jack_client_name[err] = tolower(jack_client_name[err]); } /* #ifdef WITH_JACK err = vst2_process_info_connect_jack (procinfo); if (err) { /\* g_free (procinfo); *\/ return NULL; /\* abort (); *\/ } vst2_sample_rate = jack_get_sample_rate (procinfo->jack_client); vst2_buffer_size = jack_get_sample_rate (procinfo->jack_client); jack_set_vst2_process_callback (procinfo->jack_client, vst2_process_jack, procinfo); pthread_mutex_lock( &g_activate_mutex ); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); pthread_mutex_unlock( &g_activate_mutex ); jack_activate (procinfo->jack_client); err = vst2_process_info_set_port_count (procinfo, rack_channels, connect_inputs, connect_outputs); if (err) return NULL; #endif */ return procinfo; } void vst2_process_info_destroy(vst2_process_info_t *procinfo) { #ifdef WITH_JACK if (procinfo->jack_client) { jack_deactivate(procinfo->jack_client); jack_client_close(procinfo->jack_client); } g_free(procinfo->jack_input_ports); g_free(procinfo->jack_output_ports); #endif g_free(procinfo->jack_input_buffers); g_free(procinfo->jack_output_buffers); g_free(procinfo->silent_buffer); g_free(procinfo); } void vst2_process_quit(vst2_process_info_t *procinfo) { procinfo->quit = TRUE; } mlt-7.38.0/src/modules/jackrack/vst2_process.h000664 000000 000000 00000005341 15172202314 021202 0ustar00rootroot000000 000000 /* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2024-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JLH_VST2_PROCESS_H__ #define __JLH_VST2_PROCESS_H__ #include #ifdef WITH_JACK #include #endif #include typedef struct _vst2_process_info vst2_process_info_t; /** this is what gets passed to the process() callback and contains all the data the process callback will need */ struct _vst2_process_info { /** the plugin instance chain */ struct _vst2_plugin *chain; struct _vst2_plugin *chain_end; #ifdef WITH_JACK jack_client_t *jack_client; unsigned long port_count; jack_port_t **jack_input_ports; jack_port_t **jack_output_ports; #endif unsigned long channels; unsigned long channel_mask; LADSPA_Data **jack_input_buffers; LADSPA_Data **jack_output_buffers; LADSPA_Data *silent_buffer; char *jack_client_name; int quit; }; #ifndef WITH_JACK typedef guint32 jack_nframes_t; #endif extern jack_nframes_t vst2_sample_rate; extern jack_nframes_t vst2_buffer_size; vst2_process_info_t *vst2_process_info_new(const char *client_name, unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs); void vst2_process_info_destroy(vst2_process_info_t *procinfo); void vst2_process_info_set_channels(vst2_process_info_t *procinfo, unsigned long channels, gboolean connect_inputs, gboolean connect_outputs); int process_vst2(vst2_process_info_t *procinfo, jack_nframes_t frames, LADSPA_Data **inputs, LADSPA_Data **outputs); #ifdef WITH_JACK int vst2_process_jack(jack_nframes_t frames, void *data); #endif void vst2_process_quit(vst2_process_info_t *procinfo); #endif /* __JLH_VST2_PROCESS_H__ */ mlt-7.38.0/src/modules/kdenlive/000775 000000 000000 00000000000 15172202314 016422 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/kdenlive/CMakeLists.txt000664 000000 000000 00000001643 15172202314 021166 0ustar00rootroot000000 000000 add_library(mltkdenlive MODULE factory.c filter_boxblur.c filter_freeze.c filter_wave.c producer_framebuffer.c ) file(GLOB YML "*.yml") add_custom_target(Other_kdenlive_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltkdenlive) target_compile_options(mltkdenlive PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltkdenlive PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltkdenlive PRIVATE mlt) if(MSVC) target_link_libraries(mltkdenlive PRIVATE PThreads4W::PThreads4W) else() target_link_libraries(mltkdenlive PRIVATE m) endif() set_target_properties(mltkdenlive PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltkdenlive LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_boxblur.yml filter_freeze.yml filter_wave.yml producer_framebuffer.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/kdenlive ) mlt-7.38.0/src/modules/kdenlive/factory.c000664 000000 000000 00000005535 15172202314 020245 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2007 Jean-Baptiste Mardelle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltkdenlive_export.h" #include #include #include extern mlt_filter filter_boxblur_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_freeze_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_wave_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_framebuffer_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/kdenlive/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTKDENLIVE_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "boxblur", filter_boxblur_init); MLT_REGISTER(mlt_service_filter_type, "freeze", filter_freeze_init); MLT_REGISTER(mlt_service_filter_type, "wave", filter_wave_init); MLT_REGISTER(mlt_service_producer_type, "framebuffer", producer_framebuffer_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "boxblur", metadata, "filter_boxblur.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "freeze", metadata, "filter_freeze.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "wave", metadata, "filter_wave.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "framebuffer", metadata, "producer_framebuffer.yml"); } mlt-7.38.0/src/modules/kdenlive/filter_boxblur.c000664 000000 000000 00000016122 15172202314 021612 0ustar00rootroot000000 000000 /* * filter_boxblur.c -- blur filter * Copyright (C) ?-2007 Leny Grisel * Copyright (C) 2007 Jean-Baptiste Mardelle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include static void PreCompute(uint8_t *image, int32_t *rgba, int width, int height) { register int x, y, z; int32_t pts[4]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pts[0] = image[0]; pts[1] = image[1]; pts[2] = image[2]; pts[3] = image[3]; for (z = 0; z < 4; z++) { if (x > 0) pts[z] += rgba[-4]; if (y > 0) pts[z] += rgba[width * -4]; if (x > 0 && y > 0) pts[z] -= rgba[(width + 1) * -4]; *rgba++ = pts[z]; } image += 4; } } } static inline int32_t GetRGBA(int32_t *rgba, unsigned int w, unsigned int h, unsigned int x, int offsetx, unsigned int y, int offsety, unsigned int z) { int xtheo = x + offsetx; int ytheo = y + offsety; return rgba[4 * (CLAMP(xtheo, 0, w - 1) + CLAMP(ytheo, 0, h - 1) * w) + z]; } static void DoBoxBlur(uint8_t *image, int32_t *rgba, unsigned int width, unsigned int height, unsigned int boxw, unsigned int boxh) { register int x, y; float mul = 1.f / ((boxw * 2) * (boxh * 2)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { *image++ = (GetRGBA(rgba, width, height, x, +boxw, y, +boxh, 0) + GetRGBA(rgba, width, height, x, -boxw, y, -boxh, 0) - GetRGBA(rgba, width, height, x, -boxw, y, +boxh, 0) - GetRGBA(rgba, width, height, x, +boxw, y, -boxh, 0)) * mul; *image++ = (GetRGBA(rgba, width, height, x, +boxw, y, +boxh, 1) + GetRGBA(rgba, width, height, x, -boxw, y, -boxh, 1) - GetRGBA(rgba, width, height, x, -boxw, y, +boxh, 1) - GetRGBA(rgba, width, height, x, +boxw, y, -boxh, 1)) * mul; *image++ = (GetRGBA(rgba, width, height, x, +boxw, y, +boxh, 2) + GetRGBA(rgba, width, height, x, -boxw, y, -boxh, 2) - GetRGBA(rgba, width, height, x, -boxw, y, +boxh, 2) - GetRGBA(rgba, width, height, x, +boxw, y, -boxh, 2)) * mul; *image++ = (GetRGBA(rgba, width, height, x, +boxw, y, +boxh, 3) + GetRGBA(rgba, width, height, x, -boxw, y, -boxh, 3) - GetRGBA(rgba, width, height, x, -boxw, y, +boxh, 3) - GetRGBA(rgba, width, height, x, +boxw, y, -boxh, 3)) * mul; } } } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); unsigned int boxw = 0; unsigned int boxh = 0; mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); double hori = mlt_properties_anim_get_double(properties, "hori", position, length); double vert = mlt_properties_anim_get_double(properties, "vert", position, length); // Get blur factor double factor = mlt_properties_get_int(properties, "start"); if (mlt_properties_get(properties, "end")) { double end = (double) mlt_properties_get_int(properties, "end"); factor += (end - factor) * mlt_filter_get_progress(filter, frame); } // If animated property "blur" is set, use its value. char *blur_property = mlt_properties_get(properties, "blur"); if (blur_property) { factor = mlt_properties_anim_get_double(properties, "blur", position, length); } boxw = (unsigned int) (factor * hori); boxh = (unsigned int) (factor * vert); if (boxw == 0 && boxh == 0) { // Don't do anything error = mlt_frame_get_image(frame, image, format, width, height, writable); } else { // Get the image *format = mlt_image_rgba; int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Only process if we have no error and a valid colour space if (error == 0) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); boxw *= mlt_profile_scale_width(profile, *width); boxh *= mlt_profile_scale_height(profile, *height); if (boxw || boxh) { int size = mlt_image_format_size(*format, *width, *height, NULL); int32_t *rgba = mlt_pool_alloc(4 * size); PreCompute(*image, rgba, *width, *height); DoBoxBlur(*image, rgba, *width, *height, MAX(1, boxw), MAX(1, boxh)); mlt_pool_release(rgba); } } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_boxblur_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "start", arg == NULL ? "2" : arg); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "hori", "1"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "vert", "1"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "blur", NULL); } return filter; } mlt-7.38.0/src/modules/kdenlive/filter_boxblur.yml000664 000000 000000 00000003105 15172202314 022166 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: boxblur title: Box Blur (*DEPRECATED*) version: 3 copyright: Leny Grisel, Jean-Baptiste Mardelle creator: Leny Grisel, Jean-Baptiste Mardelle license: LGPLv2.1 language: en notes: > This filter is deprecated and will eventually be removed. Use the box_blur filter instead. tags: - Video parameters: - identifier: start (*DEPRECATED*) title: Size argument: yes type: integer description: > If an end size is provided, then this is the starting size, and size is interpolated over the duration of the filter from start to end size. If the keyframable property "blur" is provided, then this is ignored, and "blur" is used instead. This parameter also affects the size both horizontally and vertically simultaneously, in amounts proportional to the horizontal size and vertical size parameters. unit: pixels mutable: yes default: 2 minimum: 1 - identifier: end (*DEPRECATED*) title: End size type: integer unit: pixels mutable: yes minimum: 1 - identifier: blur title: Size type: integer description: > If this value is set the start and end parameters are ignored. unit: pixels mutable: yes animation: yes minimum: 1 - identifier: hori title: Horizontal size type: integer unit: pixels mutable: yes animation: yes minimum: 0 default: 1 - identifier: vert title: Vertical size type: integer unit: pixels mutable: yes animation: yes minimum: 0 default: 1 mlt-7.38.0/src/modules/kdenlive/filter_freeze.c000664 000000 000000 00000014153 15172202314 021417 0ustar00rootroot000000 000000 /* * filter_freeze.c -- simple frame freezing filter * Copyright (C) 2007 Jean-Baptiste Mardelle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the image mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties props = MLT_FRAME_PROPERTIES(frame); mlt_frame freeze_frame = NULL; ; int freeze_before = mlt_properties_get_int(properties, "freeze_before"); int freeze_after = mlt_properties_get_int(properties, "freeze_after"); mlt_position pos = mlt_properties_get_position(properties, "frame") + mlt_producer_get_in(mlt_frame_get_original_producer(frame)); mlt_position currentpos = mlt_filter_get_position(filter, frame); int do_freeze = 0; if (freeze_before == 0 && freeze_after == 0) { do_freeze = 1; } else if (freeze_before != 0 && pos > currentpos) { do_freeze = 1; } else if (freeze_after != 0 && pos < currentpos) { do_freeze = 1; } if (do_freeze == 1) { mlt_service_lock(MLT_FILTER_SERVICE(filter)); freeze_frame = mlt_properties_get_data(properties, "freeze_frame", NULL); if (!freeze_frame || mlt_properties_get_position(properties, "_frame") != pos) { // freeze_frame has not been fetched yet or is not useful, so fetch it and cache it. // get parent producer mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); mlt_producer_seek(producer, pos); // Get the frame mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &freeze_frame, 0); mlt_properties freeze_properties = MLT_FRAME_PROPERTIES(freeze_frame); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set(freeze_properties, "consumer.rescale", mlt_properties_get(frame_properties, "consumer.rescale")); mlt_properties_set_double(freeze_properties, "aspect_ratio", mlt_frame_get_aspect_ratio(frame)); mlt_properties_set_int(freeze_properties, "progressive", mlt_properties_get_int(props, "progressive")); mlt_properties_set_int(freeze_properties, "consumer.progressive", mlt_properties_get_int(frame_properties, "consumer.progressive")); mlt_properties_set_data(properties, "freeze_frame", freeze_frame, 0, (mlt_destructor) mlt_frame_close, NULL); mlt_properties_set_position(properties, "_frame", pos); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Get frozen image uint8_t *buffer = NULL; int error = mlt_frame_get_image(freeze_frame, &buffer, format, width, height, 1); // Copy it to current frame int size = mlt_image_format_size(*format, *width, *height, NULL); uint8_t *image_copy = mlt_pool_alloc(size); memcpy(image_copy, buffer, size); *image = image_copy; mlt_frame_set_image(frame, *image, size, mlt_pool_release); uint8_t *alpha_buffer = mlt_frame_get_alpha(freeze_frame); if (alpha_buffer) { int alphasize = *width * *height; uint8_t *alpha_copy = mlt_pool_alloc(alphasize); memcpy(alpha_copy, alpha_buffer, alphasize); mlt_frame_set_alpha(frame, alpha_copy, alphasize, mlt_pool_release); } return error; } int error = mlt_frame_get_image(frame, image, format, width, height, 1); return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the filter on to the stack mlt_frame_push_service(frame, filter); // Push the frame filter mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_freeze_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; // Set the frame which will be chosen for freeze mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "frame", "0"); // If freeze_after = 1, only frames after the "frame" value will be frozen mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "freeze_after", "0"); // If freeze_before = 1, only frames before the "frame" value will be frozen mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "freeze_before", "0"); } return filter; } mlt-7.38.0/src/modules/kdenlive/filter_freeze.yml000664 000000 000000 00000001247 15172202314 021776 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: freeze title: Freeze frame version: 1 copyright: Jean-Baptiste Mardelle creator: Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video parameters: - identifier: frame title: Frame type: time description: The time of the frame to freeze default: 0 mutable: true - identifier: freeze_after title: Freeze After type: boolean description: Whether to only freeze the frames after default: false mutable: true - identifier: freeze_before title: Freeze Before type: boolean description: Whether to only freeze the frames before default: false mutable: true mlt-7.38.0/src/modules/kdenlive/filter_wave.c000664 000000 000000 00000014570 15172202314 021104 0ustar00rootroot000000 000000 /* * wave.c -- wave filter * Copyright (C) ?-2007 Leny Grisel * Copyright (C) 2007 Jean-Baptiste Mardelle * Copyright (c) 2022-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include // this is a utility function used by DoWave below static uint8_t getPoint(uint8_t *src, int w, int h, int x, int y, int z) { if (x < 0) x += -((-x) % w) + w; else if (x >= w) x = x % w; if (y < 0) y += -((-y) % h) + h; else if (y >= h) y = y % h; return src[CLAMP(x + y * w, 0, w * h - 1) * 4 + z]; } typedef struct { uint8_t *src; int src_w; int src_h; uint8_t *dst; mlt_position position; int speed; int factor; int deformX; int deformY; } slice_desc; // the main meat of the algorithm lies here static int do_wave_slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *d = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, d->src_h, &slice_line_start); int slice_line_end = slice_line_start + slice_height; register int x, y; int decalY, decalX, z; float amplitude, phase, pulsation; register int uneven = d->src_w % 2; int w = (d->src_w - uneven) / 2; amplitude = d->factor; pulsation = 0.5 / d->factor; // smaller means bigger period phase = d->position * pulsation * d->speed / 10; // smaller means longer uint8_t *dst = d->dst + (slice_line_start * d->src_w * 2); for (y = slice_line_start; y < slice_line_end; y++) { decalX = d->deformX ? sin(pulsation * y + phase) * amplitude : 0; for (x = 0; x < w; x++) { decalY = d->deformY ? sin(pulsation * x * 2 + phase) * amplitude : 0; for (z = 0; z < 4; z++) *dst++ = getPoint(d->src, w, d->src_h, (x + decalX), (y + decalY), z); } if (uneven) { decalY = sin(pulsation * x * 2 + phase) * amplitude; for (z = 0; z < 2; z++) *dst++ = getPoint(d->src, w, d->src_h, (x + decalX), (y + decalY), z); } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_frame_get_position(frame); // Get the image *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 0); // Only process if we have no error and a valid colour space if (error == 0) { double factor = mlt_properties_get_double(properties, "start"); mlt_position f_pos = mlt_filter_get_position(filter, frame); mlt_position f_len = mlt_filter_get_length2(filter, frame); int speed = mlt_properties_anim_get_int(properties, "speed", f_pos, f_len); int deformX = mlt_properties_anim_get_int(properties, "deformX", f_pos, f_len); int deformY = mlt_properties_anim_get_int(properties, "deformY", f_pos, f_len); if (mlt_properties_get(properties, "end")) { // Determine the time position of this frame in the transition duration double end = fabs(mlt_properties_get_double(MLT_FILTER_PROPERTIES(filter), "end")); factor += (end - factor) * mlt_filter_get_progress(filter, frame); } // If animated property "wave" is set, use its value. char *wave_property = mlt_properties_get(properties, "wave"); if (wave_property) { factor = mlt_properties_anim_get_double(properties, "wave", f_pos, f_len); } mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); factor *= mlt_profile_scale_width(profile, *width); if (factor > 0.0) { int image_size = *width * (*height) * 2; uint8_t *dst = mlt_pool_alloc(image_size); slice_desc desc; desc.src = *image; desc.src_w = *width; desc.src_h = *height; desc.dst = dst; desc.position = position; desc.speed = speed; desc.factor = MAX(factor, 1); desc.deformX = deformX; desc.deformY = deformY; mlt_slices_run_normal(0, do_wave_slice_proc, &desc); *image = dst; mlt_frame_set_image(frame, *image, image_size, mlt_pool_release); } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_wave_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "start", arg == NULL ? "10" : arg); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "speed", "5"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "deformX", "1"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "deformY", "1"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "wave", NULL); } return filter; } mlt-7.38.0/src/modules/kdenlive/filter_wave.yml000664 000000 000000 00000002615 15172202314 021460 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: wave title: Wave version: 2 copyright: Leny Grisel, Jean-Baptiste Mardelle creator: Leny Grisel, Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video parameters: - identifier: start (*DEPRECATED*) title: Amplitude argument: yes type: integer description: > If an end amplitude is provided, then this is the starting amplitude, and amplitude is interpolated over the duration of the filter from start to end amplitude. If the keyframable property "wave" is provided, then this is ignored, and "wave" is used instead. This parameter also affects the pulsation period and the phase length. mutable: yes default: 10 minimum: 1 - identifier: end (*DEPRECATED*) title: End amplitude type: integer mutable: yes minimum: 1 - identifier: wave title: Amplitude type: integer description: > If this value is set the start and end parameters are ignored. mutable: yes animation: yes minimum: 1 - identifier: speed title: Speed type: integer mutable: yes animation: yes default: 5 - identifier: deformX title: Deform horizontally? type: boolean mutable: yes animation: yes default: 1 - identifier: deformY title: Deform vertically? type: boolean mutable: yes animation: yes default: 1 mlt-7.38.0/src/modules/kdenlive/producer_framebuffer.c000664 000000 000000 00000040031 15172202314 022753 0ustar00rootroot000000 000000 /* * producer_framebuffer.c -- create subspeed frames * Copyright (C) 2007 Jean-Baptiste Mardelle * Copyright (C) 2022 Meltytech, LLC * Author: Jean-Baptiste Mardelle, based on the code of motion_est by Zachary Drew * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #ifndef _MSC_VER #include #endif // Forward references. static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index); /** Image stack(able) method */ static int framebuffer_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the filter object and properties mlt_producer producer = mlt_frame_pop_service(frame); int index = mlt_frame_pop_service_int(frame); mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); // Frame properties objects mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_frame first_frame = mlt_properties_get_data(properties, "first_frame", NULL); // Get producer parameters int strobe = mlt_properties_get_int(properties, "strobe"); int freeze = mlt_properties_get_int(properties, "freeze"); int freeze_after = mlt_properties_get_int(properties, "freeze_after"); int freeze_before = mlt_properties_get_int(properties, "freeze_before"); int in = mlt_properties_get_position(properties, "in"); // Determine the position mlt_position first_position = (first_frame != NULL) ? mlt_frame_get_position(first_frame) : -1; mlt_position need_first = freeze; if (!freeze || freeze_after || freeze_before) { double prod_speed = mlt_properties_get_double(properties, "_speed"); double actual_position = prod_speed * (in + mlt_producer_position(producer)); if (mlt_properties_get_int(properties, "reverse")) actual_position = mlt_producer_get_playtime(producer) - actual_position; if (strobe < 2) { need_first = floor(actual_position); } else { // Strobe effect wanted, calculate frame position need_first = floor(actual_position); need_first -= MLT_POSITION_MOD(need_first, strobe); } if (freeze) { if (freeze_after && need_first > freeze) need_first = freeze; else if (freeze_before && need_first < freeze) need_first = freeze; } } if (*format == mlt_image_none) { // set format to the original's producer format *format = (mlt_image_format) mlt_properties_get_int(properties, "_original_format"); } // Determine output buffer size *width = mlt_properties_get_int(frame_properties, "width"); *height = mlt_properties_get_int(frame_properties, "height"); int size = mlt_image_format_size(*format, *width, *height, NULL); // Get output buffer int buffersize = 0; int alphasize = *width * *height; uint8_t *output = mlt_properties_get_data(properties, "output_buffer", &buffersize); uint8_t *output_alpha = mlt_properties_get_data(properties, "output_alpha", NULL); if (buffersize == 0 || buffersize != size) { // invalidate cached frame first_position = -1; } if (need_first != first_position) { // invalidate cached frame first_position = -1; // Bust the cached frame mlt_properties_set_data(properties, "first_frame", NULL, 0, NULL, NULL); first_frame = NULL; } if (output && first_position != -1) { // Using the cached frame uint8_t *image_copy = mlt_pool_alloc(size); memcpy(image_copy, output, size); uint8_t *alpha_copy = mlt_pool_alloc(alphasize); memcpy(alpha_copy, output_alpha, alphasize); // Set the output image *image = image_copy; mlt_frame_set_image(frame, image_copy, size, mlt_pool_release); mlt_frame_set_alpha(frame, alpha_copy, alphasize, mlt_pool_release); *width = mlt_properties_get_int(properties, "_output_width"); *height = mlt_properties_get_int(properties, "_output_height"); *format = mlt_properties_get_int(properties, "_output_format"); mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); return 0; } // Get the cached frame if (first_frame == NULL) { // Get the frame to cache from the real producer mlt_producer real_producer = mlt_properties_get_data(properties, "producer", NULL); // Seek the producer to the correct place mlt_producer_seek(real_producer, need_first); // Get the frame mlt_service_get_frame(MLT_PRODUCER_SERVICE(real_producer), &first_frame, index); // Cache the frame mlt_properties_set_data(properties, "first_frame", first_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } mlt_properties first_frame_properties = MLT_FRAME_PROPERTIES(first_frame); // Which frames are buffered? uint8_t *first_image = mlt_properties_get_data(first_frame_properties, "image", NULL); uint8_t *first_alpha = mlt_frame_get_alpha(first_frame); if (!first_image) { mlt_properties_set(first_frame_properties, "consumer.rescale", mlt_properties_get(frame_properties, "consumer.rescale")); int error = mlt_frame_get_image(first_frame, &first_image, format, width, height, writable); if (error != 0) { mlt_log_warning(MLT_PRODUCER_SERVICE(producer), "first_image == NULL get image died\n"); mlt_properties_set_data(properties, "first_frame", NULL, 0, NULL, NULL); mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); return error; } output = mlt_pool_alloc(size); memcpy(output, first_image, size); // Let someone else clean up mlt_properties_set_data(properties, "output_buffer", output, size, mlt_pool_release, NULL); mlt_properties_set_int(properties, "_output_width", *width); mlt_properties_set_int(properties, "_output_height", *height); mlt_properties_set_int(properties, "_output_format", *format); } if (!first_alpha) { alphasize = *width * *height; first_alpha = mlt_frame_get_alpha(first_frame); if (!first_alpha) { first_alpha = mlt_pool_alloc(alphasize); memset(first_alpha, 255, alphasize); mlt_frame_set_alpha(first_frame, first_alpha, alphasize, mlt_pool_release); } output_alpha = mlt_pool_alloc(alphasize); memcpy(output_alpha, first_alpha, alphasize); mlt_properties_set_data(properties, "output_alpha", output_alpha, alphasize, mlt_pool_release, NULL); } mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); // Create a copy uint8_t *image_copy = mlt_pool_alloc(size); memcpy(image_copy, first_image, size); uint8_t *alpha_copy = mlt_pool_alloc(alphasize); memcpy(alpha_copy, first_alpha, alphasize); // Set the output image *image = image_copy; mlt_frame_set_image(frame, image_copy, size, mlt_pool_release); mlt_frame_set_alpha(frame, alpha_copy, alphasize, mlt_pool_release); return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { if (frame) { // Construct a new frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // Stack the producer and producer's get image mlt_frame_push_service_int(*frame, index); mlt_frame_push_service(*frame, producer); mlt_frame_push_service(*frame, framebuffer_get_image); mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Get frame from the real producer mlt_frame first_frame = mlt_properties_get_data(properties, "first_frame", NULL); if (first_frame == NULL) { // Get the frame to cache from the real producer mlt_producer real_producer = mlt_properties_get_data(properties, "producer", NULL); // Get the producer speed double prod_speed = mlt_properties_get_double(properties, "_speed"); // Seek the producer to the correct place mlt_producer_seek(real_producer, mlt_producer_position(producer) * prod_speed); // Get the frame mlt_service_get_frame(MLT_PRODUCER_SERVICE(real_producer), &first_frame, index); // Cache the frame mlt_properties_set_data(properties, "first_frame", first_frame, 0, (mlt_destructor) mlt_frame_close, NULL); // Find the original producer's format int width = 0; int height = 0; mlt_image_format format = mlt_image_none; uint8_t *image = NULL; int error = mlt_frame_get_image(first_frame, &image, &format, &width, &height, 0); if (!error) { // cache the original producer's pixel format mlt_properties_set_int(properties, "_original_format", (int) format); // Inform framework of the default frame format for this producer mlt_properties_set_int(frame_properties, "format", format); } } mlt_properties_inherit(frame_properties, MLT_FRAME_PROPERTIES(first_frame)); double force_aspect_ratio = mlt_properties_get_double(properties, "force_aspect_ratio"); if (force_aspect_ratio <= 0.0) force_aspect_ratio = mlt_properties_get_double(properties, "aspect_ratio"); mlt_properties_set_double(frame_properties, "aspect_ratio", force_aspect_ratio); // Give the returned frame temporal identity mlt_frame_set_position(*frame, mlt_producer_position(producer)); mlt_properties_set_int(frame_properties, "meta.media.width", mlt_properties_get_int(properties, "width")); mlt_properties_set_int(frame_properties, "meta.media.height", mlt_properties_get_int(properties, "height")); mlt_properties_pass_list(frame_properties, properties, "width, height"); } return 0; } mlt_producer producer_framebuffer_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { if (!arg) return NULL; mlt_producer producer = NULL; producer = calloc(1, sizeof(struct mlt_producer_s)); if (!producer) return NULL; if (mlt_producer_init(producer, NULL)) { free(producer); return NULL; } // Wrap loader mlt_producer real_producer; // Check if a speed was specified. /** * Speed must be appended to the filename with '?'. To play your video at 50%: melt framebuffer:my_video.mpg?0.5 * Stroboscope effect can be obtained by adding a stobe=x parameter, where x is the number of frames that will be ignored. * You can play the movie backwards by adding reverse=1 * You can freeze the clip at a determined position by adding freeze=frame_pos add freeze_after=1 to freeze only paste position or freeze_before to freeze before it **/ double speed = 0.0; char *props = strdup(arg); char *ptr = strrchr(props, '?'); if (ptr) { speed = atof(ptr + 1); if (speed != 0.0) // If speed was valid, then strip it and the delimiter. // Otherwise, an invalid speed probably means this '?' was not a delimiter. *ptr = '\0'; } real_producer = mlt_factory_producer(profile, "abnormal", props); free(props); if (speed == 0.0) speed = 1.0; if (producer != NULL && real_producer != NULL) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_set(properties, "resource", arg); // Store the producer and filter mlt_properties_set_data(properties, "producer", real_producer, 0, (mlt_destructor) mlt_producer_close, NULL); // Grab some stuff from the real_producer mlt_properties_pass_list(properties, MLT_PRODUCER_PROPERTIES(real_producer), "progressive, length, width, height, aspect_ratio"); if (speed < 0) { speed = -speed; mlt_properties_set_int(properties, "reverse", 1); } if (speed != 1.0) { double real_length = ((double) mlt_producer_get_length(real_producer)) / speed; mlt_properties_set_position(properties, "length", real_length); mlt_properties real_properties = MLT_PRODUCER_PROPERTIES(real_producer); const char *service = mlt_properties_get(real_properties, "mlt_service"); if (service && !strcmp(service, "avformat")) { int n = mlt_properties_count(real_properties); int i; for (i = 0; i < n; i++) { if (strstr(mlt_properties_get_name(real_properties, i), "stream.frame_rate")) { double source_fps = mlt_properties_get_double(real_properties, mlt_properties_get_name(real_properties, i)); if (source_fps > mlt_profile_fps(profile)) { mlt_properties_set_double(real_properties, "force_fps", source_fps * speed); mlt_properties_set_position(real_properties, "length", real_length); mlt_properties_set_position(real_properties, "out", real_length - 1); speed = 1.0; } break; } } } } mlt_properties_set_position(properties, "out", mlt_producer_get_length(producer) - 1); // Since we control the seeking, prevent it from seeking on its own mlt_producer_set_speed(real_producer, 0); mlt_producer_set_speed(producer, speed); // Override the get_frame method producer->get_frame = producer_get_frame; } else { if (producer) mlt_producer_close(producer); if (real_producer) mlt_producer_close(real_producer); producer = NULL; } return producer; } mlt-7.38.0/src/modules/kdenlive/producer_framebuffer.yml000664 000000 000000 00000000304 15172202314 023331 0ustar00rootroot000000 000000 schema_version: 0.1 type: producer identifier: framebuffer title: Speed version: 1 copyright: Jean-Baptiste Mardelle creator: Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video mlt-7.38.0/src/modules/movit/000775 000000 000000 00000000000 15172202314 015757 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/movit/CMakeLists.txt000664 000000 000000 00000004442 15172202314 020523 0ustar00rootroot000000 000000 add_library(mltmovit MODULE factory.c filter_glsl_manager.cpp filter_glsl_manager.h filter_movit_blur.cpp filter_movit_convert.cpp filter_movit_crop.cpp filter_movit_deconvolution_sharpen.cpp filter_movit_diffusion.cpp filter_movit_flip.cpp filter_movit_glow.cpp filter_movit_lift_gamma_gain.cpp filter_movit_mirror.cpp filter_movit_opacity.cpp filter_movit_overlay_mode.cpp filter_movit_rect.cpp filter_movit_resample.cpp filter_movit_resize.cpp filter_movit_saturation.cpp filter_movit_vignette.cpp filter_movit_white_balance.cpp mlt_movit_input.cpp mlt_movit_input.h optional_effect.h transition_movit_luma.cpp transition_movit_mix.cpp transition_movit_overlay.cpp ) file(GLOB YML "*.yml") add_custom_target(Other_movit_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltmovit) target_include_directories(mltmovit PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_compile_options(mltmovit PRIVATE ${MLT_COMPILE_OPTIONS}) if(RELOCATABLE) target_compile_definitions(mltmovit PRIVATE RELOCATABLE) endif() target_link_libraries(mltmovit PRIVATE Threads::Threads mlt mlt++ OpenGL::GL PkgConfig::movit) if(NOT MSVC) target_link_libraries(mltmovit PRIVATE m) endif() if(UNIX AND NOT APPLE) target_sources(mltmovit PRIVATE consumer_xgl.c) target_link_libraries(mltmovit PRIVATE X11::X11) endif() pkg_get_variable(SHADERDIR movit shaderdir) target_compile_definitions(mltmovit PRIVATE SHADERDIR="${SHADERDIR}") set_target_properties(mltmovit PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltmovit LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_xgl.yml filter_glsl_manager.yml filter_movit_blur.yml filter_movit_convert.yml filter_movit_crop.yml filter_movit_deconvolution_sharpen.yml filter_movit_diffusion.yml filter_movit_flip.yml filter_movit_glow.yml filter_movit_lift_gamma_gain.yml filter_movit_mirror.yml filter_movit_opacity.yml filter_movit_overlay_mode.yml filter_movit_rect.yml filter_movit_resample.yml filter_movit_resize.yml filter_movit_saturation.yml filter_movit_vignette.yml filter_movit_white_balance.yml transition_movit_luma.yml transition_movit_mix.yml transition_movit_overlay.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/movit ) mlt-7.38.0/src/modules/movit/consumer_xgl.c000664 000000 000000 00000047444 15172202314 020645 0ustar00rootroot000000 000000 /* * consumer_xgl.c * Copyright (C) 2012 Christophe Thommeret * Author: Christophe Thommeret * Based on Nehe's GLX port by Mihael.Vrbanec@stud.uni-karlsruhe.de * http://nehe.gamedev.net/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define GL_GLEXT_PROTOTYPES #include #include #include #include #include #include #include #include #include #include #include #define STARTWIDTH 1280 #define STARTHEIGHT 720 extern int XInitThreads(); typedef struct consumer_xgl_s *consumer_xgl; struct consumer_xgl_s { struct mlt_consumer_s parent; mlt_properties properties; mlt_deque queue; pthread_t thread; int joined; int running; int playing; int xgl_started; }; typedef struct { pthread_t thread; int running; } thread_video; typedef struct { int width; int height; double aspect_ratio; GLuint texture; pthread_mutex_t mutex; int new; mlt_frame mlt_frame_ref; } frame_new; typedef struct { int width; int height; GLuint fbo; GLuint texture; } fbo; typedef struct { Display *dpy; int screen; Window win; GLXContext ctx; } HiddenContext; typedef struct { Display *dpy; int screen; Window win; GLXContext ctx; XSetWindowAttributes attr; int x, y; unsigned int width, height; unsigned int depth; } GLWindow; static GLWindow GLWin; static HiddenContext hiddenctx; static frame_new new_frame; static fbo fb; static thread_video vthread; static consumer_xgl xgl; static mlt_filter glsl_manager; static void *video_thread(void *arg); static void update() { int _width = GLWin.width; int _height = GLWin.height; GLfloat left, right, top, bottom; GLfloat war = (GLfloat) _width / (GLfloat) _height; if (war < new_frame.aspect_ratio) { left = -1.0; right = 1.0; top = war / new_frame.aspect_ratio; bottom = -war / new_frame.aspect_ratio; } else { top = 1.0; bottom = -1.0; left = -new_frame.aspect_ratio / war; right = new_frame.aspect_ratio / war; } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glPushMatrix(); glTranslatef(_width / 2, _height / 2, 0); glScalef(_width / 2, _height / 2, 1.0); glBindTexture(GL_TEXTURE_2D, fb.texture); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(left, top); glTexCoord2f(0.0f, 1.0f); glVertex2f(left, bottom); glTexCoord2f(1.0f, 1.0f); glVertex2f(right, bottom); glTexCoord2f(1.0f, 0.0f); glVertex2f(right, top); glEnd(); glPopMatrix(); glXSwapBuffers(GLWin.dpy, GLWin.win); if (!vthread.running) { pthread_create(&vthread.thread, NULL, video_thread, NULL); vthread.running = 1; } } static void show_frame() { if ((fb.width != new_frame.width) || (fb.height != new_frame.height)) { glDeleteFramebuffers(1, &fb.fbo); glDeleteTextures(1, &fb.texture); fb.fbo = 0; fb.width = new_frame.width; fb.height = new_frame.height; glGenFramebuffers(1, &fb.fbo); glGenTextures(1, &fb.texture); glBindTexture(GL_TEXTURE_2D, fb.texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb.width, fb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.texture, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); } glPushAttrib(GL_VIEWPORT_BIT); glMatrixMode(GL_PROJECTION); glPushMatrix(); glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); glViewport(0, 0, new_frame.width, new_frame.height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, new_frame.width, 0.0, new_frame.height, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, new_frame.texture); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, new_frame.height); glTexCoord2f(1.0f, 1.0f); glVertex2f(new_frame.width, new_frame.height); glTexCoord2f(1.0f, 0.0f); glVertex2f(new_frame.width, 0.0f); glEnd(); glBindFramebuffer(GL_FRAMEBUFFER, 0); mlt_events_fire(MLT_CONSUMER_PROPERTIES(&xgl->parent), "consumer-frame-show", mlt_event_data_from_frame(new_frame.mlt_frame_ref)); mlt_frame_close(new_frame.mlt_frame_ref); new_frame.mlt_frame_ref = NULL; glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopAttrib(); update(); new_frame.new = 0; } void *video_thread(void *arg) { mlt_frame next = NULL; mlt_consumer consumer = &xgl->parent; mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES(consumer); struct timeval start, end; double duration = 0; gettimeofday(&start, NULL); while (vthread.running) { // Get a frame from the attached producer next = mlt_consumer_rt_frame(consumer); if (!mlt_properties_get_int(MLT_FILTER_PROPERTIES(glsl_manager), "glsl_supported")) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "OpenGL Shading Language is not supported on this machine.\n"); xgl->running = 0; break; } // Ensure that we have a frame if (next) { mlt_properties properties = MLT_FRAME_PROPERTIES(next); if (mlt_properties_get_int(properties, "rendered") == 1) { // Get the image, width and height mlt_image_format vfmt = mlt_image_opengl_texture; int width = 0, height = 0; GLuint *image = 0; int error = mlt_frame_get_image(next, (uint8_t **) &image, &vfmt, &width, &height, 0); if (!error && image && width > 0 && height > 0 && !new_frame.new) { new_frame.width = width; new_frame.height = height; new_frame.texture = *image; new_frame.mlt_frame_ref = next; new_frame.aspect_ratio = ((double) width / (double) height) * mlt_properties_get_double(properties, "aspect_ratio"); new_frame.new = 1; int loop = 200; while (new_frame.new && --loop) usleep(500); } else { mlt_frame_close(next); } new_frame.new = 0; gettimeofday(&end, NULL); duration = 1000000.0 / mlt_properties_get_double(consumer_props, "fps"); duration -= (end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec); if (duration > 0) usleep((int) duration); gettimeofday(&start, NULL); } else { mlt_frame_close(next); static int dropped = 0; mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "dropped video frame %d\n", ++dropped); } } else usleep(1000); } mlt_consumer_stopped(consumer); return NULL; } static void resizeGLScene() { glXMakeCurrent(GLWin.dpy, GLWin.win, GLWin.ctx); if (GLWin.height == 0) GLWin.height = 1; if (GLWin.width == 0) GLWin.width = 1; glViewport(0, 0, GLWin.width, GLWin.height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, GLWin.width, 0.0, GLWin.height, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); update(); } static void initGL(void) { glXMakeCurrent(GLWin.dpy, GLWin.win, GLWin.ctx); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepth(1.0f); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glShadeModel(GL_SMOOTH); glEnable(GL_TEXTURE_2D); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); typedef int (*GLXSWAPINTERVALSGI)(int); GLXSWAPINTERVALSGI mglXSwapInterval = (GLXSWAPINTERVALSGI) glXGetProcAddressARB( (const GLubyte *) "glXSwapIntervalSGI"); if (mglXSwapInterval) mglXSwapInterval(1); fb.fbo = 0; fb.width = STARTWIDTH; fb.height = STARTHEIGHT; glGenFramebuffers(1, &fb.fbo); glGenTextures(1, &fb.texture); glBindTexture(GL_TEXTURE_2D, fb.texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb.width, fb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.texture, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); resizeGLScene(); } static void createGLWindow() { const char *title = "OpenGL consumer"; int width = STARTWIDTH; int height = STARTHEIGHT; int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None}; int attrListDbl[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None}; XVisualInfo *vi; Colormap cmap; Atom wmDelete; Window winDummy; unsigned int borderDummy; GLWin.dpy = XOpenDisplay(0); GLWin.screen = DefaultScreen(GLWin.dpy); vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListDbl); if (!vi) vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListSgl); GLWin.ctx = glXCreateContext(GLWin.dpy, vi, 0, GL_TRUE); cmap = XCreateColormap(GLWin.dpy, RootWindow(GLWin.dpy, vi->screen), vi->visual, AllocNone); GLWin.attr.colormap = cmap; GLWin.attr.border_pixel = 0; GLWin.attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; GLWin.win = XCreateWindow(GLWin.dpy, RootWindow(GLWin.dpy, vi->screen), 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &GLWin.attr); wmDelete = XInternAtom(GLWin.dpy, "WM_DELETE_WINDOW", True); XSetWMProtocols(GLWin.dpy, GLWin.win, &wmDelete, 1); XSetStandardProperties(GLWin.dpy, GLWin.win, title, title, None, NULL, 0, NULL); XMapRaised(GLWin.dpy, GLWin.win); glXMakeCurrent(GLWin.dpy, GLWin.win, GLWin.ctx); XGetGeometry(GLWin.dpy, GLWin.win, &winDummy, &GLWin.x, &GLWin.y, &GLWin.width, &GLWin.height, &borderDummy, &GLWin.depth); // Verify GLSL works on this machine hiddenctx.ctx = glXCreateContext(GLWin.dpy, vi, GLWin.ctx, GL_TRUE); if (hiddenctx.ctx) { hiddenctx.dpy = GLWin.dpy; hiddenctx.screen = GLWin.screen; hiddenctx.win = RootWindow(hiddenctx.dpy, hiddenctx.screen); } initGL(); } static void killGLWindow() { if (GLWin.ctx) { if (!glXMakeCurrent(GLWin.dpy, None, NULL)) { printf("Error releasing drawing context : killGLWindow\n"); } glXDestroyContext(GLWin.dpy, GLWin.ctx); GLWin.ctx = NULL; } if (hiddenctx.ctx) glXDestroyContext(hiddenctx.dpy, hiddenctx.ctx); XCloseDisplay(GLWin.dpy); } static void run() { XEvent event; while (xgl->running) { while (XPending(GLWin.dpy) > 0) { XNextEvent(GLWin.dpy, &event); switch (event.type) { case Expose: if (event.xexpose.count != 0) break; break; case ConfigureNotify: if ((event.xconfigure.width != GLWin.width) || (event.xconfigure.height != GLWin.height)) { GLWin.width = event.xconfigure.width; GLWin.height = event.xconfigure.height; resizeGLScene(); } break; case KeyPress: switch (XLookupKeysym(&event.xkey, 0)) { case XK_Escape: xgl->running = 0; break; default: { mlt_producer producer = mlt_properties_get_data(xgl->properties, "transport_producer", NULL); char keyboard[2] = " "; void (*callback)(mlt_producer, char *) = mlt_properties_get_data(xgl->properties, "transport_callback", NULL); if (callback != NULL && producer != NULL) { keyboard[0] = (char) XLookupKeysym(&event.xkey, 0); callback(producer, keyboard); } break; } } break; case ClientMessage: if (*XGetAtomName(GLWin.dpy, event.xclient.message_type) == *"WM_PROTOCOLS") xgl->running = 0; break; default: break; } } if (new_frame.new) show_frame(); else usleep(1000); } } void start_xgl(consumer_xgl consumer) { xgl = consumer; pthread_mutex_init(&new_frame.mutex, NULL); new_frame.aspect_ratio = 16.0 / 9.0; new_frame.new = 0; new_frame.width = STARTWIDTH; new_frame.height = STARTHEIGHT; new_frame.mlt_frame_ref = NULL; vthread.running = 0; xgl->xgl_started = 1; createGLWindow(); run(); if (vthread.running) { vthread.running = 0; pthread_join(vthread.thread, NULL); } xgl->running = 0; } static void on_consumer_thread_started(mlt_properties owner, HiddenContext *context) { // Initialize this thread's OpenGL state glXMakeCurrent(context->dpy, context->win, context->ctx); mlt_events_fire(MLT_FILTER_PROPERTIES(glsl_manager), "init glsl", mlt_event_data_none()); } /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_xgl_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object consumer_xgl this = calloc(sizeof(struct consumer_xgl_s), 1); // If no malloc'd and consumer init ok if (this != NULL && mlt_consumer_init(&this->parent, this, profile) == 0) { // Create the queue this->queue = mlt_deque_init(); // Get the parent consumer object mlt_consumer parent = &this->parent; // We have stuff to clean up, so override the close method parent->close = consumer_close; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE(parent); this->properties = MLT_SERVICE_PROPERTIES(service); // Default scaler mlt_properties_set(this->properties, "rescale", "bilinear"); mlt_properties_set(this->properties, "consumer.deinterlacer", "onefield"); // default image format mlt_properties_set(this->properties, "mlt_image_format", "glsl"); // Default buffer for low latency mlt_properties_set_int(this->properties, "buffer", 1); // Ensure we don't join on a non-running object this->joined = 1; this->xgl_started = 0; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; // "init glsl" is required to instantiate glsl filters. glsl_manager = mlt_factory_filter(profile, "glsl.manager", NULL); if (glsl_manager) { mlt_events_listen(this->properties, &hiddenctx, "consumer-thread-started", (mlt_listener) on_consumer_thread_started); } else { mlt_consumer_close(parent); parent = NULL; } // Return the consumer produced return parent; } // malloc or consumer init failed free(this); // Indicate failure return NULL; } int consumer_start(mlt_consumer parent) { consumer_xgl this = parent->child; if (!this->running) { consumer_stop(parent); this->running = 1; this->joined = 0; pthread_create(&this->thread, NULL, consumer_thread, this); } return 0; } int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_xgl this = parent->child; if (this->running && this->joined == 0) { // Kill the thread and clean up this->joined = 1; this->running = 0; if (this->thread) pthread_join(this->thread, NULL); } return 0; } int consumer_is_stopped(mlt_consumer parent) { consumer_xgl this = parent->child; return !this->running; } static void *consumer_thread(void *arg) { // Identify the arg consumer_xgl this = arg; XInitThreads(); start_xgl(this); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_xgl this = parent->child; // Stop the consumer ///mlt_consumer_stop( parent ); mlt_filter_close(glsl_manager); // Now clean up the rest mlt_consumer_close(parent); // Close the queue mlt_deque_close(this->queue); if (this->xgl_started) killGLWindow(); // Finally clean up this free(this); } mlt-7.38.0/src/modules/movit/consumer_xgl.yml000664 000000 000000 00000000543 15172202314 021211 0ustar00rootroot000000 000000 schema_version: 7.0 type: consumer identifier: xgl title: OpenGL Video (*DEPRECATED*) version: 1 copyright: Christophe Thommeret license: LGPLv2.1 language: en tags: - Video description: Display the video only in a X11 window using OpenGL. notes: > The window is a fixed size 1280x720 regardless the profile or consumer width and height properties. mlt-7.38.0/src/modules/movit/factory.c000664 000000 000000 00000031320 15172202314 017571 0ustar00rootroot000000 000000 /* * Copyright (C) 2013-2026 Meltytech, LLC * factory.c -- the factory method interfaces * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mltmovit_export.h" #include #include extern mlt_consumer consumer_xgl_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_glsl_manager_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_blur_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_convert_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_crop_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_deconvolution_sharpen_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_diffusion_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_flip_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_glow_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_lift_gamma_gain_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_mirror_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_opacity_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_overlay_mode_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_rect_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_resample_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_resize_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_saturation_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_movit_vignette_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_white_balance_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_transition transition_movit_luma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_transition transition_movit_mix_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_transition transition_movit_overlay_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/movit/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTMOVIT_EXPORT MLT_REPOSITORY { #if !defined(__APPLE__) && !defined(_WIN32) MLT_REGISTER(mlt_service_consumer_type, "xgl", consumer_xgl_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "xgl", metadata, "consumer_xgl.yml"); #endif MLT_REGISTER(mlt_service_filter_type, "glsl.manager", filter_glsl_manager_init); MLT_REGISTER(mlt_service_filter_type, "movit.blur", filter_movit_blur_init); MLT_REGISTER(mlt_service_filter_type, "movit.convert", filter_movit_convert_init); MLT_REGISTER(mlt_service_filter_type, "movit.crop", filter_movit_crop_init); MLT_REGISTER(mlt_service_filter_type, "movit.diffusion", filter_movit_diffusion_init); MLT_REGISTER(mlt_service_filter_type, "movit.flip", filter_movit_flip_init); MLT_REGISTER(mlt_service_filter_type, "movit.glow", filter_movit_glow_init); MLT_REGISTER(mlt_service_filter_type, "movit.lift_gamma_gain", filter_lift_gamma_gain_init); MLT_REGISTER(mlt_service_filter_type, "movit.mirror", filter_movit_mirror_init); MLT_REGISTER(mlt_service_filter_type, "movit.opacity", filter_movit_opacity_init); MLT_REGISTER(mlt_service_filter_type, "movit.overlay_mode", filter_movit_overlay_mode_init); MLT_REGISTER(mlt_service_filter_type, "movit.rect", filter_movit_rect_init); MLT_REGISTER(mlt_service_filter_type, "movit.resample", filter_movit_resample_init); MLT_REGISTER(mlt_service_filter_type, "movit.resize", filter_movit_resize_init); MLT_REGISTER(mlt_service_filter_type, "movit.saturation", filter_movit_saturation_init); MLT_REGISTER(mlt_service_filter_type, "movit.sharpen", filter_deconvolution_sharpen_init); MLT_REGISTER(mlt_service_filter_type, "movit.vignette", filter_movit_vignette_init); MLT_REGISTER(mlt_service_filter_type, "movit.white_balance", filter_white_balance_init); MLT_REGISTER(mlt_service_link_type, "movit.convert", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "movit.crop", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "movit.resample", mlt_link_filter_init); MLT_REGISTER(mlt_service_link_type, "movit.resize", mlt_link_filter_init); MLT_REGISTER(mlt_service_transition_type, "movit.luma_mix", transition_movit_luma_init); MLT_REGISTER(mlt_service_transition_type, "movit.mix", transition_movit_mix_init); MLT_REGISTER(mlt_service_transition_type, "movit.overlay", transition_movit_overlay_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "glsl.manager", metadata, "filter_glsl_manager.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.blur", metadata, "filter_movit_blur.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.convert", metadata, "filter_movit_convert.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.crop", metadata, "filter_movit_crop.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.diffusion", metadata, "filter_movit_diffusion.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.flip", metadata, "filter_movit_flip.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.glow", metadata, "filter_movit_glow.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.lift_gamma_gain", metadata, "filter_movit_lift_gamma_gain.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.mirror", metadata, "filter_movit_mirror.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.opacity", metadata, "filter_movit_opacity.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.overlay_mode", metadata, "filter_movit_overlay_mode.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.rect", metadata, "filter_movit_rect.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.resample", metadata, "filter_movit_resample.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.resize", metadata, "filter_movit_resize.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.saturation", metadata, "filter_movit_saturation.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.sharpen", metadata, "filter_movit_deconvolution_sharpen.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.vignette", metadata, "filter_movit_vignette.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "movit.white_balance", metadata, "filter_movit_white_balance.yml"); MLT_REGISTER_METADATA(mlt_service_link_type, "movit.convert", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "movit.crop", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "movit.resample", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_link_type, "movit.resize", mlt_link_filter_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_transition_type, "movit.luma_mix", metadata, "transition_movit_luma.yml"); MLT_REGISTER_METADATA(mlt_service_transition_type, "movit.mix", metadata, "transition_movit_mix.yml"); MLT_REGISTER_METADATA(mlt_service_transition_type, "movit.overlay", metadata, "transition_movit_overlay.yml"); } mlt-7.38.0/src/modules/movit/filter_glsl_manager.cpp000664 000000 000000 00000060345 15172202314 022473 0ustar00rootroot000000 000000 /* * filter_glsl_manager.cpp * Copyright (C) 2011-2012 Christophe Thommeret * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filter_glsl_manager.h" #include "mlt_movit_input.h" #include #include #include #include #include #include #include #include extern "C" { #include } #if defined(__APPLE__) #include #elif defined(_WIN32) #include #include #else #include #endif // Texture pool may cause frames to appear out-of-order with NVIDIA // threaded optimizations. #define USE_TEXTURE_POOL 1 using namespace movit; void dec_ref_and_delete(GlslManager *p) { if (p->dec_ref() == 0) { delete p; } } GlslManager::GlslManager() : Mlt::Filter(mlt_filter_new()) , resource_pool(new ResourcePool()) , pbo(0) , initEvent(0) , closeEvent(0) , prev_sync(NULL) { mlt_filter filter = get_filter(); if (filter) { // Set the mlt_filter child in case we choose to override virtual functions. filter->child = this; add_ref(mlt_global_properties()); mlt_events_register(get_properties(), "init glsl"); mlt_events_register(get_properties(), "close glsl"); initEvent = listen("init glsl", this, (mlt_listener) GlslManager::onInit); closeEvent = listen("close glsl", this, (mlt_listener) GlslManager::onClose); } } GlslManager::~GlslManager() { mlt_log_debug(get_service(), "%s\n", __FUNCTION__); cleanupContext(); // XXX If there is still a frame holding a reference to a texture after this // destructor is called, then it will crash in release_texture(). // while (texture_list.peek_back()) // delete (glsl_texture) texture_list.pop_back(); delete initEvent; delete closeEvent; if (prev_sync != NULL) { glDeleteSync(prev_sync); } while (syncs_to_delete.count() > 0) { GLsync sync = (GLsync) syncs_to_delete.pop_front(); glDeleteSync(sync); } delete resource_pool; } void GlslManager::add_ref(mlt_properties properties) { inc_ref(); mlt_properties_set_data(properties, "glslManager", this, 0, (mlt_destructor) dec_ref_and_delete, NULL); } GlslManager *GlslManager::get_instance() { return (GlslManager *) mlt_properties_get_data(mlt_global_properties(), "glslManager", 0); } glsl_texture GlslManager::get_texture(int width, int height, GLint internal_format) { if (width < 1 || height < 1) { return NULL; } #if USE_TEXTURE_POOL lock(); for (int i = 0; i < texture_list.count(); ++i) { glsl_texture tex = (glsl_texture) texture_list.peek(i); if (!tex->used && (tex->width == width) && (tex->height == height) && (tex->internal_format == internal_format)) { glBindTexture(GL_TEXTURE_2D, tex->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); tex->used = 1; unlock(); return tex; } } unlock(); #endif GLuint tex = 0; glGenTextures(1, &tex); if (!tex) { return NULL; } glsl_texture gtex = new glsl_texture_s; if (!gtex) { glDeleteTextures(1, &tex); return NULL; } glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); gtex->texture = tex; gtex->width = width; gtex->height = height; gtex->internal_format = internal_format; gtex->used = 1; #if USE_TEXTURE_POOL lock(); texture_list.push_back(gtex); unlock(); #endif return gtex; } void GlslManager::release_texture(glsl_texture texture) { #if USE_TEXTURE_POOL texture->used = 0; #else GlslManager *g = GlslManager::get_instance(); g->lock(); g->texture_list.push_back(texture); g->unlock(); #endif } void GlslManager::delete_sync(GLsync sync) { // We do not know which thread we are called from, and we can only // delete this if we are in one with a valid OpenGL context. // Thus, store it for later deletion in render_frame_texture(). GlslManager *g = GlslManager::get_instance(); g->lock(); g->syncs_to_delete.push_back(sync); g->unlock(); } glsl_pbo GlslManager::get_pbo(int size) { lock(); if (!pbo) { GLuint pb = 0; glGenBuffers(1, &pb); if (!pb) { unlock(); return NULL; } pbo = new glsl_pbo_s; if (!pbo) { glDeleteBuffers(1, &pb); unlock(); return NULL; } pbo->pbo = pb; pbo->size = 0; } if (size > pbo->size) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo->pbo); glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, size, NULL, GL_STREAM_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); pbo->size = size; } unlock(); return pbo; } void GlslManager::cleanupContext() { lock(); while (texture_list.peek_back()) { glsl_texture texture = (glsl_texture) texture_list.peek_back(); glDeleteTextures(1, &texture->texture); delete texture; texture_list.pop_back(); } if (pbo) { glDeleteBuffers(1, &pbo->pbo); delete pbo; pbo = 0; } unlock(); } void GlslManager::onInit(mlt_properties owner, GlslManager *filter, mlt_event_data) { mlt_log_debug(filter->get_service(), "%s\n", __FUNCTION__); #ifdef _WIN32 std::string path = std::string(mlt_environment("MLT_APPDIR")).append("\\share\\movit"); #elif defined(RELOCATABLE) #ifdef __APPLE__ std::string path = std::string(mlt_environment("MLT_APPDIR")).append("/Resources/movit"); #else std::string path = std::string(mlt_environment("MLT_APPDIR")).append("/share/movit"); #endif #else std::string path = std::string(getenv("MLT_MOVIT_PATH") ? getenv("MLT_MOVIT_PATH") : SHADERDIR); #endif bool success = init_movit(path, mlt_log_get_level() == MLT_LOG_DEBUG ? MOVIT_DEBUG_ON : MOVIT_DEBUG_OFF); filter->set("glsl_supported", success); } void GlslManager::onClose(mlt_properties owner, GlslManager *filter, mlt_event_data) { filter->cleanupContext(); } extern "C" { mlt_filter filter_glsl_manager_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { GlslManager *g = GlslManager::get_instance(); if (g) g->inc_ref(); else g = new GlslManager(); return g->get_filter(); } } // extern "C" static void deleteChain(GlslChain *chain) { // The Input* is owned by the EffectChain, but the MltInput* is not. // Thus, we have to delete it here. for (std::map::iterator input_it = chain->inputs.begin(); input_it != chain->inputs.end(); ++input_it) { delete input_it->second; } delete chain->effect_chain; delete chain; } void *GlslManager::get_frame_specific_data(mlt_service service, mlt_frame frame, const char *key, int *length) { const char *unique_id = mlt_properties_get(MLT_SERVICE_PROPERTIES(service), "_unique_id"); char buf[256]; snprintf(buf, sizeof(buf), "%s_%s", key, unique_id); return mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), buf, length); } int GlslManager::set_frame_specific_data(mlt_service service, mlt_frame frame, const char *key, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise) { const char *unique_id = mlt_properties_get(MLT_SERVICE_PROPERTIES(service), "_unique_id"); char buf[256]; snprintf(buf, sizeof(buf), "%s_%s", key, unique_id); return mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), buf, value, length, destroy, serialise); } void GlslManager::set_chain(mlt_service service, GlslChain *chain) { mlt_properties_set_data(MLT_SERVICE_PROPERTIES(service), "_movit chain", chain, 0, (mlt_destructor) deleteChain, NULL); } GlslChain *GlslManager::get_chain(mlt_service service) { return ( GlslChain *) mlt_properties_get_data(MLT_SERVICE_PROPERTIES(service), "_movit chain", NULL); } Effect *GlslManager::get_effect(mlt_service service, mlt_frame frame) { return (Effect *) get_frame_specific_data(service, frame, "_movit effect", NULL); } Effect *GlslManager::set_effect(mlt_service service, mlt_frame frame, Effect *effect) { set_frame_specific_data(service, frame, "_movit effect", effect, 0, NULL, NULL); return effect; } MltInput *GlslManager::get_input(mlt_producer producer, mlt_frame frame) { return (MltInput *) get_frame_specific_data(MLT_PRODUCER_SERVICE(producer), frame, "_movit input", NULL); } MltInput *GlslManager::set_input(mlt_producer producer, mlt_frame frame, MltInput *input) { set_frame_specific_data(MLT_PRODUCER_SERVICE(producer), frame, "_movit input", input, 0, NULL, NULL); return input; } uint8_t *GlslManager::get_input_pixel_pointer(mlt_producer producer, mlt_frame frame) { return (uint8_t *) get_frame_specific_data(MLT_PRODUCER_SERVICE(producer), frame, "_movit input pp", NULL); } uint8_t *GlslManager::set_input_pixel_pointer(mlt_producer producer, mlt_frame frame, uint8_t *image) { set_frame_specific_data(MLT_PRODUCER_SERVICE(producer), frame, "_movit input pp", image, 0, NULL, NULL); return image; } mlt_service GlslManager::get_effect_input(mlt_service service, mlt_frame frame) { return (mlt_service) get_frame_specific_data(service, frame, "_movit effect input", NULL); } void GlslManager::set_effect_input(mlt_service service, mlt_frame frame, mlt_service input_service) { set_frame_specific_data(service, frame, "_movit effect input", input_service, 0, NULL, NULL); } void GlslManager::get_effect_secondary_input(mlt_service service, mlt_frame frame, mlt_service *input_service, mlt_frame *input_frame) { *input_service = (mlt_service) get_frame_specific_data(service, frame, "_movit effect secondary input", NULL); *input_frame = (mlt_frame) get_frame_specific_data(service, frame, "_movit effect secondary input frame", NULL); } void GlslManager::set_effect_secondary_input(mlt_service service, mlt_frame frame, mlt_service input_service, mlt_frame input_frame) { set_frame_specific_data(service, frame, "_movit effect secondary input", input_service, 0, NULL, NULL); set_frame_specific_data(service, frame, "_movit effect secondary input frame", input_frame, 0, NULL, NULL); } void GlslManager::get_effect_third_input(mlt_service service, mlt_frame frame, mlt_service *input_service, mlt_frame *input_frame) { *input_service = (mlt_service) get_frame_specific_data(service, frame, "_movit effect third input", NULL); *input_frame = (mlt_frame) get_frame_specific_data(service, frame, "_movit effect third input frame", NULL); } void GlslManager::set_effect_third_input(mlt_service service, mlt_frame frame, mlt_service input_service, mlt_frame input_frame) { set_frame_specific_data(service, frame, "_movit effect third input", input_service, 0, NULL, NULL); set_frame_specific_data(service, frame, "_movit effect third input frame", input_frame, 0, NULL, NULL); } int GlslManager::render_frame_texture( EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) { if (width < 1 || height < 1) { return 1; } glsl_texture texture = get_texture(width, height, GL_RGBA8); if (!texture) { return 1; } GLuint fbo; glGenFramebuffers(1, &fbo); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, fbo); check_error(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, 0); check_error(); lock(); while (syncs_to_delete.count() > 0) { GLsync sync = (GLsync) syncs_to_delete.pop_front(); glDeleteSync(sync); } #if !USE_TEXTURE_POOL while (texture_list.count() > 0) { glsl_texture texture = (glsl_texture) texture_list.pop_back(); glDeleteTextures(1, &texture->texture); delete texture; } #endif unlock(); // Make sure we never have more than one frame pending at any time. // This ensures we do not swamp the GPU with so much work // that we cannot actually display the frames we generate. if (prev_sync != NULL) { glFlush(); glClientWaitSync(prev_sync, 0, GL_TIMEOUT_IGNORED); glDeleteSync(prev_sync); } chain->render_to_fbo(fbo, width, height); prev_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, 0); check_error(); glDeleteFramebuffers(1, &fbo); check_error(); *image = (uint8_t *) &texture->texture; mlt_frame_set_image(frame, *image, 0, NULL); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "movit.convert.fence", sync, 0, (mlt_destructor) GlslManager::delete_sync, NULL); return 0; } int GlslManager::render_frame_rgba( EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) { if (width < 1 || height < 1) { return 1; } glsl_texture texture = get_texture(width, height, GL_RGBA8); if (!texture) { return 1; } // Use a PBO to hold the data we read back with glReadPixels(). // (Intel/DRI goes into a slow path if we don't read to PBO.) int img_size = width * height * 4; glsl_pbo pbo = get_pbo(img_size); if (!pbo) { release_texture(texture); return 1; } // Set the FBO GLuint fbo; glGenFramebuffers(1, &fbo); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, fbo); check_error(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, 0); check_error(); chain->render_to_fbo(fbo, width, height); // Read FBO into PBO glBindFramebuffer(GL_FRAMEBUFFER, fbo); check_error(); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo); check_error(); glBufferData(GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ); check_error(); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0)); check_error(); // Copy from PBO uint8_t *buf = (uint8_t *) glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); check_error(); *image = (uint8_t *) mlt_pool_alloc(img_size); mlt_frame_set_image(frame, *image, img_size, mlt_pool_release); memcpy(*image, buf, img_size); // Release PBO and FBO glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); check_error(); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, 0); check_error(); glBindTexture(GL_TEXTURE_2D, 0); check_error(); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL); glDeleteFramebuffers(1, &fbo); check_error(); return 0; } int GlslManager::render_frame_rgba64( EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) { if (width < 1 || height < 1) { return 1; } glsl_texture texture = get_texture(width, height, GL_RGBA16); if (!texture) { return 1; } // Use a PBO to hold the data we read back with glReadPixels(). // (Intel/DRI goes into a slow path if we don't read to PBO.) int img_size = width * height * 8; // 8 bytes per pixel (4 channels * 2 bytes) glsl_pbo pbo = get_pbo(img_size); if (!pbo) { release_texture(texture); return 1; } // Set the FBO GLuint fbo; glGenFramebuffers(1, &fbo); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, fbo); check_error(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, 0); check_error(); chain->render_to_fbo(fbo, width, height); // Read FBO into PBO glBindFramebuffer(GL_FRAMEBUFFER, fbo); check_error(); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo); check_error(); glBufferData(GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ); check_error(); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0)); check_error(); // Copy from PBO uint8_t *buf = (uint8_t *) glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); check_error(); *image = (uint8_t *) mlt_pool_alloc(img_size); mlt_frame_set_image(frame, *image, img_size, mlt_pool_release); memcpy(*image, buf, img_size); // Release PBO and FBO glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); check_error(); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, 0); check_error(); glBindTexture(GL_TEXTURE_2D, 0); check_error(); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL); glDeleteFramebuffers(1, &fbo); check_error(); return 0; } int GlslManager::render_frame_ycbcr( EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) { if (width < 1 || height < 1) { return 1; } glsl_texture texture = get_texture(width, height, GL_RGBA16); if (!texture) { return 1; } // Use a PBO to hold the data we read back with glReadPixels(). // (Intel/DRI goes into a slow path if we don't read to PBO.) int img_size = width * height * 4 * sizeof(uint16_t); glsl_pbo pbo = get_pbo(img_size); if (!pbo) { release_texture(texture); return 1; } // Set the FBO GLuint fbo; glGenFramebuffers(1, &fbo); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, fbo); check_error(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, 0); check_error(); chain->render_to_fbo(fbo, width, height); // Read FBO into PBO glBindFramebuffer(GL_FRAMEBUFFER, fbo); check_error(); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo); check_error(); glBufferData(GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ); check_error(); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0)); check_error(); // Copy from PBO uint16_t *buf = (uint16_t *) glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); check_error(); int mlt_size = mlt_image_format_size(mlt_image_yuv444p10, width, height, nullptr); *image = (uint8_t *) mlt_pool_alloc(mlt_size); mlt_frame_set_image(frame, *image, mlt_size, mlt_pool_release); uint8_t *planes[4]; int strides[4]; mlt_image_format_planes(mlt_image_yuv444p10, width, height, *image, planes, strides); uint16_t **p = (uint16_t **) planes; for (int i = 0; i < width * height; ++i) { p[0][i] = buf[4 * i + 0]; p[1][i] = buf[4 * i + 1]; p[2][i] = buf[4 * i + 2]; } // Release PBO and FBO glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); check_error(); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); check_error(); glBindFramebuffer(GL_FRAMEBUFFER, 0); check_error(); glBindTexture(GL_TEXTURE_2D, 0); check_error(); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL); glDeleteFramebuffers(1, &fbo); check_error(); return 0; } void GlslManager::lock_service(mlt_frame frame) { Mlt::Producer producer(mlt_producer_cut_parent(mlt_frame_get_original_producer(frame))); producer.lock(); } void GlslManager::unlock_service(mlt_frame frame) { Mlt::Producer producer(mlt_producer_cut_parent(mlt_frame_get_original_producer(frame))); producer.unlock(); } mlt-7.38.0/src/modules/movit/filter_glsl_manager.h000664 000000 000000 00000012503 15172202314 022131 0ustar00rootroot000000 000000 /* * filter_glsl_manager.h * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GLSL_MANAGER_H #define GLSL_MANAGER_H // Include a random Movit header file to get in GL.h, without including it // ourselves (which might interfere with whatever OpenGL extension library // Movit has chosen to use). #include #include #include #include #include #define MAXLISTCOUNT 1024 typedef struct glsl_list_s *glsl_list; struct glsl_list_s { void *items[MAXLISTCOUNT]; int count; int (*add)(glsl_list, void *); void *(*at)(glsl_list, int); void *(*take_at)(glsl_list, int); void *(*take)(glsl_list, void *); }; struct glsl_texture_s { int used; GLuint texture; int width; int height; GLint internal_format; }; typedef struct glsl_texture_s *glsl_texture; struct glsl_pbo_s { int size; GLuint pbo; }; typedef struct glsl_pbo_s *glsl_pbo; namespace movit { class Effect; class EffectChain; class ResourcePool; } // namespace movit class MltInput; struct GlslChain { movit::EffectChain *effect_chain; // All MltInputs in the effect chain. These are not owned by the // EffectChain (although the contained Input* is). std::map inputs; // All services owned by the effect chain and their associated Movit effect. std::map effects; // For each effect in the Movit graph, a unique identifier for the service // and whether it's disabled or not, using post-order traversal. // We need to generate the chain if and only if this has changed. std::string fingerprint; }; class GlslManager : public Mlt::Filter { public: GlslManager(); ~GlslManager(); void add_ref(mlt_properties properties); static GlslManager *get_instance(); glsl_texture get_texture(int width, int height, GLint internal_format); static void release_texture(glsl_texture); static void delete_sync(GLsync sync); glsl_pbo get_pbo(int size); void cleanupContext(); movit::ResourcePool *get_resource_pool() { return resource_pool; } static void set_chain(mlt_service, GlslChain *); static GlslChain *get_chain(mlt_service); static movit::Effect *get_effect(mlt_service, mlt_frame); static movit::Effect *set_effect(mlt_service, mlt_frame, movit::Effect *); static MltInput *get_input(mlt_producer, mlt_frame); static MltInput *set_input(mlt_producer, mlt_frame, MltInput *); static uint8_t *get_input_pixel_pointer(mlt_producer, mlt_frame); static uint8_t *set_input_pixel_pointer(mlt_producer, mlt_frame, uint8_t *); static mlt_service get_effect_input(mlt_service, mlt_frame); static void set_effect_input(mlt_service, mlt_frame, mlt_service); static void get_effect_secondary_input(mlt_service, mlt_frame, mlt_service *, mlt_frame *); static void set_effect_secondary_input(mlt_service, mlt_frame, mlt_service, mlt_frame); static void get_effect_third_input(mlt_service, mlt_frame, mlt_service *, mlt_frame *); static void set_effect_third_input(mlt_service, mlt_frame, mlt_service, mlt_frame); int render_frame_texture(movit::EffectChain *, mlt_frame, int width, int height, uint8_t **image); int render_frame_rgba(movit::EffectChain *, mlt_frame, int width, int height, uint8_t **image); int render_frame_rgba64(movit::EffectChain *, mlt_frame, int width, int height, uint8_t **image); int render_frame_ycbcr(movit::EffectChain *, mlt_frame, int width, int height, uint8_t **image); static void lock_service(mlt_frame frame); static void unlock_service(mlt_frame frame); private: static void *get_frame_specific_data(mlt_service service, mlt_frame frame, const char *key, int *length); static int set_frame_specific_data(mlt_service service, mlt_frame frame, const char *key, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise); static void onInit(mlt_properties owner, GlslManager *filter, mlt_event_data); static void onClose(mlt_properties owner, GlslManager *filter, mlt_event_data); movit::ResourcePool *resource_pool; Mlt::Deque texture_list; Mlt::Deque syncs_to_delete; glsl_pbo pbo; Mlt::Event *initEvent; Mlt::Event *closeEvent; GLsync prev_sync; }; #endif // GLSL_MANAGER_H mlt-7.38.0/src/modules/movit/filter_glsl_manager.yml000664 000000 000000 00000000520 15172202314 022477 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: glsl.manager title: GLSL Manager version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en description: > This is a special filter to manage OpenGL and Movit. See https://mltframework.org/docs/opengl/ for more information. tags: - Video - Hidden mlt-7.38.0/src/modules/movit/filter_movit_blur.cpp000664 000000 000000 00000006053 15172202314 022216 0ustar00rootroot000000 000000 /* * filter_movit_blur.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); double radius = mlt_properties_anim_get_double(properties, "radius", mlt_filter_get_position(filter, frame), mlt_filter_get_length2(filter, frame)); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { return error; } radius *= mlt_profile_scale_width(profile, *width); mlt_properties_set_double(properties, "_movit.parms.float.radius", radius); GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new BlurEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_movit_blur_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set_double(properties, "radius", 3); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_blur.yml000664 000000 000000 00000001023 15172202314 022225 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.blur title: Blur (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A separable 2D blur implemented by a combination of mipmap filtering and convolution (essentially giving a convolution with a piecewise linear approximation to the true impulse response). parameters: - identifier: radius title: Radius type: float minimum: 0 default: 3 mutable: yes animation: yes mlt-7.38.0/src/modules/movit/filter_movit_convert.cpp000664 000000 000000 00000102470 15172202314 022732 0ustar00rootroot000000 000000 /* * filter_movit_convert.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "filter_glsl_manager.h" #include "mlt_flip_effect.h" #include "mlt_movit_input.h" #include #include #include #include using namespace movit; static void set_movit_parameters(GlslChain *chain, mlt_service service, mlt_frame frame); static void yuv422_to_yuv422p(uint8_t *yuv422, uint8_t *yuv422p, int width, int height) { uint8_t *Y = yuv422p; uint8_t *U = Y + width * height; uint8_t *V = U + width * height / 2; int n = width * height / 2 + 1; while (--n) { *Y++ = *yuv422++; *U++ = *yuv422++; *Y++ = *yuv422++; *V++ = *yuv422++; } } static int convert_on_cpu(mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format) { int error = 0; mlt_filter cpu_csc = (mlt_filter) mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "_movit cpu_convert", NULL); if (cpu_csc) { int (*save_fp)(mlt_frame self, uint8_t * *image, mlt_image_format * input, mlt_image_format output) = frame->convert_image; frame->convert_image = NULL; mlt_filter_process(cpu_csc, frame); error = frame->convert_image(frame, image, format, output_format); frame->convert_image = save_fp; } else { error = 1; } return error; } static void delete_chain(EffectChain *chain) { delete chain; } // Get the gamma from the frame "color_trc" property as set by producer or this filter. static GammaCurve getFrameGamma(mlt_color_trc color_trc) { switch (color_trc) { case mlt_color_trc_linear: return GAMMA_LINEAR; case mlt_color_trc_gamma22: case mlt_color_trc_iec61966_2_1: return GAMMA_sRGB; case mlt_color_trc_bt2020_10: return GAMMA_REC_2020_10_BIT; case mlt_color_trc_bt2020_12: return GAMMA_REC_2020_12_BIT; #if MOVIT_VERSION >= 1037 case mlt_color_trc_arib_std_b67: return GAMMA_HLG; #endif default: return GAMMA_REC_709; } } // Get the gamma from the consumer's "mlt_color_trc" or "color_trc" property. // Also, update the frame's color_trc property with the selection. static GammaCurve getOutputGamma(mlt_properties properties) { const char *color_trc_str = mlt_properties_get(properties, "consumer.color_trc"); mlt_color_trc color_trc = mlt_image_color_trc_id(color_trc_str); mlt_properties_set_int(properties, "color_trc", color_trc); switch (color_trc) { case mlt_color_trc_bt709: case mlt_color_trc_smpte170m: return GAMMA_REC_709; case mlt_color_trc_linear: return GAMMA_LINEAR; case mlt_color_trc_bt2020_10: return GAMMA_REC_2020_10_BIT; case mlt_color_trc_bt2020_12: return GAMMA_REC_2020_12_BIT; #if MOVIT_VERSION >= 1037 case mlt_color_trc_arib_std_b67: return GAMMA_HLG; #endif default: break; } return GAMMA_REC_709; } static void get_format_from_properties(mlt_properties properties, ImageFormat *image_format, YCbCrFormat *ycbcr_format) { const char *colorspace_str = mlt_properties_get(properties, "colorspace"); switch (mlt_image_colorspace_id(colorspace_str)) { case mlt_colorspace_bt601: ycbcr_format->luma_coefficients = YCBCR_REC_601; break; case mlt_colorspace_bt2020_ncl: ycbcr_format->luma_coefficients = YCBCR_REC_2020; case mlt_colorspace_bt709: default: ycbcr_format->luma_coefficients = YCBCR_REC_709; break; } if (image_format) { const char *color_pri_str = mlt_properties_get(properties, "color_primaries"); switch (mlt_image_color_pri_id(color_pri_str)) { case mlt_color_pri_bt470bg: image_format->color_space = COLORSPACE_REC_601_625; break; case mlt_color_pri_smpte170m: image_format->color_space = COLORSPACE_REC_601_525; break; case mlt_color_pri_bt2020: image_format->color_space = COLORSPACE_REC_2020; break; case mlt_color_pri_bt709: default: image_format->color_space = COLORSPACE_REC_709; break; } const char *color_trc_str = mlt_properties_get(properties, "color_trc"); mlt_color_trc color_trc = mlt_image_color_trc_id(color_trc_str); image_format->gamma_curve = getFrameGamma(color_trc); } if (mlt_properties_get_int(properties, "force_full_luma")) { ycbcr_format->full_range = true; } else { ycbcr_format->full_range = (mlt_properties_get_int(properties, "full_range") == 1); } // TODO: make new frame properties set by producers ycbcr_format->cb_x_position = ycbcr_format->cr_x_position = 0.0f; ycbcr_format->cb_y_position = ycbcr_format->cr_y_position = 0.5f; } static void build_fingerprint(GlslChain *chain, mlt_service service, mlt_frame frame, std::string *fingerprint) { if (service == (mlt_service) -1) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); if (producer && chain && chain->inputs[producer]) fingerprint->append(mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "_unique_id")); else fingerprint->append("input"); return; } mlt_service input_a = GlslManager::get_effect_input(service, frame); fingerprint->push_back('('); build_fingerprint(chain, input_a, frame, fingerprint); fingerprint->push_back(')'); mlt_frame frame_b; mlt_service input_b; GlslManager::get_effect_secondary_input(service, frame, &input_b, &frame_b); if (input_b) { fingerprint->push_back('('); build_fingerprint(chain, input_b, frame_b, fingerprint); fingerprint->push_back(')'); } GlslManager::get_effect_third_input(service, frame, &input_b, &frame_b); if (input_b) { fingerprint->push_back('('); build_fingerprint(chain, input_b, frame_b, fingerprint); fingerprint->push_back(')'); } fingerprint->push_back('('); fingerprint->append(mlt_properties_get(MLT_SERVICE_PROPERTIES(service), "_unique_id")); const char *effect_fingerprint = mlt_properties_get(MLT_SERVICE_PROPERTIES(service), "_movit fingerprint"); if (effect_fingerprint) { fingerprint->push_back('['); fingerprint->append(effect_fingerprint); fingerprint->push_back(']'); } bool disable = mlt_properties_get_int(MLT_SERVICE_PROPERTIES(service), "_movit.parms.int.disable"); if (disable) { fingerprint->push_back('d'); } fingerprint->push_back(')'); } static Effect *build_movit_chain(mlt_service service, mlt_frame frame, GlslChain *chain) { if (service == (mlt_service) -1) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); MltInput *input = GlslManager::get_input(producer, frame); GlslManager::set_input(producer, frame, NULL); chain->effect_chain->add_input(input->get_input()); chain->inputs.insert(std::make_pair(producer, input)); return input->get_input(); } Effect *effect = GlslManager::get_effect(service, frame); assert(effect); GlslManager::set_effect(service, frame, NULL); mlt_service input_a = GlslManager::get_effect_input(service, frame); mlt_service input_b, input_c; mlt_frame frame_b, frame_c; GlslManager::get_effect_secondary_input(service, frame, &input_b, &frame_b); GlslManager::get_effect_third_input(service, frame, &input_c, &frame_c); Effect *effect_a = build_movit_chain(input_a, frame, chain); if (input_c && input_b) { Effect *effect_b = build_movit_chain(input_b, frame_b, chain); Effect *effect_c = build_movit_chain(input_c, frame_c, chain); chain->effect_chain->add_effect(effect, effect_a, effect_b, effect_c); } else if (input_b) { Effect *effect_b = build_movit_chain(input_b, frame_b, chain); chain->effect_chain->add_effect(effect, effect_a, effect_b); } else { chain->effect_chain->add_effect(effect, effect_a); } chain->effects.insert(std::make_pair(service, effect)); return effect; } static void dispose_movit_effects(mlt_service service, mlt_frame frame) { if (service == (mlt_service) -1) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); delete GlslManager::get_input(producer, frame); GlslManager::set_input(producer, frame, NULL); return; } delete GlslManager::get_effect(service, frame); GlslManager::set_effect(service, frame, NULL); mlt_service input_a = GlslManager::get_effect_input(service, frame); mlt_service input_b; mlt_frame frame_b; GlslManager::get_effect_secondary_input(service, frame, &input_b, &frame_b); dispose_movit_effects(input_a, frame); if (input_b) { dispose_movit_effects(input_b, frame_b); } GlslManager::get_effect_third_input(service, frame, &input_b, &frame_b); if (input_b) { dispose_movit_effects(input_b, frame_b); } } static void finalize_movit_chain(mlt_service leaf_service, mlt_frame frame, mlt_image_format format) { GlslChain *chain = GlslManager::get_chain(leaf_service); auto properties = MLT_FRAME_PROPERTIES(frame); std::string new_fingerprint; build_fingerprint(chain, leaf_service, frame, &new_fingerprint); // Build the chain if needed. if (!chain || new_fingerprint != chain->fingerprint) { mlt_log_debug(leaf_service, "=== CREATING NEW CHAIN (old chain=%p, leaf=%p, fingerprint=%s) ===\n", chain, leaf_service, new_fingerprint.c_str()); mlt_profile profile = mlt_service_profile(leaf_service); chain = new GlslChain; chain->effect_chain = new EffectChain(profile->display_aspect_num, profile->display_aspect_den, GlslManager::get_instance()->get_resource_pool()); chain->fingerprint = new_fingerprint; build_movit_chain(leaf_service, frame, chain); set_movit_parameters(chain, leaf_service, frame); chain->effect_chain->add_effect(new Mlt::VerticalFlip); ImageFormat output_format; if (format == mlt_image_yuv444p10 || format == mlt_image_yuv420p10) { YCbCrFormat ycbcr_format = {}; get_format_from_properties(properties, &output_format, &ycbcr_format); output_format.gamma_curve = getOutputGamma(properties); ycbcr_format.full_range = mlt_image_full_range( mlt_properties_get(properties, "consumer.color_range")); mlt_log_debug(nullptr, "[filter movit.convert] output gamma %d full-range %d\n", output_format.gamma_curve, ycbcr_format.full_range); mlt_properties_set_int(properties, "full_range", ycbcr_format.full_range); ycbcr_format.num_levels = 1024; ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 1; chain->effect_chain->add_ycbcr_output(output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_INTERLEAVED, GL_UNSIGNED_SHORT); chain->effect_chain->set_dither_bits(16); } else if (format == mlt_image_rgba64) { output_format.color_space = COLORSPACE_sRGB; output_format.gamma_curve = getOutputGamma(properties); chain->effect_chain->add_output(output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); chain->effect_chain->set_dither_bits(16); } else { output_format.color_space = COLORSPACE_sRGB; output_format.gamma_curve = getOutputGamma(properties); chain->effect_chain->add_output(output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); chain->effect_chain->set_dither_bits(8); } chain->effect_chain->finalize(); GlslManager::set_chain(leaf_service, chain); } else { // Delete all the created Effect instances to avoid memory leaks. dispose_movit_effects(leaf_service, frame); getOutputGamma(properties); mlt_properties_set_int(properties, "full_range", mlt_image_full_range( mlt_properties_get(properties, "consumer.color_range"))); } } static void set_movit_parameters(GlslChain *chain, mlt_service service, mlt_frame frame) { if (service == (mlt_service) -1) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); MltInput *input = chain->inputs[producer]; if (input) input->set_pixel_data(GlslManager::get_input_pixel_pointer(producer, frame)); return; } Effect *effect = chain->effects[service]; mlt_service input_a = GlslManager::get_effect_input(service, frame); set_movit_parameters(chain, input_a, frame); mlt_service input_b; mlt_frame frame_b; GlslManager::get_effect_secondary_input(service, frame, &input_b, &frame_b); if (input_b) { set_movit_parameters(chain, input_b, frame_b); } GlslManager::get_effect_third_input(service, frame, &input_b, &frame_b); if (input_b) { set_movit_parameters(chain, input_b, frame_b); } mlt_properties properties = MLT_SERVICE_PROPERTIES(service); int count = mlt_properties_count(properties); for (int i = 0; i < count; ++i) { const char *name = mlt_properties_get_name(properties, i); if (strncmp(name, "_movit.parms.float.", strlen("_movit.parms.float.")) == 0 && mlt_properties_get_value(properties, i)) { effect->set_float(name + strlen("_movit.parms.float."), mlt_properties_get_double(properties, name)); } if (strncmp(name, "_movit.parms.int.", strlen("_movit.parms.int.")) == 0 && mlt_properties_get_value(properties, i)) { effect->set_int(name + strlen("_movit.parms.int."), mlt_properties_get_int(properties, name)); } if (strncmp(name, "_movit.parms.vec3.", strlen("_movit.parms.vec3.")) == 0 && strcmp(name + strlen(name) - 3, "[0]") == 0 && mlt_properties_get_value(properties, i)) { float val[3]; char *name_copy = strdup(name); char *index_char = name_copy + strlen(name_copy) - 2; val[0] = mlt_properties_get_double(properties, name_copy); *index_char = '1'; val[1] = mlt_properties_get_double(properties, name_copy); *index_char = '2'; val[2] = mlt_properties_get_double(properties, name_copy); index_char[-1] = '\0'; effect->set_vec3(name_copy + strlen("_movit.parms.vec3."), val); free(name_copy); } if (strncmp(name, "_movit.parms.vec4.", strlen("_movit.parms.vec4.")) == 0 && strcmp(name + strlen(name) - 3, "[0]") == 0 && mlt_properties_get_value(properties, i)) { float val[4]; char *name_copy = strdup(name); char *index_char = name_copy + strlen(name_copy) - 2; val[0] = mlt_properties_get_double(properties, name_copy); *index_char = '1'; val[1] = mlt_properties_get_double(properties, name_copy); *index_char = '2'; val[2] = mlt_properties_get_double(properties, name_copy); *index_char = '3'; val[3] = mlt_properties_get_double(properties, name_copy); index_char[-1] = '\0'; effect->set_vec4(name_copy + strlen("_movit.parms.vec4."), val); free(name_copy); } } } static void dispose_pixel_pointers(GlslChain *chain, mlt_service service, mlt_frame frame) { if (service == (mlt_service) -1) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); MltInput *input = chain->inputs[producer]; if (input) input->invalidate_pixel_data(); mlt_pool_release(GlslManager::get_input_pixel_pointer(producer, frame)); return; } mlt_service input_a = GlslManager::get_effect_input(service, frame); dispose_pixel_pointers(chain, input_a, frame); mlt_service input_b; mlt_frame frame_b; GlslManager::get_effect_secondary_input(service, frame, &input_b, &frame_b); if (input_b) { dispose_pixel_pointers(chain, input_b, frame_b); } GlslManager::get_effect_third_input(service, frame, &input_b, &frame_b); if (input_b) { dispose_pixel_pointers(chain, input_b, frame_b); } } static int movit_render(EffectChain *chain, mlt_frame frame, mlt_image_format *format, mlt_image_format output_format, int width, int height, uint8_t **image) { if (width < 1 || height < 1) { mlt_log_error(NULL, "Invalid frame size for movit_render: %dx%d.\n", width, height); return 1; } GlslManager *glsl = GlslManager::get_instance(); int error = 0; switch (output_format) { case mlt_image_opengl_texture: error = glsl->render_frame_texture(chain, frame, width, height, image); break; case mlt_image_yuv444p10: case mlt_image_yuv420p10: error = glsl->render_frame_ycbcr(chain, frame, width, height, image); if (!error && output_format != mlt_image_yuv444p10) { *format = mlt_image_yuv444p10; error = convert_on_cpu(frame, image, format, output_format); } break; case mlt_image_rgba64: error = glsl->render_frame_rgba64(chain, frame, width, height, image); break; default: // Covers mlt_image_rgba and all other fallbacks. error = glsl->render_frame_rgba(chain, frame, width, height, image); if (!error && output_format != mlt_image_rgba) { *format = mlt_image_rgba; error = convert_on_cpu(frame, image, format, output_format); } break; } return error; } // Create an MltInput for an image with the given format and dimensions. static MltInput *create_input(mlt_properties properties, mlt_image_format format, int aspect_width, int aspect_height, int width, int height) { if (width < 1 || height < 1) { mlt_log_error(NULL, "Invalid frame size for create_input: %dx%d.\n", width, height); return nullptr; } MltInput *input = new MltInput(format); switch (format) { case mlt_image_rgba: case mlt_image_rgba64: // TODO: Get the color space if available. input->useFlatInput(FORMAT_RGBA_POSTMULTIPLIED_ALPHA, width, height); break; case mlt_image_rgb: // TODO: Get the color space if available. input->useFlatInput(FORMAT_RGB, width, height); break; case mlt_image_yuv420p: { ImageFormat image_format = {}; YCbCrFormat ycbcr_format = {}; get_format_from_properties(properties, &image_format, &ycbcr_format); ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 2; input->useYCbCrInput(image_format, ycbcr_format, width, height); } break; case mlt_image_yuv422: { ImageFormat image_format = {}; YCbCrFormat ycbcr_format = {}; get_format_from_properties(properties, &image_format, &ycbcr_format); ycbcr_format.chroma_subsampling_x = 2; ycbcr_format.chroma_subsampling_y = 1; input->useYCbCrInput(image_format, ycbcr_format, width, height); } break; case mlt_image_yuv420p10: { ImageFormat image_format = {}; YCbCrFormat ycbcr_format = {}; get_format_from_properties(properties, &image_format, &ycbcr_format); ycbcr_format.chroma_subsampling_x = 2; ycbcr_format.chroma_subsampling_y = 2; ycbcr_format.num_levels = 1024; input->useYCbCrInput(image_format, ycbcr_format, width, height); } break; case mlt_image_yuv444p10: { ImageFormat image_format = {}; YCbCrFormat ycbcr_format = {}; get_format_from_properties(properties, &image_format, &ycbcr_format); ycbcr_format.chroma_subsampling_x = 1; ycbcr_format.chroma_subsampling_y = 1; ycbcr_format.num_levels = 1024; input->useYCbCrInput(image_format, ycbcr_format, width, height); } break; default: // Leave input configured with its default for unsupported/other formats. break; } return input; } // Make a copy of the given image (allocated using mlt_pool_alloc) suitable // to pass as pixel pointer to an MltInput (created using create_input // with the same parameters), and return that pointer. static uint8_t *make_input_copy(mlt_image_format format, uint8_t *image, int width, int height) { if (width < 1 || height < 1) { mlt_log_error(NULL, "Invalid frame size for make_input_copy: %dx%d.\n", width, height); return NULL; } int img_size = mlt_image_format_size(format, width, height, NULL); uint8_t *img_copy = (uint8_t *) mlt_pool_alloc(img_size); if (format == mlt_image_yuv422) { yuv422_to_yuv422p(image, img_copy, width, height); } else { memcpy(img_copy, image, img_size); } return img_copy; } static int convert_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format) { // Nothing to do! if (*format == output_format) return 0; mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_log_debug(NULL, "filter_movit_convert: %s -> %s (%d)\n", mlt_image_format_name(*format), mlt_image_format_name(output_format), mlt_frame_get_position(frame)); // Use CPU if glsl not initialized or not supported. GlslManager *glsl = GlslManager::get_instance(); if (!glsl || !glsl->get_int("glsl_supported")) return convert_on_cpu(frame, image, format, output_format); // Do non-GL image conversions on a CPU-based image converter. if (*format != mlt_image_movit && output_format != mlt_image_movit && output_format != mlt_image_opengl_texture) return convert_on_cpu(frame, image, format, output_format); int error = 0; int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); if (width < 1 || height < 1) { mlt_log_error(NULL, "Invalid frame size for convert_image %dx%d.\n", width, height); return 1; } GlslManager::get_instance()->lock_service(frame); // If we're at the beginning of a series of Movit effects, store the input // sent into the chain. if (output_format == mlt_image_movit) { if (*format != mlt_image_rgba && *format != mlt_image_rgba64 && mlt_frame_get_alpha(frame)) { if (!convert_on_cpu(frame, image, format, mlt_image_rgba)) { *format = mlt_image_rgba; } } mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); MltInput *input = create_input(properties, *format, profile->width, profile->height, width, height); if (!input) { mlt_log_error(nullptr, "filter movit.convert: create_input failed\n"); return 1; } GlslManager::set_input(producer, frame, input); uint8_t *img_copy = make_input_copy(*format, *image, width, height); if (!img_copy) { mlt_log_error(nullptr, "filter movit.convert: make_input_copy failed\n"); delete input; return 1; } GlslManager::set_input_pixel_pointer(producer, frame, img_copy); *image = (uint8_t *) -1; mlt_frame_set_image(frame, *image, 0, NULL); } // If we're at the _end_ of a series of Movit effects, render the chain. if (*format == mlt_image_movit) { mlt_service leaf_service = (mlt_service) *image; if (leaf_service == (mlt_service) -1) { // Something on the way requested conversion to mlt_glsl, // but never added an effect. Don't build a Movit chain; // just do the conversion and we're done. mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); MltInput *input = GlslManager::get_input(producer, frame); *image = GlslManager::get_input_pixel_pointer(producer, frame); *format = input->get_format(); delete input; GlslManager::get_instance()->unlock_service(frame); return convert_on_cpu(frame, image, format, output_format); } // Construct the chain unless we already have a good one. finalize_movit_chain(leaf_service, frame, output_format); // Set per-frame parameters now that we know which Effect instances to set them on. // (finalize_movit_chain may already have done this, though, but twice doesn't hurt.) GlslChain *chain = GlslManager::get_chain(leaf_service); set_movit_parameters(chain, leaf_service, frame); error = movit_render(chain->effect_chain, frame, format, output_format, width, height, image); dispose_pixel_pointers(chain, leaf_service, frame); } // If we've been asked to render some frame directly to a texture (without any // effects in-between), we create a new mini-chain to do so. if (*format != mlt_image_movit && output_format == mlt_image_opengl_texture) { // We might already have a texture from a previous conversion from mlt_image_movit. glsl_texture texture = (glsl_texture) mlt_properties_get_data(properties, "movit.convert.texture", NULL); // XXX: requires a special property set on the frame by the app for now // because we do not have reliable way to clear the texture property // when a downstream filter has changed image. if (texture && mlt_properties_get_int(properties, "movit.convert.use_texture")) { *image = (uint8_t *) &texture->texture; mlt_frame_set_image(frame, *image, 0, NULL); } else { // Use a separate chain to convert image in RAM to OpenGL texture. // Use cached chain if available and compatible. Mlt::Producer producer(mlt_producer_cut_parent(mlt_frame_get_original_producer(frame))); EffectChain *chain = (EffectChain *) producer.get_data("movit.convert.chain"); MltInput *input = (MltInput *) producer.get_data("movit.convert.input"); int w = producer.get_int("movit.convert.width"); int h = producer.get_int("movit.convert.height"); mlt_image_format f = (mlt_image_format) producer.get_int("movit.convert.format"); if (!chain || !input || width != w || height != h || *format != f) { chain = new EffectChain(width, height, GlslManager::get_instance()->get_resource_pool()); input = create_input(properties, *format, width, height, width, height); if (!input) { delete chain; return 1; } chain->add_input(input->get_input()); chain->add_effect(new Mlt::VerticalFlip()); ImageFormat movit_output_format; movit_output_format.color_space = COLORSPACE_sRGB; movit_output_format.gamma_curve = GAMMA_sRGB; chain->add_output(movit_output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); chain->set_dither_bits(8); chain->finalize(); producer.set("movit.convert.chain", chain, 0, (mlt_destructor) delete_chain); producer.set("movit.convert.input", input, 0, NULL); producer.set("movit.convert.width", width); producer.set("movit.convert.height", height); producer.set("movit.convert.format", *format); } if (*format == mlt_image_yuv422) { // We need to convert to planar, which make_input_copy() will do for us. uint8_t *planar = make_input_copy(*format, *image, width, height); if (!planar) { return 1; } input->set_pixel_data(planar); error = movit_render(chain, frame, format, output_format, width, height, image); mlt_pool_release(planar); } else { input->set_pixel_data(*image); error = movit_render(chain, frame, format, output_format, width, height, image); } } } GlslManager::get_instance()->unlock_service(frame); mlt_properties_set_int(properties, "format", output_format); *format = output_format; return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { // Set a default colorspace on the frame if not yet set by the producer. // The producer may still change it during get_image. // This way we do not have to modify each producer to set a valid colorspace. mlt_properties properties = MLT_FRAME_PROPERTIES(frame); if (mlt_properties_get_int(properties, "colorspace") <= 0) mlt_properties_set_int(properties, "colorspace", mlt_service_profile(MLT_FILTER_SERVICE(filter))->colorspace); frame->convert_image = convert_image; mlt_filter cpu_csc = (mlt_filter) mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "cpu_convert", NULL); mlt_properties_inc_ref(MLT_FILTER_PROPERTIES(cpu_csc)); mlt_properties_set_data(properties, "_movit cpu_convert", cpu_csc, 0, (mlt_destructor) mlt_filter_close, NULL); return frame; } static mlt_filter create_filter(mlt_profile profile, const char *effect) { mlt_filter filter; char *id = strdup(effect); char *arg = strchr(id, ':'); if (arg != NULL) *arg++ = '\0'; filter = mlt_factory_filter(profile, id, arg); if (filter) mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "_loader", 1); free(id); return filter; } extern "C" { mlt_filter filter_movit_convert_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_filter cpu_csc = create_filter(profile, "avcolor_space"); if (!cpu_csc) cpu_csc = create_filter(profile, "imageconvert"); if (cpu_csc) mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "cpu_convert", cpu_csc, 0, (mlt_destructor) mlt_filter_close, NULL); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_convert.yml000664 000000 000000 00000000751 15172202314 022750 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.convert title: Image Converter (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en description: Remove pixels from the edges of the video. tags: - Video - Hidden description: Converts the colorspace and pixel format. notes: > This is not intended to be created directly. Rather, the loader producer loads it if it is available to set the convert_image function pointer on frames. mlt-7.38.0/src/modules/movit/filter_movit_crop.cpp000664 000000 000000 00000013277 15172202314 022223 0ustar00rootroot000000 000000 /* * filter_movit_crop.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include "optional_effect.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_image_format requested_format = *format; // Correct width/height if necessary *width = mlt_properties_get_int(properties, "crop.original_width"); *height = mlt_properties_get_int(properties, "crop.original_height"); if (*width < 1 || *height < 1) { *width = mlt_properties_get_int(properties, "meta.media.width"); *height = mlt_properties_get_int(properties, "meta.media.height"); } if (*width < 1 || *height < 1) { *width = profile->width; *height = profile->height; } if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } mlt_properties_set_int(properties, "rescale_width", *width); mlt_properties_set_int(properties, "rescale_height", *height); // Get the image as requested // *format = (mlt_image_format) mlt_properties_get_int( MLT_PRODUCER_PROPERTIES(producer), "_movit image_format" ); // This is needed to prevent conversion to mlt_image_movit after producer, // deinterlace, or fieldorder, The latter two can force output of // an image after it had already been converted to glsl. *format = mlt_image_none; error = mlt_frame_get_image(frame, image, format, width, height, writable); // Skip processing if requested. if (requested_format == mlt_image_none) return error; if (!error && *format != mlt_image_movit && frame->convert_image) { // Pin the requested format to the first one returned. // mlt_properties_set_int( MLT_PRODUCER_PROPERTIES(producer), "_movit image_format", *format ); error = frame->convert_image(frame, image, format, mlt_image_movit); } if (!error) { double left = mlt_properties_get_double(properties, "crop.left"); double right = mlt_properties_get_double(properties, "crop.right"); double top = mlt_properties_get_double(properties, "crop.top"); double bottom = mlt_properties_get_double(properties, "crop.bottom"); int owidth = *width - left - right; int oheight = *height - top - bottom; owidth = owidth < 1 ? 1 : owidth; oheight = oheight < 1 ? 1 : oheight; mlt_log_debug(MLT_FILTER_SERVICE(filter), "%dx%d -> %dx%d\n", *width, *height, owidth, oheight); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_properties_set_int(properties, "_movit.parms.int.width", owidth); mlt_properties_set_int(properties, "_movit.parms.int.height", oheight); mlt_properties_set_double(properties, "_movit.parms.float.left", -left); mlt_properties_set_double(properties, "_movit.parms.float.top", -top); bool disable = (*width == owidth && *height == oheight); mlt_properties_set_int(properties, "_movit.parms.int.disable", disable); GlslManager::get_instance()->unlock_service(frame); } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); Effect *effect = GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new OptionalEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); RGBATuple border_color(0.0f, 0.0f, 0.0f, 1.0f); effect->set_vec4("border_color", (float *) &border_color); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" mlt_filter filter_movit_crop_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); filter->process = process; } return filter; } mlt-7.38.0/src/modules/movit/filter_movit_crop.yml000664 000000 000000 00000001046 15172202314 022231 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.crop title: Crop (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en description: Remove pixels from the edges of the video. tags: - Video notes: > This filter is meant to be included as a normalizing filter attached automatically by the loader so that cropping occurs early in the processing stack and can request the full resolution of the source image. Then, the core crop filter can be use to set the parameters of the crop operation. mlt-7.38.0/src/modules/movit/filter_movit_deconvolution_sharpen.cpp000664 000000 000000 00000011502 15172202314 025655 0ustar00rootroot000000 000000 /* * filter_deconvolution_sharpen.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); int matrix_size = mlt_properties_anim_get_int(properties, "matrix_size", position, length); double circle_radius = mlt_properties_anim_get_double(properties, "circle_radius", position, length); double gaussian_radius = mlt_properties_anim_get_double(properties, "gaussian_radius", position, length); double scale = mlt_profile_scale_width(mlt_service_profile(MLT_FILTER_SERVICE(filter)), *width); circle_radius *= scale; gaussian_radius *= scale; mlt_properties_set_int(properties, "_movit.parms.int.matrix_size", matrix_size); mlt_properties_set_double(properties, "_movit.parms.float.circle_radius", circle_radius); mlt_properties_set_double(properties, "_movit.parms.float.gaussian_radius", gaussian_radius); mlt_properties_set_double(properties, "_movit.parms.float.correlation", mlt_properties_anim_get_double(properties, "correlation", position, length)); mlt_properties_set_double(properties, "_movit.parms.float.noise", mlt_properties_anim_get_double(properties, "noise", position, length)); // DeconvolutionSharpenEffect compiles the matrix size into the shader, // so we need to regenerate the chain if this changes. char fingerprint[256]; snprintf(fingerprint, sizeof(fingerprint), "s=%d", matrix_size); mlt_properties_set(properties, "_movit fingerprint", fingerprint); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new DeconvolutionSharpenEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_deconvolution_sharpen_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set_int(properties, "matrix_size", 5); mlt_properties_set_double(properties, "circle_radius", 2.0); mlt_properties_set_double(properties, "gaussian_radius", 0.0); mlt_properties_set_double(properties, "correlation", 0.95); mlt_properties_set_double(properties, "noise", 0.01); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_deconvolution_sharpen.yml000664 000000 000000 00000002735 15172202314 025704 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.sharpen title: Deconvolution Sharpen (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > Deconvolution Sharpen is a filter that sharpens by way of deconvolution (i.e., trying to reverse the blur kernel, as opposed to just boosting high frequencies), more specifically by FIR Wiener filters. It is the same algorithm as used by the (now largely abandoned) Refocus plug-in for GIMP, and I suspect the same as in Photoshop's “Smart Sharpen” filter. The effect gives generally better results than unsharp masking, but can be very GPU intensive, and requires a fair bit of tweaking to get good results without ringing and/or excessive noise. parameters: - identifier: matrix_size title: Matrix Size type: integer minimum: 0 maximum: 25 default: 5 mutable: yes animation: yes - identifier: circle_radius title: Circle Radius type: float minimum: 0 default: 2 mutable: yes animation: yes - identifier: gaussian_radius title: Gaussian Radius type: float minimum: 0 default: 0 mutable: yes animation: yes - identifier: correlation title: Correlation type: float minimum: 0 default: 0.95 mutable: yes animation: yes - identifier: noise title: Noise Level type: float minimum: 0 default: 0.01 mutable: yes animation: yes mlt-7.38.0/src/modules/movit/filter_movit_diffusion.cpp000664 000000 000000 00000006530 15172202314 023240 0ustar00rootroot000000 000000 /* * filter_movit_diffusion.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_properties_set_double(properties, "_movit.parms.float.radius", mlt_properties_anim_get_double(properties, "radius", position, length)); mlt_properties_set_double(properties, "_movit.parms.float.blurred_mix_amount", mlt_properties_anim_get_double(properties, "mix", position, length)); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new DiffusionEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_movit_diffusion_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set_double(properties, "radius", 3.0); mlt_properties_set_double(properties, "mix", 0.3); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_diffusion.yml000664 000000 000000 00000002072 15172202314 023254 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.diffusion title: Diffusion (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > There are many different effects that go under the name of "diffusion", seemingly all of the inspired by the effect you get when you put a diffusion filter in front of your camera lens. The effect most people want is a general flattening/smoothing of the light, and reduction of fine detail (most notably, blemishes in people's skin), without ruining edges, which a regular blur would do. We do a relatively simple version, sometimes known as "white diffusion", where we first blur the picture, and then overlay it on the original using the original as a matte. parameters: - identifier: radius title: Blur Radius type: float minimum: 0.0 default: 3.0 mutable: yes animation: yes - identifier: mix title: Blurriness type: float minimum: 0.0 maximum: 1.0 default: 0.3 mutable: yes animation: yes mlt-7.38.0/src/modules/movit/filter_movit_flip.cpp000664 000000 000000 00000005012 15172202314 022176 0ustar00rootroot000000 000000 /* * filter_movit_mirror.cpp * Copyright (C) 2019-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include "mlt_flip_effect.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new Mlt::VerticalFlip); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_movit_flip_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_flip.yml000664 000000 000000 00000000337 15172202314 022222 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: movit.flip title: Flip (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en description: A simple vertical flip. tags: - Video mlt-7.38.0/src/modules/movit/filter_movit_glow.cpp000664 000000 000000 00000010026 15172202314 022215 0ustar00rootroot000000 000000 /* * filter_movit_glow.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); GlslManager::get_instance()->lock_service(frame); double radius = mlt_properties_anim_get_double(properties, "radius", position, length); radius *= mlt_profile_scale_width(mlt_service_profile(MLT_FILTER_SERVICE(filter)), *width); mlt_properties_set_double(properties, "_movit.parms.float.radius", radius); mlt_properties_set_double(properties, "_movit.parms.float.blurred_mix_amount", mlt_properties_anim_get_double(properties, "blur_mix", position, length)); mlt_properties_set_double(properties, "_movit.parms.float.highlight_cutoff", mlt_properties_anim_get_double(properties, "highlight_cutoff", position, length)); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new GlowEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_movit_glow_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set_double(properties, "radius", 20.0); mlt_properties_set_double(properties, "blur_mix", 1.0); mlt_properties_set_double(properties, "highlight_cutoff", 0.2); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_glow.yml000664 000000 000000 00000001453 15172202314 022240 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.glow title: Glow (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > Cut out the highlights of the image (everything above a certain threshold), blur them, and overlay them onto the original image. parameters: - identifier: radius title: Radius type: float minimum: 0.0 default: 20.0 mutable: yes animation: yes - identifier: blur_mix title: Highlight Blurriness type: float minimum: 0.0 maximum: 1.0 default: 1.0 mutable: yes animation: yes - identifier: highlight_cutoff title: Highlight Cutoff Threshold type: float minimum: 0.0 maximum: 1.0 default: 0.2 mutable: yes animation: yes mlt-7.38.0/src/modules/movit/filter_movit_lift_gamma_gain.cpp000664 000000 000000 00000013314 15172202314 024346 0ustar00rootroot000000 000000 /* * filter_lift_gamma_gain.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_properties_set_double(properties, "_movit.parms.vec3.lift[0]", mlt_properties_anim_get_double(properties, "lift_r", position, length)); mlt_properties_set_double(properties, "_movit.parms.vec3.lift[1]", mlt_properties_anim_get_double(properties, "lift_g", position, length)); mlt_properties_set_double(properties, "_movit.parms.vec3.lift[2]", mlt_properties_anim_get_double(properties, "lift_b", position, length)); mlt_properties_set_double(properties, "_movit.parms.vec3.gamma[0]", mlt_properties_anim_get_double(properties, "gamma_r", position, length)); mlt_properties_set_double(properties, "_movit.parms.vec3.gamma[1]", mlt_properties_anim_get_double(properties, "gamma_g", position, length)); mlt_properties_set_double(properties, "_movit.parms.vec3.gamma[2]", mlt_properties_anim_get_double(properties, "gamma_b", position, length)); mlt_properties_set_double(properties, "_movit.parms.vec3.gain[0]", mlt_properties_anim_get_double(properties, "gain_r", position, length)); mlt_properties_set_double(properties, "_movit.parms.vec3.gain[1]", mlt_properties_anim_get_double(properties, "gain_g", position, length)); mlt_properties_set_double(properties, "_movit.parms.vec3.gain[2]", mlt_properties_anim_get_double(properties, "gain_b", position, length)); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return 1; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new LiftGammaGainEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_lift_gamma_gain_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set_double(properties, "lift_r", 0.0); mlt_properties_set_double(properties, "lift_g", 0.0); mlt_properties_set_double(properties, "lift_b", 0.0); mlt_properties_set_double(properties, "gamma_r", 1.0); mlt_properties_set_double(properties, "gamma_g", 1.0); mlt_properties_set_double(properties, "gamma_b", 1.0); mlt_properties_set_double(properties, "gain_r", 1.0); mlt_properties_set_double(properties, "gain_g", 1.0); mlt_properties_set_double(properties, "gain_b", 1.0); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_lift_gamma_gain.yml000664 000000 000000 00000004036 15172202314 024366 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: movit.lift_gamma_gain title: Lift, Gamma, and Gain (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A simple lift/gamma/gain effect, used for color grading. notes: > Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, although all parameters affect the entire curve. Mathematically speaking, it is a bit unusual to look at gamma as a color, but it works pretty well in practice. The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). The lift is actually a case where we actually would _not_ want linear light; since black by definition becomes equal to the lift color, we want lift to be pretty close to black, but in linear light that means lift affects the rest of the curve relatively little. Thus, we actually convert to gamma 2.2 before lift, and then back again afterwards. (Gain and gamma are, up to constants, commutative with the de-gamma operation.) parameters: - identifier: lift_r title: Lift Red type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: lift_g title: Lift Green type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: lift_b title: Lift Blue type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: gamma_r title: Gamma Red type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gamma_g title: Gamma Green type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gamma_b title: Gamma Blue type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_r title: Gain Red type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_g title: Gain Green type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_b title: Gain Blue type: float minimum: 0.0 default: 1.0 mutable: yes mlt-7.38.0/src/modules/movit/filter_movit_mirror.cpp000664 000000 000000 00000004767 15172202314 022576 0ustar00rootroot000000 000000 /* * filter_movit_mirror.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new MirrorEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_movit_mirror_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_mirror.yml000664 000000 000000 00000000352 15172202314 022577 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: movit.mirror title: Mirror (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en description: A simple horizontal mirroring. tags: - Video mlt-7.38.0/src/modules/movit/filter_movit_opacity.cpp000664 000000 000000 00000007045 15172202314 022724 0ustar00rootroot000000 000000 /* * filter_movit_opacity.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); double opacity = mlt_properties_anim_get_double(properties, "opacity", position, length); double alpha = mlt_properties_anim_get_double(properties, "alpha", position, length); mlt_properties_set_double(properties, "_movit.parms.vec4.factor[0]", opacity); mlt_properties_set_double(properties, "_movit.parms.vec4.factor[1]", opacity); mlt_properties_set_double(properties, "_movit.parms.vec4.factor[2]", opacity); mlt_properties_set_double(properties, "_movit.parms.vec4.factor[3]", alpha >= 0 ? alpha : opacity); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new MultiplyEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" mlt_filter filter_movit_opacity_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set(properties, "opacity", arg ? arg : "1"); mlt_properties_set_double(properties, "alpha", -1.0); filter->process = process; } return filter; } mlt-7.38.0/src/modules/movit/filter_movit_opacity.yml000664 000000 000000 00000002334 15172202314 022737 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.opacity title: Opacity (GLSL) version: 5 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: Adjust the opacity of an image through the alpha channel. notes: > When used in some transitions this will cause this video to be mixed with the other video. If the video this is applied to already has an alpha channel, then this preserves that by multiplying the opacity level with the alpha channel. This filter is especially handy when used in conjunction with movit.overlay. You can also use this to fade a clip from and to black. parameters: - identifier: opacity title: Opacity type: float minimum: 0 default: 1 mutable: yes animation: yes - identifier: alpha title: Alpha description: > When this is less than zero, the alpha component of the Movit multiply effect follows opacity. Otherwise, you can set this to another value to control the alpha component independently. If you set this to 1, then the opacity parameter can be used to fade to and from black. type: float minimum: -1 maximum: 1 default: -1 mutable: yes animation: yes mlt-7.38.0/src/modules/movit/filter_movit_overlay_mode.cpp000664 000000 000000 00000003112 15172202314 023730 0ustar00rootroot000000 000000 /* * filter_movit_overlay_mode.cpp * Copyright (C) 2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_properties_set(MLT_FRAME_PROPERTIES(frame), "movit.overlay.compositing", mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "mode")); return frame; } extern "C" mlt_filter filter_movit_overlay_mode_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "mode", arg ? arg : "0"); } return filter; } mlt-7.38.0/src/modules/movit/filter_movit_overlay_mode.yml000664 000000 000000 00000001051 15172202314 023747 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.overlay_mode title: Movit Overlay Mode version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: GPLv2 language: en tags: - Video description: Change the blend mode used by the movit.overlay transition. parameters: - identifier: mode argument: yes title: Blend mode type: integer description: > This filter can be used to change the blend mode for a single clip when using the movit.overlay transition to composite tracks. default: 0 widget: combo mlt-7.38.0/src/modules/movit/filter_movit_rect.cpp000664 000000 000000 00000005067 15172202314 022213 0ustar00rootroot000000 000000 /* * filter_movit_rect.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filter_glsl_manager.h" #include using namespace movit; static mlt_frame process(mlt_filter filter, mlt_frame frame) { // Drive the resize and resample filters on the b_frame for these composite parameters mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_props = MLT_FRAME_PROPERTIES(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); auto rect = mlt_properties_anim_get_rect(properties, "rect", position, length); mlt_properties_set_rect(frame_props, "resize.rect", rect); mlt_properties_set(frame_props, "resize.fill", mlt_properties_get(properties, "fill")); mlt_properties_set(frame_props, "resize.distort", mlt_properties_get(properties, "distort")); mlt_properties_set(frame_props, "resize.halign", mlt_properties_get(properties, "halign")); mlt_properties_set(frame_props, "resize.valign", mlt_properties_get(properties, "valign")); return frame; } extern "C" mlt_filter filter_movit_rect_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "rect", arg); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "fill", 1); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "distort", 0); filter->process = process; } return filter; } mlt-7.38.0/src/modules/movit/filter_movit_rect.yml000664 000000 000000 00000003076 15172202314 022230 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.rect title: Position and Size (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > Change the coordinates and scale to fit within a rectangle. parameters: - identifier: rect title: Rectangle type: rect description: Rectangle specifying the position of the top, left corner and size. mutable: yes animation: yes - identifier: distort title: Ignore aspect ratio description: > Determines whether the image aspect ratio will be distorted while scaling to completely fill the geometry rectangle. type: boolean default: 0 mutable: yes widget: checkbox - identifier: fill title: Upscale to Fill description: > Determines whether the image will be scaled up to fill the rectangle or whether the size will be constrained to 100% of the profile resolution. type: integer default: 1 minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - center - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo mlt-7.38.0/src/modules/movit/filter_movit_resample.cpp000664 000000 000000 00000011432 15172202314 023057 0ustar00rootroot000000 000000 /* * filter_movit_resample.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include "optional_effect.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Correct width/height if necessary if (*width < 0 || *height < 1) { *width = profile->width; *height = profile->height; } int iwidth = *width; int iheight = *height; double factor = mlt_properties_get_double(filter_properties, "factor"); factor = factor > 0 ? factor : 1.0; int owidth = *width * factor; int oheight = *height * factor; // If meta.media.width/height exist, we want that as minimum information if (mlt_properties_get_int(properties, "meta.media.width")) { iwidth = mlt_properties_get_int(properties, "meta.media.width"); iheight = mlt_properties_get_int(properties, "meta.media.height"); } mlt_properties_set_int(properties, "rescale_width", *width); mlt_properties_set_int(properties, "rescale_height", *height); // Deinterlace if height is changing to prevent fields mixing on interpolation if (iheight != oheight) mlt_properties_set_int(properties, "consumer.progressive", 1); GlslManager::get_instance()->lock_service(frame); mlt_properties_set_int(filter_properties, "_movit.parms.int.width", owidth); mlt_properties_set_int(filter_properties, "_movit.parms.int.height", oheight); bool disable = (iwidth == owidth && iheight == oheight); mlt_properties_set_int(filter_properties, "_movit.parms.int.disable", disable); *width = owidth; *height = oheight; GlslManager::get_instance()->unlock_service(frame); // Get the image as requested if (*format != mlt_image_none) *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, &iwidth, &iheight, writable); if (*width < 1 || *height < 1 || iwidth < 1 || iheight < 1 || owidth < 1 || oheight < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d, in: %dx%d, out: %dx%d", *width, *height, iwidth, iheight, owidth, oheight); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); Effect *effect = GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new OptionalEffect); // This needs to be something else than 0x0 at chain finalization time. bool ok = effect->set_int("width", owidth); ok |= effect->set_int("height", oheight); assert(ok); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" mlt_filter filter_movit_resample_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); filter->process = process; } return filter; } mlt-7.38.0/src/modules/movit/filter_movit_resample.yml000664 000000 000000 00000000704 15172202314 023076 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.resample title: Image Scaler (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en description: Change the resolution of a video or image. tags: - Video - Hidden notes: > This is not intended to be created directly. Rather, the rescale filter loads it if it is available to normalize video and image inputs to the consumer/profile resolution. mlt-7.38.0/src/modules/movit/filter_movit_resize.cpp000664 000000 000000 00000021317 15172202314 022553 0ustar00rootroot000000 000000 /* * filter_movit_resize.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "filter_glsl_manager.h" #include "optional_effect.h" #include #include using namespace movit; static float alignment_parse(char *align) { int ret = 0.0f; if (align == NULL) ; else if (isdigit(align[0])) ret = atoi(align); else if (align[0] == 'c' || align[0] == 'm') ret = 1.0f; else if (align[0] == 'r' || align[0] == 'b') ret = 2.0f; return ret; } static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Retrieve the aspect ratio double aspect_ratio = mlt_frame_get_aspect_ratio(frame); double consumer_aspect = mlt_profile_sar(profile); // Correct Width/height if necessary if (*width < 1 || *height < 1) { *width = profile->width; *height = profile->height; } // Assign requested width/height from our subordinate int owidth = *width; int oheight = *height; // Use a mlt_rect to compute position and size mlt_rect rect; rect.x = rect.y = 0.0; rect.w = rect.h = 1.0; if (mlt_properties_get(properties, "resize.rect")) { mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); rect = mlt_properties_anim_get_rect(properties, "resize.rect", position, length); if (strchr(mlt_properties_get(properties, "resize.rect"), '%')) { rect.x *= profile->width; rect.w *= profile->width; rect.y *= profile->height; rect.h *= profile->height; } double scale = mlt_profile_scale_width(profile, *width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); rect.y *= scale; rect.h *= scale; if (!mlt_properties_get_int(properties, "resize.fill")) { int x = mlt_properties_get_int(properties, "meta.media.width"); owidth = lrintf(rect.w > x ? x : rect.w); x = mlt_properties_get_int(properties, "meta.media.height"); oheight = lrintf(rect.h > x ? x : rect.h); } else { owidth = lrintf(rect.w); oheight = lrintf(rect.h); } } // Check for the special case - no aspect ratio means no problem :-) if (aspect_ratio == 0.0) aspect_ratio = consumer_aspect; // Reset the aspect ratio mlt_properties_set_double(properties, "aspect_ratio", aspect_ratio); // Skip processing if requested. char *rescale = mlt_properties_get(properties, "consumer.rescale"); if (*format == mlt_image_none || (rescale && !strcmp(rescale, "none"))) return mlt_frame_get_image(frame, image, format, width, height, writable); if (mlt_properties_get_int(properties, "resize.distort") == 0) { // Normalize the input and out display aspect int normalized_width = profile->width; int normalized_height = profile->height; int real_width = mlt_properties_get_int(properties, "meta.media.width"); int real_height = mlt_properties_get_int(properties, "meta.media.height"); if (real_width == 0) real_width = mlt_properties_get_int(properties, "width"); if (real_height == 0) real_height = mlt_properties_get_int(properties, "height"); double input_ar = aspect_ratio * real_width / real_height; double output_ar = consumer_aspect * owidth / oheight; // Optimised for the input_ar > output_ar case (e.g. widescreen on standard) int scaled_width = lrint((input_ar * normalized_width) / output_ar); int scaled_height = normalized_height; // Now ensure that our images fit in the output frame if (scaled_width > normalized_width) { scaled_width = normalized_width; scaled_height = lrint((output_ar * normalized_height) / input_ar); } // Now calculate the actual image size that we want owidth = lrint(scaled_width * owidth / normalized_width); oheight = lrint(scaled_height * oheight / normalized_height); mlt_log_debug(MLT_FILTER_SERVICE(filter), "real %dx%d normalized %dx%d output %dx%d sar %f in-dar %f out-dar %f\n", real_width, real_height, normalized_width, normalized_height, owidth, oheight, aspect_ratio, input_ar, output_ar); // Tell frame we have conformed the aspect to the consumer mlt_frame_set_aspect_ratio(frame, consumer_aspect); } mlt_properties_set_int(properties, "resize.distort", 0); // Now get the image *format = mlt_image_movit; error = mlt_frame_get_image(frame, image, format, &owidth, &oheight, writable); // Offset the position according to alignment if (mlt_properties_get(properties, "resize.rect")) { // default top left if rect supplied float w = float(rect.w - owidth); float h = float(rect.h - oheight); rect.x += w * alignment_parse(mlt_properties_get(properties, "resize.halign")) / 2.0f; rect.y += h * alignment_parse(mlt_properties_get(properties, "resize.valign")) / 2.0f; } else { // default center if no rect float w = float(*width - owidth); float h = float(*height - oheight); rect.x = w * 0.5f; rect.y = h * 0.5f; } if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } if (!error) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_properties_set_int(filter_properties, "_movit.parms.int.width", *width); mlt_properties_set_int(filter_properties, "_movit.parms.int.height", *height); mlt_properties_set_double(filter_properties, "_movit.parms.float.left", rect.x); mlt_properties_set_double(filter_properties, "_movit.parms.float.top", rect.y); bool disable = (*width == owidth && *height == oheight && rect.x == 0 && rect.y == 0); mlt_properties_set_int(filter_properties, "_movit.parms.int.disable", disable); GlslManager::get_instance()->unlock_service(frame); GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new OptionalEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" mlt_filter filter_movit_resize_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); filter->process = process; } return filter; } mlt-7.38.0/src/modules/movit/filter_movit_resize.yml000664 000000 000000 00000001054 15172202314 022566 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.resize title: Image Padding (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en description: Pad an image with black to fulfill the requested image size. tags: - Video - Hidden notes: > Normally resize is used to pad the producer's output to what the consumer has requested after an upstream filter first scales the image to maximise usage of the image area. This filter is automatically invoked by the loader as part of image normalization. mlt-7.38.0/src/modules/movit/filter_movit_saturation.cpp000664 000000 000000 00000006433 15172202314 023445 0ustar00rootroot000000 000000 /* * filter_movit_saturation.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_properties_set_double(properties, "_movit.parms.float.saturation", mlt_properties_anim_get_double(properties, "saturation", position, length)); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new SaturationEffect()); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_movit_saturation_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set(properties, "saturation", arg ? arg : "1.0"); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_saturation.yml000664 000000 000000 00000001242 15172202314 023455 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.saturation title: Saturation (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A simple desaturation/saturation effect. We use the Rec. 709 definition of luminance (in linear light, of course) and linearly interpolate between that (saturation=0) and the original signal (saturation=1). Extrapolating that curve further (ie., saturation > 1) gives us increased saturation if so desired. parameters: - identifier: saturation title: Saturation type: float minimum: 0 default: 1 mutable: yes animation: yes mlt-7.38.0/src/modules/movit/filter_movit_vignette.cpp000664 000000 000000 00000007074 15172202314 023103 0ustar00rootroot000000 000000 /* * filter_movit_vignette.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_properties_set_double(properties, "_movit.parms.float.radius", mlt_properties_anim_get_double(properties, "radius", position, length)); mlt_properties_set_double(properties, "_movit.parms.float.inner_radius", mlt_properties_anim_get_double(properties, "inner_radius", position, length)); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new VignetteEffect()); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_movit_vignette_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); filter->process = process; mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "radius", 0.3); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "inner_radius", 0.3); } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_vignette.yml000664 000000 000000 00000001224 15172202314 023111 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: movit.vignette title: Vignette (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A circular vignette, falling off as cos² of the distance from the center (the classic formula for approximating a real lens). parameters: - identifier: radius title: Outer Radius type: float minimum: 0.0 maximum: 1.0 default: 0.3 mutable: yes animation: yes - identifier: inner_radius title: Inner Radius type: float minimum: 0.0 maximum: 1.0 default: 0.3 mutable: yes animation: yes mlt-7.38.0/src/modules/movit/filter_movit_white_balance.cpp000664 000000 000000 00000010022 15172202314 024026 0ustar00rootroot000000 000000 /* * filter_white_balance.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static double srgb8_to_linear(int c) { double x = c / 255.0f; if (x < 0.04045f) { return (1.0 / 12.92f) * x; } else { return pow((x + 0.055) * (1.0 / 1.055f), 2.4); } } static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); GlslManager::get_instance()->lock_service(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); int color_int = mlt_properties_anim_get_int(properties, "neutral_color", position, length); RGBTriplet color(srgb8_to_linear((color_int >> 24) & 0xff), srgb8_to_linear((color_int >> 16) & 0xff), srgb8_to_linear((color_int >> 8) & 0xff)); mlt_properties_set_double(properties, "_movit.parms.vec3.neutral_color[0]", color.r); mlt_properties_set_double(properties, "_movit.parms.vec3.neutral_color[1]", color.g); mlt_properties_set_double(properties, "_movit.parms.vec3.neutral_color[2]", color.b); double output_color_temperature = mlt_properties_anim_get_double(properties, "color_temperature", position, length); mlt_properties_set_double(properties, "_movit.parms.float.output_color_temperature", output_color_temperature); GlslManager::get_instance()->unlock_service(frame); *format = mlt_image_movit; int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(MLT_FILTER_SERVICE(filter), frame, (mlt_service) *image); GlslManager::set_effect(MLT_FILTER_SERVICE(filter), frame, new WhiteBalanceEffect); *image = (uint8_t *) MLT_FILTER_SERVICE(filter); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_white_balance_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (filter = mlt_filter_new())) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); glsl->add_ref(properties); mlt_properties_set(properties, "neutral_color", arg ? arg : "#7f7f7f"); mlt_properties_set_double(properties, "color_temperature", 6500.0); filter->process = process; } return filter; } } mlt-7.38.0/src/modules/movit/filter_movit_white_balance.yml000664 000000 000000 00000001075 15172202314 024055 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: movit.white_balance title: White Balance (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: Color correction in LMS color space. parameters: - identifier: neutral_color title: Neutral Color type: string widget: color default: 0x7f7f7f00 mutable: yes - identifier: color_temperature title: Color Temperature type: float minimum: 1000.0 maximum: 15000.0 default: 6500.0 unit: Kelvin mutable: yes mlt-7.38.0/src/modules/movit/mlt_flip_effect.h000664 000000 000000 00000002640 15172202314 021254 0ustar00rootroot000000 000000 /* * mlt_flip_effect.h * Copyright (C) 2013 Dan Dennedy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MLT_FLIP_EFFECT_H #define MLT_FLIP_EFFECT_H #include namespace Mlt { class VerticalFlip : public movit::Effect { public: VerticalFlip() {} virtual std::string effect_type_id() const { return "MltVerticalFlip"; } std::string output_fragment_shader() { return "vec4 FUNCNAME(vec2 tc) { tc.y = 1.0 - tc.y; return INPUT(tc); }\n"; } virtual bool needs_linear_light() const { return false; } virtual bool needs_srgb_primaries() const { return false; } AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; } }; } // namespace Mlt #endif // MLT_FLIP_EFFECT_H mlt-7.38.0/src/modules/movit/mlt_movit_input.cpp000664 000000 000000 00000010540 15172202314 021714 0ustar00rootroot000000 000000 /* * mlt_movit_input.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mlt_movit_input.h" extern "C" { #include } using namespace movit; MltInput::MltInput(mlt_image_format format) : m_format(format) , m_width(0) , m_height(0) , input(0) , isRGB(true) {} MltInput::~MltInput() {} void MltInput::useFlatInput(MovitPixelFormat pix_fmt, unsigned width, unsigned height) { // In case someone didn't think properly and passed -1 to an unsigned if (int(width) < 1 || int(height) < 1) { mlt_log_error(NULL, "Invalid size %dx%d\n", width, height); return; } if (!input) { m_width = width; m_height = height; ImageFormat image_format; image_format.color_space = COLORSPACE_sRGB; image_format.gamma_curve = GAMMA_REC_709; // GAMMA_sRGB causes a color level problem input = new FlatInput(image_format, pix_fmt, GL_UNSIGNED_BYTE, width, height); } } void MltInput::useYCbCrInput(const ImageFormat &image_format, const YCbCrFormat &ycbcr_format, unsigned width, unsigned height) { // In case someone didn't think properly and passed -1 to an unsigned if (int(width) < 1 || int(height) < 1) { mlt_log_error(NULL, "Invalid size %dx%d\n", width, height); return; } if (!input) { m_width = width; m_height = height; input = new YCbCrInput(image_format, ycbcr_format, width, height, YCBCR_INPUT_PLANAR, ycbcr_format.num_levels == 1024 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE); isRGB = false; m_ycbcr_format = ycbcr_format; } } void MltInput::set_pixel_data(const unsigned char *data) { if (!input) { mlt_log_error(NULL, "No input for set_pixel_data"); return; } // In case someone didn't think properly and passed -1 to an unsigned if (int(m_width) < 1 || int(m_height) < 1) { mlt_log_error(NULL, "Invalid size %dx%d\n", m_width, m_height); return; } if (isRGB) { FlatInput *flat = (FlatInput *) input; flat->set_pixel_data(data); } else if (m_ycbcr_format.num_levels == 1024) { YCbCrInput *ycbcr = (YCbCrInput *) input; auto p = reinterpret_cast(data); ycbcr->set_pixel_data(0, p); ycbcr->set_pixel_data(1, &p[m_width * m_height]); ycbcr->set_pixel_data(2, &p[m_width * m_height + (m_width / m_ycbcr_format.chroma_subsampling_x * m_height / m_ycbcr_format.chroma_subsampling_y)]); } else { YCbCrInput *ycbcr = (YCbCrInput *) input; ycbcr->set_pixel_data(0, data); ycbcr->set_pixel_data(1, &data[m_width * m_height]); ycbcr->set_pixel_data(2, &data[m_width * m_height + (m_width / m_ycbcr_format.chroma_subsampling_x * m_height / m_ycbcr_format.chroma_subsampling_y)]); } } void MltInput::invalidate_pixel_data() { if (!input) { mlt_log_error(NULL, "Invalidate called without input\n"); return; } if (isRGB) { FlatInput *flat = (FlatInput *) input; flat->invalidate_pixel_data(); } else { YCbCrInput *ycbcr = (YCbCrInput *) input; ycbcr->invalidate_pixel_data(); } } mlt-7.38.0/src/modules/movit/mlt_movit_input.h000664 000000 000000 00000003621 15172202314 021363 0ustar00rootroot000000 000000 /* * mlt_movit_input.h * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MLT_MOVIT_INPUT_H #define MLT_MOVIT_INPUT_H #include #include #include #include class MltInput { public: MltInput(mlt_image_format format); ~MltInput(); void useFlatInput(movit::MovitPixelFormat pix_fmt, unsigned width, unsigned height); void useYCbCrInput(const movit::ImageFormat &image_format, const movit::YCbCrFormat &ycbcr_format, unsigned width, unsigned height); void set_pixel_data(const unsigned char *data); void invalidate_pixel_data(); movit::Input *get_input() { return input; } // The original pixel format that was used to create this MltInput, // in case we change our mind later and want to convert on the CPU instead. mlt_image_format get_format() const { return m_format; } private: mlt_image_format m_format; unsigned m_width, m_height; // Note: Owned by the EffectChain, so should not be deleted by us. movit::Input *input; bool isRGB; movit::YCbCrFormat m_ycbcr_format; }; #endif // MLT_MOVIT_INPUT_H mlt-7.38.0/src/modules/movit/optional_effect.h000664 000000 000000 00000001472 15172202314 021275 0ustar00rootroot000000 000000 #ifndef OPTIONAL_EFFECT_H #define OPTIONAL_EFFECT_H #include #include #include // A wrapper effect that, at rewrite time, can remove itself entirely from the loop. // It does so if "disable" is set to a nonzero value at finalization time. template class OptionalEffect : public T { public: OptionalEffect() : disable(0) { this->register_int("disable", &disable); } virtual std::string effect_type_id() const { return "OptionalEffect[" + T::effect_type_id() + "]"; } virtual void rewrite_graph(movit::EffectChain *graph, movit::Node *self) { if (disable) { assert(self->incoming_links.size() == 1); graph->replace_sender(self, self->incoming_links[0]); self->disabled = true; } else { T::rewrite_graph(graph, self); } } private: int disable; }; #endif mlt-7.38.0/src/modules/movit/transition_movit_luma.cpp000664 000000 000000 00000021305 15172202314 023112 0ustar00rootroot000000 000000 /* * transition_movit_luma.cpp * Copyright (C) 2014-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "filter_glsl_manager.h" #include #include #include #include #include using namespace movit; static int get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error; // Get the transition object mlt_transition transition = (mlt_transition) mlt_frame_pop_service(a_frame); mlt_service service = MLT_TRANSITION_SERVICE(transition); // Get the b frame from the stack mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame(a_frame); mlt_frame c_frame = (mlt_frame) mlt_frame_pop_frame(a_frame); // Get the properties of the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); mlt_service_lock(service); // Get the transition parameters mlt_position position = mlt_transition_get_position(transition, a_frame); mlt_position length = mlt_transition_get_length(transition); int reverse = mlt_properties_get_int(properties, "reverse"); double mix = mlt_transition_get_progress(transition, a_frame); double softness = mlt_properties_anim_get_double(properties, "softness", position, length); int invert = mlt_properties_get_int(properties, "invert"); // Make progress appear linear using a gamma curve similar to sRGB or Rec. 709 if (invert) mix = 1.0 - pow(1.0 - mix, 2.0); else mix = pow(mix, 2.0); double inverse = 1.0 - mix; if (c_frame) { // Set the Movit parameters. mlt_properties_set(properties, "_movit.parms.float.strength_first", nullptr); mlt_properties_set(properties, "_movit.parms.float.strength_second", nullptr); mlt_properties_set_double(properties, "_movit.parms.float.progress", reverse ? inverse : mix); mlt_properties_set_double(properties, "_movit.parms.float.transition_width", 1.0 / (softness + 1.0e-4)); mlt_properties_set_int(properties, "_movit.parms.int.inverse", !invert); uint8_t *a_image, *b_image, *c_image; // Get the images. *format = mlt_image_movit; error = mlt_frame_get_image(a_frame, &a_image, format, width, height, writable); error = mlt_frame_get_image(b_frame, &b_image, format, width, height, writable); error = mlt_frame_get_image(c_frame, &c_image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(service, "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(service, a_frame, (mlt_service) a_image); GlslManager::set_effect_secondary_input(service, a_frame, (mlt_service) b_image, b_frame); GlslManager::set_effect_third_input(service, a_frame, (mlt_service) c_image, c_frame); GlslManager::set_effect(service, a_frame, new LumaMixEffect()); } else { // Set the Movit parameters. mlt_properties_set(properties, "_movit.parms.int.inverse", nullptr); mlt_properties_set(properties, "_movit.parms.float.progress", nullptr); mlt_properties_set(properties, "_movit.parms.float.transition_width", nullptr); mlt_properties_set_double(properties, "_movit.parms.float.strength_first", reverse ? mix : inverse); mlt_properties_set_double(properties, "_movit.parms.float.strength_second", reverse ? inverse : mix); uint8_t *a_image, *b_image; // Get the two images. *format = mlt_image_movit; error = mlt_frame_get_image(a_frame, &a_image, format, width, height, writable); error = mlt_frame_get_image(b_frame, &b_image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(service, "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(service, a_frame, (mlt_service) a_image); GlslManager::set_effect_secondary_input(service, a_frame, (mlt_service) b_image, b_frame); GlslManager::set_effect(service, a_frame, new MixEffect()); } *image = (uint8_t *) service; mlt_service_unlock(service); return error; } static mlt_frame process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); // Obtain the wipe producer. char *resource = mlt_properties_get(properties, "resource"); char *last_resource = mlt_properties_get(properties, "_resource"); auto producer = (mlt_producer) mlt_properties_get_data(properties, "instance", nullptr); // If we haven't created the wipe producer or it has changed if (resource) if (!producer || strcmp(resource, last_resource)) { mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(transition)); // Store the last resource now mlt_properties_set(properties, "_resource", resource); producer = mlt_factory_producer(profile, nullptr, resource); if (producer) { mlt_properties_set(MLT_PRODUCER_PROPERTIES(producer), "eof", "loop"); } mlt_properties_set_data(properties, "instance", producer, 0, (mlt_destructor) mlt_producer_close, nullptr); } if (producer) { mlt_frame wipe = nullptr; mlt_position position = mlt_transition_get_position(transition, a_frame); mlt_properties_pass(MLT_PRODUCER_PROPERTIES(producer), properties, "producer."); mlt_producer_seek(producer, position); if (mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &wipe, 0) == 0) { char name[64]; snprintf(name, sizeof(name), "movit.luma %s", mlt_properties_get(properties, "_unique_id")); mlt_properties_set_data(MLT_FRAME_PROPERTIES(a_frame), name, wipe, 0, (mlt_destructor) mlt_frame_close, nullptr); mlt_properties_set_int(MLT_FRAME_PROPERTIES(wipe), "distort", 1); mlt_frame_push_frame(a_frame, wipe); } else { mlt_frame_push_frame(a_frame, nullptr); } } else { // We may still not have a producer in which case, dissolve mlt_frame_push_frame(a_frame, nullptr); } mlt_frame_push_frame(a_frame, b_frame); mlt_frame_push_service(a_frame, transition); mlt_frame_push_get_image(a_frame, get_image); return a_frame; } extern "C" mlt_transition transition_movit_luma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_transition transition = nullptr; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (transition = mlt_transition_new())) { transition->process = process; mlt_properties_set(MLT_TRANSITION_PROPERTIES(transition), "resource", arg); // Inform apps and framework that this is a video only transition mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "_transition_type", 1); } return transition; } mlt-7.38.0/src/modules/movit/transition_movit_luma.yml000664 000000 000000 00000002230 15172202314 023125 0ustar00rootroot000000 000000 schema_version: 0.2 type: transition identifier: movit.luma_mix title: Wipe (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: A generic dissolve and wipe transition processor. parameters: - identifier: resource argument: yes title: Wipe File description: Gradient image or dissolve if not supplied. type: string mutable: yes - identifier: softness title: Softness description: The blurriness of the edges of the transition. type: float minimum: 0 maximum: 1 default: 0 mutable: yes - identifier: reverse title: Reverse type: integer mutable: yes description: Reverse the direction of the transition. default: 0 minimum: 0 maximum: 1 widget: checkbox - identifier: invert title: Invert type: integer mutable: yes description: Invert the wipe. default: 0 minimum: 0 maximum: 1 widget: checkbox - identifier: producer.* title: Producer mutable: yes description: > Properties may be set on the encapsulated producer that reads resource. readonly: no mlt-7.38.0/src/modules/movit/transition_movit_mix.cpp000664 000000 000000 00000010470 15172202314 022752 0ustar00rootroot000000 000000 /* * transition_movit_mix.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include "mlt_flip_effect.h" #include "mlt_movit_input.h" #include #include #include #include using namespace movit; static int get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error; // Get the b frame from the stack mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame(a_frame); // Get the transition object mlt_transition transition = (mlt_transition) mlt_frame_pop_service(a_frame); mlt_service service = MLT_TRANSITION_SERVICE(transition); mlt_service_lock(service); // Get the properties of the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); // Get the transition parameters mlt_position position = mlt_transition_get_position(transition, a_frame); mlt_position length = mlt_transition_get_length(transition); int reverse = mlt_properties_get_int(properties, "reverse"); const char *mix_str = mlt_properties_get(properties, "mix"); double mix = (mix_str && strlen(mix_str) > 0) ? mlt_properties_anim_get_double(properties, "mix", position, length) : mlt_transition_get_progress(transition, a_frame); double inverse = 1.0 - mix; // Set the Movit parameters. mlt_properties_set_double(properties, "_movit.parms.float.strength_first", reverse ? mix : inverse); mlt_properties_set_double(properties, "_movit.parms.float.strength_second", reverse ? inverse : mix); uint8_t *a_image, *b_image; // Get the two images. *format = mlt_image_movit; error = mlt_frame_get_image(a_frame, &a_image, format, width, height, writable); error = mlt_frame_get_image(b_frame, &b_image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(service, "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(service, a_frame, (mlt_service) a_image); GlslManager::set_effect_secondary_input(service, a_frame, (mlt_service) b_image, b_frame); GlslManager::set_effect(service, a_frame, new MixEffect()); *image = (uint8_t *) service; mlt_service_unlock(service); return error; } static mlt_frame process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { mlt_frame_push_service(a_frame, transition); mlt_frame_push_frame(a_frame, b_frame); mlt_frame_push_get_image(a_frame, get_image); return a_frame; } extern "C" mlt_transition transition_movit_mix_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_transition transition = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (transition = mlt_transition_new())) { transition->process = process; mlt_properties_set(MLT_TRANSITION_PROPERTIES(transition), "mix", arg); // Inform apps and framework that this is a video only transition mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "_transition_type", 1); } return transition; } mlt-7.38.0/src/modules/movit/transition_movit_mix.yml000664 000000 000000 00000001254 15172202314 022771 0ustar00rootroot000000 000000 schema_version: 7.0 type: transition identifier: movit.mix title: Dissolve (GLSL) version: 1 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: A simple video cross-fade or mixing effect. parameters: - identifier: mix argument: yes title: Mix Level description: Performs a dissolve if a mix level is not supplied. type: float minimum: 0 maximum: 1 mutable: yes animation: yes - identifier: reverse title: Reverse type: integer mutable: yes description: > Reverse the direction of the transition. default: 0 minimum: 0 maximum: 1 widget: checkbox mlt-7.38.0/src/modules/movit/transition_movit_overlay.cpp000664 000000 000000 00000007263 15172202314 023644 0ustar00rootroot000000 000000 /* * transition_movit_overlay.cpp * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include #include #include #include using namespace movit; static int get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error; // Get the b frame from the stack mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame(a_frame); // Get the transition object mlt_transition transition = (mlt_transition) mlt_frame_pop_service(a_frame); mlt_service service = MLT_TRANSITION_SERVICE(transition); mlt_service_lock(service); // Set the Movit parameters. mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); int compositing = mlt_properties_get_int(properties, "compositing"); if (mlt_properties_exists(MLT_FRAME_PROPERTIES(b_frame), "movit.overlay.compositing")) { compositing = mlt_properties_get_int(MLT_FRAME_PROPERTIES(b_frame), "movit.overlay.compositing"); } mlt_properties_set_int(properties, "_movit.parms.int.blend_mode", compositing); uint8_t *a_image, *b_image; // Get the two images. *format = mlt_image_movit; mlt_frame_get_image(a_frame, &a_image, format, width, height, writable); error = mlt_frame_get_image(b_frame, &b_image, format, width, height, writable); if (*width < 1 || *height < 1) { mlt_log_error(service, "Invalid size for get_image: %dx%d", *width, *height); return error; } GlslManager::set_effect_input(service, a_frame, (mlt_service) a_image); GlslManager::set_effect_secondary_input(service, a_frame, (mlt_service) b_image, b_frame); GlslManager::set_effect(service, a_frame, new OverlayEffect); *image = (uint8_t *) service; mlt_service_unlock(service); return error; } static mlt_frame process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { mlt_frame_push_service(a_frame, transition); mlt_frame_push_frame(a_frame, b_frame); mlt_frame_push_get_image(a_frame, get_image); return a_frame; } extern "C" mlt_transition transition_movit_overlay_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_transition transition = NULL; GlslManager *glsl = GlslManager::get_instance(); if (glsl && (transition = mlt_transition_new())) { transition->process = process; // Inform apps and framework that this is a video only transition mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "_transition_type", 1); } return transition; } mlt-7.38.0/src/modules/movit/transition_movit_overlay.yml000664 000000 000000 00000002142 15172202314 023652 0ustar00rootroot000000 000000 schema_version: 7.0 type: transition identifier: movit.overlay title: Overlay (GLSL) version: 2 copyright: Meltytech, LLC creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A video overlay or alpha-compositing effect using a Porter-Duff operation or SVG 1.2 blend mode. parameters: - identifier: compositing title: Composition mode type: integer description: > The Porter-Duff operation or SVG 1.2 blend mode to use. mutable: yes values: - 0 (source over) - 1 (destination over) - 2 (clear) - 3 (source) - 4 (destination) - 5 (source in) - 6 (destination in) - 7 (source out) - 8 (destination out) - 9 (source atop) - 10 (destination atop) - 11 (xor) - 12 (plus) - 13 (multiply) - 14 (screen) - 15 (overlay) - 16 (darken) - 17 (lighten) - 18 (color dodge) - 19 (color burn) - 20 (hard light) - 21 (soft light) - 22 (difference) - 23 (exclusion) default: 0 minimum: 0 maximum: 23 widget: combo mlt-7.38.0/src/modules/ndi/000775 000000 000000 00000000000 15172202314 015373 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/ndi/CMakeLists.txt000664 000000 000000 00000001344 15172202314 020135 0ustar00rootroot000000 000000 add_library(mltndi MODULE consumer_ndi.c factory.c factory.h producer_ndi.c ) include(GenerateExportHeader) generate_export_header(mltndi) file(GLOB YML "*.yml") add_custom_target(Other_ndi_Files SOURCES ${YML} ) target_compile_options(mltndi PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltndi PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltndi PRIVATE mlt NDI::NDI) if(CPU_SSE) target_compile_definitions(mltndi PRIVATE USE_SSE) endif() set_target_properties(mltndi PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltndi LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_ndi.yml producer_ndi.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/ndi) mlt-7.38.0/src/modules/ndi/consumer_ndi.c000664 000000 000000 00000027342 15172202314 020234 0ustar00rootroot000000 000000 /* * consumer_ndi.c -- output through NewTek NDI * Copyright (C) 2016 Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_FORMAT_MACROS /* see inttypes.h */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include #include #include #include #include #include "factory.h" #include "Processing.NDI.Lib.h" typedef struct { struct mlt_consumer_s parent; int f_running, f_exit; char *arg; pthread_t th; int count; int sliced_swab; } consumer_ndi_t; static void *consumer_ndi_feeder(void *p) { int i; mlt_frame last = NULL; mlt_consumer consumer = p; mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); consumer_ndi_t *self = (consumer_ndi_t *) consumer->child; mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__); const NDIlib_send_create_t ndi_send_desc = {self->arg, NULL, true, false}; NDIlib_send_instance_t ndi_send = NDIlib_send_create(&ndi_send_desc); if (!ndi_send) { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "%s: NDIlib_send_create failed\n", __FUNCTION__); return NULL; } char *ndi_con_str = malloc(NDI_CON_STR_MAX); strncpy(ndi_con_str, "", NDI_CON_STR_MAX); const NDIlib_metadata_frame_t ndi_con_type = {// The length (int) strlen(ndi_con_str), // Timecode (synthesized for us !) NDIlib_send_timecode_synthesize, // The string (char *) ndi_con_str}; NDIlib_send_add_connection_metadata(ndi_send, &ndi_con_type); while (!self->f_exit) { mlt_frame frame = mlt_consumer_rt_frame(consumer); if (frame || last) { mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(consumer)); int m_isKeyer = 0, width = profile->width, height = profile->height; uint8_t *image = 0; mlt_frame frm = m_isKeyer ? frame : last; mlt_image_format format = 0 ? mlt_image_rgba : mlt_image_yuv422; int rendered = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frm), "rendered"); if (rendered && !mlt_frame_get_image(frm, &image, &format, &width, &height, 0)) { mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s:%d: width=%d, height=%d\n", __FUNCTION__, __LINE__, width, height); uint8_t *buffer = 0; int64_t timecode; int stride = width * (m_isKeyer ? 4 : 2); int progressive = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "progressive"); timecode = 10000000LL * (uint64_t) self->count * (uint64_t) profile->frame_rate_den; timecode = timecode / (uint64_t) profile->frame_rate_num; const NDIlib_video_frame_t ndi_video_frame = {// Resolution width, height, // Use YCbCr video m_isKeyer ? NDIlib_FourCC_type_BGRA : NDIlib_FourCC_type_UYVY, // The frame-eate profile->frame_rate_num, profile->frame_rate_den, // The aspect ratio (double) profile->display_aspect_num / profile->display_aspect_den, // This is a progressive frame progressive ? NDIlib_frame_format_type_progressive : NDIlib_frame_format_type_interleaved, // Timecode timecode, // The video memory used for this frame buffer = (uint8_t *) malloc(height * stride), // The line to line stride of this image stride}; if (!m_isKeyer) { unsigned char *arg[3] = {image, buffer}; ssize_t size = stride * height; // Normal non-keyer playout - needs byte swapping if (!self->sliced_swab) swab2(arg[0], arg[1], size); else { arg[2] = (unsigned char *) size; mlt_slices_run_fifo(0, swab_sliced, arg); } } else if (!mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "test_image")) { // Normal keyer output int y = height + 1; uint32_t *s = (uint32_t *) image; uint32_t *d = (uint32_t *) buffer; // Need to relocate alpha channel RGBA => ARGB while (--y) { int x = width + 1; while (--x) { *d++ = (*s << 8) | (*s >> 24); s++; } } } else { // Keying blank frames - nullify alpha memset(buffer, 0, stride * height); } mlt_audio_format aformat = mlt_audio_s16; int frequency = 48000; int m_channels = 2; int samples = mlt_audio_calculate_frame_samples(mlt_profile_fps(profile), frequency, self->count); int16_t *pcm = 0; if (!mlt_frame_get_audio(frm, (void **) &pcm, &aformat, &frequency, &m_channels, &samples)) { // Create an audio buffer const NDIlib_audio_frame_interleaved_16s_t audio_data = {// 48kHz frequency, // Lets submit stereo although there is nothing limiting us m_channels, // samples, // Timecode timecode, // reference_level 0, // The audio data, interleaved 16bpp pcm}; NDIlib_util_send_send_audio_interleaved_16s(ndi_send, &audio_data); } // We now submit the frame. NDIlib_send_send_video(ndi_send, &ndi_video_frame); // free buf free(ndi_video_frame.p_data); self->count++; } } if (frame) { if (last) mlt_frame_close(last); last = frame; } mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } NDIlib_send_destroy(ndi_send); mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__); if (last) mlt_frame_close(last); return NULL; } static int consumer_ndi_start(mlt_consumer consumer) { int r = 0; mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__); mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Set interlacing properties mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(consumer)); mlt_properties_set_int(properties, "top_field_first", !profile->progressive); consumer_ndi_t *self = (consumer_ndi_t *) consumer->child; if (!self->f_running) { self->sliced_swab = mlt_properties_get_int(properties, "sliced_swab"); // set flags self->f_exit = 0; pthread_create(&self->th, NULL, consumer_ndi_feeder, consumer); // set flags self->f_running = 1; } mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__); return r; } static int consumer_ndi_stop(mlt_consumer consumer) { int r = 0; mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__); consumer_ndi_t *self = (consumer_ndi_t *) consumer->child; if (self->f_running) { // rise flags self->f_exit = 1; // wait for thread pthread_join(self->th, NULL); // hide flags self->f_running = 0; } mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__); return r; } static int consumer_ndi_is_stopped(mlt_consumer consumer) { consumer_ndi_t *self = (consumer_ndi_t *) consumer->child; return !self->f_running; } static void consumer_ndi_close(mlt_consumer consumer) { consumer_ndi_t *self = (consumer_ndi_t *) consumer->child; mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__); // Stop the consumer mlt_consumer_stop(consumer); // Close the parent consumer->close = NULL; mlt_consumer_close(consumer); // free context if (self->arg) free(self->arg); free(self); mlt_log_debug(NULL, "%s: exiting\n", __FUNCTION__); } /** Initialise the consumer. */ mlt_consumer consumer_ndi_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Allocate the consumer consumer_ndi_t *self = (consumer_ndi_t *) calloc(1, sizeof(consumer_ndi_t)); mlt_log_debug(NULL, "%s: entering id=[%s], arg=[%s]\n", __FUNCTION__, id, arg); // If allocated if (self && !mlt_consumer_init(&self->parent, self, profile)) { mlt_consumer parent = &self->parent; // Setup context if (arg) self->arg = strdup(arg); // Setup callbacks parent->close = consumer_ndi_close; parent->start = consumer_ndi_start; parent->stop = consumer_ndi_stop; parent->is_stopped = consumer_ndi_is_stopped; mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); mlt_properties_set(properties, "consumer.deinterlacer", "onefield"); return parent; } free(self); return NULL; } mlt-7.38.0/src/modules/ndi/consumer_ndi.yml000664 000000 000000 00000001105 15172202314 020600 0ustar00rootroot000000 000000 schema_version: 7.0 type: consumer identifier: ndi title: NDI Output (*DEPRECATED*) version: 1.0 creator: Maksym Veremeyenko license: LGPLv2.1 language: en tags: - Audio - Video - Network description: Audio/Video over IP output using NewTek's NDI technology parameters: - identifier: resource argument: yes type: string title: NDI Sender name description: Name used for discovery by NDI receivers on the network required: yes mutable: no - identifier: meta.attr.* type: string title: NDI Device Metadata required: no mutable: no mlt-7.38.0/src/modules/ndi/factory.c000664 000000 000000 00000007551 15172202314 017216 0ustar00rootroot000000 000000 /* * factory.c -- output through NewTek NDI * Copyright (C) 2016 Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_FORMAT_MACROS /* see inttypes.h */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS //#define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include "factory.h" #include "Processing.NDI.Lib.h" #include "mltndi_export.h" void swab2(const void *from, void *to, int n) { #if defined(USE_SSE) #define SWAB_STEP 16 int cnt = n / SWAB_STEP; __asm__ volatile("loop_start: \n\t" /* load */ "movdqa 0(%[from]), %%xmm0 \n\t" "add $0x10, %[from] \n\t" /* duplicate to temp registers */ "movdqa %%xmm0, %%xmm1 \n\t" /* shift right temp register */ "psrlw $8, %%xmm1 \n\t" /* shift left main register */ "psllw $8, %%xmm0 \n\t" /* compose them back */ "por %%xmm0, %%xmm1 \n\t" /* save */ "movdqa %%xmm1, 0(%[to]) \n\t" "add $0x10, %[to] \n\t" "dec %[cnt] \n\t" "jnz loop_start \n\t" : [from] "+r"(from), [to] "+r"(to), [cnt] "+r"(cnt) : : "xmm0", "xmm1"); from = (unsigned char *) from + n - (n % SWAB_STEP); to = (unsigned char *) to + n - (n % SWAB_STEP); n = (n % SWAB_STEP); #endif swab((char *) from, (char *) to, n); }; #define SWAB_SLICED_ALIGN_POW 5 int swab_sliced(int id, int idx, int jobs, void *cookie) { unsigned char **args = (unsigned char **) cookie; ssize_t sz = (ssize_t) args[2]; ssize_t bsz = ((sz / jobs + (1 << SWAB_SLICED_ALIGN_POW) - 1) >> SWAB_SLICED_ALIGN_POW) << SWAB_SLICED_ALIGN_POW; ssize_t offset = bsz * idx; if (offset < sz) { if ((offset + bsz) > sz) bsz = sz - offset; swab2(args[0] + offset, args[1] + offset, bsz); } return 0; }; static void *create_service(mlt_profile profile, mlt_service_type type, const char *id, void *arg) { mlt_log_debug(NULL, "%s: entering id=[%s]\n", __FUNCTION__, id); if (!NDIlib_initialize()) { mlt_log_error(NULL, "%s: NDIlib_initialize failed\n", __FUNCTION__); return NULL; } if (type == mlt_service_producer_type) return producer_ndi_init(profile, type, id, (char *) arg); else if (type == mlt_service_consumer_type) return consumer_ndi_init(profile, type, id, (char *) arg); return NULL; } MLTNDI_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "ndi", create_service); MLT_REGISTER(mlt_service_producer_type, "ndi", create_service); } mlt-7.38.0/src/modules/ndi/factory.h000664 000000 000000 00000002707 15172202314 017221 0ustar00rootroot000000 000000 /* * factory.h -- output through NewTek NDI * Copyright (C) 2016 Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACTORY_H #define FACTORY_H #include #define NDI_CON_STR_MAX 32768 mlt_producer producer_ndi_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); mlt_consumer consumer_ndi_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); int swab_sliced(int id, int idx, int jobs, void *cookie); void swab2(const void *from, void *to, int n); #endif /* FACTORY_H */ mlt-7.38.0/src/modules/ndi/producer_ndi.c000664 000000 000000 00000054516 15172202314 020227 0ustar00rootroot000000 000000 /* * producer_ndi.c -- output through NewTek NDI * Copyright (C) 2016 Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_FORMAT_MACROS /* see inttypes.h */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include #include #include #include #include #include #include "Processing.NDI.Lib.h" #include "factory.h" #define NDI_TIMEBASE 10000000LL typedef struct { mlt_producer parent; int f_running, f_exit, f_timeout; char *arg; pthread_t th; int count; mlt_deque a_queue, v_queue; pthread_mutex_t lock; pthread_cond_t cond; NDIlib_recv_instance_t recv; int v_queue_limit, a_queue_limit, v_prefill; } producer_ndi_t; static void *producer_ndi_feeder(void *p) { mlt_producer producer = p; int ndi_src_idx; const NDIlib_source_t *ndi_srcs = NULL; NDIlib_video_frame_t *video = NULL; NDIlib_audio_frame_t audio; NDIlib_metadata_frame_t meta; producer_ndi_t *self = (producer_ndi_t *) producer->child; mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "%s: entering\n", __FUNCTION__); // find the source const NDIlib_find_create_t find_create_desc = {.show_local_sources = true, .p_groups = NULL, .p_extra_ips = NULL}; // create a finder NDIlib_find_instance_t ndi_find = NDIlib_find_create2(&find_create_desc); if (!ndi_find) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "%s: NDIlib_find_create failed\n", __FUNCTION__); return NULL; } // wait for source mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "%s: waiting for sources, searching for [%s]\n", __FUNCTION__, self->arg); for (ndi_src_idx = -1; ndi_src_idx == -1 && !self->f_exit;) { unsigned int j, f, n; // wait sources list changed f = NDIlib_find_wait_for_sources(ndi_find, 500); mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "%s: NDIlib_find_wait_for_sources=%d\n", __FUNCTION__, f); if (!f) { mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "%s: no more sources\n", __FUNCTION__); break; } // check if request present in sources list ndi_srcs = NDIlib_find_get_current_sources(ndi_find, &n); mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "%s: found %d sources\n", __FUNCTION__, n); for (j = 0; j < n && ndi_src_idx == -1; j++) if (!strcmp(self->arg, ndi_srcs[j].p_ndi_name)) ndi_src_idx = j; } // exit if nothing if (self->f_exit || ndi_src_idx == -1) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "%s: exiting, self->f_exit=%d\n", __FUNCTION__, self->f_exit); NDIlib_find_destroy(ndi_find); return NULL; } mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "%s: source [%s] found\n", __FUNCTION__, self->arg); // create receiver description NDIlib_recv_create_t recv_create_desc = {ndi_srcs[ndi_src_idx], NDIlib_recv_color_format_e_UYVY_RGBA, NDIlib_recv_bandwidth_highest, true}; // Create the receiver self->recv = NDIlib_recv_create2(&recv_create_desc); NDIlib_find_destroy(ndi_find); if (!self->recv) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "%s: exiting, NDIlib_recv_create2 failed\n", __FUNCTION__); return 0; } // set tally const NDIlib_tally_t tally_state = {true, false}; NDIlib_recv_set_tally(self->recv, &tally_state); while (!self->f_exit) { NDIlib_frame_type_e t; NDIlib_audio_frame_interleaved_16s_t *audio_packet, *audio_packet_prev; if (!video) video = mlt_pool_alloc(sizeof(NDIlib_video_frame_t)); t = NDIlib_recv_capture(self->recv, video, &audio, &meta, 10); mlt_log_debug(NULL, "%s:%d: NDIlib_recv_capture=%d\n", __FILE__, __LINE__, t); switch (t) { case NDIlib_frame_type_none: break; case NDIlib_frame_type_video: pthread_mutex_lock(&self->lock); mlt_deque_push_back(self->v_queue, video); if (mlt_deque_count(self->v_queue) >= self->v_queue_limit) { video = mlt_deque_pop_front(self->v_queue); NDIlib_recv_free_video(self->recv, video); } else video = NULL; pthread_cond_broadcast(&self->cond); pthread_mutex_unlock(&self->lock); break; case NDIlib_frame_type_audio: // convert to 16s interleaved audio_packet = mlt_pool_alloc(sizeof(NDIlib_audio_frame_interleaved_16s_t)); audio_packet->reference_level = 0; audio_packet->p_data = mlt_pool_alloc(2 * audio.no_channels * audio.no_samples); NDIlib_util_audio_to_interleaved_16s(&audio, audio_packet); NDIlib_recv_free_audio(self->recv, &audio); // store into queue pthread_mutex_lock(&self->lock); // check if it continues prev packet audio_packet_prev = mlt_deque_pop_back(self->a_queue); if (audio_packet_prev) { int64_t n = audio_packet_prev->timecode + NDI_TIMEBASE * audio_packet_prev->no_samples / audio_packet_prev->sample_rate, d = audio_packet->timecode - n; if (d && llabs(d) < 2) { mlt_log_debug(NULL, "%s:%d: audio_packet_prev->timecode=%" PRId64 ", audio_packet->timecode=%" PRId64 ", n=%" PRId64 ", d=%" PRId64 ", audio_packet_prev->no_samples=%d\n", __FILE__, __LINE__, audio_packet_prev->timecode, audio_packet->timecode, n, d, audio_packet_prev->no_samples); audio_packet_prev->p_data = mlt_pool_realloc(audio_packet_prev->p_data, (audio_packet_prev->no_samples + audio_packet->no_samples) * audio_packet->no_channels * 2); memcpy((unsigned char *) audio_packet_prev->p_data + 2 * audio_packet_prev->no_channels * audio_packet_prev->no_samples, audio_packet->p_data, 2 * audio_packet->no_channels * audio_packet->no_samples); audio_packet_prev->no_samples += audio_packet->no_samples; mlt_pool_release(audio_packet->p_data); mlt_pool_release(audio_packet); audio_packet = NULL; } mlt_deque_push_back(self->a_queue, audio_packet_prev); } if (audio_packet) mlt_deque_push_back(self->a_queue, audio_packet); if (mlt_deque_count(self->a_queue) >= self->a_queue_limit) { audio_packet = mlt_deque_pop_front(self->a_queue); mlt_pool_release(audio_packet->p_data); mlt_pool_release(audio_packet); } pthread_cond_broadcast(&self->cond); pthread_mutex_unlock(&self->lock); break; case NDIlib_frame_type_metadata: NDIlib_recv_free_metadata(self->recv, &meta); break; case NDIlib_frame_type_error: mlt_log_error(MLT_PRODUCER_SERVICE(producer), "%s: NDIlib_recv_capture failed\n", __FUNCTION__); break; } } if (video) mlt_pool_release(video); mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "%s: exiting\n", __FUNCTION__); return NULL; } static int get_audio(mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_properties fprops = MLT_FRAME_PROPERTIES(frame); NDIlib_recv_instance_t recv = mlt_properties_get_data(fprops, "ndi_recv", NULL); NDIlib_audio_frame_interleaved_16s_t *audio = mlt_properties_get_data(fprops, "ndi_audio", NULL); mlt_log_debug(NULL, "%s:%d: recv=%p, audio=%p\n", __FILE__, __LINE__, recv, audio); if (recv && audio) { size_t size; *format = mlt_audio_s16; *frequency = audio->sample_rate; *channels = audio->no_channels; *samples = audio->no_samples; size = 2 * (*samples) * (*channels); *buffer = audio->p_data; mlt_frame_set_audio(frame, (uint8_t *) *buffer, *format, size, NULL); } return 0; } static int get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { mlt_properties fprops = MLT_FRAME_PROPERTIES(frame); NDIlib_recv_instance_t recv = mlt_properties_get_data(fprops, "ndi_recv", NULL); NDIlib_video_frame_t *video = mlt_properties_get_data(fprops, "ndi_video", NULL); mlt_log_debug(NULL, "%s:%d: recv=%p, video=%p\n", __FILE__, __LINE__, recv, video); if (recv && video) { uint8_t *dst = NULL; size_t size; int j, dst_stride = 0, stride; *width = video->xres; *height = video->yres; if (NDIlib_FourCC_type_UYVY == video->FourCC || NDIlib_FourCC_type_UYVA == video->FourCC) { dst_stride = 2 * video->xres; *format = mlt_image_yuv422; } else if (NDIlib_FourCC_type_RGBA == video->FourCC || NDIlib_FourCC_type_RGBX == video->FourCC) { dst_stride = 4 * video->xres; *format = mlt_image_rgba; } else *format = mlt_image_none; size = mlt_image_format_size(*format, *width, *height, NULL); dst = mlt_pool_alloc(size); stride = (dst_stride > video->line_stride_in_bytes) ? video->line_stride_in_bytes : dst_stride; mlt_log_debug(NULL, "%s: stride=%d\n", __FUNCTION__, stride); if (NDIlib_FourCC_type_UYVY == video->FourCC || NDIlib_FourCC_type_UYVA == video->FourCC) for (j = 0; j < video->yres; j++) swab2(video->p_data + j * video->line_stride_in_bytes, dst + j * dst_stride, stride); else if (NDIlib_FourCC_type_RGBA == video->FourCC || NDIlib_FourCC_type_RGBX == video->FourCC) for (j = 0; j < video->yres; j++) memcpy(dst + j * dst_stride, video->p_data + j * video->line_stride_in_bytes, stride); if (dst) { mlt_frame_set_image(frame, (uint8_t *) dst, size, (mlt_destructor) mlt_pool_release); *buffer = dst; } if (NDIlib_FourCC_type_UYVA == video->FourCC) { uint8_t *src = video->p_data + (*height) * video->line_stride_in_bytes; size = (*width) * (*height); dst = mlt_pool_alloc(size); dst_stride = *width; stride = (dst_stride > (video->line_stride_in_bytes / 2)) ? (video->line_stride_in_bytes / 2) : dst_stride; for (j = 0; j < video->yres; j++) memcpy(dst + j * dst_stride, src + j * (video->line_stride_in_bytes / 2), stride); mlt_frame_set_alpha(frame, (uint8_t *) dst, size, (mlt_destructor) mlt_pool_release); } mlt_properties_set_int(fprops, "progressive", (video->frame_format_type == NDIlib_frame_format_type_progressive)); mlt_properties_set_int(fprops, "top_field_first", (video->frame_format_type == NDIlib_frame_format_type_interleaved)); NDIlib_recv_free_video(recv, video); } return 0; } static int get_frame(mlt_producer producer, mlt_frame_ptr pframe, int index) { mlt_frame frame = NULL; NDIlib_audio_frame_interleaved_16s_t *audio_frame = NULL; NDIlib_video_frame_t *video = NULL; double fps = mlt_producer_get_fps(producer); mlt_position position = mlt_producer_position(producer); producer_ndi_t *self = (producer_ndi_t *) producer->child; pthread_mutex_lock(&self->lock); mlt_log_debug(NULL, "%s:%d: entering %s\n", __FILE__, __LINE__, __FUNCTION__); // run thread if (!self->f_running) { // set flags self->f_exit = 0; pthread_create(&self->th, NULL, producer_ndi_feeder, producer); // set flags self->f_running = 1; } mlt_log_debug(NULL, "%s:%d: audio_cnt=%d, video_cnt=%d\n", __FILE__, __LINE__, mlt_deque_count(self->a_queue), mlt_deque_count(self->v_queue)); // wait for prefill if (mlt_deque_count(self->v_queue) < self->v_prefill) { struct timespec tm; // Wait clock_gettime(CLOCK_REALTIME, &tm); tm.tv_nsec += self->v_prefill * 1000000000LL / fps; tm.tv_sec += tm.tv_nsec / 1000000000LL; tm.tv_nsec %= 1000000000LL; pthread_cond_timedwait(&self->cond, &self->lock, &tm); } // pop frame to use if (mlt_deque_count(self->v_queue) >= self->v_prefill) video = (NDIlib_video_frame_t *) mlt_deque_pop_front(self->v_queue); if (video) { int64_t video_timecode_out, video_dur; mlt_log_debug(NULL, "%s:%d: video: timecode=%" PRId64 "\n", __FILE__, __LINE__, video->timecode); video_dur = NDI_TIMEBASE * video->frame_rate_D / video->frame_rate_N; video_timecode_out = video->timecode + video_dur; // deal with audio while (1) { NDIlib_audio_frame_interleaved_16s_t *audio_packet; int64_t audio_packet_dur, audio_packet_timecode_out, T1, T2, dst_offset, src_offset, duration; // pop audio packet audio_packet = (NDIlib_audio_frame_interleaved_16s_t *) mlt_deque_pop_front( self->a_queue); // check if audio present if (!audio_packet) break; // check if packet in a future if (video_timecode_out < audio_packet->timecode) { // push it back to queue mlt_deque_push_front(self->a_queue, audio_packet); break; } // calc packet audio_packet_dur = NDI_TIMEBASE * audio_packet->no_samples / audio_packet->sample_rate; audio_packet_timecode_out = audio_packet_dur + audio_packet->timecode; // check if packet in the past if (audio_packet_timecode_out < video->timecode) { mlt_pool_release(audio_packet->p_data); mlt_pool_release(audio_packet); continue; } // allocate new audio frame if (!audio_frame) { audio_frame = (NDIlib_audio_frame_interleaved_16s_t *) mlt_pool_alloc( sizeof(NDIlib_audio_frame_interleaved_16s_t)); audio_frame->timecode = video->timecode; audio_frame->no_channels = audio_packet->no_channels; audio_frame->sample_rate = audio_packet->sample_rate; audio_frame->no_samples = audio_packet->sample_rate * video->frame_rate_D / video->frame_rate_N; audio_frame->p_data = (short *) mlt_pool_alloc(2 * audio_frame->no_samples * audio_frame->no_channels); } // copy data of overlapping region T1 = MAX(audio_packet->timecode, video->timecode); T2 = MIN(audio_packet_timecode_out, video_timecode_out); dst_offset = (T1 - audio_frame->timecode) * audio_frame->sample_rate / NDI_TIMEBASE; src_offset = (T1 - audio_packet->timecode) * audio_packet->sample_rate / NDI_TIMEBASE; duration = (T2 - T1) * audio_frame->sample_rate / NDI_TIMEBASE; ; memcpy((unsigned char *) audio_frame->p_data + 2 * audio_frame->no_channels * dst_offset, (unsigned char *) audio_packet->p_data + 2 * audio_packet->no_channels * src_offset, 2 * audio_packet->no_channels * duration); // save packet back if it will be used later or clear it if (video_timecode_out < audio_packet_timecode_out) { // push it back to queue mlt_deque_push_front(self->a_queue, audio_packet); break; } // free packet data mlt_pool_release(audio_packet->p_data); mlt_pool_release(audio_packet); } } pthread_mutex_unlock(&self->lock); *pframe = frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (frame) { mlt_properties p = MLT_FRAME_PROPERTIES(frame); mlt_properties_set_data(p, "ndi_recv", (void *) self->recv, 0, NULL, NULL); if (video) { mlt_properties_set_data(p, "ndi_video", (void *) video, 0, mlt_pool_release, NULL); mlt_frame_push_get_image(frame, get_image); } else mlt_log_error(NULL, "%s:%d: NO VIDEO\n", __FILE__, __LINE__); if (audio_frame) { mlt_properties_set_data(p, "ndi_audio", (void *) audio_frame, 0, mlt_pool_release, NULL); mlt_properties_set_data(p, "ndi_audio_data", (void *) audio_frame->p_data, 0, mlt_pool_release, NULL); mlt_frame_push_audio(frame, (void *) get_audio); } self->f_timeout = 0; } mlt_frame_set_position(frame, position); // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } static void producer_ndi_close(mlt_producer producer) { producer_ndi_t *self = (producer_ndi_t *) producer->child; mlt_log_debug(MLT_PRODUCER_SERVICE(producer), "%s: entering\n", __FUNCTION__); if (self->f_running) { // rise flags self->f_exit = 1; // signal threads pthread_cond_broadcast(&self->cond); // wait for thread pthread_join(self->th, NULL); // hide flags self->f_running = 0; // dequeue audio frames while (mlt_deque_count(self->a_queue)) { NDIlib_audio_frame_interleaved_16s_t *audio = (NDIlib_audio_frame_interleaved_16s_t *) mlt_deque_pop_front(self->a_queue); mlt_pool_release(audio->p_data); mlt_pool_release(audio); } // dequeue video frames while (mlt_deque_count(self->v_queue)) { NDIlib_video_frame_t *video = (NDIlib_video_frame_t *) mlt_deque_pop_front( self->v_queue); NDIlib_recv_free_video(self->recv, video); mlt_pool_release(video); } // close receiver NDIlib_recv_destroy(self->recv); } mlt_deque_close(self->a_queue); mlt_deque_close(self->v_queue); pthread_mutex_destroy(&self->lock); pthread_cond_destroy(&self->cond); free(producer->child); producer->close = NULL; mlt_producer_close(producer); mlt_log_debug(NULL, "%s: exiting\n", __FUNCTION__); } /** Initialise the producer. */ mlt_producer producer_ndi_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Allocate the producer producer_ndi_t *self = (producer_ndi_t *) calloc(1, sizeof(producer_ndi_t)); mlt_producer parent = (mlt_producer) calloc(1, sizeof(*parent)); mlt_log_debug(NULL, "%s: entering id=[%s], arg=[%s]\n", __FUNCTION__, id, arg); // If allocated if (self && !mlt_producer_init(parent, self)) { self->parent = parent; mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); // Setup context self->arg = strdup(arg); pthread_mutex_init(&self->lock, NULL); pthread_cond_init(&self->cond, NULL); self->v_queue = mlt_deque_init(); self->a_queue = mlt_deque_init(); self->v_queue_limit = 6; self->a_queue_limit = 6; self->v_prefill = 2; // Set callbacks parent->close = (mlt_destructor) producer_ndi_close; parent->get_frame = get_frame; // These properties effectively make it infinite. mlt_properties_set_int(properties, "length", INT_MAX); mlt_properties_set_int(properties, "out", INT_MAX - 1); mlt_properties_set(properties, "eof", "loop"); return parent; } free(self); return NULL; } mlt-7.38.0/src/modules/ndi/producer_ndi.yml000664 000000 000000 00000000677 15172202314 020605 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: ndi title: NDI Input (*DEPRECATED*) version: 1.0 creator: Maksym Veremeyenko license: LGPLv2.1 language: en tags: - Audio - Video - Network description: Audio/Video over IP input using NewTek's NDI technology parameters: - identifier: resource argument: yes type: string title: NDI Source name description: Name of the NDI Source to receive required: yes mutable: no mlt-7.38.0/src/modules/normalize/000775 000000 000000 00000000000 15172202314 016621 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/normalize/CMakeLists.txt000664 000000 000000 00000001536 15172202314 021366 0ustar00rootroot000000 000000 add_library(mltnormalize MODULE factory.c filter_audiolevel.c filter_volume.c ) file(GLOB YML "*.yml") add_custom_target(Other_normalize_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltnormalize) target_compile_options(mltnormalize PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltnormalize PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltnormalize PRIVATE mlt) if(MSVC) target_link_libraries(mltnormalize PRIVATE PThreads4W::PThreads4W) else() target_link_libraries(mltnormalize PRIVATE m) endif() set_target_properties(mltnormalize PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltnormalize LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_audiolevel.yml filter_volume.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/normalize ) mlt-7.38.0/src/modules/normalize/factory.c000664 000000 000000 00000003733 15172202314 020442 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mltnormalize_export.h" #include #include #include extern mlt_filter filter_audiolevel_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_volume_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/normalize/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTNORMALIZE_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "audiolevel", filter_audiolevel_init); MLT_REGISTER(mlt_service_filter_type, "volume", filter_volume_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "audiolevel", metadata, "filter_audiolevel.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "volume", metadata, "filter_volume.yml"); } mlt-7.38.0/src/modules/normalize/filter_audiolevel.c000664 000000 000000 00000012645 15172202314 022473 0ustar00rootroot000000 000000 /* * filter_audiolevel.c -- get the audio level of each channel * Copyright (C) 2002 Steve Harris * Copyright (C) 2010 Marco Gittler * Copyright (C) 2012-2023 Dan Dennedy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #define AMPTODBFS(n) (log10(n) * 20.0) //---------------------------------------------------------------------------- // IEC standard dB scaling -- as borrowed from meterbridge (c) Steve Harris static inline double IEC_Scale(double dB) { double fScale = 1.0f; if (dB < -70.0f) fScale = 0.0f; else if (dB < -60.0f) // 0.0 .. 2.5 fScale = (dB + 70.0f) * 0.0025f; else if (dB < -50.0f) // 2.5 .. 7.5 fScale = (dB + 60.0f) * 0.005f + 0.025f; else if (dB < -40.0) // 7.5 .. 15.0 fScale = (dB + 50.0f) * 0.0075f + 0.075f; else if (dB < -30.0f) // 15.0 .. 30.0 fScale = (dB + 40.0f) * 0.015f + 0.15f; else if (dB < -20.0f) // 30.0 .. 50.0 fScale = (dB + 30.0f) * 0.02f + 0.3f; else if (dB < -0.001f || dB > 0.001f) // 50.0 .. 100.0 fScale = (dB + 20.0f) * 0.025f + 0.5f; return fScale; } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = mlt_frame_pop_audio(frame); // Get the properties from the filter mlt_properties filter_props = MLT_FILTER_PROPERTIES(filter); int iec_scale = mlt_properties_get_int(filter_props, "iec_scale"); int dbPeak = mlt_properties_get_int(filter_props, "dbpeak"); *format = mlt_audio_s16; int error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (error || !buffer || !buffer[0]) return error; int num_channels = *channels; int num_samples = *samples > 200 ? 200 : *samples; int num_oversample = 0; int c, s; char key[50]; int16_t *pcm = (int16_t *) *buffer; for (c = 0; c < *channels; c++) { double level = 0.0; if (dbPeak) { int16_t peakVal = 0; for (s = 0; s < num_samples; s++) { int16_t sample = abs(pcm[c + s * num_channels]); if (sample > peakVal) peakVal = sample; } if (peakVal == 0) level = -100; else level = AMPTODBFS((double) peakVal / (double) INT16_MAX); if (iec_scale) level = IEC_Scale(level); } else { double val = 0; for (s = 0; s < num_samples; s++) { double sample = fabs(pcm[c + s * num_channels] / 128.0); val += sample; if (sample == 128) num_oversample++; else num_oversample = 0; // 10 samples @max => show max signal if (num_oversample > 10) { level = 1.0; break; } // if 3 samples over max => 1 peak over 0 db (0 dB = 40.0) if (num_oversample > 3) level = 41.0 / 42.0; } // max amplitude = 40/42, 3to10 oversamples=41, more then 10 oversamples=42 if (level == 0.0 && num_samples > 0) level = val / num_samples * 40.0 / 42.0 / 127.0; if (iec_scale) level = IEC_Scale(AMPTODBFS(level)); } sprintf(key, "meta.media.audio_level.%d", c); mlt_properties_set_double(MLT_FRAME_PROPERTIES(frame), key, level); sprintf(key, "_audio_level.%d", c); mlt_properties_set_double(filter_props, key, level); mlt_log_debug(MLT_FILTER_SERVICE(filter), "channel %d level %f\n", c, level); } mlt_properties_set_position(filter_props, "_position", mlt_filter_get_position(filter, frame)); return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Constructor for the filter. */ mlt_filter filter_audiolevel_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "iec_scale", 1); } return filter; } mlt-7.38.0/src/modules/normalize/filter_audiolevel.yml000664 000000 000000 00000002176 15172202314 023050 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: audiolevel title: Audio Levels version: 2 copyright: Meltytech, LLC, Marco Gittler, and Steve Harris creator: Dan Dennedy contributor: - Marco Gittler - Steve Harris license: GPLv2 language: en description: Compute the audio amplitude. notes: > This filter provides the amplitude level as a percentage value in floating point. This does not do any "slowing" of the data by averaging out peaks and troughs of short duration like a VU meter. Applications can also get this data on the frame as meta.media.audio_level. where is the channel number starting with 0. tags: - Audio parameters: - identifier: iec_scale title: Use IEC 60268-18 Scale type: boolean default: 1 widget: checkbox - identifier: dbpeak title: output true peak value in dB instead of a percentage in _audio_level. type: boolean default: 0 widget: checkbox - identifier: _audio_level. description: > is the channel number starting with 0. This is updated on every frame with audio. readonly: yes type: float minimum: 0 maximum: 1 mlt-7.38.0/src/modules/normalize/filter_volume.c000664 000000 000000 00000035074 15172202314 021652 0ustar00rootroot000000 000000 /* * filter_volume.c -- adjust audio volume * Copyright (C) 2003-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #define EPSILON 0.00001 /* The following normalize functions come from the normalize utility: Copyright (C) 1999--2002 Chris Vaill */ #define samp_width 16 #ifndef ROUND #define ROUND(x) floor((x) + 0.5) #endif #define DBFSTOAMP(x) pow(10, (x) / 20.0) /** Return nonzero if the two strings are equal, ignoring case, up to the first n characters. */ int strncaseeq(const char *s1, const char *s2, size_t n) { for (; n > 0; n--) { if (tolower(*s1++) != tolower(*s2++)) return 0; } return 1; } /** Limiter function. / tanh((x + lev) / (1-lev)) * (1-lev) - lev (for x < -lev) | x' = | x (for |x| <= lev) | \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev) With limiter level = 0, this is equivalent to a tanh() function; with limiter level = 1, this is equivalent to clipping. */ static inline double limiter(double x, double lmtr_lvl) { double xp = x; if (x < -lmtr_lvl) xp = tanh((x + lmtr_lvl) / (1 - lmtr_lvl)) * (1 - lmtr_lvl) - lmtr_lvl; else if (x > lmtr_lvl) xp = tanh((x - lmtr_lvl) / (1 - lmtr_lvl)) * (1 - lmtr_lvl) + lmtr_lvl; return xp; } /** Takes a full smoothing window, and returns the value of the center element, smoothed. Currently, just does a mean filter, but we could do a median or gaussian filter here instead. */ static inline double get_smoothed_data(double *buf, int count) { int i, j; double smoothed = 0; for (i = 0, j = 0; i < count; i++) { if (buf[i] != -1.0) { smoothed += buf[i]; j++; } } if (j) smoothed /= j; return smoothed; } /** Get the max power level (using RMS) and peak level of the audio segment. */ double signal_max_power(int16_t *buffer, int channels, int samples, int16_t *peak) { // Determine numeric limits int bytes_per_samp = (samp_width - 1) / 8 + 1; int16_t max = (1 << (bytes_per_samp * 8 - 1)) - 1; int16_t min = -max - 1; double *sums = (double *) calloc(channels, sizeof(double)); int c, i; int16_t sample; double pow, maxpow = 0; /* initialize peaks to effectively -inf and +inf */ int16_t max_sample = min; int16_t min_sample = max; for (i = 0; i < samples; i++) { for (c = 0; c < channels; c++) { sample = *buffer++; sums[c] += (double) sample * (double) sample; /* track peak */ if (sample > max_sample) max_sample = sample; else if (sample < min_sample) min_sample = sample; } } for (c = 0; c < channels; c++) { pow = sums[c] / (double) samples; if (pow > maxpow) maxpow = pow; } free(sums); /* scale the pow value to be in the range 0.0 -- 1.0 */ maxpow /= ((double) min * (double) min); if (-min_sample > max_sample) *peak = min_sample / (double) min; else *peak = max_sample / (double) max; return sqrt(maxpow); } /* ------ End normalize functions --------------------------------------- */ /** Get the audio. */ static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Get the filter from the frame mlt_filter filter = mlt_frame_pop_audio(frame); // Get the properties from the filter mlt_properties filter_props = MLT_FILTER_PROPERTIES(filter); // Get the frame's filter instance properties mlt_properties instance_props = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(filter)); // Get the parameters double gain = mlt_properties_get_double(instance_props, "gain"); double max_gain = mlt_properties_get_double(instance_props, "max_gain"); double limiter_level = 0.5; /* -6 dBFS */ int normalize = mlt_properties_get_int(instance_props, "normalize"); double amplitude = mlt_properties_get_double(instance_props, "amplitude"); int i, j; double sample; int16_t peak; // Use animated value for gain if "level" property is set char *level_property = mlt_properties_get(filter_props, "level"); if (level_property != NULL) { mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); gain = mlt_properties_anim_get_double(filter_props, "level", position, length); gain = DBFSTOAMP(gain); } if (mlt_properties_get(instance_props, "limiter") != NULL) limiter_level = mlt_properties_get_double(instance_props, "limiter"); // Get the producer's audio *format = normalize ? mlt_audio_s16 : mlt_audio_f32le; int error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (error || !buffer || !buffer[0]) return error; mlt_service_lock(MLT_FILTER_SERVICE(filter)); if (normalize) { int window = mlt_properties_get_int(filter_props, "window"); double *smooth_buffer = mlt_properties_get_data(filter_props, "smooth_buffer", NULL); if (window > 0 && smooth_buffer != NULL) { int smooth_index = mlt_properties_get_int(filter_props, "_smooth_index"); // Compute the signal power and put into smoothing buffer smooth_buffer[smooth_index] = signal_max_power(*buffer, *channels, *samples, &peak); if (smooth_buffer[smooth_index] > EPSILON) { mlt_properties_set_int(filter_props, "_smooth_index", (smooth_index + 1) % window); // Smooth the data and compute the gain gain *= amplitude / get_smoothed_data(smooth_buffer, window); } } else { gain *= amplitude / signal_max_power(*buffer, *channels, *samples, &peak); } } if (max_gain > 0 && gain > max_gain) gain = max_gain; // Initialise filter's previous gain value to prevent an inadvertent jump from 0 mlt_position last_position = mlt_properties_get_position(filter_props, "_last_position"); mlt_position current_position = mlt_frame_get_position(frame); if (mlt_properties_get(filter_props, "_previous_gain") == NULL || current_position != last_position + 1) mlt_properties_set_double(filter_props, "_previous_gain", gain); // Start the gain out at the previous double previous_gain = mlt_properties_get_double(filter_props, "_previous_gain"); // Determine ramp increment double gain_step = (gain - previous_gain) / *samples; // Save the current gain for the next iteration mlt_properties_set_double(filter_props, "_previous_gain", gain); mlt_properties_set_position(filter_props, "_last_position", current_position); int channel_mask = mlt_properties_get_int(filter_props, "channel_mask"); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Ramp from the previous gain to the current gain = previous_gain; // Apply the gain if (normalize) { int16_t *p = *buffer; // Determine numeric limits int bytes_per_samp = (samp_width - 1) / 8 + 1; int samplemax = (1 << (bytes_per_samp * 8 - 1)) - 1; double g; for (j = 0; j < *channels; j++) { if (channel_mask & (1 << j)) for (i = 0; i < *samples; i++) { g = gain + i * gain_step; sample = p[0] * g; p[0] = ROUND(sample); if (g > 1.0 && normalize) { /* use limiter function instead of clipping */ p[*channels * i + j] = ROUND( samplemax * limiter(sample / (double) samplemax, limiter_level)); } } } } else { float *p = *buffer; for (j = 0; j < *channels; j++) { if (channel_mask & (1 << j)) for (i = 0; i < *samples; i++) { p[*channels * i + j] *= gain + i * gain_step; } } } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties filter_props = MLT_FILTER_PROPERTIES(filter); mlt_properties instance_props = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(filter)); double gain = 1.0; // no adjustment char *gain_str = mlt_properties_get(filter_props, "gain"); // Parse the gain property if (gain_str) { char *p_orig = strdup(gain_str); char *p = p_orig; if (strncaseeq(p, "normali", 7)) { mlt_properties_set(filter_props, "normalize", ""); mlt_properties_set(filter_props, "normalise", ""); } else { if (strcmp(p, "") != 0) gain = strtod(p, &p); while (isspace(*p)) p++; /* check if "dB" is given after number */ if (strncaseeq(p, "db", 2)) gain = DBFSTOAMP(gain); else gain = fabs(gain); // If there is an end adjust gain to the range if (mlt_properties_get(filter_props, "end") != NULL) { double end = -1; char *p = mlt_properties_get(filter_props, "end"); if (strcmp(p, "") != 0) end = strtod(p, &p); while (isspace(*p)) p++; /* check if "dB" is given after number */ if (strncaseeq(p, "db", 2)) end = DBFSTOAMP(end); else end = fabs(end); if (end != -1) gain += (end - gain) * mlt_filter_get_progress(filter, frame); } } free(p_orig); } mlt_properties_set_double(instance_props, "gain", gain); // Parse the maximum gain property if (mlt_properties_get(filter_props, "max_gain") != NULL) { char *p = mlt_properties_get(filter_props, "max_gain"); double gain = strtod(p, &p); // 0 = no max while (isspace(*p)) p++; /* check if "dB" is given after number */ if (strncaseeq(p, "db", 2)) gain = DBFSTOAMP(gain); else gain = fabs(gain); mlt_properties_set_double(instance_props, "max_gain", gain); } // Parse the limiter property if (mlt_properties_get(filter_props, "limiter") != NULL) { char *p = mlt_properties_get(filter_props, "limiter"); double level = 0.5; /* -6dBFS */ if (strcmp(p, "") != 0) level = strtod(p, &p); while (isspace(*p)) p++; /* check if "dB" is given after number */ if (strncaseeq(p, "db", 2)) { if (level > 0) level = -level; level = DBFSTOAMP(level); } else { if (level < 0) level = -level; } mlt_properties_set_double(instance_props, "limiter", level); } // Parse the normalize property char *norm = mlt_properties_get(filter_props, "normalize"); if (!norm) norm = mlt_properties_get(filter_props, "normalise"); if (norm != NULL) { char *p = norm; double amplitude = 0.2511886431509580; /* -12dBFS */ if (strcmp(p, "") != 0) amplitude = strtod(p, &p); while (isspace(*p)) p++; /* check if "dB" is given after number */ if (strncaseeq(p, "db", 2)) { if (amplitude > 0) amplitude = -amplitude; amplitude = DBFSTOAMP(amplitude); } else { if (amplitude < 0) amplitude = -amplitude; if (amplitude > 1.0) amplitude = 1.0; } // If there is an end adjust gain to the range if (mlt_properties_get(filter_props, "end") != NULL) { amplitude *= mlt_filter_get_progress(filter, frame); } mlt_properties_set_int(instance_props, "normalize", 1); mlt_properties_set_int(instance_props, "normalise", 1); mlt_properties_set_double(instance_props, "amplitude", amplitude); } // Parse the window property and allocate smoothing buffer if needed int window = mlt_properties_get_int(filter_props, "window"); if (mlt_properties_get(filter_props, "smooth_buffer") == NULL && window > 1) { // Create a smoothing buffer for the calculated "max power" of frame of audio used in normalization double *smooth_buffer = (double *) calloc(window, sizeof(double)); int i; for (i = 0; i < window; i++) smooth_buffer[i] = -1.0; mlt_properties_set_data(filter_props, "smooth_buffer", smooth_buffer, 0, free, NULL); } // Push the filter onto the stack mlt_frame_push_audio(frame, filter); // Override the get_audio method mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Constructor for the filter. */ mlt_filter filter_volume_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = calloc(1, sizeof(struct mlt_filter_s)); if (filter != NULL && mlt_filter_init(filter, NULL) == 0) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); filter->process = filter_process; if (arg != NULL) mlt_properties_set(properties, "gain", arg); mlt_properties_set_int(properties, "window", 75); mlt_properties_set(properties, "max_gain", "20dB"); mlt_properties_set(properties, "level", NULL); mlt_properties_set_int(properties, "channel_mask", -1); } return filter; } mlt-7.38.0/src/modules/normalize/filter_volume.yml000664 000000 000000 00000006443 15172202314 022227 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: volume title: Volume version: 3 copyright: Meltytech, LLC creator: Dan Denneedy license: GPLv2 language: en tags: - Audio description: > Adjust an audio stream's volume level. This filter is based on the 'normalize' utility parameters: - identifier: gain (*DEPRECATED*) argument: yes title: Gain type: string description: > This parameter is deprecated; use "level" instead. The gain may be indicated as a floating point value of the gain adjustment. The gain may also be indicated as a numeric value with the suffix "dB" to adjust in terms of decibels. The gain may also be set to "normalize" to normalize the volume to the target amplitude -12dBFS. This value is discarded if value for property "level" is set. - identifier: window title: Window type: integer description: > The number of video frames over which to smooth normalization. default: 75 mutable: yes - identifier: normalize title: Normalize type: string description: > Normalize the volume to the specified amplitude. The normalization may be indicated as a floating point value of the relative volume. The normalization may also be indicated as a numeric value with the suffix "dB" to set the amplitude in decibels. default: -12dBFS mutable: yes - identifier: normalise title: Normalise (*DEPRECATED*) description: Deprecated. See normalize - identifier: limiter title: Limiter type: string description: > Limit all samples above the specified amplitude. The limiting may be indicated as a floating point value of the relative volume. The limiting may also be indicated as a numeric value with the suffix "dB" to set the limiting amplitude in decibels. default: -6dBFS mutable: yes - identifier: max_gain title: Max gain type: string description: > A floating point or decibel value of the maximum gain that can be applied during normalization. default: 20dB mutable: yes - identifier: end (*DEPRECATED*) title: End gain type: string description: > A gain value just like the Gain property. This causes the gain to be interpolated from 'gain' to 'end' over the duration. This value is discarded if value for property "level" is set. mutable: yes - identifier: max_gain title: Max gain type: string description: > A floating point or decibel value of the maximum gain that can be applied during normalization. default: 20dB mutable: yes - identifier: level title: Level type: float description: > The animated value of the gain adjustment in dB. Properties "gain" and "end" are ignored if this is set. unit: dB mutable: yes animation: yes - identifier: channel_mask title: Channel Mask type: integer description: > Which channels to affect. Each bit represents a channel; for example, 1 = channel 0 (left), 2 = channel 1 (right), 3 = both left and right channels. The default is to overwrite all of the channels. required: no minimum: 0 default: 0xFFFFFFFF mutable: yes mlt-7.38.0/src/modules/normalize/gpl000664 000000 000000 00000000000 15172202314 017314 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/oldfilm/000775 000000 000000 00000000000 15172202314 016247 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/oldfilm/CMakeLists.txt000664 000000 000000 00000002164 15172202314 021012 0ustar00rootroot000000 000000 add_library(mltoldfilm MODULE common.c common.h factory.c filter_dust.c filter_grain.c filter_lines.c filter_oldfilm.c filter_tcolor.c filter_vignette.c ) file(GLOB YML "*.yml") add_custom_target(Other_oldfilm_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltoldfilm) target_compile_options(mltoldfilm PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltoldfilm PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltoldfilm PRIVATE mlt) if(MSVC) target_link_libraries(mltoldfilm PRIVATE PThreads4W::PThreads4W) else() target_link_libraries(mltoldfilm PRIVATE m) endif() set_target_properties(mltoldfilm PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltoldfilm LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_dust.yml filter_grain.yml filter_lines.yml filter_oldfilm.yml filter_tcolor.yml filter_vignette.yml dust1.svg dust2.svg dust3.svg dust4.svg dust5.svg fdust.svg grain.svg lines.svg oldfilm.svg tcolor.svg vignette.svg DESTINATION ${MLT_INSTALL_DATA_DIR}/oldfilm ) mlt-7.38.0/src/modules/oldfilm/common.c000664 000000 000000 00000002633 15172202314 017707 0ustar00rootroot000000 000000 /* * Copyright (C) 2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include void oldfilm_init_seed(oldfilm_rand_seed *seed, int init) { // Use the initial value to initialize the seed to arbitrary values. // This causes the algorithm to produce consistent results each time for the same frame number. seed->x = 521288629 + init - (init << 16); seed->y = 362436069 - init + (init << 16); } int oldfilm_fast_rand(oldfilm_rand_seed *seed) { static unsigned int a = 18000, b = 30903; seed->x = a * (seed->x & 65535) + (seed->x >> 16); seed->y = b * (seed->y & 65535) + (seed->y >> 16); int r = (seed->x << 16) + (seed->y & 65535); return abs(r); } mlt-7.38.0/src/modules/oldfilm/common.h000664 000000 000000 00000002006 15172202314 017706 0ustar00rootroot000000 000000 /* * Copyright (C) 2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_H #define COMMON_H typedef struct { unsigned int x; unsigned int y; } oldfilm_rand_seed; void oldfilm_init_seed(oldfilm_rand_seed *seed, int init); int oldfilm_fast_rand(oldfilm_rand_seed *seed); #endif // COMMON_H mlt-7.38.0/src/modules/oldfilm/dust1.svg000664 000000 000000 00000012401 15172202314 020026 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/dust2.svg000664 000000 000000 00000052562 15172202314 020043 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/dust3.svg000664 000000 000000 00000004715 15172202314 020041 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/dust4.svg000664 000000 000000 00000007414 15172202314 020041 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/dust5.svg000664 000000 000000 00000004617 15172202314 020044 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/factory.c000664 000000 000000 00000006672 15172202314 020075 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (c) 2007 Marco Gittler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltoldfilm_export.h" #include #include #include extern mlt_filter filter_dust_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_grain_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_lines_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_oldfilm_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_tcolor_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_vignette_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties oldfilm_metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/oldfilm/filter_%s.yml", mlt_environment("MLT_DATA"), id); return mlt_properties_parse_yaml(file); } MLTOLDFILM_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "oldfilm", filter_oldfilm_init); MLT_REGISTER(mlt_service_filter_type, "dust", filter_dust_init); MLT_REGISTER(mlt_service_filter_type, "lines", filter_lines_init); MLT_REGISTER(mlt_service_filter_type, "grain", filter_grain_init); MLT_REGISTER(mlt_service_filter_type, "tcolor", filter_tcolor_init); MLT_REGISTER(mlt_service_filter_type, "vignette", filter_vignette_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "vignette", oldfilm_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_filter_type, "tcolor", oldfilm_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_filter_type, "grain", oldfilm_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_filter_type, "lines", oldfilm_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_filter_type, "dust", oldfilm_metadata, NULL); MLT_REGISTER_METADATA(mlt_service_filter_type, "oldfilm", oldfilm_metadata, NULL); } mlt-7.38.0/src/modules/oldfilm/fdust.svg000664 000000 000000 00000010707 15172202314 020122 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/filter_dust.c000664 000000 000000 00000026236 15172202314 020750 0ustar00rootroot000000 000000 /* * filter_dust.c -- dust filter * Copyright (c) 2007 Marco Gittler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include static void overlay_image(uint8_t *src, int src_width, int src_height, uint8_t *overlay, int overlay_width, int overlay_height, uint8_t *alpha, int xpos, int ypos, int upsidedown, int mirror) { int x, y; for (y = ypos; y < src_height; y++) { if (y >= 0 && (y - ypos) < overlay_height) { uint8_t *scanline_image = src + src_width * y * 2; int overlay_y = upsidedown ? (overlay_height - (y - ypos) - 1) : (y - ypos); uint8_t *scanline_overlay = overlay + overlay_width * 2 * overlay_y; for (x = xpos; x < src_width && x - xpos < overlay_width; x++) { if (x > 0) { int overlay_x = mirror ? overlay_width - (x - xpos) - 1 : (x - xpos); double alp = (double) *(alpha + overlay_width * overlay_y + overlay_x) / 255.0; uint8_t *image_pixel = scanline_image + x * 2; uint8_t *overlay_pixel = scanline_overlay + overlay_x * 2; *image_pixel = (double) (*overlay_pixel) * alp + (double) *image_pixel * (1.0 - alp); if (xpos % 2 == 0) image_pixel++; else image_pixel += 3; mirror ? overlay_pixel-- : overlay_pixel++; *image_pixel = (double) (*(overlay_pixel)) * alp + (double) (*image_pixel) * (1.0 - alp); } } } } } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position pos = mlt_filter_get_position(filter, frame); mlt_position len = mlt_filter_get_length2(filter, frame); int maxdia = mlt_properties_anim_get_int(properties, "maxdiameter", pos, len); int maxcount = mlt_properties_anim_get_int(properties, "maxcount", pos, len); *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Load svg char *factory = mlt_properties_get(properties, "factory"); char temp[PATH_MAX] = ""; snprintf(temp, sizeof(temp), "%s/oldfilm/", mlt_environment("MLT_DATA")); mlt_properties direntries = mlt_properties_new(); mlt_properties_dir_list(direntries, temp, "dust*.svg", 1); if (!maxcount) return 0; double position = mlt_filter_get_progress(filter, frame); srand(position * 10000); mlt_service_lock(MLT_FILTER_SERVICE(filter)); int im = rand() % maxcount; int piccount = mlt_properties_count(direntries); while (im-- && piccount) { int picnum = rand() % piccount; int y1 = rand() % *height; int x1 = rand() % *width; char resource[1024] = ""; char savename[1024] = "", savename1[1024] = "", cachedy[100]; int dx = (*width * maxdia / 100); int luma_width, luma_height; uint8_t *luma_image = NULL; uint8_t *alpha = NULL; int updown = rand() % 2; int mirror = rand() % 2; sprintf(resource, "%s", mlt_properties_get_value(direntries, picnum)); sprintf(savename, "cache-%d-%d", picnum, dx); sprintf(savename1, "cache-alpha-%d-%d", picnum, dx); sprintf(cachedy, "cache-dy-%d-%d", picnum, dx); luma_image = mlt_properties_get_data(properties, savename, NULL); alpha = mlt_properties_get_data(properties, savename1, NULL); if (luma_image == NULL || alpha == NULL) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_producer producer = mlt_factory_producer(profile, factory, resource); if (producer != NULL) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_set(producer_properties, "eof", "loop"); mlt_frame luma_frame = NULL; if (mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &luma_frame, 0) == 0) { mlt_image_format luma_format = mlt_image_yuv422; luma_width = dx; luma_height = luma_width * mlt_properties_get_int(MLT_FRAME_PROPERTIES(luma_frame), "height") / mlt_properties_get_int(MLT_FRAME_PROPERTIES(luma_frame), "width"); mlt_properties_set(MLT_FRAME_PROPERTIES(luma_frame), "consumer.rescale", "best"); // none/nearest/tiles/hyper mlt_frame_get_image(luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0); alpha = mlt_frame_get_alpha(luma_frame); if (!alpha) { int alphasize = luma_width * luma_height; alpha = mlt_pool_alloc(alphasize); memset(alpha, 255, alphasize); mlt_frame_set_alpha(luma_frame, alpha, alphasize, mlt_pool_release); } uint8_t *savealpha = mlt_pool_alloc(luma_width * luma_height); uint8_t *savepic = mlt_pool_alloc(luma_width * luma_height * 2); if (savealpha && savepic) { memcpy(savealpha, alpha, luma_width * luma_height); memcpy(savepic, luma_image, luma_width * luma_height * 2); mlt_properties_set_data(properties, savename, savepic, luma_width * luma_height * 2, mlt_pool_release, NULL); mlt_properties_set_data(properties, savename1, savealpha, luma_width * luma_height, mlt_pool_release, NULL); mlt_properties_set_int(properties, cachedy, luma_height); overlay_image(*image, *width, *height, luma_image, luma_width, luma_height, alpha, x1, y1, updown, mirror); } else { if (savealpha) mlt_pool_release(savealpha); if (savepic) mlt_pool_release(savepic); } mlt_frame_close(luma_frame); } mlt_producer_close(producer); } } else { overlay_image(*image, *width, *height, luma_image, dx, mlt_properties_get_int(properties, cachedy), alpha, x1, y1, updown, mirror); } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); if (piccount > 0) return 0; if (error == 0 && *image) { int h = *height; int w = *width; int im = rand() % maxcount; while (im--) { int type = im % 2; int y1 = rand() % h; int x1 = rand() % w; int dx = rand() % maxdia; int dy = rand() % maxdia; int x = 0, y = 0; double v = 0.0; for (x = -dx; x < dx; x++) { for (y = -dy; y < dy; y++) { if (x1 + x < w && x1 + x > 0 && y1 + y < h && y1 + y > 0) { uint8_t *pix = *image + (y + y1) * w * 2 + (x + x1) * 2; v = pow((double) x / (double) dx * 5.0, 2.0) + pow((double) y / (double) dy * 5.0, 2.0); if (v > 10) v = 10; v = 1.0 - (v / 10.0); switch (type) { case 0: *pix -= (*pix) * v; break; case 1: *pix += (255 - *pix) * v; break; } } } } } } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_dust_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "maxdiameter", "2"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "maxcount", "10"); } return filter; } mlt-7.38.0/src/modules/oldfilm/filter_dust.yml000664 000000 000000 00000002226 15172202314 021320 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter # consumer, filter, producer, or transition identifier: dust title: Dust version: 0.2.5 copyright: Copyright (C) 2008 Marco Gittler license: GPL language: en creator: Marco Gittler tags: - Video # this may produce video description: Add dust and specks to the Video, as in old movies icon: filename: oldfilm/fdust.svg # relative to $MLT_DATA/modules/ content-type: image/svg notes: Implementation or additional usage notes go here. bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls - need to do some speed improvement. parameters: - identifier: maxdiameter title: Maximal Diameter type: integer description: Maximal diameter of a dust piece readonly: no required: yes minimum: 0 maximum: 100 default: 2 mutable: yes animation: yes widget: spinner unit: '%' - identifier: maxcount title: Maximal number of dust type: integer description: How many dust pieces are on the image readonly: no required: yes minimum: 0 maximum: 400 default: 10 mutable: yes animation: yes widget: spinner mlt-7.38.0/src/modules/oldfilm/filter_grain.c000664 000000 000000 00000010060 15172202314 021055 0ustar00rootroot000000 000000 /* * filter_grain.c -- grain filter * Copyright (c) 2007 Marco Gittler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include typedef struct { uint8_t *image; int width; int height; int noise; double contrast; double brightness; mlt_position pos; int min; int max_luma; } slice_desc; static int slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *d = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, d->height, &slice_line_start); uint8_t *p = d->image + slice_line_start * d->width * 2; oldfilm_rand_seed seed; oldfilm_init_seed(&seed, d->pos * jobs + index); for (int n = 0; n < slice_height * d->width; n++, p += 2) { if (p[0] > 20) { int pix = CLAMP(((double) p[0] - 127.0) * d->contrast + 127.0 + d->brightness, 0, 255); if (d->noise > 0) { pix -= oldfilm_fast_rand(&seed) % d->noise - d->noise; } p[0] = CLAMP(pix, d->min, d->max_luma); } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position pos = mlt_filter_get_position(filter, frame); mlt_position len = mlt_filter_get_length2(filter, frame); *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0 && *image) { int noise = mlt_properties_anim_get_int(properties, "noise", pos, len); int full_range = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_range"); slice_desc desc = { .image = *image, .width = *width, .height = *height, .noise = noise, .contrast = mlt_properties_anim_get_double(properties, "contrast", pos, len) / 100.0, .brightness = 127.0 * (mlt_properties_anim_get_double(properties, "brightness", pos, len) - 100.0) / 100.0, .pos = pos, .min = full_range ? 0 : 16, .max_luma = full_range ? 255 : 235, }; mlt_slices_run_normal(0, slice_proc, &desc); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_grain_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "noise", "40"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "contrast", "160"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "brightness", "70"); } return filter; } mlt-7.38.0/src/modules/oldfilm/filter_grain.yml000664 000000 000000 00000002512 15172202314 021437 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter # consumer, filter, producer, or transition identifier: grain title: Grain version: 0.2.5 copyright: Copyright (C) 2008 Marco Gittler license: GPL language: en creator: Marco Gittler tags: - Video # this may produce video description: Grain over the Image icon: filename: oldfilm/grain.svg # relative to $MLT_DATA/modules/ content-type: image/svg notes: Implementation or additional usage notes go here. bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls - need to do some speed improvement. parameters: - identifier: noise title: Noise type: integer description: Maximal value of noise readonly: no required: yes minimum: 0 maximum: 200 default: 40 mutable: yes animation: yes widget: spinner unit: '%' - identifier: contrast title: Contrast type: integer description: Adjust contrast for the image readonly: no required: yes minimum: 0 maximum: 400 default: 160 mutable: yes animation: yes widget: spinner - identifier: brightness title: Brightness type: integer description: Adjust brightness for the image readonly: no required: yes minimum: 0 maximum: 400 default: 70 mutable: yes animation: yes widget: spinner mlt-7.38.0/src/modules/oldfilm/filter_lines.c000664 000000 000000 00000016633 15172202314 021103 0ustar00rootroot000000 000000 /* * filter_lines.c -- lines filter * Copyright (c) 2007 Marco Gittler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include typedef struct { uint8_t *image; int width; int height; int dx; int ystart; int yend; int xmid; int type; double maxdarker; double maxlighter; int min; int max_luma; int max_chroma; } slice_desc; static int slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *d = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, d->height, &slice_line_start); uint8_t *p = d->image; for (int y = MAX(d->ystart, slice_line_start); y < MIN(d->yend, slice_line_start + slice_height); y++) { for (int x = -(d->dx); x < d->dx && (x + d->xmid) < d->width; x++) { if (x + d->xmid > 0) { int i = (y * d->width + x + d->xmid) * 2; double diff = 1.0 - (double) abs(x) / d->dx; switch (d->type) { case 1: //blackline p[i] = CLAMP(p[i] - (double) p[i] * diff * d->maxdarker / 100.0, d->min, d->max_luma); break; case 2: //whiteline p[i] = CLAMP(p[i] + (255.0 - (double) p[i]) * diff * d->maxlighter / 100.0, d->min, d->max_luma); break; case 3: //greenline p[i + 1] = CLAMP(p[i + 1] - (double) p[i + 1] * diff * d->maxlighter / 100.0, d->min, d->max_chroma); break; } } } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position pos = mlt_filter_get_position(filter, frame); mlt_position len = mlt_filter_get_length2(filter, frame); *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0 && *image) { int line_width = mlt_properties_anim_get_int(properties, "line_width", pos, len); int num = mlt_properties_anim_get_int(properties, "num", pos, len); double maxdarker = (double) mlt_properties_anim_get_int(properties, "darker", pos, len); double maxlighter = (double) mlt_properties_anim_get_int(properties, "lighter", pos, len); int full_range = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_range"); int min = full_range ? 0 : 16; int max_luma = full_range ? 255 : 235; int max_chroma = full_range ? 255 : 240; char buf[256]; char typebuf[256]; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale = mlt_profile_scale_width(profile, *width); if (line_width > 1 && scale > 0.0) line_width = MAX(2, lrint(line_width * scale)); if (line_width < 1) return 0; double position = mlt_filter_get_progress(filter, frame); oldfilm_rand_seed seed; mlt_service_lock(MLT_FILTER_SERVICE(filter)); while (num--) { oldfilm_init_seed(&seed, position * 10000 + num); int type = (oldfilm_fast_rand(&seed) % 3) + 1; int xmid = (double) (*width) * oldfilm_fast_rand(&seed) / INT_MAX; int dx = oldfilm_fast_rand(&seed) % line_width; int ystart = oldfilm_fast_rand(&seed) % (*height); int yend = oldfilm_fast_rand(&seed) % (*height); sprintf(buf, "line%d", num); sprintf(typebuf, "typeline%d", num); maxlighter += oldfilm_fast_rand(&seed) % 30 - 15; maxdarker += oldfilm_fast_rand(&seed) % 30 - 15; if (!mlt_properties_get_int(properties, buf)) { mlt_properties_set_int(properties, buf, xmid); } if (!mlt_properties_get_int(properties, typebuf)) { mlt_properties_set_int(properties, typebuf, type); } xmid = mlt_properties_get_int(properties, buf); type = mlt_properties_get_int(properties, typebuf); if (position != mlt_properties_get_double(properties, "last_oldfilm_line_pos")) { xmid += (oldfilm_fast_rand(&seed) % 11 - 5); } if (yend < ystart) { yend = *height; } if (dx) { slice_desc desc = {.image = *image, .width = *width, .height = *height, .dx = dx, .ystart = ystart, .yend = yend, .xmid = xmid, .type = type, .maxdarker = maxdarker, .maxlighter = maxlighter, .min = min, .max_luma = max_luma, .max_chroma = max_chroma}; mlt_slices_run_normal(0, slice_proc, &desc); } mlt_properties_set_int(properties, buf, xmid); } mlt_properties_set_double(properties, "last_oldfilm_line_pos", position); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_lines_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "line_width", 2); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "num", 5); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "darker", 40); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "lighter", 40); } return filter; } mlt-7.38.0/src/modules/oldfilm/filter_lines.yml000664 000000 000000 00000003225 15172202314 021453 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter # consumer, filter, producer, or transition identifier: lines title: Scratchlines version: 0.2.6 copyright: Copyright (C) 2008 Marco Gittler license: GPL language: en creator: Marco Gittler tags: - Video # this may produce video description: Scratchlines over the Picture icon: filename: oldfilm/lines.svg # relative to $MLT_DATA/modules/ content-type: image/svg notes: Implementation or additional usage notes go here. bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls - need to do some speed improvement. parameters: - identifier: line_width title: Width of line type: integer description: Linewidth in picture readonly: no required: yes minimum: 0 maximum: 100 default: 2 mutable: yes animation: yes widget: spinner unit: pixel - identifier: num title: Max number of lines type: integer description: Maximal number of lines in picture readonly: no required: yes minimum: 0 maximum: 100 default: 5 mutable: yes animation: yes widget: spinner unit: lines - identifier: darker title: Max darker type: integer description: Make image up to n values darker behind line readonly: no required: yes minimum: 0 maximum: 100 default: 40 mutable: yes animation: yes widget: spinner - identifier: lighter title: Max lighter type: integer description: Make image up to n values lighter behind line readonly: no required: yes minimum: 0 maximum: 100 default: 40 mutable: yes animation: yes widget: spinner mlt-7.38.0/src/modules/oldfilm/filter_oldfilm.c000664 000000 000000 00000017437 15172202314 021422 0ustar00rootroot000000 000000 /* * filter_oldfilm.c -- oldfilm filter * Copyright (c) 2007 Marco Gittler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include static double sinarr[] = { 0.0, 0.0627587292804297, 0.125270029508395, 0.18728744713136, 0.2485664757507, 0.308865520098932, 0.3679468485397, 0.425577530335206, 0.481530353985902, 0.535584723021826, 0.587527525713892, 0.637153975276265, 0.684268417247276, 0.728685100865749, 0.770228911401552, 0.80873606055313, 0.84405473219009, 0.876045680894979, 0.904582780944473, 0.929553523565587, 0.95085946050647, 0.968416592172968, 0.982155698800724, 0.99202261335714, 0.997978435097294, 0.999999682931835, 0.99807838800221, 0.992222125098244, 0.982453982794196, 0.968812472421035, 0.951351376233828, 0.930139535372831, 0.90526057845426, 0.876812591860795, 0.844907733031696, 0.809671788277164, 0.771243676860277, 0.72977490330168, 0.685428960066342, 0.638380682987321, 0.588815561967795, 0.536929009678953, 0.48292559113694, 0.427018217196276, 0.369427305139443, 0.310379909672042, 0.250108827749629, 0.188851680765468, 0.12684997771773, 0.064348163049637, 0.00159265291648683, -0.0611691363208864, -0.123689763546002, -0.18572273843423, -0.247023493251739, -0.307350347074556, -0.366465458626247, -0.424135763977612, -0.48013389541149, -0.534239077829989, -0.586237999170027, -0.635925651395529, -0.683106138750633, -0.727593450087328, -0.769212192222595, -0.807798281433749, -0.84319959036574, -0.875276547799941, -0.903902688919827, -0.928965153904073, -0.950365132881376, -0.968018255492714, -0.981854923525203, -0.991820585306115, -0.997875950775248, -0.999997146387718, -0.998175809236459, -0.992419120023356, -0.982749774749007, -0.969205895232745, -0.951840878815686, -0.930723187839362, -0.905936079729926, -0.877577278752084, -0.845758590726883, -0.810605462232336, -0.772256486024771, -0.730862854630786, -0.68658776426406, -0.639605771417098, -0.590102104664575, -0.538271934391528, -0.484319603325524, -0.428457820906457, -0.37090682467023, -0.311893511952568, -0.251650545336281, -0.190415435368805, -0.128429604166398, -0.0659374335968388, 0.0, }; static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position pos = mlt_filter_get_position(filter, frame); mlt_position len = mlt_filter_get_length2(filter, frame); *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0 && *image) { int h = *height; int w = *width; int x = 0; int y = 0; double position = mlt_filter_get_progress(filter, frame); srand(position * 10000); int delta = mlt_properties_anim_get_int(properties, "delta", pos, len); int every = mlt_properties_anim_get_int(properties, "every", pos, len); int bdu = mlt_properties_anim_get_int(properties, "brightnessdelta_up", pos, len); int bdd = mlt_properties_anim_get_int(properties, "brightnessdelta_down", pos, len); int bevery = mlt_properties_anim_get_int(properties, "brightnessdelta_every", pos, len); int udu = mlt_properties_anim_get_int(properties, "unevendevelop_up", pos, len); int udd = mlt_properties_anim_get_int(properties, "unevendevelop_down", pos, len); int uduration = mlt_properties_anim_get_int(properties, "unevendevelop_duration", pos, len); int diffpic = 0; if (delta) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); delta *= mlt_profile_scale_width(profile, *width); diffpic = rand() % MAX(delta, 1) * 2 - delta; } int brightdelta = 0; if ((bdu + bdd) != 0) brightdelta = rand() % (bdu + bdd) - bdd; if (rand() % 100 > every) diffpic = 0; if (rand() % 100 > bevery) brightdelta = 0; int yend, ydiff; int unevendevelop_delta = 0; if (uduration > 0) { float uval = sinarr[(((int) position) % uduration) * 100 / uduration]; unevendevelop_delta = uval * (uval > 0 ? udu : udd); } if (diffpic <= 0) { y = h; yend = 0; ydiff = -1; } else { y = 0; yend = h; ydiff = 1; } int full_range = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_range"); int min = full_range ? 0 : 16; int max_luma = full_range ? 255 : 235; for (; y != yend; y += ydiff) { int newy = y + diffpic; uint8_t *p = &(*image)[y * w * 2]; uint8_t *q = &p[diffpic * w * 2]; for (x = 0; x < w * 2; x += 2) { if (newy > 0 && newy < h) { if (((int) q[x] + brightdelta + unevendevelop_delta) > max_luma) { p[x] = max_luma; } else if (((int) q[x] + brightdelta + unevendevelop_delta) < 0) { p[x] = min; } else { p[x] = q[x] + brightdelta + unevendevelop_delta; } p[x + 1] = q[x + 1]; } else { p[x] = min; } } } } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_oldfilm_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "delta", "14"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "every", "20"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "brightnessdelta_up", "20"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "brightnessdelta_down", "30"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "brightnessdelta_every", "70"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "unevendevelop_up", "60"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "unevendevelop_down", "20"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "unevendevelop_duration", "70"); } return filter; } mlt-7.38.0/src/modules/oldfilm/filter_oldfilm.yml000664 000000 000000 00000005425 15172202314 021773 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter # consumer, filter, producer, or transition identifier: oldfilm title: Oldfilm version: 0.2.5 copyright: Copyright (C) 2008 Marco Gittler license: GPL language: en creator: Marco Gittler tags: - Video # this may produce video description: Moves the Picture up and down and random brightness change icon: filename: oldfilm/oldfilm.svg # relative to $MLT_DATA/modules/ content-type: image/svg notes: Implementation or additional usage notes go here. bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls - need to do some speed improvement. parameters: - identifier: delta title: Y-Delta type: integer description: Maximum delta value of Up/Down move readonly: no required: yes minimum: 0 maximum: 400 default: 14 mutable: yes animation: yes widget: spinner unit: pixel - identifier: every title: '% of picture have a delta' type: integer description: n'th % have a Y-Delta in picture readonly: no required: yes minimum: 0 maximum: 100 default: 20 mutable: yes animation: yes widget: spinner unit: '%' - identifier: brightnessdelta_up title: Brightness up type: integer description: Makes image n values lighter readonly: no required: yes minimum: 0 maximum: 100 default: 20 mutable: yes animation: yes widget: spinner - identifier: brightnessdelta_down title: Brightness down type: integer description: Makes image n values darker readonly: no required: yes minimum: 0 maximum: 100 default: 30 mutable: yes animation: yes widget: spinner - identifier: brightnessdelta_every title: Brightness every type: integer description: Change value only for n/100 readonly: no required: yes minimum: 0 maximum: 100 default: 70 mutable: yes animation: yes widget: spinner unit: '%' - identifier: unevendevelop_up title: Unevendevelop up type: integer description: Makes image n values lighter readonly: no required: yes minimum: 0 maximum: 100 default: 60 mutable: yes animation: yes widget: spinner - identifier: unevendevelop_down title: Unevendevelop down type: integer description: Makes image n values darker readonly: no required: yes minimum: 0 maximum: 100 default: 20 mutable: yes animation: yes widget: spinner - identifier: unevendevelop_duration title: Unevendevelop Duration type: integer description: Time (in frames) of a up/down cycle readonly: no required: yes minimum: 0 maximum: 10000 default: 70 mutable: yes animation: yes widget: spinner unit: '%' mlt-7.38.0/src/modules/oldfilm/filter_tcolor.c000664 000000 000000 00000007037 15172202314 021271 0ustar00rootroot000000 000000 /* * filter_tcolor.c -- tcolor filter * Copyright (c) 2007 Marco Gittler * Copyright (c) 2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include typedef struct { uint8_t *image; int width; int height; double over_cr; double over_cb; } slice_desc; static int do_slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *desc = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; int line_size = desc->width * 2; int x, y; for (y = slice_line_start; y < slice_line_end; y++) { uint8_t *p = desc->image + y * line_size; for (x = 0; x < line_size; x += 4) { p[x + 1] = CLAMP(((double) p[x + 1] - 127.0) * desc->over_cb + 127.0, 0, 255); p[x + 3] = CLAMP(((double) p[x + 3] - 127.0) * desc->over_cr + 127.0, 0, 255); } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position pos = mlt_filter_get_position(filter, frame); mlt_position len = mlt_filter_get_length2(filter, frame); *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0 && *image) { slice_desc desc; desc.over_cr = mlt_properties_anim_get_double(properties, "oversaturate_cr", pos, len) / 100.0; desc.over_cb = mlt_properties_anim_get_double(properties, "oversaturate_cb", pos, len) / 100.0; desc.image = *image; desc.width = *width; desc.height = *height; mlt_slices_run_normal(0, do_slice_proc, &desc); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_tcolor_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "oversaturate_cr", "190"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "oversaturate_cb", "190"); } return filter; } mlt-7.38.0/src/modules/oldfilm/filter_tcolor.yml000664 000000 000000 00000002353 15172202314 021644 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter # consumer, filter, producer, or transition identifier: tcolor title: Technicolor version: 0.2.5 copyright: Copyright (C) 2008 Marco Gittler license: GPL language: en creator: Marco Gittler tags: - Video # this may produce video description: Oversaturate the Color in Video, like in old Technicolor movies icon: filename: oldfilm/tcolor.svg # relative to $MLT_DATA/modules/ content-type: image/svg notes: Implementation or additional usage notes go here. bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls - need to do some speed improvement. parameters: - identifier: oversaturate_cr # 'argument' is a reserved name for a value supplied to the factory title: Blue/Yellow- axis type: integer description: Adjust factor for Blue/Yellow axis readonly: no required: yes minimum: -400 maximum: 400 default: 190 mutable: yes animation: yes widget: spinner - identifier: oversaturate_cb title: Red/Green-axis type: integer description: Adjust factor for Red/Green axis readonly: no required: yes minimum: -400 maximum: 400 default: 190 mutable: yes animation: yes widget: spinner mlt-7.38.0/src/modules/oldfilm/filter_vignette.c000664 000000 000000 00000012067 15172202314 021613 0ustar00rootroot000000 000000 /* * filter_vignette.c -- vignette filter * Copyright (c) 2007 Marco Gittler * Copyright (c) 2009-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include typedef struct { uint8_t *image; int width; int height; double smooth; double radius; double cx, cy; double opacity; int mode; } slice_desc; static int slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *d = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, d->height, &slice_line_start); uint8_t *p = d->image + slice_line_start * d->width * 2; int w2 = d->cx, h2 = d->cy; double delta = 1.0; for (int y = slice_line_start; y < slice_line_start + slice_height; y++) { int h2_pow2 = pow(y - h2, 2.0); for (int x = 0; x < d->width; x++, p += 2) { int dx = sqrt(h2_pow2 + pow(x - w2, 2.0)); if (d->radius - d->smooth > dx) { // center, make not darker continue; } else if (d->radius + d->smooth <= dx) { // max dark after smooth area delta = 0.0; } else { // linear pos from of opacity (from 0 to 1) delta = (d->radius + d->smooth - dx) / (2.0 * d->smooth); if (d->mode == 1) { // make cos for smoother non linear fade delta = pow(cos(((1.0 - delta) * M_PI / 2.0)), 3.0); } } delta = MAX(d->opacity, delta); p[0] = p[0] * delta; p[1] = (p[1] - 127.0) * delta + 127.0; } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = mlt_frame_pop_service(frame); *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0 && *image) { mlt_properties filter_props = MLT_FILTER_PROPERTIES(filter); mlt_position pos = mlt_filter_get_position(filter, frame); mlt_position len = mlt_filter_get_length2(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale = mlt_profile_scale_width(profile, *width); slice_desc desc = {.image = *image, .width = *width, .height = *height, .smooth = mlt_properties_anim_get_double(filter_props, "smooth", pos, len) * 100.0 * scale, .radius = mlt_properties_anim_get_double(filter_props, "radius", pos, len) * *width, .cx = mlt_properties_anim_get_double(filter_props, "x", pos, len) * *width, .cy = mlt_properties_anim_get_double(filter_props, "y", pos, len) * *height, .opacity = mlt_properties_anim_get_double(filter_props, "opacity", pos, len), .mode = mlt_properties_get_int(filter_props, "mode")}; mlt_slices_run_normal(0, slice_proc, &desc); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_vignette_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "smooth", 0.8); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "radius", 0.5); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "x", 0.5); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "y", 0.5); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "opacity", 0.0); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "mode", 0); } return filter; } mlt-7.38.0/src/modules/oldfilm/filter_vignette.yml000664 000000 000000 00000003452 15172202314 022170 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter # consumer, filter, producer, or transition identifier: vignette title: Vignette Effect version: 1.0 copyright: Copyright (C) 2008 Marco Gittler license: GPL language: en creator: Marco Gittler tags: - Video # this may produce video description: > Vignette around a point with adjustable smooth, radius, position and transparency icon: filename: oldfilm/vignette.svg # relative to $MLT_DATA/modules/ content-type: image/svg notes: Implementation or additional usage notes go here. bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls - need to do some speed improvement. parameters: - identifier: smooth title: Feathering type: float readonly: no required: yes mutable: yes animation: yes minimum: 0.0 maximum: 1.0 default: 0.8 - identifier: radius title: Radius type: float readonly: no required: yes mutable: yes animation: yes minimum: 0.0 maximum: 1.0 default: 0.5 - identifier: x title: X Position type: float readonly: no required: yes mutable: yes animation: yes minimum: 0.0 maximum: 1.0 default: 0.5 - identifier: y title: Y Position type: float readonly: no required: yes mutable: yes animation: yes minimum: 0.0 maximum: 1.0 default: 0.5 - identifier: opacity title: Opacity type: float readonly: no required: yes mutable: yes animation: yes minimum: 0.0 maximum: 1.0 default: 0.0 - identifier: mode title: Mode type: integer description: Use linear (0) or cosinus (1) mode to fade from opac to black readonly: no required: yes mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-7.38.0/src/modules/oldfilm/grain.svg000664 000000 000000 00000007552 15172202314 020101 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/lines.svg000664 000000 000000 00000004422 15172202314 020104 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/oldfilm.svg000664 000000 000000 00000004435 15172202314 020424 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/tcolor.svg000664 000000 000000 00000005764 15172202314 020306 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/oldfilm/vignette.svg000664 000000 000000 00000006344 15172202314 020624 0ustar00rootroot000000 000000 image/svg+xml mlt-7.38.0/src/modules/opencv/000775 000000 000000 00000000000 15172202314 016113 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/opencv/CMakeLists.txt000664 000000 000000 00000001361 15172202314 020654 0ustar00rootroot000000 000000 add_library(mltopencv MODULE factory.c filter_opencv_tracker.cpp) file(GLOB YML "*.yml") add_custom_target(Other_opencv_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltopencv) target_compile_options(mltopencv PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltopencv PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltopencv PRIVATE mlt ${OpenCV_LIBS}) if(MSVC) target_link_libraries(mltopencv PRIVATE PThreads4W::PThreads4W) endif() set_target_properties(mltopencv PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltopencv LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_opencv_tracker.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/opencv) mlt-7.38.0/src/modules/opencv/factory.c000664 000000 000000 00000003301 15172202314 017723 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2016 Jean-Baptiste Mardelle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mltopencv_export.h" #include #include #include extern mlt_filter filter_tracker_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/opencv/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTOPENCV_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "opencv.tracker", filter_tracker_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "opencv.tracker", metadata, "filter_opencv_tracker.yml"); } mlt-7.38.0/src/modules/opencv/filter_opencv_tracker.cpp000664 000000 000000 00000061272 15172202314 023201 0ustar00rootroot000000 000000 /* * filter_tracker.cpp -- Motion tracker * Copyright (C) 2016 Jean-Baptiste Mardelle * Copyright (C) 2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #define CV_VERSION_INT (CV_VERSION_MAJOR << 16 | CV_VERSION_MINOR << 8 | CV_VERSION_REVISION) #if CV_VERSION_INT > 0x040502 #include #include // for stat() #include // for stat() #ifndef _MSC_VER #include // for stat() #endif #endif typedef struct { cv::Ptr tracker; #if CV_VERSION_INT > 0x040502 cv::Ptr legacyTracker; #endif #if CV_VERSION_INT < 0x040500 cv::Rect2d boundingBox; #else cv::Rect boundingBox; #endif char *algo; mlt_rect startRect; bool initialized; bool playback; bool analyze; int last_position; int analyse_width; int analyse_height; mlt_position producer_in; mlt_position producer_length; bool legacyTracking; } private_data; static void property_changed(mlt_service owner, mlt_filter filter, mlt_event_data event_data) { private_data *pdata = (private_data *) filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "results")) { mlt_properties_anim_get_int(filter_properties, "results", 0, -1); mlt_animation anim = mlt_properties_get_animation(filter_properties, "results"); if (anim && mlt_animation_key_count(anim) > 0) { // We received valid analysis data pdata->initialized = true; pdata->playback = true; return; } else { // Analysis data was discarded pdata->initialized = false; pdata->producer_length = 0; pdata->playback = false; mlt_properties_set(filter_properties, "_results", NULL); } } if (!pdata->initialized) { return; } if (!strcmp(name, "rect")) { // The initial rect was changed, we need to reset the tracker with this new rect mlt_rect rect = mlt_properties_get_rect(filter_properties, "rect"); if (rect.x != pdata->startRect.x || rect.y != pdata->startRect.y || rect.w != pdata->startRect.w || rect.h != pdata->startRect.h) { pdata->playback = false; pdata->initialized = false; } } else if (!strcmp(name, "algo")) { char *algo = mlt_properties_get(filter_properties, "algo"); if (pdata->algo && *pdata->algo != '\0' && strcmp(algo, pdata->algo)) { pdata->playback = false; pdata->initialized = false; } } else if (!strcmp(name, "_reset")) { mlt_properties_set(filter_properties, "results", NULL); mlt_properties_set(filter_properties, "_results", NULL); pdata->initialized = false; pdata->playback = false; } } static void apply( mlt_filter filter, private_data *data, int width, int height, int position, int length) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_rect rect = mlt_properties_anim_get_rect(properties, "results", position, -1); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Calculate the region now double scale_width = mlt_profile_scale_width(profile, width); double scale_height = mlt_profile_scale_height(profile, height); rect.x *= scale_width; rect.w *= scale_width; rect.y *= scale_height; rect.h *= scale_height; data->boundingBox.x = rect.x; data->boundingBox.y = rect.y; data->boundingBox.width = rect.w; data->boundingBox.height = rect.h; } static void analyze(mlt_filter filter, cv::Mat cvFrame, private_data *data, int width, int height, int position, int length) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); if (data->analyse_width == -1) { // Store analyze width/height data->analyse_width = width; data->analyse_height = height; } else if (data->analyse_width != width || data->analyse_height != height) { // Frame size changed, reset all stored data data->initialized = false; data->analyse_width = width; data->analyse_height = height; } // Create tracker and initialize it if (!data->initialized) { // Build tracker data->tracker.reset(); #if CV_VERSION_INT > 0x040502 data->legacyTracker.reset(); #endif data->legacyTracking = false; data->algo = mlt_properties_get(filter_properties, "algo"); #if CV_VERSION_MAJOR > 3 || (CV_VERSION_MAJOR == 3 && CV_VERSION_MINOR >= 3) if (!data->algo || *data->algo == '\0' || !strcmp(data->algo, "KCF")) { data->tracker = cv::TrackerKCF::create(); } else if (!strcmp(data->algo, "MIL")) { data->tracker = cv::TrackerMIL::create(); } #if CV_VERSION_INT > 0x040502 else if (!strcmp(data->algo, "DaSIAM")) { if (mlt_properties_exists(filter_properties, "modelsfolder")) { char *modelsdir = mlt_properties_get(filter_properties, "modelsfolder"); cv::TrackerDaSiamRPN::Params parameters; char *model1 = (char *) calloc(1, 1000); char *model2 = (char *) calloc(1, 1000); char *model3 = (char *) calloc(1, 1000); strcat(model1, modelsdir); strcat(model2, modelsdir); strcat(model3, modelsdir); strcat(model1, "/dasiamrpn_model.onnx"); strcat(model2, "/dasiamrpn_kernel_cls1.onnx"); strcat(model3, "/dasiamrpn_kernel_r1.onnx"); // Models can be downloaded from: // - network: https://www.dropbox.com/s/rr1lk9355vzolqv/dasiamrpn_model.onnx?dl=0 // - kernel_r1: https://www.dropbox.com/s/999cqx5zrfi7w4p/dasiamrpn_kernel_r1.onnx?dl=0 // - kernel_cls1: https://www.dropbox.com/s/qvmtszx5h339a0w/dasiamrpn_kernel_cls1.onnx?dl=0 struct stat file_info; if (mlt_stat(model1, &file_info) == 0 && mlt_stat(model2, &file_info) == 0 && mlt_stat(model3, &file_info) == 0) { // Models found, process parameters.model = model1; parameters.kernel_cls1 = model2; parameters.kernel_r1 = model3; data->tracker = cv::TrackerDaSiamRPN::create(parameters); } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "DaSIAM models not found, please provide a modelsfolder parameter\n"); } free(model1); free(model2); free(model3); } #if CV_VERSION_INT > 0x040600 } else if (!strcmp(data->algo, "Nano")) { if (mlt_properties_exists(filter_properties, "modelsfolder")) { char *modelsdir = mlt_properties_get(filter_properties, "modelsfolder"); cv::TrackerNano::Params parameters; char *model1 = (char *) calloc(1, 1000); char *model2 = (char *) calloc(1, 1000); strcat(model1, modelsdir); strcat(model2, modelsdir); strcat(model1, "/nanotrack_backbone_sim.onnx"); strcat(model2, "/nanotrack_head_sim.onnx"); // Models can be downloaded from: // https://github.com/HonglinChu/SiamTrackers/tree/master/NanoTrack/models/nanotrackv2 struct stat file_info; if (mlt_stat(model1, &file_info) == 0 && mlt_stat(model2, &file_info) == 0) { // Models found, process parameters.backbone = model1; parameters.neckhead = model2; data->tracker = cv::TrackerNano::create(parameters); } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Nano models not found, please provide a modelsfolder parameter\n"); } free(model1); free(model2); } #endif } else if (!strcmp(data->algo, "MOSSE")) { data->legacyTracking = true; data->legacyTracker = cv::legacy::tracking::TrackerMOSSE::create(); } else if (!strcmp(data->algo, "MEDIANFLOW")) { data->legacyTracking = true; data->legacyTracker = cv::legacy::tracking::TrackerMedianFlow::create(); } else if (!strcmp(data->algo, "CSRT")) { data->legacyTracking = true; data->legacyTracker = cv::legacy::tracking::TrackerCSRT::create(); } #endif #if CV_VERSION_INT >= 0x030402 && CV_VERSION_INT < 0x040500 else if (!strcmp(data->algo, "CSRT")) { data->tracker = cv::TrackerCSRT::create(); } else if (!strcmp(data->algo, "MOSSE")) { data->tracker = cv::TrackerMOSSE::create(); } #endif #if CV_VERSION_INT >= 0x030402 && CV_VERSION_INT < 0x040500 else if (!strcmp(data->algo, "TLD")) { data->tracker = cv::TrackerTLD::create(); } else { data->tracker = cv::TrackerBoosting::create(); } #endif // CV_VERSION_INT >= 0x030402 && CV_VERSION_INT < 0x040500 #else if (data->algo == NULL || !strcmp(data->algo, "")) { data->tracker = cv::Tracker::create("KCF"); } else { data->tracker = cv::Tracker::create(data->algo); } #endif // Discard previous results #if CV_VERSION_INT > 0x040502 if (data->tracker == NULL && data->legacyTracker == NULL) { #else if (data->tracker == NULL) { #endif mlt_log_error(MLT_FILTER_SERVICE(filter), "Tracker initialized FAILED\n"); } else { data->startRect = mlt_properties_get_rect(filter_properties, "rect"); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale_width = mlt_profile_scale_width(profile, width); double scale_height = mlt_profile_scale_height(profile, height); data->startRect.x *= scale_width; data->startRect.w *= scale_width; data->startRect.y *= scale_height; data->startRect.h *= scale_height; // Ensure startRect is within frame boundaries if (data->startRect.x > width) { data->startRect.x = width - 10; } else { data->startRect.x = MAX(0., data->startRect.x); } if (data->startRect.y > height) { data->startRect.y = height - 10; } else { data->startRect.y = MAX(0., data->startRect.y); } if (data->startRect.x + data->startRect.w > width) { data->startRect.w = width - data->startRect.x; } if (data->startRect.y + data->startRect.h > height) { data->startRect.h = height - data->startRect.y; } data->boundingBox.x = data->startRect.x; data->boundingBox.y = data->startRect.y; data->boundingBox.width = data->startRect.w; data->boundingBox.height = data->startRect.h; if (data->boundingBox.width < 1) { data->boundingBox.width = 50; } if (data->boundingBox.height < 1) { data->boundingBox.height = 50; } #if CV_VERSION_INT >= 0x030402 && CV_VERSION_INT < 0x040500 if (data->tracker->init(cvFrame, data->boundingBox)) { #else { try { if (data->legacyTracking) { #if CV_VERSION_INT > 0x040502 data->legacyTracker->init(cvFrame, data->boundingBox); #endif } else { data->tracker->init(cvFrame, data->boundingBox); } #endif data->initialized = true; data->analyze = true; data->last_position = position - 1; } catch (cv::Exception &e) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "failed to initialize tracker: %s\n", e.what()); } } // init anim property mlt_properties_anim_get_int(filter_properties, "_results", 0, -1); mlt_properties_get_animation(filter_properties, "_results"); } } else { if (data->legacyTracking) { #if CV_VERSION_INT > 0x040502 cv::Rect2d rect(data->boundingBox); data->legacyTracker->update(cvFrame, rect); data->boundingBox = cv::Rect(rect); #endif } else { data->tracker->update(cvFrame, data->boundingBox); } } if (data->analyze && position != data->last_position + 1) { // We are in real time, do not try to store data data->analyze = false; } if (!data->analyze) { return; } // Store results in temp variable mlt_rect rect; rect.x = data->boundingBox.x; rect.y = data->boundingBox.y; rect.w = data->boundingBox.width; rect.h = data->boundingBox.height; rect.o = 0; int steps = mlt_properties_get_int(filter_properties, "steps"); if (steps > 1 && position > 0 && position < data->producer_length - 1) { if (position % steps == 0) mlt_properties_anim_set_rect(filter_properties, "_results", rect, position + data->producer_in, length, mlt_keyframe_smooth); } else { mlt_properties_anim_set_rect(filter_properties, "_results", rect, position + data->producer_in, length, mlt_keyframe_smooth); } if (position + 1 == data->producer_length) { //Analysis finished, store results mlt_animation anim = mlt_properties_get_animation(filter_properties, "_results"); mlt_properties_set(filter_properties, "results", strdup(mlt_animation_serialize(anim))); // Discard temporary data mlt_properties_set(filter_properties, "_results", (char *) NULL); data->playback = true; } data->last_position = position; } /** Get the image. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_service_lock(MLT_FILTER_SERVICE(filter)); int shape_width = mlt_properties_get_int(filter_properties, "shape_width"); int blur = mlt_properties_get_int(filter_properties, "blur"); cv::Mat cvFrame; private_data *data = (private_data *) filter->child; if (shape_width == 0 && blur == 0 && !data->playback) { error = mlt_frame_get_image(frame, image, format, width, height, 1); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return error; } else { *format = mlt_image_rgb; error = mlt_frame_get_image(frame, image, format, width, height, 1); cvFrame = cv::Mat(*height, *width, CV_8UC3, *image); } if (data->producer_length == 0) { mlt_producer producer = mlt_frame_get_original_producer(frame); producer = mlt_producer_cut_parent(producer); if (producer) { if (mlt_service_chain_type == mlt_service_identify(MLT_PRODUCER_SERVICE(producer))) { data->producer_length = mlt_filter_get_length2(filter, frame); } else { data->producer_in = mlt_producer_get_in(producer) + mlt_filter_get_in(filter); data->producer_length = mlt_filter_get_length2(filter, frame); if (data->producer_length <= 0) { data->producer_length = mlt_producer_get_playtime(producer) - mlt_filter_get_in(filter); } } } } if (!data->initialized) { mlt_properties_anim_get_int(filter_properties, "results", 0, -1); mlt_animation anim = mlt_properties_get_animation(filter_properties, "results"); if (anim && mlt_animation_key_count(anim) > 1) { data->initialized = true; data->playback = true; } } if (data->playback) { // Clip already analysed, don't re-process apply(filter, data, *width, *height, position, data->producer_in + data->producer_length); } else { analyze(filter, cvFrame, data, *width, *height, position, data->producer_in + data->producer_length); } // ensure bounding box is within the frame boundaries or OpenCV will crash if (data->boundingBox.x > *width) { data->boundingBox.x = *width; data->boundingBox.width = 0; } else if (data->boundingBox.x < 0) { data->boundingBox.width = MAX(0, data->boundingBox.width + data->boundingBox.x); data->boundingBox.x = 0; } if (data->boundingBox.y > *height) { data->boundingBox.y = *height; data->boundingBox.height = 0; } else if (data->boundingBox.y < 0) { data->boundingBox.height = MAX(0, data->boundingBox.height + data->boundingBox.y); data->boundingBox.y = 0; } if (data->boundingBox.x + data->boundingBox.width > *width) { data->boundingBox.width = *width - data->boundingBox.x; } if (data->boundingBox.y + data->boundingBox.height > *height) { data->boundingBox.height = *height - data->boundingBox.y; } if (blur > 0 && data->boundingBox.width > 1 && data->boundingBox.height > 1) { switch (mlt_properties_get_int(filter_properties, "blur_type")) { case 1: // Gaussian Blur cv::GaussianBlur(cvFrame(data->boundingBox), cvFrame(data->boundingBox), cv::Size(0, 0), blur); break; case 2: // Pixelate { cv::Mat roi = cvFrame(data->boundingBox); cv::Mat res; cv::resize(roi, res, cv::Size(MAX(2, data->boundingBox.width / blur), MAX(2, data->boundingBox.height / blur)), cv::INTER_NEAREST); cv::resize(res, roi, cv::Size(data->boundingBox.width, data->boundingBox.height), 0, 0, cv::INTER_NEAREST); cvFrame(data->boundingBox) = roi; } break; case 3: // Opaque fill, handled in shape_width option shape_width = -1; break; case 0: // Median Blur ++blur; if (blur % 2 == 0) { // median blur param must be odd and, minimum 3 ++blur; } cv::medianBlur(cvFrame(data->boundingBox), cvFrame(data->boundingBox), blur); break; default: // Do nothing break; } } // Paint overlay shape if (shape_width != 0 && data->boundingBox.width > 0 && data->boundingBox.height > 0) { // Get the OpenCV image mlt_color shape_color = mlt_properties_get_color(filter_properties, "shape_color"); shape_color = mlt_color_convert_trc(shape_color, mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "color_trc")); switch (mlt_properties_get_int(filter_properties, "shape")) { case 2: // Arrow cv::arrowedLine(cvFrame, cv::Point(data->boundingBox.x + data->boundingBox.width / 2, data->boundingBox.y - data->boundingBox.height / 2), cv::Point(data->boundingBox.x + data->boundingBox.width / 2, data->boundingBox.y), cv::Scalar(shape_color.r, shape_color.g, shape_color.b), MAX(shape_width, 1), 4, 0, .2); break; case 1: // Ellipse { cv::RotatedRect bounding = cv::RotatedRect(cv::Point2f(data->boundingBox.x + data->boundingBox.width / 2, data->boundingBox.y + data->boundingBox.height / 2), cv::Size2f(data->boundingBox.width, data->boundingBox.height), 0); cv::ellipse(cvFrame, bounding, cv::Scalar(shape_color.r, shape_color.g, shape_color.b), shape_width, 1); } break; case 0: default: // Rectangle cv::rectangle(cvFrame, data->boundingBox, cv::Scalar(shape_color.r, shape_color.g, shape_color.b), shape_width, 1); break; } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { private_data *data = (private_data *) filter->child; free(data); filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_tracker_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *data = (private_data *) calloc(1, sizeof(private_data)); if (filter && data) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "shape_width", 1); mlt_properties_set_int(properties, "steps", 5); mlt_properties_set(properties, "algo", "KCF"); data->initialized = false; data->playback = false; data->boundingBox.x = 0; data->boundingBox.y = 0; data->boundingBox.width = 0; data->boundingBox.height = 0; data->analyze = false; data->last_position = -1; data->analyse_width = -1; data->analyse_height = -1; data->producer_in = 0; data->producer_length = 0; filter->child = data; // Create a unique ID for storing data on the frame filter->close = filter_close; filter->process = filter_process; mlt_events_listen(properties, filter, "property-changed", (mlt_listener) property_changed); } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter tracker failed\n"); if (filter) { mlt_filter_close(filter); } if (data) { free(data); } filter = NULL; } return filter; } } mlt-7.38.0/src/modules/opencv/filter_opencv_tracker.yml000664 000000 000000 00000007001 15172202314 023206 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: opencv.tracker title: OpenCV Motion Tracker copyright: Jean-Baptiste Mardelle creator: Jean-Baptiste Mardelle version: 2 license: LGPLv2.1 language: en tags: - Video description: Track and follow an object in the video. notes: > If used in a non-linear workflow, this filter works best with two passes. First pass performs analysis and stores the result in a property. Parallel processing (real_time < -1 or > 1) is not supported for the first pass. Second pass simply applies the results to the image. If no analysis result is found, the filter does its best to work in real time. To analyse clip, you can use with melt, use 'melt ... -consumer xml:output.mlt all=1 real_time=-1'. Analysis data is stored in a "results" property. For the second pass, you can use output.mlt as the input. parameters: - identifier: rect title: Target Rect type: string description: > The rectangle defining the object to be followed (from the 1st frame of the filter). required: no readonly: no mutable: yes default: 0 0 50 50 - identifier: shape title: Shape type: integer description: > The shape to be drawn around tracked object. 0 for a rectangle, 1 for an ellipse, 2 for an arrow. readonly: no required: no minimum: 0 maximum: 5 default: 0 mutable: yes - identifier: shape_width title: Shape Width type: integer description: > Decide if we want to display a shape around followed object during playback. 0 means no display, -1 means a filled shape and > 0 determines the border width. readonly: no required: no minimum: -1 maximum: 100 default: 1 mutable: yes - identifier: shape_color title: Shape Color type: color description: > The color used to paint the shape around target object. readonly: no required: no default: 0xffffffff mutable: yes - identifier: blur title: Blur type: integer description: > Decide if we want to blur selected object. 0 means no blur, > 0 means blur intensity. readonly: no required: no minimum: 0 maximum: 100 default: 0 mutable: yes - identifier: blur_type title: Blur Type type: integer description: > Decide which blur method is used. 0 for median blur, 1 for gaussian blur, 2 for pixelate, 3 for opaque fill. readonly: no required: no minimum: 0 maximum: 100 default: 0 mutable: yes - identifier: algo title: Tracker Algorithm type: string description: > The algorithm used for tracking (OpenCV name). Check OpenCV doc for full algorithm list, most commons are KCF, MIL, BOOSTING, TLD readonly: no required: no default: KCF mutable: yes - identifier: steps title: Keyframes spacing type: integer description: > Defines the frequency of stored keyframes. A keyframe is created every steps frames. mutable: no readonly: no required: no default: 5 minimum: 0 - identifier: modelsfolder title: OpenCV models folder type: string description: > The folder where the tracker models are stored if any mutable: no readonly: no required: no - identifier: results title: Analysis Results type: string description: > Set after analysis. This is an animated rect following object designated with initial rect property. mutable: yes animation: yes readonly: yes mlt-7.38.0/src/modules/openfx/000775 000000 000000 00000000000 15172202314 016120 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/openfx/CMakeLists.txt000664 000000 000000 00000001744 15172202314 020666 0ustar00rootroot000000 000000 add_library(mltopenfx MODULE factory.c mlt_openfx.c mlt_openfx.h filter_openfx.c ) add_custom_target(Other_openfx_Files SOURCES filter_openfx.yml ) find_package(OpenMP) if(GPL AND TARGET PkgConfig::glib) include(GenerateExportHeader) generate_export_header(mltopenfx) target_compile_options(mltopenfx PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltopenfx PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltopenfx PRIVATE mlt PkgConfig::glib ${CMAKE_DL_LIBS}) target_include_directories(mltopenfx PRIVATE openfx/include) if(NOT MSVC) target_link_libraries(mltopenfx PRIVATE m) endif() if(OpenMP_C_FOUND) target_link_libraries(mltopenfx PRIVATE OpenMP::OpenMP_C) endif() endif() set_target_properties(mltopenfx PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltopenfx LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_openfx.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/openfx) mlt-7.38.0/src/modules/openfx/factory.c000664 000000 000000 00000024277 15172202314 017747 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2025-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mlt_openfx.h" #include #include extern OfxHost MltOfxHost; mlt_properties mltofx_context; mlt_properties mltofx_dl; #if defined(__linux__) || defined(__FreeBSD__) #define OFX_DIRLIST_SEP_CHARS ":;" #define OFX_DIRSEP "/" #include static const char *getArchStr() { if (sizeof(void *) == 4) { #if defined(__linux__) return "Linux-x86"; #else return "FreeBSD-x86"; #endif } else { #if defined(__linux__) return "Linux-x86-64"; #else return "FreeBSD-x86-64"; #endif } } #define OFX_ARCHSTR getArchStr() #elif defined(__APPLE__) #define OFX_DIRLIST_SEP_CHARS ";:" #if defined(__x86_64) || defined(__x86_64__) #define OFX_ARCHSTR "MacOS-x86-64" #else #define OFX_ARCHSTR "MacOS" #endif #define OFX_DIRSEP "/" #include #elif defined(WINDOWS) || defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) \ || defined(__MINGW64__) #define OFX_DIRLIST_SEP_CHARS ";" #if defined(_WIN64) || defined(__MINGW64__) #define OFX_ARCHSTR "Win64" #else #define OFX_ARCHSTR "Win32" #endif #define OFX_DIRSEP "\\" #if defined(__MINGW32__) || defined(__MINGW64__) #include #endif #include "shlobj.h" #include "tchar.h" #endif extern mlt_filter filter_openfx_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static void plugin_mgr_destroy(mlt_properties p) { int cN = mlt_properties_count(mltofx_context); for (int j = 0; j < cN; ++j) { char *id = mlt_properties_get_name(mltofx_context, j); mlt_properties pb = mlt_properties_get_properties(mltofx_context, id); char *dli = mlt_properties_get(pb, "dli"); int index = mlt_properties_get_int(pb, "index"); void *dlhandle = mlt_properties_get_data(mltofx_dl, dli, NULL); OfxGetPluginFn GetPluginFn = dlsym(dlhandle, "OfxGetPlugin"); if (GetPluginFn != NULL) { OfxPlugin *pt = GetPluginFn(index); if (pt == NULL) continue; pt->mainEntry(kOfxActionUnload, NULL, NULL, NULL); } } mlt_properties_close(mltofx_context); mlt_properties_close(mltofx_dl); mlt_properties_close((mlt_properties) MltOfxHost.host); } static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/openfx/%s", mlt_environment("MLT_DATA"), (char *) data); mlt_properties result = mlt_properties_parse_yaml(file); if (result == NULL) { mlt_log_error(NULL, "Failed to parse OpenFX plugin metadata file: %s\n", file); return NULL; } mlt_properties pb = mlt_properties_get_properties(mltofx_context, id); char *dli = mlt_properties_get(pb, "dli"); int index = mlt_properties_get_int(pb, "index"); void *dlhandle = mlt_properties_get_data(mltofx_dl, dli, NULL); OfxGetPluginFn getPluginFn = dlsym(dlhandle, "OfxGetPlugin"); if (!getPluginFn) { mlt_log_error(NULL, "Failed to get OfxGetPlugin function from plugin: %s\n", id); return NULL; } OfxPlugin *pt = getPluginFn(index); mlt_properties_set(result, "identifier", id); // parameters mlt_properties params = mlt_properties_new(); mlt_properties_set_data(result, "parameters", params, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties image_effect = mltofx_fetch_params(pt, params, result); mlt_properties_close(image_effect); // Fall back to the plugin identifier if the plugin did not provide a label. if (!mlt_properties_get(result, "title")) mlt_properties_set(result, "title", id); return result; } static void scan_ofx_dir(mlt_repository repository, const char *dir, int *dli, int depth) { size_t archstr_len = strlen(OFX_ARCHSTR); size_t dir_len = strlen(dir); DIR *d = opendir(dir); if (!d) return; struct dirent *de = readdir(d); while (de) { char *name = de->d_name; char *bni = NULL; if ((bni = strstr(name, ".ofx.bundle")) != NULL && bni[11] == '\0') { // bundle found at this level — load it char *barename = g_strndup(name, (int) (bni - name) + 4); size_t name_len = (size_t) (bni - name) + 4 + 7; // 12b sizeof `Contents` word, 1 sizeof null byte char *binpath = malloc(dir_len + name_len + 12 + (name_len - 7) + archstr_len + 1); sprintf(binpath, "%s/%s/Contents/%s/%s", dir, name, OFX_ARCHSTR, barename); void *dlhandle = dlopen(binpath, RTLD_LOCAL | RTLD_LAZY); free(binpath); free(barename); if (!dlhandle) { de = readdir(d); continue; } OfxGetPluginFn ofx_get_plugin = dlsym(dlhandle, "OfxGetPlugin"); OfxGetNumberOfPluginsFn ofx_get_number_of_plugins = dlsym(dlhandle, "OfxGetNumberOfPlugins"); if (!ofx_get_plugin || !ofx_get_number_of_plugins) { dlclose(dlhandle); de = readdir(d); continue; } char dl_n[16] = {'\0'}; sprintf(dl_n, "%d", *dli); mlt_properties_set_data(mltofx_dl, dl_n, dlhandle, 0, (mlt_destructor) dlclose, NULL); (*dli)++; int plugin_count = ofx_get_number_of_plugins(); for (int i = 0; i < plugin_count; ++i) { OfxPlugin *plugin_ptr = ofx_get_plugin(i); if (!plugin_ptr) break; int detected = mltofx_detect_plugin(plugin_ptr); if (!detected) continue; char *s = NULL; size_t pluginIdentifier_len = strlen(plugin_ptr->pluginIdentifier); s = malloc(pluginIdentifier_len + 8); sprintf(s, "openfx.%s", plugin_ptr->pluginIdentifier); // if colon `:` exists in plugin identifier // change it to accent sign `^` because `:` // can cause issues with mlt if put in filter // name char *str_ptr = strchr(s, ':'); while (str_ptr != NULL) { *str_ptr++ = '^'; str_ptr = strchr(str_ptr, ':'); } mlt_properties p = mlt_properties_new(); mlt_properties_set_properties(mltofx_context, s, p); mlt_properties_close(p); mlt_properties_set(p, "dli", dl_n); mlt_properties_set_int(p, "index", i); MLT_REGISTER(mlt_service_filter_type, s, filter_openfx_init); MLT_REGISTER_METADATA(mlt_service_filter_type, s, metadata, "filter_openfx.yml"); free(s); } } else if (depth == 0 && name[0] != '.') { // Try one level of subdirectory (e.g. vendor subfolders inside an OFX plugins dir) char *subdir = malloc(dir_len + strlen(name) + 2); sprintf(subdir, "%s/%s", dir, name); scan_ofx_dir(repository, subdir, dli, 1); free(subdir); } de = readdir(d); } closedir(d); } MLT_REPOSITORY { MltOfxHost.host = (OfxPropertySetHandle) mlt_properties_new(); mltofx_init_host_properties(MltOfxHost.host); // Quiet warnings from plugins that use OCIO char *ocio = getenv("OCIO"); if (!ocio || !strcmp(ocio, "")) { // If the OCIO environment variable is not set, set it to a built-in default config setenv("OCIO", "ocio://ocio://default", 1); } mltofx_context = mlt_properties_new(); mltofx_dl = mlt_properties_new(); int dli = 0; // Scan standard platform default paths per the OpenFX specification #if defined(__linux__) || defined(__FreeBSD__) scan_ofx_dir(repository, "/usr/OFX/Plugins", &dli, 0); scan_ofx_dir(repository, "/usr/local/OFX/Plugins", &dli, 0); #elif defined(__APPLE__) scan_ofx_dir(repository, "/Library/OFX/Plugins", &dli, 0); const char *home = getenv("HOME"); if (home) { char user_ofx[PATH_MAX]; snprintf(user_ofx, PATH_MAX, "%s/Library/OFX/Plugins", home); scan_ofx_dir(repository, user_ofx, &dli, 0); } #elif defined(WINDOWS) || defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) \ || defined(__MINGW64__) char common_files[MAX_PATH]; if (SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, 0, common_files) == S_OK) { char win_ofx[MAX_PATH + 13]; // +13 for "\OFX\Plugins" + NUL snprintf(win_ofx, sizeof(win_ofx), "%s\\OFX\\Plugins", common_files); scan_ofx_dir(repository, win_ofx, &dli, 0); } #endif // Also scan OFX_PLUGIN_PATH if set (supplements default paths per OpenFX spec) char *openfx_path = getenv("OFX_PLUGIN_PATH"); if (openfx_path) { char *path_copy = strdup(openfx_path); char *saveptr; for (char *strptr = path_copy;; strptr = NULL) { char *dir = strtok_r(strptr, MLT_DIRLIST_DELIMITER, &saveptr); if (dir == NULL) break; scan_ofx_dir(repository, dir, &dli, 0); } free(path_copy); } mlt_factory_register_for_clean_up(mltofx_dl, (mlt_destructor) plugin_mgr_destroy); } mlt-7.38.0/src/modules/openfx/filter_openfx.c000664 000000 000000 00000037661 15172202314 021145 0ustar00rootroot000000 000000 /* * filter_openfx.c -- filter Video through OpenFX plugins * Copyright (C) 2025-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mlt_openfx.h" #include #include #include extern OfxHost MltOfxHost; extern mlt_properties mltofx_context; extern mlt_properties mltofx_dl; static const char OFX_IMAGE_EFFECT[] = "_ofx_image_effect"; static mlt_image_format select_image_format(mlt_image_format incoming, mltofx_depths_mask plugin_support_depths, int plugin_handles_16bit, int plugin_wants_rgba, int plugin_wants_rgb) { // Honour the incoming bit-depth: stay at 8-bit when the plugin handles byte, // only escalate to 16-bit when it cannot. if (incoming == mlt_image_rgb || incoming == mlt_image_rgba || incoming == mlt_image_yuv422 || incoming == mlt_image_yuv420p) { if (plugin_support_depths & mltofx_depth_byte) return plugin_wants_rgba ? mlt_image_rgba : mlt_image_rgb; else if (plugin_handles_16bit && (plugin_wants_rgba || plugin_wants_rgb)) return mlt_image_rgba64; else return mlt_image_rgb; } else { // 10-bit or higher — prefer 16-bit OFX path when available if (plugin_handles_16bit && (plugin_wants_rgba || plugin_wants_rgb)) return mlt_image_rgba64; else return plugin_wants_rgba ? mlt_image_rgba : mlt_image_rgb; } } static void update_plugin_params(mlt_properties properties, mlt_properties image_effect_params, mlt_properties params, mlt_position position, mlt_position length) { int params_count = mlt_properties_count(params); for (int i = 0; i < params_count; ++i) { char *param_key = mlt_properties_get_name(params, i); mlt_properties param = mlt_properties_get_data(params, param_key, NULL); if (!param) continue; char *param_name = mlt_properties_get(param, "identifier"); if (!param_name) continue; char *type = mlt_properties_get(param, "type"); char *widget = mlt_properties_get(param, "widget"); if (!type) continue; if (widget && (strcmp(widget, "point") == 0 || strcmp(widget, "size") == 0) && strcmp(type, "float") == 0) { mlt_rect value = mlt_properties_anim_get_rect(properties, param_name, position, length); mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_double2d, value); } else if (strcmp(type, "float") == 0) { double value = mlt_properties_anim_get_double(properties, param_name, position, length); mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_double, value); } else if (strcmp(type, "integer") == 0 || strcmp(type, "boolean") == 0) { int value = mlt_properties_anim_get_int(properties, param_name, position, length); mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_int, value); } else if (strcmp(type, "string") == 0) { char *value = mlt_properties_anim_get(properties, param_name, position, length); if (value) mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_string, value); } else if (strcmp(type, "color") == 0) { mlt_color value = mlt_properties_anim_get_color(properties, param_name, position, length); mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_color, value); } } } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); OfxPlugin *plugin = mlt_properties_get_data(properties, "ofx_plugin", NULL); mlt_properties image_effect = mlt_properties_get_properties(properties, OFX_IMAGE_EFFECT); mlt_properties params = mlt_properties_get_properties(image_effect, "mltofx_params"); mlt_properties image_effect_params = mlt_properties_get_properties(image_effect, "params"); mlt_image_format requested_format = *format; mltofx_depths_mask plugin_support_depths = mltofx_plugin_supported_depths(image_effect); mltofx_components_mask plugin_support_components = mltofx_plugin_supported_components( image_effect); // Format negotiation const int plugin_handles_16bit = plugin_support_depths & (mltofx_depth_short | mltofx_depth_half | mltofx_depth_float); const int plugin_wants_rgba = plugin_support_components & mltofx_components_rgba; const int plugin_wants_rgb = plugin_support_components & mltofx_components_rgb; *format = select_image_format(*format, plugin_support_depths, plugin_handles_16bit, plugin_wants_rgba, plugin_wants_rgb); const int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error) return error; double pixel_aspect_ratio = mlt_frame_get_aspect_ratio(frame); if (pixel_aspect_ratio <= 0.0) pixel_aspect_ratio = 1.0; mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); double ofx_time = (double) position; // Determine depth conversion needed for the 16-bit path (short > half > float). int use_half = (*format == mlt_image_rgba64) && !(plugin_support_depths & mltofx_depth_short) && (plugin_support_depths & mltofx_depth_half); int use_float = (*format == mlt_image_rgba64) && !(plugin_support_depths & mltofx_depth_short) && !(plugin_support_depths & mltofx_depth_half) && (plugin_support_depths & mltofx_depth_float); // Allocate and convert buffers outside the lock — these are per-frame operations // and do not touch shared plugin instance state. uint16_t *half_src = NULL, *half_out = NULL; float *float_src = NULL, *float_out = NULL; uint8_t *src_copy = NULL; struct mlt_image_s src_img_copy; if (use_half) { int n = *width * *height; half_src = mltofx_rgba64_to_half((const uint16_t *) *image, n); half_out = malloc((size_t) n * 4 * sizeof(uint16_t)); if (!half_src || !half_out) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to allocate half-float buffers\n"); free(half_src); free(half_out); return 1; } } else if (use_float) { // float_out is also used as prime_buf so it must be allocated before the lock. int n = *width * *height; float_out = malloc((size_t) n * 4 * sizeof(float)); float_src = mltofx_rgba64_to_float((const uint16_t *) *image, n); if (!float_out || !float_src) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to allocate float buffers\n"); free(float_out); free(float_src); return 1; } } else { // Short/byte path: keep a read-only source copy separate from the output buffer. struct mlt_image_s src_img; mlt_image_set_values(&src_img, *image, *format, *width, *height); mlt_image_set_values(&src_img_copy, NULL, *format, *width, *height); mlt_image_alloc_data(&src_img_copy); src_copy = src_img_copy.data; memcpy(src_copy, *image, mlt_image_calculate_size(&src_img)); } const char *ofx_depth = use_half ? kOfxBitDepthHalf : use_float ? kOfxBitDepthFloat : NULL; // For the float path, prime_buf must be the output-sized float buffer so that // clip metadata advertises the correct row bytes during GetClipPreferences / GetROI. uint8_t *prime_buf = use_float ? (uint8_t *) float_out : *image; uint8_t *render_src = use_half ? (uint8_t *) half_src : use_float ? (uint8_t *) float_src : src_copy; uint8_t *render_dst = use_half ? (uint8_t *) half_out : use_float ? (uint8_t *) float_out : *image; mlt_service_lock(MLT_FILTER_SERVICE(filter)); update_plugin_params(properties, image_effect_params, params, position, length); // Prime both clips with correct metadata before pre-render actions. // Some plugins (e.g. spatial transforms) read clip depth/bounds during // GetClipPreferences / GetRegionsOfInterest to set up their pipeline. mltofx_set_source_clip_data(plugin, image_effect, prime_buf, *width, *height, *format, pixel_aspect_ratio, ofx_depth); mltofx_set_output_clip_data(plugin, image_effect, prime_buf, *width, *height, *format, pixel_aspect_ratio, ofx_depth); // OFX pre-render action order: GetClipPreferences → GetRegionsOfInterest → BeginSequenceRender mltofx_get_clip_preferences(plugin, image_effect); mltofx_get_regions_of_interest(plugin, image_effect, ofx_time, (double) *width, (double) *height); mltofx_begin_sequence_render(plugin, image_effect, ofx_time); // Point clips at the actual render buffers before calling Render. mltofx_set_source_clip_data(plugin, image_effect, render_src, *width, *height, *format, pixel_aspect_ratio, ofx_depth); mltofx_set_output_clip_data(plugin, image_effect, render_dst, *width, *height, *format, pixel_aspect_ratio, ofx_depth); mltofx_action_render(plugin, image_effect, ofx_time, *width, *height); mltofx_end_sequence_render(plugin, image_effect, ofx_time); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Convert output back to rgba64 in-place — outside the lock, per-frame only. if (use_half) { mltofx_half_to_rgba64(half_out, (uint16_t *) *image, *width * *height); free(half_src); free(half_out); } else if (use_float) { mltofx_float_to_rgba64(float_out, (uint16_t *) *image, *width * *height); free(float_src); free(float_out); } else { mlt_image_close(&src_img_copy); } if (*format != requested_format) { frame->convert_image(frame, image, format, mlt_image_rgba); *format = mlt_image_rgba; } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); OfxPlugin *plugin = mlt_properties_get_data(properties, "ofx_plugin", NULL); mlt_properties image_effect = mlt_properties_get_properties(properties, OFX_IMAGE_EFFECT); mltofx_destroy_instance(plugin, image_effect); filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } mlt_filter filter_openfx_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (!filter) { mlt_log_error(filter, "Unable to create filter: %s\n", id); return NULL; } mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set(properties, "resource", arg); if (strncmp(id, "openfx.", 7)) { mlt_log_error(filter, "Invalid ID: %s\n", id); mlt_filter_close(filter); return NULL; } mlt_properties_set(properties, "_pluginid", id + 7); mlt_properties pb = mlt_properties_get_properties(mltofx_context, id); char *dli = mlt_properties_get(pb, "dli"); int index = mlt_properties_get_int(pb, "index"); void *dlhandle = mlt_properties_get_data(mltofx_dl, dli, NULL); OfxGetPluginFn GetPluginFn = dlsym(dlhandle, "OfxGetPlugin"); if (!GetPluginFn) { mlt_log_error(filter, "Failed to get OfxGetPlugin function from plugin: %s\n", id); mlt_filter_close(filter); return NULL; } OfxPlugin *pt = GetPluginFn(index); if (!pt) { mlt_log_error(filter, "Failed to get plugin from OfxGetPlugin: %s\n", id); mlt_filter_close(filter); return NULL; } mlt_properties params = mlt_properties_new(); mlt_properties image_effect = mltofx_fetch_params(pt, params, NULL); mltofx_create_instance(pt, image_effect); mlt_properties_set_properties(image_effect, "begin_sequence_props", mlt_properties_new()); mlt_properties_close(mlt_properties_get_properties(image_effect, "get_rod_in_args")); mlt_properties_set_properties(image_effect, "end_sequence_props", mlt_properties_new()); mlt_properties_close(mlt_properties_get_properties(image_effect, "get_rod_out_args")); mlt_properties_set_properties(image_effect, "get_rod_in_args", mlt_properties_new()); mlt_properties_close(mlt_properties_get_properties(image_effect, "get_roi_in_args")); mlt_properties_set_properties(image_effect, "get_rod_out_args", mlt_properties_new()); mlt_properties_close(mlt_properties_get_properties(image_effect, "get_roi_out_args")); mlt_properties_set_properties(image_effect, "get_roi_in_args", mlt_properties_new()); mlt_properties_close(mlt_properties_get_properties(image_effect, "get_roi_in_args")); mlt_properties_set_properties(image_effect, "get_roi_out_args", mlt_properties_new()); mlt_properties_close(mlt_properties_get_properties(image_effect, "get_roi_out_args")); mlt_properties_set_properties(image_effect, "get_clippref_args", mlt_properties_new()); mlt_properties_close(mlt_properties_get_properties(image_effect, "get_clippref_args")); mlt_properties_set_properties(image_effect, "render_in_args", mlt_properties_new()); mlt_properties_close(mlt_properties_get_properties(image_effect, "render_in_args")); mlt_properties_set_data(properties, "ofx_plugin", pt, 0, NULL, NULL); mlt_properties_set_properties(properties, OFX_IMAGE_EFFECT, image_effect); mlt_properties_close(image_effect); mlt_properties_close(params); filter->process = filter_process; filter->close = filter_close; return filter; } mlt-7.38.0/src/modules/openfx/filter_openfx.yml000664 000000 000000 00000000416 15172202314 021510 0ustar00rootroot000000 000000 schema_version: 7.1 type: filter identifier: openfx title: OpenFX version: 1 license: GPLv2 language: en url: https://openeffects.org/ creator: mr.fantastic tags: - Video - experimental description: Process videos using OpenFX 1.5 plugins. mlt-7.38.0/src/modules/openfx/mlt_openfx.c000664 000000 000000 00000321416 15172202314 020446 0ustar00rootroot000000 000000 /* * mlt_openfx.c -- common mlt functions for OpenFX plugins * Copyright (C) 2025-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "mlt_openfx.h" #include #include #include #include #include #include #ifdef _OPENMP #include #endif /* OpenFX Header files https://github.com/AcademySoftwareFoundation/openfx/tree/main/include */ #include #include #include #include #include #include #include static inline uint16_t f32_to_f16(float f) { uint32_t x; memcpy(&x, &f, sizeof(x)); uint16_t sign = (x >> 31) & 0x0001U; int32_t exp = ((x >> 23) & 0x00FFU) - 127 + 15; uint16_t mant = (x >> 13) & 0x03FFU; if (exp <= 0) return (uint16_t) (sign << 15); if (exp >= 31) return (uint16_t) ((sign << 15) | 0x7C00U); return (uint16_t) ((sign << 15) | ((uint16_t) exp << 10) | mant); } static inline float f16_to_f32(uint16_t h) { uint32_t sign = (h >> 15) & 0x0001U; uint32_t exp = (h >> 10) & 0x001FU; uint32_t mant = h & 0x03FFU; uint32_t x; if (exp == 0) x = (sign << 31) | (mant << 13); else if (exp == 31) x = (sign << 31) | (0xFFU << 23) | (mant << 13); else x = (sign << 31) | ((exp - 15 + 127) << 23) | (mant << 13); float f; memcpy(&f, &x, sizeof(f)); return f; } uint16_t *mltofx_rgba64_to_half(const uint16_t *src, int n_pixels) { int count = n_pixels * 4; uint16_t *dst = malloc((size_t) count * sizeof(uint16_t)); if (!dst) return NULL; #ifdef _OPENMP #pragma omp parallel for schedule(static) #endif for (int i = 0; i < count; ++i) dst[i] = f32_to_f16((float) src[i] * (1.0f / 65535.0f)); return dst; } void mltofx_half_to_rgba64(const uint16_t *src, uint16_t *dst, int n_pixels) { int count = n_pixels * 4; #ifdef _OPENMP #pragma omp parallel for schedule(static) #endif for (int i = 0; i < count; ++i) { float v = f16_to_f32(src[i]); // This clamping using >= and <= handles NaN and +/-inf v = (v >= 0.0f) ? (v <= 1.0f ? v : 1.0f) : 0.0f; dst[i] = (uint16_t) (v * 65535.0f + 0.5f); } } float *mltofx_rgba64_to_float(const uint16_t *src, int n_pixels) { int count = n_pixels * 4; float *dst = malloc((size_t) count * sizeof(float)); if (!dst) return NULL; #ifdef _OPENMP #pragma omp parallel for schedule(static) #endif for (int i = 0; i < count; ++i) dst[i] = (float) src[i] * (1.0f / 65535.0f); return dst; } void mltofx_float_to_rgba64(const float *src, uint16_t *dst, int n_pixels) { int count = n_pixels * 4; #ifdef _OPENMP #pragma omp parallel for schedule(static) #endif for (int i = 0; i < count; ++i) { float v = src[i]; // This clamping using >= and <= handles NaN and +/-inf v = (v >= 0.0f) ? (v <= 1.0f ? v : 1.0f) : 0.0f; dst[i] = (uint16_t) (v * 65535.0f + 0.5f); } } static OfxStatus propGetInt(OfxPropertySetHandle properties, const char *property, int index, int *value); static OfxStatus propGetIntN(OfxPropertySetHandle properties, const char *property, int count, int *value); static OfxStatus getPropertySet(OfxImageEffectHandle imageEffect, OfxPropertySetHandle *propHandle) { if (!imageEffect) return kOfxStatErrBadHandle; mlt_properties image_effect = (mlt_properties) imageEffect; *propHandle = (OfxPropertySetHandle) mlt_properties_get_properties(image_effect, "props"); return kOfxStatOK; } static OfxStatus getParamSet(OfxImageEffectHandle imageEffect, OfxParamSetHandle *paramSet) { if (!imageEffect) return kOfxStatErrBadHandle; *paramSet = (OfxParamSetHandle) mlt_properties_get_properties((mlt_properties) imageEffect, "params"); return kOfxStatOK; } static OfxStatus clipDefine(OfxImageEffectHandle imageEffect, const char *name, OfxPropertySetHandle *propertySet) { mlt_log_debug(NULL, "[openfx] clipDefine: `%s` ## %p\n", name, propertySet); if (!imageEffect) return kOfxStatErrBadHandle; mlt_properties set = mlt_properties_get_properties((mlt_properties) imageEffect, "clips"); mlt_properties clip = mlt_properties_new(); mlt_properties clip_props = mlt_properties_new(); if (!clip || !clip_props) return kOfxStatErrMemory; mlt_properties_set_properties(set, name, clip); mlt_properties_close(clip); mlt_properties_set_properties(clip, "props", clip_props); mlt_properties_close(clip_props); *propertySet = (OfxPropertySetHandle) clip_props; return kOfxStatOK; } static OfxStatus clipGetHandle(OfxImageEffectHandle imageEffect, const char *name, OfxImageClipHandle *clip, OfxPropertySetHandle *propertySet) { if (!imageEffect) return kOfxStatErrBadHandle; mlt_properties set = mlt_properties_get_properties((mlt_properties) imageEffect, "clips"); mlt_properties clip_temp = mlt_properties_get_properties(set, name); *clip = (OfxImageClipHandle) clip_temp; if (propertySet != NULL) *propertySet = (OfxPropertySetHandle) mlt_properties_get_properties(clip_temp, "props"); return kOfxStatOK; } static OfxStatus clipGetPropertySet(OfxImageClipHandle clip, OfxPropertySetHandle *propHandle) { if (!clip) return kOfxStatErrBadHandle; *propHandle = (OfxPropertySetHandle) mlt_properties_get_properties((mlt_properties) clip, "props"); return kOfxStatOK; } static OfxStatus clipGetImage(OfxImageClipHandle clip, OfxTime time, const OfxRectD *region, OfxPropertySetHandle *imageHandle) { if (!clip) return kOfxStatErrBadHandle; *imageHandle = (OfxPropertySetHandle) mlt_properties_get_properties((mlt_properties) clip, "props"); if (region != NULL) { OfxRectI rect = {0, 0, 0, 0}; propGetIntN((OfxPropertySetHandle) *imageHandle, kOfxImagePropBounds, 4, &rect.x1); OfxRectD *region2 = (OfxRectD *) region; region2->x1 = (double) rect.x1; region2->x2 = (double) rect.x2; region2->y1 = (double) rect.y1; region2->y2 = (double) rect.y2; } return kOfxStatOK; } static OfxStatus clipReleaseImage(OfxPropertySetHandle imageHandle) { return kOfxStatOK; } static OfxStatus clipGetRegionOfDefinition(OfxImageClipHandle clip, OfxTime time, OfxRectD *bounds) { if (!clip || !bounds) { return kOfxStatErrBadHandle; } mlt_properties clip_prop; clipGetPropertySet(clip, (OfxPropertySetHandle *) &clip_prop); int x1 = 0, y1 = 0, x2 = 0, y2 = 0; propGetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 0, &x1); propGetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 1, &y1); propGetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 2, &x2); propGetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 3, &y2); bounds->x1 = (double) x1; bounds->y1 = (double) y1; bounds->x2 = (double) x2; bounds->y2 = (double) y2; if (bounds->x2 < bounds->x1 || bounds->y2 < bounds->y1) { // the RoD is invalid (empty is OK) return kOfxStatFailed; } return kOfxStatOK; } static int abortOfxFn(OfxImageEffectHandle imageEffect) { return 0; } static OfxStatus imageMemoryAlloc(OfxImageEffectHandle instanceHandle, size_t nBytes, OfxImageMemoryHandle *memoryHandle) { if (instanceHandle == NULL) { *memoryHandle = NULL; return kOfxStatErrBadHandle; } void *temp = calloc(1, nBytes); if (temp == NULL) return kOfxStatErrMemory; *memoryHandle = temp; return kOfxStatOK; } static OfxStatus imageMemoryFree(OfxImageMemoryHandle memoryHandle) { free(memoryHandle); return kOfxStatOK; } static OfxStatus imageMemoryLock(OfxImageMemoryHandle memoryHandle, void **returnedPtr) { if (returnedPtr) *returnedPtr = memoryHandle; return kOfxStatOK; } static OfxStatus imageMemoryUnlock(OfxImageMemoryHandle memoryHandle) { return kOfxStatOK; } static OfxImageEffectSuiteV1 MltOfxImageEffectSuiteV1 = {getPropertySet, getParamSet, clipDefine, clipGetHandle, clipGetPropertySet, clipGetImage, clipReleaseImage, clipGetRegionOfDefinition, abortOfxFn, imageMemoryAlloc, imageMemoryFree, imageMemoryLock, imageMemoryUnlock}; static mlt_properties fetch_mlt_properties(OfxPropertySetHandle properties, int index) { if (index < 0) return NULL; mlt_properties props = (mlt_properties) properties; char index_str[12] = {'\0'}; snprintf(index_str, sizeof(index_str), "%d", index); mlt_properties p = mlt_properties_get_properties(props, index_str); return p; } static mlt_properties get_mlt_properties(OfxPropertySetHandle properties, int index) { if (index < 0) return NULL; mlt_properties props = (mlt_properties) properties; char index_str[12] = {'\0'}; snprintf(index_str, sizeof(index_str), "%d", index); mlt_properties p = mlt_properties_get_properties(props, index_str); if (!p) { p = mlt_properties_new(); mlt_properties_set_properties(props, index_str, p); mlt_properties_close(p); } return p; } static OfxStatus propSetPointer(OfxPropertySetHandle properties, const char *property, int index, void *value) { if (properties == NULL) return kOfxStatErrBadHandle; mlt_properties p = get_mlt_properties(properties, index); if (!p) return kOfxStatErrBadIndex; mlt_properties_set_data(p, property, value, 0, NULL, NULL); return kOfxStatOK; } static OfxStatus propSetString(OfxPropertySetHandle properties, const char *property, int index, const char *value) { if (properties == NULL) return kOfxStatErrBadHandle; mlt_properties p = get_mlt_properties(properties, index); if (!p) return kOfxStatErrBadIndex; mlt_properties_set(p, property, value); return kOfxStatOK; } static OfxStatus propSetDouble(OfxPropertySetHandle properties, const char *property, int index, double value) { if (properties == NULL) return kOfxStatErrBadHandle; mlt_properties p = get_mlt_properties(properties, index); if (!p) return kOfxStatErrBadIndex; mlt_properties_set_double(p, property, value); return kOfxStatOK; } static OfxStatus propSetInt(OfxPropertySetHandle properties, const char *property, int index, int value) { if (properties == NULL) return kOfxStatErrBadHandle; mlt_properties p = get_mlt_properties(properties, index); if (!p) return kOfxStatErrBadIndex; mlt_properties_set_int(p, property, value); return kOfxStatOK; } static OfxStatus propSetPointerN(OfxPropertySetHandle properties, const char *property, int count, void *const *value) { if (properties == NULL) return kOfxStatErrBadHandle; for (int i = 0; i < count; ++i) { mlt_properties p = get_mlt_properties(properties, i); if (!p) return kOfxStatErrBadIndex; mlt_properties_set_data(p, property, value[i], 0, NULL, NULL); } return kOfxStatOK; } static OfxStatus propSetStringN(OfxPropertySetHandle properties, const char *property, int count, const char *const *value) { if (properties == NULL) return kOfxStatErrBadHandle; for (int i = 0; i < count; ++i) { mlt_properties p = get_mlt_properties(properties, i); if (!p) return kOfxStatErrBadIndex; mlt_properties_set(p, property, value[i]); } return kOfxStatOK; } static OfxStatus propSetDoubleN(OfxPropertySetHandle properties, const char *property, int count, const double *value) { if (properties == NULL) return kOfxStatErrBadHandle; for (int i = 0; i < count; ++i) { mlt_properties p = get_mlt_properties(properties, i); if (!p) return kOfxStatErrBadIndex; mlt_properties_set_double(p, property, value[i]); } return kOfxStatOK; } static OfxStatus propSetIntN(OfxPropertySetHandle properties, const char *property, int count, const int *value) { if (properties == NULL) return kOfxStatErrBadHandle; for (int i = 0; i < count; ++i) { mlt_properties p = get_mlt_properties(properties, i); if (!p) return kOfxStatErrBadIndex; mlt_properties_set_int(p, property, value[i]); } return kOfxStatOK; } static OfxStatus propGetPointer(OfxPropertySetHandle properties, const char *property, int index, void **value) { if (properties == NULL) return kOfxStatErrBadHandle; mlt_properties p = fetch_mlt_properties(properties, index); if (!p) return kOfxStatErrBadIndex; if (!mlt_properties_exists(p, property)) { mlt_log_debug(NULL, "[openfx] propGetPointer: '%s'[%d] not found\n", property, index); return kOfxStatErrUnknown; } *value = mlt_properties_get_data(p, property, NULL); return kOfxStatOK; } static OfxStatus propGetString(OfxPropertySetHandle properties, const char *property, int index, char **value) { if (properties == NULL) return kOfxStatErrBadHandle; mlt_properties p = fetch_mlt_properties(properties, index); if (!p) return kOfxStatErrBadIndex; if (!mlt_properties_exists(p, property)) { mlt_log_debug(NULL, "[openfx] propGetString: '%s'[%d] not found\n", property, index); return kOfxStatErrUnknown; } *value = mlt_properties_get(p, property); return kOfxStatOK; } static OfxStatus propGetDouble(OfxPropertySetHandle properties, const char *property, int index, double *value) { if (properties == NULL) return kOfxStatErrBadHandle; mlt_properties p = fetch_mlt_properties(properties, index); if (!p) return kOfxStatErrBadIndex; if (!mlt_properties_exists(p, property)) { mlt_log_debug(NULL, "[openfx] propGetDouble: '%s'[%d] not found\n", property, index); return kOfxStatErrUnknown; } *value = mlt_properties_get_double(p, property); return kOfxStatOK; } static OfxStatus propGetInt(OfxPropertySetHandle properties, const char *property, int index, int *value) { if (properties == NULL) return kOfxStatErrBadHandle; mlt_properties p = fetch_mlt_properties(properties, index); if (!p) return kOfxStatErrBadIndex; if (!mlt_properties_exists(p, property)) { mlt_log_debug(NULL, "[openfx] propGetInt: '%s'[%d] not found\n", property, index); return kOfxStatErrUnknown; } *value = mlt_properties_get_int(p, property); return kOfxStatOK; } static OfxStatus propGetPointerN(OfxPropertySetHandle properties, const char *property, int count, void **value) { if (properties == NULL) return kOfxStatErrBadHandle; for (int i = 0; i < count; ++i) { mlt_properties p = fetch_mlt_properties(properties, i); if (!p) return kOfxStatErrBadIndex; if (!mlt_properties_exists(p, property)) { mlt_log_debug(NULL, "[openfx] propGetPointerN: '%s'[%d] not found\n", property, i); return kOfxStatErrUnknown; } value[i] = mlt_properties_get_data(p, property, NULL); } return kOfxStatOK; } static OfxStatus propGetStringN(OfxPropertySetHandle properties, const char *property, int count, char **value) { if (properties == NULL) return kOfxStatErrBadHandle; for (int i = 0; i < count; ++i) { mlt_properties p = fetch_mlt_properties(properties, i); if (!p) return kOfxStatErrBadIndex; if (!mlt_properties_exists(p, property)) { mlt_log_debug(NULL, "[openfx] propGetStringN: '%s'[%d] not found\n", property, i); return kOfxStatErrUnknown; } value[i] = mlt_properties_get(p, property); } return kOfxStatOK; } static OfxStatus propGetDoubleN(OfxPropertySetHandle properties, const char *property, int count, double *value) { if (properties == NULL) return kOfxStatErrBadHandle; for (int i = 0; i < count; ++i) { mlt_properties p = fetch_mlt_properties(properties, i); if (!p) return kOfxStatErrBadIndex; if (!mlt_properties_exists(p, property)) { mlt_log_debug(NULL, "[openfx] propGetDoubleN: '%s'[%d] not found\n", property, i); return kOfxStatErrUnknown; } value[i] = mlt_properties_get_double(p, property); } return kOfxStatOK; } static OfxStatus propGetIntN(OfxPropertySetHandle properties, const char *property, int count, int *value) { if (properties == NULL) return kOfxStatErrBadHandle; for (int i = 0; i < count; ++i) { mlt_properties p = fetch_mlt_properties(properties, i); if (!p) return kOfxStatErrBadIndex; if (!mlt_properties_exists(p, property)) { mlt_log_debug(NULL, "[openfx] propGetIntN: '%s'[%d] not found\n", property, i); return kOfxStatErrUnknown; } value[i] = mlt_properties_get_int(p, property); } return kOfxStatOK; } static OfxStatus propReset(OfxPropertySetHandle properties, const char *property) { if (properties == NULL) return kOfxStatErrBadHandle; int i = 0; while (true) { mlt_properties p = fetch_mlt_properties(properties, i); if (!p) break; mlt_properties_clear(p, property); i++; } return kOfxStatOK; } static OfxStatus propGetDimension(OfxPropertySetHandle properties, const char *property, int *count) { if (properties == NULL) return kOfxStatErrBadHandle; *count = 0; while (true) { mlt_properties p = fetch_mlt_properties(properties, *count); if (!p) break; if (mlt_properties_exists(p, property)) { (*count)++; } else { break; } } return kOfxStatOK; } static OfxPropertySuiteV1 MltOfxPropertySuiteV1 = {propSetPointer, propSetString, propSetDouble, propSetInt, propSetPointerN, propSetStringN, propSetDoubleN, propSetIntN, propGetPointer, propGetString, propGetDouble, propGetInt, propGetPointerN, propGetStringN, propGetDoubleN, propGetIntN, propReset, propGetDimension}; static OfxStatus paramDefine(OfxParamSetHandle paramSet, const char *paramType, const char *name, OfxPropertySetHandle *propertySet) { mlt_log_debug(NULL, "[openfx] paramDefine: `%s`:`%s`\n", name, paramType); if (paramSet == NULL) return kOfxStatErrBadHandle; mlt_properties params = (mlt_properties) paramSet; mlt_properties p = mlt_properties_get_properties(params, name); if (p != NULL) return kOfxStatErrExists; if (strcmp(kOfxParamTypeInteger, paramType) == 0 || strcmp(kOfxParamTypeDouble, paramType) == 0 || strcmp(kOfxParamTypeBoolean, paramType) == 0 || strcmp(kOfxParamTypeChoice, paramType) == 0 || strcmp(kOfxParamTypeStrChoice, paramType) == 0 || strcmp(kOfxParamTypeRGBA, paramType) == 0 || strcmp(kOfxParamTypeRGB, paramType) == 0 || strcmp(kOfxParamTypeDouble2D, paramType) == 0 || strcmp(kOfxParamTypeInteger2D, paramType) == 0 || strcmp(kOfxParamTypeDouble3D, paramType) == 0 || strcmp(kOfxParamTypeInteger3D, paramType) == 0 || strcmp(kOfxParamTypeString, paramType) == 0 || strcmp(kOfxParamTypeCustom, paramType) == 0 || strcmp(kOfxParamTypeBytes, paramType) == 0 || strcmp(kOfxParamTypeGroup, paramType) == 0 // Currently page and push buttons are ignored but we need them so plugins keep feeding use with parameters || strcmp(kOfxParamTypePage, paramType) == 0 || strcmp(kOfxParamTypePushButton, paramType) == 0) { mlt_properties pt = mlt_properties_new(); mlt_properties_set_string(pt, "t", paramType); mlt_properties param_props = mlt_properties_new(); mlt_properties_set_properties(pt, "p", param_props); mlt_properties_close(param_props); mlt_properties_set_properties(params, name, pt); mlt_properties_close(pt); propSetString((OfxPropertySetHandle) param_props, kOfxParamPropType, 0, paramType); if (propertySet != NULL) { *propertySet = (OfxPropertySetHandle) param_props; } } else { return kOfxStatErrUnknown; } return kOfxStatOK; } static OfxStatus paramGetHandle(OfxParamSetHandle paramSet, const char *name, OfxParamHandle *param, OfxPropertySetHandle *propertySet) { if (!paramSet) return kOfxStatErrBadHandle; mlt_properties p = mlt_properties_get_properties((mlt_properties) paramSet, name); if (!p) return kOfxStatErrUnknown; *param = (OfxParamHandle) p; mlt_properties param_props = mlt_properties_get_properties(p, "p"); if (param_props != NULL && propertySet != NULL) { *propertySet = (OfxPropertySetHandle) param_props; } return kOfxStatOK; } static OfxStatus paramSetGetPropertySet(OfxParamSetHandle paramSet, OfxPropertySetHandle *propHandle) { if (!paramSet) return kOfxStatErrBadHandle; mlt_properties plugin_props = mlt_properties_get_properties((mlt_properties) paramSet, "plugin_props"); if (!plugin_props) return kOfxStatErrUnknown; *propHandle = (OfxPropertySetHandle) plugin_props; return kOfxStatOK; } static OfxStatus paramGetPropertySet(OfxParamHandle param, OfxPropertySetHandle *propHandle) { if (!param) return kOfxStatErrBadHandle; mlt_properties param_props = mlt_properties_get_properties((mlt_properties) param, "p"); if (!param_props) return kOfxStatErrUnknown; *propHandle = (OfxPropertySetHandle) param_props; return kOfxStatOK; } static OfxStatus paramGetValueImpl(OfxParamHandle paramHandle, va_list ap) { mlt_properties param = (mlt_properties) paramHandle; char *param_type = mlt_properties_get(param, "t"); mlt_properties param_props = mlt_properties_get_properties(param, "p"); if (strcmp(param_type, kOfxParamTypeInteger) == 0 || strcmp(param_type, kOfxParamTypeBoolean) == 0) { int *value = va_arg(ap, int *); OfxStatus status = propGetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); if (status != kOfxStatOK) { status = propGetInt((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, value); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } else if (strcmp(param_type, kOfxParamTypeChoice) == 0) { // MltOfxParamValue stores the string label; convert to integer index for the OFX plugin. int *value = va_arg(ap, int *); char *label = NULL; OfxStatus status = propGetString((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, &label); if (status == kOfxStatOK && label) { int count = 0; propGetDimension((OfxPropertySetHandle) param_props, kOfxParamPropChoiceOption, &count); int found = 0; for (int i = 0; i < count; i++) { char *option = NULL; propGetString((OfxPropertySetHandle) param_props, kOfxParamPropChoiceOption, i, &option); if (option && strcmp(option, label) == 0) { *value = i; found = 1; break; } } if (!found) { status = propGetInt((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, value); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } else { status = propGetInt((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, value); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } else if (strcmp(param_type, kOfxParamTypeDouble) == 0) { double *value = va_arg(ap, double *); OfxStatus status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); if (status != kOfxStatOK) { status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, value); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } else if (strcmp(param_type, kOfxParamTypeString) == 0 || strcmp(param_type, kOfxParamTypeStrChoice) == 0) { char **value = va_arg(ap, char **); OfxStatus status = propGetString((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); if (status != kOfxStatOK) { status = propGetString((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, value); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } else if (strcmp(param_type, kOfxParamTypeRGBA) == 0) { double *red = va_arg(ap, double *); double *green = va_arg(ap, double *); double *blue = va_arg(ap, double *); double *alpha = va_arg(ap, double *); OfxStatus status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 3, alpha); if (status != kOfxStatOK) { status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, red); status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 1, green); status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 2, blue); status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 3, alpha); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } else if (strcmp(param_type, kOfxParamTypeRGB) == 0) { double *red = va_arg(ap, double *); double *green = va_arg(ap, double *); double *blue = va_arg(ap, double *); OfxStatus status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); if (status != kOfxStatOK) { status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, red); status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 1, green); status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 2, blue); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } else if (strcmp(param_type, kOfxParamTypeDouble2D) == 0) { double *Y = va_arg(ap, double *); double *X = va_arg(ap, double *); OfxStatus status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, X); status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, Y); if (status != kOfxStatOK) { status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, X); status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 1, Y); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } return kOfxStatOK; } static OfxStatus paramGetValue(OfxParamHandle paramHandle, ...) { if (!paramHandle) return kOfxStatErrBadHandle; va_list ap; va_start(ap, paramHandle); OfxStatus status = paramGetValueImpl(paramHandle, ap); va_end(ap); return status; } static OfxStatus paramGetValueAtTime(OfxParamHandle paramHandle, OfxTime time, ...) { if (!paramHandle) return kOfxStatErrBadHandle; va_list ap; va_start(ap, time); OfxStatus status = paramGetValueImpl(paramHandle, ap); va_end(ap); return status; } static OfxStatus paramGetDerivative(OfxParamHandle paramHandle, OfxTime time, ...) { return kOfxStatErrUnsupported; } static OfxStatus paramGetIntegral(OfxParamHandle paramHandle, OfxTime time1, OfxTime time2, ...) { return kOfxStatErrUnsupported; } static OfxStatus paramSetValueImpl(OfxParamHandle paramHandle, va_list ap) { mlt_properties param = (mlt_properties) paramHandle; char *param_type = mlt_properties_get(param, "t"); mlt_properties param_props = mlt_properties_get_properties(param, "p"); if (strcmp(param_type, kOfxParamTypeInteger) == 0 || strcmp(param_type, kOfxParamTypeBoolean) == 0) { int value = va_arg(ap, int); propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); } else if (strcmp(param_type, kOfxParamTypeChoice) == 0) { // OFX plugin passes an integer index; convert to string label for MltOfxParamValue. int index = va_arg(ap, int); char *label = NULL; OfxStatus status = propGetString((OfxPropertySetHandle) param_props, kOfxParamPropChoiceOption, index, &label); if (status != kOfxStatOK || !label) return status != kOfxStatOK ? status : kOfxStatErrBadIndex; propSetString((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, label); } else if (strcmp(param_type, kOfxParamTypeDouble) == 0) { double value = va_arg(ap, double); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); } else if (strcmp(param_type, kOfxParamTypeRGBA) == 0) { mlt_color value = va_arg(ap, mlt_color); double red = (double) value.r / 255.0; double green = (double) value.g / 255.0; double blue = (double) value.b / 255.0; double alpha = (double) value.a / 255.0; propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 3, alpha); } else if (strcmp(param_type, kOfxParamTypeRGB) == 0) { mlt_color value = va_arg(ap, mlt_color); double red = (double) value.r / 255.0; double green = (double) value.g / 255.0; double blue = (double) value.b / 255.0; propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); } else if (strcmp(param_type, kOfxParamTypeDouble2D) == 0) { mlt_rect value = va_arg(ap, mlt_rect); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value.x); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, value.y); } else if (strcmp(param_type, kOfxParamTypeString) == 0 || strcmp(param_type, kOfxParamTypeStrChoice) == 0) { char *value = va_arg(ap, char *); propSetString((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); } return kOfxStatOK; } static OfxStatus paramSetValue(OfxParamHandle paramHandle, ...) { if (!paramHandle) return kOfxStatErrBadHandle; va_list ap; va_start(ap, paramHandle); OfxStatus status = paramSetValueImpl(paramHandle, ap); va_end(ap); return status; } static OfxStatus paramSetValueAtTime(OfxParamHandle paramHandle, OfxTime time, ...) { mlt_log_debug(NULL, "[openfx] paramSetValueAtTime\n"); if (!paramHandle) return kOfxStatErrBadHandle; va_list ap; va_start(ap, time); OfxStatus status = paramSetValueImpl(paramHandle, ap); va_end(ap); return status; } static OfxStatus paramGetNumKeys(OfxParamHandle paramHandle, unsigned int *numberOfKeys) { if (numberOfKeys) *numberOfKeys = 0; return kOfxStatOK; } static OfxStatus paramGetKeyTime(OfxParamHandle paramHandle, unsigned int nthKey, OfxTime *time) { return kOfxStatErrUnsupported; } static OfxStatus paramGetKeyIndex(OfxParamHandle paramHandle, OfxTime time, int direction, int *index) { return kOfxStatErrUnsupported; } static OfxStatus paramDeleteKey(OfxParamHandle paramHandle, OfxTime time) { return kOfxStatOK; } static OfxStatus paramDeleteAllKeys(OfxParamHandle paramHandle) { return kOfxStatOK; } static OfxStatus paramCopy(OfxParamHandle paramTo, OfxParamHandle paramFrom, OfxTime dstOffset, const OfxRangeD *frameRange) { return kOfxStatOK; } static OfxStatus paramEditBegin(OfxParamSetHandle paramSet, const char *name) { return kOfxStatOK; } static OfxStatus paramEditEnd(OfxParamSetHandle paramSet) { return kOfxStatOK; } static OfxParameterSuiteV1 MltOfxParameterSuiteV1 = {paramDefine, paramGetHandle, paramSetGetPropertySet, paramGetPropertySet, paramGetValue, paramGetValueAtTime, paramGetDerivative, paramGetIntegral, paramSetValue, paramSetValueAtTime, paramGetNumKeys, paramGetKeyTime, paramGetKeyIndex, paramDeleteKey, paramDeleteAllKeys, paramCopy, paramEditBegin, paramEditEnd}; static OfxStatus memoryAlloc(void *handle, size_t nBytes, void **allocatedData) { void *temp = calloc(1, nBytes); if (!temp) return kOfxStatErrMemory; *allocatedData = temp; return kOfxStatOK; } static OfxStatus memoryFree(void *allocatedData) { free(allocatedData); return kOfxStatOK; } static OfxMemorySuiteV1 MltOfxMemorySuiteV1 = {memoryAlloc, memoryFree}; static OfxStatus multiThread(OfxThreadFunctionV1 func, unsigned int nThreads, void *customArg) { if (!func) return kOfxStatFailed; func(0, 1, customArg); return kOfxStatOK; } static OfxStatus multiThreadNumCPUs(unsigned int *nCPUs) { if (!nCPUs) return kOfxStatFailed; *nCPUs = 1; return kOfxStatOK; } static OfxStatus multiThreadIndex(unsigned int *threadIndex) { if (!threadIndex) return kOfxStatFailed; *threadIndex = 0; return kOfxStatOK; } int multiThreadIsSpawnedThread(void) { return false; } static OfxStatus mutexCreate(OfxMutexHandle *mutex, int lockCount) { if (!mutex) return kOfxStatFailed; // do nothing single threaded *mutex = 0; return kOfxStatOK; } static OfxStatus mutexDestroy(const OfxMutexHandle mutex) { if (!mutex) return kOfxStatErrBadHandle; // do nothing single threaded return kOfxStatOK; } static OfxStatus mutexLock(const OfxMutexHandle mutex) { if (!mutex) return kOfxStatErrBadHandle; // do nothing single threaded return kOfxStatOK; } static OfxStatus mutexUnLock(const OfxMutexHandle mutex) { if (!mutex) return kOfxStatErrBadHandle; // do nothing single threaded return kOfxStatOK; } static OfxStatus mutexTryLock(const OfxMutexHandle mutex) { if (!mutex) return kOfxStatErrBadHandle; // do nothing single threaded return kOfxStatOK; } static OfxMultiThreadSuiteV1 MltOfxMultiThreadSuiteV1 = {multiThread, multiThreadNumCPUs, multiThreadIndex, multiThreadIsSpawnedThread, mutexCreate, mutexDestroy, mutexLock, mutexUnLock, mutexTryLock}; static OfxStatus message( void *handle, const char *messageType, const char *messageId, const char *format, ...) { return kOfxStatOK; } static OfxMessageSuiteV1 MltOfxMessageSuiteV1 = {message}; static OfxStatus setPersistentMessage( void *handle, const char *messageType, const char *messageId, const char *format, ...) { return kOfxStatOK; } static OfxStatus clearPersistentMessage(void *handle) { return kOfxStatOK; } static OfxMessageSuiteV2 MltOfxMessageSuiteV2 = {message, // Same as the V1 message suite call. setPersistentMessage, clearPersistentMessage}; static OfxStatus interactSwapBuffers(OfxInteractHandle interactInstance) { return kOfxStatOK; } static OfxStatus interactRedraw(OfxInteractHandle interactInstance) { return kOfxStatOK; } static OfxStatus interactGetPropertySet(OfxInteractHandle interactInstance, OfxPropertySetHandle *property) { return kOfxStatErrUnsupported; } static OfxInteractSuiteV1 MltOfxInteractSuiteV1 = {interactSwapBuffers, interactRedraw, interactGetPropertySet}; /* static OfxStatus getColour(OfxDrawContextHandle context, OfxStandardColour std_colour, OfxRGBAColourF *colour) { mlt_log_debug(NULL, "[openfx] OfxDrawSuite `%s`\n", __FUNCTION__); return kOfxStatOK; } static OfxStatus setColour(OfxDrawContextHandle context, const OfxRGBAColourF *colour) { mlt_log_debug(NULL, "[openfx] OfxDrawSuite `%s`\n", __FUNCTION__); return kOfxStatOK; } static OfxStatus setLineWidth(OfxDrawContextHandle context, float width) { mlt_log_debug(NULL, "[openfx] OfxDrawSuite `%s`\n", __FUNCTION__); return kOfxStatOK; } static OfxStatus setLineStipple(OfxDrawContextHandle context, OfxDrawLineStipplePattern pattern) { mlt_log_debug(NULL, "[openfx] OfxDrawSuite `%s`\n", __FUNCTION__); return kOfxStatOK; } static OfxStatus draw(OfxDrawContextHandle context, OfxDrawPrimitive primitive, const OfxPointD *points, int point_count) { mlt_log_debug(NULL, "[openfx] OfxDrawSuite `%s`\n", __FUNCTION__); return kOfxStatOK; } static OfxStatus drawText(OfxDrawContextHandle context, const char *text, const OfxPointD *pos, int alignment) { mlt_log_debug(NULL, "[openfx] OfxDrawSuite `%s`\n", __FUNCTION__); return kOfxStatOK; } static OfxDrawSuiteV1 MltOfxDrawSuiteV1 = {getColour, setColour, setLineWidth, setLineStipple, draw, drawText}; */ static OfxStatus progressStartV1(void *effectInstance, const char *label) { return kOfxStatOK; } static OfxStatus progressUpdateV1(void *effectInstance, double progress) { return kOfxStatOK; } static OfxStatus progressEndV1(void *effectInstance) { return kOfxStatOK; } static OfxProgressSuiteV1 MltOfxProgressSuiteV1 = {progressStartV1, progressUpdateV1, progressEndV1}; static OfxStatus progressStartV2(void *effectInstance, const char *message, const char *messageid) { return kOfxStatOK; } static OfxStatus progressUpdateV2(void *effectInstance, double progress) { return kOfxStatOK; } static OfxStatus progressEndV2(void *effectInstance) { return kOfxStatOK; } static OfxProgressSuiteV2 MltOfxProgressSuiteV2 = {progressStartV2, progressUpdateV2, progressEndV2}; static OfxStatus getTime(void *instance, double *time) { return kOfxStatErrUnsupported; } static OfxStatus gotoTime(void *instance, double time) { return kOfxStatOK; } static OfxStatus getTimeBounds(void *instance, double *firstTime, double *lastTime) { return kOfxStatErrUnsupported; } static OfxTimeLineSuiteV1 MltOfxTimeLineSuiteV1 = {getTime, gotoTime, getTimeBounds}; static OfxStatus parametricParamGetValue(OfxParamHandle param, int curveIndex, OfxTime time, double parametricPosition, double *returnValue) { if (returnValue) *returnValue = 0.0; return kOfxStatOK; } static OfxStatus parametricParamGetNControlPoints(OfxParamHandle param, int curveIndex, double time, int *returnValue) { if (returnValue) *returnValue = 0; return kOfxStatOK; } static OfxStatus parametricParamGetNthControlPoint( OfxParamHandle param, int curveIndex, double time, int nthCtl, double *key, double *value) { return kOfxStatErrUnsupported; } static OfxStatus parametricParamSetNthControlPoint(OfxParamHandle param, int curveIndex, double time, int nthCtl, double key, double value, bool addAnimationKey) { return kOfxStatOK; } static OfxStatus parametricParamAddControlPoint(OfxParamHandle param, int curveIndex, double time, double key, double value, bool addAnimationKey) { return kOfxStatOK; } static OfxStatus parametricParamDeleteControlPoint(OfxParamHandle param, int curveIndex, int nthCtl) { return kOfxStatOK; } static OfxStatus parametricParamDeleteAllControlPoints(OfxParamHandle param, int curveIndex) { return kOfxStatOK; } static OfxParametricParameterSuiteV1 MltOfxParametricParameterSuiteV1 = {parametricParamGetValue, parametricParamGetNControlPoints, parametricParamGetNthControlPoint, parametricParamSetNthControlPoint, parametricParamAddControlPoint, parametricParamDeleteControlPoint, parametricParamDeleteAllControlPoints}; /* static OfxStatus clipLoadTexture(OfxImageClipHandle clip, OfxTime time, const char *format, const OfxRectD *region, OfxPropertySetHandle *textureHandle) { return kOfxStatOK; } static OfxStatus clipFreeTexture(OfxPropertySetHandle textureHandle) { return kOfxStatOK; } static OfxStatus flushResources() { return kOfxStatOK; } static OfxImageEffectOpenGLRenderSuiteV1 MltOfxOpenGLRenderSuiteV1 = {clipLoadTexture, clipFreeTexture, flushResources}; */ static void mltofx_log_status_code(OfxStatus code, char *msg) { mlt_log_debug(NULL, "[openfx] output of `%s` is ", msg); switch (code) { case kOfxStatOK: mlt_log_debug(NULL, "kOfxStatOK\n"); break; case kOfxStatFailed: mlt_log_debug(NULL, "kOfxStatFailed\n"); break; case kOfxStatErrFatal: mlt_log_debug(NULL, "kOfxStatErrFatal\n"); break; case kOfxStatErrUnknown: mlt_log_debug(NULL, "kOfxStatErrUnknown\n"); break; case kOfxStatErrMissingHostFeature: mlt_log_debug(NULL, "kOfxStatErrMissingHostFeature\n"); break; case kOfxStatErrUnsupported: mlt_log_debug(NULL, "kOfxStatErrUnsupported\n"); break; case kOfxStatErrExists: mlt_log_debug(NULL, "kOfxStatErrExists\n"); break; case kOfxStatErrFormat: mlt_log_debug(NULL, "kOfxStatErrFormat\n"); break; case kOfxStatErrMemory: mlt_log_debug(NULL, "kOfxStatErrMemory\n"); break; case kOfxStatErrBadHandle: mlt_log_debug(NULL, "kOfxStatErrBadHandle\n"); break; case kOfxStatErrBadIndex: mlt_log_debug(NULL, "kOfxStatErrBadIndex\n"); break; case kOfxStatErrValue: mlt_log_debug(NULL, "kOfxStatErrValue\n"); break; case kOfxStatReplyYes: mlt_log_debug(NULL, "kOfxStatReplyYes\n"); break; case kOfxStatReplyNo: mlt_log_debug(NULL, "kOfxStatReplyNo\n"); break; case kOfxStatReplyDefault: mlt_log_debug(NULL, "kOfxStatReplyDefault\n"); break; default: break; } mlt_log_debug(NULL, "\n"); } const void *MltOfxfetchSuite(OfxPropertySetHandle host, const char *suiteName, int suiteVersion) { mlt_log_debug(NULL, "[openfx] fetchSuite: `%s` v%d\n", suiteName, suiteVersion); if (strcmp(suiteName, kOfxImageEffectSuite) == 0 && suiteVersion == 1) { return &MltOfxImageEffectSuiteV1; } else if (strcmp(suiteName, kOfxParameterSuite) == 0 && suiteVersion == 1) { return &MltOfxParameterSuiteV1; } else if (strcmp(suiteName, kOfxPropertySuite) == 0 && suiteVersion == 1) { return &MltOfxPropertySuiteV1; } else if (strcmp(suiteName, kOfxMemorySuite) == 0 && suiteVersion == 1) { return &MltOfxMemorySuiteV1; } else if (strcmp(suiteName, kOfxMultiThreadSuite) == 0 && suiteVersion == 1) { return &MltOfxMultiThreadSuiteV1; } else if (strcmp(suiteName, kOfxMessageSuite) == 0 && suiteVersion == 1) { return &MltOfxMessageSuiteV1; } else if (strcmp(suiteName, kOfxMessageSuite) == 0 && suiteVersion == 2) { return &MltOfxMessageSuiteV2; } else if (strcmp(suiteName, kOfxInteractSuite) == 0 && suiteVersion == 1) { return &MltOfxInteractSuiteV1; } else if (strcmp(suiteName, kOfxDrawSuite) == 0 && suiteVersion == 1) { // return &MltOfxDrawSuiteV1 return NULL; } else if (strcmp(suiteName, kOfxProgressSuite) == 0 && suiteVersion == 1) { return &MltOfxProgressSuiteV1; } else if (strcmp(suiteName, kOfxProgressSuite) == 0 && suiteVersion == 2) { return &MltOfxProgressSuiteV2; } else if (strcmp(suiteName, kOfxTimeLineSuite) == 0 && suiteVersion == 1) { return &MltOfxTimeLineSuiteV1; } else if (strcmp(suiteName, kOfxParametricParameterSuite) == 0 && suiteVersion == 1) { return &MltOfxParametricParameterSuiteV1; } else if (strcmp(suiteName, kOfxOpenGLRenderSuite) == 0 && suiteVersion == 1) { // return &MltOfxOpenGLRenderSuiteV1 return NULL; } mlt_log_debug(NULL, "[openfx] %s v%d is not supported\n", suiteName, suiteVersion); return NULL; } OfxHost MltOfxHost = {NULL, MltOfxfetchSuite}; void mltofx_init_host_properties(OfxPropertySetHandle host_properties) { mlt_log_debug(NULL, "[openfx] Initializing OpenFX host properties\n"); // Core host identity and API version propSetString(host_properties, kOfxPropName, 0, "MLT"); propSetString(host_properties, kOfxPropLabel, 0, "MLT"); propSetString(host_properties, kOfxPropVersionLabel, 0, LIBMLT_VERSION); propSetInt(host_properties, kOfxPropVersion, 0, LIBMLT_VERSION_MAJOR); propSetInt(host_properties, kOfxPropVersion, 1, LIBMLT_VERSION_MINOR); propSetInt(host_properties, kOfxPropVersion, 2, LIBMLT_VERSION_REVISION); propSetInt(host_properties, kOfxPropAPIVersion, 0, 1); propSetInt(host_properties, kOfxPropAPIVersion, 1, 5); // Image effect context and supported pixel formats propSetString(host_properties, kOfxImageEffectPropContext, 0, kOfxImageEffectContextFilter); propSetString(host_properties, kOfxImageEffectPropSupportedPixelDepths, 0, kOfxBitDepthByte); propSetString(host_properties, kOfxImageEffectPropSupportedPixelDepths, 1, kOfxBitDepthShort); propSetString(host_properties, kOfxImageEffectPropSupportedPixelDepths, 2, kOfxBitDepthHalf); propSetString(host_properties, kOfxImageEffectPropSupportedPixelDepths, 3, kOfxBitDepthFloat); propSetString(host_properties, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA); propSetString(host_properties, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentRGB); // Image effect host capabilities propSetInt(host_properties, kOfxImageEffectHostPropIsBackground, 0, 1); propSetInt(host_properties, kOfxImageEffectPropSupportsMultipleClipDepths, 0, 0); propSetInt(host_properties, kOfxImageEffectPropSupportsMultipleClipPARs, 0, 0); propSetInt(host_properties, kOfxImageEffectPropSupportsMultiResolution, 0, 0); propSetInt(host_properties, kOfxImageEffectPropSupportsTiles, 0, 0); propSetInt(host_properties, kOfxImageEffectPropTemporalClipAccess, 0, 0); propSetInt(host_properties, kOfxImageEffectPropSetableFrameRate, 0, 0); propSetInt(host_properties, kOfxImageEffectPropSetableFielding, 0, 0); propSetInt(host_properties, kOfxImageEffectInstancePropSequentialRender, 0, 0); propSetInt(host_properties, kOfxImageEffectPropSupportsOverlays, 0, 0); propSetString(host_properties, kOfxImageEffectHostPropNativeOrigin, 0, kOfxHostNativeOriginBottomLeft); // GPU/accelerated rendering — not supported; CPU rendering is supported propSetString(host_properties, kOfxImageEffectPropCPURenderSupported, 0, "true"); propSetString(host_properties, kOfxImageEffectPropOpenGLRenderSupported, 0, "false"); propSetString(host_properties, kOfxImageEffectPropCudaRenderSupported, 0, "false"); propSetString(host_properties, kOfxImageEffectPropCudaStreamSupported, 0, "false"); propSetString(host_properties, kOfxImageEffectPropMetalRenderSupported, 0, "false"); propSetString(host_properties, kOfxImageEffectPropOpenCLRenderSupported, 0, "false"); propSetString(host_properties, kOfxImageEffectPropOpenCLSupported, 0, "false"); // Parameter host capabilities propSetInt(host_properties, kOfxParamHostPropSupportsCustomAnimation, 0, 0); propSetInt(host_properties, kOfxParamHostPropSupportsStringAnimation, 0, 0); propSetInt(host_properties, kOfxParamHostPropSupportsBooleanAnimation, 0, 0); propSetInt(host_properties, kOfxParamHostPropSupportsChoiceAnimation, 0, 0); propSetInt(host_properties, kOfxParamHostPropSupportsStrChoiceAnimation, 0, 0); propSetInt(host_properties, kOfxParamHostPropSupportsStrChoice, 0, 0); propSetInt(host_properties, kOfxParamHostPropSupportsCustomInteract, 0, 0); propSetInt(host_properties, kOfxParamHostPropMaxParameters, 0, -1); propSetInt(host_properties, kOfxParamHostPropMaxPages, 0, 0); propSetInt(host_properties, kOfxParamHostPropPageRowColumnCount, 0, 0); propSetInt(host_properties, kOfxParamHostPropPageRowColumnCount, 1, 0); propSetInt(host_properties, kOfxParamHostPropSupportsParametricAnimation, 0, 0); } void mltofx_create_instance(OfxPlugin *plugin, mlt_properties image_effect) { OfxStatus status_code = plugin->mainEntry(kOfxActionCreateInstance, (OfxImageEffectHandle) image_effect, NULL, NULL); mltofx_log_status_code(status_code, kOfxActionCreateInstance); } void mltofx_destroy_instance(OfxPlugin *plugin, mlt_properties image_effect) { OfxStatus status_code = plugin->mainEntry(kOfxActionDestroyInstance, (OfxImageEffectHandle) image_effect, NULL, NULL); mltofx_log_status_code(status_code, kOfxActionDestroyInstance); } void mltofx_set_source_clip_data(OfxPlugin *plugin, mlt_properties image_effect, uint8_t *image, int width, int height, mlt_image_format format, double pixel_aspect_ratio, const char *ofx_depth) { mlt_properties clip; mlt_properties clip_prop; clipGetHandle((OfxImageEffectHandle) image_effect, "Source", (OfxImageClipHandle *) &clip, (OfxPropertySetHandle *) &clip_prop); int depth_byte_size = 4; const char *depth_format = kOfxBitDepthByte; const char *prop_component = kOfxImageComponentRGBA; if (ofx_depth) { depth_format = ofx_depth; if (!strcmp(ofx_depth, kOfxBitDepthFloat)) depth_byte_size = 16; else if (!strcmp(ofx_depth, kOfxBitDepthShort) || !strcmp(ofx_depth, kOfxBitDepthHalf)) depth_byte_size = 8; else depth_byte_size = 4; } else if (format == mlt_image_rgba64) { depth_byte_size = 8; depth_format = kOfxBitDepthShort; } else if (format == mlt_image_rgb) { depth_byte_size = 3; prop_component = kOfxImageComponentRGB; } mlt_log_debug(NULL, "[openfx] %s: format=%s, depth=%s\n", __FUNCTION__, format == mlt_image_rgba64 ? "rgba64" : (format == mlt_image_rgba ? "rgba" : (format == mlt_image_rgb ? "rgb" : "Unknown")), depth_format); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRowBytes, 0, width * depth_byte_size); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropPixelDepth, 0, depth_format); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedPixelDepth, 0, depth_format); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropComponents, 0, prop_component); propSetPointer((OfxPropertySetHandle) clip_prop, kOfxImagePropData, 0, (void *) image); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropBounds, 0, 0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropBounds, 1, 0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropBounds, 2, width); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropBounds, 3, height); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 0, 0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 1, 0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 2, width); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 3, height); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentNone); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentRGBA); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropSupportedComponents, 2, kOfxImageComponentRGB); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropSupportedComponents, 3, kOfxImageComponentAlpha); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedComponents, 0, kOfxImageComponentNone); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedComponents, 1, kOfxImageComponentRGBA); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedComponents, 2, kOfxImageComponentRGB); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedComponents, 3, kOfxImageComponentAlpha); propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImagePropPixelAspectRatio, 0, pixel_aspect_ratio); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropPreMultiplication, 0, kOfxImageUnPreMultiplied); propSetString((OfxPropertySetHandle) clip_prop, kOfxImagePropField, 0, kOfxImageFieldBoth); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropFieldOrder, 0, kOfxImageFieldBoth); propSetString((OfxPropertySetHandle) clip_prop, "OfxImageEffectPropRenderQuality", 0, "OfxImageEffectPropRenderQualityBest"); char *tstr = calloc(1, strlen("Source") + 11); sprintf(tstr, "%s%04d%04d", "Source", rand() % 9999, rand() % 9999); propSetString((OfxPropertySetHandle) clip_prop, kOfxImagePropUniqueIdentifier, 0, tstr); free(tstr); propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropRenderScale, 0, 1.0); propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropRenderScale, 1, 1.0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImageClipPropConnected, 0, 1); } void mltofx_set_output_clip_data(OfxPlugin *plugin, mlt_properties image_effect, uint8_t *image, int width, int height, mlt_image_format format, double pixel_aspect_ratio, const char *ofx_depth) { mlt_properties clip; mlt_properties clip_prop; clipGetHandle((OfxImageEffectHandle) image_effect, "Output", (OfxImageClipHandle *) &clip, (OfxPropertySetHandle *) &clip_prop); int depth_byte_size = 4; const char *depth_format = kOfxBitDepthByte; const char *prop_component = kOfxImageComponentRGBA; if (ofx_depth) { depth_format = ofx_depth; if (!strcmp(ofx_depth, kOfxBitDepthFloat)) depth_byte_size = 16; else if (!strcmp(ofx_depth, kOfxBitDepthShort) || !strcmp(ofx_depth, kOfxBitDepthHalf)) depth_byte_size = 8; else depth_byte_size = 4; } else if (format == mlt_image_rgba64) { depth_byte_size = 8; depth_format = kOfxBitDepthShort; } else if (format == mlt_image_rgb) { depth_byte_size = 3; prop_component = kOfxImageComponentRGB; } propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRowBytes, 0, width * depth_byte_size); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropPixelDepth, 0, depth_format); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedPixelDepth, 0, depth_format); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropComponents, 0, prop_component); propSetPointer((OfxPropertySetHandle) clip_prop, kOfxImagePropData, 0, (void *) image); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropBounds, 0, 0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropBounds, 1, 0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropBounds, 2, width); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropBounds, 3, height); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 0, 0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 1, 0); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 2, width); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImagePropRegionOfDefinition, 3, height); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentNone); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentRGBA); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropSupportedComponents, 2, kOfxImageComponentRGB); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropSupportedComponents, 3, kOfxImageComponentAlpha); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedComponents, 0, kOfxImageComponentNone); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedComponents, 1, kOfxImageComponentRGBA); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedComponents, 2, kOfxImageComponentRGB); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropUnmappedComponents, 3, kOfxImageComponentAlpha); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropPreMultiplication, 0, kOfxImageUnPreMultiplied); propSetString((OfxPropertySetHandle) clip_prop, kOfxImagePropField, 0, kOfxImageFieldBoth); propSetString((OfxPropertySetHandle) clip_prop, kOfxImageClipPropFieldOrder, 0, kOfxImageFieldBoth); propSetString((OfxPropertySetHandle) clip_prop, "OfxImageEffectPropRenderQuality", 0, "OfxImageEffectPropRenderQualityBest"); char *tstr = calloc(1, strlen("Output") + 11); sprintf(tstr, "%s%04d%04d", "Output", rand() % 9999, rand() % 9999); propSetString((OfxPropertySetHandle) clip_prop, kOfxImagePropUniqueIdentifier, 0, tstr); free(tstr); propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropRenderScale, 0, 1.0); propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropRenderScale, 1, 1.0); propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImagePropPixelAspectRatio, 0, pixel_aspect_ratio); } int mltofx_detect_plugin(OfxPlugin *plugin) { mlt_properties image_effect = mlt_properties_new(); mlt_properties clips = mlt_properties_new(); mlt_properties props = mlt_properties_new(); mlt_properties iparams = mlt_properties_new(); mlt_properties params = mlt_properties_new(); mlt_properties_set_properties(image_effect, "clips", clips); mlt_properties_set_properties(image_effect, "props", props); mlt_properties_set_properties(image_effect, "params", iparams); mlt_properties_set_properties(iparams, "plugin_props", props); mlt_properties_set_properties(image_effect, "mltofx_params", params); propSetString((OfxPropertySetHandle) props, kOfxImageEffectPropContext, 0, kOfxImageEffectContextFilter); plugin->setHost(&MltOfxHost); OfxStatus status_code = kOfxStatErrUnknown; status_code = plugin->mainEntry(kOfxActionLoad, NULL, NULL, NULL); mltofx_log_status_code(status_code, "kOfxActionLoad"); if (status_code != kOfxStatOK) { return 0; } status_code = plugin->mainEntry(kOfxActionDescribe, (OfxImageEffectHandle) image_effect, NULL, NULL); mltofx_log_status_code(status_code, "kOfxActionDescribe"); if (status_code != kOfxStatOK) { plugin->mainEntry(kOfxActionUnload, NULL, NULL, NULL); return 0; } status_code = plugin->mainEntry(kOfxImageEffectActionDescribeInContext, (OfxImageEffectHandle) image_effect, (OfxPropertySetHandle) MltOfxHost.host, NULL); int describe_in_context_valid = 0; mltofx_log_status_code(status_code, "kOfxImageEffectActionDescribeInContext"); if (status_code != kOfxStatErrMissingHostFeature) { plugin->mainEntry(kOfxActionUnload, NULL, NULL, NULL); describe_in_context_valid = 1; } int i = 0; int count = 0; propGetDimension((OfxPropertySetHandle) props, kOfxImageEffectPropSupportedContexts, &count); for (i = 0; i < count; i++) { char *context; propGetString((OfxPropertySetHandle) props, kOfxImageEffectPropSupportedContexts, i, &context); if (!strcmp(context, kOfxImageEffectContextFilter)) { break; } } if (i == count) { mlt_log_debug(NULL, "[openfx] Plugin not a filter: %s\n", plugin->pluginIdentifier); // since plugin is not filter then load fail so we must not unload it return 0; } count = 0; propGetDimension((OfxPropertySetHandle) props, kOfxImageEffectPropSupportedPixelDepths, &count); for (i = 0; i < count; i++) { char *depth; propGetString((OfxPropertySetHandle) props, kOfxImageEffectPropSupportedPixelDepths, i, &depth); if (!strcmp(depth, kOfxBitDepthByte) || !strcmp(depth, kOfxBitDepthShort) || !strcmp(depth, kOfxBitDepthHalf) || !strcmp(depth, kOfxBitDepthFloat)) { break; } } if (i == count) { mlt_log_verbose(NULL, "[openfx] Plugin does not support byte, short, half, or float pixels: %s\n", plugin->pluginIdentifier); // since no pixel depth is supported by us then plugin is load fail so we must not unload it return 0; } if (describe_in_context_valid) return 1; plugin->mainEntry(kOfxActionUnload, NULL, NULL, NULL); mlt_properties_close(image_effect); mlt_properties_close(clips); mlt_properties_close(iparams); mlt_properties_close(props); mlt_properties_close(params); return 0; } static int param_type_is_supported(const char *type) { return strcmp(type, kOfxParamTypeInteger2D) && strcmp(type, kOfxParamTypeDouble3D) && strcmp(type, kOfxParamTypeInteger3D) && strcmp(type, kOfxParamTypeCustom) && strcmp(type, kOfxParamTypeBytes) && strcmp(type, kOfxParamTypePage) && strcmp(type, kOfxParamTypePushButton); } void *mltofx_fetch_params(OfxPlugin *plugin, mlt_properties params, mlt_properties mlt_metadata) { mlt_properties image_effect = mlt_properties_new(); mlt_properties clips = mlt_properties_new(); mlt_properties props = mlt_properties_new(); mlt_properties iparams = mlt_properties_new(); mlt_properties_set_properties(image_effect, "clips", clips); mlt_properties_set_properties(image_effect, "props", props); mlt_properties_set_properties(image_effect, "params", iparams); mlt_properties_set_properties(iparams, "plugin_props", props); mlt_properties_set_properties(image_effect, "mltofx_params", params); propSetString((OfxPropertySetHandle) props, kOfxImageEffectPropContext, 0, kOfxImageEffectContextFilter); plugin->setHost(&MltOfxHost); OfxStatus status_code = kOfxStatErrUnknown; status_code = plugin->mainEntry(kOfxActionLoad, NULL, NULL, NULL); mltofx_log_status_code(status_code, "kOfxActionLoad"); status_code = plugin->mainEntry(kOfxActionDescribe, (OfxImageEffectHandle) image_effect, NULL, NULL); mltofx_log_status_code(status_code, "kOfxActionDescribe"); status_code = plugin->mainEntry(kOfxImageEffectActionDescribeInContext, (OfxImageEffectHandle) image_effect, (OfxPropertySetHandle) MltOfxHost.host, NULL); // some plugins need to set some attributes at Source and Output before any other operation happen mlt_properties clip = NULL, clip_props = NULL; clipGetHandle((OfxImageEffectHandle) image_effect, "Source", (OfxImageClipHandle *) &clip, (OfxPropertySetHandle *) &clip_props); if (clip_props != NULL) { propSetString((OfxPropertySetHandle) clip_props, kOfxImageClipPropFieldOrder, 0, kOfxImageFieldNone); propSetString((OfxPropertySetHandle) clip_props, "OfxImageEffectPropRenderQuality", 0, "OfxImageEffectPropRenderQualityBest"); propSetString((OfxPropertySetHandle) clip_props, kOfxImagePropField, 0, kOfxImageFieldNone); } clip = NULL, clip_props = NULL; clipGetHandle((OfxImageEffectHandle) image_effect, "Output", (OfxImageClipHandle *) &clip, (OfxPropertySetHandle *) &clip_props); if (clip_props != NULL) { propSetString((OfxPropertySetHandle) clip_props, kOfxImageClipPropFieldOrder, 0, kOfxImageFieldNone); propSetString((OfxPropertySetHandle) clip_props, "OfxImageEffectPropRenderQuality", 0, "OfxImageEffectPropRenderQualityBest"); propSetString((OfxPropertySetHandle) clip_props, kOfxImagePropField, 0, kOfxImageFieldNone); } mltofx_log_status_code(status_code, "kOfxImageEffectActionDescribeInContext"); if (mlt_metadata != NULL) { char *str_value = ""; propGetString((OfxPropertySetHandle) props, kOfxPropPluginDescription, 0, &str_value); if (str_value[0] != '\0') { mlt_properties_set(mlt_metadata, "description", str_value); } str_value = ""; propGetString((OfxPropertySetHandle) props, kOfxPropLongLabel, 0, &str_value); if (str_value[0] == '\0') propGetString((OfxPropertySetHandle) props, kOfxPropLabel, 0, &str_value); if (str_value[0] != '\0') { mlt_properties_set(mlt_metadata, "title", str_value); } str_value = ""; propGetString((OfxPropertySetHandle) props, kOfxPropVersionLabel, 0, &str_value); if (str_value[0] != '\0') { mlt_properties_set(mlt_metadata, "version", str_value); } else { // Build version string from the integer array (e.g. "1.2.3") char version_str[64] = ""; int dim = 0; propGetDimension((OfxPropertySetHandle) props, kOfxPropVersion, &dim); for (int vi = 0; vi < dim; ++vi) { int v = 0; propGetInt((OfxPropertySetHandle) props, kOfxPropVersion, vi, &v); char part[16]; snprintf(part, sizeof(part), vi == 0 ? "%d" : ".%d", v); strncat(version_str, part, sizeof(version_str) - strlen(version_str) - 1); } if (version_str[0] != '\0') mlt_properties_set(mlt_metadata, "version", version_str); } } int iparams_length = mlt_properties_count(iparams); int mlt_param_count = 0; // starting with 1 to skip the plugin_props which is not a param but just a reference to the plugin properties for (int ipiter = 1; ipiter < iparams_length; ++ipiter) { char *name = mlt_properties_get_name(iparams, ipiter); mlt_properties pp = mlt_properties_get_properties(iparams, name); mlt_properties ppp = mlt_properties_get_properties(pp, "p"); // Skip parameters marked secret. int is_secret = -1; propGetInt((OfxPropertySetHandle) ppp, kOfxParamPropSecret, 0, &is_secret); if (is_secret == 1) continue; // Skip unsupported parameter types. char *param_type = mlt_properties_get(pp, "t"); if (!param_type_is_supported(param_type)) continue; // Create a new MLT property set for this parameter and add it to the params list // in accordance with the metadata schema. mlt_properties p = mlt_properties_new(); char key[20]; snprintf(key, 20, "%d", mlt_param_count++); mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(p, "identifier", name); char *label = NULL; propGetString((OfxPropertySetHandle) ppp, kOfxPropLabel, 0, &label); if (label) mlt_properties_set(p, "title", label); char *hint = NULL; propGetString((OfxPropertySetHandle) ppp, kOfxParamPropHint, 0, &hint); if (hint) mlt_properties_set(p, "description", hint); if (strcmp(param_type, kOfxParamTypeInteger) == 0) { mlt_properties_set(p, "type", "integer"); } else if (strcmp(param_type, kOfxParamTypeDouble) == 0) { mlt_properties_set(p, "type", "float"); } else if (strcmp(param_type, kOfxParamTypeChoice) == 0) { mlt_properties_set( p, "type", "string"); // setting this to string let kdenlive and other MLT users render this as combobox dropdown list } else if (strcmp(param_type, kOfxParamTypeString) == 0) { mlt_properties_set(p, "type", "string"); } else if (strcmp(param_type, kOfxParamTypeBoolean) == 0) { mlt_properties_set(p, "type", "boolean"); } else if (strcmp(param_type, kOfxParamTypeGroup) == 0) { mlt_properties_set(p, "type", "group"); } else if (strcmp(param_type, kOfxParamTypeRGBA) == 0 || strcmp(param_type, kOfxParamTypeRGB) == 0) { mlt_properties_set(p, "type", "color"); mlt_properties_set(p, "widget", "color"); } else if (strcmp(param_type, kOfxParamTypeDouble2D) == 0) { // can be rendered as 2 double number input fields mlt_properties_set(p, "type", "float"); char *type = kOfxParamDoubleTypeXY; char *coordinate_system = kOfxParamCoordinatesCanonical; propGetString((OfxPropertySetHandle) ppp, kOfxParamPropDoubleType, 0, &type); propGetString((OfxPropertySetHandle) ppp, kOfxParamPropDefaultCoordinateSystem, 0, &coordinate_system); if (strcmp(type, kOfxParamDoubleTypeXYAbsolute) == 0) { mlt_properties_set(p, "widget", "point"); } else if (strcmp(type, kOfxParamDoubleTypeXY) == 0) { mlt_properties_set(p, "widget", "size"); } if (strcmp(coordinate_system, kOfxParamCoordinatesCanonical) == 0) { mlt_properties_set(p, "normalized_coordinates", "no"); } else if (strcmp(coordinate_system, kOfxParamCoordinatesNormalised) == 0) { mlt_properties_set(p, "normalized_coordinates", "yes"); } } if (strcmp(param_type, kOfxParamTypeGroup) != 0) { int animation = 1; propGetInt((OfxPropertySetHandle) ppp, kOfxParamPropAnimates, 0, &animation); mlt_properties_set(p, "animation", animation ? "yes" : "no"); } mlt_properties_set(p, "mutable", "yes"); // Iterate through the properties of the first dimension to find all the params. mlt_properties dim1 = mlt_properties_get_properties(ppp, "0"); int min_set = 0; int max_set = 0; int p_length = mlt_properties_count(dim1); for (int jt = 0; jt < p_length; ++jt) { char *p_name = mlt_properties_get_name(dim1, jt); if (strcmp(p_name, kOfxParamPropDefault) == 0) { if (strcmp(param_type, kOfxParamTypeInteger) == 0 || strcmp(param_type, kOfxParamTypeChoice) == 0 || strcmp(param_type, kOfxParamTypeBoolean) == 0) { int default_value = 0; propGetInt((OfxPropertySetHandle) ppp, p_name, 0, &default_value); mlt_properties_set_int(p, "default", default_value); } else if (strcmp(param_type, kOfxParamTypeDouble) == 0) { double default_value = 0.0; propGetDouble((OfxPropertySetHandle) ppp, p_name, 0, &default_value); mlt_properties_set_double(p, "default", default_value); } else if (strcmp(param_type, kOfxParamTypeDouble2D) == 0) { double default_value1 = 0.0, default_value2 = 0.0; propGetDouble((OfxPropertySetHandle) ppp, p_name, 0, &default_value1); propGetDouble((OfxPropertySetHandle) ppp, p_name, 1, &default_value2); // for some reason with DBL_MIN DBL_MAX it segfault if (!isnormal(default_value1) || default_value1 <= FLT_MIN || default_value1 >= FLT_MAX) default_value1 = 0.0; if (!isnormal(default_value2) || default_value2 <= FLT_MIN || default_value2 >= FLT_MAX) default_value2 = 0.0; char default_value[20] = ""; sprintf(default_value, "%.4f %.4f", default_value1, default_value2); mlt_properties_set(p, "default", default_value); } else if (strcmp(param_type, kOfxParamTypeString) == 0) { char *default_value = ""; propGetString((OfxPropertySetHandle) ppp, p_name, 0, &default_value); mlt_properties_set(p, "default", default_value); } else if (strcmp(param_type, kOfxParamTypeRGBA) == 0) { double r, g, b, a; propGetDouble((OfxPropertySetHandle) ppp, p_name, 0, &r); propGetDouble((OfxPropertySetHandle) ppp, p_name, 1, &g); propGetDouble((OfxPropertySetHandle) ppp, p_name, 2, &b); propGetDouble((OfxPropertySetHandle) ppp, p_name, 3, &a); char default_value[10] = "#000000FF"; sprintf(default_value, "#%02X%02X%02X%02X", (unsigned char) (a * 255.0), (unsigned char) (r * 255.0), (unsigned char) (g * 255.0), (unsigned char) (b * 255.0)); mlt_properties_set(p, "default", default_value); } else if (strcmp(param_type, kOfxParamTypeRGB) == 0) { double r, g, b; propGetDouble((OfxPropertySetHandle) ppp, p_name, 0, &r); propGetDouble((OfxPropertySetHandle) ppp, p_name, 1, &g); propGetDouble((OfxPropertySetHandle) ppp, p_name, 2, &b); char default_value[10] = "#000000FF"; sprintf(default_value, "#%02X%02X%02XFF", (unsigned char) (r * 255.0), (unsigned char) (g * 255.0), (unsigned char) (b * 255.0)); mlt_properties_set(p, "default", default_value); } if (strcmp(param_type, kOfxParamTypeChoice) == 0) { char key[20]; int count = 0; mlt_properties choices = mlt_properties_new(); mlt_properties_set_data(p, "values", choices, 0, (mlt_destructor) mlt_properties_close, NULL); propGetDimension((OfxPropertySetHandle) ppp, kOfxParamPropChoiceOption, &count); for (int jtr = 0; jtr < count; ++jtr) { key[0] = '\0'; sprintf(key, "%d", jtr); char *choice_str = NULL; propGetString((OfxPropertySetHandle) ppp, kOfxParamPropChoiceOption, jtr, &choice_str); mlt_properties_set(choices, key, choice_str); } } } else if ((strcmp(p_name, kOfxParamPropMin) == 0 && min_set == 0) || strcmp(p_name, kOfxParamPropDisplayMin) == 0) { if (strcmp(param_type, kOfxParamTypeInteger) == 0 || strcmp(param_type, kOfxParamTypeChoice) == 0 || strcmp(param_type, kOfxParamTypeBoolean) == 0) { min_set = 1; int minimum_value = 0; propGetInt((OfxPropertySetHandle) ppp, p_name, 0, &minimum_value); mlt_properties_set_int(p, "minimum", minimum_value); } else if (strcmp(param_type, kOfxParamTypeDouble) == 0) { double minimum_value = 0.0; propGetDouble((OfxPropertySetHandle) ppp, p_name, 0, &minimum_value); mlt_properties_set_double(p, "minimum", minimum_value); } else if (strcmp(param_type, kOfxParamTypeString) == 0) { char *minimum_value = ""; propGetString((OfxPropertySetHandle) ppp, p_name, 0, &minimum_value); mlt_properties_set(p, "minimum", minimum_value); } } else if ((strcmp(p_name, kOfxParamPropMax) == 0 && max_set == 0) || strcmp(p_name, kOfxParamPropDisplayMax) == 0) { if (strcmp(param_type, kOfxParamTypeInteger) == 0 || strcmp(param_type, kOfxParamTypeChoice) == 0 || strcmp(param_type, kOfxParamTypeBoolean) == 0) { max_set = 1; int maximum_value = 0; propGetInt((OfxPropertySetHandle) ppp, p_name, 0, &maximum_value); mlt_properties_set_int(p, "maximum", maximum_value); } else if (strcmp(param_type, kOfxParamTypeDouble) == 0) { double maximum_value = 0.0; propGetDouble((OfxPropertySetHandle) ppp, p_name, 0, &maximum_value); mlt_properties_set_double(p, "maximum", maximum_value); } else if (strcmp(param_type, kOfxParamTypeString) == 0) { char *maximum_value = ""; propGetString((OfxPropertySetHandle) ppp, p_name, 0, &maximum_value); mlt_properties_set(p, "maximum", maximum_value); } } else if (strcmp(p_name, kOfxParamPropStringMode) == 0) { char *str_value = ""; propGetString((OfxPropertySetHandle) ppp, p_name, 0, &str_value); if (strcmp(str_value, kOfxParamStringIsLabel) == 0) { mlt_properties_set(p, "readonly", "yes"); } } else if (strcmp(p_name, kOfxParamPropParent) == 0) { char *str_value = ""; propGetString((OfxPropertySetHandle) ppp, p_name, 0, &str_value); mlt_properties_set(p, "group", str_value); } } } mlt_properties_close(clips); mlt_properties_close(iparams); mlt_properties_close(props); return image_effect; } void mltofx_param_set_value(mlt_properties params, char *key, mltofx_property_type type, ...) { mlt_properties param = NULL; paramGetHandle((OfxParamSetHandle) params, key, (OfxParamHandle *) ¶m, NULL); if (!param) return; mlt_properties param_props = mlt_properties_get_properties(param, "p"); if (!param_props) return; va_list ap; va_start(ap, type); switch (type) { case mltofx_prop_int: { int value = va_arg(ap, int); propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); } break; case mltofx_prop_double: { double value = va_arg(ap, double); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); } break; case mltofx_prop_string: { char *value = va_arg(ap, char *); propSetString((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); } break; case mltofx_prop_color: { mlt_color value = va_arg(ap, mlt_color); double red = (double) value.r / 255.0; double green = (double) value.g / 255.0; double blue = (double) value.b / 255.0; double alpha = (double) value.a / 255.0; propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 3, alpha); } break; case mltofx_prop_double2d: { mlt_rect value = va_arg(ap, mlt_rect); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value.x); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, value.y); } break; default: break; } va_end(ap); } void mltofx_get_clip_preferences(OfxPlugin *plugin, mlt_properties image_effect) { mlt_properties get_clippref_args = mlt_properties_get_properties(image_effect, "get_clippref_args"); OfxStatus status_code = plugin->mainEntry(kOfxImageEffectActionGetClipPreferences, (OfxImageEffectHandle) image_effect, NULL, (OfxPropertySetHandle) get_clippref_args); mltofx_log_status_code(status_code, kOfxImageEffectActionGetClipPreferences); } void mltofx_get_region_of_definition(OfxPlugin *plugin, mlt_properties image_effect, double ofx_time) { mlt_properties get_rod_in_args = mlt_properties_get_properties(image_effect, "get_rod_in_args"); propSetDouble((OfxPropertySetHandle) get_rod_in_args, kOfxPropTime, 0, ofx_time); propSetDouble((OfxPropertySetHandle) get_rod_in_args, kOfxImageEffectPropRenderScale, 0, 1.0); propSetDouble((OfxPropertySetHandle) get_rod_in_args, kOfxImageEffectPropRenderScale, 1, 1.0); mlt_properties get_rod_out_args = mlt_properties_get_properties(image_effect, "get_rod_out_args"); OfxStatus status_code = plugin->mainEntry(kOfxImageEffectActionGetRegionOfDefinition, (OfxImageEffectHandle) image_effect, (OfxPropertySetHandle) get_rod_in_args, (OfxPropertySetHandle) get_rod_out_args); mltofx_log_status_code(status_code, kOfxImageEffectActionGetRegionOfDefinition); } void mltofx_get_regions_of_interest( OfxPlugin *plugin, mlt_properties image_effect, double ofx_time, double width, double height) { mlt_properties get_roi_in_args = mlt_properties_get_properties(image_effect, "get_roi_in_args"); mlt_properties get_roi_out_args = mlt_properties_get_properties(image_effect, "get_roi_out_args"); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxPropTime, 0, ofx_time); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxPropTime, 1, ofx_time); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRenderScale, 0, 1.0); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRenderScale, 1, 1.0); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRegionOfInterest, 0, 0.0); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRegionOfInterest, 1, 0.0); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRegionOfInterest, 2, width); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRegionOfInterest, 3, height); OfxStatus status_code = plugin->mainEntry(kOfxImageEffectActionGetRegionsOfInterest, (OfxImageEffectHandle) image_effect, (OfxPropertySetHandle) get_roi_in_args, (OfxPropertySetHandle) get_roi_out_args); mltofx_log_status_code(status_code, kOfxImageEffectActionGetRegionsOfInterest); } void mltofx_begin_sequence_render(OfxPlugin *plugin, mlt_properties image_effect, double ofx_time) { mlt_properties begin_sequence_props = mlt_properties_get_properties(image_effect, "begin_sequence_props"); propSetDouble((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropFrameRange, 0, ofx_time); propSetDouble((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropFrameRange, 1, ofx_time); propSetDouble((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropFrameStep, 0, 1.0); propSetInt((OfxPropertySetHandle) begin_sequence_props, kOfxPropIsInteractive, 0, 0); propSetDouble((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropRenderScale, 0, 1.0); propSetDouble((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropRenderScale, 1, 1.0); propSetInt((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropSequentialRenderStatus, 0, 1); propSetInt((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropInteractiveRenderStatus, 0, 0); propSetInt((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropOpenGLEnabled, 0, 0); OfxStatus status_code = plugin->mainEntry(kOfxImageEffectActionBeginSequenceRender, (OfxImageEffectHandle) image_effect, (OfxPropertySetHandle) begin_sequence_props, NULL); mltofx_log_status_code(status_code, kOfxImageEffectActionBeginSequenceRender); } void mltofx_end_sequence_render(OfxPlugin *plugin, mlt_properties image_effect, double ofx_time) { mlt_properties end_sequence_props = mlt_properties_get_properties(image_effect, "end_sequence_props"); propSetDouble((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropFrameRange, 0, ofx_time); propSetDouble((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropFrameRange, 1, ofx_time); propSetDouble((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropFrameStep, 0, 1.0); propSetInt((OfxPropertySetHandle) end_sequence_props, kOfxPropIsInteractive, 0, 0); propSetDouble((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropRenderScale, 0, 1.0); propSetDouble((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropRenderScale, 1, 1.0); propSetInt((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropSequentialRenderStatus, 0, 1); propSetInt((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropInteractiveRenderStatus, 0, 0); propSetInt((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropOpenGLEnabled, 0, 0); OfxStatus status_code = plugin->mainEntry(kOfxImageEffectActionEndSequenceRender, (OfxImageEffectHandle) image_effect, (OfxPropertySetHandle) end_sequence_props, NULL); mltofx_log_status_code(status_code, kOfxImageEffectActionEndSequenceRender); } void mltofx_action_render( OfxPlugin *plugin, mlt_properties image_effect, double ofx_time, int width, int height) { mlt_properties render_in_args = mlt_properties_get_properties(image_effect, "render_in_args"); propSetDouble((OfxPropertySetHandle) render_in_args, kOfxPropTime, 0, ofx_time); propSetString((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropFieldToRender, 0, kOfxImageFieldBoth); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderWindow, 0, 0); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderWindow, 1, 0); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderWindow, 2, width); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderWindow, 3, height); propSetDouble((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderScale, 0, 1.0); propSetDouble((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderScale, 1, 1.0); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropSequentialRenderStatus, 0, 1); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropInteractiveRenderStatus, 0, 0); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderQualityDraft, 0, 0); propSetString((OfxPropertySetHandle) render_in_args, "OfxImageEffectPropRenderQuality", 0, "OfxImageEffectPropRenderQualityBest"); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropOpenGLEnabled, 0, 0); OfxStatus status_code = plugin->mainEntry(kOfxImageEffectActionRender, (OfxImageEffectHandle) image_effect, (OfxPropertySetHandle) render_in_args, NULL); mltofx_log_status_code(status_code, kOfxImageEffectActionRender); } mltofx_depths_mask mltofx_plugin_supported_depths(mlt_properties image_effect) { mltofx_depths_mask mask = mltofx_depth_none; int count = 0; OfxPropertySetHandle effectProps; getPropertySet((OfxImageEffectHandle) image_effect, &effectProps); propGetDimension((OfxPropertySetHandle) effectProps, kOfxImageEffectPropSupportedPixelDepths, &count); for (int i = 0; i < count; ++i) { char *str = NULL; propGetString((OfxPropertySetHandle) effectProps, kOfxImageEffectPropSupportedPixelDepths, i, &str); if (strcmp(str, kOfxBitDepthByte) == 0) { mask |= mltofx_depth_byte; } else if (strcmp(str, kOfxBitDepthShort) == 0) { mask |= mltofx_depth_short; } else if (strcmp(str, kOfxBitDepthHalf) == 0) { mask |= mltofx_depth_half; } else if (strcmp(str, kOfxBitDepthFloat) == 0) { mask |= mltofx_depth_float; } } return mask; } mltofx_components_mask mltofx_plugin_supported_components(mlt_properties image_effect) { mltofx_components_mask mask = mltofx_components_none; mlt_properties set = mlt_properties_get_properties(image_effect, "clips"); int clips_count = mlt_properties_count(set); for (int i = 0; i < clips_count; ++i) { char *clip_name = mlt_properties_get_name(set, i); mlt_properties clip, clip_props; clipGetHandle((OfxImageEffectHandle) image_effect, clip_name, (OfxImageClipHandle *) &clip, (OfxPropertySetHandle *) &clip_props); int count = 0; propGetDimension((OfxPropertySetHandle) clip_props, kOfxImageEffectPropSupportedComponents, &count); for (int i = 0; i < count; ++i) { char *str = NULL; propGetString((OfxPropertySetHandle) clip_props, kOfxImageEffectPropSupportedComponents, i, &str); if (strcmp(str, kOfxImageComponentRGB) == 0) { mask |= mltofx_components_rgb; } else if (strcmp(str, kOfxImageComponentRGBA) == 0) { mask |= mltofx_components_rgba; } } } return mask; } mlt-7.38.0/src/modules/openfx/mlt_openfx.h000664 000000 000000 00000010167 15172202314 020451 0ustar00rootroot000000 000000 /* * mlt_openfx.h -- common mlt functions for OpenFX plugins * Copyright (C) 2025-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef MLT_OPENFX_H #define MLT_OPENFX_H #include #include #include #include #include #include #include typedef OfxStatus (*OfxSetHostFn)(const OfxHost *host); typedef OfxPlugin *(*OfxGetPluginFn)(int nth); typedef int (*OfxGetNumberOfPluginsFn)(void); typedef enum { mltofx_prop_none = 0, mltofx_prop_int = 1, mltofx_prop_string = 2, mltofx_prop_double = 8, mltofx_prop_pointer = 16, mltofx_prop_color = 32, mltofx_prop_double2d = 64, } mltofx_property_type; typedef enum { mltofx_depth_none = 0, mltofx_depth_byte = 1, mltofx_depth_short = 2, mltofx_depth_half = 4, mltofx_depth_float = 8, } mltofx_depths_mask; typedef enum { mltofx_components_none = 0, mltofx_components_rgb = 1, mltofx_components_rgba = 2, } mltofx_components_mask; void mltofx_init_host_properties(OfxPropertySetHandle host_properties); void mltofx_create_instance(OfxPlugin *plugin, mlt_properties image_effect); void mltofx_destroy_instance(OfxPlugin *plugin, mlt_properties image_effect); void mltofx_set_source_clip_data(OfxPlugin *plugin, mlt_properties image_effect, uint8_t *image, int width, int height, mlt_image_format format, double pixel_aspect_ratio, const char *ofx_depth); void mltofx_set_output_clip_data(OfxPlugin *plugin, mlt_properties image_effect, uint8_t *image, int width, int height, mlt_image_format format, double pixel_aspect_ratio, const char *ofx_depth); int mltofx_detect_plugin(OfxPlugin *plugin); void *mltofx_fetch_params(OfxPlugin *plugin, mlt_properties params, mlt_properties mlt_metadata); void mltofx_param_set_value(mlt_properties params, char *key, mltofx_property_type type, ...); void mltofx_get_clip_preferences(OfxPlugin *plugin, mlt_properties image_effect); void mltofx_get_region_of_definition(OfxPlugin *plugin, mlt_properties image_effect, double ofx_time); void mltofx_get_regions_of_interest( OfxPlugin *plugin, mlt_properties image_effect, double ofx_time, double width, double height); void mltofx_begin_sequence_render(OfxPlugin *plugin, mlt_properties image_effect, double ofx_time); void mltofx_end_sequence_render(OfxPlugin *plugin, mlt_properties image_effect, double ofx_time); void mltofx_action_render( OfxPlugin *plugin, mlt_properties image_effect, double ofx_time, int width, int height); mltofx_depths_mask mltofx_plugin_supported_depths(mlt_properties image_effect); mltofx_components_mask mltofx_plugin_supported_components(mlt_properties image_effect); uint16_t *mltofx_rgba64_to_half(const uint16_t *src, int n_pixels); void mltofx_half_to_rgba64(const uint16_t *src, uint16_t *dst, int n_pixels); float *mltofx_rgba64_to_float(const uint16_t *src, int n_pixels); void mltofx_float_to_rgba64(const float *src, uint16_t *dst, int n_pixels); #endif mlt-7.38.0/src/modules/openfx/openfx/000775 000000 000000 00000000000 15172202314 017417 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/openfx/openfx/LICENSE.md000664 000000 000000 00000002774 15172202314 021035 0ustar00rootroot000000 000000 BSD 3-Clause License Copyright (c) 2025, OpenFX and contributors to the OpenFX project 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 copyright holder 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. mlt-7.38.0/src/modules/openfx/openfx/include/000775 000000 000000 00000000000 15172202314 021042 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/openfx/openfx/include/ofxCore.h000664 000000 000000 00000110627 15172202314 022627 0ustar00rootroot000000 000000 #ifndef _ofxCore_h_ #define _ofxCore_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #include "stddef.h" // for size_t #include // for INT_MIN & INT_MAX #ifdef __cplusplus extern "C" { #endif /** @file ofxCore.h Contains the core OFX architectural struct and function definitions. For more details on the basic OFX architecture, see \ref Architecture. */ /** @brief Platform independent export macro. * * This macro is to be used before any symbol that is to be * exported from a plug-in. This is OS/compiler dependent. */ #if defined(_WIN32) #define OfxExport extern __declspec(dllexport) #else #define OfxExport extern #endif /** @brief Blind data structure to manipulate sets of properties through */ typedef struct OfxPropertySetStruct *OfxPropertySetHandle; /** @brief OFX status return type */ typedef int OfxStatus; /** @brief Generic host structure passed to OfxPlugin::setHost function This structure contains what is needed by a plug-in to bootstrap its connection to the host. */ typedef struct OfxHost { /** @brief Global handle to the host. Extract relevant host properties from this. This pointer will be valid while the binary containing the plug-in is loaded. */ OfxPropertySetHandle host; /** @brief The function which the plug-in uses to fetch suites from the host. \arg \c host the host the suite is being fetched from this \em must be the \e host member of the OfxHost struct containing fetchSuite. \arg \c suiteName ASCII string labelling the host supplied API \arg \c suiteVersion version of that suite to fetch Any API fetched will be valid while the binary containing the plug-in is loaded. Repeated calls to fetchSuite with the same parameters will return the same pointer. It is recommended that hosts should return the same host and suite pointers to all plugins in the same shared lib or bundle. returns - NULL if the API is unknown (either the api or the version requested), - pointer to the relevant API if it was found */ const void *(*fetchSuite)(OfxPropertySetHandle host, const char *suiteName, int suiteVersion); } OfxHost; /** @brief Entry point for plug-ins \arg \c action ASCII c string indicating which action to take \arg \c instance object to which action should be applied, this will need to be cast to the appropriate blind data type depending on the \e action \arg \c inData handle that contains action specific properties \arg \c outData handle where the plug-in should set various action specific properties This is how the host generally communicates with a plug-in. Entry points are used to pass messages to various objects used within OFX. The main use is within the OfxPlugin struct. The exact set of actions is determined by the plug-in API that is being implemented, however all plug-ins can perform several actions. For the list of actions consult \ref ActionsAll. */ typedef OfxStatus (OfxPluginEntryPoint)(const char *action, const void *handle, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs); /** @brief The structure that defines a plug-in to a host. * * This structure is the first element in any plug-in structure * using the OFX plug-in architecture. By examining its members * a host can determine the API that the plug-in implements, * the version of that API, its name and version. * * For details see \ref Architecture. * */ typedef struct OfxPlugin { /** Defines the type of the plug-in, this will tell the host what the plug-in does. e.g.: an image effects plug-in would be a "OfxImageEffectPlugin" */ const char *pluginApi; /** Defines the version of the pluginApi that this plug-in implements */ int apiVersion; /** String that uniquely labels the plug-in among all plug-ins that implement an API. It need not necessarily be human sensible, however the preference is to use reverse internet domain name of the developer, followed by a '.' then by a name that represents the plug-in.. It must be a legal ASCII string and have no whitespace in the name and no non printing chars. For example "uk.co.somesoftwarehouse.myPlugin" */ const char *pluginIdentifier; /** Major version of this plug-in, this gets incremented when backwards compatibility is broken. */ unsigned int pluginVersionMajor; /** Major version of this plug-in, this gets incremented when software is changed, but does not break backwards compatibility. */ unsigned int pluginVersionMinor; /** @brief Function the host uses to connect the plug-in to the host's api fetcher \arg \c fetchApi pointer to host's API fetcher Mandatory function. The very first function called in a plug-in. The plug-in \em must \em not call any OFX functions within this, it must only set its local copy of the host pointer. \pre - nothing else has been called \post - the pointer suite is valid until the plug-in is unloaded It is recommended that hosts should return the same host and suite pointers to all plugins in the same shared lib or bundle. */ void (*setHost)(OfxHost *host); /** @brief Main entry point for plug-ins Mandatory function. The exact set of actions is determined by the plug-in API that is being implemented, however all plug-ins can perform several actions. For the list of actions consult \ref ActionsAll. Preconditions - setHost has been called */ OfxPluginEntryPoint *mainEntry; } OfxPlugin; /** \defgroup ActionsAll OFX Actions These are the actions passed to a plug-in's 'main' function */ /*@{*/ /** @brief This action is the first action passed to a plug-in after the binary containing the plug-in has been loaded. It is there to allow a plug-in to create any global data structures it may need and is also when the plug-in should fetch suites from the host. The \ref handle, \ref inArgs and \ref outArgs arguments to the \ref mainEntry are redundant and should be set to NULL. \pre - The plugin's \ref OfxPlugin::setHost function has been called \post This action will not be called again while the binary containing the plug-in remains loaded. @returns - \ref kOfxStatOK, the action was trapped and all was well, - \ref kOfxStatReplyDefault, the action was ignored, - \ref kOfxStatFailed, the load action failed, no further actions will be passed to the plug-in. Interpret if possible kOfxStatFailed as plug-in indicating it does not want to load Do not create an entry in the host's UI for plug-in then. Plug-in also has the option to return 0 for OfxGetNumberOfPlugins or kOfxStatFailed if host supports OfxSetHost in which case kOfxActionLoad will never be called. - \ref kOfxStatErrFatal, fatal error in the plug-in. */ #define kOfxActionLoad "OfxActionLoad" /** @brief The kOfxActionDescribe is the second action passed to a plug-in. It is where a plugin defines how it behaves and the resources it needs to function. Note that the handle passed in acts as a descriptor for, rather than an instance of the plugin. The handle is global and unique. The plug-in is at liberty to cache the handle away for future reference until the plug-in is unloaded. Most importantly, the effect must set what image effect contexts it is capable of working in. This action *must* be trapped, it is not optional. @param handle handle to the plug-in descriptor, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionLoad has been called \post - \ref kOfxActionDescribe will not be called again, unless it fails and returns one of the error codes where the host is allowed to attempt the action again - the handle argument, being the global plug-in description handle, is a valid handle from the end of a successful describe action until the end of the \ref kOfxActionUnload action (ie: the plug-in can cache it away without worrying about it changing between actions). - \ref kOfxImageEffectActionDescribeInContext will be called once for each context that the host and plug-in mutually support. If a plug-in does not report to support any context supported by host, host should not enable the plug-in. @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatErrMissingHostFeature, in which the plugin will be unloaded and ignored, plugin may post message - \ref kOfxStatErrMemory, in which case describe may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxActionDescribe "OfxActionDescribe" /** @brief This action is the last action passed to the plug-in before the binary containing the plug-in is unloaded. It is there to allow a plug-in to destroy any global data structures it may have created. The handle, inArgs and outArgs arguments to the main entry are redundant and should be set to NULL. \pre - the \ref kOfxActionLoad action has been called - all instances of a plugin have been destroyed \post - No other actions will be called. @returns - \ref kOfxStatOK, the action was trapped all was well - \ref kOfxStatReplyDefault, the action was ignored - \ref kOfxStatErrFatal, in which case we the program will be forced to quit */ #define kOfxActionUnload "OfxActionUnload" /** @brief This action is an action that may be passed to a plug-in instance from time to time in low memory situations. Instances receiving this action should destroy any data structures they may have and release the associated memory, they can later reconstruct this from the effect's parameter set and associated information. For Image Effects, it is generally a bad idea to call this after each render, but rather it should be called after \ref kOfxImageEffectActionEndSequenceRender Some effects, typically those flagged with the \ref kOfxImageEffectInstancePropSequentialRender property, may need to cache information from previously rendered frames to function correctly, or have data structures that are expensive to reconstruct at each frame (eg: a particle system). Ideally, such effect should free such structures during the \ref kOfxImageEffectActionEndSequenceRender action. @param handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatReplyDefault, the action was ignored - \ref kOfxStatErrFatal, - \ref kOfxStatFailed, something went wrong, but no error code appropriate, the plugin should to post a message */ #define kOfxActionPurgeCaches "OfxActionPurgeCaches" /** @brief This action is called when a plugin should synchronise any private data structures to its parameter set. This generally occurs when an effect is about to be saved or copied, but it could occur in other situations as well. @param handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, \post - Any private state data can be reconstructed from the parameter set, @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatReplyDefault, the action was ignored - \ref kOfxStatErrFatal, - \ref kOfxStatFailed, something went wrong, but no error code appropriate, the plugin should to post a message */ #define kOfxActionSyncPrivateData "OfxActionSyncPrivateData" /** @brief This action is the first action passed to a plug-in's instance after its creation. It is there to allow a plugin to create any per-instance data structures it may need. @param handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionDescribe has been called - the instance is fully constructed, with all objects requested in the describe actions (eg, parameters and clips) have been constructed and have had their initial values set. This means that if the values are being loaded from an old setup, that load should have taken place before the create instance action is called. \post - the instance pointer will be valid until the \ref kOfxActionDestroyInstance action is passed to the plug-in with the same instance handle @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatReplyDefault, the action was ignored, but all was well anyway - \ref kOfxStatErrFatal - \ref kOfxStatErrMemory, in which case this may be called again after a memory purge - \ref kOfxStatFailed, something went wrong, but no error code appropriate, the plugin should to post a message if possible and the host should destroy the instanace handle and not attempt to proceed further */ #define kOfxActionCreateInstance "OfxActionCreateInstance" /** @brief This action is the last passed to a plug-in's instance before its destruction. It is there to allow a plugin to destroy any per-instance data structures it may have created. @param handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the handle, - the instance has not had any of its members destroyed yet, \post - the instance pointer is no longer valid and any operation on it will be undefined @returns To some extent, what is returned is moot, a bit like throwing an exception in a C++ destructor, so the host should continue destruction of the instance regardless. - \ref kOfxStatOK, the action was trapped and all was well, - \ref kOfxStatReplyDefault, the action was ignored as the effect had nothing to do, - \ref kOfxStatErrFatal, - \ref kOfxStatFailed, something went wrong, but no error code appropriate, the plugin should to post a message. */ #define kOfxActionDestroyInstance "OfxActionDestroyInstance" /** @brief This action signals that something has changed in a plugin's instance, either by user action, the host or the plugin itself. All change actions are bracketed by a pair of \ref kOfxActionBeginInstanceChanged and \ref kOfxActionEndInstanceChanged actions. The ``inArgs`` property set is used to determine what was the thing inside the instance that was changed. @param handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following properties - \ref kOfxPropType The type of the thing that changed which will be one of.. - \ref kOfxTypeParameter Indicating a parameter's value has changed in some way - \ref kOfxTypeClip A clip to an image effect has changed in some way (for Image Effect Plugins only) - \ref kOfxPropName the name of the thing that was changed in the instance - \ref kOfxPropChangeReason what triggered the change, which will be one of... - \ref kOfxChangeUserEdited - the user or host changed the instance somehow and caused a change to something, this includes undo/redos, resets and loading values from files or presets, - \ref kOfxChangePluginEdited - the plugin itself has changed the value of the instance in some action - \ref kOfxChangeTime - the time has changed and this has affected the value of the object because it varies over time - \ref kOfxPropTime - the effect time at which the chang occurred (for Image Effect Plugins only) - \ref kOfxImageEffectPropRenderScale - the render scale currently being applied to any image fetched from a clip (for Image Effect Plugins only) @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, - \ref kOfxActionBeginInstanceChanged has been called on the instance handle. \post - \ref kOfxActionEndInstanceChanged will be called on the instance handle. @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatReplyDefault, the action was ignored - \ref kOfxStatErrFatal, - \ref kOfxStatFailed, something went wrong, but no error code appropriate, the plugin should to post a message */ #define kOfxActionInstanceChanged "OfxActionInstanceChanged" /** @brief The \ref kOfxActionBeginInstanceChanged and \ref kOfxActionEndInstanceChanged actions are used to bracket all \ref kOfxActionInstanceChanged actions, whether a single change or multiple changes. Some changes to a plugin instance can be grouped logically (eg: a 'reset all' button resetting all the instance's parameters), the begin/end instance changed actions allow a plugin to respond appropriately to a large set of changes. For example, a plugin that maintains a complex internal state can delay any changes to that state until all parameter changes have completed. @param handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following properties - \ref kOfxPropChangeReason what triggered the change, which will be one of... - \ref kOfxChangeUserEdited - the user or host changed the instance somehow and caused a change to something, this includes undo/redos, resets and loading values from files or presets, - \ref kOfxChangePluginEdited - the plugin itself has changed the value of the instance in some action - \ref kOfxChangeTime - the time has changed and this has affected the value of the object because it varies over time @param outArgs is redundant and is set to NULL \post - For \ref kOfxActionBeginInstanceChanged , \ref kOfxActionCreateInstance has been called on the instance handle. - For \ref kOfxActionEndInstanceChanged , \ref kOfxActionBeginInstanceChanged has been called on the instance handle. - \ref kOfxActionCreateInstance has been called on the instance handle. \post - For \ref kOfxActionBeginInstanceChanged, \ref kOfxActionInstanceChanged will be called at least once on the instance handle. - \ref kOfxActionEndInstanceChanged will be called on the instance handle. @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatReplyDefault, the action was ignored - \ref kOfxStatErrFatal, - \ref kOfxStatFailed, something went wrong, but no error code appropriate, the plugin should to post a message */ #define kOfxActionBeginInstanceChanged "OfxActionBeginInstanceChanged" /** @brief Action called after the end of a set of \ref kOfxActionEndInstanceChanged actions, used with ::kOfxActionBeginInstanceChanged to bracket a grouped set of changes, see \ref kOfxActionBeginInstanceChanged*/ #define kOfxActionEndInstanceChanged "OfxActionEndInstanceChanged" /** @brief This is called when an instance is *first* actively edited by a user, ie: and interface is open and parameter values and input clips can be modified. It is there so that effects can create private user interface structures when necassary. Note that some hosts can have multiple editors open on the same effect instance simulateously. @param handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, \post - \ref kOfxActionEndInstanceEdit will be called when the last editor is closed on the instance @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatReplyDefault, the action was ignored - \ref kOfxStatErrFatal, - \ref kOfxStatFailed, something went wrong, but no error code appropriate, the plugin should to post a message */ #define kOfxActionBeginInstanceEdit "OfxActionBeginInstanceEdit" /** @brief This is called when the *last* user interface on an instance closed. It is there so that effects can destroy private user interface structures when necassary. Note that some hosts can have multiple editors open on the same effect instance simulateously, this will only be called when the last of those editors are closed. @param handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionBeginInstanceEdit has been called on the instance handle, \post - no user interface is open on the instance @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatReplyDefault, the action was ignored - \ref kOfxStatErrFatal, - \ref kOfxStatFailed, something went wrong, but no error code appropriate, the plugin should to post a message */ #define kOfxActionEndInstanceEdit "OfxActionEndInstanceEdit" /*@}*/ /** @brief Returns the 'nth' plug-in implemented inside a binary * * Returns a pointer to the 'nth' plug-in implemented in the binary. A function of this type * must be implemented in and exported from each plug-in binary. */ OfxExport OfxPlugin *OfxGetPlugin(int nth); /** @brief Defines the number of plug-ins implemented inside a binary * * A host calls this to determine how many plug-ins there are inside * a binary it has loaded. A function of this type * must be implemented in and exported from each plug-in binary. */ OfxExport int OfxGetNumberOfPlugins(void); /** @brief First thing host should call * * This host call, added in 2020, is not specified in earlier implementation of the API. * Therefore host must check if the plugin implemented it and not assume symbol exists. * The order of calls is then: 1) OfxSetHost, 2) OfxGetNumberOfPlugins, 3) OfxGetPlugin * The host pointer is only assumed valid until OfxGetPlugin where it might get reset. * Plug-in can return kOfxStatFailed to indicate it has nothing to do here, it's not for this Host and it should be skipped silently. */ OfxExport OfxStatus OfxSetHost(const OfxHost *host); /** \defgroup PropertiesAll Ofx Properties These strings are used to identify properties within OFX, they are broken up by the host suite or API they relate to. */ /*@{*/ /** \defgroup PropertiesGeneral General Properties These properties are general properties and apply to may objects across OFX */ /*@{*/ /** @brief Property on the host descriptor, saying what API version of the API is being implemented - Type - int X N - Property Set - host descriptor. This is a version string that will specify which version of the API is being implemented by a host. It can have multiple values. For example "1.0", "1.2.4" etc..... If this is not present, it is safe to assume that the version of the API is "1.0". */ #define kOfxPropAPIVersion "OfxPropAPIVersion" /** @brief General property used to get/set the time of something. - Type - double X 1 - Default - 0, if a setable property - Property Set - commonly used as an argument to actions, input and output. */ #define kOfxPropTime "OfxPropTime" /** @brief Indicates if a host is actively editing the effect with some GUI. - Type - int X 1 - Property Set - effect instance (read only) - Valid Values - 0 or 1 If false the effect currently has no interface, however this may be because the effect is loaded in a background render host, or it may be loaded on an interactive host that has not yet opened an editor for the effect. The output of an effect should only ever depend on the state of its parameters, not on the interactive flag. The interactive flag is more a courtesy flag to let a plugin know that it has an interface. If a plugin wants to have its behaviour dependent on the interactive flag, it can always make a secret parameter which shadows the state if the flag. */ #define kOfxPropIsInteractive "OfxPropIsInteractive" /** @brief The file path to the plugin. - Type - C string X 1 - Property Set - effect descriptor (read only) This is a string that indicates the file path where the plug-in was found by the host. The path is in the native path format for the host OS (eg: UNIX directory separators are forward slashes, Windows ones are backslashes). The path is to the bundle location, see \ref InstallationLocation. eg: '/usr/OFX/Plugins/AcmePlugins/AcmeFantasticPlugin.ofx.bundle' */ #define kOfxPluginPropFilePath "OfxPluginPropFilePath" /** @brief A private data pointer that the plug-in can store its own data behind. - Type - pointer X 1 - Property Set - plugin instance (read/write), - Default - NULL This data pointer is unique to each plug-in instance, so two instances of the same plug-in do not share the same data pointer. Use it to hang any needed private data structures. */ #define kOfxPropInstanceData "OfxPropInstanceData" /** @brief General property, used to identify the kind of an object behind a handle - Type - ASCII C string X 1 - Property Set - any object handle (read only) - Valid Values - currently this can be... - ::kOfxTypeImageEffectHost - ::kOfxTypeImageEffect - ::kOfxTypeImageEffectInstance - ::kOfxTypeParameter - ::kOfxTypeParameterInstance - ::kOfxTypeClip - ::kOfxTypeImage */ #define kOfxPropType "OfxPropType" /** @brief Unique name of an object. - Type - ASCII C string X 1 - Property Set - on many objects (descriptors and instances), see \ref PropertiesByObject (read only) This property is used to label objects uniquely among objects of that type. It is typically set when a plugin creates a new object with a function that takes a name. */ #define kOfxPropName "OfxPropName" /** @brief Identifies a specific version of a host or plugin. - Type - int X N - Property Set - host descriptor (read only), plugin descriptor (read/write) - Default - "0" - Valid Values - positive integers This is a multi dimensional integer property that represents the version of a host (host descriptor), or plugin (plugin descriptor). These represent a version number of the form '1.2.3.4', with each dimension adding another 'dot' on the right. A version is considered to be more recent than another if its ordered set of values is lexicographically greater than another, reading left to right. (ie: 1.2.4 is smaller than 1.2.6). Also, if the number of dimensions is different, then the values of the missing dimensions are considered to be zero (so 1.2.4 is greater than 1.2). */ #define kOfxPropVersion "OfxPropVersion" /** @brief Unique user readable version string of a plugin or host. - Type - string X 1 - Property Set - host descriptor (read only), plugin descriptor (read/write) - Default - none, the host needs to set this - Valid Values - ASCII string This is purely for user feedback, a plugin or host should use ::kOfxPropVersion if they need to check for specific versions. */ #define kOfxPropVersionLabel "OfxPropVersionLabel" /** @brief Description of the plug-in to a user. - Type - string X 1 - Property Set - plugin descriptor (read/write) and instance (read only) - Default - "" - Valid Values - UTF8 string This is a string giving a potentially verbose description of the effect. */ #define kOfxPropPluginDescription "OfxPropPluginDescription" /** @brief User visible name of an object. - Type - UTF8 C string X 1 - Property Set - on many objects (descriptors and instances), see \ref PropertiesByObject. Typically readable and writable in most cases. - Default - the ::kOfxPropName the object was created with. The label is what a user sees on any interface in place of the object's name. Note that resetting this will also reset ::kOfxPropShortLabel and ::kOfxPropLongLabel. */ #define kOfxPropLabel "OfxPropLabel" /** @brief If set this tells the host to use an icon instead of a label for some object in the interface. - Type - string X 2 - Property Set - various descriptors in the API - Default - "" - Valid Values - ASCII string The value is a path is defined relative to the Resource folder that points to an SVG or PNG file containing the icon. The first dimension, if set, will the name of and SVG file, the second a PNG file. */ #define kOfxPropIcon "OfxPropIcon" /** @brief Short user visible name of an object. - Type - UTF8 C string X 1 - Property Set - on many objects (descriptors and instances), see \ref PropertiesByObject. Typically readable and writable in most cases. - Default - initially ::kOfxPropName, but will be reset if ::kOfxPropLabel is changed. This is a shorter version of the label, typically 13 character glyphs or less. Hosts should use this if they have limited display space for their object labels. */ #define kOfxPropShortLabel "OfxPropShortLabel" /** @brief Long user visible name of an object. - Type - UTF8 C string X 1 - Property Set - on many objects (descriptors and instances), see \ref PropertiesByObject. Typically readable and writable in most cases. - Default - initially ::kOfxPropName, but will be reset if ::kOfxPropLabel is changed. This is a longer version of the label, typically 32 character glyphs or so. Hosts should use this if they have mucg display space for their object labels. */ #define kOfxPropLongLabel "OfxPropLongLabel" /** @brief Indicates why a plug-in changed. - Type - ASCII C string X 1 - Property Set - inArgs parameter on the ::kOfxActionInstanceChanged action. - Valid Values - this can be... - ::kOfxChangeUserEdited - the user directly edited the instance somehow and caused a change to something, this includes undo/redos and resets - ::kOfxChangePluginEdited - the plug-in itself has changed the value of the object in some action - ::kOfxChangeTime - the time has changed and this has affected the value of the object because it varies over time Argument property for the ::kOfxActionInstanceChanged action. */ #define kOfxPropChangeReason "OfxPropChangeReason" /** @brief A pointer to an effect instance. - Type - pointer X 1 - Property Set - on an interact instance (read only) This property is used to link an object to the effect. For example if the plug-in supplies an openGL overlay for an image effect, the interact instance will have one of these so that the plug-in can connect back to the effect the GUI links to. */ #define kOfxPropEffectInstance "OfxPropEffectInstance" /** @brief A pointer to an operating system specific application handle. - Type - pointer X 1 - Property Set - host descriptor. Some plug-in vendor want raw OS specific handles back from the host so they can do interesting things with host OS APIs. Typically this is to control windowing properly on Microsoft Windows. This property returns the appropriate 'root' window handle on the current operating system. So on Windows this would be the hWnd of the application main window. */ #define kOfxPropHostOSHandle "OfxPropHostOSHandle" /*@}*/ /*@}*/ /** @brief String used as a value to ::kOfxPropChangeReason to indicate a user has changed something */ #define kOfxChangeUserEdited "OfxChangeUserEdited" /** @brief String used as a value to ::kOfxPropChangeReason to indicate the plug-in itself has changed something */ #define kOfxChangePluginEdited "OfxChangePluginEdited" /** @brief String used as a value to ::kOfxPropChangeReason to a time varying object has changed due to a time change */ #define kOfxChangeTime "OfxChangeTime" /** @brief How time is specified within the OFX API */ typedef double OfxTime; /** @brief Defines one dimensional integer bounds */ typedef struct OfxRangeI { int min, max; } OfxRangeI; /** @brief Defines one dimensional double bounds */ typedef struct OfxRangeD { double min, max; } OfxRangeD; /** @brief Defines two dimensional integer point */ typedef struct OfxPointI { int x, y; } OfxPointI; /** @brief Defines two dimensional double point */ typedef struct OfxPointD { double x, y; } OfxPointD; /** @brief Used to flag infinite rects. Set minimums to this to indicate infinite This is effectively INT_MAX. */ #define kOfxFlagInfiniteMax INT_MAX /** @brief Used to flag infinite rects. Set minimums to this to indicate infinite. This is effectively INT_MIN */ #define kOfxFlagInfiniteMin INT_MIN /** @brief Defines two dimensional integer region Regions are x1 <= x < x2 Infinite regions are flagged by setting - x1 = \ref kOfxFlagInfiniteMin - y1 = \ref kOfxFlagInfiniteMin - x2 = \ref kOfxFlagInfiniteMax - y2 = \ref kOfxFlagInfiniteMax */ typedef struct OfxRectI { int x1, y1, x2, y2; } OfxRectI; /** @brief Defines two dimensional double region Regions are x1 <= x < x2 Infinite regions are flagged by setting - x1 = \ref kOfxFlagInfiniteMin - y1 = \ref kOfxFlagInfiniteMin - x2 = \ref kOfxFlagInfiniteMax - y2 = \ref kOfxFlagInfiniteMax */ typedef struct OfxRectD { double x1, y1, x2, y2; } OfxRectD; /** @brief String used to label unset bitdepths */ #define kOfxBitDepthNone "OfxBitDepthNone" /** @brief String used to label unsigned 8 bit integer samples */ #define kOfxBitDepthByte "OfxBitDepthByte" /** @brief String used to label unsigned 16 bit integer samples */ #define kOfxBitDepthShort "OfxBitDepthShort" /** @brief String used to label half-float (16 bit floating point) samples * \version Added in Version 1.4. Was in ofxOpenGLRender.h before. */ #define kOfxBitDepthHalf "OfxBitDepthHalf" /** @brief String used to label signed 32 bit floating point samples */ #define kOfxBitDepthFloat "OfxBitDepthFloat" /** \defgroup StatusCodes Status Codes These strings are used to identify error states within ofx, they are returned by various host suite functions, as well as plug-in functions. The valid return codes for each function are documented with that function. */ /*@{*/ /** \defgroup StatusCodesGeneral General Status Codes General status codes start at 1 and continue until 999 */ /*@{*/ /** @brief Status code indicating all was fine */ #define kOfxStatOK 0 /** @brief Status error code for a failed operation. */ #define kOfxStatFailed ((int)1) /** @brief Status error code for a fatal error Only returned in the case where the plug-in or host cannot continue to function and needs to be restarted. */ #define kOfxStatErrFatal ((int)2) /** @brief Status error code for an operation on or request for an unknown object */ #define kOfxStatErrUnknown ((int)3) /** @brief Status error code returned by plug-ins when they are missing host functionality, either an API or some optional functionality (eg: custom params). Plug-Ins returning this should post an appropriate error message stating what they are missing. */ #define kOfxStatErrMissingHostFeature ((int) 4) /** @brief Status error code for an unsupported feature/operation */ #define kOfxStatErrUnsupported ((int) 5) /** @brief Status error code for an operation attempting to create something that exists */ #define kOfxStatErrExists ((int) 6) /** @brief Status error code for an incorrect format */ #define kOfxStatErrFormat ((int) 7) /** @brief Status error code indicating that something failed due to memory shortage */ #define kOfxStatErrMemory ((int) 8) /** @brief Status error code for an operation on a bad handle */ #define kOfxStatErrBadHandle ((int) 9) /** @brief Status error code indicating that a given index was invalid or unavailable */ #define kOfxStatErrBadIndex ((int)10) /** @brief Status error code indicating that something failed due an illegal value */ #define kOfxStatErrValue ((int) 11) /** @brief OfxStatus returned indicating a 'yes' */ #define kOfxStatReplyYes ((int) 12) /** @brief OfxStatus returned indicating a 'no' */ #define kOfxStatReplyNo ((int) 13) /** @brief OfxStatus returned indicating that a default action should be performed */ #define kOfxStatReplyDefault ((int) 14) /*@}*/ /*@}*/ #ifdef __cplusplus } #endif /** @mainpage OFX : Open Plug-Ins For Special Effects This page represents the automatically extracted HTML documentation of the source headers for the OFX Image Effect API. The documentation was extracted by doxygen (http://www.doxygen.org). A more complete reference manual is https://openfx.readthedocs.io . */ #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxDialog.h000664 000000 000000 00000004441 15172202314 023132 0ustar00rootroot000000 000000 #ifndef _ofxDialog_h_ #define _ofxDialog_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #include "ofxCore.h" #include "ofxProperty.h" #ifdef __cplusplus extern "C" { #endif /** @file ofxDialog.h This file contains an optional suite which should be used to popup a native OS dialog from a host parameter changed action. When a host uses a fullscreen window and is running the OFX plugins in another thread it can lead to a lot of conflicts if that plugin will try to open its own window. This suite will provide the functionality for a plugin to request running its dialog in the UI thread, and informing the host it will do this so it can take the appropriate actions needed. (Like lowering its priority etc..) */ /** @brief The name of the Dialog suite, used to fetch from a host via OfxHost::fetchSuite */ #define kOfxDialogSuite "OfxDialogSuite" /** @brief Action called after a dialog has requested a 'Dialog' The arguments to the action are: \arg \c user_data Pointer which was provided when the plugin requested the Dialog When the plugin receives this action it is safe to popup a dialog. It runs in the host's UI thread, which may differ from the main OFX processing thread. Plugin should return from this action when all Dialog interactions are done. At that point the host will continue again. The host will not send any other messages asynchronous to this one. */ #define kOfxActionDialog "OfxActionDialog" typedef struct OfxDialogSuiteV1 { /** @brief Request the host to send a kOfxActionDialog to the plugin from its UI thread. \pre - user_data: A pointer to any user data \post @returns - ::kOfxStatOK - The host has queued the request and will send an 'OfxActionDialog' - ::kOfxStatFailed - The host has no provisio for this or can not deal with it currently. */ OfxStatus (*RequestDialog)( void *user_data ); /** @brief Inform the host of redraw event so it can redraw itself If the host runs fullscreen in OpenGL, it would otherwise not receive redraw event when a dialog in front would catch all events. \pre \post @returns - ::kOfxStatReplyDefault */ OfxStatus (*NotifyRedrawPending)( void ); } OfxDialogSuiteV1; #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxDrawSuite.h000664 000000 000000 00000014125 15172202314 023642 0ustar00rootroot000000 000000 #ifndef _ofxDraw_h_ #define _ofxDraw_h_ #include "ofxCore.h" #include "ofxPixels.h" // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #ifdef __cplusplus extern "C" { #endif /** @file ofxDrawSuite.h API for host- and GPU API-independent drawing. @version Added in OpenFX 1.5 */ /** @brief the string that names the DrawSuite, passed to OfxHost::fetchSuite */ #define kOfxDrawSuite "OfxDrawSuite" /** @brief Blind declaration of an OFX drawing context */ typedef struct OfxDrawContext *OfxDrawContextHandle; /** @brief The Draw Context handle - Type - pointer X 1 - Property Set - read only property on the inArgs of the following actions... - ::kOfxInteractActionDraw */ #define kOfxInteractPropDrawContext "OfxInteractPropDrawContext" /** @brief Defines valid values for OfxDrawSuiteV1::getColour */ typedef enum OfxStandardColour { kOfxStandardColourOverlayBackground, kOfxStandardColourOverlayActive, kOfxStandardColourOverlaySelected, kOfxStandardColourOverlayDeselected, kOfxStandardColourOverlayMarqueeFG, kOfxStandardColourOverlayMarqueeBG, kOfxStandardColourOverlayText } OfxStandardColour; /** @brief Defines valid values for OfxDrawSuiteV1::setLineStipple */ typedef enum OfxDrawLineStipplePattern { kOfxDrawLineStipplePatternSolid, // ----- kOfxDrawLineStipplePatternDot, // ..... kOfxDrawLineStipplePatternDash, // - - - kOfxDrawLineStipplePatternAltDash, // - - - kOfxDrawLineStipplePatternDotDash // .-.-.- } OfxDrawLineStipplePattern; /** @brief Defines valid values for OfxDrawSuiteV1::draw */ typedef enum OfxDrawPrimitive { kOfxDrawPrimitiveLines, kOfxDrawPrimitiveLineStrip, kOfxDrawPrimitiveLineLoop, kOfxDrawPrimitiveRectangle, kOfxDrawPrimitivePolygon, kOfxDrawPrimitiveEllipse } OfxDrawPrimitive; /** @brief Defines text alignment values for OfxDrawSuiteV1::drawText */ typedef enum OfxDrawTextAlignment { kOfxDrawTextAlignmentLeft = 0x0001, kOfxDrawTextAlignmentRight = 0x0002, kOfxDrawTextAlignmentTop = 0x0004, kOfxDrawTextAlignmentBottom = 0x0008, kOfxDrawTextAlignmentBaseline = 0x0010, kOfxDrawTextAlignmentCenterH = (kOfxDrawTextAlignmentLeft | kOfxDrawTextAlignmentRight), kOfxDrawTextAlignmentCenterV = (kOfxDrawTextAlignmentTop | kOfxDrawTextAlignmentBaseline) } OfxDrawTextAlignment; /** @brief OFX suite that allows an effect to draw to a host-defined display context. To use this, the plugin must use kOfxImageEffectPluginPropOverlayInteractV2. */ typedef struct OfxDrawSuiteV1 { /** @brief Retrieves the host's desired draw colour for \arg \c context draw context \arg \c std_colour desired colour type \arg \c colour returned RGBA colour @returns - ::kOfxStatOK - the colour was returned - ::kOfxStatErrValue - std_colour was invalid - ::kOfxStatFailed - failure, e.g. if function is called outside kOfxInteractActionDraw */ OfxStatus (*getColour)(OfxDrawContextHandle context, OfxStandardColour std_colour, OfxRGBAColourF *colour); /** @brief Sets the colour for future drawing operations (lines, filled shapes and text) \arg \c context draw context \arg \c colour RGBA colour The host should use "over" compositing when using a non-opaque colour. @returns - ::kOfxStatOK - the colour was changed - ::kOfxStatFailed - failure, e.g. if function is called outside kOfxInteractActionDraw */ OfxStatus (*setColour)(OfxDrawContextHandle context, const OfxRGBAColourF *colour); /** @brief Sets the line width for future line drawing operations \arg \c context draw context \arg \c width line width Use width 0 for a single pixel line or non-zero for a smooth line of the desired width The host should adjust for screen density. @returns - ::kOfxStatOK - the width was changed - ::kOfxStatFailed - failure, e.g. if function is called outside kOfxInteractActionDraw */ OfxStatus (*setLineWidth)(OfxDrawContextHandle context, float width); /** @brief Sets the stipple pattern for future line drawing operations \arg \c context draw context \arg \c pattern desired stipple pattern @returns - ::kOfxStatOK - the pattern was changed - ::kOfxStatErrValue - pattern was not valid - ::kOfxStatFailed - failure, e.g. if function is called outside kOfxInteractActionDraw */ OfxStatus (*setLineStipple)(OfxDrawContextHandle context, OfxDrawLineStipplePattern pattern); /** @brief Draws a primitive of the desired type \arg \c context draw context \arg \c primitive desired primitive \arg \c points array of points in the primitive \arg \c point_count number of points in the array kOfxDrawPrimitiveLines - like GL_LINES, n points draws n/2 separated lines kOfxDrawPrimitiveLineStrip - like GL_LINE_STRIP, n points draws n-1 connected lines kOfxDrawPrimitiveLineLoop - like GL_LINE_LOOP, n points draws n connected lines kOfxDrawPrimitiveRectangle - draws an axis-aligned filled rectangle defined by 2 opposite corner points kOfxDrawPrimitivePolygon - like GL_POLYGON, draws a filled n-sided polygon kOfxDrawPrimitiveEllipse - draws a axis-aligned elliptical line (not filled) within the rectangle defined by 2 opposite corner points @returns - ::kOfxStatOK - the draw was completed - ::kOfxStatErrValue - invalid primitive, or point_count not valid for primitive - ::kOfxStatFailed - failure, e.g. if function is called outside kOfxInteractActionDraw */ OfxStatus (*draw)(OfxDrawContextHandle context, OfxDrawPrimitive primitive, const OfxPointD *points, int point_count); /** @brief Draws text at the specified position \arg \c context draw context \arg \c text text to draw (UTF-8 encoded) \arg \c pos position at which to align the text \arg \c alignment text alignment flags (see kOfxDrawTextAlignment*) The text font face and size are determined by the host. @returns - ::kOfxStatOK - the text was drawn - ::kOfxStatErrValue - text or pos were not defined - ::kOfxStatFailed - failure, e.g. if function is called outside kOfxInteractActionDraw */ OfxStatus (*drawText)(OfxDrawContextHandle context, const char *text, const OfxPointD *pos, int alignment); } OfxDrawSuiteV1; #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxGPURender.h000664 000000 000000 00000110561 15172202314 023527 0ustar00rootroot000000 000000 #pragma once #ifndef __OFXGPURENDER_H__ #define __OFXGPURENDER_H__ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause /** @file ofxGPURender.h This file contains an optional suite for performing GPU-accelerated rendering of OpenFX Image Effect Plug-ins. For details see \ref ofxGPURender. It allows hosts and plug-ins to support OpenGL, OpenCL, CUDA, and Metal. Additional GPU APIs, such a Vulkan, could use similar techniques. */ #include "ofxImageEffect.h" #ifdef __cplusplus extern "C" { #endif /** @defgroup OpenGLRenderSuite OpenGL Render Suite * @{ */ /** @brief The name of the OpenGL render suite, used to fetch from a host via OfxHost::fetchSuite */ #define kOfxOpenGLRenderSuite "OfxImageEffectOpenGLRenderSuite" //#define kOfxOpenGLRenderSuite_ext "OfxImageEffectOpenGLRenderSuite_ext" #ifndef kOfxBitDepthHalf /** @brief String used to label the OpenGL half float (16 bit floating point) sample format */ #define kOfxBitDepthHalf "OfxBitDepthHalf" #endif /** @brief Indicates whether a host or plug-in can support OpenGL accelerated rendering - Type - C string X 1 - Property Set - plug-in descriptor (read/write), host descriptor (read only) - plug-in instance change (read/write) - Default - "false" for a plug-in - Valid Values - This must be one of - "false" - in which case the host or plug-in does not support OpenGL accelerated rendering - "true" - which means a host or plug-in can support OpenGL accelerated rendering, in the case of plug-ins this also means that it is capable of CPU based rendering in the absence of a GPU - "needed" - only for plug-ins, this means that an plug-in has to have OpenGL support, without which it cannot work. V1.4: It is now expected from host reporting v1.4 that the plug-in can during instance change switch from true to false and false to true. */ #define kOfxImageEffectPropOpenGLRenderSupported "OfxImageEffectPropOpenGLRenderSupported" /** @brief Indicates the bit depths supported by a plug-in during OpenGL renders. This is analogous to ::kOfxImageEffectPropSupportedPixelDepths. When a plug-in sets this property, the host will try to provide buffers/textures in one of the supported formats. Additionally, the target buffers where the plug-in renders to will be set to one of the supported formats. Unlike ::kOfxImageEffectPropSupportedPixelDepths, this property is optional. Shader-based effects might not really care about any format specifics when using OpenGL textures, so they can leave this unset and allow the host the decide the format. - Type - string X N - Property Set - plug-in descriptor (read only) - Default - none set - Valid Values - This must be one of - ::kOfxBitDepthNone (implying a clip is unconnected, not valid for an image) - ::kOfxBitDepthByte - ::kOfxBitDepthShort - ::kOfxBitDepthHalf - ::kOfxBitDepthFloat */ #define kOfxOpenGLPropPixelDepth "OfxOpenGLPropPixelDepth" /** @brief Indicates that a plug-in SHOULD use OpenGL acceleration in the current action When a plug-in and host have established they can both use OpenGL renders then when this property has been set the host expects the plug-in to render its result into the buffer it has setup before calling the render. The plug-in can then also safely use the 'OfxImageEffectOpenGLRenderSuite' - Type - int X 1 - Property Set - inArgs property set of the following actions... - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender - Valid Values - 0 indicates that the plug-in cannot use the OpenGL suite - 1 indicates that the plug-in should render into the texture, and may use the OpenGL suite functions. \note Once this property is set, the host and plug-in have agreed to use OpenGL, so the effect SHOULD access all its images through the OpenGL suite. v1.4: kOfxImageEffectPropOpenGLEnabled should probably be checked in Instance Changed prior to try to read image via clipLoadTexture */ #define kOfxImageEffectPropOpenGLEnabled "OfxImageEffectPropOpenGLEnabled" /** @brief Indicates the texture index of an image turned into an OpenGL texture by the host - Type - int X 1 - Property Set - texture handle returned by ` OfxImageEffectOpenGLRenderSuiteV1::clipLoadTexture (read only) This value should be cast to a GLuint and used as the texture index when performing OpenGL texture operations. The property set of the following actions should contain this property: - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender */ #define kOfxImageEffectPropOpenGLTextureIndex "OfxImageEffectPropOpenGLTextureIndex" /** @brief Indicates the texture target enumerator of an image turned into an OpenGL texture by the host - Type - int X 1 - Property Set - texture handle returned by OfxImageEffectOpenGLRenderSuiteV1::clipLoadTexture (read only) This value should be cast to a GLenum and used as the texture target when performing OpenGL texture operations. The property set of the following actions should contain this property: - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender */ #define kOfxImageEffectPropOpenGLTextureTarget "OfxImageEffectPropOpenGLTextureTarget" /** @brief Indicates whether a host or plug-in can (or more importantly cannot) support CPU rendering. - Type - C string X 1 - Property Set - plug-in descriptor (read/write), host descriptor (read only) - plug-in instance change (read/write) - Default - "true" for host and plug-in - Valid Values - This must be one of - "false" - in which case the host or plug-in does not support CPU rendering - "true" - which means a host or plug-in can support CPU rendering @version added in version 1.5.1. */ #define kOfxImageEffectPropCPURenderSupported "OfxImageEffectPropCPURenderSupported" /** @name StatusReturnValues OfxStatus returns indicating that a OpenGL render error has occurred: - If a plug-in returns ::kOfxStatGLRenderFailed, the host should retry the render with OpenGL rendering disabled. - If a plug-in returns ::kOfxStatGLOutOfMemory, the host may choose to free resources on the GPU and retry the OpenGL render, rather than immediately falling back to CPU rendering. */ /** * @{ */ /** @brief GPU render ran out of memory */ #define kOfxStatGPUOutOfMemory ((int) 1001) /** @brief OpenGL render ran out of memory (same as ``kOfxStatGPUOutOfMemory``) */ #define kOfxStatGLOutOfMemory ((int) 1001) /** @brief GPU render failed in a non-memory-related way */ #define kOfxStatGPURenderFailed ((int) 1002) /** @brief OpenGL render failed in a non-memory-related way (same as ``kOfxStatGPURenderFailed``) */ #define kOfxStatGLRenderFailed ((int) 1002) /* for backward compatibility */ /** @} */ /** @brief OFX suite that provides image to texture conversion for OpenGL processing */ typedef struct OfxImageEffectOpenGLRenderSuiteV1 { /** @brief loads an image from an OFX clip as a texture into OpenGL \arg \c clip clip to load the image from \arg \c time effect time to load the image from \arg \c format requested texture format (As in none,byte,word,half,float, etc..) When set to NULL, the host decides the format based on the plug-in's ::kOfxOpenGLPropPixelDepth setting. \arg \c region region of the image to load (optional, set to NULL to get a 'default' region) this is in the \ref CanonicalCoordinates. \arg \c textureHandle property set containing information about the texture An image is fetched from a clip at the indicated time for the given region and loaded into an OpenGL texture. When a specific format is requested, the host ensures it gives the requested format. When the clip specified is the "Output" clip, the format is ignored and the host must bind the resulting texture as the current color buffer (render target). This may also be done prior to calling the ::kOfxImageEffectActionRender action. If the \em region parameter is set to non-NULL, then it will be clipped to the clip's Region of Definition for the given time. The returned image will be \em at \em least as big as this region. If the region parameter is not set or is NULL, then the region fetched will be at least the Region of Interest the effect has previously specified, clipped to the clip's Region of Definition. Information about the texture, including the texture index, is returned in the \em textureHandle argument. The properties on this handle will be... - ::kOfxImageEffectPropOpenGLTextureIndex - ::kOfxImageEffectPropOpenGLTextureTarget - ::kOfxImageEffectPropPixelDepth - ::kOfxImageEffectPropComponents - ::kOfxImageEffectPropPreMultiplication - ::kOfxImageEffectPropRenderScale - ::kOfxImagePropPixelAspectRatio - ::kOfxImagePropBounds - ::kOfxImagePropRegionOfDefinition - ::kOfxImagePropRowBytes - ::kOfxImagePropField - ::kOfxImagePropUniqueIdentifier With the exception of the OpenGL specifics, these properties are the same as the properties in an image handle returned by clipGetImage in the image effect suite. \pre - clip was returned by clipGetHandle - Format property in the texture handle \post - texture handle to be disposed of by clipFreeTexture before the action returns - when the clip specified is the "Output" clip, the format is ignored and the host must bind the resulting texture as the current color buffer (render target). This may also be done prior to calling the render action. @returns - ::kOfxStatOK - the image was successfully fetched and returned in the handle, - ::kOfxStatFailed - the image could not be fetched because it does not exist in the clip at the indicated time and/or region, the plug-in should continue operation, but assume the image was black and transparent. - ::kOfxStatErrBadHandle - the clip handle was invalid, - ::kOfxStatErrMemory - not enough OpenGL memory was available for the effect to load the texture. The plug-in should abort the GL render and return ::kOfxStatErrMemory, after which the host can decide to retry the operation with CPU based processing. \note - this is the OpenGL equivalent of clipGetImage from OfxImageEffectSuiteV1 */ OfxStatus (*clipLoadTexture)(OfxImageClipHandle clip, OfxTime time, const char *format, const OfxRectD *region, OfxPropertySetHandle *textureHandle); /** @brief Releases the texture handle previously returned by clipLoadTexture For input clips, this also deletes the texture from OpenGL. This should also be called on the output clip; for the Output clip, it just releases the handle but does not delete the texture (since the host will need to read it). \pre - textureHandle was returned by clipGetImage \post - all operations on textureHandle will be invalid, and the OpenGL texture it referred to has been deleted (for source clips) @returns - ::kOfxStatOK - the image was successfully fetched and returned in the handle, - ::kOfxStatFailed - general failure for some reason, - ::kOfxStatErrBadHandle - the image handle was invalid, */ OfxStatus (*clipFreeTexture)(OfxPropertySetHandle textureHandle); /** @brief Request the host to minimize its GPU resource load When a plug-in fails to allocate GPU resources, it can call this function to request the host to flush its GPU resources if it holds any. After the function the plug-in can try again to allocate resources which then might succeed if the host actually has released anything. \pre \post - No changes to the plug-in GL state should have been made. @returns - ::kOfxStatOK - the host has actually released some resources, - ::kOfxStatReplyDefault - nothing the host could do.. */ OfxStatus (*flushResources)( ); } OfxImageEffectOpenGLRenderSuiteV1; /** @brief Action called when an effect has just been attached to an OpenGL context. The purpose of this action is to allow a plug-in to set up any data it may need to do OpenGL rendering in an instance. For example... - allocate a lookup table on a GPU, - create an OpenCL or CUDA context that is bound to the host's OpenGL context so it can share buffers. The plug-in will be responsible for deallocating any such shared resource in the \ref ::kOfxActionOpenGLContextDetached action. A host cannot call ::kOfxActionOpenGLContextAttached on the same instance without an intervening ::kOfxActionOpenGLContextDetached. A host can have a plug-in swap OpenGL contexts by issuing a attach/detach for the first context then another attach for the next context. The arguments to the action are... \arg \c handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle \arg \c inArgs is redundant and set to NULL \arg \c outArgs is redundant and set to NULL A plug-in can return... - ::kOfxStatOK, the action was trapped and all was well - ::kOfxStatReplyDefault, the action was ignored, but all was well anyway - ::kOfxStatErrMemory, in which case this may be called again after a memory purge - ::kOfxStatFailed, something went wrong, but no error code appropriate, the plug-in should to post a message if possible and the host should not attempt to run the plug-in in OpenGL render mode. */ #define kOfxActionOpenGLContextAttached "OfxActionOpenGLContextAttached" /** @brief Action called when an effect is about to be detached from an OpenGL context The purpose of this action is to allow a plug-in to deallocate any resource allocated in \ref ::kOfxActionOpenGLContextAttached just before the host decouples a plug-in from an OpenGL context. The host must call this with the same OpenGL context active as it called with the corresponding ::kOfxActionOpenGLContextAttached. The arguments to the action are... \arg \c handle handle to the plug-in instance, cast to an \ref OfxImageEffectHandle \arg \c inArgs is redundant and set to NULL \arg \c outArgs is redundant and set to NULL A plug-in can return... - ::kOfxStatOK, the action was trapped and all was well - ::kOfxStatReplyDefault, the action was ignored, but all was well anyway - ::kOfxStatErrMemory, in which case this may be called again after a memory purge - ::kOfxStatFailed, something went wrong, but no error code appropriate, the plug-in should to post a message if possible and the host should not attempt to run the plug-in in OpenGL render mode. */ #define kOfxActionOpenGLContextDetached "kOfxActionOpenGLContextDetached" /** @page ofxOpenGLRender OpenGL Acceleration of Rendering @section ofxOpenGLRenderIntro Introduction The OfxOpenGLRenderSuite allows image effects to use OpenGL commands (hopefully backed by a GPU) to accelerate rendering of their outputs. The basic scheme is simple.... - An effect indicates it wants to use OpenGL acceleration by setting the ::kOfxImageEffectPropOpenGLRenderSupported flag on its descriptor - A host indicates it supports OpenGL acceleration by setting ::kOfxImageEffectPropOpenGLRenderSupported on its descriptor - In an effect's ::kOfxImageEffectActionGetClipPreferences action, an effect indicates what clips it will be loading images from onto the GPU's memory during an effect's ::kOfxImageEffectActionRender action. @section ofxOpenGLRenderHouseKeeping OpenGL House Keeping If a host supports OpenGL rendering then it flags this with the string property ::kOfxImageEffectPropOpenGLRenderSupported on its descriptor property set. Effects that cannot run without OpenGL support should examine this in ::kOfxActionDescribe action and return a ::kOfxStatErrMissingHostFeature status flag if it is not set to "true". Effects flag to a host that they support OpenGL rendering by setting the string property ::kOfxImageEffectPropOpenGLRenderSupported on their effect descriptor during the ::kOfxActionDescribe action. Effects can work in three ways.... - purely on CPUs without any OpenGL support at all, in which case they should set ::kOfxImageEffectPropOpenGLRenderSupported to be "false" (the default), - on CPUs but with optional OpenGL support, in which case they should set ::kOfxImageEffectPropOpenGLRenderSupported to be "true", - only with OpenGL support, in which case they should set ::kOfxImageEffectPropOpenGLRenderSupported to be "needed". Hosts can examine this flag and respond to it appropriately. Effects can use OpenGL accelerated rendering during the following action... - ::kOfxImageEffectActionRender If an effect has indicated that it optionally supports OpenGL acceleration, it should check the property ::kOfxImageEffectPropOpenGLEnabled passed as an in argument to the following actions, - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender If this property is set to 0, then it should not attempt to use any calls to the OpenGL suite or OpenGL calls whilst rendering. @section ofxOpenGLRenderGettingTextures Getting Images as Textures An effect could fetch an image into memory from a host via the standard Image Effect suite "clipGetImage" call, then create an OpenGL texture from that. However as several buffer copies and various other bits of house keeping may need to happen to do this, it is more efficient for a host to create the texture directly. The OfxOpenGLRenderSuiteV1::clipLoadTexture function does this. The arguments and semantics are similar to the OfxImageEffectSuiteV2::clipGetImage function, with a few minor changes. The effect is passed back a property handle describing the texture. Once the texture is finished with, this should be disposed of via the OfxOpenGLRenderSuiteV1::clipFreeTexture function, which will also delete the associated OpenGL texture (for source clips). The returned handle has a set of properties on it, analogous to the properties returned on the image handle by OfxImageEffectSuiteV2::clipGetImage. These are: - ::kOfxImageEffectPropOpenGLTextureIndex - ::kOfxImageEffectPropOpenGLTextureTarget - ::kOfxImageEffectPropPixelDepth - ::kOfxImageEffectPropComponents - ::kOfxImageEffectPropPreMultiplication - ::kOfxImageEffectPropRenderScale - ::kOfxImagePropPixelAspectRatio - ::kOfxImagePropBounds - ::kOfxImagePropRegionOfDefinition - ::kOfxImagePropRowBytes - ::kOfxImagePropField - ::kOfxImagePropUniqueIdentifier The main difference between this and an image handle is that the ::kOfxImagePropData property is replaced by the kOfxImageEffectPropOpenGLTextureIndex property. This integer property should be cast to a GLuint and is the index to use for the OpenGL texture. Next to texture handle the texture target enumerator is given in kOfxImageEffectPropOpenGLTextureTarget Note, because the image is being directly loaded into a texture by the host it need not obey the Clip Preferences action to remap the image to the pixel depth the effect requested. @section ofxOpenGLRenderOutput Render Output Directly with OpenGL Effects can use the graphics context as they see fit. They may be doing several render passes with fetch back from the card to main memory via 'render to texture' mechanisms interleaved with passes performed on the CPU. The effect must leave output on the graphics card in the provided output image texture buffer. The host will create a default OpenGL viewport that is the size of the render window passed to the render action. The following code snippet shows how the viewport should be rooted at the bottom left of the output texture. \verbatim // set up the OpenGL context for the render to texture ... // figure the size of the render window int dx = renderWindow.x2 - renderWindow.x1; int dy = renderWindow.y2 - renderWindow.y2; // setup the output viewport glViewport(0, 0, dx, dy); \endverbatim Prior to calling the render action the host may also choose to bind the output texture as the current color buffer (render target), or they may defer doing this until clipLoadTexture is called for the output clip. After this, it is completely up to the effect to choose what OpenGL operations to render with, including projections and so on. @section ofxOpenGLRenderContext OpenGL Current Context The host is only required to make the OpenGL context current (e.g., using wglMakeCurrent, for Windows) during the following actions: - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender - ::kOfxActionOpenGLContextAttached - ::kOfxActionOpenGLContextDetached For the first 3 actions, Render through EndSequenceRender, the host is only required to set the OpenGL context if ::kOfxImageEffectPropOpenGLEnabled is set. In other words, a plug-in should not expect the OpenGL context to be current for other OFX calls, such as ::kOfxImageEffectActionDescribeInContext. */ /** @}*/ // end of OpenGLRender doc group /** * @defgroup CudaRender CUDA Rendering * @version CUDA rendering was added in version 1.5. * * @{ */ /** @brief Indicates whether a host or plug-in can support CUDA render - Type - string X 1 - Property Set - plug-in descriptor (read/write), host descriptor (read only) - Default - "false" for a plug-in - Valid Values - This must be one of - "false" - the host or plug-in does not support CUDA render - "true" - the host or plug-in can support CUDA render */ #define kOfxImageEffectPropCudaRenderSupported "OfxImageEffectPropCudaRenderSupported" /** @brief Indicates that a plug-in SHOULD use CUDA render in the current action If a plug-in and host have both set kOfxImageEffectPropCudaRenderSupported="true" then the host MAY set this property to indicate that it is passing images as CUDA memory pointers. - Type - int X 1 - Property Set - inArgs property set of the following actions... - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender - Valid Values - 0 indicates that the kOfxImagePropData of each image of each clip is a CPU memory pointer. - 1 indicates that the kOfxImagePropData of each image of each clip is a CUDA memory pointer. */ #define kOfxImageEffectPropCudaEnabled "OfxImageEffectPropCudaEnabled" /** @brief Indicates whether a host or plug-in can support CUDA streams - Type - string X 1 - Property Set - plug-in descriptor (read/write), host descriptor (read only) - Default - "false" for a plug-in - Valid Values - This must be one of - "false" - in which case the host or plug-in does not support CUDA streams - "true" - which means a host or plug-in can support CUDA streams */ #define kOfxImageEffectPropCudaStreamSupported "OfxImageEffectPropCudaStreamSupported" /** @brief The CUDA stream to be used for rendering - Type - pointer X 1 - Property Set - inArgs property set of the following actions... - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender This property will only be set if the host and plug-in both support CUDA streams. If set: - this property contains a pointer to the stream of CUDA render (cudaStream_t). In order to use it, reinterpret_cast(pointer) is needed. - the plug-in SHOULD ensure that its render action enqueues any asynchronous CUDA operations onto the supplied queue. - the plug-in SHOULD NOT wait for final asynchronous operations to complete before returning from the render action, and SHOULD NOT call cudaDeviceSynchronize() at any time. If not set: - the plug-in SHOULD ensure that any asynchronous operations it enqueues have completed before returning from the render action. */ #define kOfxImageEffectPropCudaStream "OfxImageEffectPropCudaStream" /** @}*/ // end CudaRender doc group /** * @defgroup MetalRender Apple Metal Rendering * @version Metal rendering was added in version 1.5. * @{ */ /** @brief Indicates whether a host or plug-in can support Metal render - Type - string X 1 - Property Set - plug-in descriptor (read/write), host descriptor (read only) - Default - "false" for a plug-in - Valid Values - This must be one of - "false" - the host or plug-in does not support Metal render - "true" - the host or plug-in can support Metal render */ #define kOfxImageEffectPropMetalRenderSupported "OfxImageEffectPropMetalRenderSupported" /** @brief Indicates that a plug-in SHOULD use Metal render in the current action If a plug-in and host have both set kOfxImageEffectPropMetalRenderSupported="true" then the host MAY set this property to indicate that it is passing images as Metal buffers. - Type - int X 1 - Property Set - inArgs property set of the following actions... - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender - Valid Values - 0 indicates that the kOfxImagePropData of each image of each clip is a CPU memory pointer. - 1 indicates that the kOfxImagePropData of each image of each clip is a Metal id. */ #define kOfxImageEffectPropMetalEnabled "OfxImageEffectPropMetalEnabled" /** @brief The command queue of Metal render - Type - pointer X 1 - Property Set - inArgs property set of the following actions... - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender This property contains a pointer to the command queue to be used for Metal rendering (id). In order to use it, reinterpret_cast>(pointer) is needed. The plug-in SHOULD ensure that its render action enqueues any asynchronous Metal operations onto the supplied queue. The plug-in SHOULD NOT wait for final asynchronous operations to complete before returning from the render action. */ #define kOfxImageEffectPropMetalCommandQueue "OfxImageEffectPropMetalCommandQueue" /** @}*/ // end MetalRender doc group /** * @defgroup OpenClRender OpenCL Rendering * @version OpenCL rendering was added in version 1.5. * @{ */ /** @brief Indicates whether a host or plug-in can support OpenCL Buffers render - Type - string X 1 - Property Set - plug-in descriptor (read/write), host descriptor (read only) - Default - "false" for a plug-in - Valid Values - This must be one of - "false" - the host or plug-in does not support OpenCL Buffers render - "true" - the host or plug-in can support OpenCL Buffers render */ #define kOfxImageEffectPropOpenCLRenderSupported "OfxImageEffectPropOpenCLRenderSupported" /** @brief Indicates whether a host or plug-in can support OpenCL Images render - Type - string X 1 - Property Set - plug-in descriptor (read/write), host descriptor (read only) - Default - "false" for a plug-in - Valid Values - This must be one of - "false" - in which case the host or plug-in does not support OpenCL Images render - "true" - which means a host or plug-in can support OpenCL Images render */ #define kOfxImageEffectPropOpenCLSupported "OfxImageEffectPropOpenCLSupported" /** @brief Indicates that a plug-in SHOULD use OpenCL render in the current action If a plug-in and host have both set kOfxImageEffectPropOpenCLRenderSupported="true" or have both set kOfxImageEffectPropOpenCLSupported="true" then the host MAY set this property to indicate that it is passing images as OpenCL Buffers or Images. When rendering using OpenCL Buffers, the cl_mem of the buffers are retrieved using ::kOfxImagePropData. When rendering using OpenCL Images, the cl_mem of the images are retrieved using ::kOfxImageEffectPropOpenCLImage. If both ::kOfxImageEffectPropOpenCLSupported (Buffers) and ::kOfxImageEffectPropOpenCLRenderSupported (Images) are enabled by the plug-in, it should use ::kOfxImageEffectPropOpenCLImage to determine which is being used by the host. - Type - int X 1 - Property Set - inArgs property set of the following actions... - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender - Valid Values - 0 indicates that a plug-in SHOULD use OpenCL render in the render action - 1 indicates that a plug-in SHOULD NOT use OpenCL render in the render action */ #define kOfxImageEffectPropOpenCLEnabled "OfxImageEffectPropOpenCLEnabled" /** @brief Indicates the OpenCL command queue that should be used for rendering - Type - pointer X 1 - Property Set - inArgs property set of the following actions... - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender This property contains a pointer to the command queue to be used for OpenCL rendering (cl_command_queue). In order to use it, reinterpret_cast(pointer) is needed. The plug-in SHOULD ensure that its render action enqueues any asynchronous OpenCL operations onto the supplied queue. The plug-in SHOULD NOT wait for final asynchronous operations to complete before returning from the render action. */ #define kOfxImageEffectPropOpenCLCommandQueue "OfxImageEffectPropOpenCLCommandQueue" /** @brief Indicates the image handle of an image supplied as an OpenCL Image by the host - Type - pointer X 1 - Property Set - image handle returned by clipGetImage This value should be cast to a cl_mem and used as the image handle when performing OpenCL Images operations. The property should be used (not ::kOfxImagePropData) when rendering with OpenCL Images (::kOfxImageEffectPropOpenCLSupported), and should be used to determine whether Images or Buffers should be used if a plug-in supports both ::kOfxImageEffectPropOpenCLSupported and ::kOfxImageEffectPropOpenCLRenderSupported. Note: the kOfxImagePropRowBytes property is not required to be set by the host, since OpenCL Images do not have the concept of row bytes. */ #define kOfxImageEffectPropOpenCLImage "OfxImageEffectPropOpenCLImage" #define kOfxOpenCLProgramSuite "OfxOpenCLProgramSuite" /** @brief OFX suite that allows a plug-in to get OpenCL programs compiled This is an optional suite the host can provide for building OpenCL programs for the plug-in, as an alternative to calling clCreateProgramWithSource / clBuildProgram. There are two advantages to doing this: The host can add flags (such as -cl-denorms-are-zero) to the build call, and may also cache program binaries for performance (however, if the source of the program or the OpenCL environment changes, the host must recompile so some mechanism such as hashing must be used). */ typedef struct OfxOpenCLProgramSuiteV1 { /** @brief Compiles the OpenCL program */ OfxStatus(*compileProgram)(const char *pszProgramSource, int fOptional, // if non-zero, host may skip compiling on this call void *pResult); // cast to cl_program* } OfxOpenCLProgramSuiteV1; /** @page ofxOpenCLRender OpenCL Acceleration of Rendering @section ofxOpenCLRenderIntro Introduction The OpenCL extension enables plug-ins to use OpenCL commands (typically backed by a GPU) to accelerate rendering of their outputs. The basic scheme is simple.... - an plug-in indicates it wants to use OpenCL acceleration by setting the ::kOfxImageEffectPropOpenCLSupported (Images) and/or ::kOfxImageEffectPropOpenCLRenderSupported (Buffers) flags on it's descriptor. - a host indicates it supports OpenCL acceleration by setting ::kOfxImageEffectPropOpenCLSupported (Images) and/or ::kOfxImageEffectPropOpenCLRenderSupported (Buffers) on it's descriptor. - the host decides when to use OpenCL, and sets the ::kOfxImageEffectPropOpenCLEnabled property on the BeginRender/Render/EndRender calls to indicate this. - when OpenCL Images are being used (::kOfxImageEffectPropOpenCLSupported) the clip image property ::kOfxImageEffectPropOpenCLImage will be set and non-null. - when OpenCL Buffers are being used (::kOfxImageEffectPropOpenCLRenderSupported) the clip image property ::kOfxImagePropData will be set and non-null. @section ofxOpenCLRenderDiscoveryAndEnabling Discovery and Enabling If a host supports OpenCL rendering then it flags with the string property ::kOfxImageEffectPropOpenCLSupported (Images) and/or ::kOfxImageEffectPropOpenCLRenderSupported (Buffers) on its descriptor property set. Effects that cannot run without OpenCL support should examine this in ::kOfxActionDescribe action and return a ::kOfxStatErrMissingHostFeature status flag if it is not set to "true". Effects flag to a host that they support OpenCL rendering by setting the string property ::kOfxImageEffectPropOpenCLSupported (Images) and/or ::kOfxImageEffectPropOpenCLRenderSupported (Buffers) on their effect descriptor during the ::kOfxActionDescribe action. Effects can work in two ways.... - purely on CPUs without any OpenCL support at all, in which case they should set ::kOfxImageEffectPropOpenCLSupported (Images) and ::kOfxImageEffectPropOpenCLRenderSupported (Buffers) to be "false" (the default), - on CPUs but with optional OpenCL support, in which case they should set ::kOfxImageEffectPropOpenCLSupported (Images) and/or ::kOfxImageEffectPropOpenCLRenderSupported (Buffers) to be "true" Host may support just OpenCL Images, just OpenCL Buffers, or both, as indicated by which of these two properties they set "true". Likewise plug-ins may support just OpenCL Images, just OpenCL Buffers, or both, as indicated by which of these two properties they set "true". If both host and plug-in support both, it is up to the host which it uses. Typically, it will be based on what it uses natively (to avoid an extra copy operation). If a plug-in supports both, it must use ::kOfxImageEffectPropOpenCLImage to determine if Images or Buffers are being used for a given render action. Effects can use OpenCL render only during the following action: - ::kOfxImageEffectActionRender If a plug-in has indicated that it optionally supports OpenCL acceleration, it should check the property ::kOfxImageEffectPropOpenCLEnabled passed as an in argument to the following actions, - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender If this property is set to 0, then it must not attempt to use OpenCL while rendering. If this property is set to 1, then it must use OpenCL buffers or images while rendering. If a call using OpenCL rendering fails, the host may re-attempt using CPU buffers instead, but this is not required, and might not be efficient. @section ofxOpenCLRenderVersion OpenCL platform and device versions and feature support Assume an in-order command queue. Do not assume a profiling command queue. Effects should target OpenCL 1.1 API and OpenCL C kernel language support. Only minimum required features required in OpenCL 1.1 should be used (for example, see "5.3.2.1 Minimum List of Supported Image Formats" for the list of image types which can be expected to be supported across all devices). If you have specific requirements for features beyond these minimums, you will need to check the device (e.g., using clGetDeviceInfo with CL_DEVICE_EXTENSIONS) to see if your feature is available, and have a fallback if it's not. Temporary buffers and images should not be kept past the render action. A separate extension for host managed caching is in the works. Do not retain OpenCL objects without a matching release within the render action. @section ofxOpenCLRenderMultipleDevices Multiple OpenCL Devices This is very important: The host may support multiple OpenCL devices. Therefore the plug-in should keep a separate set of kernels per OpenCL content (e.g., using a map). The OpenCL context can be found from the command queue using clGetCommandQueueInfo with CL_QUEUE_CONTEXT. Failure to do this will cause crashes or incorrect results when the host switches to another OpenCL device. */ /** @}*/ // end OpenCLRender doc group #ifdef __cplusplus } #endif #endif /*__OFXGPURENDER_H__ */ mlt-7.38.0/src/modules/openfx/openfx/include/ofxImageEffect.h000664 000000 000000 00000236755 15172202314 024111 0ustar00rootroot000000 000000 #pragma once #ifndef _ofxImageEffect_h_ #define _ofxImageEffect_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #include "ofxCore.h" #include "ofxParam.h" #include "ofxInteract.h" #include "ofxMessage.h" #include "ofxMemory.h" #include "ofxMultiThread.h" #include "ofxInteract.h" #ifdef __cplusplus extern "C" { #endif /** @brief String used to label OFX Image Effect Plug-ins Set the pluginApi member of the OfxPluginHeader inside any OfxImageEffectPluginStruct to be this so that the host knows the plugin is an image effect. */ #define kOfxImageEffectPluginApi "OfxImageEffectPluginAPI" /** @brief The current version of the Image Effect API */ #define kOfxImageEffectPluginApiVersion 1 /** @brief Blind declaration of an OFX image effect */ typedef struct OfxImageEffectStruct *OfxImageEffectHandle; /** @brief Blind declaration of an OFX image effect */ typedef struct OfxImageClipStruct *OfxImageClipHandle; /** @brief Blind declaration for an handle to image memory returned by the image memory management routines */ typedef struct OfxImageMemoryStruct *OfxImageMemoryHandle; /** @brief String to label something with unset components */ #define kOfxImageComponentNone "OfxImageComponentNone" /** @brief String to label images with RGBA components */ #define kOfxImageComponentRGBA "OfxImageComponentRGBA" /** @brief String to label images with RGB components */ #define kOfxImageComponentRGB "OfxImageComponentRGB" /** @brief String to label images with only Alpha components */ #define kOfxImageComponentAlpha "OfxImageComponentAlpha" /** @brief Use to define the generator image effect context. See \ref ::kOfxImageEffectPropContext */ #define kOfxImageEffectContextGenerator "OfxImageEffectContextGenerator" /** @brief Use to define the filter effect image effect context See \ref ::kOfxImageEffectPropContext */ #define kOfxImageEffectContextFilter "OfxImageEffectContextFilter" /** @brief Use to define the transition image effect context See \ref ::kOfxImageEffectPropContext */ #define kOfxImageEffectContextTransition "OfxImageEffectContextTransition" /** @brief Use to define the paint image effect context See \ref ::kOfxImageEffectPropContext */ #define kOfxImageEffectContextPaint "OfxImageEffectContextPaint" /** @brief Use to define the general image effect context See \ref ::kOfxImageEffectPropContext */ #define kOfxImageEffectContextGeneral "OfxImageEffectContextGeneral" /** @brief Use to define the retimer effect context See \ref ::kOfxImageEffectPropContext */ #define kOfxImageEffectContextRetimer "OfxImageEffectContextRetimer" /** @brief Used as a value for ::kOfxPropType on image effect host handles */ #define kOfxTypeImageEffectHost "OfxTypeImageEffectHost" /** @brief Used as a value for ::kOfxPropType on image effect plugin handles */ #define kOfxTypeImageEffect "OfxTypeImageEffect" /** @brief Used as a value for ::kOfxPropType on image effect instance handles */ #define kOfxTypeImageEffectInstance "OfxTypeImageEffectInstance" /** @brief Used as a value for ::kOfxPropType on image effect clips */ #define kOfxTypeClip "OfxTypeClip" /** @brief Used as a value for ::kOfxPropType on image effect images */ #define kOfxTypeImage "OfxTypeImage" /** \addtogroup ActionsAll */ /*@{*/ /** \defgroup ImageEffectActions Image Effect Actions These are the list of actions passed to an image effect plugin's main function. For more details on how to deal with actions, see \ref ImageEffectActions. */ /*@{*/ /** @brief The region of definition for an image effect is the rectangular section of the 2D image plane that it is capable of filling, given the state of its input clips and parameters. This action is used to calculate the RoD for a plugin instance at a given frame. For more details on regions of definition see \ref ImageEffectArchitectures "Image Effect Architectures" Note that hosts that have constant sized imagery need not call this action. Only hosts that allow image sizes to vary need call this. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following properties - \ref kOfxPropTime the effect time for which a region of definition is being requested - \ref kOfxImageEffectPropRenderScale the render scale that should be used in any calculations in this action - \ref kOfxImageEffectPropThumbnailRender (optional) if the host considers this render a "thumbnail" @param outArgs has the following property which the plug-in may set: - \ref kOfxImageEffectPropRegionOfDefinition the calculated region of definition, initially set by the host to the default RoD (see below), in Canonical Coordinates. If the effect does not handle this action, the host should use the default RoD instead, which depends on the context. This is... - generator context - defaults to the project window, - filter and paint contexts - defaults to the RoD of the 'Source' input clip at the given time, - transition context - defaults to the union of the RoDs of the 'SourceFrom' and 'SourceTo' input clips at the given time, - general context - defaults to the union of the RoDs of all the non optional input clips and the 'Source' input clip (if it exists and it is connected) at the given time, if none exist, then it is the project window - retimer context - defaults to the union of the RoD of the 'Source' input clip at the frame directly preceding the value of the 'SourceTime' double parameter and the frame directly after it @returns - \ref kOfxStatOK the action was trapped and the RoD was set in the outArgs property set - \ref kOfxStatReplyDefault, the action was not trapped and the host should use the default values - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionGetRegionOfDefinition "OfxImageEffectActionGetRegionOfDefinition" /** @brief This action allows a host to ask an effect, given a region I want to render, what region do you need from each of your input clips. In that way, depending on the host architecture, a host can fetch the minimal amount of the image needed as input. Note that there is a region of interest to be set in ``outArgs`` for each input clip that exists on the effect. For more details see \ref ImageEffectArchitectures "Image Effect Architectures" The default RoI is simply the value passed in on the \ref kOfxImageEffectPropRegionOfInterest ``inArgs`` property set. The host must initialize all the RoIs in the ``outArgs`` property set to this value before the action is called. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following properties - \ref kOfxPropTime the effect time for which a region of definition is being requested - \ref kOfxImageEffectPropRenderScale the render scale that should be used in any calculations in this action - \ref kOfxImageEffectPropRegionOfInterest the region to be rendered in the output image, in Canonical Coordinates. - \ref kOfxImageEffectPropThumbnailRender (optional) if the host considers this render a "thumbnail" @param outArgs has a set of 4 dimensional double properties, one for each of the input clips to the effect. The properties are each named ``OfxImageClipPropRoI_`` with the clip name postpended, for example ``OfxImageClipPropRoI_Source``. These are initialised to the default RoI. @returns - \ref kOfxStatOK, the action was trapped and at least one RoI was set in the outArgs property set - \ref kOfxStatReplyDefault, the action was not trapped and the host should use the default values - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionGetRegionsOfInterest "OfxImageEffectActionGetRegionsOfInterest" /** @brief This action allows a host to ask an effect what range of frames it can produce images over. Only effects instantiated in the \ref generalContext "General Context" can have this called on them. In all other the host is in strict control over the temporal duration of the effect. The default is: - the union of all the frame ranges of the non optional input clips, - infinite if there are no non optional input clips. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is null @param outArgs has the following property - \ref kOfxImageEffectPropFrameRange the frame range an effect can produce images for \pre - \ref kOfxActionCreateInstance has been called on the instance - the effect instance has been created in the general effect context @returns - \ref kOfxStatOK, the action was trapped and the \ref kOfxImageEffectPropFrameRange was set in the outArgs property set - \ref kOfxStatReplyDefault, the action was not trapped and the host should use the default value - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionGetTimeDomain "OfxImageEffectActionGetTimeDomain" /** @brief This action lets the host ask the effect what frames are needed from each input clip to process a given frame. For example a temporal based degrainer may need several frames around the frame to render to do its work. This action need only ever be called if the plugin has set the \ref kOfxImageEffectPropTemporalClipAccess property on the plugin descriptor to be true. Otherwise the host assumes that the only frame needed from the inputs is the current one and this action is not called. Note that each clip can have it's required frame range specified, and that you can specify discontinuous sets of ranges for each clip, for example \code{.cpp} // The effect always needs the initial frame of the source as well as the previous and current frame double rangeSource[4]; // required ranges on the source rangeSource[0] = 0; // we always need frame 0 of the source rangeSource[1] = 0; rangeSource[2] = currentFrame - 1; // we also need the previous and current frame on the source rangeSource[3] = currentFrame; gPropHost->propSetDoubleN(outArgs, "OfxImageClipPropFrameRange_Source", 4, rangeSource); \endcode Which sets two discontinuous range of frames from the 'Source' clip required as input. The default frame range is simply the single frame, kOfxPropTime..kOfxPropTime, found on the ``inArgs`` property set. All the frame ranges in the ``outArgs`` property set must initialised to this value before the action is called. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following property - \ref kOfxPropTime the effect time for which we need to calculate the frames needed on input - \ref kOfxImageEffectPropThumbnailRender (optional) if the host considers this render a "thumbnail" @param outArgs has a set of properties, one for each input clip, named ``OfxImageClipPropFrameRange_`` with the name of the clip post-pended. For example ``OfxImageClipPropFrameRange_Source``. All these properties are multi-dimensional doubles, with the dimension is a multiple of two. Each pair of values indicates a continuous range of frames that is needed on the given input. They are all initialised to the default value. @returns - \ref kOfxStatOK, the action was trapped and at least one frame range in the outArgs property set - \ref kOfxStatReplyDefault, the action was not trapped and the host should use the default values - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionGetFramesNeeded "OfxImageEffectActionGetFramesNeeded" /** @brief This action allows a plugin to dynamically specify its preferences for input and output clips. Please see \ref ImageEffectClipPreferences "Image Effect Clip Preferences" for more details on the behaviour. Clip preferences are constant for the duration of an effect, so this action need only be called once per clip, not once per frame. This should be called once after creation of an instance, each time an input clip is changed, and whenever a parameter named in the \ref kOfxImageEffectPropClipPreferencesSlaveParam has its value changed. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs is redundant and is set to NULL @param outArgs has the following properties which the plugin can set - a set of char \* X 1 properties, one for each of the input clips currently attached and the output clip, labelled with ``OfxImageClipPropComponents_`` post pended with the clip's name. This must be set to one of the component types which the host supports and the effect stated it can accept on that input - a set of char \* X 1 properties, one for each of the input clips currently attached and the output clip, labelled with ``OfxImageClipPropDepth_`` post pended with the clip's name. This must be set to one of the pixel depths both the host and plugin supports - a set of char \* X 1 properties, one for each of the input clips currently attached, labelled with ``OfxImageClipPropPreferredColourspaces_`` post pended with the clip's name. This must be set according to the requirements of the colour management style in use. - a set of double X 1 properties, one for each of the input clips currently attached and the output clip, labelled with ``OfxImageClipPropPAR_`` post pended with the clip's name. This is the pixel aspect ratio of the input and output clips. This must be set to a positive non zero double value, - \ref kOfxImageEffectPropFrameRate the frame rate of the output clip, this must be set to a positive non zero double value - \ref kOfxImageClipPropFieldOrder the fielding of the output clip - \ref kOfxImageEffectPropPreMultiplication the premultiplication of the output clip - \ref kOfxImageClipPropContinuousSamples whether the output clip can produce different images at non-frame intervals, defaults to false, - \ref kOfxImageEffectFrameVarying whether the output clip can produces different images at different times, even if all parameters and inputs are constant, defaults to false. - \ref kOfxImageEffectPropThumbnailRender (optional) if the host considers this render a "thumbnail" @returns - \ref kOfxStatOK, the action was trapped and at least one of the properties in the outArgs was changed from its default value - \ref kOfxStatReplyDefault, the action was not trapped and the host should use the default values - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionGetClipPreferences "OfxImageEffectActionGetClipPreferences" /** @brief Sometimes an effect can pass through an input uprocessed, for example a blur effect with a blur size of 0. This action can be called by a host before it attempts to render an effect to determine if it can simply copy input directly to output without having to call the render action on the effect. If the effect does not need to process any pixels, it should set the value of the \ref kOfxPropName to the clip that the host should us as the output instead, and the \ref kOfxPropTime property on ``outArgs`` to be the time at which the frame should be fetched from a clip. The default action is to call the render action on the effect. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following properties - \ref kOfxPropTime the time at which to test for identity - \ref kOfxImageEffectPropFieldToRender the field to test for identity - \ref kOfxImageEffectPropRenderWindow the window (in \\ref PixelCoordinates) to test for identity under - \ref kOfxImageEffectPropRenderScale the scale factor being applied to the images being rendered - \ref kOfxImageEffectPropThumbnailRender (optional) if the host considers this render a "thumbnail" @param outArgs has the following properties which the plugin can set - \ref kOfxPropName this to the name of the clip that should be used if the effect is an identity transform, defaults to the empty string - \ref kOfxPropTime the time to use from the indicated source clip as an identity image (allowing time slips to happen), defaults to the value in \ref kOfxPropTime in inArgs @returns - \ref kOfxStatOK, the action was trapped and the effect should not have its render action called, the values in outArgs indicate what frame from which clip to use instead - \ref kOfxStatReplyDefault, the action was not trapped and the host should call the render action - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionIsIdentity "OfxImageEffectActionIsIdentity" /** @brief This action is where an effect gets to push pixels and turn its input clips and parameter set into an output image. This is possibly quite complicated and covered in the \ref RenderingEffects "Rendering Image Effects" chapter. The render action *must* be trapped by the plug-in, it cannot return \ref kOfxStatReplyDefault. The pixels needs be pushed I'm afraid. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following properties - \ref kOfxPropTime the time at which to render - \ref kOfxImageEffectPropFieldToRender the field to render - \ref kOfxImageEffectPropRenderWindow the window (in \\ref PixelCoordinates) to render - \ref kOfxImageEffectPropRenderScale the scale factor being applied to the images being rendered - \ref kOfxImageEffectPropSequentialRenderStatus whether the effect is currently being rendered in strict frame order on a single instance - \ref kOfxImageEffectPropInteractiveRenderStatus if the render is in response to a user modifying the effect in an interactive session - \ref kOfxImageEffectPropRenderQualityDraft if the render should be done in draft mode (e.g. for faster scrubbing) - \ref kOfxImageEffectPropNoSpatialAwareness if the plugin must render without spatial awareness (e.g. for LUT generation) - \ref kOfxImageEffectPropThumbnailRender (optional) if the host considers this render a "thumbnail" @param outArgs is redundant and should be set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance - \ref kOfxImageEffectActionBeginSequenceRender has been called on the instance \post - \ref kOfxImageEffectActionEndSequenceRender action will be called on the instance @returns - \ref kOfxStatOK, the effect rendered happily - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionRender "OfxImageEffectActionRender" /** @brief This action is passed to an image effect before it renders a range of frames. It is there to allow an effect to set things up for a long sequence of frames. Note that this is still called, even if only a single frame is being rendered in an interactive environment. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following properties - \ref kOfxImageEffectPropFrameRange the range of frames (inclusive) that will be rendered - \ref kOfxImageEffectPropFrameStep what is the step between frames, generally set to 1 (for full frame renders) or 0.5 (for fielded renders) - \ref kOfxPropIsInteractive is this a single frame render due to user interaction in a GUI, or a proper full sequence render. - \ref kOfxImageEffectPropRenderScale the scale factor to apply to images for this call - \ref kOfxImageEffectPropSequentialRenderStatus whether the effect is currently being rendered in strict frame order on a single instance - \ref kOfxImageEffectPropInteractiveRenderStatus if the render is in response to a user modifying the effect in an interactive session - \ref kOfxImageEffectPropThumbnailRender (optional) if the host considers this render a "thumbnail" @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance \post - \ref kOfxImageEffectActionRender action will be called at least once on the instance - \ref kOfxImageEffectActionEndSequenceRender action will be called on the instance @returns - \ref kOfxStatOK, the action was trapped and handled cleanly by the effect, - \ref kOfxStatReplyDefault, the action was not trapped, but all is well anyway, - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge, - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message, - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionBeginSequenceRender "OfxImageEffectActionBeginSequenceRender" /** @brief This action is passed to an image effect after is has rendered a range of frames. It is there to allow an effect to free resources after a long sequence of frame renders. Note that this is still called, even if only a single frame is being rendered in an interactive environment. @param handle handle to the instance, cast to an \ref OfxImageEffectHandle @param inArgs has the following properties - \ref kOfxImageEffectPropFrameRange the range of frames (inclusive) that will be rendered - \ref kOfxImageEffectPropFrameStep what is the step between frames, generally set to 1 (for full frame renders) or 0.5 (for fielded renders), - \ref kOfxPropIsInteractive is this a single frame render due to user interaction in a GUI, or a proper full sequence render. - \ref kOfxImageEffectPropRenderScale the scale factor to apply to images for this call - \ref kOfxImageEffectPropSequentialRenderStatus whether the effect is currently being rendered in strict frame order on a single instance - \ref kOfxImageEffectPropInteractiveRenderStatus if the render is in response to a user modifying the effect in an interactive session - \ref kOfxImageEffectPropThumbnailRender (optional) if the host considers this render a "thumbnail" @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance - \ref kOfxImageEffectActionEndSequenceRender action was called on the instance - \ref kOfxImageEffectActionRender action was called at least once on the instance @returns - \ref kOfxStatOK, the action was trapped and handled cleanly by the effect, - \ref kOfxStatReplyDefault, the action was not trapped, but all is well anyway, - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge, - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message, - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionEndSequenceRender "OfxImageEffectActionEndSequenceRender" /** @brief This action is unique to OFX Image Effect plug-ins. Because a plugin is able to exhibit different behaviour depending on the context of use, each separate context will need to be described individually. It is within this action that image effects describe which parameters and input clips it requires. This action will be called multiple times, one for each of the contexts the plugin says it is capable of implementing. If a host does not support a certain context, then it need not call \ref kOfxImageEffectActionDescribeInContext for that context. This action *must* be trapped, it is not optional. @param handle handle to the context descriptor, cast to an \ref OfxImageEffectHandle this may or may not be the same as passed to \ref kOfxActionDescribe @param inArgs has the following property: - \ref kOfxImageEffectPropContext the context being described @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionDescribe has been called on the descriptor handle, - \ref kOfxActionCreateInstance has not been called @returns - \ref kOfxStatOK, the action was trapped and all was well - \ref kOfxStatErrMissingHostFeature, in which the context will be ignored by the host, the plugin may post a message - \ref kOfxStatErrMemory, in which case the action may be called again after a memory purge - \ref kOfxStatFailed, something wrong, but no error code appropriate, plugin to post message - \ref kOfxStatErrFatal */ #define kOfxImageEffectActionDescribeInContext "OfxImageEffectActionDescribeInContext" /*@}*/ /*@}*/ /** \addtogroup PropertiesAll */ /*@{*/ /** \defgroup ImageEffectPropDefines Image Effect Property Definitions These are the list of properties used by the Image Effects API. */ /*@{*/ /** @brief Indicates to the host the contexts a plugin can be used in. - Type - string X N - Property Set - image effect descriptor passed to kOfxActionDescribe (read/write) - Default - this has no defaults, it must be set - Valid Values - This must be one of - ::kOfxImageEffectContextGenerator - ::kOfxImageEffectContextFilter - ::kOfxImageEffectContextTransition - ::kOfxImageEffectContextPaint - ::kOfxImageEffectContextGeneral - ::kOfxImageEffectContextRetimer */ #define kOfxImageEffectPropSupportedContexts "OfxImageEffectPropSupportedContexts" /** @brief The plugin handle passed to the initial 'describe' action. - Type - pointer X 1 - Property Set - plugin instance, (read only) This value will be the same for all instances of a plugin. */ #define kOfxImageEffectPropPluginHandle "OfxImageEffectPropPluginHandle" /** @brief Indicates if a host is a background render. - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - This must be one of - 0 if the host is a foreground host, it may open the effect in an interactive session (or not) - 1 if the host is a background 'processing only' host, and the effect will never be opened in an interactive session. */ #define kOfxImageEffectHostPropIsBackground "OfxImageEffectHostPropIsBackground" /** @brief Indicates whether only one instance of a plugin can exist at the same time - Type - int X 1 - Property Set - plugin descriptor (read/write) - Default - 0 - Valid Values - This must be one of - 0 - which means multiple instances can exist simultaneously, - 1 - which means only one instance can exist at any one time. Some plugins, for whatever reason, may only be able to have a single instance in existence at any one time. This plugin property is used to indicate that. */ #define kOfxImageEffectPluginPropSingleInstance "OfxImageEffectPluginPropSingleInstance" /** @brief Indicates how many simultaneous renders the plugin can deal with. - Type - string X 1 - Property Set - plugin descriptor (read/write) - Default - ::kOfxImageEffectRenderInstanceSafe - Valid Values - This must be one of - ::kOfxImageEffectRenderUnsafe - indicating that only a single 'render' call can be made at any time among all instances, - ::kOfxImageEffectRenderInstanceSafe - indicating that any instance can have a single 'render' call at any one time, - ::kOfxImageEffectRenderFullySafe - indicating that any instance of a plugin can have multiple renders running simultaneously */ #define kOfxImageEffectPluginRenderThreadSafety "OfxImageEffectPluginRenderThreadSafety" /** @brief String used to label render threads as un thread safe, see, \ref ::kOfxImageEffectPluginRenderThreadSafety */ #define kOfxImageEffectRenderUnsafe "OfxImageEffectRenderUnsafe" /** @brief String used to label render threads as instance thread safe, \ref ::kOfxImageEffectPluginRenderThreadSafety */ #define kOfxImageEffectRenderInstanceSafe "OfxImageEffectRenderInstanceSafe" /** @brief String used to label render threads as fully thread safe, \ref ::kOfxImageEffectPluginRenderThreadSafety */ #define kOfxImageEffectRenderFullySafe "OfxImageEffectRenderFullySafe" /** @brief Indicates whether a plugin lets the host perform per frame SMP threading - Type - int X 1 - Property Set - plugin descriptor (read/write) - Default - 1 - Valid Values - This must be one of - 0 - which means that the plugin will perform any per frame SMP threading - 1 - which means the host can call an instance's render function simultaneously at the same frame, but with different windows to render. */ #define kOfxImageEffectPluginPropHostFrameThreading "OfxImageEffectPluginPropHostFrameThreading" /** @brief Indicates whether a host or plugin can support clips of differing component depths going into/out of an effect - Type - int X 1 - Property Set - plugin descriptor (read/write), host descriptor (read only) - Default - 0 for a plugin - Valid Values - This must be one of - 0 - in which case the host or plugin does not support clips of multiple pixel depths, - 1 - which means a host or plugin is able to to deal with clips of multiple pixel depths, If a host indicates that it can support multiple pixels depths, then it will allow the plugin to explicitly set the output clip's pixel depth in the ::kOfxImageEffectActionGetClipPreferences action. See \ref ImageEffectClipPreferences. */ #define kOfxImageEffectPropSupportsMultipleClipDepths "OfxImageEffectPropMultipleClipDepths" /** @brief Indicates whether a host or plugin can support clips of differing pixel aspect ratios going into/out of an effect - Type - int X 1 - Property Set - plugin descriptor (read/write), host descriptor (read only) - Default - 0 for a plugin - Valid Values - This must be one of - 0 - in which case the host or plugin does not support clips of multiple pixel aspect ratios - 1 - which means a host or plugin is able to to deal with clips of multiple pixel aspect ratios If a host indicates that it can support multiple pixel aspect ratios, then it will allow the plugin to explicitly set the output clip's aspect ratio in the ::kOfxImageEffectActionGetClipPreferences action. See \ref ImageEffectClipPreferences. */ #define kOfxImageEffectPropSupportsMultipleClipPARs "OfxImageEffectPropSupportsMultipleClipPARs" /** @brief Indicates the set of parameters on which a value change will trigger a change to clip preferences - Type - string X N - Property Set - plugin descriptor (read/write) - Default - none set - Valid Values - the name of any described parameter The plugin uses this to inform the host of the subset of parameters that affect the effect's clip preferences. A value change in any one of these will trigger a call to the clip preferences action. The plugin can be slaved to multiple parameters (setting index 0, then index 1 etc...) */ #define kOfxImageEffectPropClipPreferencesSlaveParam "OfxImageEffectPropClipPreferencesSlaveParam" /** @brief Indicates whether the host will let a plugin set the frame rate of the output clip. - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - This must be one of - 0 - in which case the plugin may not change the frame rate of the output clip, - 1 - which means a plugin is able to change the output clip's frame rate in the ::kOfxImageEffectActionGetClipPreferences action. See \ref ImageEffectClipPreferences. If a clip can be continuously sampled, the frame rate will be set to 0. */ #define kOfxImageEffectPropSetableFrameRate "OfxImageEffectPropSetableFrameRate" /** @brief Indicates whether the host will let a plugin set the fielding of the output clip. - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - This must be one of - 0 - in which case the plugin may not change the fielding of the output clip, - 1 - which means a plugin is able to change the output clip's fielding in the ::kOfxImageEffectActionGetClipPreferences action. See \ref ImageEffectClipPreferences. */ #define kOfxImageEffectPropSetableFielding "OfxImageEffectPropSetableFielding" /** @brief Indicates whether a plugin needs sequential rendering, and a host support it - Type - int X 1 - Property Set - plugin descriptor (read/write) or plugin instance (read/write), and host descriptor (read only) - Default - 0 - Valid Values - - 0 - for a plugin, indicates that a plugin does not need to be sequentially rendered to be correct, for a host, indicates that it cannot ever guarantee sequential rendering, - 1 - for a plugin, indicates that it needs to be sequentially rendered to be correct, for a host, indicates that it can always support sequential rendering of plugins that are sequentially rendered, - 2 - for a plugin, indicates that it is best to render sequentially, but will still produce correct results if not, for a host, indicates that it can sometimes render sequentially, and will have set ::kOfxImageEffectPropSequentialRenderStatus on the relevant actions Some effects have temporal dependencies, some information from from the rendering of frame N-1 is needed to render frame N correctly. This property is set by an effect to indicate such a situation. Also, some effects are more efficient if they run sequentially, but can still render correct images even if they do not, eg: a complex particle system. During an interactive session a host may attempt to render a frame out of sequence (for example when the user scrubs the current time), and the effect needs to deal with such a situation as best it can to provide feedback to the user. However if a host caches output, any frame frame generated in random temporal order needs to be considered invalid and needs to be re-rendered when the host finally performs a first to last render of the output sequence. In all cases, a host will set the kOfxImageEffectPropSequentialRenderStatus flag to indicate its sequential render status. */ #define kOfxImageEffectInstancePropSequentialRender "OfxImageEffectInstancePropSequentialRender" /** @brief Property on all the render action that indicate the current sequential render status of a host - Type - int X 1 - Property Set - read only property on the inArgs of the following actions... - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionEndSequenceRender - Valid Values - - 0 - the host is not currently sequentially rendering, - 1 - the host is currentely rendering in a way so that it guarantees sequential rendering. This property is set to indicate whether the effect is currently being rendered in frame order on a single effect instance. See ::kOfxImageEffectInstancePropSequentialRender for more details on sequential rendering. */ #define kOfxImageEffectPropSequentialRenderStatus "OfxImageEffectPropSequentialRenderStatus" #define kOfxHostNativeOriginBottomLeft "kOfxImageEffectHostPropNativeOriginBottomLeft" #define kOfxHostNativeOriginTopLeft "kOfxImageEffectHostPropNativeOriginTopLeft" #define kOfxHostNativeOriginCenter "kOfxImageEffectHostPropNativeOriginCenter" /** @brief Property that indicates the host native UI space - this is only a UI hint, has no impact on pixel processing - Type - UTF8 string X 1 - Property Set - read only property (host) - Valid Values - "kOfxImageEffectHostPropNativeOriginBottomLeft" - 0,0 bottom left "kOfxImageEffectHostPropNativeOriginTopLeft" - 0,0 top left "kOfxImageEffectHostPropNativeOriginCenter" - 0,0 center (screen space) This property is set to kOfxHostNativeOriginBottomLeft pre V1.4 and was to be discovered by plug-ins. This is useful for drawing overlay for points... so everything matches the rest of the app (for example expression linking to other tools, or simply match the reported location of the host viewer). */ #define kOfxImageEffectHostPropNativeOrigin "OfxImageEffectHostPropNativeOrigin" /** @brief Property that indicates if a plugin is being rendered in response to user interaction. - Type - int X 1 - Property Set - read only property on the inArgs of the following actions... - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionEndSequenceRender - Valid Values - - 0 - the host is rendering the instance due to some reason other than an interactive tweak on a UI, - 1 - the instance is being rendered because a user is modifying parameters in an interactive session. This property is set to 1 on all render calls that have been triggered because a user is actively modifying an effect (or up stream effect) in an interactive session. This typically means that the effect is not being rendered as a part of a sequence, but as a single frame. */ #define kOfxImageEffectPropInteractiveRenderStatus "OfxImageEffectPropInteractiveRenderStatus" /** @brief Indicates the effect group for this plugin. - Type - UTF8 string X 1 - Property Set - plugin descriptor (read/write) - Default - "" This is purely a user interface hint for the host so it can group related effects on any menus it may have. */ #define kOfxImageEffectPluginPropGrouping "OfxImageEffectPluginPropGrouping" /** @brief Indicates whether a host support image effect \ref ImageEffectOverlays. - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - This must be one of - 0 - the host won't allow a plugin to draw a GUI over the output image, - 1 - the host will allow a plugin to draw a GUI over the output image. */ #define kOfxImageEffectPropSupportsOverlays "OfxImageEffectPropSupportsOverlays" /** @brief Sets the entry for an effect's overlay interaction - Type - pointer X 1 - Property Set - plugin descriptor (read/write) - Default - NULL - Valid Values - must point to an ::OfxPluginEntryPoint The entry point pointed to must be one that handles custom interaction actions. */ #define kOfxImageEffectPluginPropOverlayInteractV1 "OfxImageEffectPluginPropOverlayInteractV1" /** @brief Sets the entry for an effect's overlay interaction. Unlike kOfxImageEffectPluginPropOverlayInteractV1, the overlay interact in the plug-in is expected to implement the kOfxInteractActionDraw using the OfxDrawSuiteV1. - Type - pointer X 1 - Property Set - plugin descriptor (read/write) - Default - NULL - Valid Values - must point to an ::OfxPluginEntryPoint The entry point pointed to must be one that handles custom interaction actions. */ #define kOfxImageEffectPluginPropOverlayInteractV2 "OfxImageEffectPluginPropOverlayInteractV2" /** @brief Indicates whether a plugin or host support multiple resolution images. - Type - int X 1 - Property Set - host descriptor (read only), plugin descriptor (read/write) - Default - 1 for plugins - Valid Values - This must be one of - 0 - the plugin or host does not support multiple resolutions - 1 - the plugin or host does support multiple resolutions Multiple resolution images mean... - input and output images can be of any size - input and output images can be offset from the origin */ #define kOfxImageEffectPropSupportsMultiResolution "OfxImageEffectPropSupportsMultiResolution" /** @brief Indicates whether a clip, plugin or host supports tiled images - Type - int X 1 - Property Set - host descriptor (read only), plugin descriptor (read/write), clip descriptor (read/write), instance (read/write) - Default - to 1 for a plugin and clip - Valid Values - This must be one of 0 or 1 Tiled images mean that input or output images can contain pixel data that is only a subset of their full RoD. If a clip or plugin does not support tiled images, then the host should supply full RoD images to the effect whenever it fetches one. V1.4: It is now possible (defined) to change OfxImageEffectPropSupportsTiles in Instance Changed */ #define kOfxImageEffectPropSupportsTiles "OfxImageEffectPropSupportsTiles" /** @brief Indicates support for random temporal access to images in a clip. - Type - int X 1 - Property Set - host descriptor (read only), plugin descriptor (read/write), clip descriptor (read/write) - Default - to 0 for a plugin and clip - Valid Values - This must be one of 0 or 1 On a host, it indicates whether the host supports temporal access to images. On a plugin, indicates if the plugin needs temporal access to images. On a clip, it indicates that the clip needs temporal access to images. */ #define kOfxImageEffectPropTemporalClipAccess "OfxImageEffectPropTemporalClipAccess" /** @brief Indicates the context a plugin instance has been created for. - Type - string X 1 - Property Set - image effect instance (read only) - Valid Values - This must be one of - ::kOfxImageEffectContextGenerator - ::kOfxImageEffectContextFilter - ::kOfxImageEffectContextTransition - ::kOfxImageEffectContextPaint - ::kOfxImageEffectContextGeneral - ::kOfxImageEffectContextRetimer */ #define kOfxImageEffectPropContext "OfxImageEffectPropContext" /** @brief Indicates the type of each component in a clip or image (after any mapping) - Type - string X 1 - Property Set - clip instance (read only), image instance (read only) - Valid Values - This must be one of - kOfxBitDepthNone (implying a clip is unconnected, not valid for an image) - kOfxBitDepthByte - kOfxBitDepthShort - kOfxBitDepthHalf - kOfxBitDepthFloat Note that for a clip, this is the value set by the clip preferences action, not the raw 'actual' value of the clip. */ #define kOfxImageEffectPropPixelDepth "OfxImageEffectPropPixelDepth" /** @brief Indicates the current component type in a clip or image (after any mapping) - Type - string X 1 - Property Set - clip instance (read only), image instance (read only) - Valid Values - This must be one of - kOfxImageComponentNone (implying a clip is unconnected, not valid for an image) - kOfxImageComponentRGBA - kOfxImageComponentRGB - kOfxImageComponentAlpha Note that for a clip, this is the value set by the clip preferences action, not the raw 'actual' value of the clip. */ #define kOfxImageEffectPropComponents "OfxImageEffectPropComponents" /** @brief Uniquely labels an image - Type - ASCII string X 1 - Property Set - image instance (read only) This is host set and allows a plug-in to differentiate between images. This is especially useful if a plugin caches analysed information about the image (for example motion vectors). The plugin can label the cached information with this identifier. If a user connects a different clip to the analysed input, or the image has changed in some way then the plugin can detect this via an identifier change and re-evaluate the cached information. */ #define kOfxImagePropUniqueIdentifier "OfxImagePropUniqueIdentifier" /** @brief Clip and action argument property which indicates that the clip can be sampled continuously - Type - int X 1 - Property Set - clip instance (read only), as an out argument to ::kOfxImageEffectActionGetClipPreferences action (read/write) - Default - 0 as an out argument to the ::kOfxImageEffectActionGetClipPreferences action - Valid Values - This must be one of... - 0 if the images can only be sampled at discrete times (eg: the clip is a sequence of frames), - 1 if the images can only be sampled continuously (eg: the clip is in fact an animating roto spline and can be rendered anywhen). If this is set to true, then the frame rate of a clip is effectively infinite, so to stop arithmetic errors the frame rate should then be set to 0. */ #define kOfxImageClipPropContinuousSamples "OfxImageClipPropContinuousSamples" /** @brief Indicates the type of each component in a clip before any mapping by clip preferences - Type - string X 1 - Property Set - clip instance (read only) - Valid Values - This must be one of - kOfxBitDepthNone (implying a clip is unconnected image) - kOfxBitDepthByte - kOfxBitDepthShort - kOfxBitDepthHalf - kOfxBitDepthFloat This is the actual value of the component depth, before any mapping by clip preferences. */ #define kOfxImageClipPropUnmappedPixelDepth "OfxImageClipPropUnmappedPixelDepth" /** @brief Indicates the current 'raw' component type on a clip before any mapping by clip preferences - Type - string X 1 - Property Set - clip instance (read only), - Valid Values - This must be one of - kOfxImageComponentNone (implying a clip is unconnected) - kOfxImageComponentRGBA - kOfxImageComponentRGB - kOfxImageComponentAlpha */ #define kOfxImageClipPropUnmappedComponents "OfxImageClipPropUnmappedComponents" /** @brief Indicates the premultiplication state of a clip or image - Type - string X 1 - Property Set - clip instance (read only), image instance (read only), out args property in the ::kOfxImageEffectActionGetClipPreferences action (read/write) - Valid Values - This must be one of - kOfxImageOpaque - the image is opaque and so has no premultiplication state - kOfxImagePreMultiplied - the image is premultiplied by its alpha - kOfxImageUnPreMultiplied - the image is unpremultiplied See the documentation on clip preferences for more details on how this is used with the ::kOfxImageEffectActionGetClipPreferences action. */ #define kOfxImageEffectPropPreMultiplication "OfxImageEffectPropPreMultiplication" /** Used to flag the alpha of an image as opaque */ #define kOfxImageOpaque "OfxImageOpaque" /** Used to flag an image as premultiplied */ #define kOfxImagePreMultiplied "OfxImageAlphaPremultiplied" /** Used to flag an image as unpremultiplied */ #define kOfxImageUnPreMultiplied "OfxImageAlphaUnPremultiplied" /** @brief Indicates the bit depths support by a plug-in or host - Type - string X N - Property Set - host descriptor (read only), plugin descriptor (read/write) - Default - plugin descriptor none set - Valid Values - This must be one of - kOfxBitDepthNone (implying a clip is unconnected, not valid for an image) - kOfxBitDepthByte - kOfxBitDepthShort - kOfxBitDepthHalf - kOfxBitDepthFloat The default for a plugin is to have none set, the plugin \em must define at least one in its describe action. */ #define kOfxImageEffectPropSupportedPixelDepths "OfxImageEffectPropSupportedPixelDepths" /** @brief Indicates the components supported by a clip or host, - Type - string X N - Property Set - host descriptor (read only), clip descriptor (read/write) - Valid Values - This must be one of - kOfxImageComponentNone (implying a clip is unconnected) - kOfxImageComponentRGBA - kOfxImageComponentRGB - kOfxImageComponentAlpha This list of strings indicate what component types are supported by a host or are expected as input to a clip. The default for a clip descriptor is to have none set, the plugin \em must define at least one in its define function */ #define kOfxImageEffectPropSupportedComponents "OfxImageEffectPropSupportedComponents" /** @brief Indicates if a clip is optional. - Type - int X 1 - Property Set - clip descriptor (read/write) - Default - 0 - Valid Values - This must be one of 0 or 1 */ #define kOfxImageClipPropOptional "OfxImageClipPropOptional" /** @brief Indicates that a clip is intended to be used as a mask input - Type - int X 1 - Property Set - clip descriptor (read/write) - Default - 0 - Valid Values - This must be one of 0 or 1 Set this property on any clip which will only ever have single channel alpha images fetched from it. Typically on an optional clip such as a junk matte in a keyer. This property acts as a hint to hosts indicating that they could feed the effect from a rotoshape (or similar) rather than an 'ordinary' clip. */ #define kOfxImageClipPropIsMask "OfxImageClipPropIsMask" /** @brief The pixel aspect ratio of a clip or image. - Type - double X 1 - Property Set - clip instance (read only), image instance (read only) and ::kOfxImageEffectActionGetClipPreferences action out args property (read/write) */ #define kOfxImagePropPixelAspectRatio "OfxImagePropPixelAspectRatio" /** @brief The frame rate of a clip or instance's project. - Type - double X 1 - Property Set - clip instance (read only), effect instance (read only) and ::kOfxImageEffectActionGetClipPreferences action out args property (read/write) For an input clip this is the frame rate of the clip. For an output clip, the frame rate mapped via pixel preferences. For an instance, this is the frame rate of the project the effect is in. For the outargs property in the ::kOfxImageEffectActionGetClipPreferences action, it is used to change the frame rate of the output clip. */ #define kOfxImageEffectPropFrameRate "OfxImageEffectPropFrameRate" /** @brief Indicates the original unmapped frame rate (frames/second) of a clip - Type - double X 1 - Property Set - clip instance (read only), If a plugin changes the output frame rate in the pixel preferences action, this property allows a plugin to get to the original value. */ #define kOfxImageEffectPropUnmappedFrameRate "OfxImageEffectPropUnmappedFrameRate" /** @brief The frame step used for a sequence of renders - Type - double X 1 - Property Set - an in argument for the ::kOfxImageEffectActionBeginSequenceRender action (read only) - Valid Values - can be any positive value, but typically - 1 for frame based material - 0.5 for field based material */ #define kOfxImageEffectPropFrameStep "OfxImageEffectPropFrameStep" /** @brief The frame range over which a clip has images. - Type - double X 2 - Property Set - clip instance (read only) Dimension 0 is the first frame for which the clip can produce valid data. Dimension 1 is the last frame for which the clip can produce valid data. */ #define kOfxImageEffectPropFrameRange "OfxImageEffectPropFrameRange" /** @brief The unmaped frame range over which an output clip has images. - Type - double X 2 - Property Set - clip instance (read only) Dimension 0 is the first frame for which the clip can produce valid data. Dimension 1 is the last frame for which the clip can produce valid data. If a plugin changes the output frame rate in the pixel preferences action, it will affect the frame range of the output clip, this property allows a plugin to get to the original value. */ #define kOfxImageEffectPropUnmappedFrameRange "OfxImageEffectPropUnmappedFrameRange" /** @brief Says whether the clip is actually connected at the moment. - Type - int X 1 - Property Set - clip instance (read only) - Valid Values - This must be one of 0 or 1 An instance may have a clip may not be connected to an object that can produce image data. Use this to find out. Any clip that is not optional will \em always be connected during a render action. However, during interface actions, even non optional clips may be unconnected. */ #define kOfxImageClipPropConnected "OfxImageClipPropConnected" /** @brief Indicates whether an effect will generate different images from frame to frame. - Type - int X 1 - Property Set - out argument to ::kOfxImageEffectActionGetClipPreferences action (read/write). - Default - 0 - Valid Values - This must be one of 0 or 1 This property indicates whether a plugin will generate a different image from frame to frame, even if no parameters or input image changes. For example a generator that creates random noise pixel at each frame. */ #define kOfxImageEffectFrameVarying "OfxImageEffectFrameVarying" /** @brief The proxy render scale currently being applied. - Type - double X 2 - Property Set - an image instance (read only) and as read only an in argument on the following actions, - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionEndSequenceRender - ::kOfxImageEffectActionIsIdentity - ::kOfxImageEffectActionGetRegionOfDefinition - ::kOfxImageEffectActionGetRegionsOfInterest - ::kOfxActionInstanceChanged - ::kOfxInteractActionDraw - ::kOfxInteractActionPenMotion - ::kOfxInteractActionPenDown - ::kOfxInteractActionPenUp - ::kOfxInteractActionKeyDown - ::kOfxInteractActionKeyUp - ::kOfxInteractActionKeyRepeat - ::kOfxInteractActionGainFocus - ::kOfxInteractActionLoseFocus This should be applied to any spatial parameters to position them correctly. Not that the 'x' value does not include any pixel aspect ratios. */ #define kOfxImageEffectPropRenderScale "OfxImageEffectPropRenderScale" /** @brief Indicates whether an effect can take quality shortcuts to improve speed. - Type - int X 1 - Property Set - render calls, host (read-only) - Default - 0 - 0: Best Quality (1: Draft) - Valid Values - This must be one of 0 or 1 This property indicates that the host provides the plug-in the option to render in Draft/Preview mode. This is useful for applications that must support fast scrubbing. These allow a plug-in to take short-cuts for improved performance when the situation allows and it makes sense, for example to generate thumbnails with effects applied. For example switch to a cheaper interpolation type or rendering mode. A plugin should expect frames rendered in this manner that will not be stuck in host cache unless the cache is only used in the same draft situations. If an host does not support that property a value of 0 is assumed. Also note that some hosts do implement kOfxImageEffectPropRenderScale - these two properties can be used independently. */ #define kOfxImageEffectPropRenderQualityDraft "OfxImageEffectPropRenderQualityDraft" /** @brief Indicates that the plugin can render without spatial awareness, either inherently or by disabling certain parameters at render time. If the plugin descriptor has this property set to "true", the plugin is expected to disable spatial effects when the host sets this property to "true" in the arguments passed to kOfxImageEffectActionBeginSequenceRender and kOfxImageEffectActionRender. - Type - string X 1 - Property Set - plugin descriptor (read/write), render calls - host (read-only) - Default - "false" - Valid Values - This must be one of - "false" - the plugin cannot render without spatial awareness and the host should bypass it for renders that require no spatial awareness. - "true" - the plugin can render without spatial awareness. The host will indicate this type of render by setting kOfxImageEffectPropNoSpatialAwareness to "true" in the arguments passed to kOfxImageEffectActionBeginSequenceRender and kOfxImageEffectActionRender. @version added in version 1.5.1. */ #define kOfxImageEffectPropNoSpatialAwareness "OfxImageEffectPropNoSpatialAwareness" /** @brief Indicates whether the render is what the host considers a "thumbnail" render - Type - string X 1 - Property Set - read only optional property on the inArgs of the following actions... - ::kOfxImageEffectActionGetRegionOfDefinition - ::kOfxImageEffectActionGetRegionsOfInterest - ::kOfxImageEffectActionGetFramesNeeded - ::kOfxImageEffectActionGetClipPreferences - ::kOfxImageEffectActionIsIdentity - ::kOfxImageEffectActionBeginSequenceRender - ::kOfxImageEffectActionRender - ::kOfxImageEffectActionEndSequenceRender - Default - "false" - Valid Values - This must be one of - "false" - the host does not consider this render a thumbail - "true" - the host considers this render a thumbail This property indicates that the host considers the render to be a "thumbnail", defined as a low-resolution image for use in the host's user interface. A plugin could react to this property by - rendering a badge rather than a detailed effect unlikely to be visible in a thumbail - turning itself off in kOfxImageEffectActionIsIdentity - disabling temporal inputs in kOfxImageEffectActionGetFramesNeeded @version added in version 1.5.1. */ #define kOfxImageEffectPropThumbnailRender "OfxImageEffectPropThumbnailRender" /** @brief The extent of the current project in canonical coordinates. - Type - double X 2 - Property Set - a plugin instance (read only) The extent is the size of the 'output' for the current project. See \ref NormalisedCoordinateSystem for more information on the project extent. The extent is in canonical coordinates and only returns the top right position, as the extent is always rooted at 0,0. For example a PAL SD project would have an extent of 768, 576. */ #define kOfxImageEffectPropProjectExtent "OfxImageEffectPropProjectExtent" /** @brief The size of the current project in canonical coordinates. - Type - double X 2 - Property Set - a plugin instance (read only) The size of a project is a sub set of the ::kOfxImageEffectPropProjectExtent. For example a project may be a PAL SD project, but only be a letter-box within that. The project size is the size of this sub window. The project size is in canonical coordinates. See \ref NormalisedCoordinateSystem for more information on the project extent. */ #define kOfxImageEffectPropProjectSize "OfxImageEffectPropProjectSize" /** @brief The offset of the current project in canonical coordinates. - Type - double X 2 - Property Set - a plugin instance (read only) The offset is related to the ::kOfxImageEffectPropProjectSize and is the offset from the origin of the project 'subwindow'. For example for a PAL SD project that is in letterbox form, the project offset is the offset to the bottom left hand corner of the letter box. The project offset is in canonical coordinates. See \ref NormalisedCoordinateSystem for more information on the project extent. */ #define kOfxImageEffectPropProjectOffset "OfxImageEffectPropProjectOffset" /** @brief The pixel aspect ratio of the current project - Type - double X 1 - Property Set - a plugin instance (read only) */ #define kOfxImageEffectPropProjectPixelAspectRatio "OfxImageEffectPropPixelAspectRatio" /** @brief The duration of the effect - Type - double X 1 - Property Set - a plugin instance (read only) This contains the duration of the plug-in effect, in frames. */ #define kOfxImageEffectInstancePropEffectDuration "OfxImageEffectInstancePropEffectDuration" /** @brief Which spatial field occurs temporally first in a frame. - Type - string X 1 - Property Set - a clip instance (read only) - Valid Values - This must be one of - ::kOfxImageFieldNone - the material is unfielded - ::kOfxImageFieldLower - the material is fielded, with image rows 0,2,4.... occurring first in a frame - ::kOfxImageFieldUpper - the material is fielded, with image rows line 1,3,5.... occurring first in a frame */ #define kOfxImageClipPropFieldOrder "OfxImageClipPropFieldOrder" /** @brief The pixel data pointer of an image. - Type - pointer X 1 - Property Set - an image instance (read only) This property contains one of: - a pointer to memory that is the lower left hand corner of an image - a pointer to CUDA memory, if the Render action arguments includes kOfxImageEffectPropCudaEnabled=1 - an id, if the Render action arguments includes kOfxImageEffectPropMetalEnabled=1 - a cl_mem, if the Render action arguments includes kOfxImageEffectPropOpenCLEnabled=1 See \ref kOfxImageEffectPropCudaEnabled, \ref kOfxImageEffectPropMetalEnabled and \ref kOfxImageEffectPropOpenCLEnabled */ #define kOfxImagePropData "OfxImagePropData" /** @brief The bounds of an image's pixels. - Type - integer X 4 - Property Set - an image instance (read only) The bounds, in \ref PixelCoordinates, are of the addressable pixels in an image's data pointer. The order of the values is x1, y1, x2, y2. X values are x1 <= X < x2 Y values are y1 <= Y < y2 For less than full frame images, the pixel bounds will be contained by the ::kOfxImagePropRegionOfDefinition bounds. */ #define kOfxImagePropBounds "OfxImagePropBounds" /** @brief The full region of definition of an image. - Type - integer X 4 - Property Set - an image instance (read only) An image's region of definition, in \ref PixelCoordinates, is the full frame area of the image plane that the image covers. The order of the values is x1, y1, x2, y2. X values are x1 <= X < x2 Y values are y1 <= Y < y2 The ::kOfxImagePropBounds property contains the actual addressable pixels in an image, which may be less than its full region of definition. */ #define kOfxImagePropRegionOfDefinition "OfxImagePropRegionOfDefinition" /** @brief The number of bytes in a row of an image. - Type - integer X 1 - Property Set - an image instance (read only) For various alignment reasons, a row of pixels may need to be padded at the end with several bytes before the next row starts in memory. This property indicates the number of bytes in a row of pixels. This will be at least sizeof(PIXEL) * (bounds.x2-bounds.x1). Where bounds is fetched from the ::kOfxImagePropBounds property. Note that (for CPU images only, not CUDA/Metal/OpenCL Buffers, nor OpenGL textures accessed via the OpenGL Render Suite) row bytes can be negative, which allows hosts with a native top down row order to pass image into OFX without having to repack pixels. Row bytes is not supported for OpenCL Images. */ #define kOfxImagePropRowBytes "OfxImagePropRowBytes" /** @brief Which fields are present in the image - Type - string X 1 - Property Set - an image instance (read only) - Valid Values - This must be one of - ::kOfxImageFieldNone - the image is an unfielded frame - ::kOfxImageFieldBoth - the image is fielded and contains both interlaced fields - ::kOfxImageFieldLower - the image is fielded and contains a single field, being the lower field (rows 0,2,4...) - ::kOfxImageFieldUpper - the image is fielded and contains a single field, being the upper field (rows 1,3,5...) */ #define kOfxImagePropField "OfxImagePropField" /** @brief Controls how a plugin renders fielded footage. - Type - integer X 1 - Property Set - a plugin descriptor (read/write) - Default - 1 - Valid Values - This must be one of - 0 - the plugin is to have its render function called twice, only if there is animation in any of its parameters - 1 - the plugin is to have its render function called twice always */ #define kOfxImageEffectPluginPropFieldRenderTwiceAlways "OfxImageEffectPluginPropFieldRenderTwiceAlways" /** @brief Controls how a plugin fetched fielded imagery from a clip. - Type - string X 1 - Property Set - a clip descriptor (read/write) - Default - kOfxImageFieldDoubled - Valid Values - This must be one of - kOfxImageFieldBoth - fetch a full frame interlaced image - kOfxImageFieldSingle - fetch a single field, making a half height image - kOfxImageFieldDoubled - fetch a single field, but doubling each line and so making a full height image This controls how a plug-in wishes to fetch images from a fielded clip, so it can tune it behaviour when it renders fielded footage. Note that if it fetches kOfxImageFieldSingle and the host stores images natively as both fields interlaced, it can return a single image by doubling rowbytes and tweaking the starting address of the image data. This saves on a buffer copy. */ #define kOfxImageClipPropFieldExtraction "OfxImageClipPropFieldExtraction" /** @brief Indicates which field is being rendered. - Type - string X 1 - Property Set - a read only in argument property to ::kOfxImageEffectActionRender and ::kOfxImageEffectActionIsIdentity - Valid Values - this must be one of - kOfxImageFieldNone - there are no fields to deal with, all images are full frame - kOfxImageFieldBoth - the imagery is fielded and both scan lines should be rendered - kOfxImageFieldLower - the lower field is being rendered (lines 0,2,4...) - kOfxImageFieldUpper - the upper field is being rendered (lines 1,3,5...) */ #define kOfxImageEffectPropFieldToRender "OfxImageEffectPropFieldToRender" /** @brief Used to indicate the region of definition of a plug-in - Type - double X 4 - Property Set - a read/write out argument property to the ::kOfxImageEffectActionGetRegionOfDefinition action - Default - see ::kOfxImageEffectActionGetRegionOfDefinition The order of the values is x1, y1, x2, y2. This will be in \ref CanonicalCoordinates */ #define kOfxImageEffectPropRegionOfDefinition "OfxImageEffectPropRegionOfDefinition" /** @brief The value of a region of interest. - Type - double X 4 - Property Set - a read only in argument property to the ::kOfxImageEffectActionGetRegionsOfInterest action A host passes this value into the region of interest action to specify the region it is interested in rendering. The order of the values is x1, y1, x2, y2. This will be in \ref CanonicalCoordinates. */ #define kOfxImageEffectPropRegionOfInterest "OfxImageEffectPropRegionOfInterest" /** @brief The region to be rendered. - Type - integer X 4 - Property Set - a read only in argument property to the ::kOfxImageEffectActionRender and ::kOfxImageEffectActionIsIdentity actions The order of the values is x1, y1, x2, y2. This will be in \ref PixelCoordinates */ #define kOfxImageEffectPropRenderWindow "OfxImageEffectPropRenderWindow" /** String used to label imagery as having no fields */ #define kOfxImageFieldNone "OfxFieldNone" /** String used to label the lower field (scan lines 0,2,4...) of fielded imagery */ #define kOfxImageFieldLower "OfxFieldLower" /** String used to label the upper field (scan lines 1,3,5...) of fielded imagery */ #define kOfxImageFieldUpper "OfxFieldUpper" /** String used to label both fields of fielded imagery, indicating interlaced footage */ #define kOfxImageFieldBoth "OfxFieldBoth" /** String used to label an image that consists of a single field, and so is half height */ #define kOfxImageFieldSingle "OfxFieldSingle" /** String used to label an image that consists of a single field, but each scan line is double, and so is full height */ #define kOfxImageFieldDoubled "OfxFieldDoubled" /*@}*/ /*@}*/ /** @brief String that is the name of the standard OFX output clip */ #define kOfxImageEffectOutputClipName "Output" /** @brief String that is the name of the standard OFX single source input clip */ #define kOfxImageEffectSimpleSourceClipName "Source" /** @brief String that is the name of the 'from' clip in the OFX transition context */ #define kOfxImageEffectTransitionSourceFromClipName "SourceFrom" /** @brief String that is the name of the 'from' clip in the OFX transition context */ #define kOfxImageEffectTransitionSourceToClipName "SourceTo" /** @brief the name of the mandated 'Transition' param for the transition context */ #define kOfxImageEffectTransitionParamName "Transition" /** @brief the name of the mandated 'SourceTime' param for the retime context */ #define kOfxImageEffectRetimerParamName "SourceTime" /** @brief the string that names image effect suites, passed to OfxHost::fetchSuite */ #define kOfxImageEffectSuite "OfxImageEffectSuite" /** @brief The OFX suite for image effects This suite provides the functions needed by a plugin to defined and use an image effect plugin. */ typedef struct OfxImageEffectSuiteV1 { /** @brief Retrieves the property set for the given image effect \arg \c imageEffect image effect to get the property set for \arg \c propHandle pointer to a the property set pointer, value is returned here The property handle is for the duration of the image effect handle. @returns - ::kOfxStatOK - the property set was found and returned - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrUnknown - if the type is unknown */ OfxStatus (*getPropertySet)(OfxImageEffectHandle imageEffect, OfxPropertySetHandle *propHandle); /** @brief Retrieves the parameter set for the given image effect \arg \c imageEffect image effect to get the property set for \arg \c paramSet pointer to a the parameter set, value is returned here The param set handle is valid for the lifetime of the image effect handle. @returns - ::kOfxStatOK - the property set was found and returned - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrUnknown - if the type is unknown */ OfxStatus (*getParamSet)(OfxImageEffectHandle imageEffect, OfxParamSetHandle *paramSet); /** @brief Define a clip to the effect. \arg \c pluginHandle handle passed into 'describeInContext' action \arg \c name unique name of the clip to define \arg \c propertySet property handle for the clip descriptor will be returned here This function defines a clip to a host, the returned property set is used to describe various aspects of the clip to the host. Note that this does not create a clip instance. \pre - we are inside the describe in context action. @returns */ OfxStatus (*clipDefine)(OfxImageEffectHandle imageEffect, const char *name, OfxPropertySetHandle *propertySet); /** @brief Get the property handle of the named input clip in the given instance \arg \c imageEffect an instance handle to the plugin \arg \c name name of the clip, previously used in a clip define call \arg \c clip where to return the clip \arg \c propertySet if not NULL, the descriptor handle for a parameter's property set will be placed here. The propertySet will have the same value as would be returned by OfxImageEffectSuiteV1::clipGetPropertySet This return a clip handle for the given instance, note that this will \em not be the same as the clip handle returned by clipDefine and will be distanct to clip handles in any other instance of the plugin. Not a valid call in any of the describe actions. \pre - create instance action called, - \e name passed to clipDefine for this context, - not inside describe or describe in context actions. \post - handle will be valid for the life time of the instance. */ OfxStatus (*clipGetHandle)(OfxImageEffectHandle imageEffect, const char *name, OfxImageClipHandle *clip, OfxPropertySetHandle *propertySet); /** @brief Retrieves the property set for a given clip \arg \c clip clip effect to get the property set for \arg \c propHandle pointer to a the property set handle, value is returedn her The property handle is valid for the lifetime of the clip, which is generally the lifetime of the instance. @returns - ::kOfxStatOK - the property set was found and returned - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrUnknown - if the type is unknown */ OfxStatus (*clipGetPropertySet)(OfxImageClipHandle clip, OfxPropertySetHandle *propHandle); /** @brief Get a handle for an image in a clip at the indicated time and indicated region \arg \c clip clip to extract the image from \arg \c time time to fetch the image at \arg \c region region to fetch the image from (optional, set to NULL to get a 'default' region) this is in the \ref CanonicalCoordinates. \arg \c imageHandle property set containing the image's data An image is fetched from a clip at the indicated time for the given region and returned in the imageHandle. If the \e region parameter is not set to NULL, then it will be clipped to the clip's Region of Definition for the given time. The returned image will be \em at \em least as big as this region. If the region parameter is not set, then the region fetched will be at least the Region of Interest the effect has previously specified, clipped the clip's Region of Definition. If clipGetImage is called twice with the same parameters, then two separate image handles will be returned, each of which must be release. The underlying implementation could share image data pointers and use reference counting to maintain them. \pre - clip was returned by clipGetHandle \post - image handle is only valid for the duration of the action clipGetImage is called in - image handle to be disposed of by clipReleaseImage before the action returns @returns - ::kOfxStatOK - the image was successfully fetched and returned in the handle, - ::kOfxStatFailed - the image could not be fetched because it does not exist in the clip at the indicated time and/or region, the plugin should continue operation, but assume the image was black and transparent. - ::kOfxStatErrBadHandle - the clip handle was invalid, - ::kOfxStatErrMemory - the host had not enough memory to complete the operation, plugin should abort whatever it was doing. */ OfxStatus (*clipGetImage)(OfxImageClipHandle clip, OfxTime time, const OfxRectD *region, OfxPropertySetHandle *imageHandle); /** @brief Releases the image handle previously returned by clipGetImage \pre - imageHandle was returned by clipGetImage \post - all operations on imageHandle will be invalid @returns - ::kOfxStatOK - the image was successfully fetched and returned in the handle, - ::kOfxStatErrBadHandle - the image handle was invalid, */ OfxStatus (*clipReleaseImage)(OfxPropertySetHandle imageHandle); /** @brief Returns the spatial region of definition of the clip at the given time \arg \c clipHandle return this clip's region of definition \arg \c time time to use when determining clip's region of definition \arg \c bounds (out) bounds are returned here -- in \ref CanonicalCoordinates \pre - clipHandle was returned by clipGetHandle \post - bounds will be filled the RoD of the clip at the indicated time @returns - ::kOfxStatOK - the region was successfully found and returned in the handle, - ::kOfxStatFailed - the region could not be determined, - ::kOfxStatErrBadHandle - the clip handle was invalid, - ::kOfxStatErrMemory - the host had not enough memory to complete the operation, plugin should abort whatever it was doing. */ OfxStatus (*clipGetRegionOfDefinition)(OfxImageClipHandle clip, OfxTime time, OfxRectD *bounds); /** @brief Returns whether to abort processing or not. \arg \c imageEffect instance of the image effect A host may want to signal to a plugin that it should stop whatever rendering it is doing and start again. Generally this is done in interactive threads in response to users tweaking some parameter. This function indicates whether a plugin should stop whatever processing it is doing. @returns - 0 if the effect should continue whatever processing it is doing - 1 if the effect should abort whatever processing it is doing */ int (*abort)(OfxImageEffectHandle imageEffect); /** @brief Allocate memory from the host's image memory pool \arg \c instanceHandle effect instance to associate with this memory allocation, may be NULL. \arg \c nBytes number of bytes to allocate \arg \c memoryHandle pointer to the memory handle where a return value is placed Memory handles allocated by this should be freed by OfxImageEffectSuiteV1::imageMemoryFree. To access the memory behind the handle you need to call OfxImageEffectSuiteV1::imageMemoryLock. See \ref ImageEffectsMemoryAllocation. @returns - kOfxStatOK if all went well, a valid memory handle is placed in \e memoryHandle - kOfxStatErrBadHandle if instanceHandle is not valid, memoryHandle is set to NULL - kOfxStatErrMemory if there was not enough memory to satisfy the call, memoryHandle is set to NULL */ OfxStatus (*imageMemoryAlloc)(OfxImageEffectHandle instanceHandle, size_t nBytes, OfxImageMemoryHandle *memoryHandle); /** @brief Frees a memory handle and associated memory. \arg \c memoryHandle memory handle returned by imageMemoryAlloc This function frees a memory handle and associated memory that was previously allocated via OfxImageEffectSuiteV1::imageMemoryAlloc If there are outstanding locks, these are ignored and the handle and memory are freed anyway. See \ref ImageEffectsMemoryAllocation. @returns - kOfxStatOK if the memory was cleanly deleted - kOfxStatErrBadHandle if the value of \e memoryHandle was not a valid pointer returned by OfxImageEffectSuiteV1::imageMemoryAlloc */ OfxStatus (*imageMemoryFree)(OfxImageMemoryHandle memoryHandle); /** @brief Lock the memory associated with a memory handle and make it available for use. \arg \c memoryHandle memory handle returned by imageMemoryAlloc \arg \c returnedPtr where to the pointer to the locked memory This function locks them memory associated with a memory handle and returns a pointer to it. The memory will be 16 byte aligned, to allow use of vector operations. Note that memory locks and unlocks nest. After the first lock call, the contents of the memory pointer to by \e returnedPtr is undefined. All subsequent calls to lock will return memory with the same contents as the previous call. Also, if unlocked, then relocked, the memory associated with a memory handle may be at a different address. See also OfxImageEffectSuiteV1::imageMemoryUnlock and \ref ImageEffectsMemoryAllocation. @returns - kOfxStatOK if the memory was locked, a pointer is placed in \e returnedPtr - kOfxStatErrBadHandle if the value of \e memoryHandle was not a valid pointer returned by OfxImageEffectSuiteV1::imageMemoryAlloc, null is placed in \e *returnedPtr - kOfxStatErrMemory if there was not enough memory to satisfy the call, \e *returnedPtr is set to NULL */ OfxStatus (*imageMemoryLock)(OfxImageMemoryHandle memoryHandle, void **returnedPtr); /** @brief Unlock allocated image data \arg \c allocatedData pointer to memory previously returned by OfxImageEffectSuiteV1::imageAlloc This function unlocks a previously locked memory handle. Once completely unlocked, memory associated with a memoryHandle is no longer available for use. Attempting to use it results in undefined behaviour. Note that locks and unlocks nest, and to fully unlock memory you need to match the count of locks placed upon it. Also note, if you unlock a completely unlocked handle, it has no effect (ie: the lock count can't be negative). If unlocked, then relocked, the memory associated with a memory handle may be at a different address, however the contents will remain the same. See also OfxImageEffectSuiteV1::imageMemoryLock and \ref ImageEffectsMemoryAllocation. @returns - kOfxStatOK if the memory was unlocked cleanly, - kOfxStatErrBadHandle if the value of \e memoryHandle was not a valid pointer returned by OfxImageEffectSuiteV1::imageMemoryAlloc, null is placed in \e *returnedPtr */ OfxStatus (*imageMemoryUnlock)(OfxImageMemoryHandle memoryHandle); } OfxImageEffectSuiteV1; /** \addtogroup StatusCodes */ /*@{*/ /** \defgroup StatusCodesImageEffect Image Effect API Status Codes These are status codes returned by functions in the OfxImageEffectSuite and Image Effect plugin functions. They range from 1000 until 1999 */ /*@{*/ /** @brief Error code for incorrect image formats */ #define kOfxStatErrImageFormat ((int) 1000) /*@}*/ /*@}*/ #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxInteract.h000664 000000 000000 00000053776 15172202314 023523 0ustar00rootroot000000 000000 #ifndef _ofxInteract_h_ #define _ofxInteract_h_ #include "ofxCore.h" // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #ifdef __cplusplus extern "C" { #endif /** @file ofxInteract.h Contains the API for ofx plugin defined GUIs and interaction. */ #define kOfxInteractSuite "OfxInteractSuite" /** @brief Blind declaration of an OFX interactive gui */ typedef struct OfxInteract *OfxInteractHandle; /** \addtogroup PropertiesAll */ /*@{*/ /** \defgroup PropertiesInteract Interact Property Definitions These are the list of properties used by the Interact API documented in \ref CustomInteractionPage. */ /*@{*/ /** @brief The set of parameters on which a value change will trigger a redraw for an interact. - Type - string X N - Property Set - interact instance property (read/write) - Default - no values set - Valid Values - the name of any parameter associated with this interact. If the interact is representing the state of some set of OFX parameters, then is will need to be redrawn if any of those parameters' values change. This multi-dimensional property links such parameters to the interact. The interact can be slaved to multiple parameters (setting index 0, then index 1 etc...) */ #define kOfxInteractPropSlaveToParam "OfxInteractPropSlaveToParam" /** @brief The size of a real screen pixel under the interact's canonical projection. - Type - double X 2 - Property Set - interact instance and actions (read only) */ #define kOfxInteractPropPixelScale "OfxInteractPropPixelScale" /** @brief The background colour of the application behind an interact instance - Type - double X 3 - Property Set - read only on the interact instance and in argument to the ::kOfxInteractActionDraw action - Valid Values - from 0 to 1 The components are in the order red, green then blue. */ #define kOfxInteractPropBackgroundColour "OfxInteractPropBackgroundColour" /** @brief The suggested colour to draw a widget in an interact, typically for overlays. - Type - double X 3 - Property Set - read only on the interact instance - Default - 1.0 - Valid Values - greater than or equal to 0.0 Some applications allow the user to specify colours of any overlay via a colour picker, this property represents the value of that colour. Plugins are at liberty to use this or not when they draw an overlay. If a host does not support such a colour, it should return kOfxStatReplyDefault */ #define kOfxInteractPropSuggestedColour "OfxInteractPropSuggestedColour" /** @brief The position of the pen in an interact. - Type - double X 2 - Property Set - read only in argument to the ::kOfxInteractActionPenMotion, ::kOfxInteractActionPenDown and ::kOfxInteractActionPenUp actions This value passes the position of the pen into an interact. This is in the interact's canonical coordinates. */ #define kOfxInteractPropPenPosition "OfxInteractPropPenPosition" /** @brief The position of the pen in an interact in viewport coordinates. - Type - int X 2 - Property Set - read only in argument to the ::kOfxInteractActionPenMotion, ::kOfxInteractActionPenDown and ::kOfxInteractActionPenUp actions This value passes the position of the pen into an interact. This is in the interact's openGL viewport coordinates, with 0,0 being at the bottom left. */ #define kOfxInteractPropPenViewportPosition "OfxInteractPropPenViewportPosition" /** @brief The pressure of the pen in an interact. - Type - double X 1 - Property Set - read only in argument to the ::kOfxInteractActionPenMotion, ::kOfxInteractActionPenDown and ::kOfxInteractActionPenUp actions - Valid Values - from 0 (no pressure) to 1 (maximum pressure) This is used to indicate the status of the 'pen' in an interact. If a pen has only two states (eg: a mouse button), these should map to 0.0 and 1.0. */ #define kOfxInteractPropPenPressure "OfxInteractPropPenPressure" /** @brief Indicates whether the dits per component in the interact's openGL frame buffer - Type - int X 1 - Property Set - interact instance and descriptor (read only) */ #define kOfxInteractPropBitDepth "OfxInteractPropBitDepth" /** @brief Indicates whether the interact's frame buffer has an alpha component or not - Type - int X 1 - Property Set - interact instance and descriptor (read only) - Valid Values - This must be one of - 0 indicates no alpha component - 1 indicates an alpha component */ #define kOfxInteractPropHasAlpha "OfxInteractPropHasAlpha" /*@}*/ /*@}*/ /** \addtogroup ActionsAll */ /*@{*/ /** \defgroup InteractActions Interact Actions These are the list of actions passed to an interact's entry point function. For more details on how to deal with actions, see \ref InteractActions. */ /*@{*/ /** @brief This action is the first action passed to an interact. It is where an interact defines how it behaves and the resources it needs to function. If not trapped, the default action is for the host to carry on as normal Note that the handle passed in acts as a descriptor for, rather than an instance of the interact. @param handle handle to the interact descriptor, cast to an \ref OfxInteractHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - The plugin has been loaded and the effect described. @returns - \ref kOfxStatOK the action was trapped and all was well - \ref kOfxStatErrMemory in which case describe may be called again after a memory purge - \ref kOfxStatFailed something was wrong, the host should ignore the interact - \ref kOfxStatErrFatal */ #define kOfxActionDescribeInteract kOfxActionDescribe /** @brief This action is the first action passed to an interact instance after its creation. It is there to allow a plugin to create any per-instance data structures it may need. @param handle handle to the interact instance, cast to an \ref OfxInteractHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionDescribe has been called on this interact \post - the instance pointer will be valid until the \ref kOfxActionDestroyInstance action is passed to the plug-in with the same instance handle @returns - \ref kOfxStatOK the action was trapped and all was well - \ref kOfxStatReplyDefault the action was ignored, but all was well anyway - \ref kOfxStatErrFatal - \ref kOfxStatErrMemory in which case this may be called again after a memory purge - \ref kOfxStatFailed in which case the host should ignore this interact */ #define kOfxActionCreateInstanceInteract kOfxActionCreateInstance /**@brief This action is the last passed to an interact's instance before its destruction. It is there to allow a plugin to destroy any per-instance data structures it may have created. @param handle handle to the interact instance, cast to an \ref OfxInteractHandle @param inArgs is redundant and is set to NULL @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the handle, - the instance has not had any of its members destroyed yet \post - the instance pointer is no longer valid and any operation on it will be undefined @returns To some extent, what is returned is moot, a bit like throwing an exception in a C++ destructor, so the host should continue destruction of the instance regardless - \ref kOfxStatOK the action was trapped and all was well - \ref kOfxStatReplyDefault the action was ignored as the effect had nothing to do - \ref kOfxStatErrFatal - \ref kOfxStatFailed something went wrong, but no error code appropriate. */ #define kOfxActionDestroyInstanceInteract kOfxActionDestroyInstance /** @brief This action is issued to an interact whenever the host needs the plugin to redraw the given interact. The interact should either issue OpenGL calls (if the plugin is using OverlayInteractV1) to draw itself, or use DrawSuite calls (if using OverlayInteractV2). If this is called via kOfxImageEffectPluginPropOverlayInteractV2, drawing MUST use DrawSuite. If this is called via kOfxImageEffectPluginPropOverlayInteractV1, drawing SHOULD use OpenGL. Some existing plugins may use DrawSuite via kOfxImageEffectPluginPropOverlayInteractV1 if it's supported by the host, but this is discouraged. Note that the interact may (in the case of custom parameter GUIS) or may not (in the case of image effect overlays) be required to swap buffers, that is up to the kind of interact. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin - \ref kOfxPropEffectInstance a handle to the effect for which the interact has been, - \ref kOfxInteractPropPixelScale the scale factor to convert canonical pixels to screen pixels - \ref kOfxInteractPropBackgroundColour the background colour of the application behind the current view - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle - the openGL context for this interact has been set - the projection matrix will correspond to the interact's canonical view @returns - \ref kOfxStatOK the action was trapped and all was well - \ref kOfxStatReplyDefault the action was ignored - \ref kOfxStatErrFatal - \ref kOfxStatFailed something went wrong, the host should ignore this interact in future */ #define kOfxInteractActionDraw "OfxInteractActionDraw" /** @brief This action is issued whenever the pen moves an the interact's has focus. It should be issued whether the pen is currently up or down. No openGL calls should be issued by the plug-in during this action. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin - \ref kOfxPropEffectInstance a handle to the effect for which the interact has been, - \ref kOfxInteractPropPixelScale the scale factor to convert canonical pixels to screen pixels - \ref kOfxInteractPropBackgroundColour the background colour of the application behind the current view - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched - \ref kOfxInteractPropPenPosition position of the pen in, - \ref kOfxInteractPropPenViewportPosition position of the pen in, - \ref kOfxInteractPropPenPressure the pressure of the pen, @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle - the current instance handle has had \ref kOfxInteractActionGainFocus called on it \post - if the instance returns \ref kOfxStatOK the host should not pass the pen motion to any other interactive object it may own that shares the same view. @returns - \ref kOfxStatOK the action was trapped and the host should not pass the event to other objects it may own - \ref kOfxStatReplyDefault the action was not trapped and the host can deal with it if it wants */ #define kOfxInteractActionPenMotion "OfxInteractActionPenMotion" /**@brief This action is issued when a pen transitions for the 'up' to the 'down' state. No openGL calls should be issued by the plug-in during this action. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin, - \ref kOfxPropEffectInstance a handle to the effect for which the interact has been, - \ref kOfxInteractPropPixelScale the scale factor to convert canonical pixels to screen pixels - \ref kOfxInteractPropBackgroundColour the background colour of the application behind the current view - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched - \ref kOfxInteractPropPenPosition position of the pen in - \ref kOfxInteractPropPenViewportPosition position of the pen in - \ref kOfxInteractPropPenPressure the pressure of the pen @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, - the current instance handle has had \ref kOfxInteractActionGainFocus called on it \post - if the instance returns \ref kOfxStatOK, the host should not pass the pen motion to any other interactive object it may own that shares the same view. @returns - \ref kOfxStatOK, the action was trapped and the host should not pass the event to other objects it may own - \ref kOfxStatReplyDefault , the action was not trapped and the host can deal with it if it wants */ #define kOfxInteractActionPenDown "OfxInteractActionPenDown" /**@brief This action is issued when a pen transitions for the 'down' to the 'up' state. No openGL calls should be issued by the plug-in during this action. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin, - \ref kOfxPropEffectInstance a handle to the effect for which the interact has been, - \ref kOfxInteractPropPixelScale the scale factor to convert canonical pixels to screen pixels - \ref kOfxInteractPropBackgroundColour the background colour of the application behind the current view - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched - \ref kOfxInteractPropPenPosition position of the pen in - \ref kOfxInteractPropPenViewportPosition position of the pen in - \ref kOfxInteractPropPenPressure the pressure of the pen @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, - the current instance handle has had \ref kOfxInteractActionGainFocus called on it \post - if the instance returns \ref kOfxStatOK, the host should not pass the pen motion to any other interactive object it may own that shares the same view. @returns - \ref kOfxStatOK, the action was trapped and the host should not pass the event to other objects it may own - \ref kOfxStatReplyDefault , the action was not trapped and the host can deal with it if it wants */ #define kOfxInteractActionPenUp "OfxInteractActionPenUp" /**@brief This action is issued when a key on the keyboard is depressed. No openGL calls should be issued by the plug-in during this action. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin - \ref kOfxPropEffectInstance a handle to the effect for which the interact has been, - \ref kOfxPropKeySym single integer value representing the key that was manipulated, this may not have a UTF8 representation (eg: a return key) - \ref kOfxPropKeyString UTF8 string representing a character key that was pressed, some keys have no UTF8 encoding, in which case this is "" - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, - the current instance handle has had \ref kOfxInteractActionGainFocus called on it \post - if the instance returns \ref kOfxStatOK, the host should not pass the pen motion to any other interactive object it may own that shares the same focus. @returns - \ref kOfxStatOK , the action was trapped and the host should not pass the event to other objects it may own - \ref kOfxStatReplyDefault , the action was not trapped and the host can deal with it if it wants */ #define kOfxInteractActionKeyDown "OfxInteractActionKeyDown" /**@brief This action is issued when a key on the keyboard is released. No openGL calls should be issued by the plug-in during this action. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin - \ref kOfxPropEffectInstance a handle to the effect for which the interact has been, - \ref kOfxPropKeySym single integer value representing the key that was manipulated, this may not have a UTF8 representation (eg: a return key) - \ref kOfxPropKeyString UTF8 string representing a character key that was pressed, some keys have no UTF8 encoding, in which case this is "" - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, - the current instance handle has had \ref kOfxInteractActionGainFocus called on it \post - if the instance returns \ref kOfxStatOK, the host should not pass the pen motion to any other interactive object it may own that shares the same focus. @returns - \ref kOfxStatOK , the action was trapped and the host should not pass the event to other objects it may own - \ref kOfxStatReplyDefault , the action was not trapped and the host can deal with it if it wants */ #define kOfxInteractActionKeyUp "OfxInteractActionKeyUp" /**@brief This action is issued when a key on the keyboard is repeated. No openGL calls should be issued by the plug-in during this action. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin - \ref kOfxPropEffectInstance a handle to the effect for which the interact has been, - \ref kOfxPropKeySym single integer value representing the key that was manipulated, this may not have a UTF8 representation (eg: a return key) - \ref kOfxPropKeyString UTF8 string representing a character key that was pressed, some keys have no UTF8 encoding, in which case this is "" - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, - the current instance handle has had \ref kOfxInteractActionGainFocus called on it \post - if the instance returns \ref kOfxStatOK, the host should not pass the pen motion to any other interactive object it may own that shares the same focus. @returns - \ref kOfxStatOK , the action was trapped and the host should not pass the event to other objects it may own - \ref kOfxStatReplyDefault , the action was not trapped and the host can deal with it if it wants */ #define kOfxInteractActionKeyRepeat "OfxInteractActionKeyRepeat" /**@brief This action is issued when an interact gains input focus. No openGL calls should be issued by the plug-in during this action. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin - \ref kOfxPropEffectInstance a handle to the effect for which the interact is being used on, - \ref kOfxInteractPropPixelScale the scale factor to convert canonical pixels to screen pixels, - \ref kOfxInteractPropBackgroundColour the background colour of the application behind the current view - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, @returns - \ref kOfxStatOK the action was trapped - \ref kOfxStatReplyDefault the action was not trapped */ #define kOfxInteractActionGainFocus "OfxInteractActionGainFocus" /**@brief This action is issued when an interact loses input focus. No openGL calls should be issued by the plug-in during this action. @param handle handle to an interact instance, cast to an \ref OfxInteractHandle @param inArgs has the following properties on an image effect plugin - \ref kOfxPropEffectInstance a handle to the effect for which the interact is being used on, - \ref kOfxInteractPropPixelScale the scale factor to convert canonical pixels to screen pixels, - \ref kOfxInteractPropBackgroundColour the background colour of the application behind the current view - \ref kOfxPropTime the effect time at which changed occurred - \ref kOfxImageEffectPropRenderScale the render scale applied to any image fetched @param outArgs is redundant and is set to NULL \pre - \ref kOfxActionCreateInstance has been called on the instance handle, @returns - \ref kOfxStatOK the action was trapped - \ref kOfxStatReplyDefault the action was not trapped */ #define kOfxInteractActionLoseFocus "OfxInteractActionLoseFocus" /*@}*/ /*@}*/ /** @brief OFX suite that allows an effect to interact with an openGL window so as to provide custom interfaces. */ typedef struct OfxInteractSuiteV1 { /** @brief Requests an openGL buffer swap on the interact instance */ OfxStatus (*interactSwapBuffers)(OfxInteractHandle interactInstance); /** @brief Requests a redraw of the interact instance */ OfxStatus (*interactRedraw)(OfxInteractHandle interactInstance); /** @brief Gets the property set handle for this interact handle */ OfxStatus (*interactGetPropertySet)(OfxInteractHandle interactInstance, OfxPropertySetHandle *property); } OfxInteractSuiteV1; #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxKeySyms.h000664 000000 000000 00000050401 15172202314 023334 0ustar00rootroot000000 000000 #ifndef _ofxKeySyms_h_ #define _ofxKeySyms_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause /** \addtogroup PropertiesGeneral */ /*@{*/ /** @brief Property used to indicate which a key on the keyboard or a button on a button device has been pressed - Type - int X 1 - Property Set - an read only in argument for the actions ::kOfxInteractActionKeyDown, ::kOfxInteractActionKeyUp and ::kOfxInteractActionKeyRepeat. - Valid Values - one of any specified by #defines in the file ofxKeySyms.h. This property represents a raw key press, it does not represent the 'character value' of the key. This property is associated with a ::kOfxPropKeyString property, which encodes the UTF8 value for the keypress/button press. Some keys (for example arrow keys) have no UTF8 equivalent. Some keys, especially on non-english language systems, may have a UTF8 value, but \em not a keysym values, in these cases, the keysym will have a value of kOfxKey_Unknown, but the ::kOfxPropKeyString property will still be set with the UTF8 value. */ #define kOfxPropKeySym "kOfxPropKeySym" /** @brief This property encodes a single keypresses that generates a unicode code point. The value is stored as a UTF8 string. - Type - C string X 1, UTF8 - Property Set - an read only in argument for the actions ::kOfxInteractActionKeyDown, ::kOfxInteractActionKeyUp and ::kOfxInteractActionKeyRepeat. - Valid Values - a UTF8 string representing a single character, or the empty string. This property represents the UTF8 encode value of a single key press by a user in an OFX interact. This property is associated with a ::kOfxPropKeySym which represents an integer value for the key press. Some keys (for example arrow keys) have no UTF8 equivalent, in which case this is set to the empty string "", and the associate ::kOfxPropKeySym is set to the equivalent raw key press. Some keys, especially on non-english language systems, may have a UTF8 value, but \em not a keysym values, in these cases, the keysym will have a value of kOfxKey_Unknown, but the ::kOfxPropKeyString property will still be set with the UTF8 value. */ #define kOfxPropKeyString "kOfxPropKeyString" /* The keysyms below have been lifted wholesale out of the X-Windows key symbol header file. Only the names have been changed to protect the innocent. */ /*@}*/ /* Copyright (c) 1987, 1994 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM 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 X Consortium 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 X Consortium. Copyright 1987 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. **/ /** \defgroup KeySyms OFX Key symbols These keysymbols are used as values by the ::kOfxPropKeySym property to indicate the value of a key that has been pressed. A corresponding ::kOfxPropKeyString property is also set to contain the unicode value of the key (if it has one). The special keysym ::kOfxKey_Unknown is used to set the ::kOfxPropKeySym property in cases where the key has a UTF8 value which is not supported by the symbols below. */ /*@{*/ #define kOfxKey_Unknown 0x0 /* * TTY Functions, cleverly chosen to map to ascii, for convenience of * programming, but could have been arbitrary (at the cost of lookup * tables in client code. */ #define kOfxKey_BackSpace 0xFF08 /* back space, back char */ #define kOfxKey_Tab 0xFF09 #define kOfxKey_Linefeed 0xFF0A /* Linefeed, LF */ #define kOfxKey_Clear 0xFF0B #define kOfxKey_Return 0xFF0D /* Return, enter */ #define kOfxKey_Pause 0xFF13 /* Pause, hold */ #define kOfxKey_Scroll_Lock 0xFF14 #define kOfxKey_Sys_Req 0xFF15 #define kOfxKey_Escape 0xFF1B #define kOfxKey_Delete 0xFFFF /* Delete, rubout */ /* International & multi-key character composition */ #define kOfxKey_Multi_key 0xFF20 /* Multi-key character compose */ #define kOfxKey_SingleCandidate 0xFF3C #define kOfxKey_MultipleCandidate 0xFF3D #define kOfxKey_PreviousCandidate 0xFF3E /* Japanese keyboard support */ #define kOfxKey_Kanji 0xFF21 /* Kanji, Kanji convert */ #define kOfxKey_Muhenkan 0xFF22 /* Cancel Conversion */ #define kOfxKey_Henkan_Mode 0xFF23 /* Start/Stop Conversion */ #define kOfxKey_Henkan 0xFF23 /* Alias for Henkan_Mode */ #define kOfxKey_Romaji 0xFF24 /* to Romaji */ #define kOfxKey_Hiragana 0xFF25 /* to Hiragana */ #define kOfxKey_Katakana 0xFF26 /* to Katakana */ #define kOfxKey_Hiragana_Katakana 0xFF27 /* Hiragana/Katakana toggle */ #define kOfxKey_Zenkaku 0xFF28 /* to Zenkaku */ #define kOfxKey_Hankaku 0xFF29 /* to Hankaku */ #define kOfxKey_Zenkaku_Hankaku 0xFF2A /* Zenkaku/Hankaku toggle */ #define kOfxKey_Touroku 0xFF2B /* Add to Dictionary */ #define kOfxKey_Massyo 0xFF2C /* Delete from Dictionary */ #define kOfxKey_Kana_Lock 0xFF2D /* Kana Lock */ #define kOfxKey_Kana_Shift 0xFF2E /* Kana Shift */ #define kOfxKey_Eisu_Shift 0xFF2F /* Alphanumeric Shift */ #define kOfxKey_Eisu_toggle 0xFF30 /* Alphanumeric toggle */ #define kOfxKey_Zen_Koho 0xFF3D /* Multiple/All Candidate(s) */ #define kOfxKey_Mae_Koho 0xFF3E /* Previous Candidate */ /* Cursor control & motion */ #define kOfxKey_Home 0xFF50 #define kOfxKey_Left 0xFF51 /* Move left, left arrow */ #define kOfxKey_Up 0xFF52 /* Move up, up arrow */ #define kOfxKey_Right 0xFF53 /* Move right, right arrow */ #define kOfxKey_Down 0xFF54 /* Move down, down arrow */ #define kOfxKey_Prior 0xFF55 /* Prior, previous */ #define kOfxKey_Page_Up 0xFF55 #define kOfxKey_Next 0xFF56 /* Next */ #define kOfxKey_Page_Down 0xFF56 #define kOfxKey_End 0xFF57 /* EOL */ #define kOfxKey_Begin 0xFF58 /* BOL */ /* Misc Functions */ #define kOfxKey_Select 0xFF60 /* Select, mark */ #define kOfxKey_Print 0xFF61 #define kOfxKey_Execute 0xFF62 /* Execute, run, do */ #define kOfxKey_Insert 0xFF63 /* Insert, insert here */ #define kOfxKey_Undo 0xFF65 /* Undo, oops */ #define kOfxKey_Redo 0xFF66 /* redo, again */ #define kOfxKey_Menu 0xFF67 #define kOfxKey_Find 0xFF68 /* Find, search */ #define kOfxKey_Cancel 0xFF69 /* Cancel, stop, abort, exit */ #define kOfxKey_Help 0xFF6A /* Help */ #define kOfxKey_Break 0xFF6B #define kOfxKey_Mode_switch 0xFF7E /* Character set switch */ #define kOfxKey_script_switch 0xFF7E /* Alias for mode_switch */ #define kOfxKey_Num_Lock 0xFF7F /* Keypad Functions, keypad numbers cleverly chosen to map to ascii */ #define kOfxKey_KP_Space 0xFF80 /* space */ #define kOfxKey_KP_Tab 0xFF89 #define kOfxKey_KP_Enter 0xFF8D /* enter */ #define kOfxKey_KP_F1 0xFF91 /* PF1, KP_A, ... */ #define kOfxKey_KP_F2 0xFF92 #define kOfxKey_KP_F3 0xFF93 #define kOfxKey_KP_F4 0xFF94 #define kOfxKey_KP_Home 0xFF95 #define kOfxKey_KP_Left 0xFF96 #define kOfxKey_KP_Up 0xFF97 #define kOfxKey_KP_Right 0xFF98 #define kOfxKey_KP_Down 0xFF99 #define kOfxKey_KP_Prior 0xFF9A #define kOfxKey_KP_Page_Up 0xFF9A #define kOfxKey_KP_Next 0xFF9B #define kOfxKey_KP_Page_Down 0xFF9B #define kOfxKey_KP_End 0xFF9C #define kOfxKey_KP_Begin 0xFF9D #define kOfxKey_KP_Insert 0xFF9E #define kOfxKey_KP_Delete 0xFF9F #define kOfxKey_KP_Equal 0xFFBD /* equals */ #define kOfxKey_KP_Multiply 0xFFAA #define kOfxKey_KP_Add 0xFFAB #define kOfxKey_KP_Separator 0xFFAC /* separator, often comma */ #define kOfxKey_KP_Subtract 0xFFAD #define kOfxKey_KP_Decimal 0xFFAE #define kOfxKey_KP_Divide 0xFFAF #define kOfxKey_KP_0 0xFFB0 #define kOfxKey_KP_1 0xFFB1 #define kOfxKey_KP_2 0xFFB2 #define kOfxKey_KP_3 0xFFB3 #define kOfxKey_KP_4 0xFFB4 #define kOfxKey_KP_5 0xFFB5 #define kOfxKey_KP_6 0xFFB6 #define kOfxKey_KP_7 0xFFB7 #define kOfxKey_KP_8 0xFFB8 #define kOfxKey_KP_9 0xFFB9 /* * Auxiliary Functions; note the duplicate definitions for left and right * function keys; Sun keyboards and a few other manufactures have such * function key groups on the left and/or right sides of the keyboard. * We've not found a keyboard with more than 35 function keys total. */ #define kOfxKey_F1 0xFFBE #define kOfxKey_F2 0xFFBF #define kOfxKey_F3 0xFFC0 #define kOfxKey_F4 0xFFC1 #define kOfxKey_F5 0xFFC2 #define kOfxKey_F6 0xFFC3 #define kOfxKey_F7 0xFFC4 #define kOfxKey_F8 0xFFC5 #define kOfxKey_F9 0xFFC6 #define kOfxKey_F10 0xFFC7 #define kOfxKey_F11 0xFFC8 #define kOfxKey_L1 0xFFC8 #define kOfxKey_F12 0xFFC9 #define kOfxKey_L2 0xFFC9 #define kOfxKey_F13 0xFFCA #define kOfxKey_L3 0xFFCA #define kOfxKey_F14 0xFFCB #define kOfxKey_L4 0xFFCB #define kOfxKey_F15 0xFFCC #define kOfxKey_L5 0xFFCC #define kOfxKey_F16 0xFFCD #define kOfxKey_L6 0xFFCD #define kOfxKey_F17 0xFFCE #define kOfxKey_L7 0xFFCE #define kOfxKey_F18 0xFFCF #define kOfxKey_L8 0xFFCF #define kOfxKey_F19 0xFFD0 #define kOfxKey_L9 0xFFD0 #define kOfxKey_F20 0xFFD1 #define kOfxKey_L10 0xFFD1 #define kOfxKey_F21 0xFFD2 #define kOfxKey_R1 0xFFD2 #define kOfxKey_F22 0xFFD3 #define kOfxKey_R2 0xFFD3 #define kOfxKey_F23 0xFFD4 #define kOfxKey_R3 0xFFD4 #define kOfxKey_F24 0xFFD5 #define kOfxKey_R4 0xFFD5 #define kOfxKey_F25 0xFFD6 #define kOfxKey_R5 0xFFD6 #define kOfxKey_F26 0xFFD7 #define kOfxKey_R6 0xFFD7 #define kOfxKey_F27 0xFFD8 #define kOfxKey_R7 0xFFD8 #define kOfxKey_F28 0xFFD9 #define kOfxKey_R8 0xFFD9 #define kOfxKey_F29 0xFFDA #define kOfxKey_R9 0xFFDA #define kOfxKey_F30 0xFFDB #define kOfxKey_R10 0xFFDB #define kOfxKey_F31 0xFFDC #define kOfxKey_R11 0xFFDC #define kOfxKey_F32 0xFFDD #define kOfxKey_R12 0xFFDD #define kOfxKey_F33 0xFFDE #define kOfxKey_R13 0xFFDE #define kOfxKey_F34 0xFFDF #define kOfxKey_R14 0xFFDF #define kOfxKey_F35 0xFFE0 #define kOfxKey_R15 0xFFE0 /* Modifiers */ #define kOfxKey_Shift_L 0xFFE1 /* Left shift */ #define kOfxKey_Shift_R 0xFFE2 /* Right shift */ #define kOfxKey_Control_L 0xFFE3 /* Left control */ #define kOfxKey_Control_R 0xFFE4 /* Right control */ #define kOfxKey_Caps_Lock 0xFFE5 /* Caps lock */ #define kOfxKey_Shift_Lock 0xFFE6 /* Shift lock */ #define kOfxKey_Meta_L 0xFFE7 /* Left meta */ #define kOfxKey_Meta_R 0xFFE8 /* Right meta */ #define kOfxKey_Alt_L 0xFFE9 /* Left alt */ #define kOfxKey_Alt_R 0xFFEA /* Right alt */ #define kOfxKey_Super_L 0xFFEB /* Left super */ #define kOfxKey_Super_R 0xFFEC /* Right super */ #define kOfxKey_Hyper_L 0xFFED /* Left hyper */ #define kOfxKey_Hyper_R 0xFFEE /* Right hyper */ #define kOfxKey_space 0x020 #define kOfxKey_exclam 0x021 #define kOfxKey_quotedbl 0x022 #define kOfxKey_numbersign 0x023 #define kOfxKey_dollar 0x024 #define kOfxKey_percent 0x025 #define kOfxKey_ampersand 0x026 #define kOfxKey_apostrophe 0x027 #define kOfxKey_quoteright 0x027 /* deprecated */ #define kOfxKey_parenleft 0x028 #define kOfxKey_parenright 0x029 #define kOfxKey_asterisk 0x02a #define kOfxKey_plus 0x02b #define kOfxKey_comma 0x02c #define kOfxKey_minus 0x02d #define kOfxKey_period 0x02e #define kOfxKey_slash 0x02f #define kOfxKey_0 0x030 #define kOfxKey_1 0x031 #define kOfxKey_2 0x032 #define kOfxKey_3 0x033 #define kOfxKey_4 0x034 #define kOfxKey_5 0x035 #define kOfxKey_6 0x036 #define kOfxKey_7 0x037 #define kOfxKey_8 0x038 #define kOfxKey_9 0x039 #define kOfxKey_colon 0x03a #define kOfxKey_semicolon 0x03b #define kOfxKey_less 0x03c #define kOfxKey_equal 0x03d #define kOfxKey_greater 0x03e #define kOfxKey_question 0x03f #define kOfxKey_at 0x040 #define kOfxKey_A 0x041 #define kOfxKey_B 0x042 #define kOfxKey_C 0x043 #define kOfxKey_D 0x044 #define kOfxKey_E 0x045 #define kOfxKey_F 0x046 #define kOfxKey_G 0x047 #define kOfxKey_H 0x048 #define kOfxKey_I 0x049 #define kOfxKey_J 0x04a #define kOfxKey_K 0x04b #define kOfxKey_L 0x04c #define kOfxKey_M 0x04d #define kOfxKey_N 0x04e #define kOfxKey_O 0x04f #define kOfxKey_P 0x050 #define kOfxKey_Q 0x051 #define kOfxKey_R 0x052 #define kOfxKey_S 0x053 #define kOfxKey_T 0x054 #define kOfxKey_U 0x055 #define kOfxKey_V 0x056 #define kOfxKey_W 0x057 #define kOfxKey_X 0x058 #define kOfxKey_Y 0x059 #define kOfxKey_Z 0x05a #define kOfxKey_bracketleft 0x05b #define kOfxKey_backslash 0x05c #define kOfxKey_bracketright 0x05d #define kOfxKey_asciicircum 0x05e #define kOfxKey_underscore 0x05f #define kOfxKey_grave 0x060 #define kOfxKey_quoteleft 0x060 /* deprecated */ #define kOfxKey_a 0x061 #define kOfxKey_b 0x062 #define kOfxKey_c 0x063 #define kOfxKey_d 0x064 #define kOfxKey_e 0x065 #define kOfxKey_f 0x066 #define kOfxKey_g 0x067 #define kOfxKey_h 0x068 #define kOfxKey_i 0x069 #define kOfxKey_j 0x06a #define kOfxKey_k 0x06b #define kOfxKey_l 0x06c #define kOfxKey_m 0x06d #define kOfxKey_n 0x06e #define kOfxKey_o 0x06f #define kOfxKey_p 0x070 #define kOfxKey_q 0x071 #define kOfxKey_r 0x072 #define kOfxKey_s 0x073 #define kOfxKey_t 0x074 #define kOfxKey_u 0x075 #define kOfxKey_v 0x076 #define kOfxKey_w 0x077 #define kOfxKey_x 0x078 #define kOfxKey_y 0x079 #define kOfxKey_z 0x07a #define kOfxKey_braceleft 0x07b #define kOfxKey_bar 0x07c #define kOfxKey_braceright 0x07d #define kOfxKey_asciitilde 0x07e #define kOfxKey_nobreakspace 0x0a0 #define kOfxKey_exclamdown 0x0a1 #define kOfxKey_cent 0x0a2 #define kOfxKey_sterling 0x0a3 #define kOfxKey_currency 0x0a4 #define kOfxKey_yen 0x0a5 #define kOfxKey_brokenbar 0x0a6 #define kOfxKey_section 0x0a7 #define kOfxKey_diaeresis 0x0a8 #define kOfxKey_copyright 0x0a9 #define kOfxKey_ordfeminine 0x0aa #define kOfxKey_guillemotleft 0x0ab /* left angle quotation mark */ #define kOfxKey_notsign 0x0ac #define kOfxKey_hyphen 0x0ad #define kOfxKey_registered 0x0ae #define kOfxKey_macron 0x0af #define kOfxKey_degree 0x0b0 #define kOfxKey_plusminus 0x0b1 #define kOfxKey_twosuperior 0x0b2 #define kOfxKey_threesuperior 0x0b3 #define kOfxKey_acute 0x0b4 #define kOfxKey_mu 0x0b5 #define kOfxKey_paragraph 0x0b6 #define kOfxKey_periodcentered 0x0b7 #define kOfxKey_cedilla 0x0b8 #define kOfxKey_onesuperior 0x0b9 #define kOfxKey_masculine 0x0ba #define kOfxKey_guillemotright 0x0bb /* right angle quotation mark */ #define kOfxKey_onequarter 0x0bc #define kOfxKey_onehalf 0x0bd #define kOfxKey_threequarters 0x0be #define kOfxKey_questiondown 0x0bf #define kOfxKey_Agrave 0x0c0 #define kOfxKey_Aacute 0x0c1 #define kOfxKey_Acircumflex 0x0c2 #define kOfxKey_Atilde 0x0c3 #define kOfxKey_Adiaeresis 0x0c4 #define kOfxKey_Aring 0x0c5 #define kOfxKey_AE 0x0c6 #define kOfxKey_Ccedilla 0x0c7 #define kOfxKey_Egrave 0x0c8 #define kOfxKey_Eacute 0x0c9 #define kOfxKey_Ecircumflex 0x0ca #define kOfxKey_Ediaeresis 0x0cb #define kOfxKey_Igrave 0x0cc #define kOfxKey_Iacute 0x0cd #define kOfxKey_Icircumflex 0x0ce #define kOfxKey_Idiaeresis 0x0cf #define kOfxKey_ETH 0x0d0 #define kOfxKey_Eth 0x0d0 /* deprecated */ #define kOfxKey_Ntilde 0x0d1 #define kOfxKey_Ograve 0x0d2 #define kOfxKey_Oacute 0x0d3 #define kOfxKey_Ocircumflex 0x0d4 #define kOfxKey_Otilde 0x0d5 #define kOfxKey_Odiaeresis 0x0d6 #define kOfxKey_multiply 0x0d7 #define kOfxKey_Ooblique 0x0d8 #define kOfxKey_Ugrave 0x0d9 #define kOfxKey_Uacute 0x0da #define kOfxKey_Ucircumflex 0x0db #define kOfxKey_Udiaeresis 0x0dc #define kOfxKey_Yacute 0x0dd #define kOfxKey_THORN 0x0de #define kOfxKey_ssharp 0x0df #define kOfxKey_agrave 0x0e0 #define kOfxKey_aacute 0x0e1 #define kOfxKey_acircumflex 0x0e2 #define kOfxKey_atilde 0x0e3 #define kOfxKey_adiaeresis 0x0e4 #define kOfxKey_aring 0x0e5 #define kOfxKey_ae 0x0e6 #define kOfxKey_ccedilla 0x0e7 #define kOfxKey_egrave 0x0e8 #define kOfxKey_eacute 0x0e9 #define kOfxKey_ecircumflex 0x0ea #define kOfxKey_ediaeresis 0x0eb #define kOfxKey_igrave 0x0ec #define kOfxKey_iacute 0x0ed #define kOfxKey_icircumflex 0x0ee #define kOfxKey_idiaeresis 0x0ef #define kOfxKey_eth 0x0f0 #define kOfxKey_ntilde 0x0f1 #define kOfxKey_ograve 0x0f2 #define kOfxKey_oacute 0x0f3 #define kOfxKey_ocircumflex 0x0f4 #define kOfxKey_otilde 0x0f5 #define kOfxKey_odiaeresis 0x0f6 #define kOfxKey_division 0x0f7 #define kOfxKey_oslash 0x0f8 #define kOfxKey_ugrave 0x0f9 #define kOfxKey_uacute 0x0fa #define kOfxKey_ucircumflex 0x0fb #define kOfxKey_udiaeresis 0x0fc #define kOfxKey_yacute 0x0fd #define kOfxKey_thorn 0x0fe #define kOfxKey_ydiaeresis 0x0ff /*@}*/ #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxMemory.h000664 000000 000000 00000003665 15172202314 023212 0ustar00rootroot000000 000000 #ifndef _ofxMemory_h_ #define _ofxMemory_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #ifdef __cplusplus extern "C" { #endif #define kOfxMemorySuite "OfxMemorySuite" /** @brief The OFX suite that implements general purpose memory management. Use this suite for ordinary memory management functions, where you would normally use malloc/free or new/delete on ordinary objects. For images, you should use the memory allocation functions in the image effect suite, as many hosts have specific image memory pools. \note C++ plugin developers will need to redefine new and delete as skins on top of this suite. */ typedef struct OfxMemorySuiteV1 { /** @brief Allocate memory. \arg \c handle - effect instance to associate with this memory allocation, or NULL. \arg \c nBytes number of bytes to allocate \arg \c allocatedData pointer to the return value. Allocated memory will be aligned for any use. This function has the host allocate memory using its own memory resources and returns that to the plugin. @returns - ::kOfxStatOK the memory was successfully allocated - ::kOfxStatErrMemory the request could not be met and no memory was allocated */ OfxStatus (*memoryAlloc)(void *handle, size_t nBytes, void **allocatedData); /** @brief Frees memory. \arg \c allocatedData pointer to memory previously returned by OfxMemorySuiteV1::memoryAlloc This function frees any memory that was previously allocated via OfxMemorySuiteV1::memoryAlloc. @returns - ::kOfxStatOK the memory was successfully freed - ::kOfxStatErrBadHandle \e allocatedData was not a valid pointer returned by OfxMemorySuiteV1::memoryAlloc */ OfxStatus (*memoryFree)(void *allocatedData); } OfxMemorySuiteV1; /** @file ofxMemory.h This file contains the API for general purpose memory allocation from a host. */ #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxMessage.h000664 000000 000000 00000016335 15172202314 023324 0ustar00rootroot000000 000000 #ifndef _ofxMessage_h_ #define _ofxMessage_h_ #include "ofxCore.h" // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #ifdef __cplusplus extern "C" { #endif #define kOfxMessageSuite "OfxMessageSuite" /** @brief String used to type fatal error messages Fatal error messages should only be posted by a plugin when it can no longer continue operation. */ #define kOfxMessageFatal "OfxMessageFatal" /** @brief String used to type error messages Ordinary error messages should be posted when there is an error in operation that is recoverable by user intervention. */ #define kOfxMessageError "OfxMessageError" /** @brief String used to type warning messages Warnings indicate states that allow for operations to proceed, but are not necessarily optimal. */ #define kOfxMessageWarning "OfxMessageWarning" /** @brief String used to type simple ordinary messages Ordinary messages simply convey information from the plugin directly to the user. */ #define kOfxMessageMessage "OfxMessageMessage" /** @brief String used to type log messages Log messages are written out to a log and not to the end user. */ #define kOfxMessageLog "OfxMessageLog" /** @brief String used to type yes/no messages The host is to enter a modal state which waits for the user to respond yes or no. The OfxMessageSuiteV1::message function which posted the message will only return after the user responds. When asking a question, the OfxStatus code returned by the message function will be, - kOfxStatReplyYes - if the user replied 'yes' to the question - kOfxStatReplyNo - if the user replied 'no' to the question - some error code if an error was encounterred It is an error to post a question message if the plugin is not in an interactive session. */ #define kOfxMessageQuestion "OfxMessageQuestion" /** @brief The OFX suite that allows a plug-in to pass messages back to a user. The V2 suite extends on this in a backwards compatible manner. */ typedef struct OfxMessageSuiteV1 { /** @brief Post a message on the host, using printf style varargs \arg \c handle effect handle (descriptor or instance) the message should be associated with, may be NULL \arg \c messageType string describing the kind of message to post, one of the kOfxMessageType* constants \arg \c messageId plugin specified id to associate with this message. If overriding the message in XML resource, the message is identified with this, this may be NULL, or "", in which case no override will occur, \arg \c format printf style format string \arg \c ... printf style varargs list to print \returns - ::kOfxStatOK - if the message was successfully posted - ::kOfxStatReplyYes - if the message was of type kOfxMessageQuestion and the user reply yes - ::kOfxStatReplyNo - if the message was of type kOfxMessageQuestion and the user reply no - ::kOfxStatFailed - if the message could not be posted for some reason */ OfxStatus (*message)(void *handle, const char *messageType, const char *messageId, const char *format, ...); } OfxMessageSuiteV1; /** @brief The OFX suite that allows a plug-in to pass messages back to a user. This extends OfxMessageSuiteV1, and should be considered a replacement to version 1. Note that this suite has been extended in backwards compatible manner, so that a host can return this struct for both V1 and V2. */ typedef struct OfxMessageSuiteV2 { /** @brief Post a transient message on the host, using printf style varargs. Same as the V1 message suite call. \arg \c handle effect handle (descriptor or instance) the message should be associated with, may be null \arg \c messageType string describing the kind of message to post, one of the kOfxMessageType* constants \arg \c messageId plugin specified id to associate with this message. If overriding the message in XML resource, the message is identified with this, this may be NULL, or "", in which case no override will occur, \arg \c format printf style format string \arg \c ... printf style varargs list to print \returns - ::kOfxStatOK - if the message was successfully posted - ::kOfxStatReplyYes - if the message was of type kOfxMessageQuestion and the user reply yes - ::kOfxStatReplyNo - if the message was of type kOfxMessageQuestion and the user reply no - ::kOfxStatFailed - if the message could not be posted for some reason */ OfxStatus (*message)(void *handle, const char *messageType, const char *messageId, const char *format, ...); /** @brief Post a persistent message on an effect, using printf style varargs, and set error states. New for V2 message suite. \arg \c handle effect instance handle the message should be associated with, may NOT be null, \arg \c messageType string describing the kind of message to post, should be one of... - kOfxMessageError - kOfxMessageWarning - kOfxMessageMessage \arg \c messageId plugin specified id to associate with this message. If overriding the message in XML resource, the message is identified with this, this may be NULL, or "", in which case no override will occur, \arg \c format printf style format string \arg \c ... printf style varargs list to print \returns - ::kOfxStatOK - if the message was successfully posted - ::kOfxStatErrBadHandle - the handle was rubbish - ::kOfxStatFailed - if the message could not be posted for some reason Persistent messages are associated with an effect handle until explicitly cleared by an effect. So if an error message is posted the error state, and associated message will persist and be displayed on the effect appropriately. (eg: draw a node in red on a node based compostor and display the message when clicked on). If \e messageType is error or warning, associated error states should be flagged on host applications. Posting an error message implies that the host cannot proceed, a warning allows the host to proceed, whilst a simple message should have no stop anything. */ OfxStatus (*setPersistentMessage)(void *handle, const char *messageType, const char *messageId, const char *format, ...); /** @brief Clears any persistent message on an effect handle that was set by OfxMessageSuiteV2::setPersistentMessage. New for V2 message suite. \arg \c handle effect instance handle messages should be cleared from. \arg \c handle effect handle (descriptor or instance) \returns - ::kOfxStatOK - if the message was successfully cleared - ::kOfxStatErrBadHandle - the handle was rubbish - ::kOfxStatFailed - if the message could not be cleared for some reason Clearing a message will clear any associated error state. */ OfxStatus (*clearPersistentMessage)(void *handle); } OfxMessageSuiteV2; /** @file ofxMessage.h This file contains the Host API for end user message communication. */ #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxMultiThread.h000664 000000 000000 00000012610 15172202314 024152 0ustar00rootroot000000 000000 #ifndef _ofxMultiThread_h_ #define _ofxMultiThread_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #include "ofxCore.h" #ifdef __cplusplus extern "C" { #endif /** @file ofxMultiThread.h This file contains the Host Suite for threading */ #define kOfxMultiThreadSuite "OfxMultiThreadSuite" /** @brief Mutex blind data handle */ typedef struct OfxMutex *OfxMutexHandle; /** @brief The function type to passed to the multi threading routines \arg \c threadIndex unique index of this thread, will be between 0 and threadMax \arg \c threadMax to total number of threads executing this function \arg \c customArg the argument passed into multiThread A function of this type is passed to OfxMultiThreadSuiteV1::multiThread to be launched in multiple threads. */ typedef void (OfxThreadFunctionV1)(unsigned int threadIndex, unsigned int threadMax, void *customArg); /** @brief OFX suite that provides simple SMP style multi-processing */ typedef struct OfxMultiThreadSuiteV1 { /**@brief Function to spawn SMP threads \arg \c func function to call in each thread. \arg \c nThreads number of threads to launch \arg \c customArg parameter to pass to customArg of func in each thread. This function will spawn nThreads separate threads of computation (typically one per CPU) to allow something to perform symmetric multi processing. Each thread will call 'func' passing in the index of the thread and the number of threads actually launched. multiThread will not return until all the spawned threads have returned. It is up to the host how it waits for all the threads to return (busy wait, blocking, whatever). \e nThreads can be more than the value returned by multiThreadNumCPUs, however the threads will be limited to the number of CPUs returned by multiThreadNumCPUs. This function cannot be called recursively. @returns - ::kOfxStatOK, the function func has executed and returned successfully - ::kOfxStatFailed, the threading function failed to launch - ::kOfxStatErrExists, failed in an attempt to call multiThread recursively, */ OfxStatus (*multiThread)(OfxThreadFunctionV1 func, unsigned int nThreads, void *customArg); /**@brief Function which indicates the number of CPUs available for SMP processing \arg \c nCPUs pointer to an integer where the result is returned This value may be less than the actual number of CPUs on a machine, as the host may reserve other CPUs for itself. @returns - ::kOfxStatOK, all was OK and the maximum number of threads is in nThreads. - ::kOfxStatFailed, the function failed to get the number of CPUs */ OfxStatus (*multiThreadNumCPUs)(unsigned int *nCPUs); /**@brief Function which indicates the index of the current thread \arg \c threadIndex pointer to an integer where the result is returned This function returns the thread index, which is the same as the \e threadIndex argument passed to the ::OfxThreadFunctionV1. If there are no threads currently spawned, then this function will set threadIndex to 0 @returns - ::kOfxStatOK, all was OK and the maximum number of threads is in nThreads. - ::kOfxStatFailed, the function failed to return an index */ OfxStatus (*multiThreadIndex)(unsigned int *threadIndex); /**@brief Function to enquire if the calling thread was spawned by multiThread @returns - 0 if the thread is not one spawned by multiThread - 1 if the thread was spawned by multiThread */ int (*multiThreadIsSpawnedThread)(void); /** @brief Create a mutex \arg \c mutex where the new handle is returned \arg \c count initial lock count on the mutex. This can be negative. Creates a new mutex with lockCount locks on the mutex initially set. @returns - kOfxStatOK - mutex is now valid and ready to go */ OfxStatus (*mutexCreate)(OfxMutexHandle *mutex, int lockCount); /** @brief Destroy a mutex Destroys a mutex initially created by mutexCreate. @returns - kOfxStatOK - if it destroyed the mutex - kOfxStatErrBadHandle - if the handle was bad */ OfxStatus (*mutexDestroy)(const OfxMutexHandle mutex); /** @brief Blocking lock on the mutex This tries to lock a mutex and blocks the thread it is in until the lock succeeds. A successful lock causes the mutex's lock count to be increased by one and to block any other calls to lock the mutex until it is unlocked. @returns - kOfxStatOK - if it got the lock - kOfxStatErrBadHandle - if the handle was bad */ OfxStatus (*mutexLock)(const OfxMutexHandle mutex); /** @brief Unlock the mutex This unlocks a mutex. Unlocking a mutex decreases its lock count by one. @returns - kOfxStatOK if it released the lock - kOfxStatErrBadHandle if the handle was bad */ OfxStatus (*mutexUnLock)(const OfxMutexHandle mutex); /** @brief Non blocking attempt to lock the mutex This attempts to lock a mutex, if it cannot, it returns and says so, rather than blocking. A successful lock causes the mutex's lock count to be increased by one, if the lock did not succeed, the call returns immediately and the lock count remains unchanged. @returns - kOfxStatOK - if it got the lock - kOfxStatFailed - if it did not get the lock - kOfxStatErrBadHandle - if the handle was bad */ OfxStatus (*mutexTryLock)(const OfxMutexHandle mutex); } OfxMultiThreadSuiteV1; #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxOld.h000664 000000 000000 00000007351 15172202314 022454 0ustar00rootroot000000 000000 // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #ifndef _ofxOLD_h_ #define _ofxOLD_h_ /** @brief String to label images with YUVA components --ofxImageEffects.h @deprecated - removed in v1.4. Note, this has been deprecated in v1.3 */ #define kOfxImageComponentYUVA "OfxImageComponentYUVA" /** @brief Indicates whether an effect is performing an analysis pass. --ofxImageEffects.h - Type - int X 1 - Property Set - plugin instance (read/write) - Default - to 0 - Valid Values - This must be one of 0 or 1 @deprecated - This feature has been deprecated - officially commented out v1.4. */ #define kOfxImageEffectPropInAnalysis "OfxImageEffectPropInAnalysis" /** @brief Defines an 8 bit per component YUVA pixel -- ofxPixels.h Deprecated in 1.3, removed in 1.4 */ typedef struct OfxYUVAColourB { unsigned char y, u, v, a; }OfxYUVAColourB; /** @brief Defines an 16 bit per component YUVA pixel -- ofxPixels.h @deprecated - Deprecated in 1.3, removed in 1.4 */ typedef struct OfxYUVAColourS { unsigned short y, u, v, a; }OfxYUVAColourS; /** @brief Defines an floating point component YUVA pixel -- ofxPixels.h @deprecated - Deprecated in 1.3, removed in 1.4 */ typedef struct OfxYUVAColourF { float y, u, v, a; }OfxYUVAColourF; /** @brief The size of an interact's openGL viewport -- ofxInteract.h - Type - int X 2 - Property Set - read only property on the interact instance and in argument to all the interact actions. @deprecated - V1.3: This property is the redundant and its use will be deprecated in future releases. V1.4: Removed */ #define kOfxInteractPropViewportSize "OfxInteractPropViewport" /** @brief value for the ::kOfxParamPropDoubleType property, indicating a size normalised to the X dimension. See \ref ::kOfxParamPropDoubleType. -- ofxParam.h @deprecated - V1.3: Deprecated in favour of ::OfxParamDoubleTypeX V1.4: Removed */ #define kOfxParamDoubleTypeNormalisedX "OfxParamDoubleTypeNormalisedX" /** @brief value for the ::kOfxParamPropDoubleType property, indicating a size normalised to the Y dimension. See \ref ::kOfxParamPropDoubleType. -- ofxParam.h @deprecated - V1.3: Deprecated in favour of ::OfxParamDoubleTypeY V1.4: Removed */ #define kOfxParamDoubleTypeNormalisedY "OfxParamDoubleTypeNormalisedY" /** @brief value for the ::kOfxParamPropDoubleType property, indicating an absolute position normalised to the X dimension. See \ref ::kOfxParamPropDoubleType. -- ofxParam.h @deprecated - V1.3: Deprecated in favour of ::OfxParamDoubleTypeXAbsolute V1.4: Removed */ #define kOfxParamDoubleTypeNormalisedXAbsolute "OfxParamDoubleTypeNormalisedXAbsolute" /** @brief value for the ::kOfxParamPropDoubleType property, indicating an absolute position normalised to the Y dimension. See \ref ::kOfxParamPropDoubleType. -- ofxParam.h @deprecated - V1.3: Deprecated in favour of ::OfxParamDoubleTypeYAbsolute V1.4: Removed */ #define kOfxParamDoubleTypeNormalisedYAbsolute "OfxParamDoubleTypeNormalisedYAbsolute" /** @brief value for the ::kOfxParamPropDoubleType property, indicating normalisation to the X and Y dimension for 2D params. See \ref ::kOfxParamPropDoubleType. -- ofxParam.h @deprecated - V1.3: Deprecated in favour of ::OfxParamDoubleTypeXY V1.4: Removed */ #define kOfxParamDoubleTypeNormalisedXY "OfxParamDoubleTypeNormalisedXY" /** @brief value for the ::kOfxParamPropDoubleType property, indicating normalisation to the X and Y dimension for a 2D param that can be interpreted as an absolute spatial position. See \ref ::kOfxParamPropDoubleType. -- ofxParam.h @deprecated - V1.3: Deprecated in favour of ::kOfxParamDoubleTypeXYAbsolute V1.4: Removed */ #define kOfxParamDoubleTypeNormalisedXYAbsolute "OfxParamDoubleTypeNormalisedXYAbsolute" #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxOpenGLRender.h000664 000000 000000 00000000323 15172202314 024212 0ustar00rootroot000000 000000 #pragma once #ifndef _ofxOpenGLRender_h_ #define _ofxOpenGLRender_h_ // Copyright OpenFX and Contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #endif // _ofxOpenGLRender_h_ mlt-7.38.0/src/modules/openfx/openfx/include/ofxParam.h000664 000000 000000 00000154352 15172202314 023002 0ustar00rootroot000000 000000 #ifndef _ofxParam_h_ #define _ofxParam_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #include "ofxCore.h" #include "ofxProperty.h" #ifdef __cplusplus extern "C" { #endif /** @brief string value to the ::kOfxPropType property for all parameters */ #define kOfxParameterSuite "OfxParameterSuite" /** @brief string value on the ::kOfxPropType property for all parameter definitions (ie: the handle returned in describe) */ #define kOfxTypeParameter "OfxTypeParameter" /** @brief string value on the ::kOfxPropType property for all parameter instances */ #define kOfxTypeParameterInstance "OfxTypeParameterInstance" /** @brief Blind declaration of an OFX param */ typedef struct OfxParamStruct *OfxParamHandle; /** @brief Blind declaration of an OFX parameter set */ typedef struct OfxParamSetStruct *OfxParamSetHandle; /** \defgroup ParamTypeDefines Parameter Type definitions These strings are used to identify the type of the parameter when it is defined, they are also on the ::kOfxParamPropType in any parameter instance. */ /*@{*/ /** @brief String to identify a param as a single valued integer */ #define kOfxParamTypeInteger "OfxParamTypeInteger" /** @brief String to identify a param as a Single valued floating point parameter */ #define kOfxParamTypeDouble "OfxParamTypeDouble" /** @brief String to identify a param as a Single valued boolean parameter */ #define kOfxParamTypeBoolean "OfxParamTypeBoolean" /** @brief String to identify a param as a Single valued, 'one-of-many' parameter */ #define kOfxParamTypeChoice "OfxParamTypeChoice" /** @brief String to identify a param as a string-valued 'one-of-many' parameter. \since Version 1.5 */ #define kOfxParamTypeStrChoice "OfxParamTypeStrChoice" /** @brief String to identify a param as a Red, Green, Blue and Alpha colour parameter */ #define kOfxParamTypeRGBA "OfxParamTypeRGBA" /** @brief String to identify a param as a Red, Green and Blue colour parameter */ #define kOfxParamTypeRGB "OfxParamTypeRGB" /** @brief String to identify a param as a Two dimensional floating point parameter */ #define kOfxParamTypeDouble2D "OfxParamTypeDouble2D" /** @brief String to identify a param as a Two dimensional integer point parameter */ #define kOfxParamTypeInteger2D "OfxParamTypeInteger2D" /** @brief String to identify a param as a Three dimensional floating point parameter */ #define kOfxParamTypeDouble3D "OfxParamTypeDouble3D" /** @brief String to identify a param as a Three dimensional integer parameter */ #define kOfxParamTypeInteger3D "OfxParamTypeInteger3D" /** @brief String to identify a param as a String (UTF8) parameter */ #define kOfxParamTypeString "OfxParamTypeString" /** @brief String to identify a param as a Plug-in defined parameter */ #define kOfxParamTypeCustom "OfxParamTypeCustom" /** @brief String to identify a param as a Plug-in defined opaque data parameter */ #define kOfxParamTypeBytes "OfxParamTypeBytes" /** @brief String to identify a param as a Grouping parameter */ #define kOfxParamTypeGroup "OfxParamTypeGroup" /** @brief String to identify a param as a page parameter */ #define kOfxParamTypePage "OfxParamTypePage" /** @brief String to identify a param as a PushButton parameter */ #define kOfxParamTypePushButton "OfxParamTypePushButton" /*@}*/ /** @brief Provides information for a parameter of type kOfxParamTypeBytes */ typedef struct OfxBytes { /** @brief a pointer to the data buffer */ const unsigned char *data; /** @brief the length of the data buffer, in bytes */ size_t length; } OfxBytes; /** \addtogroup PropertiesAll */ /*@{*/ /** \defgroup ParamPropDefines Parameter Property Definitions These are the list of properties used by the parameters suite. */ /*@{*/ /** @brief Indicates if the host supports animation of custom parameters - Type - int X 1 - Property Set - host descriptor (read only) - Value Values - 0 or 1 */ #define kOfxParamHostPropSupportsCustomAnimation "OfxParamHostPropSupportsCustomAnimation" /** @brief Indicates if the host supports animation of string params - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - 0 or 1 */ #define kOfxParamHostPropSupportsStringAnimation "OfxParamHostPropSupportsStringAnimation" /** @brief Indicates if the host supports animation of boolean params - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - 0 or 1 */ #define kOfxParamHostPropSupportsBooleanAnimation "OfxParamHostPropSupportsBooleanAnimation" /** @brief Indicates if the host supports animation of choice params - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - 0 or 1 */ #define kOfxParamHostPropSupportsChoiceAnimation "OfxParamHostPropSupportsChoiceAnimation" /** @brief Indicates if the host supports custom interacts for parameters - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - 0 or 1 Currently custom interacts for parameters can only be drawn using OpenGL. APIs will be added later to support using the new Draw Suite. */ #define kOfxParamHostPropSupportsCustomInteract "OfxParamHostPropSupportsCustomInteract" /** @brief Indicates the maximum numbers of parameters available on the host. - Type - int X 1 - Property Set - host descriptor (read only) If set to -1 it implies unlimited number of parameters. */ #define kOfxParamHostPropMaxParameters "OfxParamHostPropMaxParameters" /** @brief Indicates the maximum number of parameter pages. - Type - int X 1 - Property Set - host descriptor (read only) If there is no limit to the number of pages on a host, set this to -1. Hosts that do not support paged parameter layout should set this to zero. */ #define kOfxParamHostPropMaxPages "OfxParamHostPropMaxPages" /** @brief This indicates the number of parameter rows and columns on a page. - Type - int X 2 - Property Set - host descriptor (read only) If the host has supports paged parameter layout, used dimension 0 as the number of columns per page and dimension 1 as the number of rows per page. */ #define kOfxParamHostPropPageRowColumnCount "OfxParamHostPropPageRowColumnCount" /** @brief Pseudo parameter name used to skip a row in a page layout. Passed as a value to the \ref kOfxParamPropPageChild property. See \ref ParametersInterfacesPagedLayouts for more details. */ #define kOfxParamPageSkipRow "OfxParamPageSkipRow" /** @brief Pseudo parameter name used to skip a row in a page layout. Passed as a value to the \ref kOfxParamPropPageChild property. See \ref ParametersInterfacesPagedLayouts for more details. */ #define kOfxParamPageSkipColumn "OfxParamPageSkipColumn" /** @brief Overrides the parameter's standard user interface with the given interact. - Type - pointer X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - NULL - Valid Values - must point to a OfxPluginEntryPoint If set, the parameter's normal interface is replaced completely by the interact gui. Currently custom interacts for parameters can only be drawn using OpenGL. APIs will be added later to support using the new Draw Suite. */ #define kOfxParamPropInteractV1 "OfxParamPropInteractV1" /** @brief The size of a parameter instance's custom interface in screen pixels. - Type - double x 2 - Property Set - plugin parameter instance (read only) This is set by a host to indicate the current size of a custom interface if the plug-in has one. If not this is set to (0,0). */ #define kOfxParamPropInteractSize "OfxParamPropInteractSize" /** @brief The preferred aspect ratio of a parameter's custom interface. - Type - double x 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - 1.0 - Valid Values - greater than or equal to 0.0 If set to anything other than 0.0, the custom interface for this parameter will be of a size with this aspect ratio (x size/y size). */ #define kOfxParamPropInteractSizeAspect "OfxParamPropInteractSizeAspect" /** @brief The minimum size of a parameter's custom interface, in screen pixels. - Type - double x 2 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - 10,10 - Valid Values - greater than (0, 0) Any custom interface will not be less than this size. */ #define kOfxParamPropInteractMinimumSize "OfxParamPropInteractMinimumSize" /** @brief The preferred size of a parameter's custom interface. - Type - int x 2 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - 10,10 - Valid Values - greater than (0, 0) A host should attempt to set a parameter's custom interface on a parameter to be this size if possible, otherwise it will be of ::kOfxParamPropInteractSizeAspect aspect but larger than ::kOfxParamPropInteractMinimumSize. */ #define kOfxParamPropInteractPreferedSize "OfxParamPropInteractPreferedSize" /** @brief The type of a parameter. - Type - C string X 1 - Property Set - plugin parameter descriptor (read only) and instance (read only) This string will be set to the type that the parameter was create with. */ #define kOfxParamPropType "OfxParamPropType" /** @brief Flags whether a parameter can animate. - Type - int x 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - 1 - Valid Values - 0 or 1 A plug-in uses this property to indicate if a parameter is able to animate. */ #define kOfxParamPropAnimates "OfxParamPropAnimates" /** @brief Flags whether changes to a parameter should be put on the undo/redo stack - Type - int x 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - 1 - Valid Values - 0 or 1 */ #define kOfxParamPropCanUndo "OfxParamPropCanUndo" /** @brief States whether the plugin needs to resync its private data - Type - int X 1 - Property Set - param set instance (read/write) - Default - 0 - Valid Values - - 0 - no need to sync - 1 - paramset is not synced The plugin should set this flag to true whenever any internal state has not been flushed to the set of params. The host will examine this property each time it does a copy or save operation on the instance. * If it is set to 1, the host will call SyncPrivateData and then set it to zero before doing the copy/save. * If it is set to 0, the host will assume that the param data correctly represents the private state, and will not call SyncPrivateData before copying/saving. * If this property is not set, the host will always call SyncPrivateData before copying or saving the effect (as if the property were set to 1 -- but the host will not create or modify the property). */ #define kOfxPropParamSetNeedsSyncing "OfxPropParamSetNeedsSyncing" /** @brief Flags whether a parameter is currently animating. - Type - int x 1 - Property Set - plugin parameter instance (read only) - Valid Values - 0 or 1 Set by a host on a parameter instance to indicate if the parameter has a non-constant value set on it. This can be as a consequence of animation or of scripting modifying the value, or of a parameter being connected to an expression in the host. */ #define kOfxParamPropIsAnimating "OfxParamPropIsAnimating" /** @brief Flags whether the plugin will attempt to set the value of a parameter in some callback or analysis pass - Type - int x 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - 0 - Valid Values - 0 or 1 This is used to tell the host whether the plug-in is going to attempt to set the value of the parameter. @deprecated - v1.4: deprecated - to be removed in 1.5 */ #define kOfxParamPropPluginMayWrite "OfxParamPropPluginMayWrite" /** @brief Flags whether the value of a parameter should persist. - Type - int x 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - 1 - Valid Values - 0 or 1 This is used to tell the host whether the value of the parameter is important and should be save in any description of the plug-in. */ #define kOfxParamPropPersistant "OfxParamPropPersistant" /** @brief Flags whether changing a parameter's value forces an evaluation (ie: render), - Type - int x 1 - Property Set - plugin parameter descriptor (read/write) and instance (read/write only) - Default - 1 - Valid Values - 0 or 1 This is used to indicate if the value of a parameter has any affect on an effect's output, eg: the parameter may be purely for GUI purposes, and so changing its value should not trigger a re-render. */ #define kOfxParamPropEvaluateOnChange "OfxParamPropEvaluateOnChange" /** @brief Flags whether a parameter should be exposed to a user, - Type - int x 1 - Property Set - plugin parameter descriptor (read/write) and instance (read/write) - Default - 0 - Valid Values - 0 or 1 If secret, a parameter is not exposed to a user in any interface, but should otherwise behave as a normal parameter. Secret params are typically used to hide important state detail that would otherwise be unintelligible to a user, for example the result of a statical analysis that might need many parameters to store. */ #define kOfxParamPropSecret "OfxParamPropSecret" /** @brief The value to be used as the id of the parameter in a host scripting language. - Type - ASCII C string X 1, - Property Set - plugin parameter descriptor (read/write) and instance (read only), - Default - the unique name the parameter was created with. - Valid Values - ASCII string unique to all parameters in the plug-in. Many hosts have a scripting language that they use to set values of parameters and more. If so, this is the name of a parameter in such scripts. */ #define kOfxParamPropScriptName "OfxParamPropScriptName" /** @brief Specifies how modifying the value of a param will affect any output of an effect over time. - Type - C string X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only), - Default - ::kOfxParamInvalidateValueChange - Valid Values - This must be one of - ::kOfxParamInvalidateValueChange - ::kOfxParamInvalidateValueChangeToEnd - ::kOfxParamInvalidateAll Imagine an effect with an animating parameter in a host that caches rendered output. Think of the what happens when you add a new key frame. -If the parameter represents something like an absolute position, the cache will only need to be invalidated for the range of frames that keyframe affects. - If the parameter represents something like a speed which is integrated, the cache will be invalidated from the keyframe until the end of the clip. - There are potentially other situations where the entire cache will need to be invalidated (though I can't think of one off the top of my head). */ #define kOfxParamPropCacheInvalidation "OfxParamPropCacheInvalidation" /** @brief Used as a value for the ::kOfxParamPropCacheInvalidation property */ #define kOfxParamInvalidateValueChange "OfxParamInvalidateValueChange" /** @brief Used as a value for the ::kOfxParamPropCacheInvalidation property */ #define kOfxParamInvalidateValueChangeToEnd "OfxParamInvalidateValueChangeToEnd" /** @brief Used as a value for the ::kOfxParamPropCacheInvalidation property */ #define kOfxParamInvalidateAll "OfxParamInvalidateAll" /** @brief A hint to the user as to how the parameter is to be used. - Type - UTF8 C string X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - "" */ #define kOfxParamPropHint "OfxParamPropHint" /** @brief The default value of a parameter. - Type - The type is dependent on the parameter type as is the dimension. - Property Set - plugin parameter descriptor (read/write) and instance (read/write only), - Default - 0 cast to the relevant type (or "" for strings and custom parameters) The exact type and dimension is dependent on the type of the parameter. These are.... - ::kOfxParamTypeInteger - integer property of one dimension - ::kOfxParamTypeDouble - double property of one dimension - ::kOfxParamTypeBoolean - integer property of one dimension - ::kOfxParamTypeChoice - integer property of one dimension - ::kOfxParamTypeStrChoice - string property of one dimension - ::kOfxParamTypeRGBA - double property of four dimensions - ::kOfxParamTypeRGB - double property of three dimensions - ::kOfxParamTypeDouble2D - double property of two dimensions - ::kOfxParamTypeInteger2D - integer property of two dimensions - ::kOfxParamTypeDouble3D - double property of three dimensions - ::kOfxParamTypeInteger3D - integer property of three dimensions - ::kOfxParamTypeString - string property of one dimension - ::kOfxParamTypeCustom - string property of one dimension - ::kOfxParamTypeBytes - pointer to OfxBytes struct of one dimension, or nullptr - ::kOfxParamTypeGroup - does not have this property - ::kOfxParamTypePage - does not have this property - ::kOfxParamTypePushButton - does not have this property */ #define kOfxParamPropDefault "OfxParamPropDefault" /** @brief Describes how the double parameter should be interpreted by a host. - Type - C string X 1 - Default - ::kOfxParamDoubleTypePlain - Property Set - 1D, 2D and 3D float plugin parameter descriptor (read/write) and instance (read only), - Valid Values -This must be one of - ::kOfxParamDoubleTypePlain - parameter has no special interpretation, - ::kOfxParamDoubleTypeAngle - parameter is to be interpreted as an angle, - ::kOfxParamDoubleTypeScale - parameter is to be interpreted as a scale factor, - ::kOfxParamDoubleTypeTime - parameter represents a time value (1D only), - ::kOfxParamDoubleTypeAbsoluteTime - parameter represents an absolute time value (1D only), - ::kOfxParamDoubleTypeX - size wrt to the project's X dimension (1D only), in canonical coordinates, - ::kOfxParamDoubleTypeXAbsolute - absolute position on the X axis (1D only), in canonical coordinates, - ::kOfxParamDoubleTypeY - size wrt to the project's Y dimension(1D only), in canonical coordinates, - ::kOfxParamDoubleTypeYAbsolute - absolute position on the Y axis (1D only), in canonical coordinates, - ::kOfxParamDoubleTypeXY - size in 2D (2D only), in canonical coordinates, - ::kOfxParamDoubleTypeXYAbsolute - an absolute position on the image plane, in canonical coordinates. Double parameters can be interpreted in several different ways, this property tells the host how to do so and thus gives hints as to the interface of the parameter. */ #define kOfxParamPropDoubleType "OfxParamPropDoubleType" /** @brief value for the ::kOfxParamPropDoubleType property, indicating the parameter has no special interpretation and should be interpreted as a raw numeric value. */ #define kOfxParamDoubleTypePlain "OfxParamDoubleTypePlain" /** @brief value for the ::kOfxParamPropDoubleType property, indicating the parameter is to be interpreted as a scale factor. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeScale "OfxParamDoubleTypeScale" /** @brief value for the ::kOfxParamDoubleTypeAngle property, indicating the parameter is to be interpreted as an angle. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeAngle "OfxParamDoubleTypeAngle" /** @brief value for the ::kOfxParamDoubleTypeAngle property, indicating the parameter is to be interpreted as a time. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeTime "OfxParamDoubleTypeTime" /** @brief value for the ::kOfxParamDoubleTypeAngle property, indicating the parameter is to be interpreted as an absolute time from the start of the effect. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeAbsoluteTime "OfxParamDoubleTypeAbsoluteTime" /** @brief value for the ::kOfxParamPropDoubleType property, indicating a size in canonical coords in the X dimension. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeX "OfxParamDoubleTypeX" /** @brief value for the ::kOfxParamPropDoubleType property, indicating a size in canonical coords in the Y dimension. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeY "OfxParamDoubleTypeY" /** @brief value for the ::kOfxParamPropDoubleType property, indicating an absolute position in canonical coords in the X dimension. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeXAbsolute "OfxParamDoubleTypeXAbsolute" /** @brief value for the ::kOfxParamPropDoubleType property, indicating an absolute position in canonical coords in the Y dimension. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeYAbsolute "OfxParamDoubleTypeYAbsolute" /** @brief value for the ::kOfxParamPropDoubleType property, indicating a 2D size in canonical coords. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeXY "OfxParamDoubleTypeXY" /** @brief value for the ::kOfxParamPropDoubleType property, indicating a 2D position in canonical coords. See \ref ::kOfxParamPropDoubleType. */ #define kOfxParamDoubleTypeXYAbsolute "OfxParamDoubleTypeXYAbsolute" /** @brief Describes in which coordinate system a spatial double parameter's default value is specified. - Type - C string X 1 - Default - kOfxParamCoordinatesCanonical - Property Set - Non normalised spatial double parameters, ie: any double param who's ::kOfxParamPropDoubleType is set to one of... - kOfxParamDoubleTypeX - kOfxParamDoubleTypeXAbsolute - kOfxParamDoubleTypeY - kOfxParamDoubleTypeYAbsolute - kOfxParamDoubleTypeXY - kOfxParamDoubleTypeXYAbsolute - Valid Values - This must be one of - kOfxParamCoordinatesCanonical - the default is in canonical coords - kOfxParamCoordinatesNormalised - the default is in normalised coordinates This allows a spatial param to specify what its default is, so by saying normalised and "0.5" it would be in the 'middle', by saying canonical and 100 it would be at value 100 independent of the size of the image being applied to. */ #define kOfxParamPropDefaultCoordinateSystem "OfxParamPropDefaultCoordinateSystem" /** @brief Define the canonical coordinate system */ #define kOfxParamCoordinatesCanonical "OfxParamCoordinatesCanonical" /** @brief Define the normalised coordinate system */ #define kOfxParamCoordinatesNormalised "OfxParamCoordinatesNormalised" /** @brief A flag to indicate if there is a host overlay UI handle for the given parameter. - Type - int x 1 - Property Set - plugin parameter descriptor (read only) - Valid Values - 0 or 1 If set to 1, then the host is flagging that there is some sort of native user overlay interface handle available for the given parameter. */ #define kOfxParamPropHasHostOverlayHandle "OfxParamPropHasHostOverlayHandle" /** @brief A flag to indicate that the host should use a native UI overlay handle for the given parameter. - Type - int x 1 - Property Set - plugin parameter descriptor (read/write only) and instance (read only) - Default - 0 - Valid Values - 0 or 1 If set to 1, then a plugin is flaging to the host that the host should use a native UI overlay handle for the given parameter. A plugin can use this to keep a native look and feel for parameter handles. A plugin can use ::kOfxParamPropHasHostOverlayHandle to see if handles are available on the given parameter. */ #define kOfxParamPropUseHostOverlayHandle "kOfxParamPropUseHostOverlayHandle" /** @brief Enables the display of a time marker on the host's time line to indicate the value of the absolute time param. - Type - int x 1 - Property Set - plugin parameter descriptor (read/write) and instance (read/write) - Default - 0 - Valid Values - 0 or 1 If a double parameter is has ::kOfxParamPropDoubleType set to ::kOfxParamDoubleTypeAbsoluteTime, then this indicates whether any marker should be made visible on the host's time line. */ #define kOfxParamPropShowTimeMarker "OfxParamPropShowTimeMarker" /** @brief Sets the parameter pages and order of pages. - Type - C string X N - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - "" - Valid Values - the names of any page param in the plugin This property sets the preferred order of parameter pages on a host. If this is never set, the preferred order is the order the parameters were declared in. */ #define kOfxPluginPropParamPageOrder "OfxPluginPropParamPageOrder" /** @brief The names of the parameters included in a page parameter. - Type - C string X N - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - "" - Valid Values - the names of any parameter that is not a group or page, as well as ::kOfxParamPageSkipRow and ::kOfxParamPageSkipColumn This is a property on parameters of type ::kOfxParamTypePage, and tells the page what parameters it contains. The parameters are added to the page from the top left, filling in columns as we go. The two pseudo param names ::kOfxParamPageSkipRow and ::kOfxParamPageSkipColumn are used to control layout. Note parameters can appear in more than one page. */ #define kOfxParamPropPageChild "OfxParamPropPageChild" /** @brief The name of a parameter's parent group. - Type - C string X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only), - Default - "", which implies the "root" of the hierarchy, - Valid Values - the name of a parameter with type of ::kOfxParamTypeGroup Hosts that have hierarchical layouts of their params use this to recursively group parameter. By default parameters are added in order of declaration to the 'root' hierarchy. This property is used to reparent params to a predefined param of type ::kOfxParamTypeGroup. */ #define kOfxParamPropParent "OfxParamPropParent" /** @brief Whether the initial state of a group is open or closed in a hierarchical layout. - Type - int X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only) - Default - 1 - Valid Values - 0 or 1 This is a property on parameters of type ::kOfxParamTypeGroup, and tells the group whether it should be open or closed by default. */ #define kOfxParamPropGroupOpen "OfxParamPropGroupOpen" /** @brief Used to enable a parameter in the user interface. - Type - int X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - 1 - Valid Values - 0 or 1 When set to 0 a user should not be able to modify the value of the parameter. Note that the plug-in itself can still change the value of a disabled parameter. */ #define kOfxParamPropEnabled "OfxParamPropEnabled" /** @brief A private data pointer that the plug-in can store its own data behind. - Type - pointer X 1 - Property Set - plugin parameter instance (read/write), - Default - NULL This data pointer is unique to each parameter instance, so two instances of the same parameter do not share the same data pointer. Use it to hang any needed private data structures. */ #define kOfxParamPropDataPtr "OfxParamPropDataPtr" /** @brief Set options of a choice parameter. - Type - UTF8 C string X N - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - the property is empty with no options set. This property contains the set of options that will be presented to a user from a choice parameter. See @ref ParametersChoice for more details. */ #define kOfxParamPropChoiceOption "OfxParamPropChoiceOption" /** @brief Set values the host should store for a choice parameter. - Type - int X N - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - Zero-based ordinal list of same length as `OfxParamPropChoiceOption` This property specifies the order in which the options are presented. See @ref "Choice Parameters" for more details. This property is optional; if not set, the host will present the options in their natural order. This property is useful when changing order of choice param options, or adding new options in the middle, in a new version of the plugin. @verbatim Plugin v1: Option = {"OptA", "OptB", "OptC"} Order = {1, 2, 3} Plugin v2: // will be shown as OptA / OptB / NewOpt / OptC Option = {"OptA", "OptB", "OptC", NewOpt"} Order = {1, 2, 4, 3} @endverbatim Note that this only affects the host UI's display order; the project still stores the index of the selected option as always. Plugins should never reorder existing options if they desire backward compatibility. Values may be arbitrary 32-bit integers. Behavior is undefined if the same value occurs twice in the list; plugins should not do that. \since Version 1.5 */ #define kOfxParamPropChoiceOrder "OfxParamPropChoiceOrder" /** @brief Set a enumeration string in a StrChoice (string-valued choice) parameter. - Type - UTF8 C string X N - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - the property is empty with no options set. This property contains the set of enumeration strings stored by the host in the project corresponding to the options that will be presented to a user from a StrChoice parameter. See @ref ParametersChoice for more details. \since Version 1.5 */ #define kOfxParamPropChoiceEnum "OfxParamPropChoiceEnum" /** @brief Indicates if the host supports animation of string choice params. - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - 0 or 1 \since Version 1.5 */ #define kOfxParamHostPropSupportsStrChoiceAnimation "OfxParamHostPropSupportsStrChoiceAnimation" /** @brief Indicates if the host supports the StrChoice param type. - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - 0 or 1 \since Version 1.5 */ #define kOfxParamHostPropSupportsStrChoice "OfxParamHostPropSupportsStrChoice" /** @brief The minimum value for a numeric parameter. - Type - int or double X N - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - the smallest possible value corresponding to the parameter type (eg: INT_MIN for an integer, -DBL_MAX for a double parameter) Setting this will also reset ::kOfxParamPropDisplayMin. */ #define kOfxParamPropMin "OfxParamPropMin" /** @brief The maximum value for a numeric parameter. - Type - int or double X N - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - the largest possible value corresponding to the parameter type (eg: INT_MAX for an integer, DBL_MAX for a double parameter) Setting this will also reset :;kOfxParamPropDisplayMax. */ #define kOfxParamPropMax "OfxParamPropMax" /** @brief The minimum value for a numeric parameter on any user interface. - Type - int or double X N - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - the smallest possible value corresponding to the parameter type (eg: INT_MIN for an integer, -DBL_MAX for a double parameter) If a user interface represents a parameter with a slider or similar, this should be the minimum bound on that slider. */ #define kOfxParamPropDisplayMin "OfxParamPropDisplayMin" /** @brief The maximum value for a numeric parameter on any user interface. - Type - int or double X N - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - the largest possible value corresponding to the parameter type (eg: INT_MAX for an integer, DBL_MAX for a double parameter) If a user interface represents a parameter with a slider or similar, this should be the maximum bound on that slider. */ #define kOfxParamPropDisplayMax "OfxParamPropDisplayMax" /** @brief The granularity of a slider used to represent a numeric parameter. - Type - double X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - 1 - Valid Values - any greater than 0. This value is always in canonical coordinates for double parameters that are normalised. */ #define kOfxParamPropIncrement "OfxParamPropIncrement" /** @brief How many digits after a decimal point to display for a double param in a GUI. - Type - int X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - 2 - Valid Values - any greater than 0. This applies to double params of any dimension. */ #define kOfxParamPropDigits "OfxParamPropDigits" /** @brief Label for individual dimensions on a multidimensional numeric parameter. - Type - UTF8 C string X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only), - Default - "x", "y" and "z" - Valid Values - any Use this on 2D and 3D double and integer parameters to change the label on an individual dimension in any GUI for that parameter. */ #define kOfxParamPropDimensionLabel "OfxParamPropDimensionLabel" /** @brief Will a value change on the parameter add automatic keyframes. - Type - int X 1 - Property Set - plugin parameter instance (read only), - Valid Values - 0 or 1 This is set by the host simply to indicate the state of the property. */ #define kOfxParamPropIsAutoKeying "OfxParamPropIsAutoKeying" /** @brief A pointer to a custom parameter's interpolation function. - Type - pointer X 1 - Property Set - plugin parameter descriptor (read/write) and instance (read only), - Default - NULL - Valid Values - must point to a ::OfxCustomParamInterpFuncV1 It is an error not to set this property in a custom parameter during a plugin's define call if the custom parameter declares itself to be an animating parameter. */ #define kOfxParamPropCustomInterpCallbackV1 "OfxParamPropCustomCallbackV1" /** @brief Used to indicate the type of a string parameter. - Type - C string X 1 - Property Set - plugin string parameter descriptor (read/write) and instance (read only), - Default - ::kOfxParamStringIsSingleLine - Valid Values - This must be one of the following - ::kOfxParamStringIsSingleLine - ::kOfxParamStringIsMultiLine - ::kOfxParamStringIsFilePath - ::kOfxParamStringIsDirectoryPath - ::kOfxParamStringIsLabel - ::kOfxParamStringIsRichTextFormat */ #define kOfxParamPropStringMode "OfxParamPropStringMode" /** @brief Indicates string parameters of file or directory type need that file to exist already. - Type - int X 1 - Property Set - plugin string parameter descriptor (read/write) and instance (read only), - Default - 1 - Valid Values - 0 or 1 If set to 0, it implies the user can specify a new file name, not just a pre-existing one. */ #define kOfxParamPropStringFilePathExists "OfxParamPropStringFilePathExists" /** @brief Used to set a string parameter to be single line, value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsSingleLine "OfxParamStringIsSingleLine" /** @brief Used to set a string parameter to be multiple line, value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsMultiLine "OfxParamStringIsMultiLine" /** @brief Used to set a string parameter to be a file path, value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsFilePath "OfxParamStringIsFilePath" /** @brief Used to set a string parameter to be a directory path, value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsDirectoryPath "OfxParamStringIsDirectoryPath" /** @brief Use to set a string parameter to be a simple label, value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsLabel "OfxParamStringIsLabel" /** @brief String value on the ::kOfxParamPropStringMode property of a string parameter (added in 1.3) */ #define kOfxParamStringIsRichTextFormat "OfxParamStringIsRichTextFormat" /** @brief Used by interpolating custom parameters to get and set interpolated values. - Type - C string X 1 or 2 This property is on the \e inArgs property and \e outArgs property of a ::OfxCustomParamInterpFuncV1 and in both cases contains the encoded value of a custom parameter. As an \e inArgs property it will have two values, being the two keyframes to interpolate. As an \e outArgs property it will have a single value and the plugin should fill this with the encoded interpolated value of the parameter. */ #define kOfxParamPropCustomValue "OfxParamPropCustomValue" /** @brief Used by interpolating custom parameters to indicate the time a key occurs at. - Type - double X 2 - Property Set - inArgs parameter of a ::OfxCustomParamInterpFuncV1 (read only) The two values indicate the absolute times the surrounding keyframes occur at. The keyframes are encoded in a ::kOfxParamPropCustomValue property. */ #define kOfxParamPropInterpolationTime "OfxParamPropInterpolationTime" /** @brief Property used by ::OfxCustomParamInterpFuncV1 to indicate the amount of interpolation to perform - Type - double X 1 - Property Set - inArgs parameter of a ::OfxCustomParamInterpFuncV1 (read only) - Valid Values - from 0 to 1 This property indicates how far between the two ::kOfxParamPropCustomValue keys to interpolate. */ #define kOfxParamPropInterpolationAmount "OfxParamPropInterpolationAmount" /*@}*/ /*@}*/ /** @brief Function prototype for custom parameter interpolation callback functions \arg \c instance the plugin instance that this parameter occurs in \arg \c inArgs handle holding the following properties... - kOfxPropName - the name of the custom parameter to interpolate - kOfxPropTime - absolute time the interpolation is occurring at - kOfxParamPropCustomValue - string property that gives the value of the two keyframes to interpolate, in this case 2D - kOfxParamPropInterpolationTime - 2D double property that gives the time of the two keyframes we are interpolating - kOfxParamPropInterpolationAmount - 1D double property indicating how much to interpolate between the two keyframes \arg \c outArgs handle holding the following properties to be set - kOfxParamPropCustomValue - the value of the interpolated custom parameter, in this case 1D This function allows custom parameters to animate by performing interpolation between keys. The plugin needs to parse the two strings encoding keyframes on either side of the time we need a value for. It should then interpolate a new value for it, encode it into a string and set the ::kOfxParamPropCustomValue property with this on the outArgs handle. The interp value is a linear interpolation amount, however his may be derived from a cubic (or other) curve. */ typedef OfxStatus (OfxCustomParamInterpFuncV1)(OfxParamSetHandle instance, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs); /** @brief The OFX suite used to define and manipulate user visible parameters */ typedef struct OfxParameterSuiteV1 { /** @brief Defines a new parameter of the given type in a describe action \arg \c paramSet handle to the parameter set descriptor that will hold this parameter \arg \c paramType type of the parameter to create, one of the kOfxParamType* #defines \arg \c name unique name of the parameter \arg \c propertySet if not null, a pointer to the parameter descriptor's property set will be placed here. This function defines a parameter in a parameter set and returns a property set which is used to describe that parameter. This function does not actually create a parameter, it only says that one should exist in any subsequent instances. To fetch an parameter instance paramGetHandle must be called on an instance. This function can always be called in one of a plug-in's "describe" functions which defines the parameter sets common to all instances of a plugin. @returns - ::kOfxStatOK - the parameter was created correctly - ::kOfxStatErrBadHandle - if the plugin handle was invalid - ::kOfxStatErrExists - if a parameter of that name exists already in this plugin - ::kOfxStatErrUnknown - if the type is unknown - ::kOfxStatErrUnsupported - if the type is known but unsupported */ OfxStatus (*paramDefine)(OfxParamSetHandle paramSet, const char *paramType, const char *name, OfxPropertySetHandle *propertySet); /** @brief Retrieves the handle for a parameter in a given parameter set \arg \c paramSet instance of the plug-in to fetch the property handle from \arg \c name parameter to ask about \arg \c param pointer to a param handle, the value is returned here \arg \c propertySet if not null, a pointer to the parameter's property set will be placed here. Parameter handles retrieved from an instance are always distinct in each instance. The parameter handle is valid for the life-time of the instance. Parameter handles in instances are distinct from parameter handles in plugins. You cannot call this in a plugin's describe function, as it needs an instance to work on. @returns - ::kOfxStatOK - the parameter was found and returned - ::kOfxStatErrBadHandle - if the plugin handle was invalid - ::kOfxStatErrUnknown - if the type is unknown */ OfxStatus (*paramGetHandle)(OfxParamSetHandle paramSet, const char *name, OfxParamHandle *param, OfxPropertySetHandle *propertySet); /** @brief Retrieves the property set handle for the given parameter set \arg \c paramSet parameter set to get the property set for \arg \c propHandle pointer to a the property set handle, value is returedn her \note The property handle belonging to a parameter set is the same as the property handle belonging to the plugin instance. @returns - ::kOfxStatOK - the property set was found and returned - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrUnknown - if the type is unknown */ OfxStatus (*paramSetGetPropertySet)(OfxParamSetHandle paramSet, OfxPropertySetHandle *propHandle); /** @brief Retrieves the property set handle for the given parameter \arg \c param parameter to get the property set for \arg \c propHandle pointer to a the property set handle, value is returedn her The property handle is valid for the lifetime of the parameter, which is the lifetime of the instance that owns the parameter @returns - ::kOfxStatOK - the property set was found and returned - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrUnknown - if the type is unknown */ OfxStatus (*paramGetPropertySet)(OfxParamHandle param, OfxPropertySetHandle *propHandle); /** @brief Gets the current value of a parameter, \arg \c paramHandle parameter handle to fetch value from \arg \c ... one or more pointers to variables of the relevant type to hold the parameter's value This gets the current value of a parameter. The varargs ... argument needs to be pointer to C variables of the relevant type for this parameter. Note that params with multiple values (eg Colour) take multiple args here. For example... @verbatim OfxParamHandle myDoubleParam, *myColourParam; ofxHost->paramGetHandle(instance, "myDoubleParam", &myDoubleParam); double myDoubleValue; ofxHost->paramGetValue(myDoubleParam, &myDoubleValue); ofxHost->paramGetHandle(instance, "myColourParam", &myColourParam); double myR, myG, myB; ofxHost->paramGetValue(myColourParam, &myR, &myG, &myB); @endverbatim \note \c paramGetValue should only be called from within a ::kOfxActionInstanceChanged or interact action and never from the render actions (which should always use paramGetValueAtTime). @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramGetValue)(OfxParamHandle paramHandle, ...); /** @brief Gets the value of a parameter at a specific time. \arg \c paramHandle parameter handle to fetch value from \arg \c time at what point in time to look up the parameter \arg \c ... one or more pointers to variables of the relevant type to hold the parameter's value This gets the current value of a parameter. The varargs needs to be pointer to C variables of the relevant type for this parameter. See OfxParameterSuiteV1::paramGetValue for notes on the varags list @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramGetValueAtTime)(OfxParamHandle paramHandle, OfxTime time, ...); /** @brief Gets the derivative of a parameter at a specific time. \arg \c paramHandle parameter handle to fetch value from \arg \c time at what point in time to look up the parameter \arg \c ... one or more pointers to variables of the relevant type to hold the parameter's derivative This gets the derivative of the parameter at the indicated time. The varargs needs to be pointer to C variables of the relevant type for this parameter. See OfxParameterSuiteV1::paramGetValue for notes on the varags list. Only double and colour params can have their derivatives found. @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramGetDerivative)(OfxParamHandle paramHandle, OfxTime time, ...); /** @brief Gets the integral of a parameter over a specific time range, \arg \c paramHandle parameter handle to fetch integral from \arg \c time1 where to start evaluating the integral \arg \c time2 where to stop evaluating the integral \arg \c ... one or more pointers to variables of the relevant type to hold the parameter's integral This gets the integral of the parameter over the specified time range. The varargs needs to be pointer to C variables of the relevant type for this parameter. See OfxParameterSuiteV1::paramGetValue for notes on the varags list. Only double and colour params can be integrated. @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramGetIntegral)(OfxParamHandle paramHandle, OfxTime time1, OfxTime time2, ...); /** @brief Sets the current value of a parameter \arg \c paramHandle parameter handle to set value in \arg \c ... one or more variables of the relevant type to hold the parameter's value This sets the current value of a parameter. The varargs ... argument needs to be values of the relevant type for this parameter. Note that params with multiple values (eg Colour) take multiple args here. For example... @verbatim ofxHost->paramSetValue(instance, "myDoubleParam", double(10)); ofxHost->paramSetValue(instance, "myColourParam", double(pix.r), double(pix.g), double(pix.b)); @endverbatim \note \c paramSetValue should only be called from within a ::kOfxActionInstanceChanged or interact action. @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramSetValue)(OfxParamHandle paramHandle, ...); /** @brief Keyframes the value of a parameter at a specific time. \arg \c paramHandle parameter handle to set value in \arg \c time at what point in time to set the keyframe \arg \c ... one or more variables of the relevant type to hold the parameter's value This sets a keyframe in the parameter at the indicated time to have the indicated value. The varargs ... argument needs to be values of the relevant type for this parameter. See the note on OfxParameterSuiteV1::paramSetValue for more detail \note \c paramSetValueAtTime should only be called from within a ::kOfxActionInstanceChanged or interact action. V1.3: This function can be called the ::kOfxActionInstanceChanged action and during image effect analysis render passes. V1.4: This function can be called the ::kOfxActionInstanceChanged action @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramSetValueAtTime)(OfxParamHandle paramHandle, OfxTime time, // time in frames ...); /** @name Keyframe Handling These functions allow the plug-in to delete and get information about keyframes. To set keyframes, use paramSetValueAtTime(). paramGetKeyTime and paramGetKeyIndex use indices to refer to keyframes. Keyframes are stored by the host in increasing time order, so time(kf[i]) < time(kf[i+1]). Keyframe indices will change whenever keyframes are added, deleted, or moved in time, whether by the host or by the plug-in. They may vary between actions if the user changes a keyframe. The keyframe indices will not change within a single action. */ /** @{ */ /** @brief Returns the number of keyframes in the parameter \arg \c paramHandle parameter handle to interrogate \arg \c numberOfKeys pointer to integer where the return value is placed V1.3: This function can be called the ::kOfxActionInstanceChanged action and during image effect analysis render passes. V1.4: This function can be called the ::kOfxActionInstanceChanged action Returns the number of keyframes in the parameter. @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramGetNumKeys)(OfxParamHandle paramHandle, unsigned int *numberOfKeys); /** @brief Returns the time of the nth key \arg \c paramHandle parameter handle to interrogate \arg \c nthKey which key to ask about (0 to paramGetNumKeys -1), ordered by time \arg \c time pointer to OfxTime where the return value is placed @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrBadIndex - the nthKey does not exist */ OfxStatus (*paramGetKeyTime)(OfxParamHandle paramHandle, unsigned int nthKey, OfxTime *time); /** @brief Finds the index of a keyframe at/before/after a specified time. \arg \c paramHandle parameter handle to search \arg \c time what time to search from \arg \c direction - == 0 indicates search for a key at the indicated time (some small delta) - > 0 indicates search for the next key after the indicated time - < 0 indicates search for the previous key before the indicated time \arg \c index pointer to an integer which in which the index is returned set to -1 if no key was found @returns - ::kOfxStatOK - all was OK - ::kOfxStatFailed - if the search failed to find a key - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramGetKeyIndex)(OfxParamHandle paramHandle, OfxTime time, int direction, int *index); /** @brief Deletes a keyframe if one exists at the given time. \arg \c paramHandle parameter handle to delete the key from \arg \c time time at which a keyframe is @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrBadIndex - no key at the given time */ OfxStatus (*paramDeleteKey)(OfxParamHandle paramHandle, OfxTime time); /** @brief Deletes all keyframes from a parameter. \arg \c paramHandle parameter handle to delete the keys from \arg \c name parameter to delete the keyframes from is V1.3: This function can be called the ::kOfxActionInstanceChanged action and during image effect analysis render passes. V1.4: This function can be called the ::kOfxActionInstanceChanged action @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramDeleteAllKeys)(OfxParamHandle paramHandle); /** @} */ /** @brief Copies one parameter to another, including any animation etc... \arg \c paramTo parameter to set \arg \c paramFrom parameter to copy from \arg \c dstOffset temporal offset to apply to keys when writing to the paramTo \arg \c frameRange if paramFrom has animation, and frameRange is not null, only this range of keys will be copied This copies the value of \e paramFrom to \e paramTo, including any animation it may have. All the previous values in \e paramTo will be lost. To choose all animation in \e paramFrom set \e frameRange to [0, 0] V1.3: This function can be called the ::kOfxActionInstanceChanged action and during image effect analysis render passes. V1.4: This function can be called the ::kOfxActionInstanceChanged action \pre - Both parameters must be of the same type. \return - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the parameter handle was invalid */ OfxStatus (*paramCopy)(OfxParamHandle paramTo, OfxParamHandle paramFrom, OfxTime dstOffset, const OfxRangeD *frameRange); /** @brief Used to group any parameter changes for undo/redo purposes \arg \c paramSet the parameter set in which this is happening \arg \c name label to attach to any undo/redo string UTF8 If a plugin calls paramSetValue/paramSetValueAtTime on one or more parameters, either from custom GUI interaction or some analysis of imagery etc.. this is used to indicate the start of a set of a parameter changes that should be considered part of a single undo/redo block. \note \c paramEditBegin should only be called from within a ::kOfxActionInstanceChanged or interact action. See also OfxParameterSuiteV1::paramEditEnd \return - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the instance handle was invalid */ OfxStatus (*paramEditBegin)(OfxParamSetHandle paramSet, const char *name); /** @brief Used to group any parameter changes for undo/redo purposes \arg \c paramSet parameter set in which this is happening If a plugin calls paramSetValue/paramSetValueAtTime on one or more parameters, either from custom GUI interaction or some analysis of imagery etc.. this is used to indicate the end of a set of parameter changes that should be considerred part of a single undo/redo block \note \c paramEditEnd should only be called from within a ::kOfxActionInstanceChanged or interact action. See also OfxParameterSuiteV1::paramEditBegin @returns - ::kOfxStatOK - all was OK - ::kOfxStatErrBadHandle - if the instance handle was invalid */ OfxStatus (*paramEditEnd)(OfxParamSetHandle paramSet); } OfxParameterSuiteV1; #ifdef __cplusplus } #endif /** @file ofxParam.h This header contains the suite definition to manipulate host side parameters. For more details go see @ref ParametersPage */ #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxParametricParam.h000664 000000 000000 00000030036 15172202314 025002 0ustar00rootroot000000 000000 #ifndef _ofxParametricParam_h_ #define _ofxParametricParam_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause /** @file ofxParametricParam.h This header file defines the optional OFX extension to define and manipulate parametric parameters. */ #include "ofxParam.h" #ifdef __cplusplus extern "C" { #endif /** @brief string value to the ::kOfxPropType property for all parameters */ #define kOfxParametricParameterSuite "OfxParametricParameterSuite" /** \defgroup ParamTypeDefines Parameter Type definitions These strings are used to identify the type of the parameter when it is defined, they are also on the ::kOfxParamPropType in any parameter instance. */ /*@{*/ /** @brief String to identify a param as a single valued integer */ #define kOfxParamTypeParametric "OfxParamTypeParametric" /*@}*/ /** \addtogroup PropertiesAll */ /*@{*/ /** \defgroup ParamPropDefines Parameter Property Definitions These are the list of properties used by the parameters suite. */ /*@{*/ /** @brief The dimension of a parametric param - Type - int X 1 - Property Set - parametric param descriptor (read/write) and instance (read only) - default - 1 - Value Values - greater than 0 This indicates the dimension of the parametric param. */ #define kOfxParamPropParametricDimension "OfxParamPropParametricDimension" /** @brief The colour of parametric param curve interface in any UI. - Type - double X N - Property Set - parametric param descriptor (read/write) and instance (read only) - default - unset, - Value Values - three values for each dimension (see ::kOfxParamPropParametricDimension) being interpreted as R, G and B of the colour for each curve drawn in the UI. This sets the colour of a parametric param curve drawn a host user interface. A colour triple is needed for each dimension of the oparametric param. If not set, the host should generally draw these in white. */ #define kOfxParamPropParametricUIColour "OfxParamPropParametricUIColour" /** @brief Interact entry point to draw the background of a parametric parameter. - Type - pointer X 1 - Property Set - plug-in parametric parameter descriptor (read/write) and instance (read only), - Default - NULL, which implies the host should draw its default background. Defines a pointer to an interact which will be used to draw the background of a parametric parameter's user interface. None of the pen or keyboard actions can ever be called on the interact. The openGL transform will be set so that it is an orthographic transform that maps directly to the 'parametric' space, so that 'x' represents the parametric position and 'y' represents the evaluated value. */ #define kOfxParamPropParametricInteractBackground "OfxParamPropParametricInteractBackground" /** @brief Property on the host to indicate support for parametric parameter animation. - Type - int X 1 - Property Set - host descriptor (read only) - Valid Values - 0 indicating the host does not support animation of parmetric params, - 1 indicating the host does support animation of parmetric params, */ #define kOfxParamHostPropSupportsParametricAnimation "OfxParamHostPropSupportsParametricAnimation" /** @brief Property to indicate the min and max range of the parametric input value. - Type - double X 2 - Property Set - parameter descriptor (read/write only), and instance (read only) - Default Value - (0, 1) - Valid Values - any pair of numbers so that the first is less than the second. This controls the min and max values that the parameter will be evaluated at. */ #define kOfxParamPropParametricRange "OfxParamPropParametricRange" /*@}*/ /*@}*/ /** @brief The OFX suite used to define and manipulate 'parametric' parameters. This is an optional suite. Parametric parameters are in effect 'functions' a plug-in can ask a host to arbitrarily evaluate for some value 'x'. A classic use case would be for constructing look-up tables, a plug-in would ask the host to evaluate one at multiple values from 0 to 1 and use that to fill an array. A host would probably represent this to a user as a cubic curve in a standard curve editor interface, or possibly through scripting. The user would then use this to define the 'shape' of the parameter. The evaluation of such params is not the same as animation, they are returning values based on some arbitrary argument orthogonal to time, so to evaluate such a param, you need to pass a parametric position and time. Often, you would want such a parametric parameter to be multi-dimensional, for example, a colour look-up table might want three values, one for red, green and blue. Rather than declare three separate parametric parameters, it would be better to have one such parameter with multiple values in it. The major complication with these parameters is how to allow a plug-in to set values, and defaults. The default default value of a parametric curve is to be an identity lookup. If a plugin wishes to set a different default value for a curve, it can use the suite to set key/value pairs on the \em descriptor of the param. When a new instance is made, it will have these curve values as a default. */ typedef struct OfxParametricParameterSuiteV1 { /** @brief Evaluates a parametric parameter \arg \c param handle to the parametric parameter \arg \c curveIndex which dimension to evaluate \arg \c time the time to evaluate to the parametric param at \arg \c parametricPosition the position to evaluate the parametric param at \arg \c returnValue pointer to a double where a value is returned @returns - ::kOfxStatOK - all was fine - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrBadIndex - the curve index was invalid */ OfxStatus (*parametricParamGetValue)(OfxParamHandle param, int curveIndex, OfxTime time, double parametricPosition, double *returnValue); /** @brief Returns the number of control points in the parametric param. \arg \c param handle to the parametric parameter \arg \c curveIndex which dimension to check \arg \c time the time to check \arg \c returnValue pointer to an integer where the value is returned. @returns - ::kOfxStatOK - all was fine - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrBadIndex - the curve index was invalid */ OfxStatus (*parametricParamGetNControlPoints)(OfxParamHandle param, int curveIndex, double time, int *returnValue); /** @brief Returns the key/value pair of the nth control point. \arg \c param handle to the parametric parameter \arg \c curveIndex which dimension to check \arg \c time the time to check \arg \c nthCtl the nth control point to get the value of \arg \c key pointer to a double where the key will be returned \arg \c value pointer to a double where the value will be returned @returns - ::kOfxStatOK - all was fine - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrUnknown - if the type is unknown */ OfxStatus (*parametricParamGetNthControlPoint)(OfxParamHandle param, int curveIndex, double time, int nthCtl, double *key, double *value); /** @brief Modifies an existing control point on a curve \arg \c param handle to the parametric parameter \arg \c curveIndex which dimension to set \arg \c time the time to set the value at \arg \c nthCtl the control point to modify \arg \c key key of the control point \arg \c value value of the control point \arg \c addAnimationKey if the param is an animatable, setting this to true will force an animation keyframe to be set as well as a curve key, otherwise if false, a key will only be added if the curve is already animating. @returns - ::kOfxStatOK - all was fine - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrUnknown - if the type is unknown This modifies an existing control point. Note that by changing key, the order of the control point may be modified (as you may move it before or after anther point). So be careful when iterating over a curves control points and you change a key. */ OfxStatus (*parametricParamSetNthControlPoint)(OfxParamHandle param, int curveIndex, double time, int nthCtl, double key, double value, bool addAnimationKey); /** @brief Adds a control point to the curve. \arg \c param handle to the parametric parameter \arg \c curveIndex which dimension to set \arg \c time the time to set the value at \arg \c key key of the control point \arg \c value value of the control point \arg \c addAnimationKey if the param is an animatable, setting this to true will force an animation keyframe to be set as well as a curve key, otherwise if false, a key will only be added if the curve is already animating. @returns - ::kOfxStatOK - all was fine - ::kOfxStatErrBadHandle - if the parameter handle was invalid - ::kOfxStatErrUnknown - if the type is unknown This will add a new control point to the given dimension of a parametric parameter. If a key exists sufficiently close to 'key', then it will be set to the indicated control point. */ OfxStatus (*parametricParamAddControlPoint)(OfxParamHandle param, int curveIndex, double time, double key, double value, bool addAnimationKey); /** @brief Deletes the nth control point from a parametric param. \arg \c param handle to the parametric parameter \arg \c curveIndex which dimension to delete \arg \c nthCtl the control point to delete */ OfxStatus (*parametricParamDeleteControlPoint)(OfxParamHandle param, int curveIndex, int nthCtl); /** @brief Delete all curve control points on the given param. \arg \c param handle to the parametric parameter \arg \c curveIndex which dimension to clear */ OfxStatus (*parametricParamDeleteAllControlPoints)(OfxParamHandle param, int curveIndex); } OfxParametricParameterSuiteV1; #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxPixels.h000664 000000 000000 00000002514 15172202314 023176 0ustar00rootroot000000 000000 #ifndef _ofxPixels_h_ #define _ofxPixels_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #ifdef __cplusplus extern "C" { #endif /** @file ofxPixels.h Contains pixel struct definitions */ /** @brief Defines an 8 bit per component RGBA pixel */ typedef struct OfxRGBAColourB { unsigned char r, g, b, a; }OfxRGBAColourB; /** @brief Defines a 16 bit per component RGBA pixel */ typedef struct OfxRGBAColourS { unsigned short r, g, b, a; }OfxRGBAColourS; /** @brief Defines a floating point component RGBA pixel */ typedef struct OfxRGBAColourF { float r, g, b, a; }OfxRGBAColourF; /** @brief Defines a double precision floating point component RGBA pixel */ typedef struct OfxRGBAColourD { double r, g, b, a; }OfxRGBAColourD; /** @brief Defines an 8 bit per component RGB pixel */ typedef struct OfxRGBColourB { unsigned char r, g, b; }OfxRGBColourB; /** @brief Defines a 16 bit per component RGB pixel */ typedef struct OfxRGBColourS { unsigned short r, g, b; }OfxRGBColourS; /** @brief Defines a floating point component RGB pixel */ typedef struct OfxRGBColourF { float r, g, b; }OfxRGBColourF; /** @brief Defines a double precision floating point component RGB pixel */ typedef struct OfxRGBColourD { double r, g, b; }OfxRGBColourD; #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxProgress.h000664 000000 000000 00000014631 15172202314 023541 0ustar00rootroot000000 000000 // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #ifndef _ofxProgressSuite_h_ #define _ofxProgressSuite_h_ /** @brief suite for displaying a progress bar */ #define kOfxProgressSuite "OfxProgressSuite" /** @brief A suite that provides progress feedback from a plugin to an application A plugin instance can initiate, update and close a progress indicator with this suite. This is an optional suite in the Image Effect API. API V1.4: Amends the documentation of progress suite V1 so that it is expected that it can be raised in a modal manner and have a "cancel" button when invoked in instanceChanged. Plugins that perform analysis post an appropriate message, raise the progress monitor in a modal manner and should poll to see if processing has been aborted. Any cancellation should be handled gracefully by the plugin (eg: reset analysis parameters to default values), clear allocated memory... Many hosts already operate as described above. kOfxStatReplyNo should be returned to the plugin during progressUpdate when the user presses cancel. Suite V2: Adds an ID that can be looked up for internationalisation and so on. When a new version is introduced, because plug-ins need to support old versions, and plug-in's new releases are not necessary in synch with hosts (or users don't immediately update), best practice is to support the 2 suite versions. That is, the plugin should check if V2 exists; if not then check if V1 exists. This way a graceful transition is guaranteed. So plugin should fetchSuite passing 2, (OfxProgressSuiteV2*) fetchSuite(mHost->mHost->host, kOfxProgressSuite,2); and if no success pass (OfxProgressSuiteV1*) fetchSuite(mHost->mHost->host, kOfxProgressSuite,1); */ typedef struct OfxProgressSuiteV1 { /** @brief Initiate a progress bar display. Call this to initiate the display of a progress bar. \arg \c effectInstance the instance of the plugin this progress bar is associated with. It cannot be NULL. \arg \c label a text label to display in any message portion of the progress object's user interface. A UTF8 string. \pre - There is no currently ongoing progress display for this instance. \returns - ::kOfxStatOK - the handle is now valid for use - ::kOfxStatFailed - the progress object failed for some reason - ::kOfxStatErrBadHandle - effectInstance was invalid */ OfxStatus (*progressStart)(void *effectInstance, const char *label); /** @brief Indicate how much of the processing task has been completed and reports on any abort status. \arg \c effectInstance the instance of the plugin this progress bar is associated with. It cannot be NULL. \arg \c progress a number between 0.0 and 1.0 indicating what proportion of the current task has been processed. \returns - ::kOfxStatOK - the progress object was successfully updated and the task should continue - ::kOfxStatReplyNo - the progress object was successfully updated and the task should abort - ::kOfxStatErrBadHandle - the progress handle was invalid, */ OfxStatus (*progressUpdate)(void *effectInstance, double progress); /** @brief Signal that we are finished with the progress meter. Call this when you are done with the progress meter and no longer need it displayed. \arg \c effectInstance the instance of the plugin this progress bar is associated with. It cannot be NULL. \post - you can no longer call progressUpdate on the instance \returns - ::kOfxStatOK - the progress object was successfully closed - ::kOfxStatErrBadHandle - the progress handle was invalid, */ OfxStatus (*progressEnd)(void *effectInstance); } OfxProgressSuiteV1 ; typedef struct OfxProgressSuiteV2 { /** @brief Initiate a progress bar display. Call this to initiate the display of a progress bar. \arg \c effectInstance the instance of the plugin this progress bar is associated with. It cannot be NULL. \arg \c message a text label to display in any message portion of the progress object's user interface. A UTF8 string. \arg \c messageId plugin-specified id to associate with this message. If overriding the message in an XML resource, the message is identified with this, this may be NULL, or "", in which case no override will occur. New in V2 of this suite. \pre - There is no currently ongoing progress display for this instance. \returns - ::kOfxStatOK - the handle is now valid for use - ::kOfxStatFailed - the progress object failed for some reason - ::kOfxStatErrBadHandle - effectInstance was invalid */ OfxStatus (*progressStart)(void *effectInstance, const char *message, const char *messageid); /** @brief Indicate how much of the processing task has been completed and reports on any abort status. \arg \c effectInstance the instance of the plugin this progress bar is associated with. It cannot be NULL. \arg \c progress a number between 0.0 and 1.0 indicating what proportion of the current task has been processed. \returns - ::kOfxStatOK - the progress object was successfully updated and the task should continue - ::kOfxStatReplyNo - the progress object was successfully updated and the task should abort - ::kOfxStatErrBadHandle - the progress handle was invalid, */ OfxStatus (*progressUpdate)(void *effectInstance, double progress); /** @brief Signal that we are finished with the progress meter. Call this when you are done with the progress meter and no longer need it displayed. \arg \c effectInstance the instance of the plugin this progress bar is associated with. It cannot be NULL. \post - you can no longer call progressUpdate on the instance \returns - ::kOfxStatOK - the progress object was successfully closed - ::kOfxStatErrBadHandle - the progress handle was invalid, */ OfxStatus (*progressEnd)(void *effectInstance); } OfxProgressSuiteV2 ; #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxProperty.h000664 000000 000000 00000025546 15172202314 023570 0ustar00rootroot000000 000000 #ifndef _ofxPropertyHost_h_ #define _ofxPropertyHost_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #include "ofxCore.h" #ifdef __cplusplus extern "C" { #endif /** @file ofxProperty.h Contains the API for manipulating generic properties. For more details see \ref PropertiesPage. */ #define kOfxPropertySuite "OfxPropertySuite" /** @brief The OFX suite used to access properties on OFX objects. */ typedef struct OfxPropertySuiteV1 { /** @brief Set a single value in a pointer property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c index for multidimenstional properties and is dimension of the one we are setting \arg \c value value of the property we are setting @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex - ::kOfxStatErrValue */ OfxStatus (*propSetPointer)(OfxPropertySetHandle properties, const char *property, int index, void *value); /** @brief Set a single value in a string property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c index for multidimenstional properties and is dimension of the one we are setting \arg \c value value of the property we are setting @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex - ::kOfxStatErrValue */ OfxStatus (*propSetString) (OfxPropertySetHandle properties, const char *property, int index, const char *value); /** @brief Set a single value in a double property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c index for multidimenstional properties and is dimension of the one we are setting \arg \c value value of the property we are setting @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex - ::kOfxStatErrValue */ OfxStatus (*propSetDouble) (OfxPropertySetHandle properties, const char *property, int index, double value); /** @brief Set a single value in an int property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c index for multidimenstional properties and is dimension of the one we are setting \arg \c value value of the property we are setting @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex - ::kOfxStatErrValue */ OfxStatus (*propSetInt) (OfxPropertySetHandle properties, const char *property, int index, int value); /** @brief Set multiple values of the pointer property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c count number of values we are setting in that property (ie: indices 0..count-1) \arg \c value pointer to an array of property values @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex - ::kOfxStatErrValue */ OfxStatus (*propSetPointerN)(OfxPropertySetHandle properties, const char *property, int count, void *const*value); /** @brief Set multiple values of a string property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c count number of values we are setting in that property (ie: indices 0..count-1) \arg \c value pointer to an array of property values @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex - ::kOfxStatErrValue */ OfxStatus (*propSetStringN) (OfxPropertySetHandle properties, const char *property, int count, const char *const*value); /** @brief Set multiple values of a double property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c count number of values we are setting in that property (ie: indices 0..count-1) \arg \c value pointer to an array of property values @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex - ::kOfxStatErrValue */ OfxStatus (*propSetDoubleN) (OfxPropertySetHandle properties, const char *property, int count, const double *value); /** @brief Set multiple values of an int property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c count number of values we are setting in that property (ie: indices 0..count-1) \arg \c value pointer to an array of property values @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex - ::kOfxStatErrValue */ OfxStatus (*propSetIntN) (OfxPropertySetHandle properties, const char *property, int count, const int *value); /** @brief Get a single value from a pointer property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c index refers to the index of a multi-dimensional property \arg \c value pointer the return location @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex */ OfxStatus (*propGetPointer)(OfxPropertySetHandle properties, const char *property, int index, void **value); /** @brief Get a single value of a string property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c index refers to the index of a multi-dimensional property \arg \c value pointer the return location @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex */ OfxStatus (*propGetString) (OfxPropertySetHandle properties, const char *property, int index, char **value); /** @brief Get a single value of a double property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c index refers to the index of a multi-dimensional property \arg \c value pointer the return location See the note \ref ArchitectureStrings for how to deal with strings. @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex */ OfxStatus (*propGetDouble) (OfxPropertySetHandle properties, const char *property, int index, double *value); /** @brief Get a single value of an int property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c index refers to the index of a multi-dimensional property \arg \c value pointer the return location @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex */ OfxStatus (*propGetInt) (OfxPropertySetHandle properties, const char *property, int index, int *value); /** @brief Get multiple values of a pointer property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c count number of values we are getting of that property (ie: indices 0..count-1) \arg \c value pointer to an array of where we will return the property values @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex */ OfxStatus (*propGetPointerN)(OfxPropertySetHandle properties, const char *property, int count, void **value); /** @brief Get multiple values of a string property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c count number of values we are getting of that property (ie: indices 0..count-1) \arg \c value pointer to an array of where we will return the property values See the note \ref ArchitectureStrings for how to deal with strings. @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex */ OfxStatus (*propGetStringN) (OfxPropertySetHandle properties, const char *property, int count, char **value); /** @brief Get multiple values of a double property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c count number of values we are getting of that property (ie: indices 0..count-1) \arg \c value pointer to an array of where we will return the property values @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex */ OfxStatus (*propGetDoubleN) (OfxPropertySetHandle properties, const char *property, int count, double *value); /** @brief Get multiple values of an int property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property \arg \c count number of values we are getting of that property (ie: indices 0..count-1) \arg \c value pointer to an array of where we will return the property values @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown - ::kOfxStatErrBadIndex */ OfxStatus (*propGetIntN) (OfxPropertySetHandle properties, const char *property, int count, int *value); /** @brief Resets all dimensions of a property to its default value \arg \c properties handle of the thing holding the property \arg \c property string labelling the property we are resetting @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown */ OfxStatus (*propReset) (OfxPropertySetHandle properties, const char *property); /** @brief Gets the dimension of the property \arg \c properties handle of the thing holding the property \arg \c property string labelling the property we are resetting \arg \c count pointer to an integer where the value is returned @returns - ::kOfxStatOK - ::kOfxStatErrBadHandle - ::kOfxStatErrUnknown */ OfxStatus (*propGetDimension) (OfxPropertySetHandle properties, const char *property, int *count); } OfxPropertySuiteV1; /** \addtogroup ErrorCodes */ /*@{*/ /*@}*/ #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/openfx/openfx/include/ofxTimeLine.h000664 000000 000000 00000005362 15172202314 023444 0ustar00rootroot000000 000000 // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #ifndef _ofxTimeLine_h_ #define _ofxTimeLine_h_ /** @brief Name of the time line suite */ #define kOfxTimeLineSuite "OfxTimeLineSuite" /** @brief Suite to control timelines This suite is used to enquire and control a timeline associated with a plug-in instance. This is an optional suite in the Image Effect API. */ typedef struct OfxTimeLineSuiteV1 { /** @brief Get the time value of the timeline that is controlling to the indicated effect. \arg \c instance is the instance of the effect changing the timeline, cast to a void * \arg \c time pointer through which the timeline value should be returned This function returns the current time value of the timeline associated with the effect instance. @returns - ::kOfxStatOK - the time enquiry was successful - ::kOfxStatFailed - the enquiry failed for some host specific reason - ::kOfxStatErrBadHandle - the effect handle was invalid */ OfxStatus (*getTime)(void *instance, double *time); /** @brief Move the timeline control to the indicated time. \arg \c instance is the instance of the effect changing the timeline, cast to a void * \arg \c time is the time to change the timeline to. This is in the temporal coordinate system of the effect. This function moves the timeline to the indicated frame and returns. Any side effects of the timeline change are also triggered and completed before this returns (for example instance changed actions and renders if the output of the effect is being viewed). @returns - ::kOfxStatOK - the time was changed successfully, will all side effects if the change completed - ::kOfxStatFailed - the change failed for some host specific reason - ::kOfxStatErrBadHandle - the effect handle was invalid - ::kOfxStatErrValue - the time was an illegal value */ OfxStatus (*gotoTime)(void *instance, double time); /** @brief Get the current bounds on a timeline \arg \c instance is the instance of the effect changing the timeline, cast to a void * \arg \c firstTime is the first time on the timeline. This is in the temporal coordinate system of the effect. \arg \c lastTime is last time on the timeline. This is in the temporal coordinate system of the effect. This function @returns - ::kOfxStatOK - the time enquiry was successful - ::kOfxStatFailed - the enquiry failed for some host specific reason - ::kOfxStatErrBadHandle - the effect handle was invalid */ OfxStatus (*getTimeBounds)(void *instance, double *firstTime, double *lastTime); } OfxTimeLineSuiteV1; #endif mlt-7.38.0/src/modules/plus/000775 000000 000000 00000000000 15172202314 015604 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/plus/CMakeLists.txt000664 000000 000000 00000005207 15172202314 020350 0ustar00rootroot000000 000000 add_library(mltplus MODULE subtitles/subtitles.cpp consumer_blipflash.c factory.c filter_affine.c filter_charcoal.c filter_chroma_hold.c filter_chroma.c filter_dynamictext.c filter_dynamic_loudness.c filter_gradientmap.cpp filter_hslprimaries.c filter_hslrange.c filter_invert.c filter_lift_gamma_gain.c filter_loudness.c filter_loudness_meter.c filter_lumakey.c filter_rgblut.c filter_sepia.c filter_shape.c filter_spot_remover.c filter_strobe.c filter_subtitle_feed.cpp filter_subtitle.cpp filter_text.c filter_threshold.c filter_timer.c interp.h producer_blipflash.c producer_count.c producer_pgm.c producer_subtitle.c transition_affine.c ) file(GLOB YML "*.yml") add_custom_target(Other_plus_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltplus) target_compile_options(mltplus PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltplus PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltplus PRIVATE mlt Threads::Threads) if(MSVC) target_link_libraries(mltplus PRIVATE PThreads4W::PThreads4W msvccompat) else() target_link_libraries(mltplus PRIVATE m) endif() if(FFTW3_FOUND) target_sources(mltplus PRIVATE filter_dance.c filter_fft.c) target_link_libraries(mltplus PRIVATE FFTW3::fftw3) target_compile_definitions(mltplus PRIVATE USE_FFTW) install(FILES filter_dance.yml filter_fft.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/plus) endif() if(TARGET PkgConfig::libebur128) target_link_libraries(mltplus PRIVATE PkgConfig::libebur128) else() target_sources(mltplus PRIVATE ebur128/ebur128.c) target_include_directories(mltplus PRIVATE ebur128 ebur128/queue) target_compile_definitions(mltplus PRIVATE USE_INTERNAL_LIBEBUR128) endif() set_target_properties(mltplus PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltplus LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_blipflash.yml filter_affine.yml filter_charcoal.yml filter_chroma_hold.yml filter_chroma.yml filter_dynamic_loudness.yml filter_dynamictext.yml filter_gradientmap.yml filter_hslprimaries.yml filter_hslrange.yml filter_invert.yml filter_lift_gamma_gain.yml filter_loudness_meter.yml filter_loudness.yml filter_lumakey.yml filter_rgblut.yml filter_sepia.yml filter_shape.yml filter_spot_remover.yml filter_strobe.yml filter_subtitle_feed.yml filter_subtitle.yml filter_text.yml filter_threshold.yml filter_timer.yml producer_blipflash.yml producer_count.yml producer_pgm.yml producer_subtitle.yml transition_affine.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/plus ) mlt-7.38.0/src/modules/plus/consumer_blipflash.c000664 000000 000000 00000031746 15172202314 021642 0ustar00rootroot000000 000000 /* * consumer_blipflash.c -- a consumer to measure A/V sync from a blip/flash * source * Copyright (C) 2013 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // mlt Header files #include #include #include // System header files #include #include #include #include #include // Private constants #define SAMPLE_FREQ 48000 #define FLASH_LUMA_THRESHOLD 150 #define BLIP_THRESHOLD 0.5 // Private types typedef struct { int64_t flash_history[2]; int flash_history_count; int64_t blip_history[2]; int blip_history_count; int blip_in_progress; int samples_since_blip; int blip; int flash; int sample_offset; FILE *out_file; int report_frames; } avsync_stats; // Forward references. static int consumer_start(mlt_consumer consumer); static int consumer_stop(mlt_consumer consumer); static int consumer_is_stopped(mlt_consumer consumer); static void *consumer_thread(void *arg); static void consumer_close(mlt_consumer consumer); /** Initialize the consumer. */ mlt_consumer consumer_blipflash_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Allocate the consumer mlt_consumer consumer = mlt_consumer_new(profile); mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES(consumer); avsync_stats *stats = NULL; // If memory allocated and initializes without error if (consumer != NULL) { // Set up start/stop/terminated callbacks consumer->close = consumer_close; consumer->start = consumer_start; consumer->stop = consumer_stop; consumer->is_stopped = consumer_is_stopped; stats = mlt_pool_alloc(sizeof(avsync_stats)); stats->flash_history_count = 0; stats->blip_history_count = 0; stats->blip_in_progress = 0; stats->samples_since_blip = 0; stats->blip = 0; stats->flash = 0; stats->sample_offset = INT_MAX; stats->report_frames = 0; stats->out_file = stdout; if (arg != NULL) { FILE *out_file = mlt_fopen(arg, "w"); if (out_file != NULL) stats->out_file = out_file; } mlt_properties_set_data(consumer_properties, "_stats", stats, 0, NULL, NULL); mlt_properties_set(consumer_properties, "report", "blip"); } // Return this return consumer; } /** Start the consumer. */ static int consumer_start(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Check that we're not already running if (!mlt_properties_get_int(properties, "_running")) { // Allocate a thread pthread_t *thread = calloc(1, sizeof(pthread_t)); // Assign the thread to properties mlt_properties_set_data(properties, "_thread", thread, sizeof(pthread_t), free, NULL); // Set the running state mlt_properties_set_int(properties, "_running", 1); // Create the thread pthread_create(thread, NULL, consumer_thread, consumer); } return 0; } /** Stop the consumer. */ static int consumer_stop(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Check that we're running if (mlt_properties_get_int(properties, "_running")) { // Get the thread pthread_t *thread = mlt_properties_get_data(properties, "_thread", NULL); // Stop the thread mlt_properties_set_int(properties, "_running", 0); // Wait for termination if (thread) pthread_join(*thread, NULL); } return 0; } /** Determine if the consumer is stopped. */ static int consumer_is_stopped(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); return !mlt_properties_get_int(properties, "_running"); } static void detect_flash(mlt_frame frame, mlt_position pos, double fps, avsync_stats *stats) { int width = 0; int height = 0; mlt_image_format format = mlt_image_yuv422; uint8_t *image = NULL; int error = mlt_frame_get_image(frame, &image, &format, &width, &height, 0); if (!error && format == mlt_image_yuv422 && image != NULL) { int i, j = 0; int y_accumulator = 0; // Add up the luma values from 4 samples in 4 different quadrants. for (i = 1; i < 3; i++) { int x = (width / 3) * i; x = x - x % 2; // Make sure this is a luma sample for (j = 1; j < 3; j++) { int y = (height / 3) * j; y_accumulator += image[y * height * 2 + x * 2]; } } // If the average luma value is > 150, assume it is a flash. stats->flash = (y_accumulator / 4) > FLASH_LUMA_THRESHOLD; } if (stats->flash) { stats->flash_history[1] = stats->flash_history[0]; stats->flash_history[0] = mlt_audio_calculate_samples_to_position(fps, SAMPLE_FREQ, pos); if (stats->flash_history_count < 2) { stats->flash_history_count++; } } } static void detect_blip(mlt_frame frame, mlt_position pos, double fps, avsync_stats *stats) { int frequency = SAMPLE_FREQ; int channels = 1; int samples = mlt_audio_calculate_frame_samples(fps, frequency, pos); mlt_audio_format format = mlt_audio_float; float *buffer = NULL; int error = mlt_frame_get_audio(frame, (void **) &buffer, &format, &frequency, &channels, &samples); if (!error && format == mlt_audio_float && buffer != NULL) { int i = 0; for (i = 0; i < samples; i++) { if (!stats->blip_in_progress) { if (buffer[i] > BLIP_THRESHOLD || buffer[i] < -BLIP_THRESHOLD) { // This sample must start a blip stats->blip_in_progress = 1; stats->samples_since_blip = 0; stats->blip_history[1] = stats->blip_history[0]; stats->blip_history[0] = mlt_audio_calculate_samples_to_position(fps, SAMPLE_FREQ, pos); stats->blip_history[0] += i; if (stats->blip_history_count < 2) { stats->blip_history_count++; } stats->blip = 1; } } else { if (buffer[i] > -BLIP_THRESHOLD && buffer[i] < BLIP_THRESHOLD) { if (++stats->samples_since_blip > frequency / 1000) { // One ms of silence means the blip is over stats->blip_in_progress = 0; stats->samples_since_blip = 0; } } else { stats->samples_since_blip = 0; } } } } } static void calculate_sync(avsync_stats *stats) { if (stats->blip || stats->flash) { if (stats->flash_history_count > 0 && stats->blip_history_count > 0 && stats->blip_history[0] == stats->flash_history[0]) { // The flash and blip occurred at the same time. stats->sample_offset = 0; } if (stats->flash_history_count > 1 && stats->blip_history_count > 0 && stats->blip_history[0] <= stats->flash_history[0] && stats->blip_history[0] >= stats->flash_history[1]) { // The latest blip occurred between two flashes if (stats->flash_history[0] - stats->blip_history[0] > stats->blip_history[0] - stats->flash_history[1]) { // Blip is closer to the previous flash. // F1---B0--------F0 // ^----^ // Video leads audio (negative number). stats->sample_offset = (int) (stats->flash_history[1] - stats->blip_history[0]); } else { // Blip is closer to the current flash. // F1--------B0---F0 // ^----^ // Audio leads video (positive number). stats->sample_offset = (int) (stats->flash_history[0] - stats->blip_history[0]); } } else if (stats->blip_history_count > 1 && stats->flash_history_count > 0 && stats->flash_history[0] <= stats->blip_history[0] && stats->flash_history[0] >= stats->blip_history[1]) { // The latest flash occurred between two blips if (stats->blip_history[0] - stats->flash_history[0] > stats->flash_history[0] - stats->blip_history[1]) { // Flash is closer to the previous blip. // B1---F0--------B0 // ^----^ // Audio leads video (positive number). stats->sample_offset = (int) (stats->flash_history[0] - stats->blip_history[1]); } else { // Flash is closer to the latest blip. // B1--------F0---B0 // ^----^ // Video leads audio (negative number). stats->sample_offset = (int) (stats->flash_history[0] - stats->blip_history[0]); } } } } static void report_results(avsync_stats *stats, mlt_position pos) { if (stats->report_frames || stats->blip) { if (stats->sample_offset == INT_MAX) { fprintf(stats->out_file, MLT_POSITION_FMT "\t??\n", pos); } else { // Convert to milliseconds. double ms_offset = (double) stats->sample_offset * 1000.0 / (double) SAMPLE_FREQ; fprintf(stats->out_file, MLT_POSITION_FMT "\t%02.02f\n", pos, ms_offset); } } stats->blip = 0; stats->flash = 0; } /** The main thread - the argument is simply the consumer. */ static void *consumer_thread(void *arg) { // Map the argument to the object mlt_consumer consumer = arg; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Convenience functionality int terminate_on_pause = mlt_properties_get_int(properties, "terminate_on_pause"); int terminated = 0; // Frame and size mlt_frame frame = NULL; // Loop while running while (!terminated && mlt_properties_get_int(properties, "_running")) { // Get the frame frame = mlt_consumer_rt_frame(consumer); // Check for termination if (terminate_on_pause && frame != NULL) terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0; // Check that we have a frame to work with if (frame) { avsync_stats *stats = mlt_properties_get_data(properties, "_stats", NULL); double fps = mlt_properties_get_double(properties, "fps"); mlt_position pos = mlt_frame_get_position(frame); if (!strcmp(mlt_properties_get(properties, "report"), "frame")) { stats->report_frames = 1; } else { stats->report_frames = 0; } detect_flash(frame, pos, fps, stats); detect_blip(frame, pos, fps, stats); calculate_sync(stats); report_results(stats, pos); // Close the frame mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); mlt_frame_close(frame); } } // Indicate that the consumer is stopped mlt_properties_set_int(properties, "_running", 0); mlt_consumer_stopped(consumer); return NULL; } /** Close the consumer. */ static void consumer_close(mlt_consumer consumer) { mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES(consumer); avsync_stats *stats = mlt_properties_get_data(consumer_properties, "_stats", NULL); // Stop the consumer mlt_consumer_stop(consumer); // Close the file if (stats->out_file != stdout) { fclose(stats->out_file); } // Clean up memory mlt_pool_release(stats); // Close the parent mlt_consumer_close(consumer); // Free the memory free(consumer); } mlt-7.38.0/src/modules/plus/consumer_blipflash.yml000664 000000 000000 00000001430 15172202314 022204 0ustar00rootroot000000 000000 schema_version: 7.0 type: consumer identifier: blipflash title: Blip Flash version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video - Audio description: > Calculate the A/V sync for a blip flash source. Sync can be recalculated whenever a blip or a flash is detected. parameters: - identifier: resource argument: yes title: Report File type: string description: > The file to report the results to. If empty, the results will be reported to standard out. required: no widget: filesave - identifier: report title: Report Style type: string description: > When to report sync - every frame or only when blips occur. default: blip values: - blip - frame mutable: yes widget: combo mlt-7.38.0/src/modules/plus/ebur128/000775 000000 000000 00000000000 15172202314 016774 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/plus/ebur128/COPYING000664 000000 000000 00000002043 15172202314 020026 0ustar00rootroot000000 000000 Copyright (c) 2011 Jan Kokemüller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mlt-7.38.0/src/modules/plus/ebur128/ebur128.c000664 000000 000000 00000150102 15172202314 020327 0ustar00rootroot000000 000000 /* See COPYING file for copyright and license details. */ #include "ebur128.h" #include #include #include /* You may have to define _USE_MATH_DEFINES if you use MSVC */ #include #include /* This can be replaced by any BSD-like queue implementation. */ #include #define CHECK_ERROR(condition, errorcode, goto_point) \ if ((condition)) { \ errcode = (errorcode); \ goto goto_point; \ } #define EBUR128_MAX(a, b) (((a) > (b)) ? (a) : (b)) static int safe_size_mul(size_t nmemb, size_t size, size_t* result) { /* Adapted from OpenBSD reallocarray. */ #define MUL_NO_OVERFLOW (((size_t) 1) << (sizeof(size_t) * 4)) if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && /**/ nmemb > 0 && ((size_t) (-1)) / nmemb < size) { return 1; } #undef MUL_NO_OVERFLOW *result = nmemb * size; return 0; } STAILQ_HEAD(ebur128_double_queue, ebur128_dq_entry); struct ebur128_dq_entry { double z; STAILQ_ENTRY(ebur128_dq_entry) entries; }; #define ALMOST_ZERO 0.000001 #define FILTER_STATE_SIZE 5 typedef struct { unsigned int count; /* Number of coefficients in this subfilter */ unsigned int* index; /* Delay index of corresponding filter coeff */ double* coeff; /* List of subfilter coefficients */ } interp_filter; typedef struct { /* Data structure for polyphase FIR interpolator */ unsigned int factor; /* Interpolation factor of the interpolator */ unsigned int taps; /* Taps (prefer odd to increase zero coeffs) */ unsigned int channels; /* Number of channels */ unsigned int delay; /* Size of delay buffer */ interp_filter* filter; /* List of subfilters (one for each factor) */ float** z; /* List of delay buffers (one for each channel) */ unsigned int zi; /* Current delay buffer index */ } interpolator; /** BS.1770 filter state. */ typedef double filter_state[FILTER_STATE_SIZE]; struct ebur128_state_internal { /** Filtered audio data (used as ring buffer). */ double* audio_data; /** Size of audio_data array. */ size_t audio_data_frames; /** Current index for audio_data. */ size_t audio_data_index; /** How many frames are needed for a gating block. Will correspond to 400ms * of audio at initialization, and 100ms after the first block (75% overlap * as specified in the 2011 revision of BS1770). */ unsigned long needed_frames; /** The channel map. Has as many elements as there are channels. */ int* channel_map; /** How many samples fit in 100ms (rounded). */ unsigned long samples_in_100ms; /** BS.1770 filter coefficients (nominator). */ double b[5]; /** BS.1770 filter coefficients (denominator). */ double a[5]; /** one filter_state per channel. */ filter_state* v; /** Linked list of block energies. */ struct ebur128_double_queue block_list; unsigned long block_list_max; unsigned long block_list_size; /** Linked list of 3s-block energies, used to calculate LRA. */ struct ebur128_double_queue short_term_block_list; unsigned long st_block_list_max; unsigned long st_block_list_size; int use_histogram; unsigned long* block_energy_histogram; unsigned long* short_term_block_energy_histogram; /** Keeps track of when a new short term block is needed. */ size_t short_term_frame_counter; /** Maximum sample peak, one per channel */ double* sample_peak; double* prev_sample_peak; /** Maximum true peak, one per channel */ double* true_peak; double* prev_true_peak; interpolator* interp; float* resampler_buffer_input; size_t resampler_buffer_input_frames; float* resampler_buffer_output; size_t resampler_buffer_output_frames; /** The maximum window duration in ms. */ unsigned long window; unsigned long history; }; static double relative_gate = -10.0; /* Those will be calculated when initializing the library */ static double relative_gate_factor; static double minus_twenty_decibels; static double histogram_energies[1000]; static double histogram_energy_boundaries[1001]; static interpolator* interp_create(unsigned int taps, unsigned int factor, unsigned int channels) { int errcode = EBUR128_SUCCESS; interpolator* interp; unsigned int j; interp = (interpolator*) calloc(1, sizeof(interpolator)); CHECK_ERROR(!interp, EBUR128_ERROR_NOMEM, exit); interp->taps = taps; interp->factor = factor; interp->channels = channels; interp->delay = (interp->taps + interp->factor - 1) / interp->factor; /* Initialize the filter memory * One subfilter per interpolation factor. */ interp->filter = (interp_filter*) calloc(interp->factor, sizeof(*interp->filter)); CHECK_ERROR(!interp->filter, EBUR128_ERROR_NOMEM, free_interp); for (j = 0; j < interp->factor; j++) { interp->filter[j].index = (unsigned int*) calloc(interp->delay, sizeof(unsigned int)); interp->filter[j].coeff = (double*) calloc(interp->delay, sizeof(double)); CHECK_ERROR(!interp->filter[j].index || !interp->filter[j].coeff, EBUR128_ERROR_NOMEM, free_filter_index_coeff); } /* One delay buffer per channel. */ interp->z = (float**) calloc(interp->channels, sizeof(float*)); CHECK_ERROR(!interp->z, EBUR128_ERROR_NOMEM, free_filter_index_coeff); for (j = 0; j < interp->channels; j++) { interp->z[j] = (float*) calloc(interp->delay, sizeof(float)); CHECK_ERROR(!interp->z[j], EBUR128_ERROR_NOMEM, free_filter_z); } /* Calculate the filter coefficients */ for (j = 0; j < interp->taps; j++) { /* Calculate sinc */ double m = (double) j - (double) (interp->taps - 1) / 2.0; double c = 1.0; if (fabs(m) > ALMOST_ZERO) { c = sin(m * M_PI / interp->factor) / (m * M_PI / interp->factor); } /* Apply Hanning window */ c *= 0.5 * (1 - cos(2 * M_PI * j / (interp->taps - 1))); if (fabs(c) > ALMOST_ZERO) { /* Ignore any zero coeffs. */ /* Put the coefficient into the correct subfilter */ unsigned int f = j % interp->factor; unsigned int t = interp->filter[f].count++; interp->filter[f].coeff[t] = c; interp->filter[f].index[t] = j / interp->factor; } } if (errcode == EBUR128_SUCCESS) { return interp; } free_filter_z: for (j = 0; j < interp->channels; j++) { free(interp->z[j]); } free(interp->z); free_filter_index_coeff: for (j = 0; j < interp->factor; j++) { free(interp->filter[j].index); free(interp->filter[j].coeff); } free(interp->filter); free_interp: free(interp); exit: return NULL; } static void interp_destroy(interpolator* interp) { unsigned int j = 0; if (!interp) { return; } for (j = 0; j < interp->factor; j++) { free(interp->filter[j].index); free(interp->filter[j].coeff); } free(interp->filter); for (j = 0; j < interp->channels; j++) { free(interp->z[j]); } free(interp->z); free(interp); } static size_t interp_process(interpolator* interp, size_t frames, float* in, float* out) { size_t frame = 0; unsigned int chan = 0; unsigned int f = 0; unsigned int t = 0; unsigned int out_stride = interp->channels * interp->factor; float* outp = 0; double acc = 0; double c = 0; for (frame = 0; frame < frames; frame++) { for (chan = 0; chan < interp->channels; chan++) { /* Add sample to delay buffer */ interp->z[chan][interp->zi] = *in++; /* Apply coefficients */ outp = out + chan; for (f = 0; f < interp->factor; f++) { acc = 0.0; for (t = 0; t < interp->filter[f].count; t++) { int i = (int) interp->zi - (int) interp->filter[f].index[t]; if (i < 0) { i += (int) interp->delay; } c = interp->filter[f].coeff[t]; acc += (double) interp->z[chan][i] * c; } *outp = (float) acc; outp += interp->channels; } } out += out_stride; interp->zi++; if (interp->zi == interp->delay) { interp->zi = 0; } } return frames * interp->factor; } static int ebur128_init_filter(ebur128_state* st) { int errcode = EBUR128_SUCCESS; int i, j; double f0 = 1681.974450955533; double G = 3.999843853973347; double Q = 0.7071752369554196; double K = tan(M_PI * f0 / (double) st->samplerate); double Vh = pow(10.0, G / 20.0); double Vb = pow(Vh, 0.4996667741545416); double pb[3] = { 0.0, 0.0, 0.0 }; double pa[3] = { 1.0, 0.0, 0.0 }; double rb[3] = { 1.0, -2.0, 1.0 }; double ra[3] = { 1.0, 0.0, 0.0 }; double a0 = 1.0 + K / Q + K * K; pb[0] = (Vh + Vb * K / Q + K * K) / a0; pb[1] = 2.0 * (K * K - Vh) / a0; pb[2] = (Vh - Vb * K / Q + K * K) / a0; pa[1] = 2.0 * (K * K - 1.0) / a0; pa[2] = (1.0 - K / Q + K * K) / a0; /* fprintf(stderr, "%.14f %.14f %.14f %.14f %.14f\n", b1[0], b1[1], b1[2], a1[1], a1[2]); */ f0 = 38.13547087602444; Q = 0.5003270373238773; K = tan(M_PI * f0 / (double) st->samplerate); ra[1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K); ra[2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K); /* fprintf(stderr, "%.14f %.14f\n", a2[1], a2[2]); */ st->d->b[0] = pb[0] * rb[0]; st->d->b[1] = pb[0] * rb[1] + pb[1] * rb[0]; st->d->b[2] = pb[0] * rb[2] + pb[1] * rb[1] + pb[2] * rb[0]; st->d->b[3] = pb[1] * rb[2] + pb[2] * rb[1]; st->d->b[4] = pb[2] * rb[2]; st->d->a[0] = pa[0] * ra[0]; st->d->a[1] = pa[0] * ra[1] + pa[1] * ra[0]; st->d->a[2] = pa[0] * ra[2] + pa[1] * ra[1] + pa[2] * ra[0]; st->d->a[3] = pa[1] * ra[2] + pa[2] * ra[1]; st->d->a[4] = pa[2] * ra[2]; st->d->v = (filter_state*) malloc(st->channels * sizeof(filter_state)); CHECK_ERROR(!st->d->v, EBUR128_ERROR_NOMEM, exit); for (i = 0; i < (int) st->channels; ++i) { for (j = 0; j < FILTER_STATE_SIZE; ++j) { st->d->v[i][j] = 0.0; } } exit: return errcode; } static int ebur128_init_channel_map(ebur128_state* st) { size_t i; st->d->channel_map = (int*) malloc(st->channels * sizeof(int)); if (!st->d->channel_map) { return EBUR128_ERROR_NOMEM; } if (st->channels == 4) { st->d->channel_map[0] = EBUR128_LEFT; st->d->channel_map[1] = EBUR128_RIGHT; st->d->channel_map[2] = EBUR128_LEFT_SURROUND; st->d->channel_map[3] = EBUR128_RIGHT_SURROUND; } else if (st->channels == 5) { st->d->channel_map[0] = EBUR128_LEFT; st->d->channel_map[1] = EBUR128_RIGHT; st->d->channel_map[2] = EBUR128_CENTER; st->d->channel_map[3] = EBUR128_LEFT_SURROUND; st->d->channel_map[4] = EBUR128_RIGHT_SURROUND; } else { for (i = 0; i < st->channels; ++i) { switch (i) { case 0: st->d->channel_map[i] = EBUR128_LEFT; break; case 1: st->d->channel_map[i] = EBUR128_RIGHT; break; case 2: st->d->channel_map[i] = EBUR128_CENTER; break; case 3: st->d->channel_map[i] = EBUR128_UNUSED; break; case 4: st->d->channel_map[i] = EBUR128_LEFT_SURROUND; break; case 5: st->d->channel_map[i] = EBUR128_RIGHT_SURROUND; break; default: st->d->channel_map[i] = EBUR128_UNUSED; break; } } } return EBUR128_SUCCESS; } static int ebur128_init_resampler(ebur128_state* st) { int errcode = EBUR128_SUCCESS; if (st->samplerate < 96000) { st->d->interp = interp_create(49, 4, st->channels); CHECK_ERROR(!st->d->interp, EBUR128_ERROR_NOMEM, exit) } else if (st->samplerate < 192000) { st->d->interp = interp_create(49, 2, st->channels); CHECK_ERROR(!st->d->interp, EBUR128_ERROR_NOMEM, exit) } else { st->d->resampler_buffer_input = NULL; st->d->resampler_buffer_output = NULL; st->d->interp = NULL; goto exit; } st->d->resampler_buffer_input_frames = st->d->samples_in_100ms * 4; st->d->resampler_buffer_input = (float*) malloc( st->d->resampler_buffer_input_frames * st->channels * sizeof(float)); CHECK_ERROR(!st->d->resampler_buffer_input, EBUR128_ERROR_NOMEM, free_interp) st->d->resampler_buffer_output_frames = st->d->resampler_buffer_input_frames * st->d->interp->factor; st->d->resampler_buffer_output = (float*) malloc( st->d->resampler_buffer_output_frames * st->channels * sizeof(float)); CHECK_ERROR(!st->d->resampler_buffer_output, EBUR128_ERROR_NOMEM, free_input) return errcode; free_interp: interp_destroy(st->d->interp); st->d->interp = NULL; free_input: free(st->d->resampler_buffer_input); st->d->resampler_buffer_input = NULL; exit: return errcode; } static void ebur128_destroy_resampler(ebur128_state* st) { free(st->d->resampler_buffer_input); st->d->resampler_buffer_input = NULL; free(st->d->resampler_buffer_output); st->d->resampler_buffer_output = NULL; interp_destroy(st->d->interp); st->d->interp = NULL; } void ebur128_get_version(int* major, int* minor, int* patch) { *major = EBUR128_VERSION_MAJOR; *minor = EBUR128_VERSION_MINOR; *patch = EBUR128_VERSION_PATCH; } #define VALIDATE_MAX_CHANNELS (64) #define VALIDATE_MAX_SAMPLERATE (2822400) #define VALIDATE_CHANNELS_AND_SAMPLERATE(err) \ do { \ if (channels == 0 || channels > VALIDATE_MAX_CHANNELS) { \ return (err); \ } \ \ if (samplerate < 16 || samplerate > VALIDATE_MAX_SAMPLERATE) { \ return (err); \ } \ } while (0); ebur128_state* ebur128_init(unsigned int channels, unsigned long samplerate, int mode) { int result; int errcode; ebur128_state* st; unsigned int i; size_t j; VALIDATE_CHANNELS_AND_SAMPLERATE(NULL); st = (ebur128_state*) malloc(sizeof(ebur128_state)); CHECK_ERROR(!st, 0, exit) st->d = (struct ebur128_state_internal*) malloc( sizeof(struct ebur128_state_internal)); CHECK_ERROR(!st->d, 0, free_state) st->channels = channels; errcode = ebur128_init_channel_map(st); CHECK_ERROR(errcode, 0, free_internal) st->d->sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->sample_peak, 0, free_channel_map) st->d->prev_sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->prev_sample_peak, 0, free_sample_peak) st->d->true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->true_peak, 0, free_prev_sample_peak) st->d->prev_true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->prev_true_peak, 0, free_true_peak) for (i = 0; i < channels; ++i) { st->d->sample_peak[i] = 0.0; st->d->prev_sample_peak[i] = 0.0; st->d->true_peak[i] = 0.0; st->d->prev_true_peak[i] = 0.0; } st->d->use_histogram = mode & EBUR128_MODE_HISTOGRAM ? 1 : 0; st->d->history = ULONG_MAX; st->samplerate = samplerate; st->d->samples_in_100ms = (st->samplerate + 5) / 10; st->mode = mode; if ((mode & EBUR128_MODE_S) == EBUR128_MODE_S) { st->d->window = 3000; } else if ((mode & EBUR128_MODE_M) == EBUR128_MODE_M) { st->d->window = 400; } else { goto free_prev_true_peak; } st->d->audio_data_frames = st->samplerate * st->d->window / 1000; if (st->d->audio_data_frames % st->d->samples_in_100ms) { /* round up to multiple of samples_in_100ms */ st->d->audio_data_frames = (st->d->audio_data_frames + st->d->samples_in_100ms) - (st->d->audio_data_frames % st->d->samples_in_100ms); } st->d->audio_data = (double*) malloc(st->d->audio_data_frames * st->channels * sizeof(double)); CHECK_ERROR(!st->d->audio_data, 0, free_prev_true_peak) for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) { st->d->audio_data[j] = 0.0; } errcode = ebur128_init_filter(st); CHECK_ERROR(errcode, 0, free_audio_data) if (st->d->use_histogram) { st->d->block_energy_histogram = (unsigned long*) malloc(1000 * sizeof(unsigned long)); CHECK_ERROR(!st->d->block_energy_histogram, 0, free_filter) for (i = 0; i < 1000; ++i) { st->d->block_energy_histogram[i] = 0; } } else { st->d->block_energy_histogram = NULL; } if (st->d->use_histogram) { st->d->short_term_block_energy_histogram = (unsigned long*) malloc(1000 * sizeof(unsigned long)); CHECK_ERROR(!st->d->short_term_block_energy_histogram, 0, free_block_energy_histogram) for (i = 0; i < 1000; ++i) { st->d->short_term_block_energy_histogram[i] = 0; } } else { st->d->short_term_block_energy_histogram = NULL; } STAILQ_INIT(&st->d->block_list); st->d->block_list_size = 0; st->d->block_list_max = st->d->history / 100; STAILQ_INIT(&st->d->short_term_block_list); st->d->st_block_list_size = 0; st->d->st_block_list_max = st->d->history / 3000; st->d->short_term_frame_counter = 0; result = ebur128_init_resampler(st); CHECK_ERROR(result, 0, free_short_term_block_energy_histogram) /* the first block needs 400ms of audio data */ st->d->needed_frames = st->d->samples_in_100ms * 4; /* start at the beginning of the buffer */ st->d->audio_data_index = 0; /* initialize static constants */ relative_gate_factor = pow(10.0, relative_gate / 10.0); minus_twenty_decibels = pow(10.0, -20.0 / 10.0); histogram_energy_boundaries[0] = pow(10.0, (-70.0 + 0.691) / 10.0); if (st->d->use_histogram) { for (i = 0; i < 1000; ++i) { histogram_energies[i] = pow(10.0, ((double) i / 10.0 - 69.95 + 0.691) / 10.0); } for (i = 1; i < 1001; ++i) { histogram_energy_boundaries[i] = pow(10.0, ((double) i / 10.0 - 70.0 + 0.691) / 10.0); } } return st; free_short_term_block_energy_histogram: free(st->d->short_term_block_energy_histogram); free_block_energy_histogram: free(st->d->block_energy_histogram); free_filter: free(st->d->v); free_audio_data: free(st->d->audio_data); free_prev_true_peak: free(st->d->prev_true_peak); free_true_peak: free(st->d->true_peak); free_prev_sample_peak: free(st->d->prev_sample_peak); free_sample_peak: free(st->d->sample_peak); free_channel_map: free(st->d->channel_map); free_internal: free(st->d); free_state: free(st); exit: return NULL; } void ebur128_destroy(ebur128_state** st) { struct ebur128_dq_entry* entry; free((*st)->d->short_term_block_energy_histogram); free((*st)->d->block_energy_histogram); free((*st)->d->v); free((*st)->d->audio_data); free((*st)->d->channel_map); free((*st)->d->sample_peak); free((*st)->d->prev_sample_peak); free((*st)->d->true_peak); free((*st)->d->prev_true_peak); while (!STAILQ_EMPTY(&(*st)->d->block_list)) { entry = STAILQ_FIRST(&(*st)->d->block_list); STAILQ_REMOVE_HEAD(&(*st)->d->block_list, entries); free(entry); } while (!STAILQ_EMPTY(&(*st)->d->short_term_block_list)) { entry = STAILQ_FIRST(&(*st)->d->short_term_block_list); STAILQ_REMOVE_HEAD(&(*st)->d->short_term_block_list, entries); free(entry); } ebur128_destroy_resampler(*st); free((*st)->d); free(*st); *st = NULL; } static void ebur128_check_true_peak(ebur128_state* st, size_t frames) { size_t c, i, frames_out; frames_out = interp_process(st->d->interp, frames, st->d->resampler_buffer_input, st->d->resampler_buffer_output); for (i = 0; i < frames_out; ++i) { for (c = 0; c < st->channels; ++c) { double val = (double) st->d->resampler_buffer_output[i * st->channels + c]; if (EBUR128_MAX(val, -val) > st->d->prev_true_peak[c]) { st->d->prev_true_peak[c] = EBUR128_MAX(val, -val); } } } } #if defined(__SSE2_MATH__) || defined(_M_X64) || _M_IX86_FP >= 2 #include #define TURN_ON_FTZ \ unsigned int mxcsr = _mm_getcsr(); \ _mm_setcsr(mxcsr | _MM_FLUSH_ZERO_ON); #define TURN_OFF_FTZ _mm_setcsr(mxcsr); #define FLUSH_MANUALLY #else #warning "manual FTZ is being used, please enable SSE2 (-msse2 -mfpmath=sse)" #define TURN_ON_FTZ #define TURN_OFF_FTZ #define FLUSH_MANUALLY \ st->d->v[c][4] = fabs(st->d->v[c][4]) < DBL_MIN ? 0.0 : st->d->v[c][4]; \ st->d->v[c][3] = fabs(st->d->v[c][3]) < DBL_MIN ? 0.0 : st->d->v[c][3]; \ st->d->v[c][2] = fabs(st->d->v[c][2]) < DBL_MIN ? 0.0 : st->d->v[c][2]; \ st->d->v[c][1] = fabs(st->d->v[c][1]) < DBL_MIN ? 0.0 : st->d->v[c][1]; #endif #define EBUR128_FILTER(type, min_scale, max_scale) \ static void ebur128_filter_##type(ebur128_state* st, const type* src, \ size_t frames) { \ static double scaling_factor = \ EBUR128_MAX(-((double) (min_scale)), (double) (max_scale)); \ \ double* audio_data = st->d->audio_data + st->d->audio_data_index; \ size_t i, c; \ \ TURN_ON_FTZ \ \ if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK) { \ for (c = 0; c < st->channels; ++c) { \ double max = 0.0; \ for (i = 0; i < frames; ++i) { \ double cur = (double) src[i * st->channels + c]; \ if (EBUR128_MAX(cur, -cur) > max) { \ max = EBUR128_MAX(cur, -cur); \ } \ } \ max /= scaling_factor; \ if (max > st->d->prev_sample_peak[c]) { \ st->d->prev_sample_peak[c] = max; \ } \ } \ } \ if ((st->mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK && \ st->d->interp) { \ for (i = 0; i < frames; ++i) { \ for (c = 0; c < st->channels; ++c) { \ st->d->resampler_buffer_input[i * st->channels + c] = \ (float) ((double) src[i * st->channels + c] / scaling_factor); \ } \ } \ ebur128_check_true_peak(st, frames); \ } \ for (c = 0; c < st->channels; ++c) { \ if (st->d->channel_map[c] == EBUR128_UNUSED) { \ continue; \ } \ for (i = 0; i < frames; ++i) { \ st->d->v[c][0] = \ (double) ((double) src[i * st->channels + c] / scaling_factor) - \ st->d->a[1] * st->d->v[c][1] - /**/ \ st->d->a[2] * st->d->v[c][2] - /**/ \ st->d->a[3] * st->d->v[c][3] - /**/ \ st->d->a[4] * st->d->v[c][4]; \ audio_data[i * st->channels + c] = /**/ \ st->d->b[0] * st->d->v[c][0] + /**/ \ st->d->b[1] * st->d->v[c][1] + /**/ \ st->d->b[2] * st->d->v[c][2] + /**/ \ st->d->b[3] * st->d->v[c][3] + /**/ \ st->d->b[4] * st->d->v[c][4]; \ st->d->v[c][4] = st->d->v[c][3]; \ st->d->v[c][3] = st->d->v[c][2]; \ st->d->v[c][2] = st->d->v[c][1]; \ st->d->v[c][1] = st->d->v[c][0]; \ } \ FLUSH_MANUALLY \ } \ TURN_OFF_FTZ \ } EBUR128_FILTER(short, SHRT_MIN, SHRT_MAX) EBUR128_FILTER(int, INT_MIN, INT_MAX) EBUR128_FILTER(float, -1.0f, 1.0f) EBUR128_FILTER(double, -1.0, 1.0) static double ebur128_energy_to_loudness(double energy) { return 10 * (log(energy) / log(10.0)) - 0.691; } static size_t find_histogram_index(double energy) { size_t index_min = 0; size_t index_max = 1000; size_t index_mid; do { index_mid = (index_min + index_max) / 2; if (energy >= histogram_energy_boundaries[index_mid]) { index_min = index_mid; } else { index_max = index_mid; } } while (index_max - index_min != 1); return index_min; } static int ebur128_calc_gating_block(ebur128_state* st, size_t frames_per_block, double* optional_output) { size_t i, c; double sum = 0.0; double channel_sum; for (c = 0; c < st->channels; ++c) { if (st->d->channel_map[c] == EBUR128_UNUSED) { continue; } channel_sum = 0.0; if (st->d->audio_data_index < frames_per_block * st->channels) { for (i = 0; i < st->d->audio_data_index / st->channels; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } for (i = st->d->audio_data_frames - (frames_per_block - st->d->audio_data_index / st->channels); i < st->d->audio_data_frames; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } } else { for (i = st->d->audio_data_index / st->channels - frames_per_block; i < st->d->audio_data_index / st->channels; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } } if (st->d->channel_map[c] == EBUR128_Mp110 || st->d->channel_map[c] == EBUR128_Mm110 || st->d->channel_map[c] == EBUR128_Mp060 || st->d->channel_map[c] == EBUR128_Mm060 || st->d->channel_map[c] == EBUR128_Mp090 || st->d->channel_map[c] == EBUR128_Mm090) { channel_sum *= 1.41; } else if (st->d->channel_map[c] == EBUR128_DUAL_MONO) { channel_sum *= 2.0; } sum += channel_sum; } sum /= (double) frames_per_block; if (optional_output) { *optional_output = sum; return EBUR128_SUCCESS; } if (sum >= histogram_energy_boundaries[0]) { if (st->d->use_histogram) { ++st->d->block_energy_histogram[find_histogram_index(sum)]; } else { struct ebur128_dq_entry* block; if (st->d->block_list_size == st->d->block_list_max) { block = STAILQ_FIRST(&st->d->block_list); STAILQ_REMOVE_HEAD(&st->d->block_list, entries); } else { block = (struct ebur128_dq_entry*) malloc(sizeof(struct ebur128_dq_entry)); if (!block) { return EBUR128_ERROR_NOMEM; } st->d->block_list_size++; } block->z = sum; STAILQ_INSERT_TAIL(&st->d->block_list, block, entries); } } return EBUR128_SUCCESS; } int ebur128_set_channel(ebur128_state* st, unsigned int channel_number, int value) { if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } if (value == EBUR128_DUAL_MONO && (st->channels != 1 || channel_number != 0)) { fprintf(stderr, "EBUR128_DUAL_MONO only works with mono files!\n"); return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } st->d->channel_map[channel_number] = value; return EBUR128_SUCCESS; } int ebur128_change_parameters(ebur128_state* st, unsigned int channels, unsigned long samplerate) { int errcode = EBUR128_SUCCESS; size_t j; /* This is needed to suppress a clang-tidy warning. */ #ifndef __has_builtin #define __has_builtin(x) 0 #endif #if __has_builtin(__builtin_unreachable) if (st->channels == 0) { __builtin_unreachable(); } #endif VALIDATE_CHANNELS_AND_SAMPLERATE(EBUR128_ERROR_NOMEM); if (channels == st->channels && samplerate == st->samplerate) { return EBUR128_ERROR_NO_CHANGE; } free(st->d->audio_data); st->d->audio_data = NULL; if (channels != st->channels) { unsigned int i; free(st->d->channel_map); st->d->channel_map = NULL; free(st->d->sample_peak); st->d->sample_peak = NULL; free(st->d->prev_sample_peak); st->d->prev_sample_peak = NULL; free(st->d->true_peak); st->d->true_peak = NULL; free(st->d->prev_true_peak); st->d->prev_true_peak = NULL; st->channels = channels; errcode = ebur128_init_channel_map(st); CHECK_ERROR(errcode, EBUR128_ERROR_NOMEM, exit) st->d->sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->sample_peak, EBUR128_ERROR_NOMEM, exit) st->d->prev_sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->prev_sample_peak, EBUR128_ERROR_NOMEM, exit) st->d->true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->true_peak, EBUR128_ERROR_NOMEM, exit) st->d->prev_true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->prev_true_peak, EBUR128_ERROR_NOMEM, exit) for (i = 0; i < channels; ++i) { st->d->sample_peak[i] = 0.0; st->d->prev_sample_peak[i] = 0.0; st->d->true_peak[i] = 0.0; st->d->prev_true_peak[i] = 0.0; } } if (samplerate != st->samplerate) { st->samplerate = samplerate; st->d->samples_in_100ms = (st->samplerate + 5) / 10; } /* If we're here, either samplerate or channels * have changed. Re-init filter. */ free(st->d->v); st->d->v = NULL; errcode = ebur128_init_filter(st); CHECK_ERROR(errcode, EBUR128_ERROR_NOMEM, exit) st->d->audio_data_frames = st->samplerate * st->d->window / 1000; if (st->d->audio_data_frames % st->d->samples_in_100ms) { /* round up to multiple of samples_in_100ms */ st->d->audio_data_frames = (st->d->audio_data_frames + st->d->samples_in_100ms) - (st->d->audio_data_frames % st->d->samples_in_100ms); } st->d->audio_data = (double*) malloc(st->d->audio_data_frames * st->channels * sizeof(double)); CHECK_ERROR(!st->d->audio_data, EBUR128_ERROR_NOMEM, exit) for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) { st->d->audio_data[j] = 0.0; } ebur128_destroy_resampler(st); errcode = ebur128_init_resampler(st); CHECK_ERROR(errcode, EBUR128_ERROR_NOMEM, exit) /* the first block needs 400ms of audio data */ st->d->needed_frames = st->d->samples_in_100ms * 4; /* start at the beginning of the buffer */ st->d->audio_data_index = 0; /* reset short term frame counter */ st->d->short_term_frame_counter = 0; exit: return errcode; } int ebur128_set_max_window(ebur128_state* st, unsigned long window) { int errcode = EBUR128_SUCCESS; size_t j; if ((st->mode & EBUR128_MODE_S) == EBUR128_MODE_S && window < 3000) { window = 3000; } else if ((st->mode & EBUR128_MODE_M) == EBUR128_MODE_M && window < 400) { window = 400; } if (window == st->d->window) { return EBUR128_ERROR_NO_CHANGE; } size_t new_audio_data_frames; if (safe_size_mul(st->samplerate, window, &new_audio_data_frames) != 0 || new_audio_data_frames > ((size_t) -1) - st->d->samples_in_100ms) { return EBUR128_ERROR_NOMEM; } if (new_audio_data_frames % st->d->samples_in_100ms) { /* round up to multiple of samples_in_100ms */ new_audio_data_frames = (new_audio_data_frames + st->d->samples_in_100ms) - (new_audio_data_frames % st->d->samples_in_100ms); } size_t new_audio_data_size; if (safe_size_mul(new_audio_data_frames, st->channels * sizeof(double), &new_audio_data_size) != 0) { return EBUR128_ERROR_NOMEM; } double* new_audio_data = (double*) malloc(new_audio_data_size); CHECK_ERROR(!new_audio_data, EBUR128_ERROR_NOMEM, exit) st->d->window = window; free(st->d->audio_data); st->d->audio_data = new_audio_data; st->d->audio_data_frames = new_audio_data_frames; for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) { st->d->audio_data[j] = 0.0; } /* the first block needs 400ms of audio data */ st->d->needed_frames = st->d->samples_in_100ms * 4; /* start at the beginning of the buffer */ st->d->audio_data_index = 0; /* reset short term frame counter */ st->d->short_term_frame_counter = 0; exit: return errcode; } int ebur128_set_max_history(ebur128_state* st, unsigned long history) { if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA && history < 3000) { history = 3000; } else if ((st->mode & EBUR128_MODE_M) == EBUR128_MODE_M && history < 400) { history = 400; } if (history == st->d->history) { return EBUR128_ERROR_NO_CHANGE; } st->d->history = history; st->d->block_list_max = st->d->history / 100; st->d->st_block_list_max = st->d->history / 3000; while (st->d->block_list_size > st->d->block_list_max) { struct ebur128_dq_entry* block = STAILQ_FIRST(&st->d->block_list); STAILQ_REMOVE_HEAD(&st->d->block_list, entries); free(block); st->d->block_list_size--; } while (st->d->st_block_list_size > st->d->st_block_list_max) { struct ebur128_dq_entry* block = STAILQ_FIRST(&st->d->short_term_block_list); STAILQ_REMOVE_HEAD(&st->d->short_term_block_list, entries); free(block); st->d->st_block_list_size--; } return EBUR128_SUCCESS; } static int ebur128_energy_shortterm(ebur128_state* st, double* out); #define EBUR128_ADD_FRAMES(type) \ int ebur128_add_frames_##type(ebur128_state* st, const type* src, \ size_t frames) { \ size_t src_index = 0; \ unsigned int c = 0; \ for (c = 0; c < st->channels; c++) { \ st->d->prev_sample_peak[c] = 0.0; \ st->d->prev_true_peak[c] = 0.0; \ } \ while (frames > 0) { \ if (frames >= st->d->needed_frames) { \ ebur128_filter_##type(st, src + src_index, st->d->needed_frames); \ src_index += st->d->needed_frames * st->channels; \ frames -= st->d->needed_frames; \ st->d->audio_data_index += st->d->needed_frames * st->channels; \ /* calculate the new gating block */ \ if ((st->mode & EBUR128_MODE_I) == EBUR128_MODE_I) { \ if (ebur128_calc_gating_block(st, st->d->samples_in_100ms * 4, \ NULL)) { \ return EBUR128_ERROR_NOMEM; \ } \ } \ if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA) { \ st->d->short_term_frame_counter += st->d->needed_frames; \ if (st->d->short_term_frame_counter == \ st->d->samples_in_100ms * 30) { \ struct ebur128_dq_entry* block; \ double st_energy; \ if (ebur128_energy_shortterm(st, &st_energy) == EBUR128_SUCCESS && \ st_energy >= histogram_energy_boundaries[0]) { \ if (st->d->use_histogram) { \ ++st->d->short_term_block_energy_histogram \ [find_histogram_index(st_energy)]; \ } else { \ if (st->d->st_block_list_size == st->d->st_block_list_max) { \ block = STAILQ_FIRST(&st->d->short_term_block_list); \ STAILQ_REMOVE_HEAD(&st->d->short_term_block_list, entries); \ } else { \ block = (struct ebur128_dq_entry*) malloc( \ sizeof(struct ebur128_dq_entry)); \ if (!block) { \ return EBUR128_ERROR_NOMEM; \ } \ st->d->st_block_list_size++; \ } \ block->z = st_energy; \ STAILQ_INSERT_TAIL(&st->d->short_term_block_list, block, \ entries); \ } \ } \ st->d->short_term_frame_counter = st->d->samples_in_100ms * 20; \ } \ } \ /* 100ms are needed for all blocks besides the first one */ \ st->d->needed_frames = st->d->samples_in_100ms; \ /* reset audio_data_index when buffer full */ \ if (st->d->audio_data_index == \ st->d->audio_data_frames * st->channels) { \ st->d->audio_data_index = 0; \ } \ } else { \ ebur128_filter_##type(st, src + src_index, frames); \ st->d->audio_data_index += frames * st->channels; \ if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA) { \ st->d->short_term_frame_counter += frames; \ } \ st->d->needed_frames -= (unsigned long) frames; \ frames = 0; \ } \ } \ for (c = 0; c < st->channels; c++) { \ if (st->d->prev_sample_peak[c] > st->d->sample_peak[c]) { \ st->d->sample_peak[c] = st->d->prev_sample_peak[c]; \ } \ if (st->d->prev_true_peak[c] > st->d->true_peak[c]) { \ st->d->true_peak[c] = st->d->prev_true_peak[c]; \ } \ } \ return EBUR128_SUCCESS; \ } EBUR128_ADD_FRAMES(short) EBUR128_ADD_FRAMES(int) EBUR128_ADD_FRAMES(float) EBUR128_ADD_FRAMES(double) static int ebur128_calc_relative_threshold(ebur128_state* st, size_t* above_thresh_counter, double* relative_threshold) { struct ebur128_dq_entry* it; size_t i; if (st->d->use_histogram) { for (i = 0; i < 1000; ++i) { *relative_threshold += st->d->block_energy_histogram[i] * histogram_energies[i]; *above_thresh_counter += st->d->block_energy_histogram[i]; } } else { STAILQ_FOREACH(it, &st->d->block_list, entries) { ++*above_thresh_counter; *relative_threshold += it->z; } } return EBUR128_SUCCESS; } static int ebur128_gated_loudness(ebur128_state** sts, size_t size, double* out) { struct ebur128_dq_entry* it; double gated_loudness = 0.0; double relative_threshold = 0.0; size_t above_thresh_counter = 0; size_t i, j, start_index; for (i = 0; i < size; i++) { if (sts[i] && (sts[i]->mode & EBUR128_MODE_I) != EBUR128_MODE_I) { return EBUR128_ERROR_INVALID_MODE; } } for (i = 0; i < size; i++) { if (!sts[i]) { continue; } ebur128_calc_relative_threshold(sts[i], &above_thresh_counter, &relative_threshold); } if (!above_thresh_counter) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } relative_threshold /= (double) above_thresh_counter; relative_threshold *= relative_gate_factor; above_thresh_counter = 0; if (relative_threshold < histogram_energy_boundaries[0]) { start_index = 0; } else { start_index = find_histogram_index(relative_threshold); if (relative_threshold > histogram_energies[start_index]) { ++start_index; } } for (i = 0; i < size; i++) { if (!sts[i]) { continue; } if (sts[i]->d->use_histogram) { for (j = start_index; j < 1000; ++j) { gated_loudness += sts[i]->d->block_energy_histogram[j] * histogram_energies[j]; above_thresh_counter += sts[i]->d->block_energy_histogram[j]; } } else { STAILQ_FOREACH(it, &sts[i]->d->block_list, entries) { if (it->z >= relative_threshold) { ++above_thresh_counter; gated_loudness += it->z; } } } } if (!above_thresh_counter) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } gated_loudness /= (double) above_thresh_counter; *out = ebur128_energy_to_loudness(gated_loudness); return EBUR128_SUCCESS; } int ebur128_relative_threshold(ebur128_state* st, double* out) { double relative_threshold = 0.0; size_t above_thresh_counter = 0; if ((st->mode & EBUR128_MODE_I) != EBUR128_MODE_I) { return EBUR128_ERROR_INVALID_MODE; } ebur128_calc_relative_threshold(st, &above_thresh_counter, &relative_threshold); if (!above_thresh_counter) { *out = -70.0; return EBUR128_SUCCESS; } relative_threshold /= (double) above_thresh_counter; relative_threshold *= relative_gate_factor; *out = ebur128_energy_to_loudness(relative_threshold); return EBUR128_SUCCESS; } int ebur128_loudness_global(ebur128_state* st, double* out) { return ebur128_gated_loudness(&st, 1, out); } int ebur128_loudness_global_multiple(ebur128_state** sts, size_t size, double* out) { return ebur128_gated_loudness(sts, size, out); } static int ebur128_energy_in_interval(ebur128_state* st, size_t interval_frames, double* out) { if (interval_frames > st->d->audio_data_frames) { return EBUR128_ERROR_INVALID_MODE; } ebur128_calc_gating_block(st, interval_frames, out); return EBUR128_SUCCESS; } static int ebur128_energy_shortterm(ebur128_state* st, double* out) { return ebur128_energy_in_interval(st, st->d->samples_in_100ms * 30, out); } int ebur128_loudness_momentary(ebur128_state* st, double* out) { double energy; int error; error = ebur128_energy_in_interval(st, st->d->samples_in_100ms * 4, &energy); if (error) { return error; } if (energy <= 0.0) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(energy); return EBUR128_SUCCESS; } int ebur128_loudness_shortterm(ebur128_state* st, double* out) { double energy; int error; error = ebur128_energy_shortterm(st, &energy); if (error) { return error; } if (energy <= 0.0) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(energy); return EBUR128_SUCCESS; } int ebur128_loudness_window(ebur128_state* st, unsigned long window, double* out) { double energy; size_t interval_frames; int error; if (window > st->d->window) { return EBUR128_ERROR_INVALID_MODE; } interval_frames = st->samplerate * window / 1000; error = ebur128_energy_in_interval(st, interval_frames, &energy); if (error) { return error; } if (energy <= 0.0) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(energy); return EBUR128_SUCCESS; } static int ebur128_double_cmp(const void* p1, const void* p2) { const double* d1 = (const double*) p1; const double* d2 = (const double*) p2; return (*d1 > *d2) - (*d1 < *d2); } /* EBU - TECH 3342 */ int ebur128_loudness_range_multiple(ebur128_state** sts, size_t size, double* out) { size_t i, j; struct ebur128_dq_entry* it; double* stl_vector; size_t stl_size; double* stl_relgated; size_t stl_relgated_size; double stl_power, stl_integrated; /* High and low percentile energy */ double h_en, l_en; int use_histogram = 0; for (i = 0; i < size; ++i) { if (sts[i]) { if ((sts[i]->mode & EBUR128_MODE_LRA) != EBUR128_MODE_LRA) { return EBUR128_ERROR_INVALID_MODE; } if (i == 0 && sts[i]->mode & EBUR128_MODE_HISTOGRAM) { use_histogram = 1; } else if (use_histogram != !!(sts[i]->mode & EBUR128_MODE_HISTOGRAM)) { return EBUR128_ERROR_INVALID_MODE; } } } if (use_histogram) { unsigned long hist[1000] = { 0 }; size_t percentile_low, percentile_high; size_t index; stl_size = 0; stl_power = 0.0; for (i = 0; i < size; ++i) { if (!sts[i]) { continue; } for (j = 0; j < 1000; ++j) { hist[j] += sts[i]->d->short_term_block_energy_histogram[j]; stl_size += sts[i]->d->short_term_block_energy_histogram[j]; stl_power += sts[i]->d->short_term_block_energy_histogram[j] * histogram_energies[j]; } } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } stl_power /= stl_size; stl_integrated = minus_twenty_decibels * stl_power; if (stl_integrated < histogram_energy_boundaries[0]) { index = 0; } else { index = find_histogram_index(stl_integrated); if (stl_integrated > histogram_energies[index]) { ++index; } } stl_size = 0; for (j = index; j < 1000; ++j) { stl_size += hist[j]; } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } percentile_low = (size_t) ((stl_size - 1) * 0.1 + 0.5); percentile_high = (size_t) ((stl_size - 1) * 0.95 + 0.5); stl_size = 0; j = index; while (stl_size <= percentile_low) { stl_size += hist[j++]; } l_en = histogram_energies[j - 1]; while (stl_size <= percentile_high) { stl_size += hist[j++]; } h_en = histogram_energies[j - 1]; *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en); return EBUR128_SUCCESS; } stl_size = 0; for (i = 0; i < size; ++i) { if (!sts[i]) { continue; } STAILQ_FOREACH(it, &sts[i]->d->short_term_block_list, entries) { ++stl_size; } } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } stl_vector = (double*) malloc(stl_size * sizeof(double)); if (!stl_vector) { return EBUR128_ERROR_NOMEM; } j = 0; for (i = 0; i < size; ++i) { if (!sts[i]) { continue; } STAILQ_FOREACH(it, &sts[i]->d->short_term_block_list, entries) { stl_vector[j] = it->z; ++j; } } qsort(stl_vector, stl_size, sizeof(double), ebur128_double_cmp); stl_power = 0.0; for (i = 0; i < stl_size; ++i) { stl_power += stl_vector[i]; } stl_power /= (double) stl_size; stl_integrated = minus_twenty_decibels * stl_power; stl_relgated = stl_vector; stl_relgated_size = stl_size; while (stl_relgated_size > 0 && *stl_relgated < stl_integrated) { ++stl_relgated; --stl_relgated_size; } if (stl_relgated_size) { h_en = stl_relgated[(size_t) ((stl_relgated_size - 1) * 0.95 + 0.5)]; l_en = stl_relgated[(size_t) ((stl_relgated_size - 1) * 0.1 + 0.5)]; free(stl_vector); *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en); } else { free(stl_vector); *out = 0.0; } return EBUR128_SUCCESS; } int ebur128_loudness_range(ebur128_state* st, double* out) { return ebur128_loudness_range_multiple(&st, 1, out); } int ebur128_sample_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) != EBUR128_MODE_SAMPLE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = st->d->sample_peak[channel_number]; return EBUR128_SUCCESS; } int ebur128_prev_sample_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) != EBUR128_MODE_SAMPLE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = st->d->prev_sample_peak[channel_number]; return EBUR128_SUCCESS; } int ebur128_true_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_TRUE_PEAK) != EBUR128_MODE_TRUE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = EBUR128_MAX(st->d->true_peak[channel_number], st->d->sample_peak[channel_number]); return EBUR128_SUCCESS; } int ebur128_prev_true_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_TRUE_PEAK) != EBUR128_MODE_TRUE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = EBUR128_MAX(st->d->prev_true_peak[channel_number], st->d->prev_sample_peak[channel_number]); return EBUR128_SUCCESS; } mlt-7.38.0/src/modules/plus/ebur128/ebur128.h000664 000000 000000 00000036273 15172202314 020350 0ustar00rootroot000000 000000 /* See COPYING file for copyright and license details. */ #ifndef EBUR128_H_ #define EBUR128_H_ /** \file ebur128.h * \brief libebur128 - a library for loudness measurement according to * the EBU R128 standard. */ #ifdef __cplusplus extern "C" { #endif #define EBUR128_VERSION_MAJOR 1 #define EBUR128_VERSION_MINOR 2 #define EBUR128_VERSION_PATCH 6 #include /* for size_t */ /** \enum channel * Use these values when setting the channel map with ebur128_set_channel(). * See definitions in ITU R-REC-BS 1770-4 */ enum channel { EBUR128_UNUSED = 0, /**< unused channel (for example LFE channel) */ EBUR128_LEFT = 1, /**< */ EBUR128_Mp030 = 1, /**< itu M+030 */ EBUR128_RIGHT = 2, /**< */ EBUR128_Mm030 = 2, /**< itu M-030 */ EBUR128_CENTER = 3, /**< */ EBUR128_Mp000 = 3, /**< itu M+000 */ EBUR128_LEFT_SURROUND = 4, /**< */ EBUR128_Mp110 = 4, /**< itu M+110 */ EBUR128_RIGHT_SURROUND = 5, /**< */ EBUR128_Mm110 = 5, /**< itu M-110 */ EBUR128_DUAL_MONO, /**< a channel that is counted twice */ EBUR128_MpSC, /**< itu M+SC */ EBUR128_MmSC, /**< itu M-SC */ EBUR128_Mp060, /**< itu M+060 */ EBUR128_Mm060, /**< itu M-060 */ EBUR128_Mp090, /**< itu M+090 */ EBUR128_Mm090, /**< itu M-090 */ EBUR128_Mp135, /**< itu M+135 */ EBUR128_Mm135, /**< itu M-135 */ EBUR128_Mp180, /**< itu M+180 */ EBUR128_Up000, /**< itu U+000 */ EBUR128_Up030, /**< itu U+030 */ EBUR128_Um030, /**< itu U-030 */ EBUR128_Up045, /**< itu U+045 */ EBUR128_Um045, /**< itu U-030 */ EBUR128_Up090, /**< itu U+090 */ EBUR128_Um090, /**< itu U-090 */ EBUR128_Up110, /**< itu U+110 */ EBUR128_Um110, /**< itu U-110 */ EBUR128_Up135, /**< itu U+135 */ EBUR128_Um135, /**< itu U-135 */ EBUR128_Up180, /**< itu U+180 */ EBUR128_Tp000, /**< itu T+000 */ EBUR128_Bp000, /**< itu B+000 */ EBUR128_Bp045, /**< itu B+045 */ EBUR128_Bm045 /**< itu B-045 */ }; /** \enum error * Error return values. */ enum error { EBUR128_SUCCESS = 0, EBUR128_ERROR_NOMEM, EBUR128_ERROR_INVALID_MODE, EBUR128_ERROR_INVALID_CHANNEL_INDEX, EBUR128_ERROR_NO_CHANGE }; /** \enum mode * Use these values in ebur128_init (or'ed). Try to use the lowest possible * modes that suit your needs, as performance will be better. */ enum mode { /** can call ebur128_loudness_momentary */ EBUR128_MODE_M = (1 << 0), /** can call ebur128_loudness_shortterm */ EBUR128_MODE_S = (1 << 1) | EBUR128_MODE_M, /** can call ebur128_loudness_global_* and ebur128_relative_threshold */ EBUR128_MODE_I = (1 << 2) | EBUR128_MODE_M, /** can call ebur128_loudness_range */ EBUR128_MODE_LRA = (1 << 3) | EBUR128_MODE_S, /** can call ebur128_sample_peak */ EBUR128_MODE_SAMPLE_PEAK = (1 << 4) | EBUR128_MODE_M, /** can call ebur128_true_peak */ EBUR128_MODE_TRUE_PEAK = (1 << 5) | EBUR128_MODE_M | EBUR128_MODE_SAMPLE_PEAK, /** uses histogram algorithm to calculate loudness */ EBUR128_MODE_HISTOGRAM = (1 << 6) }; /** forward declaration of ebur128_state_internal */ struct ebur128_state_internal; /** \brief Contains information about the state of a loudness measurement. * * You should not need to modify this struct directly. */ typedef struct { int mode; /**< The current mode. */ unsigned int channels; /**< The number of channels. */ unsigned long samplerate; /**< The sample rate. */ struct ebur128_state_internal* d; /**< Internal state. */ } ebur128_state; /** \brief Get library version number. Do not pass null pointers here. * * @param major major version number of library * @param minor minor version number of library * @param patch patch version number of library */ void ebur128_get_version(int* major, int* minor, int* patch); /** \brief Initialize library state. * * @param channels the number of channels. * @param samplerate the sample rate. * @param mode see the mode enum for possible values. * @return an initialized library state, or NULL on error. */ ebur128_state* ebur128_init(unsigned int channels, unsigned long samplerate, int mode); /** \brief Destroy library state. * * @param st pointer to a library state. */ void ebur128_destroy(ebur128_state** st); /** \brief Set channel type. * * The default is: * - 0 -> EBUR128_LEFT * - 1 -> EBUR128_RIGHT * - 2 -> EBUR128_CENTER * - 3 -> EBUR128_UNUSED * - 4 -> EBUR128_LEFT_SURROUND * - 5 -> EBUR128_RIGHT_SURROUND * * @param st library state. * @param channel_number zero based channel index. * @param value channel type from the "channel" enum. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_set_channel(ebur128_state* st, unsigned int channel_number, int value); /** \brief Change library parameters. * * Note that the channel map will be reset when setting a different number of * channels. The current unfinished block will be lost. * * @param st library state. * @param channels new number of channels. * @param samplerate new sample rate. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM on memory allocation error. The state will be * invalid and must be destroyed. * - EBUR128_ERROR_NO_CHANGE if channels and sample rate were not changed. */ int ebur128_change_parameters(ebur128_state* st, unsigned int channels, unsigned long samplerate); /** \brief Set the maximum window duration. * * Set the maximum duration that will be used for ebur128_loudness_window(). * Note that this destroys the current content of the audio buffer. * * @param st library state. * @param window duration of the window in ms. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM on memory allocation error. The state will be * invalid and must be destroyed. * - EBUR128_ERROR_NO_CHANGE if window duration not changed. */ int ebur128_set_max_window(ebur128_state* st, unsigned long window); /** \brief Set the maximum history. * * Set the maximum history that will be stored for loudness integration. * More history provides more accurate results, but requires more resources. * * Applies to ebur128_loudness_range() and ebur128_loudness_global() when * EBUR128_MODE_HISTOGRAM is not set. * * Default is ULONG_MAX (at least ~50 days). * Minimum is 3000ms for EBUR128_MODE_LRA and 400ms for EBUR128_MODE_M. * * @param st library state. * @param history duration of history in ms. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NO_CHANGE if history not changed. */ int ebur128_set_max_history(ebur128_state* st, unsigned long history); /** \brief Add frames to be processed. * * @param st library state. * @param src array of source frames. Channels must be interleaved. * @param frames number of frames. Not number of samples! * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM on memory allocation error. */ int ebur128_add_frames_short(ebur128_state* st, const short* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_int(ebur128_state* st, const int* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_float(ebur128_state* st, const float* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_double(ebur128_state* st, const double* src, size_t frames); /** \brief Get global integrated loudness in LUFS. * * @param st library state. * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not been set. */ int ebur128_loudness_global(ebur128_state* st, double* out); /** \brief Get global integrated loudness in LUFS across multiple instances. * * @param sts array of library states. * @param size length of sts * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not been set. */ int ebur128_loudness_global_multiple(ebur128_state** sts, size_t size, double* out); /** \brief Get momentary loudness (last 400ms) in LUFS. * * @param st library state. * @param out momentary loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. */ int ebur128_loudness_momentary(ebur128_state* st, double* out); /** \brief Get short-term loudness (last 3s) in LUFS. * * @param st library state. * @param out short-term loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_S" has not been set. */ int ebur128_loudness_shortterm(ebur128_state* st, double* out); /** \brief Get loudness of the specified window in LUFS. * * window must not be larger than the current window set in st. * The current window can be changed by calling ebur128_set_max_window(). * * @param st library state. * @param window window in ms to calculate loudness. * @param out loudness in LUFS. -HUGE_VAL if result is negative infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if window larger than current window in st. */ int ebur128_loudness_window(ebur128_state* st, unsigned long window, double* out); /** \brief Get loudness range (LRA) of programme in LU. * * Calculates loudness range according to EBU 3342. * * @param st library state. * @param out loudness range (LRA) in LU. Will not be changed in case of * error. EBUR128_ERROR_NOMEM or EBUR128_ERROR_INVALID_MODE will be * returned in this case. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM in case of memory allocation error. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_LRA" has not been set. */ int ebur128_loudness_range(ebur128_state* st, double* out); /** \brief Get loudness range (LRA) in LU across multiple instances. * * Calculates loudness range according to EBU 3342. * * @param sts array of library states. * @param size length of sts * @param out loudness range (LRA) in LU. Will not be changed in case of * error. EBUR128_ERROR_NOMEM or EBUR128_ERROR_INVALID_MODE will be * returned in this case. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM in case of memory allocation error. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_LRA" has not been set. */ int ebur128_loudness_range_multiple(ebur128_state** sts, size_t size, double* out); /** \brief Get maximum sample peak from all frames that have been processed. * * The equation to convert to dBFS is: 20 * log10(out) * * @param st library state * @param channel_number channel to analyse * @param out maximum sample peak in float format (1.0 is 0 dBFS) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_SAMPLE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_sample_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get maximum sample peak from the last call to add_frames(). * * The equation to convert to dBFS is: 20 * log10(out) * * @param st library state * @param channel_number channel to analyse * @param out maximum sample peak in float format (1.0 is 0 dBFS) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_SAMPLE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_prev_sample_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get maximum true peak from all frames that have been processed. * * Uses an implementation defined algorithm to calculate the true peak. Do not * try to compare resulting values across different versions of the library, * as the algorithm may change. * * The current implementation uses a custom polyphase FIR interpolator to * calculate true peak. Will oversample 4x for sample rates < 96000 Hz, 2x for * sample rates < 192000 Hz and leave the signal unchanged for 192000 Hz. * * The equation to convert to dBTP is: 20 * log10(out) * * @param st library state * @param channel_number channel to analyse * @param out maximum true peak in float format (1.0 is 0 dBTP) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_TRUE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_true_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get maximum true peak from the last call to add_frames(). * * Uses an implementation defined algorithm to calculate the true peak. Do not * try to compare resulting values across different versions of the library, * as the algorithm may change. * * The current implementation uses a custom polyphase FIR interpolator to * calculate true peak. Will oversample 4x for sample rates < 96000 Hz, 2x for * sample rates < 192000 Hz and leave the signal unchanged for 192000 Hz. * * The equation to convert to dBTP is: 20 * log10(out) * * @param st library state * @param channel_number channel to analyse * @param out maximum true peak in float format (1.0 is 0 dBTP) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_TRUE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_prev_true_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get relative threshold in LUFS. * * @param st library state * @param out relative threshold in LUFS. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not * been set. */ int ebur128_relative_threshold(ebur128_state* st, double* out); #ifdef __cplusplus } #endif #endif /* EBUR128_H_ */ mlt-7.38.0/src/modules/plus/ebur128/queue/000775 000000 000000 00000000000 15172202314 020120 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/plus/ebur128/queue/sys/000775 000000 000000 00000000000 15172202314 020736 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/plus/ebur128/queue/sys/queue.h000664 000000 000000 00000053367 15172202314 022251 0ustar00rootroot000000 000000 /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 * $FreeBSD: src/sys/sys/queue.h,v 1.68.2.4 2011/05/24 16:06:26 mdf Exp $ */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ #include /* * This file defines four types of data structures: singly-linked lists, * singly-linked tail queues, lists and tail queues. * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A singly-linked tail queue is headed by a pair of pointers, one to the * head of the list and the other to the tail of the list. The elements are * singly linked for minimum space and pointer manipulation overhead at the * expense of O(n) removal for arbitrary elements. New elements can be added * to the list after an existing element, at the head of the list, or at the * end of the list. Elements being removed from the head of the tail queue * should use the explicit macro for this purpose for optimum efficiency. * A singly-linked tail queue may only be traversed in the forward direction. * Singly-linked tail queues are ideal for applications with large datasets * and few or no removals or for implementing a FIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * For details on the use of these macros, see the queue(3) manual page. * * * SLIST LIST STAILQ TAILQ * _HEAD + + + + * _HEAD_INITIALIZER + + + + * _ENTRY + + + + * _INIT + + + + * _EMPTY + + + + * _FIRST + + + + * _NEXT + + + + * _PREV - - - + * _LAST - - + + * _FOREACH + + + + * _FOREACH_SAFE + + + + * _FOREACH_REVERSE - - - + * _FOREACH_REVERSE_SAFE - - - + * _INSERT_HEAD + + + + * _INSERT_BEFORE - + - + * _INSERT_AFTER + + + + * _INSERT_TAIL - - + + * _CONCAT - - + + * _REMOVE_AFTER + - + - * _REMOVE_HEAD + - + - * _REMOVE + + + + * _SWAP + + + + * */ #ifdef QUEUE_MACRO_DEBUG /* Store the last 2 places the queue element or head was altered */ struct qm_trace { char * lastfile; int lastline; char * prevfile; int prevline; }; #define TRACEBUF struct qm_trace trace; #define TRASHIT(x) do {(x) = (void *)-1;} while (0) #define QMD_SAVELINK(name, link) void **name = (void *)&(link) #define QMD_TRACE_HEAD(head) do { \ (head)->trace.prevline = (head)->trace.lastline; \ (head)->trace.prevfile = (head)->trace.lastfile; \ (head)->trace.lastline = __LINE__; \ (head)->trace.lastfile = __FILE__; \ } while (0) #define QMD_TRACE_ELEM(elem) do { \ (elem)->trace.prevline = (elem)->trace.lastline; \ (elem)->trace.prevfile = (elem)->trace.lastfile; \ (elem)->trace.lastline = __LINE__; \ (elem)->trace.lastfile = __FILE__; \ } while (0) #else #define QMD_TRACE_ELEM(elem) #define QMD_TRACE_HEAD(head) #define QMD_SAVELINK(name, link) #define TRACEBUF #define TRASHIT(x) #endif /* QUEUE_MACRO_DEBUG */ /* * Singly-linked List declarations. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List functions. */ #define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_FOREACH(var, head, field) \ for ((var) = SLIST_FIRST((head)); \ (var); \ (var) = SLIST_NEXT((var), field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST((head)); \ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ (var) = (tvar)) #define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ for ((varp) = &SLIST_FIRST((head)); \ ((var) = *(varp)) != NULL; \ (varp) = &SLIST_NEXT((var), field)) #define SLIST_INIT(head) do { \ SLIST_FIRST((head)) = NULL; \ } while (0) #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ SLIST_NEXT((slistelm), field) = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ SLIST_FIRST((head)) = (elm); \ } while (0) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_REMOVE(head, elm, type, field) do { \ QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ if (SLIST_FIRST((head)) == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = SLIST_FIRST((head)); \ while (SLIST_NEXT(curelm, field) != (elm)) \ curelm = SLIST_NEXT(curelm, field); \ SLIST_REMOVE_AFTER(curelm, field); \ } \ TRASHIT(*oldnext); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ SLIST_NEXT(elm, field) = \ SLIST_NEXT(SLIST_NEXT(elm, field), field); \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ } while (0) /* * Singly-linked Tail queue declarations. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first;/* first element */ \ struct type **stqh_last;/* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue functions. */ #define STAILQ_CONCAT(head1, head2) do { \ if (!STAILQ_EMPTY((head2))) { \ *(head1)->stqh_last = (head2)->stqh_first; \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_INIT((head2)); \ } \ } while (0) #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) #define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_FOREACH(var, head, field) \ for((var) = STAILQ_FIRST((head)); \ (var); \ (var) = STAILQ_NEXT((var), field)) #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = STAILQ_FIRST((head)); \ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ (var) = (tvar)) #define STAILQ_INIT(head) do { \ STAILQ_FIRST((head)) = NULL; \ (head)->stqh_last = &STAILQ_FIRST((head)); \ } while (0) #define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ STAILQ_NEXT((tqelm), field) = (elm); \ } while (0) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ STAILQ_FIRST((head)) = (elm); \ } while (0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ STAILQ_NEXT((elm), field) = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ } while (0) #define STAILQ_LAST(head, type, field) \ (STAILQ_EMPTY((head)) ? \ NULL : \ ((struct type *)(void *) \ ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) #define STAILQ_REMOVE(head, elm, type, field) do { \ QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ if (STAILQ_FIRST((head)) == (elm)) { \ STAILQ_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = STAILQ_FIRST((head)); \ while (STAILQ_NEXT(curelm, field) != (elm)) \ curelm = STAILQ_NEXT(curelm, field); \ STAILQ_REMOVE_AFTER(head, curelm, field); \ } \ TRASHIT(*oldnext); \ } while (0) #define STAILQ_REMOVE_AFTER(head, elm, field) do { \ if ((STAILQ_NEXT(elm, field) = \ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ } while (0) #define STAILQ_REMOVE_HEAD(head, field) do { \ if ((STAILQ_FIRST((head)) = \ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ (head)->stqh_last = &STAILQ_FIRST((head)); \ } while (0) #define STAILQ_SWAP(head1, head2, type) do { \ struct type *swap_first = STAILQ_FIRST(head1); \ struct type **swap_last = (head1)->stqh_last; \ STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_FIRST(head2) = swap_first; \ (head2)->stqh_last = swap_last; \ if (STAILQ_EMPTY(head1)) \ (head1)->stqh_last = &STAILQ_FIRST(head1); \ if (STAILQ_EMPTY(head2)) \ (head2)->stqh_last = &STAILQ_FIRST(head2); \ } while (0) /* * List declarations. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List functions. */ #if (defined(_KERNEL) && defined(INVARIANTS)) #define QMD_LIST_CHECK_HEAD(head, field) do { \ if (LIST_FIRST((head)) != NULL && \ LIST_FIRST((head))->field.le_prev != \ &LIST_FIRST((head))) \ panic("Bad list head %p first->prev != head", (head)); \ } while (0) #define QMD_LIST_CHECK_NEXT(elm, field) do { \ if (LIST_NEXT((elm), field) != NULL && \ LIST_NEXT((elm), field)->field.le_prev != \ &((elm)->field.le_next)) \ panic("Bad link elm %p next->prev != elm", (elm)); \ } while (0) #define QMD_LIST_CHECK_PREV(elm, field) do { \ if (*(elm)->field.le_prev != (elm)) \ panic("Bad link elm %p prev->next != elm", (elm)); \ } while (0) #else #define QMD_LIST_CHECK_HEAD(head, field) #define QMD_LIST_CHECK_NEXT(elm, field) #define QMD_LIST_CHECK_PREV(elm, field) #endif /* (_KERNEL && INVARIANTS) */ #define LIST_EMPTY(head) ((head)->lh_first == NULL) #define LIST_FIRST(head) ((head)->lh_first) #define LIST_FOREACH(var, head, field) \ for ((var) = LIST_FIRST((head)); \ (var); \ (var) = LIST_NEXT((var), field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST((head)); \ (var) && ((tvar) = LIST_NEXT((var), field), 1); \ (var) = (tvar)) #define LIST_INIT(head) do { \ LIST_FIRST((head)) = NULL; \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ QMD_LIST_CHECK_NEXT(listelm, field); \ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ LIST_NEXT((listelm), field)->field.le_prev = \ &LIST_NEXT((elm), field); \ LIST_NEXT((listelm), field) = (elm); \ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ QMD_LIST_CHECK_PREV(listelm, field); \ (elm)->field.le_prev = (listelm)->field.le_prev; \ LIST_NEXT((elm), field) = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ QMD_LIST_CHECK_HEAD((head), field); \ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ LIST_FIRST((head)) = (elm); \ (elm)->field.le_prev = &LIST_FIRST((head)); \ } while (0) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_REMOVE(elm, field) do { \ QMD_SAVELINK(oldnext, (elm)->field.le_next); \ QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ QMD_LIST_CHECK_NEXT(elm, field); \ QMD_LIST_CHECK_PREV(elm, field); \ if (LIST_NEXT((elm), field) != NULL) \ LIST_NEXT((elm), field)->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = LIST_NEXT((elm), field); \ TRASHIT(*oldnext); \ TRASHIT(*oldprev); \ } while (0) #define LIST_SWAP(head1, head2, type, field) do { \ struct type *swap_tmp = LIST_FIRST((head1)); \ LIST_FIRST((head1)) = LIST_FIRST((head2)); \ LIST_FIRST((head2)) = swap_tmp; \ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ } while (0) /* * Tail queue declarations. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ TRACEBUF \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ TRACEBUF \ } /* * Tail queue functions. */ #if (defined(_KERNEL) && defined(INVARIANTS)) #define QMD_TAILQ_CHECK_HEAD(head, field) do { \ if (!TAILQ_EMPTY(head) && \ TAILQ_FIRST((head))->field.tqe_prev != \ &TAILQ_FIRST((head))) \ panic("Bad tailq head %p first->prev != head", (head)); \ } while (0) #define QMD_TAILQ_CHECK_TAIL(head, field) do { \ if (*(head)->tqh_last != NULL) \ panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ } while (0) #define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ if (TAILQ_NEXT((elm), field) != NULL && \ TAILQ_NEXT((elm), field)->field.tqe_prev != \ &((elm)->field.tqe_next)) \ panic("Bad link elm %p next->prev != elm", (elm)); \ } while (0) #define QMD_TAILQ_CHECK_PREV(elm, field) do { \ if (*(elm)->field.tqe_prev != (elm)) \ panic("Bad link elm %p prev->next != elm", (elm)); \ } while (0) #else #define QMD_TAILQ_CHECK_HEAD(head, field) #define QMD_TAILQ_CHECK_TAIL(head, headname) #define QMD_TAILQ_CHECK_NEXT(elm, field) #define QMD_TAILQ_CHECK_PREV(elm, field) #endif /* (_KERNEL && INVARIANTS) */ #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ QMD_TRACE_HEAD(head1); \ QMD_TRACE_HEAD(head2); \ } \ } while (0) #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_FOREACH(var, head, field) \ for ((var) = TAILQ_FIRST((head)); \ (var); \ (var) = TAILQ_NEXT((var), field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST((head)); \ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for ((var) = TAILQ_LAST((head), headname); \ (var); \ (var) = TAILQ_PREV((var), headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST((head), headname); \ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ (var) = (tvar)) #define TAILQ_INIT(head) do { \ TAILQ_FIRST((head)) = NULL; \ (head)->tqh_last = &TAILQ_FIRST((head)); \ QMD_TRACE_HEAD(head); \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ QMD_TAILQ_CHECK_NEXT(listelm, field); \ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ TAILQ_NEXT((elm), field)->field.tqe_prev = \ &TAILQ_NEXT((elm), field); \ else { \ (head)->tqh_last = &TAILQ_NEXT((elm), field); \ QMD_TRACE_HEAD(head); \ } \ TAILQ_NEXT((listelm), field) = (elm); \ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ QMD_TRACE_ELEM(&(elm)->field); \ QMD_TRACE_ELEM(&listelm->field); \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ QMD_TAILQ_CHECK_PREV(listelm, field); \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ TAILQ_NEXT((elm), field) = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ QMD_TRACE_ELEM(&(elm)->field); \ QMD_TRACE_ELEM(&listelm->field); \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ QMD_TAILQ_CHECK_HEAD(head, field); \ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ TAILQ_FIRST((head))->field.tqe_prev = \ &TAILQ_NEXT((elm), field); \ else \ (head)->tqh_last = &TAILQ_NEXT((elm), field); \ TAILQ_FIRST((head)) = (elm); \ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ QMD_TRACE_HEAD(head); \ QMD_TRACE_ELEM(&(elm)->field); \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ QMD_TAILQ_CHECK_TAIL(head, field); \ TAILQ_NEXT((elm), field) = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &TAILQ_NEXT((elm), field); \ QMD_TRACE_HEAD(head); \ QMD_TRACE_ELEM(&(elm)->field); \ } while (0) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_REMOVE(head, elm, field) do { \ QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ QMD_TAILQ_CHECK_NEXT(elm, field); \ QMD_TAILQ_CHECK_PREV(elm, field); \ if ((TAILQ_NEXT((elm), field)) != NULL) \ TAILQ_NEXT((elm), field)->field.tqe_prev = \ (elm)->field.tqe_prev; \ else { \ (head)->tqh_last = (elm)->field.tqe_prev; \ QMD_TRACE_HEAD(head); \ } \ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ TRASHIT(*oldnext); \ TRASHIT(*oldprev); \ QMD_TRACE_ELEM(&(elm)->field); \ } while (0) #define TAILQ_SWAP(head1, head2, type, field) do { \ struct type *swap_first = (head1)->tqh_first; \ struct type **swap_last = (head1)->tqh_last; \ (head1)->tqh_first = (head2)->tqh_first; \ (head1)->tqh_last = (head2)->tqh_last; \ (head2)->tqh_first = swap_first; \ (head2)->tqh_last = swap_last; \ if ((swap_first = (head1)->tqh_first) != NULL) \ swap_first->field.tqe_prev = &(head1)->tqh_first; \ else \ (head1)->tqh_last = &(head1)->tqh_first; \ if ((swap_first = (head2)->tqh_first) != NULL) \ swap_first->field.tqe_prev = &(head2)->tqh_first; \ else \ (head2)->tqh_last = &(head2)->tqh_first; \ } while (0) #ifdef _KERNEL /* * XXX insque() and remque() are an old way of handling certain queues. * They bogusly assumes that all queue heads look alike. */ struct quehead { struct quehead *qh_link; struct quehead *qh_rlink; }; #ifdef __CC_SUPPORTS___INLINE static __inline void insque(void *a, void *b) { struct quehead *element = (struct quehead *)a, *head = (struct quehead *)b; element->qh_link = head->qh_link; element->qh_rlink = head; head->qh_link = element; element->qh_link->qh_rlink = element; } static __inline void remque(void *a) { struct quehead *element = (struct quehead *)a; element->qh_link->qh_rlink = element->qh_rlink; element->qh_rlink->qh_link = element->qh_link; element->qh_rlink = 0; } #else /* !__CC_SUPPORTS___INLINE */ void insque(void *a, void *b); void remque(void *a); #endif /* __CC_SUPPORTS___INLINE */ #endif /* _KERNEL */ #endif /* !_SYS_QUEUE_H_ */ mlt-7.38.0/src/modules/plus/factory.c000664 000000 000000 00000035547 15172202314 017435 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltplus_export.h" #include #include #include extern mlt_consumer consumer_blipflash_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_affine_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_charcoal_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_chroma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_chroma_hold_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_dynamictext_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_dynamic_loudness_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_gradientmap_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_hslprimaries_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_hslrange_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_invert_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_lift_gamma_gain_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_loudness_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_loudness_meter_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_lumakey_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_rgblut_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_sepia_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_shape_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_spot_remover_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_strobe_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_subtitle_feed_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_subtitle_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_text_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_threshold_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_timer_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_blipflash_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_count_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_pgm_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_subtitle_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_transition transition_affine_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #ifdef USE_FFTW extern mlt_filter filter_dance_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_fft_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #endif static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/plus/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTPLUS_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "blipflash", consumer_blipflash_init); MLT_REGISTER(mlt_service_filter_type, "affine", filter_affine_init); MLT_REGISTER(mlt_service_filter_type, "charcoal", filter_charcoal_init); MLT_REGISTER(mlt_service_filter_type, "chroma", filter_chroma_init); MLT_REGISTER(mlt_service_filter_type, "chroma_hold", filter_chroma_hold_init); MLT_REGISTER(mlt_service_filter_type, "dynamictext", filter_dynamictext_init); MLT_REGISTER(mlt_service_filter_type, "dynamic_loudness", filter_dynamic_loudness_init); MLT_REGISTER(mlt_service_filter_type, "gradientmap", filter_gradientmap_init); MLT_REGISTER(mlt_service_filter_type, "hslprimaries", filter_hslprimaries_init); MLT_REGISTER(mlt_service_filter_type, "hslrange", filter_hslrange_init); MLT_REGISTER(mlt_service_filter_type, "invert", filter_invert_init); MLT_REGISTER(mlt_service_filter_type, "lift_gamma_gain", filter_lift_gamma_gain_init); MLT_REGISTER(mlt_service_filter_type, "loudness", filter_loudness_init); MLT_REGISTER(mlt_service_filter_type, "loudness_meter", filter_loudness_meter_init); MLT_REGISTER(mlt_service_filter_type, "lumakey", filter_lumakey_init); MLT_REGISTER(mlt_service_filter_type, "rgblut", filter_rgblut_init); MLT_REGISTER(mlt_service_filter_type, "sepia", filter_sepia_init); MLT_REGISTER(mlt_service_filter_type, "shape", filter_shape_init); MLT_REGISTER(mlt_service_filter_type, "spot_remover", filter_spot_remover_init); MLT_REGISTER(mlt_service_filter_type, "strobe", filter_strobe_init); MLT_REGISTER(mlt_service_filter_type, "subtitle_feed", filter_subtitle_feed_init); MLT_REGISTER(mlt_service_filter_type, "subtitle", filter_subtitle_init); MLT_REGISTER(mlt_service_filter_type, "text", filter_text_init); MLT_REGISTER(mlt_service_filter_type, "threshold", filter_threshold_init); MLT_REGISTER(mlt_service_filter_type, "timer", filter_timer_init); MLT_REGISTER(mlt_service_producer_type, "blipflash", producer_blipflash_init); MLT_REGISTER(mlt_service_producer_type, "count", producer_count_init); MLT_REGISTER(mlt_service_producer_type, "pgm", producer_pgm_init); MLT_REGISTER(mlt_service_producer_type, "subtitle", producer_subtitle_init); MLT_REGISTER(mlt_service_transition_type, "affine", transition_affine_init); #ifdef USE_FFTW MLT_REGISTER(mlt_service_filter_type, "dance", filter_dance_init); MLT_REGISTER(mlt_service_filter_type, "fft", filter_fft_init); #endif MLT_REGISTER_METADATA(mlt_service_consumer_type, "blipflash", metadata, "consumer_blipflash.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "affine", metadata, "filter_affine.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "charcoal", metadata, "filter_charcoal.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "chroma", metadata, "filter_chroma.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "chroma_hold", metadata, "filter_chroma_hold.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "dynamictext", metadata, "filter_dynamictext.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "dynamic_loudness", metadata, "filter_dynamic_loudness.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "gradientmap", metadata, "filter_gradientmap.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "hslprimaries", metadata, "filter_hslprimaries.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "hslrange", metadata, "filter_hslrange.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "invert", metadata, "filter_invert.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "lift_gamma_gain", metadata, "filter_lift_gamma_gain.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "loudness", metadata, "filter_loudness.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "loudness_meter", metadata, "filter_loudness_meter.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "lumakey", metadata, "filter_lumakey.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "rgblut", metadata, "filter_rgblut.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "sepia", metadata, "filter_sepia.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "shape", metadata, "filter_shape.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "spot_remover", metadata, "filter_spot_remover.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "strobe", metadata, "filter_strobe.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "subtitle_feed", metadata, "filter_subtitle_feed.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "subtitle", metadata, "filter_subtitle.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "text", metadata, "filter_text.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "threshold", metadata, "filter_threshold.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "timer", metadata, "filter_timer.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "blipflash", metadata, "producer_blipflash.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "count", metadata, "producer_count.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "pgm", metadata, "producer_pgm.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "subtitle", metadata, "producer_subtitle.yml"); MLT_REGISTER_METADATA(mlt_service_transition_type, "affine", metadata, "transition_affine.yml"); #ifdef USE_FFTW MLT_REGISTER_METADATA(mlt_service_filter_type, "dance", metadata, "filter_dance.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "fft", metadata, "filter_fft.yml"); #endif } mlt-7.38.0/src/modules/plus/filter_affine.c000664 000000 000000 00000015672 15172202314 020560 0ustar00rootroot000000 000000 /* * filter_affine.c -- affine filter * Copyright (C) 2003-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #define MLT_AFFINE_COUNT_PROPERTY "filter_affine.count" /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the filter mlt_filter filter = mlt_frame_pop_service(frame); // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES(filter); // Get the image int error = 0; if (*format != mlt_image_rgba64) *format = mlt_image_rgba; mlt_service_lock(MLT_FILTER_SERVICE(filter)); mlt_producer producer = mlt_properties_get_data(properties, "producer", NULL); mlt_transition transition = mlt_properties_get_data(properties, "transition", NULL); mlt_frame a_frame = NULL; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); char *background = mlt_properties_get(properties, "background"); char *previous = mlt_properties_get(properties, "_background"); if (producer == NULL || (background && previous && strcmp(background, previous))) { producer = mlt_factory_producer(profile, NULL, background); mlt_properties_set_data(properties, "producer", producer, 0, (mlt_destructor) mlt_producer_close, NULL); mlt_properties_set(properties, "_background", background); } if (transition == NULL) { transition = mlt_factory_transition(profile, "affine", NULL); mlt_properties_set_data(properties, "transition", transition, 0, (mlt_destructor) mlt_transition_close, NULL); if (transition) mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "b_alpha", 1); } if (producer != NULL && transition != NULL) { mlt_position position = mlt_filter_get_position(filter, frame); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_position in = mlt_filter_get_in(filter); mlt_position out = mlt_filter_get_out(filter); double consumer_ar = mlt_profile_sar(profile); mlt_transition_set_in_and_out(transition, in, out); if (out > 0) { mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(producer), "length", out - in + 1); mlt_producer_set_in_and_out(producer, in, out); } mlt_producer_seek(producer, in + position); mlt_properties_pass(MLT_PRODUCER_PROPERTIES(producer), properties, "producer."); mlt_properties_pass(MLT_TRANSITION_PROPERTIES(transition), properties, "transition."); mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &a_frame, 0); mlt_frame_set_position(a_frame, in + position); // Set the rescale interpolation to match the frame mlt_properties_set(MLT_FRAME_PROPERTIES(a_frame), "consumer.rescale", mlt_properties_get(frame_properties, "consumer.rescale")); // Special case - aspect_ratio = 0 if (mlt_frame_get_aspect_ratio(frame) == 0) mlt_frame_set_aspect_ratio(frame, consumer_ar); if (mlt_frame_get_aspect_ratio(a_frame) == 0) mlt_frame_set_aspect_ratio(a_frame, consumer_ar); // Add the affine transition onto the frame stack mlt_transition_process(transition, a_frame, frame); if (mlt_properties_get_int(properties, "use_normalized") || mlt_properties_get_int(properties, "use_normalized")) { // Use the normalized width & height mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); *width = profile->width; *height = profile->height; } // Prescale the image before applying affine filters if more than 1 if (mlt_properties_get_int(frame_properties, MLT_AFFINE_COUNT_PROPERTY) > 1) { mlt_properties_set_int(frame_properties, "always_scale", 1); } mlt_frame_get_image(a_frame, image, format, width, height, writable); mlt_properties_set_data(frame_properties, "affine_frame", a_frame, 0, (mlt_destructor) mlt_frame_close, NULL); mlt_frame_set_image(frame, *image, *width * *height * 4, NULL); uint8_t *alpha = mlt_frame_get_alpha(a_frame); if (alpha) { mlt_frame_set_alpha(frame, alpha, *width * *height, NULL); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } else { mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); // Count the number of affine filters on this frame if (mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), MLT_AFFINE_COUNT_PROPERTY)) { int count = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), MLT_AFFINE_COUNT_PROPERTY); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), MLT_AFFINE_COUNT_PROPERTY, count + 1); } else { mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), MLT_AFFINE_COUNT_PROPERTY, 1); } return frame; } /** Constructor for the filter. */ mlt_filter filter_affine_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "background", arg ? arg : "colour:0"); } return filter; } mlt-7.38.0/src/modules/plus/filter_affine.yml000664 000000 000000 00000002373 15172202314 021131 0ustar00rootroot000000 000000 schema_version: 0.2 type: filter identifier: affine title: Transform version: 6 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: background argument: yes title: Background description: > The specification for a producer to act as the background image. type: string default: colour:0 mutable: yes - identifier: producer.* title: Producer properties description: A property and its value to apply to the producer. type: properties mutable: yes - identifier: transition.* title: Transition properties description: > A property and its value to apply to the transition. This is the primary means to use this filter. See the "affine" transition for details. type: properties service-name: transition.affine mutable: yes - identifier: use_normalized title: Use normalized description: > Use the profile's video resolution when requesting the image from the filter's producer instead of the resolution requested by the consumer. type: boolean default: 0 mutable: yes - identifier: use_normalised title: Use normalized (*DEPRECATED*) description: deprecated. See use_normalized mlt-7.38.0/src/modules/plus/filter_charcoal.c000664 000000 000000 00000020625 15172202314 021076 0ustar00rootroot000000 000000 /* * filter_charcoal.c -- charcoal filter * Copyright (C) 2003-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include static inline int get_Y(uint8_t *pixels, int width, int height, int x, int y, int max_luma) { if (x < 0 || x >= width || y < 0 || y >= height) { return max_luma; } else { uint8_t *pixel = pixels + y * (width << 1) + (x << 1); return *pixel; } } static inline int sqrti(int n) { int p = 0; int q = 1; int r = n; int h = 0; while (q <= n) q = q << 2; while (q != 1) { q = q >> 2; h = p + q; p = p >> 1; if (r >= h) { p = p + q; r = r - h; } } return p; } typedef struct { uint8_t *image; uint8_t *dest; int width; int height; int x_scatter; int y_scatter; int min; int max_luma; int max_chroma; int invert; int invert_luma; float scale; float mix; } slice_desc; static int slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *d = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, d->height, &slice_line_start); uint8_t *p = d->dest + slice_line_start * d->width * 2; uint8_t *q = d->image + slice_line_start * d->width * 2; int matrix[3][3]; int sum1; int sum2; float sum; int val; for (int y = slice_line_start; y < slice_line_start + slice_height; y++) { for (int x = 0; x < d->width; x++) { // Populate the matrix matrix[0][0] = get_Y(d->image, d->width, d->height, x - d->x_scatter, y - d->y_scatter, d->max_luma); matrix[0][1] = get_Y(d->image, d->width, d->height, x, y - d->y_scatter, d->max_luma); matrix[0][2] = get_Y(d->image, d->width, d->height, x + d->x_scatter, y - d->y_scatter, d->max_luma); matrix[1][0] = get_Y(d->image, d->width, d->height, x - d->x_scatter, y, d->max_luma); matrix[1][2] = get_Y(d->image, d->width, d->height, x + d->x_scatter, y, d->max_luma); matrix[2][0] = get_Y(d->image, d->width, d->height, x - d->x_scatter, y + d->y_scatter, d->max_luma); matrix[2][1] = get_Y(d->image, d->width, d->height, x, y + d->y_scatter, d->max_luma); matrix[2][2] = get_Y(d->image, d->width, d->height, x + d->x_scatter, y + d->y_scatter, d->max_luma); // Do calculations sum1 = (matrix[2][0] - matrix[0][0]) + ((matrix[2][1] - matrix[0][1]) << 1) + (matrix[2][2] - matrix[2][0]); sum2 = (matrix[0][2] - matrix[0][0]) + ((matrix[1][2] - matrix[1][0]) << 1) + (matrix[2][2] - matrix[2][0]); sum = d->scale * sqrti(sum1 * sum1 + sum2 * sum2); // Assign value *p++ = !d->invert ? (sum >= d->min && sum <= d->max_luma ? d->invert_luma - sum : sum < d->min ? d->max_luma : d->min) : (sum >= d->min && sum <= d->max_luma ? sum : sum < d->min ? d->min : d->max_luma); q++; val = 128 + d->mix * (*q++ - 128); val = CLAMP(val, d->min, d->max_chroma); *p++ = val; } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the filter mlt_filter filter = mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); // Get the image *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 0); // Only process if we have no error and a valid colour space if (error == 0) { int size = *width * *height * 2; int full_range = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_range"); // Get the charcoal scatter value int x_scatter = mlt_properties_anim_get_double(properties, "x_scatter", position, length); int y_scatter = mlt_properties_anim_get_double(properties, "y_scatter", position, length); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale_x = mlt_profile_scale_width(profile, *width); double scale_y = mlt_profile_scale_height(profile, *height); if (scale_x > 0.0 || scale_y > 0.0) { x_scatter = MAX(1, lrint(x_scatter * scale_x)); y_scatter = MAX(1, lrint(y_scatter * scale_y)); } slice_desc desc = {// We need to create a new frame as this effect modifies the input .image = *image, .dest = mlt_pool_alloc(size), .width = *width, .height = *height, .x_scatter = x_scatter, .y_scatter = y_scatter, .min = full_range ? 0 : 16, .max_luma = full_range ? 255 : 235, .max_chroma = full_range ? 255 : 240, .invert = mlt_properties_anim_get_int(properties, "invert", position, length), .invert_luma = full_range ? 255 : 251, .scale = mlt_properties_anim_get_double(properties, "scale", position, length), .mix = mlt_properties_anim_get_double(properties, "mix", position, length)}; mlt_slices_run_normal(0, slice_proc, &desc); // Return the created image *image = desc.dest; // Store new and destroy old mlt_frame_set_image(frame, *image, size, mlt_pool_release); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_charcoal_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "x_scatter", 1); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "y_scatter", 1); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "scale", 1.5); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "mix", 0.0); } return filter; } mlt-7.38.0/src/modules/plus/filter_charcoal.yml000664 000000 000000 00000001462 15172202314 021453 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: charcoal title: Charcoal version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: x_scatter title: Line Width type: integer default: 1 minimum: 1 mutable: yes animation: yes unit: pixels - identifier: y_scatter title: Line Height type: integer default: 1 minimum: 1 mutable: yes animation: yes unit: pixels - identifier: scale title: Contrast type: float default: 1.5 mutable: yes animation: yes - identifier: mix title: Color type: float default: 0 mutable: yes animation: yes - identifier: invert title: Invert type: boolean default: 0 mutable: yes animation: yes mlt-7.38.0/src/modules/plus/filter_chroma.c000664 000000 000000 00000010037 15172202314 020567 0ustar00rootroot000000 000000 /* * filter_chroma.c -- Maps a chroma key to the alpha channel * Copyright (C) 2007-2025 Meltytech, LLC * Author: Charles Yates * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include static inline int in_range(uint8_t v, uint8_t c, int var) { return ((int) v >= c - var) && ((int) v <= c + var); } static inline uint8_t alpha_value(uint8_t a, uint8_t *p, uint8_t u, uint8_t v, int var, int odd) { if (odd == 0) return (in_range(*(p + 1), u, var) && in_range(*(p + 3), v, var)) ? 0 : a; else return (in_range((*(p + 1) + *(p + 5)) / 2, u, var) && in_range((*(p + 3) + *(p + 7)) / 2, v, var)) ? 0 : a; } /** Get the images and map the chroma to the alpha of the frame. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter this = mlt_frame_pop_service(frame); mlt_position position = mlt_filter_get_position(this, frame); mlt_position length = mlt_filter_get_length2(this, frame); int variance = 200 * mlt_properties_anim_get_double(MLT_FILTER_PROPERTIES(this), "variance", position, length); mlt_color key_val = mlt_properties_anim_get_color(MLT_FILTER_PROPERTIES(this), "key", position, length); key_val = mlt_color_convert_trc(key_val, mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "color_trc")); uint8_t u, v; RGB2UV_601_SCALED(key_val.r, key_val.g, key_val.b, u, v); *format = mlt_image_yuv422; if (mlt_frame_get_image(frame, image, format, width, height, writable) == 0) { uint8_t *alpha = mlt_frame_get_alpha(frame); if (!alpha) { int alphasize = *width * *height; alpha = mlt_pool_alloc(alphasize); memset(alpha, 255, alphasize); mlt_frame_set_alpha(frame, alpha, alphasize, mlt_pool_release); } uint8_t *p = *image; int size = *width * *height / 2; while (size--) { *alpha = alpha_value(*alpha, p, u, v, variance, 0); alpha++; *alpha = alpha_value(*alpha, p, u, v, variance, 1); alpha++; p += 4; } } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter this, mlt_frame frame) { mlt_frame_push_service(frame, this); mlt_frame_push_service(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_chroma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter this = mlt_filter_new(); if (this != NULL) { mlt_properties_set(MLT_FILTER_PROPERTIES(this), "key", arg == NULL ? "#0000ff" : arg); mlt_properties_set_double(MLT_FILTER_PROPERTIES(this), "variance", 0.15); this->process = filter_process; } return this; } mlt-7.38.0/src/modules/plus/filter_chroma.yml000664 000000 000000 00000000676 15172202314 021156 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: chroma title: Chroma Key version: 1 creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: key title: Key Color type: color argument: yes mutable: yes default: "#0000ff" animation: yes - identifier: variance title: Variance type: float mutable: yes default: 0.15 minimum: 0 maximum: 1.0 animation: yes mlt-7.38.0/src/modules/plus/filter_chroma_hold.c000664 000000 000000 00000007654 15172202314 021610 0ustar00rootroot000000 000000 /* * filter_chroma.c -- Maps a chroma key to the alpha channel * Copyright (C) 2008-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include static inline int in_range(uint8_t v, uint8_t c, int var) { return ((int) v >= c - var) && ((int) v <= c + var); } static inline uint8_t alpha_value(uint8_t a, uint8_t *p, uint8_t u, uint8_t v, int var, int odd) { if (odd == 0) return (in_range(*(p + 1), u, var) && in_range(*(p + 3), v, var)) ? 0 : a; else return (in_range((*(p + 1) + *(p + 5)) / 2, u, var) && in_range((*(p + 3) + *(p + 7)) / 2, v, var)) ? 0 : a; } /** Get the images and map the chroma to the alpha of the frame. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter this = mlt_frame_pop_service(frame); mlt_position position = mlt_filter_get_position(this, frame); mlt_position length = mlt_filter_get_length2(this, frame); int variance = 200 * mlt_properties_anim_get_double(MLT_FILTER_PROPERTIES(this), "variance", position, length); mlt_color key_val = mlt_properties_anim_get_color(MLT_FILTER_PROPERTIES(this), "key", position, length); key_val = mlt_color_convert_trc(key_val, mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "color_trc")); uint8_t r = key_val.r; uint8_t g = key_val.g; uint8_t b = key_val.b; uint8_t u, v; RGB2UV_601_SCALED(r, g, b, u, v); *format = mlt_image_yuv422; if (mlt_frame_get_image(frame, image, format, width, height, writable) == 0) { uint8_t alpha = 0; uint8_t *p = *image; int size = *width * *height / 2; while (size--) { alpha = alpha_value(255, p, u, v, variance, 0); if (alpha) *(p + 1) = 128; alpha = alpha_value(255, p, u, v, variance, 1); if (alpha) *(p + 3) = 128; p += 4; } } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter this, mlt_frame frame) { mlt_frame_push_service(frame, this); mlt_frame_push_service(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_chroma_hold_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter this = mlt_filter_new(); if (this != NULL) { mlt_properties_set(MLT_FILTER_PROPERTIES(this), "key", arg == NULL ? "#c00000" : arg); mlt_properties_set_double(MLT_FILTER_PROPERTIES(this), "variance", 0.15); this->process = filter_process; } return this; } mlt-7.38.0/src/modules/plus/filter_chroma_hold.yml000664 000000 000000 00000001110 15172202314 022144 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: chroma_hold title: Chroma Hold version: 1 creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: key argument: yes type: color title: Key Color description: The color to keep. All others are desaturated. default: "#c00000" mutable: yes animation: yes - identifier: variance title: Variance description: The threshold for colors similar to the key color. type: float default: 0.15 minimum: 0 maximum: 1.0 mutable: yes animation: yes mlt-7.38.0/src/modules/plus/filter_dance.c000664 000000 000000 00000026466 15172202314 020405 0ustar00rootroot000000 000000 /* * filter_dance.c -- animate images size and position to the audio * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include // sin() #include // calloc(), free() #include // strdup() // Private Constants static const double PI = 3.14159265358979323846; // Private Types typedef struct { mlt_filter affine; mlt_filter fft; char *mag_prop_name; int rel_pos; double phase; int preprocess_warned; } private_data; static double apply(double positive, double negative, double mag, double max_range) { if (mag == 0.0) { return 0.0; } else if (mag > 0.0 && positive > 0.0) { return positive * mag * max_range; } else if (mag < 0.0 && negative > 0.0) { return negative * mag * max_range; } else if (positive) { return positive * fabs(mag) * max_range; } else if (negative) { return negative * -fabs(mag) * max_range; } return 0.0; } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Create the FFT filter the first time. if (!pdata->fft) { pdata->fft = mlt_factory_filter(profile, "fft", NULL); mlt_properties_set_int(MLT_FILTER_PROPERTIES(pdata->fft), "window_size", mlt_properties_get_int(filter_properties, "window_size")); if (!pdata->fft) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "Unable to create FFT.\n"); return 1; } } mlt_properties fft_properties = MLT_FILTER_PROPERTIES(pdata->fft); double low_freq = mlt_properties_get_int(filter_properties, "frequency_low"); double hi_freq = mlt_properties_get_int(filter_properties, "frequency_high"); double threshold = mlt_properties_get_int(filter_properties, "threshold"); double osc = mlt_properties_get_int(filter_properties, "osc"); float peak = 0; // The service must stay locked while using the private data mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Perform FFT processing on the frame mlt_filter_process(pdata->fft, frame); mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); float *bins = mlt_properties_get_data(fft_properties, "bins", NULL); double window_level = mlt_properties_get_double(fft_properties, "window_level"); if (bins && window_level == 1.0) { // Find the peak FFT magnitude in the configured range of frequencies int bin_count = mlt_properties_get_int(fft_properties, "bin_count"); double bin_width = mlt_properties_get_double(fft_properties, "bin_width"); int bin = 0; for (bin = 0; bin < bin_count; bin++) { double F = bin_width * (double) bin; if (F >= low_freq && F <= hi_freq) { if (bins[bin] > peak) { peak = bins[bin]; } } } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Scale the magnitude to dB and apply oscillation double dB = peak > 0.0 ? 20 * log10(peak) : -1000.0; double mag = 0.0; if (dB >= threshold) { // Scale to range 0.0-1.0 mag = 1 - (dB / threshold); if (osc != 0) { // Apply the oscillation double fps = mlt_profile_fps(profile); double t = pdata->rel_pos / fps; mag = mag * sin(2 * PI * osc * t + pdata->phase); } pdata->rel_pos++; } else { pdata->rel_pos = 1; // Alternate the phase so that the dancing alternates directions to the beat. pdata->phase = pdata->phase ? 0 : PI; mag = 0; } // Save the magnitude as a property on the frame to be used in get_image() mlt_properties_set_double(MLT_FRAME_PROPERTIES(frame), pdata->mag_prop_name, mag); return 0; } /** Get the image. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); private_data *pdata = (private_data *) filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); if (mlt_properties_exists(frame_properties, pdata->mag_prop_name)) { double mag = mlt_properties_get_double(frame_properties, pdata->mag_prop_name); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // scale_x and scale_y are in the range 0.0 to x.0 with: // 0.0 = the largest possible // < 1.0 = increase size (zoom in) // 1.0 = no scaling // > 1.0 = decrease size (zoom out) double initial_zoom = mlt_properties_get_double(filter_properties, "initial_zoom"); double zoom = mlt_properties_get_double(filter_properties, "zoom"); double scale_xy = (100.0 / initial_zoom) - (fabs(mag) * (zoom / 100.0)); if (scale_xy < 0.1) scale_xy = 0.1; // ox is in the range -width to +width with: // > 0 = offset to the left // 0 = no offset // < 0 = offset to the right double left = mlt_properties_get_double(filter_properties, "left"); double right = mlt_properties_get_double(filter_properties, "right"); double ox = apply(left, right, mag, (double) profile->width / 100.0); // oy is in the range -height to +height with: // > 0 = offset up // 0 = no offset // < 0 = offset down double up = mlt_properties_get_double(filter_properties, "up"); double down = mlt_properties_get_double(filter_properties, "down"); double oy = apply(up, down, mag, (double) profile->height / 100.0); // fix_rotate_x is in the range -360 to +360 with: // > 0 = rotate clockwise // 0 = no rotation // < 0 = rotate anticlockwise double counterclockwise = mlt_properties_get_double(filter_properties, "counterclockwise"); double clockwise = mlt_properties_get_double(filter_properties, "clockwise"); double fix_rotate_x = apply(clockwise, counterclockwise, mag, 1.0); // Perform the affine. mlt_service_lock(MLT_FILTER_SERVICE(filter)); mlt_properties affine_properties = MLT_FILTER_PROPERTIES(pdata->affine); mlt_properties_set_double(affine_properties, "transition.scale_x", scale_xy); mlt_properties_set_double(affine_properties, "transition.scale_y", scale_xy); mlt_properties_set_double(affine_properties, "transition.ox", ox); mlt_properties_set_double(affine_properties, "transition.oy", oy); mlt_properties_set_double(affine_properties, "transition.fix_rotate_x", fix_rotate_x); mlt_filter_process(pdata->affine, frame); error = mlt_frame_get_image(frame, image, format, width, height, 0); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } else { if (pdata->preprocess_warned++ == 2) { // This filter depends on the consumer processing the audio before the // video. mlt_log_warning(MLT_FILTER_SERVICE(filter), "Audio not preprocessed. Unable to dance.\n"); } mlt_frame_get_image(frame, image, format, width, height, 0); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { mlt_filter_close(pdata->affine); mlt_filter_close(pdata->fft); free(pdata->mag_prop_name); free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ mlt_filter filter_dance_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); mlt_filter affine_filter = mlt_factory_filter(profile, "affine", "colour:0x00000000"); if (filter && pdata && affine_filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "_filter_private", 1); mlt_properties_set_int(properties, "frequency_low", 20); mlt_properties_set_int(properties, "frequency_high", 20000); mlt_properties_set_double(properties, "threshold", -30.0); mlt_properties_set_double(properties, "osc", 5.0); mlt_properties_set_double(properties, "initial_zoom", 100.0); mlt_properties_set_double(properties, "zoom", 0.0); mlt_properties_set_double(properties, "left", 0.0); mlt_properties_set_double(properties, "right", 0.0); mlt_properties_set_double(properties, "up", 0.0); mlt_properties_set_double(properties, "down", 0.0); mlt_properties_set_double(properties, "clockwise", 0.0); mlt_properties_set_double(properties, "counterclockwise", 0.0); mlt_properties_set_int(properties, "window_size", 2048); // Create a unique ID for storing data on the frame pdata->mag_prop_name = calloc(1, 20); snprintf(pdata->mag_prop_name, 20, "fft_mag.%p", filter); pdata->mag_prop_name[20 - 1] = '\0'; pdata->affine = affine_filter; pdata->fft = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter dance failed\n"); if (filter) { mlt_filter_close(filter); } if (affine_filter) { mlt_filter_close(affine_filter); } if (pdata) { free(pdata); } filter = NULL; } return filter; } mlt-7.38.0/src/modules/plus/filter_dance.yml000664 000000 000000 00000011366 15172202314 020755 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: dance title: Dance version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that moves the image around proportional to the magnitude of the audio spectrum. parameters: - identifier: frequency_low title: Low Frequency type: integer description: > The low end of the frequency range to be used to influence the image motion. mutable: yes readonly: no default: 20 unit: Hz - identifier: frequency_high title: High Frequency type: integer description: > The high end of the frequency range to be used to influence the image motion. mutable: yes readonly: no default: 20000 unit: Hz - identifier: threshold title: Level Threshold type: float description: > The minimum amplitude of sound that must occur within the frequency range to cause the image to move. motion. mutable: yes readonly: no default: -30 minimum: -100 maximum: 0 unit: dB - identifier: osc title: Oscillation type: float description: > Oscillation can be useful to make the image move back and forth during long periods of sound. A value of 0 specifies no oscillation. mutable: yes readonly: no default: 5 minimum: 0 unit: Hz - identifier: initial_zoom title: Initial Zoom type: float description: | The amount to zoom the image before any motion occurs. This can be used to avoid black on the sides of the image when it moves. 100% = no zoom < 100% = zoom out (make the image smaller) > 100% = zoom in (make the image larger) mutable: yes readonly: no default: 100 minimum: 0 maximum: 5000 unit: '%' - identifier: zoom title: Zoom type: float description: | The amount that the audio affects the zoom of the image. < 0% = Image will zoom out (get smaller) with more sound 0% = no zoom > 0% = Image will zoom in (get larger) with more sound mutable: yes readonly: no default: 0 minimum: -100 maximum: 100 unit: '%' - identifier: left title: Left type: float description: | The amount that the audio affects the left offset of the image. 0% = no left offset > 0% = Image will move left with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 100 unit: '%' - identifier: right title: Right type: float description: | The amount that the audio affects the right offset of the image. 0% = no right offset > 0% = Image will move right with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 100 unit: '%' - identifier: up title: Up type: float description: | The amount that the audio affects the upward offset of the image. 0% = no upward offset > 0% = Image will move up with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 100 unit: '%' - identifier: down title: Down type: float description: | The amount that the audio affects the downward offset of the image. 0% = no downward offset > 0% = Image will move down with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 100 unit: '%' - identifier: clockwise title: Clockwise type: float description: | The amount that the audio affects the clockwise rotation of the image. 0% = no clockwise rotation > 0% = Image will rotate clockwise with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 360 unit: degrees - identifier: counterclockwise title: Counterclockwise type: float description: | The amount that the audio affects the counterclockwise rotation of the image. 0% = no counterclockwise rotation > 0% = Image will rotate counterclockwise with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 360 unit: degrees - identifier: window_size title: Window Size type: integer description: > The number of samples that the FFT will be performed on. If window_size is less than the number of samples in a frame, extra samples will be ignored. If window_size is more than the number of samples in a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 2048 mlt-7.38.0/src/modules/plus/filter_dynamic_loudness.c000664 000000 000000 00000021504 15172202314 022657 0ustar00rootroot000000 000000 /* * filter_loudness.c -- normalize audio according to EBU R128 * Copyright (C) 2014-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include typedef struct { ebur128_state *r128; double target_gain; double start_gain; double end_gain; int reset; unsigned int time_elapsed_ms; mlt_position prev_o_pos; } private_data; static void property_changed(mlt_service owner, mlt_filter filter, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); private_data *pdata = (private_data *) filter->child; if (name && pdata && !strcmp(name, "window")) { pdata->reset = 1; } } static void check_for_reset(mlt_filter filter, int channels, int frequency) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; if (pdata->reset) { if (pdata->r128) { ebur128_destroy(&pdata->r128); } pdata->r128 = 0; pdata->target_gain = 0.0; pdata->start_gain = 0.0; pdata->end_gain = 0.0; pdata->reset = 0; pdata->time_elapsed_ms = 0; pdata->prev_o_pos = -1; mlt_properties_set_double(properties, "out_gain", 0.0); mlt_properties_set_double(properties, "in_loudness", -100.0); mlt_properties_set_int(properties, "reset_count", mlt_properties_get_int(properties, "reset_count") + 1); } if (!pdata->r128) { pdata->r128 = ebur128_init(channels, frequency, EBUR128_MODE_I); ebur128_set_max_window(pdata->r128, 400); ebur128_set_max_history(pdata->r128, mlt_properties_get_int(properties, "window") * 1000.0); } } static void analyze_audio(mlt_filter filter, void *buffer, int samples, int frequency) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; int result = -1; double in_loudness = 0.0; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double fps = mlt_profile_fps(profile); ebur128_add_frames_float(pdata->r128, buffer, samples); if (pdata->time_elapsed_ms < 400) { // Waiting for first program loudness measurement. // Use window loudness as initial guess. result = ebur128_loudness_window(pdata->r128, pdata->time_elapsed_ms, &in_loudness); pdata->time_elapsed_ms += samples * 1000 / frequency; } else { result = ebur128_loudness_global(pdata->r128, &in_loudness); } if (result == EBUR128_SUCCESS && in_loudness != HUGE_VAL && in_loudness != -HUGE_VAL) { mlt_properties_set_double(properties, "in_loudness", in_loudness); double target_loudness = mlt_properties_get_double(properties, "target_loudness"); pdata->target_gain = target_loudness - in_loudness; // Make sure gain limits are not exceeded. double max_gain = mlt_properties_get_double(properties, "max_gain"); double min_gain = mlt_properties_get_double(properties, "min_gain"); if (pdata->target_gain > max_gain) { pdata->target_gain = max_gain; } else if (pdata->target_gain < min_gain) { pdata->target_gain = min_gain; } } // Make sure gain does not change too quickly. pdata->start_gain = pdata->end_gain; pdata->end_gain = pdata->target_gain; double max_frame_gain = mlt_properties_get_double(properties, "max_rate") / fps; if (pdata->start_gain - pdata->end_gain > max_frame_gain) { pdata->end_gain = pdata->start_gain - max_frame_gain; } else if (pdata->end_gain - pdata->start_gain > max_frame_gain) { pdata->end_gain = pdata->start_gain + max_frame_gain; } mlt_properties_set_double(properties, "out_gain", pdata->end_gain); } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) filter->child; mlt_position o_pos = mlt_frame_original_position(frame); // Get the producer's audio *format = mlt_audio_f32le; mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); mlt_service_lock(MLT_FILTER_SERVICE(filter)); if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "discontinuity_reset") && abs(o_pos - pdata->prev_o_pos) > 1) { // Assume this is a new clip and restart // Use original position so that transitions between clips are detected. pdata->reset = 1; mlt_log_info(MLT_FILTER_SERVICE(filter), "Reset. Old Pos: %d\tNew Pos: %d\n", pdata->prev_o_pos, o_pos); } check_for_reset(filter, *channels, *frequency); if (o_pos != pdata->prev_o_pos) { // Only analyze the audio if the producer is not paused. analyze_audio(filter, *buffer, *samples, *frequency); } pdata->prev_o_pos = o_pos; if (isnan(pdata->start_gain) || isnan(pdata->end_gain)) { mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } double start_coeff = pdata->start_gain > -90.0 ? pow(10.0, pdata->start_gain / 20.0) : 0.0; double end_coeff = pdata->end_gain > -90.0 ? pow(10.0, pdata->end_gain / 20.0) : 0.0; double coeff_factor = pow((end_coeff / start_coeff), 1.0 / (double) *samples); double coeff = start_coeff; float *p = *buffer; int s = 0; int c = 0; for (s = 0; s < *samples; s++) { coeff = coeff * coeff_factor; for (c = 0; c < *channels; c++) { *p = *p * coeff; p++; } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Destructor for the filter. */ static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { if (pdata->r128) { ebur128_destroy(&pdata->r128); } free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ mlt_filter filter_dynamic_loudness_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set(properties, "target_loudness", "-23.0"); mlt_properties_set(properties, "window", "3.0"); mlt_properties_set(properties, "max_gain", "15.0"); mlt_properties_set(properties, "min_gain", "-15.0"); mlt_properties_set(properties, "max_rate", "3.0"); mlt_properties_set(properties, "discontinuity_reset", "1"); mlt_properties_set(properties, "in_loudness", "-100.0"); mlt_properties_set(properties, "out_gain", "0.0"); mlt_properties_set(properties, "reset_count", "0"); pdata->target_gain = 0.0; pdata->start_gain = 0.0; pdata->end_gain = 0.0; pdata->r128 = 0; pdata->reset = 1; pdata->time_elapsed_ms = 0; pdata->prev_o_pos = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; mlt_events_listen(properties, filter, "property-changed", (mlt_listener) property_changed); } else { if (filter) { mlt_filter_close(filter); filter = NULL; } free(pdata); } return filter; } mlt-7.38.0/src/modules/plus/filter_dynamic_loudness.yml000664 000000 000000 00000005233 15172202314 023237 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: dynamic_loudness title: Dynamic Loudness version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: Dynamically correct audio loudness as recommended by EBU R128. notes: > This filter adjusts the level of the audio based on the loudness of the input. It performs loudness measurement over a specified sliding window of time. Then, it adjusts the gain on the output based on the difference between the measured loudness and the target loudness in order to achieve the desired loudness. parameters: - identifier: target_loudness title: Target Program Loudness type: float description: > The target program loudness in LUFS (Loudness Units Full Scale). readonly: no mutable: yes default: -23.0 minimum: -50.0 maximum: -10.0 unit: LUFS - identifier: window title: Measurement Window type: float description: > The duration of time in seconds over which the loudness is calculated. readonly: no mutable: yes default: 3.0 minimum: 1 maximum: 100000 unit: seconds - identifier: max_gain title: Maximum Gain Increase type: float description: > The maximum amount that the gain will be increased by the filter. readonly: no mutable: yes default: 15 minimum: 0 maximum: 30 unit: dB - identifier: min_gain title: Maximum Gain Decrease type: float description: > The maximum amount that the gain will be decreased by the filter. readonly: no mutable: yes default: -15 minimum: 0 maximum: -30 unit: dB - identifier: discontinuity_reset title: Reset on Discontinuity type: boolean description: > Reset the measurement if a discontinuity occurs like seeking or new clip is detected. Useful for playlists and tracks. default: 1 - identifier: in_loudness title: Input Program Loudness type: float description: > The program loudness measured on the input over the duration of the window. readonly: yes unit: LUFS - identifier: out_gain title: Output Gain type: float description: > The amount of gain applied to the last frame. Updated with each new frame. readonly: yes unit: dB - identifier: reset_count title: Reset Count type: integer description: > The number of times the filter has reset the loudness measurement. The measurement is reset whenever the filter detects a discontinuity in the frame sequence. It also resets when it detects that the producer has changed if clip_reset is set. readonly: yes mlt-7.38.0/src/modules/plus/filter_dynamictext.c000664 000000 000000 00000031025 15172202314 021647 0ustar00rootroot000000 000000 /* * filter_dynamictext.c -- dynamic text overlay filter * Copyright (C) 2011-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include // for basename() #include #include #include #include // for stat() #include // for stat() #include // for strftime() and gtime() #ifndef _MSC_VER #include #endif #define MAX_TEXT_LEN 512 /** Get the next token and indicate whether it is enclosed in "# #". */ static int get_next_token(char *str, int *pos, char *token, int *is_keyword) { int token_pos = 0; int str_len = strlen(str); if ((*pos) >= str_len || str[*pos] == '\0') { return 0; } if (str[*pos] == '#') { *is_keyword = 1; (*pos)++; } else { *is_keyword = 0; } while (*pos < str_len && token_pos < MAX_TEXT_LEN - 1) { if (str[*pos] == '\\' && str[(*pos) + 1] == '#') { // Escape Sequence - "#" preceded by "\" - copy the # into the token. token[token_pos] = '#'; token_pos++; (*pos)++; // skip "\" (*pos)++; // skip "#" } else if (str[*pos] == '#') { if (*is_keyword) { // Found the end of the keyword (*pos)++; } break; } else { token[token_pos] = str[*pos]; token_pos++; (*pos)++; } } token[token_pos] = '\0'; return 1; } static void get_timecode_str(mlt_filter filter, mlt_frame frame, char *text, mlt_time_format time_format) { mlt_position frames = mlt_frame_get_position(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); char *s = mlt_properties_frames_to_time(properties, frames, time_format); if (s) strncat(text, s, MAX_TEXT_LEN - strlen(text) - 1); } static void get_frame_str(mlt_filter filter, mlt_frame frame, char *text) { int pos = mlt_frame_get_position(frame); char s[12]; snprintf(s, sizeof(s) - 1, "%d", pos); strncat(text, s, MAX_TEXT_LEN - strlen(text) - 1); } static void get_filedate_str(const char *keyword, mlt_filter filter, mlt_frame frame, char *text) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); char *filename = mlt_properties_get(producer_properties, "resource"); struct stat file_info; if (!mlt_stat(filename, &file_info)) { const char *format = "%Y/%m/%d"; int n = strlen("filedate") + 1; struct tm *time_info = gmtime(&(file_info.st_mtime)); char *date = calloc(1, MAX_TEXT_LEN); if (strlen(keyword) > n) format = &keyword[n]; strftime(date, MAX_TEXT_LEN, format, time_info); strncat(text, date, MAX_TEXT_LEN - strlen(text) - 1); free(date); } } static void get_localfiledate_str(const char *keyword, mlt_filter filter, mlt_frame frame, char *text) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); char *filename = mlt_properties_get(producer_properties, "resource"); struct stat file_info; if (!mlt_stat(filename, &file_info)) { const char *format = "%Y/%m/%d"; int n = strlen("localfiledate") + 1; struct tm *time_info = localtime(&(file_info.st_mtime)); char *date = calloc(1, MAX_TEXT_LEN); if (strlen(keyword) > n) format = &keyword[n]; strftime(date, MAX_TEXT_LEN, format, time_info); strncat(text, date, MAX_TEXT_LEN - strlen(text) - 1); free(date); } } static void get_localtime_str(const char *keyword, char *text) { const char *format = "%Y/%m/%d %H:%M:%S"; int n = strlen("localtime") + 1; time_t now = time(NULL); struct tm *time_info = localtime(&now); char *date = calloc(1, MAX_TEXT_LEN); if (strlen(keyword) > n) format = &keyword[n]; strftime(date, MAX_TEXT_LEN, format, time_info); strncat(text, date, MAX_TEXT_LEN - strlen(text) - 1); free(date); } static void get_resource_str(mlt_filter filter, mlt_frame frame, char *text) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); strncat(text, mlt_properties_get(producer_properties, "resource"), MAX_TEXT_LEN - strlen(text) - 1); } static void get_filename_str(mlt_filter filter, mlt_frame frame, char *text) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); char *filename = mlt_properties_get(producer_properties, "resource"); struct stat file_info; if (!mlt_stat(filename, &file_info)) { strncat(text, basename(filename), MAX_TEXT_LEN - strlen(text) - 1); } } static void get_basename_str(mlt_filter filter, mlt_frame frame, char *text) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); char *filename = strdup(mlt_properties_get(producer_properties, "resource")); struct stat file_info; if (!mlt_stat(filename, &file_info)) { char *bname = basename(filename); char *ext = strrchr(bname, '.'); if (ext) { *ext = '\0'; } strncat(text, bname, MAX_TEXT_LEN - strlen(text) - 1); } free(filename); } static void get_createdate_str(const char *keyword, mlt_filter filter, mlt_frame frame, char *text) { time_t creation_date = (time_t) (mlt_producer_get_creation_time(mlt_frame_get_original_producer(frame)) / 1000); const char *format = "%Y/%m/%d"; int n = strlen("createdate") + 1; if (strlen(keyword) > n) format = &keyword[n]; int text_length = strlen(text); strftime(text + text_length, MAX_TEXT_LEN - text_length - 1, format, localtime(&creation_date)); } /** Perform substitution for keywords that are enclosed in "# #". */ static void substitute_keywords(mlt_filter filter, char *result, char *value, mlt_frame frame) { char keyword[MAX_TEXT_LEN] = ""; int pos = 0; int is_keyword = 0; while (get_next_token(value, &pos, keyword, &is_keyword)) { if (!is_keyword) { strncat(result, keyword, MAX_TEXT_LEN - strlen(result) - 1); } else if (!strcmp(keyword, "timecode") || !strcmp(keyword, "smpte_df")) { get_timecode_str(filter, frame, result, mlt_time_smpte_df); } else if (!strcmp(keyword, "smpte_ndf")) { get_timecode_str(filter, frame, result, mlt_time_smpte_ndf); } else if (!strcmp(keyword, "frame")) { get_frame_str(filter, frame, result); } else if (!strncmp(keyword, "filedate", 8)) { get_filedate_str(keyword, filter, frame, result); } else if (!strncmp(keyword, "localfiledate", 13)) { get_localfiledate_str(keyword, filter, frame, result); } else if (!strncmp(keyword, "localtime", 9)) { get_localtime_str(keyword, result); } else if (!strcmp(keyword, "resource")) { get_resource_str(filter, frame, result); } else if (!strcmp(keyword, "filename")) { get_filename_str(filter, frame, result); } else if (!strcmp(keyword, "basename")) { get_basename_str(filter, frame, result); } else if (!strncmp(keyword, "createdate", 10)) { get_createdate_str(keyword, filter, frame, result); } else { // replace keyword with property value from this frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); char *frame_value = mlt_properties_get(frame_properties, keyword); if (frame_value) { strncat(result, frame_value, MAX_TEXT_LEN - strlen(result) - 1); } } } } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); char *dynamic_text = mlt_properties_get(properties, "argument"); if (!dynamic_text || !strcmp("", dynamic_text)) return frame; mlt_filter text_filter = mlt_properties_get_data(properties, "_text_filter", NULL); mlt_properties text_filter_properties = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(text_filter)); // Apply keyword substitution before passing the text to the filter. char *result = calloc(1, MAX_TEXT_LEN); substitute_keywords(filter, result, dynamic_text, frame); mlt_properties_set_string(text_filter_properties, "argument", result); free(result); mlt_properties_pass_list(text_filter_properties, properties, "geometry family size weight style fgcolour bgcolour olcolour pad " "halign valign outline underline strikethrough opacity"); mlt_filter_set_in_and_out(text_filter, mlt_filter_get_in(filter), mlt_filter_get_out(filter)); return mlt_filter_process(text_filter, frame); } /** Constructor for the filter. */ mlt_filter filter_dynamictext_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); mlt_filter text_filter = mlt_factory_filter(profile, "qtext", NULL); if (!text_filter) text_filter = mlt_factory_filter(profile, "text", NULL); if (!text_filter) mlt_log_warning(MLT_FILTER_SERVICE(filter), "Unable to create text filter.\n"); if (filter && text_filter) { mlt_properties my_properties = MLT_FILTER_PROPERTIES(filter); // Register the text filter for reuse/destruction mlt_properties_set_data(my_properties, "_text_filter", text_filter, 0, (mlt_destructor) mlt_filter_close, NULL); // Assign default values mlt_properties_set_string(my_properties, "argument", arg ? arg : "#timecode#"); mlt_properties_set_string(my_properties, "geometry", "0%/0%:100%x100%:100%"); mlt_properties_set_string(my_properties, "family", "Sans"); mlt_properties_set_string(my_properties, "size", "48"); mlt_properties_set_string(my_properties, "weight", "400"); mlt_properties_set_string(my_properties, "style", "normal"); mlt_properties_set_string(my_properties, "fgcolour", "0x000000ff"); mlt_properties_set_string(my_properties, "bgcolour", "0x00000020"); mlt_properties_set_string(my_properties, "olcolour", "0x00000000"); mlt_properties_set_string(my_properties, "pad", "0"); mlt_properties_set_string(my_properties, "halign", "left"); mlt_properties_set_string(my_properties, "valign", "top"); mlt_properties_set_string(my_properties, "outline", "0"); mlt_properties_set_string(my_properties, "underline", "0"); mlt_properties_set_string(my_properties, "strikethrough", "0"); mlt_properties_set_string(my_properties, "opacity", "1.0"); mlt_properties_set_int(my_properties, "_filter_private", 1); filter->process = filter_process; } else { if (filter) { mlt_filter_close(filter); } if (text_filter) { mlt_filter_close(text_filter); } filter = NULL; } return filter; } mlt-7.38.0/src/modules/plus/filter_dynamictext.yml000664 000000 000000 00000012567 15172202314 022240 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: dynamictext title: Dynamic text version: 4 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Overlay dynamic text onto the video notes: > The dynamic text filter will search for keywords in the text to be overlaid and will replace those keywords on a frame-by-frame basis. parameters: - identifier: argument argument: yes title: Dynamic text type: string description: | The text to overlay. May include keywords enclosed in "#". Keywords include: * #createdate# - Best guess of file creation date * #smpte_df# - SMPTE drop-frame timecode of the frame * #smpte_ndf# - SMPTE non-drop-frame timecode of the frame * #timecode# - same as #smpte_df# * #frame# - frame number of the frame * #filedate# - modification date of the file (GMT) * #localfiledate# - modification date of the file (local) * #localtime# - current system date and time * #resource# - resource of the producer that produced the frame Timecode keywords are based on the framerate and position of the frame. Time-based keywords can include a strftime format string to customize the output as long as you put some delimiter except "#" between the keyword and the format string and the keyword comes first. For example, "#localtime %I:%M:%S %p#" shows only the time in 12-hour format. Keywords may also be any frame property (e.g. #meta.media.0.codec.frame_rate#) The # may be escaped with "\". required: yes readonly: no default: > #trick to escape "#" character #timecode# widget: text - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 mutable: yes widget: spinner - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo - identifier: opacity title: Opacity type: float description: Opacity of all elements - text, outline, and background readonly: no default: 1.0 minimum: 0 maximum: 1.0 mutable: yes widget: slider mlt-7.38.0/src/modules/plus/filter_fft.c000664 000000 000000 00000024360 15172202314 020101 0ustar00rootroot000000 000000 /* * filter_fft.c -- perform fft on audio * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include // sqrt() #include // calloc(), free() #include // memset(), memmove() // Private Constants static const float MAX_S16_AMPLITUDE = 32768.0; static const int MIN_WINDOW_SIZE = 500; static const double PI = 3.14159265358979323846; // Private Types typedef struct { int initialized; unsigned int window_size; double *fft_in; fftw_complex *fft_out; fftw_plan fft_plan; int bin_count; int sample_buff_count; float *sample_buff; float *hann; float *out_bins; mlt_position expected_pos; } private_data; static int initFft(mlt_filter filter) { int error = 0; private_data *private = (private_data *) filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); if (private->window_size < MIN_WINDOW_SIZE) { private->window_size = mlt_properties_get_int(filter_properties, "window_size"); if (private->window_size >= MIN_WINDOW_SIZE) { private->initialized = 1; private->bin_count = private->window_size / 2 + 1; private->sample_buff_count = 0; private->out_bins = mlt_pool_alloc(private->bin_count * sizeof(*private->out_bins)); // Initialize sample buffer private->sample_buff = mlt_pool_alloc(private->window_size * sizeof(*private->sample_buff)); memset(private->sample_buff, 0, sizeof(*private->sample_buff) * private->window_size); // Initialize fftw variables private->fft_in = fftw_alloc_real(private->window_size); private->fft_out = fftw_alloc_complex(private->bin_count); private->fft_plan = fftw_plan_dft_r2c_1d(private->window_size, private->fft_in, private->fft_out, FFTW_ESTIMATE); // Initialize the hanning window function private->hann = mlt_pool_alloc(private->window_size * sizeof(*private->hann)); int i = 0; for (i = 0; i < private->window_size; i++) { private->hann[i] = 0.5 * (1 - cos(2 * PI * i / private->window_size)); } mlt_properties_set_int(filter_properties, "bin_count", private->bin_count); mlt_properties_set_data(filter_properties, "bins", private->out_bins, 0, 0, 0); } if (private->window_size < MIN_WINDOW_SIZE || !private->fft_in || !private->fft_out || !private->fft_plan) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Unable to initialize FFT\n"); error = 1; private->window_size = 0; } } return error; } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); private_data *private = (private_data *) filter->child; int c = 0; int s = 0; // Sanity if (*format != mlt_audio_s16 && *format != mlt_audio_float) { *format = mlt_audio_float; } // Get the audio mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); // The service must stay locked while using the private FFT data mlt_service_lock(MLT_FILTER_SERVICE(filter)); if (!private->initialized) { private->expected_pos = mlt_frame_get_position(frame); } if (!initFft(filter)) { if (private->expected_pos != mlt_frame_get_position(frame)) { // Reset the sample buffer when seeking occurs. memset(private->sample_buff, 0, sizeof(*private->sample_buff) * private->window_size); private->sample_buff_count = 0; mlt_log_info(MLT_FILTER_SERVICE(filter), "Buffer Reset %d:%d\n", private->expected_pos, mlt_frame_get_position(frame)); private->expected_pos = mlt_frame_get_position(frame); } int new_samples = 0; int old_samples = 0; if (*samples >= private->window_size) { // Ignore samples that don't fit in the window new_samples = private->window_size; old_samples = 0; } else { new_samples = *samples; // Shift the previous samples (discarding oldest samples) old_samples = private->window_size - new_samples; memmove(private->sample_buff, private->sample_buff + new_samples, sizeof(*private->sample_buff) * old_samples); } // Zero out the space for the new samples memset(private->sample_buff + old_samples, 0, sizeof(*private->sample_buff) * new_samples); // Copy the new samples into the sample buffer if (*format == mlt_audio_s16) { int16_t *aud = (int16_t *) *buffer; // For each sample, add all channels for (c = 0; c < *channels; c++) { for (s = 0; s < new_samples; s++) { double sample = aud[s * *channels + c]; // Scale to +/-1 sample /= MAX_S16_AMPLITUDE; sample /= (double) *channels; private->sample_buff[old_samples + s] += sample; } } } else if (*format == mlt_audio_float) { float *aud = (float *) *buffer; // For each sample, add all channels for (c = 0; c < *channels; c++) { for (s = 0; s < new_samples; s++) { double sample = aud[c * *samples + s]; sample /= (double) *channels; private->sample_buff[old_samples + s] += sample; } } } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Unsupported format %d\n", *format); } private->sample_buff_count += *samples; if (private->sample_buff_count > private->window_size) { private->sample_buff_count = private->window_size; } // Copy samples to fft input while applying window function for (s = 0; s < private->window_size; s++) { private->fft_in[s] = private->sample_buff[s] * private->hann[s]; } // Perform the FFT fftw_execute(private->fft_plan); // Convert to magnitudes int bin = 0; for (bin = 0; bin < private->bin_count; bin++) { // Convert FFT output to magnitudes private->out_bins[bin] = sqrt(private->fft_out[bin][0] * private->fft_out[bin][0] + private->fft_out[bin][1] * private->fft_out[bin][1]); // Scale to 0.0 - 1.0 private->out_bins[bin] = (4.0 * private->out_bins[bin]) / (float) private->window_size; } private->expected_pos++; } mlt_properties_set_double(filter_properties, "bin_width", (double) *frequency / (double) private->window_size); mlt_properties_set_double(filter_properties, "window_level", (double) private->sample_buff_count / (double) private->window_size); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } static void filter_close(mlt_filter filter) { private_data *private = (private_data *) filter->child; if (private) { fftw_free(private->fft_in); fftw_free(private->fft_out); fftw_destroy_plan(private->fft_plan); mlt_pool_release(private->sample_buff); mlt_pool_release(private->hann); mlt_pool_release(private->out_bins); free(private); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ mlt_filter filter_fft_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *private = (private_data *) calloc(1, sizeof(private_data)); if (filter && private) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "_filter_private", 1); mlt_properties_set_int(properties, "window_size", 2048); mlt_properties_set_double(properties, "window_level", 0.0); mlt_properties_set_double(properties, "bin_width", 0.0); mlt_properties_set_int(properties, "bin_count", 0); mlt_properties_set_data(properties, "bins", 0, 0, 0, 0); memset(private, 0, sizeof(private_data)); filter->close = filter_close; filter->process = filter_process; filter->child = private; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter FFT failed\n"); if (filter) { mlt_filter_close(filter); } if (private) { free(private); } filter = NULL; } return filter; } mlt-7.38.0/src/modules/plus/filter_fft.yml000664 000000 000000 00000003565 15172202314 020464 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: fft title: FFT version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: > An audio filter that computes the FFT of the audio. This filter does not modify the audio or the image. It only computes the FFT and stores the result in the "bins" property of the filter. parameters: - identifier: window_size title: Window Size type: integer description: > The number of samples that the transform will be performed on. If window_size is less than the number of samples in a frame, extra samples will be ignored. If window_size is more than the number of samples in a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 2048 - identifier: window_level title: Window Level type: float description: > The level of the sample window. 0 indicates that there are no samples in the window. 1.0 indicates that the window is full. The transform of a window that is not full may show frequency spikes that are not really present in the audio. readonly: yes minimum: 0 maximum: 1.0 - identifier: bin_width title: Bin Width type: float description: > The width of each bin in Hz. readonly: yes unit: Hz - identifier: bin_count title: Bin Count type: integer description: > The number of bins that are output from the transform. readonly: yes - identifier: bins title: Output Bins description: > A pointer to an array of floats that represent the magnitude of the output of the transform. bin[i] = sqrt( real[i]^2 + imag[i]^2 ) readonly: yes mlt-7.38.0/src/modules/plus/filter_gradientmap.cpp000664 000000 000000 00000024200 15172202314 022146 0ustar00rootroot000000 000000 extern "C" { #include #include #include } #include #include #include #include #include #include #define MAX_STOPS 32 struct stop { mlt_color color; double pos; }; bool operator<(const stop &lhs, const stop &rhs) { return lhs.pos < rhs.pos; } bool operator==(const stop &lhs, const stop &rhs) { return lhs.pos == rhs.pos; } struct gradient_cache { std::vector gradient; std::vector colors; }; typedef std::map gradientmap_cache; struct slice_desc { mlt_image_s image; mlt_image_format format; const std::vector *colors; }; static std::string gradient_key(mlt_properties props) { int count = mlt_properties_count(props); std::string res; // This function will ignore tailing color.* values if one of the indices // is missing. for (int i = 1; i <= MIN(count, MAX_STOPS); ++i) { char name[9] = ""; sprintf(name, "stop.%d", i); char *value = mlt_properties_get(props, name); if (!value) break; res += value; res += ";"; } // Gradient cache keys are stops joined by semicolons. return res; } static int parse_color(const char *value, char **end) { // Parse a hex color value as #RRGGBB or #AARRGGBB. if (value[0] == '#') { unsigned int rgb = strtoul(value + 1, end, 16); unsigned int alpha = (value - *end > 7) ? (rgb >> 24) : 0xff; return (rgb << 8) | alpha; } // Do hex and decimal explicitly to avoid decimal value with leading zeros // interpreted as octal. else if (value[0] == '0' && value[1] == 'x') { return strtoul(value + 2, end, 16); } else { return strtol(value, end, 10); } } static void deserialize_gradient(mlt_properties props, std::vector &gradient) { int count = mlt_properties_count(props); for (int i = 1; i <= MIN(count, MAX_STOPS); ++i) { char name[9]; sprintf(name, "stop.%d", i); char *value = mlt_properties_get(props, name); if (!value) break; char *p = nullptr; int c = parse_color(value, &p); value = p; double pos = strtod(value, &p); if (p == value) continue; if (p[0] == '%') pos /= 100.0; mlt_color color = {uint8_t((c >> 24) & 0xff), uint8_t((c >> 16) & 0xff), uint8_t((c >> 8) & 0xff), uint8_t(c & 0xff)}; gradient.push_back({color, pos}); } if (gradient.size() == 0) { // Use a black to white range by default. gradient.push_back({{0x00, 0x00, 0x00, 0xff}, 0.0}); gradient.push_back({{0xff, 0xff, 0xff, 0xff}, 1.0}); } else { // Sort the gradient vector using a stable sort. std::stable_sort(gradient.begin(), gradient.end()); // Deduplicate stops by their position in the gradient, first come, // only served. auto last = std::unique(gradient.begin(), gradient.end()); gradient.erase(last, gradient.end()); } } static void compute_colors(const std::vector &gradient, std::vector &colors) { for (size_t c = 0; c < colors.size(); ++c) { // Intensity is proportional to its order in the cache. float intensity = float(c) / float(colors.size()); const stop &front = gradient.front(); const stop &back = gradient.back(); // Any value positioned before or after the extremes maps to those. if (intensity <= front.pos) { colors[c] = front.color; continue; } else if (intensity >= back.pos) { colors[c] = back.color; continue; } for (size_t i = 0; i < gradient.size() - 1; ++i) { const stop &s = gradient[i]; const stop &e = gradient[i + 1]; // Only care if the intensity is bounded by start and end. if (intensity > e.pos) { continue; } else if (intensity == s.pos) { colors[c] = s.color; break; } else if (intensity == e.pos) { colors[c] = e.color; break; } // This interpolation can result in values outside the 0 to 1 range // due to roundoff errors. double pos = (intensity - s.pos) / (e.pos - s.pos); // Calculated color values can be above 255 or below 0 due to above, // so clamp it to avoid incorrect values in the color cache. colors[c].r = CLAMP(pos * e.color.r + (1 - pos) * s.color.r, 0, 255); colors[c].g = CLAMP(pos * e.color.g + (1 - pos) * s.color.g, 0, 255); colors[c].b = CLAMP(pos * e.color.b + (1 - pos) * s.color.b, 0, 255); colors[c].a = CLAMP(pos * e.color.a + (1 - pos) * s.color.a, 0, 255); break; } } } static mlt_color gradient_map( const std::vector &colors, float r, float g, float b, float a) { // This is what Krita does, although other methods could be explored. float intensity = r * 0.30 + g * 0.59 + b * 0.11; uint32_t t = intensity * colors.size() + 0.5; if (colors.size() > t) { mlt_color c = colors[t]; c.a = a; return c; } else { // Because the colors are sorted by position this is the last of the // gradient stops. return colors.back(); } } static int sliced_proc(int id, int index, int jobs, void *data) { (void) id; slice_desc *desc = reinterpret_cast(data); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->image.height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; if (desc->format == mlt_image_rgba) { int line_size = desc->image.strides[0]; for (int j = slice_line_start; j < slice_line_end; ++j) { uint8_t *p = desc->image.planes[0] + j * line_size; for (int i = 0; i < line_size; i += 4) { float r = (float) p[i] / 255.0; float g = (float) p[i + 1] / 255.0; float b = (float) p[i + 2] / 255.0; float a = (float) p[i + 3]; mlt_color c = gradient_map(*desc->colors, r, g, b, a); p[i] = c.r; p[i + 1] = c.g; p[i + 2] = c.b; p[i + 3] = c.a; } } } else if (desc->format == mlt_image_rgba64) { int line_size = desc->image.strides[0] / 2; // two bytes per word for (int j = slice_line_start; j < slice_line_end; ++j) { uint16_t *p = (uint16_t *) desc->image.planes[0] + j * line_size; for (int i = 0; i < line_size; i += 4) { float r = (float) p[i] / 65535.0; float g = (float) p[i + 1] / 65535.0; float b = (float) p[i + 2] / 65535.0; float a = (float) p[i + 3] / 257.0; mlt_color c = gradient_map(*desc->colors, r, g, b, a); p[i] = (uint16_t) c.r * 257; p[i + 1] = (uint16_t) c.g * 257; p[i + 2] = (uint16_t) c.b * 257; p[i + 3] = (uint16_t) c.a * 257; } } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = reinterpret_cast(mlt_frame_pop_service(frame)); gradientmap_cache *cache = reinterpret_cast(filter->child); if (*format != mlt_image_rgba64) *format = mlt_image_rgba; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error == 0) { // As odd as it sounds, a nested cache is used to speed things up. A // parent holds as many children identified by a key computed from the // color stops and each one of those contains cached results calculated // from the gradient. A color cache contains width plus height entries, // so this might result in a loss of color resolution. Else, the process // is really slow (O(n^3)) if each pixel has to be computed. std::string k = gradient_key(MLT_FILTER_PROPERTIES(filter)); auto it = cache->find(k); if (it == cache->end()) { it = cache->insert(it, {k, {}}); gradient_cache &e = it->second; e.gradient.reserve(MAX_STOPS); e.colors.resize(*width + *height, {0x00, 0x00, 0x00, 0xff}); deserialize_gradient(MLT_FILTER_PROPERTIES(filter), e.gradient); compute_colors(e.gradient, e.colors); } const std::vector &colors = it->second.colors; slice_desc desc; desc.colors = &colors; desc.format = *format; mlt_image_set_values(&desc.image, *image, *format, *width, *height); mlt_slices_run_normal(0, sliced_proc, &desc); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { gradientmap_cache *cache = reinterpret_cast(filter->child); delete cache; filter->child = nullptr; filter->close = nullptr; filter->parent.close = nullptr; mlt_service_close(&filter->parent); } extern "C" { mlt_filter filter_gradientmap_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); gradientmap_cache *cache = new gradientmap_cache; if (filter && cache) { filter->process = filter_process; filter->close = filter_close; filter->child = cache; } return filter; } } mlt-7.38.0/src/modules/plus/filter_gradientmap.yml000664 000000 000000 00000001547 15172202314 022176 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: gradientmap title: Gradient Map version: 2 copyright: Martin Rodriguez Reboredo creator: Martin Rodriguez Reboredo license: LGPLv2.1 language: en tags: - Video description: Maps the colors of an image to a gradient according to their intensity. parameters: - identifier: stop.* title: Color Stop type: string description: | The gradient color stops. A set of pairs that each describe a point in the gradient. A pair consists on a color and a position with a separator in between them. By default, the filter has a gradient that goes from black to white. stop.1='#ff000000 0.0' stop.2='#ffffffff 1.0' This results in the image turned into black and white. The gradient can hold up to 32 colors. On repeated stop positions only the first one is taken. mlt-7.38.0/src/modules/plus/filter_hslprimaries.c000664 000000 000000 00000030726 15172202314 022027 0ustar00rootroot000000 000000 /* * filter_hslprimaries.c * Copyright (C) 2024-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hsl.h" #include #include #include #include static const float MAX_OVERLAP_RANGE = 29.9; enum { HSL_REGION_RED, HSL_REGION_YELLOW, HSL_REGION_GREEN, HSL_REGION_CYAN, HSL_REGION_BLUE, HSL_REGION_MAGENTA, HSL_REGION_COUNT, }; struct { float beg; float mid; float end; } hueRegions[HSL_REGION_COUNT] = { [HSL_REGION_RED] = {330.0 / 360.0, 0.0 / 360.0, 30.0 / 360.0}, [HSL_REGION_YELLOW] = {30.0 / 360.0, 60.0 / 360.0, 90.0 / 360.0}, [HSL_REGION_GREEN] = {90.0 / 360.0, 120.0 / 360.0, 150.0 / 360.0}, [HSL_REGION_CYAN] = {150.0 / 360.0, 180.0 / 360.0, 210.0 / 360.0}, [HSL_REGION_BLUE] = {210.0 / 360.0, 240.0 / 360.0, 270.0 / 360.0}, [HSL_REGION_MAGENTA] = {270.0 / 360.0, 300.0 / 360.0, 330.0 / 360.0}, }; typedef struct { mlt_filter filter; uint8_t *image; mlt_image_format format; int width; int height; float h_shift[HSL_REGION_COUNT]; float s_scale[HSL_REGION_COUNT]; float l_scale[HSL_REGION_COUNT]; float overlap; float overlap_range; } sliced_desc; static int hueToRegion(float h) { for (int i = 0; i < HSL_REGION_COUNT; i++) { if (h < hueRegions[i].end) { return i; } } return HSL_REGION_RED; } static void adjust_pixel(float *r, float *g, float *b, sliced_desc *desc) { float h, s, l; rgbToHsl(*r, *g, *b, &h, &s, &l); if (s == 0) { // No color. Do not adjust. return; } int region = hueToRegion(h); float h_shift = desc->h_shift[region]; float s_scale = desc->s_scale[region]; float l_scale = desc->l_scale[region]; if (desc->overlap != 0.0) { int o_region = region; float o_strength = 0.0; if (region == HSL_REGION_RED) { // Special case to handle red wrap around if (h > hueRegions[HSL_REGION_RED].beg && h < (hueRegions[HSL_REGION_RED].beg + desc->overlap_range)) { o_region = HSL_REGION_MAGENTA; o_strength = (h - hueRegions[region].beg) / desc->overlap_range; } else if (h < hueRegions[HSL_REGION_RED].end && h > (hueRegions[HSL_REGION_RED].end - desc->overlap_range)) { o_region = HSL_REGION_YELLOW; o_strength = (hueRegions[region].end - h) / desc->overlap_range; } } else if (h < (hueRegions[region].beg + desc->overlap_range)) { o_region = (region - 1 + HSL_REGION_COUNT) % HSL_REGION_COUNT; o_strength = (h - hueRegions[region].beg) / desc->overlap_range; } else if (h > (hueRegions[region].end - desc->overlap_range)) { o_region = (region + 1) % HSL_REGION_COUNT; o_strength = (hueRegions[region].end - h) / desc->overlap_range; } if (o_region != region) { float edge_hshift = (desc->h_shift[region] + desc->h_shift[o_region]) / 2.0; h_shift = (desc->h_shift[region] * o_strength) + (edge_hshift * (1.0 - o_strength)); float edge_sscale = (desc->s_scale[region] + desc->s_scale[o_region]) / 2.0; s_scale = (desc->s_scale[region] * o_strength) + (edge_sscale * (1.0 - o_strength)); float edge_lscale = (desc->l_scale[region] + desc->l_scale[o_region]) / 2.0; l_scale = (desc->l_scale[region] * o_strength) + (edge_lscale * (1.0 - o_strength)); } } if (h_shift == 0.0 && s_scale == 1.0 && l_scale == 1.0) { // No adjustment for this pixel return; } // Apply the adjustment h = h + h_shift; h = fmod(h, 1.0); s = s * s_scale; s = s < 0.0 ? 0.0 : s > 1.0 ? 1.0 : s; l = l * l_scale; l = l < 0.0 ? 0.0 : l > 1.0 ? 1.0 : l; hslToRgb(h, s, l, r, g, b); } static int sliced_proc(int id, int index, int jobs, void *data) { (void) id; // unused sliced_desc *desc = ((sliced_desc *) data); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); int total = desc->width * slice_height; switch (desc->format) { case mlt_image_rgb: { uint8_t *sample = desc->image + slice_line_start * desc->width * 3; for (int i = 0; i < total; i++) { float r = sample[0] / 255.0; float g = sample[1] / 255.0; float b = sample[2] / 255.0; adjust_pixel(&r, &g, &b, desc); sample[0] = lrint(r * 255.0); sample[1] = lrint(g * 255.0); sample[2] = lrint(b * 255.0); sample += 3; } break; } case mlt_image_rgba: { uint8_t *sample = desc->image + slice_line_start * desc->width * 4; for (int i = 0; i < total; i++) { float r = sample[0] / 255.0; float g = sample[1] / 255.0; float b = sample[2] / 255.0; adjust_pixel(&r, &g, &b, desc); sample[0] = lrint(r * 255.0); sample[1] = lrint(g * 255.0); sample[2] = lrint(b * 255.0); sample += 4; } break; } case mlt_image_rgba64: { uint16_t *sample = (uint16_t *) desc->image + slice_line_start * desc->width * 4; for (int i = 0; i < total; i++) { float r = sample[0] / 65535.0; float g = sample[1] / 65535.0; float b = sample[2] / 65535.0; adjust_pixel(&r, &g, &b, desc); sample[0] = lrint(r * 65535.0); sample[1] = lrint(g * 65535.0); sample[2] = lrint(b * 65535.0); sample += 4; } break; } default: mlt_log_error(MLT_FILTER_SERVICE(desc->filter), "Invalid image format: %s\n", mlt_image_format_name(desc->format)); break; } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); sliced_desc desc; desc.h_shift[HSL_REGION_RED] = mlt_properties_anim_get_double(properties, "h_shift_red", position, length); desc.s_scale[HSL_REGION_RED] = mlt_properties_anim_get_double(properties, "s_scale_red", position, length); desc.l_scale[HSL_REGION_RED] = mlt_properties_anim_get_double(properties, "l_scale_red", position, length); desc.h_shift[HSL_REGION_YELLOW] = mlt_properties_anim_get_double(properties, "h_shift_yellow", position, length); desc.s_scale[HSL_REGION_YELLOW] = mlt_properties_anim_get_double(properties, "s_scale_yellow", position, length); desc.l_scale[HSL_REGION_YELLOW] = mlt_properties_anim_get_double(properties, "l_scale_yellow", position, length); desc.h_shift[HSL_REGION_GREEN] = mlt_properties_anim_get_double(properties, "h_shift_green", position, length); desc.s_scale[HSL_REGION_GREEN] = mlt_properties_anim_get_double(properties, "s_scale_green", position, length); desc.l_scale[HSL_REGION_GREEN] = mlt_properties_anim_get_double(properties, "l_scale_green", position, length); desc.h_shift[HSL_REGION_CYAN] = mlt_properties_anim_get_double(properties, "h_shift_cyan", position, length); desc.s_scale[HSL_REGION_CYAN] = mlt_properties_anim_get_double(properties, "s_scale_cyan", position, length); desc.l_scale[HSL_REGION_CYAN] = mlt_properties_anim_get_double(properties, "l_scale_cyan", position, length); desc.h_shift[HSL_REGION_BLUE] = mlt_properties_anim_get_double(properties, "h_shift_blue", position, length); desc.s_scale[HSL_REGION_BLUE] = mlt_properties_anim_get_double(properties, "s_scale_blue", position, length); desc.l_scale[HSL_REGION_BLUE] = mlt_properties_anim_get_double(properties, "l_scale_blue", position, length); desc.h_shift[HSL_REGION_MAGENTA] = mlt_properties_anim_get_double(properties, "h_shift_magenta", position, length); desc.s_scale[HSL_REGION_MAGENTA] = mlt_properties_anim_get_double(properties, "s_scale_magenta", position, length); desc.l_scale[HSL_REGION_MAGENTA] = mlt_properties_anim_get_double(properties, "l_scale_magenta", position, length); desc.overlap = mlt_properties_anim_get_double(properties, "overlap", position, length); // Check if there is any processing to do int no_change = 1; for (int i = 0; i < HSL_REGION_COUNT; i++) { if (desc.h_shift[i] != 0.0 || desc.s_scale[i] != 100.0 || desc.l_scale[i] != 100.0) { no_change = 0; break; } } if (no_change) { return mlt_frame_get_image(frame, image, format, width, height, writable); } int error = 0; // Make sure the format is acceptable if (*format != mlt_image_rgb && *format != mlt_image_rgba && *format != mlt_image_rgba64) { *format = mlt_image_rgb; } // Get the image writable = 1; error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error) { // Scale the parameters down to [0-1] for (int i = 0; i < HSL_REGION_COUNT; i++) { desc.h_shift[i] /= 360.0; desc.s_scale[i] /= 100.0; desc.l_scale[i] /= 100.0; } desc.overlap /= 100.0; desc.overlap_range = desc.overlap * MAX_OVERLAP_RANGE / 360.0; // Perform the processing desc.format = *format; desc.height = *height; desc.width = *width; desc.image = *image; mlt_slices_run_normal(0, sliced_proc, &desc); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_hslprimaries_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_double(properties, "h_shift_red", 0); mlt_properties_set_double(properties, "s_scale_red", 100); mlt_properties_set_double(properties, "l_scale_red", 100); mlt_properties_set_double(properties, "h_shift_yellow", 0); mlt_properties_set_double(properties, "s_scale_yellow", 100); mlt_properties_set_double(properties, "l_scale_yellow", 100); mlt_properties_set_double(properties, "h_shift_green", 0); mlt_properties_set_double(properties, "s_scale_green", 100); mlt_properties_set_double(properties, "l_scale_green", 100); mlt_properties_set_double(properties, "h_shift_cyan", 0); mlt_properties_set_double(properties, "s_scale_cyan", 100); mlt_properties_set_double(properties, "l_scale_cyan", 100); mlt_properties_set_double(properties, "h_shift_blue", 0); mlt_properties_set_double(properties, "s_scale_blue", 100); mlt_properties_set_double(properties, "l_scale_blue", 100); mlt_properties_set_double(properties, "h_shift_magenta", 0); mlt_properties_set_double(properties, "s_scale_magenta", 100); mlt_properties_set_double(properties, "l_scale_magenta", 100); mlt_properties_set_double(properties, "overlap", 0); filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/plus/filter_hslprimaries.yml000664 000000 000000 00000007432 15172202314 022404 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: hslprimaries title: HSL Primaries version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > Adjust Hue, Saturation and Lightness for each of the 6 primary/secondary colors. parameters: - identifier: h_shift_red title: Hue Shift Red type: float minimum: 0.0 maximum: 360.0 default: 0.0 unit: degrees mutable: yes animation: yes - identifier: s_scale_red title: Saturation Scale Red type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: l_scale_red title: Lightness Scale Red type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: h_shift_yellow title: Hue Shift Yellow type: float minimum: 0.0 maximum: 360.0 default: 0.0 unit: degrees mutable: yes animation: yes - identifier: s_scale_yellow title: Saturation Scale Yellow type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: l_scale_yellow title: Lightness Scale Yellow type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: h_shift_green title: Hue Shift Green type: float minimum: 0.0 maximum: 360.0 default: 0.0 unit: degrees mutable: yes animation: yes - identifier: s_scale_green title: Saturation Scale Green type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: l_scale_green title: Lightness Scale Green type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: h_shift_cyan title: Hue Shift Cyan type: float minimum: 0.0 maximum: 360.0 default: 0.0 unit: degrees mutable: yes animation: yes - identifier: s_scale_cyan title: Saturation Scale Cyan type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: l_scale_cyan title: Lightness Scale Cyan type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: h_shift_blue title: Hue Shift Blue type: float minimum: 0.0 maximum: 360.0 default: 0.0 unit: degrees mutable: yes animation: yes - identifier: s_scale_blue title: Saturation Scale Blue type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: l_scale_blue title: Lightness Scale Blue type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: h_shift_magenta title: Hue Shift Magenta type: float minimum: 0.0 maximum: 360.0 default: 0.0 unit: degrees mutable: yes animation: yes - identifier: s_scale_magenta title: Saturation Scale Magenta type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: l_scale_magenta title: Lightness Scale Magenta type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: overlap title: Overlap description: The amount of overlap or blending between primaries type: float minimum: 0.0 maximum: 100.0 default: 0.0 unit: percent mutable: yes animation: yes mlt-7.38.0/src/modules/plus/filter_hslrange.c000664 000000 000000 00000020554 15172202314 021126 0ustar00rootroot000000 000000 /* * filter_hslrange.c * Copyright (C) 2024-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hsl.h" #include #include #include #include typedef struct { mlt_filter filter; uint8_t *image; mlt_image_format format; int width; int height; float hue_center; float hue_range; float hue_range_max; float hue_range_min; float blend; float blend_range; float blend_threshold; float h_shift; float s_scale; float l_scale; } sliced_desc; static void adjust_pixel(float *r, float *g, float *b, sliced_desc *desc) { float h, s, l; rgbToHsl(*r, *g, *b, &h, &s, &l); if (s == 0) { // No color. Do not adjust. return; } float hue_distance = 1.0; if (desc->hue_range_max > desc->hue_range_min) { if (h < desc->hue_range_max && h > desc->hue_range_min) { hue_distance = fabs(desc->hue_center - h); } } else { // Handle the case were the range wraps around 0 if (h < desc->hue_range_max) { hue_distance = fabs(desc->hue_range - (desc->hue_range_max - h)); } else if (h > desc->hue_range_min) { hue_distance = fabs(desc->hue_range - (h - desc->hue_range_min)); } } if (hue_distance >= 1.0) { // This sample is outside the range. Do not adjust return; } float h_shift = desc->h_shift; float s_scale = desc->s_scale; float l_scale = desc->l_scale; if (hue_distance > desc->blend_threshold) { float blend_strength = 1.0 - (hue_distance - desc->blend_threshold) / desc->blend_range; h_shift = h_shift * blend_strength; s_scale = (desc->s_scale * blend_strength) + (1.0 - blend_strength); l_scale = (desc->l_scale * blend_strength) + (1.0 - blend_strength); } if (h_shift == 0.0 && s_scale == 1.0 && l_scale == 1.0) { // No adjustment for this pixel return; } // Apply the adjustment h = h + h_shift; h = fmod(h, 1.0); s = s * s_scale; s = s < 0.0 ? 0.0 : s > 1.0 ? 1.0 : s; l = l * l_scale; l = l < 0.0 ? 0.0 : l > 1.0 ? 1.0 : l; hslToRgb(h, s, l, r, g, b); } static int sliced_proc(int id, int index, int jobs, void *data) { (void) id; // unused sliced_desc *desc = ((sliced_desc *) data); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); int total = desc->width * slice_height; switch (desc->format) { case mlt_image_rgb: { uint8_t *sample = desc->image + slice_line_start * desc->width * 3; for (int i = 0; i < total; i++) { float r = sample[0] / 255.0; float g = sample[1] / 255.0; float b = sample[2] / 255.0; adjust_pixel(&r, &g, &b, desc); sample[0] = lrint(r * 255.0); sample[1] = lrint(g * 255.0); sample[2] = lrint(b * 255.0); sample += 3; } break; } case mlt_image_rgba: { uint8_t *sample = desc->image + slice_line_start * desc->width * 4; for (int i = 0; i < total; i++) { float r = sample[0] / 255.0; float g = sample[1] / 255.0; float b = sample[2] / 255.0; adjust_pixel(&r, &g, &b, desc); sample[0] = lrint(r * 255.0); sample[1] = lrint(g * 255.0); sample[2] = lrint(b * 255.0); sample += 4; } break; } case mlt_image_rgba64: { uint16_t *sample = (uint16_t *) desc->image + slice_line_start * desc->width * 4; for (int i = 0; i < total; i++) { float r = sample[0] / 65535.0; float g = sample[1] / 65535.0; float b = sample[2] / 65535.0; adjust_pixel(&r, &g, &b, desc); sample[0] = lrint(r * 65535.0); sample[1] = lrint(g * 65535.0); sample[2] = lrint(b * 65535.0); sample += 4; } break; } default: mlt_log_error(MLT_FILTER_SERVICE(desc->filter), "Invalid image format: %s\n", mlt_image_format_name(desc->format)); break; } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); sliced_desc desc; desc.hue_center = mlt_properties_anim_get_double(properties, "hue_center", position, length); desc.hue_range = mlt_properties_anim_get_double(properties, "hue_range", position, length); desc.blend = mlt_properties_anim_get_double(properties, "blend", position, length); desc.h_shift = mlt_properties_anim_get_double(properties, "h_shift", position, length); desc.s_scale = mlt_properties_anim_get_double(properties, "s_scale", position, length); desc.l_scale = mlt_properties_anim_get_double(properties, "l_scale", position, length); // Check if there is any processing to do if (desc.hue_range == 0.0 || (desc.h_shift == 0.0 && desc.s_scale == 100.0 && desc.l_scale == 100.0)) { return mlt_frame_get_image(frame, image, format, width, height, writable); } int error = 0; // Make sure the format is acceptable if (*format != mlt_image_rgb && *format != mlt_image_rgba && *format != mlt_image_rgba64) { *format = mlt_image_rgb; } // Get the image writable = 1; error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error) { // Scale the parameters down to [0-1] desc.hue_center /= 360.0; desc.hue_range /= 360.0; desc.blend /= 100.0; desc.h_shift /= 360.0; desc.s_scale /= 100.0; desc.l_scale /= 100.0; // Precompute some variables desc.hue_range /= 2; // Range from center, not whole range desc.hue_range_min = desc.hue_center - desc.hue_range; if (desc.hue_range_min < 0.0) desc.hue_range_min += 1.0; desc.hue_range_max = fmod(desc.hue_center + desc.hue_range, 1.0); desc.blend_range = desc.hue_range * desc.blend; desc.blend_threshold = desc.hue_range - desc.blend_range; desc.format = *format; desc.height = *height; desc.width = *width; desc.image = *image; mlt_slices_run_normal(0, sliced_proc, &desc); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_hslrange_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_double(properties, "hue_center", 180); mlt_properties_set_double(properties, "hue_range", 0); mlt_properties_set_double(properties, "blend", 0); mlt_properties_set_double(properties, "h_shift", 0); mlt_properties_set_double(properties, "s_scale", 100); mlt_properties_set_double(properties, "l_scale", 100); filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/plus/filter_hslrange.yml000664 000000 000000 00000003260 15172202314 021500 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: hslrange title: HSL Range version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > Adjust Hue, Saturation and Lightness for a range of hue values. The user can specify the range of the hue values to be adjusted. parameters: - identifier: hue_center title: Hue Center description: The center value for the hue range type: float minimum: 0.0 maximum: 360.0 default: 180.0 unit: degrees mutable: yes animation: yes - identifier: hue_range title: Hue Range description: The width of the hue range type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: blend title: Blend description: The amount to blend the edges of the hue range type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: h_shift title: Hue Shift description: The amount to shift the hue in the hue range type: float minimum: 0.0 maximum: 360.0 default: 0.0 unit: degrees mutable: yes animation: yes - identifier: s_scale title: Saturation Scale description: The amount to scale the saturation in the hue range type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes - identifier: l_scale title: Lightness Scale description: The amount to scale the lightness in the hue range type: float minimum: 0.0 maximum: 500.0 default: 100.0 unit: percent mutable: yes animation: yes mlt-7.38.0/src/modules/plus/filter_invert.c000664 000000 000000 00000007137 15172202314 020634 0ustar00rootroot000000 000000 /* * filter_invert.c -- invert filter * Copyright (C) 2003-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include typedef struct { uint8_t *image; int height; int width; int full_range; } slice_desc; static int do_slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *desc = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; int line_size = desc->width * 2; int min = desc->full_range ? 0 : 16; int max_luma = desc->full_range ? 255 : 235; int max_chroma = desc->full_range ? 255 : 240; int invert_luma = desc->full_range ? 255 : 251; int x, y; for (y = slice_line_start; y < slice_line_end; y++) { uint8_t *p = desc->image + y * line_size; for (x = 0; x < line_size; x += 2) { p[x] = CLAMP(invert_luma - p[x], min, max_luma); p[x + 1] = CLAMP(256 - p[x + 1], min, max_chroma); } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the image mlt_filter filter = mlt_frame_pop_service(frame); *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Only process if we have no error and a valid color space if (error == 0 && *format == mlt_image_yuv422) { slice_desc desc; desc.image = *image; desc.width = *width; desc.height = *height; desc.full_range = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_range"); mlt_slices_run_normal(0, do_slice_proc, &desc); int mask = mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "alpha"); if (mask) { int size = *width * *height; uint8_t *alpha = mlt_pool_alloc(size); memset(alpha, mask, size); mlt_frame_set_alpha(frame, alpha, size, mlt_pool_release); } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_invert_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/plus/filter_invert.yml000664 000000 000000 00000000545 15172202314 021207 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: invert title: Invert version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: alpha type: integer title: Alpha Channel description: A value to overwrite the alpha channel. minimum: 1 maximum: 255 mutable: yes mlt-7.38.0/src/modules/plus/filter_lift_gamma_gain.c000664 000000 000000 00000033060 15172202314 022415 0ustar00rootroot000000 000000 /* * filter_lift_gamma_gain.c * Copyright (C) 2014-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include typedef struct { uint8_t *rlut; uint8_t *glut; uint8_t *blut; uint16_t *rlut16; uint16_t *glut16; uint16_t *blut16; double rlift, glift, blift; double rgamma, ggamma, bgamma; double rgain, ggain, bgain; } private_data; typedef struct { mlt_filter filter; uint8_t *image; mlt_image_format format; int width; int height; uint8_t *rlut; uint8_t *glut; uint8_t *blut; uint16_t *rlut16; uint16_t *glut16; uint16_t *blut16; } sliced_desc; // Helper function to calculate lift-gamma-gain to a normalized [0.0, 1.0] value static inline void calc_lift_gamma_gain(double normalized, double *r, double *g, double *b, double rlift, double glift, double blift, double rgamma, double ggamma, double bgamma, double rgain, double ggain, double bgain) { // Convert to gamma 2.2 double gamma22 = pow(normalized, 1.0 / 2.2); *r = gamma22; *g = gamma22; *b = gamma22; // Apply lift *r += rlift * (1.0 - *r); *g += glift * (1.0 - *g); *b += blift * (1.0 - *b); // Clamp negative values *r = MAX(*r, 0.0); *g = MAX(*g, 0.0); *b = MAX(*b, 0.0); // Apply gamma *r = pow(*r, 2.2 / rgamma); *g = pow(*g, 2.2 / ggamma); *b = pow(*b, 2.2 / bgamma); // Apply gain *r *= pow(rgain, 1.0 / rgamma); *g *= pow(ggain, 1.0 / ggamma); *b *= pow(bgain, 1.0 / bgamma); // Clamp values *r = CLAMP(*r, 0.0, 1.0); *g = CLAMP(*g, 0.0, 1.0); *b = CLAMP(*b, 0.0, 1.0); } static int refresh_lut(mlt_filter filter, mlt_frame frame, mlt_image_format format) { private_data *self = (private_data *) filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); double rlift = mlt_properties_anim_get_double(properties, "lift_r", position, length); double glift = mlt_properties_anim_get_double(properties, "lift_g", position, length); double blift = mlt_properties_anim_get_double(properties, "lift_b", position, length); double rgamma = mlt_properties_anim_get_double(properties, "gamma_r", position, length); double ggamma = mlt_properties_anim_get_double(properties, "gamma_g", position, length); double bgamma = mlt_properties_anim_get_double(properties, "gamma_b", position, length); double rgain = mlt_properties_anim_get_double(properties, "gain_r", position, length); double ggain = mlt_properties_anim_get_double(properties, "gain_g", position, length); double bgain = mlt_properties_anim_get_double(properties, "gain_b", position, length); int params_changed = (self->rlift != rlift || self->glift != glift || self->blift != blift || self->rgamma != rgamma || self->ggamma != ggamma || self->bgamma != bgamma || self->rgain != rgain || self->ggain != ggain || self->bgain != bgain); if (params_changed) { // Free existing LUTs to force regeneration free(self->rlut); free(self->glut); free(self->blut); free(self->rlut16); free(self->glut16); free(self->blut16); self->rlut = NULL; self->glut = NULL; self->blut = NULL; self->rlut16 = NULL; self->glut16 = NULL; self->blut16 = NULL; } // Determine which LUT is needed for this format int need_lut8 = (format == mlt_image_rgb || format == mlt_image_rgba); int need_lut16 = (format == mlt_image_rgba64); // Allocate and generate 8-bit LUT if needed if (need_lut8 && !self->rlut) { self->rlut = malloc(256 * sizeof(uint8_t)); self->glut = malloc(256 * sizeof(uint8_t)); self->blut = malloc(256 * sizeof(uint8_t)); if (!self->rlut || !self->glut || !self->blut) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to allocate 8-bit LUTs\n"); free(self->rlut); free(self->glut); free(self->blut); self->rlut = NULL; self->glut = NULL; self->blut = NULL; return 1; } for (int i = 0; i < 256; i++) { double r, g, b; calc_lift_gamma_gain((double) i / 255.0, &r, &g, &b, rlift, glift, blift, rgamma, ggamma, bgamma, rgain, ggain, bgain); // Update 8-bit LUT self->rlut[i] = lrint(r * 255.0); self->glut[i] = lrint(g * 255.0); self->blut[i] = lrint(b * 255.0); } } // Allocate and generate 16-bit LUT if needed if (need_lut16 && !self->rlut16) { self->rlut16 = malloc(65536 * sizeof(uint16_t)); self->glut16 = malloc(65536 * sizeof(uint16_t)); self->blut16 = malloc(65536 * sizeof(uint16_t)); if (!self->rlut16 || !self->glut16 || !self->blut16) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to allocate 16-bit LUTs\n"); free(self->rlut16); free(self->glut16); free(self->blut16); self->rlut16 = NULL; self->glut16 = NULL; self->blut16 = NULL; return 1; } for (int i = 0; i < 65536; i++) { double r, g, b; calc_lift_gamma_gain((double) i / 65535.0, &r, &g, &b, rlift, glift, blift, rgamma, ggamma, bgamma, rgain, ggain, bgain); // Update 16-bit LUT self->rlut16[i] = lrint(r * 65535.0); self->glut16[i] = lrint(g * 65535.0); self->blut16[i] = lrint(b * 65535.0); } } // Store the values that created the LUT so that // changes can be detected. if (params_changed) { self->rlift = rlift; self->glift = glift; self->blift = blift; self->rgamma = rgamma; self->ggamma = ggamma; self->bgamma = bgamma; self->rgain = rgain; self->ggain = ggain; self->bgain = bgain; } return 0; } static int sliced_proc(int id, int index, int jobs, void *data) { (void) id; // unused sliced_desc *desc = ((sliced_desc *) data); int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); int total = desc->width * slice_height + 1; uint8_t *sample = desc->image + slice_line_start * mlt_image_format_size(desc->format, desc->width, 1, NULL); switch (desc->format) { case mlt_image_rgb: while (--total) { *sample = desc->rlut[*sample]; sample++; *sample = desc->glut[*sample]; sample++; *sample = desc->blut[*sample]; sample++; } break; case mlt_image_rgba: while (--total) { *sample = desc->rlut[*sample]; sample++; *sample = desc->glut[*sample]; sample++; *sample = desc->blut[*sample]; sample++; sample++; // Skip alpha } break; case mlt_image_rgba64: { int pixels = desc->width * slice_height; uint16_t *s = (uint16_t *) sample; for (int p = 0; p < pixels; p++) { *s = desc->rlut16[*s]; s++; *s = desc->glut16[*s]; s++; *s = desc->blut16[*s]; s++; s++; // Skip alpha } break; } default: mlt_log_error(MLT_FILTER_SERVICE(desc->filter), "Invalid image format: %s\n", mlt_image_format_name(desc->format)); break; } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); int error = 0; // Change the format if necessary switch (*format) { case mlt_image_yuv420p: case mlt_image_yuv422: *format = mlt_image_rgb; break; case mlt_image_yuv422p16: case mlt_image_yuv420p10: case mlt_image_yuv444p10: *format = mlt_image_rgba64; break; default: break; } // Get the image writable = 1; error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error && *image) { // Regenerate the LUT if necessary and copy pointers atomically mlt_service_lock(MLT_FILTER_SERVICE(filter)); error = refresh_lut(filter, frame, *format); if (!error) { private_data *self = (private_data *) filter->child; sliced_desc desc; desc.filter = filter; desc.image = *image; desc.format = *format; desc.width = *width; desc.height = *height; desc.rlut = self->rlut; desc.glut = self->glut; desc.blut = self->blut; desc.rlut16 = self->rlut16; desc.glut16 = self->glut16; desc.blut16 = self->blut16; // Apply the LUT with copied pointers mlt_slices_run_normal(0, sliced_proc, &desc); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { private_data *self = (private_data *) filter->child; if (self) { free(self->rlut); free(self->glut); free(self->blut); free(self->rlut16); free(self->glut16); free(self->blut16); free(self); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } mlt_filter filter_lift_gamma_gain_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *self = (private_data *) calloc(1, sizeof(private_data)); if (filter && self) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); self->rlift = self->glift = self->blift = 0.0; self->rgamma = self->ggamma = self->bgamma = 1.0; self->rgain = self->ggain = self->bgain = 1.0; // Initialize filter properties mlt_properties_set_double(properties, "lift_r", self->rlift); mlt_properties_set_double(properties, "lift_g", self->glift); mlt_properties_set_double(properties, "lift_b", self->blift); mlt_properties_set_double(properties, "gamma_r", self->rgamma); mlt_properties_set_double(properties, "gamma_g", self->ggamma); mlt_properties_set_double(properties, "gamma_b", self->bgamma); mlt_properties_set_double(properties, "gain_r", self->rgain); mlt_properties_set_double(properties, "gain_g", self->ggain); mlt_properties_set_double(properties, "gain_b", self->bgain); filter->close = filter_close; filter->process = filter_process; filter->child = self; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter lift_gamma_gain init failed\n"); mlt_filter_close(filter); filter = NULL; free(self); } return filter; } mlt-7.38.0/src/modules/plus/filter_lift_gamma_gain.yml000664 000000 000000 00000004230 15172202314 022771 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: lift_gamma_gain title: Lift, Gamma, and Gain version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > A simple lift/gamma/gain effect, used for color grading. notes: > Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, although all parameters affect the entire curve. Mathematically speaking, it is a bit unusual to look at gamma as a color, but it works pretty well in practice. The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). The lift is a case where we actually would _not_ want linear light; since black by definition becomes equal to the lift color, we want lift to be pretty close to black, but in linear light that means lift affects the rest of the curve relatively little. Thus, we actually convert to gamma 2.2 before lift, and then back again afterwards. (Gain and gamma are, up to constants, commutative with the de-gamma operation.) parameters: - identifier: lift_r title: Lift Red type: float minimum: 0.0 default: 0.0 mutable: yes animation: yes - identifier: lift_g title: Lift Green type: float minimum: 0.0 default: 0.0 mutable: yes animation: yes - identifier: lift_b title: Lift Blue type: float minimum: 0.0 default: 0.0 mutable: yes animation: yes - identifier: gamma_r title: Gamma Red type: float minimum: 0.0 default: 1.0 mutable: yes animation: yes - identifier: gamma_g title: Gamma Green type: float minimum: 0.0 default: 1.0 mutable: yes animation: yes - identifier: gamma_b title: Gamma Blue type: float minimum: 0.0 default: 1.0 mutable: yes animation: yes - identifier: gain_r title: Gain Red type: float minimum: 0.0 default: 1.0 mutable: yes animation: yes - identifier: gain_g title: Gain Green type: float minimum: 0.0 default: 1.0 mutable: yes animation: yes - identifier: gain_b title: Gain Blue type: float minimum: 0.0 default: 1.0 mutable: yes animation: yes mlt-7.38.0/src/modules/plus/filter_loudness.c000664 000000 000000 00000016310 15172202314 021152 0ustar00rootroot000000 000000 /* * filter_loudness.c -- normalize audio according to EBU R128 * Copyright (C) 20142-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #define MAX_RESULT_SIZE 512 typedef struct { ebur128_state *state; } analyze_data; typedef struct { analyze_data *analyze; mlt_position last_position; } private_data; static void destroy_analyze_data(mlt_filter filter) { private_data *private = (private_data *) filter->child; ebur128_destroy(&private->analyze->state); free(private->analyze); private->analyze = NULL; } static void init_analyze_data(mlt_filter filter, int channels, int samplerate) { private_data *private = (private_data *) filter->child; private->analyze = (analyze_data *) calloc(1, sizeof(analyze_data)); private->analyze->state = ebur128_init((unsigned int) channels, (unsigned long) samplerate, EBUR128_MODE_I | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK); private->last_position = 0; } static void analyze(mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { private_data *private = (private_data *) filter->child; mlt_position pos = mlt_filter_get_position(filter, frame); // If any frames are skipped, analysis data will be incomplete. if (private->analyze && pos != private->last_position + 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Analysis Failed: Bad frame sequence\n"); destroy_analyze_data(filter); } // Analyze Audio if (!private->analyze && pos == 0) { init_analyze_data(filter, *channels, *frequency); } if (private->analyze) { ebur128_add_frames_float(private->analyze->state, *buffer, *samples); if (pos + 1 == mlt_filter_get_length2(filter, frame)) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); double loudness = 0.0; double range = 0.0; double tmpPeak = 0.0; double peak = 0.0; int i = 0; char result[MAX_RESULT_SIZE]; ebur128_loudness_global(private->analyze->state, &loudness); ebur128_loudness_range(private->analyze->state, &range); for (i = 0; i < *channels; i++) { ebur128_sample_peak(private->analyze->state, i, &tmpPeak); if (tmpPeak > peak) { peak = tmpPeak; } } snprintf(result, MAX_RESULT_SIZE, "L: %lf\tR: %lf\tP %lf", loudness, range, peak); result[MAX_RESULT_SIZE - 1] = '\0'; mlt_log_info(MLT_FILTER_SERVICE(filter), "Stored results: %s\n", result); mlt_properties_set(properties, "results", result); destroy_analyze_data(filter); } private->last_position = pos; } } static void apply(mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); char *results = mlt_properties_get(properties, "results"); double in_loudness; double in_range; double in_peak; int scan_return = sscanf(results, "L: %lf\tR: %lf\tP %lf", &in_loudness, &in_range, &in_peak); if (scan_return != 3) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Unable to load results: %s\n", results); } else { double target_db = mlt_properties_get_double(properties, "program"); double delta_db = target_db - in_loudness; double coeff = delta_db > -90.0 ? pow(10.0, delta_db / 20.0) : 0.0; float *p = *buffer; int count = *samples * *channels; for (int i = 0; i < count; i++) { p[i] = p[i] * coeff; } } } /** Get the audio. */ static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = mlt_frame_pop_audio(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Get the producer's audio *format = mlt_audio_f32le; mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); char *results = mlt_properties_get(properties, "results"); if (buffer && buffer[0] && results && strcmp(results, "")) { apply(filter, frame, buffer, format, frequency, channels, samples); } else { analyze(filter, frame, buffer, format, frequency, channels, samples); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Destructor for the filter. */ static void filter_close(mlt_filter filter) { private_data *private = (private_data *) filter->child; if (private) { if (private->analyze) { destroy_analyze_data(filter); } free(private); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ mlt_filter filter_loudness_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *data = (private_data *) calloc(1, sizeof(private_data)); if (filter && data) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set(properties, "program", "-23.0"); data->analyze = NULL; filter->close = filter_close; filter->process = filter_process; filter->child = data; } else { if (filter) { mlt_filter_close(filter); filter = NULL; } if (data) { free(data); } } return filter; } mlt-7.38.0/src/modules/plus/filter_loudness.yml000664 000000 000000 00000002175 15172202314 021535 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: loudness title: Loudness version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: Correct audio loudness as recommended by EBU R128. notes: > This filter requires two passes. The first pass performs analysis and stores the result in the "results" property. The second pass applies the results to the audio in order to achieve the desired loudness over the range of the filter. parameters: - identifier: results title: Analysis Results type: string description: > Set after analysis. Used during application. Loudness information about the original audio. When results are not supplied, the filter computes the results and stores them in this property when the last frame has been processed. mutable: no - identifier: program title: Target Program Loudness type: float description: > Used during application. The target program loudness in LUFS (Loudness Units Full Scale). readonly: no mutable: yes default: -23.0 minimum: -50.0 maximum: -10.0 unit: LUFS mlt-7.38.0/src/modules/plus/filter_loudness_meter.c000664 000000 000000 00000025033 15172202314 022350 0ustar00rootroot000000 000000 /* * filter_loudness_meter.c -- measure audio loudness according to EBU R128 * Copyright (C) 2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include typedef struct { ebur128_state *r128; int reset; mlt_position prev_pos; } private_data; static void property_changed(mlt_service owner, mlt_filter filter, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); private_data *pdata = (private_data *) filter->child; if (name && pdata && (!strcmp(name, "reset") || !strcmp(name, "calc_program") || !strcmp(name, "calc_shortterm") || !strcmp(name, "calc_momentary") || !strcmp(name, "calc_range") || !strcmp(name, "calc_peak") || !strcmp(name, "calc_true_peak"))) { pdata->reset = 1; } } static void check_for_reset(mlt_filter filter, int channels, int frequency) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; if (pdata->reset) { if (pdata->r128) { ebur128_destroy(&pdata->r128); } pdata->r128 = 0; pdata->reset = 0; pdata->prev_pos = -1; mlt_events_block(properties, filter); mlt_properties_set(properties, "frames_processed", "0"); mlt_properties_set(properties, "program", "-100.0"); mlt_properties_set(properties, "shortterm", "-100.0"); mlt_properties_set(properties, "momentary", "-100.0"); mlt_properties_set(properties, "range", "-1.0"); mlt_properties_set_int(properties, "reset_count", mlt_properties_get_int(properties, "reset_count") + 1); mlt_properties_set_int(properties, "reset", 0); mlt_events_unblock(properties, filter); } if (!pdata->r128) { int mode = EBUR128_MODE_HISTOGRAM; if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_program")) { mode |= EBUR128_MODE_I; } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_shortterm")) { mode |= EBUR128_MODE_S; } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_momentary")) { mode |= EBUR128_MODE_M; } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_range")) { mode |= EBUR128_MODE_LRA; } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_peak")) { mode |= EBUR128_MODE_SAMPLE_PEAK; } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_true_peak")) { mode |= EBUR128_MODE_TRUE_PEAK; } pdata->r128 = ebur128_init(channels, frequency, mode); } } static void analyze_audio(mlt_filter filter, void *buffer, int samples) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; int result = -1; double loudness = 0.0; ebur128_add_frames_float(pdata->r128, buffer, samples); if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_program")) { result = ebur128_loudness_global(pdata->r128, &loudness); if (result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL) { mlt_properties_set_double(properties, "program", loudness); } } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_shortterm")) { result = ebur128_loudness_shortterm(pdata->r128, &loudness); if (result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL) { mlt_properties_set_double(properties, "shortterm", loudness); } } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_momentary")) { result = ebur128_loudness_momentary(pdata->r128, &loudness); if (result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL) { mlt_properties_set_double(properties, "momentary", loudness); } } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_range")) { double range = 0; result = ebur128_loudness_range(pdata->r128, &range); if (result == EBUR128_SUCCESS && range != HUGE_VAL && range != -HUGE_VAL) { mlt_properties_set_double(properties, "range", range); } } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_peak")) { double prev_peak = 0.0; double max_peak = 0.0; int c = 0; for (c = 0; c < pdata->r128->channels; c++) { double peak; result = ebur128_sample_peak(pdata->r128, c, &peak); if (result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak) { max_peak = peak; } result = ebur128_prev_sample_peak(pdata->r128, c, &peak); if (result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak) { prev_peak = peak; } } // cppcheck-suppress invalidFunctionArg mlt_properties_set_double(properties, "max_peak", 20 * log10(max_peak)); // cppcheck-suppress invalidFunctionArg mlt_properties_set_double(properties, "peak", 20 * log10(prev_peak)); } if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "calc_true_peak")) { double prev_peak = 0.0; double max_peak = 0.0; int c = 0; for (c = 0; c < pdata->r128->channels; c++) { double peak; result = ebur128_true_peak(pdata->r128, c, &peak); if (result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak) { max_peak = peak; } result = ebur128_prev_true_peak(pdata->r128, c, &peak); if (result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak) { prev_peak = peak; } } // cppcheck-suppress invalidFunctionArg mlt_properties_set_double(properties, "max_true_peak", 20 * log10(max_peak)); // cppcheck-suppress invalidFunctionArg mlt_properties_set_double(properties, "true_peak", 20 * log10(prev_peak)); } mlt_properties_set_position(properties, "frames_processed", mlt_properties_get_position(properties, "frames_processed") + 1); } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) filter->child; mlt_position pos = mlt_frame_get_position(frame); // Get the producer's audio *format = mlt_audio_f32le; mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); mlt_service_lock(MLT_FILTER_SERVICE(filter)); check_for_reset(filter, *channels, *frequency); if (pos != pdata->prev_pos) { // Only analyze the audio if the producer is not paused. analyze_audio(filter, *buffer, *samples); } pdata->prev_pos = pos; mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, filter_get_audio); return frame; } /** Destructor for the filter. */ static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { if (pdata->r128) { ebur128_destroy(&pdata->r128); } free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ mlt_filter filter_loudness_meter_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "calc_program", 1); mlt_properties_set_int(properties, "calc_shortterm", 1); mlt_properties_set_int(properties, "calc_momentary", 1); mlt_properties_set_int(properties, "calc_range", 1); mlt_properties_set_int(properties, "calc_peak", 1); mlt_properties_set_int(properties, "calc_true_peak", 1); mlt_properties_set(properties, "program", "-100.0"); mlt_properties_set(properties, "shortterm", "-100.0"); mlt_properties_set(properties, "momentary", "-100.0"); mlt_properties_set(properties, "range", "-1.0"); mlt_properties_set(properties, "peak", "-100.0"); mlt_properties_set(properties, "max_peak", "-100.0"); mlt_properties_set(properties, "true_peak", "-100.0"); mlt_properties_set(properties, "max_true_peak", "-100.0"); mlt_properties_set(properties, "reset", "1"); mlt_properties_set(properties, "reset_count", "0"); mlt_properties_set(properties, "frames_processed", "0"); pdata->r128 = 0; pdata->reset = 1; pdata->prev_pos = -1; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; mlt_events_listen(properties, filter, "property-changed", (mlt_listener) property_changed); } else { if (filter) { mlt_filter_close(filter); filter = NULL; } free(pdata); } return filter; } mlt-7.38.0/src/modules/plus/filter_loudness_meter.yml000664 000000 000000 00000006726 15172202314 022737 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: loudness_meter title: Loudness Meter version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio description: Measure audio loudness as recommended by EBU R128. parameters: - identifier: calc_program title: Calculate Program Loudness type: boolean description: > Whether to calculate program (integrated) loudness. readonly: no mutable: yes default: 1 - identifier: calc_shortterm title: Calculate Short-term Loudness type: boolean description: > Whether to calculate short-term loudness. readonly: no mutable: yes default: 1 - identifier: calc_momentary title: Calculate momentary Loudness type: boolean description: > Whether to calculate momentary loudness. readonly: no mutable: yes default: 1 - identifier: calc_range title: Calculate loudness range type: boolean description: > Whether to calculate loudness range. readonly: no mutable: yes default: 1 - identifier: calc_peak title: Calculate the peak sample level type: boolean description: > Whether to calculate the peak sample level. readonly: no mutable: yes default: 1 - identifier: calc_true_peak title: Calculate the true peak level type: boolean description: > Whether to calculate the true peak level. readonly: no mutable: yes default: 1 - identifier: program title: Program Loudness type: float description: The measured program loudness since the last reset. readonly: yes unit: LUFS - identifier: shortterm title: Short-term Loudness type: float description: The measured short-term loudness. readonly: yes unit: LUFS - identifier: momentary title: Momentary Loudness type: float description: The measured momentary loudness. readonly: yes unit: LUFS - identifier: range title: Loudness Range type: float description: The measured loudness range since the last reset. readonly: yes unit: LUFS - identifier: peak title: Peak type: float description: The measured peak sample value for the last frame that was processed. readonly: yes unit: dBFS - identifier: max_peak title: Max Peak type: float description: The measured peak sample value that has been received since the last reset. readonly: yes unit: dBFS - identifier: true_peak title: True Peak type: float description: The measured true peak value for the last frame that was processed. readonly: yes unit: dBTP - identifier: max_true_peak title: Max True Peak type: float description: The measured true peak value that has been received since the last reset. readonly: yes unit: dBTP - identifier: reset title: Reset type: boolean description: > Reset the measurement. Automatically resets back to 0 after the reset is complete. readonly: no mutable: yes default: 1 - identifier: reset_count title: Reset Count type: integer description: > The number of times the measurement has been reset. The filter is reset whenever reset is set to 1 or whenever a parameter is changed. readonly: yes - identifier: frames_processed title: Frames Processed type: integer description: > The number of frames that have been processed since the last reset. readonly: yes mlt-7.38.0/src/modules/plus/filter_lumakey.c000664 000000 000000 00000011226 15172202314 020766 0ustar00rootroot000000 000000 /* * filter_lumakey.c -- Luma Key filter for luma based image compositing * Copyright (C) 2014 Janne Liljeblad * Author: Janne Liljeblad * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include static int clamp(int value, int low, int high) { if (value < low) value = low; if (value > high) value = high; return value; } /** Builds a lookup table that maps luma values to opacity values. */ static void fill_opa_lut(int lgg_lut[], int prelevel, int postlevel, int slope_start, int slope_end) { int i; // Prelevel plateau for (i = 0; i < slope_start; i++) lgg_lut[i] = prelevel; // Value transition if (slope_start != slope_end) { double value = prelevel; double value_step = (double) (postlevel - prelevel) / (double) (slope_end - slope_start); for (i = slope_start; i < slope_end + 1; i++) { lgg_lut[i] = (int) value; value += value_step; } } // Postlevel plateau for (i = slope_end; i < 256; i++) lgg_lut[i] = postlevel; } /** Do image filtering. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the image mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); *format = mlt_image_rgba; int error = mlt_frame_get_image(frame, image, format, width, height, 0); // Only process if we have no error and a valid colour space if (error == 0) { // Get values and force accepted ranges int threshold = mlt_properties_anim_get_int(properties, "threshold", position, length); int slope = mlt_properties_anim_get_int(properties, "slope", position, length); int prelevel = mlt_properties_anim_get_int(properties, "prelevel", position, length); int postlevel = mlt_properties_anim_get_int(properties, "postlevel", position, length); threshold = clamp(threshold, 0, 255); slope = clamp(slope, 0, 128); prelevel = clamp(prelevel, 0, 255); postlevel = clamp(postlevel, 0, 255); int slope_start = clamp(threshold - slope, 0, 255); int slope_end = clamp(threshold + slope, 0, 255); // Build lut int opa_lut[256]; fill_opa_lut(opa_lut, prelevel, postlevel, slope_start, slope_end); // Values for calculating visual luma from RGB double R = 0.3; double G = 0.59; double B = 0.11; // Filter int i = *width * *height + 1; uint8_t *sample = *image; uint8_t r, g, b; while (--i) { r = *sample++; g = *sample++; b = *sample++; *sample++ = opa_lut[(int) (R * r + g * G + b * B)]; } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_lumakey_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "threshold", "128"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "slope", "0"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "prelevel", "0"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "postlevel", "255"); } return filter; } mlt-7.38.0/src/modules/plus/filter_lumakey.yml000664 000000 000000 00000003130 15172202314 021340 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: lumakey title: Lumakey version: 1 copyright: Janne Liljeblad creator: Janne Liljeblad license: LGPLv2.1 language: en tags: - Video description: > This filter modifies image's alpha channel as a function of its luma value. This is used together with a compositor to combine two images so that bright or dark areas of source image are overwritten on top of the destination image. parameters: - identifier: threshold title: Threshold type: integer minimum: 0 maximum: 255 default: 128 mutable: yes animation: yes description: > Luma value that defines the center point of transition from prelevel to postlevel opacity value. - identifier: slope title: Slope type: integer minimum: 0 maximum: 128 default: 0 mutable: yes animation: yes description: > This defines the width of the transition from prelevel to postlevel luma value. Start point of transition in opacity value is threshold - slope and end point is thresholt + slope, values are forced in range 0 - 255. - identifier: prelevel title: Pre-Threshold Luma Level type: integer minimum: 0 maximum: 255 default: 0 mutable: yes animation: yes description: > Opacity value before the transition in opacity value begins. - identifier: postlevel title: Post-Threshold Luma Level type: integer minimum: 0 maximum: 255 default: 255 mutable: yes animation: yes description: > Opacity value after the transition in opacity value ends. mlt-7.38.0/src/modules/plus/filter_rgblut.c000664 000000 000000 00000007151 15172202314 020620 0ustar00rootroot000000 000000 /* * filter_rgblut.c -- generic RGB look-up table filter with string interface * Copyright (C) 2014 Janne Liljeblad * Author: Janne Liljeblad * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include /** Fill channel lut with integers parsed from property string. */ static void fill_channel_lut(int lut[], char *channel_table_str) { mlt_tokeniser tokeniser = mlt_tokeniser_init(); mlt_tokeniser_parse_new(tokeniser, channel_table_str, ";"); // Only create lut from string if tokens count exactly right if (tokeniser->count == 256) { // Fill lut with token values int i; int val; for (i = 0; i < 256; i++) { val = atoi(tokeniser->tokens[i]); lut[i] = val; } } else { // Fill lut with linear no-op table int i; for (i = 0; i < 256; i++) { lut[i] = i; } } mlt_tokeniser_close(tokeniser); } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the image mlt_filter filter = mlt_frame_pop_service(frame); *format = mlt_image_rgb; int error = mlt_frame_get_image(frame, image, format, width, height, 0); // Only process if we have no error and a valid colour space if (error == 0) { // Create lut tables from properties for each RGB channel char *r_str = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "R_table"); int r_lut[256]; fill_channel_lut(r_lut, r_str); char *g_str = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "G_table"); int g_lut[256]; fill_channel_lut(g_lut, g_str); char *b_str = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "B_table"); int b_lut[256]; fill_channel_lut(b_lut, b_str); // Apply look-up tables into image int i = *width * *height + 1; uint8_t *p = *image; uint8_t *r = *image; while (--i) { *p++ = r_lut[*r++]; *p++ = g_lut[*r++]; *p++ = b_lut[*r++]; } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_rgblut_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/plus/filter_rgblut.yml000664 000000 000000 00000003114 15172202314 021172 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: rgblut title: RGBLUT version: 1 copyright: Janne Liljeblad creator: Janne Liljeblad license: LGPLv2.1 language: en tags: - Video description: Converts input strings with exactly 256 semicolon separated integer values in range 0 - 255 to look-up tables that are then used to modify R, G, B values. This creates a generic string interface for color correction. parameters: - identifier: R_table title: Red channel look-up table type: string description: > Value is tokenised using semicolon separator into exactly 256 integer values in range 0 - 255 and a look-up table for red channel values is created and applied to image. If tokenising of value fails a linear table that returns input values unchanged is used instead. - identifier: G_table title: Green channel look-up table type: string description: > Value is tokenised using semicolon separator into exactly 256 integer values in range 0 - 255 and a look-up table for green channel values is created and applied to image. If tokenising of value fails a linear table that returns input values unchanged is used instead. - identifier: B_table title: Blue channel look-up table type: string description: > Value is tokenised using semicolon separator into exactly 256 integer values in range 0 - 255 and a look-up table for green channel values is created and applied to image. If tokenising of value fails a linear table that returns input values unchanged is used instead. mlt-7.38.0/src/modules/plus/filter_sepia.c000664 000000 000000 00000007050 15172202314 020420 0ustar00rootroot000000 000000 /* * filter_sepia.c -- sepia filter * Copyright (C) 2003-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include typedef struct { uint8_t *image; int height; int width; uint8_t u; uint8_t v; } slice_desc; static int do_slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *desc = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); int slice_line_end = slice_line_start + slice_height; int line_size = desc->width * 2; int uneven = desc->width % 2; int x, y; for (y = slice_line_start; y < slice_line_end; y++) { uint8_t *p = desc->image + y * line_size; for (x = 0; x < line_size; x += 4) { p[x + 1] = desc->u; p[x + 3] = desc->v; } if (uneven) { p[line_size - 1] = desc->u; } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the filter mlt_filter filter = mlt_frame_pop_service(frame); // Get the image *format = mlt_image_yuv422; int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Only process if we have no error and a valid colour space if (error == 0 && *image) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); slice_desc desc; desc.image = *image; desc.height = *height; desc.width = *width; desc.u = mlt_properties_anim_get_int(properties, "u", position, length); desc.v = mlt_properties_anim_get_int(properties, "v", position, length); mlt_slices_run_normal(0, do_slice_proc, &desc); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_sepia_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "u", "75"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "v", "150"); } return filter; } mlt-7.38.0/src/modules/plus/filter_sepia.yml000664 000000 000000 00000001062 15172202314 020774 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: sepia title: Sepia version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Apply a color tint. Default values give a sepia tone like an old photograph. parameters: - identifier: u type: integer title: Yellow-Blue minimum: 0 maximum: 255 default: 75 mutable: yes animation: yes - identifier: v type: integer title: Cyan-Red minimum: 0 maximum: 255 default: 150 mutable: yes animation: yes mlt-7.38.0/src/modules/plus/filter_shape.c000664 000000 000000 00000102305 15172202314 020416 0ustar00rootroot000000 000000 /* * filter_shape.c -- Arbitrary alpha channel shaping * Copyright (C) 2008-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include typedef struct { uint8_t *alpha; uint8_t *mask; int width; int height; int bpp; // bytes per pixel (1 for separate alpha, 4 for rgba) double softness; double mix; int invert; int invert_mask; double offset; double divisor; } slice_desc; typedef struct { uint16_t *alpha; uint16_t *mask; int width; int height; double softness; double mix; int invert; int invert_mask; double offset; double divisor; } slice_desc16; static inline double smoothstep(const double e1, const double e2, const double a) { if (a < e1) return 0.0; if (a >= e2) return 1.0; double v = (a - e1) / (e2 - e1); return (v * v * (3 - 2 * v)); } static int slice_alpha_add(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc *desc = ((slice_desc *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint8_t *p = desc->alpha + slice_line_start * desc->width * desc->bpp; const uint8_t *q = desc->mask + slice_line_start * desc->width * desc->bpp; const int channel = desc->bpp == 4 ? 3 : 0; for (int i = 0; i < size; ++i) { uint32_t a = (uint32_t) (q[i * desc->bpp + channel] ^ desc->invert_mask); p[i * desc->bpp + channel] = ((uint8_t) MIN((uint32_t) p[i * desc->bpp + channel] + a, 255)) ^ desc->invert; } return 0; } static int slice_alpha_maximum(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc *desc = ((slice_desc *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint8_t *p = desc->alpha + slice_line_start * desc->width * desc->bpp; const uint8_t *q = desc->mask + slice_line_start * desc->width * desc->bpp; const int channel = desc->bpp == 4 ? 3 : 0; for (int i = 0; i < size; ++i) { p[i * desc->bpp + channel] = MAX(p[i * desc->bpp + channel], (q[i * desc->bpp + channel] ^ desc->invert_mask)) ^ desc->invert; } return 0; } static int slice_alpha_minimum(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc *desc = ((slice_desc *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint8_t *p = desc->alpha + slice_line_start * desc->width * desc->bpp; const uint8_t *q = desc->mask + slice_line_start * desc->width * desc->bpp; const int channel = desc->bpp == 4 ? 3 : 0; for (int i = 0; i < size; ++i) { p[i * desc->bpp + channel] = MIN(p[i * desc->bpp + channel], (q[i * desc->bpp + channel] ^ desc->invert_mask)) ^ desc->invert; } return 0; } static int slice_alpha_overwrite(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc *desc = ((slice_desc *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint8_t *p = desc->alpha + slice_line_start * desc->width * desc->bpp; const uint8_t *q = desc->mask + slice_line_start * desc->width * desc->bpp; const int channel = desc->bpp == 4 ? 3 : 0; for (int i = 0; i < size; ++i) { p[i * desc->bpp + channel] = (q[i * desc->bpp + channel] ^ desc->invert_mask) ^ desc->invert; } return 0; } static int slice_alpha_subtract(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc *desc = ((slice_desc *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint8_t *p = desc->alpha + slice_line_start * desc->width * desc->bpp; const uint8_t *q = desc->mask + slice_line_start * desc->width * desc->bpp; for (int i = 0; i < size; ++i) { uint8_t a = q[i * desc->bpp + (desc->bpp == 4 ? 3 : 0)] ^ desc->invert_mask; p[i * desc->bpp] = (p[i * desc->bpp] > a ? p[i * desc->bpp] - a : 0) ^ desc->invert; } return 0; } static int slice_alpha_proc(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc *desc = ((const slice_desc *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint8_t *p = desc->alpha + slice_line_start * desc->width * desc->bpp; const uint8_t *q = desc->mask + slice_line_start * desc->width * desc->bpp; const int channel = desc->bpp == 4 ? 3 : 0; for (int i = 0; i < size; ++i) { double a = (double) (q[i * desc->bpp + channel] ^ desc->invert_mask) / desc->divisor; a = 1.0 - smoothstep(a, a + desc->softness, desc->mix); p[i * desc->bpp + channel] = (uint8_t) (p[i * desc->bpp + channel] * a) ^ desc->invert; } return 0; } static int slice_luma_proc(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc *desc = ((const slice_desc *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint8_t *p = desc->alpha + slice_line_start * desc->width; const uint8_t *q = desc->mask + slice_line_start * desc->width * 2; for (int i = 0; i < size; ++i) { double a = ((double) (q[i * 2] ^ desc->invert_mask) - desc->offset) / desc->divisor; a = smoothstep(a, a + desc->softness, desc->mix); p[i] = (uint8_t) (p[i] * a) ^ desc->invert; } return 0; } static int slice_alpha16_add(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc16 *desc = ((const slice_desc16 *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint16_t *p = desc->alpha + slice_line_start * desc->width * 4; const uint16_t *q = desc->mask + slice_line_start * desc->width * 4; const int invert_mask16 = desc->invert_mask ? 65535 : 0; const int invert16 = desc->invert ? 65535 : 0; for (int i = 0; i < size; ++i) { uint32_t a = (uint32_t) (q[i * 4 + 3] ^ invert_mask16); p[i * 4 + 3] = ((uint16_t) MIN((uint32_t) p[i * 4 + 3] + a, 65535)) ^ invert16; } return 0; } static int slice_alpha16_maximum(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc16 *desc = ((const slice_desc16 *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint16_t *p = desc->alpha + slice_line_start * desc->width * 4; const uint16_t *q = desc->mask + slice_line_start * desc->width * 4; const int invert_mask16 = desc->invert_mask ? 65535 : 0; const int invert16 = desc->invert ? 65535 : 0; for (int i = 0; i < size; ++i) { p[i * 4 + 3] = MAX(p[i * 4 + 3], (q[i * 4 + 3] ^ invert_mask16)) ^ invert16; } return 0; } static int slice_alpha16_minimum(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc16 *desc = ((const slice_desc16 *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint16_t *p = desc->alpha + slice_line_start * desc->width * 4; const uint16_t *q = desc->mask + slice_line_start * desc->width * 4; const int invert_mask16 = desc->invert_mask ? 65535 : 0; const int invert16 = desc->invert ? 65535 : 0; for (int i = 0; i < size; ++i) { p[i * 4 + 3] = MIN(p[i * 4 + 3], (q[i * 4 + 3] ^ invert_mask16)) ^ invert16; } return 0; } static int slice_alpha16_overwrite(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc16 *desc = ((const slice_desc16 *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint16_t *p = desc->alpha + slice_line_start * desc->width * 4; const uint16_t *q = desc->mask + slice_line_start * desc->width * 4; const int invert_mask16 = desc->invert_mask ? 65535 : 0; const int invert16 = desc->invert ? 65535 : 0; for (int i = 0; i < size; ++i) { p[i * 4 + 3] = (q[i * 4 + 3] ^ invert_mask16) ^ invert16; } return 0; } static int slice_alpha16_subtract(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc16 *desc = ((const slice_desc16 *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint16_t *p = desc->alpha + slice_line_start * desc->width * 4; const uint16_t *q = desc->mask + slice_line_start * desc->width * 4; const int invert_mask16 = desc->invert_mask ? 65535 : 0; const int invert16 = desc->invert ? 65535 : 0; for (int i = 0; i < size; ++i) { uint16_t a = q[i * 4 + 3] ^ invert_mask16; p[i * 4 + 3] = (p[i * 4 + 3] > a ? p[i * 4 + 3] - a : 0) ^ invert16; } return 0; } static int slice_alpha16_proc(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc16 *desc = ((const slice_desc16 *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint16_t *p = desc->alpha + slice_line_start * desc->width * 4; const uint16_t *q = desc->mask + slice_line_start * desc->width * 4; const int invert_mask16 = desc->invert_mask ? 65535 : 0; const int invert16 = desc->invert ? 65535 : 0; for (int i = 0; i < size; ++i) { double a = (double) (q[i * 4 + 3] ^ invert_mask16) / desc->divisor; a = 1.0 - smoothstep(a, a + desc->softness, desc->mix); p[i * 4 + 3] = (uint16_t) (p[i * 4 + 3] * a) ^ invert16; } return 0; } static int slice_luma_rgba_proc(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc *desc = ((const slice_desc *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint8_t *p = desc->alpha + slice_line_start * desc->width * 4; const uint8_t *q = desc->mask + slice_line_start * desc->width * 4; for (int i = 0; i < size; ++i) { // Calculate luma from sRGB double luma = 0.2126 * q[i * 4] + 0.7152 * q[i * 4 + 1] + 0.0722 * q[i * 4 + 2]; double a = (luma - desc->offset) / desc->divisor; if (desc->invert_mask) a = 1.0 - a; a = smoothstep(a, a + desc->softness, desc->mix); p[i * 4 + 3] = (uint8_t) (p[i * 4 + 3] * a) ^ desc->invert; } return 0; } static int slice_luma_rgba64_proc(int id, int index, int jobs, void *data) { (void) id; // unused const slice_desc16 *desc = ((const slice_desc16 *) data); int slice_line_start; const int slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); const int size = desc->width * slice_height; uint16_t *p = desc->alpha + slice_line_start * desc->width * 4; const uint16_t *q = (const uint16_t *) desc->mask + slice_line_start * desc->width * 4; for (int i = 0; i < size; ++i) { // Calculate luma from sRGB using 16-bit values double luma = 0.2126 * q[i * 4] + 0.7152 * q[i * 4 + 1] + 0.0722 * q[i * 4 + 2]; double a = (luma - desc->offset) / desc->divisor; if (desc->invert_mask) a = 1.0 - a; a = smoothstep(a, a + desc->softness, desc->mix); p[i * 4 + 3] = (uint16_t) (p[i * 4 + 3] * a) ^ (desc->invert ? 65535 : 0); } return 0; } /** Apply alpha operation based on the operation type */ static void apply_alpha_operation(mlt_filter filter, slice_desc *desc, slice_desc16 *desc16, int is_16bit) { const char *op = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "alpha_operation"); if (is_16bit) { if (op && op[0] != '\0') { if (op[0] == 'a') mlt_slices_run_normal(0, slice_alpha16_add, desc16); else if (op[0] == 's') mlt_slices_run_normal(0, slice_alpha16_subtract, desc16); else if (!strncmp("ma", op, 2)) mlt_slices_run_normal(0, slice_alpha16_maximum, desc16); else if (!strncmp("mi", op, 2)) mlt_slices_run_normal(0, slice_alpha16_minimum, desc16); else mlt_slices_run_normal(0, slice_alpha16_overwrite, desc16); } else { mlt_slices_run_normal(0, slice_alpha16_overwrite, desc16); } } else { if (op && op[0] != '\0') { if (op[0] == 'a') mlt_slices_run_normal(0, slice_alpha_add, desc); else if (op[0] == 's') mlt_slices_run_normal(0, slice_alpha_subtract, desc); else if (!strncmp("ma", op, 2)) mlt_slices_run_normal(0, slice_alpha_maximum, desc); else if (!strncmp("mi", op, 2)) mlt_slices_run_normal(0, slice_alpha_minimum, desc); else mlt_slices_run_normal(0, slice_alpha_overwrite, desc); } else { mlt_slices_run_normal(0, slice_alpha_overwrite, desc); } } } /** Handle alpha channel processing for RGBA64 format */ static void process_alpha_rgba64(mlt_filter filter, uint8_t **image, uint8_t *mask_img, int width, int height, double softness, double mix, int invert, int invert_mask, int use_mix) { slice_desc16 desc = {.alpha = (uint16_t *) *image, .mask = (uint16_t *) mask_img, .width = width, .height = height, .softness = softness, .mix = mix, .invert = invert, .invert_mask = invert_mask, .offset = 0.0, .divisor = 65535.0}; if (use_mix) { mlt_slices_run_normal(0, slice_alpha16_proc, &desc); } else { apply_alpha_operation(filter, NULL, &desc, 1); } } /** Handle alpha channel processing for RGBA format */ static void process_alpha_rgba(mlt_filter filter, uint8_t **image, uint8_t *mask_img, int width, int height, double softness, double mix, int invert, int invert_mask, int use_mix) { slice_desc desc = {.alpha = *image, .mask = mask_img, .width = width, .height = height, .bpp = 4, .softness = softness, .mix = mix, .invert = invert, .invert_mask = invert_mask, .offset = 0.0, .divisor = 255.0}; if (use_mix) { mlt_slices_run_normal(0, slice_alpha_proc, &desc); } else { apply_alpha_operation(filter, &desc, NULL, 0); } } /** Handle YUV422 with separate alpha channel */ static void process_alpha_yuv422(mlt_filter filter, mlt_frame frame, mlt_frame mask, int width, int height, double softness, double mix, int invert, int invert_mask, int use_mix) { uint8_t *p = mlt_frame_get_alpha(frame); if (!p) { int alphasize = width * height; p = mlt_pool_alloc(alphasize); memset(p, 255, alphasize); mlt_frame_set_alpha(frame, p, alphasize, mlt_pool_release); } uint8_t *q = mlt_frame_get_alpha(mask); if (!q) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "failed to get alpha channel from mask: %s\n", mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "resource")); int alphasize = width * height; q = mlt_pool_alloc(alphasize); memset(q, 255, alphasize); mlt_frame_set_alpha(mask, q, alphasize, mlt_pool_release); } slice_desc desc = {.alpha = p, .mask = q, .width = width, .height = height, .bpp = 1, .softness = softness, .mix = mix, .invert = invert, .invert_mask = invert_mask, .offset = 0.0, .divisor = 255.0}; if (use_mix) { mlt_slices_run_normal(0, slice_alpha_proc, &desc); } else { apply_alpha_operation(filter, &desc, NULL, 0); } } /** Direct alpha copy without threshold (use_mix = 0, use_luminance = 0) */ static void process_alpha_copy(mlt_frame frame, uint8_t *image, uint8_t *mask_img, mlt_image_format format, int width, int height, int invert_mask) { int size = width * height; if (format == mlt_image_rgba64) { uint16_t *img16 = (uint16_t *) image; uint16_t *q16 = (uint16_t *) mask_img; int invert_mask16 = invert_mask ? 65535 : 0; for (int i = 0; i < size; i++) { double luma = 0.2126 * q16[i * 4] + 0.7152 * q16[i * 4 + 1] + 0.0722 * q16[i * 4 + 2]; img16[i * 4 + 3] = (uint16_t) luma ^ invert_mask16; } } else if (format == mlt_image_rgba) { uint8_t *q = mask_img; for (int i = 0; i < size; i++) { double luma = 0.2126 * q[i * 4] + 0.7152 * q[i * 4 + 1] + 0.0722 * q[i * 4 + 2]; image[i * 4 + 3] = (uint8_t) luma ^ invert_mask; } } else { uint8_t *p = mlt_frame_get_alpha(frame); uint8_t *q = mask_img; if (!p) { int alphasize = width * height; p = mlt_pool_alloc(alphasize); memset(p, 255, alphasize); mlt_frame_set_alpha(frame, p, alphasize, mlt_pool_release); } for (int i = 0; i < size; i++) { p[i] = q[i * 2] ^ invert_mask; } } } /** Derive alpha from luminance of mask image */ static void process_luminance(mlt_frame frame, uint8_t *image, uint8_t *mask_img, mlt_image_format format, int width, int height, double softness, double mix, int invert, int invert_mask) { if (format == mlt_image_rgba) { slice_desc desc = {.alpha = image, .mask = mask_img, .width = width, .height = height, .bpp = 4, .softness = softness * (1.0 - mix), .mix = mix, .invert = invert, .invert_mask = invert_mask, .offset = 0.0, .divisor = 255.0}; mlt_slices_run_normal(0, slice_luma_rgba_proc, &desc); } else if (format == mlt_image_rgba64) { slice_desc16 desc = {.alpha = (uint16_t *) image, .mask = (uint16_t *) mask_img, .width = width, .height = height, .softness = softness * (1.0 - mix), .mix = mix, .invert = invert, .invert_mask = invert_mask, .offset = 0.0, .divisor = 65535.0}; mlt_slices_run_normal(0, slice_luma_rgba64_proc, &desc); } else { int full_range = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_range"); uint8_t *p = mlt_frame_get_alpha(frame); if (!p) { int alphasize = width * height; p = mlt_pool_alloc(alphasize); memset(p, 255, alphasize); mlt_frame_set_alpha(frame, p, alphasize, mlt_pool_release); } slice_desc desc = {.alpha = p, .mask = mask_img, .width = width, .height = height, .bpp = 1, .softness = softness * (1.0 - mix), .mix = mix, .invert = invert, .invert_mask = invert_mask, .offset = full_range ? 0.0 : 16.0, .divisor = full_range ? 255.0 : 235.0}; mlt_slices_run_normal(0, slice_luma_proc, &desc); } } /** Get the images and apply the luminance of the mask to the alpha of the frame. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Fetch the data from the stack (mix, mask, filter) double mix = mlt_deque_pop_back_double(MLT_FRAME_IMAGE_STACK(frame)); mlt_frame mask = mlt_frame_pop_service(frame); mlt_filter filter = mlt_frame_pop_service(frame); // Obtain the constants double softness = mlt_properties_get_double(MLT_FILTER_PROPERTIES(filter), "softness"); int use_luminance = mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "use_luminance"); int use_mix = mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "use_mix"); int invert = mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "invert") * 255; int invert_mask = mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "invert_mask") * 255; if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "reverse")) { mix = 1.0 - mix; invert = !mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "invert") * 255; } // Render the frame if (*format != mlt_image_rgba && *format != mlt_image_rgba64) *format = mlt_image_yuv422; if (*format == mlt_image_yuv422) *width -= *width % 2; if (mlt_frame_get_image(frame, image, format, width, height, 1) == 0 && (!use_luminance || !use_mix || (int) mix != 1 || invert == 255 || invert_mask == 255)) { // Obtain a scaled/distorted mask to match uint8_t *mask_img = NULL; mlt_image_format mask_fmt = *format; mlt_properties_set_int(MLT_FRAME_PROPERTIES(mask), "distort", 1); mlt_properties_copy(MLT_FRAME_PROPERTIES(mask), MLT_FRAME_PROPERTIES(frame), "consumer."); if (mlt_frame_get_image(mask, &mask_img, &mask_fmt, width, height, 0) == 0) { mlt_log_debug(MLT_FILTER_SERVICE(filter), "applying mask: %s ", mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "resource")); // Route to appropriate processing function based on format and mode if (!use_luminance && mask_fmt == mlt_image_rgba64) { process_alpha_rgba64(filter, image, mask_img, *width, *height, softness, mix, invert, invert_mask, use_mix); } else if (!use_luminance && mask_fmt == mlt_image_rgba) { process_alpha_rgba(filter, image, mask_img, *width, *height, softness, mix, invert, invert_mask, use_mix); } else if (!use_luminance) { process_alpha_yuv422(filter, frame, mask, *width, *height, softness, mix, invert, invert_mask, use_mix); } else if (!use_mix) { process_alpha_copy(frame, *image, mask_img, *format, *width, *height, invert_mask); } else if ((int) mix != 1 || invert == 255 || invert_mask == 255) { mlt_log_debug(MLT_FILTER_SERVICE(filter), "applying luminance to alpha channel format: %s\n", mlt_image_format_name(mask_fmt)); process_luminance(frame, *image, mask_img, *format, *width, *height, softness, mix, invert, invert_mask); } } else { mlt_log_warning(MLT_FILTER_SERVICE(filter), "failed to get image from mask: %s\n", mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "resource")); } } else { mlt_log_warning(MLT_FILTER_SERVICE(filter), "failed to get image from frame\n"); } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Obtain the shape instance char *resource = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "resource"); if (!resource) return frame; char *last_resource = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "_resource"); mlt_producer producer = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "instance", NULL); // Calculate the position and length int position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); // If we haven't created the instance or it's changed if (producer == NULL || !last_resource || strcmp(resource, last_resource)) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); char temp[PATH_MAX]; // Store the last resource now mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "_resource", resource); // This is a hack - the idea is that we can indirectly reference the // luma modules pgm or png images by a short cut like %luma01.pgm - we then replace // the % with the full path to the image and use it if it exists, if not, check for // the file ending in a .png, and failing that, default to a fade in if (strchr(resource, '%')) { FILE *test; snprintf(temp, sizeof(temp), "%s/lumas/%s/%s", mlt_environment("MLT_DATA"), mlt_profile_lumas_dir(profile), strchr(resource, '%') + 1); test = mlt_fopen(temp, "r"); if (test == NULL) { strcat(temp, ".png"); test = mlt_fopen(temp, "r"); } if (test) { fclose(test); resource = temp; } } producer = mlt_factory_producer(profile, NULL, resource); if (producer != NULL) mlt_properties_set(MLT_PRODUCER_PROPERTIES(producer), "eof", "loop"); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "instance", producer, 0, (mlt_destructor) mlt_producer_close, NULL); } // We may still not have a producer in which case, we do nothing if (producer != NULL) { mlt_frame mask = NULL; double alpha_mix = mlt_properties_anim_get_double(MLT_FILTER_PROPERTIES(filter), "mix", position, length); mlt_properties_pass(MLT_PRODUCER_PROPERTIES(producer), MLT_FILTER_PROPERTIES(filter), "producer."); mlt_properties_clear(MLT_FILTER_PROPERTIES(filter), "producer.refresh"); mlt_producer_seek(producer, position); if (mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &mask, 0) == 0) { char name[64]; snprintf(name, sizeof(name), "shape %s", mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "_unique_id")); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), name, mask, 0, (mlt_destructor) mlt_frame_close, NULL); mlt_frame_push_service(frame, filter); mlt_frame_push_service(frame, mask); mlt_deque_push_back_double(MLT_FRAME_IMAGE_STACK(frame), alpha_mix / 100.0); mlt_frame_push_get_image(frame, filter_get_image); if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "audio_match")) { mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "meta.mixdown", 1); mlt_properties_set_double(MLT_FRAME_PROPERTIES(frame), "meta.volume", alpha_mix / 100.0); } mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "always_scale", 1); } } return frame; } /** Constructor for the filter. */ mlt_filter filter_shape_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "resource", arg); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "mix", "100"); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "use_mix", 1); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "audio_match", 1); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "invert", 0); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "softness", 0.1); filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/plus/filter_shape.yml000664 000000 000000 00000005373 15172202314 021004 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: shape title: Shape Alpha version: 7 creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: resource argument: yes title: File type: string description: > The name of a file or MLT producer URL. To use a luma wipe from the lumas module, put % in front of the base name of the luma file e.g. %luma16.pgm required: true mutable: yes - identifier: mix title: Threshold type: float description: > Convert alpha or luma values below this level as opaque and above this level as transparent. This is mostly useful for luma wipe images. unit: '%' minimum: 0 maximum: 100 default: 100 mutable: yes animation: yes - identifier: softness title: Softness type: float mutable: yes description: > When using mix (threshold) how soft to make the edge around the threshold. 0.0 = no softness, 1.0 = too soft minimum: 0 maximum: 1 default: 0.1 mutable: yes - identifier: invert title: Invert type: boolean description: Invert the resulting alpha channel. default: no mutable: yes - identifier: invert_mask title: Invert Mask description: Use the inverse of the resource's alpha channel or luma value. type: boolean default: no mutable: yes - identifier: reverse title: Reverse type: boolean description: > Use the complement of the mix level. This also inverts the output alpha, which is probably not what you want. See also invert_mask. default: no mutable: yes - identifier: use_luminance title: Use Luma type: boolean description: Use the image luma instead of the alpha channel. default: no mutable: yes - identifier: use_mix title: Use Threshold type: boolean description: > Whether to apply a threshold filter to the luma or alpha or not. If not, luma or alpha value of the resource (File) is copied to the alpha channel. default: yes mutable: yes - identifier: audio_match title: Audio volume follows Threshold type: boolean description: > This controls whether to also apply a volume level adjstment corresponding to the current mix property. The default is retained for legacy reason, but it is generally not recommended to enable this. default: yes mutable: yes - identifier: alpha_operation title: Alpha Operation type: string description: > The way to combine the alpha channel of the mask with the source, but this currently only works when use_mix = 0. default: overwrite mutable: yes values: - add - overwrite - maximum - minimum - subtract mlt-7.38.0/src/modules/plus/filter_spot_remover.c000664 000000 000000 00000025367 15172202314 022056 0ustar00rootroot000000 000000 /* * filter_remover.c -- filter to interpolate pixels to cover an area * Copyright (c) 2018-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include /** Scale a rectangle by the specified factors. */ static mlt_rect scale_rect(mlt_rect rect, double x_scale, double y_scale) { rect.x = rect.x / x_scale; rect.y = rect.y / y_scale; rect.w = rect.w / x_scale; rect.h = rect.h / y_scale; return rect; } /** Constrain a rect to be within the max dimensions with an additional 1 pixel * padding. */ static mlt_rect constrain_rect(mlt_rect rect, int max_x, int max_y) { rect.x = round(rect.x); rect.y = round(rect.y); rect.w = round(rect.w); rect.h = round(rect.h); if (rect.x < 0) { rect.w = rect.w + rect.x - 1; rect.x = 1; } if (rect.y < 0) { rect.h = rect.h + rect.y - 1; rect.y = 1; } if (rect.x + rect.w < 0) { rect.w = 0; } if (rect.y + rect.h < 0) { rect.h = 0; } if (rect.x < 1) { rect.x = 1; } if (rect.y < 1) { rect.y = 1; } if (rect.x + rect.w > max_x - 1) { rect.w = max_x - rect.x - 1; } if (rect.y + rect.h > max_y - 1) { rect.h = max_y - rect.y - 1; } return rect; } typedef struct { void *chan[4]; // pointer to the first value in the channel int rowCount[4]; // the number of values in each line (row) int step[4]; // the space between values in each line int word[4]; // the number of bits in a word (8 or 16) mlt_rect rect[4]; // rect the area to be removed } slice_desc; /** Perform spot removal on a channel. * * Values within the rectangle are replaced with interpolated values. * Each value is an interpolation of the first values outside of the rect on * the top, bottom, left and right of the value being interpolated. */ static int remove_spot_channel_proc(int id, int index, int jobs, void *data) { (void) id; // unused (void) jobs; // unused slice_desc *desc = ((slice_desc *) data); int rowCount = desc->rowCount[index]; int step = desc->step[index]; int word = desc->word[index]; mlt_rect rect = desc->rect[index]; int yStop = rect.y + rect.h; int xStop = rect.x + rect.w; int rowSize = rowCount * step; if (word == 8) { uint8_t *chan = desc->chan[index]; for (int y = rect.y; y < yStop; y++) { uint8_t *xValueL = chan + (y * rowSize) + (((int) rect.x - 1) * step); uint8_t *xValueR = xValueL + ((int) rect.w * step); uint8_t *p = chan + (y * rowSize) + ((int) rect.x * step); double yRatio = 1.0 - ((y - rect.y) / rect.h); for (int x = rect.x; x < xStop; x++) { uint8_t *yValueT = chan + (((int) rect.y - 1) * rowSize) + (x * step); uint8_t *yValueB = yValueT + (int) rect.h * rowSize; double xRatio = 1.0 - ((x - rect.x) / rect.w); unsigned int xValueInterp = (*xValueL * xRatio) + (*xValueR * (1.0 - xRatio)); unsigned int yValueInterp = (*yValueT * yRatio) + (*yValueB * (1.0 - yRatio)); unsigned int value = (xValueInterp + yValueInterp) / 2; if (value > 255) value = 255; *p = value; p += step; } } } else if (word == 16) { uint16_t *chan = desc->chan[index]; for (int y = rect.y; y < yStop; y++) { uint16_t *xValueL = chan + (y * rowSize) + (((int) rect.x - 1) * step); uint16_t *xValueR = xValueL + ((int) rect.w * step); uint16_t *p = chan + (y * rowSize) + ((int) rect.x * step); double yRatio = 1.0 - ((y - rect.y) / rect.h); for (int x = rect.x; x < xStop; x++) { uint16_t *yValueT = chan + (((int) rect.y - 1) * rowSize) + (x * step); uint16_t *yValueB = yValueT + (int) rect.h * rowSize; double xRatio = 1.0 - ((x - rect.x) / rect.w); unsigned int xValueInterp = (*xValueL * xRatio) + (*xValueR * (1.0 - xRatio)); unsigned int yValueInterp = (*yValueT * yRatio) + (*yValueB * (1.0 - yRatio)); unsigned int value = (xValueInterp + yValueInterp) / 2; if (value > 65535) value = 65535; *p = value; p += step; } } } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); char *rect_str = mlt_properties_get(filter_properties, "rect"); if (!rect_str) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "rect property not set\n"); return mlt_frame_get_image(frame, image, format, width, height, writable); } mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_rect rect = mlt_properties_anim_get_rect(filter_properties, "rect", position, length); if (strchr(rect_str, '%')) { rect.x *= profile->width; rect.w *= profile->width; rect.y *= profile->height; rect.h *= profile->height; } double scale = mlt_profile_scale_width(profile, *width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); rect.y *= scale; rect.h *= scale; rect = constrain_rect(rect, profile->width * scale, profile->height * scale); if (rect.w < 1 || rect.h < 1) { mlt_log_info(MLT_FILTER_SERVICE(filter), "rect invalid\n"); return mlt_frame_get_image(frame, image, format, width, height, writable); } switch (*format) { case mlt_image_rgba: case mlt_image_rgb: case mlt_image_yuv422: case mlt_image_yuv420p: case mlt_image_rgba64: // These formats are all supported break; default: *format = mlt_image_rgba; break; } error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error) return error; struct mlt_image_s img; mlt_image_set_values(&img, *image, *format, *width, *height); // Process each plane in a separate thread. int i; slice_desc desc; int jobs = 0; switch (*format) { case mlt_image_rgba: jobs = 4; for (i = 0; i < 4; i++) { desc.chan[i] = img.planes[0] + i; desc.rowCount[i] = img.width; desc.step[i] = 4; desc.word[i] = 8; desc.rect[i] = rect; } break; case mlt_image_rgb: jobs = 3; for (i = 0; i < 3; i++) { desc.chan[i] = img.planes[0] + i; desc.rowCount[i] = img.width; desc.step[i] = 4; desc.word[i] = 8; desc.rect[i] = rect; } break; case mlt_image_yuv422: jobs = 3; // Y desc.chan[0] = img.planes[0]; desc.rowCount[0] = img.width; desc.step[0] = 2; desc.word[0] = 8; desc.rect[0] = rect; // U desc.chan[1] = img.planes[0] + 1; desc.rowCount[1] = img.width / 2; desc.step[1] = 4; desc.word[1] = 8; desc.rect[1] = constrain_rect(scale_rect(rect, 2, 1), img.width / 2, img.height); // V desc.chan[2] = img.planes[0] + 3; desc.rowCount[2] = img.width / 2; desc.step[2] = 4; desc.word[2] = 8; desc.rect[2] = constrain_rect(scale_rect(rect, 2, 1), img.width / 2, img.height); break; case mlt_image_yuv420p: jobs = 3; // Y desc.chan[0] = img.planes[0]; desc.rowCount[0] = img.width; desc.step[0] = 1; desc.word[0] = 8; desc.rect[0] = rect; // U desc.chan[1] = img.planes[1]; desc.rowCount[1] = img.width / 2; desc.step[1] = 1; desc.word[1] = 8; desc.rect[1] = constrain_rect(scale_rect(rect, 2, 2), img.width / 2, img.height / 2); // V desc.chan[2] = img.planes[2]; desc.rowCount[2] = img.width / 2; desc.step[2] = 1; desc.word[2] = 8; desc.rect[2] = constrain_rect(scale_rect(rect, 2, 2), img.width / 2, img.height / 2); break; case mlt_image_rgba64: jobs = 4; for (i = 0; i < 4; i++) { desc.chan[i] = ((uint16_t *) img.planes[0]) + i; desc.rowCount[i] = img.width; desc.step[i] = 4; desc.word[i] = 16; desc.rect[i] = rect; } break; default: return 1; } uint8_t *alpha = mlt_frame_get_alpha(frame); if (alpha && *format != mlt_image_rgba && *format != mlt_image_rgba64) { jobs++; desc.chan[3] = alpha; desc.rowCount[3] = img.width; desc.step[3] = 1; desc.word[3] = 8; desc.rect[3] = rect; } mlt_slices_run_normal(jobs, remove_spot_channel_proc, &desc); return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } mlt_filter filter_spot_remover_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set(properties, "rect", "0% 0% 10% 10%"); filter->process = filter_process; } else { mlt_log_error(NULL, "Filter spot_remover initialization failed\n"); } return filter; } mlt-7.38.0/src/modules/plus/filter_spot_remover.yml000664 000000 000000 00000001255 15172202314 022423 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: spot_remover title: Spot Remover version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > Replace an area with interpolated pixels. The new pixel values are interpolated from the nearest pixels immediately outside of the specified area. parameters: - identifier: rect title: Rectangle description: > Defines the rectangle of the area that will be removed. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 10% 10%" readonly: no mutable: yes animation: yes mlt-7.38.0/src/modules/plus/filter_strobe.c000664 000000 000000 00000010325 15172202314 020614 0ustar00rootroot000000 000000 /* * filter_strobe.c -- simple strobing filter * Copyright (C) 2020 Martin Sandsmark * Copyright (C) 2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); int invert = mlt_properties_anim_get_int(properties, "strobe_invert", position, length); int interval = mlt_properties_anim_get_int(properties, "interval", position, length); int do_strobe = (position % (interval + 1)) > interval / 2; if (invert) { do_strobe = !do_strobe; } if (!do_strobe) { return mlt_frame_get_image(frame, image, format, width, height, 0); } if (*format == mlt_image_movit || *format == mlt_image_opengl_texture) *format = mlt_image_rgba; int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (error) { return error; } assert(*width >= 0); assert(*height >= 0); size_t pixelCount = *width * *height; if (*format == mlt_image_rgba) { uint8_t *bytes = *image; for (size_t i = 3; i < pixelCount * 4; i += 4) { bytes[i] = 0; } // Clear any alpha buffer that may be attached to the frame mlt_frame_set_alpha(frame, NULL, 0, NULL); } else if (*format == mlt_image_rgba64) { uint16_t *p = (uint16_t *) *image; for (size_t i = 0; i < pixelCount; i++) { p[3] = 0; p += 4; } // Clear any alpha buffer that may be attached to the frame mlt_frame_set_alpha(frame, NULL, 0, NULL); } else { int size = 0; uint8_t *a = mlt_frame_get_alpha_size(frame, &size); if (!a || size < pixelCount) { a = mlt_pool_alloc(pixelCount); if (!a) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Unable to allocate alpha\n"); return 1; } mlt_frame_set_alpha(frame, a, pixelCount, NULL); } memset(a, 0, pixelCount); } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the filter on to the stack mlt_frame_push_service(frame, filter); // Push the frame filter mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_strobe_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; // If strobe_invert == 1, the odd number of frames will be filtered out mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "strobe_invert", "0"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "interval", "1"); } return filter; } mlt-7.38.0/src/modules/plus/filter_strobe.yml000664 000000 000000 00000001350 15172202314 021171 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: strobe title: Alpha strobing version: 2 copyright: Martin Sandsmark creator: Martin Sandsmark license: LGPLv2.1 language: en description: Strobes the alpha channel to 0. Many other filters overwrite the alpha channel, in that case this needs to be last. tags: - Video parameters: - identifier: strobe_invert title: Invert type: boolean description: Whether to invert which frames are on and which is off default: 0 mutable: yes animation: yes - identifier: interval title: Interval type: integer description: > Duration of strobe default: 1 minimum: 1 maximum: 100 readonly: no mutable: yes animation: yes widget: spinner mlt-7.38.0/src/modules/plus/filter_subtitle.cpp000664 000000 000000 00000024477 15172202314 021526 0ustar00rootroot000000 000000 /* * filter_subtitle.cpp -- subtitle overlay filter * Copyright (C) 2024-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "subtitles/subtitles.h" #include #include #include // for stat() #include // for stat() static void destroy_subtitles(void *p) { delete static_cast(p); } static void property_changed(mlt_service owner, mlt_filter filter, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "text")) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "_reset", 1); } } static void refresh_resource_subtitles(mlt_filter filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); const char *resource = mlt_properties_get(properties, "resource"); struct stat file_info; if (mlt_stat(resource, &file_info)) { mlt_log_debug(filter, "File not found %s\n", resource); return; } int64_t mtime = (int64_t) file_info.st_mtime; if (mtime != mlt_properties_get_int64(properties, "_mtime")) { mlt_log_info(filter, "File has changed. Reopen: %s\n", resource); Subtitles::SubtitleVector *psubtitles = new Subtitles::SubtitleVector(); if (!psubtitles) { mlt_log_error(filter, "Unable to allocate subtitles %s\n", resource); return; } *psubtitles = Subtitles::readFromSrtFile(resource); mlt_properties_set_data(properties, "_subtitles", psubtitles, 0, (mlt_destructor) destroy_subtitles, NULL); mlt_properties_set_int64(properties, "_mtime", mtime); } } static void refresh_text_subtitles(mlt_filter filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); if (mlt_properties_get_int(properties, "_reset")) { const char *text = mlt_properties_get(properties, "text"); Subtitles::SubtitleVector *psubtitles = new Subtitles::SubtitleVector(); if (!psubtitles) { mlt_log_error(filter, "Unable to allocate subtitles\n"); return; } *psubtitles = Subtitles::readFromSrtString(text); mlt_properties_set_data(properties, "_subtitles", psubtitles, 0, (mlt_destructor) destroy_subtitles, NULL); mlt_properties_clear(properties, "_reset"); } } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); const char *text = NULL; if (mlt_properties_exists(properties, "resource") || mlt_properties_exists(properties, "text")) { if (mlt_properties_exists(properties, "resource")) { refresh_resource_subtitles(filter); } else if (mlt_properties_exists(properties, "text")) { refresh_text_subtitles(filter); } Subtitles::SubtitleVector *psubtitles = static_cast( mlt_properties_get_data(properties, "_subtitles", NULL)); if (!psubtitles) { psubtitles = new Subtitles::SubtitleVector(); if (!psubtitles) { mlt_log_error(filter, "Unable to allocate subtitles\n"); return frame; } mlt_properties_set_data(properties, "_subtitles", psubtitles, 0, (mlt_destructor) destroy_subtitles, NULL); } const Subtitles::SubtitleVector &subtitles = *psubtitles; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); int64_t frameMs = (int64_t) mlt_frame_get_position(frame) * 1000 * profile->frame_rate_den / profile->frame_rate_num; int prevIndex = mlt_properties_get_int(properties, "_prevIndex"); int marginMs = 999 * profile->frame_rate_den / profile->frame_rate_num; int index = Subtitles::indexForTime(subtitles, frameMs, prevIndex, marginMs); if (index > -1) { text = subtitles[index].text.c_str(); mlt_properties_set_int(properties, "_prevIndex", index); } else { return frame; } } else if (mlt_properties_exists(properties, "feed")) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties subtitle_properties = mlt_properties_get_properties(frame_properties, "subtitles"); if (!subtitle_properties) { mlt_log_info(filter, "No feed subtitles found\n"); return frame; } char *feed = mlt_properties_get(properties, "feed"); mlt_properties feed_properties = mlt_properties_get_properties(subtitle_properties, feed); if (!feed_properties) { mlt_log_info(filter, "Feed %s not found\n", feed); return frame; } text = mlt_properties_get(feed_properties, "text"); } if (!text || !strcmp(text, "")) { // Do not draw empty text return frame; } mlt_filter text_filter = (mlt_filter) mlt_properties_get_data(properties, "_t", NULL); if (!text_filter) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Text filter not found\n"); return frame; } // We have some text and a text filter. Let's apply them to the frame. mlt_properties text_filter_properties = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(text_filter)); mlt_properties_set_string(text_filter_properties, "argument", text); mlt_properties_pass_list(text_filter_properties, properties, "geometry family size weight style fgcolour bgcolour olcolour pad " "halign valign outline underline strikethrough opacity " "typewriter typewriter.step_length typewriter.step_sigma " "typewriter.random_seed typewriter.macro_type typewriter.cursor " "typewriter.cursor_blink_rate typewriter.cursor_char"); mlt_filter_set_in_and_out(text_filter, mlt_filter_get_in(filter), mlt_filter_get_out(filter)); return mlt_filter_process(text_filter, frame); } extern "C" { mlt_filter filter_subtitle_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter text_filter = mlt_factory_filter(profile, "qtext", NULL); if (!text_filter) text_filter = mlt_factory_filter(profile, "text", NULL); if (!text_filter) { mlt_log_error(NULL, "[filter_subtitle] Unable to create text filter.\n"); return NULL; } mlt_filter filter = mlt_filter_new(); if (filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); // Assign default values if (arg && strcmp(arg, "")) { mlt_properties_set_string(properties, "resource", arg); } mlt_properties_set_string(properties, "geometry", "20%/80%:60%x20%:100"); mlt_properties_set_string(properties, "family", "Sans"); mlt_properties_set_string(properties, "size", "48"); mlt_properties_set_string(properties, "weight", "400"); mlt_properties_set_string(properties, "style", "normal"); mlt_properties_set_string(properties, "fgcolour", "0x000000ff"); mlt_properties_set_string(properties, "bgcolour", "0x00000020"); mlt_properties_set_string(properties, "olcolour", "0x00000000"); mlt_properties_set_string(properties, "pad", "0"); mlt_properties_set_string(properties, "halign", "left"); mlt_properties_set_string(properties, "valign", "top"); mlt_properties_set_string(properties, "outline", "0"); mlt_properties_set_string(properties, "underline", "0"); mlt_properties_set_string(properties, "strikethrough", "0"); mlt_properties_set_string(properties, "opacity", "1.0"); mlt_properties_set_int(properties, "typewriter", 0); mlt_properties_set_int(properties, "typewriter.step_length", 25); mlt_properties_set_int(properties, "typewriter.step_sigma", 0); mlt_properties_set_int(properties, "typewriter.random_seed", 0); mlt_properties_set_int(properties, "typewriter.macro_type", 1); mlt_properties_set_int(properties, "typewriter.cursor", 1); mlt_properties_set_int(properties, "typewriter.cursor_blink_rate", 25); mlt_properties_set_string(properties, "typewriter.cursor_char", "|"); mlt_properties_set_int(properties, "_filter_private", 1); // Register the text filter for reuse/destruction mlt_properties_set_data(properties, "_t", text_filter, 0, (mlt_destructor) mlt_filter_close, NULL); filter->process = filter_process; mlt_events_listen(properties, filter, "property-changed", (mlt_listener) property_changed); } else { mlt_log_error(NULL, "[filter_subtitle] Unable to allocate filter.\n"); mlt_filter_close(text_filter); } return filter; } } mlt-7.38.0/src/modules/plus/filter_subtitle.yml000664 000000 000000 00000016160 15172202314 021533 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: subtitle title: Subtitle version: 4 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Overlay subtitles onto the video parameters: - identifier: resource argument: yes title: Resource type: string description: The path to a text file that contains SRT subtitles required: no readonly: no - identifier: text title: Text type: string description: > A string that containes a complete SRT document. This parameter is ignored if resource is set. required: no readonly: no - identifier: feed title: Text type: string description: > A string that identifies the feed in the MLT Frame to overlay. This parameter is ignored if resource or text is set. required: no default: 0 readonly: no - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 20%/80%:60%x20%:100 - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 mutable: yes widget: spinner - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo - identifier: opacity title: Opacity type: float description: Opacity of all elements - text, outline, and background readonly: no default: 1.0 minimum: 0 maximum: 1.0 mutable: yes widget: slider - identifier: typewriter title: Typewriter Effect type: boolean description: > Enable typewriter effect where text appears progressively over time. Only supported when the qtext filter is available. default: 0 minimum: 0 maximum: 1 readonly: no mutable: yes widget: checkbox - identifier: typewriter.step_length title: Typewriter Step Length type: integer description: > Number of frames between each character, word, or line appearance. Only supported when the qtext filter is available. default: 25 minimum: 1 readonly: no mutable: yes widget: spinner - identifier: typewriter.step_sigma title: Typewriter Step Variation type: integer description: > Random variation in timing (standard deviation in frames). Only supported when the qtext filter is available. default: 0 minimum: 0 readonly: no mutable: yes widget: spinner - identifier: typewriter.random_seed title: Typewriter Random Seed type: integer description: > Seed value for random number generation in timing variations. Only supported when the qtext filter is available. default: 0 minimum: 0 readonly: no mutable: yes widget: spinner - identifier: typewriter.macro_type title: Typewriter Animation Mode type: integer description: > Granularity of typewriter animation. Only supported when the qtext filter is available. values: - 1 # Character by character - 2 # Word by word - 3 # Line by line default: 1 readonly: no mutable: yes widget: combo - identifier: typewriter.cursor title: Typewriter Cursor type: integer description: > Show blinking cursor during typewriter animation. 0 = no cursor, 1 = cursor while typing, 2 = cursor always visible. Only supported when the qtext filter is available. default: 1 minimum: 0 maximum: 2 readonly: no mutable: yes widget: combo - identifier: typewriter.cursor_blink_rate title: Cursor Blink Rate type: integer description: > Number of frames for cursor blink cycle (on + off). Only supported when the qtext filter is available. default: 25 minimum: 0 readonly: no mutable: yes widget: spinner unit: frames - identifier: typewriter.cursor_char title: Cursor Character type: string description: > Character to use for the blinking cursor. Only supported when the qtext filter is available. default: "|" readonly: no mutable: yes widget: text mlt-7.38.0/src/modules/plus/filter_subtitle_feed.cpp000664 000000 000000 00000017440 15172202314 022501 0ustar00rootroot000000 000000 /* * filter_subtitle_feed.cpp * Copyright (C) 2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "subtitles/subtitles.h" #include #include #include #include // for stat() #include // for stat() static void destroy_subtitles(void *p) { delete static_cast(p); } static void property_changed(mlt_service owner, mlt_filter filter, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "text")) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "_reset", 1); } } static void refresh_resource_subtitles(mlt_filter filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); const char *resource = mlt_properties_get(properties, "resource"); struct stat file_info; if (mlt_stat(resource, &file_info)) { mlt_log_debug(filter, "File not found %s\n", resource); return; } int64_t mtime = (int64_t) file_info.st_mtime; if (mtime != mlt_properties_get_int64(properties, "_mtime")) { mlt_log_info(filter, "File has changed. Reopen: %s\n", resource); Subtitles::SubtitleVector *psubtitles = new Subtitles::SubtitleVector(); if (!psubtitles) { mlt_log_error(filter, "Unable to allocate subtitles %s\n", resource); return; } *psubtitles = Subtitles::readFromSrtFile(resource); mlt_properties_set_data(properties, "_subtitles", psubtitles, 0, (mlt_destructor) destroy_subtitles, NULL); mlt_properties_set_int64(properties, "_mtime", mtime); } } static void refresh_text_subtitles(mlt_filter filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); if (mlt_properties_get_int(properties, "_reset")) { const char *text = mlt_properties_get(properties, "text"); Subtitles::SubtitleVector *psubtitles = new Subtitles::SubtitleVector(); if (!psubtitles) { mlt_log_error(filter, "Unable to allocate subtitles\n"); return; } *psubtitles = Subtitles::readFromSrtString(text); mlt_properties_set_data(properties, "_subtitles", psubtitles, 0, (mlt_destructor) destroy_subtitles, NULL); mlt_properties_clear(properties, "_reset"); } } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); if (mlt_properties_exists(properties, "resource")) { refresh_resource_subtitles(filter); } else if (mlt_properties_exists(properties, "text")) { refresh_text_subtitles(filter); } Subtitles::SubtitleVector *psubtitles = static_cast( mlt_properties_get_data(properties, "_subtitles", NULL)); if (!psubtitles) { psubtitles = new Subtitles::SubtitleVector(); if (!psubtitles) { mlt_log_error(filter, "Unable to allocate subtitles\n"); return frame; } mlt_properties_set_data(properties, "_subtitles", psubtitles, 0, (mlt_destructor) destroy_subtitles, NULL); } const Subtitles::SubtitleVector &subtitles = *psubtitles; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_position position = mlt_frame_get_position(frame); int64_t frameMs = (int64_t) position * 1000 * profile->frame_rate_den / profile->frame_rate_num; int in = mlt_properties_get_int(frame_properties, "in"); if (in < 0) { in = 0; } int64_t minMs = (int64_t) in * 1000 * profile->frame_rate_den / profile->frame_rate_num; int out = mlt_properties_get_int(frame_properties, "out"); int64_t maxMs = INT64_MAX; if (out > 0) { maxMs = (int64_t) out * 1000 * profile->frame_rate_den / profile->frame_rate_num; } int prevIndex = mlt_properties_get_int(properties, "_prevIndex"); int marginMs = 999 * profile->frame_rate_den / profile->frame_rate_num; int index = Subtitles::indexForTime(subtitles, frameMs, prevIndex, marginMs); if (index > -1) { mlt_properties_set_int(properties, "_prevIndex", index); } mlt_properties subtitle_properties = mlt_properties_get_properties(frame_properties, "subtitles"); if (!subtitle_properties) { subtitle_properties = mlt_properties_new(); mlt_properties_set_properties(frame_properties, "subtitles", subtitle_properties); } mlt_properties feed_properties = mlt_properties_new(); mlt_properties_set(feed_properties, "lang", mlt_properties_get(properties, "lang")); if (index > -1 && subtitles[index].start >= minMs && subtitles[index].end <= maxMs) { mlt_position start = subtitles[index].start * profile->frame_rate_num / profile->frame_rate_den / 1000; start -= in; mlt_properties_set_position(feed_properties, "start", start); mlt_position end = subtitles[index].end * profile->frame_rate_num / profile->frame_rate_den / 1000; end -= in; mlt_properties_set_position(feed_properties, "end", end); mlt_properties_set(feed_properties, "text", subtitles[index].text.c_str()); } else { mlt_properties_set_position(feed_properties, "start", -1); mlt_properties_set_position(feed_properties, "end", -1); mlt_properties_set(feed_properties, "text", ""); } char *feed = mlt_properties_get(properties, "feed"); mlt_properties_set_properties(subtitle_properties, feed, feed_properties); return frame; } extern "C" { mlt_filter filter_subtitle_feed_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); // Assign default values if (arg) { mlt_properties_set_string(properties, "resource", arg); } mlt_properties_set_string(properties, "feed", "0"); mlt_properties_set_string(properties, "lang", "eng"); mlt_properties_set_int(properties, "_reset", 1); filter->process = filter_process; mlt_events_listen(properties, filter, "property-changed", (mlt_listener) property_changed); } else { mlt_log_error(NULL, "[filter_subtitle_feed] Unable to allocate filter.\n"); } return filter; } } mlt-7.38.0/src/modules/plus/filter_subtitle_feed.yml000664 000000 000000 00000002171 15172202314 022513 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: subtitle_feed title: Subtitle version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Attach subtitle data to MLT frames notes: > This filter does not modify image or audio data. It only attaches subtitle data to each frame to be used by downstream services. parameters: - identifier: resource argument: yes title: Resource type: string description: The path to a text file that contains SRT subtitles required: no readonly: no - identifier: text title: Text type: string description: > A string that contains a complete SRT document. This parameter is ignored if resource is set. required: no readonly: no - identifier: feed title: Text type: string description: > A string that identifies the feed to be output in the MLT Frame. required: no default: 0 readonly: no - identifier: lang title: Language type: string description: The three character language identifier for the subtitles. required: no default: eng readonly: no mlt-7.38.0/src/modules/plus/filter_text.c000664 000000 000000 00000030410 15172202314 020277 0ustar00rootroot000000 000000 /* * filter_text.c -- text overlay filter * Copyright (C) 2018-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include static void property_changed(mlt_service owner, mlt_filter filter, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (!name) return; if (!strcmp("geometry", name) || !strcmp("family", name) || !strcmp("size", name) || !strcmp("weight", name) || !strcmp("style", name) || !strcmp("fgcolour", name) || !strcmp("bgcolour", name) || !strcmp("olcolour", name) || !strcmp("pad", name) || !strcmp("halign", name) || !strcmp("valign", name) || !strcmp("outline", name) || !strcmp("underline", name) || !strcmp("strikethrough", name)) { mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "_reset", 1); } } static void setup_producer(mlt_producer producer, mlt_properties my_properties) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Pass the properties to the text producer mlt_properties_set_string(producer_properties, "family", mlt_properties_get(my_properties, "family")); mlt_properties_set_string(producer_properties, "size", mlt_properties_get(my_properties, "size")); mlt_properties_set_string(producer_properties, "weight", mlt_properties_get(my_properties, "weight")); mlt_properties_set_string(producer_properties, "style", mlt_properties_get(my_properties, "style")); mlt_properties_set_string(producer_properties, "fgcolour", mlt_properties_get(my_properties, "fgcolour")); mlt_properties_set_string(producer_properties, "bgcolour", mlt_properties_get(my_properties, "bgcolour")); mlt_properties_set_string(producer_properties, "olcolour", mlt_properties_get(my_properties, "olcolour")); mlt_properties_set_string(producer_properties, "pad", mlt_properties_get(my_properties, "pad")); mlt_properties_set_string(producer_properties, "outline", mlt_properties_get(my_properties, "outline")); mlt_properties_set_string(producer_properties, "underline", mlt_properties_get(my_properties, "underline")); mlt_properties_set_string(producer_properties, "strikethrough", mlt_properties_get(my_properties, "strikethrough")); mlt_properties_set_string(producer_properties, "align", mlt_properties_get(my_properties, "halign")); } static void setup_transition(mlt_filter filter, mlt_transition transition, mlt_frame frame, mlt_properties my_properties) { mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES(transition); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_service_lock(MLT_TRANSITION_SERVICE(transition)); mlt_rect rect = mlt_properties_anim_get_rect(my_properties, "geometry", position, length); if (mlt_properties_get(my_properties, "geometry") && strchr(mlt_properties_get(my_properties, "geometry"), '%')) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); rect.x *= profile->width; rect.y *= profile->height; rect.w *= profile->width; rect.h *= profile->height; } mlt_properties_set_rect(transition_properties, "rect", rect); mlt_properties_set_string(transition_properties, "halign", mlt_properties_get(my_properties, "halign")); mlt_properties_set_string(transition_properties, "valign", mlt_properties_get(my_properties, "valign")); mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); } static mlt_properties get_filter_properties(mlt_filter filter, mlt_frame frame) { mlt_properties properties = mlt_frame_get_unique_properties(frame, MLT_FILTER_SERVICE(filter)); if (!properties) properties = MLT_FILTER_PROPERTIES(filter); return properties; } /** Get the image. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = mlt_frame_pop_service(frame); char *argument = (char *) mlt_frame_pop_service(frame); mlt_properties my_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties properties = get_filter_properties(filter, frame); mlt_producer producer = mlt_properties_get_data(my_properties, "_producer", NULL); mlt_transition transition = mlt_properties_get_data(my_properties, "_transition", NULL); mlt_frame b_frame = 0; mlt_position position = 0; // Configure this filter mlt_service_lock(MLT_FILTER_SERVICE(filter)); if (mlt_properties_get_int(my_properties, "_reset")) { setup_producer(producer, properties); setup_transition(filter, transition, frame, properties); } mlt_properties_set_string(MLT_PRODUCER_PROPERTIES(producer), "text", argument); // Make sure the producer is in the correct position position = mlt_filter_get_position(filter, frame); mlt_producer_seek(producer, position); // Get the b frame and process with transition if successful if (mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &b_frame, 0) == 0) { // This lock needs to also protect the producer properties from being // modified in setup_producer() while also being used in mlt_service_get_frame(). mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Get the a and b frame properties mlt_properties a_props = MLT_FRAME_PROPERTIES(frame); mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); // Set the b_frame to be in the same position and have same consumer requirements mlt_frame_set_position(b_frame, position); mlt_properties_set_int(b_props, "consumer.progressive", mlt_properties_get_int(a_props, "consumer.progressive")); mlt_properties_set_double(b_props, "consumer_scale", mlt_properties_get_double(a_props, "consumer_scale")); // Apply all filters that are attached to this filter to the b frame mlt_service_apply_filters(MLT_FILTER_SERVICE(filter), b_frame, 0); // Process the frame mlt_transition_process(transition, frame, b_frame); // Get the image error = mlt_frame_get_image(frame, image, format, width, height, writable); // Close the temporary frames mlt_frame_close(b_frame); } else { mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } free(argument); return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = get_filter_properties(filter, frame); char *argument = mlt_properties_get(properties, "argument"); if (!argument || !strcmp("", argument)) return frame; // Save the text to be used by get_image() to support parallel processing // when this filter is encapsulated by other filters. mlt_frame_push_service(frame, strdup(argument)); // Push the filter on to the stack mlt_frame_push_service(frame, filter); // Push the get_image on to the stack mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_text_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); mlt_transition transition = mlt_factory_transition(profile, "affine", NULL); mlt_producer producer = mlt_factory_producer(profile, mlt_environment("MLT_PRODUCER"), "qtext:"); // Use pango if qtext is not available. if (!producer) producer = mlt_factory_producer(profile, mlt_environment("MLT_PRODUCER"), "pango:"); if (!producer) mlt_log_warning(MLT_FILTER_SERVICE(filter), "QT or GTK modules required for text.\n"); if (filter && transition && producer) { mlt_properties my_properties = MLT_FILTER_PROPERTIES(filter); // Register the transition for reuse/destruction mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "fill", 0); mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "b_scaled", 1); mlt_properties_set_data(my_properties, "_transition", transition, 0, (mlt_destructor) mlt_transition_close, NULL); // Register the producer for reuse/destruction mlt_properties_set_data(my_properties, "_producer", producer, 0, (mlt_destructor) mlt_producer_close, NULL); // Ensure that we loop mlt_properties_set_string(MLT_PRODUCER_PROPERTIES(producer), "eof", "loop"); // Listen for property changes. mlt_events_listen(MLT_FILTER_PROPERTIES(filter), filter, "property-changed", (mlt_listener) property_changed); // Assign default values mlt_properties_set_string(my_properties, "argument", arg ? arg : "text"); mlt_properties_set_string(my_properties, "geometry", "0%/0%:100%x100%:100%"); mlt_properties_set_string(my_properties, "family", "Sans"); mlt_properties_set_string(my_properties, "size", "48"); mlt_properties_set_string(my_properties, "weight", "400"); mlt_properties_set_string(my_properties, "style", "normal"); mlt_properties_set_string(my_properties, "fgcolour", "0x000000ff"); mlt_properties_set_string(my_properties, "bgcolour", "0x00000020"); mlt_properties_set_string(my_properties, "olcolour", "0x00000000"); mlt_properties_set_string(my_properties, "pad", "0"); mlt_properties_set_string(my_properties, "halign", "left"); mlt_properties_set_string(my_properties, "valign", "top"); mlt_properties_set_string(my_properties, "outline", "0"); mlt_properties_set_string(my_properties, "underline", "0"); mlt_properties_set_string(my_properties, "strikethrough", "0"); mlt_properties_set_int(my_properties, "_reset", 1); mlt_properties_set_int(my_properties, "_filter_private", 1); filter->process = filter_process; } else { if (filter) { mlt_filter_close(filter); } if (transition) { mlt_transition_close(transition); } if (producer) { mlt_producer_close(producer); } filter = NULL; } return filter; } mlt-7.38.0/src/modules/plus/filter_text.yml000664 000000 000000 00000007550 15172202314 020667 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: text title: Text version: 3 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Overlay text onto the video parameters: - identifier: argument title: Text type: string description: | The text to overlay. required: yes argument: yes readonly: no default: text widget: text - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 mutable: yes animation: yes - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 mutable: yes widget: spinner - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo mlt-7.38.0/src/modules/plus/filter_threshold.c000664 000000 000000 00000011203 15172202314 021306 0ustar00rootroot000000 000000 /* * filter_threshold.c -- Arbitrary alpha channel shaping * Copyright (C) 2008-2022 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include typedef struct { int midpoint; int use_alpha; int invert; int full_luma; uint8_t *image; uint8_t *alpha; int width; int height; } slice_desc; static int do_slice_proc(int id, int index, int jobs, void *data) { (void) id; // unused slice_desc *desc = (slice_desc *) data; int slice_line_start, slice_height = mlt_slices_size_slice(jobs, index, desc->height, &slice_line_start); int size = desc->width * slice_height * 2; uint8_t white = desc->full_luma ? 255 : 235; uint8_t black = desc->full_luma ? 0 : 16; uint8_t A = desc->invert ? white : black; uint8_t B = desc->invert ? black : white; uint8_t *p = desc->image + (slice_line_start * desc->width * 2); int i = 0; if (!desc->use_alpha) { for (i = 0; i < size; i += 2) { if (p[i] < desc->midpoint) p[i] = A; else p[i] = B; p[i + 1] = 128; } } else { if (desc->alpha) { uint8_t *alpha = desc->alpha + (slice_line_start * desc->width); for (i = 0; i < size; i += 2) { if (alpha[i / 2] < desc->midpoint) p[i] = A; else p[i] = B; p[i + 1] = 128; } } else { for (i = 0; i < size; i += 2) { p[i] = B; p[i + 1] = 128; } } } return 0; } /** Get the images and apply the luminance of the mask to the alpha of the frame. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = mlt_frame_pop_service(frame); // Render the frame *format = mlt_image_yuv422; if (mlt_frame_get_image(frame, image, format, width, height, writable) == 0) { slice_desc desc; mlt_properties properties = mlt_filter_properties(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); desc.midpoint = mlt_properties_anim_get_int(properties, "midpoint", position, length); desc.use_alpha = mlt_properties_get_int(properties, "use_alpha"); desc.invert = mlt_properties_get_int(properties, "invert"); desc.full_luma = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_luma"); desc.image = *image; desc.alpha = NULL; desc.width = *width; desc.height = *height; if (desc.use_alpha) { desc.alpha = mlt_frame_get_alpha(frame); } mlt_slices_run_normal(0, do_slice_proc, &desc); } return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_threshold_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "midpoint", 128); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "use_alpha", 0); mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "invert", 0); filter->process = filter_process; } return filter; } mlt-7.38.0/src/modules/plus/filter_threshold.yml000664 000000 000000 00000001315 15172202314 021670 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: threshold title: Threshold version: 2 creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: midpoint title: Threshold type: integer description: The value of the threshold mutable: yes minimum: 0 maximum: 255 default: 128 - identifier: use_alpha title: Use alpha channel type: boolean description: Whether to compare the midpoint with the alpha channel instead of luma mutable: yes default: 0 widget: checkbox - identifier: invert title: Invert type: integer description: Whether to swap black and white mutable: yes default: 0 widget: checkbox mlt-7.38.0/src/modules/plus/filter_timer.c000664 000000 000000 00000017123 15172202314 020441 0ustar00rootroot000000 000000 /* * filter_timer.c -- timer text overlay filter * Copyright (C) 2018-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #define MAX_TEXT_LEN 512 double time_to_seconds(char *time) { int hours = 0; int mins = 0; double secs = 0; if (time) sscanf(time, "%d:%d:%lf", &hours, &mins, &secs); return (hours * 60.0 * 60.0) + (mins * 60.0) + secs; } static void get_timer_str(mlt_filter filter, mlt_frame frame, char *text) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); double filter_speed = mlt_properties_get_double(properties, "speed"); mlt_position current_frame = mlt_filter_get_position(filter, frame) * filter_speed; char *direction = mlt_properties_get(properties, "direction"); double timer_start = time_to_seconds(mlt_properties_get(properties, "start")); double timer_duration = time_to_seconds(mlt_properties_get(properties, "duration")); double timer_offset = time_to_seconds(mlt_properties_get(properties, "offset")); double value = time_to_seconds( mlt_properties_frames_to_time(properties, current_frame, mlt_time_clock)); if (timer_duration <= 0.0) { // "duration" of zero means entire length of the filter. mlt_position filter_length = mlt_filter_get_length2(filter, frame) - 1; double filter_duration = time_to_seconds( mlt_properties_frames_to_time(properties, filter_length, mlt_time_clock)); timer_duration = (filter_duration - timer_start) * filter_speed; } if (value < timer_start * filter_speed) { // Hold at 0 until start time. value = 0.0; } else { value = value - timer_start * filter_speed; if (value > timer_duration) { // Hold at duration after the timer has elapsed. value = timer_duration; } } // Apply direction. if (direction && !strcmp(direction, "down")) { value = timer_duration - value; } // Apply offset value += timer_offset; int hours = value / (60 * 60); int mins = (value / 60) - (hours * 60); double secs = value - (double) (mins * 60) - (double) (hours * 60 * 60); char *format = mlt_properties_get(properties, "format"); if (!strcmp(format, "HH:MM:SS")) { snprintf(text, MAX_TEXT_LEN, "%02d:%02d:%02d", hours, mins, (int) floor(secs)); } else if (!strcmp(format, "HH:MM:SS.S")) { snprintf(text, MAX_TEXT_LEN, "%02d:%02d:%04.1f", hours, mins, floor(secs * 10.0) / 10.0); } else if (!strcmp(format, "MM:SS")) { snprintf(text, MAX_TEXT_LEN, "%02d:%02d", hours * 60 + mins, (int) floor(secs)); } else if (!strcmp(format, "MM:SS.SS")) { snprintf(text, MAX_TEXT_LEN, "%02d:%05.2f", hours * 60 + mins, floor(secs * 100.0) / 100.0); } else if (!strcmp(format, "MM:SS.SSS")) { snprintf(text, MAX_TEXT_LEN, "%02d:%06.3f", hours * 60 + mins, floor(secs * 1000.0) / 1000.0); } else if (!strcmp(format, "SS")) { snprintf(text, MAX_TEXT_LEN, "%02d", (int) floor(value)); } else if (!strcmp(format, "SS.S")) { snprintf(text, MAX_TEXT_LEN, "%04.1f", floor(value * 10.0) / 10.0); } else if (!strcmp(format, "SS.SS")) { snprintf(text, MAX_TEXT_LEN, "%05.2f", floor(value * 100.0) / 100.0); } else if (!strcmp(format, "SS.SSS")) { snprintf(text, MAX_TEXT_LEN, "%06.3f", floor(value * 1000.0) / 1000.0); } } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_filter text_filter = mlt_properties_get_data(properties, "_text_filter", NULL); mlt_properties text_filter_properties = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(text_filter)); char *result = calloc(1, MAX_TEXT_LEN); get_timer_str(filter, frame, result); mlt_properties_set(text_filter_properties, "argument", result); free(result); mlt_properties_pass_list(text_filter_properties, properties, "geometry family size weight style fgcolour bgcolour olcolour pad " "halign valign outline underline strikethrough opacity"); mlt_filter_set_in_and_out(text_filter, mlt_filter_get_in(filter), mlt_filter_get_out(filter)); return mlt_filter_process(text_filter, frame); } /** Constructor for the filter. */ mlt_filter filter_timer_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); mlt_filter text_filter = mlt_factory_filter(profile, "qtext", NULL); if (!text_filter) text_filter = mlt_factory_filter(profile, "text", NULL); if (!text_filter) mlt_log_warning(MLT_FILTER_SERVICE(filter), "Unable to create text filter.\n"); if (filter && text_filter) { mlt_properties my_properties = MLT_FILTER_PROPERTIES(filter); // Register the text filter for reuse/destruction mlt_properties_set_data(my_properties, "_text_filter", text_filter, 0, (mlt_destructor) mlt_filter_close, NULL); // Assign default values mlt_properties_set(my_properties, "format", "SS.SS"); mlt_properties_set(my_properties, "start", "00:00:00.000"); mlt_properties_set(my_properties, "duration", "00:10:00.000"); mlt_properties_set(my_properties, "offset", "00:00:00.000"); mlt_properties_set_double(my_properties, "speed", 1.0); mlt_properties_set(my_properties, "direction", "up"); mlt_properties_set(my_properties, "geometry", "0%/0%:100%x100%:100%"); mlt_properties_set(my_properties, "family", "Sans"); mlt_properties_set(my_properties, "size", "48"); mlt_properties_set(my_properties, "weight", "400"); mlt_properties_set(my_properties, "style", "normal"); mlt_properties_set(my_properties, "fgcolour", "0x000000ff"); mlt_properties_set(my_properties, "bgcolour", "0x00000020"); mlt_properties_set(my_properties, "olcolour", "0x00000000"); mlt_properties_set(my_properties, "pad", "0"); mlt_properties_set(my_properties, "halign", "left"); mlt_properties_set(my_properties, "valign", "top"); mlt_properties_set(my_properties, "outline", "0"); mlt_properties_set(my_properties, "underline", "0"); mlt_properties_set(my_properties, "strikethrough", "0"); mlt_properties_set_string(my_properties, "opacity", "1.0"); mlt_properties_set_int(my_properties, "_filter_private", 1); filter->process = filter_process; } else { if (filter) { mlt_filter_close(filter); } if (text_filter) { mlt_filter_close(text_filter); } filter = NULL; } return filter; } mlt-7.38.0/src/modules/plus/filter_timer.yml000664 000000 000000 00000014351 15172202314 021020 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: timer title: Timer version: 4 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Overlay a timer onto the video. The timer can count up or down. parameters: - identifier: format title: Format type: string description: > The time format of the overlaid timer text. values: - HH:MM:SS - HH:MM:SS.S - MM:SS - MM:SS.SS - MM:SS.SSS - SS - SS.S - SS.SS - SS.SSS default: SS.SS readonly: no mutable: yes widget: combo - identifier: start title: Start type: string description: > The time that the timer will start counting up or down. The text will be frozen at 00:00:00.000 from the start of the filter until the start time has elapsed. Must be in the format HH:MM:SS.SSS default: 00:00:00.000 readonly: no mutable: yes widget: text - identifier: duration title: Duration type: string description: > The maximum elapsed duration of the timer after the start time has elapsed. The text will be frozen at the duration time after the duration has elapsed. Must be in the format HH:MM:SS.SSS default: 00:00:10.000 readonly: no mutable: yes widget: text - identifier: offset title: Offset type: string description: > An offset to be added to the timer value. When the direction is "down", the timer will count down to "offset" instead of 00:00:00.000. When the direction is up, the timer will count up starting from "offset". Must be in the format HH:MM:SS.SSS default: 00:00:00.000 readonly: no mutable: yes widget: text - identifier: speed title: Speed type: float description: > Clock speed multiplier. For example, speed 10.0 makes the timer tick 10 seconds for each second of playback. Scales Duration but does not affect Start or Offset. For example: start 5s, duration 30s, offset 7s and speed 10.0 will have the timer start at playback time 00:00:05.000 with value 00:00:07, count 10 seconds per second of playback and stop at playback time 00:00:08.000 with value 00:00:37. default: 1.0 readonly: no mutable: yes - identifier: direction title: Direction type: string description: > Whether the counter should count up from 00:00:00.000 or down from the duration time. values: - up - down default: up readonly: no mutable: yes widget: combo - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo - identifier: opacity title: Opacity type: float description: Opacity of all elements - text, outline, and background readonly: no default: 1.0 minimum: 0 maximum: 1.0 mutable: yes widget: slider mlt-7.38.0/src/modules/plus/hsl.h000664 000000 000000 00000003730 15172202314 016546 0ustar00rootroot000000 000000 /* * Copyright (C) 2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include static void rgbToHsl(float r, float g, float b, float *h, float *s, float *l) { float max = MAX(MAX(r, g), b); float min = MIN(MIN(r, g), b); *l = (max + min) / 2; if (max == min) { // No color *h = *s = 0; } else { float d = max - min; *s = (*l > 0.5) ? d / (2 - max - min) : d / (max + min); if (max == r) *h = (g - b) / d + (g < b ? 6 : 0); else if (max == g) *h = (b - r) / d + 2; else // if (max == b) *h = (r - g) / d + 4; *h /= 6; } } static float hueToRgb(float p, float q, float t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1. / 6) return p + (q - p) * 6 * t; if (t < 1. / 2) return q; if (t < 2. / 3) return p + (q - p) * (2. / 3 - t) * 6; return p; } static void hslToRgb(float h, float s, float l, float *r, float *g, float *b) { if (0 == s) { // No color *r = *g = *b = l; } else { float q = l < 0.5 ? l * (1 + s) : l + s - l * s; float p = 2 * l - q; *r = hueToRgb(p, q, h + 1.0 / 3.0); *g = hueToRgb(p, q, h); *b = hueToRgb(p, q, h - 1.0 / 3.0); } } mlt-7.38.0/src/modules/plus/interp.h000664 000000 000000 00000022732 15172202314 017264 0ustar00rootroot000000 000000 //interp.c /* * Copyright (C) 2010 Marko Cebokli http://lea.hamradio.si/~s57uuu * Copyright (C) 2010-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include //#define TEST_XY_LIMITS //-------------------------------------------------------- // pointer to an interpolating function // parameters: // source image // source width // source height // X coordinate // Y coordinate // opacity // destination image // flag to overwrite alpha channel typedef int (*interpp32)(uint8_t *, int, int, float, float, float, uint8_t *, int); typedef int (*interpp64)(uint16_t *, int, int, float, float, float, uint16_t *, int); typedef enum { interp_nn, interp_bl, interp_bc, } interp_type; // nearest neighbor int interpNN_b32(uint8_t *s, int w, int h, float x, float y, float o, uint8_t *d, int is_atop) { #ifdef TEST_XY_LIMITS if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) return -1; #endif int p = (int) rintf(x) * 4 + (int) rintf(y) * 4 * w; float alpha_s = (float) s[p + 3] / 255.0f * o; float alpha_d = (float) d[3] / 255.0f; float alpha = alpha_s + alpha_d - alpha_s * alpha_d; d[3] = is_atop ? s[p + 3] : (255 * alpha); alpha = alpha_s / alpha; d[0] = d[0] * (1.0f - alpha) + s[p] * alpha; d[1] = d[1] * (1.0f - alpha) + s[p + 1] * alpha; d[2] = d[2] * (1.0f - alpha) + s[p + 2] * alpha; return 0; } int interpNN_b64(uint16_t *s, int w, int h, float x, float y, float o, uint16_t *d, int is_atop) { #ifdef TEST_XY_LIMITS if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) return -1; #endif int p = (int) rintf(x) * 4 + (int) rintf(y) * 4 * w; float alpha_s = (float) s[p + 3] / 65535.0f * o; float alpha_d = (float) d[3] / 65535.0f; float alpha = alpha_s + alpha_d - alpha_s * alpha_d; d[3] = is_atop ? s[p + 3] : (65535 * alpha); alpha = alpha_s / alpha; d[0] = d[0] * (1.0f - alpha) + s[p] * alpha; d[1] = d[1] * (1.0f - alpha) + s[p + 1] * alpha; d[2] = d[2] * (1.0f - alpha) + s[p + 2] * alpha; return 0; } // bilinear int interpBL_b32(uint8_t *s, int w, int h, float x, float y, float o, uint8_t *d, int is_atop) { int m, n, k, l, n1, l1, k1; float a, b; #ifdef TEST_XY_LIMITS if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) return -1; #endif m = (int) floorf(x); if (m + 2 > w) m = w - 2; n = (int) floorf(y); if (n + 2 > h) n = h - 2; k = n * w + m; l = (n + 1) * w + m; k1 = 4 * (k + 1); l1 = 4 * (l + 1); n1 = 4 * ((n + 1) * w + m); l = 4 * l; k = 4 * k; a = s[k + 3] + (s[k1 + 3] - s[k + 3]) * (x - (float) m); b = s[l + 3] + (s[l1 + 3] - s[n1 + 3]) * (x - (float) m); float alpha_s = a + (b - a) * (y - (float) n); float alpha_d = (float) d[3] / 255.0f; if (is_atop) d[3] = alpha_s; alpha_s = alpha_s / 255.0f * o; float alpha = alpha_s + alpha_d - alpha_s * alpha_d; if (!is_atop) d[3] = 255 * alpha; alpha = alpha_s / alpha; a = s[k] + (s[k1] - s[k]) * (x - (float) m); b = s[l] + (s[l1] - s[n1]) * (x - (float) m); d[0] = d[0] * (1.0f - alpha) + (a + (b - a) * (y - (float) n)) * alpha; a = s[k + 1] + (s[k1 + 1] - s[k + 1]) * (x - (float) m); b = s[l + 1] + (s[l1 + 1] - s[n1 + 1]) * (x - (float) m); d[1] = d[1] * (1.0f - alpha) + (a + (b - a) * (y - (float) n)) * alpha; a = s[k + 2] + (s[k1 + 2] - s[k + 2]) * (x - (float) m); b = s[l + 2] + (s[l1 + 2] - s[n1 + 2]) * (x - (float) m); d[2] = d[2] * (1.0f - alpha) + (a + (b - a) * (y - (float) n)) * alpha; return 0; } int interpBL_b64(uint16_t *s, int w, int h, float x, float y, float o, uint16_t *d, int is_atop) { int m, n, k, l, n1, l1, k1; float a, b; #ifdef TEST_XY_LIMITS if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) return -1; #endif m = (int) floorf(x); if (m + 2 > w) m = w - 2; n = (int) floorf(y); if (n + 2 > h) n = h - 2; k = n * w + m; l = (n + 1) * w + m; k1 = 4 * (k + 1); l1 = 4 * (l + 1); n1 = 4 * ((n + 1) * w + m); l = 4 * l; k = 4 * k; a = s[k + 3] + (s[k1 + 3] - s[k + 3]) * (x - (float) m); b = s[l + 3] + (s[l1 + 3] - s[n1 + 3]) * (x - (float) m); float alpha_s = a + (b - a) * (y - (float) n); float alpha_d = (float) d[3] / 65535.0f; if (is_atop) d[3] = alpha_s; alpha_s = alpha_s / 65535.0f * o; float alpha = alpha_s + alpha_d - alpha_s * alpha_d; if (!is_atop) d[3] = 65535 * alpha; alpha = alpha_s / alpha; a = s[k] + (s[k1] - s[k]) * (x - (float) m); b = s[l] + (s[l1] - s[n1]) * (x - (float) m); d[0] = d[0] * (1.0f - alpha) + (a + (b - a) * (y - (float) n)) * alpha; a = s[k + 1] + (s[k1 + 1] - s[k + 1]) * (x - (float) m); b = s[l + 1] + (s[l1 + 1] - s[n1 + 1]) * (x - (float) m); d[1] = d[1] * (1.0f - alpha) + (a + (b - a) * (y - (float) n)) * alpha; a = s[k + 2] + (s[k1 + 2] - s[k + 2]) * (x - (float) m); b = s[l + 2] + (s[l1 + 2] - s[n1 + 2]) * (x - (float) m); d[2] = d[2] * (1.0f - alpha) + (a + (b - a) * (y - (float) n)) * alpha; return 0; } // bicubic int interpBC_b32(uint8_t *s, int w, int h, float x, float y, float o, uint8_t *d, int is_atop) { int i, j, b, l, m, n; float k; float p[4], p1[4], p2[4], p3[4], p4[4]; float alpha = 1.0; #ifdef TEST_XY_LIMITS if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) return -1; #endif m = (int) ceilf(x) - 2; if (m < 0) m = 0; if ((m + 5) > w) m = w - 4; n = (int) ceilf(y) - 2; if (n < 0) n = 0; if ((n + 5) > h) n = h - 4; for (b = 3; b > -1; b--) { // first after y (four columns) for (i = 0; i < 4; i++) { l = m + (i + n) * w; p1[i] = s[4 * l + b]; p2[i] = s[4 * (l + 1) + b]; p3[i] = s[4 * (l + 2) + b]; p4[i] = s[4 * (l + 3) + b]; } for (j = 1; j < 4; j++) for (i = 3; i >= j; i--) { k = (y - i - n) / j; p1[i] = p1[i] + k * (p1[i] - p1[i - 1]); p2[i] = p2[i] + k * (p2[i] - p2[i - 1]); p3[i] = p3[i] + k * (p3[i] - p3[i - 1]); p4[i] = p4[i] + k * (p4[i] - p4[i - 1]); } // now after x p[0] = p1[3]; p[1] = p2[3]; p[2] = p3[3]; p[3] = p4[3]; for (j = 1; j < 4; j++) for (i = 3; i >= j; i--) p[i] = p[i] + (x - i - m) / j * (p[i] - p[i - 1]); if (p[3] < 0.0f) p[3] = 0.0f; if (p[3] > 255.0f) p[3] = 255.0f; if (b == 3) { float alpha_s = (float) p[3] / 255.0f * o; float alpha_d = (float) d[3] / 255.0f; alpha = alpha_s + alpha_d - alpha_s * alpha_d; d[3] = is_atop ? p[3] : (255 * alpha); alpha = alpha_s / alpha; } else { d[b] = d[b] * (1.0f - alpha) + p[3] * alpha; } } return 0; } int interpBC_b64(uint16_t *s, int w, int h, float x, float y, float o, uint16_t *d, int is_atop) { int i, j, b, l, m, n; float k; float p[4], p1[4], p2[4], p3[4], p4[4]; float alpha = 1.0; #ifdef TEST_XY_LIMITS if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) return -1; #endif m = (int) ceilf(x) - 2; if (m < 0) m = 0; if ((m + 5) > w) m = w - 4; n = (int) ceilf(y) - 2; if (n < 0) n = 0; if ((n + 5) > h) n = h - 4; for (b = 3; b > -1; b--) { // first after y (four columns) for (i = 0; i < 4; i++) { l = m + (i + n) * w; p1[i] = s[4 * l + b]; p2[i] = s[4 * (l + 1) + b]; p3[i] = s[4 * (l + 2) + b]; p4[i] = s[4 * (l + 3) + b]; } for (j = 1; j < 4; j++) for (i = 3; i >= j; i--) { k = (y - i - n) / j; p1[i] = p1[i] + k * (p1[i] - p1[i - 1]); p2[i] = p2[i] + k * (p2[i] - p2[i - 1]); p3[i] = p3[i] + k * (p3[i] - p3[i - 1]); p4[i] = p4[i] + k * (p4[i] - p4[i - 1]); } // now after x p[0] = p1[3]; p[1] = p2[3]; p[2] = p3[3]; p[3] = p4[3]; for (j = 1; j < 4; j++) for (i = 3; i >= j; i--) p[i] = p[i] + (x - i - m) / j * (p[i] - p[i - 1]); if (p[3] < 0.0f) p[3] = 0.0f; if (p[3] > 65535.0f) p[3] = 65535.0f; if (b == 3) { float alpha_s = (float) p[3] / 65535.0f * o; float alpha_d = (float) d[3] / 65535.0f; alpha = alpha_s + alpha_d - alpha_s * alpha_d; d[3] = is_atop ? p[3] : (65535 * alpha); alpha = alpha_s / alpha; } else { d[b] = d[b] * (1.0f - alpha) + p[3] * alpha; } } return 0; } mlt-7.38.0/src/modules/plus/producer_blipflash.c000664 000000 000000 00000025124 15172202314 021623 0ustar00rootroot000000 000000 /* * producer_blipflash.c -- blip/flash generating producer * Copyright (C) 2013 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /** Fill an audio buffer with 1kHz "blip" samples. */ static void fill_blip( mlt_properties producer_properties, float *buffer, int frequency, int channels, int samples) { int new_size = samples * channels * sizeof(float); int old_size = 0; float *blip = mlt_properties_get_data(producer_properties, "_blip", &old_size); if (!blip || new_size > old_size) { blip = mlt_pool_alloc(new_size); // Fill the blip buffer if (blip != NULL) { int s = 0; int c = 0; for (s = 0; s < samples; s++) { float f = 1000.0; float t = (float) s / (float) frequency; // Add 90 deg so the blip always starts at 1 for easy detection. float phase = M_PI / 2; float value = sin(2 * M_PI * f * t + phase); for (c = 0; c < channels; c++) { float *sample_ptr = ((float *) blip) + (c * samples) + s; *sample_ptr = value; } } } // Cache the audio blip to save from regenerating it with every blip. mlt_properties_set_data(producer_properties, "_blip", blip, new_size, mlt_pool_release, NULL); }; if (blip) memcpy(buffer, blip, new_size); } static int producer_get_audio(mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_producer producer = mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "_producer_blipflash", NULL); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); int size = *samples * *channels * sizeof(float); double fps = mlt_producer_get_fps(producer); int frames = mlt_frame_get_position(frame) + mlt_properties_get_int(producer_properties, "offset"); int seconds = frames / fps; // Correct the returns if necessary *format = mlt_audio_float; *frequency = *frequency <= 0 ? 48000 : *frequency; *channels = *channels <= 0 ? 2 : *channels; *samples = *samples <= 0 ? mlt_audio_calculate_frame_samples(fps, *frequency, frames) : *samples; // Allocate the buffer *buffer = mlt_pool_alloc(size); // Determine if this should be a blip or silence. frames = frames % lrint(fps); seconds = seconds % mlt_properties_get_int(producer_properties, "period"); if (seconds == 0 && frames == 0) { fill_blip(producer_properties, (float *) *buffer, *frequency, *channels, *samples); } else { // Fill silence. memset(*buffer, 0, size); } // Set the buffer for destruction mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); return 0; } /** Fill an image buffer with either white (flash) or black as requested. */ static void fill_image(mlt_properties producer_properties, char *color, uint8_t *buffer, mlt_image_format format, int width, int height) { int new_size = mlt_image_format_size(format, width, height, NULL); int old_size = 0; uint8_t *image = mlt_properties_get_data(producer_properties, color, &old_size); if (!image || new_size > old_size) { // Need to create a new cached image. image = mlt_pool_alloc(new_size); if (image != NULL) { uint8_t r, g, b; uint8_t *p = image; if (!strcmp(color, "_flash")) { r = g = b = 255; // White } else { r = g = b = 0; // Black } switch (format) { default: case mlt_image_yuv422: { int uneven = width % 2; int count = (width - uneven) / 2 + 1; uint8_t y, u, v; RGB2YUV_601_SCALED(r, g, b, y, u, v); int i = height + 1; while (--i) { int j = count; while (--j) { *p++ = y; *p++ = u; *p++ = y; *p++ = v; } if (uneven) { *p++ = y; *p++ = u; } } break; } case mlt_image_rgb: { int i = width * height + 1; while (--i) { *p++ = r; *p++ = g; *p++ = b; } break; } case mlt_image_rgba: { int i = width * height + 1; while (--i) { *p++ = r; *p++ = g; *p++ = b; *p++ = 255; // alpha } break; } } // Cache the image to save from regenerating it with every frame. mlt_properties_set_data(producer_properties, color, image, new_size, mlt_pool_release, NULL); } } if (image) memcpy(buffer, image, new_size); } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_producer producer = mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "_producer_blipflash", NULL); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); int size = 0; double fps = mlt_producer_get_fps(producer); int frames = mlt_frame_get_position(frame); int seconds = frames / fps; mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); // Correct the returns if necessary if (*format != mlt_image_yuv422 && *format != mlt_image_rgb && *format != mlt_image_rgba) *format = mlt_image_yuv422; if (*width <= 0) *width = mlt_service_profile(MLT_PRODUCER_SERVICE(producer))->width; if (*height <= 0) *height = mlt_service_profile(MLT_PRODUCER_SERVICE(producer))->height; // Allocate the buffer size = mlt_image_format_size(*format, *width, *height, NULL); *buffer = mlt_pool_alloc(size); // Determine if this should be a flash or black. frames = frames % lrint(fps); seconds = seconds % mlt_properties_get_int(producer_properties, "period"); if (seconds == 0 && frames == 0) { fill_image(producer_properties, "_flash", *buffer, *format, *width, *height); } else { fill_image(producer_properties, "_black", *buffer, *format, *width, *height); } mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); // Create the alpha channel int alpha_size = *width * *height; uint8_t *alpha = mlt_pool_alloc(alpha_size); if (alpha) memset(alpha, 255, alpha_size); // Update the frame mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); mlt_frame_set_alpha(frame, alpha, alpha_size, mlt_pool_release); mlt_properties_set_double(properties, "aspect_ratio", mlt_properties_get_double(producer_properties, "aspect_ratio")); mlt_properties_set_int(properties, "progressive", 1); mlt_properties_set_int(properties, "meta.media.width", *width); mlt_properties_set_int(properties, "meta.media.height", *height); return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL) { // Obtain properties of frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Save the producer to be used later mlt_properties_set_data(frame_properties, "_producer_blipflash", producer, 0, NULL, NULL); // Update time code on the frame mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Configure callbacks mlt_frame_push_get_image(*frame, producer_get_image); mlt_frame_push_audio(*frame, producer_get_audio); } // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer this) { this->close = NULL; mlt_producer_close(this); free(this); } /** Initialize. */ mlt_producer producer_blipflash_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Initialize the producer if (producer) { mlt_properties_set_int(producer_properties, "period", 1); mlt_properties_set_int(producer_properties, "offset", 0); // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; } return producer; } mlt-7.38.0/src/modules/plus/producer_blipflash.yml000664 000000 000000 00000001522 15172202314 022176 0ustar00rootroot000000 000000 schema_version: 0.1 type: producer identifier: blipflash title: Blip Flash version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio - Video description: > Generate periodic synchronized audio blips and video flashes. Blips are a 1kHz tone and last the duration of the flash frame. parameters: - identifier: period title: Flash Period type: integer description: > The period between flashes in seconds. default: 1 readonly: no mutable: yes widget: spinner - identifier: offset title: Audio Offset type: integer description: > The number of frames to offset the audio. A positive number results in audio earlier than video. An negative number results in audio later than video. default: 0 readonly: no mutable: yes widget: spinner mlt-7.38.0/src/modules/plus/producer_count.c000664 000000 000000 00000056651 15172202314 021020 0ustar00rootroot000000 000000 /* * producer_count.c -- counting producer * Copyright (C) 2013-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /* Private Constants */ #define MAX_TEXT_LEN 512 #define LINE_PIXEL_VALUE 0x00 #define RING_PIXEL_VALUE 0xff #define CLOCK_PIXEL_VALUE 0x50 #define FRAME_BACKGROUND_COLOR "0xd0d0d0ff" #define TEXT_BACKGROUND_COLOR "0x00000000" #define TEXT_FOREGROUND_COLOR "0x000000ff" // Ratio of graphic elements relative to image size #define LINE_WIDTH_RATIO 1 #define OUTER_RING_RATIO 90 #define INNER_RING_RATIO 80 #define TEXT_SIZE_RATIO 70 typedef struct { mlt_position position; int fps; int hours; int minutes; int seconds; int frames; char sep; // Either : or ; (for ndf) } time_info; static void get_time_info(mlt_producer producer, mlt_frame frame, time_info *info) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); mlt_position position = mlt_frame_original_position(frame); info->fps = ceil(mlt_producer_get_fps(producer)); char *direction = mlt_properties_get(producer_properties, "direction"); if (!strcmp(direction, "down")) { mlt_position length = mlt_properties_get_int(producer_properties, "length"); info->position = length - 1 - position; } else { info->position = position; } char *tc_str = NULL; if (mlt_properties_get_int(producer_properties, "drop")) { tc_str = mlt_properties_frames_to_time(producer_properties, info->position, mlt_time_smpte_df); } else { tc_str = mlt_properties_frames_to_time(producer_properties, info->position, mlt_time_smpte_ndf); } sscanf(tc_str, "%02d:%02d:%02d%c%d", &info->hours, &info->minutes, &info->seconds, &info->sep, &info->frames); } static inline void mix_pixel(uint8_t *image, int width, int x, int y, int value, float mix) { uint8_t *p = image + ((y * width) + x) * 4; if (mix != 1.0) { value = ((float) value * mix) + ((float) *p * (1.0 - mix)); } *p = value; p++; *p = value; p++; *p = value; } /** Fill an audio buffer with 1kHz samples. */ static void fill_beep( mlt_properties producer_properties, float *buffer, int frequency, int channels, int samples) { int s = 0; int c = 0; for (s = 0; s < samples; s++) { float f = 1000.0; float t = (float) s / (float) frequency; float value = sin(2 * M_PI * f * t); for (c = 0; c < channels; c++) { float *sample_ptr = buffer + (c * samples) + s; *sample_ptr = value; } } } static int producer_get_audio(mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_producer producer = (mlt_producer) mlt_frame_pop_audio(frame); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); char *sound = mlt_properties_get(producer_properties, "sound"); double fps = mlt_producer_get_fps(producer); mlt_position position = mlt_frame_original_position(frame); int size = 0; int do_beep = 0; time_info info; if (fps == 0) fps = 25; // Correct the returns if necessary *format = mlt_audio_float; *frequency = *frequency <= 0 ? 48000 : *frequency; *channels = *channels <= 0 ? 2 : *channels; *samples = *samples <= 0 ? mlt_audio_calculate_frame_samples(fps, *frequency, position) : *samples; // Allocate the buffer size = *samples * *channels * sizeof(float); *buffer = mlt_pool_alloc(size); mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); get_time_info(producer, frame, &info); // Determine if this should be a tone or silence. if (strcmp(sound, "none")) { if (!strcmp(sound, "2pop")) { mlt_position out = mlt_properties_get_int(producer_properties, "out"); mlt_position frames = out - position; if (frames == (info.fps * 2)) { do_beep = 1; } } else if (!strcmp(sound, "frame0")) { if (info.frames == 0) { do_beep = 1; } } } if (do_beep) { fill_beep(producer_properties, (float *) *buffer, *frequency, *channels, *samples); } else { // Fill silence. memset(*buffer, 0, size); } mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); // Set the buffer for destruction mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); return 0; } static mlt_frame get_background_frame(mlt_producer producer) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); mlt_frame bg_frame = NULL; mlt_producer color_producer = mlt_properties_get_data(producer_properties, "_color_producer", NULL); if (!color_producer) { mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); color_producer = mlt_factory_producer(profile, "loader-nogl", "colour"); mlt_properties_set_data(producer_properties, "_color_producer", color_producer, 0, (mlt_destructor) mlt_producer_close, NULL); mlt_properties color_properties = MLT_PRODUCER_PROPERTIES(color_producer); mlt_properties_set(color_properties, "colour", FRAME_BACKGROUND_COLOR); } if (color_producer) { mlt_producer_seek(color_producer, 0); mlt_service_get_frame(MLT_PRODUCER_SERVICE(color_producer), &bg_frame, 0); } return bg_frame; } static mlt_frame get_text_frame(mlt_producer producer, time_info *info) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); mlt_producer text_producer = mlt_properties_get_data(producer_properties, "_text_producer", NULL); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); mlt_frame text_frame = NULL; if (!text_producer) { text_producer = mlt_factory_producer(profile, "loader-nogl", "qtext"); // Use pango if qtext is not available. if (!text_producer) text_producer = mlt_factory_producer(profile, "loader-nogl", "pango"); if (!text_producer) mlt_log_warning(MLT_PRODUCER_SERVICE(producer), "QT or GTK modules required for count producer.\n"); // Save the producer for future use. mlt_properties_set_data(producer_properties, "_text_producer", text_producer, 0, (mlt_destructor) mlt_producer_close, NULL); // Calculate the font size. char font_size[MAX_TEXT_LEN]; snprintf(font_size, MAX_TEXT_LEN - 1, "%dpx", profile->height * TEXT_SIZE_RATIO / 100); // Configure the producer. mlt_properties text_properties = MLT_PRODUCER_PROPERTIES(text_producer); mlt_properties_set(text_properties, "size", font_size); mlt_properties_set(text_properties, "weight", "400"); mlt_properties_set(text_properties, "fgcolour", TEXT_FOREGROUND_COLOR); mlt_properties_set(text_properties, "bgcolour", TEXT_BACKGROUND_COLOR); mlt_properties_set(text_properties, "pad", "0"); mlt_properties_set(text_properties, "outline", "0"); mlt_properties_set(text_properties, "align", "center"); } if (text_producer) { mlt_properties text_properties = MLT_PRODUCER_PROPERTIES(text_producer); char *style = mlt_properties_get(producer_properties, "style"); char text[MAX_TEXT_LEN] = ""; // Apply the time style if (!strcmp(style, "frames")) { snprintf(text, MAX_TEXT_LEN - 1, MLT_POSITION_FMT, info->position); } else if (!strcmp(style, "timecode")) { snprintf(text, MAX_TEXT_LEN - 1, "%02d:%02d:%02d%c%0*d", info->hours, info->minutes, info->seconds, info->sep, (info->fps > 999 ? 4 : info->fps > 99 ? 3 : 2), info->frames); } else if (!strcmp(style, "clock")) { snprintf(text, MAX_TEXT_LEN - 1, "%.2d:%.2d:%.2d", info->hours, info->minutes, info->seconds); } else if (!strcmp(style, "seconds+1")) { snprintf(text, MAX_TEXT_LEN - 1, "%d", info->seconds + 1); } else // seconds { snprintf(text, MAX_TEXT_LEN - 1, "%d", info->seconds); } mlt_properties_set(text_properties, "text", text); // Get the frame. mlt_service_get_frame(MLT_PRODUCER_SERVICE(text_producer), &text_frame, 0); } return text_frame; } static void draw_ring(uint8_t *image, mlt_profile profile, int radius, int line_width) { float sar = mlt_profile_sar(profile); int x_center = profile->width / 2; int y_center = profile->height / 2; int max_radius = radius + line_width; int a = max_radius + 1; int b = 0; line_width += 1; // Compensate for aliasing. // Scan through each pixel in one quadrant of the circle. while (a--) { b = (max_radius / sar) + 1.0; while (b--) { // Use Pythagorean theorem to determine the distance from this pixel to the center. float a2 = a * a; float b2 = b * sar * b * sar; float c = sqrtf(a2 + b2); float distance = c - radius; if (distance > 0 && distance < line_width) { // This pixel is within the ring. float mix = 1.0; if (distance < 1.0) { // Antialias the outside of the ring mix = distance; } else if ((float) line_width - distance < 1.0) { // Antialias the inside of the ring mix = (float) line_width - distance; } // Apply this value to all 4 quadrants of the circle. mix_pixel(image, profile->width, x_center + b, y_center - a, RING_PIXEL_VALUE, mix); mix_pixel(image, profile->width, x_center - b, y_center - a, RING_PIXEL_VALUE, mix); mix_pixel(image, profile->width, x_center + b, y_center + a, RING_PIXEL_VALUE, mix); mix_pixel(image, profile->width, x_center - b, y_center + a, RING_PIXEL_VALUE, mix); } } } } static void draw_cross(uint8_t *image, mlt_profile profile, int line_width) { int x = 0; int y = 0; int i = 0; // Draw a horizontal line i = line_width; while (i--) { y = (profile->height - line_width) / 2 + i; x = profile->width - 1; while (x--) { mix_pixel(image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0); } } // Draw a vertical line line_width = lrint((float) line_width * mlt_profile_sar(profile)); i = line_width; while (i--) { x = (profile->width - line_width) / 2 + i; y = profile->height - 1; while (y--) { mix_pixel(image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0); } } } static void draw_clock(uint8_t *image, mlt_profile profile, int angle, int line_width) { float sar = mlt_profile_sar(profile); int q = 0; int x_center = profile->width / 2; int y_center = profile->height / 2; line_width += 1; // Compensate for aliasing. // Look at each quadrant of the frame to see what should be done. for (q = 1; q <= 4; q++) { int max_angle = q * 90; int x_sign = (q == 1 || q == 2) ? 1 : -1; int y_sign = (q == 1 || q == 4) ? 1 : -1; int x_start = x_center * x_sign; int y_start = y_center * y_sign; // Compensate for rounding error of even lengths // (there is no "middle" pixel so everything is offset). if (x_sign == 1 && profile->width % 2 == 0) x_start--; if (y_sign == -1 && profile->height % 2 == 0) y_start++; if (angle >= max_angle) { // This quadrant is completely behind the clock hand. Fill it in. int dx = x_start + x_sign; while (dx) { dx -= x_sign; int dy = y_start + y_sign; while (dy) { dy -= y_sign; mix_pixel(image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0); } } } else if (max_angle - angle < 90) { // This quadrant is partially filled // Calculate a point (vx,vy) that lies on the line created by the angle from 0,0. int vx = 0; int vy = y_start; float lv = 0; // Assume maximum y and calculate the corresponding x value // for a point at the other end of this line. if (x_sign * y_sign == 1) { vx = x_sign * sar * y_center / tan((max_angle - angle) * M_PI / 180.0); } else { vx = x_sign * sar * y_center * tan((max_angle - angle) * M_PI / 180.0); } // Calculate the length of the line defined by vx,vy lv = sqrtf((float) (vx * vx) * sar * sar + (float) vy * vy); // Scan through each pixel in the quadrant counting up/down to 0,0. int dx = x_start + x_sign; while (dx) { dx -= x_sign; int dy = y_start + y_sign; while (dy) { dy -= y_sign; // Calculate the cross product to determine which side of // the line this pixel lies on. int xp = vx * (vy - dy) - vy * (vx - dx); xp = xp * -1; // Easier to work with positive. Positive number means "behind" the line. if (xp > 0) { // This pixel is behind the clock hand and should be filled in. // Calculate the distance from the pixel to the line to determine // if it is part of the clock hand. float distance = (float) xp / lv; int val = CLOCK_PIXEL_VALUE; float mix = 1.0; if (distance < line_width) { // This pixel makes up the clock hand. val = LINE_PIXEL_VALUE; if (distance < 1.0) { // Antialias the outside of the clock hand mix = distance; } else if ((float) line_width - distance < 1.0) { // Antialias the inside of the clock hand mix_pixel(image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0); mix = (float) line_width - distance; } } mix_pixel(image, profile->width, x_center + dx, y_center - dy, val, mix); } } } } } } static void add_clock_to_frame(mlt_producer producer, mlt_frame frame, time_info *info) { mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); uint8_t *image = NULL; mlt_image_format format = mlt_image_rgba; int size = 0; int width = profile->width; int height = profile->height; int line_width = LINE_WIDTH_RATIO * (width > height ? height : width) / 100; int radius = (width > height ? height : width) / 2; char *direction = mlt_properties_get(producer_properties, "direction"); int clock_angle = 0; mlt_frame_get_image(frame, &image, &format, &width, &height, 1); // Calculate the angle for the clock. int frames = info->frames; if (!strcmp(direction, "down")) { frames = info->fps - info->frames - 1; } clock_angle = (frames + 1) * 360 / info->fps; draw_clock(image, profile, clock_angle, line_width); draw_cross(image, profile, line_width); draw_ring(image, profile, (radius * OUTER_RING_RATIO) / 100, line_width); draw_ring(image, profile, (radius * INNER_RING_RATIO) / 100, line_width); size = mlt_image_format_size(format, width, height, NULL); mlt_frame_set_image(frame, image, size, mlt_pool_release); } static void add_text_to_bg(mlt_producer producer, mlt_frame bg_frame, mlt_frame text_frame) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); mlt_transition transition = mlt_properties_get_data(producer_properties, "_transition", NULL); if (!transition) { mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); transition = mlt_factory_transition(profile, "composite", NULL); // Save the transition for future use. mlt_properties_set_data(producer_properties, "_transition", transition, 0, (mlt_destructor) mlt_transition_close, NULL); // Configure the transition. mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES(transition); mlt_properties_set(transition_properties, "geometry", "0%/0%:100%x100%:100"); mlt_properties_set(transition_properties, "halign", "center"); mlt_properties_set(transition_properties, "valign", "middle"); } if (transition && bg_frame && text_frame) { // Apply the transition. mlt_transition_process(transition, bg_frame, text_frame); } } static int producer_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_producer producer = mlt_frame_pop_service(frame); mlt_frame bg_frame = NULL; mlt_frame text_frame = NULL; int error = 1; int size = 0; char *background = mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "background"); time_info info; mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); get_time_info(producer, frame, &info); bg_frame = get_background_frame(producer); if (!strcmp(background, "clock")) { add_clock_to_frame(producer, bg_frame, &info); } text_frame = get_text_frame(producer, &info); add_text_to_bg(producer, bg_frame, text_frame); if (bg_frame) { // Get the image from the background frame. error = mlt_frame_get_image(bg_frame, image, format, width, height, writable); size = mlt_image_format_size(*format, *width, *height, NULL); // Detach the image from the bg_frame so it is not released. mlt_frame_set_image(bg_frame, *image, size, NULL); // Attach the image to the input frame. mlt_frame_set_image(frame, *image, size, mlt_pool_release); mlt_frame_close(bg_frame); } if (text_frame) { mlt_frame_close(text_frame); } mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); return error; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL) { // Obtain properties of frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Update time code on the frame mlt_frame_set_position(*frame, mlt_producer_frame(producer)); mlt_properties_set_int(frame_properties, "progressive", 1); mlt_properties_set_double(frame_properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(frame_properties, "meta.media.width", profile->width); mlt_properties_set_int(frame_properties, "meta.media.height", profile->height); // Inform framework that this producer creates rgba frames by default mlt_properties_set_int(frame_properties, "format", mlt_image_rgba); // Configure callbacks mlt_frame_push_service(*frame, producer); mlt_frame_push_get_image(*frame, producer_get_image); mlt_frame_push_audio(*frame, producer); mlt_frame_push_audio(*frame, producer_get_audio); } // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer this) { this->close = NULL; mlt_producer_close(this); free(this); } /** Initialize. */ mlt_producer producer_count_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); // Initialize the producer if (producer) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_properties_set(properties, "direction", "down"); mlt_properties_set(properties, "style", "seconds+1"); mlt_properties_set(properties, "sound", "none"); mlt_properties_set(properties, "background", "clock"); mlt_properties_set(properties, "drop", "0"); mlt_properties_clear(properties, "resource"); // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; } return producer; } mlt-7.38.0/src/modules/plus/producer_count.yml000664 000000 000000 00000003747 15172202314 021375 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: count title: Count version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio - Video description: > Generate frames with a counter and synchronized tone. The counter can go up or down. parameters: - identifier: direction title: Count Direction description: Whether to count up or down. type: string default: down values: - up - down mutable: yes widget: combo - identifier: style title: Counter Style description: | The style of the counter. * seconds - seconds counting up from or down to 0 * seconds+1 - seconds counting up from or down to 1 * frames - frames * timecode - timecode in the format HH:MM:SS:FF * clock - clock in the format HH:MM:SS type: string default: seconds+1 values: - seconds - seconds+1 - frames - timecode - clock mutable: yes widget: combo - identifier: sound title: Sound description: | The sound to be produced. * silent - No sound * 2pop - A 1kHz beep exactly two seconds before the out point * frame0 - A 1kHz beep at frame 0 of every second type: string default: silent values: - none - 2pop - frame0 mutable: yes widget: combo - identifier: background title: Background description: | The background style. * none - No background * clock - Film style clock animation type: string default: clock values: - none - clock mutable: yes widget: combo - identifier: drop title: Drop Frame description: | Use SMPTE style drop-frame counting for non-integer frame rates. The clock and timecode will advance two frames every minute if necessary to keep time with wall clock time mutable: yes type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-7.38.0/src/modules/plus/producer_pgm.c000664 000000 000000 00000016717 15172202314 020452 0ustar00rootroot000000 000000 /* * producer_pgm.c -- PGM producer * Copyright (C) 2008-2022 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include static int read_pgm(char *name, uint8_t **image, int *width, int *height, int *maxval); static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index); static void producer_close(mlt_producer parent); mlt_producer producer_pgm_init(mlt_profile profile, mlt_service_type type, const char *id, char *resource) { mlt_producer this = NULL; uint8_t *image = NULL; int width = 0; int height = 0; int maxval = 0; if (read_pgm(resource, &image, &width, &height, &maxval)) { if (resource && strstr(resource, "%luma")) { // Failed to read file; generate it. mlt_luma_map luma = mlt_luma_map_new(resource); if (profile) { luma->w = profile->width; luma->h = profile->height; } uint16_t *map = mlt_luma_map_render(luma); if (map) { int n = luma->w * luma->h; image = mlt_pool_alloc(n * 2); width = luma->w; height = luma->h; for (int i = 0; i < n; i++) { image[2 * i] = 16 + map[i] * 219 / USHRT_MAX; image[2 * i + 1] = 128; } mlt_pool_release(map); } free(luma); } } if (image) { this = calloc(1, sizeof(struct mlt_producer_s)); if (this != NULL && mlt_producer_init(this, NULL) == 0) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(this); this->get_frame = producer_get_frame; this->close = (mlt_destructor) producer_close; mlt_properties_set(properties, "resource", resource); mlt_properties_set_data(properties, "image", image, 0, mlt_pool_release, NULL); mlt_properties_set_int(properties, "meta.media.width", width); mlt_properties_set_int(properties, "meta.media.height", height); } else { mlt_pool_release(image); free(this); this = NULL; } } return this; } /** Load the PGM file. */ static int read_pgm(char *name, uint8_t **image, int *width, int *height, int *maxval) { uint8_t *input = NULL; int error = 0; FILE *f = mlt_fopen(name, "rb"); char data[512]; // Initialise *image = NULL; *width = 0; *height = 0; *maxval = 0; // Get the magic code if (f != NULL && fgets(data, 511, f) != NULL && data[0] == 'P' && data[1] == '5') { char *p = data + 2; int i = 0; int val = 0; // PGM Header parser (probably needs to be strengthened) for (i = 0; !error && i < 3; i++) { if (*p != '\0' && *p != '\n') val = strtol(p, &p, 10); else p = NULL; while (error == 0 && p == NULL) { if (fgets(data, 511, f) == NULL) error = 1; else if (data[0] != '#') val = strtol(data, &p, 10); } switch (i) { case 0: *width = val; break; case 1: *height = val; break; case 2: *maxval = val; break; } } if (!error) { // Determine if this is one or two bytes per pixel int bpp = *maxval > 255 ? 2 : 1; int size = *width * *height * bpp; uint8_t *p; // Allocate temporary storage for the data and the image input = mlt_pool_alloc(*width * *height * bpp); *image = mlt_pool_alloc(*width * *height * sizeof(uint8_t) * 2); p = *image; error = *image == NULL || input == NULL; if (!error) { // Read the raw data error = fread(input, *width * *height * bpp, 1, f) != 1; if (!error) { // Convert to yuv422 (very lossy - need to extend this to allow 16 bit alpha out) for (i = 0; i < size; i += bpp) { *p++ = 16 + (input[i] * 219) / 255; *p++ = 128; } } } } if (error) mlt_pool_release(*image); mlt_pool_release(input); } else { error = 1; } if (f != NULL) fclose(f); return error; } static int producer_get_image(mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { mlt_producer producer = mlt_frame_pop_service(this); int real_width = mlt_properties_get_int(MLT_FRAME_PROPERTIES(this), "meta.media.width"); int real_height = mlt_properties_get_int(MLT_FRAME_PROPERTIES(this), "meta.media.height"); int size = real_width * real_height; uint8_t *image = mlt_pool_alloc(size * 2); uint8_t *source = mlt_properties_get_data(MLT_PRODUCER_PROPERTIES(producer), "image", NULL); mlt_frame_set_image(this, image, size * 2, mlt_pool_release); *width = real_width; *height = real_height; *format = mlt_image_yuv422; *buffer = image; if (image != NULL && source != NULL) memcpy(image, source, size * 2); return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Construct a test frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); // Get the frames properties mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); // Pass the data on the frame properties mlt_properties_set_int(properties, "has_image", 1); mlt_properties_set_int(properties, "progressive", 1); mlt_properties_set_double(properties, "aspect_ratio", 1); // Inform framework that this producer creates yuv frames by default mlt_properties_set_int(properties, "format", mlt_image_yuv422); // Push the image callback mlt_frame_push_service(*frame, producer); mlt_frame_push_get_image(*frame, producer_get_image); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer parent) { parent->close = NULL; mlt_producer_close(parent); free(parent); } mlt-7.38.0/src/modules/plus/producer_pgm.yml000664 000000 000000 00000000425 15172202314 021016 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: pgm title: PGM Image version: 1 creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: resource argument: yes type: string title: File Name required: yes mutable: no mlt-7.38.0/src/modules/plus/producer_subtitle.c000664 000000 000000 00000012367 15172202314 021517 0ustar00rootroot000000 000000 /* * producer_subtitle.c * Copyright (C) 2024-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); mlt_producer color_producer = mlt_properties_get_data(producer_properties, "_c", NULL); mlt_producer_seek(color_producer, mlt_producer_position(producer)); mlt_service_get_frame(MLT_PRODUCER_SERVICE(color_producer), frame, index); if (*frame != NULL) { mlt_filter sub_filter = mlt_properties_get_data(producer_properties, "_s", NULL); if (!sub_filter) { sub_filter = mlt_factory_filter(mlt_service_profile(MLT_PRODUCER_SERVICE(producer)), "subtitle", NULL); if (!sub_filter) { mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Unable to create subtitle filter.\n"); return 0; } // Register the subtitle filter for reuse/destruction mlt_properties_set_data(producer_properties, "_s", sub_filter, 0, (mlt_destructor) mlt_filter_close, NULL); } mlt_properties_pass_list( MLT_FILTER_PROPERTIES(sub_filter), producer_properties, "resource geometry family size weight style fgcolour bgcolour " "olcolour pad halign valign outline underline strikethrough opacity"); mlt_filter_process(sub_filter, *frame); } // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer this) { this->close = NULL; mlt_producer_close(this); free(this); } mlt_producer producer_subtitle_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); mlt_producer color_producer = mlt_factory_producer(profile, "loader-nogl", "color"); // Initialize the producer if (producer && color_producer) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); if (arg) { mlt_properties_set_string(producer_properties, "resource", arg); } mlt_properties_set_string(producer_properties, "geometry", "0%/0%:100%x100%:100%"); mlt_properties_set_string(producer_properties, "family", "Sans"); mlt_properties_set_string(producer_properties, "size", "48"); mlt_properties_set_string(producer_properties, "weight", "400"); mlt_properties_set_string(producer_properties, "style", "normal"); mlt_properties_set_string(producer_properties, "fgcolour", "0xffffffff"); mlt_properties_set_string(producer_properties, "bgcolour", "0x00000020"); mlt_properties_set_string(producer_properties, "olcolour", "0x00000000"); mlt_properties_set_string(producer_properties, "pad", "0"); mlt_properties_set_string(producer_properties, "halign", "left"); mlt_properties_set_string(producer_properties, "valign", "top"); mlt_properties_set_string(producer_properties, "outline", "0"); mlt_properties_set_string(producer_properties, "underline", "0"); mlt_properties_set_string(producer_properties, "strikethrough", "0"); mlt_properties_set_string(producer_properties, "opacity", "1.0"); // Register the color producer for reuse/destruction mlt_properties_set(MLT_PRODUCER_PROPERTIES(color_producer), "resource", "0x00000000"); mlt_properties_set_data(producer_properties, "_c", color_producer, 0, (mlt_destructor) mlt_producer_close, NULL); // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; } else { if (!color_producer) mlt_log_error(MLT_PRODUCER_SERVICE(producer), "Unable to create color producer.\n"); mlt_producer_close(producer); mlt_producer_close(color_producer); producer = NULL; } return producer; } mlt-7.38.0/src/modules/plus/producer_subtitle.yml000664 000000 000000 00000010102 15172202314 022057 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: subtitle title: Subtitle version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Generate video frames with subtitle overlay parameters: - identifier: resource argument: yes title: Resource type: string description: The path to a text file that contains SRT subtitles required: no readonly: no - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 mutable: yes widget: spinner - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo - identifier: opacity title: Opacity type: float description: Opacity of all elements - text, outline, and background readonly: no default: 1.0 minimum: 0 maximum: 1.0 mutable: yes widget: slider mlt-7.38.0/src/modules/plus/subtitles/000775 000000 000000 00000000000 15172202314 017622 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/plus/subtitles/subtitles.cpp000664 000000 000000 00000016176 15172202314 022357 0ustar00rootroot000000 000000 /* * Copyright (c) 2024 Meltytech, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "subtitles.h" #include #include #include #include #include #ifdef _WIN32 #include static wchar_t *utf8ToWide(const char *strUtf8) { wchar_t *strWide = nullptr; int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, strUtf8, -1, NULL, 0); if (n > 0) { strWide = (wchar_t *) calloc(n, sizeof(wchar_t)); if (strWide) { MultiByteToWideChar(CP_UTF8, 0, strUtf8, -1, strWide, n); } } return strWide; } #endif /* ifdef _WIN32 */ static Subtitles::SubtitleVector readFromSrtStream(std::istream &stream) { enum { STATE_SEEKING_NUM, STATE_READING_TIME, STATE_READING_TEXT, }; std::string line; std::string text; int state = STATE_SEEKING_NUM; Subtitles::SubtitleItem item; Subtitles::SubtitleVector ret; while (std::getline(stream, line)) { switch (state) { case STATE_SEEKING_NUM: { state = STATE_READING_TIME; for (char &c : line) { if (!std::isdigit(c)) { // Bad line. Keep seeking state = STATE_SEEKING_NUM; break; } } break; } case STATE_READING_TIME: { int sHours, sMinutes, sSeconds, sMiliseconds, eHours, eMinutes, eSeconds, eMiliseconds; const int ret = std::sscanf(line.c_str(), "%d:%d:%d,%d --> %d:%d:%d,%d", &sHours, &sMinutes, &sSeconds, &sMiliseconds, &eHours, &eMinutes, &eSeconds, &eMiliseconds); if (ret != 8) { state = STATE_SEEKING_NUM; break; } item.start = (((((sHours * 60) + sMinutes) * 60) + sSeconds) * 1000) + sMiliseconds; item.end = (((((eHours * 60) + eMinutes) * 60) + eSeconds) * 1000) + eMiliseconds; item.text.clear(); state = STATE_READING_TEXT; break; } case STATE_READING_TEXT: { if (!line.empty()) { if (!item.text.empty()) { item.text += "\n"; } item.text += line; } else { ret.push_back(item); state = STATE_SEEKING_NUM; } break; } } } if (state == STATE_READING_TEXT) { ret.push_back(item); } return ret; } static std::string msToSrtTime(int64_t ms) { int hours = std::floor(ms / 1000.0 / 60.0 / 60.0); int minutes = std::floor((ms - (hours * 60 * 60 * 1000)) / 1000.0 / 60.0); int seconds = std::floor((ms - ((hours * 60 + minutes) * 60 * 1000)) / 1000.0); int miliseconds = ms - (((hours * 60 + minutes) * 60 + seconds) * 1000); char buff[13]; std::snprintf(buff, sizeof(buff), "%02d:%02d:%02d,%03d", hours, minutes, seconds, miliseconds); return std::string(buff); } static bool writeToSrtStream(std::ostream &stream, const Subtitles::SubtitleVector &items) { if (items.size() == 0) { return true; } int i = 1; for (auto item : items) { stream << i << "\n"; stream << msToSrtTime(item.start) << " --> " << msToSrtTime(item.end) << "\n"; stream << item.text; if (!item.text.empty() && item.text.back() != '\n') { stream << "\n"; } stream << "\n"; i++; } return true; } Subtitles::SubtitleVector Subtitles::readFromSrtFile(const std::string &path) { #ifdef _WIN32 wchar_t *wpath = utf8ToWide(path.c_str()); std::ifstream fileStream(wpath); free(wpath); #else std::ifstream fileStream(path); #endif return readFromSrtStream(fileStream); } bool Subtitles::writeToSrtFile(const std::string &path, const SubtitleVector &items) { #ifdef _WIN32 wchar_t *wpath = utf8ToWide(path.c_str()); std::ofstream fileStream(wpath, std::ios::out | std::ios::trunc); free(wpath); #else std::ofstream fileStream(path.c_str(), std::ios::out | std::ios::trunc); #endif if (!fileStream.is_open()) { return false; } return writeToSrtStream(fileStream, items); } Subtitles::SubtitleVector Subtitles::readFromSrtString(const std::string &text) { std::istringstream textStream(text); return readFromSrtStream(textStream); } bool Subtitles::writeToSrtString(std::string &text, const Subtitles::SubtitleVector &items) { std::ostringstream textStream(text); return writeToSrtStream(textStream, items); } int Subtitles::indexForTime(const Subtitles::SubtitleVector &items, int64_t msTime, int searchStart, int msMargin) { // Return -1 if there is no subtitle for the time. int index = -1; int count = (int) items.size(); if (count == 0) { // Nothing to search } else if (count > 0 && (items[0].start - msMargin) > msTime) { // No text if before the first item; } else if (count > 1 && items[count - 1].end < msTime) { // No text if after the last item; } else if (searchStart > -1 && searchStart < count && (items[searchStart].start - msMargin) <= msTime && items[searchStart].end >= msTime) { // First see if this is the same as the last subtitle index = searchStart; } else if (searchStart > -1 && (searchStart + 1) < count && items[searchStart].end < msTime && (items[searchStart + 1].start - msMargin) > msTime) { // No text if between the previous and next subtitle } else if (searchStart > -1 && (searchStart + 1) < count && (items[searchStart + 1].start - msMargin) <= msTime && items[searchStart + 1].end >= msTime) { // See if this is the next subtitle index = searchStart + 1; } else { // Perform a full search from the beginning int i = 0; for (i = 0; i < count; i++) { if ((items[i].start - msMargin) <= msTime && items[i].end >= msTime) { index = i; break; } else if (items[i].end > msTime) { break; } } } return index; } mlt-7.38.0/src/modules/plus/subtitles/subtitles.h000664 000000 000000 00000002524 15172202314 022014 0ustar00rootroot000000 000000 /* * Copyright (c) 2024 Meltytech, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SUBTITLES_H #define SUBTITLES_H #include #include #include namespace Subtitles { struct SubtitleItem { int64_t start; int64_t end; std::string text; }; typedef std::vector SubtitleVector; SubtitleVector readFromSrtFile(const std::string &path); bool writeToSrtFile(const std::string &path, const SubtitleVector &items); SubtitleVector readFromSrtString(const std::string &text); bool writeToSrtString(std::string &text, const SubtitleVector &items); int indexForTime(const SubtitleVector &items, int64_t msTime, int searchStart, int msMargin); } // namespace Subtitles #endif // SUBTITLES_H mlt-7.38.0/src/modules/plus/transition_affine.c000664 000000 000000 00000072612 15172202314 021462 0ustar00rootroot000000 000000 /* * transition_affine.c -- affine transformations * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "interp.h" #define MLT_AFFINE_MAX_DIMENSION (16000) static double alignment_parse(char *align) { int ret = 0.0; if (align == NULL) ; else if (isdigit(align[0])) ret = atoi(align); else if (align[0] == 'c' || align[0] == 'm') ret = 1.0; else if (align[0] == 'r' || align[0] == 'b') ret = 2.0; return ret; } static mlt_position repeat_position(mlt_properties properties, const char *name, mlt_position position, int length) { // Make mlt_properties parse and refresh animation. mlt_properties_anim_get_double(properties, name, position, length); mlt_animation animation = mlt_properties_get_animation(properties, name); if (animation) { // Apply repeat and mirror options. int anim_length = mlt_animation_get_length(animation); int repeat_off = mlt_properties_get_int(properties, "repeat_off"); if (!repeat_off && position >= anim_length && anim_length != 0) { int section = position / anim_length; int mirror_off = mlt_properties_get_int(properties, "mirror_off"); position -= section * anim_length; if (!mirror_off && section % 2 == 1) position = anim_length - position; } } return position; } static double anim_get_angle(mlt_properties properties, const char *name, mlt_position position, mlt_position length) { double result = 0.0; if (mlt_properties_get(properties, name)) { position = repeat_position(properties, name, position, length); result = mlt_properties_anim_get_double(properties, name, position, length); if (strchr(mlt_properties_get(properties, name), '%')) result *= 360; } return result; } typedef struct { double matrix[3][3]; } affine_t; static void affine_init(double affine[3][3]) { affine[0][0] = 1; affine[0][1] = 0; affine[0][2] = 0; affine[1][0] = 0; affine[1][1] = 1; affine[1][2] = 0; affine[2][0] = 0; affine[2][1] = 0; affine[2][2] = 1; } // Multiply two this affine transform with that static void affine_multiply(double affine[3][3], double matrix[3][3]) { double output[3][3]; int i; int j; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) output[i][j] = affine[i][0] * matrix[j][0] + affine[i][1] * matrix[j][1] + affine[i][2] * matrix[j][2]; affine[0][0] = output[0][0]; affine[0][1] = output[0][1]; affine[0][2] = output[0][2]; affine[1][0] = output[1][0]; affine[1][1] = output[1][1]; affine[1][2] = output[1][2]; affine[2][0] = output[2][0]; affine[2][1] = output[2][1]; affine[2][2] = output[2][2]; } // Rotate by a given angle static void affine_rotate_x(double affine[3][3], double angle) { double matrix[3][3]; matrix[0][0] = cos(angle * M_PI / 180); matrix[0][1] = 0 - sin(angle * M_PI / 180); matrix[0][2] = 0; matrix[1][0] = sin(angle * M_PI / 180); matrix[1][1] = cos(angle * M_PI / 180); matrix[1][2] = 0; matrix[2][0] = 0; matrix[2][1] = 0; matrix[2][2] = 1; affine_multiply(affine, matrix); } static void affine_rotate_y(double affine[3][3], double angle) { double matrix[3][3]; matrix[0][0] = cos(angle * M_PI / 180); matrix[0][1] = 0; matrix[0][2] = 0 - sin(angle * M_PI / 180); matrix[1][0] = 0; matrix[1][1] = 1; matrix[1][2] = 0; matrix[2][0] = sin(angle * M_PI / 180); matrix[2][1] = 0; matrix[2][2] = cos(angle * M_PI / 180); affine_multiply(affine, matrix); } static void affine_rotate_z(double affine[3][3], double angle) { double matrix[3][3]; matrix[0][0] = 1; matrix[0][1] = 0; matrix[0][2] = 0; matrix[1][0] = 0; matrix[1][1] = cos(angle * M_PI / 180); matrix[1][2] = sin(angle * M_PI / 180); matrix[2][0] = 0; matrix[2][1] = -sin(angle * M_PI / 180); matrix[2][2] = cos(angle * M_PI / 180); affine_multiply(affine, matrix); } static void affine_scale(double affine[3][3], double sx, double sy) { double matrix[3][3]; matrix[0][0] = sx; matrix[0][1] = 0; matrix[0][2] = 0; matrix[1][0] = 0; matrix[1][1] = sy; matrix[1][2] = 0; matrix[2][0] = 0; matrix[2][1] = 0; matrix[2][2] = 1; affine_multiply(affine, matrix); } // Shear by a given value static void affine_shear(double affine[3][3], double shear_x, double shear_y, double shear_z) { double matrix[3][3]; matrix[0][0] = 1; matrix[0][1] = tan(shear_x * M_PI / 180); matrix[0][2] = 0; matrix[1][0] = tan(shear_y * M_PI / 180); matrix[1][1] = 1; matrix[1][2] = tan(shear_z * M_PI / 180); matrix[2][0] = 0; matrix[2][1] = 0; matrix[2][2] = 1; affine_multiply(affine, matrix); } static void affine_offset(double affine[3][3], double x, double y) { affine[0][2] += x; affine[1][2] += y; } // Obtain the mapped x coordinate of the input static inline double MapX(double affine[3][3], double x, double y) { return affine[0][0] * x + affine[0][1] * y + affine[0][2]; } // Obtain the mapped y coordinate of the input static inline double MapY(double affine[3][3], double x, double y) { return affine[1][0] * x + affine[1][1] * y + affine[1][2]; } static inline double MapZ(double affine[3][3], double x, double y) { return affine[2][0] * x + affine[2][1] * y + affine[2][2]; } static void affine_max_output( double affine[3][3], double *w, double *h, double dz, double max_width, double max_height) { int tlx = MapX(affine, -max_width, max_height) / dz; int tly = MapY(affine, -max_width, max_height) / dz; int trx = MapX(affine, max_width, max_height) / dz; int try = MapY(affine, max_width, max_height) / dz; int blx = MapX(affine, -max_width, -max_height) / dz; int bly = MapY(affine, -max_width, -max_height) / dz; int brx = MapX(affine, max_width, -max_height) / dz; int bry = MapY(affine, max_width, -max_height) / dz; int max_x; int max_y; int min_x; int min_y; max_x = MAX(tlx, trx); max_x = MAX(max_x, blx); max_x = MAX(max_x, brx); min_x = MIN(tlx, trx); min_x = MIN(min_x, blx); min_x = MIN(min_x, brx); max_y = MAX(tly, try); max_y = MAX(max_y, bly); max_y = MAX(max_y, bry); min_y = MIN(tly, try); min_y = MIN(min_y, bly); min_y = MIN(min_y, bry); *w = (double) (max_x - min_x + 1) / max_width / 2.0; *h = (double) (max_y - min_y + 1) / max_height / 2.0; } #define IN_RANGE(v, r) (v >= -r / 2 && v < r / 2) static inline void get_affine(affine_t *affine, mlt_transition transition, double position, int length, double scale_width, double scale_height) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); int keyed = mlt_properties_get_int(properties, "keyed"); if (keyed == 0) { double fix_rotate_x = anim_get_angle(properties, "fix_rotate_x", position, length); double fix_rotate_y = anim_get_angle(properties, "fix_rotate_y", position, length); double fix_rotate_z = anim_get_angle(properties, "fix_rotate_z", position, length); double rotate_x = mlt_properties_get_double(properties, "rotate_x"); double rotate_y = mlt_properties_get_double(properties, "rotate_y"); double rotate_z = mlt_properties_get_double(properties, "rotate_z"); double fix_shear_x = anim_get_angle(properties, "fix_shear_x", position, length); double fix_shear_y = anim_get_angle(properties, "fix_shear_y", position, length); double fix_shear_z = anim_get_angle(properties, "fix_shear_z", position, length); double shear_x = mlt_properties_get_double(properties, "shear_x"); double shear_y = mlt_properties_get_double(properties, "shear_y"); double shear_z = mlt_properties_get_double(properties, "shear_z"); double ox = mlt_properties_anim_get_double(properties, "ox", position, length); double oy = mlt_properties_anim_get_double(properties, "oy", position, length); affine_rotate_x(affine->matrix, fix_rotate_x + rotate_x * position); affine_rotate_y(affine->matrix, fix_rotate_y + rotate_y * position); affine_rotate_z(affine->matrix, fix_rotate_z + rotate_z * position); affine_shear(affine->matrix, fix_shear_x + shear_x * position, fix_shear_y + shear_y * position, fix_shear_z + shear_z * position); affine_offset(affine->matrix, ox * scale_width, oy * scale_height); } else { double rotate_x = anim_get_angle(properties, "rotate_x", position, length); double rotate_y = anim_get_angle(properties, "rotate_y", position, length); double rotate_z = anim_get_angle(properties, "rotate_z", position, length); double shear_x = anim_get_angle(properties, "shear_x", position, length); double shear_y = anim_get_angle(properties, "shear_y", position, length); double shear_z = anim_get_angle(properties, "shear_z", position, length); double o_x = mlt_properties_anim_get_double(properties, "ox", repeat_position(properties, "ox", position, length), length); double o_y = mlt_properties_anim_get_double(properties, "oy", repeat_position(properties, "oy", position, length), length); affine_rotate_x(affine->matrix, rotate_x); affine_rotate_y(affine->matrix, rotate_y); affine_rotate_z(affine->matrix, rotate_z); affine_shear(affine->matrix, shear_x, shear_y, shear_z); affine_offset(affine->matrix, o_x * scale_width, o_y * scale_height); } } struct sliced_desc { void *a_image, *b_image; interp_type type; affine_t affine; int a_width, a_height, b_width, b_height; double lower_x, lower_y; double dz, mix; double x_offset, y_offset; int b_alpha; double minima, xmax, ymax; mlt_image_format format; }; static int sliced_proc(int id, int index, int jobs, void *cookie) { (void) id; // unused struct sliced_desc ctx = *((struct sliced_desc *) cookie); int starty, height_slice = mlt_slices_size_slice(jobs, index, ctx.a_height, &starty); double x, y; double dx, dy; int i, j; if (ctx.format == mlt_image_rgba) { uint8_t *a_image = (uint8_t *) ctx.a_image + starty * (ctx.a_width * 4); uint8_t *b_image = (uint8_t *) ctx.b_image; interpp32 interp = interpNN_b32; if (ctx.type == interp_bl) { interp = interpBL_b32; } else if (ctx.type == interp_bc) { interp = interpBC_b32; } for (i = 0, y = ctx.lower_y; i < ctx.a_height; i++, y++) { if (i >= starty && i < (starty + height_slice)) { for (j = 0, x = ctx.lower_x; j < ctx.a_width; j++, x++) { dx = MapX(ctx.affine.matrix, x, y) / ctx.dz + ctx.x_offset; dy = MapY(ctx.affine.matrix, x, y) / ctx.dz + ctx.y_offset; if (dx >= ctx.minima && dx <= ctx.xmax && dy >= ctx.minima && dy <= ctx.ymax) interp(b_image, ctx.b_width, ctx.b_height, dx, dy, ctx.mix, a_image, ctx.b_alpha); a_image += 4; } } } } else if (ctx.format == mlt_image_rgba64) { uint16_t *a_image = (uint16_t *) ctx.a_image + starty * (ctx.a_width * 4); uint16_t *b_image = (uint16_t *) ctx.b_image; interpp64 interp = interpNN_b64; if (ctx.type == interp_bl) { interp = interpBL_b64; } else if (ctx.type == interp_bc) { interp = interpBC_b64; } for (i = 0, y = ctx.lower_y; i < ctx.a_height; i++, y++) { if (i >= starty && i < (starty + height_slice)) { for (j = 0, x = ctx.lower_x; j < ctx.a_width; j++, x++) { dx = MapX(ctx.affine.matrix, x, y) / ctx.dz + ctx.x_offset; dy = MapY(ctx.affine.matrix, x, y) / ctx.dz + ctx.y_offset; if (dx >= ctx.minima && dx <= ctx.xmax && dy >= ctx.minima && dy <= ctx.ymax) interp(b_image, ctx.b_width, ctx.b_height, dx, dy, ctx.mix, a_image, ctx.b_alpha); a_image += 4; } } } } else { mlt_log_error(NULL, "[transition affine] Invalid image format\n"); } return 0; } /** Get the image. */ static int transition_get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the b frame from the stack mlt_frame b_frame = mlt_frame_pop_frame(a_frame); // Get the transition object mlt_transition transition = mlt_frame_pop_service(a_frame); // Get the properties of the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); // Get the properties of the a frame mlt_properties a_props = MLT_FRAME_PROPERTIES(a_frame); // Get the properties of the b frame mlt_properties b_props = MLT_FRAME_PROPERTIES(b_frame); if (*format != mlt_image_rgba64) *format = mlt_image_rgba; // Image, format, width, height and image for the b frame uint8_t *b_image = NULL; mlt_image_format b_format = *format; int b_width = mlt_properties_get_int(b_props, "meta.media.width"); int b_height = mlt_properties_get_int(b_props, "meta.media.height"); double b_ar = mlt_frame_get_aspect_ratio(b_frame); double b_dar = b_ar * b_width / b_height; // Assign the current position mlt_position position = mlt_transition_get_position(transition, a_frame); int mirror = mlt_properties_get_position(properties, "mirror"); int length = mlt_transition_get_length(transition); if (mlt_properties_get_int(properties, "always_active")) { mlt_properties props = mlt_properties_get_data(b_props, "_producer", NULL); mlt_position in = mlt_properties_get_int(props, "in"); mlt_position out = mlt_properties_get_int(props, "out"); length = out - in + 1; } // Obtain the normalized width and height from the a_frame mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(transition)); int normalized_width = profile->width; int normalized_height = profile->height; double consumer_ar = mlt_profile_sar(profile); if (mirror && position > length / 2) position = abs(position - length); // Preview scaling is not working correctly when offsets are active. I have // not figured out the math; so, disable preview scaling for now. double ox = mlt_properties_anim_get_double(properties, "ox", position, length); double oy = mlt_properties_anim_get_double(properties, "oy", position, length); if (ox != 0.0 || oy != 0.0) { *width = normalized_width; *height = normalized_height; } // Fetch the a frame image int error = mlt_frame_get_image(a_frame, image, format, width, height, 1); if (error || !image) return error; // Calculate the region now double scale_width = mlt_profile_scale_width(profile, *width); double scale_height = mlt_profile_scale_height(profile, *height); mlt_rect result = {0, 0, normalized_width, normalized_height, 1.0}; mlt_service_lock(MLT_TRANSITION_SERVICE(transition)); if (mlt_properties_get(properties, "rect")) { // Determine length and obtain cycle double cycle = mlt_properties_get_double(properties, "cycle"); // Allow a repeat cycle if (cycle >= 1) length = cycle; else if (cycle > 0) length *= cycle; mlt_position anim_pos = repeat_position(properties, "rect", position, length); result = mlt_properties_anim_get_rect(properties, "rect", anim_pos, length); if (mlt_properties_get(properties, "rect") && strchr(mlt_properties_get(properties, "rect"), '%')) { result.x *= normalized_width; result.y *= normalized_height; result.w *= normalized_width; result.h *= normalized_height; } result.o = (result.o == DBL_MIN) ? 1.0 : MIN(result.o, 1.0); } int threads = mlt_properties_get_int(properties, "threads"); threads = CLAMP(threads, 0, mlt_slices_count_normal()); if (threads == 1) mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); result.x *= scale_width; result.y *= scale_height; result.w *= scale_width; result.h *= scale_height; double geometry_w = result.w; double geometry_h = result.h; int fill = mlt_properties_get_int(properties, "fill"); int distort = mlt_properties_get_int(properties, "distort"); if (!fill) { double geometry_dar = result.w * consumer_ar / result.h; if (b_dar > geometry_dar) { result.w = MIN(result.w, b_width * b_ar / consumer_ar); result.h = result.w * consumer_ar / b_dar; } else { result.h = MIN(result.h, b_height); result.w = result.h * b_dar / consumer_ar; } } // Fetch the b frame image if (scale_width != 1.0 || scale_height != 1.0) { // Scale request of b frame image to consumer scale maintaining its aspect ratio. b_height = CLAMP(*height, 1, MLT_AFFINE_MAX_DIMENSION); b_width = MAX(b_height * b_dar / b_ar, 1); if (b_width > MLT_AFFINE_MAX_DIMENSION) { b_width = CLAMP(*width, 1, MLT_AFFINE_MAX_DIMENSION); b_height = MAX(b_width * b_ar / b_dar, 1); } // Set the rescale interpolation to match the frame mlt_properties_set(b_props, "consumer.rescale", mlt_properties_get(a_props, "consumer.rescale")); // Disable padding (resize filter) mlt_properties_set_int(b_props, "distort", 1); } else if (mlt_properties_get_int(b_props, "always_scale") || (!mlt_properties_get_int(b_props, "interpolation_not_required") && (fill || distort || b_width > result.w || b_height > result.h || mlt_properties_get_int(properties, "b_scaled")))) { // Request b frame image scaled to what is needed. b_height = CLAMP(result.h, 1, MLT_AFFINE_MAX_DIMENSION); b_width = MAX(b_height * b_dar / b_ar, 1); if (b_width > MLT_AFFINE_MAX_DIMENSION) { b_width = CLAMP(result.w, 1, MLT_AFFINE_MAX_DIMENSION); b_height = MAX(b_width * b_ar / b_dar, 1); } // Set the rescale interpolation to match the frame mlt_properties_set(b_props, "consumer.rescale", mlt_properties_get(a_props, "consumer.rescale")); // Disable padding (resize filter) mlt_properties_set_int(b_props, "distort", 1); } else { // Request at resolution of b frame image. This only happens when not using fill or distort mode // and the image is smaller than the rect with the intention to prevent scaling of the // image and merely position and possibly transform. mlt_properties_set_int(b_props, "rescale_width", b_width); mlt_properties_set_int(b_props, "rescale_height", b_height); const char *b_resource = mlt_properties_get(MLT_PRODUCER_PROPERTIES( mlt_frame_get_original_producer(b_frame)), "resource"); // Check if we are applied as a filter inside a transition if (b_resource && !strcmp("", b_resource)) { // Set the rescale interpolation to match the frame mlt_properties_set(b_props, "consumer.rescale", mlt_properties_get(a_props, "consumer.rescale")); } else { // Suppress padding and aspect normalization. mlt_properties_set(b_props, "consumer.rescale", "none"); } } mlt_log_debug(MLT_TRANSITION_SERVICE(transition), "requesting image B at resolution %dx%d\n", b_width, b_height); // This is not a field-aware transform. mlt_properties_set_int(b_props, "consumer.progressive", 1); error = mlt_frame_get_image(b_frame, &b_image, &b_format, &b_width, &b_height, 0); if (error || !b_image) { // Remove potentially large image on the B frame. mlt_frame_set_image(b_frame, NULL, 0, NULL); if (threads != 1) mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); return error; } // Check that both images are of the correct format and process if ((*format == mlt_image_rgba || *format == mlt_image_rgba64) && (b_format == mlt_image_rgba || b_format == mlt_image_rgba64)) { double sw, sh; // Get values from the transition double scale_x = mlt_properties_anim_get_double(properties, "scale_x", position, length); double scale_y = mlt_properties_anim_get_double(properties, "scale_y", position, length); int scale = mlt_properties_get_int(properties, "scale"); double geom_scale_x = (double) b_width / result.w; double geom_scale_y = (double) b_height / result.h; struct sliced_desc desc = {.a_image = *image, .b_image = b_image, .type = interp_bl, .a_width = *width, .a_height = *height, .b_width = b_width, .b_height = b_height, .lower_x = -(result.x + result.w / 2.0), // center .lower_y = -(result.y + result.h / 2.0), // middle .mix = result.o, .x_offset = (double) b_width / 2.0, .y_offset = (double) b_height / 2.0, .b_alpha = mlt_properties_get_int(properties, "b_alpha"), // Affine boundaries .minima = 0, .xmax = b_width - 1, .ymax = b_height - 1, .format = *format}; // Recalculate vars if alignment supplied. if (mlt_properties_get(properties, "halign") || mlt_properties_get(properties, "valign")) { double halign = alignment_parse(mlt_properties_get(properties, "halign")); double valign = alignment_parse(mlt_properties_get(properties, "valign")); desc.x_offset = halign * b_width / 2.0; desc.y_offset = valign * b_height / 2.0; desc.lower_x = -(result.x + geometry_w * halign / 2.0f); desc.lower_y = -(result.y + geometry_h * valign / 2.0f); } affine_init(desc.affine.matrix); // Compute the affine transform get_affine(&desc.affine, transition, (double) position, length, scale_width, scale_height); desc.dz = MapZ(desc.affine.matrix, 0, 0); if ((int) fabs(desc.dz * 1000) < 25) { if (threads != 1) mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); return 0; } if (mlt_properties_get_int(properties, "invert_scale")) { scale_x = 1.0 / scale_x; scale_y = 1.0 / scale_y; } // Factor scaling into the transformation based on output resolution. if (distort) { scale_x = geom_scale_x * (scale_x == 0 ? 1 : scale_x); scale_y = geom_scale_y * (scale_y == 0 ? 1 : scale_y); } else { // Determine scale with respect to aspect ratio. double consumer_dar = consumer_ar * normalized_width / normalized_height; if (b_dar > consumer_dar) { scale_x = geom_scale_x * (scale_x == 0 ? 1 : scale_x); scale_y = geom_scale_x * (scale_y == 0 ? 1 : scale_y); scale_y *= b_ar / consumer_ar; } else { scale_x = geom_scale_y * (scale_x == 0 ? 1 : scale_x); scale_y = geom_scale_y * (scale_y == 0 ? 1 : scale_y); scale_x *= consumer_ar / b_ar; } } if (scale) { affine_max_output(desc.affine.matrix, &sw, &sh, desc.dz, *width, *height); affine_scale(desc.affine.matrix, sw * MIN(geom_scale_x, geom_scale_y), sh * MIN(geom_scale_x, geom_scale_y)); } else if (scale_x != 0 && scale_y != 0) { affine_scale(desc.affine.matrix, scale_x, scale_y); } char *interps = mlt_properties_get(a_props, "consumer.rescale"); // Copy in case string is changed. if (interps) interps = strdup(interps); // Set the interpolation function if (interps == NULL || strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0 || strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0) { desc.type = interp_nn; // uses lrintf. Values should be >= -0.5 and < max + 0.5 desc.minima -= 0.5; desc.xmax += 0.49; desc.ymax += 0.49; } else if (strcmp(interps, "bilinear") == 0) { desc.type = interp_bl; // uses floorf. } else if (strcmp(interps, "bicubic") == 0 || strcmp(interps, "hyper") == 0 || strcmp(interps, "sinc") == 0 || strcmp(interps, "lanczos") == 0 || strcmp(interps, "spline") == 0) { // TODO: lanczos 8x8 // TODO: spline 4x4 or 6x6 desc.type = interp_bc; // uses ceilf. Values should be > -1 and <= max. desc.minima -= 1; } free(interps); // Do the transform with interpolation if (threads == 1) sliced_proc(0, 0, 1, &desc); else mlt_slices_run_normal(threads, sliced_proc, &desc); // Remove potentially large image on the B frame. mlt_frame_set_image(b_frame, NULL, 0, NULL); } if (threads != 1) mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); return 0; } /** Affine transition processing. */ static mlt_frame transition_process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { // Push the transition on to the frame mlt_frame_push_service(a_frame, transition); // Push the b_frame on to the stack mlt_frame_push_frame(a_frame, b_frame); // Push the transition method mlt_frame_push_get_image(a_frame, transition_get_image); return a_frame; } /** Constructor for the filter. */ mlt_transition transition_affine_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_transition transition = mlt_transition_new(); if (transition != NULL) { mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "distort", 0); mlt_properties_set(MLT_TRANSITION_PROPERTIES(transition), "rect", arg ? arg : "0%/0%:100%x100%:100%"); // Inform apps and framework that this is a video only transition mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "_transition_type", 1); mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(transition), "fill", 1); transition->process = transition_process; } return transition; } mlt-7.38.0/src/modules/plus/transition_affine.yml000664 000000 000000 00000017412 15172202314 022036 0ustar00rootroot000000 000000 schema_version: 7.0 type: transition identifier: affine title: Transform version: 8 copyright: Meltytech, LLC creator: Charles Yates contributor: - Dan Dennedy license: LGPLv2.1 language: en tags: - Video parameters: - identifier: distort title: Ignore aspect ratio description: > Determines whether the image aspect ratio will be distorted while scaling to completely fill the rectangle. type: boolean default: 0 mutable: yes widget: checkbox - identifier: fill title: Fill rectangle description: > Determines whether the image will be scaled up to fill the rectangle. Otherwise, if the B frame image fits within the rectangle, it will not be scaled. If 0, and the B frame image exceeds the rectangle, then it is scaled down to fit within the rectangle. type: boolean default: 1 mutable: yes widget: checkbox - identifier: repeat_off title: Disable looping description: > When animating properties with keyframes, whether to repeat the animation after it reaches the last key frame. type: boolean default: 0 mutable: yes widget: checkbox - identifier: mirror_off title: Disable ping-pong description: > When animating properties with keyframes and repeat_off=0, whether the animation alternates between reverses and forwards for each repetition. type: boolean default: 0 mutable: yes widget: checkbox - identifier: cycle title: Period description: > The duration to use when interpreting key frames for animation. If 0, the default, the transition length is used. If in range (0, 1), a percentage of transition length; otherwise, the number of frames. type: float default: 0 mutable: yes - identifier: keyed title: Key-framed description: Whether rotate, shear, and offset are key-framed or not. type: boolean default: 0 mutable: yes widget: checkbox - identifier: ox title: Horizontal offset type: float minimum: 0 default: 0 mutable: yes animation: yes unit: pixels - identifier: oy title: Vertical offset type: float minimum: 0 default: 0 mutable: yes animation: yes unit: pixels - identifier: rotate_x title: Rotate on X axis description: > Animate rotation around the X axis. If keyed=0, the amount to rotate per frame. type: float unit: degrees default: 0 mutable: yes - identifier: rotate_y title: Rotate on Y axis description: > Animate rotation around the Y axis. If keyed=0, the amount to rotate per frame. type: float unit: degrees default: 0 mutable: yes - identifier: rotate_z title: Rotate on Z axis description: > Animate rotation around the Z axis. If keyed=0, the amount to rotate per frame. type: float unit: degrees default: 0 mutable: yes - identifier: fix_rotate_x title: X axis rotation description: Fixed amount of rotation around the X axis. type: float unit: degrees default: 0 mutable: yes animation: yes - identifier: fix_rotate_y title: Y axis rotation description: Fixed amount of rotation around the Y axis. type: float unit: degrees default: 0 mutable: yes animation: yes - identifier: fix_rotate_z title: Z axis rotation description: Fixed amount of rotation around the Z axis. type: float unit: degrees default: 0 mutable: yes animation: yes - identifier: shear_x title: Shear along X axis description: > Animate shear along the X axis. If keyed=0, the shear angle increment per frame. type: float unit: degrees default: 0 mutable: yes - identifier: shear_y title: Shear along Y axis description: > Animate shear along the Y axis. If keyed=0, the shear angle increment per frame. type: float unit: degrees default: 0 mutable: yes - identifier: shear_z title: Shear along Z axis description: > Animate shear along the Z axis. If keyed=0, the shear angle increment per frame. type: float unit: degrees default: 0 mutable: yes - identifier: fix_shear_x title: X axis shear description: Fixed amount of shear along the X axis. type: float unit: degrees default: 0 mutable: yes animation: yes - identifier: fix_shear_y title: Y axis shear description: Fixed amount of shear along the Y axis. type: float unit: degrees default: 0 mutable: yes animation: yes - identifier: fix_shear_z title: Z axis shear description: Fixed amount of shear along the Z axis. type: float unit: degrees default: 0 mutable: yes animation: yes - identifier: mirror title: Ping-pong description: > When animating properties with key frames, whether the animation should behave with a ping-pong effect once over the duration of the transition. It will run in the forward direction over the first half the transition and in the reverse direction over the second half. type: boolean - identifier: scale title: Scale description: > Whether to automatic upscale B frame image to ensure the rectangle is filled. type: boolean default: 0 mutable: yes widget: checkbox - identifier: scale_x title: Horizontal scale description: A scale factor applied along the X axis. type: float default: 0 mutable: yes animation: yes - identifier: scale_y title: Vertical scale description: A scale factor applied along the Y axis. type: float default: 0 mutable: yes animation: yes - identifier: invert_scale title: Invert Scale description: > Whether to invert the scale_x and scale_y values. This is helpful to make animation interpolation sane because otherwise the scale values do not animate linearly. type: boolean default: 0 mutable: yes widget: checkbox - identifier: b_alpha title: Affect alpha channel description: > Whether to use the B frame's alpha channel in transformations for the output, The affine filter sets this to 1 by default. Basically, this tells the blend function to use the Porter-Duff atop mode instead of the default over. type: boolean default: 0 mutable: yes - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the rectangle. type: string default: left values: - left - center - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo - identifier: threads title: Thread count description: > Use 0 to use the slice count, which defaults to the number of detected CPUs. Otherwise, set the number of threads to use up to the slice count. minimum: 0 default: 0 - identifier: rect argument: yes title: Rectangle description: > This specifies the size and position of the image. The format of this is "X/Y:WxH[:opacity]" and can be animated with key frames. If you use percentages you must use them for every field in the above format. For example, you cannot mix and match usage of absolute coordinates and percentages for size and opacity. type: rect default: "0%/0%:100%x100%:100%" readonly: no mutable: yes animation: yes - identifier: b_scaled title: Do not use full resolution of B frame type: boolean default: 0 mutable: yes mlt-7.38.0/src/modules/plusgpl/000775 000000 000000 00000000000 15172202314 016307 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/plusgpl/CMakeLists.txt000664 000000 000000 00000002322 15172202314 021046 0ustar00rootroot000000 000000 add_library(mltplusgpl MODULE cJSON.c cJSON.h consumer_cbrts.c factory.c filter_burn.c filter_lumaliftgaingamma.c filter_outline.cpp filter_rotoscoping.c filter_telecide.c image.c utils.c utils.h ) file(GLOB YML "*.yml") add_custom_target(Other_plsugpl_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltplusgpl) target_compile_options(mltplusgpl PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltplusgpl PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltplusgpl PRIVATE mlt mlt++ Threads::Threads) if(NOT MSVC) target_link_libraries(mltplusgpl PRIVATE m) endif() if(WIN32) target_link_libraries(mltplusgpl PRIVATE ws2_32) elseif(UNIX AND NOT APPLE AND NOT ANDROID AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") target_link_libraries(mltplusgpl PRIVATE rt) endif() set_target_properties(mltplusgpl PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltplusgpl LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_cbrts.yml filter_burningtv.yml filter_lumaliftgaingamma.yml filter_outline.yml filter_rotoscoping.yml filter_telecide.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/plusgpl ) mlt-7.38.0/src/modules/plusgpl/cJSON.c000664 000000 000000 00000044277 15172202314 017405 0ustar00rootroot000000 000000 /* Copyright (c) 2009 Dave Gamble Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // cJSON // JSON parser in C. #include #include #include #include #include #include #include #include "cJSON.h" static int cJSON_strcasecmp(const char *s1,const char *s2) { if (!s1) return (s1==s2)?0:1; if (!s2) return 1; for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); } static void *(*cJSON_malloc)(size_t sz) = malloc; static void (*cJSON_free)(void *ptr) = free; static char* cJSON_strdup(const char* str) { size_t len; char* copy; len = strlen(str) + 1; if (!(copy = (char*)cJSON_malloc(len))) return 0; memcpy(copy,str,len); return copy; } void cJSON_InitHooks(cJSON_Hooks* hooks) { if (!hooks) { /* Reset hooks */ cJSON_malloc = malloc; cJSON_free = free; return; } cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; cJSON_free = (hooks->free_fn)?hooks->free_fn:free; } // Internal constructor. static cJSON *cJSON_New_Item() { cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); if (node) memset(node,0,sizeof(cJSON)); return node; } // Delete a cJSON structure. void cJSON_Delete(cJSON *c) { cJSON *next; while (c) { next=c->next; if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); if (c->string) cJSON_free(c->string); cJSON_free(c); c=next; } } // Parse the input text to generate a number, and populate the result into item. static const char *parse_number(cJSON *item,const char *num) { double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; // Could use sscanf for this? if (*num=='-') sign=-1,num++; // Has sign? if (*num=='0') num++; // is zero if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); // Number? if (*num=='.') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} // Fractional part? if (*num=='e' || *num=='E') // Exponent? { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; // With sign? while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); // Number? } n=sign*n*pow(10.0,(scale+subscale*signsubscale)); // number = +/- number.fraction * 10^+/- exponent item->valuedouble=n; item->valueint=(int)n; item->type=cJSON_Number; return num; } // Render the number nicely from the given item into a string. static char *print_number(cJSON *item) { char *str; double d=item->valuedouble; if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) { str=(char*)cJSON_malloc(21); // 2^64+1 can be represented in 21 chars. if (str) sprintf(str,"%d",item->valueint); } else { str=(char*)cJSON_malloc(64); // This is a nice tradeoff. if (str) { if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); else sprintf(str,"%f",d); } } return str; } // Parse the input text into an unescaped cstring, and populate item. static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; static const char *parse_string(cJSON *item,const char *str) { const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc; if (*str!='\"') return 0; // not a string! while (*ptr!='\"' && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++; // Skip escaped quotes. out=(char*)cJSON_malloc(len+1); // This is how long we need for the string, roughly. if (!out) return 0; ptr=str+1;ptr2=out; while (*ptr!='\"' && (unsigned char)*ptr>31) { if (*ptr!='\\') *ptr2++=*ptr++; else { ptr++; switch (*ptr) { case 'b': *ptr2++='\b'; break; case 'f': *ptr2++='\f'; break; case 'n': *ptr2++='\n'; break; case 'r': *ptr2++='\r'; break; case 't': *ptr2++='\t'; break; case 'u': // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY. sscanf(ptr+1,"%4x",&uc); // get the unicode char. len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len; switch (len) { case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 1: *--ptr2 =(uc | firstByteMark[len]); } ptr2+=len;ptr+=4; break; default: *ptr2++=*ptr; break; } ptr++; } } *ptr2=0; if (*ptr=='\"') ptr++; item->valuestring=out; item->type=cJSON_String; return ptr; } // Render the cstring provided to an escaped version that can be printed. static char *print_string_ptr(const char *str) { const char *ptr;char *ptr2,*out;int len=0; if (!str) return cJSON_strdup(""); ptr=str;while (*ptr && ++len) {if ((unsigned char)*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;} out=(char*)cJSON_malloc(len+3); if (!out) return 0; ptr2=out;ptr=str; *ptr2++='\"'; while (*ptr) { if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; else { *ptr2++='\\'; switch (*ptr++) { case '\\': *ptr2++='\\'; break; case '\"': *ptr2++='\"'; break; case '\b': *ptr2++='b'; break; case '\f': *ptr2++='f'; break; case '\n': *ptr2++='n'; break; case '\r': *ptr2++='r'; break; case '\t': *ptr2++='t'; break; default: ptr2--; break; // eviscerate with prejudice. } } } *ptr2++='\"';*ptr2++=0; return out; } // Invote print_string_ptr (which is useful) on an item. static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} // Predeclare these prototypes. static const char *parse_value(cJSON *item,const char *value); static char *print_value(cJSON *item,int depth,int fmt); static const char *parse_array(cJSON *item,const char *value); static char *print_array(cJSON *item,int depth,int fmt); static const char *parse_object(cJSON *item,const char *value); static char *print_object(cJSON *item,int depth,int fmt); // Utility to jump whitespace and cr/lf static const char *skip(const char *in) {while (in && (unsigned char)*in<=32) in++; return in;} // Parse an object - create a new root, and populate. cJSON *cJSON_Parse(const char *value) { cJSON *c=cJSON_New_Item(); if (!c) return 0; /* memory fail */ if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} return c; } // Render a cJSON item/entity/structure to text. char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} // Parser core - when encountering text, process appropriately. static const char *parse_value(cJSON *item,const char *value) { if (!value) return 0; // Fail on null. if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } if (*value=='\"') { return parse_string(item,value); } if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } if (*value=='[') { return parse_array(item,value); } if (*value=='{') { return parse_object(item,value); } return 0; // failure. } // Render a value to text. static char *print_value(cJSON *item,int depth,int fmt) { char *out=0; if (!item) return 0; switch ((item->type)&255) { case cJSON_NULL: out=cJSON_strdup("null"); break; case cJSON_False: out=cJSON_strdup("false");break; case cJSON_True: out=cJSON_strdup("true"); break; case cJSON_Number: out=print_number(item);break; case cJSON_String: out=print_string(item);break; case cJSON_Array: out=print_array(item,depth,fmt);break; case cJSON_Object: out=print_object(item,depth,fmt);break; } return out; } // Build an array from input text. static const char *parse_array(cJSON *item,const char *value) { cJSON *child; if (*value!='[') return 0; // not an array! item->type=cJSON_Array; value=skip(value+1); if (*value==']') return value+1; // empty array. item->child=child=cJSON_New_Item(); if (!item->child) return 0; // memory fail value=skip(parse_value(child,skip(value))); // skip any spacing, get the value. if (!value) return 0; while (*value==',') { cJSON *new_item; if (!(new_item=cJSON_New_Item())) return 0; // memory fail child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_value(child,skip(value+1))); if (!value) return 0; // memory fail } if (*value==']') return value+1; // end of array return 0; // malformed. } // Render an array to text static char *print_array(cJSON *item,int depth,int fmt) { char **entries; char *out=0,*ptr,*ret;int len=5; cJSON *child=item->child; int numentries=0,i=0,fail=0; // How many entries in the array? while (child) numentries++,child=child->next; // Allocate an array to hold the values for each entries=(char**)cJSON_malloc(numentries*sizeof(char*)); if (!entries) return 0; memset(entries,0,numentries*sizeof(char*)); // Retrieve all the results: child=item->child; while (child && !fail) { ret=print_value(child,depth+1,fmt); entries[i++]=ret; if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; child=child->next; } // If we didn't fail, try to malloc the output string if (!fail) out=cJSON_malloc(len); // If that fails, we fail. if (!out) fail=1; // Handle failure. if (fail) { for (i=0;itype=cJSON_Object; value=skip(value+1); if (*value=='}') return value+1; // empty array. item->child=child=cJSON_New_Item(); if (!item->child) return 0; value=skip(parse_string(child,skip(value))); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') return 0; // fail! value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. if (!value) return 0; while (*value==',') { cJSON *new_item; if (!(new_item=cJSON_New_Item())) return 0; // memory fail child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_string(child,skip(value+1))); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') return 0; // fail! value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. if (!value) return 0; } if (*value=='}') return value+1; // end of array return 0; // malformed. } // Render an object to text. static char *print_object(cJSON *item,int depth,int fmt) { char **entries=0,**names=0; char *out=0,*ptr,*ret,*str;int len=7,i=0,j; cJSON *child=item->child; int numentries=0,fail=0; // Count the number of entries. while (child) numentries++,child=child->next; // Allocate space for the names and the objects entries=(char**)cJSON_malloc(numentries*sizeof(char*)); if (!entries) return 0; names=(char**)cJSON_malloc(numentries*sizeof(char*)); if (!names) {cJSON_free(entries);return 0;} memset(entries,0,sizeof(char*)*numentries); memset(names,0,sizeof(char*)*numentries); // Collect all the results into our arrays: child=item->child;depth++;if (fmt) len+=depth; while (child) { names[i]=str=print_string_ptr(child->string); entries[i++]=ret=print_value(child,depth,fmt); if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; child=child->next; } // Try to allocate the output string if (!fail) out=(char*)cJSON_malloc(len); if (!out) fail=1; // Handle failure if (fail) { for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} // Utility for array list handling. static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} // Utility for handling references. static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} // Add item to array/object. void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; if (c->prev) c->prev->next=c->next; if (c->next) c->next->prev=c->prev; if (c==array->child) array->child=c->next; c->prev=c->next=0; return c;} void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} // Replace array/object items with new ones. void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} // Create basic types: cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} // Create Arrays: cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} mlt-7.38.0/src/modules/plusgpl/cJSON.h000664 000000 000000 00000012515 15172202314 017400 0ustar00rootroot000000 000000 /* Copyright (c) 2009 Dave Gamble Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef cJSON__h #define cJSON__h #ifdef __cplusplus extern "C" { #endif // cJSON Types: #define cJSON_False 0 #define cJSON_True 1 #define cJSON_NULL 2 #define cJSON_Number 3 #define cJSON_String 4 #define cJSON_Array 5 #define cJSON_Object 6 #define cJSON_IsReference 256 // The cJSON structure: typedef struct cJSON { struct cJSON *next,*prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem struct cJSON *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object. int type; // The type of the item, as above. char *valuestring; // The item's string, if type==cJSON_String int valueint; // The item's number, if type==cJSON_Number double valuedouble; // The item's number, if type==cJSON_Number char *string; // The item's name string, if this item is the child of, or is in the list of subitems of an object. } cJSON; typedef struct cJSON_Hooks { void *(*malloc_fn)(size_t sz); void (*free_fn)(void *ptr); } cJSON_Hooks; // Supply malloc, realloc and free functions to cJSON extern void cJSON_InitHooks(cJSON_Hooks* hooks); // Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. extern cJSON *cJSON_Parse(const char *value); // Render a cJSON entity to text for transfer/storage. Free the char* when finished. extern char *cJSON_Print(cJSON *item); // Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. extern char *cJSON_PrintUnformatted(cJSON *item); // Delete a cJSON entity and all subentities. extern void cJSON_Delete(cJSON *c); // Returns the number of items in an array (or object). extern int cJSON_GetArraySize(cJSON *array); // Retrieve item number "item" from array "array". Returns NULL if unsuccessful. extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); // Get item "string" from object. Case insensitive. extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); // These calls create a cJSON item of the appropriate type. extern cJSON *cJSON_CreateNull(); extern cJSON *cJSON_CreateTrue(); extern cJSON *cJSON_CreateFalse(); extern cJSON *cJSON_CreateBool(int b); extern cJSON *cJSON_CreateNumber(double num); extern cJSON *cJSON_CreateString(const char *string); extern cJSON *cJSON_CreateArray(); extern cJSON *cJSON_CreateObject(); // These utilities create an Array of count items. extern cJSON *cJSON_CreateIntArray(int *numbers,int count); extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); extern cJSON *cJSON_CreateStringArray(const char **strings,int count); // Append item to the specified array/object. extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); // Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); // Remove/Detach items from Arrays/Objects. extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); extern void cJSON_DeleteItemFromArray(cJSON *array,int which); extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); // Update array items. extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/plusgpl/consumer_cbrts.c000664 000000 000000 00000124417 15172202314 021514 0ustar00rootroot000000 000000 /* * consumer_cbrts.c -- output constant bitrate MPEG-2 transport stream * * Copyright (C) 2010-2015 Broadcasting Center Europe S.A. http://www.bce.lu * an RTL Group Company http://www.rtlgroup.com * Author: Dan Dennedy * Some ideas and portions come from OpenCaster, Copyright (C) Lorenzo Pallara * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef _WIN32 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include // includes for socket IO #if (_POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE) && (_POSIX_TIMERS > 0) #if !(defined(__FreeBSD_kernel__) && defined(__GLIBC__)) #define CBRTS_BSD_SOCKETS 1 #include #include #include #include #include #endif #endif #include #include #ifdef _MSC_VER #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif #define TSP_BYTES (188) #define MAX_PID (8192) #define SCR_HZ (27000000ULL) #define NULL_PID (0x1fff) #define PAT_PID (0) #define SDT_PID (0x11) #define PCR_SMOOTHING (12) #define PCR_PERIOD_MS (20) #define RTP_BYTES (12) #define UDP_MTU (RTP_BYTES + TSP_BYTES * 7) #define REMUX_BUFFER_MIN (10) #define REMUX_BUFFER_MAX (50) #define UDP_BUFFER_MINIMUM (100) #define UDP_BUFFER_DEFAULT (1000) #define RTP_VERSION (2) #define RTP_PAYLOAD (33) #define RTP_HZ (90000) #define PIDOF(packet) (ntohs(*((uint16_t *) (packet + 1))) & 0x1fff) #define HASPCR(packet) ((packet[3] & 0x20) && (packet[4] != 0) && (packet[5] & 0x10)) #define CCOF(packet) (packet[3] & 0x0f) #define ADAPTOF(packet) ((packet[3] >> 4) & 0x03) typedef struct consumer_cbrts_s *consumer_cbrts; struct consumer_cbrts_s { struct mlt_consumer_s parent; mlt_consumer avformat; pthread_t thread; int joined; int running; mlt_position last_position; mlt_event event_registered; int fd; uint8_t *leftover_data[TSP_BYTES]; int leftover_size; mlt_deque tsp_packets; uint64_t previous_pcr; uint64_t previous_packet_count; uint64_t packet_count; int is_stuffing_set; int thread_running; uint8_t pcr_count; uint16_t pmt_pid; int is_si_sdt; int is_si_pat; int is_si_pmt; int dropped; uint8_t continuity_count[MAX_PID]; uint64_t output_counter; #ifdef CBRTS_BSD_SOCKETS struct addrinfo *addr; struct timespec timer; uint32_t nsec_per_packet; uint32_t femto_per_packet; uint64_t femto_counter; #endif int (*write_tsp)(consumer_cbrts, const void *buf, size_t count); uint8_t udp_packet[UDP_MTU]; size_t udp_bytes; size_t udp_packet_size; mlt_deque udp_packets; pthread_t output_thread; pthread_mutex_t udp_deque_mutex; pthread_cond_t udp_deque_cond; uint64_t muxrate; int udp_buffer_max; uint16_t rtp_sequence; uint32_t rtp_ssrc; uint32_t rtp_counter; }; typedef struct { int size; int period; int packet_count; uint16_t pid; uint8_t data[4096]; } ts_section; typedef struct { uint8_t *data; size_t size; } buffer_t; static uint8_t null_packet[TSP_BYTES]; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); static void on_data_received(mlt_properties properties, mlt_consumer consumer, mlt_event_data); mlt_consumer consumer_cbrts_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { consumer_cbrts self = calloc(sizeof(struct consumer_cbrts_s), 1); if (self && mlt_consumer_init(&self->parent, self, profile) == 0) { // Get the parent consumer object mlt_consumer parent = &self->parent; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); // Create child consumers self->avformat = mlt_factory_consumer(profile, "avformat", NULL); parent->close = consumer_close; parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; self->joined = 1; self->tsp_packets = mlt_deque_init(); self->udp_packets = mlt_deque_init(); // Create the null packet memset(null_packet, 0xFF, TSP_BYTES); null_packet[0] = 0x47; null_packet[1] = 0x1f; null_packet[2] = 0xff; null_packet[3] = 0x10; // Create the deque mutex and condition pthread_mutex_init(&self->udp_deque_mutex, NULL); pthread_cond_init(&self->udp_deque_cond, NULL); // Set consumer property defaults mlt_properties_set_int(properties, "real_time", -1); return parent; } free(self); return NULL; } static ts_section *load_section(const char *filename) { ts_section *section = NULL; int fd; if (!filename) return NULL; if ((fd = open(filename, O_RDONLY)) < 0) { mlt_log_error(NULL, "cbrts consumer failed to load section file %s\n", filename); return NULL; } section = malloc(sizeof(*section)); memset(section, 0xff, sizeof(*section)); section->size = 0; if (read(fd, section->data, 3)) { // get the size uint16_t *p = (uint16_t *) §ion->data[1]; section->size = p[0]; section->size = ntohs(section->size) & 0x0FFF; // read the data if (section->size <= sizeof(section->data) - 3) { ssize_t has_read = 0; while (has_read < section->size) { ssize_t n = read(fd, section->data + 3 + has_read, section->size); if (n > 0) has_read += n; else break; } section->size += 3; } else { mlt_log_error(NULL, "Section too big - skipped.\n"); } } close(fd); return section; } static void load_sections(consumer_cbrts self, mlt_properties properties) { int n = mlt_properties_count(properties); // Store the sections with automatic cleanup // and make it easy to iterate over the data sections. mlt_properties si_properties = mlt_properties_get_data(properties, "si.properties", NULL); if (!si_properties) { si_properties = mlt_properties_new(); mlt_properties_set_data(properties, "si.properties", si_properties, 0, (mlt_destructor) mlt_properties_close, NULL); } while (n--) { // Look for si..file=filename const char *name = mlt_properties_get_name(properties, n); if (strncmp("si.", name, 3) == 0 && strncmp(".file", name + strlen(name) - 5, 5) == 0) { size_t len = strlen(name); char *si_name = strdup(name + 3); // unbreak compilation on OpenBSD #ifdef si_pid #undef si_pid #endif char si_pid[len + 1]; si_name[len - 3 - 5] = 0; strcpy(si_pid, "si."); strcat(si_pid, si_name); strcat(si_pid, ".pid"); // Look for si..pid= if (mlt_properties_get(properties, si_pid)) { char *filename = mlt_properties_get_value(properties, n); ts_section *section = load_section(filename); if (section) { // Determine the periodicity of the section, if supplied char si_time[len + 1]; strcpy(si_time, "si."); strcat(si_time, si_name); strcat(si_time, ".time"); int time = mlt_properties_get_int(properties, si_time); if (time == 0) time = 200; // Set flags if we are replacing PAT or SDT if (strncasecmp("pat", si_name, 3) == 0) self->is_si_pat = 1; else if (strncasecmp("pmt", si_name, 3) == 0) self->is_si_pmt = 1; else if (strncasecmp("sdt", si_name, 3) == 0) self->is_si_sdt = 1; // Calculate the period and get the PID section->period = (self->muxrate * time) / (TSP_BYTES * 8 * 1000); // output one immediately section->packet_count = section->period - 1; mlt_log_verbose(NULL, "SI %s time=%d period=%d file=%s\n", si_name, time, section->period, filename); section->pid = mlt_properties_get_int(properties, si_pid); mlt_properties_set_data(si_properties, si_name, section, section->size, free, NULL); } } free(si_name); } } } static void write_section(consumer_cbrts self, ts_section *section) { uint8_t *packet; const uint8_t *data_ptr = section->data; uint8_t *p; int size = section->size; int first, len; while (size > 0) { first = (section->data == data_ptr); p = packet = malloc(TSP_BYTES); *p++ = 0x47; *p = (section->pid >> 8); if (first) *p |= 0x40; p++; *p++ = section->pid; *p++ = 0x10; // continuity count will be written later if (first) *p++ = 0; /* 0 offset */ len = TSP_BYTES - (p - packet); if (len > size) len = size; memcpy(p, data_ptr, len); p += len; /* add known padding data */ len = TSP_BYTES - (p - packet); if (len > 0) memset(p, 0xff, len); mlt_deque_push_back(self->tsp_packets, packet); self->packet_count++; data_ptr += len; size -= len; } } static void write_sections(consumer_cbrts self) { mlt_properties properties = mlt_properties_get_data(MLT_CONSUMER_PROPERTIES(&self->parent), "si.properties", NULL); if (properties) { int n = mlt_properties_count(properties); while (n--) { ts_section *section = mlt_properties_get_data_at(properties, n, NULL); if (++section->packet_count == section->period) { section->packet_count = 0; write_section(self, section); } } } } static uint64_t get_pcr(uint8_t *packet) { uint64_t pcr = 0; pcr += (uint64_t) packet[6] << 25; pcr += packet[7] << 17; pcr += packet[8] << 9; pcr += packet[9] << 1; pcr += packet[10] >> 7; pcr *= 300; // convert 90KHz to 27MHz pcr += (packet[10] & 0x01) << 8; pcr += packet[11]; return pcr; } static void set_pcr(uint8_t *packet, uint64_t pcr) { uint64_t pcr_base = pcr / 300; uint64_t pcr_ext = pcr % 300; packet[6] = pcr_base >> 25; packet[7] = pcr_base >> 17; packet[8] = pcr_base >> 9; packet[9] = pcr_base >> 1; packet[10] = ((pcr_base & 1) << 7) | 0x7E | ((pcr_ext & 0x100) >> 8); packet[11] = pcr_ext; } static uint64_t update_pcr(consumer_cbrts self, uint64_t muxrate, unsigned packets) { return self->previous_pcr + packets * TSP_BYTES * 8 * SCR_HZ / muxrate; } static uint32_t get_rtp_timestamp(consumer_cbrts self) { return self->rtp_counter++ * self->udp_packet_size * 8 * RTP_HZ / self->muxrate; } static double measure_bitrate(consumer_cbrts self, uint64_t pcr, int drop) { double muxrate = 0; if (self->is_stuffing_set || self->previous_pcr) { muxrate = (self->packet_count - self->previous_packet_count - drop) * TSP_BYTES * 8; if (pcr >= self->previous_pcr) muxrate /= (double) (pcr - self->previous_pcr) / SCR_HZ; else muxrate /= (((double) (1ULL << 33) - 1) * 300 - self->previous_pcr + pcr) / SCR_HZ; mlt_log_debug(NULL, "measured TS bitrate %.1f bits/sec PCR %" PRIu64 "\n", muxrate, pcr); } return muxrate; } static int writen(consumer_cbrts self, const void *buf, size_t count) { int result = 0; int written = 0; while (written < count) { if ((result = write(self->fd, (const char *) buf + written, count - written)) < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Failed to write: %s\n", strerror(errno)); break; } written += result; } return result; } #ifdef CBRTS_BSD_SOCKETS static int sendn(consumer_cbrts self, const void *buf, size_t count) { int result = 0; int written = 0; while (written < count) { result = sendto(self->fd, (const char *) buf + written, count - written, 0, self->addr->ai_addr, self->addr->ai_addrlen); if (result < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Failed to send: %s\n", strerror(errno)); exit(EXIT_FAILURE); break; } written += result; } return result; } #endif static int write_udp(consumer_cbrts self, const void *buf, size_t count) { int result = 0; #ifdef CBRTS_BSD_SOCKETS if (!self->timer.tv_sec) clock_gettime(CLOCK_MONOTONIC, &self->timer); self->femto_counter += self->femto_per_packet; self->timer.tv_nsec += self->femto_counter / 1000000; self->femto_counter = self->femto_counter % 1000000; self->timer.tv_nsec += self->nsec_per_packet; self->timer.tv_sec += self->timer.tv_nsec / 1000000000; self->timer.tv_nsec = self->timer.tv_nsec % 1000000000; clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &self->timer, NULL); result = sendn(self, buf, count); #endif return result; } // socket IO code static int create_socket(consumer_cbrts self) { int result = -1; #ifdef CBRTS_BSD_SOCKETS struct addrinfo hints = {0}; mlt_properties properties = MLT_CONSUMER_PROPERTIES(&self->parent); const char *hostname = mlt_properties_get(properties, "udp.address"); const char *port = "1234"; if (mlt_properties_get(properties, "udp.port")) port = mlt_properties_get(properties, "udp.port"); // Resolve the address string and port. hints.ai_socktype = SOCK_DGRAM; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_PASSIVE; result = getaddrinfo(hostname, port, &hints, &self->addr); if (result < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Error resolving UDP address and port: %s.\n", gai_strerror(result)); return result; } // Create the socket descriptor. struct addrinfo *addr = self->addr; for (; addr; addr = addr->ai_next) { result = socket(addr->ai_addr->sa_family, SOCK_DGRAM, addr->ai_protocol); if (result != -1) { // success self->fd = result; result = 0; break; } } if (result < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Error creating socket: %s.\n", strerror(errno)); freeaddrinfo(self->addr); self->addr = NULL; return result; } // Set the reuse address socket option if not disabled (explicitly set 0). int reuse = mlt_properties_get_int(properties, "udp.reuse") || !mlt_properties_get(properties, "udp.reuse"); if (reuse) { result = setsockopt(self->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); if (result < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Error setting the reuse address socket option.\n"); close(self->fd); freeaddrinfo(self->addr); self->addr = NULL; return result; } } // Set the socket buffer size if supplied. if (mlt_properties_get(properties, "udp.sockbufsize")) { int sockbufsize = mlt_properties_get_int(properties, "udp.sockbufsize"); result = setsockopt(self->fd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, sizeof(sockbufsize)); if (result < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Error setting the socket buffer size.\n"); close(self->fd); freeaddrinfo(self->addr); self->addr = NULL; return result; } } // Set the multicast TTL if supplied. if (mlt_properties_get(properties, "udp.ttl")) { int ttl = mlt_properties_get_int(properties, "udp.ttl"); if (addr->ai_addr->sa_family == AF_INET) { result = setsockopt(self->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); } else if (addr->ai_addr->sa_family == AF_INET6) { result = setsockopt(self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); } if (result < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Error setting the multicast TTL.\n"); close(self->fd); freeaddrinfo(self->addr); self->addr = NULL; return result; } } // Set the multicast interface if supplied. if (mlt_properties_get(properties, "udp.interface")) { const char *interface = mlt_properties_get(properties, "udp.interface"); unsigned int iface = if_nametoindex(interface); if (iface) { if (addr->ai_addr->sa_family == AF_INET) { struct ip_mreqn req = {{0}}; req.imr_ifindex = iface; result = setsockopt(self->fd, IPPROTO_IP, IP_MULTICAST_IF, &req, sizeof(req)); } else if (addr->ai_addr->sa_family == AF_INET6) { result = setsockopt(self->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface)); } if (result < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Error setting the multicast interface.\n"); close(self->fd); freeaddrinfo(self->addr); self->addr = NULL; return result; } } else { mlt_log_warning(MLT_CONSUMER_SERVICE(&self->parent), "The network interface \"%s\" was not found.\n", interface); } } #endif return result; } static void *output_thread(void *arg) { consumer_cbrts self = arg; int result = 0; while (self->thread_running) { pthread_mutex_lock(&self->udp_deque_mutex); while (self->thread_running && mlt_deque_count(self->udp_packets) < 1) pthread_cond_wait(&self->udp_deque_cond, &self->udp_deque_mutex); pthread_mutex_unlock(&self->udp_deque_mutex); // Dequeue the UDP packets and write them. int i = mlt_deque_count(self->udp_packets); mlt_log_debug(MLT_CONSUMER_SERVICE(&self->parent), "%s: count %d\n", __FUNCTION__, i); while (self->thread_running && i-- && result >= 0) { pthread_mutex_lock(&self->udp_deque_mutex); uint8_t *packet = mlt_deque_pop_front(self->udp_packets); pthread_cond_broadcast(&self->udp_deque_cond); pthread_mutex_unlock(&self->udp_deque_mutex); size_t size = self->rtp_ssrc ? RTP_BYTES + self->udp_packet_size : self->udp_packet_size; result = write_udp(self, packet, size); free(packet); } } return NULL; } static int enqueue_udp(consumer_cbrts self, const void *buf, size_t count) { // Append TSP to the UDP packet. memcpy(&self->udp_packet[self->udp_bytes], buf, count); self->udp_bytes = (self->udp_bytes + count) % self->udp_packet_size; // Send the UDP packet. if (!self->udp_bytes) { size_t offset = self->rtp_ssrc ? RTP_BYTES : 0; // Duplicate the packet. uint8_t *packet = malloc(self->udp_packet_size + offset); memcpy(packet + offset, self->udp_packet, self->udp_packet_size); // Add the RTP header. if (self->rtp_ssrc) { // Padding, extension, and CSRC count are all 0. packet[0] = RTP_VERSION << 6; // Marker bit is 0. packet[1] = RTP_PAYLOAD & 0x7f; packet[2] = (self->rtp_sequence >> 8) & 0xff; packet[3] = (self->rtp_sequence >> 0) & 0xff; // Timestamp in next 4 bytes. uint32_t timestamp = get_rtp_timestamp(self); packet[4] = (timestamp >> 24) & 0xff; packet[5] = (timestamp >> 16) & 0xff; packet[6] = (timestamp >> 8) & 0xff; packet[7] = (timestamp >> 0) & 0xff; // SSRC in next 4 bytes. packet[8] = (self->rtp_ssrc >> 24) & 0xff; packet[9] = (self->rtp_ssrc >> 16) & 0xff; packet[10] = (self->rtp_ssrc >> 8) & 0xff; packet[11] = (self->rtp_ssrc >> 0) & 0xff; self->rtp_sequence++; } // Wait for room in the fifo. pthread_mutex_lock(&self->udp_deque_mutex); while (self->thread_running && mlt_deque_count(self->udp_packets) >= self->udp_buffer_max) pthread_cond_wait(&self->udp_deque_cond, &self->udp_deque_mutex); // Add the packet to the fifo. mlt_deque_push_back(self->udp_packets, packet); pthread_cond_broadcast(&self->udp_deque_cond); pthread_mutex_unlock(&self->udp_deque_mutex); } return 0; } static int insert_pcr(consumer_cbrts self, uint16_t pid, uint8_t cc, uint64_t pcr) { uint8_t packet[TSP_BYTES]; uint8_t *p = packet; *p++ = 0x47; *p++ = pid >> 8; *p++ = pid; *p++ = 0x20 | cc; // Adaptation only // Continuity Count field does not increment (see 13818-1 section 2.4.3.3) *p++ = TSP_BYTES - 5; // Adaptation Field Length *p++ = 0x10; // Adaptation flags: PCR present set_pcr(packet, pcr); p += 6; // 6 pcr bytes memset(p, 0xff, TSP_BYTES - (p - packet)); // stuffing return self->write_tsp(self, packet, TSP_BYTES); } static int output_cbr(consumer_cbrts self, uint64_t input_rate, uint64_t output_rate, uint64_t *pcr) { int n = mlt_deque_count(self->tsp_packets); unsigned output_packets = 0; unsigned packets_since_pcr = 0; int result = 0; int dropped = 0; int warned = 0; uint16_t pcr_pid = 0; uint8_t cc = 0xff; float ms_since_pcr; float ms_to_end; uint64_t input_counter = 0; uint64_t last_input_counter; mlt_log_debug(NULL, "%s: n %i output_counter %" PRIu64 " input_rate %" PRIu64 "\n", __FUNCTION__, n, self->output_counter, input_rate); while (self->thread_running && n-- && result >= 0) { uint8_t *packet = mlt_deque_pop_front(self->tsp_packets); uint16_t pid = PIDOF(packet); // Check for overflow if (input_rate > output_rate && !HASPCR(packet) && pid != SDT_PID && pid != PAT_PID && pid != self->pmt_pid) { if (!warned) { mlt_log_warning(MLT_CONSUMER_SERVICE(&self->parent), "muxrate too low %" PRIu64 " > %" PRIu64 "\n", input_rate, output_rate); warned = 1; } // Skip this packet free(packet); // Compute new input_rate based on dropped count input_rate = measure_bitrate(self, *pcr, ++dropped); continue; } if (HASPCR(packet)) { pcr_pid = pid; set_pcr(packet, update_pcr(self, output_rate, output_packets)); packets_since_pcr = 0; } // Rewrite the continuity counter if not only adaptation field if (ADAPTOF(packet) != 2) { packet[3] = (packet[3] & 0xf0) | self->continuity_count[pid]; self->continuity_count[pid] = (self->continuity_count[pid] + 1) & 0xf; } if (pcr_pid && pid == pcr_pid) cc = CCOF(packet); result = self->write_tsp(self, packet, TSP_BYTES); free(packet); if (result < 0) break; output_packets++; packets_since_pcr++; self->output_counter += TSP_BYTES * 8 * output_rate; input_counter += TSP_BYTES * 8 * input_rate; // See if we need to output a dummy packet with PCR ms_since_pcr = (float) (packets_since_pcr + 1) * 8 * TSP_BYTES * 1000 / output_rate; ms_to_end = (float) n * 8 * TSP_BYTES * 1000 / input_rate; if (pcr_pid && ms_since_pcr >= PCR_PERIOD_MS && ms_to_end > PCR_PERIOD_MS / 2.0) { uint64_t new_pcr = update_pcr(self, output_rate, output_packets); if (ms_since_pcr > 40) mlt_log_warning(NULL, "exceeded PCR interval %.2f ms queued %.2f ms\n", ms_since_pcr, ms_to_end); if ((result = insert_pcr(self, pcr_pid, cc, new_pcr)) < 0) break; packets_since_pcr = 0; output_packets++; input_counter += TSP_BYTES * 8 * input_rate; } // Output null packets as needed while (self->thread_running && input_counter + (TSP_BYTES * 8 * input_rate) <= self->output_counter) { // See if we need to output a dummy packet with PCR ms_since_pcr = (float) (packets_since_pcr + 1) * 8 * TSP_BYTES * 1000 / output_rate; ms_to_end = (float) n * 8 * TSP_BYTES * 1000 / input_rate; if (pcr_pid && ms_since_pcr >= PCR_PERIOD_MS && ms_to_end > PCR_PERIOD_MS / 2.0) { uint64_t new_pcr = update_pcr(self, output_rate, output_packets); if (ms_since_pcr > 40) mlt_log_warning(NULL, "exceeded PCR interval %.2f ms queued %.2f ms\n", ms_since_pcr, ms_to_end); if ((result = insert_pcr(self, pcr_pid, cc, new_pcr)) < 0) break; packets_since_pcr = 0; } else { // Otherwise output a null packet if ((result = self->write_tsp(self, null_packet, TSP_BYTES)) < 0) break; packets_since_pcr++; } output_packets++; // Increment input last_input_counter = input_counter; input_counter += TSP_BYTES * 8 * input_rate; // Handle wrapping if (last_input_counter > input_counter) { last_input_counter -= self->output_counter; self->output_counter = 0; input_counter = last_input_counter + TSP_BYTES * 8 * input_rate; } } } // Reset counters leaving a residual output count if (input_counter < self->output_counter) self->output_counter -= input_counter; else self->output_counter = 0; // Warn if the PCR interval is too large or small ms_since_pcr = (float) packets_since_pcr * 8 * TSP_BYTES * 1000 / output_rate; if (ms_since_pcr > 40) mlt_log_warning(NULL, "exceeded PCR interval %.2f ms\n", ms_since_pcr); else if (ms_since_pcr < PCR_PERIOD_MS / 2.0) mlt_log_debug(NULL, "PCR interval too short %.2f ms\n", ms_since_pcr); // Update the current PCR based on number of packets output *pcr = update_pcr(self, output_rate, output_packets); return result; } static void get_pmt_pid(consumer_cbrts self, uint8_t *packet) { // Skip 5 bytes of TSP header + 8 bytes of section header + 2 bytes of service ID uint16_t *p = (uint16_t *) (packet + 5 + 8 + 2); self->pmt_pid = ntohs(p[0]) & 0x1fff; mlt_log_debug(NULL, "PMT PID 0x%04x\n", self->pmt_pid); return; } static int remux_packet(consumer_cbrts self, uint8_t *packet) { mlt_service service = MLT_CONSUMER_SERVICE(&self->parent); uint16_t pid = PIDOF(packet); int result = 0; write_sections(self); // Sanity checks if (packet[0] != 0x47) { mlt_log_panic(service, "NOT SYNC BYTE 0x%02x\n", packet[0]); exit(1); } if (pid == NULL_PID) { mlt_log_panic(service, "NULL PACKET\n"); exit(1); } // Measure the bitrate between consecutive PCRs if (HASPCR(packet)) { if (self->pcr_count++ % PCR_SMOOTHING == 0) { uint64_t pcr = get_pcr(packet); double input_rate = measure_bitrate(self, pcr, 0); if (input_rate > 0) { self->is_stuffing_set = 1; if (input_rate > 1.0) { result = output_cbr(self, input_rate, self->muxrate, &pcr); set_pcr(packet, pcr); } } self->previous_pcr = pcr; self->previous_packet_count = self->packet_count; } } mlt_deque_push_back(self->tsp_packets, packet); self->packet_count++; return result; } static void start_output_thread(consumer_cbrts self) { int rtprio = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(&self->parent), "udp.rtprio"); self->thread_running = 1; if (rtprio > 0) { // Use realtime priority class struct sched_param priority; pthread_attr_t thread_attributes; pthread_attr_init(&thread_attributes); priority.sched_priority = rtprio; pthread_attr_setschedpolicy(&thread_attributes, SCHED_FIFO); pthread_attr_setschedparam(&thread_attributes, &priority); #if !defined(__ANDROID__) || (defined(__ANDROID__) && __ANDROID_API__ >= 28) pthread_attr_setinheritsched(&thread_attributes, PTHREAD_EXPLICIT_SCHED); #endif pthread_attr_setscope(&thread_attributes, PTHREAD_SCOPE_SYSTEM); if (pthread_create(&self->output_thread, &thread_attributes, output_thread, self) < 0) { mlt_log_info(MLT_CONSUMER_SERVICE(&self->parent), "failed to initialize output thread with realtime priority\n"); pthread_create(&self->output_thread, &thread_attributes, output_thread, self); } pthread_attr_destroy(&thread_attributes); } else { // Use normal priority class pthread_create(&self->output_thread, NULL, output_thread, self); } } static void stop_output_thread(consumer_cbrts self) { self->thread_running = 0; // Broadcast to the condition in case it's waiting. pthread_mutex_lock(&self->udp_deque_mutex); pthread_cond_broadcast(&self->udp_deque_cond); pthread_mutex_unlock(&self->udp_deque_mutex); // Join the thread. pthread_join(self->output_thread, NULL); // Release the buffered packets. pthread_mutex_lock(&self->udp_deque_mutex); int n = mlt_deque_count(self->udp_packets); while (n--) free(mlt_deque_pop_back(self->udp_packets)); pthread_mutex_unlock(&self->udp_deque_mutex); } static inline int filter_packet(consumer_cbrts self, uint8_t *packet) { uint16_t pid = PIDOF(packet); // We are going to keep the existing PMT; replace all other signaling sections. int result = (pid == NULL_PID) || (pid == PAT_PID && self->is_si_pat) || (pid == self->pmt_pid && self->is_si_pmt) || (pid == SDT_PID && self->is_si_sdt); // Get the PMT PID from the PAT if (pid == PAT_PID && !self->pmt_pid) { get_pmt_pid(self, packet); if (self->is_si_pmt) result = 1; } return result; } static void filter_remux_or_write_packet(consumer_cbrts self, uint8_t *packet) { int remux = !mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(&self->parent), "noremux"); // Filter out packets if (remux) { if (!filter_packet(self, packet)) remux_packet(self, packet); else free(packet); } else { self->write_tsp(self, packet, TSP_BYTES); free(packet); } } static void on_data_received(mlt_properties properties, mlt_consumer consumer, mlt_event_data event_data) { buffer_t *buffer = mlt_event_data_to_object(event_data); uint8_t *buf = buffer->data; size_t size = buffer->size; if (size > 0) { consumer_cbrts self = (consumer_cbrts) consumer->child; // Sanity check if (self->leftover_size == 0 && buf[0] != 0x47) { mlt_log_verbose(MLT_CONSUMER_SERVICE(consumer), "NOT SYNC BYTE 0x%02x\n", buf[0]); while (size && buf[0] != 0x47) { buf++; size--; } if (size <= 0) exit(1); } // Enqueue the packets int num_packets = (self->leftover_size + size) / TSP_BYTES; int remaining = (self->leftover_size + size) % TSP_BYTES; uint8_t *packet = NULL; int i; // mlt_log_verbose( MLT_CONSUMER_SERVICE(consumer), "%s: packets %d remaining %i\n", __FUNCTION__, num_packets, self->leftover_size ); if (self->leftover_size) { packet = malloc(TSP_BYTES); memcpy(packet, self->leftover_data, self->leftover_size); memcpy(packet + self->leftover_size, buf, TSP_BYTES - self->leftover_size); buf += TSP_BYTES - self->leftover_size; --num_packets; filter_remux_or_write_packet(self, packet); } for (i = 0; i < num_packets; i++, buf += TSP_BYTES) { packet = malloc(TSP_BYTES); memcpy(packet, buf, TSP_BYTES); filter_remux_or_write_packet(self, packet); } self->leftover_size = remaining; memcpy(self->leftover_data, buf, self->leftover_size); if (!self->thread_running) start_output_thread(self); mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "%s: %p 0x%x (%u)\n", __FUNCTION__, buf, *buf, (unsigned int) size % TSP_BYTES); // Do direct output // result = self->write_tsp( self, buf, size ); } } static int consumer_start(mlt_consumer parent) { consumer_cbrts self = parent->child; if (!self->running) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); mlt_properties avformat = MLT_CONSUMER_PROPERTIES(self->avformat); // Cleanup after a possible abort consumer_stop(parent); // Pass properties down mlt_properties_pass(avformat, properties, ""); mlt_properties_set_data(avformat, "app_lock", mlt_properties_get_data(properties, "app_lock", NULL), 0, NULL, NULL); mlt_properties_set_data(avformat, "app_unlock", mlt_properties_get_data(properties, "app_unlock", NULL), 0, NULL, NULL); mlt_properties_set_int(avformat, "put_mode", 1); mlt_properties_set_int(avformat, "real_time", -1); mlt_properties_set_int(avformat, "buffer", 2); mlt_properties_set_int(avformat, "terminate_on_pause", 0); mlt_properties_set_int(avformat, "muxrate", 1); mlt_properties_set_int(avformat, "redirect", 1); mlt_properties_set(avformat, "f", "mpegts"); self->dropped = 0; self->fd = STDOUT_FILENO; self->write_tsp = writen; self->muxrate = mlt_properties_get_int64(MLT_CONSUMER_PROPERTIES(&self->parent), "muxrate"); if (mlt_properties_get(properties, "udp.address")) { if (create_socket(self) >= 0) { int is_rtp = 1; if (mlt_properties_get(properties, "udp.rtp")) is_rtp = !!mlt_properties_get_int(properties, "udp.rtp"); if (is_rtp) { self->rtp_ssrc = mlt_properties_get_int(properties, "udp.rtp_ssrc"); while (!self->rtp_ssrc) self->rtp_ssrc = (uint32_t) rand(); self->rtp_counter = (uint32_t) rand(); } self->udp_packet_size = mlt_properties_get_int(properties, "udp.nb_tsp") * TSP_BYTES; if (self->udp_packet_size <= 0 || self->udp_packet_size > UDP_MTU) self->udp_packet_size = 7 * TSP_BYTES; #ifdef CBRTS_BSD_SOCKETS self->nsec_per_packet = 1000000000UL * self->udp_packet_size * 8 / self->muxrate; self->femto_per_packet = 1000000000000000ULL * self->udp_packet_size * 8 / self->muxrate - self->nsec_per_packet * 1000000; #endif self->udp_buffer_max = mlt_properties_get_int(properties, "udp.buffer"); if (self->udp_buffer_max < UDP_BUFFER_MINIMUM) self->udp_buffer_max = UDP_BUFFER_DEFAULT; self->write_tsp = enqueue_udp; } } // Load the DVB PSI/SI sections load_sections(self, properties); // Start the FFmpeg consumer and our thread mlt_consumer_start(self->avformat); pthread_create(&self->thread, NULL, consumer_thread, self); self->running = 1; self->joined = 0; } return 0; } static int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_cbrts self = parent->child; if (!self->joined) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); int app_locked = mlt_properties_get_int(properties, "app_locked"); void (*lock)(void) = mlt_properties_get_data(properties, "app_lock", NULL); void (*unlock)(void) = mlt_properties_get_data(properties, "app_unlock", NULL); if (app_locked && unlock) unlock(); // Kill the threads and clean up self->running = 0; #ifndef _WIN32 if (self->thread) #endif pthread_join(self->thread, NULL); self->joined = 1; if (self->avformat) mlt_consumer_stop(self->avformat); stop_output_thread(self); if (self->fd > 1) close(self->fd); if (app_locked && lock) lock(); } return 0; } static int consumer_is_stopped(mlt_consumer parent) { consumer_cbrts self = parent->child; return !self->running; } static void *consumer_thread(void *arg) { // Identify the arg consumer_cbrts self = arg; // Get the consumer and producer mlt_consumer consumer = &self->parent; // internal initialization mlt_frame frame = NULL; int last_position = -1; // Loop until told not to while (self->running) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame(consumer); // Ensure that we have a frame if (self->running && frame) { if (mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "rendered") != 1) { mlt_frame_close(frame); mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "dropped frame %d\n", ++self->dropped); continue; } // Get the speed of the frame double speed = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed"); // Optimisation to reduce latency if (speed == 1.0) { if (last_position != -1 && last_position + 1 != mlt_frame_get_position(frame)) mlt_consumer_purge(self->avformat); last_position = mlt_frame_get_position(frame); } else { //mlt_consumer_purge( this->play ); last_position = -1; } mlt_consumer_put_frame(self->avformat, frame); // Setup event listener as a callback from consumer avformat if (!self->event_registered) self->event_registered = mlt_events_listen(MLT_CONSUMER_PROPERTIES(self->avformat), consumer, "avformat-write", (mlt_listener) on_data_received); } else { if (frame) mlt_frame_close(frame); mlt_consumer_put_frame(self->avformat, NULL); self->running = 0; } } return NULL; } /** Callback to allow override of the close method. */ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_cbrts self = parent->child; // Stop the consumer mlt_consumer_stop(parent); // Close the child consumers mlt_consumer_close(self->avformat); // Now clean up the rest mlt_deque_close(self->tsp_packets); mlt_deque_close(self->udp_packets); mlt_consumer_close(parent); // Finally clean up this free(self); } mlt-7.38.0/src/modules/plusgpl/consumer_cbrts.yml000664 000000 000000 00000007113 15172202314 022064 0ustar00rootroot000000 000000 schema_version: 0.3 type: consumer identifier: cbrts title: CBR MPEG2-TS version: 2 copyright: Copyright (C) 2010-2015 Broadcasting Center Europe S.A. http://www.bce.lu license: GPLv2 language: en creator: Dan Dennedy tags: - Audio - Video description: Constant bit-rate MPEG-2 Transport Stream notes: | All properties, except some key operational properties such as real_time and terminate_on_pause, set on the this consumer are passed onto an encapsulated avformat consumer - no special prefix required. While some avformat properties can accept a "k" suffix, this consumer requires "muxrate" but does not understand the "k" suffix; so, specify the value in bytes per second. The stream is always output to STDOUT at this time. You can rewrite and insert table sections into the transport stream. If you choose to rewrite the PMT sections, then you need to know how libavformat sets the PIDs on the elementary streams. Currently, the video stream is 256 (0x100) and audio streams start at 257, incrementing from there. There are conventions for property names to pass the .sec files to the consumer. The conventions are: si.
.file= si.
.pid= si.
.time=
is really anything, but typically: pat, sdt, nit, eit, etc. "pat," "pmt," and "sdt" are special such that when supplied, they cause libavformat's corresponding sections to be filtered out and replaced with yours. You should always use PID 16 for NIT, 17 for SDT, and of course, 0 for PAT; PMT may be anything, but libavformat uses 4095 (0xfff). The time property indicates the frequency to insert the section - every N milliseconds. parameters: - identifier: muxrate type: integer unit: bytes/second - identifier: udp.rtprio title: Real-time priority description: > When set to a valid value, this makes the network output thread run with a real-time policy and priority where 1 is lowest and 99 is highest. type: integer minimum: 1 maximum: 99 - identifier: udp.address title: UDP address description: > If an IP address is provided, the stream is sent over UDP instead of STDOUT. type: string - identifier: udp.port title: UDP port type: integer minimum: 0 default: 1234 - identifier: udp.ttl title: Multicast TTL description: > The multicast time-to-live value controls how many routing hops the multicast will survive. type: integer minimum: 0 maximum: 255 - identifier: udp.reuse title: Reuse socket address description: > When not supplied, the socket is opened with the reuse address option. Set this to 0 to disable that. type: boolean default: 1 - identifier: udp.sockbufsize title: Socket buffer size type: integer minimum: 1 unit: bytes - identifier: udp.nb_tsp title: TS packets per UDP packet type: integer minimum: 0 maximum: 7 default: 7 - identifier: udp.buffer title: Max buffer IP packets type: integer minimum: 100 default: 1000 - identifier: udp.rtp title: Use RTP type: boolean default: 1 - identifier: udp.rtp_ssrc title: RTP synchronization source type: integer description: The default is a random number, but you can override it. - identifier: udp.interface title: Multicast interface name description: > Normally the multicast interface is selected by the IP routing table configured on the system, but this might be more convenient. It takes a name like "eth0". type: string mlt-7.38.0/src/modules/plusgpl/factory.c000664 000000 000000 00000007607 15172202314 020134 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2008-2025 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mltplusgpl_export.h" #include extern mlt_consumer consumer_cbrts_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_burn_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_lumaliftgaingamma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_outline_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_rotoscoping_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_telecide_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/plusgpl/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTPLUSGPL_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "cbrts", consumer_cbrts_init); MLT_REGISTER(mlt_service_filter_type, "BurningTV", filter_burn_init); MLT_REGISTER(mlt_service_filter_type, "burningtv", filter_burn_init); MLT_REGISTER(mlt_service_filter_type, "lumaliftgaingamma", filter_lumaliftgaingamma_init); MLT_REGISTER(mlt_service_filter_type, "outline", filter_outline_init); MLT_REGISTER(mlt_service_filter_type, "rotoscoping", filter_rotoscoping_init); MLT_REGISTER(mlt_service_filter_type, "telecide", filter_telecide_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "cbrts", metadata, "consumer_cbrts.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "BurningTV", metadata, "filter_burningtv.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "burningtv", metadata, "filter_burningtv.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "lumaliftgaingamma", metadata, "filter_lumaliftgaingamma.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "outline", metadata, "filter_outline.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "rotoscoping", metadata, "filter_rotoscoping.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "telecide", metadata, "filter_telecide.yml"); } mlt-7.38.0/src/modules/plusgpl/filter_burn.c000664 000000 000000 00000017414 15172202314 020775 0ustar00rootroot000000 000000 /* * filter_burn.c -- burning filter * Copyright (C) 2007 Stephane Fillod * * Filter taken from EffecTV - Realtime Digital Video Effector * Copyright (C) 2001-2006 FUKUCHI Kentaro * * BurningTV - burns incoming objects. * Copyright (C) 2001-2002 FUKUCHI Kentaro * * Fire routine is taken from Frank Jan Sorensen's demo program. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "utils.h" #include #include #include #include #define MaxColor 120 #define Decay 15 #define MAGIC_THRESHOLD "50" static RGB32 palette[256]; static void makePalette(void) { int i, r, g, b; uint8_t *p = (uint8_t *) palette; for (i = 0; i < MaxColor; i++) { HSItoRGB(4.6 - 1.5 * i / MaxColor, (double) i / MaxColor, (double) i / MaxColor, &r, &g, &b); *p++ = r & 0xfe; *p++ = g & 0xfe; *p++ = b & 0xfe; p++; } for (i = MaxColor; i < 256; i++) { if (r < 255) r++; if (r < 255) r++; if (r < 255) r++; if (g < 255) g++; if (g < 255) g++; if (b < 255) b++; if (b < 255) b++; *p++ = r & 0xfe; *p++ = g & 0xfe; *p++ = b & 0xfe; p++; } } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { RGB32 *background; unsigned char *diff; unsigned char *buffer; // Get the filter mlt_filter filter = mlt_frame_pop_service(frame); // Get the image *format = mlt_image_rgba; int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Only process if we have no error and a valid colour space if (error == 0) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position pos = mlt_filter_get_position(filter, frame); mlt_position len = mlt_filter_get_length2(filter, frame); // Get the "Burn the foreground" value int burn_foreground = mlt_properties_get_int(properties, "foreground"); int animated_threshold = mlt_properties_anim_get_int(properties, "threshold", pos, len); int y_threshold = image_set_threshold_y(animated_threshold); // We'll process pixel by pixel int x = 0; int y = 0; int i; int video_width = *width; int video_height = *height; int video_area = video_width * video_height; // We need to create a new frame as this effect modifies the input RGB32 *dest = (RGB32 *) *image; RGB32 *src = (RGB32 *) *image; unsigned char v, w; RGB32 a, b, c; mlt_service_lock(MLT_FILTER_SERVICE(filter)); diff = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "_diff", NULL); if (diff == NULL) { // TODO: What if the image size changes? diff = mlt_pool_alloc(video_area * sizeof(unsigned char)); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "_diff", diff, video_area * sizeof(unsigned char), mlt_pool_release, NULL); } buffer = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "_buffer", NULL); if (buffer == NULL) { // TODO: What if the image size changes? buffer = mlt_pool_alloc(video_area * sizeof(unsigned char)); memset(buffer, 0, video_area * sizeof(unsigned char)); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "_buffer", buffer, video_area * sizeof(unsigned char), mlt_pool_release, NULL); } if (burn_foreground == 1) { /* to burn the foreground, we need a background */ background = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "_background", NULL); if (background == NULL) { // TODO: What if the image size changes? background = mlt_pool_alloc(video_area * sizeof(RGB32)); image_bgset_y(background, src, video_area, y_threshold); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "_background", background, video_area * sizeof(RGB32), mlt_pool_release, NULL); } } if (burn_foreground == 1) { image_bgsubtract_y(diff, background, src, video_area, y_threshold); } else { /* default */ image_y_over(diff, src, video_area, y_threshold); } for (x = 1; x < video_width - 1; x++) { v = 0; for (y = 0; y < video_height - 1; y++) { w = diff[y * video_width + x]; buffer[y * video_width + x] |= v ^ w; v = w; } } for (x = 1; x < video_width - 1; x++) { i = video_width + x; for (y = 1; y < video_height; y++) { v = buffer[i]; if (v < Decay) buffer[i - video_width] = 0; else buffer[i - video_width + fastrand() % 3 - 1] = v - (fastrand() & Decay); i += video_width; } } i = 1; for (y = 0; y < video_height; y++) { for (x = 1; x < video_width - 1; x++) { /* FIXME: endianness? */ a = (src[i] & 0xfefeff) + palette[buffer[i]]; b = a & 0x1010100; // Add alpha if necessary or use src alpha. c = palette[buffer[i]] ? 0xff000000 : src[i] & 0xff000000; dest[i] = a | (b - (b >> 8)) | c; i++; } i += 2; } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_burn_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "foreground", "0"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "threshold", MAGIC_THRESHOLD); } if (!palette[128]) { makePalette(); } return filter; } mlt-7.38.0/src/modules/plusgpl/filter_burningtv.yml000664 000000 000000 00000001247 15172202314 022421 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: burningtv title: BurningTV version: 1 copyright: FUKUCHI Kentaro, Stephane Fillod creator: FUKUCHI Kentaro, Stephane Fillod contributor: - Jan Sorensen license: GPLv2 language: en tags: - Video parameters: - identifier: foreground title: Foreground description: > Whether to separate the background and burn only the foreground. The background is based upon the first frame received by the filter. type: boolean default: 0 mutable: yes - identifier: threshold title: Movement Threshold type: integer minimum: 0 maximum: 255 default: 50 mutable: yes animation: yes mlt-7.38.0/src/modules/plusgpl/filter_lumaliftgaingamma.c000664 000000 000000 00000011626 15172202314 023505 0ustar00rootroot000000 000000 /* * filter_lumaliftgaingamma.c -- Lift Gain Gamma filter for luma correction * Copyright (C) 2014 Janne Liljeblad * Author: Janne Liljeblad * * This filter is a port from Gimp and is distributed under a compatible license. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include static double clamp(double value, double low, double high) { if (value < low) value = low; if (value > high) value = high; return value; } static double lut_value(double value, double lift, double gain, double gamma) { double nvalue; double power; value = clamp(value + lift, 0, 1); if (gain < 0.0) value = value * (1.0 + gain); else value = value + ((1.0 - value) * gain); if (gamma < 0.0) { if (value > 0.5) nvalue = 1.0 - value; else nvalue = value; if (nvalue < 0.0) nvalue = 0.0; nvalue = 0.5 * pow(nvalue * 2.0, (double) (1.0 + gamma)); if (value > 0.5) value = 1.0 - nvalue; else value = nvalue; } else { if (value > 0.5) nvalue = 1.0 - value; else nvalue = value; if (nvalue < 0.0) nvalue = 0.0; power = (gamma == 1.0) ? 127 : 1.0 / (1.0 - gamma); nvalue = 0.5 * pow(2.0 * nvalue, power); if (value > 0.5) value = 1.0 - nvalue; else value = nvalue; } return value; } static void fill_lgg_lut(int lgg_lut[], double lift, double gain, double gamma) { int i; double val; for (i = 0; i < 256; i++) { val = (double) i / 255.0; lgg_lut[i] = (int) (lut_value(val, lift, gain, gamma) * 255.0); } } /** Do image filtering. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the image mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); *format = mlt_image_rgb; int error = mlt_frame_get_image(frame, image, format, width, height, 0); // Only process if we have no error and a valid colour space if (error == 0) { // Get values and force accepted ranges double lift = mlt_properties_anim_get_double(properties, "lift", position, length); double gain = mlt_properties_anim_get_double(properties, "gain", position, length); double gamma = mlt_properties_anim_get_double(properties, "gamma", position, length); lift = clamp(lift, -0.5, 0.5); gain = clamp(gain, -0.5, 0.5); gamma = clamp(gamma, -1.0, 1.0); // Build lut int lgg_lut[256]; fill_lgg_lut(lgg_lut, lift, gain, gamma); // Filter int i = *width * *height + 1; uint8_t *p = *image; uint8_t *r = *image; while (--i) { *p++ = lgg_lut[*r++]; *p++ = lgg_lut[*r++]; *p++ = lgg_lut[*r++]; } } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { // Push the frame filter mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_lumaliftgaingamma_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "lift", "0"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "gain", "0"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "gamma", "0"); } return filter; } mlt-7.38.0/src/modules/plusgpl/filter_lumaliftgaingamma.yml000664 000000 000000 00000002000 15172202314 024046 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: lumaliftgaingamma title: LumaLiftGainGamma version: 1 copyright: Janne Liljeblad creator: Janne Liljeblad license: GPL language: en tags: - Video description: > Filter can be used to apply lift, gain and gamma correction to luma values of image. parameters: - identifier: lift title: Lift type: float minimum: -0.5 maximum: 0.5 default: 0 description: > Adds a value computed using parameter value to color channel values. mutable: yes animation: yes - identifier: gain title: Gain type: float minimum: -0.5 maximum: 0.5 default: 0 description: > Multiplies color channel values by value computed using parameter value. mutable: yes animation: yes - identifier: gamma title: Gamma type: float minimum: -1.0 maximum: 1.0 default: 0 description: > Applies a gamma correction to all color channel values. mutable: yes animation: yes mlt-7.38.0/src/modules/plusgpl/filter_outline.cpp000664 000000 000000 00000021026 15172202314 022040 0ustar00rootroot000000 000000 /* * filter_outline.cpp * Copyright (C) 2025 Meltytech, LLC * Inspired by Kino DV Titler originally written by Alejandro Aguilar Sierra * https://sourceforge.net/p/kino/code/HEAD/tree/trunk/kino/src/dvtitler/dvtitler.cc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mlt++/MltFilter.h" #include "mlt++/MltProfile.h" #include #include #include #include struct slice_desc { mlt_color color; double thickness; mlt_image_s image; uint8_t *original; }; static int sliced_proc(int id, int index, int jobs, void *data) { (void) id; auto *desc = reinterpret_cast(data); auto start = 0; auto height = mlt_slices_size_slice(jobs, index, desc->image.height, &start); auto end = start + height; auto maxX = desc->image.width - 1; auto maxY = desc->image.height - 1; // Pre-calculate values outside loops for better performance auto radius = desc->thickness; auto radiusSquared = static_cast(radius * radius + 0.5); auto radiusInt = static_cast(radius + 0.5); // Fast rounding instead of ceil auto width = desc->image.width; if (desc->image.format == mlt_image_rgba) { auto stride = desc->image.strides[0]; auto colorR = desc->color.r; auto colorG = desc->color.g; auto colorB = desc->color.b; auto colorA = desc->color.a; auto inv255f = 1.f / 255.f; // Pre-calculate division for (auto y = start; y < end; y++) { auto srcRow = &desc->original[y * stride]; auto dstRow = &desc->image.planes[0][y * stride]; for (auto x = 0; x < width; x++) { auto oa = 0; // Optimized circular sampling - early exit on Y bounds for (auto dy = -radiusInt; dy <= radiusInt; dy++) { auto sampleY = y + dy; if (sampleY < 0 || sampleY > maxY) continue; auto dySquared = dy * dy; auto maxDxSquared = radiusSquared - dySquared; if (maxDxSquared < 0) continue; // Skip this row entirely auto sampleRow = &desc->original[sampleY * stride]; for (auto dx = -radiusInt; dx <= radiusInt; dx++) { auto dxSquared = dx * dx; // Skip if outside circle or center pixel if (dxSquared > maxDxSquared || (dxSquared + dySquared) == 0) continue; auto sampleX = CLAMP(x + dx, 0, maxX); auto alpha = sampleRow[sampleX * 4 + 3]; if (alpha > oa) oa = alpha; } } auto srcPixel = &srcRow[x * 4]; auto dstPixel = &dstRow[x * 4]; auto a = srcPixel[3] * inv255f; auto oaf = oa * inv255f; auto oneMinusA = 1.f - a; dstPixel[0] = a * srcPixel[0] + oneMinusA * (oaf * colorR); dstPixel[1] = a * srcPixel[1] + oneMinusA * (oaf * colorG); dstPixel[2] = a * srcPixel[2] + oneMinusA * (oaf * colorB); dstPixel[3] = a * srcPixel[3] + oneMinusA * (oaf * colorA); } } } else if (desc->image.format == mlt_image_rgba64) { int stride = desc->image.strides[0] / 2; uint16_t colorR = desc->color.r * 257; uint16_t colorG = desc->color.g * 257; uint16_t colorB = desc->color.b * 257; uint16_t colorA = desc->color.a * 257; float inv65535f = 1.f / 65535.f; // Pre-calculate division uint16_t *src = (uint16_t *) desc->original; uint16_t *dst = (uint16_t *) desc->image.planes[0]; for (auto y = start; y < end; y++) { uint16_t *s = src + y * stride; uint16_t *d = dst + y * stride; for (auto x = 0; x < width; x++) { float oa = 0; // Optimized circular sampling - early exit on Y bounds for (auto dy = -radiusInt; dy <= radiusInt; dy++) { auto sampleY = y + dy; if (sampleY < 0 || sampleY > maxY) continue; auto dySquared = dy * dy; auto maxDxSquared = radiusSquared - dySquared; if (maxDxSquared < 0) continue; // Skip this row entirely uint16_t *sampleRow = src + sampleY * stride; for (int dx = -radiusInt; dx <= radiusInt; dx++) { int dxSquared = dx * dx; // Skip if outside circle or center pixel if (dxSquared > maxDxSquared || (dxSquared + dySquared) == 0) continue; auto sampleX = CLAMP(x + dx, 0, maxX); float alpha = *(sampleRow + sampleX * 4 + 3); if (alpha > oa) oa = alpha; } } float a = s[3] * inv65535f; float oaf = oa * inv65535f; float oneMinusA = 1.f - a; d[0] = a * s[0] + oneMinusA * (oaf * colorR); d[1] = a * s[1] + oneMinusA * (oaf * colorG); d[2] = a * s[2] + oneMinusA * (oaf * colorB); d[3] = a * s[3] + oneMinusA * (oaf * colorA); s += 4; d += 4; } } } else { return 1; } return 0; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { Mlt::Filter filter(reinterpret_cast(mlt_frame_pop_service(frame))); if (*format != mlt_image_rgba64) *format = mlt_image_rgba; auto error = mlt_frame_get_image(frame, image, format, width, height, 0); if (!error) { Mlt::Frame fr(frame); auto position = filter.get_position(fr); auto length = filter.get_length2(fr); auto color = filter.anim_get_color("color", position, length); color = ::mlt_color_convert_trc(color, fr.get("color_trc")); slice_desc desc{.color = color, .thickness = filter.anim_get_double("thickness", position, length), .original = *image}; desc.thickness *= filter.profile()->scale_width(*width); mlt_image_set_values(&desc.image, nullptr, *format, *width, *height); mlt_image_alloc_data(&desc.image); filter.lock(); // Block frame-threading since the slice threads are heavy mlt_slices_run_normal(0, sliced_proc, &desc); filter.unlock(); *image = static_cast(desc.image.data); fr.set_image(*image, mlt_image_calculate_size(&desc.image), mlt_pool_release); } return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { filter->child = nullptr; filter->close = nullptr; filter->parent.close = nullptr; mlt_service_close(&filter->parent); } extern "C" { mlt_filter filter_outline_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; filter->close = filter_close; mlt_properties_set_color(MLT_FILTER_PROPERTIES(filter), "color", {255, 255, 255, 255}); mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "thickness", 4.0); } return filter; } } mlt-7.38.0/src/modules/plusgpl/filter_outline.yml000664 000000 000000 00000001233 15172202314 022055 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: outline title: Outline version: 2 copyright: Meltytech, LLC license: GPLv2 language: en tags: - Video description: > Simulate a stroke along edges within an alpha channel. It is not very smart or sophisticate, which is why you should limit the thickness and avoid hard angles. parameters: - identifier: color title: Color type: color default: '#ffffffff' mutable: yes animation: yes - identifier: thickness title: Thickness type: float minimum: 0 maximum: 20 default: 4 description: > The thickness of the outline stroke. mutable: yes animation: yes mlt-7.38.0/src/modules/plusgpl/filter_rotoscoping.c000664 000000 000000 00000055152 15172202314 022376 0ustar00rootroot000000 000000 /* * rotoscoping.c -- keyframable vector based rotoscoping * Copyright (C) 2011 Till Theato * Copyright (C) 2021 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "cJSON.h" #include #include #include #include #define SQR(x) (x) * (x) /** x, y tuple with double precision */ typedef struct PointF { double x; double y; } PointF; typedef struct BPointF { struct PointF h1; struct PointF p; struct PointF h2; } BPointF; enum MODES { MODE_RGB, MODE_ALPHA, MODE_LUMA }; const char *MODESTR[3] = {"rgb", "alpha", "luma"}; enum ALPHAOPERATIONS { ALPHA_CLEAR, ALPHA_MAX, ALPHA_MIN, ALPHA_ADD, ALPHA_SUB }; const char *ALPHAOPERATIONSTR[5] = {"clear", "max", "min", "add", "sub"}; /** Returns the index of \param string in \param stringList. * Useful for assigning string parameters to enums. */ static int stringValue(const char *string, const char **stringList, int max) { int i; for (i = 0; i < max; i++) if (strcmp(stringList[i], string) == 0) return i; return 0; } /** Sets "spline_is_dirty" to 1 if property "spline" was changed. * We then know when to parse the json stored in "spline" */ static void rotoPropertyChanged(mlt_service owner, mlt_filter this, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "spline")) mlt_properties_set_int(MLT_FILTER_PROPERTIES(this), "_spline_is_dirty", 1); } /** Linear interp */ static inline void lerp(const PointF *a, const PointF *b, PointF *result, double t) { result->x = a->x + (b->x - a->x) * t; result->y = a->y + (b->y - a->y) * t; } /** Linear interp. with t = 0.5 * Speed gain? */ static inline void lerpHalf(const PointF *a, const PointF *b, PointF *result) { result->x = (a->x + b->x) * .5; result->y = (a->y + b->y) * .5; } /** Helper for using qsort with an array of integers. */ int ncompare(const void *a, const void *b) { return *(const int *) a - *(const int *) b; } /** Turns a json array with two children into a point (x, y tuple). */ static void jsonGetPoint(cJSON *json, PointF *point) { if (cJSON_GetArraySize(json) == 2) { point->x = json->child->valuedouble; point->y = json->child->next->valuedouble; } } /** * Turns the array of json elements into an array of Bézier points. * \param array cJSON array. values have to be Bézier points: handle 1, point , handl2 * ( [ [ [h1x, h1y], [px, py], [h2x, h2y] ], ... ] ) * \param points pointer to array of points. Will be allocated and filled with the points in \param array * \return number of points */ static int json2BCurves(cJSON *array, BPointF **points) { int count = cJSON_GetArraySize(array); cJSON *child = array->child; *points = mlt_pool_alloc(count * sizeof(BPointF)); int i = 0; do { if (child && cJSON_GetArraySize(child) == 3) { jsonGetPoint(child->child, &(*points)[i].h1); jsonGetPoint(child->child->next, &(*points)[i].p); jsonGetPoint(child->child->next->next, &(*points)[i].h2); i++; } } while (child && (child = child->next)); if (i < count) *points = mlt_pool_realloc(*points, i * sizeof(BPointF)); return i; } /** Blurs \param src horizontally. \See function blur. */ static void blurHorizontal(uint8_t *src, uint8_t *dst, int width, int height, int radius) { int x, y, kx, yOff, total, amount, amountInit; amountInit = radius * 2 + 1; for (y = 0; y < height; ++y) { total = 0; yOff = y * width; // Process entire window for first pixel int size = MIN(radius + 1, width); for (kx = 0; kx < size; ++kx) total += src[yOff + kx]; dst[yOff] = total / (radius + 1); // Subsequent pixels just update window total for (x = 1; x < width; ++x) { amount = amountInit; // Subtract pixel leaving window if (x - radius - 1 >= 0) total -= src[yOff + x - radius - 1]; else amount -= radius - x; // Add pixel entering window if (x + radius < width) total += src[yOff + x + radius]; else amount -= radius - width + x; dst[yOff + x] = total / amount; } } } /** Blurs \param src vertically. \See function blur. */ static void blurVertical(uint8_t *src, uint8_t *dst, int width, int height, int radius) { int x, y, ky, total, amount, amountInit; amountInit = radius * 2 + 1; for (x = 0; x < width; ++x) { total = 0; int size = MIN(radius + 1, height); for (ky = 0; ky < size; ++ky) total += src[x + ky * width]; dst[x] = total / (radius + 1); for (y = 1; y < height; ++y) { amount = amountInit; if (y - radius - 1 >= 0) total -= src[(y - radius - 1) * width + x]; else amount -= radius - y; if (y + radius < height) total += src[(y + radius) * width + x]; else amount -= radius - height + y; dst[y * width + x] = total / amount; } } } /** * Blurs the \param map using a simple "average" blur. * \param map Will be blurred; 1bpp * \param width x dimension of channel stored in \param map * \param height y dimension of channel stored in \param map * \param radius blur radius * \param passes blur passes */ static void blur(uint8_t *map, int width, int height, int radius, int passes) { uint8_t *src = mlt_pool_alloc(width * height); uint8_t *tmp = mlt_pool_alloc(width * height); int i; for (i = 0; i < passes; ++i) { memcpy(src, map, width * height); blurHorizontal(src, tmp, width, height, radius); blurVertical(tmp, map, width, height, radius); } mlt_pool_release(src); mlt_pool_release(tmp); } /** * Determines which points are located in the polygon and sets their value in \param map to \param value * \param vertices points defining the polygon * \param count number of vertices * \param with x range * \param height y range * \param value value identifying points in the polygon * \param map array of integers of the dimension width * height. * The map entries belonging to the points in the polygon will be set to \param set * 255 the others to !set * 255. */ static void fillMap(PointF *vertices, int count, int width, int height, int invert, uint8_t *map) { int nodes, nodeX[1024], pixelY, i, j, value; value = !invert * 255; memset(map, invert * 255, width * height); // Loop through the rows of the image for (pixelY = 0; pixelY < height; pixelY++) { /* * Build a list of nodes. * nodes are located at the borders of the polygon * and therefore indicate a move from in to out or vice versa */ nodes = 0; for (i = 0, j = count - 1; i < count; j = i++) if ((vertices[i].y > (double) pixelY) != (vertices[j].y > (double) pixelY)) nodeX[nodes++] = (int) (vertices[i].x + (pixelY - vertices[i].y) / (vertices[j].y - vertices[i].y) * (vertices[j].x - vertices[i].x)); qsort(nodeX, nodes, sizeof(int), ncompare); // Set map values for points between the node pairs to 1 for (i = 0; i < nodes; i += 2) { if (nodeX[i] >= width) break; if (nodeX[i + 1] > 0) { nodeX[i] = MAX(0, nodeX[i]); nodeX[i + 1] = MIN(nodeX[i + 1], width); memset(map + width * pixelY + nodeX[i], value, nodeX[i + 1] - nodeX[i]); } } } } /** Determines the point in the middle of the Bézier curve (t = 0.5) defined by \param p1 and \param p2 * using De Casteljau's algorithm. */ static void deCasteljau(BPointF *p1, BPointF *p2, BPointF *mid) { struct PointF ab, bc, cd; lerpHalf(&(p1->p), &(p1->h2), &ab); lerpHalf(&(p1->h2), &(p2->h1), &bc); lerpHalf(&(p2->h1), &(p2->p), &cd); lerpHalf(&ab, &bc, &(mid->h1)); // mid->h1 = abbc lerpHalf(&bc, &cd, &(mid->h2)); // mid->h2 = bccd lerpHalf(&(mid->h1), &(mid->h2), &(mid->p)); p1->h2 = ab; p2->h1 = cd; } /** * Calculates points for the cubic Bézier curve defined by \param p1 and \param p2. * Points are calculated until the squared distanced between neighbour points is smaller than 2. * \param points Pointer to list of points. Will be allocated and filled with calculated points. * \param count Number of calculated points in \param points * \param size Allocated size of \param points (in elements not in bytes) */ static void curvePoints(BPointF p1, BPointF p2, PointF **points, int *count, int *size) { double errorSqr = SQR(p1.p.x - p2.p.x) + SQR(p1.p.y - p2.p.y); if (*count + 1 >= *size) { *size += (int) sqrt(errorSqr / 2) + 1; *points = mlt_pool_realloc(*points, *size * sizeof(struct PointF)); } (*points)[(*count)++] = p1.p; if (errorSqr <= 2) return; BPointF mid; deCasteljau(&p1, &p2, &mid); curvePoints(p1, mid, points, count, size); curvePoints(mid, p2, points, count, size); (*points)[*(count)++] = p2.p; } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_properties unique = mlt_frame_pop_service(frame); int mode = mlt_properties_get_int(unique, "mode"); // Rotoscoping points are based on the profile size / aspect ratio, so check the requested image matches profile mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE((mlt_filter) unique)); int normalized_width = profile->width; int normalized_height = profile->height; // Special case - aspect_ratio = 0 if (mlt_frame_get_aspect_ratio(frame) == 0) { double output_ar = mlt_profile_sar(profile); mlt_frame_set_aspect_ratio(frame, output_ar); } double b_ar = mlt_frame_get_aspect_ratio(frame); int scalex = *width; int scaley = *height; int offsetx = 0; int offsety = 0; // Compare aspect ratio if (*height > 0 && 100 * *width / *height != 100 * normalized_width / normalized_height) { // Source has a different aspect ratio, apply scaling double xfactor = normalized_width / *width; double yfactor = normalized_height / *height; if (xfactor < yfactor) { // Image will be stretched horizontally scalex = *width / b_ar; scaley = *width * normalized_height / normalized_width / b_ar; } else { // Image will be stretched vertically scaley = *height / b_ar; scalex = *height * normalized_width / normalized_height / b_ar; } offsetx = (scalex - *width) / 2; offsety = (scaley - *height) / 2; } // Get the image if (mode == MODE_RGB) *format = mlt_image_rgb; int error = mlt_frame_get_image(frame, image, format, width, height, writable); // Only process if we have no error and a valid colour space if (!error) { BPointF *bpoints; struct PointF *points; int bcount, length, count, size, i, j; bpoints = mlt_properties_get_data(unique, "points", &length); bcount = length / sizeof(BPointF); for (i = 0; i < bcount; i++) { // map to image dimensions bpoints[i].h1.x *= scalex; bpoints[i].p.x *= scalex; bpoints[i].h2.x *= scalex; bpoints[i].h1.x -= offsetx; bpoints[i].p.x -= offsetx; bpoints[i].h2.x -= offsetx; bpoints[i].h1.y *= scaley; bpoints[i].p.y *= scaley; bpoints[i].h2.y *= scaley; bpoints[i].h1.y -= offsety; bpoints[i].p.y -= offsety; bpoints[i].h2.y -= offsety; } count = 0; size = 1; points = mlt_pool_alloc(size * sizeof(struct PointF)); for (i = 0; i < bcount; i++) { j = (i + 1) % bcount; curvePoints(bpoints[i], bpoints[j], &points, &count, &size); } if (count) { length = *width * *height; uint8_t *map = mlt_pool_alloc(length); int invert = mlt_properties_get_int(unique, "invert"); fillMap(points, count, *width, *height, invert, map); int feather = mlt_properties_get_int(unique, "feather"); if (feather && mode != MODE_RGB) { // Adapt feathering to consumer scaling double scale_width = mlt_profile_scale_width(profile, *width); feather = MAX(1, (int) (feather * scale_width)); blur(map, *width, *height, feather, mlt_properties_get_int(unique, "feather_passes")); } int bpp; size = mlt_image_format_size(*format, *width, *height, &bpp); uint8_t *p = *image; uint8_t *q = *image + size; i = 0; uint8_t *alpha; switch (mode) { case MODE_RGB: // *format == mlt_image_rgb while (p != q) { if (!map[i++]) p[0] = p[1] = p[2] = 0; p += 3; } break; case MODE_LUMA: switch (*format) { case mlt_image_rgb: case mlt_image_rgba: while (p != q) { p[0] = p[1] = p[2] = map[i++]; p += bpp; } break; case mlt_image_yuv422: while (p != q) { p[0] = map[i++]; p[1] = 128; p += 2; } break; case mlt_image_yuv420p: memcpy(p, map, length); memset(p + length, 128, length / 2); break; default: break; } break; case MODE_ALPHA: switch (*format) { case mlt_image_rgba: switch (mlt_properties_get_int(unique, "alpha_operation")) { case ALPHA_CLEAR: while (p != q) { p[3] = map[i++]; p += 4; } break; case ALPHA_MAX: while (p != q) { p[3] = MAX(p[3], map[i]); p += 4; i++; } break; case ALPHA_MIN: while (p != q) { p[3] = MIN(p[3], map[i]); p += 4; i++; } break; case ALPHA_ADD: while (p != q) { p[3] = MIN(p[3] + map[i], 255); p += 4; i++; } break; case ALPHA_SUB: while (p != q) { p[3] = MAX(p[3] - map[i], 0); p += 4; i++; } break; } break; default: alpha = mlt_frame_get_alpha(frame); if (!alpha) { alpha = mlt_pool_alloc(length); memset(alpha, 255, length); mlt_frame_set_alpha(frame, alpha, length, mlt_pool_release); } switch (mlt_properties_get_int(unique, "alpha_operation")) { case ALPHA_CLEAR: memcpy(alpha, map, length); break; case ALPHA_MAX: for (; i < length; i++, alpha++) *alpha = MAX(map[i], *alpha); break; case ALPHA_MIN: for (; i < length; i++, alpha++) *alpha = MIN(map[i], *alpha); break; case ALPHA_ADD: for (; i < length; i++, alpha++) *alpha = MIN(*alpha + map[i], 255); break; case ALPHA_SUB: for (; i < length; i++, alpha++) *alpha = MAX(*alpha - map[i], 0); break; } break; } break; } mlt_pool_release(map); } mlt_pool_release(points); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); int splineIsDirty = mlt_properties_get_int(properties, "_spline_is_dirty"); char *modeStr = mlt_properties_get(properties, "mode"); cJSON *root = mlt_properties_get_data(properties, "_spline_parsed", NULL); if (splineIsDirty || root == NULL) { // we need to (re-)parse char *spline = mlt_properties_get(properties, "spline"); root = cJSON_Parse(spline); mlt_properties_set_data(properties, "_spline_parsed", root, 0, (mlt_destructor) cJSON_Delete, NULL); mlt_properties_set_int(properties, "_spline_is_dirty", 0); } if (root == NULL) return frame; BPointF *points; int count, i; if (root->type == cJSON_Array) { /* * constant */ count = json2BCurves(root, &points); } else if (root->type == cJSON_Object) { /* * keyframes */ mlt_position time, pos1, pos2; time = mlt_frame_get_position(frame); cJSON *keyframe = root->child; cJSON *keyframeOld = keyframe; if (!keyframe) return frame; while (atoi(keyframe->string) < time && keyframe->next) { keyframeOld = keyframe; keyframe = keyframe->next; } pos1 = atoi(keyframeOld->string); pos2 = atoi(keyframe->string); if (pos1 >= pos2 || time >= pos2) { // keyframes in wrong order or before first / after last keyframe count = json2BCurves(keyframe, &points); } else { /* * pos1 < time < pos2 */ BPointF *p1, *p2; int c1 = json2BCurves(keyframeOld, &p1); int c2 = json2BCurves(keyframe, &p2); // range 0-1 double position = (time - pos1) / (double) (pos2 - pos1); count = MIN(c1, c2); // additional points are ignored points = mlt_pool_alloc(count * sizeof(BPointF)); for (i = 0; i < count; i++) { lerp(&(p1[i].h1), &(p2[i].h1), &(points[i].h1), position); lerp(&(p1[i].p), &(p2[i].p), &(points[i].p), position); lerp(&(p1[i].h2), &(p2[i].h2), &(points[i].h2), position); } mlt_pool_release(p1); mlt_pool_release(p2); } } else { return frame; } mlt_properties unique = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(filter)); mlt_properties_set_data(unique, "points", points, count * sizeof(BPointF), (mlt_destructor) mlt_pool_release, NULL); mlt_properties_set_int(unique, "mode", stringValue(modeStr, MODESTR, 3)); mlt_properties_set_int(unique, "alpha_operation", stringValue(mlt_properties_get(properties, "alpha_operation"), ALPHAOPERATIONSTR, 5)); mlt_properties_set_int(unique, "invert", mlt_properties_get_int(properties, "invert")); mlt_properties_set_int(unique, "feather", mlt_properties_get_int(properties, "feather")); mlt_properties_set_int(unique, "feather_passes", mlt_properties_get_int(properties, "feather_passes")); mlt_frame_push_service(frame, unique); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_rotoscoping_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set(properties, "mode", "alpha"); mlt_properties_set(properties, "alpha_operation", "clear"); mlt_properties_set_int(properties, "invert", 0); mlt_properties_set_int(properties, "feather", 0); mlt_properties_set_int(properties, "feather_passes", 1); if (arg) mlt_properties_set(properties, "spline", arg); mlt_events_listen(properties, filter, "property-changed", (mlt_listener) rotoPropertyChanged); } return filter; } mlt-7.38.0/src/modules/plusgpl/filter_rotoscoping.yml000664 000000 000000 00000005130 15172202314 022744 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: rotoscoping title: Rotoscoping copyright: Copyright (C) 2011 Till Theato version: 0.3 license: GPL language: en creator: Till Theato tags: - Video description: Keyframable vector based rotoscoping bugs: - in some cases top most row in polygon is assigned to outside parameters: - identifier: mode title: Mode type: string description: How to visualize the area described by the spline readonly: no required: no default: alpha mutable: yes widget: dropdown values: - alpha - luma - rgb - identifier: alpha_operation title: Alpha Operation type: string description: | How to proceed with the current alpha mask (only if mode = alpha). clear = existing alpha mask is overwritten max = maximum: existing alpha mask, mask generated by this filter min = minimum: existing alpha mask, mask generated by this filter add = existing alpha mask + generated mask sub = existing alpha mask - generated mask readonly: no required: no default: clear mutable: yes widget: dropdown values: - clear - max - min - add - sub - identifier: invert title: Invert type: integer description: use area inside of spline (0) or the outside (1) readonly: no required: no minimum: 0 maximum: 1 default: 0 mutable: yes widget: checkbox - identifier: feather title: Feather type: integer description: amount of feathering (radius of "average" blur applied on mask) readonly: no required: no minimum: 0 maximum: 1000 # no real limit default: 0 mutable: yes widget: spinner - identifier: feather_passes title: Feathering passes type: integer description: number of blur (feathering) passes readonly: no required: no minimum: 1 maximum: 1000 # no real limit default: 1 mutable: yes widget: spinner - identifier: spline title: Spline type: string description: > The spline, a list of cubic Bézier curves, is described using JSON. The most basic parts are the coordinate tuples: [x, y]; x,y will be mapped from the range 0-1 to the image dimensions. Next layer are the Bézier points: [handle 1, point, handle 2] with handle 1, point, handle 2 being coordinate tuples The spline is a list of Bézier points. Optionally keyframes can be defined as a object of frame values, relative to the filter's in point, assigned to splines. readonly: no required: yes mutable: yes mlt-7.38.0/src/modules/plusgpl/filter_telecide.c000664 000000 000000 00000134115 15172202314 021603 0ustar00rootroot000000 000000 /* * filter_telecide.c -- Donald Graft's Inverse Telecine Filter * Copyright (C) 2003 Donald A. Graft * Copyright (C) 2008 Dan Dennedy * Author: Dan Dennedy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #define MAX_CYCLE 6 #define BLKSIZE 24 #define BLKSIZE_TIMES2 (2 * BLKSIZE) #define GUIDE_32 1 #define GUIDE_22 2 #define GUIDE_32322 3 #define AHEAD 0 #define BEHIND 1 #define POST_METRICS 1 #define POST_FULL 2 #define POST_FULL_MAP 3 #define POST_FULL_NOMATCH 4 #define POST_FULL_NOMATCH_MAP 5 #define CACHE_SIZE 100000 #define P 0 #define C 1 #define N 2 #define PBLOCK 3 #define CBLOCK 4 #define NO_BACK 0 #define BACK_ON_COMBED 1 #define ALWAYS_BACK 2 struct CACHE_ENTRY { unsigned int frame; unsigned int metrics[5]; unsigned int chosen; }; struct PREDICTION { unsigned int metric; unsigned int phase; unsigned int predicted; unsigned int predicted_metric; }; struct context_s { int is_configured; mlt_properties image_cache; int out; int tff, chroma, blend, hints, show, debug; float dthresh, gthresh, vthresh, vthresh_saved, bthresh; int y0, y1, nt, guide, post, back, back_saved; int pitch, dpitch, pitchover2, pitchtimes4; int w, h, wover2, hover2, hplus1over2, hminus2; int xblocks, yblocks; #ifdef WINDOWED_MATCH unsigned int *matchc, *matchp, highest_matchc, highest_matchp; #endif unsigned int *sumc, *sump, highest_sumc, highest_sump; int vmetric; unsigned int *overrides, *overrides_p; int film, override, inpattern, found; int force; // Used by field matching. unsigned char *fprp, *fcrp, *fcrp_saved, *fnrp; unsigned char *dstp, *finalp; int chosen; unsigned int p, c, pblock, cblock, lowest, predicted, predicted_metric; unsigned int np, nc, npblock, ncblock, nframe; float mismatch; int pframe, x, y; unsigned char *crp, *prp; unsigned char *crpU, *prpU; unsigned char *crpV, *prpV; int hard; char status[80]; // Metrics cache. struct CACHE_ENTRY *cache; // Pattern guidance data. int cycle; struct PREDICTION pred[MAX_CYCLE + 1]; }; typedef struct context_s *context; static inline void BitBlt( uint8_t *dstp, int dst_pitch, const uint8_t *srcp, int src_pitch, int row_size, int height) { uint32_t y; for (y = 0; y < height; y++) { memcpy(dstp, srcp, row_size); dstp += dst_pitch; srcp += src_pitch; } } static void Show(context cx, int frame, mlt_properties properties) { char use; char buf1[64] = {0}; char buf2[64] = {0}; char buf3[64] = {0}; char buf4[512] = {0}; if (cx->chosen == P) use = 'p'; else if (cx->chosen == C) use = 'c'; else use = 'n'; snprintf(buf1, sizeof(buf1), "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np); if (cx->post) { snprintf(buf2, sizeof(buf2), "Telecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric); } if (cx->guide) { snprintf(buf3, sizeof(buf3), "pattern mismatch=%0.2f%%\n", cx->mismatch); } snprintf(buf4, sizeof(buf4), "%s%s%sTelecide: frame %d: [%s %c]%s %s\n", buf1, buf2, buf3, frame, cx->found ? "forcing" : "using", use, cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "", cx->guide ? cx->status : ""); mlt_properties_set(properties, "meta.attr.telecide.markup", buf4); } static void Debug(context cx, int frame) { char use; if (cx->chosen == P) use = 'p'; else if (cx->chosen == C) use = 'c'; else use = 'n'; fprintf(stderr, "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np); if (cx->post) fprintf(stderr, "Telecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric); if (cx->guide) fprintf(stderr, "pattern mismatch=%0.2f%%\n", cx->mismatch); fprintf(stderr, "Telecide: frame %d: [%s %c]%s %s\n", frame, cx->found ? "forcing" : "using", use, cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "", cx->guide ? cx->status : ""); } static void WriteHints(int film, int inpattern, mlt_properties frame_properties) { mlt_properties_set_int(frame_properties, "telecide.progressive", film); mlt_properties_set_int(frame_properties, "telecide.in_pattern", inpattern); } static void PutChosen(context cx, int frame, unsigned int chosen) { int f = frame % CACHE_SIZE; if (frame < 0 || frame > cx->out || cx->cache[f].frame != frame) return; cx->cache[f].chosen = chosen; } static void CacheInsert( context cx, int frame, unsigned int p, unsigned int pblock, unsigned int c, unsigned int cblock) { int f = frame % CACHE_SIZE; if (frame < 0 || frame > cx->out) fprintf(stderr, "%s: internal error: invalid frame %d for CacheInsert", __FUNCTION__, frame); cx->cache[f].frame = frame; cx->cache[f].metrics[P] = p; if (f) cx->cache[f - 1].metrics[N] = p; cx->cache[f].metrics[C] = c; cx->cache[f].metrics[PBLOCK] = pblock; cx->cache[f].metrics[CBLOCK] = cblock; cx->cache[f].chosen = 0xff; } static int CacheQuery(context cx, int frame, unsigned int *p, unsigned int *pblock, unsigned int *c, unsigned int *cblock) { int f; f = frame % CACHE_SIZE; if (frame < 0 || frame > cx->out) fprintf(stderr, "%s: internal error: invalid frame %d for CacheQuery", __FUNCTION__, frame); if (cx->cache[f].frame != frame) { return 0; } *p = cx->cache[f].metrics[P]; *c = cx->cache[f].metrics[C]; *pblock = cx->cache[f].metrics[PBLOCK]; *cblock = cx->cache[f].metrics[CBLOCK]; return 1; } static int PredictHardYUY2(context cx, int frame, unsigned int *predicted, unsigned int *predicted_metric) { // Look for pattern in the actual delivered matches of the previous cycle of frames. // If a pattern is found, use that to predict the current match. if (cx->guide == GUIDE_22) { if (cx->cache[(frame - cx->cycle) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 1) % CACHE_SIZE].chosen == 0xff) return 0; switch ((cx->cache[(frame - cx->cycle) % CACHE_SIZE].chosen << 4) + (cx->cache[(frame - cx->cycle + 1) % CACHE_SIZE].chosen)) { case 0x11: *predicted = C; *predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case 0x22: *predicted = N; *predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; default: return 0; } } else if (cx->guide == GUIDE_32) { if (cx->cache[(frame - cx->cycle) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 1) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 2) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 3) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 4) % CACHE_SIZE].chosen == 0xff) return 0; switch ((cx->cache[(frame - cx->cycle) % CACHE_SIZE].chosen << 16) + (cx->cache[(frame - cx->cycle + 1) % CACHE_SIZE].chosen << 12) + (cx->cache[(frame - cx->cycle + 2) % CACHE_SIZE].chosen << 8) + (cx->cache[(frame - cx->cycle + 3) % CACHE_SIZE].chosen << 4) + (cx->cache[(frame - cx->cycle + 4) % CACHE_SIZE].chosen)) { case 0x11122: case 0x11221: case 0x12211: case 0x12221: case 0x21122: case 0x11222: *predicted = C; *predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case 0x22111: case 0x21112: case 0x22112: case 0x22211: *predicted = N; *predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; default: return 0; } } else if (cx->guide == GUIDE_32322) { if (cx->cache[(frame - cx->cycle) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 1) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 2) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 3) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 4) % CACHE_SIZE].chosen == 0xff || cx->cache[(frame - cx->cycle + 5) % CACHE_SIZE].chosen == 0xff) return 0; switch ((cx->cache[(frame - cx->cycle) % CACHE_SIZE].chosen << 20) + (cx->cache[(frame - cx->cycle + 1) % CACHE_SIZE].chosen << 16) + (cx->cache[(frame - cx->cycle + 2) % CACHE_SIZE].chosen << 12) + (cx->cache[(frame - cx->cycle + 3) % CACHE_SIZE].chosen << 8) + (cx->cache[(frame - cx->cycle + 4) % CACHE_SIZE].chosen << 4) + (cx->cache[(frame - cx->cycle + 5) % CACHE_SIZE].chosen)) { case 0x111122: case 0x111221: case 0x112211: case 0x122111: case 0x111222: case 0x112221: case 0x122211: case 0x222111: *predicted = C; *predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case 0x221111: case 0x211112: case 0x221112: case 0x211122: *predicted = N; *predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; default: return 0; } } #ifdef DEBUG_PATTERN_GUIDANCE fprintf(stderr, "%s: pos=%d HARD: predicted=%d\n", __FUNCTION__, frame, *predicted); #endif return 1; } static struct PREDICTION *PredictSoftYUY2(context cx, int frame) { // Use heuristics to look forward for a match. int i, j, y, c, n, phase; unsigned int metric; cx->pred[0].metric = 0xffffffff; if (frame < 0 || frame > cx->out - cx->cycle) return cx->pred; // Look at the next cycle of frames. for (y = frame + 1; y <= frame + cx->cycle; y++) { // Look for a frame where the current and next match values are // very close. Those are candidates to predict the phase, because // that condition should occur only once per cycle. Store the candidate // phases and predictions in a list sorted by goodness. The list will // be used by the caller to try the phases in order. c = cx->cache[y % CACHE_SIZE].metrics[C]; n = cx->cache[y % CACHE_SIZE].metrics[N]; if (c == 0) c = 1; metric = (100 * abs(c - n)) / c; phase = y % cx->cycle; if (metric < 5) { // Place the new candidate phase in sorted order in the list. // Find the insertion point. i = 0; while (metric > cx->pred[i].metric) i++; // Find the end-of-list marker. j = 0; while (cx->pred[j].metric != 0xffffffff) j++; // Shift all items below the insertion point down by one to make // room for the insertion. j++; for (; j > i; j--) { cx->pred[j].metric = cx->pred[j - 1].metric; cx->pred[j].phase = cx->pred[j - 1].phase; cx->pred[j].predicted = cx->pred[j - 1].predicted; cx->pred[j].predicted_metric = cx->pred[j - 1].predicted_metric; } // Insert the new candidate data. cx->pred[j].metric = metric; cx->pred[j].phase = phase; if (cx->guide == GUIDE_32) { switch ((frame % cx->cycle) - phase) { case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; case -3: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case 0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; } } else if (cx->guide == GUIDE_32322) { switch ((frame % cx->cycle) - phase) { case -5: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; case -3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case 0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[N]; break; case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; case +5: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame % CACHE_SIZE].metrics[C]; break; } } } #ifdef DEBUG_PATTERN_GUIDANCE fprintf(stderr, "%s: pos=%d metric=%d phase=%d\n", __FUNCTION__, frame, metric, phase); #endif } return cx->pred; } static void CalculateMetrics(context cx, int frame, unsigned char *fcrp, unsigned char *fcrpU, unsigned char *fcrpV, unsigned char *fprp, unsigned char *fprpU, unsigned char *fprpV) { int x, y, p, c, tmp1, tmp2, skip; int vc; unsigned char *currbot0, *currbot2, *prevbot0, *prevbot2; unsigned char *prevtop0, *prevtop2, *prevtop4, *currtop0, *currtop2, *currtop4; unsigned char *a0, *a2, *b0, *b2, *b4; unsigned int diff, index; #define T 4 /* Clear the block sums. */ for (y = 0; y < cx->yblocks; y++) { for (x = 0; x < cx->xblocks; x++) { #ifdef WINDOWED_MATCH matchp[y * xblocks + x] = 0; matchc[y * xblocks + x] = 0; #endif cx->sump[y * cx->xblocks + x] = 0; cx->sumc[y * cx->xblocks + x] = 0; } } /* Find the best field match. Subsample the frames for speed. */ currbot0 = fcrp + cx->pitch; currbot2 = fcrp + 3 * cx->pitch; currtop0 = fcrp; currtop2 = fcrp + 2 * cx->pitch; currtop4 = fcrp + 4 * cx->pitch; prevbot0 = fprp + cx->pitch; prevbot2 = fprp + 3 * cx->pitch; prevtop0 = fprp; prevtop2 = fprp + 2 * cx->pitch; prevtop4 = fprp + 4 * cx->pitch; if (cx->tff) { a0 = prevbot0; a2 = prevbot2; b0 = currtop0; b2 = currtop2; b4 = currtop4; } else { a0 = currbot0; a2 = currbot2; b0 = prevtop0; b2 = prevtop2; b4 = prevtop4; } p = c = 0; // Calculate the field match and film/video metrics. skip = 1 + (!cx->chroma); for (y = 0, index = 0; y < cx->h - 4; y += 4) { /* Exclusion band. Good for ignoring subtitles. */ if (cx->y0 == cx->y1 || y < cx->y0 || y > cx->y1) { for (x = 0; x < cx->w;) { index = (y / BLKSIZE) * cx->xblocks + x / BLKSIZE_TIMES2; // Test combination with current frame. tmp1 = ((long) currbot0[x] + (long) currbot2[x]); diff = labs((((long) currtop0[x] + (long) currtop2[x] + (long) currtop4[x])) - (tmp1 >> 1) - tmp1); if (diff > cx->nt) { c += diff; #ifdef WINDOWED_MATCH matchc[index] += diff; #endif } tmp1 = currbot0[x] + T; tmp2 = currbot0[x] - T; vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) || (tmp2 > currtop0[x] && tmp2 > currtop2[x]); if (vc) { cx->sumc[index]++; } // Test combination with previous frame. tmp1 = ((long) a0[x] + (long) a2[x]); diff = labs((((long) b0[x] + (long) b2[x] + (long) b4[x])) - (tmp1 >> 1) - tmp1); if (diff > cx->nt) { p += diff; #ifdef WINDOWED_MATCH matchp[index] += diff; #endif } tmp1 = a0[x] + T; tmp2 = a0[x] - T; vc = (tmp1 < b0[x] && tmp1 < b2[x]) || (tmp2 > b0[x] && tmp2 > b2[x]); if (vc) { cx->sump[index]++; } x += skip; if (!(x & 3)) x += 4; } } currbot0 += cx->pitchtimes4; currbot2 += cx->pitchtimes4; currtop0 += cx->pitchtimes4; currtop2 += cx->pitchtimes4; currtop4 += cx->pitchtimes4; a0 += cx->pitchtimes4; a2 += cx->pitchtimes4; b0 += cx->pitchtimes4; b2 += cx->pitchtimes4; b4 += cx->pitchtimes4; } if (cx->post) { cx->highest_sump = 0; for (y = 0; y < cx->yblocks; y++) { for (x = 0; x < cx->xblocks; x++) { if (cx->sump[y * cx->xblocks + x] > cx->highest_sump) { cx->highest_sump = cx->sump[y * cx->xblocks + x]; } } } cx->highest_sumc = 0; for (y = 0; y < cx->yblocks; y++) { for (x = 0; x < cx->xblocks; x++) { if (cx->sumc[y * cx->xblocks + x] > cx->highest_sumc) { cx->highest_sumc = cx->sumc[y * cx->xblocks + x]; } } } } #ifdef WINDOWED_MATCH CacheInsert(frame, highest_matchp, highest_sump, highest_matchc, highest_sumc); #else CacheInsert(cx, frame, p, cx->highest_sump, c, cx->highest_sumc); #endif } /** Process the image. */ static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { // Get the filter service mlt_filter filter = mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_properties = mlt_frame_properties(frame); context cx = mlt_properties_get_data(properties, "context", NULL); mlt_service producer = mlt_service_producer(mlt_filter_service(filter)); cx->out = producer ? mlt_producer_get_playtime(MLT_PRODUCER(producer)) : 999999; if (!cx->is_configured) { cx->back = mlt_properties_get_int(properties, "back"); cx->chroma = mlt_properties_get_int(properties, "chroma"); cx->guide = mlt_properties_get_int(properties, "guide"); cx->gthresh = mlt_properties_get_double(properties, "gthresh"); cx->post = mlt_properties_get_int(properties, "post"); cx->vthresh = mlt_properties_get_double(properties, "vthresh"); cx->bthresh = mlt_properties_get_double(properties, "bthresh"); cx->dthresh = mlt_properties_get_double(properties, "dthresh"); cx->blend = mlt_properties_get_int(properties, "blend"); cx->nt = mlt_properties_get_int(properties, "nt"); cx->y0 = mlt_properties_get_int(properties, "y0"); cx->y1 = mlt_properties_get_int(properties, "y1"); cx->hints = mlt_properties_get_int(properties, "hints"); cx->debug = mlt_properties_get_int(properties, "debug"); cx->show = mlt_properties_get_int(properties, "show"); } // Get the image int error = mlt_frame_get_image(frame, image, format, width, height, 1); if (!cx->sump) { int guide = mlt_properties_get_int(properties, "guide"); cx->cycle = 0; if (guide == GUIDE_32) { // 24fps to 30 fps telecine. cx->cycle = 5; } else if (guide == GUIDE_22) { // PAL guidance (expect the current match to be continued). cx->cycle = 2; } else if (guide == GUIDE_32322) { // 25fps to 30 fps telecine. cx->cycle = 6; } cx->xblocks = (*width + BLKSIZE - 1) / BLKSIZE; cx->yblocks = (*height + BLKSIZE - 1) / BLKSIZE; cx->sump = (unsigned int *) mlt_pool_alloc(cx->xblocks * cx->yblocks * sizeof(unsigned int)); cx->sumc = (unsigned int *) mlt_pool_alloc(cx->xblocks * cx->yblocks * sizeof(unsigned int)); mlt_properties_set_data(properties, "sump", cx->sump, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor) mlt_pool_release, NULL); mlt_properties_set_data(properties, "sumc", cx->sumc, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor) mlt_pool_release, NULL); cx->tff = mlt_properties_get_int(frame_properties, "top_field_first"); } // Only process if we have no error and a valid colour space if (error == 0 && *format == mlt_image_yuv422) { // Put the current image into the image cache, keyed on position size_t image_size = (*width * *height) << 1; mlt_position pos = mlt_filter_get_position(filter, frame); uint8_t *image_copy = mlt_pool_alloc(image_size); memcpy(image_copy, *image, image_size); char key[20]; sprintf(key, MLT_POSITION_FMT, pos); mlt_properties_set_data(cx->image_cache, key, image_copy, image_size, (mlt_destructor) mlt_pool_release, NULL); // Only if we have enough frame images cached if (pos > 1 && pos > cx->cycle + 1) { pos -= cx->cycle + 1; // Get the current frame image sprintf(key, MLT_POSITION_FMT, pos); cx->fcrp = mlt_properties_get_data(cx->image_cache, key, NULL); if (!cx->fcrp) return error; // Get the previous frame image cx->pframe = pos == 0 ? 0 : pos - 1; sprintf(key, "%d", cx->pframe); cx->fprp = mlt_properties_get_data(cx->image_cache, key, NULL); if (!cx->fprp) return error; // Get the next frame image cx->nframe = pos > cx->out ? cx->out : pos + 1; sprintf(key, "%d", cx->nframe); cx->fnrp = mlt_properties_get_data(cx->image_cache, key, NULL); if (!cx->fnrp) return error; cx->pitch = *width << 1; cx->pitchover2 = cx->pitch >> 1; cx->pitchtimes4 = cx->pitch << 2; cx->w = *width << 1; cx->h = *height; if ((cx->w / 2) & 1) fprintf(stderr, "%s: width must be a multiple of 2\n", __FUNCTION__); if (cx->h & 1) fprintf(stderr, "%s: height must be a multiple of 2\n", __FUNCTION__); cx->wover2 = cx->w / 2; cx->hover2 = cx->h / 2; cx->hplus1over2 = (cx->h + 1) / 2; cx->hminus2 = cx->h - 2; cx->dpitch = cx->pitch; // Ensure that the metrics for the frames // after the current frame are in the cache. They will be used for // pattern guidance. if (cx->guide) { for (cx->y = pos + 1; (cx->y <= pos + cx->cycle + 1) && (cx->y <= cx->out); cx->y++) { if (!CacheQuery(cx, cx->y, &cx->p, &cx->pblock, &cx->c, &cx->cblock)) { sprintf(key, "%d", cx->y); cx->crp = (unsigned char *) mlt_properties_get_data(cx->image_cache, key, NULL); sprintf(key, "%d", cx->y ? cx->y - 1 : 1); cx->prp = (unsigned char *) mlt_properties_get_data(cx->image_cache, key, NULL); CalculateMetrics(cx, cx->y, cx->crp, NULL, NULL, cx->prp, NULL, NULL); } } } // Check for manual overrides of the field matching. cx->found = 0; cx->film = 1; cx->override = 0; cx->inpattern = 0; cx->back = cx->back_saved; // Get the metrics for the current-previous (p), current-current (c), and current-next (n) match candidates. if (!CacheQuery(cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock)) { CalculateMetrics(cx, pos, cx->fcrp, NULL, NULL, cx->fprp, NULL, NULL); CacheQuery(cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock); } if (!CacheQuery(cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock)) { CalculateMetrics(cx, cx->nframe, cx->fnrp, NULL, NULL, cx->fcrp, NULL, NULL); CacheQuery(cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock); } // Determine the best candidate match. if (!cx->found) { cx->lowest = cx->c; cx->chosen = C; if (cx->back == ALWAYS_BACK && cx->p < cx->lowest) { cx->lowest = cx->p; cx->chosen = P; } if (cx->np < cx->lowest) { cx->lowest = cx->np; cx->chosen = N; } } if ((pos == 0 && cx->chosen == P) || (pos == cx->out && cx->chosen == N)) { cx->chosen = C; cx->lowest = cx->c; } // See if we can apply pattern guidance. cx->mismatch = 100.0; if (cx->guide) { cx->hard = 0; if (pos >= cx->cycle && PredictHardYUY2(cx, pos, &cx->predicted, &cx->predicted_metric)) { cx->inpattern = 1; cx->mismatch = 0.0; cx->hard = 1; if (cx->chosen != cx->predicted) { // The chosen frame doesn't match the prediction. if (cx->predicted_metric == 0) cx->mismatch = 0.0; else cx->mismatch = (100.0 * (cx->predicted_metric - cx->lowest)) / cx->predicted_metric; if (cx->mismatch < cx->gthresh) { // It's close enough, so use the predicted one. if (!cx->found) { cx->chosen = cx->predicted; cx->override = 1; } } else { cx->hard = 0; cx->inpattern = 0; } } } if (!cx->hard && cx->guide != GUIDE_22) { int i; struct PREDICTION *pred = PredictSoftYUY2(cx, pos); if ((pos <= cx->out - cx->cycle) && (pred[0].metric != 0xffffffff)) { // Apply pattern guidance. // If the predicted match metric is within defined percentage of the // best calculated one, then override the calculated match with the // predicted match. i = 0; while (pred[i].metric != 0xffffffff) { cx->predicted = pred[i].predicted; cx->predicted_metric = pred[i].predicted_metric; #ifdef DEBUG_PATTERN_GUIDANCE fprintf(stderr, "%s: pos=%d predicted=%d\n", __FUNCTION__, pos, cx->predicted); #endif if (cx->chosen != cx->predicted) { // The chosen frame doesn't match the prediction. if (cx->predicted_metric == 0) cx->mismatch = 0.0; else cx->mismatch = (100.0 * (cx->predicted_metric - cx->lowest)) / cx->predicted_metric; if ((int) cx->mismatch <= cx->gthresh) { // It's close enough, so use the predicted one. if (!cx->found) { cx->chosen = cx->predicted; cx->override = 1; } cx->inpattern = 1; break; } else { // Looks like we're not in a predictable pattern. cx->inpattern = 0; } } else { cx->inpattern = 1; cx->mismatch = 0.0; break; } i++; } } } } // Check the match for progressive versus interlaced. if (cx->post) { if (cx->chosen == P) cx->vmetric = cx->pblock; else if (cx->chosen == C) cx->vmetric = cx->cblock; else if (cx->chosen == N) cx->vmetric = cx->npblock; if (!cx->found && cx->back == BACK_ON_COMBED && cx->vmetric > cx->bthresh && cx->p < cx->lowest) { // Backward match. cx->vmetric = cx->pblock; cx->chosen = P; cx->inpattern = 0; cx->mismatch = 100; } if (cx->vmetric > cx->vthresh) { // After field matching and pattern guidance the frame is still combed. cx->film = 0; if (!cx->found && (cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP)) { cx->chosen = C; cx->vmetric = cx->cblock; cx->inpattern = 0; cx->mismatch = 100; } } } cx->vthresh = cx->vthresh_saved; // Setup strings for debug info. if (cx->inpattern && !cx->override) strcpy(cx->status, "[in-pattern]"); else if (cx->inpattern && cx->override) strcpy(cx->status, "[in-pattern*]"); else strcpy(cx->status, "[out-of-pattern]"); // Assemble and output the reconstructed frame according to the final match. cx->dstp = *image; if (cx->chosen == N) { // The best match was with the next frame. if (cx->tff) { BitBlt(cx->dstp, 2 * cx->dpitch, cx->fnrp, 2 * cx->pitch, cx->w, cx->hover2); BitBlt(cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2); } else { BitBlt(cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2); BitBlt(cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fnrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2); } } else if (cx->chosen == C) { // The best match was with the current frame. BitBlt(cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2); BitBlt(cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2); } else if (!cx->tff) { // The best match was with the previous frame. BitBlt(cx->dstp, 2 * cx->dpitch, cx->fprp, 2 * cx->pitch, cx->w, cx->hplus1over2); BitBlt(cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2); } else { // The best match was with the previous frame. BitBlt(cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2); BitBlt(cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fprp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2); } if (cx->guide) PutChosen(cx, pos, cx->chosen); if (!cx->post || cx->post == POST_METRICS) { if (cx->force == '+') cx->film = 0; else if (cx->force == '-') cx->film = 1; } else if ((cx->force == '+') || ((cx->post == POST_FULL || cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP) && (cx->film == 0 && cx->force != '-'))) { unsigned char *dstpp, *dstpn; int v1, v2; if (cx->blend) { // Do first and last lines. uint8_t *final = mlt_pool_alloc(image_size); cx->finalp = final; mlt_frame_set_image(frame, final, image_size, mlt_pool_release); dstpn = cx->dstp + cx->dpitch; for (cx->x = 0; cx->x < cx->w; cx->x++) { cx->finalp[cx->x] = (((int) cx->dstp[cx->x] + (int) dstpn[cx->x]) >> 1); } cx->finalp = final + (cx->h - 1) * cx->dpitch; cx->dstp = *image + (cx->h - 1) * cx->dpitch; dstpp = cx->dstp - cx->dpitch; for (cx->x = 0; cx->x < cx->w; cx->x++) { cx->finalp[cx->x] = (((int) cx->dstp[cx->x] + (int) dstpp[cx->x]) >> 1); } // Now do the rest. cx->dstp = *image + cx->dpitch; dstpp = cx->dstp - cx->dpitch; dstpn = cx->dstp + cx->dpitch; cx->finalp = final + cx->dpitch; for (cx->y = 1; cx->y < cx->h - 1; cx->y++) { for (cx->x = 0; cx->x < cx->w; cx->x++) { v1 = (int) cx->dstp[cx->x] - cx->dthresh; if (v1 < 0) v1 = 0; v2 = (int) cx->dstp[cx->x] + cx->dthresh; if (v2 > 235) v2 = 235; if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x])) { if (cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP) { if (cx->x & 1) cx->finalp[cx->x] = 128; else cx->finalp[cx->x] = 235; } else cx->finalp[cx->x] = ((int) dstpp[cx->x] + (int) dstpn[cx->x] + (int) cx->dstp[cx->x] + (int) cx->dstp[cx->x]) >> 2; } else cx->finalp[cx->x] = cx->dstp[cx->x]; } cx->finalp += cx->dpitch; cx->dstp += cx->dpitch; dstpp += cx->dpitch; dstpn += cx->dpitch; } if (cx->show) Show(cx, pos, frame_properties); if (cx->debug) Debug(cx, pos); if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties); goto final; } // Interpolate mode. cx->dstp = *image + cx->dpitch; dstpp = cx->dstp - cx->dpitch; dstpn = cx->dstp + cx->dpitch; for (cx->y = 1; cx->y < cx->h - 1; cx->y += 2) { for (cx->x = 0; cx->x < cx->w; cx->x++) { v1 = (int) cx->dstp[cx->x] - cx->dthresh; if (v1 < 0) v1 = 0; v2 = (int) cx->dstp[cx->x] + cx->dthresh; if (v2 > 235) v2 = 235; if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x])) { if (cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP) { if (cx->x & 1) cx->dstp[cx->x] = 128; else cx->dstp[cx->x] = 235; } else cx->dstp[cx->x] = (dstpp[cx->x] + dstpn[cx->x]) >> 1; } } cx->dstp += 2 * cx->dpitch; dstpp += 2 * cx->dpitch; dstpn += 2 * cx->dpitch; } } if (cx->show) Show(cx, pos, frame_properties); if (cx->debug) Debug(cx, pos); if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties); final: // Flush frame at tail of period from the cache sprintf(key, MLT_POSITION_FMT, pos - 1); mlt_properties_set_data(cx->image_cache, key, NULL, 0, NULL, NULL); } else { // Signal the first {cycle} frames as invalid mlt_properties_set_int(frame_properties, "garbage", 1); } } else if (error == 0 && *format == mlt_image_yuv420p) { fprintf(stderr, "%s: %d pos " MLT_POSITION_FMT "\n", __FUNCTION__, *width * *height * 3 / 2, mlt_frame_get_position(frame)); } return error; } /** Process the frame object. */ static mlt_frame process(mlt_filter filter, mlt_frame frame) { // Push the filter on to the stack mlt_frame_push_service(frame, filter); // Push the frame filter mlt_frame_push_get_image(frame, get_image); return frame; } /** Constructor for the filter. */ mlt_filter filter_telecide_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = process; // Allocate the context and set up for garbage collection context cx = (context) mlt_pool_alloc(sizeof(struct context_s)); memset(cx, 0, sizeof(struct context_s)); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_data(properties, "context", cx, sizeof(struct context_s), (mlt_destructor) mlt_pool_release, NULL); // Allocate the metrics cache and set up for garbage collection cx->cache = (struct CACHE_ENTRY *) mlt_pool_alloc(CACHE_SIZE * sizeof(struct CACHE_ENTRY)); mlt_properties_set_data(properties, "cache", cx->cache, CACHE_SIZE * sizeof(struct CACHE_ENTRY), (mlt_destructor) mlt_pool_release, NULL); int i; for (i = 0; i < CACHE_SIZE; i++) { cx->cache[i].frame = 0xffffffff; cx->cache[i].chosen = 0xff; } // Allocate the image cache and set up for garbage collection cx->image_cache = mlt_properties_new(); mlt_properties_set_data(properties, "image_cache", cx->image_cache, 0, (mlt_destructor) mlt_properties_close, NULL); // Initialize the parameter defaults mlt_properties_set_int(properties, "guide", 0); mlt_properties_set_int(properties, "back", 0); mlt_properties_set_int(properties, "chroma", 0); mlt_properties_set_int(properties, "post", POST_FULL); mlt_properties_set_double(properties, "gthresh", 10.0); mlt_properties_set_double(properties, "vthresh", 50.0); mlt_properties_set_double(properties, "bthresh", 50.0); mlt_properties_set_double(properties, "dthresh", 7.0); mlt_properties_set_int(properties, "blend", 0); mlt_properties_set_int(properties, "nt", 10); mlt_properties_set_int(properties, "y0", 0); mlt_properties_set_int(properties, "y1", 0); mlt_properties_set_int(properties, "hints", 1); } return filter; } mlt-7.38.0/src/modules/plusgpl/filter_telecide.yml000664 000000 000000 00000001020 15172202314 022146 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: telecide title: Inverse Telecine (*DEPRECATED*) version: 1 copyright: Donald A. Graft and Dan Dennedy creator: Donald A. Graft contributor: - Dan Dennedy license: GPLv2 language: en tags: - Video description: Deinterlace frames that were repeated in a telecine process. notes: > This is old work, which never materialized to a completely usable form. It is not able to change the frame rate. The video is deinterlaced in a smart manner, but repeated frames are not removed. mlt-7.38.0/src/modules/plusgpl/gpl000664 000000 000000 00000000000 15172202314 017002 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/plusgpl/image.c000664 000000 000000 00000013605 15172202314 017542 0ustar00rootroot000000 000000 /* * EffecTV - Realtime Digital Video Effector * Copyright (C) 2001-2006 FUKUCHI Kentaro * * image.c: utilities for image processing. * */ #include #include #include "utils.h" /* * Collection of background subtraction functions */ /* checks only fake-Y value */ /* In these function Y value is treated as R*2+G*4+B. */ int image_set_threshold_y(int threshold) { int y_threshold = threshold * 7; /* fake-Y value is timed by 7 */ return y_threshold; } void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold) { int i; int R, G, B; const RGB32 *p; short *q; p = src; q = (short *)background; for(i=0; i>(16-1); G = ((*p)&0xff00)>>(8-2); B = (*p)&0xff; *q = (short)(R + G + B); p++; q++; } } void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold) { int i; int R, G, B; const RGB32 *p; const short *q; unsigned char *r; int v; p = src; q = (const short *)background; r = diff; for(i=0; i>(16-1); G = ((*p)&0xff00)>>(8-2); B = (*p)&0xff; v = (R + G + B) - (int)(*q); *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24); p++; q++; r++; } /* The origin of subtraction function is; * diff(src, dest) = (abs(src - dest) > threshold) ? 0xff : 0; * * This functions is transformed to; * (threshold > (src - dest) > -threshold) ? 0 : 0xff; * * (v + threshold)>>24 is 0xff when v is less than -threshold. * (v - threshold)>>24 is 0xff when v is less than threshold. * So, ((v + threshold)>>24) | ((threshold - v)>>24) will become 0xff when * abs(src - dest) > threshold. */ } /* Background image is refreshed every frame */ void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold) { int i; int R, G, B; const RGB32 *p; short *q; unsigned char *r; int v; p = src; q = (short *)background; r = diff; for(i=0; i>(16-1); G = ((*p)&0xff00)>>(8-2); B = (*p)&0xff; v = (R + G + B) - (int)(*q); *q = (short)(R + G + B); *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24); p++; q++; r++; } } /* checks each RGB value */ /* The range of r, g, b are [0..7] */ RGB32 image_set_threshold_RGB(int r, int g, int b) { unsigned char R, G, B; RGB32 rgb_threshold; R = G = B = 0xff; R = R<>8); b = b ^ 0xffffff; a = a ^ b; a = a & rgb_threshold; *r++ = (0 - a)>>24; } } void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold) { int i; const RGB32 *p; RGB32 *q; unsigned a, b; unsigned char *r; p = src; q = background; r = diff; for(i=0; i>8); b = b ^ 0xffffff; a = a ^ b; a = a & rgb_threshold; *r++ = (0 - a)>>24; } } /* noise filter for subtracted image. */ void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height) { int x, y; const unsigned char *src; unsigned char *dest; unsigned int count; unsigned int sum1, sum2, sum3; src = diff; dest = diff2 + width +1; for(y=1; y>24; src++; } dest += 2; } } /* Y value filters */ void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold) { int i; int R, G, B, v; unsigned char *p = diff; for(i = video_area; i>0; i--) { R = ((*src)&0xff0000)>>(16-1); G = ((*src)&0xff00)>>(8-2); B = (*src)&0xff; v = y_threshold - (R + G + B); *p = (unsigned char)(v>>24); src++; p++; } } void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold) { int i; int R, G, B, v; unsigned char *p = diff; for(i = video_area; i>0; i--) { R = ((*src)&0xff0000)>>(16-1); G = ((*src)&0xff00)>>(8-2); B = (*src)&0xff; v = (R + G + B) - y_threshold; *p = (unsigned char)(v>>24); src++; p++; } } /* tiny edge detection */ void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold) { int x, y; const unsigned char *p; unsigned char *q; int r, g, b; int ar, ag, ab; int w; p = (const unsigned char *)src; q = diff2; w = width * sizeof(RGB32); for(y=0; y y_threshold) { *q = 255; } else { *q = 0; } q++; p += 4; } p += 4; *q++ = 0; } memset(q, 0, width); } /* horizontal flipping */ void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height) { int x, y; src += width - 1; for(y=0; y #include "utils.h" /* * HSI color system utilities */ static int itrunc(double f) { int i; i=(int)f; if(i<0)i=0; if(i>255)i=255; return i; } void HSItoRGB(double H, double S, double I, int *r, int *g, int *b) { double T,Rv,Gv,Bv; Rv=1+S*sin(H-2*M_PI/3); Gv=1+S*sin(H); Bv=1+S*sin(H+2*M_PI/3); T=255.999*I/2; *r=itrunc(Rv*T); *g=itrunc(Gv*T); *b=itrunc(Bv*T); } /* * fastrand - fast fake random number generator * Warning: The low-order bits of numbers generated by fastrand() * are bad as random numbers. For example, fastrand()%4 * generates 1,2,3,0,1,2,3,0... * You should use high-order bits. */ #ifdef __APPLE__ static #endif unsigned int fastrand_val; unsigned int fastrand(void) { return (fastrand_val=fastrand_val*1103515245+12345); } void fastsrand(unsigned int seed) { fastrand_val = seed; } mlt-7.38.0/src/modules/plusgpl/utils.h000664 000000 000000 00000003735 15172202314 017630 0ustar00rootroot000000 000000 /* * EffecTV - Realtime Digital Video Effector * Copyright (C) 2001-2006 FUKUCHI Kentaro * * utils.h: header file for utils * */ #ifndef __UTILS_H__ #define __UTILS_H__ #include typedef uint32_t RGB32; /* DEFINE's by nullset@dookie.net */ #define RED(n) ((n>>16) & 0x000000FF) #define GREEN(n) ((n>>8) & 0x000000FF) #define BLUE(n) ((n>>0) & 0x000000FF) #define RGB(r,g,b) ((0<<24) + (r<<16) + (g <<8) + (b)) #define INTENSITY(n) ( ( (RED(n)+GREEN(n)+BLUE(n))/3)) /* utils.c */ void HSItoRGB(double H, double S, double I, int *r, int *g, int *b); #ifndef __APPLE__ extern unsigned int fastrand_val; #define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) #endif unsigned int fastrand(void); void fastsrand(unsigned int); /* image.c */ int image_set_threshold_y(int threshold); void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold); void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold); void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold); RGB32 image_set_threshold_RGB(int r, int g, int b); void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area); void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold); void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold); void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height); void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold); void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold); void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold); void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height); #endif /* __UTILS_H__ */ mlt-7.38.0/src/modules/qt/000775 000000 000000 00000000000 15172202314 015245 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/qt/CMakeLists.txt000664 000000 000000 00000010120 15172202314 017777 0ustar00rootroot000000 000000 set(CMAKE_AUTOMOC ON) include(GenerateExportHeader) function(mlt_add_qt_module ARG_TARGET) cmake_parse_arguments(PARSE_ARGV 1 ARG "" "QT_VERSION;DATADIR" "") if ("${ARG_TARGET}" STREQUAL "") message(FATAL_ERROR "mlt_add_qt_module called without a valid target name.") endif() if (NOT (("${ARG_QT_VERSION}" STREQUAL "5") OR ("${ARG_QT_VERSION}" STREQUAL "6"))) message(FATAL_ERROR "mlt_add_qt_module called without a valid Qt Version (allowed are 5 or 6).") endif() if ("${ARG_DATADIR}" STREQUAL "") message(FATAL_ERROR "mlt_add_qt_module called without a valid data dir name.") endif() add_library(${ARG_TARGET} MODULE common.cpp common.h consumer_qglsl.cpp factory.c filter_audiolevelgraph.cpp filter_audiowaveform.cpp filter_dropshadow.cpp filter_gpsgraphic.cpp filter_gpsgraphic.h filter_gpstext.cpp filter_qtext.cpp filter_qtblend_mode.c filter_qtblend.cpp filter_qtcrop.cpp filter_typewriter.cpp gps_drawing.cpp gps_parser.cpp gps_parser.h graph.cpp graph.h kdenlivegraphics.cpp kdenlivetitle_wrapper.cpp producer_kdenlivetitle.c producer_qimage.c producer_qtext.cpp qimage_wrapper.cpp transition_qtblend.cpp typewriter.cpp ) file(GLOB YML "*.yml") add_custom_target(Other_${ARG_TARGET}_Files SOURCES ${YML} ) generate_export_header(${ARG_TARGET}) target_compile_options(${ARG_TARGET} PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(${ARG_TARGET} PRIVATE mlt++ mlt Threads::Threads Qt${ARG_QT_VERSION}::Core Qt${ARG_QT_VERSION}::Gui Qt${ARG_QT_VERSION}::Xml ) target_include_directories(${ARG_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) if(MSVC) target_link_libraries(${ARG_TARGET} PRIVATE PThreads4W::PThreads4W) else() target_link_libraries(${ARG_TARGET} PRIVATE m) endif() if(ARG_QT_VERSION EQUAL 6) target_link_libraries(${ARG_TARGET} PRIVATE Qt6::SvgWidgets ) if(Qt6Core5Compat_FOUND) target_link_libraries(${ARG_TARGET} PRIVATE Qt6::Core5Compat ) endif() else() target_link_libraries(${ARG_TARGET} PRIVATE Qt${ARG_QT_VERSION}::Svg Qt${ARG_QT_VERSION}::Widgets ) endif() target_compile_definitions(${ARG_TARGET} PRIVATE USE_QT_OPENGL) if(NOT WINDOWS_DEPLOY) target_compile_definitions(${ARG_TARGET} PRIVATE NODEPLOY) endif() if(GPL3) target_sources(${ARG_TARGET} PRIVATE transition_vqm.cpp) target_compile_definitions(${ARG_TARGET} PRIVATE GPL3) install(FILES transition_vqm.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/${ARG_DATADIR}) endif() if(FFTW3_FOUND) target_sources(${ARG_TARGET} PRIVATE filter_audiospectrum.cpp filter_lightshow.cpp) target_link_libraries(${ARG_TARGET} PRIVATE FFTW3::fftw3) target_compile_definitions(${ARG_TARGET} PRIVATE USE_FFTW) install(FILES filter_audiospectrum.yml filter_lightshow.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/${ARG_DATADIR}) endif() if(TARGET PkgConfig::libexif) target_link_libraries(${ARG_TARGET} PRIVATE PkgConfig::libexif) target_compile_definitions(${ARG_TARGET} PRIVATE USE_EXIF) endif() set_target_properties(${ARG_TARGET} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS ${ARG_TARGET} LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_qglsl.yml filter_audiolevelgraph.yml filter_audiospectrum.yml filter_audiowaveform.yml filter_dropshadow.yml filter_gpsgraphic.yml filter_gpstext.yml filter_qtblend_mode.yml filter_qtblend.yml filter_qtcrop.yml filter_qtext.yml filter_typewriter.yml producer_kdenlivetitle.yml producer_qimage.yml producer_qtext.yml transition_qtblend.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/${ARG_DATADIR} ) endfunction() if (MOD_QT6) mlt_add_qt_module(mltqt6 QT_VERSION 6 DATADIR qt6) endif() mlt-7.38.0/src/modules/qt/common.cpp000664 000000 000000 00000011001 15172202314 017232 0ustar00rootroot000000 000000 /* * Copyright (C) 2014-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) #include #include #endif bool createQApplicationIfNeeded(mlt_service service) { if (!qApp) { #if defined(Q_OS_WIN) && defined(NODEPLOY) QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR")) + QStringLiteral("/bin")); QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR")) + QStringLiteral("/plugins")); #endif #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) if (getenv("DISPLAY") == 0 && getenv("WAYLAND_DISPLAY") == 0) { const char *qt_qpa = getenv("QT_QPA_PLATFORM"); if (!qt_qpa || strcmp(qt_qpa, "offscreen")) { mlt_log_error( service, "The MLT Qt module requires a X11 or Wayland environment.\n" "Please either run melt from a session with a display server or use a " "fake X server like xvfb:\n" "xvfb-run -a melt (...)\n"); return false; } } #endif if (!mlt_properties_get(mlt_global_properties(), "qt_argv")) mlt_properties_set(mlt_global_properties(), "qt_argv", "MLT"); static int argc = 1; static char *argv[] = {mlt_properties_get(mlt_global_properties(), "qt_argv")}; new QApplication(argc, argv); const char *localename = mlt_properties_get_lcnumeric(MLT_SERVICE_PROPERTIES(service)); QLocale::setDefault(QLocale(localename)); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QImageReader::setAllocationLimit(1024); #endif } return true; } mlt_image_format choose_image_format(mlt_image_format format) { if (format == mlt_image_rgba64) { return mlt_image_rgba64; } return mlt_image_rgba; } void convert_qimage_to_mlt(QImage *qImg, uint8_t *mImg, int width, int height) { // Destination pointer must be the same pointer that was provided to // convert_mlt_to_qimage() Q_ASSERT(mImg == qImg->constBits()); } void convert_mlt_to_qimage( uint8_t *mImg, QImage *qImg, int width, int height, mlt_image_format format) { if (format == mlt_image_rgba64) { *qImg = QImage(mImg, width, height, QImage::Format_RGBA64); return; } Q_ASSERT(format == mlt_image_rgba); *qImg = QImage(mImg, width, height, QImage::Format_RGBA8888); } int create_image(mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable) { int error = 0; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); *image_format = mlt_image_rgba; // Use the width and height suggested by the rescale filter. if (mlt_properties_get_int(frame_properties, "rescale_width") > 0) *width = mlt_properties_get_int(frame_properties, "rescale_width"); if (mlt_properties_get_int(frame_properties, "rescale_height") > 0) *height = mlt_properties_get_int(frame_properties, "rescale_height"); // If no size is requested, use native size. if (*width <= 0) *width = mlt_properties_get_int(frame_properties, "meta.media.width"); if (*height <= 0) *height = mlt_properties_get_int(frame_properties, "meta.media.height"); int size = mlt_image_format_size(*image_format, *width, *height, NULL); *image = static_cast(mlt_pool_alloc(size)); memset(*image, 0, size); // Transparent mlt_frame_set_image(frame, *image, size, mlt_pool_release); return error; } mlt-7.38.0/src/modules/qt/common.h000664 000000 000000 00000002577 15172202314 016721 0ustar00rootroot000000 000000 /* * Copyright (C) 2014-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_H #define COMMON_H #include class QImage; bool createQApplicationIfNeeded(mlt_service service); mlt_image_format choose_image_format(mlt_image_format format); void convert_qimage_to_mlt(QImage *qImg, uint8_t *mImg, int width, int height); void convert_mlt_to_qimage( uint8_t *mImg, QImage *qImg, int width, int height, mlt_image_format format); int create_image(mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable); #endif // COMMON_H mlt-7.38.0/src/modules/qt/consumer_qglsl.cpp000664 000000 000000 00000013672 15172202314 021017 0ustar00rootroot000000 000000 /* * consumer_qglsl.cpp * Copyright (C) 2012-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include typedef void *(*thread_function_t)(void *); class RenderThread : public QThread { public: RenderThread(thread_function_t function, void *data) : QThread(0) , m_function(function) , m_data(data) , m_context(new QOpenGLContext) , m_surface(new QOffscreenSurface) { QSurfaceFormat format; format.setProfile(QSurfaceFormat::CoreProfile); format.setMajorVersion(3); format.setMinorVersion(2); format.setDepthBufferSize(0); format.setStencilBufferSize(0); m_context->setFormat(format); m_context->create(); m_context->moveToThread(this); m_surface->setFormat(format); m_surface->create(); } ~RenderThread() { m_surface->destroy(); } protected: void run() { Q_ASSERT(m_context->isValid()); m_context->makeCurrent(m_surface.get()); m_function(m_data); m_context->doneCurrent(); } private: thread_function_t m_function; void *m_data; std::unique_ptr m_context; std::unique_ptr m_surface; }; static void onThreadCreate(mlt_properties owner, mlt_consumer self, mlt_event_data event_data) { Q_UNUSED(owner) mlt_event_data_thread *t = (mlt_event_data_thread *) mlt_event_data_to_object(event_data); auto thread = new RenderThread((thread_function_t) t->function, t->data); *t->thread = thread; thread->start(); } static void onThreadJoin(mlt_properties owner, mlt_consumer self, mlt_event_data event_data) { Q_UNUSED(owner) Q_UNUSED(self) auto threadData = (mlt_event_data_thread *) mlt_event_data_to_object(event_data); if (threadData && threadData->thread) { auto thread = (RenderThread *) *threadData->thread; if (thread) { thread->quit(); thread->wait(); qApp->processEvents(); delete thread; } } } static void onThreadStarted(mlt_properties owner, mlt_consumer consumer) { mlt_service service = MLT_CONSUMER_SERVICE(consumer); mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_filter filter = (mlt_filter) mlt_properties_get_data(properties, "glslManager", NULL); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_log_debug(service, "%s\n", __FUNCTION__); { mlt_events_fire(filter_properties, "init glsl", mlt_event_data_none()); if (!mlt_properties_get_int(filter_properties, "glsl_supported")) { mlt_log_fatal(service, "OpenGL Shading Language rendering is not supported on this machine.\n"); mlt_events_fire(properties, "consumer-fatal-error", mlt_event_data_none()); } } } static void onThreadStopped(mlt_properties owner, mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_filter filter = (mlt_filter) mlt_properties_get_data(properties, "glslManager", NULL); mlt_events_fire(MLT_FILTER_PROPERTIES(filter), "close glsl", mlt_event_data_none()); } extern "C" { mlt_consumer consumer_qglsl_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_consumer consumer = mlt_factory_consumer(profile, "multi", arg); if (consumer) { mlt_filter filter = mlt_factory_filter(profile, "glsl.manager", 0); if (filter) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_properties_set_data(properties, "glslManager", filter, 0, (mlt_destructor) mlt_filter_close, NULL); mlt_events_register(properties, "consumer-cleanup"); mlt_events_listen(properties, consumer, "consumer-thread-started", (mlt_listener) onThreadStarted); mlt_events_listen(properties, consumer, "consumer-thread-stopped", (mlt_listener) onThreadStopped); if (!createQApplicationIfNeeded(MLT_CONSUMER_SERVICE(consumer))) { mlt_filter_close(filter); mlt_consumer_close(consumer); return NULL; } mlt_events_listen(properties, consumer, "consumer-thread-create", (mlt_listener) onThreadCreate); mlt_events_listen(properties, consumer, "consumer-thread-join", (mlt_listener) onThreadJoin); qApp->processEvents(); return consumer; } mlt_consumer_close(consumer); } return NULL; } } mlt-7.38.0/src/modules/qt/consumer_qglsl.yml000664 000000 000000 00000000554 15172202314 021031 0ustar00rootroot000000 000000 schema_version: 7.0 type: consumer identifier: qglsl title: Qt OpenGL Context version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > Creates an OpenGL context using Qt to use with the movit module in terminal (melt) or headless (xvfb) situations. This is based on the "multi" consumer; so, see its documentation. mlt-7.38.0/src/modules/qt/factory.c000664 000000 000000 00000022651 15172202314 017066 0ustar00rootroot000000 000000 /* * Copyright (C) 2008-2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #if defined(mltqt6_EXPORTS) #include "mltqt6_export.h" #define MLT_QT_MODULE_EXPORT MLTQT6_EXPORT #elif defined(mltqt_EXPORTS) #include "mltqt_export.h" #define MLT_QT_MODULE_EXPORT MLTQT_EXPORT #else #define MLT_QT_MODULE_EXPORT #endif #ifdef USE_QT_OPENGL extern mlt_consumer consumer_qglsl_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #endif extern mlt_filter filter_audiolevelgraph_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_audiowaveform_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_dropshadow_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_gpsgraphic_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_gpstext_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_qtext_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_qimage_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_qtext_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_kdenlivetitle_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_transition transition_vqm_init(mlt_profile profile, mlt_service_type type, const char *id, void *arg); extern mlt_transition transition_qtblend_init(mlt_profile profile, mlt_service_type type, const char *id, void *arg); extern mlt_filter filter_qtblend_mode_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_qtblend_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_qtcrop_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_typewriter_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #ifdef USE_FFTW extern mlt_filter filter_audiospectrum_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_lightshow_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); #endif static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; #if QT_VERSION_MAJOR < 6 snprintf(file, PATH_MAX, "%s/qt/%s", mlt_environment("MLT_DATA"), (char *) data); #else snprintf(file, PATH_MAX, "%s/qt6/%s", mlt_environment("MLT_DATA"), (char *) data); #endif return mlt_properties_parse_yaml(file); } MLT_QT_MODULE_EXPORT MLT_REPOSITORY { #ifdef USE_QT_OPENGL MLT_REGISTER(mlt_service_consumer_type, "qglsl", consumer_qglsl_init); #endif MLT_REGISTER(mlt_service_filter_type, "audiolevelgraph", filter_audiolevelgraph_init); MLT_REGISTER(mlt_service_filter_type, "audiowaveform", filter_audiowaveform_init); MLT_REGISTER(mlt_service_filter_type, "dropshadow", filter_dropshadow_init); MLT_REGISTER(mlt_service_filter_type, "gpsgraphic", filter_gpsgraphic_init); MLT_REGISTER(mlt_service_filter_type, "gpstext", filter_gpstext_init); MLT_REGISTER(mlt_service_filter_type, "qtext", filter_qtext_init); MLT_REGISTER(mlt_service_producer_type, "qimage", producer_qimage_init); MLT_REGISTER(mlt_service_producer_type, "qtext", producer_qtext_init); MLT_REGISTER(mlt_service_producer_type, "kdenlivetitle", producer_kdenlivetitle_init); MLT_REGISTER(mlt_service_transition_type, "qtblend", transition_qtblend_init); MLT_REGISTER(mlt_service_filter_type, "qtblend_mode", filter_qtblend_mode_init); MLT_REGISTER(mlt_service_filter_type, "qtblend", filter_qtblend_init); MLT_REGISTER(mlt_service_filter_type, "qtcrop", filter_qtcrop_init); MLT_REGISTER(mlt_service_filter_type, "typewriter", filter_typewriter_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "qglsl", metadata, "consumer_qglsl.yml"); MLT_REGISTER_METADATA(mlt_service_transition_type, "qtblend", metadata, "transition_qtblend.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "qtblend_mode", metadata, "filter_qtblend_mode.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "qtblend", metadata, "filter_qtblend.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "qtcrop", metadata, "filter_qtcrop.yml"); #ifdef USE_FFTW MLT_REGISTER(mlt_service_filter_type, "audiospectrum", filter_audiospectrum_init); MLT_REGISTER(mlt_service_filter_type, "lightshow", filter_lightshow_init); #endif MLT_REGISTER_METADATA(mlt_service_filter_type, "audiolevelgraph", metadata, "filter_audiolevelgraph.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "audiowaveform", metadata, "filter_audiowaveform.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "dropshadow", metadata, "filter_dropshadow.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "gpsgraphic", metadata, "filter_gpsgraphic.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "gpstext", metadata, "filter_gpstext.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "qtext", metadata, "filter_qtext.yml"); #ifdef USE_FFTW MLT_REGISTER_METADATA(mlt_service_filter_type, "lightshow", metadata, "filter_lightshow.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "audiospectrum", metadata, "filter_audiospectrum.yml"); #endif MLT_REGISTER_METADATA(mlt_service_producer_type, "qimage", metadata, "producer_qimage.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "qtext", metadata, "producer_qtext.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "kdenlivetitle", metadata, "producer_kdenlivetitle.yml"); #ifdef GPL3 MLT_REGISTER(mlt_service_transition_type, "vqm", transition_vqm_init); MLT_REGISTER_METADATA(mlt_service_transition_type, "vqm", metadata, "transition_vqm.yml"); #endif MLT_REGISTER_METADATA(mlt_service_filter_type, "typewriter", metadata, "filter_typewriter.yml"); } mlt-7.38.0/src/modules/qt/filter_audiolevelgraph.cpp000664 000000 000000 00000026357 15172202314 022506 0ustar00rootroot000000 000000 /* * filter_audiolevel.cpp -- audio level visualization filter * Copyright (c) 2021-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "graph.h" #include // memset #include #include #include //pow #include #include #include namespace { // Private Types typedef struct { mlt_filter levels_filter; int preprocess_warned; } private_data; } // namespace static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) filter->child; // Create the audiolevel filter the first time. if (!pdata->levels_filter) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); pdata->levels_filter = mlt_factory_filter(profile, "audiolevel", NULL); if (!pdata->levels_filter) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "Unable to create audiolevel filter.\n"); return 1; } } // The service must stay locked while using the private data mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Perform audio level processing on the frame mlt_filter_process(pdata->levels_filter, frame); mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } double get_level_from_frame(mlt_frame frame, int channel) { char prop_str[30]; snprintf(prop_str, 30, "meta.media.audio_level.%d", channel); return mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), prop_str); } static void convert_levels(mlt_filter filter, mlt_frame frame, int channels, float *levels) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); int reverse = mlt_properties_get_int(filter_properties, "reverse"); int real_channels = mlt_properties_get_int(frame_properties, "audio_channels"); if (real_channels == 0) real_channels = 1; int chan_index = 0; for (chan_index = 0; chan_index < channels; chan_index++) { double level = 0.0; if (channels == 1) { // For 1 channel display, average all channel levels int real_chan_index = 0; for (real_chan_index = 0; real_chan_index < real_channels; real_chan_index++) { level += get_level_from_frame(frame, real_chan_index); } level /= real_channels; } else { int real_chan_index = chan_index % real_channels; level = get_level_from_frame(frame, real_chan_index); } if (reverse) { levels[channels - chan_index - 1] = level; } else { levels[chan_index] = level; } } } static void draw_levels(mlt_filter filter, mlt_frame frame, QImage *qimg, int width, int height) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_rect rect = mlt_properties_anim_get_rect(filter_properties, "rect", position, length); if (strchr(mlt_properties_get(filter_properties, "rect"), '%')) { rect.x *= qimg->width(); rect.w *= qimg->width(); rect.y *= qimg->height(); rect.h *= qimg->height(); } double scale = mlt_profile_scale_width(profile, width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, height); rect.y *= scale; rect.h *= scale; char *graph_type = mlt_properties_get(filter_properties, "type"); int mirror = mlt_properties_get_int(filter_properties, "mirror"); int segments = mlt_properties_anim_get_int(filter_properties, "segments", position, length); int segment_gap = mlt_properties_anim_get_int(filter_properties, "segment_gap", position, length) * scale; int segment_width = mlt_properties_anim_get_int(filter_properties, "thickness", position, length) * scale; QVector colors = get_graph_colors(filter_properties, frame_properties, position, length); QRectF r(rect.x, rect.y, rect.w, rect.h); QPainter p(qimg); if (mirror) { // Draw two half rectangle instead of one full rectangle. r.setHeight(r.height() / 2.0); } setup_graph_painter(p, r, filter_properties, frame_properties, position, length); setup_graph_pen(p, r, filter_properties, frame_properties, scale, position, length); int channels = mlt_properties_anim_get_int(filter_properties, "channels", position, length); if (channels == 0) { // "0" means use number of channels in the frame channels = mlt_properties_get_int(frame_properties, "audio_channels"); } if (channels == 0) channels = 1; float *levels = (float *) mlt_pool_alloc(channels * sizeof(float)); convert_levels(filter, frame, channels, levels); if (graph_type && graph_type[0] == 'b') { paint_bar_graph(p, r, channels, levels); } else { paint_segment_graph(p, r, channels, levels, colors, segments, segment_gap, segment_width); } if (mirror) { // Second rectangle is mirrored. p.translate(0, r.y() * 2 + r.height() * 2); p.scale(1, -1); if (graph_type && graph_type[0] == 'b') { paint_bar_graph(p, r, channels, levels); } else { paint_segment_graph(p, r, channels, levels, colors, segments, segment_gap, segment_width); } } mlt_pool_release(levels); p.end(); } /** Get the image. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); private_data *pdata = (private_data *) filter->child; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); if (mlt_properties_get(frame_properties, "meta.media.audio_level.0")) { // Get the current image *format = choose_image_format(*format); error = mlt_frame_get_image(frame, image, format, width, height, 1); // Draw the audio levels if (!error) { QImage qimg; convert_mlt_to_qimage(*image, &qimg, *width, *height, *format); draw_levels(filter, frame, &qimg, *width, *height); convert_qimage_to_mlt(&qimg, *image, *width, *height); } } else { if (pdata->preprocess_warned++ == 2) { // This filter depends on the consumer processing the audio before // the video. mlt_log_warning(MLT_FILTER_SERVICE(filter), "Audio not preprocessed.\n"); } mlt_frame_get_image(frame, image, format, width, height, writable); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { if (mlt_frame_is_test_card(frame)) { // The producer does not generate video. This filter will create an // image on the producer's behalf. mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_properties_set_int(frame_properties, "progressive", 1); mlt_properties_set_double(frame_properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(frame_properties, "meta.media.width", profile->width); mlt_properties_set_int(frame_properties, "meta.media.height", profile->height); // Tell the framework that there really is an image. mlt_properties_set_int(frame_properties, "test_image", 0); // Push a callback to create the image. mlt_frame_push_get_image(frame, create_image); } mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, (void *) filter_get_audio); mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { mlt_filter_close(pdata->levels_filter); free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_audiolevelgraph_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata && createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "_filter_private", 1); mlt_properties_set(properties, "type", "bar"); mlt_properties_set(properties, "bgcolor", "0x00000000"); mlt_properties_set(properties, "color.1", "0xffffffff"); mlt_properties_set(properties, "rect", "0% 0% 100% 100%"); mlt_properties_set(properties, "thickness", "0"); mlt_properties_set(properties, "fill", "0"); mlt_properties_set(properties, "mirror", "0"); mlt_properties_set(properties, "reverse", "0"); mlt_properties_set(properties, "angle", "0"); mlt_properties_set(properties, "gorient", "v"); mlt_properties_set_int(properties, "channels", 2); mlt_properties_set_int(properties, "segment_gap", 10); pdata->levels_filter = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter audio level graph failed\n"); if (filter) { mlt_filter_close(filter); } if (pdata) { free(pdata); } filter = NULL; } return filter; } } mlt-7.38.0/src/modules/qt/filter_audiolevelgraph.yml000664 000000 000000 00000010141 15172202314 022505 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: audiolevelgraph title: Audio Level Visualization Filter version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that draws an audio level meter on the image. parameters: - identifier: type title: Graph type description: The type of graph to display the levels. type: string default: bar values: - segment - bar readonly: no mutable: yes widget: combo - identifier: bgcolor title: Background Color type: color description: | The background color to be applied to the entire frame. The default color is transparent. A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color animation: yes - identifier: color.* title: Foreground color type: color description: | The color of the waveform. Multiple colors can be specified with incrementing suffixes to cause the waveform to be drawn in a gradient. color.1 is the top of the waveform. Subsequent colors will produce a gradient toward the bottom. By default, the filter has one color defined: color.1=0xffffffff" This results in a white waveform. To create a gradient, define more colors: color.2=green color.3=0x77777777 A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color animation: yes - identifier: thickness title: Line Thickness type: integer description: > The thickness of the bar or segments. readonly: no default: 0 minimum: 0 maximum: 20 mutable: yes widget: spinner unit: pixels animation: yes - identifier: angle title: Angle type: float description: > The rotation angle to be applied to the waveform. readonly: no default: 0 minimum: 0 maximum: 360 mutable: yes widget: spinner animation: yes - identifier: rect title: Rectangle description: > Defines the rectangle that the waveform(s) should be drawn in. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 100% 100%" readonly: no mutable: yes animation: yes - identifier: mirror title: Mirror description: > Mirror the spectrum about the center of the rectangle. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: reverse title: Reverse description: > Draw the points starting with the right channel first. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: gorient title: Gradient Orientation description: Direction of the color gradient. type: string default: vertical values: - vertical - horizontal readonly: no mutable: yes widget: combo - identifier: channels title: Channels type: integer description: > The number of channels to show. mutable: yes readonly: no default: 2 animation: yes - identifier: segments title: Segments type: integer description: > The number of segments to draw if the graph type is "segment". mutable: yes readonly: no default: 10 minimum: 2 maximum: 100 animation: yes - identifier: segment_gap title: Segment Gap type: integer description: > The space in pixels between the segments. mutable: yes readonly: no default: 10 minimum: 0 maximum: 100 unit: pixels animation: yes mlt-7.38.0/src/modules/qt/filter_audiospectrum.cpp000664 000000 000000 00000036251 15172202314 022211 0ustar00rootroot000000 000000 /* * filter_audiospectrum.cpp -- audio spectrum visualization filter * Copyright (c) 2015-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "graph.h" #include // memset #include #include #include //pow #include #include #include // Private Types typedef struct { mlt_filter fft; char *fft_prop_name; int preprocess_warned; } private_data; static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; // Create the FFT filter the first time. if (!pdata->fft) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); pdata->fft = mlt_factory_filter(profile, "fft", NULL); mlt_properties_set_int(MLT_FILTER_PROPERTIES(pdata->fft), "window_size", mlt_properties_get_int(filter_properties, "window_size")); if (!pdata->fft) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "Unable to create FFT.\n"); return 1; } } mlt_properties fft_properties = MLT_FILTER_PROPERTIES(pdata->fft); // The service must stay locked while using the private data mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Perform FFT processing on the frame mlt_filter_process(pdata->fft, frame); mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); float *bins = (float *) mlt_properties_get_data(fft_properties, "bins", NULL); if (bins) { double window_level = mlt_properties_get_double(fft_properties, "window_level"); int bin_count = mlt_properties_get_int(fft_properties, "bin_count"); size_t bins_size = bin_count * sizeof(float); float *save_bins = (float *) mlt_pool_alloc(bins_size); if (window_level == 1.0) { memcpy(save_bins, bins, bins_size); } else { memset(save_bins, 0, bins_size); } // Save the bin data as a property on the frame to be used in get_image() mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), pdata->fft_prop_name, save_bins, bins_size, mlt_pool_release, NULL); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } static void convert_fft_to_spectrum(mlt_filter filter, mlt_frame frame, int spect_bands, float *spectrum) { private_data *pdata = (private_data *) filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties fft_properties = MLT_FILTER_PROPERTIES(pdata->fft); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); double low_freq = mlt_properties_anim_get_int(filter_properties, "frequency_low", position, length); double hi_freq = mlt_properties_anim_get_int(filter_properties, "frequency_high", position, length); int bin_count = mlt_properties_get_int(fft_properties, "bin_count"); double bin_width = mlt_properties_get_double(fft_properties, "bin_width"); float *bins = (float *) mlt_properties_get_data(frame_properties, pdata->fft_prop_name, NULL); double threshold = mlt_properties_anim_get_int(filter_properties, "threshold", position, length); int reverse = mlt_properties_get_int(filter_properties, "reverse"); // Map the linear fft bin frequencies to a log scale spectrum. double band_freq_factor = pow(hi_freq / low_freq, 1.0 / (double) spect_bands); double band_freq_low = low_freq; double band_freq_hi = band_freq_low * band_freq_factor; int bin_index = 0; int spect_index = 0; double bin_freq = 0; // Skip bins that occur before the low frequency of the spectrum while (bin_freq < band_freq_low) { bin_freq += bin_width; bin_index++; } for (spect_index = 0; spect_index < spect_bands && bin_index < bin_count; spect_index++) { float mag = 0.0; if (bin_freq > band_freq_hi) { // There is no bin for this point. Interpolate between the two closest. if (bin_index == 0) { mag = bins[bin_index]; } else { double y0 = bins[bin_index - 1]; double y1 = bins[bin_index]; double spect_center = band_freq_low + (band_freq_hi - band_freq_low) / 2; double prev_freq = bin_freq - bin_width; double t = bin_width / (spect_center - prev_freq); mag = y0 + (y1 - y0) * t; } } else { // Find the bin frequency with the greatest magnitude in the range for // this spectrum point. while (bin_freq < band_freq_hi && bin_index < bin_count) { if (mag < bins[bin_index]) { mag = bins[bin_index]; } bin_freq += bin_width; bin_index++; } } // Scale the magnitude to the range 0.0-1.0 based on dB double dB = mag > 0.0 ? 20 * log10(mag) : -1000.0; double spect_val = 0; if (dB >= threshold) { spect_val = 1.0 - (dB / threshold); } if (reverse) { spectrum[spect_bands - spect_index - 1] = spect_val; } else { spectrum[spect_index] = spect_val; } // Calculate the next spectrum point frequency range. band_freq_low = band_freq_hi; band_freq_hi = band_freq_hi * band_freq_factor; } } static void draw_spectrum(mlt_filter filter, mlt_frame frame, QImage *qimg, int width, int height) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_rect rect = mlt_properties_anim_get_rect(filter_properties, "rect", position, length); if (strchr(mlt_properties_get(filter_properties, "rect"), '%')) { rect.x *= qimg->width(); rect.w *= qimg->width(); rect.y *= qimg->height(); rect.h *= qimg->height(); } double scale = mlt_profile_scale_width(profile, width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, height); rect.y *= scale; rect.h *= scale; char *graph_type = mlt_properties_get(filter_properties, "type"); int mirror = mlt_properties_get_int(filter_properties, "mirror"); int fill = mlt_properties_get_int(filter_properties, "fill"); double tension = mlt_properties_anim_get_double(filter_properties, "tension", position, length); int segments = mlt_properties_anim_get_int(filter_properties, "segments", position, length); int segment_gap = mlt_properties_anim_get_int(filter_properties, "segment_gap", position, length) * scale; int segment_width = mlt_properties_anim_get_int(filter_properties, "thickness", position, length) * scale; QVector colors = get_graph_colors(filter_properties, frame_properties, position, length); QRectF r(rect.x, rect.y, rect.w, rect.h); QPainter p(qimg); if (mirror) { // Draw two half rectangle instead of one full rectangle. r.setHeight(r.height() / 2.0); } setup_graph_painter(p, r, filter_properties, frame_properties, position, length); setup_graph_pen(p, r, filter_properties, frame_properties, scale, position, length); int bands = mlt_properties_anim_get_int(filter_properties, "bands", position, length); if (bands == 0) { // "0" means match rectangle width bands = r.width(); } float *spectrum = (float *) mlt_pool_alloc(bands * sizeof(float)); convert_fft_to_spectrum(filter, frame, bands, spectrum); if (graph_type && graph_type[0] == 'b') { paint_bar_graph(p, r, bands, spectrum); } else if (graph_type && graph_type[0] == 's') { paint_segment_graph(p, r, bands, spectrum, colors, segments, segment_gap, segment_width); } else { paint_line_graph(p, r, bands, spectrum, tension, fill); } if (mirror) { // Second rectangle is mirrored. p.translate(0, r.y() * 2 + r.height() * 2); p.scale(1, -1); if (graph_type && graph_type[0] == 'b') { paint_bar_graph(p, r, bands, spectrum); } else if (graph_type && graph_type[0] == 's') { paint_segment_graph(p, r, bands, spectrum, colors, segments, segment_gap, segment_width); } else { paint_line_graph(p, r, bands, spectrum, tension, fill); } } mlt_pool_release(spectrum); p.end(); } /** Get the image. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); private_data *pdata = (private_data *) filter->child; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); if (mlt_properties_get_data(frame_properties, pdata->fft_prop_name, NULL)) { // Get the current image *format = choose_image_format(*format); error = mlt_frame_get_image(frame, image, format, width, height, 1); // Draw the spectrum if (!error) { QImage qimg; convert_mlt_to_qimage(*image, &qimg, *width, *height, *format); draw_spectrum(filter, frame, &qimg, *width, *height); convert_qimage_to_mlt(&qimg, *image, *width, *height); } } else { if (pdata->preprocess_warned++ == 2) { // This filter depends on the consumer processing the audio before // the video. mlt_log_warning(MLT_FILTER_SERVICE(filter), "Audio not preprocessed.\n"); } mlt_frame_get_image(frame, image, format, width, height, writable); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { if (mlt_frame_is_test_card(frame)) { // The producer does not generate video. This filter will create an // image on the producer's behalf. mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_properties_set_int(frame_properties, "progressive", 1); mlt_properties_set_double(frame_properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(frame_properties, "meta.media.width", profile->width); mlt_properties_set_int(frame_properties, "meta.media.height", profile->height); // Tell the framework that there really is an image. mlt_properties_set_int(frame_properties, "test_image", 0); // Push a callback to create the image. mlt_frame_push_get_image(frame, create_image); } mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, (void *) filter_get_audio); mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { mlt_filter_close(pdata->fft); free(pdata->fft_prop_name); free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_audiospectrum_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata && createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "_filter_private", 1); mlt_properties_set_int(properties, "frequency_low", 20); mlt_properties_set_int(properties, "frequency_high", 20000); mlt_properties_set(properties, "type", "line"); mlt_properties_set(properties, "bgcolor", "0x00000000"); mlt_properties_set(properties, "color.1", "0xffffffff"); mlt_properties_set(properties, "rect", "0% 0% 100% 100%"); mlt_properties_set(properties, "thickness", "0"); mlt_properties_set(properties, "fill", "0"); mlt_properties_set(properties, "mirror", "0"); mlt_properties_set(properties, "reverse", "0"); mlt_properties_set(properties, "tension", "0.4"); mlt_properties_set(properties, "angle", "0"); mlt_properties_set(properties, "gorient", "v"); mlt_properties_set_int(properties, "segment_gap", 10); mlt_properties_set_int(properties, "bands", 31); mlt_properties_set_double(properties, "threshold", -60.0); mlt_properties_set_int(properties, "window_size", 8192); // Create a unique ID for storing data on the frame pdata->fft_prop_name = (char *) calloc(1, 20); snprintf(pdata->fft_prop_name, 20, "fft.%p", filter); pdata->fft_prop_name[20 - 1] = '\0'; pdata->fft = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter audio spectrum failed\n"); if (filter) { mlt_filter_close(filter); } if (pdata) { free(pdata); } filter = NULL; } return filter; } } mlt-7.38.0/src/modules/qt/filter_audiospectrum.yml000664 000000 000000 00000014452 15172202314 022227 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: audiospectrum title: Audio Spectrum Filter version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that draws an audio spectrum on the image. parameters: - identifier: type title: Graph type description: The type of graph to display the spectrum. type: string default: line values: - line - bar readonly: no mutable: yes widget: combo - identifier: bgcolor title: Background Color type: color description: | The background color to be applied to the entire frame. The default color is transparent. A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color animation: yes - identifier: color.* title: Foreground color type: color description: | The color of the waveform. Multiple colors can be specified with incrementing suffixes to cause the waveform to be drawn in a gradient. color.1 is the top of the waveform. Subsequent colors will produce a gradient toward the bottom. By default, the filter has one color defined: color.1=0xffffffff" This results in a white waveform. To create a gradient, define more colors: color.2=green color.3=0x77777777 A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color animation: yes - identifier: thickness title: Line Thickness type: integer description: > The thickness of the line used to draw the waveform for line graph. The thickness of the bar for bar graph. readonly: no default: 0 minimum: 0 maximum: 20 mutable: yes widget: spinner unit: pixels animation: yes - identifier: angle title: Angle type: float description: > The rotation angle to be applied to the waveform. readonly: no default: 0 minimum: 0 maximum: 360 mutable: yes widget: spinner animation: yes - identifier: rect title: Rectangle description: > Defines the rectangle that the waveform(s) should be drawn in. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 100% 100%" readonly: no mutable: yes animation: yes - identifier: fill title: Fill description: > Whether the area under the waveform should be filled in. Only applies to line graph type. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: mirror title: Mirror description: > Mirror the spectrum about the center of the rectangle. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: reverse title: Reverse description: > Draw the points starting with the highest frequency first. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: tension title: Line Tension description: > Affects the amount of curve in the line interpolating between points. 0.0 = a straight line between points. 1.0 = very curved lines between points. values < 0 and > 1 will cause loops in the lines. Only applies to line graph type. type: float default: 0.4 readonly: no mutable: yes animation: yes - identifier: gorient title: Gradient Orientation description: Direction of the color gradient. type: string default: vertical values: - vertical - horizontal readonly: no mutable: yes widget: combo - identifier: segment_gap title: Segment Gap type: integer description: > The space in pixels between the segments. mutable: yes readonly: no default: 10 minimum: 0 maximum: 100 unit: pixels animation: yes - identifier: bands title: Points type: integer description: > The number of bands to draw in the spectrum. Each band shows up as a data point in the graph. mutable: yes readonly: no default: 31 animation: yes - identifier: frequency_low title: Low Frequency type: integer description: > The low end of the frequency range to be used for the graph. motion. mutable: yes readonly: no default: 20 unit: Hz animation: yes - identifier: frequency_high title: High Frequency type: integer description: > The high end of the frequency range to be used for the graph. motion. mutable: yes readonly: no default: 20000 unit: Hz animation: yes - identifier: threshold title: Level Threshold type: float description: > The minimum amplitude of sound that must occur within the frequency range to cause the value to be applied. motion. mutable: yes readonly: no default: -30 minimum: -100 maximum: 0 unit: dB animation: yes - identifier: segments title: Segments type: integer description: > The number of segments to draw if the graph type is "segment". mutable: yes readonly: no default: 10 minimum: 2 maximum: 100 animation: yes - identifier: window_size title: Window Size type: integer description: > The number of samples that the FFT will be performed on. If window_size is less than the number of samples in a frame, extra samples will be ignored. If window_size is more than the number of samples in a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 2048 mlt-7.38.0/src/modules/qt/filter_audiowaveform.cpp000664 000000 000000 00000042761 15172202314 022200 0ustar00rootroot000000 000000 /* * filter_audiowaveform.cpp -- audio waveform visualization filter * Copyright (c) 2015-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "graph.h" #include #include #include #include #include static const qreal MAX_S16_AMPLITUDE = 32768.0; namespace { // Private Types typedef struct { char *buffer_prop_name; int reset_window; int16_t *window_buffer; int window_samples; int window_frequency; int window_channels; } private_data; typedef struct { int16_t *buffer; int samples; int channels; } save_buffer; } // namespace static save_buffer *create_save_buffer(int samples, int channels, int16_t *buffer) { save_buffer *ret = (save_buffer *) calloc(1, sizeof(save_buffer)); int buffer_size = samples * channels * sizeof(int16_t); ret->samples = samples; ret->channels = channels; ret->buffer = (int16_t *) calloc(1, buffer_size); memcpy(ret->buffer, buffer, buffer_size); return ret; } static void destory_save_buffer(void *ptr) { if (!ptr) { mlt_log_error(NULL, "Invalid save_buffer ptr.\n"); return; } save_buffer *buff = (save_buffer *) ptr; free(buff->buffer); free(buff); } static void property_changed(mlt_service owner, mlt_filter filter, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "window")) { private_data *pdata = (private_data *) filter->child; pdata->reset_window = 1; } } static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) filter->child; if (*format != mlt_audio_s16 && *format != mlt_audio_float) { *format = mlt_audio_float; } error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (error) { return error; } if (*frequency != pdata->window_frequency || *channels != pdata->window_channels) { pdata->reset_window = true; } if (pdata->reset_window) { mlt_log_info(MLT_FILTER_SERVICE(filter), "Reset window buffer: %d.\n", mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "window")); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double fps = mlt_profile_fps(profile); int frame_samples = mlt_audio_calculate_frame_samples(fps, *frequency, mlt_frame_get_position(frame)); int window_ms = mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "window"); pdata->window_frequency = *frequency; pdata->window_channels = *channels; pdata->window_samples = window_ms * *frequency / 1000; if (pdata->window_samples < frame_samples) { pdata->window_samples = frame_samples; } free(pdata->window_buffer); pdata->window_buffer = (int16_t *) calloc(1, pdata->window_samples * pdata->window_channels * sizeof(int16_t)); pdata->reset_window = 0; } int new_sample_count = *samples; if (new_sample_count > pdata->window_samples) { new_sample_count = pdata->window_samples; } int old_sample_count = pdata->window_samples - new_sample_count; int window_buff_bytes = pdata->window_samples * pdata->window_channels * sizeof(int16_t); int new_sample_bytes = new_sample_count * pdata->window_channels * sizeof(int16_t); int old_sample_bytes = old_sample_count * pdata->window_channels * sizeof(int16_t); // Move the old samples ahead in the window buffer to make room for new samples. if (new_sample_bytes < window_buff_bytes) { char *old_sample_src = (char *) pdata->window_buffer + new_sample_bytes; char *old_sample_dst = (char *) pdata->window_buffer; memmove(old_sample_dst, old_sample_src, old_sample_bytes); } // Copy the new samples to the back of the window buffer. if (*format == mlt_audio_s16) { char *new_sample_src = (char *) *buffer; char *new_sample_dst = (char *) pdata->window_buffer + old_sample_bytes; memcpy(new_sample_dst, new_sample_src, new_sample_bytes); } else // mlt_audio_float { for (int c = 0; c < pdata->window_channels; c++) { float *src = (float *) *buffer + (*samples * c); int16_t *dst = pdata->window_buffer + (old_sample_count * pdata->window_channels) + c; for (int s = 0; s < new_sample_count; s++) { *dst = *src * MAX_S16_AMPLITUDE; src++; dst += pdata->window_channels; } } } // Copy the window buffer and pass it along with the frame. save_buffer *out = create_save_buffer(pdata->window_samples, pdata->window_channels, pdata->window_buffer); mlt_properties_set_data(MLT_FRAME_PROPERTIES(frame), pdata->buffer_prop_name, out, sizeof(save_buffer), destory_save_buffer, NULL); return 0; } static void paint_waveform( QPainter &p, QRectF &rect, int16_t *audio, int samples, int channels, int fill) { int width = rect.width(); const int16_t *q = audio; qreal half_height = rect.height() / 2.0; qreal center_y = rect.y() + half_height; if (samples < width) { // For each x position on the waveform, find the sample value that // applies to that position and draw a point at that location. QPoint point(0, *q * half_height / MAX_S16_AMPLITUDE + center_y); QPoint lastPoint = point; int lastSample = 0; for (int x = 0; x < width; x++) { int sample = (x * samples) / width; if (sample != lastSample) { lastSample = sample; q += channels; } lastPoint.setX(x + rect.x()); lastPoint.setY(point.y()); point.setX(x + rect.x()); point.setY(*q * half_height / MAX_S16_AMPLITUDE + center_y); if (fill) { // Draw the line all the way to 0 to "fill" it in. if ((point.y() > center_y && lastPoint.y() > center_y) || (point.y() < center_y && lastPoint.y() < center_y)) { lastPoint.setY(center_y); } } if (point.y() == lastPoint.y()) { p.drawPoint(point); } else { p.drawLine(lastPoint, point); } } } else { // For each x position on the waveform, find the min and max sample // values that apply to that position. Draw a vertical line from the // min value to the max value. QPoint high; QPoint low; qreal max = *q; qreal min = *q; int lastX = 0; for (int s = 0; s <= samples; s++) { int x = (s * width) / samples; if (x != lastX) { // The min and max have been determined for the previous x // So draw the line if (fill) { // Draw the line all the way to 0 to "fill" it in. if (max > 0 && min > 0) { min = 0; } else if (min < 0 && max < 0) { max = 0; } } high.setX(lastX + rect.x()); high.setY(max * half_height / MAX_S16_AMPLITUDE + center_y); low.setX(lastX + rect.x()); low.setY(min * half_height / MAX_S16_AMPLITUDE + center_y); if (high.y() == low.y()) { p.drawPoint(high); } else { p.drawLine(low, high); } lastX = x; // Swap max and min so that the next line picks up where // this one left off. int tmp = max; max = min; min = tmp; } if (*q > max) max = *q; if (*q < min) min = *q; q += channels; } } } static void draw_waveforms(mlt_filter filter, mlt_frame frame, QImage *qimg, int16_t *audio, int channels, int samples, int width, int height) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); int show_channel = mlt_properties_anim_get_int(filter_properties, "show_channel", position, length); int fill = mlt_properties_get_int(filter_properties, "fill"); mlt_rect rect = mlt_properties_anim_get_rect(filter_properties, "rect", position, length); if (strchr(mlt_properties_get(filter_properties, "rect"), '%')) { rect.x *= qimg->width(); rect.w *= qimg->width(); rect.y *= qimg->height(); rect.h *= qimg->height(); } double scale = mlt_profile_scale_width(profile, width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, height); rect.y *= scale; rect.h *= scale; QRectF r(rect.x, rect.y, rect.w, rect.h); QPainter p(qimg); setup_graph_painter(p, r, filter_properties, frame_properties, position, length); if (show_channel == -1) // Combine all channels { if (channels > 1) { int16_t *in = audio; int16_t *out = audio; for (int s = 0; s < samples; s++) { double acc = 0.0; for (int c = 0; c < channels; c++) { acc += *in++; } *out++ = acc / channels; } channels = 1; } show_channel = 1; } if (show_channel == 0) // Show all channels { QRectF c_rect = r; qreal c_height = r.height() / channels; for (int c = 0; c < channels; c++) { // Divide the rectangle into smaller rectangles for each channel. c_rect.setY(r.y() + c_height * c); c_rect.setHeight(c_height); setup_graph_pen(p, c_rect, filter_properties, frame_properties, scale, position, length); paint_waveform(p, c_rect, audio + c, samples, channels, fill); } } else if (show_channel > 0) { // Show one specific channel if (show_channel > channels) { // Sanity show_channel = 1; } setup_graph_pen(p, r, filter_properties, frame_properties, scale, position, length); paint_waveform(p, r, audio + show_channel - 1, samples, channels, fill); } p.end(); } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable) { int error = 0; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); private_data *pdata = (private_data *) filter->child; save_buffer *audio = (save_buffer *) mlt_properties_get_data(frame_properties, pdata->buffer_prop_name, NULL); if (audio) { // Get the current image *image_format = choose_image_format(*image_format); error = mlt_frame_get_image(frame, image, image_format, width, height, writable); // Draw the waveforms if (!error) { QImage qimg; convert_mlt_to_qimage(*image, &qimg, *width, *height, *image_format); draw_waveforms(filter, frame, &qimg, audio->buffer, audio->channels, audio->samples, *width, *height); convert_qimage_to_mlt(&qimg, *image, *width, *height); } } else { // This filter depends on the consumer processing the audio before // the video. mlt_log_warning(MLT_FILTER_SERVICE(filter), "Audio not preprocessed.\n"); return mlt_frame_get_image(frame, image, image_format, width, height, writable); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); if (mlt_frame_is_test_card(frame)) { // The producer does not generate video. This filter will create an // image on the producer's behalf. mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_properties_set_int(frame_properties, "progressive", 1); mlt_properties_set_double(frame_properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(frame_properties, "meta.media.width", profile->width); mlt_properties_set_int(frame_properties, "meta.media.height", profile->height); // Tell the framework that there really is an image. mlt_properties_set_int(frame_properties, "test_image", 0); // Push a callback to create the image. mlt_frame_push_get_image(frame, create_image); } mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, (void *) filter_get_audio); mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { free(pdata->window_buffer); free(pdata->buffer_prop_name); free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_audiowaveform_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata) { if (!createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_filter_close(filter); free(pdata); return NULL; } mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set(filter_properties, "bgcolor", "0x00000000"); mlt_properties_set(filter_properties, "color.1", "0xffffffff"); mlt_properties_set(filter_properties, "thickness", "0"); mlt_properties_set(filter_properties, "show_channel", "0"); mlt_properties_set(filter_properties, "angle", "0"); mlt_properties_set(filter_properties, "rect", "0 0 100% 100%"); mlt_properties_set(filter_properties, "fill", "0"); mlt_properties_set(filter_properties, "gorient", "v"); mlt_properties_set_int(filter_properties, "window", 0); pdata->reset_window = 1; // Create a unique ID for storing data on the frame pdata->buffer_prop_name = (char *) calloc(1, 20); snprintf(pdata->buffer_prop_name, 20, "audiowave.%p", filter); pdata->buffer_prop_name[20 - 1] = '\0'; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; mlt_events_listen(filter_properties, filter, "property-changed", (mlt_listener) property_changed); } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to initialize\n"); if (filter) { mlt_filter_close(filter); } if (pdata) { free(pdata); } filter = NULL; } return filter; } } mlt-7.38.0/src/modules/qt/filter_audiowaveform.yml000664 000000 000000 00000007536 15172202314 022220 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: audiowaveform title: Audio Waveform Filter version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that draws an audio waveform on the image. parameters: - identifier: bgcolor title: Background Color type: color description: | The background color to be applied to the entire frame. The default color is transparent. A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color animation: yes - identifier: color.* title: Foreground color type: color description: | The color of the waveform. Multiple colors can be specified with incrementing suffixes to cause the waveform to be drawn in a gradient. color.1 is the top of the waveform. Subsequent colors will produce a gradient toward the bottom. By default, the filter has one color defined: color.1=0xffffffff" This results in a white waveform. To create a gradient, define more colors: color.2=green color.3=0x77777777 A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color animation: yes - identifier: thickness title: Line Thickness type: integer description: > The thickness of the line used to draw the waveform. readonly: no default: 0 minimum: 0 maximum: 20 mutable: yes widget: spinner unit: pixels - identifier: show_channel title: Audio Channel type: integer description: > The audio channel to draw. "0" indicates that all channels should be drawn. "-1" indicates that all channels will be added together in a single waveform. readonly: no default: 0 minimum: 0 maximum: 20 mutable: yes widget: spinner animation: yes - identifier: angle title: Angle type: float description: > The rotation angle to be applied to the waveform. readonly: no default: 0 minimum: 0 maximum: 360 mutable: yes widget: spinner - identifier: rect title: Rectangle description: > Defines the rectangle that the waveform(s) should be drawn in. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 100% 100%" readonly: no mutable: yes animation: yes - identifier: fill title: Fill description: Whether the area under the waveform should be filled in. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: gorient title: Gradient Orientation description: Direction of the color gradient. type: string default: vertical values: - vertical - horizontal readonly: no mutable: yes widget: combo - identifier: window title: Window type: integer description: > The duration of the audio (in ms) to be drawn in the waveform. If the window is less than the duration of a frame, the duration of a frame will be used. If the window is more than the duration of a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 0 mlt-7.38.0/src/modules/qt/filter_dropshadow.cpp000664 000000 000000 00000006654 15172202314 021503 0ustar00rootroot000000 000000 /* * filter_dropshadow.cpp -- Drop Shadow Effect * Copyright (c) 2024-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable) { auto error = 0; auto filter = Mlt::Filter(mlt_filter(mlt_frame_pop_service(frame))); *image_format = choose_image_format(*image_format); error = mlt_frame_get_image(frame, image, image_format, width, height, writable); if (!error) { QImage qimg; convert_mlt_to_qimage(*image, &qimg, *width, *height, *image_format); auto shadow = new QGraphicsDropShadowEffect; auto f = Mlt::Frame(frame); mlt_position pos = filter.get_position(f); mlt_position len = filter.get_length2(f); auto color = filter.anim_get_color("color", pos, len); color = ::mlt_color_convert_trc(color, f.get("color_trc")); shadow->setColor(QColor(color.r, color.g, color.b, color.a)); shadow->setBlurRadius(filter.anim_get_double("radius", pos, len)); shadow->setXOffset(filter.anim_get_double("x", pos, len)); shadow->setYOffset(filter.anim_get_double("y", pos, len)); QGraphicsScene scene; QGraphicsPixmapItem item; scene.setItemIndexMethod(QGraphicsScene::NoIndex); item.setPixmap(QPixmap::fromImage(qimg)); item.setGraphicsEffect(shadow); scene.addItem(&item); QPainter painter(&qimg); scene.render(&painter); painter.end(); convert_qimage_to_mlt(&qimg, *image, *width, *height); } return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_dropshadow_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (!filter) return nullptr; if (!createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_filter_close(filter); return nullptr; } filter->process = process; auto properties = Mlt::Properties(filter); properties.set("color", "#b4636363"); properties.set("radius", 1.0); properties.set("x", 8.0); properties.set("y", 8.0); return filter; } } // extern "C" mlt-7.38.0/src/modules/qt/filter_dropshadow.yml000664 000000 000000 00000002073 15172202314 021511 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: dropshadow title: Drop Shadow version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Create a shadow effect from the alpha channel. parameters: - identifier: color title: Color type: color description: The color of the shadow including alpha default: "#b4636363" readonly: no mutable: yes animation: yes widget: color - identifier: radius title: Blur radius type: float description: The amount to blur the edge of the shadow default: 1 readonly: no mutable: yes animation: yes widget: spinner unit: pixels - identifier: x title: X Offset type: float description: The relative horizontal position of the shadow default: 8.0 readonly: no mutable: yes animation: yes widget: spinner - identifier: y title: Y Offset type: float description: The relative vertical position of the shadow default: 8.0 readonly: no mutable: yes animation: yes widget: spinner mlt-7.38.0/src/modules/qt/filter_gpsgraphic.cpp000664 000000 000000 00000103672 15172202314 021456 0ustar00rootroot000000 000000 /* * filter_gpsgraphic.cpp -- draws gps related graphics * Copyright (c) 2015-2025 Meltytech, LLC * Original author: Daniel F * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "filter_gpsgraphic.h" #include "common.h" #include static QMutex f_mutex; // Sets the private data to default values and frees gps points array static void default_priv_data(private_data *pdata) { if (pdata) { if (pdata->gps_points_r) { free(pdata->gps_points_r); pdata->gps_points_r = NULL; } if (pdata->gps_points_p) { free(pdata->gps_points_p); pdata->gps_points_p = NULL; } pdata->gps_points_size = 0; pdata->last_smooth_lvl = 0; pdata->last_searched_index = 0; pdata->first_gps_time = 0; pdata->last_gps_time = 0; pdata->gps_offset = 0; pdata->speed_multiplier = 1.0; pdata->last_filename[0] = '\0'; pdata->interpolated = 0; pdata->minmax.set_defaults(); memset(&pdata->ui_crops, 0, sizeof(pdata->ui_crops)); pdata->graph_data_source = 0; pdata->graph_type = 0; memset(&pdata->img_rect, 0, sizeof(pdata->img_rect)); pdata->last_bg_img_path[0] = '\0'; pdata->map_aspect_ratio_from_distance = 0.0; pdata->bg_img = QImage(); pdata->bg_img_scaled = QImage(); pdata->bg_img_scaled_width = 0.0; pdata->bg_img_scaled_height = 0.0; pdata->bg_img_matched_rect = QRectF(); pdata->swap_180 = 0; } } //like decimals_needed() but graph data source aware int decimals_needed_bysrc(mlt_filter filter, double v) { private_data *pdata = (private_data *) filter->child; if (pdata->graph_data_source == gspg_location_src) return 6; if (pdata->graph_data_source == gpsg_altitude_src || pdata->graph_data_source == gpsg_speed_src) return (decimals_needed(v)); if (pdata->graph_data_source == gpsg_hr_src) return 0; return 0; } //convert_[distance|speed]_to_format but graph data source aware double convert_bysrc_to_format(mlt_filter filter, double val) { private_data *pdata = (private_data *) filter->child; char *legend_unit = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "legend_unit"); if (pdata->graph_data_source == gpsg_altitude_src) return convert_distance_to_format(val, legend_unit); if (pdata->graph_data_source == gpsg_speed_src) return convert_speed_to_format(val, legend_unit); return val; } //helps pass filter private data for gps_parser.cpp calls gps_private_data filter_to_gps_data(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; gps_private_data ret; ret.gps_points_r = pdata->gps_points_r; ret.gps_points_p = pdata->gps_points_p; ret.ptr_to_gps_points_r = &pdata->gps_points_r; ret.ptr_to_gps_points_p = &pdata->gps_points_p; ret.gps_points_size = &pdata->gps_points_size; ret.last_searched_index = &pdata->last_searched_index; ret.first_gps_time = &pdata->first_gps_time; ret.last_gps_time = &pdata->last_gps_time; ret.interpolated = &pdata->interpolated; ret.swap180 = &pdata->swap_180; ret.gps_proc_start_t = 0; ret.last_smooth_lvl = pdata->last_smooth_lvl; ret.last_filename = pdata->last_filename; ret.filter = filter; return ret; } /* Gets the value from pdata->minmax (or custom|existing gps_point) depending on data_src value get_type represents: 0=current_value, 1=max_value, -1=min_value subtype is used for longitude (planned more but didn't make sense so far) gps_p is used to get (only for get_type=0) the proper value by source from points not in pdata->gps_points_p[] (mostly weighted point) */ double get_by_src(mlt_filter filter, int get_type, int i_gps /* = 0 */, int subtype /* = 0 */, gps_point_proc *gps_p /* = NULL */) { private_data *pdata = (private_data *) filter->child; if (i_gps < 0 || i_gps >= pdata->gps_points_size) return 0; if (pdata->graph_data_source == gspg_location_src) { if (get_type == -1) { if (subtype == gpsg_latitude_id) return pdata->minmax.min_lat_projected; else if (subtype == gpsg_longitude_id) return pdata->minmax.min_lon; } else if (get_type == 1) { if (subtype == gpsg_latitude_id) return pdata->minmax.max_lat_projected; else if (subtype == gpsg_longitude_id) return pdata->minmax.max_lon; } else if (get_type == 0) { if (subtype == gpsg_latitude_id) { if (gps_p == NULL) return pdata->gps_points_p[i_gps].lat_projected; else if (gps_p != NULL) return gps_p->lat_projected; } else if (subtype == gpsg_longitude_id) { if (gps_p == NULL) return pdata->gps_points_p[i_gps].lon; else if (gps_p != NULL) return gps_p->lon; } } } else if (pdata->graph_data_source == gpsg_altitude_src) { if (get_type == -1) return pdata->minmax.min_ele; else if (get_type == 1) return pdata->minmax.max_ele; else if (get_type == 0) { if (gps_p == NULL) return pdata->gps_points_p[i_gps].ele; else if (gps_p != NULL) return gps_p->ele; } } else if (pdata->graph_data_source == gpsg_hr_src) { if (get_type == -1) return pdata->minmax.min_hr; else if (get_type == 1) return pdata->minmax.max_hr; else if (get_type == 0) { if (gps_p == NULL) return pdata->gps_points_p[i_gps].hr; else if (gps_p != NULL) return gps_p->hr; } } else if (pdata->graph_data_source == gpsg_speed_src) { if (get_type == -1) return pdata->minmax.min_speed; else if (get_type == 1) return pdata->minmax.max_speed; else if (get_type == 0) { if (gps_p == NULL) return pdata->gps_points_p[i_gps].speed; else if (gps_p != NULL) return gps_p->speed; } } mlt_log_warning(filter, "Invalid combination of arguments to get_by_src: (get_type=%d, i_gps=%d, " "subtype=%d, gps_p=%p)\n", get_type, i_gps, subtype, gps_p); return 0; } //get_by_src helper shortcuts double get_min_bysrc(mlt_filter filter, int subtype /* = 0 */) { return get_by_src(filter, -1, 0, subtype, NULL); } double get_max_bysrc(mlt_filter filter, int subtype /* = 0 */) { return get_by_src(filter, 1, 0, subtype, NULL); } double get_crtval_bysrc(mlt_filter filter, int i_gps, int subtype /* = 0 */, gps_point_proc *gps_p /* = NULL */) { return get_by_src(filter, 0, i_gps, subtype, gps_p); } // Returns the unix time (miliseconds) of "Media Created" metadata, or fallbacks to "Modified Time" from OS static int64_t get_original_video_file_time_mseconds(mlt_frame frame) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); return mlt_producer_get_creation_time(producer); } /** Returns absolute* current frame time in miliseconds (original file creation + current timecode) * *also applies speed_multiplier */ static int64_t get_current_frame_time_ms(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; int64_t file_time = 0, fr_time = 0; file_time = get_original_video_file_time_mseconds(frame); mlt_position frame_position = mlt_frame_original_position(frame); // mlt_log_info(filter, "frame_pos=%d, frame_orig_pos=%d; file_time=%d\n", mlt_frame_get_position(frame), mlt_frame_original_position(frame), file_time/1000); f_mutex.lock(); char *s = mlt_properties_frames_to_time(properties, frame_position, mlt_time_clock); if (s) { int h = 0, m = 0, sec = 0, msec = 0; sscanf(s, "%d:%d:%d.%d", &h, &m, &sec, &msec); fr_time = (h * 3600 + m * 60 + sec) * 1000 + msec; } else mlt_log_warning(filter, "get_current_frame_time_ms time string null, giving up " "[mlt_frame_original_position()=%d], retry result:%s\n", frame_position, mlt_properties_frames_to_time(properties, frame_position, mlt_time_clock)); f_mutex.unlock(); return file_time + fr_time * pdata->speed_multiplier; } //gets the nearest gps point [index] according to video time + input offset int get_now_gpspoint_index(mlt_filter filter, mlt_frame frame, bool force_result /* = true */) { private_data *pdata = (private_data *) filter->child; int64_t video_time_synced = get_current_frame_time_ms(filter, frame) + pdata->gps_offset; return binary_search_gps(filter_to_gps_data(filter), video_time_synced, force_result); } //returns the next gps point with a valid value for crt_source (starting with crt_i+1) int get_next_valid_gpspoint_index(mlt_filter filter, int crt_i) { private_data *pdata = (private_data *) filter->child; while (++crt_i < pdata->gps_points_size && get_crtval_bysrc(filter, crt_i) == GPS_UNINIT) ; //maybe TODO: add restriction for MAX_GPS_TIME? and allow depending on force_result return CLAMP(crt_i, 0, pdata->gps_points_size - 1); } //returns an interpolated gps point at the exact current video time + offset gps_point_proc get_now_weighted_gpspoint(mlt_filter filter, mlt_frame frame, bool force_result /* = true */) { private_data *pdata = (private_data *) filter->child; gps_point_proc crt; int i_now = -1, non_forced_i = -1; int max_gps_diff_ms = get_max_gps_diff_ms(filter_to_gps_data(filter)); int64_t video_time_synced = get_current_frame_time_ms(filter, frame) + pdata->gps_offset; i_now = binary_search_gps(filter_to_gps_data(filter), video_time_synced, force_result); //make sure we don't mistakenly interpolate between gps[0] and gps[0+1] because the "forced" search returned gps[0] due to outside time non_forced_i = binary_search_gps(filter_to_gps_data(filter), video_time_synced, false); if (i_now == -1) //if force_result is true this will never happen return uninit_gps_proc_point; //interpolate if everything ok int next_i = get_next_valid_gpspoint_index(filter, i_now); if (non_forced_i != -1) crt = weighted_middle_point_proc(&pdata->gps_points_p[i_now], &pdata->gps_points_p[next_i], video_time_synced, max_gps_diff_ms); else crt = pdata->gps_points_p[i_now]; return crt; } //initializes stuff and chooses which type of graph draw to call static void draw_graphics(mlt_filter filter, mlt_frame frame, QImage *qimg, int width, int height, s_base_crops &used_crops) { private_data *pdata = (private_data *) filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); QPainter p(qimg); prepare_canvas(filter, frame, qimg, p, width, height, used_crops); if (pdata->ui_crops.start_index < pdata->ui_crops.end_index) { //draw the main graph line if (pdata->graph_type == gpsg_2d_map_graph || pdata->graph_type == gpsg_crop_center_graph) draw_main_line_graph(filter, frame, p, used_crops); else if (pdata->graph_type == gpsg_speedometer_graph) draw_main_speedometer(filter, frame, p, used_crops); } else mlt_log_info(filter, "min > max so nothing to print (index: start=%d,end=%d; start_p:%f,end_p:%f; " "vertical: UIbot=%f,UItop=%f; horizontal: UIleft:%f,UIright:%f)\n", pdata->ui_crops.start_index, pdata->ui_crops.end_index, mlt_properties_get_double(properties, "trim_start_p"), mlt_properties_get_double(properties, "trim_end_p"), pdata->ui_crops.bot, pdata->ui_crops.top, pdata->ui_crops.left, pdata->ui_crops.right); p.end(); } template static double get_as_percentage(T val, T min, T max) { double ret = 1; if (max - min != 0) ret = (double) (val - min) / (max - min); return ret; } //computes used_crops from pdata->ui_crops (they change on each frame so can't store them in pdata) static void process_frame_properties(mlt_filter filter, mlt_frame frame, s_base_crops &used_crops) { private_data *pdata = (private_data *) filter->child; //if CROP_CENTER we'll compute used_crops from pdata->ui_crops every frame to keep the point in center if (pdata->graph_type == gpsg_crop_center_graph) { int i_now = get_now_gpspoint_index(filter, frame); gps_point_proc crt_weighted = get_now_weighted_gpspoint(filter, frame); if (get_crtval_bysrc(filter, i_now, 0, &crt_weighted) != GPS_UNINIT) { double now_perc_vertical = get_as_percentage(get_crtval_bysrc(filter, 0, 0, &crt_weighted), get_min_bysrc(filter), get_max_bysrc(filter)); double now_perc_horizontal; if (pdata->graph_data_source == gspg_location_src) now_perc_horizontal = get_as_percentage(crt_weighted.lon, get_min_bysrc(filter, gpsg_longitude_id), get_max_bysrc(filter, gpsg_longitude_id)); else now_perc_horizontal = get_as_percentage(crt_weighted.time, pdata->ui_crops.min_crop_time, pdata->ui_crops.max_crop_time); //final crop is now_percentage +/- half of 100 - user requested crop (so a 90% crop means show 10% of the map, 5% above and 5% below the centered now_point) double delta_h = (100 - pdata->ui_crops.left) / 2.0; used_crops.left = now_perc_horizontal * 100 - delta_h; used_crops.right = now_perc_horizontal * 100 + delta_h; double delta_v = (100 - pdata->ui_crops.bot) / 2.0; used_crops.bot = now_perc_vertical * 100 - delta_v; used_crops.top = now_perc_vertical * 100 + delta_v; //if not 2d map, center for vertical axis is not something we want, so just use standard crop logic if (pdata->graph_data_source != gspg_location_src) { used_crops.bot = pdata->ui_crops.bot; used_crops.top = pdata->ui_crops.top; } } } else /* --> not gpsg_crop_center_graph */ { used_crops.bot = pdata->ui_crops.bot; used_crops.top = pdata->ui_crops.top; used_crops.left = pdata->ui_crops.left; used_crops.right = pdata->ui_crops.right; } } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); s_base_crops used_crops = {0, 100, 0, 100}; // Get the current image *format = choose_image_format(*format); error = mlt_frame_get_image(frame, image, format, width, height, writable); // Draw the graph if (!error) { process_frame_properties(filter, frame, used_crops); QImage qimg; convert_mlt_to_qimage(*image, &qimg, *width, *height, *format); draw_graphics(filter, frame, &qimg, *width, *height, used_crops); convert_qimage_to_mlt(&qimg, *image, *width, *height); } else mlt_log_warning(MLT_FILTER_SERVICE(filter), "mlt_frame_get_image error=%d, can't draw at all\n", error); return error; } //if crop mode is absolute we'll need to convert those values to a percentage as the rest of the code expects static void convert_crop_values_to_percentages(mlt_filter filter, mlt_frame frame) { private_data *pdata = (private_data *) filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); int crop_mode_v = mlt_properties_get_int(properties, "crop_mode_v"); int crop_mode_h = mlt_properties_get_int(properties, "crop_mode_h"); if (crop_mode_h + crop_mode_v == 0) //0=already percentage, 1=need to convert return; double v_min = get_min_bysrc(filter); double v_max = get_max_bysrc(filter); v_min = convert_bysrc_to_format(filter, v_min); v_max = convert_bysrc_to_format(filter, v_max); //now calculate the percentages //resulted_crop_percentage = (input_absolute_value - crt_type_minimum_value) / (crt_type_maximum_value - crt_type_minimum_value) * 100 if (crop_mode_v) { pdata->ui_crops.bot = (pdata->ui_crops.bot - v_min) / (v_max - v_min) * 100; pdata->ui_crops.top = (pdata->ui_crops.top - v_min) / (v_max - v_min) * 100; } //this is just to cover all cases, I doubt someone would manually input coordinates or unix time (in milliseconds) when they can just visually set the percentage if (crop_mode_h) { if (pdata->graph_data_source == gspg_location_src) //longitude { v_min = get_min_bysrc(filter, gpsg_longitude_id); v_max = get_max_bysrc(filter, gpsg_longitude_id); pdata->ui_crops.left = (pdata->ui_crops.left - v_min) / (v_max - v_min) * 100; pdata->ui_crops.right = (pdata->ui_crops.right - v_min) / (v_max - v_min) * 100; } else //time { v_min = pdata->ui_crops.min_crop_time; v_max = pdata->ui_crops.max_crop_time; pdata->ui_crops.left = (pdata->ui_crops.left - v_min) / (v_max - v_min) * 100; pdata->ui_crops.right = (pdata->ui_crops.right - v_min) / (v_max - v_min) * 100; } } } //goes through ALL gps data and finds min/max for gps, altitude, hr + computes map's aspect ratio and middle point static void find_minmax_of_data(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; pdata->minmax.set_defaults(); #define assign_if_smaller(x, m) \ if (x != GPS_UNINIT && x < m) \ m = x; #define assign_if_bigger(x, m) \ if (x != GPS_UNINIT && x > m) \ m = x; for (int i = 0; i < pdata->gps_points_size; i++) { gps_point_proc *crt = &pdata->gps_points_p[i]; assign_if_smaller(crt->lat, pdata->minmax.min_lat); assign_if_bigger(crt->lat, pdata->minmax.max_lat); assign_if_smaller(crt->lon, pdata->minmax.min_lon); assign_if_bigger(crt->lon, pdata->minmax.max_lon); assign_if_smaller(crt->ele, pdata->minmax.min_ele); assign_if_bigger(crt->ele, pdata->minmax.max_ele); assign_if_smaller(crt->speed, pdata->minmax.min_speed); assign_if_bigger(crt->speed, pdata->minmax.max_speed); assign_if_smaller(crt->hr, pdata->minmax.min_hr); assign_if_bigger(crt->hr, pdata->minmax.max_hr); assign_if_smaller(crt->grade_p, pdata->minmax.min_grade_p); assign_if_bigger(crt->grade_p, pdata->minmax.max_grade_p); } pdata->minmax.min_lat_projected = project_latitude(pdata->minmax.min_lat); pdata->minmax.max_lat_projected = project_latitude(pdata->minmax.max_lat); #undef assign_if_smaller #undef assign_if_bigger //compute the gps track aspect ratio (from coords) double map_aspect_ratio = 1; double map_width = pdata->minmax.max_lon - pdata->minmax.min_lon; double map_height = pdata->minmax.max_lat_projected - pdata->minmax.min_lat_projected; if (map_width && map_height) { map_aspect_ratio = map_width / map_height; pdata->map_aspect_ratio_from_distance = map_aspect_ratio; } mlt_properties_set_double(MLT_FILTER_PROPERTIES(filter), "map_original_aspect_ratio", map_aspect_ratio); char middle_point[255]; double middle_lat = (pdata->minmax.min_lat + pdata->minmax.max_lat) / 2; double middle_lon = (pdata->minmax.min_lon + pdata->minmax.max_lon) / 2; snprintf(middle_point, 255, "%.6f, %.6f", middle_lat, swap_180_if_needed(middle_lon)); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "map_coords_hint", middle_point); mlt_log_info(filter, "gps file [%d points] minmax: min_lat,lon-max_lat,lon: %f,%f:%f,%f; ele: %f,%f; " "speed:%f,%f; " "hr:%f,%f; grade_p:%f,%f%%, map_ar:%f, mid_point:%s \n", pdata->gps_points_size, pdata->minmax.min_lat, pdata->minmax.min_lon, pdata->minmax.max_lat, pdata->minmax.max_lon, pdata->minmax.min_ele, pdata->minmax.max_ele, pdata->minmax.min_speed, pdata->minmax.max_speed, pdata->minmax.min_hr, pdata->minmax.max_hr, pdata->minmax.min_grade_p, pdata->minmax.max_grade_p, map_aspect_ratio, middle_point); } // Reads and updates all necessary filter properties, and smooths+processes the gps data if needed static void process_filter_properties(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; char do_smoothing = 0; //read properties int read_video_offset1 = mlt_properties_get_int(properties, "time_offset"); int read_smooth_val = mlt_properties_get_int(properties, "smoothing_value"); double read_speed_multiplier = mlt_properties_get_double(properties, "speed_multiplier"); int64_t original_video_time = get_original_video_file_time_mseconds(frame); //process properties pdata->gps_offset = (int64_t) read_video_offset1 * 1000; pdata->speed_multiplier = (read_speed_multiplier ? read_speed_multiplier : 1); if (pdata->last_smooth_lvl != read_smooth_val) { pdata->last_smooth_lvl = read_smooth_val; do_smoothing = 1; } char video_start_text[255], gps_start_text[255]; mseconds_to_timestring(original_video_time, NULL, video_start_text); mseconds_to_timestring(pdata->first_gps_time, NULL, gps_start_text); if (do_smoothing) { //also always does processing + updates minmax process_gps_smoothing(filter_to_gps_data(filter), 1); find_minmax_of_data(filter); } pdata->graph_data_source = mlt_properties_get_int(properties, "graph_data_source"); pdata->graph_type = mlt_properties_get_int(properties, "graph_type"); pdata->ui_crops.trim_start_p = mlt_properties_get_double(properties, "trim_start_p"); pdata->ui_crops.start_index = pdata->gps_points_size / 100.0 * pdata->ui_crops.trim_start_p; pdata->ui_crops.start_index = CLAMP(pdata->ui_crops.start_index, 0, pdata->gps_points_size - 1); pdata->ui_crops.min_crop_time = pdata->gps_points_p[pdata->ui_crops.start_index].time; pdata->ui_crops.trim_end_p = mlt_properties_get_double(properties, "trim_end_p"); pdata->ui_crops.end_index = pdata->gps_points_size / 100.0 * pdata->ui_crops.trim_end_p; pdata->ui_crops.end_index = CLAMP(pdata->ui_crops.end_index, 0, pdata->gps_points_size - 1); pdata->ui_crops.max_crop_time = pdata->gps_points_p[pdata->ui_crops.end_index].time; pdata->ui_crops.bot = mlt_properties_get_double(properties, "crop_bot_p"); pdata->ui_crops.top = mlt_properties_get_double(properties, "crop_top_p"); pdata->ui_crops.left = mlt_properties_get_double(properties, "crop_left_p"); pdata->ui_crops.right = mlt_properties_get_double(properties, "crop_right_p"); convert_crop_values_to_percentages(filter, frame); //(re-)read background img file if path changed char *bg_path = mlt_properties_get(properties, "bg_img_path"); bool new_img_loaded = false; if (bg_path && strlen(bg_path) > 0 && bg_path[0] != '!') { if (strcmp(bg_path, pdata->last_bg_img_path)) { if (pdata->bg_img.load(bg_path)) { pdata->bg_img = pdata->bg_img.convertToFormat(QImage::Format_ARGB32); strncpy(pdata->last_bg_img_path, bg_path, 255); new_img_loaded = true; } else { mlt_log_warning(filter, "Failed to load background image: %s\n", bg_path); } } } else //delete the previous image { if (strlen(pdata->last_bg_img_path) > 0) { pdata->last_bg_img_path[0] = 0; pdata->bg_img = QImage(); pdata->bg_img_scaled = QImage(); } } //process the image so it matches the map when overlaid //-> image center must equal gps track center //-> unless image size is already perfectly matching track min/max, we'll need user input to re-scale it double bg_scale_w = mlt_properties_get_double(properties, "bg_scale_w"); if (!pdata->bg_img.isNull() && (new_img_loaded || bg_scale_w != pdata->bg_img_scaled_width)) //only update if something changed { double map_ar = pdata->map_aspect_ratio_from_distance; //mlt_properties_get_double(properties, "map_original_aspect_ratio"); double bg_ar = (double) pdata->bg_img.width() / pdata->bg_img.height(); double bg_scale_h = bg_scale_w * bg_ar / map_ar; pdata->bg_img_scaled_width = bg_scale_w; pdata->bg_img_scaled_height = bg_scale_h; // mlt_log_info(filter, "scale w=%f -> h=%f; bg_ar=%f, map_ar=%f", bg_scale_w, bg_scale_h, bg_ar, map_ar); //step 1) scale image width or height by map_aspect_ratio so now they both have the same aspect_ratio double new_bg_width = pdata->bg_img.width(); double new_bg_height = pdata->bg_img.height(); if (map_ar > 1) new_bg_width *= map_ar; else new_bg_height /= map_ar; pdata->bg_img_scaled = pdata->bg_img.scaled(new_bg_width, new_bg_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); // mlt_log_info(filter, "scaled image= x,y w,h= %d,%d %d,%d\n", pdata->bg_img_scaled.rect().x(), pdata->bg_img_scaled.rect().y(), pdata->bg_img_scaled.rect().width(), pdata->bg_img_scaled.rect().height()); // pdata->bg_img_scaled.save("qimage_saved02_scaled_by_mapAR.jpg"); //next steps are in prepare_canvas() as they depend on each frame position (used_crops) } //write properties mlt_properties_set(properties, "gps_start_text", gps_start_text); mlt_properties_set(properties, "video_start_text", video_start_text); mlt_properties_set_int(properties, "auto_gps_offset_start", (pdata->first_gps_time - original_video_time) / 1000); mlt_properties_set_int(properties, "auto_gps_offset_now", (pdata->first_gps_time - get_current_frame_time_ms(filter, frame)) / 1000); } //checks if a new file is selected and if so, does private_data cleanup and calls qxml_parse_file //+ computes some one time pdata values static void process_file(mlt_filter filter, mlt_frame frame) { private_data *pdata = (private_data *) filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); char *filename = mlt_properties_get(properties, "resource"); bool guess_offset = (mlt_properties_get_int(properties, "time_offset") == 0) && (strlen(pdata->last_filename) == 0); //if there's no file selected just return if (!filename || !strcmp(filename, "")) return; //check if the file has been changed, if not, current data is ok, do nothing if (strcmp(pdata->last_filename, filename)) { // mlt_log_info(filter, "Reading new file: last_filename (%s) != entered_filename (%s), swap_180 = %d\n", pdata->last_filename, filename, swap); default_priv_data(pdata); strcpy(pdata->last_filename, filename); if (qxml_parse_file(filter_to_gps_data(filter)) == 1) { get_first_gps_time(filter_to_gps_data(filter)); get_last_gps_time(filter_to_gps_data(filter)); //when loading the first file, sync gps start with video start int64_t original_video_time = get_original_video_file_time_mseconds(frame); if (guess_offset) { pdata->gps_offset = pdata->first_gps_time - original_video_time; mlt_properties_set_int(properties, "time_offset", pdata->gps_offset / 1000); } //assume smooth is 5 (default) so we can guarantee *gps_points_p and min/max allocation pdata->last_smooth_lvl = 5; process_gps_smoothing(filter_to_gps_data(filter), 1); find_minmax_of_data(filter); } else { default_priv_data(pdata); //store name in pdata or it'll retry reading every frame if bad file strcpy(pdata->last_filename, filename); } } } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { private_data *pdata = (private_data *) filter->child; process_file(filter, frame); if (pdata->gps_points_r == NULL || pdata->gps_points_size == 0) { return frame; } process_filter_properties(filter, frame); mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; default_priv_data(pdata); free(pdata); filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_gpsgraphic_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata && createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_string(properties, "resource", arg); //sync stuff mlt_properties_set_int(properties, "time_offset", 0); mlt_properties_set_int(properties, "smoothing_value", 5); mlt_properties_set_double(properties, "speed_multiplier", 1); //graph data stuff mlt_properties_set_int(properties, "graph_data_source", 2); mlt_properties_set_int(properties, "graph_type", 0); mlt_properties_set_double(properties, "trim_start_p", 0); mlt_properties_set_double(properties, "trim_end_p", 100); mlt_properties_set_int(properties, "crop_mode_h", 0); mlt_properties_set_double(properties, "crop_left_p", 0); mlt_properties_set_double(properties, "crop_right_p", 100); mlt_properties_set_int(properties, "crop_mode_v", 0); mlt_properties_set_double(properties, "crop_bot_p", 0); mlt_properties_set_double(properties, "crop_top_p", 100); //graph design stuff mlt_properties_set_int(properties, "color_style", 1); mlt_properties_set(properties, "color.1", "#ff00aaff"); mlt_properties_set(properties, "color.2", "#ff00e000"); mlt_properties_set(properties, "color.3", "#ffffff00"); mlt_properties_set(properties, "color.4", "#ffff8c00"); mlt_properties_set(properties, "color.5", "#ffff0000"); mlt_properties_set_int(properties, "show_now_dot", 1); mlt_properties_set(properties, "now_dot_color", "#00ffffff"); mlt_properties_set_int(properties, "show_now_text", 1); mlt_properties_set_double(properties, "angle", 0); mlt_properties_set_int(properties, "thickness", 5); mlt_properties_set(properties, "rect", "10% 10% 30% 30%"); mlt_properties_set_int(properties, "show_grid", 0); mlt_properties_set(properties, "legend_unit", ""); mlt_properties_set_int(properties, "draw_individual_dots", 0); //background image mlt_properties_set(properties, "map_coords_hint", ""); mlt_properties_set(properties, "bg_img_path", ""); mlt_properties_set_double(properties, "bg_scale_w", 1); mlt_properties_set_double(properties, "bg_opacity", 1); filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter gps graphic failed\n"); if (filter) { mlt_filter_close(filter); } if (pdata) { free(pdata); } filter = NULL; } return filter; } } mlt-7.38.0/src/modules/qt/filter_gpsgraphic.h000664 000000 000000 00000014646 15172202314 021125 0ustar00rootroot000000 000000 #ifndef GPS_GRAPHIC_H #define GPS_GRAPHIC_H //only using get_graph_colors() from graph.h #include "gps_parser.h" #include "graph.h" #include #include struct s_crops { double trim_start_p, trim_end_p; double bot, top, left, right; int64_t min_crop_time, max_crop_time; int start_index, end_index; }; struct s_gps_data_bounds { double min_lat, min_lat_projected, max_lat, max_lat_projected, min_lon, max_lon; double min_ele, max_ele, min_speed, max_speed; double min_hr, max_hr, min_grade_p, max_grade_p; void set_defaults() { min_lat = 90, min_lat_projected = project_latitude(90), min_lon = 180, max_lat = -90, max_lat_projected = project_latitude(-90), max_lon = -180; min_ele = 99999, max_ele = -99999, min_speed = 99999, max_speed = -99999; min_hr = 99999, max_hr = 0, min_grade_p = 99999, max_grade_p = -99999; } }; namespace { typedef struct { gps_point_raw *gps_points_r; //raw gps data from file gps_point_proc *gps_points_p; //processed gps data int gps_points_size; int last_smooth_lvl; int last_searched_index; //used to optimize repeated searches int64_t first_gps_time, last_gps_time; int64_t gps_offset; double speed_multiplier; char last_filename[256]; //gps file fullpath char interpolated; s_gps_data_bounds minmax; s_crops ui_crops; int graph_data_source, graph_type; mlt_rect img_rect; char last_bg_img_path[256]; double map_aspect_ratio_from_distance; QImage bg_img, bg_img_scaled; double bg_img_scaled_width, bg_img_scaled_height; QRectF bg_img_matched_rect; int swap_180; } private_data; } // namespace typedef enum { gspg_location_src = 0, gpsg_altitude_src, gpsg_hr_src, gpsg_speed_src } gpsg_data_sources; typedef enum { gpsg_latitude_id = gspg_location_src, gpsg_longitude_id = gpsg_latitude_id + 100 } gpsg_data_subids; typedef enum { gpsg_2d_map_graph = 0, gpsg_crop_center_graph, gpsg_speedometer_graph //gpsg_ladder_graph, //was thinking a vertical ruler/gauge thing for altitude, couldn't design something that actually looks good //gpsg_compass_graph //probably easy to adapt from speedometer but not really that important to be worth the extra code } gpsg_graph_types; typedef enum { gpsg_color_by_solid = 0, gpsg_color_by_solid_past_future, gpsg_color_by_solid_past, gpsg_color_by_solid_future, gpsg_color_by_vertical_gradient, gpsg_color_by_horizontal_gradient, gpsg_color_by_duration, gpsg_color_by_altitude, gpsg_color_by_hr, gpsg_color_by_speed, gpsg_color_by_speed_max100, gpsg_color_by_grade_max90, gpsg_color_by_grade_max20 } gpsg_color_styles; struct s_base_crops { double bot, top, left, right; }; struct point_2d { double x, y; }; /**** functions in filter_gpsgraphic.cpp needed in gps_drawing.cpp ****/ //apply crop percentages to [min, max] then compute value val as a percentage of [new_min, new_max] //if inside_only is true, result is always inside interval [0..1] template double crop_and_normalize(T val, T min, T max, double p_min, double p_max, bool inside_only = false) { double ret = 0; T delta = max - min; T new_min = min + p_min * delta / 100.0; T new_max = min + delta * p_max / 100.0; if (new_max == new_min) //divide by zero -> draw a constant line ret = 0.5; else ret = (double) (val - new_min) / (new_max - new_min); // mlt_log_info(NULL, "crop_and_normalize (val=%f, min=%f, max=%f, p_min=%f, p_max=%f) result=%f [restricted to 0..1]\n", val, min, max, p_min, p_max, ret); if (inside_only) ret = CLAMP(ret, 0, 1); return ret; } //convert_[distance|speed]_to_format but graph data source aware double convert_bysrc_to_format(mlt_filter filter, double val); //like decimals_needed() but graph data source aware int decimals_needed_bysrc(mlt_filter filter, double v); /* Gets the value from pdata->minmax (or custom|existing gps_point) depending on data_src value get_type represents: 0=current_value, 1=max_value, -1=min_value subtype is used for longitude gps_p is used to get (only for get_type=0) the proper value by source from points not in pdata->gps_points_p[] */ double get_by_src( mlt_filter filter, int get_type, int i_gps = 0, int subtype = 0, gps_point_proc *gps_p = NULL); //get_by_src shortcuts double get_min_bysrc(mlt_filter filter, int subtype = 0); double get_max_bysrc(mlt_filter filter, int subtype = 0); double get_crtval_bysrc(mlt_filter filter, int i_gps, int subtype = 0, gps_point_proc *gps_p = NULL); //returns the next gps point with a valid value for crt_source (starting with crt_i+1) int get_next_valid_gpspoint_index(mlt_filter filter, int crt_i); //gets the nearest gps point [index] according to video time + input offset int get_now_gpspoint_index(mlt_filter filter, mlt_frame frame, bool force_result = true); //returns an interpolated gps point at the exact current video time + offset gps_point_proc get_now_weighted_gpspoint(mlt_filter filter, mlt_frame frame, bool force_result = true); gps_private_data filter_to_gps_data(mlt_filter filter); /**** functions in gps_drawing.cpp needed in filter_gpsgraphic.cpp ****/ //draws 5 horizontal (+5 vertical for 2D map) with small text for each line showing the graph value at that point void draw_legend_grid(mlt_filter filter, mlt_frame frame, QPainter &p, s_base_crops &used_crops); //inits drawing rect +clipping, antialiasing, and applies rotate void prepare_canvas(mlt_filter filter, mlt_frame frame, QImage *qimg, QPainter &p, int width, int height, s_base_crops &used_crops); //draws a small dot/disc on the map/graph according to the current video time + sync void draw_now_dot(mlt_filter filter, mlt_frame frame, QPainter &p, s_base_crops &used_crops); //draws a speedometer from the current data source (for map it displays % of total) void draw_main_speedometer(mlt_filter filter, mlt_frame frame, QPainter &p, s_base_crops &used_crops); //draws a 2d graph of the chosen graph source (for non-location, second axis is time) void draw_main_line_graph(mlt_filter filter, mlt_frame frame, QPainter &p, s_base_crops &used_crops); #endif mlt-7.38.0/src/modules/qt/filter_gpsgraphic.yml000664 000000 000000 00000040174 15172202314 021472 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: gpsgraphic title: GPS Graphic version: 3 copyright: Meltytech, LLC creator: Daniel F license: LGPLv2.1 language: en tags: - Video description: Overlay GPS-related graphics onto the video parameters: - identifier: resource title: GPS File/URL description: > The fullpath of file containing location (GPS) data. Supported extensions: .gpx, .tcx. type: string required: yes readonly: no widget: fileopen - identifier: time_offset title: Time offset description: > An offset (in seconds) to be added to the video file to match it to the GPS track. Most of the time this will at least need to be set to the timezone difference between the 2 files plus some seconds if the video recording device isn't precisely set to correct time. GPS time is always exact and in UTC. Use positive values if GPS is ahead of video and negative otherwise. type: integer default: 0 required: no readonly: no mutable: yes widget: text - identifier: smoothing_value title: Smoothing level description: > How many GPS points to smooth in order to eliminate GPS errors. A value of 1 does not smooth locations, it only calculates the extra fields (speed, distance, etc) it also interpolates missing values for heart rate and altitude. type: integer default: 5 minimum: 1 readonly: no required: no mutable: yes widget: text - identifier: speed_multiplier title: Speed multiplier description: > If the video file is a timelapse (or slow motion), use this value to set the speed up/slow down ratio. Sample values: 30 for 30x timelapse, 0.25 for 4x slow motion footage. type: float default: 1 readonly: no required: no mutable: yes widget: text # graph data source params: - identifier: graph_data_source title: Graph data source description: | What data from the GPS file is used for drawing: 0 = GPS location/track 1 = altitude (if available) 2 = heart rate (if available) 3 = speed (always available, computed from location) type: integer default: 0 minimum: 0 maximum: 3 readonly: no mutable: yes widget: combo - identifier: graph_type title: Graph type description: | How to draw the selected data: 0 = a basic 2D map line (for location) or 1D graph per time (others) 1 = zooms in onto the map/graph and centers around the current location 2 = draws a speedometer (available for all data sources but recommended only for speed; for the map type it represents the "percentage done" from trimmed start - end) Note: for type 1 (follow centered dot): * crop values are only valid as a percentage and only the bottom (resp left) values will be taken into consideration as both values (ie: bot/top) will need to be equal to keep the dot centered) * if data source is not GPS location, the centering will only be done for horizontal axis (time), vertical axis crop will behave just like for the type 0 (it will statically keep the same min/max limit allowing the now_dot to move up and down). type: integer default: 0 minimum: 0 maximum: 2 readonly: no mutable: yes widget: combo - identifier: trim_start_p title: Trim start description: > Trim data from the start of the gps file (as a percentage of total). Note: both trim_start_p and trim_end_p are computed from the total file so trimming 50% start and 50% end will result in trimming the entire file. type: float default: 0 minimum: 0 maximum: 100 readonly: no mutable: yes widget: spinner - identifier: trim_end_p title: Trim end description: > Trim data from the end of the gps file (as a *reversed* percentage of total, ie: 100 means no trim, 50 means half of total file, 5 means trim 95% of the file). Note: both trim_start_p and trim_end_p are computed from the total file so trimming 50% start and 50% end will result in trimming the entire file. type: float default: 100 minimum: 0 maximum: 100 readonly: no mutable: yes widget: spinner - identifier: crop_mode_h title: Crop mode horizontal description: | Decides how to interpret the crop_left_p and crop_right_p values: 0 = a percentage from min..max 1 = an absolute value (ie: it can crop between 22.2 and 22.3 degrees of longitude) Note: for the horizontal type, absolute values are the longitude (for the location source type) and time (in miliseconds since epoch) for the rest of the data source types) type: integer default: 0 minimum: 0 maximum: 1 readonly: no mutable: yes widget: combo - identifier: crop_left_p title: Crop left description: > Crops data from the left side of the graph (effectively zooming in). The value is interpreted either as a percentage of total or an absolute value depending on crop_mode_h. In percentage mode the values are not restricted to 0-100 to allow for "zoom out" behaviour (ie: cropping -50 left will add an extra half of the total horizontal distance). Values over 100 (in % mode) will effectively not display anything. If graph_type is speedometer, all crop left/right values are ignored. type: float default: 0 readonly: no mutable: yes widget: spinner - identifier: crop_right_p title: Crop left description: > Same as crop_left_p but for the right side and percentage type is interpreted as an inverse percentage (ie: 100 = do not crop anything). Values under 0 will effectively not display anything. type: float default: 100 readonly: no mutable: yes widget: spinner - identifier: crop_mode_v title: Crop mode vertical description: | Decides how to interpret the crop_bot_p and crop_top_p values: 0 = a percentage from min..max 1 = an absolute value (ie: it can zoom in to between 100 and 150m of altitude to show small changes in altitude between those 2 values better) Note: for the vertical type, absolute values are latitude degrees (for the location source type) and altitude, heart rate, speed for the others interpreted as the legend_unit type where applicable (ie: a value of 10 for altitude will be considered meters by default but if changing legend_unit to feet it will mean 10 feet). type: integer default: 0 minimum: 0 maximum: 1 readonly: no mutable: yes widget: combo - identifier: crop_bot_p title: Crop left description: > Crops data from the bottom side of the graph (effectively zooming in). The value is interpreted either as a percentage of total or an absolute value depending on crop_mode_v. In percentage mode the values are not restricted to 0-100 to allow for "zoom out" behaviour (ie: cropping -50 bot will add an extra half of the total vertical distance to the bottom). Values over 100 (in % mode) will effectively not display anything. If graph_type is speedometer, this will set the minimum needle position which will clamp all values that are lower. type: float default: 0 readonly: no mutable: yes widget: spinner - identifier: crop_top_p title: Crop left description: > Same as crop_bot_p but for the top side and percentage type is interpreted as an inverse percentage (ie: 100 = do not crop anything). Values under 0 will effectively not display anything. type: float default: 100 readonly: no mutable: yes widget: spinner # graph design params: - identifier: color_style title: Graph color style description: | Chooses one of the following styles to draw the graph line: 0 = one color -> same color and size for the entire graph 1 = two colors -> same as 2 and 3 but the entire line is the same thickness 2 = solid past, thin future -> from the beginning of the graph to the current position (="past") it will be drawn using the 1st color and chosen thickness, but for the "future" part of the graph it will use the 2nd color and thickness will be 2px (or 1px if main thickness is below 3) 3 = solid future, thin past 4 = vertical gradient -> the line will be coloured as a vertical gradient relative to the entire rect area 5 = horizontal gradient 6 = color by duration -> the selected colors will be used as a gradient, in chronological time (except for location source, this will effectively be a left to right gradient for 1D graphs) 7 = color by altitude -> the selected colors will be used as a gradient from the minimum altitude value from file to the maximum one, not affected by crops or trim 8 = color by heart rate 9 = color by speed -> same as above but gradient is affected by smoothing 10 = color by speed (max 100km/h) -> max speed accepted is 100km/h - to cover bad gps errors 11 = color by grade (max 90 degrees) -> the gradient follows the grade value but relative to a maximum of 90 degrees (if max grade is less than 90, it will use that value); affected by smoothing 12 = Color by grade (max 20 degrees) -> as above but max color is relative to max 20 degrees; this provides expected colors for most GPS tracks otherwise a single steeper area would make the rest of the track indistinguishible from flat ground type: integer default: 1 minimum: 0 maximum: 9 readonly: no mutable: yes widget: combo - identifier: color.* title: Colours type: color description: | Sets the colours of the graph line. Multiple colours can be specified with incrementing suffixes to cause the line to be drawn in a specific way (ie: gradient or past/future). By default, the filter has 5 colors (blue, green, yellow, orange, red): color.1 = #ff00aaff color.2 = #ff00e000 color.3 = #ffffff00 color.4 = #ffff8c00 color.5 = #ffff0000 A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color animation: yes - identifier: show_now_dot title: Show now dot description: > Enable it to draw a disc at the current location/time over the graph line. If graph type is speedometer, this affects the needle. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: now_dot_color title: Now dot colour type: color description: > Choose the outer circle color of the now_dot disc. The size of this circle is the same as the line thickness. The inside of the disc is always white. If the alpha value of the color is 0 (default) this will use the same colour as the nearby (or past) line (including for gradient types) thus effectively making it change color in time. A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color animation: yes - identifier: show_now_text title: Show now text description: > Enable it to draw the current value in big white bold letters on the bottom right side of the rect. The legend_unit value will be appended at the end and it will be used as the current unit (if a valid unit is found ie: kilometers if "km" is found anywhere in the legend_unit string). type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: angle title: Rotation description: > Rotate the entire graph rect. For speedometer type the text stays horizontal. type: float default: 0 readonly: no mutable: yes - identifier: thickness title: Thickness description: > Sets the thickness of the line graph in px. type: integer default: 5 minimum: 1 readonly: no mutable: yes - identifier: show_grid title: Draw legend description: > If enabled it will draw 5 horizontal (and vertical for map type) lines with small values each corresponding to the current data source value at 0%, 25%, 50%, 75% and 100% of current graph shown, affected by the legend_unit type if applicable and with the string appended to the value. For speedometer type this shows division values (but without appending unit). type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: legend_unit title: Legend unit description: > Sets the unit to be used for displaying values of type altitude and speed. Default is meters and km/h respectively. The unit is matched anywhere in the string so extra spaces can be used to tweak displaying. Supported formats, distance: m|meter, km|kilometer*, mi|mile*, ft|feet, nm|nautical*; speed: ms|m/s|meter, km|km/h|kilometer, mi|mi/h|mile, ft|ft/s|feet, kn|nm/h|knots, mmin|m/min, ftmin|ft/min. type: string default: m readonly: no mutable: yes - identifier: draw_individual_dots title: Show gots only description: > If enabled the graph will be drawn using individual dots instead of lines. This will effectively show the individual data points as affected by smoothing (ie: for location data it will display each GPS fix if smoothing is 1) and can either be used as a cool effect when zoomed in enough or to debug unexpected line jumps. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: rect title: Rectangle description: > Defines the rectangle that the graph should be drawn in. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 100% 100%" readonly: no mutable: yes # background params: - identifier: bg_img_path title: Background image description: > If a valid image file is selected it will be used as a background for the rect area. Paths starting with the "!" character are ignored. TIP: use a map image to add context to the gps track. type: string required: no readonly: no widget: fileopen - identifier: bg_scale_w title: Background scale description: > Scale the background image (relative to center) to match it to the above gps track. Values smaller than 1 zoom into the image, values larger than 1 zoom out. 0 hides it. type: float default: 1 minimum: 0 maximum: 2 readonly: no mutable: yes - identifier: bg_opacity title: Background opacity description: > Sets the opacity of the background image. type: float default: 1 minimum: 0 maximum: 1 readonly: no mutable: yes # read only: - identifier: gps_start_text title: GPS start time description: Date and time of the first valid GPS point. readonly: yes - identifier: video_start_text title: Video start time description: Date and time of the video file. readonly: yes - identifier: auto_gps_offset_start title: Auto offset start description: > Provides a helper offset to guarantee start of video file syncs with the start of gps file. Correctly sets the offset if video file and gps recording was started at the same time. readonly: yes - identifier: auto_gps_offset_now title: Auto offset now description: > Provides a helper offset to sync the first gps point to current video time (it is updated every second). Correctly sets the offset if you video record the moment gps starts. readonly: yes - identifier: map_coords_hint title: Map hint description: Returns the middle lat, lon coordinates of the gps file. readonly: yes mlt-7.38.0/src/modules/qt/filter_gpstext.cpp000664 000000 000000 00000072543 15172202314 021027 0ustar00rootroot000000 000000 /* * filter_gpstext.c -- overlays GPS data as text on video * Copyright (C) 2011-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gps_parser.h" #include static QMutex f_mutex; #define MAX_TEXT_LEN 1024 namespace { typedef struct { gps_point_raw *gps_points_r; //raw gps data from file gps_point_proc *gps_points_p; //processed gps data int gps_points_size; int last_smooth_lvl; int last_searched_index; //used to optimize repeated searches int64_t first_gps_time; int64_t last_gps_time; int64_t gps_offset; int64_t gps_proc_start_t; //process only points after this time (epoch miliseconds) double speed_multiplier; double updates_per_second; char last_filename[256]; //gps file fullpath char interpolated; int swap_180; } private_data; } // namespace // Sets the private data to default values and frees gps points array static void default_priv_data(private_data *pdata) { if (pdata) { if (pdata->gps_points_r) free(pdata->gps_points_r); if (pdata->gps_points_p) free(pdata->gps_points_p); memset(pdata, 0, sizeof(private_data)); pdata->speed_multiplier = 1; pdata->updates_per_second = 1; } } // Collects often used filter private data into a single variable static gps_private_data filter_to_gps_data(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; gps_private_data ret; ret.gps_points_r = pdata->gps_points_r; ret.gps_points_p = pdata->gps_points_p; ret.ptr_to_gps_points_r = &pdata->gps_points_r; ret.ptr_to_gps_points_p = &pdata->gps_points_p; ret.gps_points_size = &pdata->gps_points_size; ret.last_searched_index = &pdata->last_searched_index; ret.first_gps_time = &pdata->first_gps_time; ret.last_gps_time = &pdata->last_gps_time; ret.interpolated = &pdata->interpolated; ret.swap180 = &pdata->swap_180; ret.gps_proc_start_t = pdata->gps_proc_start_t; ret.last_smooth_lvl = pdata->last_smooth_lvl; ret.last_filename = pdata->last_filename; ret.filter = filter; return ret; } // Get the next token and indicate whether it is enclosed in "# #". static int get_next_token(char *str, int *pos, char *token, int *is_keyword) { int token_pos = 0; int str_len = strlen(str); if ((*pos) >= str_len || str[*pos] == '\0') return 0; if (str[*pos] == '#') { *is_keyword = 1; (*pos)++; } else *is_keyword = 0; while (*pos < str_len && token_pos < MAX_TEXT_LEN - 1) { if (str[*pos] == '\\' && str[(*pos) + 1] == '#') { // Escape Sequence - "#" preceded by "\" - copy the # into the token. token[token_pos] = '#'; token_pos++; (*pos)++; // skip "\" (*pos)++; // skip "#" } else if (str[*pos] == '#') { if (*is_keyword) { // Found the end of the keyword (*pos)++; } break; } else { token[token_pos] = str[*pos]; token_pos++; (*pos)++; } } token[token_pos] = '\0'; return 1; } /* Processes the offset (seconds) in a time keyword and removes it * #gps_datetime_now +3600# -> add 1hour * returns value in miliesconds */ static int64_t extract_offset_time_ms_keyword(char *keyword) { //mlt_log_info(NULL, "extract_offset_time_keyword: in keyword: %s\n", keyword); char *end = NULL; if (keyword == NULL) return 0; int val = strtol(keyword, &end, 0); //if found, remove the processed number if (val != 0) { if (strlen(end) == 0) *keyword = '\0'; else memmove(keyword, end, strlen(end) + 1); } return val * 1000; } /** Replaces the GPS keywords with actual values, also parses any keyword extra format. * Returns "--" for keywords with no valid return. */ static void gps_point_to_output(mlt_filter filter, char *keyword, char *result_gps_text, int i_now, int64_t req_time, gps_point_proc now_gps) { private_data *pdata = (private_data *) filter->child; char *format = NULL; char gps_text[MAX_TEXT_LEN]; strcpy(gps_text, "--"); if (i_now == -1 || pdata->gps_points_r == NULL) { strncat(result_gps_text, gps_text, MAX_TEXT_LEN - strlen(result_gps_text) - 1); return; } //assign the raw or processed values to a tmp point (depending on smoothing value) gps_point_proc crt_point = uninit_gps_proc_point; gps_point_raw raw = pdata->gps_points_r[i_now]; if (pdata->last_smooth_lvl == 0) { crt_point.lat = raw.lat; crt_point.lat_projected = raw.lat_projected; crt_point.lon = raw.lon; crt_point.speed = raw.speed; crt_point.total_dist = raw.total_dist; crt_point.ele = raw.ele; crt_point.bearing = raw.bearing; crt_point.hr = raw.hr; crt_point.cad = raw.cad; crt_point.atemp = raw.atemp; crt_point.power = raw.power; } else { if (pdata->gps_points_p == NULL) return; //now_gps is already interpolated to frame time + updates_per_second + speed if (pdata->updates_per_second != 0) crt_point = now_gps; else crt_point = pdata->gps_points_p[i_now]; } //check for the generic "decimals" extra-keyword and if present read a single digit after it (+whitespace) char *ptr = NULL; int use_decimals = -1; if ((ptr = strstr(keyword, "decimals"))) { ptr += strlen("decimals"); while (ptr && isspace(*ptr)) ptr++; if (ptr && isdigit(*ptr)) use_decimals = *ptr - '0'; } //check for generic "RAW" extra keyword bool use_raw = 0; if (strstr(keyword, "RAW")) use_raw = 1; /* for every keyword: we first check if the "RAW" keyword is used and if so, we print the value read from file (or --) then we check if there's a format keyword and apply the necessary conversion/format. The format must appear after the main keyword, RAW and format order is not important (strstr is used) */ if (!strncmp(keyword, "gps_lat", strlen("gps_lat")) && crt_point.lat != GPS_UNINIT) { double val = use_raw ? raw.lat : crt_point.lat; if (val == GPS_UNINIT) return; snprintf(gps_text, 15, "%3.*f", (use_decimals == -1 ? 6 : use_decimals), val); } else if (!strncmp(keyword, "gps_lon", strlen("gps_lon")) && crt_point.lon != GPS_UNINIT) { double val = swap_180_if_needed(use_raw ? raw.lon : crt_point.lon); if (val == GPS_UNINIT) return; snprintf(gps_text, 15, "%3.*f", (use_decimals == -1 ? 6 : use_decimals), val); } else if (!strncmp(keyword, "gps_elev", strlen("gps_elev")) && crt_point.ele != GPS_UNINIT) { if (strlen(keyword) > strlen("gps_elev")) format = keyword + strlen("gps_elev"); double val = convert_distance_to_format((use_raw ? raw.ele : crt_point.ele), format); if (val == GPS_UNINIT) return; snprintf(gps_text, 15, "%.*f", decimals_needed(val, use_decimals), val); } else if (!strncmp(keyword, "gps_speed", strlen("gps_speed")) && crt_point.speed != GPS_UNINIT) { if (strlen(keyword) > strlen("gps_speed")) format = keyword + strlen("gps_speed"); double val = use_raw ? raw.speed : crt_point.speed; if (!use_raw && strstr(keyword, "vertical")) val = crt_point.speed_vertical; else if (!use_raw && strstr(keyword, "3d")) val = crt_point.speed_3d; if (val == GPS_UNINIT) return; val = convert_speed_to_format(val, format); snprintf(gps_text, 15, "%.*f", decimals_needed(val, use_decimals), val); } else if (!strncmp(keyword, "gps_hr", strlen("gps_hr")) && crt_point.hr != GPS_UNINIT) { double val = use_raw ? raw.hr : crt_point.hr; if (val == GPS_UNINIT) return; snprintf(gps_text, 15, "%.*f", (use_decimals == -1 ? 0 : use_decimals), val); } else if (!strncmp(keyword, "gps_bearing", strlen("gps_bearing")) && crt_point.bearing != GPS_UNINIT) { double val = use_raw ? raw.bearing : crt_point.bearing; if (val == GPS_UNINIT) return; snprintf(gps_text, 15, "%.*f", (use_decimals == -1 ? 0 : use_decimals), val); } else if (!strncmp(keyword, "gps_compass", strlen("gps_compass")) && crt_point.bearing != GPS_UNINIT) { const char *val = (const char *) (bearing_to_compass(use_raw ? raw.bearing : crt_point.bearing)); snprintf(gps_text, 4, "%s", val); } else if (!strncmp(keyword, "gps_cadence", strlen("gps_cadence")) && crt_point.cad != GPS_UNINIT) { double val = use_raw ? raw.cad : crt_point.cad; if (val == GPS_UNINIT) return; snprintf(gps_text, 15, "%.*f", (use_decimals == -1 ? 0 : use_decimals), crt_point.cad); } else if (!strncmp(keyword, "gps_temperature", strlen("gps_temperature")) && crt_point.atemp != GPS_UNINIT) { double val = use_raw ? raw.atemp : crt_point.atemp; if (val == GPS_UNINIT) return; if (strstr(keyword, "F")) val = val * 1.8 + 32; else if (strstr(keyword, "K")) val = val + 273.15; snprintf(gps_text, 15, "%.*f", (use_decimals == -1 ? 0 : use_decimals), val); } else if (!strncmp(keyword, "gps_power", strlen("gps_power")) && crt_point.power != GPS_UNINIT) { double val = use_raw ? raw.power : crt_point.power; if (val == GPS_UNINIT) return; snprintf(gps_text, 15, "%.*f", (use_decimals == -1 ? 0 : use_decimals), val); } else if (!strncmp(keyword, "gps_vdist_up", strlen("gps_vdist_up")) && crt_point.elev_up != GPS_UNINIT) { if (strlen(keyword) > strlen("gps_vdist_up")) format = keyword + strlen("gps_vdist_up"); double val = convert_distance_to_format(fabs(crt_point.elev_up), format); snprintf(gps_text, 15, "%.*f", decimals_needed(val, use_decimals), val); } else if (!strncmp(keyword, "gps_vdist_down", strlen("gps_vdist_down")) && crt_point.elev_down != GPS_UNINIT) { if (strlen(keyword) > strlen("gps_vdist_down")) format = keyword + strlen("gps_vdist_down"); double val = convert_distance_to_format(fabs(crt_point.elev_down), format); snprintf(gps_text, 15, "%.*f", decimals_needed(val, use_decimals), val); } else if (!strncmp(keyword, "gps_dist_uphill", strlen("gps_dist_uphill")) && crt_point.dist_up != GPS_UNINIT) { if (strlen(keyword) > strlen("gps_dist_uphill")) format = keyword + strlen("gps_dist_uphill"); double val = convert_distance_to_format(crt_point.dist_up, format); snprintf(gps_text, 15, "%.*f", decimals_needed(val, use_decimals), val); } else if (!strncmp(keyword, "gps_dist_downhill", strlen("gps_dist_downhill")) && crt_point.dist_down != GPS_UNINIT) { if (strlen(keyword) > strlen("gps_dist_downhill")) format = keyword + strlen("gps_dist_downhill"); double val = convert_distance_to_format(crt_point.dist_down, format); snprintf(gps_text, 15, "%.*f", decimals_needed(val, use_decimals), val); } else if (!strncmp(keyword, "gps_dist_flat", strlen("gps_dist_flat")) && crt_point.dist_flat != GPS_UNINIT) { if (strlen(keyword) > strlen("gps_dist_flat")) format = keyword + strlen("gps_dist_flat"); double val = convert_distance_to_format(crt_point.dist_flat, format); snprintf(gps_text, 15, "%.*f", decimals_needed(val, use_decimals), val); } //NOTE: gps_dist must be below gps_dist_up/down/flat or it'll match them else if (!strncmp(keyword, "gps_dist", strlen("gps_dist")) && crt_point.total_dist != GPS_UNINIT) { if (strlen(keyword) > strlen("gps_dist")) format = keyword + strlen("gps_dist"); double val = convert_distance_to_format((use_raw ? raw.total_dist : crt_point.total_dist), format); if (val == GPS_UNINIT) return; snprintf(gps_text, 15, "%.*f", decimals_needed(val, use_decimals), val); } else if (!strncmp(keyword, "gps_grade_percentage", strlen("gps_grade_percentage")) && crt_point.grade_p != GPS_UNINIT) { double val = crt_point.grade_p; snprintf(gps_text, 15, "%+.*f", (use_decimals == -1 ? 0 : use_decimals), val); } else if (!strncmp(keyword, "gps_grade_degrees", strlen("gps_grade_degrees")) && crt_point.grade_p != GPS_UNINIT) { double val = to_deg(atan(crt_point.grade_p / 100.0)); snprintf(gps_text, 15, "%+.*f", (use_decimals == -1 ? 0 : use_decimals), val); } else if (!strncmp(keyword, "gps_datetime_now", strlen("gps_datetime_now")) && raw.time != GPS_UNINIT) { int64_t val = 0; char *offset = NULL; if ((offset = strstr(keyword, "+")) != NULL || (offset = strstr(keyword, "-")) != NULL) val = extract_offset_time_ms_keyword(offset); if (strlen(keyword) > strlen("gps_datetime_now")) format = keyword + strlen("gps_datetime_now"); mseconds_to_timestring(raw.time + val, format, gps_text); } strncat(result_gps_text, gps_text, MAX_TEXT_LEN - strlen(result_gps_text) - 1); // mlt_log_info(NULL, "filter_gps.c gps_point_to_output, keyword=%s, result_gps_text=%s\n", keyword, result_gps_text); } // Returns the unix time (miliseconds) of "Media Created" metadata, or fallbacks to "Modified Time" from OS static int64_t get_original_video_file_time_mseconds(mlt_frame frame) { mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer(frame)); return mlt_producer_get_creation_time(producer); } //Restricts how many updates per second are done (the searched gps - frame time is altered) static int64_t restrict_updates(int64_t fr, double upd_per_sec) { if (upd_per_sec == 0) // = disabled return fr; int64_t rez = fr - fr % (int) (1000.0 / upd_per_sec); //mlt_log_info(NULL, "_time restrict: %d [%f x] -> %d\n", fr%100000, upd_per_sec, rez%100000); return rez; } /** Returns absolute* current frame time in miliseconds * (original file creation + current timecode) * *also applies updates_per_second and speed_multiplier */ static int64_t get_current_frame_time_ms(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; int64_t file_time = 0, fr_time = 0; file_time = get_original_video_file_time_mseconds(frame); mlt_position frame_position = mlt_frame_original_position(frame); f_mutex.lock(); char *s = mlt_properties_frames_to_time(properties, frame_position, mlt_time_clock); if (s) { int h = 0, m = 0, sec = 0, msec = 0; sscanf(s, "%d:%d:%d.%d", &h, &m, &sec, &msec); fr_time = (h * 3600 + m * 60 + sec) * 1000 + msec; } else mlt_log_warning(filter, "get_current_frame_time_ms time string null, giving up " "[mlt_frame_original_position()=%d], retry result:%s\n", frame_position, mlt_properties_frames_to_time(properties, frame_position, mlt_time_clock)); f_mutex.unlock(); return file_time + restrict_updates(fr_time, pdata->updates_per_second) * pdata->speed_multiplier; } /** Reads and updates all necessary filter properties, and processes the gps data if needed */ static void process_filter_properties(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; char do_smoothing = 0, do_processing = 0; //read properties int read_video_offset1 = mlt_properties_get_int(properties, "time_offset"); int read_smooth_val = mlt_properties_get_int(properties, "smoothing_value"); char *read_gps_processing_start_time = mlt_properties_get(properties, "gps_processing_start_time"); double read_speed_multiplier = mlt_properties_get_double(properties, "speed_multiplier"); double read_updates_per_second = mlt_properties_get_double(properties, "updates_per_second"); int64_t original_video_time = get_original_video_file_time_mseconds(frame); // mlt_log_info(filter, "process_filter_properties - read values: offset1=%d, smooth=%d, gps_start_time=%s, speed=%f, updates=%f\n", // read_video_offset1, read_smooth_val, read_gps_processing_start_time, read_speed_multiplier, read_updates_per_second); //process properties pdata->gps_offset = (int64_t) read_video_offset1 * 1000; pdata->speed_multiplier = (read_speed_multiplier ? read_speed_multiplier : 1); pdata->updates_per_second = read_updates_per_second; if (pdata->last_smooth_lvl != read_smooth_val) { pdata->last_smooth_lvl = read_smooth_val; do_smoothing = 1; } if (read_gps_processing_start_time != NULL) { int64_t gps_proc_t = 0; if (strlen(read_gps_processing_start_time) != 0 && strcmp(read_gps_processing_start_time, "yyyy-MM-dd hh:mm:ss")) gps_proc_t = datetimeXMLstring_to_mseconds(read_gps_processing_start_time, (char *) "yyyy-MM-dd hh:mm:ss"); if (gps_proc_t != pdata->gps_proc_start_t) { pdata->gps_proc_start_t = gps_proc_t; do_processing = 1; } } else if (pdata->gps_proc_start_t != 0) { pdata->gps_proc_start_t = 0; do_processing = 1; } char video_start_text[255], gps_start_text[255]; mseconds_to_timestring(original_video_time, NULL, video_start_text); mseconds_to_timestring(pdata->first_gps_time, NULL, gps_start_text); if (do_smoothing) process_gps_smoothing(filter_to_gps_data(filter), 1); else if (do_processing) //smoothing also does processing recalculate_gps_data(filter_to_gps_data(filter)); char gps_processing_start_now[255]; int64_t gps_now = get_current_frame_time_ms(filter, frame) + pdata->gps_offset; mseconds_to_timestring(gps_now, NULL, gps_processing_start_now); //write properties mlt_properties_set(properties, "gps_start_text", gps_start_text); mlt_properties_set(properties, "video_start_text", video_start_text); mlt_properties_set_int(properties, "auto_gps_offset_start", (pdata->first_gps_time - original_video_time) / 1000); mlt_properties_set_int(properties, "auto_gps_offset_now", (pdata->first_gps_time - get_current_frame_time_ms(filter, frame)) / 1000); mlt_properties_set(properties, "auto_gps_processing_start_now", gps_processing_start_now); } /** Replaces file_datetime_now with absolute time-date string (video created + current timecode) * (time includes speed_multiplier and updates per second) */ static void get_current_frame_time_str(char *keyword, mlt_filter filter, mlt_frame frame, char *result) { int64_t val = 0; char *offset = NULL, *format = NULL; //check for seconds offset if ((offset = strstr(keyword, "+")) != NULL || (offset = strstr(keyword, "-")) != NULL) val = extract_offset_time_ms_keyword(offset); //check for time format char text[MAX_TEXT_LEN]; if (strlen(keyword) > strlen("file_datetime_now")) format = keyword + strlen("file_datetime_now"); mseconds_to_timestring(val + get_current_frame_time_ms(filter, frame), format, text); strncat(result, text, MAX_TEXT_LEN - strlen(result) - 1); } /** Perform substitution for keywords that are enclosed in "# #". * Also prepares [current] gps point */ static void substitute_keywords(mlt_filter filter, char *result, char *value, mlt_frame frame) { private_data *pdata = (private_data *) filter->child; char keyword[MAX_TEXT_LEN] = ""; int pos = 0, is_keyword = 0; //prepare current gps point here so it is reused for all keywords int64_t video_time_synced = get_current_frame_time_ms(filter, frame) + pdata->gps_offset; int i_now = binary_search_gps(filter_to_gps_data(filter), video_time_synced); int max_gps_diff_ms = get_max_gps_diff_ms(filter_to_gps_data(filter)); gps_point_proc crt_point = uninit_gps_proc_point; if (i_now != -1 && pdata->gps_points_p && time_val_between_indices_proc(video_time_synced, pdata->gps_points_p, i_now, pdata->gps_points_size - 1, max_gps_diff_ms, false)) crt_point = weighted_middle_point_proc(&pdata->gps_points_p[i_now], &pdata->gps_points_p[i_now + 1], video_time_synced, max_gps_diff_ms); while (get_next_token(value, &pos, keyword, &is_keyword)) { if (!is_keyword) { strncat(result, keyword, MAX_TEXT_LEN - strlen(result) - 1); } else if (!strncmp(keyword, "gps_", strlen("gps_"))) { gps_point_to_output(filter, keyword, result, i_now, video_time_synced, crt_point); } else if (!strncmp(keyword, "file_datetime_now", strlen("file_datetime_now"))) { get_current_frame_time_str(keyword, filter, frame, result); } else { // replace keyword with property value from this frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); char *frame_value = mlt_properties_get(frame_properties, keyword); if (frame_value) { strncat(result, frame_value, MAX_TEXT_LEN - strlen(result) - 1); } } } } //checks if a new file is selected and if so, does private_data cleanup and calls xml_parse_file static void process_file(mlt_filter filter, mlt_frame frame) { private_data *pdata = (private_data *) filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); char *filename = mlt_properties_get(properties, "resource"); if (filename == NULL) filename = mlt_properties_get(properties, "gps.file"); /* for backwards compatibility with v1 */ //if there's no file selected just return if (!filename || !strcmp(filename, "")) return; //check if the file has been changed, if not, current data is ok, do nothing if (strcmp(pdata->last_filename, filename)) { // mlt_log_info(filter, "Reading new file: last_filename (%s) != entered_filename (%s), swap_180 = %d\n", pdata->last_filename, filename, swap); default_priv_data(pdata); strcpy(pdata->last_filename, filename); if (qxml_parse_file(filter_to_gps_data(filter)) == 1) { get_first_gps_time(filter_to_gps_data(filter)); get_last_gps_time(filter_to_gps_data(filter)); //assume smooth is 5 (default) so we can guarantee *gps_points_p and save some time pdata->last_smooth_lvl = 5; process_gps_smoothing(filter_to_gps_data(filter), 1); } else { default_priv_data(pdata); //store name in pdata or it'll retry reading every frame if bad file strcpy(pdata->last_filename, filename); } } } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); char *dynamic_text = mlt_properties_get(properties, "argument"); if (!dynamic_text || !strcmp("", dynamic_text)) return frame; mlt_filter text_filter = (mlt_filter) mlt_properties_get_data(properties, "_text_filter", NULL); mlt_properties text_filter_properties = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(text_filter)); //read and process file if needed process_file(filter, frame); process_filter_properties(filter, frame); // Apply keyword substitution before passing the text to the filter. char *result = (char *) calloc(1, MAX_TEXT_LEN); substitute_keywords(filter, result, dynamic_text, frame); mlt_properties_set_string(text_filter_properties, "argument", result); free(result); mlt_properties_pass_list(text_filter_properties, properties, "geometry family size weight style fgcolour bgcolour olcolour pad " "halign valign outline underline strikethrough opacity"); mlt_filter_set_in_and_out(text_filter, mlt_filter_get_in(filter), mlt_filter_get_out(filter)); return mlt_filter_process(text_filter, frame); } /** Destructor for the filter. */ static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; default_priv_data(pdata); free(pdata); filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_gpstext_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { (void) type; // unused (void) id; // unused mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); default_priv_data(pdata); mlt_filter text_filter = mlt_factory_filter(profile, "qtext", NULL); if (!text_filter) text_filter = mlt_factory_filter(profile, "text", NULL); if (!text_filter) mlt_log_warning(MLT_FILTER_SERVICE(filter), "Unable to create text filter.\n"); if (filter && text_filter && pdata) { mlt_properties my_properties = MLT_FILTER_PROPERTIES(filter); // Register the text filter for reuse/destruction mlt_properties_set_data(my_properties, "_text_filter", text_filter, 0, (mlt_destructor) mlt_filter_close, NULL); // Assign default values mlt_properties_set_string(my_properties, "argument", arg ? arg : "Speed: #gps_speed#km/h\n" "Distance: #gps_dist#m\n" "Altitude: #gps_elev#m\n\n" "GPS time: #gps_datetime_now# UTC\n" "GPS location: #gps_lat#, #gps_lon#"); mlt_properties_set_string(my_properties, "geometry", "10%/10%:80%x80%:100%"); mlt_properties_set_string(my_properties, "family", "Sans"); mlt_properties_set_string(my_properties, "size", "26"); mlt_properties_set_string(my_properties, "weight", "400"); mlt_properties_set_string(my_properties, "style", "normal"); mlt_properties_set_string(my_properties, "fgcolour", "0xffffffff"); mlt_properties_set_string(my_properties, "bgcolour", "0x00000000"); mlt_properties_set_string(my_properties, "olcolour", "0x000000ff"); mlt_properties_set_string(my_properties, "pad", "5"); mlt_properties_set_string(my_properties, "halign", "left"); mlt_properties_set_string(my_properties, "valign", "bottom"); mlt_properties_set_string(my_properties, "outline", "0"); mlt_properties_set_string(my_properties, "underline", "0"); mlt_properties_set_string(my_properties, "strikethrough", "0"); mlt_properties_set_string(my_properties, "opacity", "1.0"); mlt_properties_set_int(my_properties, "_filter_private", 1); mlt_properties_set_int(my_properties, "time_offset", 0); mlt_properties_set_int(my_properties, "smoothing_value", 5); mlt_properties_set_int(my_properties, "speed_multiplier", 1); mlt_properties_set_int(my_properties, "updates_per_second", 1); filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { if (filter) { mlt_filter_close(filter); } if (text_filter) { mlt_filter_close(text_filter); } free(pdata); filter = NULL; } return filter; } } mlt-7.38.0/src/modules/qt/filter_gpstext.yml000664 000000 000000 00000026074 15172202314 021044 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: gpstext title: GPS Text version: 7 copyright: Meltytech, LLC creator: Daniel F license: LGPLv2.1 language: en tags: - Video description: Overlay GPS-related text onto the video notes: > The GPS text filter will search for keywords in the text to be overlaid and will replace those keywords on a frame-by-frame basis. It is based on dynamictext filter. parameters: - identifier: argument argument: yes title: GPS text type: string description: | The text to overlay. May include keywords enclosed in "#". Keywords include: * #gps_lat# - the GPS latitude value * #gps_lon# - the GPS longitude value * #gps_elev# - the GPS altitude value * #gps_speed# - GPS speed * #gps_dist# - total distance so far * #gps_datetime_now# - date and time of current gps point shown * #file_datetime_now# - date and time of current frame in video file * #hr# - heart rate (if present in gps file) * #gps_bearing# - current GPS bearing (degrees 0-360) * #gps_compass# - current GPS direction (8 divisions: N, NE, E, etc) * #gps_vdist_up# - total GPS altitude gain so far * #gps_vdist_down# - total GPS altitude loss so far * #gps_dist_uphill# - total distance travelled uphill so far * #gps_dist_downhill# - total distance travelled downhill so far * #gps_dist_flat# - total distance travelled on flat area so far * #gps_cadence# - (bike) cadence, in revolutions per minute * #gps_temperature# - ambient temperature ( in .gpx) * #gps_power# - power in watts * #gps_grade_percentage# - gradient of GPS track (computed from GPS and elev) * #gps_grade_degrees# - same as above but converted to degrees An extra word "RAW" (uppercase!) can be added to the keyword to display the unchanged value from the file. If a keyword can't produce valid data it will print "--". Keywords returning numeric values can use the extra word "decimals" to forcefully set a specific number of decimals (0-9). Example: #gps_speed decimals 3#. Time-based keywords can include a strftime format string to customize the output and a number (representing seconds) preceded by '+' or '-' which will offset the actual time. For example, "#gps_datetime_now %I:%M:%S %p +3600#" shows only the time in 12-hour format, offset by 1 hour. The speed keyword can have an extra "vertical" or "3D" word as a format to show vertical speed, and 3D speed (takes into account altitude). Speed and distance keywords may include an extra format keyword to convert the value to metric/imperial units. Default is meters and km/h respectively. Supported formats, distance: m|meter, km|kilometer*, mi|mile*, ft|feet, nm|nautical*; speed: ms|m/s|meter, km|km/h|kilo, mi|mi/h|mile, ft|ft/s|feet, kn|nm/h|knots, mmin|m/min, ftmin|ft/min. Computed values are calculated since beginning of GPS file or since "gps_processing_start_time" property, if set. Temperature can include the extra uppercase letter "F" or "K" to convert degrees Celsius (default) to Fahrenheit or Kelvin respectively. The # may be escaped with "\". required: yes readonly: no default: > Speed: #gps_speed#km/h\n Distance: #gps_dist#m\n Altitude: #gps_elev#m\n\n GPS time: #gps_datetime_now#\n GPS location: #gps_lat#, #gps_lon# widget: text - identifier: resource title: GPS File/URL description: > "The fullpath of file containing location (GPS) data. Supported extensions: .gpx, .tcx." type: string required: yes readonly: no widget: fileopen - identifier: time_offset title: Time offset type: integer description: > An offset (in seconds) to be added to the video file to match it to the GPS track. Most of the time this will at least need to be set to the timezone difference between the 2 files plus some seconds if the video recording device isn't precisely set to correct time. GPS time is always exact and in UTC. Use positive values if GPS is ahead of video and negative otherwise. default: 0 readonly: no required: no mutable: yes widget: text - identifier: smoothing_value title: Smoothing level type: integer description: > How many GPS points to smooth in order to eliminate GPS errors. A value of 0 only exposes the raw values from file. A value of 1 does not smooth locations, it only calculates the extra fields (speed, distance, etc) it also interpolates missing values for heart rate and altitude. default: 5 minimum: 0 readonly: no required: no mutable: yes widget: text - identifier: gps_processing_start_time title: GPS processing start time type: string description: > A UTC date and time (formatted as "yyyy-MM-dd hh:mm:ss") from which to start processing the gps file. Use if the GPS file has extra points before the video and you want to ignore them (can be used to track the distance since beginning of a segment). If the string provided doesn't perfectly match the format, it will be ignored. default: "yyyy-MM-dd hh:mm:ss" readonly: no required: no mutable: yes widget: text - identifier: speed_multiplier title: Speed multiplier type: float description: > If the video file is a timelapse (or slow motion), use this value to set the speed up/slow down ratio. Sample values: 30 for 30x timelapse, 0.25 for 4x slow motion footage. default: 1 readonly: no required: no mutable: yes widget: text - identifier: updates_per_second title: Updates per second type: float description: > Controls how many times per second is the GPS text updated on video (interpolated). A value of 0 will only update text when a real gps point has been recorded. Can be used in combination with speed_multiplier. default: 1 readonly: no required: no mutable: yes widget: text # READ ONLY: - identifier: gps_start_text title: GPS start time description: Date and time of the first valid GPS point. readonly: yes - identifier: video_start_text title: Video start time description: Date and time of the video file. readonly: yes - identifier: auto_gps_offset_start title: Auto offset start description: > Provides a helper offset to guarantee start of video file syncs with the start of gps file. Correctly sets the offset if video file and gps recording was started at the same time. readonly: yes - identifier: auto_gps_offset_now title: Auto offset now description: > Provides a helper offset to sync the first gps point to current video time (it is updated every second). Correctly sets the offset if you video record the moment gps starts. readonly: yes - identifier: auto_gps_processing_start_now title: Auto gps processing start now description: > Provides a helper offset to sync the gps_processing_start_time property to current video time (it is updated every second). DateTime string. readonly: yes - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 10%/10%:80%x80%:100% - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 26 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0xffffffff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000000 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: bottom values: - top - middle - bottom mutable: yes widget: combo - identifier: opacity title: Opacity type: float description: Opacity of all elements - text, outline, and background readonly: no default: 1.0 minimum: 0 maximum: 1.0 mutable: yes widget: slider mlt-7.38.0/src/modules/qt/filter_lightshow.cpp000664 000000 000000 00000031015 15172202314 021326 0ustar00rootroot000000 000000 /* * filter_lightshow.cpp -- animate color to the audio * Copyright (C) 2015-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include // sin() #include // calloc(), free() #include #include // Private Constants static const double PI = 3.14159265358979323846; namespace { // Private Types typedef struct { mlt_filter fft; char *mag_prop_name; int rel_pos; int preprocess_warned; } private_data; } // namespace static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; // Create the FFT filter the first time. if (!pdata->fft) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); pdata->fft = mlt_factory_filter(profile, "fft", NULL); mlt_properties_set_int(MLT_FILTER_PROPERTIES(pdata->fft), "window_size", mlt_properties_get_int(filter_properties, "window_size")); if (!pdata->fft) { mlt_log_warning(MLT_FILTER_SERVICE(filter), "Unable to create FFT.\n"); return 1; } } mlt_properties fft_properties = MLT_FILTER_PROPERTIES(pdata->fft); double low_freq = mlt_properties_get_int(filter_properties, "frequency_low"); double hi_freq = mlt_properties_get_int(filter_properties, "frequency_high"); double threshold = mlt_properties_get_int(filter_properties, "threshold"); double osc = mlt_properties_get_int(filter_properties, "osc"); float peak = 0; // The service must stay locked while using the private data mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Perform FFT processing on the frame mlt_filter_process(pdata->fft, frame); mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); float *bins = (float *) mlt_properties_get_data(fft_properties, "bins", NULL); double window_level = mlt_properties_get_double(fft_properties, "window_level"); if (bins && window_level == 1.0) { // Find the peak FFT magnitude in the configured range of frequencies int bin_count = mlt_properties_get_int(fft_properties, "bin_count"); double bin_width = mlt_properties_get_double(fft_properties, "bin_width"); int bin = 0; for (bin = 0; bin < bin_count; bin++) { double F = bin_width * (double) bin; if (F >= low_freq && F <= hi_freq) { if (bins[bin] > peak) { peak = bins[bin]; } } } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Scale the magnitude to dB and apply oscillation double dB = peak > 0.0 ? 20 * log10(peak) : -1000.0; double mag = 0.0; if (dB >= threshold) { // Scale to range 0.0-1.0 mag = 1 - (dB / threshold); if (osc != 0) { // Apply the oscillation double fps = mlt_profile_fps(mlt_service_profile(MLT_FILTER_SERVICE(filter))); double t = pdata->rel_pos / fps; mag = mag * sin(2 * PI * osc * t); } pdata->rel_pos++; } else { pdata->rel_pos = 1; mag = 0; } // Save the magnitude as a property on the frame to be used in get_image() mlt_properties_set_double(MLT_FRAME_PROPERTIES(frame), pdata->mag_prop_name, mag); return 0; } static void setup_pen(QPainter &p, QRect &rect, mlt_properties filter_properties, mlt_properties frame_properties) { QVector colors; bool color_found = true; // Find user specified colors for the gradient while (color_found) { QString prop_name = QStringLiteral("color.") + QString::number(colors.size() + 1); if (mlt_properties_exists(filter_properties, prop_name.toUtf8().constData())) { mlt_color mcolor = mlt_properties_get_color(filter_properties, prop_name.toUtf8().constData()); mcolor = mlt_color_convert_trc(mcolor, mlt_properties_get(frame_properties, "color_trc")); colors.append(QColor(mcolor.r, mcolor.g, mcolor.b, mcolor.a)); } else { color_found = false; } } if (!colors.size()) { // No color specified. Just use white. p.setBrush(Qt::white); } else if (colors.size() == 1) { // Only use one color p.setBrush(colors[0]); } else { // Use Gradient qreal sx = 1.0; qreal sy = 1.0; qreal dx = rect.x(); qreal dy = rect.y(); qreal radius = rect.width() / 2; if (rect.width() > rect.height()) { radius = rect.height() / 2; sx = (qreal) rect.width() / (qreal) rect.height(); } else if (rect.height() > rect.width()) { radius = rect.width() / 2; sy = (qreal) rect.height() / (qreal) rect.width(); } QPointF center(radius, radius); QRadialGradient gradient(center, radius); qreal step = 1.0 / (colors.size() - 1); for (int i = 0; i < colors.size(); i++) { gradient.setColorAt((qreal) i * step, colors[i]); } QBrush brush(gradient); QTransform transform(sx, 0.0, 0.0, 0.0, sy, 0.0, dx, dy, 1.0); brush.setTransform(transform); p.setBrush(brush); } p.setPen(QColor(0, 0, 0, 0)); // Clear pen } static void draw_light(mlt_properties filter_properties, mlt_properties frame_properties, QImage *qimg, mlt_rect *rect, double mag) { QPainter p(qimg); QRect r(rect->x, rect->y, rect->w, rect->h); p.setRenderHint(QPainter::Antialiasing); // Output transparency = input transparency p.setCompositionMode(QPainter::CompositionMode_SourceAtop); setup_pen(p, r, filter_properties, frame_properties); p.setOpacity(mag); p.drawRect(r); p.end(); } /** Get the image. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); private_data *pdata = (private_data *) filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); if (mlt_properties_exists(frame_properties, pdata->mag_prop_name)) { double mag = mlt_properties_get_double(frame_properties, pdata->mag_prop_name); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_rect rect = mlt_properties_anim_get_rect(filter_properties, "rect", position, length); // Get the current image *format = choose_image_format(*format); error = mlt_frame_get_image(frame, image, format, width, height, 1); if (strchr(mlt_properties_get(filter_properties, "rect"), '%')) { rect.x *= *width; rect.w *= *width; rect.y *= *height; rect.h *= *height; } else { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale = mlt_profile_scale_width(profile, *width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); rect.y *= scale; rect.h *= scale; } // Draw the light if (!error) { QImage qimg; convert_mlt_to_qimage(*image, &qimg, *width, *height, *format); draw_light(filter_properties, frame_properties, &qimg, &rect, mag); convert_qimage_to_mlt(&qimg, *image, *width, *height); } } else { if (pdata->preprocess_warned++ == 2) { // This filter depends on the consumer processing the audio before // the video. mlt_log_warning(MLT_FILTER_SERVICE(filter), "Audio not preprocessed.\n"); } mlt_frame_get_image(frame, image, format, width, height, writable); } return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { if (mlt_frame_is_test_card(frame)) { // The producer does not generate video. This filter will create an // image on the producer's behalf. mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_properties_set_int(frame_properties, "progressive", 1); mlt_properties_set_double(frame_properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(frame_properties, "meta.media.width", profile->width); mlt_properties_set_int(frame_properties, "meta.media.height", profile->height); // Tell the framework that there really is an image. mlt_properties_set_int(frame_properties, "test_image", 0); // Push a callback to create the image. mlt_frame_push_get_image(frame, create_image); } mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, (void *) filter_get_audio); mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { mlt_filter_close(pdata->fft); free(pdata->mag_prop_name); free(pdata); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_lightshow_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata && createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "_filter_private", 1); mlt_properties_set_int(properties, "frequency_low", 20); mlt_properties_set_int(properties, "frequency_high", 20000); mlt_properties_set_double(properties, "threshold", -30.0); mlt_properties_set_double(properties, "osc", 5.0); mlt_properties_set(properties, "color.1", "0xffffffff"); mlt_properties_set(properties, "rect", "0% 0% 100% 100%"); mlt_properties_set_int(properties, "window_size", 2048); // Create a unique ID for storing data on the frame pdata->mag_prop_name = (char *) calloc(1, 20); snprintf(pdata->mag_prop_name, 20, "fft_mag.%p", filter); pdata->mag_prop_name[20 - 1] = '\0'; pdata->fft = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter lightshow failed\n"); if (filter) { mlt_filter_close(filter); } if (pdata) { free(pdata); } filter = NULL; } return filter; } } mlt-7.38.0/src/modules/qt/filter_lightshow.yml000664 000000 000000 00000006307 15172202314 021353 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: lightshow title: Light Show version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that colors the image proportional to the magnitude of the audio spectrum. parameters: - identifier: frequency_low title: Low Frequency type: integer description: > The low end of the frequency range to be used to influence the image motion. mutable: yes readonly: no default: 20 unit: Hz - identifier: frequency_high title: High Frequency type: integer description: > The high end of the frequency range to be used to influence the image motion. mutable: yes readonly: no default: 20000 unit: Hz - identifier: threshold title: Level Threshold type: float description: > The minimum amplitude of sound that must occur within the frequency range to cause the image to move. motion. mutable: yes readonly: no default: -30 minimum: -100 maximum: 0 unit: dB - identifier: osc title: Oscillation type: float description: > Oscillation can be useful to make the image move back and forth during long periods of sound. A value of 0 specifies no oscillation. mutable: yes readonly: no default: 5 minimum: 0 unit: Hz - identifier: color.* title: Light Color type: color description: | The color of the light. Multiple colors can be specified with incrementing suffixes to cause the waveform to be drawn in a circular gradient. color.1 is the inside of the circle. Subsequent colors will produce a gradient toward the outside. By default, the filter has one color defined: color.1=0xffffffff" This results in the image being lightened. To create a gradient, define more colors: color.2=green color.3=0x77777777 color.4=0x00000000 A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: rect title: Rectangle description: > Defines the rectangle that the color should be drawn in. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 100% 100%" readonly: no mutable: yes animation: yes - identifier: window_size title: Window Size type: integer description: > The number of samples that the FFT will be performed on. If window_size is less than the number of samples in a frame, extra samples will be ignored. If window_size is more than the number of samples in a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 2048 mlt-7.38.0/src/modules/qt/filter_qtblend.cpp000664 000000 000000 00000032122 15172202314 020747 0ustar00rootroot000000 000000 /* * filter_qtblend.cpp -- Qt composite filter * Copyright (C) 2015-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include // sin() #include // calloc(), free() #include // strchr() #include #include #include #define MLT_QTBLEND_MAX_DIMENSION (16000) /** Get the image. */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; // Get the filter mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); bool hasAlpha = false; // Only process if we have no error and a valid colour space mlt_service_lock(MLT_FILTER_SERVICE(filter)); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Check transform QTransform transform; int normalized_width = profile->width; int normalized_height = profile->height; double consumer_ar = mlt_profile_sar(profile); // Destination rect mlt_rect rect = {0, 0, (double) normalized_width, (double) normalized_height, 1.0}; int b_width = mlt_properties_get_int(frame_properties, "meta.media.width"); int b_height = mlt_properties_get_int(frame_properties, "meta.media.height"); bool distort = mlt_properties_get_int(properties, "distort"); if (b_height == 0) { b_width = normalized_width; b_height = normalized_height; } // Special case - aspect_ratio = 0 if (mlt_frame_get_aspect_ratio(frame) == 0) { mlt_frame_set_aspect_ratio(frame, consumer_ar); } double b_ar = mlt_frame_get_aspect_ratio(frame); double b_dar = b_ar * b_width / b_height; double opacity = 1.0; // If the _qtblend_scaled property is defined, a qtblend filter was already applied double qtblendScaleX = qMin(1., mlt_properties_get_double(frame_properties, "qtblend_scalingx")); if (mlt_properties_get(properties, "rect")) { rect = mlt_properties_anim_get_rect(properties, "rect", position, length); if (::strchr(mlt_properties_get(properties, "rect"), '%')) { rect.x *= normalized_width; rect.y *= normalized_height; rect.w *= normalized_width; rect.h *= normalized_height; } } if (qtblendScaleX > 0.) { // Another qtblend filter was already applied // In this case, the *width and *height are set to the source resolution to ensure we don't lose too much details on multiple scaling operations // We requested a image with full media resolution, adjust rect to profile // Check if we have consumer scaling enabled since we cannot use *width and *height double qtblendScaleY = qMin(1., mlt_properties_get_double(frame_properties, "qtblend_scalingy")); // Consumer scaling was already applied to b_width/b_height // Always request an image that follows the consumer aspect ratio int tmpWidth = b_width; int tmpHeight = b_height; double scaleFactor = qMax(*width / rect.w, *height / rect.h); if (scaleFactor > 1.) { // Use the highest necessary resolution image tmpWidth *= scaleFactor; tmpHeight *= scaleFactor; } if (consumer_ar * normalized_height / normalized_width < 1.) { *width = qBound(qRound(normalized_width * qtblendScaleX), tmpWidth, MLT_QTBLEND_MAX_DIMENSION); *height = qRound(*width * consumer_ar * normalized_height / normalized_width); } else { *height = qBound(qRound(normalized_height * qtblendScaleY), tmpHeight, MLT_QTBLEND_MAX_DIMENSION); *width = qRound(*height * normalized_width / normalized_height / consumer_ar); } // Adjust rect to new scaling double scale = (double) *width / normalized_width; if (scale != 1.0) { rect.x *= scale; rect.w *= scale; } scale = (double) *height / normalized_height; if (scale != 1.0) { rect.y *= scale; rect.h *= scale; } } else { // First instance of a qtblend filter // Check if requested frame size is scaled double scalex = mlt_profile_scale_width(profile, *width); double scaley = mlt_profile_scale_height(profile, *height); // Store consumer scaling for further uses mlt_properties_set_double(frame_properties, "qtblend_scalingx", scalex); mlt_properties_set_double(frame_properties, "qtblend_scalingy", scaley); // Apply scaling if (scalex != 1.0) { rect.x *= scalex; rect.w *= scalex; if (b_width < normalized_width) { // Adjust scale so that we don't request too small images scalex = qBound(scalex, normalized_width * scalex / b_width, 1.); } // Apply consumer scaling to the source image if (scalex < 1.) { b_width *= scalex; b_height *= scalex; } } if (scaley != 1.0) { rect.y *= scaley; rect.h *= scaley; } } transform.translate(rect.x, rect.y); opacity = rect.o; hasAlpha = rect.o < 1 || rect.x != 0 || rect.y != 0 || rect.w != *width || rect.h != *height || rect.w / b_dar < *height || rect.h * b_dar < *width || b_width != *width || b_height != *height; if (mlt_properties_get(properties, "rotation")) { double angle = mlt_properties_anim_get_double(properties, "rotation", position, length); if (angle != 0.0) { if (mlt_properties_get(properties, "rotate_anchor")) { mlt_rect anchor = mlt_properties_anim_get_rect(properties, "rotate_anchor", position, length); // Use custom anchor point (x,y are normalized 0-1 coordinates) where 0, 0 is top left and 1, 1 is bottom right // negative values are allowed so its possible to rotate around a point outside the rectangle double anchor_x = anchor.x * rect.w; double anchor_y = anchor.y * rect.h; transform.translate(anchor_x, anchor_y); transform.rotate(angle); transform.translate(-anchor_x, -anchor_y); } else if (mlt_properties_get_int(properties, "rotate_center")) { // old style rotation (from center) to keep compatibility, equivalent to rotate_anchor = 0.5, 0.5 transform.translate(rect.w / 2.0, rect.h / 2.0); transform.rotate(angle); transform.translate(-rect.w / 2.0, -rect.h / 2.0); } else { // old style rotation (from top left corner) to keep compatibility, equivalent to rotate_anchor = 0, 0 transform.rotate(angle); } hasAlpha = true; } } if (!hasAlpha && mlt_properties_get_int(properties, "compositing") != 0) { hasAlpha = true; } if (!hasAlpha) { uint8_t *src_image = NULL; error = mlt_frame_get_image(frame, &src_image, format, &b_width, &b_height, 0); if (*format == mlt_image_rgba || *format == mlt_image_rgba64 || mlt_frame_get_alpha(frame)) { hasAlpha = true; } else { // Prepare output image *image = src_image; *width = b_width; *height = b_height; return 0; } } // fetch image *format = choose_image_format(*format); uint8_t *src_image = NULL; error = mlt_frame_get_image(frame, &src_image, format, &b_width, &b_height, 0); // Put source buffer into QImage QImage sourceImage; convert_mlt_to_qimage(src_image, &sourceImage, b_width, b_height, *format); int image_size = mlt_image_format_size(*format, *width, *height, NULL); char *interps = mlt_properties_get(frame_properties, "consumer.rescale"); bool hqPainting = interps && strcmp(interps, "nearest") && strcmp(interps, "neighbor"); // resize to rect bool scaled = false; QImage scaledSource; if (distort) { if (rect.w != b_width || rect.h != b_height) { if (rect.w < b_width || rect.h < b_height) { scaled = true; scaledSource = sourceImage.scaled(qRound(rect.w), qRound(rect.h), Qt::IgnoreAspectRatio, hqPainting ? Qt::SmoothTransformation : Qt::FastTransformation); } else { transform.scale(rect.w / b_width, rect.h / b_height); } } } else { double scale; double resize_dar = rect.w * consumer_ar / rect.h; if (b_dar >= resize_dar) { scale = rect.w / b_width; } else { scale = rect.h / b_height * b_ar; } if (scale < 1.) { // Use higher quality QImage scaling scaled = true; const QSize finalSize(qRound(sourceImage.width() * scale), qRound(sourceImage.height() * scale)); // Center image in rect transform.translate((rect.w - finalSize.width()) / 2.0, (rect.h - finalSize.height()) / 2.0); // Scale scaledSource = sourceImage.scaled(finalSize, Qt::IgnoreAspectRatio, hqPainting ? Qt::SmoothTransformation : Qt::FastTransformation); } else { // Center image in rect transform.translate((rect.w - (b_width * scale)) / 2.0, (rect.h - (b_height * scale)) / 2.0); if (scale > 1.) { // Use QPainter scaling transform.scale(scale, scale); } } } uint8_t *dest_image = NULL; dest_image = (uint8_t *) mlt_pool_alloc(image_size); QImage destImage; convert_mlt_to_qimage(dest_image, &destImage, *width, *height, *format); destImage.fill(mlt_properties_get_int(properties, "background_color")); QPainter painter(&destImage); painter.setCompositionMode( (QPainter::CompositionMode) mlt_properties_get_int(properties, "compositing")); painter.setTransform(transform); painter.setOpacity(opacity); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, hqPainting); // Composite top frame if (scaled) { painter.drawImage(0, 0, scaledSource); } else { painter.drawImage(0, 0, sourceImage); } // finish Qt drawing painter.end(); convert_qimage_to_mlt(&destImage, dest_image, *width, *height); *image = dest_image; mlt_frame_set_image(frame, *image, *width * *height * 4, mlt_pool_release); return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ extern "C" { mlt_filter filter_qtblend_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter && createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { filter->process = filter_process; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "rotate_center", 0); } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Filter qtblend failed\n"); if (filter) { mlt_filter_close(filter); } filter = NULL; } return filter; } } mlt-7.38.0/src/modules/qt/filter_qtblend.yml000664 000000 000000 00000003540 15172202314 020770 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: qtblend title: Composite and transform version: 3 copyright: Meltytech, LLC creator: Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video description: > A filter allowing compositing and transform. parameters: - identifier: rect title: Rectangle type: rect description: > Keyframable rectangle specification. mutable: yes animation: yes - identifier: compositing title: Composition mode description: > Defines which composition operation will be performed (see QPainter CompositionMode for doc). type: integer default: 0 minimum: 0 maximum: 40 mutable: yes widget: spinner - identifier: rotation title: Rotation angle description: > Angle for rotation. type: float default: 1 minimum: 0 maximum: 360 mutable: yes animation: yes widget: spinner unit: degrees - identifier: rotate_center title: Rotate from center type: integer description: > Process the rotation from center if set, otherwise from top left corner minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: rotate_anchor title: Rotation anchor point type: rect description: > Keyframable anchor point for rotation. X and Y coordinates define the rotation center. Values are normalized (0-1 for inside rectangle, negative or >1 for outside). Default is center (0.5, 0.5). Takes precedence over rotate_center when set. mutable: yes animation: yes - identifier: background_color title: Background color type: integer description: > An integer formed like 0xaabbggrr that will be used as background color for the compositing operation. Defaults to 0 (fully transparent) default: 0 mutable: yes widget: color mlt-7.38.0/src/modules/qt/filter_qtblend_mode.c000664 000000 000000 00000003121 15172202314 021410 0ustar00rootroot000000 000000 /* * filter_atblend_mode.c * Copyright (C) 2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties_set(MLT_FRAME_PROPERTIES(frame), "qtblend.mode", mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "mode")); return frame; } mlt_filter filter_qtblend_mode_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "mode", arg ? arg : "0"); } return filter; } mlt-7.38.0/src/modules/qt/filter_qtblend_mode.yml000664 000000 000000 00000001006 15172202314 021767 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: qtblend_mode title: Set Qt Blend Mode version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: Change the blend mode used by the qtblend transition. parameters: - identifier: mode argument: yes title: Blend mode type: integer description: > This filter can be used to change the blend mode for a single clip when using the qtblend transition to composite tracks. default: 0 mlt-7.38.0/src/modules/qt/filter_qtcrop.cpp000664 000000 000000 00000011522 15172202314 020627 0ustar00rootroot000000 000000 /* * filter_qtcrop.cpp -- cropping filter * Copyright (c) 2020-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "math.h" #include #include #include #include #include #include static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_rect rect = mlt_properties_anim_get_rect(properties, "rect", position, length); // Get the current image *format = choose_image_format(*format); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "resize_alpha", 255); error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error && (*format == mlt_image_rgba || *format == mlt_image_rgba64)) { QImage bgImage; convert_mlt_to_qimage(*image, &bgImage, *width, *height, *format); QImage fgImage = bgImage.copy(); QPainter painter(&bgImage); QPainterPath path; mlt_color color = mlt_properties_anim_get_color(properties, "color", position, length); color = mlt_color_convert_trc(color, mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "color_trc")); double radius = mlt_properties_anim_get_double(properties, "radius", position, length); painter.setRenderHints(QPainter::Antialiasing #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | QPainter::HighQualityAntialiasing #endif ); bgImage.fill(QColor(color.r, color.g, color.b, color.a)); if (mlt_properties_get_int(properties, "circle")) { QPointF center(double(*width) / 2.0, double(*height) / 2.0); double hypotenuse = sqrt(pow(*width, 2) + pow(*height, 2)); radius *= 0.5 * hypotenuse; path.addEllipse(center, radius, radius); } else { const char *s = mlt_properties_get(properties, "rect"); if (qstrlen(s) > 0 && strchr(s, '%')) { rect.x *= *width; rect.w *= *width; rect.y *= *height; rect.h *= *height; } else { double scale = mlt_profile_scale_width(profile, *width); double scale_height = mlt_profile_scale_height(profile, *height); rect.x *= scale; rect.y *= scale_height; rect.w *= scale; rect.h *= scale_height; } radius *= 0.5 * MIN(rect.w, rect.h); path.addRoundedRect(rect.x, rect.y, rect.w, rect.h, radius, radius); } painter.setClipPath(path); painter.drawImage(QPointF(0, 0), fgImage); painter.end(); convert_qimage_to_mlt(&bgImage, *image, *width, *height); } return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } extern "C" { mlt_filter filter_qtcrop_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { Q_UNUSED(profile) Q_UNUSED(type) Q_UNUSED(id) mlt_filter filter = mlt_filter_new(); if (!filter || !createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_filter_close(filter); return NULL; } filter->process = process; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_string(properties, "rect", arg ? arg : "0%/0%:100%x100%"); mlt_properties_set_int(properties, "circle", 0); mlt_properties_set_string(properties, "color", "#00000000"); mlt_properties_set_double(properties, "radius", 0.0); return filter; } } mlt-7.38.0/src/modules/qt/filter_qtcrop.yml000664 000000 000000 00000002040 15172202314 020641 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: qtcrop title: Crop by padding version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > This filter crops the image to a rounded rectangle or circle by padding it with a color. parameters: - identifier: rect title: Rectangle type: rect description: Keyframable rectangle specification argument: yes default: "0%/0%:100%x100%" mutable: yes animation: yes - identifier: circle title: Use Circle description: Whether to use a circle instead of a rectangle type: boolean default: 0 mutable: yes - identifier: radius title: Radius description: Keyframable amount of circle or rectangle rounding type: float default: 0.0 minimum: 0.0 maximum: 1.0 mutable: yes animation: yes - identifier: color title: Padding color type: color description: The color to use for padding including alpha default: "#00000000" mutable: yes animation: yes mlt-7.38.0/src/modules/qt/filter_qtext.cpp000664 000000 000000 00000061276 15172202314 020477 0ustar00rootroot000000 000000 /* * filter_qtext.cpp -- text overlay filter * Copyright (c) 2018-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) #include #else #include #endif #include "typewriter.h" #include #include static QMutex g_mutex; struct TypewriterFilterData { TypeWriter typewriter; std::string original_text; int macro_type; mlt_position start_position; TypewriterFilterData() : macro_type(0) , start_position(0) {} }; static void close_typewriter_filter_data(void *data) { delete static_cast(data); } static std::string get_typewriter_text_for_filter(mlt_properties filter_properties, mlt_properties state_props, const char *original_text, mlt_position position) { if (!mlt_properties_get_int(filter_properties, "typewriter") || !original_text) { return original_text ? std::string(original_text) : std::string(""); } auto *tw_data = static_cast( mlt_properties_get_data(state_props, "_typewriter_filter_data", NULL)); if (!tw_data) { tw_data = new TypewriterFilterData(); mlt_properties_set_data(state_props, "_typewriter_filter_data", static_cast(tw_data), 0, close_typewriter_filter_data, NULL); } auto text_str = std::string(original_text); unsigned int step_length = mlt_properties_get_int(filter_properties, "typewriter.step_length"); unsigned int step_sigma = mlt_properties_get_int(filter_properties, "typewriter.step_sigma"); unsigned int random_seed = mlt_properties_get_int(filter_properties, "typewriter.random_seed"); int macro_type = mlt_properties_get_int(filter_properties, "typewriter.macro_type"); // Check if text or typewriter settings changed if (tw_data->original_text != text_str || tw_data->typewriter.getFrameStep() != step_length || tw_data->typewriter.getStepSigma() != step_sigma || tw_data->typewriter.getStepSeed() != random_seed || tw_data->macro_type != macro_type) { tw_data->original_text = text_str; tw_data->start_position = position; tw_data->typewriter.setPattern(text_str); if (step_length == 0) step_length = 25; tw_data->typewriter.setFrameStep(step_length); tw_data->typewriter.setStepSigma(step_sigma); tw_data->typewriter.setStepSeed(random_seed); tw_data->macro_type = macro_type; // Apply macro type if specified if (macro_type > 0) { char *buff = new char[text_str.length() + 10]; char c = 0; switch (macro_type) { case 1: c = 'c'; break; // character case 2: c = 'w'; break; // word case 3: c = 'l'; break; // line default: break; } if (c != 0) { sprintf(buff, ":%c{%s}", c, text_str.c_str()); tw_data->typewriter.setPattern(std::string(buff)); } delete[] buff; } tw_data->typewriter.parse(); } mlt_position relative_position = position - tw_data->start_position; std::string rendered_text = tw_data->typewriter.render(relative_position); // Add blinking cursor if enabled auto cursor_enabled = mlt_properties_get_int(filter_properties, "typewriter.cursor"); if (cursor_enabled) { int cursor_blink_rate = mlt_properties_get_int(filter_properties, "typewriter.cursor_blink_rate"); if (cursor_blink_rate < 0) cursor_blink_rate = 25; // Default to 25 frames (1 second at 25fps) // Check if we should show cursor (blink on/off) bool show_cursor = (cursor_blink_rate == 0) || (relative_position / cursor_blink_rate) % 2 == 0; // Only show cursor if text is still being typed or if we're at the end bool still_typing = !tw_data->typewriter.isEnd(); if (still_typing || cursor_enabled == 2) { // 2 = always show cursor char *cursor_char = mlt_properties_get(filter_properties, "typewriter.cursor_char"); if (!show_cursor) { cursor_char = (char *) " "; } else if (!cursor_char || strlen(cursor_char) == 0) { cursor_char = (char *) "|"; // Default cursor character } rendered_text += cursor_char; } } return rendered_text; } static QRectF get_text_path(QPainterPath *qpath, mlt_properties filter_properties, mlt_properties state_props, const char *text, double scale, mlt_position position = 0) { int outline = mlt_properties_get_int(filter_properties, "outline") * scale; char halign = mlt_properties_get(filter_properties, "halign")[0]; const char *style = mlt_properties_get(filter_properties, "style"); int pad = mlt_properties_get_int(filter_properties, "pad") * scale; int offset = pad + (outline / 2); int width = 0; int height = 0; qpath->setFillRule(Qt::WindingFill); // Get the strings to display (with typewriter effect if enabled) std::string processed_text = get_typewriter_text_for_filter(filter_properties, state_props, text, position); QString s = QString::fromUtf8(processed_text.c_str()); QStringList lines = s.split("\n"); // Configure the font QFont font; font.setPixelSize(mlt_properties_get_int(filter_properties, "size") * scale); font.setFamily(mlt_properties_get(filter_properties, "family")); #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) font.setWeight(QFont::Weight((mlt_properties_get_int(filter_properties, "weight") / 10) - 1)); #else font.setWeight(QFont::Weight(mlt_properties_get_int(filter_properties, "weight"))); #endif switch (style[0]) { case 'i': case 'I': font.setStyle(QFont::StyleItalic); break; default: font.setStyleName(style); } mlt_log_debug(NULL, "[filter_qtext] family %s style %s\n", font.family().toUtf8().constData(), style); // Apply text decoration properties font.setUnderline(mlt_properties_get_int(filter_properties, "underline")); font.setStrikeOut(mlt_properties_get_int(filter_properties, "strikethrough")); QFontMetrics fm(font); // Determine the text rectangle size height = fm.lineSpacing() * lines.size(); for (int i = 0; i < lines.size(); ++i) { const QString line = lines[i]; int line_width = fm.horizontalAdvance(line); int bearing = (line.size() > 0) ? fm.leftBearing(line.at(0)) : 0; if (bearing < 0) line_width -= bearing; bearing = (line.size() > 0) ? fm.rightBearing(line.at(line.size() - 1)) : 0; if (bearing < 0) line_width -= bearing; if (line_width > width) width = line_width; } // Lay out the text in the path int x = 0; int y = fm.ascent() + offset; for (int i = 0; i < lines.size(); ++i) { QString line = lines.at(i); x = offset; int line_width = fm.horizontalAdvance(line); int bearing = (line.size() > 0) ? fm.leftBearing(line.at(0)) : 0; if (bearing < 0) { line_width -= bearing; x -= bearing; } bearing = (line.size() > 0) ? fm.rightBearing(line.at(line.size() - 1)) : 0; if (bearing < 0) line_width -= bearing; switch (halign) { default: case 'l': case 'L': break; case 'c': case 'C': x += (width - line_width) / 2; break; case 'r': case 'R': x += width - line_width; break; } qpath->addText(x, y, font, line); y += fm.lineSpacing(); } // Account for outline and pad width += offset * 2; height += offset * 2; // Sanity check if (width == 0) width = 1; height += 2; // I found some fonts whose descenders get cut off. return QRectF(0, 0, width, height); } static QColor get_qcolor(mlt_properties filter_properties, const char *name, mlt_properties frame_properties, int position, int length) { mlt_color color = mlt_properties_anim_get_color(filter_properties, name, position, length); color = mlt_color_convert_trc(color, mlt_properties_get(frame_properties, "color_trc")); return QColor(color.r, color.g, color.b, color.a); } static void transform_painter(QPainter *painter, mlt_rect frame_rect, QRectF path_rect, mlt_properties filter_properties, mlt_profile profile) { qreal sx = 1.0; qreal sy = mlt_profile_sar(profile); qreal path_width = path_rect.width() * sx; if (path_width > frame_rect.w) { sx *= frame_rect.w / path_width; sy *= frame_rect.w / path_width; } qreal path_height = path_rect.height() * sy; if (path_height > frame_rect.h) { sx *= frame_rect.h / path_height; sy *= frame_rect.h / path_height; } qreal dx = frame_rect.x; qreal dy = frame_rect.y; char halign = mlt_properties_get(filter_properties, "halign")[0]; switch (halign) { default: case 'l': case 'L': break; case 'c': case 'C': dx += (frame_rect.w - (sx * path_rect.width())) / 2; break; case 'r': case 'R': dx += frame_rect.w - (sx * path_rect.width()); break; } char valign = mlt_properties_get(filter_properties, "valign")[0]; switch (valign) { default: case 't': case 'T': break; case 'm': case 'M': dy += (frame_rect.h - (sy * path_rect.height())) / 2; break; case 'b': case 'B': dy += frame_rect.h - (sy * path_rect.height()); break; } QTransform transform; transform.translate(dx, dy); transform.scale(sx, sy); painter->setTransform(transform); } static void paint_background(QPainter *painter, QRectF path_rect, mlt_properties filter_properties, mlt_properties frame_properties, int position, int length) { QColor bg_color = get_qcolor(filter_properties, "bgcolour", frame_properties, position, length); painter->fillRect(path_rect, bg_color); } static void paint_text(QPainter *painter, QPainterPath *qpath, mlt_properties filter_properties, mlt_properties frame_properties, int position, int length) { // Draw the outline first, and then draw the fill on top of it. // This avoids the outline encroaching on the text fill. // Draw the outline if requested int outline = mlt_properties_get_int(filter_properties, "outline"); if (outline) { QPen pen; pen.setWidth(outline); QColor color = get_qcolor(filter_properties, "olcolour", frame_properties, position, length); pen.setColor(color); painter->setPen(pen); painter->setBrush(Qt::NoBrush); // No brush needed for outline painter->drawPath(*qpath); } // Fill the text area QColor color = get_qcolor(filter_properties, "fgcolour", frame_properties, position, length); QBrush brush(color); painter->setBrush(brush); painter->setPen(Qt::NoPen); // No pen needed for fill painter->drawPath(*qpath); } static void close_qtextdoc(void *p) { delete static_cast(p); } static QTextDocument *get_rich_text(mlt_properties properties, double width, double height) { QTextDocument *doc = (QTextDocument *) mlt_properties_get_data(properties, "QTextDocument", NULL); auto html = QString::fromUtf8(mlt_properties_get(properties, "html")); auto prevHtml = QString::fromUtf8(mlt_properties_get(properties, "_html")); auto resource = QString::fromUtf8(mlt_properties_get(properties, "resource")); auto prevResource = QString::fromUtf8(mlt_properties_get(properties, "_resource")); auto prevWidth = mlt_properties_get_double(properties, "_width"); auto prevHeight = mlt_properties_get_double(properties, "_height"); bool changed = !doc || qAbs(width - prevWidth) > 1 || qAbs(height - prevHeight) > 1; if (!resource.isEmpty() && (changed || resource != prevResource)) { QFile file(resource); if (file.open(QFile::ReadOnly)) { QByteArray data = file.readAll(); doc = new QTextDocument; doc->setPageSize(QSizeF(width, height)); #if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) QTextCodec *codec = QTextCodec::codecForHtml(data); doc->setHtml(codec->toUnicode(data)); #else QStringDecoder decoder = QStringDecoder::decoderForHtml(data); doc->setHtml(decoder(data)); #endif mlt_properties_set_data(properties, "QTextDocument", doc, 0, (mlt_destructor) close_qtextdoc, NULL); mlt_properties_set(properties, "_resource", resource.toUtf8().constData()); mlt_properties_set_double(properties, "_width", width); mlt_properties_set_double(properties, "_height", height); } } else if (!html.isEmpty() && (changed || html != prevHtml)) { // fprintf(stderr, "%s\n", html.toUtf8().constData()); doc = new QTextDocument; doc->setPageSize(QSizeF(width, height)); doc->setHtml(html); mlt_properties_set_data(properties, "QTextDocument", doc, 0, (mlt_destructor) close_qtextdoc, NULL); mlt_properties_set(properties, "_html", html.toUtf8().constData()); mlt_properties_set_double(properties, "_width", width); mlt_properties_set_double(properties, "_height", height); } return doc; } static mlt_properties get_filter_properties(mlt_filter filter, mlt_frame frame) { mlt_properties properties = mlt_frame_get_unique_properties(frame, MLT_FILTER_SERVICE(filter)); if (!properties) properties = MLT_FILTER_PROPERTIES(filter); return properties; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); char *argument = (char *) mlt_frame_pop_service(frame); mlt_properties filter_properties = get_filter_properties(filter, frame); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); bool isRichText = qstrlen(mlt_properties_get(filter_properties, "html")) > 0 || qstrlen(mlt_properties_get(filter_properties, "resource")) > 0; double opacity = 1.0; if (mlt_properties_exists(filter_properties, "opacity")) { opacity = mlt_properties_anim_get_double(filter_properties, "opacity", position, length); } if (opacity == 0.0) { free(argument); return mlt_frame_get_image(frame, image, image_format, width, height, writable); } QString geom_str = QString::fromLatin1(mlt_properties_get(filter_properties, "geometry")); if (geom_str.isEmpty()) { free(argument); mlt_log_warning(MLT_FILTER_SERVICE(filter), "geometry property not set\n"); return mlt_frame_get_image(frame, image, image_format, width, height, writable); } mlt_rect rect = mlt_properties_anim_get_rect(filter_properties, "geometry", position, length); // Get the current image *image_format = choose_image_format(*image_format); mlt_properties_set_int(frame_properties, "resize_alpha", 255); mlt_service_lock(MLT_FILTER_SERVICE(filter)); error = mlt_frame_get_image(frame, image, image_format, width, height, writable); if (!error) { double scale = mlt_profile_scale_width(profile, *width); double scale_height = mlt_profile_scale_height(profile, *height); if (geom_str.contains('%')) { rect.x *= *width; rect.w *= *width; rect.y *= *height; rect.h *= *height; } else { rect.x *= scale; rect.y *= scale_height; rect.w *= scale; rect.h *= scale_height; } QImage qimg; convert_mlt_to_qimage(*image, &qimg, *width, *height, *image_format); QPainterPath text_path; #ifdef Q_OS_WIN auto pixel_ratio = mlt_properties_get_double(filter_properties, "pixel_ratio"); #else auto pixel_ratio = 1.0; #endif QRectF path_rect(0, 0, rect.w / scale * pixel_ratio, rect.h / scale_height * pixel_ratio); QPainter painter(&qimg); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | QPainter::HighQualityAntialiasing #endif ); painter.setOpacity(opacity); if (isRichText) { auto overflowY = mlt_properties_exists(filter_properties, "overflow-y") ? !!mlt_properties_get_int(filter_properties, "overflow-y") : (path_rect.height() >= profile->height * pixel_ratio); auto drawRect = overflowY ? QRectF() : path_rect; QMutexLocker mutexLock(&g_mutex); auto doc = get_rich_text(filter_properties, path_rect.width(), std::numeric_limits::max()); if (doc) { transform_painter(&painter, rect, path_rect, filter_properties, profile); if (overflowY) { path_rect.setHeight(qMax(path_rect.height(), doc->size().height())); } paint_background(&painter, path_rect, filter_properties, frame_properties, position, length); doc->drawContents(&painter, drawRect); } } else { mlt_properties state_props = MLT_FILTER_PROPERTIES(filter); path_rect = get_text_path(&text_path, filter_properties, state_props, argument, scale, position); transform_painter(&painter, rect, path_rect, filter_properties, profile); paint_background(&painter, path_rect, filter_properties, frame_properties, position, length); paint_text(&painter, &text_path, filter_properties, frame_properties, position, length); } painter.end(); convert_qimage_to_mlt(&qimg, *image, *width, *height); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); free(argument); return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = get_filter_properties(filter, frame); if (mlt_properties_get_int(properties, "_hide")) { return frame; } char *argument = mlt_properties_get(properties, "argument"); char *html = mlt_properties_get(properties, "html"); char *resource = mlt_properties_get(properties, "resource"); // Save the text to be used by get_image() to support parallel processing // when this filter is encapsulated by other filters. if (qstrlen(resource)) { mlt_frame_push_service(frame, NULL); } else if (qstrlen(html)) { mlt_frame_push_service(frame, NULL); } else if (qstrlen(argument)) { mlt_frame_push_service(frame, strdup(argument)); } else { return frame; } // Push the filter on to the stack mlt_frame_push_service(frame, filter); // Push the get_image on to the stack mlt_frame_push_get_image(frame, filter_get_image); return frame; } /** Constructor for the filter. */ extern "C" { mlt_filter filter_qtext_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (!filter) return NULL; if (!createQApplicationIfNeeded(MLT_FILTER_SERVICE(filter))) { mlt_filter_close(filter); return NULL; } filter->process = filter_process; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); // Assign default values mlt_properties_set_string(filter_properties, "argument", arg ? arg : "text"); mlt_properties_set_string(filter_properties, "geometry", "0%/0%:100%x100%:100%"); mlt_properties_set_string(filter_properties, "family", "Sans"); mlt_properties_set_string(filter_properties, "size", "48"); mlt_properties_set_string(filter_properties, "weight", "400"); mlt_properties_set_string(filter_properties, "style", "normal"); mlt_properties_set_string(filter_properties, "fgcolour", "0x000000ff"); mlt_properties_set_string(filter_properties, "bgcolour", "0x00000020"); mlt_properties_set_string(filter_properties, "olcolour", "0x00000000"); mlt_properties_set_string(filter_properties, "pad", "0"); mlt_properties_set_string(filter_properties, "halign", "left"); mlt_properties_set_string(filter_properties, "valign", "top"); mlt_properties_set_string(filter_properties, "outline", "0"); mlt_properties_set_string(filter_properties, "underline", "0"); mlt_properties_set_string(filter_properties, "strikethrough", "0"); mlt_properties_set_double(filter_properties, "pixel_ratio", 1.0); mlt_properties_set_double(filter_properties, "opacity", 1.0); mlt_properties_set_int(filter_properties, "_filter_private", 1); // Initialize typewriter properties mlt_properties_set_int(filter_properties, "typewriter", 0); mlt_properties_set_int(filter_properties, "typewriter.step_length", 25); mlt_properties_set_int(filter_properties, "typewriter.step_sigma", 0); mlt_properties_set_int(filter_properties, "typewriter.random_seed", 0); mlt_properties_set_int(filter_properties, "typewriter.macro_type", 1); mlt_properties_set_int(filter_properties, "typewriter.cursor", 1); mlt_properties_set_int(filter_properties, "typewriter.cursor_blink_rate", 25); mlt_properties_set_string(filter_properties, "typewriter.cursor_char", "|"); return filter; } } mlt-7.38.0/src/modules/qt/filter_qtext.yml000664 000000 000000 00000020624 15172202314 020506 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: qtext title: QText version: 7 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: Overlay text onto the video parameters: - identifier: argument title: Text type: string description: | The text to overlay. argument: yes required: yes default: text widget: text - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 mutable: yes animation: yes - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 mutable: yes widget: spinner unit: pixels - identifier: style title: Font style type: string description: > The style of the font, for example, "italic" or "narrow" depending upon family default: normal mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: color description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff mutable: yes widget: color animation: yes - identifier: bgcolour title: Background color type: color description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 mutable: yes widget: color animation: yes - identifier: olcolour title: Outline color type: color description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. mutable: yes widget: color animation: yes - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner unit: pixels - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 mutable: yes widget: checkbox - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. default: 0 mutable: yes widget: spinner unit: pixels - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - l - left - c - center - centre - r - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - t - top - m - middle - b - bottom mutable: yes widget: combo - identifier: html title: HTML String type: string description: > Render rich text from a string containing a subset of HTML 4. The only other properties that can be used with this are geometry and bgcolour. The geometry width and height defines the page or block size while its X and Y coordinates determine its position. This property has a higher priority than argument; argument is ignored if this property is set. mutable: yes - identifier: resource title: HTML File type: string description: > Render rich text from a file containing a subset of HTML 4. The only other properties that can be used with this are geometry and bgcolour. The geometry width and height defines the page or block size while its X and Y coordinates determine its position. This property has a higher priority than both argument and html; argument and html are ignored if this property is set. mutable: yes - identifier: _hide title: Hide type: boolean description: > Setting this property will not be serialized (unlike "disable"). When set true (1), the filter does not render. This allows an authoring tool to provide its own rendering while editing and then let the filter render outside the tool UI. mutable: yes default: 0 - identifier: overflow-y title: Vertical Overflow type: boolean description: > This option applies only when using html or resource properties. It controls whether the text will be cropped to the geometry property or allowed to overflow. When not set, it is automatic based on whether the geometry height is greater than or equal to the profile height. The default is unset/automatic. mutable: yes - identifier: pixel_ratio title: Pixel Ratio type: float description: > This option applies only when using html or resource properties. It adds a scaling factor to the rendering. This can be used to help to make MLT's output match a user interface. NOTE: this property is only used on Windows because it is the only platform found thus far that has different rendering behavior relative to device pixel ratio. minimum: 1.0 default: 1.0 mutable: yes - identifier: opacity title: Opacity type: float description: Opacity of all elements - text, outline, and background default: 1.0 minimum: 0 maximum: 1.0 mutable: yes widget: slider - identifier: typewriter title: Typewriter Effect type: boolean description: > Enable typewriter effect where text appears progressively over time. default: 0 minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: typewriter.step_length title: Typewriter Step Length type: integer description: > Number of frames between each character, word, or line appearance. default: 25 minimum: 1 mutable: yes widget: spinner - identifier: typewriter.step_sigma title: Typewriter Step Variation type: integer description: > Random variation in timing (standard deviation in frames). default: 0 minimum: 0 mutable: yes widget: spinner - identifier: typewriter.random_seed title: Typewriter Random Seed type: integer description: > Seed value for random number generation in timing variations. default: 0 minimum: 0 mutable: yes widget: spinner - identifier: typewriter.macro_type title: Typewriter Animation Mode type: integer description: > Granularity of typewriter animation. values: - 1 # Character by character - 2 # Word by word - 3 # Line by line default: 1 mutable: yes widget: combo - identifier: typewriter.cursor title: Typewriter Cursor type: integer description: > Show blinking cursor during typewriter animation. 0 = no cursor, 1 = cursor while typing, 2 = cursor always visible. default: 1 minimum: 0 maximum: 2 mutable: yes widget: combo - identifier: typewriter.cursor_blink_rate title: Cursor Blink Rate type: integer description: > Number of frames for cursor blink cycle (on + off). default: 25 minimum: 0 mutable: yes widget: spinner unit: frames - identifier: typewriter.cursor_char title: Cursor Character type: string description: > Character to use for the blinking cursor. default: "|" mutable: yes widget: text mlt-7.38.0/src/modules/qt/filter_typewriter.cpp000664 000000 000000 00000022200 15172202314 021530 0ustar00rootroot000000 000000 /* * filter_typewriter.cpp -- typewriter filter * Copyright (c) 2021 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied wrenderedanty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "kdenlivetitle_wrapper.h" #include "typewriter.h" struct FilterContainer { XmlParser xp; std::vector renders; // rendered data [array] bool init; // 1 if initialized int current_frame; // currently parsed frame std::string xml_data; // data field content (xml data) bool is_template; int step_length; // frame step value float sigma; // sigma of fluctuations int seed; // seed for random fluctuations int macro; // macro type: 0 - custom, 1 - char, 2 - word, 3 - line int producer_type; // 1 - kdenlivetitle mlt_producer producer; // hold producer pointer FilterContainer() { clean(); } void clean() { renders.clear(); init = false; current_frame = -1; xml_data.clear(); is_template = false; step_length = 0; sigma = 0; seed = 0; macro = 0; producer_type = 0; producer = nullptr; } }; /* * Get data for display. */ static int get_producer_data(mlt_properties filter_p, mlt_properties frame_p, FilterContainer *cont) { if (cont == nullptr) return 0; char *d = nullptr; int step_length = 0; int sigma = 0; int seed = 0; int macro = 0; mlt_producer producer = nullptr; mlt_properties producer_properties = nullptr; unsigned int update_mask = 0; /* Try with kdenlivetitle */ producer_ktitle kt = static_cast( mlt_properties_get_data(frame_p, "producer_kdenlivetitle", NULL)); if (kt != nullptr) { /* Obtain properties of producer */ producer = &kt->parent; producer_properties = MLT_PRODUCER_PROPERTIES(producer); if (producer == nullptr || producer_properties == nullptr) return 0; d = mlt_properties_get(producer_properties, "resource"); cont->is_template = (d && d[0] != '\0'); if (cont->is_template) d = mlt_properties_get(producer_properties, "_xmldata"); else d = mlt_properties_get(producer_properties, "xmldata"); if (d == nullptr) return 0; step_length = mlt_properties_get_int(filter_p, "step_length"); sigma = mlt_properties_get_int(filter_p, "step_sigma"); seed = mlt_properties_get_int(filter_p, "random_seed"); macro = mlt_properties_get_int(filter_p, "macro_type"); // if xml data changed, set update mask 0x1 if (cont->xml_data != d || macro != cont->macro) update_mask = 0x3; if (step_length != cont->step_length || sigma != cont->sigma || seed != cont->seed) update_mask |= 0x2; // clear and prepare for new parsing if (0 == update_mask) return 1; } else { return 0; } if (update_mask & 0x1) { cont->clean(); // save new data field name cont->xml_data = d; // Get content data and backup in the tw container. cont->xp.setDocument(d); cont->xp.parse(); unsigned int n = cont->xp.getContentNodesNumber(); for (uint i = 0; i < n; ++i) { std::string key = cont->xp.getNodeContent(i).toStdString(); TypeWriter data; if (macro) { char *buff = new char[key.length() + 5]; char c = 0; switch (macro) { case 1: c = 'c'; break; case 2: c = 'w'; break; case 3: c = 'l'; break; default: break; } sprintf(buff, ":%c{%s}", c, key.c_str()); data.setPattern(buff); delete[] buff; } else { data.setPattern(key); } cont->renders.push_back(data); } cont->macro = macro; cont->producer_type = 1; cont->producer = producer; // mark as inited cont->init = true; } if (update_mask & 0x2) { for (auto &render : cont->renders) { render.setFrameStep(step_length); render.setStepSigma(sigma); render.setStepSeed(seed); render.parse(); } cont->step_length = step_length; cont->sigma = sigma; cont->seed = seed; } return 1; } static int update_producer(mlt_frame frame, mlt_properties /*frame_p*/, FilterContainer *cont, bool restore) { if (cont->init == false) return 0; mlt_position pos = mlt_frame_original_position(frame); mlt_properties producer_properties = nullptr; if (cont->producer_type == 1) { producer_properties = MLT_PRODUCER_PROPERTIES(cont->producer); if (restore) mlt_properties_set_int(producer_properties, "force_reload", 0); else mlt_properties_set_int(producer_properties, "force_reload", 1); } if (producer_properties == nullptr) return 0; if (restore == true) { if (cont->is_template) mlt_properties_set(producer_properties, "_xmldata", cont->xml_data.c_str()); else mlt_properties_set(producer_properties, "xmldata", cont->xml_data.c_str()); return 1; } assert((cont->xp.getContentNodesNumber() == cont->renders.size())); // render the string and set as a content value unsigned int n = cont->xp.getContentNodesNumber(); for (uint i = 0; i < n; ++i) { cont->xp.setNodeContent(i, cont->renders[i].render(pos).c_str()); } // update producer for rest of the frame QString dom = cont->xp.getDocument(); if (cont->is_template) mlt_properties_set(producer_properties, "_xmldata", dom.toStdString().c_str()); else mlt_properties_set(producer_properties, "xmldata", dom.toStdString().c_str()); cont->current_frame = pos; return 1; } static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int /*writable*/) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); FilterContainer *cont = (FilterContainer *) filter->child; mlt_service_lock(MLT_FILTER_SERVICE(filter)); int res = get_producer_data(properties, frame_properties, cont); if (res == 0) return mlt_frame_get_image(frame, image, format, width, height, 1); update_producer(frame, frame_properties, cont, false); error = mlt_frame_get_image(frame, image, format, width, height, 1); update_producer(frame, frame_properties, cont, true); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void filter_close(mlt_filter filter) { FilterContainer *cont = (FilterContainer *) filter->child; cont->clean(); } extern "C" { mlt_filter filter_typewriter_init(mlt_profile /*profile*/, mlt_service_type /*type*/, const char * /*id*/, char * /*arg*/) { mlt_filter filter = mlt_filter_new(); FilterContainer *cont = new FilterContainer; if (filter != nullptr && cont != nullptr) { filter->process = filter_process; filter->child = cont; filter->close = filter_close; } mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties_set_int(properties, "step_length", 25); mlt_properties_set_int(properties, "step_sigma", 0); mlt_properties_set_int(properties, "random_seed", 0); mlt_properties_set_int(properties, "macro_type", 1); return filter; } } mlt-7.38.0/src/modules/qt/filter_typewriter.yml000664 000000 000000 00000002477 15172202314 021565 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter # consumer, filter, producer, or transition identifier: typewriter title: TypeWriter version: 0.3.5 copyright: Copyright (C) Rafal Lalik license: GPL language: en creator: Rafal Lalik tags: - Video description: | Typewriter effect applied to kdenlivetitler producer notes: > Original development: https://github.com/rlalik/mlt_extra_modules parameters: - identifier: step_length title: Distance between basic steps type: integer description: Defines how many frames it takes to display next character readonly: no mutable: yes default: 25 - identifier: step_sigma title: Fluctuation of step length type: integer description: Varies the step position by random value following normal distribution readonly: no mutable: yes default: 0 - identifier: random_seed title: Seed value type: integer description: Seed value for the random generator readonly: no mutable: yes default: 0 - identifier: macro_type title: Macro expansion type type: integer description: | Defines type of macro expansion: 0 - custom macro, 1 - expansion char-by-char 2 - expansion word-by-word 3 - expansion line-by-line readonly: no mutable: yes default: 1 mlt-7.38.0/src/modules/qt/gpl000664 000000 000000 00000000000 15172202314 015740 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/qt/gps_drawing.cpp000664 000000 000000 00000105410 15172202314 020256 0ustar00rootroot000000 000000 /* * filter_drawing.cpp -- draws gps related graphics * Copyright (c) 2015-2025 Meltytech, LLC * Original author: Daniel F * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "filter_gpsgraphic.h" //apply crop percentages to [min, max] then compute value at specific percentage template static double get_value_from_percent_after_crop(double perc, T min, T max, double p_min, double p_max) { double ret = 0; T delta = max - min; T new_min = min + p_min * delta / 100.0; T new_max = min + delta * p_max / 100.0; ret = (double) (new_min + perc * (new_max - new_min)); // mlt_log_info(NULL, "get_value_from_percent_after_crop (perc=%f, min=%f, max=%f, p_min=%f, p_max=%f) rezult=%f [restricted to 0..1]\n", perc, min, max, p_min, p_max, ret); return ret; } //returns a 2d point of current source-type from gp, scaled to the rect area static point_2d get_gpspoint_to_rect( mlt_filter filter, mlt_frame frame, gps_point_proc *gp, mlt_rect rect, s_base_crops used_crops) { private_data *pdata = (private_data *) filter->child; point_2d tmp = {-1, -1}; tmp.y = crop_and_normalize(get_crtval_bysrc(filter, 0, 0, gp), get_min_bysrc(filter), get_max_bysrc(filter), used_crops.bot, used_crops.top); if (pdata->graph_data_source == gspg_location_src) //if location, x-axis is longitude, else time tmp.x = crop_and_normalize(get_crtval_bysrc(filter, 0, gpsg_longitude_id, gp), get_min_bysrc(filter, gpsg_longitude_id), get_max_bysrc(filter, gpsg_longitude_id), used_crops.left, used_crops.right); else tmp.x = crop_and_normalize(gp->time, pdata->ui_crops.min_crop_time, pdata->ui_crops.max_crop_time, used_crops.left, used_crops.right); //scale point to rect area tmp.x = rect.x + tmp.x * rect.w; tmp.y = rect.y + rect.h - tmp.y * rect.h; return tmp; } //there isn't a Qt function for rect - line intersection check :( static bool rect_intersects_line(QRectF rect, point_2d pt1, point_2d pt2) { //if one point is inside the rect -> true if (rect.contains(pt1.x, pt1.y) || rect.contains(pt2.x, pt2.y)) return true; //check if each side of the rect intersects (bounded) with the line QLineF line = QLineF(pt1.x, pt1.y, pt2.x, pt2.y); if (line.intersects(QLineF(rect.topLeft(), rect.topRight()), NULL) == QLineF::BoundedIntersection) return true; if (line.intersects(QLineF(rect.bottomLeft(), rect.bottomRight()), NULL) == QLineF::BoundedIntersection) return true; if (line.intersects(QLineF(rect.topLeft(), rect.bottomLeft()), NULL) == QLineF::BoundedIntersection) return true; if (line.intersects(QLineF(rect.topRight(), rect.bottomRight()), NULL) == QLineF::BoundedIntersection) return true; return false; } //get a smooth change in color when drawing individual lines for maps //percentage is of the entire colors array, interpolation is done for the 2 nearby colors static QColor interpolate_color_from_gradient(double p, QVector &colors) { QColor ret = Qt::black; p = CLAMP(p, 0, 1); if (colors.size() == 0) return ret; if (p == 1 || colors.size() == 1) return colors[colors.size() - 1]; //the 2 colors we need to interpolate from int c1 = p * (colors.size() - 1); c1 = CLAMP(c1, 0, colors.size() - 1); int c2 = c1 + 1; c2 = CLAMP(c2, 0, colors.size() - 1); double ratio = (p * (colors.size() - 1)) - c1; //=the fractional part ratio = CLAMP(ratio, 0, 1); //Result = (color2 - color1) * ratio + color1 #define interp(v1, v2, r) (v2 - v1) * r + v1 ret.setRed(interp(colors[c1].red(), colors[c2].red(), ratio)); ret.setGreen(interp(colors[c1].green(), colors[c2].green(), ratio)); ret.setBlue(interp(colors[c1].blue(), colors[c2].blue(), ratio)); ret.setAlpha(interp(colors[c1].alpha(), colors[c2].alpha(), ratio)); #undef interp return ret; } //draws 5 horizontal (+5 vertical for 2D map) with small text for each line showing the graph value at that point void draw_legend_grid(mlt_filter filter, mlt_frame frame, QPainter &p, s_base_crops &used_crops) { private_data *pdata = (private_data *) filter->child; mlt_rect rect = pdata->img_rect; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); char *legend_unit = mlt_properties_get(properties, "legend_unit"); int nr_legend_lines = 5; QPainterPath path_grid_lines; QPen pen_5lines; pen_5lines.setWidth(1); pen_5lines.setColor(Qt::white); QFont font = p.font(); int text_size = MIN(rect.w, rect.h) / 25; font.setPixelSize(text_size); p.setFont(font); p.setPen(pen_5lines); p.setClipping(false); //legend text goes a bit outside the rect for (int i = 0; i < nr_legend_lines; i++) { path_grid_lines.moveTo(rect.x, rect.y + rect.h - (rect.h / (nr_legend_lines - 1) * i)); //for each of the nr_legend_lines compute it's absolute value = percentage of min_max (after applying crop bot/top) double crt_val = get_value_from_percent_after_crop((double) i / (nr_legend_lines - 1), get_min_bysrc(filter), get_max_bysrc(filter), used_crops.bot, used_crops.top); if (pdata->graph_data_source == gspg_location_src) { crt_val = unproject_latitude(crt_val); } crt_val = convert_bysrc_to_format(filter, crt_val); p.drawText(path_grid_lines.currentPosition().x() + 3, path_grid_lines.currentPosition().y() - 3, QString::number(crt_val, 'f', decimals_needed_bysrc(filter, crt_val)) + legend_unit); path_grid_lines.lineTo(rect.x + rect.w, rect.y + rect.h - (rect.h / 4 * i)); } //+vertical lines only for the map (longitude) if (pdata->graph_data_source == gspg_location_src) { for (int i = 0; i < nr_legend_lines; i++) { path_grid_lines.moveTo(rect.x + rect.w / (nr_legend_lines - 1) * i, rect.y); double crt_val = get_value_from_percent_after_crop((double) i / (nr_legend_lines - 1), get_min_bysrc(filter, gpsg_longitude_id), get_max_bysrc(filter, gpsg_longitude_id), used_crops.left, used_crops.right); crt_val = swap_180_if_needed(crt_val); p.drawText(path_grid_lines.currentPosition().x() + 3, path_grid_lines.currentPosition().y() + text_size + 3, QString::number(crt_val, 'f', 6)); path_grid_lines.lineTo(rect.x + rect.w / (nr_legend_lines - 1) * i, rect.y + rect.h); } } p.drawPath(path_grid_lines); p.setClipping(true); } //draws a small dot/disc on the map/graph according to the current video time + sync void draw_now_dot(mlt_filter filter, mlt_frame frame, QPainter &p, s_base_crops &used_crops) { private_data *pdata = (private_data *) filter->child; mlt_rect rect = pdata->img_rect; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); int thickness = mlt_properties_get_int(properties, "thickness"); mlt_color dot_color = mlt_properties_anim_get_color(properties, "now_dot_color", position, length); dot_color = mlt_color_convert_trc(dot_color, mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "color_trc")); //disc with internal color = white and outer color=now dot color or last used for graph line QPen dot_pen = p.pen(); dot_pen.setWidth(thickness / 1.5); //or fixed to 3px ? int disc_size = thickness * 1.5; if (dot_color.a != 0) dot_pen.setColor(QColor(dot_color.r, dot_color.g, dot_color.b, dot_color.a)); p.setBrush(Qt::white); p.setPen(dot_pen); double px, py; bool inside_only = true; //the now_dot will never leave the rect area to help in visual sync gps_point_proc crt = get_now_weighted_gpspoint(filter, frame); if (get_crtval_bysrc(filter, 0, 0, &crt) != GPS_UNINIT) { py = crop_and_normalize(get_crtval_bysrc(filter, 0, 0, &crt), get_min_bysrc(filter), get_max_bysrc(filter), used_crops.bot, used_crops.top, inside_only); if (pdata->graph_data_source == gspg_location_src) px = crop_and_normalize(get_crtval_bysrc(filter, 0, gpsg_longitude_id, &crt), get_min_bysrc(filter, gpsg_longitude_id), get_max_bysrc(filter, gpsg_longitude_id), used_crops.left, used_crops.right, inside_only); else //time px = crop_and_normalize(crt.time, pdata->ui_crops.min_crop_time, pdata->ui_crops.max_crop_time, used_crops.left, used_crops.right, inside_only); QPointF disc; disc.setX(rect.x + px * rect.w); disc.setY(rect.y + rect.h - py * rect.h); p.setClipping(false); p.drawEllipse(disc, disc_size, disc_size); p.setClipping(true); } } //draws a 2d graph of the chosen graph source (for non-location, second axis is time) void draw_main_line_graph(mlt_filter filter, mlt_frame frame, QPainter &p, s_base_crops &used_crops) { private_data *pdata = (private_data *) filter->child; mlt_rect rect = pdata->img_rect; QRectF qrect = QRectF(rect.x, rect.y, rect.w, rect.h); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); int color_style = mlt_properties_get_int(properties, "color_style"); int thickness = qAbs(mlt_properties_get_int(properties, "thickness")); int dots_only = mlt_properties_get_int(properties, "draw_individual_dots"); char *legend_unit = mlt_properties_get(properties, "legend_unit"); QVector colors = get_graph_colors(properties, MLT_FRAME_PROPERTIES(frame), position, length); QPen last_graph_pen; if (colors.size() < 2) colors.append(colors[0]); //original state p.save(); //draw legend grid lines + text if enabled if ((pdata->graph_type == gpsg_2d_map_graph || pdata->graph_type == gpsg_crop_center_graph) && mlt_properties_get_int(properties, "show_grid")) draw_legend_grid(filter, frame, p, used_crops); int i_now = get_now_gpspoint_index(filter, frame); gps_point_proc gps_now = get_now_weighted_gpspoint(filter, frame); point_2d crt_pt = {-1, -1}, next_pt = {-1, -1}; QPen pen_solid_color0; pen_solid_color0.setWidth(thickness); pen_solid_color0.setColor(colors[0]); pen_solid_color0.setCapStyle(Qt::RoundCap); last_graph_pen = pen_solid_color0; QPen pen_solid_color1; pen_solid_color1.setWidth(thickness); pen_solid_color1.setColor(colors[1]); pen_solid_color1.setCapStyle(Qt::RoundCap); QPen pen_thin_color1; //1px dots are too small, 1px line feels just too small and sometimes aliaseses weirdly at an angle //meanwhile, I've added the "Two colors" style if the future points/lines are too small, might reconsider this if (thickness > 2) pen_thin_color1.setWidth(2); else pen_thin_color1.setWidth(1); pen_thin_color1.setColor(colors[1]); pen_thin_color1.setCapStyle( Qt::RoundCap); //( Qt::SquareCap ); // I don't remember what issue I had that made me use this, keeping it for now QPen pen_gradients; pen_gradients.setWidth(thickness); pen_gradients.setCapStyle(Qt::RoundCap); //enhancement TODO: if detected gap in GPS data draw a dashed line (but needs a bunch of extra changes) //pen_dash.setStyle( Qt::DashLine ); //go through the [start_index..end_index] interval of gps_points_p and draw each pair of valid points for (int i = pdata->ui_crops.start_index; i < pdata->ui_crops.end_index; i++) { int next_i = get_next_valid_gpspoint_index(filter, i); if (i == next_i || get_crtval_bysrc(filter, i) == GPS_UNINIT || get_crtval_bysrc(filter, next_i) == GPS_UNINIT) { // mlt_log_info(filter, "incomplete pair (i=%d, %d) GPS_UNINIT, skipping drawing\n", i, next_i); continue; } crt_pt = get_gpspoint_to_rect(filter, frame, &pdata->gps_points_p[i], rect, used_crops); next_pt = get_gpspoint_to_rect(filter, frame, &pdata->gps_points_p[next_i], rect, used_crops); //don't draw line at all if it is completely outside the rect if (!rect_intersects_line(qrect, crt_pt, next_pt)) continue; //apply color style if (color_style == gpsg_color_by_solid) { p.setPen(pen_solid_color0); } else if (color_style == gpsg_color_by_solid_past_future) { if (i <= i_now) p.setPen(pen_solid_color0); else if (i > i_now) p.setPen(pen_solid_color1); } else if ((color_style == gpsg_color_by_solid_past && i <= i_now) || (color_style == gpsg_color_by_solid_future && i > i_now)) { p.setPen(pen_solid_color0); } else if ((color_style == gpsg_color_by_solid_past && i > i_now) || (color_style == gpsg_color_by_solid_future && i <= i_now)) { p.setPen(pen_thin_color1); } else if (color_style == gpsg_color_by_vertical_gradient || color_style == gpsg_color_by_horizontal_gradient) { QLinearGradient gradient; gradient.setStart(rect.x, rect.y); if (color_style == gpsg_color_by_vertical_gradient) gradient.setFinalStop(rect.x, rect.y + rect.h); else gradient.setFinalStop(rect.x + rect.w, rect.y); qreal step = 1.0 / (colors.size() - 1); for (int i = 0; i < colors.size(); i++) gradient.setColorAt((qreal) i * step, colors[i]); pen_gradients.setBrush(gradient); p.setPen(pen_gradients); } else if (color_style >= gpsg_color_by_duration && color_style <= gpsg_color_by_grade_max20) { //compute current value as a percentage of min..max #define calc_perc(v, min, max) \ (double) (v - min) / ((max - min) != 0 ? (max - min) : ((v - min) ? (v - min) : 1)) double perc = 0; if (color_style == gpsg_color_by_duration) { //this one is relative to trim, not entire gps track perc = calc_perc(pdata->gps_points_p[i].time, pdata->ui_crops.min_crop_time, pdata->ui_crops.max_crop_time); } else if (color_style == gpsg_color_by_altitude) { perc = calc_perc(pdata->gps_points_p[i].ele, pdata->minmax.min_ele, pdata->minmax.max_ele); } else if (color_style == gpsg_color_by_hr) { perc = calc_perc(pdata->gps_points_p[i].hr, pdata->minmax.min_hr, pdata->minmax.max_hr); } else if (color_style == gpsg_color_by_speed || color_style == gpsg_color_by_speed_max100) { //max 100km/h (27.777 m/s) variant to cover for bad GPS errors double used_max_speed = pdata->minmax.max_speed; if (color_style == gpsg_color_by_speed_max100 && used_max_speed > 27.777) used_max_speed = 27.777; perc = calc_perc(pdata->gps_points_p[i].speed, pdata->minmax.min_speed, used_max_speed); } else if (color_style == gpsg_color_by_grade_max90 || color_style == gpsg_color_by_grade_max20) { //limit to 90* (100%) or 20* (36.397%) - only if max is over this value double max_allowed_percentage = MAX(abs(pdata->minmax.min_grade_p), abs(pdata->minmax.max_grade_p)); max_allowed_percentage = MIN(max_allowed_percentage, (color_style == gpsg_color_by_grade_max20 ? 36.397 : 100)); double safe_grade_p = CLAMP(pdata->gps_points_p[i].grade_p, -max_allowed_percentage, max_allowed_percentage); //this one is special because middle color is always for value 0; if (pdata->gps_points_p[i].grade_p < 0) perc = calc_perc(safe_grade_p, -max_allowed_percentage, 0) / 2.0; else perc = calc_perc(safe_grade_p, 0, max_allowed_percentage) / 2.0 + 0.5; } //assign the interpolated color at p% in the colors array pen_gradients.setColor(interpolate_color_from_gradient(perc, colors)); p.setPen(pen_gradients); } else { p.setPen(pen_solid_color0); } if (i == i_now) last_graph_pen = p.pen(); //for the past/future segment we need to split it exactly at the now point into 2 different colors or it will look horrible if zoomed in enough if ((i == i_now) && (color_style == gpsg_color_by_solid_past || color_style == gpsg_color_by_solid_future || color_style == gpsg_color_by_solid_past_future)) { point_2d now_pt = get_gpspoint_to_rect(filter, frame, &gps_now, rect, used_crops); //if we got a valid intermediary point for the current location, we'll draw the past/future with different styles if (get_crtval_bysrc(filter, 0, 0, &gps_now) != GPS_UNINIT) { //"past" sub-segment if (color_style == gpsg_color_by_solid_past || color_style == gpsg_color_by_solid_past_future) p.setPen(pen_solid_color0); else if (color_style == gpsg_color_by_solid_future) p.setPen(pen_thin_color1); if (dots_only) p.drawPoint(QPointF(crt_pt.x, crt_pt.y)); else p.drawLine(QPointF(crt_pt.x, crt_pt.y), QPointF(now_pt.x, now_pt.y)); //"future" sub-segment if (color_style == gpsg_color_by_solid_past) p.setPen(pen_thin_color1); else if (color_style == gpsg_color_by_solid_future) p.setPen(pen_solid_color0); else if (color_style == gpsg_color_by_solid_past_future) p.setPen(pen_solid_color1); if (!dots_only) p.drawLine(QPointF(now_pt.x, now_pt.y), QPointF(next_pt.x, next_pt.y)); } else { //if invalid point, consider the entire line "future" if (color_style == gpsg_color_by_solid_past) p.setPen(pen_thin_color1); else if (color_style == gpsg_color_by_solid_future) p.setPen(pen_solid_color0); else if (color_style == gpsg_color_by_solid_past_future) p.setPen(pen_solid_color1); if (dots_only) p.drawPoint(QPointF(crt_pt.x, crt_pt.y)); else p.drawLine(QPointF(crt_pt.x, crt_pt.y), QPointF(next_pt.x, next_pt.y)); } } else //= full segment lines not intersecting now_dot { //IMPORTANT: the function call without QPointF() cast loses precision due to int!! fun times debugging the small random wiggles from this one if (dots_only) p.drawPoint(QPointF(crt_pt.x, crt_pt.y)); else p.drawLine(QPointF(crt_pt.x, crt_pt.y), QPointF(next_pt.x, next_pt.y)); } } //draw the current value in the bot-right corner, big bold white text if (mlt_properties_get_int(properties, "show_now_text")) { double now_val = get_crtval_bysrc(filter, 0, 0, &gps_now); if (now_val != GPS_UNINIT) { QRectF now_text_rect = QRectF(rect.x, rect.y + rect.h / 2, rect.w, rect.h / 2); QFont font = p.font(); font.setPixelSize(rect.w / 15); font.setWeight(QFont::Bold); p.setFont(font); p.setPen(Qt::white); if (pdata->graph_data_source == gspg_location_src) { now_val = unproject_latitude(now_val); } now_val = convert_bysrc_to_format(filter, now_val); QString now_text = QString::number(now_val, 'f', decimals_needed_bysrc(filter, now_val)); if (pdata->graph_data_source == gspg_location_src) { now_val = get_crtval_bysrc(filter, 0, gpsg_longitude_id, &gps_now); now_val = swap_180_if_needed(now_val); now_text += ", " + QString::number(now_val, 'f', decimals_needed_bysrc(filter, now_val)); } now_text += QString(legend_unit); p.drawText(now_text_rect, Qt::AlignBottom | Qt::AlignRight, now_text); } } //restore to before legend + main_graph p.restore(); //set color so by default the now_dot will be the same color as nearby graph p.setPen(last_graph_pen); //draw a circle for current point if (mlt_properties_get_int(properties, "show_now_dot") && pdata->graph_type <= 1) draw_now_dot(filter, frame, p, used_crops); } //draws a speedometer from the current data source (for map it displays % of total) void draw_main_speedometer(mlt_filter filter, mlt_frame frame, QPainter &p, s_base_crops &used_crops) { private_data *pdata = (private_data *) filter->child; mlt_rect rect = pdata->img_rect; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); // int color_style = mlt_properties_get_int( properties, "color_style" ) ; // int thickness = qAbs( mlt_properties_get_int( properties, "thickness" ) ); int show_grid = mlt_properties_get_int(properties, "show_grid"); QVector colors = get_graph_colors(properties, MLT_FRAME_PROPERTIES(frame), position, length); if (colors.size() == 1) colors.append(colors[0]); //draw the divisions double canvas_angle = mlt_properties_get_double(properties, "angle"); const double max_angle = 240.0; const double start_angle = 90 + 30; const int nr_big_lines = 6; const int nr_small_lines = 3; int small_side = MIN(rect.w, rect.h); double big_line_width = small_side / 10.0; double big_line_height = big_line_width / 5.0; double big_angle = max_angle / nr_big_lines; double small_angle = big_angle / (nr_small_lines + 1); QRectF big_line(0, -big_line_height / 2, -big_line_width, big_line_height); QRectF small_line(0, big_line.y() / 2, big_line.width() / 2, big_line.height() / 2); //legend = big line numbers QPen pen_speedo_nr; pen_speedo_nr.setWidth(1); pen_speedo_nr.setColor(Qt::white); QFont font = p.font(); int text_size = big_line_height * 3; font.setPixelSize(text_size); font.setWeight(QFont::Bold); p.setFont(font); p.setPen(pen_speedo_nr); p.translate(rect.x + rect.w / 2, rect.y + rect.h / 2); p.rotate(start_angle); for (int i = 0; i <= nr_big_lines; i++) { //big lines p.rotate(i * big_angle); p.translate(small_side / 2, 0); p.fillRect(big_line, colors[0]); //legend values if (show_grid) { p.save(); double crt_val = 0; if (pdata->graph_data_source != gspg_location_src) { crt_val = get_value_from_percent_after_crop((double) i / nr_big_lines, get_min_bysrc(filter), get_max_bysrc(filter), used_crops.bot, used_crops.top); crt_val = convert_bysrc_to_format(filter, crt_val); } else //just show a percentage of total for map { crt_val = 100.0 * i / nr_big_lines; } //center & rotate the text: compute the text bounding box, translate to it's center, rotate, translate back and drawtext within the rect bounding box QString l_text = QString::number(crt_val, 'f', 0); int text_width = p.fontMetrics().horizontalAdvance(l_text) + p.fontMetrics().horizontalAdvance(" "); int text_height = p.fontMetrics().height(); QRectF text_rect = QRectF(-text_width, -text_height / 2, text_width, text_height); p.translate(-big_line_width, 0); p.translate(text_rect.center()); p.rotate(-(canvas_angle + start_angle + i * big_angle)); p.translate(-text_rect.center()); p.drawText(text_rect, Qt::AlignCenter, l_text); p.restore(); } // small lines p.translate(-small_side / 2, 0); for (int j = 1; j <= nr_small_lines && i != nr_big_lines; j++) { p.rotate(j * small_angle); p.translate(small_side / 2, 0); p.fillRect(small_line, colors[1]); p.translate(-small_side / 2, 0); p.rotate(-j * small_angle); } p.rotate(-i * big_angle); } //find out the needle's angle gps_point_proc gps_now = get_now_weighted_gpspoint(filter, frame); double now_value = 0, n_angle = 0; if (pdata->graph_data_source != gspg_location_src) { if (get_crtval_bysrc(filter, 0, 0, &gps_now) != GPS_UNINIT) { n_angle = crop_and_normalize(get_crtval_bysrc(filter, 0, 0, &gps_now), get_min_bysrc(filter), get_max_bysrc(filter), used_crops.bot, used_crops.top, true); now_value = convert_bysrc_to_format(filter, get_crtval_bysrc(filter, 0, 0, &gps_now)); } } else //just show a percentage of total { n_angle = crop_and_normalize(gps_now.time, pdata->ui_crops.min_crop_time, pdata->ui_crops.max_crop_time, used_crops.left, used_crops.right, true); now_value = n_angle * 100; } p.rotate(n_angle * max_angle); //the needle considered as a "Now dot in UI"; just a long triangle if (mlt_properties_get_int(properties, "show_now_dot")) { mlt_color dot_color = mlt_properties_anim_get_color(properties, "now_dot_color", position, length); dot_color = mlt_color_convert_trc(dot_color, mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "color_trc")); if (dot_color.a == 0) // if transparent -> use main color { p.setBrush(colors[0]); p.setPen(colors[0]); } else { p.setBrush(QColor(dot_color.r, dot_color.g, dot_color.b, dot_color.a)); p.setPen(QColor(dot_color.r, dot_color.g, dot_color.b, dot_color.a)); } double needle_base_len = big_line_width / 8; QPointF needle[3] = {QPointF(-needle_base_len, -needle_base_len), QPointF(-needle_base_len, needle_base_len), QPointF(small_side / 2.0, 0)}; p.drawConvexPolygon(needle, 3); //small dot in center double needle_circle_len = big_line_width / 2; p.drawEllipse(-needle_circle_len / 2, -needle_circle_len / 2, needle_circle_len, needle_circle_len); } p.rotate(-n_angle * max_angle); p.rotate(-start_angle); //draw big bold text in white here if (mlt_properties_get_int(properties, "show_now_text")) { QRectF now_text_rect = QRectF(-small_side * 0.25, 0, small_side * 0.75, small_side / 2 - text_size * 2); QString now_text = QString::number(now_value, 'f', decimals_needed(now_value)); int now_text_size = text_size * 3; font.setPixelSize(now_text_size); p.setFont(font); p.setPen(Qt::white); p.translate(now_text_rect.center()); p.rotate(-canvas_angle); p.translate(-now_text_rect.center()); p.drawText(now_text_rect, Qt::AlignBottom | Qt::AlignHCenter, now_text); //print the legend_unit with 1/3 smaller text size below QRectF now_text_unit_rect = QRectF(-small_side * 0.25, small_side / 2 - 2 * text_size, small_side * 0.75, text_size * 2); QString now_text_unit = mlt_properties_get(properties, "legend_unit"); font.setPixelSize(text_size); p.setFont(font); p.setPen(Qt::white); p.drawText(now_text_unit_rect, Qt::AlignTop | Qt::AlignHCenter, now_text_unit); } } //inits drawing rect +clipping, antialiasing, and applies rotate void prepare_canvas(mlt_filter filter, mlt_frame frame, QImage *qimg, QPainter &p, int width, int height, s_base_crops &used_crops) { private_data *pdata = (private_data *) filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_rect rect = mlt_properties_anim_get_rect(properties, "rect", position, length); //rect area and scaling stuff if (strchr(mlt_properties_get(properties, "rect"), '%')) { rect.x *= qimg->width(); rect.w *= qimg->width(); rect.y *= qimg->height(); rect.h *= qimg->height(); } double scale = mlt_profile_scale_width(profile, width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, height); rect.y *= scale; rect.h *= scale; pdata->img_rect = rect; QRectF r(rect.x, rect.y, rect.w, rect.h); // Apply rotation double angle = mlt_properties_get_double(properties, "angle"); if (angle) { p.translate(r.x() + r.width() / 2, r.y() + r.height() / 2); p.rotate(angle); p.translate(-(r.x() + r.width() / 2), -(r.y() + r.height() / 2)); } p.setClipRect(r); //this almost doubles processing time if using large backgrounds (from 6-7ms to 10-11ms/frame) but massively improves drawImage smoothness (visible neighbor pixel jumps from frame to frame if disabled) p.setRenderHint(QPainter::SmoothPixmapTransform, true); //draw background image (cropped and matched) if (pdata->bg_img_scaled_width != 0 && strlen(pdata->last_bg_img_path) > 0 && !pdata->bg_img.isNull()) { //step 1 scales image to map aspect ratio (in process_filter_properties()) //step 2) apply bg_scale_w/h to correctly match img to GPS track -> create rescaled_bg_rect instead of modifying image here double bg_width = pdata->bg_img_scaled.width(); double bg_height = pdata->bg_img_scaled.height(); double new_bg_width = bg_width * pdata->bg_img_scaled_width; double new_bg_height = bg_height * pdata->bg_img_scaled_height; double new_bg_x = (bg_width - new_bg_width) / 2; double new_bg_y = (bg_height - new_bg_height) / 2; QRectF rescaled_bg_rect = QRectF(new_bg_x, new_bg_y, new_bg_width, new_bg_height); //step 3) apply crop percentages to the rescaled_bg_rect to follow now_dot or user input QPointF top_left = QPointF(rescaled_bg_rect.x() + rescaled_bg_rect.width() * used_crops.left / 100.0, rescaled_bg_rect.y() + rescaled_bg_rect.height() * (1 - used_crops.top / 100.0)); QPointF bot_right = QPointF(rescaled_bg_rect.bottomRight().x() - rescaled_bg_rect.width() * (1 - used_crops.right / 100.0), rescaled_bg_rect.bottomRight().y() - rescaled_bg_rect.height() * used_crops.bot / 100.0); QRectF crop_rect = QRectF(top_left, bot_right); // mlt_log_info(filter, "crop rect: x,y w,h= %f,%f %f,%f\n", crop_rect.x(), crop_rect.y(), crop_rect.width(), crop_rect.height()); //step 4) crop the computed rect from the step 1) scaled image p.setOpacity(mlt_properties_get_double(properties, "bg_opacity")); p.drawImage(r, pdata->bg_img_scaled, crop_rect); p.setOpacity(1); } p.setRenderHint(QPainter::Antialiasing, true); } mlt-7.38.0/src/modules/qt/gps_parser.cpp000664 000000 000000 00000174016 15172202314 020127 0ustar00rootroot000000 000000 /* * gps_parser.h -- Contains gps parsing (.gpx and .tcx) and processing code * Copyright (C) 2011-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __USE_XOPEN #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "gps_parser.h" #include #include #define _x (const xmlChar *) #define _s (const char *) #define _qxml_getthestring(s) qUtf8Printable((s).toString()) // Maps are usually in ESPG3857 format, not in GPS format EPSG4326 / WG84. We have to project // the latitude, otherwise big maps have wrong aspect ratio double project_latitude(double lat) { return log(tan(((90 + (double) lat) * MATH_PI) / 360)) / (MATH_PI / 180); } double unproject_latitude(double lat_projected) { return (atan(pow(exp(1), lat_projected * (MATH_PI / 180))) * 360) / MATH_PI - 90; } //shifts all (longitude) values from near 180 to 0 double get_180_swapped(double lon) { if (lon == GPS_UNINIT) return lon; return lon + (lon > 0 ? -180 : 180); } //testing max valid time between 2 points = 10 * avg_gps_time, better option would be a user entered value int get_max_gps_diff_ms(gps_private_data gdata) { return (10 * get_avg_gps_time_ms(gdata)) * 1000; } /** Computes the average time between recorded gps points * Note: needs first/last gps time already computed */ double get_avg_gps_time_ms(gps_private_data gdata) { int64_t tdiff = *gdata.last_gps_time - *gdata.first_gps_time; int gp_size = *gdata.gps_points_size; if (gp_size == 0) return 0; return (double) tdiff / gp_size; } /* Converts the datetime string from gps file into seconds since epoch * Note: assumes UTC */ int64_t datetimeXMLstring_to_mseconds(const char *text, char *format /* = NULL*/) { int64_t ret = 0; QDateTime datetime; if (format != NULL) datetime = QDateTime::fromString(QString(text), QString(format)); else datetime = QDateTime::fromString(QString(text), Qt::ISODateWithMs); if (!datetime.isValid()) { mlt_log_warning(NULL, "[filter gpstext] %s failed on string: %.25s\n", __FUNCTION__, text); return 0; } #if QT_VERSION > QT_VERSION_CHECK(6, 5, 0) datetime.setTimeZone(QTimeZone::UTC); #else datetime.setTimeZone(QTimeZone(0)); #endif ret = datetime.toMSecsSinceEpoch(); return ret; } //checks if provided char* is only made of whitespace chars static int is_whitespace_string(char *str) { unsigned i; for (i = 0; i < strlen(str); i++) { if (!isspace(str[i])) return 0; } return 1; } //Converts miliseconds to a date-time with optional format (no miliesconds in output) void mseconds_to_timestring(int64_t seconds, char *format, char *result) { time_t secs = llabs(seconds) / 1000; struct tm *ptm = gmtime(&secs); if (!format || is_whitespace_string(format)) strftime(result, 25, "%Y-%m-%d %H:%M:%S", ptm); else strftime(result, 50, format, ptm); } //returns the distance between 2 gps points (accurate for very large distances) double distance_haversine_2p(double p1_lat, double p1_lon, double p2_lat, double p2_lon) { const int R = 6371000; double dlat = to_rad(p2_lat - p1_lat); double dlon = to_rad(p2_lon - p1_lon); double a = sin(dlat / 2.0) * sin(dlat / 2.0) + cos(to_rad(p1_lat)) * cos(to_rad(p2_lat)) * sin(dlon / 2.0) * sin(dlon / 2.0); double c = 2.0 * atan2(sqrt(a), sqrt(1 - a)); return R * c; //return is in meters because radius is in meters } // Returns the distance between 2 gps points (uses haversine formula if needed) double distance_equirectangular_2p(double p1_lat, double p1_lon, double p2_lat, double p2_lon) { //use the haversine formula for very far points if (fabs(p1_lat - p2_lat) > 0.05 || fabs(p1_lat - p2_lat) > 0.05) { //~5.5km at equator to ~2km at arctic circle mlt_log_info(NULL, "distance_equirectangular_2p: points are too far away, doing haversine (%f,%f " "to %f,%f)\n", p1_lat, p1_lon, p2_lat, p2_lon); return distance_haversine_2p(p1_lat, p1_lon, p2_lat, p2_lon); } const int R = 6371000; double x = (to_rad(p2_lon) - to_rad(p1_lon)) * cos((to_rad(p2_lat) + to_rad(p1_lat)) / 2.0); double y = to_rad(p1_lat) - to_rad(p2_lat); return sqrt(x * x + y * y) * R; //NOTE: adding altitude is very risky, GPS altitude seems pretty bad and with sudden large jumps //maybe consider adding distance_2d and distance_3d fields //sqrt(2D_distance^2 + delta_alt^2) } // Computes bearing from 2 gps points double bearing_2p(double p1_lat, double p1_lon, double p2_lat, double p2_lon) { double lat1 = to_rad(p1_lat); double lat2 = to_rad(p2_lat); double dlon = to_rad(p2_lon - p1_lon); double y = sin(dlon) * cos(lat2); double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlon); double bearing = to_deg(atan2(y, x)); //normalize to 0-360 return fmod(bearing + 360, 360); } /** Searches for and returns the first valid time with locaton in the gps_points_r array * Returns in miliesconds, 0 on error */ void get_first_gps_time(gps_private_data gdata) { gps_point_raw *gps_points = gdata.gps_points_r; if (!gps_points) { *gdata.first_gps_time = 0; return; } int i = 0; while (i < *gdata.gps_points_size) { if (gps_points[i].time && has_valid_location(gps_points[i])) { //mlt_log_info(gdata.filter, "get_first_gps_time found: %d\n", gps_points[i].time); *gdata.first_gps_time = gps_points[i].time; return; } i++; } *gdata.first_gps_time = 0; } /** Searches for and returns the last valid time with location in the gps_points_r array * returns in miliesconds, returns 0 on error */ void get_last_gps_time(gps_private_data gdata) { gps_point_raw *gps_points = gdata.gps_points_r; if (!gps_points) { *gdata.last_gps_time = 0; return; } int i = *gdata.gps_points_size - 1; while (i >= 0) { if (gps_points[i].time && has_valid_location(gps_points[i])) { //mlt_log_info(gdata.filter, "get_last_gps_time found: %d\n", gps_points[i].time); *gdata.last_gps_time = gps_points[i].time; return; } i--; } *gdata.last_gps_time = 0; } //checks if time value val is between gps_points[i] and gps_points[i+1] with size checks //note: size must be inclusive (ie: gps_points[size] valid!) int time_val_between_indices_raw( int64_t time_val, gps_point_raw *gp, int i, int size, int max_gps_diff_ms, bool force_result) { if (i < 0 || i > size) return 0; else if (time_val == gp[i].time) return 1; else if (i + 1 <= size && gp[i].time <= time_val && time_val < gp[i + 1].time) { if (force_result) return 1; else if (llabs(gp[i + 1].time - gp[i].time) <= max_gps_diff_ms) return 1; } return 0; } //checks if time value val is between gps_points[i] and gps_points[i+1] with size checks //note: size must be inclusive (ie: gps_points[size] valid) int time_val_between_indices_proc( int64_t time_val, gps_point_proc *gp, int i, int size, int max_gps_diff_ms, bool force_result) { if (i < 0 || i > size) return 0; else if (time_val == gp[i].time) return 1; else if (i + 1 <= size && gp[i].time <= time_val && time_val < gp[i + 1].time) { if (force_result) return 1; else if (llabs(gp[i + 1].time - gp[i].time) <= max_gps_diff_ms) return 1; } return 0; } /** Returns the [index of] nearest gps point in time, but not farther than MAX_GPS_DIFF (10 seconds) * or -1 if search fails * Searches in raw values directly * If force_result is nonzero, it will ignore MAX_GPS_DIFF restriction */ int binary_search_gps(gps_private_data gdata, int64_t video_time, bool force_result /* = false */) { gps_point_raw *gps_points = gdata.gps_points_r; const int gps_points_size = *gdata.gps_points_size - 1; //size-1 !! int last_index = *gdata.last_searched_index; int max_gps_diff_ms = get_max_gps_diff_ms(gdata); int il = 0; int ir = gps_points_size; int mid = 0; if (!gps_points || gps_points_size <= 0) return -1; //optimize repeated calls (exact match or in between points) if (time_val_between_indices_raw(video_time, gps_points, last_index, gps_points_size, max_gps_diff_ms, force_result)) { // printf("_binary_search_gps, last_index(%d) v1 video_time: %I64d match: %I64d\n", last_index, video_time, gps_points[last_index].time); return last_index; } //optimize consecutive playback calls last_index++; if (time_val_between_indices_raw(video_time, gps_points, last_index, gps_points_size, max_gps_diff_ms, force_result)) { *gdata.last_searched_index = last_index; return last_index; } //optimize for the previous index last_index -= 2; //cancel the +1 above if (last_index >= 0 && time_val_between_indices_raw(video_time, gps_points, last_index, gps_points_size, max_gps_diff_ms, force_result)) { *gdata.last_searched_index = last_index; return last_index; } //optimize for outside values, if force result it will return 0 or (size-1) if (video_time < *gdata.first_gps_time - max_gps_diff_ms) return (force_result ? 0 : -1); if (video_time > *gdata.last_gps_time + max_gps_diff_ms) return (force_result ? gps_points_size : -1); //binary search while (il < ir) { mid = (ir + il) / 2; //mlt_log_info(gdata.filter, "binary_search_gps: mid=%d, l=%d, r=%d, mid-time=%d (s)\n", mid, il, ir, gps_points[mid].time/1000); if (time_val_between_indices_raw(video_time, gps_points, mid, gps_points_size, max_gps_diff_ms, force_result)) { *gdata.last_searched_index = mid; break; } else if (gps_points[mid].time > video_time) ir = mid; else il = mid + 1; } //don't return the closest gps point if time difference is too large (unless force_result is 1) if (llabs(video_time - gps_points[mid].time) > max_gps_diff_ms) return (force_result ? mid : -1); else return mid; } /** Returns a nicer number of decimal values for floats * [ 1.23m | 12.3m | 123m ] * - argument use_decimals defaults to -1 (in .h) */ int decimals_needed(double x, int use_decimals) { if (use_decimals != -1) return use_decimals; if (fabs(x) < 10) return 2; if (fabs(x) < 100) return 1; return 0; } /** Converts the distance (stored in meters) to the unit requested in extended keyword */ double convert_distance_to_format(double x, const char *format) { if (format == NULL || x == GPS_UNINIT) return x; if (strstr(format, "km") || strstr(format, "kilometer")) return x / 1000.0; else if (strstr(format, "mi") || strstr(format, "mile")) return x * 0.00062137; else if (strstr(format, "nm") || strstr(format, "nautical")) return x * 0.0005399568; else if (strstr(format, "ft") || strstr(format, "feet")) return x * 3.2808399; return x; } /** Converts the speed (stored in meters/second) to the unit requested in extended keyword */ double convert_speed_to_format(double x, const char *format) { if (x == GPS_UNINIT) return x; //order is important as short keywords will match anywhere (ms in kms and mi in mi[nutes]) if (format == NULL || strstr(format, "kms") || strstr(format, "km/s") || strstr(format, "kilometer")) return x * 3.6; //default km/h if (strstr(format, "ms") || strstr(format, "m/s") || strstr(format, "meter")) return x; if (strstr(format, "mmin") || strstr(format, "m/min")) return x * 60; if (strstr(format, "ftmin") || strstr(format, "ft/min")) return x * 196.850393; if (strstr(format, "mi") || strstr(format, "mi/h") || strstr(format, "mile")) return x * 2.23693629; if (strstr(format, "kn") || strstr(format, "nm/h") || strstr(format, "knots")) return x * 1.94384449; if (strstr(format, "ft") || strstr(format, "ft/s") || strstr(format, "feet")) return x * 3.2808399; return x * 3.6; } /* Converts the bearing angle (0-360) to a cardinal direction * 8 sub-divisions */ const char *bearing_to_compass(double x) { if (x == GPS_UNINIT) return "--"; if (x <= 22.5 || x >= 360 - 22.5) return "N"; else if (x < 45 + 22.5) return "NE"; else if (x <= 90 + 22.5) return "E"; else if (x < 90 + 45 + 22.5) return "SE"; else if (x <= 180 + 22.5) return "S"; else if (x < 180 + 45 + 22.5) return "SW"; else if (x <= 270 + 22.5) return "W"; else if (x < 270 + 45 + 22.5) return "NW"; return "-"; } // Updates gps-coords-based data in gps_points_p using already smoothed lat/lon void recalculate_gps_data(gps_private_data gdata) { int i; int req_smooth = gdata.last_smooth_lvl; if (req_smooth == 0) return; if (gdata.gps_points_r == NULL) { mlt_log_warning(gdata.filter, "recalculate_gps_data - gps_points_r is null!\n"); return; } if (gdata.gps_points_p == NULL) { if ((*gdata.ptr_to_gps_points_p = (gps_point_proc *) calloc(*gdata.gps_points_size, sizeof(gps_point_proc))) == NULL) { mlt_log_warning(gdata.filter, "calloc error, size=%u\n", (unsigned) (*gdata.gps_points_size * sizeof(gps_point_proc))); return; } else { //alloc ok gdata.gps_points_p = *gdata.ptr_to_gps_points_p; process_gps_smoothing(gdata, 0); } } gps_point_proc *gps_points = gdata.gps_points_p; const int gps_points_size = *gdata.gps_points_size; //compute gps_start_time actual offset int offset_start = 0; if (gdata.gps_proc_start_t != 0) offset_start = binary_search_gps(gdata, gdata.gps_proc_start_t, true) + 1; //mlt_log_info(gdata.filter, "recalculate_gps_data, offset=%d, points=%p, new:%p, size:%d, newSize:%d\n", offset, gdata.gps_points, gps_points, *gdata.gps_points_size, gps_points_size); int ignore_points_before = 0; double total_dist = 0, total_d_elev = 0, total_elev_up = 0, total_elev_down = 0, total_dist_up = 0, total_dist_down = 0, total_dist_flat = 0; double start_dist = 0, start_d_elev = 0, start_elev_up = 0, start_elev_down = 0, start_dist_up = 0, start_dist_down = 0, start_dist_flat = 0; gps_point_proc *crt_point = NULL, *prev_point = NULL, *prev_nrsmooth_point = NULL; int grade_bucket[100], speed_avg_count = 0; double speed_avg = 0, last_grade = 0; memset(&grade_bucket, 0, 100 * sizeof(int)); for (i = 0; i < gps_points_size; i++) { //store values at processing_start_time to substract them at the end if (i - 1 == offset_start) { start_dist = total_dist; start_d_elev = total_d_elev; start_elev_up = total_elev_up; start_elev_down = total_elev_down; start_dist_up = total_dist_up; start_dist_down = total_dist_down; start_dist_flat = total_dist_flat; } crt_point = &gps_points[i]; //this is the farthest valid point (behind current) that can be used for smoothing, limited by start of array or big gap in time int smooth_index = MAX(ignore_points_before, MAX(0, i - req_smooth)); //ignore points with missing lat/lon, some devices use 0,0 for no fix so we ignore those too if (!has_valid_location_ptr(crt_point) || (!crt_point->lat && !crt_point->lon)) { //set the last valid values to these points so output won't be "--" crt_point->total_dist = total_dist; crt_point->d_elev = 0; crt_point->elev_up = total_elev_up; crt_point->elev_down = total_elev_down; crt_point->dist_up = total_dist_up; crt_point->dist_down = total_dist_down; crt_point->dist_flat = total_dist_flat; continue; } //previous valid point must exist for math to happen if (prev_point == NULL) { prev_point = crt_point; //first local valid point, just set its distance to 0, other stuff can't be calculated crt_point->total_dist = total_dist; continue; } //find a valid point from i-req_smooth to i (to use in smoothing calc) while (smooth_index < i && !has_valid_location(gps_points[smooth_index])) smooth_index++; if (smooth_index < i) prev_nrsmooth_point = &gps_points[smooth_index]; else prev_nrsmooth_point = NULL; //1) get distance difference between last 2 valid points double d_dist = distance_equirectangular_2p(prev_point->lat, prev_point->lon, crt_point->lat, crt_point->lon); double d_elev = 0, d_dist_smoothed = 0, d_elev_smoothed = 0; //the int64 diff will be small enough for double double d_time_sec = (crt_point->time - prev_point->time) / 1000.0; double d_time_smoothed_sec = d_time_sec; //if time difference is way longer for one point than usual, don't do math on that gap (treat recording paused, move far, then resume) if (d_time_sec > 10.0 * (*gdata.last_gps_time - *gdata.first_gps_time) / *gdata.gps_points_size) { prev_nrsmooth_point = NULL; ignore_points_before = i; crt_point->total_dist = total_dist; prev_point = crt_point; continue; } //this is the total distance since beginning of gps processing time total_dist += d_dist; crt_point->total_dist = total_dist; //distance and height difference if smoothed point can be used if (prev_nrsmooth_point) { d_time_smoothed_sec = (crt_point->time - prev_nrsmooth_point->time) / 1000.0; d_dist_smoothed = crt_point->total_dist - prev_nrsmooth_point->total_dist; if (crt_point->ele != GPS_UNINIT && prev_nrsmooth_point->ele != GPS_UNINIT) d_elev_smoothed = crt_point->ele - prev_nrsmooth_point->ele; } //2)+3) speed and bearing if (req_smooth < 2) { crt_point->speed = d_dist / d_time_sec; //in m/s crt_point->bearing = bearing_2p(prev_point->lat, prev_point->lon, crt_point->lat, crt_point->lon); } else if (prev_nrsmooth_point) { //for "smoothing" we calculate distance between 2 points "nr_smoothing" distance behind crt_point->speed = d_dist_smoothed / d_time_smoothed_sec; crt_point->bearing = bearing_2p(prev_nrsmooth_point->lat, prev_nrsmooth_point->lon, crt_point->lat, crt_point->lon); } //continuously compute speed average if (crt_point->speed > 0.27) { speed_avg = (speed_avg * speed_avg_count + crt_point->speed) / (speed_avg_count + 1); ++speed_avg_count; } //4) altitude stuff if (crt_point->ele != GPS_UNINIT && prev_point->ele != GPS_UNINIT) { d_elev = crt_point->ele - prev_point->ele; total_d_elev += d_elev; if (crt_point->ele > prev_point->ele) { total_elev_up += d_elev; total_dist_up += d_dist; } else if (crt_point->ele < prev_point->ele) { total_elev_down += d_elev; total_dist_down += d_dist; } else { total_dist_flat += d_dist; } crt_point->d_elev = total_d_elev; crt_point->elev_up = total_elev_up; crt_point->elev_down = total_elev_down; crt_point->dist_up = total_dist_up; crt_point->dist_down = total_dist_down; crt_point->dist_flat = total_dist_flat; } //5) grade, vertical and 3d speed (if altitude present) if (crt_point->ele != GPS_UNINIT) { bool ok = 1; double used_d_elev = d_elev, used_d_dist = d_dist, used_d_time_sec = d_time_sec; if (prev_nrsmooth_point && prev_nrsmooth_point->ele != GPS_UNINIT) { used_d_elev = d_elev_smoothed; used_d_dist = d_dist_smoothed; used_d_time_sec = d_time_smoothed_sec; } else if (prev_point->ele == GPS_UNINIT) { //this looks cleaner; no previous elevation available ok = 0; } if (ok) { //cut-off grade computation (->just duplicate it) under 2km/h and half the avg speed (but don't cut-off over 15km/h) if (used_d_dist > 0.28 * (req_smooth + 1) //this covers division by 0 && used_d_dist > MIN(speed_avg / 2, 4) * req_smooth) { crt_point->grade_p = 100.0 * used_d_elev / used_d_dist; int indx = CLAMP(abs(crt_point->grade_p) / 10, 0, 99); grade_bucket[indx]++; last_grade = crt_point->grade_p; } else { //better option: set this to GPS_UNINIT and run an interpolation at the end to re-fill crt_point->grade_p = last_grade / 2; } //vertical + 3d speed crt_point->speed_vertical = used_d_elev / used_d_time_sec; crt_point->speed_3d = sqrt(used_d_dist * used_d_dist + used_d_elev * used_d_elev) / used_d_time_sec; } } prev_point = crt_point; } //for gps grade only: trim down the highest 1st percentile of values if (req_smooth > 1) { int nr_1p = gps_points_size / 100; int max_grade_1p = -1; for (i = 99; i > 1; --i) { if (grade_bucket[i] > nr_1p) { max_grade_1p = (i + 1) * 10; break; } } if (max_grade_1p > 1) { for (i = 0; i < gps_points_size; i++) { if (gps_points[i].grade_p != GPS_UNINIT && abs(gps_points[i].grade_p) > max_grade_1p) { //if speed is over 15km/h assume grade is correct if (gps_points[i].speed < MAX(speed_avg / 2, 4)) { // mlt_log_info(NULL, // "clamping [%d].grade_p from %f to %d; speed = %f\n", // i, // gps_points[i].grade_p, // max_grade_1p, // gps_points[i].speed); gps_points[i].grade_p = (gps_points[i].grade_p >= 0 ? max_grade_1p : -max_grade_1p); } } } } } //clean up computed values for relative stuff (distances mostly) before gps_processing_start time if (gdata.gps_proc_start_t != 0 && offset_start > 0 && offset_start < gps_points_size) { //mlt_log_info(gdata.filter, "recalculate_gps_data: clearing gps data from 0 to %d due to set GPS start time:%d s\n", offset_start, gdata.gps_proc_start_t/1000); for (i = 0; i < offset_start; i++) { gps_point_proc *crt_point = &(gdata.gps_points_p[i]); if (crt_point->total_dist != 0) start_dist = crt_point->total_dist; crt_point->total_dist = 0; crt_point->d_elev = 0; crt_point->elev_up = 0; crt_point->elev_down = 0; crt_point->dist_up = 0; crt_point->dist_down = 0; crt_point->dist_flat = 0; } //remove the distances from before //mlt_log_info(gdata.filter, "recalculate_gps_data: substracting values at start time! (start_dist=%f @ start index=%d)\n", start_dist, offset_start); for (i = offset_start; i < gps_points_size; i++) { gps_point_proc *crt_point = &(gdata.gps_points_p[i]); crt_point->total_dist -= start_dist; crt_point->d_elev -= start_d_elev; crt_point->elev_up -= start_elev_up; crt_point->elev_down -= start_elev_down; crt_point->dist_up -= start_dist_up; crt_point->dist_down -= start_dist_down; crt_point->dist_flat -= start_dist_flat; } } } /* Returns a weighted (type:double) value for an intermediary time * Notes: time limit 10s, if one value is GPS_UNINIT, returns the other */ double weighted_middle_double( double v1, int64_t t1, double v2, int64_t t2, int64_t new_t, int max_gps_diff_ms) { int64_t d_time = t2 - t1; if (v1 == GPS_UNINIT) return v2; if (v2 == GPS_UNINIT) return v1; if (d_time > max_gps_diff_ms || d_time == 0) return v1; double prev_weight = 1 - (double) (new_t - t1) / d_time; double next_weight = 1 - (double) (t2 - new_t) / d_time; double rez = v1 * prev_weight + v2 * next_weight; //mlt_log_info(NULL, "weighted_middle_double in: v:%f-%f, %d-%d %d out: %f\n", v1, v2, t1/1000, t2/1000, new_t/1000, rez); return rez; } /* Returns a weighted (type:int64_t) value for an intermediary time * Notes: time limit 10s, if one value is GPS_UNINIT, returns the other */ int64_t weighted_middle_int64( int64_t v1, int64_t t1, int64_t v2, int64_t t2, int64_t new_t, int max_gps_diff_ms) { int64_t d_time = t2 - t1; if (v1 == GPS_UNINIT) return v2; if (v2 == GPS_UNINIT) return v1; if (d_time > max_gps_diff_ms || d_time == 0) return v1; double prev_weight = 1 - (double) (new_t - t1) / d_time; double next_weight = 1 - (double) (t2 - new_t) / d_time; int64_t rez = v1 * prev_weight + v2 * next_weight; //mlt_log_info(NULL, "weighted_middle_int64 in: v:%d-%d, %d-%d %d out: %d (weights: prev=%f, next=%f)\n", v1%1000000, v2%1000000, t1/1000, t2/1000, new_t/1000, rez%1000000, prev_weight, next_weight); return rez; } //compute a virtual point at a specific time between 2 real points gps_point_proc weighted_middle_point_proc(gps_point_proc *p1, gps_point_proc *p2, int64_t new_t, int max_gps_diff_ms) { if (p1 == p2) return *p1; if (llabs(p2->time - p1->time) > max_gps_diff_ms) return *p1; if (new_t < MIN(p1->time, p2->time)) return *p1; if (new_t > MAX(p1->time, p2->time)) return *p2; gps_point_proc crt_point = uninit_gps_proc_point; crt_point.lat = weighted_middle_double(p1->lat, p1->time, p2->lat, p2->time, new_t, max_gps_diff_ms); crt_point.lat_projected = project_latitude(crt_point.lat); crt_point.lon = weighted_middle_double(p1->lon, p1->time, p2->lon, p2->time, new_t, max_gps_diff_ms); crt_point.speed = weighted_middle_double(p1->speed, p1->time, p2->speed, p2->time, new_t, max_gps_diff_ms); crt_point.speed_vertical = weighted_middle_double(p1->speed_vertical, p1->time, p2->speed_vertical, p2->time, new_t, max_gps_diff_ms); crt_point.speed_3d = weighted_middle_double(p1->speed_3d, p1->time, p2->speed_3d, p2->time, new_t, max_gps_diff_ms); crt_point.total_dist = weighted_middle_double(p1->total_dist, p1->time, p2->total_dist, p2->time, new_t, max_gps_diff_ms); crt_point.ele = weighted_middle_double(p1->ele, p1->time, p2->ele, p2->time, new_t, max_gps_diff_ms); crt_point.time = weighted_middle_int64(p1->time, p1->time, p2->time, p2->time, new_t, max_gps_diff_ms); crt_point.d_elev = weighted_middle_double(p1->d_elev, p1->time, p2->d_elev, p2->time, new_t, max_gps_diff_ms); crt_point.elev_up = weighted_middle_double(p1->elev_up, p1->time, p2->elev_up, p2->time, new_t, max_gps_diff_ms); crt_point.elev_down = weighted_middle_double(p1->elev_down, p1->time, p2->elev_down, p2->time, new_t, max_gps_diff_ms); crt_point.dist_up = weighted_middle_double(p1->dist_up, p1->time, p2->dist_up, p2->time, new_t, max_gps_diff_ms); crt_point.dist_down = weighted_middle_double(p1->dist_down, p1->time, p2->dist_down, p2->time, new_t, max_gps_diff_ms); crt_point.dist_flat = weighted_middle_double(p1->dist_flat, p1->time, p2->dist_flat, p2->time, new_t, max_gps_diff_ms); crt_point.bearing = weighted_middle_double(p1->bearing, p1->time, p2->bearing, p2->time, new_t, max_gps_diff_ms); crt_point.hr = weighted_middle_double(p1->hr, p1->time, p2->hr, p2->time, new_t, max_gps_diff_ms); crt_point.cad = weighted_middle_double(p1->cad, p1->time, p2->cad, p2->time, new_t, max_gps_diff_ms); crt_point.grade_p = weighted_middle_double(p1->grade_p, p1->time, p2->grade_p, p2->time, new_t, max_gps_diff_ms); crt_point.atemp = weighted_middle_double(p1->atemp, p1->time, p2->atemp, p2->time, new_t, max_gps_diff_ms); crt_point.power = weighted_middle_double(p1->power, p1->time, p2->power, p2->time, new_t, max_gps_diff_ms); return crt_point; } //checks whether 2 points (not necessarily consecutive) are not after a long pause in tracking int in_gps_time_window(gps_private_data gdata, int crt, int next) { gps_point_raw *gp = gdata.gps_points_r; int64_t d_time = llabs(gp[next].time - gp[crt].time); int d_indices = abs(next - crt); return d_time <= (get_avg_gps_time_ms(gdata) * d_indices + get_max_gps_diff_ms(gdata)); } /* Processes the entire gps_points_p array to fill the lat, lon values * Also does linear interpolation of HR, altitude (+lat/lon*) if necessary to fill missing values * After this, if do_processing is 1, calls recalculate_gps_data to update distance, speed + other fields for all points * Returns without doing anything if smoothing level is 0 */ void process_gps_smoothing(gps_private_data gdata, char do_processing) { int req_smooth = gdata.last_smooth_lvl; if (gdata.last_smooth_lvl == 0) return; if (gdata.gps_points_r == NULL) { mlt_log_warning(gdata.filter, "process_gps_smoothing - gps_points_r is null!\n"); return; } if (gdata.gps_points_p == NULL) { if ((*gdata.ptr_to_gps_points_p = (gps_point_proc *) calloc(*gdata.gps_points_size, sizeof(gps_point_proc))) == NULL) { mlt_log_warning(gdata.filter, "calloc failed, size = %u\n", (unsigned) (*gdata.gps_points_size * sizeof(gps_point_proc))); return; } else gdata.gps_points_p = *gdata.ptr_to_gps_points_p; } int max_gps_diff_ms = get_max_gps_diff_ms(gdata); int i, j, nr_hr = 0, nr_ele = 0, nr_cad = 0, nr_atemp = 0, nr_power = 0; double hr = GPS_UNINIT, ele = GPS_UNINIT, cad = GPS_UNINIT, atemp = GPS_UNINIT, power = GPS_UNINIT; //linear interpolation for heart rate, elevation, cadence and temperature, one time per file, ignores start offset if (*gdata.interpolated == 0) { //figure out how many seconds are between 2 average fixes so we can set a limit (in time) to interpolation double avg_time = (*gdata.last_gps_time - *gdata.first_gps_time) / 1000 / (double) *gdata.gps_points_size; double nr_one_minute = 60.0 / (avg_time ? avg_time : 1); gps_point_raw *gp_r = gdata.gps_points_r; gps_point_proc *gp_p = gdata.gps_points_p; for (i = 0; i < *gdata.gps_points_size; i++) { //calloc made everything 0, fill back with GPS_UNINIT if needed gp_p[i].hr = gp_r[i].hr; gp_p[i].ele = gp_r[i].ele; gp_p[i].cad = gp_r[i].cad; gp_p[i].atemp = gp_r[i].atemp; gp_p[i].power = gp_r[i].power; //heart rate if (gp_r[i].hr != GPS_UNINIT) { //found valid hr if (hr != GPS_UNINIT && nr_hr > 0 && nr_hr <= nr_one_minute) { //there were missing values and had a hr before nr_hr++; for (j = i; j > i - nr_hr; j--) { //go backwards and fill values gp_p[j].hr = hr + (gp_r[i].hr - hr) * (1.0 * (j - (i - nr_hr)) / nr_hr); //printf("_i=%d, j=%d, nr_hr=%d hr = %d; gp_r[i].hr=%f, gp_p[j].hr=%f \n", i,j,nr_hr, hr, gp_r[i].hr, gp_p[j].hr); } } hr = gp_r[i].hr; nr_hr = 0; } else nr_hr++; //altitude if (gp_r[i].ele != GPS_UNINIT) { if (ele != GPS_UNINIT && nr_ele > 0 && nr_ele <= nr_one_minute * 10) { nr_ele++; for (j = i; j > i - nr_ele; j--) { gp_p[j].ele = ele + 1.0 * (gp_r[i].ele - ele) * (1.0 * (j - (i - nr_ele)) / nr_ele); //printf("_i=%d, j=%d, nr_ele=%d ele = %f; gp_r[i].ele=%f, gp_p[j].ele=%f \n", i,j,nr_ele, ele, gp_r[i].ele, gp_p[j].ele); } } ele = gp_r[i].ele; nr_ele = 0; } else nr_ele++; //cadence if (gp_r[i].cad != GPS_UNINIT) { if (cad != GPS_UNINIT && nr_cad > 0 && nr_cad <= nr_one_minute) { nr_cad++; for (j = i; j > i - nr_cad; j--) { gp_p[j].cad = cad + 1.0 * (gp_r[i].cad - cad) * (1.0 * (j - (i - nr_cad)) / nr_cad); } } cad = gp_r[i].cad; nr_cad = 0; } else nr_cad++; //temperature if (gp_r[i].atemp != GPS_UNINIT) { if (atemp != GPS_UNINIT && nr_atemp > 0 && nr_atemp <= nr_one_minute * 60) { nr_atemp++; for (j = i; j > i - nr_atemp; j--) { gp_p[j].atemp = atemp + 1.0 * (gp_r[i].atemp - atemp) * (1.0 * (j - (i - nr_atemp)) / nr_atemp); } } atemp = gp_r[i].atemp; nr_atemp = 0; } else { nr_atemp++; } //power if (gp_r[i].power != GPS_UNINIT) { if (power != GPS_UNINIT && nr_power > 0 && nr_power <= nr_one_minute) { nr_power++; for (j = i; j > i - nr_power; j--) { gp_p[j].power = power + 1.0 * (gp_r[i].power - power) * (1.0 * (j - (i - nr_power)) / nr_power); } } power = gp_r[i].power; nr_power = 0; } else { nr_power++; } //these are not interpolated but as long as we're iterating we can copy them now gp_p[i].time = gp_r[i].time; gp_p[i].lat = gp_r[i].lat; gp_p[i].lat_projected = gp_r[i].lat_projected; gp_p[i].lon = gp_r[i].lon; } } gps_point_raw *gps_points_r = gdata.gps_points_r; gps_point_proc *gps_points_p = gdata.gps_points_p; const int gps_points_size = *gdata.gps_points_size; for (i = 0; i < gps_points_size; i++) { if (req_smooth == 1) { //copy raw lat/lon to calc lat/lon and interpolate 1 location if necessary gps_points_p[i].lat = gps_points_r[i].lat; gps_points_p[i].lat_projected = gps_points_r[i].lat_projected; gps_points_p[i].lon = gps_points_r[i].lon; //this can happen often if location and altitude are stored at different time intervals (every 3s vs every 10s) if (i - 1 >= 0 && i + 1 < gps_points_size //if we're not at start/end && !has_valid_location(gps_points_r[i]) && has_valid_location(gps_points_r[i - 1]) && has_valid_location( gps_points_r[i + 1]) //if current point has no lat/lon but nearby ones do && llabs(gps_points_r[i + 1].time - gps_points_r[i - 1].time) < max_gps_diff_ms) //if time difference is lower than max_gps_diff_ms { //place a weighted "middle" point here depending on time difference gps_points_p[i].lat = weighted_middle_double(gps_points_r[i - 1].lat, gps_points_r[i - 1].time, gps_points_r[i + 1].lat, gps_points_r[i + 1].time, gps_points_r[i].time, max_gps_diff_ms); gps_points_p[i].lat_projected = project_latitude(gps_points_p[i].lat); gps_points_p[i].lon = weighted_middle_double(gps_points_r[i - 1].lon, gps_points_r[i - 1].time, gps_points_r[i + 1].lon, gps_points_r[i + 1].time, gps_points_r[i].time, max_gps_diff_ms); //mlt_log_info(gdata.filter, "interpolating position for smooth=1, new point[%d]:%f,%f @time=%d s\n", i, gps_points_p[i].lat, gps_points_p[i].lon, gps_points_r[i].time/1000); } } else if (req_smooth > 1) { //for each point average "req_smooth/2" values before and after double lat_sum = 0, lon_sum = 0; int nr_div = 0; //passing 180 meridian is ok, but passing both 180 and 0 (so more than one full circle around the earth) will smooth out badly; no fix planned for this for (j = MAX(0, i - req_smooth / 2); j < MIN(i + req_smooth / 2, gps_points_size); j++) { if (has_valid_location(gps_points_r[j]) && in_gps_time_window(gdata, i, j)) { lat_sum += gps_points_r[j].lat; lon_sum += gps_points_r[j].lon; nr_div++; } } if (nr_div != 0) { gps_points_p[i].lat = lat_sum / nr_div; gps_points_p[i].lat_projected = project_latitude(gps_points_p[i].lat); gps_points_p[i].lon = lon_sum / nr_div; } else { gps_points_p[i].lat = gps_points_r[i].lat; gps_points_p[i].lat_projected = gps_points_r[i].lat; gps_points_p[i].lon = gps_points_r[i].lon; } //mlt_log_info(filter, "i=%d, lat_sum=%f, lon_sum=%f, nr_div=%d, time=%d\n", i, lat_sum, lon_sum, nr_div, gps_points[i].time); } } *gdata.interpolated = 1; if (req_smooth != 0 && do_processing == 1) recalculate_gps_data(gdata); } //File parsing with QT's XML stream reader // Parses a .gpx file into a gps_point_raw linked list void qxml_parse_gpx(QXmlStreamReader &reader, gps_point_ll **gps_list, int *count_pts) { int64_t last_time = -1; //support no time in file because many services (Strava included) do this for exported routes gps_point_ll *no_time_head = NULL, *no_time_prev = NULL; int no_time_count = 0; /* // sample point from .GPX file (version 1.0) + TrackPointExtension 868.3005248873908 0.0 337.1557 0 //strava ... //official garmin extension 132 //heart rate in beats per minute 75 //cadence in revolutions per minute 27.5 //ambient temperature in degrees celsius */ while (!reader.atEnd() && !reader.hasError()) { reader.readNext(); // mlt_log_info(NULL, " readNext tag: %s type = %d\n", _qxml_getthestring(reader.name()), reader.tokenType()); if (reader.isStartElement() && reader.name() == QStringLiteral("trkpt")) { gps_point_raw crt_point = uninit_gps_raw_point; QXmlStreamAttributes attributes = reader.attributes(); if (attributes.hasAttribute("lat")) { crt_point.lat = attributes.value("lat").toDouble(); crt_point.lat_projected = project_latitude(crt_point.lat); } if (attributes.hasAttribute("lon")) crt_point.lon = attributes.value("lon").toDouble(); while (reader.readNext() && !(reader.name() == QStringLiteral("trkpt") && reader.tokenType() == QXmlStreamReader::EndElement)) //until closing trkpt { // mlt_log_info(NULL, "[trkpt->1] readNext tag: %s, type=%d\n", _qxml_getthestring(reader.name()), reader.tokenType()); if (!reader.isStartElement()) continue; if (reader.name() == QStringLiteral("ele")) crt_point.ele = reader.readElementText().toDouble(); else if (reader.name() == QStringLiteral("time")) crt_point.time = datetimeXMLstring_to_mseconds( qUtf8Printable(reader.readElementText())); else if (reader.name() == QStringLiteral("speed")) crt_point.speed = reader.readElementText().toDouble(); else if (reader.name() == QStringLiteral("course")) crt_point.bearing = reader.readElementText().toDouble(); else if (reader.name() == QStringLiteral("extensions")) { while (reader.readNext() && !(reader.name() == QStringLiteral("extensions") && reader.tokenType() == QXmlStreamReader::EndElement)) { if (reader.name() == QStringLiteral("power")) crt_point.power = reader.readElementText().toDouble(); else if (reader.name() == QStringLiteral("TrackPointExtension")) { while (reader.readNext() && !(reader.name() == QStringLiteral("TrackPointExtension") && reader.tokenType() == QXmlStreamReader::EndElement)) { if (reader.name() == QStringLiteral("hr")) crt_point.hr = reader.readElementText().toDouble(); else if (reader.name() == QStringLiteral("cad")) crt_point.cad = reader.readElementText().toDouble(); else if (reader.name() == QStringLiteral("atemp")) crt_point.atemp = reader.readElementText().toDouble(); } } } } } //now add the point to linked list (but only if increasing time) if (crt_point.time != GPS_UNINIT && crt_point.time > last_time) { *gps_list = (gps_point_ll *) calloc(1, sizeof(gps_point_ll)); if (*gps_list == NULL) return; *count_pts += 1; (*gps_list)->gp = crt_point; (*gps_list)->next = NULL; gps_list = &(*gps_list)->next; last_time = crt_point.time; } else if (crt_point.time == GPS_UNINIT) { //if no time is stored, we make it up starting from 1 gps_point_ll *tmp = (gps_point_ll *) calloc(1, sizeof(gps_point_ll)); if (tmp == NULL) return; if (no_time_head == NULL) { no_time_head = tmp; no_time_prev = tmp; } else { no_time_prev->next = tmp; no_time_prev = tmp; } no_time_count += 1; crt_point.time = no_time_count * 1000; tmp->gp = crt_point; tmp->next = NULL; } else { mlt_log_info(NULL, "qxml_parse_gpx: skipping point due to time [%d] %f,%f - crt:%u.%u, " "last:%u.%u\n", *count_pts, crt_point.lat, crt_point.lon, (unsigned) (crt_point.time / 1000), (unsigned) (crt_point.time % 1000), (unsigned) (last_time / 1000), (unsigned) (last_time % 1000)); } } } //if no points had time, we replace the real list with the no_time one if (*count_pts == 0 && no_time_count > 0) { mlt_log_info(NULL, "qxml_parse_gpx: all GPS points are missing time values, inserting each " "point at 1 second interval!\n"); *gps_list = no_time_head; *count_pts = no_time_count; } else if (*count_pts > 0 && no_time_count > 0) { mlt_log_info(NULL, "qxml_parse_gpx: %d GPS points have no time in original file and have been " "discarded (total valid points kept: %d)!\n", no_time_count, *count_pts); //cleanup the no_time list as we're not using it while (no_time_head) { gps_point_ll *tmp = no_time_head; no_time_head = no_time_head->next; free(tmp); } } } // Parses a .tcx file into a gps_point_raw linked list void qxml_parse_tcx(QXmlStreamReader &reader, gps_point_ll **gps_list, int *count_pts) { int64_t last_time = -1; //support no time in file because many services (Strava included) do this for exported routes gps_point_ll *no_time_head = NULL, *no_time_prev = NULL; int no_time_count = 0; /* //sample point from .TCX file 44.48205950925148 26.1843781367422 65.0 25919.213486999884 124 78 //unofficial, but some popular converters use it 25 //unofficial/not widely used //garmin's extension for power 0.3449999988079071 0 */ while (!reader.atEnd() && !reader.hasError()) { reader.readNext(); // mlt_log_info(NULL, " readNextStartElement tag: %s\n", _qxml_getthestring(reader.name())); if (reader.isStartElement() && reader.name() == QStringLiteral("Trackpoint")) { gps_point_raw crt_point = uninit_gps_raw_point; while ( reader.readNext() && !(reader.name() == QStringLiteral("Trackpoint") && reader.tokenType() == QXmlStreamReader::EndElement)) //loop until we hit the closing { // mlt_log_info(NULL, "[Trackpoint->1] readNext tag: %s, type=%d\n", _qxml_getthestring(reader.name()), reader.tokenType()); if (!reader.isStartElement()) continue; if (reader.name() == QStringLiteral("Time")) crt_point.time = datetimeXMLstring_to_mseconds( qUtf8Printable(reader.readElementText())); else if (reader.name() == QStringLiteral("Position")) { reader.readNextStartElement(); if (reader.name() == QStringLiteral("LatitudeDegrees")) { crt_point.lat = reader.readElementText().toDouble(); crt_point.lat_projected = project_latitude(crt_point.lat); } reader.readNextStartElement(); if (reader.name() == QStringLiteral("LongitudeDegrees")) crt_point.lon = reader.readElementText().toDouble(); } else if (reader.name() == QStringLiteral("AltitudeMeters")) { crt_point.ele = reader.readElementText().toDouble(); } else if (reader.name() == QStringLiteral("DistanceMeters")) { crt_point.total_dist = reader.readElementText().toDouble(); } else if (reader.name() == QStringLiteral("HeartRateBpm")) { reader.readNextStartElement(); if (reader.name() == QStringLiteral("Value")) crt_point.hr = reader.readElementText().toDouble(); } else if (reader.name() == QStringLiteral("Cadence")) { crt_point.cad = reader.readElementText().toDouble(); } else if (reader.name() == QStringLiteral("Temperature")) { crt_point.atemp = reader.readElementText().toDouble(); } else if (reader.isStartElement() && reader.name() == QStringLiteral("Extensions")) { while (reader.readNext() && !(reader.name() == QStringLiteral("TPX") && reader.tokenType() == QXmlStreamReader::EndElement)) { if (reader.name() == QStringLiteral("Speed")) crt_point.speed = reader.readElementText().toDouble(); else if (reader.name() == QStringLiteral("Watts")) crt_point.power = reader.readElementText().toDouble(); } } } //now add the point to linked list (but only if increasing time) if (crt_point.time != GPS_UNINIT && crt_point.time > last_time) { *gps_list = (gps_point_ll *) calloc(1, sizeof(gps_point_ll)); if (*gps_list == NULL) return; *count_pts += 1; (*gps_list)->gp = crt_point; (*gps_list)->next = NULL; gps_list = &(*gps_list)->next; last_time = crt_point.time; } else if (crt_point.time == GPS_UNINIT) { //if no time is stored, we make it up starting from 1 gps_point_ll *tmp = (gps_point_ll *) calloc(1, sizeof(gps_point_ll)); if (tmp == NULL) return; if (no_time_head == NULL) { no_time_head = tmp; no_time_prev = tmp; } else { no_time_prev->next = tmp; no_time_prev = tmp; } no_time_count += 1; crt_point.time = no_time_count * 1000; tmp->gp = crt_point; tmp->next = NULL; } else mlt_log_info(NULL, "qxml_parse_tcx: skipping point due to time [%d] %f,%f - crt:%u.%u, " "last:%u.%u\n", *count_pts, crt_point.lat, crt_point.lon, (unsigned) (crt_point.time / 1000), (unsigned) (crt_point.time % 1000), (unsigned) (last_time / 1000), (unsigned) (last_time % 1000)); } } //if no points had time, we replace the real list with the no_time one if (*count_pts == 0 && no_time_count > 0) { mlt_log_info(NULL, "qxml_parse_gpx: all GPS points are missing time values, inserting each " "point at 1 second interval!\n"); *gps_list = no_time_head; *count_pts = no_time_count; } else if (*count_pts > 0 && no_time_count > 0) { //cleanup the no_time list as we're not using it mlt_log_info(NULL, "qxml_parse_gpx: %d GPS points have no time in original file and have been " "discarded (total valid points kept: %d)!\n", no_time_count, *count_pts); while (no_time_head) { gps_point_ll *tmp = no_time_head; no_time_head = no_time_head->next; free(tmp); } } } /* Reads file, parses it and stores the gps values in a gps_point_raw array (allocated inside) * and its size in gps_points_size. * swap_to_180 -> shifts all longitudes around with 180 becoming 0, only use it if user requests it * Returns 0 on failure, 1 on success. */ int qxml_parse_file(gps_private_data gdata) { gps_point_ll *gps_list_head = NULL, *tmp = NULL; int count_pts = 0, nr = 0; char *filename = gdata.last_filename; gps_point_raw *gps_array = NULL; int &swap_to_180 = *gdata.swap180; // mlt_log_info(gdata.filter, "in qxml_parse_file, filename: %s, swap_to_180=%d\n", filename, swap_to_180); QFile gps_file(filename); if (!gps_file.open(QFile::ReadOnly | QFile::Text)) { mlt_log_warning(gdata.filter, "qxml_parse_file couldn't read file: %s\n", filename); return 0; } QXmlStreamReader qxml_reader(&gps_file); qxml_reader.setNamespaceProcessing(false); while (!qxml_reader.atEnd() && !qxml_reader.hasError()) { qxml_reader.readNextStartElement(); if (qxml_reader.isStartDocument()) { // mlt_log_info(NULL, "qxml_parse_file: StartDocument found. Encoding=%s\n", qUtf8Printable(qxml_reader.documentEncoding().toString())); continue; } // mlt_log_info(NULL, " readNext element tag: %s, type=%d\n", qUtf8Printable(qxml_reader.name().toString()), qxml_reader.tokenType()); if (qxml_reader.name() == QStringLiteral("TrainingCenterDatabase")) { qxml_parse_tcx(qxml_reader, &gps_list_head, &count_pts); } else if (qxml_reader.name() == QStringLiteral("gpx")) { qxml_parse_gpx(qxml_reader, &gps_list_head, &count_pts); } else { mlt_log_warning( gdata.filter, "qxml_parse_file: fail to parse file: %s, unknown root element: %s. Aborting.\n", filename, qUtf8Printable(qxml_reader.name().toString())); return 0; } } if (qxml_reader.hasError()) { mlt_log_info(NULL, "qxml_reader.hasError! line:%u, errString:%s\n", (unsigned) qxml_reader.lineNumber(), qUtf8Printable(qxml_reader.errorString())); return 0; } qxml_reader.clear(); //not sure if really needed if not reused if (count_pts < 2) { mlt_log_warning(gdata.filter, "qxml_parse_file: less than 2 gps points read (%d). Aborting.\n", count_pts); return 0; } *gdata.ptr_to_gps_points_r = (gps_point_raw *) calloc(count_pts, sizeof(gps_point_raw)); gps_array = *gdata.ptr_to_gps_points_r; //just an alias if (gps_array == NULL) { mlt_log_error(gdata.filter, "malloc error (size=%u)\n", (unsigned) (count_pts * sizeof(gps_point_raw))); } *gdata.gps_points_size = count_pts; //copy points to private array and free list while (gps_list_head) { gps_array[nr++] = gps_list_head->gp; tmp = gps_list_head; gps_list_head = gps_list_head->next; free(tmp); } //search to find if we cross the 180 and/or the 0 meridian bool crosses0 = false, crosses180 = false; for (int i = 0; i < *gdata.gps_points_size - 1; i++) { double crt = gps_array[i].lon; double next = gps_array[i + 1].lon; if (crt == GPS_UNINIT || next == GPS_UNINIT) continue; if ((crt < 0 && next > 0) || (crt > 0 && next < 0)) { if (crt - next > 180 || next - crt > 180) crosses180 = true; else crosses0 = true; } } mlt_log_info( NULL, "_automatic 180 meridian detection: crosses180=%d, crosses0=%d --> swapping180=%d\n", crosses180, crosses0, (crosses180 && !crosses0)); //if only 180 is crossed, we swap, otherswise we don't do anything if (crosses180 && !crosses0) { swap_to_180 = 1; for (int i = 0; i < *gdata.gps_points_size; i++) gps_array[i].lon = get_180_swapped(gps_array[i].lon); } else swap_to_180 = 0; // //debug result: // for (int i = 0; i < *gdata.gps_points_size; i += 100) { // gps_point_raw *crt_point = &gps_array[i]; // mlt_log_info(NULL, // "_xml_parse_file read point[%d]: time:%d, lat:%f, lon:%f, speed:%f, " // "distance:%f, ele:%f, hr:%f, bearing:%f, cadence:%f, temp:%f, power:%f\n", // i, // crt_point->time / 1000, // crt_point->lat, // crt_point->lon, // crt_point->speed, // crt_point->total_dist, // crt_point->ele, // crt_point->hr, // crt_point->bearing, // crt_point->cad, // crt_point->atemp, // crt_point->power); //} return 1; } mlt-7.38.0/src/modules/qt/gps_parser.h000664 000000 000000 00000021657 15172202314 017576 0ustar00rootroot000000 000000 /* * gps_parser.h -- Header for gps parsing (.gpx and .tcx) and processing * Copyright (C) 2011-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GPS_PARSER_H #define GPS_PARSER_H #include #include #include #include #define GPS_UNINIT -9999 typedef struct { double lat, lon, lat_projected, speed, total_dist, ele, hr, bearing, cad, atemp, power; int64_t time; //epoch milliseconds } gps_point_raw; typedef struct gps_point_raw_list { gps_point_raw gp; struct gps_point_raw_list *next; } gps_point_ll; typedef struct { double lat, lon, lat_projected, speed, speed_vertical, speed_3d, total_dist, ele, hr, bearing, cad, atemp, power; int64_t time; double d_elev, elev_up, elev_down, dist_up, dist_down, dist_flat, grade_p; } gps_point_proc; //0 is a valid value for many fields, use GPS_UNINIT (-9999) to differentiate missing values static const gps_point_raw uninit_gps_raw_point = {.lat = GPS_UNINIT, .lon = GPS_UNINIT, .lat_projected = GPS_UNINIT, .speed = GPS_UNINIT, .total_dist = GPS_UNINIT, .ele = GPS_UNINIT, .hr = GPS_UNINIT, .bearing = GPS_UNINIT, .cad = GPS_UNINIT, .atemp = GPS_UNINIT, .power = GPS_UNINIT, .time = GPS_UNINIT}; static const gps_point_proc uninit_gps_proc_point = {.lat = GPS_UNINIT, .lon = GPS_UNINIT, .lat_projected = GPS_UNINIT, .speed = GPS_UNINIT, .speed_vertical = GPS_UNINIT, .speed_3d = GPS_UNINIT, .total_dist = GPS_UNINIT, .ele = GPS_UNINIT, .hr = GPS_UNINIT, .bearing = GPS_UNINIT, .cad = GPS_UNINIT, .atemp = GPS_UNINIT, .power = GPS_UNINIT, .time = GPS_UNINIT, .d_elev = GPS_UNINIT, .elev_up = GPS_UNINIT, .elev_down = GPS_UNINIT, .dist_up = GPS_UNINIT, .dist_down = GPS_UNINIT, .dist_flat = GPS_UNINIT, .grade_p = GPS_UNINIT}; //structure used to ease argument passing between filter and GPS parser api typedef struct { gps_point_raw *gps_points_r; //raw gps data from file gps_point_proc *gps_points_p; //processed gps data gps_point_raw **ptr_to_gps_points_r; //if malloc is needed gps_point_proc **ptr_to_gps_points_p; int *gps_points_size; int *last_searched_index; //used to cache repeated searches int64_t *first_gps_time; int64_t *last_gps_time; char *interpolated; int *swap180; //read only: int64_t gps_proc_start_t; //process only points after this time (epoch miliseconds) int last_smooth_lvl; char *last_filename; //gps file fullpath mlt_filter filter; } gps_private_data; #define has_valid_location(x) (((x).lat) != GPS_UNINIT && ((x).lon) != GPS_UNINIT) #define has_valid_location_ptr(x) ((x) && ((x)->lat) != GPS_UNINIT && ((x)->lon) != GPS_UNINIT) #define MATH_PI 3.14159265358979323846 #define to_rad(x) ((x) *MATH_PI / 180.0) #define to_deg(x) ((x) *180.0 / MATH_PI) int64_t datetimeXMLstring_to_mseconds(const char *text, char *format = NULL); void mseconds_to_timestring(int64_t seconds, char *format, char *result); double distance_haversine_2p(double p1_lat, double p1_lon, double p2_lat, double p2_lon); double distance_equirectangular_2p(double p1_lat, double p1_lon, double p2_lat, double p2_lon); double bearing_2p(double p1_lat, double p1_lon, double p2_lat, double p2_lon); void get_first_gps_time(gps_private_data gdata); void get_last_gps_time(gps_private_data gdata); double get_avg_gps_time_ms(gps_private_data gdata); int get_max_gps_diff_ms(gps_private_data gdata); int binary_search_gps(gps_private_data gdata, int64_t video_time, bool force_result = false); int decimals_needed(double x, int use_decimals = -1); double convert_distance_to_format(double x, const char *format); double convert_speed_to_format(double x, const char *format); const char *bearing_to_compass(double x); void recalculate_gps_data(gps_private_data gdata); void process_gps_smoothing(gps_private_data gdata, char do_processing); double project_latitude(double lat); double unproject_latitude(double lat_projected); double weighted_middle_double( double v1, int64_t t1, double v2, int64_t t2, int64_t new_t, int max_gps_diff_ms); int64_t weighted_middle_int64( int64_t v1, int64_t t1, int64_t v2, int64_t t2, int64_t new_t, int max_gps_diff_ms); gps_point_proc weighted_middle_point_proc(gps_point_proc *p1, gps_point_proc *p2, int64_t new_t, int max_gps_diff_ms); void qxml_parse_gpx(QXmlStreamReader &reader, gps_point_ll **gps_list, int *count_pts); void qxml_parse_tcx(QXmlStreamReader &reader, gps_point_ll **gps_list, int *count_pts); int qxml_parse_file(gps_private_data gdata); #define swap_180_if_needed(lon) (pdata->swap_180 ? get_180_swapped(lon) : lon) double get_180_swapped(double lon); int time_val_between_indices_proc( int64_t time_val, gps_point_proc *gp, int i, int size, int max_gps_diff_ms, bool force_result); int time_val_between_indices_raw( int64_t time_val, gps_point_raw *gp, int i, int size, int max_gps_diff_ms, bool force_result); //TODO: this is copied from src\framework\mlt_producer.c /* * Boost implementation of timegm() * (C) Copyright Howard Hinnant * (C) Copyright 2010-2011 Vicente J. Botet Escriba */ static inline int32_t is_leap(int32_t year) { if (year % 400 == 0) return 1; if (year % 100 == 0) return 0; if (year % 4 == 0) return 1; return 0; } static inline int32_t days_from_0(int32_t year) { year--; return 365 * year + (year / 400) - (year / 100) + (year / 4); } static inline int32_t days_from_1970(int32_t year) { const int days_from_0_to_1970 = days_from_0(1970); return days_from_0(year) - days_from_0_to_1970; } static inline int32_t days_from_1jan(int32_t year, int32_t month, int32_t day) { static const int32_t days[2][12] = {{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}}; return days[is_leap(year)][month - 1] + day - 1; } static inline time_t internal_timegm(struct tm const *t) { int year = t->tm_year + 1900; int month = t->tm_mon; if (month > 11) { year += month / 12; month %= 12; } else if (month < 0) { int years_diff = (-month + 11) / 12; year -= years_diff; month += 12 * years_diff; } month++; int day = t->tm_mday; int day_of_year = days_from_1jan(year, month, day); int days_since_epoch = days_from_1970(year) + day_of_year; time_t seconds_in_day = 3600 * 24; time_t result = seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec; return result; } /* End of Boost implementation of timegm(). */ #endif /* GPS_PARSER_H */ mlt-7.38.0/src/modules/qt/graph.cpp000664 000000 000000 00000024171 15172202314 017057 0ustar00rootroot000000 000000 /* * Copyright (c) 2015-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "graph.h" #include #include #include QVector get_graph_colors(mlt_properties filter_properties, mlt_properties frame_properties, int position, int length) { QVector colors; // Find user specified colors for the gradient while (true) { QString prop_name = QStringLiteral("color.") + QString::number(colors.size() + 1); if (mlt_properties_exists(filter_properties, prop_name.toUtf8().constData())) { mlt_color mcolor = mlt_properties_anim_get_color(filter_properties, prop_name.toUtf8().constData(), position, length); mcolor = mlt_color_convert_trc(mcolor, mlt_properties_get(frame_properties, "color_trc")); colors.append(QColor(mcolor.r, mcolor.g, mcolor.b, mcolor.a)); } else { break; } } if (colors.size() == 0) { // No colors found - use a default. colors.append(QColor(Qt::white)); } return colors; } /* * Apply the "bgcolor" and "angle" parameters to the QPainter */ void setup_graph_painter(QPainter &p, QRectF &r, mlt_properties filter_properties, mlt_properties frame_properties, int position, int length) { mlt_color bg_color = mlt_properties_anim_get_color(filter_properties, "bgcolor", position, length); bg_color = mlt_color_convert_trc(bg_color, mlt_properties_get(frame_properties, "color_trc")); double angle = mlt_properties_anim_get_double(filter_properties, "angle", position, length); p.setRenderHint(QPainter::Antialiasing); // Fill background if (bg_color.r || bg_color.g || bg_color.g || bg_color.a) { QColor qbgcolor(bg_color.r, bg_color.g, bg_color.b, bg_color.a); p.fillRect(0, 0, p.device()->width(), p.device()->height(), qbgcolor); } // Apply rotation if (angle) { p.translate(r.x() + r.width() / 2, r.y() + r.height() / 2); p.rotate(angle); p.translate(-(r.x() + r.width() / 2), -(r.y() + r.height() / 2)); } } /* * Apply the "thickness", "gorient" and "color.x" parameters to the QPainter */ void setup_graph_pen(QPainter &p, QRectF &r, mlt_properties filter_properties, mlt_properties frame_properties, double scale, int position, int length) { int thickness = mlt_properties_anim_get_int(filter_properties, "thickness", position, length) * scale; QString gorient = mlt_properties_get(filter_properties, "gorient"); QVector colors = get_graph_colors(filter_properties, frame_properties, position, length); QPen pen; pen.setWidth(qAbs(thickness)); if (colors.size() == 1) { // Only use one color pen.setBrush(colors[0]); } else { QLinearGradient gradient; if (gorient.startsWith("h", Qt::CaseInsensitive)) { gradient.setStart(r.x(), r.y()); gradient.setFinalStop(r.x() + r.width(), r.y()); } else { // Vertical gradient.setStart(r.x(), r.y()); gradient.setFinalStop(r.x(), r.y() + r.height()); } qreal step = 1.0 / (colors.size() - 1); for (int i = 0; i < colors.size(); i++) { gradient.setColorAt((qreal) i * step, colors[i]); } pen.setBrush(gradient); } p.setPen(pen); } static inline void fix_point(QPointF &point, QRectF &rect) { if (point.x() < rect.x()) { point.setX(rect.x()); } else if (point.x() > rect.x() + rect.width()) { point.setX(rect.x() + rect.width()); } if (point.y() < rect.y()) { point.setY(rect.y()); } else if (point.y() > rect.y() + rect.height()) { point.setY(rect.y() + rect.height()); } } void paint_line_graph(QPainter &p, QRectF &rect, int points, float *values, double tension, int fill) { double width = rect.width(); double height = rect.height(); double pixelsPerPoint = width / (double) (points - 1); // Calculate cubic control points QVector controlPoints((points - 1) * 2); int cpi = 0; // First control point is equal to first point controlPoints[cpi++] = QPointF(rect.x(), rect.y() + height - values[0] * height); // Calculate control points between data points // Based on ideas from: // http://scaledinnovation.com/analytics/splines/aboutSplines.html for (int i = 0; i < (points - 2); i++) { double x0 = rect.x() + (double) i * pixelsPerPoint; double x1 = rect.x() + (double) (i + 1) * pixelsPerPoint; double x2 = rect.x() + (double) (i + 2) * pixelsPerPoint; double y0 = rect.y() + height - values[i] * height; double y1 = rect.y() + height - values[i + 1] * height; double y2 = rect.y() + height - values[i + 2] * height; double d01 = sqrt(pow(x1 - x0, 2) + pow(y1 - y0, 2)); double d12 = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2)); double fa = tension * d01 / (d01 + d12); double fb = tension * d12 / (d01 + d12); double p1x = x1 - fa * (x2 - x0); double p1y = y1 - fa * (y2 - y0); QPointF c1(p1x, p1y); fix_point(c1, rect); double p2x = x1 + fb * (x2 - x0); double p2y = y1 + fb * (y2 - y0); QPointF c2(p2x, p2y); fix_point(c2, rect); controlPoints[cpi++] = c1; controlPoints[cpi++] = c2; } // Last control point is equal to last point controlPoints[cpi++] = QPointF(rect.x() + width, rect.y() + height - values[points - 1] * height); // Draw a sequence of bezier curves to interpolate between points. QPainterPath curvePath; // Begin at the first data point. curvePath.moveTo(rect.x(), rect.y() + height - values[0] * height); cpi = 0; for (int i = 1; i < points; i++) { QPointF c1 = controlPoints[cpi++]; QPointF c2 = controlPoints[cpi++]; double endX = rect.x() + (double) i * pixelsPerPoint; double endY = rect.y() + height - values[i] * height; QPointF end(endX, endY); curvePath.cubicTo(c1, c2, end); } if (fill) { curvePath.lineTo(rect.x() + width, rect.y() + height); curvePath.lineTo(rect.x(), rect.y() + height); curvePath.closeSubpath(); p.fillPath(curvePath, p.pen().brush()); } else { p.drawPath(curvePath); } } void paint_bar_graph(QPainter &p, QRectF &rect, int points, float *values) { double width = rect.width(); double height = rect.height(); double pixelsPerPoint = width / (double) points; QPointF bottomPoint(rect.x() + pixelsPerPoint / 2, rect.y() + height); QPointF topPoint(rect.x() + pixelsPerPoint / 2, rect.y()); for (int i = 0; i < points; i++) { topPoint.setY(rect.y() + height - values[i] * height); p.drawLine(bottomPoint, topPoint); bottomPoint.setX(bottomPoint.x() + pixelsPerPoint); topPoint.setX(bottomPoint.x()); } } void paint_segment_graph(QPainter &p, const QRectF &rect, int points, const float *values, const QVector &colors, int segments, int segment_gap, int segment_width) { double pixelsPerPoint = rect.width() / (double) points; if (segment_width > pixelsPerPoint) { segment_width = pixelsPerPoint; } double segment_space = pixelsPerPoint - segment_width; double segmentSize = rect.height() / (double) segments; if (segment_gap >= segmentSize) { segment_gap = segmentSize - 1; } segmentSize = (rect.height() - ((double) segment_gap * (double) (segments - 1))) / (double) segments; for (int i = 0; i < points; i++) { QPointF bottomPoint(rect.x() + (segment_space / 2.0) + (pixelsPerPoint * (double) i), rect.y() + rect.height()); QPointF topPoint(bottomPoint.x() + segment_width, bottomPoint.y() - segmentSize); qreal segmentRatio = 1.0 / (qreal) segments; for (int s = 0; s < segments; s++) { int colorIndex = colors.size() - qRound((qreal) s / (qreal) segments * (qreal) colors.size()) - 1; colorIndex = qBound(0, colorIndex, colors.size()); QColor segmentColor = colors[colorIndex]; qreal minSegmentValue = segmentRatio * (qreal) s; qreal maxSegmentValue = segmentRatio * (qreal) (s + 1); if (values[i] < minSegmentValue) { break; } else if (values[i] < maxSegmentValue) { qreal opacity = (values[i] - minSegmentValue) / segmentRatio; segmentColor.setAlphaF(opacity); } p.fillRect(QRectF(topPoint, bottomPoint), segmentColor); bottomPoint.setY(topPoint.y() - segment_gap); topPoint.setY(bottomPoint.y() - segmentSize); } } } mlt-7.38.0/src/modules/qt/graph.h000664 000000 000000 00000004311 15172202314 016516 0ustar00rootroot000000 000000 /* * Copyright (c) 2015-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GRAPH_H #define GRAPH_H #include #include #include #include #include QVector get_graph_colors(mlt_properties filter_properties, mlt_properties frame_properties, int position, int length); void setup_graph_painter(QPainter &p, QRectF &r, mlt_properties filter_properties, mlt_properties frame_properties, int position, int length); void setup_graph_pen(QPainter &p, QRectF &r, mlt_properties filter_properties, mlt_properties frame_properties, double scale, int position, int length); void paint_line_graph(QPainter &p, QRectF &rect, int points, float *values, double tension, int fill); void paint_bar_graph(QPainter &p, QRectF &rect, int points, float *values); void paint_segment_graph(QPainter &p, const QRectF &rect, int points, const float *values, const QVector &colors, int segments, int segment_gap, int segment_width); #endif // GRAPH_H mlt-7.38.0/src/modules/qt/kdenlivegraphics.cpp000664 000000 000000 00000001466 15172202314 021302 0ustar00rootroot000000 000000 #include "kdenlivegraphics.h" KdenliveGraphicsRect::KdenliveGraphicsRect(QRectF rect, const QBrush &brush, const QPen &pen, int cornerRadius) { this->setRect(rect); this->setBrush(brush); this->setPen(pen); this->cornerRadius = cornerRadius; } void KdenliveGraphicsRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { int radius = qMin(cornerRadius, (int) qMin(rect().width(), rect().height()) / 2); painter->setBrush(brush()); painter->setPen(pen()); painter->setRenderHint(QPainter::Antialiasing); painter->drawRoundedRect(rect(), radius, radius); } mlt-7.38.0/src/modules/qt/kdenlivegraphics.h000664 000000 000000 00000001057 15172202314 020743 0ustar00rootroot000000 000000 #pragma once #include #include #include #include class KdenliveGraphicsRect : public QGraphicsRectItem { public: explicit KdenliveGraphicsRect(QRectF rect, const QBrush &brush, const QPen &pen, int cornerRadius); protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; private: int cornerRadius{0}; }; mlt-7.38.0/src/modules/qt/kdenlivetitle_wrapper.cpp000664 000000 000000 00000127520 15172202314 022363 0ustar00rootroot000000 000000 /* * kdenlivetitle_wrapper.cpp -- kdenlivetitle wrapper * Copyright (c) 2009 Marco Gittler * Copyright (c) 2009 Jean-Baptiste Mardelle * Copyright (c) 2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "kdenlivetitle_wrapper.h" #include "kdenlivegraphics.h" #include "typewriter.h" #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QTextCursor); Q_DECLARE_METATYPE(std::shared_ptr); // Private Constants static const double PI = 3.14159265358979323846; class ImageItem : public QGraphicsItem { public: ImageItem(QImage img) { m_img = img; } virtual QRectF boundingRect() const { return QRectF(0, 0, m_img.width(), m_img.height()); } virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget *) { painter->setRenderHint(QPainter::SmoothPixmapTransform, true); painter->drawImage(QPoint(), m_img); } private: QImage m_img; }; void blur(QImage &image, int radius) { int tab[] = {14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2}; int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius - 1]; int r1 = 0; int r2 = image.height() - 1; int c1 = 0; int c2 = image.width() - 1; int bpl = image.bytesPerLine(); int rgba[4]; unsigned char *p; int i1 = 0; int i2 = 3; for (int col = c1; col <= c2; col++) { p = image.scanLine(r1) + col * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p += bpl; for (int j = r1; j < r2; j++, p += bpl) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int row = r1; row <= r2; row++) { p = image.scanLine(row) + c1 * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p += 4; for (int j = c1; j < c2; j++, p += 4) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int col = c1; col <= c2; col++) { p = image.scanLine(r2) + col * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p -= bpl; for (int j = r1; j < r2; j++, p -= bpl) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int row = r1; row <= r2; row++) { p = image.scanLine(row) + c2 * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p -= 4; for (int j = c1; j < c2; j++, p -= 4) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } } class PlainTextItem : public QGraphicsItem { public: PlainTextItem(QString text, QFont font, double width, double height, QBrush brush, QColor outlineColor, double outline, int align, int lineSpacing, int tabWidth) : m_metrics(QFontMetrics(font)) { m_boundingRect = QRectF(0, 0, width, height); m_brush = brush; m_outline = outline; m_pen = QPen(outlineColor); m_pen.setWidthF(outline); m_pen.setJoinStyle(Qt::RoundJoin); m_font = font; m_lineSpacing = lineSpacing + m_metrics.lineSpacing(); m_align = align; m_width = width; m_tabWidth = tabWidth; updateText(text); } void updateText(const QString text) { m_path.clear(); // Calculate line width const QStringList lines = text.split('\n'); double linePos = m_metrics.ascent(); foreach (const QString &line, lines) { QPainterPath linePath; const QStringList tabLines = line.split(QLatin1Char('\t')); if (m_tabWidth > 0 && tabLines.count() > 1) { qreal pos = 0; qreal currentPos = 0; for (const QString &tline : tabLines) { QPainterPath tabPath; if (!tline.isEmpty()) { tabPath.addText(pos, linePos, m_font, tline); linePath.addPath(tabPath); currentPos = pos + tabPath.boundingRect().width(); } else { // Several chained tabs currentPos = pos + m_tabWidth / 2; } int tabsCount = ceil(currentPos / m_tabWidth); pos = tabsCount * m_tabWidth; } } else { linePath.addText(0, linePos, m_font, line); } linePos += m_lineSpacing; if (m_align == Qt::AlignHCenter) { double offset = (m_width - m_metrics.horizontalAdvance(line)) / 2; linePath.translate(offset, 0); } else if (m_align == Qt::AlignRight) { double offset = (m_width - m_metrics.horizontalAdvance(line)); linePath.translate(offset, 0); } m_path.addPath(linePath); } m_path.setFillRule(Qt::WindingFill); if (!m_path.isEmpty()) { int minWidth = m_path.boundingRect().width(); int minHeight = m_lineSpacing * lines.size(); if (m_boundingRect.width() < minWidth) { m_boundingRect.setWidth(minWidth); } if (m_boundingRect.height() < minHeight) { m_boundingRect.setHeight(minHeight); } } } virtual QRectF boundingRect() const { return m_boundingRect; } virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *w) { if (!m_shadow.isNull()) { painter->drawImage(m_shadowOffset, m_shadow); } if (m_outline > 0) { painter->strokePath(m_path.simplified(), m_pen); } painter->fillPath(m_path, m_brush); } void addShadow(QStringList params) { m_params = params; updateShadows(); } void updateShadows() { if (m_params.count() < 5 || m_params.at(0).toInt() == false) { // Invalid or no shadow wanted return; } // Build shadow image QColor shadowColor = QColor(m_params.at(1)); int blurRadius = m_params.at(2).toInt(); int offsetX = m_params.at(3).toInt(); int offsetY = m_params.at(4).toInt(); m_shadow = QImage(m_boundingRect.width() + abs(offsetX) + 4 * blurRadius + 2 * m_outline, m_boundingRect.height() + abs(offsetY) + 4 * blurRadius + 2 * m_outline, QImage::Format_ARGB32_Premultiplied); m_shadow.fill(Qt::transparent); QPainterPath shadowPath = m_path; offsetX -= (2 * blurRadius + m_outline + 1); offsetY -= (2 * blurRadius + m_outline + 1); m_shadowOffset = QPoint(offsetX, offsetY); shadowPath.translate(2 * blurRadius + m_outline + 1, 2 * blurRadius + m_outline + 1); QPainter shadowPainter(&m_shadow); if (m_outline > 0) { QPainterPathStroker strokePath; strokePath.setWidth(m_outline); strokePath.setJoinStyle(Qt::RoundJoin); QPainterPath stroke = strokePath.createStroke(shadowPath); shadowPath.addPath(stroke); } shadowPainter.fillPath(shadowPath, QBrush(shadowColor)); shadowPainter.end(); blur(m_shadow, blurRadius); } private: QRectF m_boundingRect; QImage m_shadow; QPoint m_shadowOffset; QPainterPath m_path; QBrush m_brush; QPen m_pen; QFont m_font; int m_lineSpacing; int m_align; double m_width; int m_tabWidth; QFontMetrics m_metrics; double m_outline; QStringList m_params; }; QRectF stringToRect(const QString &s) { const QStringList l = s.split(','); if (l.size() < 4) return QRectF(); return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()) .normalized(); } QColor stringToColor(const QString &s) { const QStringList l = s.split(','); if (l.size() < 4) return QColor(); return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt()); ; } QTransform stringToTransform(const QString &s) { const QStringList l = s.split(','); if (l.size() < 9) return QTransform(); return QTransform(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(), l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()); } static void qscene_delete(void *data) { QGraphicsScene *scene = (QGraphicsScene *) data; delete scene; scene = NULL; } void loadFromXml(producer_ktitle self, QGraphicsScene *scene, const char *templateXml, const char *templateText) { scene->clear(); mlt_producer producer = &self->parent; self->has_alpha = true; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); QDomDocument doc; QString data = QString::fromUtf8(templateXml); QString replacementText = QString::fromUtf8(templateText); doc.setContent(data); QDomElement title = doc.documentElement(); // Check for invalid title if (title.isNull() || title.tagName() != "kdenlivetitle") return; // Check title locale if (title.hasAttribute("LC_NUMERIC")) { QString locale = title.attribute("LC_NUMERIC"); QLocale::setDefault(QLocale(locale)); } int originalWidth; int originalHeight; if (title.hasAttribute("width")) { originalWidth = title.attribute("width").toInt(); originalHeight = title.attribute("height").toInt(); scene->setSceneRect(0, 0, originalWidth, originalHeight); } else { originalWidth = scene->sceneRect().width(); originalHeight = scene->sceneRect().height(); } if (title.hasAttribute("out")) { mlt_properties_set_position(producer_props, "_animation_out", title.attribute("out").toDouble()); } else { mlt_properties_set_position(producer_props, "_animation_out", mlt_producer_get_out(producer)); } mlt_properties_set_int(producer_props, "meta.media.width", originalWidth); mlt_properties_set_int(producer_props, "meta.media.height", originalHeight); const QDir rootDir(QFileInfo(mlt_properties_get(producer_props, "resource")).absolutePath()); QDomNode node; QDomNodeList items = title.elementsByTagName("item"); int itemId = 1; for (int i = 0; i < items.count(); i++) { QGraphicsItem *gitem = NULL; node = items.item(i); QDomNamedNodeMap nodeAttributes = node.attributes(); int zValue = nodeAttributes.namedItem("z-index").nodeValue().toInt(); if (zValue > -1000) { if (nodeAttributes.namedItem("type").nodeValue() == "QGraphicsTextItem") { QDomNamedNodeMap txtProperties = node.namedItem("content").attributes(); QFont font(txtProperties.namedItem("font").nodeValue()); QDomNode propsNode = txtProperties.namedItem("font-bold"); if (!propsNode.isNull()) { // Old: Bold/Not bold. font.setBold(propsNode.nodeValue().toInt()); } else { // New: Font weight (QFont::) font.setWeight( QFont::Weight(txtProperties.namedItem("font-weight").nodeValue().toInt())); } font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt()); font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt()); int letterSpacing = txtProperties.namedItem("font-spacing").nodeValue().toInt(); if (letterSpacing != 0) { font.setLetterSpacing(QFont::AbsoluteSpacing, letterSpacing); } // Older Kdenlive version did not store pixel size but point size if (txtProperties.namedItem("font-pixel-size").isNull()) { QFont f2; f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt()); font.setPixelSize(QFontInfo(f2).pixelSize()); } else { font.setPixelSize( txtProperties.namedItem("font-pixel-size").nodeValue().toInt()); } if (!txtProperties.namedItem("letter-spacing").isNull()) { font.setLetterSpacing(QFont::AbsoluteSpacing, txtProperties.namedItem("letter-spacing") .nodeValue() .toInt()); } QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue())); QString text = node.namedItem("content").firstChild().nodeValue(); if (!replacementText.isEmpty()) { text = text.replace("%s", replacementText); } QColor outlineColor( stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())); int align = 1; if (txtProperties.namedItem("alignment").isNull() == false) { align = txtProperties.namedItem("alignment").nodeValue().toInt(); } int tabWidth = 0; if (txtProperties.namedItem("tab-width").isNull() == false) { tabWidth = txtProperties.namedItem("tab-width").nodeValue().toInt(); } double boxWidth = 0; double boxHeight = 0; if (txtProperties.namedItem("box-width").isNull()) { // This is an old version title, find out dimensions from QGraphicsTextItem QGraphicsTextItem *txt = scene->addText(text, font); QRectF br = txt->boundingRect(); boxWidth = br.width(); boxHeight = br.height(); scene->removeItem(txt); delete txt; } else { boxWidth = txtProperties.namedItem("box-width").nodeValue().toDouble(); boxHeight = txtProperties.namedItem("box-height").nodeValue().toDouble(); } QBrush brush; if (txtProperties.namedItem("gradient").isNull() == false) { // Calculate gradient QString gradientData = txtProperties.namedItem("gradient").nodeValue(); QStringList values = gradientData.split(";"); if (values.count() < 5) { // invalid gradient, use default values = QStringList({QStringLiteral("#ff0000"), QStringLiteral("#2e0046"), QStringLiteral("0"), QStringLiteral("100"), QStringLiteral("90")}); } QLinearGradient gr; gr.setColorAt(values.at(2).toDouble() / 100, values.at(0)); gr.setColorAt(values.at(3).toDouble() / 100, values.at(1)); double angle = values.at(4).toDouble(); if (angle <= 90) { gr.setStart(0, 0); gr.setFinalStop(boxWidth * cos(angle * PI / 180), boxHeight * sin(angle * PI / 180)); } else { gr.setStart(boxWidth, 0); gr.setFinalStop(boxWidth + boxWidth * cos(angle * PI / 180), boxHeight * sin(angle * PI / 180)); } brush = QBrush(gr); } else { brush = QBrush(col); } if (txtProperties.namedItem("compatibility").isNull()) { // Workaround Qt5 crash in threaded drawing of QGraphicsTextItem, paint by ourselves PlainTextItem *txt = new PlainTextItem( text, font, boxWidth, boxHeight, brush, outlineColor, txtProperties.namedItem("font-outline").nodeValue().toDouble(), align, txtProperties.namedItem("line-spacing").nodeValue().toInt(), tabWidth); if (txtProperties.namedItem("shadow").isNull() == false) { const QStringList values = txtProperties.namedItem("shadow").nodeValue().split(";"); txt->addShadow(values); } if (!txtProperties.namedItem("typewriter").isNull()) { // typewriter effect const QStringList values = txtProperties.namedItem("typewriter").nodeValue().split(";"); int enabled = (static_cast(values.at(0).toInt())); if (enabled && values.count() >= 5) { mlt_properties_set_int(producer_props, "_animated", 1); std::shared_ptr tw(new TypeWriter()); tw->setFrameStep(values.at(1).toInt()); int macro = values.at(2).toInt(); tw->setStepSigma(values.at(3).toInt()); tw->setStepSeed(values.at(4).toInt()); QString pattern; if (macro) { char c = 0; switch (macro) { case 1: c = 'c'; break; case 2: c = 'w'; break; case 3: c = 'l'; break; default: break; } pattern = QStringLiteral(":%1{%2}").arg(c).arg(text); } else { pattern = text; } tw->setPattern(pattern.toStdString()); tw->parse(); tw->printParseResult(); scene->setProperty(QString::number(itemId).toLatin1(), QVariant::fromValue>(tw)); txt->setData(0, itemId); itemId++; } else { txt->setData(0, QVariant()); } } scene->addItem(txt); gitem = txt; } else { QGraphicsTextItem *txt = scene->addText(text, font); gitem = txt; if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) { QTextDocument *doc = txt->document(); // Make sure some that the text item does not request refresh by itself doc->blockSignals(true); QTextCursor cursor(doc); cursor.select(QTextCursor::Document); QTextCharFormat format; format.setTextOutline( QPen(QColor(stringToColor( txtProperties.namedItem("font-outline-color").nodeValue())), txtProperties.namedItem("font-outline").nodeValue().toDouble(), Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); format.setForeground(QBrush(col)); cursor.mergeCharFormat(format); } else { txt->setDefaultTextColor(col); } // Effects if (!txtProperties.namedItem("typewriter").isNull()) { // typewriter effect mlt_properties_set_int(producer_props, "_animated", 1); const QStringList effetData = {"typewriter", text, txtProperties.namedItem("typewriter").nodeValue()}; txt->setData(0, effetData); if (!txtProperties.namedItem("textwidth").isNull()) txt->setData(1, txtProperties.namedItem("textwidth").nodeValue()); } if (txtProperties.namedItem("alignment").isNull() == false) { txt->setTextWidth(txt->boundingRect().width()); QTextOption opt = txt->document()->defaultTextOption(); opt.setAlignment( (Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt()); txt->document()->setDefaultTextOption(opt); } if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) { //txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt()); } if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) { //txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt()); } if (!txtProperties.namedItem("preferred-width").isNull()) { txt->setTextWidth( txtProperties.namedItem("preferred-width").nodeValue().toInt()); } } } else if (nodeAttributes.namedItem("type").nodeValue() == "QGraphicsRectItem" || nodeAttributes.namedItem("type").nodeValue() == "QGraphicsEllipseItem") { QDomNamedNodeMap rectProperties = node.namedItem("content").attributes(); QRectF rect = stringToRect(rectProperties.namedItem("rect").nodeValue()); QString pen_str = rectProperties.namedItem("pencolor").nodeValue(); double penwidth = rectProperties.namedItem("penwidth").nodeValue().toDouble(); double cornerRadius = rectProperties.namedItem("cornerRadius").nodeValue().toDouble(); QBrush brush; if (!rectProperties.namedItem("gradient").isNull()) { // Calculate gradient QString gradientData = rectProperties.namedItem("gradient").nodeValue(); QStringList values = gradientData.split(";"); if (values.count() < 5) { // invalid gradient, use default values = QStringList({QStringLiteral("#ff0000"), QStringLiteral("#2e0046"), QStringLiteral("0"), QStringLiteral("100"), QStringLiteral("90")}); } QLinearGradient gr; gr.setColorAt(values.at(2).toDouble() / 100, values.at(0)); gr.setColorAt(values.at(3).toDouble() / 100, values.at(1)); double angle = values.at(4).toDouble(); if (angle <= 90) { gr.setStart(0, 0); gr.setFinalStop(rect.width() * cos(angle * PI / 180), rect.height() * sin(angle * PI / 180)); } else { gr.setStart(rect.width(), 0); gr.setFinalStop(rect.width() + rect.width() * cos(angle * PI / 180), rect.height() * sin(angle * PI / 180)); } brush = QBrush(gr); } else { brush = QBrush( stringToColor(rectProperties.namedItem("brushcolor").nodeValue())); } QPen pen; if (penwidth == 0) { pen = QPen(Qt::NoPen); } else { pen = QPen(QBrush(stringToColor(pen_str)), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin); } if (nodeAttributes.namedItem("type").nodeValue() == "QGraphicsEllipseItem") { QGraphicsEllipseItem *ellipse = scene->addEllipse(rect, pen, brush); gitem = ellipse; } else { // QGraphicsRectItem KdenliveGraphicsRect *rec = new KdenliveGraphicsRect(rect, brush, pen, cornerRadius); scene->addItem(rec); gitem = rec; } } else if (nodeAttributes.namedItem("type").nodeValue() == "QGraphicsPixmapItem") { const QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); QImage img; if (!base64.isEmpty()) { img.loadFromData(QByteArray::fromBase64(base64.toLatin1())); } else { QString url = node.namedItem("content").attributes().namedItem("url").nodeValue(); if (QFileInfo(url).isRelative()) { url = QDir::cleanPath(rootDir.absoluteFilePath(url)); } img.load(url); } ImageItem *rec = new ImageItem(img); scene->addItem(rec); gitem = rec; } else if (nodeAttributes.namedItem("type").nodeValue() == "QGraphicsSvgItem") { QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); QGraphicsSvgItem *rec = NULL; if (!base64.isEmpty()) { rec = new QGraphicsSvgItem(); QSvgRenderer *renderer = new QSvgRenderer(QByteArray::fromBase64(base64.toLatin1()), rec); rec->setSharedRenderer(renderer); } else { QString url = items.item(i) .namedItem("content") .attributes() .namedItem("url") .nodeValue(); if (QFileInfo(url).isRelative()) { url = QDir::cleanPath(rootDir.absoluteFilePath(url)); } rec = new QGraphicsSvgItem(url); } if (rec) { scene->addItem(rec); gitem = rec; } } } //pos and transform if (gitem) { QPointF p(node.namedItem("position").attributes().namedItem("x").nodeValue().toDouble(), node.namedItem("position").attributes().namedItem("y").nodeValue().toDouble()); gitem->setPos(p); gitem->setTransform(stringToTransform( node.namedItem("position").firstChild().firstChild().nodeValue())); int zValue = nodeAttributes.namedItem("z-index").nodeValue().toInt(); gitem->setZValue(zValue); // effects QDomNode eff = items.item(i).namedItem("effect"); if (!eff.isNull()) { QDomElement e = eff.toElement(); if (e.attribute("type") == "blur") { QGraphicsBlurEffect *blur = new QGraphicsBlurEffect(); blur->setBlurRadius(e.attribute("blurradius").toInt()); gitem->setGraphicsEffect(blur); } else if (e.attribute("type") == "shadow") { QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); shadow->setBlurRadius(e.attribute("blurradius").toInt()); shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt()); gitem->setGraphicsEffect(shadow); } } } } QDomNode n = title.firstChildElement("background"); if (!n.isNull()) { QColor color = QColor(stringToColor(n.attributes().namedItem("color").nodeValue())); self->has_alpha = color.alpha() != 255; if (color.alpha() > 0) { QGraphicsRectItem *rec = scene->addRect(0, 0, scene->width(), scene->height(), QPen(Qt::NoPen), QBrush(color)); rec->setZValue(-1100); } } QString startRect; n = title.firstChildElement("startviewport"); // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport if (!n.isNull() && !n.toElement().hasAttribute("x")) { startRect = n.attributes().namedItem("rect").nodeValue(); } n = title.firstChildElement("endviewport"); // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport if (!n.isNull() && !n.toElement().hasAttribute("x")) { QString rect = n.attributes().namedItem("rect").nodeValue(); if (startRect != rect) { mlt_properties_set(producer_props, "_endrect", rect.toUtf8().data()); } else { mlt_properties_clear(producer_props, "_endrect"); } } if (!startRect.isEmpty()) { mlt_properties_set(producer_props, "_startrect", startRect.toUtf8().data()); } return; } int initTitleProducer(mlt_producer producer) { if (!createQApplicationIfNeeded(MLT_PRODUCER_SERVICE(producer))) { return false; } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (!QMetaType::type("QTextCursor")) { qRegisterMetaType("QTextCursor"); } #else if (!QMetaType::fromType().isRegistered()) { qRegisterMetaType(); } if (!QMetaType::fromType>().isRegistered()) { qRegisterMetaType>(); } #endif return true; } void drawKdenliveTitle(producer_ktitle self, mlt_frame frame, mlt_image_format format, int width, int height, double position, int force_refresh) { // Obtain the producer mlt_producer producer = &self->parent; mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES(frame); pthread_mutex_lock(&self->mutex); // Check if user wants us to reload the image or if we need animation bool animated = mlt_properties_get(producer_props, "_endrect") != NULL; if (mlt_properties_get(producer_props, "_animated") != NULL || force_refresh == 1 || width != self->current_width || height != self->current_height || animated) { if (!animated) { // Cache image only if no animation self->current_image = NULL; mlt_properties_set_data(producer_props, "_cached_image", NULL, 0, NULL, NULL); } mlt_properties_set_int(producer_props, "force_reload", 0); } int image_size = width * height * 4; if (self->current_image == NULL || animated) { // restore QGraphicsScene QGraphicsScene *scene = static_cast( mlt_properties_get_data(producer_props, "qscene", NULL)); self->current_alpha = NULL; if (force_refresh == 1 && scene) { scene = NULL; mlt_properties_set_data(producer_props, "qscene", NULL, 0, NULL, NULL); } if (scene == NULL) { scene = new QGraphicsScene(); scene->setItemIndexMethod(QGraphicsScene::NoIndex); scene->setSceneRect(0, 0, mlt_properties_get_int(properties, "width"), mlt_properties_get_int(properties, "height")); if (mlt_properties_get(producer_props, "resource") && mlt_properties_get(producer_props, "resource")[0] != '\0') { // The title has a resource property, so we read all properties from the resource. // Do not serialize the xmldata loadFromXml(self, scene, mlt_properties_get(producer_props, "_xmldata"), mlt_properties_get(producer_props, "templatetext")); } else { // The title has no resource, all data should be serialized loadFromXml(self, scene, mlt_properties_get(producer_props, "xmldata"), mlt_properties_get(producer_props, "templatetext")); } mlt_properties_set_data(producer_props, "qscene", scene, 0, (mlt_destructor) qscene_delete, NULL); } QRectF start = stringToRect(QString(mlt_properties_get(producer_props, "_startrect"))); QRectF end = stringToRect(QString(mlt_properties_get(producer_props, "_endrect"))); const QRectF source(0, 0, width, height); if (start.isNull()) { start = QRectF(0, 0, mlt_properties_get_int(producer_props, "meta.media.width"), mlt_properties_get_int(producer_props, "meta.media.height")); } // Effects QList items = scene->items(); PlainTextItem *titem = NULL; for (int i = 0; i < items.count(); i++) { titem = dynamic_cast(items.at(i)); if (titem && !titem->data(0).isNull()) { int itemId = titem->data(0).toInt(); std::shared_ptr ptr = scene->property(QString::number(itemId).toLatin1()) .value>(); titem->updateText(ptr->render(position).c_str()); titem->updateShadows(); } } //must be extracted from kdenlive title self->rgba_image = (uint8_t *) mlt_pool_alloc(image_size); // Initialize the QImage with the MLT image because the data formats match. QImage img(self->rgba_image, width, height, QImage::Format_RGBA8888); img.fill(0); QPainter p1; p1.begin(&img); p1.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | QPainter::HighQualityAntialiasing #endif ); //| QPainter::SmoothPixmapTransform ); mlt_position anim_out = mlt_properties_get_position(producer_props, "_animation_out"); if (end.isNull()) { scene->render(&p1, source, start, Qt::IgnoreAspectRatio); } else if (position > anim_out) { scene->render(&p1, source, end, Qt::IgnoreAspectRatio); } else { double percentage = 0; if (position && anim_out) percentage = position / anim_out; QPointF topleft = start.topLeft() + (end.topLeft() - start.topLeft()) * percentage; QPointF bottomRight = start.bottomRight() + (end.bottomRight() - start.bottomRight()) * percentage; const QRectF r1(topleft, bottomRight); scene->render(&p1, source, r1, Qt::IgnoreAspectRatio); if (profile && !profile->progressive) { int line = 0; double percentage_next_filed = (position + 0.5) / anim_out; QPointF topleft_next_field = start.topLeft() + (end.topLeft() - start.topLeft()) * percentage_next_filed; QPointF bottomRight_next_field = start.bottomRight() + (end.bottomRight() - start.bottomRight()) * percentage_next_filed; const QRectF r2(topleft_next_field, bottomRight_next_field); QImage img1(width, height, QImage::Format_ARGB32); img1.fill(0); QPainter p2; p2.begin(&img1); p2.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | QPainter::HighQualityAntialiasing #endif ); scene->render(&p2, source, r2, Qt::IgnoreAspectRatio); p2.end(); int next_field_line = (mlt_properties_get_int(producer_props, "top_field_first") ? 1 : 0); for (line = next_field_line; line < height; line += 2) { memcpy(img.scanLine(line), img1.scanLine(line), img.bytesPerLine()); } } } p1.end(); self->format = mlt_image_rgba; convert_qimage_to_mlt(&img, self->rgba_image, width, height); self->current_image = (uint8_t *) mlt_pool_alloc(image_size); memcpy(self->current_image, self->rgba_image, image_size); mlt_properties_set_data(producer_props, "_cached_buffer", self->rgba_image, image_size, mlt_pool_release, NULL); mlt_properties_set_data(producer_props, "_cached_image", self->current_image, image_size, mlt_pool_release, NULL); self->current_width = width; self->current_height = height; uint8_t *alpha = NULL; if ((alpha = mlt_frame_get_alpha(frame))) { self->current_alpha = (uint8_t *) mlt_pool_alloc(width * height); memcpy(self->current_alpha, alpha, width * height); mlt_properties_set_data(producer_props, "_cached_alpha", self->current_alpha, width * height, mlt_pool_release, NULL); } } // Convert image to requested format if (format != mlt_image_none && format != mlt_image_movit && format != self->format) { uint8_t *buffer = NULL; if (self->format != mlt_image_rgba) { // Image buffer was previously converted, revert to original rgba buffer self->current_image = (uint8_t *) mlt_pool_alloc(image_size); memcpy(self->current_image, self->rgba_image, image_size); mlt_properties_set_data(producer_props, "_cached_image", self->current_image, image_size, mlt_pool_release, NULL); self->format = mlt_image_rgba; } // First, set the image so it can be converted when we get it mlt_frame_replace_image(frame, self->current_image, self->format, width, height); mlt_frame_set_image(frame, self->current_image, image_size, NULL); self->format = format; // get_image will do the format conversion mlt_frame_get_image(frame, &buffer, &format, &width, &height, 0); // cache copies of the image and alpha buffers if (buffer) { image_size = mlt_image_format_size(format, width, height, NULL); self->current_image = (uint8_t *) mlt_pool_alloc(image_size); memcpy(self->current_image, buffer, image_size); mlt_properties_set_data(producer_props, "_cached_image", self->current_image, image_size, mlt_pool_release, NULL); } if ((buffer = mlt_frame_get_alpha(frame))) { self->current_alpha = (uint8_t *) mlt_pool_alloc(width * height); memcpy(self->current_alpha, buffer, width * height); mlt_properties_set_data(producer_props, "_cached_alpha", self->current_alpha, width * height, mlt_pool_release, NULL); } } pthread_mutex_unlock(&self->mutex); mlt_properties_set_int(properties, "width", self->current_width); mlt_properties_set_int(properties, "height", self->current_height); } mlt-7.38.0/src/modules/qt/kdenlivetitle_wrapper.h000664 000000 000000 00000003124 15172202314 022021 0ustar00rootroot000000 000000 /* * kdenlivetitle_wrapper.h -- kdenlivetitle wrapper * Copyright (c) 2009 Marco Gittler * Copyright (c) 2009 Jean-Baptiste Mardelle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #ifdef __cplusplus extern "C" { #endif #include #include #include struct producer_ktitle_s { struct mlt_producer_s parent; uint8_t *rgba_image; uint8_t *current_image; uint8_t *current_alpha; mlt_image_format format; int current_width; int current_height; int has_alpha; pthread_mutex_t mutex; }; typedef struct producer_ktitle_s *producer_ktitle; extern void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, mlt_image_format format, int, int, double, int); extern int initTitleProducer(mlt_producer producer); #ifdef __cplusplus } #endif mlt-7.38.0/src/modules/qt/producer_kdenlivetitle.c000664 000000 000000 00000020115 15172202314 022156 0ustar00rootroot000000 000000 /* * producer_kdenlivetitle.c -- kdenlive producer * Copyright (c) 2009 Marco Gittler * Copyright (c) 2009 Jean-Baptiste Mardelle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "kdenlivetitle_wrapper.h" #include #include #include void read_xml(mlt_properties properties) { const char *resource = mlt_properties_get(properties, "resource"); FILE *f = mlt_fopen(resource, "r"); if (f != NULL) { int size = 0; long lSize; if (fseek(f, 0, SEEK_END) < 0) goto error; lSize = ftell(f); if (lSize <= 0) goto error; rewind(f); char *infile = (char *) mlt_pool_alloc(lSize + 1); if (infile) { size = fread(infile, 1, lSize, f); if (size) { infile[size] = '\0'; mlt_properties_set(properties, "_xmldata", infile); } mlt_pool_release(infile); } error: fclose(f); } } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; /* Obtain properties of frame */ mlt_properties properties = MLT_FRAME_PROPERTIES(frame); /* Obtain the producer for this frame */ producer_ktitle self = mlt_properties_get_data(properties, "producer_kdenlivetitle", NULL); /* Obtain properties of producer */ mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); if (mlt_properties_get_int(properties, "rescale_width") > 0) *width = mlt_properties_get_int(properties, "rescale_width"); if (mlt_properties_get_int(properties, "rescale_height") > 0) *height = mlt_properties_get_int(properties, "rescale_height"); mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); /* Allocate the image */ if (mlt_properties_get_int(producer_props, "force_reload")) { if (mlt_properties_get_int(producer_props, "force_reload") > 1) read_xml(producer_props); mlt_properties_set_int(producer_props, "force_reload", 0); drawKdenliveTitle(self, frame, *format, *width, *height, mlt_frame_original_position(frame), 1); } else { drawKdenliveTitle(self, frame, *format, *width, *height, mlt_frame_original_position(frame), 0); } // Get width and height (may have changed during the refresh) *width = mlt_properties_get_int(properties, "width"); *height = mlt_properties_get_int(properties, "height"); *format = self->format; if (self->current_image) { // Clone the image and the alpha int image_size = mlt_image_format_size(self->format, self->current_width, self->current_height, NULL); uint8_t *image_copy = mlt_pool_alloc(image_size); memcpy(image_copy, self->current_image, mlt_image_format_size(self->format, self->current_width, self->current_height, NULL)); // Now update properties so we free the copy after mlt_frame_set_image(frame, image_copy, image_size, mlt_pool_release); // We're going to pass the copy on *buffer = image_copy; // Clone the alpha channel if (self->current_alpha) { image_copy = mlt_pool_alloc(self->current_width * self->current_height); memcpy(image_copy, self->current_alpha, self->current_width * self->current_height); mlt_frame_set_alpha(frame, image_copy, self->current_width * self->current_height, mlt_pool_release); } } else { error = 1; } mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); return error; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Get the real structure for this producer producer_ktitle this = producer->child; /* Generate a frame */ *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL) { /* Obtain properties of frame and producer */ mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); /* Obtain properties of producer */ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); /* Set the producer on the frame properties */ mlt_properties_set_data(properties, "producer_kdenlivetitle", this, 0, NULL, NULL); /* Update timecode on the frame we're creating */ mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Set producer-specific frame properties mlt_properties_set_int(properties, "progressive", 1); // Inform framework that this producer creates rgba frames by default // TODO: read the producer's xml on opening so we know if we have RGB or RGBA data mlt_properties_set_int(properties, "format", mlt_image_rgba); double force_ratio = mlt_properties_get_double(producer_props, "force_aspect_ratio"); if (force_ratio > 0.0) mlt_properties_set_double(properties, "aspect_ratio", force_ratio); else mlt_properties_set_double(properties, "aspect_ratio", mlt_properties_get_double(producer_props, "aspect_ratio")); /* Push the get_image method */ mlt_frame_push_get_image(*frame, producer_get_image); } /* Calculate the next timecode */ mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer producer) { producer_ktitle self = producer->child; producer->close = NULL; mlt_service_cache_purge(MLT_PRODUCER_SERVICE(producer)); mlt_producer_close(producer); free(self); } mlt_producer producer_kdenlivetitle_init(mlt_profile profile, mlt_service_type type, const char *id, char *filename) { /* Create a new producer object */ producer_ktitle self = calloc(1, sizeof(struct producer_ktitle_s)); if (self != NULL && mlt_producer_init(&self->parent, self) == 0) { mlt_producer producer = &self->parent; /* Get the properties interface */ mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); /* Callback registration */ producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; mlt_properties_set(properties, "resource", filename); mlt_properties_set_int(properties, "meta.media.progressive", 1); mlt_properties_set_int(properties, "aspect_ratio", 1); mlt_properties_set_int(properties, "seekable", 1); if (!initTitleProducer(producer)) { mlt_producer_close(producer); return NULL; } read_xml(properties); return producer; } free(self); return NULL; } mlt-7.38.0/src/modules/qt/producer_kdenlivetitle.yml000664 000000 000000 00000000356 15172202314 022542 0ustar00rootroot000000 000000 schema_version: 0.1 type: producer identifier: kdenlivetitle title: Kdenlive Titler version: 7 copyright: Marco Gittler, Jean-Baptiste Mardelle creator: Marco Gittler, Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video mlt-7.38.0/src/modules/qt/producer_qimage.c000664 000000 000000 00000035445 15172202314 020572 0ustar00rootroot000000 000000 /* * producer_image.c -- a Qt/QImage based producer for MLT * Copyright (C) 2006-2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qimage_wrapper.h" #include #include #include #include #include #include #include #include #ifndef _MSC_VER #include #endif static void load_filenames(producer_qimage self, mlt_properties producer_properties); static int producer_get_frame(mlt_producer parent, mlt_frame_ptr frame, int index); static void producer_close(mlt_producer parent); static void refresh_length(mlt_properties properties, producer_qimage self) { if (self->count > mlt_properties_get_int(properties, "length") || mlt_properties_get_int(properties, "autolength")) { int ttl = mlt_properties_get_int(properties, "ttl"); mlt_position length = self->count * ttl; mlt_properties_set_position(properties, "length", length); mlt_properties_set_position(properties, "out", length - 1); } } static void on_property_changed(mlt_service owner, mlt_producer producer, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "ttl")) refresh_length(MLT_PRODUCER_PROPERTIES(producer), producer->child); } mlt_producer producer_qimage_init(mlt_profile profile, mlt_service_type type, const char *id, char *filename) { producer_qimage self = calloc(1, sizeof(struct producer_qimage_s)); if (self != NULL && mlt_producer_init(&self->parent, self) == 0) { mlt_producer producer = &self->parent; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES(&self->parent); // Initialize KDE image plugins and get supported animation frame count self->count = init_qimage(producer, filename); if (!self->count) { mlt_producer_close(producer); free(self); return NULL; } // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; // Set the default properties mlt_properties_set(properties, "resource", filename); mlt_properties_set_int(properties, "ttl", self->count > 1 ? 1 : 25); mlt_properties_set_int(properties, "aspect_ratio", 1); mlt_properties_set_int(properties, "meta.media.progressive", 1); mlt_properties_set_int(properties, "seekable", 1); // Validate the resource if (self->count == 1 && filename) { load_filenames(self, properties); } else { refresh_length(properties, self); } if (self->count) { mlt_frame frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (frame) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties_set_data(frame_properties, "producer_qimage", self, 0, NULL, NULL); mlt_frame_set_position(frame, mlt_producer_position(producer)); int enable_caching = self->count == 1; refresh_qimage(self, frame, enable_caching); if (enable_caching) { mlt_cache_item_close(self->qimage_cache); } mlt_frame_close(frame); } } if (self->current_width == 0) { producer_close(producer); producer = NULL; } else { mlt_events_listen(properties, self, "property-changed", (mlt_listener) on_property_changed); } return producer; } free(self); return NULL; } static int load_svg(producer_qimage self, mlt_properties properties, const char *filename) { int result = 0; // Read xml string if (strstr(filename, " start && (end[0] == 'd' || end[0] == 'i' || end[0] == 'u')) { int n = end - start; char *s = calloc(1, n + 1); strncpy(s, start, n); mlt_properties_set(properties, "begin", s); free(s); s = calloc(1, strlen(filename) + 2); strncpy(s, filename, start - filename); sprintf(s + (start - filename), ".%d%s", n, end); result = load_sequence_sprintf(self, properties, s); free(s); } } return result; } static int load_sequence_querystring(producer_qimage self, mlt_properties properties, const char *filename) { int result = 0; // Obtain filenames with pattern and begin value in query string if (strchr(filename, '%') && strchr(filename, '?')) { // Split filename into pattern and query string char *s = strdup(filename); char *querystring = strrchr(s, '?'); *querystring++ = '\0'; if (strstr(filename, "begin=")) mlt_properties_set(properties, "begin", strstr(querystring, "begin=") + 6); else if (strstr(filename, "begin:")) mlt_properties_set(properties, "begin", strstr(querystring, "begin:") + 6); // Coerce to an int value so serialization does not have any extra query string cruft mlt_properties_set_int(properties, "begin", mlt_properties_get_int(properties, "begin")); result = load_sequence_sprintf(self, properties, s); free(s); } return result; } static void load_filenames(producer_qimage self, mlt_properties properties) { char *filename = mlt_properties_get(properties, "resource"); self->filenames = mlt_properties_new(); if (!load_svg(self, properties, filename) && !load_sequence_querystring(self, properties, filename) && !load_sequence_sprintf(self, properties, filename) && !load_sequence_deprecated(self, properties, filename) && !load_folder(self, filename)) { mlt_properties_set(self->filenames, "0", filename); } self->count = mlt_properties_count(self->filenames); refresh_length(properties, self); } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(frame); producer_qimage self = mlt_properties_get_data(properties, "producer_qimage", NULL); mlt_producer producer = &self->parent; // Use the width and height suggested by the rescale filter because we can do our own scaling. if (mlt_properties_get_int(properties, "rescale_width") > 0) *width = mlt_properties_get_int(properties, "rescale_width"); if (mlt_properties_get_int(properties, "rescale_height") > 0) *height = mlt_properties_get_int(properties, "rescale_height"); mlt_service_lock(MLT_PRODUCER_SERVICE(&self->parent)); int enable_caching = (self->count <= 1 || mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(producer), "ttl") > 1); // Refresh the image if (enable_caching) { self->qimage_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "qimage.qimage"); self->qimage = mlt_cache_item_data(self->qimage_cache, NULL); self->image_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "qimage.image"); self->current_image = mlt_cache_item_data(self->image_cache, NULL); self->alpha_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "qimage.alpha"); self->current_alpha = mlt_cache_item_data(self->alpha_cache, &self->alpha_size); const char *dst_color_range = mlt_properties_get(properties, "consumer.color_range"); if (mlt_image_full_range(dst_color_range)) mlt_properties_set_int(properties, "full_range", 1); } refresh_image(self, frame, *format, *width, *height, enable_caching); // Get width and height (may have changed during the refresh) *width = mlt_properties_get_int(properties, "width"); *height = mlt_properties_get_int(properties, "height"); *format = self->format; // NB: Cloning is necessary with this producer (due to processing of images ahead of use) // The fault is not in the design of mlt, but in the implementation of the qimage producer... if (self->current_image) { int image_size = mlt_image_format_size(self->format, self->current_width, self->current_height, NULL); if (enable_caching) { // Clone the image and the alpha uint8_t *image_copy = mlt_pool_alloc(image_size); memcpy(image_copy, self->current_image, image_size); // Now update properties so we free the copy after mlt_frame_set_image(frame, image_copy, image_size, mlt_pool_release); // We're going to pass the copy on *buffer = image_copy; mlt_log_debug(MLT_PRODUCER_SERVICE(&self->parent), "%dx%d (%s)\n", self->current_width, self->current_height, mlt_image_format_name(*format)); // Clone the alpha channel if (self->current_alpha) { if (!self->alpha_size) self->alpha_size = self->current_width * self->current_height; uint8_t *alpha_copy = mlt_pool_alloc(self->alpha_size); memcpy(alpha_copy, self->current_alpha, self->alpha_size); mlt_frame_set_alpha(frame, alpha_copy, self->alpha_size, mlt_pool_release); } } else { // For image sequences with ttl = 1 we recreate self->current_image on each frame, no need to clone mlt_frame_set_image(frame, self->current_image, image_size, mlt_pool_release); *buffer = self->current_image; if (self->current_alpha) { if (!self->alpha_size) self->alpha_size = self->current_width * self->current_height; mlt_frame_set_alpha(frame, self->current_alpha, self->alpha_size, mlt_pool_release); } } } else { error = 1; } if (enable_caching) { // Release references and locks mlt_cache_item_close(self->qimage_cache); mlt_cache_item_close(self->image_cache); mlt_cache_item_close(self->alpha_cache); } mlt_service_unlock(MLT_PRODUCER_SERVICE(&self->parent)); return error; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Get the real structure for this producer producer_qimage self = producer->child; // Fetch the producers properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Cache miss if (!self->filenames && !self->count && mlt_properties_get(producer_properties, "resource")) { self->count = init_qimage(producer, mlt_properties_get(producer_properties, "resource")); if (!self->count) { return 1; } if (self->count == 1) { load_filenames(self, producer_properties); } else { refresh_length(producer_properties, self); } } // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL && self->count > 0) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); // Set the producer on the frame properties mlt_properties_set_data(properties, "producer_qimage", self, 0, NULL, NULL); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Refresh the image if (self->count == 1 || mlt_properties_get_int(producer_properties, "ttl") > 1) { self->qimage_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "qimage.qimage"); self->qimage = mlt_cache_item_data(self->qimage_cache, NULL); refresh_qimage(self, *frame, 1); mlt_cache_item_close(self->qimage_cache); } // Set producer-specific frame properties mlt_properties_set_int(properties, "progressive", 1); mlt_properties_set_int(properties, "format", mlt_properties_get_int(producer_properties, "format")); double force_ratio = mlt_properties_get_double(producer_properties, "force_aspect_ratio"); if (force_ratio > 0.0) mlt_properties_set_double(properties, "aspect_ratio", force_ratio); else mlt_properties_set_double(properties, "aspect_ratio", mlt_properties_get_double(producer_properties, "aspect_ratio")); // Push the get_image method mlt_frame_push_get_image(*frame, producer_get_image); } // Calculate the next timecode mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer parent) { producer_qimage self = parent->child; parent->close = NULL; mlt_service_cache_purge(MLT_PRODUCER_SERVICE(parent)); mlt_producer_close(parent); mlt_properties_close(self->filenames); free(self); } mlt-7.38.0/src/modules/qt/producer_qimage.yml000664 000000 000000 00000006222 15172202314 021140 0ustar00rootroot000000 000000 schema_version: 0.3 type: producer identifier: qimage title: Qt QImage version: 2 creator: Charles Yates license: GPLv2 language: en tags: - Video description: > A still graphics to video generator using Qt QImage notes: > QImage has builtin scaling. It will rescale the originally rendered title to whatever the consumer requests. Therefore, it will lose its aspect ratio if so requested, and it is up to the consumer to request a proper width and height that maintains the image aspect. parameters: - identifier: resource title: File type: string description: > The name of a graphics file loadable by Qt. If "%" in filename, the filename is used with sprintf to generate a filename from a counter for multi-file/flipbook animation. The file sequence ends when numeric discontinuity exceeds 100. If the file sequence does not begin within the count of 100 you can pass the begin property like a query string parameter, for example: anim-%04d.png?begin=1000. If filename contains "/.all.", suffix with an extension to load all pictures with matching extension from a directory. If filename contains the string " Reload the file instead of using its cached image. This property automatically resets itself once it has been set 1 and processed. minimum: 0 maximum: 1 mutable: yes - identifier: disable_exif title: Disable auto-rotation type: boolean default: 0 widget: checkbox - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio mutable: yes - identifier: autolength title: Automatically compute length description: Whether to automatically compute the length and out point for an image sequence. type: boolean default: 0 widget: checkbox mlt-7.38.0/src/modules/qt/producer_qtext.cpp000664 000000 000000 00000053051 15172202314 021025 0ustar00rootroot000000 000000 /* * producer_qtext.c -- text generating producer * Copyright (C) 2013-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #include #else #include #endif static void close_qimg(void *qimg) { delete static_cast(qimg); } static void close_qpath(void *qpath) { delete static_cast(qpath); } static void copy_qimage_to_mlt_image(QImage *qImg, uint8_t *mImg) { int height = qImg->height(); int width = qImg->width(); int y = 0; // convert qimage to mlt y = height + 1; while (--y) { QRgb *src = (QRgb *) qImg->scanLine(height - y); int x = width + 1; while (--x) { *mImg++ = qRed(*src); *mImg++ = qGreen(*src); *mImg++ = qBlue(*src); *mImg++ = qAlpha(*src); src++; } } } static void copy_image_to_alpha(uint8_t *image, uint8_t *alpha, int width, int height) { int len = width * height; // Extract the alpha mask from the RGBA image using Duff's Device uint8_t *s = image + 3; // start on the alpha component uint8_t *d = alpha; int n = (len + 7) / 8; switch (len % 8) { case 0: do { *d++ = *s; s += 4; case 7: *d++ = *s; s += 4; case 6: *d++ = *s; s += 4; case 5: *d++ = *s; s += 4; case 4: *d++ = *s; s += 4; case 3: *d++ = *s; s += 4; case 2: *d++ = *s; s += 4; case 1: *d++ = *s; s += 4; } while (--n > 0); } } /** Check if the qpath needs to be regenerated. Return true if regeneration is required. */ static bool check_qpath(mlt_properties producer_properties) { #define MAX_SIG 1000 bool result = false; char *new_path_sig = new char[MAX_SIG]; // Generate a signature that represents the current properties snprintf(new_path_sig, MAX_SIG, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", mlt_properties_get(producer_properties, "text"), mlt_properties_get(producer_properties, "fgcolour"), mlt_properties_get(producer_properties, "bgcolour"), mlt_properties_get(producer_properties, "olcolour"), mlt_properties_get(producer_properties, "outline"), mlt_properties_get(producer_properties, "underline"), mlt_properties_get(producer_properties, "strikethrough"), mlt_properties_get(producer_properties, "align"), mlt_properties_get(producer_properties, "pad"), mlt_properties_get(producer_properties, "family"), mlt_properties_get(producer_properties, "size"), mlt_properties_get(producer_properties, "style"), mlt_properties_get(producer_properties, "weight"), mlt_properties_get(producer_properties, "encoding")); new_path_sig[MAX_SIG - 1] = '\0'; // Check if the properties have changed by comparing this signature with the // last one. char *last_path_sig = mlt_properties_get(producer_properties, "_path_sig"); if (!last_path_sig || strcmp(new_path_sig, last_path_sig)) { mlt_properties_set(producer_properties, "_path_sig", new_path_sig); result = true; } delete[] new_path_sig; return result; } static void generate_qpath(mlt_properties producer_properties) { QPainterPath *qPath = static_cast( mlt_properties_get_data(producer_properties, "_qpath", NULL)); int outline = mlt_properties_get_int(producer_properties, "outline"); char *align = mlt_properties_get(producer_properties, "align"); char *style = mlt_properties_get(producer_properties, "style"); char *text = mlt_properties_get(producer_properties, "text"); char *encoding = mlt_properties_get(producer_properties, "encoding"); int pad = mlt_properties_get_int(producer_properties, "pad"); int offset = pad + (outline / 2); int width = 0; int height = 0; // Make the path empty *qPath = QPainterPath(); qPath->setFillRule(Qt::WindingFill); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Get the strings to display QTextCodec *codec = QTextCodec::codecForName(encoding); QTextDecoder *decoder = codec->makeDecoder(); QString s = decoder->toUnicode(text); delete decoder; #else auto decoder = QStringDecoder(encoding); QString s = decoder(text); #endif QStringList lines = s.split("\n"); // Configure the font QFont font; font.setPixelSize(mlt_properties_get_int(producer_properties, "size")); font.setFamily(mlt_properties_get(producer_properties, "family")); font.setWeight(QFont::Weight((mlt_properties_get_int(producer_properties, "weight") / 10) - 1)); switch (style[0]) { case 'i': case 'I': font.setStyle(QFont::StyleItalic); break; default: font.setStyleName(style); break; } // Apply text decoration properties font.setUnderline(mlt_properties_get_int(producer_properties, "underline")); font.setStrikeOut(mlt_properties_get_int(producer_properties, "strikethrough")); QFontMetrics fm(font); // Determine the text rectangle size height = fm.lineSpacing() * lines.size(); for (int i = 0; i < lines.size(); ++i) { const QString line = lines[i]; int line_width = fm.horizontalAdvance(line); int bearing = (line.size() > 0) ? fm.leftBearing(line.at(0)) : 0; if (bearing < 0) line_width -= bearing; bearing = (line.size() > 0) ? fm.rightBearing(line.at(line.size() - 1)) : 0; if (bearing < 0) line_width -= bearing; if (line_width > width) width = line_width; } // Lay out the text in the path int x = 0; int y = fm.ascent() + offset; for (int i = 0; i < lines.size(); ++i) { QString line = lines.at(i); x = offset; int line_width = fm.horizontalAdvance(line); int bearing = (line.size() > 0) ? fm.leftBearing(line.at(0)) : 0; if (bearing < 0) { line_width -= bearing; x -= bearing; } bearing = (line.size() > 0) ? fm.rightBearing(line.at(line.size() - 1)) : 0; if (bearing < 0) line_width -= bearing; switch (align[0]) { default: case 'l': case 'L': break; case 'c': case 'C': x += (width - line_width) / 2; break; case 'r': case 'R': x += width - line_width; break; } qPath->addText(x, y, font, line); y += fm.lineSpacing(); } // Account for outline and pad width += offset * 2; height += offset * 2; // Sanity check if (width == 0) width = 1; height += 2; // I found some fonts whose descenders get cut off. mlt_properties_set_int(producer_properties, "meta.media.width", width); mlt_properties_set_int(producer_properties, "meta.media.height", height); } static bool check_qimage(mlt_properties frame_properties) { mlt_producer producer = static_cast( mlt_properties_get_data(frame_properties, "_producer_qtext", NULL)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); QImage *qImg = static_cast( mlt_properties_get_data(producer_properties, "_qimg", NULL)); QSize target_size(mlt_properties_get_int(frame_properties, "rescale_width"), mlt_properties_get_int(frame_properties, "rescale_height")); QSize native_size(mlt_properties_get_int(frame_properties, "meta.media.width"), mlt_properties_get_int(frame_properties, "meta.media.height")); // Check if the last image signature is different from the path signature // for this frame. char *last_img_sig = mlt_properties_get(producer_properties, "_img_sig"); char *path_sig = mlt_properties_get(frame_properties, "_path_sig"); if (!last_img_sig || strcmp(path_sig, last_img_sig)) { mlt_properties_set(producer_properties, "_img_sig", path_sig); return true; } // Check if the last image size matches the requested image size QSize output_size = target_size; if (output_size.isEmpty()) { output_size = native_size; } if (output_size != qImg->size()) { return true; } return false; } static void generate_qimage(mlt_properties frame_properties) { mlt_producer producer = static_cast( mlt_properties_get_data(frame_properties, "_producer_qtext", NULL)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); QImage *qImg = static_cast( mlt_properties_get_data(producer_properties, "_qimg", NULL)); QSize target_size(mlt_properties_get_int(frame_properties, "rescale_width"), mlt_properties_get_int(frame_properties, "rescale_height")); QSize native_size(mlt_properties_get_int(frame_properties, "meta.media.width"), mlt_properties_get_int(frame_properties, "meta.media.height")); QPainterPath *qPath = static_cast( mlt_properties_get_data(frame_properties, "_qpath", NULL)); mlt_color bg_color = mlt_properties_get_color(frame_properties, "_bgcolour"); bg_color = mlt_color_convert_trc(bg_color, mlt_properties_get(frame_properties, "color_trc")); mlt_color fg_color = mlt_properties_get_color(frame_properties, "_fgcolour"); fg_color = mlt_color_convert_trc(fg_color, mlt_properties_get(frame_properties, "color_trc")); mlt_color ol_color = mlt_properties_get_color(frame_properties, "_olcolour"); ol_color = mlt_color_convert_trc(ol_color, mlt_properties_get(frame_properties, "color_trc")); int outline = mlt_properties_get_int(frame_properties, "_outline"); qreal sx = 1.0; qreal sy = 1.0; // Create a new image and set up scaling if (!target_size.isEmpty() && target_size != native_size) { *qImg = QImage(target_size, QImage::Format_ARGB32); sx = (qreal) target_size.width() / (qreal) native_size.width(); sy = (qreal) target_size.height() / (qreal) native_size.height(); } else { *qImg = QImage(native_size, QImage::Format_ARGB32); } qImg->fill(QColor(bg_color.r, bg_color.g, bg_color.b, bg_color.a).rgba()); // Draw the text QPainter painter(qImg); // Scale the painter rather than the image for better looking results. painter.scale(sx, sy); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | QPainter::HighQualityAntialiasing #endif ); // Draw the outline first, and then draw the fill on top of it. // This avoids the outline encroaching on the text fill. // Draw the outline if requested if (outline) { QPen pen; pen.setWidth(outline); pen.setColor(QColor(ol_color.r, ol_color.g, ol_color.b, ol_color.a)); painter.setPen(pen); painter.setBrush(Qt::NoBrush); // No brush needed for outline painter.drawPath(*qPath); } // Fill the text area QBrush brush(QColor(fg_color.r, fg_color.g, fg_color.b, fg_color.a)); painter.setBrush(brush); painter.setPen(Qt::NoPen); // No pen needed for fill painter.drawPath(*qPath); } static int producer_get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_producer producer = static_cast( mlt_properties_get_data(frame_properties, "_producer_qtext", NULL)); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); int img_size = 0; int alpha_size = 0; uint8_t *alpha = NULL; QImage *qImg = static_cast( mlt_properties_get_data(producer_properties, "_qimg", NULL)); mlt_service_lock(MLT_PRODUCER_SERVICE(producer)); // Regenerate the qimage if necessary if (check_qimage(frame_properties) == true) { generate_qimage(frame_properties); } *format = mlt_image_rgba; *width = qImg->width(); *height = qImg->height(); // Allocate and fill the image buffer img_size = mlt_image_format_size(*format, *width, *height, NULL); *buffer = static_cast(mlt_pool_alloc(img_size)); copy_qimage_to_mlt_image(qImg, *buffer); mlt_service_unlock(MLT_PRODUCER_SERVICE(producer)); // Allocate and fill the alpha buffer alpha_size = *width * *height; alpha = static_cast(mlt_pool_alloc(alpha_size)); copy_image_to_alpha(*buffer, alpha, *width, *height); // Update the frame mlt_frame_set_image(frame, *buffer, img_size, mlt_pool_release); mlt_frame_set_alpha(frame, alpha, alpha_size, mlt_pool_release); mlt_properties_set_int(frame_properties, "width", *width); mlt_properties_set_int(frame_properties, "height", *height); return 0; } static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) { // Generate a frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); if (*frame != NULL) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Regenerate the QPainterPath if necessary if (check_qpath(producer_properties)) { generate_qpath(producer_properties); } // Give the frame a copy of the painter path QPainterPath *prodPath = static_cast( mlt_properties_get_data(producer_properties, "_qpath", NULL)); QPainterPath *framePath = new QPainterPath(*prodPath); mlt_properties_set_data(frame_properties, "_qpath", static_cast(framePath), 0, close_qpath, NULL); // Pass properties to the frame that will be needed to render the path mlt_properties_set(frame_properties, "_path_sig", mlt_properties_get(producer_properties, "_path_sig")); mlt_properties_set(frame_properties, "_bgcolour", mlt_properties_get(producer_properties, "bgcolour")); mlt_properties_set(frame_properties, "_fgcolour", mlt_properties_get(producer_properties, "fgcolour")); mlt_properties_set(frame_properties, "_olcolour", mlt_properties_get(producer_properties, "olcolour")); mlt_properties_set(frame_properties, "_outline", mlt_properties_get(producer_properties, "outline")); mlt_properties_set_data(frame_properties, "_producer_qtext", static_cast(producer), 0, NULL, NULL); // Inform framework that this producer creates rgba frames by default mlt_properties_set_int(frame_properties, "format", mlt_image_rgba); // Set frame properties mlt_properties_set_int(frame_properties, "progressive", 1); double force_ratio = mlt_properties_get_double(producer_properties, "force_aspect_ratio"); if (force_ratio > 0.0) mlt_properties_set_double(frame_properties, "aspect_ratio", force_ratio); else mlt_properties_set_double(frame_properties, "aspect_ratio", 1.0); // Update time code on the frame mlt_frame_set_position(*frame, mlt_producer_position(producer)); // Configure callbacks mlt_frame_push_get_image(*frame, producer_get_image); } // Calculate the next time code mlt_producer_prepare_next(producer); return 0; } static void producer_close(mlt_producer producer) { producer->close = NULL; mlt_producer_close(producer); free(producer); } /** Initialize. */ extern "C" { mlt_producer producer_qtext_init(mlt_profile profile, mlt_service_type type, const char *id, char *filename) { // Create a new producer object mlt_producer producer = mlt_producer_new(profile); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); // Initialize the producer if (producer) { if (!createQApplicationIfNeeded(MLT_PRODUCER_SERVICE(producer))) { mlt_producer_close(producer); return NULL; } mlt_properties_set(producer_properties, "text", ""); mlt_properties_set(producer_properties, "fgcolour", "0xffffffff"); mlt_properties_set(producer_properties, "bgcolour", "0x00000000"); mlt_properties_set(producer_properties, "olcolour", "0x00000000"); mlt_properties_set(producer_properties, "outline", "0"); mlt_properties_set(producer_properties, "underline", "0"); mlt_properties_set(producer_properties, "strikethrough", "0"); mlt_properties_set(producer_properties, "align", "left"); mlt_properties_set(producer_properties, "pad", "0"); mlt_properties_set(producer_properties, "family", "Sans"); mlt_properties_set(producer_properties, "size", "48"); mlt_properties_set(producer_properties, "style", "normal"); mlt_properties_set(producer_properties, "weight", "400"); mlt_properties_set(producer_properties, "encoding", "UTF-8"); mlt_properties_set_int(producer_properties, "meta.media.progressive", 1); // Parse the filename argument if (filename == NULL || !strcmp(filename, "") || strstr(filename, "")) { } else if (filename[0] == '+' || strstr(filename, "/+")) { char *copy = strdup(filename + 1); char *tmp = copy; if (strstr(tmp, "/+")) tmp = strstr(tmp, "/+") + 2; if (strrchr(tmp, '.')) (*strrchr(tmp, '.')) = '\0'; while (strchr(tmp, '~')) (*strchr(tmp, '~')) = '\n'; mlt_properties_set(producer_properties, "text", tmp); mlt_properties_set(producer_properties, "resource", filename); free(copy); } else { mlt_properties_set(producer_properties, "resource", filename); FILE *f = mlt_fopen(filename, "r"); if (f != NULL) { char line[81]; char *tmp = NULL; size_t size = 0; line[80] = '\0'; while (fgets(line, 80, f)) { size += strlen(line) + 1; if (tmp) { // Use a temporary pointer to avoid losing the original buffer on realloc failure char *newtmp = (char *) realloc(tmp, size); if (newtmp) { tmp = newtmp; strcat(tmp, line); } else { // Allocation failed: keep existing content and stop appending break; } } else { tmp = strdup(line); } } fclose(f); if (tmp && tmp[strlen(tmp) - 1] == '\n') tmp[strlen(tmp) - 1] = '\0'; if (tmp) mlt_properties_set(producer_properties, "text", tmp); free(tmp); } } // Create QT objects to be reused. mlt_properties_set_data(producer_properties, "_qimg", static_cast(new QImage()), 0, close_qimg, NULL); mlt_properties_set_data(producer_properties, "_qpath", static_cast(new QPainterPath()), 0, close_qpath, NULL); // Callback registration producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; } return producer; } } mlt-7.38.0/src/modules/qt/producer_qtext.yml000664 000000 000000 00000012252 15172202314 021042 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: qtext title: Qt Titler version: 4 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Video description: > A title generator that uses the Qt framework to render text. notes: > qtext accepts a file name with at ".txt" extension. If the filename begins with "+" the qtext producer interprets the filename as text. This is a shortcut to embed titles in melt commands. For MLT XML, it is recommended that you embed the title text in the "text" property value. qtext has builtin scaling. It will rescale the originally rendered title to whatever the consumer requests. Therefore, it will lose its aspect ratio if so requested, and it is up to the consumer to request a proper width and height that maintains the image aspect. parameters: - identifier: resource title: File type: string description: | A text file containing text to be rendered. The text file contents initialize the value of the "text" parameter. readonly: no argument: yes mutable: no widget: fileopen - identifier: text title: Text type: string description: | A text string to be rendered. readonly: no argument: yes mutable: yes widget: textbox - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner - identifier: underline title: Underline type: boolean description: Apply underline decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: strikethrough title: Strikethrough type: boolean description: Apply strikethrough decoration to the text. default: 0 readonly: no mutable: yes widget: checkbox - identifier: align title: Paragraph alignment type: string description: > left, center, right readonly: no default: left mutable: yes widget: combo - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: family title: Font family type: string description: > The font typeface. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font, for example, "italic" or "narrow" depending upon family default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: encoding title: Encoding type: string description: > The text encoding type of the "text" parameter. default: UTF-8 readonly: no mutable: yes widget: combo - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio mutable: yes - identifier: meta.media.width title: Real width type: integer description: The original, unscaled width of the rendered image. readonly: yes - identifier: meta.media.height title: Real height type: integer description: The original, unscaled height of the rendered image. readonly: yes - identifier: width title: Width type: integer description: The last requested scaled image width. readonly: yes - identifier: height title: Height type: integer description: The last requested scaled image height. readonly: yes mlt-7.38.0/src/modules/qt/qimage_wrapper.cpp000664 000000 000000 00000040234 15172202314 020757 0ustar00rootroot000000 000000 /* * qimage_wrapper.cpp -- a Qt/QImage based producer for MLT * Copyright (C) 2006-2023 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qimage_wrapper.h" #include "common.h" #ifdef USE_KDE4 #include #endif #include #include #include #include #include #include #include #include #include #ifdef USE_EXIF #include #include #endif #include #include #include #ifndef _MSC_VER #include #endif extern "C" { #include #include #ifdef USE_KDE4 static KComponentData *instance = 0L; #endif static void qimage_delete(void *data) { QImage *image = (QImage *) data; delete image; image = NULL; #if defined(USE_KDE4) delete instance; instance = 0L; #endif } /// Returns frame count or 0 on error int init_qimage(mlt_producer producer, const char *filename) { if (!createQApplicationIfNeeded(MLT_PRODUCER_SERVICE(producer))) { return 0; } QImageReader reader; reader.setDecideFormatFromContent(true); reader.setFileName(filename); if (reader.canRead() && reader.imageCount() > 1) { return reader.format() == "webp" ? reader.imageCount() : 0; } #ifdef USE_KDE4 if (!instance) { instance = new KComponentData("qimage_prod"); } #endif return 1; } int refresh_qimage(producer_qimage self, mlt_frame frame, int enable_caching) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); // Check if user wants us to reload the image if (mlt_properties_get_int(producer_props, "force_reload")) { self->qimage = NULL; self->current_image = NULL; mlt_properties_set_int(producer_props, "force_reload", 0); } // Get the original position of this frame mlt_position position = mlt_frame_original_position(frame); position += mlt_producer_get_in(producer); // Image index int image_idx = (int) floor((double) position / mlt_properties_get_int(producer_props, "ttl")) % self->count; int disable_exif = mlt_properties_get_int(producer_props, "disable_exif"); if (image_idx != self->qimage_idx) { self->qimage = NULL; } if (!self->qimage || mlt_properties_get_int(producer_props, "_disable_exif") != disable_exif) { self->current_image = NULL; QImageReader reader; QImage *qimage; // Use Qt's orientation detection reader.setAutoTransform(!disable_exif); QString filename = QString::fromUtf8(mlt_properties_get_value(self->filenames, image_idx)); if (filename.isEmpty()) { filename = QString::fromUtf8(mlt_properties_get(producer_props, "resource")); } // First try to detect the file type based on the content // in case the file extension is incorrect. reader.setDecideFormatFromContent(true); reader.setFileName(filename); if (reader.imageCount() > 1) { QMovie movie(filename); movie.setCacheMode(QMovie::CacheAll); movie.jumpToFrame(image_idx); qimage = new QImage(movie.currentImage()); } else { qimage = new QImage(reader.read()); } if (qimage->isNull()) { mlt_log_info(MLT_PRODUCER_SERVICE(&self->parent), "QImage retry: %d - %s\n", reader.error(), reader.errorString().toLatin1().data()); delete qimage; // If detection fails, try a more comprehensive detection including file extension reader.setDecideFormatFromContent(false); reader.setFileName(filename); qimage = new QImage(reader.read()); if (qimage->isNull()) { mlt_log_info(MLT_PRODUCER_SERVICE(&self->parent), "QImage fail: %d - %s\n", reader.error(), reader.errorString().toLatin1().data()); } } self->qimage = qimage; if (!qimage->isNull()) { if (enable_caching) { // Register qimage for destruction and reuse mlt_cache_item_close(self->qimage_cache); mlt_service_cache_put(MLT_PRODUCER_SERVICE(producer), "qimage.qimage", qimage, 0, (mlt_destructor) qimage_delete); self->qimage_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "qimage.qimage"); } else { // Ensure original image data will be deleted mlt_properties_set_data(producer_props, "qimage.qimage", qimage, 0, (mlt_destructor) qimage_delete, NULL); } self->qimage_idx = image_idx; // Store the width/height of the qimage self->current_width = qimage->width(); self->current_height = qimage->height(); mlt_events_block(producer_props, NULL); mlt_properties_set_int(producer_props, "format", qimage->hasAlphaChannel() ? mlt_image_rgba : mlt_image_rgb); mlt_properties_set_int(producer_props, "meta.media.width", self->current_width); mlt_properties_set_int(producer_props, "meta.media.height", self->current_height); mlt_properties_set_int(producer_props, "_disable_exif", disable_exif); mlt_events_unblock(producer_props, NULL); } else { delete qimage; self->qimage = NULL; } } // Set width/height of frame mlt_properties_set_int(properties, "width", self->current_width); mlt_properties_set_int(properties, "height", self->current_height); return image_idx; } void refresh_image(producer_qimage self, mlt_frame frame, mlt_image_format format, int width, int height, int enable_caching) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_producer producer = &self->parent; // Get index and qimage int image_idx = refresh_qimage(self, frame, enable_caching); // optimization for subsequent iterations on single picture if (!enable_caching || image_idx != self->image_idx || width != self->current_width || height != self->current_height) self->current_image = NULL; // If we have a qimage and need a new scaled image if (self->qimage && (!self->current_image || (format != mlt_image_none && format != mlt_image_movit && format != self->format))) { QString interps = mlt_properties_get(properties, "consumer.rescale"); bool interp = (interps != "nearest") && (interps != "none"); QImage *qimage = static_cast(self->qimage); int has_alpha = qimage->hasAlphaChannel(); QImage::Format qimageFormat = has_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32; // Note - the original qimage is already safe and ready for destruction if (enable_caching && qimage->format() != qimageFormat) { QImage temp = qimage->convertToFormat(qimageFormat); qimage = new QImage(temp); self->qimage = qimage; mlt_cache_item_close(self->qimage_cache); mlt_service_cache_put(MLT_PRODUCER_SERVICE(producer), "qimage.qimage", qimage, 0, (mlt_destructor) qimage_delete); self->qimage_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "qimage.qimage"); } QImage scaled = interp ? qimage->scaled(QSize(width, height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) : qimage->scaled(QSize(width, height)); // Store width and height self->current_width = width; self->current_height = height; // Allocate/define image self->current_alpha = NULL; self->alpha_size = 0; // Convert scaled image to target format (it might be premultiplied after scaling). scaled = scaled.convertToFormat(qimageFormat); // Copy the image int image_size; if (has_alpha) { self->format = mlt_image_rgba; scaled = scaled.convertToFormat(QImage::Format_RGBA8888); image_size = mlt_image_format_size(self->format, width, height, NULL); self->current_image = (uint8_t *) mlt_pool_alloc(image_size); memcpy(self->current_image, scaled.constBits(), scaled.sizeInBytes()); } else { self->format = mlt_image_rgb; scaled = scaled.convertToFormat(QImage::Format_RGB888); image_size = mlt_image_format_size(self->format, width, height, NULL); self->current_image = (uint8_t *) mlt_pool_alloc(image_size); for (int y = 0; y < height; y++) { QRgb *values = reinterpret_cast(scaled.scanLine(y)); memcpy(&self->current_image[3 * y * width], values, 3 * width); } } // Convert image to requested format if (format != mlt_image_none && format != mlt_image_movit && format != self->format && enable_caching) { uint8_t *buffer = NULL; // First, set the image so it can be converted when we get it mlt_frame_replace_image(frame, self->current_image, self->format, width, height); mlt_frame_set_image(frame, self->current_image, image_size, mlt_pool_release); // get_image will do the format conversion mlt_frame_get_image(frame, &buffer, &format, &width, &height, 0); // cache copies of the image and alpha buffers if (buffer) { self->current_width = width; self->current_height = height; self->format = format; image_size = mlt_image_format_size(format, width, height, NULL); self->current_image = (uint8_t *) mlt_pool_alloc(image_size); memcpy(self->current_image, buffer, image_size); } if ((buffer = (uint8_t *) mlt_frame_get_alpha_size(frame, &self->alpha_size))) { if (!self->alpha_size) self->alpha_size = self->current_width * self->current_height; self->current_alpha = (uint8_t *) mlt_pool_alloc(self->alpha_size); memcpy(self->current_alpha, buffer, self->alpha_size); } } self->image_idx = image_idx; if (enable_caching) { // Update the cache mlt_cache_item_close(self->image_cache); mlt_service_cache_put(MLT_PRODUCER_SERVICE(producer), "qimage.image", self->current_image, image_size, mlt_pool_release); self->image_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "qimage.image"); mlt_cache_item_close(self->alpha_cache); self->alpha_cache = NULL; if (self->current_alpha) { mlt_service_cache_put(MLT_PRODUCER_SERVICE(producer), "qimage.alpha", self->current_alpha, self->alpha_size, mlt_pool_release); self->alpha_cache = mlt_service_cache_get(MLT_PRODUCER_SERVICE(producer), "qimage.alpha"); } } } // Set width/height of frame mlt_properties_set_int(properties, "width", self->current_width); mlt_properties_set_int(properties, "height", self->current_height); } extern void make_tempfile(producer_qimage self, const char *xml) { // Generate a temporary file for the svg QTemporaryFile tempFile("mlt.XXXXXX"); tempFile.setAutoRemove(false); if (tempFile.open()) { // Write the svg into the temp file QByteArray fullname = tempFile.fileName().toUtf8(); // Strip leading crap while (xml[0] != '<') xml++; qint64 remaining_bytes = strlen(xml); while (remaining_bytes > 0) remaining_bytes -= tempFile.write(xml + strlen(xml) - remaining_bytes, remaining_bytes); tempFile.close(); mlt_properties_set(self->filenames, "0", fullname.data()); mlt_properties_set_data(MLT_PRODUCER_PROPERTIES(&self->parent), "__temporary_file__", fullname.data(), 0, (mlt_destructor) unlink, NULL); } } int load_sequence_sprintf(producer_qimage self, mlt_properties properties, const char *filename) { int result = 0; // Obtain filenames with pattern if (filename && strchr(filename, '%')) { // handle picture sequences int i = mlt_properties_get_int(properties, "begin"); int keyvalue = 0; for (int gap = 0; gap < 100;) { QString full = QString::asprintf(filename, i++); if (full == filename) { break; } if (QFile::exists(full)) { QString key = QString::asprintf("%d", keyvalue++); mlt_properties_set(self->filenames, key.toLatin1().constData(), full.toUtf8().constData()); gap = 0; } else { gap++; } } if (mlt_properties_count(self->filenames) > 0) { mlt_properties_set_int(properties, "ttl", 1); result = 1; } } return result; } int load_folder(producer_qimage self, const char *filename) { int result = 0; // Obtain filenames within folder if (strstr(filename, "/.all.") != NULL) { mlt_properties filename_property = self->filenames; QFileInfo info(filename); QDir dir = info.absoluteDir(); QStringList filters = {QStringLiteral("*.%1").arg(info.suffix())}; QStringList files = dir.entryList(filters, QDir::Files, QDir::Name); int key; for (auto &f : files) { key = mlt_properties_count(filename_property); mlt_properties_set_string(filename_property, QString::number(key).toLatin1(), dir.absoluteFilePath(f).toUtf8().constData()); } result = 1; } return result; } } // extern "C" mlt-7.38.0/src/modules/qt/qimage_wrapper.h000664 000000 000000 00000004010 15172202314 020414 0ustar00rootroot000000 000000 /* * qimage_wrapper.h -- a QT/QImage based producer for MLT * Copyright (C) 2006-2021 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MLT_QIMAGE_WRAPPER #define MLT_QIMAGE_WRAPPER #include #include #ifdef __cplusplus extern "C" { #endif struct producer_qimage_s { struct mlt_producer_s parent; mlt_properties filenames; int count; int image_idx; int qimage_idx; uint8_t *current_image; uint8_t *current_alpha; int current_width; int current_height; int alpha_size; mlt_cache_item image_cache; mlt_cache_item alpha_cache; mlt_cache_item qimage_cache; void *qimage; mlt_image_format format; }; typedef struct producer_qimage_s *producer_qimage; extern int refresh_qimage(producer_qimage self, mlt_frame frame, int enable_caching); extern void refresh_image( producer_qimage, mlt_frame, mlt_image_format, int width, int height, int enable_caching); extern void make_tempfile(producer_qimage, const char *xml); extern int init_qimage(mlt_producer producer, const char *filename); extern int load_sequence_sprintf(producer_qimage self, mlt_properties properties, const char *filename); extern int load_folder(producer_qimage self, const char *filename); #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/qt/transition_qtblend.cpp000664 000000 000000 00000034530 15172202314 021661 0ustar00rootroot000000 000000 /* * transition_qtblend.cpp -- Qt composite transition * Copyright (c) 2016-2025 Jean-Baptiste Mardelle * Copyright (c) 2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include static int format_is_rgba(mlt_image_format format) { return format == mlt_image_rgba || format == mlt_image_rgba64; } static int get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_frame b_frame = mlt_frame_pop_frame(a_frame); mlt_properties b_properties = MLT_FRAME_PROPERTIES(b_frame); mlt_properties properties = MLT_FRAME_PROPERTIES(a_frame); mlt_transition transition = MLT_TRANSITION(mlt_frame_pop_service(a_frame)); mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES(transition); uint8_t *b_image = NULL; // hasAlpha indicates whether the source material has an alpha channel bool hasAlpha = format_is_rgba(*format); double opacity = 1.0; QTransform transform; QPainter::CompositionMode mode = (QPainter::CompositionMode) mlt_properties_get_int(transition_properties, "compositing"); if (mlt_properties_exists(b_properties, "qtblend.mode")) { // Override from qtblend_mode filter mode = (QPainter::CompositionMode) mlt_properties_get_int(b_properties, "qtblend.mode"); } // Determine length mlt_position length = mlt_transition_get_length(transition); // Get current position mlt_position position = mlt_transition_get_position(transition, a_frame); // Obtain the normalized width and height from the a_frame mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(transition)); int b_width = mlt_properties_get_int(b_properties, "meta.media.width"); int b_height = mlt_properties_get_int(b_properties, "meta.media.height"); int normalized_width = profile->width; int normalized_height = profile->height; // reference rect mlt_rect rect = {0, 0, (double) normalized_width, (double) normalized_height, 1.0}; bool distort = mlt_properties_get_int(transition_properties, "distort"); double consumer_ar = mlt_profile_sar(profile); // Check the producer's native format before fetching image mlt_image_format sourceFormat = (mlt_image_format) mlt_properties_get_int(b_properties, "format"); if (format_is_rgba(sourceFormat) || sourceFormat == mlt_image_rgb) { hasAlpha = format_is_rgba(sourceFormat); if (*format != mlt_image_rgba64) *format = mlt_image_rgba; } if (b_height == 0) { b_width = *width; b_height = *height; } double b_ar = mlt_frame_get_aspect_ratio(b_frame); double b_dar = b_ar * b_width / b_height; double geometry_dar = consumer_ar * normalized_width / normalized_height; double transformScale = 1.; // forceAlpha is true if some operation makes it mandatory to perform the alpha compositing, like padding or scaling bool forceAlpha = b_dar != geometry_dar; if (forceAlpha) { // Ensure we ask for an image with same dar as consumer b_width = geometry_dar * b_height; } if (!distort && (b_height < *height || b_width < *width)) { // Source image is smaller than profile, request full frame if (b_dar > geometry_dar) { transformScale = b_dar / geometry_dar; } else { transformScale = geometry_dar / b_dar; } b_width = *width; b_height = *height; } if (mlt_properties_get(transition_properties, "rect")) { rect = mlt_properties_anim_get_rect(transition_properties, "rect", position, length); if (::strchr(mlt_properties_get(transition_properties, "rect"), '%')) { // We have percentage values, scale to frame size rect.x *= normalized_width; rect.y *= normalized_height; rect.w *= normalized_width; rect.h *= normalized_height; } } else { // Optimization, request profile sized image b_width = normalized_width; b_height = normalized_height; } int request_width = *width; int request_height = *height; double scalex = mlt_profile_scale_width(profile, *width); double scaley = mlt_profile_scale_height(profile, *height); // Adjust to preview scaling if (scalex != 1.0) { // We are using consumer scaling, fetch a lower resolution image too rect.x *= scalex; rect.w *= scalex; if (scalex < 1.) { b_width *= scalex; } } if (scaley != 1.0) { rect.y *= scaley; rect.h *= scaley; if (scaley < 1.) { b_height *= scaley; } } transform.translate(rect.x, rect.y); opacity = rect.o; if (!forceAlpha && (opacity < 1 || rect.x != 0 || rect.y != 0 || (rect.x + rect.w != *width) || (rect.y + rect.h != *height))) { // we will process operations on top frame, so also process b_frame forceAlpha = true; } // Ensure we don't request an image with a 0 width or height b_width = qMax(1, b_width); b_height = qMax(1, b_height); /*} else { b_height = *height; b_width = *width; }*/ if (mlt_frame_get_aspect_ratio(b_frame) == 0) { mlt_frame_set_aspect_ratio(b_frame, consumer_ar); } if (mlt_properties_get(transition_properties, "rotation")) { double angle = mlt_properties_anim_get_double(transition_properties, "rotation", position, length); if (angle != 0.0) { if (mlt_properties_get_int(transition_properties, "rotate_center")) { transform.translate(rect.w / 2.0, rect.h / 2.0); transform.rotate(angle); transform.translate(-rect.w / 2.0, -rect.h / 2.0); } else { transform.rotate(angle); } forceAlpha = true; } } // This is not a field-aware transform. mlt_properties_set_int(b_properties, "consumer.progressive", 1); // Check profile dar vs image dar image if (!forceAlpha && rect.w == -1 && b_dar != mlt_profile_dar(profile)) { // Activate transparency if the clips don't have the same aspect ratio forceAlpha = true; } if (!forceAlpha && (mode != 0 || b_width < *width || b_height < *height)) { forceAlpha = true; } // Check if we have transparency bool imageFetched = false; if (!forceAlpha) { if (!hasAlpha || format_is_rgba(*format)) { // fetch image in native format error = mlt_frame_get_image(b_frame, &b_image, format, &b_width, &b_height, 0); imageFetched = true; if (!hasAlpha && (format_is_rgba(*format) || mlt_frame_get_alpha(b_frame))) { hasAlpha = true; } if (hasAlpha && format_is_rgba(*format)) { struct mlt_image_s bimg; mlt_image_set_values(&bimg, b_image, *format, b_width, b_height); hasAlpha = !mlt_image_is_opaque(&bimg); } } if (!hasAlpha) { // Pass all required frame properties mlt_properties_pass_list(properties, b_properties, "progressive,distort,colorspace,full_range,force_full_luma," "top_field_first,color_trc"); // Prepare output image if (b_frame->convert_image && (b_width != request_width || b_height != request_height)) { mlt_properties_set_int(b_properties, "convert_image_width", request_width); mlt_properties_set_int(b_properties, "convert_image_height", request_height); b_frame->convert_image(b_frame, &b_image, format, *format); *width = request_width; *height = request_height; } else { *width = b_width; *height = b_height; } *image = b_image; return 0; } } if (!imageFetched) { if (*format != mlt_image_rgba64) *format = mlt_image_rgba; error = mlt_frame_get_image(b_frame, &b_image, format, &b_width, &b_height, 0); } if (b_frame->convert_image && !format_is_rgba(*format)) { b_frame->convert_image(b_frame, &b_image, format, mlt_image_rgba); } if (*format != mlt_image_rgba64) *format = mlt_image_rgba; bool scaled = false; QImage scaledSource; // We don't do subpixel smoothing for nearest neighbour interpolation // so people can use that to upscale pixel art and keep the hard edges. char *interps = mlt_properties_get(properties, "consumer.rescale"); bool hqPainting = interps && strcmp(interps, "nearest") && strcmp(interps, "neighbor"); // convert top mlt image to qimage QImage topImg; convert_mlt_to_qimage(b_image, &topImg, b_width, b_height, *format); if (distort) { if (b_width != 0 && b_height != 0) { if (rect.w < b_width || rect.h < b_height) { scaled = true; scaledSource = topImg.scaled(qRound(rect.w), qRound(rect.h), Qt::IgnoreAspectRatio, hqPainting ? Qt::SmoothTransformation : Qt::FastTransformation); } else { transform.scale(rect.w / b_width, rect.h / b_height); } } } else if (rect.w > 0 && rect.h > 0) { double resize_dar = rect.w * consumer_ar / rect.h; double scale; if (b_dar > resize_dar) { scale = rect.w / b_width; if (b_dar < geometry_dar) { scale *= transformScale; } } else { scale = rect.h / b_height; if (b_dar > geometry_dar) { scale *= transformScale; } } if (scale < 1.) { // Use higher quality QImage scaling scaled = true; const QSize finalSize(qRound(topImg.width() * scale), qRound(topImg.height() * scale)); // Center image in rect transform.translate((rect.w - finalSize.width()) / 2.0, (rect.h - finalSize.height()) / 2.0); // Scale scaledSource = topImg.scaled(finalSize, Qt::IgnoreAspectRatio, hqPainting ? Qt::SmoothTransformation : Qt::FastTransformation); } else { // Center image in rect transform.translate((rect.w - (b_width * scale)) / 2.0, (rect.h - (b_height * scale)) / 2.0); if (scale > 1.) { // Use QPainter scaling transform.scale(scale, scale); } } } // Get bottom frame uint8_t *a_image = NULL; error = mlt_frame_get_image(a_frame, &a_image, format, width, height, 0); if (error) { return error; } // Prepare output image int image_size = mlt_image_format_size(*format, *width, *height, NULL); *image = (uint8_t *) mlt_pool_alloc(image_size); // Copy bottom frame in output memcpy(*image, a_image, image_size); // convert bottom mlt image to qimage QImage bottomImg; convert_mlt_to_qimage(*image, &bottomImg, *width, *height, *format); // setup Qt drawing QPainter painter(&bottomImg); painter.setCompositionMode(mode); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, hqPainting); painter.setTransform(transform); painter.setOpacity(opacity); // Composite top frame if (scaled) { painter.drawImage(0, 0, scaledSource); } else { painter.drawImage(0, 0, topImg); } // finish Qt drawing painter.end(); convert_qimage_to_mlt(&bottomImg, *image, *width, *height); mlt_frame_set_image(a_frame, *image, image_size, mlt_pool_release); // Remove potentially large image on the B frame. mlt_frame_set_image(b_frame, NULL, 0, NULL); return error; } static mlt_frame process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { mlt_frame_push_service(a_frame, transition); mlt_frame_push_frame(a_frame, b_frame); mlt_frame_push_get_image(a_frame, get_image); return a_frame; } extern "C" { mlt_transition transition_qtblend_init(mlt_profile profile, mlt_service_type type, const char *id, void *arg) { mlt_transition transition = mlt_transition_new(); if (transition) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); if (!createQApplicationIfNeeded(MLT_TRANSITION_SERVICE(transition))) { mlt_transition_close(transition); return NULL; } transition->process = process; mlt_properties_set_int(properties, "_transition_type", 1); // video only mlt_properties_set(properties, "rect", (char *) arg); mlt_properties_set_int(properties, "compositing", 0); mlt_properties_set_int(properties, "distort", 0); mlt_properties_set_int(properties, "rotate_center", 0); } return transition; } } // extern "C" mlt-7.38.0/src/modules/qt/transition_qtblend.yml000664 000000 000000 00000003627 15172202314 021703 0ustar00rootroot000000 000000 schema_version: 7.0 type: transition identifier: qtblend title: Composite and transform version: 4 copyright: Meltytech, LLC creator: Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video description: > A transition allowing compositing and transform. parameters: - identifier: rect title: Rectangle type: rect description: > Keyframable rectangle specification. mutable: yes argument: yes - identifier: distort title: Ignore aspect ratio description: > Determines whether the image aspect ratio will be distorted while scaling to completely fill the geometry rectangle. type: boolean default: 0 mutable: yes widget: checkbox - identifier: compositing title: Composition mode description: > The Porter-Duff operation or SVG 1.2 blend mode to use. type: integer mutable: yes values: - 0 (source over) - 1 (destination over) - 2 (clear) - 3 (source) - 4 (destination) - 5 (source in) - 6 (destination in) - 7 (source out) - 8 (destination out) - 9 (source atop) - 10 (destination atop) - 11 (xor) - 12 (plus) - 13 (multiply) - 14 (screen) - 15 (overlay) - 16 (darken) - 17 (lighten) - 18 (color dodge) - 19 (color burn) - 20 (hard light) - 21 (soft light) - 22 (difference) - 23 (exclusion) default: 0 minimum: 0 maximum: 23 widget: combo - identifier: rotation title: Rotation angle description: > Angle for rotation. type: float default: 1 minimum: 0 maximum: 360 mutable: yes unit: degrees - identifier: rotate_center title: Rotate from center type: integer description: Process the rotation from center if set, otherwise from top left corner minimum: 0 maximum: 1 mutable: yes widget: checkbox mlt-7.38.0/src/modules/qt/transition_vqm.cpp000664 000000 000000 00000020450 15172202314 021027 0ustar00rootroot000000 000000 /* * transition_vqm.c -- video quality measurement * Copyright (c) 2012-2026 Meltytech, LLC * Core psnr and ssim routines based on code from * qsnr (C) 2010 E. Oriani, ema fastwebnet it * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see */ #include "common.h" #include #include #include #include #include #include #include #include #include #include static double calc_psnr(const uint8_t *a, const uint8_t *b, int size, int bpp) { double mse = 0.0; int n = size + 1; while (--n) { int diff = *a - *b; mse += diff * diff; a += bpp; b += bpp; } return 10.0 * log10(255.0 * 255.0 / (mse == 0 ? 1e-10 : mse / size)); } static double calc_ssim( const uint8_t *a, const uint8_t *b, int width, int height, int window_size, int bpp) { int windows_x = width / window_size; int windows_y = height / window_size; double avg = 0.0; if (!windows_x || !windows_y) return 0.0; // for each window for (int y = 0; y < windows_y; ++y) for (int x = 0; x < windows_x; ++x) { int base_offset = x * window_size + y * window_size * width; double ref_acc = 0.0, ref_acc_2 = 0.0, cmp_acc = 0.0, cmp_acc_2 = 0.0, ref_cmp_acc = 0.0; // accumulate the pixel values for this window for (int j = 0; j < window_size; ++j) for (int i = 0; i < window_size; ++i) { uint8_t c_a = a[bpp * (base_offset + j * width + i)]; uint8_t c_b = b[bpp * (base_offset + j * width + i)]; ref_acc += c_a; ref_acc_2 += c_a * c_a; cmp_acc += c_b; cmp_acc_2 += c_b * c_b; ref_cmp_acc += c_a * c_b; } // compute the SSIM for this window // http://en.wikipedia.org/wiki/SSIM // http://en.wikipedia.org/wiki/Variance // http://en.wikipedia.org/wiki/Covariance double n_samples = window_size * window_size, ref_avg = ref_acc / n_samples, ref_var = ref_acc_2 / n_samples - ref_avg * ref_avg, cmp_avg = cmp_acc / n_samples, cmp_var = cmp_acc_2 / n_samples - cmp_avg * cmp_avg, ref_cmp_cov = ref_cmp_acc / n_samples - ref_avg * cmp_avg, c1 = 6.5025, // (0.01*255.0)^2 c2 = 58.5225, // (0.03*255)^2 ssim_num = (2.0 * ref_avg * cmp_avg + c1) * (2.0 * ref_cmp_cov + c2), ssim_den = (ref_avg * ref_avg + cmp_avg * cmp_avg + c1) * (ref_var + cmp_var + c2); // accumulate the SSIM avg += ssim_num / ssim_den; } // return the average SSIM return avg / windows_x / windows_y; } static int get_image(mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_frame b_frame = mlt_frame_pop_frame(a_frame); mlt_properties properties = MLT_FRAME_PROPERTIES(a_frame); mlt_transition transition = MLT_TRANSITION(mlt_frame_pop_service(a_frame)); uint8_t *b_image; int window_size = mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(transition), "window_size"); double psnr[3], ssim[3]; *format = mlt_image_yuv422; mlt_frame_get_image(b_frame, &b_image, format, width, height, writable); mlt_frame_get_image(a_frame, image, format, width, height, writable); psnr[0] = calc_psnr(*image, b_image, *width * *height, 2); psnr[1] = calc_psnr(*image + 1, b_image + 1, *width * *height / 2, 4); psnr[2] = calc_psnr(*image + 3, b_image + 3, *width * *height / 2, 4); ssim[0] = calc_ssim(*image, b_image, *width, *height, window_size, 2); ssim[1] = calc_ssim(*image + 1, b_image + 1, *width / 2, *height, window_size, 4); ssim[2] = calc_ssim(*image + 3, b_image + 3, *width / 2, *height, window_size, 4); mlt_properties_set_double(properties, "meta.vqm.psnr.y", psnr[0]); mlt_properties_set_double(properties, "meta.vqm.psnr.cb", psnr[1]); mlt_properties_set_double(properties, "meta.vqm.psnr.cr", psnr[2]); mlt_properties_set_double(properties, "meta.vqm.ssim.y", ssim[0]); mlt_properties_set_double(properties, "meta.vqm.ssim.cb", ssim[1]); mlt_properties_set_double(properties, "meta.vqm.ssim.cr", ssim[2]); printf("%05d %05.2f %05.2f %05.2f %5.3f %5.3f %5.3f\n", mlt_frame_get_position(a_frame), psnr[0], psnr[1], psnr[2], ssim[0], ssim[1], ssim[2]); // copy the B frame to the bottom of the A frame for comparison window_size = mlt_image_format_size(*format, *width, *height, NULL) / 2; memcpy(*image + window_size, b_image + window_size, window_size); if (!mlt_properties_get_int(MLT_TRANSITION_PROPERTIES(transition), "render")) return 0; // get RGBA image for Qt drawing *format = mlt_image_rgba; mlt_frame_get_image(a_frame, image, format, width, height, 1); // convert mlt image to qimage QImage img; convert_mlt_to_qimage(*image, &img, *width, *height, *format); // setup Qt drawing QPainter painter(&img); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | QPainter::HighQualityAntialiasing #endif ); // draw some stuff with Qt QFont font; font.setBold(true); font.setPointSize(30 * *height / 1080); painter.setPen(Qt::black); painter.drawLine(0, *height / 2 + 1, *width, *height / 2); painter.setPen(Qt::white); painter.drawLine(0, *height / 2 - 1, *width, *height / 2); painter.setFont(font); auto s = QString::asprintf("Frame: %05d\nPSNR: %05.2f (Y) %05.2f (Cb) %05.2f (Cr)\nSSIM: " "%5.3f (Y) %5.3f (Cb) %5.3f (Cr)", mlt_frame_get_position(a_frame), psnr[0], psnr[1], psnr[2], ssim[0], ssim[1], ssim[2]); painter.setPen(Qt::black); painter.drawText(52, *height * 8 / 10 + 2, *width, *height, 0, s); painter.setPen(Qt::white); painter.drawText(50, *height * 8 / 10, *width, *height, 0, s); // finish Qt drawing painter.end(); convert_qimage_to_mlt(&img, *image, *width, *height); return 0; } static mlt_frame process(mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame) { mlt_frame_push_service(a_frame, transition); mlt_frame_push_frame(a_frame, b_frame); mlt_frame_push_get_image(a_frame, get_image); return a_frame; } extern "C" { mlt_transition transition_vqm_init(mlt_profile profile, mlt_service_type type, const char *id, void *arg) { mlt_transition transition = mlt_transition_new(); if (transition) { mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); if (!createQApplicationIfNeeded(MLT_TRANSITION_SERVICE(transition))) { mlt_transition_close(transition); return NULL; } transition->process = process; mlt_properties_set_int(properties, "_transition_type", 1); // video only mlt_properties_set_int(properties, "window_size", 8); printf("frame psnr[Y] psnr[Cb] psnr[Cr] ssim[Y] ssim[Cb] ssim[Cr]\n"); } return transition; } } // extern "C" mlt-7.38.0/src/modules/qt/transition_vqm.yml000664 000000 000000 00000001352 15172202314 021046 0ustar00rootroot000000 000000 schema_version: 0.1 type: transition identifier: vqm title: Video Quality Measurement version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: GPLv3 language: en description: > This performs the PSNR and SSIM video quality measurements by comparing the B frames to the reference frame A. It outputs the numbers to stdout in space-delimited format for easy by another tool. The bottom half of the B frame is placed below the top half of the A frame for visual comparison. tags: - Video parameters: - identifier: render title: Render description: > Render a line between top and bottom halves and the values atop the video. type: integer default: 0 minimum: 0 maximum: 1 widget: checkbox mlt-7.38.0/src/modules/qt/typewriter.cpp000664 000000 000000 00000037624 15172202314 020203 0ustar00rootroot000000 000000 /* * Copyright (c) 2017 Rafal Lalik rafallalik@gmail.com * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "typewriter.h" #include #include #include #include using namespace std; /*======================================================================* TypeWriter parser related stuff *======================================================================*/ const char macro_char = ':'; const char nextframe_char = ','; const char nextstep_char = '>'; const char delkey_char = '<'; const char optbeg_char = '['; const char optend_char = ']'; const char rangebeg_char = '{'; const char rangeend_char = '}'; const char escape_char = '\\'; std::string null_string; TypeWriter::TypeWriter() : frame_rate(25) , frame_step(1) , parsing_err(0) , last_used_idx(-1) {} void TypeWriter::clear() { frames.clear(); } void TypeWriter::setPattern(const std::string &str) { raw_string = str; frames.reserve(raw_string.length()); } int TypeWriter::parse() { clear(); gen.seed(step_seed); if (step_sigma > 0.) d = std::normal_distribution<>{0, step_sigma}; previous_total_frame = -1; int start_frame = 0; parsing_err = parseString(raw_string, start_frame); return parsing_err; } void TypeWriter::printParseResult() { if (parsing_err < 0) { fprintf(stderr, "Parsing error:\n%.*s\n", -parsing_err - 1, raw_string.c_str()); fprintf(stderr, "%*c%c\n", -parsing_err - 2, ' ', '^'); } else { printf("Parsing OK: frames=%u strings=%zu\n", count(), frames.size()); } } uint TypeWriter::count() const { return frames.back().frame; } uint TypeWriter::getOrInsertFrame(uint frame) { // create new or reuse old frame // by design last->frame cannot be larger than frame // take the last frame then FIXME: should we break parser here? int real_frame = frame * frame_step; uint n = frames.size(); if (!n) { int s = step_sigma > 0. ? std::round(d(gen)) : 0; if ((s + real_frame) > 0) real_frame += s; if (real_frame <= previous_total_frame) real_frame = previous_total_frame + 1; previous_total_frame = real_frame; frames.push_back(Frame(frame, real_frame)); return 0; } if (frames[n - 1].frame >= frame) return n - 1; int s = step_sigma > 0. ? std::round(d(gen)) : 0; if ((s + real_frame) > 0) real_frame += s; if (real_frame <= previous_total_frame) real_frame = previous_total_frame + 1; previous_total_frame = real_frame; Frame f = Frame(frame, real_frame); f.s = frames[n - 1].s; frames.push_back(f); return n; } void TypeWriter::insertChar(char c, uint frame) { char buff[2] = "\0"; buff[0] = c; insertString(buff, frame); } void TypeWriter::insertString(const std::string &str, uint frame) { uint n = getOrInsertFrame(frame); Frame &f = frames[n]; f.s.append(str); } void TypeWriter::insertBypass(uint frame) { uint n = getOrInsertFrame(frame); addBypass(n); } const std::string &TypeWriter::render(uint frame) { uint n = frames.size(); if (!n) return null_string; if (last_used_idx == -1) last_used_idx = 0; // start with current frame Frame f = frames[last_used_idx]; // but if current is ahead 'frame', start from beginning if (f.real_frame > frame) last_used_idx = 0; if (frames[last_used_idx].real_frame > frame) return null_string; for (; last_used_idx < (int) n - 1; ++last_used_idx) { f = frames[last_used_idx + 1]; if (f.real_frame > frame) return frames[last_used_idx].s; } return frames[last_used_idx].s; } void TypeWriter::addBypass(uint idx) { if (idx == 0) { frames[idx].s.clear(); return; } int pidx = -1; if (frames[idx].bypass == -2) pidx = idx - 1; else pidx = frames[idx].bypass; if (pidx == -1) return; while (true) { if (frames[pidx].bypass != -2) { pidx = frames[pidx].bypass; } else { --pidx; break; } } frames[idx].bypass = pidx; if (frames[idx].bypass >= 0) frames[idx].s = frames[frames[idx].bypass].s; else frames[idx].s.clear(); } Frame::Frame(uint frame, uint real_frame) : frame(frame) , real_frame(real_frame) , bypass(-2) {} void Frame::print() { printf("%c [%d] (%d) %s %c\n", true ? '-' : '|', frame, real_frame, s.c_str(), true ? '-' : '|'); } std::string TypeWriter::detectUtf8(const std::string &str, size_t pos) { /* * 0x00 do 0x7F – bits 0xxxxxxx * 0x80 do 0x7FF – bits 110xxxxx 10xxxxxx * 0x800 do 0xFFFF – bits 1110xxxx 10xxxxxx 10xxxxxx * 0x10000 do 0x1FFFFF – bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * 0x200000 do 0x3FFFFFF – bits 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx * 0x4000000 do 0x7FFFFFFF – bits 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ unsigned char c = str[pos]; const unsigned char mask = 0xfc; // largest possible utf char // five patterns possible for (int i = 0; i < 5; ++i) { unsigned char m = mask << i; if ((c & m) == m) return str.substr(pos, 6 - i); } return str.substr(pos, 1); } struct TypeWriter::ParseOptions { ParseOptions() : n(0) , fskip(0) , sskip(0) {} uint n; uint fskip; uint sskip; }; uint TypeWriter::getFrameSkipFromOptions(const ParseOptions &po, bool steps) { if (steps) return (po.n + po.sskip) * frame_rate; else return po.sskip * frame_rate + po.fskip + po.n; } int TypeWriter::parseString(const std::string &line, int start_frame) { size_t limit = line.length(); uint frame = start_frame; std::string frame_text; char check_for_options = 0; bool was_escaped = false; uint i = 0; while (i < limit) { was_escaped = false; check_for_options = 0; char c = line[i]; if (c == escape_char) { ++i; c = line[i]; if (!c) return -i - 1; was_escaped = true; } else if (c == nextframe_char) { ++frame; // increase frame number check_for_options = nextframe_char; } else if (c == nextstep_char) { frame += frame_rate; check_for_options = nextstep_char; } else if (c == macro_char) { ++i; int ret = parseMacro(line, i, frame); if (ret < 0) return ret; continue; } if (check_for_options) { // get next char and check whether it is an option ++i; c = line[i]; ParseOptions po; int ret = parseOptions(line, i, po); if (ret < 0) { return ret; } uint n = getFrameSkipFromOptions(po, check_for_options == nextstep_char); if (check_for_options == nextframe_char) { if (n > 0) frame += (n - 1); } else if (check_for_options == nextstep_char) { if (n > 0) frame += (n - frame_rate); } continue; } // append values if (!was_escaped and c == delkey_char) { // get next char and check whether it is an option ++i; c = line[i]; ParseOptions po; po.n = 1; int ret = parseOptions(line, i, po); if (ret < 0) { return ret; } for (uint i = 0; i < po.n; ++i) insertBypass(frame); } else { if (was_escaped) { if (c == 'n') c = '\n'; else if (c == 't') c = '\t'; } std::string test_str = detectUtf8(line, i); insertString(test_str, frame); i += test_str.length(); } } return frame; } int TypeWriter::parseOptions(const std::string &line, uint &i, ParseOptions &po) { char c = line[i]; if (c != optbeg_char) return i; ++i; c = line[i]; int n = 0; // stores number of frames to skip while (c != optend_char and c) { // if is digit then add to frames skip number if (isdigit(c)) { int v = c - 48; // quick conv from char to int n = n * 10 + v; } // s if for seconds: mult frames by f. rate else if (c == 's') { po.sskip = n; n = 0; } // just frames else if (c == 'f') { po.fskip = n; n = 0; } else if (c == ',') { if (n) po.n = n; } else { // unknown char, return error return -i - 1; } ++i; c = line[i]; } if (n) po.n = n; ++i; return i; } int TypeWriter::parseMacro(const std::string &line, uint &i, uint &frame) { std::vector string_list; uint n = 0; char c = line[i]; if (c == 'c') // split by characters { ++i; // calculate skip from options ParseOptions po; int ret = parseOptions(line, i, po); if (ret < 0) { return ret; } n = getFrameSkipFromOptions(po); if (n == 0) n = 1; c = line[i]; if (c != rangebeg_char) return -i - 1; ++i; c = line[i]; while (c != rangeend_char and c) { if (c == escape_char) { ++i; c = line[i]; if (!c) return -i - 1; } std::string test_str = detectUtf8(line, i); insertString(test_str, frame); frame += n; i += test_str.length(); c = line[i]; } } else if (c == 'w') // split by words { ++i; // calculate skip from options ParseOptions po; int ret = parseOptions(line, i, po); if (ret < 0) { return ret; } n = getFrameSkipFromOptions(po); if (n == 0) n = 1; c = line[i]; if (c != rangebeg_char) return -i - 1; ++i; c = line[i]; size_t i_end = i; while (true) { // search for range end i_end = line.find_first_of(rangeend_char, i_end); // if end of string, error if (i_end == line.npos) return -i_end - 1; // check if endrange char is not escaped if (line[i_end - 1] != escape_char or line[i_end - 2] == escape_char) break; ++i_end; } std::string substr = line.substr(i, i_end - i); size_t pos = 0; while (true) { pos = substr.find_first_of(escape_char, pos); if (pos == substr.npos) break; substr.replace(pos, 1, ""); } pos = 0; size_t pos2 = 0; std::string s; while (pos2 != substr.npos) { pos2 = substr.find_first_of(" \t\n", pos); if (pos2 != substr.npos) { pos2 = substr.find_first_not_of(" \t\n", pos2); if (pos2 != substr.npos) s = substr.substr(pos, pos2 - pos); else s = substr.substr(pos); } else { s = substr.substr(pos); } insertString(s, frame); frame += n; pos = pos2; } i = i_end; ++i; } else if (c == 'l') // split by lines { ++i; // calculate skip from options ParseOptions po; int ret = parseOptions(line, i, po); if (ret < 0) { return ret; } n = getFrameSkipFromOptions(po); if (n == 0) n = 1; c = line[i]; if (c != rangebeg_char) return -i - 1; ++i; c = line[i]; size_t i_end = i; while (true) { // search for range end i_end = line.find_first_of(rangeend_char, i_end); // if end of string, error if (i_end == line.npos) return -i_end - 1; // check if endrange char is not escaped if (line[i_end - 1] != escape_char or line[i_end - 2] == escape_char) break; ++i_end; } std::string substr = line.substr(i, i_end - i); size_t pos = 0; while (true) { pos = substr.find_first_of(escape_char, pos); if (pos == substr.npos) break; substr.replace(pos, 1, ""); } pos = 0; size_t pos2 = 0; std::string s; while (pos2 != substr.npos) { pos2 = substr.find_first_of("\n", pos); if (pos2 != substr.npos) { pos2 = substr.find_first_not_of("\n", pos2); if (pos2 != substr.npos) s = substr.substr(pos, pos2 - pos); else s = substr.substr(pos); } else { s = substr.substr(pos); } insertString(s, frame); frame += n; pos = pos2; } i = i_end; ++i; } else // error { return -i - 1; } ++i; return i; } /*======================================================================* XML Parser related stuff *======================================================================*/ char *clone_string(const char *string) { char *new_string = new char[strlen(string) + 1]; if (new_string) strcpy(new_string, string); return new_string; } XmlParser::XmlParser() {} XmlParser::~XmlParser() {} void XmlParser::clear() {} void XmlParser::setDocument(const char *xml) { clear(); doc = QString::fromUtf8(xml); dom.setContent(doc); QDomElement title = dom.documentElement(); items = title.elementsByTagName("item"); } int XmlParser::parse() { node_vec.clear(); for (int i = 0; i < items.count(); ++i) { QDomNode node = items.item(i); QDomNamedNodeMap nodeAttributes = node.attributes(); if (nodeAttributes.namedItem("type").nodeValue() == "QGraphicsTextItem") { QDomNode lnode = node.namedItem("content").firstChild(); node_vec.push_back(lnode); } } return 1; } QString XmlParser::getNodeContent(uint i) const { if (i >= node_vec.size()) return nullptr; return node_vec[i].nodeValue(); } void XmlParser::setNodeContent(uint i, const QString &content) { if (i >= node_vec.size()) return; node_vec[i].setNodeValue(content); } QString XmlParser::getDocument() const { return dom.toString(); } mlt-7.38.0/src/modules/qt/typewriter.h000664 000000 000000 00000007221 15172202314 017636 0ustar00rootroot000000 000000 /* * Copyright (c) 2017 Rafal Lalik rafallalik@gmail.com * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TYPEWRITER_H #define TYPEWRITER_H #include #include #include #include using namespace std; struct Frame { Frame(uint frame, uint real_frame); uint frame; uint real_frame; std::string s; int bypass; void print(); }; class TypeWriter { public: TypeWriter(); virtual ~TypeWriter() = default; void setFrameRate(uint fr) { frame_rate = fr; } uint getFrameRate() const { return frame_rate; } void setFrameStep(uint fs) { frame_step = fs; } uint getFrameStep() const { return frame_step; } void setStepSigma(float ss) { step_sigma = ss; } uint getStepSigma() const { return step_sigma; } void setStepSeed(float ss) { step_seed = ss; } uint getStepSeed() const { return step_seed; } void setPattern(const std::string &str); const std::string &getPattern() const { return raw_string; } int parse(); void printParseResult(); const std::string &render(uint frame); uint count() const; bool isEnd() const { return last_used_idx == (int) frames.size() - 1; } void clear(); void debug() const { for (Frame f : frames) f.print(); } private: int parseString(const std::string &line, int start_frame); struct ParseOptions; int parseOptions(const std::string &line, uint &i, ParseOptions &po); int parseMacro(const std::string &line, uint &i, uint &frame); std::string detectUtf8(const std::string &str, size_t pos); void insertChar(char c, uint frame); void insertString(const std::string &str, uint frame); void insertBypass(uint frame); uint getFrameSkipFromOptions(const ParseOptions &po, bool steps = false); uint getOrInsertFrame(uint frame); void addBypass(uint idx); private: size_t frame_rate; size_t frame_step; float step_sigma; size_t step_seed; int parsing_err; int previous_total_frame; std::string raw_string; std::vector frames; int last_used_idx; std::mt19937 gen; std::normal_distribution<> d; }; class XmlParser { public: XmlParser(); virtual ~XmlParser(); void setDocument(const char *xml); int parse(); uint getContentNodesNumber() const { return node_vec.size(); } QString getNodeContent(uint i) const; void setNodeContent(uint i, const QString &content); void clear(); QString getDocument() const; private: QString doc; QDomDocument dom; QDomNodeList items; std::vector node_vec; }; #endif /* TYPEWRITER_H */ mlt-7.38.0/src/modules/resample/000775 000000 000000 00000000000 15172202314 016431 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/resample/CMakeLists.txt000664 000000 000000 00000001323 15172202314 021170 0ustar00rootroot000000 000000 add_library(mltresample MODULE factory.c filter_resample.c link_resample.c) file(GLOB YML "*.yml") add_custom_target(Other_resample_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltresample) target_compile_options(mltresample PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltresample PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltresample PRIVATE mlt PkgConfig::samplerate) set_target_properties(mltresample PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltresample LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_resample.yml link_resample.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/resample ) mlt-7.38.0/src/modules/resample/factory.c000664 000000 000000 00000003701 15172202314 020245 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mltresample_export.h" #include #include #include extern mlt_filter filter_resample_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_link link_resample_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/resample/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTRESAMPLE_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "resample", filter_resample_init); MLT_REGISTER(mlt_service_link_type, "resample", link_resample_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "resample", metadata, "filter_resample.yml"); MLT_REGISTER_METADATA(mlt_service_link_type, "resample", metadata, "link_resample.yml"); } mlt-7.38.0/src/modules/resample/filter_resample.c000664 000000 000000 00000022352 15172202314 021756 0ustar00rootroot000000 000000 /* * filter_resample.c -- adjust audio sample frequency * Copyright (C) 2003-2021 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #define PROCESS_BUFF_SIZE (10000 * sizeof(float)) // Private Types typedef struct { SRC_STATE *s; int error; int channels; float buff[PROCESS_BUFF_SIZE]; int leftover_samples; } private_data; /** Get the audio. */ static int resample_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) filter->child; struct mlt_audio_s in; struct mlt_audio_s out; mlt_audio_set_values(&out, NULL, *frequency, *format, *samples, *channels); // Apply user requested rate - else will normalize to consumer requested rate; if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "frequency")) { out.frequency = mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "frequency"); } // Get the producer's audio int error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (error || *format == mlt_audio_none || *frequency <= 0 || out.frequency <= 0 || *channels <= 0) { // Error situation. Do not attempt to convert. mlt_log_error(MLT_FILTER_SERVICE(filter), "Invalid Parameters: %dS - %dHz %dC %s -> %dHz %dC %s\n", *samples, *frequency, *channels, mlt_audio_format_name(*format), out.frequency, out.channels, mlt_audio_format_name(out.format)); return error; } if (*samples == 0) { // Noting to convert. No error message needed. return error; } if (*frequency == out.frequency && !pdata) { // No frequency change, and there is no stored state. return error; } // *Proceed to convert the sampling frequency* // The converter operates on interleaved float. Convert the samples if necessary if (*format != mlt_audio_f32le) { // Do not convert to float unless we need to change the rate frame->convert_audio(frame, buffer, format, mlt_audio_f32le); } // Set up audio structures and allocate output buffer mlt_audio_set_values(&in, *buffer, *frequency, *format, *samples, *channels); out.format = in.format; out.channels = in.channels; mlt_audio_alloc_data(&out); mlt_log_debug(MLT_FILTER_SERVICE(filter), "%dHz -> %dHz\n", in.frequency, out.frequency); mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Create the private data if it does not exist if (!pdata) { pdata = (private_data *) calloc(1, sizeof(private_data)); pdata->s = NULL; pdata->channels = 0; pdata->leftover_samples = 0; filter->child = pdata; } // Recreate the resampler if necessary if (!pdata->s || pdata->channels != in.channels) { mlt_log_debug(MLT_FILTER_SERVICE(filter), "Create resample state %d channels\n", in.channels); pdata->s = src_delete(pdata->s); pdata->s = src_new(SRC_SINC_BEST_QUALITY, in.channels, &pdata->error); pdata->channels = in.channels; } int total_consumed_samples = 0; int consumed_samples = 0; int received_samples = 0; int process_buff_samples = PROCESS_BUFF_SIZE / sizeof(float) / in.channels; // First copy samples that are leftover from the previous frame if (pdata->leftover_samples) { int samples_to_copy = pdata->leftover_samples; if (samples_to_copy > out.samples) { samples_to_copy = out.samples; } memcpy(out.data, pdata->buff, samples_to_copy * out.channels * sizeof(float)); received_samples += samples_to_copy; pdata->leftover_samples -= samples_to_copy; } // Process all input samples while (total_consumed_samples < in.samples || received_samples < out.samples) { if (pdata->leftover_samples) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Discard leftover samples %d\n", pdata->leftover_samples); pdata->leftover_samples = 0; } if (consumed_samples >= in.samples) { // Continue to repeat input samples into the resampler until it // provides the desired number of samples out. consumed_samples = 0; mlt_log_debug(MLT_FILTER_SERVICE(filter), "Repeat samples\n"); } SRC_DATA data; data.end_of_input = 0; data.src_ratio = (double) out.frequency / (double) in.frequency; data.data_in = (float *) in.data + (consumed_samples * in.channels); data.input_frames = in.samples - consumed_samples; data.data_out = pdata->buff; data.output_frames = process_buff_samples; if (total_consumed_samples >= in.samples) { // All input samples have been read once. // Limit the output frames to the minimum necessary to fill the output frame. // Limit the input frames to 1 at a time to minimize the duplicated samples. // Sometimes one input frame can cause many frames to be output from the resampler. data.input_frames = 1; if (data.output_frames > (out.samples - received_samples)) { data.output_frames = out.samples - received_samples; } } // Resample the audio src_set_ratio(pdata->s, data.src_ratio); error = src_process(pdata->s, &data); if (error) { mlt_log_error(MLT_FILTER_SERVICE(filter), "%s %d,%d,%d\n", src_strerror(error), in.frequency, in.samples, out.frequency); break; } // Copy resampled samples from buff to output if (data.output_frames_gen) { float *dst = (float *) out.data + (received_samples * out.channels); int samples_to_copy = data.output_frames_gen; if (samples_to_copy > (out.samples - received_samples)) { samples_to_copy = out.samples - received_samples; } int bytes_to_copy = samples_to_copy * out.channels * sizeof(float); memcpy(dst, pdata->buff, bytes_to_copy); if (samples_to_copy < data.output_frames_gen) { // Move leftover samples to the beginning of buff to use next time pdata->leftover_samples = data.output_frames_gen - samples_to_copy; float *src = pdata->buff + (samples_to_copy * out.channels); memmove(pdata->buff, src, pdata->leftover_samples * out.channels * sizeof(float)); } received_samples += samples_to_copy; } consumed_samples += data.input_frames_used; total_consumed_samples += data.input_frames_used; } mlt_frame_set_audio(frame, out.data, out.format, 0, out.release_data); mlt_audio_get_values(&out, buffer, frequency, format, samples, channels); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return error; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { if (mlt_frame_is_test_audio(frame) == 0) { mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, resample_get_audio); } return frame; } static void close_filter(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { if (pdata->s) { src_delete(pdata->s); } free(pdata); filter->child = NULL; } } /** Constructor for the filter. */ mlt_filter filter_resample_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; filter->close = close_filter; filter->child = NULL; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to initialize\n"); } return filter; } mlt-7.38.0/src/modules/resample/filter_resample.yml000664 000000 000000 00000001233 15172202314 022330 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: resample title: Resample version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Hidden description: > Adjust an audio stream's sampling rate. This filter is automatically invoked by the loader producer to normalize audio from the producer to provide the rate requested by the consumer. parameters: - identifier: frequency title: Frequency type: integer description: > The target sample rate. If not set, the filter will convert to the sample rate requested by the consumer. required: no readonly: no mlt-7.38.0/src/modules/resample/gpl000664 000000 000000 00000000000 15172202314 017124 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/resample/link_resample.c000664 000000 000000 00000027124 15172202314 021430 0ustar00rootroot000000 000000 /* * link_resample.c * Copyright (C) 2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include // Private Types #define FUTURE_FRAMES 1 typedef struct { // Used by get_audio mlt_position expected_frame; mlt_position continuity_frame; int continuity_sample; SRC_STATE *s; int channels; } private_data; static void link_configure(mlt_link self, mlt_profile chain_profile) { // Operate at the same frame rate as the next link mlt_service_set_profile(MLT_LINK_SERVICE(self), mlt_service_profile(MLT_PRODUCER_SERVICE(self->next))); } static int link_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { int requested_frequency = *frequency <= 0 ? 48000 : *frequency; int requested_samples = *samples; mlt_link self = (mlt_link) mlt_frame_pop_audio(frame); private_data *pdata = (private_data *) self->child; int src_frequency = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "audio_frequency"); src_frequency = src_frequency <= 0 ? *frequency : src_frequency; int src_samples = mlt_audio_calculate_frame_samples(mlt_producer_get_fps( MLT_LINK_PRODUCER(self)), src_frequency, mlt_frame_get_position(frame)); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); struct mlt_audio_s in; struct mlt_audio_s out; mlt_audio_set_values(&in, *buffer, src_frequency, *format, src_samples, *channels); mlt_audio_set_values(&out, NULL, requested_frequency, *format, requested_samples, *channels); // Get the producer's audio int error = mlt_frame_get_audio(frame, &in.data, &in.format, &in.frequency, &in.channels, &in.samples); if (error || in.format == mlt_audio_none || out.format == mlt_audio_none || in.frequency <= 0 || out.frequency <= 0 || in.channels <= 0 || out.channels <= 0) { // Error situation. Do not attempt to convert. mlt_audio_get_values(&in, buffer, frequency, format, samples, channels); mlt_log_error(MLT_LINK_SERVICE(self), "Invalid Parameters: %dS - %dHz %dC %s -> %dHz %dC %s\n", in.samples, in.frequency, in.channels, mlt_audio_format_name(in.format), out.frequency, out.channels, mlt_audio_format_name(out.format)); return error; } if (in.samples == 0) { // Noting to convert. return error; } if (in.frequency == requested_frequency && !pdata->s) { // No change necessary mlt_audio_get_values(&in, buffer, frequency, format, samples, channels); return error; } // Set up audio structures and allocate output buffer in.format = mlt_audio_f32le; out.format = mlt_audio_f32le; out.channels = in.channels; mlt_audio_alloc_data(&out); mlt_log_debug(MLT_LINK_SERVICE(self), "%dHz -> %dHz\n", in.frequency, out.frequency); mlt_service_lock(MLT_LINK_SERVICE(self)); // Recreate the resampler if necessary if (!pdata->s || pdata->channels != in.channels || pdata->expected_frame != mlt_frame_get_position(frame)) { mlt_log_info(MLT_LINK_SERVICE(self), "%dHz -> %dHz\n", in.frequency, out.frequency); pdata->s = src_delete(pdata->s); pdata->s = src_new(SRC_SINC_BEST_QUALITY, in.channels, &error); pdata->channels = in.channels; pdata->expected_frame = mlt_frame_get_position(frame); pdata->continuity_frame = mlt_frame_get_position(frame); pdata->continuity_sample = 0; } int received_samples = 0; while (received_samples < out.samples && !error) { mlt_frame src_frame = NULL; if (pdata->continuity_frame == mlt_frame_get_position(frame)) { src_frame = frame; } else { // The input frame is insufficient to fill the output frame. // Request audio from future frames. mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_LINK_SERVICE(self)); if (!unique_properties) { error = 1; break; } char key[19]; int frame_delta = mlt_frame_get_position(frame) - mlt_frame_original_position(frame); sprintf(key, "%d", pdata->continuity_frame - frame_delta); src_frame = (mlt_frame) mlt_properties_get_data(unique_properties, key, NULL); } if (!src_frame) { mlt_log_error(MLT_LINK_SERVICE(self), "Frame not found: %d\n", pdata->continuity_frame); error = 1; break; } in.samples = mlt_audio_calculate_frame_samples(mlt_producer_get_fps(MLT_LINK_PRODUCER(self)), in.frequency, pdata->continuity_frame); error = mlt_frame_get_audio(src_frame, &in.data, &in.format, &in.frequency, &in.channels, &in.samples); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Unable to get audio for %d\n", pdata->continuity_frame); break; } while (pdata->continuity_sample < in.samples && received_samples < out.samples) { SRC_DATA data; data.end_of_input = 0; data.src_ratio = (double) out.frequency / (double) in.frequency; data.data_out = (float *) out.data + (received_samples * out.channels); data.output_frames = out.samples - received_samples; data.data_in = (float *) in.data + (pdata->continuity_sample * in.channels); // Calculate the fewest samples that can be sent to the resampler to get the needed output. // Round down just to be sure. This is done to reduce the latency through the resampler and // to borrow as few samples from future frames as possible. data.input_frames = ((data.output_frames * in.frequency) / out.frequency) - 1; if (data.input_frames > (in.samples - pdata->continuity_sample)) { data.input_frames = in.samples - pdata->continuity_sample; } if (data.input_frames <= 0) { data.input_frames = 1; } // Resample the audio src_set_ratio(pdata->s, data.src_ratio); error = src_process(pdata->s, &data); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "%s %d,%d,%d\n", src_strerror(error), in.frequency, in.samples, out.frequency); break; } received_samples += data.output_frames_gen; pdata->continuity_sample += data.input_frames_used; } if (pdata->continuity_sample >= in.samples) { // All the samples from this frame are used. pdata->continuity_sample = 0; pdata->continuity_frame++; } } if (received_samples == 0) { mlt_log_info(MLT_LINK_SERVICE(self), "Failed to get any samples - return silence\n"); mlt_audio_silence(&out, out.samples, 0); } else if (received_samples < out.samples) { // Duplicate samples to return the exact number requested. mlt_audio_copy(&out, &out, received_samples, 0, out.samples - received_samples); } mlt_frame_set_audio(frame, out.data, out.format, 0, out.release_data); mlt_audio_get_values(&out, buffer, frequency, format, samples, channels); mlt_properties_set(frame_properties, "channel_layout", mlt_audio_channel_layout_name(out.layout)); pdata->expected_frame = mlt_frame_get_position(frame) + 1; mlt_service_unlock(MLT_LINK_SERVICE(self)); return error; } static int link_get_frame(mlt_link self, mlt_frame_ptr frame, int index) { int error = 0; mlt_position frame_pos = mlt_producer_position(MLT_LINK_PRODUCER(self)); mlt_producer_seek(self->next, frame_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), frame, index); if (error) { return error; } mlt_properties unique_properties = mlt_frame_unique_properties(*frame, MLT_LINK_SERVICE(self)); // Pass future frames int i = 0; for (i = 0; i < FUTURE_FRAMES; i++) { mlt_position future_pos = frame_pos + i + 1; mlt_frame future_frame = NULL; mlt_producer_seek(self->next, future_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), &future_frame, index); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Error getting frame: %d\n", (int) future_pos); } char key[19]; sprintf(key, "%d", (int) future_pos); mlt_properties_set_data(unique_properties, key, future_frame, 0, (mlt_destructor) mlt_frame_close, NULL); } mlt_frame_push_audio(*frame, (void *) self); mlt_frame_push_audio(*frame, link_get_audio); mlt_producer_prepare_next(MLT_LINK_PRODUCER(self)); return error; } static void link_close(mlt_link self) { if (self) { private_data *pdata = (private_data *) self->child; if (pdata) { src_delete(pdata->s); } free(pdata); self->close = NULL; self->child = NULL; mlt_link_close(self); free(self); } } mlt_link link_resample_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_link self = mlt_link_init(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (self && pdata) { pdata->continuity_frame = -1; pdata->expected_frame = -1; self->child = pdata; // Callback registration self->configure = link_configure; self->get_frame = link_get_frame; self->close = link_close; } else { if (pdata) { free(pdata); } if (self) { mlt_link_close(self); self = NULL; } } return self; } mlt-7.38.0/src/modules/resample/link_resample.yml000664 000000 000000 00000000570 15172202314 022003 0ustar00rootroot000000 000000 schema_version: 7.0 type: link identifier: resample title: SRC Resampler version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en url: http://www.mega-nerd.com/SRC/ tags: - Audio - Hidden description: > Change the audio sampling rate. This link can be added to a chain to normalize audio from the producer to provide the rate requested by the consumer.mlt-7.38.0/src/modules/rtaudio/000775 000000 000000 00000000000 15172202314 016270 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/rtaudio/CMakeLists.txt000664 000000 000000 00000003635 15172202314 021037 0ustar00rootroot000000 000000 add_library(mltrtaudio MODULE consumer_rtaudio.cpp ) file(GLOB YML "*.yml") add_custom_target(Other_rtaudio_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltrtaudio) target_compile_options(mltrtaudio PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltrtaudio PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltrtaudio PRIVATE mlt Threads::Threads) if(MSVC) target_link_libraries(mltrtaudio PRIVATE PThreads4W::PThreads4W) endif() if(TARGET PkgConfig::rtaudio) target_link_libraries(mltrtaudio PRIVATE PkgConfig::rtaudio) else() target_sources(mltrtaudio PRIVATE RtAudio.cpp RtAudio.h) target_include_directories(mltrtaudio PRIVATE .) if(APPLE) target_link_libraries(mltrtaudio PRIVATE "-framework CoreAudio" "-framework CoreFoundation") target_compile_definitions(mltrtaudio PRIVATE __MACOSX_CORE__) elseif(WIN32) target_link_libraries(mltrtaudio PRIVATE ole32 dsound winmm ksuser mfplat mfuuid wmcodecdspuuid) target_compile_definitions(mltrtaudio PRIVATE __WINDOWS_DS__ __WINDOWS_WASAPI__) else() if(TARGET PkgConfig::alsa) target_link_libraries(mltrtaudio PRIVATE PkgConfig::alsa) target_compile_definitions(mltrtaudio PRIVATE __LINUX_ALSA__) endif() if(TARGET PkgConfig::libpulse-simple) target_link_libraries(mltrtaudio PRIVATE PkgConfig::libpulse-simple) target_compile_definitions(mltrtaudio PRIVATE __LINUX_PULSE__) endif() if(NOT (TARGET PkgConfig::alsa OR TARGET PkgConfig::libpulse-simple)) target_link_libraries(mltrtaudio PRIVATE ossaudio) target_compile_definitions(mltrtaudio PRIVATE __LINUX_OSS__) endif() endif() endif() set_target_properties(mltrtaudio PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltrtaudio LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_rtaudio.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/rtaudio) mlt-7.38.0/src/modules/rtaudio/LICENSE000664 000000 000000 00000002520 15172202314 017274 0ustar00rootroot000000 000000 RtAudio: a set of realtime audio i/o C++ classes Copyright (c) 2001-2023 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is asked to send the modifications to the original developer so that they can be incorporated into the canonical version. This is, however, not a binding provision of this license. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mlt-7.38.0/src/modules/rtaudio/RtAudio.cpp000664 000000 000000 00001443622 15172202314 020357 0ustar00rootroot000000 000000 /************************************************************************/ /*! \class RtAudio \brief Realtime audio i/o C++ classes. RtAudio provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, Jack, and OSS), Macintosh OS X (CoreAudio and Jack), and Windows (DirectSound, ASIO and WASAPI) operating systems. RtAudio GitHub site: https://github.com/thestk/rtaudio RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio: realtime audio i/o C++ classes Copyright (c) 2001-2023 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is asked to send the modifications to the original developer so that they can be incorporated into the canonical version. This is, however, not a binding provision of this license. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /************************************************************************/ // RtAudio: Version 6.0.1 #include "RtAudio.h" #include #include #include #include #include #include #include #include #if defined(_WIN32) #include #endif // Static variable definitions. const unsigned int RtApi::MAX_SAMPLE_RATES = 14; const unsigned int RtApi::SAMPLE_RATES[] = { 4000, 5512, 8000, 9600, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; template inline std::string convertCharPointerToStdString(const T *text); template<> inline std::string convertCharPointerToStdString(const char *text) { return text; } template<> inline std::string convertCharPointerToStdString(const wchar_t *text) { return std::wstring_convert>{}.to_bytes(text); } #if defined(_MSC_VER) #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A) #define MUTEX_DESTROY(A) DeleteCriticalSection(A) #define MUTEX_LOCK(A) EnterCriticalSection(A) #define MUTEX_UNLOCK(A) LeaveCriticalSection(A) #else #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL) #define MUTEX_DESTROY(A) pthread_mutex_destroy(A) #define MUTEX_LOCK(A) pthread_mutex_lock(A) #define MUTEX_UNLOCK(A) pthread_mutex_unlock(A) #endif // *************************************************** // // // RtApi subclass prototypes. // // *************************************************** // #if defined(__MACOSX_CORE__) #include class RtApiCore: public RtApi { public: RtApiCore(); ~RtApiCore(); RtAudio::Api getCurrentApi( void ) override { return RtAudio::MACOSX_CORE; } unsigned int getDefaultOutputDevice( void ) override; unsigned int getDefaultInputDevice( void ) override; void closeStream( void ) override; RtAudioErrorType startStream( void ) override; RtAudioErrorType stopStream( void ) override; RtAudioErrorType abortStream( void ) override; // This function is intended for internal use only. It must be // public because it is called by an internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! bool callbackEvent( AudioDeviceID deviceId, const AudioBufferList *inBufferList, const AudioBufferList *outBufferList ); private: void probeDevices( void ) override; bool probeDeviceInfo( AudioDeviceID id, RtAudio::DeviceInfo &info ); bool probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) override; static const char* getErrorCode( OSStatus code ); std::vector< AudioDeviceID > deviceIds_; }; #endif #if defined(__UNIX_JACK__) #include class RtApiJack: public RtApi { public: RtApiJack(); ~RtApiJack(); RtAudio::Api getCurrentApi( void ) override { return RtAudio::UNIX_JACK; } void closeStream( void ) override; RtAudioErrorType startStream( void ) override; RtAudioErrorType stopStream( void ) override; RtAudioErrorType abortStream( void ) override; // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! bool callbackEvent( unsigned long nframes ); private: void probeDevices( void ) override; bool probeDeviceInfo( RtAudio::DeviceInfo &info, jack_client_t *client ); bool probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) override; bool shouldAutoconnect_; }; #endif #if defined(__WINDOWS_ASIO__) class RtApiAsio: public RtApi { public: RtApiAsio(); ~RtApiAsio(); RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_ASIO; } void closeStream( void ) override; RtAudioErrorType startStream( void ) override; RtAudioErrorType stopStream( void ) override; RtAudioErrorType abortStream( void ) override; // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! bool callbackEvent( long bufferIndex ); private: bool coInitialized_; void probeDevices( void ) override; bool probeDeviceInfo( RtAudio::DeviceInfo &info ); bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) override; }; #endif #if defined(__WINDOWS_DS__) class RtApiDs: public RtApi { public: RtApiDs(); ~RtApiDs(); RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_DS; } void closeStream( void ) override; RtAudioErrorType startStream( void ) override; RtAudioErrorType stopStream( void ) override; RtAudioErrorType abortStream( void ) override; // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! void callbackEvent( void ); private: bool coInitialized_; bool buffersRolling; long duplexPrerollBytes; std::vector dsDevices_; void probeDevices( void ) override; bool probeDeviceInfo( RtAudio::DeviceInfo &info, DsDevice &dsDevice ); bool probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) override; }; #endif #if defined(__WINDOWS_WASAPI__) struct IMMDeviceEnumerator; class RtApiWasapi : public RtApi { public: RtApiWasapi(); virtual ~RtApiWasapi(); RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_WASAPI; } unsigned int getDefaultOutputDevice( void ) override; unsigned int getDefaultInputDevice( void ) override; void closeStream( void ) override; RtAudioErrorType startStream( void ) override; RtAudioErrorType stopStream( void ) override; RtAudioErrorType abortStream( void ) override; private: bool coInitialized_; IMMDeviceEnumerator* deviceEnumerator_; std::vector< std::pair< std::string, bool> > deviceIds_; void probeDevices( void ) override; bool probeDeviceInfo( RtAudio::DeviceInfo &info, LPWSTR deviceId, bool isCaptureDevice ); bool probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int* bufferSize, RtAudio::StreamOptions* options ) override; static DWORD WINAPI runWasapiThread( void* wasapiPtr ); static DWORD WINAPI stopWasapiThread( void* wasapiPtr ); static DWORD WINAPI abortWasapiThread( void* wasapiPtr ); void wasapiThread(); }; #endif #if defined(__LINUX_ALSA__) class RtApiAlsa: public RtApi { public: RtApiAlsa(); ~RtApiAlsa(); RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_ALSA; } void closeStream( void ) override; RtAudioErrorType startStream( void ) override; RtAudioErrorType stopStream( void ) override; RtAudioErrorType abortStream( void ) override; // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! void callbackEvent( void ); private: std::vector> deviceIdPairs_; void probeDevices( void ) override; bool probeDeviceInfo( RtAudio::DeviceInfo &info, std::string name ); bool probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) override; }; #endif #if defined(__LINUX_PULSE__) #include class RtApiPulse: public RtApi { public: ~RtApiPulse(); RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_PULSE; } void closeStream( void ) override; RtAudioErrorType startStream( void ) override; RtAudioErrorType stopStream( void ) override; RtAudioErrorType abortStream( void ) override; // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! void callbackEvent( void ); struct PaDeviceInfo { std::string sinkName; std::string sourceName; }; private: std::vector< PaDeviceInfo > paDeviceList_; void probeDevices( void ) override; bool probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) override; }; #endif #if defined(__LINUX_OSS__) #include class RtApiOss: public RtApi { public: RtApiOss(); ~RtApiOss(); RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_OSS; } void closeStream( void ) override; RtAudioErrorType startStream( void ) override; RtAudioErrorType stopStream( void ) override; RtAudioErrorType abortStream( void ) override; // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! void callbackEvent( void ); private: void probeDevices( void ) override; bool probeDeviceInfo( RtAudio::DeviceInfo &info, oss_audioinfo &ainfo ); bool probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) override; }; #endif #if defined(__RTAUDIO_DUMMY__) class RtApiDummy: public RtApi { public: RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RTAUDIO_WARNING ); } RtAudio::Api getCurrentApi( void ) override { return RtAudio::RTAUDIO_DUMMY; } void closeStream( void ) override {} RtAudioErrorType startStream( void ) override { return RTAUDIO_NO_ERROR; } RtAudioErrorType stopStream( void ) override { return RTAUDIO_NO_ERROR; } RtAudioErrorType abortStream( void ) override { return RTAUDIO_NO_ERROR; } private: bool probeDeviceOpen( unsigned int /*deviceId*/, StreamMode /*mode*/, unsigned int /*channels*/, unsigned int /*firstChannel*/, unsigned int /*sampleRate*/, RtAudioFormat /*format*/, unsigned int * /*bufferSize*/, RtAudio::StreamOptions * /*options*/ ) override { return false; } }; #endif // *************************************************** // // // RtAudio definitions. // // *************************************************** // std::string RtAudio :: getVersion( void ) { return RTAUDIO_VERSION; } // Define API names and display names. // Must be in same order as API enum. extern "C" { const char* rtaudio_api_names[][2] = { { "unspecified" , "Unknown" }, { "core" , "CoreAudio" }, { "alsa" , "ALSA" }, { "jack" , "Jack" }, { "pulse" , "Pulse" }, { "oss" , "OpenSoundSystem" }, { "asio" , "ASIO" }, { "wasapi" , "WASAPI" }, { "ds" , "DirectSound" }, { "dummy" , "Dummy" }, }; const unsigned int rtaudio_num_api_names = sizeof(rtaudio_api_names)/sizeof(rtaudio_api_names[0]); // The order here will control the order of RtAudio's API search in // the constructor. extern "C" const RtAudio::Api rtaudio_compiled_apis[] = { #if defined(__MACOSX_CORE__) RtAudio::MACOSX_CORE, #endif #if defined(__LINUX_ALSA__) RtAudio::LINUX_ALSA, #endif #if defined(__UNIX_JACK__) RtAudio::UNIX_JACK, #endif #if defined(__LINUX_PULSE__) RtAudio::LINUX_PULSE, #endif #if defined(__LINUX_OSS__) RtAudio::LINUX_OSS, #endif #if defined(__WINDOWS_ASIO__) RtAudio::WINDOWS_ASIO, #endif #if defined(__WINDOWS_WASAPI__) RtAudio::WINDOWS_WASAPI, #endif #if defined(__WINDOWS_DS__) RtAudio::WINDOWS_DS, #endif #if defined(__RTAUDIO_DUMMY__) RtAudio::RTAUDIO_DUMMY, #endif RtAudio::UNSPECIFIED, }; extern "C" const unsigned int rtaudio_num_compiled_apis = sizeof(rtaudio_compiled_apis)/sizeof(rtaudio_compiled_apis[0])-1; } // This is a compile-time check that rtaudio_num_api_names == RtAudio::NUM_APIS. // If the build breaks here, check that they match. template class StaticAssert { private: StaticAssert() {} }; template<> class StaticAssert{ public: StaticAssert() {} }; class StaticAssertions { StaticAssertions() { StaticAssert(); }}; void RtAudio :: getCompiledApi( std::vector &apis ) { apis = std::vector(rtaudio_compiled_apis, rtaudio_compiled_apis + rtaudio_num_compiled_apis); } std::string RtAudio :: getApiName( RtAudio::Api api ) { if (api < 0 || api >= RtAudio::NUM_APIS) return ""; return rtaudio_api_names[api][0]; } std::string RtAudio :: getApiDisplayName( RtAudio::Api api ) { if (api < 0 || api >= RtAudio::NUM_APIS) return "Unknown"; return rtaudio_api_names[api][1]; } RtAudio::Api RtAudio :: getCompiledApiByName( const std::string &name ) { unsigned int i=0; for (i = 0; i < rtaudio_num_compiled_apis; ++i) if (name == rtaudio_api_names[rtaudio_compiled_apis[i]][0]) return rtaudio_compiled_apis[i]; return RtAudio::UNSPECIFIED; } RtAudio::Api RtAudio :: getCompiledApiByDisplayName( const std::string &name ) { unsigned int i=0; for (i = 0; i < rtaudio_num_compiled_apis; ++i) if (name == rtaudio_api_names[rtaudio_compiled_apis[i]][1]) return rtaudio_compiled_apis[i]; return RtAudio::UNSPECIFIED; } void RtAudio :: openRtApi( RtAudio::Api api ) { if ( rtapi_ ) delete rtapi_; rtapi_ = 0; #if defined(__UNIX_JACK__) if ( api == UNIX_JACK ) rtapi_ = new RtApiJack(); #endif #if defined(__LINUX_ALSA__) if ( api == LINUX_ALSA ) rtapi_ = new RtApiAlsa(); #endif #if defined(__LINUX_PULSE__) if ( api == LINUX_PULSE ) rtapi_ = new RtApiPulse(); #endif #if defined(__LINUX_OSS__) if ( api == LINUX_OSS ) rtapi_ = new RtApiOss(); #endif #if defined(__WINDOWS_ASIO__) if ( api == WINDOWS_ASIO ) rtapi_ = new RtApiAsio(); #endif #if defined(__WINDOWS_WASAPI__) if ( api == WINDOWS_WASAPI ) rtapi_ = new RtApiWasapi(); #endif #if defined(__WINDOWS_DS__) if ( api == WINDOWS_DS ) rtapi_ = new RtApiDs(); #endif #if defined(__MACOSX_CORE__) if ( api == MACOSX_CORE ) rtapi_ = new RtApiCore(); #endif #if defined(__RTAUDIO_DUMMY__) if ( api == RTAUDIO_DUMMY ) rtapi_ = new RtApiDummy(); #endif } RtAudio :: RtAudio( RtAudio::Api api, RtAudioErrorCallback&& errorCallback ) { rtapi_ = 0; std::string errorMessage; if ( api != UNSPECIFIED ) { // Attempt to open the specified API. openRtApi( api ); if ( rtapi_ ) { if ( errorCallback ) rtapi_->setErrorCallback( errorCallback ); return; } // No compiled support for specified API value. Issue a warning // and continue as if no API was specified. errorMessage = "RtAudio: no compiled support for specified API argument!"; if ( errorCallback ) errorCallback( RTAUDIO_INVALID_USE, errorMessage ); else std::cerr << '\n' << errorMessage << '\n' << std::endl; } // Iterate through the compiled APIs and return as soon as we find // one with at least one device or we reach the end of the list. std::vector< RtAudio::Api > apis; getCompiledApi( apis ); for ( unsigned int i=0; igetDeviceNames()).size() > 0 ) break; } if ( rtapi_ ) { if ( errorCallback ) rtapi_->setErrorCallback( errorCallback ); return; } // It should not be possible to get here because the preprocessor // definition __RTAUDIO_DUMMY__ is automatically defined in RtAudio.h // if no API-specific definitions are passed to the compiler. But just // in case something weird happens, issue an error message and abort. errorMessage = "RtAudio: no compiled API support found ... critical error!"; if ( errorCallback ) errorCallback( RTAUDIO_INVALID_USE, errorMessage ); else std::cerr << '\n' << errorMessage << '\n' << std::endl; abort(); } RtAudio :: ~RtAudio() { if ( rtapi_ ) delete rtapi_; } RtAudioErrorType RtAudio :: openStream( RtAudio::StreamParameters *outputParameters, RtAudio::StreamParameters *inputParameters, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData, RtAudio::StreamOptions *options ) { return rtapi_->openStream( outputParameters, inputParameters, format, sampleRate, bufferFrames, callback, userData, options ); } // *************************************************** // // // Public RtApi definitions (see end of file for // private or protected utility functions). // // *************************************************** // RtApi :: RtApi() { clearStreamInfo(); MUTEX_INITIALIZE( &stream_.mutex ); errorCallback_ = 0; showWarnings_ = true; currentDeviceId_ = 129; } RtApi :: ~RtApi() { MUTEX_DESTROY( &stream_.mutex ); } RtAudioErrorType RtApi :: openStream( RtAudio::StreamParameters *oParams, RtAudio::StreamParameters *iParams, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData, RtAudio::StreamOptions *options ) { if ( stream_.state != STREAM_CLOSED ) { errorText_ = "RtApi::openStream: a stream is already open!"; return error( RTAUDIO_INVALID_USE ); } // Clear stream information potentially left from a previously open stream. clearStreamInfo(); if ( oParams && oParams->nChannels < 1 ) { errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one."; return error( RTAUDIO_INVALID_PARAMETER ); } if ( iParams && iParams->nChannels < 1 ) { errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one."; return error( RTAUDIO_INVALID_PARAMETER ); } if ( oParams == NULL && iParams == NULL ) { errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!"; return error( RTAUDIO_INVALID_PARAMETER ); } if ( formatBytes(format) == 0 ) { errorText_ = "RtApi::openStream: 'format' parameter value is undefined."; return error( RTAUDIO_INVALID_PARAMETER ); } // Scan devices if none currently listed. if ( deviceList_.size() == 0 ) probeDevices(); unsigned int m, oChannels = 0; if ( oParams ) { oChannels = oParams->nChannels; // Verify that the oParams->deviceId is found in our list for ( m=0; mdeviceId ) break; } if ( m == deviceList_.size() ) { errorText_ = "RtApi::openStream: output device ID is invalid."; return error( RTAUDIO_INVALID_PARAMETER ); } } unsigned int iChannels = 0; if ( iParams ) { iChannels = iParams->nChannels; for ( m=0; mdeviceId ) break; } if ( m == deviceList_.size() ) { errorText_ = "RtApi::openStream: input device ID is invalid."; return error( RTAUDIO_INVALID_PARAMETER ); } } bool result; if ( oChannels > 0 ) { result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel, sampleRate, format, bufferFrames, options ); if ( result == false ) return error( RTAUDIO_SYSTEM_ERROR ); } if ( iChannels > 0 ) { result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel, sampleRate, format, bufferFrames, options ); if ( result == false ) return error( RTAUDIO_SYSTEM_ERROR ); } stream_.callbackInfo.callback = (void *) callback; stream_.callbackInfo.userData = userData; if ( options ) options->numberOfBuffers = stream_.nBuffers; stream_.state = STREAM_STOPPED; return RTAUDIO_NO_ERROR; } void RtApi :: probeDevices( void ) { // This function MUST be implemented in all subclasses! Within each // API, this function will be used to: // - enumerate the devices and fill or update our // std::vector< RtAudio::DeviceInfo> deviceList_ class variable // - store corresponding (usually API-specific) identifiers that // are needed to open each device // - make sure that the default devices are properly identified // within the deviceList_ (unless API-specific functions are // available for this purpose). // // The function should not reprobe devices that have already been // found. The function must properly handle devices that are removed // or added. // // Ideally, we would also configure callback functions to be invoked // when devices are added or removed (which could be used to inform // clients about changes). However, none of the APIs currently // support notification of _new_ devices and I don't see the // usefulness of having this work only for device removal. return; } unsigned int RtApi :: getDeviceCount( void ) { probeDevices(); return (unsigned int)deviceList_.size(); } std::vector RtApi :: getDeviceIds( void ) { probeDevices(); // Copy device IDs into output vector. std::vector deviceIds; for ( unsigned int m=0; m RtApi :: getDeviceNames( void ) { probeDevices(); // Copy device names into output vector. std::vector deviceNames; for ( unsigned int m=0; m 0 ) { deviceList_[i].isDefaultInput = true; return deviceList_[i].ID; } } return 0; } unsigned int RtApi :: getDefaultOutputDevice( void ) { // Should be reimplemented in subclasses if necessary. if ( deviceList_.size() == 0 ) probeDevices(); for ( unsigned int i = 0; i < deviceList_.size(); i++ ) { if ( deviceList_[i].isDefaultOutput ) return deviceList_[i].ID; } // If not found, find the first device with output channels, set it // as the default, and return the ID. for ( unsigned int i = 0; i < deviceList_.size(); i++ ) { if ( deviceList_[i].outputChannels > 0 ) { deviceList_[i].isDefaultOutput = true; return deviceList_[i].ID; } } return 0; } RtAudio::DeviceInfo RtApi :: getDeviceInfo( unsigned int deviceId ) { if ( deviceList_.size() == 0 ) probeDevices(); for ( unsigned int m=0; m= 0.0 ) stream_.streamTime = time; /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ } unsigned int RtApi :: getStreamSampleRate( void ) { if ( isStreamOpen() ) return stream_.sampleRate; else return 0; } // *************************************************** // // // OS/API-specific methods. // // *************************************************** // #if defined(__MACOSX_CORE__) #include // The OS X CoreAudio API is designed to use a separate callback // procedure for each of its audio devices. A single RtAudio duplex // stream using two different devices is supported here, though it // cannot be guaranteed to always behave correctly because we cannot // synchronize these two callbacks. // // A property listener is installed for over/underrun information. // However, no functionality is currently provided to allow property // listeners to trigger user handlers because it is unclear what could // be done if a critical stream parameter (buffer size, sample rate, // device disconnect) notification arrived. The listeners entail // quite a bit of extra code and most likely, a user program wouldn't // be prepared for the result anyway. However, we do provide a flag // to the client callback function to inform of an over/underrun. // A structure to hold various information related to the CoreAudio API // implementation. struct CoreHandle { AudioDeviceID id[2]; // device ids #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) AudioDeviceIOProcID procId[2]; #endif UInt32 iStream[2]; // device stream index (or first if using multiple) UInt32 nStreams[2]; // number of streams to use bool xrun[2]; char *deviceBuffer; pthread_cond_t condition; int drainCounter; // Tracks callback counts when draining bool internalDrain; // Indicates if stop is initiated from callback or not. bool xrunListenerAdded[2]; bool disconnectListenerAdded[2]; CoreHandle() :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; procId[0] = 0; procId[1] = 0; xrun[0] = false; xrun[1] = false; xrunListenerAdded[0] = false; xrunListenerAdded[1] = false; disconnectListenerAdded[0] = false; disconnectListenerAdded[1] = false; } }; #if defined( MAC_OS_VERSION_12_0 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0 ) #define KAUDIOOBJECTPROPERTYELEMENT kAudioObjectPropertyElementMain #else #define KAUDIOOBJECTPROPERTYELEMENT kAudioObjectPropertyElementMaster // deprecated with macOS 12 #endif RtApiCore:: RtApiCore() { #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER ) // This is a largely undocumented but absolutely necessary // requirement starting with OS-X 10.6. If not called, queries and // updates to various audio device properties are not handled // correctly. CFRunLoopRef theRunLoop = NULL; AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, KAUDIOOBJECTPROPERTYELEMENT }; OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); if ( result != noErr ) { errorText_ = "RtApiCore::RtApiCore: error setting run loop property!"; error( RTAUDIO_SYSTEM_ERROR ); } #endif } RtApiCore :: ~RtApiCore() { // The subclass destructor gets called before the base class // destructor, so close an existing stream before deallocating // apiDeviceId memory. if ( stream_.state != STREAM_CLOSED ) closeStream(); } unsigned int RtApiCore :: getDefaultOutputDevice( void ) { AudioDeviceID id; UInt32 dataSize = sizeof( AudioDeviceID ); AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, KAUDIOOBJECTPROPERTYELEMENT }; OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device."; error( RTAUDIO_SYSTEM_ERROR ); return 0; } for ( unsigned int m=0; mobject; info->deviceDisconnected = true; object->closeStream(); return kAudioHardwareUnspecifiedError; } } return kAudioHardwareNoError; } void RtApiCore :: probeDevices( void ) { // See list of required functionality in RtApi::probeDevices(). // Find out how many audio devices there are. UInt32 dataSize; AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, KAUDIOOBJECTPROPERTYELEMENT }; OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &property, 0, NULL, &dataSize ); if ( result != noErr ) { errorText_ = "RtApiCore::probeDevices: OS-X system error getting device info!"; error( RTAUDIO_SYSTEM_ERROR ); return; } unsigned int nDevices = dataSize / sizeof( AudioDeviceID ); if ( nDevices == 0 ) { deviceList_.clear(); deviceIds_.clear(); return; } AudioDeviceID ids[ nDevices ]; property.mSelector = kAudioHardwarePropertyDevices; result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &ids ); if ( result != noErr ) { errorText_ = "RtApiCore::probeDevices: OS-X system error getting device IDs."; error( RTAUDIO_SYSTEM_ERROR ); return; } // Fill or update the deviceList_ and also save a corresponding list of Ids. for ( unsigned int n=0; n::iterator it=deviceIds_.begin(); it!=deviceIds_.end(); ) { for ( m=0; mmNumberBuffers; for ( i=0; imBuffers[i].mNumberChannels; free( bufferList ); // Get the input stream "configuration". property.mScope = kAudioDevicePropertyScopeInput; result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << info.name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc( dataSize ); if ( bufferList == NULL ) { errorText_ = "RtApiCore::probeDeviceInfo: memory error allocating input AudioBufferList."; error( RTAUDIO_WARNING ); return false; } result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); if (result != noErr || dataSize == 0) { free( bufferList ); errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << info.name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Get input channel information. nStreams = bufferList->mNumberBuffers; for ( i=0; imBuffers[i].mNumberChannels; free( bufferList ); // If device opens for both playback and capture, we determine the channels. if ( info.outputChannels > 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; // Probe the device sample rates. bool isInput = false; if ( info.outputChannels == 0 ) isInput = true; // Determine the supported sample rates. property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput; result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); if ( result != kAudioHardwareNoError || dataSize == 0 ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } UInt32 nRanges = dataSize / sizeof( AudioValueRange ); AudioValueRange rangeList[ nRanges ]; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList ); if ( result != kAudioHardwareNoError ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // The sample rate reporting mechanism is a bit of a mystery. It // seems that it can either return individual rates or a range of // rates. I assume that if the min / max range values are the same, // then that represents a single supported rate and if the min / max // range values are different, the device supports an arbitrary // range of values (though there might be multiple ranges, so we'll // use the most conservative range). Float64 minimumRate = 1.0, maximumRate = 10000000000.0; bool haveValueRange = false; info.sampleRates.clear(); for ( UInt32 i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = tmpSr; } else { haveValueRange = true; if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum; if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum; } } if ( haveValueRange ) { for ( unsigned int k=0; k= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) { info.sampleRates.push_back( SAMPLE_RATES[k] ); if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[k]; } } } // Sort and remove any redundant values std::sort( info.sampleRates.begin(), info.sampleRates.end() ); info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() ); if ( info.sampleRates.size() == 0 ) { errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << info.name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Probe the currently configured sample rate Float64 nominalRate; dataSize = sizeof( Float64 ); property.mSelector = kAudioDevicePropertyNominalSampleRate; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate ); if ( result == noErr ) info.currentSampleRate = (unsigned int) nominalRate; // CoreAudio always uses 32-bit floating point data for PCM streams. // Thus, any other "physical" formats supported by the device are of // no interest to the client. info.nativeFormats = RTAUDIO_FLOAT32; return true; } static OSStatus callbackHandler( AudioDeviceID inDevice, const AudioTimeStamp* /*inNow*/, const AudioBufferList* inInputData, const AudioTimeStamp* /*inInputTime*/, AudioBufferList* outOutputData, const AudioTimeStamp* /*inOutputTime*/, void* infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; if(info == NULL || info->object == NULL) return kAudioHardwareUnspecifiedError; RtApiCore *object = (RtApiCore *) info->object; if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false ) return kAudioHardwareUnspecifiedError; else return kAudioHardwareNoError; } static OSStatus xrunListener( AudioObjectID /*inDevice*/, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void* handlePointer ) { CoreHandle *handle = (CoreHandle *) handlePointer; for ( UInt32 i=0; ixrun[1] = true; else handle->xrun[0] = true; } } return kAudioHardwareNoError; } bool RtApiCore :: probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { AudioDeviceID id = 0; for ( unsigned int m=0; mmNumberBuffers; bool monoMode = false; bool foundStream = false; // First check that the device supports the requested number of // channels. UInt32 deviceChannels = 0; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; if ( deviceChannels < ( channels + firstChannel ) ) { free( bufferList ); errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << deviceId << ") does not support the requested channel count."; errorText_ = errorStream_.str(); return FAILURE; } // Look for a single stream meeting our needs. UInt32 firstStream = 0, streamCount = 1, streamChannels = 0, channelOffset = 0; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; if ( streamChannels >= channels + offsetCounter ) { firstStream = iStream; channelOffset = offsetCounter; foundStream = true; break; } if ( streamChannels > offsetCounter ) break; offsetCounter -= streamChannels; } // If we didn't find a single stream above, then we should be able // to meet the channel specification with multiple streams. if ( foundStream == false ) { monoMode = true; offsetCounter = firstChannel; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; if ( streamChannels > offsetCounter ) break; offsetCounter -= streamChannels; } firstStream = iStream; channelOffset = offsetCounter; Int32 channelCounter = channels + offsetCounter - streamChannels; if ( streamChannels > 1 ) monoMode = false; while ( channelCounter > 0 ) { streamChannels = bufferList->mBuffers[++iStream].mNumberChannels; if ( streamChannels > 1 ) monoMode = false; channelCounter -= streamChannels; streamCount++; } } free( bufferList ); // Determine the buffer size. AudioValueRange bufferRange; dataSize = sizeof( AudioValueRange ); property.mSelector = kAudioDevicePropertyBufferFrameSizeRange; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << deviceId << ")."; errorText_ = errorStream_.str(); return FAILURE; } if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned int) bufferRange.mMinimum; else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned int) bufferRange.mMaximum; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned int) bufferRange.mMinimum; // Set the buffer size. For multiple streams, I'm assuming we only // need to make this setting for the master channel. UInt32 theSize = (UInt32) *bufferSize; dataSize = sizeof( UInt32 ); property.mSelector = kAudioDevicePropertyBufferFrameSize; result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << deviceId << ")."; errorText_ = errorStream_.str(); return FAILURE; } // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! *bufferSize = theSize; if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << deviceId << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.bufferSize = *bufferSize; stream_.nBuffers = 1; // Try to set "hog" mode ... it's not clear to me this is working. if ( options && options->flags & RTAUDIO_HOG_DEVICE ) { pid_t hog_pid; dataSize = sizeof( hog_pid ); property.mSelector = kAudioDevicePropertyHogMode; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!"; errorText_ = errorStream_.str(); return FAILURE; } if ( hog_pid != getpid() ) { hog_pid = getpid(); result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!"; errorText_ = errorStream_.str(); return FAILURE; } } } // Check and if necessary, change the sample rate for the device. Float64 nominalRate; dataSize = sizeof( Float64 ); property.mSelector = kAudioDevicePropertyNominalSampleRate; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate."; errorText_ = errorStream_.str(); return FAILURE; } // Only try to change the sample rate if off by more than 1 Hz. if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) { nominalRate = (Float64) sampleRate; result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << deviceId << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Now wait until the reported nominal rate is what we just set. UInt32 microCounter = 0; Float64 reportedRate = 0.0; while ( reportedRate != nominalRate ) { microCounter += 5000; if ( microCounter > 2000000 ) break; usleep( 5000 ); result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &reportedRate ); } if ( microCounter > 2000000 ) { errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << deviceId << ")."; errorText_ = errorStream_.str(); return FAILURE; } } // Now set the stream format for all streams. Also, check the // physical format of the device and change that if necessary. AudioStreamBasicDescription description; dataSize = sizeof( AudioStreamBasicDescription ); property.mSelector = kAudioStreamPropertyVirtualFormat; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << deviceId << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Set the sample rate and data format id. However, only make the // change if the sample rate is not within 1.0 of the desired // rate and the format is not linear pcm. bool updateFormat = false; if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) { description.mSampleRate = (Float64) sampleRate; updateFormat = true; } if ( description.mFormatID != kAudioFormatLinearPCM ) { description.mFormatID = kAudioFormatLinearPCM; updateFormat = true; } if ( updateFormat ) { result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << deviceId << ")."; errorText_ = errorStream_.str(); return FAILURE; } } // Now check the physical format. property.mSelector = kAudioStreamPropertyPhysicalFormat; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << deviceId << ")."; errorText_ = errorStream_.str(); return FAILURE; } //std::cout << "Current physical stream format:" << std::endl; //std::cout << " mBitsPerChan = " << description.mBitsPerChannel << std::endl; //std::cout << " aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl; //std::cout << " bytesPerFrame = " << description.mBytesPerFrame << std::endl; //std::cout << " sample rate = " << description.mSampleRate << std::endl; if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) { description.mFormatID = kAudioFormatLinearPCM; //description.mSampleRate = (Float64) sampleRate; AudioStreamBasicDescription testDescription = description; UInt32 formatFlags; // We'll try higher bit rates first and then work our way down. std::vector< std::pair > physicalFormats; formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger; physicalFormats.push_back( std::pair( 32, formatFlags ) ); formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; physicalFormats.push_back( std::pair( 32, formatFlags ) ); physicalFormats.push_back( std::pair( 24, formatFlags ) ); // 24-bit packed formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh ); physicalFormats.push_back( std::pair( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low formatFlags |= kAudioFormatFlagIsAlignedHigh; physicalFormats.push_back( std::pair( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; physicalFormats.push_back( std::pair( 16, formatFlags ) ); physicalFormats.push_back( std::pair( 8, formatFlags ) ); bool setPhysicalFormat = false; for( unsigned int i=0; iflags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; if ( monoMode == true ) stream_.deviceInterleaved[mode] = false; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; if ( streamCount == 1 ) { if ( stream_.nUserChannels[mode] > 1 && stream_.userInterleaved != stream_.deviceInterleaved[mode] ) stream_.doConvertBuffer[mode] = true; } else if ( monoMode && stream_.userInterleaved ) stream_.doConvertBuffer[mode] = true; // Allocate our CoreHandle structure for the stream. CoreHandle *handle = 0; if ( stream_.apiHandle == 0 ) { try { handle = new CoreHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory."; goto error; } if ( pthread_cond_init( &handle->condition, NULL ) ) { errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable."; goto error; } stream_.apiHandle = (void *) handle; } else handle = (CoreHandle *) stream_.apiHandle; handle->iStream[mode] = firstStream; handle->nStreams[mode] = streamCount; handle->id[mode] = id; // Allocate necessary internal buffers. unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory."; goto error; } // If possible, we will make use of the CoreAudio stream buffers as // "device buffers". However, we can't do this if using multiple // streams. if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } stream_.sampleRate = sampleRate; stream_.deviceId[mode] = deviceId; stream_.state = STREAM_STOPPED; stream_.callbackInfo.object = (void *) this; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if ( streamCount > 1 ) setConvertInfo( mode, 0 ); else setConvertInfo( mode, channelOffset ); } if ( mode == INPUT && stream_.mode == OUTPUT && stream_.deviceId[0] == deviceId ) // Only one callback procedure and property listener per device. stream_.mode = DUPLEX; else { #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] ); #else // deprecated in favor of AudioDeviceCreateIOProcID() result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo ); #endif if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << deviceId << ")."; errorText_ = errorStream_.str(); goto error; } if ( stream_.mode == OUTPUT && mode == INPUT ) stream_.mode = DUPLEX; else stream_.mode = mode; // Setup the device property listener for over/underload. property.mSelector = kAudioDeviceProcessorOverload; property.mScope = kAudioObjectPropertyScopeGlobal; result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error setting xrun listener for device (" << deviceId << ")."; errorText_ = errorStream_.str(); goto error; } handle->xrunListenerAdded[mode] = true; // Setup a listener to detect a possible device disconnect. property.mSelector = kAudioDevicePropertyDeviceIsAlive; result = AudioObjectAddPropertyListener( id , &property, streamDisconnectListener, (void *) &stream_.callbackInfo ); if ( result != noErr ) { AudioObjectRemovePropertyListener( id, &property, xrunListener, (void *) handle ); errorStream_ << "RtApiCore::probeDeviceOpen: system error setting disconnect listener for device (" << deviceId << ")."; errorText_ = errorStream_.str(); goto error; } handle->disconnectListenerAdded[mode] = true; } return SUCCESS; error: closeStream(); // this should safely clear out procedures, listeners and memory, even for duplex stream return FAILURE; } void RtApiCore :: closeStream( void ) { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiCore::closeStream(): no open stream to close!"; error( RTAUDIO_WARNING ); return; } CoreHandle *handle = (CoreHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle ) { AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, KAUDIOOBJECTPROPERTYELEMENT }; if ( handle->xrunListenerAdded[0] ) { property.mSelector = kAudioDeviceProcessorOverload; if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) { errorText_ = "RtApiCore::closeStream(): error removing xrun property listener!"; error( RTAUDIO_WARNING ); } } if ( handle->disconnectListenerAdded[0] ) { property.mSelector = kAudioDevicePropertyDeviceIsAlive; if (AudioObjectRemovePropertyListener( handle->id[0], &property, streamDisconnectListener, (void *) &stream_.callbackInfo ) != noErr) { errorText_ = "RtApiCore::closeStream(): error removing disconnect property listener!"; error( RTAUDIO_WARNING ); } } #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) if ( handle->procId[0] ) { if ( stream_.state == STREAM_RUNNING ) AudioDeviceStop( handle->id[0], handle->procId[0] ); AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] ); } #else // deprecated behaviour if ( stream_.state == STREAM_RUNNING ) AudioDeviceStop( handle->id[0], callbackHandler ); AudioDeviceRemoveIOProc( handle->id[0], callbackHandler ); #endif } } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.deviceId[0] != stream_.deviceId[1] ) ) { if ( handle ) { AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, KAUDIOOBJECTPROPERTYELEMENT }; if ( handle->xrunListenerAdded[1] ) { property.mSelector = kAudioDeviceProcessorOverload; if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) { errorText_ = "RtApiCore::closeStream(): error removing xrun property listener!"; error( RTAUDIO_WARNING ); } } if ( handle->disconnectListenerAdded[0] ) { property.mSelector = kAudioDevicePropertyDeviceIsAlive; if (AudioObjectRemovePropertyListener( handle->id[1], &property, streamDisconnectListener, (void *) &stream_.callbackInfo ) != noErr) { errorText_ = "RtApiCore::closeStream(): error removing disconnect property listener!"; error( RTAUDIO_WARNING ); } } #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) if ( handle->procId[1] ) { if ( stream_.state == STREAM_RUNNING ) AudioDeviceStop( handle->id[1], handle->procId[1] ); AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] ); } #else // deprecated behaviour if ( stream_.state == STREAM_RUNNING ) AudioDeviceStop( handle->id[1], callbackHandler ); AudioDeviceRemoveIOProc( handle->id[1], callbackHandler ); #endif } } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } // Destroy pthread condition variable. pthread_cond_signal( &handle->condition ); // signal condition variable in case stopStream is blocked pthread_cond_destroy( &handle->condition ); delete handle; stream_.apiHandle = 0; CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; if ( info->deviceDisconnected ) { errorText_ = "RtApiCore: the stream device was disconnected (and closed)!"; error( RTAUDIO_DEVICE_DISCONNECT ); } clearStreamInfo(); } RtAudioErrorType RtApiCore :: startStream( void ) { if ( stream_.state != STREAM_STOPPED ) { if ( stream_.state == STREAM_RUNNING ) errorText_ = "RtApiCore::startStream(): the stream is already running!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiCore::startStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ OSStatus result = noErr; CoreHandle *handle = (CoreHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) result = AudioDeviceStart( handle->id[0], handle->procId[0] ); #else // deprecated behaviour result = AudioDeviceStart( handle->id[0], callbackHandler ); #endif if ( result != noErr ) { errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.deviceId[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.deviceId[0] != stream_.deviceId[1] ) ) { // Clear user input buffer unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat ); memset( stream_.userBuffer[1], 0, bufferBytes * sizeof(char) ); #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) result = AudioDeviceStart( handle->id[1], handle->procId[1] ); #else // deprecated behaviour result = AudioDeviceStart( handle->id[1], callbackHandler ); #endif if ( result != noErr ) { errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.deviceId[1] << ")."; errorText_ = errorStream_.str(); goto unlock; } } handle->drainCounter = 0; handle->internalDrain = false; stream_.state = STREAM_RUNNING; unlock: if ( result == noErr ) return RTAUDIO_NO_ERROR; return error( RTAUDIO_SYSTEM_ERROR ); } RtAudioErrorType RtApiCore :: stopStream( void ) { if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiCore::stopStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_CLOSED ) errorText_ = "RtApiCore::stopStream(): the stream is closed!"; return error( RTAUDIO_WARNING ); } OSStatus result = noErr; CoreHandle *handle = (CoreHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { handle->drainCounter = 2; pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled } #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) result = AudioDeviceStop( handle->id[0], handle->procId[0] ); #else // deprecated behaviour result = AudioDeviceStop( handle->id[0], callbackHandler ); #endif if ( result != noErr ) { errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.deviceId[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.deviceId[0] != stream_.deviceId[1] ) ) { #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) result = AudioDeviceStop( handle->id[1], handle->procId[1] ); #else // deprecated behaviour result = AudioDeviceStop( handle->id[1], callbackHandler ); #endif if ( result != noErr ) { errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.deviceId[1] << ")."; errorText_ = errorStream_.str(); goto unlock; } } stream_.state = STREAM_STOPPED; unlock: if ( result == noErr ) return RTAUDIO_NO_ERROR; return error( RTAUDIO_SYSTEM_ERROR ); } RtAudioErrorType RtApiCore :: abortStream( void ) { if ( stream_.state != STREAM_RUNNING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiCore::abortStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiCore::abortStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } CoreHandle *handle = (CoreHandle *) stream_.apiHandle; handle->drainCounter = 2; stream_.state = STREAM_STOPPING; return stopStream(); } // This function will be called by a spawned thread when the user // callback function signals that the stream should be stopped or // aborted. It is better to handle it this way because the // callbackEvent() function probably should return before the // AudioDeviceStop() function is called. static void *coreStopStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiCore *object = (RtApiCore *) info->object; object->stopStream(); pthread_exit( NULL ); } bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, const AudioBufferList *inBufferList, const AudioBufferList *outBufferList ) { if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RTAUDIO_WARNING ); return FAILURE; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; CoreHandle *handle = (CoreHandle *) stream_.apiHandle; // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > 3 ) { ThreadHandle threadId; stream_.state = STREAM_STOPPING; if ( handle->internalDrain == true ) pthread_create( &threadId, NULL, coreStopStream, info ); else // external call to stopStream() pthread_cond_signal( &handle->condition ); return SUCCESS; } AudioDeviceID outputDevice = handle->id[0]; // Invoke user callback to get fresh output data UNLESS we are // draining stream or duplex mode AND the input/output devices are // different AND this function is called for the input device. if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) { RtAudioCallback callback = (RtAudioCallback) info->callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, info->userData ); if ( cbReturnValue == 2 ) { abortStream(); return SUCCESS; } else if ( cbReturnValue == 1 ) { handle->drainCounter = 1; handle->internalDrain = true; } } if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) { if ( handle->drainCounter > 1 ) { // write zeros to the output stream if ( handle->nStreams[0] == 1 ) { memset( outBufferList->mBuffers[handle->iStream[0]].mData, 0, outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); } else { // fill multiple streams with zeros for ( unsigned int i=0; inStreams[0]; i++ ) { memset( outBufferList->mBuffers[handle->iStream[0]+i].mData, 0, outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize ); } } } else if ( handle->nStreams[0] == 1 ) { if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData, stream_.userBuffer[0], stream_.convertInfo[0] ); } else { // copy from user buffer memcpy( outBufferList->mBuffers[handle->iStream[0]].mData, stream_.userBuffer[0], outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); } } else { // fill multiple streams Float32 *inBuffer = (Float32 *) stream_.userBuffer[0]; if ( stream_.doConvertBuffer[0] ) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); inBuffer = (Float32 *) stream_.deviceBuffer; } if ( stream_.deviceInterleaved[0] == false ) { // mono mode UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; for ( unsigned int i=0; imBuffers[handle->iStream[0]+i].mData, (void *)&inBuffer[i*stream_.bufferSize], bufferBytes ); } } else { // fill multiple multi-channel streams with interleaved data UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset; Float32 *out, *in; bool inInterleaved = ( stream_.userInterleaved ) ? true : false; UInt32 inChannels = stream_.nUserChannels[0]; if ( stream_.doConvertBuffer[0] ) { inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode inChannels = stream_.nDeviceChannels[0]; } if ( inInterleaved ) inOffset = 1; else inOffset = stream_.bufferSize; channelsLeft = inChannels; for ( unsigned int i=0; inStreams[0]; i++ ) { in = inBuffer; out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData; streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels; outJump = 0; // Account for possible channel offset in first stream if ( i == 0 && stream_.channelOffset[0] > 0 ) { streamChannels -= stream_.channelOffset[0]; outJump = stream_.channelOffset[0]; out += outJump; } // Account for possible unfilled channels at end of the last stream if ( streamChannels > channelsLeft ) { outJump = streamChannels - channelsLeft; streamChannels = channelsLeft; } // Determine input buffer offsets and skips if ( inInterleaved ) { inJump = inChannels; in += inChannels - channelsLeft; } else { inJump = 1; in += (inChannels - channelsLeft) * inOffset; } for ( unsigned int i=0; idrainCounter ) { handle->drainCounter++; goto unlock; } AudioDeviceID inputDevice; inputDevice = handle->id[1]; if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) { if ( handle->nStreams[1] == 1 ) { if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer convertBuffer( stream_.userBuffer[1], (char *) inBufferList->mBuffers[handle->iStream[1]].mData, stream_.convertInfo[1] ); } else { // copy to user buffer memcpy( stream_.userBuffer[1], inBufferList->mBuffers[handle->iStream[1]].mData, inBufferList->mBuffers[handle->iStream[1]].mDataByteSize ); } } else { // read from multiple streams Float32 *outBuffer = (Float32 *) stream_.userBuffer[1]; if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer; if ( stream_.deviceInterleaved[1] == false ) { // mono mode UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize; for ( unsigned int i=0; imBuffers[handle->iStream[1]+i].mData, bufferBytes ); } } else { // read from multiple multi-channel streams UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset; Float32 *out, *in; bool outInterleaved = ( stream_.userInterleaved ) ? true : false; UInt32 outChannels = stream_.nUserChannels[1]; if ( stream_.doConvertBuffer[1] ) { outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode outChannels = stream_.nDeviceChannels[1]; } if ( outInterleaved ) outOffset = 1; else outOffset = stream_.bufferSize; channelsLeft = outChannels; for ( unsigned int i=0; inStreams[1]; i++ ) { out = outBuffer; in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData; streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels; inJump = 0; // Account for possible channel offset in first stream if ( i == 0 && stream_.channelOffset[1] > 0 ) { streamChannels -= stream_.channelOffset[1]; inJump = stream_.channelOffset[1]; in += inJump; } // Account for possible unread channels at end of the last stream if ( streamChannels > channelsLeft ) { inJump = streamChannels - channelsLeft; streamChannels = channelsLeft; } // Determine output buffer offsets and skips if ( outInterleaved ) { outJump = outChannels; out += outChannels - channelsLeft; } else { outJump = 1; out += (outChannels - channelsLeft) * outOffset; } for ( unsigned int i=0; iid[0] == handle->id[1] ) // same device, only one callback RtApi::tickStreamTime(); else if ( deviceId == handle->id[0] ) RtApi::tickStreamTime(); // two devices, only tick on the output callback } else RtApi::tickStreamTime(); // input or output stream only return SUCCESS; } const char* RtApiCore :: getErrorCode( OSStatus code ) { switch( code ) { case kAudioHardwareNotRunningError: return "kAudioHardwareNotRunningError"; case kAudioHardwareUnspecifiedError: return "kAudioHardwareUnspecifiedError"; case kAudioHardwareUnknownPropertyError: return "kAudioHardwareUnknownPropertyError"; case kAudioHardwareBadPropertySizeError: return "kAudioHardwareBadPropertySizeError"; case kAudioHardwareIllegalOperationError: return "kAudioHardwareIllegalOperationError"; case kAudioHardwareBadObjectError: return "kAudioHardwareBadObjectError"; case kAudioHardwareBadDeviceError: return "kAudioHardwareBadDeviceError"; case kAudioHardwareBadStreamError: return "kAudioHardwareBadStreamError"; case kAudioHardwareUnsupportedOperationError: return "kAudioHardwareUnsupportedOperationError"; case kAudioDeviceUnsupportedFormatError: return "kAudioDeviceUnsupportedFormatError"; case kAudioDevicePermissionsError: return "kAudioDevicePermissionsError"; default: return "CoreAudio unknown error"; } } //******************** End of __MACOSX_CORE__ *********************// #endif #if defined(__UNIX_JACK__) // JACK is a low-latency audio server, originally written for the // GNU/Linux operating system and now also ported to OS-X and // Windows. It can connect a number of different applications to an // audio device, as well as allowing them to share audio between // themselves. // // When using JACK with RtAudio, "devices" refer to JACK clients that // have ports connected to the server, while ports correspond to device // channels. The JACK server is typically started in a terminal as // follows: // // .jackd -d alsa -d hw:0 // // or through an interface program such as qjackctl. Many of the // parameters normally set for a stream are fixed by the JACK server // and can be specified when the JACK server is started. In // particular, // // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4 // // specifies a sample rate of 44100 Hz, a buffer size of 512 sample // frames, and number of buffers = 4. Once the server is running, it // is not possible to override these values. If the values are not // specified in the command-line, the JACK server uses default values. // // The JACK server does not have to be running when an instance of // RtApiJack is created, though the function getDeviceCount() will // report 0 devices found until JACK has been started. When no // devices are available (i.e., the JACK server is not running), a // stream cannot be opened. #include #include // A structure to hold various information related to the Jack API // implementation. struct JackHandle { jack_client_t *client; jack_port_t **ports[2]; std::string deviceName[2]; bool xrun[2]; pthread_cond_t condition; int drainCounter; // Tracks callback counts when draining bool internalDrain; // Indicates if stop is initiated from callback or not. JackHandle() :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; } }; std::string escapeJackPortRegex(std::string &str) { const std::string need_escaping = "()[]{}*+?$^.|\\"; std::string escaped_string; for (auto c : str) { if (need_escaping.find(c) != std::string::npos) escaped_string.push_back('\\'); escaped_string.push_back(c); } return escaped_string; } #if !defined(__RTAUDIO_DEBUG__) static void jackSilentError( const char * ) {}; #endif RtApiJack :: RtApiJack() :shouldAutoconnect_(true) { // Nothing to do here. #if !defined(__RTAUDIO_DEBUG__) // Turn off Jack's internal error reporting. jack_set_error_function( &jackSilentError ); #endif } RtApiJack :: ~RtApiJack() { if ( stream_.state != STREAM_CLOSED ) closeStream(); } void RtApiJack :: probeDevices( void ) { // See list of required functionality in RtApi::probeDevices(). // See if we can become a jack client. jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption; jack_status_t *status = NULL; jack_client_t *client = jack_client_open( "RtApiJackProbe", options, status ); if ( client == 0 ) { deviceList_.clear(); // in case the server is shutdown after a previous successful probe errorText_ = "RtApiJack::probeDevices: Jack server not found or connection error!"; //error( RTAUDIO_SYSTEM_ERROR ); error( RTAUDIO_WARNING ); return; } const char **ports; std::string port, previousPort; unsigned int nChannels = 0, nDevices = 0; std::vector portNames; ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 ); if ( ports ) { // Parse the port names up to the first colon (:). size_t iColon = 0; do { port = (char *) ports[ nChannels ]; iColon = port.find(":"); if ( iColon != std::string::npos ) { port = port.substr( 0, iColon ); if ( port != previousPort ) { portNames.push_back( port ); nDevices++; previousPort = port; } } } while ( ports[++nChannels] ); free( ports ); } // Fill or update the deviceList_. unsigned int m, n; for ( n=0; n::iterator it=deviceList_.begin(); it!=deviceList_.end(); ) { for ( m=0; m 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; // Jack always uses 32-bit floats. info.nativeFormats = RTAUDIO_FLOAT32; return true; } static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; RtApiJack *object = (RtApiJack *) info->object; if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1; return 0; } // This function will be called by a spawned thread when the Jack // server signals that it is shutting down. It is necessary to handle // it this way because the jackShutdown() function must return before // the jack_deactivate() function (in closeStream()) will return. static void *jackCloseStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiJack *object = (RtApiJack *) info->object; info->deviceDisconnected = true; object->closeStream(); pthread_exit( NULL ); } /* // Could be used to catch client connections but requires open client. static void jackClientChange( const char *name, int registered, void *infoPointer ) { std::cout << "in jackClientChange, name = " << name << ", registered = " << registered << std::endl; } */ static void jackShutdown( void *infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; RtApiJack *object = (RtApiJack *) info->object; // Check current stream state. If stopped, then we'll assume this // was called as a result of a call to RtApiJack::stopStream (the // deactivation of a client handle causes this function to be called). // If not, we'll assume the Jack server is shutting down or some // other problem occurred and we should close the stream. if ( object->isStreamRunning() == false ) return; ThreadHandle threadId; pthread_create( &threadId, NULL, jackCloseStream, info ); } static int jackXrun( void *infoPointer ) { JackHandle *handle = *((JackHandle **) infoPointer); if ( handle->ports[0] ) handle->xrun[0] = true; if ( handle->ports[1] ) handle->xrun[1] = true; return 0; } bool RtApiJack :: probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { JackHandle *handle = (JackHandle *) stream_.apiHandle; // Look for jack server and try to become a client (only do once per stream). jack_client_t *client = 0; if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) { jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption; jack_status_t *status = NULL; if ( options && !options->streamName.empty() ) client = jack_client_open( options->streamName.c_str(), jackoptions, status ); else client = jack_client_open( "RtApiJack", jackoptions, status ); if ( client == 0 ) { errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!"; error( RTAUDIO_WARNING ); return FAILURE; } } else { // The handle must have been created on an earlier pass. client = handle->client; } std::string deviceName; for ( unsigned int m=0; mflags & RTAUDIO_JACK_DONT_CONNECT)) ) { // Count the available ports containing the client name as device // channels. Jack "input ports" equal RtAudio output channels. unsigned int nChannels = 0; ports = jack_get_ports( client, escapeJackPortRegex(deviceName).c_str(), JACK_DEFAULT_AUDIO_TYPE, flag ); if ( ports ) { while ( ports[ nChannels ] ) nChannels++; free( ports ); } // Compare the jack ports for specified client to the requested number of channels. if ( nChannels < (channels + firstChannel) ) { errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << deviceName << ")."; errorText_ = errorStream_.str(); return FAILURE; } } // Check the jack server sample rate. unsigned int jackRate = jack_get_sample_rate( client ); if ( sampleRate != jackRate ) { jack_client_close( client ); errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.sampleRate = jackRate; // Get the latency of the JACK port. ports = jack_get_ports( client, escapeJackPortRegex(deviceName).c_str(), JACK_DEFAULT_AUDIO_TYPE, flag ); if ( ports[ firstChannel ] ) { // Added by Ge Wang jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency); // the range (usually the min and max are equal) jack_latency_range_t latrange; latrange.min = latrange.max = 0; // get the latency range jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange ); // be optimistic, use the min! stream_.latency[mode] = latrange.min; //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) ); } free( ports ); // The jack server always uses 32-bit floating-point data. stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; stream_.userFormat = format; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; // Jack always uses non-interleaved buffers. stream_.deviceInterleaved[mode] = false; // Jack always provides host byte-ordered data. stream_.doByteSwap[mode] = false; // Get the buffer size. The buffer size and number of buffers // (periods) is set when the jack server is started. stream_.bufferSize = (int) jack_get_buffer_size( client ); *bufferSize = stream_.bufferSize; stream_.nDeviceChannels[mode] = channels; stream_.nUserChannels[mode] = channels; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate our JackHandle structure for the stream. if ( handle == 0 ) { try { handle = new JackHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory."; goto error; } if ( pthread_cond_init(&handle->condition, NULL) ) { errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable."; goto error; } stream_.apiHandle = (void *) handle; handle->client = client; } handle->deviceName[mode] = deviceName; // Allocate necessary internal buffers. unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; if ( mode == OUTPUT ) bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); else { // mode == INPUT bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] ); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( bufferBytes < bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } // Allocate memory for the Jack ports (channels) identifiers. handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels ); if ( handle->ports[mode] == NULL ) { errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory."; goto error; } stream_.channelOffset[mode] = firstChannel; stream_.state = STREAM_STOPPED; stream_.callbackInfo.object = (void *) this; if ( stream_.mode == OUTPUT && mode == INPUT ) // We had already set up the stream for output. stream_.mode = DUPLEX; else { stream_.mode = mode; jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo ); jack_set_xrun_callback( handle->client, jackXrun, (void *) &stream_.apiHandle ); jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); //jack_set_client_registration_callback( handle->client, jackClientChange, (void *) &stream_.callbackInfo ); } // Register our ports. char label[64]; if ( mode == OUTPUT ) { for ( unsigned int i=0; iports[0][i] = jack_port_register( handle->client, (const char *)label, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); } } else { for ( unsigned int i=0; iports[1][i] = jack_port_register( handle->client, (const char *)label, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); } } // Setup the buffer conversion information structure. We don't use // buffers to do channel offsets, so we override that parameter // here. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 ); if ( options && options->flags & RTAUDIO_JACK_DONT_CONNECT ) shouldAutoconnect_ = false; return SUCCESS; error: if ( handle ) { pthread_cond_destroy( &handle->condition ); jack_client_close( handle->client ); if ( handle->ports[0] ) free( handle->ports[0] ); if ( handle->ports[1] ) free( handle->ports[1] ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } return FAILURE; } void RtApiJack :: closeStream( void ) { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiJack::closeStream(): no open stream to close!"; error( RTAUDIO_WARNING ); return; } JackHandle *handle = (JackHandle *) stream_.apiHandle; if ( handle ) { if ( stream_.state == STREAM_RUNNING ) jack_deactivate( handle->client ); if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { for ( unsigned int i=0; iclient, handle->ports[0][i] ); } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { for ( unsigned int i=0; iclient, handle->ports[1][i] ); } jack_client_close( handle->client ); if ( handle->ports[0] ) free( handle->ports[0] ); if ( handle->ports[1] ) free( handle->ports[1] ); pthread_cond_destroy( &handle->condition ); delete handle; stream_.apiHandle = 0; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; if ( info->deviceDisconnected ) { errorText_ = "RtApiJack: the Jack server is shutting down this client ... stream stopped and closed!"; error( RTAUDIO_DEVICE_DISCONNECT ); } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } clearStreamInfo(); } RtAudioErrorType RtApiJack :: startStream( void ) { if ( stream_.state != STREAM_STOPPED ) { if ( stream_.state == STREAM_RUNNING ) errorText_ = "RtApiJack::startStream(): the stream is already running!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiJack::startStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ JackHandle *handle = (JackHandle *) stream_.apiHandle; int result = jack_activate( handle->client ); if ( result ) { errorText_ = "RtApiJack::startStream(): unable to activate JACK client!"; goto unlock; } const char **ports; // Get the list of available ports. if ( shouldAutoconnect_ && (stream_.mode == OUTPUT || stream_.mode == DUPLEX) ) { ports = jack_get_ports( handle->client, escapeJackPortRegex(handle->deviceName[0]).c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput); if ( ports == NULL) { errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!"; goto unlock; } // Now make the port connections. Since RtAudio wasn't designed to // allow the user to select particular channels of a device, we'll // just open the first "nChannels" ports with offset. for ( unsigned int i=0; iclient, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] ); if ( result ) { free( ports ); errorText_ = "RtApiJack::startStream(): error connecting output ports!"; goto unlock; } } free(ports); } if ( shouldAutoconnect_ && (stream_.mode == INPUT || stream_.mode == DUPLEX) ) { ports = jack_get_ports( handle->client, escapeJackPortRegex(handle->deviceName[1]).c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput ); if ( ports == NULL) { errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!"; goto unlock; } // Now make the port connections. See note above. for ( unsigned int i=0; iclient, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) ); if ( result ) { free( ports ); errorText_ = "RtApiJack::startStream(): error connecting input ports!"; goto unlock; } } free(ports); } handle->drainCounter = 0; handle->internalDrain = false; stream_.state = STREAM_RUNNING; unlock: if ( result == 0 ) return RTAUDIO_NO_ERROR; return error( RTAUDIO_SYSTEM_ERROR ); } RtAudioErrorType RtApiJack :: stopStream( void ) { if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiJack::stopStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_CLOSED ) errorText_ = "RtApiJack::stopStream(): the stream is closed!"; return error( RTAUDIO_WARNING ); } JackHandle *handle = (JackHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { handle->drainCounter = 2; pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled } } jack_deactivate( handle->client ); stream_.state = STREAM_STOPPED; return RTAUDIO_NO_ERROR; } RtAudioErrorType RtApiJack :: abortStream( void ) { if ( stream_.state != STREAM_RUNNING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiJack::abortStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiJack::abortStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } JackHandle *handle = (JackHandle *) stream_.apiHandle; handle->drainCounter = 2; return stopStream(); } // This function will be called by a spawned thread when the user // callback function signals that the stream should be stopped or // aborted. It is necessary to handle it this way because the // callbackEvent() function must return before the jack_deactivate() // function will return. static void *jackStopStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiJack *object = (RtApiJack *) info->object; object->stopStream(); pthread_exit( NULL ); } bool RtApiJack :: callbackEvent( unsigned long nframes ) { if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiJack::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RTAUDIO_WARNING ); return FAILURE; } if ( stream_.bufferSize != nframes ) { errorText_ = "RtApiJack::callbackEvent(): the JACK buffer size has changed ... cannot process!"; error( RTAUDIO_WARNING ); return FAILURE; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; JackHandle *handle = (JackHandle *) stream_.apiHandle; // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > 3 ) { ThreadHandle threadId; stream_.state = STREAM_STOPPING; if ( handle->internalDrain == true ) pthread_create( &threadId, NULL, jackStopStream, info ); else // external call to stopStream() pthread_cond_signal( &handle->condition ); return SUCCESS; } // Invoke user callback first, to get fresh output data. if ( handle->drainCounter == 0 ) { RtAudioCallback callback = (RtAudioCallback) info->callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, info->userData ); if ( cbReturnValue == 2 ) { stream_.state = STREAM_STOPPING; handle->drainCounter = 2; ThreadHandle id; pthread_create( &id, NULL, jackStopStream, info ); return SUCCESS; } else if ( cbReturnValue == 1 ) { handle->drainCounter = 1; handle->internalDrain = true; } } jack_default_audio_sample_t *jackbuffer; unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t ); if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter > 1 ) { // write zeros to the output stream for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); memset( jackbuffer, 0, bufferBytes ); } } else if ( stream_.doConvertBuffer[0] ) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); } } else { // no buffer conversion for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes ); } } } // Don't bother draining input if ( handle->drainCounter ) { handle->drainCounter++; goto unlock; } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { if ( stream_.doConvertBuffer[1] ) { for ( unsigned int i=0; iports[1][i], (jack_nframes_t) nframes ); memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes ); } convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); } else { // no buffer conversion for ( unsigned int i=0; iports[1][i], (jack_nframes_t) nframes ); memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes ); } } } unlock: RtApi::tickStreamTime(); return SUCCESS; } //******************** End of __UNIX_JACK__ *********************// #endif #if defined(__WINDOWS_ASIO__) // ASIO API on Windows // The ASIO API is designed around a callback scheme, so this // implementation is similar to that used for OS-X CoreAudio and unix // Jack. The primary constraint with ASIO is that it only allows // access to a single driver at a time. Thus, it is not possible to // have more than one simultaneous RtAudio stream. // // This implementation also requires a number of external ASIO files // and a few global variables. The ASIO callback scheme does not // allow for the passing of user data, so we must create a global // pointer to our callbackInfo structure. // // On unix systems, we make use of a pthread condition variable. // Since there is no equivalent in Windows, I hacked something based // on information found in // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html. #include "asiosys.h" #include "asio.h" #include "iasiothiscallresolver.h" #include "asiodrivers.h" #include static AsioDrivers drivers; static ASIOCallbacks asioCallbacks; static ASIODriverInfo driverInfo; static CallbackInfo *asioCallbackInfo; static bool asioXRun; static bool streamOpen = false; // Tracks whether any instance of RtAudio has a stream open struct AsioHandle { int drainCounter; // Tracks callback counts when draining bool internalDrain; // Indicates if stop is initiated from callback or not. ASIOBufferInfo *bufferInfos; HANDLE condition; AsioHandle() :drainCounter(0), internalDrain(false), bufferInfos(0) {} }; // Function declarations (definitions at end of section) static const char* getAsioErrorString( ASIOError result ); static void sampleRateChanged( ASIOSampleRate sRate ); static long asioMessages( long selector, long value, void* message, double* opt ); RtApiAsio :: RtApiAsio() { // ASIO cannot run on a multi-threaded apartment. You can call // CoInitialize beforehand, but it must be for apartment threading // (in which case, CoInitilialize will return S_FALSE here). coInitialized_ = false; HRESULT hr = CoInitialize( NULL ); if ( FAILED(hr) ) { errorText_ = "RtApiAsio::ASIO requires a single-threaded apartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)"; error( RTAUDIO_WARNING ); } coInitialized_ = true; // Check whether another RtAudio instance has an ASIO stream open. if ( streamOpen ) { errorText_ = "RtApiAsio(): Another RtAudio ASIO stream is open, functionality may be limited."; error( RTAUDIO_WARNING ); } else drivers.removeCurrentDriver(); driverInfo.asioVersion = 2; // See note in DirectSound implementation about GetDesktopWindow(). driverInfo.sysRef = GetForegroundWindow(); } RtApiAsio :: ~RtApiAsio() { if ( stream_.state != STREAM_CLOSED ) closeStream(); if ( coInitialized_ ) CoUninitialize(); } void RtApiAsio :: probeDevices( void ) { // See list of required functionality in RtApi::probeDevices(). if ( streamOpen ) { errorText_ = "RtApiAsio::probeDevices: Another RtAudio ASIO stream is open, cannot probe devices."; error( RTAUDIO_WARNING ); return; } unsigned int nDevices = drivers.asioGetNumDev(); if ( nDevices == 0 ) { deviceList_.clear(); return; } char tmp[32]; std::vector< std::string > driverNames; unsigned int n, m; for ( n=0; n::iterator it=deviceList_.begin(); it!=deviceList_.end(); ) { for ( m=0; m 0) { getDefaultInputDevice(); getDefaultOutputDevice(); } } bool RtApiAsio :: probeDeviceInfo( RtAudio::DeviceInfo &info ) { if ( !drivers.loadDriver( const_cast(info.name.c_str()) ) ) { errorStream_ << "RtApiAsio::probeDeviceInfo: unable to load driver (" << info.name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } ASIOError result = ASIOInit( &driverInfo ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); errorStream_ << "RtApiAsio::probeDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << info.name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Determine the device channel information. long inputChannels, outputChannels; result = ASIOGetChannels( &inputChannels, &outputChannels ); if ( result != ASE_OK ) { ASIOExit(); drivers.removeCurrentDriver(); errorStream_ << "RtApiAsio::probeDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << info.name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } info.outputChannels = outputChannels; info.inputChannels = inputChannels; if ( info.outputChannels > 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; // Determine the supported sample rates. info.sampleRates.clear(); for ( unsigned int i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[i]; } } // Determine supported data types ... just check first channel and assume rest are the same. ASIOChannelInfo channelInfo; channelInfo.channel = 0; channelInfo.isInput = true; if ( info.inputChannels <= 0 ) channelInfo.isInput = false; result = ASIOGetChannelInfo( &channelInfo ); if ( result != ASE_OK ) { ASIOExit(); drivers.removeCurrentDriver(); errorStream_ << "RtApiAsio::probeDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << info.name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } info.nativeFormats = 0; if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) info.nativeFormats |= RTAUDIO_SINT16; else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) info.nativeFormats |= RTAUDIO_SINT32; else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) info.nativeFormats |= RTAUDIO_FLOAT32; else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) info.nativeFormats |= RTAUDIO_FLOAT64; else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) info.nativeFormats |= RTAUDIO_SINT24; ASIOExit(); drivers.removeCurrentDriver(); return true; } static void bufferSwitch( long index, ASIOBool /*processNow*/ ) { RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object; object->callbackEvent( index ); } bool RtApiAsio :: probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { bool isDuplexInput = mode == INPUT && stream_.mode == OUTPUT; // For ASIO, a duplex stream MUST use the same driver. if ( isDuplexInput && stream_.deviceId[0] != deviceId ) { errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!"; return FAILURE; } std::string driverName; for ( unsigned int m=0; m(driverName.c_str()) ) ) { errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ")."; errorText_ = errorStream_.str(); return FAILURE; } result = ASIOInit( &driverInfo ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ")."; errorText_ = errorStream_.str(); return FAILURE; } } bool buffersAllocated = false; AsioHandle *handle = (AsioHandle *) stream_.apiHandle; unsigned int nChannels; // Check the device channel count. long inputChannels, outputChannels; result = ASIOGetChannels( &inputChannels, &outputChannels ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; errorText_ = errorStream_.str(); goto error; } if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) || ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ")."; errorText_ = errorStream_.str(); goto error; } stream_.nDeviceChannels[mode] = channels; stream_.nUserChannels[mode] = channels; stream_.channelOffset[mode] = firstChannel; // Verify the sample rate is supported. result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ")."; errorText_ = errorStream_.str(); goto error; } // Get the current sample rate ASIOSampleRate currentRate; result = ASIOGetSampleRate( ¤tRate ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate."; errorText_ = errorStream_.str(); goto error; } // Set the sample rate only if necessary if ( currentRate != sampleRate ) { result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ")."; errorText_ = errorStream_.str(); goto error; } } // Determine the driver data type. ASIOChannelInfo channelInfo; channelInfo.channel = 0; if ( mode == OUTPUT ) channelInfo.isInput = false; else channelInfo.isInput = true; result = ASIOGetChannelInfo( &channelInfo ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format."; errorText_ = errorStream_.str(); goto error; } // Assuming WINDOWS host is always little-endian. stream_.doByteSwap[mode] = false; stream_.userFormat = format; stream_.deviceFormat[mode] = 0; if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) { stream_.deviceFormat[mode] = RTAUDIO_SINT16; if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) { stream_.deviceFormat[mode] = RTAUDIO_SINT32; if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) { stream_.deviceFormat[mode] = RTAUDIO_SINT24; if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true; } if ( stream_.deviceFormat[mode] == 0 ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); goto error; } // Set the buffer size. For a duplex stream, this will end up // setting the buffer size based on the input constraints, which // should be ok. long minSize, maxSize, preferSize, granularity; result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size."; errorText_ = errorStream_.str(); goto error; } if ( isDuplexInput ) { // When this is the duplex input (output was opened before), then we have to use the same // buffersize as the output, because it might use the preferred buffer size, which most // likely wasn't passed as input to this. The buffer sizes have to be identically anyway, // So instead of throwing an error, make them equal. The caller uses the reference // to the "bufferSize" param as usual to set up processing buffers. *bufferSize = stream_.bufferSize; } else { if ( *bufferSize == 0 ) *bufferSize = preferSize; else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; else if ( granularity == -1 ) { // Make sure bufferSize is a power of two. int log2_of_min_size = 0; int log2_of_max_size = 0; for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) { if ( minSize & ((long)1 << i) ) log2_of_min_size = i; if ( maxSize & ((long)1 << i) ) log2_of_max_size = i; } long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) ); int min_delta_num = log2_of_min_size; for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) { long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) ); if (current_delta < min_delta) { min_delta = current_delta; min_delta_num = i; } } *bufferSize = ( (unsigned int)1 << min_delta_num ); if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; } else if ( granularity != 0 ) { // Set to an even multiple of granularity, rounding up. *bufferSize = (*bufferSize + granularity-1) / granularity * granularity; } } /* // we don't use it anymore, see above! // Just left it here for the case... if ( isDuplexInput && stream_.bufferSize != *bufferSize ) { errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!"; goto error; } */ stream_.bufferSize = *bufferSize; stream_.nBuffers = 2; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; // ASIO always uses non-interleaved buffers. stream_.deviceInterleaved[mode] = false; // Allocate, if necessary, our AsioHandle structure for the stream. if ( handle == 0 ) { try { handle = new AsioHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory."; goto error; } handle->bufferInfos = 0; // Create a manual-reset event. handle->condition = CreateEvent( NULL, // no security TRUE, // manual-reset FALSE, // non-signaled initially NULL ); // unnamed stream_.apiHandle = (void *) handle; } // Create the ASIO internal buffers. Since RtAudio sets up input // and output separately, we'll have to dispose of previously // created output buffers for a duplex stream. if ( mode == INPUT && stream_.mode == OUTPUT ) { ASIODisposeBuffers(); if ( handle->bufferInfos ) free( handle->bufferInfos ); } // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. unsigned int i; nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) ); if ( handle->bufferInfos == NULL ) { errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ")."; errorText_ = errorStream_.str(); goto error; } ASIOBufferInfo *infos; infos = handle->bufferInfos; for ( i=0; iisInput = ASIOFalse; infos->channelNum = i + stream_.channelOffset[0]; infos->buffers[0] = infos->buffers[1] = 0; } for ( i=0; iisInput = ASIOTrue; infos->channelNum = i + stream_.channelOffset[1]; infos->buffers[0] = infos->buffers[1] = 0; } // prepare for callbacks stream_.sampleRate = sampleRate; stream_.deviceId[mode] = deviceId; stream_.mode = isDuplexInput ? DUPLEX : mode; // store this class instance before registering callbacks, that are going to use it asioCallbackInfo = &stream_.callbackInfo; stream_.callbackInfo.object = (void *) this; // Set up the ASIO callback structure and create the ASIO data buffers. asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.asioMessage = &asioMessages; asioCallbacks.bufferSwitchTimeInfo = NULL; result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); if ( result != ASE_OK ) { // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges // but only accept the preferred buffer size as parameter for ASIOCreateBuffers (e.g. Creative's ASIO driver). // In that case, let's be naïve and try that instead. *bufferSize = preferSize; stream_.bufferSize = *bufferSize; result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); } if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers."; errorText_ = errorStream_.str(); goto error; } buffersAllocated = true; stream_.state = STREAM_STOPPED; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( isDuplexInput && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } // Determine device latencies long inputLatency, outputLatency; result = ASIOGetLatencies( &inputLatency, &outputLatency ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING); // warn but don't fail } else { stream_.latency[0] = outputLatency; stream_.latency[1] = inputLatency; } // Setup the buffer conversion information structure. We don't use // buffers to do channel offsets, so we override that parameter // here. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 ); streamOpen = true; return SUCCESS; error: if ( !isDuplexInput ) { // the cleanup for error in the duplex input, is done by RtApi::openStream // So we clean up for single channel only if ( buffersAllocated ) ASIODisposeBuffers(); ASIOExit(); drivers.removeCurrentDriver(); if ( handle ) { CloseHandle( handle->condition ); if ( handle->bufferInfos ) free( handle->bufferInfos ); delete handle; stream_.apiHandle = 0; } if ( stream_.userBuffer[mode] ) { free( stream_.userBuffer[mode] ); stream_.userBuffer[mode] = 0; } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } } return FAILURE; } void RtApiAsio :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAsio::closeStream(): no open stream to close!"; error( RTAUDIO_WARNING ); return; } if ( stream_.state == STREAM_RUNNING ) { stream_.state = STREAM_STOPPED; ASIOStop(); } stream_.state = STREAM_CLOSED; CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; if ( info->deviceDisconnected ) { // This could be either a disconnect or a sample rate change. errorText_ = "RtApiAsio: the streaming device was disconnected or the sample rate changed, closing stream!"; error( RTAUDIO_DEVICE_DISCONNECT ); } ASIODisposeBuffers(); ASIOExit(); drivers.removeCurrentDriver(); AsioHandle *handle = (AsioHandle *) stream_.apiHandle; if ( handle ) { CloseHandle( handle->condition ); if ( handle->bufferInfos ) free( handle->bufferInfos ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } clearStreamInfo(); streamOpen = false; //stream_.mode = UNINITIALIZED; //stream_.state = STREAM_CLOSED; } RtAudioErrorType RtApiAsio :: startStream() { if ( stream_.state != STREAM_STOPPED ) { if ( stream_.state == STREAM_RUNNING ) errorText_ = "RtApiAsio::startStream(): the stream is already running!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiAsio::startStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ AsioHandle *handle = (AsioHandle *) stream_.apiHandle; ASIOError result = ASIOStart(); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device."; errorText_ = errorStream_.str(); goto unlock; } handle->drainCounter = 0; handle->internalDrain = false; ResetEvent( handle->condition ); stream_.state = STREAM_RUNNING; asioXRun = false; unlock: if ( result == ASE_OK ) return RTAUDIO_NO_ERROR; return error( RTAUDIO_SYSTEM_ERROR ); } RtAudioErrorType RtApiAsio :: stopStream() { if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_CLOSED ) errorText_ = "RtApiAsio::stopStream(): the stream is closed!"; return error( RTAUDIO_WARNING ); } AsioHandle *handle = (AsioHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { handle->drainCounter = 2; WaitForSingleObject( handle->condition, INFINITE ); // block until signaled } } stream_.state = STREAM_STOPPED; ASIOError result = ASIOStop(); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device."; errorText_ = errorStream_.str(); } if ( result == ASE_OK ) return RTAUDIO_NO_ERROR; return error( RTAUDIO_SYSTEM_ERROR ); } RtAudioErrorType RtApiAsio :: abortStream() { if ( stream_.state != STREAM_RUNNING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiAsio::abortStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } // The following lines were commented-out because some behavior was // noted where the device buffers need to be zeroed to avoid // continuing sound, even when the device buffers are completely // disposed. So now, calling abort is the same as calling stop. // AsioHandle *handle = (AsioHandle *) stream_.apiHandle; // handle->drainCounter = 2; stopStream(); return RTAUDIO_NO_ERROR; } // This function will be called by a spawned thread when: 1. The user // callback function signals that the stream should be stopped or // aborted; or 2. When a signal is received indicating that the device // sample rate has changed or it has been disconnected. It is // necessary to handle it this way because the callbackEvent() or // signaling function must return before the ASIOStop() function will // return (or the driver can be removed). static unsigned __stdcall asioStopStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiAsio *object = (RtApiAsio *) info->object; if ( info->deviceDisconnected == false ) object->stopStream(); // drain the stream else object->closeStream(); // disconnect or sample rate change ... close the stream _endthreadex( 0 ); return 0; } bool RtApiAsio :: callbackEvent( long bufferIndex ) { if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RTAUDIO_WARNING ); return FAILURE; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; AsioHandle *handle = (AsioHandle *) stream_.apiHandle; // Check if we were draining the stream and signal if finished. if ( handle->drainCounter > 3 ) { stream_.state = STREAM_STOPPING; if ( handle->internalDrain == false ) SetEvent( handle->condition ); else { // spawn a thread to stop the stream unsigned threadId; stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream, &stream_.callbackInfo, 0, &threadId ); } return SUCCESS; } // Invoke user callback to get fresh output data UNLESS we are // draining stream. if ( handle->drainCounter == 0 ) { RtAudioCallback callback = (RtAudioCallback) info->callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && asioXRun == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; asioXRun = false; } if ( stream_.mode != OUTPUT && asioXRun == true ) { status |= RTAUDIO_INPUT_OVERFLOW; asioXRun = false; } int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, info->userData ); if ( cbReturnValue == 2 ) { stream_.state = STREAM_STOPPING; handle->drainCounter = 2; unsigned threadId; stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream, &stream_.callbackInfo, 0, &threadId ); return SUCCESS; } else if ( cbReturnValue == 1 ) { handle->drainCounter = 1; handle->internalDrain = true; } } unsigned int nChannels, bufferBytes, i, j; nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] ); if ( handle->drainCounter > 1 ) { // write zeros to the output stream for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes ); } } else if ( stream_.doConvertBuffer[0] ) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); if ( stream_.doByteSwap[0] ) byteSwapBuffer( stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[0], stream_.deviceFormat[0] ); for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) memcpy( handle->bufferInfos[i].buffers[bufferIndex], &stream_.deviceBuffer[j++*bufferBytes], bufferBytes ); } } else { if ( stream_.doByteSwap[0] ) byteSwapBuffer( stream_.userBuffer[0], stream_.bufferSize * stream_.nUserChannels[0], stream_.userFormat ); for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) memcpy( handle->bufferInfos[i].buffers[bufferIndex], &stream_.userBuffer[0][bufferBytes*j++], bufferBytes ); } } } // Don't bother draining input if ( handle->drainCounter ) { handle->drainCounter++; goto unlock; } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]); if (stream_.doConvertBuffer[1]) { // Always interleave ASIO input data. for ( i=0, j=0; ibufferInfos[i].isInput == ASIOTrue ) memcpy( &stream_.deviceBuffer[j++*bufferBytes], handle->bufferInfos[i].buffers[bufferIndex], bufferBytes ); } if ( stream_.doByteSwap[1] ) byteSwapBuffer( stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[1], stream_.deviceFormat[1] ); convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); } else { for ( i=0, j=0; ibufferInfos[i].isInput == ASIOTrue ) { memcpy( &stream_.userBuffer[1][bufferBytes*j++], handle->bufferInfos[i].buffers[bufferIndex], bufferBytes ); } } if ( stream_.doByteSwap[1] ) byteSwapBuffer( stream_.userBuffer[1], stream_.bufferSize * stream_.nUserChannels[1], stream_.userFormat ); } } unlock: // The following call was suggested by Malte Clasen. While the API // documentation indicates it should not be required, some device // drivers apparently do not function correctly without it. ASIOOutputReady(); RtApi::tickStreamTime(); return SUCCESS; } static void sampleRateChanged( ASIOSampleRate sRate ) { // The ASIO documentation says that this usually only happens during // external sync. Audio processing is not stopped by the driver, // actual sample rate might not have even changed, maybe only the // sample rate status of an AES/EBU or S/PDIF digital input at the // audio device. RtApi *object = (RtApi *) asioCallbackInfo->object; if ( object->getStreamSampleRate() != sRate ) { asioCallbackInfo->deviceDisconnected = true; // flag for either rate change or disconnect unsigned threadId; asioCallbackInfo->thread = _beginthreadex( NULL, 0, &asioStopStream, asioCallbackInfo, 0, &threadId ); } } static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ ) { long ret = 0; switch( selector ) { case kAsioSelectorSupported: if ( value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest || value == kAsioLatenciesChanged // The following three were added for ASIO 2.0, you don't // necessarily have to support them. || value == kAsioSupportsTimeInfo || value == kAsioSupportsTimeCode || value == kAsioSupportsInputMonitor) ret = 1L; break; case kAsioResetRequest: // This message is received when a device is disconnected (and // perhaps when the sample rate changes). It indicates that the // driver should be reset, which is accomplished by calling // ASIOStop(), ASIODisposeBuffers() and removing the driver. But // since this message comes from the driver, we need to let this // function return before attempting to close the stream and // remove the driver. Thus, we invoke a thread to initiate the // stream closing. asioCallbackInfo->deviceDisconnected = true; // flag for either rate change or disconnect unsigned threadId; asioCallbackInfo->thread = _beginthreadex( NULL, 0, &asioStopStream, asioCallbackInfo, 0, &threadId ); //std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl; ret = 1L; break; case kAsioResyncRequest: // This informs the application that the driver encountered some // non-fatal data loss. It is used for synchronization purposes // of different media. Added mainly to work around the Win16Mutex // problems in Windows 95/98 with the Windows Multimedia system, // which could lose data because the Mutex was held too long by // another thread. However a driver can issue it in other // situations, too. // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl; asioXRun = true; ret = 1L; break; case kAsioLatenciesChanged: // This will inform the host application that the drivers were // latencies changed. Beware, it this does not mean that the // buffer sizes have changed! You might need to update internal // delay data. std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl; ret = 1L; break; case kAsioEngineVersion: // Return the supported ASIO version of the host application. If // a host application does not implement this selector, ASIO 1.0 // is assumed by the driver. ret = 2L; break; case kAsioSupportsTimeInfo: // Informs the driver whether the // asioCallbacks.bufferSwitchTimeInfo() callback is supported. // For compatibility with ASIO 1.0 drivers the host application // should always support the "old" bufferSwitch method, too. ret = 0; break; case kAsioSupportsTimeCode: // Informs the driver whether application is interested in time // code info. If an application does not need to know about time // code, the driver has less work to do. ret = 0; break; } return ret; } static const char* getAsioErrorString( ASIOError result ) { struct Messages { ASIOError value; const char*message; }; static const Messages m[] = { { ASE_NotPresent, "Hardware input or output is not present or available." }, { ASE_HWMalfunction, "Hardware is malfunctioning." }, { ASE_InvalidParameter, "Invalid input parameter." }, { ASE_InvalidMode, "Invalid mode." }, { ASE_SPNotAdvancing, "Sample position not advancing." }, { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." }, { ASE_NoMemory, "Not enough memory to complete the request." } }; for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i ) if ( m[i].value == result ) return m[i].message; return "Unknown error."; } //******************** End of __WINDOWS_ASIO__ *********************// #endif #if defined(__WINDOWS_WASAPI__) // Windows WASAPI API // Authored by Marcus Tomlinson , April 2014 // Updates for new device selection scheme by Gary Scavone, January 2022 // - Introduces support for the Windows WASAPI API // - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required // - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface // - Includes automatic internal conversion of sample rate and buffer size between hardware and the user #ifndef INITGUID #define INITGUID #endif #include #include #include #include #include #include #include #include #include #ifndef MF_E_TRANSFORM_NEED_MORE_INPUT #define MF_E_TRANSFORM_NEED_MORE_INPUT _HRESULT_TYPEDEF_(0xc00d6d72) #endif #ifndef MFSTARTUP_NOSOCKET #define MFSTARTUP_NOSOCKET 0x1 #endif #ifdef _MSC_VER #pragma comment( lib, "ksuser" ) #pragma comment( lib, "mfplat.lib" ) #pragma comment( lib, "mfuuid.lib" ) #pragma comment( lib, "wmcodecdspuuid" ) #endif //============================================================================= #define SAFE_RELEASE( objectPtr )\ if ( objectPtr )\ {\ objectPtr->Release();\ objectPtr = NULL;\ } typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex ); #ifndef __IAudioClient3_INTERFACE_DEFINED__ MIDL_INTERFACE( "00000000-0000-0000-0000-000000000000" ) IAudioClient3 { virtual HRESULT GetSharedModeEnginePeriod( WAVEFORMATEX*, UINT32*, UINT32*, UINT32*, UINT32* ) = 0; virtual HRESULT InitializeSharedAudioStream( DWORD, UINT32, WAVEFORMATEX*, LPCGUID ) = 0; virtual HRESULT Release() = 0; }; #ifdef __CRT_UUID_DECL __CRT_UUID_DECL( IAudioClient3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ) #endif #endif //----------------------------------------------------------------------------- // WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size. // Therefore we must perform all necessary conversions to user buffers in order to satisfy these // requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to // provide intermediate storage for read / write synchronization. class WasapiBuffer { public: WasapiBuffer() : buffer_( NULL ), bufferSize_( 0 ), inIndex_( 0 ), outIndex_( 0 ) {} ~WasapiBuffer() { free( buffer_ ); } // sets the length of the internal ring buffer void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) { free( buffer_ ); buffer_ = ( char* ) calloc( bufferSize, formatBytes ); bufferSize_ = bufferSize; inIndex_ = 0; outIndex_ = 0; } // attempt to push a buffer into the ring buffer at the current "in" index bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format ) { if ( !buffer || // incoming buffer is NULL bufferSize == 0 || // incoming buffer has no data bufferSize > bufferSize_ ) // incoming buffer too large { return false; } unsigned int relOutIndex = outIndex_; unsigned int inIndexEnd = inIndex_ + bufferSize; if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) { relOutIndex += bufferSize_; } // the "IN" index CAN BEGIN at the "OUT" index // the "IN" index CANNOT END at the "OUT" index if ( inIndex_ < relOutIndex && inIndexEnd >= relOutIndex ) { return false; // not enough space between "in" index and "out" index } // copy buffer from external to internal int fromZeroSize = inIndex_ + bufferSize - bufferSize_; fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize; int fromInSize = bufferSize - fromZeroSize; switch( format ) { case RTAUDIO_SINT8: memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) ); memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) ); break; case RTAUDIO_SINT16: memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) ); memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) ); break; case RTAUDIO_SINT24: memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) ); memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) ); break; case RTAUDIO_SINT32: memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) ); memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) ); break; case RTAUDIO_FLOAT32: memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) ); memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) ); break; case RTAUDIO_FLOAT64: memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) ); memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) ); break; } // update "in" index inIndex_ += bufferSize; inIndex_ %= bufferSize_; return true; } // attempt to pull a buffer from the ring buffer from the current "out" index bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format ) { if ( !buffer || // incoming buffer is NULL bufferSize == 0 || // incoming buffer has no data bufferSize > bufferSize_ ) // incoming buffer too large { return false; } unsigned int relInIndex = inIndex_; unsigned int outIndexEnd = outIndex_ + bufferSize; if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) { relInIndex += bufferSize_; } // the "OUT" index CANNOT BEGIN at the "IN" index // the "OUT" index CAN END at the "IN" index if ( outIndex_ <= relInIndex && outIndexEnd > relInIndex ) { return false; // not enough space between "out" index and "in" index } // copy buffer from internal to external int fromZeroSize = outIndex_ + bufferSize - bufferSize_; fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize; int fromOutSize = bufferSize - fromZeroSize; switch( format ) { case RTAUDIO_SINT8: memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) ); memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) ); break; case RTAUDIO_SINT16: memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) ); memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) ); break; case RTAUDIO_SINT24: memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) ); memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) ); break; case RTAUDIO_SINT32: memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) ); memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) ); break; case RTAUDIO_FLOAT32: memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) ); memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) ); break; case RTAUDIO_FLOAT64: memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) ); memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) ); break; } // update "out" index outIndex_ += bufferSize; outIndex_ %= bufferSize_; return true; } private: char* buffer_; unsigned int bufferSize_; unsigned int inIndex_; unsigned int outIndex_; }; //----------------------------------------------------------------------------- // In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate // between HW and the user. The WasapiResampler class is used to perform this conversion between // HwIn->UserIn and UserOut->HwOut during the stream callback loop. class WasapiResampler { public: WasapiResampler( bool isFloat, unsigned int bitsPerSample, unsigned int channelCount, unsigned int inSampleRate, unsigned int outSampleRate ) : _bytesPerSample( bitsPerSample / 8 ) , _channelCount( channelCount ) , _sampleRatio( ( float ) outSampleRate / inSampleRate ) , _transformUnk( NULL ) , _transform( NULL ) , _mediaType( NULL ) , _inputMediaType( NULL ) , _outputMediaType( NULL ) #ifdef __IWMResamplerProps_FWD_DEFINED__ , _resamplerProps( NULL ) #endif { // 1. Initialization MFStartup( MF_VERSION, MFSTARTUP_NOSOCKET ); // 2. Create Resampler Transform Object CoCreateInstance( CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, ( void** ) &_transformUnk ); _transformUnk->QueryInterface( IID_PPV_ARGS( &_transform ) ); #ifdef __IWMResamplerProps_FWD_DEFINED__ _transformUnk->QueryInterface( IID_PPV_ARGS( &_resamplerProps ) ); _resamplerProps->SetHalfFilterLength( 60 ); // best conversion quality #endif // 3. Specify input / output format MFCreateMediaType( &_mediaType ); _mediaType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio ); _mediaType->SetGUID( MF_MT_SUBTYPE, isFloat ? MFAudioFormat_Float : MFAudioFormat_PCM ); _mediaType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, channelCount ); _mediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, inSampleRate ); _mediaType->SetUINT32( MF_MT_AUDIO_BLOCK_ALIGNMENT, _bytesPerSample * channelCount ); _mediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * inSampleRate ); _mediaType->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample ); _mediaType->SetUINT32( MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE ); MFCreateMediaType( &_inputMediaType ); _mediaType->CopyAllItems( _inputMediaType ); _transform->SetInputType( 0, _inputMediaType, 0 ); MFCreateMediaType( &_outputMediaType ); _mediaType->CopyAllItems( _outputMediaType ); _outputMediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, outSampleRate ); _outputMediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * outSampleRate ); _transform->SetOutputType( 0, _outputMediaType, 0 ); // 4. Send stream start messages to Resampler _transform->ProcessMessage( MFT_MESSAGE_COMMAND_FLUSH, 0 ); _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0 ); _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0 ); } ~WasapiResampler() { // 8. Send stream stop messages to Resampler _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0 ); _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_STREAMING, 0 ); // 9. Cleanup MFShutdown(); SAFE_RELEASE( _transformUnk ); SAFE_RELEASE( _transform ); SAFE_RELEASE( _mediaType ); SAFE_RELEASE( _inputMediaType ); SAFE_RELEASE( _outputMediaType ); #ifdef __IWMResamplerProps_FWD_DEFINED__ SAFE_RELEASE( _resamplerProps ); #endif } void Convert( char* outBuffer, const char* inBuffer, unsigned int inSampleCount, unsigned int& outSampleCount, int maxOutSampleCount = -1 ) { unsigned int inputBufferSize = _bytesPerSample * _channelCount * inSampleCount; if ( _sampleRatio == 1 ) { // no sample rate conversion required memcpy( outBuffer, inBuffer, inputBufferSize ); outSampleCount = inSampleCount; return; } unsigned int outputBufferSize = 0; if ( maxOutSampleCount != -1 ) { outputBufferSize = _bytesPerSample * _channelCount * maxOutSampleCount; } else { outputBufferSize = ( unsigned int ) ceilf( inputBufferSize * _sampleRatio ) + ( _bytesPerSample * _channelCount ); } IMFMediaBuffer* rInBuffer; IMFSample* rInSample; BYTE* rInByteBuffer = NULL; // 5. Create Sample object from input data MFCreateMemoryBuffer( inputBufferSize, &rInBuffer ); rInBuffer->Lock( &rInByteBuffer, NULL, NULL ); memcpy( rInByteBuffer, inBuffer, inputBufferSize ); rInBuffer->Unlock(); rInByteBuffer = NULL; rInBuffer->SetCurrentLength( inputBufferSize ); MFCreateSample( &rInSample ); rInSample->AddBuffer( rInBuffer ); // 6. Pass input data to Resampler _transform->ProcessInput( 0, rInSample, 0 ); SAFE_RELEASE( rInBuffer ); SAFE_RELEASE( rInSample ); // 7. Perform sample rate conversion IMFMediaBuffer* rOutBuffer = NULL; BYTE* rOutByteBuffer = NULL; MFT_OUTPUT_DATA_BUFFER rOutDataBuffer; DWORD rStatus; DWORD rBytes = outputBufferSize; // maximum bytes accepted per ProcessOutput // 7.1 Create Sample object for output data memset( &rOutDataBuffer, 0, sizeof rOutDataBuffer ); MFCreateSample( &( rOutDataBuffer.pSample ) ); MFCreateMemoryBuffer( rBytes, &rOutBuffer ); rOutDataBuffer.pSample->AddBuffer( rOutBuffer ); rOutDataBuffer.dwStreamID = 0; rOutDataBuffer.dwStatus = 0; rOutDataBuffer.pEvents = NULL; // 7.2 Get output data from Resampler if ( _transform->ProcessOutput( 0, 1, &rOutDataBuffer, &rStatus ) == MF_E_TRANSFORM_NEED_MORE_INPUT ) { outSampleCount = 0; SAFE_RELEASE( rOutBuffer ); SAFE_RELEASE( rOutDataBuffer.pSample ); return; } // 7.3 Write output data to outBuffer SAFE_RELEASE( rOutBuffer ); rOutDataBuffer.pSample->ConvertToContiguousBuffer( &rOutBuffer ); rOutBuffer->GetCurrentLength( &rBytes ); rOutBuffer->Lock( &rOutByteBuffer, NULL, NULL ); memcpy( outBuffer, rOutByteBuffer, rBytes ); rOutBuffer->Unlock(); rOutByteBuffer = NULL; outSampleCount = rBytes / _bytesPerSample / _channelCount; SAFE_RELEASE( rOutBuffer ); SAFE_RELEASE( rOutDataBuffer.pSample ); } private: unsigned int _bytesPerSample; unsigned int _channelCount; float _sampleRatio; IUnknown* _transformUnk; IMFTransform* _transform; IMFMediaType* _mediaType; IMFMediaType* _inputMediaType; IMFMediaType* _outputMediaType; #ifdef __IWMResamplerProps_FWD_DEFINED__ IWMResamplerProps* _resamplerProps; #endif }; //----------------------------------------------------------------------------- // A structure to hold various information related to the WASAPI implementation. struct WasapiHandle { IAudioClient* captureAudioClient; IAudioClient* renderAudioClient; IAudioCaptureClient* captureClient; IAudioRenderClient* renderClient; HANDLE captureEvent; HANDLE renderEvent; WasapiHandle() : captureAudioClient( NULL ), renderAudioClient( NULL ), captureClient( NULL ), renderClient( NULL ), captureEvent( NULL ), renderEvent( NULL ) {} }; //----------------------------------------------------------------------------- RtApiWasapi::RtApiWasapi() : coInitialized_( false ), deviceEnumerator_( NULL ) { // WASAPI can run either apartment or multi-threaded HRESULT hr = CoInitialize( NULL ); if ( !FAILED( hr ) ) coInitialized_ = true; // Instantiate device enumerator hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ), ( void** ) &deviceEnumerator_ ); // If this runs on an old Windows, it will fail. Ignore and proceed. if ( FAILED( hr ) ) deviceEnumerator_ = NULL; } //----------------------------------------------------------------------------- RtApiWasapi::~RtApiWasapi() { MUTEX_LOCK( &stream_.mutex ); if ( stream_.state != STREAM_CLOSED ) { MUTEX_UNLOCK( &stream_.mutex ); closeStream(); MUTEX_LOCK( &stream_.mutex ); } SAFE_RELEASE( deviceEnumerator_ ); // If this object previously called CoInitialize() if ( coInitialized_ ) CoUninitialize(); MUTEX_UNLOCK( &stream_.mutex ); } //----------------------------------------------------------------------------- unsigned int RtApiWasapi::getDefaultInputDevice( void ) { IMMDevice* devicePtr = NULL; LPWSTR defaultId = NULL; std::string id; if ( !deviceEnumerator_ ) return 0; // invalid ID errorText_.clear(); // Get the default capture device Id. HRESULT hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDefaultInputDevice: Unable to retrieve default capture device handle."; goto Release; } hr = devicePtr->GetId( &defaultId ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDefaultInputDevice: Unable to get default capture device Id."; goto Release; } id = convertCharPointerToStdString( defaultId ); Release: SAFE_RELEASE( devicePtr ); CoTaskMemFree( defaultId ); if ( !errorText_.empty() ) { error( RTAUDIO_DRIVER_ERROR ); return 0; } for ( unsigned int m=0; mGetDefaultAudioEndpoint( eRender, eConsole, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDefaultOutputDevice: Unable to retrieve default render device handle."; goto Release; } hr = devicePtr->GetId( &defaultId ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDefaultOutputDevice: Unable to get default render device Id."; goto Release; } id = convertCharPointerToStdString( defaultId ); Release: SAFE_RELEASE( devicePtr ); CoTaskMemFree( defaultId ); if ( !errorText_.empty() ) { error( RTAUDIO_DRIVER_ERROR ); return 0; } for ( unsigned int m=0; m > ids; LPWSTR deviceId = NULL; if ( !deviceEnumerator_ ) return; errorText_.clear(); // Count capture devices HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to retrieve capture device collection."; goto Exit; } hr = captureDevices->GetCount( &captureDeviceCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to retrieve capture device count."; goto Exit; } // Count render devices hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to retrieve render device collection."; goto Exit; } hr = renderDevices->GetCount( &renderDeviceCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to retrieve render device count."; goto Exit; } nDevices = captureDeviceCount + renderDeviceCount; if ( nDevices == 0 ) { errorText_ = "RtApiWasapi::probeDevices: No devices found."; goto Exit; } // Get the default capture device Id. hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &devicePtr ); if ( SUCCEEDED( hr) ) { hr = devicePtr->GetId( &defaultCaptureId ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to get default capture device Id."; goto Exit; } defaultCaptureString = convertCharPointerToStdString( defaultCaptureId ); } // Get the default render device Id. SAFE_RELEASE( devicePtr ); hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &devicePtr ); if ( SUCCEEDED( hr) ) { hr = devicePtr->GetId( &defaultRenderId ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to get default render device Id."; goto Exit; } defaultRenderString = convertCharPointerToStdString( defaultRenderId ); } // Collect device IDs with mode. for ( unsigned int n=0; nItem( n, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to retrieve render device handle."; error( RTAUDIO_WARNING ); continue; } } else { hr = captureDevices->Item( n - renderDeviceCount, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to retrieve capture device handle."; error( RTAUDIO_WARNING ); continue; } isCaptureDevice = true; } hr = devicePtr->GetId( &deviceId ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDevices: Unable to get device Id."; error( RTAUDIO_WARNING ); continue; } ids.push_back( std::pair< std::string, bool>(convertCharPointerToStdString(deviceId), isCaptureDevice) ); CoTaskMemFree( deviceId ); } // Fill or update the deviceList_ and also save a corresponding list of Ids. for ( unsigned int n=0; n >::iterator it=deviceIds_.begin(); it!=deviceIds_.end(); ) { for ( m=0; mGetDevice( deviceId, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceInfo: Unable to retrieve device handle."; goto Exit; } // Get device name hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceInfo: Unable to open device property store."; goto Exit; } PropVariantInit( &deviceNameProp ); hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp ); if ( FAILED( hr ) || deviceNameProp.pwszVal == nullptr ) { errorText_ = "RtApiWasapi::probeDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName."; goto Exit; } info.name = convertCharPointerToStdString( deviceNameProp.pwszVal ); // Get audio client hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceInfo: Unable to retrieve device audio client."; goto Exit; } hr = audioClient->GetMixFormat( &deviceFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceInfo: Unable to retrieve device mix format."; goto Exit; } // Set channel count if ( isCaptureDevice ) { info.inputChannels = deviceFormat->nChannels; info.outputChannels = 0; info.duplexChannels = 0; } else { info.inputChannels = 0; info.outputChannels = deviceFormat->nChannels; info.duplexChannels = 0; } // Set sample rates info.sampleRates.clear(); // Allow support for all sample rates as we have a built-in sample rate converter. for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) { info.sampleRates.push_back( SAMPLE_RATES[i] ); } info.preferredSampleRate = deviceFormat->nSamplesPerSec; // Set native formats info.nativeFormats = 0; if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT || ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE && ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) ) { if ( deviceFormat->wBitsPerSample == 32 ) { info.nativeFormats |= RTAUDIO_FLOAT32; } else if ( deviceFormat->wBitsPerSample == 64 ) { info.nativeFormats |= RTAUDIO_FLOAT64; } } else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM || ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE && ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) ) { if ( deviceFormat->wBitsPerSample == 8 ) { info.nativeFormats |= RTAUDIO_SINT8; } else if ( deviceFormat->wBitsPerSample == 16 ) { info.nativeFormats |= RTAUDIO_SINT16; } else if ( deviceFormat->wBitsPerSample == 24 ) { info.nativeFormats |= RTAUDIO_SINT24; } else if ( deviceFormat->wBitsPerSample == 32 ) { info.nativeFormats |= RTAUDIO_SINT32; } } Exit: // Release all references PropVariantClear( &deviceNameProp ); SAFE_RELEASE( devicePtr ); SAFE_RELEASE( audioClient ); SAFE_RELEASE( devicePropStore ); CoTaskMemFree( deviceFormat ); CoTaskMemFree( closestMatchFormat ); if ( !errorText_.empty() ) { error( errorType ); return false; } return true; } void RtApiWasapi::closeStream( void ) { MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiWasapi::closeStream: No open stream to close."; error( RTAUDIO_WARNING ); MUTEX_UNLOCK( &stream_.mutex ); return; } if ( stream_.state != STREAM_STOPPED ) { MUTEX_UNLOCK( &stream_.mutex ); stopStream(); MUTEX_LOCK( &stream_.mutex ); } // clean up stream memory SAFE_RELEASE(((WasapiHandle*)stream_.apiHandle)->captureClient) SAFE_RELEASE(((WasapiHandle*)stream_.apiHandle)->renderClient) SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent ) CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent ); if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent ) CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent ); delete ( WasapiHandle* ) stream_.apiHandle; stream_.apiHandle = NULL; for ( int i = 0; i < 2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } clearStreamInfo(); MUTEX_UNLOCK( &stream_.mutex ); } //----------------------------------------------------------------------------- RtAudioErrorType RtApiWasapi::startStream( void ) { MUTEX_LOCK( &stream_.mutex ); if ( stream_.state != STREAM_STOPPED ) { if ( stream_.state == STREAM_RUNNING ) errorText_ = "RtApiWasapi::startStream(): the stream is already running!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiWasapi::startStream(): the stream is stopping or closed!"; MUTEX_UNLOCK( &stream_.mutex ); return error( RTAUDIO_WARNING ); } /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ // update stream state stream_.state = STREAM_RUNNING; // create WASAPI stream thread stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL ); if ( !stream_.callbackInfo.thread ) { errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread."; MUTEX_UNLOCK( &stream_.mutex ); return error( RTAUDIO_THREAD_ERROR ); } else { SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority ); ResumeThread( ( void* ) stream_.callbackInfo.thread ); } MUTEX_UNLOCK( &stream_.mutex ); return RTAUDIO_NO_ERROR; } //----------------------------------------------------------------------------- RtAudioErrorType RtApiWasapi::stopStream( void ) { MUTEX_LOCK( &stream_.mutex ); if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiWasapi::stopStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_CLOSED ) errorText_ = "RtApiWasapi::stopStream(): the stream is closed!"; MUTEX_UNLOCK( &stream_.mutex ); return error( RTAUDIO_WARNING ); } // inform stream thread by setting stream state to STREAM_STOPPING stream_.state = STREAM_STOPPING; WaitForSingleObject( ( void* ) stream_.callbackInfo.thread, INFINITE ); // close thread handle if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread."; MUTEX_UNLOCK( &stream_.mutex ); return error( RTAUDIO_THREAD_ERROR ); } stream_.callbackInfo.thread = (ThreadHandle) NULL; MUTEX_UNLOCK( &stream_.mutex ); return RTAUDIO_NO_ERROR; } //----------------------------------------------------------------------------- RtAudioErrorType RtApiWasapi::abortStream( void ) { MUTEX_LOCK( &stream_.mutex ); if ( stream_.state != STREAM_RUNNING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiWasapi::abortStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiWasapi::abortStream(): the stream is stopping or closed!"; MUTEX_UNLOCK( &stream_.mutex ); return error( RTAUDIO_WARNING ); } // inform stream thread by setting stream state to STREAM_STOPPING stream_.state = STREAM_STOPPING; WaitForSingleObject( ( void* ) stream_.callbackInfo.thread, INFINITE ); // close thread handle if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread."; MUTEX_UNLOCK( &stream_.mutex ); return error( RTAUDIO_THREAD_ERROR ); } stream_.callbackInfo.thread = (ThreadHandle) NULL; MUTEX_UNLOCK( &stream_.mutex ); return RTAUDIO_NO_ERROR; } //----------------------------------------------------------------------------- bool RtApiWasapi::probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int* bufferSize, RtAudio::StreamOptions* options ) { MUTEX_LOCK( &stream_.mutex ); bool methodResult = FAILURE; IMMDevice* devicePtr = NULL; WAVEFORMATEX* deviceFormat = NULL; unsigned int bufferBytes; stream_.state = STREAM_STOPPED; bool isInput = false; std::string id; unsigned int deviceIdx; for ( deviceIdx=0; deviceIdxGetDevice( (LPWSTR)temp.c_str(), &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device handle."; MUTEX_UNLOCK( &stream_.mutex ); return FAILURE; } // Create API handle if not already created. if ( !stream_.apiHandle ) stream_.apiHandle = ( void* ) new WasapiHandle(); if ( isInput ) { IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient; hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &captureAudioClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device audio client."; goto Exit; } hr = captureAudioClient->GetMixFormat( &deviceFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device mix format."; goto Exit; } stream_.nDeviceChannels[mode] = deviceFormat->nChannels; captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); } // If an output device and is configured for loopback (input mode) if ( isInput == false && mode == INPUT ) { // If renderAudioClient is not initialised, initialise it now IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; if ( !renderAudioClient ) { MUTEX_UNLOCK( &stream_.mutex ); probeDeviceOpen( deviceId, OUTPUT, channels, firstChannel, sampleRate, format, bufferSize, options ); MUTEX_LOCK( &stream_.mutex ); } // Retrieve captureAudioClient from our stream handle. IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient; hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &captureAudioClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device audio client."; goto Exit; } hr = captureAudioClient->GetMixFormat( &deviceFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device mix format."; goto Exit; } stream_.nDeviceChannels[mode] = deviceFormat->nChannels; captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); } // If output device and is configured for output. if ( isInput == false && mode == OUTPUT ) { // If renderAudioClient is already initialised, don't initialise it again IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; if ( renderAudioClient ) { methodResult = SUCCESS; goto Exit; } hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &renderAudioClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device audio client."; goto Exit; } hr = renderAudioClient->GetMixFormat( &deviceFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device mix format."; goto Exit; } stream_.nDeviceChannels[mode] = deviceFormat->nChannels; renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); } // Fill stream data if ( ( stream_.mode == OUTPUT && mode == INPUT ) || ( stream_.mode == INPUT && mode == OUTPUT ) ) { stream_.mode = DUPLEX; } else { stream_.mode = mode; } stream_.deviceId[mode] = deviceId; stream_.doByteSwap[mode] = false; stream_.sampleRate = sampleRate; stream_.bufferSize = *bufferSize; stream_.nBuffers = 1; stream_.nUserChannels[mode] = channels; stream_.channelOffset[mode] = firstChannel; stream_.userFormat = format; stream_.deviceFormat[mode] = deviceList_[deviceIdx].nativeFormats; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] || stream_.nUserChannels[0] != stream_.nDeviceChannels[0] || stream_.nUserChannels[1] != stream_.nDeviceChannels[1] ) stream_.doConvertBuffer[mode] = true; else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); // Allocate necessary internal buffers bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 ); if ( !stream_.userBuffer[mode] ) { errorType = RTAUDIO_MEMORY_ERROR; errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory."; goto Exit; } if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) stream_.callbackInfo.priority = 15; else stream_.callbackInfo.priority = 0; ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback ///! TODO: RTAUDIO_HOG_DEVICE // Exclusive mode methodResult = SUCCESS; Exit: //clean up SAFE_RELEASE( devicePtr ); CoTaskMemFree( deviceFormat ); // if method failed, close the stream if ( methodResult == FAILURE ) { MUTEX_UNLOCK( &stream_.mutex ); closeStream(); MUTEX_LOCK( &stream_.mutex ); } if ( !errorText_.empty() ) error( errorType ); MUTEX_UNLOCK( &stream_.mutex ); return methodResult; } //============================================================================= DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr ) { if ( wasapiPtr ) ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread(); return 0; } DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr ) { if ( wasapiPtr ) ( ( RtApiWasapi* ) wasapiPtr )->stopStream(); return 0; } DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr ) { if ( wasapiPtr ) ( ( RtApiWasapi* ) wasapiPtr )->abortStream(); return 0; } //----------------------------------------------------------------------------- void RtApiWasapi::wasapiThread() { // as this is a new thread, we must CoInitialize it CoInitialize( NULL ); HRESULT hr; IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient; IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient; IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient; HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent; HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent; WAVEFORMATEX* captureFormat = NULL; WAVEFORMATEX* renderFormat = NULL; float captureSrRatio = 0.0f; float renderSrRatio = 0.0f; WasapiBuffer captureBuffer; WasapiBuffer renderBuffer; WasapiResampler* captureResampler = NULL; WasapiResampler* renderResampler = NULL; // declare local stream variables RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback; BYTE* streamBuffer = NULL; DWORD captureFlags = 0; unsigned int bufferFrameCount = 0; unsigned int numFramesPadding = 0; unsigned int convBufferSize = 0; bool loopbackEnabled = stream_.deviceId[INPUT] == stream_.deviceId[OUTPUT]; bool callbackPushed = true; bool callbackPulled = false; bool callbackStopped = false; int callbackResult = 0; // convBuffer is used to store converted buffers between WASAPI and the user char* convBuffer = NULL; unsigned int convBuffSize = 0; unsigned int deviceBuffSize = 0; std::string errorText; RtAudioErrorType errorType = RTAUDIO_DRIVER_ERROR; // Attempt to assign "Pro Audio" characteristic to thread HMODULE AvrtDll = LoadLibraryW( L"AVRT.dll" ); if ( AvrtDll ) { DWORD taskIndex = 0; TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) (void(*)()) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" ); AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex ); FreeLibrary( AvrtDll ); } // start capture stream if applicable if ( captureAudioClient ) { hr = captureAudioClient->GetMixFormat( &captureFormat ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format."; goto Exit; } // init captureResampler captureResampler = new WasapiResampler( stream_.deviceFormat[INPUT] == RTAUDIO_FLOAT32 || stream_.deviceFormat[INPUT] == RTAUDIO_FLOAT64, formatBytes( stream_.deviceFormat[INPUT] ) * 8, stream_.nDeviceChannels[INPUT], captureFormat->nSamplesPerSec, stream_.sampleRate ); captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate ); if ( !captureClient ) { IAudioClient3* captureAudioClient3 = nullptr; captureAudioClient->QueryInterface( __uuidof( IAudioClient3 ), ( void** ) &captureAudioClient3 ); if ( captureAudioClient3 && !loopbackEnabled ) { UINT32 Ignore; UINT32 MinPeriodInFrames; hr = captureAudioClient3->GetSharedModeEnginePeriod( captureFormat, &Ignore, &Ignore, &MinPeriodInFrames, &Ignore ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client."; goto Exit; } hr = captureAudioClient3->InitializeSharedAudioStream( AUDCLNT_STREAMFLAGS_EVENTCALLBACK, MinPeriodInFrames, captureFormat, NULL ); SAFE_RELEASE(captureAudioClient3); } else { hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, loopbackEnabled ? AUDCLNT_STREAMFLAGS_LOOPBACK : AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, captureFormat, NULL ); } if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client."; goto Exit; } hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ), ( void** ) &captureClient ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle."; goto Exit; } // don't configure captureEvent if in loopback mode if ( !loopbackEnabled ) { // configure captureEvent to trigger on every available capture buffer captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( !captureEvent ) { errorType = RTAUDIO_SYSTEM_ERROR; errorText = "RtApiWasapi::wasapiThread: Unable to create capture event."; goto Exit; } hr = captureAudioClient->SetEventHandle( captureEvent ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to set capture event handle."; goto Exit; } ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent; } ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient; // reset the capture stream hr = captureAudioClient->Reset(); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to reset capture stream."; goto Exit; } // start the capture stream hr = captureAudioClient->Start(); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to start capture stream."; goto Exit; } } unsigned int inBufferSize = 0; hr = captureAudioClient->GetBufferSize( &inBufferSize ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to get capture buffer size."; goto Exit; } // scale outBufferSize according to stream->user sample rate ratio unsigned int outBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT]; inBufferSize *= stream_.nDeviceChannels[INPUT]; // set captureBuffer size captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) ); } // start render stream if applicable if ( renderAudioClient ) { hr = renderAudioClient->GetMixFormat( &renderFormat ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format."; goto Exit; } // init renderResampler renderResampler = new WasapiResampler( stream_.deviceFormat[OUTPUT] == RTAUDIO_FLOAT32 || stream_.deviceFormat[OUTPUT] == RTAUDIO_FLOAT64, formatBytes( stream_.deviceFormat[OUTPUT] ) * 8, stream_.nDeviceChannels[OUTPUT], stream_.sampleRate, renderFormat->nSamplesPerSec ); renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate ); if ( !renderClient ) { IAudioClient3* renderAudioClient3 = nullptr; renderAudioClient->QueryInterface( __uuidof( IAudioClient3 ), ( void** ) &renderAudioClient3 ); if ( renderAudioClient3 ) { UINT32 Ignore; UINT32 MinPeriodInFrames; hr = renderAudioClient3->GetSharedModeEnginePeriod( renderFormat, &Ignore, &Ignore, &MinPeriodInFrames, &Ignore ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to initialize render audio client."; goto Exit; } hr = renderAudioClient3->InitializeSharedAudioStream( AUDCLNT_STREAMFLAGS_EVENTCALLBACK, MinPeriodInFrames, renderFormat, NULL ); SAFE_RELEASE(renderAudioClient3); } else { hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, renderFormat, NULL ); } if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to initialize render audio client."; goto Exit; } hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ), ( void** ) &renderClient ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle."; goto Exit; } // configure renderEvent to trigger on every available render buffer renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( !renderEvent ) { errorType = RTAUDIO_SYSTEM_ERROR; errorText = "RtApiWasapi::wasapiThread: Unable to create render event."; goto Exit; } hr = renderAudioClient->SetEventHandle( renderEvent ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to set render event handle."; goto Exit; } ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient; ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent; // reset the render stream hr = renderAudioClient->Reset(); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to reset render stream."; goto Exit; } // start the render stream hr = renderAudioClient->Start(); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to start render stream."; goto Exit; } } unsigned int outBufferSize = 0; hr = renderAudioClient->GetBufferSize( &outBufferSize ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to get render buffer size."; goto Exit; } // scale inBufferSize according to user->stream sample rate ratio unsigned int inBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT]; outBufferSize *= stream_.nDeviceChannels[OUTPUT]; // set renderBuffer size renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) ); } // malloc buffer memory if ( stream_.mode == INPUT ) { using namespace std; // for ceilf convBuffSize = ( unsigned int ) ( ceilf( stream_.bufferSize * captureSrRatio ) ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); } else if ( stream_.mode == OUTPUT ) { convBuffSize = ( unsigned int ) ( ceilf( stream_.bufferSize * renderSrRatio ) ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ); deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ); } else if ( stream_.mode == DUPLEX ) { convBuffSize = std::max( ( unsigned int ) ( ceilf( stream_.bufferSize * captureSrRatio ) ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), ( unsigned int ) ( ceilf( stream_.bufferSize * renderSrRatio ) ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ); deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ); } convBuffSize *= 2; // allow overflow for *SrRatio remainders convBuffer = ( char* ) calloc( convBuffSize, 1 ); stream_.deviceBuffer = ( char* ) calloc( deviceBuffSize, 1 ); if ( !convBuffer || !stream_.deviceBuffer ) { errorType = RTAUDIO_MEMORY_ERROR; errorText = "RtApiWasapi::wasapiThread: Error allocating device buffer memory."; goto Exit; } // stream process loop while ( stream_.state != STREAM_STOPPING ) { if ( !callbackPulled ) { // Callback Input // ============== // 1. Pull callback buffer from inputBuffer // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count // Convert callback buffer to user format if ( captureAudioClient ) { int samplesToPull = ( unsigned int ) floorf( stream_.bufferSize * captureSrRatio ); convBufferSize = 0; while ( convBufferSize < stream_.bufferSize ) { // Pull callback buffer from inputBuffer callbackPulled = captureBuffer.pullBuffer( convBuffer, samplesToPull * stream_.nDeviceChannels[INPUT], stream_.deviceFormat[INPUT] ); if ( !callbackPulled ) { break; } // Convert callback buffer to user sample rate unsigned int deviceBufferOffset = convBufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); unsigned int convSamples = 0; captureResampler->Convert( stream_.deviceBuffer + deviceBufferOffset, convBuffer, samplesToPull, convSamples, convBufferSize == 0 ? -1 : stream_.bufferSize - convBufferSize ); convBufferSize += convSamples; samplesToPull = 1; // now pull one sample at a time until we have stream_.bufferSize samples } if ( callbackPulled ) { if ( stream_.doConvertBuffer[INPUT] ) { // Convert callback buffer to user format convertBuffer( stream_.userBuffer[INPUT], stream_.deviceBuffer, stream_.convertInfo[INPUT] ); } else { // no further conversion, simple copy deviceBuffer to userBuffer memcpy( stream_.userBuffer[INPUT], stream_.deviceBuffer, stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) ); } } } else { // if there is no capture stream, set callbackPulled flag callbackPulled = true; } // Execute Callback // ================ // 1. Execute user callback method // 2. Handle return value from callback // if callback has not requested the stream to stop if ( callbackPulled && !callbackStopped ) { // Execute user callback method callbackResult = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT], stream_.bufferSize, getStreamTime(), captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0, stream_.callbackInfo.userData ); // tick stream time RtApi::tickStreamTime(); // Handle return value from callback if ( callbackResult == 1 ) { // instantiate a thread to stop this thread HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL ); if ( !threadHandle ) { errorType = RTAUDIO_THREAD_ERROR; errorText = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread."; goto Exit; } else if ( !CloseHandle( threadHandle ) ) { errorType = RTAUDIO_THREAD_ERROR; errorText = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle."; goto Exit; } callbackStopped = true; } else if ( callbackResult == 2 ) { // instantiate a thread to stop this thread HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL ); if ( !threadHandle ) { errorType = RTAUDIO_THREAD_ERROR; errorText = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread."; goto Exit; } else if ( !CloseHandle( threadHandle ) ) { errorType = RTAUDIO_THREAD_ERROR; errorText = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle."; goto Exit; } callbackStopped = true; } } } // Callback Output // =============== // 1. Convert callback buffer to stream format // 2. Convert callback buffer to stream sample rate and channel count // 3. Push callback buffer into outputBuffer if ( renderAudioClient && callbackPulled ) { // if the last call to renderBuffer.PushBuffer() was successful if ( callbackPushed || convBufferSize == 0 ) { if ( stream_.doConvertBuffer[OUTPUT] ) { // Convert callback buffer to stream format convertBuffer( stream_.deviceBuffer, stream_.userBuffer[OUTPUT], stream_.convertInfo[OUTPUT] ); } else { // no further conversion, simple copy userBuffer to deviceBuffer memcpy( stream_.deviceBuffer, stream_.userBuffer[OUTPUT], stream_.bufferSize * stream_.nUserChannels[OUTPUT] * formatBytes( stream_.userFormat ) ); } // Convert callback buffer to stream sample rate renderResampler->Convert( convBuffer, stream_.deviceBuffer, stream_.bufferSize, convBufferSize ); } // Push callback buffer into outputBuffer callbackPushed = renderBuffer.pushBuffer( convBuffer, convBufferSize * stream_.nDeviceChannels[OUTPUT], stream_.deviceFormat[OUTPUT] ); } else { // if there is no render stream, set callbackPushed flag callbackPushed = true; } // Stream Capture // ============== // 1. Get capture buffer from stream // 2. Push capture buffer into inputBuffer // 3. If 2. was successful: Release capture buffer if ( captureAudioClient ) { // if the callback input buffer was not pulled from captureBuffer, wait for next capture event if ( !callbackPulled ) { WaitForSingleObject( loopbackEnabled ? renderEvent : captureEvent, INFINITE ); } // Get capture buffer from stream hr = captureClient->GetBuffer( &streamBuffer, &bufferFrameCount, &captureFlags, NULL, NULL ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer."; goto Exit; } if ( bufferFrameCount != 0 ) { // Push capture buffer into inputBuffer if ( captureBuffer.pushBuffer( ( char* ) streamBuffer, bufferFrameCount * stream_.nDeviceChannels[INPUT], stream_.deviceFormat[INPUT] ) ) { // Release capture buffer hr = captureClient->ReleaseBuffer( bufferFrameCount ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; goto Exit; } } else { // Inform WASAPI that capture was unsuccessful hr = captureClient->ReleaseBuffer( 0 ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; goto Exit; } } } else { // Inform WASAPI that capture was unsuccessful hr = captureClient->ReleaseBuffer( 0 ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; goto Exit; } } } // Stream Render // ============= // 1. Get render buffer from stream // 2. Pull next buffer from outputBuffer // 3. If 2. was successful: Fill render buffer with next buffer // Release render buffer if ( renderAudioClient ) { // if the callback output buffer was not pushed to renderBuffer, wait for next render event if ( callbackPulled && !callbackPushed ) { WaitForSingleObject( renderEvent, INFINITE ); } // Get render buffer from stream hr = renderAudioClient->GetBufferSize( &bufferFrameCount ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size."; goto Exit; } hr = renderAudioClient->GetCurrentPadding( &numFramesPadding ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding."; goto Exit; } bufferFrameCount -= numFramesPadding; if ( bufferFrameCount != 0 ) { hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer."; goto Exit; } // Pull next buffer from outputBuffer // Fill render buffer with next buffer if ( renderBuffer.pullBuffer( ( char* ) streamBuffer, bufferFrameCount * stream_.nDeviceChannels[OUTPUT], stream_.deviceFormat[OUTPUT] ) ) { // Release render buffer hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer."; goto Exit; } } else { // Inform WASAPI that render was unsuccessful hr = renderClient->ReleaseBuffer( 0, 0 ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer."; goto Exit; } } } else { // Inform WASAPI that render was unsuccessful hr = renderClient->ReleaseBuffer( 0, 0 ); if ( FAILED( hr ) ) { errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer."; goto Exit; } } } // if the callback buffer was pushed renderBuffer reset callbackPulled flag if ( callbackPushed ) { // unsetting the callbackPulled flag lets the stream know that // the audio device is ready for another callback output buffer. callbackPulled = false; } } Exit: // clean up CoTaskMemFree( captureFormat ); CoTaskMemFree( renderFormat ); free ( convBuffer ); delete renderResampler; delete captureResampler; CoUninitialize(); if ( !errorText.empty() ) { errorText_ = errorText; error( errorType ); } // update stream state stream_.state = STREAM_STOPPED; } //******************** End of __WINDOWS_WASAPI__ *********************// #endif #if defined(__WINDOWS_DS__) // Windows DirectSound API // Modified by Robin Davies, October 2005 // - Improvements to DirectX pointer chasing. // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30. // - Auto-call CoInitialize for DSOUND and ASIO platforms. // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007 // Changed device query structure for RtAudio 4.0.7, January 2010 #include #include #include #include #include #include #include #if defined(__MINGW32__) // missing from latest mingw winapi #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */ #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */ #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ #endif #define MINIMUM_DEVICE_BUFFER_SIZE 32768 #ifdef _MSC_VER // if Microsoft Visual C++ #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually. #endif static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize ) { if ( pointer > bufferSize ) pointer -= bufferSize; if ( laterPointer < earlierPointer ) laterPointer += bufferSize; if ( pointer < earlierPointer ) pointer += bufferSize; return pointer >= earlierPointer && pointer < laterPointer; } // A structure to hold various information related to the DirectSound // API implementation. struct DsHandle { unsigned int drainCounter; // Tracks callback counts when draining bool internalDrain; // Indicates if stop is initiated from callback or not. void *id[2]; void *buffer[2]; bool xrun[2]; UINT bufferPointer[2]; DWORD dsBufferSize[2]; DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by. HANDLE condition; DsHandle() :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; } }; // Declarations for utility functions, callbacks, and structures // specific to the DirectSound implementation. static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext ); static const char* getErrorString( int code ); static unsigned __stdcall callbackHandler( void *ptr ); struct DsDevice { LPGUID id; bool isInput; std::string name; std::string epID; // endpoint ID DsDevice() : isInput(false) {} }; struct DsProbeData { bool isInput; std::vector* dsDevices; }; RtApiDs :: RtApiDs() { // Dsound will run both-threaded. If CoInitialize fails, then just // accept whatever the mainline chose for a threading model. coInitialized_ = false; HRESULT hr = CoInitialize( NULL ); if ( !FAILED( hr ) ) coInitialized_ = true; } RtApiDs :: ~RtApiDs() { if ( stream_.state != STREAM_CLOSED ) closeStream(); if ( coInitialized_ ) CoUninitialize(); // balanced call. } void RtApiDs :: probeDevices( void ) { // See list of required functionality in RtApi::probeDevices(). // Query DirectSound devices. struct DsProbeData probeInfo; probeInfo.isInput = false; std::vector< struct DsDevice > devices; probeInfo.dsDevices = &devices; HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::probeDevices: error (" << getErrorString( result ) << ") enumerating output devices!"; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); } // Query DirectSoundCapture devices. probeInfo.isInput = true; result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::probeDevices: error (" << getErrorString( result ) << ") enumerating input devices!"; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); } // Now fill or update our deviceList_ vector. unsigned int m, n; for ( n=0; n::iterator it=dsDevices_.begin(); it!=dsDevices_.end(); ) { for ( n=0; nGetCaps( &outCaps ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!"; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto probeInput; } // Get output channel information. info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; // Get sample rate information. info.sampleRates.clear(); for ( unsigned int k=0; k= (unsigned int) outCaps.dwMinSecondarySampleRate && SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) { info.sampleRates.push_back( SAMPLE_RATES[k] ); if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[k]; } } // Get format information. if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16; if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8; output->Release(); info.name = dsDevice.name; return true; probeInput: LPDIRECTSOUNDCAPTURE input; result = DirectSoundCaptureCreate( dsDevice.id, &input, NULL ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::probeDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevice.name << ")!"; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } DSCCAPS inCaps; inCaps.dwSize = sizeof( inCaps ); result = input->GetCaps( &inCaps ); if ( FAILED( result ) ) { input->Release(); errorStream_ << "RtApiDs::probeDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevice.name << ")!"; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Get input channel information. info.inputChannels = inCaps.dwChannels; // Get sample rate and format information. std::vector rates; if ( inCaps.dwChannels >= 2 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( info.nativeFormats & RTAUDIO_SINT16 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 ); if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 ); if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 ); if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 ); } else if ( info.nativeFormats & RTAUDIO_SINT8 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 ); if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 ); if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 ); if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 ); } } else if ( inCaps.dwChannels == 1 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( info.nativeFormats & RTAUDIO_SINT16 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 ); if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 ); if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 ); if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 ); } else if ( info.nativeFormats & RTAUDIO_SINT8 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 ); if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 ); if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 ); if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 ); } } else info.inputChannels = 0; // technically, this would be an error input->Release(); if ( info.inputChannels == 0 ) return false; // Copy the supported rates to the info structure but avoid duplication. bool found; for ( unsigned int i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = info.sampleRates[i]; } // Copy name and return. info.name = dsDevice.name; return true; } bool RtApiDs :: probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { if ( channels + firstChannel > 2 ) { errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device."; return FAILURE; } size_t nDevices = dsDevices_.size(); if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiDs::probeDeviceOpen: no devices found!"; return FAILURE; } int deviceIdx = -1; for ( unsigned int m=0; mnumberOfBuffers; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2; if ( nBuffers < 2 ) nBuffers = 3; // Check the lower range of the user-specified buffer size and set // (arbitrarily) to a lower bound of 32. if ( *bufferSize < 32 ) *bufferSize = 32; // Create the wave format structure. The data format setting will // be determined later. WAVEFORMATEX waveFormat; ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) ); waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nChannels = channels + firstChannel; waveFormat.nSamplesPerSec = (unsigned long) sampleRate; // Determine the device buffer size. By default, we'll use the value // defined above (32K), but we will grow it to make allowances for // very large software buffer sizes. DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE; DWORD dsPointerLeadTime = 0; void *ohandle = 0, *bhandle = 0; HRESULT result; if ( mode == OUTPUT ) { LPDIRECTSOUND output; result = DirectSoundCreate( dsDevices_[ deviceIdx ].id, &output, NULL ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } DSCAPS outCaps; outCaps.dwSize = sizeof( outCaps ); result = output->GetCaps( &outCaps ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Check channel information. if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) { errorStream_ << "RtApiDs::probeDeviceInfo: the output device (" << dsDevices_[ deviceIdx ].name << ") does not support stereo playback."; errorText_ = errorStream_.str(); return FAILURE; } // Check format information. Use 16-bit format unless not // supported or user requests 8-bit. if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT && !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) { waveFormat.wBitsPerSample = 16; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } else { waveFormat.wBitsPerSample = 8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } stream_.userFormat = format; // Update wave format structure and buffer information. waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; // If the user wants an even bigger buffer, increase the device buffer size accordingly. while ( dsPointerLeadTime * 2U > dsBufferSize ) dsBufferSize *= 2; // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes. // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE ); // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes. result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Even though we will write to the secondary buffer, we need to // access the primary buffer to set the correct output format // (since the default is 8-bit, 22 kHz!). Setup the DS primary // buffer description. DSBUFFERDESC bufferDescription; ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSBUFFERDESC ); bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; // Obtain the primary buffer LPDIRECTSOUNDBUFFER buffer; result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Set the primary DS buffer sound format. result = buffer->SetFormat( &waveFormat ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Setup the secondary DS buffer description. ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSBUFFERDESC ); bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCHARDWARE ); // Force hardware mixing bufferDescription.dwBufferBytes = dsBufferSize; bufferDescription.lpwfxFormat = &waveFormat; // Try to create the secondary DS buffer. If that doesn't work, // try to use software mixing. Otherwise, there's a problem. result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE ); // Force software mixing result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } } // Get the buffer size ... might be different from what we specified. DSBCAPS dsbcaps; dsbcaps.dwSize = sizeof( DSBCAPS ); result = buffer->GetCaps( &dsbcaps ); if ( FAILED( result ) ) { output->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } dsBufferSize = dsbcaps.dwBufferBytes; // Lock the DS buffer LPVOID audioPtr; DWORD dataLen; result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { output->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Zero the DS buffer ZeroMemory( audioPtr, dataLen ); // Unlock the DS buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { output->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } ohandle = (void *) output; bhandle = (void *) buffer; } if ( mode == INPUT ) { LPDIRECTSOUNDCAPTURE input; result = DirectSoundCaptureCreate( dsDevices_[ deviceIdx ].id, &input, NULL ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } DSCCAPS inCaps; inCaps.dwSize = sizeof( inCaps ); result = input->GetCaps( &inCaps ); if ( FAILED( result ) ) { input->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Check channel information. if ( inCaps.dwChannels < channels + firstChannel ) { errorText_ = "RtApiDs::probeDeviceInfo: the input device does not support requested input channels."; return FAILURE; } // Check format information. Use 16-bit format unless user // requests 8-bit. DWORD deviceFormats; if ( channels + firstChannel == 2 ) { deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08; if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) { waveFormat.wBitsPerSample = 8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } else { // assume 16-bit is supported waveFormat.wBitsPerSample = 16; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } } else { // channel == 1 deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08; if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) { waveFormat.wBitsPerSample = 8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } else { // assume 16-bit is supported waveFormat.wBitsPerSample = 16; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } } stream_.userFormat = format; // Update wave format structure and buffer information. waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; // If the user wants an even bigger buffer, increase the device buffer size accordingly. while ( dsPointerLeadTime * 2U > dsBufferSize ) dsBufferSize *= 2; // Setup the secondary DS buffer description. DSCBUFFERDESC bufferDescription; ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSCBUFFERDESC ); bufferDescription.dwFlags = 0; bufferDescription.dwReserved = 0; bufferDescription.dwBufferBytes = dsBufferSize; bufferDescription.lpwfxFormat = &waveFormat; // Create the capture buffer. LPDIRECTSOUNDCAPTUREBUFFER buffer; result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { input->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Get the buffer size ... might be different from what we specified. DSCBCAPS dscbcaps; dscbcaps.dwSize = sizeof( DSCBCAPS ); result = buffer->GetCaps( &dscbcaps ); if ( FAILED( result ) ) { input->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } dsBufferSize = dscbcaps.dwBufferBytes; // NOTE: We could have a problem here if this is a duplex stream // and the play and capture hardware buffer sizes are different // (I'm actually not sure if that is a problem or not). // Currently, we are not verifying that. // Lock the capture buffer LPVOID audioPtr; DWORD dataLen; result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { input->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Zero the buffer ZeroMemory( audioPtr, dataLen ); // Unlock the buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { input->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices_[ deviceIdx ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } ohandle = (void *) input; bhandle = (void *) buffer; } // Set various stream parameters DsHandle *handle = 0; stream_.nDeviceChannels[mode] = channels + firstChannel; stream_.nUserChannels[mode] = channels; stream_.bufferSize = *bufferSize; stream_.channelOffset[mode] = firstChannel; stream_.deviceInterleaved[mode] = true; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; // Set flag for buffer conversion stream_.doConvertBuffer[mode] = false; if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.userFormat != stream_.deviceFormat[mode]) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= (long) bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } // Allocate our DsHandle structures for the stream. if ( stream_.apiHandle == 0 ) { try { handle = new DsHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory."; goto error; } // Create a manual-reset event. handle->condition = CreateEvent( NULL, // no security TRUE, // manual-reset FALSE, // non-signaled initially NULL ); // unnamed stream_.apiHandle = (void *) handle; } else handle = (DsHandle *) stream_.apiHandle; handle->id[mode] = ohandle; handle->buffer[mode] = bhandle; handle->dsBufferSize[mode] = dsBufferSize; handle->dsPointerLeadTime[mode] = dsPointerLeadTime; stream_.deviceId[mode] = deviceIdx; stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT && mode == INPUT ) // We had already set up an output stream. stream_.mode = DUPLEX; else stream_.mode = mode; stream_.nBuffers = nBuffers; stream_.sampleRate = sampleRate; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); // Setup the callback thread. if ( stream_.callbackInfo.isRunning == false ) { unsigned threadId; stream_.callbackInfo.isRunning = true; stream_.callbackInfo.object = (void *) this; stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler, &stream_.callbackInfo, 0, &threadId ); if ( stream_.callbackInfo.thread == 0 ) { errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!"; goto error; } // Boost DS thread priority SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST ); } return SUCCESS; error: if ( handle ) { if ( handle->buffer[0] ) { // the object pointer can be NULL and valid LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; if ( buffer ) buffer->Release(); object->Release(); } if ( handle->buffer[1] ) { LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; if ( buffer ) buffer->Release(); object->Release(); } CloseHandle( handle->condition ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.state = STREAM_CLOSED; return FAILURE; } void RtApiDs :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiDs::closeStream(): no open stream to close!"; error( RTAUDIO_WARNING ); return; } // Stop the callback thread. stream_.callbackInfo.isRunning = false; WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE ); CloseHandle( (HANDLE) stream_.callbackInfo.thread ); DsHandle *handle = (DsHandle *) stream_.apiHandle; if ( handle ) { if ( handle->buffer[0] ) { // the object pointer can be NULL and valid LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; if ( buffer ) { buffer->Stop(); buffer->Release(); } object->Release(); } if ( handle->buffer[1] ) { LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; if ( buffer ) { buffer->Stop(); buffer->Release(); } object->Release(); } CloseHandle( handle->condition ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } clearStreamInfo(); //stream_.mode = UNINITIALIZED; //stream_.state = STREAM_CLOSED; } RtAudioErrorType RtApiDs :: startStream() { if ( stream_.state != STREAM_STOPPED ) { if ( stream_.state == STREAM_RUNNING ) errorText_ = "RtApiDs::startStream(): the stream is already running!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiDs::startStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ DsHandle *handle = (DsHandle *) stream_.apiHandle; // Increase scheduler frequency on lesser windows (a side-effect of // increasing timer accuracy). On greater windows (Win2K or later), // this is already in effect. timeBeginPeriod( 1 ); buffersRolling = false; duplexPrerollBytes = 0; if ( stream_.mode == DUPLEX ) { // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize. duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] ); } HRESULT result = 0; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; result = buffer->Play( 0, 0, DSBPLAY_LOOPING ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!"; errorText_ = errorStream_.str(); goto unlock; } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; result = buffer->Start( DSCBSTART_LOOPING ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!"; errorText_ = errorStream_.str(); goto unlock; } } handle->drainCounter = 0; handle->internalDrain = false; ResetEvent( handle->condition ); stream_.state = STREAM_RUNNING; unlock: if ( FAILED( result ) ) error( RTAUDIO_SYSTEM_ERROR ); return RTAUDIO_NO_ERROR; } RtAudioErrorType RtApiDs :: stopStream() { if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiDs::stopStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_CLOSED ) errorText_ = "RtApiDs::stopStream(): the stream is closed!"; return error( RTAUDIO_WARNING ); } HRESULT result = 0; LPVOID audioPtr; DWORD dataLen; DsHandle *handle = (DsHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { handle->drainCounter = 2; WaitForSingleObject( handle->condition, INFINITE ); // block until signaled } stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); // Stop the buffer and clear memory LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; result = buffer->Stop(); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!"; errorText_ = errorStream_.str(); goto unlock; } // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!"; errorText_ = errorStream_.str(); goto unlock; } // Zero the DS buffer ZeroMemory( audioPtr, dataLen ); // Unlock the DS buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!"; errorText_ = errorStream_.str(); goto unlock; } // If we start playing again, we must begin at beginning of buffer. handle->bufferPointer[0] = 0; } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; audioPtr = NULL; dataLen = 0; stream_.state = STREAM_STOPPED; if ( stream_.mode != DUPLEX ) MUTEX_LOCK( &stream_.mutex ); result = buffer->Stop(); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!"; errorText_ = errorStream_.str(); goto unlock; } // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!"; errorText_ = errorStream_.str(); goto unlock; } // Zero the DS buffer ZeroMemory( audioPtr, dataLen ); // Unlock the DS buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!"; errorText_ = errorStream_.str(); goto unlock; } // If we start recording again, we must begin at beginning of buffer. handle->bufferPointer[1] = 0; } unlock: timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows. MUTEX_UNLOCK( &stream_.mutex ); if ( FAILED( result ) ) error( RTAUDIO_SYSTEM_ERROR ); return RTAUDIO_NO_ERROR; } RtAudioErrorType RtApiDs :: abortStream() { if ( stream_.state != STREAM_RUNNING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiDs::abortStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiDs::abortStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } DsHandle *handle = (DsHandle *) stream_.apiHandle; handle->drainCounter = 2; return stopStream(); } void RtApiDs :: callbackEvent() { if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) { Sleep( 50 ); // sleep 50 milliseconds return; } if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RTAUDIO_WARNING ); return; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; DsHandle *handle = (DsHandle *) stream_.apiHandle; // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > stream_.nBuffers + 2 ) { stream_.state = STREAM_STOPPING; if ( handle->internalDrain == false ) SetEvent( handle->condition ); else stopStream(); return; } // Invoke user callback to get fresh output data UNLESS we are // draining stream. if ( handle->drainCounter == 0 ) { RtAudioCallback callback = (RtAudioCallback) info->callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, info->userData ); if ( cbReturnValue == 2 ) { stream_.state = STREAM_STOPPING; handle->drainCounter = 2; abortStream(); return; } else if ( cbReturnValue == 1 ) { handle->drainCounter = 1; handle->internalDrain = true; } } HRESULT result; DWORD currentWritePointer, safeWritePointer; DWORD currentReadPointer, safeReadPointer; UINT nextWritePointer; LPVOID buffer1 = NULL; LPVOID buffer2 = NULL; DWORD bufferSize1 = 0; DWORD bufferSize2 = 0; char *buffer; long bufferBytes; MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_STOPPED ) { MUTEX_UNLOCK( &stream_.mutex ); return; } if ( buffersRolling == false ) { if ( stream_.mode == DUPLEX ) { //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); // It takes a while for the devices to get rolling. As a result, // there's no guarantee that the capture and write device pointers // will move in lockstep. Wait here for both devices to start // rolling, and then set our buffer pointers accordingly. // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600 // bytes later than the write buffer. // Stub: a serious risk of having a pre-emptive scheduling round // take place between the two GetCurrentPosition calls... but I'm // really not sure how to solve the problem. Temporarily boost to // Realtime priority, maybe; but I'm not sure what priority the // DirectSound service threads run at. We *should* be roughly // within a ms or so of correct. LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; DWORD startSafeWritePointer, startSafeReadPointer; result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } while ( true ) { result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break; Sleep( 1 ); } //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; handle->bufferPointer[1] = safeReadPointer; } else if ( stream_.mode == OUTPUT ) { // Set the proper nextWritePosition after initial startup. LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; } buffersRolling = true; } if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; if ( handle->drainCounter > 1 ) { // write zeros to the output stream bufferBytes = stream_.bufferSize * stream_.nUserChannels[0]; bufferBytes *= formatBytes( stream_.userFormat ); memset( stream_.userBuffer[0], 0, bufferBytes ); } // Setup parameters and do buffer conversion if necessary. if ( stream_.doConvertBuffer[0] ) { buffer = stream_.deviceBuffer; convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0]; bufferBytes *= formatBytes( stream_.deviceFormat[0] ); } else { buffer = stream_.userBuffer[0]; bufferBytes = stream_.bufferSize * stream_.nUserChannels[0]; bufferBytes *= formatBytes( stream_.userFormat ); } // No byte swapping necessary in DirectSound implementation. // Ahhh ... windoze. 16-bit data is signed but 8-bit data is // unsigned. So, we need to convert our signed 8-bit data here to // unsigned. if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 ) for ( int i=0; idsBufferSize[0]; nextWritePointer = handle->bufferPointer[0]; DWORD endWrite, leadPointer; while ( true ) { // Find out where the read and "safe write" pointers are. result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } // We will copy our output buffer into the region between // safeWritePointer and leadPointer. If leadPointer is not // beyond the next endWrite position, wait until it is. leadPointer = safeWritePointer + handle->dsPointerLeadTime[0]; //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl; if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize; if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset endWrite = nextWritePointer + bufferBytes; // Check whether the entire write region is behind the play pointer. if ( leadPointer >= endWrite ) break; // If we are here, then we must wait until the leadPointer advances // beyond the end of our next write region. We use the // Sleep() function to suspend operation until that happens. double millis = ( endWrite - leadPointer ) * 1000.0; millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); } if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize ) || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { // We've strayed into the forbidden zone ... resync the read pointer. handle->xrun[0] = true; nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes; if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize; handle->bufferPointer[0] = nextWritePointer; endWrite = nextWritePointer + bufferBytes; } // Lock free space in the buffer result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } // Copy our buffer into the DS buffer CopyMemory( buffer1, buffer, bufferSize1 ); if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 ); // Update our buffer offset and unlock sound buffer dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize; handle->bufferPointer[0] = nextWritePointer; } // Don't bother draining input if ( handle->drainCounter ) { handle->drainCounter++; goto unlock; } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { // Setup parameters. if ( stream_.doConvertBuffer[1] ) { buffer = stream_.deviceBuffer; bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1]; bufferBytes *= formatBytes( stream_.deviceFormat[1] ); } else { buffer = stream_.userBuffer[1]; bufferBytes = stream_.bufferSize * stream_.nUserChannels[1]; bufferBytes *= formatBytes( stream_.userFormat ); } LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; long nextReadPointer = handle->bufferPointer[1]; DWORD dsBufferSize = handle->dsBufferSize[1]; // Find out where the write and "safe read" pointers are. result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset DWORD endRead = nextReadPointer + bufferBytes; // Handling depends on whether we are INPUT or DUPLEX. // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode, // then a wait here will drag the write pointers into the forbidden zone. // // In DUPLEX mode, rather than wait, we will back off the read pointer until // it's in a safe position. This causes dropouts, but it seems to be the only // practical way to sync up the read and write pointers reliably, given the // the very complex relationship between phase and increment of the read and write // pointers. // // In order to minimize audible dropouts in DUPLEX mode, we will // provide a pre-roll period of 0.5 seconds in which we return // zeros from the read buffer while the pointers sync up. if ( stream_.mode == DUPLEX ) { if ( safeReadPointer < endRead ) { if ( duplexPrerollBytes <= 0 ) { // Pre-roll time over. Be more aggressive. int adjustment = endRead-safeReadPointer; handle->xrun[1] = true; // Two cases: // - large adjustments: we've probably run out of CPU cycles, so just resync exactly, // and perform fine adjustments later. // - small adjustments: back off by twice as much. if ( adjustment >= 2*bufferBytes ) nextReadPointer = safeReadPointer-2*bufferBytes; else nextReadPointer = safeReadPointer-bufferBytes-adjustment; if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; } else { // In pre=roll time. Just do it. nextReadPointer = safeReadPointer - bufferBytes; while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; } endRead = nextReadPointer + bufferBytes; } } else { // mode == INPUT while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) { // See comments for playback. double millis = (endRead - safeReadPointer) * 1000.0; millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); // Wake up and find out where we are now. result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset } } // Lock free space in the buffer result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } if ( duplexPrerollBytes <= 0 ) { // Copy our buffer into the DS buffer CopyMemory( buffer, buffer1, bufferSize1 ); if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 ); } else { memset( buffer, 0, bufferSize1 ); if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 ); duplexPrerollBytes -= bufferSize1 + bufferSize2; } // Update our buffer offset and unlock sound buffer nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize; dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RTAUDIO_SYSTEM_ERROR ); return; } handle->bufferPointer[1] = nextReadPointer; // No byte swapping necessary in DirectSound implementation. // If necessary, convert 8-bit data from unsigned to signed. if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 ) for ( int j=0; jobject; bool* isRunning = &info->isRunning; while ( *isRunning == true ) { object->callbackEvent(); } _endthreadex( 0 ); return 0; } static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, LPCTSTR description, LPCTSTR lpctstr, LPVOID lpContext ) { struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext; std::vector& dsDevices = *probeInfo.dsDevices; HRESULT hr; bool validDevice = false; if ( probeInfo.isInput == true ) { DSCCAPS caps; LPDIRECTSOUNDCAPTURE object; hr = DirectSoundCaptureCreate( lpguid, &object, NULL ); if ( hr != DS_OK ) return TRUE; caps.dwSize = sizeof(caps); hr = object->GetCaps( &caps ); if ( hr == DS_OK ) { if ( caps.dwChannels > 0 && caps.dwFormats > 0 ) validDevice = true; } object->Release(); } else { DSCAPS caps; LPDIRECTSOUND object; hr = DirectSoundCreate( lpguid, &object, NULL ); if ( hr != DS_OK ) return TRUE; caps.dwSize = sizeof(caps); hr = object->GetCaps( &caps ); if ( hr == DS_OK ) { if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO ) validDevice = true; } object->Release(); } if ( validDevice ) { // If good device, then save its name and guid. DsDevice device; device.name = convertCharPointerToStdString( description ); device.epID = convertCharPointerToStdString(lpctstr); device.id = lpguid; device.isInput = probeInfo.isInput; dsDevices.push_back( device ); } return TRUE; } static const char* getErrorString( int code ) { switch ( code ) { case DSERR_ALLOCATED: return "Already allocated"; case DSERR_CONTROLUNAVAIL: return "Control unavailable"; case DSERR_INVALIDPARAM: return "Invalid parameter"; case DSERR_INVALIDCALL: return "Invalid call"; case DSERR_GENERIC: return "Generic error"; case DSERR_PRIOLEVELNEEDED: return "Priority level needed"; case DSERR_OUTOFMEMORY: return "Out of memory"; case DSERR_BADFORMAT: return "The sample rate or the channel format is not supported"; case DSERR_UNSUPPORTED: return "Not supported"; case DSERR_NODRIVER: return "No driver"; case DSERR_ALREADYINITIALIZED: return "Already initialized"; case DSERR_NOAGGREGATION: return "No aggregation"; case DSERR_BUFFERLOST: return "Buffer lost"; case DSERR_OTHERAPPHASPRIO: return "Another application already has priority"; case DSERR_UNINITIALIZED: return "Uninitialized"; default: return "DirectSound unknown error"; } } //******************** End of __WINDOWS_DS__ *********************// #endif #if defined(__LINUX_ALSA__) #include #include // A structure to hold various information related to the ALSA API // implementation. struct AlsaHandle { snd_pcm_t *handles[2]; bool synchronized; bool xrun[2]; pthread_cond_t runnable_cv; bool runnable; AlsaHandle() #if _cplusplus >= 201103L :handles{nullptr, nullptr}, synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; } #else : synchronized(false), runnable(false) { handles[0] = NULL; handles[1] = NULL; xrun[0] = false; xrun[1] = false; } #endif }; static void *alsaCallbackHandler( void * ptr ); RtApiAlsa :: RtApiAlsa() { // Nothing to do here. } RtApiAlsa :: ~RtApiAlsa() { if ( stream_.state != STREAM_CLOSED ) closeStream(); } void RtApiAlsa :: probeDevices( void ) { // See list of required functionality in RtApi::probeDevices(). int result, device, card; char name[128]; snd_ctl_t *handle = 0; snd_ctl_card_info_t *ctlinfo; snd_pcm_info_t *pcminfo; snd_ctl_card_info_alloca(&ctlinfo); snd_pcm_info_alloca(&pcminfo); // First element isthe device hw ID, second is the device "pretty name" std::vector> deviceID_prettyName; snd_pcm_stream_t stream; std::string defaultDeviceName; // Add the default interface if available. result = snd_ctl_open( &handle, "default", 0 ); if (result == 0) { deviceID_prettyName.push_back({"default", "Default ALSA Device"}); defaultDeviceName = deviceID_prettyName[0].second; snd_ctl_close( handle ); } // Add the Pulse interface if available. result = snd_ctl_open( &handle, "pulse", 0 ); if (result == 0) { deviceID_prettyName.push_back({"pulse", "PulseAudio Sound Server"}); snd_ctl_close( handle ); } // Count cards and devices and get ascii identifiers. card = -1; snd_card_next( &card ); while ( card >= 0 ) { sprintf( name, "hw:%d", card ); result = snd_ctl_open( &handle, name, 0 ); if ( result < 0 ) { handle = 0; errorStream_ << "RtApiAlsa::probeDevices: control open, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto nextcard; } result = snd_ctl_card_info( handle, ctlinfo ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::probeDevices: control info, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto nextcard; } device = -1; while( 1 ) { result = snd_ctl_pcm_next_device( handle, &device ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::probeDevices: control next device, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); break; } if ( device < 0 ) break; snd_pcm_info_set_device( pcminfo, device ); snd_pcm_info_set_subdevice( pcminfo, 0 ); stream = SND_PCM_STREAM_PLAYBACK; snd_pcm_info_set_stream( pcminfo, stream ); result = snd_ctl_pcm_info( handle, pcminfo ); if ( result < 0 ) { if ( result == -ENOENT ) { // try as input stream stream = SND_PCM_STREAM_CAPTURE; snd_pcm_info_set_stream( pcminfo, stream ); result = snd_ctl_pcm_info( handle, pcminfo ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::probeDevices: control pcm info, card = " << card << ", device = " << device << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); continue; } } else continue; } sprintf( name, "hw:%s,%d", snd_ctl_card_info_get_id(ctlinfo), device ); std::string id(name); sprintf( name, "%s (%s)", snd_ctl_card_info_get_name(ctlinfo), snd_pcm_info_get_id(pcminfo) ); std::string prettyName(name); deviceID_prettyName.push_back( {id, prettyName} ); if ( card == 0 && device == 0 && defaultDeviceName.empty() ) defaultDeviceName = name; } nextcard: if ( handle ) snd_ctl_close( handle ); snd_card_next( &card ); } if ( deviceID_prettyName.size() == 0 ) { deviceList_.clear(); deviceIdPairs_.clear(); return; } // Clean removed devices for ( auto it = deviceIdPairs_.begin(); it != deviceIdPairs_.end(); ) { bool found = false; for ( auto& d: deviceID_prettyName ) { if ( d.first == (*it).first ) { found = true; break; } } if ( found ) ++it; else it = deviceIdPairs_.erase(it); } // Fill or update the deviceList_ and also save a corresponding list of Ids. for ( auto& d : deviceID_prettyName ) { bool found = false; for ( auto& dID : deviceIdPairs_ ) { if ( d.first == dID.first ) { found = true; break; // We already have this device. } } if ( found ) continue; // new device RtAudio::DeviceInfo info; info.name = d.second; if ( probeDeviceInfo( info, d.first ) == false ) continue; // ignore if probe fails info.ID = currentDeviceId_++; // arbitrary internal device ID if ( info.name == defaultDeviceName ) { if ( info.outputChannels > 0 ) info.isDefaultOutput = true; if ( info.inputChannels > 0 ) info.isDefaultInput = true; } deviceList_.push_back( info ); deviceIdPairs_.push_back({d.first, info.ID}); // I don't see that ALSA provides property listeners to know if // devices are removed or added. } // Remove any devices left in the list that are no longer available. for ( std::vector::iterator it=deviceList_.begin(); it!=deviceList_.end(); ) { auto itID = deviceIdPairs_.begin(); while ( itID != deviceIdPairs_.end() ) { if ( (*it).ID == (*itID).second ) { break; } ++itID; } if ( itID == deviceIdPairs_.end() ) { // not found so remove it from our list it = deviceList_.erase( it ); } else ++it; } } bool RtApiAlsa :: probeDeviceInfo( RtAudio::DeviceInfo& info, std::string name ) { int result, openMode = SND_PCM_ASYNC; snd_pcm_stream_t stream; snd_pcm_t *phandle; snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca( ¶ms ); // First try for playback stream = SND_PCM_STREAM_PLAYBACK; result = snd_pcm_open( &phandle, name.c_str(), stream, openMode | SND_PCM_NONBLOCK ); if ( result < 0 ) { if ( result == -16 ) return false; // device busy ... can't probe or use if ( result != -2 ) { // device doesn't support playback errorStream_ << "RtApiAlsa::probeDeviceInfo: snd_pcm_open (playback) error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); } goto captureProbe; } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any( phandle, params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto captureProbe; } // Get output channel information. unsigned int value; result = snd_pcm_hw_params_get_channels_max( params, &value ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto captureProbe; } info.outputChannels = value; snd_pcm_close( phandle ); captureProbe: stream = SND_PCM_STREAM_CAPTURE; result = snd_pcm_open( &phandle, name.c_str(), stream, openMode | SND_PCM_NONBLOCK); if ( result < 0 && result ) { if ( result != -2 && result != -16 ) { // device busy or doesn't support capture errorStream_ << "RtApiAlsa::probeDeviceInfo: snd_pcm_open (capture) error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); } if ( info.outputChannels == 0 ) return false; goto probeParameters; } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any( phandle, params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); if ( info.outputChannels == 0 ) return false; goto probeParameters; } result = snd_pcm_hw_params_get_channels_max( params, &value ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); if ( info.outputChannels == 0 ) return false; goto probeParameters; } info.inputChannels = value; snd_pcm_close( phandle ); // If device opens for both playback and capture, we determine the channels. if ( info.outputChannels > 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; probeParameters: // At this point, we just need to figure out the supported data // formats and sample rates. We'll proceed by opening the device in // the direction with the maximum number of channels, or playback if // they are equal. This might limit our sample rate options, but so // be it. if ( info.outputChannels >= info.inputChannels ) stream = SND_PCM_STREAM_PLAYBACK; else stream = SND_PCM_STREAM_CAPTURE; result = snd_pcm_open( &phandle, name.c_str(), stream, openMode | SND_PCM_NONBLOCK); if ( result < 0 ) { errorStream_ << "RtApiAlsa::probeDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any( phandle, params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Test our discrete set of sample rate values. info.sampleRates.clear(); for ( unsigned int i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[i]; } } if ( info.sampleRates.size() == 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceInfo: no supported sample rates found for device (" << name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Probe the supported data formats ... we don't care about endian-ness just yet snd_pcm_format_t format; info.nativeFormats = 0; format = SND_PCM_FORMAT_S8; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_SINT8; format = SND_PCM_FORMAT_S16; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_SINT16; format = SND_PCM_FORMAT_S24; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_SINT24; format = SND_PCM_FORMAT_S32; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_SINT32; format = SND_PCM_FORMAT_FLOAT; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_FLOAT32; format = SND_PCM_FORMAT_FLOAT64; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_FLOAT64; // Check that we have at least one supported format if ( info.nativeFormats == 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Close the device and return snd_pcm_close( phandle ); return true; } bool RtApiAlsa :: probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { #if defined(__RTAUDIO_DEBUG__) struct SndOutputTdealloc { SndOutputTdealloc() : _out(NULL) { snd_output_stdio_attach(&_out, stderr, 0); } ~SndOutputTdealloc() { snd_output_close(_out); } operator snd_output_t*() { return _out; } snd_output_t *_out; } out; #endif std::string name; for ( auto& id : deviceIdPairs_) { if ( id.second == deviceId ) { name = id.first; break; } } snd_pcm_stream_t stream; if ( mode == OUTPUT ) stream = SND_PCM_STREAM_PLAYBACK; else stream = SND_PCM_STREAM_CAPTURE; snd_pcm_t *phandle; int openMode = SND_PCM_ASYNC; int result = snd_pcm_open( &phandle, name.c_str(), stream, openMode ); if ( result < 0 ) { if ( mode == OUTPUT ) errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output."; else errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input."; errorText_ = errorStream_.str(); return FAILURE; } // Fill the parameter structure. snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca( &hw_params ); result = snd_pcm_hw_params_any( phandle, hw_params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" ); snd_pcm_hw_params_dump( hw_params, out ); #endif // Set access ... check user preference. if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) { stream_.userInterleaved = false; result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED ); if ( result < 0 ) { result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ); stream_.deviceInterleaved[mode] = true; } else stream_.deviceInterleaved[mode] = false; } else { stream_.userInterleaved = true; result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ); if ( result < 0 ) { result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED ); stream_.deviceInterleaved[mode] = false; } else stream_.deviceInterleaved[mode] = true; } if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // Determine how to set the device format. stream_.userFormat = format; snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN; if ( format == RTAUDIO_SINT8 ) deviceFormat = SND_PCM_FORMAT_S8; else if ( format == RTAUDIO_SINT16 ) deviceFormat = SND_PCM_FORMAT_S16; else if ( format == RTAUDIO_SINT24 ) deviceFormat = SND_PCM_FORMAT_S24; else if ( format == RTAUDIO_SINT32 ) deviceFormat = SND_PCM_FORMAT_S32; else if ( format == RTAUDIO_FLOAT32 ) deviceFormat = SND_PCM_FORMAT_FLOAT; else if ( format == RTAUDIO_FLOAT64 ) deviceFormat = SND_PCM_FORMAT_FLOAT64; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) { stream_.deviceFormat[mode] = format; goto setFormat; } // The user requested format is not natively supported by the device. deviceFormat = SND_PCM_FORMAT_FLOAT64; if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; goto setFormat; } deviceFormat = SND_PCM_FORMAT_FLOAT; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; goto setFormat; } deviceFormat = SND_PCM_FORMAT_S32; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_SINT32; goto setFormat; } deviceFormat = SND_PCM_FORMAT_S24; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_SINT24; goto setFormat; } deviceFormat = SND_PCM_FORMAT_S16; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_SINT16; goto setFormat; } deviceFormat = SND_PCM_FORMAT_S8; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_SINT8; goto setFormat; } // If we get here, no supported format was found. snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); return FAILURE; setFormat: result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // Determine whether byte-swaping is necessary. stream_.doByteSwap[mode] = false; if ( deviceFormat != SND_PCM_FORMAT_S8 ) { result = snd_pcm_format_cpu_endian( deviceFormat ); if ( result == 0 ) stream_.doByteSwap[mode] = true; else if (result < 0) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } } // Set the sample rate. result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // Determine the number of channels for this device. We support a possible // minimum device channel number > than the value requested by the user. stream_.nUserChannels[mode] = channels; unsigned int value; result = snd_pcm_hw_params_get_channels_max( hw_params, &value ); unsigned int deviceChannels = value; if ( result < 0 || deviceChannels < channels + firstChannel ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } result = snd_pcm_hw_params_get_channels_min( hw_params, &value ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } deviceChannels = value; if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel; stream_.nDeviceChannels[mode] = deviceChannels; // Set the device channels. result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // Set the buffer (or period) size. int dir = 0; snd_pcm_uframes_t periodSize = *bufferSize; result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } *bufferSize = periodSize; // Set the buffer number, which in ALSA is referred to as the "period". unsigned int periods = 0; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2; if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers; if ( periods < 2 ) periods = 4; // a fairly safe default value result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.bufferSize = *bufferSize; // Install the hardware configuration result = snd_pcm_hw_params( phandle, hw_params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n"); snd_pcm_hw_params_dump( hw_params, out ); #endif // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns. snd_pcm_sw_params_t *sw_params = NULL; snd_pcm_sw_params_alloca( &sw_params ); snd_pcm_sw_params_current( phandle, sw_params ); snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize ); snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX ); snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 ); // The following two settings were suggested by Theo Veenker //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize ); //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 ); // here are two options for a fix //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX ); snd_pcm_uframes_t val; snd_pcm_sw_params_get_boundary( sw_params, &val ); snd_pcm_sw_params_set_silence_size( phandle, sw_params, val ); result = snd_pcm_sw_params( phandle, sw_params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n"); snd_pcm_sw_params_dump( sw_params, out ); #endif // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate the ApiHandle if necessary and then save. AlsaHandle *apiInfo = 0; if ( stream_.apiHandle == 0 ) { try { apiInfo = (AlsaHandle *) new AlsaHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory."; goto error; } if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) { errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable."; goto error; } stream_.apiHandle = (void *) apiInfo; apiInfo->handles[0] = 0; apiInfo->handles[1] = 0; } else { apiInfo = (AlsaHandle *) stream_.apiHandle; } apiInfo->handles[mode] = phandle; phandle = 0; // Allocate necessary internal buffers. unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } stream_.sampleRate = sampleRate; stream_.nBuffers = periods; stream_.deviceId[mode] = deviceId; stream_.state = STREAM_STOPPED; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); // Setup thread if necessary. if ( stream_.mode == OUTPUT && mode == INPUT ) { // We had already set up an output stream. stream_.mode = DUPLEX; // Link the streams if possible. apiInfo->synchronized = false; if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 ) apiInfo->synchronized = true; else { errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices."; error( RTAUDIO_WARNING ); } } else { stream_.mode = mode; // Setup callback thread. stream_.callbackInfo.object = (void *) this; // Set the thread attributes for joinable and realtime scheduling // priority (optional). The higher priority will only take affect // if the program is run as root or suid. Note, under Linux // processes with CAP_SYS_NICE privilege, a user can change // scheduling policy and priority (thus need not be root). See // POSIX "capabilities". pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); #if defined(SCHED_RR) && !defined(__OpenBSD__) // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { stream_.callbackInfo.doRealtime = true; struct sched_param param; int priority = options->priority; int min = sched_get_priority_min( SCHED_RR ); int max = sched_get_priority_max( SCHED_RR ); if ( priority < min ) priority = min; else if ( priority > max ) priority = max; param.sched_priority = priority; // Set the policy BEFORE the priority. Otherwise it fails. pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); // This is definitely required. Otherwise it fails. pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedparam(&attr, ¶m); } else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif stream_.callbackInfo.isRunning = true; result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo ); pthread_attr_destroy( &attr ); if ( result ) { // Failed. Try instead with default attributes. result = pthread_create( &stream_.callbackInfo.thread, NULL, alsaCallbackHandler, &stream_.callbackInfo ); if ( result ) { stream_.callbackInfo.isRunning = false; errorText_ = "RtApiAlsa::error creating callback thread!"; goto error; } } } return SUCCESS; error: if ( apiInfo ) { pthread_cond_destroy( &apiInfo->runnable_cv ); if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] ); if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] ); delete apiInfo; stream_.apiHandle = 0; } if ( phandle) snd_pcm_close( phandle ); for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.state = STREAM_CLOSED; return FAILURE; } void RtApiAlsa :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAlsa::closeStream(): no open stream to close!"; error( RTAUDIO_WARNING ); return; } AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; stream_.callbackInfo.isRunning = false; MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_STOPPED ) { apiInfo->runnable = true; pthread_cond_signal( &apiInfo->runnable_cv ); } MUTEX_UNLOCK( &stream_.mutex ); pthread_join( stream_.callbackInfo.thread, NULL ); if ( stream_.state == STREAM_RUNNING ) { stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) snd_pcm_drop( apiInfo->handles[0] ); if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) snd_pcm_drop( apiInfo->handles[1] ); } if ( apiInfo ) { pthread_cond_destroy( &apiInfo->runnable_cv ); if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] ); if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] ); delete apiInfo; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } clearStreamInfo(); } RtAudioErrorType RtApiAlsa :: startStream() { // This method calls snd_pcm_prepare if the device isn't already in that state. if ( stream_.state != STREAM_STOPPED ) { if ( stream_.state == STREAM_RUNNING ) errorText_ = "RtApiAlsa::startStream(): the stream is already running!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiAlsa::startStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } MUTEX_LOCK( &stream_.mutex ); /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ int result = 0; snd_pcm_state_t state; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { state = snd_pcm_state( handle[0] ); if ( state != SND_PCM_STATE_PREPARED ) { result = snd_pcm_prepare( handle[0] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } } if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open state = snd_pcm_state( handle[1] ); if ( state != SND_PCM_STATE_PREPARED ) { result = snd_pcm_prepare( handle[1] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } } stream_.state = STREAM_RUNNING; unlock: apiInfo->runnable = true; pthread_cond_signal( &apiInfo->runnable_cv ); MUTEX_UNLOCK( &stream_.mutex ); if ( result < 0 ) return error( RTAUDIO_SYSTEM_ERROR ); return RTAUDIO_NO_ERROR; } RtAudioErrorType RtApiAlsa :: stopStream() { if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_CLOSED ) errorText_ = "RtApiAlsa::stopStream(): the stream is closed!"; return error( RTAUDIO_WARNING ); } stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); int result = 0; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( apiInfo->synchronized ) result = snd_pcm_drop( handle[0] ); else result = snd_pcm_drain( handle[0] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { result = snd_pcm_drop( handle[1] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } unlock: apiInfo->runnable = false; // fixes high CPU usage when stopped MUTEX_UNLOCK( &stream_.mutex ); if ( result < 0 ) return error( RTAUDIO_SYSTEM_ERROR ); return RTAUDIO_NO_ERROR; } RtAudioErrorType RtApiAlsa :: abortStream() { if ( stream_.state != STREAM_RUNNING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiAlsa::abortStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); int result = 0; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { result = snd_pcm_drop( handle[0] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { result = snd_pcm_drop( handle[1] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } unlock: apiInfo->runnable = false; // fixes high CPU usage when stopped MUTEX_UNLOCK( &stream_.mutex ); if ( result < 0 ) return error( RTAUDIO_SYSTEM_ERROR ); return RTAUDIO_NO_ERROR; } void RtApiAlsa :: callbackEvent() { AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; if ( stream_.state == STREAM_STOPPED ) { MUTEX_LOCK( &stream_.mutex ); while ( !apiInfo->runnable ) pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex ); if ( stream_.state != STREAM_RUNNING ) { MUTEX_UNLOCK( &stream_.mutex ); return; } MUTEX_UNLOCK( &stream_.mutex ); } if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RTAUDIO_WARNING ); return; } int doStopStream = 0; RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; apiInfo->xrun[0] = false; } if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; apiInfo->xrun[1] = false; } doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); if ( doStopStream == 2 ) { abortStream(); return; } MUTEX_LOCK( &stream_.mutex ); // The state might change while waiting on a mutex. if ( stream_.state == STREAM_STOPPED ) goto unlock; int result; char *buffer; int channels; snd_pcm_t **handle; snd_pcm_sframes_t frames; RtAudioFormat format; handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { // Setup parameters. if ( stream_.doConvertBuffer[1] ) { buffer = stream_.deviceBuffer; channels = stream_.nDeviceChannels[1]; format = stream_.deviceFormat[1]; } else { buffer = stream_.userBuffer[1]; channels = stream_.nUserChannels[1]; format = stream_.userFormat; } // Read samples from device in interleaved/non-interleaved format. if ( stream_.deviceInterleaved[1] ) result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize ); else { void *bufs[channels]; size_t offset = stream_.bufferSize * formatBytes( format ); for ( int i=0; ixrun[1] = true; result = snd_pcm_prepare( handle[1] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } } else { errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } } else { errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } error( RTAUDIO_WARNING ); goto tryOutput; } // Do byte swapping if necessary. if ( stream_.doByteSwap[1] ) byteSwapBuffer( buffer, stream_.bufferSize * channels, format ); // Do buffer conversion if necessary. if ( stream_.doConvertBuffer[1] ) convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); // Check stream latency result = snd_pcm_delay( handle[1], &frames ); if ( result == 0 && frames > 0 ) stream_.latency[1] = frames; } tryOutput: if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { // Setup parameters and do buffer conversion if necessary. if ( stream_.doConvertBuffer[0] ) { buffer = stream_.deviceBuffer; convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); channels = stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { buffer = stream_.userBuffer[0]; channels = stream_.nUserChannels[0]; format = stream_.userFormat; } // Do byte swapping if necessary. if ( stream_.doByteSwap[0] ) byteSwapBuffer(buffer, stream_.bufferSize * channels, format); // Write samples to device in interleaved/non-interleaved format. if ( stream_.deviceInterleaved[0] ) result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize ); else { void *bufs[channels]; size_t offset = stream_.bufferSize * formatBytes( format ); for ( int i=0; ixrun[0] = true; result = snd_pcm_prepare( handle[0] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } else errorText_ = "RtApiAlsa::callbackEvent: audio write error, underrun."; } else { errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } } else { errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } error( RTAUDIO_WARNING ); goto unlock; } // Check stream latency result = snd_pcm_delay( handle[0], &frames ); if ( result == 0 && frames > 0 ) stream_.latency[0] = frames; } unlock: MUTEX_UNLOCK( &stream_.mutex ); RtApi::tickStreamTime(); if ( doStopStream == 1 ) this->stopStream(); } static void *alsaCallbackHandler( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiAlsa *object = (RtApiAlsa *) info->object; bool *isRunning = &info->isRunning; #if defined(SCHED_RR) && !defined(__OpenBSD__) // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) if ( info->doRealtime ) { std::cerr << "RtAudio alsa: " << (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << "running realtime scheduling" << std::endl; } #endif while ( *isRunning == true ) { pthread_testcancel(); object->callbackEvent(); } pthread_exit( NULL ); } //******************** End of __LINUX_ALSA__ *********************// #endif #if defined(__LINUX_PULSE__) // Code written by Peter Meerwald, pmeerw@pmeerw.net and Tristan Matthews. // Updated by Gary Scavone, 2021. #include #include #include // A structure needed to pass variables for device probing. struct PaDeviceProbeInfo { pa_mainloop_api *paMainLoopApi; std::string defaultSinkName; std::string defaultSourceName; int defaultRate; unsigned int *currentDeviceId; std::vector< std::string > deviceNames; std::vector< RtApiPulse::PaDeviceInfo > *paDeviceList; std::vector< RtAudio::DeviceInfo > *rtDeviceList; }; static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000, 44100, 48000, 96000, 192000, 0}; struct rtaudio_pa_format_mapping_t { RtAudioFormat rtaudio_format; pa_sample_format_t pa_format; }; static const rtaudio_pa_format_mapping_t supported_sampleformats[] = { {RTAUDIO_SINT16, PA_SAMPLE_S16LE}, {RTAUDIO_SINT24, PA_SAMPLE_S24LE}, {RTAUDIO_SINT32, PA_SAMPLE_S32LE}, {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE}, {0, PA_SAMPLE_INVALID}}; struct PulseAudioHandle { pa_simple *s_play; pa_simple *s_rec; pthread_t thread; pthread_cond_t runnable_cv; bool runnable; PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { } }; // The following 3 functions are called by the device probing // system. This first one gets overall system information. static void rt_pa_set_server_info( pa_context *context, const pa_server_info *info, void *userdata ) { (void)context; pa_sample_spec ss; PaDeviceProbeInfo *paProbeInfo = static_cast( userdata ); if (!info) { paProbeInfo->paMainLoopApi->quit( paProbeInfo->paMainLoopApi, 1 ); return; } ss = info->sample_spec; paProbeInfo->defaultRate = ss.rate; paProbeInfo->defaultSinkName = info->default_sink_name; paProbeInfo->defaultSourceName = info->default_source_name; } // Used to get output device information. static void rt_pa_set_sink_info( pa_context * /*c*/, const pa_sink_info *i, int eol, void *userdata ) { if ( eol ) return; PaDeviceProbeInfo *paProbeInfo = static_cast( userdata ); std::string name = pa_proplist_gets( i->proplist, "device.description" ); paProbeInfo->deviceNames.push_back( name ); for ( size_t n=0; nrtDeviceList->size(); n++ ) if ( paProbeInfo->rtDeviceList->at(n).name == name ) return; // we've already probed this one RtAudio::DeviceInfo info; info.name = name; info.outputChannels = i->sample_spec.channels; info.preferredSampleRate = i->sample_spec.rate; info.isDefaultOutput = ( paProbeInfo->defaultSinkName == i->name ); for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) info.sampleRates.push_back( *sr ); for ( const rtaudio_pa_format_mapping_t *fm = supported_sampleformats; fm->rtaudio_format; ++fm ) info.nativeFormats |= fm->rtaudio_format; info.ID = *(paProbeInfo->currentDeviceId); *(paProbeInfo->currentDeviceId) = info.ID + 1; paProbeInfo->rtDeviceList->push_back( info ); RtApiPulse::PaDeviceInfo painfo; painfo.sinkName = i->name; paProbeInfo->paDeviceList->push_back( painfo ); } // Used to get input device information. static void rt_pa_set_source_info_and_quit( pa_context * /*c*/, const pa_source_info *i, int eol, void *userdata ) { PaDeviceProbeInfo *paProbeInfo = static_cast( userdata ); if ( eol ) { paProbeInfo->paMainLoopApi->quit( paProbeInfo->paMainLoopApi, 0 ); return; } std::string name = pa_proplist_gets( i->proplist, "device.description" ); paProbeInfo->deviceNames.push_back( name ); for ( size_t n=0; nrtDeviceList->size(); n++ ) { if ( paProbeInfo->rtDeviceList->at(n).name == name ) { // Check if we've already probed this as an output. if ( !paProbeInfo->paDeviceList->at(n).sinkName.empty() ) { // This must be a duplex device. Update the device info. paProbeInfo->paDeviceList->at(n).sourceName = i->name; paProbeInfo->rtDeviceList->at(n).inputChannels = i->sample_spec.channels; paProbeInfo->rtDeviceList->at(n).isDefaultInput = ( paProbeInfo->defaultSourceName == i->name ); paProbeInfo->rtDeviceList->at(n).duplexChannels = (paProbeInfo->rtDeviceList->at(n).inputChannels < paProbeInfo->rtDeviceList->at(n).outputChannels) ? paProbeInfo->rtDeviceList->at(n).inputChannels : paProbeInfo->rtDeviceList->at(n).outputChannels; } return; // we already have this } } RtAudio::DeviceInfo info; info.name = name; info.inputChannels = i->sample_spec.channels; info.preferredSampleRate = i->sample_spec.rate; info.isDefaultInput = ( paProbeInfo->defaultSourceName == i->name ); for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) info.sampleRates.push_back( *sr ); for ( const rtaudio_pa_format_mapping_t *fm = supported_sampleformats; fm->rtaudio_format; ++fm ) info.nativeFormats |= fm->rtaudio_format; info.ID = *(paProbeInfo->currentDeviceId); *(paProbeInfo->currentDeviceId) = info.ID + 1; paProbeInfo->rtDeviceList->push_back( info ); RtApiPulse::PaDeviceInfo painfo; painfo.sourceName = i->name; paProbeInfo->paDeviceList->push_back( painfo ); } // This is the initial function that is called when the callback is // set. This one then calls the functions above. static void rt_pa_context_state_callback( pa_context *context, void *userdata ) { PaDeviceProbeInfo *paProbeInfo = static_cast( userdata ); auto state = pa_context_get_state(context); switch (state) { case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: pa_context_get_server_info( context, rt_pa_set_server_info, userdata ); // server info pa_context_get_sink_info_list( context, rt_pa_set_sink_info, userdata ); // output info ... needs to be before input pa_context_get_source_info_list( context, rt_pa_set_source_info_and_quit, userdata ); // input info break; case PA_CONTEXT_TERMINATED: paProbeInfo->paMainLoopApi->quit( paProbeInfo->paMainLoopApi, 0 ); break; case PA_CONTEXT_FAILED: default: paProbeInfo->paMainLoopApi->quit( paProbeInfo->paMainLoopApi, 1 ); } } RtApiPulse::~RtApiPulse() { if ( stream_.state != STREAM_CLOSED ) closeStream(); } void RtApiPulse :: probeDevices( void ) { // See list of required functionality in RtApi::probeDevices(). pa_mainloop *ml = NULL; pa_context *context = NULL; char *server = NULL; int ret = 1; PaDeviceProbeInfo paProbeInfo; if (!(ml = pa_mainloop_new())) { errorStream_ << "RtApiPulse::probeDevices: pa_mainloop_new() failed."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto quit; } paProbeInfo.paMainLoopApi = pa_mainloop_get_api( ml ); paProbeInfo.currentDeviceId = ¤tDeviceId_; paProbeInfo.paDeviceList = &paDeviceList_; paProbeInfo.rtDeviceList = &deviceList_; if (!(context = pa_context_new_with_proplist( paProbeInfo.paMainLoopApi, NULL, NULL ))) { errorStream_ << "RtApiPulse::probeDevices: pa_context_new() failed."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto quit; } pa_context_set_state_callback( context, rt_pa_context_state_callback, &paProbeInfo ); if (pa_context_connect( context, server, PA_CONTEXT_NOFLAGS, NULL ) < 0) { errorStream_ << "RtApiPulse::probeDevices: pa_context_connect() failed: " << pa_strerror(pa_context_errno(context)); errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto quit; } if (pa_mainloop_run( ml, &ret ) < 0) { errorStream_ << "RtApiPulse::probeDevices: pa_mainloop_run() failed."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto quit; } if (ret != 0) { errorStream_ << "RtApiPulse::probeDevices: could not get server info."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); goto quit; } // Check for devices that have been unplugged. unsigned int m; for ( std::vector::iterator it=deviceList_.begin(); it!=deviceList_.end(); ) { for ( m=0; m( user ); RtApiPulse *context = static_cast( cbi->object ); volatile bool *isRunning = &cbi->isRunning; #if defined(SCHED_RR) && !defined(__OpenBSD__) // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) if (cbi->doRealtime) { std::cerr << "RtAudio pulse: " << (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << "running realtime scheduling" << std::endl; } #endif while ( *isRunning ) { pthread_testcancel(); context->callbackEvent(); } pthread_exit( NULL ); } bool RtApiPulse::probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { PulseAudioHandle *pah = 0; unsigned long bufferBytes = 0; pa_sample_spec ss; int deviceIdx = -1; for ( unsigned int m=0; mrtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) { if ( format == sf->rtaudio_format ) { sf_found = true; stream_.userFormat = sf->rtaudio_format; stream_.deviceFormat[mode] = stream_.userFormat; ss.format = sf->pa_format; break; } } if ( !sf_found ) { // Use internal data format conversion. stream_.userFormat = format; stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; ss.format = PA_SAMPLE_FLOAT32LE; } // Set other stream parameters. if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; stream_.nBuffers = options ? options->numberOfBuffers : 1; stream_.doByteSwap[mode] = false; stream_.nUserChannels[mode] = channels; stream_.nDeviceChannels[mode] = channels + firstChannel; stream_.channelOffset[mode] = 0; std::string streamName = "RtAudio"; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] ) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers. bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory."; goto error; } stream_.bufferSize = *bufferSize; if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } stream_.deviceId[mode] = deviceIdx; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); if ( !stream_.apiHandle ) { PulseAudioHandle *pah = new PulseAudioHandle; if ( !pah ) { errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle."; goto error; } stream_.apiHandle = pah; if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) { errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable."; goto error; } } pah = static_cast( stream_.apiHandle ); int error; if ( options && !options->streamName.empty() ) streamName = options->streamName; switch ( mode ) { pa_buffer_attr buffer_attr; case INPUT: buffer_attr.fragsize = bufferBytes; buffer_attr.maxlength = -1; pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, dev_input, "Record", &ss, NULL, &buffer_attr, &error ); if ( !pah->s_rec ) { errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server."; goto error; } break; case OUTPUT: { pa_buffer_attr * attr_ptr; if ( options && options->numberOfBuffers > 0 ) { // pa_buffer_attr::fragsize is recording-only. // Hopefully PortAudio won't access uninitialized fields. buffer_attr.maxlength = bufferBytes * options->numberOfBuffers; buffer_attr.minreq = -1; buffer_attr.prebuf = -1; buffer_attr.tlength = -1; attr_ptr = &buffer_attr; } else { attr_ptr = nullptr; } pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, dev_output, "Playback", &ss, NULL, attr_ptr, &error ); if ( !pah->s_play ) { errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server."; goto error; } break; } case DUPLEX: /* Note: We could add DUPLEX by synchronizing multiple streams, but it would mean moving from Simple API to Asynchronous API: https://freedesktop.org/software/pulseaudio/doxygen/streams.html#sync_streams */ errorText_ = "RtApiPulse::probeDeviceOpen: duplex not supported for PulseAudio."; goto error; default: goto error; } if ( stream_.mode == UNINITIALIZED ) stream_.mode = mode; else if ( stream_.mode == mode ) goto error; else stream_.mode = DUPLEX; if ( !stream_.callbackInfo.isRunning ) { stream_.callbackInfo.object = this; stream_.state = STREAM_STOPPED; // Set the thread attributes for joinable and realtime scheduling // priority (optional). The higher priority will only take affect // if the program is run as root or suid. Note, under Linux // processes with CAP_SYS_NICE privilege, a user can change // scheduling policy and priority (thus need not be root). See // POSIX "capabilities". pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); #if defined(SCHED_RR) && !defined(__OpenBSD__) // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { stream_.callbackInfo.doRealtime = true; struct sched_param param; int priority = options->priority; int min = sched_get_priority_min( SCHED_RR ); int max = sched_get_priority_max( SCHED_RR ); if ( priority < min ) priority = min; else if ( priority > max ) priority = max; param.sched_priority = priority; // Set the policy BEFORE the priority. Otherwise it fails. pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); // This is definitely required. Otherwise it fails. pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedparam(&attr, ¶m); } else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif stream_.callbackInfo.isRunning = true; int result = pthread_create( &pah->thread, &attr, pulseaudio_callback, (void *)&stream_.callbackInfo); pthread_attr_destroy(&attr); if(result != 0) { // Failed. Try instead with default attributes. result = pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo); if(result != 0) { stream_.callbackInfo.isRunning = false; errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread."; goto error; } } } return SUCCESS; error: if ( pah && stream_.callbackInfo.isRunning ) { pthread_cond_destroy( &pah->runnable_cv ); delete pah; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.state = STREAM_CLOSED; return FAILURE; } void RtApiPulse::closeStream( void ) { PulseAudioHandle *pah = static_cast( stream_.apiHandle ); stream_.callbackInfo.isRunning = false; if ( pah ) { MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_STOPPED ) { pah->runnable = true; pthread_cond_signal( &pah->runnable_cv ); } MUTEX_UNLOCK( &stream_.mutex ); pthread_join( pah->thread, 0 ); if ( pah->s_play ) { pa_simple_flush( pah->s_play, NULL ); pa_simple_free( pah->s_play ); } if ( pah->s_rec ) pa_simple_free( pah->s_rec ); pthread_cond_destroy( &pah->runnable_cv ); delete pah; stream_.apiHandle = 0; } if ( stream_.userBuffer[0] ) { free( stream_.userBuffer[0] ); stream_.userBuffer[0] = 0; } if ( stream_.userBuffer[1] ) { free( stream_.userBuffer[1] ); stream_.userBuffer[1] = 0; } clearStreamInfo(); } void RtApiPulse::callbackEvent( void ) { PulseAudioHandle *pah = static_cast( stream_.apiHandle ); if ( stream_.state == STREAM_STOPPED ) { MUTEX_LOCK( &stream_.mutex ); while ( !pah->runnable ) pthread_cond_wait( &pah->runnable_cv, &stream_.mutex ); if ( stream_.state != STREAM_RUNNING ) { MUTEX_UNLOCK( &stream_.mutex ); return; } MUTEX_UNLOCK( &stream_.mutex ); } if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... " "this shouldn't happen!"; error( RTAUDIO_WARNING ); return; } RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); if ( doStopStream == 2 ) { abortStream(); return; } MUTEX_LOCK( &stream_.mutex ); void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT]; void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT]; if ( stream_.state != STREAM_RUNNING ) goto unlock; int pa_error; size_t bytes; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( stream_.doConvertBuffer[OUTPUT] ) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer[OUTPUT], stream_.convertInfo[OUTPUT] ); bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize * formatBytes( stream_.deviceFormat[OUTPUT] ); } else bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize * formatBytes( stream_.userFormat ); if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::callbackEvent: audio write error, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX) { if ( stream_.doConvertBuffer[INPUT] ) bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize * formatBytes( stream_.deviceFormat[INPUT] ); else bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize * formatBytes( stream_.userFormat ); if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::callbackEvent: audio read error, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); } if ( stream_.doConvertBuffer[INPUT] ) { convertBuffer( stream_.userBuffer[INPUT], stream_.deviceBuffer, stream_.convertInfo[INPUT] ); } } unlock: MUTEX_UNLOCK( &stream_.mutex ); RtApi::tickStreamTime(); if ( doStopStream == 1 ) stopStream(); } RtAudioErrorType RtApiPulse::startStream( void ) { if ( stream_.state != STREAM_STOPPED ) { if ( stream_.state == STREAM_RUNNING ) errorText_ = "RtApiPulse::startStream(): the stream is already running!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiPulse::startStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } PulseAudioHandle *pah = static_cast( stream_.apiHandle ); MUTEX_LOCK( &stream_.mutex ); /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ stream_.state = STREAM_RUNNING; pah->runnable = true; pthread_cond_signal( &pah->runnable_cv ); MUTEX_UNLOCK( &stream_.mutex ); return RTAUDIO_NO_ERROR; } RtAudioErrorType RtApiPulse::stopStream( void ) { if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_CLOSED ) errorText_ = "RtApiPulse::stopStream(): the stream is closed!"; return error( RTAUDIO_WARNING ); } PulseAudioHandle *pah = static_cast( stream_.apiHandle ); stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); if ( pah ) { pah->runnable = false; if ( pah->s_play ) { int pa_error; if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::stopStream: error draining output device, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); return error( RTAUDIO_SYSTEM_ERROR ); } } } stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); return RTAUDIO_NO_ERROR; } RtAudioErrorType RtApiPulse::abortStream( void ) { if ( stream_.state != STREAM_RUNNING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiPulse::abortStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } PulseAudioHandle *pah = static_cast( stream_.apiHandle ); stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); if ( pah ) { pah->runnable = false; if ( pah->s_play ) { int pa_error; if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::abortStream: error flushing output device, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); return error( RTAUDIO_SYSTEM_ERROR ); } } } stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); return RTAUDIO_NO_ERROR; } //******************** End of __LINUX_PULSE__ *********************// #endif #if defined(__LINUX_OSS__) #include #include #include #include #include #include static void *ossCallbackHandler(void * ptr); // A structure to hold various information related to the OSS API // implementation. struct OssHandle { int id[2]; // device ids bool xrun[2]; bool triggered; pthread_cond_t runnable; OssHandle() :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } }; RtApiOss :: RtApiOss() { // Nothing to do here. } RtApiOss :: ~RtApiOss() { if ( stream_.state != STREAM_CLOSED ) closeStream(); } void RtApiOss :: probeDevices( void ) { // See list of required functionality in RtApi::probeDevices(). int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); if ( mixerfd == -1 ) { errorText_ = "RtApiOss::probeDevices: error opening '/dev/mixer'."; error( RTAUDIO_SYSTEM_ERROR ); return; } oss_sysinfo sysinfo; if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) { close( mixerfd ); errorText_ = "RtApiOss::probeDevices: error getting sysinfo, OSS version >= 4.0 is required."; error( RTAUDIO_SYSTEM_ERROR ); return; } unsigned int nDevices = sysinfo.numaudios; if ( nDevices == 0 ) { close( mixerfd ); deviceList_.clear(); return; } oss_audioinfo ainfo; unsigned int m, n; std::vector deviceNames; for ( n=0; n::iterator it=deviceList_.begin(); it!=deviceList_.end(); ) { for ( m=0; m 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; } // Probe data formats ... do for input unsigned long mask = ainfo.iformats; if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE ) info.nativeFormats |= RTAUDIO_SINT16; if ( mask & AFMT_S8 ) info.nativeFormats |= RTAUDIO_SINT8; if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE ) info.nativeFormats |= RTAUDIO_SINT32; #ifdef AFMT_FLOAT if ( mask & AFMT_FLOAT ) info.nativeFormats |= RTAUDIO_FLOAT32; #endif if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE ) info.nativeFormats |= RTAUDIO_SINT24; // Check that we have at least one supported format if ( info.nativeFormats == 0 ) { errorStream_ << "RtApiOss::probeDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } // Probe the supported sample rates. info.sampleRates.clear(); if ( ainfo.nrates ) { for ( unsigned int i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[k]; break; } } } } else { // Check min and max rate values; for ( unsigned int k=0; k= (int) SAMPLE_RATES[k] ) { info.sampleRates.push_back( SAMPLE_RATES[k] ); if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[k]; } } } if ( info.sampleRates.size() == 0 ) { errorStream_ << "RtApiOss::probeDeviceInfo: no supported sample rates found for device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); error( RTAUDIO_WARNING ); return false; } return true; } bool RtApiOss :: probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); if ( mixerfd == -1 ) { errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'."; return FAILURE; } oss_sysinfo sysinfo; int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ); if ( result == -1 ) { close( mixerfd ); errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required."; return FAILURE; } unsigned int nDevices = sysinfo.numaudios; if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. close( mixerfd ); errorText_ = "RtApiOss::probeDeviceOpen: no devices found!"; return FAILURE; } std::string deviceName; unsigned int m, device; for ( m=0; mid[0] ); handle->id[0] = 0; if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) { errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode."; errorText_ = errorStream_.str(); return FAILURE; } // Check that the number previously set channels is the same. if ( stream_.nUserChannels[0] != channels ) { errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } flags |= O_RDWR; } else flags |= O_RDONLY; } // Set exclusive access if specified. if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL; // Try to open the device. int fd; fd = open( ainfo.devnode, flags, 0 ); if ( fd == -1 ) { if ( errno == EBUSY ) errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy."; else errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } // For duplex operation, specifically set this mode (this doesn't seem to work). /* if ( flags | O_RDWR ) { result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL ); if ( result == -1) { errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } } */ // Check the device channel support. stream_.nUserChannels[mode] = channels; if ( ainfo.max_channels < (int)(channels + firstChannel) ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters."; errorText_ = errorStream_.str(); return FAILURE; } // Set the number of channels. int deviceChannels = channels + firstChannel; result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels ); if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.nDeviceChannels[mode] = deviceChannels; // Get the data format mask int mask; result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask ); if ( result == -1 ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats."; errorText_ = errorStream_.str(); return FAILURE; } // Determine how to set the device format. stream_.userFormat = format; int deviceFormat = -1; stream_.doByteSwap[mode] = false; if ( format == RTAUDIO_SINT8 ) { if ( mask & AFMT_S8 ) { deviceFormat = AFMT_S8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } } else if ( format == RTAUDIO_SINT16 ) { if ( mask & AFMT_S16_NE ) { deviceFormat = AFMT_S16_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } else if ( mask & AFMT_S16_OE ) { deviceFormat = AFMT_S16_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.doByteSwap[mode] = true; } } else if ( format == RTAUDIO_SINT24 ) { if ( mask & AFMT_S24_NE ) { deviceFormat = AFMT_S24_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT24; } else if ( mask & AFMT_S24_OE ) { deviceFormat = AFMT_S24_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT24; stream_.doByteSwap[mode] = true; } } else if ( format == RTAUDIO_SINT32 ) { if ( mask & AFMT_S32_NE ) { deviceFormat = AFMT_S32_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; } else if ( mask & AFMT_S32_OE ) { deviceFormat = AFMT_S32_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; stream_.doByteSwap[mode] = true; } } if ( deviceFormat == -1 ) { // The user requested format is not natively supported by the device. if ( mask & AFMT_S16_NE ) { deviceFormat = AFMT_S16_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } else if ( mask & AFMT_S32_NE ) { deviceFormat = AFMT_S32_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; } else if ( mask & AFMT_S24_NE ) { deviceFormat = AFMT_S24_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT24; } else if ( mask & AFMT_S16_OE ) { deviceFormat = AFMT_S16_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.doByteSwap[mode] = true; } else if ( mask & AFMT_S32_OE ) { deviceFormat = AFMT_S32_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; stream_.doByteSwap[mode] = true; } else if ( mask & AFMT_S24_OE ) { deviceFormat = AFMT_S24_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT24; stream_.doByteSwap[mode] = true; } else if ( mask & AFMT_S8) { deviceFormat = AFMT_S8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } } if ( stream_.deviceFormat[mode] == 0 ) { // This really shouldn't happen ... close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); return FAILURE; } // Set the data format. int temp = deviceFormat; result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat ); if ( result == -1 || deviceFormat != temp ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Attempt to set the buffer size. According to OSS, the minimum // number of buffers is two. The supposed minimum buffer size is 16 // bytes, so that will be our lower bound. The argument to this // call is in the form 0xMMMMSSSS (hex), where the buffer size (in // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM. // We'll check the actual value used near the end of the setup // procedure. int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels; if ( ossBufferBytes < 16 ) ossBufferBytes = 16; int buffers = 0; if ( options ) buffers = options->numberOfBuffers; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2; if ( buffers < 2 ) buffers = 3; temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) ); result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp ); if ( result == -1 ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.nBuffers = buffers; // Save buffer size (in sample frames). *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels ); stream_.bufferSize = *bufferSize; // Set the sample rate. int srate = sampleRate; result = ioctl( fd, SNDCTL_DSP_SPEED, &srate ); if ( result == -1 ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Verify the sample rate setup worked. if ( abs( srate - (int)sampleRate ) > 100 ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.sampleRate = sampleRate; if ( mode == INPUT && stream_.mode == OUTPUT && stream_.deviceId[0] == device) { // We're doing duplex setup here. stream_.deviceFormat[0] = stream_.deviceFormat[1]; stream_.nDeviceChannels[0] = deviceChannels; } // Set interleaving parameters. stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate the stream handles if necessary and then save. if ( stream_.apiHandle == 0 ) { try { handle = new OssHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory."; goto error; } if ( pthread_cond_init( &handle->runnable, NULL ) ) { errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable."; goto error; } stream_.apiHandle = (void *) handle; } else { handle = (OssHandle *) stream_.apiHandle; } handle->id[mode] = fd; // Allocate necessary internal buffers. unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } stream_.deviceId[mode] = device; stream_.state = STREAM_STOPPED; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); // Setup thread if necessary. if ( stream_.mode == OUTPUT && mode == INPUT ) { // We had already set up an output stream. stream_.mode = DUPLEX; if ( stream_.deviceId[0] == device ) handle->id[0] = fd; } else { stream_.mode = mode; // Setup callback thread. stream_.callbackInfo.object = (void *) this; // Set the thread attributes for joinable and realtime scheduling // priority. The higher priority will only take affect if the // program is run as root or suid. pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); #if defined(SCHED_RR) && !defined(__OpenBSD__) // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { stream_.callbackInfo.doRealtime = true; struct sched_param param; int priority = options->priority; int min = sched_get_priority_min( SCHED_RR ); int max = sched_get_priority_max( SCHED_RR ); if ( priority < min ) priority = min; else if ( priority > max ) priority = max; param.sched_priority = priority; // Set the policy BEFORE the priority. Otherwise it fails. pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); // This is definitely required. Otherwise it fails. pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedparam(&attr, ¶m); } else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif stream_.callbackInfo.isRunning = true; result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo ); pthread_attr_destroy( &attr ); if ( result ) { // Failed. Try instead with default attributes. result = pthread_create( &stream_.callbackInfo.thread, NULL, ossCallbackHandler, &stream_.callbackInfo ); if ( result ) { stream_.callbackInfo.isRunning = false; errorText_ = "RtApiOss::error creating callback thread!"; goto error; } } } return SUCCESS; error: if ( handle ) { pthread_cond_destroy( &handle->runnable ); if ( handle->id[0] ) close( handle->id[0] ); if ( handle->id[1] ) close( handle->id[1] ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.state = STREAM_CLOSED; return FAILURE; } void RtApiOss :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiOss::closeStream(): no open stream to close!"; error( RTAUDIO_WARNING ); return; } OssHandle *handle = (OssHandle *) stream_.apiHandle; stream_.callbackInfo.isRunning = false; MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_STOPPED ) pthread_cond_signal( &handle->runnable ); MUTEX_UNLOCK( &stream_.mutex ); pthread_join( stream_.callbackInfo.thread, NULL ); if ( stream_.state == STREAM_RUNNING ) { if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); else ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); stream_.state = STREAM_STOPPED; } if ( handle ) { pthread_cond_destroy( &handle->runnable ); if ( handle->id[0] ) close( handle->id[0] ); if ( handle->id[1] ) close( handle->id[1] ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } clearStreamInfo(); //stream_.mode = UNINITIALIZED; //stream_.state = STREAM_CLOSED; } RtAudioErrorType RtApiOss :: startStream() { if ( stream_.state != STREAM_STOPPED ) { if ( stream_.state == STREAM_RUNNING ) errorText_ = "RtApiOss::startStream(): the stream is already running!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiOss::startStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } MUTEX_LOCK( &stream_.mutex ); /* #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif */ stream_.state = STREAM_RUNNING; // No need to do anything else here ... OSS automatically starts // when fed samples. MUTEX_UNLOCK( &stream_.mutex ); OssHandle *handle = (OssHandle *) stream_.apiHandle; pthread_cond_signal( &handle->runnable ); return RTAUDIO_NO_ERROR; } RtAudioErrorType RtApiOss :: stopStream() { if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiOss::stopStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_CLOSED ) errorText_ = "RtApiOss::stopStream(): the stream is closed!"; return error( RTAUDIO_WARNING ); } MUTEX_LOCK( &stream_.mutex ); // The state might change while waiting on a mutex. if ( stream_.state == STREAM_STOPPED ) { MUTEX_UNLOCK( &stream_.mutex ); return RTAUDIO_NO_ERROR; } int result = 0; OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { // Flush the output with zeros a few times. char *buffer; int samples; RtAudioFormat format; if ( stream_.doConvertBuffer[0] ) { buffer = stream_.deviceBuffer; samples = stream_.bufferSize * stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { buffer = stream_.userBuffer[0]; samples = stream_.bufferSize * stream_.nUserChannels[0]; format = stream_.userFormat; } memset( buffer, 0, samples * formatBytes(format) ); for ( unsigned int i=0; iid[0], buffer, samples * formatBytes(format) ); if ( result == -1 ) { errorText_ = "RtApiOss::stopStream: audio write error."; error( RTAUDIO_WARNING ); } } result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); if ( result == -1 ) { errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.deviceId[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } handle->triggered = false; } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) { result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); if ( result == -1 ) { errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.deviceId[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } } unlock: stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); if ( result != -1 ) return RTAUDIO_NO_ERROR; return error( RTAUDIO_SYSTEM_ERROR ); } RtAudioErrorType RtApiOss :: abortStream() { if ( stream_.state != STREAM_RUNNING ) { if ( stream_.state == STREAM_STOPPED ) errorText_ = "RtApiOss::abortStream(): the stream is already stopped!"; else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED ) errorText_ = "RtApiOss::abortStream(): the stream is stopping or closed!"; return error( RTAUDIO_WARNING ); } MUTEX_LOCK( &stream_.mutex ); // The state might change while waiting on a mutex. if ( stream_.state == STREAM_STOPPED ) { MUTEX_UNLOCK( &stream_.mutex ); return RTAUDIO_NO_ERROR; } int result = 0; OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); if ( result == -1 ) { errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.deviceId[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } handle->triggered = false; } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) { result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); if ( result == -1 ) { errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.deviceId[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } } unlock: stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); if ( result != -1 ) return RTAUDIO_SYSTEM_ERROR; return error( RTAUDIO_SYSTEM_ERROR ); } void RtApiOss :: callbackEvent() { OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.state == STREAM_STOPPED ) { MUTEX_LOCK( &stream_.mutex ); pthread_cond_wait( &handle->runnable, &stream_.mutex ); if ( stream_.state != STREAM_RUNNING ) { MUTEX_UNLOCK( &stream_.mutex ); return; } MUTEX_UNLOCK( &stream_.mutex ); } if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RTAUDIO_WARNING ); return; } // Invoke user callback to get fresh output data. int doStopStream = 0; RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); if ( doStopStream == 2 ) { this->abortStream(); return; } MUTEX_LOCK( &stream_.mutex ); // The state might change while waiting on a mutex. if ( stream_.state == STREAM_STOPPED ) goto unlock; int result; char *buffer; int samples; RtAudioFormat format; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { // Setup parameters and do buffer conversion if necessary. if ( stream_.doConvertBuffer[0] ) { buffer = stream_.deviceBuffer; convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); samples = stream_.bufferSize * stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { buffer = stream_.userBuffer[0]; samples = stream_.bufferSize * stream_.nUserChannels[0]; format = stream_.userFormat; } // Do byte swapping if necessary. if ( stream_.doByteSwap[0] ) byteSwapBuffer( buffer, samples, format ); if ( stream_.mode == DUPLEX && handle->triggered == false ) { int trig = 0; ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig ); result = write( handle->id[0], buffer, samples * formatBytes(format) ); trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT; ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig ); handle->triggered = true; } else // Write samples to device. result = write( handle->id[0], buffer, samples * formatBytes(format) ); if ( result == -1 ) { // We'll assume this is an underrun, though there isn't a // specific means for determining that. handle->xrun[0] = true; errorText_ = "RtApiOss::callbackEvent: audio write error."; error( RTAUDIO_WARNING ); // Continue on to input section. } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { // Setup parameters. if ( stream_.doConvertBuffer[1] ) { buffer = stream_.deviceBuffer; samples = stream_.bufferSize * stream_.nDeviceChannels[1]; format = stream_.deviceFormat[1]; } else { buffer = stream_.userBuffer[1]; samples = stream_.bufferSize * stream_.nUserChannels[1]; format = stream_.userFormat; } // Read samples from device. result = read( handle->id[1], buffer, samples * formatBytes(format) ); if ( result == -1 ) { // We'll assume this is an overrun, though there isn't a // specific means for determining that. handle->xrun[1] = true; errorText_ = "RtApiOss::callbackEvent: audio read error."; error( RTAUDIO_WARNING ); goto unlock; } // Do byte swapping if necessary. if ( stream_.doByteSwap[1] ) byteSwapBuffer( buffer, samples, format ); // Do buffer conversion if necessary. if ( stream_.doConvertBuffer[1] ) convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); } unlock: MUTEX_UNLOCK( &stream_.mutex ); RtApi::tickStreamTime(); if ( doStopStream == 1 ) this->stopStream(); } static void *ossCallbackHandler( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiOss *object = (RtApiOss *) info->object; bool *isRunning = &info->isRunning; #if defined(SCHED_RR) && !defined(__OpenBSD__) // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) if (info->doRealtime) { std::cerr << "RtAudio oss: " << (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << "running realtime scheduling" << std::endl; } #endif while ( *isRunning == true ) { pthread_testcancel(); object->callbackEvent(); } pthread_exit( NULL ); } //******************** End of __LINUX_OSS__ *********************// #endif // *************************************************** // // // Protected common (OS-independent) RtAudio methods. // // *************************************************** // // This method can be modified to control the behavior of error // message printing. RtAudioErrorType RtApi :: error( RtAudioErrorType type ) { errorStream_.str(""); // clear the ostringstream to avoid repeated messages // Don't output warnings if showWarnings_ is false if ( type == RTAUDIO_WARNING && showWarnings_ == false ) return type; if ( errorCallback_ ) { //const std::string errorMessage = errorText_; //errorCallback_( type, errorMessage ); errorCallback_( type, errorText_ ); } else std::cerr << '\n' << errorText_ << "\n\n"; return type; } /* void RtApi :: verifyStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApi:: a stream is not open!"; error( RtAudioError::INVALID_USE ); } } */ void RtApi :: clearStreamInfo() { stream_.mode = UNINITIALIZED; stream_.state = STREAM_CLOSED; stream_.sampleRate = 0; stream_.bufferSize = 0; stream_.nBuffers = 0; stream_.userFormat = 0; stream_.userInterleaved = true; stream_.streamTime = 0.0; stream_.apiHandle = 0; stream_.deviceBuffer = 0; stream_.callbackInfo.callback = 0; stream_.callbackInfo.userData = 0; stream_.callbackInfo.isRunning = false; stream_.callbackInfo.deviceDisconnected = false; for ( int i=0; i<2; i++ ) { stream_.deviceId[i] = 11111; stream_.doConvertBuffer[i] = false; stream_.deviceInterleaved[i] = true; stream_.doByteSwap[i] = false; stream_.nUserChannels[i] = 0; stream_.nDeviceChannels[i] = 0; stream_.channelOffset[i] = 0; stream_.deviceFormat[i] = 0; stream_.latency[i] = 0; stream_.userBuffer[i] = 0; stream_.convertInfo[i].channels = 0; stream_.convertInfo[i].inJump = 0; stream_.convertInfo[i].outJump = 0; stream_.convertInfo[i].inFormat = 0; stream_.convertInfo[i].outFormat = 0; stream_.convertInfo[i].inOffset.clear(); stream_.convertInfo[i].outOffset.clear(); } } unsigned int RtApi :: formatBytes( RtAudioFormat format ) { if ( format == RTAUDIO_SINT16 ) return 2; else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 ) return 4; else if ( format == RTAUDIO_FLOAT64 ) return 8; else if ( format == RTAUDIO_SINT24 ) return 3; else if ( format == RTAUDIO_SINT8 ) return 1; errorText_ = "RtApi::formatBytes: undefined format."; error( RTAUDIO_WARNING ); return 0; } void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel ) { if ( mode == INPUT ) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) { if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) || ( mode == INPUT && stream_.userInterleaved ) ) { for ( int k=0; k 0 ) { if ( stream_.deviceInterleaved[mode] ) { if ( mode == OUTPUT ) { for ( int k=0; k info.inJump ) memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) ); int j; if (info.outFormat == RTAUDIO_FLOAT64) { Float64 *out = (Float64 *)outBuffer; if (info.inFormat == RTAUDIO_SINT8) { signed char *in = (signed char *)inBuffer; for (unsigned int i=0; i> 8); //out[info.outOffset[j]] >>= 8; } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_FLOAT32) { Float32 *in = (Float32 *)inBuffer; for (unsigned int i=0; i> 8); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT32) { Int32 *in = (Int32 *)inBuffer; for (unsigned int i=0; i> 16) & 0x0000ffff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_FLOAT32) { Float32 *in = (Float32 *)inBuffer; for (unsigned int i=0; i> 8) & 0x00ff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT24) { Int24 *in = (Int24 *)inBuffer; for (unsigned int i=0; i> 16); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT32) { Int32 *in = (Int32 *)inBuffer; for (unsigned int i=0; i> 24) & 0x000000ff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_FLOAT32) { Float32 *in = (Float32 *)inBuffer; for (unsigned int i=0; i>8) | (x<<8); } //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); } //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); } void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ) { char val; char *ptr; ptr = buffer; if ( format == RTAUDIO_SINT16 ) { for ( unsigned int i=0; i 0 #define RTAUDIO_VERSION RTAUDIO_TOSTRING(RTAUDIO_VERSION_MAJOR) \ "." RTAUDIO_TOSTRING(RTAUDIO_VERSION_MINOR) \ "." RTAUDIO_TOSTRING(RTAUDIO_VERSION_PATCH) \ "beta" RTAUDIO_TOSTRING(RTAUDIO_VERSION_BETA) #else #define RTAUDIO_VERSION RTAUDIO_TOSTRING(RTAUDIO_VERSION_MAJOR) \ "." RTAUDIO_TOSTRING(RTAUDIO_VERSION_MINOR) \ "." RTAUDIO_TOSTRING(RTAUDIO_VERSION_PATCH) #endif #if defined _WIN32 || defined __CYGWIN__ #if defined(RTAUDIO_EXPORT) #define RTAUDIO_DLL_PUBLIC __declspec(dllexport) #else #define RTAUDIO_DLL_PUBLIC #endif #else #if __GNUC__ >= 4 #define RTAUDIO_DLL_PUBLIC __attribute__( (visibility( "default" )) ) #else #define RTAUDIO_DLL_PUBLIC #endif #endif #include #include #include #include /*! \typedef typedef unsigned long RtAudioFormat; \brief RtAudio data format type. Support for signed integers and floats. Audio data fed to/from an RtAudio stream is assumed to ALWAYS be in host byte order. The internal routines will automatically take care of any necessary byte-swapping between the host format and the soundcard. Thus, endian-ness is not a concern in the following format definitions. Note that there are no range checks for floating-point values that extend beyond plus/minus 1.0. - \e RTAUDIO_SINT8: 8-bit signed integer. - \e RTAUDIO_SINT16: 16-bit signed integer. - \e RTAUDIO_SINT24: 24-bit signed integer. - \e RTAUDIO_SINT32: 32-bit signed integer. - \e RTAUDIO_FLOAT32: Normalized between plus/minus 1.0. - \e RTAUDIO_FLOAT64: Normalized between plus/minus 1.0. */ typedef unsigned long RtAudioFormat; static const RtAudioFormat RTAUDIO_SINT8 = 0x1; // 8-bit signed integer. static const RtAudioFormat RTAUDIO_SINT16 = 0x2; // 16-bit signed integer. static const RtAudioFormat RTAUDIO_SINT24 = 0x4; // 24-bit signed integer. static const RtAudioFormat RTAUDIO_SINT32 = 0x8; // 32-bit signed integer. static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0. static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0. /*! \typedef typedef unsigned long RtAudioStreamFlags; \brief RtAudio stream option flags. The following flags can be OR'ed together to allow a client to make changes to the default stream behavior: - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). - \e RTAUDIO_JACK_DONT_CONNECT: Do not automatically connect ports (JACK only). By default, RtAudio streams pass and receive audio data from the client in an interleaved format. By passing the RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio data will instead be presented in non-interleaved buffers. In this case, each buffer argument in the RtAudioCallback function will point to a single array of data, with \c nFrames samples for each channel concatenated back-to-back. For example, the first sample of data for the second channel would be located at index \c nFrames (assuming the \c buffer pointer was recast to the correct data type for the stream). Certain audio APIs offer a number of parameters that influence the I/O latency of a stream. By default, RtAudio will attempt to set these parameters internally for robust (glitch-free) performance (though some APIs, like Windows DirectSound, make this difficult). By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() function, internal stream settings will be influenced in an attempt to minimize stream latency, though possibly at the expense of stream performance. If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to open the input and/or output stream device(s) for exclusive use. Note that this is not possible with all supported audio APIs. If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt to select realtime scheduling (round-robin) for the callback thread. If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to open the "default" PCM device when using the ALSA API. Note that this will override any specified input or output device id. If the RTAUDIO_JACK_DONT_CONNECT flag is set, RtAudio will not attempt to automatically connect the ports of the client to the audio device. */ typedef unsigned int RtAudioStreamFlags; static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1; // Use non-interleaved buffers (default = interleaved). static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2; // Attempt to set stream parameters for lowest possible latency. static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4; // Attempt grab device and prevent use by others. static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread. static const RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT = 0x10; // Use the "default" PCM device (ALSA only). static const RtAudioStreamFlags RTAUDIO_JACK_DONT_CONNECT = 0x20; // Do not automatically connect ports (JACK only). /*! \typedef typedef unsigned long RtAudioStreamStatus; \brief RtAudio stream status (over- or underflow) flags. Notification of a stream over- or underflow is indicated by a non-zero stream \c status argument in the RtAudioCallback function. The stream status can be one of the following two options, depending on whether the stream is open for output and/or input: - \e RTAUDIO_INPUT_OVERFLOW: Input data was discarded because of an overflow condition at the driver. - \e RTAUDIO_OUTPUT_UNDERFLOW: The output buffer ran low, likely producing a break in the output sound. */ typedef unsigned int RtAudioStreamStatus; static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1; // Input data was discarded because of an overflow condition at the driver. static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2; // The output buffer ran low, likely causing a gap in the output sound. //! RtAudio callback function prototype. /*! All RtAudio clients must create a function of type RtAudioCallback to read and/or write data from/to the audio stream. When the underlying audio system is ready for new input or output data, this function will be invoked. \param outputBuffer For output (or duplex) streams, the client should write \c nFrames of audio sample frames into this buffer. This argument should be recast to the datatype specified when the stream was opened. For input-only streams, this argument will be NULL. \param inputBuffer For input (or duplex) streams, this buffer will hold \c nFrames of input audio sample frames. This argument should be recast to the datatype specified when the stream was opened. For output-only streams, this argument will be NULL. \param nFrames The number of sample frames of input or output data in the buffers. The actual buffer size in bytes is dependent on the data type and number of channels in use. \param streamTime The number of seconds that have elapsed since the stream was started. \param status If non-zero, this argument indicates a data overflow or underflow condition for the stream. The particular condition can be determined by comparison with the RtAudioStreamStatus flags. \param userData A pointer to optional data provided by the client when opening the stream (default = NULL). \return To continue normal stream operation, the RtAudioCallback function should return a value of zero. To stop the stream and drain the output buffer, the function should return a value of one. To abort the stream immediately, the client should return a value of two. */ typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData ); enum RtAudioErrorType { RTAUDIO_NO_ERROR = 0, /*!< No error. */ RTAUDIO_WARNING, /*!< A non-critical error. */ RTAUDIO_UNKNOWN_ERROR, /*!< An unspecified error type. */ RTAUDIO_NO_DEVICES_FOUND, /*!< No devices found on system. */ RTAUDIO_INVALID_DEVICE, /*!< An invalid device ID was specified. */ RTAUDIO_DEVICE_DISCONNECT, /*!< A device in use was disconnected. */ RTAUDIO_MEMORY_ERROR, /*!< An error occurred during memory allocation. */ RTAUDIO_INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ RTAUDIO_INVALID_USE, /*!< The function was called incorrectly. */ RTAUDIO_DRIVER_ERROR, /*!< A system driver error occurred. */ RTAUDIO_SYSTEM_ERROR, /*!< A system error occurred. */ RTAUDIO_THREAD_ERROR /*!< A thread error occurred. */ }; //! RtAudio error callback function prototype. /*! \param type Type of error. \param errorText Error description. */ typedef std::function RtAudioErrorCallback; // **************************************************************** // // // RtAudio class declaration. // // RtAudio is a "controller" used to select an available audio i/o // interface. It presents a common API for the user to call but all // functionality is implemented by the class RtApi and its // subclasses. RtAudio creates an instance of an RtApi subclass // based on the user's API choice. If no choice is made, RtAudio // attempts to make a "logical" API selection. // // **************************************************************** // class RtApi; class RTAUDIO_DLL_PUBLIC RtAudio { public: //! Audio API specifier arguments. enum Api { UNSPECIFIED, /*!< Search for a working compiled API. */ MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */ LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */ LINUX_PULSE, /*!< The Linux PulseAudio API. */ LINUX_OSS, /*!< The Linux Open Sound System API. */ WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */ WINDOWS_WASAPI, /*!< The Microsoft WASAPI API. */ WINDOWS_DS, /*!< The Microsoft DirectSound API. */ RTAUDIO_DUMMY, /*!< A compilable but non-functional API. */ NUM_APIS /*!< Number of values in this enum. */ }; //! The public device information structure for returning queried values. struct DeviceInfo { unsigned int ID{}; /*!< Device ID used to specify a device to RtAudio. */ std::string name; /*!< Character string device name. */ unsigned int outputChannels{}; /*!< Maximum output channels supported by device. */ unsigned int inputChannels{}; /*!< Maximum input channels supported by device. */ unsigned int duplexChannels{}; /*!< Maximum simultaneous input/output channels supported by device. */ bool isDefaultOutput{false}; /*!< true if this is the default output device. */ bool isDefaultInput{false}; /*!< true if this is the default input device. */ std::vector sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ unsigned int currentSampleRate{}; /*!< Current sample rate, system sample rate as currently configured. */ unsigned int preferredSampleRate{}; /*!< Preferred sample rate, e.g. for WASAPI the system sample rate. */ RtAudioFormat nativeFormats{}; /*!< Bit mask of supported data formats. */ }; //! The structure for specifying input or output stream parameters. struct StreamParameters { //std::string deviceName{}; /*!< Device name from device list. */ unsigned int deviceId{}; /*!< Device id as provided by getDeviceIds(). */ unsigned int nChannels{}; /*!< Number of channels. */ unsigned int firstChannel{}; /*!< First channel index on device (default = 0). */ }; //! The structure for specifying stream options. /*! The following flags can be OR'ed together to allow a client to make changes to the default stream behavior: - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. - \e RTAUDIO_SCHEDULE_REALTIME: Attempt to select realtime scheduling for callback thread. - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). By default, RtAudio streams pass and receive audio data from the client in an interleaved format. By passing the RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio data will instead be presented in non-interleaved buffers. In this case, each buffer argument in the RtAudioCallback function will point to a single array of data, with \c nFrames samples for each channel concatenated back-to-back. For example, the first sample of data for the second channel would be located at index \c nFrames (assuming the \c buffer pointer was recast to the correct data type for the stream). Certain audio APIs offer a number of parameters that influence the I/O latency of a stream. By default, RtAudio will attempt to set these parameters internally for robust (glitch-free) performance (though some APIs, like Windows DirectSound, make this difficult). By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() function, internal stream settings will be influenced in an attempt to minimize stream latency, though possibly at the expense of stream performance. If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to open the input and/or output stream device(s) for exclusive use. Note that this is not possible with all supported audio APIs. If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt to select realtime scheduling (round-robin) for the callback thread. The \c priority parameter will only be used if the RTAUDIO_SCHEDULE_REALTIME flag is set. It defines the thread's realtime priority. If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to open the "default" PCM device when using the ALSA API. Note that this will override any specified input or output device id. The \c numberOfBuffers parameter can be used to control stream latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs only. A value of two is usually the smallest allowed. Larger numbers can potentially result in more robust stream performance, though likely at the cost of stream latency. The value set by the user is replaced during execution of the RtAudio::openStream() function by the value actually used by the system. The \c streamName parameter can be used to set the client name when using the Jack API or the application name when using the Pulse API. By default, the Jack client name is set to RtApiJack. However, if you wish to create multiple instances of RtAudio with Jack, each instance must have a unique client name. The default Pulse application name is set to "RtAudio." */ struct StreamOptions { RtAudioStreamFlags flags{}; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */ unsigned int numberOfBuffers{}; /*!< Number of stream buffers. */ std::string streamName; /*!< A stream name (currently used only in Jack). */ int priority{}; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */ }; //! A static function to determine the current RtAudio version. static std::string getVersion( void ); //! A static function to determine the available compiled audio APIs. /*! The values returned in the std::vector can be compared against the enumerated list values. Note that there can be more than one API compiled for certain operating systems. */ static void getCompiledApi( std::vector &apis ); //! Return the name of a specified compiled audio API. /*! This obtains a short lower-case name used for identification purposes. This value is guaranteed to remain identical across library versions. If the API is unknown, this function will return the empty string. */ static std::string getApiName( RtAudio::Api api ); //! Return the display name of a specified compiled audio API. /*! This obtains a long name used for display purposes. If the API is unknown, this function will return the empty string. */ static std::string getApiDisplayName( RtAudio::Api api ); //! Return the compiled audio API having the given name. /*! A case insensitive comparison will check the specified name against the list of compiled APIs, and return the one that matches. On failure, the function returns UNSPECIFIED. */ static RtAudio::Api getCompiledApiByName( const std::string &name ); //! Return the compiled audio API having the given display name. /*! A case sensitive comparison will check the specified display name against the list of compiled APIs, and return the one that matches. On failure, the function returns UNSPECIFIED. */ static RtAudio::Api getCompiledApiByDisplayName( const std::string &name ); //! The class constructor. /*! The constructor attempts to create an RtApi instance. If an API argument is specified but that API has not been compiled, a warning is issued and an instance of an available API is created. If no compiled API is found, the routine will abort (though this should be impossible because RtDummy is the default if no API-specific preprocessor definition is provided to the compiler). If no API argument is specified and multiple API support has been compiled, the default order of use is JACK, ALSA, OSS (Linux systems) and ASIO, DS (Windows systems). An optional errorCallback function can be specified to subsequently receive warning and error messages. */ RtAudio( RtAudio::Api api=UNSPECIFIED, RtAudioErrorCallback&& errorCallback=0 ); //! The destructor. /*! If a stream is running or open, it will be stopped and closed automatically. */ ~RtAudio(); //! Returns the audio API specifier for the current instance of RtAudio. RtAudio::Api getCurrentApi( void ); //! A public function that queries for the number of audio devices available. /*! This function performs a system query of available devices each time it is called, thus supporting devices (dis)connected \e after instantiation. If a system error occurs during processing, a warning will be issued. */ unsigned int getDeviceCount( void ); //! A public function that returns a vector of audio device IDs. /*! The ID values returned by this function are used internally by RtAudio to identify a given device. The values themselves are arbitrary and do not correspond to device IDs used by the underlying API (nor are they index values). This function performs a system query of available devices each time it is called, thus supporting devices (dis)connected \e after instantiation. If no devices are available, the vector size will be zero. If a system error occurs during processing, a warning will be issued. */ std::vector getDeviceIds( void ); //! A public function that returns a vector of audio device names. /*! This function performs a system query of available devices each time it is called, thus supporting devices (dis)connected \e after instantiation. If no devices are available, the vector size will be zero. If a system error occurs during processing, a warning will be issued. */ std::vector getDeviceNames( void ); //! Return an RtAudio::DeviceInfo structure for a specified device ID. /*! Any device ID returned by getDeviceIds() is valid, unless it has been removed between the call to getDevceIds() and this function. If an invalid argument is provided, an RTAUDIO_INVALID_USE will be passed to the user-provided errorCallback function (or otherwise printed to stderr) and all members of the returned RtAudio::DeviceInfo structure will be initialized to default, invalid values (ID = 0, empty name, ...). If the specified device is the current default input or output device, the corresponding "isDefault" member will have a value of "true". */ RtAudio::DeviceInfo getDeviceInfo( unsigned int deviceId ); //! A function that returns the ID of the default output device. /*! If the underlying audio API does not provide a "default device", the first probed output device ID will be returned. If no devices are available, the return value will be 0 (which is an invalid device identifier). */ unsigned int getDefaultOutputDevice( void ); //! A function that returns the ID of the default input device. /*! If the underlying audio API does not provide a "default device", the first probed input device ID will be returned. If no devices are available, the return value will be 0 (which is an invalid device identifier). */ unsigned int getDefaultInputDevice( void ); //! A public function for opening a stream with the specified parameters. /*! An RTAUDIO_SYSTEM_ERROR is returned if a stream cannot be opened with the specified parameters or an error occurs during processing. An RTAUDIO_INVALID_USE is returned if a stream is already open or any invalid stream parameters are specified. \param outputParameters Specifies output stream parameters to use when opening a stream, including a device ID, number of channels, and starting channel number. For input-only streams, this argument should be NULL. The device ID is a value returned by getDeviceIds(). \param inputParameters Specifies input stream parameters to use when opening a stream, including a device ID, number of channels, and starting channel number. For output-only streams, this argument should be NULL. The device ID is a value returned by getDeviceIds(). \param format An RtAudioFormat specifying the desired sample data format. \param sampleRate The desired sample rate (sample frames per second). \param bufferFrames A pointer to a value indicating the desired internal buffer size in sample frames. The actual value used by the device is returned via the same pointer. A value of zero can be specified, in which case the lowest allowable value is determined. \param callback A client-defined function that will be invoked when input data is available and/or output data is needed. \param userData An optional pointer to data that can be accessed from within the callback function. \param options An optional pointer to a structure containing various global stream options, including a list of OR'ed RtAudioStreamFlags and a suggested number of stream buffers that can be used to control stream latency. More buffers typically result in more robust performance, though at a cost of greater latency. If a value of zero is specified, a system-specific median value is chosen. If the RTAUDIO_MINIMIZE_LATENCY flag bit is set, the lowest allowable value is used. The actual value used is returned via the structure argument. The parameter is API dependent. */ RtAudioErrorType openStream( RtAudio::StreamParameters *outputParameters, RtAudio::StreamParameters *inputParameters, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData = NULL, RtAudio::StreamOptions *options = NULL ); //! A function that closes a stream and frees any associated stream memory. /*! If a stream is not open, an RTAUDIO_WARNING will be passed to the user-provided errorCallback function (or otherwise printed to stderr). */ void closeStream( void ); //! A function that starts a stream. /*! An RTAUDIO_SYSTEM_ERROR is returned if an error occurs during processing. An RTAUDIO_WARNING is returned if a stream is not open or is already running. */ RtAudioErrorType startStream( void ); //! Stop a stream, allowing any samples remaining in the output queue to be played. /*! An RTAUDIO_SYSTEM_ERROR is returned if an error occurs during processing. An RTAUDIO_WARNING is returned if a stream is not open or is already stopped. */ RtAudioErrorType stopStream( void ); //! Stop a stream, discarding any samples remaining in the input/output queue. /*! An RTAUDIO_SYSTEM_ERROR is returned if an error occurs during processing. An RTAUDIO_WARNING is returned if a stream is not open or is already stopped. */ RtAudioErrorType abortStream( void ); //! Retrieve the error message corresponding to the last error or warning condition. /*! This function can be used to get a detailed error message when a non-zero RtAudioErrorType is returned by a function. This is the same message sent to the user-provided errorCallback function. */ const std::string getErrorText( void ); //! Returns true if a stream is open and false if not. bool isStreamOpen( void ) const; //! Returns true if the stream is running and false if it is stopped or not open. bool isStreamRunning( void ) const; //! Returns the number of seconds of processed data since the stream was started. /*! The stream time is calculated from the number of sample frames processed by the underlying audio system, which will increment by units of the audio buffer size. It is not an absolute running time. If a stream is not open, the returned value may not be valid. */ double getStreamTime( void ); //! Set the stream time to a time in seconds greater than or equal to 0.0. void setStreamTime( double time ); //! Returns the internal stream latency in sample frames. /*! The stream latency refers to delay in audio input and/or output caused by internal buffering by the audio system and/or hardware. For duplex streams, the returned value will represent the sum of the input and output latencies. If a stream is not open, the returned value will be invalid. If the API does not report latency, the return value will be zero. */ long getStreamLatency( void ); //! Returns actual sample rate in use by the (open) stream. /*! On some systems, the sample rate used may be slightly different than that specified in the stream parameters. If a stream is not open, a value of zero is returned. */ unsigned int getStreamSampleRate( void ); //! Set a client-defined function that will be invoked when an error or warning occurs. void setErrorCallback( RtAudioErrorCallback errorCallback ); //! Specify whether warning messages should be output or not. /*! The default behaviour is for warning messages to be output, either to a client-defined error callback function (if specified) or to stderr. */ void showWarnings( bool value = true ); protected: void openRtApi( RtAudio::Api api ); RtApi *rtapi_; }; // Operating system dependent thread functionality. #if defined(_MSC_VER) #ifndef NOMINMAX #define NOMINMAX #endif #include #include #include typedef uintptr_t ThreadHandle; typedef CRITICAL_SECTION StreamMutex; #else // Using pthread library for various flavors of unix. #include typedef pthread_t ThreadHandle; typedef pthread_mutex_t StreamMutex; #endif // Setup for "dummy" behavior if no apis specified. #if !(defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) \ || defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) \ || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)) #define __RTAUDIO_DUMMY__ #endif // This global structure type is used to pass callback information // between the private RtAudio stream structure and global callback // handling functions. struct CallbackInfo { void *object{}; // Used as a "this" pointer. ThreadHandle thread{}; void *callback{}; void *userData{}; void *apiInfo{}; // void pointer for API specific callback information bool isRunning{false}; bool doRealtime{false}; int priority{}; bool deviceDisconnected{false}; }; // **************************************************************** // // // RtApi class declaration. // // Subclasses of RtApi contain all API- and OS-specific code necessary // to fully implement the RtAudio API. // // Note that RtApi is an abstract base class and cannot be // explicitly instantiated. The class RtAudio will create an // instance of an RtApi subclass (RtApiOss, RtApiAlsa, // RtApiJack, RtApiCore, RtApiDs, or RtApiAsio). // // **************************************************************** // #pragma pack(push, 1) class S24 { protected: unsigned char c3[3]; public: S24() {} S24& operator = ( const int& i ) { c3[0] = (unsigned char)(i & 0x000000ff); c3[1] = (unsigned char)((i & 0x0000ff00) >> 8); c3[2] = (unsigned char)((i & 0x00ff0000) >> 16); return *this; } S24( const double& d ) { *this = (int) d; } S24( const float& f ) { *this = (int) f; } S24( const signed short& s ) { *this = (int) s; } S24( const char& c ) { *this = (int) c; } int asInt() { int i = c3[0] | (c3[1] << 8) | (c3[2] << 16); if (i & 0x800000) i |= ~0xffffff; return i; } }; #pragma pack(pop) #if defined( HAVE_GETTIMEOFDAY ) #include #endif #include class RTAUDIO_DLL_PUBLIC RtApi { public: RtApi(); virtual ~RtApi(); virtual RtAudio::Api getCurrentApi( void ) = 0; unsigned int getDeviceCount( void ); std::vector getDeviceIds( void ); std::vector getDeviceNames( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int deviceId ); virtual unsigned int getDefaultInputDevice( void ); virtual unsigned int getDefaultOutputDevice( void ); RtAudioErrorType openStream( RtAudio::StreamParameters *outputParameters, RtAudio::StreamParameters *inputParameters, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData, RtAudio::StreamOptions *options ); virtual void closeStream( void ); virtual RtAudioErrorType startStream( void ) = 0; virtual RtAudioErrorType stopStream( void ) = 0; virtual RtAudioErrorType abortStream( void ) = 0; const std::string getErrorText( void ) const { return errorText_; } long getStreamLatency( void ); unsigned int getStreamSampleRate( void ); virtual double getStreamTime( void ) const { return stream_.streamTime; } virtual void setStreamTime( double time ); bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; } bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; } void setErrorCallback( RtAudioErrorCallback errorCallback ) { errorCallback_ = errorCallback; } void showWarnings( bool value ) { showWarnings_ = value; } protected: static const unsigned int MAX_SAMPLE_RATES; static const unsigned int SAMPLE_RATES[]; enum { FAILURE, SUCCESS }; enum StreamState { STREAM_STOPPED, STREAM_STOPPING, STREAM_RUNNING, STREAM_CLOSED = -50 }; enum StreamMode { OUTPUT, INPUT, DUPLEX, UNINITIALIZED = -75 }; // A protected structure used for buffer conversion. struct ConvertInfo { int channels; int inJump, outJump; RtAudioFormat inFormat, outFormat; std::vector inOffset; std::vector outOffset; }; // A protected structure for audio streams. struct RtApiStream { unsigned int deviceId[2]; // Playback and record, respectively. void *apiHandle; // void pointer for API specific stream handle information StreamMode mode; // OUTPUT, INPUT, or DUPLEX. StreamState state; // STOPPED, RUNNING, or CLOSED char *userBuffer[2]; // Playback and record, respectively. char *deviceBuffer; bool doConvertBuffer[2]; // Playback and record, respectively. bool userInterleaved; bool deviceInterleaved[2]; // Playback and record, respectively. bool doByteSwap[2]; // Playback and record, respectively. unsigned int sampleRate; unsigned int bufferSize; unsigned int nBuffers; unsigned int nUserChannels[2]; // Playback and record, respectively. unsigned int nDeviceChannels[2]; // Playback and record channels, respectively. unsigned int channelOffset[2]; // Playback and record, respectively. unsigned long latency[2]; // Playback and record, respectively. RtAudioFormat userFormat; RtAudioFormat deviceFormat[2]; // Playback and record, respectively. StreamMutex mutex; CallbackInfo callbackInfo; ConvertInfo convertInfo[2]; double streamTime; // Number of elapsed seconds since the stream started. #if defined(HAVE_GETTIMEOFDAY) struct timeval lastTickTimestamp; #endif RtApiStream() :apiHandle(0), deviceBuffer(0) {} // { device[0] = std::string(); device[1] = std::string(); } }; typedef S24 Int24; typedef signed short Int16; typedef signed int Int32; typedef float Float32; typedef double Float64; std::ostringstream errorStream_; std::string errorText_; RtAudioErrorCallback errorCallback_; bool showWarnings_; std::vector deviceList_; unsigned int currentDeviceId_; RtApiStream stream_; /*! Protected, api-specific method that attempts to probe all device capabilities in a system. The function will not re-probe devices that were previously found and probed. This function MUST be implemented by all subclasses. If an error is encountered during the probe, a "warning" message may be reported and the internal list of devices may be incomplete. */ virtual void probeDevices( void ); /*! Protected, api-specific method that attempts to open a device with the given parameters. This function MUST be implemented by all subclasses. If an error is encountered during the probe, a "warning" message is reported and FAILURE is returned. A successful probe is indicated by a return value of SUCCESS. */ virtual bool probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); //! A protected function used to increment the stream time. void tickStreamTime( void ); //! Protected common method to clear an RtApiStream structure. void clearStreamInfo(); //! Protected common error method to allow global control over error handling. RtAudioErrorType error( RtAudioErrorType type ); /*! Protected method used to perform format, channel number, and/or interleaving conversions between the user and device buffers. */ void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info ); //! Protected common method used to perform byte-swapping on buffers. void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ); //! Protected common method that returns the number of bytes for a given format. unsigned int formatBytes( RtAudioFormat format ); //! Protected common method that sets up the parameters for buffer conversion. void setConvertInfo( StreamMode mode, unsigned int firstChannel ); }; // **************************************************************** // // // Inline RtAudio definitions. // // **************************************************************** // inline RtAudio::Api RtAudio :: getCurrentApi( void ) { return rtapi_->getCurrentApi(); } inline unsigned int RtAudio :: getDeviceCount( void ) { return rtapi_->getDeviceCount(); } inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int deviceId ) { return rtapi_->getDeviceInfo( deviceId ); } inline std::vector RtAudio :: getDeviceIds( void ) { return rtapi_->getDeviceIds(); } inline std::vector RtAudio :: getDeviceNames( void ) { return rtapi_->getDeviceNames(); } inline unsigned int RtAudio :: getDefaultInputDevice( void ) { return rtapi_->getDefaultInputDevice(); } inline unsigned int RtAudio :: getDefaultOutputDevice( void ) { return rtapi_->getDefaultOutputDevice(); } inline void RtAudio :: closeStream( void ) { return rtapi_->closeStream(); } inline RtAudioErrorType RtAudio :: startStream( void ) { return rtapi_->startStream(); } inline RtAudioErrorType RtAudio :: stopStream( void ) { return rtapi_->stopStream(); } inline RtAudioErrorType RtAudio :: abortStream( void ) { return rtapi_->abortStream(); } inline const std::string RtAudio :: getErrorText( void ) { return rtapi_->getErrorText(); } inline bool RtAudio :: isStreamOpen( void ) const { return rtapi_->isStreamOpen(); } inline bool RtAudio :: isStreamRunning( void ) const { return rtapi_->isStreamRunning(); } inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); } inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); } inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); } inline void RtAudio :: setStreamTime( double time ) { return rtapi_->setStreamTime( time ); } inline void RtAudio :: setErrorCallback( RtAudioErrorCallback errorCallback ) { rtapi_->setErrorCallback( errorCallback ); } inline void RtAudio :: showWarnings( bool value ) { rtapi_->showWarnings( value ); } #endif // Indentation settings for Vim and Emacs // // Local Variables: // c-basic-offset: 2 // indent-tabs-mode: nil // End: // // vim: et sts=2 sw=2 mlt-7.38.0/src/modules/rtaudio/consumer_rtaudio.cpp000664 000000 000000 00000074635 15172202314 022375 0ustar00rootroot000000 000000 /* * consumer_rtaudio.c -- output through RtAudio audio wrapper * Copyright (C) 2011-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #ifdef USE_INTERNAL_RTAUDIO #include "RtAudio.h" #else #include #endif #include "mltrtaudio_export.h" #if defined(RTAUDIO_VERSION_MAJOR) && RTAUDIO_VERSION_MAJOR >= 6 #define RTAUDIO_VERSION_6 #endif static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer consumer, mlt_event_data); static int rtaudio_callback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData); static void *consumer_thread_proxy(void *arg); static void *video_thread_proxy(void *arg); static const char *rtaudio_api_str(RtAudio::Api api) { switch (api) { case RtAudio::UNSPECIFIED: return "UNSPECIFIED"; case RtAudio::LINUX_ALSA: return "LINUX_ALSA"; case RtAudio::LINUX_PULSE: return "LINUX_PULSE"; case RtAudio::LINUX_OSS: return "LINUX_OSS"; case RtAudio::UNIX_JACK: return "UNIX_JACK"; case RtAudio::MACOSX_CORE: return "MACOSX_CORE"; case RtAudio::WINDOWS_WASAPI: return "WINDOWS_WASAPI"; case RtAudio::WINDOWS_ASIO: return "WINDOWS_ASIO"; case RtAudio::WINDOWS_DS: return "WINDOWS_DS"; case RtAudio::RTAUDIO_DUMMY: return "RTAUDIO_DUMMY"; default: break; } return "UNKNOWN!?!"; } class RtAudioConsumer { public: struct mlt_consumer_s consumer; RtAudio *rt; int device_id; mlt_deque queue; pthread_t thread; int joined; int running; int out_channels; uint8_t audio_buffer[4096 * 10]; int audio_avail; pthread_mutex_t audio_mutex; pthread_cond_t audio_cond; pthread_mutex_t video_mutex; pthread_cond_t video_cond; int playing; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; bool is_purge; mlt_consumer getConsumer() { return &consumer; } RtAudioConsumer() : rt(nullptr) , device_id(-1) , queue(nullptr) , joined(0) , running(0) , audio_avail(0) , playing(0) , refresh_count(0) , is_purge(false) { memset(&consumer, 0, sizeof(consumer)); } ~RtAudioConsumer() { // Close the queue mlt_deque_close(queue); // Destroy mutexes pthread_mutex_destroy(&audio_mutex); pthread_cond_destroy(&audio_cond); pthread_mutex_destroy(&video_mutex); pthread_cond_destroy(&video_cond); pthread_mutex_destroy(&refresh_mutex); pthread_cond_destroy(&refresh_cond); if (rt && rt->isStreamOpen()) rt->closeStream(); delete rt; rt = nullptr; } bool create_rtaudio(RtAudio::Api api, int channels, int frequency) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(getConsumer()); const char *resource = mlt_properties_get(properties, "resource"); unsigned int bufferFrames = mlt_properties_get_int(properties, "audio_buffer"); mlt_log_info(getConsumer(), "Attempt to open RtAudio: %s\t%d\t%d\n", rtaudio_api_str(api), channels, frequency); rt = new RtAudio(api); if (!rt) { return false; } if (rt->getDeviceCount() < 1) { mlt_log_warning(getConsumer(), "no audio devices found\n"); delete rt; rt = nullptr; return false; } #if defined(RTAUDIO_VERSION_6) || !defined(__LINUX_ALSA__) device_id = rt->getDefaultOutputDevice(); #endif if (resource && strcmp(resource, "") && strcmp(resource, "default")) { // Get device ID by name RtAudio::DeviceInfo info; unsigned int i; #ifdef RTAUDIO_VERSION_6 auto ids = rt->getDeviceIds(); for (i = 0; i < ids.size(); i++) { info = rt->getDeviceInfo(ids[i]); mlt_log_verbose(nullptr, "RtAudio device %u = %s\n", ids[i], info.name.c_str()); if (info.name.find(resource) != std::string::npos) { device_id = info.ID; break; } } if (i == ids.size()) #else unsigned int n = rt->getDeviceCount(); for (i = 0; i < n; i++) { info = rt->getDeviceInfo(i); mlt_log_verbose(nullptr, "RtAudio device %d = %s\n", i, info.name.c_str()); if (info.probed && info.name == resource) { device_id = i; break; } } // Name selection failed, try arg as numeric if (i == n) #endif device_id = (int) strtol(resource, nullptr, 0); } RtAudio::StreamParameters parameters; parameters.deviceId = device_id; parameters.nChannels = channels; parameters.firstChannel = 0; RtAudio::StreamOptions options; if (device_id == -1) { options.flags = RTAUDIO_ALSA_USE_DEFAULT; parameters.deviceId = 0; } if (resource) { #ifdef RTAUDIO_VERSION_6 auto ids = rt->getDeviceIds(); for (unsigned i = 0; i < ids.size(); i++) { auto info = rt->getDeviceInfo(ids[i]); if (info.name == resource) { device_id = parameters.deviceId = info.ID; break; } } #else unsigned n = rt->getDeviceCount(); for (unsigned i = 0; i < n; i++) { RtAudio::DeviceInfo info = rt->getDeviceInfo(i); if (info.name == resource) { device_id = parameters.deviceId = i; break; } } #endif } #ifdef RTAUDIO_VERSION_6 if (rt->isStreamOpen()) { rt->closeStream(); } if (rt->openStream(¶meters, nullptr, RTAUDIO_SINT16, frequency, &bufferFrames, &rtaudio_callback, this, &options) || rt->startStream()) { mlt_log_info(getConsumer(), "%s\n", rt->getErrorText().c_str()); delete rt; rt = nullptr; return false; } #else try { if (rt->isStreamOpen()) { rt->closeStream(); } rt->openStream(¶meters, nullptr, RTAUDIO_SINT16, frequency, &bufferFrames, &rtaudio_callback, this, &options); rt->startStream(); } #ifdef RTERROR_H catch (RtError &e) { #else catch (RtAudioError &e) { #endif mlt_log_info(getConsumer(), "%s\n", e.getMessage().c_str()); delete rt; rt = nullptr; return false; } #endif mlt_log_info(getConsumer(), "Opened RtAudio: %s\t%d\t%d\n", rtaudio_api_str(rt->getCurrentApi()), channels, frequency); return true; } bool find_and_create_rtaudio(int requested_channels, int frequency, int *actual_channels) { bool result = false; #ifdef __WINDOWS_DS__ // Prefer DirectSound on Windows RtAudio::Api PREFERRED_API = RtAudio::WINDOWS_DS; #else RtAudio::Api PREFERRED_API = RtAudio::UNSPECIFIED; #endif *actual_channels = requested_channels; // First try with preferred API. result = create_rtaudio(PREFERRED_API, *actual_channels, frequency); if (!result) { // If the preferred API fails, try other APIs that are available. std::vector apis; RtAudio::getCompiledApi(apis); for (size_t i = 0; i < apis.size(); i++) { if (apis[i] == PREFERRED_API || apis[i] == RtAudio::RTAUDIO_DUMMY) { continue; } result = create_rtaudio(apis[i], *actual_channels, frequency); if (result) { break; } } } if (!result && *actual_channels != 2) { // If surround has failed for all APIs, try stereo. *actual_channels = 2; mlt_log_info(getConsumer(), "Unable to open %d channels. Try %d channels\n", requested_channels, *actual_channels); std::vector apis; RtAudio::getCompiledApi(apis); for (size_t i = 0; i < apis.size(); i++) { if (apis[i] == RtAudio::RTAUDIO_DUMMY) { continue; } result = create_rtaudio(apis[i], *actual_channels, frequency); if (result) { break; } } } return result; } bool open(const char *arg) { // Create the queue queue = mlt_deque_init(); // get a handle on properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(&consumer); // Set the default volume mlt_properties_set_double(properties, "volume", 1.0); // This is the initialisation of the consumer pthread_mutex_init(&audio_mutex, nullptr); pthread_cond_init(&audio_cond, nullptr); pthread_mutex_init(&video_mutex, nullptr); pthread_cond_init(&video_cond, nullptr); // Default scaler (for now we'll use nearest) mlt_properties_set(properties, "rescale", "nearest"); mlt_properties_set(properties, "consumer.deinterlacer", "onefield"); // Default buffer for low latency mlt_properties_set_int(properties, "buffer", 1); // Default audio buffer mlt_properties_set_int(properties, "audio_buffer", 1024); // Set the resource to the device name arg mlt_properties_set(properties, "resource", arg); // Ensure we don't join on a non-running object joined = 1; // Initialize the refresh handler pthread_cond_init(&refresh_cond, nullptr); pthread_mutex_init(&refresh_mutex, nullptr); mlt_events_listen(properties, this, "property-changed", (mlt_listener) consumer_refresh_cb); return true; } int start() { if (!running) { stop(); running = 1; joined = 0; pthread_create(&thread, nullptr, consumer_thread_proxy, this); } return 0; } int stop() { if (running && !joined) { // Kill the thread and clean up joined = 1; running = 0; // Unlatch the consumer thread pthread_mutex_lock(&refresh_mutex); pthread_cond_broadcast(&refresh_cond); pthread_mutex_unlock(&refresh_mutex); // Cleanup the main thread pthread_join(thread, nullptr); // Unlatch the video thread pthread_mutex_lock(&video_mutex); pthread_cond_broadcast(&video_cond); pthread_mutex_unlock(&video_mutex); // Unlatch the audio callback pthread_mutex_lock(&audio_mutex); pthread_cond_broadcast(&audio_cond); pthread_mutex_unlock(&audio_mutex); if (rt && rt->isStreamOpen()) #ifdef RTAUDIO_VERSION_6 if (rt->stopStream()) { mlt_log_error(getConsumer(), "%s\n", rt->getErrorText().c_str()); } #else try { // Stop the stream rt->stopStream(); } #ifdef RTERROR_H catch (RtError &e) { #else catch (RtAudioError &e) { #endif mlt_log_error(getConsumer(), "%s\n", e.getMessage().c_str()); } #endif delete rt; rt = nullptr; } return 0; } void purge() { if (running) { pthread_mutex_lock(&video_mutex); mlt_frame frame = MLT_FRAME(mlt_deque_peek_back(queue)); // When playing rewind or fast forward then we need to keep one // frame in the queue to prevent playback stalling. double speed = frame ? mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") : 0; int n = (speed == 0.0 || speed == 1.0) ? 0 : 1; while (mlt_deque_count(queue) > n) mlt_frame_close(MLT_FRAME(mlt_deque_pop_back(queue))); is_purge = true; pthread_cond_broadcast(&video_cond); pthread_mutex_unlock(&video_mutex); } } void consumer_thread() { // Get the properties mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES(getConsumer()); // Video thread pthread_t thread; // internal initialization int init_audio = 1; int init_video = 1; mlt_frame frame = nullptr; mlt_properties properties = nullptr; int64_t duration = 0; int64_t playtime = mlt_properties_get_int(consumer_props, "video_delay") * 1000; struct timespec tm = {0, 100000}; // int last_position = -1; pthread_mutex_lock(&refresh_mutex); refresh_count = 0; pthread_mutex_unlock(&refresh_mutex); // Loop until told not to while (running) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame(getConsumer()); // Ensure that we have a frame if (frame) { // Get the frame properties properties = MLT_FRAME_PROPERTIES(frame); // Get the speed of the frame double speed = mlt_properties_get_double(properties, "_speed"); // Get refresh request for the current frame int refresh = mlt_properties_get_int(consumer_props, "refresh"); // Clear refresh mlt_events_block(consumer_props, consumer_props); mlt_properties_set_int(consumer_props, "refresh", 0); mlt_events_unblock(consumer_props, consumer_props); // Play audio init_audio = play_audio(frame, init_audio, &duration); // Determine the start time now if (playing && init_video) { // Create the video thread pthread_create(&thread, nullptr, video_thread_proxy, this); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame in microseconds mlt_properties_set_int64(properties, "playtime", playtime); while (running && speed != 0 && mlt_deque_count(queue) > 15) nanosleep(&tm, nullptr); // Push this frame to the back of the video queue if (running && speed) { pthread_mutex_lock(&video_mutex); if (is_purge && speed == 1.0) { mlt_frame_close(frame); is_purge = false; } else { mlt_deque_push_back(queue, frame); pthread_cond_broadcast(&video_cond); } pthread_mutex_unlock(&video_mutex); // Calculate the next playtime playtime += duration; } else if (running) { pthread_mutex_lock(&refresh_mutex); if (refresh == 0 && refresh_count <= 0) { play_video(frame); pthread_cond_wait(&refresh_cond, &refresh_mutex); } mlt_frame_close(frame); refresh_count--; pthread_mutex_unlock(&refresh_mutex); } else { mlt_frame_close(frame); frame = nullptr; } // Optimisation to reduce latency if (frame && speed == 1.0) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else if (speed == 0.0) { mlt_consumer_purge(getConsumer()); // last_position = -1; } } } // Kill the video thread if (init_video == 0) { pthread_mutex_lock(&video_mutex); pthread_cond_broadcast(&video_cond); pthread_mutex_unlock(&video_mutex); pthread_join(thread, nullptr); } while (mlt_deque_count(queue)) mlt_frame_close((mlt_frame) mlt_deque_pop_back(queue)); audio_avail = 0; } int callback(int16_t *outbuf, int16_t *inbuf, unsigned int samples, double streamTime, RtAudioStreamStatus status) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(getConsumer()); double volume = mlt_properties_get_double(properties, "volume"); int len = mlt_audio_format_size(mlt_audio_s16, samples, out_channels); pthread_mutex_lock(&audio_mutex); // Block until audio received while (running && len > audio_avail) pthread_cond_wait(&audio_cond, &audio_mutex); if (audio_avail >= len) { // Place in the audio buffer memcpy(outbuf, audio_buffer, len); // Remove len from the audio available audio_avail -= len; // Remove the samples memmove(audio_buffer, audio_buffer + len, audio_avail); } else { // Just to be safe, wipe the stream first memset(outbuf, 0, len); // Copy what we have memcpy(outbuf, audio_buffer, audio_avail); // No audio left audio_avail = 0; } if (volume != 1.0) { int16_t *p = outbuf; int i = samples * out_channels + 1; while (--i) *p++ *= volume; } // We're definitely playing now playing = 1; pthread_cond_broadcast(&audio_cond); pthread_mutex_unlock(&audio_mutex); return 0; } int play_audio(mlt_frame frame, int init_audio, int64_t *duration) { // Get the properties of this consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES(getConsumer()); mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int(properties, "channels"); int frequency = mlt_properties_get_int(properties, "frequency"); int scrub = mlt_properties_get_int(properties, "scrub_audio"); static int counter = 0; int samples = mlt_audio_calculate_frame_samples(mlt_properties_get_double(properties, "fps"), frequency, counter++); int16_t *pcm; mlt_frame_get_audio(frame, (void **) &pcm, &afmt, &frequency, &channels, &samples); *duration = 1000000LL * samples / frequency; if (mlt_properties_get_int(properties, "audio_off")) { playing = 1; return init_audio; } if (init_audio == 1) { if (find_and_create_rtaudio(channels, frequency, &out_channels)) { init_audio = 0; playing = 1; } else { rt = nullptr; mlt_log_error(getConsumer(), "Unable to initialize RtAudio\n"); init_audio = 2; } } if (init_audio == 0) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int samples_copied = 0; int dst_stride = out_channels * sizeof(*pcm); pthread_mutex_lock(&audio_mutex); while (running && samples_copied < samples) { int sample_space = (sizeof(audio_buffer) - audio_avail) / dst_stride; while (running && sample_space == 0) { pthread_cond_wait(&audio_cond, &audio_mutex); sample_space = (sizeof(audio_buffer) - audio_avail) / dst_stride; } if (running) { int samples_to_copy = samples - samples_copied; if (samples_to_copy > sample_space) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if (scrub || mlt_properties_get_double(properties, "_speed") == 1) { if (channels == out_channels) { memcpy(&audio_buffer[audio_avail], pcm, dst_bytes); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t *) &audio_buffer[audio_avail]; int i = samples_to_copy + 1; while (--i) { memcpy(dest, pcm, dst_stride); pcm += channels; dest += out_channels; } } } else { memset(&audio_buffer[audio_avail], 0, dst_bytes); pcm += samples_to_copy * channels; } audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast(&audio_cond); } pthread_mutex_unlock(&audio_mutex); } return init_audio; } int play_video(mlt_frame frame) { // Get the properties of this consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES(getConsumer()); if (running && !mlt_consumer_is_stopped(getConsumer())) { mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } return 0; } void video_thread() { // Obtain time of thread start struct timeval now; int64_t start = 0; int64_t elapsed = 0; struct timespec tm; mlt_frame next = nullptr; mlt_properties consumerProperties = MLT_CONSUMER_PROPERTIES(getConsumer()); double speed = 0; // Get real time flag int real_time = mlt_properties_get_int(consumerProperties, "real_time"); // Get the current time gettimeofday(&now, nullptr); // Determine start time start = (int64_t) now.tv_sec * 1000000 + now.tv_usec; while (running) { // Pop the next frame pthread_mutex_lock(&video_mutex); next = (mlt_frame) mlt_deque_pop_front(queue); while (next == nullptr && running) { pthread_cond_wait(&video_cond, &video_mutex); next = (mlt_frame) mlt_deque_pop_front(queue); } pthread_mutex_unlock(&video_mutex); if (!running || next == nullptr) break; // Get the properties mlt_properties properties = MLT_FRAME_PROPERTIES(next); // Get the speed of the frame speed = mlt_properties_get_double(properties, "_speed"); // Get the current time gettimeofday(&now, nullptr); // Get the elapsed time elapsed = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - start; // See if we have to delay the display of the current frame if (mlt_properties_get_int(properties, "rendered") == 1 && running) { // Obtain the scheduled playout time in microseconds int64_t scheduled = mlt_properties_get_int64(properties, "playtime"); // Determine the difference between the elapsed time and the scheduled playout time int64_t difference = scheduled - elapsed; // Smooth playback a bit if (real_time && (difference > 20000 && speed == 1.0)) { tm.tv_sec = difference / 1000000; tm.tv_nsec = (difference % 1000000) * 1000; nanosleep(&tm, nullptr); } // Show current frame if not too old if (!real_time || (difference > -10000 || speed != 1.0 || mlt_deque_count(queue) < 2)) play_video(next); // If the queue is empty, recalculate start to allow build up again if (real_time && (mlt_deque_count(queue) == 0 && speed == 1.0)) { gettimeofday(&now, nullptr); start = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - scheduled + 20000; start += mlt_properties_get_int(consumerProperties, "video_delay") * 1000; } } // This frame can now be closed mlt_frame_close(next); next = nullptr; } if (next != nullptr) mlt_frame_close(next); mlt_consumer_stopped(getConsumer()); } }; static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer consumer, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "refresh")) { RtAudioConsumer *rtaudio = (RtAudioConsumer *) consumer->child; pthread_mutex_lock(&rtaudio->refresh_mutex); rtaudio->refresh_count = rtaudio->refresh_count <= 0 ? 1 : rtaudio->refresh_count + 1; pthread_cond_broadcast(&rtaudio->refresh_cond); pthread_mutex_unlock(&rtaudio->refresh_mutex); } } static int rtaudio_callback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { RtAudioConsumer *rtaudio = (RtAudioConsumer *) userData; return rtaudio->callback((int16_t *) outputBuffer, (int16_t *) inputBuffer, nFrames, streamTime, status); } static void *consumer_thread_proxy(void *arg) { RtAudioConsumer *rtaudio = (RtAudioConsumer *) arg; rtaudio->consumer_thread(); return nullptr; } static void *video_thread_proxy(void *arg) { RtAudioConsumer *rtaudio = (RtAudioConsumer *) arg; rtaudio->video_thread(); return nullptr; } /** Start the consumer. */ static int start(mlt_consumer consumer) { RtAudioConsumer *rtaudio = (RtAudioConsumer *) consumer->child; return rtaudio->start(); } /** Stop the consumer. */ static int stop(mlt_consumer consumer) { RtAudioConsumer *rtaudio = (RtAudioConsumer *) consumer->child; return rtaudio->stop(); } /** Determine if the consumer is stopped. */ static int is_stopped(mlt_consumer consumer) { RtAudioConsumer *rtaudio = (RtAudioConsumer *) consumer->child; return !rtaudio->running; } static void purge(mlt_consumer consumer) { RtAudioConsumer *rtaudio = (RtAudioConsumer *) consumer->child; rtaudio->purge(); } /** Close the consumer. */ static void close(mlt_consumer consumer) { // Stop the consumer mlt_consumer_stop(consumer); // Close the parent consumer->close = nullptr; mlt_consumer_close(consumer); // Free the memory delete (RtAudioConsumer *) consumer->child; } extern "C" { mlt_consumer consumer_rtaudio_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Allocate the consumer RtAudioConsumer *rtaudio = new RtAudioConsumer(); mlt_consumer consumer = nullptr; // If allocated if (rtaudio && !mlt_consumer_init(rtaudio->getConsumer(), rtaudio, profile)) { // If initialises without error if (rtaudio->open(arg ? arg : getenv("AUDIODEV"))) { // Setup callbacks consumer = rtaudio->getConsumer(); consumer->close = close; consumer->start = start; consumer->stop = stop; consumer->is_stopped = is_stopped; consumer->purge = purge; } else { mlt_consumer_close(rtaudio->getConsumer()); delete rtaudio; } } // Return consumer return consumer; } static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; const char *service_type = "consumer"; snprintf(file, PATH_MAX, "%s/rtaudio/%s_%s.yml", mlt_environment("MLT_DATA"), service_type, id); return mlt_properties_parse_yaml(file); } MLTRTAUDIO_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "rtaudio", consumer_rtaudio_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "rtaudio", metadata, nullptr); } } // extern C mlt-7.38.0/src/modules/rtaudio/consumer_rtaudio.yml000664 000000 000000 00000002466 15172202314 022405 0ustar00rootroot000000 000000 schema_version: 0.3 type: consumer identifier: rtaudio title: RtAudio description: > RtAudio provides native, realtime audio output across Linux, Macintosh OS X, Windows, and some BSD operating systems. url: http://www.music.mcgill.ca/~gary/rtaudio/ version: 2 copyright: Meltytech, LLC creator: Dan Dennedy creator: Gary P. Scavone license: LGPLv2.1 language: en tags: - Audio parameters: - identifier: resource title: Device description: An optional device name, number, or ID to use. type: string required: no argument: yes - identifier: audio_buffer title: Audio buffer type: integer minimum: 256 maximum: 8192 default: 1024 unit: samples - identifier: volume title: Volume type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: refresh description: > Applications should set this to update the video frame when paused. type: boolean minimum: 0 maximum: 1 - identifier: scrub_audio title: Audio scrubbing type: boolean description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: video_delay title: Video delay mutable: no type: integer unit: milliseconds default: 0 mlt-7.38.0/src/modules/rubberband/000775 000000 000000 00000000000 15172202314 016727 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/rubberband/CMakeLists.txt000664 000000 000000 00000001274 15172202314 021473 0ustar00rootroot000000 000000 add_library(mltrubberband MODULE factory.c filter_rbpitch.cpp) file(GLOB YML "*.yml") add_custom_target(Other_rubberband_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltrubberband) target_compile_options(mltrubberband PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltrubberband PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltrubberband PRIVATE mlt PkgConfig::rubberband) set_target_properties(mltrubberband PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltrubberband LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_rbpitch.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/rubberband) mlt-7.38.0/src/modules/rubberband/factory.c000664 000000 000000 00000003077 15172202314 020551 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2020 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mltrubberband_export.h" #include #include #include extern mlt_filter filter_rbpitch_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/rubberband/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTRUBBERBAND_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "rbpitch", filter_rbpitch_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "rbpitch", metadata, "filter_rbpitch.yml"); } mlt-7.38.0/src/modules/rubberband/filter_rbpitch.cpp000664 000000 000000 00000027136 15172202314 022444 0ustar00rootroot000000 000000 /* * filter_rbpitch.c -- adjust audio pitch * Copyright (C) 2020-2024 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include using namespace RubberBand; // Private Types typedef struct { RubberBandStretcher *s; int rubberband_frequency; uint64_t in_samples; uint64_t out_samples; } private_data; static const size_t MAX_CHANNELS = 10; static int rbpitch_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_filter filter = static_cast(mlt_frame_pop_audio(frame)); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); private_data *pdata = (private_data *) filter->child; if (*channels > (int) MAX_CHANNELS) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Too many channels requested: %d > %d\n", *channels, (int) MAX_CHANNELS); return mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_FILTER_SERVICE(filter)); if (!unique_properties) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Missing unique_properites\n"); return mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } // Get the producer's audio int requested_frequency = *frequency; int requested_samples = *samples; *format = mlt_audio_float; int error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); if (error) return error; // Make sure the audio is in the correct format // This is useful if the filter is encapsulated in a producer and does not // have a normalizing filter before it. if (*format != mlt_audio_float && frame->convert_audio != NULL) { frame->convert_audio(frame, buffer, format, mlt_audio_float); } // Sanity check parameters // rubberband library crashes have been seen with a very large scale factor // or very small sampling frequency. Very small scale factor and very high // sampling frequency can result in too much audio lag. // Disallow these extreme scenarios for now. Maybe it will be improved in // the future. double pitchscale = mlt_properties_get_double(unique_properties, "pitchscale"); pitchscale = CLAMP(pitchscale, 0.05, 50.0); double timeratio = 1.0; int stretch = mlt_properties_get_int(unique_properties, "stretch"); int rubberband_frequency = *frequency; if (stretch) { rubberband_frequency = requested_frequency; timeratio = (double) requested_samples / (double) *samples; } rubberband_frequency = CLAMP(rubberband_frequency, 10000, 300000); // Protect the RubberBandStretcher instance. mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Configure the stretcher. RubberBandStretcher *s = pdata->s; if (!s || s->available() == -1 || (int) s->getChannelCount() != *channels || pdata->rubberband_frequency != rubberband_frequency) { mlt_log_debug(MLT_FILTER_SERVICE(filter), "Create a new stretcher\t%d\t%d\t%f\n", *channels, rubberband_frequency, pitchscale); delete s; // Create a rubberband instance RubberBandStretcher::Options options = RubberBandStretcher::OptionProcessRealTime; #if RUBBERBAND_API_MAJOR_VERSION >= 2 && RUBBERBAND_API_MINOR_VERSION >= 7 // Use the higher quality engine if available. options |= RubberBandStretcher::OptionEngineFiner; #endif s = new RubberBandStretcher(rubberband_frequency, *channels, options, 1.0, pitchscale); pdata->s = s; pdata->rubberband_frequency = rubberband_frequency; pdata->in_samples = 0; pdata->out_samples = 0; } s->setPitchScale(pitchscale); if (pitchscale >= 0.5 && pitchscale <= 2.0) { // Pitch adjustment < 200% #if RUBBERBAND_API_MAJOR_VERSION <= 2 && RUBBERBAND_API_MINOR_VERSION < 7 s->setPitchOption(RubberBandStretcher::OptionPitchHighQuality); #endif s->setTransientsOption(RubberBandStretcher::OptionTransientsCrisp); } else { // Pitch adjustment > 200% // "HighConsistency" and "Smooth" options help to avoid large memory // consumption and crashes that can occur for large pitch adjustments. #if RUBBERBAND_API_MAJOR_VERSION <= 2 && RUBBERBAND_API_MINOR_VERSION < 7 s->setPitchOption(RubberBandStretcher::OptionPitchHighConsistency); #endif s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth); } s->setTimeRatio(timeratio); // Configure input and output buffers and counters. int consumed_samples = 0; int total_consumed_samples = 0; int received_samples = 0; struct mlt_audio_s in; struct mlt_audio_s out; mlt_audio_set_values(&in, *buffer, *frequency, *format, *samples, *channels); if (stretch) { *frequency = requested_frequency; *samples = requested_samples; } mlt_audio_set_values(&out, NULL, *frequency, *format, *samples, *channels); mlt_audio_alloc_data(&out); // Process all input samples while (true) { // Send more samples to the stretcher if (consumed_samples == in.samples) { // Continue to repeat input samples into the stretcher until it // provides the desired number of samples out. consumed_samples = 0; mlt_log_debug(MLT_FILTER_SERVICE(filter), "Repeat samples\n"); } int process_samples = std::min(in.samples - consumed_samples, (int) s->getSamplesRequired()); if (process_samples == 0 && received_samples == out.samples && total_consumed_samples < in.samples) { // No more out samples are needed, but input samples are still available. // Send the final input samples for processing. process_samples = in.samples - total_consumed_samples; } if (process_samples > 0) { float *in_planes[MAX_CHANNELS]; for (int i = 0; i < in.channels; i++) { in_planes[i] = ((float *) in.data) + (in.samples * i) + consumed_samples; } s->process(in_planes, process_samples, false); consumed_samples += process_samples; total_consumed_samples += process_samples; pdata->in_samples += process_samples; } // Receive samples from the stretcher int retrieve_samples = std::min(out.samples - received_samples, s->available()); if (retrieve_samples > 0) { float *out_planes[MAX_CHANNELS]; for (int i = 0; i < out.channels; i++) { out_planes[i] = ((float *) out.data) + (out.samples * i) + received_samples; } retrieve_samples = (int) s->retrieve(out_planes, retrieve_samples); received_samples += retrieve_samples; pdata->out_samples += retrieve_samples; } mlt_log_debug(MLT_FILTER_SERVICE(filter), "Process: %d\t Retrieve: %d\n", process_samples, retrieve_samples); if (received_samples == out.samples && total_consumed_samples >= in.samples) { // There is nothing more to do; break; } } // Save the processed samples. mlt_audio_shrink(&out, received_samples); mlt_frame_set_audio(frame, out.data, out.format, 0, out.release_data); mlt_audio_get_values(&out, buffer, frequency, format, samples, channels); // Report the latency. double latency = (double) (pdata->in_samples - pdata->out_samples) * 1000.0 / (double) *frequency; mlt_properties_set_double(filter_properties, "latency", latency); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); mlt_log_debug(MLT_FILTER_SERVICE(filter), "Requested: %d\tReceived: %d\tSent: %d\tLatency: %d(%fms)\n", requested_samples, in.samples, out.samples, (int) (pdata->in_samples - pdata->out_samples), latency); return error; } static mlt_frame filter_process(mlt_filter filter, mlt_frame frame) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); // Determine the pitchscale double pitchscale = 1.0; if (mlt_properties_exists(filter_properties, "pitchscale")) { pitchscale = mlt_properties_anim_get_double(filter_properties, "pitchscale", position, length); } else { double octaveshift = mlt_properties_anim_get_double(filter_properties, "octaveshift", position, length); pitchscale = pow(2, octaveshift); } if (pitchscale <= 0.0 || /*check for nan:*/ pitchscale != pitchscale) { pitchscale = 1.0; } // Save the pitchscale on the frame to be used in rbpitch_get_audio mlt_properties unique_properties = mlt_frame_unique_properties(frame, MLT_FILTER_SERVICE(filter)); mlt_properties_set_double(unique_properties, "pitchscale", pitchscale); mlt_properties_set_int(unique_properties, "stretch", mlt_properties_get_int(filter_properties, "stretch")); mlt_frame_push_audio(frame, (void *) filter); mlt_frame_push_audio(frame, (void *) rbpitch_get_audio); return frame; } static void close_filter(mlt_filter filter) { private_data *pdata = (private_data *) filter->child; if (pdata) { RubberBandStretcher *s = static_cast(pdata->s); if (s) { delete s; } free(pdata); filter->child = NULL; } } extern "C" { mlt_filter filter_rbpitch_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (filter && pdata) { pdata->s = NULL; pdata->rubberband_frequency = 0; pdata->in_samples = 0; pdata->out_samples = 0; filter->process = filter_process; filter->close = close_filter; filter->child = pdata; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Failed to initialize\n"); if (filter) { mlt_filter_close(filter); } free(pdata); filter = NULL; } return filter; } } mlt-7.38.0/src/modules/rubberband/filter_rbpitch.yml000664 000000 000000 00000003646 15172202314 022463 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: rbpitch title: Rubberband Pitch version: 1 copyright: Meltytech, LLC license: GPLv2 language: en tags: - Audio description: Adjust the audio pitch using the Rubberband library. parameters: - identifier: octaveshift title: Octave Shift type: float description: > The octave shift. This is the octave shift of the source frequency. For example, a shift of +1 would double the frequency; -1 would halve the frequency and 0 would leave the pitch unaffected. To put this in frequency terms, a frequency shift f (where f greater than one for upwards shift and less than one for downwards) is: o = log(f) / log(2). Ignored if pitchscale is set. readonly: no mutable: yes animation: yes default: 0.0 minimum: -3.3 maximum: 3.3 unit: octaves - identifier: pitchscale title: Pitch Scale type: float description: > The pitch scaling ratio. This is the ratio of target frequency to source frequency. For example, a ratio of 2.0 would shift up by one octave; 0.5 down by one octave; or 1.0 leave the pitch unaffected. To put this in musical terms, a pitch scaling ratio corresponding to a shift of o octaves (where o is positive for an upwards shift and negative for downwards) is: f = pow(2.0, o). Overrides octaveshift. readonly: no mutable: yes animation: yes default: 1.0 minimum: 0.1 maximum: 10 - identifier: stretch title: Stretch type: boolean description: > Stretch the audio to fill the requested samples. This option will have no effect if the requested sample size is the same as the received sample size. readonly: yes - identifier: latency title: Latency type: float description: > The amount of delay for each sample from the input to the output. readonly: yes unit: ms mlt-7.38.0/src/modules/rubberband/gpl000664 000000 000000 00000000000 15172202314 017422 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/sdl/000775 000000 000000 00000000000 15172202314 015403 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/sdl/CMakeLists.txt000664 000000 000000 00000001757 15172202314 020155 0ustar00rootroot000000 000000 add_library(mltsdl MODULE consumer_sdl_audio.c consumer_sdl_preview.c consumer_sdl_still.c consumer_sdl.c factory.c ) file(GLOB YML "*.yml") add_custom_target(Other_sdl_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltsdl) target_compile_options(mltsdl PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltsdl PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltsdl PRIVATE mlt Threads::Threads PkgConfig::sdl) if(NOT MSVC) target_link_libraries(mltsdl PRIVATE m) endif() if(APPLE) target_link_libraries(mltsdl PRIVATE objc "-framework Foundation") elseif(UNIX) target_link_libraries(mltsdl PRIVATE X11) endif() set_target_properties(mltsdl PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltsdl LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_sdl_audio.yml consumer_sdl_preview.yml consumer_sdl_still.yml consumer_sdl.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/sdl ) mlt-7.38.0/src/modules/sdl/consumer_sdl.c000664 000000 000000 00000100126 15172202314 020244 0ustar00rootroot000000 000000 /* * consumer_sdl.c -- A Simple DirectMedia Layer consumer * Copyright (C) 2003-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "consumer_sdl_osx.h" extern pthread_mutex_t mlt_sdl_mutex; /** This classes definition. */ typedef struct consumer_sdl_s *consumer_sdl; struct consumer_sdl_s { struct mlt_consumer_s parent; mlt_properties properties; mlt_deque queue; pthread_t thread; int joined; atomic_int running; uint8_t audio_buffer[4096 * 10]; int audio_avail; pthread_mutex_t audio_mutex; pthread_cond_t audio_cond; pthread_mutex_t video_mutex; pthread_cond_t video_cond; int window_width; int window_height; int previous_width; int previous_height; int width; int height; atomic_int playing; int sdl_flags; SDL_Overlay *sdl_overlay; SDL_Rect rect; uint8_t *buffer; int bpp; int is_purge; }; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_purge(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); static int consumer_get_dimensions(int *width, int *height); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_sdl_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object consumer_sdl self = calloc(1, sizeof(struct consumer_sdl_s)); // If no malloc'd and consumer init ok if (self != NULL && mlt_consumer_init(&self->parent, self, profile) == 0) { // Create the queue self->queue = mlt_deque_init(); // Get the parent consumer object mlt_consumer parent = &self->parent; // We have stuff to clean up, so override the close method parent->close = consumer_close; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE(parent); self->properties = MLT_SERVICE_PROPERTIES(service); // Set the default volume mlt_properties_set_double(self->properties, "volume", 1.0); // This is the initialisation of the consumer pthread_mutex_init(&self->audio_mutex, NULL); pthread_cond_init(&self->audio_cond, NULL); pthread_mutex_init(&self->video_mutex, NULL); pthread_cond_init(&self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set(self->properties, "rescale", "nearest"); mlt_properties_set(self->properties, "consumer.deinterlacer", "onefield"); mlt_properties_set_int(self->properties, "top_field_first", -1); // Default buffer for low latency mlt_properties_set_int(self->properties, "buffer", 1); // Default audio buffer mlt_properties_set_int(self->properties, "audio_buffer", 2048); // Default scrub audio mlt_properties_set_int(self->properties, "scrub_audio", 1); // Ensure we don't join on a non-running object self->joined = 1; // process actual param if (arg && sscanf(arg, "%dx%d", &self->width, &self->height)) { mlt_properties_set_int(self->properties, "_arg_size", 1); } else { self->width = mlt_properties_get_int(self->properties, "width"); self->height = mlt_properties_get_int(self->properties, "height"); } // Set the sdl flags self->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_DOUBLEBUF; #if !defined(__APPLE__) self->sdl_flags |= SDL_RESIZABLE; #endif // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Register specific events mlt_events_register(self->properties, "consumer-sdl-event"); // Return the consumer produced return parent; } // malloc or consumer init failed free(self); // Indicate failure return NULL; } int consumer_start(mlt_consumer parent) { consumer_sdl self = parent->child; if (!self->running) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); int video_off = mlt_properties_get_int(properties, "video_off"); int preview_off = mlt_properties_get_int(properties, "preview_off"); int display_off = video_off | preview_off; int audio_off = mlt_properties_get_int(properties, "audio_off"); int sdl_started = mlt_properties_get_int(properties, "sdl_started"); char *output_display = mlt_properties_get(properties, "output_display"); char *window_id = mlt_properties_get(properties, "window_id"); char *audio_driver = mlt_properties_get(properties, "audio_driver"); char *video_driver = mlt_properties_get(properties, "video_driver"); char *audio_device = mlt_properties_get(properties, "audio_device"); consumer_stop(parent); self->running = 1; self->joined = 0; if (output_display != NULL) setenv("DISPLAY", output_display, 1); if (window_id != NULL) setenv("SDL_WINDOWID", window_id, 1); if (video_driver != NULL) setenv("SDL_VIDEODRIVER", video_driver, 1); if (audio_driver != NULL) setenv("SDL_AUDIODRIVER", audio_driver, 1); if (audio_device != NULL) setenv("AUDIODEV", audio_device, 1); if (!mlt_properties_get_int(self->properties, "_arg_size")) { if (mlt_properties_get_int(self->properties, "width") > 0) self->width = mlt_properties_get_int(self->properties, "width"); if (mlt_properties_get_int(self->properties, "height") > 0) self->height = mlt_properties_get_int(self->properties, "height"); } self->bpp = mlt_properties_get_int(self->properties, "bpp"); if (sdl_started == 0 && display_off == 0) { pthread_mutex_lock(&mlt_sdl_mutex); int ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE); pthread_mutex_unlock(&mlt_sdl_mutex); if (ret < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError()); return -1; } SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); SDL_EnableUNICODE(1); } if (audio_off == 0) SDL_InitSubSystem(SDL_INIT_AUDIO); // Default window size if (mlt_properties_get_int(self->properties, "_arg_size")) { self->window_width = self->width; self->window_height = self->height; } else { double display_ratio = mlt_properties_get_double(self->properties, "display_ratio"); self->window_width = (double) self->height * display_ratio + 0.5; self->window_height = self->height; } pthread_mutex_lock(&mlt_sdl_mutex); if (!SDL_GetVideoSurface() && display_off == 0) { if (mlt_properties_get_int(self->properties, "fullscreen")) { const SDL_VideoInfo *vi; vi = SDL_GetVideoInfo(); self->window_width = vi->current_w; self->window_height = vi->current_h; self->sdl_flags |= SDL_FULLSCREEN; SDL_ShowCursor(SDL_DISABLE); } SDL_SetVideoMode(self->window_width, self->window_height, 0, self->sdl_flags); } pthread_mutex_unlock(&mlt_sdl_mutex); pthread_create(&self->thread, NULL, consumer_thread, self); } return 0; } int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; if (self->joined == 0) { // Kill the thread and clean up self->joined = 1; self->running = 0; if (!mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "audio_off")) { pthread_mutex_lock(&self->audio_mutex); pthread_cond_broadcast(&self->audio_cond); pthread_mutex_unlock(&self->audio_mutex); } #ifndef _WIN32 if (self->thread) #endif pthread_join(self->thread, NULL); // cleanup SDL pthread_mutex_lock(&mlt_sdl_mutex); if (self->sdl_overlay != NULL) SDL_FreeYUVOverlay(self->sdl_overlay); self->sdl_overlay = NULL; if (!mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "audio_off")) SDL_QuitSubSystem(SDL_INIT_AUDIO); if (mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "sdl_started") == 0) SDL_Quit(); pthread_mutex_unlock(&mlt_sdl_mutex); } return 0; } int consumer_is_stopped(mlt_consumer parent) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge(mlt_consumer parent) { consumer_sdl self = parent->child; if (self->running) { pthread_mutex_lock(&self->video_mutex); while (mlt_deque_count(self->queue)) mlt_frame_close(mlt_deque_pop_back(self->queue)); self->is_purge = 1; pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); } } static int sdl_lock_display() { pthread_mutex_lock(&mlt_sdl_mutex); SDL_Surface *screen = SDL_GetVideoSurface(); int result = screen != NULL && (!SDL_MUSTLOCK(screen) || SDL_LockSurface(screen) >= 0); pthread_mutex_unlock(&mlt_sdl_mutex); return result; } static void sdl_unlock_display() { pthread_mutex_lock(&mlt_sdl_mutex); SDL_Surface *screen = SDL_GetVideoSurface(); if (screen != NULL && SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); pthread_mutex_unlock(&mlt_sdl_mutex); } static void sdl_fill_audio(void *udata, uint8_t *stream, int len) { consumer_sdl self = udata; // Get the volume double volume = mlt_properties_get_double(self->properties, "volume"); pthread_mutex_lock(&self->audio_mutex); // Block until audio received while (self->running && len > self->audio_avail) pthread_cond_wait(&self->audio_cond, &self->audio_mutex); if (self->audio_avail >= len) { // Place in the audio buffer if (volume != 1.0) SDL_MixAudio(stream, self->audio_buffer, len, (int) ((float) SDL_MIX_MAXVOLUME * volume)); else memcpy(stream, self->audio_buffer, len); // Remove len from the audio available self->audio_avail -= len; // Remove the samples memmove(self->audio_buffer, self->audio_buffer + len, self->audio_avail); } else { // Just to be safe, wipe the stream first memset(stream, 0, len); // Mix the audio SDL_MixAudio(stream, self->audio_buffer, len, (int) ((float) SDL_MIX_MAXVOLUME * volume)); // No audio left self->audio_avail = 0; } // We're definitely playing now self->playing = 1; pthread_cond_broadcast(&self->audio_cond); pthread_mutex_unlock(&self->audio_mutex); } static int consumer_play_audio(consumer_sdl self, mlt_frame frame, int init_audio, int *duration) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int(properties, "channels"); int dest_channels = channels; int frequency = mlt_properties_get_int(properties, "frequency"); int scrub = mlt_properties_get_int(properties, "scrub_audio"); static int counter = 0; int samples = mlt_audio_calculate_frame_samples(mlt_properties_get_double(self->properties, "fps"), frequency, counter++); int16_t *pcm; mlt_frame_get_audio(frame, (void **) &pcm, &afmt, &frequency, &channels, &samples); *duration = ((samples * 1000) / frequency); pcm += mlt_properties_get_int(properties, "audio_offset"); if (mlt_properties_get_int(properties, "audio_off")) { self->playing = 1; init_audio = 1; return init_audio; } if (init_audio == 1) { SDL_AudioSpec request; SDL_AudioSpec got; int audio_buffer = mlt_properties_get_int(properties, "audio_buffer"); // specify audio format memset(&request, 0, sizeof(SDL_AudioSpec)); self->playing = 0; request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = dest_channels; request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *) self; if (SDL_OpenAudio(&request, &got) != 0) { mlt_log_error(MLT_CONSUMER_SERVICE(self), "SDL failed to open audio: %s\n", SDL_GetError()); init_audio = 2; } else if (got.size != 0) { SDL_PauseAudio(0); init_audio = 0; } } if (init_audio == 0) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int samples_copied = 0; int dst_stride = dest_channels * sizeof(*pcm); pthread_mutex_lock(&self->audio_mutex); while (self->running && samples_copied < samples) { int sample_space = (sizeof(self->audio_buffer) - self->audio_avail) / dst_stride; while (self->running && sample_space == 0) { pthread_cond_wait(&self->audio_cond, &self->audio_mutex); sample_space = (sizeof(self->audio_buffer) - self->audio_avail) / dst_stride; } if (self->running) { int samples_to_copy = samples - samples_copied; if (samples_to_copy > sample_space) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if (scrub || mlt_properties_get_double(properties, "_speed") == 1) { if (channels == dest_channels) { memcpy(&self->audio_buffer[self->audio_avail], pcm, dst_bytes); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t *) &self->audio_buffer[self->audio_avail]; int i = samples_to_copy + 1; while (--i) { memcpy(dest, pcm, dst_stride); pcm += channels; dest += dest_channels; } } } else { memset(&self->audio_buffer[self->audio_avail], 0, dst_bytes); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast(&self->audio_cond); } pthread_mutex_unlock(&self->audio_mutex); } else { self->playing = 1; } return init_audio; } static int consumer_play_video(consumer_sdl self, mlt_frame frame) { // Get the properties of this consumer mlt_properties properties = self->properties; mlt_image_format vfmt = mlt_properties_get_int(properties, "mlt_image_format"); int width = self->width, height = self->height; uint8_t *image; int changed = 0; int video_off = mlt_properties_get_int(properties, "video_off"); int preview_off = mlt_properties_get_int(properties, "preview_off"); mlt_image_format preview_format = mlt_properties_get_int(properties, "preview_format"); int display_off = video_off | preview_off; if (self->running && display_off == 0) { // Get the image, width and height mlt_frame_get_image(frame, &image, &vfmt, &width, &height, 0); void *pool = mlt_cocoa_autorelease_init(); // Handle events if (SDL_GetVideoSurface()) { SDL_Event event; sdl_lock_display(); pthread_mutex_lock(&mlt_sdl_mutex); changed = consumer_get_dimensions(&self->window_width, &self->window_height); pthread_mutex_unlock(&mlt_sdl_mutex); sdl_unlock_display(); while (SDL_PollEvent(&event)) { mlt_events_fire(self->properties, "consumer-sdl-event", mlt_event_data_from_object(&event)); switch (event.type) { case SDL_VIDEORESIZE: self->window_width = event.resize.w; self->window_height = event.resize.h; changed = 1; break; case SDL_QUIT: self->running = 0; break; case SDL_KEYDOWN: { mlt_producer producer = mlt_properties_get_data(properties, "transport_producer", NULL); char keyboard[2] = " "; void (*callback)(mlt_producer, char *) = mlt_properties_get_data(properties, "transport_callback", NULL); if (callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0) { keyboard[0] = (char) event.key.keysym.unicode; callback(producer, keyboard); } } break; } } } sdl_lock_display(); if (width != self->width || height != self->height) { if (self->sdl_overlay != NULL) SDL_FreeYUVOverlay(self->sdl_overlay); self->sdl_overlay = NULL; } if (self->running && (!SDL_GetVideoSurface() || changed)) { // Force an overlay recreation if (self->sdl_overlay != NULL) SDL_FreeYUVOverlay(self->sdl_overlay); self->sdl_overlay = NULL; // open SDL window with video overlay, if possible pthread_mutex_lock(&mlt_sdl_mutex); SDL_Surface *screen = SDL_SetVideoMode(self->window_width, self->window_height, self->bpp, self->sdl_flags); if (consumer_get_dimensions(&self->window_width, &self->window_height)) screen = SDL_SetVideoMode(self->window_width, self->window_height, self->bpp, self->sdl_flags); pthread_mutex_unlock(&mlt_sdl_mutex); if (screen) { uint32_t color = mlt_properties_get_int(self->properties, "window_background"); SDL_FillRect(screen, NULL, color >> 8); SDL_Flip(screen); } } if (self->running) { // Determine window's new display aspect ratio double this_aspect = (double) self->window_width / self->window_height; // Get the display aspect ratio double display_ratio = mlt_properties_get_double(properties, "display_ratio"); // Determine frame's display aspect ratio double frame_aspect = mlt_frame_get_aspect_ratio(frame) * width / height; // Store the width and height received self->width = width; self->height = height; // If using hardware scaler if (mlt_properties_get(properties, "rescale") != NULL && !strcmp(mlt_properties_get(properties, "rescale"), "none")) { // Use hardware scaler to normalize display aspect ratio self->rect.w = frame_aspect / this_aspect * self->window_width; self->rect.h = self->window_height; if (self->rect.w > self->window_width) { self->rect.w = self->window_width; self->rect.h = this_aspect / frame_aspect * self->window_height; } } // Special case optimisation to negate odd effect of sample aspect ratio // not corresponding exactly with image resolution. else if ((int) (this_aspect * 1000) == (int) (display_ratio * 1000)) { self->rect.w = self->window_width; self->rect.h = self->window_height; } // Use hardware scaler to normalize sample aspect ratio else if (self->window_height * display_ratio > self->window_width) { self->rect.w = self->window_width; self->rect.h = self->window_width / display_ratio; } else { self->rect.w = self->window_height * display_ratio; self->rect.h = self->window_height; } self->rect.x = (self->window_width - self->rect.w) / 2; self->rect.y = (self->window_height - self->rect.h) / 2; self->rect.x -= self->rect.x % 2; mlt_properties_set_int(self->properties, "rect_x", self->rect.x); mlt_properties_set_int(self->properties, "rect_y", self->rect.y); mlt_properties_set_int(self->properties, "rect_w", self->rect.w); mlt_properties_set_int(self->properties, "rect_h", self->rect.h); SDL_SetClipRect(SDL_GetVideoSurface(), &self->rect); } if (self->running && SDL_GetVideoSurface() && self->sdl_overlay == NULL) { SDL_SetClipRect(SDL_GetVideoSurface(), &self->rect); self->sdl_overlay = SDL_CreateYUVOverlay(width, height, (vfmt == mlt_image_yuv422 ? SDL_YUY2_OVERLAY : SDL_IYUV_OVERLAY), SDL_GetVideoSurface()); } if (self->running && SDL_GetVideoSurface() && self->sdl_overlay != NULL) { self->buffer = self->sdl_overlay->pixels[0]; if (SDL_LockYUVOverlay(self->sdl_overlay) >= 0) { int size = mlt_image_format_size(vfmt, width, height, NULL); if (image != NULL) memcpy(self->buffer, image, size); SDL_UnlockYUVOverlay(self->sdl_overlay); SDL_DisplayYUVOverlay(self->sdl_overlay, &SDL_GetVideoSurface()->clip_rect); } } sdl_unlock_display(); mlt_cocoa_autorelease_close(pool); mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } else if (self->running) { vfmt = preview_format == mlt_image_none ? mlt_image_rgba : preview_format; if (!video_off) mlt_frame_get_image(frame, &image, &vfmt, &width, &height, 0); mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } return 0; } static void *video_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Obtain time of thread start struct timeval now; int64_t start = 0; int64_t elapsed = 0; struct timespec tm; mlt_frame next = NULL; mlt_properties properties = NULL; double speed = 0; // Get real time flag int real_time = mlt_properties_get_int(self->properties, "real_time"); // Get the current time gettimeofday(&now, NULL); // Determine start time start = (int64_t) now.tv_sec * 1000000 + now.tv_usec; while (self->running) { // Pop the next frame pthread_mutex_lock(&self->video_mutex); next = mlt_deque_pop_front(self->queue); while (next == NULL && self->running) { pthread_cond_wait(&self->video_cond, &self->video_mutex); next = mlt_deque_pop_front(self->queue); } pthread_mutex_unlock(&self->video_mutex); if (!self->running || next == NULL) break; // Get the properties properties = MLT_FRAME_PROPERTIES(next); // Get the speed of the frame speed = mlt_properties_get_double(properties, "_speed"); // Get the current time gettimeofday(&now, NULL); // Get the elapsed time elapsed = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - start; // See if we have to delay the display of the current frame if (mlt_properties_get_int(properties, "rendered") == 1 && self->running) { // Obtain the scheduled playout time int64_t scheduled = mlt_properties_get_int(properties, "playtime"); // Determine the difference between the elapsed time and the scheduled playout time int64_t difference = scheduled - elapsed; // Smooth playback a bit if (real_time && (difference > 20000 && speed == 1.0)) { tm.tv_sec = difference / 1000000; tm.tv_nsec = (difference % 1000000) * 500; nanosleep(&tm, NULL); } // Show current frame if not too old if (!real_time || (difference > -10000 || speed != 1.0 || mlt_deque_count(self->queue) < 2)) consumer_play_video(self, next); // If the queue is empty, recalculate start to allow build up again if (real_time && (mlt_deque_count(self->queue) == 0 && speed == 1.0)) { gettimeofday(&now, NULL); start = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - scheduled + 20000; } } else { static int dropped = 0; mlt_log_info(MLT_CONSUMER_SERVICE(&self->parent), "dropped video frame %d\n", ++dropped); } // This frame can now be closed mlt_frame_close(next); next = NULL; } if (next != NULL) mlt_frame_close(next); mlt_consumer_stopped(&self->parent); return NULL; } /** Threaded wrapper for pipe. */ static void *consumer_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Convenience functionality int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "terminate_on_pause"); int terminated = 0; // Video thread pthread_t thread; // internal initialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; int duration = 0; int64_t playtime = 0; struct timespec tm = {0, 100000}; // Loop until told not to while (self->running) { // Get a frame from the attached producer frame = !terminated ? mlt_consumer_rt_frame(consumer) : NULL; // Check for termination if (terminate_on_pause && frame) terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0; // Ensure that we have a frame if (frame) { // Play audio init_audio = consumer_play_audio(self, frame, init_audio, &duration); // Determine the start time now if (self->playing && init_video) { // Create the video thread pthread_create(&thread, NULL, video_thread, self); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "playtime", playtime); while (self->running && mlt_deque_count(self->queue) > 15) nanosleep(&tm, NULL); // Push this frame to the back of the queue pthread_mutex_lock(&self->video_mutex); if (self->is_purge) { mlt_frame_close(frame); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back(self->queue, frame); pthread_cond_broadcast(&self->video_cond); } pthread_mutex_unlock(&self->video_mutex); // Calculate the next playtime playtime += (duration * 1000); } else if (terminated) { if (init_video || mlt_deque_count(self->queue) == 0) break; else nanosleep(&tm, NULL); } } self->running = 0; // Unblock sdl_preview if (mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "put_mode") && mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "put_pending")) { frame = mlt_consumer_get_frame(consumer); if (frame) mlt_frame_close(frame); frame = NULL; } // Kill the video thread if (init_video == 0) { pthread_mutex_lock(&self->video_mutex); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); pthread_join(thread, NULL); } while (mlt_deque_count(self->queue)) mlt_frame_close(mlt_deque_pop_back(self->queue)); pthread_mutex_lock(&self->audio_mutex); self->audio_avail = 0; pthread_mutex_unlock(&self->audio_mutex); return NULL; } static int consumer_get_dimensions(int *width, int *height) { int changed = 0; // SDL windows manager structure SDL_SysWMinfo wm; // Specify the SDL Version SDL_VERSION(&wm.version); // Lock the display //sdl_lock_display(); #ifndef __APPLE__ // Get the wm structure if (SDL_GetWMInfo(&wm) == 1) { #ifndef _WIN32 // Check that we have the X11 wm if (wm.subsystem == SDL_SYSWM_X11) { // Get the SDL window Window window = wm.info.x11.window; // Get the display session Display *display = wm.info.x11.display; // Get the window attributes XWindowAttributes attr; XGetWindowAttributes(display, window, &attr); // Determine whether window has changed changed = *width != attr.width || *height != attr.height; // Return width and height *width = attr.width; *height = attr.height; } #endif } #endif // Unlock the display //sdl_unlock_display(); return changed; } /** Callback to allow override of the close method. */ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer ///mlt_consumer_stop( parent ); // Now clean up the rest mlt_consumer_close(parent); // Close the queue mlt_deque_close(self->queue); // Destroy mutexes pthread_mutex_destroy(&self->audio_mutex); pthread_cond_destroy(&self->audio_cond); // Finally clean up this free(self); } mlt-7.38.0/src/modules/sdl/consumer_sdl.yml000664 000000 000000 00000002726 15172202314 020632 0ustar00rootroot000000 000000 schema_version: 7.0 type: consumer identifier: sdl title: SDL Fast YUV (*DEPRECATED*) version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > Deprecated: use "sdl2" instead. Simple DirectMedia Layer audio and video output module. parameters: - identifier: resolution title: Resolution type: string description: The size of the window as WxH pixels. argument: yes required: no - identifier: volume title: Volume type: float description: Audio level factor. mutable: yes - identifier: video_off title: Video off type: boolean description: Disable video output mutable: yes default: 0 widget: checkbox - identifier: audio_off title: Audio off type: boolean description: Disable audio output mutable: yes default: 0 widget: checkbox - identifier: audio_buffer title: Audio buffer type: integer description: Size of the SDL audio buffer. mutable: yes default: 2048 minimum: 128 - identifier: scrub_audio title: Audio scrubbing type: boolean description: Play sound even when the speed is not normal. mutable: yes default: 1 widget: checkbox - identifier: terminate_on_pause title: Stop automatically type: boolean description: > Whether to stop playback at the end of the producer or when playback is paused. default: 0 widget: checkbox mlt-7.38.0/src/modules/sdl/consumer_sdl_audio.c000664 000000 000000 00000054263 15172202314 021437 0ustar00rootroot000000 000000 /* * consumer_sdl_audio.c -- A Simple DirectMedia Layer audio-only consumer * Copyright (C) 2009-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include extern pthread_mutex_t mlt_sdl_mutex; /** This classes definition. */ typedef struct consumer_sdl_s *consumer_sdl; struct consumer_sdl_s { struct mlt_consumer_s parent; mlt_properties properties; mlt_deque queue; pthread_t thread; int joined; atomic_int running; uint8_t audio_buffer[4096 * 10]; int audio_avail; pthread_mutex_t audio_mutex; pthread_cond_t audio_cond; pthread_mutex_t video_mutex; pthread_cond_t video_cond; atomic_int playing; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; int is_purge; }; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_purge(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer self, mlt_event_data); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_sdl_audio_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object consumer_sdl self = calloc(1, sizeof(struct consumer_sdl_s)); // If no malloc'd and consumer init ok if (self != NULL && mlt_consumer_init(&self->parent, self, profile) == 0) { // Create the queue self->queue = mlt_deque_init(); // Get the parent consumer object mlt_consumer parent = &self->parent; // We have stuff to clean up, so override the close method parent->close = consumer_close; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE(parent); self->properties = MLT_SERVICE_PROPERTIES(service); // Set the default volume mlt_properties_set_double(self->properties, "volume", 1.0); // This is the initialisation of the consumer pthread_mutex_init(&self->audio_mutex, NULL); pthread_cond_init(&self->audio_cond, NULL); pthread_mutex_init(&self->video_mutex, NULL); pthread_cond_init(&self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set(self->properties, "rescale", "nearest"); mlt_properties_set(self->properties, "consumer.deinterlacer", "onefield"); mlt_properties_set_int(self->properties, "top_field_first", -1); // Default buffer for low latency mlt_properties_set_int(self->properties, "buffer", 1); // Default audio buffer mlt_properties_set_int(self->properties, "audio_buffer", 2048); #if defined(_WIN32) && SDL_MAJOR_VERSION == 2 mlt_properties_set(self->properties, "audio_driver", "DirectSound"); #endif // Ensure we don't join on a non-running object self->joined = 1; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Initialize the refresh handler pthread_cond_init(&self->refresh_cond, NULL); pthread_mutex_init(&self->refresh_mutex, NULL); mlt_events_listen(MLT_CONSUMER_PROPERTIES(parent), self, "property-changed", (mlt_listener) consumer_refresh_cb); // Return the consumer produced return parent; } // malloc or consumer init failed free(self); // Indicate failure return NULL; } static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer parent, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "refresh")) { consumer_sdl self = parent->child; pthread_mutex_lock(&self->refresh_mutex); if (self->refresh_count < 2) self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1; pthread_cond_broadcast(&self->refresh_cond); pthread_mutex_unlock(&self->refresh_mutex); } } int consumer_start(mlt_consumer parent) { consumer_sdl self = parent->child; if (!self->running) { consumer_stop(parent); mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); char *audio_driver = mlt_properties_get(properties, "audio_driver"); char *audio_device = mlt_properties_get(properties, "audio_device"); if (audio_driver && strcmp(audio_driver, "")) setenv("SDL_AUDIODRIVER", audio_driver, 1); if (audio_device && strcmp(audio_device, "")) setenv("AUDIODEV", audio_device, 1); pthread_mutex_lock(&mlt_sdl_mutex); int ret = SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE); pthread_mutex_unlock(&mlt_sdl_mutex); if (ret < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError()); return -1; } self->running = 1; self->joined = 0; pthread_create(&self->thread, NULL, consumer_thread, self); } return 0; } int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; if (self->running && !self->joined) { // Kill the thread and clean up self->joined = 1; self->running = 0; // Unlatch the consumer thread pthread_mutex_lock(&self->refresh_mutex); pthread_cond_broadcast(&self->refresh_cond); pthread_mutex_unlock(&self->refresh_mutex); // Cleanup the main thread #ifndef _WIN32 if (self->thread) #endif pthread_join(self->thread, NULL); // Unlatch the video thread pthread_mutex_lock(&self->video_mutex); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); // Unlatch the audio callback pthread_mutex_lock(&self->audio_mutex); pthread_cond_broadcast(&self->audio_cond); pthread_mutex_unlock(&self->audio_mutex); SDL_QuitSubSystem(SDL_INIT_AUDIO); } return 0; } int consumer_is_stopped(mlt_consumer parent) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge(mlt_consumer parent) { consumer_sdl self = parent->child; if (self->running) { pthread_mutex_lock(&self->video_mutex); mlt_frame frame = MLT_FRAME(mlt_deque_peek_back(self->queue)); // When playing rewind or fast forward then we need to keep one // frame in the queue to prevent playback stalling. double speed = frame ? mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") : 0; int n = (speed == 0.0 || speed == 1.0) ? 0 : 1; while (mlt_deque_count(self->queue) > n) mlt_frame_close(mlt_deque_pop_back(self->queue)); self->is_purge = 1; pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); } } static void sdl_fill_audio(void *udata, uint8_t *stream, int len) { consumer_sdl self = udata; // Get the volume double volume = mlt_properties_get_double(self->properties, "volume"); // Wipe the stream first memset(stream, 0, len); pthread_mutex_lock(&self->audio_mutex); if (self->audio_avail >= len) { // Place in the audio buffer if (volume != 1.0) SDL_MixAudio(stream, self->audio_buffer, len, (int) ((float) SDL_MIX_MAXVOLUME * volume)); else memcpy(stream, self->audio_buffer, len); // Remove len from the audio available self->audio_avail -= len; // Remove the samples memmove(self->audio_buffer, self->audio_buffer + len, self->audio_avail); } else { // Mix the audio SDL_MixAudio(stream, self->audio_buffer, self->audio_avail, (int) ((float) SDL_MIX_MAXVOLUME * volume)); // No audio left self->audio_avail = 0; } // We're definitely playing now self->playing = 1; pthread_cond_broadcast(&self->audio_cond); pthread_mutex_unlock(&self->audio_mutex); } static int consumer_play_audio(consumer_sdl self, mlt_frame frame, int init_audio, int *duration) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int(properties, "channels"); int dest_channels = channels; int frequency = mlt_properties_get_int(properties, "frequency"); int scrub = mlt_properties_get_int(properties, "scrub_audio"); static int counter = 0; int samples = mlt_audio_calculate_frame_samples(mlt_properties_get_double(self->properties, "fps"), frequency, counter++); int16_t *pcm; mlt_frame_get_audio(frame, (void **) &pcm, &afmt, &frequency, &channels, &samples); *duration = ((samples * 1000) / frequency); pcm += mlt_properties_get_int(properties, "audio_offset"); if (mlt_properties_get_int(properties, "audio_off")) { self->playing = 1; init_audio = 1; return init_audio; } if (init_audio == 1) { SDL_AudioSpec request; SDL_AudioSpec got; int audio_buffer = mlt_properties_get_int(properties, "audio_buffer"); // specify audio format memset(&request, 0, sizeof(SDL_AudioSpec)); self->playing = 0; request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = dest_channels; request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *) self; if (SDL_OpenAudio(&request, &got) != 0) { mlt_log_error(MLT_CONSUMER_SERVICE(self), "SDL failed to open audio: %s\n", SDL_GetError()); init_audio = 2; } else if (got.size != 0) { SDL_PauseAudio(0); init_audio = 0; } } if (init_audio == 0) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int samples_copied = 0; int dst_stride = dest_channels * sizeof(*pcm); pthread_mutex_lock(&self->audio_mutex); while (self->running && samples_copied < samples) { int sample_space = (sizeof(self->audio_buffer) - self->audio_avail) / dst_stride; while (self->running && sample_space == 0) { pthread_cond_wait(&self->audio_cond, &self->audio_mutex); sample_space = (sizeof(self->audio_buffer) - self->audio_avail) / dst_stride; } if (self->running) { int samples_to_copy = samples - samples_copied; if (samples_to_copy > sample_space) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if (scrub || mlt_properties_get_double(properties, "_speed") == 1) { if (channels == dest_channels) { memcpy(&self->audio_buffer[self->audio_avail], pcm, dst_bytes); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t *) &self->audio_buffer[self->audio_avail]; int i = samples_to_copy + 1; while (--i) { memcpy(dest, pcm, dst_stride); pcm += channels; dest += dest_channels; } } } else { memset(&self->audio_buffer[self->audio_avail], 0, dst_bytes); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast(&self->audio_cond); } pthread_mutex_unlock(&self->audio_mutex); } else { self->playing = 1; } return init_audio; } static int consumer_play_video(consumer_sdl self, mlt_frame frame) { // Get the properties of this consumer mlt_properties properties = self->properties; mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); return 0; } static void *video_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Obtain time of thread start struct timeval now; int64_t start = 0; int64_t elapsed = 0; struct timespec tm; mlt_frame next = NULL; mlt_properties properties = NULL; double speed = 0; // Get real time flag int real_time = mlt_properties_get_int(self->properties, "real_time"); // Get the current time gettimeofday(&now, NULL); // Determine start time start = (int64_t) now.tv_sec * 1000000 + now.tv_usec; while (self->running) { // Pop the next frame pthread_mutex_lock(&self->video_mutex); next = mlt_deque_pop_front(self->queue); while (next == NULL && self->running) { pthread_cond_wait(&self->video_cond, &self->video_mutex); next = mlt_deque_pop_front(self->queue); } pthread_mutex_unlock(&self->video_mutex); if (!self->running || next == NULL) break; // Get the properties properties = MLT_FRAME_PROPERTIES(next); // Get the speed of the frame speed = mlt_properties_get_double(properties, "_speed"); // Get the current time gettimeofday(&now, NULL); // Get the elapsed time elapsed = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - start; // See if we have to delay the display of the current frame if (mlt_properties_get_int(properties, "rendered") == 1) { // Obtain the scheduled playout time int64_t scheduled = mlt_properties_get_int(properties, "playtime"); // Determine the difference between the elapsed time and the scheduled playout time int64_t difference = scheduled - elapsed; // Smooth playback a bit if (real_time && (difference > 20000 && speed == 1.0)) { tm.tv_sec = difference / 1000000; tm.tv_nsec = (difference % 1000000) * 500; nanosleep(&tm, NULL); } // Show current frame if not too old if (!real_time || (difference > -10000 || speed != 1.0 || mlt_deque_count(self->queue) < 2)) consumer_play_video(self, next); // If the queue is empty, recalculate start to allow build up again if (real_time && (mlt_deque_count(self->queue) == 0 && speed == 1.0)) { gettimeofday(&now, NULL); start = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - scheduled + 20000; } } // This frame can now be closed mlt_frame_close(next); next = NULL; } // This consumer is stopping. But audio has already been played for all // the frames in the queue. Spit out all the frames so that the display has // the option to catch up with the audio. if (next != NULL) { consumer_play_video(self, next); mlt_frame_close(next); next = NULL; } while (mlt_deque_count(self->queue) > 0) { next = mlt_deque_pop_front(self->queue); consumer_play_video(self, next); mlt_frame_close(next); next = NULL; } mlt_consumer_stopped(&self->parent); return NULL; } /** Threaded wrapper for pipe. */ static void *consumer_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES(consumer); // Video thread pthread_t thread; // internal initialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; mlt_properties properties = NULL; int duration = 0; int64_t playtime = 0; struct timespec tm = {0, 100000}; // int last_position = -1; pthread_mutex_lock(&self->refresh_mutex); self->refresh_count = 0; pthread_mutex_unlock(&self->refresh_mutex); // Loop until told not to while (self->running) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame(consumer); // Ensure that we have a frame if (frame) { // Get the frame properties properties = MLT_FRAME_PROPERTIES(frame); // Get the speed of the frame double speed = mlt_properties_get_double(properties, "_speed"); // Clear refresh mlt_events_block(consumer_props, consumer_props); mlt_properties_set_int(consumer_props, "refresh", 0); mlt_events_unblock(consumer_props, consumer_props); // Play audio init_audio = consumer_play_audio(self, frame, init_audio, &duration); // Determine the start time now if (self->playing && init_video) { // Create the video thread pthread_create(&thread, NULL, video_thread, self); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame mlt_properties_set_int(properties, "playtime", playtime); while (self->running && speed != 0 && mlt_deque_count(self->queue) > 15) nanosleep(&tm, NULL); // Push this frame to the back of the video queue if (self->running && speed) { pthread_mutex_lock(&self->video_mutex); if (self->is_purge && speed == 1.0) { mlt_frame_close(frame); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back(self->queue, frame); pthread_cond_broadcast(&self->video_cond); } pthread_mutex_unlock(&self->video_mutex); // Calculate the next playtime playtime += (duration * 1000); } else if (self->running) { pthread_mutex_lock(&self->refresh_mutex); consumer_play_video(self, frame); mlt_frame_close(frame); frame = NULL; self->refresh_count--; if (self->refresh_count <= 0) { pthread_cond_wait(&self->refresh_cond, &self->refresh_mutex); } pthread_mutex_unlock(&self->refresh_mutex); } // Optimisation to reduce latency if (speed == 1.0) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else if (speed == 0.0) { mlt_consumer_purge(consumer); // last_position = -1; } } } // Kill the video thread if (init_video == 0) { pthread_mutex_lock(&self->video_mutex); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); pthread_join(thread, NULL); } if (frame) { // The video thread has cleared out the queue. But the audio was played // for this frame. So play the video before stopping so the display has // the option to catch up with the audio. consumer_play_video(self, frame); mlt_frame_close(frame); frame = NULL; } pthread_mutex_lock(&self->audio_mutex); self->audio_avail = 0; pthread_mutex_unlock(&self->audio_mutex); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer mlt_consumer_stop(parent); // Now clean up the rest mlt_consumer_close(parent); // Close the queue mlt_deque_close(self->queue); // Destroy mutexes pthread_mutex_destroy(&self->audio_mutex); pthread_cond_destroy(&self->audio_cond); pthread_mutex_destroy(&self->video_mutex); pthread_cond_destroy(&self->video_cond); pthread_mutex_destroy(&self->refresh_mutex); pthread_cond_destroy(&self->refresh_cond); // Finally clean up this free(self); } mlt-7.38.0/src/modules/sdl/consumer_sdl_audio.yml000664 000000 000000 00000002007 15172202314 022003 0ustar00rootroot000000 000000 schema_version: 0.1 type: consumer identifier: sdl_audio title: SDL Audio Only (*DEPRECATED*) version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: > Deprecate: use "sdl2_audio" instead. Simple DirectMedia Layer audio only output module. parameters: - identifier: volume title: Volume type: float description: Audio level factor. mutable: yes - identifier: audio_off title: Audio off type: integer description: If 1, disable audio output mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: audio_buffer title: Audio buffer type: integer description: Size of the sdl audio buffer. mutable: yes default: 2048 minimum: 128 - identifier: scrub_audio title: Audio scrubbing type: integer description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-7.38.0/src/modules/sdl/consumer_sdl_osx.h000664 000000 000000 00000002237 15172202314 021146 0ustar00rootroot000000 000000 /* * consumer_sdl_osx.m -- An OS X compatibility shim for SDL * Copyright (C) 2010-2014 Meltytech, LLC * Author: Dan Dennedy * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CONSUMER_SDL_OSX_H_ #define _CONSUMER_SDL_OSX_H_ #ifdef __APPLE__ void *mlt_cocoa_autorelease_init(); void mlt_cocoa_autorelease_close(void *); #else static inline void *mlt_cocoa_autorelease_init() { return NULL; } static inline void mlt_cocoa_autorelease_close(void *p) {} #endif #endif mlt-7.38.0/src/modules/sdl/consumer_sdl_osx.m000664 000000 000000 00000005633 15172202314 021156 0ustar00rootroot000000 000000 /* * consumer_sdl_osx.m -- An OS X compatibility shim for SDL * Copyright (C) 2010-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #import void* mlt_cocoa_autorelease_init() { return [[NSAutoreleasePool alloc] init]; } void mlt_cocoa_autorelease_close( void* p ) { NSAutoreleasePool* pool = ( NSAutoreleasePool* ) p; [pool release]; } #if 0 /* The code below is not used at this time - could not get it to work, but * it is based on code from gruntster on the avidemux project team. */ #import static NSWindow* nsWindow = nil; static NSQuickDrawView* nsView = nil; void sdl_cocoa_init(void* parent, int x, int y, int width, int height) { NSRect contentRect; contentRect = NSMakeRect(x, y, width, height); if (!nsWindow) { // initWithWindowRef always returns the same result for the same WindowRef nsWindow = [[NSWindow alloc] initWithWindowRef:(WindowRef)parent]; [nsWindow setContentView:[[[NSView alloc] initWithFrame:contentRect] autorelease]]; } if (!nsView) { nsView = [[NSQuickDrawView alloc] initWithFrame:contentRect]; [[nsWindow contentView] addSubview:nsView]; [nsView release]; [nsWindow orderOut:nil]; // very important, otherwise window won't be placed correctly on repeat showings [nsWindow orderFront:nil]; } else { [nsView setFrame:contentRect]; [[nsWindow contentView] setFrame:contentRect]; } // finally, set SDL environment variables with all this nonsense char SDL_windowhack[20]; sprintf(SDL_windowhack, "%d", (int)nsWindow); setenv("SDL_NSWindowPointer", SDL_windowhack, 1); sprintf(SDL_windowhack,"%d", (int)nsView); setenv("SDL_NSQuickDrawViewPointer", SDL_windowhack, 1); } void sdl_cocoa_close(void) { if (nsWindow) { // Reference count cannot fall below 2 because SDL releases the window when closing // and again when reinitialising (even though this is our own window...). if ([nsWindow retainCount] > 2) [nsWindow release]; // SDL takes care of all the destroying...a little too much, so make sure our Carbon // window is still displayed (via its Cocoa wrapper) [nsWindow makeKeyAndOrderFront:nil]; } } #endif mlt-7.38.0/src/modules/sdl/consumer_sdl_osx_hack.h000664 000000 000000 00000002106 15172202314 022127 0ustar00rootroot000000 000000 /* * Purpose: A dummy thread object to inform Cocoa that it needs to be thread safe. * Author: Zachary Drew * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #import @interface DummyThread : NSObject - init; - (void)startThread:(id)arg; @end @implementation DummyThread - init { [super init]; return self; } - (void)startThread:(id)arg { return; } @end mlt-7.38.0/src/modules/sdl/consumer_sdl_preview.c000664 000000 000000 00000052447 15172202314 022021 0ustar00rootroot000000 000000 /* * consumer_sdl_preview.c -- A Simple DirectMedia Layer consumer * Copyright (C) 2004-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include extern pthread_mutex_t mlt_sdl_mutex; typedef struct consumer_sdl_s *consumer_sdl; struct consumer_sdl_s { struct mlt_consumer_s parent; mlt_consumer active; int ignore_change; mlt_consumer play; mlt_consumer still; pthread_t thread; int joined; int running; int sdl_flags; double last_speed; mlt_position last_position; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; }; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_purge(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); static void consumer_frame_show_cb(mlt_consumer sdl, mlt_consumer self, mlt_event_data); static void consumer_sdl_event_cb(mlt_consumer sdl, mlt_consumer self, mlt_event_data); static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer self, mlt_event_data); mlt_consumer consumer_sdl_preview_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { consumer_sdl self = calloc(1, sizeof(struct consumer_sdl_s)); if (self != NULL && mlt_consumer_init(&self->parent, self, profile) == 0) { // Get the parent consumer object mlt_consumer parent = &self->parent; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); // Get the width and height int width = mlt_properties_get_int(properties, "width"); int height = mlt_properties_get_int(properties, "height"); // Process actual param if (arg == NULL || sscanf(arg, "%dx%d", &width, &height) == 2) { mlt_properties_set_int(properties, "width", width); mlt_properties_set_int(properties, "height", height); } // Create child consumers self->play = mlt_factory_consumer(profile, "sdl", arg); self->still = mlt_factory_consumer(profile, "sdl_still", arg); mlt_properties_set(properties, "rescale", "nearest"); mlt_properties_set(properties, "consumer.deinterlacer", "onefield"); mlt_properties_set_int(properties, "prefill", 1); mlt_properties_set_int(properties, "top_field_first", -1); parent->close = consumer_close; parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; self->joined = 1; mlt_events_listen(MLT_CONSUMER_PROPERTIES(self->play), self, "consumer-frame-show", (mlt_listener) consumer_frame_show_cb); mlt_events_listen(MLT_CONSUMER_PROPERTIES(self->still), self, "consumer-frame-show", (mlt_listener) consumer_frame_show_cb); mlt_events_listen(MLT_CONSUMER_PROPERTIES(self->play), self, "consumer-sdl-event", (mlt_listener) consumer_sdl_event_cb); mlt_events_listen(MLT_CONSUMER_PROPERTIES(self->still), self, "consumer-sdl-event", (mlt_listener) consumer_sdl_event_cb); pthread_cond_init(&self->refresh_cond, NULL); pthread_mutex_init(&self->refresh_mutex, NULL); mlt_events_listen(MLT_CONSUMER_PROPERTIES(parent), self, "property-changed", (mlt_listener) consumer_refresh_cb); mlt_events_register(properties, "consumer-sdl-paused"); return parent; } free(self); return NULL; } void consumer_frame_show_cb(mlt_consumer sdl, mlt_consumer parent, mlt_event_data event_data) { mlt_frame frame = mlt_event_data_to_frame(event_data); consumer_sdl self = parent->child; if (frame && self) { self->last_speed = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed"); self->last_position = mlt_frame_get_position(frame); mlt_events_fire(MLT_CONSUMER_PROPERTIES(parent), "consumer-frame-show", event_data); } } static void consumer_sdl_event_cb(mlt_consumer sdl, mlt_consumer parent, mlt_event_data event_data) { mlt_events_fire(MLT_CONSUMER_PROPERTIES(parent), "consumer-sdl-event", event_data); } static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer parent, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "refresh")) { consumer_sdl self = parent->child; pthread_mutex_lock(&self->refresh_mutex); self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1; pthread_cond_broadcast(&self->refresh_cond); pthread_mutex_unlock(&self->refresh_mutex); } } static int consumer_start(mlt_consumer parent) { consumer_sdl self = parent->child; if (!self->running) { // properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); mlt_properties play = MLT_CONSUMER_PROPERTIES(self->play); mlt_properties still = MLT_CONSUMER_PROPERTIES(self->still); char *window_id = mlt_properties_get(properties, "window_id"); char *audio_driver = mlt_properties_get(properties, "audio_driver"); char *video_driver = mlt_properties_get(properties, "video_driver"); char *audio_device = mlt_properties_get(properties, "audio_device"); char *output_display = mlt_properties_get(properties, "output_display"); int progressive = mlt_properties_get_int(properties, "progressive") | mlt_properties_get_int(properties, "deinterlace"); consumer_stop(parent); self->running = 1; self->joined = 0; self->last_speed = 1; if (output_display != NULL) setenv("DISPLAY", output_display, 1); if (window_id != NULL) setenv("SDL_WINDOWID", window_id, 1); if (video_driver != NULL) setenv("SDL_VIDEODRIVER", video_driver, 1); if (audio_driver != NULL) setenv("SDL_AUDIODRIVER", audio_driver, 1); if (audio_device != NULL) setenv("AUDIODEV", audio_device, 1); pthread_mutex_lock(&mlt_sdl_mutex); int ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE); pthread_mutex_unlock(&mlt_sdl_mutex); if (ret < 0) { fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); return -1; } SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); SDL_EnableUNICODE(1); // Pass properties down mlt_properties_set_data(play, "transport_producer", mlt_properties_get_data(properties, "transport_producer", NULL), 0, NULL, NULL); mlt_properties_set_data(still, "transport_producer", mlt_properties_get_data(properties, "transport_producer", NULL), 0, NULL, NULL); mlt_properties_set_data(play, "transport_callback", mlt_properties_get_data(properties, "transport_callback", NULL), 0, NULL, NULL); mlt_properties_set_data(still, "transport_callback", mlt_properties_get_data(properties, "transport_callback", NULL), 0, NULL, NULL); mlt_properties_set_int(play, "progressive", progressive); mlt_properties_set_int(still, "progressive", progressive); mlt_properties_pass_list( play, properties, "deinterlacer,deinterlace_method,resize,rescale,width,height,aspect_ratio,display_" "ratio,preview_off,preview_format,window_background" ",top_field_first,volume,real_time,buffer,prefill,audio_off,frequency,drop_max"); mlt_properties_pass_list( still, properties, "deinterlacer,deinterlace_method,resize,rescale,width,height,aspect_ratio,display_" "ratio,preview_off,preview_format,window_background" ",top_field_first"); mlt_properties_pass(play, properties, "play."); mlt_properties_pass(still, properties, "still."); mlt_properties_set_data(play, "app_lock", mlt_properties_get_data(properties, "app_lock", NULL), 0, NULL, NULL); mlt_properties_set_data(still, "app_lock", mlt_properties_get_data(properties, "app_lock", NULL), 0, NULL, NULL); mlt_properties_set_data(play, "app_unlock", mlt_properties_get_data(properties, "app_unlock", NULL), 0, NULL, NULL); mlt_properties_set_data(still, "app_unlock", mlt_properties_get_data(properties, "app_unlock", NULL), 0, NULL, NULL); mlt_properties_set_int(play, "put_mode", 1); mlt_properties_set_int(still, "put_mode", 1); mlt_properties_set_int(play, "terminate_on_pause", 1); // Start the still producer just to initialise the gui mlt_consumer_start(self->still); self->active = self->still; // Inform child consumers that we control the sdl mlt_properties_set_int(play, "sdl_started", 1); mlt_properties_set_int(still, "sdl_started", 1); pthread_create(&self->thread, NULL, consumer_thread, self); } return 0; } static int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; if (self->joined == 0) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); int app_locked = mlt_properties_get_int(properties, "app_locked"); void (*lock)(void) = mlt_properties_get_data(properties, "app_lock", NULL); void (*unlock)(void) = mlt_properties_get_data(properties, "app_unlock", NULL); if (app_locked && unlock) unlock(); // Kill the thread and clean up self->running = 0; pthread_mutex_lock(&self->refresh_mutex); pthread_cond_broadcast(&self->refresh_cond); pthread_mutex_unlock(&self->refresh_mutex); #ifndef _WIN32 if (self->thread) #endif pthread_join(self->thread, NULL); self->joined = 1; if (app_locked && lock) lock(); pthread_mutex_lock(&mlt_sdl_mutex); SDL_Quit(); pthread_mutex_unlock(&mlt_sdl_mutex); } return 0; } static int consumer_is_stopped(mlt_consumer parent) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge(mlt_consumer parent) { consumer_sdl self = parent->child; if (self->running) mlt_consumer_purge(self->play); } static void *consumer_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // internal initialization mlt_frame frame = NULL; int last_position = -1; int eos = 0; int eos_threshold = 20; if (self->play) eos_threshold = eos_threshold + mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(self->play), "buffer"); // Determine if the application is dealing with the preview int preview_off = mlt_properties_get_int(properties, "preview_off"); pthread_mutex_lock(&self->refresh_mutex); self->refresh_count = 0; pthread_mutex_unlock(&self->refresh_mutex); // Loop until told not to while (self->running) { // Get a frame from the attached producer frame = mlt_consumer_get_frame(consumer); // Ensure that we have a frame if (self->running && frame != NULL) { // Get the speed of the frame double speed = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed"); // Lock during the operation mlt_service_lock(MLT_CONSUMER_SERVICE(consumer)); // Get refresh request for the current frame int refresh = mlt_properties_get_int(properties, "refresh"); // Decrement refresh and clear changed mlt_events_block(properties, properties); mlt_properties_set_int(properties, "refresh", 0); mlt_events_unblock(properties, properties); // Unlock after the operation mlt_service_unlock(MLT_CONSUMER_SERVICE(consumer)); // Set the changed property on this frame for the benefit of still mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "refresh", refresh); // Make sure the recipient knows that this frame isn't really rendered mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "rendered", 0); // Optimisation to reduce latency if (speed == 1.0) { if (last_position != -1 && last_position + 1 != mlt_frame_get_position(frame)) mlt_consumer_purge(self->play); last_position = mlt_frame_get_position(frame); } else { //mlt_consumer_purge( self->play ); last_position = -1; } // If we aren't playing normally, then use the still if (speed != 1) { mlt_producer producer = MLT_PRODUCER( mlt_service_get_producer(MLT_CONSUMER_SERVICE(consumer))); mlt_position duration = producer ? mlt_producer_get_playtime(producer) : -1; int pause = 0; #ifndef SKIP_WAIT_EOS if (self->active == self->play) { // Do not interrupt the play consumer near the end if (duration - self->last_position > eos_threshold) { // Get a new frame at the sought position mlt_frame_close(frame); if (producer) mlt_producer_seek(producer, self->last_position); frame = mlt_consumer_get_frame(consumer); pause = 1; } else { // Send frame with speed 0 to stop it if (frame && !mlt_consumer_is_stopped(self->play)) { mlt_consumer_put_frame(self->play, frame); frame = NULL; eos = 1; } // Check for end of stream if (mlt_consumer_is_stopped(self->play)) { // Stream has ended mlt_log_verbose(MLT_CONSUMER_SERVICE(consumer), "END OF STREAM\n"); pause = 1; eos = 0; // reset eos indicator } else { // Prevent a tight busy loop struct timespec tm = {0, 100000L}; // 100 usec nanosleep(&tm, NULL); } } } #else pause = self->active == self->play; #endif if (pause) { // Start the still consumer if (!mlt_consumer_is_stopped(self->play)) mlt_consumer_stop(self->play); self->last_speed = speed; self->active = self->still; self->ignore_change = 0; mlt_consumer_start(self->still); } // Send the frame to the active child if (frame && !eos) { mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "refresh", 1); if (self->active) mlt_consumer_put_frame(self->active, frame); } if (pause && speed == 0.0) { mlt_events_fire(properties, "consumer-sdl-paused", mlt_event_data_none()); } } // Allow a little grace time before switching consumers on speed changes else if (self->ignore_change-- > 0 && self->active != NULL && !mlt_consumer_is_stopped(self->active)) { mlt_consumer_put_frame(self->active, frame); } // Otherwise use the normal player else { if (!mlt_consumer_is_stopped(self->still)) mlt_consumer_stop(self->still); if (mlt_consumer_is_stopped(self->play)) { self->last_speed = speed; self->active = self->play; self->ignore_change = 0; mlt_consumer_start(self->play); } if (self->play) mlt_consumer_put_frame(self->play, frame); } // Copy the rectangle info from the active consumer if (self->running && preview_off == 0 && self->active) { mlt_properties active = MLT_CONSUMER_PROPERTIES(self->active); mlt_service_lock(MLT_CONSUMER_SERVICE(consumer)); mlt_properties_set_int(properties, "rect_x", mlt_properties_get_int(active, "rect_x")); mlt_properties_set_int(properties, "rect_y", mlt_properties_get_int(active, "rect_y")); mlt_properties_set_int(properties, "rect_w", mlt_properties_get_int(active, "rect_w")); mlt_properties_set_int(properties, "rect_h", mlt_properties_get_int(active, "rect_h")); mlt_service_unlock(MLT_CONSUMER_SERVICE(consumer)); } if (self->active == self->still) { pthread_mutex_lock(&self->refresh_mutex); if (self->running && speed == 0 && self->refresh_count <= 0) { mlt_events_fire(properties, "consumer-sdl-paused", mlt_event_data_none()); pthread_cond_wait(&self->refresh_cond, &self->refresh_mutex); } self->refresh_count--; pthread_mutex_unlock(&self->refresh_mutex); } } else { if (frame) mlt_frame_close(frame); mlt_consumer_put_frame(self->active, NULL); self->running = 0; } } if (self->play) mlt_consumer_stop(self->play); if (self->still) mlt_consumer_stop(self->still); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer mlt_consumer_stop(parent); // Close the child consumers mlt_consumer_close(self->play); mlt_consumer_close(self->still); // Now clean up the rest mlt_consumer_close(parent); // Finally clean up self free(self); } mlt-7.38.0/src/modules/sdl/consumer_sdl_preview.yml000664 000000 000000 00000000302 15172202314 022357 0ustar00rootroot000000 000000 schema_version: 0.1 type: consumer identifier: sdl title: SDL (*DEPRECATED*) version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video mlt-7.38.0/src/modules/sdl/consumer_sdl_still.c000664 000000 000000 00000051002 15172202314 021451 0ustar00rootroot000000 000000 /* * consumer_sdl_still.c -- A Simple DirectMedia Layer consumer * Copyright (C) 2003-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "consumer_sdl_osx.h" extern pthread_mutex_t mlt_sdl_mutex; /** This classes definition. */ typedef struct consumer_sdl_s *consumer_sdl; struct consumer_sdl_s { struct mlt_consumer_s parent; mlt_properties properties; pthread_t thread; int joined; atomic_int running; int window_width; int window_height; int width; int height; atomic_int playing; int sdl_flags; SDL_Rect rect; uint8_t *buffer; int last_position; mlt_producer last_producer; }; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); static int consumer_get_dimensions(int *width, int *height); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_sdl_still_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object consumer_sdl this = calloc(1, sizeof(struct consumer_sdl_s)); // If no malloc'd and consumer init ok if (this != NULL && mlt_consumer_init(&this->parent, this, profile) == 0) { // Get the parent consumer object mlt_consumer parent = &this->parent; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE(parent); this->properties = MLT_SERVICE_PROPERTIES(service); // We have stuff to clean up, so override the close method parent->close = consumer_close; // Default scaler (for now we'll use nearest) mlt_properties_set(this->properties, "rescale", "nearest"); // We're always going to run this in non-realtime mode mlt_properties_set(this->properties, "real_time", "0"); // Ensure we don't join on a non-running object this->joined = 1; // process actual param if (arg == NULL || sscanf(arg, "%dx%d", &this->width, &this->height) != 2) { this->width = mlt_properties_get_int(this->properties, "width"); this->height = mlt_properties_get_int(this->properties, "height"); } else { mlt_properties_set_int(this->properties, "width", this->width); mlt_properties_set_int(this->properties, "height", this->height); } // Set the sdl flags this->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE | SDL_DOUBLEBUF; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; // Register specific events mlt_events_register(this->properties, "consumer-sdl-event"); // Return the consumer produced return parent; } // malloc or consumer init failed free(this); // Indicate failure return NULL; } static int consumer_start(mlt_consumer parent) { consumer_sdl this = parent->child; if (!this->running) { int preview_off = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "preview_off"); int sdl_started = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "sdl_started"); consumer_stop(parent); this->last_position = -1; this->running = 1; this->joined = 0; // Allow the user to force resizing to window size this->width = mlt_properties_get_int(this->properties, "width"); this->height = mlt_properties_get_int(this->properties, "height"); // Default window size double display_ratio = mlt_properties_get_double(this->properties, "display_ratio"); this->window_width = (double) this->height * display_ratio + 0.5; this->window_height = this->height; if (sdl_started == 0 && preview_off == 0) { pthread_mutex_lock(&mlt_sdl_mutex); int ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE); pthread_mutex_unlock(&mlt_sdl_mutex); if (ret < 0) { fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); return -1; } SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); SDL_EnableUNICODE(1); } pthread_mutex_lock(&mlt_sdl_mutex); if (!SDL_GetVideoSurface() && preview_off == 0) SDL_SetVideoMode(this->window_width, this->window_height, 0, this->sdl_flags); pthread_mutex_unlock(&mlt_sdl_mutex); pthread_create(&this->thread, NULL, consumer_thread, this); } return 0; } static int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_sdl this = parent->child; if (this->joined == 0) { int preview_off = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "preview_off"); int sdl_started = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "sdl_started"); // Kill the thread and clean up this->running = 0; pthread_join(this->thread, NULL); this->joined = 1; if (sdl_started == 0 && preview_off == 0) { pthread_mutex_lock(&mlt_sdl_mutex); SDL_Quit(); pthread_mutex_unlock(&mlt_sdl_mutex); } } return 0; } static int consumer_is_stopped(mlt_consumer parent) { consumer_sdl this = parent->child; return !this->running; } static int sdl_lock_display() { pthread_mutex_lock(&mlt_sdl_mutex); SDL_Surface *screen = SDL_GetVideoSurface(); int result = screen != NULL && (!SDL_MUSTLOCK(screen) || SDL_LockSurface(screen) >= 0); pthread_mutex_unlock(&mlt_sdl_mutex); return result; } static void sdl_unlock_display() { pthread_mutex_lock(&mlt_sdl_mutex); SDL_Surface *screen = SDL_GetVideoSurface(); if (screen != NULL && SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); pthread_mutex_unlock(&mlt_sdl_mutex); } static inline void display_1( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height) { // Generate the affine transform scaling values if (rect.w == 0 || rect.h == 0) return; int scale_width = (width << 16) / rect.w; int scale_height = (height << 16) / rect.h; int stride = width * 4; int x, y, row_index; uint8_t *q, *row; // Constants defined for clarity and optimisation int scanlength = screen->pitch; uint8_t *start = (uint8_t *) screen->pixels + rect.y * scanlength + rect.x; uint8_t *p; // Iterate through the screen using a very basic scaling algorithm for (y = 0; y < rect.h; y++) { p = start; row_index = (32768 + scale_height * y) >> 16; row = image + stride * row_index; for (x = 0; x < rect.w; x++) { q = row + (((32768 + scale_width * x) >> 16) * 4); *p++ = SDL_MapRGB(screen->format, *q, *(q + 1), *(q + 2)); } start += scanlength; } } static inline void display_2( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height) { // Generate the affine transform scaling values if (rect.w == 0 || rect.h == 0) return; int scale_width = (width << 16) / rect.w; int scale_height = (height << 16) / rect.h; int stride = width * 4; int x, y, row_index; uint8_t *q, *row; // Constants defined for clarity and optimisation int scanlength = screen->pitch / 2; uint16_t *start = (uint16_t *) screen->pixels + rect.y * scanlength + rect.x; uint16_t *p; // Iterate through the screen using a very basic scaling algorithm for (y = 0; y < rect.h; y++) { p = start; row_index = (32768 + scale_height * y) >> 16; row = image + stride * row_index; for (x = 0; x < rect.w; x++) { q = row + (((32768 + scale_width * x) >> 16) * 4); *p++ = SDL_MapRGB(screen->format, *q, *(q + 1), *(q + 2)); } start += scanlength; } } static inline void display_3( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height) { // Generate the affine transform scaling values if (rect.w == 0 || rect.h == 0) return; int scale_width = (width << 16) / rect.w; int scale_height = (height << 16) / rect.h; int stride = width * 4; int x, y, row_index; uint8_t *q, *row; // Constants defined for clarity and optimisation int scanlength = screen->pitch; uint8_t *start = (uint8_t *) screen->pixels + rect.y * scanlength + rect.x; uint8_t *p; uint32_t pixel; // Iterate through the screen using a very basic scaling algorithm for (y = 0; y < rect.h; y++) { p = start; row_index = (32768 + scale_height * y) >> 16; row = image + stride * row_index; for (x = 0; x < rect.w; x++) { q = row + (((32768 + scale_width * x) >> 16) * 4); pixel = SDL_MapRGB(screen->format, *q, *(q + 1), *(q + 2)); *p++ = (pixel & 0xFF0000) >> 16; *p++ = (pixel & 0x00FF00) >> 8; *p++ = (pixel & 0x0000FF); } start += scanlength; } } static inline void display_4( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height) { // Generate the affine transform scaling values if (rect.w == 0 || rect.h == 0) return; int scale_width = (width << 16) / rect.w; int scale_height = (height << 16) / rect.h; int stride = width * 4; int x, y, row_index; uint8_t *q, *row; // Constants defined for clarity and optimisation int scanlength = screen->pitch / 4; uint32_t *start = (uint32_t *) screen->pixels + rect.y * scanlength + rect.x; uint32_t *p; // Iterate through the screen using a very basic scaling algorithm for (y = 0; y < rect.h; y++) { p = start; row_index = (32768 + scale_height * y) >> 16; row = image + stride * row_index; for (x = 0; x < rect.w; x++) { q = row + (((32768 + scale_width * x) >> 16) * 4); *p++ = SDL_MapRGB(screen->format, *q, *(q + 1), *(q + 2)); } start += scanlength; } } static int consumer_play_video(consumer_sdl this, mlt_frame frame) { // Get the properties of this consumer mlt_properties properties = this->properties; mlt_image_format vfmt = mlt_image_rgba; int height = this->height; int width = this->width; uint8_t *image = NULL; int changed = 0; double display_ratio = mlt_properties_get_double(this->properties, "display_ratio"); void (*lock)(void) = mlt_properties_get_data(properties, "app_lock", NULL); void (*unlock)(void) = mlt_properties_get_data(properties, "app_unlock", NULL); if (lock != NULL) lock(); void *pool = mlt_cocoa_autorelease_init(); sdl_lock_display(); // Handle events if (SDL_GetVideoSurface()) { SDL_Event event; pthread_mutex_lock(&mlt_sdl_mutex); changed = consumer_get_dimensions(&this->window_width, &this->window_height); pthread_mutex_unlock(&mlt_sdl_mutex); while (SDL_PollEvent(&event)) { mlt_events_fire(this->properties, "consumer-sdl-event", mlt_event_data_from_object(&event)); switch (event.type) { case SDL_VIDEORESIZE: this->window_width = event.resize.w; this->window_height = event.resize.h; changed = 1; break; case SDL_QUIT: this->running = 0; break; case SDL_KEYDOWN: { mlt_producer producer = mlt_properties_get_data(properties, "transport_producer", NULL); char keyboard[2] = " "; void (*callback)(mlt_producer, char *) = mlt_properties_get_data(properties, "transport_callback", NULL); if (callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0) { keyboard[0] = (char) event.key.keysym.unicode; callback(producer, keyboard); } } break; } } } if (!SDL_GetVideoSurface() || changed) { // open SDL window pthread_mutex_lock(&mlt_sdl_mutex); SDL_Surface *screen = SDL_SetVideoMode(this->window_width, this->window_height, 0, this->sdl_flags); if (consumer_get_dimensions(&this->window_width, &this->window_height)) screen = SDL_SetVideoMode(this->window_width, this->window_height, 0, this->sdl_flags); if (screen) { uint32_t color = mlt_properties_get_int(this->properties, "window_background"); SDL_FillRect(screen, NULL, color >> 8); changed = 1; } pthread_mutex_unlock(&mlt_sdl_mutex); } mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "test_audio", 1); if (changed == 0 && this->last_position == mlt_frame_get_position(frame) && this->last_producer == mlt_frame_get_original_producer(frame) && !mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "refresh")) { sdl_unlock_display(); mlt_cocoa_autorelease_close(pool); if (unlock != NULL) unlock(); struct timespec tm = {0, 100000}; nanosleep(&tm, NULL); return 0; } // Update last frame shown info this->last_position = mlt_frame_get_position(frame); this->last_producer = mlt_frame_get_original_producer(frame); // Get the image, width and height mlt_frame_get_image(frame, &image, &vfmt, &width, &height, 0); if (image != NULL) { char *rescale = mlt_properties_get(properties, "rescale"); if (rescale != NULL && strcmp(rescale, "none")) { double this_aspect = display_ratio / ((double) this->window_width / (double) this->window_height); this->rect.w = this_aspect * this->window_width; this->rect.h = this->window_height; if (this->rect.w > this->window_width) { this->rect.w = this->window_width; this->rect.h = (1.0 / this_aspect) * this->window_height; } } else { double frame_aspect = mlt_frame_get_aspect_ratio(frame) * width / height; this->rect.w = frame_aspect * this->window_height; this->rect.h = this->window_height; if (this->rect.w > this->window_width) { this->rect.w = this->window_width; this->rect.h = (1.0 / frame_aspect) * this->window_width; } } this->rect.x = (this->window_width - this->rect.w) / 2; this->rect.y = (this->window_height - this->rect.h) / 2; mlt_properties_set_int(this->properties, "rect_x", this->rect.x); mlt_properties_set_int(this->properties, "rect_y", this->rect.y); mlt_properties_set_int(this->properties, "rect_w", this->rect.w); mlt_properties_set_int(this->properties, "rect_h", this->rect.h); } pthread_mutex_lock(&mlt_sdl_mutex); SDL_Surface *screen = SDL_GetVideoSurface(); if (!mlt_consumer_is_stopped(&this->parent) && screen && screen->pixels) { switch (screen->format->BytesPerPixel) { case 1: display_1(screen, this->rect, image, width, height); break; case 2: display_2(screen, this->rect, image, width, height); break; case 3: display_3(screen, this->rect, image, width, height); break; case 4: display_4(screen, this->rect, image, width, height); break; default: fprintf(stderr, "Unsupported video depth %d\n", screen->format->BytesPerPixel); break; } // Flip it into sight SDL_Flip(screen); } pthread_mutex_unlock(&mlt_sdl_mutex); sdl_unlock_display(); mlt_cocoa_autorelease_close(pool); if (unlock != NULL) unlock(); mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); return 1; } /** Threaded wrapper for pipe. */ static void *consumer_thread(void *arg) { // Identify the arg consumer_sdl this = arg; // Get the consumer mlt_consumer consumer = &this->parent; mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_frame frame = NULL; // Allow the hosting app to provide the preview int preview_off = mlt_properties_get_int(properties, "preview_off"); // Loop until told not to while (this->running) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame(consumer); // Ensure that we have a frame if (this->running && frame != NULL) { if (preview_off == 0) { consumer_play_video(this, frame); } else { mlt_image_format vfmt = mlt_image_rgba; int height = this->height; int width = this->width; uint8_t *image = NULL; mlt_image_format preview_format = mlt_properties_get_int(properties, "preview_format"); // Check if a specific colour space has been requested if (preview_off && preview_format != mlt_image_none) vfmt = preview_format; mlt_frame_get_image(frame, &image, &vfmt, &width, &height, 0); mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "format", vfmt); mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } mlt_frame_close(frame); } else { if (frame) mlt_frame_close(frame); this->running = 0; } } return NULL; } static int consumer_get_dimensions(int *width, int *height) { int changed = 0; // SDL windows manager structure SDL_SysWMinfo wm; // Specify the SDL Version SDL_VERSION(&wm.version); #ifndef __APPLE__ // Get the wm structure if (SDL_GetWMInfo(&wm) == 1) { #ifndef _WIN32 // Check that we have the X11 wm if (wm.subsystem == SDL_SYSWM_X11) { // Get the SDL window Window window = wm.info.x11.window; // Get the display session Display *display = wm.info.x11.display; // Get the window attributes XWindowAttributes attr; XGetWindowAttributes(display, window, &attr); // Determine whether window has changed changed = *width != attr.width || *height != attr.height; // Return width and height *width = attr.width; *height = attr.height; } #endif } #endif return changed; } /** Callback to allow override of the close method. */ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_sdl this = parent->child; // Stop the consumer mlt_consumer_stop(parent); // Now clean up the rest mlt_consumer_close(parent); // Finally clean up this free(this); } mlt-7.38.0/src/modules/sdl/consumer_sdl_still.yml000664 000000 000000 00000000302 15172202314 022025 0ustar00rootroot000000 000000 schema_version: 0.1 type: consumer identifier: sdl_still title: SDL RGB (*DEPRECATED*) version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video mlt-7.38.0/src/modules/sdl/factory.c000664 000000 000000 00000006150 15172202314 017220 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltsdl_export.h" #include #include #include #include extern mlt_consumer consumer_sdl_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_consumer consumer_sdl_audio_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_consumer consumer_sdl_still_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_consumer consumer_sdl_preview_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/sdl/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTSDL_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "sdl", consumer_sdl_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "sdl", metadata, "consumer_sdl.yml"); MLT_REGISTER(mlt_service_consumer_type, "sdl_audio", consumer_sdl_audio_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "sdl_audio", metadata, "consumer_sdl_audio.yml"); MLT_REGISTER(mlt_service_consumer_type, "sdl_preview", consumer_sdl_preview_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "sdl_preview", metadata, "consumer_sdl_preview.yml"); MLT_REGISTER(mlt_service_consumer_type, "sdl_still", consumer_sdl_still_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "sdl_still", metadata, "consumer_sdl_still.yml"); } mlt-7.38.0/src/modules/sdl2/000775 000000 000000 00000000000 15172202314 015465 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/sdl2/CMakeLists.txt000664 000000 000000 00000001646 15172202314 020234 0ustar00rootroot000000 000000 add_library(mltsdl2 MODULE common.c consumer_sdl2_audio.c consumer_sdl2.c factory.c ) file(GLOB YML "*.yml") add_custom_target(Other_sdl2_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltsdl2) target_compile_options(mltsdl2 PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltsdl2 PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltsdl2 PRIVATE mlt Threads::Threads SDL2::SDL2) if(MSVC) target_link_libraries(mltsdl2 PRIVATE PThreads4W::PThreads4W msvccompat) else() target_link_libraries(mltsdl2 PRIVATE m) endif() if(MINGW) target_link_libraries(mltsdl2 PRIVATE ${MLT_PTHREAD_LIBS}) endif() set_target_properties(mltsdl2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltsdl2 LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_sdl2_audio.yml consumer_sdl2.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/sdl2) mlt-7.38.0/src/modules/sdl2/common.c000664 000000 000000 00000004662 15172202314 017131 0ustar00rootroot000000 000000 /* * common.h * Copyright (C) 2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include SDL_AudioDeviceID sdl2_open_audio(const SDL_AudioSpec *desired, SDL_AudioSpec *obtained) { SDL_AudioDeviceID dev = 0; // First try to open using default/user requested driver. dev = SDL_OpenAudioDevice(NULL, 0, desired, obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE); if (dev == 0) { mlt_log_info(NULL, "Failed to open audio device: %s\n", SDL_GetError()); // Try alternative drivers. int i = 0; int driver_count = SDL_GetNumAudioDrivers(); for (i = 0; i < driver_count; i++) { const char *driver = SDL_GetAudioDriver(i); if (strcmp(driver, "disk") == 0 || strcmp(driver, "dummy") == 0) { continue; } if (SDL_AudioInit(driver) != 0) { continue; } mlt_log_info(NULL, "[sdl2] Try alternative driver: %s\n", driver); dev = SDL_OpenAudioDevice(NULL, 0, desired, obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE); if (dev != 0) { break; } else { mlt_log_info(NULL, "[sdl2] Open failed: %s\n", SDL_GetError()); } } } if (dev == 0 && desired->channels > 2) { // All drivers have failed to open with the provided spec. // Try stereo channels since all drivers support that. mlt_log_info(NULL, "Failed to open surround device. Try stereo instead\n"); SDL_AudioSpec desired_copy = *desired; desired_copy.channels = 2; SDL_AudioInit(NULL); dev = sdl2_open_audio(&desired_copy, obtained); } return dev; } mlt-7.38.0/src/modules/sdl2/common.h000664 000000 000000 00000001704 15172202314 017130 0ustar00rootroot000000 000000 /* * common.h * Copyright (C) 2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_H #define COMMON_H #include SDL_AudioDeviceID sdl2_open_audio(const SDL_AudioSpec *desired, SDL_AudioSpec *obtained); #endif // COMMON_H mlt-7.38.0/src/modules/sdl2/consumer_sdl2.c000664 000000 000000 00000100012 15172202314 020402 0ustar00rootroot000000 000000 /* * consumer_sdl.c -- A Simple DirectMedia Layer consumer * Copyright (C) 2017-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _MSC_VER #include #else #include #endif #undef MLT_IMAGE_FORMAT // only yuv422 working currently MLT_EXPORT extern pthread_mutex_t mlt_sdl_mutex; /** This classes definition. */ typedef struct consumer_sdl_s *consumer_sdl; struct consumer_sdl_s { struct mlt_consumer_s parent; mlt_properties properties; mlt_deque queue; pthread_t thread; int joined; atomic_int running; uint8_t audio_buffer[4096 * 10]; int audio_avail; pthread_mutex_t audio_mutex; pthread_cond_t audio_cond; pthread_mutex_t video_mutex; pthread_cond_t video_cond; int window_width; int window_height; int previous_width; int previous_height; int width; int height; int out_channels; SDL_Window *sdl_window; SDL_Renderer *sdl_renderer; SDL_Texture *sdl_texture; SDL_Rect sdl_rect; uint8_t *buffer; int is_purge; #ifdef _WIN32 int no_quit_subsystem; #endif }; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_purge(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); static int setup_sdl_video(consumer_sdl self); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_sdl2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object consumer_sdl self = calloc(1, sizeof(struct consumer_sdl_s)); // If no malloc'd and consumer init ok if (self != NULL && mlt_consumer_init(&self->parent, self, profile) == 0) { // Create the queue self->queue = mlt_deque_init(); // Get the parent consumer object mlt_consumer parent = &self->parent; // We have stuff to clean up, so override the close method parent->close = consumer_close; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE(parent); self->properties = MLT_SERVICE_PROPERTIES(service); // Set the default volume mlt_properties_set_double(self->properties, "volume", 1.0); // This is the initialisation of the consumer pthread_mutex_init(&self->audio_mutex, NULL); pthread_cond_init(&self->audio_cond, NULL); pthread_mutex_init(&self->video_mutex, NULL); pthread_cond_init(&self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set(self->properties, "rescale", "nearest"); mlt_properties_set(self->properties, "consumer.deinterlacer", "onefield"); mlt_properties_set_int(self->properties, "top_field_first", -1); // Default buffer for low latency mlt_properties_set_int(self->properties, "buffer", 1); // Default audio buffer mlt_properties_set_int(self->properties, "audio_buffer", 2048); // Default scrub audio mlt_properties_set_int(self->properties, "scrub_audio", 1); // Ensure we don't join on a non-running object self->joined = 1; // process actual param if (arg && sscanf(arg, "%dx%d", &self->width, &self->height)) { mlt_properties_set_int(self->properties, "resolution", 1); } else { self->width = mlt_properties_get_int(self->properties, "width"); self->height = mlt_properties_get_int(self->properties, "height"); } // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Register specific events mlt_events_register(self->properties, "consumer-sdl-event"); // Return the consumer produced return parent; } // malloc or consumer init failed free(self); // Indicate failure return NULL; } int consumer_start(mlt_consumer parent) { consumer_sdl self = parent->child; if (!self->running) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); int audio_off = mlt_properties_get_int(properties, "audio_off"); char *output_display = mlt_properties_get(properties, "output_display"); char *audio_driver = mlt_properties_get(properties, "audio_driver"); char *video_driver = mlt_properties_get(properties, "video_driver"); char *audio_device = mlt_properties_get(properties, "audio_device"); consumer_stop(parent); self->running = 1; self->joined = 0; if (output_display != NULL) setenv("DISPLAY", output_display, 1); if (video_driver != NULL) setenv("SDL_VIDEODRIVER", video_driver, 1); if (audio_driver != NULL) setenv("SDL_AUDIODRIVER", audio_driver, 1); if (audio_device != NULL) setenv("AUDIODEV", audio_device, 1); if (!mlt_properties_get_int(self->properties, "resolution")) { if (mlt_properties_get_int(self->properties, "width") > 0) self->width = mlt_properties_get_int(self->properties, "width"); if (mlt_properties_get_int(self->properties, "height") > 0) self->height = mlt_properties_get_int(self->properties, "height"); } if (audio_off == 0) SDL_InitSubSystem(SDL_INIT_AUDIO); // Default window size if (mlt_properties_get_int(self->properties, "resolution")) { self->window_width = self->width; self->window_height = self->height; } else { double display_ratio = mlt_properties_get_double(self->properties, "display_ratio"); self->window_width = (double) self->height * display_ratio + 0.5; self->window_height = self->height; } // Initialize SDL video if needed. #ifdef __APPLE__ if (setup_sdl_video(self)) return 1; #else if (!SDL_WasInit(SDL_INIT_VIDEO)) { pthread_mutex_lock(&mlt_sdl_mutex); int ret = SDL_Init(SDL_INIT_VIDEO); pthread_mutex_unlock(&mlt_sdl_mutex); if (ret < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Failed to initialize SDL: %s\n", SDL_GetError()); return 1; } } #endif pthread_create(&self->thread, NULL, consumer_thread, self); } return 0; } int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; if (self->joined == 0) { // Kill the thread and clean up self->joined = 1; self->running = 0; if (!mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "audio_off")) { pthread_mutex_lock(&self->audio_mutex); pthread_cond_broadcast(&self->audio_cond); pthread_mutex_unlock(&self->audio_mutex); } #ifndef _WIN32 if (self->thread) #endif pthread_join(self->thread, NULL); // cleanup SDL pthread_mutex_lock(&mlt_sdl_mutex); if (self->sdl_texture) SDL_DestroyTexture(self->sdl_texture); self->sdl_texture = NULL; if (self->sdl_renderer) SDL_DestroyRenderer(self->sdl_renderer); self->sdl_renderer = NULL; if (self->sdl_window) SDL_DestroyWindow(self->sdl_window); self->sdl_window = NULL; #ifdef _WIN32 if (!self->no_quit_subsystem) #endif if (!mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "audio_off")) SDL_QuitSubSystem(SDL_INIT_AUDIO); if (mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(parent), "sdl_started") == 0) SDL_Quit(); pthread_mutex_unlock(&mlt_sdl_mutex); } return 0; } int consumer_is_stopped(mlt_consumer parent) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge(mlt_consumer parent) { consumer_sdl self = parent->child; if (self->running) { pthread_mutex_lock(&self->video_mutex); while (mlt_deque_count(self->queue)) mlt_frame_close(mlt_deque_pop_back(self->queue)); self->is_purge = 1; pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); } } static void sdl_fill_audio(void *udata, uint8_t *stream, int len) { consumer_sdl self = udata; // Get the volume double volume = mlt_properties_get_double(self->properties, "volume"); // Wipe the stream first memset(stream, 0, len); pthread_mutex_lock(&self->audio_mutex); // Block until audio received while (self->running && len > self->audio_avail) pthread_cond_wait(&self->audio_cond, &self->audio_mutex); if (self->audio_avail >= len) { // Place in the audio buffer if (volume != 1.0) SDL_MixAudio(stream, self->audio_buffer, len, (int) ((float) SDL_MIX_MAXVOLUME * volume)); else memcpy(stream, self->audio_buffer, len); // Remove len from the audio available self->audio_avail -= len; // Remove the samples memmove(self->audio_buffer, self->audio_buffer + len, self->audio_avail); } else { // Mix the audio SDL_MixAudio(stream, self->audio_buffer, len, (int) ((float) SDL_MIX_MAXVOLUME * volume)); // No audio left self->audio_avail = 0; } pthread_cond_broadcast(&self->audio_cond); pthread_mutex_unlock(&self->audio_mutex); } static int consumer_play_audio(consumer_sdl self, mlt_frame frame, int init_audio, int *duration) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int(properties, "channels"); int frequency = mlt_properties_get_int(properties, "frequency"); int scrub = mlt_properties_get_int(properties, "scrub_audio"); static int counter = 0; int samples = mlt_audio_calculate_frame_samples(mlt_properties_get_double(self->properties, "fps"), frequency, counter++); int16_t *pcm; mlt_frame_get_audio(frame, (void **) &pcm, &afmt, &frequency, &channels, &samples); *duration = ((samples * 1000) / frequency); pcm += mlt_properties_get_int(properties, "audio_offset"); if (mlt_properties_get_int(properties, "audio_off")) { init_audio = 1; return init_audio; } if (init_audio == 1) { SDL_AudioSpec request; SDL_AudioSpec got; SDL_AudioDeviceID dev; int audio_buffer = mlt_properties_get_int(properties, "audio_buffer"); // specify audio format memset(&request, 0, sizeof(SDL_AudioSpec)); request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = mlt_properties_get_int(properties, "channels"); request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *) self; dev = sdl2_open_audio(&request, &got); if (dev == 0) { mlt_log_error(MLT_CONSUMER_SERVICE(self), "SDL failed to open audio\n"); init_audio = 2; } else { if (got.channels != request.channels) { mlt_log_info(MLT_CONSUMER_SERVICE(self), "Unable to output %d channels. Change to %d\n", request.channels, got.channels); } mlt_log_info(MLT_CONSUMER_SERVICE(self), "Audio Opened: driver=%s channels=%d frequency=%d\n", SDL_GetCurrentAudioDriver(), got.channels, got.freq); SDL_PauseAudioDevice(dev, 0); init_audio = 0; self->out_channels = got.channels; } } if (init_audio == 0) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int samples_copied = 0; int dst_stride = self->out_channels * sizeof(*pcm); pthread_mutex_lock(&self->audio_mutex); while (self->running && samples_copied < samples) { int sample_space = (sizeof(self->audio_buffer) - self->audio_avail) / dst_stride; while (self->running && sample_space == 0) { struct timeval now; struct timespec tm; gettimeofday(&now, NULL); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait(&self->audio_cond, &self->audio_mutex, &tm); sample_space = (sizeof(self->audio_buffer) - self->audio_avail) / dst_stride; if (sample_space == 0 && self->running) { mlt_log_warning(MLT_CONSUMER_SERVICE(&self->parent), "audio timed out\n"); pthread_mutex_unlock(&self->audio_mutex); #ifdef _WIN32 self->no_quit_subsystem = 1; #endif return 1; } } if (self->running) { int samples_to_copy = samples - samples_copied; if (samples_to_copy > sample_space) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if (scrub || mlt_properties_get_double(properties, "_speed") == 1) { if (channels == self->out_channels) { memcpy(&self->audio_buffer[self->audio_avail], pcm, dst_bytes); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t *) &self->audio_buffer[self->audio_avail]; int i = samples_to_copy + 1; while (--i) { memcpy(dest, pcm, dst_stride); pcm += channels; dest += self->out_channels; } } } else { memset(&self->audio_buffer[self->audio_avail], 0, dst_bytes); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast(&self->audio_cond); } pthread_mutex_unlock(&self->audio_mutex); } return init_audio; } static int setup_sdl_video(consumer_sdl self) { int error = 0; int sdl_flags = SDL_WINDOW_RESIZABLE; int texture_format = SDL_PIXELFORMAT_YUY2; // Skip this if video is disabled. int video_off = mlt_properties_get_int(self->properties, "video_off"); int preview_off = mlt_properties_get_int(self->properties, "preview_off"); uintptr_t window_id = mlt_properties_get_int(self->properties, "window_id"); if (video_off || preview_off) return error; #ifdef __APPLE__ if (!SDL_WasInit(SDL_INIT_VIDEO)) { pthread_mutex_lock(&mlt_sdl_mutex); int ret = SDL_Init(SDL_INIT_VIDEO); pthread_mutex_unlock(&mlt_sdl_mutex); if (ret < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Failed to initialize SDL: %s\n", SDL_GetError()); return -1; } } #endif #ifdef MLT_IMAGE_FORMAT int image_format = mlt_properties_get_int(self->properties, "mlt_image_format"); if (image_format) switch (image_format) { case mlt_image_rgb: texture_format = SDL_PIXELFORMAT_RGB24; break; case mlt_image_rgba: texture_format = SDL_PIXELFORMAT_ABGR8888; break; case mlt_image_yuv420p: texture_format = SDL_PIXELFORMAT_IYUV; break; case mlt_image_yuv422: texture_format = SDL_PIXELFORMAT_YUY2; break; default: mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Invalid image format %s\n", mlt_image_format_name(image_format)); return -1; } #endif if (mlt_properties_get_int(self->properties, "fullscreen")) { self->window_width = self->width; self->window_height = self->height; sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; SDL_ShowCursor(SDL_DISABLE); } pthread_mutex_lock(&mlt_sdl_mutex); if (window_id) { self->sdl_window = SDL_CreateWindowFrom((void *) window_id); SDL_SetWindowResizable(self->sdl_window, SDL_TRUE); } else { self->sdl_window = SDL_CreateWindow("MLT", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, self->window_width, self->window_height, sdl_flags); } self->sdl_renderer = SDL_CreateRenderer(self->sdl_window, -1, SDL_RENDERER_ACCELERATED); if (self->sdl_renderer) { // Get texture width and height from the profile. int width = mlt_properties_get_int(self->properties, "width"); int height = mlt_properties_get_int(self->properties, "height"); self->sdl_texture = SDL_CreateTexture(self->sdl_renderer, texture_format, SDL_TEXTUREACCESS_STREAMING, width, height); if (self->sdl_texture) { SDL_SetRenderDrawColor(self->sdl_renderer, 0, 0, 0, 255); } else { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL texture: %s\n", SDL_GetError()); error = -1; } } else { mlt_log_error(MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL renderer: %s\n", SDL_GetError()); error = -1; } pthread_mutex_unlock(&mlt_sdl_mutex); return error; } static int consumer_play_video(consumer_sdl self, mlt_frame frame) { // Get the properties of this consumer mlt_properties properties = self->properties; #ifdef MLT_IMAGE_FORMAT mlt_image_format vfmt = mlt_properties_get_int(properties, "mlt_image_format"); #else mlt_image_format vfmt = mlt_image_yuv422; #endif int width = self->width, height = self->height; uint8_t *image; int video_off = mlt_properties_get_int(properties, "video_off"); int preview_off = mlt_properties_get_int(properties, "preview_off"); int display_off = video_off | preview_off; uintptr_t window_id = mlt_properties_get_int(self->properties, "window_id"); if (self->running && !display_off) { if (!self->sdl_window) { int error = setup_sdl_video(self); if (error) return error; } // Get the image, width and height mlt_frame_get_image(frame, &image, &vfmt, &width, &height, 0); if (self->running) { // Determine window's new display aspect ratio, and resize if it's an existing window int w = mlt_properties_get_int(properties, "window_width"); int h = mlt_properties_get_int(properties, "window_height"); int width_changed = (w && w != self->window_width); int height_changed = (h && h != self->window_height); if (width_changed || height_changed) { if (width_changed) { self->window_width = w; } if (height_changed) { self->window_height = h; } if (window_id) { SDL_SetWindowSize(self->sdl_window, self->window_width, self->window_height); } } double this_aspect = (double) self->window_width / self->window_height; // Get the display aspect ratio double display_ratio = mlt_properties_get_double(properties, "display_ratio"); // Determine frame's display aspect ratio double frame_aspect = mlt_frame_get_aspect_ratio(frame) * width / height; // Store the width and height received self->width = width; self->height = height; // If using hardware scaler if (mlt_properties_get(properties, "rescale") != NULL && !strcmp(mlt_properties_get(properties, "rescale"), "none")) { // Use hardware scaler to normalize display aspect ratio self->sdl_rect.w = frame_aspect / this_aspect * self->window_width; self->sdl_rect.h = self->window_height; if (self->sdl_rect.w > self->window_width) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = this_aspect / frame_aspect * self->window_height; } } // Special case optimisation to negate odd effect of sample aspect ratio // not corresponding exactly with image resolution. else if ((int) (this_aspect * 1000) == (int) (display_ratio * 1000)) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = self->window_height; } // Use hardware scaler to normalize sample aspect ratio else if (self->window_height * display_ratio > self->window_width) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = self->window_width / display_ratio; } else { self->sdl_rect.w = self->window_height * display_ratio; self->sdl_rect.h = self->window_height; } self->sdl_rect.x = (self->window_width - self->sdl_rect.w) / 2; self->sdl_rect.y = (self->window_height - self->sdl_rect.h) / 2; self->sdl_rect.x -= self->sdl_rect.x % 2; mlt_properties_set_int(self->properties, "rect_x", self->sdl_rect.x); mlt_properties_set_int(self->properties, "rect_y", self->sdl_rect.y); mlt_properties_set_int(self->properties, "rect_w", self->sdl_rect.w); mlt_properties_set_int(self->properties, "rect_h", self->sdl_rect.h); } if (self->running && image) { unsigned char *planes[4]; int strides[4]; mlt_image_format_planes(vfmt, width, height, image, planes, strides); if (strides[1]) { SDL_UpdateYUVTexture(self->sdl_texture, NULL, planes[0], strides[0], planes[1], strides[1], planes[2], strides[2]); } else { SDL_UpdateTexture(self->sdl_texture, NULL, planes[0], strides[0]); } SDL_RenderClear(self->sdl_renderer); SDL_RenderCopy(self->sdl_renderer, self->sdl_texture, NULL, &self->sdl_rect); SDL_RenderPresent(self->sdl_renderer); } mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } else if (self->running) { if (!video_off) { mlt_image_format preview_format = mlt_properties_get_int(properties, "preview_format"); vfmt = preview_format == mlt_image_none ? mlt_image_rgba : preview_format; mlt_frame_get_image(frame, &image, &vfmt, &width, &height, 0); } mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); } return 0; } static void *video_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Obtain time of thread start struct timeval now; int64_t start = 0; int64_t elapsed = 0; struct timespec tm; mlt_frame next = NULL; mlt_properties properties = NULL; double speed = 0; // Get real time flag int real_time = mlt_properties_get_int(self->properties, "real_time"); // Determine start time gettimeofday(&now, NULL); start = (int64_t) now.tv_sec * 1000000 + now.tv_usec; while (self->running) { // Pop the next frame pthread_mutex_lock(&self->video_mutex); next = mlt_deque_pop_front(self->queue); while (next == NULL && self->running) { pthread_cond_wait(&self->video_cond, &self->video_mutex); next = mlt_deque_pop_front(self->queue); } pthread_mutex_unlock(&self->video_mutex); if (!self->running || next == NULL) { if (self->running) { mlt_log_warning( MLT_CONSUMER_SERVICE(&self->parent), "video thread got a null frame even though the consumer is still running!\n"); } break; } // Get the properties properties = MLT_FRAME_PROPERTIES(next); // Get the speed of the frame speed = mlt_properties_get_double(properties, "_speed"); // Get the current time gettimeofday(&now, NULL); // Get the elapsed time elapsed = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - start; // See if we have to delay the display of the current frame if (mlt_properties_get_int(properties, "rendered") == 1 && self->running) { // Obtain the scheduled playout time int64_t scheduled = mlt_properties_get_int(properties, "playtime"); // Determine the difference between the elapsed time and the scheduled playout time int64_t difference = scheduled - elapsed; // Smooth playback a bit if (real_time && (difference > 20000 && speed == 1.0)) { tm.tv_sec = difference / 1000000; tm.tv_nsec = (difference % 1000000) * 500; nanosleep(&tm, NULL); } // Show current frame if not too old if (!real_time || (difference > -10000 || speed != 1.0 || mlt_deque_count(self->queue) < 2)) consumer_play_video(self, next); // If the queue is empty, recalculate start to allow build up again if (real_time && (mlt_deque_count(self->queue) == 0 && speed == 1.0)) { gettimeofday(&now, NULL); start = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - scheduled + 20000; } } else { static int dropped = 0; mlt_log_info(MLT_CONSUMER_SERVICE(&self->parent), "dropped video frame %d\n", ++dropped); } // This frame can now be closed mlt_frame_close(next); next = NULL; } if (next != NULL) mlt_frame_close(next); mlt_consumer_stopped(&self->parent); return NULL; } static void *consumer_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Convenience functionality int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "terminate_on_pause"); int terminated = 0; // Video thread pthread_t thread; // internal initialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; int duration = 0; int64_t playtime = 0; struct timespec tm = {0, 100000}; // Loop until told not to while (self->running) { // Get a frame from the attached producer frame = !terminated ? mlt_consumer_rt_frame(consumer) : NULL; // Check for termination if (terminate_on_pause && frame) terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0; // Ensure that we have a frame if (frame) { // Play audio init_audio = consumer_play_audio(self, frame, init_audio, &duration); if (init_video) { // Create the video thread pthread_create(&thread, NULL, video_thread, self); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "playtime", playtime); while (self->running && mlt_deque_count(self->queue) > 15) nanosleep(&tm, NULL); // Push this frame to the back of the queue pthread_mutex_lock(&self->video_mutex); if (self->is_purge) { mlt_frame_close(frame); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back(self->queue, frame); pthread_cond_broadcast(&self->video_cond); } pthread_mutex_unlock(&self->video_mutex); // Calculate the next playtime playtime += (duration * 1000); } else if (terminated) { if (init_video || mlt_deque_count(self->queue) == 0) break; else nanosleep(&tm, NULL); } } self->running = 0; // Unblock sdl_preview if (mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "put_mode") && mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "put_pending")) { frame = mlt_consumer_get_frame(consumer); if (frame) mlt_frame_close(frame); frame = NULL; } // Kill the video thread if (init_video == 0) { pthread_mutex_lock(&self->video_mutex); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); pthread_join(thread, NULL); } while (mlt_deque_count(self->queue)) mlt_frame_close(mlt_deque_pop_back(self->queue)); pthread_mutex_lock(&self->audio_mutex); self->audio_avail = 0; pthread_mutex_unlock(&self->audio_mutex); return NULL; } static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer ///mlt_consumer_stop( parent ); // Now clean up the rest mlt_consumer_close(parent); // Close the queue mlt_deque_close(self->queue); // Destroy mutexes pthread_mutex_destroy(&self->audio_mutex); pthread_cond_destroy(&self->audio_cond); // Finally clean up this free(self); } mlt-7.38.0/src/modules/sdl2/consumer_sdl2.yml000664 000000 000000 00000002632 15172202314 020772 0ustar00rootroot000000 000000 schema_version: 0.3 type: consumer identifier: sdl2 title: SDL2 version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > Simple DirectMedia Layer audio and video output module parameters: - identifier: resolution title: Resolution type: string description: The size of the window as WxH pixels argument: yes required: no - identifier: volume title: Volume type: float description: Audio level factor mutable: yes - identifier: video_off title: Video off type: boolean description: Disable video output mutable: yes default: 0 widget: checkbox - identifier: audio_off title: Audio off type: boolean description: Disable audio output mutable: yes default: 0 widget: checkbox - identifier: audio_buffer title: Audio buffer type: integer description: Size of the SDL audio buffer mutable: yes default: 2048 minimum: 128 - identifier: scrub_audio title: Audio scrubbing type: boolean description: Play sound even when the speed is not normal. mutable: yes default: 1 widget: checkbox - identifier: terminate_on_pause title: Stop automatically type: boolean description: > Whether to stop playback at the end of the producer or when playback is paused. default: 0 widget: checkbox mlt-7.38.0/src/modules/sdl2/consumer_sdl2_audio.c000664 000000 000000 00000056536 15172202314 021610 0ustar00rootroot000000 000000 /* * consumer_sdl2_audio.c -- A Simple DirectMedia Layer audio-only consumer * Copyright (C) 2009-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _MSC_VER #include #else #include #endif MLT_EXPORT extern pthread_mutex_t mlt_sdl_mutex; /** This classes definition. */ typedef struct consumer_sdl_s *consumer_sdl; struct consumer_sdl_s { struct mlt_consumer_s parent; mlt_properties properties; mlt_deque queue; pthread_t thread; int joined; atomic_int running; uint8_t audio_buffer[4096 * 10]; int audio_avail; pthread_mutex_t audio_mutex; pthread_cond_t audio_cond; pthread_mutex_t video_mutex; pthread_cond_t video_cond; int out_channels; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; int is_purge; #ifdef _WIN32 int no_quit_subsystem; #endif }; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer parent); static void consumer_purge(mlt_consumer parent); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *); static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer self, mlt_event_data); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_sdl2_audio_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object consumer_sdl self = calloc(1, sizeof(struct consumer_sdl_s)); // If no malloc'd and consumer init ok if (self != NULL && mlt_consumer_init(&self->parent, self, profile) == 0) { // Create the queue self->queue = mlt_deque_init(); // Get the parent consumer object mlt_consumer parent = &self->parent; // We have stuff to clean up, so override the close method parent->close = consumer_close; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE(parent); self->properties = MLT_SERVICE_PROPERTIES(service); // Set the default volume mlt_properties_set_double(self->properties, "volume", 1.0); // This is the initialisation of the consumer pthread_mutex_init(&self->audio_mutex, NULL); pthread_cond_init(&self->audio_cond, NULL); pthread_mutex_init(&self->video_mutex, NULL); pthread_cond_init(&self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set(self->properties, "rescale", "nearest"); mlt_properties_set(self->properties, "consumer.deinterlacer", "onefield"); mlt_properties_set_int(self->properties, "top_field_first", -1); // Default buffer for low latency mlt_properties_set_int(self->properties, "buffer", 1); // Default audio buffer mlt_properties_set_int(self->properties, "audio_buffer", 2048); // Ensure we don't join on a non-running object self->joined = 1; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Initialize the refresh handler pthread_cond_init(&self->refresh_cond, NULL); pthread_mutex_init(&self->refresh_mutex, NULL); mlt_events_listen(MLT_CONSUMER_PROPERTIES(parent), self, "property-changed", (mlt_listener) consumer_refresh_cb); // Return the consumer produced return parent; } // malloc or consumer init failed free(self); // Indicate failure return NULL; } static void consumer_refresh_cb(mlt_consumer sdl, mlt_consumer parent, mlt_event_data event_data) { const char *name = mlt_event_data_to_string(event_data); if (name && !strcmp(name, "refresh")) { consumer_sdl self = parent->child; pthread_mutex_lock(&self->refresh_mutex); if (self->refresh_count < 2) self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1; pthread_cond_broadcast(&self->refresh_cond); pthread_mutex_unlock(&self->refresh_mutex); } } int consumer_start(mlt_consumer parent) { consumer_sdl self = parent->child; if (!self->running) { consumer_stop(parent); mlt_properties properties = MLT_CONSUMER_PROPERTIES(parent); char *audio_driver = mlt_properties_get(properties, "audio_driver"); char *audio_device = mlt_properties_get(properties, "audio_device"); if (audio_driver && strcmp(audio_driver, "")) setenv("SDL_AUDIODRIVER", audio_driver, 1); if (audio_device && strcmp(audio_device, "")) setenv("AUDIODEV", audio_device, 1); pthread_mutex_lock(&mlt_sdl_mutex); int ret = SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE); pthread_mutex_unlock(&mlt_sdl_mutex); if (ret < 0) { mlt_log_error(MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError()); return -1; } self->running = 1; self->joined = 0; pthread_create(&self->thread, NULL, consumer_thread, self); } return 0; } int consumer_stop(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; if (self->running && !self->joined) { // Kill the thread and clean up self->joined = 1; self->running = 0; // Unlatch the consumer thread pthread_mutex_lock(&self->refresh_mutex); pthread_cond_broadcast(&self->refresh_cond); pthread_mutex_unlock(&self->refresh_mutex); // Cleanup the main thread #ifndef _WIN32 if (self->thread) #endif pthread_join(self->thread, NULL); // Unlatch the video thread pthread_mutex_lock(&self->video_mutex); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); // Unlatch the audio callback pthread_mutex_lock(&self->audio_mutex); pthread_cond_broadcast(&self->audio_cond); pthread_mutex_unlock(&self->audio_mutex); #ifdef _WIN32 if (!self->no_quit_subsystem) #endif SDL_QuitSubSystem(SDL_INIT_AUDIO); } return 0; } int consumer_is_stopped(mlt_consumer parent) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge(mlt_consumer parent) { consumer_sdl self = parent->child; if (self->running) { pthread_mutex_lock(&self->video_mutex); mlt_frame frame = MLT_FRAME(mlt_deque_peek_back(self->queue)); // When playing rewind or fast forward then we need to keep one // frame in the queue to prevent playback stalling. double speed = frame ? mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") : 0; int n = (speed == 0.0 || speed == 1.0) ? 0 : 1; while (mlt_deque_count(self->queue) > n) mlt_frame_close(mlt_deque_pop_back(self->queue)); self->is_purge = 1; pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); } } static void sdl_fill_audio(void *udata, uint8_t *stream, int len) { consumer_sdl self = udata; // Get the volume double volume = mlt_properties_get_double(self->properties, "volume"); // Wipe the stream first memset(stream, 0, len); pthread_mutex_lock(&self->audio_mutex); int bytes = MIN(len, self->audio_avail); // Place in the audio buffer if (volume != 1.0) { // Adjust the volume while copying. int16_t *src = (int16_t *) self->audio_buffer; int16_t *dst = (int16_t *) stream; int i = bytes / sizeof(*dst) + 1; while (--i) { *dst++ = CLAMP(volume * src[0], -32768, 32767); src++; } } else { memcpy(stream, self->audio_buffer, bytes); } // Remove len from the audio available self->audio_avail -= bytes; // Remove the samples memmove(self->audio_buffer, self->audio_buffer + bytes, self->audio_avail); pthread_cond_broadcast(&self->audio_cond); pthread_mutex_unlock(&self->audio_mutex); } static int consumer_play_audio(consumer_sdl self, mlt_frame frame, int init_audio, int64_t *duration) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int(properties, "channels"); int frequency = mlt_properties_get_int(properties, "frequency"); int scrub = mlt_properties_get_int(properties, "scrub_audio"); static int counter = 0; int samples = mlt_audio_calculate_frame_samples(mlt_properties_get_double(self->properties, "fps"), frequency, counter++); int16_t *pcm; mlt_frame_get_audio(frame, (void **) &pcm, &afmt, &frequency, &channels, &samples); *duration = 1000000LL * samples / frequency; pcm += mlt_properties_get_int(properties, "audio_offset"); if (mlt_properties_get_int(properties, "audio_off")) { init_audio = 1; return init_audio; } if (init_audio == 1) { SDL_AudioSpec request; SDL_AudioSpec got; SDL_AudioDeviceID dev; int audio_buffer = mlt_properties_get_int(properties, "audio_buffer"); // specify audio format memset(&request, 0, sizeof(SDL_AudioSpec)); request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = mlt_properties_get_int(properties, "channels"); request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *) self; dev = sdl2_open_audio(&request, &got); if (dev == 0) { mlt_log_error(MLT_CONSUMER_SERVICE(self), "SDL failed to open audio\n"); init_audio = 2; } else { if (got.channels != request.channels) { mlt_log_info(MLT_CONSUMER_SERVICE(self), "Unable to output %d channels. Change to %d\n", request.channels, got.channels); } mlt_log_info(MLT_CONSUMER_SERVICE(self), "Audio Opened: driver=%s channels=%d frequency=%d\n", SDL_GetCurrentAudioDriver(), got.channels, got.freq); SDL_PauseAudioDevice(dev, 0); init_audio = 0; self->out_channels = got.channels; } } if (init_audio == 0) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int samples_copied = 0; int dst_stride = self->out_channels * sizeof(*pcm); pthread_mutex_lock(&self->audio_mutex); while (self->running && samples_copied < samples) { int sample_space = (sizeof(self->audio_buffer) - self->audio_avail) / dst_stride; while (self->running && sample_space == 0) { struct timeval now; struct timespec tm; gettimeofday(&now, NULL); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait(&self->audio_cond, &self->audio_mutex, &tm); sample_space = (sizeof(self->audio_buffer) - self->audio_avail) / dst_stride; if (sample_space == 0) { mlt_log_warning(MLT_CONSUMER_SERVICE(&self->parent), "audio timed out\n"); pthread_mutex_unlock(&self->audio_mutex); #ifdef _WIN32 self->no_quit_subsystem = 1; #endif return 1; } } if (self->running) { int samples_to_copy = samples - samples_copied; if (samples_to_copy > sample_space) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if (scrub || mlt_properties_get_double(properties, "_speed") == 1) { if (channels == self->out_channels) { memcpy(&self->audio_buffer[self->audio_avail], pcm, dst_bytes); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t *) &self->audio_buffer[self->audio_avail]; int i = samples_to_copy + 1; while (--i) { memcpy(dest, pcm, dst_stride); pcm += channels; dest += self->out_channels; } } } else { memset(&self->audio_buffer[self->audio_avail], 0, dst_bytes); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast(&self->audio_cond); } pthread_mutex_unlock(&self->audio_mutex); } return init_audio; } static int consumer_play_video(consumer_sdl self, mlt_frame frame) { // Get the properties of this consumer mlt_properties properties = self->properties; mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); return 0; } static void *video_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Obtain time of thread start struct timeval now; int64_t start = 0; int64_t elapsed = 0; struct timespec tm; mlt_frame next = NULL; mlt_properties properties = NULL; double speed = 0; // Get real time flag int real_time = mlt_properties_get_int(self->properties, "real_time"); // Get the current time gettimeofday(&now, NULL); // Determine start time start = (int64_t) now.tv_sec * 1000000 + now.tv_usec; while (self->running) { // Pop the next frame pthread_mutex_lock(&self->video_mutex); next = mlt_deque_pop_front(self->queue); while (next == NULL && self->running) { pthread_cond_wait(&self->video_cond, &self->video_mutex); next = mlt_deque_pop_front(self->queue); } pthread_mutex_unlock(&self->video_mutex); if (!self->running || next == NULL) { if (self->running) { mlt_log_warning( MLT_CONSUMER_SERVICE(&self->parent), "video thread got a null frame even though the consumer is still running!\n"); } break; } // Get the properties properties = MLT_FRAME_PROPERTIES(next); // Get the speed of the frame speed = mlt_properties_get_double(properties, "_speed"); // Get the current time gettimeofday(&now, NULL); // Get the elapsed time elapsed = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - start; // See if we have to delay the display of the current frame if (mlt_properties_get_int(properties, "rendered") == 1) { // Obtain the scheduled playout time in microseconds int64_t scheduled = mlt_properties_get_int64(properties, "playtime"); // Determine the difference between the elapsed time and the scheduled playout time int64_t difference = scheduled - elapsed; // Smooth playback a bit if (real_time && (difference > 20000 && speed == 1.0)) { tm.tv_sec = difference / 1000000; tm.tv_nsec = (difference % 1000000) * 1000; nanosleep(&tm, NULL); } // Show current frame if not too old if (!real_time || (difference > -10000 || speed != 1.0 || mlt_deque_count(self->queue) < 2)) consumer_play_video(self, next); // If the queue is empty, recalculate start to allow build up again if (real_time && (mlt_deque_count(self->queue) == 0 && speed == 1.0)) { gettimeofday(&now, NULL); start = ((int64_t) now.tv_sec * 1000000 + now.tv_usec) - scheduled + 20000; start += mlt_properties_get_int(self->properties, "video_delay") * 1000; } } // This frame can now be closed mlt_frame_close(next); next = NULL; } // This consumer is stopping. But audio has already been played for all // the frames in the queue. Spit out all the frames so that the display has // the option to catch up with the audio. if (next != NULL) { consumer_play_video(self, next); mlt_frame_close(next); next = NULL; } while (mlt_deque_count(self->queue) > 0) { next = mlt_deque_pop_front(self->queue); consumer_play_video(self, next); mlt_frame_close(next); next = NULL; } mlt_consumer_stopped(&self->parent); return NULL; } /** Threaded wrapper for pipe. */ static void *consumer_thread(void *arg) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES(consumer); // Video thread pthread_t thread; // internal initialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; mlt_properties properties = NULL; int64_t duration = 0; int64_t playtime = mlt_properties_get_int(consumer_props, "video_delay") * 1000; struct timespec tm = {0, 100000}; // int last_position = -1; pthread_mutex_lock(&self->refresh_mutex); self->refresh_count = 0; pthread_mutex_unlock(&self->refresh_mutex); // Loop until told not to while (self->running) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame(consumer); // Ensure that we have a frame if (frame) { // Get the frame properties properties = MLT_FRAME_PROPERTIES(frame); // Get the speed of the frame double speed = mlt_properties_get_double(properties, "_speed"); // Clear refresh mlt_events_block(consumer_props, consumer_props); mlt_properties_set_int(consumer_props, "refresh", 0); mlt_events_unblock(consumer_props, consumer_props); // Play audio init_audio = consumer_play_audio(self, frame, init_audio, &duration); if (init_video) { // Create the video thread pthread_create(&thread, NULL, video_thread, self); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame in microseconds mlt_properties_set_int64(properties, "playtime", playtime); while (self->running && speed != 0 && mlt_deque_count(self->queue) > 15) nanosleep(&tm, NULL); // Push this frame to the back of the queue if (self->running && speed) { pthread_mutex_lock(&self->video_mutex); if (self->is_purge && speed == 1.0) { mlt_frame_close(frame); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back(self->queue, frame); pthread_cond_broadcast(&self->video_cond); } pthread_mutex_unlock(&self->video_mutex); // Calculate the next playtime playtime += duration; } else if (self->running) { pthread_mutex_lock(&self->refresh_mutex); consumer_play_video(self, frame); mlt_frame_close(frame); frame = NULL; self->refresh_count--; if (self->refresh_count <= 0) { pthread_cond_wait(&self->refresh_cond, &self->refresh_mutex); } pthread_mutex_unlock(&self->refresh_mutex); } // Optimisation to reduce latency if (speed == 1.0) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else if (speed == 0.0) { mlt_consumer_purge(consumer); } } } // Kill the video thread if (init_video == 0) { pthread_mutex_lock(&self->video_mutex); pthread_cond_broadcast(&self->video_cond); pthread_mutex_unlock(&self->video_mutex); pthread_join(thread, NULL); } if (frame) { // The video thread has cleared out the queue. But the audio was played // for this frame. So play the video before stopping so the display has // the option to catch up with the audio. consumer_play_video(self, frame); mlt_frame_close(frame); frame = NULL; } pthread_mutex_lock(&self->audio_mutex); self->audio_avail = 0; pthread_mutex_unlock(&self->audio_mutex); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer mlt_consumer_stop(parent); // Now clean up the rest mlt_consumer_close(parent); // Close the queue mlt_deque_close(self->queue); // Destroy mutexes pthread_mutex_destroy(&self->audio_mutex); pthread_cond_destroy(&self->audio_cond); pthread_mutex_destroy(&self->video_mutex); pthread_cond_destroy(&self->video_cond); pthread_mutex_destroy(&self->refresh_mutex); pthread_cond_destroy(&self->refresh_cond); // Finally clean up this free(self); } mlt-7.38.0/src/modules/sdl2/consumer_sdl2_audio.yml000664 000000 000000 00000002117 15172202314 022151 0ustar00rootroot000000 000000 schema_version: 0.3 type: consumer identifier: sdl2_audio title: SDL2 Audio Only version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: > Simple DirectMedia Layer audio only output module. parameters: - identifier: volume title: Volume type: float description: Audio level factor. mutable: yes - identifier: audio_off title: Audio off type: integer description: If 1, disable audio output mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: audio_buffer title: Audio buffer type: integer description: Size of the sdl audio buffer. mutable: yes default: 2048 minimum: 128 - identifier: scrub_audio title: Audio scrubbing type: integer description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: video_delay title: Video delay mutable: no type: integer unit: milliseconds default: 0 mlt-7.38.0/src/modules/sdl2/factory.c000664 000000 000000 00000004151 15172202314 017301 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2018-2022 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltsdl2_export.h" #include #include #include #include extern mlt_consumer consumer_sdl2_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_consumer consumer_sdl2_audio_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/sdl2/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTSDL2_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "sdl2", consumer_sdl2_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "sdl2", metadata, "consumer_sdl2.yml"); MLT_REGISTER(mlt_service_consumer_type, "sdl2_audio", consumer_sdl2_audio_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "sdl2_audio", metadata, "consumer_sdl2_audio.yml"); } mlt-7.38.0/src/modules/sox/000775 000000 000000 00000000000 15172202314 015432 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/sox/CMakeLists.txt000664 000000 000000 00000001435 15172202314 020175 0ustar00rootroot000000 000000 add_library(mltsox MODULE factory.c filter_sox.c) file(GLOB YML "*.yml") add_custom_target(Other_sox_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltsox) target_compile_options(mltsox PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltsox PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltsox PRIVATE mlt PkgConfig::sox) if(NOT MSVC) target_link_libraries(mltsox PRIVATE m) endif() if(${sox_VERSION} GREATER 13) target_compile_definitions(mltsox PRIVATE SOX14) endif() set_target_properties(mltsox PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltsox LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_sox_effect.yml filter_sox.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/sox) mlt-7.38.0/src/modules/sox/factory.c000664 000000 000000 00000006257 15172202314 017257 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #ifdef SOX14 #include #endif #include "mltsox_export.h" extern mlt_filter filter_sox_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; mlt_properties result = NULL; // Load the yaml file snprintf(file, PATH_MAX, "%s/sox/filter_%s.yml", mlt_environment("MLT_DATA"), strcmp(id, "sox") ? "sox_effect" : "sox"); result = mlt_properties_parse_yaml(file); #ifdef SOX14 if (result && (type == mlt_service_filter_type) && strcmp(id, "sox")) { // Annotate the yaml properties with sox effect usage. mlt_properties params = mlt_properties_get_data(result, "parameters", NULL); const sox_effect_handler_t *e; int i; for (i = 0; sox_effect_fns[i]; i++) { e = sox_effect_fns[i](); if (e && e->name && !strcmp(e->name, id + 4)) { mlt_properties p = mlt_properties_get_data(params, "0", NULL); mlt_properties_set(result, "identifier", e->name); mlt_properties_set(result, "title", e->name); mlt_properties_set(p, "type", "string"); mlt_properties_set(p, "title", "Options"); if (e->usage) mlt_properties_set(p, "format", e->usage); break; } } } #endif return result; } MLTSOX_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "sox", filter_sox_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "sox", metadata, NULL); #ifdef SOX14 int i; const sox_effect_handler_t *e; char name[64] = "sox."; for (i = 0; sox_effect_fns[i]; i++) { e = sox_effect_fns[i](); if (e && e->name && !(e->flags & SOX_EFF_DEPRECATED) #if (SOX_LIB_VERSION_CODE >= SOX_LIB_VERSION(14, 3, 0)) && !(e->flags & SOX_EFF_INTERNAL) #endif ) { strcpy(name + 4, e->name); MLT_REGISTER(mlt_service_filter_type, name, filter_sox_init); MLT_REGISTER_METADATA(mlt_service_filter_type, name, metadata, NULL); } } #endif } mlt-7.38.0/src/modules/sox/filter_sox.c000664 000000 000000 00000044663 15172202314 017771 0ustar00rootroot000000 000000 /* * filter_sox.c -- apply any number of SOX effects using libst * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include // TODO: does not support multiple effects with SoX v14.1.0+ #ifdef SOX14 #include #define ST_EOF SOX_EOF #define ST_SUCCESS SOX_SUCCESS #define st_sample_t sox_sample_t #define eff_t sox_effect_t * #define ST_LIB_VERSION_CODE SOX_LIB_VERSION_CODE #define ST_LIB_VERSION SOX_LIB_VERSION #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14, 2, 0)) #define st_size_t size_t #else #define st_size_t sox_size_t #endif #define ST_SIGNED_WORD_TO_SAMPLE(d, clips) SOX_SIGNED_16BIT_TO_SAMPLE(d, clips) #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14, 1, 0)) #define ST_SSIZE_MIN SOX_SAMPLE_MIN #else #define ST_SSIZE_MIN SOX_SSIZE_MIN #endif #define ST_SAMPLE_TO_SIGNED_WORD(d, clips) SOX_SAMPLE_TO_SIGNED_16BIT(d, clips) #else #include #endif #define BUFFER_LEN 8192 #define AMPLITUDE_NORM 0.2511886431509580 /* -12dBFS */ #define AMPLITUDE_MIN 0.00001 #define DBFSTOAMP(x) pow(10, (x) / 20.0) /** Compute the mean of a set of doubles skipping unset values flagged as -1 */ static inline double mean(double *buf, int count) { double mean = 0; int i; int j = 0; for (i = 0; i < count; i++) { if (buf[i] != -1.0) { mean += buf[i]; j++; } } if (j > 0) mean /= j; return mean; } #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14, 1, 0)) static void delete_effect(eff_t effp) { free(effp->priv); free((void *) effp->in_encoding); free(effp); } #endif /** Create an effect state instance for a channels */ static int create_effect(mlt_filter this, char *value, int count, int channel, int frequency) { mlt_tokeniser tokeniser = mlt_tokeniser_init(); char id[256]; int error = 1; // Tokenise the effect specification mlt_tokeniser_parse_new(tokeniser, value, " "); if (tokeniser->count < 1) { mlt_tokeniser_close(tokeniser); return error; } // Locate the effect mlt_destructor effect_destructor = mlt_pool_release; #ifdef SOX14 //fprintf(stderr, "%s: effect %s count %d\n", __FUNCTION__, tokeniser->tokens[0], tokeniser->count ); #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14, 1, 0)) sox_effect_handler_t const *eff_handle = sox_find_effect(tokeniser->tokens[0]); if (eff_handle == NULL) return error; eff_t eff = sox_create_effect(eff_handle); effect_destructor = (mlt_destructor) delete_effect; sox_encodinginfo_t *enc = calloc(1, sizeof(sox_encodinginfo_t)); enc->encoding = SOX_ENCODING_SIGN2; enc->bits_per_sample = 16; eff->in_encoding = eff->out_encoding = enc; #else eff_t eff = mlt_pool_alloc(sizeof(sox_effect_t)); sox_create_effect(eff, sox_find_effect(tokeniser->tokens[0])); #endif int opt_count = tokeniser->count - 1; #else eff_t eff = mlt_pool_alloc(sizeof(struct st_effect)); int opt_count = st_geteffect_opt(eff, tokeniser->count, tokeniser->tokens); #endif // If valid effect if (opt_count != ST_EOF) { // Supply the effect parameters #ifdef SOX14 #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14, 2, 0)) if (sox_effect_options(eff, opt_count, &tokeniser->tokens[tokeniser->count > 1 ? 1 : 0]) == ST_SUCCESS) #else if ((*eff->handler.getopts)(eff, opt_count, &tokeniser->tokens[tokeniser->count > 1 ? 1 : 0]) == ST_SUCCESS) #endif #else if ((*eff->h->getopts)(eff, opt_count, &tokeniser->tokens[tokeniser->count - opt_count]) == ST_SUCCESS) #endif { // Set the sox signal parameters #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14, 1, 0)) eff->in_signal.rate = frequency; eff->out_signal.rate = frequency; eff->in_signal.channels = 1; eff->out_signal.channels = 1; eff->in_signal.precision = 16; eff->out_signal.precision = 16; eff->in_signal.length = 0; eff->out_signal.length = 0; #else eff->ininfo.rate = frequency; eff->outinfo.rate = frequency; eff->ininfo.channels = 1; eff->outinfo.channels = 1; #endif // Start the effect #ifdef SOX14 if ((*eff->handler.start)(eff) == ST_SUCCESS) #else if ((*eff->h->start)(eff) == ST_SUCCESS) #endif { // Construct id sprintf(id, "_effect_%d_%d", count, channel); // Save the effect state mlt_properties_set_data(MLT_FILTER_PROPERTIES(this), id, eff, 0, effect_destructor, NULL); error = 0; } } } // Some error occurred so delete the temp effect state if (error == 1) effect_destructor(eff); mlt_tokeniser_close(tokeniser); return error; } /** Get the audio. */ static int filter_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14, 3, 0)) SOX_SAMPLE_LOCALS; #endif // Get the filter service mlt_filter filter = mlt_frame_pop_audio(frame); // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Get the properties st_sample_t *input_buffer; // = mlt_properties_get_data( filter_properties, "input_buffer", NULL ); st_sample_t *output_buffer = mlt_properties_get_data(filter_properties, "output_buffer", NULL); int i; // channel int count = mlt_properties_get_int(filter_properties, "_effect_count"); int analysis = mlt_properties_get(filter_properties, "effect") && !strcmp(mlt_properties_get(filter_properties, "effect"), "analysis"); // Get the producer's audio *format = mlt_audio_s32; mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); // Even though some effects are multi-channel aware, it is not reliable // We must maintain a separate effect state for each channel for (i = 0; i < *channels; i++) { char id[256]; sprintf(id, "_effect_0_%d", i); // Get an existing effect state eff_t e = mlt_properties_get_data(filter_properties, id, NULL); // Validate the existing effect state #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14, 1, 0)) if (e != NULL && (e->in_signal.rate != *frequency || e->out_signal.rate != *frequency)) #else if (e != NULL && (e->ininfo.rate != *frequency || e->outinfo.rate != *frequency)) #endif e = NULL; // (Re)Create the effect state if (e == NULL) { int j = 0; // Reset the count count = 0; // Loop over all properties for (j = 0; j < mlt_properties_count(filter_properties); j++) { // Get the name of this property char *name = mlt_properties_get_name(filter_properties, j); // If the name does not contain a . and matches effect if (!strncmp(name, "effect", 6)) { // Get the effect specification char *value = mlt_properties_get_value(filter_properties, j); // Create an instance if (create_effect(filter, value, count, i, *frequency) == 0) count++; } } // Save the number of filters mlt_properties_set_int(filter_properties, "_effect_count", count); } if (*samples > 0 && (count > 0 || analysis)) { input_buffer = (st_sample_t *) *buffer + i * *samples; st_sample_t *p = input_buffer; st_size_t isamp = *samples; st_size_t osamp = *samples; int j = *samples + 1; int normalize = mlt_properties_get_int(filter_properties, "normalize") || mlt_properties_get_int(filter_properties, "normalise"); double normalized_gain = 1.0; if (analysis) { // Run analysis to compute a gain level to normalize the audio across entire filter duration double max_power = mlt_properties_get_double(filter_properties, "_max_power"); double peak = mlt_properties_get_double(filter_properties, "_max_peak"); double use_peak = mlt_properties_get_int(filter_properties, "use_peak"); double power = 0; int n = *samples + 1; // Compute power level of samples in this channel of this frame while (--n) { double s = abs(*p++); // Track peak if (s > peak) { peak = s; mlt_properties_set_double(filter_properties, "_max_peak", peak); } power += s * s; } power /= *samples; // Track maximum power if (power > max_power) { max_power = power; mlt_properties_set_double(filter_properties, "_max_power", max_power); } // Complete analysis the last channel of the last frame. if (i + 1 == *channels && mlt_filter_get_position(filter, frame) + 1 == mlt_filter_get_length2(filter, frame)) { double rms = sqrt(max_power / ST_SSIZE_MIN / ST_SSIZE_MIN); char effect[32]; // Convert RMS or peak to gain if (use_peak) normalized_gain = ST_SSIZE_MIN / -peak; else { double gain = DBFSTOAMP(-12); // default -12 dBFS char *p = mlt_properties_get(filter_properties, "analysis_level"); if (p) { gain = mlt_properties_get_double(filter_properties, "analysis_level"); if (strstr(p, "dB")) gain = DBFSTOAMP(gain); } normalized_gain = gain / rms; } // Set properties for serialization snprintf(effect, sizeof(effect), "vol %f", normalized_gain); effect[31] = 0; mlt_properties_set(filter_properties, "effect", effect); mlt_properties_set(filter_properties, "analyze", NULL); // Show output comparable to normalize --no-adjust --fractions mlt_properties_set_double(filter_properties, "level", rms); mlt_properties_set_double(filter_properties, "gain", normalized_gain); mlt_properties_set_double(filter_properties, "peak", -peak / ST_SSIZE_MIN); } // restore some variables p = input_buffer; } if (normalize) { int window = mlt_properties_get_int(filter_properties, "window"); double *smooth_buffer = mlt_properties_get_data(filter_properties, "smooth_buffer", NULL); double max_gain = mlt_properties_get_double(filter_properties, "max_gain"); double rms = 0; // Default the maximum gain factor to 20dBFS if (max_gain == 0) max_gain = 10.0; // Compute rms amplitude while (--j) { rms += (double) *p * (double) *p; p++; } rms = sqrt(rms / *samples / ST_SSIZE_MIN / ST_SSIZE_MIN); // The smoothing buffer prevents radical shifts in the gain level if (window > 0 && smooth_buffer != NULL) { int smooth_index = mlt_properties_get_int(filter_properties, "_smooth_index"); smooth_buffer[smooth_index] = rms; // Ignore very small values that adversely affect the mean if (rms > AMPLITUDE_MIN) mlt_properties_set_int(filter_properties, "_smooth_index", (smooth_index + 1) % window); // Smoothing is really just a mean over the past N values normalized_gain = AMPLITUDE_NORM / mean(smooth_buffer, window); } else if (rms > 0) { // Determine gain to apply as current amplitude normalized_gain = AMPLITUDE_NORM / rms; } //printf("filter_sox: rms %.3f gain %.3f\n", rms, normalized_gain ); // Govern the maximum gain if (normalized_gain > max_gain) normalized_gain = max_gain; } // For each effect for (j = 0; j < count; j++) { sprintf(id, "_effect_%d_%d", j, i); e = mlt_properties_get_data(filter_properties, id, NULL); // We better have this guy if (e != NULL) { float saved_gain = 1.0; // XXX: hack to apply the normalized gain level to the vol effect #ifdef SOX14 if (normalize && strcmp(e->handler.name, "vol") == 0) #else if (normalize && strcmp(e->name, "vol") == 0) #endif { float *f = (float *) (e->priv); saved_gain = *f; *f = saved_gain * normalized_gain; } // Apply the effect #ifdef SOX14 if ((*e->handler.flow)(e, input_buffer, output_buffer, &isamp, &osamp) != ST_SUCCESS) #else if ((*e->h->flow)(e, input_buffer, output_buffer, &isamp, &osamp) != ST_SUCCESS) #endif { mlt_log_warning(MLT_FILTER_SERVICE(filter), "effect processing failed\n"); } // XXX: hack to restore the original vol gain to prevent accumulation #ifdef SOX14 if (normalize && strcmp(e->handler.name, "vol") == 0) #else if (normalize && strcmp(e->name, "vol") == 0) #endif { float *f = (float *) (e->priv); *f = saved_gain; } } } // Write back memcpy(input_buffer, output_buffer, *samples * sizeof(st_sample_t)); } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return 0; } /** Filter processing. */ static mlt_frame filter_process(mlt_filter this, mlt_frame frame) { if (mlt_frame_is_test_audio(frame) == 0) { // Add the filter to the frame mlt_frame_push_audio(frame, this); mlt_frame_push_audio(frame, filter_get_audio); // Parse the window property and allocate smoothing buffer if needed mlt_properties properties = MLT_FILTER_PROPERTIES(this); int window = mlt_properties_get_int(properties, "window"); if (mlt_properties_get(properties, "smooth_buffer") == NULL && window > 1) { // Create a smoothing buffer for the calculated "max power" of frame of audio used in normalization double *smooth_buffer = (double *) calloc(window, sizeof(double)); int i; for (i = 0; i < window; i++) smooth_buffer[i] = -1.0; mlt_properties_set_data(properties, "smooth_buffer", smooth_buffer, 0, free, NULL); } } return frame; } /** Constructor for the filter. */ mlt_filter filter_sox_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter this = mlt_filter_new(); if (this != NULL) { void *input_buffer = mlt_pool_alloc(BUFFER_LEN); void *output_buffer = mlt_pool_alloc(BUFFER_LEN); mlt_properties properties = MLT_FILTER_PROPERTIES(this); this->process = filter_process; if (!strncmp(id, "sox.", 4)) { char *s = malloc(strlen(id) + (arg ? strlen(arg) + 2 : 1)); strcpy(s, id + 4); if (arg) { strcat(s, " "); strcat(s, arg); } mlt_properties_set(properties, "effect", s); free(s); } else if (arg) mlt_properties_set(properties, "effect", arg); mlt_properties_set_data(properties, "input_buffer", input_buffer, BUFFER_LEN, mlt_pool_release, NULL); mlt_properties_set_data(properties, "output_buffer", output_buffer, BUFFER_LEN, mlt_pool_release, NULL); mlt_properties_set_int(properties, "window", 75); mlt_properties_set(properties, "version", sox_version()); } return this; } // What to do when a libst internal failure occurs void cleanup(void) {} mlt-7.38.0/src/modules/sox/filter_sox.yml000664 000000 000000 00000005136 15172202314 020340 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: sox title: SoX version: 2 copyright: Meltytech, LLC license: LGPL language: en url: http://sox.sourceforge.net/ creator: Dan Dennedy tags: - Audio description: Process audio using a SoX effect. bugs: - Some effects are stereo only, but MLT processes each channel separately. - Some effects have a temporal side-effect that do not work well. parameters: - identifier: effect argument: yes title: Effect name and options type: string format: effect [options] description: > If the effect name is "analysis" then it does not run any effect. Instead, it analyzes the audio to determine a normalized gain level. The results are put into the level, peak, and gain properties as well as this effect property as the parameter to the vol effect. - identifier: analysis_level title: Normalization level type: string default: -12dBFS description: > Normalize the volume to the specified amplitude. The normalization may be indicated as a floating point value of the relative volume with 1.0 being maximum. The normalization may also be indicated as a numeric value with the suffix "dB" to set the amplitude in decibels. - identifier: level title: Signal power level (RMS) type: float readonly: yes - identifier: peak title: Peak signal level type: float readonly: yes - identifier: gain title: Gain to normalize type: float readonly: yes - identifier: use_peak title: Use peak description: > Use peak signal level instead of RMS (root mean square) power level to compute gain for normalization. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: normalize title: Dynamic normalization description: > This computes the gain for normalization dynamically per frame, but it uses a sliding smoothing window to prevent the gain from fluctuating wildly. Currently, this must be used in combination with some SoX effect. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: normalise title: Dynamic normalisation (*DEPRECATED*) description: Deprecated. See normalize - identifier: window title: Smoothing window size type: integer minimum: 0 default: 75 unit: frames widget: spinner - identifier: max_gain title: Maximum gain description: > With dynamic normalization, this puts a maximum limit on the amount of gain. type: float minimum: 0 maximum: 20 default: 10 mlt-7.38.0/src/modules/sox/filter_sox_effect.yml000664 000000 000000 00000001017 15172202314 021646 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: sox title: sox version: 1 copyright: Meltytech, LLC license: LGPL language: en url: http://sox.sourceforge.net/ creator: Dan Dennedy tags: - Audio description: Process audio using a SoX effect. bugs: - Some effects are stereo only, but MLT processes each channel separately. - Some effects have a temporal side-effect that do not work well. parameters: - identifier: effect argument: yes title: Effect name and options type: string format: effect [options] mlt-7.38.0/src/modules/spatialaudio/000775 000000 000000 00000000000 15172202314 017300 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/spatialaudio/CMakeLists.txt000664 000000 000000 00000001470 15172202314 022042 0ustar00rootroot000000 000000 add_library(mltspatialaudio MODULE factory.c filter_ambisonic-decoder.cpp filter_ambisonic-encoder.cpp ) file(GLOB YML "*.yml") add_custom_target(Other_spatialaudio_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltspatialaudio) target_compile_options(mltspatialaudio PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltspatialaudio PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltspatialaudio PRIVATE mlt PkgConfig::spatialaudio) set_target_properties(mltspatialaudio PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltspatialaudio LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_ambisonic-decoder.yml filter_ambisonic-encoder.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/spatialaudio ) mlt-7.38.0/src/modules/spatialaudio/factory.c000664 000000 000000 00000004245 15172202314 021120 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "mltspatialaudio_export.h" #include #include extern mlt_filter filter_ambisonic_decoder_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_ambisonic_encoder_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; mlt_properties result = NULL; // Load the yaml file snprintf(file, PATH_MAX, "%s/spatialaudio/filter_%s.yml", mlt_environment("MLT_DATA"), id); result = mlt_properties_parse_yaml(file); return result; } MLTSPATIALAUDIO_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "ambisonic-decoder", filter_ambisonic_decoder_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "ambisonic-decoder", metadata, NULL); MLT_REGISTER(mlt_service_filter_type, "ambisonic-encoder", filter_ambisonic_encoder_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "ambisonic-encoder", metadata, NULL); } mlt-7.38.0/src/modules/spatialaudio/filter_ambisonic-decoder.cpp000664 000000 000000 00000023601 15172202314 024722 0ustar00rootroot000000 000000 /* * filter_ambisonic-decoder.cpp -- decode ambisonic audio to speaker channels * Copyright (C) 2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include static const auto MAX_CHANNELS = 6; static const auto AMBISONICS_BLOCK_SIZE = 1024; static const auto AMBISONICS_ORDER = 1; static const auto AMBISONICS_1_CHANNELS = 4; extern "C" { static mlt_frame process(mlt_filter filter, mlt_frame frame); static void close_filter(mlt_filter filter); } class SpatialAudio { private: mlt_filter m_filter; CAmbisonicBinauralizer binauralizer; unsigned tailLength; CAmbisonicDecoder decoder; CAmbisonicProcessor processor; CAmbisonicZoomer zoomer; float *speakers[MAX_CHANNELS]; public: SpatialAudio() { m_filter = mlt_filter_new(); if (m_filter) { m_filter->child = this; m_filter->close = close_filter; m_filter->process = process; } } ~SpatialAudio() { m_filter->child = nullptr; m_filter->close = nullptr; m_filter->parent.close = nullptr; mlt_service_close(MLT_FILTER_SERVICE(m_filter)); m_filter = nullptr; } mlt_filter filter() { return m_filter; } mlt_properties properties() { return MLT_FILTER_PROPERTIES(filter()); } double getDouble(const char *name, int position, int length) { return mlt_properties_anim_get_double(properties(), name, position, length); } bool getAudio(mlt_frame frame, float *buffer, int samples, int channels) { bool error = false; bool binaural = channels >= 2 && mlt_properties_get_int(properties(), "binaural"); // First time setup if (binaural && !binauralizer.GetChannelCount()) { mlt_log_verbose(MLT_FILTER_SERVICE(filter()), "configuring spatial audio binauralizer\n"); error = !binauralizer.Configure(AMBISONICS_ORDER, true, mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "audio_frequency"), samples, tailLength); if (!error) { binauralizer.Reset(); } else { mlt_log_error(MLT_FILTER_SERVICE(filter()), "failed to configure spatial audio binauralizer\n"); } } else if (!binaural && !decoder.GetChannelCount()) { mlt_log_verbose(MLT_FILTER_SERVICE(filter()), "configuring spatial audio decoder for %d channels\n", channels); error = !decoder.Configure(AMBISONICS_ORDER, true, AMBISONICS_BLOCK_SIZE, channels == 6 ? kAmblib_51 : channels == 2 ? kAmblib_Stereo : channels == 4 ? kAmblib_Quad : kAmblib_CustomSpeakerSetUp, channels); if (!error) { mlt_log_verbose(MLT_FILTER_SERVICE(filter()), "configuring spatial audio processor\n"); error = !processor.Configure(AMBISONICS_ORDER, true, AMBISONICS_BLOCK_SIZE, 0); if (!error) { mlt_log_verbose(MLT_FILTER_SERVICE(filter()), "configuring spatial audio zoomer\n"); error = !zoomer.Configure(AMBISONICS_ORDER, true, AMBISONICS_BLOCK_SIZE, 0); if (error) { mlt_log_error(MLT_FILTER_SERVICE(filter()), "failed to configure spatial audio zoomer\n"); } } else { mlt_log_error(MLT_FILTER_SERVICE(filter()), "failed to configure spatial audio processor\n"); } } else { mlt_log_error(MLT_FILTER_SERVICE(filter()), "failed to configure spatial audio decoder\n"); } } // Processing if (!error) { CBFormat bformat; bformat.Configure(AMBISONICS_ORDER, true, samples); for (unsigned i = 0; i < AMBISONICS_1_CHANNELS; ++i) bformat.InsertStream(&buffer[samples * i], i, samples); if (!binaural) { mlt_position position = mlt_filter_get_position(filter(), frame); mlt_position length = mlt_filter_get_length2(filter(), frame); processor.SetOrientation({-DegreesToRadians(getDouble("yaw", position, length)), DegreesToRadians(getDouble("pitch", position, length)), DegreesToRadians(getDouble("roll", position, length))}); processor.Refresh(); processor.Process(&bformat, samples); zoomer.SetZoom(getDouble("zoom", position, length)); zoomer.Refresh(); zoomer.Process(&bformat, samples); } if (channels == 6) { // libspatialaudio has a different channel order for 5.1 speakers[0] = &buffer[samples * 0]; // left speakers[1] = &buffer[samples * 1]; // right speakers[2] = &buffer[samples * 4]; // center speakers[3] = &buffer[samples * 5]; // LFE (subwoofer) speakers[4] = &buffer[samples * 2]; // left surround speakers[5] = &buffer[samples * 3]; // right surround decoder.Process(&bformat, samples, speakers); } else if (channels == 4 && mlt_properties_get_int(properties(), "ambisonic")) { for (int i = 0; i < channels; ++i) bformat.ExtractStream(&buffer[samples * i], i, samples); } else { for (int i = 0; i < channels; ++i) speakers[i] = &buffer[samples * i]; if (binaural) { binauralizer.Process(&bformat, speakers, samples); if (channels > 2) { for (int i = 2; i < channels; ++i) ::memset(speakers[i], 0, samples * sizeof(float)); } } else { decoder.Process(&bformat, samples, speakers); } } } return error; } }; extern "C" { static int get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { auto error = 0; // Get the filter service mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Get the producer's audio if (*channels > 1) { *format = mlt_audio_float; int requestChannels = MAX(AMBISONICS_1_CHANNELS, *channels); auto properties = MLT_FRAME_PROPERTIES(frame); auto channel_layout = mlt_audio_channel_layout_id( mlt_properties_get(properties, "consumer.channel_layout")); mlt_properties_set(MLT_FRAME_PROPERTIES(frame), "consumer.channel_layout", mlt_audio_channel_layout_name(mlt_channel_independent)); error = mlt_frame_get_audio(frame, buffer, format, frequency, &requestChannels, samples); // Restore the saved channel layout mlt_properties_set(properties, "consumer.channel_layout", mlt_audio_channel_layout_name(channel_layout)); if (!error && requestChannels >= AMBISONICS_1_CHANNELS) { auto spatial = static_cast(filter->child); error = spatial->getAudio(frame, static_cast(*buffer), *samples, *channels); } } else { // pass through mono error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { if (mlt_frame_is_test_audio(frame) == 0) { // Add the filter to the frame mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, (void *) get_audio); } return frame; } static void close_filter(mlt_filter filter) { delete reinterpret_cast(filter->child); } mlt_filter filter_ambisonic_decoder_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = nullptr; auto *spatial = new SpatialAudio; if (spatial) { filter = spatial->filter(); if (!filter) delete spatial; } return filter; } } // extern mlt-7.38.0/src/modules/spatialaudio/filter_ambisonic-decoder.yml000664 000000 000000 00000003006 15172202314 024736 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: ambisonic-decoder title: Ambisonic Decoder version: 1 copyright: Meltytech, LLC license: LGPL language: en url: https://github.com/videolabs/libspatialaudio tags: - Audio description: Decode, rotate (pan), and zoom Ambisonic audio. notes: > This only supports first order for now. It can render to stereo, quad surround, and 5.1 surround depending on the number of channels configured on the consumer (2, 4, or 6, respectively). parameters: - identifier: yaw title: Yaw type: float default: 0 unit: degrees mutable: yes animation: yes - identifier: pitch title: Pitch type: float default: 0 unit: degrees mutable: yes animation: yes - identifier: roll title: Roll type: float default: 0 unit: degrees mutable: yes animation: yes - identifier: zoom title: Zoom type: float default: 0 minimum: -1 maximum: 1 unit: percent mutable: yes animation: yes - identifier: ambisonic title: Keep Ambisonic description: > This indicates whether to keep the Ambisonics or render to speakers. This only works at the moment when the channel count is 4 for first order. type: boolean default: 0 mutable: yes - identifier: binaural title: Binaural Output description: > This indicates whether to render as binaural instead of stereo. This property overrides the "ambisonic" property. type: boolean default: 0 mutable: no mlt-7.38.0/src/modules/spatialaudio/filter_ambisonic-encoder.cpp000664 000000 000000 00000013754 15172202314 024744 0ustar00rootroot000000 000000 /* * filter_ambisonic-encoder.cpp -- position mono and stero sound in ambisonic space * Copyright (C) 2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include // static const auto MAX_CHANNELS = 6; static const auto AMBISONICS_ORDER = 1; static const auto AMBISONICS_1_CHANNELS = 4; extern "C" { static mlt_frame process(mlt_filter filter, mlt_frame frame); static void close_filter(mlt_filter filter); } class SpatialAudioEncoder { private: mlt_filter m_filter; CAmbisonicEncoder encoder; public: SpatialAudioEncoder() { m_filter = mlt_filter_new(); if (m_filter) { m_filter->child = this; m_filter->close = close_filter; m_filter->process = process; } } ~SpatialAudioEncoder() { m_filter->child = nullptr; m_filter->close = nullptr; m_filter->parent.close = nullptr; mlt_service_close(MLT_FILTER_SERVICE(m_filter)); m_filter = nullptr; } mlt_filter filter() { return m_filter; } mlt_properties properties() { return MLT_FILTER_PROPERTIES(filter()); } double getDouble(const char *name, int position, int length) { return mlt_properties_anim_get_double(properties(), name, position, length); } bool getAudio(mlt_frame frame, float *buffer, int samples, int channels) { bool error = false; // First time setup if (!encoder.GetOrder()) { mlt_log_verbose(MLT_FILTER_SERVICE(filter()), "configuring spatial audio encoder\n"); error = !encoder.Configure(AMBISONICS_ORDER, true, 0); if (error) { mlt_log_error(MLT_FILTER_SERVICE(filter()), "failed to configure spatial audio encoder\n"); } } // Processing if (!error) { CBFormat bformat; PolarPoint polar; mlt_position position = mlt_filter_get_position(filter(), frame); mlt_position length = mlt_filter_get_length2(filter(), frame); bformat.Configure(AMBISONICS_ORDER, true, samples); polar.fAzimuth = -DegreesToRadians(getDouble("azimuth", position, length)); polar.fElevation = DegreesToRadians(getDouble("elevation", position, length)); polar.fDistance = getDouble("distance", position, length); encoder.SetPosition(polar, 1.f); encoder.Refresh(); encoder.Process(buffer, samples, &bformat); for (int i = 0; i < AMBISONICS_1_CHANNELS; ++i) bformat.ExtractStream(&buffer[samples * i], i, samples); for (int i = AMBISONICS_1_CHANNELS; i < channels; ++i) ::memset(&buffer[samples * i], 0, samples * sizeof(float)); } return error; } }; extern "C" { static int get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { auto error = 0; // Get the filter service mlt_filter filter = (mlt_filter) mlt_frame_pop_audio(frame); mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Get the producer's audio if (*channels >= AMBISONICS_1_CHANNELS) { *format = mlt_audio_float; auto properties = MLT_FRAME_PROPERTIES(frame); auto channel_layout = mlt_audio_channel_layout_id( mlt_properties_get(properties, "consumer.channel_layout")); mlt_properties_set(MLT_FRAME_PROPERTIES(frame), "consumer.channel_layout", mlt_audio_channel_layout_name(mlt_channel_independent)); error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); // Restore the saved channel layout mlt_properties_set(properties, "consumer.channel_layout", mlt_audio_channel_layout_name(channel_layout)); if (!error && *channels >= AMBISONICS_1_CHANNELS) { auto spatial = static_cast(filter->child); error = spatial->getAudio(frame, static_cast(*buffer), *samples, *channels); } } else { // pass through mono and stereo error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { if (mlt_frame_is_test_audio(frame) == 0) { // Add the filter to the frame mlt_frame_push_audio(frame, filter); mlt_frame_push_audio(frame, (void *) get_audio); } return frame; } static void close_filter(mlt_filter filter) { delete reinterpret_cast(filter->child); } mlt_filter filter_ambisonic_encoder_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = nullptr; auto *spatial = new SpatialAudioEncoder; if (spatial) { filter = spatial->filter(); if (!filter) delete spatial; } return filter; } } // extern mlt-7.38.0/src/modules/spatialaudio/filter_ambisonic-encoder.yml000664 000000 000000 00000001516 15172202314 024754 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: ambisonic-encoder title: Ambisonic Encoder version: 1 copyright: Meltytech, LLC license: LGPL language: en url: https://github.com/videolabs/libspatialaudio tags: - Audio description: Position a mono sound in Ambisonic space. notes: > This only supports first order for now. The consumer should be configured for 4 or more channels. If the source has more than one channel, only the first channel is used. parameters: - identifier: azimuth title: Azimuth type: float default: 0 unit: degrees mutable: yes animation: yes - identifier: elevation title: Elevation type: float default: 0 unit: degrees mutable: yes animation: yes - identifier: distance title: Distance type: float default: 0 mutable: yes animation: yes mlt-7.38.0/src/modules/vid.stab/000775 000000 000000 00000000000 15172202314 016333 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/vid.stab/CMakeLists.txt000664 000000 000000 00000001451 15172202314 021074 0ustar00rootroot000000 000000 add_library(mltvidstab MODULE common.c common.h factory.c filter_deshake.cpp filter_vidstab.cpp ) include(GenerateExportHeader) generate_export_header(mltvidstab) file(GLOB YML "*.yml") add_custom_target(Other_vidstab_Files SOURCES ${YML} ) target_compile_options(mltvidstab PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltvidstab PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltvidstab PRIVATE mlt mlt++ PkgConfig::vidstab) if(NOT MSVC) target_link_libraries(mltvidstab PRIVATE m) endif() set_target_properties(mltvidstab PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltvidstab LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_deshake.yml filter_vidstab.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/vid.stab) mlt-7.38.0/src/modules/vid.stab/common.c000664 000000 000000 00000013715 15172202314 017776 0ustar00rootroot000000 000000 /* * common.c * Copyright (C) 2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "common.h" #include #include mlt_image_format validate_format(mlt_image_format format) { switch (format) { case mlt_image_yuv420p: return mlt_image_yuv420p; default: return mlt_image_yuv422; } } /** Convert an MLT image to one that can be used by VS. * Use free_vsimage() when done with the resulting image. */ VSPixelFormat mltimage_to_vsimage( mlt_image_format mlt_format, int width, int height, uint8_t *mlt_img, uint8_t **vs_img) { switch (mlt_format) { case mlt_image_yuv420p: // This format maps with no conversion { *vs_img = mlt_img; return PF_YUV420P; } case mlt_image_yuv422: // Convert packed YUV422 to planar YUV444 // Note: vid.stab 0.98 seems to suffer chroma bleeding // when using PF_YUV422P - which is why PF_YUV444P is used. { *vs_img = mlt_pool_alloc(width * height * 3); uint8_t *yp = *vs_img; uint8_t *up = yp + (width * height); uint8_t *vp = up + (width * height); int i, j, n = width / 2 + 1; for (i = 0; i < height; i++) { j = n; while (--j) { *yp++ = mlt_img[0]; *up++ = mlt_img[1]; *vp++ = mlt_img[3]; *yp++ = mlt_img[2]; *up++ = mlt_img[1]; *vp++ = mlt_img[3]; mlt_img += 4; } if (width % 2) { *yp++ = mlt_img[0]; *up++ = mlt_img[1]; *vp++ = (mlt_img - 4)[3]; mlt_img += 2; } } return PF_YUV444P; } default: return PF_NONE; } } /** Convert a VS image back to the MLT image it originally came from in mltimage_to_vsimage(). */ void vsimage_to_mltimage( uint8_t *vs_img, uint8_t *mlt_img, mlt_image_format mlt_format, int width, int height) { switch (mlt_format) { case mlt_image_yuv420p: // This format was never converted break; case mlt_image_yuv422: // Convert planar YUV444 to packed YUV422 { uint8_t *yp = vs_img; uint8_t *up = yp + (width * height); uint8_t *vp = up + (width * height); int i, j, n = width / 2 + 1; for (i = 0; i < height; i++) { j = n; while (--j) { *mlt_img++ = yp[0]; *mlt_img++ = (up[0] + up[1]) >> 1; *mlt_img++ = yp[1]; *mlt_img++ = (vp[0] + vp[1]) >> 1; yp += 2; up += 2; vp += 2; } if (width % 2) { *mlt_img++ = yp[0]; *mlt_img++ = up[0]; yp += 1; up += 1; vp += 1; } } } break; default: break; } } /** Free an image allocated by mltimage_to_vsimage(). */ void free_vsimage(uint8_t *vs_img, VSPixelFormat format) { if (format != PF_YUV420P) { mlt_pool_release(vs_img); } } /** Compare two VSMotionDetectConfig structures. * Return 1 if they are different. 0 if they are the same. */ int compare_motion_config(VSMotionDetectConfig *a, VSMotionDetectConfig *b) { if (a->shakiness != b->shakiness || a->accuracy != b->accuracy || a->stepSize != b->stepSize || // Skip: Deprecated // a->algo != b->algo || a->virtualTripod != b->virtualTripod || a->show != b->show || // Skip: inconsequential? // a->modName != b->modName || a->contrastThreshold != b->contrastThreshold) { return 1; } return 0; } /** Compare two VSTransformConfig structures. * Return 1 if they are different. 0 if they are the same. */ int compare_transform_config(VSTransformConfig *a, VSTransformConfig *b) { if (a->relative != b->relative || a->smoothing != b->smoothing || a->crop != b->crop || a->invert != b->invert || a->zoom != b->zoom || a->optZoom != b->optZoom || a->zoomSpeed != b->zoomSpeed || a->interpolType != b->interpolType || a->maxShift != b->maxShift || a->maxAngle != b->maxAngle || // Skip: inconsequential? // a->modName != b->modName || // Skip: unused? // a->verbose != b->verbose || a->simpleMotionCalculation != b->simpleMotionCalculation || // Skip: unused? // a->storeTransforms != b->storeTransforms || a->smoothZoom != b->smoothZoom || a->camPathAlgo != b->camPathAlgo) { return 1; } return 0; } static int vs_log_wrapper(int type, const char *tag, const char *format, ...) { va_list vl; if (type > mlt_log_get_level()) return VS_OK; va_start(vl, format); fprintf(stderr, "[%s] ", tag); vfprintf(stderr, format, vl); va_end(vl); return VS_OK; } void init_vslog() { VS_ERROR_TYPE = MLT_LOG_ERROR; VS_WARN_TYPE = MLT_LOG_WARNING; VS_INFO_TYPE = MLT_LOG_INFO; VS_MSG_TYPE = MLT_LOG_VERBOSE; vs_log = vs_log_wrapper; } mlt-7.38.0/src/modules/vid.stab/common.h000664 000000 000000 00000002762 15172202314 020003 0ustar00rootroot000000 000000 /* * common.h * Copyright (C) 2013 Jakub Ksiezniak * Copyright (C) 2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VIDSTAB_COMMON_H_ #define VIDSTAB_COMMON_H_ #include #include mlt_image_format validate_format(mlt_image_format format); VSPixelFormat mltimage_to_vsimage( mlt_image_format mlt_format, int width, int height, uint8_t *mlt_img, uint8_t **vs_img); void vsimage_to_mltimage( uint8_t *vs_img, uint8_t *mlt_img, mlt_image_format mlt_format, int width, int height); void free_vsimage(uint8_t *vs_img, VSPixelFormat format); int compare_motion_config(VSMotionDetectConfig *a, VSMotionDetectConfig *b); int compare_transform_config(VSTransformConfig *a, VSTransformConfig *b); void init_vslog(); #endif /* VIDSTAB_COMMON_H_ */ mlt-7.38.0/src/modules/vid.stab/factory.c000664 000000 000000 00000003707 15172202314 020155 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2013-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mltvidstab_export.h" #include #include #include extern mlt_filter filter_deshake_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/vid.stab/filter_%s.yml", mlt_environment("MLT_DATA"), id); return mlt_properties_parse_yaml(file); } MLTVIDSTAB_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "deshake", filter_deshake_init); MLT_REGISTER(mlt_service_filter_type, "vidstab", filter_vidstab_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "deshake", metadata, "filter_deshake.yml"); MLT_REGISTER_METADATA(mlt_service_filter_type, "vidstab", metadata, "filter_vidstab.yml"); } mlt-7.38.0/src/modules/vid.stab/filter_deshake.cpp000664 000000 000000 00000020600 15172202314 022006 0ustar00rootroot000000 000000 /* * filter_deshake.cpp * Copyright (C) 2013 Marco Gittler * Copyright (C) 2013 Jakub Ksiezniak * Copyright (C) 2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern "C" { #include "common.h" #include } #include #include #include typedef struct _deshake_data { bool initialized; VSMotionDetect md; VSTransformData td; VSSlidingAvgTrans avg; VSMotionDetectConfig mconf; VSTransformConfig tconf; mlt_position lastFrame; } DeshakeData; static void get_config(VSTransformConfig *tconf, VSMotionDetectConfig *mconf, mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); const char *filterName = mlt_properties_get(properties, "mlt_service"); memset(mconf, 0, sizeof(VSMotionDetectConfig)); *mconf = vsMotionDetectGetDefaultConfig(filterName); mconf->shakiness = mlt_properties_get_int(properties, "shakiness"); mconf->accuracy = mlt_properties_get_int(properties, "accuracy"); mconf->stepSize = mlt_properties_get_int(properties, "stepsize"); mconf->contrastThreshold = mlt_properties_get_double(properties, "mincontrast"); memset(tconf, 0, sizeof(VSTransformConfig)); *tconf = vsTransformGetDefaultConfig(filterName); tconf->smoothing = mlt_properties_get_int(properties, "smoothing"); tconf->maxShift = mlt_properties_get_int(properties, "maxshift"); tconf->maxAngle = mlt_properties_get_double(properties, "maxangle"); tconf->crop = (VSBorderType) mlt_properties_get_int(properties, "crop"); tconf->zoom = mlt_properties_get_int(properties, "zoom"); tconf->optZoom = mlt_properties_get_int(properties, "optzoom"); tconf->zoomSpeed = mlt_properties_get_double(properties, "zoomspeed"); tconf->relative = 1; // by default a bicubic interpolation is selected const char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "consumer.rescale"); tconf->interpolType = VS_BiCubic; if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0) tconf->interpolType = VS_Zero; else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0) tconf->interpolType = VS_Linear; else if (strcmp(interps, "bilinear") == 0) tconf->interpolType = VS_BiLinear; } static int check_config(mlt_filter filter, mlt_frame frame) { DeshakeData *data = static_cast(filter->child); VSTransformConfig new_tconf; VSMotionDetectConfig new_mconf; get_config(&new_tconf, &new_mconf, filter, frame); if (compare_transform_config(&data->tconf, &new_tconf) || compare_motion_config(&data->mconf, &new_mconf)) { return 1; } return 0; } static void init_deshake(DeshakeData *data, mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int *width, int *height) { VSFrameInfo fiIn, fiOut; vsFrameInfoInit(&fiIn, *width, *height, vs_format); vsFrameInfoInit(&fiOut, *width, *height, vs_format); get_config(&data->tconf, &data->mconf, filter, frame); vsMotionDetectInit(&data->md, &data->mconf, &fiIn); vsTransformDataInit(&data->td, &data->tconf, &fiIn, &fiOut); data->avg.initialized = 0; } static void clear_deshake(DeshakeData *data) { if (data->initialized) { vsMotionDetectionCleanup(&data->md); vsTransformDataCleanup(&data->td); } } static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); uint8_t *vs_image = NULL; VSPixelFormat vs_format = PF_NONE; // VS only works on progressive frames mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "consumer.progressive", 1); *format = validate_format(*format); DeshakeData *data = static_cast(filter->child); int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Convert the received image to a format vid.stab can handle if (!error) { vs_format = mltimage_to_vsimage(*format, *width, *height, *image, &vs_image); } if (vs_image) { // Service locks are for concurrency control mlt_service_lock(MLT_FILTER_SERVICE(filter)); // clear deshake data, when seeking or dropping frames mlt_position pos = mlt_filter_get_position(filter, frame); if (pos != data->lastFrame + 1 || check_config(filter, frame) == 1) { clear_deshake(data); data->initialized = false; } data->lastFrame = pos; if (!data->initialized) { init_deshake(data, filter, frame, vs_format, width, height); data->initialized = true; } VSMotionDetect *md = &data->md; VSTransformData *td = &data->td; LocalMotions localmotions; VSTransform motion; VSFrame vsFrame; vsFrameFillFromBuffer(&vsFrame, vs_image, &md->fi); vsMotionDetection(md, &localmotions, &vsFrame); const char *filterName = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "mlt_service"); motion = vsSimpleMotionsToTransform(md->fi, filterName, &localmotions); vs_vector_del(&localmotions); vsTransformPrepare(td, &vsFrame, &vsFrame); VSTransform t = vsLowPassTransforms(td, &data->avg, &motion); // mlt_log_warning(filter, "Trans: det: %f %f %f \n\t\t act: %f %f %f %f", // motion.x, motion.y, motion.alpha, // t.x, t.y, t.alpha, t.zoom); vsDoTransform(td, t); vsTransformFinish(td); vsimage_to_mltimage(vs_image, *image, *format, *width, *height); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); free_vsimage(vs_image, vs_format); } return error; } static mlt_frame process_filter(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } static void close_filter(mlt_filter filter) { DeshakeData *data = static_cast(filter->child); if (data) { clear_deshake(data); delete data; filter->child = NULL; } } extern "C" { mlt_filter filter_deshake_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = NULL; DeshakeData *data = new DeshakeData; memset(data, 0, sizeof(DeshakeData)); if ((filter = mlt_filter_new())) { filter->process = process_filter; filter->close = close_filter; filter->child = data; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); //properties for stabilize mlt_properties_set(properties, "shakiness", "4"); mlt_properties_set(properties, "accuracy", "4"); mlt_properties_set(properties, "stepsize", "6"); mlt_properties_set_double(properties, "mincontrast", 0.3); //properties for transform mlt_properties_set(properties, "smoothing", "15"); mlt_properties_set(properties, "maxshift", "-1"); mlt_properties_set(properties, "maxangle", "-1"); mlt_properties_set(properties, "crop", "0"); mlt_properties_set(properties, "zoom", "0"); mlt_properties_set(properties, "optzoom", "1"); mlt_properties_set_double(properties, "zoomspeed", 0.25); init_vslog(); return filter; } delete data; return NULL; } } mlt-7.38.0/src/modules/vid.stab/filter_deshake.yml000664 000000 000000 00000006237 15172202314 022037 0ustar00rootroot000000 000000 schema_version: 0.1 type: filter identifier: deshake title: Vid.Stab Deshake copyright: Jakub Ksiezniak creator: Georg Martius version: 1 license: GPLv2 language: en url: http://public.hronopik.de/vid.stab/ tags: - Video description: Stabilize Video (for wiggly/rolling video) notes: > Deshakes a video clip by extracting relative transformations of subsequent frames and transforms the high-frequency away. This is a single pass version of the vidstab filter. parameters: - identifier: shakiness title: Shakiness type: integer description: How shaky the video is. readonly: no required: no minimum: 1 maximum: 10 default: 4 mutable: yes widget: spinner - identifier: accuracy title: Accuracy type: integer description: The accuracy of shakiness detection. readonly: no required: no minimum: 1 maximum: 15 default: 4 mutable: yes widget: spinner - identifier: stepsize title: Stepsize type: integer description: The step size of the search process. readonly: no required: no minimum: 0 maximum: 100 default: 6 mutable: yes widget: spinner - identifier: mincontrast title: Minimum Contrast type: float description: Below this contrast, a field is discarded. readonly: no required: no minimum: 0 maximum: 1 default: 0.3 mutable: yes widget: spinner - identifier: smoothing title: Smoothing type: integer description: Number of frames for lowpass filtering (2N + 1 frames) readonly: no required: no minimum: 0 maximum: 100 default: 15 mutable: yes widget: spinner - identifier: maxshift title: Maxshift type: integer description: Maximum number of pixels to transform the image. -1 = no limit unit: pixels readonly: no required: no minimum: -1 maximum: 1000 default: -1 mutable: yes widget: spinner - identifier: maxangle title: Maxangle type: float description: Maximum angle to rotate, -1 = no limit unit: radians readonly: no required: no minimum: -1 maximum: 3.142 default: -1 mutable: yes widget: spinner - identifier: crop title: Crop type: integer description: 0 = keep border, 1 = black background readonly: no required: no minimum: 0 maximum: 1 default: 0 mutable: yes widget: spinner - identifier: zoom title: Zoom type: integer description: Additional zoom amount unit: percent readonly: no required: no minimum: -500 maximum: 500 default: 0 mutable: yes widget: spinner - identifier: optzoom title: Optimal Zoom type: integer description: Automatically determine optimal zoom. 1 - static zoom, 2 - adaptive zoom readonly: no required: no minimum: 0 maximum: 2 default: 1 mutable: yes widget: spinner - identifier: zoomspeed title: Optimal Zoom Speed type: float description: Zoom per frame (used when optzoom = 2) unit: percent readonly: no required: no minimum: 0 maximum: 1 default: 0.25 mutable: yes widget: spinner mlt-7.38.0/src/modules/vid.stab/filter_vidstab.cpp000664 000000 000000 00000040126 15172202314 022043 0ustar00rootroot000000 000000 /* * filter_vidstab.cpp * Copyright (C) 2013 Marco Gittler * Copyright (C) 2013 Jakub Ksiezniak * Copyright (C) 2014-2023 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern "C" { #include "common.h" #include #include #include } #include #include #include typedef struct { VSMotionDetect md; FILE *results; mlt_position last_position; } vs_analyze; typedef struct { VSTransformData td; VSTransformConfig conf; VSTransformations trans; VSPixelFormat format; } vs_apply; typedef struct { vs_analyze *analyze_data; vs_apply *apply_data; } vs_data; static void get_transform_config(VSTransformConfig *conf, mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); const char *filterName = mlt_properties_get(properties, "mlt_service"); *conf = vsTransformGetDefaultConfig(filterName); conf->smoothing = mlt_properties_get_int(properties, "smoothing"); conf->maxShift = mlt_properties_get_int(properties, "maxshift"); conf->maxAngle = mlt_properties_get_double(properties, "maxangle"); conf->crop = (VSBorderType) mlt_properties_get_int(properties, "crop"); conf->zoom = mlt_properties_get_int(properties, "zoom"); conf->optZoom = mlt_properties_get_int(properties, "optzoom"); conf->zoomSpeed = mlt_properties_get_double(properties, "zoomspeed"); conf->relative = mlt_properties_get_int(properties, "relative"); conf->invert = mlt_properties_get_int(properties, "invert"); if (mlt_properties_get_int(properties, "tripod") != 0) { // Virtual tripod mode: relative=False, smoothing=0 conf->relative = 0; conf->smoothing = 0; } // by default a bicubic interpolation is selected const char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "consumer.rescale"); conf->interpolType = VS_BiCubic; if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0) conf->interpolType = VS_Zero; else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0) conf->interpolType = VS_Linear; else if (strcmp(interps, "bilinear") == 0) conf->interpolType = VS_BiLinear; } static int check_apply_config(mlt_filter filter, mlt_frame frame, VSPixelFormat format) { vs_apply *apply_data = ((vs_data *) filter->child)->apply_data; if (apply_data) { VSTransformConfig new_conf; memset(&new_conf, 0, sizeof(VSTransformConfig)); get_transform_config(&new_conf, filter, frame); return format != apply_data->format || compare_transform_config(&apply_data->conf, &new_conf); } return 0; } static void destroy_apply_data(vs_apply *apply_data) { if (apply_data) { vsTransformDataCleanup(&apply_data->td); vsTransformationsCleanup(&apply_data->trans); free(apply_data); } } static void init_apply_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); vs_data *data = (vs_data *) filter->child; vs_apply *apply_data = (vs_apply *) calloc(1, sizeof(vs_apply)); char *results = mlt_properties_get(properties, "results"); char *filename = mlt_properties_get(properties, "filename"); // The XML producer can convert "filename" from relative to absolute, but // it does not do the same for "results". Therefore, if both exist and // "filename" ends with "results", then use "filename" instead. if (results && filename && strlen(filename) >= strlen(results) && !strcmp(&filename[strlen(filename) - strlen(results)], results)) { filename = mlt_properties_get(properties, "filename"); } else { filename = mlt_properties_get(properties, "results"); } mlt_log_info(MLT_FILTER_SERVICE(filter), "Load results from %s\n", filename); // Initialize the VSTransformConfig memset(apply_data, 0, sizeof(vs_apply)); get_transform_config(&apply_data->conf, filter, frame); // Initialize VSTransformData VSFrameInfo fi_src, fi_dst; vsFrameInfoInit(&fi_src, width, height, vs_format); vsFrameInfoInit(&fi_dst, width, height, vs_format); vsTransformDataInit(&apply_data->td, &apply_data->conf, &fi_src, &fi_dst); apply_data->format = vs_format; // Initialize VSTransformations vsTransformationsInit(&apply_data->trans); // Load the motions from the analyze step and convert them to VSTransformations FILE *f = mlt_fopen(filename, "r"); VSManyLocalMotions mlms; if (f && vsReadLocalMotionsFile(f, &mlms) == VS_OK) { int i = 0; mlt_log_info(MLT_FILTER_SERVICE(filter), "Successfully loaded %d motions\n", vs_vector_size(&mlms)); vsLocalmotions2Transforms(&apply_data->td, &mlms, &apply_data->trans); vsPreprocessTransforms(&apply_data->td, &apply_data->trans); // Free the MultipleLocalMotions for (i = 0; i < vs_vector_size(&mlms); i++) { LocalMotions *lms = (LocalMotions *) vs_vector_get(&mlms, i); if (lms) { vs_vector_del(lms); } } vs_vector_del(&mlms); data->apply_data = apply_data; } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Can not read results file: %s\n", filename); destroy_apply_data(apply_data); data->apply_data = NULL; } if (f) { fclose(f); } } void destroy_analyze_data(vs_analyze *analyze_data) { if (analyze_data) { vsMotionDetectionCleanup(&analyze_data->md); if (analyze_data->results) { fclose(analyze_data->results); analyze_data->results = NULL; } free(analyze_data); } } static void init_analyze_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); vs_data *data = (vs_data *) filter->child; vs_analyze *analyze_data = (vs_analyze *) calloc(1, sizeof(vs_analyze)); memset(analyze_data, 0, sizeof(vs_analyze)); // Initialize a VSMotionDetectConfig const char *filterName = mlt_properties_get(properties, "mlt_service"); VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig(filterName); conf.shakiness = mlt_properties_get_int(properties, "shakiness"); conf.accuracy = mlt_properties_get_int(properties, "accuracy"); conf.stepSize = mlt_properties_get_int(properties, "stepsize"); conf.contrastThreshold = mlt_properties_get_double(properties, "mincontrast"); conf.show = mlt_properties_get_int(properties, "show"); conf.virtualTripod = mlt_properties_get_int(properties, "tripod"); // Initialize a VSFrameInfo VSFrameInfo fi; vsFrameInfoInit(&fi, width, height, vs_format); // Initialize the saved VSMotionDetect vsMotionDetectInit(&analyze_data->md, &conf, &fi); #ifdef ASCII_SERIALIZATION_MODE analyze_data->md.serializationMode = ASCII_SERIALIZATION_MODE; #endif // Initialize the file to save results to char *filename = mlt_properties_get(properties, "filename"); analyze_data->results = mlt_fopen(filename, "w"); if (vsPrepareFile(&analyze_data->md, analyze_data->results) != VS_OK) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Can not write to results file: %s\n", filename); destroy_analyze_data(analyze_data); data->analyze_data = NULL; } else { data->analyze_data = analyze_data; } } static int apply_results(mlt_filter filter, mlt_frame frame, uint8_t *vs_image, VSPixelFormat vs_format, int width, int height) { int error = 0; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); vs_data *data = (vs_data *) filter->child; if (check_apply_config(filter, frame, vs_format) || mlt_properties_get_int(properties, "reload")) { mlt_properties_set_int(properties, "reload", 0); destroy_apply_data(data->apply_data); data->apply_data = NULL; } // Init transform data if necessary (first time) if (!data->apply_data) { init_apply_data(filter, frame, vs_format, width, height); } if (data->apply_data) { // Apply transformations to this image VSTransformData *td = &data->apply_data->td; VSTransformations *trans = &data->apply_data->trans; VSFrame vsFrame; vsFrameFillFromBuffer(&vsFrame, vs_image, vsTransformGetSrcFrameInfo(td)); trans->current = mlt_filter_get_position(filter, frame); vsTransformPrepare(td, &vsFrame, &vsFrame); VSTransform t = vsGetNextTransform(td, trans); vsDoTransform(td, t); vsTransformFinish(td); } return error; } static void analyze_image(mlt_filter filter, mlt_frame frame, uint8_t *vs_image, VSPixelFormat vs_format, int width, int height) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); vs_data *data = (vs_data *) filter->child; mlt_position pos = mlt_filter_get_position(filter, frame); // If any frames are skipped, analysis data will be incomplete. if (data->analyze_data && pos != data->analyze_data->last_position + 1) { mlt_log_error(MLT_FILTER_SERVICE(filter), "Bad frame sequence pos %d last_position %d\n", pos, data->analyze_data->last_position); destroy_analyze_data(data->analyze_data); data->analyze_data = NULL; } if (!data->analyze_data && pos == 0) { // Analysis must start on the first frame init_analyze_data(filter, frame, vs_format, width, height); } if (data->analyze_data) { // Initialize the VSFrame to be analyzed. VSMotionDetect *md = &data->analyze_data->md; LocalMotions localmotions; VSFrame vsFrame; vsFrameFillFromBuffer(&vsFrame, vs_image, &md->fi); // Detect and save motions. if (vsMotionDetection(md, &localmotions, &vsFrame) == VS_OK) { vsWriteToFile(md, data->analyze_data->results, &localmotions); vs_vector_del(&localmotions); } else { mlt_log_error(MLT_FILTER_SERVICE(filter), "Motion detection failed\n"); destroy_analyze_data(data->analyze_data); data->analyze_data = NULL; } // Publish the motions if this is the last frame. if (pos + 1 == mlt_filter_get_length2(filter, frame)) { mlt_log_info(MLT_FILTER_SERVICE(filter), "Analysis complete\n"); destroy_analyze_data(data->analyze_data); data->analyze_data = NULL; mlt_properties_set(properties, "results", mlt_properties_get(properties, "filename")); } else if (data->analyze_data) { data->analyze_data->last_position = pos; } } } static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); uint8_t *vs_image = NULL; VSPixelFormat vs_format = PF_NONE; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } // VS only works on progressive frames mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "consumer.progressive", 1); *format = validate_format(*format); int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Convert the received image to a format vid.stab can handle if (!error) { vs_format = mltimage_to_vsimage(*format, *width, *height, *image, &vs_image); } if (vs_image) { mlt_service_lock(MLT_FILTER_SERVICE(filter)); char *results = mlt_properties_get(properties, "results"); if (results && strcmp(results, "")) { apply_results(filter, frame, vs_image, vs_format, *width, *height); vsimage_to_mltimage(vs_image, *image, *format, *width, *height); } else if (!mlt_properties_get(properties, "analyze") || mlt_properties_get_int(properties, "analyze")) { analyze_image(filter, frame, vs_image, vs_format, *width, *height); if (mlt_properties_get_int(properties, "show") == 1) { vsimage_to_mltimage(vs_image, *image, *format, *width, *height); } } mlt_service_unlock(MLT_FILTER_SERVICE(filter)); free_vsimage(vs_image, vs_format); } return error; } static mlt_frame process_filter(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } static void filter_close(mlt_filter filter) { vs_data *data = (vs_data *) filter->child; if (data) { if (data->analyze_data) destroy_analyze_data(data->analyze_data); if (data->apply_data) destroy_apply_data(data->apply_data); free(data); } filter->close = NULL; filter->child = NULL; filter->parent.close = NULL; mlt_service_close(&filter->parent); } extern "C" { mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); vs_data *data = (vs_data *) calloc(1, sizeof(vs_data)); if (filter && data) { data->analyze_data = NULL; data->apply_data = NULL; filter->close = filter_close; filter->child = data; filter->process = process_filter; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); //properties for analyze mlt_properties_set(properties, "filename", "vidstab.trf"); mlt_properties_set(properties, "shakiness", "4"); mlt_properties_set(properties, "accuracy", "4"); mlt_properties_set(properties, "stepsize", "6"); mlt_properties_set(properties, "algo", "1"); mlt_properties_set_double(properties, "mincontrast", 0.3); mlt_properties_set(properties, "show", "0"); mlt_properties_set(properties, "tripod", "0"); // properties for apply mlt_properties_set(properties, "smoothing", "15"); mlt_properties_set(properties, "maxshift", "-1"); mlt_properties_set(properties, "maxangle", "-1"); mlt_properties_set(properties, "crop", "0"); mlt_properties_set(properties, "invert", "0"); mlt_properties_set(properties, "relative", "1"); mlt_properties_set(properties, "zoom", "0"); mlt_properties_set(properties, "optzoom", "1"); mlt_properties_set_double(properties, "zoomspeed", 0.25); mlt_properties_set(properties, "reload", "0"); mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION); init_vslog(); } else { if (filter) { mlt_filter_close(filter); } if (data) { free(data); } filter = NULL; } return filter; } } mlt-7.38.0/src/modules/vid.stab/filter_vidstab.yml000664 000000 000000 00000014367 15172202314 022072 0ustar00rootroot000000 000000 schema_version: 0.3 type: filter identifier: vidstab title: Vid.Stab Detect and Transform copyright: Jakub Ksiezniak creator: Marco Gittler version: 2 license: GPL language: en url: http://public.hronopik.de/vid.stab/ tags: - Video description: Stabilize Video (for wiggly/rolling video) notes: > This filter requires two passes. The first pass performs analysis and stores the result in a file. Upon successful completion of the analysis, the "results" property is updated with the name of the file storing the results. The second pass applies the results to the image. To use with melt, use 'melt ... -consumer xml:output.mlt all=1 real_time=-1' for the first pass. Parallel processing (real_time < -1 or > 1) is not supported for the first pass. For the second pass, use output.mlt as the input. parameters: - identifier: results title: Analysis Results type: string description: > Set after analysis. Used during application. A set of image motion information that describes the analyzed clip. When results are not supplied, the filter computes the results and stores them in a file. This property is updated with the name of that file when the last frame has been processed. mutable: no - identifier: filename title: Target File Name type: string description: > Used during analysis. The name of the file to store the analysis results in. required: no readonly: no default: vidstab.trf widget: fileopen - identifier: shakiness title: Shakiness type: integer description: > Used during analysis. How shaky the video is. readonly: no required: no minimum: 1 maximum: 10 default: 4 mutable: no widget: spinner - identifier: accuracy title: Accuracy type: integer description: > Used during analysis. The accuracy of shakiness detection. readonly: no required: no minimum: 1 maximum: 15 default: 4 mutable: no widget: spinner - identifier: stepsize title: Stepsize type: integer description: > Used during analysis. The step size of the search process. readonly: no required: no minimum: 0 maximum: 100 default: 6 mutable: no widget: spinner - identifier: mincontrast title: Minimum Contrast type: float description: > Used during analysis. Below this contrast, a field is discarded. readonly: no required: no minimum: 0 maximum: 1 default: 0.3 mutable: no widget: spinner - identifier: show title: Show type: integer description: > Used during analysis. 0 = draw nothing 1 or 2 = show fields and transforms readonly: no required: no minimum: 0 maximum: 2 default: 0 mutable: no widget: spinner - identifier: tripod title: Tripod type: integer description: > Used during analysis and application. if 0, tripod mode is disabled. if > 0, specifies the frame to be used as a reference frame for tripod mode During application, relative and smoothing properties are both ignored if tripod mode is in use. readonly: no required: no minimum: 0 maximum: 100000 default: 0 mutable: no widget: spinner - identifier: smoothing title: Smoothing type: integer description: > Used during application. Number of frames for lowpass filtering (2N + 1 frames) readonly: no required: no minimum: 0 maximum: 100 default: 15 mutable: yes widget: spinner - identifier: maxshift title: Maxshift type: integer description: > Used during application. Maximum number of pixels to transform the image. -1 = no limit unit: pixels readonly: no required: no minimum: -1 maximum: 1000 default: -1 mutable: yes widget: spinner - identifier: maxangle title: Maxangle type: float description: > Used during application. Maximum angle to rotate, -1 = no limit unit: radians readonly: no required: no minimum: -1 maximum: 3.142 default: -1 mutable: yes widget: spinner - identifier: crop title: Crop type: boolean description: > Used during application. 0 = keep border, 1 = black background readonly: no required: no default: 0 mutable: yes - identifier: invert title: Invert type: boolean description: > Used during application. Invert transforms readonly: no required: no default: 0 mutable: yes - identifier: relative title: Relative type: boolean description: > Used during application. Consider transforms as absolute (0) or relative (1) readonly: no required: no default: 1 mutable: yes - identifier: zoom title: Zoom type: integer description: > Used during application. Additional zoom amount unit: percent readonly: no required: no minimum: -500 maximum: 500 default: 0 mutable: yes widget: spinner - identifier: optzoom title: Optimal Zoom type: integer description: > Used during application. Automatically determine optimal zoom. 1 - static zoom, 2 - adaptive zoom readonly: no required: no minimum: 0 maximum: 2 default: 1 mutable: yes widget: spinner - identifier: zoomspeed title: Optimal Zoom Speed type: float description: > Used during application. Zoom per frame (used when optzoom = 2) unit: percent readonly: no required: no minimum: 0 maximum: 1 default: 0.25 mutable: yes widget: spinner - identifier: reload title: Reload Results description: > The application should set this to 1 when it updates the results property to indicate that the results should be reloaded. type: boolean mutable: yes - identifier: analyze title: Commence analysis description: > This is optional, but it can help applications avoid race conditions writing to the file. When not set, it does not affect the start of analysis. When set, analysis only starts and the file written if true. type: boolean mlt-7.38.0/src/modules/vid.stab/gpl000664 000000 000000 00000000000 15172202314 017026 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/vorbis/000775 000000 000000 00000000000 15172202314 016125 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/vorbis/CMakeLists.txt000664 000000 000000 00000001252 15172202314 020665 0ustar00rootroot000000 000000 add_library(mltvorbis MODULE factory.c producer_vorbis.c) file(GLOB YML "*.yml") add_custom_target(Other_vorbis_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltvorbis) target_compile_options(mltvorbis PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltvorbis PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltvorbis PRIVATE mlt PkgConfig::vorbis PkgConfig::vorbisfile) set_target_properties(mltvorbis PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltvorbis LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES producer_vorbis.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/vorbis) mlt-7.38.0/src/modules/vorbis/factory.c000664 000000 000000 00000003136 15172202314 017743 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltvorbis_export.h" #include #include #include extern mlt_producer producer_vorbis_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/vorbis/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTVORBIS_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_producer_type, "vorbis", producer_vorbis_init); MLT_REGISTER_METADATA(mlt_service_producer_type, "vorbis", metadata, "producer_vorbis.yml"); } mlt-7.38.0/src/modules/vorbis/producer_vorbis.c000664 000000 000000 00000027207 15172202314 021510 0ustar00rootroot000000 000000 /* * producer_vorbis.c -- vorbis producer * Copyright (C) 2003-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // MLT Header files #include #include #include // vorbis Header files #include #include // System header files #include #include #include // Forward references. static int producer_open(mlt_producer this, mlt_profile profile, char *file); static int producer_get_frame(mlt_producer this, mlt_frame_ptr frame, int index); /** Structure for metadata reading */ typedef struct _sw_metadata sw_metadata; struct _sw_metadata { char *name; char *content; }; static sw_metadata *vorbis_metadata_from_str(char *str) { sw_metadata *meta = NULL; int i; for (i = 0; str[i]; i++) { str[i] = tolower(str[i]); if (str[i] == '=') { str[i] = '\0'; meta = malloc(sizeof(sw_metadata)); meta->name = malloc(strlen(str) + 18); sprintf(meta->name, "meta.attr.%s.markup", str); meta->content = strdup(&str[i + 1]); break; } } return meta; } /** Constructor for libvorbis. */ mlt_producer producer_vorbis_init(mlt_profile profile, mlt_service_type type, const char *id, char *file) { mlt_producer this = NULL; // Check that we have a non-NULL argument if (file != NULL) { // Construct the producer this = calloc(1, sizeof(struct mlt_producer_s)); // Initialise it if (mlt_producer_init(this, NULL) == 0) { // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES(this); // Set the resource property (required for all producers) mlt_properties_set(properties, "resource", file); // Register our get_frame implementation this->get_frame = producer_get_frame; // Open the file if (producer_open(this, profile, file) != 0) { // Clean up mlt_producer_close(this); this = NULL; } } } return this; } /** Destructor for ogg files. */ static void producer_file_close(void *file) { if (file != NULL) { // Close the ogg vorbis structure ov_clear(file); // Free the memory free(file); } } /** Open the file. */ static int producer_open(mlt_producer this, mlt_profile profile, char *file) { // FILE pointer for file FILE *input = mlt_fopen(file, "rb"); // Error code to return int error = input == NULL; // Continue if file is open if (error == 0) { // OggVorbis file structure OggVorbis_File *ov = calloc(1, sizeof(OggVorbis_File)); // Attempt to open the stream error = ov == NULL || ov_open(input, ov, NULL, 0) != 0; // Assign to producer properties if successful if (error == 0) { // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES(this); // Assign the ov structure mlt_properties_set_data(properties, "ogg_vorbis_file", ov, 0, producer_file_close, NULL); // Read metadata sw_metadata *metadata = NULL; char **ptr = ov_comment(ov, -1)->user_comments; while (*ptr) { metadata = vorbis_metadata_from_str(*ptr); if (metadata != NULL) { mlt_properties_set(properties, metadata->name, metadata->content); free(metadata->name); free(metadata->content); free(metadata); } ++ptr; } if (ov_seekable(ov)) { // Get the length of the file double length = ov_time_total(ov, -1); // We will treat everything with the producer fps double fps = mlt_profile_fps(profile); // Set out and length of file mlt_properties_set_position(properties, "out", (length * fps) - 1); mlt_properties_set_position(properties, "length", (length * fps)); // Get the vorbis info vorbis_info *vi = ov_info(ov, -1); mlt_properties_set_int(properties, "audio_frequency", (int) vi->rate); mlt_properties_set_int(properties, "audio_channels", vi->channels); // Set some media metadata mlt_properties_set_int(properties, "meta.media.nb_streams", 1); mlt_properties_set_int(properties, "audio_index", 0); mlt_properties_set(properties, "meta.media.0.stream.type", "audio"); mlt_properties_set(properties, "meta.media.0.codec.name", "vorbis"); mlt_properties_set(properties, "meta.media.0.codec.long_name", "Vorbis"); } } else { // Clean up free(ov); // Must close input file when open fails fclose(input); } } return error; } /** Convert a frame position to a time code. */ static double producer_time_of_frame(mlt_producer this, mlt_position position) { return (double) position / mlt_producer_get_fps(this); } /** Get the audio from a frame. */ static int producer_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples) { // Obtain the frame number of this frame mlt_position position = mlt_frame_original_position(frame); // Get the producer mlt_producer this = mlt_frame_pop_audio(frame); // Get the producer properties mlt_properties properties = MLT_PRODUCER_PROPERTIES(this); mlt_service_lock(MLT_PRODUCER_SERVICE(this)); // Get the ogg vorbis file OggVorbis_File *ov = mlt_properties_get_data(properties, "ogg_vorbis_file", NULL); // Obtain the expected frame number mlt_position expected = mlt_properties_get_position(properties, "audio_expected"); // Get the fps for this producer double fps = mlt_producer_get_fps(this); // Get the vorbis info vorbis_info *vi = ov_info(ov, -1); // Obtain the audio buffer int16_t *audio_buffer = mlt_properties_get_data(properties, "audio_buffer", NULL); // Get amount of audio used int audio_used = mlt_properties_get_int(properties, "audio_used"); // Number of frames to ignore (for ffwd) int ignore = 0; // Flag for paused (silence) int paused = 0; // Check for audio buffer and create if necessary if (audio_buffer == NULL) { // Allocate the audio buffer audio_buffer = mlt_pool_alloc(131072 * sizeof(int16_t)); // And store it on properties for reuse mlt_properties_set_data(properties, "audio_buffer", audio_buffer, 0, mlt_pool_release, NULL); } // Seek if necessary if (position != expected) { if (position + 1 == expected) { // We're paused - silence required paused = 1; } else if (position > expected && (position - expected) < 250) { // Fast forward - seeking is inefficient for small distances - just ignore following frames ignore = position - expected; } else { // Seek to the required position ov_time_seek(ov, producer_time_of_frame(this, position)); expected = position; audio_used = 0; } } // Return info in frame *frequency = vi->rate; *channels = vi->channels; // Get the audio if required if (!paused) { // Bitstream section int current_section; // Get the number of samples for the current frame *samples = mlt_audio_calculate_frame_samples(fps, *frequency, expected++); while (*samples > audio_used) { // Read the samples int bytes = ov_read(ov, (char *) (&audio_buffer[audio_used * 2]), 4096, 0, 2, 1, ¤t_section); // Break if error or eof if (bytes <= 0) break; // Increment number of samples used audio_used += bytes / (sizeof(int16_t) * *channels); // Handle ignore while (ignore && audio_used >= *samples) { ignore--; audio_used -= *samples; memmove(audio_buffer, &audio_buffer[*samples * *channels], audio_used * sizeof(int16_t) * *channels); *samples = mlt_audio_calculate_frame_samples(fps, *frequency, expected++); } } // Now handle the audio if we have enough if (audio_used >= *samples) { int size = *samples * *channels * sizeof(int16_t); *format = mlt_audio_s16; *buffer = mlt_pool_alloc(size); memcpy(*buffer, audio_buffer, size); audio_used -= *samples; memmove(audio_buffer, &audio_buffer[*samples * *channels], audio_used * *channels * sizeof(int16_t)); mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release); } else { mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); audio_used = 0; } // Store the number of audio samples still available mlt_properties_set_int(properties, "audio_used", audio_used); } else { // Get silence and don't touch the context *samples = mlt_audio_calculate_frame_samples(fps, *frequency, position); mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples); } // Regardless of speed, we expect to get the next frame (cos we ain't too bright) mlt_properties_set_position(properties, "audio_expected", position + 1); mlt_service_unlock(MLT_PRODUCER_SERVICE(this)); return 0; } /** Our get frame implementation. */ static int producer_get_frame(mlt_producer this, mlt_frame_ptr frame, int index) { // Create an empty frame *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(this)); // Update timecode on the frame we're creating mlt_frame_set_position(*frame, mlt_producer_position(this)); // Set up the audio mlt_frame_push_audio(*frame, this); mlt_frame_push_audio(*frame, producer_get_audio); // Pass audio properties to the frame mlt_properties_pass_list(MLT_FRAME_PROPERTIES(*frame), MLT_PRODUCER_PROPERTIES(this), "frequency, channels"); // Calculate the next timecode mlt_producer_prepare_next(this); return 0; } mlt-7.38.0/src/modules/vorbis/producer_vorbis.yml000664 000000 000000 00000000675 15172202314 022067 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: vorbis title: Ogg Vorbis version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio description: | OGG Vorbis file reader. parameters: - identifier: resource argument: yes title: File type: string description: File to use (only .ogg supported at the moment) readonly: no required: yes mutable: no widget: fileopen mlt-7.38.0/src/modules/xine/000775 000000 000000 00000000000 15172202314 015564 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/xine/CMakeLists.txt000664 000000 000000 00000002735 15172202314 020333 0ustar00rootroot000000 000000 add_library(mltxine MODULE common.c common.h factory.c deinterlace.c deinterlace.h yadif.c yadif.h filter_deinterlace.c link_deinterlace.c ) file(GLOB YML "*.yml") add_custom_target(Other_xine_Files SOURCES ${YML} ) include(GenerateExportHeader) generate_export_header(mltxine) target_compile_options(mltxine PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltxine PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltxine PRIVATE mlt ${CMAKE_DL_LIBS}) set_target_properties(mltxine PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_definitions(mltxine PRIVATE PIC) if(CPU_MMX) target_compile_definitions(mltxine PRIVATE USE_MMX) target_sources(mltxine PRIVATE cpu_accel.c) if(CMAKE_C_COMPILER_ID MATCHES "GNU") # avoid crash in yadif filter_line_sse2 target_compile_options(mltxine PRIVATE -fno-tree-dominator-opts -fno-tree-pre) endif() endif() if(CPU_SSE) target_compile_definitions(mltxine PRIVATE USE_SSE) endif() if(CPU_SSE2) target_compile_definitions(mltxine PRIVATE USE_SSE2) endif() if(CPU_X86_32) target_compile_definitions(mltxine PRIVATE ARCH_X86) endif() if(CPU_X86_64) target_compile_definitions(mltxine PRIVATE ARCH_X86_64) endif() set_target_properties(mltxine PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltxine LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES filter_deinterlace.yml link_deinterlace.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/xine ) mlt-7.38.0/src/modules/xine/attributes.h000664 000000 000000 00000002721 15172202314 020125 0ustar00rootroot000000 000000 /* * attributes.h * Copyright (C) 1999-2000 Aaron Holtzman * * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. * * mpeg2dec is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * mpeg2dec is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* use gcc attribs to align critical data structures */ #ifndef ATTRIBUTE_H_ #define ATTRIBUTE_H_ #ifdef ATTRIBUTE_ALIGNED_MAX #define ATTR_ALIGN(align) __attribute__ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX < align) ? ATTRIBUTE_ALIGNED_MAX : align))) #else #define ATTR_ALIGN(align) #endif /* disable GNU __attribute__ extension, when not compiling with GNU C */ #if defined(__GNUC__) || defined (__ICC) #ifndef ATTRIBUTE_PACKED #define ATTRIBUTE_PACKED 1 #endif #else #undef ATTRIBUTE_PACKED #ifndef __attribute__ #define __attribute__(x) /**/ #endif /* __attribute __*/ #endif #endif /* ATTRIBUTE_H_ */ mlt-7.38.0/src/modules/xine/common.c000664 000000 000000 00000023112 15172202314 017217 0ustar00rootroot000000 000000 /* * common.c * Copyright (C) 2023 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "common.h" #include "deinterlace.h" #include "yadif.h" static yadif_filter *init_yadif(int width, int height) { yadif_filter *yadif = mlt_pool_alloc(sizeof(*yadif)); yadif->cpu = 0; // Pure C #ifdef USE_SSE yadif->cpu |= AVS_CPU_INTEGER_SSE; #endif #ifdef USE_SSE2 yadif->cpu |= AVS_CPU_SSE2; #endif // Create intermediate planar planes yadif->yheight = height; yadif->ywidth = width; yadif->uvwidth = yadif->ywidth / 2; yadif->ypitch = (yadif->ywidth + 15) / 16 * 16; yadif->uvpitch = (yadif->uvwidth + 15) / 16 * 16; yadif->ysrc = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->ypitch); yadif->usrc = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->vsrc = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->yprev = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->ypitch); yadif->uprev = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->vprev = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->ynext = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->ypitch); yadif->unext = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->vnext = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->ydest = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->ypitch); yadif->udest = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->vdest = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); return yadif; } static void close_yadif(yadif_filter *yadif) { mlt_pool_release(yadif->ysrc); mlt_pool_release(yadif->usrc); mlt_pool_release(yadif->vsrc); mlt_pool_release(yadif->yprev); mlt_pool_release(yadif->uprev); mlt_pool_release(yadif->vprev); mlt_pool_release(yadif->ynext); mlt_pool_release(yadif->unext); mlt_pool_release(yadif->vnext); mlt_pool_release(yadif->ydest); mlt_pool_release(yadif->udest); mlt_pool_release(yadif->vdest); mlt_pool_release(yadif); #if defined(__GNUC__) && !defined(PIC) // Set SSSE3 bit to cpu asm("mov $1, %%eax \n\t" "push %%ebx \n\t" "cpuid \n\t" "pop %%ebx \n\t" "mov %%ecx, %%edx \n\t" "shr $9, %%edx \n\t" "and $1, %%edx \n\t" "shl $9, %%edx \n\t" "and $511, %%ebx \n\t" "or %%edx, %%ebx \n\t" : "=b"(yadif->cpu) : "p"(yadif->cpu) : "%eax", "%ecx", "%edx"); #endif } mlt_deinterlacer supported_method(mlt_deinterlacer method) { // Round the method down to the next one that is supported if (method <= mlt_deinterlacer_onefield) method = mlt_deinterlacer_onefield; else if (method <= mlt_deinterlacer_linearblend) method = mlt_deinterlacer_linearblend; else if (method <= mlt_deinterlacer_weave) method = mlt_deinterlacer_weave; else if (method <= mlt_deinterlacer_bob) method = mlt_deinterlacer_bob; else if (method <= mlt_deinterlacer_greedy) method = mlt_deinterlacer_greedy; else if (method <= mlt_deinterlacer_yadif_nospatial) method = mlt_deinterlacer_yadif_nospatial; else if (method <= mlt_deinterlacer_invalid) method = mlt_deinterlacer_yadif; return method; } int deinterlace_image( mlt_image dst, mlt_image src, mlt_image prev, mlt_image next, int tff, mlt_deinterlacer method) { if (!dst || !src || !dst->data || !src->data) { return 1; } if (method >= mlt_deinterlacer_yadif_nospatial && (!prev || !next || !prev->data || !next->data)) { method = mlt_deinterlacer_linearblend; } else if ((method == mlt_deinterlacer_weave || method == mlt_deinterlacer_greedy) && (!next || !next->data)) { method = mlt_deinterlacer_linearblend; } if (method == mlt_deinterlacer_bob) { deinterlace_yuv(dst->data, (uint8_t **) &src->data, src->width * 2, src->height, DEINTERLACE_BOB); } else if (method == mlt_deinterlacer_weave) { uint8_t *src_array[2]; src_array[0] = src->data; src_array[1] = next->data; deinterlace_yuv(dst->data, src_array, src->width * 2, src->height, DEINTERLACE_WEAVE); } else if (method == mlt_deinterlacer_onefield) { deinterlace_yuv(dst->data, (uint8_t **) &src->data, src->width * 2, src->height, DEINTERLACE_ONEFIELD); } else if (method == mlt_deinterlacer_linearblend) { deinterlace_yuv(dst->data, (uint8_t **) &src->data, src->width * 2, src->height, DEINTERLACE_LINEARBLEND); } else if (method == mlt_deinterlacer_greedy) { uint8_t *src_array[2]; src_array[0] = src->data; src_array[1] = next->data; deinterlace_yuv(dst->data, src_array, src->width * 2, src->height, DEINTERLACE_GREEDY); } else if (method >= mlt_deinterlacer_yadif_nospatial) { yadif_filter *yadif = init_yadif(src->width, src->height); if (yadif) { const int pitch = src->width << 1; const int parity = 0; const int YADIF_MODE_TEMPORAL_SPATIAL = 0; const int YADIF_MODE_TEMPORAL = 2; int mode = YADIF_MODE_TEMPORAL_SPATIAL; if (method == mlt_deinterlacer_yadif_nospatial) { mode = YADIF_MODE_TEMPORAL; } // Convert packed to planar YUY2ToPlanes(src->data, pitch, src->width, src->height, yadif->ysrc, yadif->ypitch, yadif->usrc, yadif->vsrc, yadif->uvpitch, yadif->cpu); YUY2ToPlanes(prev->data, pitch, src->width, src->height, yadif->yprev, yadif->ypitch, yadif->uprev, yadif->vprev, yadif->uvpitch, yadif->cpu); YUY2ToPlanes(next->data, pitch, src->width, src->height, yadif->ynext, yadif->ypitch, yadif->unext, yadif->vnext, yadif->uvpitch, yadif->cpu); // Deinterlace each plane filter_plane(mode, yadif->ydest, yadif->ypitch, yadif->yprev, yadif->ysrc, yadif->ynext, yadif->ypitch, src->width, src->height, parity, tff, yadif->cpu); filter_plane(mode, yadif->udest, yadif->uvpitch, yadif->uprev, yadif->usrc, yadif->unext, yadif->uvpitch, src->width >> 1, src->height, parity, tff, yadif->cpu); filter_plane(mode, yadif->vdest, yadif->uvpitch, yadif->vprev, yadif->vsrc, yadif->vnext, yadif->uvpitch, src->width >> 1, src->height, parity, tff, yadif->cpu); // Convert planar to packed YUY2FromPlanes(dst->data, pitch, src->width, src->height, yadif->ydest, yadif->ypitch, yadif->udest, yadif->vdest, yadif->uvpitch, yadif->cpu); close_yadif(yadif); } } else { // If all else fails, default to linear blend deinterlace_yuv(dst->data, (uint8_t **) &src->data, src->width * 2, src->height, DEINTERLACE_LINEARBLEND); } return 0; } mlt-7.38.0/src/modules/xine/common.h000664 000000 000000 00000002030 15172202314 017220 0ustar00rootroot000000 000000 /* * common.h * Copyright (C) 2023 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __COMMON_H__ #define __COMMON_H__ #include mlt_deinterlacer supported_method(mlt_deinterlacer method); int deinterlace_image( mlt_image dst, mlt_image src, mlt_image prev, mlt_image next, int tff, mlt_deinterlacer method); #endif mlt-7.38.0/src/modules/xine/cpu_accel.c000664 000000 000000 00000012700 15172202314 017646 0ustar00rootroot000000 000000 /* * cpu_accel.c * Copyright (C) 1999-2001 Aaron Holtzman * * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. * * mpeg2dec is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * mpeg2dec is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ //#include "config.h" #include #include #include #include #include #include #define LOG_MODULE "cpu_accel" #define LOG_VERBOSE /* #define LOG */ #include "xineutils.h" #if defined(ARCH_X86) || defined(ARCH_X86_64) #if defined __x86_64__ static uint32_t arch_accel (void) { uint32_t caps; /* No need to test for this on AMD64, we know what the platform has. */ caps = MM_ACCEL_X86_MMX | MM_ACCEL_X86_SSE | MM_ACCEL_X86_MMXEXT | MM_ACCEL_X86_SSE2; return caps; } #else static uint32_t arch_accel (void) { #ifndef _MSC_VER uint32_t eax, ebx, ecx, edx; int AMD; uint32_t caps; #ifndef PIC #define cpuid(op,eax,ebx,ecx,edx) \ __asm__ ("cpuid" \ : "=a" (eax), \ "=b" (ebx), \ "=c" (ecx), \ "=d" (edx) \ : "a" (op) \ : "cc") #else /* PIC version : save ebx */ #define cpuid(op,eax,ebx,ecx,edx) \ __asm__ ("pushl %%ebx\n\t" \ "cpuid\n\t" \ "movl %%ebx,%1\n\t" \ "popl %%ebx" \ : "=a" (eax), \ "=r" (ebx), \ "=c" (ecx), \ "=d" (edx) \ : "a" (op) \ : "cc") #endif __asm__ ("pushfl\n\t" "pushfl\n\t" "popl %0\n\t" "movl %0,%1\n\t" "xorl $0x200000,%0\n\t" "pushl %0\n\t" "popfl\n\t" "pushfl\n\t" "popl %0\n\t" "popfl" : "=r" (eax), "=r" (ebx) : : "cc"); if (eax == ebx) /* no cpuid */ return 0; cpuid (0x00000000, eax, ebx, ecx, edx); if (!eax) /* vendor string only */ return 0; AMD = (ebx == 0x68747541) && (ecx == 0x444d4163) && (edx == 0x69746e65); cpuid (0x00000001, eax, ebx, ecx, edx); if (! (edx & 0x00800000)) /* no MMX */ return 0; caps = MM_ACCEL_X86_MMX; if (edx & 0x02000000) /* SSE - identical to AMD MMX extensions */ caps |= MM_ACCEL_X86_SSE | MM_ACCEL_X86_MMXEXT; if (edx & 0x04000000) /* SSE2 */ caps |= MM_ACCEL_X86_SSE2; cpuid (0x80000000, eax, ebx, ecx, edx); if (eax < 0x80000001) /* no extended capabilities */ return caps; cpuid (0x80000001, eax, ebx, ecx, edx); if (edx & 0x80000000) caps |= MM_ACCEL_X86_3DNOW; if (AMD && (edx & 0x00400000)) /* AMD MMX extensions */ caps |= MM_ACCEL_X86_MMXEXT; return caps; #else /* _MSC_VER */ return 0; #endif } #endif /* x86_64 */ static jmp_buf sigill_return; static void sigill_handler (int n) { longjmp(sigill_return, 1); } #endif /* ARCH_X86 */ #if defined (ARCH_PPC) && defined (ENABLE_ALTIVEC) static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump = 0; static void sigill_handler (int sig) { if (!canjump) { signal (sig, SIG_DFL); raise (sig); } canjump = 0; siglongjmp (jmpbuf, 1); } static uint32_t arch_accel (void) { signal (SIGILL, sigill_handler); if (sigsetjmp (jmpbuf, 1)) { signal (SIGILL, SIG_DFL); return 0; } canjump = 1; __asm__ volatile ("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0" : : "r" (-1)); signal (SIGILL, SIG_DFL); return MM_ACCEL_PPC_ALTIVEC; } #endif /* ARCH_PPC */ uint32_t xine_mm_accel (void) { static int initialized = 0; static uint32_t accel; if (!initialized) { #if defined (ARCH_X86) || defined(ARCH_X86_64) || (defined (ARCH_PPC) && defined (ENABLE_ALTIVEC)) accel = arch_accel (); #elif defined (HAVE_MLIB) #ifdef MLIB_LAZYLOAD void *hndl; if ((hndl = dlopen("libmlib.so.2", RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE)) == NULL) { accel = 0; } else { dlclose(hndl); accel = MM_ACCEL_MLIB; } #else accel = MM_ACCEL_MLIB; #endif #else accel = 0; #endif #if defined(ARCH_X86) || defined(ARCH_X86_64) #ifndef _MSC_VER /* test OS support for SSE */ if( accel & MM_ACCEL_X86_SSE ) { void (*old_sigill_handler)(int); old_sigill_handler = signal (SIGILL, sigill_handler); if (setjmp(sigill_return)) { lprintf ("OS doesn't support SSE instructions.\n"); accel &= ~(MM_ACCEL_X86_SSE|MM_ACCEL_X86_SSE2); } else { __asm__ volatile ("xorps %xmm0, %xmm0"); } signal (SIGILL, old_sigill_handler); } #endif /* _MSC_VER */ #endif /* ARCH_X86 || ARCH_X86_64 */ if(getenv("XINE_NO_ACCEL")) { accel = 0; } initialized = 1; } return accel; } mlt-7.38.0/src/modules/xine/deinterlace.c000664 000000 000000 00000066227 15172202314 020224 0ustar00rootroot000000 000000 /* * Copyright (C) 2001 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * Deinterlace routines by Miguel Freitas * based of DScaler project sources (deinterlace.sourceforge.net) * * Currently only available for Xv driver and MMX extensions * * small todo list: * - implement non-MMX versions for all methods * - support MMX2 instructions * - move some generic code from xv driver to this file * - make it also work for yuy2 frames * */ #include #include #include "deinterlace.h" #include "xineutils.h" #define xine_fast_memcpy memcpy #define xine_fast_memmove memmove /* DeinterlaceFieldBob algorithm Based on Virtual Dub plugin by Gunnar Thalin MMX asm version from dscaler project (deinterlace.sourceforge.net) Linux version for Xine player by Miguel Freitas */ static void deinterlace_bob_yuv_mmx( uint8_t *pdst, uint8_t *psrc[], int width, int height ) { #ifdef USE_MMX int Line; uint64_t *YVal1; uint64_t *YVal2; uint64_t *YVal3; uint64_t *Dest; uint8_t* pEvenLines = psrc[0]; uint8_t* pOddLines = psrc[0]+width; int LineLength = width; int SourcePitch = width * 2; int IsOdd = 1; long EdgeDetect = 625; long JaggieThreshold = 73; int n; uint64_t qwEdgeDetect; uint64_t qwThreshold; static mmx_t YMask = {.ub={0xff,0,0xff,0,0xff,0,0xff,0}}; static mmx_t Mask = {.ub={0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}}; qwEdgeDetect = EdgeDetect; qwEdgeDetect += (qwEdgeDetect << 48) + (qwEdgeDetect << 32) + (qwEdgeDetect << 16); qwThreshold = JaggieThreshold; qwThreshold += (qwThreshold << 48) + (qwThreshold << 32) + (qwThreshold << 16); // copy first even line no matter what, and the first odd line if we're // processing an odd field. xine_fast_memcpy(pdst, pEvenLines, LineLength); if (IsOdd) xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength); height = height / 2; for (Line = 0; Line < height - 1; ++Line) { if (IsOdd) { YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch); YVal2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch); YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch); Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength); } else { YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch); YVal2 = (uint64_t *)(pOddLines + Line * SourcePitch); YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch); Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength); } // For ease of reading, the comments below assume that we're operating on an odd // field (i.e., that bIsOdd is true). The exact same processing is done when we // operate on an even field, but the roles of the odd and even fields are reversed. // It's just too cumbersome to explain the algorithm in terms of "the next odd // line if we're doing an odd field, or the next even line if we're doing an // even field" etc. So wherever you see "odd" or "even" below, keep in mind that // half the time this function is called, those words' meanings will invert. // Copy the odd line to the overlay verbatim. xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength); n = LineLength >> 3; while( n-- ) { movq_m2r (*YVal1++, mm0); movq_m2r (*YVal2++, mm1); movq_m2r (*YVal3++, mm2); // get intensities in mm3 - 4 movq_r2r ( mm0, mm3 ); pand_m2r ( YMask, mm3 ); movq_r2r ( mm1, mm4 ); pand_m2r ( YMask, mm4 ); movq_r2r ( mm2, mm5 ); pand_m2r ( YMask, mm5 ); // get average in mm0 pand_m2r ( Mask, mm0 ); pand_m2r ( Mask, mm2 ); psrlw_i2r ( 01, mm0 ); psrlw_i2r ( 01, mm2 ); paddw_r2r ( mm2, mm0 ); // work out (O1 - E) * (O2 - E) / 2 - EdgeDetect * (O1 - O2) ^ 2 >> 12 // result will be in mm6 psrlw_i2r ( 01, mm3 ); psrlw_i2r ( 01, mm4 ); psrlw_i2r ( 01, mm5 ); movq_r2r ( mm3, mm6 ); psubw_r2r ( mm4, mm6 ); //mm6 = O1 - E movq_r2r ( mm5, mm7 ); psubw_r2r ( mm4, mm7 ); //mm7 = O2 - E pmullw_r2r ( mm7, mm6 ); // mm6 = (O1 - E) * (O2 - E) movq_r2r ( mm3, mm7 ); psubw_r2r ( mm5, mm7 ); // mm7 = (O1 - O2) pmullw_r2r ( mm7, mm7 ); // mm7 = (O1 - O2) ^ 2 psrlw_i2r ( 12, mm7 ); // mm7 = (O1 - O2) ^ 2 >> 12 pmullw_m2r ( *&qwEdgeDetect, mm7 );// mm7 = EdgeDetect * (O1 - O2) ^ 2 >> 12 psubw_r2r ( mm7, mm6 ); // mm6 is what we want pcmpgtw_m2r ( *&qwThreshold, mm6 ); movq_r2r ( mm6, mm7 ); pand_r2r ( mm6, mm0 ); pandn_r2r ( mm1, mm7 ); por_r2r ( mm0, mm7 ); movq_r2m ( mm7, *Dest++ ); } } // Copy last odd line if we're processing an even field. if (! IsOdd) { xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength, pOddLines + (height - 1) * SourcePitch, LineLength); } // clear out the MMX registers ready for doing floating point // again emms(); #endif } /* Deinterlace the latest field, with a tendency to weave rather than bob. Good for high detail on low-movement scenes. Seems to produce bad output in general case, need to check if this is normal or if the code is broken. */ static int deinterlace_weave_yuv_mmx( uint8_t *pdst, uint8_t *psrc[], int width, int height ) { #ifdef USE_MMX int Line; uint64_t *YVal1; uint64_t *YVal2; uint64_t *YVal3; uint64_t *YVal4; uint64_t *Dest; uint8_t* pEvenLines = psrc[0]; uint8_t* pOddLines = psrc[0]+width; uint8_t* pPrevLines; int LineLength = width; int SourcePitch = width * 2; int IsOdd = 1; long TemporalTolerance = 300; long SpatialTolerance = 600; long SimilarityThreshold = 25; int n; uint64_t qwSpatialTolerance; uint64_t qwTemporalTolerance; uint64_t qwThreshold; static mmx_t YMask = {.ub={0xff,0,0xff,0,0xff,0,0xff,0}}; static mmx_t Mask = {.ub={0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}}; // Make sure we have all the data we need. if ( psrc[0] == NULL || psrc[1] == NULL ) return 0; if (IsOdd) pPrevLines = psrc[1] + width; else pPrevLines = psrc[1]; // Since the code uses MMX to process 4 pixels at a time, we need our constants // to be represented 4 times per quadword. qwSpatialTolerance = SpatialTolerance; qwSpatialTolerance += (qwSpatialTolerance << 48) + (qwSpatialTolerance << 32) + (qwSpatialTolerance << 16); qwTemporalTolerance = TemporalTolerance; qwTemporalTolerance += (qwTemporalTolerance << 48) + (qwTemporalTolerance << 32) + (qwTemporalTolerance << 16); qwThreshold = SimilarityThreshold; qwThreshold += (qwThreshold << 48) + (qwThreshold << 32) + (qwThreshold << 16); // copy first even line no matter what, and the first odd line if we're // processing an even field. xine_fast_memcpy(pdst, pEvenLines, LineLength); if (!IsOdd) xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength); height = height / 2; for (Line = 0; Line < height - 1; ++Line) { if (IsOdd) { YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch); YVal2 = (uint64_t *)(pOddLines + Line * SourcePitch); YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch); YVal4 = (uint64_t *)(pPrevLines + Line * SourcePitch); Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength); } else { YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch); YVal2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch); YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch); YVal4 = (uint64_t *)(pPrevLines + (Line + 1) * SourcePitch); Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength); } // For ease of reading, the comments below assume that we're operating on an odd // field (i.e., that bIsOdd is true). The exact same processing is done when we // operate on an even field, but the roles of the odd and even fields are reversed. // It's just too cumbersome to explain the algorithm in terms of "the next odd // line if we're doing an odd field, or the next even line if we're doing an // even field" etc. So wherever you see "odd" or "even" below, keep in mind that // half the time this function is called, those words' meanings will invert. // Copy the even scanline below this one to the overlay buffer, since we'll be // adapting the current scanline to the even lines surrounding it. The scanline // above has already been copied by the previous pass through the loop. xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength); n = LineLength >> 3; while( n-- ) { movq_m2r ( *YVal1++, mm0 ); // mm0 = E1 movq_m2r ( *YVal2++, mm1 ); // mm1 = O movq_m2r ( *YVal3++, mm2 ); // mm2 = E2 movq_r2r ( mm0, mm3 ); // mm3 = intensity(E1) movq_r2r ( mm1, mm4 ); // mm4 = intensity(O) movq_r2r ( mm2, mm6 ); // mm6 = intensity(E2) pand_m2r ( YMask, mm3 ); pand_m2r ( YMask, mm4 ); pand_m2r ( YMask, mm6 ); // Average E1 and E2 for interpolated bobbing. // leave result in mm0 pand_m2r ( Mask, mm0 ); // mm0 = E1 with lower chroma bit stripped off pand_m2r ( Mask, mm2 ); // mm2 = E2 with lower chroma bit stripped off psrlw_i2r ( 01, mm0 ); // mm0 = E1 / 2 psrlw_i2r ( 01, mm2 ); // mm2 = E2 / 2 paddb_r2r ( mm2, mm0 ); // The meat of the work is done here. We want to see whether this pixel is // close in luminosity to ANY of: its top neighbor, its bottom neighbor, // or its predecessor. To do this without branching, we use MMX's // saturation feature, which gives us Z(x) = x if x>=0, or 0 if x<0. // // The formula we're computing here is // Z(ST - (E1 - O) ^ 2) + Z(ST - (E2 - O) ^ 2) + Z(TT - (Oold - O) ^ 2) // where ST is spatial tolerance and TT is temporal tolerance. The idea // is that if a pixel is similar to none of its neighbors, the resulting // value will be pretty low, probably zero. A high value therefore indicates // that the pixel had a similar neighbor. The pixel in the same position // in the field before last (Oold) is considered a neighbor since we want // to be able to display 1-pixel-high horizontal lines. movq_m2r ( *&qwSpatialTolerance, mm7 ); movq_r2r ( mm3, mm5 ); // mm5 = E1 psubsw_r2r ( mm4, mm5 ); // mm5 = E1 - O psraw_i2r ( 1, mm5 ); pmullw_r2r ( mm5, mm5 ); // mm5 = (E1 - O) ^ 2 psubusw_r2r ( mm5, mm7 ); // mm7 = ST - (E1 - O) ^ 2, or 0 if that's negative movq_m2r ( *&qwSpatialTolerance, mm3 ); movq_r2r ( mm6, mm5 ); // mm5 = E2 psubsw_r2r ( mm4, mm5 ); // mm5 = E2 - O psraw_i2r ( 1, mm5 ); pmullw_r2r ( mm5, mm5 ); // mm5 = (E2 - O) ^ 2 psubusw_r2r ( mm5, mm3 ); // mm0 = ST - (E2 - O) ^ 2, or 0 if that's negative paddusw_r2r ( mm3, mm7 ); // mm7 = (ST - (E1 - O) ^ 2) + (ST - (E2 - O) ^ 2) movq_m2r ( *&qwTemporalTolerance, mm3 ); movq_m2r ( *YVal4++, mm5 ); // mm5 = Oold pand_m2r ( YMask, mm5 ); psubsw_r2r ( mm4, mm5 ); // mm5 = Oold - O psraw_i2r ( 1, mm5 ); // XXX pmullw_r2r ( mm5, mm5 ); // mm5 = (Oold - O) ^ 2 psubusw_r2r ( mm5, mm3 ); /* mm0 = TT - (Oold - O) ^ 2, or 0 if that's negative */ paddusw_r2r ( mm3, mm7 ); // mm7 = our magic number /* * Now compare the similarity totals against our threshold. The pcmpgtw * instruction will populate the target register with a bunch of mask bits, * filling words where the comparison is true with 1s and ones where it's * false with 0s. A few ANDs and NOTs and an OR later, we have bobbed * values for pixels under the similarity threshold and weaved ones for * pixels over the threshold. */ pcmpgtw_m2r( *&qwThreshold, mm7 ); // mm7 = 0xffff where we're greater than the threshold, 0 elsewhere movq_r2r ( mm7, mm6 ); // mm6 = 0xffff where we're greater than the threshold, 0 elsewhere pand_r2r ( mm1, mm7 ); // mm7 = weaved data where we're greater than the threshold, 0 elsewhere pandn_r2r ( mm0, mm6 ); // mm6 = bobbed data where we're not greater than the threshold, 0 elsewhere por_r2r ( mm6, mm7 ); // mm7 = bobbed and weaved data movq_r2m ( mm7, *Dest++ ); } } // Copy last odd line if we're processing an odd field. if (IsOdd) { xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength, pOddLines + (height - 1) * SourcePitch, LineLength); } // clear out the MMX registers ready for doing floating point // again emms(); #endif return 1; } // This is a simple lightweight DeInterlace method that uses little CPU time // but gives very good results for low or intermedite motion. (MORE CPU THAN BOB) // It defers frames by one field, but that does not seem to produce noticeable // lip sync problems. // // The method used is to take either the older or newer weave pixel depending // upon which give the smaller comb factor, and then clip to avoid large damage // when wrong. // // I'd intended this to be part of a larger more elaborate method added to // Blended Clip but this give too good results for the CPU to ignore here. static int deinterlace_greedy_yuv_mmx( uint8_t *pdst, uint8_t *psrc[], int width, int height ) { #ifdef USE_MMX int Line; int LoopCtr; uint64_t *L1; // ptr to Line1, of 3 uint64_t *L2; // ptr to Line2, the weave line uint64_t *L3; // ptr to Line3 uint64_t *LP2; // ptr to prev Line2 uint64_t *Dest; uint8_t* pEvenLines = psrc[0]; uint8_t* pOddLines = psrc[0]+width; uint8_t* pPrevLines; static mmx_t ShiftMask = {.ub={0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}}; int LineLength = width; int SourcePitch = width * 2; int IsOdd = 1; long GreedyMaxComb = 15; static mmx_t MaxComb; int i; if ( psrc[0] == NULL || psrc[1] == NULL ) return 0; if (IsOdd) pPrevLines = psrc[1] + width; else pPrevLines = psrc[1]; for( i = 0; i < 8; i++ ) MaxComb.ub[i] = GreedyMaxComb; // How badly do we let it weave? 0-255 // copy first even line no matter what, and the first odd line if we're // processing an EVEN field. (note diff from other deint rtns.) xine_fast_memcpy(pdst, pEvenLines, LineLength); //DL0 if (!IsOdd) xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength); //DL1 height = height / 2; for (Line = 0; Line < height - 1; ++Line) { LoopCtr = LineLength / 8; // there are LineLength / 8 qwords per line if (IsOdd) { L1 = (uint64_t *)(pEvenLines + Line * SourcePitch); L2 = (uint64_t *)(pOddLines + Line * SourcePitch); L3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch); LP2 = (uint64_t *)(pPrevLines + Line * SourcePitch); // prev Odd lines Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength); } else { L1 = (uint64_t *)(pOddLines + Line * SourcePitch); L2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch); L3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch); LP2 = (uint64_t *)(pPrevLines + (Line + 1) * SourcePitch); //prev even lines Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength); } xine_fast_memcpy((char *)Dest + LineLength, L3, LineLength); // For ease of reading, the comments below assume that we're operating on an odd // field (i.e., that info->IsOdd is true). Assume the obvious for even lines.. while( LoopCtr-- ) { movq_m2r ( *L1++, mm1 ); movq_m2r ( *L2++, mm2 ); movq_m2r ( *L3++, mm3 ); movq_m2r ( *LP2++, mm0 ); // average L1 and L3 leave result in mm4 movq_r2r ( mm1, mm4 ); // L1 pand_m2r ( ShiftMask, mm4 ); psrlw_i2r ( 01, mm4 ); movq_r2r ( mm3, mm5 ); // L3 pand_m2r ( ShiftMask, mm5 ); psrlw_i2r ( 01, mm5 ); paddb_r2r ( mm5, mm4 ); // the average, for computing comb // get abs value of possible L2 comb movq_r2r ( mm2, mm7 ); // L2 psubusb_r2r ( mm4, mm7 ); // L2 - avg movq_r2r ( mm4, mm5 ); // avg psubusb_r2r ( mm2, mm5 ); // avg - L2 por_r2r ( mm7, mm5 ); // abs(avg-L2) movq_r2r ( mm4, mm6 ); // copy of avg for later // get abs value of possible LP2 comb movq_r2r ( mm0, mm7 ); // LP2 psubusb_r2r ( mm4, mm7 ); // LP2 - avg psubusb_r2r ( mm0, mm4 ); // avg - LP2 por_r2r ( mm7, mm4 ); // abs(avg-LP2) // use L2 or LP2 depending upon which makes smaller comb psubusb_r2r ( mm5, mm4 ); // see if it goes to zero psubusb_r2r ( mm5, mm5 ); // 0 pcmpeqb_r2r ( mm5, mm4 ); // if (mm4=0) then FF else 0 pcmpeqb_r2r ( mm4, mm5 ); // opposite of mm4 // if Comb(LP2) <= Comb(L2) then mm4=ff, mm5=0 else mm4=0, mm5 = 55 pand_r2r ( mm2, mm5 ); // use L2 if mm5 == ff, else 0 pand_r2r ( mm0, mm4 ); // use LP2 if mm4 = ff, else 0 por_r2r ( mm5, mm4 ); // may the best win // Now lets clip our chosen value to be not outside of the range // of the high/low range L1-L3 by more than abs(L1-L3) // This allows some comb but limits the damages and also allows more // detail than a boring oversmoothed clip. movq_r2r ( mm1, mm2 ); // copy L1 psubusb_r2r ( mm3, mm2 ); // - L3, with saturation paddusb_r2r ( mm3, mm2 ); // now = Max(L1,L3) pcmpeqb_r2r ( mm7, mm7 ); // all ffffffff psubusb_r2r ( mm1, mm7 ); // - L1 paddusb_r2r ( mm7, mm3 ); // add, may sat at fff.. psubusb_r2r ( mm7, mm3 ); // now = Min(L1,L3) // allow the value to be above the high or below the low by amt of MaxComb paddusb_m2r ( MaxComb, mm2 ); // increase max by diff psubusb_m2r ( MaxComb, mm3 ); // lower min by diff psubusb_r2r ( mm3, mm4 ); // best - Min paddusb_r2r ( mm3, mm4 ); // now = Max(best,Min(L1,L3) pcmpeqb_r2r ( mm7, mm7 ); // all ffffffff psubusb_r2r ( mm4, mm7 ); // - Max(best,Min(best,L3) paddusb_r2r ( mm7, mm2 ); // add may sat at FFF.. psubusb_r2r ( mm7, mm2 ); // now = Min( Max(best, Min(L1,L3), L2 )=L2 clipped movq_r2m ( mm2, *Dest++ ); // move in our clipped best } } /* Copy last odd line if we're processing an Odd field. */ if (IsOdd) { xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength, pOddLines + (height - 1) * SourcePitch, LineLength); } /* clear out the MMX registers ready for doing floating point again */ emms(); #endif return 1; } /* Use one field to interpolate the other (low cpu utilization) Will lose resolution but does not produce weaving effect (good for fast moving scenes) also know as "linear interpolation" */ static void deinterlace_onefield_yuv_mmx( uint8_t *pdst, uint8_t *psrc[], int width, int height ) { #ifdef USE_MMX int Line; uint64_t *YVal1; uint64_t *YVal3; uint64_t *Dest; uint8_t* pEvenLines = psrc[0]; uint8_t* pOddLines = psrc[0]+width; int LineLength = width; int SourcePitch = width * 2; int IsOdd = 1; int n; static mmx_t Mask = {.ub={0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}}; /* * copy first even line no matter what, and the first odd line if we're * processing an odd field. */ xine_fast_memcpy(pdst, pEvenLines, LineLength); if (IsOdd) xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength); height = height / 2; for (Line = 0; Line < height - 1; ++Line) { if (IsOdd) { YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch); YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch); Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength); } else { YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch); YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch); Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength); } // Copy the odd line to the overlay verbatim. xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength); n = LineLength >> 3; while( n-- ) { movq_m2r (*YVal1++, mm0); movq_m2r (*YVal3++, mm2); // get average in mm0 pand_m2r ( Mask, mm0 ); pand_m2r ( Mask, mm2 ); psrlw_i2r ( 01, mm0 ); psrlw_i2r ( 01, mm2 ); paddw_r2r ( mm2, mm0 ); movq_r2m ( mm0, *Dest++ ); } } /* Copy last odd line if we're processing an even field. */ if (! IsOdd) { xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength, pOddLines + (height - 1) * SourcePitch, LineLength); } /* clear out the MMX registers ready for doing floating point * again */ emms(); #endif } /* Linear Blend filter - does a kind of vertical blurring on the image. (idea borrowed from mplayer's sources) */ static void deinterlace_linearblend_yuv_mmx( uint8_t *pdst, uint8_t *psrc[], int width, int height ) { #ifdef USE_MMX int Line; uint64_t *YVal1; uint64_t *YVal2; uint64_t *YVal3; uint64_t *Dest; int LineLength = width; int n; /* Copy first line */ xine_fast_memmove(pdst, psrc[0], LineLength); for (Line = 1; Line < height - 1; ++Line) { YVal1 = (uint64_t *)(psrc[0] + (Line - 1) * LineLength); YVal2 = (uint64_t *)(psrc[0] + (Line) * LineLength); YVal3 = (uint64_t *)(psrc[0] + (Line + 1) * LineLength); Dest = (uint64_t *)(pdst + Line * LineLength); n = LineLength >> 3; while( n-- ) { /* load data from 3 lines */ movq_m2r (*YVal1++, mm0); movq_m2r (*YVal2++, mm1); movq_m2r (*YVal3++, mm2); /* expand bytes to words */ punpckhbw_r2r (mm0, mm3); punpckhbw_r2r (mm1, mm4); punpckhbw_r2r (mm2, mm5); punpcklbw_r2r (mm0, mm0); punpcklbw_r2r (mm1, mm1); punpcklbw_r2r (mm2, mm2); /* * deinterlacing: * deint_line = (line0 + 2*line1 + line2) / 4 */ psrlw_i2r (07, mm0); psrlw_i2r (06, mm1); psrlw_i2r (07, mm2); psrlw_i2r (07, mm3); psrlw_i2r (06, mm4); psrlw_i2r (07, mm5); paddw_r2r (mm1, mm0); paddw_r2r (mm2, mm0); paddw_r2r (mm4, mm3); paddw_r2r (mm5, mm3); psrlw_i2r (03, mm0); psrlw_i2r (03, mm3); /* pack 8 words to 8 bytes in mm0 */ packuswb_r2r (mm3, mm0); movq_r2m ( mm0, *Dest++ ); } } /* Copy last line */ xine_fast_memmove(pdst + Line * LineLength, psrc[0] + Line * LineLength, LineLength); /* clear out the MMX registers ready for doing floating point * again */ emms(); #endif } /* Linear Blend filter - C version contributed by Rogerio Brito. This algorithm has the same interface as the other functions. The destination "screen" (pdst) is constructed from the source screen (psrc[0]) line by line. The i-th line of the destination screen is the average of 3 lines from the source screen: the (i-1)-th, i-th and (i+1)-th lines, with the i-th line having weight 2 in the computation. Remarks: * each line on pdst doesn't depend on previous lines; * due to the way the algorithm is defined, the first & last lines of the screen aren't deinterlaced. */ static void deinterlace_linearblend_yuv( uint8_t *pdst, uint8_t *psrc[], int width, int height ) { register int x, y; register uint8_t *l0, *l1, *l2, *l3; l0 = pdst; /* target line */ l1 = psrc[0]; /* 1st source line */ l2 = l1 + width; /* 2nd source line = line that follows l1 */ l3 = l2 + width; /* 3rd source line = line that follows l2 */ /* Copy the first line */ xine_fast_memcpy(l0, l1, width); l0 += width; for (y = 1; y < height-1; ++y) { /* computes avg of: l1 + 2*l2 + l3 */ for (x = 0; x < width; ++x) { l0[x] = (l1[x] + (l2[x]<<1) + l3[x]) >> 2; } /* updates the line pointers */ l1 = l2; l2 = l3; l3 += width; l0 += width; } /* Copy the last line */ xine_fast_memcpy(l0, l1, width); } static int check_for_mmx(void) { #ifdef USE_MMX static int config_flags = -1; if ( config_flags == -1 ) config_flags = xine_mm_accel(); if (config_flags & MM_ACCEL_X86_MMX) return 1; return 0; #else return 0; #endif } /* generic YUV deinterlacer pdst -> pointer to destination bitmap psrc -> array of pointers to source bitmaps ([0] = most recent) width,height -> dimension for bitmaps method -> DEINTERLACE_xxx */ void deinterlace_yuv( uint8_t *pdst, uint8_t *psrc[], int width, int height, int method ) { switch( method ) { case DEINTERLACE_NONE: xine_fast_memcpy(pdst,psrc[0],width*height); break; case DEINTERLACE_BOB: if( check_for_mmx() ) deinterlace_bob_yuv_mmx(pdst,psrc,width,height); else /* FIXME: provide an alternative? */ deinterlace_linearblend_yuv(pdst,psrc,width,height); break; case DEINTERLACE_WEAVE: if( check_for_mmx() ) { if( !deinterlace_weave_yuv_mmx(pdst,psrc,width,height) ) xine_fast_memcpy(pdst,psrc[0],width*height); } else /* FIXME: provide an alternative? */ deinterlace_linearblend_yuv(pdst,psrc,width,height); break; case DEINTERLACE_GREEDY: if( check_for_mmx() ) { if( !deinterlace_greedy_yuv_mmx(pdst,psrc,width,height) ) xine_fast_memcpy(pdst,psrc[0],width*height); } else /* FIXME: provide an alternative? */ deinterlace_linearblend_yuv(pdst,psrc,width,height); break; case DEINTERLACE_ONEFIELD: if( check_for_mmx() ) deinterlace_onefield_yuv_mmx(pdst,psrc,width,height); else /* FIXME: provide an alternative? */ deinterlace_linearblend_yuv(pdst,psrc,width,height); break; case DEINTERLACE_ONEFIELDXV: lprintf("ONEFIELDXV must be handled by the video driver.\n"); break; case DEINTERLACE_LINEARBLEND: if( check_for_mmx() ) deinterlace_linearblend_yuv_mmx(pdst,psrc,width,height); else deinterlace_linearblend_yuv(pdst,psrc,width,height); break; default: // cppcheck-suppress preprocessorErrorDirective lprintf("unknown method %d.\n",method); break; } } int deinterlace_yuv_supported ( int method ) { switch( method ) { case DEINTERLACE_NONE: return 1; case DEINTERLACE_BOB: case DEINTERLACE_WEAVE: case DEINTERLACE_GREEDY: case DEINTERLACE_ONEFIELD: return check_for_mmx(); case DEINTERLACE_ONEFIELDXV: lprintf ("ONEFIELDXV must be handled by the video driver.\n"); return 0; case DEINTERLACE_LINEARBLEND: return 1; } return 0; } const char *deinterlace_methods[] = { "none", "bob", "weave", "greedy", "onefield", "onefield_xv", "linearblend", NULL }; mlt-7.38.0/src/modules/xine/deinterlace.h000664 000000 000000 00000003063 15172202314 020216 0ustar00rootroot000000 000000 /* * Copyright (C) 2001 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * Deinterlace routines by Miguel Freitas * based of DScaler project sources (deinterlace.sourceforge.net) * * Currently only available for Xv driver and MMX extensions * */ #ifndef __DEINTERLACE_H__ #define __DEINTERLACE_H__ //#include "video_out.h" #include int deinterlace_yuv_supported ( int method ); void deinterlace_yuv( uint8_t *pdst, uint8_t *psrc[], int width, int height, int method ); #define DEINTERLACE_NONE 0 #define DEINTERLACE_BOB 1 #define DEINTERLACE_WEAVE 2 #define DEINTERLACE_GREEDY 3 #define DEINTERLACE_ONEFIELD 4 #define DEINTERLACE_ONEFIELDXV 5 #define DEINTERLACE_LINEARBLEND 6 #define DEINTERLACE_YADIF 7 #define DEINTERLACE_YADIF_NOSPATIAL 8 extern const char *deinterlace_methods[]; #endif mlt-7.38.0/src/modules/xine/factory.c000664 000000 000000 00000004046 15172202314 017403 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2022 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mltxine_export.h" #include #include extern mlt_filter filter_deinterlace_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_filter link_deinterlace_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/xine/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTXINE_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_filter_type, "deinterlace", filter_deinterlace_init); MLT_REGISTER_METADATA(mlt_service_filter_type, "deinterlace", metadata, "filter_deinterlace.yml"); MLT_REGISTER(mlt_service_link_type, "deinterlace", link_deinterlace_init); MLT_REGISTER_METADATA(mlt_service_link_type, "deinterlace", metadata, "link_deinterlace.yml"); } mlt-7.38.0/src/modules/xine/filter_deinterlace.c000664 000000 000000 00000043564 15172202314 021570 0ustar00rootroot000000 000000 /* * filter_deinterlace.c -- deinterlace filter * Copyright (C) 2003-2014 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "deinterlace.h" #include "yadif.h" #include #include #include #include #include #include #include #define YADIF_MODE_TEMPORAL_SPATIAL (0) #define YADIF_MODE_TEMPORAL (2) static yadif_filter *init_yadif(int width, int height) { yadif_filter *yadif = mlt_pool_alloc(sizeof(*yadif)); yadif->cpu = 0; // Pure C #ifdef USE_SSE yadif->cpu |= AVS_CPU_INTEGER_SSE; #endif #ifdef USE_SSE2 yadif->cpu |= AVS_CPU_SSE2; #endif // Create intermediate planar planes yadif->yheight = height; yadif->ywidth = width; yadif->uvwidth = yadif->ywidth / 2; yadif->ypitch = (yadif->ywidth + 15) / 16 * 16; yadif->uvpitch = (yadif->uvwidth + 15) / 16 * 16; yadif->ysrc = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->ypitch); yadif->usrc = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->vsrc = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->yprev = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->ypitch); yadif->uprev = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->vprev = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->ynext = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->ypitch); yadif->unext = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->vnext = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->ydest = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->ypitch); yadif->udest = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); yadif->vdest = (unsigned char *) mlt_pool_alloc(yadif->yheight * yadif->uvpitch); return yadif; } static void close_yadif(yadif_filter *yadif) { mlt_pool_release(yadif->ysrc); mlt_pool_release(yadif->usrc); mlt_pool_release(yadif->vsrc); mlt_pool_release(yadif->yprev); mlt_pool_release(yadif->uprev); mlt_pool_release(yadif->vprev); mlt_pool_release(yadif->ynext); mlt_pool_release(yadif->unext); mlt_pool_release(yadif->vnext); mlt_pool_release(yadif->ydest); mlt_pool_release(yadif->udest); mlt_pool_release(yadif->vdest); mlt_pool_release(yadif); #if defined(__GNUC__) && !defined(PIC) // Set SSSE3 bit to cpu asm("mov $1, %%eax \n\t" "push %%ebx \n\t" "cpuid \n\t" "pop %%ebx \n\t" "mov %%ecx, %%edx \n\t" "shr $9, %%edx \n\t" "and $1, %%edx \n\t" "shl $9, %%edx \n\t" "and $511, %%ebx \n\t" "or %%edx, %%ebx \n\t" : "=b"(yadif->cpu) : "p"(yadif->cpu) : "%eax", "%ecx", "%edx"); #endif } static int deinterlace_yadif(mlt_frame frame, mlt_filter filter, uint8_t **image, mlt_image_format *format, int *width, int *height, int mode) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_frame previous_frame = mlt_properties_get_data(properties, "previous frame", NULL); uint8_t *previous_image = NULL; int previous_width = *width; int previous_height = *height; mlt_frame next_frame = mlt_properties_get_data(properties, "next frame", NULL); uint8_t *next_image = NULL; int next_width = *width; int next_height = *height; mlt_log_debug(MLT_FILTER_SERVICE(filter), "previous " MLT_POSITION_FMT " current " MLT_POSITION_FMT " next " MLT_POSITION_FMT "\n", previous_frame ? mlt_frame_original_position(previous_frame) : -1, mlt_frame_original_position(frame), next_frame ? mlt_frame_original_position(next_frame) : -1); if (!previous_frame || !next_frame) return 1; mlt_service_lock(MLT_FILTER_SERVICE(filter)); // Get the preceding frame's image int error = mlt_frame_get_image(previous_frame, &previous_image, format, &previous_width, &previous_height, 0); int progressive = mlt_properties_get_int(MLT_FRAME_PROPERTIES(previous_frame), "progressive"); // Check that we aren't already progressive if (!error && previous_image && !progressive) { // OK, now we know we have work to do and can request the image in our format frame->convert_image(previous_frame, &previous_image, format, mlt_image_yuv422); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Get the current frame's image *format = mlt_image_yuv422; error = mlt_frame_get_image(frame, image, format, width, height, 1); if (!error && *image && *format == mlt_image_yuv422) { // Get the following frame's image error = mlt_frame_get_image(next_frame, &next_image, format, &next_width, &next_height, 0); if (!error && next_image && *format == mlt_image_yuv422) { yadif_filter *yadif = init_yadif(*width, *height); if (yadif) { const int order = mlt_properties_get_int(properties, "top_field_first"); const int pitch = *width << 1; const int parity = 0; // Convert packed to planar YUY2ToPlanes(*image, pitch, *width, *height, yadif->ysrc, yadif->ypitch, yadif->usrc, yadif->vsrc, yadif->uvpitch, yadif->cpu); YUY2ToPlanes(previous_image, pitch, *width, *height, yadif->yprev, yadif->ypitch, yadif->uprev, yadif->vprev, yadif->uvpitch, yadif->cpu); YUY2ToPlanes(next_image, pitch, *width, *height, yadif->ynext, yadif->ypitch, yadif->unext, yadif->vnext, yadif->uvpitch, yadif->cpu); // Deinterlace each plane filter_plane(mode, yadif->ydest, yadif->ypitch, yadif->yprev, yadif->ysrc, yadif->ynext, yadif->ypitch, *width, *height, parity, order, yadif->cpu); filter_plane(mode, yadif->udest, yadif->uvpitch, yadif->uprev, yadif->usrc, yadif->unext, yadif->uvpitch, *width >> 1, *height, parity, order, yadif->cpu); filter_plane(mode, yadif->vdest, yadif->uvpitch, yadif->vprev, yadif->vsrc, yadif->vnext, yadif->uvpitch, *width >> 1, *height, parity, order, yadif->cpu); // Convert planar to packed YUY2FromPlanes(*image, pitch, *width, *height, yadif->ydest, yadif->ypitch, yadif->udest, yadif->vdest, yadif->uvpitch, yadif->cpu); close_yadif(yadif); } } } } else { mlt_service_unlock(MLT_FILTER_SERVICE(filter)); // Get the current frame's image error = mlt_frame_get_image(frame, image, format, width, height, 0); } return error; } /** Do it :-). */ static int filter_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_filter filter = mlt_frame_pop_service(frame); mlt_properties properties = MLT_FRAME_PROPERTIES(frame); int deinterlace = mlt_properties_get_int(properties, "consumer.progressive"); // The progressive var should only represent the frame's original state and not the state // as modified by this filter! int progressive = mlt_properties_get_int(properties, "progressive"); // At this point - before image was requested - (progressive == 0) cannot be trusted because // some producers (avformat) do not yet know. if (deinterlace && !mlt_properties_get_int(properties, "test_image")) { // Determine deinterlace method char *method_str = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "method"); int method = DEINTERLACE_NONE; char *frame_method_str = mlt_properties_get(properties, "consumer.deinterlacer"); if (frame_method_str) method_str = frame_method_str; if (!method_str || strcmp(method_str, "yadif") == 0) method = DEINTERLACE_YADIF; else if (strcmp(method_str, "yadif-nospatial") == 0) method = DEINTERLACE_YADIF_NOSPATIAL; else if (strcmp(method_str, "onefield") == 0) method = DEINTERLACE_ONEFIELD; else if (strcmp(method_str, "linearblend") == 0) method = DEINTERLACE_LINEARBLEND; else if (strcmp(method_str, "bob") == 0) method = DEINTERLACE_BOB; else if (strcmp(method_str, "weave") == 0) method = DEINTERLACE_BOB; else if (strcmp(method_str, "greedy") == 0) method = DEINTERLACE_GREEDY; // Some producers like pixbuf want rescale_width & _height, but will not get them if you request // the previous image first. So, on the first iteration, we use linearblend. if ((method == DEINTERLACE_YADIF || method == DEINTERLACE_YADIF_NOSPATIAL) && !mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "_notfirst")) { mlt_properties_set_int(MLT_FILTER_PROPERTIES(filter), "_notfirst", 1); method = DEINTERLACE_LINEARBLEND; error = 1; } if (method == DEINTERLACE_YADIF) { error = deinterlace_yadif(frame, filter, image, format, width, height, YADIF_MODE_TEMPORAL_SPATIAL); } else if (method == DEINTERLACE_YADIF_NOSPATIAL) { error = deinterlace_yadif(frame, filter, image, format, width, height, YADIF_MODE_TEMPORAL); } if (error || (method > DEINTERLACE_NONE && method < DEINTERLACE_YADIF)) { mlt_service service = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "service", NULL); // Get the current frame's image int error2 = mlt_frame_get_image(frame, image, format, width, height, writable); progressive = mlt_properties_get_int(properties, "progressive"); if (error) { method = DEINTERLACE_LINEARBLEND; // If YADIF requested, prev/next cancelled because some previous frames were progressive, // but new frames are interlaced, then turn prev/next frames back on. if (!progressive) mlt_properties_set_int(MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 1); } else { // Signal that we no longer need previous and next frames mlt_properties_set_int(MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 0); } error = error2; if (!error && !progressive) { // OK, now we know we have work to do and can request the image in our format error = frame->convert_image(frame, image, format, mlt_image_yuv422); // Check that we aren't already progressive if (!error && *image && *format == mlt_image_yuv422) { // Deinterlace the image using one of the Xine deinterlacers int image_size = mlt_image_format_size(*format, *width, *height, NULL); uint8_t *new_image = mlt_pool_alloc(image_size); deinterlace_yuv(new_image, image, *width * 2, *height, method); mlt_frame_set_image(frame, new_image, image_size, mlt_pool_release); *image = new_image; } } } else if (method == DEINTERLACE_NONE) { error = mlt_frame_get_image(frame, image, format, width, height, writable); } // update progressive flag after having obtained image progressive = mlt_properties_get_int(properties, "progressive"); mlt_log_debug(MLT_FILTER_SERVICE(filter), "error %d deint %d prog %d fmt %s method %s\n", error, deinterlace, progressive, mlt_image_format_name(*format), method_str ? method_str : "yadif"); if (!error) { // Make sure that others know the frame is deinterlaced mlt_properties_set_int(properties, "progressive", 1); } } else { // Pass through error = mlt_frame_get_image(frame, image, format, width, height, writable); } if (!deinterlace || progressive) { // Signal that we no longer need previous and next frames mlt_service service = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "service", NULL); if (service) mlt_properties_set_int(MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 0); } return error; } /** Deinterlace filter processing - this should be lazy evaluation here... */ static mlt_frame deinterlace_process(mlt_filter filter, mlt_frame frame) { // Push filter on to the service stack mlt_frame_push_service(frame, filter); // Push the get_image method on to the stack mlt_frame_push_get_image(frame, filter_get_image); return frame; } static void on_service_changed(mlt_service owner, mlt_service filter) { mlt_service service = mlt_properties_get_data(MLT_SERVICE_PROPERTIES(filter), "service", NULL); mlt_properties_set_int(MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 1); } /** Constructor for the filter. */ mlt_filter filter_deinterlace_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter != NULL) { filter->process = deinterlace_process; mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "method", arg); mlt_events_listen(MLT_FILTER_PROPERTIES(filter), filter, "service-changed", (mlt_listener) on_service_changed); } return filter; } mlt-7.38.0/src/modules/xine/filter_deinterlace.yml000664 000000 000000 00000000674 15172202314 022142 0ustar00rootroot000000 000000 schema_version: 7.0 type: filter identifier: deinterlace title: Xine Deinterlacer version: 1 copyright: Meltytech, LLC license: GPLv2 language: en url: http://xinehq.de/ tags: - Video - Hidden description: Deinterlace interlaced video. notes: > This is not intended to be created directly. Rather, the loader producer loads it if it is available to set deinterlace interlaced input when the consumer or profile is set to progressive. mlt-7.38.0/src/modules/xine/gpl000664 000000 000000 00000000000 15172202314 016257 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/xine/link_deinterlace.c000664 000000 000000 00000021556 15172202314 021235 0ustar00rootroot000000 000000 /* * link_deinterlace.c * Copyright (C) 2023 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "common.h" #include #include #include #include #include // Private Types typedef struct { // Used by get_frame and get_image int prev_next_required; } private_data; static void link_configure(mlt_link self, mlt_profile chain_profile) { // Operate at the same frame rate as the next link mlt_service_set_profile(MLT_LINK_SERVICE(self), mlt_service_profile(MLT_PRODUCER_SERVICE(self->next))); } static int link_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { int error = 0; mlt_link self = (mlt_link) mlt_frame_pop_service(frame); private_data *pdata = (private_data *) self->child; mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); struct mlt_image_s srcimg = {0}; struct mlt_image_s dstimg = {0}; struct mlt_image_s previmg = {0}; struct mlt_image_s nextimg = {0}; mlt_deinterlacer method = mlt_deinterlacer_id( mlt_properties_get(frame_properties, "consumer.deinterlacer")); if (!mlt_properties_get_int(frame_properties, "consumer.progressive") || method == mlt_deinterlacer_none || mlt_frame_is_test_card(frame)) { mlt_log_debug(MLT_LINK_SERVICE(self), "Do not deinterlace\n"); return mlt_frame_get_image(frame, image, format, width, height, writable); } // At this point, we know we need to deinterlace. method = supported_method(method); if (method == mlt_deinterlacer_weave || method == mlt_deinterlacer_greedy || method >= mlt_deinterlacer_yadif_nospatial) { pdata->prev_next_required = 1; } if (srcimg.data) // Maybe already received during progressive check { if (srcimg.format != mlt_image_yuv422) { error = frame->convert_image(frame, (uint8_t **) &srcimg.data, &srcimg.format, mlt_image_yuv422); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Failed to convert image\n"); return error; } } } else { mlt_image_set_values(&srcimg, NULL, mlt_image_yuv422, *width, *height); error = mlt_frame_get_image(frame, (uint8_t **) &srcimg.data, &srcimg.format, &srcimg.width, &srcimg.height, 0); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Failed to get image\n"); return error; } } mlt_image_set_values(&dstimg, NULL, srcimg.format, srcimg.width, srcimg.height); mlt_image_alloc_data(&dstimg); if (pdata->prev_next_required) { mlt_properties unique_properties = mlt_frame_unique_properties(frame, MLT_LINK_SERVICE(self)); mlt_frame prevframe = mlt_properties_get_data(unique_properties, "prev", NULL); if (prevframe) { mlt_image_set_values(&previmg, NULL, mlt_image_yuv422, srcimg.width, srcimg.height); error = mlt_frame_get_image(prevframe, (uint8_t **) &previmg.data, &previmg.format, &previmg.width, &previmg.height, 0); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Failed to get prev image\n"); previmg.data = NULL; } } mlt_frame nextframe = mlt_properties_get_data(unique_properties, "next", NULL); if (nextframe) { mlt_image_set_values(&nextimg, NULL, mlt_image_yuv422, srcimg.width, srcimg.height); error = mlt_frame_get_image(nextframe, (uint8_t **) &nextimg.data, &nextimg.format, &nextimg.width, &nextimg.height, 0); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Failed to get next image\n"); nextimg.data = NULL; } } } int tff = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "top_field_first"); error = deinterlace_image(&dstimg, &srcimg, &previmg, &nextimg, tff, method); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Deinterlace failed\n"); return error; } mlt_image_get_values(&dstimg, (void **) image, format, width, height); mlt_frame_set_image(frame, dstimg.data, 0, dstimg.release_data); mlt_properties_set_int(frame_properties, "progressive", 1); return 0; } static int link_get_frame(mlt_link self, mlt_frame_ptr frame, int index) { int error = 0; private_data *pdata = (private_data *) self->child; mlt_position frame_pos = mlt_producer_position(MLT_LINK_PRODUCER(self)); mlt_producer_seek(self->next, frame_pos); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), frame, index); mlt_producer original_producer = mlt_frame_get_original_producer(*frame); mlt_producer_probe(original_producer); if (mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(original_producer), "meta.media.progressive") || mlt_properties_get_int(MLT_PRODUCER_PROPERTIES(original_producer), "progressive")) { // Source is progressive. Deinterlace is not required; return error; } mlt_frame prev = NULL; mlt_frame next = NULL; if (pdata->prev_next_required) { mlt_properties unique_properties = mlt_frame_unique_properties(*frame, MLT_LINK_SERVICE(self)); mlt_producer_seek(self->next, frame_pos - 1); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), &prev, index); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Unable to get prev: %d\n", frame_pos); } mlt_properties_set_data(unique_properties, "prev", prev, 0, (mlt_destructor) mlt_frame_close, NULL); mlt_producer_seek(self->next, frame_pos + 1); error = mlt_service_get_frame(MLT_PRODUCER_SERVICE(self->next), &next, index); if (error) { mlt_log_error(MLT_LINK_SERVICE(self), "Unable to get next: %d\n", frame_pos); } mlt_properties_set_data(unique_properties, "next", next, 0, (mlt_destructor) mlt_frame_close, NULL); } mlt_frame_push_service(*frame, self); mlt_frame_push_get_image(*frame, link_get_image); mlt_producer_prepare_next(MLT_LINK_PRODUCER(self)); return error; } static void link_close(mlt_link self) { if (self) { private_data *pdata = (private_data *) self->child; free(pdata); self->close = NULL; self->child = NULL; mlt_link_close(self); free(self); } } mlt_link link_deinterlace_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_link self = mlt_link_init(); private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); if (self && pdata) { self->child = pdata; // Callback registration self->configure = link_configure; self->get_frame = link_get_frame; self->close = link_close; } else { free(pdata); mlt_link_close(self); self = NULL; } return self; } mlt-7.38.0/src/modules/xine/link_deinterlace.yml000664 000000 000000 00000000575 15172202314 021612 0ustar00rootroot000000 000000 schema_version: 7.0 type: link identifier: deinterlace title: Xine Deinterlacer version: 1 copyright: Meltytech, LLC license: GPLv2 language: en url: http://xinehq.de/ tags: - Video - Hidden description: > Deinterlace interlaced video. This link can be added to a chain to normalize video from the producer to provide deinterlaced images if requested by the consumer. mlt-7.38.0/src/modules/xine/vf_yadif_template.h000664 000000 000000 00000025266 15172202314 021432 0ustar00rootroot000000 000000 /* * Copyright (C) 2006 Michael Niedermayer * * SSE2/SSSE3 version (custom optimization) by h.yamagata * * Small fix by Alexander Balakhnin (fizick@avisynth.org.ru) * * MPlayer is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * MPlayer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with MPlayer; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #define LOAD8(mem,dst) \ "movq "mem", "#dst" \n\t"\ "punpcklbw %%xmm7, "#dst" \n\t" #define CHECK(pj,mj) \ "movdqu "#pj"(%[cur],%[mrefs]), %%xmm2 \n\t" /* cur[x-refs-1+j] */\ "movdqu "#mj"(%[cur],%[prefs]), %%xmm3 \n\t" /* cur[x+refs-1-j] */\ "movdqa %%xmm2, %%xmm4 \n\t"\ "movdqa %%xmm2, %%xmm5 \n\t"\ "pxor %%xmm3, %%xmm4 \n\t"\ "pavgb %%xmm3, %%xmm5 \n\t"\ "pand %[pb1], %%xmm4 \n\t"\ "psubusb %%xmm4, %%xmm5 \n\t"\ "psrldq $1, %%xmm5 \n\t"\ "punpcklbw %%xmm7, %%xmm5 \n\t" /* (cur[x-refs+j] + cur[x+refs-j])>>1 */\ "movdqa %%xmm2, %%xmm4 \n\t"\ "psubusb %%xmm3, %%xmm2 \n\t"\ "psubusb %%xmm4, %%xmm3 \n\t"\ "pmaxub %%xmm3, %%xmm2 \n\t"\ "movdqa %%xmm2, %%xmm3 \n\t"\ "movdqa %%xmm2, %%xmm4 \n\t" /* ABS(cur[x-refs-1+j] - cur[x+refs-1-j]) */\ "psrldq $1, %%xmm3 \n\t" /* ABS(cur[x-refs +j] - cur[x+refs -j]) */\ "psrldq $2, %%xmm4 \n\t" /* ABS(cur[x-refs+1+j] - cur[x+refs+1-j]) */\ "punpcklbw %%xmm7, %%xmm2 \n\t"\ "punpcklbw %%xmm7, %%xmm3 \n\t"\ "punpcklbw %%xmm7, %%xmm4 \n\t"\ "paddw %%xmm3, %%xmm2 \n\t"\ "paddw %%xmm4, %%xmm2 \n\t" /* score */ #define CHECK1 \ "movdqa %%xmm0, %%xmm3 \n\t"\ "pcmpgtw %%xmm2, %%xmm3 \n\t" /* if(score < spatial_score) */\ "pminsw %%xmm2, %%xmm0 \n\t" /* spatial_score= score; */\ "movdqa %%xmm3, %%xmm6 \n\t"\ "pand %%xmm3, %%xmm5 \n\t"\ "pandn %%xmm1, %%xmm3 \n\t"\ "por %%xmm5, %%xmm3 \n\t"\ "movdqa %%xmm3, %%xmm1 \n\t" /* spatial_pred= (cur[x-refs+j] + cur[x+refs-j])>>1; */ #define CHECK2 /* pretend not to have checked dir=2 if dir=1 was bad.\ hurts both quality and speed, but matches the C version. */\ "paddw %[pw1], %%xmm6 \n\t"\ "psllw $14, %%xmm6 \n\t"\ "paddsw %%xmm6, %%xmm2 \n\t"\ "movdqa %%xmm0, %%xmm3 \n\t"\ "pcmpgtw %%xmm2, %%xmm3 \n\t"\ "pminsw %%xmm2, %%xmm0 \n\t"\ "pand %%xmm3, %%xmm5 \n\t"\ "pandn %%xmm1, %%xmm3 \n\t"\ "por %%xmm5, %%xmm3 \n\t"\ "movdqa %%xmm3, %%xmm1 \n\t" /* mode argument mod - Fizick */ /* static attribute_align_arg void FILTER_LINE_FUNC_NAME(YadifContext *yadctx, uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int refs, int parity){ const int mode = yadctx->mode; */ static attribute_align_arg void FILTER_LINE_FUNC_NAME(int mode, uint8_t *dst, const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w, int refs, int parity){ DECLARE_ALIGNED(16, uint8_t, tmp0[16]); DECLARE_ALIGNED(16, uint8_t, tmp1[16]); DECLARE_ALIGNED(16, uint8_t, tmp2[16]); DECLARE_ALIGNED(16, uint8_t, tmp3[16]); int x; static DECLARE_ALIGNED(16, const unsigned short, pw_1[]) = { 0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001 }; static DECLARE_ALIGNED(16, const unsigned short, pb_1[]) = { 0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101 }; #define FILTER\ for(x=0; x>1 */\ "movdqa %%xmm0, %[tmp0] \n\t" /* c */\ "movdqa %%xmm3, %[tmp1] \n\t" /* d */\ "movdqa %%xmm1, %[tmp2] \n\t" /* e */\ "psubw %%xmm4, %%xmm2 \n\t"\ PABS( %%xmm4, %%xmm2) /* temporal_diff0 */\ LOAD8("(%[prev],%[mrefs])", %%xmm3) /* prev[x-refs] */\ LOAD8("(%[prev],%[prefs])", %%xmm4) /* prev[x+refs] */\ "psubw %%xmm0, %%xmm3 \n\t"\ "psubw %%xmm1, %%xmm4 \n\t"\ PABS( %%xmm5, %%xmm3)\ PABS( %%xmm5, %%xmm4)\ "paddw %%xmm4, %%xmm3 \n\t" /* temporal_diff1 */\ "psrlw $1, %%xmm2 \n\t"\ "psrlw $1, %%xmm3 \n\t"\ "pmaxsw %%xmm3, %%xmm2 \n\t"\ LOAD8("(%[next],%[mrefs])", %%xmm3) /* next[x-refs] */\ LOAD8("(%[next],%[prefs])", %%xmm4) /* next[x+refs] */\ "psubw %%xmm0, %%xmm3 \n\t"\ "psubw %%xmm1, %%xmm4 \n\t"\ PABS( %%xmm5, %%xmm3)\ PABS( %%xmm5, %%xmm4)\ "paddw %%xmm4, %%xmm3 \n\t" /* temporal_diff2 */\ "psrlw $1, %%xmm3 \n\t"\ "pmaxsw %%xmm3, %%xmm2 \n\t"\ "movdqa %%xmm2, %[tmp3] \n\t" /* diff */\ \ "paddw %%xmm0, %%xmm1 \n\t"\ "paddw %%xmm0, %%xmm0 \n\t"\ "psubw %%xmm1, %%xmm0 \n\t"\ "psrlw $1, %%xmm1 \n\t" /* spatial_pred */\ PABS( %%xmm2, %%xmm0) /* ABS(c-e) */\ \ "movdqu -1(%[cur],%[mrefs]), %%xmm2 \n\t" /* cur[x-refs-1] */\ "movdqu -1(%[cur],%[prefs]), %%xmm3 \n\t" /* cur[x+refs-1] */\ "movdqa %%xmm2, %%xmm4 \n\t"\ "psubusb %%xmm3, %%xmm2 \n\t"\ "psubusb %%xmm4, %%xmm3 \n\t"\ "pmaxub %%xmm3, %%xmm2 \n\t"\ /*"pshuflw $9,%%xmm2, %%xmm3 \n\t"*/\ /*"pshufhw $9,%%xmm2, %%xmm3 \n\t"*/\ "movdqa %%xmm2, %%xmm3 \n\t" /* correct replacement (here) */\ "psrldq $2, %%xmm3 \n\t"/* for "pshufw $9,%%mm2, %%mm3" - fix by Fizick */\ "punpcklbw %%xmm7, %%xmm2 \n\t" /* ABS(cur[x-refs-1] - cur[x+refs-1]) */\ "punpcklbw %%xmm7, %%xmm3 \n\t" /* ABS(cur[x-refs+1] - cur[x+refs+1]) */\ "paddw %%xmm2, %%xmm0 \n\t"\ "paddw %%xmm3, %%xmm0 \n\t"\ "psubw %[pw1], %%xmm0 \n\t" /* spatial_score */\ \ CHECK(-2,0)\ CHECK1\ CHECK(-3,1)\ CHECK2\ CHECK(0,-2)\ CHECK1\ CHECK(1,-3)\ CHECK2\ \ /* if(yadctx->mode<2) ... */\ "movdqa %[tmp3], %%xmm6 \n\t" /* diff */\ "cmpl $2, %[mode] \n\t"\ "jge 1f \n\t"\ LOAD8("(%["prev2"],%[mrefs],2)", %%xmm2) /* prev2[x-2*refs] */\ LOAD8("(%["next2"],%[mrefs],2)", %%xmm4) /* next2[x-2*refs] */\ LOAD8("(%["prev2"],%[prefs],2)", %%xmm3) /* prev2[x+2*refs] */\ LOAD8("(%["next2"],%[prefs],2)", %%xmm5) /* next2[x+2*refs] */\ "paddw %%xmm4, %%xmm2 \n\t"\ "paddw %%xmm5, %%xmm3 \n\t"\ "psrlw $1, %%xmm2 \n\t" /* b */\ "psrlw $1, %%xmm3 \n\t" /* f */\ "movdqa %[tmp0], %%xmm4 \n\t" /* c */\ "movdqa %[tmp1], %%xmm5 \n\t" /* d */\ "movdqa %[tmp2], %%xmm7 \n\t" /* e */\ "psubw %%xmm4, %%xmm2 \n\t" /* b-c */\ "psubw %%xmm7, %%xmm3 \n\t" /* f-e */\ "movdqa %%xmm5, %%xmm0 \n\t"\ "psubw %%xmm4, %%xmm5 \n\t" /* d-c */\ "psubw %%xmm7, %%xmm0 \n\t" /* d-e */\ "movdqa %%xmm2, %%xmm4 \n\t"\ "pminsw %%xmm3, %%xmm2 \n\t"\ "pmaxsw %%xmm4, %%xmm3 \n\t"\ "pmaxsw %%xmm5, %%xmm2 \n\t"\ "pminsw %%xmm5, %%xmm3 \n\t"\ "pmaxsw %%xmm0, %%xmm2 \n\t" /* max */\ "pminsw %%xmm0, %%xmm3 \n\t" /* min */\ "pxor %%xmm4, %%xmm4 \n\t"\ "pmaxsw %%xmm3, %%xmm6 \n\t"\ "psubw %%xmm2, %%xmm4 \n\t" /* -max */\ "pmaxsw %%xmm4, %%xmm6 \n\t" /* diff= MAX3(diff, min, -max); */\ "1: \n\t"\ \ "movdqa %[tmp1], %%xmm2 \n\t" /* d */\ "movdqa %%xmm2, %%xmm3 \n\t"\ "psubw %%xmm6, %%xmm2 \n\t" /* d-diff */\ "paddw %%xmm6, %%xmm3 \n\t" /* d+diff */\ "pmaxsw %%xmm2, %%xmm1 \n\t"\ "pminsw %%xmm3, %%xmm1 \n\t" /* d = clip(spatial_pred, d-diff, d+diff); */\ "packuswb %%xmm1, %%xmm1 \n\t"\ \ :[tmp0]"=m"(tmp0),\ [tmp1]"=m"(tmp1),\ [tmp2]"=m"(tmp2),\ [tmp3]"=m"(tmp3)\ :[prev] "r"(prev),\ [cur] "r"(cur),\ [next] "r"(next),\ [prefs]"r"((intptr_t)refs),\ [mrefs]"r"((intptr_t)-refs),\ [pw1] "m"(*pw_1),\ [pb1] "m"(*pb_1),\ [mode] "g"(mode)\ );\ __asm__ volatile("movq %%xmm1, %0" :"=m"(*dst));\ dst += 8;\ prev+= 8;\ cur += 8;\ next+= 8;\ } if(parity){ #define prev2 "prev" #define next2 "cur" FILTER #undef prev2 #undef next2 }else{ #define prev2 "cur" #define next2 "next" FILTER #undef prev2 #undef next2 } } #undef LOAD8 #undef PABS #undef CHECK #undef CHECK1 #undef CHECK2 #undef FILTER #undef FILTER_LINE_FUNC_NAME mlt-7.38.0/src/modules/xine/xineutils.h000664 000000 000000 00000111210 15172202314 017755 0ustar00rootroot000000 000000 /* * Copyright (C) 2000-2004 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef XINEUTILS_H #define XINEUTILS_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #if HAVE_LIBGEN_H # include #endif //#ifdef XINE_COMPILE # include "attributes.h" //# include "compat.h" //# include "xmlparser.h" //# include "xine_buffer.h" //# include "configfile.h" //#else //# include //# include //# include //# include //# include //#endif //#ifdef HAVE_CONFIG_H //#include "config.h" //#endif #include #include /* * debugable mutexes */ typedef struct { pthread_mutex_t mutex; char id[80]; char *locked_by; } xine_mutex_t; int xine_mutex_init (xine_mutex_t *mutex, const pthread_mutexattr_t *mutexattr, char *id); int xine_mutex_lock (xine_mutex_t *mutex, char *who); int xine_mutex_unlock (xine_mutex_t *mutex, char *who); int xine_mutex_destroy (xine_mutex_t *mutex); /* CPU Acceleration */ /* * The type of an value that fits in an MMX register (note that long * long constant values MUST be suffixed by LL and unsigned long long * values by ULL, lest they be truncated by the compiler) */ /* generic accelerations */ #define MM_ACCEL_MLIB 0x00000001 /* x86 accelerations */ #define MM_ACCEL_X86_MMX 0x80000000 #define MM_ACCEL_X86_3DNOW 0x40000000 #define MM_ACCEL_X86_MMXEXT 0x20000000 #define MM_ACCEL_X86_SSE 0x10000000 #define MM_ACCEL_X86_SSE2 0x08000000 /* powerpc accelerations */ #define MM_ACCEL_PPC_ALTIVEC 0x04000000 /* x86 compat defines */ #define MM_MMX MM_ACCEL_X86_MMX #define MM_3DNOW MM_ACCEL_X86_3DNOW #define MM_MMXEXT MM_ACCEL_X86_MMXEXT #define MM_SSE MM_ACCEL_X86_SSE #define MM_SSE2 MM_ACCEL_X86_SSE2 uint32_t xine_mm_accel (void); #ifdef USE_MMX typedef union { int64_t q; /* Quadword (64-bit) value */ uint64_t uq; /* Unsigned Quadword */ int d[2]; /* 2 Doubleword (32-bit) values */ unsigned int ud[2]; /* 2 Unsigned Doubleword */ short w[4]; /* 4 Word (16-bit) values */ unsigned short uw[4]; /* 4 Unsigned Word */ char b[8]; /* 8 Byte (8-bit) values */ unsigned char ub[8]; /* 8 Unsigned Byte */ float s[2]; /* Single-precision (32-bit) value */ } ATTR_ALIGN(8) mmx_t; /* On an 8-byte (64-bit) boundary */ #define mmx_i2r(op,imm,reg) \ __asm__ __volatile__ (#op " %0, %%" #reg \ : /* nothing */ \ : "i" (imm) ) #define mmx_m2r(op,mem,reg) \ __asm__ __volatile__ (#op " %0, %%" #reg \ : /* nothing */ \ : "m" (mem)) #define mmx_r2m(op,reg,mem) \ __asm__ __volatile__ (#op " %%" #reg ", %0" \ : "=m" (mem) \ : /* nothing */ ) #define mmx_r2r(op,regs,regd) \ __asm__ __volatile__ (#op " %" #regs ", %" #regd) #define emms() __asm__ __volatile__ ("emms") #define movd_m2r(var,reg) mmx_m2r (movd, var, reg) #define movd_r2m(reg,var) mmx_r2m (movd, reg, var) #define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd) #define movq_m2r(var,reg) mmx_m2r (movq, var, reg) #define movq_r2m(reg,var) mmx_r2m (movq, reg, var) #define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd) #define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg) #define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd) #define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg) #define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd) #define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg) #define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd) #define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg) #define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd) #define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg) #define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd) #define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg) #define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd) #define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg) #define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd) #define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg) #define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd) #define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg) #define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd) #define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg) #define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd) #define pand_m2r(var,reg) mmx_m2r (pand, var, reg) #define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd) #define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg) #define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd) #define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg) #define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd) #define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg) #define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd) #define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg) #define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd) #define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg) #define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd) #define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg) #define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd) #define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg) #define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd) #define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg) #define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd) #define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg) #define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd) #define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg) #define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd) #define por_m2r(var,reg) mmx_m2r (por, var, reg) #define por_r2r(regs,regd) mmx_r2r (por, regs, regd) #define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg) #define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg) #define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd) #define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg) #define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg) #define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd) #define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg) #define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg) #define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd) #define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg) #define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg) #define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd) #define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg) #define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg) #define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd) #define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg) #define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg) #define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd) #define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg) #define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg) #define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd) #define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg) #define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg) #define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd) #define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg) #define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd) #define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg) #define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd) #define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg) #define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd) #define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg) #define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd) #define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg) #define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd) #define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg) #define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd) #define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg) #define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd) #define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg) #define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd) #define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg) #define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd) #define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg) #define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd) #define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg) #define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd) #define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg) #define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd) #define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg) #define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd) #define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg) #define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd) /* 3DNOW extensions */ #define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg) #define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd) /* AMD MMX extensions - also available in intel SSE */ #define mmx_m2ri(op,mem,reg,imm) \ __asm__ __volatile__ (#op " %1, %0, %%" #reg \ : /* nothing */ \ : "X" (mem), "X" (imm)) #define mmx_r2ri(op,regs,regd,imm) \ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ : /* nothing */ \ : "X" (imm) ) #define mmx_fetch(mem,hint) \ __asm__ __volatile__ ("prefetch" #hint " %0" \ : /* nothing */ \ : "X" (mem)) #define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg) #define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var) #define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg) #define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd) #define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg) #define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd) #define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm) #define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm) #define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg) #define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd) #define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg) #define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd) #define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg) #define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd) #define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg) #define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd) #define pmovmskb(mmreg,reg) \ __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) #define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg) #define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd) #define prefetcht0(mem) mmx_fetch (mem, t0) #define prefetcht1(mem) mmx_fetch (mem, t1) #define prefetcht2(mem) mmx_fetch (mem, t2) #define prefetchnta(mem) mmx_fetch (mem, nta) #define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg) #define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd) #define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm) #define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm) #define sfence() __asm__ __volatile__ ("sfence\n\t") typedef union { float sf[4]; /* Single-precision (32-bit) value */ } ATTR_ALIGN(16) sse_t; /* On a 16 byte (128-bit) boundary */ #define sse_i2r(op, imm, reg) \ __asm__ __volatile__ (#op " %0, %%" #reg \ : /* nothing */ \ : "X" (imm) ) #define sse_m2r(op, mem, reg) \ __asm__ __volatile__ (#op " %0, %%" #reg \ : /* nothing */ \ : "X" (mem)) #define sse_r2m(op, reg, mem) \ __asm__ __volatile__ (#op " %%" #reg ", %0" \ : "=X" (mem) \ : /* nothing */ ) #define sse_r2r(op, regs, regd) \ __asm__ __volatile__ (#op " %" #regs ", %" #regd) #define sse_r2ri(op, regs, regd, imm) \ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ : /* nothing */ \ : "X" (imm) ) #define sse_m2ri(op, mem, reg, subop) \ __asm__ __volatile__ (#op " %0, %%" #reg ", " #subop \ : /* nothing */ \ : "X" (mem)) #define movaps_m2r(var, reg) sse_m2r(movaps, var, reg) #define movaps_r2m(reg, var) sse_r2m(movaps, reg, var) #define movaps_r2r(regs, regd) sse_r2r(movaps, regs, regd) #define movntps_r2m(xmmreg, var) sse_r2m(movntps, xmmreg, var) #define movups_m2r(var, reg) sse_m2r(movups, var, reg) #define movups_r2m(reg, var) sse_r2m(movups, reg, var) #define movups_r2r(regs, regd) sse_r2r(movups, regs, regd) #define movhlps_r2r(regs, regd) sse_r2r(movhlps, regs, regd) #define movlhps_r2r(regs, regd) sse_r2r(movlhps, regs, regd) #define movhps_m2r(var, reg) sse_m2r(movhps, var, reg) #define movhps_r2m(reg, var) sse_r2m(movhps, reg, var) #define movlps_m2r(var, reg) sse_m2r(movlps, var, reg) #define movlps_r2m(reg, var) sse_r2m(movlps, reg, var) #define movss_m2r(var, reg) sse_m2r(movss, var, reg) #define movss_r2m(reg, var) sse_r2m(movss, reg, var) #define movss_r2r(regs, regd) sse_r2r(movss, regs, regd) #define shufps_m2r(var, reg, index) sse_m2ri(shufps, var, reg, index) #define shufps_r2r(regs, regd, index) sse_r2ri(shufps, regs, regd, index) #define cvtpi2ps_m2r(var, xmmreg) sse_m2r(cvtpi2ps, var, xmmreg) #define cvtpi2ps_r2r(mmreg, xmmreg) sse_r2r(cvtpi2ps, mmreg, xmmreg) #define cvtps2pi_m2r(var, mmreg) sse_m2r(cvtps2pi, var, mmreg) #define cvtps2pi_r2r(xmmreg, mmreg) sse_r2r(cvtps2pi, mmreg, xmmreg) #define cvttps2pi_m2r(var, mmreg) sse_m2r(cvttps2pi, var, mmreg) #define cvttps2pi_r2r(xmmreg, mmreg) sse_r2r(cvttps2pi, mmreg, xmmreg) #define cvtsi2ss_m2r(var, xmmreg) sse_m2r(cvtsi2ss, var, xmmreg) #define cvtsi2ss_r2r(reg, xmmreg) sse_r2r(cvtsi2ss, reg, xmmreg) #define cvtss2si_m2r(var, reg) sse_m2r(cvtss2si, var, reg) #define cvtss2si_r2r(xmmreg, reg) sse_r2r(cvtss2si, xmmreg, reg) #define cvttss2si_m2r(var, reg) sse_m2r(cvtss2si, var, reg) #define cvttss2si_r2r(xmmreg, reg) sse_r2r(cvtss2si, xmmreg, reg) #define movmskps(xmmreg, reg) \ __asm__ __volatile__ ("movmskps %" #xmmreg ", %" #reg) #define addps_m2r(var, reg) sse_m2r(addps, var, reg) #define addps_r2r(regs, regd) sse_r2r(addps, regs, regd) #define addss_m2r(var, reg) sse_m2r(addss, var, reg) #define addss_r2r(regs, regd) sse_r2r(addss, regs, regd) #define subps_m2r(var, reg) sse_m2r(subps, var, reg) #define subps_r2r(regs, regd) sse_r2r(subps, regs, regd) #define subss_m2r(var, reg) sse_m2r(subss, var, reg) #define subss_r2r(regs, regd) sse_r2r(subss, regs, regd) #define mulps_m2r(var, reg) sse_m2r(mulps, var, reg) #define mulps_r2r(regs, regd) sse_r2r(mulps, regs, regd) #define mulss_m2r(var, reg) sse_m2r(mulss, var, reg) #define mulss_r2r(regs, regd) sse_r2r(mulss, regs, regd) #define divps_m2r(var, reg) sse_m2r(divps, var, reg) #define divps_r2r(regs, regd) sse_r2r(divps, regs, regd) #define divss_m2r(var, reg) sse_m2r(divss, var, reg) #define divss_r2r(regs, regd) sse_r2r(divss, regs, regd) #define rcpps_m2r(var, reg) sse_m2r(rcpps, var, reg) #define rcpps_r2r(regs, regd) sse_r2r(rcpps, regs, regd) #define rcpss_m2r(var, reg) sse_m2r(rcpss, var, reg) #define rcpss_r2r(regs, regd) sse_r2r(rcpss, regs, regd) #define rsqrtps_m2r(var, reg) sse_m2r(rsqrtps, var, reg) #define rsqrtps_r2r(regs, regd) sse_r2r(rsqrtps, regs, regd) #define rsqrtss_m2r(var, reg) sse_m2r(rsqrtss, var, reg) #define rsqrtss_r2r(regs, regd) sse_r2r(rsqrtss, regs, regd) #define sqrtps_m2r(var, reg) sse_m2r(sqrtps, var, reg) #define sqrtps_r2r(regs, regd) sse_r2r(sqrtps, regs, regd) #define sqrtss_m2r(var, reg) sse_m2r(sqrtss, var, reg) #define sqrtss_r2r(regs, regd) sse_r2r(sqrtss, regs, regd) #define andps_m2r(var, reg) sse_m2r(andps, var, reg) #define andps_r2r(regs, regd) sse_r2r(andps, regs, regd) #define andnps_m2r(var, reg) sse_m2r(andnps, var, reg) #define andnps_r2r(regs, regd) sse_r2r(andnps, regs, regd) #define orps_m2r(var, reg) sse_m2r(orps, var, reg) #define orps_r2r(regs, regd) sse_r2r(orps, regs, regd) #define xorps_m2r(var, reg) sse_m2r(xorps, var, reg) #define xorps_r2r(regs, regd) sse_r2r(xorps, regs, regd) #define maxps_m2r(var, reg) sse_m2r(maxps, var, reg) #define maxps_r2r(regs, regd) sse_r2r(maxps, regs, regd) #define maxss_m2r(var, reg) sse_m2r(maxss, var, reg) #define maxss_r2r(regs, regd) sse_r2r(maxss, regs, regd) #define minps_m2r(var, reg) sse_m2r(minps, var, reg) #define minps_r2r(regs, regd) sse_r2r(minps, regs, regd) #define minss_m2r(var, reg) sse_m2r(minss, var, reg) #define minss_r2r(regs, regd) sse_r2r(minss, regs, regd) #define cmpps_m2r(var, reg, op) sse_m2ri(cmpps, var, reg, op) #define cmpps_r2r(regs, regd, op) sse_r2ri(cmpps, regs, regd, op) #define cmpeqps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 0) #define cmpeqps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 0) #define cmpltps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 1) #define cmpltps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 1) #define cmpleps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 2) #define cmpleps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 2) #define cmpunordps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 3) #define cmpunordps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 3) #define cmpneqps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 4) #define cmpneqps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 4) #define cmpnltps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 5) #define cmpnltps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 5) #define cmpnleps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 6) #define cmpnleps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 6) #define cmpordps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 7) #define cmpordps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 7) #define cmpss_m2r(var, reg, op) sse_m2ri(cmpss, var, reg, op) #define cmpss_r2r(regs, regd, op) sse_r2ri(cmpss, regs, regd, op) #define cmpeqss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 0) #define cmpeqss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 0) #define cmpltss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 1) #define cmpltss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 1) #define cmpless_m2r(var, reg) sse_m2ri(cmpss, var, reg, 2) #define cmpless_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 2) #define cmpunordss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 3) #define cmpunordss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 3) #define cmpneqss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 4) #define cmpneqss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 4) #define cmpnltss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 5) #define cmpnltss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 5) #define cmpnless_m2r(var, reg) sse_m2ri(cmpss, var, reg, 6) #define cmpnless_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 6) #define cmpordss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 7) #define cmpordss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 7) #define comiss_m2r(var, reg) sse_m2r(comiss, var, reg) #define comiss_r2r(regs, regd) sse_r2r(comiss, regs, regd) #define ucomiss_m2r(var, reg) sse_m2r(ucomiss, var, reg) #define ucomiss_r2r(regs, regd) sse_r2r(ucomiss, regs, regd) #define unpcklps_m2r(var, reg) sse_m2r(unpcklps, var, reg) #define unpcklps_r2r(regs, regd) sse_r2r(unpcklps, regs, regd) #define unpckhps_m2r(var, reg) sse_m2r(unpckhps, var, reg) #define unpckhps_r2r(regs, regd) sse_r2r(unpckhps, regs, regd) #define fxrstor(mem) \ __asm__ __volatile__ ("fxrstor %0" \ : /* nothing */ \ : "X" (mem)) #define fxsave(mem) \ __asm__ __volatile__ ("fxsave %0" \ : /* nothing */ \ : "X" (mem)) #define stmxcsr(mem) \ __asm__ __volatile__ ("stmxcsr %0" \ : /* nothing */ \ : "X" (mem)) #define ldmxcsr(mem) \ __asm__ __volatile__ ("ldmxcsr %0" \ : /* nothing */ \ : "X" (mem)) #endif /* USE_MMX */ /* Optimized/fast memcpy */ /* TODO : fix dll linkage problem for xine_fast_memcpy on win32 xine_fast_memcpy dll linkage is screwy here. declaring as dllimport seems to fix the problem but causes compiler warning with libxineutils */ #ifdef _MSC_VER __declspec( dllimport ) extern void *(* xine_fast_memcpy)(void *to, const void *from, size_t len); #else extern void *(* xine_fast_memcpy)(void *to, const void *from, size_t len); #endif #ifdef HAVE_XINE_INTERNAL_H /* Benchmark available memcpy methods */ void xine_probe_fast_memcpy(xine_t *xine); #endif /* * Debug stuff */ /* * profiling (unworkable in non DEBUG isn't defined) */ void xine_profiler_init (void); int xine_profiler_allocate_slot (char *label); void xine_profiler_start_count (int id); void xine_profiler_stop_count (int id); void xine_profiler_print_results (void); /* * Allocate and clean memory size_t 'size', then return the pointer * to the allocated memory. */ #if !defined(__GNUC__) || __GNUC__ < 3 void *xine_xmalloc(size_t size); #else void *xine_xmalloc(size_t size) __attribute__ ((__malloc__)); #endif /* * Same as above, but memory is aligned to 'alignement'. * **base is used to return pointer to un-aligned memory, use * this to free the mem chunk */ void *xine_xmalloc_aligned(size_t alignment, size_t size, void **base); /* * Get user home directory. */ const char *xine_get_homedir(void); /* * Clean a string (remove spaces and '=' at the begin, * and '\n', '\r' and spaces at the end. */ char *xine_chomp (char *str); /* * A thread-safe usecond sleep */ void xine_usec_sleep(unsigned usec); /* * Some string functions */ void xine_strdupa(char *dest, char *src); #define xine_strdupa(d, s) do { \ (d) = NULL; \ if((s) != NULL) { \ (d) = (char *) alloca(strlen((s)) + 1); \ strcpy((d), (s)); \ } \ } while(0) /* Shamefully copied from glibc 2.2.3 */ #ifdef HAVE_STRPBRK #define xine_strpbrk strpbrk #else static inline char *_private_strpbrk(char *s, const char *accept) { while(*s != '\0') { const char *a = accept; while(*a != '\0') if(*a++ == *s) return s; ++s; } return NULL; } #define xine_strpbrk _private_strpbrk #endif #if defined HAVE_STRSEP && !defined(_MSC_VER) #define xine_strsep strsep #else static inline char *_private_strsep(char **stringp, const char *delim) { char *begin, *end; begin = *stringp; if(begin == NULL) return NULL; if(delim[0] == '\0' || delim[1] == '\0') { char ch = delim[0]; if(ch == '\0') end = NULL; else { if(*begin == ch) end = begin; else if(*begin == '\0') end = NULL; else end = strchr(begin + 1, ch); } } else end = xine_strpbrk(begin, delim); if(end) { *end++ = '\0'; *stringp = end; } else *stringp = NULL; return begin; } #define xine_strsep _private_strsep #endif #ifdef HAVE_SETENV #define xine_setenv setenv #else static inline void _private_setenv(const char *name, const char *val, int _xx) { int len = strlen(name) + strlen(val) + 2; char env[len]; sprintf(env, "%s%c%s", name, '=', val); putenv(env); } #define xine_setenv _private_setenv #endif /* * Color Conversion Utility Functions * The following data structures and functions facilitate the conversion * of RGB images to packed YUV (YUY2) images. There are also functions to * convert from YUV9 -> YV12. All of the meaty details are written in * color.c. */ typedef struct yuv_planes_s { unsigned char *y; unsigned char *u; unsigned char *v; unsigned int row_width; /* frame width */ unsigned int row_count; /* frame height */ } yuv_planes_t; void init_yuv_conversion(void); void init_yuv_planes(yuv_planes_t *yuv_planes, int width, int height); void free_yuv_planes(yuv_planes_t *yuv_planes); extern void (*yuv444_to_yuy2) (yuv_planes_t *yuv_planes, unsigned char *yuy2_map, int pitch); extern void (*yuv9_to_yv12) (unsigned char *y_src, int y_src_pitch, unsigned char *y_dest, int y_dest_pitch, unsigned char *u_src, int u_src_pitch, unsigned char *u_dest, int u_dest_pitch, unsigned char *v_src, int v_src_pitch, unsigned char *v_dest, int v_dest_pitch, int width, int height); extern void (*yuv411_to_yv12) (unsigned char *y_src, int y_src_pitch, unsigned char *y_dest, int y_dest_pitch, unsigned char *u_src, int u_src_pitch, unsigned char *u_dest, int u_dest_pitch, unsigned char *v_src, int v_src_pitch, unsigned char *v_dest, int v_dest_pitch, int width, int height); extern void (*yv12_to_yuy2) (unsigned char *y_src, int y_src_pitch, unsigned char *u_src, int u_src_pitch, unsigned char *v_src, int v_src_pitch, unsigned char *yuy2_map, int yuy2_pitch, int width, int height, int progressive); extern void (*yuy2_to_yv12) (unsigned char *yuy2_map, int yuy2_pitch, unsigned char *y_dst, int y_dst_pitch, unsigned char *u_dst, int u_dst_pitch, unsigned char *v_dst, int v_dst_pitch, int width, int height); #define SCALEFACTOR 65536 #define CENTERSAMPLE 128 #define COMPUTE_Y(r, g, b) \ (unsigned char) \ ((y_r_table[r] + y_g_table[g] + y_b_table[b]) / SCALEFACTOR) #define COMPUTE_U(r, g, b) \ (unsigned char) \ ((u_r_table[r] + u_g_table[g] + u_b_table[b]) / SCALEFACTOR + CENTERSAMPLE) #define COMPUTE_V(r, g, b) \ (unsigned char) \ ((v_r_table[r] + v_g_table[g] + v_b_table[b]) / SCALEFACTOR + CENTERSAMPLE) #define UNPACK_BGR15(packed_pixel, r, g, b) \ b = (packed_pixel & 0x7C00) >> 7; \ g = (packed_pixel & 0x03E0) >> 2; \ r = (packed_pixel & 0x001F) << 3; #define UNPACK_BGR16(packed_pixel, r, g, b) \ b = (packed_pixel & 0xF800) >> 8; \ g = (packed_pixel & 0x07E0) >> 3; \ r = (packed_pixel & 0x001F) << 3; #define UNPACK_RGB15(packed_pixel, r, g, b) \ r = (packed_pixel & 0x7C00) >> 7; \ g = (packed_pixel & 0x03E0) >> 2; \ b = (packed_pixel & 0x001F) << 3; #define UNPACK_RGB16(packed_pixel, r, g, b) \ r = (packed_pixel & 0xF800) >> 8; \ g = (packed_pixel & 0x07E0) >> 3; \ b = (packed_pixel & 0x001F) << 3; extern int y_r_table[256]; extern int y_g_table[256]; extern int y_b_table[256]; extern int u_r_table[256]; extern int u_g_table[256]; extern int u_b_table[256]; extern int v_r_table[256]; extern int v_g_table[256]; extern int v_b_table[256]; /* frame copying functions */ extern void yv12_to_yv12 (unsigned char *y_src, int y_src_pitch, unsigned char *y_dst, int y_dst_pitch, unsigned char *u_src, int u_src_pitch, unsigned char *u_dst, int u_dst_pitch, unsigned char *v_src, int v_src_pitch, unsigned char *v_dst, int v_dst_pitch, int width, int height); extern void yuy2_to_yuy2 (unsigned char *src, int src_pitch, unsigned char *dst, int dst_pitch, int width, int height); /* print a hexdump of the given data */ void xine_hexdump (const char *buf, int length); /* * Optimization macros for conditions * Taken from the FIASCO L4 microkernel sources */ #if !defined(__GNUC__) || __GNUC__ < 3 # define EXPECT_TRUE(x) (x) # define EXPECT_FALSE(x) (x) #else # define EXPECT_TRUE(x) __builtin_expect((x),1) # define EXPECT_FALSE(x) __builtin_expect((x),0) #endif #ifdef NDEBUG #define _x_assert(exp) \ do { \ if (!(exp)) \ fprintf(stderr, "assert: %s:%d: %s: Assertion `%s' failed.\n", \ __FILE__, __LINE__, __XINE_FUNCTION__, #exp); \ } while(0) #else #define _x_assert(exp) \ do { \ if (!(exp)) { \ fprintf(stderr, "assert: %s:%d: %s: Assertion `%s' failed.\n", \ __FILE__, __LINE__, __XINE_FUNCTION__, #exp); \ abort(); \ } \ } while(0) #endif #define _x_abort() \ do { \ fprintf(stderr, "abort: %s:%d: %s: Aborting.\n", \ __FILE__, __LINE__, __XINE_FUNCTION__); \ abort(); \ } while(0) /****** logging with xine **********************************/ #ifndef LOG_MODULE #define LOG_MODULE __FILE__ #endif /* LOG_MODULE */ #define LOG_MODULE_STRING printf("%s: ", LOG_MODULE ); #ifdef LOG_VERBOSE #define LONG_LOG_MODULE_STRING \ printf("%s: (%s:%d) ", LOG_MODULE, __XINE_FUNCTION__, __LINE__ ); #else #define LONG_LOG_MODULE_STRING LOG_MODULE_STRING #endif /* LOG_VERBOSE */ #ifdef LOG #ifdef __GNUC__ #define lprintf(fmt, args...) \ do { \ LONG_LOG_MODULE_STRING \ printf(fmt, ##args); \ } while(0) #else /* __GNUC__ */ #ifdef _MSC_VER #define lprintf(fmtargs) \ do { \ LONG_LOG_MODULE_STRING \ printf("%s", fmtargs); \ } while(0) #else /* _MSC_VER */ #define lprintf(fmt, ...) \ do { \ LONG_LOG_MODULE_STRING \ printf(__VA_ARGS__); \ } while(0) #endif /* _MSC_VER */ #endif /* __GNUC__ */ #else /* LOG */ #ifdef __GNUC__ #define lprintf(fmt, args...) do {} while(0) #else #ifdef _MSC_VER #define lprintf #else #define lprintf(...) do {} while(0) #endif /* _MSC_VER */ #endif /* __GNUC__ */ #endif /* LOG */ #ifdef __GNUC__ #define llprintf(cat, fmt, args...) \ do{ \ if(cat){ \ LONG_LOG_MODULE_STRING \ printf( fmt, ##args ); \ } \ }while(0) #else #ifdef _MSC_VER #define llprintf(cat, fmtargs) \ do{ \ if(cat){ \ LONG_LOG_MODULE_STRING \ printf( "%s", fmtargs ); \ } \ }while(0) #else #define llprintf(cat, ...) \ do{ \ if(cat){ \ LONG_LOG_MODULE_STRING \ printf( __VA_ARGS__ ); \ } \ }while(0) #endif /* _MSC_VER */ #endif /* __GNUC__ */ #ifdef __GNUC__ #define xprintf(xine, verbose, fmt, args...) \ do { \ if((xine) && (xine)->verbosity >= verbose){ \ xine_log(xine, XINE_LOG_TRACE, fmt, ##args); \ } \ } while(0) #else #ifdef _MSC_VER #define xprintf(xine, verbose, fmtargs) \ do { \ if((xine) && (xine)->verbosity >= verbose){ \ xine_log(xine, XINE_LOG_TRACE, fmtargs); \ } \ } while(0) #else #define xprintf(xine, verbose, ...) \ do { \ if((xine) && (xine)->verbosity >= verbose){ \ xine_log(xine, XINE_LOG_TRACE, __VA_ARGS__); \ } \ } while(0) #endif /* _MSC_VER */ #endif /* __GNUC__ */ /* time measuring macros for profiling tasks */ #ifdef DEBUG # define XINE_PROFILE(function) \ do { \ struct timeval current_time; \ double dtime; \ gettimeofday(¤t_time, NULL); \ dtime = -(current_time.tv_sec + (current_time.tv_usec / 1000000.0)); \ function; \ gettimeofday(¤t_time, NULL); \ dtime += current_time.tv_sec + (current_time.tv_usec / 1000000.0); \ printf("%s: (%s:%d) took %lf seconds\n", \ LOG_MODULE, __XINE_FUNCTION__, __LINE__, dtime); \ } while(0) # define XINE_PROFILE_ACCUMULATE(function) \ do { \ struct timeval current_time; \ static double dtime = 0; \ gettimeofday(¤t_time, NULL); \ dtime -= current_time.tv_sec + (current_time.tv_usec / 1000000.0); \ function; \ gettimeofday(¤t_time, NULL); \ dtime += current_time.tv_sec + (current_time.tv_usec / 1000000.0); \ printf("%s: (%s:%d) took %lf seconds\n", \ LOG_MODULE, __XINE_FUNCTION__, __LINE__, dtime); \ } while(0) #else # define XINE_PROFILE(function) function # define XINE_PROFILE_ACCUMULATE(function) function #endif /* LOG */ /******** double chained lists with builtin iterator *******/ typedef struct xine_node_s { struct xine_node_s *next, *prev; void *content; int priority; } xine_node_t; typedef struct { xine_node_t *first, *last, *cur; } xine_list_t; xine_list_t *xine_list_new (void); /** * dispose the whole list. * note: disposes _only_ the list structure, content must be free()d elsewhere */ void xine_list_free(xine_list_t *l); /** * returns: Boolean */ int xine_list_is_empty (xine_list_t *l); /** * return content of first entry in list. */ void *xine_list_first_content (xine_list_t *l); /** * return next content in list. */ void *xine_list_next_content (xine_list_t *l); /** * Return last content of list. */ void *xine_list_last_content (xine_list_t *l); /** * Return previous content of list. */ void *xine_list_prev_content (xine_list_t *l); /** * Append content to list, sorted by decreasing priority. */ void xine_list_append_priority_content (xine_list_t *l, void *content, int priority); /** * Append content to list. */ void xine_list_append_content (xine_list_t *l, void *content); /** * Insert content in list. */ void xine_list_insert_content (xine_list_t *l, void *content); /** * Remove current content in list. * note: removes only the list entry; content must be free()d elsewhere. */ void xine_list_delete_current (xine_list_t *l); #ifndef HAVE_BASENAME /* * get base name */ char *basename (char const *name); #endif #ifdef __cplusplus } #endif #endif mlt-7.38.0/src/modules/xine/yadif.c000664 000000 000000 00000050442 15172202314 017031 0ustar00rootroot000000 000000 /* Yadif C-plugin for Avisynth 2.5 - Yet Another DeInterlacing Filter Copyright (C)2007 Alexander G. Balakhnin aka Fizick http://avisynth.org.ru Port of YADIF filter from MPlayer Copyright (C) 2006 Michael Niedermayer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Avisynth_C plugin Assembler optimized for GNU C compiler */ #include "yadif.h" #include #include #include #define MIN(a,b) ((a) > (b) ? (b) : (a)) #define MAX(a,b) ((a) < (b) ? (b) : (a)) #define ABS(a) ((a) > 0 ? (a) : (-(a))) #define MIN3(a,b,c) MIN(MIN(a,b),c) #define MAX3(a,b,c) MAX(MAX(a,b),c) static void (*filter_line)(int mode, uint8_t *dst, const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w, int refs, int parity); #if defined(__GNUC__) && defined(USE_SSE) #define LOAD4(mem,dst) \ "movd "mem", "#dst" \n\t"\ "punpcklbw %%mm7, "#dst" \n\t" #define PABS(tmp,dst) \ "pxor "#tmp", "#tmp" \n\t"\ "psubw "#dst", "#tmp" \n\t"\ "pmaxsw "#tmp", "#dst" \n\t" #define CHECK(pj,mj) \ "movq "#pj"(%[cur],%[mrefs]), %%mm2 \n\t" /* cur[x-refs-1+j] */\ "movq "#mj"(%[cur],%[prefs]), %%mm3 \n\t" /* cur[x+refs-1-j] */\ "movq %%mm2, %%mm4 \n\t"\ "movq %%mm2, %%mm5 \n\t"\ "pxor %%mm3, %%mm4 \n\t"\ "pavgb %%mm3, %%mm5 \n\t"\ "pand %[pb1], %%mm4 \n\t"\ "psubusb %%mm4, %%mm5 \n\t"\ "psrlq $8, %%mm5 \n\t"\ "punpcklbw %%mm7, %%mm5 \n\t" /* (cur[x-refs+j] + cur[x+refs-j])>>1 */\ "movq %%mm2, %%mm4 \n\t"\ "psubusb %%mm3, %%mm2 \n\t"\ "psubusb %%mm4, %%mm3 \n\t"\ "pmaxub %%mm3, %%mm2 \n\t"\ "movq %%mm2, %%mm3 \n\t"\ "movq %%mm2, %%mm4 \n\t" /* ABS(cur[x-refs-1+j] - cur[x+refs-1-j]) */\ "psrlq $8, %%mm3 \n\t" /* ABS(cur[x-refs +j] - cur[x+refs -j]) */\ "psrlq $16, %%mm4 \n\t" /* ABS(cur[x-refs+1+j] - cur[x+refs+1-j]) */\ "punpcklbw %%mm7, %%mm2 \n\t"\ "punpcklbw %%mm7, %%mm3 \n\t"\ "punpcklbw %%mm7, %%mm4 \n\t"\ "paddw %%mm3, %%mm2 \n\t"\ "paddw %%mm4, %%mm2 \n\t" /* score */ #define CHECK1 \ "movq %%mm0, %%mm3 \n\t"\ "pcmpgtw %%mm2, %%mm3 \n\t" /* if(score < spatial_score) */\ "pminsw %%mm2, %%mm0 \n\t" /* spatial_score= score; */\ "movq %%mm3, %%mm6 \n\t"\ "pand %%mm3, %%mm5 \n\t"\ "pandn %%mm1, %%mm3 \n\t"\ "por %%mm5, %%mm3 \n\t"\ "movq %%mm3, %%mm1 \n\t" /* spatial_pred= (cur[x-refs+j] + cur[x+refs-j])>>1; */ #define CHECK2 /* pretend not to have checked dir=2 if dir=1 was bad.\ hurts both quality and speed, but matches the C version. */\ "paddw %[pw1], %%mm6 \n\t"\ "psllw $14, %%mm6 \n\t"\ "paddsw %%mm6, %%mm2 \n\t"\ "movq %%mm0, %%mm3 \n\t"\ "pcmpgtw %%mm2, %%mm3 \n\t"\ "pminsw %%mm2, %%mm0 \n\t"\ "pand %%mm3, %%mm5 \n\t"\ "pandn %%mm1, %%mm3 \n\t"\ "por %%mm5, %%mm3 \n\t"\ "movq %%mm3, %%mm1 \n\t" static void filter_line_mmx2(int mode, uint8_t *dst, const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w, int refs, int parity){ static const uint64_t pw_1 = 0x0001000100010001ULL; static const uint64_t pb_1 = 0x0101010101010101ULL; // const int mode = p->mode; uint64_t tmp0, tmp1, tmp2, tmp3; int x; #define FILTER\ for(x=0; x>1 */\ "movq %%mm0, %[tmp0] \n\t" /* c */\ "movq %%mm3, %[tmp1] \n\t" /* d */\ "movq %%mm1, %[tmp2] \n\t" /* e */\ "psubw %%mm4, %%mm2 \n\t"\ PABS( %%mm4, %%mm2) /* temporal_diff0 */\ LOAD4("(%[prev],%[mrefs])", %%mm3) /* prev[x-refs] */\ LOAD4("(%[prev],%[prefs])", %%mm4) /* prev[x+refs] */\ "psubw %%mm0, %%mm3 \n\t"\ "psubw %%mm1, %%mm4 \n\t"\ PABS( %%mm5, %%mm3)\ PABS( %%mm5, %%mm4)\ "paddw %%mm4, %%mm3 \n\t" /* temporal_diff1 */\ "psrlw $1, %%mm2 \n\t"\ "psrlw $1, %%mm3 \n\t"\ "pmaxsw %%mm3, %%mm2 \n\t"\ LOAD4("(%[next],%[mrefs])", %%mm3) /* next[x-refs] */\ LOAD4("(%[next],%[prefs])", %%mm4) /* next[x+refs] */\ "psubw %%mm0, %%mm3 \n\t"\ "psubw %%mm1, %%mm4 \n\t"\ PABS( %%mm5, %%mm3)\ PABS( %%mm5, %%mm4)\ "paddw %%mm4, %%mm3 \n\t" /* temporal_diff2 */\ "psrlw $1, %%mm3 \n\t"\ "pmaxsw %%mm3, %%mm2 \n\t"\ "movq %%mm2, %[tmp3] \n\t" /* diff */\ \ "paddw %%mm0, %%mm1 \n\t"\ "paddw %%mm0, %%mm0 \n\t"\ "psubw %%mm1, %%mm0 \n\t"\ "psrlw $1, %%mm1 \n\t" /* spatial_pred */\ PABS( %%mm2, %%mm0) /* ABS(c-e) */\ \ "movq -1(%[cur],%[mrefs]), %%mm2 \n\t" /* cur[x-refs-1] */\ "movq -1(%[cur],%[prefs]), %%mm3 \n\t" /* cur[x+refs-1] */\ "movq %%mm2, %%mm4 \n\t"\ "psubusb %%mm3, %%mm2 \n\t"\ "psubusb %%mm4, %%mm3 \n\t"\ "pmaxub %%mm3, %%mm2 \n\t"\ /*"pshufw $9,%%mm2, %%mm3 \n\t"*/\ "movq %%mm2, %%mm3 \n\t" /* replace for "pshufw $9,%%mm2, %%mm3" - Fizick */\ "psrlq $16, %%mm3 \n\t"/* replace for "pshufw $9,%%mm2, %%mm3" - Fizick*/\ "punpcklbw %%mm7, %%mm2 \n\t" /* ABS(cur[x-refs-1] - cur[x+refs-1]) */\ "punpcklbw %%mm7, %%mm3 \n\t" /* ABS(cur[x-refs+1] - cur[x+refs+1]) */\ "paddw %%mm2, %%mm0 \n\t"\ "paddw %%mm3, %%mm0 \n\t"\ "psubw %[pw1], %%mm0 \n\t" /* spatial_score */\ \ CHECK(-2,0)\ CHECK1\ CHECK(-3,1)\ CHECK2\ CHECK(0,-2)\ CHECK1\ CHECK(1,-3)\ CHECK2\ \ /* if(p->mode<2) ... */\ "movq %[tmp3], %%mm6 \n\t" /* diff */\ "cmpl $2, %[mode] \n\t"\ "jge 1f \n\t"\ LOAD4("(%["prev2"],%[mrefs],2)", %%mm2) /* prev2[x-2*refs] */\ LOAD4("(%["next2"],%[mrefs],2)", %%mm4) /* next2[x-2*refs] */\ LOAD4("(%["prev2"],%[prefs],2)", %%mm3) /* prev2[x+2*refs] */\ LOAD4("(%["next2"],%[prefs],2)", %%mm5) /* next2[x+2*refs] */\ "paddw %%mm4, %%mm2 \n\t"\ "paddw %%mm5, %%mm3 \n\t"\ "psrlw $1, %%mm2 \n\t" /* b */\ "psrlw $1, %%mm3 \n\t" /* f */\ "movq %[tmp0], %%mm4 \n\t" /* c */\ "movq %[tmp1], %%mm5 \n\t" /* d */\ "movq %[tmp2], %%mm7 \n\t" /* e */\ "psubw %%mm4, %%mm2 \n\t" /* b-c */\ "psubw %%mm7, %%mm3 \n\t" /* f-e */\ "movq %%mm5, %%mm0 \n\t"\ "psubw %%mm4, %%mm5 \n\t" /* d-c */\ "psubw %%mm7, %%mm0 \n\t" /* d-e */\ "movq %%mm2, %%mm4 \n\t"\ "pminsw %%mm3, %%mm2 \n\t"\ "pmaxsw %%mm4, %%mm3 \n\t"\ "pmaxsw %%mm5, %%mm2 \n\t"\ "pminsw %%mm5, %%mm3 \n\t"\ "pmaxsw %%mm0, %%mm2 \n\t" /* max */\ "pminsw %%mm0, %%mm3 \n\t" /* min */\ "pxor %%mm4, %%mm4 \n\t"\ "pmaxsw %%mm3, %%mm6 \n\t"\ "psubw %%mm2, %%mm4 \n\t" /* -max */\ "pmaxsw %%mm4, %%mm6 \n\t" /* diff= MAX3(diff, min, -max); */\ "1: \n\t"\ \ "movq %[tmp1], %%mm2 \n\t" /* d */\ "movq %%mm2, %%mm3 \n\t"\ "psubw %%mm6, %%mm2 \n\t" /* d-diff */\ "paddw %%mm6, %%mm3 \n\t" /* d+diff */\ "pmaxsw %%mm2, %%mm1 \n\t"\ "pminsw %%mm3, %%mm1 \n\t" /* d = clip(spatial_pred, d-diff, d+diff); */\ "packuswb %%mm1, %%mm1 \n\t"\ \ :[tmp0]"=m"(tmp0),\ [tmp1]"=m"(tmp1),\ [tmp2]"=m"(tmp2),\ [tmp3]"=m"(tmp3)\ :[prev] "r"(prev),\ [cur] "r"(cur),\ [next] "r"(next),\ [prefs]"r"((intptr_t)refs),\ [mrefs]"r"((intptr_t)-refs),\ [pw1] "m"(pw_1),\ [pb1] "m"(pb_1),\ [mode] "g"(mode)\ );\ asm volatile("movd %%mm1, %0" :"=m"(*dst));\ dst += 4;\ prev+= 4;\ cur += 4;\ next+= 4;\ } if(parity){ #define prev2 "prev" #define next2 "cur" FILTER #undef prev2 #undef next2 }else{ #define prev2 "cur" #define next2 "next" FILTER #undef prev2 #undef next2 } } #undef LOAD4 #undef PABS #undef CHECK #undef CHECK1 #undef CHECK2 #undef FILTER #ifndef attribute_align_arg #if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__>1) # define attribute_align_arg __attribute__((force_align_arg_pointer)) #else # define attribute_align_arg #endif #endif // for proper alignment SSE2 we need in GCC 4.2 and above #if (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__>1) #ifndef DECLARE_ALIGNED #define DECLARE_ALIGNED(n,t,v) t v __attribute__ ((aligned (n))) #endif // ================= SSE2 ================= #if defined(USE_SSE2) && defined(ARCH_X86_64) #define PABS(tmp,dst) \ "pxor "#tmp", "#tmp" \n\t"\ "psubw "#dst", "#tmp" \n\t"\ "pmaxsw "#tmp", "#dst" \n\t" #define FILTER_LINE_FUNC_NAME filter_line_sse2 #include "vf_yadif_template.h" #endif // ================ SSSE3 ================= #ifdef USE_SSE3 #define PABS(tmp,dst) \ "pabsw "#dst", "#dst" \n\t" #define FILTER_LINE_FUNC_NAME filter_line_ssse3 #include "vf_yadif_template.h" #endif #endif // GCC 4.2+ #endif // GNUC, USE_SSE static void filter_line_c(int mode, uint8_t *dst, const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w, int refs, int parity){ int x; const uint8_t *prev2= parity ? prev : cur ; const uint8_t *next2= parity ? cur : next; for(x=0; x>1; int e= cur[+refs]; int temporal_diff0= ABS(prev2[0] - next2[0]); int temporal_diff1=( ABS(prev[-refs] - c) + ABS(prev[+refs] - e) )>>1; int temporal_diff2=( ABS(next[-refs] - c) + ABS(next[+refs] - e) )>>1; int diff= MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2); int spatial_pred= (c+e)>>1; int spatial_score= ABS(cur[-refs-1] - cur[+refs-1]) + ABS(c-e) + ABS(cur[-refs+1] - cur[+refs+1]) - 1; #define CHECK(j)\ { int score= ABS(cur[-refs-1+ j] - cur[+refs-1- j])\ + ABS(cur[-refs + j] - cur[+refs - j])\ + ABS(cur[-refs+1+ j] - cur[+refs+1- j]);\ if(score < spatial_score){\ spatial_score= score;\ spatial_pred= (cur[-refs + j] + cur[+refs - j])>>1;\ CHECK(-1) CHECK(-2) }} }} CHECK( 1) CHECK( 2) }} }} if(mode<2){ int b= (prev2[-2*refs] + next2[-2*refs])>>1; int f= (prev2[+2*refs] + next2[+2*refs])>>1; #if 0 int a= cur[-3*refs]; int g= cur[+3*refs]; int max= MAX3(d-e, d-c, MIN3(MAX(b-c,f-e),MAX(b-c,b-a),MAX(f-g,f-e)) ); int min= MIN3(d-e, d-c, MAX3(MIN(b-c,f-e),MIN(b-c,b-a),MIN(f-g,f-e)) ); #else int max= MAX3(d-e, d-c, MIN(b-c, f-e)); int min= MIN3(d-e, d-c, MAX(b-c, f-e)); #endif diff= MAX3(diff, min, -max); } if(spatial_pred > d + diff) spatial_pred = d + diff; else if(spatial_pred < d - diff) spatial_pred = d - diff; dst[0] = spatial_pred; dst++; cur++; prev++; next++; prev2++; next2++; } } static void interpolate(uint8_t *dst, const uint8_t *cur0, const uint8_t *cur2, int w) { int x; for (x=0; x>1; // simple average } } void filter_plane(int mode, uint8_t *dst, int dst_stride, const uint8_t *prev0, const uint8_t *cur0, const uint8_t *next0, int refs, int w, int h, int parity, int tff, int cpu){ int y; filter_line = filter_line_c; #ifdef __GNUC__ #if (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__>1) #ifdef USE_SSE3 if (cpu & AVS_CPU_SSSE3) filter_line = filter_line_ssse3; else #endif #if defined(USE_SSE2) && defined(ARCH_X86_64) if (cpu & AVS_CPU_SSE2) filter_line = filter_line_sse2; else #endif #endif // GCC 4.2+ #ifdef USE_SSE if (cpu & AVS_CPU_INTEGER_SSE) filter_line = filter_line_mmx2; #endif #endif // GNUC y=0; if(((y ^ parity) & 1)){ memcpy(dst, cur0 + refs, w);// duplicate 1 }else{ memcpy(dst, cur0, w); } y=1; if(((y ^ parity) & 1)){ interpolate(dst + dst_stride, cur0, cur0 + refs*2, w); // interpolate 0 and 2 }else{ memcpy(dst + dst_stride, cur0 + refs, w); // copy original } for(y=2; y= AVS_CPU_INTEGER_SSE) asm volatile("emms"); #endif } #if defined(__GNUC__) && defined(USE_SSE) && !defined(PIC) static attribute_align_arg void YUY2ToPlanes_mmx(const unsigned char *srcYUY2, int pitch_yuy2, int width, int height, unsigned char *py, int pitch_y, unsigned char *pu, unsigned char *pv, int pitch_uv) { /* process by 16 bytes (8 pixels), so width is assumed mod 8 */ int widthdiv2 = width>>1; // static unsigned __int64 Ymask = 0x00FF00FF00FF00FFULL; int h; for (h=0; h> 1; int h; for (h=0; h>1)] = pSrcYUY2[w2+1]; pSrcV[(w>>1)] = pSrcYUY2[w2+3]; } pSrcY += srcPitchY; pSrcU += srcPitchUV; pSrcV += srcPitchUV; pSrcYUY2 += nSrcPitchYUY2; } } //---------------------------------------------------------------------------------------------- void YUY2FromPlanes(unsigned char *pSrcYUY2, int nSrcPitchYUY2, int nWidth, int nHeight, const unsigned char * pSrcY, int srcPitchY, const unsigned char * pSrcU, const unsigned char * pSrcV, int srcPitchUV, int cpu) { int h,w; int w0 = 0; #if defined(__GNUC__) && defined(USE_SSE) && !defined(PIC) if (cpu & AVS_CPU_INTEGER_SSE) { w0 = (nWidth/8)*8; YUY2FromPlanes_mmx(pSrcYUY2, nSrcPitchYUY2, w0, nHeight, pSrcY, srcPitchY, pSrcU, pSrcV, srcPitchUV); } #endif for (h=0; h>1)]; pSrcYUY2[w2+2] = pSrcY[w+1]; pSrcYUY2[w2+3] = pSrcV[(w>>1)]; } pSrcY += srcPitchY; pSrcU += srcPitchUV; pSrcV += srcPitchUV; pSrcYUY2 += nSrcPitchYUY2; } } mlt-7.38.0/src/modules/xine/yadif.h000664 000000 000000 00000004103 15172202314 017027 0ustar00rootroot000000 000000 /* Yadif C-plugin for Avisynth 2.5 - Yet Another DeInterlacing Filter Copyright (C)2007 Alexander G. Balakhnin aka Fizick http://avisynth.org.ru Port of YADIF filter from MPlayer Copyright (C) 2006 Michael Niedermayer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Avisynth_C plugin Assembler optimized for GNU C compiler */ #ifndef YADIF_H_ #define YADIF_H_ #include #define AVS_CPU_INTEGER_SSE 0x1 #define AVS_CPU_SSE2 0x2 #define AVS_CPU_SSSE3 0x4 typedef struct yadif_filter { int cpu; // optimization int yheight; int ypitch; int uvpitch; int ywidth; int uvwidth; unsigned char *ysrc; unsigned char *usrc; unsigned char *vsrc; unsigned char *yprev; unsigned char *uprev; unsigned char *vprev; unsigned char *ynext; unsigned char *unext; unsigned char *vnext; unsigned char *ydest; unsigned char *udest; unsigned char *vdest; } yadif_filter; void filter_plane(int mode, uint8_t *dst, int dst_stride, const uint8_t *prev0, const uint8_t *cur0, const uint8_t *next0, int refs, int w, int h, int parity, int tff, int cpu); void YUY2ToPlanes(const unsigned char *pSrcYUY2, int nSrcPitchYUY2, int nWidth, int nHeight, unsigned char * pSrcY, int srcPitchY, unsigned char * pSrcU, unsigned char * pSrcV, int srcPitchUV, int cpu); void YUY2FromPlanes(unsigned char *pSrcYUY2, int nSrcPitchYUY2, int nWidth, int nHeight, const unsigned char * pSrcY, int srcPitchY, const unsigned char * pSrcU, const unsigned char * pSrcV, int srcPitchUV, int cpu); #endif mlt-7.38.0/src/modules/xml/000775 000000 000000 00000000000 15172202314 015421 5ustar00rootroot000000 000000 mlt-7.38.0/src/modules/xml/CMakeLists.txt000664 000000 000000 00000001700 15172202314 020157 0ustar00rootroot000000 000000 add_library(mltxml MODULE common.c common.h consumer_xml.c factory.c producer_xml.c producer_xml-clip.c ) file(GLOB YML "*.yml") add_custom_target(Other_xml_Files SOURCES ${YML} mlt-xml.dtd ) include(GenerateExportHeader) generate_export_header(mltxml) target_compile_options(mltxml PRIVATE ${MLT_COMPILE_OPTIONS}) target_include_directories(mltxml PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mltxml PRIVATE mlt Threads::Threads PkgConfig::xml) if(MSVC) target_link_libraries(mltxml PRIVATE PThreads4W::PThreads4W) else() target_link_libraries(mltxml PRIVATE m) endif() set_target_properties(mltxml PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}") install(TARGETS mltxml LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR}) install(FILES consumer_xml.yml producer_xml-nogl.yml producer_xml-string.yml producer_xml.yml producer_xml-clip.yml mlt-xml.dtd DESTINATION ${MLT_INSTALL_DATA_DIR}/xml ) mlt-7.38.0/src/modules/xml/common.c000664 000000 000000 00000003550 15172202314 017060 0ustar00rootroot000000 000000 /* * Copyright (C) 2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include // Returns string length of "plain:" prefix if webvx or speed parameter if timewarp. // Otherwise, returns 0. size_t mlt_xml_prefix_size(mlt_properties properties, const char *name, const char *value) { size_t result = 0; if (!strcmp("resource", name)) { const char *mlt_service = mlt_properties_get(properties, "mlt_service"); const char *plain = "plain:"; size_t plain_len = strlen(plain); if (mlt_service && !strcmp("timewarp", mlt_service)) { const char *delimiter = strchr(value, ':'); if (delimiter) result = delimiter - value; if (result && (value[result - 1] == '.' || value[result - 1] == ',' || isdigit(value[result - 1]))) ++result; // include the delimiter else result = 0; // invalid } else if (!strncmp(value, plain, plain_len)) { result = plain_len; } } return result; } mlt-7.38.0/src/modules/xml/common.h000664 000000 000000 00000002001 15172202314 017053 0ustar00rootroot000000 000000 /* * Copyright (C) 2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_XML_COMMON_H #define MLT_XML_COMMON_H #include size_t mlt_xml_prefix_size(mlt_properties properties, const char *name, const char *value); #endif // MLT_XML_COMMON_H mlt-7.38.0/src/modules/xml/consumer_xml.c000664 000000 000000 00000132556 15172202314 020314 0ustar00rootroot000000 000000 /* * consumer_xml.c -- a libxml2 serialiser of mlt service networks * Copyright (C) 2003-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #ifndef _MSC_VER #include #else #include #endif #define ID_SIZE 128 #define TIME_PROPERTY "_consumer_xml" #define _x (const xmlChar *) #define _s (const char *) // This maintains counters for adding ids to elements struct serialise_context_s { mlt_properties id_map; int producer_count; int multitrack_count; int playlist_count; int tractor_count; int filter_count; int transition_count; int chain_count; int link_count; int pass; mlt_properties hide_map; char *root; char *store; int no_meta; mlt_profile profile; mlt_time_format time_format; }; typedef struct serialise_context_s *serialise_context; /** Forward references to static functions. */ static int consumer_start(mlt_consumer parent); static int consumer_stop(mlt_consumer parent); static int consumer_is_stopped(mlt_consumer consumer); static void consumer_close(mlt_consumer parent); static void *consumer_thread(void *arg); static void serialise_service(serialise_context context, mlt_service service, xmlNode *node); typedef enum { xml_existing, xml_producer, xml_multitrack, xml_playlist, xml_tractor, xml_filter, xml_transition, xml_chain, xml_link, } xml_type; /** Create or retrieve an id associated to this service. */ static char *xml_get_id(serialise_context context, mlt_service service, xml_type type) { char *id = NULL; int i = 0; mlt_properties map = context->id_map; // Search the map for the service for (i = 0; i < mlt_properties_count(map); i++) if (mlt_properties_get_data_at(map, i, NULL) == service) break; // If the service is not in the map, and the type indicates a new id is needed... if (i >= mlt_properties_count(map) && type != xml_existing) { // Attempt to reuse existing id id = mlt_properties_get(MLT_SERVICE_PROPERTIES(service), "id"); // If no id, or the id is used in the map (for another service), then // create a new one. if (id == NULL || mlt_properties_get_data(map, id, NULL) != NULL) { char temp[ID_SIZE]; do { switch (type) { case xml_producer: sprintf(temp, "producer%d", context->producer_count++); break; case xml_multitrack: sprintf(temp, "multitrack%d", context->multitrack_count++); break; case xml_playlist: sprintf(temp, "playlist%d", context->playlist_count++); break; case xml_tractor: sprintf(temp, "tractor%d", context->tractor_count++); break; case xml_filter: sprintf(temp, "filter%d", context->filter_count++); break; case xml_transition: sprintf(temp, "transition%d", context->transition_count++); break; case xml_chain: sprintf(temp, "chain%d", context->chain_count++); break; case xml_link: sprintf(temp, "link%d", context->link_count++); break; case xml_existing: // Never gets here break; } } while (mlt_properties_get_data(map, temp, NULL) != NULL); // Set the data at the generated name mlt_properties_set_data(map, temp, service, 0, NULL, NULL); // Get the pointer to the name (i is the end of the list) id = mlt_properties_get_name(map, i); } else { // Store the existing id in the map mlt_properties_set_data(map, id, service, 0, NULL, NULL); } } else if (type == xml_existing) { id = mlt_properties_get_name(map, i); } return id; } /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_xml_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object mlt_consumer consumer = calloc(1, sizeof(struct mlt_consumer_s)); // If no malloc'd and consumer init ok if (consumer != NULL && mlt_consumer_init(consumer, NULL, profile) == 0) { // Allow thread to be started/stopped consumer->start = consumer_start; consumer->stop = consumer_stop; consumer->is_stopped = consumer_is_stopped; // Assign close callback consumer->close = consumer_close; mlt_properties_set(MLT_CONSUMER_PROPERTIES(consumer), "resource", arg); mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "real_time", 0); mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "prefill", 1); mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(consumer), "terminate_on_pause", 1); // Return the consumer produced return consumer; } // malloc or consumer init failed free(consumer); // Indicate failure return NULL; } static void serialise_properties(serialise_context context, mlt_properties properties, xmlNode *node) { int i; xmlNode *p; // Enumerate the properties for (i = 0; i < mlt_properties_count(properties); i++) { char *name = mlt_properties_get_name(properties, i); if (name == NULL || name[0] == '_') { continue; } else if (mlt_properties_get_value(properties, i) != NULL && (!context->no_meta || strncmp(name, "meta.", 5)) && strcmp(name, "mlt") && strcmp(name, "mlt_type") && strcmp(name, "in") && strcmp(name, "out") && strcmp(name, "id") && strcmp(name, "title") && strcmp(name, "root") && strcmp(name, "width") && strcmp(name, "height")) { char *value = mlt_properties_get_value_tf(properties, i, context->time_format); if (value) { int rootlen = strlen(context->root); const char *value_orig = value; size_t prefix_size = mlt_xml_prefix_size(properties, name, value); // Strip off prefix. if (prefix_size) value += prefix_size; // Ignore trailing slash on root. if (rootlen && (context->root[rootlen - 1] == '/' || context->root[rootlen - 1] == '\\')) --rootlen; // convert absolute path to relative if (rootlen && !strncmp(value, context->root, rootlen) && (value[rootlen] == '/' || value[rootlen] == '\\')) { if (prefix_size) { char *s = calloc(1, strlen(value_orig) - rootlen + 1); strncat(s, value_orig, prefix_size); strcat(s, value + rootlen + 1); p = xmlNewTextChild(node, NULL, _x("property"), _x(s)); free(s); } else { p = xmlNewTextChild(node, NULL, _x("property"), _x(value_orig + rootlen + 1)); } } else p = xmlNewTextChild(node, NULL, _x("property"), _x(value_orig)); xmlNewProp(p, _x("name"), _x(name)); } } else if (mlt_properties_get_properties_at(properties, i) != NULL) { mlt_properties child_properties = mlt_properties_get_properties_at(properties, i); p = xmlNewChild(node, NULL, _x("properties"), NULL); xmlNewProp(p, _x("name"), _x(name)); serialise_properties(context, child_properties, p); } } } static void serialise_store_properties(serialise_context context, mlt_properties properties, xmlNode *node, const char *store) { int i; xmlNode *p; // Enumerate the properties for (i = 0; store != NULL && i < mlt_properties_count(properties); i++) { char *name = mlt_properties_get_name(properties, i); if (!strncmp(name, store, strlen(store))) { char *value = mlt_properties_get_value_tf(properties, i, context->time_format); if (value) { int rootlen = strlen(context->root); // convert absolute path to relative if (rootlen && !strncmp(value, context->root, rootlen) && value[rootlen] == '/') p = xmlNewTextChild(node, NULL, _x("property"), _x(value + rootlen + 1)); else p = xmlNewTextChild(node, NULL, _x("property"), _x(value)); xmlNewProp(p, _x("name"), _x(name)); } else if (mlt_properties_get_properties_at(properties, i) != NULL) { mlt_properties child_properties = mlt_properties_get_properties_at(properties, i); p = xmlNewChild(node, NULL, _x("properties"), NULL); xmlNewProp(p, _x("name"), _x(name)); serialise_properties(context, child_properties, p); } } } } static inline void serialise_service_filters(serialise_context context, mlt_service service, xmlNode *node) { int i; xmlNode *p; mlt_filter filter = NULL; // Enumerate the filters for (i = 0; (filter = mlt_producer_filter(MLT_PRODUCER(service), i)) != NULL; i++) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); if (mlt_properties_get_int(properties, "_loader") == 0) { // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, MLT_FILTER_SERVICE(filter), xml_filter); if (id != NULL) { p = xmlNewChild(node, NULL, _x("filter"), NULL); xmlNewProp(p, _x("id"), _x(id)); if (mlt_properties_get(properties, "title")) xmlNewProp(p, _x("title"), _x(mlt_properties_get(properties, "title"))); if (mlt_properties_get_position(properties, "in")) xmlNewProp(p, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); if (mlt_properties_get_position(properties, "out")) xmlNewProp(p, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); serialise_properties(context, properties, p); serialise_service_filters(context, MLT_FILTER_SERVICE(filter), p); } } } } static void serialise_producer(serialise_context context, mlt_service service, xmlNode *node) { xmlNode *child = node; mlt_service parent = MLT_SERVICE(mlt_producer_cut_parent(MLT_PRODUCER(service))); if (context->pass == 0) { mlt_properties properties = MLT_SERVICE_PROPERTIES(parent); // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, parent, xml_producer); if (id == NULL) return; child = xmlNewChild(node, NULL, _x("producer"), NULL); // Set the id xmlNewProp(child, _x("id"), _x(id)); if (mlt_properties_get(properties, "title")) xmlNewProp(child, _x("title"), _x(mlt_properties_get(properties, "title"))); xmlNewProp(child, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); xmlNewProp(child, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); // If the xml producer fails to load a producer, it creates a text producer that says INVALID // and sets the xml_mlt_service property to the original service. const char *xml_mlt_service = mlt_properties_get(properties, "_xml_mlt_service"); if (xml_mlt_service) { // We should not serialize this as a text producer but using the original mlt_service. mlt_properties_set(properties, "mlt_service", xml_mlt_service); } serialise_properties(context, properties, child); serialise_service_filters(context, service, child); // Add producer to the map mlt_properties_set_int(context->hide_map, id, mlt_properties_get_int(properties, "hide")); } else { char *id = xml_get_id(context, parent, xml_existing); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); xmlNewProp(node, _x("parent"), _x(id)); xmlNewProp(node, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); xmlNewProp(node, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); } } static void serialise_tractor(serialise_context context, mlt_service service, xmlNode *node); static void serialise_multitrack(serialise_context context, mlt_service service, xmlNode *node) { int i; if (context->pass == 0) { // Iterate over the tracks to collect the producers for (i = 0; i < mlt_multitrack_count(MLT_MULTITRACK(service)); i++) { mlt_producer producer = mlt_producer_cut_parent( mlt_multitrack_track(MLT_MULTITRACK(service), i)); serialise_service(context, MLT_SERVICE(producer), node); } } else { // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, service, xml_multitrack); if (id == NULL) return; // Serialise the tracks for (i = 0; i < mlt_multitrack_count(MLT_MULTITRACK(service)); i++) { xmlNode *track = xmlNewChild(node, NULL, _x("track"), NULL); int hide = 0; mlt_producer producer = mlt_multitrack_track(MLT_MULTITRACK(service), i); mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer); mlt_service parent = MLT_SERVICE(mlt_producer_cut_parent(producer)); char *id = xml_get_id(context, MLT_SERVICE(parent), xml_existing); xmlNewProp(track, _x("producer"), _x(id)); if (mlt_producer_is_cut(producer)) { xmlNewProp(track, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); xmlNewProp(track, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); serialise_store_properties(context, MLT_PRODUCER_PROPERTIES(producer), track, context->store); serialise_store_properties(context, MLT_PRODUCER_PROPERTIES(producer), track, "xml_"); if (!context->no_meta) serialise_store_properties(context, MLT_PRODUCER_PROPERTIES(producer), track, "meta."); serialise_service_filters(context, MLT_PRODUCER_SERVICE(producer), track); } hide = mlt_properties_get_int(context->hide_map, id); if (hide) xmlNewProp(track, _x("hide"), _x(hide == 1 ? "video" : (hide == 2 ? "audio" : "both"))); } serialise_service_filters(context, service, node); } } static void serialise_playlist(serialise_context context, mlt_service service, xmlNode *node) { int i; xmlNode *child = node; mlt_playlist_clip_info info; mlt_properties properties = MLT_SERVICE_PROPERTIES(service); if (context->pass == 0) { // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, service, xml_playlist); if (id == NULL) return; // Iterate over the playlist entries to collect the producers for (i = 0; i < mlt_playlist_count(MLT_PLAYLIST(service)); i++) { if (!mlt_playlist_get_clip_info(MLT_PLAYLIST(service), &info, i)) { if (info.producer != NULL) { mlt_producer producer = mlt_producer_cut_parent(info.producer); char *service_s = mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "mlt_service"); char *resource_s = mlt_properties_get(MLT_PRODUCER_PROPERTIES(producer), "resource"); if (resource_s != NULL && !strcmp(resource_s, "")) serialise_playlist(context, MLT_SERVICE(producer), node); else if (service_s != NULL && strcmp(service_s, "blank") != 0) serialise_service(context, MLT_SERVICE(producer), node); } } } child = xmlNewChild(node, NULL, _x("playlist"), NULL); // Set the id xmlNewProp(child, _x("id"), _x(id)); if (mlt_properties_get(properties, "title")) xmlNewProp(child, _x("title"), _x(mlt_properties_get(properties, "title"))); // Store application specific properties serialise_store_properties(context, properties, child, context->store); serialise_store_properties(context, properties, child, "xml_"); if (!context->no_meta) serialise_store_properties(context, properties, child, "meta."); // Add producer to the map mlt_properties_set_int(context->hide_map, id, mlt_properties_get_int(properties, "hide")); // Iterate over the playlist entries for (i = 0; i < mlt_playlist_count(MLT_PLAYLIST(service)); i++) { if (!mlt_playlist_get_clip_info(MLT_PLAYLIST(service), &info, i)) { mlt_producer producer = mlt_producer_cut_parent(info.producer); mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); char *service_s = mlt_properties_get(producer_props, "mlt_service"); if (service_s != NULL && strcmp(service_s, "blank") == 0) { xmlNode *entry = xmlNewChild(child, NULL, _x("blank"), NULL); mlt_properties_set_data(producer_props, "_profile", context->profile, 0, NULL, NULL); mlt_properties_set_position(producer_props, TIME_PROPERTY, info.frame_count); xmlNewProp(entry, _x("length"), _x(mlt_properties_get_time(producer_props, TIME_PROPERTY, context->time_format))); } else { char temp[20]; xmlNode *entry = xmlNewChild(child, NULL, _x("entry"), NULL); id = xml_get_id(context, MLT_SERVICE(producer), xml_existing); xmlNewProp(entry, _x("producer"), _x(id)); mlt_properties_set_position(producer_props, TIME_PROPERTY, info.frame_in); xmlNewProp(entry, _x("in"), _x(mlt_properties_get_time(producer_props, TIME_PROPERTY, context->time_format))); mlt_properties_set_position(producer_props, TIME_PROPERTY, info.frame_out); xmlNewProp(entry, _x("out"), _x(mlt_properties_get_time(producer_props, TIME_PROPERTY, context->time_format))); if (info.repeat > 1) { sprintf(temp, "%d", info.repeat); xmlNewProp(entry, _x("repeat"), _x(temp)); } if (mlt_producer_is_cut(info.cut)) { serialise_store_properties(context, MLT_PRODUCER_PROPERTIES(info.cut), entry, context->store); serialise_store_properties(context, MLT_PRODUCER_PROPERTIES(info.cut), entry, "xml_"); if (!context->no_meta) serialise_store_properties(context, MLT_PRODUCER_PROPERTIES(info.cut), entry, "meta."); serialise_service_filters(context, MLT_PRODUCER_SERVICE(info.cut), entry); } } } } serialise_service_filters(context, service, child); } else if (xmlStrcmp(node->name, _x("tractor")) != 0) { char *id = xml_get_id(context, service, xml_existing); xmlNewProp(node, _x("producer"), _x(id)); } } static void serialise_tractor(serialise_context context, mlt_service service, xmlNode *node) { xmlNode *child = node; mlt_properties properties = MLT_SERVICE_PROPERTIES(service); if (context->pass == 0) { // Recurse on connected producer serialise_service(context, mlt_service_producer(service), node); } else { // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, service, xml_tractor); if (id == NULL) return; child = xmlNewChild(node, NULL, _x("tractor"), NULL); // Set the id xmlNewProp(child, _x("id"), _x(id)); if (mlt_properties_get(properties, "title")) xmlNewProp(child, _x("title"), _x(mlt_properties_get(properties, "title"))); if (mlt_properties_get_position(properties, "in") >= 0) xmlNewProp(child, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); if (mlt_properties_get_position(properties, "out") >= 0) xmlNewProp(child, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); // Store application specific properties serialise_store_properties(context, MLT_SERVICE_PROPERTIES(service), child, context->store); serialise_store_properties(context, MLT_SERVICE_PROPERTIES(service), child, "xml_"); if (!context->no_meta) serialise_store_properties(context, MLT_SERVICE_PROPERTIES(service), child, "meta."); // Recurse on connected producer serialise_service(context, mlt_service_producer(service), child); serialise_service_filters(context, service, child); } } static void serialise_filter(serialise_context context, mlt_service service, xmlNode *node) { xmlNode *child = node; mlt_properties properties = MLT_SERVICE_PROPERTIES(service); // Recurse on connected producer serialise_service(context, mlt_service_producer(service), node); if (context->pass == 1) { // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, service, xml_filter); if (id == NULL) return; child = xmlNewChild(node, NULL, _x("filter"), NULL); // Set the id xmlNewProp(child, _x("id"), _x(id)); if (mlt_properties_get(properties, "title")) xmlNewProp(child, _x("title"), _x(mlt_properties_get(properties, "title"))); if (mlt_properties_get_position(properties, "in")) xmlNewProp(child, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); if (mlt_properties_get_position(properties, "out")) xmlNewProp(child, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); serialise_properties(context, properties, child); serialise_service_filters(context, service, child); } } static void serialise_transition(serialise_context context, mlt_service service, xmlNode *node) { xmlNode *child = node; mlt_properties properties = MLT_SERVICE_PROPERTIES(service); // Recurse on connected producer serialise_service(context, MLT_SERVICE(MLT_TRANSITION(service)->producer), node); if (context->pass == 1) { // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, service, xml_transition); if (id == NULL) return; child = xmlNewChild(node, NULL, _x("transition"), NULL); // Set the id xmlNewProp(child, _x("id"), _x(id)); if (mlt_properties_get(properties, "title")) xmlNewProp(child, _x("title"), _x(mlt_properties_get(properties, "title"))); if (mlt_properties_get_position(properties, "in")) xmlNewProp(child, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); if (mlt_properties_get_position(properties, "out")) xmlNewProp(child, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); serialise_properties(context, properties, child); serialise_service_filters(context, service, child); } } static void serialise_link(serialise_context context, mlt_service service, xmlNode *node) { xmlNode *child = node; mlt_properties properties = MLT_SERVICE_PROPERTIES(service); if (context->pass == 0) { // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, service, xml_link); if (id == NULL) return; child = xmlNewChild(node, NULL, _x("link"), NULL); // Set the id xmlNewProp(child, _x("id"), _x(id)); if (mlt_properties_get(properties, "title")) xmlNewProp(child, _x("title"), _x(mlt_properties_get(properties, "title"))); if (mlt_properties_get_position(properties, "in")) { xmlNewProp(child, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); } else if (mlt_properties_get(properties, "in")) { xmlNewProp(child, _x("in"), _x(mlt_properties_get(properties, "in"))); } if (mlt_properties_get_position(properties, "out")) { xmlNewProp(child, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); } else if (mlt_properties_get(properties, "out")) { xmlNewProp(child, _x("out"), _x(mlt_properties_get(properties, "out"))); } serialise_properties(context, properties, child); serialise_service_filters(context, service, child); } } static void serialise_chain(serialise_context context, mlt_service service, xmlNode *node) { int i = 0; xmlNode *child = node; mlt_properties properties = MLT_SERVICE_PROPERTIES(service); if (context->pass == 0) { // Get a new id - if already allocated, do nothing char *id = xml_get_id(context, service, xml_chain); if (id == NULL) return; child = xmlNewChild(node, NULL, _x("chain"), NULL); // Set the id xmlNewProp(child, _x("id"), _x(id)); if (mlt_properties_get(properties, "title")) xmlNewProp(child, _x("title"), _x(mlt_properties_get(properties, "title"))); if (mlt_properties_get_position(properties, "in")) xmlNewProp(child, _x("in"), _x(mlt_properties_get_time(properties, "in", context->time_format))); if (mlt_properties_get_position(properties, "out")) xmlNewProp(child, _x("out"), _x(mlt_properties_get_time(properties, "out", context->time_format))); serialise_properties(context, properties, child); // Serialize links for (i = 0; i < mlt_chain_link_count(MLT_CHAIN(service)); i++) { mlt_link link = mlt_chain_link(MLT_CHAIN(service), i); if (link && mlt_properties_get_int(MLT_LINK_PROPERTIES(link), "_loader") == 0) { serialise_link(context, MLT_LINK_SERVICE(link), child); } } serialise_service_filters(context, service, child); } } static void serialise_service(serialise_context context, mlt_service service, xmlNode *node) { // Iterate over consumer/producer connections while (service != NULL) { mlt_properties properties = MLT_SERVICE_PROPERTIES(service); char *mlt_type = mlt_properties_get(properties, "mlt_type"); // Tell about the producer if (strcmp(mlt_type, "producer") == 0) { char *mlt_service = mlt_properties_get(properties, "mlt_service"); if (mlt_properties_get(properties, "xml") == NULL && (mlt_service != NULL && !strcmp(mlt_service, "tractor"))) { context->pass = 0; serialise_tractor(context, service, node); context->pass = 1; serialise_tractor(context, service, node); context->pass = 0; break; } else { serialise_producer(context, service, node); } if (mlt_properties_get(properties, "xml") != NULL) break; } // Tell about the framework container producers else if (strcmp(mlt_type, "mlt_producer") == 0) { char *resource = mlt_properties_get(properties, "resource"); // Recurse on multitrack's tracks if (resource && strcmp(resource, "") == 0) { serialise_multitrack(context, service, node); break; } // Recurse on playlist's clips else if (resource && strcmp(resource, "") == 0) { serialise_playlist(context, service, node); } // Recurse on tractor's producer else if (resource && strcmp(resource, "") == 0) { context->pass = 0; serialise_tractor(context, service, node); context->pass = 1; serialise_tractor(context, service, node); context->pass = 0; break; } // Treat it as a normal chain else if (mlt_properties_get_int(properties, "_original_type") == mlt_service_chain_type) { serialise_chain(context, service, node); mlt_properties_set(properties, "mlt_type", "chain"); if (mlt_properties_get(properties, "xml") != NULL) break; } // Treat it as a normal producer else { serialise_producer(context, service, node); if (mlt_properties_get(properties, "xml") != NULL) break; } } // Tell about a chain else if (strcmp(mlt_type, "chain") == 0) { serialise_chain(context, service, node); break; } // Tell about a filter else if (strcmp(mlt_type, "filter") == 0) { serialise_filter(context, service, node); break; } // Tell about a transition else if (strcmp(mlt_type, "transition") == 0) { serialise_transition(context, service, node); break; } // Get the next connected service service = mlt_service_producer(service); } } static void serialise_other(mlt_properties properties, struct serialise_context_s *context, xmlNodePtr root) { int i; for (i = 0; i < mlt_properties_count(properties); i++) { const char *name = mlt_properties_get_name(properties, i); if (strlen(name) > 10 && !strncmp(name, "xml_retain", 10)) { mlt_service service = mlt_properties_get_data_at(properties, i, NULL); if (service) { mlt_properties_set_int(MLT_SERVICE_PROPERTIES(service), "xml_retain", 1); serialise_service(context, service, root); } } } } xmlDocPtr xml_make_doc(mlt_consumer consumer, mlt_service service) { mlt_properties properties = MLT_SERVICE_PROPERTIES(service); xmlDocPtr doc = xmlNewDoc(_x("1.0")); xmlNodePtr root = xmlNewNode(NULL, _x("mlt")); struct serialise_context_s *context = calloc(1, sizeof(struct serialise_context_s)); mlt_profile profile = mlt_service_profile(MLT_CONSUMER_SERVICE(consumer)); char tmpstr[32]; xmlDocSetRootElement(doc, root); // Indicate the numeric locale if (mlt_properties_get_lcnumeric(properties)) xmlNewProp(root, _x("LC_NUMERIC"), _x(mlt_properties_get_lcnumeric(properties))); else #ifdef _WIN32 { char *lcnumeric = getlocale(); mlt_properties_set(properties, "_xml_lcnumeric_in", lcnumeric); free(lcnumeric); mlt_properties_to_utf8(properties, "_xml_lcnumeric_in", "_xml_lcnumeric_out"); lcnumeric = mlt_properties_get(properties, "_xml_lcnumeric_out"); xmlNewProp(root, _x("LC_NUMERIC"), _x(lcnumeric)); } #else xmlNewProp(root, _x("LC_NUMERIC"), _x(setlocale(LC_NUMERIC, NULL))); #endif // Indicate the version xmlNewProp(root, _x("version"), _x(mlt_version_get_string())); // If we have root, then deal with it now if (mlt_properties_get(properties, "root") != NULL) { if (!mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "no_root")) xmlNewProp(root, _x("root"), _x(mlt_properties_get(properties, "root"))); context->root = strdup(mlt_properties_get(properties, "root")); } else { context->root = strdup(""); } // Assign the additional 'storage' pattern for properties context->store = mlt_properties_get(MLT_CONSUMER_PROPERTIES(consumer), "store"); context->no_meta = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "no_meta"); const char *time_format = mlt_properties_get(MLT_CONSUMER_PROPERTIES(consumer), "time_format"); if (time_format && (!strcmp(time_format, "smpte") || !strcmp(time_format, "SMPTE") || !strcmp(time_format, "timecode") || !strcmp(time_format, "smpte_df"))) context->time_format = mlt_time_smpte_df; else if (time_format && (!strcmp(time_format, "smpte_ndf"))) context->time_format = mlt_time_smpte_ndf; else if (time_format && (!strcmp(time_format, "clock") || !strcmp(time_format, "CLOCK"))) context->time_format = mlt_time_clock; // Assign a title property if (mlt_properties_get(properties, "title") != NULL) xmlNewProp(root, _x("title"), _x(mlt_properties_get(properties, "title"))); // Add a profile child element if (profile) { if (!mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "no_profile")) { xmlNodePtr profile_node = xmlNewChild(root, NULL, _x("profile"), NULL); if (profile->description) xmlNewProp(profile_node, _x("description"), _x(profile->description)); sprintf(tmpstr, "%d", profile->width); xmlNewProp(profile_node, _x("width"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->height); xmlNewProp(profile_node, _x("height"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->progressive); xmlNewProp(profile_node, _x("progressive"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->sample_aspect_num); xmlNewProp(profile_node, _x("sample_aspect_num"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->sample_aspect_den); xmlNewProp(profile_node, _x("sample_aspect_den"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->display_aspect_num); xmlNewProp(profile_node, _x("display_aspect_num"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->display_aspect_den); xmlNewProp(profile_node, _x("display_aspect_den"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->frame_rate_num); xmlNewProp(profile_node, _x("frame_rate_num"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->frame_rate_den); xmlNewProp(profile_node, _x("frame_rate_den"), _x(tmpstr)); sprintf(tmpstr, "%d", profile->colorspace); xmlNewProp(profile_node, _x("colorspace"), _x(tmpstr)); } context->profile = profile; } // Construct the context maps context->id_map = mlt_properties_new(); context->hide_map = mlt_properties_new(); // Ensure producer is a framework producer mlt_properties_set_int(properties, "_original_type", mlt_service_identify(service)); mlt_properties_set(MLT_SERVICE_PROPERTIES(service), "mlt_type", "mlt_producer"); // In pass one, we serialise the end producers and playlists, // adding them to a map keyed by address. serialise_other(MLT_SERVICE_PROPERTIES(service), context, root); serialise_service(context, service, root); // In pass two, we serialise the tractor and reference the // producers and playlists context->pass++; serialise_other(MLT_SERVICE_PROPERTIES(service), context, root); serialise_service(context, service, root); // Cleanup resource mlt_properties_close(context->id_map); mlt_properties_close(context->hide_map); free(context->root); free(context); return doc; } static void output_xml(mlt_consumer consumer) { // Get the producer service mlt_service service = mlt_service_producer(MLT_CONSUMER_SERVICE(consumer)); mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); char *resource = mlt_properties_get(properties, "resource"); xmlDocPtr doc = NULL; if (!service) return; // Set the title if provided if (mlt_properties_get(properties, "title")) mlt_properties_set(MLT_SERVICE_PROPERTIES(service), "title", mlt_properties_get(properties, "title")); // Check for a root on the consumer properties and pass to service if (mlt_properties_get(properties, "root")) mlt_properties_set(MLT_SERVICE_PROPERTIES(service), "root", mlt_properties_get(properties, "root")); // Specify roots in other cases... if (resource != NULL && mlt_properties_get(properties, "root") == NULL) { // Get the current working directory char *cwd = getcwd(NULL, 0); mlt_properties_set(MLT_SERVICE_PROPERTIES(service), "root", cwd); free(cwd); } // Make the document doc = xml_make_doc(consumer, service); // Handle the output if (resource == NULL || !strcmp(resource, "")) { xmlDocFormatDump(stdout, doc, 1); } else if (strchr(resource, '.') == NULL) { xmlChar *buffer = NULL; int length = 0; xmlDocDumpMemoryEnc(doc, &buffer, &length, "utf-8"); mlt_properties_set(properties, resource, _s(buffer)); #ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet(&xmlFree, NULL, NULL, NULL); #endif xmlFree(buffer); } else { xmlSaveFormatFileEnc(resource, doc, "utf-8", 1); } // Close the document xmlFreeDoc(doc); } static int consumer_start(mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); if (mlt_properties_get_int(properties, "all")) { // Check that we're not already running if (!mlt_properties_get_int(properties, "running")) { // Allocate a thread pthread_t *thread = calloc(1, sizeof(pthread_t)); // Assign the thread to properties mlt_properties_set_data(properties, "thread", thread, sizeof(pthread_t), free, NULL); // Set the running state mlt_properties_set_int(properties, "running", 1); mlt_properties_set_int(properties, "joined", 0); // Create the thread pthread_create(thread, NULL, consumer_thread, consumer); } } else { output_xml(consumer); mlt_consumer_stop(consumer); mlt_consumer_stopped(consumer); } return 0; } static int consumer_is_stopped(mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); return !mlt_properties_get_int(properties, "running"); } static int consumer_stop(mlt_consumer consumer) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Check that we're running if (!mlt_properties_get_int(properties, "joined")) { // Get the thread pthread_t *thread = mlt_properties_get_data(properties, "thread", NULL); // Stop the thread mlt_properties_set_int(properties, "running", 0); mlt_properties_set_int(properties, "joined", 1); // Wait for termination if (thread) pthread_join(*thread, NULL); } return 0; } static void *consumer_thread(void *arg) { // Map the argument to the object mlt_consumer consumer = arg; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Convenience functionality int terminate_on_pause = mlt_properties_get_int(properties, "terminate_on_pause"); int terminated = 0; // Frame and size mlt_frame frame = NULL; int video_off = mlt_properties_get_int(properties, "video_off"); int audio_off = mlt_properties_get_int(properties, "audio_off"); // Loop while running while (!terminated && mlt_properties_get_int(properties, "running")) { // Get the frame frame = mlt_consumer_rt_frame(consumer); // Check for termination if (terminate_on_pause && frame != NULL) terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0; // Check that we have a frame to work with if (frame) { int width = 0, height = 0; int frequency = mlt_properties_get_int(properties, "frequency"); int channels = mlt_properties_get_int(properties, "channels"); float fps = mlt_profile_fps(mlt_service_profile(MLT_CONSUMER_SERVICE(consumer))); int samples = mlt_audio_calculate_frame_samples(fps, frequency, mlt_frame_get_position(frame)); mlt_image_format iformat = mlt_image_yuv422; mlt_audio_format aformat = mlt_audio_s16; uint8_t *buffer; if (!video_off) mlt_frame_get_image(frame, &buffer, &iformat, &width, &height, 0); if (!audio_off) mlt_frame_get_audio(frame, (void **) &buffer, &aformat, &frequency, &channels, &samples); // Close the frame mlt_events_fire(properties, "consumer-frame-show", mlt_event_data_from_frame(frame)); mlt_frame_close(frame); } } output_xml(consumer); // Indicate that the consumer is stopped mlt_properties_set_int(properties, "running", 0); mlt_consumer_stopped(consumer); return NULL; } static void consumer_close(mlt_consumer consumer) { // Stop the consumer mlt_consumer_stop(consumer); // Close the parent mlt_consumer_close(consumer); // Free the memory free(consumer); } mlt-7.38.0/src/modules/xml/consumer_xml.yml000664 000000 000000 00000007155 15172202314 020667 0ustar00rootroot000000 000000 schema_version: 0.3 type: consumer identifier: xml title: XML version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > Serialise the service network to XML. See docs/mlt-xml.txt for more information. notes: > If you set a data property beginning with (and longer than) "xml_retain" on the service connected to this consumer where the data is a mlt_service pointer, then the pointed at service will also be serialized before the connected service. This can be useful, for example, to save a playlist as a media bin along with a multitrack. You can serialize more than one of these additional services by setting more than property, each with a unique key beginning with "xml_retain". bugs: - Untested arbitrary nesting of multitracks and playlists. - > Property "id" is generated as service type followed by number if no property named "id" exists, but it fails to guarantee uniqueness. parameters: - identifier: resource argument: yes title: File type: string description: > The name of a file in which to store the XML. If the value does not contain a period (to start an extension), then the value is interpreted as the name of a property in which to store the XML. This makes it easy for an application to use the consumer to serialize a service network and retrieve the XML in-memory. readonly: no required: no mutable: no default: stdout widget: fileopen - identifier: all title: Process all frames type: boolean description: > Without this option, the XML consumer does not process any frames and simply serializes the service network. However, some filters (.e.g, vid.stab) require two passes where the first pass performs some analysis and stores the result in a property. Therefore, set this property to 1 (true) to cause the consumer to process all frames before serializing to XML. default: 0 - identifier: title title: Title type: string description: > You can give the composition a friendly name that some applications may use. - identifier: root title: Base path type: string description: > If a file name in the XML is relative, but not relative to the current XML file's directory, then you can set the directory to which it is relative here. - identifier: no_meta title: Exclude meta properties type: boolean description: > Set this to disable the output of properties with the prefix "meta." default: 0 widget: checkbox - identifier: no_root title: No root attribute type: boolean description: > Set this to disable the output of the root attribute on the root element. default: 0 widget: checkbox - identifier: time_format title: Time format type: string description: Output time-based values as timecode or clock formats. values: - frames - smpte_df # SMPTE drop-frame timecode - smpte_ndf # SMPTE non-drop-frame timecode - smpte # or SMPTE - same as smpte_df - timecode # same as smpte_df - clock # or CLOCK default: frames widget: dropdown - identifier: store title: Include property prefix type: string description: > To save additional properties that MLT does not know about, supply an application-specific property name prefix that you are using. - identifier: no_profile title: No profile element type: boolean description: Set this to disable the output of the profile element. default: 0 widget: checkbox mlt-7.38.0/src/modules/xml/factory.c000664 000000 000000 00000005433 15172202314 017241 0ustar00rootroot000000 000000 /* * factory.c -- the factory method interfaces * Copyright (C) 2003-2024 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mltxml_export.h" #include #include #include extern mlt_consumer consumer_xml_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_xml_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); extern mlt_producer producer_xmlclip_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg); static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/xml/%s", mlt_environment("MLT_DATA"), (char *) data); return mlt_properties_parse_yaml(file); } MLTXML_EXPORT MLT_REPOSITORY { MLT_REGISTER(mlt_service_consumer_type, "xml", consumer_xml_init); MLT_REGISTER(mlt_service_producer_type, "xml", producer_xml_init); MLT_REGISTER(mlt_service_producer_type, "xml-string", producer_xml_init); MLT_REGISTER(mlt_service_producer_type, "xml-nogl", producer_xml_init); MLT_REGISTER(mlt_service_producer_type, "xml-clip", producer_xmlclip_init); MLT_REGISTER_METADATA(mlt_service_consumer_type, "xml", metadata, "consumer_xml.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "xml", metadata, "producer_xml.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "xml-string", metadata, "producer_xml-string.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "xml-nogl", metadata, "producer_xml-nogl.yml"); MLT_REGISTER_METADATA(mlt_service_producer_type, "xml-clip", metadata, "producer_xml-clip.yml"); } mlt-7.38.0/src/modules/xml/mlt-xml.dtd000664 000000 000000 00000006646 15172202314 017524 0ustar00rootroot000000 000000 mlt-7.38.0/src/modules/xml/producer_xml-clip.c000664 000000 000000 00000022472 15172202314 021224 0ustar00rootroot000000 000000 /* * producer_xml-clip.c -- A wrapper for mlt XML in native profile * Copyright (C) 2024-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include static int producer_get_audio(mlt_frame frame, void **audio, mlt_audio_format *format, int *frequency, int *channels, int *samples) { mlt_producer self = (mlt_producer) mlt_frame_pop_audio(frame); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_PRODUCER_SERVICE(self)); mlt_frame xml_frame = (mlt_frame) mlt_properties_get_data(unique_properties, "xml_frame", NULL); if (!xml_frame) { mlt_log_error(MLT_PRODUCER_SERVICE(self), "XML Frame not found\n"); return 1; } mlt_properties xml_frame_properties = MLT_FRAME_PROPERTIES(xml_frame); mlt_properties_copy(xml_frame_properties, frame_properties, "consumer."); int error = mlt_frame_get_audio(xml_frame, audio, format, frequency, channels, samples); if (error) { mlt_log_error(MLT_PRODUCER_SERVICE(self), "No audio\n"); return 1; } if (*format == mlt_audio_none) { mlt_log_error(MLT_LINK_SERVICE(self), "Audio none\n"); return 1; } mlt_frame_set_audio(frame, *audio, *format, 0, NULL); mlt_properties_set_int(frame_properties, "audio_frequency", *frequency); mlt_properties_set_int(frame_properties, "audio_channels", *channels); mlt_properties_set_int(frame_properties, "audio_samples", *samples); mlt_properties_set_int(frame_properties, "audio_format", *format); mlt_properties_pass_property(frame_properties, xml_frame_properties, "channel_layout"); return 0; } static int producer_get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_producer self = mlt_frame_pop_service(frame); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(self)); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); mlt_properties unique_properties = mlt_frame_get_unique_properties(frame, MLT_PRODUCER_SERVICE(self)); mlt_frame xml_frame = (mlt_frame) mlt_properties_get_data(unique_properties, "xml_frame", NULL); if (!xml_frame) { mlt_log_error(MLT_PRODUCER_SERVICE(self), "XML Frame not found\n"); return 1; } mlt_properties xml_frame_properties = MLT_FRAME_PROPERTIES(xml_frame); mlt_properties_copy(xml_frame_properties, frame_properties, "consumer."); // Force the dimension request to match the profile *width = profile->width; *height = profile->height; int error = mlt_frame_get_image(xml_frame, image, format, width, height, writable); if (error) { mlt_log_error(MLT_PRODUCER_SERVICE(self), "Failed to get image from xml producer\n"); return error; } mlt_frame_set_image(frame, *image, 0, NULL); int size; uint8_t *data = mlt_frame_get_alpha_size(xml_frame, &size); if (data) { mlt_frame_set_alpha(frame, data, size, NULL); } mlt_properties_set_int(frame_properties, "format", *format); mlt_properties_set_int(frame_properties, "width", *width); mlt_properties_set_int(frame_properties, "height", *height); mlt_properties_pass_list( frame_properties, xml_frame_properties, "colorspace aspect_ratio progressive full_range top_field_first color_trc color_primaries"); return error; } static int producer_get_frame(mlt_producer self, mlt_frame_ptr frame, int index) { mlt_producer xml_producer = self->child; *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(self)); mlt_frame_set_position(*frame, mlt_producer_position(self)); mlt_properties unique_properties = mlt_frame_unique_properties(*frame, MLT_PRODUCER_SERVICE(self)); if (!unique_properties) { mlt_log_error(MLT_PRODUCER_SERVICE(self), "Unique properties missing\n"); return 1; } if (mlt_producer_frame(self) != mlt_producer_frame(xml_producer)) { mlt_producer_seek(xml_producer, mlt_producer_frame(self)); } mlt_frame xml_frame = NULL; int result = mlt_service_get_frame(MLT_PRODUCER_SERVICE(xml_producer), &xml_frame, index); if (result) { mlt_log_error(MLT_PRODUCER_SERVICE(self), "Unable to get frame from xml producer\n"); return result; } mlt_frame_push_service(*frame, self); mlt_frame_push_get_image(*frame, producer_get_image); mlt_frame_push_audio(*frame, (void *) self); mlt_frame_push_audio(*frame, producer_get_audio); mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(self)); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); mlt_properties_set_double(frame_properties, "aspect_ratio", mlt_profile_sar(profile)); mlt_properties_set_int(frame_properties, "width", profile->width); mlt_properties_set_int(frame_properties, "height", profile->height); mlt_properties_set_int(frame_properties, "meta.media.width", profile->width); mlt_properties_set_int(frame_properties, "meta.media.height", profile->height); mlt_properties_set_int(frame_properties, "progressive", profile->progressive); mlt_properties_set_int(frame_properties, "colorspace", profile->colorspace); // Save the xml frame for future use and destruction mlt_properties_set_data(unique_properties, "xml_frame", xml_frame, 0, (mlt_destructor) mlt_frame_close, NULL); mlt_producer_prepare_next(self); return result; } static void producer_close(mlt_producer self) { mlt_producer xml_producer = self->child; mlt_producer_close(xml_producer); self->close = NULL; mlt_producer_close(self); free(self); } mlt_producer producer_xmlclip_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_profile native_profile = mlt_profile_init(NULL); mlt_producer xml_producer = mlt_factory_producer(native_profile, "xml", arg); mlt_producer self = mlt_producer_new(native_profile); if (!self || !native_profile || !xml_producer) { mlt_log_error(NULL, "[xml-clip] Failed to allocate producer\n"); mlt_producer_close(self); mlt_producer_close(xml_producer); mlt_profile_close(native_profile); return NULL; } self->get_frame = producer_get_frame; self->close = (mlt_destructor) producer_close; self->child = xml_producer; mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); mlt_properties_set_data(properties, "_profile", native_profile, 0, (mlt_destructor) mlt_profile_close, NULL); mlt_properties_set(properties, "resource", arg); mlt_properties_pass_list(properties, MLT_PRODUCER_PROPERTIES(xml_producer), "out, length"); mlt_properties_set_int(properties, "meta.media.width", native_profile->width); mlt_properties_set_int(properties, "meta.media.height", native_profile->height); mlt_properties_set_int(properties, "meta.media.progressive", native_profile->progressive); mlt_properties_set_int(properties, "meta.media.frame_rate_num", native_profile->frame_rate_num); mlt_properties_set_int(properties, "meta.media.frame_rate_den", native_profile->frame_rate_den); mlt_properties_set_int(properties, "meta.media.sample_aspect_num", native_profile->sample_aspect_num); mlt_properties_set_int(properties, "meta.media.sample_aspect_den", native_profile->sample_aspect_den); mlt_properties_set_int(properties, "meta.media.colorspace", native_profile->colorspace); mlt_properties_set_int(properties, "seekable", 1); // "static_profile" indicates that this producer does not support changing the profile. mlt_properties_set_int(properties, "static_profile", 1); return self; } mlt-7.38.0/src/modules/xml/producer_xml-clip.yml000664 000000 000000 00000001517 15172202314 021600 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: xml-clip title: An XML clip producer version: 1 copyright: Meltytech, LLC license: LGPLv2.1 language: en tags: - Audio - Video description: > This producer encapsulates a producer generated by the XML producer module. The encapsulated producer will be operated at the profile specified in the XML file. This producer will ignore any profile passed into the init routine and will use a profile generated from the XML. This producer is only suitible in a project that has an exact match for the XML profile, or encapsulated in a chain where the frame rate can be adapted. parameters: - identifier: resource argument: yes title: File type: string description: An XML text file containing MLT XML. readonly: no required: yes mutable: no widget: fileopenmlt-7.38.0/src/modules/xml/producer_xml-nogl.yml000664 000000 000000 00000000661 15172202314 021607 0ustar00rootroot000000 000000 schema_version: 0.1 type: producer identifier: xml-nogl title: XML without OpenGL version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > This is the same as the regular "xml" producer except it prevents automatically creating the qglsl consumer when it detects the usage of OpenGL-based services within the XML. See ProducerXml for more information. mlt-7.38.0/src/modules/xml/producer_xml-string.yml000664 000000 000000 00000000726 15172202314 022160 0ustar00rootroot000000 000000 schema_version: 0.1 type: producer identifier: xml-string title: XML String version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > This is the same as the regular "xml" producer except it takes a pointer to a string as the constructor argument. That means it can only be used by applications and not directly exposed to users of those applications. See ProducerXml for more information. mlt-7.38.0/src/modules/xml/producer_xml.c000664 000000 000000 00000261655 15172202314 020307 0ustar00rootroot000000 000000 /* * producer_xml.c -- a libxml2 parser of mlt service networks * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // TODO: destroy unreferenced producers (they are currently destroyed // when the returned producer is closed). #include "common.h" #include #include #include #include #include #ifndef _MSC_VER #include #endif #include #include // for xmlCreateFileParserCtxt #include #define BRANCH_SIG_LEN 4000 #define _x (const xmlChar *) #define _s (const char *) #undef DEBUG #ifdef DEBUG extern xmlDocPtr xml_make_doc(mlt_service service); #endif enum service_type { mlt_invalid_type, mlt_unknown_type, mlt_producer_type, mlt_playlist_type, mlt_entry_type, mlt_tractor_type, mlt_multitrack_type, mlt_filter_type, mlt_transition_type, mlt_consumer_type, mlt_field_type, mlt_services_type, mlt_dummy_filter_type, mlt_dummy_transition_type, mlt_dummy_producer_type, mlt_dummy_consumer_type, mlt_chain_type, mlt_link_type, }; struct deserialise_context_s { mlt_deque stack_types; mlt_deque stack_service; mlt_deque stack_properties; mlt_properties producer_map; mlt_properties destructors; char *property; int is_value; xmlDocPtr value_doc; mlt_deque stack_node; xmlDocPtr entity_doc; int entity_is_replace; mlt_deque stack_branch; const xmlChar *publicId; const xmlChar *systemId; mlt_properties params; mlt_profile profile; mlt_profile consumer_profile; int pass; char *lc_numeric; mlt_consumer consumer; int multi_consumer; int consumer_count; int seekable; mlt_consumer qglsl; }; typedef struct deserialise_context_s *deserialise_context; /** Trim the leading and trailing whitespace from a string in-place. */ static char *trim(char *s) { int n; if (s && (n = strlen(s))) { int i = 0; while (i < n && isspace(s[i])) i++; while (--n && isspace(s[n])) ; n = n - i + 1; if (n > 0) memmove(s, s + i, n); s[n] = 0; } return s; } /** Convert the numerical current branch address to a dot-delimited string. */ static char *serialise_branch(deserialise_context context, char *s) { int i, n = mlt_deque_count(context->stack_branch); s[0] = 0; for (i = 0; i < n - 1; i++) { int len = strlen(s); snprintf(s + len, BRANCH_SIG_LEN - len, "%" PRIu64 ".", (uint64_t) mlt_deque_peek(context->stack_branch, i)); } return s; } /** Push a service. */ static void context_push_service(deserialise_context context, mlt_service that, enum service_type type) { mlt_deque_push_back(context->stack_service, that); mlt_deque_push_back_int(context->stack_types, type); // Record the tree branch on which this service lives if (that != NULL && mlt_properties_get(MLT_SERVICE_PROPERTIES(that), "_xml_branch") == NULL) { char s[BRANCH_SIG_LEN]; mlt_properties_set_string(MLT_SERVICE_PROPERTIES(that), "_xml_branch", serialise_branch(context, s)); } } /** Pop a service. */ static mlt_service context_pop_service(deserialise_context context, enum service_type *type) { mlt_service result = NULL; if (type) *type = mlt_invalid_type; if (mlt_deque_count(context->stack_service) > 0) { result = mlt_deque_pop_back(context->stack_service); if (type != NULL) *type = mlt_deque_pop_back_int(context->stack_types); // Set the service's profile and locale so mlt_property time-to-position conversions can get fps if (result) { mlt_properties_set_data(MLT_SERVICE_PROPERTIES(result), "_profile", context->profile, 0, NULL, NULL); mlt_properties_set_lcnumeric(MLT_SERVICE_PROPERTIES(result), context->lc_numeric); } } return result; } /** Push a node. */ static void context_push_node(deserialise_context context, xmlNodePtr node) { mlt_deque_push_back(context->stack_node, node); } /** Pop a node. */ static xmlNodePtr context_pop_node(deserialise_context context) { return mlt_deque_pop_back(context->stack_node); } // Get the current properties object to add each property to. // The object could be a service, or it could be a properties instance // nested under a service. static mlt_properties current_properties(deserialise_context context) { mlt_properties properties = NULL; if (mlt_deque_count(context->stack_properties)) { // Nested properties are being parsed properties = mlt_deque_peek_back(context->stack_properties); } else if (mlt_deque_count(context->stack_service)) { // Properties belong to the service being parsed mlt_service service = mlt_deque_peek_back(context->stack_service); properties = MLT_SERVICE_PROPERTIES(service); } if (properties) { mlt_properties_set_data(properties, "_profile", context->profile, 0, NULL, NULL); mlt_properties_set_lcnumeric(properties, context->lc_numeric); } return properties; } // Set the destructor on a new service static void track_service(mlt_properties properties, void *service, mlt_destructor destructor) { int registered = mlt_properties_get_int(properties, "registered"); char *key = mlt_properties_get(properties, "registered"); mlt_properties_set_data(properties, key, service, 0, destructor, NULL); mlt_properties_set_int(properties, "registered", ++registered); } static inline int is_known_prefix(const char *resource) { char *prefix = strchr(resource, ':'); if (prefix) { const char *whitelist[] = {"alsa", "avfoundation", "dshow", "fbdev", "gdigrab", "jack", "lavfi", "oss", "pulse", "sndio", "video4linux2", "v4l2", "x11grab", "async", "cache", "concat", "crypto", "data", "ffrtmphttp", "file", "ftp", "fs", "gopher", "hls", "http", "httpproxy", "https", "mmsh", "mmst", "pipe", "rtmp", "rtmpt", "rtp", "srtp", "subfile", "tcp", "udp", "udplite", "unix", "color", "colour", "consumer"}; size_t i, n = prefix - resource; for (i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); ++i) { if (!strncmp(whitelist[i], resource, n)) return 1; } } return 0; } // Prepend the property value with the document root static inline void qualify_property(deserialise_context context, mlt_properties properties, const char *name) { const char *resource_orig = mlt_properties_get(properties, name); char *resource = mlt_properties_get(properties, name); if (resource != NULL && resource[0]) { char *root = mlt_properties_get(context->producer_map, "root"); int n = strlen(root) + strlen(resource) + 2; size_t prefix_size = mlt_xml_prefix_size(properties, name, resource); // Strip off prefix. if (prefix_size) resource += prefix_size; // Qualify file name properties if (root != NULL && strcmp(root, "")) { char *full_resource = calloc(1, n); int drive_letter = strlen(resource) > 3 && resource[1] == ':' && (resource[2] == '/' || resource[2] == '\\'); if (resource[0] != '/' && resource[0] != '\\' && !drive_letter && !is_known_prefix(resource)) { if (prefix_size) strncat(full_resource, resource_orig, prefix_size); strcat(full_resource, root); strcat(full_resource, "/"); strcat(full_resource, resource); } else { strcpy(full_resource, resource_orig); } // Workaround the "&" entity not properly decoded const char *amp_entity = "&"; char *amp = strstr(full_resource, amp_entity); while (amp) { memmove(amp + 1, amp + 5, strlen(amp + 5) + 1); amp = strstr(amp + 1, amp_entity); } mlt_properties_set_string(properties, name, full_resource); free(full_resource); } } } /** This function adds a producer to a playlist or multitrack when there is no entry or track element. */ static int add_producer(deserialise_context context, mlt_service service, mlt_position in, mlt_position out) { // Return value (0 = service remains top of stack, 1 means it can be removed) int result = 0; // Get the parent producer enum service_type type = mlt_invalid_type; mlt_service container = context_pop_service(context, &type); int contained = 0; if (service != NULL && container != NULL) { char *container_branch = mlt_properties_get(MLT_SERVICE_PROPERTIES(container), "_xml_branch"); char *service_branch = mlt_properties_get(MLT_SERVICE_PROPERTIES(service), "_xml_branch"); contained = !strncmp(container_branch, service_branch, strlen(container_branch)); } if (contained) { mlt_properties properties = MLT_SERVICE_PROPERTIES(service); char *hide_s = mlt_properties_get(properties, "hide"); // Indicate that this service is no longer top of stack result = 1; switch (type) { case mlt_tractor_type: { mlt_multitrack multitrack = mlt_tractor_multitrack(MLT_TRACTOR(container)); mlt_multitrack_connect(multitrack, MLT_PRODUCER(service), mlt_multitrack_count(multitrack)); } break; case mlt_multitrack_type: { mlt_multitrack_connect(MLT_MULTITRACK(container), MLT_PRODUCER(service), mlt_multitrack_count(MLT_MULTITRACK(container))); } break; case mlt_playlist_type: { mlt_playlist_append_io(MLT_PLAYLIST(container), MLT_PRODUCER(service), in, out); } break; default: result = 0; mlt_log_warning( NULL, "[producer_xml] Producer defined inside something that isn't a container\n"); break; }; // Set the hide state of the track producer if (hide_s != NULL) { if (strcmp(hide_s, "video") == 0) mlt_properties_set_int(properties, "hide", 1); else if (strcmp(hide_s, "audio") == 0) mlt_properties_set_int(properties, "hide", 2); else if (strcmp(hide_s, "both") == 0) mlt_properties_set_int(properties, "hide", 3); } } // Put the parent producer back if (container != NULL) context_push_service(context, container, type); return result; } /** Attach filters defined on that to this. */ static void attach_filters(mlt_service service, mlt_service that) { if (that != NULL) { int i = 0; mlt_filter filter = NULL; for (i = 0; (filter = mlt_service_filter(that, i)) != NULL; i++) { mlt_service_attach(service, filter); attach_filters(MLT_FILTER_SERVICE(filter), MLT_FILTER_SERVICE(filter)); } } } static void on_start_profile(deserialise_context context, const xmlChar *name, const xmlChar **atts) { mlt_profile p = context->profile; for (; atts != NULL && *atts != NULL; atts += 2) { if (xmlStrcmp(atts[0], _x("name")) == 0 || xmlStrcmp(atts[0], _x("profile")) == 0) { mlt_profile my_profile = mlt_profile_init(_s(atts[1])); if (my_profile) { p->description = strdup(my_profile->description); p->display_aspect_den = my_profile->display_aspect_den; p->display_aspect_num = my_profile->display_aspect_num; p->frame_rate_den = my_profile->frame_rate_den; p->frame_rate_num = my_profile->frame_rate_num; p->width = my_profile->width; p->height = my_profile->height; p->progressive = my_profile->progressive; p->sample_aspect_den = my_profile->sample_aspect_den; p->sample_aspect_num = my_profile->sample_aspect_num; p->colorspace = my_profile->colorspace; p->is_explicit = 1; mlt_profile_close(my_profile); } } else if (xmlStrcmp(atts[0], _x("description")) == 0) { free(p->description); p->description = strdup(_s(atts[1])); p->is_explicit = 1; } else if (xmlStrcmp(atts[0], _x("display_aspect_den")) == 0) p->display_aspect_den = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("display_aspect_num")) == 0) p->display_aspect_num = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("sample_aspect_num")) == 0) p->sample_aspect_num = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("sample_aspect_den")) == 0) p->sample_aspect_den = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("width")) == 0) p->width = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("height")) == 0) p->height = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("progressive")) == 0) p->progressive = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("frame_rate_num")) == 0) p->frame_rate_num = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("frame_rate_den")) == 0) p->frame_rate_den = strtol(_s(atts[1]), NULL, 0); else if (xmlStrcmp(atts[0], _x("colorspace")) == 0) p->colorspace = strtol(_s(atts[1]), NULL, 0); } } static void on_start_tractor(deserialise_context context, const xmlChar *name, const xmlChar **atts) { mlt_tractor tractor = mlt_tractor_new(); mlt_service service = MLT_TRACTOR_SERVICE(tractor); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); track_service(context->destructors, service, (mlt_destructor) mlt_tractor_close); mlt_properties_set_lcnumeric(MLT_SERVICE_PROPERTIES(service), context->lc_numeric); for (; atts != NULL && *atts != NULL; atts += 2) mlt_properties_set_string(MLT_SERVICE_PROPERTIES(service), (const char *) atts[0], atts[1] == NULL ? "" : (const char *) atts[1]); if (mlt_properties_get(properties, "id") != NULL) mlt_properties_set_data(context->producer_map, mlt_properties_get(properties, "id"), service, 0, NULL, NULL); context_push_service(context, service, mlt_tractor_type); } static void on_end_tractor(deserialise_context context, const xmlChar *name) { // Get the tractor enum service_type type; mlt_service tractor = context_pop_service(context, &type); if (tractor != NULL && type == mlt_tractor_type) { // See if the tractor should be added to a playlist or multitrack if (add_producer(context, tractor, 0, mlt_producer_get_out(MLT_PRODUCER(tractor))) == 0) context_push_service(context, tractor, type); } else { mlt_log_error(NULL, "[producer_xml] Invalid state for tractor\n"); } } static void on_start_multitrack(deserialise_context context, const xmlChar *name, const xmlChar **atts) { enum service_type type; mlt_service parent = context_pop_service(context, &type); // If we don't have a parent, then create one now, providing we're in a state where we can if (parent == NULL || (type == mlt_playlist_type || type == mlt_multitrack_type)) { mlt_tractor tractor = NULL; // Push the parent back if (parent != NULL) context_push_service(context, parent, type); // Create a tractor to contain the multitrack tractor = mlt_tractor_new(); parent = MLT_TRACTOR_SERVICE(tractor); track_service(context->destructors, parent, (mlt_destructor) mlt_tractor_close); mlt_properties_set_lcnumeric(MLT_SERVICE_PROPERTIES(parent), context->lc_numeric); type = mlt_tractor_type; // Flag it as a synthesised tractor for clean up later mlt_properties_set_int(MLT_SERVICE_PROPERTIES(parent), "loader_synth", 1); } if (type == mlt_tractor_type) { mlt_service service = MLT_SERVICE(mlt_tractor_multitrack(MLT_TRACTOR(parent))); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); for (; atts != NULL && *atts != NULL; atts += 2) mlt_properties_set_string(properties, (const char *) atts[0], atts[1] == NULL ? "" : (const char *) atts[1]); if (mlt_properties_get(properties, "id") != NULL) mlt_properties_set_data(context->producer_map, mlt_properties_get(properties, "id"), service, 0, NULL, NULL); context_push_service(context, parent, type); context_push_service(context, service, mlt_multitrack_type); } else { mlt_log_error(NULL, "[producer_xml] Invalid multitrack position\n"); } } static void on_end_multitrack(deserialise_context context, const xmlChar *name) { // Get the multitrack from the stack enum service_type type; mlt_service service = context_pop_service(context, &type); if (service == NULL || type != mlt_multitrack_type) mlt_log_error(NULL, "[producer_xml] End multitrack in the wrong state...\n"); } static void on_start_playlist(deserialise_context context, const xmlChar *name, const xmlChar **atts) { mlt_playlist playlist = mlt_playlist_new(context->profile); mlt_service service = MLT_PLAYLIST_SERVICE(playlist); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); track_service(context->destructors, service, (mlt_destructor) mlt_playlist_close); for (; atts != NULL && *atts != NULL; atts += 2) { mlt_properties_set_string(properties, (const char *) atts[0], atts[1] == NULL ? "" : (const char *) atts[1]); // Out will be overwritten later as we append, so we need to save it if (xmlStrcmp(atts[0], _x("out")) == 0) mlt_properties_set_string(properties, "_xml.out", (const char *) atts[1]); } if (mlt_properties_get(properties, "id") != NULL) mlt_properties_set_data(context->producer_map, mlt_properties_get(properties, "id"), service, 0, NULL, NULL); context_push_service(context, service, mlt_playlist_type); } static void on_end_playlist(deserialise_context context, const xmlChar *name) { // Get the playlist from the stack enum service_type type; mlt_service service = context_pop_service(context, &type); if (service != NULL && type == mlt_playlist_type) { mlt_properties properties = MLT_SERVICE_PROPERTIES(service); mlt_position in = -1; mlt_position out = -1; if (mlt_properties_get(properties, "in")) in = mlt_properties_get_position(properties, "in"); if (mlt_properties_get(properties, "out")) out = mlt_properties_get_position(properties, "out"); // See if the playlist should be added to a playlist or multitrack if (add_producer(context, service, in, out) == 0) context_push_service(context, service, type); } else { mlt_log_error(NULL, "[producer_xml] Invalid state of playlist end %d\n", type); } } static void on_start_chain(deserialise_context context, const xmlChar *name, const xmlChar **atts) { mlt_chain chain = mlt_chain_init(context->profile); mlt_service service = MLT_CHAIN_SERVICE(chain); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); track_service(context->destructors, service, (mlt_destructor) mlt_chain_close); for (; atts != NULL && *atts != NULL; atts += 2) { mlt_properties_set_string(properties, (const char *) atts[0], atts[1] == NULL ? "" : (const char *) atts[1]); // Out will be overwritten later as we append, so we need to save it if (xmlStrcmp(atts[0], _x("out")) == 0) mlt_properties_set_string(properties, "_xml.out", (const char *) atts[1]); } if (mlt_properties_get(properties, "id") != NULL) mlt_properties_set_data(context->producer_map, mlt_properties_get(properties, "id"), service, 0, NULL, NULL); context_push_service(context, service, mlt_chain_type); } static void on_end_chain(deserialise_context context, const xmlChar *name) { // Get the chain from the stack enum service_type type; mlt_service service = context_pop_service(context, &type); if (service != NULL && type == mlt_chain_type) { mlt_chain chain = MLT_CHAIN(service); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); mlt_position in = -1; mlt_position out = -1; mlt_producer source = NULL; qualify_property(context, properties, "resource"); char *resource = mlt_properties_get(properties, "resource"); // Let Kino-SMIL src be a synonym for resource if (resource == NULL) { qualify_property(context, properties, "src"); resource = mlt_properties_get(properties, "src"); } // Instantiate the producer if (mlt_properties_get(properties, "mlt_service") != NULL) { char *service_name = trim(mlt_properties_get(properties, "mlt_service")); if (resource) { // If a document was saved as +INVALID.txt (see below), then ignore the mlt_service and // try to load it just from the resource. This is an attempt to recover the failed // producer in case, for example, a file returns. if (!strcmp("qtext", service_name)) { const char *text = mlt_properties_get(properties, "text"); if (text && !strcmp("INVALID", text)) { service_name = NULL; } } else if (!strcmp("pango", service_name)) { const char *markup = mlt_properties_get(properties, "markup"); if (markup && !strcmp("INVALID", markup)) { service_name = NULL; } } if (service_name) { char *temp = calloc(1, strlen(service_name) + strlen(resource) + 2); strcat(temp, service_name); strcat(temp, ":"); strcat(temp, resource); source = mlt_factory_producer(context->profile, NULL, temp); free(temp); } } else { source = mlt_factory_producer(context->profile, NULL, service_name); } } // Just in case the plugin requested doesn't exist... if (!source && resource) source = mlt_factory_producer(context->profile, NULL, resource); if (!source) { mlt_log_error(NULL, "[producer_xml] failed to load chain \"%s\"\n", resource); source = mlt_factory_producer(context->profile, NULL, "+INVALID.txt"); if (source) { // Save the original mlt_service for the consumer to serialize it as original. mlt_properties_set_string(properties, "_xml_mlt_service", mlt_properties_get(properties, "mlt_service")); } } if (!source) source = mlt_factory_producer(context->profile, NULL, "colour:red"); // Propagate properties to the source mlt_properties_inherit(MLT_PRODUCER_PROPERTIES(source), properties); // Add the source producer to the chain mlt_chain_set_source(chain, source); mlt_producer_close(source); mlt_chain_attach_normalizers(chain); // See if the chain should be added to a playlist or multitrack if (mlt_properties_get(properties, "in")) in = mlt_properties_get_position(properties, "in"); if (mlt_properties_get(properties, "out")) out = mlt_properties_get_position(properties, "out"); if (add_producer(context, service, in, out) == 0) context_push_service(context, service, type); } else { mlt_log_error(NULL, "[producer_xml] Invalid state of chain end %d\n", type); } } static void on_start_link(deserialise_context context, const xmlChar *name, const xmlChar **atts) { // Store properties until the service type is known mlt_service service = calloc(1, sizeof(struct mlt_service_s)); mlt_service_init(service, NULL); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); context_push_service(context, service, mlt_link_type); for (; atts != NULL && *atts != NULL; atts += 2) mlt_properties_set_string(properties, (const char *) atts[0], atts[1] == NULL ? "" : (const char *) atts[1]); } static void on_end_link(deserialise_context context, const xmlChar *name) { enum service_type type; mlt_service service = context_pop_service(context, &type); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service(context, &parent_type); if (service != NULL && type == mlt_link_type) { char *id = trim(mlt_properties_get(properties, "mlt_service")); mlt_service link = MLT_SERVICE(mlt_factory_link(id, NULL)); mlt_properties link_props = MLT_SERVICE_PROPERTIES(link); if (!link) { mlt_log_error(NULL, "[producer_xml] failed to load link \"%s\"\n", id); if (parent) context_push_service(context, parent, parent_type); mlt_service_close(service); free(service); return; } track_service(context->destructors, link, (mlt_destructor) mlt_link_close); mlt_properties_set_lcnumeric(MLT_SERVICE_PROPERTIES(link), context->lc_numeric); // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set_string(properties, "mlt_type", NULL); mlt_properties_set_string(properties, "mlt_service", NULL); // Propagate the properties mlt_properties_inherit(link_props, properties); // Attach the link to the chain if (parent != NULL) { if (parent_type == mlt_chain_type) { mlt_chain_attach(MLT_CHAIN(parent), MLT_LINK(link)); } else { mlt_log_error(NULL, "[producer_xml] link can only be added to a chain...\n"); } // Put the parent back on the stack context_push_service(context, parent, parent_type); } else { mlt_log_error(NULL, "[producer_xml] link closed with invalid parent...\n"); } } else { mlt_log_error(NULL, "[producer_xml] Invalid top of stack on link close\n"); } if (service) { mlt_service_close(service); free(service); } } static void on_start_producer(deserialise_context context, const xmlChar *name, const xmlChar **atts) { // use a dummy service to hold properties to allow arbitrary nesting mlt_service service = calloc(1, sizeof(struct mlt_service_s)); mlt_service_init(service, NULL); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); context_push_service(context, service, mlt_dummy_producer_type); for (; atts != NULL && *atts != NULL; atts += 2) mlt_properties_set_string(properties, (const char *) atts[0], atts[1] == NULL ? "" : (const char *) atts[1]); } static void on_end_producer(deserialise_context context, const xmlChar *name) { enum service_type type; mlt_service service = context_pop_service(context, &type); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); if (service != NULL && type == mlt_dummy_producer_type) { mlt_service producer = NULL; qualify_property(context, properties, "resource"); char *resource = mlt_properties_get(properties, "resource"); // Let Kino-SMIL src be a synonym for resource if (resource == NULL) { qualify_property(context, properties, "src"); resource = mlt_properties_get(properties, "src"); } // Instantiate the producer if (mlt_properties_get(properties, "mlt_service") != NULL) { char *service_name = trim(mlt_properties_get(properties, "mlt_service")); if (resource) { // If a document was saved as +INVALID.txt (see below), then ignore the mlt_service and // try to load it just from the resource. This is an attempt to recover the failed // producer in case, for example, a file returns. if (!strcmp("qtext", service_name)) { const char *text = mlt_properties_get(properties, "text"); if (text && !strcmp("INVALID", text)) { service_name = NULL; } } else if (!strcmp("pango", service_name)) { const char *markup = mlt_properties_get(properties, "markup"); if (markup && !strcmp("INVALID", markup)) { service_name = NULL; } } if (service_name) { char *temp = calloc(1, strlen(service_name) + strlen(resource) + 2); strcat(temp, service_name); strcat(temp, ":"); strcat(temp, resource); producer = MLT_SERVICE(mlt_factory_producer(context->profile, NULL, temp)); free(temp); } } else { producer = MLT_SERVICE(mlt_factory_producer(context->profile, NULL, service_name)); } } // Just in case the plugin requested doesn't exist... if (!producer && resource) producer = MLT_SERVICE(mlt_factory_producer(context->profile, NULL, resource)); if (!producer) { mlt_log_error(NULL, "[producer_xml] failed to load producer \"%s\"\n", resource); producer = MLT_SERVICE(mlt_factory_producer(context->profile, NULL, "+INVALID.txt")); if (producer) { // Save the original mlt_service for the consumer to serialize it as original. mlt_properties_set_string(MLT_SERVICE_PROPERTIES(producer), "_xml_mlt_service", mlt_properties_get(properties, "mlt_service")); } } if (!producer) producer = MLT_SERVICE(mlt_factory_producer(context->profile, NULL, "colour:red")); if (!producer) { mlt_service_close(service); free(service); return; } // Track this producer track_service(context->destructors, producer, (mlt_destructor) mlt_producer_close); mlt_properties producer_props = MLT_SERVICE_PROPERTIES(producer); mlt_properties_set_lcnumeric(producer_props, context->lc_numeric); if (mlt_properties_get(producer_props, "seekable")) context->seekable &= mlt_properties_get_int(producer_props, "seekable"); // Propagate the properties qualify_property(context, properties, "resource"); qualify_property(context, properties, "luma"); qualify_property(context, properties, "luma.resource"); qualify_property(context, properties, "composite.luma"); qualify_property(context, properties, "producer.resource"); qualify_property(context, properties, "argument"); // timewarp producer // Handle in/out properties separately mlt_position in = -1; mlt_position out = -1; // Get in if (mlt_properties_get(properties, "in")) in = mlt_properties_get_position(properties, "in"); // Let Kino-SMIL clipBegin be a synonym for in else if (mlt_properties_get(properties, "clipBegin")) in = mlt_properties_get_position(properties, "clipBegin"); // Get out if (mlt_properties_get(properties, "out")) out = mlt_properties_get_position(properties, "out"); // Let Kino-SMIL clipEnd be a synonym for out else if (mlt_properties_get(properties, "clipEnd")) out = mlt_properties_get_position(properties, "clipEnd"); // Remove in and out mlt_properties_clear(properties, "in"); mlt_properties_clear(properties, "out"); // Let a child XML length extend the length of a parent XML clip. if (mlt_properties_get(producer_props, "mlt_service") && (!strcmp("xml", mlt_properties_get(producer_props, "mlt_service")) || !strcmp("consumer", mlt_properties_get(producer_props, "mlt_service"))) && mlt_properties_get_position(producer_props, "length") > mlt_properties_get_position(properties, "length")) { mlt_properties_set_position(properties, "length", mlt_properties_get_position(producer_props, "length")); in = mlt_properties_get_position(producer_props, "in"); out = mlt_properties_get_position(producer_props, "out"); } // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set_string(properties, "mlt_type", NULL); mlt_properties_set_string(properties, "mlt_service", NULL); // Inherit the properties mlt_properties_inherit(producer_props, properties); // Attach all filters from service onto producer attach_filters(producer, service); // Add the producer to the producer map if (mlt_properties_get(properties, "id") != NULL) mlt_properties_set_data(context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL); // See if the producer should be added to a playlist or multitrack if (add_producer(context, producer, in, out) == 0) { // Otherwise, set in and out on... if (in != -1 || out != -1) { // Get the parent service enum service_type type; mlt_service parent = context_pop_service(context, &type); if (parent != NULL) { // Get the parent properties properties = MLT_SERVICE_PROPERTIES(parent); char *resource = mlt_properties_get(properties, "resource"); // Put the parent producer back context_push_service(context, parent, type); // If the parent is a track or entry if (resource && (strcmp(resource, "") == 0)) { if (in > -1) mlt_properties_set_position(properties, "in", in); if (out > -1) mlt_properties_set_position(properties, "out", out); } else { // Otherwise, set in and out on producer directly mlt_producer_set_in_and_out(MLT_PRODUCER(producer), in, out); } } else { // Otherwise, set in and out on producer directly mlt_producer_set_in_and_out(MLT_PRODUCER(producer), in, out); } } // Push the producer onto the stack context_push_service(context, producer, mlt_producer_type); } } if (service) { mlt_service_close(service); free(service); } } static void on_start_blank(deserialise_context context, const xmlChar *name, const xmlChar **atts) { // Get the playlist from the stack enum service_type type; mlt_service service = context_pop_service(context, &type); if (type == mlt_playlist_type && service != NULL) { // Look for the length attribute for (; atts != NULL && *atts != NULL; atts += 2) { if (xmlStrcmp(atts[0], _x("length")) == 0) { // Append a blank to the playlist mlt_playlist_blank_time(MLT_PLAYLIST(service), _s(atts[1])); break; } } // Push the playlist back onto the stack context_push_service(context, service, type); } else { mlt_log_error(NULL, "[producer_xml] blank without a playlist - a definite no no\n"); } } static void on_start_entry(deserialise_context context, const xmlChar *name, const xmlChar **atts) { mlt_producer entry = NULL; mlt_properties temp = mlt_properties_new(); mlt_properties_set_data(temp, "_profile", context->profile, 0, NULL, NULL); mlt_properties_set_lcnumeric(temp, context->lc_numeric); for (; atts != NULL && *atts != NULL; atts += 2) { mlt_properties_set_string(temp, (const char *) atts[0], atts[1] == NULL ? "" : (const char *) atts[1]); // Look for the producer attribute if (xmlStrcmp(atts[0], _x("producer")) == 0) { mlt_producer producer = mlt_properties_get_data(context->producer_map, (const char *) atts[1], NULL); if (producer != NULL) mlt_properties_set_data(temp, "producer", producer, 0, NULL, NULL); } } // If we have a valid entry if (mlt_properties_get_data(temp, "producer", NULL) != NULL) { mlt_playlist_clip_info info; enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service(context, &parent_type); mlt_producer producer = mlt_properties_get_data(temp, "producer", NULL); if (parent_type == mlt_playlist_type) { // Append the producer to the playlist mlt_position in = -1; mlt_position out = -1; if (mlt_properties_get(temp, "in")) in = mlt_properties_get_position(temp, "in"); if (mlt_properties_get(temp, "out")) out = mlt_properties_get_position(temp, "out"); mlt_playlist_append_io(MLT_PLAYLIST(parent), producer, in, out); // Handle the repeat property if (mlt_properties_get_int(temp, "repeat") > 0) { mlt_playlist_repeat_clip(MLT_PLAYLIST(parent), mlt_playlist_count(MLT_PLAYLIST(parent)) - 1, mlt_properties_get_int(temp, "repeat")); } mlt_playlist_get_clip_info(MLT_PLAYLIST(parent), &info, mlt_playlist_count(MLT_PLAYLIST(parent)) - 1); entry = info.cut; } else { mlt_log_error(NULL, "[producer_xml] Entry not part of a playlist...\n"); } context_push_service(context, parent, parent_type); } // Push the cut onto the stack context_push_service(context, MLT_PRODUCER_SERVICE(entry), mlt_entry_type); mlt_properties_close(temp); } static void on_end_entry(deserialise_context context, const xmlChar *name) { // Get the entry from the stack enum service_type entry_type = mlt_invalid_type; mlt_service entry = context_pop_service(context, &entry_type); if (entry == NULL && entry_type != mlt_entry_type) { mlt_log_error(NULL, "[producer_xml] Invalid state at end of entry\n"); } } static void on_start_track(deserialise_context context, const xmlChar *name, const xmlChar **atts) { // use a dummy service to hold properties to allow arbitrary nesting mlt_service service = calloc(1, sizeof(struct mlt_service_s)); mlt_service_init(service, NULL); // Push the dummy service onto the stack context_push_service(context, service, mlt_entry_type); mlt_properties_set_string(MLT_SERVICE_PROPERTIES(service), "resource", ""); for (; atts != NULL && *atts != NULL; atts += 2) { mlt_properties_set_string(MLT_SERVICE_PROPERTIES(service), (const char *) atts[0], atts[1] == NULL ? "" : (const char *) atts[1]); // Look for the producer attribute if (xmlStrcmp(atts[0], _x("producer")) == 0) { mlt_producer producer = mlt_properties_get_data(context->producer_map, (const char *) atts[1], NULL); if (producer != NULL) mlt_properties_set_data(MLT_SERVICE_PROPERTIES(service), "producer", producer, 0, NULL, NULL); } } } static void on_end_track(deserialise_context context, const xmlChar *name) { // Get the track from the stack enum service_type track_type; mlt_service track = context_pop_service(context, &track_type); if (track != NULL && track_type == mlt_entry_type) { mlt_properties track_props = MLT_SERVICE_PROPERTIES(track); enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service(context, &parent_type); mlt_multitrack multitrack = NULL; mlt_producer producer = mlt_properties_get_data(track_props, "producer", NULL); mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); if (parent_type == mlt_tractor_type) multitrack = mlt_tractor_multitrack(MLT_TRACTOR(parent)); else if (parent_type == mlt_multitrack_type) multitrack = MLT_MULTITRACK(parent); else mlt_log_error(NULL, "[producer_xml] track contained in an invalid container\n"); if (multitrack != NULL) { // Set producer i/o if specified if (mlt_properties_get(track_props, "in") != NULL || mlt_properties_get(track_props, "out") != NULL) { mlt_position in = -1; mlt_position out = -1; if (mlt_properties_get(track_props, "in")) in = mlt_properties_get_position(track_props, "in"); if (mlt_properties_get(track_props, "out")) out = mlt_properties_get_position(track_props, "out"); mlt_producer cut = mlt_producer_cut(MLT_PRODUCER(producer), in, out); mlt_multitrack_connect(multitrack, cut, mlt_multitrack_count(multitrack)); mlt_properties_inherit(MLT_PRODUCER_PROPERTIES(cut), track_props); track_props = MLT_PRODUCER_PROPERTIES(cut); mlt_producer_close(cut); } else { mlt_multitrack_connect(multitrack, producer, mlt_multitrack_count(multitrack)); } // Set the hide state of the track producer char *hide_s = mlt_properties_get(track_props, "hide"); if (hide_s != NULL) { if (strcmp(hide_s, "video") == 0) mlt_properties_set_int(producer_props, "hide", 1); else if (strcmp(hide_s, "audio") == 0) mlt_properties_set_int(producer_props, "hide", 2); else if (strcmp(hide_s, "both") == 0) mlt_properties_set_int(producer_props, "hide", 3); } } if (parent != NULL) context_push_service(context, parent, parent_type); } else { mlt_log_error(NULL, "[producer_xml] Invalid state at end of track\n"); } if (track) { mlt_service_close(track); free(track); } } static void on_start_filter(deserialise_context context, const xmlChar *name, const xmlChar **atts) { // use a dummy service to hold properties to allow arbitrary nesting mlt_service service = calloc(1, sizeof(struct mlt_service_s)); mlt_service_init(service, NULL); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); context_push_service(context, service, mlt_dummy_filter_type); // Set the properties for (; atts != NULL && *atts != NULL; atts += 2) mlt_properties_set_string(properties, (const char *) atts[0], (const char *) atts[1]); } static void on_end_filter(deserialise_context context, const xmlChar *name) { enum service_type type; mlt_service service = context_pop_service(context, &type); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service(context, &parent_type); if (service != NULL && type == mlt_dummy_filter_type) { char *id = trim(mlt_properties_get(properties, "mlt_service")); mlt_service filter = MLT_SERVICE(mlt_factory_filter(context->profile, id, NULL)); mlt_properties filter_props = MLT_SERVICE_PROPERTIES(filter); if (!filter) { mlt_log_error(NULL, "[producer_xml] failed to load filter \"%s\"\n", id); if (parent) context_push_service(context, parent, parent_type); mlt_service_close(service); free(service); return; } track_service(context->destructors, filter, (mlt_destructor) mlt_filter_close); mlt_properties_set_lcnumeric(MLT_SERVICE_PROPERTIES(filter), context->lc_numeric); // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set_string(properties, "mlt_type", NULL); mlt_properties_set_string(properties, "mlt_service", NULL); // Propagate the properties qualify_property(context, properties, "resource"); qualify_property(context, properties, "luma"); qualify_property(context, properties, "luma.resource"); qualify_property(context, properties, "composite.luma"); qualify_property(context, properties, "producer.resource"); qualify_property(context, properties, "filename"); qualify_property(context, properties, "av.file"); qualify_property(context, properties, "av.filename"); qualify_property(context, properties, "filter.resource"); mlt_properties_inherit(filter_props, properties); // Attach all filters from service onto filter attach_filters(filter, service); // Associate the filter with the parent if (parent != NULL) { if (parent_type == mlt_tractor_type && mlt_properties_get(properties, "track")) { mlt_field field = mlt_tractor_field(MLT_TRACTOR(parent)); mlt_field_plant_filter(field, MLT_FILTER(filter), mlt_properties_get_int(properties, "track")); mlt_filter_set_in_and_out(MLT_FILTER(filter), mlt_properties_get_int(properties, "in"), mlt_properties_get_int(properties, "out")); } else { mlt_service_attach(parent, MLT_FILTER(filter)); } // Put the parent back on the stack context_push_service(context, parent, parent_type); } else { mlt_log_error(NULL, "[producer_xml] filter closed with invalid parent...\n"); } } else { mlt_log_error(NULL, "[producer_xml] Invalid top of stack on filter close\n"); } if (service) { mlt_service_close(service); free(service); } } static void on_start_transition(deserialise_context context, const xmlChar *name, const xmlChar **atts) { // use a dummy service to hold properties to allow arbitrary nesting mlt_service service = calloc(1, sizeof(struct mlt_service_s)); mlt_service_init(service, NULL); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); context_push_service(context, service, mlt_dummy_transition_type); // Set the properties for (; atts != NULL && *atts != NULL; atts += 2) mlt_properties_set_string(properties, (const char *) atts[0], (const char *) atts[1]); } static void on_end_transition(deserialise_context context, const xmlChar *name) { enum service_type type; mlt_service service = context_pop_service(context, &type); mlt_properties properties = MLT_SERVICE_PROPERTIES(service); enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service(context, &parent_type); if (service != NULL && type == mlt_dummy_transition_type) { char *id = trim(mlt_properties_get(properties, "mlt_service")); mlt_service effect = MLT_SERVICE(mlt_factory_transition(context->profile, id, NULL)); mlt_properties effect_props = MLT_SERVICE_PROPERTIES(effect); if (!effect) { mlt_log_error(NULL, "[producer_xml] failed to load transition \"%s\"\n", id); if (parent) context_push_service(context, parent, parent_type); mlt_service_close(service); free(service); return; } track_service(context->destructors, effect, (mlt_destructor) mlt_transition_close); mlt_properties_set_lcnumeric(MLT_SERVICE_PROPERTIES(effect), context->lc_numeric); // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set_string(properties, "mlt_type", NULL); mlt_properties_set_string(properties, "mlt_service", NULL); // Propagate the properties qualify_property(context, properties, "resource"); qualify_property(context, properties, "luma"); qualify_property(context, properties, "luma.resource"); qualify_property(context, properties, "composite.luma"); qualify_property(context, properties, "producer.resource"); mlt_properties_inherit(effect_props, properties); // Attach all filters from service onto effect attach_filters(effect, service); // Associate the filter with the parent if (parent != NULL) { if (parent_type == mlt_tractor_type) { mlt_field field = mlt_tractor_field(MLT_TRACTOR(parent)); mlt_field_plant_transition(field, MLT_TRANSITION(effect), mlt_properties_get_int(properties, "a_track"), mlt_properties_get_int(properties, "b_track")); mlt_transition_set_in_and_out(MLT_TRANSITION(effect), mlt_properties_get_int(properties, "in"), mlt_properties_get_int(properties, "out")); } else { mlt_log_warning(NULL, "[producer_xml] Misplaced transition - ignoring\n"); } // Put the parent back on the stack context_push_service(context, parent, parent_type); } else { mlt_log_error(NULL, "[producer_xml] transition closed with invalid parent...\n"); } } else { mlt_log_error(NULL, "[producer_xml] Invalid top of stack on transition close\n"); } if (service) { mlt_service_close(service); free(service); } } static void on_start_consumer(deserialise_context context, const xmlChar *name, const xmlChar **atts) { if (context->pass == 1) { mlt_properties properties = mlt_properties_new(); mlt_properties_set_lcnumeric(properties, context->lc_numeric); context_push_service(context, (mlt_service) properties, mlt_dummy_consumer_type); // Set the properties from attributes for (; atts != NULL && *atts != NULL; atts += 2) mlt_properties_set_string(properties, (const char *) atts[0], (const char *) atts[1]); } } static void set_preview_scale(mlt_profile *consumer_profile, mlt_profile *profile, double scale) { *consumer_profile = mlt_profile_clone(*profile); if (*consumer_profile) { (*consumer_profile)->width *= scale; (*consumer_profile)->width -= (*consumer_profile)->width % 2; (*consumer_profile)->height *= scale; (*consumer_profile)->height -= (*consumer_profile)->height % 2; } } static void on_end_consumer(deserialise_context context, const xmlChar *name) { if (context->pass == 1) { // Get the consumer from the stack enum service_type type; mlt_properties properties = (mlt_properties) context_pop_service(context, &type); if (properties && type == mlt_dummy_consumer_type) { qualify_property(context, properties, "resource"); qualify_property(context, properties, "target"); char *resource = mlt_properties_get(properties, "resource"); if (context->multi_consumer > 1 || context->qglsl || mlt_properties_get_int(context->params, "multi")) { // Instantiate the multi consumer if (!context->consumer) { if (context->qglsl) context->consumer = context->qglsl; else context->consumer = mlt_factory_consumer(context->profile, "multi", NULL); if (context->consumer) { // Track this consumer track_service(context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close); mlt_properties_set_lcnumeric(MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric); } } if (context->consumer) { // Set this properties object on multi consumer mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES(context->consumer); char key[20]; snprintf(key, sizeof(key), "%d", context->consumer_count++); mlt_properties_inc_ref(properties); mlt_properties_set_data(consumer_properties, key, properties, 0, (mlt_destructor) mlt_properties_close, NULL); // Pass in / out if provided mlt_properties_pass_list(consumer_properties, properties, "in, out"); // Pass along quality and performance properties to the multi consumer and its render thread(s). mlt_properties_pass_list( consumer_properties, properties, "real_time, deinterlacer, deinterlace_method, rescale, progressive, " "top_field_first, channels, channel_layout"); // We only really know how to optimize real_time for the avformat consumer. const char *service_name = mlt_properties_get(properties, "mlt_service"); if (service_name && !strcmp("avformat", service_name)) mlt_properties_set_int(properties, "real_time", -1); } } else { double scale = mlt_properties_get_double(properties, "scale"); if (scale > 0.0) { set_preview_scale(&context->consumer_profile, &context->profile, scale); } // Instantiate the consumer char *id = trim(mlt_properties_get(properties, "mlt_service")); mlt_profile profile = context->consumer_profile ? context->consumer_profile : context->profile; context->consumer = mlt_factory_consumer(profile, id, resource); if (context->consumer) { // Track this consumer track_service(context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close); mlt_properties_set_lcnumeric(MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric); if (context->consumer_profile) { mlt_properties_set_data(MLT_CONSUMER_PROPERTIES(context->consumer), "_profile", context->consumer_profile, sizeof(*context->consumer_profile), (mlt_destructor) mlt_profile_close, NULL); } // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set_string(properties, "mlt_type", NULL); mlt_properties_set_string(properties, "mlt_service", NULL); // Inherit the properties mlt_properties_inherit(MLT_CONSUMER_PROPERTIES(context->consumer), properties); } } } // Close the dummy if (properties) mlt_properties_close(properties); } } static void on_start_property(deserialise_context context, const xmlChar *name, const xmlChar **atts) { mlt_properties properties = current_properties(context); const char *value = NULL; if (properties != NULL) { // Set the properties for (; atts != NULL && *atts != NULL; atts += 2) { if (xmlStrcmp(atts[0], _x("name")) == 0) context->property = strdup(_s(atts[1])); else if (xmlStrcmp(atts[0], _x("value")) == 0) value = _s(atts[1]); } if (context->property != NULL) mlt_properties_set_string(properties, context->property, value == NULL ? "" : value); // Tell parser to collect any further nodes for serialisation context->is_value = 1; } else { mlt_log_error(NULL, "[producer_xml] Property without a parent '%s'?\n", (const char *) name); } } static void on_end_property(deserialise_context context, const xmlChar *name) { mlt_properties properties = current_properties(context); if (properties != NULL) { // Tell parser to stop building a tree context->is_value = 0; // See if there is a xml tree for the value if (context->property != NULL && context->value_doc != NULL) { xmlChar *value; int size; // Serialise the tree to get value xmlDocDumpMemory(context->value_doc, &value, &size); mlt_properties_set_string(properties, context->property, _s(value)); #ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet(&xmlFree, NULL, NULL, NULL); #endif xmlFree(value); xmlFreeDoc(context->value_doc); context->value_doc = NULL; } // Close this property handling free(context->property); context->property = NULL; } else { mlt_log_error(NULL, "[producer_xml] Property without a parent '%s'??\n", (const char *) name); } } static void on_start_properties(deserialise_context context, const xmlChar *name, const xmlChar **atts) { mlt_properties parent_properties = current_properties(context); if (parent_properties != NULL) { mlt_properties properties = NULL; // Get the name for (; atts != NULL && *atts != NULL; atts += 2) { if (xmlStrcmp(atts[0], _x("name")) == 0) { properties = mlt_properties_new(); mlt_properties_set_properties(parent_properties, _s(atts[1]), properties); mlt_properties_dec_ref(properties); } else { mlt_log_warning(NULL, "[producer_xml] Invalid attribute for properties '%s=%s'\n", (const char *) atts[0], (const char *) atts[1]); } } if (properties) { mlt_deque_push_back(context->stack_properties, properties); } else { mlt_log_error(NULL, "[producer_xml] Properties without a name '%s'?\n", (const char *) name); } } else { mlt_log_error(NULL, "[producer_xml] Properties without a parent '%s'?\n", (const char *) name); } } static void on_end_properties(deserialise_context context, const xmlChar *name) { if (mlt_deque_count(context->stack_properties)) { mlt_deque_pop_back(context->stack_properties); } else { mlt_log_error(NULL, "[producer_xml] Properties end missing properties '%s'.\n", (const char *) name); } } static void on_start_element(void *ctx, const xmlChar *name, const xmlChar **atts) { struct _xmlParserCtxt *xmlcontext = (struct _xmlParserCtxt *) ctx; deserialise_context context = (deserialise_context) (xmlcontext->_private); if (context->pass == 0) { if (xmlStrcmp(name, _x("mlt")) == 0 || xmlStrcmp(name, _x("profile")) == 0 || xmlStrcmp(name, _x("profileinfo")) == 0) on_start_profile(context, name, atts); if (xmlStrcmp(name, _x("consumer")) == 0) context->multi_consumer++; // Check for a service beginning with glsl. or movit. for (; atts != NULL && *atts != NULL; atts += 2) { if (!xmlStrncmp(atts[1], _x("glsl."), 5) || !xmlStrncmp(atts[1], _x("movit."), 6)) { mlt_properties_set_int(context->params, "qglsl", 1); break; } } return; } mlt_deque_push_back_int(context->stack_branch, mlt_deque_pop_back_int(context->stack_branch) + 1); mlt_deque_push_back_int(context->stack_branch, 0); // Build a tree from nodes within a property value if (context->is_value == 1 && context->pass == 1) { xmlNodePtr node = xmlNewNode(NULL, name); if (context->value_doc == NULL) { // Start a new tree context->value_doc = xmlNewDoc(_x("1.0")); xmlDocSetRootElement(context->value_doc, node); } else { // Append child to tree xmlAddChild(mlt_deque_peek_back(context->stack_node), node); } context_push_node(context, node); // Set the attributes for (; atts != NULL && *atts != NULL; atts += 2) xmlSetProp(node, atts[0], atts[1]); } else if (xmlStrcmp(name, _x("tractor")) == 0) on_start_tractor(context, name, atts); else if (xmlStrcmp(name, _x("multitrack")) == 0) on_start_multitrack(context, name, atts); else if (xmlStrcmp(name, _x("playlist")) == 0 || xmlStrcmp(name, _x("seq")) == 0 || xmlStrcmp(name, _x("smil")) == 0) on_start_playlist(context, name, atts); else if (xmlStrcmp(name, _x("producer")) == 0 || xmlStrcmp(name, _x("video")) == 0) on_start_producer(context, name, atts); else if (xmlStrcmp(name, _x("blank")) == 0) on_start_blank(context, name, atts); else if (xmlStrcmp(name, _x("entry")) == 0) on_start_entry(context, name, atts); else if (xmlStrcmp(name, _x("track")) == 0) on_start_track(context, name, atts); else if (xmlStrcmp(name, _x("filter")) == 0) on_start_filter(context, name, atts); else if (xmlStrcmp(name, _x("transition")) == 0) on_start_transition(context, name, atts); else if (xmlStrcmp(name, _x("chain")) == 0) on_start_chain(context, name, atts); else if (xmlStrcmp(name, _x("link")) == 0) on_start_link(context, name, atts); else if (xmlStrcmp(name, _x("property")) == 0) on_start_property(context, name, atts); else if (xmlStrcmp(name, _x("properties")) == 0) on_start_properties(context, name, atts); else if (xmlStrcmp(name, _x("consumer")) == 0) on_start_consumer(context, name, atts); else if (xmlStrcmp(name, _x("westley")) == 0 || xmlStrcmp(name, _x("mlt")) == 0) { for (; atts != NULL && *atts != NULL; atts += 2) { if (xmlStrcmp(atts[0], _x("LC_NUMERIC"))) mlt_properties_set_string(context->producer_map, _s(atts[0]), _s(atts[1])); else if (!context->lc_numeric) context->lc_numeric = strdup(_s(atts[1])); } } } static void on_end_element(void *ctx, const xmlChar *name) { struct _xmlParserCtxt *xmlcontext = (struct _xmlParserCtxt *) ctx; deserialise_context context = (deserialise_context) (xmlcontext->_private); if (context->is_value == 1 && context->pass == 1 && xmlStrcmp(name, _x("property")) != 0) context_pop_node(context); else if (xmlStrcmp(name, _x("multitrack")) == 0) on_end_multitrack(context, name); else if (xmlStrcmp(name, _x("playlist")) == 0 || xmlStrcmp(name, _x("seq")) == 0 || xmlStrcmp(name, _x("smil")) == 0) on_end_playlist(context, name); else if (xmlStrcmp(name, _x("track")) == 0) on_end_track(context, name); else if (xmlStrcmp(name, _x("entry")) == 0) on_end_entry(context, name); else if (xmlStrcmp(name, _x("tractor")) == 0) on_end_tractor(context, name); else if (xmlStrcmp(name, _x("property")) == 0) on_end_property(context, name); else if (xmlStrcmp(name, _x("properties")) == 0) on_end_properties(context, name); else if (xmlStrcmp(name, _x("producer")) == 0 || xmlStrcmp(name, _x("video")) == 0) on_end_producer(context, name); else if (xmlStrcmp(name, _x("filter")) == 0) on_end_filter(context, name); else if (xmlStrcmp(name, _x("transition")) == 0) on_end_transition(context, name); else if (xmlStrcmp(name, _x("chain")) == 0) on_end_chain(context, name); else if (xmlStrcmp(name, _x("link")) == 0) on_end_link(context, name); else if (xmlStrcmp(name, _x("consumer")) == 0) on_end_consumer(context, name); mlt_deque_pop_back_int(context->stack_branch); } static void on_characters(void *ctx, const xmlChar *ch, int len) { struct _xmlParserCtxt *xmlcontext = (struct _xmlParserCtxt *) ctx; deserialise_context context = (deserialise_context) (xmlcontext->_private); char *value = calloc(1, len + 1); mlt_properties properties = current_properties(context); value[len] = 0; strncpy(value, (const char *) ch, len); if (mlt_deque_count(context->stack_node)) xmlNodeAddContent(mlt_deque_peek_back(context->stack_node), (xmlChar *) value); // libxml2 generates an on_characters immediately after a get_entity within // an element value, and we ignore it because it is called again during // actual substitution. else if (context->property != NULL && context->entity_is_replace == 0) { char *s = mlt_properties_get(properties, context->property); if (s != NULL) { // Append new text to existing content char *new = calloc(1, strlen(s) + len + 1); strcat(new, s); strcat(new, value); mlt_properties_set_string(properties, context->property, new); free(new); } else mlt_properties_set_string(properties, context->property, value); } context->entity_is_replace = 0; // Check for a service beginning with glsl. or movit. if (!strncmp(value, "glsl.", 5) || !strncmp(value, "movit.", 6)) mlt_properties_set_int(context->params, "qglsl", 1); free(value); } /** Convert parameters parsed from resource into entity declarations. */ static void params_to_entities(deserialise_context context) { if (context->params != NULL) { int i; // Add our params as entity declarations for (i = 0; i < mlt_properties_count(context->params); i++) { xmlChar *name = (xmlChar *) mlt_properties_get_name(context->params, i); xmlAddDocEntity(context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY, context->publicId, context->systemId, (xmlChar *) mlt_properties_get(context->params, _s(name))); } // Flag completion mlt_properties_close(context->params); context->params = NULL; } } // The following 3 facilitate entity substitution in the SAX parser static void on_internal_subset(void *ctx, const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId) { struct _xmlParserCtxt *xmlcontext = (struct _xmlParserCtxt *) ctx; deserialise_context context = (deserialise_context) (xmlcontext->_private); context->publicId = publicId; context->systemId = systemId; xmlCreateIntSubset(context->entity_doc, name, publicId, systemId); // Override default entities with our parameters params_to_entities(context); } // TODO: Check this with Dan... I think this is for parameterisation // but it's breaking standard escaped entities (like < etc). static void on_entity_declaration(void *ctx, const xmlChar *name, int type, const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) { struct _xmlParserCtxt *xmlcontext = (struct _xmlParserCtxt *) ctx; deserialise_context context = (deserialise_context) (xmlcontext->_private); xmlAddDocEntity(context->entity_doc, name, type, publicId, systemId, content); } // TODO: Check this functionality (see on_entity_declaration) static xmlEntityPtr on_get_entity(void *ctx, const xmlChar *name) { struct _xmlParserCtxt *xmlcontext = (struct _xmlParserCtxt *) ctx; deserialise_context context = (deserialise_context) (xmlcontext->_private); xmlEntityPtr e = NULL; // Setup for entity declarations if not ready if (xmlGetIntSubset(context->entity_doc) == NULL) { xmlCreateIntSubset(context->entity_doc, _x("mlt"), _x(""), _x("")); context->publicId = _x(""); context->systemId = _x(""); } // Add our parameters if not already params_to_entities(context); e = xmlGetPredefinedEntity(name); // Send signal to on_characters that an entity substitution is pending if (e == NULL) { e = xmlGetDocEntity(context->entity_doc, name); if (e != NULL) context->entity_is_replace = 1; } return e; } static void on_error(void *ctx, const char *msg, ...) { const struct _xmlError *err_ptr = xmlCtxtGetLastError(ctx); switch (err_ptr->level) { case XML_ERR_WARNING: mlt_log_warning(NULL, "[producer_xml] parse warning: %s\trow: %d\tcol: %d\n", err_ptr->message, err_ptr->line, err_ptr->int2); break; case XML_ERR_ERROR: mlt_log_error(NULL, "[producer_xml] parse error: %s\trow: %d\tcol: %d\n", err_ptr->message, err_ptr->line, err_ptr->int2); break; default: case XML_ERR_FATAL: mlt_log_fatal(NULL, "[producer_xml] parse fatal: %s\trow: %d\tcol: %d\n", err_ptr->message, err_ptr->line, err_ptr->int2); break; } } /** Convert a hexadecimal character to its value. */ static int tohex(char p) { return isdigit(p) ? p - '0' : tolower(p) - 'a' + 10; } /** Decode a url-encoded string containing hexadecimal character sequences. */ static char *url_decode(char *dest, char *src) { char *p = dest; while (*src) { if (*src == '%') { *p++ = (tohex(*(src + 1)) << 4) | tohex(*(src + 2)); src += 3; } else { *p++ = *src++; } } *p = *src; return dest; } /** Extract the filename from a URL attaching parameters to a properties list. */ static void parse_url(mlt_properties properties, char *url) { int i; int n = strlen(url); char *name = NULL; char *value = NULL; int is_query = 0; for (i = 0; i < n; i++) { switch (url[i]) { case '?': url[i++] = '\0'; name = &url[i]; is_query = 1; break; case ':': #ifdef _WIN32 if (url[i + 1] != '/' && url[i + 1] != '\\') #endif case '=': if (is_query) { url[i++] = '\0'; value = &url[i]; } break; case '&': if (is_query) { url[i++] = '\0'; if (name != NULL && value != NULL) mlt_properties_set_string(properties, name, value); name = &url[i]; value = NULL; } break; } } if (name != NULL && value != NULL) mlt_properties_set_string(properties, name, value); } // Quick workaround to avoid unnecessary libxml2 warnings static int file_exists(char *name) { int exists = 0; if (name != NULL) { FILE *f = mlt_fopen(name, "r"); exists = f != NULL; if (exists) fclose(f); } return exists; } // This function will add remaining services in the context service stack marked // with a "xml_retain" property to a property named "xml_retain" on the returned // service. The property is a mlt_properties data property. static void retain_services(struct deserialise_context_s *context, mlt_service service) { mlt_properties retain_list = mlt_properties_new(); enum service_type type; mlt_service retain_service = context_pop_service(context, &type); while (retain_service) { mlt_properties retain_properties = MLT_SERVICE_PROPERTIES(retain_service); if (mlt_properties_get_int(retain_properties, "xml_retain")) { // Remove the retained service from the destructors list. int i; for (i = mlt_properties_count(context->destructors) - 1; i >= 1; i--) { const char *name = mlt_properties_get_name(context->destructors, i); if (mlt_properties_get_data_at(context->destructors, i, NULL) == retain_service) { mlt_properties_set_data(context->destructors, name, retain_service, 0, NULL, NULL); break; } } const char *name = mlt_properties_get(retain_properties, "id"); if (name) mlt_properties_set_data(retain_list, name, retain_service, 0, (mlt_destructor) mlt_service_close, NULL); } retain_service = context_pop_service(context, &type); } if (mlt_properties_count(retain_list) > 0) { mlt_properties_set_data(MLT_SERVICE_PROPERTIES(service), "xml_retain", retain_list, 0, (mlt_destructor) mlt_properties_close, NULL); } else { mlt_properties_close(retain_list); } } static deserialise_context context_new(mlt_profile profile) { deserialise_context context = calloc(1, sizeof(struct deserialise_context_s)); if (context) { context->producer_map = mlt_properties_new(); context->destructors = mlt_properties_new(); context->params = mlt_properties_new(); context->profile = profile; context->seekable = 1; context->stack_service = mlt_deque_init(); context->stack_types = mlt_deque_init(); context->stack_properties = mlt_deque_init(); context->stack_node = mlt_deque_init(); context->stack_branch = mlt_deque_init(); mlt_deque_push_back_int(context->stack_branch, 0); } return context; } static void context_close(deserialise_context context) { mlt_properties_close(context->producer_map); mlt_properties_close(context->destructors); mlt_properties_close(context->params); mlt_deque_close(context->stack_service); mlt_deque_close(context->stack_types); mlt_deque_close(context->stack_properties); mlt_deque_close(context->stack_node); mlt_deque_close(context->stack_branch); xmlFreeDoc(context->entity_doc); free(context->lc_numeric); free(context); } mlt_producer producer_xml_init(mlt_profile profile, mlt_service_type servtype, const char *id, char *data) { xmlSAXHandler *sax, *sax_orig; deserialise_context context; mlt_properties properties = NULL; int i = 0; struct _xmlParserCtxt *xmlcontext; int well_formed = 0; char *filename = NULL; int is_filename = strcmp(id, "xml-string"); // Strip file:// prefix if (data && strlen(data) >= 7 && strncmp(data, "file://", 7) == 0) data += 7; if (data == NULL || !strcmp(data, "")) return NULL; context = context_new(profile); if (context == NULL) return NULL; // Decode URL and parse parameters mlt_properties_set_string(context->producer_map, "root", ""); if (is_filename) { mlt_properties_set_string(context->params, "_mlt_xml_resource", data); filename = mlt_properties_get(context->params, "_mlt_xml_resource"); parse_url(context->params, url_decode(filename, data)); // We need the directory prefix which was used for the xml if (strchr(filename, '/') || strchr(filename, '\\')) { char *root = NULL; mlt_properties_set_string(context->producer_map, "root", filename); root = mlt_properties_get(context->producer_map, "root"); if (strchr(root, '/')) *(strrchr(root, '/')) = '\0'; else if (strchr(root, '\\')) *(strrchr(root, '\\')) = '\0'; // If we don't have an absolute path here, we're heading for disaster... if (root[0] != '/' && !strchr(root, ':')) { char *cwd = getcwd(NULL, 0); char *real = malloc(strlen(cwd) + strlen(root) + 2); sprintf(real, "%s/%s", cwd, root); mlt_properties_set_string(context->producer_map, "root", real); free(real); free(cwd); } } if (!file_exists(filename)) { // Try the un-converted text encoding as a fallback. // Fixes launching melt as child process from Shotcut on Windows // when there are extended characters in the path. filename = mlt_properties_get(context->params, "_mlt_xml_resource"); } if (!file_exists(filename)) { context_close(context); return NULL; } } // We need to track the number of registered filters mlt_properties_set_int(context->destructors, "registered", 0); // Setup SAX callbacks for first pass sax = calloc(1, sizeof(xmlSAXHandler)); sax->startElement = on_start_element; sax->characters = on_characters; sax->warning = on_error; sax->error = on_error; sax->fatalError = on_error; // Setup libxml2 SAX parsing xmlInitParser(); // Do not remove the below deprecated function until libxml2 version 2.9.13 // is at least 6 years old because in that version XML_PARSE_NOENT does not work. xmlSubstituteEntitiesDefault(1); // This is used to facilitate entity substitution in the SAX parser context->entity_doc = xmlNewDoc(_x("1.0")); if (is_filename) xmlcontext = xmlCreateFileParserCtxt(filename); else xmlcontext = xmlCreateMemoryParserCtxt(data, strlen(data)); // Invalid context - clean up and return NULL if (xmlcontext == NULL) { context_close(context); free(sax); return NULL; } xmlCtxtUseOptions(xmlcontext, XML_PARSE_NOENT); // Parse sax_orig = xmlcontext->sax; xmlcontext->sax = sax; xmlcontext->_private = (void *) context; xmlParseDocument(xmlcontext); well_formed = xmlcontext->wellFormed; // Cleanup after parsing xmlcontext->sax = sax_orig; xmlcontext->_private = NULL; if (xmlcontext->myDoc) xmlFreeDoc(xmlcontext->myDoc); xmlFreeParserCtxt(xmlcontext); // Bad xml - clean up and return NULL if (!well_formed) { context_close(context); free(sax); return NULL; } // Setup the second pass context->pass++; if (is_filename) xmlcontext = xmlCreateFileParserCtxt(filename); else xmlcontext = xmlCreateMemoryParserCtxt(data, strlen(data)); // Invalid context - clean up and return NULL if (xmlcontext == NULL) { context_close(context); free(sax); return NULL; } // Reset the stack. mlt_deque_close(context->stack_service); mlt_deque_close(context->stack_types); mlt_deque_close(context->stack_properties); mlt_deque_close(context->stack_node); context->stack_service = mlt_deque_init(); context->stack_types = mlt_deque_init(); context->stack_properties = mlt_deque_init(); context->stack_node = mlt_deque_init(); // Create the qglsl consumer now, if requested, so that glsl.manager // may exist when trying to load glsl. or movit. services. // The "if requested" part can come from query string qglsl=1 or when // a service beginning with glsl. or movit. appears in the XML. if (mlt_properties_get_int(context->params, "qglsl") && strcmp(id, "xml-nogl") // Only if glslManager does not yet exist. && !mlt_properties_get_data(mlt_global_properties(), "glslManager", NULL)) context->qglsl = mlt_factory_consumer(profile, "qglsl", NULL); // Setup SAX callbacks for second pass sax->endElement = on_end_element; sax->cdataBlock = on_characters; sax->internalSubset = on_internal_subset; sax->entityDecl = on_entity_declaration; sax->getEntity = on_get_entity; // Parse sax_orig = xmlcontext->sax; xmlcontext->sax = sax; xmlcontext->_private = (void *) context; xmlParseDocument(xmlcontext); well_formed = xmlcontext->wellFormed && mlt_profile_is_valid(profile); // Cleanup after parsing xmlFreeDoc(context->entity_doc); context->entity_doc = NULL; free(sax); xmlMemoryDump(); // for debugging xmlcontext->sax = sax_orig; xmlcontext->_private = NULL; if (xmlcontext->myDoc) xmlFreeDoc(xmlcontext->myDoc); xmlFreeParserCtxt(xmlcontext); // Get the last producer on the stack enum service_type type; mlt_service service = context_pop_service(context, &type); if (well_formed && service != NULL) { // Verify it is a producer service (mlt_type="mlt_producer") // (producer, chain, playlist, multitrack) char *type = mlt_properties_get(MLT_SERVICE_PROPERTIES(service), "mlt_type"); if (type == NULL || (strcmp(type, "mlt_producer") != 0 && strcmp(type, "producer") != 0 && strcmp(type, "chain") != 0)) service = NULL; } #ifdef DEBUG xmlDocPtr doc = xml_make_doc(service); xmlDocFormatDump(stdout, doc, 1); xmlFreeDoc(doc); service = NULL; #endif if (well_formed && service != NULL) { char *title = mlt_properties_get(context->producer_map, "title"); // Need the complete producer list for various reasons properties = context->destructors; // Now make sure we don't have a reference to the service in the properties for (i = mlt_properties_count(properties) - 1; i >= 1; i--) { char *name = mlt_properties_get_name(properties, i); if (mlt_properties_get_data_at(properties, i, NULL) == service) { mlt_properties_set_data(properties, name, service, 0, NULL, NULL); break; } } // We are done referencing destructor property list // Set this var to service properties for convenience properties = MLT_SERVICE_PROPERTIES(service); // Assign the title mlt_properties_set_string(properties, "title", title); // Optimise for overlapping producers mlt_producer_optimise(MLT_PRODUCER(service)); // Handle deep copies if (getenv("MLT_XML_DEEP") == NULL) { // Now assign additional properties if (is_filename && (mlt_service_identify(service) == mlt_service_tractor_type || mlt_service_identify(service) == mlt_service_playlist_type || mlt_service_identify(service) == mlt_service_multitrack_type)) { mlt_properties_set_int(properties, "_original_type", mlt_service_identify(service)); mlt_properties_set_string(properties, "_original_resource", mlt_properties_get(properties, "resource")); mlt_properties_set_string(properties, "resource", data); } // This tells consumer_xml not to deep copy mlt_properties_set_string(properties, "xml", "was here"); } else { // Allow the project to be edited mlt_properties_set_string(properties, "_xml", "was here"); mlt_properties_set_int(properties, "_mlt_service_hidden", 1); } // Make consumer available mlt_properties_inc_ref(MLT_CONSUMER_PROPERTIES(context->consumer)); mlt_properties_set_data(properties, "consumer", context->consumer, 0, (mlt_destructor) mlt_consumer_close, NULL); mlt_properties_set_int(properties, "seekable", context->seekable); retain_services(context, service); } else { // Return null if not well formed service = NULL; } // Clean up if (context->qglsl && context->consumer != context->qglsl) mlt_consumer_close(context->qglsl); context_close(context); return MLT_PRODUCER(service); } mlt-7.38.0/src/modules/xml/producer_xml.yml000664 000000 000000 00000002074 15172202314 020652 0ustar00rootroot000000 000000 schema_version: 7.0 type: producer identifier: xml title: XML File version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: | Construct a service network from an XML description. See docs/mlt-xml.txt. notes: > If there is a service with a property "xml_retain=1" that is not the producer, and if it also has an "id" property; then the extra service is put into a properties list keyed on the id property. Then, that properties list is placed as a property on the returned service with the name "xml_retain". This lets an application retrieve additional deserialized services that are not the lastmost producer or anywhere in its graph. bugs: - > This producer is not thread-safe during its construction because it may modify the mlt_profile, even if is_explicit is set. parameters: - identifier: resource argument: yes title: File type: string description: An XML text file containing MLT XML. readonly: no required: yes mutable: no widget: fileopen mlt-7.38.0/src/msvc/000775 000000 000000 00000000000 15172202314 014121 5ustar00rootroot000000 000000 mlt-7.38.0/src/msvc/CMakeLists.txt000664 000000 000000 00000000207 15172202314 016660 0ustar00rootroot000000 000000 add_library(msvccompat STATIC libgen.c gettimeofday.c) target_include_directories(msvccompat PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) mlt-7.38.0/src/msvc/gettimeofday.c000664 000000 000000 00000004155 15172202314 016753 0ustar00rootroot000000 000000 /* * Copied from vcpkg: * https://github.com/microsoft/vcpkg/blob/master/ports/gettimeofday/gettimeofday.c * * Originally copied from PostgreSQL source: * http://doxygen.postgresql.org/gettimeofday_8c_source.html * */ /* * gettimeofday.c * Win32 gettimeofday() replacement * * src/port/gettimeofday.c * * Copyright (c) 2003 SRA, Inc. * Copyright (c) 2003 SKC, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose, without fee, and without a * written agreement is hereby granted, provided that the above * copyright notice and this paragraph and the following two * paragraphs appear in all copies. * * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ #ifdef _MSC_VER #include /* FILETIME of Jan 1 1970 00:00:00. */ static const unsigned __int64 epoch = 116444736000000000Ui64; /* * timezone information is stored outside the kernel so tzp isn't used anymore. * * Note: this function is not for Win32 high precision timing purpose. See * elapsed_time(). */ int gettimeofday(struct timeval *tp, struct timezone *tzp) { FILETIME file_time; SYSTEMTIME system_time; ULARGE_INTEGER ularge; GetSystemTime(&system_time); SystemTimeToFileTime(&system_time, &file_time); ularge.LowPart = file_time.dwLowDateTime; ularge.HighPart = file_time.dwHighDateTime; tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L); tp->tv_usec = (long) (system_time.wMilliseconds * 1000); return 0; } #endif /* _MSC_VER */ mlt-7.38.0/src/msvc/gettimeofday.h000664 000000 000000 00000000535 15172202314 016756 0ustar00rootroot000000 000000 /* * Copied from vcpkg: * https://github.com/microsoft/vcpkg/blob/master/ports/gettimeofday/gettimeofday.h * */ #ifndef _MY_GETTIMEOFDAY_H_ #define _MY_GETTIMEOFDAY_H_ #ifdef _MSC_VER #include #include int gettimeofday(struct timeval *tp, struct timezone *tzp); #endif /* _MSC_VER */ #endif /* _MY_GETTIMEOFDAY_H_ */ mlt-7.38.0/src/msvc/libgen.c000664 000000 000000 00000021355 15172202314 015533 0ustar00rootroot000000 000000 /** * This file has no copyright assigned and is placed in the Public Domain. * This file has been copied from the mingw-w64 runtime package. */ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include "libgen.h" #include /* A 'directory separator' is a byte that equals 0x2F ('solidus' or more * commonly 'forward slash') or 0x5C ('reverse solidus' or more commonly * 'backward slash'). The byte 0x5C may look different from a backward slash * in some locales; for example, it looks the same as a Yen sign in Japanese * locales and a Won sign in Korean locales. Despite its appearance, it still * functions as a directory separator. * * A 'path' comprises an optional DOS drive letter with a colon, and then an * arbitrary number of possibily empty components, separated by non-empty * sequences of directory separators (in other words, consecutive directory * separators are treated as a single one). A path that comprises an empty * component denotes the current working directory. * * An 'absolute path' comprises at least two components, the first of which * is empty. * * A 'relative path' is a path that is not an absolute path. In other words, * it either comprises an empty component, or begins with a non-empty * component. * * POSIX doesn't have a concept about DOS drives. A path that does not have a * drive letter starts from the same drive as the current working directory. * * For example: * (Examples without drive letters match POSIX.) * * Argument dirname() returns basename() returns * -------- ----------------- ------------------ * `` or NULL `.` `.` * `usr` `.` `usr` * `usr\` `.` `usr` * `\` `\` `\` * `\usr` `\` `usr` * `\usr\lib` `\usr` `lib` * `\home\\dwc\\test` `\home\\dwc` `test` * `\\host\usr` `\\host\.` `usr` * `\\host\usr\lib` `\\host\usr` `lib` * `\\host\\usr` `\\host\\` `usr` * `\\host\\usr\lib` `\\host\\usr` `lib` * `C:` `C:.` `.` * `C:usr` `C:.` `usr` * `C:usr\` `C:.` `usr` * `C:\` `C:\` `\` * `C:\\` `C:\` `\` * `C:\\\` `C:\` `\` * `C:\usr` `C:\` `usr` * `C:\usr\lib` `C:\usr` `lib` * `C:\\usr\\lib\\` `C:\\usr` `lib` * `C:\home\\dwc\\test` `C:\home\\dwc` `test` */ static void do_get_path_info(struct path_info* info, char* path) { char* pos = path; int unc_ncoms = 0; DWORD cp; int dbcs_tb, prev_dir_sep, dir_sep; /* Get the code page for paths in the same way as `fopen()`. */ cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP; /* Set the structure to 'no data'. */ info->prefix_end = NULL; info->base_sep_begin = NULL; // info->base_sep_end = NULL; info->term_sep_begin = NULL; if(IS_DIR_SEP(pos[0]) && IS_DIR_SEP(pos[1])) { /* The path is UNC. */ pos += 2; /* Seek to the end of the share/device name. */ dbcs_tb = 0; prev_dir_sep = 0; while(*pos != 0) { dir_sep = 0; if(dbcs_tb) dbcs_tb = 0; else if(IsDBCSLeadByteEx(cp, *pos)) dbcs_tb = 1; else dir_sep = IS_DIR_SEP(*pos); /* If a separator has been encountered and the previous character * was not, mark this as the end of the current component. */ if(dir_sep && !prev_dir_sep) { unc_ncoms ++; /* The first component is the host name, and the second is the * share name. So we stop at the end of the second component. */ if(unc_ncoms == 2) break; } prev_dir_sep = dir_sep; pos ++; } /* The UNC prefix terminates here. The terminating directory separator * is not part of the prefix, and initiates a new absolute path. */ info->prefix_end = pos; } else if((pos[0] >= 'A' && pos[0] <= 'Z' && pos[1] == ':') || (pos[0] >= 'a' && pos[0] <= 'z' && pos[1] == ':')) { /* The path contains a DOS drive letter in the beginning. */ pos += 2; /* The DOS drive prefix terminates here. Unlike UNC paths, the remaing * part can be relative. For example, `C:foo` denotes `foo` in the * working directory of drive `C:`. */ info->prefix_end = pos; } /* The remaining part of the path is almost the same as POSIX. */ dbcs_tb = 0; prev_dir_sep = 0; while(*pos != 0) { dir_sep = 0; if(dbcs_tb) dbcs_tb = 0; else if(IsDBCSLeadByteEx(cp, *pos)) dbcs_tb = 1; else dir_sep = IS_DIR_SEP(*pos); /* If a separator has been encountered and the previous character * was not, mark this as the beginning of the terminating separator * sequence. */ if(dir_sep && !prev_dir_sep) info->term_sep_begin = pos; /* If a non-separator character has been encountered and a previous * terminating separator sequence exists, start a new component. */ if(!dir_sep && prev_dir_sep) { info->base_sep_begin = info->term_sep_begin; info->base_sep_end = pos; info->term_sep_begin = NULL; } prev_dir_sep = dir_sep; pos ++; } /* Store the end of the path for convenience. */ info->path_end = pos; } char* dirname(char* path) { struct path_info info; char* upath; const char* top; static char* static_path_copy; if(path == NULL || path[0] == 0) return (char*) "."; do_get_path_info(&info, path); upath = info.prefix_end ? info.prefix_end : path; top = (IS_DIR_SEP(path[0]) || IS_DIR_SEP(upath[0])) ? "\\" : "."; /* If a non-terminating directory separator exists, it terminates the * dirname. Truncate the path there. */ if(info.base_sep_begin) { info.base_sep_begin[0] = 0; /* If the unprefixed path has not been truncated to empty, it is now * the dirname, so return it. */ if(upath[0]) return path; } /* The dirname is empty. In principle we return `.` if the * path is relative and `\` if it is absolute. This can be * optimized if there is no prefix. */ if(upath == path) return (char*) top; /* When there is a prefix, we must append a character to the prefix. * If there is enough room in the original path, we just reuse its * storage. */ if(upath != info.path_end) { upath[0] = *top; upath[1] = 0; return path; } /* This is only the last resort. If there is no room, we have to copy * the prefix elsewhere. */ upath = realloc(static_path_copy, info.prefix_end - path + 2); if(!upath) return (char*) top; static_path_copy = upath; memcpy(upath, path, info.prefix_end - path); upath += info.prefix_end - path; upath[0] = *top; upath[1] = 0; return static_path_copy; } char* basename(char* path) { struct path_info info; char* upath; if(path == NULL || path[0] == 0) return (char*) "."; do_get_path_info(&info, path); upath = info.prefix_end ? info.prefix_end : path; /* If the path is non-UNC and empty, then it's relative. POSIX says '.' * shall be returned. */ if(IS_DIR_SEP(path[0]) == 0 && upath[0] == 0) return (char*) "."; /* If a terminating separator sequence exists, it is not part of the * name and shall be truncated. */ if(info.term_sep_begin) info.term_sep_begin[0] = 0; /* If some other separator sequence has been found, the basename * immediately follows it. */ if(info.base_sep_end) return info.base_sep_end; /* If removal of the terminating separator sequence has caused the * unprefixed path to become empty, it must have comprised only * separators. POSIX says `/` shall be returned, but on Windows, we * return `\` instead. */ if(upath[0] == 0) return (char*) "\\"; /* Return the unprefixed path. */ return upath; } mlt-7.38.0/src/msvc/libgen.h000664 000000 000000 00000001563 15172202314 015537 0ustar00rootroot000000 000000 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include /* Structure to hold path information */ struct path_info { /* This points to end of the UNC prefix and drive letter, if any. */ char* prefix_end; /* These point to the directory separator in front of the last non-empty component. */ char* base_sep_begin; char* base_sep_end; /* This points to the last directory separator sequence if no other non-separator characters follow it. */ char* term_sep_begin; /* This points to the end of the string. */ char* path_end; }; /* Macro to check if a character is a directory separator */ #define IS_DIR_SEP(c) ((c) == '/' || (c) == '\\') /* Function to extract directory name from a path */ char* dirname(char* path); /* Function to extract base name from a path */ char* basename(char* path); mlt-7.38.0/src/swig/000775 000000 000000 00000000000 15172202314 014122 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/CMakeLists.txt000664 000000 000000 00000001141 15172202314 016657 0ustar00rootroot000000 000000 include(UseSWIG) if(SWIG_CSHARP AND CSHARP_MONO_FOUND) add_subdirectory(csharp) endif() if(SWIG_JAVA AND JNI_FOUND) add_subdirectory(java) endif() if(SWIG_LUA AND LUA_FOUND) add_subdirectory(lua) endif() if(SWIG_NODEJS AND NODEJS_INCLUDE_DIRS) add_subdirectory(nodejs) endif() if(SWIG_PERL AND PERLLIBS_FOUND) add_subdirectory(perl) endif() if(SWIG_PHP AND PHP_FOUND) add_subdirectory(php) endif() if(SWIG_PYTHON AND Python3_FOUND) add_subdirectory(python) endif() if(SWIG_RUBY AND RUBY_FOUND) add_subdirectory(ruby) endif() if(SWIG_TCL AND TCL_FOUND) add_subdirectory(tcl) endif() mlt-7.38.0/src/swig/csharp/000775 000000 000000 00000000000 15172202314 015402 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/csharp/CMakeLists.txt000664 000000 000000 00000001066 15172202314 020145 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mltsharp LANGUAGE csharp OUTPUT_DIR src_swig SOURCES ../mlt.i) target_compile_options(mltsharp PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltsharp PRIVATE mlt mlt++) set_target_properties(mltsharp PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/csharp") add_custom_command(TARGET mltsharp POST_BUILD COMMAND mcs -out:mlt-sharp.dll -target:library src_swig/*.cs WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) mlt-7.38.0/src/swig/csharp/build000775 000000 000000 00000001665 15172202314 016437 0ustar00rootroot000000 000000 #!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -rf *.cxx *.snk *.so *.o *.exe *.dll mlt.i ../.cs src_swig ) exit 0 fi path=`which mcs 2> /dev/null` if [ $? = 0 ] then ln -sf ../mlt.i # Invoke swig mkdir src_swig swig -c++ -I../../mlt++ -I../.. -csharp -dllimport libmltsharp -outdir src_swig -namespace Mlt mlt.i || exit $? # Compile the wrapper ${CXX} -fPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. mlt_wrap.cxx || exit $? # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o libmltsharp.so || exit $? # Compile the library assembly mcs -out:mlt-sharp.dll -target:library src_swig/*.cs # uncomment the below if you want to sign the assembly # sn -k mlt-sharp.snk # mcs -out:mlt-sharp.dll -target:library -keyfile:mlt-sharp.snk src_swig/*.cs # Compile the example mcs -r:mlt-sharp.dll play.cs else echo Mono C# compiler not installed. exit 1 fi mlt-7.38.0/src/swig/csharp/play.cs000664 000000 000000 00000000666 15172202314 016706 0ustar00rootroot000000 000000 using System; using System.Threading; using Mlt; public class Play { public static void Main(String[] args) { Console.WriteLine("Welcome to MLT."); Factory.init(); Profile profile = new Profile(""); Producer p = new Producer(profile, args[0], null); if (p.is_valid()) { Consumer c = new Consumer(profile, "sdl2", null); c.connect(p); c.start(); while (!c.is_stopped()) Thread.Sleep(300); c.stop(); } } } mlt-7.38.0/src/swig/csharp/play.sh000775 000000 000000 00000000035 15172202314 016704 0ustar00rootroot000000 000000 #!/bin/sh mono play.exe "$@" mlt-7.38.0/src/swig/java/000775 000000 000000 00000000000 15172202314 015043 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/java/CMakeLists.txt000664 000000 000000 00000001213 15172202314 017600 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mltjava LANGUAGE java OUTPUT_DIR src_swig SOURCES ../mlt.i) target_compile_options(mltjava PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltjava PRIVATE mlt mlt++) target_include_directories(mltjava PRIVATE ${JNI_INCLUDE_DIRS}) set_target_properties(mltjava PROPERTIES OUTPUT_NAME "mlt_java") set_target_properties(mltjava PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/java") add_custom_command(TARGET mltjava POST_BUILD COMMAND javac src_swig/*.java WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) mlt-7.38.0/src/swig/java/Play.java000664 000000 000000 00000001633 15172202314 016616 0ustar00rootroot000000 000000 import org.mltframework.*; public class Play { static { System.loadLibrary("mlt_java"); } public static void main (String[] args) { // Start the mlt system Factory.init( null ); // Set the output profile Profile profile = new Profile( "" ); // Create the producer Producer p = new Producer( profile, args[0], null ); if ( p.is_valid() ) { p.set ("eof", "loop"); // Create the consumer Consumer c = new Consumer( profile, "sdl2", null); // Connect the producer to the consumer c.connect(p); // Start the consumer c.start(); // Wait until the user stops the consumer Object o = new Object(); while ( !c.is_stopped() ) { synchronized (o) { try { o.wait(1000); } catch (InterruptedException e) { // ignored } } } // Stop it anyway c.stop(); } else { System.out.println ("Unable to open " + args[0]); } } } mlt-7.38.0/src/swig/java/Play.sh000775 000000 000000 00000000076 15172202314 016312 0ustar00rootroot000000 000000 #!/bin/sh java -Djava.library.path=. -cp .:src_swig Play "$@" mlt-7.38.0/src/swig/java/build000775 000000 000000 00000001701 15172202314 016067 0ustar00rootroot000000 000000 #!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -rf *.cxx *.so *.o mlt.i ../.java *.class src_swig ) exit 0 fi path=`which java 2> /dev/null` if [ $? = 0 ] then # Locate the path for the include path=`dirname $path` path=`dirname $path` # Change this as needed # export JAVA_INCLUDE="-I$path/include -I$path/include/linux" ln -sf ../mlt.i # Invoke swig mkdir -p src_swig/org/mltframework swig -c++ -I../../mlt++ -I../.. -java -outdir src_swig/org/mltframework -package org.mltframework mlt.i || exit $? # Compile the wrapper ${CXX} -fPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. mlt_wrap.cxx $JAVA_INCLUDE || exit $? # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o libmlt_java.so || exit $? # Compile the test javac `find src_swig -name '*.java'` || exit $? export CLASSPATH=`pwd`/src_swig javac Play.java else echo "Java command not found" exit 1 fi mlt-7.38.0/src/swig/lua/000775 000000 000000 00000000000 15172202314 014703 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/lua/CMakeLists.txt000664 000000 000000 00000000751 15172202314 017446 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mltlua LANGUAGE lua SOURCES ../mlt.i) target_compile_options(mltlua PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltlua PRIVATE mlt mlt++) target_include_directories(mltlua PRIVATE ${LUA_INCLUDE_DIR}) set_target_properties(mltlua PROPERTIES OUTPUT_NAME "mlt") set_target_properties(mltlua PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lua") mlt-7.38.0/src/swig/lua/build000775 000000 000000 00000001023 15172202314 015724 0ustar00rootroot000000 000000 #!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.lua ) exit 0 fi path=`which lua 2> /dev/null` if [ $? = 0 ] then ln -sf ../mlt.i # Invoke swig swig -c++ -I../../mlt++ -I../.. -lua mlt.i || exit $? # Compile the wrapper ${CXX} -fPIC -DPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. mlt_wrap.cxx || exit $? # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o mlt.so || exit $? else echo Lua not installed. exit 1 fi mlt-7.38.0/src/swig/lua/play.lua000775 000000 000000 00000000666 15172202314 016366 0ustar00rootroot000000 000000 #!/usr/bin/env lua require("mlt") mlt.Factory_init() profile = mlt.Profile() producer = mlt.Producer( profile, arg[1] ) if producer:is_valid() then consumer = mlt.Consumer( profile, "sdl2" ) consumer:set( "terminate_on_pause", 1 ) consumer:connect( producer ) event = consumer:setup_wait_for( "consumer-stopped" ) consumer:start() consumer:wait_for( event ) else print( "Unable to open "..arg[1] ) end mlt.Factory_close() mlt-7.38.0/src/swig/mlt.i000664 000000 000000 00000016620 15172202314 015075 0ustar00rootroot000000 000000 /** * mlt.i - Swig Bindings for mlt++ * Copyright (C) 2004-2026 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // SWIG is a parser, not a full C++ compiler, and it fails on compiler-specific // keywords like __declspec(dllexport). These %define directives make all of // our module-specific export macros invisible *only* to SWIG's parser, // resolving syntax errors during wrapper generation. %module mlt %define MLT_EXPORT %enddef %define MLTAVFORMAT_EXPORT %enddef %define MLTCORE_EXPORT %enddef %define MLTDECKLINK_EXPORT %enddef %define MLTFREI0R_EXPORT %enddef %define MLTGDK_EXPORT %enddef %define MLTGLAXNIMATE_EXPORT %enddef %define MLTGLAXNIMATE_QT6_EXPORT %enddef %define MLTJACKRACK_EXPORT %enddef %define MLTKDENLIVE_EXPORT %enddef %define MLTMOVIT_EXPORT %enddef %define MLTNDI_EXPORT %enddef %define MLTNORMALIZE_EXPORT %enddef %define MLTOLDFILM_EXPORT %enddef %define MLTOPENCV_EXPORT %enddef %define MLTOPENFX_EXPORT %enddef %define MLTPLUS_EXPORT %enddef %define MLTPLUSGPL_EXPORT %enddef %define MLTQT_EXPORT %enddef %define MLTQT6_EXPORT %enddef %define MLTRESAMPLE_EXPORT %enddef %define MLTRTAUDIO_EXPORT %enddef %define MLTRUBBERBAND_EXPORT %enddef %define MLTSDL_EXPORT %enddef %define MLTSDL2_EXPORT %enddef %define MLTSOX_EXPORT %enddef %define MLTSPATIALAUDIO_EXPORT %enddef %define MLTVIDSTAB_EXPORT %enddef %define MLTVORBIS_EXPORT %enddef %define MLTXINE_EXPORT %enddef %define MLTXML_EXPORT %enddef %include "carrays.i" %array_class(unsigned char, UnsignedCharArray); %{ #include #include %} /** These methods return objects which should be gc'd. */ namespace Mlt { %newobject Factory::init( const char * ); %newobject Factory::producer( Profile &, char *, char * ); %newobject Factory::filter( Profile &, char *, char * ); %newobject Factory::transition( Profile &, char *, char * ); %newobject Factory::consumer( Profile &, char *, char * ); %newobject Properties::listen( const char *, void *, mlt_listener ); %newobject Properties::setup_wait_for( const char * ); %newobject Properties::parse_yaml( const char * ); %newobject Service::producer( ); %newobject Service::consumer( ); %newobject Service::get_frame( int ); %newobject Service::filter( int ); %newobject Producer::filter( int ); %newobject Producer::cut( int, int ); %newobject Playlist::current( ); %newobject Playlist::clip_info( int ); %newobject Playlist::get_clip( int ); %newobject Multitrack::track( int ); %newobject Tractor::multitrack( ); %newobject Tractor::field( ); %newobject Tractor::track( int ); %newobject Frame::get_original_producer( ); %newobject Repository::consumers( ); %newobject Repository::filters( ); %newobject Repository::producers( ); %newobject Repository::transitions( ); %newobject Repository::metadata( mlt_service_type, const char * ); %newobject Repository::languages( ); %newobject Profile::list(); %newobject Repository::presets(); %newobject Properties::get_anim(); %newobject Animation::Animation(); %rename(__assign__) Animation::operator=; %rename(__assign__) Frame::operator=; #if defined(SWIGPYTHON) %feature("autodoc", "1"); %feature("shadow") Frame::get_waveform(int, int) %{ def get_waveform(*args): return _mlt7.frame_get_waveform(*args) %} %feature("shadow") Frame::get_image(mlt_image_format&, int&, int&) %{ def get_image(*args): return _mlt7.frame_get_image(*args) %} #endif } /** Classes to wrap. */ %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include #if defined(SWIGRUBY) %{ static void ruby_listener( mlt_properties owner, void *object ); class RubyListener { protected: VALUE callback; Mlt::Event *event; public: RubyListener( VALUE callback ) : callback( callback ) {} RubyListener( Mlt::Properties &properties, char *id, VALUE callback ) : callback( callback ) { event = properties.listen( id, this, ( mlt_listener )ruby_listener ); } virtual ~RubyListener( ) { delete event; } void mark( ) { ((void (*)(VALUE))(rb_gc_mark))( callback ); } void doit( ) { ID method = rb_intern( "call" ); rb_funcall( callback, method, 0 ); } }; static void ruby_listener( mlt_properties owner, void *object ) { RubyListener *o = static_cast< RubyListener * >( object ); o->doit( ); } void markRubyListener( void* p ) { RubyListener *o = static_cast( p ); o->mark( ); } static void on_playlist_next( mlt_properties owner, void *object, mlt_event_data ); class PlaylistNextListener : RubyListener { private: Mlt::Event *event; public: PlaylistNextListener( Mlt::Properties *properties, VALUE callback ) : RubyListener( callback ) { event = properties->listen( "playlist-next", this, ( mlt_listener )on_playlist_next ); } ~PlaylistNextListener() { delete event; } void yield(const Mlt::EventData& eventData) { ID method = rb_intern( "call" ); rb_funcall( callback, method, 1, INT2FIX( eventData.to_int() ) ); } }; static void on_playlist_next( mlt_properties owner, void *object, mlt_event_data event_data ) { PlaylistNextListener *o = static_cast< PlaylistNextListener * >( object ); Mlt::EventData data(event_data); o->yield(data); } %} // Ruby wrapper %rename( Listener ) RubyListener; %markfunc RubyListener "markRubyListener"; %markfunc PlaylistNextListener "markRubyListener"; class RubyListener { public: RubyListener( Mlt::Properties &properties, char *id, VALUE callback ); }; class PlaylistNextListener { public: PlaylistNextListener( Mlt::Properties *properties, VALUE proc ); }; #endif // SWIGGRUBY #if defined(SWIGPYTHON) %{ typedef struct { int size; char* data; } binary_data; binary_data frame_get_waveform( Mlt::Frame &frame, int w, int h ) { binary_data result = { w * h, (char*) frame.get_waveform( w, h ) }; return result; } binary_data frame_get_image( Mlt::Frame &frame, mlt_image_format format, int w, int h ) { binary_data result = { mlt_image_format_size( format, w, h, NULL ), (char*) frame.get_image( format, w, h ) }; return result; } %} %typemap(out) binary_data { $result = %#if PY_MAJOR_VERSION < 3 PyString_FromStringAndSize( %#else PyByteArray_FromStringAndSize( %#endif $1.data, $1.size ); } binary_data frame_get_waveform(Mlt::Frame&, int, int); binary_data frame_get_image(Mlt::Frame&, mlt_image_format, int, int); #endif mlt-7.38.0/src/swig/nodejs/000775 000000 000000 00000000000 15172202314 015404 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/nodejs/CMakeLists.txt000664 000000 000000 00000001052 15172202314 020142 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mltnodejs LANGUAGE javascript SOURCES ../mlt.i) target_compile_options(mltnodejs PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltnodejs PRIVATE mlt mlt++) target_include_directories(mltnodejs PRIVATE ${NODEJS_INCLUDE_DIRS}) set_target_properties(mltnodejs PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/nodejs") set_target_properties(mltnodejs PROPERTIES OUTPUT_NAME "mlt" SWIG_COMPILE_OPTIONS -node ) mlt-7.38.0/src/swig/nodejs/apply-filter.js000664 000000 000000 00000002003 15172202314 020345 0ustar00rootroot000000 000000 #!/usr/bin/env node const path = require('path') const mlt = require('./node-gyp-build/Release/mlt.node') const { Factory, Profile, Producer, Filter, Consumer } = mlt; Factory.init('') let profile = new Profile('hdv_720_25p') let producer = new Producer(profile, 'avformat', process.argv[2]) if (!producer.is_valid()) { console.log(`Unable to open producer`) process.exit(1) } producer.set_in_and_out(100, 200) let videoOutputFilePath = path.join(__dirname, 'output.mp4') console.log('output:', videoOutputFilePath) let consumer = new Consumer(profile, 'avformat', videoOutputFilePath) consumer.set('rescale', 'none') consumer.set('vcodec', 'libx264') consumer.set('acodec', 'aac') let filter = new Filter(profile, 'charcoal') producer.attach(filter) consumer.connect(producer) consumer.start() check() function check() { process.stdout.write('.') if (consumer.is_stopped()) { consumer.stop() Factory.close() return } setTimeout(check, 1000) } mlt-7.38.0/src/swig/nodejs/binding.gyp000664 000000 000000 00000000305 15172202314 017535 0ustar00rootroot000000 000000 { "targets": [{ "target_name": "mlt", "sources": [ "mlt_wrap.cxx" ], "include_dirs": [ "../.." ], "libraries": [ "-L../../../mlt++ -lmlt++" ] }] } mlt-7.38.0/src/swig/nodejs/build000775 000000 000000 00000000726 15172202314 016436 0ustar00rootroot000000 000000 #!/bin/sh CXX=${CXX:-g++} gypBuildDir="node-gyp-build" if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.nodejs; rm -r "$gypBuildDir" ) exit 0 fi which node-gyp 2> /dev/null if [ $? = 0 ] then ln -sf ../mlt.i # Invoke swig swig -c++ -I../../mlt++ -I../.. -javascript -node mlt.i || exit $? # Compile the wrapper NODE_GYP_BUILD_DIR="$gypBuildDir" node-gyp configure build || exit $? else echo node-gyp not installed. exit 1 fi mlt-7.38.0/src/swig/nodejs/list-members.js000664 000000 000000 00000001155 15172202314 020347 0ustar00rootroot000000 000000 #!/usr/bin/env node const mlt = require('./node-gyp-build/Release/mlt.node') const members = Object.keys(mlt) const maxKeyLength = members.reduce((max, key) => key.length > max ? key.length : max, 0) const output = members.map(member => [member, mlt[member]]) .map(([key, val]) => `${padRight(key, maxKeyLength)}\t${isFunction(val) ? '[function]' : val}`) .join('\n') console.log(output) function padRight(str, len, char = ' ') { while (str.length < len) { str += char } return str; } function isFunction(val) { return typeof val === 'function' } function entries(obj) { return } mlt-7.38.0/src/swig/nodejs/play.js000664 000000 000000 00000001443 15172202314 016711 0ustar00rootroot000000 000000 #!/usr/bin/env node const path = require('path') const mlt = require('./node-gyp-build/Release/mlt.node') const { Factory, Profile, Producer, Consumer } = mlt; const videoFile = process.argv[2] console.log(videoFile) Factory.init('') let profile = new Profile('hdv_720_25p') let producer = new Producer(profile, 'avformat', videoFile) if (!producer.is_valid()) { console.log(`Unable to open producer`) process.exit(1) } console.log('get_fps', producer.get_fps()) let consumer = new Consumer(profile, 'sdl2') consumer.connect(producer) consumer.start() check() function check() { console.log('is_stopped', consumer.is_stopped()) if (consumer.is_stopped()) { consumer.stop() Factory.close() return } setTimeout(check, 1000) } mlt-7.38.0/src/swig/perl/000775 000000 000000 00000000000 15172202314 015064 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/perl/CMakeLists.txt000664 000000 000000 00000001336 15172202314 017627 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mltperl LANGUAGE perl SOURCES ../mlt.i) target_compile_options(mltperl PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltperl PRIVATE mlt mlt++) target_include_directories(mltperl PRIVATE ${PERL_INCLUDE_PATH}) set_target_properties(mltperl PROPERTIES OUTPUT_NAME "mlt") set_target_properties(mltperl PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/perl") string(REGEX MATCH "lib.*" PERL_MODULE_INSTALL_DIR ${PERL_VENDORARCH}) install(TARGETS mltperl DESTINATION ${PERL_MODULE_INSTALL_DIR}/auto/mlt) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mlt.pm DESTINATION ${PERL_MODULE_INSTALL_DIR}) mlt-7.38.0/src/swig/perl/Makefile.PL000664 000000 000000 00000000645 15172202314 017043 0ustar00rootroot000000 000000 #!/bin/env perl use ExtUtils::MakeMaker; my $CXX = $ENV{'CXX'} || 'g++'; system( "ln -sf ../mlt.i" ); system( "swig -c++ -I../../mlt++ -I../.. -perl5 mlt.i" ); WriteMakefile( 'NAME' => 'mlt', 'CC' => '${CXX} -fPIC ${CXXFLAGS} -I../..', 'OPTIMIZE' => '-O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions', 'LIBS' => ['-L../../mlt++ -lmlt++'], 'OBJECT' => 'mlt_wrap.o', 'DESTDIR' => $ENV{'DESTDIR'}, ); mlt-7.38.0/src/swig/perl/build000775 000000 000000 00000000250 15172202314 016106 0ustar00rootroot000000 000000 #!/bin/sh if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.perl mlt.pm ) exit 0 fi CXXFLAGS="$CXXFLAGS" perl Makefile.PL || exit 1 make mlt-7.38.0/src/swig/perl/play.pl000775 000000 000000 00000001357 15172202314 016377 0ustar00rootroot000000 000000 #!/usr/bin/env perl # Import required modules use mlt; # Not sure why the mlt::Factory.init method fails... mlt::mlt_factory_init( undef ); # Establish the MLT profile $profile = new mlt::Profile( undef ); # Create the producer $p = new mlt::Producer( $profile, $ARGV[0] ); if ( $p->is_valid( ) ) { # Loop the video $p->set( "eof", "loop" ); # Create the consumer $c = new mlt::FilteredConsumer( $profile, "sdl2" ); # Connect the producer to the consumer $c->connect( $p ); $e = $c->setup_wait_for( "consumer-stopped" ); # Start the consumer $c->start; # Wait until the user stops the consumer $c->wait_for( $e ); $e = undef; $c = undef; $p = undef; } else { print "Unable to open $ARGV[0]\n"; } mlt::mlt_factory_close( ); mlt-7.38.0/src/swig/php/000775 000000 000000 00000000000 15172202314 014711 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/php/CMakeLists.txt000664 000000 000000 00000001320 15172202314 017445 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mltphp LANGUAGE php SOURCES ../mlt.i) target_compile_options(mltphp PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltphp PRIVATE mlt mlt++ PHP::Extension) set_target_properties(mltphp PROPERTIES OUTPUT_NAME "mlt") set_target_properties(mltphp PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/php") if(PHP_EXTENSION_DIR) string(REGEX MATCH "lib.*" PHP_EXTENSION_INSTALL_DIR ${PHP_EXTENSION_DIR}) install(TARGETS mltphp DESTINATION ${PHP_EXTENSION_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mlt.php DESTINATION ${PHP_EXTENSION_INSTALL_DIR} OPTIONAL) endif() mlt-7.38.0/src/swig/php/build000775 000000 000000 00000000617 15172202314 015742 0ustar00rootroot000000 000000 #!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cpp *.so *.o mlt.i ../.php mlt.php *.h ) exit 0 fi ln -sf ../mlt.i swig -c++ -I../../mlt++ -I../.. -php5 -noproxy mlt.i ${CXX} -fPIC -DPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. `php-config --includes` mlt_wrap.cpp ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o mlt.so || exit $? mlt-7.38.0/src/swig/php/play.php000775 000000 000000 00000000667 15172202314 016403 0ustar00rootroot000000 000000 mlt-7.38.0/src/swig/python/000775 000000 000000 00000000000 15172202314 015443 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/python/CMakeLists.txt000664 000000 000000 00000001603 15172202314 020203 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mltpython LANGUAGE python SOURCES ../mlt.i) target_compile_options(mltpython PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltpython PRIVATE mlt mlt++ Python3::Module) set_target_properties(mltpython PROPERTIES PREFIX "_" OUTPUT_NAME "mlt${MLT_VERSION_MAJOR}") set_target_properties(mltpython PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/python") string(REGEX MATCH "[lL]ib.*" PYTHON_MODULE_INSTALL_DIR ${Python3_SITEARCH}) if(SWIG_VERSION VERSION_GREATER_EQUAL "4.0.0") set_property(TARGET mltpython PROPERTY SWIG_COMPILE_OPTIONS -doxygen) endif() install(TARGETS mltpython DESTINATION ${PYTHON_MODULE_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mlt.py DESTINATION ${PYTHON_MODULE_INSTALL_DIR} RENAME mlt${MLT_VERSION_MAJOR}.py ) mlt-7.38.0/src/swig/python/build000775 000000 000000 00000001421 15172202314 016466 0ustar00rootroot000000 000000 #!/bin/sh CXX=${CXX:-g++} PYTHON=${PYTHON:-python3} path=`which "$PYTHON" 2> /dev/null` if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.python mlt.py* ) exit 0 fi if [ $? = 0 ] then # Change this as needed export PYTHON_INCLUDE="$("${PYTHON}-config" --includes)" [ -z "$PYTHON_INCLUDE" ] && echo "$PYTHON" development missing && exit 1 ln -sf ../mlt.i # Invoke swig swig -c++ -I../../mlt++ -I../.. -python mlt.i || exit $? # Compile the wrapper ${CXX} -fPIC -D_GNU_SOURCE ${CXXFLAGS} -c -I../.. $PYTHON_INCLUDE mlt_wrap.cxx || exit $? # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -L../../framework -lmlt $("${PYTHON}-config" --ldflags) -o _mlt.so || exit $? else echo Python not installed. exit 1 fi mlt-7.38.0/src/swig/python/codecs.py000775 000000 000000 00000000777 15172202314 017273 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Import required modules from __future__ import print_function import mlt7 as mlt # Start the mlt system mlt.Factory().init() # Create the consumer c = mlt.Consumer(mlt.Profile(), "avformat") # Ask for video codecs supports c.set('vcodec', 'list') # Start the consumer to generate the list c.start() # Get the vcodec property codecs = mlt.Properties(c.get_data('vcodec')) # Print the list of codecs for i in range(0, codecs.count()): print(codecs.get(i)) mlt-7.38.0/src/swig/python/getimage.py000775 000000 000000 00000001526 15172202314 017606 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import mlt7 as mlt import sys from PIL import Image # setup mlt.Factory.init() profile = mlt.Profile('square_pal_wide') prod = mlt.Producer(profile, sys.argv[1]) # This builds a profile from the attributes of the producer: auto-profile. profile.from_producer(prod) # Ensure the image is square pixels - optional. profile.set_width(int(profile.width() * profile.sar())) profile.set_sample_aspect(1, 0) # Seek to 10% and get a Mlt frame. prod.seek(int(prod.get_length() * 0.1)) frame = prod.get_frame() # Make sure we deinterlace if input is interlaced. frame.set("consumer.progressive", 1) # Now we are ready to get the image and save it. size = (profile.width(), profile.height()) rgb = frame.get_image(mlt.mlt_image_rgb, *size) img = Image.fromstring('RGB', size, rgb) img.save(sys.argv[1] + '.png') mlt-7.38.0/src/swig/python/play.py000775 000000 000000 00000001605 15172202314 016767 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Import required modules from __future__ import print_function import mlt7 as mlt import time import sys # Start the mlt system mlt.Factory().init() # Establish a default (usually "dv_pal") profile profile = mlt.Profile() # Create the producer p = mlt.Producer(profile, sys.argv[1]) if p.is_valid(): # Derive a profile based on the producer profile.from_producer(p) # Reload the producer using the derived profile p = mlt.Producer(profile, sys.argv[1]) # Create the consumer c = mlt.Consumer(profile, "sdl2") if not c.is_valid(): print("Failed to open the sdl2 consumer") sys.exit(1) # Connect the producer to the consumer c.connect(p) # Start the consumer c.start() # Wait until the user stops the consumer while not c.is_stopped(): time.sleep(1) else: # Diagnostics print("Unable to open ", sys.argv[1]) sys.exit(1) mlt-7.38.0/src/swig/python/play_gtk.py000775 000000 000000 00000011745 15172202314 017642 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import gi import sys import mlt7 as mlt gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gdk, GObject, GLib VIDEO = 'video.mp4' class VideoPlayer(Gtk.Window): def __init__(self, filename): super().__init__(title="MLT Video Player") self.set_default_size(800, 600) self.connect("destroy", self.on_destroy) # Initialize the mlt system mlt.Factory().init() # Create a default profile self.profile = mlt.Profile() # Create a producer for the video self.producer = mlt.Producer(self.profile, filename) if self.producer.is_valid(): self.profile.from_producer(self.producer) self.producer = mlt.Producer(self.profile, filename) # Create the consumer for rendering using SDL2 self.consumer = mlt.Consumer(self.profile, "sdl2") # GTK Layout self.layout = Gtk.VBox(spacing=6) self.add(self.layout) # Create the drawing area for the video self.drawing_area = Gtk.DrawingArea() self.drawing_area.set_size_request(800, 450) self.drawing_area.connect("size-allocate",self.drawing_area_resized) self.layout.pack_start(self.drawing_area, True, True, 0) self.drawing_area.realize() print(int(self.drawing_area.get_window().get_xid())) self.consumer.set('window_id', str(self.drawing_area.get_window().get_xid())) if not self.consumer.is_valid(): print("Failed to open the sdl2 consumer") sys.exit(1) # Create a multitrack playlist and set it to the tractor self.playlist = mlt.Playlist(self.profile) self.playlist.append(self.producer) self.tractor = mlt.Tractor() self.tractor.set_track(self.playlist, 0) # Connect the producer to the consumer self.consumer.connect(self.tractor) # Control Buttons controls_box = Gtk.HBox(spacing=6) self.layout.pack_start(controls_box, False, False, 0) # Play button self.play_button = Gtk.Button.new_with_label("Play") self.play_button.connect("clicked", self.play_video) controls_box.pack_start(self.play_button, False, False, 0) # Pause button self.pause_button = Gtk.Button.new_with_label("Pause") self.pause_button.connect("clicked", self.pause_video) controls_box.pack_start(self.pause_button, False, False, 0) # Stop button self.stop_button = Gtk.Button.new_with_label("Stop") self.stop_button.connect("clicked", self.stop_video) controls_box.pack_start(self.stop_button, False, False, 0) # Slider self.timeline_slider = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0, 1000, 1) self.timeline_slider.set_draw_value(False) self.slider_handler_id = self.timeline_slider.connect("value-changed", self.on_slider_changed) self.timeline_slider.connect("button-release-event", self.on_slider_button_release) controls_box.pack_start(self.timeline_slider, True, True, 0) # Timer to update the slider self.update_timer = GLib.timeout_add(1000 // 24, self.update_slider) # Start the consumer self.consumer.start() self.pause_video() def drawing_area_resized(self, widget, _): self.consumer.set('window_width', widget.get_allocated_width()) self.consumer.set('window_height', widget.get_allocated_height()) def update_slider(self): position = self.consumer.position() playtime = self.tractor.get_playtime() if playtime > 0: value = int((position / playtime) * 1000) self.timeline_slider.handler_block(self.slider_handler_id) self.timeline_slider.set_value(value) self.timeline_slider.handler_unblock(self.slider_handler_id) return True def play_video(self, button): self.tractor.set_speed(1) self.consumer.set('volume', 1) def pause_video(self, button=None): if not self.consumer.is_stopped(): self.tractor.set_speed(0) self.consumer.set('volume', 0) def stop_video(self, button): self.tractor.seek(0) self.tractor.set_speed(0) self.timeline_slider.set_value(0) def on_slider_changed(self, slider): value = slider.get_value() self.consumer.set('volume', 0) playtime = self.tractor.get_playtime() self.tractor.seek(int((value / 1000.0) * playtime)) def on_slider_button_release(self, slider, _): if not self.tractor.get_speed() == 0: self.consumer.set('volume', 1) def on_destroy(self, widget): self.consumer.stop() mlt.Factory().close() Gtk.main_quit() if __name__ == "__main__": # Initialize GTK GObject.threads_init() Gtk.init(sys.argv) # Create and run the video player player = VideoPlayer(sys.argv[1] if len(sys.argv) > 1 else VIDEO) player.show_all() Gtk.main() mlt-7.38.0/src/swig/python/play_pyqt6.py000775 000000 000000 00000012027 15172202314 020132 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import mlt7 as mlt import sys from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QSlider, QStyle from PyQt6.QtCore import Qt, QTimer VIDEO = 'video.mp4' class VideoPlayer(QWidget): def __init__(self, filename): super().__init__() self.setWindowTitle("MLT Video Player") layout = QVBoxLayout() class video_viewer(QWidget): """The main window (QWidget) class""" def __init__(self): super().__init__() self.setMinimumSize(380, 260) self.blockResize = False def resizeEvent(self, event): if not self.blockResize: self.window().consumer.set('window_width', self.width()) self.window().consumer.set('window_height', self.height()) event.accept() self.blockResize = not self.blockResize self.video_viewer = video_viewer() layout.addWidget(self.video_viewer, 1) buttons_layout = QHBoxLayout() self.play_button = QPushButton() self.play_button.setFlat(True) self.play_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) self.play_button.clicked.connect(self.play_video) buttons_layout.addWidget(self.play_button) self.pause_button = QPushButton() self.pause_button.setFlat(True) self.pause_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPause)) self.pause_button.clicked.connect(self.pause_video) buttons_layout.addWidget(self.pause_button) self.stop_button = QPushButton() self.stop_button.setFlat(True) self.stop_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaStop)) self.stop_button.clicked.connect(self.stop_video) buttons_layout.addWidget(self.stop_button) self.timeline_slider = QSlider(Qt.Orientation.Horizontal) self.timeline_slider.setRange(0, 1000) self.timeline_slider.sliderPressed.connect(self.stop_update_slider) self.timeline_slider.sliderMoved.connect(self.timeline_slider_changed) self.timeline_slider.sliderReleased.connect(self.set_volume_back_to_normal) buttons_layout.addWidget(self.timeline_slider) layout.addLayout(buttons_layout) self.setLayout(layout) self.timer = QTimer(self) self.timer.timeout.connect(self.update_slider) # Start the mlt system mlt.Factory().init() # Establish a default (usually "dv_pal") profile self.profile = mlt.Profile() # Create the producer self.producer = mlt.Producer(self.profile, filename) if self.producer.is_valid(): # Derive a profile based on the producer self.profile.from_producer(self.producer) # Reload the producer using the derived profile self.producer = mlt.Producer(self.profile, filename) # Create the consumer self.consumer = mlt.Consumer(self.profile, "sdl2") self.consumer.set('window_id', str(int(self.video_viewer.winId()))) # PyQt6 requires str or int for set('window_id') if not self.consumer.is_valid(): print("Failed to open the sdl2 consumer") sys.exit(1) # Setup a multitrack self.playlist = mlt.Playlist(self.profile) self.playlist.append(self.producer) self.tractor = mlt.Tractor() self.tractor.set_track(self.playlist, 0) # Connect the producer to the consumer self.consumer.connect(self.tractor) # Start the consumer self.consumer.start() self.pause_video() def update_slider(self): self.timeline_slider.setValue(int((self.consumer.position() / self.tractor.get_playtime()) * 1000)) def play_video(self): self.tractor.set_speed(1) self.consumer.set('volume', 1) self.timer.start(int(1000 / 24)) def pause_video(self): if not self.consumer.is_stopped(): self.tractor.set_speed(0) self.consumer.set('volume', 0) def stop_video(self): self.tractor.seek(0) self.tractor.set_speed(0) self.timeline_slider.setValue(0) self.timer.stop() def timeline_slider_changed(self, value): self.consumer.set('volume', 0) self.tractor.seek(int((value / 1000.0) * self.tractor.get_playtime())) def stop_update_slider(self): self.timer.stop() def set_volume_back_to_normal(self): if not self.tractor.get_speed() == 0: self.consumer.set('volume', 1) self.timer.start(int(1000 / 24)) def closeEvent(self, event): self.consumer.stop() event.accept() if __name__ == "__main__": app = QApplication(sys.argv) player = VideoPlayer(sys.argv[1] if len(sys.argv) > 1 else VIDEO) player.show() sys.exit(app.exec()) mlt-7.38.0/src/swig/python/play_pyside6.py000775 000000 000000 00000011656 15172202314 020441 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import mlt7 as mlt import sys from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QSlider, QStyle from PySide6.QtCore import Qt, QTimer VIDEO = 'video.mp4' class VideoPlayer(QWidget): def __init__(self, filename): super().__init__() self.setWindowTitle("MLT Video Player") layout = QVBoxLayout() class video_viewer(QWidget): """The main window (QWidget) class""" def __init__(self): super().__init__() self.setMinimumSize(380, 260) self.blockResize = False def resizeEvent(self, event): if not self.blockResize: self.window().consumer.set('window_width', self.width()) self.window().consumer.set('window_height', self.height()) event.accept() self.blockResize = not self.blockResize self.video_viewer = video_viewer() layout.addWidget(self.video_viewer, 1) buttons_layout = QHBoxLayout() self.play_button = QPushButton() self.play_button.setFlat(True) self.play_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.play_button.clicked.connect(self.play_video) buttons_layout.addWidget(self.play_button) self.pause_button = QPushButton() self.pause_button.setFlat(True) self.pause_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPause)) self.pause_button.clicked.connect(self.pause_video) buttons_layout.addWidget(self.pause_button) self.stop_button = QPushButton() self.stop_button.setFlat(True) self.stop_button.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) self.stop_button.clicked.connect(self.stop_video) buttons_layout.addWidget(self.stop_button) self.timeline_slider = QSlider(Qt.Horizontal) self.timeline_slider.setRange(0, 1000) self.timeline_slider.sliderPressed.connect(self.stop_update_slider) self.timeline_slider.sliderMoved.connect(self.timeline_slider_changed) self.timeline_slider.sliderReleased.connect(self.set_volume_back_to_normal) buttons_layout.addWidget(self.timeline_slider) layout.addLayout(buttons_layout) self.setLayout(layout) self.timer = QTimer(self) self.timer.timeout.connect(self.update_slider) # Start the mlt system mlt.Factory().init() # Establish a default (usually "dv_pal") profile self.profile = mlt.Profile() # Create the producer self.producer = mlt.Producer(self.profile, filename) if self.producer.is_valid(): # Derive a profile based on the producer self.profile.from_producer(self.producer) # Reload the producer using the derived profile self.producer = mlt.Producer(self.profile, filename) # Create the consumer self.consumer = mlt.Consumer(self.profile, "sdl2") self.consumer.set('window_id', self.video_viewer.winId()) if not self.consumer.is_valid(): print("Failed to open the sdl2 consumer") sys.exit(1) # Setup a multitrack self.playlist = mlt.Playlist(self.profile) self.playlist.append(self.producer) self.tractor = mlt.Tractor() self.tractor.set_track(self.playlist, 0) # Connect the producer to the consumer self.consumer.connect(self.tractor) # Start the consumer self.consumer.start() self.pause_video() def update_slider(self): self.timeline_slider.setValue(int((self.consumer.position() / self.tractor.get_playtime()) * 1000)) def play_video(self): self.tractor.set_speed(1) self.consumer.set('volume', 1) self.timer.start(1000/24) def pause_video(self): if not self.consumer.is_stopped(): self.tractor.set_speed(0) self.consumer.set('volume', 0) def stop_video(self): self.tractor.seek(0) self.tractor.set_speed(0) self.timeline_slider.setValue(0) self.timer.stop() def timeline_slider_changed(self, value): self.consumer.set('volume', 0) self.tractor.seek(int((value / 1000.0) * self.tractor.get_playtime())) def stop_update_slider(self): self.timer.stop() def set_volume_back_to_normal(self): if not self.tractor.get_speed() == 0: self.consumer.set('volume', 1) self.timer.start(1000/24) def closeEvent(self, event): self.consumer.stop() event.accept() if __name__ == "__main__": import sys app = QApplication(sys.argv) player = VideoPlayer(sys.argv[1] if len(sys.argv) > 1 else VIDEO) player.show() sys.exit(app.exec())mlt-7.38.0/src/swig/python/python_bindings.dox000664 000000 000000 00000001413 15172202314 021354 0ustar00rootroot000000 000000 /** * \namespace mlt * \brief Python bindings for MLT * * Mirrors the \c Mlt:: C++ namespace. Import as: * \code{.py} * import mlt7 as mlt * mlt.Factory().init(None) * profile = mlt.Profile() * producer = mlt.Producer(profile, 'clip.mp4') * \endcode * * \see Mlt::Factory, Mlt::Producer, Mlt::Consumer */ /** * \defgroup python_bindings Python Bindings * \brief Python bindings for MLT * * The Python bindings wrap the \ref mlt++ "MLT C++ API" via SWIG. * The generated module is named \c mlt7 and mirrors the \c Mlt:: C++ namespace. * * \par Usage * \code{.py} * import mlt7 as mlt * mlt.Factory().init(None) * profile = mlt.Profile() * producer = mlt.Producer(profile, 'clip.mp4') * \endcode * * \see Mlt::Factory, Mlt::Producer, Mlt::Consumer */ mlt-7.38.0/src/swig/python/switcher.py000775 000000 000000 00000003504 15172202314 017652 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Import required modules import mlt7 as mlt import time import sys import tornado.ioloop import tornado.web # Start the mlt system mlt.mlt_log_set_level(40) # verbose mlt.Factory.init() # Establish a pipeline profile = mlt.Profile("atsc_1080i_5994") profile.set_explicit(1) tractor = mlt.Tractor() tractor.set("eof", "loop") fg_resource = "decklink:0" bg_resource = "decklink:1" if len(sys.argv) > 2: fg_resource = sys.argv[1] bg_resource = sys.argv[2] fg = mlt.Producer(profile, fg_resource) bg = mlt.Producer(profile, bg_resource) tractor.set_track(bg, 0) tractor.set_track(fg, 1) composite = mlt.Transition(profile, "composite") composite.set("fill", 1) tractor.plant_transition(composite) # Setup the consumer consumer = "decklink:2" if len(sys.argv) > 3: consumer = sys.argv[3] consumer = mlt.Consumer(profile, consumer) consumer.connect(tractor) consumer.set("real_time", -2) consumer.start() flip_flop = False def switch(): global composite, flip_flop frame = fg.frame() + 2 if flip_flop: s = "0=20%%/0:100%%x80%%; %d=20%%/0:100%%x80%%; %d=0/0:100%%x100%%" % (frame, frame + 30) composite.set("geometry", s) else: s = "0=0/0:100%%x100%%; %d=0/0:100%%x100%%; %d=20%%/0:100%%x80%%" % (frame, frame + 30) composite.set("geometry", s) flip_flop = not flip_flop def output_form(handler): handler.write('
') class SwitchHandler(tornado.web.RequestHandler): def get(self): switch() class MainHandler(tornado.web.RequestHandler): def get(self): output_form(self) def post(self): switch() output_form(self) application = tornado.web.Application([ (r"/", MainHandler), (r"/switch", SwitchHandler) ]) application.listen(8888) tornado.ioloop.IOLoop.instance().start() mlt-7.38.0/src/swig/python/test_animation.py000775 000000 000000 00000000471 15172202314 021040 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- from __future__ import print_function import mlt7 as mlt p = mlt.Properties() p.anim_set("foo", "bar", 10) a = p.get_anim("bar") print("bar is valid:", a.is_valid()) a = p.get_anim("foo") print("foo is valid:", a.is_valid()) print("serialize:", a.serialize_cut()) mlt-7.38.0/src/swig/python/waveforms.py000775 000000 000000 00000000564 15172202314 020036 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import mlt7 as mlt from PIL import Image mlt.Factory.init() profile = mlt.Profile() prod = mlt.Producer(profile, 'test.wav') size = (320, 240) for i in range(0, prod.get_length()): frm = prod.get_frame() wav = frm.get_waveform(size[0], size[1]) img = Image.fromstring('L', size, wav) img.save('test-%04d.pgm' % (i)) mlt-7.38.0/src/swig/python/webvfx_generator.py000775 000000 000000 00000006670 15172202314 021400 0ustar00rootroot000000 000000 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # webvfx_generator.py # Copyright (C) 2013 Dan Dennedy # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Import required modules import mlt7 as mlt import time import sys import tornado.ioloop import tornado.web import shutil import tempfile import os import os.path # Start the mlt system mlt.mlt_log_set_level(40) # verbose mlt.Factory.init() # Establish a pipeline profile = mlt.Profile("atsc_1080i_5994") #profile = mlt.Profile('square_ntsc_wide') profile.set_explicit(1) tractor = mlt.Tractor() tractor.set('eof', 'loop') playlist = mlt.Playlist() playlist.append(mlt.Producer(profile, 'color:')) # Setup the consumer consumer = 'decklink:0' if len(sys.argv) > 1: consumer = sys.argv[1] consumer = mlt.Consumer(profile, consumer) consumer.connect(playlist) #consumer.set("real_time", -2) consumer.start() def switch(resource): global playlist resource = resource playlist.lock() playlist.append(mlt.Producer(profile, str(resource))) playlist.remove(0) playlist.unlock() state = {} state['tempdir'] = None class MainHandler(tornado.web.RequestHandler): def get(self): resource = self.get_argument('url', None) if resource: global state self.write('Playing %s\n' % (resource)) switch(resource) olddir = state['tempdir'] if olddir: shutil.rmtree(olddir, True) state['tempdir'] = None else: self.write('''

POST a bunch of files to / to change the output.

Or GET / with query string parameter "url" to display something from the network.

''') def post(self): if len(self.request.files) == 0: self.write('POST a bunch of files to / to change the output') else: global state olddir = state['tempdir'] resource = None state['tempdir'] = tempfile.mkdtemp() for key, items in self.request.files.iteritems(): for item in items: path = os.path.dirname(key) fn = item.filename if path: if not os.path.exists(os.path.join(state['tempdir'], path)): os.makedirs(os.path.join(state['tempdir'], path)) fn = os.path.join(path, fn) fn = os.path.join(state['tempdir'], fn) if not path and fn.endswith('.html') or fn.endswith('.qml'): resource = fn with open(fn, 'w') as fo: fo.write(item.body) self.write("Uploaded %s\n" % (fn)) if resource: self.write('Playing %s\n' % (resource)) switch(resource) if olddir: shutil.rmtree(olddir, True) application = tornado.web.Application([ (r"/", MainHandler), ]) application.listen(8888) try: tornado.ioloop.IOLoop.instance().start() except: pass consumer.stop() if state['tempdir']: shutil.rmtree(state['tempdir'], True) mlt-7.38.0/src/swig/ruby/000775 000000 000000 00000000000 15172202314 015103 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/ruby/CMakeLists.txt000664 000000 000000 00000001076 15172202314 017647 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mltruby LANGUAGE ruby SOURCES ../mlt.i) target_compile_options(mltruby PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mltruby PRIVATE mlt++) target_include_directories(mltruby PRIVATE ${RUBY_INCLUDE_DIRS}) set_target_properties(mltruby PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ruby") set_target_properties(mltruby PROPERTIES OUTPUT_NAME "mlt") install(TARGETS mltruby DESTINATION ${CMAKE_INSTALL_LIBDIR}/ruby/vendor_ruby) mlt-7.38.0/src/swig/ruby/build000775 000000 000000 00000000754 15172202314 016136 0ustar00rootroot000000 000000 #!/usr/bin/env ruby require 'mkmf' if ARGV.shift == 'clean' system( "rm -f *.cxx *.so *.o mlt.i ../.ruby Makefile" ) exit 0 end system( "ln -sf ../mlt.i" ) system( "swig -c++ -ruby -I../../mlt++ -I../.. mlt.i" ) $CFLAGS = $CFLAGS.to_s + " -I../.. " + (ENV.has_key?('CXXFLAGS')? ENV['CXXFLAGS'] : '') $CXXFLAGS = $CXXFLAGS.to_s + " -I../.. " + (ENV.has_key?('CXXFLAGS')? ENV['CXXFLAGS'] : '') $LDFLAGS = $LDFLAGS.to_s + " -L../../mlt++ -lmlt++" create_makefile('mlt') system( "make V=1" ) mlt-7.38.0/src/swig/ruby/metadata.rb000775 000000 000000 00000007326 15172202314 017223 0ustar00rootroot000000 000000 #!/usr/bin/env ruby require 'erb' require 'yaml' require './mlt' $folder = 'markdown' $repo = Mlt::Factory::init $optional_params = [ 'minimum', 'maximum', 'default', 'unit', 'scale', 'format', 'widget' ] template = %q{--- layout: standard title: Documentation wrap_title: "<%= type_title %>: <%= yml['identifier'] %>" category: plugin --- * TOC {:toc} ## Plugin Information title: <%= yml['title'] %> % if yml['tags'] media types: % yml['tags'].each do |x| <%= x + " " %> % end <%= "\n" %> % end description: <%= ERB::Util.h(yml['description']) %> version: <%= yml['version'] %> creator: <%= yml['creator'] %> % yml['contributor'] and yml['contributor'].each do |x| contributor: <%= x %> % end <%= "copyright: #{yml['copyright']} \n" if yml['copyright'] %> <%= "license: #{yml['license']} \n" if yml['license'] %> <%= "URL: [#{yml['url']}](#{yml['url']}) \n" if yml['url'] %> % if yml['notes'] ## Notes <%= ERB::Util.h(yml['notes']) %> % end % if yml['bugs'] ## Bugs % yml['bugs'].each do |x| * <%= x %> % end % end % if yml['parameters'] ## Parameters % yml['parameters'].each do |param| ### <%= param['identifier'] %> <%= "title: #{param['title']} " if param['title'] %> % if param['description'] description: % if param['description'].include? "\n" ``` <%= param['description'] %> ``` % else <%= "#{ERB::Util.h(param['description'])} \n" %> % end % end type: <%= param['type'] %> readonly: <%= param['readonly'] and 'yes' or 'no' %> required: <%= param['required'] and 'yes' or 'no' %> <%= "animation: yes \n" if param['animation'] %> % $optional_params.each do |key| <%= "#{key}: #{param[key].to_s.gsub('](', ']\(')} \n" if param[key] %> % end % if param['values'] values: % param['values'].each do |value| * <%= value %> % end % end % end % end } $processor = ERB.new(template, 0, "%<>") def output(mlt_type, services, type_title) filename = File.join($folder, "Plugins#{type_title}s.md") index = File.open(filename, 'w') index.puts '---' index.puts 'title: Documentation' index.puts "wrap_title: #{type_title} Plugins" index.puts '---' unsorted = [] (0..(services.count - 1)).each do |i| unsorted << services.get_name(i) end unsorted.sort().each do |name| meta = $repo.metadata(mlt_type, name) if meta.is_valid if !meta.get_data('parameters') puts "No parameters for #{name} #{type_title}" end filename = File.join($folder, type_title + name.capitalize.gsub('.', '-')) # puts "Processing #{filename}" begin raw = meta.serialise_yaml yml = YAML.load(raw) rescue => e puts "Failed to parse YAML for #{filename}: #{e.message}" # Write bad YAML to a temp file for inspection tmp_filename = "/tmp/bad_#{name}.yaml" File.write(tmp_filename, raw) puts "See #{tmp_filename}" next end if yml File.open(filename + '.md', 'w') do |f| f.puts $processor.result(binding) end else puts "Failed to write file for #{filename}" end filename = type_title + name.capitalize.gsub('.', '-') index.puts "* [#{name}](../#{filename}/): #{meta.get('title')}\n" else puts "No metadata for #{name} #{type_title}" end unless name.start_with?('lv2.', 'vst2.') end index.close end Dir.mkdir($folder) if not Dir.exists?($folder) [ [Mlt::Mlt_service_consumer_type, $repo.consumers, 'Consumer'], [Mlt::Mlt_service_filter_type, $repo.filters, 'Filter'], [Mlt::Mlt_service_link_type, $repo.links, 'Link'], [Mlt::Mlt_service_producer_type, $repo.producers, 'Producer'], [Mlt::Mlt_service_transition_type, $repo.transitions, 'Transition'] ].each {|x| output *x} mlt-7.38.0/src/swig/ruby/play.rb000775 000000 000000 00000001431 15172202314 016377 0ustar00rootroot000000 000000 #!/usr/bin/env ruby # Import required modules require './mlt' # Create the mlt system Mlt::Factory::init # Establish the mlt profile profile = Mlt::Profile.new # Get and check the argument file = ARGV.shift raise "Usage: test.rb file" if file.nil? # Create the producer producer = Mlt::Factory::producer(profile, file) raise "Unable to load #{file}" if !producer.is_valid # Create the consumer consumer = Mlt::Consumer.new(profile, "sdl2") raise "Unable to open sdl2 consumer" if !consumer.is_valid # Set up a 'wait for' event event = consumer.setup_wait_for("consumer-stopped") # Start the consumer consumer.start # Connect the producer to the consumer consumer.connect(producer) # Wait until the user stops the consumer consumer.wait_for(event) # Clean up consumer consumer.stop mlt-7.38.0/src/swig/ruby/playlist.rb000775 000000 000000 00000002526 15172202314 017301 0ustar00rootroot000000 000000 #!/usr/bin/env ruby # Import required modules require './mlt' # Create the mlt system Mlt::Factory::init # Establish the mlt profile profile = Mlt::Profile.new # Get and check the argument file = ARGV.shift raise "Usage: test.rb file1 file2" if file.nil? file2 = ARGV.shift raise "Usage: test.rb file1 file2" if file2.nil? pls = Mlt::Playlist.new(profile) # Create the producer producer = Mlt::Factory::producer( profile, file ) raise "Unable to load #{file}" if !producer.is_valid producer2 = Mlt::Factory::producer( profile, file2 ) raise "Unable to load #{file2}" if !producer2.is_valid pls.append(producer) #pls.repeat(0, 2) pls.append(producer2) # Create the consumer consumer = Mlt::Consumer.new( profile, "sdl2" ) raise "Unable to open sdl consumer" if !consumer.is_valid # Turn off the default rescaling consumer.set( "rescale", "none" ) consumer.set("volume", 0.1) consumer.set("terminate_on_pause", 1) Mlt::PlaylistNextListener.new(pls, Proc.new { |i| info = pls.clip_info(i) puts "finished playing #{info.resource}\n" }) # Set up a 'wait for' event event = consumer.setup_wait_for( "consumer-stopped" ) # Start the consumer consumer.start # Connect the producer to the consumer consumer.connect( pls ) # Wait until the user stops the consumer consumer.wait_for( event ) puts "Done and closing" # Clean up consumer consumer.stop mlt-7.38.0/src/swig/ruby/thumbs.rb000775 000000 000000 00000001741 15172202314 016740 0ustar00rootroot000000 000000 #!/usr/bin/env ruby # Required modules require './mlt' # Create the mlt system Mlt::Factory::init # Establish the mlt profile profile = Mlt::Profile.new( "quarter_pal" ) # Get and check the argument file = ARGV.shift name = ARGV.shift size = ARGV.shift size = "176x144" if size.nil? raise "Usage: thumbs.rb file name [ size ]" if file.nil? || name.nil? # Create the producer producer = Mlt::Producer.new( profile, file ) raise "Unable to load #{file}" if !producer.is_valid # Construct the playlist playlist = Mlt::Playlist.new( ) # Get the out point out = producer.get_int( "out" ); # Calculate position of frames [ 0, 0.25, 0.5, 0.75, 1 ].each { |x| playlist.append( producer, Integer(x*out), Integer(x*out) ) } # Create the thumb nail generator generator = Mlt::Consumer.new( profile, "avformat", "#{name}%d.jpg" ) generator.set( "real_time", "0" ) generator.set( "progressive", "1" ) generator.set( "s", size ) # Connect the consumer generator.connect( playlist ); generator.run mlt-7.38.0/src/swig/tcl/000775 000000 000000 00000000000 15172202314 014704 5ustar00rootroot000000 000000 mlt-7.38.0/src/swig/tcl/CMakeLists.txt000664 000000 000000 00000000752 15172202314 017450 0ustar00rootroot000000 000000 set_source_files_properties(../mlt.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES ON CPLUSPLUS ON) swig_add_library(mlttcl LANGUAGE tcl SOURCES ../mlt.i) target_compile_options(mlttcl PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(mlttcl PRIVATE mlt mlt++) target_include_directories(mlttcl PRIVATE ${TCL_INCLUDE_PATH}) set_target_properties(mlttcl PROPERTIES OUTPUT_NAME "mlt") set_target_properties(mlttcl PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/tcl") mlt-7.38.0/src/swig/tcl/build000775 000000 000000 00000001031 15172202314 015724 0ustar00rootroot000000 000000 #!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.tcl ) exit 0 fi path=`which tclsh 2>/dev/null` if [ "$path" != "" ] then ln -sf ../mlt.i # Invoke swig swig -c++ -I../../mlt++ -I../.. -tcl mlt.i || exit 1 # Compile the wrapper ${CXX} -fPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. mlt_wrap.cxx || exit 1 # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o mlt.so || exit 1 else echo "Unable to locate tclsh." exit 1 fi mlt-7.38.0/src/swig/tcl/play.tcl000775 000000 000000 00000000553 15172202314 016363 0ustar00rootroot000000 000000 #!/usr/bin/env tclsh load mlt.so Factory_init set profile [Profile] set arg1 [lindex $argv 0] set p [Factory_producer $profile loader $arg1] set c [Factory_consumer $profile sdl ""] Properties_set $c "rescale" "none" Consumer_connect $c $p Consumer_start $c while { ![Consumer_is_stopped $c] } { after 1000 } delete_Consumer $c delete_Producer $p Factory_close mlt-7.38.0/src/tests/000775 000000 000000 00000000000 15172202314 014313 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/CMakeLists.txt000664 000000 000000 00000010455 15172202314 017060 0ustar00rootroot000000 000000 set(CMAKE_AUTOMOC ON) function(add_qt_test) set(options "") set(oneValueArgs TEST_NAME) set(multiValueArgs LINK_LIBRARIES INCLUDE_DIRS SOURCE_FILES) cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(_testname test_${arg_TEST_NAME}) add_executable(${_testname} ${_testname}/${_testname}.cpp ${arg_SOURCE_FILES}) target_compile_options(${_testname} PRIVATE ${MLT_COMPILE_OPTIONS}) target_link_libraries(${_testname} PRIVATE Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Test mlt++ ${ARG_LINK_LIBRARIES} ) if(MSVC) target_link_libraries(${_testname} PRIVATE PThreads4W::PThreads4W) endif() target_include_directories(${_testname} PRIVATE ${arg_INCLUDE_DIRS}) add_test(NAME "QtTest:${arg_TEST_NAME}" COMMAND ${_testname}) if(NOT WIN32) set_tests_properties("QtTest:${arg_TEST_NAME}" PROPERTIES ENVIRONMENT "LANG=en_US") endif() endfunction() add_qt_test(TEST_NAME animation) add_qt_test(TEST_NAME audio) add_qt_test(TEST_NAME events) add_qt_test(TEST_NAME filter) add_qt_test(TEST_NAME frame) add_qt_test(TEST_NAME image) add_qt_test(TEST_NAME multitrack) add_qt_test(TEST_NAME playlist) add_qt_test(TEST_NAME producer) add_qt_test(TEST_NAME properties) add_qt_test(TEST_NAME repository) add_qt_test(TEST_NAME service) add_qt_test(TEST_NAME tractor) add_qt_test(TEST_NAME xml) if(MOD_AVFORMAT) add_qt_test(TEST_NAME mod_avformat) endif() if(MOD_QT6) add_qt_test( TEST_NAME mod_qt_gps SOURCE_FILES "${CMAKE_SOURCE_DIR}/src/modules/qt/gps_parser.h" "${CMAKE_SOURCE_DIR}/src/modules/qt/gps_parser.cpp" ) endif() if(Kwalify_FOUND) file(GLOB YML_FILES "${CMAKE_SOURCE_DIR}/src/modules/*/*.yml") foreach(YML_FILE ${YML_FILES}) get_filename_component(FILE_NAME ${YML_FILE} NAME) file(RELATIVE_PATH KWALIFY_TEST_NAME "${CMAKE_SOURCE_DIR}/src/modules" ${YML_FILE}) if(NOT FILE_NAME MATCHES "resolution_scale.yml") add_test(NAME "kwalify:${KWALIFY_TEST_NAME}" COMMAND ${Kwalify_EXECUTABLE} -f "${CMAKE_SOURCE_DIR}/src/framework/metaschema.yaml" ${YML_FILE}) endif() endforeach() add_test( NAME "kwalify:runtime:filters" COMMAND ${CMAKE_COMMAND} -DMLT_EXECUTABLE=$ -DKWALIFY_EXECUTABLE=${Kwalify_EXECUTABLE} -DMETASCHEMA_FILE=${CMAKE_SOURCE_DIR}/src/framework/metaschema.yaml -DLIST_QUERY=filters -DQUERY_PREFIX=filter= -P ${CMAKE_CURRENT_SOURCE_DIR}/kwalify_runtime_query.cmake ) set_tests_properties("kwalify:runtime:filters" PROPERTIES TIMEOUT 300) add_test( NAME "kwalify:runtime:transitions" COMMAND ${CMAKE_COMMAND} -DMLT_EXECUTABLE=$ -DKWALIFY_EXECUTABLE=${Kwalify_EXECUTABLE} -DMETASCHEMA_FILE=${CMAKE_SOURCE_DIR}/src/framework/metaschema.yaml -DLIST_QUERY=transitions -DQUERY_PREFIX=transition= -P ${CMAKE_CURRENT_SOURCE_DIR}/kwalify_runtime_query.cmake ) set_tests_properties("kwalify:runtime:transitions" PROPERTIES TIMEOUT 300) add_test( NAME "kwalify:runtime:producers" COMMAND ${CMAKE_COMMAND} -DMLT_EXECUTABLE=$ -DKWALIFY_EXECUTABLE=${Kwalify_EXECUTABLE} -DMETASCHEMA_FILE=${CMAKE_SOURCE_DIR}/src/framework/metaschema.yaml -DLIST_QUERY=producers -DQUERY_PREFIX=producer= -P ${CMAKE_CURRENT_SOURCE_DIR}/kwalify_runtime_query.cmake ) set_tests_properties("kwalify:runtime:producers" PROPERTIES TIMEOUT 300) add_test( NAME "kwalify:runtime:links" COMMAND ${CMAKE_COMMAND} -DMLT_EXECUTABLE=$ -DKWALIFY_EXECUTABLE=${Kwalify_EXECUTABLE} -DMETASCHEMA_FILE=${CMAKE_SOURCE_DIR}/src/framework/metaschema.yaml -DLIST_QUERY=links -DQUERY_PREFIX=link= -P ${CMAKE_CURRENT_SOURCE_DIR}/kwalify_runtime_query.cmake ) set_tests_properties("kwalify:runtime:links" PROPERTIES TIMEOUT 300) add_test( NAME "kwalify:runtime:consumers" COMMAND ${CMAKE_COMMAND} -DMLT_EXECUTABLE=$ -DKWALIFY_EXECUTABLE=${Kwalify_EXECUTABLE} -DMETASCHEMA_FILE=${CMAKE_SOURCE_DIR}/src/framework/metaschema.yaml -DLIST_QUERY=consumers -DQUERY_PREFIX=consumer= -P ${CMAKE_CURRENT_SOURCE_DIR}/kwalify_runtime_query.cmake ) set_tests_properties("kwalify:runtime:consumers" PROPERTIES TIMEOUT 300) endif() mlt-7.38.0/src/tests/clock16ntsc.pgm000664 000000 000000 00002506021 15172202314 017160 0ustar00rootroot000000 000000 P5 720 480 65535   !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''((((  !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((  !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))**  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))******  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))********  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))******************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))**********************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))****************************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))********************************  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************************************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************************++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************************++++++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************************++++++++++++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))****************************************++++++++++++++++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++++++++++++,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------....  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------........  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------............  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------................  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------....................  !!!!!!!!!!!!!!!!""""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------........................  !!!!!!!!!!!!!!!!""""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------............................  !!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..................................  !!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------......................................  !!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..........................................  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..............................................  !!!!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..............................................////  !!!!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////  !!!!!!!!!!!!!!!!!!""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////  !!!!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////////  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////////////  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..........................................////////////////////////////  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................////////////////////////////////  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................////////////////////////////////////  !!!!!!!!!!!!!!!!""""""""""""""""""################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................////////////////////////////////////////  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................//////////////////////////////////////////////  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................////////////////////////////////////////////////00  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................//////////////////////////////////////////////00000000  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................//////////////////////////////////////////////000000000000  !!!!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////000000000000000000  !!!!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////0000000000000000000000  !!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////00000000000000000000000000  !!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------..........................................////////////////////////////////////////////00000000000000000000000000000000  !!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................//////////////////////////////////////////////000000000000000000000000000000000000  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////000000000000000000000000000000000000000000  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////0000000000000000000000000000000000000000000000  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))****************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////00000000000000000000000000000000000000000000000011  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................////////////////////////////////////////////00000000000000000000000000000000000000000000000000111111  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................////////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111  !!!!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................//////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111111111  !!!!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................//////////////////////////////////////////0000000000000000000000000000000000000000000000001111111111111111111111  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................//////////////////////////////////////////0000000000000000000000000000000000000000000000001111111111111111111111111111  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................//////////////////////////////////////////00000000000000000000000000000000000000000000000011111111111111111111111111111111  !!!!!!!!!!!!!!""""""""""""""""##############$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................//////////////////////////////////////////000000000000000000000000000000000000000000000011111111111111111111111111111111111111  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................//////////////////////////////////////////0000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................////////////////////////////////////////0000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111122  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................////////////////////////////////////////00000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111222222  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))**************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................////////////////////////////////////////000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111222222222222  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------..................................////////////////////////////////////////000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------..................................////////////////////////////////////////0000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222222222  !!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------....................................//////////////////////////////////////0000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222222222222222  !!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------....................................//////////////////////////////////////00000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111112222222222222222222222222222222222  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................////////////////////////////////////////000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////0000000000000000000000000000000000000000001111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................////////////////////////////////////0000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))************************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------................................//////////////////////////////////////00000000000000000000000000000000000000000011111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------................................//////////////////////////////////////000000000000000000000000000000000000000011111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------................................////////////////////////////////////000000000000000000000000000000000000000000111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..................................////////////////////////////////////0000000000000000000000000000000000000000111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333333333333333  !!!!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................////////////////////////////////////0000000000000000000000000000000000000000001111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333  !!!!!!!!!!!!""""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................////////////////////////////////////00000000000000000000000000000000000000001111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333  !!!!!!!!!!!!""""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................////////////////////////////////////000000000000000000000000000000000000001111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................//////////////////////////////////000000000000000000000000000000000000000011111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////////0000000000000000000000000000000000000011111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................//////////////////////////////////0000000000000000000000000000000000000000111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333334444  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................//////////////////////////////////00000000000000000000000000000000000000111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333334444444444  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................//////////////////////////////////000000000000000000000000000000000000001111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333334444444444444444  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////000000000000000000000000000000000000001111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................//////////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444444444  !!!!!!!!!!!!""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................////////////////////////////////0000000000000000000000000000000000000011111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................////////////////////////////////00000000000000000000000000000000000011111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''(((((((((((((((((())))))))))))))))))********************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................////////////////////////////////000000000000000000000000000000000000111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////000000000000000000000000000000000000111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((((())))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444444444  !!!!!!!!!!""""""""""""##########$$$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................//////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444444555555  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................//////////////////////////////00000000000000000000000000000000001111111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444455555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------..........................////////////////////////////////000000000000000000000000000000000011111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444445555555555555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------..........................//////////////////////////////000000000000000000000000000000000011111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444445555555555555555555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................//////////////////////////////0000000000000000000000000000000000111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555  !!!!!!!!!!""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................////////////////////////////0000000000000000000000000000000000111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555  !!!!!!!!""""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................//////////////////////////////00000000000000000000000000000000111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555  !!!!!!!!""""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................//////////////////////////////000000000000000000000000000000001111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))))****************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////000000000000000000000000000000001111111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////0000000000000000000000000000000011111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555556666  !!!!!!!!!!""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------........................////////////////////////////0000000000000000000000000000000011111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555666666666666  !!!!!!!!!!""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------........................////////////////////////////00000000000000000000000000000000111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555566666666666666666666  !!!!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,----------------------........................////////////////////////////000000000000000000000000000000111111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666  !!!!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------..........................//////////////////////////000000000000000000000000000000111111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666  !!!!!!!!!!""""""""##########$$$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................////////////////////////////0000000000000000000000000000001111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666  !!!!!!!!!!""""""""##########$$$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................//////////////////////////0000000000000000000000000000001111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666  !!!!!!!!!!""""""""##########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................//////////////////////////00000000000000000000000000000011111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666  !!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((())))))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................//////////////////////////000000000000000000000000000011111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666  !!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------......................//////////////////////////000000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666  !!!!!!!!""""""""""########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&''''''''''''''(((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------......................//////////////////////////0000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666667777777777  !!!!!!!!""""""""""########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))**************++++++++++++++++++,,,,,,,,,,,,,,,,,,--------------------......................////////////////////////0000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777  !!!!!!!!""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,--------------------......................////////////////////////00000000000000000000000000001111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333334444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777  !!!!!!!!""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,--------------------....................//////////////////////////000000000000000000000000001111111111111111111111111111111122222222222222222222222222222222222233333333333333333333333333333333333333334444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777  !!!!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////000000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333334444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777  !!!!!!!!""""""""########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////0000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333334444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777  !!!!!!!!""""""""########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................//////////////////////0000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777  !!!!!!!!""""""""########$$$$$$$$$$%%%%%%%%&&&&&&&&&&&&''''''''''(((((((((((())))))))))))))**************++++++++++++++,,,,,,,,,,,,,,,,,,------------------....................////////////////////////00000000000000000000000000111111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333333444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777  !!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................////////////////////////000000000000000000000000111111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333333444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777  !!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''(((((((((((())))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////000000000000000000000000001111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888  !!!!!!""""""""########$$$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''''(((((((((())))))))))))))************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////0000000000000000000000001111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888  !!!!!!""""""""########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''(((((((((((())))))))))))**************++++++++++++++,,,,,,,,,,,,,,,,------------------..................//////////////////////0000000000000000000000000011111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888  !!!!!!!!""""""""########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))**************++++++++++++++,,,,,,,,,,,,,,,,------------------..................//////////////////////00000000000000000000000011111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888  !!!!!!!!""""""""########$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''(((((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,,,----------------....................////////////////////00000000000000000000000011111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888  !!!!!!!!""""""""######$$$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,,,----------------....................////////////////////000000000000000000000000111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888  !!!!!!!!""""""########$$$$$$$$%%%%%%%%%%&&&&&&&&''''''''''(((((((((((())))))))))**************++++++++++++++,,,,,,,,,,,,,,----------------..................//////////////////////0000000000000000000000111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888  !!!!!!!!""""""########$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,----------------..................////////////////////0000000000000000000000001111111111111111111111111122222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888  !!!!!!!!""""""########$$$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,----------------..................////////////////////00000000000000000000001111111111111111111111111122222222222222222222222222223333333333333333333333333333333333444444444444444444444444444444444444444455555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888  !!!!!!""""""""########$$$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))************++++++++++++++,,,,,,,,,,,,,,----------------..................//////////////////00000000000000000000001111111111111111111111111122222222222222222222222222223333333333333333333333333333333333444444444444444444444444444444444444444455555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999  !!!!!!""""""""######$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''(((((((((())))))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////000000000000000000000011111111111111111111111122222222222222222222222222223333333333333333333333333333333333444444444444444444444444444444444444445555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999  !!!!!!""""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////0000000000000000000011111111111111111111111122222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444444555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999  !!!!!!""""""########$$$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,,,----------------................//////////////////0000000000000000000000111111111111111111111111222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444444555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999  !!!!!!""""""########$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))**********++++++++++++,,,,,,,,,,,,,,----------------................//////////////////00000000000000000000111111111111111111111111222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444455555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999  !!!!!!""""""########$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,--------------................//////////////////00000000000000000000001111111111111111111111222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444455555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999  !!!!!!""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((())))))))))************++++++++++++,,,,,,,,,,,,--------------................//////////////////000000000000000000001111111111111111111111222222222222222222222222223333333333333333333333333333334444444444444444444444444444444444445555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999  !!!!!!""""""######$$$$$$$$%%%%%%&&&&&&&&''''''''(((((((((())))))))))**********++++++++++++,,,,,,,,,,,,--------------................//////////////////0000000000000000001111111111111111111111112222222222222222222222223333333333333333333333333333334444444444444444444444444444444444555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999  !!!!!!""""""######$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((())))))))))**********++++++++++++,,,,,,,,,,,,--------------................////////////////0000000000000000000011111111111111111111112222222222222222222222223333333333333333333333333333334444444444444444444444444444444444555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999  !!!!!!""""""######$$$$$$%%%%%%%%&&&&&&''''''''(((((((((())))))))************++++++++++,,,,,,,,,,,,--------------................////////////////000000000000000000111111111111111111111122222222222222222222222233333333333333333333333333334444444444444444444444444444444444555555555555555555555555555555555555555566666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::  !!!!!!""""######$$$$$$$$%%%%%%&&&&&&&&''''''''(((((((())))))))))**********++++++++++,,,,,,,,,,,,--------------..............////////////////0000000000000000000011111111111111111111222222222222222222222222333333333333333333333333333344444444444444444444444444444444445555555555555555555555555555555555555566666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::  !!!!""""""######$$$$$$%%%%%%%%&&&&&&&&''''''(((((((((())))))))**********++++++++++,,,,,,,,,,,,--------------..............////////////////00000000000000000011111111111111111111222222222222222222222222333333333333333333333333333344444444444444444444444444444444555555555555555555555555555555555555556666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::  !!!!""""""######$$$$$$%%%%%%%%&&&&&&''''''''(((((((())))))))**********++++++++++++,,,,,,,,,,--------------..............////////////////0000000000000000001111111111111111111122222222222222222222223333333333333333333333333333444444444444444444444444444444555555555555555555555555555555555555556666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::  !!!!""""""######$$$$$$%%%%%%&&&&&&&&''''''(((((((())))))))))**********++++++++++,,,,,,,,,,--------------..............//////////////00000000000000000011111111111111111111222222222222222222222233333333333333333333333333444444444444444444444444444444445555555555555555555555555555555555556666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  !!!!""""""######$$$$$$%%%%%%&&&&&&''''''''(((((((())))))))**********++++++++++,,,,,,,,,,--------------............////////////////000000000000000011111111111111111111222222222222222222222233333333333333333333333333444444444444444444444444444444555555555555555555555555555555555555666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  !!!!""""""####$$$$$$%%%%%%%%&&&&&&''''''(((((((())))))))))********++++++++++,,,,,,,,,,------------..............//////////////0000000000000000001111111111111111112222222222222222222222333333333333333333333333334444444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  !!!!""""""####$$$$$$%%%%%%&&&&&&''''''''(((((((())))))))********++++++++++,,,,,,,,,,------------..............//////////////000000000000000011111111111111111122222222222222222222223333333333333333333333334444444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  !!!!!!""""######$$$$$$%%%%%%&&&&&&''''''(((((((())))))))**********++++++++,,,,,,,,,,------------..............//////////////00000000000000001111111111111111112222222222222222222233333333333333333333333344444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  !!!!!!""""######$$$$$$%%%%%%&&&&&&''''''(((((((())))))))********++++++++,,,,,,,,,,------------............//////////////00000000000000001111111111111111112222222222222222222233333333333333333333333344444444444444444444444444445555555555555555555555555555555566666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;  !!!!!!""""######$$$$%%%%%%&&&&&&''''''''(((((())))))))********++++++++++,,,,,,,,,,----------............//////////////0000000000000011111111111111111122222222222222222222333333333333333333333333444444444444444444444444445555555555555555555555555555555566666666666666666666666666666666666666667777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!!!""""####$$$$$$%%%%%%&&&&&&''''''(((((())))))))********++++++++++,,,,,,,,,,----------............////////////00000000000000001111111111111111222222222222222222223333333333333333333333444444444444444444444444444455555555555555555555555555555566666666666666666666666666666666666666667777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""""####$$$$$$%%%%&&&&&&''''''''(((((())))))))********++++++++,,,,,,,,,,----------............////////////000000000000001111111111111111112222222222222222223333333333333333333333444444444444444444444444445555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""""####$$$$%%%%%%&&&&&&''''''(((((())))))))********++++++++,,,,,,,,,,----------..........//////////////00000000000000111111111111111122222222222222222233333333333333333333334444444444444444444444445555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""######$$$$%%%%%%&&&&&&''''''(((((())))))********++++++++,,,,,,,,,,----------..........////////////000000000000001111111111111111222222222222222222333333333333333333334444444444444444444444444455555555555555555555555555556666666666666666666666666666666666667777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""######$$$$%%%%&&&&&&''''''(((((())))))))******++++++++,,,,,,,,,,--------............////////////00000000000000111111111111112222222222222222223333333333333333333344444444444444444444444455555555555555555555555555555566666666666666666666666666666666667777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""####$$$$$$%%%%&&&&&&''''''(((((())))))******++++++++,,,,,,,,,,--------............//////////00000000000000111111111111112222222222222222223333333333333333333344444444444444444444444455555555555555555555555555556666666666666666666666666666666666777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""####$$$$%%%%%%&&&&''''''(((((())))))********++++++++,,,,,,,,--------..........////////////0000000000001111111111111111222222222222222233333333333333333333444444444444444444444455555555555555555555555555556666666666666666666666666666666677777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""####$$$$%%%%&&&&&&''''''(((())))))))******++++++++,,,,,,,,--------..........////////////000000000000111111111111112222222222222222333333333333333333444444444444444444444455555555555555555555555555556666666666666666666666666666666677777777777777777777777777777777777777778888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<  !!!!""""####$$$$%%%%&&&&&&''''(((((())))))******++++++++,,,,,,,,--------..........//////////000000000000111111111111112222222222222222333333333333333333444444444444444444444455555555555555555555555555666666666666666666666666666666777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!!""""##$$$$$$%%%%&&&&''''''(((())))))******++++++++,,,,,,,,--------..........//////////00000000000011111111111122222222222222223333333333333333334444444444444444444455555555555555555555555555666666666666666666666666666666777777777777777777777777777777777777778888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!!""####$$$$%%%%&&&&&&''''(((((())))))******++++++,,,,,,,,--------........//////////000000000000111111111111112222222222222233333333333333333344444444444444444444555555555555555555555555666666666666666666666666666677777777777777777777777777777777777777888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""####$$$$%%%%&&&&''''''(((())))))******++++++,,,,,,,,--------........//////////00000000001111111111111122222222222222333333333333333344444444444444444444555555555555555555555555666666666666666666666666666677777777777777777777777777777777777788888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""####$$$$%%%%&&&&''''(((((())))******++++++,,,,,,,,--------........//////////000000000011111111111122222222222222333333333333333344444444444444444455555555555555555555555566666666666666666666666666777777777777777777777777777777777777888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""####$$%%%%&&&&''''''(((())))))****++++++,,,,,,,,--------........////////0000000000111111111111222222222222223333333333333333444444444444444444555555555555555555555566666666666666666666666666777777777777777777777777777777777788888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""##$$$$%%%%&&&&''''(((())))))******++++++,,,,,,------........//////////000000000011111111112222222222222233333333333333444444444444444444555555555555555555555566666666666666666666666666777777777777777777777777777777778888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""##$$$$%%%%&&&&''''(((())))******++++++,,,,,,------........////////000000000011111111111122222222222233333333333333444444444444444444555555555555555555556666666666666666666666667777777777777777777777777777777788888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""####$$$$%%&&&&''''(((())))))****++++++,,,,,,------........////////00000000001111111111222222222222333333333333334444444444444444555555555555555555556666666666666666666666667777777777777777777777777777778888888888888888888888888888888888888899999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================  !!""####$$%%%%&&&&''''(((())))****++++++,,,,,,------........////////0000000011111111112222222222223333333333333344444444444444555555555555555555556666666666666666666666777777777777777777777777777777888888888888888888888888888888888888999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<============================================  !!""####$$%%%%&&''''(((())))******++++,,,,,,------......////////0000000011111111112222222222223333333333334444444444444444555555555555555555666666666666666666666677777777777777777777777777778888888888888888888888888888888888999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================  !!""##$$$$%%&&&&''''(((())))****++++,,,,,,------......////////000000001111111122222222222233333333333344444444444444555555555555555555666666666666666666667777777777777777777777777777888888888888888888888888888888889999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================================  !!!!""##$$$$%%&&&&''(((())))****++++,,,,,,------......//////0000000011111111112222222222333333333333444444444444445555555555555555666666666666666666667777777777777777777777777788888888888888888888888888888888999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================  !!!!""##$$%%%%&&''''(())))****++++++,,,,------......//////000000001111111122222222223333333333444444444444445555555555555555666666666666666666667777777777777777777777778888888888888888888888888888889999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================================  !!""""##$$%%&&&&''(((())))****++++,,,,------....////////0000001111111122222222223333333333444444444444555555555555555566666666666666666677777777777777777777777788888888888888888888888888889999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================================================================================================================================  !!""####$$%%&&''''(())))****++++,,,,----......//////00000000111111222222222233333333334444444444445555555555555566666666666666666677777777777777777777778888888888888888888888888899999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================================================================================================  !!""##$$%%%%&&''(((())****++++,,,,----......//////0000001111111122222222333333333344444444445555555555555566666666666666667777777777777777777777888888888888888888888888889999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================================================================================================================================  !!""##$$%%&&&&''(())))**++++,,,,----......////0000001111111122222222333333334444444444445555555555556666666666666666777777777777777777778888888888888888888888889999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%&&''(((())****++,,,,----....//////000000111111222222223333333344444444445555555555556666666666666677777777777777777777888888888888888888888899999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%&&''(())****++,,,,----....//////00001111112222222233333333444444445555555555556666666666666677777777777777777788888888888888888888889999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""$$%%&&''(())))**++++,,----....////000000111122222222333333444444444455555555556666666666666677777777777777778888888888888888888899999999999999999999999999::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!##$$%%&&''(())**++++,,----....////0000111111222222333333444444445555555555666666666666777777777777777788888888888888888899999999999999999999999999::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!##$$%%''(())****++,,----..////00001111112222223333334444444455555555666666666666777777777777778888888888888888999999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!##$$&&''(())**++,,----..////0000111122222233333344444455555555666666666677777777777777888888888888888899999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  ""##%%&&(())**++,,----..////00111122222233334444445555555566666666667777777777778888888888888899999999999999999999::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  ""$$%%''((**++,,--....//000011112222333344444455555566666666777777777777888888888888999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  ""$$&&(())**,,--..////00111122223333444455555566666666777777777788888888889999999999999999::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<============================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????  ""%%''))**++--..//000011222233444444555566666666777777778888888888999999999999::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  ##%%((**++--..//0011222233444455556666667777777788888888999999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ##&&))++--..//112222334455556666667777778888889999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  $$((**--//0011223344556666777777888899999999::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  %%**--//11224455666677778888999999::::::::;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<========================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ((--002244667777889999::::::;;;;;;;;<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? --22667799::::;;;;<<<<<<==============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@__RRMMIIHHFFEEEEDDDDCCCCCCBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ll__WWRROOMMKKIIHHHHGGFFFFEEEEEEDDDDDDDDCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@rrgg__ZZUURRPPNNMMKKJJIIIIHHHHGGGGFFFFFFEEEEEEEEDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@uullee__[[WWUURRPPOONNMMLLKKJJIIIIHHHHHHGGGGFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@wwppiidd__\\YYVVTTRRQQPPNNMMMMLLKKKKJJIIIIIIHHHHHHGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@yyrrllggcc__\\ZZWWUUTTRRQQPPOONNMMMMLLKKKKJJJJIIIIIIHHHHHHHHGGGGGGGGFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@yyttoojjffbb__]]ZZXXWWUUTTRRQQPPOOOONNMMMMLLKKKKKKJJJJIIIIIIIIHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@zzuuqqlliieebb__]][[YYWWVVUUSSRRQQPPPPOONNNNMMMMLLLLKKKKJJJJJJIIIIIIIIHHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@{{vvrrnnkkggddbb__]][[ZZXXWWUUTTSSRRQQQQPPOOOONNNNMMMMLLLLKKKKKKJJJJJJIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA{{wwsspplliiffddbb__]]\\ZZYYWWVVUUTTSSRRRRQQPPPPOONNNNMMMMMMLLLLKKKKKKKKJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA||xxttqqnnkkhhffccaa__^^\\[[YYXXWWVVUUTTSSRRRRQQPPPPOOOONNNNMMMMMMLLLLLLKKKKKKJJJJJJJJIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA||yyuurroolljjggeeccaa__^^\\[[ZZXXWWVVUUUUTTSSRRRRQQPPPPOOOONNNNNNMMMMMMLLLLLLKKKKKKKKJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA||yyvvssppnnkkiiggeeccaa__^^\\[[ZZYYXXWWVVUUTTTTSSRRRRQQQQPPPPOOOONNNNNNMMMMMMLLLLLLKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA||yywwttqqoolljjhhffddbbaa__^^]][[ZZYYXXWWWWVVUUTTTTSSRRRRQQQQPPPPOOOOOONNNNMMMMMMMMLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}}zzwwuurrppnnkkiiggffddbbaa__^^]]\\[[ZZYYXXWWVVUUUUTTSSSSRRRRQQQQPPPPPPOOOONNNNNNMMMMMMMMLLLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}}zzxxuussqqoolljjiiggeeddbbaa__^^]]\\[[ZZYYXXWWWWVVUUUUTTSSSSRRRRQQQQPPPPPPOOOOOONNNNNNMMMMMMMMLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}}{{xxvvttqqoommkkjjhhffeeccbbaa__^^]]\\[[ZZYYYYXXWWVVVVUUTTTTSSSSRRRRQQQQQQPPPPOOOOOONNNNNNNNMMMMMMMMLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAA}}{{yyvvttrrppnnllkkiiggffddccbbaa__^^]]\\[[ZZZZYYXXWWWWVVUUUUTTTTSSSSRRRRQQQQQQPPPPPPOOOOOONNNNNNNNMMMMMMMMLLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB}}{{yywwuussqqoommlljjhhggeeddccbb``__^^]]\\\\[[ZZYYXXXXWWVVVVUUUUTTTTSSSSRRRRRRQQQQPPPPPPOOOOOOOONNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB}}{{yywwuussrrppnnllkkiihhffeeddccbb``__^^]]]]\\[[ZZYYYYXXWWWWVVVVUUUUTTTTSSSSRRRRRRQQQQPPPPPPPPOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB}}{{yyxxvvttrrppoommlljjiiggffeeddbbaa``__^^^^]]\\[[ZZZZYYXXXXWWWWVVUUUUTTTTTTSSSSRRRRRRQQQQQQPPPPPPOOOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB}}||zzxxvvttssqqoonnllkkjjhhggffeeccbbaa``__^^^^]]\\[[[[ZZYYYYXXWWWWVVVVUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB}}||zzxxwwuussrrppoommlljjiihhggeeddccbbaa``____^^]]\\[[[[ZZYYYYXXXXWWWWVVVVUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~||zzyywwuuttrrqqoonnllkkjjiiggffeeddccbbaa``____^^]]\\\\[[ZZZZYYXXXXWWWWVVVVUUUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~||zzyywwvvttssqqppnnmmllkkiihhggffeeddccbbaa``____^^]]\\\\[[ZZZZYYYYXXXXWWWWVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~||{{yyxxvvuussrrppoonnllkkjjiihhggffeeddccbbaa``____^^]]\\\\[[[[ZZYYYYXXXXWWWWVVVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBB~~||{{yyxxvvuuttrrqqppnnmmllkkjjhhggffeeddddccbbaa``____^^]]]]\\[[[[ZZZZYYYYXXXXWWWWVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~||{{yyxxwwuuttssqqppoonnllkkjjiihhggffeeddccbbbbaa``____^^]]]]\\[[[[ZZZZYYYYXXXXWWWWWWVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~||{{zzxxwwvvttssrrqqoonnmmllkkjjiihhggffeeddccbbbbaa``____^^]]]]\\\\[[ZZZZYYYYXXXXXXWWWWVVVVVVUUUUTTTTTTTTSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}{{zzyywwvvuussrrqqppoonnllkkjjiihhggffffeeddccbbbbaa``____^^]]]]\\\\[[[[ZZZZYYYYXXXXWWWWWWVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}{{zzyywwvvuuttssqqppoonnmmllkkjjiihhggffeeeeddccbbaaaa``____^^^^]]\\\\[[[[ZZZZYYYYXXXXXXWWWWVVVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}{{zzyyxxwwuuttssrrqqppoommllkkjjjjiihhggffeeddddccbbaaaa``____^^^^]]\\\\[[[[ZZZZYYYYYYXXXXWWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||zzyyxxwwvvttssrrqqppoonnmmllkkjjiihhggggffeeddccccbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYXXXXXXWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{yyxxwwvvuuttssqqppoonnmmllkkkkjjiihhggffffeeddccccbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYYYXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{yyxxwwvvuuttssrrqqppoonnmmllkkjjiihhhhggffeeeeddccbbbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCC~~}}||{{zzyywwvvuuttssrrqqppoonnmmllllkkjjiihhggggffeeddddccbbbbaaaa``____^^^^]]]]\\\\[[[[ZZZZZZYYYYXXXXXXWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmllkkjjiiiihhggffffeeddddccbbbbaaaa``____^^^^]]]]\\\\[[[[[[ZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUUUTTTTTTSSSSSSSSSSRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmllllkkjjiihhhhggffeeeeddccccbbbbaa````____^^^^]]]]\\\\\\[[[[ZZZZYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvuuttssrrqqppoooonnmmllkkjjjjiihhggggffeeeeddccccbbbbaa````____^^^^]]]]\\\\\\[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvuuttssssrrqqppoonnmmllllkkjjiiiihhggffffeeddddccccbbbbaa````____^^^^]]]]]]\\\\[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvvvuuttssrrqqppoonnnnmmllkkkkjjiihhhhggffffeeddddccccbbaaaa````____^^^^]]]]]]\\\\[[[[[[ZZZZZZYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyyyxxwwvvuuttssrrqqppppoonnmmllllkkjjiiiihhggggffeeeeddddccbbbbaaaa````____^^^^^^]]]]\\\\[[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWWVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{{{zzyyxxwwvvuuttssrrrrqqppoonnnnmmllkkkkjjiihhhhggggffeeeeddddccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDD~~}}||||{{zzyyxxwwvvuuttttssrrqqppoooonnmmllllkkjjjjiihhhhggffffeeeeddccccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDD~~}}}}||{{zzyyxxwwvvvvuuttssrrqqqqppoonnnnmmllkkkkjjiiiihhggggffffeeddddccccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~}}}}||{{zzyyxxwwwwvvuuttssrrrrqqppoooonnmmllllkkjjjjiihhhhggggffeeeeddddccccbbbbaaaa````______^^^^]]]]]]\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~~}}||{{zzyyxxxxwwvvuuttttssrrqqppppoonnmmmmllkkkkjjiiiihhhhggffffeeeeddddccccbbbbaaaa````______^^^^]]]]]]\\\\[[[[[[ZZZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~~}}||{{zzyyyyxxwwvvuuuuttssrrqqqqppoooonnmmllllkkjjjjiiiihhggggffffeeeeddddccccbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~~}}||{{zzyyyyxxwwvvvvuuttssrrrrqqppppoonnmmmmllkkkkjjjjiihhhhggggffffeeddddccccbbbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXWWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~~}}||{{zzzzyyxxwwvvvvuuttssssrrqqqqppoonnnnmmllllkkkkjjiiiihhhhggffffeeeeddddccccbbbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~~}}||{{{{zzyyxxwwwwvvuuttttssrrqqqqppoooonnmmmmllkkkkjjjjiiiihhggggffffeeeeddddccccbbbbaaaaaa````______^^^^]]]]]]\\\\\\[[[[[[[[ZZZZZZYYYYYYYYXXXXXXWWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~~}}||{{{{zzyyxxxxwwvvuuuuttssrrrrqqppppoonnnnmmllllkkkkjjiiiihhhhggggffffeeeeddddccccbbbbaaaaaa````______^^^^^^]]]]\\\\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEE~~~~}}||{{{{zzyyxxxxwwvvuuuuttssssrrqqqqppoooonnmmmmllllkkjjjjiiiihhhhggggffffeeeeddddccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEE~~}}||||{{zzyyyyxxwwvvvvuuttttssrrrrqqppppoonnnnmmllllkkkkjjjjiihhhhggggffffeeeeddddddccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}||||{{zzyyyyxxwwwwvvuuttttssrrrrqqppppoooonnmmmmllllkkjjjjiiiihhhhggggffffeeeeddddccccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[[[ZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}||||{{zzyyyyxxwwwwvvuuuuttssssrrqqqqppoooonnnnmmllllkkkkjjjjiiiihhhhggggffffeeeeddddccccbbbbbbaaaa``````______^^^^^^]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}||||{{zzzzyyxxxxwwvvuuuuttssssrrrrqqppppoonnnnmmmmllllkkjjjjiiiihhhhggggffffeeeeeeddddccccbbbbbbaaaa``````______^^^^^^]]]]]]\\\\\\\\[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}||||{{zzzzyyxxxxwwvvvvuuttttssrrrrqqqqppoooonnnnmmllllkkkkjjjjiiiihhhhggggffffeeeeddddddccccbbbbbbaaaa``````______^^^^^^]]]]]]\\\\\\\\[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}}}||{{zzzzyyxxxxwwvvvvuuuuttssssrrqqqqppppoonnnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeeddddddccccbbbbbbaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}}}||{{{{zzyyyyxxwwwwvvuuuuttssssrrrrqqppppoooonnnnmmllllkkkkjjjjiiiihhhhggggffffffeeeeddddccccccbbbbbbaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}}}||{{{{zzyyyyxxwwwwvvuuuuttttssrrrrqqqqppoooonnnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeeeeddddccccccbbbbaaaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFF~~}}}}||{{{{zzyyyyxxwwwwvvvvuuttttssssrrqqqqppppoooonnmmmmllllkkkkjjjjiiiihhhhhhggggffffeeeeeeddddccccccbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFF~~}}}}||{{{{zzyyyyxxxxwwvvvvuuuuttssssrrrrqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhggggggffffeeeeddddddccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||{{{{zzzzyyxxxxwwwwvvuuuuttttssrrrrqqqqppppoooonnmmmmllllkkkkjjjjjjiiiihhhhggggffffffeeeeddddddccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||||{{zzzzyyxxxxwwwwvvuuuuttttssssrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhhhggggffffeeeeeeddddccccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||||{{zzzzyyyyxxwwwwvvvvuuttttssssrrrrqqqqppoooonnnnmmmmllllkkkkkkjjjjiiiihhhhggggggffffeeeeeeddddccccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||||{{zzzzyyyyxxwwwwvvvvuuuuttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiiihhhhggggffffffeeeeddddddccccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||||{{{{zzyyyyxxxxwwvvvvuuuuttttssssrrqqqqppppoooonnnnmmmmllllkkkkkkjjjjiiiihhhhhhggggffffffeeeeddddddccccccbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||||{{{{zzyyyyxxxxwwwwvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiiihhhhggggggffffeeeeeeddddddccccccbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~~~}}||||{{{{zzyyyyxxxxwwwwvvvvuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiihhhhhhggggffffffeeeeeeddddddccccbbbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGG~~~~}}||||{{{{zzzzyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjjjiiiihhhhhhggggffffffeeeeeeddddccccccbbbbbbaaaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGG~~~~}}||||{{{{zzzzyyyyxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiiiihhhhggggggffffffeeeeddddddccccccbbbbbbaaaaaaaa``````________^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}||||{{{{zzzzyyyyxxxxwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnnmmmmllllkkkkjjjjjjiiiihhhhhhggggggffffeeeeeeddddddccccccbbbbbbaaaaaaaa``````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}}}||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiiiihhhhhhggggffffffeeeeeeddddddccccccbbbbbbaaaaaaaa``````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}}}||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnnmmmmllllkkkkkkjjjjiiiiiihhhhggggggffffffeeeeeeddddddccccccbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}}}||||{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppppoooonnnnmmmmllllllkkkkjjjjjjiiiihhhhhhggggggffffeeeeeeddddddccccccccbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}}}||||{{{{zzyyyyxxxxwwwwvvvvuuuuttttssssssrrrrqqqqppppoooonnnnnnmmmmllllkkkkkkjjjjiiiiiihhhhhhggggffffffeeeeeeddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooooonnnnmmmmllllllkkkkjjjjjjiiiihhhhhhggggggffffffeeeeeeddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqqqppppoooonnnnmmmmmmllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHH~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssssrrrrqqqqppppoooooonnnnmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHH~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttttssssrrrrqqqqppppppoooonnnnmmmmmmllllkkkkkkjjjjjjiiiihhhhhhggggggffffffeeeeeeddddddddccccccbbbbbbaaaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvvuuuuttttssssrrrrqqqqqqppppoooonnnnnnmmmmllllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddccccccccbbbbbbaaaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuuttttssssrrrrrrqqqqppppoooooonnnnmmmmmmllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddccccccccbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvuuuuttttttssssrrrrqqqqppppppoooonnnnnnmmmmllllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeeeddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||{{{{{{zzzzyyyyxxxxwwwwvvvvuuuuuuttttssssrrrrqqqqqqppppoooooonnnnmmmmmmllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||{{{{{{zzzzyyyyxxxxwwwwvvvvvvuuuuttttssssrrrrrrqqqqppppppoooonnnnnnmmmmllllllkkkkkkjjjjjjiiiihhhhhhhhggggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||||{{{{zzzzyyyyxxxxwwwwwwvvvvuuuuttttssssssrrrrqqqqqqppppoooooonnnnmmmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggggffffffffeeeeeeddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuuttttttssssrrrrrrqqqqppppoooooonnnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeeeddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIII~~~~}}}}}}||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuuuuttttssssrrrrrrqqqqppppppoooonnnnnnmmmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIII~~~~}}}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvvvuuuuttttssssssrrrrqqqqqqppppoooooonnnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJII~~~~}}}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqppppppoooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeeeddddddddccccccbbbbbbbbaaaaaaaaaa````````__________^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~~~}}}}}}||||{{{{zzzzzzyyyyxxxxwwwwwwvvvvuuuuttttttssssrrrrrrqqqqqqppppoooooonnnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeeeddddddccccccccbbbbbbbbaaaaaaaaaa````````__________^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~~~~~}}}}||||{{{{zzzzzzyyyyxxxxwwwwwwvvvvuuuuuuttttssssssrrrrqqqqqqppppppoooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffffeeeeeeeeddddddccccccccbbbbbbbbaaaaaaaaaa````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~~~~~}}}}||||{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqppppppoooooonnnnmmmmmmllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggffffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~~~~~}}}}||||{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqqqppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~~~~~}}}}||||{{{{{{zzzzyyyyyyxxxxwwwwwwvvvvuuuuuuttttssssssrrrrqqqqqqppppppoooooonnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~~~~~}}}}||||||{{{{zzzzyyyyyyxxxxwwwwwwvvvvuuuuuuttttssssssrrrrrrqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhhhggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJ~~~~~~}}}}||||||{{{{zzzzyyyyyyxxxxwwwwwwvvvvvvuuuuttttttssssrrrrrrqqqqqqppppppoooonnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhggggggggffffffffeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJ~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssssrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJ~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppoooooonnnnnnmmmmmmllllllkkkkkkkkjjjjjjiiiiiihhhhhhhhggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}||||||{{{{{{zzzzyyyyyyxxxxwwwwwwvvvvvvuuuuttttttssssssrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxwwwwwwvvvvvvuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqppppppoooooonnnnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}}}||||{{{{{{zzzzzzyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}}}||||{{{{{{zzzzzzyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppppoooooonnnnnnnnmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKK~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKK~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKK~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbaaaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddddccccccccbbbbbbbbbbaaaaaaaaaa````````````__________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~~~~}}}}}}||||||{{{{{{zzzzzzyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLL~~~~~~}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqqqppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLL~~~~~~}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLL~~~~~~}}}}}}||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLL~~~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLL~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttttssssssrrrrrrqqqqqqqqppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrrrqqqqqqppppppoooooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvvvuuuuuuttttttssssssrrrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeddddddddddccccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxwwwwwwvvvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMM~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrrrqqqqqqqqppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}}}||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNN~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNN~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNN~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONN~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOO~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOO~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOO~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRR~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRR~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRR~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSS~~~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSS~~~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````______________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTT~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUU~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUU~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUU~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVV~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWmlt-7.38.0/src/tests/clock16pal.pgm000664 000000 000000 00003124021 15172202314 016762 0ustar00rootroot000000 000000 P5 720 576 65535   !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''(((( $  !!!!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""""###########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((>??@AABCDEFGIKLNPQSTUVZ_`  !!!!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""""###########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''''((((((((qqrsstuvwxyz{}jo s vwxy{|}~ !!!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""""###########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((إ٦٦ڧۨܩܩݪޫ߬5 < = AFKLMNPQR S!!!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""""U"U#########################V#V$$$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''''''Z((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((  #%  !!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""""###########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''''((((((((((((((((((((==>>??@@ABCDEFGHIKMPRUVXc  !!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""""###########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((ppqrstuvvwxz{|}hmn p q tvz|}~ !!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""""###########################$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''((((((((((((((((((((((((((פإ٦ڧۨܩݪޫ߬8 > ADEGIKLMNOPQR S!!!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""""U#########################V#V$$$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((  !#  !!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""#########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((==>>??@ABCDEFGHIJLPVZ[\a  !!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""###########################$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((pqrsstuvwwxyz{|}~n s tvy|}~ !!!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""###########################$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())פפإإ٦ڧۨۨܩݪޫ߬69 ACDFGIJKLOPQR S!!!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""""U#########################V#V$$$$$$$$$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((((((((([))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((()))))))))))) !$%&'(  !!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""""#########################$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))=>?@ABCDEFGHIJKLMNPQRSTW]b  !!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""""#########################$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((())))))))))))))))ooppqrrsttuvwxyz|}~n vxy{| !!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))֣פإإ٦ڧۨܩݪޫ߬57 = > ? @CFGIJKLOPR S!!!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""U#########################V$$$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((((((([))))))))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))) !(  !!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""#########################$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))<==>?@AABCDEFGHIJKLMNPQRTWXY]_a  !!!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""#########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))ooppqqrrsstuvwxyz{|}~gln q vwz{|~ !!!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))֣֣פإ٦ڧۨܩݪݪޫ߬6 < DEIKLMNPQR S!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""U"U#######################V$$$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((((((([)))))))))))))))))))))))))))))))))))))\  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))**  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))******  !&  !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""#########################$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))))******<==>>?@AABCDEFGHJKLMNS[]  !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""#########################$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))********oopqrrstuvwwxyz{|}kn p s tvwy{|~ !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************֣֣פפإ٦ڧڧۨܩݪޫ߬߬ > DFIJKMNOPQR S!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""U#######################V$$$$$$$$$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))))))))\****************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))******************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))**********************  $'-  !!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""#######################$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**********************;;<=>>?@ABBCCDEFHIJMRY]  !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))**************************nnooppqrrsstuvwxyz{}~n tuvwxz|} !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""#######################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************բբ֣֣פإإ٦ڧۨܩݪޫ߬5 = ? EFGJKLMNOPQR S!!!!!!!!!!!!!!!!!!!T!T"""""""""""""""""""""U#######################V$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))))))\)\********************************  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************************************  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************************************** #*  !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""""#######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))*************************************;;<<==>?@ABBCDEFGHIJKLNQTUXZ]  !!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++nopqrstuvwxyz{|}~m q uyz|}~ !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""#######################$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))**************************************++++++բբ֣֣פפإإ٦٦ڧڧۨۨܩݪޫ߬:; > @CDGKLMNOPQR S!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""U#######################V$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))))))\)\*************************************]++++++++++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************************++++++++++++  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))****************************************++++++++++++++++ #%*+,-.  !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""#######################$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))**************************************++++++++++++++++:;<<==>?@@ABCDDEFGHIJKMNOQRSTU\]  !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++mmnnopqqrsttuuvwxyz|}~ s u{|}~ !!!!!!!!!!!!!!!!!!!!!"""""""""""""""""""""#####################$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))*************************************++++++++++++++++++++++++ԡԡբբ֣֣פפإ٦٦ڧۨܩݪޫ߬89:; < CDEFGJKMNOPQR S!!!!!!!!!!!!!!!!!!!T!T"""""""""""""""""""U"U#####################V$$$$$$$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))))))\)\***********************************]*]++++++++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++++++  $  !!!!!!!!!!!!!!!!!!!"""""""""""""""""""""#######################$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++:;;<<=>>??@AABCDEFGHIJKLMNOPUWZ[\]^_d  !!!!!!!!!!!!!!!!!!!"""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++mmnnoppqrstuuvvwxyz{|} uxyz{|} !!!!!!!!!!!!!!!!!!!"""""""""""""""""""""#####################$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))***********************************++++++++++++++++++++++++++++++++++++++++ԡԡբ֣֣פإ٦٦ڧڧۨܩݪޫ߬5:; < = > CIJKLMNOPQR S!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""U#####################V$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((([)))))))))))))))))))))))))))))))))\***********************************]*]+++++++++++++++++++++++++++++++++++++^+^,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,  %&'()  !!!!!!!!!!!!!!!!!!!"""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,:;<<==>>??@@ABCDEFGHJLMOPQRSUWX^  !!!!!!!!!!!!!!!!!!!"""""""""""""""""""#####################$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))***********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,llmmnnopqrstuvwxyz{|}~ko p s twz{~ !!!!!!!!!!!!!!!!!!!"""""""""""""""""""#####################$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************+++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,ӠӠԡԡբբ֣֣פפإإ٦ڧۨܩܩݪޫ߬: ADEHIKLNOPQR S!!!!!!!!!!!!!!!!!T!T"""""""""""""""""""U#####################V$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((([)))))))))))))))))))))))))))))))\)\*********************************]*]+++++++++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, !"),  !!!!!!!!!!!!!!!!!"""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))*********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,99:;;<<==>??@ABCDEFGHIJMOPQRTX  !!!!!!!!!!!!!!!!!""""""""""""""""""""###################$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,llmmnnopqrstuvwxy{}~lo wy~ !!!!!!!!!!!!!!!!!"""""""""""""""""""#####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((()))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ӠӠԡԡբ֣֣פפإإ٦ڧڧۨۨܩܩݪޫ߬: > ? @ABDFGHIJKNPQR S!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""U#####################V$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''Z((((((((((((((((((((((((((((([)))))))))))))))))))))))))))))))\)\*******************************]*]+++++++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,-------- !"%&)/  !!!!!!!!!!!!!!!!!!!"""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------99::;<<=>?@@ABCDEFGHIJKLNOPST[  !!!!!!!!!!!!!!!!!!!"""""""""""""""""""###################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------llmnnooppqqrrstuvwxyz{|}~i q r xz !!!!!!!!!!!!!!!!!!!""""""""""""""""""###################$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''((((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------ҟӠӠԡԡբբ֣פإ٦ڧۨۨܩݪݪޫ߬ @ABEGHIJKLMNOPQR S!!!!!!!!!!!!!!!!!!!T"""""""""""""""""U#####################V$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''Z((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))\*********************************]+++++++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_--------------------  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------  !"#$').  !!!!!!!!!!!!!!!!!"""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------8889::;;<<==>>?@ABCDEFGHIJKMNOPRSY_  !!!!!!!!!!!!!!!!!"""""""""""""""""""###################$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((()))))))))))))))))))))))))))))********************************+++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------kkllmnnoopqrrssttuvwxyz|}~jm s yz{}~ !!!!!!!!!!!!!!!!!"""""""""""""""""""###################$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''((((((((((((((((((((((((((())))))))))))))))))))))))))))))*******************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------ҟҟӠӠԡԡբ֣פפإ٦ڧۨܩݪޫ߬ < > BCDGHKLMNOPQR S!!!!!!!!!!!!!!!!!T"""""""""""""""""""U###################V$$$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''Z'Z((((((((((((((((((((((((([([)))))))))))))))))))))))))))\)\*******************************]*]+++++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_--------------------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------.... "#)  !!!!!!!!!!!!!!!!!"""""""""""""""""###################$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------....8899::;;<<=>>?@@AABCCDEFGHIJKLMNOQXZ\`  !!!!!!!!!!!!!!!!!"""""""""""""""""###################$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))*******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------........kkllmnooppqrrstuvwxyz{|} p r t{}~ !!!!!!!!!!!!!!!!!"""""""""""""""""###################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''((((((((((((((((((((((((()))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............ҟҟӠӠԡԡբբ֣פפإإ٦ڧۨܩݪޫ߬9 ? BCDEFHJKMNOPR S!!!!!!!!!!!!!!!!!T"""""""""""""""""U###################V$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''Z((((((((((((((((((((((((([([)))))))))))))))))))))))))))\)\*****************************]*]+++++++++++++++++++++++++++++++++^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------------------`-`................  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------....................  !!!!!!!!!!!!!!!!""""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------........................ ")  !!!!!!!!!!!!!!!"""""""""""""""""""#################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''(((((((((((((((((((((((((()))))))))))))))))))))))))))*******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------........................778899::;;<=>?@ABCDEFGHIJLMOQRSTWY\  !!!!!!!!!!!!!!!""""""""""""""""""#################$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,---------------------------------------............................jjkkllmnnooppqqrsstuvwxyz{|}~hij q wxyz{} !!!!!!!!!!!!!!!"""""""""""""""""###################$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..................................ўўҟҟӠӠԡԡբ֣פإ٦٦ڧۨܩݪޫ߬78 > @BHJKLMOPQR S!!!!!!!!!!!!!!!T"""""""""""""""""U###################V$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''Z'Z((((((((((((((((((((((([([)))))))))))))))))))))))))))\*****************************]*]+++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-------------------------------------`-`......................................  !!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..........................................  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..............................................  "$()1  !!!!!!!!!!!!!!!"""""""""""""""""#################$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))******************************+++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................778899::;;<==>?@ABCDEEFGHJKLMNOUWZ[  !!!!!!!!!!!!!!!!!"""""""""""""""""#################$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''((((((((((((((((((((((((()))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------...........................................////jjkkllmnoopqqrstuvwxyz{|}~ r s uw} !!!!!!!!!!!!!!!!!""""""""""""""""#################$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................//////////ўўҟҟӠӠԡԡբբ֣פפإ٦٦ڧۨۨܩݪޫޫ߬ @BDEFGHIJKLMOPQ S!!!!!!!!!!!!!!!!!T"""""""""""""""U#################V#V$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''Z((((((((((((((((((((((((([)))))))))))))))))))))))))\)\*****************************]+++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-------------------------------------`-`.........................................a.a//////////////  !!!!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////////  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////////////  "%&'*+  !!!!!!!!!!!!!!!"""""""""""""""""#################$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------.........................................//////////////////////66778899::;;<=>>??@ABBCDEFGHIJKLNORSUX`  !!!!!!!!!!!!!!!"""""""""""""""""#################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,-------------------------------------........................................////////////////////////////iijjkkllmmnnooppqrssttuvvwxyz{}~ uy}~ !!!!!!!!!!!!!!!"""""""""""""""""#################$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''(((((((((((((((((((((((()))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------..........................................////////////////////////////////НННўўҟҟӠӠԡբբ֣֣פإ٦٦ڧۨܩݪޫ߬6 < = > BDFGHIKMOPQR S!!!!!!!!!!!!!!!T"""""""""""""""""U###############V#V$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''Z'Z((((((((((((((((((((((([)))))))))))))))))))))))))\)\***************************]+++++++++++++++++++++++++++++++^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-----------------------------------`-`.........................................a.a////////////////////////////////////  !!!!!!!!!!!!!!!!""""""""""""""""""################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................////////////////////////////////////////  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................////////////////////////////////////////////// "*.  !!!!!!!!!!!!!!!"""""""""""""""#################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''((((((((((((((((((((((()))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................../////////////////////////////////////////////666778889::;;<=>>??@AABBCDEFGIJKMNOPQU_  !!!!!!!!!!!!!!!"""""""""""""""#################$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................../////////////////////////////////////////////00iijjkkllmmnooppqrstuvwxyz{|}il p q r s tuxz{~ !!!!!!!!!!!!!!!"""""""""""""""#################$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((()))))))))))))))))))))))))***************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////00000000ННўўҟҟӠӠԡբբ֣֣פإإ٦ڧڧۨܩݪޫ߬; BCEGHIJKLNPQR S!!!!!!!!!!!!!!!T"""""""""""""""U###############V#V$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''Z((((((((((((((((((((((([)))))))))))))))))))))))))\)\*************************]*]+++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-----------------------------------`-`.....................................a.a.a/////////////////////////////////////////b/b/b000000000000  !!!!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////000000000000000000  !!!!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////0000000000000000000000 !"#$%&  !!!!!!!!!!!!!!""""""""""""""###############$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''((((((((((((((((((((((()))))))))))))))))))))))))**************************+++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................////////////////////////////////////////////000000000000000000000055667778899::;<==>??@ABCDEFHIJLMPQT[^c  !!!!!!!!!!!!!"""""""""""""""#################$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))*************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,-----------------------------------......................................///////////////////////////////////////////00000000000000000000000000hhhiiijjkkllmmnnoopqqrsstuuvwxyz{|}~ p uy{ !!!!!!!!!!!!!"""""""""""""""#################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''((((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------.......................................//////////////////////////////////////////00000000000000000000000000000000ϜϜННўўҟҟӠӠԡբբ֣֣פפإ٦ڧۨܩݪޫޫ߬; > ? @BCDFHIJKLNOPQR S!!!!!!!!!!!!!T"""""""""""""""U###############V#V$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''Z'Z((((((((((((((((((((([([)))))))))))))))))))))))\*************************]*]+++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------------`-`.....................................a.a/////////////////////////////////////////b/b/b000000000000000000000000000000000000  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////000000000000000000000000000000000000000000  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////0000000000000000000000000000000000000000000000  !"#'  !!!!!!!!!!!!!"""""""""""""""###############$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&''''''''''''''''''''((((((((((((((((((((())))))))))))))))))))))))*************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................//////////////////////////////////////////00000000000000000000000000000000000000000000055566778899::;;<<=>??@ABCDEFGHIJLMPRSXY^`  !!!!!!!!!!!!!"""""""""""""""###############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&'''''''''''''''''''(((((((((((((((((((((()))))))))))))))))))))))**************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------...................................../////////////////////////////////////////000000000000000000000000000000000000000000000011hhiijjjkkllmmnoppqqrstuuvwxyz{|}~mo r vw{}~ !!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&''''''''''''''''''''((((((((((((((((((((())))))))))))))))))))))))************************+++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------....................................//////////////////////////////////////////00000000000000000000000000000000000000000000000111111ϜϜϜНННўўҟҟӠԡբբ֣֣פإ٦ڧڧۨܩݪݪޫ߬7 = @ADEFHKMNOPQR S S!!!!!!!!!!!!!T"""""""""""""U###############V$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''Z((((((((((((((((((((([([)))))))))))))))))))))\)\***********************]*]+++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------------`-`...................................a.a///////////////////////////////////////b/b/b0000000000000000000000000000000000000000000c0c0c111111111111  !!!!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................//////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111111111  !!!!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................//////////////////////////////////////////0000000000000000000000000000000000000000000000001111111111111111111111  !"%  !!!!!!!!!!!!!!"""""""""""""###############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&'''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,---------------------------------...................................///////////////////////////////////////00000000000000000000000000000000000000000000011111111111111111111114445566778899::;<<==>>?@@ABCDEFGHJKLMNPQWZ\a  !!!!!!!!!!!!!"""""""""""""""###############$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&'''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................////////////////////////////////////////00000000000000000000000000000000000000000000001111111111111111111111111111ggghhhiiijjkkllmmnnoppqrrstuvwxz{|}~io q s vwxyz}~ !!!!!!!!!!!!!"""""""""""""""##############$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&''''''''''''''''''''((((((((((((((((((()))))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................////////////////////////////////////////00000000000000000000000000000000000000000000011111111111111111111111111111111ΛΛϜϜННўўўҟҟӠӠԡԡբբ֣֣פإ٦ڧۨۨܩܩݪޫ߬9 AGHJKLMNOPQR S!!!!!!!!!!!!!T"""""""""""""U"U#############V$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&Y'''''''''''''''''''Z((((((((((((((((((([([)))))))))))))))))))))\)\***********************]+++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-------------------------------`-`...................................a.a/////////////////////////////////////b/b/b0000000000000000000000000000000000000000000c0c11111111111111111111111111111111111111  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................//////////////////////////////////////////0000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111 !"$&(0  !!!!!!!!!!!!!"""""""""""""###############$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&'''''''''''''''''''(((((((((((((((((((()))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................///////////////////////////////////////0000000000000000000000000000000000000000000111111111111111111111111111111111111111111111114455566778899::;;<<==>>?@ABCCDEFGHIJKLNPQRSTWY_  !!!!!!!!!!!!!"""""""""""""###############$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''((((((((((((((((((())))))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111122ggghhiijjkkllmmnnopqrrsttuvwwxyz{}~ko r tvw} !!!!!!!!!!!!!"""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&'''''''''''''''''''(((((((((((((((((((())))))))))))))))))))***********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////00000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111222222ΛΛΛϜϜϜНННўўўҟҟӠӠԡԡբ֣֣פפإإ٦ڧڧۨܩܩݪޫ߬8 < ? ADEFGHJLMNOPQR S!!!!!!!!!!!!!T"""""""""""""U#############V$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&Y&Y'''''''''''''''''Z'Z((((((((((((((((([([)))))))))))))))))))))\***********************]*]+++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-----------------------------`-`...............................a.a.a///////////////////////////////////b/b/b00000000000000000000000000000000000000000c0c11111111111111111111111111111111111111111111111d1d1d222222222222  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------..................................////////////////////////////////////////000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------..................................////////////////////////////////////////0000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222222222 "(.  !!!!!!!!!!!!""""""""""""#############$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,-----------------------------................................/////////////////////////////////////00000000000000000000000000000000000000000111111111111111111111111111111111111111111111112222222222222222222222223334445566677889::;<<=>?@@AABCDEFGIJKMNTX[a  !!!!!!!!!!!"""""""""""""###############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&'''''''''''''''''((((((((((((((((((()))))))))))))))))))))***********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..................................////////////////////////////////////000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111222222222222222222222222222222fffggghhhiijjkkllmmnnoppqqrrstuvwxyz{|} p tvyz|}~ !!!!!!!!!!!"""""""""""""##############$$$$$$$$$$$$$%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************+++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------.................................////////////////////////////////////00000000000000000000000000000000000000000111111111111111111111111111111111111111111111112222222222222222222222222222222222͚͚͚ΛΛϜϜННўўҟҟӠӠԡԡբբ֣֣פإ٦ڧۨܩݪޫ߬ = CDFGHIKMNOPQR S!!!!!!!!!!!T"""""""""""""U#############V$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&Y&Y'''''''''''''''''Z((((((((((((((((((([)))))))))))))))))))\)\*********************]*]+++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------`-`...............................a.a///////////////////////////////////b/b/b000000000000000000000000000000000000000c0c111111111111111111111111111111111111111111111d1d1d2222222222222222222222222222222222222222  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////0000000000000000000000000000000000000000001111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222  "&  !!!!!!!!!!!"""""""""""""#############$$$$$$$$$$$$$%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&'''''''''''''''''(((((((((((((((((())))))))))))))))))))*********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................///////////////////////////////////000000000000000000000000000000000000000011111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233334455566778899::;;<<==>?@ABCDEFGHIJLMRSUXY  !!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------...............................//////////////////////////////////0000000000000000000000000000000000000000011111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222233fffggghhiiijjkkllmmnnopqqrstuvwxy{|}~ r s vxz{|}~ !!!!!!!!!!!"""""""""""#############$$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&'''''''''''''''''(((((((((((((((((()))))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////////00000000000000000000000000000000000000011111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222233333333͚͚͚ΛΛΛϜϜϜННўўҟҟӠӠԡԡբ֣֣פפإ٦٦ڧڧۨܩݪޫ߬4 @CEHIJLMNOPQR S!!!!!!!!!!!T"""""""""""U#############V$$$$$$$$$$$$$W$W%%%%%%%%%%%%%X&&&&&&&&&&&&&&&Y&Y'''''''''''''''Z'Z((((((((((((((((([)))))))))))))))))))\)\*******************]*]+++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------`-`.............................a.a/////////////////////////////////b/b/b0000000000000000000000000000000000000c0c1111111111111111111111111111111111111111111d1d1d2222222222222222222222222222222222222222222222222e2e2e33333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------................................////////////////////////////////////000000000000000000000000000000000000000000111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..................................////////////////////////////////////0000000000000000000000000000000000000000111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333333333333333  "#-  !!!!!!!!!!!"""""""""""#############$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&''''''''''''''''((((((((((((((((()))))))))))))))))))*********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------.............................../////////////////////////////////0000000000000000000000000000000000000011111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222333333333333333333333333333332223334445566778899::;<<==>>?@@ABCDDEFGHIJKMNQSU  !!!!!!!!!!!!"""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%&&&&&&&&&&&&&&&&'''''''''''''''(((((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................//////////////////////////////////0000000000000000000000000000000000000001111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222233333333333333333333333333333333eeefffgghhhiijjkkllmmnnoopqrstuvvwxyz{|~l vy{| !!!!!!!!!!!"""""""""""""###########$$$$$$$$$$$$$%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................//////////////////////////////////00000000000000000000000000000000000001111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222233333333333333333333333333333333333333̙̙̙͚͚͚ΛΛΛϜϜНННўўҟҟӠԡԡբբ֣פפإ٦٦ڧۨۨܩݪޫ߬ CDFHIJKLMNPQR S!!!!!!!!!!!T"""""""""""U"U###########V$$$$$$$$$$$$$W%%%%%%%%%%%%%X%X&&&&&&&&&&&&&Y&Y'''''''''''''''Z((((((((((((((((([)))))))))))))))))))\*******************]*]+++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,_,_-------------------------`-`.............................a.a///////////////////////////////b/b/b00000000000000000000000000000000000c0c11111111111111111111111111111111111111111d1d1d22222222222222222222222222222222222222222222222e2e2e33333333333333333333333333333333333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................//////////////////////////////////000000000000000000000000000000000000000011111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////////0000000000000000000000000000000000000011111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333 #$%+  !!!!!!!!!!!"""""""""""#############$$$$$$$$$$$$%%%%%%%%%%%%%&&&&&&&&&&&&&&'''''''''''''''((((((((((((((((())))))))))))))))))********************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------............................/////////////////////////////////000000000000000000000000000000000000111111111111111111111111111111111111111111122222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333344222333444555666778899::;;<<=>>??@ABCDEFGHIJKLMNOQTUVZ[  !!!!!!!!!!!"""""""""""############$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&''''''''''''''''(((((((((((((((()))))))))))))))))********************+++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////0000000000000000000000000000000000000111111111111111111111111111111111111111112222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333334444eeefffggghhiijjkkllmmnnooppqrsstuvwxyz|~m r s tuvwz|~ !!!!!!!!!!!"""""""""""###########$$$$$$$$$$$$$%%%%%%%%%%%%%&&&&&&&&&&&&&&&'''''''''''''''((((((((((((((((())))))))))))))))))******************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////000000000000000000000000000000000000111111111111111111111111111111111111111112222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333334444444444̙̙̙͚͚͚ΛΛϜϜННўўҟҟӠӠԡԡբ֣פإإ٦٦ڧۨۨܩݪޫ߬ < = DEGHILNOPQR S!!!!!!!!!!!T"""""""""""U###########V$$$$$$$$$$$$$W%%%%%%%%%%%%%X&&&&&&&&&&&&&Y&Y'''''''''''''Z'Z((((((((((((((([([)))))))))))))))\)\*******************]+++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,_,_-------------------------`-`...........................a.a/////////////////////////////b/b/b000000000000000000000000000000000c0c0c1111111111111111111111111111111111111d1d1d222222222222222222222222222222222222222222222e2e2e333333333333333333333333333333333333333333333333333f3f3f3f4444444444444444  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////000000000000000000000000000000000000001111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................//////////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444444444 '-  !!!!!!!!!!!""""""""""##########$$$$$$$$$$$%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''((((((((((((((())))))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................///////////////////////////////000000000000000000000000000000000011111111111111111111111111111111111111111222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333334444444444444444444444444444444441111222233344555667778899::;;<<==>??@@ABCDEFGHJKLNOPRSTUVWX\  !!!!!!!!!!"""""""""###########$$$$$$$$$$$$$%%%%%%%%%%%%%&&&&&&&&&&&&&'''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................//////////////////////////////000000000000000000000000000000000001111111111111111111111111111111111111112222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333444444444444444444444444444444444444dddeeefffggghhiiijjkkllmmnoopqqrsttuvwxyz{|k p tuvwxyz|~ !!!!!!!!!"""""""""""###########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&''''''''''''''((((((((((((((()))))))))))))))))*******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................//////////////////////////////000000000000000000000000000000000011111111111111111111111111111111111111122222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444˘˘˘̙̙̙͚͚͚ΛΛΛϜϜϜННўўҟҟӠӠԡԡբբ֣פפإ٦ڧۨܩݪޫ߬9 ? GHIJKLNOPQR S!!!!!!!!!T"""""""""""U###########V$$$$$$$$$$$W%%%%%%%%%%%%%X&&&&&&&&&&&&&Y&Y'''''''''''''Z((((((((((((((([([)))))))))))))))\)\*****************]*]+++++++++++++++++++^,,,,,,,,,,,,,,,,,,,,,_,_-----------------------`-`.........................a.a.a///////////////////////////b/b/b0000000000000000000000000000000c0c0c11111111111111111111111111111111111d1d1d2222222222222222222222222222222222222222222e2e2e3333333333333333333333333333333333333333333333333f3f3f44444444444444444444444444444444444444444444444444  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////000000000000000000000000000000000000111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((((())))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444444444  !%(*,.  !!!!!!!!!"""""""""""##########$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&'''''''''''''(((((((((((((((()))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------........................../////////////////////////////00000000000000000000000000000000011111111111111111111111111111111111112222222222222222222222222222222222222222333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444444455511112223334445566677889::;;<==>??@AABBCDEFGIJLMOPVWZ  !!!!!!!!!""""""""""#########$$$$$$$$$$$$%%%%%%%%%%%&&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))*****************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////000000000000000000000000000000000111111111111111111111111111111111111122222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444555555dddeeefffggghhhiijjjkkllmmnnoopqqrsstuvwxyz{~j r uv|} !!!!!!!!!"""""""""###########$$$$$$$$$$$%%%%%%%%%%%%%&&&&&&&&&&&&'''''''''''''((((((((((((((()))))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------.........................////////////////////////////00000000000000000000000000000000111111111111111111111111111111111111122222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444455555555555555˘˘˘˘̙̙̙̙͚͚͚ΛΛϜϜННўўҟҟӠӠԡԡբբ֣פפإ٦ڧۨܩݪݪޫ߬ @DEFGHJLMNOPQR S!!!!!!!!!T"""""""""U###########V$$$$$$$$$$$W%%%%%%%%%%%X%X&&&&&&&&&&&Y'''''''''''''Z'Z((((((((((((([([)))))))))))))))\)\***************]*]+++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,_,_-----------------------`-`.......................a.a///////////////////////////b/b/b00000000000000000000000000000c0c0c11111111111111111111111111111111111d1d22222222222222222222222222222222222222222e2e2e33333333333333333333333333333333333333333333333f3f3f4444444444444444444444444444444444444444444444444444444g4g4g5555555555555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------..........................//////////////////////////////000000000000000000000000000000000011111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444445555555555555555555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................//////////////////////////////0000000000000000000000000000000000111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555  !$&(*  !!!!!!!!!"""""""""##########$$$$$$$$$$%%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((()))))))))))))))*****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,----------------------..........................///////////////////////////000000000000000000000000000000011111111111111111111111111111111111222222222222222222222222222222222222223333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555500001112223334455566778899::;;<=>>?@ABBCDEFGHIJKLOPQRSVY  !!!!!!!!"""""""""#########$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&'''''''''''''((((((((((((((())))))))))))))))****************+++++++++++++++++,,,,,,,,,,,,,,,,,,,,----------------------.........................//////////////////////////000000000000000000000000000000011111111111111111111111111111111111222222222222222222222222222222222222222223333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555ccccdddeeefffggghhiiijjkkllmmnooppqrrstuvwxyz{|~h q s uvyz{|}~ !!!!!!!"""""""""""#########$$$$$$$$$$$%%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,----------------------........................////////////////////////////0000000000000000000000000000001111111111111111111111111111111111122222222222222222222222222222222222222222333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555ʗʗʗ˘˘˘˘̙̙̙͚͚͚ΛΛΛϜϜϜННўўўҟҟӠӠԡԡբբ֣פإ٦ڧۨܩݪޫ߬8; CDKLMOPQR S S!!!!!!!T"""""""""U"U#########V$$$$$$$$$W$W%%%%%%%%%%%X&&&&&&&&&&&Y'''''''''''''Z((((((((((((([([)))))))))))))))\***************]*]+++++++++++++++++^+^,,,,,,,,,,,,,,,,,_,_---------------------`-`.......................a.a/////////////////////////b/b/b000000000000000000000000000c0c0c111111111111111111111111111111111d1d222222222222222222222222222222222222222e2e2e3333333333333333333333333333333333333333333f3f3f44444444444444444444444444444444444444444444444444444g4g4g5555555555555555555555555555555555555555555555555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))))****************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////000000000000000000000000000000001111111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555  !!!!!!!!!!""""""""""############$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////0000000000000000000000000000000011111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555556666 !%'/  !!!!!!!!!"""""""""##########$$$$$$$$$%%%%%%%%%%%&&&&&&&&&&&&'''''''''''(((((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,----------------------......................./////////////////////////00000000000000000000000000000111111111111111111111111111111111222222222222222222222222222222222222333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555556666666600001112223334445566778899:;;<==>>?@ABCCDEFGHIJKMNOPQTVX^  !!!!!!!!!"""""""""#########$$$$$$$$$$$%%%%%%%%%%%&&&&&&&&&&&'''''''''''''((((((((((((()))))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,----------------------......................//////////////////////////00000000000000000000000000000011111111111111111111111111111111122222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555666666666666ccccddddeeefffgghhhiijjkkllmmnnoopqrrsstuuvwxyz{|~n r x{|}~ !!!!!!!!!"""""""""#########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,,----------------------......................//////////////////////////0000000000000000000000000000011111111111111111111111111111111222222222222222222222222222222222222222333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555566666666666666666666ʗʗʗ˘˘˘̙̙̙͚͚ΛΛΛϜϜНННўўўҟҟӠԡԡբբ֣֣פפإإ٦ڧۨۨܩݪޫ߬6; ? ACEFIJLMOPQR S!!!!!!!!!T"""""""""U#########V$$$$$$$$$W%%%%%%%%%%%X&&&&&&&&&&&Y'''''''''''''Z((((((((((((([)))))))))))))\)\***************]+++++++++++++++++^,,,,,,,,,,,,,,,,,,,_,_-----------------`-`-`.....................a.a///////////////////////b/b/b0000000000000000000000000c0c0c1111111111111111111111111111111d1d1d22222222222222222222222222222222222e2e2e33333333333333333333333333333333333333333f3f3f4444444444444444444444444444444444444444444444444g4g4g55555555555555555555555555555555555555555555555555555555555h5h5h5h6666666666666666666666666666  !!!!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------..........................//////////////////////////000000000000000000000000000000111111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666  !!!!!!!!!!""""""""##########$$$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................////////////////////////////0000000000000000000000000000001111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666 #&,  !!!!!!!!!"""""""#########$$$$$$$$$$$%%%%%%%%%&&&&&&&&&&&&'''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------....................../////////////////////////000000000000000000000000000111111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666////0000111222333444555666778899:;;<==>?@ABCDEFGHIJKLMNOPRTVY^_  !!!!!!!!!"""""""#########$$$$$$$$$$%%%%%%%%%&&&&&&&&&&&''''''''''''((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////000000000000000000000000000011111111111111111111111111111112222222222222222222222222222222222222333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666bbbbccccddddeeeefffggghhiijjkkllmmnoopqqrrsttuuvwxyz|~no twy{|} !!!!!!!!"""""""#########$$$$$$$$$%%%%%%%%%%&&&&&&&&&&'''''''''''(((((((((((()))))))))))))***************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////0000000000000000000000000001111111111111111111111111111112222222222222222222222222222222222222333333333333333333333333333333333333333334444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666ɖɖɖɖɖʗʗʗ˘˘˘̙̙̙͚͚͚ΛΛϜϜϜННўўҟҟӠӠԡԡբբ֣֣פפإ٦ڧڧۨܩݪޫ߬ < = ADFIJKLMNOPQR S!!!!!!!T"""""""""U#########V$$$$$$$$$W%%%%%%%%%X&&&&&&&&&&&Y'''''''''''Z'Z((((((((((([)))))))))))))\)\*************]*]+++++++++++++^+^,,,,,,,,,,,,,,,,,_,_-----------------`-`...................a.a.a/////////////////////b/b/b0000000000000000000000000c0c11111111111111111111111111111d1d1d222222222222222222222222222222222e2e2e3333333333333333333333333333333333333f3f3f3f444444444444444444444444444444444444444444444g4g4g5555555555555555555555555555555555555555555555555555555h5h5h5h66666666666666666666666666666666666666666666666666666666666666666666  !!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------......................//////////////////////////000000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666  !!!!!!!!""""""""""########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&''''''''''''''(((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------......................//////////////////////////0000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666667777777777  #$  !!!!!!!"""""""""#######$$$$$$$$$%%%%%%%%%%&&&&&&&&&''''''''''''((((((((((()))))))))))))**************+++++++++++++++,,,,,,,,,,,,,,,,,,------------------....................///////////////////////00000000000000000000000001111111111111111111111111111122222222222222222222222222222222333333333333333333333333333333333333333444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666677777777777777////00001111222333445566778899:;<=>?@AABCDEFHIJKLOQSV  !!!!!!!""""""""#######$$$$$$$$$%%%%%%%%%&&&&&&&&&&&'''''''''''(((((((((((())))))))))))*************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////000000000000000000000000001111111111111111111111111111122222222222222222222222222222222222333333333333333333333333333333333333334444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666777777777777777777bbbbcccdddeeefffggghhhiiijjkkllmmnnooppqqrsstuvwxyz{|lmno p q r wxz| !!!!!!!"""""""#########$$$$$$$$$%%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))**************++++++++++++++,,,,,,,,,,,,,,,,------------------...................//////////////////////000000000000000000000000011111111111111111111111111112222222222222222222222222222222222233333333333333333333333333333333333334444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777ȕȕȕɖɖɖɖɖʗʗʗʗ˘˘˘̙̙̙͚͚ΛΛΛϜϜННўўҟҟӠӠԡԡբբ֣֣פإ٦٦ڧۨܩܩݪޫ߬7 ? @AEGIJLMNPQR S!!!!!!!T"""""""U#########V$$$$$$$W$W%%%%%%%X%X&&&&&&&&&Y'''''''''''Z((((((((((([)))))))))))))\*************]*]+++++++++++++^+^,,,,,,,,,,,,,,,_,_---------------`-`-`.................a.a/////////////////////b/b/b00000000000000000000000c0c111111111111111111111111111d1d1d22222222222222222222222222222e2e2e2e33333333333333333333333333333333333f3f3f4444444444444444444444444444444444444444444g4g4g55555555555555555555555555555555555555555555555555555h5h5h666666666666666666666666666666666666666666666666666666666666666i6i6i6i6i777777777777777777777777777777777777  !!!!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////000000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333334444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777  !!!!!!!!""""""""########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////0000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333334444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777 .  !!!!!!!"""""""#######$$$$$$$$$%%%%%%%%%&&&&&&&&&'''''''''''((((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,,,----------------..................../////////////////////0000000000000000000000011111111111111111111111111122222222222222222222222222223333333333333333333333333333333333333444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777.....////0000111122233344455566778899:;;<<==>>?@ABCDEFGHLMOQRUVWXYZ[\  !!!!!!!"""""""#######$$$$$$$$$%%%%%%%%&&&&&&&&&''''''''''(((((((((()))))))))))*************++++++++++++++,,,,,,,,,,,,,,,,----------------...................////////////////////00000000000000000000000011111111111111111111111111112222222222222222222222222222222333333333333333333333333333333333333344444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777aaaaabbbbcccdddeeefffggghhiijjkkllmmnnopqrstuvvwxyz{|}~n xy|~ !!!!!!""""""#######$$$$$$$$%%%%%%%&&&&&&&&&&'''''''''((((((((((())))))))))))************+++++++++++++,,,,,,,,,,,,,,,,----------------..................//////////////////////00000000000000000000000111111111111111111111111111222222222222222222222222222223333333333333333333333333333333333333444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777ȕȕȕȕɖɖɖɖɖʗʗʗʗ˘˘˘˘̙̙̙͚͚͚ΛΛϜϜϜНННўўҟҟӠӠԡԡբ֣֣פפإإ٦٦ڧڧۨܩܩݪޫ߬9 AFGIKMNOPQR S!!!!!T"""""""U#########V$$$$$$$W%%%%%%%%%X&&&&&&&&&Y'''''''''Z'Z((((((((([([)))))))))\)\***********]*]+++++++++++++^+^,,,,,,,,,,,,,_,_---------------`-`.................a.a///////////////////b/b/b000000000000000000000c0c1111111111111111111111111d1d1d222222222222222222222222222e2e2e333333333333333333333333333333333f3f3f3f444444444444444444444444444444444444444g4g4g5555555555555555555555555555555555555555555555555h5h5h5h666666666666666666666666666666666666666666666666666666666i6i6i6i6i7777777777777777777777777777777777777777777777777777777777777777777777777j7j7j7j  !!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''(((((((((((())))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////000000000000000000000000001111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888  !!!!!!""""""""########$$$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''''(((((((((())))))))))))))************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////0000000000000000000000001111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888 ',  !!!!!"""""""#######$$$$$$$$%%%%%%%&&&&&&&&&''''''''''((((((((())))))))))))***********++++++++++++++,,,,,,,,,,,,,,----------------.................///////////////////000000000000000000000011111111111111111111111112222222222222222222222222233333333333333333333333333333333333444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888....////000111122233344556667778899:;<<=>>?@@ABCDEFGHIKLNORSTUVW\  !!!!!"""""""#######$$$$$$$%%%%%%%%%&&&&&&&&&'''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////000000000000000000000001111111111111111111111112222222222222222222222222222233333333333333333333333333333333333444444444444444444444444444444444444444455555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888``aaaaabbbbccccdddeeeffggghhhiijjkkllmmnnoppqrstuvwxyz{|}~n r s {|~ !!!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&&''''''''((((((((()))))))))))************++++++++++++,,,,,,,,,,,,,,---------------................///////////////////0000000000000000000001111111111111111111111111222222222222222222222222222333333333333333333333333333333333344444444444444444444444444444444444444555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888ǔǔǔǔǔȕȕȕȕȕɖɖɖɖɖʗʗʗʗ˘˘˘˘̙̙̙͚͚͚ΛΛΛϜϜННўўҟҟӠӠԡԡբբ֣֣פפإإ٦٦ڧۨܩݪޫ߬ = BCDEFIJLNOPQR S!!!!!!!T"""""""U#####V#V$$$$$$$W%%%%%%%X&&&&&&&&&Y'''''''''Z((((((((([([)))))))))\)\*********]*]+++++++++++^+^,,,,,,,,,,,,,_,_-------------`-`.................a.a/////////////////b/b000000000000000000000c0c11111111111111111111111d1d1d2222222222222222222222222e2e2e3333333333333333333333333333333f3f3f4444444444444444444444444444444444444g4g4g555555555555555555555555555555555555555555555h5h5h5h66666666666666666666666666666666666666666666666666666i6i6i6i777777777777777777777777777777777777777777777777777777777777777777777j7j7j7j7j888888888888888888888888888888888888888888888888  !!!!!!!!""""""""######$$$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,,,----------------....................////////////////////000000000000000000000000111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888  !!!!!!!!""""""########$$$$$$$$%%%%%%%%%%&&&&&&&&''''''''''(((((((((((())))))))))**************++++++++++++++,,,,,,,,,,,,,,----------------..................//////////////////////0000000000000000000000111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888  $%(+0  !!!!!!!"""""#######$$$$$$$%%%%%%%%&&&&&&&'''''''''(((((((((()))))))))************++++++++++++,,,,,,,,,,,,--------------................///////////////////0000000000000000000011111111111111111111111122222222222222222222222233333333333333333333333333333333344444444444444444444444444444444444445555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888-----.....////000111222333444555667778899::;;<<=>??@ABCDEFGHIKMNOUVZ]  !!!!!!!"""""#######$$$$$$$%%%%%%%&&&&&&&&''''''''(((((((())))))))))**********++++++++++++,,,,,,,,,,,,--------------................//////////////////0000000000000000000001111111111111111111111122222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444455555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888``````aaaaabbbbccccdddeeeefffgghhiijjkkllmnnoppqqrstuvwxyz{|}~n q txyz{|} !!!!!!"""""#######$$$$$$$%%%%%%%&&&&&&&'''''''''((((((((())))))))))**********++++++++++++,,,,,,,,,,,,--------------................/////////////////00000000000000000001111111111111111111111122222222222222222222222223333333333333333333333333333333444444444444444444444444444444444444455555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888ǔǔǔǔǔȕȕȕȕɖɖɖɖɖʗʗʗʗ˘˘˘˘̙̙̙͚͚͚ΛΛΛϜϜϜНННўўҟҟӠԡԡբ֣֣פإ٦٦ڧڧۨۨܩݪޫ߬ < ? BCJKLMNOPQR S!!!!!T"""""""U#####V#V$$$$$W$W%%%%%X%X&&&&&&&Y'''''''Z'Z((((((([([)))))))))\***********]+++++++++++^+^,,,,,,,,,,,_,_-------------`-`.............a.a.a///////////////b/b0000000000000000000c0c111111111111111111111d1d1d22222222222222222222222e2e2e33333333333333333333333333333f3f3f444444444444444444444444444444444g4g4g4g555555555555555555555555555555555555555h5h5h5h6666666666666666666666666666666666666666666666666i6i6i6i777777777777777777777777777777777777777777777777777777777777777j7j7j7j7j8888888888888888888888888888888888888888888888888888888888888888888888888888888k8k8k8k8k8k99999999  !!!!!!""""""""######$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''(((((((((())))))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////000000000000000000000011111111111111111111111122222222222222222222222222223333333333333333333333333333333333444444444444444444444444444444444444445555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999  !!!!!!""""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////0000000000000000000011111111111111111111111122222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444444555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999  !$&  !!!!!""""""#####$$$$$$$%%%%%%%&&&&&&&''''''''(((((((())))))))**********++++++++++,,,,,,,,,,,,--------------............../////////////////00000000000000000011111111111111111111112222222222222222222222333333333333333333333333333334444444444444444444444444444444444455555555555555555555555555555555555555555666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999,-----....////000011122233344455666778899::;<<==>??@@ABCDEFGHIKLMNOPQRV[]  !!!!!"""""#######$$$$$$%%%%%%&&&&&&'''''''((((((((()))))))))**********++++++++++,,,,,,,,,,,,--------------..............////////////////00000000000000000001111111111111111111112222222222222222222222233333333333333333333333333333444444444444444444444444444444444455555555555555555555555555555555555555556666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999______`````aaaaabbbbccccddddeeeffggghhhiijjjkkllmnnopqqrsttuvwxyz|} p r uwx{|}~ !!!!!"""""#######$$$$$%%%%%%%&&&&&&&''''''''(((((((())))))))*********+++++++++++,,,,,,,,,,,,-------------.............///////////////0000000000000000001111111111111111111112222222222222222222222233333333333333333333333333333444444444444444444444444444444444555555555555555555555555555555555555555566666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999ƓƓƓƓƓƓƓǔǔǔǔǔǔȕȕȕȕȕɖɖɖɖɖʗʗʗʗ˘˘˘̙̙̙͚͚͚ΛΛϜϜННўўҟҟӠӠԡԡբբ֣֣פפإ٦ڧۨܩݪݪޫޫ߬8: @BEFMNOPQR S!!!!!T"""""U#####V#V$$$$$W%%%%%%%X&&&&&&&Y'''''''Z((((((([([)))))))\)\*********]*]+++++++++^+^,,,,,,,,,_,_-----------`-`.............a.a///////////////b/b00000000000000000c0c0c11111111111111111d1d1d222222222222222222222e2e2e3333333333333333333333333f3f3f3f44444444444444444444444444444g4g4g4g55555555555555555555555555555555555h5h5h5h666666666666666666666666666666666666666666666i6i6i6i777777777777777777777777777777777777777777777777777777777j7j7j7j7j8888888888888888888888888888888888888888888888888888888888888888888888888k8k8k8k8k999999999999999999999999999999999999999999999999999999999999999999  !!!!!!""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((())))))))))************++++++++++++,,,,,,,,,,,,--------------................//////////////////000000000000000000001111111111111111111111222222222222222222222222223333333333333333333333333333334444444444444444444444444444444444445555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999  !!!!!!""""""######$$$$$$$$%%%%%%&&&&&&&&''''''''(((((((((())))))))))**********++++++++++++,,,,,,,,,,,,--------------................//////////////////0000000000000000001111111111111111111111112222222222222222222222223333333333333333333333333333334444444444444444444444444444444444555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 !#%'  !!!!!"""""#####$$$$$$%%%%%&&&&&&&'''''''(((((((())))))))********++++++++++,,,,,,,,,,------------..............///////////////0000000000000000111111111111111111111222222222222222222333333333333333333333333333444444444444444444444444444444455555555555555555555555555555555555556666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,,,,,,------.....////0000111222333445566778899::;;<<=>>??@AABCDEFGHIPQSTVX]`c  !!!!!"""""#####$$$$$%%%%%%%&&&&&&''''''((((((())))))))*********++++++++++,,,,,,,,,,------------..............//////////////000000000000000001111111111111111111222222222222222222222333333333333333333333333334444444444444444444444444444445555555555555555555555555555555555556666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999______`````aaaaabbbbccccddddeeefffggghhhiiijjkkllmnnoppqrstuvwxxy{|}~ s uwyz|}~ !!!!!""""####$$$$$%%%%%%&&&&&'''''''(((((((()))))))**********++++++++,,,,,,,,,,------------............./////////////00000000000000001111111111111111111222222222222222222222333333333333333333333333344444444444444444444444444444445555555555555555555555555555555555556666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::ƓƓƓƓƓƓƓǔǔǔǔǔȕȕȕȕɖɖɖɖɖʗʗʗʗ˘˘˘̙̙̙͚͚͚ΛΛΛϜϜННўўўҟҟӠӠԡԡբ֣֣פإإ٦ڧڧۨܩݪޫ߬7: = @BDFGJKLMNOPQR S!!!T!T"""U#####V$$$$$W$W%%%%%X&&&&&&&Y'''''Z'Z((((((([)))))))\)\*******]*]+++++++^+^,,,,,,,,,_,_-----------`-`...........a.a/////////////b/b000000000000000c0c0c111111111111111d1d1d2222222222222222222e2e2e33333333333333333333333f3f3f444444444444444444444444444g4g4g4g5555555555555555555555555555555h5h5h5h66666666666666666666666666666666666666666i6i6i6i777777777777777777777777777777777777777777777777777j7j7j7j7j88888888888888888888888888888888888888888888888888888888888888888k8k8k8k8k8k99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999l9l9l9l9l9l::::::::::::::::::::::  !!!!""""""######$$$$$$%%%%%%%%&&&&&&&&''''''(((((((((())))))))**********++++++++++,,,,,,,,,,,,--------------..............////////////////00000000000000000011111111111111111111222222222222222222222222333333333333333333333333333344444444444444444444444444444444555555555555555555555555555555555555556666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::  !!!!""""""######$$$$$$%%%%%%%%&&&&&&''''''''(((((((())))))))**********++++++++++++,,,,,,,,,,--------------..............////////////////0000000000000000001111111111111111111122222222222222222222223333333333333333333333333333444444444444444444444444444444555555555555555555555555555555555555556666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999:::::::::::::::::::::::::::::::::::::::::::::::: !#&*.  !!!"""""#####$$$$$%%%%%%&&&&&''''''(((((()))))))*********++++++++++,,,,,,,,------------............/////////////0000000000000001111111111111111122222222222222223333333333333333333333344444444444444444444444444445555555555555555555555555555555556666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999:::::::::::::::::::::::::::::::::::::::::::::::::::::::+++++++,,,,,,------.....////00001111222233344455566778899::;;<<=>>?@@ABCCDEFGHKLMNPRTVY]  !!!"""""#####$$$$$%%%%%&&&&&&'''''((((((())))))))********++++++++,,,,,,,,------------...........////////////00000000000000011111111111111111222222222222222222233333333333333333333333444444444444444444444444444455555555555555555555555555555555666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::^^^^^^^______`````aaaabbbbccccdddeeeffggghhiijjkkllmnooppqrrstuvwxyz{|}~m q r uwxz|} !!!"""""####$$$$%%%%%&&&&&''''''(((((()))))))********++++++++,,,,,,,,-----------........../////////////00000000000000111111111111111112222222222222222222333333333333333333333334444444444444444444444444445555555555555555555555555555555566666666666666666666666666666666666666667777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ŒŒŒŒŒŒŒŒƓƓƓƓƓƓƓǔǔǔǔǔȕȕȕȕȕɖɖɖɖʗʗʗʗ˘˘˘̙̙̙͚͚͚ΛΛϜϜϜНННўўҟҟӠӠԡԡբբ֣פإإ٦ڧۨܩݪޫ߬4: ? BEGIJLMNOPQR S!!!T"""""U###V$$$$$W%%%%%X%X&&&Y&Y'''''Z((((((([)))))))\)\*****]*]+++++++^+^,,,,,,,_,_---------`-`...........a.a///////////b/b0000000000000c0c0c1111111111111d1d1d22222222222222222e2e2e3333333333333333333f3f3f3f44444444444444444444444g4g4g4g555555555555555555555555555h5h5h5h66666666666666666666666666666666666i6i6i6i6i777777777777777777777777777777777777777777777j7j7j7j7j888888888888888888888888888888888888888888888888888888888k8k8k8k8k8k9999999999999999999999999999999999999999999999999999999999999999999999999999999l9l9l9l9l9l9l::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  !!!!""""""####$$$$$$%%%%%%&&&&&&''''''''(((((((())))))))********++++++++++,,,,,,,,,,------------..............//////////////000000000000000011111111111111111122222222222222222222223333333333333333333333334444444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  !!!!!!""""######$$$$$$%%%%%%&&&&&&''''''(((((((())))))))**********++++++++,,,,,,,,,,------------..............//////////////00000000000000001111111111111111112222222222222222222233333333333333333333333344444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  !$%*  !!!!!"""#####$$$$$%%%%%&&&&&'''''((((((()))))))********++++++,,,,,,,,----------...........///////////0000000000000111111111111111222222222222223333333333333333333334444444444444444444444444555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;+++++++,,,,,,-----.....////000011112223334455666778899::;;<<==>?@ABBCDEFGHIKLNPSVW\]  !!!!!"""#####$$$$%%%%&&&&'''''(((((())))))******+++++++,,,,,,,,,----------..........////////////0000000000000111111111111111222222222222222223333333333333333333334444444444444444444444445555555555555555555555555555666666666666666666666666666666666666677777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;^^^^^^^______``````aaaaabbbbccccdddeeefffggghhhiijjkkllmmnnoopqqrrsstuvwwxyz{}~kl tuxy{|~ !!!!!"""####$$$%%%%%&&&&&''''''(((())))))******++++++++,,,,,,,,--------..........///////////00000000000011111111111111122222222222222222333333333333333333334444444444444444444444455555555555555555555555555556666666666666666666666666666666666667777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;ŒŒŒŒŒŒŒŒƓƓƓƓƓƓǔǔǔǔǔȕȕȕȕɖɖɖɖʗʗʗʗ˘˘˘˘̙̙̙̙͚͚͚ΛΛΛϜϜННўўҟҟӠӠԡբ֣פפإ٦ڧۨۨܩݪޫ߬9:; BCFGIKMNPQR S!!!T!T"""U###V$$$$$W%%%X%X&&&Y&Y'''''Z((((([)))))))\*******]+++++++^+^,,,,,,,_,_-------`-`.........a.a/////////b/b00000000000c0c0c1111111111111d1d222222222222222e2e2e33333333333333333f3f3f444444444444444444444g4g4g4g55555555555555555555555h5h5h5h6666666666666666666666666666666i6i6i6i6i777777777777777777777777777777777777777j7j7j7j7j888888888888888888888888888888888888888888888888888k8k8k8k8k99999999999999999999999999999999999999999999999999999999999999999999999l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""""####$$$$$$%%%%&&&&&&''''''''(((((())))))))********++++++++,,,,,,,,,,----------............////////////000000000000001111111111111111112222222222222222223333333333333333333333444444444444444444444444445555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""""####$$$$%%%%%%&&&&&&''''''(((((())))))))********++++++++,,,,,,,,,,----------..........//////////////00000000000000111111111111111122222222222222222233333333333333333333334444444444444444444444445555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;    !!!""""###$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,,--------........///////////000000000001111111111111222222222222333333333333333334444444444444444444444555555555555555555555555566666666666666666666666666666666777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*********++++++++,,,,,,-----.....////00001112223344455566778899::;;<<=>?@@ABBCDEFGHIKMNPQRVWXYZ[\]^_`a  !!!"""#####$$$%%%%&&&&''''(((()))))******++++++,,,,,,,,-------........//////////00000000000011111111111112222222222222223333333333333333344444444444444444444445555555555555555555555555666666666666666666666666666666667777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;]]]]]]]]]^^^^^^^______`````aaaabbbbccccdddeeefffgghhiiijjkklmmnooppqqrrsttuvwxyz{|}~ z{|~ !!!"""####$$$%%%&&&&&'''''((((())))))****++++++,,,,,,,,------........../////////00000000000111111111112222222222222223333333333333333344444444444444444444455555555555555555555555555666666666666666666666666666666777777777777777777777777777777777777777888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;đđđđđđđđđŒŒŒŒŒŒŒƓƓƓƓƓƓǔǔǔǔǔǔȕȕȕȕȕɖɖɖɖʗʗʗ˘˘˘˘̙̙̙͚͚͚ΛΛΛϜϜϜННўўҟҟӠӠԡբբ֣פإ٦ڧۨܩݪޫ߬:; < = > ? @ABCDEIJLMNOPR S!!!T"""U###V$$$W$W%%%X&&&Y&Y'''Z'Z((([([)))\)\*****]+++++++^,,,,,,,_,_-----`-`.......a.a.a///////b/b000000000c0c0c11111111111d1d2222222222222e2e2e333333333333333f3f3f44444444444444444g4g4g4g555555555555555555555h5h5h5h6666666666666666666666666i6i6i6i6i777777777777777777777777777777777j7j7j7j7j8888888888888888888888888888888888888888888k8k8k8k8k8k99999999999999999999999999999999999999999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""####$$$$%%%%%%&&&&''''''(((((())))))********++++++++,,,,,,,,--------..........////////////0000000000001111111111111111222222222222222233333333333333333333444444444444444444444455555555555555555555555555556666666666666666666666666666666677777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  !!!!""""####$$$$%%%%&&&&&&''''''(((())))))))******++++++++,,,,,,,,--------..........////////////000000000000111111111111112222222222222222333333333333333333444444444444444444444455555555555555555555555555556666666666666666666666666666666677777777777777777777777777777777777777778888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<< '(  !!!"""###$$$%%%&&&&&''''((())))))****++++++,,,,,,------......../////////00000000011111111111222222222233333333333333344444444444444444445555555555555555555555566666666666666666666666666777777777777777777777777777777777778888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;< < < < < < < < < < <<<<<<<<<<<<*********+++++++,,,,,,------.....////00001112223334445566778899::;;<==>>?@ABCDEGHIJKLMQRSTU]^  !!!"""##$$$%%%&&&&'''(((())))****++++++,,,,,,------........////////0000000000111111111112222222222222333333333333333444444444444444444555555555555555555555566666666666666666666666666777777777777777777777777777777777778888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]]]]]]]]]^^^^^^^^______`````aaaaabbbbccccddddeeefffggghhhiijjkkllmmnnooppqrssttuuvvwxyz|}j p xyz{| !!!""#$$$$%%&&&''''((()))))*****++++++,,,,,,------.......///////000000000111111111122222222222223333333333333334444444444444444455555555555555555555556666666666666666666666666777777777777777777777777777777777888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÐđđđđđđđđđŒŒŒŒŒŒŒƓƓƓƓƓƓǔǔǔǔǔǔȕȕȕȕȕɖɖɖɖʗʗʗ˘˘˘̙̙̙͚͚͚ΛΛϜϜННўўҟҟӠӠԡբբ֣פإإ٦٦ڧۨܩݪޫ߬:; @ABCIJKLNOPQR S!T!T"U###V$$$W%%%X&&&Y&Y'''Z((([([)))\)\***]*]+++^+^,,,,,_,_-----`-`.....a.a///////b/b0000000c0c0c111111111d1d1d222222222e2e2e33333333333f3f3f3f4444444444444g4g4g4g55555555555555555h5h5h5h666666666666666666666i6i6i6i77777777777777777777777777777j7j7j7j7j88888888888888888888888888888888888k8k8k8k8k8k9999999999999999999999999999999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""####$$$$%%%%&&&&''''''(((())))))******++++++,,,,,,,,--------........//////////00000000001111111111111122222222222222333333333333333344444444444444444444555555555555555555555555666666666666666666666666666677777777777777777777777777777777777788888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!""""####$$$$%%%%&&&&''''(((((())))******++++++,,,,,,,,--------........//////////000000000011111111111122222222222222333333333333333344444444444444444455555555555555555555555566666666666666666666666666777777777777777777777777777777777777888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $)  !"""###$$%%&&'''(((()))****++++,,,,,,------......///////000000011111111122222222333333333333344444444444444455555555555555555556666666666666666666666777777777777777777777777777777888888888888888888888888888888888888899999999999999999999999999999999999999999999999999:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;< < < < < < < < < < < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<)))))))))))*********+++++++,,,,,,------.....////00001112223344555667778899::;;<==>>?@AABCDDEFGHIJSTUYZa  !"""##$%%%&&&''''(())))***+++++,,,,,,-----.....//////0000000011111111122222222222333333333333444444444444445555555555555555556666666666666666666666777777777777777777777777777778888888888888888888888888888888888889999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\\\\\\\\\\\\]]]]]]]]]^^^^^^^______`````aaaabbbbcccddddeeeefffggghhiiijjkkllmmnoopqrstuuvwxyz{|}~o swx}~ !"""#$$$%%%&&&'''((())))****++++,,,,----......///////00000001111111122222222222333333333334444444444444445555555555555555556666666666666666666667777777777777777777777777778888888888888888888888888888888888999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÐÐÐÐÐÐÐÐÐÐÐđđđđđđđđđŒŒŒŒŒŒŒŒƓƓƓƓƓƓǔǔǔǔǔȕȕȕȕȕɖɖɖɖʗʗʗʗ˘˘˘̙̙̙͚͚͚ΛΛΛϜϜϜННўўҟҟӠӠԡբբ֣פפإإ٦٦ڧۨܩݪޫ߬ > ABEFGHOPQR S!T"U"U#V$$$W%X%X&Y&Y'Z'Z([([)))\***]*]+++^+^,,,_,_---`-`.....a.a/////b/b0000000c0c1111111d1d1d2222222e2e2e333333333f3f3f44444444444g4g4g4g5555555555555h5h5h5h66666666666666666i6i6i6i77777777777777777777777j7j7j7j7j88888888888888888888888888888k8k8k8k8k99999999999999999999999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<??@ABCDEFGHIJKLMNQRUX[`  !"##$%%&'''((()))****++,,,,----....//////0000001111111222222222333333333444444444444555555555555556666666666666666677777777777777777777777888888888888888888888888888899999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================[[[\\\\\\\\\\\\]]]]]]]]]^^^^^^^______``````aaaaabbbbcccddddeeefffggghhhiijjjkkllmmnnooppqqrsstuvwxyz{|}~o q tvwyz}~ !"#$$$%&&&''(())**++,,,,----..../////000001111112222222223333333334444444444455555555555555666666666666666677777777777777777777777888888888888888888888888888999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================================ÐÐÐÐÐÐÐÐÐÐÐđđđđđđđđđŒŒŒŒŒŒŒƓƓƓƓƓƓǔǔǔǔǔȕȕȕȕȕɖɖɖɖʗʗʗʗ˘˘˘̙̙̙͚͚͚ΛΛϜϜНННўўҟҟӠӠԡԡբբ֣֣פإ٦٦ڧۨܩܩݪޫ߬68 ?ADGHJKLMOPQR S!!!T"U#V$W$W%X&Y&Y'Z([([)\)\*]*]+++^,,,_,_---`-`...a.a///b/b00000c0c11111d1d1d22222e2e2e33333f3f3f3f4444444g4g4g4g555555555h5h5h5h6666666666666i6i6i6i77777777777777777j7j7j7j7j888888888888888888888k8k8k8k8k8k99999999999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<??@ABCDEFGHJKLNOQSU  !"#$W$%&''())**++,,--....////000001111222222233333334444444455555555556666666666666777777777777777778888888888888888888889999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================================================================================================[[[[[[[[[[[[[[[[\\\\\\\\\\\\]]]]]]]]]^^^^^^^^______`````aaaabbbbccccdddeeefffggghhiijjjkkllmmnoopqqrstuvwxyz}~km tvxz{} !"#$%%&'(()**++,,--....///00011111222223333334444444555555555566666666666677777777777777777888888888888888888889999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================================================================================================================ÐÐÐÐÐÐÐÐÐÐÐđđđđđđđđđŒŒŒŒŒŒŒƓƓƓƓƓƓǔǔǔǔǔǔȕȕȕȕȕɖɖɖɖʗʗʗʗ˘˘˘˘̙̙̙͚͚͚ΛΛΛϜϜϜННўўҟҟӠӠԡnբ֣pפإ٦ڧtuvw߬ < >EGHJLMOP S!T"U#V$W%X&Y&'Z([)\)\*]+^+^,_,_-`-`.a.a.a/b/b000c0c111d1d1d222e2e2e333f3f3f44444g4g4g4g55555h5h5h5h6666666i6i6i6i6i77777777777j7j7j7j7j8888888888888k8k8k8k8k8k999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%&&''(((())****++,,,,----....//////000000111111222222223333333344444444445555555555556666666666666677777777777777777777888888888888888888888899999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!""##$$%%&&''(())****++,,,,----....//////00001111112222222233333333444444445555555555556666666666666677777777777777777788888888888888888888889999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>9; CDG!$&)  !#$%&'(()**+,,--..///00111223334444445555555666666666677777777777788888888888888899999999999999999999:::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;< < < < < < < < < < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<= = = = = = = = = = = = = = ==================================================================================================================================================================> > > > > > > > > > > > > > > > > > > > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>''''''''(((((((((((((((())))))))))))*********+++++++,,,,,,-----....////00011112222333444556677889Ӡ:;<=פإ٦ABCޫFHRTW R "U#V$%&'()*]*+,_,--..//0001122222333444444555555666666666777777777778888888888888899999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=========================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[\\\\\\\\\\\]]]]]]]]]^^^^^^^______`````aaaaabbbbccccdddeeefffgghhijkklmopq v{} p sv{ !#$%&'()*++,--..//001112223334444455555566666666777777777778888888888889999999999999999999::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÐÐÐÐÐÐÐÐÐÐÐÐđđđđđđđđđŒŒŒŒŒŒŒŒƓƓƓƓƓƓǔǔǔǔǔǔȕȕȕȕȕɖɖɖɖʗʗʗʗ˘˘˘˘̙̙̙͚͚gΛΛϜϜНjўklӠԡnopqrڧuݪxz|~9 = @CF|K S!#V$%'Z([)\*]*+,_-`-.a/b/b0c0c1d1d1d2e2e2e3f3f3f4g4g4g4g5h5h5h5h666i6i6i6i6i77777j7j7j7j7j8888888k8k8k8k8k99999999999l9l9l9l9l9l9l:::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  !!##$$&&''(())**++,,----..////0000111122222233333344444455555555666666666677777777777777888888888888888899999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  ""##%%&&(())**++,,----..////00111122222233334444445555555566666666667777777777778888888888888899999999999999999999::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>156789:;<=?@BDFHKNQ!%)- !#$&')*+,--.//001133344555666677777778888888999999999999:::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;< < < < < < < < < < < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<= = = = = = = = = = = = = ================================================================================================> > > > > > > > > > > > > > > > > > > > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'''''''''''''''''''''''''''((((((((((((((((())))))))))))*********++++++++,,,,,,------.....///ɖ00011˘2̙3͚4Λ5ϜНўҟӠԡբ إڧ\ EHLO!T#V%X&([)\+^,_-`.a./b0c01d12e23f34g445h5556666777777788888899999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[\\\\\\\\\\\]]]]]]]]]^^^^^^^______`````aaaabbccdefg֣إ ޫ"'l qJNQ #V%X'Z()+^,-.//01133344555566667777788888999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????ÐÐÐÐÐÐÐÐÐÐÐÐđđđđđđđđđŒŒŒŒŒŒŒƓƓƓƓƓƓǔǔǔǔǔȕȕȕbɖɖcʗddeeffghi78lm<>@BEHLP r "%')*+-./0012234g445h56i6i6i67j7j7j78k8k8k8k8k9l9l9l9l9l9l:::::m:m:m:m:m:m:m:m;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  ##%%((**++--..//0011222233444455556666667777777788888888999999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ##&&))++--..//112222334455556666667777778888889999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????*+,,--../b01defgϜўӠբإ\ EL#V'')\,_.a/012345566777888999:::::;;;;;;;< < < < < < < < < < <<<<<<= = = = = = = = = = = = = = ==========================> > > > > > > > > > > > > > > > > > > > > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''(((((((((((((((()))))))))))Ð*****đđđ++ŒŒ,ƓƓƓǔǔȕɖgiBH& ##'++--/0234566i7j7j8k8k8k9l9l99:m:m::::;n;n;;;;;;;;<<<<<<<<<<<<<==================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[\\\\\\]ŒƓƓǔǔȕ/01i ܩvN#V([,//1133445h6i7j7j8k8k99l:::;;;;;;< < < < <<<<<<<<<<====================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????[[[[[\\\\\\*]]++,˘fբCP"**.135h788999:::;;;;<<<<<<=p=p=p=p=p=p=p=p=p=p=p=p=p=p>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? --22667799::::;;;;<<<<<<==============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@$$$$$$$$$$VVVVV RN} Y&NJGzFEDDDDCvCvBBBAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%$$$$$$$$$$$$$$$$$$$$$###SRIחg4Z'SSNLJ}HGFFEEEDDDCvCvCvCvBuBuBuBuBuBuBuAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUTT칆Iܤqi)nbZUQOOMMKKJJI|H{GzGzFyFyFExEEEDDDDCCCCCCBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@UTTTSSSRRRQQPO⮮qks hh`[[VSSQQOMLKJIHHGGFFFEEExExDDDwDwDwDwCCCvCvCvCvCvCvCvCvBuBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@uullee__[[WWUURRPPOONNMMLLKKJJIIIIHHHHHHGGGGFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@wwppiidd__\\YYVVTTRRQQPPNNMMMMLLKKKKJJIIIIIIHHHHHHGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@𽽽\JGFECA?pmie,&yr kfb^[YVTSQPONMLLKJJJIIIHHHGGGGGFFFFFFFEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$################"""""""""""!!!!!!!!! ~}|yxܧڥأ֠ӝЙ̕ȑ^yysn;i6e2a.^+\)Y&W$V#T!S QPONNMLLK~JJ}J}II|HHHH{GGGGzFFFFFEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@WWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTSSSSSSSRRRRRRQQQQQPPPPOONNMLJ߫ުݩvtأ֡nkhʓƏŠytp=k8gc`^+[YX%V#U"SRQPOONMMLLKKJJIIIHHHHGGGGGFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@MLK~J}IHGFEDC@> 852/+'Vzuqlieb_][YWVUSRQPPONNMMLLKK~JJ}J}II|I|I|H{H{H{H{HHGzGzGzGzGzFyFyFyFyFyFyFFFFExExExExExExExEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s{{vvrrnnkkggddbb__]][[ZZXXWWUUTTSSRRQQQQPPOOOONNNNMMMMLLLLKKKKKKJJJJJJIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA{{wwsspplliiffddbb__]]\\ZZYYWWVVUUTTSSRRRRQQPPPPOONNNNMMMMMMLLLLKKKKKKKKJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA𽽽\칹븸귷鶶ުݩܨۧڦ ֢Ǒč􊽇|IxEtAq>nkhec0`^][ZXWVUTSS RQQPOONNNMMLLLLKKKJJJJIIIIIIHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA$$$$$$$$$$$$$$$$$$$$$$$$$$$#################"""""""""""!!!!!!!!! ~{zywvuts pomkige`]탃||yur oligdb`^][ZYWVUTTSRQQPOONNMMMLLLKKKJJJJIIIIIIHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTSSSSSSSSRRRRRRQQQQQPPPPPOOOONNNNMMMLLLKKKJJIIHGFFEDݪCBA@?ף֢;ӟ86420ǒ+(%"||yyurom:jh5f3d1b/`-^]*[ZY&X%W$V#UTSS RQQPPOONNMMMLLLKKKKKJJJJJIIIIIIIIHHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~~~}}||{{zzyExwvBA@?>po:l7531/`^[||yyvspnkigeca_^\[ZYXWVUTTSRRQQPPOONNNMMMMLLLK~K~K~KKJ}J}J}J}JJI|I|I|I|IIIIH{H{H{H{H{HHHHHHGzGzGzGzGzGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBuBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA||yywwttqqoolljjhhffddbbaa__^^]][[ZZYYXXWWWWVVUUTTTTSSRRRRQQQQPPPPOOOOOONNNNMMMMMMMMLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}}zzwwuurrppnnkkiiggffddbbaa__^^]]\\[[ZZYYXXWWVVUUUUTTSSSSRRRRQQQQPPPPPPOOOONNNNNNMMMMMMMMLLLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\칹븸귷鶶赵紴泳岲䱱㰰߬߫ޫުݩܩܨۧڧ ؤף֢աҞѝΚ˖ɔǒŐÎ}}zzxEuus@q>o=<;:987ϛ4310.,*(&$"}}{{xvvttqomkjhfecba`-^]\[ZYYXWVVUTTSSRRQQQQPPOOONNNNNMMMMMLLLLLKKKKKKKKJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAA~~}}}|||{{zzyyxxwwvuutssrqponmlkjhgfdca_]O}}{{yyvtrpnlkigfdcba_^]\[ZZYXWWVUUTTSSRRQQQPPPPOOOONNNNNMMMMMMLLLLLLLK~K~K~KKKKJ}J}J}J}JJJJJJI|I|I|I|IIIIIIIIH{H{H{H{H{HHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB}}{{yywwuussqqoommlljjhhggeeddccbb``__^^]]\\\\[[ZZYYXXXXWWVVVVUUUUTTTTSSSSRRRRRRQQQQPPPPPPOOOOOOOONNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB}}{{yywwuussrrppnnllkkiihhffeeddccbb``__^^]]]]\\[[ZZYYYYXXWWWWVVVVUUUUTTTTSSSSRRRRRRQQQQPPPPPPPPOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\칹븸귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߫ޫޫݪܩۧڧڦ٦٥ؤף֣֢աԠӟҞѝМϛΚ͘˗ʖɔǓƑĐ}}{{yyxEvCtArrppo>=<<;:987654321/.-+*(% }}||zzxxvtssqonlkjjhgfeecba`_^^]\[[[ZYYYXWWVVUUTTSSSSRRRRQQQQPPPPOOOOONNNNNNNMMMMMMMLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~~}}||{{{zzyyxxwvutssrqponmlkjihgfeda`]ZWR}||zzxwwuusrrpoomlljihggedcba`___^]\[[ZYYXXXWWWVVVUUUTTSSSSRRRRQQQQPPPPPPOOOOOONNNNNNNNMMMMMMMMLLLLLLLLLK~K~K~KKKKKKKKJ}J}J}J}JJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~||zzyywwuuttrrqqoonnllkkjjiiggffeeddccbbaa``____^^]]\\\\[[ZZZZYYXXXXWWWWVVVVUUUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB~~||zzyywwvvttssqqppnnmmllkkiihhggffeeddccbbaa``____^^]]\\\\[[ZZZZYYYYXXXXWWWWVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB칹븸귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬߬ޫުݪݪܩܩۨڧ٦إף֣աԡӟҞўϛΚ͙̘˗ǓƒőŽ~~||{HyyxEvvuBssr?ppo=<<;:9876543210/.-,('# ~~||{{yyxxvuuttrqqppnmllkkjjhgfedddccbba`___^]]]\[[[ZZZYYYXXXWWWVVUUUUTTTTSSSSRRRRQQQQQQPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~~}}}||{{{zzyyxxwwvuuttsrqqponnmlkjih^]YXQ~~||{{yxxwwuttssqpoonnlkjihgfedcbbba`___^]]]\[[ZZZYYYXXXWWWWVVVUUUUTTTTTSSSSRRRRQQQQQPPPPPPPOOOOOOOONNNNNNNNMMMMMMMMMMMLLLLLLLLLLLK~K~K~K~KKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~||{{zzxxwwvvttssrrqqoonnmmllkkjjiihhggffeeddccbbbbaa``____^^]]]]\\\\[[ZZZZYYYYXXXXXXWWWWVVVVVVUUUUTTTTTTTTSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}{{zzyywwvvuussrrqqppoonnllkkjjiihhggffffeeddccbbbbaa``____^^]]]]\\\\[[[[ZZZZYYYYXXXXWWWWWWVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC칹븸귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩۨۨڧڧ٦إפ֣բԠӠҞўМϜ͙̘˗ʗώ~~}}{{zzyywwvvuutAs@qqppoonnm:l9k8j7i6h5g4ffeee2d1c0bbaaa.`-___,^+^+]*\\\)[[[(ZZZ'YYY&XXX%X%WWW$VVV#V#UUU"U"TTT!T!SSSSS S RRRRRRQQQQQQPPPPPPPPOOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC""!!!!!!!!!  ~~}}{{zzyyxwuuttssr q p ommllkkjjjihgeeeddcbaaa`___^^^]\\\[[[ZZZYYYXXXXWWWVVVVUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCTTTTTTTTTSSSSSSSRRRRRRRQQQQQPPPPPOOOONNNNMMMMLLLKKKJJJIIIHHGGFFEEDDCCBBAA@?>>=<;;:9754210/.('&%~~}}{zzyyxxwwuttssrrqqppoomlkjjjiihhggfedddccbaaa`___^^^]\\[[ZZYYYYXXXWWWWVVVVVUUUUUTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~~}}||{{zzyyxxwwvuttsrqqpponmlkjihgfea`_^]UT~~}}||zyyxxwwvvtsrqqppoonnmmllkkjihgggffedcccbaaa`___^^^]]]\\\[[[ZZZYYYXXXXWWWVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{yyxxwwvvuuttssqqppoonnmmllkkkkjjiihhggffffeeddccccbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYYYXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~~}}||{{yyxxwwvvuuttssrrqqppoonnmmllkkjjiihhhhggffeeeeddccbbbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCC칹븸귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧڧ٦٦إפפ֣բԡӠҟѝНϛΛ͙̙ʖɖőĐώ~~}}||{{zGyFwwvvuuttssrrqqppoonnmmlll9k8j7i6hhggg4f3eeddd1c0bbb/aaa.`-___,^^^+]]]*\\\)[[[(ZZZ'Z'YYY&XXXXX%WWWWW$VVVVV#UUUUU"U"TTTTT!T!SSSSS S RRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD!!!!!!!!!  耀~~}}||{{zzyyxwvut s r q p onmllkkjjiiihggffedddcbbbaaa`___^^^]]]\\\[[[ZZZZYYYXXXXWWWWVVVVUUUUUUTTTTTTSSSSSSSRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDTTTTTTTTTSSSSSSSRRRRRRRQQQQQQPPPPPOOOONNNNMMMLLLKKJJIIHHHGGFFEEDDCBBAA@@??>==<;:987654320/,+*)~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmllkjiiihhgfffeedddccbbbaaa``___^^^]]]\\\[[[[ZZZYYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTSSSSSSSSRRRRRRQQQQQQQQQPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~~}}}||{{zzyyxxwwvutsrqpponmljigedba`\[ZYXWVUTSRQ~~}}||{{zzyyxxwwvvuuttssrrqqppoonmlllkkjjihhhggfeeedcccbbba```___^^^]]]\\\\\[[[ZZZYYYYXXXXWWWWVVVVVVUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLK~K~K~K~KKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvuuttssrrqqppoooonnmmllkkjjjjiihhggggffeeeeddccccbbbbaa````____^^^^]]]]\\\\\\[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}||{{zzyyxxwwvvuuttssssrrqqppoonnmmllllkkjjiiiihhggffffeeddddccccbbbbaa````____^^^^]]]]]]\\\\[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD칹븸귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩۨۨڧ٦٦إפ֣բԡԡӠҟўМϜΛ͚˘ɕȕƒŒŽ~~}}||{{zzyyxxwwvvvCuBtAssrrqqppoonnn;m:llkkk8j7iihhh5g4fff3e2ddd1ccc0bbaaa.```-___,^^^+]]]]]*\\\)[[[([(ZZZ'Z'YYY&XXXXX%X%WWW$W$VVVVV#V#UUUUU"U"TTTTT!T!SSSSSSS S RRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD  򋋊~~}}||{{zzyyyxwvvuuttssrrqqppp onnmmlllkkjjiiihhgggffeedddcccbbaaa```___^^^]]]]\\\[[[[[ZZZZZYYYXXXXXXWWWWWVVVVVVUUUUUUUTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDSSSSSSSSRRRRRRQQQQQPPPPPOOOONNNNMMMLLLLKKKJJJIIIHHGGFFEEDDCCBBA@@??>>=<;::9876431/-,)$#~~}}||{{zyyyxxwwvvuuttssrrqpppoonnmlllkkjiiihhgggfeeedddcbbbaaa```___^^^^^]]]\\\[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD~~}}|||{{zzyyxxwwvvuutsrqponmlkjigfdb_\[VU~~}}||{{{zzyyxxwwvvuuttsrrrqqppoonnnmmllkkkjjihhhgggffeeedddccbbbaaa```___^^^^^]]]\\\\\[[[ZZZZYYYYXXXXXXWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDD~~}}||||{{zzyyxxwwvvuuttttssrrqqppoooonnmmllllkkjjjjiihhhhggffffeeeeddccccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDD~~}}}}||{{zzyyxxwwvvvvuuttssrrqqqqppoonnnnmmllkkkkjjiiiihhggggffffeeddddccccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE칹븸귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧڧ٦إإפפ֣֣բԡӠҟўНϜΛ͚̙ʗȕǓƓđ~~}}}}||{{zzyyxxwwwDvvuuttssrrr?qqppooo=<;:987654321/-+)&"~~~~}}||{{zzyyxxxwwvvuutttssrrqqpppoonmmmllkkkjjiiihhhggfffeeedddcccbbbaaa```_____^^^]]]]]\\\[[[[ZZZZZZYYYYYXXXXXXXWWWWWVVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~}}||{{zzyxxwwvvuttsrrqpoonmlkjfedb`^\YU~~~~}}||{{zzyyyyxxwwvvuuuttssrrqqqppooonnmmlllkkjjjiiihhgggfffeeeddddccccbbbbaaa```_____^^^]]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~~}}||{{zzyyyyxxwwvvvvuuttssrrrrqqppppoonnmmmmllkkkkjjjjiihhhhggggffffeeddddccccbbbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXWWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~~~~}}||{{zzzzyyxxwwvvvvuuttssssrrqqqqppoonnnnmmllllkkkkjjiiiihhhhggffffeeeeddddccccbbbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE븸귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧ٦إإפ֣բԡӠҟўНϜΛ͚̙˘ȕǔŒÐ~~~~}}||{{{HzzyyxxwwwDvvuutttAssrrqqqqppooo>=<;:9876543210/,$~~~~}}||{{{{zzyyxxxxwwvvuuuuttssrrrqqpppoonnnmmlllkkkjjiiihhhgggfffeeedddcccbbbaaaaa```_____^^^^^]]]\\\\\\[[[[[ZZZZZYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEE~~~}}}||{{zzyyxxwwvvuttssrqqponmlkjiba_^\ZUR~~~}}||{{{zzyyxxxwwvvuuuttsssrrqqqppooonnmmmllllkkjjjiiihhhhggggffffeeeeddddcccbbbaaaaa```_____^^^^^]]]]]\\\\\[[[[[ZZZZZZZYYYYYXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExEx~~}}||||{{zzyyyyxxwwvvvvuuttttssrrrrqqppppoonnnnmmllllkkkkjjjjiihhhhggggffffeeeeddddddccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}||||{{zzyyyyxxwwwwvvuuttttssrrrrqqppppoooonnmmmmllllkkjjjjiiiihhhhggggffffeeeeddddccccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[[[ZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF븸귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧ٦٦إإפפ֣բբԡӠҟўНϜΛ͚̙˘ʗɖȕǔƓđÐ~~}}||||{{zzyyyyxxwwwwvvuuuBttsss@rrqqq>ppoooonnn;mmllllkkk8jjj7iii6hhh5ggg4fff3eee2ddd1ccccbbbbb/aaaa`````-_____,^^^^^+]]]]]*\\\\\)[[[[[[[(ZZZZZ'Z'YYYYY&Y&XXXXX%X%WWWWWWW$W$VVVVVVV#V#UUUUUUU"U"TTTTTTTTT!T!SSSSSSSSS S RRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 󌌋񊊉~~}}||||{{zzzyyxxxwwvvuuuuttssssrrr qqppp oonnnnmmmlllkkjjjjiiiihhhhggggffffeeeedddcccbbbbbaaa`````_____^^^^^]]]]]\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFRRRRRRQQQQQQPPPPOOOOONNNNMMMMLLLKKKJJJIIIHHGGGFFEEDCCBA@@?>==<;::98754210/.*'%~~}}||||{{zzzzyyxxxxwwvvuuuttsssrrrrqqpppoonnnmmmllllkkjjjiiihhhgggfffeeeeeddddcccbbbbbaaa`````_____^^^^^]]]]]\\\\\\\[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}||{{zzyyxxwvvuutssrrqponmlkjigfedcba`_\WUS~~}}|||{{zzzzyyxxxxwwvvvvuutttssrrrqqqqppooonnnnmmlllkkkjjjiiihhhgggfffeeedddddcccbbbbbaaa`````_____^^^^^]]]]]\\\\\\[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}}}||{{zzzzyyxxxxwwvvvvuuuuttssssrrqqqqppppoonnnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeeddddddccccbbbbbbaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~~}}}}||{{{{zzyyyyxxwwwwvvuuuuttssssrrrrqqppppoooonnnnmmllllkkkkjjjjiiiihhhhggggffffffeeeeddddccccccbbbbbbaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫݪݪܩܩۨۨڧڧ٦٦إפ֣բբԡӠҟўНϜ͚̙ʗɖȕǔƓŒđ~~}}}}||{{{{zzyyyyxxwwwwvvuuuutttAssrrrrqqq>ppoooonnnnmmm:lll9kkk8jjj7iii6hhh5ggg4ffffeeeee2ddd1ccccc0bbbbaaaaa.`````-_____,^^^^^+]]]]]]]*\\\\\)[[[[[[[(ZZZZZZZ'YYYYYYY&Y&XXXXXXX%WWWWWWWWW$VVVVVVVVV#V#UUUUUUUUU"U"TTTTTTTTT!T!SSSSSSSSSSS S RRRRRRRRRRRRRRQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFF 򋋊넄~~}}}}||{{{{zzyyyyxxwwwwvvvuuttttsss rrqqqqppp ooonnmmmmllllkkkkjjjjiiiihhhhhgggffeeeeedddcccccbbbaaaaa`````_____^^^^^]]]]]]\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFQQQQQPPPPPOOOOONNNMMMMLLLKKKJJIIHHHGGFFEEDCCBAA@@?>=<;:987654320/.-,+'&#~~}}}}||{{{{zzyyyyxxwwwvvvvuutttssssrrqqqppppoooonnmmmlllkkkjjjiiihhhhhggggfffeeeeeddddcccccbbbbaaaaa`````_____^^^^^^^]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFF~~~}}}||{{{zzyyxxwwvvutssrqponmlkjigedc]\XU~~}}}}||{{{{zzyyyxxxxwwvvvuuuuttsssrrrrqqpppooonnnmmmmllllkkkkjjjjiiihhhgggggffffeeedddddcccbbbbbaaaaa`````_____^^^^^^^]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYYXXXXXXXWWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||{{{{zzzzyyxxxxwwwwvvuuuuttttssrrrrqqqqppppoooonnmmmmllllkkkkjjjjjjiiiihhhhggggffffffeeeeddddddccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||||{{zzzzyyxxxxwwwwvvuuuuttttssssrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhhhggggffffeeeeeeddddccccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG귷鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧ٦إפפ֣բԡӠҟНΛ͚˘ɖȕǔ~~}}}}||||{{zzzzyyyFxxwwwwvvvCuuttttssssrrr?qqq>ppoooonnnnmmmmllllkkkkk8jjj7iii6hhhhggggg4fff3eeeee2ddd1ccccc0bbbbb/aaaaa.`````-_____,^^^^^^^+]]]]]*]*\\\\\)\)[[[[[([(ZZZZZZZ'YYYYYYY&Y&XXXXXXX%X%WWWWWWWWW$VVVVVVVVV#V#UUUUUUUUUUU"TTTTTTTTTTTTT!SSSSSSSSSSSSS S RRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG 񊊉톆ꃃ~~}}}}||||{{zzzzyyyyxxwwwwvvvvuuuttssssrrrrqqqqppp ooonnnmmmlllkkkkjjjjiiiiihhhggggffffeeeeeddddcccccbbbbbaaaaa`````_____^^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGQQPPPPOOOOONNNMMMMLLLKKKJJJIIIHHGGFFEEDCCBA@?>=<;:98765431/.-&%!~~}}}}||||{{zzzyyyyxxwwwvvvvuuuuttsssrrrqqqqppppoooonnnnmmmmllllkkkkjjjiiiiihhhhgggfffffeeedddddcccccbbbbbaaaaa`````_____^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}|||{{zzyyxxwvvuuttsrrqponmlkjigfdc`_^]\[~~}}}}||||{{{{zzyyyyxxxxwwvvvuuuuttttssssrrqqqpppooonnnmmmlllkkkkkjjjjiiiihhhhhggggfffffeeeedddddcccccbbbbbaaaaa`````_______^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZZYYYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~}}}}||||{{{{zzyyyyxxxxwwwwvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiiihhhhggggggffffeeeeeeddddddccccccbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGG~~~~}}||||{{{{zzyyyyxxxxwwwwvvvvuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiihhhhhhggggffffffeeeeeeddddddccccbbbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGG鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨڧ٦إפ֣֣բԡӠҟўНϜΛ͚̙ʗȕŒđЏ~~~~}}||||{{{{zzzGyyxxxxwwwwvvvvuuuBtttAsss@rrr?qqq>ppp=ooo=<;;:98763210/.,+*! ~~~~}}||||{{{{zzzzyyyyxxwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmlllllkkkkjjjjiiiiihhhhgggggfffffeeeedddddcccccbbbbbaaaaaaa`````_______^^^^^]]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~}}}||{{zzyxxwwvvuuttsrqponmlkihfeca]\[ZYXWV~~~~}}|||{{{{zzzzyyyyxxxxwwvvvuuutttsssrrrqqqpppooonnnnnmmmmllllkkkjjjjjiiiihhhhhgggggffffeeeeedddddcccccbbbbbaaaaaaa`````_______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[[ZZZZZZZYYYYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}}}||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiiiihhhhhhggggffffffeeeeeeddddddccccccbbbbbbaaaaaaaa``````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~~}}}}||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnnmmmmllllkkkkkkjjjjiiiiiihhhhggggggffffffeeeeeeddddddccccccbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH鶶赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩۨڧڧ٦٦إפ֣բԡӠҟўНϜΛ͚̙˘ɖȕƓŒÐ~~~~}}}}|||I{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppp=ooo=<<;:986510.-,*)%$#"! ~~~~}}}}||||{{{{zzyyyxxxwwwvvvuuutttsssssrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjiiiiihhhhhhggggfffffeeeeedddddcccccbbbbbbbaaaaa```````_______^^^^^^^]]]]]]]\\\\\\\\\[[[[[[[[[ZZZZZZZZZYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~~~}}||{{zzyyxwvvutsrrqqponmlkjhgfca_\~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqpppooooonnnnmmmmlllllkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddcccccbbbbbbbaaaaa```````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[[ZZZZZZZZZYYYYYYYYYYYXXXXXXXXXXXWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHH~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqqqppppoooonnnnmmmmmmllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHH~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssssrrrrqqqqppppoooooonnnnmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHH赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨڧ٦٦إפ֣բԡԡӠҟўНϜΛ͚̙˘ʗɖȕƓđ~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuutttttAssssrrrrqqqqppppp=oooonnnnmmmmm:llllkkkkk8jjjjj7iiiihhhhhhggggg4fffff3eeeee2ddddddd1ccccc0bbbbbbaaaaaaa.```````-_______,^^^^^^^+]]]]]]]]]*\\\\\\\)\)[[[[[[[([(ZZZZZZZZZ'YYYYYYYYYYY&XXXXXXXXXXX%WWWWWWWWWWW$W$VVVVVVVVVVV#V#UUUUUUUUUUUUU"U"TTTTTTTTTTTTTTT!SSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII 􍍍~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuttttssssrrrrqqqqq ppppoooonnnnnmmmmlllllkkkkkjjjjiiiiihhhhhgggggffffeeeeedddddddccccccbbbbbaaaaaaa```````_______^^^^^^^]]]]]]]]]\\\\\\\\\[[[[[[[[[ZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIOOOONNNNMMMLLLKKKJJJIIHHGGFFEEDDCCBBA@@??>==<<;:98753210-+)&!~~~~}}}}||||{{{{zzzzyyyyxxxxwwwvvvvvuuuuttttssssrrrrqqqqqppppoooonnnnnmmmmlllllkkkkkkjjjjiiiiihhhhhgggggfffffeeeeedddddcccccccbbbbbaaaaaaa```````_______^^^^^^^]]]]]]]]\\\\\\\\\[[[[[[[[[ZZZZZZZZZZZYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~}}}|||{{zzyyxwwvuutsrqponmlkjihgfedcb`^\YTS~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuuttttsssrrrrrqqqqppppooooonnnnmmmmmllllkkkkkjjjjjiiiiiihhhhhhggggggffffffeeeeeedddddcccccccbbbbbbaaaaaaa```````_______^^^^^^^^^]]]]]]]\\\\\\\\\[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvuuuuttttttssssrrrrqqqqppppppoooonnnnnnmmmmllllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeeeddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII~~~~}}}}||||{{{{{{zzzzyyyyxxxxwwwwvvvvuuuuuuttttssssrrrrqqqqqqppppoooooonnnnmmmmmmllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII赵紴泳岲䱱㰰⯯ᮮ୭߬߬ޫݪݪܩۨڧڧ٦إإפ֣բԡӠӠҟўНϜ͚˘ʗɖȕŒ~~~~}}}}||||{{{{{{zzzzyyyyxxxxwwwwvvvvvCuuuuttttssssrrrrr?qqqqppppp=oooonnnnn;mmmmlllll9kkkkk8jjjjj7iiiihhhhhhh5ggggg4fffff3eeeee2ddddddd1ccccc0bbbbbbb/aaaaaaa.```````-_______,^^^^^^^^^+]]]]]]]]]*\\\\\\\\\)[[[[[[[[[(ZZZZZZZZZZZ'YYYYYYYYYYY&XXXXXXXXXXX%X%WWWWWWWWWWW$W$VVVVVVVVVVVVV#UUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII 򋋋~~~~}}}}|||||{{{{zzzzyyyyxxxxwwwwwvvvvuuuuttttsssss rrrrqqqqq ppppooooonnnnmmmmmlllllkkkkjjjjjjiiiiihhhhhhggggggffffffeeeeedddddddccccccbbbbbbbaaaaaaa```````_______^^^^^^^^^]]]]]]]]]\\\\\\\\\[[[[[[[[[ZZZZZZZZZZZYYYYYYYYYYYXXXXXXXXXXXXXWWWWWWWWWWWWWVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIINNNNMMMMLLLKKKJJJIIIHHGGFFEEDDCCBBA@??>>=<;:9876532,+)'$!~~~~}}}}||||||{{{{zzzzyyyyxxxxwwwwwvvvvuuuuttttsssssrrrrqqqqqppppoooooonnnnmmmmmllllllkkkkjjjjjiiiiihhhhhgggggfffffffeeeeeedddddcccccccbbbbbbbaaaaaaa```````_______^^^^^^^^^]]]]]]]]]\\\\\\\\\[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIII~~~}}||{{zzyyxwvuuttsrqppoonmlkjihgedcba^\ZT~~~~}}}}|||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuutttttssssrrrrrrqqqqppppooooonnnnnnmmmmlllllkkkkkjjjjjjiiiiiihhhhhhggggggfffffeeeeeeeddddddcccccccbbbbbbbaaaaaaa```````_______^^^^^^^^^]]]]]]]]]\\\\\\\\\[[[[[[[[[[[ZZZZZZZZZZZYYYYYYYYYYYXXXXXXXXXXXXXWWWWWWWWWWWWWVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIII~~~~}}}}}}||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuuuuttttssssrrrrrrqqqqppppppoooonnnnnnmmmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIII~~~~}}}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvvvuuuuttttssssssrrrrqqqqqqppppoooooonnnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJII紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨڧڧ٦إإפ֣֣բԡӠҟўΛ͚̙˘ȕǔÐ~~~~}}}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvvvuuuutttttAssssrrrrr?qqqqppppp=oooonnnnnnmmmmm:lllll9kkkkk8jjjjj7iiiii6hhhhh5ggggg4ffffffeeeeeee2ddddddd1ccccccbbbbbbbbaaaaaaaaa.```````-_______,_,^^^^^^^+]]]]]]]]]*\\\\\\\\\\\)[[[[[[[[[([(ZZZZZZZZZ'Z'YYYYYYYYYYY&XXXXXXXXXXXXX%WWWWWWWWWWWWW$W$VVVVVVVVVVVVV#V#UUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ 󌌌񊊊~~~~}}}}}}||||{{{{zzzzzyyyyxxxxwwwwwvvvvuuuuttttttssssrrrrrrqqqqq ppppooooonnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhgggggggffffeeeeeeedddddddccccccbbbbbbbaaaaaaaaa```````_________^^^^^^^]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[ZZZZZZZZZZZYYYYYYYYYYYXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJNNNNMMMMLLLKKKJJJIIIHHGGGFFEEDDCBBAA@?>=<;:98765321*)'!~~~~}}}}}||||{{{{zzzzzzyyyyxxxxwwwwwwvvvvuuuutttttssssrrrrrqqqqqqppppooooonnnnnnmmmmlllllkkkkkjjjjjiiiiihhhhhgggggggffffffeeeeeeedddddcccccccbbbbbbbaaaaaaaaa```````_________^^^^^^^]]]]]]]]]]\\\\\\\\\[[[[[[[[[[[ZZZZZZZZZZZYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~}}|||{{zzyyxwwvuttssrqpponmlkjihfdcba`_\YW~~~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwwvvvvuuuuuttttsssssrrrrqqqqqppppppoooonnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhgggggfffffffeeeeeeeddddddcccccccbbbbbbbaaaaaaaaa````````_________^^^^^^^^^]]]]]]]]]\\\\\\\\\[[[[[[[[[[[ZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~~~~~}}}}||||{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqppppppoooooonnnnmmmmmmllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggffffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~~~~~~}}}}||||{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqqqppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ紴泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩۨۨڧ٦٦إإפ֣բԡӠҟўНϜΛ͚̙ʗȕǔƓŒđÐ~~~~~~}}}}||||{{{{{{zzzzyyyyyFxxxxwwwwwDvvvvuuuuuBttttsssss@rrrrqqqqqqppppp=ooooo>=<;:9876210.-,+*&!~~~~~~}}}}||||||{{{{zzzzyyyyyyxxxxwwwwwwvvvvuuuuuttttsssssrrrrrrqqqqpppppooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiihhhhhhhggggggfffffffeeeeeeeddddddddccccccccbbbbbbbbaaaaaaa`````````_________^^^^^^^^^]]]]]]]]]\\\\\\\\\\\[[[[[[[[[[[ZZZZZZZZZZZZZYYYYYYYYYYYYYXXXXXXXXXXXXXWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJ~~~}}}||{{{zzyyxwwvuutssrqponmkihgecb\[X~~~~~~}}}}||||||{{{{zzzzyyyyyxxxxwwwwwvvvvvvuuuuttttttssssrrrrrqqqqqqppppppoooonnnnnmmmmmlllllkkkkkjjjjjjjiiiiiihhhhhgggggggfffffffeeeeeedddddddcccccccbbbbbbbbbaaaaaaa`````````_________^^^^^^^^^]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJ~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssssrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJ~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppoooooonnnnnnmmmmmmllllllkkkkkkkkjjjjjjiiiiiihhhhhhhhggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK泳岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧڧ٦٦إפפ֣բԡӠӠҟўНϜΛ͚̙˘ɖǔƓ~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwwDvvvvuuuuuutttttAssssrrrrrrqqqqqqppppp=ooooo=<;:9876510.-%~~~~~~}}}}|||||{{{{{{zzzzyyyyyyxxxxwwwwwwvvvvvvuuuuttttttssssssrrrrqqqqqpppppooooonnnnnmmmmmlllllkkkkkkkjjjjjjiiiiiiihhhhhhgggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbaaaaaaaaa`````````_________^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\[[[[[[[[[[[[[ZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~}}||{{zzyyxxwwvuutssrqqponmljhgfdcb`_^]\[ZVS~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxwwwwwvvvvvvuuuutttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkjjjjjjjiiiiiihhhhhhhggggggfffffffeeeeeeedddddddcccccccccbbbbbbbaaaaaaaaa`````````_________^^^^^^^^^^^]]]]]]]]]\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZYYYYYYYYYYYYYXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqppppppoooooonnnnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩۨۨڧڧ٦إפ֣բԡӠҟўНϜΛ͚̙˘ʗɖǔŒđ~~~~~~}}}}}}||||{{{{{{zzzzzGyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqq>ppppp=ooooo>==<;:98765321.,+"~~~~~}}}}}}||||{{{{{zzzzzzyyyyxxxxxwwwwwwvvvvvvuuuuuuttttsssssrrrrrqqqqqpppppooooonnnnnnnmmmmmmllllllkkkkkkkjjjjjjiiiiiiihhhhhhgggggggfffffffeeeeeeedddddddddcccccccbbbbbbbbbaaaaaaaaa`````````_________^^^^^^^^^^^]]]]]]]]]]]\\\\\\\\\\\[[[[[[[[[[[[[ZZZZZZZZZZZZZYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKK~~}}}|||{{zzyyxxwwvuttsrqponmmlkjihgfedca^]WO~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmlllllllkkkkkkjjjjjjjiiiiiihhhhhhhgggggggfffffffeeeeeeedddddddddccccccccbbbbbbbbbaaaaaaaaa`````````_________^^^^^^^^^^^]]]]]]]]]]]\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKK~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKK~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL岲䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧ٦إפפ֣բբԡӠҟўНϜΛ͚˘ʗɖǔƓÐ~~~~}}}}}}||||||{{{{{HzzzzyyyyyyxxxxxxwwwwwwvvvvvCuuuuuBtttttAsssss@rrrrr?qqqqq>ppppp=oooooonnnnnnmmmmmmm:llllllkkkkkkk8jjjjjjiiiiiiiihhhhhhh5ggggggg4fffffff3eeeeeeeee2ddddddd1ccccccccc0bbbbbbbbaaaaaaaaaaa.`````````-_________,^^^^^^^^^^^+]]]]]]]]]]]*\\\\\\\\\\\\\)[[[[[[[[[[[[[(ZZZZZZZZZZZZZ'YYYYYYYYYYYYYYY&XXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 􍍍~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppooooooonnnnnmmmmmmlllllllkkkkkkjjjjjjjiiiiiiihhhhhhhgggggggffffffeeeeeeeeeddddddddcccccccccbbbbbbbbaaaaaaaaaaa``````````_________^^^^^^^^^^^]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[ZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKJJJIIIHHGGFFEEDDCBBA@?>=<;;:98765432.*&%$#"~~~~}}}}}}||||||{{{{{{zzzzyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppooooooonnnnnnmmmmmmlllllllkkkkkkjjjjjjjiiiiiiihhhhhhhhggggggggffffffffeeeeeeedddddddddcccccccbbbbbbbbbaaaaaaaaa```````````_________^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~~~}}||{{zzyyxxwwvvuuttsrrqponmlkjihgedca`_]~~~~}}}}}}||||||{{{{{{zzzzzzyyyyxxxxxwwwwwvvvvvvuuuuuuttttttssssssrrrrrqqqqqpppppppoooooonnnnnnmmmmmmmllllllkkkkkkkjjjjjjjjiiiiiihhhhhhhgggggggfffffffffeeeeeeeedddddddddccccccccbbbbbbbbbaaaaaaaaa```````````___________^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLL䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧڧ٦٦إפ֣֣բբԡӠҟўНϜ͚̙˘Ɠ~~~~~K}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqq>ppppppoooooonnnnnnn;mmmmmmlllllll9kkkkkkk8jjjjjjiiiiiiiihhhhhhhhggggggggg4fffffff3eeeeeeeeddddddddd1ccccccccc0bbbbbbbbb/aaaaaaaaa.```````````-___________,^^^^^^^^^^^+]]]]]]]]]]]*\\\\\\\\\\\\\)[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLL 񊊊톆셅~~~~~~}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttsssssss rrrrr qqqqqqppppppooooooonnnnnnmmmmmmmllllllkkkkkkkkjjjjjjjiiiiiiihhhhhhhggggggggffffffffeeeeeeedddddddddcccccccccbbbbbbbbbaaaaaaaaa```````````___________^^^^^^^^^^^]]]]]]]]]]]\\\\\\\\\\\\\[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKJJIIHHGGFFEEDDCCBBAA@@??>>=<;:9876543210/.-*'~~~~~~}}}}|||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuutttttsssssssrrrrrrqqqqqqppppppooooooonnnnnnmmmmmmmllllllkkkkkkkjjjjjjjiiiiiiihhhhhhhgggggggfffffffffeeeeeeeedddddddddcccccccccbbbbbbbbbaaaaaaaaa```````````___________^^^^^^^^^^^]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLL~~~}}}|||{{zzyyxxwwvvutsrqponmlkjihf`_]ZY~~~~~~}}}}}}||||{{{{{zzzzzyyyyyyxxxxxxwwwwwvvvvvuuuuuuuttttttssssssrrrrrrqqqqqqpppppppoooooonnnnnnnmmmmmmlllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggfffffffeeeeeeeeedddddddddccccccccccbbbbbbbbbbaaaaaaaaa```````````___________^^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\[[[[[[[[[[[[[ZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLL~~~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLL~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM䱱㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧڧ٦٦إפפ֣բԡԡӠҟўНϜΛ͚̙ʗɖŒđ~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuutttttttAssssssrrrrrrqqqqqqq>ppppppoooooonnnnnnnnmmmmmmm:lllllll9kkkkkkjjjjjjjjiiiiiiiihhhhhhhhh5ggggggg4fffffffff3eeeeeee2ddddddddd1ccccccccc0bbbbbbbbbbaaaaaaaaaaa.```````````-___________,^^^^^^^^^^^+]]]]]]]]]]]]]*\\\\\\\\\\\\\)[[[[[[[[[[[[[([(ZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 󌌌톆~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuttttttssssssrrrrrrr qqqqqqppppppooooooonnnnnnnmmmmmmllllllllkkkkkkkjjjjjjjiiiiiiihhhhhhhhgggggggggffffffffeeeeeeeedddddddddcccccccccbbbbbbbbbaaaaaaaaaaa```````````___________^^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMKKKJJJIIIHHGGFFEEDDCBBAA@@??>=<;:987654210/-,+)'%~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuttttttssssssrrrrrrrrqqqqqqppppppooooooonnnnnnnnmmmmmmlllllllkkkkkkkjjjjjjjiiiiiiihhhhhhhgggggggggffffffffeeeeeeeeedddddddddcccccccccbbbbbbbbbaaaaaaaaaaa```````````___________^^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM~~~}}||{{zzyyxxwwvvutsrrqqponmlkihgeb^XRQ~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvvuuuuuuttttttssssssrrrrrrrqqqqqqpppppppoooooonnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhgggggggfffffffffeeeeeeeeedddddddddcccccccccbbbbbbbbbaaaaaaaaaaa```````````___________^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\\[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMM~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeddddddddddccccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMM㰰⯯ᮮ୭߬߬ޫޫݪݪܩܩۨۨڧ٦إפ֣բԡӠҟўНϜΛ͚̙˘ɖЏ~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyyFxxxxxxwwwwwwvvvvvvuuuuuuuBttttttssssssrrrrrrrrqqqqqqppppppppooooooo>==<;:98764310/)&# ~~~~~~}}}}}}||||||{{{{{{zzzzzzzyyyyyyxxxxxxwwwwwwvvvvvvvuuuuuuttttttsssssssrrrrrrqqqqqqqppppppooooooonnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiihhhhhhhhhggggggggfffffffffeeeeeeeeedddddddddcccccccccbbbbbbbbbbbaaaaaaaaaaa```````````___________^^^^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMM~~~}}}||{{zzzyyxxwwvvuuttsrqpoonnmlkjihgfdba`_^[YS~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuutttttttssssssrrrrrrrrqqqqqqpppppppoooooooonnnnnnmmmmmmmlllllllkkkkkkkjjjjjjjjjiiiiiiiihhhhhhhhgggggggggfffffffffeeeeeeeeeeddddddddddcccccccccbbbbbbbbbbbaaaaaaaaaaa```````````___________^^^^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMM~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrrrqqqqqqqqppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN㰰⯯ᮮ୭߬߬ޫޫݪܩܩۨۨڧڧ٦إإפפ֣բԡӠҟНϜΛ͚ʗɖƓŒđÐ~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyyFxxxxxxwwwwwwvvvvvvvCuuuuuutttttttAssssssrrrrrrr?qqqqqqppppppppooooooo==<<;:987654321/-,+*)&~~~~~~}}}}}}|||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwvvvvvvvuuuuuutttttttssssssrrrrrrrqqqqqqqqppppppooooooonnnnnnnmmmmmmmlllllllkkkkkkkkkjjjjjjjjiiiiiiihhhhhhhhhgggggggggffffffffffeeeeeeeeeedddddddddcccccccccccbbbbbbbbbbaaaaaaaaaaa`````````````___________^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN~~}}|||{{{zzyyxxwvvuttsrrqponmlkjhgfedba[XVTQ~~~~~~}}}}}}}}||||||{{{{{{zzzzzzyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjiiiiiiiihhhhhhhhhggggggggfffffffffeeeeeeeeeeeddddddddddcccccccccbbbbbbbbbbbaaaaaaaaaaa`````````````___________^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNN~~~~~~}}}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNN⯯ᮮ୭߬߬ޫޫݪܩܩۨۨڧ٦٦إإפ֣բԡӠҟўНϜΛ͚̙˘ʗȕǔƓ~~~~~~}}}}}}}}||||||{{{{{{{Hzzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttsssssss@rrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkk8jjjjjjjjiiiiiiiii6hhhhhhhhh5ggggggggg4fffffffff3eeeeeeeee2ddddddddddccccccccccc0bbbbbbbbbbb/aaaaaaaaaaa.`````````````-___________,_,^^^^^^^^^^^^^+]]]]]]]]]]]]]*\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWW$VVVVVVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNN 􍍍~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyxxxxxxwwwwwwwvvvvvvuuuuuuuttttttssssssssrrrrrrr qqqqqqq ppppppp ooooooonnnnnnnmmmmmmmlllllllkkkkkkkkjjjjjjjjjiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeedddddddddcccccccccccbbbbbbbbbbbaaaaaaaaaaa`````````````_____________^^^^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNIIIHHGGFFEEDDCCBAA@@?>=<<;:9876543/-*)$~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttsssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkjjjjjjjjjiiiiiiiiiihhhhhhhhgggggggggfffffffffeeeeeeeeeeeddddddddddcccccccccccbbbbbbbbbbbaaaaaaaaaaa`````````````_____________^^^^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNN}}||{{{zzyyxxwwvvutssrqponmlkjihfedb`_ZYVTR~~~~~~}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuttttttttssssssrrrrrrrqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmlllllllllkkkkkkkkjjjjjjjiiiiiiiiihhhhhhhhhgggggggggfffffffffeeeeeeeeedddddddddddcccccccccccbbbbbbbbbbbaaaaaaaaaaa`````````````_____________^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNN~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONN~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO⯯ᮮ୭߬߬ޫޫݪܩۨڧ٦٦إإפפ֣֣բԡӠҟўНϜΛ͚̙˘ȕǔŒđ~~~~~~~~}}}}}}|||||||I{{{{{{zzzzzzzGyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuBtttttttAssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmm:llllllllkkkkkkkkk8jjjjjjjjiiiiiiiii6hhhhhhhhh5ggggggggg4ffffffffffeeeeeeeeeee2ddddddddddccccccccccc0bbbbbbbbbbbbaaaaaaaaaaaaa.`````````````-_____________,^^^^^^^^^^^^^+]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWW$VVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO 󌌌򋋋셅~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwwvvvvvvuuuuuuuuttttttttsssssss rrrrrrr qqqqqqq ppppppp ooooooonnnnnnnmmmmmmmmlllllllllkkkkkkkkjjjjjjjjjiiiiiiiiihhhhhhhhhgggggggggffffffffeeeeeeeeeeedddddddddcccccccccccbbbbbbbbbbbaaaaaaaaaaaaa`````````````_____________^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOIIHHHGGFFEEDDCCBA@@?>=<;:98654210/.,('"~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxwwwwwwwwvvvvvvuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmlllllllllkkkkkkkkjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeddddddddddcccccccccccbbbbbbbbbbbaaaaaaaaaaaaa`````````````_____________^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO}}}||{{zzyyxxwvvuutssrrqpponnmlkjihgfba_^W~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuutttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnmmmmmmmmmllllllllkkkkkkkkkjjjjjjjjiiiiiiiiihhhhhhhhhgggggggggfffffffffffeeeeeeeeedddddddddddcccccccccccbbbbbbbbbbbaaaaaaaaaaaaa`````````````_____________^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOO~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOᮮ୭߬߬ޫޫݪܩܩۨڧڧ٦إפ֣բԡӠҟўНϜ̙˘ʗɖȕǔƓđÐ~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnn;mmmmmmmmlllllllll9kkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggg4fffffffff3eeeeeeeeeee2ddddddddddd1ccccccccccc0bbbbbbbbbbb/aaaaaaaaaaaaa.`````````````-_____________,^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTT!SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOO 넄~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppp ooooooonnnnnnnnmmmmmmmmmllllllllkkkkkkkkkjjjjjjjjjiiiiiiiiihhhhhhhhhggggggggggffffffffffeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaa`````````````_____________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOHHGGFFEEDDCCBAA@?>=<;:98754320/.+('&%$#~~~~~~~~}}}}}}|||||||{{{{{{{{zzzzzzyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuutttttttsssssssrrrrrrrqqqqqqqpppppppppoooooooonnnnnnnnmmmmmmmmmllllllllkkkkkkkkkjjjjjjjjjiiiiiiiiiihhhhhhhhhhgggggggggfffffffffffeeeeeeeeeedddddddddddcccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaa`````````````_____________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOO||{{zzyyxxwwvvuttssrqqpponmlkjihgedba`^]S~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnnmmmmmmmmlllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiihhhhhhhhhhhggggggggggfffffffffeeeeeeeeeeedddddddddddcccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaa``````````````_____________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOO~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOO~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPᮮ୭߬߬ޫޫݪܩۨڧ٦إפ֣բԡӠҟўНϜΛ̙˘ɖȕǔƓŒÐ~~~~~~~~}}}}}}}}|||||||I{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwDvvvvvvvCuuuuuutttttttttAsssssss@rrrrrrr?qqqqqqqqppppppppooooooooo==<;;:987654321/.-*('~~~~~~~~}}}}}}}}||||||||{{{{{{zzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqpppppppppoooooooonnnnnnnnnmmmmmmmmlllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhgggggggggggffffffffffeeeeeeeeeeedddddddddddcccccccccccccbbbbbbbbbbbaaaaaaaaaaaaa```````````````_____________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP||{{zzzyyxxwvuuttsrrqponmlkjihgfdc`_]ZR~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrqqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmllllllllkkkkkkkkkjjjjjjjjjiiiiiiiiiiihhhhhhhhhhgggggggggfffffffffffeeeeeeeeeeedddddddddddcccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaa```````````````_____________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPᮮ୭߬߬ޫޫݪݪܩܩۨڧ٦٦إפ֣բԡӠҟўΛʗɖȕǔŒđ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxEwwwwwwwDvvvvvvvCuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqq>ppppppppooooooooo=<;::98764321/.-,+(%~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttsssssssssrrrrrrrrqqqqqqqqpppppppppoooooooonnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhggggggggggfffffffffffeeeeeeeeeeedddddddddddcccccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaa```````````````_____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPP{{zzyxxwwvuutsrqponmlkjihgfedc][XW~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuutttttttttssssssssrrrrrrrrqqqqqqqqqppppppppooooooooonnnnnnnnmmmmmmmmmlllllllllkkkkkkkkkjjjjjjjjjiiiiiiiiiiihhhhhhhhhhgggggggggggfffffffffffeeeeeeeeeeedddddddddddcccccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaa```````````````_______________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPP~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ୭߬߬ޫޫݪݪܩۨڧڧ٦٦إפ֣֣բԡӠҟўНϜΛ̙˘ʗȕ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyFxxxxxxxEwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqq>ppppppppoooooooooonnnnnnnnn;mmmmmmmmm:lllllllll9kkkkkkkkk8jjjjjjjjjjiiiiiiiiiii6hhhhhhhhhhggggggggggg4fffffffffff3eeeeeeeeeee2ddddddddddddd1ccccccccccccbbbbbbbbbbbbbbb/aaaaaaaaaaaaa.```````````````-_______________,^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\)\)[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ 􍍍񊊊셅넄ꃃ~~~~~~~}}}}}}}|||||||{{{{{{{zzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttt ssssssssrrrrrrrrr qqqqqqqqppppppppp oooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhgggggggggggffffffffffeeeeeeeeeeedddddddddddddcccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaa```````````````_______________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQGGFFEEDDCCBBAA@@??>>=<;:98765430/.(&#~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuutttttttttssssssssrrrrrrrrrrqqqqqqqqpppppppppoooooooonnnnnnnnnmmmmmmmmmlllllllllkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhgggggggggggffffffffffffeeeeeeeeeeeedddddddddddddccccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaaaa```````````````_______________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ{{zzyyxxwvutsrqponmmkjihfeca`_^][YV~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuttttttttssssssssrrrrrrrrrqqqqqqqqqqppppppppooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhhggggggggggfffffffffffeeeeeeeeeeeeedddddddddddcccccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaaaa```````````````_______________^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQ୭߬߬ޫޫݪݪܩܩۨۨڧ٦إפ֣֣բբԡӠҟўНϜΛ̙˘ɖȕƓŒđЏ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxEwwwwwwwwvvvvvvvvuuuuuuuuuBttttttttssssssssrrrrrrrrrrqqqqqqqqq>ppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkk8jjjjjjjjjjiiiiiiiiiii6hhhhhhhhhhh5ggggggggggg4fffffffffff3eeeeeeeeeee2ddddddddddddd1ccccccccccccc0bbbbbbbbbbbbb/aaaaaaaaaaaaaaa.```````````````-_______________,^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWWWWWW$VVVVVVVVVVVVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQ 􍍍톆~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttsssssssss rrrrrrrrqqqqqqqqqqppppppppp ooooooooonnnnnnnnnmmmmmmmmmlllllllllkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeedddddddddddddcccccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaaaa```````````````_______________^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQFFEEDDCBBA@??>=<;:987654210/-,+*)(%~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuttttttttsssssssssrrrrrrrrqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhgggggggggggfffffffffffeeeeeeeeeeeeedddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaa````````````````_______________^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQzzyyxxwwvuuttsrqponmljihgfcbaZXV~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqpppppppppooooooooonnnnnnnnnmmmmmmmmmlllllllllkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhgggggggggggfffffffffffeeeeeeeeeeeeeddddddddddddcccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaa`````````````````_______________^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQ~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQ߬߬ޫݪݪܩۨۨڧڧ٦إإפ֣բբԡӠҟўНϜ̙˘ʗɖȕƓЏ~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxEwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrr?qqqqqqqqq>ppppppppoooooooooonnnnnnnnnnmmmmmmmmmmm:lllllllll9kkkkkkkkkkjjjjjjjjjjj7iiiiiiiiiii6hhhhhhhhhhh5ggggggggggg4ffffffffffffeeeeeeeeeeeee2ddddddddddddd1ccccccccccccc0bbbbbbbbbbbbbbb/aaaaaaaaaaaaa.```````````````-_________________,^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWW$VVVVVVVVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR 󌌌톆ꃃ~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvuuuuuuuuttttttttt ssssssssrrrrrrrrrrqqqqqqqqqqppppppppp ooooooooonnnnnnnnnmmmmmmmmmmlllllllllllkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhhgggggggggggffffffffffeeeeeeeeeeeeedddddddddddddcccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaa```````````````_________________^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRFFEEDDCCBBA@??>>=<;:98754320-+*)('$"~~~~~~~~}}}}}}}}||||||||{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmlllllllllllkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeedddddddddddddcccccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaaaa```````````````_________________^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRzzyyxxwwvuuttsrqqponmlkjihgedcb`Y~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvuuuuuuuutttttttttssssssssssrrrrrrrrqqqqqqqqqpppppppppooooooooonnnnnnnnnmmmmmmmmmmmllllllllllkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhgggggggggggfffffffffffffeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbaaaaaaaaaaaaaaa```````````````_________________^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRR߬߬ޫޫݪݪܩܩۨۨڧڧ٦٦إإפפ֣բԡӠҟўНϜΛ˘ɖȕŒ~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzGyyyyyyyyxxxxxxxxxEwwwwwwwwvvvvvvvvvCuuuuuuuuttttttttttsssssssss@rrrrrrrrr?qqqqqqqqppppppppppooooooooooo==<;;:987654321,)('& ~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvuuuuuuuuuuttttttttsssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmllllllllllkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggfffffffffffffeeeeeeeeeeeeeddddddddddddddcccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaaa```````````````_________________^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRyyxxwwvvutssrqponmlkjihgfdcba_W~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{zzzzzzzzzyyyyyyyyxxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssrrrrrrrrrqqqqqqqqqpppppppppooooooooonnnnnnnnnnnmmmmmmmmmmlllllllllllkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhgggggggggggggfffffffffffffeeeeeeeeeeeedddddddddddddddccccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaaa```````````````_________________^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRR~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRR~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRޫޫݪݪܩܩۨۨڧ٦٦إإפ֣բԡӠҟўНΛ͚̙ʗȕǔđ~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxEwwwwwwwwvvvvvvvvvvuuuuuuuuuBttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppp=ooooooooo>=<;:987643210+('$# ~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnmmmmmmmmmmlllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhgggggggggggggffffffffffffeeeeeeeeeeeeeeeddddddddddddddcccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa```````````````___________________^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSyyxxwwvvuutssrqponmlkjihecba`^ZY~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqpppppppppooooooooooonnnnnnnnnnmmmmmmmmmmmllllllllllkkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhhhggggggggggggfffffffffffffeeeeeeeeeeeeedddddddddddddddccccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa```````````````___________________^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSޫޫݪݪܩܩۨۨڧڧ٦إפפ֣֣բԡԡӠӠҟўНϜΛ͚̙˘ʗȕǔƓŒÐ~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppp=oooooooooonnnnnnnnnnmmmmmmmmmmmmlllllllllll9kkkkkkkkkkk8jjjjjjjjjjj7iiiiiiiiiiiihhhhhhhhhhhhh5ggggggggggggffffffffffffffeeeeeeeeeeeeeee2ddddddddddddd1ccccccccccccccc0bbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaa.```````````````-___________________,^^^^^^^^^^^^^^^^^+^+]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSS 󌌌򋋋񊊊셅~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqq ppppppppppoooooooooonnnnnnnnnnnmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhhgggggggggggggffffffffffffeeeeeeeeeeeeeeeddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa````````````````___________________^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSEEDCBBAA@?>=<<;:9876540.-,*(~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvuuuuuuuuutttttttttsssssssssrrrrrrrrrqqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhgggggggggggggfffffffffffffeeeeeeeeeeeeedddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSxxwwvvuuttssrrqponmlkhgfecb`_[T~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppooooooooooonnnnnnnnnnmmmmmmmmmmmlllllllllllkkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiiiiihhhhhhhhhhhhgggggggggggggfffffffffffffeeeeeeeeeeeeedddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSS~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSݪݪܩܩۨۨڧڧ٦إפ֣բԡӠҟўНϜΛ͚̙ɖȕǔƓŒđÐ~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyFxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttsssssssssss@rrrrrrrrr?qqqqqqqqqqppppppppppp=oooooooooonnnnnnnnnnn;mmmmmmmmmmm:lllllllllll9kkkkkkkkkkk8jjjjjjjjjjj7iiiiiiiiiiiihhhhhhhhhhhhh5ggggggggggggg4fffffffffffff3eeeeeeeeeeeeeee2ddddddddddddd1ccccccccccccccc0bbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaa.`````````````````-___________________,^^^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSS 􍍍~~~~~~~~~~}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxwwwwwwwwwvvvvvvvvvuuuuuuuuuttttttttt ssssssssssrrrrrrrrrrqqqqqqqqqqq ppppppppppooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhhhgggggggggggggffffffffffffeeeeeeeeeeeeeeeddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSDCCBAA@?>>=<<;:9876543210.-,+*(&%~~~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmlllllllllllkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeedddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSxxwwvvuttsrqqpoonmlkjihgfdc][XWTS~~~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrqqqqqqqqqqppppppppppooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiihhhhhhhhhhhhgggggggggggggfffffffffffffffeeeeeeeeeeeeeedddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTݪݪܩܩۨۨڧ٦٦إפפ֣բԡӠҟўНϜΛ͚˘ʗɖǔ~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{HzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvCuuuuuuuuuBttttttttttssssssssssrrrrrrrrrrr?qqqqqqqqqqppppppppppp=oooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjj7iiiiiiiiiiiii6hhhhhhhhhhhhggggggggggggggfffffffffffffff3eeeeeeeeeeeeeeddddddddddddddd1ccccccccccccccccbbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaa.`````````````````-___________________,^^^^^^^^^^^^^^^^^^^+^+]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT 􍍍򋋋񊊊~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzyyyyyyyyyxxxxxxxxxwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttsssssssssss rrrrrrrrrrqqqqqqqqqqppppppppppppooooooooooonnnnnnnnnnnmmmmmmmmmmmlllllllllllkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhgggggggggggggffffffffffffffeeeeeeeeeeeeedddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTDCCBA@@?>=<;:9876543.(&#~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttsssssssssssrrrrrrrrrrqqqqqqqqqqpppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhgggggggggggggfffffffffffffffeeeeeeeeeeeeeedddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTxxwvvuutsrrqqpponmlkjihgfedca`_^]VO~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuutttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqppppppppppooooooooooonnnnnnnnnnnmmmmmmmmmmmlllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggfffffffffffffeeeeeeeeeeeeeeedddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTܩܩۨۨڧڧ٦٦إפ֣բԡӠӠҟўНϜ͚̙˘ɖǔƓŒ~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwDvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppp=ooooooooooo==<;:987654321.,('%"~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuutttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqpppppppppppooooooooooonnnnnnnnnnnnmmmmmmmmmmmmlllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhgggggggggggggggffffffffffffffeeeeeeeeeeeeeeedddddddddddddddcccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTwwvvutsrqpponmlkjihgfdca_^]ZXU~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiiihhhhhhhhhhhhhgggggggggggggfffffffffffffffeeeeeeeeeeeeeeedddddddddddddddcccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa`````````````````___________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTT~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````______________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTT~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUܩܩۨۨڧ٦إפפ֣բԡӠҟўНΛ͚̙˘ʗƓđЏ~~~~~~~~~~}}}}}}}}}}}J||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwDvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqq>ppppppppppp=ooooooooooo=<;:986543210.-+*)('$"~~~~~~~~~~}}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqpppppppppppooooooooooonnnnnnnnnnnmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiihhhhhhhhhhhhhhhggggggggggggggfffffffffffffffeeeeeeeeeeeeeeedddddddddddddddcccccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUwvvuttsrrqponmlkjihec`YU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuttttttttttsssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiiihhhhhhhhhhhhhgggggggggggggggfffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddcccccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUܩܩۨڧ٦إإפ֣֣բԡӠҟўНϜΛ͚̙˘ʗɖŒ~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzzGyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuBttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqq>ppppppppppp=ooooooooooo=<;:987640/-,*)&%~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrqqqqqqqqqqqpppppppppppooooooooooonnnnnnnnnnnnnmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhgggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeedddddddddddddddcccccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUvvuuttsrqponmlkihgfedb_\[WU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuutttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiiihhhhhhhhhhhhhhhggggggggggggggfffffffffffffffeeeeeeeeeeeeeeeeeddddddddddddddddcccccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUU~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUU~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUۨڧڧ٦٦إإפ֣բԡӠҟўНϜΛ͚̙ɖđ~~~~~~~~~~}}}}}}}}}}|||||||||||I{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttsssssssssss@rrrrrrrrrrr?qqqqqqqqqqq>ppppppppppp=oooooooooooonnnnnnnnnnnnn;mmmmmmmmmmmmlllllllllllll9kkkkkkkkkkkkk8jjjjjjjjjjjjj7iiiiiiiiiiiii6hhhhhhhhhhhhhhh5ggggggggggggggfffffffffffffffff3eeeeeeeeeeeeeee2ddddddddddddddddd1ccccccccccccccccc0bbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaaaa.```````````````````-_____________________,^^^^^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]]]]]*]*\\\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#UU 톆聁~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhgggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeedddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU響BAA@@?>=<;;:99876543210/+)#~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuutttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhgggggggggggggggfffffffffffffffeeeeeeeeeeeeeeeeeddddddddddddddddcccccccccccccccccbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVvvutssrrqqponmlkjgfdba`^\[ZYXU~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttsssssssssssrrrrrrrrrrrqqqqqqqqqqqpppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiihhhhhhhhhhhhhhhgggggggggggggggfffffffffffffffeeeeeeeeeeeeeeedddddddddddddddddcccccccccccccccccbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVۨڧ٦٦إإפ֣բբԡӠҟўϜΛ͚˘ʗɖǔƓŒ~~~~~~~~~~}}}}}}}}}}}J||||||||||{{{{{{{{{{{HzzzzzzzzzzyyyyyyyyyyyFxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvCuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqq>ppppppppppp=oooooooooooonnnnnnnnnnnnn;mmmmmmmmmmmmm:llllllllllllkkkkkkkkkkkkkkk8jjjjjjjjjjjjj7iiiiiiiiiiiiiihhhhhhhhhhhhhhh5ggggggggggggggg4fffffffffffffff3eeeeeeeeeeeeeeeee2ddddddddddddddddd1ccccccccccccccccc0bbbbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaaaa.```````````````````-_____________________,^^^^^^^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVV 􍍍󌌌򋋋񊊊톆~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuttttttttttt sssssssssss rrrrrrrrrrr qqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhgggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeedddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVBBA@@?>==<;:9876543/-,)~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeddddddddddddddddcccccccccccccccccccbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaa```````````````````_____________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVuutssrqponmlkjigfedba_^\[UR~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqpppppppppppppoooooooooooonnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhgggggggggggggggfffffffffffffffffeeeeeeeeeeeeeeeedddddddddddddddddcccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaa````````````````````_____________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVV~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVڧ٦إפ֣բբԡԡӠҟўНϜΛ͚̙˘ǔƓđÐ~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjj7iiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggfffffffffffffffff3eeeeeeeeeeeeeeeee2ddddddddddddddddd1ccccccccccccccccc0bbbbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaaaa.`````````````````````-_____________________,_,^^^^^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVV 􍍍~~~~~~~~~~}}}}}}}}}}}}|||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrr qqqqqqqqqqq ppppppppppppooooooooooooonnnnnnnnnnnnnmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhgggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeddddddddddddddddddcccccccccccccccccbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa`````````````````````_______________________^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVA@?>>=<;:9876543210/-,+*& ~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhgggggggggggggggfffffffffffffffffeeeeeeeeeeeeeeeedddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa`````````````````````_______________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVuuttssrqponnmkjifb\YXWVUQN~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvuuuuuuuuuuutttttttttttsssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqpppppppppppppoooooooooooonnnnnnnnnnnnnmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggfffffffffffffffeeeeeeeeeeeeeeeeedddddddddddddddddcccccccccccccccccccbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaa`````````````````````_______________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWڧڧ٦إפ֣բԡӠҟўНϜ͚̙˘ʗȕǔđ~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttsssssssssssss@rrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppooooooooooooo=<;:98654210/.+*'$#" ~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxwwwwwwwwwwwvvvvvvvvvvvuuuuuuuuuuutttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnnmmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhgggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeedddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaa`````````````````````_______________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWuttsrqpoonmlkjigeba`]\ZU~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrqqqqqqqqqqqqpppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhgggggggggggggggfffffffffffffffffeeeeeeeeeeeeeeeeedddddddddddddddddcccccccccccccccccccbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaa`````````````````````_______________________^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWڧ٦٦إפ֣֣բԡӠҟўНϜΛ͚̙˘ʗɖȕǔŒ~~~~~~~~~~~~}}}}}}}}}}}J||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuuBttttttttttttssssssssssssrrrrrrrrrrrrr?qqqqqqqqqqqqppppppppppppppooooooooooooo>=<;:98754320/.,'%"!~~~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{zzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwvvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnnmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhggggggggggggggggfffffffffffffffffeeeeeeeeeeeeeeeeedddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaa`````````````````````_______________________^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWtsrqponmlkjigfeda_^]\TSRQ~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyxxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttsssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhgggggggggggggggggfffffffffffffffffeeeeeeeeeeeeeeeeeedddddddddddddddddcccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaa`````````````````````_______________________^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWW~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWmlt-7.38.0/src/tests/common.pri000664 000000 000000 00000001124 15172202314 016315 0ustar00rootroot000000 000000 QT += testlib QT -= gui CONFIG += console CONFIG -= app_bundle CONFIG += testcase CONFIG += c++11 QMAKE_CXXFLAGS += -std=c++11 TEMPLATE = app DEFINES += SRCDIR=\\\"$$PWD/\\\" win32 { INCLUDEPATH += $$PWD/.. LIBS += -L$$PWD/../framework -L$$PWD/../mlt++ -lmlt++ -lmlt isEmpty(PREFIX) { message("Install PREFIX not set; using C:\\Projects\\Shotcut. You can change this with 'qmake PREFIX=...'") PREFIX = C:\\Projects\\Shotcut } target.path = $$PREFIX INSTALLS += target } else { CONFIG += link_pkgconfig PKGCONFIG += mlt++-7 } mlt-7.38.0/src/tests/kwalify_runtime_query.cmake000664 000000 000000 00000011325 15172202314 021755 0ustar00rootroot000000 000000 if(NOT DEFINED MLT_EXECUTABLE) message(FATAL_ERROR "Missing required variable: MLT_EXECUTABLE") endif() if(NOT DEFINED KWALIFY_EXECUTABLE) message(FATAL_ERROR "Missing required variable: KWALIFY_EXECUTABLE") endif() if(NOT DEFINED METASCHEMA_FILE) message(FATAL_ERROR "Missing required variable: METASCHEMA_FILE") endif() if(NOT DEFINED LIST_QUERY) message(FATAL_ERROR "Missing required variable: LIST_QUERY") endif() if(NOT DEFINED QUERY_PREFIX) message(FATAL_ERROR "Missing required variable: QUERY_PREFIX") endif() execute_process( COMMAND "${MLT_EXECUTABLE}" -query "${LIST_QUERY}" RESULT_VARIABLE list_result OUTPUT_VARIABLE list_output ERROR_VARIABLE list_error ) if(NOT list_result EQUAL 0) message(FATAL_ERROR "Failed to query services from melt.\n" "Command: ${MLT_EXECUTABLE} -query ${LIST_QUERY}\n" "Exit code: ${list_result}\n" "stderr:\n${list_error}" ) endif() string(REPLACE "\r\n" "\n" list_output "${list_output}") string(REPLACE "\r" "\n" list_output "${list_output}") string(REPLACE "\n" ";" list_lines "${list_output}") set(service_names "") set(service_list_headers filters filter transitions transition links link consumers consumer producers producer ) foreach(line IN LISTS list_lines) string(STRIP "${line}" line) if(line STREQUAL "") continue() endif() if(line MATCHES "^[-*][ \\t]+([A-Za-z0-9_.+-]+)$") set(candidate "${CMAKE_MATCH_1}") elseif(line MATCHES "^([A-Za-z0-9_.+-]+)([ \\t]+.*)?$") set(candidate "${CMAKE_MATCH_1}") else() continue() endif() string(TOLOWER "${candidate}" candidate_lower) list(FIND service_list_headers "${candidate_lower}" header_index) if(NOT header_index EQUAL -1) continue() endif() list(APPEND service_names "${candidate}") endforeach() list(REMOVE_DUPLICATES service_names) list(LENGTH service_names service_count) if(service_count EQUAL 0) message(FATAL_ERROR "No services parsed from 'melt -query ${LIST_QUERY}'.\n" "Raw output:\n${list_output}\n" "stderr:\n${list_error}" ) endif() set(failure_text "") set(failure_count 0) set(validated_count 0) foreach(service_name IN LISTS service_names) execute_process( COMMAND "${MLT_EXECUTABLE}" -query "${QUERY_PREFIX}${service_name}" RESULT_VARIABLE service_result OUTPUT_VARIABLE service_yaml ERROR_VARIABLE service_error ) if(NOT service_result EQUAL 0) message(STATUS "[runtime-kwalify:${LIST_QUERY}] ${service_name} ... FAIL") string(STRIP "${service_error}" detail) string(REPLACE "\n" "\n " detail " ${detail}") string(APPEND failure_text "\n[${service_name}] melt query failed (exit=${service_result})\n${detail}\n") math(EXPR failure_count "${failure_count} + 1") continue() endif() string(STRIP "${service_yaml}" service_yaml_stripped) if(service_yaml_stripped STREQUAL "") message(STATUS "[runtime-kwalify:${LIST_QUERY}] ${service_name} ... FAIL") string(APPEND failure_text "\n[${service_name}] melt returned empty metadata\n") math(EXPR failure_count "${failure_count} + 1") continue() endif() string(REGEX REPLACE "[^A-Za-z0-9_.-]" "_" safe_service_name "${service_name}") set(service_yaml_file "${CMAKE_CURRENT_BINARY_DIR}/kwalify_${LIST_QUERY}_${safe_service_name}.yml") file(WRITE "${service_yaml_file}" "${service_yaml}") execute_process( COMMAND "${KWALIFY_EXECUTABLE}" -f "${METASCHEMA_FILE}" "${service_yaml_file}" RESULT_VARIABLE schema_result OUTPUT_VARIABLE schema_output ERROR_VARIABLE schema_error ) set(schema_text "${schema_output}\n${schema_error}") set(schema_invalid_output FALSE) if(schema_text MATCHES "INVALID") set(schema_invalid_output TRUE) endif() if((NOT schema_result EQUAL 0) OR schema_invalid_output) message(STATUS "[runtime-kwalify:${LIST_QUERY}] ${service_name} ... FAIL") # Clean up kwalify output (stdout + stderr): strip surrounding whitespace, collapse multiple blank lines set(raw_detail "${schema_output}\n${schema_error}") string(STRIP "${raw_detail}" detail) string(REGEX REPLACE "\n[ \t]*\n[ \t]*" "\n" detail "${detail}") if(detail STREQUAL "") set(detail "(no output from kwalify)") endif() string(REPLACE "\n" "\n " detail " ${detail}") string(APPEND failure_text "\n[${service_name}]\n${detail}\n") math(EXPR failure_count "${failure_count} + 1") continue() endif() math(EXPR validated_count "${validated_count} + 1") message(STATUS "[runtime-kwalify:${LIST_QUERY}] ${service_name} ... PASS") endforeach() math(EXPR total_count "${validated_count} + ${failure_count}") message(STATUS "[runtime-kwalify:${LIST_QUERY}] ${total_count} tested, ${failure_count} failed") if(failure_count GREATER 0) message(FATAL_ERROR "${failure_text}") endif() mlt-7.38.0/src/tests/setenv000664 000000 000000 00000000307 15172202314 015542 0ustar00rootroot000000 000000 export MLT_REPOSITORY=`pwd`/../modules export LD_LIBRARY_PATH=`pwd`/../framework:\ `pwd`/../modules/bluefish:\ `pwd`/../../../bluefish/lib:\ `pwd`/../../../mpeg_sdk_demo/bin:\ `pwd`/../../../dv_sdk mlt-7.38.0/src/tests/test_animation/000775 000000 000000 00000000000 15172202314 017331 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_animation/test_animation.cpp000664 000000 000000 00000107007 15172202314 023060 0ustar00rootroot000000 000000 /* * Copyright (C) 2015-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestAnimation : public QObject { Q_OBJECT private Q_SLOTS: void DefaultConstructorIsInvalid() { Animation a; QVERIFY(!a.is_valid()); } void ConstructFromProperties() { Properties p; p.anim_set("foo", "bar", 10); Animation *a = p.get_anim("foo"); QVERIFY(a); QVERIFY(a->is_valid()); delete a; a = p.get_anim("bar"); QVERIFY(a); QVERIFY(!a->is_valid()); } void ConstructFromCType() { Properties p; p.anim_set("foo", "bar", 10); Animation a1(p.get_animation("foo")); QVERIFY(a1.is_valid()); Animation a2(a1.get_animation()); QVERIFY(a2.is_valid()); QVERIFY(a1.get_animation() == a2.get_animation()); } void CopyConstructor() { Properties p; p.anim_set("foo", "bar", 10); Animation a1(p.get_animation("foo")); QVERIFY(a1.is_valid()); Animation a2(a1); QVERIFY(a2.is_valid()); QVERIFY(a1.get_animation() == a2.get_animation()); } void Assignment() { Properties p; p.anim_set("foo", "bar", 10); Animation a1 = p.get_animation("foo"); QVERIFY(a1.is_valid()); Animation a2; a2 = a1; QVERIFY(a2.is_valid()); QVERIFY(a1.get_animation() == a2.get_animation()); } void LengthIsCorrect() { Properties p; p.anim_set("foo", "bar", 10); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.length(), 10); } void IsAKeyFrame() { Properties p; p.anim_set("foo", "bar", 10); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QVERIFY(!a.is_key(5)); QVERIFY(a.is_key(10)); } void KeyFrameTypeIsDiscrete() { Properties p; p.anim_set("foo", "bar", 10); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.keyframe_type(0), mlt_keyframe_discrete); QCOMPARE(a.keyframe_type(10), mlt_keyframe_discrete); QCOMPARE(a.keyframe_type(11), mlt_keyframe_discrete); } void KeyFrameTypeIsLinear() { Properties p; p.anim_set("foo", 1, 10); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.keyframe_type(0), mlt_keyframe_linear); QCOMPARE(a.keyframe_type(10), mlt_keyframe_linear); QCOMPARE(a.keyframe_type(11), mlt_keyframe_linear); } void KeyFrameTypeIsSmooth() { Properties p; int pos = 10; p.anim_set("foo", 1, pos, pos, mlt_keyframe_smooth); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.keyframe_type(0), mlt_keyframe_smooth); QCOMPARE(a.keyframe_type(10), mlt_keyframe_smooth); QCOMPARE(a.keyframe_type(11), mlt_keyframe_smooth); } void GetItem() { Properties p; int pos = 10; p.anim_set("foo", 1, pos, pos, mlt_keyframe_smooth); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); bool is_key = true; mlt_keyframe_type type = mlt_keyframe_linear; int error = a.get_item(10, is_key, type); QVERIFY(!error); QVERIFY(is_key); QCOMPARE(type, mlt_keyframe_smooth); error = a.get_item(1, is_key, type); QVERIFY(!error); QVERIFY(!is_key); QCOMPARE(type, mlt_keyframe_smooth); } void AnimationFromStringProperty() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); Animation a = p.get_animation("foo"); QVERIFY(!a.is_valid()); // Cause the string to be interpreted as animated value. p.anim_get("foo", 0); a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.length(), 100); } void SetLength() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.length(), 100); a.set_length(200); QCOMPARE(a.length(), 200); a.set_length(60); QCOMPARE(a.length(), 60); QCOMPARE(a.serialize_cut(), "50=100;60=60"); } void RemoveMiddleKeyframe() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int error = a.remove(60); QVERIFY(!error); QCOMPARE(a.serialize_cut(), "50=100;100=0"); QCOMPARE(a.length(), 100); QCOMPARE(p.anim_get_int("foo", 50), 100); QCOMPARE(p.anim_get_int("foo", 75), 50); QCOMPARE(p.anim_get_int("foo", 100), 0); } void RemoveFirstKeyframe() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int error = a.remove(0); QVERIFY(error); error = a.remove(50); QVERIFY(!error); QCOMPARE(a.serialize_cut(), "60=60;100=0"); QCOMPARE(a.length(), 100); QCOMPARE(p.anim_get_int("foo", 50), 60); QCOMPARE(p.anim_get_int("foo", 80), 30); QCOMPARE(p.anim_get_int("foo", 100), 0); } void RemoveLastKeyframe() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int error = a.remove(101); QVERIFY(error); error = a.remove(100); QVERIFY(!error); QCOMPARE(a.serialize_cut(), "50=100;60=60"); QCOMPARE(a.length(), 60); QCOMPARE(p.anim_get_int("foo", 50), 100); QCOMPARE(p.anim_get_int("foo", 55), 80); QCOMPARE(p.anim_get_int("foo", 60), 60); } void EmptyAnimationIsInvalid() { Properties p; p.set("foo", ""); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(!a.is_valid()); } void NonEmptyAnimationKeyCount() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.key_count(), 3); } void RemoveKeyframeCount() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); a.remove(50); QCOMPARE(a.key_count(), 2); } void GetKeyFrame() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int frame = -1; mlt_keyframe_type type = mlt_keyframe_smooth; int error = a.key_get(0, frame, type); QVERIFY(!error); QCOMPARE(frame, 50); QCOMPARE(type, mlt_keyframe_linear); QCOMPARE(a.key_count(), 3); QCOMPARE(a.key_get_frame(0), 50); QCOMPARE(a.key_get_frame(1), 60); QCOMPARE(a.key_get_frame(2), 100); QCOMPARE(a.key_get_frame(3), -1); QCOMPARE(a.keyframe_type(0), mlt_keyframe_linear); QCOMPARE(a.keyframe_type(1), mlt_keyframe_linear); QCOMPARE(a.keyframe_type(2), mlt_keyframe_linear); } void SerializesInTimeFormat() { Profile profile; Properties p; p.set("_profile", profile.get_profile(), 0); p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.serialize_cut(mlt_time_clock), "00:00:02.000=100;00:00:02.400=60;00:00:04.000=0"); QCOMPARE(a.serialize_cut(mlt_time_smpte_ndf), "00:00:02:00=100;00:00:02:10=60;00:00:04:00=0"); } void GetPropertyInTimeFormat() { Profile profile; Properties p; p.set("_profile", profile.get_profile(), 0); p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); for (int i = 0; i < p.count(); i++) { if (!qstrcmp(p.get_name(i), "foo")) { QCOMPARE(p.get(i, mlt_time_clock), "00:00:02.000=100;00:00:02.400=60;00:00:04.000=0"); QCOMPARE(p.get(i, mlt_time_smpte_ndf), "00:00:02:00=100;00:00:02:10=60;00:00:04:00=0"); break; } } } void AnimationClears() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); p.clear("foo"); QCOMPARE(p.get_animation("foo"), mlt_animation(0)); } void CanBeEscapedWithQuotes() { Properties p; p.set("foo", "\"50=100; 60=60; 100=0\""); // Quotes are retained when using the non-anim getter. QCOMPARE(p.get("foo"), "\"50=100; 60=60; 100=0\""); // Quotes are removed when using the anim getter. QCOMPARE(p.anim_get("foo", 0), "50=100; 60=60; 100=0"); // Anim strings may contain delimiters and equal signs if quoted. p.set("foo", "50=100; 60=\"60; 100=0\";\"hello=world\""); QCOMPARE(p.anim_get("foo", 0), "hello=world"); QCOMPARE(p.anim_get("foo", 50), "100"); QCOMPARE(p.anim_get("foo", 60), "60; 100=0"); } void ShiftFramesPositive() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); a.shift_frames(60); QCOMPARE(a.key_get_frame(0), 110); QCOMPARE(a.key_get_frame(1), 120); QCOMPARE(a.key_get_frame(2), 160); QCOMPARE(a.key_get_frame(3), -1); } void ShiftFramesNegative() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); a.shift_frames(-60); QCOMPARE(a.key_get_frame(0), -10); QCOMPARE(a.key_get_frame(1), 0); QCOMPARE(a.key_get_frame(2), 40); QCOMPARE(a.key_get_frame(3), -1); } void NextKey() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int key; bool ret; ret = a.next_key(0, key); QCOMPARE(ret, false); QCOMPARE(key, 50); ret = a.next_key(59, key); QCOMPARE(ret, false); QCOMPARE(key, 60); ret = a.next_key(60, key); QCOMPARE(ret, false); QCOMPARE(key, 60); ret = a.next_key(61, key); QCOMPARE(ret, false); QCOMPARE(key, 100); ret = a.next_key(100, key); QCOMPARE(ret, false); QCOMPARE(key, 100); key = 7; // random value ret = a.next_key(101, key); QCOMPARE(ret, true); // error - No next key QCOMPARE(key, 7); // Not modified on error } void PreviousKey() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int key; bool ret; key = 7; // random value ret = a.previous_key(0, key); QCOMPARE(ret, true); // error - no previous key QCOMPARE(key, 7); // Not modified on error ret = a.previous_key(59, key); QCOMPARE(ret, false); QCOMPARE(key, 50); ret = a.previous_key(60, key); QCOMPARE(ret, false); QCOMPARE(key, 60); ret = a.previous_key(61, key); QCOMPARE(ret, false); QCOMPARE(key, 60); ret = a.previous_key(100, key); QCOMPARE(ret, false); QCOMPARE(key, 100); ret = a.previous_key(101, key); QCOMPARE(ret, false); QCOMPARE(key, 100); } void LinearInterpolationOneKey() { Properties p; p.set("foo", "50=10"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 10 should all be 10 for (int i = 0; i <= 50; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 10); } } void SmoothInterpolationOneKey() { Properties p; p.set("foo", "50~=10"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 10 should all be 10 for (int i = 0; i <= 50; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 10); } } void LinearInterpolationTwoKey() { Properties p; double prev; p.set("foo", "10=50; 20=100"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 10 should all be 50 for (int i = 0; i <= 10; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 50); } // Values from 10 to 20 should step by 5 prev = 50 - 5; for (int i = 10; i <= 20; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, prev + 5); prev = current; } // Values after 20 should all be 100 for (int i = 20; i <= 30; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 100); } } void SmoothInterpolationTwoKey() { Properties p; double prev; p.set("foo", "10~=50; 20~=100"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 10 should all be 50 for (int i = 0; i <= 10; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 50); } // Values from 10 to 20 should increase but not exceed 100 prev = 49; for (int i = 10; i <= 20; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current <= 100); prev = current; } // Values after 20 should all be 100 for (int i = 20; i <= 30; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 100); } } void LinearInterpolationThreeKey() { Properties p; double prev; p.set("foo", "10=50; 20=100; 30=50"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 10 should all be 50 for (int i = 0; i <= 10; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 50); } // Values from 10 to 20 should step up by 5 prev = 50 - 5; for (int i = 10; i <= 20; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, prev + 5); prev = current; } // Values from 20 to 30 should step down by 5 prev = 100 + 5; for (int i = 20; i <= 30; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, prev - 5); prev = current; } // Values after 30 should all be 50 for (int i = 30; i <= 40; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 50); } } void SmoothInterpolationThreeKey() { Properties p; double prev; p.set("foo", "10~=50; 20~=100; 30~=50"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 10 should all be 50 for (int i = 0; i <= 10; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 50); } // Values from 10 to 20 should increase but not exceed 100 prev = 49; for (int i = 10; i <= 20; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current <= 100); prev = current; } // Test two arbitrary intermediate points QCOMPARE(p.anim_get_double("foo", 13), 64.475); QCOMPARE(p.anim_get_double("foo", 18), 95.6); // Values from 20 to 30 should decrease but not exceed 50 prev = 101; for (int i = 20; i <= 30; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current < prev); QVERIFY(current >= 50); prev = current; } // Test two arbitrary intermediate points QCOMPARE(p.anim_get_double("foo", 23), 90.775); QCOMPARE(p.anim_get_double("foo", 28), 58.4); // Values after 30 should all be 50 for (int i = 30; i <= 40; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 50); } } void LinearDoesNotReverse() { Properties p; double prev; // This sequence of keyframes has abrupt changes. For some interpolation algorithms, this // can result in values changing direction along the interpolation path (cusp or // overshoot). // The purpose of this test is to ensure that values do not reverse direction (exept as // expected at keyframes if the user specified a direction change). p.set("foo", "50=0; 60=100; 100=110; 150=200; 200=110; 240=100; 260=50; 300=200; 301=10; 350=11"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 50 should all be 0 for (int i = 0; i <= 50; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 0); } // Values from 50 to 150 should only go up prev = 0; for (int i = 50; i <= 150; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current >= prev); prev = current; } // Values from 150 to 260 should only go down prev = 201; for (int i = 150; i <= 260; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current < prev); prev = current; } // Values from 260 to 300 should only go up prev = 49; for (int i = 260; i <= 300; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current < 200.01); prev = current; } // Values above 301 to 350 should go up from 10 to 11 prev = 9.99; for (int i = 301; i <= 350; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current < 11.01); prev = current; } // Values above 350 should be 11 for (int i = 350; i <= 360; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 11); } } void SmoothLooseCanReverse() { Properties p; p.set("foo", "50~=0; 60~=100; 100~=110; 150~=200; 200~=110; 240~=100; 260~=50; 300~=200; 301~=10; " "350~=11"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 50 should all be 0 for (int i = 0; i <= 50; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 0); } // Values from 50 to 150 will cusp for (int i = 50; i <= 150; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current >= 0); QVERIFY(current < 200.01); } // Values from 150 to 260 will cusp for (int i = 150; i <= 260; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current < 200.01); QVERIFY(current > 47); } // Values from 260 to 300 will overshoot for (int i = 260; i <= 300; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > 49.99); QVERIFY(current < 205.01); } // Values above 301 to 350 will overshoot below 10 for (int i = 301; i <= 350; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > -3.8); QVERIFY(current < 11.01); } // Values above 350 should be 11 for (int i = 350; i <= 360; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 11); } } void SmoothNaturalDoesNotReverse() { Properties p; double prev; // This sequence of keyframes has abrupt changes. For some interpolation algorithms, this // can result in values changing direction along the interpolation path (cusp or // overshoot). // The purpose of this test is to ensure that values do not reverse direction (exept as // expected at keyframes if the user specified a direction change). p.set("foo", "50$=0; 60$=100; 100$=110; 150$=200; 200$=110; 240$=100; 260$=50; 300$=200; 301$=10; " "350$=11"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 50 should all be 0 for (int i = 0; i <= 50; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 0); } // Values from 50 to 150 should only go up prev = -0.01; for (int i = 50; i <= 150; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current < 200.01); prev = current; } // Values from 150 to 260 should only go down prev = 200.01; for (int i = 150; i <= 260; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current < prev); prev = current; } // Values from 260 to 300 should only go up prev = 49.99; for (int i = 260; i <= 300; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current < 200.01); prev = current; } // Values above 301 to 350 should go up from 10 to 11 prev = 9.99; for (int i = 301; i <= 350; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current < 11.01); prev = current; } // Values above 350 should be 11 for (int i = 350; i <= 360; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 11); } } void SmoothTightDoesNotReverse() { Properties p; double prev; // This sequence of keyframes has abrupt changes. For some interpolation algorithms, this // can result in values changing direction along the interpolation path (cusp or // overshoot). // The purpose of this test is to ensure that values do not reverse direction (exept as // expected at keyframes if the user specified a direction change). p.set("foo", "50-=0; 60-=100; 100-=110; 150-=200; 200-=110; 240-=100; 260-=50; 300-=200; 301-=10; " "350-=11"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); // Values from 0 to 50 should all be 0 for (int i = 0; i <= 50; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 0); } // Values from 50 to 150 should only go up prev = -0.01; for (int i = 50; i <= 150; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current < 200.01); prev = current; } // Values from 150 to 260 should only go down prev = 200.01; for (int i = 150; i <= 260; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current < prev); prev = current; } // Values from 260 to 300 should only go up prev = 49.99; for (int i = 260; i <= 300; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current < 200.01); prev = current; } // Values above 301 to 350 should go up from 10 to 11 prev = 9.99; for (int i = 301; i <= 350; i++) { double current = p.anim_get_double("foo", i); QVERIFY(current > prev); QVERIFY(current < 11.01); prev = current; } // Values above 350 should be 11 for (int i = 350; i <= 360; i++) { double current = p.anim_get_double("foo", i); QCOMPARE(current, 11); } } void EaseIn() { Properties p; p.set("sinu", "0a=0; 100a=100;"); p.set("quad", "0d=0; 100d=100;"); p.set("cube", "0g=0; 100g=100;"); p.set("quar", "0j=0; 100j=100;"); p.set("quin", "0m=0; 100m=100;"); p.set("expo", "0p=0; 100p=100;"); p.set("circ", "0s=0; 100s=100;"); p.set("back", "0v=0; 100v=100;"); p.set("elas", "0y=0; 100y=100;"); p.set("boun", "0B=0; 100B=100;"); p.anim_get_int("sinu", 0); p.anim_get_int("quad", 0); p.anim_get_int("cube", 0); p.anim_get_int("quar", 0); p.anim_get_int("quin", 0); p.anim_get_int("expo", 0); p.anim_get_int("circ", 0); p.anim_get_int("back", 0); p.anim_get_int("elas", 0); p.anim_get_int("boun", 0); // fprintf(stderr, "sinu\tquad\tcube\tquar\tquin\texpo\tcirc\tback\telas\tboun\n"); for (int i = 0; i <= 100; i++) { double sinu = p.anim_get_double("sinu", i); double quad = p.anim_get_double("quad", i); double cube = p.anim_get_double("cube", i); double quar = p.anim_get_double("quar", i); double quin = p.anim_get_double("quin", i); double expo = p.anim_get_double("expo", i); double circ = p.anim_get_double("circ", i); double back = p.anim_get_double("back", i); double elas = p.anim_get_double("elas", i); double boun = p.anim_get_double("boun", i); // fprintf(stderr,"%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n", sinu, quad, cube, quar, quin, expo, circ, back, elas, boun); QVERIFY(sinu >= 0.0); QVERIFY(sinu <= 100.0); QVERIFY(quad >= 0.0); QVERIFY(quad <= 100.0); QVERIFY(quad <= sinu); QVERIFY(cube >= 0.0); QVERIFY(cube <= 100.0); QVERIFY(cube <= quad); QVERIFY(quar >= 0.0); QVERIFY(quar <= 100.0); QVERIFY(quar <= cube); QVERIFY(quin >= 0.0); QVERIFY(quin <= 100.0); QVERIFY(quin <= quar); QVERIFY(expo >= 0.0); QVERIFY(expo <= 100.0); QVERIFY(circ >= 0.0); QVERIFY(circ <= 100.0); QVERIFY(back >= -37.88); QVERIFY(back <= 100.0); QVERIFY(elas >= -36.39); QVERIFY(elas <= 100.0); QVERIFY(boun >= -0.00000001); QVERIFY(boun <= 100.0); } } void EaseOut() { Properties p; p.set("sinu", "0b=0; 100b=100;"); p.set("quad", "0e=0; 100e=100;"); p.set("cube", "0h=0; 100h=100;"); p.set("quar", "0k=0; 100k=100;"); p.set("quin", "0n=0; 100n=100;"); p.set("expo", "0q=0; 100q=100;"); p.set("circ", "0t=0; 100t=100;"); p.set("back", "0w=0; 100w=100;"); p.set("elas", "0z=0; 100z=100;"); p.set("boun", "0C=0; 100C=100;"); p.anim_get_int("sinu", 0); p.anim_get_int("quad", 0); p.anim_get_int("cube", 0); p.anim_get_int("quar", 0); p.anim_get_int("quin", 0); p.anim_get_int("expo", 0); p.anim_get_int("circ", 0); p.anim_get_int("back", 0); p.anim_get_int("elas", 0); p.anim_get_int("boun", 0); // fprintf(stderr, "sinu\tquad\tcube\tquar\tquin\texpo\tcirc\tback\telas\tboun\n"); for (int i = 0; i <= 100; i++) { double sinu = p.anim_get_double("sinu", i); double quad = p.anim_get_double("quad", i); double cube = p.anim_get_double("cube", i); double quar = p.anim_get_double("quar", i); double quin = p.anim_get_double("quin", i); double expo = p.anim_get_double("expo", i); double circ = p.anim_get_double("circ", i); double back = p.anim_get_double("back", i); double elas = p.anim_get_double("elas", i); double boun = p.anim_get_double("boun", i); // fprintf(stderr,"%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n", sinu, quad, cube, quar, quin, expo, circ, back, elas, boun); QVERIFY(sinu >= 0.0); QVERIFY(sinu <= 100.0); QVERIFY(quad >= 0.0); QVERIFY(quad <= 100.0); QVERIFY(quad >= sinu); QVERIFY(cube >= 0.0); QVERIFY(cube <= 100.0); QVERIFY(cube >= quad); QVERIFY(quar >= 0.0); QVERIFY(quar <= 100.0); QVERIFY(quar >= cube); QVERIFY(quin >= 0.0); QVERIFY(quin <= 100.0); QVERIFY(quin >= quar); QVERIFY(expo >= 0.0); QVERIFY(expo <= 100.0); QVERIFY(circ >= 0.0); QVERIFY(circ <= 100.0); QVERIFY(back >= 0.0); QVERIFY(back <= 137.9); QVERIFY(elas >= 0.0); QVERIFY(elas <= 136.4); QVERIFY(boun >= 0.0); QVERIFY(boun <= 100.1); } } void EaseInOut() { Properties p; p.set("sinu", "0c=0; 100c=100;"); p.set("quad", "0f=0; 100f=100;"); p.set("cube", "0i=0; 100i=100;"); p.set("quar", "0l=0; 100l=100;"); p.set("quin", "0o=0; 100o=100;"); p.set("expo", "0r=0; 100r=100;"); p.set("circ", "0u=0; 100u=100;"); p.set("back", "0x=0; 100x=100;"); p.set("elas", "0A=0; 100A=100;"); p.set("boun", "0D=0; 100D=100;"); p.anim_get_int("sinu", 0); p.anim_get_int("quad", 0); p.anim_get_int("cube", 0); p.anim_get_int("quar", 0); p.anim_get_int("quin", 0); p.anim_get_int("expo", 0); p.anim_get_int("circ", 0); p.anim_get_int("back", 0); p.anim_get_int("elas", 0); p.anim_get_int("boun", 0); // fprintf(stderr, "sinu\tquad\tcube\tquar\tquin\texpo\tcirc\tback\telas\tboun\n"); for (int i = 0; i <= 100; i++) { double sinu = p.anim_get_double("sinu", i); double quad = p.anim_get_double("quad", i); double cube = p.anim_get_double("cube", i); double quar = p.anim_get_double("quar", i); double quin = p.anim_get_double("quin", i); double expo = p.anim_get_double("expo", i); double circ = p.anim_get_double("circ", i); double back = p.anim_get_double("back", i); double elas = p.anim_get_double("elas", i); double boun = p.anim_get_double("boun", i); // fprintf(stderr,"%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n", sinu, quad, cube, quar, quin, expo, circ, back, elas, boun); if (i < 50) { QVERIFY(quad <= sinu); QVERIFY(cube <= quad); QVERIFY(quar <= cube); QVERIFY(quin <= quar); } else { QVERIFY(quad >= sinu); QVERIFY(cube >= quad); QVERIFY(quar >= cube); QVERIFY(quin >= quar); } QVERIFY(sinu >= 0.0); QVERIFY(sinu <= 100.0); QVERIFY(quad >= 0.0); QVERIFY(quad <= 100.0); QVERIFY(cube >= 0.0); QVERIFY(cube <= 100.0); QVERIFY(quar >= 0.0); QVERIFY(quar <= 100.0); QVERIFY(quin >= 0.0); QVERIFY(quin <= 100.0); QVERIFY(expo >= 0.0); QVERIFY(expo <= 100.0); QVERIFY(circ >= 0.0); QVERIFY(circ <= 100.0); QVERIFY(back >= -19.0); QVERIFY(back <= 119.0); QVERIFY(elas >= -18.2); QVERIFY(elas <= 118.2); QVERIFY(boun >= -0.01); QVERIFY(boun <= 100.1); } } }; QTEST_APPLESS_MAIN(TestAnimation) #include "test_animation.moc" mlt-7.38.0/src/tests/test_audio/000775 000000 000000 00000000000 15172202314 016453 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_audio/test_audio.cpp000664 000000 000000 00000002677 15172202314 021333 0ustar00rootroot000000 000000 /* * Copyright (C) 2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestAudio : public QObject { Q_OBJECT private Q_SLOTS: void DefaultConstructor() { Audio a; QVERIFY(a.samples() == 0); } void ConstructFromAudio() { mlt_audio audio = mlt_audio_new(); audio->samples = 500; Audio a(audio); QVERIFY(a.samples() == 500); } void GetSetData() { Audio a; void *data = malloc(500); a.set_data(data); QVERIFY(a.data() == data); free(data); a.set_data(nullptr); } }; QTEST_APPLESS_MAIN(TestAudio) #include "test_audio.moc" mlt-7.38.0/src/tests/test_events/000775 000000 000000 00000000000 15172202314 016656 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_events/test_events.cpp000664 000000 000000 00000005421 15172202314 021727 0ustar00rootroot000000 000000 /* * Copyright (C) 2019-2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestEvents : public QObject { Q_OBJECT private: mlt_properties m_properties; public: TestEvents() : m_properties(nullptr) { Factory::init(); } private: void checkOwner(mlt_properties owner) { QVERIFY(owner == m_properties); } static void onPropertyChanged(mlt_properties owner, TestEvents *self, mlt_event_data data) { QVERIFY(self != nullptr); QCOMPARE(Mlt::EventData(data).to_string(), "foo"); self->checkOwner(owner); } private Q_SLOTS: void ListenToPropertyChanged() { Profile profile; Producer producer(profile, "noise"); QVERIFY(producer.is_valid()); Event *event = producer.listen("property-changed", this, (mlt_listener) onPropertyChanged); QVERIFY(event != nullptr); QVERIFY(event->is_valid()); m_properties = producer.get_properties(); producer.set("foo", 1); delete event; } void BlockEvent() { Profile profile; Producer producer(profile, "noise"); m_properties = nullptr; Event *event = producer.listen("property-changed", nullptr, (mlt_listener) onPropertyChanged); QVERIFY(event != nullptr); QVERIFY(event->is_valid()); event->block(); producer.set("bar", 1); delete event; } void UnblockEvent() { Profile profile; Producer producer(profile, "noise"); m_properties = producer.get_properties(); Event *event = producer.listen("property-changed", this, (mlt_listener) onPropertyChanged); QVERIFY(event != nullptr); QVERIFY(event->is_valid()); event->block(); producer.set("bar", 1); event->unblock(); producer.set("foo", 1); delete event; } }; QTEST_APPLESS_MAIN(TestEvents) #include "test_events.moc" mlt-7.38.0/src/tests/test_filter/000775 000000 000000 00000000000 15172202314 016637 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_filter/test_filter.cpp000664 000000 000000 00000004240 15172202314 021667 0ustar00rootroot000000 000000 /* * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestFilter : public QObject { Q_OBJECT mlt_locale_t locale; public: TestFilter() { #if defined(__linux__) || defined(__APPLE__) locale = newlocale(LC_NUMERIC_MASK, "POSIX", NULL); #endif Factory::init(); } ~TestFilter() { #if defined(__linux__) || defined(__APPLE__) freelocale(locale); #endif } private Q_SLOTS: void ProcessModifiesFrame() { Profile profile("dv_ntsc"); Producer producer(profile, "noise", NULL); Filter filter(profile, "resize"); int width = 0; int height = 0; mlt_image_format format = mlt_image_rgb; // Get a frame from the producer Frame *frame = producer.get_frame(); // Get the default image size: width should match profile frame->get_image(format, width, height, 0); QCOMPARE(width, 720); // Without applying the filter, the width request is not honored. width = 360; frame->get_image(format, width, height, 0); QCOMPARE(width, 720); // Apply the filter and the requested width will be provided width = 360; filter.process(*frame); frame->get_image(format, width, height, 0); QCOMPARE(width, 360); delete frame; } }; QTEST_APPLESS_MAIN(TestFilter) #include "test_filter.moc" mlt-7.38.0/src/tests/test_frame/000775 000000 000000 00000000000 15172202314 016444 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_frame/test_frame.cpp000664 000000 000000 00000005653 15172202314 021312 0ustar00rootroot000000 000000 /* * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestFrame : public QObject { Q_OBJECT public: TestFrame() {} private Q_SLOTS: void FrameConstructorAddsReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f(frame); QCOMPARE(f.ref_count(), 2); mlt_frame_close(frame); } void CopyConstructorAddsReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f1(frame); QCOMPARE(f1.ref_count(), 2); Frame f2(f1); QCOMPARE(f1.ref_count(), 3); mlt_frame_close(frame); } void ConstCopyConstructorAddsReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f1(frame); QCOMPARE(f1.ref_count(), 2); const Frame &cf1 = f1; // Force const to avoid non-const constructor. Frame f2(cf1); QCOMPARE(f1.ref_count(), 3); QCOMPARE(f2.ref_count(), 3); mlt_frame_close(frame); } void DefaultConstructorIsNotValid() { Frame f1; QCOMPARE(f1.is_valid(), false); QCOMPARE(f1.ref_count(), 0); } void OperatorEqualsAddsReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f1(frame); QCOMPARE(f1.ref_count(), 2); Frame f2; f2 = f1; QCOMPARE(f1.ref_count(), 3); QCOMPARE(f2.ref_count(), 3); mlt_frame_close(frame); } void DestructionRemovesReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f1(frame); QCOMPARE(f1.ref_count(), 2); Frame *f2 = new Frame(f1); QCOMPARE(f2->ref_count(), 3); delete f2; QCOMPARE(f1.ref_count(), 2); mlt_frame_close(frame); } }; QTEST_APPLESS_MAIN(TestFrame) #include "test_frame.moc" mlt-7.38.0/src/tests/test_image/000775 000000 000000 00000000000 15172202314 016434 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_image/test_image.cpp000664 000000 000000 00000012671 15172202314 021270 0ustar00rootroot000000 000000 /* * Copyright (C) 2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestImage : public QObject { Q_OBJECT public: TestImage() { Factory::init(); } private Q_SLOTS: void DefaultConstructor() { Image i; QVERIFY(i.width() == 0); } void ConstructFromImage() { mlt_image image = mlt_image_new(); image->width = 500; Image i(image); QCOMPARE(i.width(), 500); } void ConstructAndAlloc() { Image i(1920, 1080, mlt_image_rgb); QVERIFY(i.plane(0) != nullptr); } void PlaneAndStrideRgb() { Image i(1920, 1080, mlt_image_rgb); QVERIFY(i.plane(0) != nullptr); QCOMPARE(i.stride(0), 1920 * 3); QVERIFY(i.plane(1) == nullptr); QCOMPARE(i.stride(1), 0); QVERIFY(i.plane(2) == nullptr); QCOMPARE(i.stride(2), 0); QVERIFY(i.plane(3) == nullptr); QCOMPARE(i.stride(3), 0); } void PlaneAndStrideRgba() { Image i(1920, 1080, mlt_image_rgba); QVERIFY(i.plane(0) != nullptr); QCOMPARE(i.stride(0), 1920 * 4); QVERIFY(i.plane(1) == nullptr); QCOMPARE(i.stride(1), 0); QVERIFY(i.plane(2) == nullptr); QCOMPARE(i.stride(2), 0); QVERIFY(i.plane(3) == nullptr); QCOMPARE(i.stride(3), 0); } void PlaneAndStrideYuv422() { Image i(1920, 1080, mlt_image_yuv422); QVERIFY(i.plane(0) != nullptr); QCOMPARE(i.stride(0), 1920 * 2); QVERIFY(i.plane(1) == nullptr); QCOMPARE(i.stride(1), 0); QVERIFY(i.plane(2) == nullptr); QCOMPARE(i.stride(2), 0); QVERIFY(i.plane(3) == nullptr); QCOMPARE(i.stride(3), 0); } void PlaneAndStrideYuv420p() { Image i(1920, 1080, mlt_image_yuv420p); QVERIFY(i.plane(0) != nullptr); QCOMPARE(i.stride(0), 1920); QVERIFY(i.plane(1) != nullptr); QCOMPARE(i.stride(1), 1920 / 2); QVERIFY(i.plane(2) != nullptr); QCOMPARE(i.stride(2), 1920 / 2); QVERIFY(i.plane(3) == nullptr); QCOMPARE(i.stride(3), 0); } void PlaneAndStrideYuv422p16() { Image i(1920, 1080, mlt_image_yuv420p); QVERIFY(i.plane(0) != nullptr); QCOMPARE(i.stride(0), 1920); QVERIFY(i.plane(1) != nullptr); QCOMPARE(i.stride(1), 1920 / 2); QVERIFY(i.plane(2) != nullptr); QCOMPARE(i.stride(2), 1920 / 2); QVERIFY(i.plane(3) == nullptr); QCOMPARE(i.stride(3), 0); } void GetSetColorspace() { Image i(1920, 1080, mlt_image_rgb); i.set_colorspace(mlt_colorspace_bt709); QCOMPARE(i.colorspace(), mlt_colorspace_bt709); } void InitAlpha() { Image i(1920, 1080, mlt_image_rgb); i.init_alpha(); QVERIFY(i.plane(3) != nullptr); } void ColorTrc() { // Test conversion from short name QCOMPARE(mlt_color_trc_bt709, mlt_image_color_trc_id("bt709")); QCOMPARE(mlt_color_trc_linear, mlt_image_color_trc_id("linear")); // Test conversion from number QCOMPARE(mlt_color_trc_bt709, mlt_image_color_trc_id("1")); QCOMPARE(mlt_color_trc_linear, mlt_image_color_trc_id("8")); } void Colorspace() { // Test conversion from short name QCOMPARE(mlt_image_colorspace_id("bt709"), mlt_colorspace_bt709); QCOMPARE(mlt_image_colorspace_id("bt470bg"), mlt_colorspace_bt470bg); QCOMPARE(mlt_image_colorspace_id("bt601"), mlt_colorspace_bt601); QCOMPARE(mlt_image_colorspace_id("rgb"), mlt_colorspace_rgb); // Test conversion from number QCOMPARE(mlt_image_colorspace_id("709"), mlt_colorspace_bt709); QCOMPARE(mlt_image_colorspace_id("470"), mlt_colorspace_bt470bg); QCOMPARE(mlt_image_colorspace_id("601"), mlt_colorspace_bt601); QCOMPARE(mlt_image_colorspace_id("0"), mlt_colorspace_rgb); } void ColorPrimaries() { // Test conversion from short name QCOMPARE(mlt_image_color_pri_id("bt709"), mlt_color_pri_bt709); QCOMPARE(mlt_image_color_pri_id("bt470bg"), mlt_color_pri_bt470bg); QCOMPARE(mlt_image_color_pri_id("smpte170m"), mlt_color_pri_smpte170m); QCOMPARE(mlt_image_color_pri_id("bt2020"), mlt_color_pri_bt2020); // Test conversion from number QCOMPARE(mlt_image_color_pri_id("1"), mlt_color_pri_bt709); QCOMPARE(mlt_image_color_pri_id("5"), mlt_color_pri_bt470bg); QCOMPARE(mlt_image_color_pri_id("6"), mlt_color_pri_smpte170m); QCOMPARE(mlt_image_color_pri_id("9"), mlt_color_pri_bt2020); } }; QTEST_APPLESS_MAIN(TestImage) #include "test_image.moc" mlt-7.38.0/src/tests/test_mod_avformat/000775 000000 000000 00000000000 15172202314 020030 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_mod_avformat/test_mod_avformat.cpp000664 000000 000000 00000002547 15172202314 024261 0ustar00rootroot000000 000000 /* * Copyright (C) 2026 Julius Künzel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestModAvformat : public QObject { Q_OBJECT public: TestModAvformat() { Factory::init(); } ~TestModAvformat() {} private Q_SLOTS: void BasicAvformatProducer() { Profile profile; Producer producer(profile, "avformat:blue.mpg"); } void BasicAvformatNovalidateProducer() { Profile profile; Producer producer(profile, "avformat-novalidate:blue.mpg"); } }; QTEST_APPLESS_MAIN(TestModAvformat) #include "test_mod_avformat.moc" mlt-7.38.0/src/tests/test_mod_qt_gps/000775 000000 000000 00000000000 15172202314 017506 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_mod_qt_gps/test_mod_qt_gps.cpp000664 000000 000000 00000006275 15172202314 023417 0ustar00rootroot000000 000000 /* * Copyright (C) 2025 Julius Künzel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include extern int64_t datetimeXMLstring_to_mseconds(const char *text, char *format); class TestModQt : public QObject { Q_OBJECT public: TestModQt() {} ~TestModQt() {} private Q_SLOTS: void DatetimeXMLstring_to_mseconds_ValidConversion() { int64_t gps_proc_t = 0; // ISO 8601 extended date with timezone info Z const char *text = "2020-07-11T09:03:23.000Z"; gps_proc_t = datetimeXMLstring_to_mseconds(text); QCOMPARE(gps_proc_t, 1594458203000); // ISO 8601 extended date with timezone offset text = "2021-02-27T12:10:00+00:00"; gps_proc_t = datetimeXMLstring_to_mseconds(text); QCOMPARE(gps_proc_t, 1614427800000); // ISO 8601 extended date with ms and timezone offset text = "2020-07-11T09:03:23.000+00:00"; gps_proc_t = datetimeXMLstring_to_mseconds(text); QCOMPARE(gps_proc_t, 1594458203000); // ISO 8601 extended date without timezone info text = "2020-07-11T09:03:23.000"; gps_proc_t = datetimeXMLstring_to_mseconds(text); QCOMPARE(gps_proc_t, 1594458203000); } void DatetimeXMLstring_to_mseconds_FormatString() { int64_t gps_proc_t = 0; // Unknown input, with format string as hint const char *text = "12:10:00 27-02-2021"; gps_proc_t = datetimeXMLstring_to_mseconds(text, (char *) "HH:mm:ss dd-MM-yyyy"); QCOMPARE(gps_proc_t, 1614427800000); // Unknown input, without format string text = "12:10:00 27-02-2021"; gps_proc_t = datetimeXMLstring_to_mseconds(text); QCOMPARE(gps_proc_t, 0); // Valid input, but mismatching format string text = "2021-02-27T12:10:00+00:00"; gps_proc_t = datetimeXMLstring_to_mseconds(text, (char *) "HH:mm:ss dd-MM-yyyy"); QCOMPARE(gps_proc_t, 0); } void DatetimeXMLstring_to_mseconds_InvalidInput() { int64_t gps_proc_t = 0; // Invalid input, without format string const char *text = "invalid-date-format"; gps_proc_t = datetimeXMLstring_to_mseconds(text); QCOMPARE(gps_proc_t, 0); // Invalid input, with format string gps_proc_t = datetimeXMLstring_to_mseconds(text, (char *) "%Y-%m-%dT%H:%M:%S"); QCOMPARE(gps_proc_t, 0); } }; QTEST_APPLESS_MAIN(TestModQt) #include "test_mod_qt_gps.moc" mlt-7.38.0/src/tests/test_multitrack/000775 000000 000000 00000000000 15172202314 017531 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_multitrack/test_multitrack.cpp000664 000000 000000 00000006706 15172202314 023464 0ustar00rootroot000000 000000 /* * Copyright (C) 2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestMultitrack : public QObject { Q_OBJECT Profile profile; public: TestMultitrack() : profile("dv_pal") { Factory::init(); } private Q_SLOTS: void TestConstructor() { mlt_multitrack multitrack = mlt_multitrack_init(); QVERIFY(multitrack != nullptr); Multitrack multiTrack(multitrack); QVERIFY(multiTrack.get_multitrack() == multitrack); mlt_multitrack_close(multitrack); } void ConnectAndDisconnect() { mlt_multitrack multitrack = mlt_multitrack_init(); Producer producer(profile, "color"); Multitrack multiTrack(multitrack); QVERIFY(multiTrack.connect(producer, 0) == 0); QCOMPARE(multiTrack.count(), 1); QVERIFY(multiTrack.disconnect(0) == 0); QCOMPARE(multiTrack.count(), 0); mlt_multitrack_close(multitrack); } void InsertAndClip() { mlt_multitrack multitrack = mlt_multitrack_init(); Producer producer(profile, "color"); Multitrack multiTrack(multitrack); QVERIFY(multiTrack.insert(producer, 0) == 0); QCOMPARE(multiTrack.count(), 1); QVERIFY(multiTrack.clip(::mlt_whence_relative_start, 0) == 0); mlt_multitrack_close(multitrack); } void GetTrack() { mlt_multitrack multitrack = mlt_multitrack_init(); Producer producer(profile, "color"); Multitrack multiTrack(multitrack); QVERIFY(multiTrack.insert(producer, 0) == 0); Producer *track = multiTrack.track(0); QVERIFY(track != nullptr); QCOMPARE(track->get_producer(), producer.get_producer()); delete track; mlt_multitrack_close(multitrack); } void GetFrame() { mlt_multitrack multitrack = mlt_multitrack_init(); Producer producer(profile, "color"); Multitrack multiTrack(multitrack); QVERIFY(multiTrack.connect(producer, 0) == 0); auto position = 3; multiTrack.seek(position); auto frame = multiTrack.get_frame(); QVERIFY(frame != nullptr); QCOMPARE(frame->get_position(), position); // The color producer created the frame QCOMPARE(frame->get_int("progressive"), 1); delete frame; // Test hiding and muting the producer producer.set("hide", 3); frame = multiTrack.get_frame(); QVERIFY(frame != nullptr); // Returns a dummy frame QCOMPARE(frame->get_int("progressive"), 0); delete frame; mlt_multitrack_close(multitrack); } }; QTEST_APPLESS_MAIN(TestMultitrack) #include "test_multitrack.moc" mlt-7.38.0/src/tests/test_playlist/000775 000000 000000 00000000000 15172202314 017213 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_playlist/test_playlist.cpp000664 000000 000000 00000017020 15172202314 022617 0ustar00rootroot000000 000000 /* * Copyright (C) 2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestPlaylist : public QObject { Q_OBJECT Profile profile; public: TestPlaylist() : profile("dv_pal") { Factory::init(); } private Q_SLOTS: void Append() { Playlist pl(profile); QVERIFY(pl.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(pl.count(), 0); int result = pl.append(p); QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 1); } void Remove() { Playlist pl(profile); QVERIFY(pl.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(pl.count(), 0); pl.append(p); QCOMPARE(pl.count(), 1); int result = pl.remove(0); QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 0); } void RemoveErrorOnInvalidIndex() { Playlist pl(profile); QVERIFY(pl.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(pl.count(), 0); pl.append(p); QCOMPARE(pl.count(), 1); int result = pl.remove(10); // Invalid index QCOMPARE(result, 1); // Fail QCOMPARE(pl.count(), 1); } void Move() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Move first to last int result = pl.move(0, 2); QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 3); // New order: 2, 3, 1 Producer *pp1 = pl.get_clip(0); Producer *pp2 = pl.get_clip(1); Producer *pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 2); QCOMPARE(pp2->parent().get_int("id"), 3); QCOMPARE(pp3->parent().get_int("id"), 1); delete pp1; delete pp2; delete pp3; } void MoveCorrectInvalidIndex() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Move first to an invalid index int result = pl.move(0, 10); // 10 is invalid // This is still successful because move() corrects the index to be the last entry. QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 3); // The first will be moved to the last after move() corrects the index. Producer *pp1 = pl.get_clip(0); Producer *pp2 = pl.get_clip(1); Producer *pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 2); QCOMPARE(pp2->parent().get_int("id"), 3); QCOMPARE(pp3->parent().get_int("id"), 1); delete pp1; delete pp2; delete pp3; } void Reorder() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Reverse the order of the clips. int new_order[] = {2, 1, 0}; int result = pl.reorder(new_order); QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 3); // The order will be reversed. Producer *pp1 = pl.get_clip(0); Producer *pp2 = pl.get_clip(1); Producer *pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 3); QCOMPARE(pp2->parent().get_int("id"), 2); QCOMPARE(pp3->parent().get_int("id"), 1); delete pp1; delete pp2; delete pp3; } void ReorderErrorOnDuplicate() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Provide an order with duplicate indices. int new_order[] = {2, 2, 2}; int result = pl.reorder(new_order); QCOMPARE(result, 1); // Fail QCOMPARE(pl.count(), 3); // The order will unchanged. Producer *pp1 = pl.get_clip(0); Producer *pp2 = pl.get_clip(1); Producer *pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 1); QCOMPARE(pp2->parent().get_int("id"), 2); QCOMPARE(pp3->parent().get_int("id"), 3); delete pp1; delete pp2; delete pp3; } void ReorderErrorOnInvalidIndex() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Provide an order with an invalid index. int new_order[] = {0, 1, 10}; int result = pl.reorder(new_order); QCOMPARE(result, 1); // Fail QCOMPARE(pl.count(), 3); // The order will unchanged. Producer *pp1 = pl.get_clip(0); Producer *pp2 = pl.get_clip(1); Producer *pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 1); QCOMPARE(pp2->parent().get_int("id"), 2); QCOMPARE(pp3->parent().get_int("id"), 3); delete pp1; delete pp2; delete pp3; } }; QTEST_APPLESS_MAIN(TestPlaylist) #include "test_playlist.moc" mlt-7.38.0/src/tests/test_producer/000775 000000 000000 00000000000 15172202314 017175 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_producer/test_producer.cpp000664 000000 000000 00000003421 15172202314 022563 0ustar00rootroot000000 000000 /* * Copyright (C) 2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; #include #include #include using namespace Mlt; class TestProducer : public QObject { Q_OBJECT public: TestProducer() { Factory::init(); } private Q_SLOTS: void DefaultConstructorIsInvalid() { Producer p; QVERIFY(!p.is_valid()); } void CutIdentifiesAsProducer() { Profile profile; Producer producer(profile, "noise"); QCOMPARE(producer.type(), mlt_service_producer_type); // Taken as a service, the identity is correct Mlt::Service *cutService = producer.cut(0, 1); QCOMPARE(cutService->type(), mlt_service_producer_type); // Promoted to a producer and the identity is correct Mlt::Producer cutProducer(*cutService); QCOMPARE(cutProducer.type(), mlt_service_producer_type); delete cutService; } }; QTEST_APPLESS_MAIN(TestProducer) #include "test_producer.moc" mlt-7.38.0/src/tests/test_properties/000775 000000 000000 00000000000 15172202314 017546 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_properties/test_properties.cpp000664 000000 000000 00000145562 15172202314 023522 0ustar00rootroot000000 000000 /* * Copyright (C) 2013-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include using namespace Mlt; extern "C" { #define __APPLE__ #include #include } #include static const bool kRunLongTests = true; class TestProperties : public QObject { Q_OBJECT mlt_locale_t locale; public: TestProperties() { #if !defined(_WIN32) && (defined(__GLIBC__) || defined(__APPLE__)) locale = newlocale(LC_NUMERIC_MASK, "POSIX", NULL); #else locale = 0; #endif Factory::init(); } ~TestProperties() { #if !defined(_WIN32) && (defined(__GLIBC__) || defined(__APPLE__)) freelocale(locale); #endif } private Q_SLOTS: void InstantiationIsAReference() { Properties p; QCOMPARE(p.ref_count(), 1); } void CopyAddsReference() { Properties p; Properties q = p; QCOMPARE(p.ref_count(), 2); } void DestructionRemovesReference() { Properties p; Properties *q = new Properties(p); QCOMPARE(p.ref_count(), 2); delete q; QCOMPARE(p.ref_count(), 1); } void SetAndGetString() { Properties p; p.set("key", "value"); QVERIFY(p.get("key")); QVERIFY(QString(p.get("key")) != QStringLiteral("")); QCOMPARE(p.get("key"), "value"); } void SetAndGetInt() { Properties p; int i = 1; p.set("key", i); QCOMPARE(p.get_int("key"), i); } void SetAndGetDouble() { Properties p; double d = 1.0; p.set("key", d); QCOMPARE(p.get_double("key"), d); } void SetAndGetInt64() { Properties p; int64_t i = 1LL << 32; p.set("key", i); QCOMPARE(p.get_int64("key"), i); } void SetAndGetData() { Properties p; const char *value = "value"; char *const s = strdup(value); p.set("key", s, strlen(s), free); int size = 0; QCOMPARE((char *) p.get_data("key", size), value); QCOMPARE(size, int(strlen(value))); } void IntFromString() { Properties p; const char *s = "-1"; int i = -1; p.set("key", i); QCOMPARE(p.get("key"), s); p.set("key", s); QCOMPARE(p.get_int("key"), i); } void Int64FromString() { Properties p; const char *s = "-1"; int64_t i = -1; p.set("key", i); QCOMPARE(p.get("key"), s); p.set("key", s); QCOMPARE(p.get_int64("key"), i); } void DoubleFromString() { Properties p; const char *s = "-1.23456"; double d = -1.23456; p.set("key", d); QCOMPARE(p.get("key"), s); p.set("key", s); QCOMPARE(p.get_double("key"), d); } void SetNullRemovesProperty() { Properties p; const char *s = NULL; p.set("key", "value"); p.set("key", s); QCOMPARE(p.get("key"), s); } void SetAndGetHexColor() { Properties p; const char *hexColorString = "0xaabbccdd"; int hexColorInt = 0xaabbccdd; p.set("key", hexColorString); QCOMPARE(p.get_int("key"), hexColorInt); } void SetAndGetCssColor() { Properties p; const char *cssColorString = "#aabbcc"; int cssColorInt = 0xaabbccff; p.set("key", cssColorString); QCOMPARE(p.get_int("key"), cssColorInt); const char *cssColorAlphaString = "#00aabbcc"; int cssColorAlphaInt = 0xaabbcc00; p.set("key", cssColorAlphaString); QCOMPARE(p.get_int("key"), cssColorAlphaInt); } void SetAndGetTimeCode() { Profile profile; Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33:04"; p.set("key", timeString); QCOMPARE(p.get_int("key"), 1023829); p.set("key", 1023829); QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); } void SetAndGetTimeCodeNtscFps() { Profile profile("atsc_720p_2997"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "00:00:17;09"; // Looking just add seconds: // 510 f / 29.97 fps = 17.017017017 s // 29.97 fps * 17 s = 509.49 f // round(509.49) = 509 f, which it not equal to 510, the original amount. // However, ceiling(509.49) = 510 f // Now, adding on the frames part: // 509.49 f + 9 f = 518.49 f // ceiling(518.49) = 519 f const int frames = 519; p.set("key", timeString); QCOMPARE(p.get_int("key"), frames); p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); } void SetAndGetTimeCode5994Fps() { Profile profile("atsc_720p_5994"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "00:01:00;04"; int frames = 3600; p.set("key", timeString); QCOMPARE(p.get_int("key"), frames); p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); timeString = "00:10:00;01"; frames = 36001 - 9 * 4; p.set("key", timeString); QCOMPARE(p.get_int("key"), frames); p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); } void SetAndGetTimeCodeNonIntFps() { Profile profile("atsc_720p_2398"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33;04"; // 11 * 3600 + 22 * 60 + 33 = 40953 s // floor(23.98 fps * 40953 s) = 981890 f // 981890 f + 4 f = 981894 f int frames = 981894; p.set("key", timeString); QCOMPARE(p.get_int("key"), frames); p.set("key", frames); // floor(981894 f / (24000/1001 * 3600)) = 11 h // 981894 f - floor(11 * 3600 * 24000/1001) = 32444 f // floor(32444 f / (24000/1001 * 60)) = 22 m // 981894 f - floor((11 * 3600 + 22 * 60) * 24000/1001) = 796 f // floor(796 f / (24000/1001)) = 33 s // 981894 f - ceil((11 * 3600 + 22 * 60 + 33) * 24000/1001) = 4f QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); QCOMPARE(p.get_time("key", mlt_time_clock), "11:22:33.200"); // Case that is known to have floating point error frames = 2877; p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_smpte_df), "00:02:00;00"); QCOMPARE(p.get_time("key", mlt_time_clock), "00:02:00.000"); if (kRunLongTests) for (int i = 0; i < 9999999; ++i) { p.set("key", i); // QWARN(p.get_time("key", mlt_time_smpte_df)); p.set("test", p.get_time("key", mlt_time_smpte_df)); QCOMPARE(p.get_int("test"), i); } } void SetAndGetTimeCodeNonDropFrame() { Profile profile("dv_ntsc"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33:04"; const int frames = 1228594; p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_smpte_ndf), timeString); } void SetAndGetTimeClock() { Profile profile; Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33.400"; p.set("key", timeString); QCOMPARE(p.get_int("key"), 1023835); p.set("key", 1023835); QCOMPARE(p.get_time("key", mlt_time_clock), timeString); } void SetAndGetTimeClockNtscFps() { Profile profile("dv_ntsc"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33.400"; const int frames = 1227374; p.set("key", timeString); QCOMPARE(p.get_int("key"), frames); p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_clock), timeString); if (kRunLongTests) for (int i = 0; i < 9999999; ++i) { p.set("key", i); // QWARN(p.get_time("key", mlt_time_clock)); p.set("test", p.get_time("key", mlt_time_clock)); QCOMPARE(p.get_int("test"), i); } } void SetAndGetTimeClockNonIntFps() { Profile profile("atsc_720p_2398"); Properties p; p.set("_profile", profile.get_profile(), 0); if (kRunLongTests) for (int i = 0; i < 9999999; ++i) { p.set("key", i); // QWARN(p.get_time("key", mlt_time_clock)); p.set("test", p.get_time("key", mlt_time_clock)); QCOMPARE(p.get_int("test"), i); } } void SetSimpleMathExpression() { Properties p; p.set("key", "@16.0/9.0 *2 +3 -1"); QCOMPARE(p.get_int("key"), 5); QCOMPARE(p.get_double("key"), 16.0 / 9.0 * 2 + 3 - 1); } void SetMathExpressionWithProperty() { Properties p; p.set("width", 100); p.set("key", "@16.0/9.0 *width"); QCOMPARE(p.get_int("key"), 177); } void PassOneProperty() { Properties p[2]; const char *s = "value"; p[0].set("key", s); QCOMPARE(p[1].get("key"), (void *) 0); p[1].pass_property(p[0], "key"); QCOMPARE(p[1].get("key"), s); } void PassMultipleByPrefix() { Properties p[2]; const char *s = "value"; p[0].set("key.one", s); p[0].set("key.two", s); QCOMPARE(p[1].get("key.one"), (void *) 0); QCOMPARE(p[1].get("key.two"), (void *) 0); p[1].pass_values(p[0], "key."); QCOMPARE(p[1].get("one"), s); QCOMPARE(p[1].get("two"), s); } void PassMultipleByList() { Properties p[2]; const char *s = "value"; p[0].set("key.one", s); p[0].set("key.two", s); QCOMPARE(p[1].get("key.one"), (void *) 0); QCOMPARE(p[1].get("key.two"), (void *) 0); p[1].pass_list(p[0], "key.one key.two"); QCOMPARE(p[1].get("key.one"), s); QCOMPARE(p[1].get("key.two"), s); } void MirrorProperties() { Properties p[2]; p[0].mirror(p[1]); p[0].set("key", "value"); QCOMPARE(p[1].get("key"), "value"); } void InheritProperties() { Properties p[2]; p[0].set("key", "value"); QVERIFY(p[1].get("key") == 0); p[1].inherit(p[0]); QCOMPARE(p[1].get("key"), "value"); } void ParseString() { Properties p; QCOMPARE(p.get("key"), (void *) 0); p.parse("key=value"); QCOMPARE(p.get("key"), "value"); p.parse("key=\"new value\""); QCOMPARE(p.get("key"), "new value"); } void RenameProperty() { Properties p; p.set("key", "value"); QVERIFY(p.get("new key") == 0); p.rename("key", "new key"); QCOMPARE(p.get("new key"), "value"); } void SequenceDetected() { Properties p; p.set("1", 1); p.set("2", 2); p.set("3", 3); QVERIFY(p.is_sequence()); p.set("four", 4); QVERIFY(!p.is_sequence()); } void SerializesToYamlTiny() { Properties p[3]; p[0].set("key1", "value1"); p[0].set("key:2", "value[2]"); p[1].set("1", "value3"); p[1].set("2", "value:4"); p[2].set("1", "value5"); p[2].set("2", "\"value6\""); p[0].set("seq1", p[1].get_properties(), 0); p[0].set("seq'2", p[2].get_properties(), 0); char *serializedYaml = p[0].serialise_yaml(); QCOMPARE(serializedYaml, "---\n" "key1: value1\n" "\"key:2\": \"value[2]\"\n" "seq1:\n" " - value3\n" " - \"value:4\"\n" "\"seq'2\":\n" " - value5\n" " - '\"value6\"'\n" "...\n"); free(serializedYaml); } void SerializesScientificNotationAsFixedPoint() { // Negative exponent in a map value Properties p; p.set("minimum", "1e-08"); char *yaml = p.serialise_yaml(); QVERIFY(QString(yaml).contains("minimum: 0.00000001\n")); free(yaml); // Negative exponent in a sequence value Properties seq; Properties child; child.set("1", "2.5e-3"); seq.set("values", child.get_properties(), 0); yaml = seq.serialise_yaml(); QVERIFY(QString(yaml).contains(" - 0.0025\n")); free(yaml); // Positive exponent should also produce fixed-point Properties p2; p2.set("maximum", "1e+02"); yaml = p2.serialise_yaml(); QVERIFY(QString(yaml).contains("maximum: 100\n")); free(yaml); // Non-numeric strings that merely start with digits must not be rewritten. Properties p3; p3.set("currency", "1euro"); p3.set("example", "1example"); yaml = p3.serialise_yaml(); QVERIFY(QString(yaml).contains("currency: 1euro\n")); QVERIFY(QString(yaml).contains("example: 1example\n")); free(yaml); } void SerializesScientificNotationUsingCLocale() { #if !defined(_WIN32) && (defined(__GLIBC__) || defined(__APPLE__)) mlt_locale_t comma_locale = newlocale(LC_NUMERIC_MASK, "de_DE.UTF-8", NULL); if (!comma_locale) comma_locale = newlocale(LC_NUMERIC_MASK, "fr_FR.UTF-8", NULL); if (!comma_locale) QSKIP("No locale with comma decimal separator available"); mlt_locale_t orig_locale = uselocale(comma_locale); Properties p; p.set("minimum", "1e-08"); char *yaml = p.serialise_yaml(); uselocale(orig_locale); freelocale(comma_locale); QVERIFY(QString(yaml).contains("minimum: 0.00000001\n")); QVERIFY(!QString(yaml).contains(",")); free(yaml); #else QSKIP("Thread-local locale switching test not supported on this platform"); #endif } void ParsesYamlTiny() { QTemporaryFile tempFile; if (tempFile.open()) { tempFile.write("---\n" "key1: value1\n" "\"key:2\":\"value[2]\"\n" "seq1:\n" " - value3\n" " - \"value:4\"\n" "\"seq'2\":\n" " - value5\n" " - \"value:6\"\n" "...\n"); tempFile.close(); } QScopedPointer p( Properties::parse_yaml(tempFile.fileName().toUtf8().constData())); QVERIFY(!p.isNull()); QVERIFY(p->is_valid()); QCOMPARE(p->get("key1"), "value1"); QCOMPARE(p->get("key:2"), "value[2]"); } void RadixRespondsToLocale() { Properties p; p.set_lcnumeric("en_US.UTF-8"); p.set("key", "0.125"); QCOMPARE(p.get_double("key"), double(1) / double(8)); #if !defined(_WIN32) p.set_lcnumeric("de_DE.UTF-8"); p.set("key", "0,125"); QCOMPARE(p.get_double("key"), double(1) / double(8)); #endif } void AnimationInsert() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; item.is_key = 1; item.keyframe_type = mlt_keyframe_discrete; item.property = mlt_property_init(); item.frame = 0; mlt_property_set_string(item.property, "0"); mlt_animation_insert(a, &item); item.frame = 1; mlt_property_set_string(item.property, "1"); mlt_animation_insert(a, &item); item.frame = 2; mlt_property_set_string(item.property, "2"); mlt_animation_insert(a, &item); QCOMPARE(mlt_animation_get_length(a), 2); char *a_serialized = mlt_animation_serialize(a); mlt_animation_parse(a, a_serialized, 0, fps, locale); QCOMPARE(a_serialized, "0|=0;1|=1;2|=2"); if (a_serialized) free(a_serialized); mlt_property_close(item.property); mlt_animation_close(a); } void DoubleAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50=1; 60=60; 100=0", 100, fps, locale); mlt_animation_remove(a, 60); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=1;100=0"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.5); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void IntAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50=100; 60=60; 100=0", 100, fps, locale); mlt_animation_remove(a, 60); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=100;100=0"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 50); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void AnimationWithTimeValueKeyframes() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, ":2.0=1; :4.0=0", 100, fps, locale); char *a_serialized = mlt_animation_serialize(a); // Time serializes to frame units :-\. QCOMPARE(a_serialized, "50=1;100=0"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.5); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void DiscreteIntAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50|=100; 60|=60; 100|=0", 100, fps, locale); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50|=100;60|=60;100|=0"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 55); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 60); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 60); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 60); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void StringAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50=hello world; 60=\"good night\"; 100=bar", 100, fps, locale); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=hello world;60=good night;100=bar"); if (a_serialized) free(a_serialized); mlt_animation_parse(a, "50=hello world; 60=\"good; night\"; 100=bar", 100, fps, locale); a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=hello world;60=\"good; night\";100=bar"); if (a_serialized) free(a_serialized); mlt_animation_parse(a, "50=hello world; 60=\"\"good night\"\"; 100=bar", 100, fps, locale); a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=hello world;60=\"good night\";100=bar"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_string(item.property), "hello world"); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_string(item.property), "hello world"); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_string(item.property), "\"good night\""); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_string(item.property), "bar"); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_string(item.property), "bar"); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void test_property_anim_get_double() { double fps = 25.0; int len = 0; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=100; 20=200"); QCOMPARE(mlt_property_get_double(p, fps, locale), 10.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 0, len), 100.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 15, len), 150.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 20, len), 200.0); mlt_property_set_string(p, "1.5"); QCOMPARE(mlt_property_get_double(p, fps, locale), 1.5); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 10, 100), 1.5); mlt_property_close(p); } void test_property_anim_get_int() { double fps = 25.0; int len = 100; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=100; 20=200"); QCOMPARE(mlt_property_get_int(p, fps, locale), 10); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 0, len), 100); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 15, len), 150); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 20, len), 200); mlt_property_set_string(p, "1.5"); QCOMPARE(mlt_property_get_int(p, fps, locale), 1); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 10, 100), 1); mlt_property_close(p); } void test_property_anim_get_color() { double fps = 25.0; int len = 100; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=#ffff00ff;20=green"); QCOMPARE(mlt_property_get_int(p, fps, locale), 10); mlt_color color = mlt_property_anim_get_color(p, fps, locale, 0, len); QCOMPARE(color.r, 255); QCOMPARE(color.g, 0); QCOMPARE(color.b, 255); QCOMPARE(color.a, 255); color = mlt_property_anim_get_color(p, fps, locale, 15, len); QCOMPARE(color.r, 127); QCOMPARE(color.g, 127); QCOMPARE(color.b, 127); QCOMPARE(color.a, 255); color = mlt_property_anim_get_color(p, fps, locale, 20, len); QCOMPARE(color.r, 0); QCOMPARE(color.g, 255); QCOMPARE(color.b, 0); QCOMPARE(color.a, 255); mlt_property_close(p); } void SmoothIntAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "0=80;10~=80; 20~=30; 30~=40; 40~=28; 50=90; 60=0; 70=60; 80=20", 100, fps, locale); item.property = mlt_property_init(); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "0=80;10~=80;20~=30;30~=40;40~=28;50=90;60=0;70=60;80=20"); if (a_serialized) free(a_serialized); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 80); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 90); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 55); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 45); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 60); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 40); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 20); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 20); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void test_property_anim_set_double() { double fps = 25.0; int len = 100; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=100; 20=200"); mlt_property_anim_set_double(p, 1.5, fps, locale, 30, len, mlt_keyframe_linear); QCOMPARE(mlt_property_get_double(p, fps, locale), 10.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 0, len), 100.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 15, len), 150.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 20, len), 200.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 25, len), 100.75); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 30, len), 1.5); mlt_property_close(p); } void test_property_anim_set_int() { double fps = 25.0; int len = 0; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=100; 20=200"); mlt_property_anim_set_int(p, 300, fps, locale, 30, len, mlt_keyframe_linear); QCOMPARE(mlt_property_get_int(p, fps, locale), 10); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 0, len), 100); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 15, len), 150); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 20, len), 200); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 25, len), 250); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 30, len), 300); mlt_property_close(p); } void PercentAsRatio() { Properties p; p.set("foo", "12.3%"); QCOMPARE(p.get_double("foo"), 0.123); p.set("foo", "456 %"); QCOMPARE(p.get_double("foo"), 456.0); } void PropertiesAnimInt() { Properties p; p.set_lcnumeric("POSIX"); // Construct animation from scratch p.anim_set("foo", 0, 0); p.anim_set("foo", 100, 50, -1, mlt_keyframe_smooth); QCOMPARE(p.anim_get_int("foo", 0), 0); QCOMPARE(p.anim_get_int("foo", 25), 50); QCOMPARE(p.anim_get_int("foo", 50), 100); QCOMPARE(p.get("foo"), "0=0;50~=100"); // Animation from string value p.set("foo", "10=100;20=200"); QCOMPARE(p.anim_get_int("foo", 0), 100); QCOMPARE(p.anim_get_int("foo", 15), 150); QCOMPARE(p.anim_get_int("foo", 20), 200); // Animation from string using time clock values // Need to set a profile so fps can be used to convert time to frames. Profile profile("dv_pal"); p.set("_profile", profile.get_profile(), 0); p.set("foo", ":0.0=100; :2.0=200"); QCOMPARE(p.anim_get_int("foo", 0), 100); QCOMPARE(p.anim_get_int("foo", 25), 150); QCOMPARE(p.anim_get_int("foo", 50), 200); } void PropertiesAnimDouble() { Properties p; p.set_lcnumeric("POSIX"); // Construct animation from scratch p.anim_set("foo", 0.0, 0); p.anim_set("foo", 100.0, 50, -1, mlt_keyframe_smooth); QCOMPARE(p.anim_get_double("foo", 0), 0.0); QCOMPARE(p.anim_get_double("foo", 25), 50.0); QCOMPARE(p.anim_get_double("foo", 50), 100.0); QCOMPARE(p.get("foo"), "0=0;50~=100"); // Animation from string value p.set("foo", "10=100.2;20=200.8"); QCOMPARE(p.anim_get_double("foo", 0), 100.2); QCOMPARE(p.anim_get_double("foo", 15), 150.5); QCOMPARE(p.anim_get_double("foo", 20), 200.8); // Animation from string using time clock values // Need to set a profile so fps can be used to convert time to frames. Profile profile("dv_pal"); p.set("_profile", profile.get_profile(), 0); p.set("foo", ":0.0=100; :2.0=200"); QCOMPARE(p.anim_get_double("foo", 0), 100.0); QCOMPARE(p.anim_get_double("foo", 25), 150.0); QCOMPARE(p.anim_get_double("foo", 50), 200.0); // Test a non-animation string. p.set("bar", "0.5"); QCOMPARE(p.anim_get_double("bar", 25), 0.5); } void PropertiesStringAnimation() { Properties p; p.anim_set("key", "foo", 10); p.anim_set("key", "bar", 30); QCOMPARE(p.get("key"), "10|=foo;30|=bar"); p.set("key", "0=; 10=foo bar; 30=hello world"); QCOMPARE(p.anim_get("key", 1), ""); QCOMPARE(p.anim_get("key", 15), "foo bar"); QCOMPARE(p.anim_get("key", 45), "hello world"); } void PropertyRefreshOnAnimationChange() { // Create an animation property from string and see that it works. // Get the animation and modify the first position. // Ensure that change affects other get() functions { Properties p; p.set("foo", "10=100; 20=200"); QCOMPARE(p.get_double("foo"), 10.0); // Call anim_get_double() to create the animation QCOMPARE(p.anim_get_double("foo", 15, 20), 150.0); Mlt::Animation animation = p.get_animation("foo"); animation.key_set_frame(0, 15); QCOMPARE(p.get_double("foo"), 15.0); } { Properties p; p.set("foo", "10=100;20=200"); QCOMPARE(p.anim_get_double("foo", 15, 20), 150.0); Mlt::Animation animation = p.get_animation("foo"); animation.key_set_frame(0, 15); QCOMPARE(p.anim_get_double("foo", 15, 0), 100.0); } { Properties p; p.set("foo", "10=100; 20=200"); QCOMPARE(p.get_int("foo"), 10); // Call anim_get_int() to create the animation QCOMPARE(p.anim_get_int("foo", 15, 20), 150); Mlt::Animation animation = p.get_animation("foo"); animation.key_set_frame(0, 15); QCOMPARE(p.get_int("foo"), 15); } { Properties p; p.set("foo", "10=100; 20=200"); QCOMPARE(p.anim_get_int("foo", 15, 20), 150); Mlt::Animation animation = p.get_animation("foo"); animation.key_set_frame(0, 15); QCOMPARE(p.anim_get_int("foo", 15, 0), 100); } { Properties p; p.set("foo", "10=100;20=200"); // Call anim_get_int() to create the animation QCOMPARE(p.anim_get_int("foo", 15, 20), 150); Mlt::Animation animation = p.get_animation("foo"); QCOMPARE(p.get("foo"), "10=100;20=200"); animation.key_set_frame(0, 15); QCOMPARE(p.get("foo"), "15=100;20=200"); } } void test_mlt_rect() { mlt_property p = mlt_property_init(); mlt_rect r = {1, 2, 3, 4, 5}; mlt_property_set_rect(p, r); QCOMPARE(mlt_property_get_string(p), "1 2 3 4 5"); r.o = DBL_MIN; mlt_property_set_rect(p, r); QCOMPARE(mlt_property_get_string(p), "1 2 3 4"); r.w = DBL_MIN; r.h = DBL_MIN; mlt_property_set_rect(p, r); QCOMPARE(mlt_property_get_string(p), "1 2"); mlt_property_set_string(p, "1.1/2.2:3.3x4.4:5.5"); r = mlt_property_get_rect(p, locale); QCOMPARE(r.x, 1.1); QCOMPARE(r.y, 2.2); QCOMPARE(r.w, 3.3); QCOMPARE(r.h, 4.4); QCOMPARE(r.o, 5.5); mlt_property_set_string(p, "1.1 2.2"); r = mlt_property_get_rect(p, locale); QCOMPARE(r.x, 1.1); QCOMPARE(r.y, 2.2); QCOMPARE(r.w, DBL_MIN); mlt_property_set_int64(p, UINT_MAX); r = mlt_property_get_rect(p, locale); QCOMPARE(r.x, double(UINT_MAX)); mlt_property_close(p); } void SetAndGetRect() { Properties p; mlt_rect r; r.x = 1.1; r.y = 2.2; r.w = 3.3; r.h = 4.4; r.o = 5.5; p.set("key", r); mlt_rect q = p.get_rect("key"); QCOMPARE(q.x, 1.1); QCOMPARE(q.y, 2.2); QCOMPARE(q.w, 3.3); QCOMPARE(q.h, 4.4); QCOMPARE(q.o, 5.5); p.set("key", 10, 20, 30, 40); q = p.get_rect("key"); QCOMPARE(q.x, 10.0); QCOMPARE(q.y, 20.0); QCOMPARE(q.w, 30.0); QCOMPARE(q.h, 40.0); } void RectFromString() { Properties p; p.set_lcnumeric("POSIX"); const char *s = "1.1 2.2 3.3 4.4 5.5"; mlt_rect r = {1.1, 2.2, 3.3, 4.4, 5.5}; p.set("key", r); QCOMPARE(p.get("key"), s); p.set("key", s); r = p.get_rect("key"); QCOMPARE(r.x, 1.1); QCOMPARE(r.y, 2.2); QCOMPARE(r.w, 3.3); QCOMPARE(r.h, 4.4); QCOMPARE(r.o, 5.5); } void RectAnimation() { mlt_rect r1 = {0, 0, 200, 200, 0}; mlt_rect r2 = {100, 100, 400, 400, 1.0}; Properties p; p.set_lcnumeric("POSIX"); // Construct animation from scratch p.anim_set("key", r1, 0); p.anim_set("key", r2, 50); QCOMPARE(p.anim_get_rect("key", 0).x, 0.0); QCOMPARE(p.anim_get_rect("key", 25).x, 50.0); QCOMPARE(p.anim_get_rect("key", 25).y, 50.0); QCOMPARE(p.anim_get_rect("key", 25).w, 300.0); QCOMPARE(p.anim_get_rect("key", 25).h, 300.0); QCOMPARE(p.anim_get_rect("key", 25).o, 0.5); QCOMPARE(p.anim_get_rect("key", 50).x, 100.0); QCOMPARE(p.get("key"), "0=0 0 200 200 0;50=100 100 400 400 1"); // Animation from string value QCOMPARE(p.anim_get_rect("key", 0).x, 0.0); QCOMPARE(p.anim_get_rect("key", 0).y, 0.0); QCOMPARE(p.anim_get_rect("key", 0).w, 200.0); QCOMPARE(p.anim_get_rect("key", 0).h, 200.0); QCOMPARE(p.anim_get_rect("key", 0).o, 0.0); QCOMPARE(p.anim_get_rect("key", 50).x, 100.0); QCOMPARE(p.anim_get_rect("key", 50).y, 100.0); QCOMPARE(p.anim_get_rect("key", 50).w, 400.0); QCOMPARE(p.anim_get_rect("key", 50).h, 400.0); QCOMPARE(p.anim_get_rect("key", 50).o, 1.0); QCOMPARE(p.anim_get_rect("key", 15).x, 30.0); QCOMPARE(p.anim_get_rect("key", 15).y, 30.0); QCOMPARE(p.anim_get_rect("key", 15).w, 260.0); QCOMPARE(p.anim_get_rect("key", 15).h, 260.0); QCOMPARE(p.anim_get_rect("key", 15).o, 0.3); // Smooth animation p.set("key", "0~=0/0:200x200:0; 50=100/100:400x400:1"); QCOMPARE(p.anim_get_rect("key", 0).x, 0.0); QCOMPARE(p.anim_get_rect("key", 0).y, 0.0); QCOMPARE(p.anim_get_rect("key", 0).w, 200.0); QCOMPARE(p.anim_get_rect("key", 0).h, 200.0); QCOMPARE(p.anim_get_rect("key", 0).o, 0.0); QCOMPARE(p.anim_get_rect("key", 50).x, 100.0); QCOMPARE(p.anim_get_rect("key", 50).y, 100.0); QCOMPARE(p.anim_get_rect("key", 50).w, 400.0); QCOMPARE(p.anim_get_rect("key", 50).h, 400.0); QCOMPARE(p.anim_get_rect("key", 50).o, 1.0); QCOMPARE(p.anim_get_rect("key", 15).x, 25.8); QCOMPARE(p.anim_get_rect("key", 15).y, 25.8); QCOMPARE(p.anim_get_rect("key", 15).w, 251.6); QCOMPARE(p.anim_get_rect("key", 15).h, 251.6); QCOMPARE(p.anim_get_rect("key", 15).o, 0.258); // Using percentages p.set("key", "0=0 0; 50=100% 200%"); QCOMPARE(p.anim_get_rect("key", 25).x, 0.5); QCOMPARE(p.anim_get_rect("key", 25).y, 1.0); } void ColorFromInt() { Properties p; p.set_lcnumeric("POSIX"); p.set("key", (int) 0xaabbccdd); mlt_color color = p.get_color("key"); QCOMPARE(color.r, quint8(0xaa)); QCOMPARE(color.g, quint8(0xbb)); QCOMPARE(color.b, quint8(0xcc)); QCOMPARE(color.a, quint8(0xdd)); p.set("key", color); QCOMPARE(p.get_int("key"), int(0xaabbccdd)); } void ColorFromString() { Properties p; p.set_lcnumeric("POSIX"); p.set("key", "red"); mlt_color color = p.get_color("key"); QCOMPARE(color.r, quint8(0xff)); QCOMPARE(color.g, quint8(0x00)); QCOMPARE(color.b, quint8(0x00)); QCOMPARE(color.a, quint8(0xff)); //pattern #AARRGGBB p.set("key", "#deadd00d"); color = p.get_color("key"); QCOMPARE(color.r, quint8(0xad)); QCOMPARE(color.g, quint8(0xd0)); QCOMPARE(color.b, quint8(0x0d)); QCOMPARE(color.a, quint8(0xde)); //pattern #0xRRGGBBAA p.set("key", "0xadd00dde"); color = p.get_color("key"); QCOMPARE(color.r, quint8(0xad)); QCOMPARE(color.g, quint8(0xd0)); QCOMPARE(color.b, quint8(0x0d)); QCOMPARE(color.a, quint8(0xde)); } void StringFromColor() { mlt_color color; color.r = quint8(0xad); color.g = quint8(0xd0); color.b = quint8(0x0d); color.a = quint8(0xde); mlt_property p = mlt_property_init(); mlt_property_set_color(p, color); QCOMPARE(mlt_property_get_string(p), "#deadd00d"); mlt_property_close(p); Properties pies; pies.set("key", color); QCOMPARE(pies.get("key"), "#deadd00d"); } void ColorAnimationCpp() { mlt_color c1 = {0xff, 0xef, 0xab, 0x00}; mlt_color c2 = {0x00, 0xff, 0xab, 0xdf}; Properties p; p.set_lcnumeric("POSIX"); // Construct animation from scratch p.anim_set("key", c1, 0); p.anim_set("key", c2, 50); QCOMPARE(p.anim_get_color("key", 0).r, 255); QCOMPARE(p.anim_get_color("key", 25).g, 247); QCOMPARE(p.anim_get_color("key", 25).b, 171); QCOMPARE(p.anim_get_color("key", 25).a, 111); QCOMPARE(p.get("key"), "0=#00ffefab;50=#df00ffab"); // Animation from string value QCOMPARE(p.anim_get_color("key", 0).r, 255); QCOMPARE(p.anim_get_color("key", 0).g, 239); QCOMPARE(p.anim_get_color("key", 0).b, 171); QCOMPARE(p.anim_get_color("key", 0).a, 0); QCOMPARE(p.anim_get_color("key", 50).r, 0); QCOMPARE(p.anim_get_color("key", 50).g, 255); QCOMPARE(p.anim_get_color("key", 50).b, 171); QCOMPARE(p.anim_get_color("key", 50).a, 223); QCOMPARE(p.anim_get_color("key", 15).r, 178); QCOMPARE(p.anim_get_color("key", 15).g, 243); QCOMPARE(p.anim_get_color("key", 15).b, 171); QCOMPARE(p.anim_get_color("key", 15).a, 66); } void SmoothColorAnimationCpp() { Properties p; p.set_lcnumeric("POSIX"); // Smooth animation p.set("key", "0~=#00ffefab; 50~=#df00ffab; 100~=#df00ffab; 150~=#df00ffab"); QCOMPARE(p.anim_get_color("key", 0).r, 255); QCOMPARE(p.anim_get_color("key", 0).g, 239); QCOMPARE(p.anim_get_color("key", 0).b, 171); QCOMPARE(p.anim_get_color("key", 0).a, 0); QCOMPARE(p.anim_get_color("key", 25).r, 127); QCOMPARE(p.anim_get_color("key", 25).g, 247); QCOMPARE(p.anim_get_color("key", 25).b, 171); QCOMPARE(p.anim_get_color("key", 25).a, 111); QCOMPARE(p.anim_get_color("key", 50).r, 0); QCOMPARE(p.anim_get_color("key", 50).g, 255); QCOMPARE(p.anim_get_color("key", 50).b, 171); QCOMPARE(p.anim_get_color("key", 50).a, 223); QCOMPARE(p.anim_get_color("key", 60).a, 237); QCOMPARE(p.anim_get_color("key", 70).a, 239); QCOMPARE(p.anim_get_color("key", 75).r, 0); QCOMPARE(p.anim_get_color("key", 75).g, 255); QCOMPARE(p.anim_get_color("key", 75).b, 171); QCOMPARE(p.anim_get_color("key", 75).a, 236); QCOMPARE(p.anim_get_color("key", 100).r, 0); QCOMPARE(p.anim_get_color("key", 100).g, 255); QCOMPARE(p.anim_get_color("key", 100).b, 171); QCOMPARE(p.anim_get_color("key", 100).a, 223); } void ColorAnimationCssString() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50=0xff00ffff; 60=0xaa00ffff; 100=0x00ff00ff", 100, fps, locale); mlt_animation_remove(a, 60); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=0xff00ffff;100=0x00ff00ff"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); mlt_color color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 255); QCOMPARE(color.g, 0); QCOMPARE(color.b, 255); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 255); QCOMPARE(color.g, 0); QCOMPARE(color.b, 255); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 127); QCOMPARE(color.g, 127); QCOMPARE(color.b, 127); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 0); QCOMPARE(color.g, 255); QCOMPARE(color.b, 0); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 0); QCOMPARE(color.g, 255); QCOMPARE(color.b, 0); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void ColorAnimationHexString() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50=#ffff00ff; 60=#ffaa00ff; 100=#ff00ff00", 100, fps, locale); mlt_animation_remove(a, 60); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=#ffff00ff;100=#ff00ff00"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); mlt_color color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 255); QCOMPARE(color.g, 0); QCOMPARE(color.b, 255); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 255); QCOMPARE(color.g, 0); QCOMPARE(color.b, 255); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 127); QCOMPARE(color.g, 127); QCOMPARE(color.b, 127); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 0); QCOMPARE(color.g, 255); QCOMPARE(color.b, 0); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); color = mlt_property_get_color(item.property, fps, locale); QCOMPARE(color.r, 0); QCOMPARE(color.g, 255); QCOMPARE(color.b, 0); QCOMPARE(color.a, 255); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void SetIntAndGetAnim() { Properties p; p.set_lcnumeric("POSIX"); p.set("key", 123); QCOMPARE(p.anim_get_int("key", 10, 50), 123); p.set("key", "123"); QCOMPARE(p.anim_get_int("key", 10, 50), 123); } void SetDoubleAndGetAnim() { Properties p; p.set_lcnumeric("POSIX"); p.set("key", 123.0); QCOMPARE(p.anim_get_double("key", 10, 50), 123.0); p.set("key", "123"); QCOMPARE(p.anim_get_double("key", 10, 50), 123.0); } void AnimNegativeTimevalue() { Properties p; Profile profile("dv_pal"); p.set("_profile", profile.get_profile(), 0); p.set_lcnumeric("POSIX"); p.set("key", "0=100; -1=200"); QCOMPARE(p.anim_get_int("key", 75, 100), 175); p.set("key", "0=100; -1:=200"); QCOMPARE(p.anim_get_int("key", 75, 125), 175); } void NestedProperties() { Properties parent; Properties child1; child1.set("c1A", "A"); child1.set("c1B", "B"); parent.set("c1", child1); QCOMPARE(child1.ref_count(), 2); Properties child2; child2.set("c2C", "C"); child2.set("c2D", "D"); parent.set("c2", child2); QCOMPARE(child2.ref_count(), 2); QCOMPARE(parent.count(), 2); Properties *pChild1 = parent.get_props("c1"); QCOMPARE(pChild1->get("c1B"), "B"); delete pChild1; Properties *pChild2 = parent.get_props_at(1); QCOMPARE(pChild2->get("c2D"), "D"); delete pChild2; } void PropertyClears() { Properties p; p.set("key", 1); QCOMPARE(p.get_int("key"), 1); QCOMPARE(p.get("key"), "1"); p.clear("key"); QCOMPARE(p.get("key"), (char *) 0); QCOMPARE(p.get_data("key"), (void *) 0); QCOMPARE(p.get_animation("key"), mlt_animation(0)); QCOMPARE(p.get_int("key"), 0); } void PropertyExists() { Properties p; // Never set should return false QCOMPARE(p.property_exists("key"), false); // Set should return true p.set("key", 1); QCOMPARE(p.property_exists("key"), true); // Cleared should return false p.clear("key"); QCOMPARE(p.property_exists("key"), false); } void AnimationExists() { Properties p; // Never set should return false QCOMPARE(p.property_exists("key"), false); // Get an animation but don't set anything - should return false Mlt::Animation animation = p.get_animation("key"); QCOMPARE(p.property_exists("key"), false); // Set animation should return true p.anim_set("key", 1, 0); QCOMPARE(p.property_exists("key"), true); // Cleared should return false p.clear("key"); QCOMPARE(p.property_exists("key"), false); } void SetString() { Properties p; p.set_string("foo", "123.4"); QCOMPARE(p.get("foo"), "123.4"); QCOMPARE(p.get_int("foo"), 123); QCOMPARE(p.get_double("foo"), 123.4); } }; QTEST_APPLESS_MAIN(TestProperties) #include "test_properties.moc" mlt-7.38.0/src/tests/test_repository/000775 000000 000000 00000000000 15172202314 017571 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_repository/test_repository.cpp000664 000000 000000 00000003111 15172202314 023547 0ustar00rootroot000000 000000 /* * Copyright (C) 2013-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestRepository : public QObject { Q_OBJECT public: TestRepository() {} private Q_SLOTS: void ThereAreProducers() { Repository *r = Factory::init(); Properties *producers = r->producers(); QVERIFY(producers->is_valid()); if (producers->is_valid()) QVERIFY(producers->count() > 0); delete producers; } void ThereAreConsumers() { Repository *r = Factory::init(); Properties *consumers = r->consumers(); QVERIFY(consumers->is_valid()); if (consumers->is_valid()) QVERIFY(consumers->count() > 0); delete consumers; } }; QTEST_APPLESS_MAIN(TestRepository) #include "test_repository.moc" mlt-7.38.0/src/tests/test_service/000775 000000 000000 00000000000 15172202314 017012 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_service/test_service.cpp000664 000000 000000 00000007553 15172202314 022227 0ustar00rootroot000000 000000 /* * Copyright (C) 2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestService : public QObject { Q_OBJECT public: TestService() { repo = Factory::init(); } ~TestService() { Factory::close(); } private Q_SLOTS: void CanIdentifyServicesFromFactory() { Profile profile; Producer producer(profile, "color"); QCOMPARE(mlt_service_identify(producer.get_service()), mlt_service_producer_type); Filter filter(profile, "resize"); QCOMPARE(mlt_service_identify(filter.get_service()), mlt_service_filter_type); Transition transition(profile, "mix"); QCOMPARE(mlt_service_identify(transition.get_service()), mlt_service_transition_type); Consumer consumer(profile, "null"); QCOMPARE(mlt_service_identify(consumer.get_service()), mlt_service_consumer_type); } void CanIdentifyServicesFromAPI() { mlt_profile profile = mlt_profile_init(NULL); mlt_playlist playlist = mlt_playlist_new(profile); QCOMPARE(mlt_service_identify(MLT_PLAYLIST_SERVICE(playlist)), mlt_service_playlist_type); mlt_tractor tractor = mlt_tractor_new(); QCOMPARE(mlt_service_identify(MLT_TRACTOR_SERVICE(tractor)), mlt_service_tractor_type); QCOMPARE(mlt_service_identify(MLT_MULTITRACK_SERVICE(mlt_tractor_multitrack(tractor))), mlt_service_multitrack_type); mlt_producer producer = mlt_producer_new(profile); QCOMPARE(mlt_service_identify(MLT_PRODUCER_SERVICE(producer)), mlt_service_producer_type); mlt_filter filter = mlt_filter_new(); QCOMPARE(mlt_service_identify(MLT_FILTER_SERVICE(filter)), mlt_service_filter_type); mlt_transition transition = mlt_transition_new(); QCOMPARE(mlt_service_identify(MLT_TRANSITION_SERVICE(transition)), mlt_service_transition_type); mlt_consumer consumer = mlt_consumer_new(profile); QCOMPARE(mlt_service_identify(MLT_CONSUMER_SERVICE(consumer)), mlt_service_consumer_type); } void GetSetConsumer() { Service *service; Profile profile; Producer producer(profile, "color"); // Test a default consumer (should be null or invalid) service = producer.consumer(); QVERIFY(!service->is_valid()); delete service; // Test a service connected to a consumer Consumer c(profile, "xml", "string"); c.connect(producer); c.start(); service = producer.consumer(); QCOMPARE(c.get_service(), service->get_service()); delete service; // Replace the consumer service Service invalidService; producer.set_consumer(invalidService); service = producer.consumer(); QCOMPARE(invalidService.get_service(), service->get_service()); delete service; // Restore the consumer service producer.set_consumer(c); service = producer.consumer(); QCOMPARE(c.get_service(), service->get_service()); delete service; } private: Repository *repo; }; QTEST_APPLESS_MAIN(TestService) #include "test_service.moc" mlt-7.38.0/src/tests/test_tractor/000775 000000 000000 00000000000 15172202314 017030 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_tractor/test_tractor.cpp000664 000000 000000 00000027335 15172202314 022263 0ustar00rootroot000000 000000 /* * Copyright (C) 2015-2026 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestTractor : public QObject { Q_OBJECT Profile profile; public: TestTractor() : profile("dv_pal") { Factory::init(); } private Q_SLOTS: void CreateSingleTrack() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p, t.count()); QCOMPARE(t.count(), 1); } void FailSameProducerNewTrack() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p, t.count()); QCOMPARE(t.count(), 1); int result = t.set_track(p, t.count()); QCOMPARE(result, 3); QCOMPARE(t.count(), 1); } void CreateMultipleTracks() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); } void PlantTransitionWorks() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); } void InsertTrackBelowAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.insert_track(p3, 0); QCOMPARE(t.count(), 3); QCOMPARE(trans.get_int("a_track"), 1); QCOMPARE(trans.get_int("b_track"), 2); } void InsertTrackMiddleAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.insert_track(p3, 1); QCOMPARE(t.count(), 3); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 2); } void InsertTrackAboveDoesNotAffectTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.insert_track(p3, 2); QCOMPARE(t.count(), 3); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); } void RemoveTrackBelowAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.set_track(p3, t.count()); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); t.remove_track(0); QCOMPARE(t.count(), 2); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 0); // This transition is a candidate for removal. } void RemoveMiddleTrackAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.set_track(p3, t.count()); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 2); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 2); t.remove_track(1); QCOMPARE(t.count(), 2); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); } void RemoveTrackAboveAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.set_track(p3, t.count()); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 1, 2); QCOMPARE(trans.get_int("a_track"), 1); QCOMPARE(trans.get_int("b_track"), 2); t.remove_track(2); QCOMPARE(t.count(), 2); QCOMPARE(trans.get_int("a_track"), 1); QCOMPARE(trans.get_int("b_track"), 1); // This transition is a candidate for removal. } void PlantFilterWorks() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 0); QCOMPARE(filter.get_track(), 0); } void InsertTrackBelowAdjustsFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 0); QCOMPARE(filter.get_track(), 0); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.insert_track(p2, 0); QCOMPARE(t.count(), 2); QCOMPARE(filter.get_track(), 1); } void InsertTrackAboveDoesNotAffectFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 0); QCOMPARE(filter.get_track(), 0); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.insert_track(p2, 1); QCOMPARE(t.count(), 2); QCOMPARE(filter.get_track(), 0); } void RemoveTrackBelowAdjustsFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 1); QCOMPARE(filter.get_track(), 1); t.remove_track(0); QCOMPARE(t.count(), 1); QCOMPARE(filter.get_track(), 0); } void RemoveFilteredTrackAdjustsFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 1); QCOMPARE(filter.get_track(), 1); t.remove_track(1); QCOMPARE(t.count(), 1); QCOMPARE(filter.get_track(), 0); // This filter is a candidate for removal. } void RemoveTrackAboveDoesNotAffectFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 0); QCOMPARE(filter.get_track(), 0); t.remove_track(1); QCOMPARE(t.count(), 1); QCOMPARE(filter.get_track(), 0); } }; QTEST_APPLESS_MAIN(TestTractor) #include "test_tractor.moc" mlt-7.38.0/src/tests/test_xml/000775 000000 000000 00000000000 15172202314 016152 5ustar00rootroot000000 000000 mlt-7.38.0/src/tests/test_xml/test_xml.cpp000664 000000 000000 00000004076 15172202314 020524 0ustar00rootroot000000 000000 /* * Copyright (C) 2021 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; #include #include #include using namespace Mlt; class TestXml : public QObject { Q_OBJECT public: TestXml() { Factory::init(); } private Q_SLOTS: void NestedPropertiesRoundTrip() { // Create a producer Profile profile; Producer producer(profile, "noise"); // Nest properties two deep Properties child1; producer.set("child1", child1); Properties child2; child2.set("test_param", "C2"); child1.set("child2", child2); // Convert to XML Consumer c(profile, "xml", "string"); c.connect(producer); c.start(); Producer retProducer(profile, "xml-string", c.get("string")); QCOMPARE(retProducer.get("mlt_service"), "noise"); Properties *pchild1 = retProducer.get_props("child1"); QVERIFY(pchild1 != nullptr); QVERIFY(pchild1->is_valid()); Properties *pchild2 = pchild1->get_props("child2"); QVERIFY(pchild2 != nullptr); QVERIFY(pchild2->is_valid()); QCOMPARE(pchild2->get("test_param"), "C2"); delete pchild1; delete pchild2; } }; QTEST_APPLESS_MAIN(TestXml) #include "test_xml.moc" mlt-7.38.0/src/win32/000775 000000 000000 00000000000 15172202314 014113 5ustar00rootroot000000 000000 mlt-7.38.0/src/win32/fnmatch.c000664 000000 000000 00000013430 15172202314 015700 0ustar00rootroot000000 000000 /* * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Guido van Rossum. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. * * From FreeBSD fnmatch.c 1.11 * $Id$ */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94"; #endif /* LIBC_SCCS and not lint */ /* * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. * Compares a filename or pathname to a pattern. */ #include #include #include #include "fnmatch.h" #define EOS '\0' static const char *rangematch(const char *, char, int); int fnmatch(const char *pattern, const char *string, int flags) { const char *stringstart; char c, test; for (stringstart = string;;) switch (c = *pattern++) { case EOS: if ((flags & FNM_LEADING_DIR) && *string == '/') return (0); return (*string == EOS ? 0 : FNM_NOMATCH); case '?': if (*string == EOS) return (FNM_NOMATCH); if (*string == '/' && (flags & FNM_PATHNAME)) return (FNM_NOMATCH); if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); ++string; break; case '*': c = *pattern; /* Collapse multiple stars. */ while (c == '*') c = *++pattern; if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); /* Optimize for pattern with * at end or before /. */ if (c == EOS) if (flags & FNM_PATHNAME) return ((flags & FNM_LEADING_DIR) || strchr(string, '/') == NULL ? 0 : FNM_NOMATCH); else return (0); else if (c == '/' && flags & FNM_PATHNAME) { if ((string = strchr(string, '/')) == NULL) return (FNM_NOMATCH); break; } /* General case, use recursion. */ while ((test = *string) != EOS) { if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) return (0); if (test == '/' && flags & FNM_PATHNAME) break; ++string; } return (FNM_NOMATCH); case '[': if (*string == EOS) return (FNM_NOMATCH); if (*string == '/' && flags & FNM_PATHNAME) return (FNM_NOMATCH); if ((pattern = rangematch(pattern, *string, flags)) == NULL) return (FNM_NOMATCH); ++string; break; case '\\': if (!(flags & FNM_NOESCAPE)) { if ((c = *pattern++) == EOS) { c = '\\'; --pattern; } } /* FALLTHROUGH */ default: if (c == *string) ; else if ((flags & FNM_CASEFOLD) && (tolower((unsigned char)c) == tolower((unsigned char)*string))) ; else if ((flags & FNM_PREFIX_DIRS) && *string == EOS && ((c == '/' && string != stringstart) || (string == stringstart+1 && *stringstart == '/')) ) return (0); else return (FNM_NOMATCH); string++; break; } /* NOTREACHED */ } static const char * rangematch(const char *pattern, char test, int flags) { int negate, ok; char c, c2; /* * A bracket expression starting with an unquoted circumflex * character produces unspecified results (IEEE 1003.2-1992, * 3.13.2). This implementation treats it like '!', for * consistency with the regular expression syntax. * J.T. Conklin (conklin@ngai.kaleida.com) */ if ( (negate = (*pattern == '!' || *pattern == '^')) ) ++pattern; if (flags & FNM_CASEFOLD) test = tolower((unsigned char)test); for (ok = 0; (c = *pattern++) != ']';) { if (c == '\\' && !(flags & FNM_NOESCAPE)) c = *pattern++; if (c == EOS) return (NULL); if (flags & FNM_CASEFOLD) c = tolower((unsigned char)c); if (*pattern == '-' && (c2 = *(pattern+1)) != EOS && c2 != ']') { pattern += 2; if (c2 == '\\' && !(flags & FNM_NOESCAPE)) c2 = *pattern++; if (c2 == EOS) return (NULL); if (flags & FNM_CASEFOLD) c2 = tolower((unsigned char)c2); if ((unsigned char)c <= (unsigned char)test && (unsigned char)test <= (unsigned char)c2) ok = 1; } else if (c == test) ok = 1; } return (ok == negate ? NULL : pattern); } mlt-7.38.0/src/win32/fnmatch.h000664 000000 000000 00000004657 15172202314 015720 0ustar00rootroot000000 000000 /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. * * @(#)fnmatch.h 8.1 (Berkeley) 6/2/93 * * From FreeBSD fnmatch.h 1.7 * $Id$ */ #ifndef _FNMATCH_H_ #define _FNMATCH_H_ #define FNM_NOMATCH 1 /* Match failed. */ #define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ #define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ #define FNM_PERIOD 0x04 /* Period must be matched by period. */ #define FNM_LEADING_DIR 0x08 /* Ignore / after Imatch. */ #define FNM_CASEFOLD 0x10 /* Case insensitive search. */ #define FNM_PREFIX_DIRS 0x20 /* Directory prefixes of pattern match too. */ int fnmatch(const char *pattern, const char *string, int flags); #endif /* !_FNMATCH_H_ */ mlt-7.38.0/src/win32/win32.c000664 000000 000000 00000020173 15172202314 015224 0ustar00rootroot000000 000000 /** * \file win32.c * \brief Miscellaneous utility functions for Windows. * * Copyright (C) 2003-2025 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "../framework/mlt_properties.h" #include "../framework/mlt_log.h" int usleep(unsigned int useconds) { HANDLE timer; LARGE_INTEGER due; due.QuadPart = -(10 * (__int64)useconds); timer = CreateWaitableTimer(NULL, TRUE, NULL); SetWaitableTimer(timer, &due, 0, NULL, NULL, 0); WaitForSingleObject(timer, INFINITE); CloseHandle(timer); return 0; } #ifndef WIN_PTHREADS_TIME_H int nanosleep(const struct timespec * rqtp, struct timespec * rmtp) { if (rqtp->tv_nsec > 999999999) { /* The time interval specified 1,000,000 or more microseconds. */ errno = EINVAL; return -1; } return usleep(rqtp->tv_sec * 1000000 + rqtp->tv_nsec / 1000); } #endif int setenv(const char *name, const char *value, int overwrite) { int result = 1; if (overwrite == 0 && getenv (name)) { result = 0; } else { result = SetEnvironmentVariable (name,value); } return result; } static int iconv_from_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out, const char* encoding ) { char *text = mlt_properties_get( properties, prop_name ); int result = 0; if ( text ) { iconv_t cd = iconv_open( encoding, "UTF-8" ); if ( cd != (iconv_t) -1 ) { size_t inbuf_n = strlen( text ); size_t outbuf_n = inbuf_n * 6; char *outbuf = mlt_pool_alloc( outbuf_n ); char *outbuf_p = outbuf; memset( outbuf, 0, outbuf_n ); if ( text != NULL && strcmp( text, "" ) && iconv( cd, &text, &inbuf_n, &outbuf_p, &outbuf_n ) != -1 ) mlt_properties_set( properties, prop_name_out, outbuf ); else mlt_properties_set( properties, prop_name_out, "" ); mlt_pool_release( outbuf ); result = iconv_close( cd ); } else { result = -1; } } return result; } static int iconv_to_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out, const char* encoding ) { char *text = mlt_properties_get( properties, prop_name ); int result = 0; if ( text ) { iconv_t cd = iconv_open( "UTF-8", encoding ); if ( cd != (iconv_t) -1 ) { size_t inbuf_n = strlen( text ); size_t outbuf_n = inbuf_n * 6; char *outbuf = mlt_pool_alloc( outbuf_n ); char *outbuf_p = outbuf; memset( outbuf, 0, outbuf_n ); if ( text != NULL && strcmp( text, "" ) && iconv( cd, &text, &inbuf_n, &outbuf_p, &outbuf_n ) != -1 ) mlt_properties_set( properties, prop_name_out, outbuf ); else mlt_properties_set( properties, prop_name_out, "" ); mlt_pool_release( outbuf ); result = iconv_close( cd ); } else { result = -1; } } return result; } int mlt_properties_from_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out ) { int result = -1; UINT codepage = GetACP(); if ( codepage > 0 ) { // numeric code page char codepage_str[10]; int n = snprintf(codepage_str, sizeof(codepage_str), "CP%u", codepage); if (n > 0) { codepage_str[sizeof(codepage_str) - 1] = '\0'; result = iconv_from_utf8(properties, prop_name, prop_name_out, codepage_str); } } if ( result < 0 ) { result = mlt_properties_set( properties, prop_name_out, mlt_properties_get( properties, prop_name ) ); mlt_log_warning( NULL, "iconv failed to convert \"%s\" from UTF-8 to code page %u: %s\n", prop_name, codepage, mlt_properties_get( properties, prop_name ) ); } return result; } int mlt_properties_to_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out ) { int result = -1; UINT codepage = GetACP(); if ( codepage > 0 ) { // numeric code page char codepage_str[10]; int n = snprintf(codepage_str, sizeof(codepage_str), "CP%u", codepage); if (n > 0) { codepage_str[sizeof(codepage_str) - 1] = '\0'; result = iconv_to_utf8(properties, prop_name, prop_name_out, codepage_str); } } if ( result < 0 ) { result = mlt_properties_set( properties, prop_name_out, mlt_properties_get( properties, prop_name ) ); mlt_log_warning( NULL, "iconv failed to convert \"%s\" from code page %u to UTF-8\n", prop_name, codepage ); } return result; } /* Adapted from g_win32_getlocale() - free() the result */ char* getlocale() { LCID lcid; LANGID langid; char *ev; int primary, sub; char iso639[10]; char iso3166[10]; const char *script = ""; char result[33]; /* Let the user override the system settings through environment * variables, as on POSIX systems. */ if (((ev = getenv ("LC_ALL")) != NULL && ev[0] != '\0') || ((ev = getenv ("LC_MESSAGES")) != NULL && ev[0] != '\0') || ((ev = getenv ("LANG")) != NULL && ev[0] != '\0')) return strdup (ev); lcid = GetThreadLocale (); if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, sizeof (iso639)) || !GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166))) return strdup ("C"); /* Strip off the sorting rules, keep only the language part. */ langid = LANGIDFROMLCID (lcid); /* Split into language and territory part. */ primary = PRIMARYLANGID (langid); sub = SUBLANGID (langid); /* Handle special cases */ switch (primary) { case LANG_AZERI: switch (sub) { case SUBLANG_AZERI_LATIN: script = "@Latn"; break; case SUBLANG_AZERI_CYRILLIC: script = "@Cyrl"; break; } break; case LANG_SERBIAN: /* LANG_CROATIAN == LANG_SERBIAN */ switch (sub) { case SUBLANG_SERBIAN_LATIN: case 0x06: /* Serbian (Latin) - Bosnia and Herzegovina */ script = "@Latn"; break; } break; case LANG_UZBEK: switch (sub) { case SUBLANG_UZBEK_LATIN: script = "@Latn"; break; case SUBLANG_UZBEK_CYRILLIC: script = "@Cyrl"; break; } break; } snprintf (result, sizeof(result), "%s_%s%s", iso639, iso3166, script); result[sizeof(result) - 1] = '\0'; return strdup (result); } FILE* win32_fopen(const char *filename_utf8, const char *mode_utf8) { // Convert UTF-8 to wide chars. int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename_utf8, -1, NULL, 0); if (n > 0) { wchar_t *filename_w = (wchar_t *) calloc(n, sizeof(wchar_t)); if (filename_w) { int m = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode_utf8, -1, NULL, 0); if (m > 0) { wchar_t *mode_w = (wchar_t *) calloc(m, sizeof(wchar_t)); if (mode_w) { MultiByteToWideChar(CP_UTF8, 0, filename_utf8, -1, filename_w, n); MultiByteToWideChar(CP_UTF8, 0, mode_utf8, -1, mode_w, n); FILE *fh = _wfopen(filename_w, mode_w); free(mode_w); if (fh) return fh; } } free(filename_w); } } // Try with regular old fopen. return fopen(filename_utf8, mode_utf8); } int win32_stat(const char *filename_utf8, struct stat *buffer) { int ret = stat(filename_utf8, buffer); if (!ret) { return ret; } // Convert UTF-8 to wide chars. int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename_utf8, -1, NULL, 0); if (n > 0) { wchar_t *filename_w = (wchar_t *) calloc(n, sizeof(wchar_t)); if (filename_w) { MultiByteToWideChar(CP_UTF8, 0, filename_utf8, -1, filename_w, n); ret = _wstat(filename_w, (struct _stat *)buffer); free(filename_w); return ret; } } return ret; } mlt-7.38.0/vcpkg-configuration.json000664 000000 000000 00000000534 15172202314 017236 0ustar00rootroot000000 000000 { "default-registry": { "kind": "git", "baseline": "cfcbdb245f1179a5a493890a0b69531d66969e62", "repository": "https://github.com/microsoft/vcpkg" }, "registries": [ { "kind": "artifact", "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", "name": "microsoft" } ] } mlt-7.38.0/vcpkg.json000664 000000 000000 00000000713 15172202314 014370 0ustar00rootroot000000 000000 { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "name": "mlt", "dependencies": [ "dirent", "dlfcn-win32", "libiconv", "pkgconf", "pthreads", "sdl2", "ffmpeg", "qtbase", "qtsvg", "libxml2", "libebur128", "gdk-pixbuf", "rubberband", "libvorbis", "fftw3", "libexif", { "name": "opencv", "features": [ "contrib" ] } ] } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/shapes.hpp000664 001750 001750 00000002605 15165022620 032635 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/bezier/bezier.hpp" namespace glaxnimate::math::bezier { inline Bezier rect(const QRectF& r) { Bezier bezier; bezier.add_point(r.topRight()); bezier.add_point(r.bottomRight()); bezier.add_point(r.bottomLeft()); bezier.add_point(r.topLeft()); bezier.close(); return bezier; } inline Bezier ellipse(const QPointF& center, const QSizeF& diameter) { QPointF radius{diameter.width() / 2, diameter.height() / 2}; QPointF tangent = radius * math::ellipse_bezier; qreal x = center.x(); qreal y = center.y(); Bezier shape; shape.close(); shape.add_point( QPointF(x, y - radius.y()), QPointF(-tangent.x(), 0), QPointF(tangent.x(), 0) ); shape.add_point( QPointF(x + radius.x(), y), QPointF(0, -tangent.y()), QPointF(0, tangent.y()) ); shape.add_point( QPointF(x, y + radius.y()), QPointF(tangent.x(), 0), QPointF(-tangent.x(), 0) ); shape.add_point( QPointF(x - radius.x(), y), QPointF(0, tangent.y()), QPointF(0, -tangent.y()) ); return shape; } inline Bezier ellipse(const QRectF& rect) { return ellipse(rect.center(), rect.size()); } } // namespace glaxnimate::math::bezier src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/join_animatables.hpp000664 001750 001750 00000027634 15165022620 035451 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/animation/animatable.hpp" namespace glaxnimate::model { /** * \brief Utility to join keyframes from multiple animatables */ class JoinAnimatables { private: using MidTransition = model::AnimatedPropertyBase::MidTransition; public: struct Keyframe { FrameTime time; std::vector values; std::vector transitions; Keyframe(FrameTime time, std::size_t prop_count) : time(time) { values.reserve(prop_count); transitions.reserve(prop_count); } static KeyframeTransition mix_transitions(const std::vector& transitions) { int count = 0; QPointF in; QPointF out; for ( const auto& transition : transitions ) { if ( !transition.hold() ) { in += transition.before(); out += transition.after(); count++; } } if ( count == 0 ) return {{0, 0}, {1, 1}, model::KeyframeTransition::Special::Hold}; return {in / count, out / count}; } KeyframeTransition transition() const { return mix_transitions(transitions); } }; enum Flags { Normal = 0x00, NoKeyframes = 0x01, NoValues = 0x02, }; using iterator = typename std::vector::const_iterator; JoinAnimatables(std::vector properties, int flags = Normal) : properties_(std::move(properties)) { if ( !(flags & NoKeyframes) ) load_keyframes(flags); } /** * \brief Whether the result has more than one keyframe */ bool animated() const { return raw_keyframes_.size() > 1; } /** * \brief Keyframe range begin */ auto begin() const { return raw_keyframes_.begin(); } /** * \brief Keyframe range end */ auto end() const { return raw_keyframes_.end(); } /** * \brief Current value as a vector of variants */ std::vector current_value() const { std::vector values; values.reserve(properties_.size()); for ( auto prop : properties_ ) values.push_back(prop->static_value()); return values; } /** * \brief Value at time as a vector of variants */ std::vector value_at(FrameTime time) const { std::vector values; values.reserve(properties_.size()); for ( auto prop : properties_ ) values.push_back(prop->value(time)); return values; } const std::vector& properties() const { return properties_; } const std::vector& keyframes() const { return raw_keyframes_; } /** * \brief Current value combined by a callback * \pre each property can be converted to the corresponding \p AnimatedProperty. */ template auto combine_current_value(const Func& func) { return invoke_combine_get(func, std::index_sequence_for()); } /** * \brief Value at a given time combined by a callback * \pre each property can be converted to the corresponding \p AnimatedProperty. */ template auto combine_value_at(FrameTime time, const Func& func) { return invoke_combine_get_at(time, func, std::index_sequence_for()); } /** * \brief Fills \p target with the combined values * \pre Each property can be converted to the corresponding \p AnimatedProperty. * \pre \p target values can be initialized by what \p func returns */ template void apply_to(Target* target, const Func& func, const model::AnimatedProperty*...) { target->clear_keyframes(); target->set(combine_current_value(func)); for ( const auto& keyframe : raw_keyframes_ ) { auto real_kf = target->set_keyframe(keyframe.time, combine_value_at(keyframe.time, func)); real_kf->set_transition(keyframe.transition()); } } private: std::vector properties_; std::vector raw_keyframes_; void load_keyframes(int flags) { std::set time_set; for ( auto prop : properties_ ) for ( const auto& kf : prop->keyframe_range() ) time_set.insert(kf.time()); std::vector time_vector(time_set.begin(), time_set.end()); time_set.clear(); std::vector> mids; mids.reserve(time_vector.size()); for ( FrameTime t : time_vector ) { mids.push_back({}); mids.back().reserve(properties_.size()); for ( auto prop : properties_ ) mids.back().push_back(prop->mid_transition(t)); } raw_keyframes_.reserve(time_vector.size()); for ( std::size_t i = 0; i < time_vector.size(); i++ ) { raw_keyframes_.emplace_back(time_vector[i], properties_.size()); for ( std::size_t j = 0; j < properties_.size(); j++ ) { if ( !(flags & NoValues) ) raw_keyframes_.back().values.push_back(mids[i][j].value); raw_keyframes_.back().transitions.push_back(mids[i][j].to_next); if ( mids[i][j].type == MidTransition::Middle && i > 0 && mids[i-1][j].type != MidTransition::Middle ) { raw_keyframes_[i-1].transitions[j] = mids[i][j].from_previous; } } } } template auto invoke_combine_get(const Func& func, std::integer_sequence) { return func(static_cast*>(properties_[Indices])->get()...); } template auto invoke_combine_get_at(FrameTime t, const Func& func, std::integer_sequence) { return func(static_cast*>(properties_[Indices])->get_at(t)...); } }; class JoinedAnimatable; class JoinedKeyframe : public KeyframeBase { public: JoinedKeyframe(JoinedAnimatable* parent, const JoinAnimatables::Keyframe* subkf) : KeyframeBase(subkf->time), parent(parent), subkf(subkf) { transition_ = subkf->transition(); } JoinedKeyframe(JoinedAnimatable* parent, model::FrameTime time) : KeyframeBase(time), parent(parent), subkf(nullptr) {} JoinedKeyframe(JoinedKeyframe&&) = default; std::vector get() const; QVariant value() const override; // read only bool set_value(const QVariant&) override { return false; } KeyframeTransition transition() const override { return transition_; } void set_transition(const KeyframeTransition&) override {} protected: std::unique_ptr do_clone() const override { return std::make_unique(parent, subkf); } class Splitter : public KeyframeSplitter { public: Splitter(const JoinedKeyframe* a, const JoinedKeyframe* b) : a(a), b(b) {} void step(const QPointF&) override {} std::unique_ptr left(const QPointF& p) const override { return std::make_unique( a->parent, math::lerp(a->time(), b->time(), p.x()) ); } std::unique_ptr right(const QPointF& p) const override { return std::make_unique( a->parent, math::lerp(a->time(), b->time(), p.x()) ); } std::unique_ptr last() const override { return b->clone(); } const JoinedKeyframe* a; const JoinedKeyframe* b; }; std::unique_ptr splitter(const KeyframeBase* other) const override { return std::make_unique(this, static_cast(other)); } private: JoinedAnimatable* parent; const JoinAnimatables::Keyframe* subkf = nullptr; KeyframeTransition transition_; }; /** * \brief JoinAnimatables implementing AnimatedPropertyBase */ class JoinedAnimatable : public detail::AnimatableImpl, public JoinAnimatables { public: using ConversionFunction = std::function& args)>; JoinedAnimatable(std::vector properties, ConversionFunction converter, int flags = Normal) : JoinAnimatables(std::move(properties), flags), converter(std::move(converter)) { for ( auto& kf : keyframes() ) keyframes_.insert(kf.time, JoinedKeyframe(this, &kf)); } using JoinAnimatables::animated; int animatable_flags() const override { return HasValue; } QVariant value_at_time(FrameTime time) const override { return converter(value_at(time)); } QVariant static_value() const override { return converter(current_value()); } // read only bool set_static_value(const QVariant&) override { return false; } bool value_mismatch() const override { return false; } // bool valid_value(const QVariant&) const override { return false; } // bool set_undoable(const QVariant&, bool) override { return false; } /*AnimatedPropertyBase::MoveResult move_keyframe(FrameTime, FrameTime) override { return MoveResult::NotFound; } KeyframeBase* set_keyframe(FrameTime , const QVariant& , SetKeyframeInfo*, bool ) override { return nullptr; } void clear_keyframes() override {}; bool remove_keyframe_at_time(FrameTime) override { return false; } void set_transition(FrameTime, const KeyframeTransition&) override {} void set_transition_before(FrameTime, const KeyframeTransition&) override {}*/ QString visual_name() const override { return {}; } QUndoCommand* command_add_smooth_keyframe(FrameTime, const QVariant&, bool, QUndoCommand*) override { return nullptr; } QUndoCommand* command_remove_keyframe(FrameTime, QUndoCommand*) override { return nullptr; } QUndoCommand* command_clear_keyframes(QUndoCommand*) override { return nullptr; } QUndoCommand* command_set_transition( model::FrameTime, const model::KeyframeTransition&, QUndoCommand* ) override { return nullptr; } QUndoCommand* command_set_transition_side( model::FrameTime, model::KeyframeTransition::Descriptive, const QPointF&, bool, QUndoCommand* ) override { return nullptr; } QUndoCommand* command_move_keyframe(model::FrameTime, model::FrameTime, QUndoCommand*) override { return nullptr; } protected: // void on_set_time(FrameTime) override {} // Shouldn't be needed QVariant do_mid_transition_value(const KeyframeBase*, const KeyframeBase*, qreal) const override { return {}; } private: friend JoinedKeyframe; ConversionFunction converter; }; inline std::vector JoinedKeyframe::get() const { if ( subkf ) return subkf->values; else return parent->value_at(time()); } inline QVariant JoinedKeyframe::value() const { if ( subkf ) return parent->converter(subkf->values); else return parent->converter(parent->value_at(time())); } } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/clipboard.cpp000664 001750 001750 00000010530 15165022620 032505 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/command/clipboard.hpp" #include #include #include QMimeData * glaxnimate::command::copy_helper( const std::vector nodes, const std::vector& supported_mimes ) { QMimeData* data = new QMimeData; for ( const auto& serializer : supported_mimes ) { serializer->to_mime_data(*data, nodes); } return data; } using integer_type = int; static const QString keyframe_mime = QStringLiteral("application/x.glaxnimate-keyframes"); QMimeData *glaxnimate::command::keyframes_to_mime_data(const ClipboardProperties &selection) { if ( selection.properties.empty() ) return nullptr; QMimeData* data = new QMimeData; QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); stream << integer_type(selection.properties.size()); for ( const auto& prop : selection.properties ) { stream << integer_type(prop.property_type); stream << integer_type(prop.keyframes.size()); if ( !prop.keyframes.empty() ) { auto start_time = prop.keyframes.begin()->time; for ( const auto& kf : prop.keyframes ) { stream << kf.time - start_time << kf.value; } } } data->setData(keyframe_mime, encoded); return data; } QMimeData *glaxnimate::command::keyframe_to_mime_data(int property_type, const QVariant &value) { QMimeData* data = new QMimeData; QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); stream << integer_type(1); // 1 property stream << integer_type(property_type); stream << integer_type(1); // 1 keyframe stream << model::FrameTime(0); // time offset stream << value; data->setData(keyframe_mime, encoded); return data; } glaxnimate::command::ClipboardProperties glaxnimate::command::keyframes_from_mime_data(const QMimeData *data) { ClipboardProperties result; if ( data->hasFormat(keyframe_mime) ) { QByteArray encoded = data->data(keyframe_mime); QDataStream stream(&encoded, QIODevice::ReadOnly); integer_type prop_count = 0; stream >> prop_count; result.properties.resize(prop_count); for ( integer_type pi = 0; pi < prop_count; pi++ ) { integer_type kf_count = 0; stream >> result.properties[pi].property_type >> kf_count; for ( integer_type ki = 0; ki < kf_count; ki++ ) { ClipboardKeyframe kf; stream >> kf.time >> kf.value; result.properties[pi].keyframes.insert(kf.time, kf); } } } return result; } bool glaxnimate::command::keyframes_mime_data_has_type(const QMimeData *data, int property_type) { if ( !data->hasFormat(keyframe_mime) ) return false; QByteArray encoded = data->data(keyframe_mime); QDataStream stream(&encoded, QIODevice::ReadOnly); integer_type prop_count = 0; stream >> prop_count; for ( integer_type pi = 0; pi < prop_count; pi++ ) { integer_type type = model::PropertyTraits::Unknown; integer_type kf_count = 0; stream >> type >> kf_count; if ( type == property_type ) return kf_count > 0; for ( integer_type ki = 0; ki < kf_count; ki++ ) { ClipboardKeyframe kf; stream >> kf.time >> kf.value; } } return false; } QUndoCommand *glaxnimate::command::keyframes_paste_command( const QMimeData *data, model::AnimatableBase *target_property, int property_type, model::FrameTime start_time ) { auto clipboard_data = command::keyframes_from_mime_data(data); auto* prop = clipboard_data.by_type(property_type); if ( !prop || prop->keyframes.empty() ) return nullptr; auto cmd = new QUndoCommand(i18np("Paste keyframe", "Paste keyframes", prop->keyframes.size())); for ( const auto& kf : prop->keyframes ) { target_property->command_add_smooth_keyframe(start_time + kf.time, kf.value, true, cmd); } return cmd; } bool glaxnimate::command::has_keyframe_data(const QMimeData *data) { return data->hasFormat(keyframe_mime); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/aep_format.cpp000664 001750 001750 00000002765 15165022620 035752 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/aep/aep_format.hpp" #include "glaxnimate/module/extraformats/aep/aep_parser.hpp" #include "glaxnimate/module/extraformats/aep/aep_loader.hpp" #include "glaxnimate/module/extraformats/aep/aepx.hpp" bool glaxnimate::io::aep::AepFormat::riff_to_document(const RiffChunk& chunk, model::Document* document, const QString& filename) { AepParser parser(this); try { Project project = parser.parse(chunk); QFileInfo finfo(filename); AepLoader loader(document, project, finfo.dir(), this); loader.load_project(); return true; } catch ( const AepError& err ) { return false; } } bool glaxnimate::io::aep::AepFormat::on_open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap&) { AepRiff riff_parser; try { RiffChunk chunk = riff_parser.parse(&file); return riff_to_document(chunk, document, filename); } catch ( const RiffError& r ) { error(i18n("Could not load file: %1", r.message)); return false; } } bool glaxnimate::io::aep::AepxFormat::on_open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap&) { QDomDocument dom; dom.setContent(file.readAll()); AepxConverter aepx; return riff_to_document(aepx.aepx_to_chunk(dom.documentElement()), document, filename); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/gzip.hpp000664 001750 001750 00000002314 15165022620 032345 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include namespace glaxnimate::gzip { using ErrorFunc = std::function; bool compress(const QByteArray& data, QIODevice& output, const gzip::ErrorFunc& on_error, int level, quint32* compressed_size); bool decompress(QIODevice& input, QByteArray& output, const ErrorFunc& on_error); bool decompress(const QByteArray& input, QByteArray& output, const ErrorFunc& on_error); bool is_compressed(QIODevice& input); bool is_compressed(const QByteArray& input); class GzipStream : public QIODevice { public: GzipStream(QIODevice* target, const ErrorFunc& on_error); ~GzipStream(); bool atEnd() const override; bool isSequential() const override { return true; } bool open(QIODevice::OpenMode mode) override; qint64 ouput_size() const; protected: qint64 readData(char * data, qint64 maxlen) override; qint64 writeData(const char * data, qint64 len) override; private: class Private; std::unique_ptr d; }; } // namespace glaxnimate::gzip mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/css_parser.hpp000664 001750 001750 00000026166 15165022620 032523 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/svg/detail.hpp" #include #include namespace glaxnimate::io::svg::detail { class CssSelector { public: void set_tag(const QString& tag) { if ( tag != '*' && this->tag.isEmpty() ) specificity += 1; this->tag = tag; } void set_id(const QString& id) { if ( this->id.isEmpty() ) specificity += 100; this->id = id; } void add_class(const QString& class_name) { classes.push_back(class_name); specificity += 10; } bool match(const QDomElement& element, const std::unordered_set& class_names) const { if ( !tag.isEmpty() && tag != "*" && tag != element.tagName() ) return false; if ( !id.isEmpty() && id != element.attribute("id") ) return false; for ( const auto& class_name : classes ) { if ( class_names.count(class_name) == 0 ) return false; } if ( !rule.isEmpty() ) return false; return true; } bool empty() const { return tag.isEmpty() && id.isEmpty() && classes.empty() && rule.isEmpty(); } void set_at_rule(const QString& rule) { this->rule = rule; } const QString& at_rule() const { return this->rule; } private: int specificity = 0; QString tag; QString id; QStringList classes; QString rule; friend struct CssStyleBlock; }; struct CssStyleBlock { CssSelector selector; Style::Map style; void merge_into(Style& output) const { for ( const auto& p : style ) output[p.first] = p.second; } bool operator<(const CssStyleBlock& other) const { return selector.specificity < other.selector.specificity; } }; class CssParser { public: CssParser(std::vector& blocks) : blocks(blocks) { } void parse(const QString& css) { data = css; index = -1; parse_selector(); } private: enum class TokenType { SelectorTag, SelectorClass, SelectorId, SelectorOther, SelectorComma, SelectorAt, BlockBegin, BlockEnd, RuleName, RuleColon, RuleArg, RuleSemicolon, Eof, }; using Token = std::pair; QChar next_ch_raw() { ++index; if ( eof() ) return {}; return data[index]; } bool eof() const { return index >= data.size(); } void back() { if ( !eof() ) --index; } QChar next_ch() { QChar c = next_ch_raw(); // Skip comments if ( c == '/' ) { QChar d = next_ch_raw(); if ( d == '*' ) { while ( true ) { d = next_ch_raw(); if ( eof() ) return {}; if ( d == '*' ) { d = next_ch_raw(); // Treat comments as spaces if ( d == '/' ) return ' '; back(); } } } else { back(); } } return c; } static bool is_identifier_start(const QChar& ch) { return ch.isLetter() || ch == '_' || ch == '-'; } static bool is_identifier(const QChar& ch) { return is_identifier_start(ch) || ch.isNumber(); } QString lex_identifier() { QString id; QChar ch; while ( true ) { ch = next_ch(); if ( is_identifier(ch) ) id += ch; else break; } back(); return id; } QString lex_at_selector() { QString id = "@"; QChar ch; while ( true ) { ch = next_ch(); if ( ch == '{' || ch == ',' ) break; else id += ch; } back(); return id.trimmed(); } Token lex_selector() { QChar ch = next_ch(); if ( eof() ) return {TokenType::Eof, {}}; if ( is_identifier_start(ch) ) return {TokenType::SelectorTag, ch + lex_identifier()}; else if ( ch == '#' ) return {TokenType::SelectorId, lex_identifier()}; else if ( ch == '.' ) return {TokenType::SelectorClass, lex_identifier()}; else if ( ch == ',' ) return {TokenType::SelectorComma, {}}; else if ( ch == '{' ) return {TokenType::BlockBegin, {}}; else if ( ch == '*' ) return {TokenType::SelectorTag, ch}; else if ( ch == '@' ) return {TokenType::SelectorAt, lex_at_selector()}; if ( ch.isSpace() ) { skip_space(); ch = next_ch(); if ( ch == ',' ) return {TokenType::SelectorComma, {}}; if ( ch == '{' ) return {TokenType::BlockBegin, {}}; back(); } return {TokenType::SelectorOther, {}}; } Token ignore_selector() { Token token = {TokenType::Eof, {}}; do { token = lex_selector(); if ( token.first == TokenType::SelectorComma ) return lex_selector(); } while ( token.first != TokenType::Eof && token.first != TokenType::BlockBegin ); return token; } void skip_space() { QChar c; do { c = next_ch(); } while ( !eof() && c.isSpace() ); back(); } void ignore_block() { Token token = {TokenType::Eof, {}}; do { token = lex_selector(); } while ( token.first != TokenType::Eof && token.first != TokenType::BlockEnd ); } bool parse_selector_step(const Token& token) { if ( token.first == TokenType::SelectorClass ) selectors.back().add_class(token.second); else if ( token.first == TokenType::SelectorId ) selectors.back().set_id(token.second); else if ( token.first == TokenType::SelectorTag ) selectors.back().set_tag(token.second); else if ( token.first == TokenType::SelectorAt ) selectors.back().set_at_rule(token.second); else return false; return true; } void parse_selector() { while ( true ) { skip_space(); selectors.clear(); Token token = lex_selector(); if ( token.first == TokenType::BlockBegin ) { ignore_block(); token = lex_selector(); } while ( true ) { selectors.push_back({}); while ( parse_selector_step(token) ) token = lex_selector(); if ( eof() ) return; if ( token.first == TokenType::BlockBegin ) { if ( selectors.back().empty() ) selectors.pop_back(); break; } if ( token.first != TokenType::SelectorComma ) { token = ignore_selector(); selectors.pop_back(); } else { skip_space(); token = lex_selector(); } } if ( selectors.empty() ) ignore_block(); else parse_block(); } } Token lex_rule() { skip_space(); QChar ch = next_ch(); if ( eof() ) return {TokenType::Eof, {}}; if ( is_identifier_start(ch) ) return {TokenType::RuleName, ch + lex_identifier()}; else if ( ch == ':' ) return {TokenType::RuleColon, {}}; else if ( ch == ';' ) return {TokenType::RuleSemicolon, {}}; else if ( ch == '}' ) return {TokenType::BlockEnd, {}}; return {TokenType::RuleArg, ch}; } Token ignore_rule() { Token token = lex_rule(); while ( token.first != TokenType::Eof && token.first != TokenType::RuleSemicolon && token.first != TokenType::BlockEnd ) token = lex_rule(); return token; } void lex_quoted_string(QString& value, QChar terminator) { while ( true ) { QChar ch = next_ch(); if ( eof() ) break; value += ch; if ( ch == terminator ) break; if ( ch == '\\' ) { ch = next_ch(); if ( eof() ) break; value += ch; } } } Token lex_rule_value(QString& value) { if ( value == "\"" || value == "'" ) lex_quoted_string(value, value[0]); while ( true ) { QChar ch = next_ch(); if ( eof() ) return {TokenType::Eof, {}}; else if ( ch == ';' ) return {TokenType::RuleSemicolon, {}}; else if ( ch == '}' ) return {TokenType::BlockEnd, {}}; value += ch; if ( ch == '"' || ch == '\'' ) { lex_quoted_string(value, ch); } } } void parse_block() { rules.clear(); while ( true ) { Token token = lex_rule(); if ( eof() || token.first == TokenType::BlockEnd ) break; if ( token.first != TokenType::RuleName ) { ignore_rule(); continue; } QString name = token.second; if ( lex_rule().first != TokenType::RuleColon ) { ignore_rule(); continue; } token = lex_rule(); if ( eof() || token.first == TokenType::BlockEnd ) break; if ( token.first == TokenType::RuleSemicolon ) continue; QString value = token.second; token = lex_rule_value(value); if ( !value.isEmpty() ) rules[name] = value.trimmed(); if ( eof() || token.first == TokenType::BlockEnd ) break; } for ( const auto& selector : selectors ) blocks.push_back({selector, rules}); rules.clear(); selectors.clear(); } QString data; int index = 0; std::vector& blocks; std::vector selectors; Style::Map rules; }; } // namespace glaxnimate::io::svg::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/CMakeLists.txt000664 001750 001750 00000001071 15165022620 033422 0ustar00ddennedyddennedy000000 000000 # SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia # SPDX-License-Identifier: BSD-2-Clause set(SOURCES gzip-zlib.cpp gzip.cpp gzip_module.cpp svgz_format.cpp tgs_format.cpp ) add_library(GlaxnimateGzip OBJECT ${SOURCES}) if ( EMSCRIPTEN ) target_compile_options(GlaxnimateGzip PUBLIC "-sUSE_ZLIB=1") target_link_options(GlaxnimateGzip PUBLIC "-sUSE_ZLIB=1") else() find_package(ZLIB REQUIRED) target_link_libraries(GlaxnimateGzip PUBLIC ZLIB::ZLIB) endif() glaxnimate_enable_exceptions(GlaxnimateGzip PUBLIC) src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/offset_path.cpp000664 001750 001750 00000025045 15165022620 035726 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/modifiers/offset_path.hpp" #include "glaxnimate/math/geom.hpp" #include "glaxnimate/math/vector.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::OffsetPath) using namespace glaxnimate; using namespace glaxnimate::math::bezier; static bool point_fuzzy_compare(const QPointF& a, const QPointF& b) { return qFuzzyCompare(a.x(), b.x()) && qFuzzyCompare(a.y(), b.y()); } /* Simple offset of a linear segment */ static std::pair linear_offset(const QPointF& p1, const QPointF& p2, float amount) { auto angle = math::atan2(p2.x() - p1.x(), p2.y() - p1.y()); auto offset = math::from_polar(amount, -angle); return { p1 + offset, p2 + offset }; } template static std::array offset_polygon(std::array points, float amount) { std::array, size - 1> off_lines; for ( int i = 1; i < size; i++ ) { off_lines[i-1] = linear_offset(points[i-1], points[i], amount); } std::array off_points; off_points[0] = off_lines[0].first; off_points[size - 1] = off_lines.back().second; for ( int i = 1; i < size - 1; i++ ) { off_points[i] = math::line_intersection(off_lines[i-1].first, off_lines[i-1].second, off_lines[i].first, off_lines[i].second).value_or(off_lines[i].first); } return off_points; } /* Offset a bezier segment only works well if the segment is flat enough */ static math::bezier::CubicBezierSolver offset_segment(const math::bezier::CubicBezierSolver& segment, float amount) { bool same01 = point_fuzzy_compare(segment.points()[0], segment.points()[1]); bool same23 = point_fuzzy_compare(segment.points()[2], segment.points()[3]); if ( same01 && same23 ) { auto off = linear_offset(segment.points()[0], segment.points().back(), amount); return {off.first, math::lerp(off.first, off.second, 1./3.), math::lerp(off.first, off.second, 2./3.), off.second}; } else if ( same01 ) { auto poly = offset_polygon<3>({segment.points()[0], segment.points()[2], segment.points()[3]}, amount); return {poly[0], poly[0], poly[1], poly[2]}; } else if ( same23 ) { auto poly = offset_polygon<3>({segment.points()[0], segment.points()[1], segment.points()[3]}, amount); return {poly[0], poly[1], poly[2], poly[2]}; } return offset_polygon<4>(segment.points(), amount); } /* Join two segments */ static QPointF join_lines( Bezier& output_bezier, const CubicBezierSolver& seg1, const CubicBezierSolver& seg2, glaxnimate::model::Stroke::Join line_join, float miter_limit ) { QPointF p0 = seg1.points()[3]; QPointF p1 = seg2.points()[0]; if ( line_join == glaxnimate::model::Stroke::BevelJoin ) return p0; // Connected, they don't need a joint if ( point_fuzzy_compare(p0, p1) ) return p0; auto& last_point = output_bezier.points().back(); if ( line_join == glaxnimate::model::Stroke::RoundJoin ) { auto angle_out = seg1.tangent_angle(1); auto angle_in = seg2.tangent_angle(0) + math::pi; auto offset = math::from_polar(100, angle_out + math::pi / 2); auto center = math::line_intersection(p0, p0 + offset, p1, p1 + offset); auto radius = center ? math::distance(*center, p0) : math::distance(p0, p1) / 2; last_point.tan_out = last_point.pos + math::from_polar(2 * radius * math::ellipse_bezier, angle_out); output_bezier.add_point(p1, math::from_polar(2 * radius * math::ellipse_bezier, angle_in)); return p1; } // Miter auto t0 = point_fuzzy_compare(p0, seg1.points()[2]) ? seg1.points()[0] : seg1.points()[2]; auto t1 = point_fuzzy_compare(p1, seg2.points()[1]) ? seg2.points()[3] : seg2.points()[1]; auto intersection = math::line_intersection(t0, p0, p1, t1); if ( intersection && math::distance(*intersection, p0) < miter_limit ) { output_bezier.add_point(*intersection); return *intersection; } return p0; } static std::optional> get_intersection( const CubicBezierSolver&a, const CubicBezierSolver& b) { auto intersect = a.intersections(b, 2, 3, 7); std::size_t i = 0; if ( !intersect.empty() && qFuzzyCompare(intersect[0].first, 1) ) i++; if ( intersect.size() > i ) return intersect[i]; return {}; } static std::pair>, std::vector>> prune_segment_intersection( const std::vector>& a, const std::vector>& b ) { auto out_a = a; auto out_b = b; auto intersect = get_intersection(a.back(), b[0]); if ( intersect ) { out_a.back() = a.back().split(intersect->first).first; out_b[0] = b[0].split(intersect->second).second; } if ( a.size() > 1 && b.size() > 1 ) { intersect = get_intersection(a[0], b.back()); if ( intersect ) { return { {a[0].split(intersect->first).first}, {b.back().split(intersect->second).second}, }; } } return {out_a, out_b}; } void prune_intersections(std::vector>>& segments) { for ( std::size_t i = 1; i < segments.size() ; i++ ) { std::tie(segments[i-1], segments[i]) = prune_segment_intersection(segments[i - 1], segments[i]); } if ( segments.size() > 1 ) std::tie(segments.back(), segments[0]) = prune_segment_intersection(segments.back(), segments[0]); } static std::vector> split_inflections( const math::bezier::CubicBezierSolver& segment ) { /* We split each bezier segment into smaller pieces based on inflection points, this ensures the control point polygon is convex. (A cubic bezier can have none, one, or two inflection points) */ auto flex = segment.inflection_points(); if ( flex.size() == 0 ) { return {segment}; } else if ( flex.size() == 1 || flex[1] == 1 ) { auto split = segment.split(flex[0]); return { split.first, split.second }; } else { auto split_1 = segment.split(flex[0]); float t = (flex[1] - flex[0]) / (1 - flex[0]); auto split_2 = CubicBezierSolver(split_1.second).split(t); return { split_1.first, split_2.first, split_2.second, }; } } static bool needs_more_split(const math::bezier::CubicBezierSolver& segment) { auto n1 = math::from_polar(1, segment.normal_angle(0)); auto n2 = math::from_polar(1, segment.normal_angle(1)); auto s = QPointF::dotProduct(n1, n2); return math::abs(math::acos(s)) >= math::pi / 3; } static std::vector> offset_segment_split( const math::bezier::CubicBezierSolver& segment, float amount ) { std::vector> offset; offset.reserve(6); for ( const auto& chunk: split_inflections(segment) ) { if ( needs_more_split(chunk) ) { auto split = chunk.split(0.5); offset.push_back(offset_segment(split.first, amount)); offset.push_back(offset_segment(split.second, amount)); } else { offset.push_back(offset_segment(chunk, amount)); } } return offset; } static MultiBezier offset_path( // Beziers as collected from the other shapes const MultiBezier& collected_shapes, float amount, model::Stroke::Join line_join, float miter_limit ) { MultiBezier result; for ( const auto& input_bezier : collected_shapes.beziers() ) { int count = input_bezier.segment_count(); Bezier output_bezier; output_bezier.set_closed(input_bezier.closed()); std::vector>> multi_segments; for ( int i = 0; i < count; i++ ) multi_segments.push_back(offset_segment_split(input_bezier.segment(i), amount)); // Open paths are stroked rather than being simply offset if ( !input_bezier.closed() ) { for ( int i = count - 1; i >= 0; i-- ) multi_segments.push_back(offset_segment_split(input_bezier.inverted_segment(i), amount)); } prune_intersections(multi_segments); // Add bezier segments to the output and apply line joints QPointF last_point; const math::bezier::CubicBezierSolver* last_seg = nullptr; for ( const auto& multi_segment : multi_segments ) { if ( last_seg ) last_point = join_lines(output_bezier, *last_seg, multi_segment[0], line_join, miter_limit * amount); last_seg = &multi_segment.back(); for ( const auto& segment : multi_segment ) { if ( !point_fuzzy_compare(segment.points()[0], last_point) || output_bezier.empty() ) output_bezier.add_point(segment.points()[0]); output_bezier.back().tan_out = segment.points()[1]; output_bezier.add_point(segment.points()[3]); output_bezier.back().tan_in = segment.points()[2]; last_point = segment.points()[3]; } } if ( !multi_segments.empty() ) join_lines(output_bezier, *last_seg, multi_segments[0][0], line_join, miter_limit * amount); result.beziers().push_back(output_bezier); } return result; } QIcon glaxnimate::model::OffsetPath::static_tree_icon() { return QIcon::fromTheme("path-offset-dynamic"); } QString glaxnimate::model::OffsetPath::static_type_name_human() { return i18n("Offset Path"); } bool glaxnimate::model::OffsetPath::process_collected() const { return false; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::OffsetPath::process( glaxnimate::model::FrameTime t, const math::bezier::MultiBezier& mbez ) const { if ( mbez.empty() ) return {}; auto amount = this->amount.get_at(t); if ( qFuzzyIsNull(amount) ) return mbez; return offset_path(mbez, amount, join.get(), miter_limit.get_at(t)); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_serializer.cpp000664 001750 001750 00000006133 15165022620 037224 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/rive/rive_serializer.hpp" #include glaxnimate::io::rive::RiveSerializer::RiveSerializer(QIODevice* file) : stream(file) { } void glaxnimate::io::rive::RiveSerializer::write_header(int vmaj, int vmin, glaxnimate::io::rive::Identifier file_id) { stream.write("RIVE"); stream.write_uint_leb128(vmaj); stream.write_uint_leb128(vmin); stream.write_uint_leb128(file_id); } void glaxnimate::io::rive::RiveSerializer::write_property_table(const glaxnimate::io::rive::PropertyTable& properties) { for ( const auto& p: properties ) stream.write_uint_leb128(p.first); stream.write_byte(0); quint32 current_int = 0; quint32 bit = 0; for ( const auto& p: properties ) { int type = 0; switch ( p.second ) { case PropertyType::VarUint: case PropertyType::Bool: type = 0; break; case PropertyType::Bytes: case PropertyType::String: type = 1; break; case PropertyType::Float: type = 2; break; case PropertyType::Color: type = 3; break; } current_int <<= 2; current_int |= type; bit += 2; if ( bit == 8 ) { stream.write_uint32_le(current_int); bit = 0; current_int = 0; } } if ( bit != 0 ) stream.write_uint32_le(current_int); } void glaxnimate::io::rive::RiveSerializer::write_object(const glaxnimate::io::rive::Object& output) { stream.write_uint_leb128(VarUint(output.type().id)); for ( const auto& p : output.properties() ) { if ( !p.second.isValid() || (p.second.userType() == QMetaType::QString && p.second.toString().isEmpty()) ) continue; stream.write_uint_leb128(p.first->id); write_property_value(p.first->type, p.second); } stream.write_byte(0); } void glaxnimate::io::rive::RiveSerializer::write_property_value(glaxnimate::io::rive::PropertyType id, const QVariant& value) { switch ( id ) { case PropertyType::Bool: stream.write_byte(value.toBool()); return; case PropertyType::VarUint: stream.write_uint_leb128(value.value()); return; case PropertyType::Color: stream.write_uint32_le(value.value().rgba()); return; case PropertyType::Bytes: { auto data = value.toByteArray(); stream.write_uint_leb128(data.size()); stream.write(data); return; } case PropertyType::String: { auto data = value.toString().toUtf8(); stream.write_uint_leb128(data.size()); stream.write(data); return; } case PropertyType::Float: stream.write_float32_le(value.toFloat()); } } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/regexp.hpp000664 001750 001750 00000002746 15165022620 031601 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::utils::regexp { namespace detail { class EndIterator {}; class NotDumbIterator { public: NotDumbIterator(QRegularExpressionMatchIterator dumb) : dumb(std::move(dumb)) { ++*this; } NotDumbIterator& operator++() { end = !this->dumb.hasNext(); if ( !end ) match = this->dumb.next(); return *this; } QRegularExpressionMatch& operator*() { return match; } bool operator!=(const NotDumbIterator& oth) const { return end != oth.end; }; bool operator!=(const EndIterator&) const { return !end; } private: QRegularExpressionMatchIterator dumb; QRegularExpressionMatch match; bool end = false; }; } // namespace detail struct MatchRange { const detail::NotDumbIterator& begin() const { return begin_iter; } const detail::EndIterator& end() const { return end_iter; } detail::NotDumbIterator begin_iter; detail::EndIterator end_iter = {}; }; inline MatchRange find_all(const QRegularExpression& pattern, const QString& subject) { return MatchRange{ detail::NotDumbIterator(pattern.globalMatch(subject)) }; } } // namespace glaxnimate::utils::regexp mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/CMakeLists.txt000664 001750 001750 00000000661 15165022620 033552 0ustar00ddennedyddennedy000000 000000 # SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia # SPDX-License-Identifier: BSD-2-Clause set(SOURCES postscript_format.cpp pdf_format.cpp cairo_module.cpp ) add_library(GlaxnimateCairo OBJECT ${SOURCES}) find_package(PkgConfig REQUIRED) pkg_check_modules(CAIRO REQUIRED cairo) target_link_libraries(GlaxnimateCairo PUBLIC ${CAIRO_LIBRARIES}) glaxnimate_enable_exceptions(GlaxnimateCairo PUBLIC) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/postscript_format.hpp000664 001750 001750 00000001623 15165022620 035304 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" namespace glaxnimate::cairo { class PostScriptFormat : public io::ImportExport { Q_OBJECT public: QString slug() const override { return "postscript"; } QString name() const override { return i18n("PostScript"); } QStringList extensions(Direction direction) const override; bool can_save() const override { return false; } bool can_open() const override { return false; } bool can_save_static() const override { return true; } std::unique_ptr save_settings(model::Composition*) const override; protected: bool on_save_static(QIODevice& dev, const QString&, model::Composition* comp, model::FrameTime time, const QVariantMap&) override; }; } // namespace glaxnimate::cairo mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/svgz_format.cpp000664 001750 001750 00000003105 15165022620 033727 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/gzip/svgz_format.hpp" #include "glaxnimate/module/gzip/gzip.hpp" bool glaxnimate::io::svg::SvgzFormat::on_open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& options) { if ( gzip::is_compressed(file) ) { auto on_error = [this](const QString& s){warning(s);}; gzip::GzipStream decompressed(&file, on_error); decompressed.open(QIODevice::ReadOnly); return SvgFormat::on_open(decompressed, filename, document, options); } return SvgFormat::on_open(file, filename, document, options); } bool glaxnimate::io::svg::SvgzFormat::on_save(QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& options) { auto on_error = [this](const QString& s){warning(s);}; gzip::GzipStream compressed(&file, on_error); compressed.open(QIODevice::WriteOnly); return SvgFormat::on_save(compressed, filename, comp, options); } bool glaxnimate::io::svg::SvgzFormat::on_save_static(QIODevice &file, const QString &filename, model::Composition *comp, model::FrameTime time, const QVariantMap &options) { auto on_error = [this](const QString& s){warning(s);}; gzip::GzipStream compressed(&file, on_error); compressed.open(QIODevice::WriteOnly); return SvgFormat::on_save_static(compressed, filename, comp, time, options); } QStringList glaxnimate::io::svg::SvgzFormat::extensions(Direction) const { return {"svgz"}; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/app_info.cpp000664 001750 001750 00000001705 15165022620 030727 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/app_info.hpp" #include "glaxnimate/utils/i18n.hpp" #include "glaxnimate/application_info_generated.hpp" QString glaxnimate::AppInfo::name() const { return i18n("Glaxnimate"); } QString glaxnimate::AppInfo::slug() const { return QStringLiteral(PROJECT_SLUG); } QString glaxnimate::AppInfo::version() const { return QStringLiteral(PROJECT_VERSION); } QString glaxnimate::AppInfo::organization() const { return QStringLiteral(PROJECT_SLUG); } QUrl glaxnimate::AppInfo::url_docs() const { return QUrl(URL_DOCS); } QString glaxnimate::AppInfo::description() const { return QStringLiteral(PROJECT_DESCRIPTION); } QString glaxnimate::AppInfo::project_id() const { return QStringLiteral(PROJECT_ID); } QUrl glaxnimate::AppInfo::url_home() const { return QUrl(PROJECT_HOMEPAGE_URL); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/polystar.cpp000664 001750 001750 00000004636 15165022620 034665 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/math/math.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::PolyStar) glaxnimate::math::bezier::Bezier glaxnimate::model::PolyStar::to_bezier(model::FrameTime t) const { return draw( type.get(), position.get_at(t), inner_radius.get_at(t), outer_radius.get_at(t), math::deg2rad(angle.get_at(t)), points.get_at(t), inner_roundness.get_at(t), outer_roundness.get_at(t), reversed.get() ); } glaxnimate::math::bezier::Bezier glaxnimate::model::PolyStar::draw( model::PolyStar::StarType type, const QPointF& pos, float radius_inner, float radius_outer, float angle_radians, int p, float round_inner, float round_outer, bool reverse ) { math::bezier::Bezier bezier; bezier.close(); qreal direction = reverse ? -1 : 1; qreal halfd = math::pi / p * direction; qreal tangent_len_outer = round_outer * (math::tau * radius_outer) / (p * 4) * direction; qreal tangent_len_inner = round_inner * (math::tau * radius_inner) / (p * 4) * direction; for ( int i = 0; i < p; i++ ) { qreal main_angle = -math::pi / 2 + angle_radians + i * halfd * 2; qreal dx = radius_outer * math::cos(main_angle); qreal dy = radius_outer * math::sin(main_angle); QPointF tangents_outer; if ( radius_outer != 0 ) tangents_outer = { dy / radius_outer, -dx / radius_outer, }; bezier.add_point( QPointF(pos.x() + dx, pos.y() + dy), tangent_len_outer * tangents_outer, -tangent_len_outer * tangents_outer ); if ( type == Star ) { dx = radius_inner * math::cos(main_angle+halfd); dy = radius_inner * math::sin(main_angle+halfd); QPointF tangents_inner; if ( radius_inner != 0 ) tangents_inner = { dy / radius_inner, -dx / radius_inner, }; bezier.add_point( QPointF(pos.x() + dx, pos.y() + dy), tangent_len_inner * tangents_inner, -tangent_len_inner * tangents_inner ); } } return bezier; } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/avd/avd_parser.cpp000664 001750 001750 00000065467 15165022620 036000 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/avd/avd_parser.hpp" #include #include "glaxnimate/io/svg/svg_parser_private.hpp" #include "glaxnimate/model/shapes/modifiers/trim.hpp" using namespace glaxnimate::io::svg; using namespace glaxnimate::io::svg::detail; class glaxnimate::io::avd::AvdParser::Private : public svg::detail::SvgParserPrivate { public: Private( const QDir& resource_path, model::Document* document, const std::function& on_warning, ImportExport* io, QSize forced_size, model::FrameTime default_time ) : SvgParserPrivate(document, on_warning, io, forced_size, default_time), resource_path(resource_path) {} protected: void on_parse_prepare(const QDomElement&) override { for ( const auto& p : shape_parsers ) to_process += dom.elementsByTagName(p.first).count(); for ( const auto& target : ElementRange(dom.elementsByTagName("target")) ) { QString name = target.attribute("name"); if ( name.isEmpty() ) continue; for ( const auto& attr : ElementRange(target) ) { if ( attr.tagName() != "attr" || !attr.attribute("name").endsWith("animation") ) continue; auto iter = animations.find(name); if ( iter == animations.end() ) iter = animations.insert({name, {}}).first; auto& props = iter->second; for ( const auto& anim : ElementRange(attr.elementsByTagName("objectAnimator")) ) { parse_animator(props, anim); } } } } QSizeF get_size(const QDomElement& svg) override { return { len_attr(svg, "width", size.width()), len_attr(svg, "height", size.height()) }; } void on_parse(const QDomElement& root) override { static const Style default_style(Style::Map{ {"fillColor", "black"}, }); if ( root.tagName() == "vector" ) { parse_vector({root, &main->shapes, default_style, false}); } else { if ( root.hasAttribute("drawable") ) { if ( auto res = get_resource(root.attribute("drawable")) ) { if ( res->element.tagName() == "vector" ) parse_vector({res->element, &main->shapes, default_style, false}); } } for ( const auto& ch : ElementRange(root) ) { if ( ch.tagName() == "attr" && ch.attribute("name").endsWith("drawable") ) { for ( const auto& e : ElementRange(ch) ) if ( e.tagName() == "vector" ) parse_vector({e, &main->shapes, default_style, false}); } } } main->name.set( attr(root, "android", "name", "") ); } void parse_shape(const ParseFuncArgs& args) override { auto it = shape_parsers.find(args.element.tagName()); if ( it != shape_parsers.end() ) { mark_progress(); (this->*it->second)(args); } } private: struct Resource { QString name; QDomElement element; model::Asset* asset = nullptr; }; void parse_vector(const ParseFuncArgs& args) { QPointF pos; QVector2D scale{1, 1}; model::Layer* layer = add_layer(args.shape_parent); set_name(layer, args.element); if ( args.element.hasAttribute("viewportWidth") && args.element.hasAttribute("viewportHeight") ) { qreal vbw = len_attr(args.element, "viewportWidth"); qreal vbh = len_attr(args.element, "viewportHeight"); if ( !forced_size.isValid() ) { if ( !args.element.hasAttribute("width") ) size.setWidth(vbw); if ( !args.element.hasAttribute("height") ) size.setHeight(vbh); } if ( vbw != 0 && vbh != 0 ) { scale = QVector2D(size.width() / vbw, size.height() / vbh); if ( forced_size.isValid() ) { auto single = qMin(scale.x(), scale.y()); scale = QVector2D(single, single); } } } layer->transform.get()->position.set(-pos); layer->transform.get()->scale.set(scale); parse_children({args.element, &layer->shapes, args.parent_style, false}); } void parse_animator(AnimateParser::AnimatedProperties& props, const QDomElement& anim) { model::FrameTime start_time = qRound(anim.attribute("startOffset", "0").toDouble() / 1000 * animate_parser.fps); model::FrameTime end_time = qRound(start_time + anim.attribute("duration", "0").toDouble() / 1000 * animate_parser.fps); animate_parser.register_time_range(start_time, end_time); std::vector updated_props; QString name = anim.attribute("propertyName"); if ( !name.isEmpty() ) { auto& prop = props.properties[name]; updated_props.push_back(&prop); parse_animated_prop(prop, name, anim, start_time, end_time); } for ( const auto& value_holder : ElementRange(anim) ) { if ( value_holder.tagName() != "propertyValuesHolder" ) continue; name = value_holder.attribute("propertyName"); if ( !name.isEmpty() ) { auto& prop = props.properties[name]; updated_props.push_back(&prop); parse_animated_prop(prop, name, value_holder, start_time, end_time); } } for ( auto prop : updated_props ) prop->sort(); } model::KeyframeTransition interpolator(const QString& interpolator) { using Type = model::KeyframeTransition::Descriptive; if ( interpolator == "@android:interpolator/fast_out_slow_in" ) return model::KeyframeTransition(Type::Fast, Type::Ease); if ( interpolator == "@android:interpolator/fast_out_linear_in" ) return model::KeyframeTransition(Type::Fast, Type::Linear); if ( interpolator == "@android:interpolator/linear_out_slow_in" ) return model::KeyframeTransition(Type::Linear, Type::Ease); if ( interpolator == "@android:anim/accelerate_decelerate_interpolator" ) return model::KeyframeTransition(Type::Ease, Type::Ease); if ( interpolator == "@android:anim/accelerate_interpolator" ) return model::KeyframeTransition(Type::Ease, Type::Fast); if ( interpolator == "@android:anim/decelerate_interpolator" ) return model::KeyframeTransition(Type::Fast, Type::Ease); if ( interpolator == "@android:anim/linear_interpolator" ) return model::KeyframeTransition(Type::Linear, Type::Linear); // TODO? // @android:anim/anticipate_interpolator // @android:anim/overshoot_interpolator // @android:anim/bounce_interpolator // @android:anim/anticipate_overshoot_interpolator if ( interpolator != "" ) warning(i18n("Unknown interpolator %s", interpolator)); return model::KeyframeTransition(Type::Ease, Type::Ease); } ValueVariant parse_animated_value(const QString& value, ValueVariant::Type type) { switch ( type ) { case ValueVariant::Vector: return value.toDouble(); case ValueVariant::Bezier: return PathDParser(value).parse(); case ValueVariant::String: return value; case ValueVariant::Color: return parse_color(value); } return {}; } void parse_animated_prop( AnimatedProperty& prop, const QString& name, const QDomElement& value_holder, model::FrameTime start_time, model::FrameTime end_time ) { static model::KeyframeTransition transition; ValueVariant::Type type = ValueVariant::Vector; if ( name == "pathData" ) type = ValueVariant::Bezier; else if ( name.endsWith("Color") ) type = ValueVariant::Color; if ( value_holder.hasAttribute("valueFrom") ) { prop.keyframes.push_back({ start_time, parse_animated_value(value_holder.attribute("valueFrom"), type), interpolator(value_holder.attribute("interpolator")) }); } if ( value_holder.hasAttribute("valueTo") ) { prop.keyframes.push_back({ end_time, parse_animated_value(value_holder.attribute("valueTo"), type), model::KeyframeTransition(model::KeyframeTransition::Ease) }); } for ( const auto& kf : ElementRange(value_holder) ) { if ( kf.tagName() != "keyframe" ) continue; auto fraction = kf.attribute("fraction").toDouble(); prop.keyframes.push_back({ math::lerp(start_time, end_time, fraction), parse_animated_value(kf.attribute("value"), type), interpolator(kf.attribute("interpolator")) }); } } void add_shapes(const ParseFuncArgs& args, ShapeCollection&& shapes) { Style style = parse_style(args.element, args.parent_style); auto group = std::make_unique(document); // apply_common_style(group.get(), args.element, style); set_name(group.get(), args.element); add_style_shapes(args, &group->shapes, style); for ( auto& shape : shapes ) group->shapes.insert(std::move(shape)); args.shape_parent->insert(std::move(group)); } Style parse_style(const QDomElement& element, const Style& parent_style) { Style style = parent_style; for ( const auto& domnode : ItemCountRange(element.attributes()) ) { auto attr = domnode.toAttr(); if ( style_atrrs.count(attr.name()) ) style[attr.name()] = attr.value(); } for ( const auto& child : ItemCountRange(element.childNodes()) ) { if ( child.isElement() ) { auto attr = child.toElement(); if ( attr.tagName() == "attr" ) { auto attr_name = attr.attribute("name").split(":").back(); for ( const auto& grandchild : ItemCountRange(child.childNodes()) ) { if ( grandchild.isElement() ) { style[attr_name] = add_as_resource(grandchild.toElement()); break; } } } } } return style; } void set_name(model::DocumentNode* node, const QDomElement& element) { QString name = attr(element, "", "name", node->type_name_human()); node->name.set(name); } void add_style_shapes(const ParseFuncArgs& args, model::ShapeListProperty* shapes, const Style& style) { add_fill(args, shapes, style); add_stroke(args, shapes, style); if ( style.contains("trimPathEnd") || style.contains("trimPathStart") ) add_trim(args, shapes, style); } QColor parse_color(const QString& color) { if ( !color.isEmpty() && color[0] == '#' ) { if ( color.size() == 5 ) return svg::parse_color("#" + color.mid(2) + color[1]); if ( color.size() == 9 ) return svg::parse_color("#" + color.mid(3) + color.mid(1, 2)); } return svg::parse_color(color); } void set_styler_style(model::Styler* styler, const QString& color) { if ( color.isEmpty() ) { styler->visible.set(false); } else if ( color[0] == '@' ) { auto res = get_resource(color); if ( res && res->element.tagName() == "gradient" ) styler->use.set(parse_gradient(res)); } else if ( color[0] == '?' ) { styler->use.set(color_from_theme(color)); } else { styler->color.set(parse_color(color)); } } void add_stroke(const ParseFuncArgs& args, model::ShapeListProperty* shapes, const Style& style) { auto stroke = std::make_unique(document); set_styler_style(stroke.get(), style.get("strokeColor", "")); stroke->opacity.set(percent_1(style.get("strokeAlpha", "1"))); stroke->width.set(parse_unit(style.get("strokeWidth", "1"))); stroke->cap.set(line_cap(style.get("strokeLineCap", "butt"))); stroke->join.set(line_join(style.get("strokeLineJoin", "butt"))); stroke->miter_limit.set(parse_unit(style.get("strokeMiterLimit", "4"))); auto anim = get_animations(args.element); for ( const auto& kf : anim.single("strokeColor") ) stroke->color.set_keyframe(kf.time, kf.values.color())->set_transition(kf.transition); for ( const auto& kf : anim.single("strokeAlpha") ) stroke->opacity.set_keyframe(kf.time, kf.values.scalar())->set_transition(kf.transition); for ( const auto& kf : anim.single("strokeWidth") ) stroke->width.set_keyframe(kf.time, kf.values.scalar())->set_transition(kf.transition); shapes->insert(std::move(stroke)); } const AnimateParser::AnimatedProperties& get_animations(const QDomElement& element) { auto name = element.attribute("name"); return animations[name]; } void add_fill(const ParseFuncArgs& args, model::ShapeListProperty* shapes, const Style& style) { auto fill = std::make_unique(document); set_styler_style(fill.get(), style.get("fillColor", "")); fill->opacity.set(percent_1(style.get("fillAlpha", "1"))); if ( style.get("fillType", "") == "evenOdd" ) fill->fill_rule.set(model::Fill::EvenOdd); auto anim = get_animations(args.element); for ( const auto& kf : anim.single("fillColor") ) fill->color.set_keyframe(kf.time, kf.values.color())->set_transition(kf.transition); for ( const auto& kf : anim.single("fillAlpha") ) fill->opacity.set_keyframe(kf.time, kf.values.scalar())->set_transition(kf.transition); shapes->insert(std::move(fill)); } void add_trim(const ParseFuncArgs& args, model::ShapeListProperty* shapes, const Style& style) { auto trim = std::make_unique(document); trim->start.set(percent_1(style.get("trimPathStart", "1"))); trim->end.set(percent_1(style.get("trimPathEnd", "1"))); trim->offset.set(percent_1(style.get("trimPathOffset", "1"))); auto anim = get_animations(args.element); for ( const auto& kf : anim.single("trimPathStart") ) trim->start.set_keyframe(kf.time, kf.values.scalar())->set_transition(kf.transition); for ( const auto& kf : anim.single("trimPathEnd") ) trim->end.set_keyframe(kf.time, kf.values.scalar())->set_transition(kf.transition); for ( const auto& kf : anim.single("trimPathOffset") ) trim->offset.set_keyframe(kf.time, kf.values.scalar())->set_transition(kf.transition); shapes->insert(std::move(trim)); } model::Gradient* parse_gradient(Resource* res) { if ( res->element.tagName() != "gradient" ) return nullptr; if ( res->asset ) return res->asset->cast(); // Load colors auto colors = document->assets()->add_gradient_colors(); QGradientStops stops; if ( res->element.hasAttribute("startColor") ) stops.push_back({0.0, parse_color(res->element.attribute("startColor"))}); if ( res->element.hasAttribute("centerColor") ) stops.push_back({0.5, parse_color(res->element.attribute("centerColor"))}); if ( res->element.hasAttribute("endColor") ) stops.push_back({1.0, parse_color(res->element.attribute("endColor"))}); for ( QDomElement e : ElementRange(res->element.childNodes()) ) { if ( e.tagName() == "item" ) stops.push_back({ e.attribute("offset", "0").toDouble(), parse_color(e.attribute("color")) }); } colors->colors.set(stops); // Load gradient auto gradient = document->assets()->add_gradient(); gradient->colors.set(colors); QString type = res->element.attribute("type", "linear"); if ( type == "linear" ) gradient->type.set(model::Gradient::Linear); else if ( type == "radial" ) gradient->type.set(model::Gradient::Radial); else if ( type == "sweeo" ) gradient->type.set(model::Gradient::Conical); gradient->start_point.set({ len_attr(res->element, "startX"), len_attr(res->element, "startY"), }); gradient->end_point.set({ len_attr(res->element, "endX"), len_attr(res->element, "endY"), }); // TODO center / radius res->asset = gradient; return gradient; } void parse_transform(model::Transform* trans, const ParseFuncArgs& args) { QPointF anchor = { len_attr(args.element, "pivotX"), len_attr(args.element, "pivotY"), }; trans->anchor_point.set(anchor); trans->position.set(anchor + QPointF{ len_attr(args.element, "translateX"), len_attr(args.element, "translateY"), }); trans->scale.set(QVector2D( percent_1(args.element.attribute("scaleX", "1")), percent_1(args.element.attribute("scaleY", "1")) )); trans->rotation.set(args.element.attribute("rotation", "0").toDouble()); auto anim = get_animations(args.element); for ( const auto& kf : anim.joined({"pivotX", "pivotY", "translateX", "translateY"}) ) { anchor = QPointF(kf.values[0].scalar(), kf.values[1].scalar()); trans->anchor_point.set_keyframe(kf.time, anchor)->set_transition(kf.transition); QPointF pos(kf.values[2].scalar(), kf.values[3].scalar()); trans->position.set_keyframe(kf.time, anchor + pos)->set_transition(kf.transition); } for ( const auto& kf : anim.joined({"scaleX", "scaleY"}) ) { QVector2D scale(kf.values[0].scalar(), kf.values[1].scalar()); trans->scale.set_keyframe(kf.time, scale)->set_transition(kf.transition); } for ( const auto& kf : anim.single("rotation") ) trans->rotation.set_keyframe(kf.time, kf.values.scalar())->set_transition(kf.transition); } std::unique_ptr parse_clip(const QDomElement& element) { auto clip = std::make_unique(document); set_name(clip.get(), element); QString d = element.attribute("pathData"); math::bezier::MultiBezier bez = PathDParser(d).parse(); auto fill = std::make_unique(document); fill->color.set(QColor(255, 255, 255)); clip->shapes.insert(std::move(fill)); std::vector shapes; for ( const auto& bezier : bez.beziers() ) { auto shape = std::make_unique(document); shape->shape.set(bezier); shape->closed.set(bezier.closed()); shapes.push_back(shape.get()); clip->shapes.insert(std::move(shape)); } path_animation(shapes, get_animations(element), "pathData"); return clip; } void parseshape_group(const ParseFuncArgs& args) { std::unique_ptr clip; for ( auto e : ElementRange(args.element.elementsByTagName("clip-path")) ) { clip = parse_clip(e); break; } model::Group* group = nullptr; if ( clip ) { auto obj = std::make_unique(document); group = obj.get(); args.shape_parent->insert(std::move(obj)); } else { auto obj = std::make_unique(document); group = obj.get(); args.shape_parent->insert(std::move(obj)); } set_name(group, args.element); parse_transform(group->transform.get(), args); parse_children({args.element, &group->shapes, args.parent_style, true}); } void parseshape_path(const ParseFuncArgs& args) { QString d = args.element.attribute("pathData"); math::bezier::MultiBezier bez = PathDParser(d).parse(); ShapeCollection shapes; std::vector paths; for ( const auto& bezier : bez.beziers() ) { auto shape = push(shapes); shape->shape.set(bezier); shape->closed.set(bezier.closed()); paths.push_back(shape); } add_shapes(args, std::move(shapes)); path_animation(paths, get_animations(args.element), "pathData"); } Resource* get_resource(const QString& id) { auto iter = resources.find(id); if ( iter != resources.end() ) return &iter->second; if ( resource_path.isRoot() || id.isEmpty() || id[0] != '@' || id.back() == '\0' ) { warning(i18n("Unknown resource id %1", id)); return {}; } QString path = resource_path.filePath(id.mid(1) + ".xml"); QFile resource_file(path); if ( !resource_file.open(QIODevice::ReadOnly) ) { warning(i18n("Could not read file %1", path)); warning(i18n("Could not load resource %1", id)); return {}; } SvgParseError err; QDomDocument resource_dom; #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) auto result = resource_dom.setContent(&resource_file, QDomDocument::ParseOption::UseNamespaceProcessing); err.message = result.errorMessage; err.line = result.errorLine; err.column = result.errorColumn; #else bool result = resource_dom.setContent(&resource_file, true, &err.message, &err.line, &err.column); #endif if ( !result ) { warning(err.formatted(path)); warning(i18n("Could not load resource %1", id)); return {}; } iter = resources.insert({id, {id, resource_dom.documentElement()}}).first; return &iter->second; } QString add_as_resource(const QDomElement& e) { internal_resource_id++; QString id = QString("@(internal)%1").arg(internal_resource_id); id.push_back(QChar(0)); resources[id] = {e.tagName(), e}; return id; } model::NamedColor* color_from_theme(const QString& color) { QString norm_name; if ( color.contains("/") ) norm_name = color.split("/").back(); else norm_name = color.mid(1); auto iter = palette_colors.find(norm_name); if ( iter != palette_colors.end() ) return iter->second; QColor col = Qt::black; auto it2 = theme_colors.find(norm_name); if ( it2 != theme_colors.end() ) col = it2->second; auto asset = document->assets()->add_color(col); palette_colors.emplace(norm_name, asset); return asset; } QDir resource_path; std::map resources; int internal_resource_id = 0; std::map palette_colors; std::map animations; static const std::map shape_parsers; static const std::unordered_set style_atrrs; static const std::unordered_map theme_colors; }; const std::map glaxnimate::io::avd::AvdParser::Private::shape_parsers = { {"group", &glaxnimate::io::avd::AvdParser::Private::parseshape_group}, {"path", &glaxnimate::io::avd::AvdParser::Private::parseshape_path}, }; const std::unordered_set glaxnimate::io::avd::AvdParser::Private::style_atrrs = { "fillColor", "fillAlpha", "fillType", "strokeColor", "strokeAlpha", "strokeWidth", "strokeLineCap", "strokeLineJoin", "strokeMiterLimit", "trimPathStart", "trimPathEnd", "trimPathOffset", }; glaxnimate::io::avd::AvdParser::AvdParser( QIODevice* device, const QDir& resource_path, model::Document* document, const std::function& on_warning, ImportExport* io, QSize forced_size, model::FrameTime default_time ) : d(std::make_unique(resource_path, document, on_warning, io, forced_size, default_time)) { d->load(device); } glaxnimate::io::avd::AvdParser::~AvdParser() = default; void glaxnimate::io::avd::AvdParser::parse_to_document() { d->parse(); } /// Based on the material theme /// Extracted using https://gitlab.com/-/snippets/2502132 const std::unordered_map glaxnimate::io::avd::AvdParser::Private::theme_colors = { {"colorForeground", "#ffffffff"}, {"colorForegroundInverse", "#ff000000"}, {"colorBackground", "#ff303030"}, {"colorBackgroundFloating", "#ff424242"}, {"colorError", "#ff7043"}, {"opacityListDivider", "#1f000000"}, {"textColorPrimary", "#ff000000"}, {"textColorSecondary", "#ff000000"}, {"textColorHighlight", "#ffffffff"}, {"textColorHighlightInverse", "#ffffffff"}, {"navigationBarColor", "#ff000000"}, {"panelColorBackground", "#000"}, {"colorPrimaryDark", "#ff000000"}, {"colorPrimary", "#ff212121"}, {"colorAccent", "#ff80cbc4"}, {"tooltipForegroundColor", "#ff000000"}, {"colorPopupBackground", "#ff303030"}, {"colorListDivider", "#ffffffff"}, {"textColorLink", "#ff80cbc4"}, {"textColorLinkInverse", "#ff80cbc4"}, {"editTextColor", "#ff000000"}, {"windowBackground", "#ff303030"}, {"statusBarColor", "#ff000000"}, {"panelBackground", "#ff303030"}, {"panelColorForeground", "#ff000000"}, {"detailsElementBackground", "#ff303030"}, {"actionMenuTextColor", "#ff000000"}, {"colorEdgeEffect", "#ff212121"}, {"colorControlNormal", "#ff000000"}, {"colorControlActivated", "#ff80cbc4"}, {"colorProgressBackgroundNormal", "#ff000000"}, }; mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/settings/settings_group.hpp000664 001750 001750 00000002703 15165022620 034054 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/settings/setting.hpp" #include "custom_settings_group.hpp" namespace glaxnimate::settings { class SettingsGroup : public CustomSettingsGroupBase { public: using iterator = SettingList::const_iterator; SettingsGroup(QString slug, QString label, const QString& icon, SettingList settings); SettingsGroup(SettingList settings); QString slug() const override; QString label() const override; QIcon icon() const override; void load(QSettings& settings) override; void save(QSettings& settings) override; bool has_visible_settings() const override; QVariant get_variant(const QString& setting_slug) const override; bool set_variant(const QString& setting_slug, const QVariant& value) override; QVariant get_default(const QString& setting_slug) const override; QVariant define(const QString& setting_slug, const QVariant& default_value) override; iterator begin() const { return settings_.begin(); } iterator end() const { return settings_.end(); } SettingList& settings() { return settings_; } QVariantMap& values() { return values_; } private: QString slug_; QString label_; QString icon_; SettingList settings_; QVariantMap values_; }; } // namespace glaxnimate::settings mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/animated_properties.hpp000664 001750 001750 00000016227 15165022620 033613 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/math/bezier/bezier.hpp" #include "glaxnimate/model/animation/keyframe_transition.hpp" #include "glaxnimate/model/animation/frame_time.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" namespace glaxnimate::io::detail { struct ValueVariant { public: enum Type { Vector, Bezier, String, Color }; ValueVariant(qreal v) : value_(std::vector{v}) {} ValueVariant(std::vector v = {}) : value_(std::move(v)) {} ValueVariant(math::bezier::MultiBezier v ) : value_(std::move(v)) {} ValueVariant(QString v) : value_(std::move(v)) {} ValueVariant(QColor v) : value_(std::move(v)) {} ValueVariant(const QVariant& v) { if ( v.userType() == QMetaType::QColor ) value_ = v.value(); else if ( v.userType() == QMetaType::QString ) value_ = v.value(); else if ( v.canConvert() ) value_ = std::vector(1, v.toReal()); } Type type() const { return Type(value_.index()); } qreal scalar() const { return vector()[0]; } const std::vector& vector() const { return std::get(value_); } const math::bezier::MultiBezier& bezier() const { return std::get(value_); } const QString& string() const { return std::get(value_); } const QColor& color() const { return std::get(value_); } ValueVariant lerp(const ValueVariant& other, qreal t) const { if ( type() != other.type() ) return *this; switch ( type() ) { case Type::Vector: return math::lerp(vector(), other.vector(), t); case Type::Bezier: if ( bezier().size() == 1 && other.bezier().size() == 1 ) { math::bezier::MultiBezier mb; mb.beziers().push_back(bezier()[0].lerp(other.bezier()[0], t)); return mb; } return *this; case Type::String: return t < 1 ? string() : other.string(); case Type::Color: return math::lerp(color(), other.color(), t); } return {}; } bool compatible(const ValueVariant& other) const { if ( type() != other.type() ) return false; if ( type() == Vector ) return vector().size() == other.vector().size(); return true; } private: std::variant, math::bezier::MultiBezier, QString, QColor> value_; }; struct PropertyKeyframe { model::FrameTime time; ValueVariant values; model::KeyframeTransition transition; bool operator< (const PropertyKeyframe& o) const { return time < o.time; } }; struct JoinedPropertyKeyframe { model::FrameTime time; std::vector values; model::KeyframeTransition transition; }; struct AnimatedProperty { std::vector keyframes; math::bezier::Bezier motion; bool auto_orient = false; void sort() { std::sort(keyframes.begin(), keyframes.end()); } }; struct JoinedProperty { std::variant prop; int index = 0; bool at_end() const { if ( prop.index() == 0 ) return index + 1 == int(std::get<0>(prop)->keyframes.size()); return true; } const PropertyKeyframe* keyframe(int off = 0) const { return &std::get<0>(prop)->keyframes[index+off]; } template decltype(auto) get() const noexcept { return std::get(prop); } }; struct AnimatedProperties { std::map properties; virtual ~AnimatedProperties() {} virtual bool prepare_joined(std::vector&) const { return true; } bool has(const QString& name) const { return properties.count(name); } std::vector single(const QString& prop_name) const { auto it = properties.find(prop_name); if ( it == properties.end() || it->second.keyframes.size() < 2 ) return {}; return it->second.keyframes; } std::vector joined(const std::vector& prop_names) const { std::vector props; props.reserve(prop_names.size()); int found = 0; for ( const auto& name : prop_names ) { auto it = properties.find(name); if ( it == properties.end() || it->second.keyframes.size() < 2 ) { props.push_back({&name}); } else { props.push_back({&it->second}); found++; } } if ( !found ) return {}; if ( !prepare_joined(props) ) return {}; std::vector keyframes; bool cont = true; while ( cont ) { model::FrameTime time = std::numeric_limits::max(); for ( const auto& p : props ) { if ( p.prop.index() == 0 && p.keyframe()->time < time ) time = p.keyframe()->time; } std::vector values; values.reserve(props.size()); std::vector transitions; transitions.resize(found); cont = false; for ( auto& p : props ) { if ( p.prop.index() == 0 ) { auto kf = p.keyframe(); if ( (p.at_end() && kf->time <= time) || (p.index == 0 && kf->time > time) ) { values.push_back(kf->values); } else if ( kf->time == time ) { p.index++; values.push_back(kf->values); transitions.push_back(kf->transition); cont = true; } else { auto kf1 = p.keyframe(-1); qreal x = math::unlerp(kf1->time, kf->time, time); qreal t = kf1->transition.lerp_factor(x); values.push_back(kf1->values.lerp(kf->values, t)); transitions.push_back(kf1->transition.split(x).first); cont = true; } } else { values.push_back(p.get<2>()); } } keyframes.push_back({time, std::move(values), model::JoinAnimatables::Keyframe::mix_transitions(transitions)}); } return keyframes; } }; } // namespace glaxnimate::io::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/animatable_base.cpp000664 001750 001750 00000004544 15165022620 035306 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/animation/animatable_base.hpp" #include "glaxnimate/command/animation_commands.hpp" #include "glaxnimate/model/animation/animatable.hpp" QUndoCommand *glaxnimate::model::AnimatedPropertyBase::command_add_smooth_keyframe(FrameTime time, const QVariant &value, bool commit, QUndoCommand *parent) { return new command::SetKeyframe(this, time, value.isNull() ? static_value() : value, commit, parent); } QUndoCommand *glaxnimate::model::AnimatedPropertyBase::command_remove_keyframe(FrameTime time, QUndoCommand *parent) { if ( !keyframe_at(time) ) return nullptr; return new command::RemoveKeyframeTime(this, time, parent); } QUndoCommand *glaxnimate::model::AnimatedPropertyBase::command_clear_keyframes(QUndoCommand *parent) { return new command::RemoveAllKeyframes(this, static_value(), parent); } QUndoCommand *glaxnimate::model::AnimatedPropertyBase::command_set_transition(FrameTime time, const model::KeyframeTransition &transition, QUndoCommand *parent) { if ( !keyframe_at(time) ) return nullptr; return new command::SetKeyframeTransition(this, time, transition, parent); } QUndoCommand *glaxnimate::model::AnimatedPropertyBase::command_set_transition_side(FrameTime time, model::KeyframeTransition::Descriptive desc, const QPointF &point, bool before_transition, QUndoCommand *parent) { if ( !keyframe_at(time) ) return nullptr; auto transition = command::SetKeyframeTransition::transition_side(this, time, desc, point, before_transition); return new command::SetKeyframeTransition(this, time, transition, parent); } QUndoCommand *glaxnimate::model::AnimatedPropertyBase::command_move_keyframe(FrameTime time_before, FrameTime time_after, QUndoCommand *parent) { if ( !keyframe_at(time_before) ) return nullptr; if ( !keyframe_at(time_after) ) { return new command::MoveKeyframe(this, time_before, time_after, parent); } auto subp = new QUndoCommand(i18n("Move keyframe"), parent); new command::RemoveKeyframeTime(this, time_after, subp); new command::MoveKeyframe(this, time_before, time_after, subp); return subp; } int glaxnimate::model::AnimatableBase::property_type() const { return model::PropertyTraits::Unknown; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/000775 001750 001750 00000000000 15165022620 032431 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/video/000775 001750 001750 00000000000 15165022620 031020 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/validation.hpp000664 001750 001750 00000002002 15165022620 033171 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/lottie/lottie_format.hpp" #include "glaxnimate/model/visitor.hpp" namespace glaxnimate::io::lottie { class ValidationVisitor : public model::Visitor { public: explicit ValidationVisitor(LottieFormat* fmt) : fmt(fmt) {} protected: void show_error(model::DocumentNode * node, const QString& message, log::Severity severity) { fmt->message(i18n("%1: %2", node->object_name(), message), severity); } void on_visit_document(model::Document * document, model::Composition* main) override; LottieFormat* fmt; QSize fixed_size; std::vector allowed_fps; int max_frames = 0; }; /** * \brief Triggers warnings on \p format if \p document isn't suitable for Discord stickers */ void validate_discord(model::Document* document, model::Composition* main, LottieFormat* format); } // namespace glaxnimate::io::lottie mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/io_registry.hpp000664 001750 001750 00000012235 15165022620 032107 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" #include "glaxnimate/io/mime/mime_serializer.hpp" namespace glaxnimate::io { namespace detail { inline bool compare_ie_ptr(const ImportExport* ptr_a, const ImportExport* ptr_b) noexcept { return ptr_a->priority() > ptr_b->priority(); } inline bool compare_ie_unique_ptr(const std::unique_ptr& ptr_a, const std::unique_ptr& ptr_b) noexcept { return compare_ie_ptr(ptr_a.get(), ptr_b.get()); } } // namespace detail class IoRegistry { public: static IoRegistry& instance() { static IoRegistry factory; return factory; } ImportExport* register_object(std::unique_ptr ie) { using namespace detail; auto iter = std::upper_bound(object_list.begin(), object_list.end(), ie, &compare_ie_unique_ptr); ImportExport* format = ie.get(); object_list.insert(iter, std::move(ie)); if ( format->can_save() ) exporters_.insert(std::upper_bound(exporters_.begin(), exporters_.end(), format, &compare_ie_ptr), format); if ( format->can_open() ) importers_.insert(std::upper_bound(importers_.begin(), importers_.end(), format, &compare_ie_ptr), format); if ( format->can_save_static() ) static_exporters_.insert(std::upper_bound(static_exporters_.begin(), static_exporters_.end(), format, &compare_ie_ptr), format); return format; } void unregister(ImportExport* object) { for ( auto it = object_list.begin(); it != object_list.end(); ++it ) { if ( it->get() == object ) { object_list.erase(it); break; } } importers_.erase(std::remove(importers_.begin(), importers_.end(), object), importers_.end()); exporters_.erase(std::remove(exporters_.begin(), exporters_.end(), object), exporters_.end()); } mime::MimeSerializer* register_object(std::unique_ptr ie) { mime_list.push_back(std::move(ie)); mime::MimeSerializer* format = mime_list.back().get(); mime_pointers.push_back(format); return format; } const std::vector& handlers(ImportExport::Direction direction) const { if ( direction == ImportExport::FrameExport ) return static_exporters_; if ( direction == ImportExport::Export ) return exporters_; return importers_; } const std::vector& importers() const { return importers_; } const std::vector& exporters() const { return exporters_; } const std::vector& static_exporters() const { return static_exporters_; } const std::vector& serializers() const { return mime_pointers; } const std::vector>& registered() const { return object_list; } ImportExport* from_extension(const QString& extension, ImportExport::Direction direction) const { int top_priority = std::numeric_limits::min(); ImportExport* best = nullptr; for ( const auto& p : handlers(direction) ) { if ( p->can_handle_extension(extension, direction) && p->priority() > top_priority ) { best = p; top_priority = p->priority(); } } return best; } ImportExport* from_filename(const QString& filename, ImportExport::Direction direction) const { int top_priority = std::numeric_limits::min(); ImportExport* best = nullptr; for ( const auto& p : handlers(direction) ) { if ( p->can_handle_filename(filename, direction) && p->priority() > top_priority ) { best = p; top_priority = p->priority(); } } return best; } ImportExport* from_slug(const QString& slug, ImportExport::Direction direction) const { for ( const auto& p : handlers(direction) ) if ( p->slug() == slug ) return p; return nullptr; } mime::MimeSerializer* serializer_from_slug(const QString& slug) const { for ( const auto& serializer : mime_list ) { if ( serializer->slug() == slug ) return serializer.get(); } return nullptr; } template Class* register_class(Args&&... args) { return static_cast( IoRegistry::instance().register_object(std::make_unique(std::forward(args)...)) ); } private: std::vector> object_list; std::vector importers_; std::vector exporters_; std::vector static_exporters_; std::vector> mime_list; std::vector mime_pointers; IoRegistry() = default; ~IoRegistry() = default; }; } // namespace glaxnimate::io mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_parser_private.hpp000664 001750 001750 00000026522 15165022620 034260 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/utils/regexp.hpp" #include "glaxnimate/utils/sort_gradient.hpp" #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/composable/layer.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/shapes/shapes/rect.hpp" #include "glaxnimate/model/shapes/shapes/ellipse.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/model/shapes/shapes/text.hpp" #include "glaxnimate/model/shapes/style/fill.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/named_color.hpp" #include "glaxnimate/math/math.hpp" #include "glaxnimate/io/svg/path_parser.hpp" #include "glaxnimate/io/svg/animate_parser.hpp" #include "glaxnimate/io/svg/parse_error.hpp" #include "glaxnimate/io/svg/font_weight.hpp" #include "glaxnimate/io/svg/css_parser.hpp" #include "glaxnimate/io/svg/detail.hpp" namespace glaxnimate::io::svg::detail { class SvgParserPrivate { public: using ShapeCollection = std::vector>; struct ParseFuncArgs { const QDomElement& element; model::ShapeListProperty* shape_parent; const Style& parent_style; bool in_group; }; SvgParserPrivate( model::Document* document, const std::function& on_warning, ImportExport* io, QSize forced_size, model::FrameTime default_time ) : document(document), on_warning(on_warning), io(io), forced_size(forced_size), default_time(default_time == 0 ? 180 : default_time) { animate_parser.on_warning = on_warning; } void load(QIODevice* device) { SvgParseError err; #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) auto result = dom.setContent(device, QDomDocument::ParseOption::UseNamespaceProcessing); err.message = result.errorMessage; err.line = result.errorLine; err.column = result.errorColumn; #else bool result = dom.setContent(device, true, &err.message, &err.line, &err.column); #endif if ( !result ) throw err; } virtual ~SvgParserPrivate() = default; void parse(model::Document* document = nullptr) { if ( document ) this->document = document; if ( this->document->assets()->compositions->values.empty() ) main = this->document->assets()->compositions->values.insert(std::make_unique(this->document)); else main = this->document->assets()->compositions->values[0]; animate_parser.fps = main->fps.get(); size = main->size(); auto root = dom.documentElement(); if ( forced_size.isValid() ) { size = forced_size; } else { size = get_size(root); } to_process = 0; on_parse_prepare(root); if ( io ) io->progress_max_changed(to_process); on_parse(root); write_document_data(); } protected: QString attr(const QDomElement& e, const QString& ns, const QString& name, const QString& defval = {}) { if ( ns.isEmpty() ) return e.attribute(name, defval); return e.attributeNS(xmlns.at(ns), name, defval); } qreal len_attr(const QDomElement& e, const QString& name, qreal defval = 0) { if ( e.hasAttribute(name) ) return parse_unit(e.attribute(name)); return defval; } qreal parse_unit(const QString& svg_value) { QRegularExpressionMatch match = unit_re.match(svg_value); if ( match.hasMatch() ) { qreal value = match.captured(1).toDouble(); qreal mult = unit_multiplier(match.captured(2)); if ( mult != 0 ) return value * mult; } warning(QString("Unknown length value %1").arg(svg_value)); return 0; } qreal unit_multiplier(const QString& unit) { static const constexpr qreal cmin = 2.54; if ( unit == "px" || unit == "" || unit == "dp" || unit == "dip" || unit == "sp" ) return 1; else if ( unit == "vw" ) return size.width() * 0.01; else if ( unit == "vh" ) return size.height() * 0.01; else if ( unit == "vmin" ) return std::min(size.width(), size.height()) * 0.01; else if ( unit == "vmax" ) return std::max(size.width(), size.height()) * 0.01; else if ( unit == "in" ) return dpi; else if ( unit == "pc" ) return dpi / 6; else if ( unit == "pt" ) return dpi / 72; else if ( unit == "cm" ) return dpi / cmin; else if ( unit == "mm" ) return dpi / cmin / 10; else if ( unit == "Q" ) return dpi / cmin / 40; return 0; } qreal unit_convert(qreal val, const QString& from, const QString& to) { return val * unit_multiplier(from) / unit_multiplier(to); } void warning(const QString& msg) { if ( on_warning ) on_warning(msg); } model::Layer* add_layer(model::ShapeListProperty* parent) { model::Layer* lay = new model::Layer(document); parent->insert(std::unique_ptr(lay)); layers.push_back(lay); return lay; } QStringList split_attr(const QDomElement& e, const QString& name) { return e.attribute(name).split(AnimateParser::separator, Qt::SkipEmptyParts); } void parse_children(const ParseFuncArgs& args) { for ( const auto& domnode : ItemCountRange(args.element.childNodes()) ) { if ( domnode.isElement() ) { auto child = domnode.toElement(); parse_shape({child, args.shape_parent, args.parent_style, args.in_group}); } } } template T* push(ShapeCollection& sc) { T* t = new T(document); sc.emplace_back(t); return t; } void populate_ids(const QDomElement& elem) { if ( elem.hasAttribute("id") ) map_ids[elem.attribute("id")] = elem; for ( const auto& domnode : ItemCountRange(elem.childNodes()) ) { if ( domnode.isElement() ) populate_ids(domnode.toElement()); } } QDomElement element_by_id(const QString& id) { // dom.elementById() doesn't work ;_; if ( map_ids.empty() ) populate_ids(dom.documentElement()); auto it = map_ids.find(id); if ( it == map_ids.end() ) return {}; return it->second; } void mark_progress() { processed++; if ( io && processed % 10 == 0 ) io->progress(processed); } QDomElement query_element(const std::vector& path, const QDomElement& parent, std::size_t index = 0) { if ( index >= path.size() ) return parent; auto head = path[index]; for ( const auto& domnode : ItemCountRange(parent.childNodes()) ) { if ( domnode.isElement() ) { auto child = domnode.toElement(); if ( child.tagName() == head ) return query_element(path, child, index+1); } } return {}; } QString query(const std::vector& path, const QDomElement& parent, std::size_t index = 0) { return query_element(path, parent, index).text(); } std::vector double_args(const QString& str) { auto args_s = QStringView{str}.split(AnimateParser::separator, Qt::SkipEmptyParts); std::vector args; args.reserve(args_s.size()); std::transform(args_s.begin(), args_s.end(), std::back_inserter(args), [](const QStringView& s){ return s.toDouble(); }); return args; } // parse attributes like opacity where it's a value in [0-1] or a percentage static double percent_1(const QString& s) { if ( s.contains('%') ) return QStringView{s}.mid(0, s.size()-1).toDouble() / 100; return s.toDouble(); } static model::Stroke::Cap line_cap(const QString& linecap) { if ( linecap == "round" ) return model::Stroke::RoundCap; else if ( linecap == "butt" ) return model::Stroke::ButtCap; else if ( linecap == "square" ) return model::Stroke::SquareCap; return model::Stroke::ButtCap; } static model::Stroke::Join line_join(const QString& linecap) { if ( linecap == "round" ) return model::Stroke::RoundJoin; else if ( linecap == "bevel" ) return model::Stroke::BevelJoin; else if ( linecap == "miter" ) return model::Stroke::MiterJoin; return model::Stroke::MiterJoin; } void path_animation( const std::vector& paths, const AnimatedProperties& anim, const QString& attr ) { if ( !paths.empty() ) { for ( const auto& kf : anim.single(attr) ) { for ( int i = 0; i < math::min(kf.values.bezier().size(), paths.size()); i++ ) paths[i]->shape.set_keyframe(kf.time, kf.values.bezier()[i])->set_transition(kf.transition); } } } private: void write_document_data() { main->width.set(size.width()); main->height.set(size.height()); if ( !animate_parser.kf_range_initialized ) { animate_parser.min_kf = 0; animate_parser.max_kf = default_time; } else { animate_parser.max_kf = qRound(animate_parser.max_kf); } main->animation->first_frame.set(animate_parser.min_kf); main->animation->last_frame.set(animate_parser.max_kf); for ( auto lay : layers ) { lay->animation->last_frame.set(animate_parser.min_kf); lay->animation->last_frame.set(animate_parser.max_kf); } document->undo_stack().clear(); } protected: virtual void on_parse_prepare(const QDomElement& root) = 0; virtual QSizeF get_size(const QDomElement& root) = 0; virtual void parse_shape(const ParseFuncArgs& args) = 0; virtual void on_parse(const QDomElement& root) = 0; QDomDocument dom; qreal dpi = 96; QSizeF size; model::Document* document; AnimateParser animate_parser; std::function on_warning; std::unordered_map map_ids; std::unordered_map brush_styles; std::unordered_map gradients; std::vector layers; int to_process = 0; int processed = 0; ImportExport* io = nullptr; QSize forced_size; model::FrameTime default_time; model::Composition* main = nullptr; static const QRegularExpression unit_re; }; } // namespace glaxnimate::io::svg::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/lottie_html_format.hpp000664 001750 001750 00000001607 15165022620 034745 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" namespace glaxnimate::io::lottie { class LottieHtmlFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "lottie_html"; } QString name() const override { return i18n("Lottie HTML Preview"); } QStringList extensions(Direction) const override { return {"html", "htm"}; } bool can_save() const override { return true; } bool can_open() const override { return false; } static QByteArray html_head(ImportExport* ie, model::Composition* comp, const QString& extra); private: bool on_save(QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& setting_values) override; }; } // namespace glaxnimate::io::lottie mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/script/000775 001750 001750 00000000000 15165022620 027731 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/property.hpp000664 001750 001750 00000025072 15165022620 034014 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include "glaxnimate/model/animation/frame_time.hpp" #include "glaxnimate/utils/i18n.hpp" namespace glaxnimate::math::bezier { class Bezier; } namespace glaxnimate::model { class Object; class Document; struct PropertyTraits { enum Type { Unknown, Object, ObjectReference, Bool, Int, Float, Point, Color, Size, Scale, String, Enum, Uuid, Bezier, Data, Gradient, }; enum Flags { NoFlags = 0x00, List = 0x01, ///< list/array of values ReadOnly = 0x02, ///< not modifiable by GUI Animated = 0x04, ///< animated Visual = 0x08, ///< has visible effects OptionList = 0x10, ///< has a set of valid values Percent = 0x20, ///< for Float, show as percentage on the GUI Hidden = 0x40, ///< for Visual, not shown prominently }; Type type = Unknown; int flags = NoFlags; bool is_object() const { return type == Object || type == ObjectReference; } template static constexpr Type get_type() noexcept; template static PropertyTraits from_scalar(int flags=NoFlags) { return { get_type(), flags }; } }; namespace detail { template struct GetType; template static constexpr bool is_object_v = std::is_base_of_v || std::is_same_v; // template // struct GetType>> // { // static constexpr const PropertyTraits::Type value = PropertyTraits::ObjectReference; // }; template struct GetType, std::enable_if_t>> { static constexpr const PropertyTraits::Type value = PropertyTraits::Object; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Bool; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Float; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Scale; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Color; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Size; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::String; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Uuid; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Point; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Bezier; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Data; }; template<> struct GetType { static constexpr const PropertyTraits::Type value = PropertyTraits::Gradient; }; template struct GetType>> { static constexpr const PropertyTraits::Type value = PropertyTraits::Int; }; template struct GetType>> { static constexpr const PropertyTraits::Type value = PropertyTraits::Enum; }; } // namespace detail template inline constexpr PropertyTraits::Type PropertyTraits::get_type() noexcept { return detail::GetType::value; } #define GLAXNIMATE_PROPERTY_IMPL(type, name) \ public: \ type get_##name() const { return name.get(); } \ bool set_##name(const type& v) { \ return name.set_undoable(QVariant::fromValue(v)); \ } \ private: \ Q_PROPERTY(type name READ get_##name WRITE set_##name) \ Q_CLASSINFO(#name, "property " #type) \ // macro end #define GLAXNIMATE_PROPERTY(type, name, ...) \ public: \ Property name{this, kli18n(#name), __VA_ARGS__}; \ GLAXNIMATE_PROPERTY_IMPL(type, name) \ // macro end #define GLAXNIMATE_PROPERTY_RO(type, name, default_value) \ public: \ Property name{this, kli18n(#name), default_value, {}, {}, PropertyTraits::ReadOnly}; \ type get_##name() const { return name.get(); } \ private: \ Q_PROPERTY(type name READ get_##name) \ Q_CLASSINFO(#name, "property " #type) \ // macro end class BaseProperty { Q_GADGET public: BaseProperty(Object* object, const utils::LazyLocalizedString& name, PropertyTraits traits); BaseProperty(const BaseProperty&) = delete; BaseProperty& operator=(const BaseProperty&) = delete; virtual ~BaseProperty() = default; virtual QVariant value() const = 0; virtual bool set_value(const QVariant& val) = 0; virtual bool set_undoable(const QVariant& val, bool commit = true); virtual void set_time(FrameTime t) = 0; virtual void transfer(Document*) {}; virtual bool valid_value(const QVariant& v) const = 0; virtual bool assign_from(const BaseProperty* prop) { return set_value(prop->value()); } /** * \brief Stretches animations by the given amount */ virtual void stretch_time(qreal multiplier) { Q_UNUSED(multiplier); } QString localized_name() const { return name_.toString(); } QString name() const { return QString::fromLatin1(name_.untranslatedText()); } PropertyTraits traits() const { return traits_; } Object* object() const { return object_; } protected: void value_changed(); private: Object* object_; utils::LazyLocalizedString name_; PropertyTraits traits_; }; namespace detail { template inline T defval() { return T(); } template<> inline void defval() {} template auto invoke_impl(const FuncT& fun, std::index_sequence, const std::tuple& args) { return fun(std::get(args)...); } template auto invoke(const FuncT& fun, const Args&... t) { return invoke_impl(fun, std::make_index_sequence(), std::make_tuple(t...)); } } // namespace detail template class PropertyCallback { private: class HolderBase { public: virtual ~HolderBase() = default; virtual Return invoke(Object* obj, const ArgType&... v) const = 0; }; template class Holder : public HolderBase { public: using FuncP = std::function; Holder(FuncP func) : func(std::move(func)) {} Return invoke(Object* obj, const ArgType&... v) const override { return detail::invoke(func, static_cast(obj), v...); } FuncP func; }; std::unique_ptr holder; public: PropertyCallback() = default; PropertyCallback(std::nullptr_t) {} template PropertyCallback(Return (ObjT::*func)(Arg...)) : holder(std::make_unique>(func)) {} template PropertyCallback(Return (ObjT::*func)(Arg...) const) : holder(std::make_unique>(func)) {} explicit operator bool() const { return bool(holder); } Return operator() (Object* obj, const ArgType&... v) const { if ( holder ) return holder->invoke(obj, v...); return detail::defval(); } }; namespace detail { template std::optional variant_cast(const QVariant& val) { if ( !val.canConvert() ) return {}; QVariant converted = val; if ( !converted.convert(QMetaType::fromType()) ) return {}; return converted.value(); } template class PropertyTemplate : public Base { public: using value_type = Type; using reference = const Type&; PropertyTemplate(Object* obj, const utils::LazyLocalizedString& name, Type default_value = Type(), PropertyCallback emitter = {}, PropertyCallback validator = {}, int flags = PropertyTraits::NoFlags ) : Base(obj, name, PropertyTraits::from_scalar(flags)), value_(std::move(default_value)), emitter(std::move(emitter)), validator(std::move(validator)) {} bool valid_value(const QVariant& val) const override { if ( auto v = detail::variant_cast(val) ) return !validator || validator(this->object(), *v); return false; } bool set(Type value) { if ( validator && !validator(this->object(), value) ) return false; std::swap(value_, value); this->value_changed(); if ( emitter ) emitter(this->object(), value_, value); return true; } reference get() const { return value_; } QVariant value() const override { return QVariant::fromValue(value_); } bool set_value(const QVariant& val) override { if ( auto v = detail::variant_cast(val) ) return set(*v); return false; } void set_time(FrameTime) override {} private: Type value_; PropertyCallback emitter; PropertyCallback validator; }; } // namespace detail template class Property : public detail::PropertyTemplate { public: using detail::PropertyTemplate::PropertyTemplate; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/sort_gradient.hpp000664 001750 001750 00000000767 15165022620 033154 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include namespace glaxnimate::utils { inline bool gradient_stop_comparator(const QGradientStop& a, const QGradientStop& b) noexcept { return a.first <= b.first; } inline void sort_gradient(QGradientStops& stops) { std::sort(stops.begin(), stops.end(), &gradient_stop_comparator); } } // namespace glaxnimate::utils mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/mime/mime_serializer.hpp000664 001750 001750 00000003361 15165022620 033657 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include "glaxnimate/log/log_line.hpp" namespace glaxnimate::model { class Document; class Composition; class DocumentNode; } // namespace glaxnimate::model namespace glaxnimate::io::mime { struct DeserializedData { DeserializedData(); DeserializedData(const DeserializedData&) = delete; DeserializedData(DeserializedData&&); DeserializedData& operator=(const DeserializedData&) = delete; DeserializedData& operator=(DeserializedData&&); ~DeserializedData(); std::unique_ptr document; model::Composition* main = nullptr; bool empty() const; void initialize_data(); }; class MimeSerializer { public: virtual ~MimeSerializer() = default; virtual QString slug() const = 0; virtual QString name() const = 0; virtual QStringList mime_types() const = 0; virtual QByteArray serialize(const std::vector& objects) const = 0; virtual io::mime::DeserializedData deserialize(const QByteArray& data) const; virtual bool can_deserialize() const = 0; virtual void to_mime_data(QMimeData& out, const std::vector& objects) const { QByteArray data = serialize(objects); for ( const QString& mime : mime_types() ) out.setData(mime, data); } io::mime::DeserializedData from_mime_data(const QMimeData& data) const; protected: void message(const QString& message, log::Severity severity = log::Warning) const; }; } // namespace glaxnimate::io::mime mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/renderer/renderer.cpp000664 001750 001750 00000002471 15165022620 032551 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ // #include "glaxnimate/qpainter_renderer.hpp" #include "glaxnimate/renderer/renderer.hpp" std::unique_ptr glaxnimate::renderer::RendererRegistry::default_renderer(int quality) { // if ( quality == 0 ) // return std::make_unique(); return (*default_factory)(quality); } std::unique_ptr glaxnimate::renderer::RendererRegistry::factory_build(const QString &id, int quality) { auto it = factory_map.find(id); if ( it == factory_map.end() ) return {}; return it->second(quality); } void glaxnimate::renderer::RendererRegistry::register_factory(const QString &id, FactoryFunction func, bool make_default) { auto it = factory_map.emplace(id, std::move(func)).first; if ( make_default || !default_factory ) default_factory = &it->second; } const std::map &glaxnimate::renderer::RendererRegistry::factories() { return factory_map; } glaxnimate::renderer::RendererRegistry &glaxnimate::renderer::RendererRegistry::instance() { static RendererRegistry registry; return registry; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/mime/json_mime.hpp000664 001750 001750 00000001513 15165022620 032454 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/glaxnimate/glaxnimate_mime.hpp" namespace glaxnimate::io::mime { class JsonMime : public io::mime::MimeSerializer { public: QString slug() const override { return "json"; } QString name() const override { return i18n("JSON"); } QStringList mime_types() const override { return {"application/json", "text/plain"}; } QByteArray serialize(const std::vector& selection) const override { QJsonDocument json = io::glaxnimate::GlaxnimateMime::serialize_json(selection); return json.toJson(QJsonDocument::Indented); } bool can_deserialize() const override { return false; } }; } // namespace glaxnimate::io::mime mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/meta.cpp000664 001750 001750 00000002612 15165022620 032271 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/bezier/meta.hpp" using namespace glaxnimate; QDataStream& operator<<(QDataStream& ds, const math::bezier::Point& p) { return ds << p.pos << p.tan_in << p.tan_out << qint16(p.type); } QDataStream& operator<<(QDataStream& ds, const math::bezier::Bezier& bez) { ds << quint32(bez.size()) << bez.closed(); for ( const auto& p : bez ) ds << p; return ds; } QDataStream& operator>>(QDataStream& ds, math::bezier::Point& p) { qint16 type = 0; ds >> p.pos >> p.tan_in >> p.tan_out >> type; p.type = math::bezier::PointType(type); return ds; } QDataStream& operator>>(QDataStream& ds, math::bezier::Bezier& bez) { bez.clear(); quint32 size = 0; bool closed = false; ds >> size >> closed; bez.set_closed(closed); for ( quint32 i = 0; i < size; i++ ) { math::bezier::Point p{{}, {}, {}}; ds >> p; bez.push_back(p); } return ds; } void math::bezier::register_meta() { qRegisterMetaType("glaxnimate::math::bezier::Bezier"); qRegisterMetaType("glaxnimate::math::bezier::Point"); QMetaType::registerConverter(&Point::position); QMetaType::registerConverter([](const QPointF& p) { return Point{p, p, p}; }); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/cbor_write_json.hpp000664 001750 001750 00000000503 15165022620 034233 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include namespace glaxnimate::io::lottie { QByteArray cbor_write_json(const QCborMap& obj, bool compact); } // namespace glaxnimate::io::lottie mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/image.cpp000664 001750 001750 00000004555 15165022620 034733 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Image) bool glaxnimate::model::Image::is_valid_image(glaxnimate::model::DocumentNode* node) const { return document()->assets()->images->values.is_valid_reference_value(node, false); } std::vector glaxnimate::model::Image::valid_images() const { return document()->assets()->images->values.valid_reference_values(false); } QRectF glaxnimate::model::Image::local_bounding_rect(glaxnimate::model::FrameTime) const { if ( !image.get() ) return {}; return QRectF(0, 0, image->width.get(), image->height.get()); } void glaxnimate::model::Image::on_paint(renderer::Renderer* painter, glaxnimate::model::FrameTime time, glaxnimate::model::VisualNode::PaintMode mode, glaxnimate::model::Modifier* mod) const { if ( image.get() ) { Composable::on_paint(painter, time, mode, mod); image->paint(painter); } } void glaxnimate::model::Image::on_image_changed(glaxnimate::model::Bitmap* new_use, glaxnimate::model::Bitmap* old_use) { if ( old_use ) { disconnect(old_use, &Bitmap::loaded, this, &Image::on_update_image); } if ( new_use ) { connect(new_use, &Bitmap::loaded, this, &Image::on_update_image); } } void glaxnimate::model::Image::on_update_image() { Q_EMIT property_changed(&image, {}); } QTransform glaxnimate::model::Image::local_transform_matrix(glaxnimate::model::FrameTime t) const { return transform->transform_matrix(t); } void glaxnimate::model::Image::add_shapes(FrameTime, math::bezier::MultiBezier&, const QTransform&) const { } QIcon glaxnimate::model::Image::tree_icon() const { return QIcon::fromTheme("x-shape-image"); } QString glaxnimate::model::Image::type_name_human() const { return i18n("Image"); } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Image::to_painter_path_impl(FrameTime time) const { auto trans = transform.get()->transform_matrix(time); glaxnimate::math::bezier::MultiBezier p; p.append(trans.map(QRectF(QPointF(0, 0), image.get() ? image->pixmap().size() : QSize(0, 0)))); return p; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/data_paths.cpp000664 001750 001750 00000003723 15165022620 032406 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/utils/data_paths.hpp" #include #include #include QString glaxnimate::utils::writable_data_path(const QString& name) { QString search = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); if ( !search.isEmpty() ) { return QDir::cleanPath(QDir(search).absoluteFilePath(name)); } return QString(); } QStringList glaxnimate::utils::data_paths(const QString& name) { QStringList found; for ( const QDir& d: data_roots() ) { if ( d.exists(name) ) found << QDir::cleanPath(d.absoluteFilePath(name)); } found.removeDuplicates(); return found; } QStringList glaxnimate::utils::data_paths_unchecked(const QString& name) { QStringList filter; for ( const QDir& d: data_roots() ) { filter << QDir::cleanPath(d.absoluteFilePath(name)); } filter.removeDuplicates(); return filter; } QList glaxnimate::utils::data_roots() { QList search; // std paths for ( const QString& str : QStandardPaths::standardLocations(QStandardPaths::AppDataLocation) ) search.push_back(QDir(str)); // executable dir QDir binpath(QCoreApplication::applicationDirPath()); auto app = QCoreApplication::instance(); #ifdef Q_OS_WIN // some Windows apps do not use a bin subfolder search.push_back(binpath.filePath(QString("share/%1/%2").arg(app->organizationName()).arg(app->applicationName()))); #endif binpath.cdUp(); search.push_back(binpath.filePath(QString("share/%1/%2").arg(app->organizationName()).arg(app->applicationName()))); #ifdef Q_OS_MAC // some macOS app bundles use a Resources subfolder search.push_back(binpath.filePath(QString("Resources/%1/%2").arg(app->organizationName()).arg(app->applicationName()))); #endif return search; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/frame_time.hpp000664 001750 001750 00000000354 15165022620 034327 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once namespace glaxnimate::model { using FrameTime = double; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/log/logger.cpp000664 001750 001750 00000000225 15165022620 031170 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "logger.hpp" src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/round_corners.cpp000664 001750 001750 00000005270 15165022620 036304 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/modifiers/round_corners.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::RoundCorners) using namespace glaxnimate::math; QIcon glaxnimate::model::RoundCorners::static_tree_icon() { return QIcon::fromTheme("transform-affect-rounded-corners"); } QString glaxnimate::model::RoundCorners::static_type_name_human() { return i18n("Round Corners"); } bool glaxnimate::model::RoundCorners::process_collected() const { return false; } static std::pair get_vert_tan(const glaxnimate::math::bezier::Bezier& bezier, const QPointF& current_vertex, int closest_index, qreal round_distance) { // value from https://spencermortensen.com/articles/bezier-circle/ static const qreal tangent_length = 0.5519; if ( closest_index < 0 ) closest_index += bezier.size(); auto closest_vertex = bezier[closest_index].pos; auto distance = glaxnimate::math::length(current_vertex - closest_vertex); auto new_pos_perc = distance != 0 ? glaxnimate::math::min(distance/2, round_distance) / distance : 0; auto vertex = current_vertex + (closest_vertex - current_vertex) * new_pos_perc; auto tangent = - (vertex - current_vertex) * tangent_length; return {vertex, tangent}; } static glaxnimate::math::bezier::Bezier round_corners(const glaxnimate::math::bezier::Bezier& original, qreal round_distance) { glaxnimate::math::bezier::Bezier result; result.set_closed(original.closed()); for ( int i = 0; i < original.size(); i++ ) { if ( (!original.closed() && (i == 0 || i == original.size() - 1)) || !fuzzy_compare(original[i].tan_in, original[i].pos) || !fuzzy_compare(original[i].tan_out, original[i].pos) ) { result.push_back(original[i]); } else { QPointF vert1, vert2, out_t, in_t; std::tie(vert1, out_t) = get_vert_tan(original, original[i].pos, i - 1, round_distance); result.add_point(vert1, {0, 0}, out_t); std::tie(vert2, in_t) = get_vert_tan(original, original[i].pos, i + 1, round_distance); result.add_point(vert2, in_t, {0, 0}); } } return result; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::RoundCorners::process(glaxnimate::model::FrameTime t, const math::bezier::MultiBezier& mbez) const { qreal round_distance = radius.get_at(t); math::bezier::MultiBezier result; for ( const auto& inbez : mbez.beziers() ) result.beziers().push_back(round_corners(inbez, round_distance)); return result; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/asset.hpp000664 001750 001750 00000001473 15165022620 032664 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/model/document_node.hpp" #include "glaxnimate/model/assets/asset_base.hpp" #include "glaxnimate/model/property/reference_property.hpp" namespace glaxnimate::model { class Asset : public DocumentNode, public AssetBase { Q_OBJECT public: using DocumentNode::DocumentNode; Q_SIGNALS: void users_changed(); protected: int docnode_child_count() const override { return 0; } DocumentNode* docnode_child(int) const override { return nullptr; } int docnode_child_index(DocumentNode*) const override { return -1; } QIcon tree_icon() const override { return instance_icon(); } }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/structure_commands.cpp000664 001750 001750 00000003752 15165022620 034477 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/command/structure_commands.hpp" #include "glaxnimate/command/shape_commands.hpp" using namespace glaxnimate; command::DeferredCommandBase::~DeferredCommandBase() = default; bool command::DeferredCommandBase::has_action() const { return bool(d); } void command::DeferredCommandBase::redo() { if ( d ) d->redo(); } void command::DeferredCommandBase::undo() { if ( d ) d->undo(); } bool command::ReorderCommand::resolve_position(model::ShapeElement* shape, int& new_position) { if ( new_position < 0 ) { switch ( command::ReorderCommand::SpecialPosition(new_position) ) { case command::ReorderCommand::MoveUp: new_position = shape->position() + 1; break; case command::ReorderCommand::MoveDown: new_position = shape->position() - 1; break; case command::ReorderCommand::MoveTop: new_position = shape->owner()->size() - 1; break; case command::ReorderCommand::MoveBottom: new_position = 0; break; } } if ( new_position == shape->position() || new_position < 0 || new_position >= shape->owner()->size() ) return false; return true; } std::unique_ptr reorder_shape(model::ShapeElement* shape, int new_position) { if ( !command::ReorderCommand::resolve_position(shape, new_position) ) return {}; return std::make_unique(shape, shape->owner(), shape->owner(), new_position); } command::ReorderCommand::ReorderCommand(model::ShapeElement* shape, int new_position) : DeferredCommandBase(name(shape)) { d = reorder_shape(shape, new_position); } QString command::ReorderCommand::name(model::DocumentNode* node) { return i18n("Move %1", node->object_name()); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_parser.hpp000664 001750 001750 00000002643 15165022620 032524 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include "glaxnimate/io/mime/mime_serializer.hpp" #include "glaxnimate/io/base.hpp" namespace glaxnimate::model { class Composition; class Document; class DocumentNode; class Object; } // namespace glaxnimate::model namespace glaxnimate::io::svg { class SvgParser { public: // How to parse elements enum GroupMode { Groups, ///< As group shapes Layers, ///< As shape layers Inkscape, ///< Follow inkscape:groupmode }; /** * \throws SvgParseError on error */ SvgParser( QIODevice* device, GroupMode group_mode, model::Document* document, const std::function& on_warning = {}, ImportExport* io = nullptr, QSize forced_size = {}, model::FrameTime default_time = 180, QDir default_asset_path = {} ); ~SvgParser(); void parse_to_document(); io::mime::DeserializedData parse_to_objects(); class Private; private: std::unique_ptr d; }; /** * \brief Parses a CSS color string * \see https://www.w3.org/wiki/CSS/Properties/color */ QColor parse_color(const QString& color_str); } // namespace glaxnimate::io::svg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/base.hpp000664 001750 001750 00000013431 15165022620 030461 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/settings/settings_group.hpp" #include "glaxnimate/log/log_line.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/composition.hpp" namespace glaxnimate::io { class ImportExport : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name) Q_PROPERTY(QString slug READ slug) Q_PROPERTY(bool can_open READ can_open) Q_PROPERTY(bool can_save READ can_save) public: enum Direction { Import, Export, FrameExport, }; Q_ENUM(Direction) virtual ~ImportExport() = default; Q_INVOKABLE bool can_handle(glaxnimate::io::ImportExport::Direction direction) const { if ( direction == Import ) return can_open(); else if ( direction == Export ) return can_save(); else if ( direction == FrameExport ) return can_save_static(); return false; } Q_INVOKABLE bool can_handle_extension(const QString& extension, glaxnimate::io::ImportExport::Direction direction) const { return can_handle(direction) && extensions(direction).contains(extension); } Q_INVOKABLE bool can_handle_filename(const QString& filename, glaxnimate::io::ImportExport::Direction direction) const { return can_handle_extension(QFileInfo(filename).completeSuffix(), direction) || can_handle_extension(QFileInfo(filename).suffix(), direction); } /** * @pre @p setting_values contains all the settings correctly && can_open() */ bool open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& setting_values); /** * @pre @p setting_values contains all the settings correctly && can_save() * @param file File to write to * @param filename Filename for error reporting * @param comp Composition, for formats supporting multiple comps, use comp->document() * @param setting_values Values based on save_settings() */ bool save(QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& setting_values); /** * \brief Will save the first comp in the document */ bool save(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& setting_values); /** * @brief Saves a single frame * @pre can_save_static() * @param file File to write to * @param filename Filename for error reporting * @param comp Composition, for formats supporting multiple comps, use comp->document() * @param time Time at which to render * @param setting_values Values based on save_settings() * @return */ bool save_static(QIODevice& file, const QString& filename, model::Composition* comp, model::FrameTime time, const QVariantMap& setting_values); Q_INVOKABLE QByteArray save(glaxnimate::model::Composition* comp, const QVariantMap& setting_values={}, const QString& filename = "data"); Q_INVOKABLE QByteArray save_static(glaxnimate::model::Composition* comp, double time, const QVariantMap& setting_values={}, const QString& filename = "data"); Q_INVOKABLE bool load(glaxnimate::model::Document* document, const QByteArray& data, const QVariantMap& setting_values={}, const QString& filename = "data"); virtual QString name() const = 0; virtual QString slug() const = 0; Q_INVOKABLE virtual QStringList extensions(glaxnimate::io::ImportExport::Direction direction) const = 0; virtual std::unique_ptr open_settings() const { return {}; } virtual std::unique_ptr save_settings(model::Composition* ) const { return {}; } virtual bool can_open() const = 0; virtual bool can_save() const = 0; virtual bool can_save_static() const { return false; } /** * \brief Priority when multiple classes support the same file types */ virtual int priority() const { return 100; } /** * \brief File dialog name filter */ Q_INVOKABLE QString name_filter(Direction direction) const; Q_INVOKABLE void warning(const QString& message) { Q_EMIT this->message(message, log::Warning); } Q_INVOKABLE void information(const QString& message) { Q_EMIT this->message(message, log::Info); } Q_INVOKABLE void error(const QString& message) { Q_EMIT this->message(message, log::Error); } protected: virtual bool auto_open() const { return true; } virtual bool on_open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& setting_values) { Q_UNUSED(file); Q_UNUSED(filename); Q_UNUSED(document); Q_UNUSED(setting_values); return false; } virtual bool on_save( QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& setting_values) { Q_UNUSED(file); Q_UNUSED(filename); Q_UNUSED(comp); Q_UNUSED(setting_values); return false; } virtual bool on_save_static( QIODevice& file, const QString& filename, model::Composition* comp, model::FrameTime time, const QVariantMap& setting_values) { Q_UNUSED(file); Q_UNUSED(filename); Q_UNUSED(comp); Q_UNUSED(time); Q_UNUSED(setting_values); return false; } Q_SIGNALS: void message(const QString& message, log::Severity severity); void progress_max_changed(int max); void progress(int value); void completed(bool success); }; } // namespace glaxnimate::io src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/string_decoder.hpp000664 001750 001750 00000000560 15165022620 036624 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include namespace glaxnimate::io::aep { QString decode_string(const QByteArray& data); QString decode_utf16(const QByteArray& data, bool big_endian); } // namespace glaxnimate::io::aep src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_html_format.cpp000664 001750 001750 00000002437 15165022620 037372 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/rive/rive_html_format.hpp" #include "glaxnimate/module/extraformats/rive/rive_exporter.hpp" #include "glaxnimate/io/lottie/lottie_html_format.hpp" bool glaxnimate::io::rive::RiveHtmlFormat::on_save( QIODevice& file, const QString&, model::Composition* comp, const QVariantMap& ) { file.write(lottie::LottieHtmlFormat::html_head(this, comp, "" )); QBuffer buffer; buffer.open(QIODevice::WriteOnly); RiveExporter exp(&buffer, this); exp.write_document(comp->document()); file.write(QString(R"( )"); return true; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/shape_commands.cpp000664 001750 001750 00000025444 15165022620 033541 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/command/shape_commands.hpp" #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/command/undo_macro_guard.hpp" #include "glaxnimate/model/simple_visitor.hpp" using namespace glaxnimate; namespace { /** * \returns The parent node for \p shape */ model::VisualNode* shape_parent(model::ShapeElement* shape) { return static_cast(shape->owner()->object()); } /** * \returns The parent node for \p shape */ model::VisualNode* shape_parent(model::VisualNode* shape) { if ( auto se = qobject_cast(shape) ) return shape_parent(se); return nullptr; } /** * \brief Represents a sequence of nested nodes to reach */ struct PathToLayer { PathToLayer() = default; explicit PathToLayer(model::VisualNode* node) { composition = nullptr; while ( node && !composition ) { composition = qobject_cast(node); if ( composition ) break; if ( auto group = qobject_cast(node) ) { steps.push_back(group); node = shape_parent(group); } else { return; } } } std::vector steps; model::Composition* composition = nullptr; model::ShapeListProperty* lowest() const { if ( !steps.empty() ) return &steps.front()->shapes; return &composition->shapes; } model::ShapeListProperty* combine(const PathToLayer& other) { if ( other.composition != composition ) return nullptr; int i = 0; for ( int e = std::min(steps.size(), other.steps.size()); i < e; i++ ) if ( steps[i] != other.steps[i] ) break; if ( i < int(steps.size()) ) steps.erase(steps.begin()+i, steps.end()); return lowest(); } }; } // namespace command::GroupShapes::Data command::GroupShapes::collect_shapes(const std::vector& selection) { if ( selection.empty() ) return {}; Data data; PathToLayer collected; int i = 0; for ( ; i < int(selection.size()) && !data.parent; i++ ) { collected = PathToLayer(shape_parent(selection[i])); data.parent = collected.lowest(); } for ( ; i < int(selection.size()) && data.parent; i++ ) { data.parent = collected.combine(PathToLayer(shape_parent(selection[i]))); if ( !data.parent ) return {}; } data.elements.reserve(selection.size()); for ( auto n : selection ) data.elements.push_back(static_cast(n)); return data; } command::GroupShapes::GroupShapes(const command::GroupShapes::Data& data) : detail::RedoInCtor(i18n("Group Shapes")) { if ( data.parent ) { std::unique_ptr grp = std::make_unique(data.parent->object()->document()); group = grp.get(); data.parent->object()->document()->set_best_name(group); (new AddShape(data.parent, std::move(grp), data.parent->size(), this))->redo(); for ( int i = 0; i < int(data.elements.size()); i++ ) { (new MoveShape(data.elements[i], data.elements[i]->owner(), &group->shapes, i, this))->redo(); } } } void command::detail::RedoInCtor::redo() { if ( !did ) { QUndoCommand::redo(); did = true; } } void command::detail::RedoInCtor::undo() { QUndoCommand::undo(); did = false; } command::UngroupShapes::UngroupShapes(model::Group* group) : detail::RedoInCtor(i18n("Ungroup Shapes")) { int pos = group->owner()->index_of(group); (new RemoveShape(group, group->owner(), this))->redo(); for ( int i = 0, e = group->shapes.size(); i < e; i++ ) { (new MoveShape(group->shapes[0], group->shapes[0]->owner(), group->owner(), pos+i, this))->redo(); } } command::AddShape * command::duplicate_shape ( model::ShapeElement* shape ) { std::unique_ptr new_shape ( static_cast(shape->clone().release()) ); new_shape->refresh_uuid(); new_shape->recursive_rename(); new_shape->set_time(shape->docnode_parent()->time()); return new command::AddShape( shape->owner(), std::move(new_shape), shape->owner()->index_of(shape)+1, nullptr, i18n("Duplicate %1", shape->object_name()) ); } void command::convert_to_path(const std::vector& shapes, std::vector* out) { if ( shapes.empty() ) return; QString macro_name = i18n("Convert to path"); if ( shapes.size() == 1 ) macro_name = i18n("Convert %1 to path", (*shapes.begin())->name.get()); std::unordered_map converted_layers; model::Document* doc = shapes[0]->document(); command::UndoMacroGuard guard(macro_name, doc, false); for ( auto shape : shapes ) { auto path = shape->to_path(); if ( out ) out->push_back(path.get()); if ( path ) { if ( auto lay = shape->cast() ) converted_layers[lay] = static_cast(path.get()); guard.start(); doc->push_command( new command::AddObject( shape->owner(), std::move(path), shape->position() ) ); doc->push_command( new command::RemoveObject(shape, shape->owner()) ); } } // Maintain parenting of layers that have been converted for ( const auto& p : converted_layers ) { if ( auto src_parent = p.first->parent.get() ) { auto it = converted_layers.find(src_parent); if ( it != converted_layers.end() ) p.second->parent.set(it->second); } } } void command::recursive_reverse_path(model::DocumentNode* node) { command::UndoMacroGuard guard(i18n("Reverse path"), node->document()); if ( auto shape = qobject_cast(node) ) { shape->reversed.set_undoable(!shape->reversed.get()); } for ( const auto& child : node->docnode_children() ) recursive_reverse_path(child); } model::PreCompLayer* command::precompose( model::Composition* comp, const std::vector& objects, model::ObjectListProperty* layer_parent, int layer_index ) { if ( objects.empty() ) return nullptr; auto doc = comp->document(); command::UndoMacroGuard guard(i18n("Precompose"), doc); auto ucomp = std::make_unique(doc); model::Composition* new_comp = ucomp.get(); new_comp->width.set(comp->width.get()); new_comp->height.set(comp->height.get()); new_comp->fps.set(comp->fps.get()); new_comp->animation->first_frame.set(comp->animation->first_frame.get()); new_comp->animation->last_frame.set(comp->animation->last_frame.get()); if ( objects.size() > 1 || objects[0]->name.get().isEmpty() ) doc->set_best_name(new_comp); else new_comp->name.set(objects[0]->name.get()); doc->push_command(new command::AddObject(&doc->assets()->compositions->values, std::move(ucomp))); for ( auto node : objects ) { if ( auto shape = node->cast() ) doc->push_command(new command::MoveShape( shape, shape->owner(), &new_comp->shapes, new_comp->shapes.size() )); } auto pcl = std::make_unique(doc); pcl->composition.set(new_comp); pcl->size.set(new_comp->size()); doc->set_best_name(pcl.get()); auto pcl_ptr = pcl.get(); doc->push_command(new command::AddShape(layer_parent, std::move(pcl), layer_index)); return pcl_ptr; } void command::trim_end_time(model::VisualNode *node, model::FrameTime last_frame) { if ( auto comp = node->cast() ) { auto comp_last = comp->animation->last_frame.get(); if ( last_frame == comp_last ) return; command::UndoMacroGuard guard(i18n("Set End Time"), comp->document()); comp->animation->last_frame.set_undoable(last_frame); if ( last_frame < comp_last ) { // Trim model::simple_visit(comp, true, [last_frame](model::Layer* layer){ if ( layer->animation->last_frame.get() >= last_frame ) layer->animation->last_frame.set_undoable(last_frame); }); } else { // Expand model::simple_visit(comp, true, [last_frame, comp_last](model::Layer* layer){ if ( layer->animation->last_frame.get() >= comp_last ) layer->animation->last_frame.set_undoable(last_frame); }); } } else if ( auto layer = node->cast() ) { if ( last_frame == layer->animation->last_frame.get() ) return; layer->animation->last_frame.set_undoable(last_frame); } } void command::trim_start_time(model::VisualNode *node, model::FrameTime start_time) { if ( auto comp = node->cast() ) { auto comp_first = comp->animation->first_frame.get(); if ( comp_first == start_time ) return; command::UndoMacroGuard guard(i18n("Set Start Time"), comp->document()); comp->animation->first_frame.set_undoable(start_time); if ( start_time > comp_first ) { // Trim model::simple_visit(comp, true, [start_time](model::Layer* layer){ if ( layer->animation->first_frame.get() <= start_time ) layer->animation->first_frame.set_undoable(start_time); }); } else { // Expand model::simple_visit(comp, true, [start_time, comp_first](model::Layer* layer){ if ( layer->animation->first_frame.get() <= comp_first ) layer->animation->first_frame.set_undoable(start_time); }); } } else if ( auto layer = node->cast() ) { if ( start_time == layer->animation->first_frame.get() ) return; layer->animation->first_frame.set_undoable(start_time); } } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/group.hpp000664 001750 001750 00000003044 15165022620 035002 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/composable/composable.hpp" namespace glaxnimate::model { class Group : public StaticOverrides { GLAXNIMATE_OBJECT(Group) public: GLAXNIMATE_PROPERTY_LIST(ShapeElement, shapes) public: using Ctor::Ctor; int docnode_child_count() const override { return shapes.size(); } DocumentNode* docnode_child(int index) const override { return shapes[index]; } int docnode_child_index(DocumentNode* obj) const override { return shapes.index_of(static_cast(obj)); } static QIcon static_tree_icon() { return QIcon::fromTheme("object-group"); } static QString static_type_name_human() { return i18n("Group"); } void add_shapes(model::FrameTime t, math::bezier::MultiBezier & bez, const QTransform& transform) const override; QRectF local_bounding_rect(FrameTime t) const override; QTransform local_transform_matrix(model::FrameTime t) const override; glaxnimate::math::bezier::MultiBezier to_clip(model::FrameTime t) const override; std::unique_ptr to_path() const override; protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(model::FrameTime t) const override; void on_graphics_changed() override; void on_composition_changed(model::Composition* old_comp, model::Composition* new_comp) override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/000775 001750 001750 00000000000 15165022620 027525 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/path.cpp000664 001750 001750 00000000344 15165022620 033734 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/shapes/path.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Path) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/animate_parser.hpp000664 001750 001750 00000035115 15165022620 033343 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/model/animation/keyframe_transition.hpp" #include "glaxnimate/model/animation/frame_time.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" #include "glaxnimate/math/bezier/bezier.hpp" #include "glaxnimate/io/svg/path_parser.hpp" #include "glaxnimate/io/svg/detail.hpp" #include "glaxnimate/io/animated_properties.hpp" namespace glaxnimate::io::svg { QColor parse_color(const QString& string); } // namespace glaxnimate::io::svg namespace glaxnimate::io::svg::detail { using namespace glaxnimate::io::detail; class AnimateParser { public: struct AnimatedProperties : public glaxnimate::io::detail::AnimatedProperties { QDomElement element; bool apply_motion(model::AnimatedProperty& prop, const QPointF& delta_pos = {}, model::Property* auto_orient = nullptr) const { auto motion = properties.find("motion"); if ( motion == properties.end() ) return false; if ( auto_orient ) auto_orient->set(motion->second.auto_orient); for ( const auto& kf : motion->second.keyframes ) prop.set_keyframe(kf.time, QPointF())->set_transition(kf.transition); if ( qFuzzyIsNull(math::length(delta_pos)) ) { prop.set_bezier(motion->second.motion); } else { math::bezier::Bezier bez = motion->second.motion; for ( auto& p : bez ) p.translate(delta_pos); prop.set_bezier(bez); } return true; } bool prepare_joined(std::vector& props) const override { for ( auto& p : props ) { if ( p.prop.index() == 1 ) { if ( !element.hasAttribute(*p.get<1>()) ) return false; p.prop = split_values(element.attribute(*p.get<1>())); } } return true; } }; static std::vector split_values(const QString& v) { if ( !v.contains(separator) ) { bool ok = false; qreal val = v.toDouble(&ok); if ( ok ) return {val}; QColor c(v); if ( c.isValid() ) return {c.redF(), c.greenF(), c.blueF(), c.alphaF()}; return {}; } auto split = QStringView{v}.split(separator, Qt::SkipEmptyParts ); std::vector values; values.reserve(split.size()); for ( const auto& r : split ) values.push_back(r.toDouble()); return values; } model::FrameTime clock_to_frame(const QString& clock) { auto match = clock_re.match(clock, 0, QRegularExpression::PartialPreferCompleteMatch); if ( !match.hasMatch() ) return 0; static constexpr const qreal minutes = 60; static constexpr const qreal hours = 60*60; static const std::map units = { {"ms", 0.001}, {"s", 1.0}, {"min", minutes}, {"h", hours} }; if ( !match.captured("unit").isEmpty() ) return match.captured("timecount").toDouble() * units.at(match.captured("unit")) * fps; return ( match.captured("hours").toDouble() * hours + match.captured("minutes").toDouble() * minutes + match.captured("seconds").toDouble() ) * fps; } ValueVariant parse_value(const QString& str, ValueVariant::Type type) const { switch ( type ) { case ValueVariant::Vector: return split_values(str); case ValueVariant::Bezier: return PathDParser(str).parse(); case ValueVariant::String: return str; case ValueVariant::Color: return parse_color(str); } return {}; } std::vector get_values(const QDomElement& animate) { QString attr = animate.attribute("attributeName"); ValueVariant::Type type = ValueVariant::Vector; if ( attr == "d" ) type = ValueVariant::Bezier; else if ( attr == "display" ) type = ValueVariant::String; else if ( attr == "fill" || attr == "stroke" || attr == "stop-color" ) type = ValueVariant::Color; std::vector values; if ( animate.hasAttribute("values") ) { auto val_str = animate.attribute("values").split(frame_separator_re, Qt::SkipEmptyParts); values.reserve(val_str.size()); for ( const auto& val : val_str ) values.push_back(parse_value(val, type)); if ( values.size() < 2 ) { warning("Not enough values in `animate`"); return {}; } for ( uint i = 1; i < values.size(); i++ ) { if ( !values[i].compatible(values[0]) ) { warning("Mismatching `values` in `animate`"); return {}; } } } else { if ( animate.hasAttribute("from") ) { values.push_back(parse_value(animate.attribute("from"), type)); } else { if ( attr == "transform" ) { warning("You need to set `values` or `from` in `animateTransform`"); return {}; } else if ( !animate.hasAttribute(attr) ) { warning("Missing `from` in `animate`"); return {}; } values.push_back(parse_value(animate.attribute(attr), type)); } if ( animate.hasAttribute("to") ) { values.push_back(parse_value(animate.attribute("to"), type)); } else if ( type == ValueVariant::Vector && animate.hasAttribute("by") ) { auto by = split_values(animate.attribute("to")); if ( by.size() != values[0].vector().size() ) { warning("Mismatching `by` and `from` in `animate`"); return {}; } for ( uint i = 0; i < by.size(); i++ ) by[i] += values[0].vector()[i]; values.push_back(std::move(by)); } else { warning("Missing `to` or `by` in `animate`"); return {}; } } if ( type == ValueVariant::Bezier ) { for ( const auto& v : values ) { if ( v.bezier().size() != 1 ) { warning("Can only load animated `d` if each keyframe has exactly 1 path"); return {}; } } } return values; } void register_time_range(model::FrameTime start_time, model::FrameTime end_time) { if ( !kf_range_initialized ) { kf_range_initialized = true; min_kf = start_time; max_kf = end_time; } else { if ( min_kf > start_time ) min_kf = start_time; if ( max_kf < end_time ) max_kf = end_time; } } void parse_animate(const QDomElement& animate, AnimatedProperty& prop, bool motion) { if ( !prop.keyframes.empty() ) { warning("Multiple `animate` for the same property"); return; } model::FrameTime start_time = 0; model::FrameTime end_time = 0; if ( animate.hasAttribute("begin") ) start_time = clock_to_frame(animate.attribute("begin")); if ( animate.hasAttribute("dur") ) end_time = start_time + clock_to_frame(animate.attribute("dur")); else if ( animate.hasAttribute("end") ) end_time = clock_to_frame(animate.attribute("end")); if ( start_time >= end_time ) { warning("Invalid timings in `animate`"); return; } register_time_range(start_time, end_time); std::vector values; if ( motion ) { if ( !animate.hasAttribute("path") ) { warning("Missing path for animateMotion"); return; } if ( animate.hasAttribute("rotate") ) { QString rotate = animate.attribute("rotate"); if ( rotate == "auto" ) { prop.auto_orient = true; } else { bool is_number = false; int degrees = rotate.toInt(&is_number) % 360; if ( !is_number || degrees != 0 ) { warning("The only supported values for animateMotion.rotate are auto or 0"); prop.auto_orient = rotate == "auto-reverse"; } } } auto mbez = PathDParser(animate.attribute("path")).parse(); /// \todo Automatically add Hold transitions for sub-bezier end points for ( const auto& b : mbez.beziers() ) { for ( const auto& p : b ) { values.push_back(std::vector{p.pos.x(), p.pos.y()}); prop.motion.push_back(p); } } /// \todo keyPoints } else { values = get_values(animate); } if ( values.empty() ) return; std::vector times; times.reserve(values.size()); if ( animate.hasAttribute("keyTimes") ) { auto strings = animate.attribute("keyTimes").split(frame_separator_re, Qt::SkipEmptyParts); if ( strings.size() != int(values.size()) ) { warning(QString("`keyTimes` (%1) and `values` (%2) mismatch").arg(strings.size()).arg(int(values.size()))); return; } for ( const auto& s : strings ) times.push_back(math::lerp(start_time, end_time, s.toDouble())); } else { for ( qreal i = 0; i < values.size(); i++ ) times.push_back(math::lerp(start_time, end_time, i/(values.size()-1))); } std::vector transitions; QString calc = animate.attribute("calcMode", "linear"); if ( calc == "spline" ) { if ( !animate.hasAttribute("keySplines") ) { warning("Missing `keySplines`"); return; } auto splines = animate.attribute("keySplines").split(frame_separator_re, Qt::SkipEmptyParts); if ( splines.size() != int(values.size()) - 1 ) { warning("Wrong number of `keySplines` values"); return; } transitions.reserve(values.size()); for ( const auto& spline : splines ) { auto params = split_values(spline); if ( params.size() != 4 ) { warning("Invalid value for `keySplines`"); return; } transitions.push_back({{params[0], params[1]}, {params[2], params[3]}}); } transitions.emplace_back(); } else { model::KeyframeTransition def; if ( calc == "discrete" ) def.set_hold(true); transitions = std::vector(values.size(), def); } prop.keyframes.reserve(values.size()); for ( uint i = 0; i < values.size(); i++ ) prop.keyframes.push_back({times[i], values[i], transitions[i]}); } void store_animate(const QString& target, const QDomElement& animate) { stored_animate[target].push_back(animate); } template AnimatedProperties parse_animated_elements(const QDomElement& parent, const FuncT& func) { AnimatedProperties props; props.element = parent; for ( const auto& child: ElementRange(parent) ) func(child, props); if ( parent.hasAttribute("id") ) { auto it = stored_animate.find(parent.attribute("id")); if ( it != stored_animate.end() ) { for ( const auto& child: it->second ) func(child, props); } } return props; } AnimatedProperties parse_animated_properties(const QDomElement& parent) { return parse_animated_elements(parent, [this](const QDomElement& child, AnimatedProperties& props){ if ( child.tagName() == "animate" && child.hasAttribute("attributeName") ) parse_animate(child, props.properties[child.attribute("attributeName")], false); else if ( child.tagName() == "animateMotion" ) parse_animate(child, props.properties["motion"], true); }); } AnimatedProperties parse_animated_transform(const QDomElement& parent) { return parse_animated_elements(parent, [this](const QDomElement& child, AnimatedProperties& props){ if ( child.tagName() == "animateTransform" && child.hasAttribute("type") && child.attribute("attributeName") == "transform" ) parse_animate(child, props.properties[child.attribute("type")], false); else if ( child.tagName() == "animateMotion" ) parse_animate(child, props.properties["motion"], true); }); } void warning(const QString& msg) { if ( on_warning ) on_warning(msg); } qreal fps = 60; qreal min_kf = 0; qreal max_kf = 0; bool kf_range_initialized = false; std::function on_warning; std::unordered_map> stored_animate; static const QRegularExpression separator; static const QRegularExpression clock_re; static const QRegularExpression frame_separator_re; }; } // namespace glaxnimate::io::svg::detail src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/object_list_property.hpp000664 001750 001750 00000023620 15165022620 036313 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/property/property.hpp" #include "glaxnimate/model/document_node.hpp" #define GLAXNIMATE_PROPERTY_LIST_IMPL(type, name) \ public: \ QVariantList get_##name() const \ { \ QVariantList ret; \ for ( const auto & ptr : name ) \ ret.push_back(QVariant::fromValue(ptr.get())); \ return ret; \ } \ private: \ Q_PROPERTY(QVariantList name READ get_##name) \ Q_CLASSINFO(#name, "property list " #type) \ // macro end #define GLAXNIMATE_PROPERTY_LIST(type, name) \ public: \ ObjectListProperty name{this, kli18n(#name)}; \ GLAXNIMATE_PROPERTY_LIST_IMPL(type, name) \ // macro end namespace glaxnimate::model { class ObjectListPropertyBase : public BaseProperty { public: ObjectListPropertyBase(Object* obj, const utils::LazyLocalizedString& name) : BaseProperty(obj, name, {PropertyTraits::Object, PropertyTraits::List|PropertyTraits::Visual}) {} /** * \brief Inserts a clone of the passed object * \return The internal object or \b nullptr in case of failure */ virtual Object* insert_clone(Object* object, int index = -1) = 0; bool set_value(const QVariant& val) override { if ( !val.canConvert() ) return false; for ( const auto& v : val.toList() ) { if ( !v.canConvert() ) continue; insert_clone(v.value()); } return true; } bool valid_value(const QVariant& val) const override { return val.canConvert(); } virtual std::vector valid_reference_values(bool allow_null) const = 0; virtual bool is_valid_reference_value(model::DocumentNode *, bool allow_null) const = 0; protected: void object_removed(model::DocumentNode* ptr) { ptr->removed_from_list(); } void object_added(model::DocumentNode* ptr) { ptr->added_to_list(static_cast(object())); } }; namespace detail { template class ObjectListProperty : public ObjectListPropertyBase { public: using value_type = Type; using pointer = std::unique_ptr; using reference = Type&; // using const_reference = const Type&; using iterator = typename std::vector::const_iterator; /** * \brief Utility to perform raw operations on the list of objects * \warning Use with care, this won't invoke any callbacks */ class Raw { public: using iterator = typename std::vector::iterator; using move_iterator = std::move_iterator; void clear() { subject->objects.clear(); } iterator begin() { return subject->objects.begin(); } iterator end() { return subject->objects.end(); } move_iterator move_begin() { return move_iterator(begin()); } move_iterator move_end() { return move_iterator(end()); } private: friend ObjectListProperty; Raw(ObjectListProperty* subject) : subject(subject) {} ObjectListProperty* subject; }; ObjectListProperty( Object* obj, const utils::LazyLocalizedString& name, PropertyCallback callback_insert = &DocumentNode::docnode_child_add_end, PropertyCallback callback_remove = &DocumentNode::docnode_child_remove_end, PropertyCallback callback_insert_begin = &DocumentNode::docnode_child_add_begin, PropertyCallback callback_remove_begin = &DocumentNode::docnode_child_remove_begin, PropertyCallback callback_move_begin = &DocumentNode::docnode_child_move_begin, PropertyCallback callback_move_end = &DocumentNode::docnode_child_move_end ) : ObjectListPropertyBase(obj, name), callback_insert(std::move(callback_insert)), callback_remove(std::move(callback_remove)), callback_insert_begin(std::move(callback_insert_begin)), callback_remove_begin(std::move(callback_remove_begin)), callback_move_begin(std::move(callback_move_begin)), callback_move_end(std::move(callback_move_end)) {} value_type* operator[](int i) const { return objects[i].get(); } int size() const { return objects.size(); } bool empty() const { return objects.empty(); } iterator begin() const { return objects.begin(); } iterator end() const { return objects.end(); } value_type* back() const { return objects.back().get(); } value_type* emplace(value_type* p, int position = -1) { return insert(std::unique_ptr(p), position); } value_type* insert(pointer p, int position = -1) { if ( !valid_index(position) ) position = size(); callback_insert_begin(this->object(), position); auto ptr = p.get(); objects.insert(objects.begin()+position, std::move(p)); ptr->set_time(object()->time()); object_added(ptr); on_insert(position); callback_insert(this->object(), ptr, position); value_changed(); return ptr; } bool valid_index(int index) { return index >= 0 && index < int(objects.size()); } pointer remove(int index) { if ( !valid_index(index) ) return {}; callback_remove_begin(object(), index); auto it = objects.begin() + index; auto v = std::move(*it); objects.erase(it); object_removed(v.get()); on_remove(index); callback_remove(object(), v.get(), index); value_changed(); return v; } void move(int index_a, int index_b) { if ( index_b >= size() ) index_b = size() - 1; if ( !valid_index(index_a) || !valid_index(index_b) || index_a == index_b ) return; callback_move_begin(this->object(), index_a, index_b); auto moved = std::move(objects[index_a]); if ( index_a < index_b ) std::move(objects.begin() + index_a + 1, objects.begin() + index_b + 1, objects.begin() + index_a); else std::move_backward(objects.begin() + index_b, objects.begin() + index_a, objects.begin() + index_a + 1); objects[index_b] = std::move(moved); on_move(index_a, index_b); callback_move_end(this->object(), objects[index_b].get(), index_a, index_b); value_changed(); } QVariant value() const override { QVariantList list; for ( const auto& p : objects ) list.append(QVariant::fromValue((Object*)p.get())); return list; } Object* insert_clone(Object* object, int index = -1) override { if ( !object ) return nullptr; auto basep = object->clone(); Type* casted = qobject_cast(basep.get()); if ( casted ) { basep.release(); insert(pointer(casted), index); return casted; } return nullptr; } void set_time(FrameTime t) override { for ( const auto& o : objects ) o->set_time(t); } int index_of(value_type* obj, int not_found = -1) const { for ( int i = 0; i < size(); i++ ) if ( objects[i].get() == obj ) return i; return not_found; } /** * \brief Allows to perform raw operations on the elements * \warning Use with care, no callbacks will be invoked */ Raw raw() { return Raw{this}; } void transfer(Document* doc) override { for ( const auto& obj : objects ) obj->transfer(doc); } std::vector valid_reference_values(bool allow_null) const override { std::vector res; if ( allow_null ) { res.reserve(objects.size() + 1); res.push_back(nullptr); } else { res.reserve(objects.size()); } for ( const auto& c : objects ) res.push_back(c.get()); return res; } bool is_valid_reference_value(model::DocumentNode * value, bool allow_null) const override { if ( !value ) return allow_null; for ( const auto& c : objects ) if ( c.get() == value ) return true; return false; } void stretch_time(qreal multiplier) override { for ( const auto& object : objects ) object->stretch_time(multiplier); } protected: virtual void on_insert(int index) { Q_UNUSED(index); } virtual void on_remove(int index) { Q_UNUSED(index); } virtual void on_move(int index_a, int index_b) { Q_UNUSED(index_a); Q_UNUSED(index_b); } std::vector objects; PropertyCallback callback_insert; PropertyCallback callback_remove; PropertyCallback callback_insert_begin; PropertyCallback callback_remove_begin; PropertyCallback callback_move_begin; PropertyCallback callback_move_end; }; } // namespace detail template class ObjectListProperty : public detail::ObjectListProperty { public: using detail::ObjectListProperty::ObjectListProperty; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/log/listener_stderr.hpp000664 001750 001750 00000001306 15165022620 033127 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/log/logger.hpp" namespace glaxnimate::log { class ListenerStderr: public LogListener { protected: void on_line(const LogLine& line) override { QMessageLogger logger; QDebug stream = logger.warning(); if ( line.severity == Info ) stream = logger.info(); stream << line.time.toString(Qt::ISODate) << Logger::severity_name(line.severity) << line.source << line.source_detail << line.message ; } }; } // namespace glaxnimate::log mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/object.hpp000664 001750 001750 00000011714 15165022620 031510 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include "glaxnimate/utils/i18n.hpp" #include "glaxnimate/model/animation/frame_time.hpp" #include "glaxnimate/model/factory.hpp" class QUndoCommand; /** * \brief Sets up declarations of concrete Object sub-classes * \note Call GLAXNIMATE_OBJECT_IMPL for every class declared with GLAXNIMATE_OBJECT */ #define GLAXNIMATE_OBJECT(cls) \ private: \ Q_OBJECT \ static bool _reg; \ std::unique_ptr clone_impl() const override; \ public: \ std::unique_ptr clone_covariant() const; \ // macro end /** * \brief Registers a class declared with GLAXNIMATE_OBJECT to be constructed * with model::Factory */ #define GLAXNIMATE_OBJECT_IMPL(cls) \ bool cls::_reg{glaxnimate::model::Factory::instance().register_type()}; \ std::unique_ptr cls::clone_covariant() const \ { \ auto object = std::make_unique(this->document()); \ this->clone_into(object.get()); \ return object; \ } \ std::unique_ptr cls::clone_impl() const \ { \ return clone_covariant(); \ } \ // macro end namespace glaxnimate::model { class BaseProperty; class Document; class MetaAnimatable; class Object : public QObject { Q_OBJECT Q_PROPERTY(QString object_name READ object_name) Q_PROPERTY(QString type_name_human READ type_name_human) Q_PROPERTY(QString type_name READ type_name) Q_PROPERTY(double time READ time) public: explicit Object(Document* document); ~Object(); std::unique_ptr clone() const { return clone_impl(); } std::unique_ptr clone_covariant() const { auto object = std::make_unique(document()); clone_into(object.get()); return object; } virtual void assign_from(const Object* other); Q_INVOKABLE QVariant get(const QString& property) const; bool set(const QString& property, const QVariant& value); bool set_undoable(const QString& property, const QVariant& value); Q_INVOKABLE bool has(const QString& property) const; const std::vector& properties() const; BaseProperty* get_property(const QString& property); virtual QString object_name() const { return type_name_human(); } virtual QString type_name_human() const { return i18n("Unknown Object"); } virtual void set_time(FrameTime t); FrameTime time() const; QString type_name() const; Document* document() const; void transfer(Document* document); void push_command(QUndoCommand* cmd); template T* cast() { return qobject_cast(this); } template const T* cast() const { return qobject_cast(this); } template bool is_instance() const { return metaObject()->inherits(&T::staticMetaObject); } virtual void stretch_time(qreal multiplier); virtual QIcon tree_icon() const { return {}; } MetaAnimatable& grouped_animations(); // Used because emscripten bindings is very restrictive MetaAnimatable* grouped_animations_ptr() const; Q_SIGNALS: void property_changed(const model::BaseProperty* prop, const QVariant& value); void visual_property_changed(const model::BaseProperty* prop, const QVariant& value); void removed(); protected: virtual void on_property_changed(const BaseProperty* prop, const QVariant& value) { Q_UNUSED(prop); Q_UNUSED(value); } void clone_into(Object* dest) const; virtual void on_transfer(model::Document* doc) {Q_UNUSED(doc)}; class Autoreg { public: Autoreg(const QMetaObject&); Autoreg(const Autoreg&) = delete; Autoreg& operator=(const Autoreg&) = delete; }; private: virtual std::unique_ptr clone_impl() const { return clone_covariant(); } void add_property(BaseProperty* prop); void property_value_changed(const BaseProperty* prop, const QVariant& value); friend BaseProperty; class Private; std::unique_ptr d; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/cairo_module.hpp000664 001750 001750 00000000727 15165022620 034170 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/module.hpp" namespace glaxnimate::cairo { class Module : public module::Module { public: Module() : module::Module(i18n("Cairo Renderer")) {} std::vector components() const override; protected: void initialize() override; }; } // namespace glaxnimate::video mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/000775 001750 001750 00000000000 15165022620 032771 5ustar00ddennedyddennedy000000 000000 src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/type_def.cpp000664 001750 001750 00000072356 15165022620 035637 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ /** * NOTE: This file is generated automatically, do not edit manually * To generate this file run * ./external/rive_typedef.py -t source >src/core/io/rive/type_def.cpp */ #include "glaxnimate/module/extraformats/rive/type_def.hpp" using namespace glaxnimate::io::rive; std::unordered_map glaxnimate::io::rive::defined_objects = { { TypeId::Artboard, { "Artboard", TypeId::Artboard, TypeId::WorldTransformComponent, { {"clip", 196, PropertyType::Bool}, {"width", 7, PropertyType::Float}, {"height", 8, PropertyType::Float}, {"x", 9, PropertyType::Float}, {"y", 10, PropertyType::Float}, {"originX", 11, PropertyType::Float}, {"originY", 12, PropertyType::Float}, {"defaultStateMachineId", 236, PropertyType::VarUint}, } } }, { TypeId::Node, { "Node", TypeId::Node, TypeId::TransformComponent, { {"x", 13, PropertyType::Float}, {"y", 14, PropertyType::Float}, } } }, { TypeId::Shape, { "Shape", TypeId::Shape, TypeId::Drawable, { } } }, { TypeId::Ellipse, { "Ellipse", TypeId::Ellipse, TypeId::ParametricPath, { } } }, { TypeId::StraightVertex, { "StraightVertex", TypeId::StraightVertex, TypeId::PathVertex, { {"radius", 26, PropertyType::Float}, } } }, { TypeId::CubicDetachedVertex, { "CubicDetachedVertex", TypeId::CubicDetachedVertex, TypeId::CubicVertex, { {"inRotation", 84, PropertyType::Float}, {"inDistance", 85, PropertyType::Float}, {"outRotation", 86, PropertyType::Float}, {"outDistance", 87, PropertyType::Float}, } } }, { TypeId::Rectangle, { "Rectangle", TypeId::Rectangle, TypeId::ParametricPath, { {"linkCornerRadius", 164, PropertyType::Bool}, {"cornerRadiusTL", 31, PropertyType::Float}, {"cornerRadiusTR", 161, PropertyType::Float}, {"cornerRadiusBL", 162, PropertyType::Float}, {"cornerRadiusBR", 163, PropertyType::Float}, } } }, { TypeId::Triangle, { "Triangle", TypeId::Triangle, TypeId::ParametricPath, { } } }, { TypeId::Component, { "Component", TypeId::Component, TypeId::NoType, { {"name", 4, PropertyType::String}, {"parentId", 5, PropertyType::VarUint}, } } }, { TypeId::ContainerComponent, { "ContainerComponent", TypeId::ContainerComponent, TypeId::Component, { } } }, { TypeId::Path, { "Path", TypeId::Path, TypeId::Node, { {"pathFlags", 128, PropertyType::VarUint}, } } }, { TypeId::Drawable, { "Drawable", TypeId::Drawable, TypeId::Node, { {"blendModeValue", 23, PropertyType::VarUint}, {"drawableFlags", 129, PropertyType::VarUint}, } } }, { TypeId::PathVertex, { "PathVertex", TypeId::PathVertex, TypeId::Vertex, { } } }, { TypeId::ParametricPath, { "ParametricPath", TypeId::ParametricPath, TypeId::Path, { {"width", 20, PropertyType::Float}, {"height", 21, PropertyType::Float}, {"originX", 123, PropertyType::Float}, {"originY", 124, PropertyType::Float}, } } }, { TypeId::PointsPath, { "PointsPath", TypeId::PointsPath, TypeId::Path, { {"isClosed", 32, PropertyType::Bool}, } } }, { TypeId::RadialGradient, { "RadialGradient", TypeId::RadialGradient, TypeId::LinearGradient, { } } }, { TypeId::SolidColor, { "SolidColor", TypeId::SolidColor, TypeId::Component, { {"colorValue", 37, PropertyType::Color}, } } }, { TypeId::GradientStop, { "GradientStop", TypeId::GradientStop, TypeId::Component, { {"colorValue", 38, PropertyType::Color}, {"position", 39, PropertyType::Float}, } } }, { TypeId::Fill, { "Fill", TypeId::Fill, TypeId::ShapePaint, { {"fillRule", 40, PropertyType::VarUint}, } } }, { TypeId::ShapePaint, { "ShapePaint", TypeId::ShapePaint, TypeId::ContainerComponent, { {"isVisible", 41, PropertyType::Bool}, } } }, { TypeId::LinearGradient, { "LinearGradient", TypeId::LinearGradient, TypeId::ContainerComponent, { {"startX", 42, PropertyType::Float}, {"startY", 33, PropertyType::Float}, {"endX", 34, PropertyType::Float}, {"endY", 35, PropertyType::Float}, {"opacity", 46, PropertyType::Float}, } } }, { TypeId::Backboard, { "Backboard", TypeId::Backboard, TypeId::NoType, { } } }, { TypeId::Stroke, { "Stroke", TypeId::Stroke, TypeId::ShapePaint, { {"thickness", 47, PropertyType::Float}, {"cap", 48, PropertyType::VarUint}, {"join", 49, PropertyType::VarUint}, {"transformAffectsStroke", 50, PropertyType::Bool}, } } }, { TypeId::KeyedObject, { "KeyedObject", TypeId::KeyedObject, TypeId::NoType, { {"objectId", 51, PropertyType::VarUint}, } } }, { TypeId::KeyedProperty, { "KeyedProperty", TypeId::KeyedProperty, TypeId::NoType, { {"propertyKey", 53, PropertyType::VarUint}, } } }, { TypeId::Animation, { "Animation", TypeId::Animation, TypeId::NoType, { {"name", 55, PropertyType::String}, } } }, { TypeId::CubicInterpolator, { "CubicInterpolator", TypeId::CubicInterpolator, TypeId::NoType, { {"x1", 63, PropertyType::Float}, {"y1", 64, PropertyType::Float}, {"x2", 65, PropertyType::Float}, {"y2", 66, PropertyType::Float}, } } }, { TypeId::KeyFrame, { "KeyFrame", TypeId::KeyFrame, TypeId::NoType, { {"frame", 67, PropertyType::VarUint}, {"interpolationType", 68, PropertyType::VarUint}, {"interpolatorId", 69, PropertyType::VarUint}, } } }, { TypeId::KeyFrameDouble, { "KeyFrameDouble", TypeId::KeyFrameDouble, TypeId::KeyFrame, { {"value", 70, PropertyType::Float}, } } }, { TypeId::LinearAnimation, { "LinearAnimation", TypeId::LinearAnimation, TypeId::Animation, { {"fps", 56, PropertyType::VarUint}, {"duration", 57, PropertyType::VarUint}, {"speed", 58, PropertyType::Float}, {"loopValue", 59, PropertyType::VarUint}, {"workStart", 60, PropertyType::VarUint}, {"workEnd", 61, PropertyType::VarUint}, {"enableWorkArea", 62, PropertyType::Bool}, } } }, { TypeId::CubicAsymmetricVertex, { "CubicAsymmetricVertex", TypeId::CubicAsymmetricVertex, TypeId::CubicVertex, { {"rotation", 79, PropertyType::Float}, {"inDistance", 80, PropertyType::Float}, {"outDistance", 81, PropertyType::Float}, } } }, { TypeId::CubicMirroredVertex, { "CubicMirroredVertex", TypeId::CubicMirroredVertex, TypeId::CubicVertex, { {"rotation", 82, PropertyType::Float}, {"distance", 83, PropertyType::Float}, } } }, { TypeId::CubicVertex, { "CubicVertex", TypeId::CubicVertex, TypeId::PathVertex, { } } }, { TypeId::KeyFrameColor, { "KeyFrameColor", TypeId::KeyFrameColor, TypeId::KeyFrame, { {"value", 88, PropertyType::Color}, } } }, { TypeId::TransformComponent, { "TransformComponent", TypeId::TransformComponent, TypeId::WorldTransformComponent, { {"rotation", 15, PropertyType::Float}, {"scaleX", 16, PropertyType::Float}, {"scaleY", 17, PropertyType::Float}, } } }, { TypeId::SkeletalComponent, { "SkeletalComponent", TypeId::SkeletalComponent, TypeId::TransformComponent, { } } }, { TypeId::Bone, { "Bone", TypeId::Bone, TypeId::SkeletalComponent, { {"length", 89, PropertyType::Float}, } } }, { TypeId::RootBone, { "RootBone", TypeId::RootBone, TypeId::Bone, { {"x", 90, PropertyType::Float}, {"y", 91, PropertyType::Float}, } } }, { TypeId::ClippingShape, { "ClippingShape", TypeId::ClippingShape, TypeId::Component, { {"sourceId", 92, PropertyType::VarUint}, {"fillRule", 93, PropertyType::VarUint}, {"isVisible", 94, PropertyType::Bool}, } } }, { TypeId::Skin, { "Skin", TypeId::Skin, TypeId::ContainerComponent, { {"xx", 104, PropertyType::Float}, {"yx", 105, PropertyType::Float}, {"xy", 106, PropertyType::Float}, {"yy", 107, PropertyType::Float}, {"tx", 108, PropertyType::Float}, {"ty", 109, PropertyType::Float}, } } }, { TypeId::Tendon, { "Tendon", TypeId::Tendon, TypeId::Component, { {"boneId", 95, PropertyType::VarUint}, {"xx", 96, PropertyType::Float}, {"yx", 97, PropertyType::Float}, {"xy", 98, PropertyType::Float}, {"yy", 99, PropertyType::Float}, {"tx", 100, PropertyType::Float}, {"ty", 101, PropertyType::Float}, } } }, { TypeId::Weight, { "Weight", TypeId::Weight, TypeId::Component, { {"values", 102, PropertyType::VarUint}, {"indices", 103, PropertyType::VarUint}, } } }, { TypeId::CubicWeight, { "CubicWeight", TypeId::CubicWeight, TypeId::Weight, { {"inValues", 110, PropertyType::VarUint}, {"inIndices", 111, PropertyType::VarUint}, {"outValues", 112, PropertyType::VarUint}, {"outIndices", 113, PropertyType::VarUint}, } } }, { TypeId::TrimPath, { "TrimPath", TypeId::TrimPath, TypeId::Component, { {"start", 114, PropertyType::Float}, {"end", 115, PropertyType::Float}, {"offset", 116, PropertyType::Float}, {"modeValue", 117, PropertyType::VarUint}, } } }, { TypeId::DrawTarget, { "DrawTarget", TypeId::DrawTarget, TypeId::Component, { {"drawableId", 119, PropertyType::VarUint}, {"placementValue", 120, PropertyType::VarUint}, } } }, { TypeId::DrawRules, { "DrawRules", TypeId::DrawRules, TypeId::ContainerComponent, { {"drawTargetId", 121, PropertyType::VarUint}, } } }, { TypeId::KeyFrameId, { "KeyFrameId", TypeId::KeyFrameId, TypeId::KeyFrame, { {"value", 122, PropertyType::VarUint}, } } }, { TypeId::Polygon, { "Polygon", TypeId::Polygon, TypeId::ParametricPath, { {"points", 125, PropertyType::VarUint}, {"cornerRadius", 126, PropertyType::Float}, } } }, { TypeId::Star, { "Star", TypeId::Star, TypeId::Polygon, { {"innerRadius", 127, PropertyType::Float}, } } }, { TypeId::StateMachine, { "StateMachine", TypeId::StateMachine, TypeId::Animation, { } } }, { TypeId::StateMachineComponent, { "StateMachineComponent", TypeId::StateMachineComponent, TypeId::NoType, { {"name", 138, PropertyType::String}, } } }, { TypeId::StateMachineInput, { "StateMachineInput", TypeId::StateMachineInput, TypeId::StateMachineComponent, { } } }, { TypeId::StateMachineNumber, { "StateMachineNumber", TypeId::StateMachineNumber, TypeId::StateMachineInput, { {"value", 140, PropertyType::Float}, } } }, { TypeId::StateMachineLayer, { "StateMachineLayer", TypeId::StateMachineLayer, TypeId::StateMachineComponent, { } } }, { TypeId::StateMachineTrigger, { "StateMachineTrigger", TypeId::StateMachineTrigger, TypeId::StateMachineInput, { } } }, { TypeId::StateMachineBool, { "StateMachineBool", TypeId::StateMachineBool, TypeId::StateMachineInput, { {"value", 141, PropertyType::Bool}, } } }, { TypeId::LayerState, { "LayerState", TypeId::LayerState, TypeId::StateMachineLayerComponent, { } } }, { TypeId::AnimationState, { "AnimationState", TypeId::AnimationState, TypeId::LayerState, { {"animationId", 149, PropertyType::VarUint}, } } }, { TypeId::AnyState, { "AnyState", TypeId::AnyState, TypeId::LayerState, { } } }, { TypeId::EntryState, { "EntryState", TypeId::EntryState, TypeId::LayerState, { } } }, { TypeId::ExitState, { "ExitState", TypeId::ExitState, TypeId::LayerState, { } } }, { TypeId::StateTransition, { "StateTransition", TypeId::StateTransition, TypeId::StateMachineLayerComponent, { {"stateToId", 151, PropertyType::VarUint}, {"flags", 152, PropertyType::VarUint}, {"duration", 158, PropertyType::VarUint}, {"exitTime", 160, PropertyType::VarUint}, } } }, { TypeId::StateMachineLayerComponent, { "StateMachineLayerComponent", TypeId::StateMachineLayerComponent, TypeId::NoType, { } } }, { TypeId::TransitionCondition, { "TransitionCondition", TypeId::TransitionCondition, TypeId::NoType, { {"inputId", 155, PropertyType::VarUint}, } } }, { TypeId::TransitionTriggerCondition, { "TransitionTriggerCondition", TypeId::TransitionTriggerCondition, TypeId::TransitionCondition, { } } }, { TypeId::TransitionValueCondition, { "TransitionValueCondition", TypeId::TransitionValueCondition, TypeId::TransitionCondition, { {"opValue", 156, PropertyType::VarUint}, } } }, { TypeId::TransitionNumberCondition, { "TransitionNumberCondition", TypeId::TransitionNumberCondition, TypeId::TransitionValueCondition, { {"value", 157, PropertyType::Float}, } } }, { TypeId::TransitionBoolCondition, { "TransitionBoolCondition", TypeId::TransitionBoolCondition, TypeId::TransitionValueCondition, { } } }, { TypeId::BlendState, { "BlendState", TypeId::BlendState, TypeId::LayerState, { } } }, { TypeId::BlendStateDirect, { "BlendStateDirect", TypeId::BlendStateDirect, TypeId::BlendState, { } } }, { TypeId::BlendAnimation, { "BlendAnimation", TypeId::BlendAnimation, TypeId::NoType, { {"animationId", 165, PropertyType::VarUint}, } } }, { TypeId::BlendAnimation1D, { "BlendAnimation1D", TypeId::BlendAnimation1D, TypeId::BlendAnimation, { {"value", 166, PropertyType::Float}, } } }, { TypeId::BlendState1D, { "BlendState1D", TypeId::BlendState1D, TypeId::BlendState, { {"inputId", 167, PropertyType::VarUint}, } } }, { TypeId::BlendAnimationDirect, { "BlendAnimationDirect", TypeId::BlendAnimationDirect, TypeId::BlendAnimation, { {"inputId", 168, PropertyType::VarUint}, } } }, { TypeId::BlendStateTransition, { "BlendStateTransition", TypeId::BlendStateTransition, TypeId::StateTransition, { {"exitBlendAnimationId", 171, PropertyType::VarUint}, } } }, { TypeId::Constraint, { "Constraint", TypeId::Constraint, TypeId::Component, { {"strength", 172, PropertyType::Float}, } } }, { TypeId::TargetedConstraint, { "TargetedConstraint", TypeId::TargetedConstraint, TypeId::Constraint, { {"targetId", 173, PropertyType::VarUint}, } } }, { TypeId::IKConstraint, { "IKConstraint", TypeId::IKConstraint, TypeId::TargetedConstraint, { {"invertDirection", 174, PropertyType::Bool}, {"parentBoneCount", 175, PropertyType::VarUint}, } } }, { TypeId::DistanceConstraint, { "DistanceConstraint", TypeId::DistanceConstraint, TypeId::TargetedConstraint, { {"distance", 177, PropertyType::Float}, {"modeValue", 178, PropertyType::VarUint}, } } }, { TypeId::TransformConstraint, { "TransformConstraint", TypeId::TransformConstraint, TypeId::TransformSpaceConstraint, { } } }, { TypeId::KeyFrameBool, { "KeyFrameBool", TypeId::KeyFrameBool, TypeId::KeyFrame, { {"value", 181, PropertyType::Bool}, } } }, { TypeId::TransformComponentConstraint, { "TransformComponentConstraint", TypeId::TransformComponentConstraint, TypeId::TransformSpaceConstraint, { {"minMaxSpaceValue", 195, PropertyType::VarUint}, {"copyFactor", 182, PropertyType::Float}, {"minValue", 183, PropertyType::Float}, {"maxValue", 184, PropertyType::Float}, {"offset", 188, PropertyType::Bool}, {"doesCopy", 189, PropertyType::Bool}, {"min", 190, PropertyType::Bool}, {"max", 191, PropertyType::Bool}, } } }, { TypeId::TransformComponentConstraintY, { "TransformComponentConstraintY", TypeId::TransformComponentConstraintY, TypeId::TransformComponentConstraint, { {"copyFactorY", 185, PropertyType::Float}, {"minValueY", 186, PropertyType::Float}, {"maxValueY", 187, PropertyType::Float}, {"doesCopyY", 192, PropertyType::Bool}, {"minY", 193, PropertyType::Bool}, {"maxY", 194, PropertyType::Bool}, } } }, { TypeId::TranslationConstraint, { "TranslationConstraint", TypeId::TranslationConstraint, TypeId::TransformComponentConstraintY, { } } }, { TypeId::ScaleConstraint, { "ScaleConstraint", TypeId::ScaleConstraint, TypeId::TransformComponentConstraintY, { } } }, { TypeId::RotationConstraint, { "RotationConstraint", TypeId::RotationConstraint, TypeId::TransformComponentConstraint, { } } }, { TypeId::TransformSpaceConstraint, { "TransformSpaceConstraint", TypeId::TransformSpaceConstraint, TypeId::TargetedConstraint, { {"sourceSpaceValue", 179, PropertyType::VarUint}, {"destSpaceValue", 180, PropertyType::VarUint}, } } }, { TypeId::WorldTransformComponent, { "WorldTransformComponent", TypeId::WorldTransformComponent, TypeId::ContainerComponent, { {"opacity", 18, PropertyType::Float}, } } }, { TypeId::NestedArtboard, { "NestedArtboard", TypeId::NestedArtboard, TypeId::Drawable, { {"artboardId", 197, PropertyType::VarUint}, } } }, { TypeId::NestedAnimation, { "NestedAnimation", TypeId::NestedAnimation, TypeId::ContainerComponent, { {"animationId", 198, PropertyType::VarUint}, } } }, { TypeId::NestedStateMachine, { "NestedStateMachine", TypeId::NestedStateMachine, TypeId::NestedAnimation, { } } }, { TypeId::NestedSimpleAnimation, { "NestedSimpleAnimation", TypeId::NestedSimpleAnimation, TypeId::NestedLinearAnimation, { {"speed", 199, PropertyType::Float}, {"isPlaying", 201, PropertyType::Bool}, } } }, { TypeId::NestedLinearAnimation, { "NestedLinearAnimation", TypeId::NestedLinearAnimation, TypeId::NestedAnimation, { {"mix", 200, PropertyType::Float}, } } }, { TypeId::NestedRemapAnimation, { "NestedRemapAnimation", TypeId::NestedRemapAnimation, TypeId::NestedLinearAnimation, { {"time", 202, PropertyType::Float}, } } }, { TypeId::Asset, { "Asset", TypeId::Asset, TypeId::NoType, { {"name", 203, PropertyType::String}, } } }, { TypeId::Image, { "Image", TypeId::Image, TypeId::Drawable, { {"assetId", 206, PropertyType::VarUint}, } } }, { TypeId::Folder, { "Folder", TypeId::Folder, TypeId::Asset, { } } }, { TypeId::FileAsset, { "FileAsset", TypeId::FileAsset, TypeId::Asset, { {"assetId", 204, PropertyType::VarUint}, } } }, { TypeId::DrawableAsset, { "DrawableAsset", TypeId::DrawableAsset, TypeId::FileAsset, { {"height", 207, PropertyType::Float}, {"width", 208, PropertyType::Float}, } } }, { TypeId::ImageAsset, { "ImageAsset", TypeId::ImageAsset, TypeId::DrawableAsset, { } } }, { TypeId::FileAssetContents, { "FileAssetContents", TypeId::FileAssetContents, TypeId::NoType, { {"bytes", 212, PropertyType::Bytes}, } } }, { TypeId::Vertex, { "Vertex", TypeId::Vertex, TypeId::ContainerComponent, { {"x", 24, PropertyType::Float}, {"y", 25, PropertyType::Float}, } } }, { TypeId::MeshVertex, { "MeshVertex", TypeId::MeshVertex, TypeId::Vertex, { {"u", 215, PropertyType::Float}, {"v", 216, PropertyType::Float}, } } }, { TypeId::Mesh, { "Mesh", TypeId::Mesh, TypeId::ContainerComponent, { {"triangleIndexBytes", 223, PropertyType::Bytes}, } } }, { TypeId::Text, { "Text", TypeId::Text, TypeId::Node, { {"value", 218, PropertyType::String}, } } }, { TypeId::ContourMeshVertex, { "ContourMeshVertex", TypeId::ContourMeshVertex, TypeId::MeshVertex, { } } }, { TypeId::ForcedEdge, { "ForcedEdge", TypeId::ForcedEdge, TypeId::Component, { {"fromId", 219, PropertyType::VarUint}, {"toId", 220, PropertyType::VarUint}, } } }, { TypeId::TextRun, { "TextRun", TypeId::TextRun, TypeId::Drawable, { {"pointSize", 221, PropertyType::Float}, {"textLength", 222, PropertyType::VarUint}, } } }, { TypeId::StateMachineListener, { "StateMachineListener", TypeId::StateMachineListener, TypeId::StateMachineComponent, { {"targetId", 224, PropertyType::VarUint}, {"listenerTypeValue", 225, PropertyType::VarUint}, } } }, { TypeId::ListenerTriggerChange, { "ListenerTriggerChange", TypeId::ListenerTriggerChange, TypeId::ListenerInputChange, { } } }, { TypeId::ListenerInputChange, { "ListenerInputChange", TypeId::ListenerInputChange, TypeId::ListenerAction, { {"inputId", 227, PropertyType::VarUint}, } } }, { TypeId::ListenerBoolChange, { "ListenerBoolChange", TypeId::ListenerBoolChange, TypeId::ListenerInputChange, { {"value", 228, PropertyType::VarUint}, } } }, { TypeId::ListenerNumberChange, { "ListenerNumberChange", TypeId::ListenerNumberChange, TypeId::ListenerInputChange, { {"value", 229, PropertyType::Float}, } } }, { TypeId::LayeredAsset, { "LayeredAsset", TypeId::LayeredAsset, TypeId::DrawableAsset, { } } }, { TypeId::LayerImageAsset, { "LayerImageAsset", TypeId::LayerImageAsset, TypeId::ImageAsset, { {"layer", 233, PropertyType::VarUint}, {"x", 234, PropertyType::Float}, {"y", 235, PropertyType::Float}, } } }, { TypeId::NestedInput, { "NestedInput", TypeId::NestedInput, TypeId::Component, { {"inputId", 237, PropertyType::VarUint}, } } }, { TypeId::NestedTrigger, { "NestedTrigger", TypeId::NestedTrigger, TypeId::NestedInput, { } } }, { TypeId::NestedBool, { "NestedBool", TypeId::NestedBool, TypeId::NestedInput, { {"nestedValue", 238, PropertyType::Bool}, } } }, { TypeId::NestedNumber, { "NestedNumber", TypeId::NestedNumber, TypeId::NestedInput, { {"nestedValue", 239, PropertyType::Float}, } } }, { TypeId::ListenerAction, { "ListenerAction", TypeId::ListenerAction, TypeId::NoType, { } } }, { TypeId::ListenerAlignTarget, { "ListenerAlignTarget", TypeId::ListenerAlignTarget, TypeId::ListenerAction, { {"targetId", 240, PropertyType::VarUint}, } } }, }; src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/avd/avd_format.hpp000664 001750 001750 00000001632 15165022620 035761 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" #include "glaxnimate/io/io_registry.hpp" namespace glaxnimate::io::avd { class AvdFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "avd"; } QString name() const override { return i18n("Android Vector Drawable"); } QStringList extensions(Direction) const override { return {"xml"}; } bool can_save() const override { return true; } bool can_open() const override { return true; } protected: bool on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap& options) override; bool on_save(QIODevice & file, const QString & filename, model::Composition* comp, const QVariantMap & setting_values) override; }; } // namespace glaxnimate::io::avd src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/keyframe_container.hpp000664 001750 001750 00000022243 15165022620 036006 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/model/animation/frame_time.hpp" namespace glaxnimate::model { class KeyframeBase; template class SimpleRange { public: using iterator = Iterator; SimpleRange(iterator begin_iter, iterator end_iter) : begin_iter(std::move(begin_iter)), end_iter(std::move(end_iter)) {} iterator begin() const { return begin_iter; } iterator end() const { return end_iter; } private: iterator begin_iter; iterator end_iter; }; namespace detail { class TypeErasedKeyframeIterator { public: using value_type = KeyframeBase; using reference = const value_type&; using pointer = const value_type*; using iterator_category = std::bidirectional_iterator_tag; using difference_type = ptrdiff_t; private: class VirtualIter { public: bool end = false; virtual ~VirtualIter() = default; virtual void increment() = 0; virtual void decrement() = 0; virtual pointer value() const = 0; virtual std::unique_ptr copy() const = 0; bool compare(const VirtualIter& oth) const { return ( end && oth.end) || oth.value() == value(); } }; template class TemplateIter : public VirtualIter { public: MapIter iter; TemplateIter(MapIter iter, bool end) : iter(iter) { this->end = end; } void increment() override { ++iter; } void decrement() override { --iter; } pointer value() const override { return &iter->second; } std::unique_ptr copy() const override { return std::make_unique(iter, this->end); } }; std::unique_ptr iter; public: template TypeErasedKeyframeIterator(MapIter iter, bool at_end) : iter(std::make_unique>(iter, at_end)) {} TypeErasedKeyframeIterator(const TypeErasedKeyframeIterator& oth) : iter(oth.iter->copy()) {} TypeErasedKeyframeIterator& operator=(const TypeErasedKeyframeIterator& oth) { iter = oth.iter->copy(); return *this; } TypeErasedKeyframeIterator(TypeErasedKeyframeIterator&& oth) = default; TypeErasedKeyframeIterator& operator=(TypeErasedKeyframeIterator&& oth) = default; TypeErasedKeyframeIterator& operator++() noexcept { iter->increment(); return *this; } TypeErasedKeyframeIterator operator++(int) noexcept { auto copy = *this; ++*this; return copy; } TypeErasedKeyframeIterator& operator--() noexcept { iter->decrement(); return *this; } TypeErasedKeyframeIterator operator--(int) noexcept { auto copy = *this; --*this; return copy; } bool operator==(const TypeErasedKeyframeIterator& oth) const noexcept { return iter->compare(*oth.iter); } bool operator!=(const TypeErasedKeyframeIterator& oth) const noexcept { return !iter->compare(*oth.iter); } reference operator*() const { return *iter->value(); } pointer operator->() const { return iter->value(); } }; using TypeErasedKeyframeRange = SimpleRange; } // namespace detail template class KeyframeContainer { private: using container = std::map; template class iterator_type { public: using value_type = std::decay_t; using reference = CVT&; using pointer = CVT*; using iterator_category = typename InnerIter::iterator_category; using difference_type = typename InnerIter::difference_type; iterator_type(InnerIter iter) noexcept : iter(iter) {} iterator_type& operator++() noexcept { ++iter; return *this; } iterator_type operator++(int) noexcept { auto copy = *this; ++*this; return copy; } iterator_type& operator--() noexcept { --iter; return *this; } iterator_type operator--(int) noexcept { auto copy = *this; --*this; return copy; } bool operator==(const iterator_type& oth) const noexcept { return iter == oth.iter; } bool operator!=(const iterator_type& oth) const noexcept { return iter != oth.iter; } reference operator*() const { return iter->second; } pointer operator->() const { return &iter->second; } pointer ptr() const { return &iter->second; } FrameTime key() const { return iter->first; } // Allow conversion from non-const to const iterator template, int> = 0> iterator_type( const iterator_type& mutable_iterator ) noexcept : iter(mutable_iterator.iter) {} private: friend KeyframeContainer; InnerIter iter; // friend detail::TypeErasedKeyframeIterator::TemplateIter; }; public: using value_type = T; using reference = T&; using const_reference = const T&; using iterator = iterator_type; // TODO use std::basic_const_iterator? using const_iterator = iterator_type; using size_type = int; /** * \brief Returns iterator to the value before or equal to the given time * If all items are after \p time, it will return an iterator to the first item */ iterator find_best(FrameTime time) { auto it = map.lower_bound(time); if ( it != map.begin() && (it == map.end() || it->first > time) ) --it; return it; } const_iterator find_best(FrameTime time) const { auto it = map.lower_bound(time); if ( it != map.begin() ) --it; return it; } /** * \brief Finds a keyframe from its actual time */ iterator find(FrameTime time) { return map.find(time); } /** * \brief Finds a keyframe from its actual time */ const_iterator find(FrameTime time) const { return map.find(time); } /** * \brief Iterator whose time is not less than \p time */ iterator lower_bound(FrameTime time) { return map.lower_bound(time); } /** * \brief Iterator whose time is not less than \p time */ const_iterator lower_bound(FrameTime time) const { return map.lower_bound(time); } /** * \brief Iterator whose time is greater than \p time */ iterator upper_bound(FrameTime time) { return map.upper_bound(time); } /** * \brief Iterator whose time is greater than \p time */ const_iterator upper_bound(FrameTime time) const { return map.upper_bound(time); } bool empty() const { return map.empty(); } size_type size() const { return map.size(); } iterator insert(FrameTime time, value_type t) { return map.emplace(time, std::move(t)).first; } iterator insert(iterator hint, FrameTime time, value_type t) { return map.emplace_hint(hint.iter, time, std::move(t)); } iterator begin() { return map.begin(); } iterator end() { return map.end(); } const_iterator begin() const { return map.begin(); } const_iterator end() const { return map.end(); } const_iterator cbegin() const { return map.begin(); } const_iterator cend() const { return map.end(); } detail::TypeErasedKeyframeRange type_erased() const { return { detail::TypeErasedKeyframeIterator(map.begin(), false), detail::TypeErasedKeyframeIterator(map.end(), true) }; } detail::TypeErasedKeyframeIterator type_erased(const const_iterator& iter) const { return detail::TypeErasedKeyframeIterator(iter.iter, iter.iter == map.end()); } /** * Updates the given iterator to have the given key * \pre no other element at \p dest */ iterator move(iterator it, FrameTime dest) { auto node = map.extract(it.iter); node.key() = dest; return map.insert(std::move(node)).position; } iterator erase(iterator it) { return map.erase(it.iter); } void clear() { map.clear(); } /** * \brief Whether the iterator points to a segment that contains the given time * meadning find_best(time) will return it */ bool contains_time(const_iterator it, FrameTime time) const { if ( it.iter == map.end() ) return false; if ( it.key() > time ) return it.iter == map.begin(); ++it; if ( it.iter == map.end() ) return true; return it.key() >= time; } /** * @brief Whethers the given time would be affected by chages of values at the time of the given iterator * @returns \b true if time lies on the given keyframe or the one before */ bool affects_time(const_iterator it, FrameTime time) const { if ( contains_time(it, time) ) return true; if ( it.key() > time && it.iter != map.begin() ) { --it; return contains_time(it, time); } return false; } private: std::map map; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/style/stroke.hpp000664 001750 001750 00000003575 15165022620 034202 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/model/shapes/style/styler.hpp" #include "glaxnimate/model/animation/animatable.hpp" namespace glaxnimate::model { class Stroke : public StaticOverrides { GLAXNIMATE_OBJECT(Stroke) public: enum Cap { ButtCap = Qt::FlatCap, RoundCap = Qt::RoundCap, SquareCap = Qt::SquareCap, }; enum Join { MiterJoin = Qt::MiterJoin, RoundJoin = Qt::RoundJoin, BevelJoin = Qt::BevelJoin, }; private: Q_ENUM(Cap); Q_ENUM(Join); GLAXNIMATE_ANIMATABLE(float, width, 1, {}, 0) GLAXNIMATE_PROPERTY(Cap, cap, RoundCap, nullptr, nullptr, PropertyTraits::Visual) GLAXNIMATE_PROPERTY(Join, join, RoundJoin, nullptr, nullptr, PropertyTraits::Visual) GLAXNIMATE_PROPERTY(float, miter_limit, 0, nullptr, nullptr, PropertyTraits::Visual) public: using Ctor::Ctor; QRectF local_bounding_rect(FrameTime t) const override { if ( !visible.get() ) return {}; qreal half_width = width.get_at(t) / 2; return collect_shapes(t, {}).bounding_box().adjusted( -half_width, -half_width, half_width, half_width ); } static QIcon static_tree_icon() { return QIcon::fromTheme("format-stroke-color"); } static QString static_type_name_human() { return i18n("Stroke"); } void set_pen_style(const QPen& p); void set_pen_style_undoable(const QPen& p); protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(FrameTime t) const override; void on_paint(renderer::Renderer* p, FrameTime t, PaintMode, model::Modifier* modifier) const override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/text.hpp000664 001750 001750 00000010337 15165022620 033774 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/property/sub_object_property.hpp" #include "glaxnimate/model/property/option_list_property.hpp" #include "glaxnimate/model/property/reference_property.hpp" #include "glaxnimate/model/shapes/shape.hpp" namespace glaxnimate::model { class Font : public Object { GLAXNIMATE_OBJECT(Font) GLAXNIMATE_PROPERTY_OPTIONS(QString, family, "", QStringList, &Font::families, &Font::on_family_changed, {}, PropertyTraits::Visual, OptionListPropertyBase::FontCombo) GLAXNIMATE_PROPERTY_OPTIONS(float, size, 32, QList, &Font::standard_sizes, &Font::on_font_changed, {}, PropertyTraits::Visual, OptionListPropertyBase::LaxValues) GLAXNIMATE_PROPERTY_OPTIONS(QString, style, "", QStringList, &Font::styles, &Font::on_font_changed, &Font::valid_style, PropertyTraits::Visual) GLAXNIMATE_PROPERTY(float, line_height, 1, &Font::on_font_changed, {}, PropertyTraits::Visual|PropertyTraits::Percent) public: struct CharData { quint32 glyph; QPointF position; }; struct LineData { std::vector glyphs; QRectF bounds; QPointF baseline; QPointF advance; QString text; }; using ParagraphData = std::vector; using CharDataCache = std::unordered_map; explicit Font(Document* doc); ~Font(); const QRawFont& raw_font() const; const QFont& query() const; const QFontMetricsF& metrics() const; void from_qfont(const QFont& f); QStringList styles() const; QStringList families() const; QList standard_sizes() const; QString type_name_human() const override; ParagraphData layout(const QString& string) const; /** * \brief Distance between two baselines */ qreal line_spacing() const; /** * \brief Distance between two baselines for a line_height of 1 */ qreal line_spacing_unscaled() const; glaxnimate::math::bezier::MultiBezier path_for_glyph(quint32 glyph, CharDataCache& cache, bool fix_paint) const; Q_SIGNALS: void font_changed(); protected: void on_transfer(model::Document* doc) override; private: void on_family_changed(); void on_font_changed(); void refresh_data(bool update_styles); bool valid_style(const QString& style); class Private; std::unique_ptr d; }; class TextShape : public ShapeElement { GLAXNIMATE_OBJECT(TextShape) GLAXNIMATE_PROPERTY(QString, text, {}, &TextShape::on_text_changed, {}, PropertyTraits::Visual) GLAXNIMATE_ANIMATABLE(QPointF, position, QPointF()) GLAXNIMATE_SUBOBJECT(Font, font) GLAXNIMATE_PROPERTY_REFERENCE(model::ShapeElement, path, &TextShape::valid_paths, &TextShape::is_valid_path, &TextShape::path_changed) GLAXNIMATE_ANIMATABLE(float, path_offset, 0, &TextShape::on_text_changed) public: explicit TextShape(model::Document* document); void add_shapes(FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const override; glaxnimate::math::bezier::MultiBezier shape_data(FrameTime t) const; QIcon tree_icon() const override; QRectF local_bounding_rect(FrameTime t) const override; QString type_name_human() const override; std::unique_ptr to_path() const override; /** * \brief Position where the next character would be * * ie: where to add another TextShape to make them flow together */ QPointF offset_to_next_character() const; protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(FrameTime t) const override; private: void on_font_changed(); void on_text_changed(); const glaxnimate::math::bezier::MultiBezier& untranslated_path(FrameTime t) const; std::vector valid_paths() const; bool is_valid_path(DocumentNode* node) const; void path_changed(model::ShapeElement* new_path, model::ShapeElement* old_path); mutable Font::CharDataCache cache; mutable glaxnimate::math::bezier::MultiBezier shape_cache; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/trim.hpp000664 001750 001750 00000002215 15165022620 034455 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/modifiers/path_modifier.hpp" namespace glaxnimate::model { class Trim : public StaticOverrides { GLAXNIMATE_OBJECT(Trim) GLAXNIMATE_ANIMATABLE(float, start, 0, {}, 0, 1, PropertyTraits::Percent) GLAXNIMATE_ANIMATABLE(float, end, 1, {}, 0, 1, PropertyTraits::Percent) GLAXNIMATE_ANIMATABLE(float, offset, 0, {}, std::numeric_limits::lowest(), std::numeric_limits::max(), PropertyTraits::Percent) public: enum MultipleShapes { Individually = 1, Simultaneously = 2, }; Q_ENUM(MultipleShapes) GLAXNIMATE_PROPERTY(MultipleShapes, multiple, Individually, {}, {}, PropertyTraits::Visual) public: using Ctor::Ctor; static QIcon static_tree_icon(); static QString static_type_name_human(); math::bezier::MultiBezier process(FrameTime t, const math::bezier::MultiBezier& mbez) const override; protected: bool process_collected() const override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/thorvg/000775 001750 001750 00000000000 15165022620 031223 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/video/CMakeLists.txt000664 001750 001750 00000001105 15165022620 033555 0ustar00ddennedyddennedy000000 000000 # SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia # SPDX-License-Identifier: BSD-2-Clause set(SOURCES video_format.cpp video_module.cpp ) add_library(GlaxnimateVideo OBJECT ${SOURCES}) add_library(Glaxnimate::Video ALIAS GlaxnimateVideo) # target_link_libraries( # GlaxnimateVideo PUBLIC # Glaxnimate::Core # ) find_package(Libav COMPONENTS codec util format swscale REQUIRED) include_directories(${Libav_INCLUDE_DIRS}) target_link_libraries(GlaxnimateVideo PUBLIC ${Libav_LIBRARIES} ) glaxnimate_enable_exceptions(GlaxnimateVideo PUBLIC) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/parse_error.hpp000664 001750 001750 00000001663 15165022620 032675 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include namespace glaxnimate::io::svg { class SvgParseError : public std::exception { public: SvgParseError() {} #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) SvgParseError(const QDomDocument::ParseResult& result) : message(result.errorMessage), line(result.errorLine), column(result.errorColumn) {} #endif QString formatted(const QString& filename) const { return QString("%1:%2:%3: XML Parse Error: %4") .arg(filename) .arg(line) .arg(column) .arg(message) ; } explicit operator bool() const { return line != -1; } QString message; int line = -1; int column = -1; }; } // namespace glaxnimate::io::svg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_mime.hpp000664 001750 001750 00000003262 15165022620 032155 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/svg/svg_parser.hpp" #include "glaxnimate/io/svg/parse_error.hpp" #include "glaxnimate/io/svg/svg_renderer.hpp" #include "glaxnimate/io/mime/mime_serializer.hpp" namespace glaxnimate::io::svg { class SvgMime : public io::mime::MimeSerializer { public: QString slug() const override { return "svg"; } QString name() const override { return i18n("SVG"); } QStringList mime_types() const override { return {"image/svg+xml"}; } QByteArray serialize(const std::vector& selection) const override { io::svg::SvgRenderer svg_rend(io::svg::NotAnimated, io::svg::CssFontType::FontFace); for ( auto node : selection ) svg_rend.write_node(node, node->document()->current_time()); return svg_rend.dom().toByteArray(0); } io::mime::DeserializedData deserialize(const QByteArray& data) const override { QBuffer buffer(const_cast(&data)); buffer.open(QIODevice::ReadOnly); auto on_error = [this](const QString& s){message(s);}; try { return io::svg::SvgParser(&buffer, deserialize_group_mode, nullptr, on_error) .parse_to_objects(); } catch ( const io::svg::SvgParseError& err ) { message(err.formatted("Clipboard")); return {}; } } bool can_deserialize() const override { return true; } /// \todo show in settings io::svg::SvgParser::GroupMode deserialize_group_mode = io::svg::SvgParser::Inkscape; }; } // namespace glaxnimate::io::svg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/pending_asset.hpp000664 001750 001750 00000000563 15165022620 034367 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include namespace glaxnimate::model { struct PendingAsset { int id = 0; QUrl url; QByteArray data; QString name_alias; bool loaded = false; }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_loader.hpp000664 001750 001750 00000001715 15165022620 036327 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/binary_stream.hpp" #include "glaxnimate/module/extraformats/rive/type_system.hpp" #include "glaxnimate/module/extraformats/rive/rive_format.hpp" namespace glaxnimate::io::rive { class RiveLoader { public: RiveLoader(BinaryInputStream& stream, RiveFormat* format); std::vector load_object_list(); bool load_document(model::Document* document); const PropertyTable& extra_properties() const; private: Object read_object(); QVariant read_property_value(PropertyType type); PropertyTable read_property_table(); void skip_value(PropertyType type); QByteArray read_raw_string(); QString read_string_utf8(); BinaryInputStream& stream; RiveFormat* format; PropertyTable extra_props; TypeSystem types; }; } // namespace glaxnimate::io::rive mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/bitmap.cpp000664 001750 001750 00000014140 15165022620 033007 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/bitmap.hpp" #include #include #include #include #include #include #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/command/object_list_commands.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Bitmap) void glaxnimate::model::Bitmap::paint(renderer::Renderer* painter) const { painter->draw_image(image); } void glaxnimate::model::Bitmap::refresh(bool rebuild_embedded) { QImageReader reader; QImage qimage; bool load_data = true; if ( rebuild_embedded || data.get().isEmpty() ) { if ( !filename.get().isEmpty() ) { QFileInfo finfo = file_info(); if ( !finfo.isFile() ) return; reader.setFileName(finfo.absoluteFilePath()); format.set(reader.format()); qimage = reader.read(); if ( rebuild_embedded && embedded() ) data.set(build_embedded(qimage)); load_data = false; } else if ( !url.get().isEmpty() ) { document()->assets()->network_downloader.get(QUrl(url.get()), [this, rebuild_embedded](QByteArray response){ QImageReader reader; QImage qimage; QBuffer buf(&response); buf.open(QIODevice::ReadOnly); reader.setDevice(&buf); format.set(reader.format()); qimage = reader.read(); image = qimage.convertToFormat(QImage::Format_ARGB32_Premultiplied); if ( rebuild_embedded && embedded() ) data.set(build_embedded(image)); width.set(image.width()); height.set(image.height()); document()->graphics_invalidated(); Q_EMIT loaded(); }, this); return; } } if ( load_data ) { QBuffer buf(const_cast(&data.get())); buf.open(QIODevice::ReadOnly); reader.setDevice(&buf); format.set(reader.format()); qimage = reader.read(); } image = qimage.convertToFormat(QImage::Format_ARGB32_Premultiplied); width.set(image.width()); height.set(image.height()); Q_EMIT loaded(); } QByteArray glaxnimate::model::Bitmap::build_embedded(const QImage& img) const { QByteArray new_data; QBuffer buf(&new_data); buf.open(QIODevice::WriteOnly); QImageWriter writer(&buf, format.get().toLatin1()); writer.write(img); return new_data; } bool glaxnimate::model::Bitmap::embedded() const { return !data.get().isEmpty(); } void glaxnimate::model::Bitmap::embed(bool embedded) { if ( embedded == this->embedded() ) return; if ( !embedded ) data.set_undoable({}); else data.set_undoable(build_embedded(image)); } void glaxnimate::model::Bitmap::on_refresh() { refresh(false); } QIcon glaxnimate::model::Bitmap::instance_icon() const { return pixmap(); } bool glaxnimate::model::Bitmap::from_url(const QUrl& url) { if ( url.scheme().isEmpty() || url.scheme() == "file" ) return from_file(url.path()); if ( url.scheme() == "data" ) return from_base64(url.path()); this->url.set(url.toString()); return true; } bool glaxnimate::model::Bitmap::from_file(const QString& file) { filename.set(file); return !image.isNull(); } bool glaxnimate::model::Bitmap::from_base64(const QString& data) { auto chunks = data.split(','); if ( chunks.size() != 2 ) return false; auto mime_settings = chunks[0].split(';'); if ( mime_settings.size() != 2 || mime_settings[1] != "base64" ) return false; auto formats = QImageReader::imageFormatsForMimeType(mime_settings[0].toLatin1()); if ( formats.empty() ) return false; auto decoded = QByteArray::fromBase64(chunks[1].toLatin1()); format.set(formats[0]); this->data.set(decoded); return !image.isNull(); } bool glaxnimate::model::Bitmap::from_raw_data(const QByteArray& data) { QBuffer buf(const_cast(&data)); buf.open(QBuffer::ReadOnly); auto format = QImageReader::imageFormat(&buf); if ( format.isEmpty() ) return false; this->format.set(format); this->data.set(data); return !image.isNull(); } QUrl glaxnimate::model::Bitmap::to_url() const { if ( !embedded() ) { return QUrl::fromLocalFile(file_info().absoluteFilePath()); } QByteArray fmt = format.get().toLatin1(); QByteArray mime_type; for ( const auto& mime : QImageWriter::supportedMimeTypes() ) if ( QImageWriter::imageFormatsForMimeType(mime).contains(fmt) ) { mime_type = mime; break; } if ( mime_type.isEmpty() ) return {}; QString data_url = "data:"; data_url += mime_type; data_url += ";base64,"; data_url += data.get().toBase64(); return QUrl(data_url); } QString glaxnimate::model::Bitmap::object_name() const { if ( embedded() ) return i18n("Embedded image"); return QFileInfo(filename.get()).fileName(); } QFileInfo glaxnimate::model::Bitmap::file_info() const { return QFileInfo(document()->io_options().path, filename.get()); } bool glaxnimate::model::Bitmap::remove_if_unused(bool) { if ( users().empty() ) { document()->push_command(new command::RemoveObject( this, &document()->assets()->images->values )); return true; } return false; } void glaxnimate::model::Bitmap::set_pixmap(const QImage& pix, const QString& format) { this->format.set(format); data.set(build_embedded(pix)); } QByteArray glaxnimate::model::Bitmap::image_data() const { if ( !data.get().isEmpty() ) return data.get(); if ( image.isNull() ) return {}; return build_embedded(image); } QSize glaxnimate::model::Bitmap::size() const { return {width.get(), height.get()}; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/animation_commands.cpp000664 001750 001750 00000027474 15165022620 034425 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/command/animation_commands.hpp" #include "glaxnimate/command/undo_macro_guard.hpp" #include "glaxnimate/model/document.hpp" glaxnimate::command::SetKeyframe::SetKeyframe( model::AnimatedPropertyBase* prop, model::FrameTime time, const QVariant& value, bool commit, QUndoCommand* parent ) : Parent(command_name(prop, time), commit, parent), prop(prop), time(time), before(prop->value_at_time(time)), after(value), had_before(prop->has_keyframe(time)) {} QString glaxnimate::command::SetKeyframe::command_name(model::AnimatableBase *prop, model::FrameTime time) { return i18n("Update %1 keyframe at %2", prop->visual_name(), time); } void glaxnimate::command::SetKeyframe::undo() { if ( had_before ) prop->set_keyframe(time, before); else prop->remove_keyframe_at_time(time); prop->set_transition_before(time, trans_before); } void glaxnimate::command::SetKeyframe::redo() { model::KeyframeBase* kf = nullptr; if ( !calculated ) { auto mid = prop->mid_transition(time); model::AnimatedPropertyBase::SetKeyframeInfo info; kf = prop->set_keyframe(time, after, &info); if ( kf && info.insertion ) { if ( mid.type != model::AnimatedPropertyBase::MidTransition::Middle ) { adjust_transition = false; } else { auto kf_before = prop->keyframe_before(time); adjust_transition = kf_before; trans_before = kf_before->transition(); left = mid.from_previous; right = mid.to_next; } } } else { prop->set_keyframe(time, after, nullptr); } if ( adjust_transition ) { prop->set_transition_before(time, left); prop->set_transition(time, right); } } bool glaxnimate::command::SetKeyframe::merge_with(const SetKeyframe& other) { if ( other.prop != prop ) return false; after = other.after; return true; } glaxnimate::command::RemoveKeyframeTime::RemoveKeyframeTime( model::AnimatedPropertyBase* prop, model::FrameTime time, QUndoCommand* parent ) : QUndoCommand(command_name(prop, time), parent), prop(prop), time(time) { auto kf = prop->keyframe_at(time); before = kf->value(); transition_before = kf->transition(); if ( auto kf_before = prop->keyframe_before(time) ) { prev_transition_after = prev_transition_before = kf_before->transition(); if ( !prev_transition_after.hold() ) prev_transition_after.set_after(kf->transition().after()); } } QString glaxnimate::command::RemoveKeyframeTime::command_name(model::AnimatableBase *prop, model::FrameTime time) { return i18n("Remove %1 keyframe at %2", prop->visual_name(), time); } void glaxnimate::command::RemoveKeyframeTime::undo() { prop->set_keyframe(time, before)->set_transition(transition_before); prop->set_transition_before(time, prev_transition_before); } void glaxnimate::command::RemoveKeyframeTime::redo() { prop->set_transition_before(time, prev_transition_after); prop->remove_keyframe_at_time(time); } glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated(model::AnimatedPropertyBase* prop, QVariant after, bool commit) : SetMultipleAnimated( auto_name(prop), {prop}, {}, {after}, commit, prop->time(), prop->object()->document()->record_to_keyframe() ) {} glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated( const QString& name, const std::vector& props, const QVariantList& before, const QVariantList& after, bool commit, model::FrameTime time, bool record_to_keyframe ) : Parent(name, commit), props(props), before(before), after(after), keyframe_after_global(record_to_keyframe), time(time) { bool add_before = before.empty(); for ( auto prop : props ) { if ( add_before ) this->before.push_back(prop->static_value()); keyframe_before.push_back(prop->has_keyframe(time)); keyframe_after.push_back(prop->animated()); add_0.push_back(time != 0 && !prop->animated() && keyframe_after_global); } } glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated(const QString& name, bool commit) : Parent(name, commit), keyframe_after_global(false) { } void glaxnimate::command::SetMultipleAnimated::push_property(model::AnimatedPropertyBase* prop, const QVariant& after_val) { keyframe_after_global = keyframe_after_global || prop->object()->document()->record_to_keyframe(); keyframe_after.push_back(prop->animated()); time = prop->time(); int insert = props.size(); props.push_back(prop); before.insert(before.begin() + insert, prop->value()); after.insert(after.begin() + insert, after_val); keyframe_before.push_back(prop->has_keyframe(time)); add_0.push_back(!prop->animated() && keyframe_after_global); } void glaxnimate::command::SetMultipleAnimated::push_property_not_animated(model::BaseProperty* prop, const QVariant& after_val) { props_not_animated.push_back(prop); before.push_back(prop->value()); after.push_back(after_val); } void glaxnimate::command::SetMultipleAnimated::undo() { for ( int i = 0; i < int(props.size()); i++ ) { auto prop = props[i]; if ( add_0[i] ) prop->remove_keyframe_at_time(0); if ( keyframe_after_global || keyframe_after[i] ) { if ( keyframe_before[i] ) { prop->set_keyframe(time, before[i]); } else { prop->remove_keyframe_at_time(time); prop->set_static_value(before[i]); } } else { if ( keyframe_before[i] ) prop->set_keyframe(time, before[i]); else if ( !prop->animated() ) prop->set_static_value(before[i]); } } for ( int i = 0; i < int(props_not_animated.size()); i++ ) { props_not_animated[i]->set_value(before[i+props.size()]); } } void glaxnimate::command::SetMultipleAnimated::redo() { for ( int i = 0; i < int(props.size()); i++ ) { auto prop = props[i]; if ( add_0[i] ) prop->set_keyframe(0, before[i]); if ( keyframe_after_global || keyframe_after[i] ) prop->set_keyframe(time, after[i]); else prop->set_static_value(after[i]); } for ( int i = 0; i < int(props_not_animated.size()); i++ ) { props_not_animated[i]->set_value(after[i+props.size()]); } } bool glaxnimate::command::SetMultipleAnimated::merge_with(const SetMultipleAnimated& other) { if ( other.props.size() != props.size() || keyframe_after != other.keyframe_after || time != other.time || other.props_not_animated.size() != props_not_animated.size()) return false; for ( int i = 0; i < int(props.size()); i++ ) if ( props[i] != other.props[i] ) return false; for ( int i = 0; i < int(props_not_animated.size()); i++ ) if ( props_not_animated[i] != other.props_not_animated[i] ) return false; after = other.after; return true; } QString glaxnimate::command::SetMultipleAnimated::auto_name(model::AnimatedPropertyBase* prop) { bool key_before = prop->has_keyframe(prop->time()); bool key_after = prop->object()->document()->record_to_keyframe(); if ( key_after && !key_before ) return i18n("Add keyframe for %1 at %2", prop->name(), prop->time()); if ( key_before ) return i18n("Update %1 at %2", prop->name(), prop->time()); return i18n("Update %1", prop->name()); } bool glaxnimate::command::SetMultipleAnimated::empty() const { return props.empty() && props_not_animated.empty(); } glaxnimate::command::SetKeyframeTransition::SetKeyframeTransition( model::AnimatedPropertyBase* prop, model::FrameTime time, const model::KeyframeTransition& transition, QUndoCommand *parent ) : QUndoCommand(i18n("Update keyframe transition"), parent), prop(prop), time(time), undo_value(prop->keyframe_at(time)->transition()), redo_value(transition) { } void glaxnimate::command::SetKeyframeTransition::undo() { prop->set_transition(time, undo_value); } void glaxnimate::command::SetKeyframeTransition::redo() { prop->set_transition(time, redo_value); } glaxnimate::model::KeyframeTransition glaxnimate::command::SetKeyframeTransition::transition_side( model::AnimatedPropertyBase *prop, model::FrameTime time, model::KeyframeTransition::Descriptive desc, const QPointF &point, bool before_transition) { auto transition = prop->keyframe_at(time)->transition(); if ( desc == model::KeyframeTransition::Custom ) { if ( before_transition ) transition.set_before(point); else transition.set_after(point); } else { if ( before_transition ) transition.set_before_descriptive(desc); else transition.set_after_descriptive(desc); } return transition; } glaxnimate::command::MoveKeyframe::MoveKeyframe( model::AnimatedPropertyBase* prop, model::FrameTime time_before, model::FrameTime time_after, QUndoCommand* parent ) : QUndoCommand(i18n("Move keyframe"), parent), prop(prop), time_before(time_before), time_after(time_after) {} void glaxnimate::command::MoveKeyframe::undo() { prop->move_keyframe(time_after, time_before); } void glaxnimate::command::MoveKeyframe::redo() { prop->move_keyframe(time_before, time_after); } glaxnimate::command::RemoveAllKeyframes::RemoveAllKeyframes(model::AnimatedPropertyBase* prop, QVariant after, QUndoCommand *parent) : QUndoCommand(command_name(prop), parent), prop(prop), before(prop->static_value()), after(std::move(after)) { int count = prop->keyframe_count(); keyframes.reserve(count); for ( const auto& kf : prop->keyframe_range() ) { keyframes.push_back({ kf.time(), kf.value(), kf.transition() }); } } QString glaxnimate::command::RemoveAllKeyframes::command_name(model::AnimatableBase *prop) { return i18n("Remove animations from %1", prop->visual_name()); } void glaxnimate::command::RemoveAllKeyframes::redo() { prop->clear_keyframes(); prop->set_static_value(after); } void glaxnimate::command::RemoveAllKeyframes::undo() { for ( const auto& kf : keyframes ) { prop->set_keyframe(kf.time, kf.value, nullptr, true)->set_transition(kf.transition); } // prop->set_time(prop->time()); prop->set_static_value(before); } glaxnimate::command::SetPositionBezier::SetPositionBezier( model::detail::AnimatedPropertyPosition* prop, math::bezier::Bezier after, bool commit, const QString& name ) : SetPositionBezier(prop, prop->bezier(), std::move(after), commit, name) { } glaxnimate::command::SetPositionBezier::SetPositionBezier( model::detail::AnimatedPropertyPosition* prop, math::bezier::Bezier before, math::bezier::Bezier after, bool commit, const QString& name ) : Parent(name.isEmpty() ? i18n("Update animation path") : name, commit), property(prop), before(std::move(before)), after(std::move(after)) { } bool glaxnimate::command::SetPositionBezier::merge_with(const glaxnimate::command::SetPositionBezier& other) { return property == other.property; } void glaxnimate::command::SetPositionBezier::undo() { property->set_bezier(before); } void glaxnimate::command::SetPositionBezier::redo() { property->set_bezier(after); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/visitor.cpp000664 001750 001750 00000001700 15165022620 031726 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/visitor.hpp" #include "glaxnimate/model/document_node.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/assets/composition.hpp" void glaxnimate::model::Visitor::visit(glaxnimate::model::Document* doc, model::Composition* main, bool skip_locked) { on_visit_document(doc, main); visit(doc->assets(), skip_locked); on_visit_document_end(doc, main); } void glaxnimate::model::Visitor::visit(glaxnimate::model::DocumentNode* node, bool skip_locked) { if ( skip_locked ) { auto visual = node->cast(); if ( visual && visual->locked.get() ) return; } on_visit(node); for ( auto ch : node->docnode_children() ) visit(ch, skip_locked); on_visit_end(node); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/ellipse.hpp000664 001750 001750 00000001236 15165022620 034443 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/shape.hpp" namespace glaxnimate::model { class Ellipse : public Shape { GLAXNIMATE_OBJECT(Ellipse) GLAXNIMATE_ANIMATABLE(QPointF, position, QPointF()) GLAXNIMATE_ANIMATABLE(QSizeF, size, QSizeF()) public: using Shape::Shape; QIcon tree_icon() const override; QString type_name_human() const override; math::bezier::Bezier to_bezier(FrameTime t) const override; QRectF local_bounding_rect(FrameTime t) const override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/script/register_machinery.hpp000664 001750 001750 00000010744 15165022620 034333 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "registrar_common.hpp" #include "glaxnimate/script/register_impl.hpp" #include #include namespace glaxnimate::script { template void register_method(const QMetaMethod& meth, const QMetaObject& cls, Class& handle) { if ( meth.access() != QMetaMethod::Public ) return; if ( meth.methodType() != QMetaMethod::Method && meth.methodType() != QMetaMethod::Slot ) return; if ( meth.parameterCount() > 9 ) { log::LogStream("Scripting", "", log::Error) << "Too many arguments for method " << cls.className() << "::" << meth.name() << ": " << meth.parameterCount(); return; } for ( int i = 0; i < meth.parameterCount(); i++ ) { if ( meth.parameterType(i) == QMetaType::UnknownType ) { log::LogStream("Scripting", "", log::Error) << "Invalid parameter type" << cls.className() << "::" << meth.name() << i << meth.parameterNames()[i]; return; } } Reg::register_method(meth, handle); } template void register_property(const QMetaProperty& prop, const QMetaObject& meta, Class& handle) { if ( !prop.isScriptable() ) return; if ( !Reg::register_property(prop, handle) ) log::LogStream("Python", "", log::Error) << "Invalid property" << meta.className() << "::" << prop.name() << "of type" << prop.userType() << prop.typeName(); } template struct enums; template struct enums : public enums { template void process(Class& scope) { Reg::template register_enum(QMetaEnum::fromType(), scope); enums::template process(scope); } }; template<> struct enums<> { template void process(Class&) {} }; template typename Reg::template class_ declare_from_meta(const typename Reg::module& scope) { const QMetaObject& meta = CppClass::staticMetaObject; const char* name = meta.className(); const char* clean_name = std::strrchr(name, ':'); if ( clean_name == nullptr ) clean_name = name; else clean_name++; return Reg::template define_class (scope, clean_name); } template typename Reg::template class_& populate_from_meta( typename Reg::template class_& reg, enums reg_enums = {}) { const QMetaObject& meta = CppClass::staticMetaObject; for ( int i = meta.propertyOffset(); i < meta.propertyCount(); i++ ) { register_property(meta.property(i), meta, reg); } for ( int i = meta.methodOffset(); i < meta.methodCount(); i++ ) { register_method(meta.method(i), meta, reg); } if ( meta.classInfoOffset() < meta.classInfoCount() ) { QVariantMap classinfo; for ( int i = meta.classInfoOffset(); i < meta.classInfoCount(); i++ ) { auto info = meta.classInfo(i); classinfo[info.name()] = info.value(); } Reg::set_class_static(reg, "__classinfo__", QVariant(classinfo)); } reg_enums.template process(reg); return reg; } template typename Reg::template class_ register_from_meta( const typename Reg::module& scope, enums reg_enums = {} ) { auto reg = declare_from_meta(scope); populate_from_meta(reg, reg_enums); return reg; } template auto register_constructible(const typename Reg::module& module, FwArgs&&... args) { auto reg = register_from_meta(module, std::forward(args)...); Reg::template glaxnimate_constructible(reg); return reg; } namespace detail { template QString qdebug_operator_to_string(const T& o) { QString data; QDebug(&data) << o; return data; } } // namespace detail template auto qdebug_operator_to_string() { return &detail::qdebug_operator_to_string; } } // namespace glaxnimate::script mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation_container.hpp000664 001750 001750 00000003017 15165022620 034260 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/object.hpp" #include "glaxnimate/model/property/property.hpp" namespace glaxnimate::model { /** * \brief Helper class for document nodes that enclose an animation */ class AnimationContainer: public Object { GLAXNIMATE_OBJECT(AnimationContainer) GLAXNIMATE_PROPERTY(float, first_frame, 0, &AnimationContainer::on_first_frame_changed, &AnimationContainer::validate_first_frame, PropertyTraits::Visual) GLAXNIMATE_PROPERTY(float, last_frame, -1, &AnimationContainer::on_last_frame_changed, &AnimationContainer::validate_last_frame, PropertyTraits::Visual) Q_PROPERTY(bool time_visible READ time_visible) Q_PROPERTY(float duration READ duration) public: using Object::Object; /** * \brief Whether time() is within first/last frame */ bool time_visible() const; bool time_visible(FrameTime t) const; void set_time(FrameTime t) override; float duration() const; QString type_name_human() const override; void stretch_time(qreal multiplier) override; Q_SIGNALS: void first_frame_changed(float); void last_frame_changed(float); void time_visible_changed(bool visible); private Q_SLOTS: void on_first_frame_changed(float); void on_last_frame_changed(float); private: bool validate_first_frame(int f) const; bool validate_last_frame(int f) const; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/aepx.hpp000664 001750 001750 00000006050 15165022620 034645 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/module/extraformats/aep/aep_riff.hpp" #include "glaxnimate/io/svg/detail.hpp" namespace glaxnimate::io::aep { class AepxConverter { public: RiffChunk aepx_to_chunk(const QDomElement& element) { QString header = element.tagName(); if ( header == "ProjectXMPMetadata" ) { return chunk("XMPM", text(element.text())); } else if ( header == "string" ) { return chunk("Utf8", text(element.text())); } else if ( header == "numS" ) { std::uint32_t val = element.firstChildElement().text().toUInt(); auto data = buffer(Endianness::Big().write_uint(val)); return chunk(header, data); } else if ( header == "ppSn" ) { std::uint32_t val = element.firstChildElement().text().toDouble(); auto data = buffer(Endianness::Big().write_float64(val)); return chunk(header, data); } else if ( element.hasAttribute("bdata") ) { return chunk(header, hex(element.attribute("bdata"))); } ChunkId riff_header = header.toLatin1(); ChunkId subheader = {""}; if ( header == "AfterEffectsProject" ) { riff_header = {"RIFX"}; } else if ( !AepRiff::is_fake_list(riff_header) ) { subheader = riff_header; riff_header = {"LIST"}; } return {riff_header, 0, subheader, {}, read_chunk_list(svg::detail::ElementRange(element))}; } std::vector> read_chunk_list(const svg::detail::ElementRange& range) { std::vector> out; out.reserve(range.size()); for ( const auto& el : range ) out.push_back(std::make_unique(aepx_to_chunk(el))); return out; } private: struct BinaryData { QByteArray data; QBuffer file; std::uint32_t length; }; RiffChunk chunk(const QString& header, BinaryData* data, const QString& subheader = {}) { return { header.toLatin1(), data->length, subheader.toLatin1(), {Endianness::Big(), &data->file, data->length, 0} }; } BinaryData* buffer(QByteArray&& content) { data.push_back(std::make_unique()); data.back()->length = content.size(); data.back()->data = std::move(content); data.back()->file.setBuffer(&data.back()->data); data.back()->file.open(QIODevice::ReadOnly); return data.back().get(); } BinaryData* hex(const QString& hex) { return buffer(QByteArray::fromHex(hex.toLatin1())); } BinaryData* text(const QString& string) { return buffer(string.toUtf8()); } std::vector> data; }; } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/mask_settings.cpp000664 001750 001750 00000001161 15165022620 033103 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/mask_settings.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::MaskSettings) QString glaxnimate::model::MaskSettings::type_name_human() const { return i18n("Mask"); } glaxnimate::model::MaskSettings::MaskMode glaxnimate::model::MaskSettings::next_mode(MaskMode previous) { switch ( previous ) { case NoMask: return Alpha; case Alpha: return Luma; case Luma: default: return NoMask; } } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/named_color.cpp000664 001750 001750 00000001741 15165022620 034020 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/named_color.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/command/object_list_commands.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::NamedColor) QString glaxnimate::model::NamedColor::type_name_human() const { return i18n("Unnamed Color"); } QBrush glaxnimate::model::NamedColor::brush_style(FrameTime t) const { return color.get_at(t); } void glaxnimate::model::NamedColor::fill_icon(QPixmap& icon) const { icon.fill(color.get_at(0)); } bool glaxnimate::model::NamedColor::remove_if_unused(bool clean_lists) { if ( clean_lists && users().empty() ) { document()->push_command(new command::RemoveObject( this, &document()->assets()->colors->values )); return true; } return false; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/gradient.hpp000664 001750 001750 00000005170 15165022620 033340 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/assets/brush_style.hpp" #include "glaxnimate/model/animation/animatable.hpp" #include "glaxnimate/math/vector.hpp" namespace glaxnimate::math { template<> QGradientStops lerp(const QGradientStops& a, const QGradientStops& b, double factor); } // namespace glaxnimate::math namespace glaxnimate::model { namespace detail { template<> std::optional variant_cast(const QVariant& val); } // namespace detail class GradientColors : public Asset { GLAXNIMATE_OBJECT(GradientColors) GLAXNIMATE_ANIMATABLE(QGradientStops, colors, {}, &GradientColors::colors_changed) public: using Asset::Asset; QIcon instance_icon() const override; QString type_name_human() const override; bool remove_if_unused(bool clean_lists) override; Q_INVOKABLE void split_segment(int segment_index, float factor = 0.5, const QColor& new_color = {}); Q_INVOKABLE void remove_stop(int index); Q_SIGNALS: void colors_changed(const QGradientStops&); }; class Gradient : public BrushStyle { GLAXNIMATE_OBJECT(Gradient) public: enum GradientType { Linear = 1, Radial = 2, Conical = 3 }; Q_ENUM(GradientType) GLAXNIMATE_PROPERTY_REFERENCE(GradientColors, colors, &Gradient::valid_refs, &Gradient::is_valid_ref, &Gradient::on_ref_changed) GLAXNIMATE_PROPERTY(GradientType, type, Linear, {}, {}, PropertyTraits::Visual) GLAXNIMATE_ANIMATABLE(QPointF, start_point, {}) GLAXNIMATE_ANIMATABLE(QPointF, end_point, {}) GLAXNIMATE_ANIMATABLE(QPointF, highlight, {}) GLAXNIMATE_ANIMATABLE(float, angle, 0, {}) public: using BrushStyle::BrushStyle; QString type_name_human() const override; QBrush brush_style(FrameTime t) const override; QBrush constrained_brush_style(FrameTime t, const QRectF& bounds) const override; Q_INVOKABLE qreal radius(double t) const; static QString gradient_type_name(GradientType t); bool remove_if_unused(bool clean_lists) override; private: std::vector valid_refs() const; bool is_valid_ref(DocumentNode* node) const; void on_ref_changed(GradientColors* new_ref, GradientColors* old_ref); void on_ref_visual_changed(); void fill_icon(QPixmap& icon) const override; void on_property_changed(const BaseProperty* prop, const QVariant& value) override; Q_SIGNALS: void colors_changed_from(GradientColors* old_use, GradientColors* new_use); }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/glaxnimate_html_format.hpp000664 001750 001750 00000001466 15165022620 036353 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" namespace glaxnimate::io::glaxnimate { class GlaxnimateHtmlFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "glaxnimate_html"; } QString name() const override { return i18n("Glaxnimate HTML Preview"); } QStringList extensions(Direction) const override { return {"html", "htm"}; } bool can_save() const override { return true; } bool can_open() const override { return false; } private: bool on_save(QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& setting_values) override; }; } // namespace glaxnimate::io::glaxnimate mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/raster/raster_format.hpp000664 001750 001750 00000002022 15165022620 033711 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/io/base.hpp" namespace glaxnimate::io::raster { class RasterFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "raster"; } QString name() const override { return i18n("Raster Image"); } QStringList extensions(io::ImportExport::Direction direction) const override; bool can_save() const override { return false; } bool can_save_static() const override { return true; } bool can_open() const override { return true; } int priority() const override { return -1; } protected: bool on_open(QIODevice& dev, const QString&, model::Document* document, const QVariantMap&) override; bool on_save_static(QIODevice &file, const QString &filename, model::Composition *comp, model::FrameTime time, const QVariantMap &setting_values) override; }; } // namespace glaxnimate::io::raster mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/tgs_format.cpp000664 001750 001750 00000007357 15165022620 033550 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "tgs_format.hpp" #include "glaxnimate/io/lottie/cbor_write_json.hpp" #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" #include "glaxnimate/model/shapes/modifiers/repeater.hpp" #include "glaxnimate/model/shapes/modifiers/inflate_deflate.hpp" #include "glaxnimate/model/shapes/modifiers/offset_path.hpp" #include "glaxnimate/model/shapes/modifiers/zig_zag.hpp" #include "glaxnimate/io/lottie/validation.hpp" #include "glaxnimate/module/gzip/gzip.hpp" using namespace glaxnimate; using namespace glaxnimate::io::lottie; namespace { class TgsVisitor : public glaxnimate::io::lottie::ValidationVisitor { public: explicit TgsVisitor(LottieFormat* fmt) : ValidationVisitor(fmt) { allowed_fps.push_back(30); allowed_fps.push_back(60); fixed_size = QSize(512, 512); max_frames = 180; } private: void on_visit(model::DocumentNode * node) override { if ( qobject_cast(node) ) { show_error(node, i18n("Star Shapes are not officially supported"), log::Info); } else if ( qobject_cast(node) || qobject_cast(node) ) { show_error(node, i18n("Images are not supported"), log::Error); } else if ( auto st = qobject_cast(node) ) { if ( qobject_cast(st->use.get()) ) show_error(node, i18n("Gradient strokes are not officially supported"), log::Info); } else if ( auto layer = qobject_cast(node) ) { if ( layer->mask->has_mask() ) show_error(node, i18n("Masks are not officially supported"), log::Error); } else if ( qobject_cast(node) ) { show_error(node, i18n("Repeaters are not officially supported"), log::Info); } else if ( qobject_cast(node) ) { show_error(node, i18n("Inflate/Deflate is not supported"), log::Warning); } else if ( qobject_cast(node) ) { show_error(node, i18n("Offset Path is not supported"), log::Warning); } else if ( qobject_cast(node) ) { show_error(node, i18n("ZigZag is not supported"), log::Warning); } } }; } // namespace bool glaxnimate::io::lottie::TgsFormat::on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&) { QByteArray json; if ( !gzip::decompress(file, json, [this](const QString& s){ error(s); }) ) return false; return load_json(json, document); } bool glaxnimate::io::lottie::TgsFormat::on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap&) { QVariantMap settings; settings[QStringLiteral("duplicate_masks")] = true; validate(comp->document(), comp); QCborMap json = LottieFormat::to_json(comp, true, true, settings); json[QLatin1String("tgs")] = 1; QByteArray data = cbor_write_json(json, true); quint32 compressed_size = 0; if ( !gzip::compress(data, file, [this](const QString& s){ error(s); }, 9, &compressed_size) ) return false; qreal size_k = compressed_size / 1024.0; if ( size_k > 64 ) error(i18n("File too large: %1k, should be under 64k", size_k)); return true; } void glaxnimate::io::lottie::TgsFormat::validate(model::Document* document, model::Composition* comp) { TgsVisitor(this).visit(document, comp); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/composable.hpp000664 001750 001750 00000002032 15165022620 035707 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2025 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/shape.hpp" #include "glaxnimate/model/transform.hpp" #include "glaxnimate/model/property/sub_object_property.hpp" namespace glaxnimate::model { class Composable: public ShapeElement { Q_OBJECT GLAXNIMATE_SUBOBJECT(Transform, transform) GLAXNIMATE_ANIMATABLE(float, opacity, 1, &Composable::opacity_changed, 0, 1, PropertyTraits::Percent) GLAXNIMATE_PROPERTY(renderer::BlendMode, blend_mode, renderer::BlendMode::Normal, &Composable::blend_mode_changed, {}, PropertyTraits::Visual|PropertyTraits::Hidden) public: Composable(Document* document); protected: void on_paint(renderer::Renderer*, FrameTime, PaintMode, model::Modifier*) const override; Q_SIGNALS: void opacity_changed(float op); void blend_mode_changed(renderer::BlendMode mode); private Q_SLOTS: void on_transform_matrix_changed(); }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/import_state.hpp000664 001750 001750 00000052273 15165022620 034421 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/math/bezier/bezier.hpp" #include "glaxnimate/io/glaxnimate/glaxnimate_format.hpp" #include "glaxnimate/model/assets/assets.hpp" namespace glaxnimate::io::glaxnimate::detail { class ImportState { private: struct UnresolvedPath { struct Step { model::Object* object = nullptr; model::BaseProperty* prop = nullptr; }; struct Item { QString propname; int index = -1; model::Object* step(model::Object* prev) const { auto prop = prev->get_property(propname); if ( !prop || prop->traits().type != model::PropertyTraits::Object ) return nullptr; if ( prop->traits().flags & model::PropertyTraits::List ) { if ( index == -1 ) return nullptr; auto val_list = prop->value().toList(); if ( val_list.size() <= index ) return nullptr; return val_list[index].value(); } return prop->value().value(); } }; UnresolvedPath(model::Object* base_object = nullptr) : base_object(base_object) {} UnresolvedPath sub(model::BaseProperty* prop) const { auto copy = *this; copy.items.push_back({prop->name()}); return copy; } UnresolvedPath sub(int index) const { auto copy = *this; copy.items.back().index = index; return copy; } model::BaseProperty* prop() const { if ( items.empty() || !base_object ) return nullptr; model::Object* object = base_object; for ( int i = 0, e = items.size() - 1; i < e; i++ ) { object = items[i].step(object); if ( !object ) return nullptr; } return object->get_property(items.back().propname); } model::Object* base_object = nullptr; std::vector items; }; public: ImportState(GlaxnimateFormat* fmt, model::Document* document, int document_version = GlaxnimateFormat::format_version) : fmt(fmt), document(document), document_version(document_version) {} ~ImportState() {} void resolve() { for ( const auto& p : unresolved_references ) { model::BaseProperty* prop = p.first.prop(); model::DocumentNode* node = document->find_by_uuid(p.second); if ( !node ) { error(i18n("Property %1 of %2 refers to unexisting object %3", prop->name(), prop->object()->object_name(), p.second.toString() )); } else { if ( !prop->set_value(QVariant::fromValue(node)) ) error(i18n("Could not load %1 for %2: uuid refers to an unacceptable object", prop->name(), prop->object()->object_name() )); } } for ( model::Object* obj : unwanted ) { if ( obj ) { error(i18n("Object %1 is invalid", obj->object_name())); delete obj; } } } void load_object ( model::Object* target, QJsonObject object ) { version_fixup(object); do_load_object(target, object, target); } void load_metadata(const QJsonObject& object) { document->metadata() = object["metadata"].toObject().toVariantMap(); auto info = object["info"]; document->info().author = info["author"].toString(); document->info().description = info["description"].toString(); for ( const auto& kw: info["keywords"].toArray() ) document->info().keywords.push_back(kw.toString()); } void load_document(QJsonObject top_level) { auto assets = top_level[document_version < 3 ? "defs" : "assets"].toObject(); if ( document_version < 8 ) { QJsonObject precomps; QJsonArray values; QJsonObject main; bool has_main = false; if ( top_level["animation"].isObject() ) { main = top_level["animation"].toObject(); has_main = true; top_level.remove("animation"); values.push_front(main); } if ( assets.contains("precompositions") ) { precomps = assets["precompositions"].toObject(); for ( const QJsonValue& value : precomps["values"].toArray() ) { auto precomp = value.toObject(); if ( has_main ) precomp["animation"] = main["animation"]; values.push_back(precomp); } } else { precomps["__type__"] = "CompositionList"; } precomps["values"] = values; assets["precompositions"] = precomps; } load_metadata(top_level); load_object(document->assets(), assets); resolve(); } private: void error(const QString& msg) { if ( fmt ) Q_EMIT fmt->warning(msg); } QJsonObject fixed_asset_list(const QString& type, const QJsonValue& values) { QJsonObject fixed; fixed["__type__"] = type; fixed["values"] = values; fixed["uuid"] = QUuid::createUuid().toString(); return fixed; } void version_fixup(QJsonObject& object) { QString type = object["__type__"].toString(); if ( document_version == 1 ) { static const auto fix_ac = [](QJsonObject& object){ QJsonObject ac; ac["__type__"] = "AnimationContainer"; ac["first_frame"] = object["first_frame"]; ac["last_frame"] = object["last_frame"]; object.remove("first_frame"); object.remove("last_frame"); }; if ( type == "MainComposition" ) { fix_ac(object); object["shapes"] = object["layers"]; object.remove("layers"); } else if ( type == "ShapeLayer" ) { fix_ac(object); object["__type__"] = "Layer"; } else if ( type == "EmptyLayer" ) { fix_ac(object); object["__type__"] = "Layer"; object["shapes"] = QJsonArray(); } } if ( document_version < 3 && type == "Defs" ) { static const std::vector> types = { {"colors", "NamedColorList"}, {"gradient_colors", "GradientColorsList"}, {"gradients", "GradientList"}, {"images", "BitmapList"}, {"precompositions", "PrecompositionList"}, }; for ( const auto & pair : types ) { if ( object.contains(pair.first) ) { object[pair.first] = fixed_asset_list(pair.second, object[pair.first]); } } object["uuid"] = QUuid::createUuid().toString(); object["__type__"] = "Assets"; type = "Assets"; } if ( document_version < 4 && type == "Assets" ) { object["fonts"] = fixed_asset_list("FontList", QJsonArray()); } if ( document_version < 5 ) { if ( type == "Trim" ) { // values were swapped if ( object["multiple"].toString() == "Individually" ) object["multiple"] = "Simultaneously"; else object["multiple"] = "Individually"; } } if ( document_version < 6 ) { if ( type == "MaskSettings" ) object["mask"] = int(object["mask"].toBool()); } if ( document_version < 8 ) { if ( type == "MainComposition" ) { object["__type__"] = "Composition"; type = "Composition"; } else if ( type == "Precomposition" ) { object["__type__"] = "Composition"; type = "Composition"; if ( !document->assets()->compositions->values.empty() ) { auto main = document->assets()->compositions->values[0]; if ( !object.contains("fps") ) object["fps"] = main->fps.get(); if ( !object.contains("width") ) object["width"] = main->width.get(); if ( !object.contains("height") ) object["height"] = main->height.get(); } } else if ( type == "PrecompositionList" ) { object["__type__"] = "CompositionList"; type = "CompositionList"; } else if ( type == "Assets" ) { QJsonObject comps = object["precompositions"].toObject(); object.remove("precompositions"); object["compositions"] = comps; } } if ( document_version < 9 ) { if ( (type == "Layer" || type == "Group") && object.contains("auto_orient") ) { auto tf = object["transform"].toObject(); tf["auto_orient"] = object["auto_orient"]; object.remove("auto_orient"); } } } void do_load_object ( model::Object* target, QJsonObject object, const UnresolvedPath& path ) { QString type = object["__type__"].toString(); if ( type != target->type_name() ) error(i18n("Wrong object type: expected '%1' but got '%2'", target->type_name(), type)); for ( model::BaseProperty* prop : target->properties() ) { if ( object.contains(prop->name()) && !load_prop(prop, object[prop->name()], path.sub(prop)) ) { error(i18n("Could not load %1 for %2", prop->name(), prop->object()->object_name())); } } for ( auto it = object.begin(); it != object.end(); ++it ) { if ( !target->has(it.key()) && it.key() != "__type__" ) { if ( !target->set(it.key(), it->toVariant()) ) error(i18n("Could not set property %1", it.key())); } } } bool load_prop ( model::BaseProperty* target, const QJsonValue& val, const UnresolvedPath& path ) { if ( target->traits().flags & model::PropertyTraits::List ) { if ( !val.isArray() ) return false; QVariantList list; for ( QJsonValue item : val.toArray() ) list.push_back(load_prop_value(target, item, false, {})); if ( target->traits().type == model::PropertyTraits::Object ) { int index = 0; for ( const QVariant& item : list ) { auto ptr = item.value(); model::ObjectListPropertyBase* prop = static_cast(target); if ( !ptr ) { error(i18n( "Item %1 for %2 in %3 isn't an object", index, target->name(), target->object()->object_name() )); } else { auto inserted = prop->insert_clone(ptr); if ( !inserted ) { error(i18n( "Item %1 for %2 in %3 is not acceptable", index, target->name(), target->object()->object_name() )); } else { do_load_object(inserted, deferred_loads[ptr], path.sub(index)); } deferred_loads.remove(ptr); } index++; } return true; } else { return target->set_value(list); } } else if ( target->traits().flags & model::PropertyTraits::Animated ) { QJsonObject jso = val.toObject(); if ( jso.contains("value") ) { return target->set_value(load_prop_value(target, jso["value"], true, path)); } else { model::AnimatedPropertyBase* anim = static_cast(target); bool position = anim->traits().type == model::PropertyTraits::Point; for ( auto v : jso["keyframes"].toArray() ) { QJsonObject kfobj = v.toObject(); if ( !kfobj.contains("time") ) { error(i18n("Keyframe must specify a time")); continue; } if ( !kfobj.contains("value") ) { error(i18n("Keyframe must specify a value")); continue; } model::KeyframeBase* kf = anim->set_keyframe( kfobj["time"].toDouble(), load_prop_value(target, kfobj["value"], false, {}) ); if ( !kf ) { error(i18n("Could not add keyframe")); continue; } QPointF before, after; if ( load_2d(kfobj["before"], "x", "y", before) && load_2d(kfobj["after"], "x", "y", after) ) { kf->set_transition({before, after}); } else { kf->set_transition({{0, 0}, {1, 1}, model::KeyframeTransition::Special::Hold}); } if ( position ) { auto pkf = static_cast*>(kf); QPointF tan_in = pkf->get(); QPointF tan_out = pkf->get(); bool load_ti = load_2d(kfobj["tan_in"], "x", "y", tan_in); bool load_to = load_2d(kfobj["tan_out"], "x", "y", tan_out); if ( load_ti || load_to ) { auto type = math::bezier::PointType(kfobj["point_type"].toInt()); pkf->set_point({pkf->get(), tan_in, tan_out, type}); } } } return true; } } if ( target->traits().type == model::PropertyTraits::ObjectReference ) { QUuid uuid(val.toString()); if ( !uuid.isNull() ) unresolved_references.emplace_back(path, uuid); return true; } else if ( target->traits().type == model::PropertyTraits::Uuid ) { QUuid uuid(val.toString()); if ( uuid.isNull() ) return false; return target->set_value(uuid); } QVariant loaded_val = load_prop_value(target, val, true, path); if ( !target->set_value(loaded_val) ) { if ( target->traits().type == model::PropertyTraits::Object ) unwanted.push_back(loaded_val.value()); return false; } return true; } QColor load_color(const QJsonValue& val) { QString name = val.toString(); // We want #rrggbbaa, qt does #aarrggbb if ( name.startsWith("#") && name.size() == 9 ) { int alpha = name.right(2).toInt(nullptr, 16); QColor col(name.left(7)); col.setAlpha(alpha); return col; } return QColor(name); } QVariant load_prop_value ( model::BaseProperty* target, const QJsonValue& val, bool load_objects, const UnresolvedPath& path ) { switch ( target->traits().type ) { case model::PropertyTraits::Object: { if ( !val.isObject() ) return {}; QJsonObject jobj = val.toObject(); version_fixup(jobj); model::Object* object = create_object(jobj["__type__"].toString()); if ( !object ) return {}; if ( load_objects ) do_load_object(object, jobj, path); else deferred_loads.insert(object, jobj); return QVariant::fromValue(object); } case model::PropertyTraits::ObjectReference: case model::PropertyTraits::Uuid: // handled above return {}; case model::PropertyTraits::Color: return load_color(val); case model::PropertyTraits::Point: { QPointF p; if ( load_2d(val, "x", "y", p) ) return p; return {}; } case model::PropertyTraits::Size: { QSizeF p; if ( load_2d(val, "width", "height", p) ) return p; return {}; } case model::PropertyTraits::Scale: { QVector2D p; if ( load_2d(val, "x", "y", p) ) return p; return {}; } case model::PropertyTraits::Bezier: { if ( !val.isObject() ) return {}; math::bezier::Bezier bezier; QJsonObject obj = val.toObject(); bezier.set_closed(obj["closed"].toBool()); for ( auto jspv : obj["points"].toArray() ) { if ( !jspv.isObject() ) continue; QJsonObject jsp = jspv.toObject(); math::bezier::Point p{{}, {}, {}}; load_2d(jsp["pos"], "x", "y", p.pos); load_2d(jsp["tan_in"], "x", "y", p.tan_in); load_2d(jsp["tan_out"], "x", "y", p.tan_out); p.type = math::bezier::PointType(jsp["type"].toInt()); bezier.push_back(p); } return QVariant::fromValue(bezier); } case model::PropertyTraits::Gradient: { if ( !val.isArray() ) return {}; QGradientStops stops; for ( auto jstopv : val.toArray() ) { if ( !jstopv.isObject() ) continue; auto jstop = jstopv.toObject(); stops.push_back({jstop["offset"].toDouble(), load_color(jstop["color"])}); } return QVariant::fromValue(stops); } case model::PropertyTraits::Data: return QByteArray::fromBase64(val.toString().toLatin1()); default: return val.toVariant(); } } template bool load_2d(const QJsonValue& val, const QString& x, const QString& y, Type& ret) { QJsonObject obj = val.toObject(); if ( obj.empty() ) return false; ret = Type(obj[x].toDouble(), obj[y].toDouble()); return true; } model::Object* create_object(const QString& type) { if ( auto obj = model::Factory::instance().build(type, document) ) { temporaries.emplace_back(obj); return obj; } error(i18n("Unknown object of type '%1'", type)); temporaries.emplace_back(new model::Object(document)); return temporaries.back().get(); } GlaxnimateFormat* fmt; model::Document* document = nullptr; QMap references; std::vector> unresolved_references; QMap deferred_loads; std::vector unwanted; std::vector> temporaries; int document_version; }; } // namespace glaxnimate::io::glaxnimate::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/000775 001750 001750 00000000000 15165022620 027356 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/layer.cpp000664 001750 001750 00000011626 15165022620 034762 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/composable/layer.hpp" #include #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/model/document.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Layer) void glaxnimate::model::Layer::ChildLayerIterator::find_first() { while ( index < comp->size() && (*comp)[index]->docnode_group_parent() != parent ) ++index; } glaxnimate::model::VisualNode* glaxnimate::model::Layer::ChildLayerIterator::operator*() const { return (*comp)[index]; } glaxnimate::model::VisualNode* glaxnimate::model::Layer::ChildLayerIterator::operator->() const { return (*comp)[index]; } glaxnimate::model::VisualNode * glaxnimate::model::Layer::docnode_group_parent() const { return parent.get(); } glaxnimate::model::VisualNode * glaxnimate::model::Layer::docnode_group_child(int index) const { ChildLayerIterator iter(owner(), this, 0); std::advance(iter, index); return *iter; } int glaxnimate::model::Layer::docnode_group_child_count() const { if ( !owner() ) return 0; int sz = 0; for ( const auto& sib : *owner() ) if ( sib->docnode_group_parent() == this ) sz++; return sz; } std::vector glaxnimate::model::Layer::valid_parents() const { std::vector refs; refs.push_back(nullptr); if ( is_top_level() ) { for ( const auto& sh : *owner() ) { if ( auto lay = qobject_cast(sh.get()) ) if ( !is_ancestor_of(lay) ) refs.push_back(lay); } } return refs; } bool glaxnimate::model::Layer::is_valid_parent(glaxnimate::model::DocumentNode* node) const { if ( node == nullptr ) return true; if ( is_top_level() ) { if ( Layer* layer = qobject_cast(node) ) return !is_ancestor_of(layer); } return false; } bool glaxnimate::model::Layer::is_ancestor_of ( const glaxnimate::model::Layer* other ) const { while ( other ) { if ( other == this ) return true; other = other->parent.get(); } return false; } void glaxnimate::model::Layer::set_time(glaxnimate::model::FrameTime t) { Object::set_time(relative_time(t)); } bool glaxnimate::model::Layer::is_top_level() const { return qobject_cast(docnode_parent()); } void glaxnimate::model::Layer::paint(renderer::Renderer* painter, FrameTime time, PaintMode mode, glaxnimate::model::Modifier* modifier) const { if ( !visible.get() || (mode == Render && !render.get()) ) return; time = relative_time(time); if ( !animation->time_visible(time) ) return; auto n_shapes = shapes.size(); auto mask_mode = mask->mask.get(); if ( mask->has_mask() && n_shapes > 1 && shapes[0]->visible.get() ) { int renderer_mask_mode = mask_mode; if ( mask->inverted.get() ) renderer_mask_mode |= renderer::MaskInverted; painter->layer_start(); auto transform = group_transform_matrix(time); painter->transform(transform); painter->mask_start(renderer_mask_mode); painter->transform(transform); shapes[0]->paint(painter, time, mode); painter->mask_end(); on_paint(painter, time, mode, modifier); for ( int i = 1; i < n_shapes; i++ ) docnode_visual_child(i)->paint(painter, time, mode); painter->layer_end(); } else { VisualNode::paint(painter, time, mode); } } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Layer::to_clip(glaxnimate::model::FrameTime time) const { time = relative_time(time); if ( !animation->time_visible(time) || !render.get() ) return {}; return Group::to_clip(time); } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Layer::to_painter_path_impl(glaxnimate::model::FrameTime time) const { time = relative_time(time); if ( !animation->time_visible(time) || !render.get() ) return {}; return Group::to_painter_path_impl(time); } QIcon glaxnimate::model::Layer::tree_icon() const { return QIcon::fromTheme("folder"); } QIcon glaxnimate::model::Layer::static_tree_icon() { return QIcon::fromTheme("folder"); } std::unique_ptr glaxnimate::model::Layer::to_path() const { auto clone = std::make_unique(document()); for ( BaseProperty* prop : properties() ) { if ( prop != &shapes ) clone->get_property(prop->name())->assign_from(prop); } for ( const auto& shape : shapes ) { clone->shapes.insert(shape->to_path()); if ( shape->is_instance() ) break; } return clone; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/keyframe_base.hpp000664 001750 001750 00000005037 15165022620 035017 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/model/animation/keyframe_transition.hpp" #include "glaxnimate/model/animation/frame_time.hpp" namespace glaxnimate::model { class KeyframeBase : public QObject { Q_OBJECT Q_PROPERTY(QVariant value READ value) Q_PROPERTY(double time READ time) Q_PROPERTY(KeyframeTransition transition READ transition) public: explicit KeyframeBase(FrameTime time) : time_ { time } {} virtual ~KeyframeBase() = default; KeyframeBase(KeyframeBase&& oth) : time_(oth.time_) {} KeyframeBase(const KeyframeBase&) = delete; KeyframeBase& operator=(const KeyframeBase&) = delete; virtual QVariant value() const = 0; virtual bool set_value(const QVariant& value) = 0; FrameTime time() const { return time_; } void set_time(FrameTime t) { time_ = t; } /** * \brief Transition into the next value */ virtual KeyframeTransition transition() const = 0; virtual void set_transition(const KeyframeTransition& trans) = 0; void stretch_time(qreal multiplier) { time_ *= multiplier; } /** * \brief Splits a keyframe into multiple segments * \param other The keyframe following this * \param splits Array of splits in [0, 1], indicating the fractions at which splits shall occur * \pre \p other must be the same type of keyframe as \b this * \returns An array of keyframes matching the splits, * this will include a copy of \p other, which might have been modified slightly. * This should be used as \p this for the next keyframe */ std::vector> split(const KeyframeBase* other, std::vector splits) const; std::unique_ptr clone() const { auto clone = do_clone(); return clone; } protected: virtual std::unique_ptr do_clone() const = 0; class KeyframeSplitter { public: virtual ~KeyframeSplitter() = default; virtual void step(const QPointF& p) = 0; virtual std::unique_ptr left(const QPointF& p) const = 0; virtual std::unique_ptr right(const QPointF& p) const = 0; virtual std::unique_ptr last() const = 0; }; virtual std::unique_ptr splitter(const KeyframeBase* other) const = 0; private: FrameTime time_; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/animatable.hpp000664 001750 001750 00000061767 15165022620 034333 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/property/property.hpp" #include "glaxnimate/math/math.hpp" #include "glaxnimate/math/bezier/point.hpp" #include "glaxnimate/math/bezier/solver.hpp" #include "glaxnimate/math/bezier/bezier_length.hpp" #include "glaxnimate/model/animation/animatable_base.hpp" /* * Arguments: type, name, default, emitter, flags * For float: type, name, default, emitter, min, max, flags */ #define GLAXNIMATE_ANIMATABLE(type, name, ...) \ public: \ glaxnimate::model::AnimatedProperty name{this, kli18n(#name), __VA_ARGS__}; \ glaxnimate::model::AnimatableBase* get_##name() { return &name; } \ private: \ Q_PROPERTY(glaxnimate::model::AnimatableBase* name READ get_##name) \ Q_CLASSINFO(#name, "property animated " #type) \ // macro end namespace glaxnimate::model { class AnimatedPropertyBase : public AnimatableBase, public BaseProperty { Q_OBJECT Q_PROPERTY(QVariant value READ value WRITE set_undoable) public: AnimatedPropertyBase(Object* object, const utils::LazyLocalizedString& name, PropertyTraits traits); int animatable_flags() const override { return HasValue|IsWritable|IsProperty; } bool set_undoable(const QVariant& val, bool commit=true) override; using BaseProperty::value; bool assign_from(const BaseProperty* prop) override; /** * \brief Set the current time * \post value() == value(time) */ void set_time(FrameTime time) override { current_time = time; on_set_time(time); } FrameTime time() const { return current_time; } QVariant static_value() const override { return value(); } bool set_static_value(const QVariant& v) override { return set_value(v); } QVariant value(FrameTime time) const { return value_at_time(time); } QVariant value() const override = 0; QString visual_name() const override { return name(); } /** * \brief Removes all keyframes * \post !animated() */ virtual void clear_keyframes() = 0; /** * \brief Removes the keyframe with the given time * \returns whether a keyframe was found and removed */ virtual bool remove_keyframe_at_time(FrameTime time) = 0; /** * @brief Sets the transition at the given keyframe * @param time * @param transition */ virtual void set_transition(FrameTime time, const KeyframeTransition& transition) = 0; /** * @brief Sets the transition at the keyframe before \p time * @param time * @param transition */ virtual void set_transition_before(FrameTime time, const KeyframeTransition& transition) = 0; /** * \brief Moves a keyframe * \param from_time Time of the keyframe to move * \param to_time New time for the keyframe */ virtual MoveResult move_keyframe(FrameTime from_time, FrameTime to_time) = 0; /** * \brief Sets a value at a keyframe * \param time Time to set the value at * \param value Value to set * \param info If not nullptr, it will be written to with information about what has been node * \param force_insert If \b true, it will always add a new keyframe * \post value(time) == \p value && animate() == true * \return The keyframe or nullptr if it couldn't be added. * If there is already a keyframe at \p time the returned value might be an existing keyframe */ virtual KeyframeBase* set_keyframe(FrameTime time, const QVariant& value, SetKeyframeInfo* info = nullptr, bool force_insert = false) = 0; int property_type() const override { return traits().type; } QUndoCommand* command_add_smooth_keyframe(FrameTime time, const QVariant& value, bool commit = true, QUndoCommand* parent = nullptr) override; QUndoCommand* command_remove_keyframe(FrameTime time, QUndoCommand* parent = nullptr) override; QUndoCommand* command_clear_keyframes(QUndoCommand* parent = nullptr) override; QUndoCommand* command_set_transition(model::FrameTime time, const model::KeyframeTransition& transition, QUndoCommand* parent = nullptr) override; QUndoCommand* command_set_transition_side( model::FrameTime time, model::KeyframeTransition::Descriptive desc, const QPointF& point, bool before_transition, QUndoCommand* parent = nullptr ) override; QUndoCommand* command_move_keyframe(model::FrameTime time_before, model::FrameTime time_after, QUndoCommand* parent = nullptr) override; protected: virtual void on_set_time(FrameTime time) = 0; FrameTime current_time = 0; }; template class Keyframe : public KeyframeBase { public: using value_type = Type; using reference = const Type&; Keyframe(FrameTime time, Type value) : KeyframeBase(time), value_(std::move(value)) {} Keyframe(Keyframe&&) = default; void set(reference value) { value_ = value; } reference get() const { return value_; } QVariant value() const override { return QVariant::fromValue(value_); } bool set_value(const QVariant& val) override { if ( auto v = detail::variant_cast(val) ) { set(*v); return true; } return false; } value_type lerp(const Keyframe& other, double t) const { return math::lerp(value_, other.get(), this->transition().lerp_factor(t)); } KeyframeTransition transition() const override { return transition_; } void set_transition(const KeyframeTransition& trans) override { transition_ = trans; } protected: std::unique_ptr do_clone() const override { auto clone = std::make_unique(time(), value_); clone->set_transition(transition_); return clone; } class TypedKeyframeSplitter : public KeyframeSplitter { public: TypedKeyframeSplitter(const Keyframe* a, const Keyframe* b) : a(a), b(b) {} void step(const QPointF&) override {} std::unique_ptr left(const QPointF& p) const override { return std::make_unique( math::lerp(a->time(), b->time(), p.x()), math::lerp(a->get(), b->get(), p.y()) ); } std::unique_ptr right(const QPointF& p) const override { return std::make_unique( math::lerp(a->time(), b->time(), p.x()), math::lerp(a->get(), b->get(), p.y()) ); } std::unique_ptr last() const override { return b->clone(); } const Keyframe* a; const Keyframe* b; }; virtual std::unique_ptr splitter(const KeyframeBase* other) const override { return std::make_unique(this, static_cast(other)); } private: Type value_; KeyframeTransition transition_; }; template<> class Keyframe : public KeyframeBase { public: using value_type = QPointF; using reference = const QPointF&; Keyframe(FrameTime time, const QPointF& value) : KeyframeBase(time), point_(value) {} Keyframe(FrameTime time, const math::bezier::Point& value) : KeyframeBase(time), point_(value), linear(point_is_linear(value)) {} Keyframe(Keyframe&&) = default; void set(reference value) { point_.translate_to(value); } reference get() const { return point_.pos; } QVariant value() const override { return QVariant::fromValue(point_); } bool set_value(const QVariant& val) override { if ( val.userType() == QMetaType::QPointF ) { set(val.value()); return true; } else if ( auto v = detail::variant_cast(val) ) { set_point(*v); return true; } return false; } value_type lerp(const Keyframe& other, double t) const { auto factor = transition().lerp_factor(t); if ( linear && other.linear ) return math::lerp(get(), other.get(), factor); auto solver = bezier_solver(other); math::bezier::LengthData len(solver, 20); return solver.solve(len.at_ratio(factor).ratio); } void set_point(const math::bezier::Point& point) { point_ = point; linear = point_is_linear(point); } const math::bezier::Point& point() const { return point_; } math::bezier::CubicBezierSolver bezier_solver(const Keyframe& other) const { return math::bezier::CubicBezierSolver( point_.pos, point_.tan_out, other.point_.tan_in, other.point_.pos ); } bool is_linear() const { return linear; } KeyframeTransition transition() const override { return transition_; } void set_transition(const KeyframeTransition& trans) override { transition_ = trans; } protected: std::unique_ptr do_clone() const override { auto clone = std::make_unique(time(), point_); clone->set_transition(transition_); return clone; } class PointKeyframeSplitter; std::unique_ptr splitter(const KeyframeBase* other) const override; private: static bool point_is_linear(const math::bezier::Point& point) { return point.tan_in == point.pos && point.tan_out == point.pos; } math::bezier::Point point_; bool linear = true; KeyframeTransition transition_; }; template class AnimatedProperty; namespace detail { template class AnimatedProperty : public AnimatableImpl, AnimatedPropertyBase> { using Base = AnimatableImpl, AnimatedPropertyBase>; public: using keyframe_type = Keyframe; using value_type = typename Keyframe::value_type; using reference = typename Keyframe::reference; using iterator = typename KeyframeContainer::const_iterator; using mutable_iterator = typename KeyframeContainer::iterator; AnimatedProperty( Object* object, const utils::LazyLocalizedString& name, reference default_value, PropertyCallback emitter = {}, int flags = 0 ) : Base( object, name, PropertyTraits::from_scalar( PropertyTraits::Animated|PropertyTraits::Visual|flags )), value_{default_value}, emitter(std::move(emitter)) {} QVariant value() const override { return QVariant::fromValue(value_); } QVariant value_at_time(FrameTime time) const override { return QVariant::fromValue(get_at(time)); } keyframe_type* set_keyframe(FrameTime time, const QVariant& val, AnimatedPropertyBase::SetKeyframeInfo* info = nullptr, bool force_insert = false) override { if ( auto v = detail::variant_cast(val) ) return static_cast*>(this)->set_keyframe(time, *v, info, force_insert); return nullptr; } void clear_keyframes() override { int n = this->keyframes_.size(); this->keyframes_.clear(); for ( int i = n - 1; i >= 0; i-- ) Q_EMIT this->keyframe_removed(i); } bool remove_keyframe_at_time(FrameTime time) override { auto iter = this->keyframes_.find(time); if ( iter != this->keyframes_.end() ) { auto prev = iter; bool update_prev = prev != this->keyframes_.begin(); if ( update_prev ) --prev; this->keyframes_.erase(iter); Q_EMIT this->keyframe_removed(time); if ( update_prev ) on_keyframe_updated(prev); else this->value_changed(); return true; } return false; } bool set_value(const QVariant& val) override { if ( auto v = detail::variant_cast(val) ) return static_cast*>(this)->set(*v); return false; } bool valid_value(const QVariant& val) const override { if ( detail::variant_cast(val) ) return true; return false; } bool set(reference val) { value_ = val; mismatched_ = !this->keyframes_.empty(); this->value_changed(); emitter(this->object(), value_); return true; } keyframe_type* set_keyframe(FrameTime time, reference value, AnimatedPropertyBase::SetKeyframeInfo* info = nullptr, bool force_insert = false) { // First keyframe if ( this->keyframes_.empty() ) { value_ = value; this->value_changed(); emitter(this->object(), value_); auto it = this->keyframes_.insert(time, keyframe_type(time, value)); Q_EMIT this->keyframe_added(time); if ( info ) *info = {true}; return it.ptr(); } // Find the right keyframe auto kf = this->keyframes_.find_best(time); bool includes_current_time = this->keyframes_.affects_time(kf, this->time()); // Time matches, update if ( kf->time() == time && !force_insert ) { kf->set(value); if ( includes_current_time ) this->set_time(this->time()); Q_EMIT this->keyframe_updated(time); on_keyframe_updated(kf); if ( info ) *info = {false}; return kf.ptr(); } // First keyframe not at 0, might have to add the new keyframe at 0 if ( kf == this->keyframes_.begin() && kf->time() > time ) { auto it = this->keyframes_.insert(kf, time, keyframe_type(time, value)); if ( includes_current_time ) this->set_time(this->time()); Q_EMIT this->keyframe_added(time); on_keyframe_updated(it); if ( info ) *info = {true}; return it.ptr(); } // Insert somewhere in the middle auto it = this->keyframes_.insert(kf, time, keyframe_type(time, value)); if ( includes_current_time ) this->set_time(this->time()); Q_EMIT this->keyframe_added(time); on_keyframe_updated(it); if ( info ) *info = {true}; return it.ptr(); } void set_transition(FrameTime time, const KeyframeTransition& transition) override { auto kf = this->keyframes_.find(time); if ( kf != this->keyframes_.end() ) { kf->set_transition(transition); if ( this->keyframes_.affects_time(kf, this->time()) ) this->set_time(this->time()); Q_EMIT this->transition_changed(time, transition.before_descriptive(), transition.after_descriptive()); } } void set_transition_before(FrameTime time, const KeyframeTransition& transition) override { auto kf = this->keyframes_.find_best(time); if ( kf == this->keyframes_.end() || kf == this->keyframes_.begin()) return; --kf; kf->set_transition(transition); if ( this->keyframes_.affects_time(kf, time) ) this->set_time(this->time()); Q_EMIT this->transition_changed(time, transition.before_descriptive(), transition.after_descriptive()); } value_type get() const { return value_; } value_type get_at(FrameTime time) const { if ( time == this->time() ) return value_; return get_at_impl(time); } bool value_mismatch() const override { return mismatched_; } AnimatableBase::MoveResult move_keyframe(FrameTime from_time, FrameTime to_time) override { auto kf_at = this->keyframes_.find(from_time); if ( kf_at == this->keyframes_.end() ) return AnimatableBase::MoveResult::NotFound; // Nothing to do if ( to_time == from_time ) return AnimatableBase::MoveResult::Moved; bool includes_current_time = this->keyframes_.affects_time(kf_at, this->time()); // Just move to a new position auto iter = this->keyframes_.lower_bound(to_time); if ( iter == this->keyframes_.end() || iter->time() != to_time ) { kf_at->set_time(to_time); kf_at = this->keyframes_.move(kf_at, to_time); if ( includes_current_time || this->keyframes_.affects_time(kf_at, this->time()) ) this->set_time(this->time()); Q_EMIT this->keyframe_moved(from_time, to_time); return AnimatableBase::MoveResult::Moved; } // Needs to overwite iter->set_transition(kf_at->transition()); iter->set(kf_at->get()); this->keyframes_.erase(kf_at); if ( includes_current_time || this->keyframes_.affects_time(iter, this->time()) ) this->set_time(this->time()); Q_EMIT this->keyframe_removed(from_time); Q_EMIT this->keyframe_updated(to_time); return AnimatableBase::MoveResult::OverwrittenDestination; // why did I need all this? /* QPointF incoming(-1, -1); if ( keyframe_index > 0 ) { auto trans_before_src = keyframes_[keyframe_index - 1]->transition(); incoming = trans_before_src.after(); trans_before_src.set_after(keyframes_[keyframe_index]->transition().after()); keyframes_[keyframe_index - 1]->set_transition(trans_before_src); } auto move = std::move(keyframes_[keyframe_index]); keyframes_.erase(keyframes_.begin() + keyframe_index); keyframes_.insert(keyframes_.begin() + new_index, std::move(move)); int ia = keyframe_index; int ib = new_index; if ( ia > ib ) std::swap(ia, ib); if ( new_index > 0 ) { auto trans_before_dst = keyframes_[new_index - 1]->transition(); QPointF outgoing = trans_before_dst.after(); if ( incoming.x() != -1 ) { trans_before_dst.set_after(incoming); keyframes_[new_index - 1]->set_transition(trans_before_dst); } auto trans_moved = keyframes_[new_index]->transition(); trans_moved.set_after(outgoing); keyframes_[new_index]->set_transition(trans_moved); }*/ } SimpleRange mutable_range() { return { this->keyframes_.begin(), this->keyframes_.end() }; } void stretch_time(qreal multiplier) override { std::vector iters; iters.reserve(this->keyframes_.size()); for ( auto it = this->keyframes_.begin(); it != this->keyframes_.end(); ++it ) iters.push_back(it); // Ensure we never clash if ( multiplier > 1 ) std::reverse(iters.begin(), iters.end()); for ( auto& iter : iters ) { auto from_time = iter->time(); iter->stretch_time(multiplier); auto to_time = iter->time(); this->keyframes_.move(iter, to_time); Q_EMIT this->keyframe_moved(from_time, to_time); } this->current_time *= multiplier; } protected: void on_set_time(FrameTime time) override { if ( !this->keyframes_.empty() ) { value_ = get_at_impl(time); this->value_changed(); emitter(this->object(), value_); } mismatched_ = false; } void on_keyframe_updated(mutable_iterator kf) { auto cur_time = this->time(); // if no keyframes or the current keyframe is being modified => update value_ if ( !this->keyframes_.empty() && cur_time != kf->time() ) { if ( kf->time() > cur_time ) { // if the modified keyframe is far ahead => don't update value_ if ( kf->time() > cur_time ) return; } else { ++kf; // if the modified keyframe is far behind => don't update value_ if ( kf != this->keyframes_.end() && kf->time() < cur_time ) return; } } on_set_time(cur_time); } value_type get_at_impl(FrameTime time) const { auto iter_during = this->keyframes_.find_best(time); // No keyframe if ( iter_during == this->keyframes_.end() ) return value_; // Before the first keyframe if ( time < iter_during->time() ) return iter_during->get(); auto iter_after = iter_during; ++iter_after; // After the last keyframe if ( iter_after == this->keyframes_.end() ) return iter_during->get(); // Interpolate between two keyframes double scaled_time = (time - iter_during->time()) / (iter_after->time() - iter_during->time()); return iter_during->lerp(*iter_after, scaled_time); } QVariant do_mid_transition_value(const KeyframeBase* kf_before, const KeyframeBase* kf_after, qreal ratio) const override { return QVariant::fromValue( static_cast(kf_before)->lerp( *static_cast(kf_after), ratio ) ); } value_type value_; bool mismatched_ = false; PropertyCallback emitter; }; // Intermediare non-templated class so Q_OBJECT works class AnimatedPropertyPosition: public detail::AnimatedProperty { Q_OBJECT public: AnimatedPropertyPosition( Object* object, const utils::LazyLocalizedString& name, reference default_value, PropertyCallback emitter = {}, int flags = 0 ) : detail::AnimatedProperty(object, name, default_value, std::move(emitter), flags) { } void set_closed(bool closed); Q_INVOKABLE void split_segment(int index, qreal factor); Q_INVOKABLE bool set_bezier(glaxnimate::math::bezier::Bezier bezier); Q_INVOKABLE glaxnimate::math::bezier::Bezier bezier() const; void remove_points(const std::set& indices); keyframe_type* set_keyframe(FrameTime time, const QVariant& val, SetKeyframeInfo* info = nullptr, bool force_insert = false) override; keyframe_type* set_keyframe(FrameTime time, reference value, SetKeyframeInfo* info = nullptr, bool force_insert = false); bool set_value(const QVariant& val) override; bool valid_value(const QVariant& val) const override; QUndoCommand* command_add_smooth_keyframe(FrameTime time, const QVariant& value, bool commit = true, QUndoCommand* parent = nullptr) override; /** * \brief Gets the bezier derivative at the given time * \returns The derivative point, if defined */ std::optional derivative_at(FrameTime time) const; Q_SIGNALS: /// Invoked on set_bezier() void bezier_set(const math::bezier::Bezier& bezier); }; } // namespace detail template class AnimatedProperty : public detail::AnimatedProperty { public: using detail::AnimatedProperty::AnimatedProperty; }; template<> class AnimatedProperty : public detail::AnimatedProperty { public: AnimatedProperty( Object* object, const utils::LazyLocalizedString& name, reference default_value, PropertyCallback emitter = {}, float min = std::numeric_limits::lowest(), float max = std::numeric_limits::max(), int flags = 0 ) : detail::AnimatedProperty(object, name, default_value, std::move(emitter), flags), min_(min), max_(max) { } float max() const { return max_; } float min() const { return min_; } bool set(reference val) { return detail::AnimatedProperty::set(bound(val)); } using AnimatedPropertyBase::set_keyframe; keyframe_type* set_keyframe(FrameTime time, reference value, SetKeyframeInfo* info = nullptr, bool force_insert = false) { return detail::AnimatedProperty::set_keyframe(time, bound(value), info, force_insert); } private: float bound(float value) const { return math::bound(min_, value, max_); } float min_; float max_; }; template<> class AnimatedProperty : public detail::AnimatedPropertyPosition { public: using detail::AnimatedPropertyPosition::AnimatedPropertyPosition; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/000775 001750 001750 00000000000 15165022620 023344 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/document_node.cpp000664 001750 001750 00000025162 15165022620 033062 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/document_node.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/shapes/shape.hpp" #include "glaxnimate/model/property/reference_property.hpp" #include "glaxnimate/model/property/sub_object_property.hpp" #include "glaxnimate/utils/pseudo_mutex.hpp" class glaxnimate::model::DocumentNode::Private { public: std::unordered_set users; utils::PseudoMutex detaching; DocumentNode* list_parent = nullptr; }; glaxnimate::model::DocumentNode::DocumentNode(glaxnimate::model::Document* document) : DocumentNode(document, std::make_unique()) { } glaxnimate::model::DocumentNode::DocumentNode(glaxnimate::model::Document* document, std::unique_ptr d) : Object ( document ), d(std::move(d)) { uuid.set_value(QUuid::createUuid()); } glaxnimate::model::DocumentNode::~DocumentNode() = default; void glaxnimate::model::DocumentNode::removed_from_list() { auto old = d->list_parent; d->list_parent = nullptr; document()->decrease_node_name(name.get()); on_parent_changed(old, d->list_parent); Q_EMIT removed(); } void glaxnimate::model::DocumentNode::added_to_list ( glaxnimate::model::DocumentNode* new_parent ) { auto old = d->list_parent; d->list_parent = new_parent; document()->increase_node_name(name.get()); on_parent_changed(old, d->list_parent); } void glaxnimate::model::DocumentNode::on_name_changed(const QString& name, const QString& old_name) { if ( old_name != name ) { document()->decrease_node_name(old_name); document()->increase_node_name(name); Q_EMIT name_changed(name); } } glaxnimate::model::DocumentNode * glaxnimate::model::DocumentNode::docnode_parent() const { return d->list_parent; } bool glaxnimate::model::DocumentNode::docnode_is_instance(const QString& type_name) const { if ( type_name.isEmpty() ) return true; for ( const QMetaObject* meta = metaObject(); meta; meta = meta->superClass() ) { if ( detail::naked_type_name(meta->className()) == type_name ) return true; } return false; } void glaxnimate::model::DocumentNode::recursive_rename() { document()->set_best_name(this, name.get()); for ( auto child : docnode_children() ) child->recursive_rename(); } void glaxnimate::model::DocumentNode::refresh_uuid() { uuid.set_value(QUuid::createUuid()); for ( auto prop : properties() ) { if ( prop->traits().type == PropertyTraits::Object ) { if ( prop->traits().flags & PropertyTraits::List ) { for ( auto v : prop->value().toList() ) { if ( auto obj = v.value() ) obj->refresh_uuid(); } } else { if ( auto obj = qobject_cast(static_cast(prop)->sub_object()) ) obj->refresh_uuid(); } } } } QString glaxnimate::model::DocumentNode::object_name() const { if ( name.get().isEmpty() ) return type_name_human(); return name.get(); } void glaxnimate::model::DocumentNode::add_user(glaxnimate::model::DocumentNode::User* user) { if ( !d->detaching ) { d->users.insert(user); Q_EMIT users_changed(); } } void glaxnimate::model::DocumentNode::remove_user(glaxnimate::model::DocumentNode::User* user) { if ( !d->detaching ) { d->users.erase(user); Q_EMIT users_changed(); } } const std::unordered_set & glaxnimate::model::DocumentNode::users() const { return d->users; } void glaxnimate::model::DocumentNode::attach() { if ( auto lock = d->detaching.get_lock() ) { for ( auto user : d->users ) user->set_ref(this); } } void glaxnimate::model::DocumentNode::detach() { if ( auto lock = d->detaching.get_lock() ) { for ( auto user : d->users ) user->set_ref(nullptr); } } bool glaxnimate::model::DocumentNode::is_descendant_of(const model::DocumentNode* other) const { if ( !other ) return false; if ( other == this ) return true; auto parent = docnode_parent(); if ( parent ) return parent->is_descendant_of(other); return false; } class glaxnimate::model::VisualNode::Private : public DocumentNode::Private { public: std::unique_ptr group_icon; }; glaxnimate::model::VisualNode::VisualNode(model::Document* document) : DocumentNode(document, std::make_unique()) { } glaxnimate::model::VisualNode::Private * glaxnimate::model::VisualNode::dd() const { return static_cast(d.get()); } glaxnimate::model::VisualNode* glaxnimate::model::VisualNode::docnode_group_parent() const { return nullptr; } int glaxnimate::model::VisualNode::docnode_group_child_count() const { return 0; } glaxnimate::model::VisualNode* glaxnimate::model::VisualNode::docnode_group_child(int) const { return nullptr; } glaxnimate::model::VisualNode* glaxnimate::model::VisualNode::docnode_fuzzy_parent() const { if ( auto p = docnode_group_parent() ) return p; return docnode_visual_parent(); } QColor glaxnimate::model::VisualNode::docnode_group_color() const { if ( !docnode_valid_color() ) { if ( auto parent = docnode_fuzzy_parent() ) return parent->docnode_group_color(); return Qt::transparent; } return group_color.get(); } glaxnimate::model::VisualNode* glaxnimate::model::VisualNode::docnode_visual_child(int index) const { return static_cast(docnode_child(index)); } glaxnimate::model::VisualNode* glaxnimate::model::VisualNode::docnode_visual_parent() const { auto p = docnode_parent(); if ( p ) return p->cast(); return nullptr; } void glaxnimate::model::VisualNode::on_group_color_changed(const QColor&) { if ( dd()->group_icon && !dd()->group_icon->isNull() ) { if ( docnode_valid_color() ) dd()->group_icon->fill(group_color.get()); else dd()->group_icon->fill(Qt::white); } docnode_on_update_group(true); } void glaxnimate::model::VisualNode::docnode_on_update_group(bool) { // if ( force || docnode_valid_color() ) { Q_EMIT docnode_group_color_changed(docnode_group_color()); for ( auto gc : docnode_group_children() ) gc->docnode_on_update_group(); for ( auto gc : docnode_visual_children() ) gc->docnode_on_update_group(); } Q_EMIT group_transform_matrix_changed(group_transform_matrix(time())); } bool glaxnimate::model::VisualNode::docnode_valid_color() const { QColor col = group_color.get(); return col.isValid() && col.alpha() > 0; } QIcon glaxnimate::model::VisualNode::instance_icon() const { if ( !docnode_valid_color() ) { if ( auto parent = docnode_fuzzy_parent() ) return parent->instance_icon(); } if ( !dd()->group_icon ) { dd()->group_icon = std::make_unique(33, 33); dd()->group_icon->fill(docnode_group_color()); } return *dd()->group_icon; } bool glaxnimate::model::VisualNode::docnode_locked_recursive() const { for ( const VisualNode* n = this; n; n = n->docnode_visual_parent() ) { if ( n->locked.get() ) return true; } return false; } void glaxnimate::model::VisualNode::paint(renderer::Renderer* painter, FrameTime time, PaintMode mode, glaxnimate::model::Modifier* modifier) const { if ( !visible.get() ) return; painter->layer_start(); painter->transform(group_transform_matrix(time)); on_paint(painter, time, mode, modifier); for ( auto c : docnode_visual_children() ) { c->paint(painter, time, mode, modifier); if ( c->is_instance() && c->visible.get() ) break; } painter->layer_end(); } bool glaxnimate::model::VisualNode::docnode_selectable() const { if ( !visible.get() || locked.get() ) return false; if ( auto p = docnode_visual_parent() ) return p->docnode_selectable(); return true; } bool glaxnimate::model::VisualNode::docnode_visible_recursive() const { if ( !visible.get() ) return false; if ( auto p = docnode_visual_parent() ) return p->docnode_visible_recursive(); return true; } QTransform glaxnimate::model::VisualNode::transform_matrix(glaxnimate::model::FrameTime t) const { auto matrix = local_transform_matrix(t); glaxnimate::model::VisualNode* parent = docnode_visual_parent(); if ( parent ) matrix *= parent->transform_matrix(t); parent = docnode_group_parent(); if ( parent ) matrix *= parent->transform_matrix(t); return matrix; } QTransform glaxnimate::model::VisualNode::group_transform_matrix(glaxnimate::model::FrameTime t) const { auto parent = docnode_group_parent(); if ( parent ) return local_transform_matrix(t) * parent->transform_matrix(t); return local_transform_matrix(t); } void glaxnimate::model::VisualNode::on_visible_changed(bool visible) { Q_EMIT docnode_visible_changed(visible); Q_EMIT docnode_visible_recursive_changed(visible); for ( auto ch : docnode_visual_children() ) ch->propagate_visible(visible); } void glaxnimate::model::VisualNode::propagate_visible(bool visible) { if ( !this->visible.get() ) return; Q_EMIT docnode_visible_recursive_changed(visible); for ( auto ch : docnode_visual_children() ) ch->propagate_visible(visible && this->visible.get()); } void glaxnimate::model::VisualNode::propagate_transform_matrix_changed(const QTransform& t_global, const QTransform& t_group) { Q_EMIT transform_matrix_changed(t_global); Q_EMIT group_transform_matrix_changed(t_group); for ( auto ch : docnode_group_children() ) { auto ltm = ch->local_transform_matrix(ch->time()); ch->propagate_transform_matrix_changed(ltm * t_global, ltm * t_group); } for ( auto ch : docnode_visual_children() ) { auto ltm = ch->local_transform_matrix(ch->time()); ch->propagate_transform_matrix_changed(ltm * t_global, ltm); } } void glaxnimate::model::VisualNode::propagate_bounding_rect_changed() { on_graphics_changed(); Q_EMIT bounding_rect_changed(); if ( auto parent = docnode_visual_parent() ) parent->propagate_bounding_rect_changed(); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/geom.cpp000664 001750 001750 00000004155 15165022620 031016 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/geom.hpp" #include #include "glaxnimate/math/math.hpp" using namespace glaxnimate; QPointF math::line_closest_point(const QPointF& line_a, const QPointF& line_b, const QPointF& p) { QPointF a_to_p = p - line_a; QPointF a_to_b = line_b - line_a; qreal atb2 = length_squared(a_to_b); qreal atp_dot_atb = QPointF::dotProduct(a_to_p, a_to_b); qreal t = atp_dot_atb / atb2; return line_a + a_to_b * t; } // Algorithm from http://ambrsoft.com/TrigoCalc/Circle3D.htm QPointF math::circle_center(const QPointF& p1, const QPointF& p2, const QPointF& p3) { qreal x1 = p1.x(); qreal x2 = p2.x(); qreal x3 = p3.x(); qreal y1 = p1.y(); qreal y2 = p2.y(); qreal y3 = p3.y(); qreal A = 2 * (x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2); qreal p12 = x1*x1 + y1*y1; qreal p22 = x2*x2 + y2*y2; qreal p32 = x3*x3 + y3*y3; qreal B = p12 * (y3 - y2) + p22 * (y1 - y3) + p32 * (y2 - y1); qreal C = p12 * (x2 - x3) + p22 * (x3 - x1) + p32 * (x1 - x2); return { - B / A, - C / A }; } // Custom implementation rather than using QVector3D to keep precision static std::array cross_product(const std::array& a, const std::array& b) { return { a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], }; } std::optional math::line_intersection(const QPointF& start1, const QPointF& end1, const QPointF& start2, const QPointF& end2) { std::array v1{start1.x(), start1.y(), 1}; std::array v2{end1.x(), end1.y(), 1}; std::array v3{start2.x(), start2.y(), 1}; std::array v4{end2.x(), end2.y(), 1}; std::array cp = cross_product( cross_product(v1, v2), cross_product(v3, v4) ); // More forgiving than qFuzzyIsNull if ( math::abs(cp[2]) <= 0.00001 ) return {}; return QPointF(cp[0] / cp[2], cp[1] / cp[2]); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/stretchable_time.cpp000664 001750 001750 00000001306 15165022620 033547 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/stretchable_time.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::StretchableTime) bool glaxnimate::model::StretchableTime::validate_stretch(float stretch) { return stretch > 0; } float glaxnimate::model::StretchableTime::time_to_local(float global) const { return (global - start_time.get()) / stretch.get(); } float glaxnimate::model::StretchableTime::time_from_local(float local) const { return local * stretch.get() + start_time.get(); } QString glaxnimate::model::StretchableTime::type_name_human() const { return i18n("Timing"); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/aep_format.hpp000664 001750 001750 00000002621 15165022620 035746 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" #include "glaxnimate/io/io_registry.hpp" namespace glaxnimate::io::aep { struct RiffChunk; class AepFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "aep"; } QString name() const override { return i18n("Adobe After Effects Project"); } QStringList extensions(Direction) const override { return {"aep"}; } bool can_save() const override { return false; } bool can_open() const override { return true; } protected: bool on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap& options) override; bool riff_to_document(const RiffChunk& chunk, model::Document* document, const QString& filename); }; class AepxFormat : public AepFormat { Q_OBJECT public: QString slug() const override { return "aepx"; } QString name() const override { return i18n("Adobe After Effects Project XML"); } QStringList extensions(Direction) const override { return {"aepx"}; } bool can_save() const override { return false; } bool can_open() const override { return true; } protected: bool on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap& options) override; }; } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/document.cpp000664 001750 001750 00000016544 15165022620 032061 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/document.hpp" #include #include "glaxnimate/io/glaxnimate/glaxnimate_format.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/assets/pending_asset.hpp" class glaxnimate::model::Document::Private { public: using NameIndex = unsigned long long; Private(Document* doc) : assets(doc) { io_options.format = io::glaxnimate::GlaxnimateFormat::instance(); } std::pair name_index(const QString& name) const { static QRegularExpression detect_numbers("^(.*) ([0-9]+)$"); QRegularExpressionMatch match = detect_numbers.match(name); if ( match.hasMatch() ) { return {match.captured(1), match.captured(2).toULongLong()}; } return {name, 0}; } void increase(std::pair pair) { auto iter = name_indices.find(pair.first); if ( iter != name_indices.end() ) { if ( iter->second < pair.second ) iter->second = pair.second; } else { name_indices.emplace(std::move(pair)); } } void decrease(const std::pair& pair) { if ( pair.second == 0 ) return; auto iter = name_indices.find(pair.first); if ( iter != name_indices.end() ) { if ( iter->second == pair.second ) iter->second -= 1; } } QString name_suggestion(const QString& base_name) { auto index_pair = name_index(base_name); auto iter = name_indices.find(index_pair.first); if ( iter == name_indices.end() ) return base_name; return QString("%1 %2").arg(iter->first).arg(iter->second + 1); } int add_pending_asset(QUrl url, QByteArray data, const QString& name_alias) { int id = max_pending_id; ++max_pending_id; pending_assets[id] = {id, std::move(url), std::move(data), name_alias}; return id; } QUndoStack undo_stack; QVariantMap metadata; io::Options io_options; FrameTime current_time = 0; bool record_to_keyframe = false; Assets assets; glaxnimate::model::CompGraph comp_graph; std::unordered_map name_indices; std::map pending_assets; int max_pending_id = 0; DocumentInfo info; QUuid uuid; }; glaxnimate::model::Document::Document(const QString& filename) : d ( std::make_unique(this) ) { d->io_options.filename = filename; d->uuid = QUuid::createUuid(); } glaxnimate::model::Document::~Document() = default; QString glaxnimate::model::Document::filename() const { return d->io_options.filename; } QUuid glaxnimate::model::Document::uuid() const { return d->uuid; } QVariantMap & glaxnimate::model::Document::metadata() { return d->metadata; } QUndoStack & glaxnimate::model::Document::undo_stack() { return d->undo_stack; } const glaxnimate::io::Options & glaxnimate::model::Document::io_options() const { return d->io_options; } void glaxnimate::model::Document::set_io_options(const io::Options& opt) { bool em = opt.filename != d->io_options.filename; d->io_options = opt; if ( em ) Q_EMIT filename_changed(d->io_options.filename); } glaxnimate::model::DocumentNode * glaxnimate::model::Document::find_by_uuid(const QUuid& n) const { return d->assets.docnode_find_by_uuid(n); } glaxnimate::model::DocumentNode * glaxnimate::model::Document::find_by_name(const QString& name) const { return d->assets.docnode_find_by_name(name); } QVariantList glaxnimate::model::Document::find_by_type_name(const QString& type_name) const { return d->assets.find_by_type_name(type_name); } bool glaxnimate::model::Document::redo() { if ( ! d->undo_stack.canRedo() ) return false; d->undo_stack.redo(); return true; } bool glaxnimate::model::Document::undo() { if ( ! d->undo_stack.canUndo() ) return false; d->undo_stack.undo(); return true; } glaxnimate::model::FrameTime glaxnimate::model::Document::current_time() const { return d->current_time; } void glaxnimate::model::Document::set_current_time(glaxnimate::model::FrameTime t) { Q_EMIT current_time_changing(t); d->assets.set_time(t); Q_EMIT current_time_changed(d->current_time = t); } bool glaxnimate::model::Document::record_to_keyframe() const { return d->record_to_keyframe; } void glaxnimate::model::Document::set_record_to_keyframe(bool r) { Q_EMIT record_to_keyframe_changed(d->record_to_keyframe = r); } void glaxnimate::model::Document::push_command(QUndoCommand* cmd) { d->undo_stack.push(cmd); } QString glaxnimate::model::Document::get_best_name(glaxnimate::model::DocumentNode* node, const QString& suggestion) const { if ( !node ) return {}; if ( suggestion.isEmpty() ) return d->name_suggestion(node->type_name_human()); return d->name_suggestion(suggestion); } void glaxnimate::model::Document::set_best_name(glaxnimate::model::DocumentNode* node, const QString& suggestion) const { if ( node ) node->name.set(get_best_name(node, suggestion)); } glaxnimate::model::Assets * glaxnimate::model::Document::assets() const { return &d->assets; } glaxnimate::model::Object * glaxnimate::model::Document::assets_obj() const { return assets(); } void glaxnimate::model::Document::set_metadata(const QVariantMap& meta) { d->metadata = meta; } glaxnimate::model::CompGraph & glaxnimate::model::Document::comp_graph() { return d->comp_graph; } void glaxnimate::model::Document::decrease_node_name(const QString& old_name) { if ( !old_name.isEmpty() ) d->decrease(d->name_index(old_name)); } void glaxnimate::model::Document::increase_node_name(const QString& new_name) { if ( !new_name.isEmpty() ) d->increase(d->name_index(new_name)); } void glaxnimate::model::Document::stretch_time(qreal multiplier) { qreal time = d->current_time; d->assets.stretch_time(multiplier); set_current_time(qRound(time * multiplier)); } int glaxnimate::model::Document::add_pending_asset(const QString& name, const QByteArray& data) { return d->add_pending_asset({}, data, name); } int glaxnimate::model::Document::add_pending_asset(const QString& name, const QUrl& url) { return d->add_pending_asset(url, {}, name); } int glaxnimate::model::Document::add_pending_asset(const PendingAsset& ass) { return d->add_pending_asset(ass.url, ass.data, ass.name_alias); } void glaxnimate::model::Document::mark_asset_loaded(int id) { auto it = d->pending_assets.find(id); if ( it != d->pending_assets.end() ) it->second.loaded = true; } std::vector glaxnimate::model::Document::pending_assets() { std::vector assets; assets.reserve(d->pending_assets.size()); for ( const auto& ass : d->pending_assets ) assets.push_back(ass.second); return assets; } void glaxnimate::model::Document::clear_pending_assets() { for ( auto& ass : d->pending_assets ) ass.second.loaded = true; } glaxnimate::model::Document::DocumentInfo & glaxnimate::model::Document::info() { return d->info; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/range.hpp000664 001750 001750 00000001270 15165022620 031372 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::utils { template class Range { public: using iterator = Iterator; using value_type = typename Traits::value_type; Range(iterator begin, iterator end) : begin_(begin), end_(end) {} iterator begin() const { return begin_; } iterator end() const { return end_; } iterator cbegin() const { return begin_; } iterator cend() const { return end_; } private: iterator begin_; iterator end_; }; } // namespace glaxnimate::utils mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/factory.hpp000664 001750 001750 00000004752 15165022620 031715 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include "glaxnimate/utils/qstring_hash.hpp" namespace glaxnimate::model { class Object; class Document; class Composition; class ShapeElement; namespace detail { QString naked_type_name(QString class_name); inline QString naked_type_name(const QMetaObject* obj) { return naked_type_name(obj->className()); } template QString naked_type_name() { return naked_type_name(&T::staticMetaObject); } template class InternalFactory { private: class Builder { private: class Holder { public: virtual ~Holder() = default; virtual BaseType* construct(Args... args) const = 0; }; template class ConcreteHolder : public Holder { public: BaseType* construct(Args... args) const override { return new Type(args...); } }; public: template static Builder for_type() { return std::unique_ptr(std::make_unique>()); } BaseType* construct(Args... args) const { return constructor->construct(args...); } private: Builder(std::unique_ptr constructor) : constructor(std::move(constructor)) {} std::unique_ptr constructor; }; public: BaseType* build(const QString& name, Args... args) const { auto it = constructors.find(name); if ( it == constructors.end() ) return nullptr; return it->second.construct(args...); } template bool register_type() { constructors.emplace(detail::naked_type_name(), Builder::template for_type()); return true; } private: std::unordered_map constructors; }; } // namespace detail class Factory : public detail::InternalFactory { public: static Factory& instance() { static Factory instance; return instance; } static Object* static_build(const QString& name, model::Document* doc); private: ~Factory() = default; Factory() = default; Factory(const Factory&) = delete; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/qstring_exception.hpp000664 001750 001750 00000001027 15165022620 034043 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include namespace glaxnimate::utils { template class QStringException : public Base { protected: using Ctor = QStringException; public: QStringException(const QString& what) : Base(what.toStdString()) {} QString message() const { return QString(this->what()); } }; } // namespace glaxnimate::utils mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/assets.hpp000664 001750 001750 00000014233 15165022620 033045 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/object.hpp" #include "glaxnimate/model/property/object_list_property.hpp" #include "glaxnimate/model/property/sub_object_property.hpp" #include "glaxnimate/model/assets/named_color.hpp" #include "glaxnimate/model/assets/bitmap.hpp" #include "glaxnimate/model/assets/gradient.hpp" #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/model/assets/embedded_font.hpp" #include "glaxnimate/model/assets/network_downloader.hpp" namespace glaxnimate::model { namespace detail { DocumentNode* defs(model::Document* doc); } // detail template class AssetListBase : public DocumentNode { protected: using Ctor = AssetListBase; public: ObjectListProperty values{this, kli18n("values"), &AssetListBase::on_added, &AssetListBase::on_removed, &AssetListBase::docnode_child_add_begin, &AssetListBase::docnode_child_remove_begin, &AssetListBase::docnode_child_move_begin, &AssetListBase::docnode_child_move_end }; public: using DocumentNode::DocumentNode; DocumentNode* docnode_parent() const override { return detail::defs(document()); } int docnode_child_count() const override { return values.size(); } DocumentNode* docnode_child(int index) const override { return values[index]; } int docnode_child_index(DocumentNode* dn) const override { return values.index_of(static_cast(dn)); } QIcon instance_icon() const override { return tree_icon(); } protected: virtual void on_added(T* obj, int row) { obj->attach(); Q_EMIT docnode_child_add_end(obj, row); } virtual void on_removed(T* obj, int row) { obj->detach(); Q_EMIT docnode_child_remove_end(obj, row); } }; #define ASSET_LIST_CLASS(type) \ GLAXNIMATE_PROPERTY_LIST_IMPL(type, values) \ public: \ using Ctor::Ctor; \ // END class NamedColorList : public AssetListBase { GLAXNIMATE_OBJECT(NamedColorList) ASSET_LIST_CLASS(NamedColor) public: QIcon tree_icon() const override; QString type_name_human() const override { return i18n("Swatch"); } Q_SIGNALS: void color_changed(int position, model::NamedColor* color); void color_added(int position, model::NamedColor* color); void color_removed(int position, model::NamedColor* color); protected: void on_added(model::NamedColor* color, int position) override; void on_removed(model::NamedColor* color, int position) override; }; class BitmapList : public AssetListBase { GLAXNIMATE_OBJECT(BitmapList) ASSET_LIST_CLASS(Bitmap) public: QIcon tree_icon() const override; QString type_name_human() const override { return i18n("Images"); } }; class GradientColorsList : public AssetListBase { GLAXNIMATE_OBJECT(GradientColorsList) ASSET_LIST_CLASS(GradientColors) public: QIcon tree_icon() const override; QString type_name_human() const override { return i18n("Gradient Colors"); } }; class GradientList : public AssetListBase { GLAXNIMATE_OBJECT(GradientList) ASSET_LIST_CLASS(Gradient) public: QIcon tree_icon() const override; QString type_name_human() const override { return i18n("Gradients"); } }; class CompositionList : public AssetListBase { GLAXNIMATE_OBJECT(CompositionList) ASSET_LIST_CLASS(Composition) public: QIcon tree_icon() const override; protected: void on_added(model::Composition* obj, int position) override; void on_removed(model::Composition* obj, int position) override; QString type_name_human() const override { return i18n("Compositions"); } Q_SIGNALS: void precomp_added(model::Composition* obj, int position); }; class FontList : public AssetListBase { GLAXNIMATE_OBJECT(FontList) ASSET_LIST_CLASS(EmbeddedFont) public: QIcon tree_icon() const override { return QIcon::fromTheme("font"); } protected: void on_added(model::EmbeddedFont* obj, int position) override; Q_SIGNALS: void font_added(model::EmbeddedFont* font); protected: QString type_name_human() const override { return i18n("Fonts"); } }; class Assets : public DocumentNode { GLAXNIMATE_OBJECT(Assets) GLAXNIMATE_SUBOBJECT(NamedColorList, colors) GLAXNIMATE_SUBOBJECT(BitmapList, images) GLAXNIMATE_SUBOBJECT(GradientColorsList, gradient_colors) GLAXNIMATE_SUBOBJECT(GradientList, gradients) GLAXNIMATE_SUBOBJECT(CompositionList, compositions) GLAXNIMATE_SUBOBJECT(FontList, fonts) public: using DocumentNode::DocumentNode; Q_INVOKABLE glaxnimate::model::NamedColor* add_color(const QColor& color, const QString& name = {}); Q_INVOKABLE glaxnimate::model::Bitmap* add_image_file(const QString& filename, bool embed); Q_INVOKABLE glaxnimate::model::Bitmap* add_image(const QImage& image, const QString& store_as = "png"); Q_INVOKABLE glaxnimate::model::GradientColors* add_gradient_colors(int index = -1); Q_INVOKABLE glaxnimate::model::Gradient* add_gradient(int index = -1); Q_INVOKABLE glaxnimate::model::EmbeddedFont* add_font(const QByteArray& ttf_data); glaxnimate::model::EmbeddedFont* add_font(const CustomFont& font); Q_INVOKABLE glaxnimate::model::EmbeddedFont* font_by_index(int database_index) const; Q_INVOKABLE glaxnimate::model::Composition* add_composition(); glaxnimate::model::Composition* add_comp_no_undo(); DocumentNode* docnode_parent() const override; int docnode_child_count() const override; DocumentNode* docnode_child(int index) const override; int docnode_child_index(DocumentNode* dn) const override; QIcon tree_icon() const override; QIcon instance_icon() const override; QString type_name_human() const override { return i18n("Assets"); } NetworkDownloader network_downloader; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/log/log_line.hpp000664 001750 001750 00000001036 15165022620 031507 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include namespace glaxnimate::log { enum Severity { Info, Warning, Error, }; struct LogLine { Severity severity; QString source; QString source_detail; QString message; QDateTime time; }; } // namespace glaxnimate::log Q_DECLARE_METATYPE(glaxnimate::log::LogLine) Q_DECLARE_METATYPE(glaxnimate::log::Severity) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/000775 001750 001750 00000000000 15165022620 024274 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/brush_style.cpp000664 001750 001750 00000000756 15165022620 034106 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/brush_style.hpp" QIcon glaxnimate::model::BrushStyle::instance_icon() const { if ( !icon ) { icon = std::make_unique(32, 32); fill_icon(*icon); } return *icon; } QBrush glaxnimate::model::BrushStyle::constrained_brush_style(FrameTime t, const QRectF& ) const { return brush_style(t); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/network_downloader.cpp000664 001750 001750 00000000271 15165022620 035442 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/network_downloader.hpp" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/style/styler.cpp000664 001750 001750 00000003527 15165022620 034205 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/style/styler.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/named_color.hpp" std::vector glaxnimate::model::Styler::valid_uses() const { auto v = document()->assets()->gradients->values.valid_reference_values(true); auto v2 = document()->assets()->colors->values.valid_reference_values(false); v.insert(v.end(), v2.begin(), v2.end()); return v; } bool glaxnimate::model::Styler::is_valid_use(DocumentNode* node) const { return document()->assets()->gradients->values.is_valid_reference_value(node, true) || document()->assets()->colors->values.is_valid_reference_value(node, false); } void glaxnimate::model::Styler::on_use_changed(glaxnimate::model::BrushStyle* new_use, glaxnimate::model::BrushStyle* old_use) { QColor reset; if ( old_use ) { disconnect(old_use, &BrushStyle::style_changed, this, &Styler::on_update_style); if ( auto old_col = qobject_cast(old_use) ) reset = old_col->color.get(); } if ( new_use ) { connect(new_use, &BrushStyle::style_changed, this, &Styler::on_update_style); if ( auto new_col = qobject_cast(new_use) ) reset = new_col->color.get(); } if ( reset.isValid() ) color.set(reset); Q_EMIT use_changed(new_use); Q_EMIT use_changed_from(old_use, new_use); } void glaxnimate::model::Styler::on_update_style() { Q_EMIT property_changed(&use, use.value()); } QBrush glaxnimate::model::Styler::brush(FrameTime t) const { if ( use.get() ) return use->brush_style(t); return color.get_at(t); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/operations.cpp000664 001750 001750 00000013103 15165022620 033523 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/bezier/operations.hpp" #include using namespace glaxnimate; // Algorithm from https://www.particleincell.com/2012/bezier-splines/ void math::bezier::auto_smooth(math::bezier::Bezier& curve, int start, int end) { if ( start < 0 || end > curve.size() || end - start < 2 ) return; int n = end - start - 1; // rhs vector std::vector a, b, c; std::vector r; // left most segment a.push_back(0); b.push_back(2); c.push_back(1); r.push_back(curve[start].pos + 2 * curve[start+1].pos); // internal segments for ( int i = 1; i < n - 1; i++ ) { a.push_back(1); b.push_back(4); c.push_back(1); r.push_back(4 * curve[start+i].pos + 2 * curve[start+i+1].pos); } // right segment a.push_back(2); b.push_back(7); c.push_back(0); r.push_back(8 * curve[end-2].pos + curve[end-1].pos); // solves Ax=b with the Thomas algorithm (from Wikipedia) for ( int i = 1; i < n; i++ ) { qreal m = a[i] / b[i-1]; b[i] = b[i] - m * c[i - 1]; r[i] = r[i] - m * r[i-1]; } QPointF last = r[n-1]/b[n-1]; curve[end-2].tan_in = last; for ( int i = n - 2; i >= 0; --i ) { last = (r[i] - c[i] * last) / b[i]; QPointF relative = (last - curve[start+i].pos); curve[start+i].tan_in = curve[start+i].pos - relative; curve[start+i].tan_out = curve[start+i].pos + relative; curve[start+i].type = math::bezier::Smooth; } } static qreal triangle_area(const math::bezier::Bezier& curve, int point) { QPointF prev = curve[point-1].pos; QPointF here = curve[point].pos; QPointF next = curve[point+1].pos; return qAbs( prev.x() * here.y() - here.x() * prev.y() + here.x() * next.y() - next.x() * here.y() + next.x() * prev.y() - prev.x() * next.y() ); } void math::bezier::simplify(math::bezier::Bezier& curve, qreal threshold) { if ( curve.size() < 3 || threshold <= 0 ) return; // Algorithm based on https://bost.ocks.org/mike/simplify/ std::vector tris; tris.reserve(curve.size()); tris.push_back(threshold); // [0] not used but keeping it for my own sanity for ( int i = 1; i < curve.size() - 1; i++ ) tris.push_back(triangle_area(curve, i)); while ( !tris.empty() ) { qreal min = threshold; int index = -1; for ( int i = 0; i < int(tris.size()); i++ ) { if ( tris[i] < min ) { index = i; min = tris[i]; } } if ( index == -1 ) break; tris.erase(tris.begin() + index); curve.points().erase(curve.begin() + index); if ( index < int(tris.size()) ) tris[index] = triangle_area(curve, index); if ( index > 1 ) tris[index-1] = triangle_area(curve, index - 1); } // Fake smoothness auto_smooth(curve, 0, curve.size()); } static math::bezier::ProjectResult project_extreme(int index, qreal t, const QPointF& p) { return {index, t, math::length_squared(p), p}; } static void project_impl(const math::bezier::CubicBezierSolver& solver, const QPointF& p, int index, math::bezier::ProjectResult& best) { static constexpr const double min_dist = 0.01; math::bezier::ProjectResult left = project_extreme(index, 0, solver.points()[0]); math::bezier::ProjectResult right = project_extreme(index, 1, solver.points()[3]); math::bezier::ProjectResult middle = {index, 0, 0, {}}; while ( true ) { middle.factor = (left.factor + right.factor) / 2; middle.point = solver.solve(middle.factor); middle.distance = math::length_squared(middle.point); if ( right.distance < left.distance ) left = middle; else right = middle; auto len = math::length_squared(left.point - right.point); if ( len <= min_dist || !std::isfinite(len) ) break; } if ( right.distance < left.distance ) left = right; if ( left.distance < best.distance ) { best = left; best.point += p; } } static void project_impl(const math::bezier::Bezier& curve, const QPointF& p, int index, math::bezier::ProjectResult& best) { math::bezier::CubicBezierSolver solver{ curve[index].pos - p, curve[index].tan_out - p, curve[index + 1].tan_in - p, curve[index + 1].pos - p }; project_impl(solver, p, index, best); } math::bezier::ProjectResult math::bezier::project(const math::bezier::Bezier& curve, const QPointF& p) { if ( curve.empty() ) return {0, 0, 0, p}; if ( curve.size() == 1 ) return {0, 0, math::length_squared(curve[0].pos - p), curve[0].pos}; ProjectResult best {0, 0, std::numeric_limits::max(), curve[0].pos}; for ( int i = 0; i < curve.size() - 1; i++ ) project_impl(curve, p, i, best); if ( curve.closed() ) project_impl(curve, p, curve.size() - 1, best); return best; } math::bezier::ProjectResult math::bezier::project(const math::bezier::BezierSegment& segment, const QPointF& p) { ProjectResult best {0, 0, std::numeric_limits::max(), segment[0]}; math::bezier::CubicBezierSolver solver{ segment[0] - p, segment[1] - p, segment[2] - p, segment[3] - p }; project_impl(solver, p, 0, best); return best; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/pdf_format.cpp000664 001750 001750 00000004352 15165022620 033640 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/cairo/pdf_format.hpp" #include #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/app_info.hpp" #include "cairo_renderer.hpp" QStringList glaxnimate::cairo::PdfFormat::extensions(Direction) const { return {QStringLiteral("pdf")}; } static cairo_status_t write_to_io(void *closure, const unsigned char *data, unsigned int length) { ((QIODevice*)closure)->write((const char*)data, length); return CAIRO_STATUS_SUCCESS; } static void set_metadata(cairo_surface_t *surface, cairo_pdf_metadata_t metadata, const QString& string) { if ( !string.isEmpty() ) cairo_pdf_surface_set_metadata(surface, metadata, string.toStdString().c_str()); } bool glaxnimate::cairo::PdfFormat::on_save_static(QIODevice &dev, const QString &, model::Composition *comp, model::FrameTime time, const QVariantMap &) { CairoRenderer renderer(10); auto surface = cairo_pdf_surface_create_for_stream(&write_to_io, &dev, comp->width.get(), comp->height.get()); if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) { error(i18n("Could not create PDF surface: %1", QString(cairo_status_to_string(cairo_surface_status(surface))))); return false; } // Metadata set_metadata(surface, CAIRO_PDF_METADATA_TITLE, comp->name.get()); set_metadata(surface, CAIRO_PDF_METADATA_AUTHOR, comp->document()->info().author); set_metadata(surface, CAIRO_PDF_METADATA_SUBJECT, comp->document()->info().description); set_metadata(surface, CAIRO_PDF_METADATA_KEYWORDS, comp->document()->info().keywords.join(' ')); auto& info = glaxnimate::AppInfo::instance(); set_metadata(surface, CAIRO_PDF_METADATA_CREATOR, QStringLiteral("%1 %2").arg(info.name()).arg(info.version())); renderer.set_cairo_surface(surface, comp->width.get(), comp->height.get()); renderer.render_start(); comp->paint(&renderer, time, model::VisualNode::Render); renderer.render_end(); return true; } std::unique_ptr glaxnimate::cairo::PdfFormat::save_settings(model::Composition*) const { return {}; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/video/video_format.hpp000664 001750 001750 00000001535 15165022620 034213 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" namespace glaxnimate::video { class VideoFormat : public io::ImportExport { Q_OBJECT public: QString slug() const override { return "video"; } QString name() const override { return i18n("Video"); } QStringList extensions(Direction direction) const override; bool can_save() const override { return true; } bool can_open() const override { return false; } std::unique_ptr save_settings(model::Composition*) const override; int priority() const override { return -100; } protected: bool on_save(QIODevice& dev, const QString&, model::Composition* comp, const QVariantMap&) override; }; } // namespace glaxnimate::video mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/thorvg/CMakeLists.txt000664 001750 001750 00000000627 15165022620 033770 0ustar00ddennedyddennedy000000 000000 # SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia # SPDX-License-Identifier: BSD-2-Clause set(SOURCES thorvg_module.cpp ) add_library(GlaxnimateThorVG OBJECT ${SOURCES}) glaxnimate_enable_exceptions(GlaxnimateThorVG PUBLIC) add_subdirectory(../../../../../external/thorvg/ ${CMAKE_CURRENT_BINARY_DIR}/thorvg EXCLUDE_FROM_ALL) target_link_libraries(GlaxnimateThorVG PUBLIC thorvg) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/transform.hpp000664 001750 001750 00000002445 15165022620 032256 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/animation/animatable.hpp" #include "glaxnimate/model/object.hpp" #include #include namespace glaxnimate::model { class Transform : public Object { GLAXNIMATE_OBJECT(Transform) GLAXNIMATE_ANIMATABLE(QPointF, anchor_point, QPointF(0, 0)) GLAXNIMATE_ANIMATABLE(QPointF, position, QPointF(0, 0)) GLAXNIMATE_ANIMATABLE(QVector2D, scale, QVector2D(1, 1)) GLAXNIMATE_ANIMATABLE(float, rotation, 0, {}) GLAXNIMATE_PROPERTY(bool, auto_orient, false, {}, {}, PropertyTraits::Visual|PropertyTraits::Hidden) public: using Object::Object; virtual QIcon tree_icon() const override { return QIcon::fromTheme("node-transform"); } virtual QString type_name_human() const override { return i18n("Transform"); } QTransform transform_matrix(FrameTime f) const; void set_transform_matrix(const QTransform& t); /** * \brief Returns the transform at the given time using the given anchor point instead of the transform's */ QTransform transform_matrix_with_anchor(FrameTime f, const QPointF& anchor) const; void copy(Transform* other); }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/color.hpp000664 001750 001750 00000000716 15165022620 031420 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::utils::color { inline constexpr qint32 rgba_distance_squared(QRgb c1, qint32 r, qint32 g, qint32 b, qint32 a) noexcept { r -= qRed(c1); g -= qGreen(c1); b -= qBlue(c1); a -= qAlpha(c1); return r*r + g*g + b*b + a*a; } } // namespace glaxnimate::utils::color mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/000775 001750 001750 00000000000 15165022620 027712 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/log/000775 001750 001750 00000000000 15165022620 027206 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/polystar.hpp000664 001750 001750 00000003377 15165022620 034673 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/shape.hpp" namespace glaxnimate::model { class PolyStar : public Shape { GLAXNIMATE_OBJECT(PolyStar) public: enum StarType { Star = 1, Polygon = 2, }; Q_ENUM(StarType) GLAXNIMATE_PROPERTY(StarType, type, Star, {}, {}, PropertyTraits::Visual) GLAXNIMATE_ANIMATABLE(QPointF, position, QPointF()) GLAXNIMATE_ANIMATABLE(float, outer_radius, 0, {}, 0) GLAXNIMATE_ANIMATABLE(float, inner_radius, 0, {}, 0) GLAXNIMATE_ANIMATABLE(float, angle, 0, {}) GLAXNIMATE_ANIMATABLE(int, points, 5) GLAXNIMATE_ANIMATABLE(float, outer_roundness, 0, {}, 0, 100, PropertyTraits::Percent) GLAXNIMATE_ANIMATABLE(float, inner_roundness, 0, {}, 0, 100, PropertyTraits::Percent) public: using Shape::Shape; QIcon tree_icon() const override { if ( type.get() == Star ) return QIcon::fromTheme("draw-star"); return QIcon::fromTheme("draw-polygon"); } QString type_name_human() const override { return i18n("PolyStar"); } math::bezier::Bezier to_bezier(FrameTime t) const override; QRectF local_bounding_rect(FrameTime t) const override { float radius = qMax(this->outer_radius.get_at(t), this->inner_radius.get_at(t)); return QRectF(position.get_at(t) - QPointF(radius, radius), QSizeF(radius*2, radius*2)); } bool is_rounded() const; static math::bezier::Bezier draw( StarType type, const QPointF& pos, float r_in, float r_out, float angle_radians, int p, float round_in, float round_out, bool reverse); }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_html_format.hpp000664 001750 001750 00000002171 15165022620 033540 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/lottie/lottie_html_format.hpp" #include "glaxnimate/io/svg/svg_renderer.hpp" namespace glaxnimate::io::svg { class SvgHtmlFormat : public ImportExport { public: QString slug() const override { return "svg_html"; } QString name() const override { return i18n("SVG Preview"); } QStringList extensions(Direction) const override { return {"html", "htm"}; } bool can_save() const override { return true; } bool can_open() const override { return false; } private: bool on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap&) override { file.write(lottie::LottieHtmlFormat::html_head(this, comp, {})); file.write("
"); SvgRenderer rend(SMIL, CssFontType::FontFace); rend.write_main(comp, comp->document()->current_time()); rend.write(&file, true); file.write("
"); return true; } }; } // namespace glaxnimate::io::svg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/binary_stream.hpp000664 001750 001750 00000002101 15165022620 032376 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/io/binary_types.hpp" namespace glaxnimate::io { class BinaryInputStream { public: explicit BinaryInputStream(QIODevice* file); explicit BinaryInputStream(QByteArray data); quint32 read_uint32_le(); Float32 read_float32_le(); VarUint read_uint_leb128(); bool eof() const; bool has_error() const; QByteArray read(qint64 max_size); quint8 next(); private: void on_overflow(); private: QByteArray data; const char* data_start; const char* data_end; bool error = false; }; class BinaryOutputStream { public: explicit BinaryOutputStream(QIODevice* file); void write_uint32_le(quint32 v); void write_float32_le(Float32 v); void write_uint_leb128(VarUint v); void write_byte(quint8 v); void write(const QByteArray& data); private: QIODevice* file = nullptr; }; } // namespace glaxnimate::io mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/detail.hpp000664 001750 001750 00000006057 15165022620 031616 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include "glaxnimate/utils/qstring_hash.hpp" namespace glaxnimate::io::svg::detail { extern const std::map xmlns; struct Style { public: using Map = std::map; Style(Map&& map) : map(std::move(map)) {} Style() = default; QString& operator[](const QString& s) { return map[s]; } const QString& operator[](const QString& s) const { return map.at(s); } const QString& get(const QString& k, const QString& def = {}) const { auto it = map.find(k); if ( it == map.end() ) return def; return it->second; } void set(const QString& k, const QString& v) { map[k] = v; } bool contains(const QString& k) const { return map.count(k); } Map map; QColor color = Qt::black; }; extern const std::unordered_set css_atrrs; template struct ItemCountRange { // using value_type = decltype(std::declval().item(0)); struct iterator { auto operator*() const { return range->dom_list.item(index); } iterator& operator++() { index++; return *this; } bool operator != (const iterator& it) const { return range != it.range || index != it.index; } const ItemCountRange* range; int index; }; ItemCountRange(const T& dom_list) : dom_list(dom_list) {} iterator begin() const { return {this, 0}; } iterator end() const { return {this, dom_list.count()}; } int size() const { return dom_list.count(); } T dom_list; }; struct ElementRange { struct iterator { auto operator*() const { return range->dom_list.item(index).toElement(); } iterator& operator++() { index++; while ( index < range->dom_list.count() && !acceptable() ) index++; return *this; } bool operator != (const iterator& it) const { return range != it.range || index != it.index; } bool acceptable() const { if ( !range->dom_list.item(index).isElement() ) return false; if ( range->tag_name.isEmpty() ) return true; return range->dom_list.item(index).toElement().tagName() == range->tag_name; } const ElementRange* range; int index; }; ElementRange(const QDomNodeList& dom_list, QString tag_name = {}) : dom_list(dom_list), tag_name(std::move(tag_name)) {} ElementRange(const QDomElement& el, QString tag_name = {}) : dom_list(el.childNodes()), tag_name(std::move(tag_name)) {} iterator begin() const { return {this, 0}; } iterator end() const { return {this, dom_list.count()}; } int size() const { return dom_list.count(); } QDomNodeList dom_list; QString tag_name; }; } // io::svg::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/edit_utils.hpp000664 001750 001750 00000002222 15165022620 034360 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/property/sub_object_property.hpp" #include "glaxnimate/model/animation/animatable.hpp" namespace glaxnimate::model { inline void all_animated_properties(model::Object* object, std::vector& out) { for ( auto prop : object->properties() ) { if ( prop->traits().flags & PropertyTraits::Animated ) { out.push_back(static_cast(prop)); } else if ( prop->traits().type == PropertyTraits::Object ) { if ( !(prop->traits().flags & PropertyTraits::List) ) all_animated_properties(static_cast(prop)->sub_object(), out); } } } /** * @brief Finds all animated properties in the object and direct subobjects */ inline std::vector all_animated_properties(model::Object* object) { std::vector out; all_animated_properties(object, out); return out; } } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/clipboard.hpp000664 001750 001750 00000002760 15165022620 032520 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/document_node.hpp" #include "glaxnimate/io/mime/mime_serializer.hpp" namespace glaxnimate::command { QMimeData* copy_helper( const std::vector< glaxnimate::model::DocumentNode* > nodes, const std::vector< glaxnimate::io::mime::MimeSerializer* >& supported_mimes); struct ClipboardKeyframe { model::FrameTime time = 0; QVariant value = {}; }; struct ClipboardProperty { int property_type = model::PropertyTraits::Unknown; model::KeyframeContainer keyframes = {}; }; struct ClipboardProperties { std::vector properties; const ClipboardProperty* by_type(int property_type) const { for ( const auto& prop : properties ) if ( prop.property_type == property_type ) return ∝ return nullptr; } }; QMimeData* keyframes_to_mime_data(const ClipboardProperties &selection); QMimeData* keyframe_to_mime_data(int property_type, const QVariant& value); ClipboardProperties keyframes_from_mime_data(const QMimeData* data); bool keyframes_mime_data_has_type(const QMimeData *data, int property_type); QUndoCommand* keyframes_paste_command(const QMimeData *data, model::AnimatableBase* target_property, int property_type, model::FrameTime start_time); bool has_keyframe_data(const QMimeData *data); } // namespace glaxnimate::command mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/000775 001750 001750 00000000000 15165022620 031411 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/application_info_generated.in.hpp000664 001750 001750 00000000756 15165022620 035107 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #define PROJECT_VERSION "${PROJECT_VERSION}" #define PROJECT_SLUG "${PROJECT_SLUG}" #define PROJECT_NAME "${PROJECT_NAME}" #define PROJECT_ID "${PROJECT_ID}" #define URL_DOCS "${URL_DOCS}" #define URL_ISSUES "${URL_ISSUES}" #define URL_DONATE "${URL_DONATE}" #define PROJECT_DESCRIPTION "${PROJECT_DESCRIPTION}" #define PROJECT_HOMEPAGE_URL "${PROJECT_HOMEPAGE_URL}" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/000775 001750 001750 00000000000 15165022620 031504 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/gzip_module.cpp000664 001750 001750 00000001047 15165022620 033707 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "gzip_module.hpp" #include "tgs_format.hpp" #include "svgz_format.hpp" #include std::vector glaxnimate::gzip::Module::components() const { return {{QStringLiteral("zlib"), {}, ZLIB_VERSION, QStringLiteral("https://zlib.net/"), "Custom"}}; } void glaxnimate::gzip::Module::initialize() { register_io_classes(); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/aep_loader.cpp000664 001750 001750 00000155640 15165022620 035731 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/aep/aep_loader.hpp" #include "glaxnimate/model/shapes/shapes/rect.hpp" #include "glaxnimate/model/shapes/shapes/ellipse.hpp" #include "glaxnimate/model/shapes/shapes/text.hpp" #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/shapes/style/fill.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include "glaxnimate/model/shapes/modifiers/trim.hpp" #include "glaxnimate/model/shapes/modifiers/offset_path.hpp" #include "glaxnimate/model/shapes/modifiers/inflate_deflate.hpp" #include "glaxnimate/model/shapes/modifiers/zig_zag.hpp" #include "glaxnimate/model/shapes/modifiers/round_corners.hpp" #include "glaxnimate/model/shapes/modifiers/repeater.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" using namespace glaxnimate::io::aep; using namespace glaxnimate; using glaxnimate::io::ImportExport; static constexpr std::array label_colors = { 0x00000000, // None 0xffb4393b, // Red 0xffe2d759, // Yellow 0xffabcbc8, // Aqua 0xffe5bcca, // Pink 0xffa9aac9, // Lavender 0xffe5c19f, // Peach 0xffb4c7b4, // Sea Foam 0xff687fdd, // Blue 0xff4ea350, // Green 0xff8d3299, // Purple 0xffe79228, // Orange 0xff7e442c, // Brown 0xfff371d5, // Fuchsia 0xff43a2a4, // Cyan 0xffa7967a, // Sandstone 0xff203f1f // Dark Green }; static QMap layer_blend_modes = { { 2, renderer::BlendMode::Normal}, {10, renderer::BlendMode::Darken}, { 5, renderer::BlendMode::Multiply}, {28, renderer::BlendMode::ColorBurn}, {30, renderer::BlendMode::ColorBurn}, // Linear Burn {36, renderer::BlendMode::Darken}, // Darker Color {11, renderer::BlendMode::Lighten}, { 6, renderer::BlendMode::Screen}, {27, renderer::BlendMode::ColorDodge}, {29, renderer::BlendMode::ColorDodge}, // Linear Dodge {35, renderer::BlendMode::Lighten}, // Lighter Color { 7, renderer::BlendMode::Overlay}, { 8, renderer::BlendMode::SoftLight}, { 9, renderer::BlendMode::HardLight}, {31, renderer::BlendMode::SoftLight}, // Linear Light {32, renderer::BlendMode::SoftLight}, // Vivid Light {33, renderer::BlendMode::HardLight}, // Pin Light {34, renderer::BlendMode::Normal}, // Hard Mix (exported by lottie) {26, renderer::BlendMode::Difference}, {25, renderer::BlendMode::Exclusion}, {13, renderer::BlendMode::Hue}, {14, renderer::BlendMode::Saturation}, {15, renderer::BlendMode::Color}, {16, renderer::BlendMode::Luminosity}, { 4, renderer::BlendMode::Add}, {22, renderer::BlendMode::Normal}, // Alpha Add {24, renderer::BlendMode::ColorBurn}, // Classic Color Burn {12, renderer::BlendMode::Difference}, // Classic Difference {38, renderer::BlendMode::Difference}, // Divide {21, renderer::BlendMode::Normal}, // Luminescent Premul {19, renderer::BlendMode::Normal}, // Silhouette Alpha {20, renderer::BlendMode::Normal}, // Silhouette Luma {17, renderer::BlendMode::Normal}, // Stencil Alpha {18, renderer::BlendMode::Normal}, // Stencil Luma {37, renderer::BlendMode::Difference}, // Subtract }; static QMap shape_blend_modes = { { 1, renderer::BlendMode::Normal}, { 3, renderer::BlendMode::Darken}, { 4, renderer::BlendMode::Multiply}, { 5, renderer::BlendMode::ColorBurn}, { 6, renderer::BlendMode::ColorBurn}, // Linear Burn { 7, renderer::BlendMode::Darken}, // Darker Color { 9, renderer::BlendMode::Lighten}, {10, renderer::BlendMode::Screen}, {11, renderer::BlendMode::ColorDodge}, {12, renderer::BlendMode::ColorDodge}, // Linear Dodge {13, renderer::BlendMode::Lighten}, // Lighter Color {15, renderer::BlendMode::Overlay}, {16, renderer::BlendMode::SoftLight}, {17, renderer::BlendMode::HardLight}, {18, renderer::BlendMode::SoftLight}, // Linear Light {19, renderer::BlendMode::SoftLight}, // Vivid Light {20, renderer::BlendMode::HardLight}, // Pin Light {21, renderer::BlendMode::Normal}, // Hard Mix (exported by lottie) {23, renderer::BlendMode::Difference}, {24, renderer::BlendMode::Exclusion}, {26, renderer::BlendMode::Hue}, {27, renderer::BlendMode::Saturation}, {28, renderer::BlendMode::Color}, {29, renderer::BlendMode::Luminosity}, }; void glaxnimate::io::aep::AepLoader::load_project() { for ( const auto& comp : project.compositions ) get_comp(comp->id); for ( const auto& pair : project.assets ) load_asset(pair.second); for ( const auto& comp : project.compositions ) load_comp(*comp); } void glaxnimate::io::aep::AepLoader::load_asset(const glaxnimate::io::aep::FolderItem* item) { if ( item->type() == FolderItem::Asset ) { auto image = std::make_unique(document); auto asset = static_cast(item); if ( asset->path.exists() ) { image->filename.set(asset->path.filePath()); } else { // Handle collected assets QFileInfo path(asset_path.filePath(asset->path.fileName())); if ( !path.exists() ) warning(i18n("External asset not found: %1", asset->path.filePath())); else image->filename.set(path.filePath()); } image->name.set(item->name); images[item->id] = image.get(); document->assets()->images->values.insert(std::move(image)); asset_size[item->id] = QPointF(asset->width, asset->height); } else if ( item->type() == FolderItem::Solid ) { auto color = std::make_unique(document); auto solid = static_cast(item); color->color.set(solid->color); color->name.set(solid->name); colors[item->id] = {color.get(), solid}; document->assets()->colors->values.insert(std::move(color)); asset_size[item->id] = QPointF(solid->width, solid->height); } else if ( item->type() == FolderItem::Composition ) { auto aecomp = static_cast(item); asset_size[item->id] = QPointF(aecomp->width, aecomp->height); auto comp = get_comp(item->id); comp->width.set(aecomp->width); comp->height.set(aecomp->height); comp->name.set(aecomp->name); } } void glaxnimate::io::aep::AepLoader::warning(const QString& msg) { io->warning(msg); } void glaxnimate::io::aep::AepLoader::info(const QString& msg) { io->information(msg); } static bool unknown_mn(glaxnimate::io::ImportExport* io, const QString& context, const QString& mn) { io->information(i18n("Unknown property \"%1\" of \"%2\"", mn, context)); return true; } model::Composition * glaxnimate::io::aep::AepLoader::get_comp(glaxnimate::io::aep::Id id) { if ( !id ) return nullptr; auto& comp = comps[id]; if ( !comp ) comp = document->assets()->add_comp_no_undo(); return comp; } struct glaxnimate::io::aep::AepLoader::LayerData { Layer* ae_layer = nullptr; model::Layer* layer = nullptr; model::Composition* matte_comp = nullptr; LayerData* parent = nullptr; LayerData* matte_parent = nullptr; int matte_count = 0; int child_count = 0; bool finished = false; bool skip = false; std::unique_ptr external; QString name() const { return ae_layer->name.isEmpty() ? i18n("Layer #%1", ae_layer->id) : ae_layer->name; } }; struct glaxnimate::io::aep::AepLoader::CompData { LayerData* by_id(Id id) { auto it = layer_ids.find(id); if ( it == layer_ids.end() ) return nullptr; return &layers[it->second]; } model::Composition* comp; const Composition* ae_comp; model::ObjectListProperty* shapes; std::unordered_map layer_ids = {}; std::vector layers = {}; }; void glaxnimate::io::aep::AepLoader::load_comp(const glaxnimate::io::aep::Composition& ae_comp) { auto comp = get_comp(ae_comp.id); comp->name.set(ae_comp.name); comp->width.set(ae_comp.width); comp->height.set(ae_comp.height); comp->fps.set(1 / ae_comp.frame_time); comp->animation->first_frame.set(ae_comp.in_time); comp->animation->last_frame.set(ae_comp.out_time); comp->group_color.set(ae_comp.color); comp->group_color.set(label_colors[int(ae_comp.label_color)]); CompData data{comp, &ae_comp, &comp->shapes}; data.layers.reserve(ae_comp.layers.size()); // Pass 1 collect parenting info for ( const auto& layer : ae_comp.layers ) { data.layer_ids[layer->id] = data.layers.size(); data.layers.push_back({}); data.layers.back().ae_layer = layer.get(); } // Pass 2 update child counters for ( auto& layer : data.layers ) { if ( layer.ae_layer->parent_id ) { if ( auto p = data.by_id(layer.ae_layer->parent_id) ) { layer.parent = p; p->child_count++; } else { warning(i18n("Parent #%1 of layer %2 could not be found", layer.ae_layer->parent_id, layer.name())); } } if ( layer.ae_layer->matte_id ) { if ( auto p = data.by_id(layer.ae_layer->matte_id) ) { layer.matte_parent = p; p->matte_count++; } else { warning(i18n("Mask parent #%1 of layer %2 could not be found", layer.ae_layer->matte_id, layer.name())); } } } // Pass 3 externalize multi-matte layers for ( auto& layer : data.layers ) { if ( layer.matte_count > 0 ) { if ( layer.matte_count > 1 || layer.child_count > 0 || layer.parent ) { auto inner_comp = document->assets()->compositions->values.insert(std::make_unique(document)); inner_comp->name.set(layer.ae_layer->name); inner_comp->width.set(ae_comp.width); inner_comp->height.set(ae_comp.height); inner_comp->fps.set(1 / ae_comp.frame_time); inner_comp->animation->last_frame.set(ae_comp.out_time); inner_comp->animation->first_frame.set(ae_comp.in_time); inner_comp->group_color.set(label_colors[int(layer.ae_layer->label_color)]); layer.matte_comp = inner_comp; model::PreCompLayer* external_link = nullptr; if ( layer.child_count > 0 || layer.parent ) { layer.external = std::make_unique(document); auto precomp = std::make_unique(document); external_link = precomp.get(); precomp->composition.set(inner_comp); layer.external->shapes.insert(std::move(precomp)); } CompData sub_data{inner_comp, &ae_comp, &inner_comp->shapes}; load_layer(layer, sub_data); if ( layer.external ) { layer.external->name.set(layer.layer->name.get()); layer.external->group_color.set(layer.layer->group_color.get()); external_link->name.set(layer.layer->name.get()); external_link->group_color.set(layer.layer->group_color.get()); layer.layer = layer.external.get(); } } layer.skip = true; } } // Pass 4 load layers for ( auto& layer : data.layers ) { if ( layer.external ) data.comp->shapes.insert(std::move(layer.external), 0); else if ( !layer.skip ) load_layer(layer, data); } // Pass 5 resolve parenting for ( const auto& layer : data.layers ) { if ( layer.parent ) layer.layer->parent.set(layer.parent->layer); } } namespace { template T convert_value(const PropertyValue& v) { return std::get(v.value); } template<> float convert_value(const PropertyValue& v) { return convert_value(v); } template<> int convert_value(const PropertyValue& v) { return convert_value(v); } template<> QPointF convert_value(const PropertyValue& v) { if ( v.type() == PropertyValue::Vector2D ) return std::get(v.value); auto p = convert_value(v.value); return {p.x(), p.y()}; } template<> QVector2D convert_value(const PropertyValue& v) { if ( v.type() == PropertyValue::Vector2D ) { auto p = std::get(v.value); return QVector2D(p.x(), p.y()); } else { auto p = convert_value(v.value); return {p.x(), p.y()}; } } template<> QSizeF convert_value(const PropertyValue& v) { auto p = convert_value(v.value); return {p.x(), p.y()}; } template<> math::bezier::Bezier convert_value(const PropertyValue& v) { const auto& aebez = std::get(v.value); math::bezier::Bezier bez; int count = aebez.points.size(); for ( int i = 0; i < count; i += 3 ) { /// \todo smooth etc? math::bezier::Point p(aebez.convert_point(aebez.points[i])); if ( i > 0 ) p.tan_in = aebez.convert_point(aebez.points[i-1]); else p.tan_in = aebez.convert_point(aebez.points.back()); p.tan_out = aebez.convert_point(aebez.points[i+1]); if ( i == count - 1 && aebez.closed && math::fuzzy_compare(bez[0].pos, p.pos) ) { bez[0].tan_in = p.tan_in; break; } bez.push_back(p); } bez.set_closed(aebez.closed); return bez; } template<> QGradientStops convert_value(const PropertyValue& v) { return convert_value(v).to_qt(); } template struct DefaultConverter { T operator()(const PropertyValue& v) const { return convert_value(v); } }; template> bool load_property(model::Property& prop, const Property& ae_prop, const Converter& conv = {}) { if ( ae_prop.value.type() ) prop.set(conv(ae_prop.value)); else if ( !ae_prop.keyframes.empty() && ae_prop.keyframes[0].value.type() ) prop.set(conv(ae_prop.keyframes[0].value)); else return false; return true; } template void kf_extra_data(model::Keyframe* kf, const Keyframe& aekf) { (void)kf; (void)aekf; } template<> void kf_extra_data(model::Keyframe* kf, const Keyframe& aekf) { auto p = kf->get(); kf->set_point(math::bezier::Point( p, p + aekf.in_tangent, p + aekf.out_tangent )); } qreal vector_length(const std::vector& v) { qreal len = 0; for ( double a : v ) len += a * a; return math::sqrt(len); } model::KeyframeTransition keyframe_transition(const Property& prop, const Keyframe& kf, const Keyframe& next_kf) { qreal duration = next_kf.time - kf.time; if ( qFuzzyIsNull(duration) ) return model::KeyframeTransition(model::KeyframeTransition::Linear); qreal average_speed = 0; if ( prop.type == PropertyType::Position ) { math::bezier::BezierSegment bez; if ( kf.value.type() == PropertyValue::Vector2D ) { bez[0] = std::get(kf.value.value); bez[3] = std::get(next_kf.value.value); } else { auto p = std::get(kf.value.value); bez[0] = {p.x(), p.y()}; p = std::get(next_kf.value.value); bez[3] = {p.x(), p.y()}; } bez[1] = kf.out_tangent; bez[2] = kf.in_tangent; average_speed = math::bezier::LengthData(math::bezier::CubicBezierSolver(bez), 20).length(); } else if ( prop.type == PropertyType::NoValue ) { average_speed = 1; } else { average_speed = math::abs(kf.value.magnitude() - next_kf.value.magnitude()); } average_speed /= duration; qreal out_influence = vector_length(kf.out_influence); qreal in_influence = vector_length(kf.in_influence); qreal out_speed = vector_length(kf.out_speed); qreal in_speed = vector_length(kf.in_speed); QPointF ease_out; QPointF ease_in; ease_out.setX(out_influence); ease_in.setX(1 - in_influence); if ( qFuzzyIsNull(average_speed) ) { ease_out.setY(out_influence); ease_in.setY(1 - in_influence); } else { ease_out.setY(out_influence * out_speed / average_speed); ease_in.setY(1 - in_influence * in_speed / average_speed); } return model::KeyframeTransition(ease_out, ease_in); } template> bool load_property( model::AnimatedProperty& prop, const Property& ae_prop, const Converter& conv = {} ) { if ( !ae_prop.animated && ae_prop.value.type() ) { prop.set(conv(ae_prop.value)); return true; } for ( std::size_t i = 0; i < ae_prop.keyframes.size(); i++ ) { const auto& aekf = ae_prop.keyframes[i]; auto kf = prop.set_keyframe(aekf.time, conv(aekf.value)); kf_extra_data(kf, aekf); /// \todo easing if ( aekf.transition_type == KeyframeTransitionType::Hold ) kf->set_transition(model::KeyframeTransition(model::KeyframeTransition::Hold)); else if ( aekf.transition_type == KeyframeTransitionType::Linear ) kf->set_transition(model::KeyframeTransition(model::KeyframeTransition::Linear)); else if ( i + 1 < ae_prop.keyframes.size() ) kf->set_transition(keyframe_transition(ae_prop, aekf, ae_prop.keyframes[i+1])); } return true; } template> void load_property_check( ImportExport* io, PropT& prop, const PropertyBase& ae_prop, const QString& match_name, const Converter& conv = {} ) { if ( ae_prop.class_type() != PropertyBase::Property ) { io->warning(i18n("Expected property for %1", match_name)); return; } try { if ( !load_property(prop, static_cast(ae_prop), conv) ) io->warning(i18n("Could convert %1", match_name)); } catch ( const std::bad_variant_access& ) { io->error(i18n("Invalid value for %1", match_name)); } } template> bool load_property(ImportExport* io, PropT& prop, const PropertyPair& ae_prop, const char* match_name, const Converter& conv = {}) { if ( ae_prop.match_name != match_name ) return false; load_property_check(io, prop, *ae_prop.value, ae_prop.match_name, conv); return true; } bool convert_shape_reverse(const PropertyValue& v) { return convert_value(v) == 3; } template T convert_divide(const PropertyValue& v) { return convert_value(v) / Divisor; } template T convert_enum(const PropertyValue& v) { return T(convert_value(v)); } template<> model::Fill::Rule convert_enum(const PropertyValue& v) { if ( convert_value(v) == 2 ) return model::Fill::Rule::EvenOdd; return model::Fill::Rule::NonZero; } template<> model::Stroke::Cap convert_enum(const PropertyValue& v) { switch ( convert_value(v) ) { default: case 1: return model::Stroke::Cap::ButtCap; case 2: return model::Stroke::Cap::RoundCap; case 3: return model::Stroke::Cap::SquareCap; } } template<> model::Stroke::Join convert_enum(const PropertyValue& v) { switch ( convert_value(v) ) { default: case 1: return model::Stroke::Join::MiterJoin; case 2: return model::Stroke::Join::RoundJoin; case 3: return model::Stroke::Join::BevelJoin; } } template<> renderer::BlendMode convert_enum(const PropertyValue& v) { return shape_blend_modes.value(convert_value(v), renderer::BlendMode::Normal); } struct AnchorMult { QPointF operator()(const PropertyValue& v) const { auto a = convert_value(v); return {a.x() * p.x(), a.y() * p.y()}; } QPointF p; }; bool load_position_component(io::ImportExport* io, const PropertyGroup& group, int suffix, model::AnimatedProperty& out, bool force) { auto pair = group.get_pair(QString("ADBE Position_%1").arg(suffix)); if ( !pair ) return false; if ( pair->value->class_type() != PropertyBase::Property ) return false; const Property& prop = static_cast(*pair->value); if ( !prop.is_component && !force ) return false; load_property_check(io, out, prop, pair->match_name); return true; } void load_transform(io::ImportExport* io, model::Transform* tf, const PropertyBase& prop, model::AnimatedProperty* opacity, const QPointF& anchor_mult, bool divide_100) { if ( prop.class_type() != PropertyBase::PropertyGroup ) { io->warning(i18n("Expected property group for transform")); return; } const PropertyGroup& g = static_cast(prop); bool is_3d = false; int split_position = 1; for ( const auto& p : g.properties ) { if ( p.match_name.endsWith("Anchor Point") || p.match_name.endsWith("Anchor") ) load_property_check(io, tf->anchor_point, *p.value, p.match_name, AnchorMult{anchor_mult}); else if ( p.match_name.endsWith("Position") ) { if ( p.value->class_type() == PropertyBase::Property ) { const Property& pos_prop = static_cast(*p.value); if ( pos_prop.split ) { split_position = 2; } else { split_position = 0; load_property_check(io, tf->position, *p.value, p.match_name); } } } else if ( p.match_name.endsWith("Scale") ) load_property_check(io, tf->scale, *p.value, p.match_name, divide_100 ? &convert_divide<100, QVector2D> : &convert_divide<1, QVector2D>); else if ( p.match_name.endsWith("Rotation") || p.match_name.endsWith("Rotate Z") ) load_property_check(io, tf->rotation, *p.value, p.match_name); else if ( opacity && p.match_name.endsWith("Opacity") ) load_property_check(io, *opacity, *p.value, p.match_name, divide_100 ? &convert_divide<100> : &convert_divide<1>); else if ( p.match_name.endsWith("Rotate X") || p.match_name.endsWith("Rotate Y") || p.match_name.endsWith("Orientation") || p.match_name.endsWith("Position_2") ) is_3d = true; else if ( !p.match_name.endsWith("Position_1") && !p.match_name.endsWith("Position_0") && !p.match_name.endsWith("Opacity") && !p.match_name.endsWith("Opacity 1") && !p.match_name.endsWith("Opacity 2") && !p.match_name.endsWith("Envir Appear in Reflect") ) io->information(i18n("Unknown property \"%1\"", p.match_name)); } if ( split_position ) { model::Document dummydoc(""); model::Object dummy(&dummydoc); model::AnimatedProperty ax(&dummy, {}, 0); model::AnimatedProperty ay(&dummy, {}, 0); bool force_split = split_position == 2; bool xok = load_position_component(io, g, 0, ax, force_split); bool yok = load_position_component(io, g, 1, ay, force_split); if ( split_position == 1 ) force_split = xok || yok; if ( force_split ) { model::JoinAnimatables join({&ax, &ay}); join.apply_to(&tf->position, [](float x, float y) -> QPointF { return QPointF(x, y); }, &ax, &ay); } } if ( is_3d ) { /// \todo figure a way of determining whether the transform is actually 3D /// as layer transforms seem to often have the 3D properties (void)is_3d; // warning(i18n("3D transforms are not supported")); } } template struct PropertyConverterBase { virtual ~PropertyConverterBase() noexcept = default; virtual void load(ImportExport* io, Obj* object, const PropertyBase& ae_prop) const = 0; virtual void set_default(Obj* object) const = 0; }; template> struct PropertyConverter : PropertyConverterBase { PropertyConverter(PropT (Base::*prop), const char* match_name, const Converter& converter, const std::optional& default_value = {}) : prop(prop), match_name(match_name), converter(converter), default_value(default_value) {} void load(ImportExport* io, Obj* object, const PropertyBase& ae_prop) const override { load_property_check(io, object->*prop, ae_prop, match_name, converter); } void set_default(Obj* object) const override { if ( default_value ) (object->*prop).set(*default_value); } PropT Base::*prop; QString match_name; Converter converter = {}; std::optional default_value ; }; template struct ObjectConverterBase { virtual ~ObjectConverterBase() noexcept = default; virtual std::unique_ptr load(ImportExport* io, model::Document* document, const PropertyPair& prop) const = 0; }; template struct ObjectConverterFunctor : public ObjectConverterBase { template ObjectConverterFunctor(F&& functor) : functor(std::forward(functor)) {} std::unique_ptr load(ImportExport* io, model::Document* document, const PropertyPair& prop) const override { return functor(io, document, prop); } FuncT functor; }; struct FallbackConverterBase { virtual ~FallbackConverterBase() noexcept = default; virtual void set_default() const = 0; virtual void load_property(ImportExport* io, model::Document* document, const PropertyPair& prop_parent, const PropertyPair& prop) const = 0; }; template struct FallbackConverter; template struct ObjectConverter : public ObjectConverterBase { std::unique_ptr load(ImportExport* io, model::Document* document, const PropertyPair& prop) const override { return load_object(io, document, prop); } void set_default(Obj* object, FallbackConverterBase* fallback) const { for ( const auto& conv : converters ) if ( conv.second ) conv.second->set_default(object); if ( fallback ) fallback->set_default(); } void load_property(Obj* object, ImportExport* io, model::Document* document, const PropertyPair& prop_parent, const PropertyPair& prop, FallbackConverterBase* fallback) const { auto it = converters.find(prop.match_name); if ( it == converters.end() ) { if ( fallback ) fallback->load_property(io, document, prop_parent, prop); else unknown_mn(io, prop_parent.match_name, prop.match_name); } else if ( it->second ) { it->second->load(io, object, *prop.value); } } void load_properties(Obj* object, ImportExport* io, model::Document* document, const PropertyPair& prop, FallbackConverterBase* fallback = nullptr) const { set_default(object, fallback); for ( const auto& p : *prop.value ) this->load_property(object, io, document, prop, p, fallback); } std::unique_ptr load_object(ImportExport* io, model::Document* document, const PropertyPair& prop) const { auto object = std::make_unique(document); load_properties(object.get(), io, document, prop); return object; } template> ObjectConverter& prop(PropT O2::* property, const char* match_name, const Converter& conv = {}) { auto ptr = std::make_unique>(property, match_name, conv); converters.emplace(match_name, std::move(ptr)); return *this; } template> ObjectConverter& prop(PropT O2::*property, const char* match_name, const Converter& conv, const T& default_value) { converters.emplace(match_name, std::make_unique>(property, match_name, conv, default_value)); return *this; } ObjectConverter& ignore(const char* match_name) { converters.emplace(match_name, nullptr); return *this; } FallbackConverter fallback(Obj* object, FallbackConverterBase* next) const { return {object, this, next}; } std::unordered_map>> converters; }; template struct FallbackConverter : public FallbackConverterBase { FallbackConverter(Obj* object, const ObjectConverter* converter, FallbackConverterBase* next) : object(object), converter(converter), next(next) {} void set_default() const override { converter->set_default(object, next); } void load_property(ImportExport* io, model::Document* document, const PropertyPair& prop_parent, const PropertyPair& prop) const override { converter->load_property(object, io, document, prop_parent, prop, next); } Obj* object; const ObjectConverter* converter; FallbackConverterBase* next; }; template struct ObjectFactory { std::unique_ptr load(ImportExport* io, model::Document* document, const PropertyPair& prop) const { auto it = converters.find(prop.match_name); if ( it == converters.end() ) return {}; return it->second->load(io, document, prop); } template void obj(const char* match_name, FuncT&& func) { assert(converters.count(match_name) == 0); auto up = std::make_unique>>(std::forward(func)); converters.emplace(match_name, std::move(up)); } template ObjectConverter& obj(const char* match_name) { assert(converters.count(match_name) == 0); auto up = std::make_unique>(); auto ptr = up.get(); converters.emplace(match_name, std::move(up)); return *ptr; } std::unordered_map>> converters; }; std::unique_ptr create_shape(ImportExport* io, model::Document* document, const PropertyPair& prop); std::unique_ptr load_shape(ImportExport* io, model::Document* document, const PropertyPair& prop) { auto shape = create_shape(io, document, prop); if ( shape && prop.value->class_type() == PropertyBase::PropertyGroup ) { const auto& gp = static_cast(*prop.value); shape->visible.set(gp.visible); } return shape; } const ObjectConverter& gradient_converter() { static ObjectConverter gradient; static bool initialized = false; if ( !initialized ) { initialized = true; gradient .prop(&model::Gradient::type, "ADBE Vector Grad Type", &convert_enum) .prop(&model::Gradient::start_point, "ADBE Vector Grad Start Pt") .prop(&model::Gradient::end_point, "ADBE Vector Grad End Pt") .ignore("ADBE Vector Grad HiLite Length") /// \todo .ignore("ADBE Vector Grad HiLite Angle") /// \todo ; } return gradient; } const ObjectConverter& gradient_stop_converter() { static ObjectConverter gradient; static bool initialized = false; if ( !initialized ) { initialized = true; gradient .prop(&model::GradientColors::colors, "ADBE Vector Grad Colors", {}, {{0, QColor(255, 255, 255)}, {1, QColor(0, 0, 0)}}) ; } return gradient; } template std::unique_ptr load_gradient(const ObjectConverter* base_converter, ImportExport* io, model::Document* document, const PropertyPair& prop) { auto shape = std::make_unique(document); auto grad_colors = document->assets()->gradient_colors->values.insert( std::make_unique(document) ); auto grad = document->assets()->gradients->values.insert( std::make_unique(document) ); grad->end_point.set({100, 0}); // default value grad->colors.set(grad_colors); shape->use.set(grad); auto f1 = gradient_stop_converter().fallback(grad_colors, nullptr); auto f2 = gradient_converter().fallback(grad, &f1); base_converter->load_properties(shape.get(), io, document, prop, &f2); auto* highlight_len = prop.value->get_pair("ADBE Vector Grad HiLite Length"); auto* highlight_angle = prop.value->get_pair("ADBE Vector Grad HiLite Angle"); if ( highlight_len || highlight_angle ) { model::Document dummydoc(""); model::Object dummy(&dummydoc); model::AnimatedProperty length(&dummy, {}, 0); model::AnimatedProperty angle(&dummy, {}, 0); if ( highlight_len ) load_property_check(io, length, *highlight_len->value, highlight_len->match_name); if ( highlight_angle ) load_property_check(io, angle, *highlight_angle->value, highlight_angle->match_name); model::JoinAnimatables join({&grad->start_point, &grad->end_point, &length, &angle}); join.apply_to(&grad->highlight, [](const QPointF& p, const QPointF& e, float length, float angle) -> QPointF { angle = math::deg2rad(angle + 90); length = math::length(e - p) * length / 100; return p + math::from_polar(length, angle); }, &grad->start_point, &grad->end_point, &length, &angle); } else { grad->highlight.set(grad->start_point.get()); } return shape; } /** * \brief Checks for the default rectangle created by AE with merge path * bodymovin has a similar check on export */ bool is_merge_rect(const model::ShapeElement& shape) { auto path = shape.cast(); if ( !path ) return false; if ( path->shape.animated() ) return false; const auto& bez = path->shape.get(); if ( !bez.closed() || bez.size() != 4 ) return false; for ( const auto& p : bez ) if ( !math::fuzzy_compare(p.pos, p.tan_in) || !math::fuzzy_compare(p.pos, p.tan_out) ) return false; std::array x; std::array y; for ( int i = 0; i < 4; i++ ) { x[i] = bez[i].pos.x(); y[i] = bez[i].pos.y(); } std::sort(x.begin(), x.end()); std::sort(y.begin(), y.end()); return qFuzzyIsNull(x[0]-x[1]) && qFuzzyIsNull(x[2]-x[3]) && qFuzzyIsNull(y[0]-y[1]) && qFuzzyIsNull(y[2]-y[3]); } bool skip_merge(const PropertyPair& prop) { if ( prop.match_name != "ADBE Vector Filter - Merge" ) return false; auto type = prop.value->get("ADBE Vector Merge Type"); if ( !type || type->class_type() != PropertyBase::Property ) return false; auto type_prop = static_cast(type); if ( type_prop->animated ) return false; if ( !qFuzzyCompare(type_prop->value.magnitude(), 4) ) return false; return true; } void load_shape_list(ImportExport* io, model::Document* document, const PropertyBase& properties, model::ShapeListProperty& shapes) { model::ShapeElement* merge_rect = nullptr; for ( const auto& prop : properties ) { if ( merge_rect && skip_merge(prop) ) { shapes.remove(shapes.index_of(merge_rect)); merge_rect = nullptr; continue; } if ( auto shape = load_shape(io, document, prop) ) { if ( !merge_rect && is_merge_rect(*shape) ) merge_rect = shape.get(); shapes.insert(std::move(shape), 0); } } } template> auto make_prop_converter(PropT O2::* property, const char* match_name, const Converter& conv = {}) { auto ptr = std::make_unique>(property, match_name, conv); return ptr; } const ObjectFactory& shape_factory() { static ObjectFactory factory; static bool initialized = false; if ( !initialized ) { initialized = true; factory.obj("ADBE Vector Group", [](ImportExport* io, model::Document* document, const PropertyPair& prop) { auto gp = std::make_unique(document); load_transform(io, gp->transform.get(), (*prop.value)["ADBE Vector Transform Group"], &gp->opacity, {1, 1}, true); load_shape_list(io, document, (*prop.value)["ADBE Vectors Group"], gp->shapes); if ( auto bmprop = prop.value->get("ADBE Vector Blend Mode") ) { auto conv = make_prop_converter(&model::Composable::blend_mode, "ADBE Vector Blend Mode", &convert_enum); conv->load(io, gp.get(), *bmprop); } return gp; }); factory.obj("ADBE Vector Shape - Rect") .prop(&model::Rect::reversed, "ADBE Vector Shape Direction", &convert_shape_reverse) .prop(&model::Rect::position, "ADBE Vector Rect Position") .prop(&model::Rect::size, "ADBE Vector Rect Size") .prop(&model::Rect::rounded, "ADBE Vector Rect Roundness") ; factory.obj("ADBE Vector Shape - Ellipse") .prop(&model::Ellipse::reversed, "ADBE Vector Shape Direction", &convert_shape_reverse) .prop(&model::Ellipse::position, "ADBE Vector Ellipse Position") .prop(&model::Ellipse::size, "ADBE Vector Ellipse Size") ; factory.obj("ADBE Vector Shape - Star") .prop(&model::PolyStar::reversed, "ADBE Vector Shape Direction", &convert_shape_reverse) .prop(&model::PolyStar::position, "ADBE Vector Star Position") .prop(&model::PolyStar::type, "ADBE Vector Star Type", &convert_enum) .prop(&model::PolyStar::points, "ADBE Vector Star Points") .prop(&model::PolyStar::angle, "ADBE Vector Star Rotation") .prop(&model::PolyStar::inner_radius, "ADBE Vector Star Inner Radius") .prop(&model::PolyStar::outer_radius, "ADBE Vector Star Outer Radius") .prop(&model::PolyStar::inner_roundness, "ADBE Vector Star Inner Roundess", &convert_divide<100>) .prop(&model::PolyStar::outer_roundness, "ADBE Vector Star Outer Roundess", &convert_divide<100>) ; factory.obj("ADBE Vector Shape - Group") .prop(&model::Path::reversed, "ADBE Vector Shape Direction", &convert_shape_reverse) .prop(&model::Path::shape, "ADBE Vector Shape") ; const auto* fill = &factory.obj("ADBE Vector Graphic - Fill") .ignore("ADBE Vector Blend Mode") .prop(&model::Fill::color, "ADBE Vector Fill Color", {}, QColor(255, 0, 0)) .prop(&model::Fill::opacity, "ADBE Vector Fill Opacity", &convert_divide<100>) .prop(&model::Fill::fill_rule, "ADBE Vector Fill Rule", &convert_enum) .ignore("ADBE Vector Composite Order") /// \todo could be parsed ; const auto* stroke = &factory.obj("ADBE Vector Graphic - Stroke") .ignore("ADBE Vector Blend Mode") .prop(&model::Stroke::color, "ADBE Vector Stroke Color", {}, QColor(255, 255, 255)) .prop(&model::Stroke::opacity, "ADBE Vector Stroke Opacity", &convert_divide<100>) .prop(&model::Stroke::width, "ADBE Vector Stroke Width", {}, 2) .prop(&model::Stroke::cap, "ADBE Vector Stroke Line Cap", &convert_enum, model::Stroke::ButtCap) .prop(&model::Stroke::join, "ADBE Vector Stroke Line Join", &convert_enum, model::Stroke::MiterJoin) .prop(&model::Stroke::miter_limit, "ADBE Vector Stroke Miter Limit", {}, 4) .ignore("ADBE Vector Stroke Dashes") .ignore("ADBE Vector Stroke Taper") .ignore("ADBE Vector Stroke Wave") .ignore("ADBE Vector Composite Order") /// \todo could be parsed ; factory.obj("ADBE Vector Filter - RC") .prop(&model::RoundCorners::radius, "ADBE Vector RoundCorner Radius", {}, 10) ; factory.obj("ADBE Vector Filter - Trim") .prop(&model::Trim::start, "ADBE Vector Trim Start", &convert_divide<100>) .prop(&model::Trim::end, "ADBE Vector Trim End", &convert_divide<100>) .prop(&model::Trim::offset, "ADBE Vector Trim Offset", &convert_divide<360>) .prop(&model::Trim::multiple, "ADBE Vector Trim Type", &convert_enum) ; factory.obj("ADBE Vector Filter - Offset") .prop(&model::OffsetPath::amount, "ADBE Vector Offset Amount") .prop(&model::OffsetPath::join, "ADBE Vector Offset Line Join", &convert_enum, model::Stroke::MiterJoin) .prop(&model::OffsetPath::miter_limit, "ADBE Vector Offset Miter Limit", {}, 4) ; factory.obj("ADBE Vector Filter - PB") .prop(&model::InflateDeflate::amount, "ADBE Vector PuckerBloat Amount", &convert_divide<100>) ; factory.obj("ADBE Vector Filter - Zigzag") .prop(&model::ZigZag::amplitude, "ADBE Vector Zigzag Size", {}, 5) .prop(&model::ZigZag::frequency, "ADBE Vector Zigzag Detail", {}, 10) .prop(&model::ZigZag::style, "ADBE Vector Zigzag Points", &convert_enum) ; factory.obj("ADBE Vector Graphic - G-Fill", [fill](ImportExport* io, model::Document* document, const PropertyPair& prop) { return load_gradient(fill, io, document, prop); }); factory.obj("ADBE Vector Graphic - G-Stroke", [stroke](ImportExport* io, model::Document* document, const PropertyPair& prop) { return load_gradient(stroke, io, document, prop); }); factory.obj("ADBE Vector Filter - Repeater", [](ImportExport* io, model::Document* document, const PropertyPair& prop) { auto shape = std::make_unique(document); if ( auto tf = prop.value->get("ADBE Vector Repeater Transform") ) { load_transform(io, shape->transform.get(), *tf, nullptr, {1, 1}, false); const char* pmn = "ADBE Vector Repeater Start Opacity"; if ( auto o = tf->get(pmn) ) { load_property_check(io, shape->start_opacity, *o, pmn, &convert_divide<100>); } else { const char* pmn = "ADBE Vector Repeater Opacity 1"; if ( auto o = tf->get(pmn) ) load_property_check(io, shape->start_opacity, *o, pmn, &convert_divide<100>); } pmn = "ADBE Vector Repeater End Opacity"; if ( auto o = tf->get(pmn) ) { load_property_check(io, shape->end_opacity, *o, pmn, &convert_divide<100>); } else { const char* pmn = "ADBE Vector Repeater Opacity 2"; if ( auto o = tf->get(pmn) ) load_property_check(io, shape->end_opacity, *o, pmn, &convert_divide<100>); } } if ( auto copies = prop.value->get("ADBE Vector Repeater Copies") ) { load_property_check(io, shape->copies, *copies, "ADBE Vector Repeater Copies"); } return shape; }); } return factory; }; std::unique_ptr create_shape(ImportExport* io, model::Document* document, const PropertyPair& prop) { if ( auto shape = shape_factory().load(io, document, prop) ) return shape; io->information(i18n("Unknown shape %1", prop.match_name)); return nullptr; } } // namespace void glaxnimate::io::aep::AepLoader::load_layer(LayerData& layer_data, CompData& data) { const auto& ae_layer = *layer_data.ae_layer; auto ulayer = std::make_unique(document); auto layer = ulayer.get(); layer_data.layer = layer; data.shapes->insert(std::move(ulayer), 0); model::Layer* composable = layer_data.external ? layer_data.external.get() : layer; layer->name.set(ae_layer.name); layer->render.set(!ae_layer.is_guide); layer->animation->last_frame.set(ae_layer.out_time); layer->animation->first_frame.set(ae_layer.in_time); composable->visible.set(ae_layer.properties.visible); /// \todo could be nice to toggle visibility based on solo/shy layer->group_color.set(label_colors[int(ae_layer.label_color)]); composable->blend_mode.set(layer_blend_modes.value(ae_layer.blend_mode, renderer::BlendMode::Normal)); composable->transform->position.set({ data.comp->width.get() / 2., data.comp->height.get() / 2., }); QPointF anchor{1, 1}; auto it = asset_size.find(ae_layer.asset_id); if ( it != asset_size.end() && !ae_layer.is_null ) { anchor = it->second; composable->transform->anchor_point.set(anchor / 2); } load_transform(io, composable->transform.get(), ae_layer.properties["ADBE Transform Group"], &composable->opacity, anchor, false); composable->transform->auto_orient.set(ae_layer.auto_orient); if ( ae_layer.is_null ) return; else if ( ae_layer.asset_id ) asset_layer(layer, ae_layer, data); else if ( ae_layer.type == LayerType::ShapeLayer ) shape_layer(layer, ae_layer, data); else if ( ae_layer.type == LayerType::TextLayer ) text_layer(layer, ae_layer, data); auto mask_parade = ae_layer.properties.get("ADBE Mask Parade"); if ( mask_parade ) { layer->mask->mask.set(model::MaskSettings::Alpha); auto clip_p = std::make_unique(document); auto clip = clip_p.get(); layer->shapes.insert(std::move(clip_p), 0); document->set_best_name(clip, i18n("Clip")); for ( const auto& mask : *mask_parade ) { if ( mask.match_name != "ADBE Mask Atom" ) continue; auto group = std::make_unique(document); auto fill = std::make_unique(document); fill->color.set(QColor(255, 255, 255)); document->set_best_name(fill.get()); auto mask_opacity = mask.value->get_pair("ADBE Mask Opacity"); if ( mask_opacity ) load_property_check(io, fill->opacity, *mask_opacity->value, mask_opacity->match_name, &convert_divide<100>); group->shapes.insert(std::move(fill)); if ( auto expansion = mask.value->get_pair("ADBE Mask Offset") ) { auto offset = std::make_unique(document); document->set_best_name(offset.get()); load_property_check(io, offset->amount, *expansion->value, expansion->match_name); group->shapes.insert(std::move(offset)); } if ( auto shape = mask.value->get_pair("ADBE Mask Shape") ) { auto path = std::make_unique(document); document->set_best_name(path.get()); load_property_check(io, path->shape, *shape->value, shape->match_name, {}); path->closed.set(true); group->shapes.insert(std::move(path)); } clip->shapes.insert(std::move(group)); } } if ( layer_data.matte_parent ) { if ( layer_data.matte_parent->matte_comp ) { auto ext_comp = layer_data.matte_parent->matte_comp; auto external = std::make_unique(document); external->name.set(ext_comp->name.get()); external->group_color.set(ext_comp->group_color.get()); external->composition.set(ext_comp); layer->shapes.insert(std::move(external)); } else { CompData sub_data{data.comp, data.ae_comp, &layer->shapes}; load_layer(*layer_data.matte_parent, sub_data); } switch ( ae_layer.matte_mode ) { case None: break; case Alpha: layer->mask->mask.set(model::MaskSettings::Alpha); break; case AlphaInverted: layer->mask->mask.set(model::MaskSettings::Alpha); layer->mask->inverted.set(true); break; case Luma: layer->mask->mask.set(model::MaskSettings::Luma); break; case LumaInverted: layer->mask->mask.set(model::MaskSettings::Luma); layer->mask->inverted.set(true); break; } } } void glaxnimate::io::aep::AepLoader::shape_layer( model::Layer* layer, const glaxnimate::io::aep::Layer& ae_layer, glaxnimate::io::aep::AepLoader::CompData& ) { load_shape_list(io, document, ae_layer.properties["ADBE Root Vectors Group"], layer->shapes); } void glaxnimate::io::aep::AepLoader::asset_layer( model::Layer* layer, const Layer& ae_layer, CompData& ) { auto img_it = images.find(ae_layer.asset_id); if ( img_it != images.end() ) { auto image = std::make_unique(document); image->image.set(img_it->second); image->name.set(img_it->second->name.get()); if ( layer->name.get().isEmpty() ) layer->name.set(image->name.get()); layer->shapes.insert(std::move(image)); return; } auto comp_it = comps.find(ae_layer.asset_id); if ( comp_it != comps.end() ) { /// \todo ADBE Time Remapping /// \todo Time stretch / start_time auto precomp = std::make_unique(document); precomp->timing->start_time.set(ae_layer.start_time); precomp->timing->stretch.set(ae_layer.time_stretch); precomp->composition.set(comp_it->second); precomp->name.set(comp_it->second->name.get()); precomp->size.set(comp_it->second->size()); if ( layer->name.get().isEmpty() ) layer->name.set(precomp->name.get()); layer->shapes.insert(std::move(precomp)); return; } auto solid_it = colors.find(ae_layer.asset_id); if ( solid_it != colors.end() ) { auto fill = std::make_unique(document); fill->color.set(solid_it->second.asset->color.get()); fill->use.set(solid_it->second.asset); layer->shapes.insert(std::move(fill)); auto rect = std::make_unique(document); rect->size.set(QSizeF( solid_it->second.solid->width, solid_it->second.solid->height )); rect->position.set(QPointF( solid_it->second.solid->width / 2, solid_it->second.solid->height / 2 )); layer->shapes.insert(std::move(rect)); if ( layer->name.get().isEmpty() ) layer->name.set(solid_it->second.asset->name.get()); return; } warning(i18n("Unknown asset type for %1", ae_layer.name.isEmpty() ? "Layer" : ae_layer.name)); } namespace { std::unique_ptr text_to_shapes( const TextDocument& doc, const std::vector& fonts, model::Document* document ) { auto group = std::make_unique(document); auto pt = std::make_unique(document); auto text = pt.get(); group->shapes.insert(std::move(pt)); text->text.set(doc.text); if ( !doc.character_styles.empty() ) { /// \todo Style text spans const auto& style = doc.character_styles[0]; /// \todo figure out weight etc if ( style.font_index >= 0 && style.font_index < int(fonts.size()) ) text->font->family.set(fonts[style.font_index].family); text->font->size.set(style.size); std::unique_ptr fill; std::unique_ptr stroke; if ( style.stroke_enabled ) { stroke = std::make_unique(document); stroke->color.set(style.stroke_color); stroke->width.set(style.stroke_width); } /// \todo fill enabled? fill = std::make_unique(document); fill->color.set(style.fill_color); if ( style.stroke_over_fill ) { if ( fill ) group->shapes.insert(std::move(fill)); if ( stroke ) group->shapes.insert(std::move(stroke)); } else { if ( stroke ) group->shapes.insert(std::move(stroke)); if ( fill ) group->shapes.insert(std::move(fill)); } } return group; } } // namespace void glaxnimate::io::aep::AepLoader::text_layer(model::Layer* layer, const Layer& ae_layer, CompData&) { auto prop = ae_layer.properties["ADBE Text Properties"]["ADBE Text Document"]; if ( prop.class_type() != PropertyBase::TextProperty ) return; const auto& tprop = static_cast(prop); if ( tprop.documents.value.type() == PropertyValue::TextDocument ) { layer->shapes.insert(text_to_shapes( std::get(tprop.documents.value.value), tprop.fonts, document )); return; } if ( tprop.documents.keyframes.empty() ) return; /// \todo animated text const auto& kf = tprop.documents.keyframes[0]; if ( kf.value.type() == PropertyValue::TextDocument ) { layer->shapes.insert(text_to_shapes( std::get(kf.value.value), tprop.fonts, document )); return; } } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/enum_map.hpp000664 001750 001750 00000003271 15165022620 032150 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include "glaxnimate/renderer/renderer.hpp" #pragma once namespace glaxnimate::io::svg::detail { const std::vector> blend_modes = { {renderer::BlendMode::Normal, "normal"}, {renderer::BlendMode::Multiply, "multiply"}, {renderer::BlendMode::Screen, "screen"}, {renderer::BlendMode::Overlay, "overlay"}, {renderer::BlendMode::Darken, "darken"}, {renderer::BlendMode::Lighten, "lighten"}, {renderer::BlendMode::ColorDodge, "color-dodge"}, {renderer::BlendMode::ColorBurn, "color-burn"}, {renderer::BlendMode::HardLight, "hard-light"}, {renderer::BlendMode::SoftLight, "soft-light"}, {renderer::BlendMode::Difference, "difference"}, {renderer::BlendMode::Exclusion, "exclusion"}, {renderer::BlendMode::Hue, "hue"}, {renderer::BlendMode::Saturation, "saturation"}, {renderer::BlendMode::Color, "color"}, {renderer::BlendMode::Luminosity, "luminosity"}, {renderer::BlendMode::Add, "plus-lighter"}, }; template QString enum_to_svg(const T& value, const std::vector>& pairs, const QString& default_val) { for ( const auto& p : pairs ) if ( p.first == value ) return p.second; return default_val; } template T enum_from_svg(const QString& value, const std::vector>& pairs, const T& default_val) { for ( const auto& p : pairs ) if ( p.second == value ) return p.first; return default_val; } } // namespace glaxnimate::io::svg::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/bezier.cpp000664 001750 001750 00000022030 15165022620 032617 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/bezier/bezier.hpp" using namespace glaxnimate; QRectF math::bezier::Bezier::bounding_box() const { if ( size() < 2 ) return {}; auto pair = solver_for_point(0).bounds(); QRectF box(pair.first, pair.second); for ( int i = 1; i < size() - 1; i++ ) { pair = solver_for_point(i).bounds(); box |= QRectF(pair.first, pair.second); } if ( closed_ ) { pair = solver_for_point(size()-1).bounds(); box |= QRectF(pair.first, pair.second); } return box; } void math::bezier::Bezier::split_segment(int index, qreal factor) { if ( points_.empty() ) return; if ( index < 0 ) { points_.insert(points_.begin(), points_[0]); return; } else if ( index >= size() ) { points_.insert(points_.end(), points_.back()); return; } auto split_points = solver_for_point(index).split(factor); points_[index].tan_out = split_points.first[1]; points_[(index+1) % size()].tan_in = split_points.second[2]; auto type = Smooth; if ( factor <= 0 ) type = points_[index].type; else if ( factor >= 1 ) type = points_[(index+1) % size()].type; points_.insert(points_.begin() + index + 1, Point( split_points.first[3], split_points.first[2], split_points.second[1], type )); } math::bezier::Point math::bezier::Bezier::split_segment_point(int index, qreal factor) const { if ( index < 0 ) return points_[0]; else if ( index >= size() ) return points_.back(); if ( factor <= 0 ) return points_[index]; else if ( factor >= 1 ) return points_[(index+1) % size()]; auto split_points = solver_for_point(index).split(factor); return Point( split_points.first[3], split_points.first[2], split_points.second[1], Smooth ); } void math::bezier::Bezier::add_to_painter_path(QPainterPath& out) const { if ( size() < 2 ) return; out.moveTo(points_[0].pos); for ( int i = 1; i < size(); i++ ) { out.cubicTo(points_[i-1].tan_out, points_[i].tan_in, points_[i].pos); } if ( closed_ ) { out.cubicTo(points_.back().tan_out, points_[0].tan_in, points_[0].pos); out.closeSubpath(); } } math::bezier::Bezier math::bezier::Bezier::lerp(const math::bezier::Bezier& other, qreal factor) const { if ( other.closed_ != closed_ || other.size() != size() ) return *this; math::bezier::Bezier lerped; lerped.closed_ = closed_; lerped.points_.reserve(size()); for ( int i = 0; i < size(); i++ ) lerped.points_.push_back(Point::from_relative( math::lerp(points_[i].pos, other.points_[i].pos, factor), math::lerp( points_[i].tan_in - points_[i].pos, other.points_[i].tan_in - other.points_[i].pos, factor ), math::lerp( points_[i].tan_out - points_[i].pos, other.points_[i].tan_out - other.points_[i].pos, factor ) )); return lerped; } void math::bezier::Bezier::reverse() { std::reverse(points_.begin(), points_.end()); if ( closed_ && points_.size() > 1 ) { auto back = points_.back(); points_.pop_back(); points_.insert(points_.begin(), back); } for ( auto& p : points_ ) std::swap(p.tan_in, p.tan_out); } math::bezier::BezierSegment math::bezier::Bezier::segment(int index) const { return { points_[index].pos, points_[index].tan_out, points_[(index+1) % points_.size()].tan_in, points_[(index+1) % points_.size()].pos }; } math::bezier::BezierSegment math::bezier::Bezier::inverted_segment(int index) const { return { points_[(index+1) % points_.size()].pos, points_[(index+1) % points_.size()].tan_in, points_[index].tan_out, points_[index].pos }; } void math::bezier::Bezier::set_segment(int index, const math::bezier::BezierSegment& s) { points_[index].pos = s[0]; points_[index].drag_tan_out(s[1]); points_[(index+1) % points_.size()].pos = s[3]; points_[(index+1) % points_.size()].drag_tan_in(s[2]); } math::bezier::Bezier math::bezier::Bezier::transformed(const QTransform& t) const { auto copy = *this; copy.transform(t); return copy; } void math::bezier::Bezier::transform(const QTransform& t) { for ( auto& p : points_ ) p.transform(t); } int glaxnimate::math::bezier::Bezier::segment_count() const { return closed_ || points_.empty() ? points_.size() : points_.size() - 1; } math::bezier::Bezier math::bezier::Bezier::removed_points(const std::set& indices) const { math::bezier::Bezier new_bez; new_bez.set_closed(closed_); for ( int i = 0; i < size(); i++ ) if ( !indices.count(i) ) new_bez.push_back(points_[i]); return new_bez; } void glaxnimate::math::bezier::Bezier::add_close_point() { if ( closed_ && !points_.empty() && !math::fuzzy_compare(points_[0].pos, points_.back().pos) ) { points_.push_back(points_[0]); points_.back().tan_out = points_[0].tan_in = points_[0].pos; } } glaxnimate::math::bezier::Point glaxnimate::math::bezier::Bezier::point_with_type(int index, PointType point_type) const { auto point = points_[index]; if ( point.type == point_type && point.type == math::bezier::PointType::Corner ) { point.tan_in = point.tan_out = point.pos; } point.type = point_type; if ( point_type != math::bezier::PointType::Corner ) { if ( math::fuzzy_compare(point.tan_in, point.pos) && (index > 0 || closed_) ) { const auto& prev = index == 0 ? points_.back() : points_[index - 1]; point.tan_in = (prev.pos - point.pos) / 6 + point.pos; } if ( math::fuzzy_compare(point.tan_out, point.pos) && (index < size() - 1 || closed_) ) { const auto& next = points_[(index + 1) % points_.size()]; point.tan_out = (next.pos - point.pos) / 6 + point.pos; } } point.adjust_handles_from_type(); return point; } QRectF math::bezier::MultiBezier::bounding_box() const { if ( beziers_.empty() ) return {}; QRectF box; for ( const Bezier& bez : beziers_ ) { QRectF bb = bez.bounding_box(); if ( box.isNull() ) box = bb; else if ( !bb.isNull() ) box |= bb; } return box; } void math::bezier::MultiBezier::append(const QPainterPath& path) { std::array data; int data_i = 0; for ( int i = 0; i < path.elementCount(); i++ ) { auto element = path.elementAt(i); switch ( element.type ) { case QPainterPath::MoveToElement: if ( !beziers_.empty() && beziers_.back()[0].pos == beziers_.back().back().pos ) close(); move_to(element); break; case QPainterPath::LineToElement: line_to(element); break; case QPainterPath::CurveToElement: data_i = 0; data[0] = element; break; case QPainterPath::CurveToDataElement: ++data_i; data[data_i] = element; if ( data_i == 2 ) { cubic_to(data[0], data[1], data[2]); data_i = -1; } break; } } } void math::bezier::MultiBezier::transform(const QTransform& t) { for ( auto& bez : beziers_ ) bez.transform(t); } glaxnimate::math::bezier::MultiBezier glaxnimate::math::bezier::MultiBezier::transformed(const QTransform& matrix) const { auto copy = *this; copy.transform(matrix); return copy; } void glaxnimate::math::bezier::MultiBezier::translate(const QPointF& p) { for ( auto& bez : beziers_ ) { for ( Point& pt : bez ) { pt.pos += p; pt.tan_in += p; pt.tan_out += p; } } } glaxnimate::math::bezier::MultiBezier glaxnimate::math::bezier::MultiBezier::translated(const QPointF& p) const { auto copy = *this; copy.translate(p); return copy; } math::bezier::MultiBezier math::bezier::MultiBezier::from_painter_path(const QPainterPath& path) { math::bezier::MultiBezier bez; bez.append(path); return bez; } void glaxnimate::math::bezier::MultiBezier::append(const QPolygonF& path) { if ( path.empty() ) return; move_to(path[0]); for ( int i = 1; i < path.size(); i++ ) line_to(path[i]); close(); } void glaxnimate::math::bezier::MultiBezier::reverse() { for ( auto& bez : beziers_ ) bez.reverse(); } glaxnimate::math::bezier::MultiBezier glaxnimate::math::bezier::MultiBezier::reversed() const { auto copy = *this; copy.reverse(); return copy; } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/gradient_xml.cpp000664 001750 001750 00000004251 15165022620 036302 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/aep/gradient_xml.hpp" #include "glaxnimate/io/svg/detail.hpp" using namespace glaxnimate::io; using namespace glaxnimate::io::aep; CosValue aep::xml_value(const QDomElement& element) { if ( element.tagName() == "prop.map" ) return xml_value(element.firstChildElement()); else if ( element.tagName() == "prop.list" ) return xml_list(element); else if ( element.tagName() == "array" ) return xml_array(element); else if ( element.tagName() == "int" ) return element.text().toDouble(); else if ( element.tagName() == "float" ) return element.text().toDouble(); else if ( element.tagName() == "string" ) return element.text(); else return {}; } CosArray aep::xml_array(const QDomElement& element) { auto data = std::make_unique(); for ( const auto& child : svg::detail::ElementRange(element) ) { if ( child.tagName() != "array.type" ) data->push_back(xml_value(child)); } return data; } CosObject aep::xml_list(const QDomElement& element) { auto data = std::make_unique(); for ( const auto& pair : svg::detail::ElementRange(element, "prop.pair") ) { QString key; CosValue value; for ( const auto& ch : svg::detail::ElementRange(pair) ) { if ( ch.tagName() == "key" ) key = ch.text(); else value = xml_value(ch); } data->emplace(key, std::move(value)); } return data; } Gradient aep::parse_gradient_xml(const CosValue& value) { Gradient gradient; auto& data = get(value, "Gradient Color Data"); gradient.color_stops = get_gradient_stops(data); gradient.alpha_stops = get_gradient_stops(data); return gradient; } Gradient aep::parse_gradient_xml(const QString& xml) { QDomDocument dom; dom.setContent(xml.trimmed()); return parse_gradient_xml(xml_value(dom.documentElement())); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/offset_path.hpp000664 001750 001750 00000001646 15165022620 035734 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/modifiers/path_modifier.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" namespace glaxnimate::model { class OffsetPath : public StaticOverrides { GLAXNIMATE_OBJECT(OffsetPath) GLAXNIMATE_ANIMATABLE(float, amount, 0) GLAXNIMATE_ANIMATABLE(float, miter_limit, 100, {}, 0) GLAXNIMATE_PROPERTY(glaxnimate::model::Stroke::Join, join, Stroke::RoundJoin, nullptr, nullptr, PropertyTraits::Visual) public: using Ctor::Ctor; static QIcon static_tree_icon(); static QString static_type_name_human(); math::bezier::MultiBezier process(FrameTime t, const math::bezier::MultiBezier& mbez) const override; protected: bool process_collected() const override; }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/avd/avd_parser.hpp000664 001750 001750 00000001447 15165022620 035771 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/io/base.hpp" namespace glaxnimate::io::avd { class AvdParser { private: public: /** * \throws SvgParseError on error */ AvdParser( QIODevice* device, const QDir& resource_path, model::Document* document, const std::function& on_warning = {}, ImportExport* io = nullptr, QSize forced_size = {}, model::FrameTime default_time = 180 ); ~AvdParser(); void parse_to_document(); class Private; private: std::unique_ptr d; }; } // namespace glaxnimate::io::avd src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/type_system.cpp000664 001750 001750 00000003250 15165022620 036410 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/rive/type_system.hpp" const glaxnimate::io::rive::ObjectDefinition * glaxnimate::io::rive::TypeSystem::get_definition(glaxnimate::io::rive::TypeId type_id) { auto it = defined_objects.find(type_id); if ( it == defined_objects.end() ) { Q_EMIT type_not_found(int(type_id)); return nullptr; } return &it->second; } bool glaxnimate::io::rive::TypeSystem::gather_definitions(glaxnimate::io::rive::ObjectType& type, glaxnimate::io::rive::TypeId type_id) { auto* def = get_definition(type_id); if ( !def ) return false; type.definitions.push_back(def); if ( def->extends != TypeId::NoType ) { if ( !gather_definitions(type, def->extends) ) return false; } for ( const auto& prop : def->properties ) { type.property_from_name[prop.name] = ∝ type.property_from_id[prop.id] = ∝ type.properties.push_back(&prop); } return true; } const glaxnimate::io::rive::ObjectType * glaxnimate::io::rive::TypeSystem::get_type(glaxnimate::io::rive::TypeId type_id) { auto it = types.find(type_id); if ( it != types.end() ) return &it->second; ObjectType type(type_id); if ( !gather_definitions(type, type_id) ) return nullptr; return &types.emplace(type_id, std::move(type)).first->second; } QString glaxnimate::io::rive::TypeSystem::type_name(glaxnimate::io::rive::TypeId type_id) { if ( auto def = get_definition(type_id) ) return def->name; return {}; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/000775 001750 001750 00000000000 15165022620 026425 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/000775 001750 001750 00000000000 15165022620 033176 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/svgz_format.hpp000664 001750 001750 00000001663 15165022620 033743 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" #include "glaxnimate/io/svg/svg_format.hpp" namespace glaxnimate::io::svg { class SvgzFormat : public SvgFormat { Q_OBJECT public: QString slug() const override { return "svgz"; } QString name() const override { return i18n("Compressed SVG"); } QStringList extensions(Direction) const override; protected: bool on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&) override; bool on_save(QIODevice & file, const QString & filename, model::Composition* comp, const QVariantMap & setting_values) override; bool on_save_static(QIODevice & file, const QString & filename, model::Composition* comp, model::FrameTime time, const QVariantMap & setting_values) override; }; } // namespace glaxnimate::io::svg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/app_info.hpp000664 001750 001750 00000002000 15165022620 030721 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include namespace glaxnimate { class AppInfo { public: static AppInfo& instance() { static AppInfo singleton; return singleton; } /** * \brief Project machine-readable name */ QString slug() const; /** * \brief Project machine-readable org name */ QString organization() const; /** * \brief Project version */ QString version() const; /** * \brief Project human-readable name */ QString name() const; /** * \brief Documentation URL */ QUrl url_docs() const; /** * \brief Application description */ QString description() const; QString project_id() const; QUrl url_home() const; private: AppInfo() = default; ~AppInfo() = default; }; } // namespace glaxnimate mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/glaxnimate_format.cpp000664 001750 001750 00000017107 15165022620 035400 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/glaxnimate/glaxnimate_format.hpp" #include #include #include "glaxnimate/app_info.hpp" #include "glaxnimate/math/bezier/bezier.hpp" #include "glaxnimate/model/assets/assets.hpp" using namespace glaxnimate; const int glaxnimate::io::glaxnimate::GlaxnimateFormat::format_version = 9; bool io::glaxnimate::GlaxnimateFormat::on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap&) { return file.write(to_json(comp->document()).toJson(QJsonDocument::Indented)); } QJsonObject io::glaxnimate::GlaxnimateFormat::format_metadata() { QJsonObject object; object["generator"] = AppInfo::instance().name(); object["generator_version"] = AppInfo::instance().version(); object["format_version"] = format_version; return object; } QJsonDocument io::glaxnimate::GlaxnimateFormat::to_json ( model::Document* document ) { QJsonObject doc_obj; doc_obj["format"] = format_metadata(); doc_obj["metadata"] = QJsonObject::fromVariantMap(document->metadata()); QJsonObject info; info["author"] = document->info().author; info["description"] = document->info().description; QJsonArray keywords; for ( const auto& kw: document->info().keywords ) keywords.push_back(kw); info["keywords"] = keywords; doc_obj["info"] = info; doc_obj["assets"] = to_json(document->assets()); return QJsonDocument(doc_obj); } QJsonObject io::glaxnimate::GlaxnimateFormat::to_json ( model::Object* object ) { QJsonObject obj; obj["__type__"] = object->type_name(); for ( model::BaseProperty* prop : object->properties() ) obj[prop->name()] = to_json(prop); return obj; } namespace { QJsonValue point_to_json(const QPointF& v) { QJsonObject o; o["x"] = v.x(); o["y"] = v.y(); return o; } } // namespace QJsonValue io::glaxnimate::GlaxnimateFormat::to_json ( model::BaseProperty* property ) { if ( property->traits().flags & model::PropertyTraits::List ) { QJsonArray arr; for ( const QVariant& val : property->value().toList() ) { arr.push_back(to_json(val, property->traits())); } return arr; } else if ( property->traits().flags & model::PropertyTraits::Animated ) { model::AnimatedPropertyBase* anim = static_cast(property); bool position = anim->traits().type == model::PropertyTraits::Point; QJsonObject jso; if ( !anim->animated() ) { jso["value"] = to_json(anim->value(), property->traits()); } else { QJsonArray keyframes; for ( const auto& kf : anim->keyframe_range() ) { QJsonObject jkf; jkf["time"] = kf.time(); jkf["value"] = to_json(kf.value(), property->traits()); if ( !kf.transition().hold() ) { jkf["before"] = to_json(kf.transition().before()); jkf["after"] = to_json(kf.transition().after()); } if ( position ) { const auto& pkf = static_cast&>(kf); jkf["tan_in"] = point_to_json(pkf.point().tan_in); jkf["tan_out"] = point_to_json(pkf.point().tan_out); jkf["point_type"] = pkf.point().type; } keyframes.push_back(jkf); } jso["keyframes"] = keyframes; } return jso; } return to_json(property->value(), property->traits()); } QJsonValue io::glaxnimate::GlaxnimateFormat::to_json ( const QVariant& value, model::PropertyTraits traits ) { switch ( traits.type ) { case model::PropertyTraits::Object: if ( auto obj = value.value() ) return to_json(obj); return {}; case model::PropertyTraits::ObjectReference: if ( auto dn = value.value() ) return QJsonValue::fromVariant(dn->uuid.get()); return {}; case model::PropertyTraits::Enum: return value.toString(); case model::PropertyTraits::Bezier: { math::bezier::Bezier bezier = value.value(); QJsonObject jsbez; jsbez["closed"] = bezier.closed(); QJsonArray points; for ( const auto& p : bezier ) { QJsonObject jsp; jsp["pos"] = point_to_json(p.pos); jsp["tan_in"] = point_to_json(p.tan_in); jsp["tan_out"] = point_to_json(p.tan_out); jsp["type"] = p.type; points.push_back(jsp); } jsbez["points"] = points; return jsbez; } case model::PropertyTraits::Gradient: { QJsonArray stops; for ( const auto& stop : value.value() ) { QJsonObject jstop; jstop["offset"] = stop.first; jstop["color"] = to_json(stop.second); stops.push_back(jstop); } return stops; } default: return to_json(value); } } QJsonValue io::glaxnimate::GlaxnimateFormat::to_json ( const QVariant& value ) { if ( !value.isValid() ) return {}; switch ( value.userType() ) { case QMetaType::Bool: case QMetaType::Int: case QMetaType::UInt: case QMetaType::Short: case QMetaType::UShort: case QMetaType::Long: case QMetaType::ULong: case QMetaType::LongLong: case QMetaType::ULongLong: case QMetaType::Double: case QMetaType::Float: case QMetaType::QChar: case QMetaType::QString: case QMetaType::QJsonArray: case QMetaType::QJsonObject: case QMetaType::QJsonValue: case QMetaType::QUuid: return QJsonValue::fromVariant(value); case QMetaType::QByteArray: return QString(value.toByteArray().toBase64()); case QMetaType::UnknownType: return {}; case QMetaType::QSize: { auto v = value.toSize(); QJsonObject o; o["width"] = v.width(); o["height"] = v.height(); return o; } case QMetaType::QSizeF: { auto v = value.toSizeF(); QJsonObject o; o["width"] = v.width(); o["height"] = v.height(); return o; } case QMetaType::QPoint: { auto v = value.toPoint(); QJsonObject o; o["x"] = v.x(); o["y"] = v.y(); return o; } case QMetaType::QVector2D: { auto v = value.value(); QJsonObject o; o["x"] = v.x(); o["y"] = v.y(); return o; } case QMetaType::QPointF: return point_to_json(value.toPointF()); case QMetaType::QColor: { auto v = value.value(); QString col = v.name(); if ( v.alpha() != 255 ) col += QStringView(QString::number(v.alpha()|0x100, 16)).right(2); return col; } } if ( value.canConvert() ) return point_to_json(value.toPointF()); return {}; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/module.cpp000664 001750 001750 00000004355 15165022620 031712 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/module.hpp" #include "glaxnimate/math/bezier/meta.hpp" #include "glaxnimate/app_info.hpp" // Core formats #include "glaxnimate/io/glaxnimate/glaxnimate_format.hpp" #include "glaxnimate/io/glaxnimate/glaxnimate_mime.hpp" #include "glaxnimate/io/mime/json_mime.hpp" #include "glaxnimate/io/lottie/lottie_format.hpp" // Raster #include "glaxnimate/io/raster/raster_format.hpp" #include "glaxnimate/io/raster/raster_mime.hpp" #include "glaxnimate/io/raster/spritesheet_format.hpp" // SVG-like #include "glaxnimate/io/svg/svg_format.hpp" #include "glaxnimate/io/svg/svg_mime.hpp" using namespace glaxnimate; glaxnimate::io::glaxnimate::GlaxnimateFormat* default_format = nullptr; glaxnimate::io::glaxnimate::GlaxnimateFormat* glaxnimate::io::glaxnimate::GlaxnimateFormat::instance() { return default_format; } namespace { class CoreModule : public glaxnimate::module::Module { public: CoreModule() : Module(i18n("Glaxnimate Core"), AppInfo::instance().version()) {} std::vector components() const override { return {}; } protected: void initialize() override { math::bezier::register_meta(); init_file_formats(); } void init_file_formats() { using namespace glaxnimate::io; default_format = io::IoRegistry::instance().register_class(); register_io_classes< io::glaxnimate::GlaxnimateMime, mime::JsonMime, lottie::LottieFormat, svg::SvgFormat, svg::SvgMime, raster::RasterFormat, raster::RasterMime, raster::SpritesheetFormat >(); } }; } // namespace glaxnimate::module::Registry::Registry() { install(); register_loaded_modules(*this); } module::Module::Module(const QString &name, const QString &version) : name_(name), version_(version.isEmpty() ? AppInfo::instance().version() : version) {} module::Registry &module::registry() { return Registry::instance(); } void module::initialize() { Registry::instance(); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/segment.hpp000664 001750 001750 00000000461 15165022620 033012 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include namespace glaxnimate::math::bezier { using BezierSegment = std::array; } // namespace glaxnimate::math::bezier mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/animatable_base.hpp000664 001750 001750 00000023441 15165022620 035310 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/animation/keyframe_base.hpp" #include "glaxnimate/model/animation/keyframe_container.hpp" namespace glaxnimate::model { class AnimatableBase : public QObject { Q_OBJECT Q_PROPERTY(int keyframe_count READ keyframe_count) Q_PROPERTY(bool animated READ animated) public: enum KeyframeStatus { NotAnimated, ///< Value is not animated Tween, ///< Value is animated but the given time isn't a keyframe IsKeyframe, ///< Value is animated and the given time is a keyframe Mismatch ///< Value is animated and the current value doesn't match the animated value }; enum class MoveResult { NotFound, OverwrittenDestination, Moved, }; enum AnimatablableFlags { NoFlags = 0, HasValue = 1, IsWritable = 2, IsProperty = 4, IsMeta = 8 }; struct SetKeyframeInfo { bool insertion; }; struct MidTransition { enum Type { Invalid, SingleKeyframe, Middle, }; Type type = Invalid; QVariant value; model::KeyframeTransition from_previous; model::KeyframeTransition to_next; }; virtual ~AnimatableBase() = default; virtual int animatable_flags() const = 0; /** * \brief Number of keyframes */ virtual int keyframe_count() const = 0; /** * \brief Keyframe whose transition contains \p time * * This keyframe starts before or at \p time and it ends at \p time * * If all keyframes are after \p time, returns first one * This means keyframe(keyframe_index(t)) is always valid when animated * * keyframe_containing * v-------v * t0 t1 t2 t3 * |-------|-------|-------| * ^ ^ * time * * \return the Corresponding keyframe or nullptr if not found * \note Uses double instead of FrameTime because Qt's MOC is too dumb to figure out typedefs */ Q_INVOKABLE virtual const KeyframeBase* keyframe_containing(double time) const = 0; virtual KeyframeBase* keyframe_containing(FrameTime time) = 0; /** * \brief Returns the keyframe fully before the given time * * * keyframe_before * v-------v * t0 t1 t2 t3 * |-------|-------|-------| * ^ ^ * time * */ Q_INVOKABLE virtual KeyframeBase* keyframe_before(double time) = 0; virtual const KeyframeBase* keyframe_before(FrameTime time) const = 0; /** * \brief Returns the keyframe fully before the given time * * * keyframe_before * v-------v * t0 t1 t2 t3 * |-------|-------|-------| * ^ ^ * time * */ Q_INVOKABLE virtual KeyframeBase* keyframe_after(double time) = 0; virtual const KeyframeBase* keyframe_after(FrameTime time) const = 0; /** * \brief Keyframe at that specific time * \return the Corresponding keyframe or nullptr if not found */ Q_INVOKABLE virtual const KeyframeBase* keyframe_at(double time) const = 0; virtual KeyframeBase* keyframe_at(FrameTime time) = 0; /** * \brief Whether it has multiple keyframes */ bool animated() const { return keyframe_count() != 0; } /** * If animated(), whether the current value has been changed over the animated value */ Q_INVOKABLE virtual bool value_mismatch() const = 0; KeyframeStatus keyframe_status(FrameTime time) const { if ( !animated() ) return NotAnimated; if ( value_mismatch() ) return Mismatch; if ( keyframe_at(time) ) return IsKeyframe; return Tween; } Q_INVOKABLE bool has_keyframe(double time) const { if ( !animated() ) return false; return keyframe_at(time); } virtual detail::TypeErasedKeyframeRange keyframe_range() const = 0; virtual detail::TypeErasedKeyframeIterator find(model::FrameTime t) const = 0; Q_INVOKABLE virtual const KeyframeBase* first_keyframe() const = 0; Q_INVOKABLE virtual const KeyframeBase* last_keyframe() const = 0; // Renamed to avoid clashing with BaseProperty /** * \brief Get the value at the given time */ Q_INVOKABLE virtual QVariant value_at_time(double time) const = 0; // Renamed to avoid clashing with BaseProperty Q_INVOKABLE virtual QVariant static_value() const = 0; virtual bool set_static_value(const QVariant& v) = 0; /** * \brief Adds a keyframe at the given time */ virtual QUndoCommand* command_add_smooth_keyframe(FrameTime time, const QVariant& value, bool commit = true, QUndoCommand* parent = nullptr) = 0; virtual QUndoCommand* command_remove_keyframe(FrameTime time, QUndoCommand* parent = nullptr) = 0; /** * \brief Clears all keyframes and creates an associated undo action */ virtual QUndoCommand* command_clear_keyframes(QUndoCommand* parent = nullptr) = 0; /** * @brief Creates an appropriate undo command for changing a keyframe's transition */ virtual QUndoCommand* command_set_transition(model::FrameTime time, const model::KeyframeTransition& transition, QUndoCommand* parent = nullptr) = 0; virtual QUndoCommand* command_set_transition_side( model::FrameTime time, model::KeyframeTransition::Descriptive desc, const QPointF& point, bool before_transition, QUndoCommand* parent = nullptr ) = 0; virtual QUndoCommand* command_move_keyframe(model::FrameTime time_before, model::FrameTime time_after, QUndoCommand* parent = nullptr) = 0; // Renamed to avoid clashing with BaseProperty Q_INVOKABLE virtual QString visual_name() const = 0; MidTransition mid_transition(FrameTime time) const; virtual int property_type() const; protected: MidTransition do_mid_transition(const KeyframeBase* kf_before, const KeyframeBase* kf_after, qreal ratio) const; virtual QVariant do_mid_transition_value(const KeyframeBase* kf_before, const KeyframeBase* kf_after, qreal ratio) const = 0; Q_SIGNALS: void keyframe_added(FrameTime time); void keyframe_removed(FrameTime time); void keyframe_updated(FrameTime time); void keyframe_moved(FrameTime from_time, FrameTime to_time); void transition_changed(FrameTime time, KeyframeTransition::Descriptive before, KeyframeTransition::Descriptive after); }; namespace detail { /** * @brief Implements common operations based on the keyframe container */ template class AnimatableImpl : public Base { public: using Ctor = AnimatableImpl; using keyframe_type = KeyframeT; using Base::Base; int keyframe_count() const override { return keyframes_.size(); } const keyframe_type* keyframe_containing(FrameTime time) const override { auto it = keyframes_.find_best(time); if ( it == keyframes_.end() ) return nullptr; return it.ptr(); } keyframe_type* keyframe_containing(FrameTime time) override { auto it = keyframes_.find_best(time); if ( it == keyframes_.end() ) return nullptr; return it.ptr(); } keyframe_type* keyframe_before(FrameTime time) override { auto it = keyframes_.find_best(time); if ( it == keyframes_.end() || it == keyframes_.begin()) return nullptr; --it; return it.ptr(); } keyframe_type* keyframe_after(FrameTime time) override { auto it = keyframes_.upper_bound(time); if ( it == keyframes_.end() ) return nullptr; return it.ptr(); } const keyframe_type* keyframe_before(FrameTime time) const override { auto it = keyframes_.find_best(time); if ( it == keyframes_.end() || it == keyframes_.begin()) return nullptr; --it; return it.ptr(); } const keyframe_type* keyframe_after(FrameTime time) const override { auto it = keyframes_.upper_bound(time); if ( it == keyframes_.end() ) return nullptr; return it.ptr(); } const keyframe_type* keyframe_at(FrameTime time) const override { auto it = keyframes_.find(time); if ( it == keyframes_.end() ) return nullptr; return it.ptr(); } keyframe_type* keyframe_at(FrameTime time) override { auto it = keyframes_.find(time); if ( it == keyframes_.end() ) return nullptr; return it.ptr(); } detail::TypeErasedKeyframeRange keyframe_range() const override { return keyframes_.type_erased(); } detail::TypeErasedKeyframeIterator find(model::FrameTime t) const override { return keyframes_.type_erased(keyframes_.find(t)); } const KeyframeBase* first_keyframe() const override { return keyframes_.empty() ? nullptr : keyframes_.begin().ptr(); } const KeyframeBase* last_keyframe() const override { if ( keyframes_.empty() ) return nullptr; auto iter = keyframes_.end(); --iter; return iter.ptr(); } typename KeyframeContainer::const_iterator begin() const { return this->keyframes_.begin(); } typename KeyframeContainer::const_iterator end() const { return this->keyframes_.end(); } protected: KeyframeContainer keyframes_; }; } // namespace detail } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_serializer.hpp000664 001750 001750 00000001262 15165022620 037227 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/binary_stream.hpp" #include "glaxnimate/module/extraformats/rive/type_system.hpp" namespace glaxnimate::io::rive { class RiveSerializer { public: explicit RiveSerializer(QIODevice* file); void write_header(int vmaj, int vmin, Identifier file_id); void write_property_table(const PropertyTable& properties); void write_object(const Object& output); void write_property_value(PropertyType id, const QVariant& value); private: BinaryOutputStream stream; }; } // namespace glaxnimate::io::rive mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/thorvg/thorvg_renderer.hpp000664 001750 001750 00000033120 15165022620 035132 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/renderer/renderer.hpp" namespace glaxnimate::thorvg { /** * \brief Renderer that uses ThorVG */ class ThorvgRenderer : public renderer::Renderer { private: renderer::Fill fill; renderer::Stroke stroke; int mode = renderer::ShapeMode::NothingMode; int mask_flags = 0; int effect_quality; // BlendMode blend_mode = BlendMode::Normal; std::unique_ptr canvas; std::vector layers; tvg::Fill* make_fill(const QBrush& brush, qreal opacity) { auto gradient = brush.gradient(); if ( !gradient ) return nullptr; tvg::Fill* fill = nullptr; if ( gradient->type() == QGradient::RadialGradient ) { const QRadialGradient* rad = static_cast(gradient); auto radial = tvg::RadialGradient::gen(); fill = radial; radial->radial( rad->center().x(), rad->center().y(), rad->centerRadius(), rad->focalPoint().x(), rad->focalPoint().y(), rad->focalRadius() ); } else if ( gradient->type() == QGradient::LinearGradient ) { const QLinearGradient* lin = static_cast(gradient); auto linear = tvg::LinearGradient::gen(); fill = linear; linear->linear( lin->start().x(), lin->start().y(), lin->finalStop().x(), lin->finalStop().y() ); } if ( !fill ) return nullptr; std::vector stops; for ( const auto& stop : gradient->stops() ) { const auto& color = stop.second; stops.push_back({float(stop.first), uint8_t(color.red()), uint8_t(color.green()), uint8_t(color.blue()), uint8_t(color.alpha() * opacity)}); } fill->colorStops(stops.data(), stops.size()); fill->spread( gradient->spread() == QGradient::PadSpread ? tvg::FillSpread::Pad : (gradient->spread() == QGradient::ReflectSpread ? tvg::FillSpread::Reflect : tvg::FillSpread::Repeat ) ); fill->transform(convert_transform(brush.transform())); return fill; } tvg::Matrix convert_transform(const QTransform& matrix) { return tvg::Matrix { float(matrix.m11()), float(matrix.m21()), float(matrix.m31()), float(matrix.m12()), float(matrix.m22()), float(matrix.m32()), float(matrix.m13()), float(matrix.m23()), float(matrix.m33()), }; } void draw_path(const math::bezier::MultiBezier& path, tvg::Shape* shape) { for ( const auto& bez : path.beziers() ) { if ( bez.size() == 0 ) continue; shape->moveTo(bez[0].pos.x(), bez[0].pos.y()); for ( int i = 0; i < bez.size() - 1; i++ ) { const auto& before = bez[i]; const auto& after = bez[i+1]; shape->cubicTo( before.tan_out.x(), before.tan_out.y(), after.tan_in.x(), after.tan_in.y(), after.pos.x(), after.pos.y() ); } if ( bez.closed() ) { const auto& before = bez.back(); const auto& after = bez[0]; shape->cubicTo( before.tan_out.x(), before.tan_out.y(), after.tan_in.x(), after.tan_in.y(), after.pos.x(), after.pos.y() ); shape->close(); } } } tvg::ColorSpace convert_image_format(QImage::Format fmt) const { switch ( fmt ) { case QImage::Format_ARGB32: return tvg::ColorSpace::ARGB8888S; case QImage::Format_ARGB32_Premultiplied: return tvg::ColorSpace::ARGB8888; case QImage::Format_Grayscale8: return tvg::ColorSpace::Grayscale8; default: return tvg::ColorSpace::Unknown; } } tvg::Picture* convert_image(const QImage & image, bool copy = false) { auto picture = tvg::Picture::gen(); auto result = picture->load( reinterpret_cast(image.constBits()), image.width(), image.height(), convert_image_format(image.format()), copy ); if ( result != tvg::Result::Success ) { picture->unref(); return nullptr; } return picture; } public: explicit ThorvgRenderer(int quality): effect_quality(quality * 10) { // 4 threads tvg::Initializer::init(4); } ~ThorvgRenderer() { tvg::Initializer::term(); } int supported_surfaces() const override { return renderer::SurfaceType::OpenGL; } void set_image_surface(QImage * destination) override { auto sw_canvas = tvg::SwCanvas::gen(); sw_canvas->target( reinterpret_cast(destination->bits()), // destination->bytesPerLine(), destination->width(), destination->width(), destination->height(), convert_image_format(destination->format()) ); canvas.reset(sw_canvas); } bool set_gl_surface(void * context, int framebuffer, int width, int height) override { #ifdef OPENGL_ENABLED auto gl_canvas = tvg::GlCanvas::gen(); if ( !gl_canvas ) return false; canvas.reset(gl_canvas); return gl_canvas->target(nullptr, nullptr, context, framebuffer, width, height, tvg::ColorSpace::ABGR8888S) == tvg::Result::Success; #else Q_UNUSED(context); Q_UNUSED(framebuffer); Q_UNUSED(width); Q_UNUSED(height); return false; #endif } bool set_painter_surface(QPainter*, int, int) override { return false; } void render_start() override { layer_start(); } void render_end() override { canvas->add(layers[0]); layers.clear(); canvas->draw(true); canvas->sync(); mask_flags = 0; } void set_fill(const renderer::Fill & fill) override { this->fill = fill; mode |= renderer::FillMode; } void set_stroke(const renderer::Stroke & stroke) override { this->stroke = stroke; mode |= renderer::StrokeMode; } void fill_rect(const QRectF & rect, const QBrush & brush) override { auto shape = tvg::Shape::gen(); shape->appendRect(rect.left(), rect.top(), rect.width(), rect.height(), 0, 0); if ( auto fill = make_fill(brush, 1) ) { shape->fill(fill); } else { QColor col = brush.color(); shape->fill(col.red(), col.green(), col.blue(), col.alpha()); } layers.back()->add(shape); } void draw_path(const math::bezier::MultiBezier & path) override { if ( !mode ) return; std::optional mask_brush; auto shape = tvg::Shape::gen(); draw_path(path, shape); if ( mode & renderer::FillMode ) { if ( auto tfill = make_fill(fill.brush, fill.opacity) ) { shape->fill(tfill); } else if ( fill.brush.gradient() && fill.brush.gradient()->type() == QGradient::ConicalGradient ) { mask_brush = fill.brush; shape->fill(255, 255, 255, 255); } else { QColor col = fill.brush.color(); shape->fill(col.red(), col.green(), col.blue(), col.alpha() * fill.opacity); } shape->fillRule(fill.rule == Qt::OddEvenFill ? tvg::FillRule::EvenOdd : tvg::FillRule::NonZero); } if ( mode & renderer::StrokeMode ) { if ( auto tfill = make_fill(stroke.pen.brush(), stroke.opacity) ) { shape->strokeFill(tfill); } else if ( stroke.pen.brush().gradient() && stroke.pen.brush().gradient()->type() == QGradient::ConicalGradient ) { mask_brush = stroke.pen.brush(); shape->strokeFill(255, 255, 255, 255); } else { QColor col = stroke.pen.brush().color(); shape->strokeFill(col.red(), col.green(), col.blue(), col.alpha() * stroke.opacity); } shape->strokeWidth(stroke.pen.width()); auto qcap = stroke.pen.capStyle(); auto cap = qcap == Qt::RoundCap ? tvg::StrokeCap::Round : (qcap == Qt::SquareCap ? tvg::StrokeCap::Square : tvg::StrokeCap::Butt ) ; shape->strokeCap(cap); auto qjoin = stroke.pen.joinStyle(); auto join = qjoin == Qt::RoundJoin ? tvg::StrokeJoin::Round : (qjoin == Qt::MiterJoin ? tvg::StrokeJoin::Miter : tvg::StrokeJoin::Bevel ) ; shape->strokeJoin(join); shape->strokeMiterlimit(stroke.pen.miterLimit()); } if ( mask_brush ) { // Bit of a hack and very slow but should be fine for now auto rect = path.bounding_box(); if ( mode & renderer::StrokeMode ) { auto margin = stroke.pen.width() / 2; rect.adjust(-margin, -margin, margin, margin); } QImage image(rect.width(), rect.height(), QImage::Format_ARGB32); QPainter painter(&image); auto tr = mask_brush->transform(); tr.translate(-rect.left(), -rect.top()); mask_brush->setTransform(tr); painter.fillRect(0, 0, rect.width(), rect.height(), *mask_brush); painter.end(); if ( auto picture = convert_image(image, true) ) { auto scene = tvg::Scene::gen(); scene->mask(shape, tvg::MaskMethod::Alpha); picture->translate(rect.left(), rect.top()); picture->clip(shape); // layers.back()->add(picture); scene->add(picture); layers.back()->add(scene); } } else { layers.back()->add(shape); } mode = renderer::NothingMode; } void layer_start() override { layers.push_back(tvg::Scene::gen()); } void layer_end() override { auto scene = layers.back(); layers.pop_back(); layers.back()->add(scene); } void set_opacity(qreal opacity) override { layers.back()->opacity(opacity * 255); } qreal opacity() const override { return layers.back()->opacity() / 255.; } void clip_rect(const QRectF & rect) override { auto shape = tvg::Shape::gen(); shape->transform(layers.back()->transform()); shape->appendRect(rect.left(), rect.top(), rect.width(), rect.height(), 0, 0); layers.back()->clip(shape); } void draw_image(const QImage & image) override { if ( auto picture = convert_image(image) ) layers.back()->add(picture); } void fill_pattern(const QRectF& rect, const QImage& pattern) override { if ( auto picture = convert_image(pattern) ) { layer_start(); clip_rect(rect); for ( int y = rect.top(); y < rect.bottom(); y += pattern.height() ) { for ( int x = rect.left(); x < rect.right(); x += pattern.width() ) { auto copy = picture->duplicate(); copy->translate(x, y); layers.back()->add(copy); } } layer_end(); picture->unref(); } } void scale(qreal x, qreal) override { layers.back()->scale(x); } void translate(qreal x, qreal y) override { layers.back()->translate(x, y); } void transform(const QTransform & matrix) override { layers.back()->transform(convert_transform(matrix)); } void mask_start(int mask_flags) override { layer_start(); this->mask_flags = mask_flags; } void mask_end() override { auto mask = layers.back(); layers.pop_back(); if ( !mask_flags ) return; tvg::MaskMethod method = tvg::MaskMethod::None; bool inverted = mask_flags & renderer::MaskFlags::MaskInverted; if ( mask_flags & renderer::MaskFlags::MaskSourceLuma ) method = inverted ? tvg::MaskMethod::InvLuma : tvg::MaskMethod::Luma; else method = inverted ? tvg::MaskMethod::InvAlpha : tvg::MaskMethod::Alpha; layers.back()->mask(mask, method); mask_flags = 0; } void set_blend_mode(renderer::BlendMode mode) override { layers.back()->blend(tvg::BlendMethod(mode)); } void set_quality(int quality) override { effect_quality = quality; } }; } // namespace glaxnimate::renderer mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/embedded_font.cpp000664 001750 001750 00000003140 15165022620 034310 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/embedded_font.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/command/object_list_commands.hpp" #include "glaxnimate/model/assets/assets.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::EmbeddedFont) glaxnimate::model::EmbeddedFont::EmbeddedFont(model::Document* document) : Asset(document) { } glaxnimate::model::EmbeddedFont::EmbeddedFont(model::Document* document, CustomFont custom_font) : Asset(document), custom_font_(std::move(custom_font)) { this->data.set(this->custom_font_.data()); this->source_url.set(this->custom_font_.source_url()); this->css_url.set(this->custom_font_.css_url()); } QIcon glaxnimate::model::EmbeddedFont::instance_icon() const { return QIcon::fromTheme("font"); } QString glaxnimate::model::EmbeddedFont::object_name() const { return custom_font_.family() + " " + custom_font_.style_name(); } QString glaxnimate::model::EmbeddedFont::type_name_human() const { return i18n("Font"); } bool glaxnimate::model::EmbeddedFont::remove_if_unused(bool clean_lists) { /// \todo Needs a way to keep track users... if ( clean_lists && users().empty() ) { document()->push_command(new command::RemoveObject( this, &document()->assets()->fonts->values )); return true; } return false; } void glaxnimate::model::EmbeddedFont::on_data_changed() { custom_font_ = CustomFontDatabase::instance().add_font("", data.get()); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/simple_visitor.hpp000664 001750 001750 00000001437 15165022620 033313 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/visitor.hpp" namespace glaxnimate::model { template class SimpleVisitor : public Visitor { public: SimpleVisitor(Callback callback) : callback(std::move(callback)) {} private: void on_visit(model::DocumentNode* node) override { if ( auto obj = node->cast() ) callback(obj); } Callback callback; }; template void simple_visit(model::DocumentNode* node, bool skip_locked, Callback callback) { SimpleVisitor(std::move(callback)).visit(node, skip_locked); } } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/000775 001750 001750 00000000000 15165022620 032273 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/composition.cpp000664 001750 001750 00000004614 15165022620 034103 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/command/object_list_commands.hpp" #include "glaxnimate/renderer/renderer.hpp" using namespace glaxnimate; GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Composition) QIcon glaxnimate::model::Composition::tree_icon() const { return QIcon::fromTheme("video-x-generic"); } QString glaxnimate::model::Composition::type_name_human() const { return i18n("Composition"); } bool glaxnimate::model::Composition::remove_if_unused(bool clean_lists) { if ( clean_lists && users().empty() ) { document()->push_command(new command::RemoveObject( this, &document()->assets()->compositions->values )); return true; } return false; } glaxnimate::model::DocumentNode * glaxnimate::model::Composition::docnode_parent() const { return document()->assets()->compositions.get(); } int glaxnimate::model::Composition::docnode_child_index(glaxnimate::model::DocumentNode* dn) const { return shapes.index_of(static_cast(dn)); } QRectF glaxnimate::model::Composition::content_rect() const { if ( shapes.empty() ) return rect(); return shapes.bounding_rect(time()); } QRectF glaxnimate::model::Composition::local_bounding_rect(FrameTime) const { return rect(); } QImage glaxnimate::model::Composition::render_image(float time, QSize image_size, const QColor& background) const { QSizeF real_size = size(); if ( !image_size.isValid() ) image_size = real_size.toSize(); QImage image(image_size, QImage::Format_ARGB32); if ( !background.isValid() ) image.fill(Qt::transparent); else image.fill(background); auto renderer = renderer::RendererRegistry::instance().default_renderer(10); renderer->set_image_surface(&image); renderer->render_start(); renderer->scale( image_size.width() / real_size.width(), image_size.height() / real_size.height() ); paint(renderer.get(), time, VisualNode::Render); renderer->render_end(); return image; } QImage glaxnimate::model::Composition::render_image() const { return render_image(document()->current_time(), size().toSize()); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/ae_project.hpp000664 001750 001750 00000036513 15165022620 035753 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "glaxnimate/model/animation/frame_time.hpp" #include "glaxnimate/math/math.hpp" #include "glaxnimate/math/vector.hpp" #include "glaxnimate/utils/iterator.hpp" namespace glaxnimate::io::aep { using Id = std::uint32_t; struct PropertyIterator; struct PropertyPair; struct PropertyBase { enum Type { Null, PropertyGroup, Property, TextProperty, EffectInstance, Mask }; virtual ~PropertyBase() noexcept = default; virtual Type class_type() const noexcept { return Null; } const PropertyBase* get(const QString& key) const; virtual const PropertyPair* get_pair(const QString& key) const { Q_UNUSED(key); return nullptr; } explicit operator bool() const { return class_type() != Null; } const PropertyBase& operator[](const QString& key) const { auto prop = get(key); if ( prop ) return *prop; static PropertyBase null_property; return null_property; } virtual PropertyIterator begin() const; virtual PropertyIterator end() const; }; struct PropertyPair { QString match_name; std::unique_ptr value; }; inline const PropertyBase* PropertyBase::get(const QString& key) const { if ( auto p = get_pair(key) ) return p->value.get(); return nullptr; } struct PropertyIterator : public utils::RandomAccessIteratorWrapper::const_iterator> { public: PropertyIterator(InternalIterator it = {}) : Parent(it) {} const PropertyPair* operator->() const { return iter.operator->(); } const PropertyPair& operator*() const { return *iter; } }; inline PropertyIterator PropertyBase::begin() const { return {}; } inline PropertyIterator PropertyBase::end() const { return {}; } struct PropertyGroup : PropertyBase { bool visible = true; QString name = ""; std::vector properties; Type class_type() const noexcept override { return PropertyBase::PropertyGroup; } const PropertyPair* property_pair(const QString& match_name) const { for ( const auto& prop : properties ) { if ( prop.match_name == match_name ) return ∝ } return nullptr; } const PropertyBase* property(const QString& match_name) const { if ( auto p = property_pair(match_name) ) return p->value.get(); return nullptr; } const PropertyPair* get_pair(const QString& key) const override { return property_pair(key); } PropertyIterator begin() const override { return properties.begin(); } PropertyIterator end() const override { return properties.end(); } }; enum class KeyframeTransitionType { Linear = 1, Bezier = 2, Hold = 3, }; enum class KeyframeBezierMode { Normal, Continuous, Auto, }; struct BezierData { bool closed = false; QPointF minimum; QPointF maximum; std::vector points; /** * \brief Converts points from the weird bounding box notation to absolute */ QPointF convert_point(const QPointF& p) const { return { math::lerp(minimum.x(), maximum.x(), p.x()), math::lerp(minimum.y(), maximum.y(), p.y()), }; } QPointF converted_point(int index) const { return convert_point(points[index]); } }; template struct GradientStop { double offset; double mid_point; Type value; }; template class GradientStops : public std::vector> { public: using Stop = GradientStop; using std::vector::vector; int size() const { return std::vector::size(); } T value_at(double t, int& index) const { if ( this->empty() ) return 1; if ( this->size() == 1 ) return this->front().value; if ( t >= this->back().offset || index + 1 >= this->size() ) { index = this->size(); return this->back().value; } while ( t >= (*this)[index+1].offset ) index++; if ( index + 1 >= this->size() ) return this->back().value; const auto& before = (*this)[index]; const auto& after = (*this)[index+1]; auto factor = math::unlerp(before.offset, after.offset, t); if ( !qFuzzyCompare(before.mid_point, 0.5) ) { auto vbefore = before.value; auto vafter = after.value; auto vmid = math::lerp(before.value, after.value, before.mid_point); if ( factor < after.mid_point ) { factor = math::unlerp(0., before.mid_point, factor); vafter = vmid; } else { factor = math::unlerp(before.mid_point, 1., factor); vbefore = vmid; } return math::lerp(vbefore, vafter, factor); } return math::lerp(before.value, after.value, factor); } GradientStops split_midpoints() const { double midpoint = 0.5; Stop previous; GradientStops stops; for ( const auto& stop : *this ) { if ( !qFuzzyCompare(midpoint, 0.5) ) { auto midoffset = math::lerp(previous.offset, stop.offset, midpoint); auto midvalue = math::lerp(previous.value, stop.value, midpoint); stops.push_back({midoffset, 0.5, midvalue}); } midpoint = stop.mid_point; stops.push_back({stop.offset, 0.5, stop.value}); previous = stop; } return stops; } }; struct Gradient { GradientStops alpha_stops; GradientStops color_stops; QGradientStops to_qt() const { QGradientStops stops; int index = 0; for ( const auto& stop : color_stops.split_midpoints() ) { auto alpha = alpha_stops.value_at(stop.offset, index); auto color = stop.value; color.setAlphaF(alpha); stops.push_back({stop.offset, color}); } return stops; } }; enum class LabelColors { None, Red, Yellow, Aqua, Pink, Lavender, Peach, SeaFoam, Blue, Green, Purple, Orange, Brown, Fuchsia, Cyan, Sandstone, DarkGreen, }; struct Marker { model::FrameTime duration = 0; LabelColors label_color = LabelColors::None; bool is_protected = false; QString name = ""; }; struct Font { QString family; }; enum class TextTransform { Normal, SmallCaps, AllCaps }; enum class TextVerticalAlign { Normal, Superscript, Subscript, }; enum class TextJustify { Left, Right, Center, JustifyLastLineLeft, JustifyLastLineRight, JustifyLastLineCenter, JustifyLastLineFull, }; struct LineStyle { TextJustify text_justify = TextJustify::Left; // Number of characters the style applies to // Note that in AE LineStyle represents a line // Rather than an arbitrary span of text int character_count = 0; }; struct CharacterStyle { int font_index = 0; double size = 0; bool faux_bold = false; bool faux_italic = false; TextTransform text_transform = TextTransform::Normal; TextVerticalAlign vertical_align = TextVerticalAlign::Normal; QColor fill_color; QColor stroke_color; bool stroke_enabled = false; bool stroke_over_fill = false; double stroke_width = 0; // Number of characters the style applies to int character_count = 0; }; struct TextDocument { QString text; std::vector line_styles; std::vector character_styles; }; enum class LayerSource { Layer = 0, Effects = -1, Masks = -2 }; struct LayerSelection { Id layer_id; LayerSource layer_source = LayerSource::Layer; }; class PropertyValue { public: enum Index { None, Vector2D, Vector3D, Color, Number, Gradient, BezierData, Marker, TextDocument, LayerSelection }; template PropertyValue(T&& v) : value(std::forward(v)) {} PropertyValue() = default; PropertyValue(PropertyValue& v) = delete; PropertyValue(const PropertyValue& v) = delete; PropertyValue(PropertyValue&& v) = default; PropertyValue& operator=(PropertyValue&& v) = default; Index type() const { return Index(value.index()); } std::variant< std::nullptr_t, QPointF, QVector3D, QColor, qreal, aep::Gradient, aep::BezierData, aep::Marker, aep::TextDocument, aep::LayerSelection > value = nullptr; qreal magnitude() const { switch ( type() ) { default: case None: return 0; case Vector2D: return math::length(std::get(value)); case Vector3D: return math::length(std::get(value)); case Color: { const auto& c = std::get(value); return math::hypot(c.red(), c.green(), c.blue(), c.alpha()); } case Number: return std::get(value); } } }; struct Keyframe { PropertyValue value; model::FrameTime time = 0; std::vector in_influence; std::vector in_speed; std::vector out_influence; std::vector out_speed; QPointF in_tangent; QPointF out_tangent; KeyframeTransitionType transition_type = KeyframeTransitionType::Linear; KeyframeBezierMode bezier_mode = KeyframeBezierMode::Normal; bool roving = false; LabelColors label_color = LabelColors::None; }; enum class PropertyType { Color, NoValue, Position, MultiDimensional, LayerSelection, Integer, MaskIndex, }; struct Property : PropertyBase { bool split = false; bool animated = false; bool is_component = false; int components = 0; PropertyValue value; quint32 keyframe_time_denominator = 1; std::vector keyframes; PropertyType type = PropertyType::MultiDimensional; std::optional expression; Type class_type() const noexcept override { return PropertyBase::Property; } }; struct TextProperty : PropertyBase { std::vector fonts; aep::Property documents; Type class_type() const noexcept override { return PropertyBase::TextProperty; } }; enum class LayerQuality { Wireframe, Draft, Best }; enum class LayerType { AssetLayer, LightLayer, CameraLayer, TextLayer, ShapeLayer }; enum TrackMatteType { None, Alpha, AlphaInverted, Luma, LumaInverted, }; struct Layer { Id id = 0; LayerQuality quality = LayerQuality::Draft; model::FrameTime start_time = 0; qreal time_stretch = 1; model::FrameTime in_time = 0; model::FrameTime out_time = 0; bool is_guide = false; bool bicubic_sampling = false; bool auto_orient = false; bool is_adjustment = false; bool threedimensional = false; bool solo = false; bool is_null = false; bool visible = true; bool effects_enabled = false; bool motion_blur = false; bool locked = false; bool shy = false; bool continuously_rasterize = false; Id asset_id = 0; LabelColors label_color = LabelColors::None; QString name = ""; LayerType type = LayerType::ShapeLayer; Id parent_id = 0; TrackMatteType matte_mode = TrackMatteType::None; Id matte_id = 0; int blend_mode = -1; PropertyGroup properties; }; enum class EffectParameterType { Layer = 0, Scalar = 2, Angle = 3, Boolean = 4, Color = 5, Vector2D = 6, Enum = 7, Group = 9, Slider = 10, Unknown = 15, Vector3D = 16, }; struct EffectParameter { QString match_name = ""; QString name = ""; EffectParameterType type = EffectParameterType::Unknown; PropertyValue default_value; PropertyValue last_value; }; struct EffectDefinition { QString match_name; QString name; std::vector parameters; std::map parameter_map; }; struct EffectInstance : PropertyBase { QString name; aep::PropertyGroup parameters; Type class_type() const noexcept override { return PropertyBase::EffectInstance; } }; enum class MaskMode { None, Add, Subtract, Intersect, Darken, Lighten, Difference }; struct Mask : PropertyBase { bool inverted = false; bool locked = false; MaskMode mode = MaskMode::Add; aep::PropertyGroup properties; Type class_type() const noexcept override { return PropertyBase::Mask; } const PropertyPair* get_pair(const QString& key) const override { return properties.property_pair(key); } }; struct FolderItem { enum Type { Composition, Folder, Asset, Solid }; Id id; QString name = ""; LabelColors label_color = LabelColors::None; virtual ~FolderItem() noexcept = default; virtual Type type() const noexcept = 0; }; struct Composition : FolderItem { std::vector> layers; std::uint16_t resolution_x = 0; std::uint16_t resolution_y = 0; double frame_time = 0; model::FrameTime playhead_time = 0; model::FrameTime in_time = 0; model::FrameTime out_time = 0; model::FrameTime duration = 0; QColor color; bool shy = false; bool motion_blur = false; bool frame_blending = false; bool preserve_framerate = false; bool preserve_resolution = false; double width = 0; double height = 0; std::uint32_t pixel_ratio_width = 1; std::uint32_t pixel_ratio_height = 1; std::uint16_t framerate = 0; std::uint16_t shutter_angle = 0; std::int32_t shutter_phase = 0; std::uint32_t samples_limit = 0; std::uint32_t samples_per_frame = 0; std::unique_ptr markers; std::vector> views; Type type() const noexcept override { return FolderItem::Composition; } model::FrameTime time_to_frames(model::FrameTime time) const { return time / frame_time; }; }; struct Asset : FolderItem { int width = 0; int height = 0; }; struct FileAsset : Asset { QFileInfo path; Type type() const noexcept override { return FolderItem::Asset; } }; struct Solid : Asset { QColor color; Type type() const noexcept override { return FolderItem::Solid; } }; struct Folder : FolderItem { std::vector> items; template T* add() { auto item = std::make_unique(); auto ptr = item.get(); items.push_back(std::move(item)); return ptr; } Type type() const noexcept override { return FolderItem::Folder; } }; struct Project { std::unordered_map assets; Folder folder; std::vector compositions; std::unordered_map effects; FolderItem* current_item = nullptr; }; } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/renderer/renderer.hpp000664 001750 001750 00000011530 15165022620 032552 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/math/bezier/bezier.hpp" namespace glaxnimate::renderer { Q_NAMESPACE struct Fill { QBrush brush = {}; qreal opacity = 1; Qt::FillRule rule = Qt::OddEvenFill; }; struct Stroke { QPen pen = {}; qreal opacity = 1; }; enum ShapeMode { NothingMode = 0, FillMode = 1, StrokeMode = 2 }; enum SurfaceType { OpenGL = 0x10, Painter = 0x20, }; enum MaskFlags { MaskSourceAlpha = 0x0001, ///< Use mask Alpha to determine what is shown MaskSourceLuma = 0x0002, ///< Use mask Luma to determine what is shown MaskInverted = 0x0100, ///< Invert masking }; enum class BlendMode { Normal = 0, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity, Add, //HardMix, // in lottie but not in svg }; Q_ENUM_NS(BlendMode) /** * Abstracted renderer interface */ class Renderer { public: Renderer() = default; Renderer(const Renderer&) = delete; Renderer& operator=(const Renderer&) = delete; virtual ~Renderer() noexcept = default; // Query virtual int supported_surfaces() const = 0; bool supports_surface(int type) const { return (supported_surfaces() & type) == type; } // Surface setup /** * \brief Sets \p destination as the destination surface */ virtual void set_image_surface(QImage* destination) = 0; /** * \brief Sets the given OpenGL context as the destination surface * \returns \b true on success */ virtual bool set_gl_surface(void* context, int framebuffer, int width, int height) = 0; /** * \brief Sets a QPainter as the render target */ virtual bool set_painter_surface(QPainter* painter, int width, int height) = 0; /** * \brief Performs any operation needed to begin the rendering */ virtual void render_start() = 0; /** * \brief Cleans up and applies any pending drawings to the surface */ virtual void render_end() = 0; // Drawing ops /** * \brief Sets the fill for the following drawing operation */ virtual void set_fill(const Fill& fill) = 0; /** * \brief Sets the stroke for the following drawing operation */ virtual void set_stroke(const Stroke& stroke) = 0; /** * \brief Draws the path based on the last specified fill or stroke */ virtual void draw_path(const math::bezier::MultiBezier& bez) = 0; /** * \brief Fills a rectangle with a brush */ virtual void fill_rect(const QRectF& rect, const QBrush& brush) = 0; /** * \brief Fills a rectangle with a repeating image */ virtual void fill_pattern(const QRectF& rect, const QImage& pattern) = 0; // Compositing ops /** * \brief Starts a new layer for compositing */ virtual void layer_start() = 0; /** * \brief Applies the layer to the current surface */ virtual void layer_end() = 0; /** * \brief Sets the blend mode for the current layer */ virtual void set_blend_mode(BlendMode mode) = 0; /** * \brief Starts a new masking layer */ virtual void mask_start(int mask_flags) = 0; /** * \brief Applies the mask to the current surface */ virtual void mask_end() = 0; /** * \brief Sets layer opacity */ virtual void set_opacity(qreal opacity) = 0; /** * \brief Layer opacity */ virtual qreal opacity() const = 0; /** * \brief Sets a clipping rect */ virtual void clip_rect(const QRectF& rect) = 0; virtual void draw_image(const QImage& image) = 0; virtual void set_quality(int quality) = 0; // Transform virtual void scale(qreal x, qreal y) = 0; virtual void translate(qreal x, qreal y) = 0; virtual void transform(const QTransform& matrix) = 0; }; class RendererRegistry { public: using FactoryFunction = std::function(int)>; static RendererRegistry& instance(); std::unique_ptr factory_build(const QString& id, int quality); void register_factory(const QString& id, FactoryFunction func, bool make_default=false); const std::map& factories(); /** * \brief Creates the default renderer for the given quality * \param quality number from 0 to 10 */ std::unique_ptr default_renderer(int quality); private: RendererRegistry() {} RendererRegistry(const RendererRegistry&) = delete; RendererRegistry& operator=(const RendererRegistry&) = delete; std::map factory_map; FactoryFunction* default_factory = nullptr; }; } // namespace glaxnimate::renderer mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/font_weight.hpp000664 001750 001750 00000002536 15165022620 032667 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/math/vector.hpp" #include "glaxnimate/math/math.hpp" namespace glaxnimate::io::svg { /** * Compare: * https://doc.qt.io/qt-5/qfont.html#Weight-enum * https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#common_weight_name_mapping */ struct WeightConverter { inline static constexpr const std::array qt = { 00, 12, 25, 50, 57, 63, 75, 81, 87, }; inline static constexpr const std::array css = { 100, 200, 300, 400, 500, 600, 700, 900, 950, }; static int convert(int old, const std::array& from, const std::array& to) { int index; for ( index = 0; index < 9; index++ ) { if ( from[index] == old ) return to[index]; else if ( from[index] > old ) break; } if ( index == 9 ) index--; qreal t = (old - from[index]) / qreal(from[index+1] - from[index]); return qRound(math::lerp(to[index], to[index+1], t)); } }; } // namespace glaxnimate::io::svg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/meta.hpp000664 001750 001750 00000001153 15165022620 032275 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/math/bezier/bezier.hpp" QDataStream& operator<<(QDataStream& ds, const glaxnimate::math::bezier::Point& p); QDataStream& operator<<(QDataStream& ds, const glaxnimate::math::bezier::Bezier& bez); QDataStream& operator>>(QDataStream& ds, glaxnimate::math::bezier::Point& p); QDataStream& operator>>(QDataStream& ds, glaxnimate::math::bezier::Bezier& bez); namespace glaxnimate::math::bezier { void register_meta(); } // namespace glaxnimate::math::bezier mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/data_paths.hpp000664 001750 001750 00000001722 15165022620 032410 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::utils { /** * \brief A path to write user preferences into * \param name Name of the data subdirectory */ QString writable_data_path(const QString& name); /** * \brief Get all available directories to search data from * \param name Name of the data directory */ QStringList data_paths(const QString& name); /** * \brief Get all directories to search data from * * This function may include directories that don't exist but that will be * checked if they existed * * \param name Name of the data directory */ QStringList data_paths_unchecked(const QString& name); /** * \brief Get all possible directories to search data from */ QList data_roots(); } // namespace glaxnimate::utils mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/point.cpp000664 001750 001750 00000001752 15165022620 032500 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/bezier/point.hpp" using namespace glaxnimate; void math::bezier::Point::adjust_handles_from_type() { if ( type != math::bezier::PointType::Corner ) { math::PolarVector p_in(tan_in - pos); math::PolarVector p_out(tan_out - pos); qreal in_angle = (p_in.angle + p_out.angle + pi) / 2; if ( p_in.angle < p_out.angle ) in_angle += pi; p_in.angle = in_angle; p_out.angle = in_angle + pi; if ( type == math::bezier::PointType::Symmetrical ) p_in.length = p_out.length = (p_in.length + p_out.length) / 2; tan_in = p_in.to_cartesian() + pos; tan_out = p_out.to_cartesian() + pos; } } void math::bezier::Point::transform(const QTransform& t) { pos = t.map(pos); tan_in = t.map(tan_in); tan_out = t.map(tan_out); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/sub_object_property.hpp000664 001750 001750 00000005073 15165022620 036133 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/object.hpp" #include "glaxnimate/model/property/property.hpp" #define GLAXNIMATE_SUBOBJECT(type, name) \ public: \ SubObjectProperty name{this, kli18n(#name)}; \ type* get_##name() { return name.get(); } \ private: \ Q_PROPERTY(type* name READ get_##name) \ // macro end namespace glaxnimate::model { class SubObjectPropertyBase : public BaseProperty { public: SubObjectPropertyBase(Object* obj, const utils::LazyLocalizedString& name) : BaseProperty(obj, name, {PropertyTraits::Object}) {} virtual const model::Object* sub_object() const = 0; virtual model::Object* sub_object() = 0; protected: void register_animatable(Object* sub_object); }; template class SubObjectProperty : public SubObjectPropertyBase { public: SubObjectProperty(Object* obj, const utils::LazyLocalizedString& name) : SubObjectPropertyBase(obj, name), sub_obj(obj->document()) { register_animatable(&sub_obj); } const Type* operator->() const { return &sub_obj; } Type* operator->() { return &sub_obj; } QVariant value() const override { return QVariant::fromValue(const_cast(&sub_obj)); } bool valid_value(const QVariant & v) const override { return v.value(); } bool set_value(const QVariant& val) override { if ( !val.canConvert() ) return false; if ( Type* t = val.value() ) return set_clone(t); return false; } Type* set_clone(Type* object) { if ( !object ) return nullptr; sub_obj.assign_from(object); return &sub_obj; } Type* get() { return &sub_obj; } const Type* get() const { return &sub_obj; } model::Object * sub_object() override { return &sub_obj; } const model::Object * sub_object() const override { return &sub_obj; } void set_time(FrameTime t) override { sub_obj.set_time(t); } void transfer(Document* doc) override { sub_obj.transfer(doc); } void stretch_time(qreal multiplier) override { sub_obj.stretch_time(multiplier); } private: Type sub_obj; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/image.hpp000664 001750 001750 00000002624 15165022620 034733 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/assets/bitmap.hpp" #include "glaxnimate/model/shapes/composable/composable.hpp" #include "glaxnimate/model/transform.hpp" #include "glaxnimate/model/property/reference_property.hpp" #include "glaxnimate/model/property/sub_object_property.hpp" namespace glaxnimate::model { class Image : public Composable { GLAXNIMATE_OBJECT(Image) GLAXNIMATE_PROPERTY_REFERENCE(Bitmap, image, &Image::valid_images, &Image::is_valid_image, &Image::on_image_changed) public: using Composable::Composable; void add_shapes(FrameTime, math::bezier::MultiBezier&, const QTransform&) const override; QIcon tree_icon() const override; QString type_name_human() const override; QRectF local_bounding_rect(FrameTime t) const override; QTransform local_transform_matrix(model::FrameTime t) const override; protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(FrameTime t) const override; void on_paint(renderer::Renderer* p, FrameTime t, PaintMode, model::Modifier*) const override; private: std::vector valid_images() const; bool is_valid_image(DocumentNode* node) const; void on_image_changed(Bitmap* new_use, Bitmap* old_use); void on_update_image(); }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/round_corners.hpp000664 001750 001750 00000001313 15165022620 036303 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/modifiers/path_modifier.hpp" namespace glaxnimate::model { class RoundCorners : public StaticOverrides { GLAXNIMATE_OBJECT(RoundCorners) GLAXNIMATE_ANIMATABLE(float, radius, 0, {}, 0,) public: using Ctor::Ctor; static QIcon static_tree_icon(); static QString static_type_name_human(); math::bezier::MultiBezier process(FrameTime t, const math::bezier::MultiBezier& mbez) const override; protected: bool process_collected() const override; }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/aep_riff.hpp000664 001750 001750 00000001724 15165022620 035407 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/extraformats/aep/riff.hpp" namespace glaxnimate::io::aep { class AepRiff : public RiffReader { public: static bool is_fake_list(const ChunkId& header) { return header == "tdsn" || header == "fnam" || header == "pdnm"; } protected: void on_chunk(RiffChunk & chunk) override { if ( is_fake_list(chunk.header) ) { chunk.children = read_chunks(chunk.reader); } else if ( chunk.header == "LIST" ) { chunk.subheader = chunk.reader.read(4); if ( chunk.subheader != "btdk" ) chunk.children = read_chunks(chunk.reader); else chunk.reader.defer(); } else { chunk.reader.defer(); } } }; } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/log/logger.hpp000664 001750 001750 00000002753 15165022620 031205 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/log/log_line.hpp" namespace glaxnimate::log { class Logger; class LogListener { public: LogListener() {} virtual ~LogListener() = default; protected: virtual void on_line(const LogLine& line) = 0; friend class Logger; }; class Logger : public QObject { Q_OBJECT public: static QString severity_name(Severity s) { switch ( s ) { case Info: return "Info"; case Warning: return "Warning"; case Error: return "Error"; default: return "?"; } } static Logger& instance() { static Logger instance; return instance; } template T* add_listener(Args&&... args) { listeners.push_back(std::make_unique(std::forward(args)...)); return static_cast(listeners.back().get()); } public Q_SLOTS: void log(const LogLine& line) { for ( const auto& listener : listeners ) listener->on_line(line); Q_EMIT logged(line); } Q_SIGNALS: void logged(const LogLine& line); private: Logger() = default; ~Logger() = default; std::vector> listeners; }; } // namespace glaxnimate::log mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/video/video_module.cpp000664 001750 001750 00000001670 15165022620 034203 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "video_module.hpp" #include "video_format.hpp" #include #include #include #include std::vector glaxnimate::video::Module::components() const { return { {QStringLiteral("avutil"), {}, LIBAVUTIL_IDENT, QStringLiteral("https://libav.org/"), "LGPL"}, {QStringLiteral("avformat"), {}, LIBAVFORMAT_IDENT, QStringLiteral("https://libav.org/"), "LGPL"}, {QStringLiteral("avcodec"), {}, LIBAVCODEC_IDENT, QStringLiteral("https://libav.org/"), "LGPL"}, {QStringLiteral("swscale"), {}, LIBSWSCALE_IDENT, QStringLiteral("https://libav.org/"), "LGPL"}, }; } void glaxnimate::video::Module::initialize() { register_io_classes(); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/riff.hpp000664 001750 001750 00000033267 15165022620 034650 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include #include #include #include #include "glaxnimate/utils/i18n.hpp" namespace glaxnimate::io::aep { template struct IntSize; template<> struct IntSize<1> { using uint = std::uint8_t; using sint = std::int8_t; }; template<> struct IntSize<2> { using uint = std::uint16_t; using sint = std::int16_t; }; template<> struct IntSize<3> { using uint = std::uint32_t; using sint = std::int32_t; }; template<> struct IntSize<4> { using uint = std::uint32_t; using sint = std::int32_t; }; template<> struct IntSize<8> { using uint = std::uint64_t; using sint = std::int64_t; }; class Endianness { public: template constexpr T read_uint(const QByteArray& arr) const noexcept { if constexpr ( sizeof(T) == 1 ) { return arr[0]; } else { T v = 0; for ( int i = 0; i < arr.size(); i++ ) { int j = swap() ? arr.size() - i - 1 : i; v <<= 8; v |= std::uint8_t(arr[j]); } return v; } } template constexpr typename IntSize::uint read_uint(const QByteArray& arr) const noexcept { return read_uint::uint>(arr); } template constexpr typename IntSize::sint read_sint(const QByteArray& arr) const noexcept { using uint_t = typename IntSize::uint; using sint_t = typename IntSize::uint; uint_t uint = read_uint(arr); constexpr const uint_t sbit = 1ull << (size * 8 - 1); if ( !(uint & sbit) ) return uint; return -sint_t(~uint + 1); } template constexpr T read_sint(const QByteArray& arr) const noexcept { return read_uint(arr); } /** * \note Expects IEEE 754 floats */ constexpr float read_float32(const QByteArray& arr) const noexcept { union { std::uint32_t vali; float valf; } x {read_uint(arr)}; return x.valf; } /** * \note Expects IEEE 754 floats */ constexpr double read_float64(const QByteArray& arr) const noexcept { union { std::uint64_t vali; double valf; } x {read_uint(arr)}; return x.valf; } template QByteArray write_uint(T val) const { QByteArray out(sizeof(T), 0); for ( int i = 0; i < out.size(); i++ ) { int j = i; if ( byte_order == QSysInfo::Endian::BigEndian ) j = sizeof(T) - 1 - i; out[j] = val & 0xff; val >>= 8; } return out; } /** * \note Expects IEEE 754 floats */ QByteArray write_float32(float val) const noexcept { union { float valf; std::uint32_t vali; } x {val}; return write_uint(x.vali); } /** * \note Expects IEEE 754 floats */ QByteArray write_float64(double val) const noexcept { union { double valf; std::uint64_t vali; } x {val}; return write_uint(x.vali); } static constexpr const Endianness Big() noexcept { return {QSysInfo::BigEndian}; } static constexpr const Endianness Little() noexcept { return {QSysInfo::LittleEndian}; } private: constexpr bool swap() const noexcept { return QSysInfo::ByteOrder == byte_order; } constexpr Endianness(QSysInfo::Endian byte_order) noexcept : byte_order(byte_order) {} QSysInfo::Endian byte_order; }; class RiffError : public std::runtime_error { public: RiffError(QString message) : runtime_error(message.toStdString()), message(std::move(message)) {} QString message; }; class Flags { public: constexpr Flags(std::uint32_t data) noexcept : data(data) {} constexpr bool get(int byte, int bit) const noexcept { return (data >> (8*byte)) & (1 << bit); } private: std::uint32_t data; }; class BinaryReader { public: BinaryReader() : endian(Endianness::Big()), file(nullptr), file_pos(0), length_left(0) {} BinaryReader(Endianness endian, QIODevice* file, std::uint32_t length, qint64 pos) : endian(endian), file(file), file_pos(pos), length_left(length) {} BinaryReader(Endianness endian, QIODevice* file, std::uint32_t length) : endian(endian), file(file), file_pos(file->pos()), length_left(length) {} /* BinaryReader(Endianness endian, QByteArray& data, std::uint32_t length) : endian(endian), buffer(std::make_unique(&data)), file(buffer.get()), length_left(length) {} */ BinaryReader sub_reader(std::uint32_t length) { if ( length > length_left ) throw RiffError(i18n("Not enough data")); length_left -= length; BinaryReader reader{endian, file, length, file_pos}; file_pos += length; return reader; } /** * \brief Creates a sub-reader without affecting the current reader */ BinaryReader sub_reader(std::uint32_t length, std::uint32_t offset) const { if ( length + offset > length_left ) throw RiffError(i18n("Not enough data")); return {endian, file, length, file_pos + offset}; } void set_endianness(const Endianness& endian) { this->endian = endian; } QByteArray read() { return read(length_left); } QByteArray read(std::uint32_t length) { length_left -= length; file_pos += length; auto data = file->read(length); if ( std::uint32_t(data.size()) < length ) throw RiffError(i18n("Not enough data")); return data; } template typename IntSize::uint read_uint() { return endian.read_uint(read(size)); } template typename IntSize::sint read_sint() { return endian.read_sint(read(size)); } std::uint8_t read_uint8() { return read_uint<1>(); } std::uint16_t read_uint16() { return read_uint<2>(); } std::uint32_t read_uint32() { return read_uint<4>(); } std::int16_t read_sint16() { return read_sint<2>(); } std::int32_t read_sint32() { return read_sint<4>(); } /** * Reads sint32, uint32 and divides them */ qreal read_fraction_64() { auto num = read_sint32(); auto den = read_uint32(); return qreal(num) / den; } float read_float32() { return endian.read_float32(read(4)); } double read_float64() { return endian.read_float64(read(8)); } void skip(std::uint32_t length) { length_left -= length; file_pos += length; if ( file->skip(length) < length ) throw RiffError(i18n("Not enough data")); } std::int64_t available() const { return length_left; } QString read_utf8(std::uint32_t length) { return QString::fromUtf8(read(length)); } /** * \brief Read a NUL-terminated UTF-8 string */ QString read_utf8_nul(std::uint32_t length) { auto data = read(length); int str_len = data.indexOf('\0'); return QString::fromUtf8(data.data(), str_len == -1 ? length : str_len); } QString read_utf8_nul() { return read_utf8_nul(length_left); } std::uint32_t size() const { return length_left; } void prepare() const { file->seek(file_pos); } /** * \brief Defer data reading to a later point */ void defer() { file->skip(length_left); } template std::vector read_array(T (BinaryReader::*read_fn)(), int count) { std::vector out; out.reserve(count); for ( int i = 0; i < count; i++ ) out.push_back((this->*read_fn)()); return out; } QIODevice* device() const { return file; } private: Endianness endian; // std::unique_ptr buffer; QIODevice* file; qint64 file_pos; std::int64_t length_left; }; struct ChunkId { char name[4] = ""; ChunkId(const QByteArray& arr) { std::memcpy(name, (void*)arr.data(), std::min(4, arr.size())); } bool operator==(const char* ch) const { return std::strncmp(name, ch, 4) == 0; } bool operator!=(const char* ch) const { return std::strncmp(name, ch, 4) != 0; } QString to_string() const { return QString::fromLatin1(QByteArray(name, 4)); } }; struct RiffChunk { ChunkId header; std::uint32_t length = 0; ChunkId subheader = {""}; BinaryReader reader = {}; std::vector> children = {}; using iterator = std::vector>::const_iterator; struct RangeIterator { public: RangeIterator(const iterator& internal, const char* name, const RiffChunk* chunk) : internal(internal), name(name), chunk(chunk) {} RangeIterator& operator++() { internal = chunk->find(name, internal + 1); return *this; } const RiffChunk& operator*() const { return **internal; } const RiffChunk* operator->() const { return internal->get(); } bool operator==(const RangeIterator& other) const { return other.internal == internal; } bool operator!=(const RangeIterator& other) const { return other.internal != internal; } private: iterator internal; const char* name; const RiffChunk* chunk; }; struct FindRange { RangeIterator a, b; RangeIterator begin() const { return a; } RangeIterator end() const { return b; } }; bool operator==(const char* name) const { if ( header == name ) return true; if ( header == "LIST" ) return subheader == name; return false; } bool operator!=(const char* name) const { return !(*this == name); } BinaryReader data() const { BinaryReader data = reader; data.prepare(); return data; } iterator find(const char* name) const { return find(name, children.begin()); } iterator find(const char* name, iterator from) const { return std::find_if(from, children.end(), [name](const std::unique_ptr& c){ return *c == name; }); } const RiffChunk* child(const char* name) const { auto it = find(name); if ( it == children.end() ) return nullptr; return it->get(); } FindRange find_all(const char* name) const { return {{find(name), name, this}, {children.end(), name, this}}; } void find_multiple( const std::vector& out, const std::vector names ) const { std::size_t found = 0; for ( const auto& child: children ) { for ( std::size_t i = 0; i < names.size(); i++ ) { if ( !*out[i] && *child == names[i] ) { *out[i] = child.get(); found++; if ( found == names.size() ) return; } } } } const ChunkId& name() const { if ( header == "LIST" ) return subheader; return header; } }; class RiffReader { public: virtual ~RiffReader() = default; RiffChunk parse(QIODevice* file) { auto headerraw = file->read(4); ChunkId header = headerraw; Endianness endian = Endianness::Big(); if ( header == "RIFF" ) endian = Endianness::Little(); else if ( header != "RIFX" ) throw RiffError(i18n("Unknown format %1", QString(headerraw))); auto length = endian.read_uint<4>(file->read(4)); BinaryReader reader = BinaryReader(endian, file, length); ChunkId format = reader.read(4); RiffChunk chunk{header, length, format}; chunk.reader = reader; on_root(chunk); return chunk; } protected: RiffChunk read_chunk(BinaryReader& reader) { ChunkId header = reader.read(4); auto length = reader.read_uint<4>(); RiffChunk chunk{header, length}; chunk.reader = reader.sub_reader(length); on_chunk(chunk); if ( length % 2 ) reader.skip(1); return chunk; } std::vector> read_chunks(BinaryReader& reader) { std::vector> chunks; while ( reader.available() ) chunks.push_back(std::make_unique(read_chunk(reader))); return chunks; } virtual void on_root(RiffChunk& chunk) { chunk.children = read_chunks(chunk.reader); } virtual void on_chunk(RiffChunk& chunk) { if ( chunk.header == "LIST" ) { chunk.subheader = chunk.reader.read(4); chunk.children = read_chunks(chunk.reader); } else { chunk.reader.defer(); } } }; } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/animatable_path.cpp000664 001750 001750 00000011167 15165022620 035327 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/animation/animatable_path.hpp" #include "glaxnimate/command/undo_macro_guard.hpp" #include "glaxnimate/command/animation_commands.hpp" using namespace glaxnimate; void glaxnimate::model::detail::AnimatedPropertyBezier::set_closed(bool closed) { value_.set_closed(closed); for ( auto& keyframe : keyframes_ ) { auto v = keyframe.get(); v.set_closed(closed); keyframe.set(v); } value_changed(); emitter(object(), value_); } void glaxnimate::model::detail::AnimatedPropertyBezier::split_segment(int index, qreal factor) { command::UndoMacroGuard guard(i18n("Split Segment"), object()->document()); QVariant before = QVariant::fromValue(value_); auto bez = value_; bool set = true; for ( const auto& kf : keyframes_ ) { auto bez = kf.get(); bez.split_segment(index, factor); if ( !mismatched_ && kf.time() == time() ) set = false; object()->push_command(new command::SetKeyframe(this, kf.time(), QVariant::fromValue(bez), true, nullptr)); } if ( set ) { bez.split_segment(index, factor); QVariant after = QVariant::fromValue(bez); object()->push_command(new command::SetMultipleAnimated( "", {this}, {before}, {after}, true, time(), object()->document()->record_to_keyframe() )); } } void glaxnimate::model::detail::AnimatedPropertyBezier::remove_point(int index) { remove_points({index}); } void glaxnimate::model::detail::AnimatedPropertyBezier::remove_points(const std::set& indices) { command::UndoMacroGuard guard(i18n("Remove Nodes"), object()->document()); QVariant before = QVariant::fromValue(value_); auto bez = value_; bool set = true; for ( const auto& kf : keyframes_ ) { auto bez = kf.get().removed_points(indices); if ( !mismatched_ && kf.time() == time() ) set = false; object()->push_command(new command::SetKeyframe(this, kf.time(), QVariant::fromValue(bez), true, nullptr)); } if ( set ) { bez = bez.removed_points(indices); object()->push_command(new command::SetMultipleAnimated(this, QVariant::fromValue(bez), true)); } } static QVariant extend_impl(math::bezier::Bezier subject, const math::bezier::Bezier& target, bool at_end) { if ( target.closed() ) { subject.set_closed(true); if ( !subject.empty() ) { if ( at_end ) subject[0].type = math::bezier::Corner; else subject.back().type = math::bezier::Corner; if ( !target.empty() ) { subject[0].tan_in = target[0].tan_in; subject.back().tan_out = target.back().tan_out; } } } if ( subject.size() < target.size() ) { if ( at_end ) { if ( !subject.empty() ) { subject.back().type = math::bezier::Corner; subject.back().tan_out = target.back().tan_out; } subject.points().insert( subject.points().end(), target.points().begin() + subject.size(), target.points().end() ); } else { if ( !subject.empty() ) { subject[0].type = math::bezier::Corner; subject[0].tan_in = target[0].tan_in; } subject.points().insert( subject.points().begin(), target.points().begin(), target.points().begin() + target.size() - subject.size() ); } } return QVariant::fromValue(subject); } void glaxnimate::model::detail::AnimatedPropertyBezier::extend(const math::bezier::Bezier& target, bool at_end) { command::UndoMacroGuard guard(i18n("Extend Shape"), object()->document()); auto bez = value_; bool set = true; for ( auto& kf : keyframes_ ) { if ( !mismatched_ && kf.time() == time() ) set = false; object()->push_command( new command::SetKeyframe(this, kf.time(), extend_impl(kf.get(), target, at_end), true, nullptr) ); } if ( set ) { QVariant before = QVariant::fromValue(bez); QVariant after = extend_impl(bez, target, at_end); object()->push_command(new command::SetMultipleAnimated( "", {this}, {before}, {after}, true, time(), object()->document()->record_to_keyframe() )); } } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/avd/avd_renderer.cpp000664 001750 001750 00000050531 15165022620 036274 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/avd/avd_renderer.hpp" #include "glaxnimate/io/svg/detail.hpp" #include "glaxnimate/io/svg/svg_renderer.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/modifiers/trim.hpp" #include "glaxnimate/model/shapes/style/fill.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" #include #include class glaxnimate::io::avd::AvdRenderer::Private { public: using PropRet = std::vector>; struct Keyframe { QString value; // TODO interpolators }; class AnimationHelper { public: Private* parent = nullptr; QString name; std::map> keyframes = {}; bool animated() const { return !keyframes.empty(); } static QString attr(const QString& name) { return "android:" + name; } qreal frame_to_ms(model::FrameTime f) const { return f * 1000 / parent->fps; } template void render_properties( QDomElement& element, std::vector properties, const Callback& callback ) { model::JoinAnimatables j(std::move(properties), model::JoinAnimatables::Normal); auto xml_values = callback(j.current_value()); for ( const auto& p : xml_values ) element.setAttribute(attr(p.first), p.second); if ( j.animated() ) { for ( const auto& kf : j ) { xml_values = callback(kf.values); for ( const auto& p : xml_values ) { keyframes[p.first][frame_to_ms(kf.time)] = Keyframe{p.second}; } } } } /// \todo Option for propertyValuesHolder+keyframe? QDomElement render_object_animators() const { QDomElement target = parent->dom.createElement("target"); target.setAttribute("android:name", name); QDomElement attr = parent->dom.createElement("aapt:attr"); target.appendChild(attr); attr.setAttribute("name", "android:animation"); QDomElement set = parent->dom.createElement("set"); attr.appendChild(set); for ( const auto& prop : keyframes ) { QString type; if ( prop.first == "pathData" ) type = "pathType"; else if ( prop.first.contains("Color") ) type = "colorType"; else type = "floatType"; auto iter = prop.second.begin(); while ( iter != prop.second.end() ) { auto start = iter->first; QDomElement anim = parent->dom.createElement("objectAnimator"); anim.setAttribute("android:propertyName", prop.first); anim.setAttribute("android:valueType", type); anim.setAttribute("android:startOffset", QString::number(start)); anim.setAttribute("android:valueFrom", iter->second.value); ++iter; if ( iter == prop.second.end() ) break; anim.setAttribute("android:valueTo", iter->second.value); anim.setAttribute("android:duration", QString::number(iter->first - start)); set.appendChild(anim); } } return target; } }; void render(model::Composition* comp) { fps = comp->fps.get(); vector = dom.createElement("vector"); vector.setAttribute("android:width", QString("%1dp").arg(comp->width.get())); vector.setAttribute("android:height", QString("%1dp").arg(comp->height.get())); vector.setAttribute("android:viewportWidth", QString::number(comp->width.get())); vector.setAttribute("android:viewportHeight", QString::number(comp->height.get())); render_comp(comp, vector); } void render_comp(model::Composition* comp, QDomElement& parent) { parent.setAttribute("android:name", unique_name(comp, false)); for ( const auto& layer : comp->shapes ) render_element(layer.get(), parent); } void render_element(model::ShapeElement* elm, QDomElement& parent) { if ( auto l = elm->cast() ) { render_layer(l, parent); } else if ( auto g = elm->cast() ) { render_group(g, parent); } else if ( elm->is_instance() ) { warning(i18n("%s should be in a group", elm->object_name())); } else if ( !elm->is_instance() && !elm->is_instance() ) { warning(i18n("%s is not supported", elm->type_name_human())); } } void warning(const QString& s) { if ( on_warning ) on_warning(s); } QDomElement render_layer_parents(model::Layer* lay, QDomElement& parent) { if ( auto parlay = lay->parent.get() ) { auto p = render_layer_parents(parlay, parent); QDomElement group = dom.createElement("group"); p.appendChild(group); render_transform(parlay->transform.get(), group, unique_name(parlay, true) ); return p; } return parent; } void render_layer(model::Layer* lay, QDomElement& parent) { auto parent_element = parent; QDomElement p = render_layer_parents(lay, parent); auto elm = render_group(lay, p); if ( lay->mask->mask.get() != model::MaskSettings::NoMask ) { auto mask = render_clip_path(lay->shapes[0]); elm.insertBefore(mask, {}); } } QString unique_name(model::DocumentNode* node, bool is_duplicate) { QString base = node->name.get(); if ( base.isEmpty() ) base = "item_" + node->uuid.get().toString(QUuid::Id128); QString name = base; if ( is_duplicate ) name += "_" + QString::number(unique_id++); while ( names.count(name) ) { name = base + "_" + QString::number(unique_id++); } names.insert(name); return name; } QDomElement render_group(model::Group* group, QDomElement& parent) { QDomElement elm = dom.createElement("group"); parent.appendChild(elm); render_transform(group->transform.get(), elm, unique_name(group, false)); model::Fill* fill = nullptr; model::Stroke* stroke = nullptr; model::Trim* trim = nullptr; std::vector> children; std::vector shapes; std::vector groups; for ( const auto& ch : group->shapes ) { if ( auto f = ch->cast() ) fill = f; else if ( auto s = ch->cast() ) stroke = s; else if ( auto t = ch->cast() ) trim = t; else if ( auto g = ch->cast() ) { groups.push_back(g); children.push_back(g); } else if ( auto s = ch->cast() ) { shapes.push_back(s); children.push_back(s); } else { warning(i18n("%s are not supported", ch->type_name_human())); } } bool unify_shapes = !groups.empty(); if ( !shapes.empty() ) unify_shapes = true; else if ( trim ) unify_shapes = trim->multiple.get() == model::Trim::Simultaneously; if ( unify_shapes ) { for ( const auto& g : groups ) render_group(g, elm); if ( !shapes.empty() ) { QString name = shapes.size() == 1 ? unique_name(shapes[0], false) : unique_name(group, true); render_shapes(shapes, name, elm, fill, stroke, trim); } } else { for ( const auto& ch : children ) { if ( ch.index() == 0 ) render_shapes({std::get<0>(ch)}, unique_name(std::get<0>(ch), false), elm, fill, stroke, trim); else render_group(std::get<1>(ch), elm); } } return elm; } void render_shapes( const std::vector& shapes, const QString& name, QDomElement& parent, model::Fill* fill, model::Stroke* stroke, model::Trim* trim ) { if ( shapes.empty() ) return; auto path = dom.createElement("path"); parent.appendChild(path); path.setAttribute("android:name", name); render_shapes_to_path_data(shapes, name, path); render_fill(fill, name, path); render_stroke(stroke, name, path); render_trim(trim, name, path); } void render_shapes_to_path_data(const std::vector& shapes, const QString& name, QDomElement& elem) { std::vector> saved; std::vector paths; paths.reserve(shapes.size()); for ( const auto& sh : shapes ) { if ( auto p = sh->cast() ) { paths.push_back(&p->shape); } else { auto conv = sh->to_path(); collect_paths(conv.get(), paths); saved.push_back(std::move(conv)); } } auto& anim = animator(name); anim.render_properties(elem, paths, [](const std::vector& v) -> PropRet { return { {"pathData", paths_to_path_data(v)}, }; }); } void collect_paths(model::ShapeElement* element, std::vector& paths) { if ( auto p = element->cast() ) { paths.push_back(&p->shape); } else if ( auto g = element->cast() ) { for ( const auto& c : g->shapes ) collect_paths(c.get(), paths); } } static QString paths_to_path_data(const std::vector& paths) { math::bezier::MultiBezier bez; for ( const auto& path : paths ) bez.beziers().push_back(path.value()); return svg::path_data(bez).first; } void render_fill(model::Fill* fill, const QString& name, QDomElement& element) { if ( !fill ) return; render_styler_color(fill, name, "fillColor", element); auto& anim = animator(name); anim.render_properties(element, {&fill->opacity}, [](const std::vector& v) -> PropRet { return { {"fillAlpha", QString::number(v[0].toDouble())}, }; }); element.setAttribute("android:fillType", fill->fill_rule.get() == model::Fill::EvenOdd ? "evenOdd" : "nonZero"); } void render_stroke(model::Stroke* stroke, const QString& name, QDomElement& element) { if ( !stroke ) return; render_styler_color(stroke, name, "strokeColor", element); auto& anim = animator(name); anim.render_properties(element, {&stroke->opacity}, [](const std::vector& v) -> PropRet { return { {"strokeAlpha", QString::number(v[0].toDouble())}, }; }); anim.render_properties(element, {&stroke->width}, [](const std::vector& v) -> PropRet { return { {"strokeWidth", QString::number(v[0].toDouble())}, }; }); element.setAttribute("android:strokeWidth", QString::number(stroke->width.get())); element.setAttribute("android:strokeMiterLimit", QString::number(stroke->miter_limit.get())); switch ( stroke->cap.get() ) { case model::Stroke::RoundCap: element.setAttribute("android:strokeLineCap", "round"); break; case model::Stroke::ButtCap: element.setAttribute("android:strokeLineCap", "butt"); break; case model::Stroke::SquareCap: element.setAttribute("android:strokeLineCap", "square"); break; } switch ( stroke->join.get() ) { case model::Stroke::RoundJoin: element.setAttribute("android:strokeLineJoin", "round"); break; case model::Stroke::MiterJoin: element.setAttribute("android:strokeLineJoin", "miter"); break; case model::Stroke::BevelJoin: element.setAttribute("android:strokeLineJoin", "bevel"); break; } } void render_styler_color(model::Styler* styler, const QString& name, const QString& attr, QDomElement& element) { auto use = styler->use.get(); if ( auto color = use->cast() ) { auto& anim = animator(name); anim.render_properties(element, {&color->color}, [&attr](const std::vector& v) -> PropRet { return { {attr, render_color(v[0].value())}, };}); } else if ( auto gradient = use->cast() ) { render_gradient(attr, gradient, element); } else { auto& anim = animator(name); anim.render_properties(element, {&styler->color}, [&attr](const std::vector& v) -> PropRet { return { {attr, render_color(v[0].value())}, };}); } } void render_gradient(const QString& attr_name, model::Gradient* gradient, QDomElement& element) { auto attr = dom.createElement("aapt:attr"); attr.setAttribute("name", "android:" + attr_name); element.appendChild(attr); auto gradel = dom.createElement("gradient"); attr.appendChild(gradel); switch ( gradient->type.get() ) { case model::Gradient::Linear: gradel.setAttribute("android:type", "linear"); break; case model::Gradient::Radial: gradel.setAttribute("android:type", "radial"); break; case model::Gradient::Conical: gradel.setAttribute("android:type", "sweep"); break; } gradel.setAttribute("startX", gradient->start_point.get().x()); gradel.setAttribute("startY", gradient->start_point.get().y()); gradel.setAttribute("endX", gradient->end_point.get().x()); gradel.setAttribute("endY", gradient->end_point.get().y()); if ( auto cols = gradient->colors.get() ) { for ( const auto& stop : cols->colors.get() ) { auto item = dom.createElement("item"); item.setAttribute("android:color", render_color(stop.second)); item.setAttribute("android:offset", QString::number(stop.first)); } } } void render_trim(model::Trim* trim, const QString& name, QDomElement& element) { if ( !trim ) return; auto& anim = animator(name); anim.render_properties(element, {&trim->start}, [](const std::vector& v) -> PropRet { return { {"trimPathStart", QString::number(v[0].toDouble())}, };}); anim.render_properties(element, {&trim->end}, [](const std::vector& v) -> PropRet { return { {"trimPathEnd", QString::number(v[0].toDouble())}, };}); anim.render_properties(element, {&trim->offset}, [](const std::vector& v) -> PropRet { return { {"trimPathOffset", QString::number(v[0].toDouble())}, };}); } QDomElement render_clip_path(model::ShapeElement* element) { QDomElement clip = dom.createElement("clip-path"); QString name = unique_name(element, false); clip.setAttribute("android:name", name); if ( auto group = element->cast() ) { std::vector shapes = group->docnode_find_by_type(); render_shapes_to_path_data(shapes, name, clip); } else if ( auto shape = element->cast() ) { render_shapes_to_path_data({shape}, name, clip); } else { warning(i18n("%s cannot be a clip path", element->object_name())); return {}; } return clip; } static QString color_comp(int comp) { return QString::number(comp, 16).rightJustified(2, '0'); } static QString render_color(const QColor& color) { return "#" + color_comp(color.alpha()) + color_comp(color.red()) + color_comp(color.green()) + color_comp(color.blue()); } void render_transform(model::Transform* trans, QDomElement& elm, const QString& name) { auto& anim = animator(name); anim.render_properties(elm, {&trans->anchor_point, &trans->position}, [](const std::vector& v) -> PropRet { auto ap = v[0].toPointF(); auto pos = v[1].toPointF() - ap; return { {"pivotX", QString::number(ap.x())}, {"pivotY", QString::number(ap.y())}, {"translateX", QString::number(pos.x())}, {"translateY", QString::number(pos.y())}, }; }); anim.render_properties(elm, {&trans->scale}, [](const std::vector& v) -> PropRet { auto scale = v[0].value(); return { {"scaleX", QString::number(scale.x())}, {"scaleY", QString::number(scale.y())}, }; }); anim.render_properties(elm, {&trans->rotation}, [](const std::vector& v) -> PropRet { return { {"rotation", QString::number(v[0].toDouble())}, }; }); } void render_anim(QDomElement& container) { for ( const auto& p : animations ) { if ( p.second.animated() ) container.appendChild(p.second.render_object_animators()); } } AnimationHelper& animator(const QString& name) { auto iter = animations.find(name); if ( iter == animations.end() ) iter = animations.insert({name, {this, name}}).first; return iter->second; } int fps = 60; int unique_id = 0; QDomDocument dom; QDomElement vector; std::map animations; std::function on_warning; std::unordered_set names; }; glaxnimate::io::avd::AvdRenderer::AvdRenderer(const std::function& on_warning) : d(std::make_unique()) { d->on_warning = on_warning; } glaxnimate::io::avd::AvdRenderer::~AvdRenderer() { } void glaxnimate::io::avd::AvdRenderer::render(model::Composition* comp) { d->render( comp); } QDomElement glaxnimate::io::avd::AvdRenderer::graphics() { return d->vector; } QDomDocument glaxnimate::io::avd::AvdRenderer::single_file() { QDomDocument dom; auto av = dom.createElement("animated-vector"); dom.appendChild(av); av.setAttribute("xmlns", svg::detail::xmlns.at("android")); for ( const auto& p : svg::detail::xmlns ) { if ( p.second.contains("android") ) av.setAttribute("xmlns:" + p.first, p.second); } auto attr = dom.createElement("aapt:attr"); av.appendChild(attr); attr.setAttribute("name", "android:drawable"); attr.appendChild(graphics()); d->render_anim(av); return dom; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/gzip-kde.cpp000664 001750 001750 00000004311 15165022620 033100 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include "glaxnimate/utils/i18n.hpp" #include "gzip.hpp" using namespace glaxnimate; bool gzip::decompress(QIODevice& input, QByteArray& output, const gzip::ErrorFunc& on_error) { KCompressionDevice compressed(&input, false, KCompressionDevice::GZip); compressed.open(QIODevice::ReadOnly); output = compressed.readAll(); if ( compressed.error() ) { on_error(i18n("Could not decompress data")); return false; } return true; } bool gzip::decompress(const QByteArray& input, QByteArray& output, const gzip::ErrorFunc& on_error) { QBuffer buf(const_cast(&input)); return decompress(buf, output, on_error); } bool gzip::compress(const QByteArray& data, QIODevice& output, const gzip::ErrorFunc& on_error, int, quint32* compressed_size) { KCompressionDevice compressed(&output, false, KCompressionDevice::GZip); compressed.open(QIODevice::WriteOnly); compressed.write(data); if ( compressed.error() ) { on_error(compressed.errorString()); return false; } compressed.close(); if ( compressed_size ) *compressed_size = output.pos(); return true; } class gzip::GzipStream::Private { public: Private(QIODevice* target, const ErrorFunc&) : device(target, false, KCompressionDevice::GZip) {} KCompressionDevice device; }; gzip::GzipStream::GzipStream(QIODevice* target, const gzip::ErrorFunc& on_error) : d(std::make_unique(target, on_error)) {} gzip::GzipStream::~GzipStream() {} bool gzip::GzipStream::open(QIODevice::OpenMode mode) { if ( d->device.openMode() != NotOpen ) { setErrorString(i18n("Gzip stream already open")); return false; } return d->device.open(mode); } bool gzip::GzipStream::atEnd() const { return d->device.atEnd(); } qint64 gzip::GzipStream::writeData(const char* data, qint64 len) { return d->device.write(data, len); } qint64 gzip::GzipStream::readData(char* data, qint64 maxlen) { return d->device.read(data, maxlen); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_html_format.hpp000664 001750 001750 00000001427 15165022620 037375 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" namespace glaxnimate::io::rive { class RiveHtmlFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "rive_html"; } QString name() const override { return i18n("RIVE HTML Preview"); } QStringList extensions(Direction) const override { return {"html", "htm"}; } bool can_save() const override { return true; } bool can_open() const override { return false; } private: bool on_save(QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& setting_values) override; }; } // namespace glaxnimate::io::rive mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shape.cpp000664 001750 001750 00000023317 15165022620 032622 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/shape.hpp" #include "glaxnimate/utils/range.hpp" #include "glaxnimate/model/shapes/style/styler.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" using namespace glaxnimate; class glaxnimate::model::ShapeElement::Private { public: ShapeListProperty* property = nullptr; int position = -1; glaxnimate::model::Composition* owner_composition = nullptr; PathCache cached_path; void update_comp(glaxnimate::model::Composition* comp, ShapeElement* parent) { if ( comp != owner_composition ) { auto old = owner_composition; owner_composition = comp; parent->on_composition_changed(old, comp); } } }; glaxnimate::model::ShapeElement::ShapeElement(glaxnimate::model::Document* document) : VisualNode(document), d(std::make_unique()) { } glaxnimate::model::ShapeElement::~ShapeElement() = default; glaxnimate::model::ShapeListProperty * glaxnimate::model::ShapeElement::owner() const { return d->property; } void glaxnimate::model::ShapeElement::clear_owner() { d->property = nullptr; d->position = -1; d->owner_composition = nullptr; } glaxnimate::model::Composition * glaxnimate::model::ShapeElement::owner_composition() const { return d->owner_composition; } int glaxnimate::model::ShapeElement::position() const { return d->position; } const glaxnimate::model::ShapeListProperty& glaxnimate::model::ShapeElement::siblings() const { return *d->property; } glaxnimate::model::ObjectListProperty::iterator glaxnimate::model::ShapeListProperty::past_first_modifier() const { auto it = std::find_if(begin(), end(), [](const pointer& p){ return qobject_cast(p.get()); }); if ( it != end() ) ++it; return it; } void glaxnimate::model::ShapeElement::refresh_owner_composition(glaxnimate::model::Composition* comp) { d->update_comp(comp, this); } void glaxnimate::model::ShapeElement::set_position(ShapeListProperty* property, int pos) { d->property = property; d->position = pos; position_updated(); if ( property ) { auto parent = d->property->object(); if ( !parent ) d->update_comp(nullptr, this); else if ( auto comp = parent->cast() ) d->update_comp(comp, this); else if ( auto sh = parent->cast() ) d->update_comp(sh->d->owner_composition, this); } } void glaxnimate::model::ShapeElement::on_parent_changed(model::DocumentNode* old_parent, model::DocumentNode* new_parent) { if ( auto old_visual = qobject_cast(old_parent) ) disconnect(this, &VisualNode::bounding_rect_changed, old_visual, &VisualNode::bounding_rect_changed); if ( auto new_visual = qobject_cast(new_parent) ) connect(this, &VisualNode::bounding_rect_changed, new_visual, &VisualNode::bounding_rect_changed); if ( !new_parent ) d->update_comp(nullptr, this); } void glaxnimate::model::ShapeElement::on_property_changed(const glaxnimate::model::BaseProperty* prop, const QVariant&) { if ( prop->traits().flags & PropertyTraits::Visual ) propagate_bounding_rect_changed(); } math::bezier::MultiBezier glaxnimate::model::ShapeElement::shapes(glaxnimate::model::FrameTime t) const { math::bezier::MultiBezier bez; add_shapes(t, bez, {}); return bez; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::ShapeElement::to_clip(FrameTime t) const { return to_painter_path(t); } glaxnimate::math::bezier::MultiBezier glaxnimate::model::ShapeElement::to_painter_path(FrameTime t) const { if ( d->cached_path.is_dirty(t) ) d->cached_path.set_path(t, to_painter_path_impl(t)); return d->cached_path.path(); } void glaxnimate::model::ShapeElement::on_graphics_changed() { d->cached_path.mark_dirty(); } std::unique_ptr glaxnimate::model::ShapeElement::to_path() const { return std::unique_ptr(static_cast(clone().release())); } QRectF glaxnimate::model::ShapeListProperty::bounding_rect(FrameTime t) const { QRectF rect; for ( const auto& ch : utils::Range(begin(), past_first_modifier()) ) { QRectF local_rect = ch->local_bounding_rect(t); if ( local_rect.isNull() ) continue; QRectF child_rect = ch->local_transform_matrix(t).map(local_rect).boundingRect(); if ( rect.isNull() ) rect = child_rect; else rect |= child_rect; } return rect; } std::unique_ptr glaxnimate::model::Shape::to_path() const { std::vector properties; auto flags = PropertyTraits::Visual|PropertyTraits::Animated; for ( auto prop : this->properties() ) { if ( (prop->traits().flags & flags) == flags ) properties.push_back(static_cast(prop)); } auto path = std::make_unique(document()); path->name.set(name.get()); path->group_color.set(group_color.get()); path->visible.set(visible.get()); if ( !properties.empty() ) { JoinAnimatables ja(std::move(properties)); FrameTime cur_time = time(); path->set_time(cur_time); if ( ja.animated() ) { for ( const auto & kf : ja ) { auto path_kf = path->shape.set_keyframe(kf.time, to_bezier(kf.time)); path_kf->set_transition(kf.transition()); } } path->shape.set(to_bezier(cur_time)); path->closed.set(path->shape.get().closed()); } return path; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Shape::to_painter_path_impl(FrameTime t) const { glaxnimate::math::bezier::MultiBezier p; p.append(to_bezier(t)); return p; } void glaxnimate::model::Shape::add_shapes(FrameTime t, math::bezier::MultiBezier & bez, const QTransform& transform) const { auto shape = to_bezier(t); if ( !transform.isIdentity() ) shape.transform(transform); bez.beziers().emplace_back(std::move(shape)); } glaxnimate::model::ShapeOperator::ShapeOperator(glaxnimate::model::Document* doc) : ShapeElement(doc) { connect(this, &ShapeElement::position_updated, this, &ShapeOperator::update_affected); connect(this, &ShapeElement::siblings_changed, this, &ShapeOperator::update_affected); } void glaxnimate::model::ShapeOperator::do_collect_shapes(const std::vector& shapes, glaxnimate::model::FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const { for ( auto sib : shapes ) { if ( sib->visible.get() ) sib->add_shapes(t, bez, transform); } } math::bezier::MultiBezier glaxnimate::model::ShapeOperator::collect_shapes_from(const std::vector& shapes, glaxnimate::model::FrameTime t, const QTransform& transform) const { math::bezier::MultiBezier bez; if ( visible.get() ) do_collect_shapes(shapes, t, bez, transform); return bez; } math::bezier::MultiBezier glaxnimate::model::ShapeOperator::collect_shapes(FrameTime t, const QTransform& transform) const { if ( bezier_cache.is_dirty(t) ) bezier_cache.set_path(t, collect_shapes_from(affected_elements, t, transform)); return bezier_cache.path(); } void glaxnimate::model::ShapeOperator::update_affected() { if ( !owner() ) return; std::vector curr_siblings; curr_siblings.reserve(owner()->size() - position()); bool skip = skip_stylers(); for ( auto it = owner()->begin() + position() + 1; it < owner()->end(); ++it ) { if ( skip && qobject_cast(it->get()) ) continue; curr_siblings.push_back(it->get()); if ( qobject_cast(it->get()) ) break; } affected_elements = curr_siblings; std::reverse(affected_elements.begin(), affected_elements.end()); } void glaxnimate::model::ShapeOperator::on_graphics_changed() { ShapeElement::on_graphics_changed(); bezier_cache.mark_dirty(); Q_EMIT shape_changed(); } void glaxnimate::model::Modifier::add_shapes(FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const { bez.append(collect_shapes(t, transform)); } void glaxnimate::model::Modifier::do_collect_shapes(const std::vector& shapes, glaxnimate::model::FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const { bool post = process_collected(); if ( post ) { math::bezier::MultiBezier temp; for ( auto sib : shapes ) { if ( sib->visible.get() ) sib->add_shapes(t, temp, transform); } bez.append(process(t, temp)); } else { for ( auto sib : shapes ) { if ( sib->visible.get() ) { math::bezier::MultiBezier temp; sib->add_shapes(t, temp, transform); bez.append(process(t, temp)); } } } } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Modifier::to_painter_path_impl(glaxnimate::model::FrameTime t) const { math::bezier::MultiBezier bez; add_shapes(t, bez, {}); return bez; } QRectF glaxnimate::model::Modifier::local_bounding_rect(glaxnimate::model::FrameTime t) const { return to_painter_path(t).bounding_box(); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/mime/000775 001750 001750 00000000000 15165022620 027763 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/ellipse_solver.cpp000664 001750 001750 00000010441 15165022620 033111 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/ellipse_solver.hpp" #include "glaxnimate/math/bezier/bezier.hpp" #include "glaxnimate/math/vector.hpp" #include "glaxnimate/math/math.hpp" using namespace glaxnimate; math::EllipseSolver::EllipseSolver(const QPointF& center, const QPointF& radii, qreal xrot) : center(center), radii(radii), xrot(xrot) {} QPointF math::EllipseSolver::point(qreal t) const { return QPointF( center.x() + radii.x() * qCos(xrot) * qCos(t) - radii.y() * qSin(xrot) * qSin(t), center.y() + radii.x() * qSin(xrot) * qCos(t) + radii.y() * qCos(xrot) * qSin(t) ); } QPointF math::EllipseSolver::derivative(qreal t) const { return QPointF( - radii.x() * qCos(xrot) * qSin(t) - radii.y() * qSin(xrot) * qCos(t), - radii.x() * qSin(xrot) * qSin(t) + radii.y() * qCos(xrot) * qCos(t) ); } math::bezier::Bezier math::EllipseSolver::to_bezier(qreal anglestart, qreal angle_delta) { bezier::Bezier points; qreal angle1 = anglestart; qreal angle_left = qAbs(angle_delta); qreal step = math::pi / 2; qreal sign = anglestart+angle_delta < angle1 ? -1 : 1; // We need to fix the first handle qreal firststep = qMin(angle_left, step) * sign; qreal alpha = _alpha(firststep); QPointF q1 = derivative(angle1) * alpha; points.push_back(bezier::Point::from_relative(point(angle1), QPointF(0, 0), q1, math::bezier::Symmetrical)); // Then we iterate until the angle has been completed qreal tolerance = step / 2; do { qreal lstep = qMin(angle_left, step); qreal step_sign = lstep * sign; qreal angle2 = angle1 + step_sign; angle_left -= abs(lstep); alpha = _alpha(step_sign); QPointF p2 = point(angle2); QPointF q2 = derivative(angle2) * alpha; points.push_back(bezier::Point::from_relative(p2, -q2, q2, math::bezier::Symmetrical)); angle1 = angle2; } while ( angle_left > tolerance ); if ( points.size() > 1 && qFuzzyCompare(angle_delta, math::tau) ) { points.close(); points[0].tan_in = points.back().tan_in; points.points().pop_back(); } return points; } math::bezier::Bezier math::EllipseSolver::from_svg_arc( QPointF start, qreal rx, qreal ry, qreal xrot, bool large, bool sweep, QPointF dest ) { rx = qAbs(rx); ry = qAbs(ry); qreal x1 = start.x(); qreal y1 = start.y(); qreal x2 = dest.x(); qreal y2 = dest.y(); qreal phi = pi * xrot / 180; QPointF p1 = _matrix_mul(phi, (start-dest)/2, -1); qreal x1p = p1.x(); qreal y1p = p1.y(); qreal cr = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry); if ( cr > 1 ) { qreal s = qSqrt(cr); rx *= s; ry *= s; } qreal dq = rx * rx * y1p * y1p + ry * ry * x1p * x1p; qreal pq = (rx * rx * ry * ry - dq) / dq; qreal cpm = qSqrt(qMax(0., pq)); if ( large == sweep ) cpm = -cpm; QPointF cp(cpm * rx * y1p / ry, -cpm * ry * x1p / rx); QPointF c = _matrix_mul(phi, cp) + QPointF((x1+x2)/2, (y1+y2)/2); qreal theta1 = _angle(QPointF(1, 0), QPointF((x1p - cp.x()) / rx, (y1p - cp.y()) / ry)); qreal deltatheta = std::fmod(_angle( QPointF((x1p - cp.x()) / rx, (y1p - cp.y()) / ry), QPointF((-x1p - cp.x()) / rx, (-y1p - cp.y()) / ry) ), 2*pi); if ( !sweep && deltatheta > 0 ) deltatheta -= 2*pi; else if ( sweep && deltatheta < 0 ) deltatheta += 2*pi; return EllipseSolver(c, QPointF(rx, ry), phi).to_bezier(theta1, deltatheta); } qreal math::EllipseSolver::_alpha(qreal step) { return qSin(step) * (qSqrt(4+3*qPow(qTan(step/2), 2)) - 1) / 3; } QPointF math::EllipseSolver::_matrix_mul(qreal phi, const QPointF p, qreal sin_mul) { qreal c = qCos(phi); qreal s = qSin(phi) * sin_mul; qreal xr = c * p.x() - s * p.y(); qreal yr = s * p.x() + c * p.y(); return QPointF(xr, yr); } qreal math::EllipseSolver::_angle(const QPointF& u, const QPointF& v) { qreal arg = qAcos(qMax(-1., qMin(1., QPointF::dotProduct(u, v) / (length(u) * length(v))))); if ( u.x() * v.y() - u.y() * v.x() < 0 ) return -arg; return arg; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/comp_graph.cpp000664 001750 001750 00000011050 15165022620 032345 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/comp_graph.hpp" #include #include #include #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" void glaxnimate::model::CompGraph::add_composition(glaxnimate::model::Composition* comp) { std::vector& comp_layers = layers[comp]; std::deque nodes(comp->docnode_children().begin(), comp->docnode_children().end()); while ( !nodes.empty() ) { auto front = nodes.front(); nodes.pop_front(); if ( auto layer = front->cast() ) comp_layers.push_back(layer); else nodes.insert(nodes.end(), front->docnode_children().begin(), front->docnode_children().end()); } } void glaxnimate::model::CompGraph::remove_composition(glaxnimate::model::Composition* comp) { layers.erase(comp); } bool glaxnimate::model::CompGraph::is_ancestor_of(glaxnimate::model::Composition* ancestor, glaxnimate::model::Composition* descendant) const { std::unordered_set checked; std::unordered_set not_checked; not_checked.insert(ancestor); while ( !not_checked.empty() ) { std::unordered_set next; for ( glaxnimate::model::Composition* comp : not_checked ) { if ( comp == descendant ) return true; auto it = layers.find(comp); if ( it == layers.end() ) continue; for ( auto layer : layers.at(comp) ) { auto laycomp = layer->composition.get(); if ( laycomp && !checked.count(laycomp) ) next.insert(laycomp); } checked.insert(comp); } not_checked = std::move(next); } return false; } std::vector glaxnimate::model::CompGraph::children(glaxnimate::model::Composition* comp) const { std::unordered_set vals; for ( auto layer : layers.at(comp) ) { if ( auto laycomp = layer->composition.get() ) vals.insert(laycomp); } return std::vector(vals.begin(), vals.end()); } static bool recursive_is_ancestor_of( glaxnimate::model::Composition* ancestor, glaxnimate::model::Composition* descendant, std::unordered_map& cache, const std::unordered_map>& layers ) { if ( ancestor == descendant ) return cache[ancestor] = true; auto it = cache.find(ancestor); if ( it != cache.end() ) return it->second; int is_ancestor = 0; for ( auto layer : layers.at(ancestor) ) { if ( auto laycomp = layer->composition.get() ) is_ancestor += recursive_is_ancestor_of(laycomp, descendant, cache, layers); } return cache[ancestor] = is_ancestor; } std::vector glaxnimate::model::CompGraph::possible_descendants(glaxnimate::model::Composition* ancestor, glaxnimate::model::Document* document) const { std::unordered_map cache; std::vector valid; for ( const auto& precomp : document->assets()->compositions->values ) { if ( !recursive_is_ancestor_of(precomp.get(), ancestor, cache, layers) ) valid.push_back(precomp.get()); } return valid; } void glaxnimate::model::CompGraph::add_connection(glaxnimate::model::Composition* comp, glaxnimate::model::PreCompLayer* layer) { auto it = layers.find(comp); if ( it != layers.end() ) it->second.push_back(layer); } void glaxnimate::model::CompGraph::remove_connection(glaxnimate::model::Composition* comp, glaxnimate::model::PreCompLayer* layer) { auto it_map = layers.find(comp); if ( it_map != layers.end() ) { auto it_v = std::find(it_map->second.begin(), it_map->second.end(), layer); if ( it_v != it_map->second.end() ) { if ( it_v != it_map->second.end() - 1 ) std::swap(*it_v, it_map->second.back()); it_map->second.pop_back(); } } } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/structure_commands.hpp000664 001750 001750 00000002111 15165022620 034470 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/document_node.hpp" #include "glaxnimate/model/shapes/shape.hpp" namespace glaxnimate::command { class DeferredCommandBase : public QUndoCommand { public: ~DeferredCommandBase(); void undo() override; void redo() override; bool has_action() const; protected: DeferredCommandBase(const QString& name, std::unique_ptr d = {}) : QUndoCommand(name), d(std::move(d)) {} std::unique_ptr d; }; class ReorderCommand : public DeferredCommandBase { public: enum SpecialPosition { MoveUp = -1, MoveDown = -2, MoveTop = -3, MoveBottom = -4, }; static bool resolve_position(model::ShapeElement* node, int& position); ReorderCommand(model::ShapeElement* node, int new_position); private: static QString name(model::DocumentNode* node); }; } // namespace glaxnimate::command mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/lottie_format.cpp000664 001750 001750 00000004314 15165022620 033712 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/lottie/lottie_format.hpp" #include "glaxnimate/io/lottie/lottie_importer.hpp" #include "glaxnimate/io/lottie/lottie_exporter.hpp" bool glaxnimate::io::lottie::LottieFormat::on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap& setting_values) { file.write(cbor_write_json(to_json(comp, setting_values["strip"].toBool(), false, setting_values), !setting_values["pretty"].toBool())); return true; } QCborMap glaxnimate::io::lottie::LottieFormat::to_json(model::Composition* comp, bool strip, bool strip_raster, const QVariantMap& settings) { detail::LottieExporterState exp(this, comp, strip, strip_raster, settings); return exp.to_json(); } bool glaxnimate::io::lottie::LottieFormat::load_json(const QByteArray& data, model::Document* document) { QJsonDocument jdoc; try { jdoc = QJsonDocument::fromJson(data); } catch ( const QJsonParseError& err ) { Q_EMIT error(i18n("Could not parse JSON: %1", err.errorString())); return false; } if ( !jdoc.isObject() ) { Q_EMIT error(i18n("No JSON object found")); return false; } QJsonObject top_level = jdoc.object(); detail::LottieImporterState imp{document, this}; imp.load(top_level); return true; } bool glaxnimate::io::lottie::LottieFormat::on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&) { return load_json(file.readAll(), document); } std::unique_ptr glaxnimate::io::lottie::LottieFormat::save_settings(model::Composition*) const { return std::make_unique(glaxnimate::settings::SettingList{ glaxnimate::settings::Setting("pretty", i18n("Pretty"), i18n("Pretty print the JSON"), false), glaxnimate::settings::Setting("strip", i18n("Strip"), i18n("Strip unused properties"), false), glaxnimate::settings::Setting("auto_embed", i18n("Embed Images"), i18n("Automatically embed non-embedded images"), false), }); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/polynomial.cpp000664 001750 001750 00000004605 15165022620 032252 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/polynomial.hpp" #include "glaxnimate/math/math.hpp" namespace math = glaxnimate::math; // Returns the real cube root of a value static double cuberoot(double v) { if ( v < 0 ) return -math::pow(-v, 1./3); return math::pow(v, 1./3); } std::vector glaxnimate::math::cubic_roots(double a, double b, double c, double d) { // If a is 0, it's a quadratic if ( qFuzzyIsNull(a) ) return quadratic_roots(b, c, d); // Cardano's algorithm. b /= a; c /= a; d /= a; double p = (3*c - b * b) / 3; double p3 = p / 3; double q = (2 * b*b*b - 9 * b * c + 27 * d) / 27; double q2 = q / 2; double discriminant = q2 * q2 + p3 * p3 * p3; // and some variables we're going to use later on: // 3 real roots: if ( discriminant < 0) { double mp3 = -p / 3; double r = math::sqrt(mp3*mp3*mp3); double t = -q / (2*r); double cosphi = t < -1 ? -1 : t > 1 ? 1 : t; double phi = math::acos(cosphi); double crtr = cuberoot(r); double t1 = 2 * crtr; double root1 = t1 * math::cos(phi / 3) - b / 3; double root2 = t1 * math::cos((phi + 2 * math::pi) / 3) - b / 3; double root3 = t1 * math::cos((phi + 4 * math::pi) / 3) - b / 3; return {root1, root2, root3}; } // 2 real roots if ( qFuzzyIsNull(discriminant) ) { double u1 = q2 < 0 ? cuberoot(-q2) : -cuberoot(q2); double root1 = 2*u1 - b / 3; double root2 = -u1 - b / 3; return {root1, root2}; } // 1 real root, 2 complex roots double sd = math::sqrt(discriminant); double u1 = cuberoot(sd - q2); double v1 = cuberoot(sd + q2); return {u1 - v1 - b / 3}; } std::vector glaxnimate::math::quadratic_roots(double a, double b, double c) { // linear if ( qFuzzyIsNull(a) ) { if ( qFuzzyIsNull(b) ) return {}; return {-c / b}; } double s = b * b - 4 * a * c; // Complex roots if ( s < 0 ) return {}; double single_root = -b / (2 * a); // 1 root if ( qFuzzyIsNull(s) ) return {single_root}; double delta = math::sqrt(s) / (2 * a); // 2 roots return {single_root - delta, single_root + delta}; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/path.hpp000664 001750 001750 00000002443 15165022620 033743 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #pragma once #include "glaxnimate/model/shapes/shape.hpp" #include "glaxnimate/model/animation/animatable_path.hpp" namespace glaxnimate::model { class Path : public Shape { GLAXNIMATE_OBJECT(Path) using PointType = math::bezier::PointType; Q_ENUM(PointType) public: GLAXNIMATE_ANIMATABLE(math::bezier::Bezier, shape, &Path::shape_changed) GLAXNIMATE_PROPERTY(bool, closed, false, &Path::closed_changed) public: using Shape::Shape; QIcon tree_icon() const override { return QIcon::fromTheme("draw-bezier-curves"); } QString type_name_human() const override { return i18n("Path"); } math::bezier::Bezier to_bezier(FrameTime t) const override { auto bezier = shape.get_at(t); if ( reversed.get() ) bezier.reverse(); return bezier; } QRectF local_bounding_rect(FrameTime t) const override { return shape.get_at(t).bounding_box(); } private: void closed_changed(bool closed) { shape.set_closed(closed); } Q_SIGNALS: void shape_changed(const math::bezier::Bezier& bez); }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/shape_commands.hpp000664 001750 001750 00000005432 15165022620 033541 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/model/shapes/shape.hpp" #include "glaxnimate/command/object_list_commands.hpp" namespace glaxnimate::model { class Group; class PreCompLayer; } namespace glaxnimate::command { using AddShape = AddObject; using RemoveShape = RemoveObject; using MoveShape = MoveObject; namespace detail { class RedoInCtor : public QUndoCommand { public: void undo() override; void redo() override; protected: using QUndoCommand::QUndoCommand; private: bool did = true; }; } // namespace detail class GroupShapes : public detail::RedoInCtor { public: struct Data { std::vector elements; model::ShapeListProperty* parent = nullptr; }; GroupShapes(const Data& data); static Data collect_shapes(const std::vector& selection); private: model::Group* group = nullptr; }; class UngroupShapes : public detail::RedoInCtor { public: UngroupShapes(model::Group* group); }; AddShape* duplicate_shape(model::ShapeElement* shape); /** * \brief Converts \p shapes to path * \param out if not null, it will be populated with the new shapes * \returns The converted shapes */ void convert_to_path(const std::vector& shapes, std::vector* out = nullptr); /** * \brief Recursively traverses the node and reverses the path direction of any shape as a single command */ void recursive_reverse_path(model::DocumentNode* node); /** * \brief Precomposes the given objects into a new composition * \param source_comp Original composition containing the objects * \param objects Objects to move to the new composition * \param layer_parent Property to add the precomp layer to * \param layer_index Where in the property to insert the precomp layer to (-1 for default position) * \returns The created precomp layer * \pre All objects belong to \p source_comp * \pre layer_parent->object() not in \p objects * \post The returned layer is either null or it references the new composition */ model::PreCompLayer* precompose( model::Composition* source_comp, const std::vector& objects, model::ObjectListProperty* layer_parent, int layer_index ); /** * @brief Changes the end time of a composition or layer and adjusted affected layers */ void trim_end_time(model::VisualNode* node, model::FrameTime end_time); void trim_start_time(model::VisualNode* node, model::FrameTime start_time); } // namespace glaxnimate::command mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/thorvg/thorvg_module.cpp000664 001750 001750 00000001377 15165022620 034615 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "thorvg_module.hpp" #include "thorvg_renderer.hpp" using namespace glaxnimate::thorvg; std::vector glaxnimate::thorvg::Module::components() const { return { {QStringLiteral("thorvg"), {}, QStringLiteral("%1.%2.%3") .arg(TVG_VERSION_MAJOR) .arg(TVG_VERSION_MINOR) .arg(TVG_VERSION_MICRO), {}, "MIT"} }; } void glaxnimate::thorvg::Module::initialize() { renderer::RendererRegistry::instance().register_factory(QStringLiteral("ThorVG"), [](int q){ return std::make_unique(q); }, true); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/type_ids.hpp000664 001750 001750 00000006521 15165022620 035654 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once /** * NOTE: This file is generated automatically, do not edit manually * To generate this file run * ./external/rive_typedef.py -t ids >src/core/io/rive/type_ids.hpp */ namespace glaxnimate::io::rive { enum class TypeId { NoType = 0, Artboard = 1, Node = 2, Shape = 3, Ellipse = 4, StraightVertex = 5, CubicDetachedVertex = 6, Rectangle = 7, Triangle = 8, Component = 10, ContainerComponent = 11, Path = 12, Drawable = 13, PathVertex = 14, ParametricPath = 15, PointsPath = 16, RadialGradient = 17, SolidColor = 18, GradientStop = 19, Fill = 20, ShapePaint = 21, LinearGradient = 22, Backboard = 23, Stroke = 24, KeyedObject = 25, KeyedProperty = 26, Animation = 27, CubicInterpolator = 28, KeyFrame = 29, KeyFrameDouble = 30, LinearAnimation = 31, CubicAsymmetricVertex = 34, CubicMirroredVertex = 35, CubicVertex = 36, KeyFrameColor = 37, TransformComponent = 38, SkeletalComponent = 39, Bone = 40, RootBone = 41, ClippingShape = 42, Skin = 43, Tendon = 44, Weight = 45, CubicWeight = 46, TrimPath = 47, DrawTarget = 48, DrawRules = 49, KeyFrameId = 50, Polygon = 51, Star = 52, StateMachine = 53, StateMachineComponent = 54, StateMachineInput = 55, StateMachineNumber = 56, StateMachineLayer = 57, StateMachineTrigger = 58, StateMachineBool = 59, LayerState = 60, AnimationState = 61, AnyState = 62, EntryState = 63, ExitState = 64, StateTransition = 65, StateMachineLayerComponent = 66, TransitionCondition = 67, TransitionTriggerCondition = 68, TransitionValueCondition = 69, TransitionNumberCondition = 70, TransitionBoolCondition = 71, BlendState = 72, BlendStateDirect = 73, BlendAnimation = 74, BlendAnimation1D = 75, BlendState1D = 76, BlendAnimationDirect = 77, BlendStateTransition = 78, Constraint = 79, TargetedConstraint = 80, IKConstraint = 81, DistanceConstraint = 82, TransformConstraint = 83, KeyFrameBool = 84, TransformComponentConstraint = 85, TransformComponentConstraintY = 86, TranslationConstraint = 87, ScaleConstraint = 88, RotationConstraint = 89, TransformSpaceConstraint = 90, WorldTransformComponent = 91, NestedArtboard = 92, NestedAnimation = 93, NestedStateMachine = 95, NestedSimpleAnimation = 96, NestedLinearAnimation = 97, NestedRemapAnimation = 98, Asset = 99, Image = 100, Folder = 102, FileAsset = 103, DrawableAsset = 104, ImageAsset = 105, FileAssetContents = 106, Vertex = 107, MeshVertex = 108, Mesh = 109, Text = 110, ContourMeshVertex = 111, ForcedEdge = 112, TextRun = 113, StateMachineListener = 114, ListenerTriggerChange = 115, ListenerInputChange = 116, ListenerBoolChange = 117, ListenerNumberChange = 118, LayeredAsset = 119, LayerImageAsset = 120, NestedInput = 121, NestedTrigger = 122, NestedBool = 123, NestedNumber = 124, ListenerAction = 125, ListenerAlignTarget = 126, }; } // namespace glaxnimate::io::rive mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_renderer.cpp000664 001750 001750 00000144764 15165022620 033044 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/svg/svg_renderer.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/composable/layer.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include "glaxnimate/model/shapes/shapes/rect.hpp" #include "glaxnimate/model/shapes/shapes/ellipse.hpp" #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/model/shapes/shapes/text.hpp" #include "glaxnimate/model/shapes/style/fill.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" #include "glaxnimate/model/shapes/modifiers/repeater.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" #include "glaxnimate/model/custom_font.hpp" #include "glaxnimate/math/math.hpp" #include "glaxnimate/io/svg/detail.hpp" #include "glaxnimate/io/svg/font_weight.hpp" #include "glaxnimate/io/utils.hpp" #include "glaxnimate/io/svg/enum_map.hpp" using namespace glaxnimate::io::svg::detail; using namespace glaxnimate; class io::svg::SvgRenderer::Private { public: void collect_defs(model::Composition* comp, model::FrameTime t) { if ( !at_start ) return; fps = comp->fps.get(); ip = comp->animation->first_frame.get(); op = comp->animation->last_frame.get(); if ( ip >= op ) animated = NotAnimated; at_start = false; defs = element(svg, "defs"); for ( const auto& color : comp->document()->assets()->colors->values ) write_named_color(defs, color.get(), t); for ( const auto& color : comp->document()->assets()->gradient_colors->values ) write_gradient_colors(defs, color.get()); for ( const auto& gradient : comp->document()->assets()->gradients->values ) write_gradient(defs, gradient.get(), t); auto view = element(svg, "sodipodi:namedview"); view.setAttribute("inkscape:pagecheckerboard", "true"); view.setAttribute("borderlayer", "true"); view.setAttribute("bordercolor", "#666666"); view.setAttribute("pagecolor", "#ffffff"); view.setAttribute("inkscape:document-units", "px"); add_fonts(comp->document()); write_meta(comp); } void write_meta(model::Composition* comp) { auto rdf = element(element(svg, "metadata"), "rdf:RDF"); auto work = element(rdf, "cc:Work"); element(work, "dc:format").appendChild(dom.createTextNode("image/svg+xml")); QString dc_type = animated ? "MovingImage" : "StillImage"; element(work, "dc:type").setAttribute("rdf:resource", "http://purl.org/dc/dcmitype/" + dc_type); element(work, "dc:title").appendChild(dom.createTextNode(comp->name.get())); auto document = comp->document(); if ( document->info().empty() ) return; if ( !document->info().author.isEmpty() ) element(element(element(work, "dc:creator"), "cc:Agent"), "dc:title").appendChild(dom.createTextNode(document->info().author)); if ( !document->info().description.isEmpty() ) element(work, "dc:description").appendChild(dom.createTextNode(document->info().description)); if ( !document->info().keywords.empty() ) { auto bag = element(element(work, "dc:subject"), "rdf:Bag"); for ( const auto& kw: document->info().keywords ) element(bag, "rdf:li").appendChild(dom.createTextNode(kw)); } } void add_fonts(model::Document* document) { if ( font_type == CssFontType::None ) return; QString css; static QString font_face = R"( @font-face { font-family: '%1'; font-style: %2; font-weight: %3; src: url(%4); } )"; for ( const auto & font : document->assets()->fonts->values ) { auto custom = font->custom_font(); if ( !custom.is_valid() ) continue; QRawFont raw = custom.raw_font(); auto type = qMin(suggested_type(font.get()), font_type); if ( type == CssFontType::Link ) { auto link = element(svg, "link"); link.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); link.setAttribute("rel", "stylesheet"); link.setAttribute("href", font->css_url.get()); link.setAttribute("type", "text/css"); } else if ( type == CssFontType::FontFace ) { css += font_face .arg(custom.family()) .arg(WeightConverter::convert(raw.weight(), WeightConverter::qt, WeightConverter::css)) .arg(raw.style() == QFont::StyleNormal ? 0 : 1) .arg(font->source_url.get()) ; } else if ( type == CssFontType::Embedded ) { QString base64_encoded = font->data.get().toBase64(QByteArray::Base64UrlEncoding); QString format = model::CustomFontDatabase::font_data_format(font->data.get()) == model::FontFileFormat::OpenType ? "opentype" : "ttf"; css += font_face .arg(custom.family()) .arg(WeightConverter::convert(raw.weight(), WeightConverter::qt, WeightConverter::css)) .arg(raw.style() == QFont::StyleNormal ? 0 : 1) .arg("data:application/x-font-" + format + ";charset=utf-8;base64," + base64_encoded) ; } } if ( !css.isEmpty() ) element(svg, "style").appendChild(dom.createTextNode(css)); } QDomElement element(QDomNode parent, const char* tag) { QDomElement e = dom.createElement(tag); parent.appendChild(e); return e; } void write_composition(QDomElement& parent, model::Composition* comp, model::FrameTime t) { for ( const auto& lay : comp->shapes ) write_shape(parent, lay.get(), false, t); } void write_visibility_attributes(QDomElement& parent, model::VisualNode* node) { if ( !node->visible.get() ) parent.setAttribute("display", "none"); if ( node->locked.get() ) parent.setAttribute("sodipodi:insensitive", "true"); } void write_shapes(QDomElement& parent, const model::ShapeListProperty& shapes, bool has_mask, model::FrameTime t) { if ( shapes.empty() ) return; auto it = shapes.begin(); if ( has_mask ) ++it; for ( ; it != shapes.end(); ++it ) write_shape(parent, it->get(), false, t); } QString styler_to_css(model::Styler* styler) { if ( styler->use.get() ) return "url(#" + non_uuid_ids_map[styler->use.get()] + ")"; if ( styler->color.get().alpha() == 0 ) return "transparent"; return styler->color.get().name(); } QDomElement write_styler_shapes(QDomElement& parent, model::Styler* styler, const Style::Map& style, model::FrameTime t) { if ( styler->affected().size() == 1 ) { write_shape_shape(parent, styler->affected()[0], style, t); write_visibility_attributes(parent, styler); parent.setAttribute("id", id(styler)); return parent; } auto g = start_group(parent, styler); write_style(g, style); write_visibility_attributes(g, styler); g.setAttribute("id", id(styler)); for ( model::ShapeElement* subshape : styler->affected() ) { write_shape_shape(g, subshape, style, t); } return g; } QString unlerp_time(model::FrameTime time) const { return format_float(math::unlerp(ip, op, time)); } struct AnimationData { struct Attribute { QString attribute; QStringList values = {}; }; AnimationData(SvgRenderer::Private* parent, const std::vector& attrs, int n_keyframes, qreal time_stretch, model::FrameTime time_start) : parent(parent), time_stretch(time_stretch), time_start(time_start) { attributes.reserve(attrs.size()); for ( const auto& attr : attrs ) { attributes.push_back({attr}); attributes.back().values.reserve(n_keyframes); } } QString key_spline(const model::KeyframeTransition& trans) { return QStringLiteral("%1 %2 %3 %4") .arg(parent->format_float(trans.before().x())) .arg(parent->format_float(trans.before().y())) .arg(parent->format_float(trans.after().x())) .arg(parent->format_float(trans.after().y())) ; } void add_values(const std::vector& vals) { for ( std::size_t i = 0; i != attributes.size(); i++ ) attributes[i].values.push_back(vals[i]); } void add_motion_keyframe(model::FrameTime time, qreal lengthpc, const model::KeyframeTransition& trans) { if ( time < parent->ip || time > parent->op ) return; if ( key_times.empty() && time > parent->ip ) { key_times.push_back("0"); key_splines.push_back("0 0 1 1"); key_points.push_back("0"); } else if ( hold && last + 1 < time ) { key_times.push_back(parent->unlerp_time(time - 1)); key_splines.push_back("0 0 1 1"); key_points.push_back(key_points.back()); } key_times.push_back(parent->unlerp_time(time)); key_splines.push_back(key_spline(trans)); key_points.push_back(parent->format_float(lengthpc)); last = time; hold = trans.hold(); } void add_keyframe(model::FrameTime time, const std::vector& vals, const model::KeyframeTransition& trans) { if ( time < parent->ip || time > parent->op ) return; if ( key_times.empty() && time > parent->ip ) { key_times.push_back("0"); key_splines.push_back("0 0 1 1"); add_values(vals); } else if ( hold && last + 1 < time ) { key_times.push_back(parent->unlerp_time(time - 1)); key_splines.push_back("0 0 1 1"); for ( std::size_t i = 0; i != attributes.size(); i++ ) attributes[i].values.push_back(attributes[i].values.back()); } key_times.push_back(parent->unlerp_time(time)); key_splines.push_back(key_spline(trans)); add_values(vals); last = time; hold = trans.hold(); } void add_dom( QDomElement& element, const char* tag = "animate", const QString& type = {}, const QString& path = {}, bool auto_orient = false ) { if ( last < parent->op && path.isEmpty() ) { key_times.push_back("1"); for ( auto& attr : attributes ) { if ( !attr.values.empty() ) attr.values.push_back(attr.values.back()); } } else { key_splines.pop_back(); } QString key_times_str = key_times.join("; "); QString key_splines_str = key_splines.join("; "); for ( const auto& data : attributes ) { QDomElement animation = parent->element(element, tag); animation.setAttribute("begin", parent->clock(time_start + time_stretch * parent->ip)); animation.setAttribute("dur", parent->clock(time_start + time_stretch * parent->op-parent->ip)); animation.setAttribute("attributeName", data.attribute); animation.setAttribute("calcMode", "spline"); if ( !path.isEmpty() ) { animation.setAttribute("path", path); animation.setAttribute("keyPoints", key_points.join("; ")); if ( auto_orient ) animation.setAttribute("rotate", "auto"); } animation.setAttribute("keyTimes", key_times_str); animation.setAttribute("keySplines", key_splines_str); animation.setAttribute("repeatCount", "indefinite"); if ( !type.isEmpty() ) animation.setAttribute("type", type); if ( !data.values.isEmpty() ) animation.setAttribute("values", data.values.join("; ")); } } SvgRenderer::Private* parent; std::vector attributes; QStringList key_times = {}; QStringList key_splines = {}; QStringList key_points = {}; model::FrameTime last = 0; bool hold = false; qreal time_stretch = 1; model::FrameTime time_start = 0; }; void write_property( QDomElement& element, model::AnimatedPropertyBase* property, const QString& attr, model::FrameTime t ) { element.setAttribute(attr, property->value(t).toString()); if ( animated ) { if ( property->keyframe_count() < 2 ) return; auto keyframes = split_keyframes(property); AnimationData data(this, {attr}, keyframes.size(), time_stretch, time_start); for ( int i = 0; i < int(keyframes.size()); i++ ) { auto kf = keyframes[i].get(); data.add_keyframe(time_to_global(kf->time()), {kf->value().toString()}, kf->transition()); } data.add_dom(element); } } qreal time_to_global(qreal time) { for ( auto it = timing.rbegin(), end = timing.rend(); it != end; ++it ) time = (*it)->time_from_local(time); return time; } template void write_properties( QDomElement& element, model::FrameTime t, std::vector properties, const std::vector& attrs, const Callback& callback ) { auto jflags = animated == NotAnimated ? model::JoinAnimatables::NoKeyframes : model::JoinAnimatables::Normal; model::JoinedAnimatable j(std::move(properties), {}, jflags); { auto vals = callback(j.value_at(t)); for ( std::size_t i = 0; i != attrs.size(); i++ ) element.setAttribute(attrs[i], vals[i]); } if ( j.animated() && animated ) { auto keys = split_keyframes(&j); AnimationData data(this, attrs, keys.size(), time_stretch, time_start); for ( const auto& kf : keys ) data.add_keyframe(time_to_global(kf->time()), callback(j.value_at(kf->time())), kf->transition()); data.add_dom(element); } } static std::vector callback_point(const std::vector& values) { return callback_point_result(values[0].toPointF()); } static std::vector callback_point_result(const QPointF& c) { return std::vector{ QString::number(c.x()), QString::number(c.y()) }; } void write_shape_rect(QDomElement& parent, model::Rect* rect, const Style::Map& style, model::FrameTime t) { auto e = element(parent, "rect"); write_style(e, style); write_properties(e, t, {&rect->position, &rect->size}, {"x", "y"}, [this](const std::vector& values){ QPointF c = values[0].toPointF(); QSizeF s = values[1].toSizeF(); return std::vector{ format_float(c.x() - s.width()/2), format_float(c.y() - s.height()/2) }; } ); write_properties(e, t, {&rect->size}, {"width", "height"}, [this](const std::vector& values){ QSizeF s = values[0].toSizeF(); return std::vector{ format_float(s.width()), format_float(s.height()) }; } ); write_property(e, &rect->rounded, "ry", t); } void write_shape_ellipse(QDomElement& parent, model::Ellipse* ellipse, const Style::Map& style, model::FrameTime t) { auto e = element(parent, "ellipse"); write_style(e, style); write_properties(e, t, {&ellipse->position}, {"cx", "cy"}, &Private::callback_point); write_properties(e, t, {&ellipse->size}, {"rx", "ry"}, [this](const std::vector& values){ QSizeF s = values[0].toSizeF(); return std::vector{ format_float(s.width() / 2), format_float(s.height() / 2) }; } ); } void write_shape_star(QDomElement& parent, model::PolyStar* star, const Style::Map& style, model::FrameTime t) { model::FrameTime time = star->time(); auto e = write_bezier(parent, star, style, t); if ( star->outer_roundness.animated() || !qFuzzyIsNull(star->outer_roundness.get()) || star->inner_roundness.animated() || !qFuzzyIsNull(star->inner_roundness.get()) ) return; set_attribute(e, "sodipodi:type", "star"); set_attribute(e, "inkscape:randomized", "0"); // inkscape:rounded Works differently than lottie so we leave it as 0 set_attribute(e, "inkscape:rounded", "0"); int sides = star->points.get_at(time); set_attribute(e, "sodipodi:sides", sides); set_attribute(e, "inkscape:flatsided", star->type.get() == model::PolyStar::Polygon); QPointF c = star->position.get_at(time); set_attribute(e, "sodipodi:cx", c.x()); set_attribute(e, "sodipodi:cy", c.y()); set_attribute(e, "sodipodi:r1", star->outer_radius.get_at(time)); set_attribute(e, "sodipodi:r2", star->inner_radius.get_at(time)); qreal angle = math::deg2rad(star->angle.get_at(time) - 90); set_attribute(e, "sodipodi:arg1", angle); set_attribute(e, "sodipodi:arg2", angle + math::pi / sides); } void write_shape_text(QDomElement& parent, model::TextShape* text, Style::Map style, model::FrameTime t) { QFontInfo font_info(text->font->query()); // QFontInfo is broken, so we do something else int weight = QFontDatabase::weight(font_info.family(), font_info.styleName()); QFont::Style font_style = QFontDatabase::italic(font_info.family(), font_info.styleName()) ? QFont::StyleItalic : QFont::StyleNormal; // Convert weight weight = WeightConverter::convert(weight, WeightConverter::qt, WeightConverter::css); style["font-family"] = font_info.family(); style["font-size"] = QString("%1pt").arg(font_info.pointSizeF()); style["line-height"] = QString("%1px").arg(text->font->line_spacing()); style["font-weight"] = QString::number(weight); switch ( font_style ) { case QFont::StyleNormal: style["font-style"] = "normal"; break; case QFont::StyleItalic: style["font-style"] = "italic"; break; case QFont::StyleOblique: style["font-style"] = "oblique"; break; } auto e = element(parent, "text"); write_style(e, style); write_properties(e, t, {&text->position}, {"x", "y"}, &Private::callback_point); model::Font::CharDataCache cache; for ( const auto& line : text->font->layout(text->text.get()) ) { auto tspan = element(e, "tspan"); tspan.appendChild(dom.createTextNode(line.text)); set_attribute(tspan, "sodipodi:role", "line"); write_properties(tspan, t, {&text->position}, {"x", "y"}, [base=line.baseline](const std::vector& values){ return callback_point_result(values[0].toPointF() + base); }); tspan.setAttribute("xml:space", "preserve"); } } void write_shape_shape(QDomElement& parent, model::ShapeElement* shape, const Style::Map& style, model::FrameTime t) { if ( auto rect = qobject_cast(shape) ) { write_shape_rect(parent, rect, style, t); } else if ( auto ellipse = qobject_cast(shape) ) { write_shape_ellipse(parent, ellipse, style, t); } else if ( auto star = qobject_cast(shape) ) { write_shape_star(parent, star, style, t); } else if ( auto text = shape->cast() ) { write_shape_text(parent, text, style, t); } else if ( !qobject_cast(shape) ) { write_bezier(parent, shape, style, t); } } void write_styler_attrs(QDomElement& element, model::Styler* styler, const QString& attr, model::FrameTime t) { if ( styler->use.get() ) { element.setAttribute(attr, "url(#" + non_uuid_ids_map[styler->use.get()] + ")"); return; } write_property(element, &styler->color, attr, t); write_property(element, &styler->opacity, attr+"-opacity", t); } void write_composable(model::Composable* comp, QDomElement& e, model::FrameTime t) { transform_to_attr(e, t, comp->transform.get()); if ( comp->blend_mode.get() != renderer::BlendMode::Normal ) set_attribute(e, "style", "mix-blend-mode: " + detail::enum_to_svg(comp->blend_mode.get(), detail::blend_modes, "source-over")); } void write_image(model::Image* img, QDomElement& parent, model::FrameTime t) { if ( img->image.get() ) { auto e = element(parent, "image"); // set_attribute(e, "x", 0); // set_attribute(e, "y", 0); set_attribute(e, "width", img->image->width.get()); set_attribute(e, "height", img->image->height.get()); write_composable(img, e, t); set_attribute(e, "xlink:href", img->image->to_url().toString()); } } void write_stroke(model::Stroke* stroke, QDomElement& parent, model::FrameTime t) { Style::Map style; style["fill"] = "none"; if ( !animated ) { style["stroke"] = styler_to_css(stroke); style["stroke-opacity"] = format_float(stroke->opacity.get()); style["stroke-width"] = format_float(stroke->width.get()); } switch ( stroke->cap.get() ) { case model::Stroke::Cap::ButtCap: style["stroke-linecap"] = "butt"; break; case model::Stroke::Cap::RoundCap: style["stroke-linecap"] = "round"; break; case model::Stroke::Cap::SquareCap: style["stroke-linecap"] = "square"; break; } switch ( stroke->join.get() ) { case model::Stroke::Join::BevelJoin: style["stroke-linejoin"] = "bevel"; break; case model::Stroke::Join::RoundJoin: style["stroke-linejoin"] = "round"; break; case model::Stroke::Join::MiterJoin: style["stroke-linejoin"] = "miter"; style["stroke-miterlimit"] = format_float(stroke->miter_limit.get()); break; } style["stroke-dasharray"] = "none"; QDomElement g = write_styler_shapes(parent, stroke, style, t); if ( animated ) { write_styler_attrs(g, stroke, "stroke", t); write_property(g, &stroke->width, "stroke-width", t); } } void write_fill(model::Fill* fill, QDomElement& parent, model::FrameTime t) { Style::Map style; if ( !animated ) { style["fill"] = styler_to_css(fill); style["fill-opacity"] = format_float(fill->opacity.get()); } style["stroke"] = "none"; QDomElement g = write_styler_shapes(parent, fill, style, t); if ( animated ) write_styler_attrs(g, fill, "fill", t); } void write_precomp_layer(model::PreCompLayer* layer, QDomElement& parent, model::FrameTime t) { auto comp = layer->composition.get(); if ( comp ) { auto inner_time = layer->timing.get()->time_to_local(t); if ( !animated && !comp->animation.get()->time_visible(inner_time) ) return; timing.push_back(layer->timing.get()); if ( !layer->unbounded.get() ) { auto clip = element(defs, "clipPath"); set_attribute(clip, "id", "clip_" + id(layer)); set_attribute(clip, "clipPathUnits", "userSpaceOnUse"); auto clip_rect = element(clip, "rect"); set_attribute(clip_rect, "x", "0"); set_attribute(clip_rect, "y", "0"); set_attribute(clip_rect, "width", layer->size.get().width()); set_attribute(clip_rect, "height", layer->size.get().height()); } auto e = start_layer(parent, layer); write_composable(layer, e, t); write_property(e, &layer->opacity, "opacity", t); write_visibility_attributes(parent, layer); time_stretch = layer->timing->stretch.get(); time_start = layer->timing->start_time.get(); write_time_range_display(e, layer, comp->animation.get()); write_composition(e, comp, inner_time); time_stretch = 1; time_start = 0; timing.pop_back(); } } void write_repeater_vis(QDomElement& element, model::Repeater* repeater, int index, int n_copies) { element.setAttribute("display", index < repeater->copies.get() ? "block" : "none"); float alpha_lerp = float(index) / (n_copies == 1 ? 1 : n_copies - 1); model::JoinAnimatables opacity({&repeater->start_opacity, &repeater->end_opacity}, model::JoinAnimatables::NoValues); auto opacity_func = [&alpha_lerp](float a, float b){ return math::lerp(a, b, alpha_lerp); }; set_attribute(element, "opacity", opacity.combine_current_value(opacity_func)); if ( animated ) { int kf_count = repeater->copies.keyframe_count(); if ( kf_count >= 2 ) { AnimationData anim_display(this, {"display"}, kf_count, time_stretch, time_start); for ( const auto& kf : repeater->copies ) { anim_display.add_keyframe(time_to_global(kf.time()), {index < kf.get() ? "block" : "none"}, kf.transition()); } anim_display.add_dom(element); } if ( opacity.animated() ) { AnimationData anim_opacity(this, {"opacity"}, opacity.keyframes().size(), time_stretch, time_start); for ( const auto& keyframe : opacity.keyframes() ) { anim_opacity.add_keyframe( time_to_global(keyframe.time), {format_float(opacity.combine_value_at(keyframe.time, opacity_func))}, keyframe.transition() ); } } } } void write_repeater(model::Repeater* repeater, QDomElement& parent, bool force_draw, model::FrameTime t) { int n_copies = repeater->max_copies(); if ( n_copies < 1 ) return; QDomElement container = start_group(parent, repeater); QString base_id = id(repeater); QString prev_clone_id = base_id + "_0"; QDomElement og = element(container, "g"); og.setAttribute("id", prev_clone_id); for ( const auto& sib : repeater->affected() ) write_shape(og, sib, force_draw, t); write_repeater_vis(og, repeater, 0, n_copies); for ( int i = 1; i < n_copies; i++ ) { QString clone_id = base_id + "_" + QString::number(i);; QDomElement use = element(container, "use"); use.setAttribute("xlink:href", "#" + prev_clone_id); use.setAttribute("id", clone_id); write_repeater_vis(use, repeater, i, n_copies); transform_to_attr(use, t, repeater->transform.get()); prev_clone_id = clone_id; } } void write_shape(QDomElement& parent, model::ShapeElement* shape, bool force_draw, model::FrameTime t) { if ( auto grp = qobject_cast(shape) ) { write_group_shape(parent, grp, t); } else if ( auto stroke = qobject_cast(shape) ) { if ( stroke->visible.get() ) write_stroke(stroke, parent, t); } else if ( auto fill = qobject_cast(shape) ) { if ( fill->visible.get() ) write_fill(fill, parent, t); } else if ( auto img = qobject_cast(shape) ) { write_image(img, parent, t); } else if ( auto layer = qobject_cast(shape) ) { write_precomp_layer(layer, parent, t); } else if ( auto repeater = qobject_cast(shape) ) { write_repeater(repeater, parent, force_draw, t); } else if ( force_draw ) { write_shape_shape(parent, shape, {}, t); write_visibility_attributes(parent, shape); set_attribute(parent, "id", id(shape)); } } QDomElement write_bezier(QDomElement& parent, model::ShapeElement* shape, const Style::Map& style, model::FrameTime t) { QDomElement path = element(parent, "path"); write_style(path, style); QString d; QString nodetypes; std::tie(d, nodetypes) = path_data(shape->shapes(t)); set_attribute(path, "d", d); set_attribute(path, "sodipodi:nodetypes", nodetypes); if ( animated ) { std::vector props; for ( auto prop : shape->properties() ) { if ( prop->traits().flags & model::PropertyTraits::Animated ) props.push_back(static_cast(prop)); } model::JoinAnimatables j(std::move(props), model::JoinAnimatables::NoValues); if ( j.animated() ) { AnimationData data(this, {"d"}, j.keyframes().size(), time_stretch, time_start); for ( const auto& kf : j ) data.add_keyframe(time_to_global(kf.time), {path_data(shape->shapes(kf.time)).first}, kf.transition()); data.add_dom(path); } } return path; } /** * \brief Creates a element for recurse_parents * \param parent DOM element to add the into * \param ancestor Ancestor layer (to create the for) * \param descendant Descendant layer */ QDomElement start_layer_recurse_parents(const QDomElement& parent, model::Layer* ancestor, model::Layer* descendant, model::FrameTime t) { QDomElement g = element(parent, "g"); g.setAttribute("id", id(descendant) + "_" + id(ancestor)); g.setAttribute("inkscape:label", i18n("%1 (%2)", descendant->object_name(), ancestor->object_name())); g.setAttribute("inkscape:groupmode", "layer"); transform_to_attr(g, t, ancestor->transform.get()); return g; } /** * \brief Creates nested elements for each layer parent (using the parent property) * \param parent DOM element to add the elements into * \param ancestor Ancestor layer (searched recursively for parents) * \param descendant Descendant layer */ QDomElement recurse_parents(const QDomElement& parent, model::Layer* ancestor, model::Layer* descendant, model::FrameTime t) { if ( !ancestor->parent.get() ) return start_layer_recurse_parents(parent, ancestor, descendant, t); return start_layer_recurse_parents(recurse_parents(parent, ancestor->parent.get(), descendant, t), ancestor, descendant, t); } void write_time_range_display(QDomElement& parent, model::ShapeElement* layer, model::AnimationContainer* lay_range) { if ( animated && layer->visible.get() ) { auto* doc_range = layer->owner_composition()->animation.get(); auto lay_first = time_to_global(lay_range->first_frame.get()); auto lay_end = time_to_global(lay_range->last_frame.get()); bool has_start = lay_first > doc_range->first_frame.get(); bool has_end = lay_end < doc_range->last_frame.get(); if ( has_start || has_end ) { QDomElement animation = element(parent, "animate"); animation.setAttribute("begin", clock(ip)); animation.setAttribute("dur", clock(op-ip)); animation.setAttribute("calcMode", "discrete"); animation.setAttribute("attributeName", "display"); animation.setAttribute("repeatCount", "indefinite"); QString times; QString vals; times += "0;"; if ( has_start ) { vals += "none;inline;"; times += unlerp_time(lay_first) + ";"; } else { vals += "inline;"; } if ( has_end ) { vals += "none;"; times += unlerp_time(lay_end) + ";"; } animation.setAttribute("values", vals); animation.setAttribute("keyTimes", times); } } } void write_group_shape(QDomElement& parent, model::Group* group, model::FrameTime t) { QDomElement g; bool has_mask = false; if ( auto layer = group->cast() ) { if ( !layer->render.get() ) return; if ( layer->parent.get() ) { QDomElement parent_g = recurse_parents(parent, layer->parent.get(), layer, t); g = start_layer(parent_g, group); } else { g = start_layer(parent, group); } if ( layer->mask->has_mask() ) { has_mask = true; QDomElement clip = element(defs, "mask"); QString mask_id = "clip_" + id(layer); clip.setAttribute("id", mask_id); clip.setAttribute("mask-type", layer->mask->mask.value() == model::MaskSettings::Luma ? "luminance" : "alpha"); if ( layer->shapes.size() > 1 ) write_shape(clip, layer->shapes[0], false, t); g.setAttribute("mask", "url(#" + mask_id + ")"); } write_time_range_display(g, layer, layer->animation.get()); } else { g = start_group(parent, group); } write_composable(group, g, t); write_property(g, &group->opacity, "opacity", t); write_visibility_attributes(g, group); write_shapes(g, group->shapes, has_mask, t); } template QDomElement transform_property( QDomElement& e, model::FrameTime t, const char* name, PropT* prop, const Callback& callback, const math::bezier::Bezier& path = {}, bool auto_orient = false ) { model::JoinAnimatables j({prop}, model::JoinAnimatables::NoValues); auto parent = e.parentNode(); QDomElement g = dom.createElement("g"); parent.insertBefore(g, e); parent.removeChild(e); g.appendChild(e); if ( j.animated() ) { AnimationData data(this, {"transform"}, j.keyframes().size(), time_stretch, time_start); if ( !path.empty() ) { if ( j.keyframes()[0].time > ip ) data.add_motion_keyframe(time_to_global(ip), 0, {}); math::bezier::LengthData bezlen(path, 12); int lenid = 0; for ( const auto& kf : j ) data.add_motion_keyframe(time_to_global(kf.time), bezlen.child_start(lenid++) / bezlen.length(), kf.transition()); if ( j.keyframes().back().time < op ) { data.add_motion_keyframe(time_to_global(op), 1, model::KeyframeTransition::Special::Hold); } data.add_dom(g, "animateMotion", "", path_data(path).first, auto_orient); return g; } else { for ( const auto& kf : j ) data.add_keyframe(time_to_global(kf.time), {callback(prop->get_at(kf.time))}, kf.transition()); data.add_dom(g, "animateTransform", name); } } g.setAttribute("transform", QString("%1(%2)").arg(name).arg(callback(prop->get_at(t)))); return g; } void transform_to_attr(QDomElement& parent, model::FrameTime t, model::Transform* transf) { if ( animated && (transf->position.animated() || transf->scale.animated() || transf->rotation.animated() || transf->anchor_point.animated()) ) { QDomElement subject = parent; if ( transf->anchor_point.animated() || transf->anchor_point.get() != QPointF() ) subject = transform_property(subject, t, "translate", &transf->anchor_point, [](const QPointF& val){ return QString("%1 %2").arg(-val.x()).arg(-val.y()); }); if ( transf->scale.animated() || transf->scale.get() != QVector2D(1, 1) ) subject = transform_property(subject, t, "scale", &transf->scale, [](const QVector2D& val){ return QString("%1 %2").arg(val.x()).arg(val.y()); }); if ( transf->rotation.animated() || transf->rotation.get() != 0 ) subject = transform_property(subject, t, "rotate", &transf->rotation, [this](qreal val){ return format_float(val); }); if ( transf->position.animated() || transf->position.get() != QPointF() ) { math::bezier::Bezier mb = transf->position.bezier(); subject = transform_property(subject, t, "translate", &transf->position, [](const QPointF& val){ return QString("%1 %2").arg(val.x()).arg(val.y()); }, mb, transf->auto_orient.get()); } } else { auto matr = transf->transform_matrix(t); parent.setAttribute("transform", QString("matrix(%1, %2, %3, %4, %5, %6)") .arg(matr.m11()) .arg(matr.m12()) .arg(matr.m21()) .arg(matr.m22()) .arg(matr.m31()) .arg(matr.m32()) ); } } void write_style(QDomElement& element, const Style::Map& s) { QString st; for ( auto it : s ) { st.append(it.first); st.append(':'); st.append(it.second); st.append(';'); } element.setAttribute("style", st); } QDomElement start_group(QDomElement& parent, model::DocumentNode* node) { QDomElement g = element(parent, "g"); g.setAttribute("id", id(node)); g.setAttribute("inkscape:label", node->object_name()); return g; } QDomElement start_layer(QDomElement& parent, model::DocumentNode* node) { auto g = start_group(parent, node); g.setAttribute("inkscape:groupmode", "layer"); return g; } QString id(model::DocumentNode* node) { return node->type_name() + "_" + node->uuid.get().toString(QUuid::Id128); } /// Avoid locale nonsense by defining these functions (on ASCII chars) manually static constexpr bool valid_id_start(char c) noexcept { return ( c >= 'a' && c <= 'z') || ( c >= 'A' && c <= 'Z') || c == '_'; } static constexpr bool valid_id(char c) noexcept { return valid_id_start(c) || ( c >= '0' && c <= '9') || c == '-'; } void write_named_color(QDomElement& parent, model::NamedColor* color, model::FrameTime t) { auto gradient = element(parent, "linearGradient"); gradient.setAttribute("osb:paint", "solid"); QString id = pretty_id(color->name.get(), color); non_uuid_ids_map[color] = id; gradient.setAttribute("id", id); auto stop = element(gradient, "stop"); stop.setAttribute("offset", "0"); write_property(stop, &color->color, "stop-color", t); } QString pretty_id(const QString& s, model::DocumentNode* node) { if ( s.isEmpty() ) return id(node); QByteArray str = s.toLatin1(); QString id_attempt; if ( !valid_id_start(str[0]) ) id_attempt.push_back('_'); for ( char c : str ) { if ( c == ' ' ) id_attempt.push_back('_'); else if ( valid_id(c) ) id_attempt.push_back(c); } if ( id_attempt.isEmpty() ) return id(node); QString id_final = id_attempt; int i = 1; while ( non_uuid_ids.count(id_final) ) id_final = id_attempt + QString::number(i++); return id_final; } template std::enable_if_t && !std::is_same_v> set_attribute(QDomElement& e, const QString& name, T val) { // not using e.setAttribute overloads to bypass locale settings e.setAttribute(name, QString::number(val)); } void set_attribute(QDomElement& e, const QString& name, bool val) { e.setAttribute(name, val ? "true" : "false"); } void set_attribute(QDomElement& e, const QString& name, const char* val) { e.setAttribute(name, val); } void set_attribute(QDomElement& e, const QString& name, const QString& val) { e.setAttribute(name, val); } void write_gradient_colors(QDomElement& parent, model::GradientColors* gradient) { auto e = element(parent, "linearGradient"); QString id = pretty_id(gradient->name.get(), gradient); non_uuid_ids_map[gradient] = id; e.setAttribute("id", id); if ( animated && gradient->colors.keyframe_count() > 1 ) { int n_stops = std::numeric_limits::max(); for ( const auto& kf : gradient->colors ) if ( kf.get().size() < n_stops ) n_stops = kf.get().size(); auto stops = gradient->colors.get(); for ( int i = 0; i < n_stops; i++ ) { AnimationData data(this, {"offset", "stop-color"}, gradient->colors.keyframe_count(), time_stretch, time_start); for ( const auto& kf : gradient->colors ) { auto stop = kf.get()[i]; data.add_keyframe( time_to_global(kf.time()), {format_float(stop.first), stop.second.name()}, kf.transition() ); } auto s = element(e, "stop"); s.setAttribute("stop-opacity", "1"); set_attribute(s, "offset", stops[i].first); s.setAttribute("stop-color", stops[i].second.name()); data.add_dom(s); } } else { for ( const auto& stop : gradient->colors.get() ) { auto s = element(e, "stop"); s.setAttribute("stop-opacity", "1"); set_attribute(s, "offset", stop.first); s.setAttribute("stop-color", stop.second.name()); } } } void write_gradient(QDomElement& parent, model::Gradient* gradient, model::FrameTime t) { QDomElement e; if ( gradient->type.get() == model::Gradient::Radial || gradient->type.get() == model::Gradient::Conical ) { e = element(parent, "radialGradient"); write_properties(e, t, {&gradient->start_point}, {"cx", "cy"}, &Private::callback_point); write_properties(e, t, {&gradient->highlight}, {"fx", "fy"}, &Private::callback_point); write_properties(e, t, {&gradient->start_point, &gradient->end_point}, {"r"}, [this](const std::vector& values) -> std::vector { return { format_float( math::length(values[1].toPointF() - values[0].toPointF()) )}; }); } else { e = element(parent, "linearGradient"); write_properties(e, t, {&gradient->start_point}, {"x1", "y1"}, &Private::callback_point); write_properties(e, t, {&gradient->end_point}, {"x2", "y2"}, &Private::callback_point); } QString id = pretty_id(gradient->name.get(), gradient); non_uuid_ids_map[gradient] = id; e.setAttribute("id", id); e.setAttribute("gradientUnits", "userSpaceOnUse"); auto it = non_uuid_ids_map.find(gradient->colors.get()); if ( it != non_uuid_ids_map.end() ) e.setAttribute("xlink:href", "#" + it->second); } QString clock(model::FrameTime time) { return format_float(time / fps); } QString format_float(double val) const { if ( qFuzzyIsNull(math::fmod(val, 1.)) ) return QString::number(int(val)); return QString::number(val, 'f', 3); } std::vector timing; QDomDocument dom; qreal fps = 60; qreal ip = 0; qreal op = 60; bool at_start = true; std::set non_uuid_ids; std::map non_uuid_ids_map; AnimationType animated; QDomElement svg; QDomElement defs; CssFontType font_type; qreal time_stretch = 1; model::FrameTime time_start = 0; }; io::svg::SvgRenderer::SvgRenderer(AnimationType animated, CssFontType font_type) : d(std::make_unique()) { d->animated = animated; d->font_type = font_type; d->svg = d->dom.createElement("svg"); d->dom.appendChild(d->svg); d->svg.setAttribute("xmlns", detail::xmlns.at("svg")); for ( const auto& p : detail::xmlns ) { if ( !p.second.contains("android") ) d->svg.setAttribute("xmlns:" + p.first, p.second); } d->write_style(d->svg, { {"fill", "none"}, {"stroke", "none"} }); d->svg.setAttribute("inkscape:export-xdpi", "96"); d->svg.setAttribute("inkscape:export-ydpi", "96"); d->svg.setAttribute("version", "1.1"); } io::svg::SvgRenderer::~SvgRenderer() { } void io::svg::SvgRenderer::write_composition(model::Composition* comp, model::FrameTime t) { d->collect_defs(comp, t); auto g = d->start_layer(d->svg, comp); d->write_composition(g, comp, t); } void io::svg::SvgRenderer::write_main(model::Composition* comp, model::FrameTime t) { if ( d->at_start ) { QString w = QString::number(comp->width.get()); QString h = QString::number(comp->height.get()); d->svg.setAttribute("width", w); d->svg.setAttribute("height", h); d->svg.setAttribute("viewBox", QString("0 0 %1 %2").arg(w).arg(h)); d->svg.appendChild(d->dom.createElement("title")).appendChild(d->dom.createTextNode(comp->name.get())); write_composition(comp, t); } else { write_composition(comp, t); } } void io::svg::SvgRenderer::write_shape(model::ShapeElement* shape, model::FrameTime t) { d->collect_defs(shape->owner_composition(), t); d->write_shape(d->svg, shape, true, t); } void io::svg::SvgRenderer::write_node(model::DocumentNode* node, model::FrameTime t) { if ( auto co = qobject_cast(node) ) write_main(co, t); else if ( auto sh = qobject_cast(node) ) write_shape(sh, t); } QDomDocument io::svg::SvgRenderer::dom() const { return d->dom; } void io::svg::SvgRenderer::write(QIODevice* device, bool indent) { device->write(d->dom.toByteArray(indent ? 4 : -1)); } glaxnimate::io::svg::CssFontType glaxnimate::io::svg::SvgRenderer::suggested_type(model::EmbeddedFont* font) { if ( !font->css_url.get().isEmpty() ) return CssFontType::Link; if ( !font->source_url.get().isEmpty() ) return CssFontType::FontFace; if ( !font->data.get().isEmpty() ) return CssFontType::Embedded; return CssFontType::None; } static char bezier_node_type(const math::bezier::Point& p) { switch ( p.type ) { case math::bezier::PointType::Smooth: return 's'; case math::bezier::PointType::Symmetrical: return 'z'; case math::bezier::PointType::Corner: default: return 'c'; } } std::pair glaxnimate::io::svg::path_data(const math::bezier::MultiBezier& shape) { QString d; QString nodetypes; for ( const math::bezier::Bezier& b : shape.beziers() ) { if ( b.empty() ) continue; d += QString("M %1,%2 C").arg(b[0].pos.x()).arg(b[0].pos.y()); nodetypes += bezier_node_type(b[0]); for ( int i = 1; i < b.size(); i++ ) { d += QString(" %1,%2 %3,%4 %5,%6") .arg(b[i-1].tan_out.x()).arg(b[i-1].tan_out.y()) .arg(b[i].tan_in.x()).arg(b[i].tan_in.y()) .arg(b[i].pos.x()).arg(b[i].pos.y()) ; nodetypes += bezier_node_type(b[i]); } if ( b.closed() ) { d += QString(" %1,%2 %3,%4 %5,%6") .arg(b.back().tan_out.x()).arg(b.back().tan_out.y()) .arg(b[0].tan_in.x()).arg(b[0].tan_in.y()) .arg(b[0].pos.x()).arg(b[0].pos.y()) ; d += " Z"; } } return {d, nodetypes}; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/avd/000775 001750 001750 00000000000 15165022620 033203 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/path_parser.hpp000664 001750 001750 00000030351 15165022620 032656 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include "glaxnimate/utils/regexp.hpp" #include "glaxnimate/math/bezier/bezier.hpp" #include "glaxnimate/math/ellipse_solver.hpp" namespace glaxnimate::io::svg::detail { class PathDParser { public: enum TokenType { Command, Parameter, }; using Token = std::variant; private: struct Lexer { Lexer(const QString& d, std::vector& tokens) : d(d), tokens(tokens) {} QString d; int off = 0; std::vector& tokens; QString lexed; QChar ch; bool eof() const { return off >= d.size(); } bool next() { ++off; if ( !eof() ) { ch = d[off]; return true; } ch = {}; return false; } void lex() { static QString cmds = "MLHVCSQTAZ"; ch = d[off]; while ( off < d.size() ) { if ( cmds.contains(ch.toUpper()) ) { tokens.emplace_back(ushort(ch.unicode())); next(); } else if ( ch.isSpace() || ch == ',') { next(); } else { lex_value(); } } } void lex_value() { lexed.clear(); if ( ch == '+' || ch == '-' ) { lexed += ch; if ( !next() ) return; } if ( ch.isDigit() ) lex_value_int(); if ( ch == '.' ) { lexed += ch; if ( !next() ) return; lex_value_decimal(); } else if ( ch.toUpper() == 'E' ) { lexed += ch; if ( !next() ) return; lex_value_exponent(); } if ( lexed.isEmpty() ) { next(); return; } tokens.emplace_back(lexed.toDouble()); lexed.clear(); } void lex_value_int() { while ( off < d.size() && ch.isDigit() ) { lexed += ch; next(); } } void lex_value_decimal() { lex_value_int(); if ( ch.toUpper() == 'E' ) { lexed += ch; if ( !next() ) return; lex_value_exponent(); } } void lex_value_exponent() { if ( ch == '+' || ch == '-' ) { lexed += ch; if ( !next() ) return; } lex_value_int(); } }; public: PathDParser(const QString& d) { tokenize(d); } const math::bezier::MultiBezier& parse() { while ( !eof() ) { if ( la_type() == Command ) { ushort cmd = std::get(la()); next_token(); parse_command(cmd); } else { parse_command(implicit); } } return bez; } private: const Token& la() const { return tokens[index]; } void next_token() { ++index; } bool eof() const { return index >= int(tokens.size()); } TokenType la_type() const { return TokenType(la().index()); } void tokenize(const QString& d) { if ( d.isEmpty() ) return; Lexer(d, tokens).lex(); } qreal read_param() { if ( la_type() == Parameter ) { qreal v = std::get(la()); next_token(); return v; } return 0; } QPointF read_vector() { return {read_param(), read_param()}; } void parse_M() { if ( la_type() != Parameter ) { next_token(); return; } p = read_vector(); bez.move_to(p); implicit = 'L'; } void parse_m() { if ( la_type() != Parameter ) { next_token(); return; } p += read_vector(); bez.move_to(p); implicit = 'l'; } void parse_L() { if ( la_type() != Parameter ) { next_token(); return; } p = read_vector(); bez.line_to(p); implicit = 'L'; } void parse_l() { if ( la_type() != Parameter ) { next_token(); return; } p += read_vector(); bez.line_to(p); implicit = 'l'; } void parse_H() { if ( la_type() != Parameter ) { next_token(); return; } p.setX(read_param()); bez.line_to(p); implicit = 'H'; } void parse_h() { if ( la_type() != Parameter ) { next_token(); return; } p.setX(p.x() + read_param()); bez.line_to(p); implicit = 'h'; } void parse_V() { if ( la_type() != Parameter ) { next_token(); return; } p.setY(read_param()); bez.line_to(p); implicit = 'V'; } void parse_v() { if ( la_type() != Parameter ) { next_token(); return; } p.setY(p.y() + read_param()); bez.line_to(p); implicit = 'v'; } void parse_C() { if ( la_type() != Parameter ) { next_token(); return; } QPointF tan_out = read_vector(); QPointF tan_in = read_vector(); p = read_vector(); bez.cubic_to(tan_out, tan_in, p); implicit = 'C'; } void parse_c() { if ( la_type() != Parameter ) { next_token(); return; } QPointF tan_out = p + read_vector(); QPointF tan_in = p + read_vector(); p += read_vector(); bez.cubic_to(tan_out, tan_in, p); implicit = 'c'; } void parse_S() { if ( la_type() != Parameter ) { next_token(); return; } QPointF old_p = p; QPointF tan_in = read_vector(); p = read_vector(); if ( bez.beziers().empty() || bez.beziers().back().empty() ) { bez.cubic_to(old_p, tan_in, p); } else { auto& prev = bez.beziers().back().points().back(); QPointF tan_out = prev.pos - prev.relative_tan_in(); prev.type = math::bezier::Symmetrical; bez.cubic_to(tan_out, tan_in, p); } implicit = 'S'; } void parse_s() { if ( la_type() != Parameter ) { next_token(); return; } QPointF old_p = p; QPointF tan_in = p+read_vector(); p += read_vector(); if ( bez.beziers().empty() || bez.beziers().back().empty() ) { bez.cubic_to(old_p, tan_in, p); } else { auto& prev = bez.beziers().back().points().back(); QPointF tan_out = prev.pos - prev.relative_tan_in(); prev.type = math::bezier::Symmetrical; bez.cubic_to(tan_out, tan_in, p); } implicit = 's'; } void parse_Q() { if ( la_type() != Parameter ) { next_token(); return; } QPointF tan = read_vector(); p = read_vector(); bez.quadratic_to(tan, p); implicit = 'Q'; } void parse_q() { if ( la_type() != Parameter ) { next_token(); return; } QPointF tan = p+read_vector(); p += read_vector(); bez.quadratic_to(tan, p); implicit = 'q'; } void parse_T() { if ( la_type() != Parameter ) { next_token(); return; } QPointF old_p = p; p = read_vector(); if ( bez.beziers().empty() || bez.beziers().back().empty() ) { bez.quadratic_to(old_p, p); } else { auto& prev = bez.beziers().back().points().back(); QPointF tan_out = prev.pos - prev.relative_tan_in(); prev.type = math::bezier::Symmetrical; bez.quadratic_to(tan_out, p); } implicit = 'T'; } void parse_t() { if ( la_type() != Parameter ) { next_token(); return; } QPointF old_p = p; p += read_vector(); if ( bez.beziers().empty() || bez.beziers().back().empty() ) { bez.quadratic_to(old_p, p); } else { auto& prev = bez.beziers().back().points().back(); QPointF tan_out = prev.pos - prev.relative_tan_in(); prev.type = math::bezier::Symmetrical; bez.quadratic_to(tan_out, p); } implicit = 't'; } void do_arc(qreal rx, qreal ry, qreal xrot, bool large, bool sweep, const QPointF& dest) { if ( p == dest ) return; // straight line if ( rx == 0 || ry == 0 ) { p = dest; bez.line_to(p); return; } if ( bez.beziers().empty() || bez.beziers().back().empty() ) return; math::bezier::Bezier points = math::EllipseSolver::from_svg_arc( p, rx, ry, xrot, large, sweep, dest ); auto& target_points = bez.beziers().back().points(); target_points.back().tan_out = points[0].tan_out; target_points.insert(target_points.end(), points.begin()+1, points.end()); p = dest; } void parse_A() { if ( la_type() != Parameter ) { next_token(); return; } QPointF r = read_vector(); qreal xrot = read_param(); qreal large = read_param(); qreal sweep = read_param(); QPointF dest = read_vector(); do_arc(r.x(), r.y(), xrot, large, sweep, dest); implicit = 'A'; } void parse_a() { if ( la_type() != Parameter ) { next_token(); return; } QPointF r = read_vector(); qreal xrot = read_param(); qreal large = read_param(); qreal sweep = read_param(); QPointF dest = p + read_vector(); do_arc(r.x(), r.y(), xrot, large, sweep, dest); implicit = 'a'; } void parse_command(ushort c) { switch ( c ) { case 'M': return parse_M(); case 'm': return parse_m(); case 'L': return parse_L(); case 'l': return parse_l(); case 'H': return parse_H(); case 'h': return parse_h(); case 'V': return parse_V(); case 'v': return parse_v(); case 'C': return parse_C(); case 'c': return parse_c(); case 'S': return parse_S(); case 's': return parse_s(); case 'Q': return parse_Q(); case 'q': return parse_q(); case 'T': return parse_T(); case 't': return parse_t(); case 'A': return parse_A(); case 'a': return parse_a(); case 'Z': case 'z': bez.close(); if ( !bez.empty() && !bez.back().empty() ) p = bez.back()[0].pos; break; default: next_token(); } } std::vector tokens; int index = 0; ushort implicit = 'M'; QPointF p{0, 0}; math::bezier::MultiBezier bez; }; } // namespace glaxnimate::io::svg::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/bezier_length.cpp000664 001750 001750 00000007527 15165022620 034176 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/bezier/bezier_length.hpp" glaxnimate::math::bezier::LengthData::LengthData(const Solver& segment, int steps) { if ( steps == 0 ) return; children_.reserve(steps); QPointF p = segment.points()[0]; for ( int i = 1; i <= steps; i++ ) { qreal t = qreal(i) / steps; QPointF q = segment.solve(t); auto l = math::length(p - q); length_ += l; children_.push_back({t, l, length_}); p = q; } } glaxnimate::math::bezier::LengthData::LengthData(const Bezier& bez, int steps) { children_.reserve(bez.size()); int count = bez.segment_count(); for ( int i = 0; i < count; i++ ) { children_.emplace_back(bez.segment(i), steps); length_ += children_.back().length_; children_.back().cumulative_length_ = length_; } } glaxnimate::math::bezier::LengthData::LengthData(const MultiBezier& mbez, int steps) { children_.reserve(mbez.size()); for ( const auto& bez : mbez.beziers() ) { children_.emplace_back(bez, steps); length_ += children_.back().length_; children_.back().cumulative_length_ = length_; } } glaxnimate::math::bezier::LengthData::SplitInfo glaxnimate::math::bezier::LengthData::at_ratio(qreal ratio) const { return at_length(length_ * ratio); } glaxnimate::math::bezier::LengthData::SplitInfo glaxnimate::math::bezier::LengthData::at_length(qreal length) const { if ( length <= 0 ) return {0, 0., 0., &children_.front()}; if ( length >= length_ ) return { int(children_.size() - 1), 1., length - (children_.size() == 1 ? 0 : children_[children_.size() - 2].length_), &children_.back() }; qreal prev_length = 0; for ( int i = 0; i < int(children_.size()); i++ ) { const auto& child = children_[i]; if ( child.cumulative_length_ > length ) { qreal residual_length = length - prev_length; qreal ratio = qFuzzyIsNull(child.length_) ? 0 : residual_length / child.length_; if ( child.leaf_ ) ratio = math::lerp(i == 0 ? 0 : children_[i - 1].t_, children_[i].t_, ratio); return {i, ratio, residual_length, &child}; } prev_length = child.cumulative_length_; } return {int(children_.size() - 1), 1., length, &children_.back()}; } qreal glaxnimate::math::bezier::LengthData::length() const noexcept { return length_; } glaxnimate::math::bezier::LengthData::LengthData(qreal t, qreal length, qreal cumulative_length) : t_(t), length_(length), cumulative_length_(cumulative_length), leaf_(true) {} qreal glaxnimate::math::bezier::LengthData::from_ratio(qreal ratio) const { if ( ratio <= 0 ) return 0; if ( ratio >= 1 ) return length_; for ( int i = 0; i < int(children_.size()); i++ ) { if ( qFuzzyCompare(children_[i].t_, ratio) ) return children_[i].cumulative_length_; if ( children_[i].t_ >= ratio ) { if ( i == 0 ) { qreal factor = ratio * children_[i].t_; return factor * children_[i].cumulative_length_; } qreal factor = (ratio - children_[i-1].t_) * (children_[i].t_ - children_[i-1].t_); return math::lerp(children_[i-1].cumulative_length_, children_[i].cumulative_length_, factor); } } return length_; } qreal glaxnimate::math::bezier::LengthData::child_start(int index) const { if ( index == 0 ) return 0; return children_[index - 1].cumulative_length_; } qreal glaxnimate::math::bezier::LengthData::child_end(int index) const { return children_[index].cumulative_length_; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/000775 001750 001750 00000000000 15165022620 027034 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/cubic_struts.cpp000664 001750 001750 00000006106 15165022620 034056 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/math/bezier/cubic_struts.hpp" #include "glaxnimate/math/math.hpp" #include "glaxnimate/math/geom.hpp" #include "glaxnimate/math/bezier/operations.hpp" using namespace glaxnimate; // see https://pomax.github.io/bezierinfo/#abc (this returns A given B) QPointF math::bezier::get_quadratic_handle(const math::bezier::BezierSegment& segment, const QPointF& B, qreal t) { qreal t1 = (1-t); qreal t13 = t1 * t1 * t1; qreal t3 = t * t * t; qreal u = t13 / (t3 + t13); qreal ratio = math::abs((t3 + t13 - 1) / (t3 + t13)); QPointF C = math::lerp(segment[3], segment[0], u); QPointF A; if ( t == 0 ) A = segment[1]; else if ( t == 1 ) A = segment[2]; else A = B + ( B - C ) / ratio; return A; } math::bezier::BezierSegment math::bezier::cubic_segment_from_struts( const math::bezier::BezierSegment& segment, const BezierStruts& struts ) { if ( struts.t == 0 || struts.t == 1) return segment; QPointF A = get_quadratic_handle(segment, struts.B, struts.t); QPointF v1 = A + (struts.e1 - A) / (1-struts.t); QPointF v2 = A + (struts.e2 - A) / struts.t; return { segment[0], segment[0] + (v1 - segment[0]) / struts.t, segment[3] + (v2 - segment[3]) / (1-struts.t), segment[3] }; } // see https://pomax.github.io/bezierinfo/#pointcurves math::bezier::BezierStruts math::bezier::cubic_struts_idealized(const math::bezier::BezierSegment& segment, const QPointF& B) { BezierStruts struts; struts.B = B; qreal d1 = math::length(segment[0] - B); qreal d2 = math::length(segment[3] - B); struts.t = d1 / (d1+d2); QPointF center = circle_center(segment[0], B, segment[3]); qreal tanlen = math::length(segment[3] - segment[0]) / 3; qreal phi = math::fmod( math::atan2(segment[3].y() - segment[0].y(), segment[3].x() - segment[0].x()) - math::atan2(B.y() - segment[0].y(), B.x() - segment[0].x()) + math::tau, math::tau ); if ( phi < math::pi ) tanlen = -tanlen; qreal de1 = struts.t * tanlen; qreal de2 = (1-struts.t) * tanlen; QPointF tangent = struts.B - center; tangent /= math::length(tangent); tangent = { -tangent.y(), tangent.x() }; struts.e1 = struts.B + de1 * tangent; struts.e2 = struts.B - de2 * tangent; return struts; } math::bezier::BezierStruts math::bezier::cubic_struts_projection( const math::bezier::BezierSegment& segment, const QPointF& B, const math::bezier::ProjectResult& projection ) { BezierStruts struts; struts.B = B; struts.t = projection.factor; auto v1 = math::lerp(segment[0], segment[1], struts.t); auto v2 = math::lerp(segment[2], segment[3], struts.t); auto A = get_quadratic_handle(segment, projection.point, struts.t); struts.e1 = math::lerp(v1, A, struts.t) - projection.point + B; struts.e2 = math::lerp(A, v2, struts.t) - projection.point + B; return struts; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/iterator.hpp000664 001750 001750 00000004540 15165022620 032132 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::utils { /** * \brief CRTP that adds all the boilerplate of an iterator * wrapping a random-access iterator. * * All you need to do is overload operator->() and operator*(). */ template class RandomAccessIteratorWrapper { public: using iterator_category = std::random_access_iterator_tag; using difference_type = typename BaseIterator::difference_type; // Iterator RandomAccessIteratorWrapper& operator++() { ++iter; return *this; } // Input/Output Iterator bool operator==(const RandomAccessIteratorWrapper& o) const { return iter == o.iter; } bool operator!=(const RandomAccessIteratorWrapper& o) const { return iter != o.iter; } Iterator operator++(int) { auto copy = *this; ++iter; return copy; } // Forward Iterator RandomAccessIteratorWrapper() = default; // Bidirectional Iterator Iterator& operator--() { --iter; return *cast_derived(); } Iterator operator--(int) { auto copy = *this; --iter; return copy; } // Random Access Iterator Iterator& operator+= (difference_type i) { iter += i; return *cast_derived(); } Iterator operator+ (difference_type i) const { return iter + i; } friend Iterator operator+ (difference_type i, const Iterator& iter) { return iter.iter + i; } Iterator& operator-= (difference_type i) { iter -= i; return *cast_derived(); } Iterator operator- (difference_type i) const { return iter - i; } Iterator operator- (const Iterator& o) const { return iter - o.iter; } bool operator<(const Iterator& o) const { return iter < o.iter; } bool operator<=(const Iterator& o) const { return iter <= o.iter; } bool operator>(const Iterator& o) const { return iter > o.iter; } bool operator>=(const Iterator& o) const { return iter >= o.iter; } protected: using InternalIterator = BaseIterator; using Parent = RandomAccessIteratorWrapper; RandomAccessIteratorWrapper(BaseIterator iter) : iter(std::move(iter)) {} BaseIterator iter; private: Iterator* cast_derived() { return static_cast(this); } const Iterator* cast_derived() const { return static_cast(this); } }; } // namespace glaxnimate::utils src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/option_list_property.cpp000664 001750 001750 00000000275 15165022620 036351 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/property/option_list_property.hpp" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/000775 001750 001750 00000000000 15165022620 027633 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/zig_zag.cpp000664 001750 001750 00000012231 15165022620 035126 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/modifiers/zig_zag.hpp" #include "glaxnimate/math/geom.hpp" #include "glaxnimate/math/vector.hpp" #include "glaxnimate/math/bezier/bezier_length.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::ZigZag) using namespace glaxnimate; using namespace glaxnimate::math::bezier; using BezierSolver = glaxnimate::math::bezier::CubicBezierSolver; static double angle_mean(double a, double b) { if ( math::abs(a-b) > math::pi ) return (a + b) / 2 + math::pi; return (a + b) / 2; } static void zig_zag_corner(Bezier& output_bezier, const BezierSolver* segment_before, const BezierSolver* segment_after, float amplitude, int direction, float tangent_length) { QPointF point; double angle; double tan_angle; // We use 0.01 and 0.99 instead of 0 and 1 because they yield better results if ( !segment_before ) { point = segment_after->points()[0]; angle = segment_after->normal_angle(0.01); tan_angle = segment_after->tangent_angle(0.01); } else if ( !segment_after ) { point = segment_before->points()[3]; angle = segment_before->normal_angle(0.99); tan_angle = segment_before->tangent_angle(0.99); } else { point = segment_after->points()[0]; angle = -angle_mean(segment_after->normal_angle(0.01), segment_before->normal_angle(0.99)); tan_angle = angle_mean(segment_after->tangent_angle(0.01), segment_before->tangent_angle(0.99)); } output_bezier.add_point(point + math::from_polar(direction * amplitude, angle)); auto& vertex = output_bezier.back(); // It's ok to float-compare as it's a value we set explicitly to 0 if ( tangent_length != 0 ) { vertex.tan_in = vertex.pos + math::from_polar(-tangent_length, tan_angle); vertex.tan_out = vertex.pos + math::from_polar(tangent_length, tan_angle); } } static int zig_zag_segment(Bezier& output_bezier,const BezierSolver& segment, const LengthData& seg_len, float amplitude, int frequency, int direction, float tangent_length) { for ( int i = 0; i < frequency; i++ ) { auto f = (i + 1.) / (frequency + 1.); auto t = seg_len.at_ratio(f).ratio; auto angle = segment.normal_angle(t); auto point = segment.solve(t); output_bezier.add_point(point + math::from_polar(direction * amplitude, -angle)); auto& vertex = output_bezier.back(); // It's ok to float-compare as it's a value we set explicitly to 0 if ( tangent_length != 0 ) { auto tan_angle = segment.tangent_angle(t); vertex.tan_in = vertex.pos + math::from_polar(-tangent_length, tan_angle); vertex.tan_out = vertex.pos + math::from_polar(tangent_length, tan_angle); } direction = -direction; } return direction; } static Bezier zig_zag_bezier(const Bezier& input_bezier, float amplitude, int frequency, model::ZigZag::Style style) { Bezier output_bezier; output_bezier.set_closed(input_bezier.closed()); auto count = input_bezier.segment_count(); if ( count == 0 ) return output_bezier; auto direction = -1; BezierSolver segment = input_bezier.segment(count - 1); BezierSolver next_segment = input_bezier.segment(0); LengthData seg_len(next_segment, 20); auto tangent_length = style == model::ZigZag::Wave ? seg_len.length() / (frequency + 1.) / 2. : 0; zig_zag_corner(output_bezier, input_bezier.closed() ? &segment : nullptr, &next_segment, amplitude, direction, tangent_length); for ( auto i = 0; i < count; i++ ) { segment = next_segment; direction = zig_zag_segment(output_bezier, segment, seg_len, amplitude, frequency, -direction, tangent_length); if ( i == count - 1 && !input_bezier.closed() ) { zig_zag_corner(output_bezier, &segment, nullptr, amplitude, direction, tangent_length); } else { next_segment = input_bezier.segment((i + 1) % count); seg_len = LengthData (next_segment, 20); zig_zag_corner(output_bezier, &segment, &next_segment, amplitude, direction, tangent_length); } } return output_bezier; } QIcon glaxnimate::model::ZigZag::static_tree_icon() { return QIcon::fromTheme("path-simplify"); } QString glaxnimate::model::ZigZag::static_type_name_human() { return i18n("Zig Zag"); } bool glaxnimate::model::ZigZag::process_collected() const { return false; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::ZigZag::process( glaxnimate::model::FrameTime t, const math::bezier::MultiBezier& mbez ) const { if ( mbez.empty() ) return {}; int frequency = math::max(0, qRound(this->frequency.get_at(t))); auto amplitude = this->amplitude.get_at(t); auto point_type = this->style.get(); MultiBezier out; for ( const auto& inbez : mbez.beziers() ) out.beziers().push_back(zig_zag_bezier(inbez, amplitude, frequency, point_type)); return out; } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/type_def.hpp000664 001750 001750 00000002057 15165022620 035633 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include "glaxnimate/module/extraformats/rive/type_ids.hpp" #include "glaxnimate/io/binary_types.hpp" namespace glaxnimate::io::rive { using Identifier = VarUint; enum class PropertyType { VarUint = 0, // LEB128 Uint Bool = 1, // Byte String = 2, // length(LEB128 Uint) + Utf8 Bytes = 3, // length(LEB128 Uint) + Data Float = 4, // Float32 Color = 5, // Uint32 }; using PropertyTable = std::unordered_map; struct Property { QString name; Identifier id = 0; PropertyType type = PropertyType::VarUint; }; struct ObjectDefinition { QString name; TypeId type_id = TypeId::NoType; TypeId extends = TypeId::NoType; std::vector properties; }; extern std::unordered_map defined_objects; } // namespace glaxnimate::io::rive mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/000775 001750 001750 00000000000 15165022620 030334 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/application_info_generated.hpp000664 001750 001750 00000000506 15164340462 032350 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #define PROJECT_VERSION "7.37.0" #define PROJECT_SLUG "" #define PROJECT_NAME "MLT" #define URL_DOCS "" #define URL_ISSUES "" #define URL_DONATE "" #define PROJECT_DESCRIPTION "Multimedia Framework" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/000775 001750 001750 00000000000 15165022620 027565 5ustar00ddennedyddennedy000000 000000 src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/reference_property.cpp000664 001750 001750 00000000657 15165022620 035750 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/property/reference_property.hpp" #include "glaxnimate/model/document.hpp" void glaxnimate::model::ReferencePropertyBase::transfer(model::Document* doc) { auto ref = get_ref(); if ( ref && !is_valid_option(ref) ) set_ref(doc->find_by_uuid(ref->uuid.get())); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/000775 001750 001750 00000000000 15165022620 031165 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/CMakeLists.txt000664 001750 001750 00000001124 15165022620 035167 0ustar00ddennedyddennedy000000 000000 # SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia # SPDX-License-Identifier: BSD-2-Clause set(SOURCES extraformats_module.cpp avd/avd_parser.cpp avd/avd_format.cpp avd/avd_renderer.cpp aep/aep_format.cpp aep/aep_loader.cpp aep/string_decoder.cpp aep/gradient_xml.cpp rive/rive_format.cpp rive/rive_html_format.cpp rive/rive_loader.cpp rive/rive_serializer.cpp rive/type_def.cpp rive/type_system.cpp ) add_library(GlaxnimateExtraFormats OBJECT ${SOURCES}) glaxnimate_enable_exceptions(GlaxnimateExtraFormats PUBLIC) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/settings/000775 001750 001750 00000000000 15165022620 030265 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/style/fill.cpp000664 001750 001750 00000001513 15165022620 033602 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/style/fill.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Fill) void glaxnimate::model::Fill::on_paint(renderer::Renderer* p, glaxnimate::model::FrameTime t, glaxnimate::model::VisualNode::PaintMode, glaxnimate::model::Modifier* modifier) const { p->set_fill({brush(t), opacity.get_at(t), Qt::FillRule(fill_rule.get())}); math::bezier::MultiBezier bez; if ( modifier ) bez = modifier->collect_shapes_from(affected(), t, {}); else bez = collect_shapes(t, {}); p->draw_path(bez); } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Fill::to_painter_path_impl(glaxnimate::model::FrameTime t) const { return collect_shapes(t, {}); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/CMakeLists.txt000664 001750 001750 00000016071 15165022620 027041 0ustar00ddennedyddennedy000000 000000 # SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia # SPDX-License-Identifier: BSD-2-Clause set(SOURCES glaxnimate/app_info.cpp glaxnimate/command/structure_commands.cpp glaxnimate/command/shape_commands.cpp glaxnimate/command/animation_commands.cpp glaxnimate/command/clipboard.cpp glaxnimate/io/base.cpp glaxnimate/io/binary_stream.cpp glaxnimate/io/utils.cpp glaxnimate/io/glaxnimate/glaxnimate_format.cpp glaxnimate/io/glaxnimate/glaxnimate_importer.cpp glaxnimate/io/glaxnimate/glaxnimate_mime.cpp glaxnimate/io/glaxnimate/glaxnimate_html_format.cpp glaxnimate/io/lottie/cbor_write_json.cpp glaxnimate/io/lottie/lottie_format.cpp glaxnimate/io/lottie/lottie_html_format.cpp glaxnimate/io/lottie/validation.cpp glaxnimate/io/mime/mime_serializer.cpp glaxnimate/io/raster/raster_format.cpp glaxnimate/io/raster/spritesheet_format.cpp glaxnimate/io/svg/detail.cpp glaxnimate/io/svg/svg_format.cpp glaxnimate/io/svg/svg_parser.cpp glaxnimate/io/svg/svg_renderer.cpp glaxnimate/log/logger.cpp glaxnimate/math/geom.cpp glaxnimate/math/polynomial.cpp glaxnimate/math/ellipse_solver.cpp glaxnimate/math/bezier/bezier.cpp glaxnimate/math/bezier/point.cpp glaxnimate/math/bezier/operations.cpp glaxnimate/math/bezier/cubic_struts.cpp glaxnimate/math/bezier/meta.cpp glaxnimate/math/bezier/bezier_length.cpp glaxnimate/model/document.cpp glaxnimate/model/document_node.cpp glaxnimate/model/object.cpp glaxnimate/model/transform.cpp glaxnimate/model/factory.cpp glaxnimate/model/animation_container.cpp glaxnimate/model/stretchable_time.cpp glaxnimate/model/comp_graph.cpp glaxnimate/model/mask_settings.cpp glaxnimate/model/visitor.cpp glaxnimate/model/custom_font.cpp glaxnimate/model/animation/keyframe_transition.cpp glaxnimate/model/animation/keyframe_base.cpp glaxnimate/model/animation/animatable.cpp glaxnimate/model/animation/animatable_path.cpp glaxnimate/model/animation/animatable_base.cpp glaxnimate/model/animation/meta_animatable.cpp glaxnimate/model/property/property.cpp glaxnimate/model/property/reference_property.cpp glaxnimate/model/property/option_list_property.cpp glaxnimate/model/assets/assets.cpp glaxnimate/model/assets/brush_style.cpp glaxnimate/model/assets/named_color.cpp glaxnimate/model/assets/bitmap.cpp glaxnimate/model/assets/gradient.cpp glaxnimate/model/assets/asset_base.cpp glaxnimate/model/assets/asset.cpp glaxnimate/model/assets/composition.cpp glaxnimate/model/assets/embedded_font.cpp glaxnimate/model/assets/network_downloader.cpp glaxnimate/model/shapes/shape.cpp glaxnimate/model/shapes/style/fill.cpp glaxnimate/model/shapes/style/stroke.cpp glaxnimate/model/shapes/style/styler.cpp glaxnimate/model/shapes/shapes/rect.cpp glaxnimate/model/shapes/shapes/ellipse.cpp glaxnimate/model/shapes/shapes/path.cpp glaxnimate/model/shapes/shapes/polystar.cpp glaxnimate/model/shapes/shapes/text.cpp glaxnimate/model/shapes/composable/group.cpp glaxnimate/model/shapes/composable/layer.cpp glaxnimate/model/shapes/composable/image.cpp glaxnimate/model/shapes/composable/precomp_layer.cpp glaxnimate/model/shapes/composable/composable.cpp glaxnimate/model/shapes/modifiers/repeater.cpp glaxnimate/model/shapes/modifiers/trim.cpp glaxnimate/model/shapes/modifiers/inflate_deflate.cpp glaxnimate/model/shapes/modifiers/path_modifier.cpp glaxnimate/model/shapes/modifiers/round_corners.cpp glaxnimate/model/shapes/modifiers/offset_path.cpp glaxnimate/model/shapes/modifiers/zig_zag.cpp glaxnimate/renderer/renderer.cpp glaxnimate/settings/settings_group.cpp glaxnimate/utils/data_paths.cpp glaxnimate/module/module.cpp ) # Qt find_package(Qt6 COMPONENTS Core Gui Xml Network REQUIRED) set(CMAKE_AUTOMOC ON) set(CORE_DEPS Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Gui # Needed for QColor / QUndoCommand Qt${QT_MAJOR_VERSION}::Xml Qt${QT_MAJOR_VERSION}::Network ) # Basic target setup add_library(GlaxnimateCore STATIC) add_library(Glaxnimate::Core ALIAS GlaxnimateCore) add_library(GlaxnimateCoreBase OBJECT) target_link_libraries(GlaxnimateCore PUBLIC GlaxnimateCoreBase) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/glaxnimate/application_info_generated.in.hpp ${CMAKE_CURRENT_BINARY_DIR}/glaxnimate/application_info_generated.hpp ) target_include_directories(GlaxnimateCoreBase PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(GlaxnimateCoreBase PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(GlaxnimateCoreBase PUBLIC $) target_sources(GlaxnimateCoreBase PRIVATE ${SOURCES}) # Qt - cont'd target_link_libraries(GlaxnimateCoreBase PUBLIC ${CORE_DEPS}) set_property(TARGET GlaxnimateCoreBase APPEND PROPERTY AUTOMOC_MACRO_NAMES "GLAXNIMATE_OBJECT") # (Optional) KDE stuff set(GLAXNIMATE_CORE_KDE ${KF${KF_MAJOR}_FOUND}) function(glaxnimate_enable_exceptions TARGET) if ( COMMAND KDE_TARGET_ENABLE_EXCEPTIONS ) kde_target_enable_exceptions(${TARGET} ${ARGN}) endif() endfunction() if ( GLAXNIMATE_CORE_KDE ) target_compile_definitions(GlaxnimateCoreBase PUBLIC -DGLAXNIMATE_CORE_KDE) target_link_libraries(GlaxnimateCoreBase PUBLIC KF${KF_MAJOR}::I18n ) else() message(STATUS "Building Glaxnimate core without KDE") endif() glaxnimate_enable_exceptions(GlaxnimateCoreBase PUBLIC) # Modules set(GLAXNIMATE_MODULES_) macro(glaxnimate_module MODULE DESCRIPTION DEFAULT) string(TOUPPER ${MODULE} MODULE_UPPER) string(TOLOWER ${MODULE} MODULE_LOWER) option(GLAXNIMATE_${MODULE_UPPER}_ENABLED ${DESCRIPTION} ${DEFAULT}) if ( GLAXNIMATE_${MODULE_UPPER}_ENABLED ) add_subdirectory(glaxnimate/module/${MODULE_LOWER}) target_link_libraries(Glaxnimate${MODULE} PUBLIC GlaxnimateCoreBase ) list(APPEND GLAXNIMATE_MODULES_ ${MODULE}) endif() endmacro() if ( NOT DEFINED EXTRA_DEFAULT ) set(EXTRA_DEFAULT OFF) endif() glaxnimate_module("ThorVG" "Default renderer" ON) glaxnimate_module("Gzip" "Support for gzip-compressed formats" ON) glaxnimate_module("ExtraFormats" "Experimental file format support" ON) glaxnimate_module("Video" "Support for video format I/O" ${EXTRA_DEFAULT}) glaxnimate_module("Cairo" "Support for format I/O using cairo" OFF) # Process modules and generate auto-registration file for the enabled modules set(GLAXNIMATE_MODULES ${GLAXNIMATE_MODULES_} PARENT_SCOPE) message(STATUS "Glaxnimate Modules Enabled:") set(GLAXNIMATE_MODULES_INCLUDES) set(GLAXNIMATE_MODULES_REGISTER) foreach(MODULE ${GLAXNIMATE_MODULES_}) message(STATUS " ${MODULE}") string(TOLOWER ${MODULE} MODULE_LOWER) string(CONCAT GLAXNIMATE_MODULES_INCLUDES ${GLAXNIMATE_MODULES_INCLUDES} "#include \"glaxnimate/module/${MODULE_LOWER}/${MODULE_LOWER}_module.hpp\"\n") string(CONCAT GLAXNIMATE_MODULES_REGISTER "${GLAXNIMATE_MODULES_REGISTER}registry.install();\n") target_link_libraries(GlaxnimateCore PUBLIC Glaxnimate${MODULE}) endforeach() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/glaxnimate/module/module_autoregister.in.cpp ${CMAKE_CURRENT_BINARY_DIR}/module_autoregister.cpp ) target_sources(GlaxnimateCore PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/module_autoregister.cpp) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/maybe_ptr.hpp000664 001750 001750 00000003012 15165022620 032254 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::utils { /** * \brief Smart pointer that may or may not own the underlaying object */ template class maybe_ptr { private: T* data; bool owns; void delete_owned() noexcept { if ( owns ) { delete data; owns = false; } } void clear() noexcept { data = nullptr; owns = false; } public: using pointer = T*; maybe_ptr(T* data, bool owns) noexcept : data(data), owns(owns) {} maybe_ptr() noexcept : data(nullptr), owns(false) {} maybe_ptr(const maybe_ptr&) = delete; maybe_ptr(maybe_ptr&& o) noexcept : data(o.data), owns(o.owns) { if ( o.owns ) o.clear(); } maybe_ptr& operator=(const maybe_ptr&) = delete; maybe_ptr& operator=(maybe_ptr&& o) noexcept { std::swap(o.data, data); std::swap(o.owns, owns); return *this; } ~maybe_ptr() noexcept { delete_owned(); } void reset() { delete_owned(); data = nullptr; } void reset(T* data, bool owns) { delete_owned(); this->data = data; this->owns = owns; } bool owns_pointer() const noexcept { return owns; } T* operator->() const noexcept { return data; } explicit operator bool() const noexcept { return data; } }; } // glaxnimate::utils mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/bitmap.hpp000664 001750 001750 00000003755 15165022620 033026 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include "glaxnimate/model/assets/asset.hpp" namespace glaxnimate::model { class Bitmap : public Asset { GLAXNIMATE_OBJECT(Bitmap) GLAXNIMATE_PROPERTY(QByteArray, data, {}, &Bitmap::on_refresh) GLAXNIMATE_PROPERTY(QString, filename, {}, &Bitmap::on_refresh) GLAXNIMATE_PROPERTY(QString, url, {}, &Bitmap::on_refresh) GLAXNIMATE_PROPERTY_RO(QString, format, {}) GLAXNIMATE_PROPERTY_RO(int, width, -1) GLAXNIMATE_PROPERTY_RO(int, height, -1) Q_PROPERTY(bool embedded READ embedded WRITE embed) Q_PROPERTY(QImage image READ get_image) public: using Asset::Asset; void paint(renderer::Renderer* painter) const; bool embedded() const; QIcon instance_icon() const override; QString type_name_human() const override { return i18n("Bitmap"); } bool from_url(const QUrl& url); bool from_file(const QString& file); bool from_base64(const QString& data); bool from_raw_data(const QByteArray& data); QUrl to_url() const; QString object_name() const override; QFileInfo file_info() const; QPixmap pixmap() const { return QPixmap::fromImage(image); } void set_pixmap(const QImage& qimage, const QString& format); bool remove_if_unused(bool clean_lists) override; const QImage& get_image() const { return image; } /** * \brief If `embedded()` returns `data`, otherwise tries to load the data based on filename */ QByteArray image_data() const; QSize size() const; public Q_SLOTS: void refresh(bool rebuild_embedded); void embed(bool embedded); private: QByteArray build_embedded(const QImage& img) const; private Q_SLOTS: void on_refresh(); Q_SIGNALS: void loaded(); private: QImage image; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/000775 001750 001750 00000000000 15165022620 031010 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/animation_commands.hpp000664 001750 001750 00000016710 15165022620 034421 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/command/base.hpp" #include "glaxnimate/model/animation/animatable.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/object.hpp" namespace glaxnimate::command { class SetKeyframe : public MergeableCommand { public: SetKeyframe( model::AnimatedPropertyBase* prop, model::FrameTime time, const QVariant& value, bool commit, QUndoCommand* parent ); static QString command_name(model::AnimatableBase* prop, model::FrameTime time); void undo() override; void redo() override; bool merge_with(const SetKeyframe& other); private: model::AnimatedPropertyBase* prop; model::FrameTime time; QVariant before; QVariant after; bool had_before; bool calculated = false; model::KeyframeTransition trans_before; model::KeyframeTransition left; model::KeyframeTransition right; bool adjust_transition = false; }; class RemoveKeyframeTime : public QUndoCommand { public: RemoveKeyframeTime( model::AnimatedPropertyBase* prop, model::FrameTime time, QUndoCommand* parent ); static QString command_name(model::AnimatableBase* prop, model::FrameTime time); void undo() override; void redo() override; private: model::AnimatedPropertyBase* prop; model::FrameTime time; QVariant before; model::KeyframeTransition prev_transition_before; model::KeyframeTransition prev_transition_after; model::KeyframeTransition transition_before; }; class RemoveAllKeyframes : public QUndoCommand { public: RemoveAllKeyframes(model::AnimatedPropertyBase* prop, QVariant value, QUndoCommand* parent); static QString command_name(model::AnimatableBase* prop); void undo() override; void redo() override; private: struct Keframe { model::FrameTime time; QVariant value; model::KeyframeTransition transition; }; model::AnimatedPropertyBase* prop; std::vector keyframes; QVariant before; QVariant after; }; /** * \brief Command that sets multiple animated properties at once, * setting keyframes based on the document record_to_keyframe */ class SetMultipleAnimated : public MergeableCommand { public: SetMultipleAnimated(model::AnimatedPropertyBase* prop, QVariant after, bool commit); template SetMultipleAnimated( const QString& name, bool commit, const std::vector& props, Args... vals ) : SetMultipleAnimated( name, std::vector(props.begin(), props.end()), {}, {QVariant::fromValue(vals)...}, commit, props.empty() ? 0 : props[0]->time(), props.empty() ? false : props[0]->object()->document()->record_to_keyframe() ) {} /** * \pre props.size() == after.size() && (props.size() == before.size() || before.empty()) * * If before.empty() it will be populated by the properties */ SetMultipleAnimated( const QString& name, const std::vector& props, const QVariantList& before, const QVariantList& after, bool commit, model::FrameTime time, bool record_to_keyframe ); SetMultipleAnimated(const QString& name, bool commit); void push_property(model::AnimatedPropertyBase* prop, const QVariant& after); void push_property_not_animated(model::BaseProperty* prop, const QVariant& after); void undo() override; void redo() override; bool merge_with(const SetMultipleAnimated& other); const std::vector& properties() const { return props; } bool empty() const; private: static QString auto_name(model::AnimatedPropertyBase* prop); std::vector props; QVariantList before; QVariantList after; std::vector keyframe_before; bool keyframe_after_global; std::vector keyframe_after; model::FrameTime time; std::vector add_0; std::vector props_not_animated; }; class SetKeyframeTransition: public QUndoCommand { public: SetKeyframeTransition( model::AnimatedPropertyBase* prop, model::FrameTime time, const model::KeyframeTransition& transition, QUndoCommand* parent ); void undo() override; void redo() override; static model::KeyframeTransition transition_side( model::AnimatedPropertyBase* prop, model::FrameTime time, model::KeyframeTransition::Descriptive desc, const QPointF& point, bool before_transition ); private: model::AnimatedPropertyBase* prop; model::FrameTime time; model::KeyframeTransition undo_value; model::KeyframeTransition redo_value; }; class MoveKeyframe : public QUndoCommand { public: MoveKeyframe( model::AnimatedPropertyBase* prop, model::FrameTime time_before, model::FrameTime time_after, QUndoCommand* parent ); void undo() override; void redo() override; private: model::AnimatedPropertyBase* prop; model::FrameTime time_before; model::FrameTime time_after; }; class StretchTimeCommand: public QUndoCommand { public: /** * \pre multiplier > 0 */ StretchTimeCommand(model::Object* target, qreal multiplier) : QUndoCommand(i18n("Stretch Time")), target(target), multiplier(multiplier) {} void undo() override { target->stretch_time(1/multiplier); target->set_time(target->document()->current_time()); } void redo() override { target->stretch_time(multiplier); target->set_time(target->document()->current_time()); } private: model::Object* target; qreal multiplier; }; /** * \brief Command that sets the path of an animated position */ class SetPositionBezier : public MergeableCommand { public: SetPositionBezier(model::detail::AnimatedPropertyPosition* prop, math::bezier::Bezier after, bool commit, const QString& name = ""); SetPositionBezier(model::detail::AnimatedPropertyPosition* prop, math::bezier::Bezier before, math::bezier::Bezier after, bool commit, const QString& name = ""); void undo() override; void redo() override; bool merge_with(const SetPositionBezier& other); private: model::detail::AnimatedPropertyPosition* property; math::bezier::Bezier before; math::bezier::Bezier after; }; /** * \brief Undo command whose children are done and undone in custom order */ class ReorderedUndoCommand : public QUndoCommand { public: using QUndoCommand::QUndoCommand; void add_command(std::unique_ptr cmd, int order_redo, int order_undo) { undo_map[order_undo] = cmd.get(); redo_map[order_redo] = std::move(cmd); } void undo() override { for ( const auto& p : undo_map ) p.second->undo(); } void redo() override { for ( const auto& p : redo_map ) p.second->redo(); } private: std::map> redo_map; std::map undo_map; }; } // namespace glaxnimate::command mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/visitor.hpp000664 001750 001750 00000001577 15165022620 031747 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::model { class Document; class Composition; class DocumentNode; class Visitor { public: virtual ~Visitor() {} void visit(model::Document* doc, model::Composition* main, bool skip_locked = false); void visit(model::DocumentNode* node, bool skip_locked = false); private: virtual void on_visit(model::DocumentNode* node) = 0; virtual void on_visit_end(model::DocumentNode* node) { Q_UNUSED(node) } virtual void on_visit_document(model::Document* document, model::Composition* main) { Q_UNUSED(document) Q_UNUSED(main) } virtual void on_visit_document_end(model::Document* document, model::Composition* main) { Q_UNUSED(document) Q_UNUSED(main) } }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/extraformats_module.cpp000664 001750 001750 00000001137 15165022620 037144 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "extraformats_module.hpp" #include "aep/aep_format.hpp" #include "rive/rive_format.hpp" #include "avd/avd_format.hpp" using namespace glaxnimate::io; std::vector glaxnimate::extraformats::Module::components() const { return { }; } void glaxnimate::extraformats::Module::initialize() { register_io_classes< aep::AepFormat, aep::AepxFormat, avd::AvdFormat, rive::RiveFormat >(); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/i18n.hpp000664 001750 001750 00000002604 15165022620 031057 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #ifdef GLAXNIMATE_CORE_KDE #include namespace glaxnimate::utils { using LazyLocalizedString = KLazyLocalizedString; } // namespace glaxnimate::utils #else #include namespace { inline const QString& arg_spread(const QString& str) { return str; } template QString arg_spread(const QString& str, Head&& head, Args&&... args) { return arg_spread(str.arg(std::forward(head)), std::forward(args)...); } } // namespace template QString i18n(Initializer&& init, Args&&... args) { return arg_spread(QString(std::forward(init)), std::forward(args)...); } template QString i18np(Init1&& /*singular*/, Init2&& plural, int /*count*/, Args&&... args) { return arg_spread(QString(std::forward(plural)), std::forward(args)...); } #define kli18n(x) x namespace glaxnimate::utils { class LazyLocalizedString { const char * text; public: constexpr LazyLocalizedString(const char* text = "") : text(text) {} constexpr inline const char *untranslatedText() const { return text; } QString toString() const { return text; } }; } // namespace glaxnimate::utils #endif src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_format.cpp000664 001750 001750 00000012572 15165022620 036347 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/rive/rive_format.hpp" #include #include #include "glaxnimate/module/extraformats/rive/rive_loader.hpp" #include "glaxnimate/module/extraformats/rive/rive_exporter.hpp" bool glaxnimate::io::rive::RiveFormat::on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&) { BinaryInputStream stream(&file); if ( stream.read(4) != "RIVE" ) { error(i18n("Unsupported format")); return false; } auto vmaj = stream.read_uint_leb128(); auto vmin = stream.read_uint_leb128(); stream.read_uint_leb128(); // file id if ( stream.has_error() ) { error(i18n("Could not read header")); return false; } if ( vmaj != RiveFormat::format_version ) { error(i18n("Loading unsupported rive file version %1.%2, the only supported version is %3", vmaj, vmin, 7)); return false; } if ( stream.has_error() ) { error(i18n("Could not read property table")); return false; } return RiveLoader(stream, this).load_document(document); } bool glaxnimate::io::rive::RiveFormat::on_save(QIODevice& device, const QString&, model::Composition* comp, const QVariantMap&) { RiveExporter exporter(&device, this); exporter.write_document(comp->document()); return true; } static QString property_type_to_string(glaxnimate::io::rive::PropertyType type) { switch ( type ) { case glaxnimate::io::rive::PropertyType::VarUint: return "VarUint"; case glaxnimate::io::rive::PropertyType::Bool: return "bool"; case glaxnimate::io::rive::PropertyType::String: return "string"; case glaxnimate::io::rive::PropertyType::Bytes: return "bytes"; case glaxnimate::io::rive::PropertyType::Float: return "float"; case glaxnimate::io::rive::PropertyType::Color: return "color"; } return "?"; } QJsonDocument glaxnimate::io::rive::RiveFormat::to_json(const QByteArray& binary_data) { BinaryInputStream stream(binary_data); if ( stream.read(4) != "RIVE" ) return {}; auto vmaj = stream.read_uint_leb128(); auto vmin = stream.read_uint_leb128(); auto file_id = stream.read_uint_leb128(); if ( stream.has_error() || vmaj != RiveFormat::format_version ) return {}; RiveLoader loader(stream, this); QJsonArray summary; QJsonArray objects; int id = 0; bool has_artboard = false; for ( const auto& rive_obj : loader.load_object_list() ) { if ( !rive_obj ) { summary.push_back("Invalid"); objects.push_back("Invalid"); continue; } if ( rive_obj.type().id == TypeId::Artboard ) { has_artboard = true; id = 0; } QJsonObject summary_obj; QJsonObject obj; QJsonArray types; for ( const auto& def : rive_obj.type().definitions ) { QJsonObject jdef; jdef["id"] = int(def->type_id); jdef["name"] = def->name; types.push_back(jdef); } obj["class"] = types; QJsonArray props; for ( const auto& p : rive_obj.type().properties ) { QJsonObject prop; prop["id"] = int(p->id); prop["name"] = p->name; prop["type"] = property_type_to_string(p->type); auto iter = rive_obj.properties().find(p); QJsonValue val; if ( iter != rive_obj.properties().end() && iter->second.isValid() ) { if ( iter->second.userType() == QMetaType::QColor ) val = iter->second.value().name(); else if ( iter->second.userType() == QMetaType::ULongLong || iter->second.userType() == QMetaType::ULong ) val = iter->second.toInt(); else if ( iter->second.userType() == QMetaType::QByteArray ) val = QString::fromLatin1(iter->second.toByteArray().toBase64()); else val = QJsonValue::fromVariant(iter->second); summary_obj[iter->first->name] = val; } prop["value"] = val; props.push_back(prop); } obj["properties"] = props; QJsonObject summary_obj_parent; summary_obj_parent[!rive_obj ? "?" : rive_obj.definition()->name] = summary_obj; if ( has_artboard ) { summary_obj_parent["-id"] = id; obj["object_id"] = id; id++; } objects.push_back(obj); summary.push_back(summary_obj_parent); } QJsonObject header; QJsonArray version; version.push_back(int(vmaj)); version.push_back(int(vmin)); header["version"] = version; header["file_id"] = int(file_id); QJsonArray extra_props; for ( const auto& p : loader.extra_properties() ) { QJsonObject prop; prop["id"] = int(p.first); prop["type"] = property_type_to_string(p.second); } header["toc"] = extra_props; QJsonObject root; root["brief"] = summary; root["detail"] = objects; root["header"] = header; return QJsonDocument(root); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/mask_settings.hpp000664 001750 001750 00000002221 15165022620 033106 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/object.hpp" #include "glaxnimate/model/property/reference_property.hpp" #include "glaxnimate/model/animation/frame_time.hpp" #include "glaxnimate/model/shapes/shape.hpp" namespace glaxnimate::model { class MaskSettings : public Object { GLAXNIMATE_OBJECT(MaskSettings) public: enum MaskMode { NoMask = 0, Alpha = renderer::MaskSourceAlpha, Luma = renderer::MaskSourceLuma, }; Q_ENUM(MaskMode) GLAXNIMATE_PROPERTY(MaskMode, mask, NoMask, &MaskSettings::mask_changed, {}, PropertyTraits::Visual) GLAXNIMATE_PROPERTY(bool, inverted, false, &MaskSettings::inverted_changed, {}, PropertyTraits::Visual) public: using Object::Object; QString type_name_human() const override; bool has_mask() const { return mask.get(); } /** * \brief Returns all modes in a cycle */ static MaskMode next_mode(MaskMode previous); Q_SIGNALS: void mask_changed(); void inverted_changed(); }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/keyframe_transition.cpp000664 001750 001750 00000016004 15165022620 036207 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/animation/keyframe_transition.hpp" #include "glaxnimate/math/bezier/segment.hpp" namespace { constexpr QPointF bound_vec(const QPointF& v) { return { qBound(glaxnimate::math::scalar_type(0), v.x(), glaxnimate::math::scalar_type(1)), v.y() }; } } // namespace glaxnimate::model::KeyframeTransition::Descriptive glaxnimate::model::KeyframeTransition::before_descriptive() const { if ( special_ != Special::Normal ) return Descriptive(special_); if ( qFuzzyIsNull(bezier_.points()[1].x() - bezier_.points()[1].y()) ) return Linear; if ( bezier_.points()[1].y() == 0 ) return Ease; if ( bezier_.points()[1].y() < 0 ) return Overshoot; if ( bezier_.points()[1].x() < bezier_.points()[1].y() ) return Fast; return Custom; } glaxnimate::model::KeyframeTransition::Descriptive glaxnimate::model::KeyframeTransition::after_descriptive() const { if ( special_ != Special::Normal ) return Descriptive(special_); if ( qFuzzyIsNull(bezier_.points()[2].x() - bezier_.points()[2].y()) ) return Linear; if ( bezier_.points()[2].y() == 1 ) return Ease; if ( bezier_.points()[2].y() > 1 ) return Overshoot; if ( bezier_.points()[2].x() > bezier_.points()[2].y() ) return Fast; return Custom; } void glaxnimate::model::KeyframeTransition::set_before_descriptive(model::KeyframeTransition::Descriptive d) { switch ( d ) { case NoValue: case Hold: special_ = Special(d); return; case Linear: bezier_.set<1>(QPointF{1./3., 1./3.}); special_ = Special::Normal; break; case Ease: bezier_.set<1>(QPointF{1./3., 0}); special_ = Special::Normal; break; case Fast: bezier_.set<1>(QPointF{1./6., 1./3.}); special_ = Special::Normal; break; case Overshoot: bezier_.set<1>(QPointF{2./3., -1./3.}); special_ = Special::Normal; break; case Custom: special_ = Special::Normal; break; } } void glaxnimate::model::KeyframeTransition::set_after_descriptive(model::KeyframeTransition::Descriptive d) { switch ( d ) { case NoValue: case Hold: special_ = Special(d); return; case Linear: bezier_.set<2>(QPointF{2./3., 2./3.}); special_ = Special::Normal; break; case Ease: bezier_.set<2>(QPointF{2./3., 1}); special_ = Special::Normal; break; case Fast: bezier_.set<2>(QPointF{5./6., 2./3.}); special_ = Special::Normal; break; case Overshoot: bezier_.set<2>(QPointF{1./3., 4./3.}); special_ = Special::Normal; break; case Custom: special_ = Special::Normal; break; } } void glaxnimate::model::KeyframeTransition::set_after(const QPointF& after) { bezier_.set<2>(bound_vec(after)); } void glaxnimate::model::KeyframeTransition::set_before(const QPointF& before) { bezier_.set<1>(bound_vec(before)); } void glaxnimate::model::KeyframeTransition::set_handles(const QPointF& before, const QPointF& after) { set_before(before); set_after(after); } void glaxnimate::model::KeyframeTransition::set_hold(bool hold) { special_ = hold ? Special::Hold : Special::Normal; } double glaxnimate::model::KeyframeTransition::lerp_factor(double ratio) const { if ( special_ == Special::Hold ) { if ( ratio >= 1 || qFuzzyCompare(float(ratio), 1.f) ) return 1; return 0; } if ( ratio <= 0 ) return 0; if ( ratio >= 1 ) return 1; double t = bezier_.t_at_value(ratio); return bezier_.solve_component(t, 1); } double glaxnimate::model::KeyframeTransition::bezier_parameter(double ratio) const { if ( ratio <= 0 || special_ == Special::Hold ) return 0; if ( ratio >= 1 ) return 1; return bezier_.t_at_value(ratio); } glaxnimate::model::KeyframeTransition::KeyframeTransition(const QPointF& before_handle, const QPointF& after_handle, Special special) : bezier_({0, 0}, before_handle, after_handle, {1,1}), special_(special) {} glaxnimate::model::KeyframeTransition::KeyframeTransition( glaxnimate::model::KeyframeTransition::Descriptive before, glaxnimate::model::KeyframeTransition::Descriptive after) : KeyframeTransition() { set_before_descriptive(before); set_after_descriptive(after); } glaxnimate::model::KeyframeTransition::KeyframeTransition(glaxnimate::model::KeyframeTransition::Descriptive descriptive) : KeyframeTransition(descriptive, descriptive) { } glaxnimate::model::KeyframeTransition::KeyframeTransition(Special special) : KeyframeTransition({0, 0}, {1, 1}, special) { } std::pair glaxnimate::model::KeyframeTransition::split(double x) const { return split_t(bezier_.t_at_value(x)); } std::pair glaxnimate::model::KeyframeTransition::split_t(double t) const { if ( special_ != Special::Normal ) return { {{0, 0}, {1, 1}, special_}, {{0, 0}, {1, 1}, special_} }; if ( qFuzzyIsNull(t) ) { return { {{0, 0}, {1, 1}, special_}, *this }; } else if ( qFuzzyCompare(t, 1) ) { return { *this, {{0, 0}, {1, 1}, special_} }; } qreal x = bezier_.solve_component(t, 0); qreal y = bezier_.solve_component(t, 1); math::bezier::BezierSegment left, right; std::tie(left, right) = bezier_.split(t); qreal left_factor_x = 1 / x; qreal left_factor_y = 1 / y; qreal right_factor_x = 1 / (1-x); qreal right_factor_y = 1 / (1-y); qreal right_offset_y = 0; QPointF left_p1{left[1].x() * left_factor_x, left[1].y() * left_factor_y}; QPointF left_p2{left[2].x() * left_factor_x, left[2].y() * left_factor_y}; QPointF right_p1{(right[1].x() - x) / (1-x), (right[1].y() - y) / (1-y)}; QPointF right_p2{(right[2].x() - x) / (1-x), (right[2].y() - y) / (1-y)}; if ( y < 0 ) { left_p1.setY(-left[1].y() / (y - 1)); left_p2.setY(1 - left[2].y() / (y - 1)); } else if ( y > 1 ) { right_p1.setY(-(right[1].y() - 1) / (y - 1)); right_p2.setY(1 - (right[2].y() - 1) / (y - 1)); } return { { {left[1].x() * left_factor_x, left[1].y() * left_factor_y}, {left[2].x() * left_factor_x, left[2].y() * left_factor_y} }, { {(right[1].x() - x) * right_factor_x, right_offset_y + (right[1].y() - y) * right_factor_y}, {(right[2].x() - x) * right_factor_x, right_offset_y + (right[2].y() - y) * right_factor_y} } }; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/named_color.hpp000664 001750 001750 00000001341 15165022620 034021 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/assets/brush_style.hpp" #include "glaxnimate/model/animation/animatable.hpp" namespace glaxnimate::model { class NamedColor : public BrushStyle { GLAXNIMATE_OBJECT(NamedColor) GLAXNIMATE_ANIMATABLE(QColor, color, QColor(0, 0, 0), &NamedColor::invalidate_icon) public: using BrushStyle::BrushStyle; QString type_name_human() const override; QBrush brush_style(FrameTime t) const override; bool remove_if_unused(bool clean_lists) override; protected: void fill_icon(QPixmap& icon) const override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/tgs_format.hpp000664 001750 001750 00000002072 15165022620 033542 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/lottie/lottie_format.hpp" namespace glaxnimate::io::lottie { class TgsFormat : public LottieFormat { Q_OBJECT public: QString slug() const override { return "tgs"; } QString name() const override { return i18n("Telegram Animated Sticker"); } QStringList extensions(Direction) const override { return {"tgs"}; } bool can_save() const override { return true; } bool can_open() const override { return true; } std::unique_ptr save_settings(model::Composition*) const override { return {}; } void validate(model::Document* document, model::Composition* comp); private: bool on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap&) override; bool on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&) override; }; } // namespace glaxnimate::io::lottie src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/precomp_layer.cpp000664 001750 001750 00000006762 15165022620 036435 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/model/assets/assets.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::PreCompLayer) QIcon glaxnimate::model::PreCompLayer::tree_icon() const { return QIcon::fromTheme("component"); } QString glaxnimate::model::PreCompLayer::type_name_human() const { return i18n("Composition Layer"); } glaxnimate::model::FrameTime glaxnimate::model::PreCompLayer::relative_time(glaxnimate::model::FrameTime time) const { return timing->time_to_local(time); } void glaxnimate::model::PreCompLayer::set_time(glaxnimate::model::FrameTime t) { ShapeElement::set_time(relative_time(t)); Q_EMIT document()->graphics_invalidated(); } std::vector glaxnimate::model::PreCompLayer::valid_precomps() const { auto comps = document()->comp_graph().possible_descendants(owner_composition(), document()); return std::vector(comps.begin(), comps.end()); } bool glaxnimate::model::PreCompLayer::is_valid_precomp(glaxnimate::model::DocumentNode* node) const { auto owncomp = owner_composition(); if ( auto precomp = qobject_cast(node) ) return !document()->comp_graph().is_ancestor_of(precomp, owncomp); return false; } void glaxnimate::model::PreCompLayer::on_paint(renderer::Renderer* painter, glaxnimate::model::FrameTime time, glaxnimate::model::VisualNode::PaintMode mode, glaxnimate::model::Modifier* mod) const { if ( composition.get() ) { time = timing->time_to_local(time); Composable::on_paint(painter, time, mode, mod); if ( !unbounded.get() ) painter->clip_rect(QRectF(QPointF(0, 0), size.get())); composition->paint(painter, time, mode); } } QRectF glaxnimate::model::PreCompLayer::local_bounding_rect(FrameTime) const { return QRectF(QPointF(0, 0), size.get()); } QTransform glaxnimate::model::PreCompLayer::local_transform_matrix(glaxnimate::model::FrameTime t) const { return transform.get()->transform_matrix(t); } void glaxnimate::model::PreCompLayer::add_shapes(glaxnimate::model::FrameTime, math::bezier::MultiBezier&, const QTransform&) const { } void glaxnimate::model::PreCompLayer::on_composition_changed(model::Composition* old_comp, model::Composition* new_comp) { if ( old_comp ) document()->comp_graph().remove_connection(old_comp, this); if ( new_comp ) document()->comp_graph().add_connection(new_comp, this); if ( composition.get() ) { if ( !new_comp ) composition->remove_user(&composition); else if ( !old_comp ) composition->add_user(&composition); } } glaxnimate::math::bezier::MultiBezier glaxnimate::model::PreCompLayer::to_painter_path_impl(glaxnimate::model::FrameTime time) const { glaxnimate::math::bezier::MultiBezier p; if ( composition.get() ) { time = timing->time_to_local(time); for ( const auto& sh : composition->shapes ) p.append(sh->to_clip(time)); } return p; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::PreCompLayer::to_clip(glaxnimate::model::FrameTime time) const { return to_painter_path(time).transformed(transform.get()->transform_matrix(time)); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_format.cpp000664 001750 001750 00000006053 15165022620 032512 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/svg/svg_format.hpp" #include #include "glaxnimate/io/svg/svg_parser.hpp" #include "glaxnimate/io/svg/parse_error.hpp" #include "glaxnimate/io/svg/svg_renderer.hpp" #include "glaxnimate/model/assets/assets.hpp" bool glaxnimate::io::svg::SvgFormat::on_open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& options) { /// \todo layer mode setting SvgParser::GroupMode mode = SvgParser::Inkscape; auto on_error = [this](const QString& s){warning(s);}; try { QSize forced_size = options["forced_size"].toSize(); model::FrameTime default_time = options["default_time"].toFloat(); auto default_asset_path = QFileInfo(filename).dir(); SvgParser(&file, mode, document, on_error, this, forced_size, default_time, default_asset_path).parse_to_document(); return true; } catch ( const SvgParseError& err ) { error(err.formatted(QFileInfo(filename).baseName())); return false; } } std::unique_ptr glaxnimate::io::svg::SvgFormat::save_settings(model::Composition* comp) const { CssFontType max = CssFontType::None; for ( const auto & font : comp->document()->assets()->fonts->values ) { auto type = SvgRenderer::suggested_type(font.get()); if ( type > max ) max = type; } if ( max == CssFontType::None ) return {}; QVariantMap choices; if ( max >= CssFontType::Link ) choices[i18n("External Stylesheet")] = int(CssFontType::Link); if ( max >= CssFontType::FontFace ) choices[i18n("Font face with external url")] = int(CssFontType::FontFace); if ( max >= CssFontType::Embedded ) choices[i18n("Embedded data")] = int(CssFontType::Embedded); choices[i18n("Ignore")] = int(CssFontType::None); return std::make_unique(glaxnimate::settings::SettingList{ glaxnimate::settings::Setting("font_type", i18n("External Fonts"), i18n("How to include external font"), glaxnimate::settings::Setting::Int, int(qMin(max, CssFontType::FontFace)), choices) }); } bool glaxnimate::io::svg::SvgFormat::on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap& options) { SvgRenderer rend(SMIL, CssFontType(options["font_type"].toInt())); rend.write_main(comp, comp->document()->current_time()); rend.write(&file, true); return true; } bool glaxnimate::io::svg::SvgFormat::on_save_static(QIODevice &file, const QString &, model::Composition *comp, model::FrameTime time, const QVariantMap &) { // TODO settings? io::svg::SvgRenderer rend(io::svg::NotAnimated, io::svg::CssFontType::FontFace); rend.write_main(comp, time); rend.write(&file, true); return true; } QStringList glaxnimate::io::svg::SvgFormat::extensions(Direction) const { return {"svg"}; } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/aep_parser.hpp000664 001750 001750 00000101742 15165022620 035756 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/module/extraformats/aep/aep_riff.hpp" #include "glaxnimate/module/extraformats/aep/ae_project.hpp" #include "glaxnimate/module/extraformats/aep/cos.hpp" #include "glaxnimate/module/extraformats/aep/gradient_xml.hpp" #include "glaxnimate/module/extraformats/aep/aep_format.hpp" namespace glaxnimate::io::aep { class AepError : public std::runtime_error { public: AepError(QString message) : runtime_error(message.toStdString()), message(std::move(message)) {} QString message; }; class AepParser { private: using Chunk = const RiffChunk*; using ChunkRange = RiffChunk::FindRange; struct PropertyContext { Composition* comp = nullptr; Layer* layer = nullptr; model::FrameTime time_to_frames(model::FrameTime time) const { return time / comp->frame_time + layer->start_time; } }; public: AepParser(ImportExport* io) : io(io) {} Project parse(const RiffChunk& root) { if ( root.subheader != "Egg!" ) throw AepError(i18n("Not an AEP file")); Project project; Chunk fold = nullptr, efdg = nullptr; root.find_multiple({&fold, &efdg}, {"Fold", "EfdG"}); if ( load_unecessary && efdg ) parse_effect_definitions(efdg->find_all("EfDf"), project); parse_folder(fold, project.folder, project); for ( auto& comp : project.compositions ) parse_composition(comp_chunks[comp->id], *comp); return project; } private: void parse_folder(Chunk chunk, Folder& folder, Project& project) { FolderItem* current_item = nullptr; for ( const auto& child : chunk->children ) { if ( *child == "fiac" ) { if ( current_item && child->data().read_uint8() ) project.current_item = current_item; } else if ( *child == "Item" ) { Chunk item = nullptr; Chunk name_chunk = nullptr; child->find_multiple({&item, &name_chunk}, {"idta", "Utf8"}); current_item = nullptr; if ( !item ) continue; auto name = to_string(name_chunk); auto data = item->data(); auto type = data.read_uint16(); data.skip(14); auto id = data.read_uint32(); data.skip(38); auto color = LabelColors(data.read_uint8()); switch ( type ) { case 1: // Folder { auto child_item = folder.add(); child_item->id = id; child_item->name = name; current_item = child_item; if ( auto contents = child->child("Sfdr") ) parse_folder(contents, *child_item, project); break; } case 4: // Composition { auto comp = folder.add(); comp->id = id; comp->name = name; current_item = comp; project.compositions.push_back(comp); project.assets[id] = comp; comp_chunks[id] = child.get(); break; } case 7: // Asset current_item = parse_asset(id, child->child("Pin "), folder, project); break; default: warning(i18n("Unknown Item type %s", type)); } if ( current_item ) current_item->label_color = color; } } } void parse_composition(Chunk chunk, Composition& comp) { auto cdta = chunk->child("cdta"); if ( !cdta ) { warning(i18n("Missing composition data")); return; } /// \todo label color? auto data = cdta->data(); /*0000*/comp.resolution_x = data.read_uint16(); /*0002*/comp.resolution_y = data.read_uint16(); // Time stuff /*0004*/comp.frame_time = data.read_fraction_64(); /*000c*/data.skip(8); /*0014*/comp.playhead_time = comp.time_to_frames(data.read_fraction_64()); /*001c*/comp.in_time = comp.time_to_frames(data.read_fraction_64()); /*0024*/auto out_time_n = data.read_sint32(); /*0029*/auto out_time_d = data.read_uint32(); /*002c*/comp.duration = comp.time_to_frames(data.read_fraction_64()); if ( out_time_n == -1 ) comp.out_time = comp.duration; else comp.out_time = comp.time_to_frames(qreal(out_time_n) / out_time_d); // Background /*0034*/comp.color.setRed(data.read_uint8()); /*0035*/comp.color.setGreen(data.read_uint8()); /*0036*/comp.color.setBlue(data.read_uint8()); // Flags /*0037*/data.skip(84); Flags attr(data.read_uint8()); comp.shy = attr.get(0, 0); comp.motion_blur = attr.get(0, 3); comp.frame_blending = attr.get(0, 4); comp.preserve_framerate = attr.get(0, 5); comp.preserve_resolution = attr.get(0, 7); // Lottie comp.width = data.read_uint16(); comp.height = data.read_uint16(); comp.pixel_ratio_width = data.read_uint32(); comp.pixel_ratio_height = data.read_uint32(); data.skip(4); comp.framerate = data.read_uint16(); // Misc data.skip(16); comp.shutter_angle = data.read_uint16(); comp.shutter_phase = data.read_sint32(); data.skip(16); comp.samples_limit = data.read_uint32(); comp.samples_per_frame = data.read_uint32(); for ( const auto& child : chunk->children ) { if ( *child == "Layr" ) comp.layers.push_back(parse_layer(child.get(), comp)); else if ( load_unecessary && *child == "SecL" ) comp.markers = parse_layer(child.get(), comp); else if ( load_unecessary && (*child == "CLay" || *child == "DLay" || *child == "SLay") ) comp.views.push_back(parse_layer(child.get(), comp)); } } QString to_string(Chunk chunk) { if ( !chunk ) return ""; auto data = chunk->data().read(); if ( data == placeholder ) return ""; if ( chunk->header == "Utf8" ) return QString::fromUtf8(data); warning(i18n("Unknown encoding for %1", chunk->header.to_string())); return ""; } void warning(const QString& msg) const { io->warning(msg); } FolderItem* parse_asset(Id id, Chunk chunk, Folder& folder, Project& project) { Chunk sspc, als2, opti; sspc = als2 = opti = nullptr; chunk->find_multiple({&sspc, &als2, &opti}, {"sspc", "Als2", "opti"}); if ( !sspc || !opti ) { warning(i18n("Missing asset data")); return nullptr; } QStringList name_chunks; for ( const RiffChunk& utf8 : chunk->find_all("Utf8") ) name_chunks.push_back(to_string(&utf8)); QString name = name_chunks.join(' '); auto asset_reader = sspc->data(); asset_reader.skip(30); auto width = asset_reader.read_uint32(); auto height = asset_reader.read_uint32(); Asset* asset; auto data = opti->data(); if ( data.read(4) == "Soli" ) { data.skip(6); auto solid = folder.add(); solid->color.setAlphaF(data.read_float32()); solid->color.setRedF(data.read_float32()); solid->color.setGreenF(data.read_float32()); solid->color.setBlueF(data.read_float32()); solid->name = data.read_utf8_nul(256); asset = solid; } else { auto doc = QJsonDocument::fromJson(als2->child("alas")->data().read()); QString path = doc.object()["fullpath"].toString(); // Handle weird windows paths if ( path.contains('\\') && QDir::separator() == '/' ) { path = path.replace('\\', '/'); if ( path.size() > 1 && path[1] == ':' ) path = '/' + path; } auto file = folder.add(); file->path = QFileInfo(path); file->name = name.isEmpty() ? file->path.fileName() : name; asset = file; } asset->width = width; asset->height = height; asset->id = id; project.assets[id] = asset; return asset; } std::unique_ptr parse_layer(Chunk chunk, Composition& comp) { auto layer = std::make_unique(); Chunk ldta, utf8, tdgp; ldta = utf8 = tdgp = nullptr; chunk->find_multiple({&ldta, &utf8, &tdgp}, {"ldta", "Utf8", "tdgp"}); if ( !ldta ) { warning(i18n("Missing layer data")); return {}; } PropertyContext context{&comp, layer.get()}; layer->name = to_string(utf8); auto data = ldta->data(); /*0000*/layer->id = data.read_uint32(); /*0004*/layer->quality = LayerQuality(data.read_uint16()); /*0006*/data.skip(2); /*0008*/layer->time_stretch = data.read_uint32(); // A lot of these are given as numerator / denominator /*000c*/qreal start_time = data.read_fraction_64(); layer->start_time = comp.time_to_frames(start_time); /*0014*/qint32 in_time = data.read_fraction_64(); layer->in_time = context.time_to_frames(in_time); /*001c*/qint32 out_time = data.read_fraction_64(); layer->out_time = context.time_to_frames(out_time); /*0024*/Flags flags = data.read_uint<4>(); layer->is_guide = flags.get(2, 1); layer->bicubic_sampling = flags.get(2, 6); layer->auto_orient = flags.get(1, 0); layer->is_adjustment = flags.get(1, 1); layer->threedimensional = flags.get(1, 2); layer->solo = flags.get(1, 3); layer->is_null = flags.get(1, 7); layer->visible = flags.get(0, 0); layer->effects_enabled = flags.get(0, 2); layer->motion_blur = flags.get(0, 3); layer->locked = flags.get(0, 5); layer->shy = flags.get(0, 6); layer->continuously_rasterize = flags.get(0, 7); /*0028*/layer->asset_id = data.read_uint32(); /*002c*/data.skip(17); /*003d*/layer->label_color = LabelColors(data.read_uint8()); /*003e*/data.skip(2); /*0040*/data.skip(32); // Name, we get it from Utf8 instead /*0060*/layer->blend_mode = data.read_uint32(); /*0064*/data.skip(4); /*0068*/layer->matte_mode = TrackMatteType(data.read_uint32()); /*006c*/data.skip(2); /*006e*/layer->time_stretch /= data.read_uint16(); /*0070*/data.skip(19); /*0083*/layer->type = LayerType(data.read_uint8()); /*0084*/layer->parent_id = data.read_uint32(); /*0088*/data.skip(24); /*00a0*/layer->matte_id = data.read_uint32(); parse_property_group(tdgp, layer->properties, context); return layer; } void parse_property_group(Chunk chunk, PropertyGroup& group, const PropertyContext& context) { QString match_name; for ( auto it = chunk->children.begin(); it != chunk->children.end(); ++it ) { auto child = it->get(); if ( *child == "tdmn" ) { match_name = child->data().read_utf8_nul(); } else if ( *child == "tdsb" ) { Flags flags = child->data().read_uint32(); group.visible = flags.get(0, 0); } else if ( *child == "tdsn" ) { group.name = to_string(child->child("Utf8")); } else if ( *child == "mkif" ) { auto mask = std::make_unique(); auto data = child->data(); mask->inverted = data.read_uint8(); mask->locked = data.read_uint8(); data.skip(4); mask->mode = MaskMode(data.read_uint16()); ++it; if ( it == chunk->children.end() ) { warning(i18n("Missing mask properties")); return; } if ( **it != "tdgp" ) { warning(i18n("Missing mask properties")); continue; } parse_property_group(it->get(), mask->properties, context); group.properties.push_back({match_name, std::move(mask)}); match_name.clear(); } else if ( !match_name.isEmpty() ) { auto prop = parse_property(child, context); if ( prop ) group.properties.push_back({match_name, std::move(prop)}); match_name.clear(); } } } std::unique_ptr parse_property_group(Chunk chunk, const PropertyContext& context) { auto group = std::make_unique(); parse_property_group(chunk, *group, context); return group; } std::unique_ptr parse_property(Chunk chunk, const PropertyContext& context) { if ( *chunk == "tdgp" ) return parse_property_group(chunk, context); else if ( *chunk == "tdbs" ) return parse_animated_property(chunk, context, {}); else if ( *chunk == "om-s" ) return parse_animated_with_values(chunk, context, "omks", "shap", &AepParser::parse_bezier); else if ( *chunk == "GCst" ) return parse_animated_with_values(chunk, context, "GCky", "Utf8", &AepParser::parse_gradient); else if ( *chunk == "btds" ) return parse_animated_text(chunk, context); else if ( *chunk == "sspc" ) return parse_effect_instance(chunk, context); else if ( *chunk == "otst" ) return load_unecessary ? parse_animated_with_values(chunk, context, "otky", "otda", &AepParser::parse_orientation) : nullptr; else if ( *chunk == "mrst" ) return load_unecessary ? parse_animated_with_values(chunk, context, "mrky", "Nmrd", &AepParser::parse_marker) : nullptr; // I've seen these in files but I'm not sure how to parse them else if ( *chunk == "OvG2" || *chunk == "blsi" || *chunk == "blsv" ) return {}; warning(i18n("Unknown property type: %1", chunk->name().to_string())); return {}; } std::unique_ptr parse_animated_property( Chunk chunk, const PropertyContext& context, std::vector&& values ) { auto prop = std::make_unique(); parse_animated_property(prop.get(), chunk, context, std::move(values)); return prop; } void parse_animated_property( Property* prop, Chunk chunk, const PropertyContext& context, std::vector values ) { Chunk tdsb, header, value, keyframes, expression, tdpi, tdps, tdli; tdsb = header = value = keyframes = expression = tdpi = tdps = tdli = nullptr; chunk->find_multiple( {&tdsb, &header, &value, &keyframes, &expression, &tdpi, &tdps, &tdli}, {"tdsb", "tdb4", "cdat", "list", "Utf8", "tdpi", "tdps", "tdli"} ); if ( tdsb ) { Flags flags(tdsb->data().read_uint32()); prop->split = flags.get(1, 3); } auto data = header->data(); data.skip(2); prop->components = data.read_uint16(); bool position = Flags(data.read_uint16()).get(0, 3); // data.skip(10+8*5); data.skip(6); prop->keyframe_time_denominator = data.read_uint32(); data.skip(40); Flags type = data.read_uint32(); bool no_value = type.get(2, 0); bool color = type.get(0, 0); bool integer = type.get(0, 2); data.skip(8); if ( position ) prop->type = PropertyType::Position; else if ( color ) prop->type = PropertyType::Color; else if ( no_value ) prop->type = PropertyType::NoValue; else if ( integer ) prop->type = PropertyType::Integer; else prop->type = PropertyType::MultiDimensional; prop->animated = data.read_uint8() == 1; data.skip(6); prop->is_component = data.read_uint8() == 1; if ( integer && tdpi ) { prop->type = PropertyType::LayerSelection; LayerSelection val; val.layer_id = tdpi->data().read_uint32(); if ( tdps ) val.layer_source = LayerSource(tdps->data().read_sint32()); prop->value = val; } else if ( integer && tdli ) { prop->type = PropertyType::MaskIndex; prop->value = tdli->data().read_uint32(); } else if ( keyframes ) { auto raw_keys = list_values(keyframes); for ( std::size_t i = 0; i < raw_keys.size(); i++ ) { prop->keyframes.push_back(load_keyframe(i, raw_keys[i], *prop, context, values, prop->keyframe_time_denominator)); } } else if ( value ) { auto vdat = value->data(); auto raw_value = vdat.read_array(&BinaryReader::read_float64, prop->components); prop->value = property_value(0, raw_value, values, prop->type); } if ( expression ) prop->expression = to_string(expression); } PropertyValue property_value( int index, const std::vector& raw_value, std::vector& values, PropertyType type ) { switch ( type ) { case PropertyType::NoValue: if ( index < int(values.size()) ) return std::move(values[index]); return nullptr; case PropertyType::Color: if ( raw_value.size() < 4 ) return QColor(); return QColor(raw_value[1], raw_value[2], raw_value[3], raw_value[0] * 255); default: return vector_value(raw_value); } } PropertyValue vector_value(const std::vector& raw_value) { switch ( raw_value.size() ) { case 0: return nullptr; case 1: return raw_value[0]; case 2: return QPointF(raw_value[0], raw_value[1]); case 3: default: return QVector3D(raw_value[0], raw_value[1], raw_value[2]); } } double nan_or_0(double v) const { return qIsNaN(v) ? 0 : v; } Keyframe load_keyframe(int index, BinaryReader& reader, Property& prop, const PropertyContext& context, std::vector& values, qreal time_den) { reader.prepare(); Keyframe kf; kf.time = context.time_to_frames(reader.read_sint32() / time_den); reader.skip(1); kf.transition_type = KeyframeTransitionType(reader.read_uint8()); kf.label_color = LabelColors(reader.read_uint8()); Flags flags = reader.read_uint8(); kf.roving = flags.get(0, 5); if ( flags.get(0, 3) ) kf.bezier_mode = KeyframeBezierMode::Continuous; else if ( flags.get(0, 4) ) kf.bezier_mode = KeyframeBezierMode::Auto; else kf.bezier_mode = KeyframeBezierMode::Normal; if ( prop.type == PropertyType::NoValue ) { reader.skip(16); kf.in_speed.push_back(nan_or_0(reader.read_float64())); kf.in_influence.push_back(reader.read_float64()); kf.out_speed.push_back(nan_or_0(reader.read_float64())); kf.out_influence.push_back(reader.read_float64()); kf.value = std::move(values[index]); } else if ( prop.type == PropertyType::MultiDimensional || prop.type == PropertyType::Integer ) { kf.value = vector_value(reader.read_array(&BinaryReader::read_float64, prop.components)); kf.in_speed = reader.read_array(&BinaryReader::read_float64, prop.components); kf.in_influence = reader.read_array(&BinaryReader::read_float64, prop.components); kf.out_speed = reader.read_array(&BinaryReader::read_float64, prop.components); kf.out_influence = reader.read_array(&BinaryReader::read_float64, prop.components); } else if ( prop.type == PropertyType::Position ) { reader.skip(16); kf.in_speed.push_back(nan_or_0(reader.read_float64())); kf.in_influence.push_back(reader.read_float64()); kf.out_speed.push_back(nan_or_0(reader.read_float64())); kf.out_influence.push_back(reader.read_float64()); kf.value = vector_value(reader.read_array(&BinaryReader::read_float64, prop.components)); auto it = reader.read_array(&BinaryReader::read_float64, prop.components); auto ot = reader.read_array(&BinaryReader::read_float64, prop.components); if ( prop.components >= 2 ) { kf.in_tangent = {it[0], it[1]}; kf.out_tangent = {ot[0], ot[1]}; } } else if ( prop.type == PropertyType::Color ) { reader.skip(16); kf.in_speed.push_back(nan_or_0(reader.read_float64())); kf.in_influence.push_back(reader.read_float64()); kf.out_speed.push_back(nan_or_0(reader.read_float64())); kf.out_influence.push_back(reader.read_float64()); auto value = reader.read_array(&BinaryReader::read_float64, prop.components); kf.value = QColor(value[1], value[2], value[3], value[0]); } return kf; } template std::unique_ptr parse_animated_with_values( Chunk chunk, const PropertyContext& context, const char* container, const char* value_name, T (AepParser::*parse)(Chunk chunk) ) { Chunk value_container, tdbs; value_container = tdbs = nullptr; chunk->find_multiple({&value_container, &tdbs}, {container, "tdbs"}); std::vector values; for ( const RiffChunk& value_chunk : value_container->find_all(value_name) ) values.emplace_back((this->*parse)(&value_chunk)); return parse_animated_property(tdbs, context, std::move(values)); } std::vector list_values(Chunk list) { Chunk head, vals; head = vals = nullptr; list->find_multiple({&head, &vals}, {"lhd3", "ldat"}); if ( !head || !vals ) { warning(i18n("Missing list data")); return {}; } auto data = head->data(); data.skip(10); std::uint32_t count = data.read_uint16(); data.skip(6); std::uint32_t size = data.read_uint16(); std::uint32_t total_size = count * size; if ( vals->reader.size() < total_size ) { warning(i18n("Not enough data in list")); return {}; } std::vector values; values.reserve(count); for ( std::uint32_t i = 0; i < count; i++ ) values.push_back(vals->reader.sub_reader(size, i * size)); vals->reader.prepare(); return values; } BezierData parse_bezier(Chunk chunk) { BezierData data; auto bounds = chunk->child("shph")->data(); bounds.skip(3); data.closed = !Flags(bounds.read_uint8()).get(0, 3); data.minimum.setX(bounds.read_float32()); data.minimum.setY(bounds.read_float32()); data.maximum.setX(bounds.read_float32()); data.maximum.setY(bounds.read_float32()); for ( auto& pt : list_values(chunk->child("list")) ) { float x = pt.read_float32(); float y = pt.read_float32(); // Ignore corrupted points if ( !qIsNaN(x) && !qIsNaN(y) ) data.points.push_back({x, y}); } return data; } Gradient parse_gradient(Chunk chunk) { return parse_gradient_xml(to_string(chunk)); } QVector3D parse_orientation(Chunk chunk) { auto data = chunk->data(); QVector3D v; v.setX(data.read_float64()); v.setY(data.read_float64()); v.setZ(data.read_float64()); return v; } Marker parse_marker(Chunk chunk) { Marker marker; marker.name = to_string(chunk->child("Utf8")); auto data = chunk->child("NmHd")->data(); data.skip(3); marker.is_protected = data.read_uint8() & 2; marker.duration = data.read_fraction_64(); marker.label_color = LabelColors(data.read_uint8()); return marker; } QColor cos_color(const CosValue& cos) { const auto& arr = *cos.get(); if ( arr.size() < 4 ) throw CosError("Not enough components for color"); return QColor::fromRgbF( arr[1].get(), arr[2].get(), arr[3].get(), arr[0].get() ); } TextDocument parse_text_document(const CosValue& cos) { TextDocument doc; doc.text = get_as(cos, 0, 0); for ( const auto& cs : *get_as(cos, 0, 5, 0) ) { LineStyle style; style.character_count = get_as(cs, 1); const auto& data = get(cs, 0, 0, 5); style.text_justify = TextJustify(get_as(data, 0)); doc.line_styles.emplace_back(std::move(style)); } for ( const auto& cs : *get_as(cos, 0, 6, 0) ) { CharacterStyle style; style.character_count = get_as(cs, 1); const auto& data = get(cs, 0, 0, 6); style.font_index = get_as(data, 0); style.size = get_as(data, 1); style.faux_bold = get_as(data, 2); style.faux_italic = get_as(data, 3); style.text_transform = TextTransform(get_as(data, 12)); style.vertical_align = TextVerticalAlign(get_as(data, 13)); style.fill_color = cos_color(get(data, 53, 0, 1)); style.stroke_color = cos_color(get(data, 54, 0, 1)); style.stroke_enabled = get_as(data, 57); style.stroke_over_fill = get_as(data, 58); style.stroke_width = get_as(data, 63); doc.character_styles.emplace_back(std::move(style)); } return doc; } std::unique_ptr parse_animated_text(Chunk chunk, const PropertyContext& context) { Chunk text_data, tdbs; text_data = tdbs = nullptr; chunk->find_multiple({&text_data, &tdbs}, {"btdk", "tdbs"}); try { auto val = CosParser(text_data->data().read()).parse(); if ( val.type() != CosValue::Index::Object ) throw CosError("Expected Object"); auto property = std::make_unique(); for ( const auto& font : *get_as(val, 0, 1, 0) ) property->fonts.push_back({get_as(font, 0, 0, 0)}); std::vector values; for ( const auto& doc : *get_as(val, 1, 1) ) values.push_back(parse_text_document(doc)); parse_animated_property(&property->documents, tdbs, context, std::move(values)); return property; } catch ( const CosError& err ) { warning(i18n("Invalid text document: %1", err.message)); return {}; } } void parse_effect_definitions(const ChunkRange& range, Project& project) { for ( const auto& chunk : range ) { Chunk tdmn, sspc; tdmn = sspc = nullptr; chunk.find_multiple({&tdmn, &sspc}, {"tdmn", "sspc"}); if ( !tdmn || !sspc ) continue; auto mn = tdmn->data().read_utf8_nul(); EffectDefinition& effect = project.effects[mn]; effect.match_name = mn; Chunk fnam, part; fnam = part = nullptr; chunk.find_multiple({&fnam, &part}, {"fnam", "parT"}); if ( fnam ) effect.name = to_string(fnam->child("Utf8")); QString param_mn; for ( const auto& param_chunk : part->children ) { if ( *param_chunk == "tdmn" ) { param_mn = param_chunk->data().read_utf8_nul(); } else { auto& param = effect.parameter_map[param_mn]; param.match_name = param_mn; effect.parameters.push_back(¶m); parse_effect_parameter(param, param_chunk->data()); } } } } void parse_effect_parameter(EffectParameter& param, BinaryReader data) { data.skip(15); param.type = EffectParameterType(data.read_uint8()); param.name = data.read_utf8_nul(32); data.skip(8); switch ( param.type ) { case EffectParameterType::Layer: param.last_value = LayerSelection(); param.default_value = LayerSelection(); break; case EffectParameterType::Scalar: case EffectParameterType::Angle: param.last_value = data.read_sint32() / 0x10000; param.default_value = 0; break; case EffectParameterType::Boolean: param.last_value = data.read_uint32(); param.default_value = data.read_uint8(); break; case EffectParameterType::Color: { auto a = data.read_uint8(); auto r = data.read_uint8(); auto g = data.read_uint8(); auto b = data.read_uint8(); param.last_value = QColor(r, g, b, a); data.skip(1); a = 255; r = data.read_uint8(); g = data.read_uint8(); b = data.read_uint8(); param.default_value = QColor(r, g, b, a); break; } case EffectParameterType::Vector2D: { qreal x = data.read_sint32(); qreal y = data.read_sint32(); param.last_value = QPointF(x / 0x80, y / 0x80); param.default_value = QPointF(); break; } case EffectParameterType::Enum: param.last_value = data.read_uint32(); data.skip(2); // Number of enum values param.default_value = data.read_uint16(); break; case EffectParameterType::Slider: param.last_value = data.read_float64(); param.default_value = 0; break; case EffectParameterType::Vector3D: { auto x3 = data.read_float64() * 512; auto y3 = data.read_float64() * 512; auto z3 = data.read_float64() * 512; param.last_value = QVector3D(x3, y3, z3); param.default_value = QVector3D(0, 0, 0); break; } default: param.last_value = 0; param.default_value = 0; break; } } std::unique_ptr parse_effect_instance(Chunk chunk, const PropertyContext& context) { if ( !load_unecessary ) return {}; auto effect = std::make_unique(); Chunk fnam, tdgp; fnam = tdgp = nullptr; chunk->find_multiple({&fnam, &tdgp}, {"fnam", "tdgp"}); if ( fnam ) effect->name = to_string(fnam->child("Utf8")); parse_property_group(tdgp, effect->parameters, context); return effect; } static constexpr const char* const placeholder = "-_0_/-"; std::unordered_map comp_chunks; // For loading into the object model stuff Glaxnimate doesn't support // I'm adding the code to load them just in case someone wants to do // something else with them and so that if Glaxnimate ever supports given // features, it's easier to add const bool load_unecessary = false; ImportExport* io; }; } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/style/000775 001750 001750 00000000000 15165022620 032150 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/custom_font.cpp000664 001750 001750 00000016514 15165022620 032600 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/custom_font.hpp" #include #include #include #include "glaxnimate/utils/qbytearray_hash.hpp" glaxnimate::model::FontFileFormat glaxnimate::model::CustomFontDatabase::font_data_format(const QByteArray& data) { QByteArray head = data.left(4); if ( head == "OTTO" ) return FontFileFormat::OpenType; if ( head == QByteArray("\0\1\0\0", 4) ) return FontFileFormat::TrueType; if ( head == "wOF2" ) return FontFileFormat::Woff2; if ( head == "wOFF" ) return FontFileFormat::Woff; return FontFileFormat::Unknown; } class glaxnimate::model::CustomFontDatabase::CustomFontData { public: CustomFontData() = default; CustomFontData(const QRawFont& font, int database_index, const QByteArray& data_hash, const QByteArray& data) : font(font), database_index(database_index), data_hash(data_hash), data(data) {} QString family_name() const { return font.familyName(); } QRawFont font; int database_index = -1; QByteArray data_hash; QByteArray data; QString source_url; QString css_url; std::set name_aliases; }; class glaxnimate::model::CustomFontDatabase::Private { public: std::unordered_map fonts; // we keep track of hashes to avoid registering the exact same file twice std::unordered_map hashes; std::unordered_map> name_aliases; void tag_alias(const DataPtr& data, const QString& name) { if ( !name.isEmpty() && name != data->family_name() && data->name_aliases.insert(name).second ) name_aliases[name].push_back(data->database_index); } void uninstall(std::unordered_map::iterator iterator) { for ( const auto& name : iterator->second->name_aliases ) { auto iter = name_aliases.find(name); if ( iter != name_aliases.end() ) { if ( iter->second.size() <= 1 ) name_aliases.erase(iter); else iter->second.erase(std::find(iter->second.begin(), iter->second.end(), iterator->second->database_index)); } } hashes.erase(iterator->second->data_hash); QFontDatabase::removeApplicationFont(iterator->first); fonts.erase(iterator); } void remove_reference(int font) { auto it = fonts.find(font); if ( it == fonts.end() ) return; if ( it->second.use_count() == 1 ) uninstall(it); } DataPtr install(const QString& name_alias, const QByteArray& data) { auto hash = QCryptographicHash::hash(data, QCryptographicHash::Sha1); auto hashit = hashes.find(hash); if ( hashit != hashes.end() ) { auto item = fonts.at(hashit->second); tag_alias(item, name_alias); return item; } QRawFont raw(data, 16); if ( !raw.isValid() ) return {}; int index = QFontDatabase::addApplicationFontFromData(data); if ( index == -1 ) return {}; hashes[hash] = index; auto ptr = std::make_shared(raw, index, hash, data); fonts.emplace(index, ptr); tag_alias(ptr, name_alias); return ptr; } }; glaxnimate::model::CustomFontDatabase::CustomFontDatabase() : d(std::make_unique()) { } glaxnimate::model::CustomFontDatabase::~CustomFontDatabase() { } glaxnimate::model::CustomFontDatabase & glaxnimate::model::CustomFontDatabase::instance() { static CustomFontDatabase instance; return instance; } std::vector glaxnimate::model::CustomFontDatabase::fonts() const { std::vector fonts; fonts.reserve(d->fonts.size()); for ( const auto& font : d->fonts ) fonts.emplace_back(font.second); return fonts; } glaxnimate::model::CustomFont glaxnimate::model::CustomFontDatabase::add_font(const QString& name_alias, const QByteArray& ttf_data) { return d->install(name_alias, ttf_data); } glaxnimate::model::CustomFont glaxnimate::model::CustomFontDatabase::get_font(int database_index) { auto it = d->fonts.find(database_index); if ( it == d->fonts.end() ) return {}; return it->second; } QFont glaxnimate::model::CustomFontDatabase::font(const QString& family, const QString& style_name, qreal size) const { auto it = d->name_aliases.find(family); if ( it == d->name_aliases.end() ) { QFont font(family); font.setPointSizeF(size); font.setStyleName(style_name); return font; } CustomFontData* match = d->fonts.at(it->second[0]).get(); for ( int id : it->second ) { const auto& font = d->fonts.at(id); if ( font->font.styleName() == style_name ) { match = font.get(); break; } } QFont font(match->family_name()); font.setPointSizeF(size); font.setStyleName(style_name); return font; } std::unordered_map> glaxnimate::model::CustomFontDatabase::aliases() const { std::unordered_map> map; for ( const auto& p : d->name_aliases ) { std::set names; for ( const auto& id : p.second ) names.insert(d->fonts.at(id)->family_name()); map[p.first] = names; } return map; } glaxnimate::model::CustomFont::CustomFont(CustomFontDatabase::DataPtr dd) : d(std::move(dd)) { if ( !d ) d = std::make_shared(); } glaxnimate::model::CustomFont::CustomFont() : CustomFont(std::make_shared()) { } glaxnimate::model::CustomFont::CustomFont(int database_index) : CustomFont(CustomFontDatabase::instance().get_font(database_index)) { } glaxnimate::model::CustomFont::~CustomFont() { if ( d ) { int index = d->database_index; if ( index != -1 ) { d = {}; CustomFontDatabase::instance().d->remove_reference(index); } } } bool glaxnimate::model::CustomFont::is_valid() const { return d->database_index != -1; } int glaxnimate::model::CustomFont::database_index() const { return d->database_index; } QString glaxnimate::model::CustomFont::family() const { return d->family_name(); } QString glaxnimate::model::CustomFont::style_name() const { return d->font.styleName(); } QFont glaxnimate::model::CustomFont::font(int size) const { QFont font(family(), size); font.setStyleName(style_name()); return font; } const QRawFont & glaxnimate::model::CustomFont::raw_font() const { return d->font; } QByteArray glaxnimate::model::CustomFont::data() const { return d->data; } void glaxnimate::model::CustomFont::set_css_url(const QString& url) { d->css_url = url; } void glaxnimate::model::CustomFont::set_source_url(const QString& url) { d->source_url = url; } const QString & glaxnimate::model::CustomFont::css_url() const { return d->css_url; } const QString & glaxnimate::model::CustomFont::source_url() const { return d->source_url; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/layer.hpp000664 001750 001750 00000006323 15165022620 034765 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/property/reference_property.hpp" #include "glaxnimate/model/animation_container.hpp" #include "glaxnimate/model/mask_settings.hpp" namespace glaxnimate::model { class Layer : public StaticOverrides { GLAXNIMATE_OBJECT(Layer) GLAXNIMATE_SUBOBJECT(AnimationContainer, animation) GLAXNIMATE_PROPERTY_REFERENCE(Layer, parent, &Layer::valid_parents, &Layer::is_valid_parent, &Layer::docnode_on_update_group) /** * \brief Whether the layer will be rendered / exported in other formats */ GLAXNIMATE_PROPERTY(bool, render, true) GLAXNIMATE_SUBOBJECT(MaskSettings, mask) public: class ChildLayerIterator { public: using value_type = VisualNode; using reference = value_type&; using pointer = value_type*; using difference_type = int; using iterator_category = std::forward_iterator_tag; ChildLayerIterator& operator++() { ++index; find_first(); return *this; } pointer operator*() const; pointer operator->() const; bool operator==(const ChildLayerIterator& other) const { return comp == other.comp && parent == other.parent && index == other.index; } bool operator!=(const ChildLayerIterator& other) const { return !(*this == other); } private: ChildLayerIterator(const ShapeListProperty* comp, const Layer* parent, int index) : comp(comp), parent(parent), index(index) { find_first(); } void find_first(); friend Layer; friend Composition; const ShapeListProperty* comp; const Layer* parent; int index; }; using Ctor::Ctor; VisualNode* docnode_group_parent() const override; int docnode_group_child_count() const override; VisualNode* docnode_group_child(int index) const override; QIcon tree_icon() const override; static QIcon static_tree_icon(); static QString static_type_name_human() { return i18n("Layer"); } void set_time(FrameTime t) override; /** * \brief Returns the (frame) time relative to this layer * * Useful for stretching / remapping etc. * Always use this to get animated property values, * even if currently it doesn't do anything */ FrameTime relative_time(FrameTime time) const { return time; } bool is_ancestor_of(const Layer* other) const; bool is_top_level() const; void paint(renderer::Renderer*, FrameTime, PaintMode, model::Modifier* modifier) const override; glaxnimate::math::bezier::MultiBezier to_clip(model::FrameTime t) const override; std::unique_ptr to_path() const override; bool is_valid_parent(DocumentNode* node) const; protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(model::FrameTime t) const override; private: std::vector valid_parents() const; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/asset_base.cpp000664 001750 001750 00000000261 15165022620 033643 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/asset_base.hpp" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/utils.cpp000664 001750 001750 00000003244 15165022620 030703 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/utils.hpp" std::vector> glaxnimate::io::split_keyframes(model::AnimatableBase* prop) { std::vector> split_kfs; if ( !prop->animated() ) return split_kfs; auto range = prop->keyframe_range(); std::unique_ptr previous = range.begin()->clone(); auto kf = range.begin(); for ( ++kf; kf != range.end(); ++kf ) { if ( previous->transition().hold() ) { split_kfs.push_back(std::move(previous)); previous = kf->clone(); continue; } std::array raw_splits; std::tie(raw_splits[0], raw_splits[1]) = previous->transition().bezier().extrema(1); std::vector splits; for ( qreal t : raw_splits ) { QPointF p = previous->transition().bezier().solve(t); if ( p.y() < 0 || p.y() > 1 ) splits.push_back(t); } if ( splits.size() == 0 ) { split_kfs.push_back(std::move(previous)); previous = kf->clone(); } else { auto next = kf; auto next_segment = previous->split(&*next, splits); previous = std::move(next_segment.back()); split_kfs.insert(split_kfs.end(), std::make_move_iterator(next_segment.begin()), std::make_move_iterator(next_segment.end() - 1)); } } split_kfs.push_back(std::move(previous)); return split_kfs; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/glaxnimate_mime.cpp000664 001750 001750 00000010341 15165022620 035030 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/glaxnimate/glaxnimate_mime.hpp" #include #include "glaxnimate/io/glaxnimate/import_state.hpp" #include "glaxnimate/model/shapes/shape.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/visitor.hpp" #include "glaxnimate/log/log.hpp" using namespace glaxnimate; namespace { class GetDeps : public model::Visitor { public: GetDeps(const std::vector& objects) : skip(objects.begin(), objects.end()) {} void on_visit(model::DocumentNode * node) override { for ( auto property : node->properties() ) { if ( property->traits().type == model::PropertyTraits::ObjectReference && property->name() != "parent" ) { auto ptr = static_cast(property)->get_ref(); if ( !ptr || skip.count(ptr)) continue; skip.insert(ptr); referenced[ptr->uuid.get().toString()] = ptr; on_visit(ptr); } } } std::set skip; std::map referenced; }; } // namespace QStringList io::glaxnimate::GlaxnimateMime::mime_types() const { return {"application/vnd.glaxnimate.rawr+json"}; } QJsonDocument io::glaxnimate::GlaxnimateMime::serialize_json(const std::vector& objects) { QJsonArray arr; GetDeps gd(objects); for ( auto object : objects ) { gd.visit(object); arr.push_back(GlaxnimateFormat::to_json(object)); } for ( const auto& p: gd.referenced ) arr.push_front(GlaxnimateFormat::to_json(p.second)); return QJsonDocument(arr); } QByteArray io::glaxnimate::GlaxnimateMime::serialize(const std::vector& objects) const { return serialize_json(objects).toJson(QJsonDocument::Compact); } io::mime::DeserializedData io::glaxnimate::GlaxnimateMime::deserialize(const QByteArray& data) const { QJsonDocument jdoc; try { jdoc = QJsonDocument::fromJson(data); } catch ( const QJsonParseError& err ) { message(i18n("Could not parse JSON: %1", err.errorString())); return {}; } if ( !jdoc.isArray() ) { message(i18n("No JSON object found")); return {}; } QJsonArray input_objects = jdoc.array(); io::mime::DeserializedData output; output.initialize_data(); detail::ImportState state(nullptr, output.document.get()); for ( auto json_val : input_objects ) { if ( !json_val.isObject() ) continue; QJsonObject json_object = json_val.toObject(); auto obj = model::Factory::instance().build(json_object["__type__"].toString(), output.document.get()); if ( !obj ) continue; if ( auto shape = qobject_cast(obj) ) { output.main->shapes.emplace(shape); } else if ( auto composition = qobject_cast(obj) ) { state.load_object(composition, json_object); output.main->assign_from(composition); delete composition; continue; } else if ( auto color = qobject_cast(obj) ) { output.document->assets()->colors->values.emplace(color); } else if ( auto bitmap = qobject_cast(obj) ) { output.document->assets()->images->values.emplace(bitmap); } else if ( auto gradient = qobject_cast(obj) ) { output.document->assets()->gradients->values.emplace(gradient); } else if ( auto gradient_colors = qobject_cast(obj) ) { output.document->assets()->gradient_colors->values.emplace(gradient_colors); } else { log::Log("I/O").stream() << "Could not deserialize " << obj->type_name(); delete obj; continue; } state.load_object(obj, json_object); } state.resolve(); return output; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/000775 001750 001750 00000000000 15165022620 033376 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/raster/000775 001750 001750 00000000000 15165022620 030334 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/document_node.hpp000664 001750 001750 00000026716 15165022620 033075 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include "glaxnimate/model/animation/animatable.hpp" #include "glaxnimate/model/object.hpp" #include "glaxnimate/renderer/renderer.hpp" namespace glaxnimate::model { class Document; class ReferencePropertyBase; class ObjectListPropertyBase; /** * \brief Base class for elements of the document tree, that need to show in the tree view etc. */ class DocumentNode : public Object { Q_OBJECT public: /** * @brief Unique identifier for the node */ GLAXNIMATE_PROPERTY_RO(QUuid, uuid, {}) /** * @brief Name of the node, used to display it in the UI */ GLAXNIMATE_PROPERTY(QString, name, "", &DocumentNode::on_name_changed) protected: template class ChildRange { public: using get_func_t = Base* (Base::*) (int) const; using count_func_t = int (Base::*) () const; class ChildIterator { public: using value_type = Base*; using reference = value_type*; using pointer = value_type*; using difference_type = int; using iterator_category = std::forward_iterator_tag; ChildIterator& operator++() noexcept { ++index; return *this; } Base* operator->() const { return (parent->*get_func)(index); } Base* operator*() const { return (parent->*get_func)(index); } bool operator==(const ChildIterator& oth) const noexcept { return parent == oth.parent && index == oth.index; } bool operator!=(const ChildIterator& oth) const noexcept { return !(*this == oth ); } private: ChildIterator(const Base* parent, int index, get_func_t get_func) noexcept : parent(parent), index(index), get_func(get_func) {} const Base* parent; int index; get_func_t get_func; friend ChildRange; }; ChildIterator begin() const noexcept { return ChildIterator{parent, 0, get_func}; } ChildIterator end() const noexcept { return ChildIterator{parent, size(), get_func}; } int size() const { return (parent->*count_func)(); } private: ChildRange(const Base* parent, get_func_t get_func, count_func_t count_func) noexcept : parent(parent), get_func(get_func), count_func(count_func) {} const Base* parent; get_func_t get_func; count_func_t count_func; friend Base; }; public: using User = ReferencePropertyBase; explicit DocumentNode(model::Document* document); ~DocumentNode(); virtual DocumentNode* docnode_parent() const; virtual int docnode_child_count() const = 0; virtual DocumentNode* docnode_child(int index) const = 0; virtual int docnode_child_index(DocumentNode* dn) const = 0; virtual QIcon instance_icon() const = 0; ChildRange docnode_children() const noexcept { return ChildRange{this, &DocumentNode::docnode_child, &DocumentNode::docnode_child_count}; } template T* docnode_find_by_uuid(const QUuid& uuid) { if ( this->uuid.get() == uuid && qobject_cast(this) ) return this; for ( DocumentNode* child : docnode_children() ) if ( auto found = child->docnode_find_by_uuid(uuid) ) return found; return nullptr; } template T* docnode_find_by_name(const QString& name) { if ( this->name.get() == name && qobject_cast(this) ) return this; for ( DocumentNode* child : docnode_children() ) if ( auto found = child->docnode_find_by_name(name) ) return found; return nullptr; } template std::vector docnode_find_by_type_name(const QString& type_name) { std::vector matches; docnode_find_impl(type_name, matches); return matches; } template std::vector docnode_find_by_type() { std::vector matches; docnode_find_impl({}, matches); return matches; } bool docnode_is_instance(const QString& type_name) const; Q_INVOKABLE glaxnimate::model::DocumentNode* find_by_name(const QString& name) { return docnode_find_by_name(name); } Q_INVOKABLE glaxnimate::model::DocumentNode* find_by_uuid(const QUuid& uuid) { return docnode_find_by_uuid(uuid); } Q_INVOKABLE QVariantList find_by_type_name(const QString& type_name) { auto ob = docnode_find_by_type_name(type_name); QVariantList ret; ret.reserve(ob.size()); for ( auto o : ob ) ret.push_back(QVariant::fromValue(o)); return ret; } /** * \brief Updates the name of this node and all of its children * using the "best name" document functions */ void recursive_rename(); /** * \brief Recursively updates uuid */ void refresh_uuid(); QString object_name() const override; /** * \brief List of properties referencing this node */ const std::unordered_set& users() const; /** * \brief Mark \p user as referencing this object */ void add_user(User* user); /** * \brief Remove a user */ void remove_user(User* user); /** * \brief Signals all users that the item has been reinstated */ void attach(); /** * \brief Signals all users that the item has been removed */ void detach(); /** * \brief Whether this node is a descendant of \p other */ bool is_descendant_of(const model::DocumentNode* other) const; protected: virtual void on_parent_changed(model::DocumentNode* old_parent, model::DocumentNode* new_parent) { Q_UNUSED(old_parent); Q_UNUSED(new_parent); } virtual void on_graphics_changed() {} private: template void docnode_find_impl(const QString& type_name, std::vector& matches) { if ( type_name.isEmpty() || docnode_is_instance(type_name) ) { if ( auto obj = qobject_cast(this) ) matches.push_back(obj); } for ( DocumentNode* child : docnode_children() ) child->docnode_find_impl(type_name, matches); } void removed_from_list(); void added_to_list(DocumentNode* new_parent); void on_name_changed(const QString& name, const QString& old_name); Q_SIGNALS: void docnode_child_add_begin(int row); void docnode_child_add_end(DocumentNode* node, int row); void docnode_child_remove_begin(int row); void docnode_child_remove_end(DocumentNode* node, int row); void docnode_child_move_begin(int from, int to); void docnode_child_move_end(DocumentNode* node, int from, int to); void name_changed(const QString&); Q_SIGNALS: void users_changed(); protected: class Private; DocumentNode(model::Document* document, std::unique_ptr d); std::unique_ptr d; friend ObjectListPropertyBase; }; class Modifier; /** * \brief A document node that has a physical size and shape in the document */ class VisualNode : public DocumentNode { Q_OBJECT /** * @brief Color of the node the tree UI to highlight grouped items * * Generally parent/child relationshitps define groups but layers can * be grouped with each other even if they are children of a composition */ GLAXNIMATE_PROPERTY(QColor, group_color, QColor(0, 0, 0, 0), &VisualNode::on_group_color_changed) /** * \brief Visible setting for this node */ GLAXNIMATE_PROPERTY(bool, visible, true, &VisualNode::on_visible_changed, {}, PropertyTraits::Visual|PropertyTraits::Hidden) /** * \brief Locked setting for this node */ GLAXNIMATE_PROPERTY(bool, locked, false, &VisualNode::docnode_locked_changed) Q_PROPERTY(bool visible_recursive READ docnode_visible_recursive) Q_PROPERTY(bool locked_recursive READ docnode_locked_recursive) Q_PROPERTY(bool selectable READ docnode_selectable) public: enum PaintMode { Canvas, ///< Paint everything Render ///< Recursive, but hide objects marked with render == false }; explicit VisualNode(model::Document* document); QColor docnode_group_color() const; virtual VisualNode* docnode_group_parent() const; virtual int docnode_group_child_count() const; virtual VisualNode* docnode_group_child(int index) const; VisualNode* docnode_fuzzy_parent() const; VisualNode* docnode_visual_child(int index) const; VisualNode* docnode_visual_parent() const; ChildRange docnode_visual_children() const noexcept { return ChildRange{this, &VisualNode::docnode_visual_child, &VisualNode::docnode_child_count}; } ChildRange docnode_group_children() const noexcept { return ChildRange{this, &VisualNode::docnode_group_child, &VisualNode::docnode_group_child_count}; } /** * \brief Bounding rect in local coordinates (current frame) */ virtual QRectF local_bounding_rect(FrameTime t) const = 0; /** * \brief \b true iff this node and all of its ancestors are visible and unlocked */ bool docnode_selectable() const; /** * \brief \b true iff this node and all of its ancestors are visible */ bool docnode_visible_recursive() const; /** * \brief \b true iff this node or any of its ancestors is locked */ bool docnode_locked_recursive() const; /** * \brief Transform matrix mapping points from document coordinates to local coordinates */ QTransform transform_matrix(FrameTime t) const; QTransform group_transform_matrix(FrameTime t) const; /** * \brief Transform matrix mapping points from parent coordinates to local coordinates */ virtual QTransform local_transform_matrix(FrameTime) const { return QTransform(); } virtual void paint(renderer::Renderer* painter, FrameTime time, PaintMode mode, model::Modifier* modifier = nullptr) const; QIcon instance_icon() const override; Q_SIGNALS: void docnode_visible_changed(bool); void docnode_locked_changed(bool); void docnode_visible_recursive_changed(bool); void docnode_group_color_changed(const QColor&); /** * \note Do not emit directly, call propagate_bounding_rect_changed instead */ void bounding_rect_changed(); void transform_matrix_changed(const QTransform& t); void group_transform_matrix_changed(const QTransform& t); void local_transform_matrix_changed(const QTransform& t); private: void propagate_visible(bool visible); void on_group_color_changed(const QColor& color); protected: void docnode_on_update_group(bool force = false); bool docnode_valid_color() const; void propagate_transform_matrix_changed(const QTransform& t_global, const QTransform& t_group); void propagate_bounding_rect_changed(); virtual void on_paint(renderer::Renderer*, FrameTime, PaintMode, model::Modifier*) const {} private: void on_visible_changed(bool visible); class Private; Private* dd() const; }; } // namespace glaxnimate::model Q_DECLARE_METATYPE(std::vector) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/geom.hpp000664 001750 001750 00000002045 15165022620 031017 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/math/vector.hpp" namespace glaxnimate::math { /** * \brief Finds the closest point to a line * \param line_a Point to determine the line * \param line_b Second point to determine the line * \param p Point to find the closest of * \returns The point on the line that is closest to \p p */ QPointF line_closest_point(const QPointF& line_a, const QPointF& line_b, const QPointF& p); /** * \brief Gets the center of the circle passing through 3 points */ QPointF circle_center(const QPointF& p1, const QPointF& p2, const QPointF& p3); /** * \brief Intersection point between two lines, each defined by two points * \note the intersection might lay outside the segments provided */ std::optional line_intersection(const QPointF& start1, const QPointF& end1, const QPointF& start2, const QPointF& end2); } // namespace glaxnimate::math mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/renderer/000775 001750 001750 00000000000 15165022620 030233 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/stretchable_time.hpp000664 001750 001750 00000001537 15165022620 033562 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/object.hpp" #include "glaxnimate/model/property/property.hpp" namespace glaxnimate::model { class StretchableTime : public Object { GLAXNIMATE_OBJECT(StretchableTime) GLAXNIMATE_PROPERTY(float, start_time, 0, &StretchableTime::timing_changed, {}, PropertyTraits::Visual) GLAXNIMATE_PROPERTY(float, stretch, 1, &StretchableTime::timing_changed, {}, PropertyTraits::Visual|PropertyTraits::Percent) public: using Object::Object; float time_to_local(float global) const; float time_from_local(float local) const; QString type_name_human() const override; private: bool validate_stretch(float stretch); Q_SIGNALS: void timing_changed(); }; } // model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/module_autoregister.in.cpp000664 001750 001750 00000000471 15165022620 035107 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/module.hpp" ${GLAXNIMATE_MODULES_INCLUDES} void glaxnimate::module::Registry::register_loaded_modules(Registry& registry) { ${GLAXNIMATE_MODULES_REGISTER} } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/pseudo_mutex.hpp000664 001750 001750 00000001366 15165022620 033025 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::utils { class PseudoMutex { public: bool try_lock() noexcept { if ( locked ) return false; locked = true; return true; } void lock() noexcept { locked = true; } void unlock() noexcept { locked = false; } explicit operator bool() const noexcept { return locked; } std::unique_lock get_lock() { return std::unique_lock(*this, std::try_to_lock); } private: bool locked = false; }; } // namespace glaxnimate::utils src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/path_modifier.cpp000664 001750 001750 00000006600 15165022620 036232 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/modifiers/path_modifier.hpp" #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" static void to_path_value( const glaxnimate::math::bezier::MultiBezier& mbez, std::vector& paths, glaxnimate::model::Group* group, glaxnimate::model::Document* document ) { for ( int i = 0; i < mbez.size(); i++ ) { if ( i >= int(paths.size()) ) { auto new_path = std::make_unique(document); paths.push_back(new_path.get()); group->shapes.insert(std::move(new_path)); } auto path = paths[i]; path->shape.set(mbez.beziers()[i]); } } static void to_path_frame( const glaxnimate::math::bezier::MultiBezier& mbez, std::vector& paths, glaxnimate::model::FrameTime t, const glaxnimate::model::KeyframeTransition& transition, glaxnimate::model::Group* group, glaxnimate::model::Document* document ) { for ( int i = 0; i < mbez.size(); i++ ) { if ( i >= int(paths.size()) ) { auto new_path = std::make_unique(document); if ( t > 0 ) { glaxnimate::model::KeyframeTransition trans; trans.set_hold(true); new_path->shape.set_keyframe(0, glaxnimate::math::bezier::Bezier{})->set_transition({trans}); } paths.push_back(new_path.get()); group->shapes.insert(std::move(new_path)); } auto path = paths[i]; path->shape.set_keyframe(t, mbez.beziers()[i])->set_transition(transition); } } std::unique_ptr glaxnimate::model::PathModifier::to_path() const { auto group = std::make_unique(document()); group->name.set(name.get()); group->group_color.set(group_color.get()); group->visible.set(visible.get()); std::vector properties; auto flags = PropertyTraits::Visual|PropertyTraits::Animated; for ( auto prop : this->properties() ) { if ( (prop->traits().flags & flags) == flags ) properties.push_back(static_cast(prop)); } JoinAnimatables ja(std::move(properties), JoinAnimatables::NoValues); FrameTime cur_time = ja.properties()[0]->time(); std::vector paths; if ( ja.animated() ) { for ( const auto & kf : ja ) { auto bez = collect_shapes(kf.time, {}); to_path_frame(bez, paths, kf.time, kf.transition(), group.get(), document()); } } else { auto bez = collect_shapes(time(), {}); to_path_value(bez, paths, group.get(), document()); } group->set_time(cur_time); return group; } void glaxnimate::model::PathModifier::on_paint(renderer::Renderer* painter, glaxnimate::model::FrameTime t, glaxnimate::model::VisualNode::PaintMode mode, glaxnimate::model::Modifier*) const { for ( auto sib : affected() ) sib->paint(painter, t, mode, const_cast(this)); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/raster/spritesheet_format.cpp000664 001750 001750 00000006503 15165022620 034753 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/raster/spritesheet_format.hpp" #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/renderer/renderer.hpp" #include #include #include QStringList glaxnimate::io::raster::SpritesheetFormat::extensions(Direction) const { QStringList formats; formats << "png"; for ( const auto& fmt : QImageWriter::supportedImageFormats() ) if ( fmt != "jpg" && fmt != "svg" ) formats << QString::fromUtf8(fmt); return formats; } std::unique_ptr glaxnimate::io::raster::SpritesheetFormat::save_settings(model::Composition* comp) const { int first_frame = comp->animation->first_frame.get(); int last_frame = comp->animation->last_frame.get(); int frames = last_frame - first_frame; return std::make_unique(glaxnimate::settings::SettingList{ glaxnimate::settings::Setting("frame_width", i18n("Frame Width"), i18n("Width of each frame"), int(comp->width.get()), 1, 999'999), glaxnimate::settings::Setting("frame_height", i18n("Frame Height"), i18n("Height of each frame"), int(comp->height.get()), 1, 999'999), glaxnimate::settings::Setting("columns", i18n("Columns"), i18n("Number of columns in the sheet"), std::ceil(math::sqrt(frames)), 1, 64), glaxnimate::settings::Setting("frame_step", i18n("Time Step"), i18n("By how much each rendered frame should increase time (in frames)"), 1, 1, 16), }); } bool glaxnimate::io::raster::SpritesheetFormat::on_save(QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& setting_values) { Q_UNUSED(filename); int frame_w = setting_values["frame_width"].toInt(); int frame_h = setting_values["frame_height"].toInt(); int columns = setting_values["columns"].toInt(); int frame_step = setting_values["frame_step"].toInt(); if ( frame_w <= 0 || frame_h <= 0 || columns <= 0 || frame_step <= 0 ) return false; int first_frame = comp->animation->first_frame.get(); int last_frame = comp->animation->last_frame.get(); int frames = (last_frame - first_frame) / frame_step; int rows = qCeil(float(frames) / columns); qreal scale_x = qreal(frame_w) / comp->width.get(); qreal scale_y = qreal(frame_h) / comp->height.get(); QImage bmp(frame_w * columns, frame_h * rows, QImage::Format_ARGB32); bmp.fill(Qt::transparent); auto renderer = renderer::RendererRegistry::instance().default_renderer(10); renderer->set_image_surface(&bmp); renderer->render_start(); for ( int i = first_frame; i <= last_frame; i += frame_step ) { renderer->layer_start(); renderer->scale(scale_x, scale_y); qreal x = (i % columns) * frame_w; qreal y = (i / columns) * frame_h; renderer->translate(x, y); renderer->clip_rect(QRectF(x, y, frame_w, frame_h)); comp->paint(renderer.get(), i, model::VisualNode::Render); renderer->layer_end(); } renderer->render_end(); QImageWriter writer(&file, {}); writer.setOptimizedWrite(true); if ( writer.write(bmp) ) return true; error(writer.errorString()); return false; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/document.hpp000664 001750 001750 00000006365 15165022620 032066 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/io/options.hpp" #include "glaxnimate/model/comp_graph.hpp" #include "glaxnimate/model/document_node.hpp" namespace glaxnimate::model { class Assets; struct PendingAsset; class Composition; class Document : public QObject { Q_OBJECT Q_PROPERTY(QString filename READ filename) Q_PROPERTY(double current_time READ current_time WRITE set_current_time NOTIFY current_time_changed) Q_PROPERTY(bool record_to_keyframe READ record_to_keyframe WRITE set_record_to_keyframe NOTIFY record_to_keyframe_changed) Q_PROPERTY(Object* assets READ assets_obj) Q_PROPERTY(QVariantMap metadata READ metadata WRITE set_metadata) public: struct DocumentInfo { QString author; QString description; QStringList keywords; bool empty() const { return author.isEmpty() && description.isEmpty() && keywords.empty(); } }; explicit Document(const QString& filename = {}); ~Document(); QString filename() const; QUuid uuid() const; QVariantMap& metadata(); void set_metadata(const QVariantMap& meta); DocumentInfo& info(); Composition* current_comp(); void set_current_comp(Composition* comp); QUndoStack& undo_stack(); const io::Options& io_options() const; void set_io_options(const io::Options& opt); Q_INVOKABLE glaxnimate::model::DocumentNode* find_by_uuid(const QUuid& n) const; Q_INVOKABLE glaxnimate::model::DocumentNode* find_by_name(const QString& name) const; Q_INVOKABLE QVariantList find_by_type_name(const QString& type_name) const; Q_INVOKABLE bool undo(); Q_INVOKABLE bool redo(); void push_command(QUndoCommand* cmd); FrameTime current_time() const; void set_current_time(FrameTime t); /** * \brief Whether animated values should add keyframes when their value changes */ bool record_to_keyframe() const; void set_record_to_keyframe(bool r); Q_INVOKABLE QString get_best_name(glaxnimate::model::DocumentNode* node, const QString& suggestion={}) const; Q_INVOKABLE void set_best_name(glaxnimate::model::DocumentNode* node, const QString& suggestion={}) const; model::Assets* assets() const; model::CompGraph& comp_graph(); void stretch_time(qreal multiplier); int add_pending_asset(const QString& name, const QUrl& url); int add_pending_asset(const QString& name, const QByteArray& data); int add_pending_asset(const model::PendingAsset& asset); std::vector pending_assets(); void mark_asset_loaded(int pending_id); void clear_pending_assets(); Q_SIGNALS: void filename_changed(const QString& n); void current_time_changing(FrameTime t); void current_time_changed(FrameTime t); void record_to_keyframe_changed(bool r); void graphics_invalidated(); private: Object* assets_obj() const; void decrease_node_name(const QString& old_name); void increase_node_name(const QString& new_name); private: class Private; friend DocumentNode; std::unique_ptr d; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/rect.cpp000664 001750 001750 00000002662 15165022620 033742 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/shapes/rect.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Rect) glaxnimate::math::bezier::Bezier glaxnimate::model::Rect::to_bezier(model::FrameTime t) const { math::bezier::Bezier bezier; QRectF bb = local_bounding_rect(t); float rounded = this->rounded.get_at(t); float max_r = std::min(bb.width()/2, bb.height()/2); if ( rounded > max_r ) rounded = max_r; if ( rounded == 0 && !this->rounded.animated() ) { bezier.add_point(bb.topRight()); bezier.add_point(bb.bottomRight()); bezier.add_point(bb.bottomLeft()); bezier.add_point(bb.topLeft()); } else { QPointF hh(rounded/2, 0); QPointF vh(0, rounded/2); QPointF hd(rounded, 0); QPointF vd(0, rounded); bezier.add_point(bb.topRight()+vd, -vh); bezier.add_point(bb.bottomRight()-vd, {0,0}, vh); bezier.add_point(bb.bottomRight()-hd, hh); bezier.add_point(bb.bottomLeft()+hd, {0,0}, -hh); bezier.add_point(bb.bottomLeft()-vd, vh); bezier.add_point(bb.topLeft()+vd, {0,0}, -vh); bezier.add_point(bb.topLeft()+hd, -hh); bezier.add_point(bb.topRight()-hd, {0,0}, hh); } bezier.close(); if ( reversed.get() ) bezier.reverse(); return bezier; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/mime/mime_serializer.cpp000664 001750 001750 00000003167 15165022620 033656 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/mime/mime_serializer.hpp" #include "glaxnimate/log/log.hpp" #include "glaxnimate/model/object.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" glaxnimate::io::mime::DeserializedData glaxnimate::io::mime::MimeSerializer::from_mime_data(const QMimeData& data) const { if ( !can_deserialize() ) return {}; for ( const QString& mime : mime_types() ) if ( data.hasFormat(mime) ) return deserialize(data.data(mime)); return {}; } void glaxnimate::io::mime::MimeSerializer::message(const QString& message, log::Severity severity) const { log::Log(slug()).log(message, severity); } glaxnimate::io::mime::DeserializedData glaxnimate::io::mime::MimeSerializer::deserialize(const QByteArray&) const { return {}; } glaxnimate::io::mime::DeserializedData::DeserializedData() = default; glaxnimate::io::mime::DeserializedData::DeserializedData(DeserializedData &&) = default; glaxnimate::io::mime::DeserializedData & glaxnimate::io::mime::DeserializedData::operator=(DeserializedData &&) = default; glaxnimate::io::mime::DeserializedData::~DeserializedData() = default; bool glaxnimate::io::mime::DeserializedData::empty() const { return !document || main->shapes.empty(); } void glaxnimate::io::mime::DeserializedData::initialize_data() { document = std::make_unique(""); main = document->assets()->compositions->values.insert(std::make_unique(document.get())); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/inflate_deflate.cpp000664 001750 001750 00000003253 15165022620 036527 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/modifiers/inflate_deflate.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::InflateDeflate) QIcon glaxnimate::model::InflateDeflate::static_tree_icon() { return QIcon::fromTheme("zoom-draw"); } QString glaxnimate::model::InflateDeflate::static_type_name_human() { return i18n("Inflate and Deflate"); } bool glaxnimate::model::InflateDeflate::process_collected() const { return false; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::InflateDeflate::process(glaxnimate::model::FrameTime t, const math::bezier::MultiBezier& mbez) const { if ( mbez.empty() ) return {}; auto amount = this->amount.get_at(t); if ( amount == 0 ) return mbez; QPointF center; qreal count = 0; for ( const auto& bez : mbez.beziers() ) { for ( const auto& point : bez ) { center += point.pos; } count += bez.size(); } if ( count == 0 ) return mbez; center /= count; math::bezier::MultiBezier out; for ( const auto& in_bez : mbez.beziers() ) { math::bezier::Bezier out_bez; for ( const auto& point : in_bez ) { out_bez.points().push_back(math::bezier::Point( math::lerp(point.pos, center, amount), math::lerp(point.tan_in, center, -amount), math::lerp(point.tan_out, center, -amount) )); } if ( in_bez.closed() ) out_bez.close(); out.beziers().push_back(out_bez); } return out; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/brush_style.hpp000664 001750 001750 00000001525 15165022620 034106 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/assets/asset.hpp" namespace glaxnimate::model { class BrushStyle : public Asset { Q_OBJECT public: using User = ReferenceProperty; using Asset::Asset; QIcon instance_icon() const override; virtual QBrush brush_style(FrameTime t) const = 0; virtual QBrush constrained_brush_style(FrameTime t, const QRectF& bounds) const; Q_SIGNALS: void style_changed(); protected: virtual void fill_icon(QPixmap& icon) const = 0; void invalidate_icon() { icon = {}; Q_EMIT style_changed(); } private: mutable std::unique_ptr icon; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/operations.hpp000664 001750 001750 00000001763 15165022620 033541 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/math/bezier/bezier.hpp" namespace glaxnimate::math::bezier { /** * \brief Turns all points in the curve to smooth, sutomatically setting tangents */ void auto_smooth(Bezier& curve, int start, int end); /** * \brief Reduces the number of points in a bezier curve */ void simplify(Bezier& curve, qreal threshold); struct ProjectResult { int index = 0; qreal factor = 0; qreal distance = std::numeric_limits::max(); QPointF point = {}; }; /** * \brief Projects a point onto a bezier curve * \param curve The target bezier * \param p The point to project * \returns ProjectResult with the point on \p curve closest to \p p */ ProjectResult project(const Bezier& curve, const QPointF& p); ProjectResult project(const BezierSegment& segment, const QPointF& p); } // namespace glaxnimate::math mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/pdf_format.hpp000664 001750 001750 00000001623 15165022620 033643 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" namespace glaxnimate::cairo { class PdfFormat : public io::ImportExport { Q_OBJECT public: QString slug() const override { return "pdf"; } QString name() const override { return i18n("Portable Document Format"); } QStringList extensions(Direction direction) const override; bool can_save() const override { return false; } bool can_save_static() const override { return true; } bool can_open() const override { return false; } std::unique_ptr save_settings(model::Composition*) const override; protected: bool on_save_static(QIODevice& dev, const QString&, model::Composition* comp, model::FrameTime time, const QVariantMap&) override; }; } // namespace glaxnimate::cairo mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/trim.cpp000664 001750 001750 00000021335 15165022620 034454 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/modifiers/trim.hpp" #include "glaxnimate/math/bezier/bezier_length.hpp" #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Trim) QIcon glaxnimate::model::Trim::static_tree_icon() { return QIcon::fromTheme("edit-cut"); } QString glaxnimate::model::Trim::static_type_name_human() { return i18n("Trim Path"); } bool glaxnimate::model::Trim::process_collected() const { return multiple.get() == Simultaneously; } static void chunk_start(const glaxnimate::math::bezier::Bezier& in, glaxnimate::math::bezier::Bezier& out, const glaxnimate::math::bezier::LengthData::SplitInfo& split, int max = -1) { using namespace glaxnimate::math::bezier; if ( max == -1 ) max = in.closed_size(); // empty if ( split.ratio == 0 && split.index == 0 && max == in.closed_size() ) { out = in; return; } int index = split.index; // gotta split mid segment if ( split.ratio < 1 && split.ratio > 0 ) { auto split_points = CubicBezierSolver(in.segment(split.index)).split(split.ratio); out.push_back(Point( split_points.first[3], split_points.first[2], split_points.second[1], Smooth )); index += 1; //we use the tangents from the split for the next point if ( index < max ) { out.push_back(Point( split_points.second[3], split_points.second[2], in[index].tan_out, in[index].type )); index += 1; } } for ( int i = index; i < max; i++ ) out.push_back(in[i]); } static void chunk_end(const glaxnimate::math::bezier::Bezier& in, glaxnimate::math::bezier::Bezier& out, const glaxnimate::math::bezier::LengthData::SplitInfo& split, int min = 0) { using namespace glaxnimate::math::bezier; if ( split.ratio == 1 && min == 0 ) { out = in; return; } for ( int i = min; i <= split.index; i++ ) out.push_back(in[i]); // gotta split mid segment if ( split.ratio > 0 ) { auto split_points = CubicBezierSolver(in.segment(split.index)).split(split.ratio); // adjust tangents for the pevious point if ( !out.empty() ) out[out.size()-1].tan_out = split_points.first[1]; out.push_back(Point( split_points.first[3], split_points.first[2], split_points.second[1], Smooth )); } } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Trim::process( glaxnimate::model::FrameTime t, const math::bezier::MultiBezier& mbez ) const { if ( mbez.empty() ) return {}; auto offset = this->offset.get_at(t); auto start = this->start.get_at(t); auto end = this->end.get_at(t); // Normalize Inputs offset = math::fmod(offset, 1.f); start = math::bound(0.f, start, 1.f) + offset; end = math::bound(0.f, end, 1.f) + offset; if ( end < start ) std::swap(start, end); // Handle the degenerate cases if ( math::abs(start * 1000 - end * 1000) < 1 ) return {}; if ( qFuzzyIsNull(start) && qFuzzyCompare(end, 1.f) ) return mbez; // Get the bezier chunk ratios // Note that now 0 <= s < e <= 2 struct Chunk { float start; float end; }; std::vector chunks; if ( end <= 1 ) { // Simplest case, the segment is in [0, 1] chunks.push_back({start, end}); } else if ( start > 1 ) { // The whole segment is outside [0, 1] chunks.push_back({start - 1, end - 1}); } else { // The segment goes over the end point, so we need two splits chunks.push_back({start, 1}); chunks.push_back({0, end - 1}); } const int length_steps = 5; math::bezier::MultiBezier out; math::bezier::LengthData length_data(mbez, length_steps); for ( const auto& chunk : chunks ) { auto start_data = length_data.at_ratio(chunk.start); auto end_data = length_data.at_ratio(chunk.end); /* Chunk of a single curve * * [ bez[0] ... bez[start==end] ... bez[n] ] * aa|BBCCCCDDD|ee * * [ seg[0] ... seg[single_start] ... seg[single_end] ... seg[m] ] * aaaaa|BBBBBBBBBBB|CCC|DDDDDDDD|eeeeee */ if ( start_data.index == end_data.index ) { auto single_start_data = start_data.descend(); auto single_end_data = end_data.descend(); math::bezier::Bezier b; /** * Same bezier segment * [ seg[0] ... seg[single_start=single_end] ... seg[m] ] * aaaaa|BBBBBBBBBBBBBBBB|ccccc */ if ( single_start_data.index == single_end_data.index ) { const auto& in = mbez.beziers()[start_data.index]; int index = single_start_data.index; // split the segment at start qreal ratio_start = single_start_data.ratio; auto truncated_segment = math::bezier::CubicBezierSolver(in.segment(index)).split(ratio_start).second; // find the end ratio for the truncated segment and split it there qreal ratio_end = (single_end_data.ratio - ratio_start) / (1-ratio_start); auto result = math::bezier::CubicBezierSolver(truncated_segment).split(ratio_end).first; // add to the bezier b.push_back(math::bezier::Point( result[0], result[0], result[1], math::bezier::Corner )); b.push_back(math::bezier::Point( result[3], result[2], result[3], math::bezier::Corner )); } else { int start_max = single_end_data.index; if ( single_end_data.index == single_start_data.index + 1 ) start_max += 1; chunk_start(mbez.beziers()[start_data.index], b, single_start_data, start_max); int end_min = qMax(start_max, single_start_data.index + 1); chunk_end(mbez.beziers()[start_data.index], b, single_end_data, end_min); } if ( !b.empty() && !out.beziers().empty() && !out.back().empty() && out.back().back().pos == b[0].pos ) { auto& out_bez = out.back(); out_bez.back().tan_out = b[0].tan_out; out_bez.back().type = math::bezier::Corner; out_bez.points().insert(out_bez.end(), b.begin() + 1, b.end()); } else { out.beziers().push_back(b); } continue; } /* Sequential chunk * * [ bez[0] ... bez[start] ... bez[end] ... bez[n] ] * aa|BBBBBBB|CCC|DDDDD|ee */ out.beziers().reserve(end_data.index - start_data.index); /* we skip the "a" part and get the "B" part * [ seg[0] ... seg[single_start] ... seg[m] ] * aaaaaaaaaaaaaaa|BBBBBBBBBBBBBBBBBBBBBBB */ { math::bezier::Bezier b; auto single_start_data = start_data.descend(); chunk_start(mbez.beziers()[start_data.index], b, single_start_data); out.beziers().push_back(b); } // We get the segment between start and end ("C" part) for ( int i = start_data.index + 1; i < end_data.index; i++ ) { out.beziers().push_back(mbez.beziers()[i]); } /* we get the "D" part and skip the "e" part * [ seg[0] ... seg[single_start] ... seg[m] ] * DDDDDDDDDDDDDDDDDDDDDDD|eeeeeeeeeeeeeee */ if ( end_data.ratio > 0 ) { math::bezier::Bezier b; auto single_end_data = end_data.descend(); chunk_end(mbez.beziers()[end_data.index], b, single_end_data); out.beziers().push_back(b); } } for ( auto& bez : out.beziers() ) { if ( !bez.empty() && math::fuzzy_compare(bez[0].pos, bez.back().pos) ) { bez[0].tan_in = bez.back().tan_in; bez.points().pop_back(); bez.close(); } } return out; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/network_downloader.hpp000664 001750 001750 00000005741 15165022620 035456 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include namespace glaxnimate::model { class NetworkDownloader: public QObject { Q_OBJECT private: struct PendingRequest { PendingRequest(QNetworkReply* reply) : reply(reply) {} PendingRequest(const PendingRequest&) = delete; PendingRequest& operator=(const PendingRequest&) = delete; PendingRequest(PendingRequest&& oth) : reply(oth.reply) { oth.reply = nullptr; } PendingRequest& operator=(PendingRequest&& oth) { std::swap(reply, oth.reply); return *this; } ~PendingRequest() { if ( reply ) { aborted = true; if ( reply->isRunning() ) reply->abort(); reply->deleteLater(); } } QNetworkReply* reply = nullptr; qint64 received = 0; qint64 total = 0; bool aborted = false; }; public: template void get(const QUrl& url, const Func& callback, QObject* receiver = nullptr) { auto reply = manager.get(QNetworkRequest(url)); pending.insert({reply, PendingRequest(reply)}); connect(reply, &QNetworkReply::downloadProgress, this, &NetworkDownloader::on_download_progress); connect(reply, &QNetworkReply::finished, receiver ? receiver : this, [this, reply, callback]{ if ( !reply->error() ) callback(reply->readAll()); auto it = pending.find(reply); if ( it != pending.end() && !it->second.aborted ) { total -= it->second.total; received -= it->second.received; pending.erase(it); if ( pending.empty() ) Q_EMIT download_finished(); } }); } private Q_SLOTS: void on_download_progress(qint64 bytes_received, qint64 bytes_total) { if ( bytes_total == -1 ) bytes_total = 0; QObject* request = sender(); auto it = pending.find(request); if ( it == pending.end() ) return; if ( bytes_total != it->second.total ) { total += bytes_total - it->second.total; it->second.total = bytes_total; } it->second.received = bytes_received; received += bytes_received; if ( bytes_total > 0 ) Q_EMIT download_progress(received, total); } Q_SIGNALS: void download_progress(qint64 bytes_received, qint64 bytes_total); void download_finished(); private: QNetworkAccessManager manager; std::unordered_map pending; qint64 total = 0; qint64 received = 0; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/style/styler.hpp000664 001750 001750 00000002343 15165022620 034205 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/shape.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/property/reference_property.hpp" namespace glaxnimate::model { /** * \brief Base class for elements that add a style */ class Styler : public ShapeOperator { Q_OBJECT GLAXNIMATE_ANIMATABLE(QColor, color, QColor()) GLAXNIMATE_ANIMATABLE(float, opacity, 1, {}, 0, 1, PropertyTraits::Percent) GLAXNIMATE_PROPERTY_REFERENCE(BrushStyle, use, &Styler::valid_uses, &Styler::is_valid_use, &Styler::on_use_changed) public: using ShapeOperator::ShapeOperator; void add_shapes(FrameTime, math::bezier::MultiBezier&, const QTransform&) const override {} protected: QBrush brush(FrameTime t) const; private: std::vector valid_uses() const; bool is_valid_use(DocumentNode* node) const; void on_use_changed(BrushStyle* new_use, BrushStyle* old_use); void on_update_style(); Q_SIGNALS: void use_changed(BrushStyle* new_use); void use_changed_from(BrushStyle* old_use, BrushStyle* new_use); }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_exporter.hpp000664 001750 001750 00000052201 15165022620 036725 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/extraformats/rive/rive_serializer.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/shapes/shapes/rect.hpp" #include "glaxnimate/model/shapes/shapes/ellipse.hpp" #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/shapes/style/fill.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" #include "glaxnimate/module/extraformats/rive/rive_format.hpp" namespace glaxnimate::io::rive { namespace detail { inline const QVariant& noop(const QVariant& v, model::FrameTime) { return v; } } // namespace name class RiveExporter { public: explicit RiveExporter(QIODevice* file, ImportExport* format) : serializer(file), format(format) { serializer.write_header(7, 0, 0); serializer.write_property_table({}); write_object(TypeId::Backboard); } void write_document(model::Document* document) { write_assets(document->assets()->images.get()); for ( const auto& comp : document->assets()->compositions->values ) write_composition(comp.get(), comp->size()); } private: void write_assets(const model::BitmapList* assets) { for ( const auto& image : assets->values ) { write_bitmap(image.get()); } } void write_bitmap(model::Bitmap* image) { auto name = image->name.get(); if ( name.isEmpty() ) name = image->filename.get(); // idk what this is used for, let's just set it to a unique value the lazy way Identifier asset_id = reinterpret_cast(image); auto obj = types.object(TypeId::ImageAsset); if ( !obj ) return; object_ids[image] = next_asset++; obj.set("name", name); obj.set("width", image->width.get()); obj.set("height", image->height.get()); obj.set("assetId", asset_id); serializer.write_object(obj); auto data = image->image_data(); if ( !data.isEmpty() ) { auto contents = types.object(TypeId::FileAssetContents); if ( !contents ) return; obj.set("bytes", data); } } bool write_object(TypeId type, const QVariantMap& props = {}) { auto obj = types.object(type); if ( !obj ) return false; for ( auto it = props.begin(); it != props.end(); ++it ) obj.set(it.key(), *it); serializer.write_object(obj); return true; } void write_composition(model::Composition* comp, QSizeF size) { object_ids[comp] = next_artboard++; next_artboard_child = 1; animations.clear(); if ( !write_object(TypeId::Artboard, { {"name", comp->name.get()}, {"width", size.width()}, {"height", size.height()}, {"x", (24 + size.width()) * (next_artboard - 1)} }) ) return; for ( const auto& shape : comp->shapes ) write_shape(shape.get(), 0); write_object(TypeId::LinearAnimation, {{"loopValue", 1}}); for ( const auto& anim : animations ) { write_object(TypeId::KeyedObject, {{"objectId", QVariant::fromValue(anim.first)}}); for ( const auto& obj : anim.second ) serializer.write_object(obj); } write_object(TypeId::StateMachine, {}); write_object(TypeId::StateMachineLayer, {}); write_object(TypeId::AnimationState, {{"animationId", 0}}); write_object(TypeId::EntryState, {}); write_object(TypeId::StateTransition, {{"stateToId", 0}}); write_object(TypeId::AnyState, {}); write_object(TypeId::ExitState, {}); } void write_shape(model::ShapeElement* element, Identifier parent_id) { auto id = next_artboard_child++; object_ids[element] = id; if ( auto layer = element->cast() ) { auto object = shape_object(TypeId::Node, element, parent_id); write_group(object, layer, id); } else if ( auto group = element->cast() ) { auto object = shape_object(TypeId::Shape, element, parent_id); write_group(object, group, id); } else if ( auto shape = element->cast() ) { write_rect(shape, id, parent_id); } else if ( auto shape = element->cast() ) { write_ellipse(shape, id, parent_id); } else if ( auto shape = element->cast() ) { write_polystar(shape, id, parent_id); } else if ( auto shape = element->cast() ) { auto object = shape_object(TypeId::Fill, element, parent_id); object.set("isVisible", shape->visible.get()); /// \todo fillRule serializer.write_object(object); write_styler(shape, id); } else if ( auto shape = element->cast() ) { auto object = shape_object(TypeId::Stroke, element, parent_id); write_property(object, "thickness", shape->width, id, &detail::noop); object.set("isVisible", shape->visible.get()); /// \todo cap + join serializer.write_object(object); write_styler(shape, id); } else if ( auto shape = element->cast() ) { auto object = shape_object(TypeId::Image, element, parent_id); write_transform(object, shape->transform.get(), id, shape->local_bounding_rect(0)); auto asset_id = object_ids.find(shape->image.get()); if ( asset_id != object_ids.end() ) object.set("assetId", asset_id->second); serializer.write_object(object); } else if ( auto shape = element->cast() ) { write_precomp_layer(shape, id, parent_id); } else if ( auto shape = element->cast() ) { write_path(shape, id, parent_id); } else { serializer.write_object(shape_object(TypeId::Shape, element, parent_id)); } } void write_rect(model::Rect* shape, Identifier id, Identifier parent_id) { auto object = shape_object(TypeId::Rectangle, shape, parent_id); write_position(object, shape->position, id); write_property(object, "width", shape->size, id, [](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.toSizeF().width()); } ); write_property(object, "height", shape->size, id, [](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.toSizeF().height()); } ); write_property(object, "cornerRadiusTL", shape->rounded, id, &detail::noop); write_property(object, "cornerRadiusTR", shape->rounded, id, &detail::noop); write_property(object, "cornerRadiusBL", shape->rounded, id, &detail::noop); write_property(object, "cornerRadiusBR", shape->rounded, id, &detail::noop); serializer.write_object(object); } void write_ellipse(model::Ellipse* shape, Identifier id, Identifier parent_id) { auto object = shape_object(TypeId::Ellipse, shape, parent_id); write_position(object, shape->position, id); write_property(object, "width", shape->size, id, [](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.toSizeF().width()); } ); write_property(object, "height", shape->size, id, [](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.toSizeF().height()); } ); serializer.write_object(object); } void write_polystar(model::PolyStar* shape, Identifier id, Identifier parent_id) { auto type = shape->type.get() == model::PolyStar::Star ? TypeId::Star : TypeId::Polygon; auto object = shape_object(type, shape, parent_id); /// \todo cornerRadius write_position(object, shape->position, id); write_property(object, "points", shape->points, id, &detail::noop); write_property(object, "width", shape->outer_radius, id, &detail::noop); write_property(object, "height", shape->outer_radius, id, &detail::noop); if ( type == TypeId::Star ) { write_property(object, "innerRadius", shape->inner_radius, id, [shape](const QVariant& v, model::FrameTime t) { auto outer = shape->outer_radius.get_at(t); return QVariant::fromValue(qFuzzyIsNull(outer) ? 0 : v.toDouble() / outer); } ); } serializer.write_object(object); } void write_precomp_layer(model::PreCompLayer* shape, Identifier id, Identifier parent_id) { auto object = shape_object(TypeId::Rectangle, shape, parent_id); write_transform(object, shape->transform.get(), id, shape->local_bounding_rect(0)); write_property(object, "opacity", shape->opacity, id, &detail::noop); if ( auto comp = shape->composition.get() ) { Identifier comp_index = 1; for ( const auto& declared_comp : shape->document()->assets()->compositions->values ) { if ( declared_comp.get() == comp ) break; comp_index++; } object.set("artboardId", comp_index); } serializer.write_object(object); } void write_path(model::Path* shape, Identifier id, Identifier parent_id) { auto object = shape_object(TypeId::PointsPath, shape, parent_id); object.set("isClosed", shape->closed.get()); serializer.write_object(object); auto first_point_id = next_artboard_child; auto animated = shape->shape.keyframe_count() > 1; for ( const auto& point: shape->shape.get() ) { Object pobj; auto pto = point.polar_tan_out(); auto pti = point.polar_tan_in(); if ( animated || ( point.type == math::bezier::PointType::Corner && (!qFuzzyIsNull(pto.length) || !qFuzzyIsNull(pti.length)) ) ) { pobj = types.object(TypeId::CubicDetachedVertex); pobj.set("outRotation", pto.angle); pobj.set("outDistance", pto.length); pobj.set("inRotation", pti.angle); pobj.set("inDistance", pti.length); } else if ( point.type == math::bezier::PointType::Symmetrical) { pobj = types.object(TypeId::CubicMirroredVertex); pobj.set("rotation", pto.angle); pobj.set("distance", pto.length); } else if ( point.type == math::bezier::PointType::Smooth ) { pobj = types.object(TypeId::CubicAsymmetricVertex); pobj.set("rotation", pto.angle); pobj.set("outDistance", pto.length); pobj.set("inDistance", pti.length); } else { pobj = types.object(TypeId::StraightVertex); } pobj.set("parentId", id); pobj.set("x", point.pos.x()); pobj.set("y", point.pos.y()); serializer.write_object(pobj); next_artboard_child++; } if ( animated ) { auto type = types.get_type(TypeId::CubicDetachedVertex); const Identifier prop_x = 0; const Identifier prop_y = 1; const Identifier prop_in_rot = 2; const Identifier prop_in_len = 3; const Identifier prop_out_rot = 4; const Identifier prop_out_len = 5; auto kf_type = types.get_type(TypeId::KeyFrameDouble); std::array>, 6> props_template{{ {type->property("x")->id, {}}, {type->property("y")->id, {}}, {type->property("inRotation")->id, {}}, {type->property("inDistance")->id, {}}, {type->property("outRotation")->id, {}}, {type->property("outDistance")->id, {}} }}; int point_count = next_artboard_child - first_point_id; for ( auto pt_id = 0; pt_id < point_count; pt_id++ ) { auto props = props_template; for ( const auto& kf : shape->shape ) { if ( int(pt_id) >= kf.get().size() ) { format->error(i18n("Bezier has mismatching number of points")); continue; } for ( auto& prop: props ) { Object rive_kf(kf_type); /// \todo interpolations rive_kf.set("interpolationType", 1); rive_kf.set("frame", kf.time()); prop.second.push_back(std::move(rive_kf)); } auto point = kf.get()[pt_id]; auto pto = point.polar_tan_out(); auto pti = point.polar_tan_in(); props[prop_x].second.back().set("value", point.pos.x()); props[prop_y].second.back().set("value", point.pos.y()); props[prop_in_rot].second.back().set("value", pti.angle); props[prop_in_len].second.back().set("value", pti.length); props[prop_out_rot].second.back().set("value", pto.angle + math::pi); props[prop_out_len].second.back().set("value", pto.length); } auto& keyed_point = animations[first_point_id + pt_id]; for ( const auto& prop : props ) { keyed_point.emplace_back(types.get_type(TypeId::KeyedProperty)); keyed_point.back().set("propertyKey", prop.first); for ( auto& rkf : prop.second ) keyed_point.emplace_back(std::move(rkf)); } } } } Object shape_object(TypeId type_id, model::DocumentNode* shape, Identifier parent_id) { auto object = types.object(type_id); object.set("name", shape->name.get()); object.set("parentId", parent_id); return object; } void write_group(Object& object, model::Group* group, Identifier id) { write_property(object, "opacity", group->opacity, id, &detail::noop); write_transform(object, group->transform.get(), id, group->local_bounding_rect(0)); serializer.write_object(object); for ( const auto& shape : group->shapes ) write_shape(shape.get(), id); } template void write_property( Object& object, const QString& name, const model::AnimatedProperty& prop, Identifier object_id, const FuncT& transform) { auto rive_prop = object.type().property(name); if ( !rive_prop ) { format->warning(i18n( "Unknown property %1 of %2 (%3, %4)", name, int(object.type().id), types.type_name(object.type().id), prop.object()->type_name_human() )); return; } object.set(rive_prop, transform(prop.value(), 0)); if ( !prop.animated() ) return; const ObjectType* kf_type = nullptr; QString attr; switch ( rive_prop->type ) { case PropertyType::Float: case PropertyType::VarUint: attr = "value"; kf_type = types.get_type(TypeId::KeyFrameDouble); break; case PropertyType::Color: attr = "colorValue"; kf_type = types.get_type(TypeId::KeyFrameColor); break; default: break; } if ( !kf_type ) { format->warning(i18n( "Unknown keyframe type for property %1 of %2 (%3, %4)", name, int(object.type().id), types.type_name(object.type().id), prop.object()->type_name_human() )); return; } auto& keyed_object = animations[object_id]; auto keyed_prop = types.object(TypeId::KeyedProperty); keyed_prop.set("propertyKey", rive_prop->id); keyed_object.emplace_back(std::move(keyed_prop)); for ( const auto& kf : prop ) { Object rive_kf(kf_type); /// \todo interpolations rive_kf.set("interpolationType", 1); rive_kf.set(attr, transform(kf.value(), kf.time())); rive_kf.set("frame", kf.time()); keyed_object.emplace_back(std::move(rive_kf)); } } void write_position(Object& object, const model::AnimatedProperty& prop, Identifier object_id) { write_property(object, "x", prop, object_id, [](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.toPointF().x()); } ); write_property(object, "y", prop, object_id, [](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.toPointF().y()); } ); } void write_transform(Object& object, model::Transform* trans, Identifier object_id, const QRectF& box) { if ( object.type().property("originX") ) { write_position(object, trans->position, object_id); if ( box.width() > 0 ) { write_property(object, "originX", trans->anchor_point, object_id, [&box](const QVariant& v, model::FrameTime) { return QVariant::fromValue( (v.toPointF().x() - box.left()) / box.width() ); } ); } if ( box.height() > 0 ) { write_property(object, "originY", trans->anchor_point, object_id, [&box](const QVariant& v, model::FrameTime) { return QVariant::fromValue( (v.toPointF().y() - box.top()) / box.height() ); } ); } } else { /// \todo Handle animated anchor point auto anchor = trans->anchor_point.get(); write_property(object, "x", trans->position, object_id, [anchor](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.toPointF().x() - anchor.x()); } ); write_property(object, "y", trans->position, object_id, [anchor](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.toPointF().y() - anchor.y()); } ); } write_property(object, "rotation", trans->rotation, object_id, &detail::noop); write_property(object, "scaleX", trans->scale, object_id, [](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.value().x()); } ); write_property(object, "scaleY", trans->scale, object_id, [](const QVariant& v, model::FrameTime) { return QVariant::fromValue(v.value().x()); } ); } void write_styler(model::Styler* shape, Identifier object_id) { auto use = shape->use.get(); auto id = next_artboard_child++; if ( auto grad = use->cast() ) { auto object = shape_object( grad->type.get() == model::Gradient::Radial ? TypeId::RadialGradient : TypeId::LinearGradient, grad, object_id ); write_property(object, "opacity", shape->color, id, &detail::noop); serializer.write_object(object); /// \todo finish } else if ( auto col = use->cast() ) { auto object = shape_object(TypeId::SolidColor, col, object_id); write_property(object, "colorValue", col->color, id, &detail::noop); serializer.write_object(object); } else { auto object = shape_object(TypeId::SolidColor, shape, object_id); write_property(object, "colorValue", shape->color, id, &detail::noop); serializer.write_object(object); } } Identifier next_asset = 0; Identifier next_artboard = 0; Identifier next_artboard_child = 0; std::unordered_map object_ids; RiveSerializer serializer; ImportExport* format; std::unordered_map> animations; TypeSystem types; }; } // namespace glaxnimate::io::rive mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/repeater.cpp000664 001750 001750 00000010160 15165022620 035302 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/modifiers/repeater.hpp" #include #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" using namespace glaxnimate; GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Repeater) QIcon glaxnimate::model::Repeater::static_tree_icon() { return QIcon::fromTheme("table"); } QString glaxnimate::model::Repeater::static_type_name_human() { return i18n("Repeater"); } math::bezier::MultiBezier glaxnimate::model::Repeater::process(FrameTime t, const math::bezier::MultiBezier& mbez) const { QTransform matrix = transform->transform_matrix(t); math::bezier::MultiBezier out; math::bezier::MultiBezier copy = mbez; for ( int i = 0; i < copies.get_at(t); i++ ) { out.append(copy); copy.transform(matrix); } return out; } bool glaxnimate::model::Repeater::process_collected() const { return true; } void glaxnimate::model::Repeater::on_paint(renderer::Renderer* painter, glaxnimate::model::FrameTime t, glaxnimate::model::VisualNode::PaintMode mode, glaxnimate::model::Modifier*) const { QTransform matrix = transform->transform_matrix(t); auto alpha_s = start_opacity.get_at(t); auto alpha_e = end_opacity.get_at(t); int n_copies = copies.get_at(t); auto initial_opacity = painter->opacity(); for ( int i = 0; i < n_copies; i++ ) { float alpha_lerp = float(i) / (n_copies == 1 ? 1 : n_copies - 1); auto alpha = math::lerp(alpha_s, alpha_e, alpha_lerp); painter->set_opacity(alpha * initial_opacity); for ( auto sib : affected() ) { if ( sib->visible.get() ) sib->paint(painter, t, mode); } painter->transform(matrix); } } template> static void increase_transform(glaxnimate::model::AnimatedProperty& into, const glaxnimate::model::AnimatedProperty& from, Func func = {}) { auto from_kf = from.begin(); for ( auto& into_kf : into.mutable_range() ) { into_kf.set(func(into_kf.get(), from_kf->get())); ++from_kf; } into.set(func(into.get(), from.get())); } static void increase_transform(glaxnimate::model::Transform* into, const glaxnimate::model::Transform* from) { increase_transform(into->position, from->position); increase_transform(into->anchor_point, from->anchor_point); increase_transform(into->rotation, from->rotation); increase_transform(into->scale, from->scale, [](const QVector2D& a, const QVector2D& b){ return QVector2D(a.x() * b.x(), a.y() * b.y()); }); } std::unique_ptr glaxnimate::model::Repeater::to_path() const { auto group = std::make_unique(document()); group->name.set(name.get()); group->visible.set(visible.get()); group->locked.set(locked.get()); auto child = std::make_unique(document()); for ( auto sib : affected() ) child->shapes.insert(sib->to_path()); JoinAnimatables anim({&start_opacity, &end_opacity}, JoinAnimatables::NoValues); int n_copies = copies.get(); float alpha_lerp = 1; auto func = [&alpha_lerp](float a, float b){ return math::lerp(a, b, alpha_lerp); }; for ( int i = 0; i < n_copies; i++ ) { alpha_lerp = float(i) / (n_copies == 1 ? 1 : n_copies - 1); auto cloned = static_cast(group->shapes.insert_clone(child.get())); anim.apply_to(&cloned->opacity, func, &start_opacity, &end_opacity); if ( i == 0 ) child->transform->assign_from(transform.get()); else increase_transform(child->transform.get(), transform.get()); } group->set_time(time()); return group; } int glaxnimate::model::Repeater::max_copies() const { int max = copies.get(); for ( const auto& kf : copies ) { int val = kf.get(); if ( val > max ) max = val; } return max; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/qbytearray_hash.hpp000664 001750 001750 00000000645 15165022620 033471 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) namespace std { template<> struct hash { std::size_t operator()(const QByteArray& s) const noexcept { return (size_t) qHash(s); } }; } #endif src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/aep_loader.hpp000664 001750 001750 00000003116 15165022620 035724 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/extraformats/aep/ae_project.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/module/extraformats/aep/aep_format.hpp" #include "glaxnimate/model/assets/assets.hpp" namespace glaxnimate::io::aep { class AepLoader { public: AepLoader(model::Document* document, const Project& project, QDir asset_path, ImportExport* io) : document(document), project(project), asset_path(asset_path), io(io) {} void load_project(); private: struct CompData; struct LayerData; struct ColorInfo { model::NamedColor* asset; const Solid* solid; }; void load_comp(const Composition& comp); void load_asset(const FolderItem* item); void load_layer(LayerData& layer, CompData& data); void asset_layer(model::Layer* layer, const Layer& ae_layer, CompData& data); void shape_layer(model::Layer* layer, const Layer& ae_layer, CompData& data); void text_layer(model::Layer* layer, const Layer& ae_layer, CompData& data); void warning(const QString& msg); void info(const QString& msg); model::Composition* get_comp(Id id); model::Document* document; const Project& project; QDir asset_path; ImportExport* io; std::unordered_map colors; std::unordered_map comps; std::unordered_map images; std::unordered_map asset_size; }; } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/lottie_importer.hpp000664 001750 001750 00000137761 15165022620 034305 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include "glaxnimate/io/lottie/lottie_private_common.hpp" #include "glaxnimate/io/svg/svg_parser.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" namespace glaxnimate::io::lottie::detail { struct FontInfo { QString name; QString family; QString style; }; class LottieImporterState { public: LottieImporterState( model::Document* document, io::lottie::LottieFormat* format ) : document(document), format(format) {} void load(const QJsonObject& json) { load_version(json); load_meta(json["meta"]); main = document->assets()->compositions->values.insert(std::make_unique(document)); auto comps = load_assets(json["assets"].toArray()); load_fonts(json["fonts"]["list"].toArray()); load_composition(json, main); load_comps(comps); } private: void load_version(const QJsonObject& json) { if ( json.contains("v") ) { auto parts = json["v"].toString().split("."); if ( parts.size() == 3 ) { for ( int i = 0; i < 3; i++ ) version[i] = parts[i].toInt(); } } } // https://github.com/airbnb/lottie-web/blob/0d658b34c40d4e81eafdccbf698815346454a899/player/js/utils/DataManager.js#L170 bool is_version_older_than(int major, int minor, int patch) { if (major > version[0]) { return true; } if (version[0] > major) { return false; } if (minor > version[1]) { return true; } if (version[1] > minor) { return false; } if (patch > version[2]) { return true; } if (version[2] > patch) { return false; } return false; } bool animated(const QJsonObject& obj) { if ( obj.contains("a") ) return obj["a"].toInt(); if ( !obj["k"].isArray() ) return 0; auto karr = obj["k"].toArray(); return karr.size() > 0 && karr[0].isObject() && karr[0].toObject().contains("s"); } template auto make_node(model::Document* document) { auto ptr = std::make_unique(document); current_node = ptr.get(); return ptr; } void warning(QString str, const QJsonObject& json) { if ( json.contains("nm") ) str = json["nm"].toString() + ": " + str; Q_EMIT format->warning(str); } void load_stretchable_animation_container(const QJsonObject& json, model::StretchableTime* animation) { animation->start_time.set(json["st"].toDouble()); animation->stretch.set(json["sr"].toDouble(1)); } void load_animation_container(const QJsonObject& json, model::AnimationContainer* animation) { animation->first_frame.set(json["ip"].toDouble()); animation->last_frame.set(json["op"].toDouble()); } void load_composition(const QJsonObject& json, model::Composition* composition) { this->composition = composition; layer_indices.clear(); deferred.clear(); if ( composition != main ) { composition->width.set(main->width.get()); composition->height.set(main->height.get()); composition->fps.set(main->fps.get()); composition->animation->first_frame.set(main->animation->first_frame.get()); composition->animation->last_frame.set(main->animation->last_frame.get()); } if ( json.contains("fr") ) composition->fps.set(json["fr"].toDouble()); if ( json.contains("w") ) composition->width.set(json["w"].toInt()); if ( json.contains("h") ) composition->height.set(json["h"].toInt()); load_animation_container(json, composition->animation.get()); load_basic(json, composition); std::vector layers; std::map lottie_to_array_index; auto raw_layers = json["layers"].toArray(); layers.reserve(layers.size()); // First pass, get index info for ( auto val : raw_layers ) { LayerData data; data.json = val.toObject(); if ( data.json.contains("parent") ) data.has_parent = true; if ( data.json.contains("ind") ) { data.lottie_index = data.json["ind"].toInt(); data.has_index = true; lottie_to_array_index[data.lottie_index] = layers.size(); } data.name = data.json["nm"].toString(); layers.emplace_back(std::move(data)); } // Second pass, figure out matte + parent links for ( std::size_t i = 0; i < layers.size(); i++ ) { auto& layer = layers[i]; if ( layer.json["tt"].toInt() ) { layer.matte_type = layer.json["tt"].toInt(); if ( layer.json.contains("tp") ) { int lottie_index = layer.json["tp"].toInt(); auto it = lottie_to_array_index.find(lottie_index); if ( it == lottie_to_array_index.end() || it->second == int(i) ) { warning(i18n("Mask parent %1 of layer %2 could not be found", lottie_index, layer.identifier()), layer.json); layer.matte_type = -1; } else { layer.matte = &layers[it->second]; } } else if ( i == 0 ) { warning(i18n("Mask parent of layer %1 could not be found", layer.identifier()), layer.json); } else { layer.matte = &layers[i - 1]; } if ( layer.matte ) layer.matte->matte_count += 1; } if ( layer.has_parent ) { int parent_index = layer.json["parent"].toInt(); auto it = lottie_to_array_index.find(parent_index); if ( it == lottie_to_array_index.end() ) { layer.has_parent = false; warning( i18n("Invalid parent layer %1", parent_index), json ); } else { layer.parent_array_index = it->second; layers[it->second].is_parent = true; } } } // Third pass precompose layers that act as mattes for multiple other layers for ( auto& layer : layers ) { if ( layer.matte_count ) { layer.skip = true; if ( layer.matte_count > 1 ) { auto inner_comp = document->assets()->compositions->values.insert(std::make_unique(document)); inner_comp->width.set(main->width.get()); inner_comp->height.set(main->height.get()); inner_comp->fps.set(main->fps.get()); inner_comp->animation->first_frame.set(main->animation->first_frame.get()); inner_comp->animation->last_frame.set(main->animation->last_frame.get()); auto props = load_basic_setup(layer.json); load_properties(inner_comp, fields["DocumentNode"], layer.json, props); if ( inner_comp->name.get().isEmpty() ) document->set_best_name(inner_comp); inner_comp->shapes.insert(create_layer(layer, inner_comp)); // Gotta set this after we create the layer for the inner comp! layer.matte_comp = inner_comp; // Transform already applied within the comp layer.json.remove("ks"); } } } // Fourth pass, create layers for ( auto& layer : layers ) { if ( !layer.skip ) { composition->shapes.insert(create_layer(layer, composition), 0); } } // Fifth pass, assign parents for ( const auto& layer : layers ) { if ( layer.has_parent ) { const auto& parent = layers[layer.parent_array_index]; if ( parent.layer ) layer.layer->parent.set(parent.layer); } } } struct LayerData { QJsonObject json = {}; QString name = {}; bool is_parent = false; bool has_parent = false; int parent_array_index = -1; bool skip = false; int matte_count = 0; int lottie_index = -1; bool has_index = false; int matte_type = -1; LayerData* matte = nullptr; model::Composition* matte_comp = nullptr; model::Layer* layer = nullptr; QString identifier() const { if ( has_index ) return QString::number(lottie_index); return {}; } }; void load_visibility(model::VisualNode* node, const QJsonObject& json) { if ( json.contains("hd") && json["hd"].toBool() ) node->visible.set(false); } std::unique_ptr layer_object_from_ty(LayerData& data, std::set& props) { int ty = 3; // default to null layer if ( !data.json.contains("ty") || !data.json["ty"].isDouble() ) { warning(i18n("Missing type for layer %1", data.identifier()), data.json); } else { ty = data.json["ty"].toInt(); } switch ( ty ) { case 0: // precomp return load_precomp_layer(data.json, props); case 1: // solid color { props.erase("sw"); props.erase("sh"); props.erase("sc"); auto g = make_node(document); auto color_name = data.json["sc"].toString(); auto color = svg::parse_color(color_name); auto stroke = std::make_unique(document); stroke->color.set(color); stroke->visible.set(false); g->shapes.insert(std::move(stroke)); auto fill = std::make_unique(document); fill->color.set(color); g->shapes.insert(std::move(fill)); auto rect = std::make_unique(document); auto w = data.json["sw"].toDouble(); auto h = data.json["sh"].toDouble(); rect->size.set(QSizeF(w, h)); rect->position.set(QPointF(w/2, h/2)); g->shapes.insert(std::move(rect)); return g; } case 2: // image layer { return create_image_layer(data.json, props); } case 3: // empty { auto layer = std::make_unique(document); data.layer = layer.get(); return layer; } case 4: // shape { props.erase("shapes"); auto layer = make_node(document); current_layer = layer.get(); data.layer = layer.get(); load_shapes(layer->shapes, data.json["shapes"].toArray()); return layer; } case 5: // text { props.erase("t"); auto layer = make_node(document); current_layer = layer.get(); data.layer = layer.get(); load_text_layer(layer->shapes, data.json["t"].toObject()); return layer; } default: { QString type = data.json["ty"].toVariant().toString(); auto it = unsupported_layers.find(ty); if ( it != unsupported_layers.end() ) type = *it; warning(i18n("Unsupported layer of type %1", type), data.json); auto layer = std::make_unique(document); data.layer = layer.get(); return layer; } } } std::unique_ptr create_layer(LayerData& data, model::Composition* comp) { std::unique_ptr inner_shape; std::set props = load_basic_setup(data.json); if ( data.matte_comp ) { // rendered as a precomp because multiple layers use it as matte auto precomp = make_node(document); props.erase("td"); load_properties(precomp.get(), fields["DocumentNode"], data.json, props); load_properties(precomp.get(), fields["__Layer__"], data.json, props); load_visibility(precomp.get(), data.json); props.erase("ks"); load_transform(data.json["ks"].toObject(), precomp->transform.get(), &precomp->opacity); precomp->composition.set(data.matte_comp); precomp->unbounded.set(true); inner_shape = std::move(precomp); } else { inner_shape = layer_object_from_ty(data, props); qreal ip = comp->animation->first_frame.get(); qreal op = comp->animation->last_frame.get(); if ( data.layer ) { load_layer(data, props); } else { // Some non-layer composables need to be wrapped within an actual layer if ( data.json.contains("parent") || data.is_parent || !qFuzzyCompare(data.json["ip"].toDouble(ip), ip) || !qFuzzyCompare(data.json["op"].toDouble(op), op) || data.matte || data.json.contains("masksProperties") ) { auto layer = make_node(document); layer->name.set(inner_shape->name.get()); if ( auto composable = inner_shape->cast() ) { layer->blend_mode.set(composable->blend_mode.get()); composable->blend_mode.set(renderer::BlendMode::Normal); } layer->shapes.insert(std::move(inner_shape), 0); data.layer = layer.get(); load_layer(data, props); inner_shape = std::move(layer); } else { ignore_properties(fields["__Layer__"], props); } } } // handle mattes if ( data.matte && data.matte_type > 0 ) { auto mask_layer = std::make_unique(document); load_layer_ip_op(data.json, mask_layer.get()); mask_layer->name.set(data.name); mask_layer->shapes.insert(create_layer(*data.matte, comp)); mask_layer->shapes.insert(std::move(inner_shape)); switch ( data.matte_type ) { case 1: mask_layer->mask->mask.set(model::MaskSettings::Alpha); break; case 2: mask_layer->mask->mask.set(model::MaskSettings::Alpha); mask_layer->mask->inverted.set(true); break; case 3: mask_layer->mask->mask.set(model::MaskSettings::Luma); break; case 4: mask_layer->mask->mask.set(model::MaskSettings::Luma); mask_layer->mask->inverted.set(true); break; } inner_shape = std::move(mask_layer); } load_basic_check(props); return inner_shape; } std::unique_ptr load_precomp_layer(const QJsonObject& json, std::set& props) { auto precomp = make_node(document); load_visibility(precomp.get(), json); load_stretchable_animation_container(json, precomp->timing.get()); for ( const FieldInfo& field : fields["__Layer__"] ) props.erase(field.lottie); load_basic_properties(json, precomp.get(), props); auto comp = precomp_ids[json["refId"].toString()]; if ( comp ) { precomp->composition.set(comp); if ( !json.contains("nm") ) precomp->name.set(comp->name.get()); } props.erase("w"); props.erase("h"); precomp->size.set(QSize( json["w"].toInt(), json["h"].toInt() )); load_composable(json, precomp.get(), props); return precomp; } void load_mask(const QJsonObject& json, model::Group* parent, bool subgroup, model::MaskSettings* mask) { model::Group* group = parent; if ( subgroup ) { auto clip_group_p = make_node(document); group = clip_group_p.get(); parent->shapes.insert(std::move(clip_group_p)); document->set_best_name(group, i18n("Clip")); } char mode = json["mode"].toString("a")[0].toLatin1(); switch ( mode ) { // 'l', 'a' don't need anything // TODO 'i' intersect case 'n': group->visible.set(false); break; case 's': case 'f': case 'd': if ( subgroup ) group->blend_mode.set(renderer::BlendMode::Difference); else mask->inverted.set(!json["inv"].toBool(false)); break; } // TODO this doesn't work with multiple mask properties with different values of "inv" if ( subgroup && json["inv"].toBool(false) ) { mask->inverted.set(true); } auto fill = make_node(document); fill->color.set(QColor(255, 255, 255)); document->set_best_name(fill.get()); load_animated(&fill->opacity, json["o"], {}); group->shapes.insert(std::move(fill)); auto j_stroke = json["x"].toObject(); if ( animated(j_stroke) || j_stroke["k"].toDouble() != 0 ) { auto stroke = make_node(document); stroke->color.set(QColor(255, 255, 255)); load_animated(&stroke->opacity, json["o"], {}); document->set_best_name(stroke.get()); load_animated(&stroke->width, json["x"], {}); group->shapes.insert(std::move(stroke)); } auto path = make_node(document); document->set_best_name(path.get()); load_animated(&path->shape, json["pt"], {}); group->shapes.insert(std::move(path)); } void load_layer_ip_op(const QJsonObject& json, model::Layer* layer) { if ( !json.contains("ip") && !json.contains("op") ) { auto comp = layer->owner_composition(); layer->animation->first_frame.set(comp->animation->first_frame.get()); layer->animation->last_frame.set(comp->animation->last_frame.get()); } else { load_animation_container(json, layer->animation.get()); } } void load_composable(const QJsonObject& json, model::Composable* layer, std::set& props) { props.erase("ks"); props.erase("bm"); load_transform(json["ks"].toObject(), layer->transform.get(), &layer->opacity); layer->blend_mode.set(renderer::BlendMode(json["bm"].toInt(0))); } void load_layer(const LayerData& data, std::set& props) { current_node = current_layer = data.layer; load_layer_ip_op(data.json, data.layer); props.erase("ind"); load_properties(data.layer, fields["DocumentNode"], data.json, props); load_properties(data.layer, fields["Composable"], data.json, props); load_properties(data.layer, fields["__Layer__"], data.json, props); load_composable(data.json, data.layer, props); load_visibility(data.layer, data.json); props.erase("hasMask"); props.erase("masksProperties"); if ( data.json.contains("masksProperties") ) { auto masks = data.json["masksProperties"].toArray(); if ( !masks.empty() ) { data.layer->mask->mask.set(model::MaskSettings::Alpha); // Group clip shapes go into auto clip_p = make_node(document); auto clip = clip_p.get(); data.layer->shapes.insert(std::move(clip_p)); document->set_best_name(clip, i18n("Clip")); if ( masks.size() == 1 ) { load_mask(masks[0].toObject(), clip, false, data.layer->mask.get()); } else { for ( const auto& mask : masks ) { load_mask(mask.toObject(), clip, true, data.layer->mask.get()); } } } } } std::unique_ptr create_image_layer(const QJsonObject& json, std::set& props) { auto image = make_node(document); props.erase("refId"); image->image.set(bitmap_ids[json["refId"].toString()]); load_basic_properties(json, image.get(), props); load_composable(json, image.get(), props); return image; } void load_shapes(model::ShapeListProperty& shapes, const QJsonArray& jshapes) { deferred.clear(); for ( int i = jshapes.size() - 1; i >= 0; i-- ) create_shape(jshapes[i].toObject(), shapes); auto deferred_shapes = std::move(deferred); deferred.clear(); for ( const auto& pair: deferred_shapes ) load_shape(pair.second, static_cast(pair.first)); } void create_shape(const QJsonObject& json, model::ShapeListProperty& shapes) { if ( !json.contains("ty") || !json["ty"].isString() ) { warning(i18n("Missing shape type"), json); return; } QString base_type = json["ty"].toString(); QString type = shape_types.key(base_type); if ( type.isEmpty() ) { type = shape_types_repeat[base_type]; if ( type.isEmpty() ) { // "mm" is marked as unsupported by lottie and it appears in several animations so we ignore the warning if ( base_type != "mm" ) warning(i18n("Unsupported shape type %1", json["ty"].toString()), json); return; } } model::ShapeElement* shape = static_cast( model::Factory::instance().build(type, document) ); if ( !shape ) { warning(i18n("Unsupported shape type %1", json["ty"].toString()), json); return; } deferred.emplace_back(shape, json); shapes.insert(std::unique_ptr(shape), shapes.size()); } std::set load_basic_setup(const QJsonObject& json_obj) { std::set props; for ( auto it = json_obj.begin(); it != json_obj.end(); ++it ) props.insert(it.key()); return props; } void load_basic_check(const std::set& props) { for ( const auto& not_found : props ) { Q_EMIT format->information( i18n("Unknown field %2%1", not_found, object_error_string(nullptr)) ); } } void load_basic(const QJsonObject& json_obj, model::Object* obj) { std::set props = load_basic_setup(json_obj); load_basic_properties(json_obj, obj, props); load_basic_check(props); } void load_basic_properties(const QJsonObject& json_obj, model::Object* obj, std::set& props) { for ( const QMetaObject* mo = obj->metaObject(); mo; mo = mo->superClass() ) load_properties( obj, fields[model::detail::naked_type_name(mo)], json_obj, props ); } void load_basic(const QJsonObject& json_obj, model::DocumentNode* obj) { load_basic(json_obj, static_cast(obj)); if ( obj->name.get().isEmpty() ) document->set_best_name(obj); } void load_transform(const QJsonObject& transform, model::Transform* tf, model::AnimatedPropertyBase* opacity) { load_basic(transform, tf); if ( transform.contains("o") && opacity ) load_animated(opacity, transform["o"], FloatMult(100)); if ( transform.contains("p") ) { auto pos = transform["p"].toObject(); if ( pos.contains("x") && pos.contains("y") ) { model::Document dummydoc(""); model::Object dummy(&dummydoc); model::AnimatedProperty px(&dummy, {}, 0); model::AnimatedProperty py(&dummy, {}, 0); load_animated(&px, pos["x"], {}); load_animated(&py, pos["y"], {}); model::JoinAnimatables join({&px, &py}); join.apply_to(&tf->position, [](float x, float y) -> QPointF { return QPointF(x, y); }, &px, &py); } else { load_animated(&tf->position, transform["p"], {}); } } } void load_styler(model::Styler* styler, const QJsonObject& json_obj) { load_visibility(styler, json_obj); std::set props = load_basic_setup(json_obj); load_basic_properties(json_obj, styler, props); if ( json_obj.contains("fillEnabled") ) styler->visible.set(json_obj["fillEnabled"].toBool()); if ( json_obj["ty"].toString().startsWith('g') ) { auto gradient = document->assets()->gradients->values.insert(std::make_unique(document)); styler->use.set(gradient); auto colors = document->assets()->gradient_colors->values.insert(std::make_unique(document)); gradient->colors.set(colors); load_properties(gradient, fields["Gradient"], json_obj, props); if ( json_obj.contains("h") ) { model::Document dummydoc(""); model::Object dummy(&dummydoc); model::AnimatedProperty length(&dummy, {}, 0); if ( json_obj.contains("h") ) load_animated(&length, json_obj["h"], {}); glaxnimate::model::JoinAnimatables join({&gradient->start_point, &gradient->end_point, &length, &gradient->angle}); join.apply_to(&gradient->highlight, [](const QPointF& p, const QPointF& e, float length, float angle) -> QPointF { angle = math::deg2rad(angle); length = math::length(e - p) * length / 100; return p + math::from_polar(length, angle); }, &gradient->start_point, &gradient->end_point, &length, &gradient->angle); } else { gradient->highlight.set(gradient->start_point.get()); } auto jcolors = json_obj["g"].toObject(); load_animated(&colors->colors, jcolors["k"], GradientLoad{jcolors["p"].toInt()}); } else { load_animated(&styler->color, json_obj["c"], {}); } if ( styler->name.get().isEmpty() ) document->set_best_name(styler); load_basic_check(props); } void load_shape(const QJsonObject& json, model::ShapeElement* shape) { current_node = shape; if ( auto styler = shape->cast() ) return load_styler(styler, json); load_basic(json, shape); load_visibility(shape, json); QString type_name = shape->type_name(); if ( type_name == "Group" ) { auto gr = static_cast(shape); QJsonArray shapes = json["it"].toArray(); QJsonObject transform; for ( int i = shapes.size() - 1; i >= 0; i-- ) { QJsonObject shi = shapes[i].toObject(); if ( shi["ty"] == "tr" ) { transform = shi; transform.remove("ty"); shapes.erase(shapes.begin() + i); break; } } if ( !transform.empty() ) load_transform(transform, gr->transform.get(), &gr->opacity); load_shapes(gr->shapes, shapes); } else if ( type_name == "Repeater" ) { auto repeater = static_cast(shape); QJsonObject transform = json["tr"].toObject(); load_animated(&repeater->start_opacity, transform["so"], FloatMult(100)); load_animated(&repeater->end_opacity, transform["eo"], FloatMult(100)); transform.remove("so"); transform.remove("eo"); transform.remove("ty"); load_transform(transform, repeater->transform.get(), nullptr); } else if ( type_name == "Path" ) { auto path = static_cast(shape); if ( version[0] < 5 && json.contains("closed") ) path->shape.set_closed(json["closed"].toBool()); path->closed.set_value(path->shape.closed()); } } void ignore_properties( const QVector& fields, std::set& avail_obj_keys ) { for ( const FieldInfo& field : fields ) avail_obj_keys.erase(field.lottie); } void load_properties( model::Object* obj, const QVector& fields, const QJsonObject& json_obj, std::set& avail_obj_keys ) { for ( const FieldInfo& field : fields ) { avail_obj_keys.erase(field.lottie); if ( field.mode >= Ignored || !json_obj.contains(field.lottie) ) continue; model::BaseProperty * prop = obj->get_property(field.name); if ( !prop ) { logger.stream() << field.name << "is not a property"; continue; } if ( prop->traits().flags & model::PropertyTraits::Animated ) { load_animated(static_cast(prop), json_obj[field.lottie], field.transform); } else if ( field.mode == AnimatedToStatic ) { load_static(prop, json_obj[field.lottie], field.transform); } else { load_value(prop, json_obj[field.lottie], field.transform); } } } template bool compound_value_2d_raw(const QJsonValue& val, T& out, double mul = 1) { QJsonArray arr = val.toArray(); if ( arr.size() < 2 || !arr[0].isDouble() || !arr[1].isDouble() ) return false; out = T(arr[0].toDouble() * mul, arr[1].toDouble() * mul); return true; } template std::optional compound_value_2d(const QJsonValue& val, double mul = 1) { T v; if ( !compound_value_2d_raw(val, v, mul) ) return {}; return QVariant::fromValue(v); } bool is_scalar(model::BaseProperty * prop) { switch ( prop->traits().type ) { case model::PropertyTraits::Bool: case model::PropertyTraits::Int: case model::PropertyTraits::Float: case model::PropertyTraits::String: case model::PropertyTraits::Uuid: case model::PropertyTraits::Enum: case model::PropertyTraits::Bezier: return true; default: return false; } } bool compound_value_color(const QJsonValue& val, QColor& out) { QJsonArray arr = val.toArray(); // https://github.com/airbnb/lottie-web/blob/0d658b34c40d4e81eafdccbf698815346454a899/player/js/utils/DataManager.js#L329 if ( is_version_older_than(4, 1, 9) ) { auto iter = std::find_if(arr.begin(), arr.end(), [](const QJsonValue& v){ return v.toDouble() > 1; }); if ( iter != arr.end() ) { if ( arr.size() == 3 ) out = QColor::fromRgb( arr[0].toInt(), arr[1].toInt(), arr[2].toInt() ); else if ( arr.size() == 4 ) out = QColor::fromRgb( arr[0].toInt(), arr[1].toInt(), arr[2].toInt(), qMin(255, arr[3].toInt()) ); else return false; return true; } } if ( arr.size() == 3 ) out = QColor::fromRgbF( arr[0].toDouble(), arr[1].toDouble(), arr[2].toDouble() ); else if ( arr.size() == 4 ) out = QColor::fromRgbF( arr[0].toDouble(), arr[1].toDouble(), arr[2].toDouble(), qMin(1., arr[3].toDouble()) ); else return false; return true; } std::optional value_to_variant(model::BaseProperty * prop, const QJsonValue& val) { switch ( prop->traits().type ) { case model::PropertyTraits::Bool: case model::PropertyTraits::Int: case model::PropertyTraits::Float: case model::PropertyTraits::String: return val.toVariant(); case model::PropertyTraits::Uuid: { QUuid uuid = val.toVariant().toUuid(); if ( uuid.isNull() ) uuid = QUuid::createUuid(); return QVariant::fromValue(uuid); } case model::PropertyTraits::Point: return compound_value_2d(val); case model::PropertyTraits::Size: return compound_value_2d(val); case model::PropertyTraits::Scale: return compound_value_2d(val, 0.01); case model::PropertyTraits::Color: { QColor col; if ( compound_value_color(val, col) ) return QVariant::fromValue(col); return {}; } case model::PropertyTraits::Bezier: { QJsonObject jsbez = val.toObject(); math::bezier::Bezier bezier; bezier.set_closed(jsbez["c"].toBool()); QJsonArray pos = jsbez["v"].toArray(); QJsonArray tan_in = jsbez["i"].toArray(); QJsonArray tan_out = jsbez["o"].toArray(); int sz = std::min(pos.size(), std::min(tan_in.size(), tan_out.size())); for ( int i = 0; i < sz; i++ ) { QPointF p, ti, to; if ( !compound_value_2d_raw(pos[i], p) ) { Q_EMIT format->warning( i18n("Invalid bezier point %1 in %2", i, property_error_string(prop)) ); continue; } compound_value_2d_raw(tan_in[i], ti); compound_value_2d_raw(tan_out[i], to); bezier.push_back(math::bezier::Point::from_relative(p, ti, to)); } return QVariant::fromValue(bezier); } case model::PropertyTraits::Enum: return val.toInt(); case model::PropertyTraits::Gradient: return val.toArray().toVariantList(); default: logger.stream(log::Error) << "Unsupported type" << prop->traits().type << "for" << property_error_string(prop); return {}; } } QString object_error_string(model::Object* ignore) { QString str; if ( current_layer && current_node != current_layer ) str = "(" + current_layer->object_name() + ") "; if ( current_node && current_node != ignore ) str += current_node->object_name() + "."; return str; } QString property_error_string(model::BaseProperty * prop) { QString str = object_error_string(prop->object()); str += prop->object()->object_name() + "." + prop->name(); return str; } void load_value(model::BaseProperty * prop, const QJsonValue& val, const TransformFunc& trans) { auto v = value_to_variant(prop, val); if ( !v || !prop->set_value(trans.from_lottie(*v, 0)) ) Q_EMIT format->warning(i18n("Invalid value for %1", prop->name())); } void load_static(model::BaseProperty * prop, const QJsonValue& val, const TransformFunc& trans) { if ( val.isObject() ) { QJsonObject obj = val.toObject(); if ( obj.contains("k") ) { load_value(prop, obj["k"], trans); return; } } load_value(prop, val, trans); } void load_animated(model::AnimatedPropertyBase* prop, const QJsonValue& val, const TransformFunc& trans) { if ( !val.isObject() ) { Q_EMIT format->warning(i18n("Invalid value for %1", property_error_string(prop))); return; } QJsonObject obj = val.toObject(); if ( !obj.contains("k") ) { Q_EMIT format->warning(i18n("Invalid value for %1", property_error_string(prop))); return; } if ( animated(obj) ) { if ( !obj["k"].isArray() ) { Q_EMIT format->warning(i18n("Invalid keyframes for %1", property_error_string(prop))); return; } bool position = prop->traits().type == model::PropertyTraits::Point; auto karr = obj["k"].toArray(); for ( int i = 0; i < karr.size(); i++ ) { QJsonValue jkf = karr[i]; model::FrameTime time = jkf["t"].toDouble(); QJsonValue s = jkf["s"]; if ( s.isUndefined() && i == karr.size() - 1 && i > 0 ) s = karr[i-1].toObject()["e"]; if ( s.isArray() && is_scalar(prop) ) s = s.toArray()[0]; auto v = value_to_variant(prop, s); model::KeyframeBase* kf = nullptr; if ( v ) kf = prop->set_keyframe(time, trans.from_lottie(*v, time)); if ( kf ) { kf->set_transition({ keyframe_bezier_handle(jkf["o"]), keyframe_bezier_handle(jkf["i"]), jkf["h"].toInt() ? model::KeyframeTransition::Special::Hold : model::KeyframeTransition::Special::Normal }); if ( position ) { auto pkf = static_cast*>(kf); QPointF tan_out; compound_value_2d_raw(jkf["to"], tan_out); tan_out += pkf->get(); QPointF tan_in; if ( i > 0 ) compound_value_2d_raw(karr[i-1].toObject()["ti"], tan_in); tan_in += pkf->get(); pkf->set_point({pkf->get(), tan_in, tan_out}); } } else { QString value; if ( !v ) { value = i18n("(null)"); } else { value = v->toString(); if ( value == "" ) value = i18n("(empty)"); value += " "; value += QMetaType(v->userType()).name(); } Q_EMIT format->warning(i18n( "Cannot load keyframe at %1 for %2 with value %3", time, property_error_string(prop), value )); } } } else { load_value(prop, obj["k"], trans); } } qreal keyframe_bezier_handle_comp(const QJsonValue& comp) { if ( comp.isArray() ) return comp[0].toDouble(); return comp.toDouble(); } QPointF keyframe_bezier_handle(const QJsonValue& val) { return {keyframe_bezier_handle_comp(val["x"]), keyframe_bezier_handle_comp(val["y"])}; } std::vector> load_assets(const QJsonArray& assets) { std::vector> comps; for ( const auto& assetv : assets ) { QJsonObject asset = assetv.toObject(); if ( asset.contains("e") && asset.contains("p") && asset.contains("w") ) load_asset_bitmap(asset); else if ( asset.contains("layers") ) comps.emplace_back(asset, load_asset_precomp(asset)); } return comps; } void load_comps(const std::vector>& comps) { for ( const auto& p : comps ) load_composition(p.first, p.second); } void load_asset_bitmap(const QJsonObject& asset) { auto bmp = document->assets()->images->values.insert(std::make_unique(document)); QString id = asset["id"].toString(); if ( bitmap_ids.count(id) ) format->warning(i18n("Duplicate Bitmap ID: %1", id)); bitmap_ids[id] = bmp; if ( asset.contains("nm") ) bmp->name.set(asset["nm"].toString()); if ( asset["e"].toInt() ) { bmp->from_url(QUrl(asset["p"].toString())); } else { QString path = asset["u"].toString(); if ( path.contains("://") ) { path += asset["p"].toString(); bmp->from_url(QUrl(path)); } else { QDir dir(path); bmp->from_file(dir.filePath(asset["p"].toString())); } } } model::Composition* load_asset_precomp(QJsonObject asset) { auto comp = document->assets()->compositions->values.insert(std::make_unique(document)); QString id = asset["id"].toString(); if ( precomp_ids.count(id) ) format->warning(i18n("Duplicate Composition ID: %1", id)); precomp_ids[id] = comp; comp->name.set(id); return comp; } enum class FontOrigin { System = 0, CssUrl = 1, ScriptUrl = 2, FontUrl = 3, }; void load_fonts(const QJsonArray& fonts_arr) { for ( const auto& fontv : fonts_arr ) { QJsonObject font = fontv.toObject(); FontInfo info; info.family = font["fFamily"].toString(); info.name = font["fName"].toString(); info.style = font["fStyle"].toString(); fonts[info.name] = info; FontOrigin font_origin = FontOrigin::System; if ( font.contains("origin") ) { font_origin = FontOrigin(font["origin"].toInt()); } else if ( font.contains("fOrigin") ) { QString fOrigin = font["fOrigin"].toString(); fOrigin.append(" "); switch ( fOrigin[0].toLatin1() ) { case 'n': font_origin = FontOrigin::System; break; case 'g': font_origin = FontOrigin::CssUrl; break; case 't': font_origin = FontOrigin::ScriptUrl; break; case 'p': font_origin = FontOrigin::FontUrl; break; } } switch ( font_origin ) { case FontOrigin::System: // nothing to do break; case FontOrigin::CssUrl: case FontOrigin::FontUrl: // Queue dynamic font loading document->add_pending_asset(info.family, QUrl(font["fPath"].toString())); break; case FontOrigin::ScriptUrl: // idk how these work break; } } } FontInfo get_font(const QString& name) { auto it = fonts.find(name); if ( it != fonts.end() ) return *it; return {"", name, "Regular"}; } void load_text_layer(model::ShapeListProperty& shapes, const QJsonObject& text) { // TODO "a" "m" "p" model::Group* prev = nullptr; model::KeyframeTransition jump({}, {}, model::KeyframeTransition::Special::Hold); for ( const auto& v : text["d"].toObject()["k"].toArray() ) { auto keyframe = v.toObject(); qreal time = keyframe["t"].toDouble(); auto text_document = keyframe["s"].toObject(); auto group = std::make_unique(document); if ( time > 0 ) group->opacity.set_keyframe(0, 0)->set_transition(jump); group->opacity.set_keyframe(time, 1)->set_transition(jump); if ( prev ) prev->opacity.set_keyframe(time, 0)->set_transition(jump); prev = group.get(); auto fill = std::make_unique(document); QColor color; compound_value_color(text_document["fc"], color); fill->color.set(color); group->shapes.insert(std::move(fill)); auto shape = make_node(document); auto font = get_font(text_document["f"].toString()); shape->font->family.set(font.family); shape->font->style.set(font.style); shape->font->size.set(text_document["s"].toDouble()); shape->text.set(text_document["t"].toString().replace('\r', '\n')); group->shapes.insert(std::move(shape)); shapes.insert(std::move(group), shapes.size()); } } void load_meta(const QJsonValue& meta) { if ( !meta.isObject() ) return; document->info().author = meta["a"].toString(); document->info().description = meta["d"].toString(); for ( const auto& kw : meta["k"].toArray() ) document->info().keywords.push_back(kw.toString()); } model::Document* document; io::lottie::LottieFormat* format; QMap layer_indices; std::set invalid_indices; std::vector> deferred; model::Composition* composition = nullptr; log::Log logger{"Lottie Import"}; QMap bitmap_ids; QMap precomp_ids; QMap fonts; model::DocumentNode* current_node = nullptr; model::Layer* current_layer = nullptr; std::array version = {5,5,1}; model::Composition* main = nullptr; }; } // namespace glaxnimate::io::lottie::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/utils/qstring_hash.hpp000664 001750 001750 00000000633 15165022620 032772 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) namespace std { template<> struct hash { std::size_t operator()(const QString& s) const noexcept { return (size_t) qHash(s); } }; } #endif mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/cos.hpp000664 001750 001750 00000031746 15165022620 034506 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include #include #include "glaxnimate/module/extraformats/aep/string_decoder.hpp" namespace glaxnimate::io::aep { enum class CosTokenType { // /foo Identifier, // 123 Number, // (foo) String, // HexString, // true Boolean, // << ObjectStart, // >> ObjectEnd, // [ ArrayStart, // ] ArrayEnd, // null Null, // end of file Eof, }; class CosError : public std::runtime_error { public: CosError(QString message) : runtime_error(message.toStdString()), message(std::move(message)) {} QString message; }; struct CosValue { enum class Index { Null, Number, String, Boolean, Bytes, Object, Array }; using Object = std::unique_ptr>; using Array = std::unique_ptr>; template CosValue(T&& v) : value(std::forward(v)) {} CosValue() = default; CosValue(CosValue& v) = delete; CosValue(const CosValue& v) = delete; CosValue(CosValue&& v) = default; CosValue& operator=(CosValue&& v) = default; template const auto& get() const { if ( Ind != type() ) throw CosError("Invalid COS value type"); return std::get(value); } Index type() const { return Index(value.index()); } std::variant< std::nullptr_t, double, QString, bool, QByteArray, Object, Array > value = nullptr; }; using CosObject = CosValue::Object; using CosArray = CosValue::Array; struct CosToken { CosTokenType type = CosTokenType::Eof; CosValue value = {}; CosToken() = default; CosToken(CosToken&&) = default; CosToken& operator=(CosToken&&) = default; }; class CosLexer { public: CosLexer(QByteArray data) : data(std::move(data)) {} CosToken next_token() { int ch; while ( true ) { ch = get_char(); if ( ch == -1 ) return CosToken(); else if ( ch == '%' ) lex_comment(); else if ( !std::isspace(ch) ) break; } // << if ( ch == '<' ) { ch = get_char(); if ( ch == '<' ) return {CosTokenType::ObjectStart}; else if ( ch == -1 ) throw_lex("<"); else if ( std::isxdigit(ch) ) return lex_hex_string(ch); else throw_lex(QString("<") + QChar(ch)); } // >> if ( ch == '>' ) { auto d = get_char(); if ( d != '>' ) { QString tok{QChar(ch)}; if ( d != -1 ) tok += QChar(d); throw_lex(tok, ">>"); } return {CosTokenType::ObjectEnd}; } // [ if ( ch == '[' ) return {CosTokenType::ArrayStart}; // ] if ( ch == ']' ) return {CosTokenType::ArrayEnd}; // /foo if ( ch == '/' ) { return lex_identifier(); } // (foo) if ( ch == '(' ) { return lex_string(); } // Keyword if ( std::isalpha(ch) ) return lex_keyword(ch); // Number if ( std::isdigit(ch) || ch == '-' || ch == '+' || ch == '.' ) return lex_number(ch); throw_lex(QString() + QChar(ch)); } [[noreturn]] void throw_lex(const QString& token, const QString& exp = {}) { QString msg = "Unknown COS token %1"; msg = msg.arg(token); if ( !exp.isEmpty() ) { msg += ", expected "; msg += exp; } throw CosError(msg); } int get_char() { if ( offset >= data.size() ) return -1; int ch = std::uint8_t(data[offset]); offset += 1; return ch; } void unget() { offset -= 1; if ( offset < 0 ) throw CosError("Buffer underflow"); } void lex_comment() { while ( true ) { auto ch = get_char(); if ( ch == -1 || ch == '\n' ) break; } } CosToken lex_number(int ch) { if ( ch == '.' ) return lex_number_fract(QString(QChar(ch))); else if ( ch == '+' || ch == '-' ) return lex_number_int(get_char(), QChar(ch)); else return lex_number_int(ch, '+'); } CosToken lex_number_int(int ch, QChar sign) { QString head; head += sign; while ( true ) { if ( ch == '.' ) { return lex_number_fract(head + QChar(ch)); } else if ( ch == -1 ) { break; } else if ( std::isdigit(ch) ) { head += QChar(ch); ch = get_char(); } else { unget(); break; } } return {CosTokenType::Number, head.toDouble()}; } CosToken lex_number_fract(QString num) { while ( true ) { int ch = get_char(); if ( ch == -1 ) { break; } else if ( std::isdigit(ch) ) { num += QChar(ch); } else { unget(); break; } } return {CosTokenType::Number, num.toDouble()}; } CosToken lex_keyword(char start) { QString kw(start); while ( true ) { auto ch = get_char(); if ( ch == -1 ) { break; } else if ( std::isalpha(ch) ) { kw += QChar(ch); } else { unget(); break; } } if ( kw == "true" ) return {CosTokenType::Boolean, true}; if ( kw == "false" ) return {CosTokenType::Boolean, false}; if ( kw == "null") return {CosTokenType::Null}; throw CosError("Unknown keyword " + kw); } CosToken lex_string() { QByteArray string; while ( true ) { auto ch = lex_string_char(); if ( ch == -1 ) break; string.push_back(ch); } return {CosTokenType::String, decode_string(string)}; } int lex_string_char() { auto ch = get_char(); if ( ch == -1 ) throw CosError("Unterminated String"); if ( ch == ')' ) return -1; if ( ch == '\\' ) return lex_string_escape(); if ( ch == '\r' ) { if ( get_char() != '\n' ) unget(); return '\n'; } else if ( ch == '\n' ) { if ( get_char() != '\r' ) unget(); return '\n'; } return ch; } bool is_octal(char ch) { return '0' <= ch && ch <= '7'; } char lex_string_escape() { auto ch = get_char(); if ( ch == -1 ) throw CosError("Unterminated string"); switch ( ch ) { case 'b': return '\b'; case 'n': return '\n'; case 'f': return '\f'; case 'r': return '\r'; case '(': case ')': case '\\': return ch; } if ( is_octal(ch) ) { QString octal{QChar(ch)}; for ( auto i = 0; i < 2; i++ ) { ch = get_char(); if ( ch == -1 ) break; if ( !is_octal(ch) ) { unget(); break; } octal += QChar(ch); } return octal.toInt(nullptr, 8); } throw CosError("Invalid escape sequence"); } CosToken lex_hex_string(char head) { QByteArray data; data.push_back(head); while ( true ) { auto ch = get_char(); if ( ch == -1 ) { throw CosError("Unterminated hex string"); } else if ( std::isxdigit(ch) ) { data.push_back(ch); } else if ( ch == '>' ) { if ( data.size() % 2 != 0 ) data.push_back('0'); break; } else if ( !std::isspace(ch) ) { throw CosError(QString("Invalid character in hex string: ") + QChar(ch)); } } return {CosTokenType::HexString, QByteArray::fromHex(data)}; } CosToken lex_identifier() { QString ident = ""; const QString special = "()[]<>/%"; while ( true ) { auto ch = get_char(); if ( ch == -1 ) break; if ( ch < 0x21 || ch > 0x7e ) { unget(); break; } if ( ch == '#' ) { QByteArray hexstr; for ( auto i = 0; i < 2; i++ ) { ch = get_char(); if ( ch == -1 || !std::isxdigit(ch) ) throw CosError("Invalid Identifier"); hexstr += std::uint8_t(ch); } ident += QChar(hexstr.toInt(nullptr, 16)); } else if ( special.indexOf(QChar(ch)) != -1 ) { unget(); break; } else { ident += QChar(ch); } } return {CosTokenType::Identifier, ident}; } private: QByteArray data; int offset = 0; }; class CosParser { public: CosParser(QByteArray data) : lexer(std::move(data)) {} CosValue parse() { lex(); if ( lookahead.type == CosTokenType::Identifier ) return parse_object_content(); auto val = parse_value(); if ( lookahead.type == CosTokenType::Eof ) return val; CosArray arr = parse_array_content(); arr->emplace(arr->begin(), std::move(val)); return arr; } private: CosToken lookahead; CosLexer lexer; void lex() { lookahead = lexer.next_token(); } CosObject parse_object_content() { CosObject value = std::make_unique(); while ( true ) { if ( lookahead.type == CosTokenType::Eof || lookahead.type == CosTokenType::ObjectEnd ) break; expect(CosTokenType::Identifier); auto key = lookahead.value.get(); lex(); auto val = parse_value(); value->emplace(key, std::move(val)); } return value; } void expect(CosTokenType token_type) { if ( lookahead.type != token_type ) throw CosError(QString("Expected token %1, got %2").arg(int(token_type)).arg(int(lookahead.type))); } CosArray parse_array_content() { CosArray value = std::make_unique(); while ( true ) { if ( lookahead.type == CosTokenType::Eof || lookahead.type == CosTokenType::ArrayEnd ) break; value->push_back(parse_value()); } return value; } CosValue parse_value() { CosValue val; switch ( lookahead.type ) { case CosTokenType::String: case CosTokenType::HexString: case CosTokenType::Null: case CosTokenType::Boolean: case CosTokenType::Identifier: case CosTokenType::Number: val = std::move(lookahead.value); lex(); return val; case CosTokenType::ObjectStart: lex(); val = parse_object_content(); expect(CosTokenType::ObjectEnd); lex(); return val; case CosTokenType::ArrayStart: lex(); val = parse_array_content(); expect(CosTokenType::ArrayEnd); lex(); return val; default: throw CosError(QString("Expected token COS value, got %1").arg(int(lookahead.type))); } } }; } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/cairo_renderer.hpp000664 001750 001750 00000027354 15165022620 034516 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/renderer/renderer.hpp" namespace glaxnimate::cairo { using namespace glaxnimate::renderer; /** * \brief Renderer that uses cairo */ class CairoRenderer : public renderer::Renderer { private: int quality; int width = 0; int height = 0; renderer::Fill fill; renderer::Stroke stroke; int mode = renderer::ShapeMode::NothingMode; QImage* target = nullptr; cairo_surface_t* surface = nullptr; cairo_t* canvas = nullptr; struct LayerData { qreal alpha = 1; cairo_operator_t blend_mode = CAIRO_OPERATOR_OVER; cairo_surface_t* mask_surface = nullptr; cairo_t* mask_canvas = nullptr; int mask_flags = 0; }; std::vector layer_data; cairo_pattern_t *pattern = nullptr; void set_brush(const QBrush& brush, qreal opacity) { auto gradient = brush.gradient(); if ( !gradient ) { auto color = brush.color(); cairo_set_source_rgba(canvas, color.redF(), color.greenF(), color.blueF(), color.alphaF() * opacity); return; } if ( gradient->type() == QGradient::RadialGradient ) { const QRadialGradient* rad = static_cast(gradient); pattern = cairo_pattern_create_radial( rad->center().x(), rad->center().y(), rad->centerRadius(), rad->focalPoint().x(), rad->focalPoint().y(), rad->focalRadius() ); } else if ( gradient->type() == QGradient::LinearGradient ) { const QLinearGradient* lin = static_cast(gradient); pattern = cairo_pattern_create_linear( lin->start().x(), lin->start().y(), lin->finalStop().x(), lin->finalStop().y() ); } if ( !pattern ) return; for ( const auto& stop : gradient->stops() ) { const auto& color = stop.second; cairo_pattern_add_color_stop_rgba(pattern, stop.first, color.redF(), color.greenF(), color.blueF(), color.alphaF() * opacity); } cairo_set_source(canvas, pattern); } void create_path(const math::bezier::MultiBezier& path) { cairo_new_path(canvas); for ( const auto& bez : path.beziers() ) { if ( bez.size() == 0 ) continue; cairo_move_to(canvas, bez[0].pos.x(), bez[0].pos.y()); for ( int i = 0; i < bez.size() - 1; i++ ) { const auto& before = bez[i]; const auto& after = bez[i+1]; cairo_curve_to( canvas, before.tan_out.x(), before.tan_out.y(), after.tan_in.x(), after.tan_in.y(), after.pos.x(), after.pos.y() ); } if ( bez.closed() && bez.size() > 1 ) { const auto& before = bez.back(); const auto& after = bez[0]; cairo_curve_to( canvas, before.tan_out.x(), before.tan_out.y(), after.tan_in.x(), after.tan_in.y(), after.pos.x(), after.pos.y() ); cairo_close_path(canvas); } } } cairo_operator_t convert_blend_mode(BlendMode mode) const { switch ( mode ) { case renderer::BlendMode::Normal: return CAIRO_OPERATOR_OVER; case renderer::BlendMode::Add: return CAIRO_OPERATOR_ADD; case renderer::BlendMode::Color: return CAIRO_OPERATOR_HSL_COLOR; case renderer::BlendMode::ColorBurn: return CAIRO_OPERATOR_COLOR_BURN; case renderer::BlendMode::ColorDodge: return CAIRO_OPERATOR_COLOR_DODGE; case renderer::BlendMode::Darken: return CAIRO_OPERATOR_DARKEN; case renderer::BlendMode::Difference: return CAIRO_OPERATOR_DIFFERENCE; case renderer::BlendMode::Exclusion: return CAIRO_OPERATOR_EXCLUSION; case renderer::BlendMode::HardLight: return CAIRO_OPERATOR_HARD_LIGHT; case renderer::BlendMode::Hue: return CAIRO_OPERATOR_HSL_HUE; case renderer::BlendMode::Luminosity: return CAIRO_OPERATOR_HSL_LUMINOSITY; case renderer::BlendMode::Lighten: return CAIRO_OPERATOR_LIGHTEN; case renderer::BlendMode::Multiply: return CAIRO_OPERATOR_MULTIPLY; case renderer::BlendMode::Overlay: return CAIRO_OPERATOR_OVERLAY; case renderer::BlendMode::Saturation: return CAIRO_OPERATOR_HSL_SATURATION; case renderer::BlendMode::Screen: return CAIRO_OPERATOR_SCREEN; case renderer::BlendMode::SoftLight: return CAIRO_OPERATOR_SOFT_LIGHT; } return CAIRO_OPERATOR_OVER; } public: CairoRenderer() : CairoRenderer(5) {} explicit CairoRenderer(int quality) : quality(quality) { } int supported_surfaces() const override { return 0; } void set_cairo_surface(cairo_surface_t* surface, int width, int height) { this->width = width; this->height = height; this->surface = surface; canvas = cairo_create(surface); } void set_image_surface(QImage * destination) override { target = destination; width = target->width(); height = target->height(); surface = cairo_image_surface_create_for_data((unsigned char*)target->bits(), CAIRO_FORMAT_ARGB32, target->width(), target->height(), target->bytesPerLine()); canvas = cairo_create(surface); } bool set_gl_surface(void *, int, int, int) override { return false; } bool set_painter_surface(QPainter *, int, int) override { return false; } void render_start() override { cairo_antialias_t anti; if ( quality < 1 ) anti = CAIRO_ANTIALIAS_NONE; else if ( quality < 4 ) anti = CAIRO_ANTIALIAS_FAST; else if ( quality < 8 ) anti = CAIRO_ANTIALIAS_GOOD; else anti = CAIRO_ANTIALIAS_BEST; cairo_set_antialias(canvas, anti); layer_data.push_back({}); } void render_end() override { layer_data.clear(); cairo_destroy(canvas); cairo_surface_destroy(surface); } void set_fill(const Fill & fill) override { this->fill = fill; mode |= FillMode; } void set_stroke(const Stroke & stroke) override { this->stroke = stroke; mode |= StrokeMode; } void draw_path(const math::bezier::MultiBezier & path) override { if ( mode & FillMode ) { set_brush(fill.brush, fill.opacity); create_path(path); cairo_set_fill_rule(canvas, fill.rule == Qt::OddEvenFill ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); cairo_fill(canvas); if ( pattern ) { cairo_pattern_destroy(pattern); pattern = nullptr; } } if ( mode & StrokeMode ) { set_brush(stroke.pen.brush(), stroke.opacity); cairo_set_line_width(canvas, stroke.pen.width()); auto qcap = stroke.pen.capStyle(); auto cap = qcap == Qt::RoundCap ? CAIRO_LINE_CAP_ROUND : (qcap == Qt::SquareCap ? CAIRO_LINE_CAP_SQUARE : CAIRO_LINE_CAP_BUTT ) ; cairo_set_line_cap(canvas, cap); auto qjoin = stroke.pen.joinStyle(); auto join = qjoin == Qt::RoundJoin ? CAIRO_LINE_JOIN_ROUND : (qjoin == Qt::MiterJoin ? CAIRO_LINE_JOIN_MITER : CAIRO_LINE_JOIN_BEVEL ) ; cairo_set_line_join(canvas, join); cairo_set_miter_limit(canvas, stroke.pen.miterLimit()); create_path(path); cairo_stroke(canvas); if ( pattern ) { cairo_pattern_destroy(pattern); pattern = nullptr; } } mode = NothingMode; } // used by gl stuff which we don't support void fill_rect(const QRectF&, const QBrush&) override {} void fill_pattern(const QRectF&, const QImage&) override {} void layer_start() override { layer_data.push_back({}); cairo_push_group(canvas); } void layer_end() override { auto data = layer_data.back(); layer_data.pop_back(); cairo_pop_group_to_source(canvas); cairo_set_operator(canvas, data.blend_mode); cairo_paint_with_alpha(canvas, data.alpha); if ( data.mask_surface ) { cairo_mask_surface(canvas, data.mask_surface, 0, 0); cairo_destroy(data.mask_canvas); cairo_surface_destroy(data.mask_surface); } } void set_blend_mode(BlendMode mode) override { layer_data.back().blend_mode = convert_blend_mode(mode); } void mask_start(int mask_flags) override { layer_data.back().mask_surface = surface; layer_data.back().mask_canvas = canvas; layer_data.back().mask_flags = mask_flags; surface = cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR_ALPHA, width, height); canvas = cairo_create(surface); if ( mask_flags & MaskFlags::MaskInverted ) { cairo_set_source_rgb(canvas, 1, 1, 1); cairo_paint(canvas); } cairo_push_group(canvas); } void mask_end() override { if ( layer_data.back().mask_flags & MaskFlags::MaskInverted ) { cairo_set_operator(canvas, CAIRO_OPERATOR_CLEAR); } cairo_pop_group_to_source(canvas); cairo_paint(canvas); // cairo_surface_flush(surface); std::swap(canvas, layer_data.back().mask_canvas); std::swap(surface, layer_data.back().mask_surface); } void set_opacity(qreal opacity) override { layer_data.back().alpha = opacity; } qreal opacity() const override { return layer_data.back().alpha; } void clip_rect(const QRectF & rect) override { cairo_rectangle(canvas, rect.left(), rect.top(), rect.width(), rect.height()); cairo_clip(canvas); } void draw_image(const QImage & image) override { // QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); cairo_surface_t *surface = cairo_image_surface_create_for_data( const_cast(image.bits()), CAIRO_FORMAT_ARGB32, image.width(), image.height(), image.bytesPerLine() ); cairo_set_source_surface(canvas, surface, 0, 0); cairo_paint(canvas); cairo_surface_destroy(surface); } void set_quality(int quality) override { this->quality = quality; } void scale(qreal x, qreal y) override { cairo_scale(canvas, x, y); } void translate(qreal x, qreal y) override { cairo_translate(canvas, x, y); } void transform(const QTransform & matrix) override { cairo_matrix_t cm { matrix.m11(), matrix.m12(), matrix.m21(), matrix.m22(), matrix.dx(), matrix.dy() }; cairo_transform(canvas, &cm); } }; } // namespace glaxnimate::cairo src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/type_system.hpp000664 001750 001750 00000007672 15165022620 036431 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/extraformats/rive/type_def.hpp" #include "glaxnimate/utils/qstring_hash.hpp" namespace glaxnimate::io::rive { class Object; struct PropertyAnimation { const Property* property = nullptr; std::vector keyframes = {}; }; class ObjectType { public: ObjectType(TypeId id = TypeId::NoType) : id(id) {} TypeId id = TypeId::NoType; std::vector properties; std::vector definitions; std::unordered_map property_from_id; std::unordered_map property_from_name; const Property* property(const QString& name) const { auto it = property_from_name.find(name); if ( it == property_from_name.end() ) return nullptr; return it->second; } const Property* property(Identifier id) const { auto it = property_from_id.find(id); if ( it == property_from_id.end() ) return nullptr; return it->second; } }; class Object { public: Object(const ObjectType* type = nullptr) : type_(type) {} const ObjectType& type() const { return *type_; } const std::unordered_map& properties() const { return properties_; } template bool set(const QString& name, T value) { if ( auto prop = type_->property(name) ) { properties_[prop].setValue(value); return true; } return false; } bool set(const QString& name, const QVariant& value) { if ( auto prop = type_->property(name) ) { properties_[prop] = value; return true; } return false; } void set(const Property* prop, const QVariant& value) { properties_[prop] = value; } template T get(const QString& name, T value = {}) const { if ( auto prop = type_->property(name) ) { auto it = properties_.find(prop); if ( it != properties_.end() ) return it->second.value(); } return value; } QVariant get_variant(const QString& name) { if ( auto prop = type_->property(name) ) { auto it = properties_.find(prop); if ( it != properties_.end() ) return it->second; } return {}; } bool has(const QString& name) const { if ( auto prop = type_->property(name) ) return properties_.count(prop); return false; } std::vector& animations() { return animations_; } bool has_type(TypeId type) const { for ( const auto& def : type_->definitions ) if ( def->type_id == type ) return true; return false; } std::vector& children() { return children_; } explicit operator bool() const { return type_; } const ObjectDefinition* definition() const { return type_->definitions[0]; } private: const ObjectType* type_; std::unordered_map properties_; std::vector animations_; std::vector children_; }; class TypeSystem : public QObject { Q_OBJECT public: const ObjectDefinition* get_definition(TypeId type_id); const ObjectType* get_type(TypeId type_id); Object object(TypeId type_id) { return Object(get_type(type_id)); } QString type_name(TypeId type_id); Q_SIGNALS: void type_not_found(int type_id); private: bool gather_definitions(ObjectType& type, TypeId type_id); std::unordered_map types; }; } // namespace glaxnimate::io::rive mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/meta_animatable.cpp000664 001750 001750 00000012324 15165022620 035315 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/animation/meta_animatable.hpp" #include "glaxnimate/model/object.hpp" #include "glaxnimate/command/animation_commands.hpp" void glaxnimate::model::MetaKeyframe::set_transition(const KeyframeTransition &) { } std::unique_ptr glaxnimate::model::MetaKeyframe::do_clone() const { auto clone = std::make_unique(time()); clone->external_ = external_; return clone; } class glaxnimate::model::MetaAnimatable::Private { public: Private(Object* object) : object(object) {} std::vector props; Object* object; }; glaxnimate::model::MetaAnimatable::MetaAnimatable(Object *object) : d(std::make_unique(object)) { } glaxnimate::model::MetaAnimatable::~MetaAnimatable() = default; glaxnimate::model::Object *glaxnimate::model::MetaAnimatable::object() const { return d->object; } void glaxnimate::model::MetaAnimatable::add_animatable(AnimatableBase *prop) { connect(prop, &AnimatableBase::keyframe_added, this, &MetaAnimatable::external_keyframe_added); connect(prop, &AnimatableBase::keyframe_removed, this, &MetaAnimatable::external_keyframe_removed); connect(prop, &AnimatableBase::keyframe_moved, this, &MetaAnimatable::external_keyframe_moved); d->props.push_back(prop); } QUndoCommand* glaxnimate::model::MetaAnimatable::command_add_smooth_keyframe(FrameTime time, const QVariant &, bool, QUndoCommand* parent) { if ( !d->props.size() ) return nullptr; auto cmd = new QUndoCommand(command::SetKeyframe::command_name(this, time), parent); for ( auto prop: d->props ) prop->command_add_smooth_keyframe(time, prop->value_at_time(time), true, cmd); return cmd; } QUndoCommand* glaxnimate::model::MetaAnimatable::command_remove_keyframe(FrameTime time, QUndoCommand* parent) { if ( !d->props.size() ) return nullptr; auto cmd = new QUndoCommand(command::SetKeyframe::command_name(this, time), parent); for ( auto prop: d->props ) prop->command_remove_keyframe(time, cmd); return cmd; } QUndoCommand* glaxnimate::model::MetaAnimatable::command_clear_keyframes(QUndoCommand* parent) { if ( !d->props.size() ) return nullptr; auto cmd = new QUndoCommand(command::RemoveAllKeyframes::command_name(this), parent); for ( auto prop: d->props ) prop->command_clear_keyframes(cmd); return cmd; } QUndoCommand* glaxnimate::model::MetaAnimatable::command_set_transition(FrameTime time, const model::KeyframeTransition &transition, QUndoCommand* parent) { if ( !d->props.size() ) return nullptr; auto kf = keyframes_.find(time); if ( kf == keyframes_.end() ) return nullptr; auto cmd = new QUndoCommand(i18n("Set keyframe transition"), parent); for ( auto prop: kf->external() ) prop->command_set_transition(time, transition, cmd); return cmd; } QUndoCommand* glaxnimate::model::MetaAnimatable::command_set_transition_side(FrameTime time, model::KeyframeTransition::Descriptive desc, const QPointF &point, bool before_transition, QUndoCommand* parent) { if ( !d->props.size() ) return nullptr; auto kf = keyframes_.find(time); if ( kf == keyframes_.end() ) return nullptr; auto cmd = new QUndoCommand(i18n("Set keyframe transition"), parent); for ( auto prop: kf->external() ) prop->command_set_transition_side(time, desc, point, before_transition, cmd); return cmd; } QUndoCommand* glaxnimate::model::MetaAnimatable::command_move_keyframe(FrameTime time_before, FrameTime time_after, QUndoCommand* parent) { if ( !d->props.size() ) return nullptr; auto kfit = keyframes_.find(time_before); if ( kfit == keyframes_.end() ) return nullptr; auto kf = &*kfit; auto cmd = new QUndoCommand(i18n("Move Keyframe"), parent); for ( auto prop: kf->external() ) prop->command_move_keyframe(time_before, time_after, cmd); return cmd; } const std::vector &glaxnimate::model::MetaAnimatable::animatables() const { return d->props; } QString glaxnimate::model::MetaAnimatable::visual_name() const { return d->object->object_name(); } void glaxnimate::model::MetaAnimatable::external_keyframe_added(FrameTime time) { auto prop = static_cast(sender()); auto kf = keyframes_.find(time); if ( kf != keyframes_.end() ) { kf->add_property(prop); } else { keyframes_.insert(time, MetaKeyframe(time, {prop})); Q_EMIT keyframe_added(time); } } void glaxnimate::model::MetaAnimatable::external_keyframe_removed(FrameTime time) { auto kf = keyframes_.find(time); if ( kf == keyframes_.end() ) return; auto prop = static_cast(sender()); kf->remove_property(prop); if ( kf->empty() ) { keyframes_.erase(kf); Q_EMIT keyframe_removed(time); } } void glaxnimate::model::MetaAnimatable::external_keyframe_moved(FrameTime from_time, FrameTime to_time) { external_keyframe_removed(from_time); external_keyframe_added(to_time); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/gradient_xml.hpp000664 001750 001750 00000006004 15165022620 036305 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/module/extraformats/aep/cos.hpp" #include "glaxnimate/module/extraformats/aep/ae_project.hpp" namespace glaxnimate::io::aep { CosArray xml_array(const QDomElement& element); CosObject xml_map(const QDomElement& element); CosObject xml_list(const QDomElement& element); CosValue xml_value(const QDomElement& element); inline const CosValue& get_value(const CosObject& v, const QString& key) { return v->at(key); } inline const CosValue& get_value(const CosArray& v, int key) { return v->at(key); } inline const CosValue& get_value(const CosValue& v, const QString& key) { return v.get()->at(key); } inline const CosValue& get_value(const CosValue& v, int key) { return v.get()->at(key); } template const CosValue& get(const V& v) { return v; } template const CosValue& get(const V& v, const H& key, const T&... keys) { return get(get_value(v, key), keys...); } template const auto& get_as(const V& v, const T&... keys) { return get(v, keys...).template get(); } struct GradientStopAlpha { static constexpr const char* const name1 = "Alpha Stops"; static constexpr const char* const name2 = "Stops Alpha"; using Value = double; static Value get(const CosArray::element_type* arr) { return arr->at(2).get(); } }; struct GradientStopColor { static constexpr const char* const name1 = "Color Stops"; static constexpr const char* const name2 = "Stops Color"; using Value = QColor; static Value get(const CosArray::element_type* arr) { return QColor::fromRgbF( arr->at(2).get(), arr->at(3).get(), arr->at(4).get() ); } }; template GradientStops get_gradient_stops(const CosValue& data) { using Stop = GradientStop; GradientStops stops; for ( auto& stop : *get_as(data, Policy::name1, "Stops List") ) { auto& stop_arr = get(stop.second, Policy::name2); auto ptr = stop_arr.template get().get(); qreal offset = get_as(stop_arr, 0); qreal midpoint = get_as(stop_arr, 1); auto value = Policy::get(ptr); stops.push_back(Stop{offset, midpoint, value}); } std::sort(stops.begin(), stops.end(), [](const Stop& a, const Stop& b) { return a.offset <= b.offset; }); return stops; } Gradient parse_gradient_xml(const CosValue& value); Gradient parse_gradient_xml(const QString& xml); } // namespace glaxnimate::io::aep mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/glaxnimate_format.hpp000664 001750 001750 00000002705 15165022620 035403 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/io/base.hpp" #include "glaxnimate/io/io_registry.hpp" namespace glaxnimate::io::glaxnimate { class GlaxnimateFormat : public ImportExport { Q_OBJECT public: static const int format_version; QString slug() const override { return "glaxnimate"; } QString name() const override { return i18n("Glaxnimate Animation"); } // RAWR = Reasonable Animation at Whatever Resolution QStringList extensions(Direction) const override { return {"rawr"}; } bool can_save() const override { return true; } bool can_open() const override { return true; } static QJsonDocument to_json(model::Document* document); static QJsonObject to_json(model::Object* object); static QJsonValue to_json(model::BaseProperty* property); static QJsonValue to_json(const QVariant& value); static QJsonValue to_json(const QVariant& value, model::PropertyTraits traits); static QJsonObject format_metadata(); static GlaxnimateFormat* instance(); protected: bool on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap&) override; bool on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&) override; }; } // namespace glaxnimate::io::glaxnimate mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/settings/settings_group.cpp000664 001750 001750 00000006227 15165022620 034054 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "settings_group.hpp" #include glaxnimate::settings::SettingsGroup::SettingsGroup(QString slug, QString label, const QString& icon, SettingList settings) : slug_(std::move(slug)), label_(std::move(label)), icon_(std::move(icon)), settings_(std::move(settings)) { } glaxnimate::settings::SettingsGroup::SettingsGroup(SettingList settings) : settings_(std::move(settings)) { } QString glaxnimate::settings::SettingsGroup::slug() const { return slug_; } QString glaxnimate::settings::SettingsGroup::label() const { return label_; } QIcon glaxnimate::settings::SettingsGroup::icon() const { return QIcon::fromTheme(icon_); } void glaxnimate::settings::SettingsGroup::load(QSettings& settings) { auto avail_keys = settings.childKeys(); std::set unprocessed_keys(avail_keys.begin(), avail_keys.end()); for ( const Setting& setting : settings_ ) { unprocessed_keys.erase(setting.slug); values_[setting.slug] = settings.value(setting.slug, setting.default_value); if ( setting.side_effects ) setting.side_effects(values_[setting.slug]); } for ( const QString& key : unprocessed_keys ) values_[key] = settings.value(key); } void glaxnimate::settings::SettingsGroup::save(QSettings& settings) { for ( const Setting& setting : settings_ ) settings.setValue(setting.slug, setting.get_variant(values_)); } bool glaxnimate::settings::SettingsGroup::has_visible_settings() const { for ( const auto& set : settings_ ) if ( set.type != Setting::Internal ) return true; return false; } QVariant glaxnimate::settings::SettingsGroup::get_variant(const QString& setting_slug) const { for ( const Setting& setting : settings_ ) if ( setting.slug == setting_slug ) return setting.get_variant(values_); return {}; } bool glaxnimate::settings::SettingsGroup::set_variant(const QString& setting_slug, const QVariant& value) { for ( const Setting& setting : settings_ ) { if ( setting.slug == setting_slug ) { if ( !setting.valid_variant(value) ) return false; values_[setting.slug] = value; if ( setting.side_effects ) setting.side_effects(value); return true; } } return false; } QVariant glaxnimate::settings::SettingsGroup::get_default(const QString& setting_slug) const { for ( const Setting& setting : settings_ ) if ( setting.slug == setting_slug ) return setting.default_value; return {}; } QVariant glaxnimate::settings::SettingsGroup::define(const QString& setting_slug, const QVariant& default_value) { for ( const Setting& setting : settings_ ) if ( setting.slug == setting_slug ) return setting.get_variant(values_); settings_.push_back(Setting{setting_slug, {}, {}, Setting::Internal, default_value}); auto it = values_.find(setting_slug); if ( it != values_.end() ) return *it; return default_value; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/gzip.cpp000664 001750 001750 00000000634 15165022620 032343 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "gzip.hpp" #include using namespace glaxnimate; bool gzip::is_compressed(QIODevice& input) { return input.peek(2) == "\x1f\x8b"; } bool gzip::is_compressed(const QByteArray& input) { return input.size() >= 2 && input[0] == '\x1f' && input[1] == '\x8b'; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/base.hpp000664 001750 001750 00000002224 15165022620 031466 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::command { enum class Id { SetPropertyValue, SetMultipleProperties, SetKeyframe, SetMultipleAnimated, SetPositionBezier, // For additional commands, use values increadising from here CustomCommand, }; template class MergeableCommand : public QUndoCommand { public: int id() const final { return int(id_enum); } bool mergeWith ( const QUndoCommand * other ) final { if ( commit ) return false; auto oth = static_cast(other); if ( static_cast(this)->merge_with(*oth) ) { commit = oth->commit; return true; } return false; } protected: using Parent = MergeableCommand; MergeableCommand(const QString& name, bool commit = true, QUndoCommand* parent = nullptr) : QUndoCommand(name, parent), commit(commit) {} bool commit; }; } // namespace glaxnimate::command mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/bezier.hpp000664 001750 001750 00000023653 15165022620 032640 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/math/bezier/solver.hpp" #include "glaxnimate/math/bezier/point.hpp" #include "glaxnimate/math/bezier/segment.hpp" namespace glaxnimate::math::bezier { using Solver = math::bezier::CubicBezierSolver; class Bezier { public: using value_type = Point; Bezier() = default; explicit Bezier(const Point& initial_point) : points_(1, initial_point) {} explicit Bezier(const QPointF& initial_point) : points_(1, initial_point) {} const std::vector& points() const { return points_; } std::vector& points() { return points_; } int size() const { return points_.size(); } int closed_size() const { return points_.size() + (closed_ ? 1 : 0); } bool empty() const { return points_.empty(); } auto begin() { return points_.begin(); } auto begin() const { return points_.begin(); } auto cbegin() const { return points_.begin(); } auto end() { return points_.end(); } auto end() const { return points_.end(); } auto cend() const { return points_.end(); } void push_back(const Point& p) { points_.push_back(p); } void clear() { points_.clear(); closed_ = false; } const Point& back() const { return points_.back(); } Point& back() { return points_.back(); } const Point& operator[](int index) const { return points_[index % points_.size()]; } Point& operator[](int index) { return points_[index % points_.size()]; } bool closed() const { return closed_; } void set_closed(bool closed) { closed_ = closed; } /** * \brief Inserts a point at the given index * \param index Index to insert the point at * \param p Point to add * \returns \c this, for easy chaining */ Bezier& insert_point(int index, const Point& p) { points_.insert(points_.begin() + qBound(0, index, size()), p); return *this; } /** * \brief Appends a point to the curve (relative tangents) * \see insert_point() */ Bezier& add_point(const QPointF& p, const QPointF& in_t = {0, 0}, const QPointF& out_t = {0, 0}) { points_.push_back(Point::from_relative(p, in_t, out_t)); return *this; } /** * \brief Appends a point with symmetrical (relative) tangents * \see insert_point() */ Bezier& add_smooth_point(const QPointF& p, const QPointF& in_t) { points_.push_back(Point::from_relative(p, in_t, -in_t, Smooth)); return *this; } /** * \brief Closes the bezier curve * \returns \c this, for easy chaining */ Bezier& close() { closed_ = true; return *this; } /** * \brief Line from the last point to \p p * \returns \c this, for easy chaining */ Bezier& line_to(const QPointF& p) { if ( !empty() ) points_.back().tan_out = points_.back().pos; points_.push_back(p); return *this; } /** * \brief Quadratic bezier from the last point to \p dest * \param handle Quadratic bezier handle * \param dest Destination point * \returns \c this, for easy chaining */ Bezier& quadratic_to(const QPointF& handle, const QPointF& dest) { if ( !empty() ) points_.back().tan_out = points_.back().pos + 2.0/3.0 * (handle - points_.back().pos); push_back(dest); points_.back().tan_in = points_.back().pos + 2.0/3.0 * (handle - points_.back().pos); return *this; } /** * \brief Cubic bezier from the last point to \p dest * \param handle1 First cubic bezier handle * \param handle2 Second cubic bezier handle * \param dest Destination point * \returns \c this, for easy chaining */ Bezier& cubic_to(const QPointF& handle1, const QPointF& handle2, const QPointF& dest) { if ( !empty() ) points_.back().tan_out = handle1; push_back(dest); points_.back().tan_in = handle2; return *this; } /** * \brief Reverses the orders of the points */ void reverse(); QRectF bounding_box() const; /** * \brief Split a segmet * \param index index of the point at the beginning of the segment to split * \param factor Value between [0,1] to determine the split point * \post size() increased by one and points[index+1] is the new point */ void split_segment(int index, qreal factor); /** * \brief The point you'd get by calling split_segment(index, factor) */ Point split_segment_point(int index, qreal factor) const; void remove_point(int index) { if ( index >= 0 && index < size() ) points_.erase(points_.begin() + index); } void add_to_painter_path(QPainterPath& out) const; math::bezier::Bezier lerp(const math::bezier::Bezier& other, qreal factor) const; void set_point(int index, const math::bezier::Point& p) { if ( index >= 0 && index < size() ) points_[index] = p; } BezierSegment segment(int index) const; void set_segment(int index, const BezierSegment& s); BezierSegment inverted_segment(int index) const; int segment_count() const; Bezier transformed(const QTransform& t) const; void transform(const QTransform& t); /** * \brief Returns a new bezier with the given points removed */ math::bezier::Bezier removed_points(const std::set& indices) const; /** * \brief For closed beziers, ensure the last segment is present */ void add_close_point(); /** * \brief Sets the given point to the type, and adjusts its tangents as needed * \returns The updated point * \note This doesn't update the bezier itself */ Point point_with_type(int index, math::bezier::PointType point_type) const; private: /** * \brief Solver for the point \p p to the point \p p + 1 */ math::bezier::CubicBezierSolver solver_for_point(int p) const { return segment(p); } std::vector points_; bool closed_ = false; }; class MultiBezier { public: MultiBezier() {} MultiBezier(const QPainterPath& path) { append(path); } MultiBezier(const Bezier& path) { append(path); } const std::vector& beziers() const { return beziers_; } std::vector& beziers() { return beziers_; } Bezier& back() { return beziers_.back(); } const Bezier& back() const { return beziers_.back(); } MultiBezier& move_to(const QPointF& p) { beziers_.push_back(Bezier(p)); at_end = false; return *this; } MultiBezier& line_to(const QPointF& p) { handle_end(); beziers_.back().line_to(p); return *this; } MultiBezier& quadratic_to(const QPointF& handle, const QPointF& dest) { handle_end(); beziers_.back().quadratic_to(handle, dest); return *this; } MultiBezier& cubic_to(const QPointF& handle1, const QPointF& handle2, const QPointF& dest) { handle_end(); beziers_.back().cubic_to(handle1, handle2, dest); return *this; } MultiBezier& close() { if ( !beziers_.empty() ) beziers_.back().close(); at_end = true; return *this; } QRectF bounding_box() const; QPainterPath painter_path() const { QPainterPath p; for ( const Bezier& bez : beziers_ ) bez.add_to_painter_path(p); return p; } void append(const MultiBezier& other) { beziers_.insert(beziers_.end(), other.beziers_.begin(), other.beziers_.end()); } void append(const QPainterPath& path); void append(const QPolygonF& path); void append(const Bezier& path) { if ( path.size() > 1 ) beziers_.push_back(path); } void append(const QLineF& path) { move_to(path.p1()); line_to(path.p2()); } template MultiBezier& operator+= (const T& val) { append(val); return *this; } static MultiBezier from_painter_path(const QPainterPath& path); int size() const { return beziers_.size(); } bool empty() const { return beziers_.empty(); } void clear() { beziers_.clear(); } auto begin() { return beziers_.begin(); } auto begin() const { return beziers_.begin(); } auto cbegin() const { return beziers_.begin(); } auto end() { return beziers_.end(); } auto end() const { return beziers_.end(); } auto cend() const { return beziers_.end(); } const Bezier& operator[](int index) const { return beziers_[index]; } Bezier& operator[](int index) { return beziers_[index]; } void transform(const QTransform& t); glaxnimate::math::bezier::MultiBezier transformed(const QTransform& matrix) const; void translate(const QPointF& p); glaxnimate::math::bezier::MultiBezier translated(const QPointF& p) const; void reverse(); glaxnimate::math::bezier::MultiBezier reversed() const; private: void handle_end() { if ( at_end ) { beziers_.push_back(Bezier()); if ( beziers_.size() > 1 ) beziers_.back().add_point(beziers_[beziers_.size()-2].points().back().pos); at_end = false; } } std::vector beziers_; bool at_end = true; }; } // namespace glaxnimate::math namespace glaxnimate::math { inline bezier::Bezier lerp(const math::bezier::Bezier& a, const math::bezier::Bezier& b, qreal factor) { return a.lerp(b, factor); } } // namespace glaxnimate::math Q_DECLARE_METATYPE(glaxnimate::math::bezier::Bezier) QDataStream &operator<<(QDataStream &out, const glaxnimate::math::bezier::Bezier& val); QDataStream &operator>>(QDataStream &in, glaxnimate::math::bezier::Bezier &val); mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/module.hpp000664 001750 001750 00000004640 15165022620 031714 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/utils/iterator.hpp" #include "glaxnimate/utils/i18n.hpp" #include "glaxnimate/io/io_registry.hpp" namespace glaxnimate::module { /** * @brief Data about external components used by a module */ struct ExternalComponent { QString name; QString description; QString version; QString website; const char* license; }; class Registry; /** * @brief Base class for optional modules */ class Module { public: virtual ~Module() {} virtual std::vector components() const { return {}; } const QString& name() const { return name_; } const QString& version() const { return version_; } protected: Module(const QString& name, const QString& version = {}); template void register_io_classes() { (glaxnimate::io::IoRegistry::instance().register_class(), ...); } /** * @brief Register everything that is needed in here */ virtual void initialize() = 0; friend Registry; QString name_; QString version_; }; class Registry { public: using container = std::vector>; public: class iterator : public utils::RandomAccessIteratorWrapper { public: const Module* operator->() const { return iter->get(); } const Module& operator*() const { return **iter; } private: iterator(InternalIterator it = {}) : Parent(it) {} friend Registry; }; static Registry& instance() { static Registry instance; return instance; } template void install(Args&&... args) { modules.push_back(std::make_unique(std::forward(args)...)); modules.back()->initialize(); } iterator begin() const { return modules.begin(); } iterator end() const { return modules.end(); } private: Registry(); static void register_loaded_modules(Registry& reg); container modules; }; /** * @brief Returns the module registry * @note Calling this initializes the default modules */ Registry& registry(); /** * @brief Initializes the default modules */ void initialize(); } // namespace glaxnimate::module mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/000775 001750 001750 00000000000 15165022620 031027 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/000775 001750 001750 00000000000 15165022620 030663 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/vector.hpp000664 001750 001750 00000010045 15165022620 031371 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include "glaxnimate/math/math.hpp" class QVector2D; class QVector3D; class QVector4D; class QPointF; namespace glaxnimate::math { namespace detail { template struct VecSize; template<> struct VecSize { static constexpr int value = 2; }; template<> struct VecSize { static constexpr int value = 3; }; template<> struct VecSize { static constexpr int value = 4; }; template<> struct VecSize { static constexpr int value = 2; }; template struct VecScalar { using type = std::decay_t()[0])>; }; template<> struct VecScalar { using type = qreal; }; template<> struct VecScalar { using type = qreal; }; template using scalar_type = typename detail::VecScalar>::type; template constexpr const scalar_type& get(const VecT& vt, int off) noexcept { return reinterpret_cast::type*>(&vt)[off]; } template constexpr scalar_type& get(VecT& vt, int off) noexcept { return reinterpret_cast::type*>(&vt)[off]; } template struct LengthHelper { static constexpr scalar_type sumsq(const VecT& v) noexcept { return get(v, d-1) * get(v, d-1) + LengthHelper::sumsq(v); } }; template struct LengthHelper { static constexpr scalar_type sumsq(const VecT& v) noexcept { return get(v, 0) * get(v, 0); } }; } // namespace detail using detail::scalar_type; using detail::get; inline QColor lerp(const QColor& a, const QColor& b, double factor) { return QColor::fromRgbF( lerp(a.redF(), b.redF(), factor), lerp(a.greenF(), b.greenF(), factor), lerp(a.blueF(), b.blueF(), factor), lerp(a.alphaF(), b.alphaF(), factor) ); } template constexpr std::vector lerp(const std::vector& a, const std::vector& b, double factor) { if ( a.size() != b.size() ) return a; std::vector c; c.reserve(a.size()); for ( std::size_t i = 0; i < a.size(); i++ ) c.push_back(lerp(a[i], b[i], factor)); return c; } template constexpr scalar_type length_squared(const VecT& v) { return detail::LengthHelper::value>::sumsq(v); } /** * \brief 2-norm length of a vector */ template constexpr scalar_type length(const VecT& v) { return std::sqrt(length_squared(v)); } /** * \brief 2-norm distance between two points */ template constexpr scalar_type distance(const VecT& a, const VecT& b) { return length(b - a); } /** * \brief Angle to the x axis of a 2D cartesian vector */ template scalar_type angle(const VecT& cartesian) { return std::atan2(detail::get(cartesian, 1), detail::get(cartesian, 0)); } template VecT from_polar(scalar_type length, scalar_type angle) { return {std::cos(angle) * length, std::sin(angle) * length}; } template struct PolarVector { using scalar = scalar_type; scalar length; scalar angle; constexpr PolarVector() noexcept : PolarVector(0, 0) {} constexpr PolarVector(scalar length, scalar angle) noexcept : length(length), angle(angle) {} PolarVector(const VecT& cartesian) : length(math::length(cartesian)), angle(math::angle(cartesian)) {} VecT to_cartesian() const { return {std::cos(angle) * length, std::sin(angle) * length}; } }; template bool fuzzy_compare(const VecT& a, const VecT& b) { for ( int i = 0; i < detail::VecSize::value; i++ ) if ( !qFuzzyCompare(detail::get(a, i), detail::get(b, i)) ) return false; return true; } } // namespace glaxnimate::math mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/validation.cpp000664 001750 001750 00000004332 15165022620 033174 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/lottie/validation.hpp" #include "glaxnimate/model/visitor.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/assets/composition.hpp" using namespace glaxnimate; using namespace glaxnimate::io::lottie; void glaxnimate::io::lottie::ValidationVisitor::on_visit_document(model::Document*, model::Composition* main) { if ( !main ) return; if ( fixed_size.isValid() ) { qreal width = main->height.get(); if ( width != fixed_size.width() ) fmt->error(i18n("Invalid width: %1, should be %2", width, fixed_size.width())); qreal height = main->height.get(); if ( height != fixed_size.height() ) fmt->error(i18n("Invalid height: %1, should be %2", height, fixed_size.height())); } if ( !allowed_fps.empty() ) { qreal fps = main->fps.get(); if ( std::find(allowed_fps.begin(), allowed_fps.end(), fps) == allowed_fps.end() ) { QStringList allowed; for ( auto f : allowed_fps ) allowed.push_back(QString::number(f)); fmt->error(i18n("Invalid fps: %1, should be %2", fps, allowed.join(" or "))); } } if ( max_frames > 0 ) { auto duration = main->animation->duration(); if ( duration > max_frames ) fmt->error(i18n("Too many frames: %1, should be less than %2", duration, max_frames)); } } namespace { class DiscordVisitor : public ValidationVisitor { public: explicit DiscordVisitor(LottieFormat* fmt) : ValidationVisitor(fmt) { allowed_fps.push_back(60); fixed_size = QSize(320, 320); } private: void on_visit(model::DocumentNode * node) override { if ( qobject_cast(node) ) { show_error(node, i18n("Images are not supported"), log::Error); } } }; } // namespace void glaxnimate::io::lottie::validate_discord(model::Document* document, model::Composition* main, glaxnimate::io::lottie::LottieFormat* format) { DiscordVisitor(format).visit(document, main); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/property.cpp000664 001750 001750 00000002211 15165022620 033775 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/property/property.hpp" #include "glaxnimate/model/object.hpp" #include "glaxnimate/command/property_commands.hpp" #include "glaxnimate/model/property/sub_object_property.hpp" #include "glaxnimate/model/animation/meta_animatable.hpp" glaxnimate::model::BaseProperty::BaseProperty(Object* object, const utils::LazyLocalizedString& name, PropertyTraits traits) : object_(object), name_(name), traits_(traits) { if ( object ) object_->add_property(this); } void glaxnimate::model::BaseProperty::value_changed() { object_->property_value_changed(this, value()); } bool glaxnimate::model::BaseProperty::set_undoable ( const QVariant& val, bool commit ) { if ( !valid_value(val) ) return false; object_->push_command(new command::SetPropertyValue(this, value(), val, commit)); return true; } void glaxnimate::model::SubObjectPropertyBase::register_animatable(Object* sub_object) { object()->grouped_animations().add_animatable(&sub_object->grouped_animations()); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/invoke.hpp000664 001750 001750 00000001157 15165022620 031535 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::model::detail { template auto invoke_impl(const FuncT& fun, std::index_sequence, const std::tuple& args) { return fun(std::get(args)...); } template auto invoke(const FuncT& fun, const Args&... t) { return invoke_impl(fun, std::make_index_sequence(), std::make_tuple(t...)); } } // namespace glaxnimate::model::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/lottie_html_format.cpp000664 001750 001750 00000004724 15165022620 034743 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/lottie/lottie_html_format.hpp" #include "glaxnimate/io/lottie/lottie_exporter.hpp" #include "glaxnimate/io/lottie/cbor_write_json.hpp" QByteArray glaxnimate::io::lottie::LottieHtmlFormat::html_head(ImportExport* ie, model::Composition* comp, const QString& extra) { return QString( R"( %4: %5 %3 )") .arg(comp->width.get()) .arg(comp->height.get()) .arg(extra) .arg(comp->object_name()) .arg(ie->name()) .toUtf8() ; } bool glaxnimate::io::lottie::LottieHtmlFormat::on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap& settings) { const char* lottie_web_version = "5.12.2"; file.write(html_head(this, comp, QString("").arg(lottie_web_version) )); file.write(R"(
)") .arg(settings["renderer"].toString()) .toUtf8() ); return true; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/embedded_font.hpp000664 001750 001750 00000002500 15165022620 034314 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/assets/asset.hpp" #include "glaxnimate/model/custom_font.hpp" namespace glaxnimate::model { class EmbeddedFont : public Asset { GLAXNIMATE_OBJECT(EmbeddedFont) GLAXNIMATE_PROPERTY(QByteArray, data, {}, &EmbeddedFont::on_data_changed) GLAXNIMATE_PROPERTY(QString, source_url, {}) GLAXNIMATE_PROPERTY(QString, css_url, {}) Q_PROPERTY(QString family READ family) Q_PROPERTY(QString style_name READ style_name) Q_PROPERTY(int database_index READ database_index) public: EmbeddedFont(model::Document* document); EmbeddedFont(model::Document* document, CustomFont custom_font); QIcon instance_icon() const override; QString type_name_human() const override; QString object_name() const override; bool remove_if_unused(bool clean_lists) override; QString family() const { return custom_font_.family(); } QString style_name() const { return custom_font_.style_name(); } int database_index() const { return custom_font_.database_index(); } const CustomFont& custom_font() const { return custom_font_; } private: void on_data_changed(); CustomFont custom_font_; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/gzip_module.hpp000664 001750 001750 00000000727 15165022620 033720 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/module.hpp" namespace glaxnimate::gzip { class Module : public module::Module { public: Module() : module::Module(i18n("Gzip Compression")) {} std::vector components() const override; protected: void initialize() override; }; } // namespace glaxnimate::gzip mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/000775 001750 001750 00000000000 15165022620 031007 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/raster/raster_mime.hpp000664 001750 001750 00000006723 15165022620 033364 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/io/mime/mime_serializer.hpp" #include "glaxnimate/io/io_registry.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/renderer/renderer.hpp" namespace glaxnimate::io::raster { class RasterMime : public io::mime::MimeSerializer { public: QString slug() const override { return "raster"; } QString name() const override { return i18n("Raster Image"); } QStringList mime_types() const override { return {"image/png"}; } QByteArray serialize(const std::vector& selection) const override { QByteArray data; QBuffer buffer(&data); to_image(selection).save(&buffer, "PNG"); return data; } bool can_deserialize() const override { return true; } void to_mime_data(QMimeData& mime, const std::vector& objects) const override { mime.setImageData(to_image(objects)); } static QImage to_image(const std::vector& selection) { if ( selection.empty() ) return {}; std::vector visual_nodes; visual_nodes.reserve(selection.size()); QRectF box; for ( auto node : selection ) { if ( auto visual = node->cast() ) { visual_nodes.push_back(visual); box |= visual->local_bounding_rect(visual->time()); } } QImage image(box.size().toSize(), QImage::Format_ARGB32); image.fill(Qt::transparent); auto renderer = renderer::RendererRegistry::instance().default_renderer(10); renderer->set_image_surface(&image); renderer->render_start(); renderer->translate(-box.left(), -box.top()); for ( auto visual : visual_nodes ) { visual->paint(renderer.get(), visual->time(), model::VisualNode::Render); } renderer->render_end(); return image; } static QImage frame_to_image(const model::VisualNode* node, model::FrameTime time) { if ( !node ) return {}; QImage image(node->local_bounding_rect(time).size().toSize(), QImage::Format_ARGB32); image.fill(Qt::transparent); auto renderer = renderer::RendererRegistry::instance().default_renderer(10); renderer->set_image_surface(&image); renderer->render_start(); node->paint(renderer.get(), time, model::VisualNode::Render); renderer->render_end(); return image; } io::mime::DeserializedData deserialize(const QByteArray& data) const override { io::mime::DeserializedData out; out.initialize_data(); auto bmp = out.document->assets()->images->values.insert(std::make_unique(out.document.get())); bmp->data.set(data); auto img = std::make_unique(out.document.get()); img->image.set(bmp); QPointF p(bmp->pixmap().width() / 2.0, bmp->pixmap().height() / 2.0); img->transform->anchor_point.set(p); img->transform->position.set(p); out.main->shapes.insert(std::move(img)); return out; } }; } // namespace glaxnimate::io::mime mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/composition.hpp000664 001750 001750 00000005151 15165022620 034105 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/assets/asset.hpp" #include "glaxnimate/model/property/object_list_property.hpp" #include "glaxnimate/model/shapes/composable/layer.hpp" #include "glaxnimate/utils/range.hpp" namespace glaxnimate::model { class Composition : public VisualNode, public AssetBase { GLAXNIMATE_OBJECT(Composition) GLAXNIMATE_PROPERTY_LIST(model::ShapeElement, shapes) GLAXNIMATE_SUBOBJECT(AnimationContainer, animation) // type name default notify validate GLAXNIMATE_PROPERTY(float, fps, 60, &Composition::fps_changed, &Composition::validate_fps) GLAXNIMATE_PROPERTY(float, width, 512, &Composition::width_changed, &Composition::validate_nonzero, PropertyTraits::Visual) GLAXNIMATE_PROPERTY(float, height, 512, &Composition::height_changed, &Composition::validate_nonzero, PropertyTraits::Visual) Q_PROPERTY(QSizeF size READ size) Q_PROPERTY(QRectF rect READ rect) Q_PROPERTY(QRectF content_rect READ content_rect) public: using VisualNode::VisualNode; utils::Range top_level() const { return { Layer::ChildLayerIterator(&shapes, nullptr, 0), Layer::ChildLayerIterator(&shapes, nullptr, shapes.size()) }; } DocumentNode* docnode_child(int index) const override { return shapes[index]; } int docnode_child_count() const override { return shapes.size(); } int docnode_child_index(DocumentNode* dn) const override; QRectF local_bounding_rect(FrameTime t) const override; QIcon tree_icon() const override; QString type_name_human() const override; bool remove_if_unused(bool clean_lists) override; DocumentNode* docnode_parent() const override; QSizeF size() const { return QSizeF(width.get(), height.get()); } QRectF rect() const { return QRectF(QPointF(0, 0), size()); } QRectF content_rect() const; Q_INVOKABLE QImage render_image(float time, QSize size = {}, const QColor& background = {}) const; Q_INVOKABLE QImage render_image() const; Q_SIGNALS: void fps_changed(float fps); void width_changed(float); void height_changed(float); private: bool validate_nonzero(int size) const { return size > 0; } bool validate_out_point(int p) const { return p > 0; } bool validate_fps(float v) const { return v > 0; } }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/000775 001750 001750 00000000000 15165022620 030636 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/cbor_write_json.cpp000664 001750 001750 00000026132 15165022620 034234 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/lottie/cbor_write_json.hpp" #include #include #include #include /****************************************************************************** * These function are mostly taken from Qt code * See * https://github.com/qt/qtbase/blob/dev/src/corelib/serialization/qjsonwriter.cpp * https://github.com/qt/qtbase/blob/dev/src/corelib/text/qstringconverter_p.h ******************************************************************************/ #ifndef __cpp_char8_t enum qchar8_t : uchar {}; #else using qchar8_t = char8_t; #endif struct QUtf8BaseTraits { static const bool isTrusted = false; static const bool allowNonCharacters = true; static const bool skipAsciiHandling = false; static const int Error = -1; static const int EndOfString = -2; static bool isValidCharacter(uint u) { return int(u) >= 0; } static void appendByte(uchar *&ptr, uchar b) { *ptr++ = b; } static void appendByte(qchar8_t *&ptr, qchar8_t b) { *ptr++ = b; } static uchar peekByte(const uchar *ptr, qsizetype n = 0) { return ptr[n]; } static uchar peekByte(const qchar8_t *ptr, int n = 0) { return ptr[n]; } static qptrdiff availableBytes(const uchar *ptr, const uchar *end) { return end - ptr; } static qptrdiff availableBytes(const qchar8_t *ptr, const qchar8_t *end) { return end - ptr; } static void advanceByte(const uchar *&ptr, qsizetype n = 1) { ptr += n; } static void advanceByte(const qchar8_t *&ptr, int n = 1) { ptr += n; } static void appendUtf16(ushort *&ptr, ushort uc) { *ptr++ = uc; } static void appendUtf16(char16_t *&ptr, ushort uc) { *ptr++ = char16_t(uc); } static void appendUcs4(ushort *&ptr, uint uc) { appendUtf16(ptr, QChar::highSurrogate(uc)); appendUtf16(ptr, QChar::lowSurrogate(uc)); } static void appendUcs4(char16_t *&ptr, char32_t uc) { appendUtf16(ptr, QChar::highSurrogate(uc)); appendUtf16(ptr, QChar::lowSurrogate(uc)); } static ushort peekUtf16(const ushort *ptr, qsizetype n = 0) { return ptr[n]; } static ushort peekUtf16(const char16_t *ptr, int n = 0) { return ptr[n]; } static qptrdiff availableUtf16(const ushort *ptr, const ushort *end) { return end - ptr; } static qptrdiff availableUtf16(const char16_t *ptr, const char16_t *end) { return end - ptr; } static void advanceUtf16(const ushort *&ptr, qsizetype n = 1) { ptr += n; } static void advanceUtf16(const char16_t *&ptr, int n = 1) { ptr += n; } // it's possible to output to UCS-4 too static void appendUtf16(uint *&ptr, ushort uc) { *ptr++ = uc; } static void appendUtf16(char32_t *&ptr, ushort uc) { *ptr++ = char32_t(uc); } static void appendUcs4(uint *&ptr, uint uc) { *ptr++ = uc; } static void appendUcs4(char32_t *&ptr, uint uc) { *ptr++ = char32_t(uc); } }; namespace QUtf8Functions { /// returns 0 on success; errors can only happen if \a u is a surrogate: /// Error if \a u is a low surrogate; /// if \a u is a high surrogate, Error if the next isn't a low one, /// EndOfString if we run into the end of the string. template inline int toUtf8(ushort u, OutputPtr &dst, InputPtr &src, InputPtr end) { if (!Traits::skipAsciiHandling && u < 0x80) { // U+0000 to U+007F (US-ASCII) - one byte Traits::appendByte(dst, uchar(u)); return 0; } else if (u < 0x0800) { // U+0080 to U+07FF - two bytes // first of two bytes Traits::appendByte(dst, 0xc0 | uchar(u >> 6)); } else { if (!QChar::isSurrogate(u)) { // U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes if (!Traits::allowNonCharacters && QChar::isNonCharacter(u)) return Traits::Error; // first of three bytes Traits::appendByte(dst, 0xe0 | uchar(u >> 12)); } else { // U+10000 to U+10FFFF - four bytes // need to get one extra codepoint if (Traits::availableUtf16(src, end) == 0) return Traits::EndOfString; ushort low = Traits::peekUtf16(src); if (!QChar::isHighSurrogate(u)) return Traits::Error; if (!QChar::isLowSurrogate(low)) return Traits::Error; Traits::advanceUtf16(src); uint ucs4 = QChar::surrogateToUcs4(u, low); if (!Traits::allowNonCharacters && QChar::isNonCharacter(ucs4)) return Traits::Error; // first byte Traits::appendByte(dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf)); // second of four bytes Traits::appendByte(dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f)); // for the rest of the bytes u = ushort(ucs4); } // second to last byte Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f)); } // last byte Traits::appendByte(dst, 0x80 | (u & 0x3f)); return 0; } } static void objectContentToJson(const QCborMap& p, QByteArray &json, int indent, bool compact); static void arrayContentToJson(const QCborArray& a, QByteArray &json, int indent, bool compact); static inline uchar hexdig(uint u) { return (u < 0xa ? '0' + u : 'a' + u - 0xa); } static QByteArray escapedString(const QString &s) { // give it a minimum size to ensure the resize() below always adds enough space QByteArray ba(qMax(s.length(), 16), Qt::Uninitialized); uchar *cursor = reinterpret_cast(const_cast(ba.constData())); const uchar *ba_end = cursor + ba.length(); const ushort *src = reinterpret_cast(s.constBegin()); const ushort *const end = reinterpret_cast(s.constEnd()); while (src != end) { if (cursor >= ba_end - 6) { // ensure we have enough space int pos = cursor - (const uchar *)ba.constData(); ba.resize(ba.size()*2); cursor = (uchar *)ba.data() + pos; ba_end = (const uchar *)ba.constData() + ba.length(); } uint u = *src++; if (u < 0x80) { if (u < 0x20 || u == 0x22 || u == 0x5c) { *cursor++ = '\\'; switch (u) { case 0x22: *cursor++ = '"'; break; case 0x5c: *cursor++ = '\\'; break; case 0x8: *cursor++ = 'b'; break; case 0xc: *cursor++ = 'f'; break; case 0xa: *cursor++ = 'n'; break; case 0xd: *cursor++ = 'r'; break; case 0x9: *cursor++ = 't'; break; default: *cursor++ = 'u'; *cursor++ = '0'; *cursor++ = '0'; *cursor++ = hexdig(u>>4); *cursor++ = hexdig(u & 0xf); } } else { *cursor++ = (uchar)u; } } else if (QUtf8Functions::toUtf8(u, cursor, src, end) < 0) { // failed to get valid utf8 use JSON escape sequence *cursor++ = '\\'; *cursor++ = 'u'; *cursor++ = hexdig(u>>12 & 0x0f); *cursor++ = hexdig(u>>8 & 0x0f); *cursor++ = hexdig(u>>4 & 0x0f); *cursor++ = hexdig(u & 0x0f); } } ba.resize(cursor - (const uchar *)ba.constData()); return ba; } static void valueToJson(const QCborValue &v, QByteArray &json, int indent, bool compact) { QCborValue::Type type = v.type(); switch (type) { case QCborValue::True: json += "true"; break; case QCborValue::False: json += "false"; break; case QCborValue::Integer: json += QByteArray::number(v.toInteger()); break; case QCborValue::Double: { const double d = v.toDouble(); if (qIsFinite(d)) { QByteArray dstr; if ( compact ) { // prec is weird with 'g' so we emulate it QByteArray f = QByteArray::number(d, 'f', 3); QByteArray e = QByteArray::number(d, 'e', 3); dstr = e.size() < f.size() ? e : f; } else { dstr = QByteArray::number(d, 'g', QLocale::FloatingPointShortest); } if ( dstr.endsWith(".000") ) dstr = dstr.left(dstr.size()-4); json += dstr; } else { json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4) } break; } case QCborValue::String: json += '"'; json += escapedString(v.toString()); json += '"'; break; case QCborValue::Array: json += compact ? "[" : "[\n"; arrayContentToJson(v.toArray(), json, indent + (compact ? 0 : 1), compact); json += QByteArray(4*indent, ' '); json += ']'; break; case QCborValue::Map: json += compact ? "{" : "{\n"; objectContentToJson(v.toMap(), json, indent + (compact ? 0 : 1), compact); json += QByteArray(4*indent, ' '); json += '}'; break; case QCborValue::Null: default: json += "null"; } } static void arrayContentToJson(const QCborArray& a, QByteArray &json, int indent, bool compact) { if ( a.empty() ) return; QByteArray indentString(4*indent, ' '); qsizetype i = 0; while (true) { json += indentString; valueToJson(a.at(i), json, indent, compact); if (++i == a.size()) { if (!compact) json += '\n'; break; } json += compact ? "," : ",\n"; } } static void objectContentToJson(const QCborMap& o, QByteArray &json, int indent, bool compact) { if ( o.empty() ) return; QByteArray indentString(4*indent, ' '); auto it = o.begin(); auto end = o.end(); while (true) { json += indentString; json += '"'; json += escapedString(it.key().toString()); json += compact ? "\":" : "\": "; valueToJson(it.value(), json, indent, compact); ++it; if ( it == end ) { if (!compact) json += '\n'; break; } json += compact ? "," : ",\n"; } } QByteArray glaxnimate::io::lottie::cbor_write_json(const QCborMap &o, bool compact) { QByteArray json; json += compact ? "{" : "{\n"; objectContentToJson(o, json, 0, compact); json += compact ? "}" : "}\n"; return json; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/property_commands.hpp000664 001750 001750 00000005622 15165022620 034326 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/command/base.hpp" #include "glaxnimate/model/property/property.hpp" namespace glaxnimate::command { class SetPropertyValue : public MergeableCommand { public: SetPropertyValue(model::BaseProperty* prop, const QVariant& value, bool commit = true) : SetPropertyValue(prop, prop->value(), value, commit) {} SetPropertyValue(model::BaseProperty* prop, const QVariant& before, const QVariant& after, bool commit = true, const QString& name = {}) : Parent(name.isEmpty() ? i18n("Update %1", prop->name()) : name, commit), prop(prop), before(before), after(after) {} void undo() override { prop->set_value(before); } void redo() override { prop->set_value(after); } bool merge_with(const SetPropertyValue& other) { if ( other.prop != prop ) return false; after = other.after; return true; } private: model::BaseProperty* prop; QVariant before; QVariant after; }; class SetMultipleProperties : public MergeableCommand { public: template SetMultipleProperties( const QString& name, bool commit, const QVector& props, Args... vals ) : SetMultipleProperties(name, props, {}, {QVariant::fromValue(vals)...}, commit) {} /** * \pre props.size() == after.size() && (props.size() == before.size() || before.empty()) * * If before.empty() it will be populated by the properties */ SetMultipleProperties( const QString& name, const QVector& props, const QVariantList& before, const QVariantList& after, bool commit ) : Parent(name, commit), props(props), before(before), after(after) { if ( before.empty() ) for ( auto prop : props ) this->before.push_back(prop->value()); } void undo() override { for ( int i = 0; i < props.size(); i++ ) props[i]->set_value(before[i]); } void redo() override { for ( int i = 0; i < props.size(); i++ ) props[i]->set_value(after[i]); } bool merge_with(const SetMultipleProperties& other) { if ( other.props.size() != props.size() ) return false; for ( int i = 0; i < props.size(); i++ ) if ( props[i] != other.props[i] ) return false; after = other.after; return true; } private: QVector props; QVariantList before; QVariantList after; }; } // namespace glaxnimate::command mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/point.hpp000664 001750 001750 00000005470 15165022620 032506 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include "glaxnimate/math/vector.hpp" #include "glaxnimate/math/math.hpp" namespace glaxnimate::math::bezier { enum PointType { Corner, Smooth, Symmetrical, }; struct Point { QPointF pos; QPointF tan_in; QPointF tan_out; PointType type; Point( const QPointF& pos, const QPointF& tan_in, const QPointF& tan_out, PointType type = Corner ) : pos(pos), tan_in(tan_in), tan_out(tan_out), type(type) {} Point(const QPointF& pos = {0, 0}) : Point(pos, pos, pos, Corner) {} static Point from_relative( const QPointF& pos, const QPointF& tan_in_rel = {0, 0}, const QPointF& tan_out_rel = {0, 0}, PointType type = Corner ) { return {pos, pos+tan_in_rel, pos+tan_out_rel, type}; } QPointF relative_tan_in() const { return tan_in - pos; } QPointF relative_tan_out() const { return tan_out - pos; } PolarVector polar_tan_in() const { return relative_tan_in(); } PolarVector polar_tan_out() const { return relative_tan_out(); } void drag_tan_in(const QPointF& p) { tan_in = p; tan_out = drag_tangent(tan_in, tan_out, pos, type); } void drag_tan_out(const QPointF& p) { tan_out = p; tan_in = drag_tangent(tan_out, tan_in, pos, type); } void adjust_handles_from_type(); void set_point_type(PointType t) { type = t; adjust_handles_from_type(); } static QPointF drag_tangent(const QPointF& dragged, const QPointF& other, const QPointF& pos, PointType type) { if ( type == math::bezier::PointType::Symmetrical ) { return 2*pos - dragged; } else if ( type == math::bezier::PointType::Smooth ) { return math::PolarVector{ math::length(other - pos), pi + math::angle(dragged - pos) }.to_cartesian() + pos; } return other; } void translate(const QPointF& delta) { pos += delta; tan_in += delta; tan_out += delta; } void translate_to(const QPointF& new_pos) { translate(new_pos - pos); } void transform(const QTransform& t); QPointF position() const { return pos; } }; } // namespace glaxnimate::math::bezier Q_DECLARE_METATYPE(glaxnimate::math::bezier::Point) QDataStream &operator<<(QDataStream &out, const glaxnimate::math::bezier::Point& val); QDataStream &operator>>(QDataStream &in, glaxnimate::math::bezier::Point &val); mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/video/video_module.hpp000664 001750 001750 00000000735 15165022620 034211 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/module.hpp" namespace glaxnimate::video { class Module : public module::Module { public: Module() : module::Module(i18n("Video Format Support")) {} std::vector components() const override; protected: void initialize() override; }; } // namespace glaxnimate::video mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/object_list_commands.hpp000664 001750 001750 00000006664 15165022620 034752 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/model/property/object_list_property.hpp" namespace glaxnimate::command { template> class AddObject : public QUndoCommand { public: AddObject( PropT* object_parent, std::unique_ptr object, int position = -1, QUndoCommand* parent = nullptr, const QString& name = {} ) : QUndoCommand(name.isEmpty() ? i18n("Create %1", object->object_name()) : name, parent), object_parent(object_parent), object_(std::move(object)), position(position == -1 ? object_parent->size() : position) {} void undo() override { object_ = object_parent->remove(position); } void redo() override { object_parent->insert(std::move(object_), position); } ItemT* object() const { if ( object_ ) return object_.get(); return (*object_parent)[position]; } private: PropT* object_parent; std::unique_ptr object_; int position; }; template> class RemoveObject : public QUndoCommand { public: RemoveObject(ItemT* object, PropT* object_parent, QUndoCommand* parent = nullptr, QString text = {}) : QUndoCommand(text.isEmpty() ? i18n("Remove %1", object->object_name()) : text, parent), object_parent(object_parent), position(object_parent->index_of(object, -1)) {} RemoveObject(int index, PropT* object_parent, QUndoCommand* parent = nullptr, QString text = {}) : QUndoCommand(text.isEmpty() ? i18n("Remove %1", (*object_parent)[index]->object_name()) : text, parent), object_parent(object_parent), position(index) {} void undo() override { object_parent->insert(std::move(object), position); } void redo() override { object = object_parent->remove(position); } private: PropT* object_parent; std::unique_ptr object; int position; }; template> class MoveObject : public QUndoCommand { public: MoveObject( ItemT* object, PropT* parent_before, PropT* parent_after, int position_after, QUndoCommand* parent = nullptr ) : QUndoCommand(i18n("Move Object"), parent), parent_before(parent_before), position_before(parent_before->index_of(object, -1)), parent_after(parent_after), position_after(position_after) {} void undo() override { if ( parent_before == parent_after ) parent_before->move(position_before, position_after); else if ( auto object = parent_after->remove(position_after) ) parent_before->insert(std::move(object), position_before); } void redo() override { if ( parent_before == parent_after ) parent_before->move(position_before, position_after); else if ( auto object = parent_before->remove(position_before) ) parent_after->insert(std::move(object), position_after); } private: PropT* parent_before; int position_before; PropT* parent_after; int position_after; }; } // namespace glaxnimate::command mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_parser.cpp000664 001750 001750 00000155402 15165022620 032521 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/svg/svg_parser.hpp" #include "glaxnimate/io/svg/svg_parser_private.hpp" #include "glaxnimate/io/svg/enum_map.hpp" using namespace glaxnimate::io::svg::detail; class glaxnimate::io::svg::SvgParser::Private : public SvgParserPrivate { public: Private( model::Document* document, const std::function& on_warning, ImportExport* io, QSize forced_size, model::FrameTime default_time, GroupMode group_mode, QDir default_asset_path ) : SvgParserPrivate(document, on_warning, io, forced_size, default_time), group_mode(group_mode), default_asset_path(default_asset_path) {} protected: void on_parse_prepare(const QDomElement&) override { for ( const auto& p : shape_parsers ) to_process += dom.elementsByTagName(p.first).count(); } QSizeF get_size(const QDomElement& svg) override { return { len_attr(svg, "width", size.width()), len_attr(svg, "height", size.height()) }; } void on_parse(const QDomElement& svg) override { dpi = attr(svg, "inkscape", "export-xdpi", "96").toDouble(); QPointF pos; QVector2D scale{1, 1}; if ( svg.hasAttribute("viewBox") ) { auto vb = split_attr(svg, "viewBox"); if ( vb.size() == 4 ) { qreal vbx = vb[0].toDouble(); qreal vby = vb[1].toDouble(); qreal vbw = vb[2].toDouble(); qreal vbh = vb[3].toDouble(); if ( !forced_size.isValid() ) { if ( !svg.hasAttribute("width") ) size.setWidth(vbw); if ( !svg.hasAttribute("height") ) size.setHeight(vbh); } pos = -QPointF(vbx, vby); if ( vbw != 0 && vbh != 0 ) { scale = QVector2D(size.width() / vbw, size.height() / vbh); if ( forced_size.isValid() ) { auto single = qMin(scale.x(), scale.y()); scale = QVector2D(single, single); } } } } for ( const auto& link_node : ItemCountRange(dom.elementsByTagName("link")) ) { auto link = link_node.toElement(); if ( link.attribute("rel") == "stylesheet" ) { QString url = link.attribute("href"); if ( !url.isEmpty() ) document->add_pending_asset("", QUrl(url)); } } parse_css(); parse_assets(); parse_metadata(); model::Layer* parent_layer = add_layer(&main->shapes); parent_layer->transform.get()->position.set(-pos); parent_layer->transform.get()->scale.set(scale); parent_layer->name.set( attr(svg, "sodipodi", "docname", svg.attribute("id", parent_layer->type_name_human())) ); Style default_style(Style::Map{ {"fill", "black"}, }); parse_children({svg, &parent_layer->shapes, parse_style(svg, default_style), false}); main->name.set( attr(svg, "sodipodi", "docname", "") ); } void parse_shape(const ParseFuncArgs& args) override { if ( handle_mask(args) ) return; parse_shape_impl(args); } private: void parse_css() { CssParser parser(css_blocks); for ( const auto& style : ItemCountRange(dom.elementsByTagName("style")) ) { QString data; for ( const auto & child : ItemCountRange(style.childNodes()) ) { if ( child.isText() || child.isCDATASection() ) data += child.toCharacterData().data(); } if ( data.contains("@font-face") ) document->add_pending_asset("", data.toUtf8()); parser.parse(data); } std::stable_sort(css_blocks.begin(), css_blocks.end()); } void parse_defs(const QDomNode& node) { if ( !node.isElement() ) return; auto defs = node.toElement(); for ( const auto& def : ElementRange(defs) ) { if ( def.tagName().startsWith("animate") ) { QString link = attr(def, "xlink", "href"); if ( link.isEmpty() || link[0] != '#' ) continue; animate_parser.store_animate(link.mid(1), def); } } } void parse_assets() { std::vector later; for ( const auto& domnode : ItemCountRange(dom.elementsByTagName("linearGradient")) ) parse_gradient_node(domnode, later); for ( const auto& domnode : ItemCountRange(dom.elementsByTagName("radialGradient")) ) parse_gradient_node(domnode, later); std::vector unprocessed; while ( !later.empty() && unprocessed.size() != later.size() ) { unprocessed.clear(); for ( const auto& element : later ) parse_brush_style_check(element, unprocessed); std::swap(later, unprocessed); } for ( const auto& defs : ItemCountRange(dom.elementsByTagName("defs")) ) parse_defs(defs); } void parse_gradient_node(const QDomNode& domnode, std::vector& later) { if ( !domnode.isElement() ) return; auto gradient = domnode.toElement(); QString id = gradient.attribute("id"); if ( id.isEmpty() ) return; if ( parse_brush_style_check(gradient, later) ) parse_gradient_nolink(gradient, id); } bool parse_brush_style_check(const QDomElement& element, std::vector& later) { QString link = attr(element, "xlink", "href"); if ( link.isEmpty() ) return true; if ( !link.startsWith("#") ) return false; auto it = brush_styles.find(link); if ( it != brush_styles.end() ) { brush_styles["#" + element.attribute("id")] = it->second; return false; } auto it1 = gradients.find(link); if ( it1 != gradients.end() ) { parse_gradient(element, element.attribute("id"), it1->second); return false; } later.push_back(element); return false; } QGradientStops parse_gradient_stops(const QDomElement& gradient) { QGradientStops stops; for ( const auto& domnode : ItemCountRange(gradient.childNodes()) ) { if ( !domnode.isElement() ) continue; auto stop = domnode.toElement(); if ( stop.tagName() != "stop" ) continue; Style style = parse_style(stop, {}); if ( !style.contains("stop-color") ) continue; QColor color = parse_color(style["stop-color"], QColor()); color.setAlphaF(color.alphaF() * style.get("stop-opacity", "1").toDouble()); stops.push_back({stop.attribute("offset", "0").toDouble(), color}); } utils::sort_gradient(stops); return stops; } void parse_gradient_nolink(const QDomElement& gradient, const QString& id) { QGradientStops stops = parse_gradient_stops(gradient); if ( stops.empty() ) return; if ( stops.size() == 1 ) { auto col = std::make_unique(document); col->name.set(id); col->color.set(stops[0].second); brush_styles["#"+id] = col.get(); auto anim = parse_animated(gradient.firstChildElement("stop")); for ( const auto& kf : anim.single("stop-color") ) col->color.set_keyframe(kf.time, kf.values.color())->set_transition(kf.transition); document->assets()->colors->values.insert(std::move(col)); return; } auto colors = std::make_unique(document); colors->name.set(id); colors->colors.set(stops); gradients["#"+id] = colors.get(); auto ptr = colors.get(); document->assets()->gradient_colors->values.insert(std::move(colors)); parse_gradient(gradient, id, ptr); } void parse_gradient(const QDomElement& element, const QString& id, model::GradientColors* colors) { auto gradient = std::make_unique(document); QTransform gradient_transform; if ( element.hasAttribute("gradientTransform") ) gradient_transform = svg_transform(element.attribute("gradientTransform"), {}).transform; if ( element.tagName() == "linearGradient" ) { if ( !element.hasAttribute("x1") || !element.hasAttribute("x2") || !element.hasAttribute("y1") || !element.hasAttribute("y2") ) return; gradient->type.set(model::Gradient::Linear); gradient->start_point.set(gradient_transform.map(QPointF( len_attr(element, "x1"), len_attr(element, "y1") ))); gradient->end_point.set(gradient_transform.map(QPointF( len_attr(element, "x2"), len_attr(element, "y2") ))); auto anim = parse_animated(element); for ( const auto& kf : anim.joined({"x1", "y1"}) ) gradient->start_point.set_keyframe(kf.time, {kf.values[0].vector()[0], kf.values[1].vector()[0]})->set_transition(kf.transition); for ( const auto& kf : anim.joined({"x2", "y2"}) ) gradient->end_point.set_keyframe(kf.time, {kf.values[0].vector()[0], kf.values[1].vector()[0]})->set_transition(kf.transition); } else if ( element.tagName() == "radialGradient" ) { if ( !element.hasAttribute("cx") || !element.hasAttribute("cy") || !element.hasAttribute("r") ) return; gradient->type.set(model::Gradient::Radial); QPointF c = QPointF( len_attr(element, "cx"), len_attr(element, "cy") ); gradient->start_point.set(gradient_transform.map(c)); if ( element.hasAttribute("fx") ) gradient->highlight.set(gradient_transform.map(QPointF( len_attr(element, "fx"), len_attr(element, "fy") ))); else gradient->highlight.set(gradient_transform.map(c)); gradient->end_point.set(gradient_transform.map(QPointF( c.x() + len_attr(element, "r"), c.y() ))); auto anim = parse_animated(element); for ( const auto& kf : anim.joined({"cx", "cy"}) ) gradient->start_point.set_keyframe(kf.time, gradient_transform.map(QPointF{kf.values[0].vector()[0], kf.values[1].vector()[0]}) )->set_transition(kf.transition); for ( const auto& kf : anim.joined({"fx", "fy"}) ) gradient->highlight.set_keyframe(kf.time, gradient_transform.map(QPointF{kf.values[0].vector()[0], kf.values[1].vector()[0]}) )->set_transition(kf.transition); for ( const auto& kf : anim.joined({"cx", "cy", "r"}) ) gradient->end_point.set_keyframe(kf.time, gradient_transform.map(QPointF{kf.values[0].vector()[0] + kf.values[2].vector()[0], kf.values[1].vector()[0]}) )->set_transition(kf.transition); } else { return; } gradient->name.set(id); gradient->colors.set(colors); brush_styles["#"+id] = gradient.get(); document->assets()->gradients->values.insert(std::move(gradient)); } Style parse_style(const QDomElement& element, const Style& parent_style) { Style style = parent_style; auto class_names_list = element.attribute("class").split(" ", Qt::SkipEmptyParts); std::unordered_set class_names(class_names_list.begin(), class_names_list.end()); for ( const auto& rule : css_blocks ) { if ( rule.selector.match(element, class_names) ) rule.merge_into(style); } if ( element.hasAttribute("style") ) { for ( const auto& item : element.attribute("style").split(';') ) { auto split = QStringView{item}.split(':'); if ( split.size() == 2 ) { QString name = split[0].trimmed().toString(); if ( !name.isEmpty() )// && css_atrrs.count(name) ) style[name] = split[1].trimmed().toString(); } } } for ( const auto& domnode : ItemCountRange(element.attributes()) ) { auto attr = domnode.toAttr(); if ( css_atrrs.count(attr.name()) ) style[attr.name()] = attr.value(); } for ( auto it = style.map.begin(); it != style.map.end(); ) { if ( it->second == "inherit" ) { QString parent = parent_style.get(it->first, ""); if ( parent.isEmpty() || parent == "inherit" ) { it = style.map.erase(it); continue; } it->second = parent; } ++it; } if ( !style.contains("fill") ) style.set("fill", parent_style.get("fill")); style.color = parse_color(style.get("color", ""), parent_style.color); return style; } bool handle_mask(const ParseFuncArgs& args) { QString mask_ref; if ( args.element.hasAttribute("clip-path") ) mask_ref = args.element.attribute("clip-path"); else if ( args.element.hasAttribute("mask") ) mask_ref = args.element.attribute("mask"); if ( mask_ref.isEmpty() ) return false; auto match = url_re.match(mask_ref); if ( !match.hasMatch() ) return false; QString id = match.captured(1).mid(1); QDomElement mask_element = element_by_id(id); if ( mask_element.isNull() ) return false; Style style = parse_style(args.element, args.parent_style); auto layer = add_layer(args.shape_parent); apply_common_style(layer, args.element, style); set_name(layer, args.element); QDomElement element = args.element; QDomElement trans_copy = dom.createElement("g"); trans_copy.setAttribute("style", element.attribute("style")); element.removeAttribute("style"); trans_copy.setAttribute("transform", element.attribute("transform")); element.removeAttribute("transform"); for ( const auto& attr : detail::css_atrrs ) element.removeAttribute(attr); Style mask_style = parse_style(mask_element, {}); mask_style["stroke"] = "none"; parse_g_to_layer({ mask_element, &layer->shapes, mask_style, false }); layer->mask->mask.set( mask_style.get("mask-style", "luminance") == "alpha" ? model::MaskSettings::Alpha : model::MaskSettings::Luma ); parse_shape_impl({ element, &layer->shapes, style, false }); parse_transform(trans_copy, layer, layer->transform.get()); return true; } void parse_shape_impl(const ParseFuncArgs& args) { auto it = shape_parsers.find(args.element.tagName()); if ( it != shape_parsers.end() ) { mark_progress(); (this->*it->second)(args); } } void parse_transform( const QDomElement& element, model::Composable* node, model::Transform* transform ) { auto bb = node->local_bounding_rect(0); bool anchor_from_inkscape = false; QPointF center = bb.center(); if ( element.hasAttributeNS(detail::xmlns.at("inkscape"), "transform-center-x") ) { anchor_from_inkscape = true; qreal ix = element.attributeNS(detail::xmlns.at("inkscape"), "transform-center-x").toDouble(); qreal iy = -element.attributeNS(detail::xmlns.at("inkscape"), "transform-center-y").toDouble(); center += QPointF(ix, iy); } bool anchor_from_rotate = false; if ( element.hasAttribute("transform") ) { auto trans = svg_transform( element.attribute("transform"), transform->transform_matrix(transform->time()) ); transform->set_transform_matrix(trans.transform); anchor_from_rotate = trans.anchor_set; if ( trans.anchor_set ) center = trans.anchor; } /// Adjust anchor point QPointF delta_pos; if ( anchor_from_rotate ) { transform->anchor_point.set(center); delta_pos = center; } else if ( anchor_from_inkscape ) { auto matrix = transform->transform_matrix(transform->time()); QPointF p1 = matrix.map(QPointF(0, 0)); transform->anchor_point.set(center); matrix = transform->transform_matrix(transform->time()); QPointF p2 = matrix.map(QPointF(0, 0)); delta_pos = p1 - p2; } transform->position.set(transform->position.get() + delta_pos); auto anim = animate_parser.parse_animated_transform(element); if ( !anim.apply_motion(transform->position, delta_pos, &node->transform->auto_orient) ) { for ( const auto& kf : anim.single("translate") ) transform->position.set_keyframe(kf.time, QPointF{kf.values.vector()[0], kf.values.vector()[1]} + delta_pos)->set_transition(kf.transition); } for ( const auto& kf : anim.single("scale") ) transform->scale.set_keyframe(kf.time, QVector2D(kf.values.vector()[0], kf.values.vector()[1]))->set_transition(kf.transition); for ( const auto& kf : anim.single("rotate") ) { transform->rotation.set_keyframe(kf.time, kf.values.vector()[0])->set_transition(kf.transition); if ( kf.values.vector().size() == 3 ) { QPointF p = {kf.values.vector()[1], kf.values.vector()[2]}; transform->anchor_point.set_keyframe(kf.time, p)->set_transition(kf.transition); transform->position.set_keyframe(kf.time, p)->set_transition(kf.transition); } } } struct ParsedTransformInfo { QTransform transform; QPointF anchor = {}; bool anchor_set = false; }; ParsedTransformInfo svg_transform(const QString& attr, const QTransform& trans) { ParsedTransformInfo info{trans}; for ( const QRegularExpressionMatch& match : utils::regexp::find_all(transform_re, attr) ) { auto args = double_args(match.captured(2)); if ( args.empty() ) { warning("Missing transformation parameters"); continue; } QString name = match.captured(1); if ( name == "translate" ) { info.transform.translate(args[0], args.size() > 1 ? args[1] : 0); } else if ( name == "scale" ) { info.transform.scale(args[0], (args.size() > 1 ? args[1] : args[0])); } else if ( name == "rotate" ) { qreal ang = args[0]; if ( args.size() > 2 ) { qreal x = args[1]; qreal y = args[2]; info.anchor = {x, y}; info.anchor_set = true; // info.transform.translate(-x, -y); info.transform.rotate(ang); // info.transform.translate(x, y); } else { info.transform.rotate(ang); } } else if ( name == "skewX" ) { info.transform *= QTransform( 1, 0, 0, qTan(args[0]), 1, 0, 0, 0, 1 ); } else if ( name == "skewY" ) { info.transform *= QTransform( 1, qTan(args[0]), 0, 0, 1, 0, 0, 0, 1 ); } else if ( name == "matrix" ) { if ( args.size() == 6 ) { info.transform *= QTransform( args[0], args[1], 0, args[2], args[3], 0, args[4], args[5], 1 ); } else { warning("Wrong translation matrix"); } } else { warning(QString("Unknown transformation %1").arg(name)); } } return info; } void parse_composable(const ParseFuncArgs& args, model::Composable* comp, const Style& style) { parse_transform(args.element, comp, comp->transform.get()); comp->blend_mode.set(detail::enum_from_svg( style.get("mix-blend-mode", "source-over"), detail::blend_modes, renderer::BlendMode::Normal )); } void add_shapes(const ParseFuncArgs& args, ShapeCollection&& shapes) { Style style = parse_style(args.element, args.parent_style); auto group = std::make_unique(document); apply_common_style(group.get(), args.element, style); set_name(group.get(), args.element); add_style_shapes(args, &group->shapes, style); for ( auto& shape : shapes ) group->shapes.insert(std::move(shape)); // parse_composable at the end so the bounding box isn't empty parse_composable(args, group.get(), style); args.shape_parent->insert(std::move(group)); } void apply_common_style(model::VisualNode* node, const QDomElement& element, const Style& style) { if ( style.get("display") == "none" || style.get("visibility") == "hidden" ) node->visible.set(false); node->locked.set(attr(element, "sodipodi", "insensitive") == "true"); node->set("opacity", percent_1(style.get("opacity", "1"))); node->get("transform").value(); } void set_name(model::DocumentNode* node, const QDomElement& element) { QString name = attr(element, "inkscape", "label"); if ( name.isEmpty() ) { name = attr(element, "android", "name"); if ( name.isEmpty() ) name = element.attribute("id"); } node->name.set(name); } void add_style_shapes(const ParseFuncArgs& args, model::ShapeListProperty* shapes, const Style& style) { QString paint_order = style.get("paint-order", "normal"); if ( paint_order == "normal" ) paint_order = "fill stroke"; for ( const auto& sr : paint_order.split(' ', Qt::SkipEmptyParts) ) { if ( sr == "fill" ) add_fill(args, shapes, style); else if ( sr == "stroke" ) add_stroke(args, shapes, style); } } void display_to_opacity(model::VisualNode* node, const detail::AnimateParser::AnimatedProperties& anim, model::AnimatedProperty& opacity, Style* style) { if ( !anim.has("display") ) return; if ( opacity.keyframe_count() > 2 ) { warning("Either animate `opacity` or `display`, not both"); return; } if ( style ) style->map.erase("display"); model::KeyframeTransition hold; hold.set_hold(true); for ( const auto& kf : anim.single("display") ) { opacity.set_keyframe(kf.time, kf.values.string() == "none" ? 0 : 1)->set_transition(hold); } node->visible.set(true); } void add_stroke(const ParseFuncArgs& args, model::ShapeListProperty* shapes, const Style& style) { QString stroke_color = style.get("stroke", "transparent"); if ( stroke_color == "none" ) return; auto stroke = std::make_unique(document); set_styler_style(stroke.get(), stroke_color, style.color); stroke->opacity.set(percent_1(style.get("stroke-opacity", "1"))); stroke->width.set(parse_unit(style.get("stroke-width", "1"))); stroke->cap.set(line_cap(style.get("stroke-linecap", "butt"))); stroke->join.set(line_join(style.get("stroke-linejoin", "miter"))); stroke->miter_limit.set(parse_unit(style.get("stroke-miterlimit", "4"))); auto anim = parse_animated(args.element); for ( const auto& kf : anim.single("stroke") ) stroke->color.set_keyframe(kf.time, kf.values.color())->set_transition(kf.transition); for ( const auto& kf : anim.single("stroke-opacity") ) stroke->opacity.set_keyframe(kf.time, kf.values.vector()[0])->set_transition(kf.transition); for ( const auto& kf : anim.single("stroke-width") ) stroke->width.set_keyframe(kf.time, kf.values.vector()[0])->set_transition(kf.transition); display_to_opacity(stroke.get(), anim, stroke->opacity, nullptr); shapes->insert(std::move(stroke)); } void set_styler_style(model::Styler* styler, const QString& color_str, const QColor& current_color) { if ( !color_str.startsWith("url") ) { styler->color.set(parse_color(color_str, current_color)); return; } auto match = url_re.match(color_str); if ( match.hasMatch() ) { QString id = match.captured(1); auto it = brush_styles.find(id); if ( it != brush_styles.end() ) { styler->use.set(it->second); return; } } styler->color.set(current_color); } void add_fill(const ParseFuncArgs& args, model::ShapeListProperty* shapes, const Style& style) { QString fill_color = style.get("fill", ""); auto fill = std::make_unique(document); set_styler_style(fill.get(), fill_color, style.color); fill->opacity.set(percent_1(style.get("fill-opacity", "1"))); if ( style.get("fill-rule", "") == "evenodd" ) fill->fill_rule.set(model::Fill::EvenOdd); auto anim = parse_animated(args.element); for ( const auto& kf : anim.single("fill") ) fill->color.set_keyframe(kf.time, kf.values.color())->set_transition(kf.transition); for ( const auto& kf : anim.single("fill-opacity") ) fill->opacity.set_keyframe(kf.time, kf.values.vector()[0])->set_transition(kf.transition); if ( fill_color == "none" ) fill->visible.set(false); display_to_opacity(fill.get(), anim, fill->opacity, nullptr); shapes->insert(std::move(fill)); } QColor parse_color(const QString& color_str, const QColor& current_color) { if ( color_str.isEmpty() || color_str == "currentColor" ) return current_color; return glaxnimate::io::svg::parse_color(color_str); } void parseshape_rect(const ParseFuncArgs& args) { ShapeCollection shapes; auto rect = push(shapes); qreal w = len_attr(args.element, "width", 0); qreal h = len_attr(args.element, "height", 0); rect->position.set(QPointF( len_attr(args.element, "x", 0) + w / 2, len_attr(args.element, "y", 0) + h / 2 )); rect->size.set(QSizeF(w, h)); qreal rx = len_attr(args.element, "rx", 0); qreal ry = len_attr(args.element, "ry", 0); rect->rounded.set(qMax(rx, ry)); auto anim = parse_animated(args.element); /// \todo handle offset anim.apply_motion(rect->position); for ( const auto& kf : anim.joined({"x", "y", "width", "height"}) ) rect->position.set_keyframe(kf.time, { kf.values[0].vector()[0] + kf.values[2].vector()[0] / 2, kf.values[1].vector()[0] + kf.values[3].vector()[0] / 2 })->set_transition(kf.transition); for ( const auto& kf : anim.joined({"width", "height"}) ) rect->size.set_keyframe(kf.time, {kf.values[0].vector()[0], kf.values[1].vector()[0]})->set_transition(kf.transition); for ( const auto& kf : anim.joined({"rx", "ry"}) ) rect->rounded.set_keyframe(kf.time, qMax(kf.values[0].vector()[0], kf.values[1].vector()[0]))->set_transition(kf.transition); add_shapes(args, std::move(shapes)); } void parseshape_ellipse(const ParseFuncArgs& args) { ShapeCollection shapes; auto ellipse = push(shapes); ellipse->position.set(QPointF( len_attr(args.element, "cx", 0), len_attr(args.element, "cy", 0) )); qreal rx = len_attr(args.element, "rx", 0); qreal ry = len_attr(args.element, "ry", 0); ellipse->size.set(QSizeF(rx * 2, ry * 2)); auto anim = parse_animated(args.element); anim.apply_motion(ellipse->position); for ( const auto& kf : anim.joined({"cx", "cy"}) ) ellipse->position.set_keyframe(kf.time, {kf.values[0].vector()[0], kf.values[1].vector()[0]})->set_transition(kf.transition); for ( const auto& kf : anim.joined({"rx", "ry"}) ) ellipse->size.set_keyframe(kf.time, {kf.values[0].vector()[0]*2, kf.values[1].vector()[0]*2})->set_transition(kf.transition); add_shapes(args, std::move(shapes)); } void parseshape_circle(const ParseFuncArgs& args) { ShapeCollection shapes; auto ellipse = push(shapes); ellipse->position.set(QPointF( len_attr(args.element, "cx", 0), len_attr(args.element, "cy", 0) )); qreal d = len_attr(args.element, "r", 0) * 2; ellipse->size.set(QSizeF(d, d)); auto anim = parse_animated(args.element); anim.apply_motion(ellipse->position); for ( const auto& kf : anim.joined({"cx", "cy"}) ) ellipse->position.set_keyframe(kf.time, {kf.values[0].vector()[0], kf.values[1].vector()[0]})->set_transition(kf.transition); for ( const auto& kf : anim.single({"r"}) ) ellipse->size.set_keyframe(kf.time, {kf.values.vector()[0]*2, kf.values.vector()[0]*2})->set_transition(kf.transition); add_shapes(args, std::move(shapes)); } void parseshape_g(const ParseFuncArgs& args) { switch ( group_mode ) { case Groups: parse_g_to_shape(args); break; case Layers: parse_g_to_layer(args); break; case Inkscape: if ( args.in_group ) parse_g_to_shape(args); else if ( attr(args.element, "inkscape", "groupmode") == "layer" ) parse_g_to_layer(args); else parse_g_to_shape(args); break; } } void parse_g_to_layer(const ParseFuncArgs& args) { Style style = parse_style(args.element, args.parent_style); auto layer = add_layer(args.shape_parent); parse_g_common( {args.element, &layer->shapes, style, false}, layer, layer->transform.get(), style ); } void parse_g_to_shape(const ParseFuncArgs& args) { Style style = parse_style(args.element, args.parent_style); auto ugroup = std::make_unique(document); auto group = ugroup.get(); args.shape_parent->insert(std::move(ugroup)); parse_g_common( {args.element, &group->shapes, style, true}, group, group->transform.get(), style ); } void parse_g_common( const ParseFuncArgs& args, model::Group* g_node, model::Transform* transform, Style& style ) { apply_common_style(g_node, args.element, args.parent_style); auto anim = parse_animated(args.element); for ( const auto& kf : anim.single("opacity") ) g_node->opacity.set_keyframe(kf.time, kf.values.vector()[0])->set_transition(kf.transition); display_to_opacity(g_node, anim, g_node->opacity, &style); set_name(g_node, args.element); // Avoid doubling opacity values style.map.erase("opacity"); parse_children(args); parse_transform(args.element, g_node, transform); } std::vector parse_bezier_impl(const ParseFuncArgs& args, const math::bezier::MultiBezier& bez) { if ( bez.beziers().empty() ) return {}; ShapeCollection shapes; std::vector paths; for ( const auto& bezier : bez.beziers() ) { model::Path* shape = push(shapes); paths.push_back(shape); shape->shape.set(bezier); shape->closed.set(bezier.closed()); } add_shapes(args, std::move(shapes)); return paths; } model::Path* parse_bezier_impl_single(const ParseFuncArgs& args, const math::bezier::Bezier& bez) { ShapeCollection shapes; auto path = push(shapes); path->shape.set(bez); add_shapes(args, {std::move(shapes)}); return path; } detail::AnimateParser::AnimatedProperties parse_animated(const QDomElement& element) { return animate_parser.parse_animated_properties(element); } void parseshape_line(const ParseFuncArgs& args) { math::bezier::Bezier bez; bez.add_point(QPointF( len_attr(args.element, "x1", 0), len_attr(args.element, "y1", 0) )); bez.line_to(QPointF( len_attr(args.element, "x2", 0), len_attr(args.element, "y2", 0) )); auto path = parse_bezier_impl_single(args, bez); for ( const auto& kf : parse_animated(args.element).joined({"x1", "y1", "x2", "y2"}) ) { math::bezier::Bezier bez; bez.add_point({kf.values[0].vector()[0], kf.values[1].vector()[0]}); bez.add_point({kf.values[2].vector()[0], kf.values[3].vector()[0]}); path->shape.set_keyframe(kf.time, bez)->set_transition(kf.transition); } } math::bezier::Bezier build_poly(const std::vector& coords, bool close) { math::bezier::Bezier bez; if ( coords.size() < 4 ) { if ( !coords.empty() ) warning("Not enough `points` for `polygon` / `polyline`"); return bez; } bez.add_point(QPointF(coords[0], coords[1])); for ( int i = 2; i < int(coords.size()); i+= 2 ) bez.line_to(QPointF(coords[i], coords[i+1])); if ( close ) bez.close(); return bez; } void handle_poly(const ParseFuncArgs& args, bool close) { auto path = parse_bezier_impl_single(args, build_poly(double_args(args.element.attribute("points", "")), close)); if ( !path ) return; for ( const auto& kf : parse_animated(args.element).single("points") ) path->shape.set_keyframe(kf.time, build_poly(kf.values.vector(), close))->set_transition(kf.transition); } void parseshape_polyline(const ParseFuncArgs& args) { handle_poly(args, false); } void parseshape_polygon(const ParseFuncArgs& args) { handle_poly(args, true); } void parseshape_path(const ParseFuncArgs& args) { if ( parse_star(args) ) return; QString d = args.element.attribute("d"); math::bezier::MultiBezier bez = PathDParser(d).parse(); /// \todo sodipodi:nodetypes auto paths = parse_bezier_impl(args, bez); path_animation(paths, parse_animated(args.element), "d" ); } bool parse_star(const ParseFuncArgs& args) { if ( attr(args.element, "sodipodi", "type") != "star" ) return false; qreal randomized = attr(args.element, "inkscape", "randomized", "0").toDouble(); if ( !qFuzzyCompare(randomized, 0.0) ) return false; qreal rounded = attr(args.element, "inkscape", "rounded", "0").toDouble(); if ( !qFuzzyCompare(rounded, 0.0) ) return false; ShapeCollection shapes; auto shape = push(shapes); shape->points.set( attr(args.element, "sodipodi", "sides").toInt() ); auto flat = attr(args.element, "inkscape", "flatsided"); shape->type.set( flat == "true" ? model::PolyStar::Polygon : model::PolyStar::Star ); shape->position.set(QPointF( attr(args.element, "sodipodi", "cx").toDouble(), attr(args.element, "sodipodi", "cy").toDouble() )); shape->outer_radius.set(attr(args.element, "sodipodi", "r1").toDouble()); shape->inner_radius.set(attr(args.element, "sodipodi", "r2").toDouble()); shape->angle.set( math::rad2deg(attr(args.element, "sodipodi", "arg1").toDouble()) +90 ); add_shapes(args, std::move(shapes)); return true; } void parseshape_use(const ParseFuncArgs& args) { QString id = attr(args.element, "xlink", "href"); if ( !id.startsWith('#') ) return; id.remove(0, 1); QDomElement element = element_by_id(id); if ( element.isNull() ) return; Style style = parse_style(args.element, args.parent_style); auto group = std::make_unique(document); apply_common_style(group.get(), args.element, style); set_name(group.get(), args.element); parse_shape({element, &group->shapes, style, true}); group->transform.get()->position.set( QPointF(len_attr(args.element, "x", 0), len_attr(args.element, "y", 0)) ); parse_transform(args.element, group.get(), group->transform.get()); args.shape_parent->insert(std::move(group)); } QString find_asset_file(const QString& path) { QFileInfo finfo(path); if ( finfo.exists() ) return path; else if ( default_asset_path.exists(path) ) return default_asset_path.filePath(path); else if ( default_asset_path.exists(finfo.fileName()) ) return default_asset_path.filePath(finfo.fileName()); return {}; } bool open_asset_file(model::Bitmap* image, const QString& path) { if ( path.isEmpty() ) return false; auto file = find_asset_file(path); if ( file.isEmpty() ) return false; return image->from_file(file); } void parse_image_position(const ParseFuncArgs& args, model::Transform* tf) { tf->position.set(QPointF( len_attr(args.element, "x", 0), len_attr(args.element, "y", 0) )); auto anim = parse_animated(args.element); for ( const auto& kf : anim.joined({"x", "y"}) ) tf->position.set_keyframe(kf.time, {kf.values[0].vector()[0], kf.values[1].vector()[0]})->set_transition(kf.transition); } void parseshape_image(const ParseFuncArgs& args) { auto bitmap = std::make_unique(document); bool open = false; QString href = attr(args.element, "xlink", "href"); QUrl url = QUrl(href); if ( url.isRelative() ) open = open_asset_file(bitmap.get(), href); if ( !open ) { if ( url.isLocalFile() ) open = open_asset_file(bitmap.get(), url.toLocalFile()); else open = bitmap->from_url(url); } if ( !open ) { QString path = attr(args.element, "sodipodi", "absref"); open = open_asset_file(bitmap.get(), path); } if ( !open ) warning(QString("Could not load image %1").arg(href)); auto image = std::make_unique(document); image->image.set(document->assets()->images->values.insert(std::move(bitmap))); Style style = parse_style(args.element, args.parent_style); parse_composable(args, image.get(), style); if ( args.element.hasAttribute("x") || args.element.hasAttribute("y") ) { if ( args.element.hasAttribute("transform") ) { // Has both x,y and a transform, therefore add an intermediate layer auto wrapper = std::make_unique(document); wrapper->name.set(image->name.get()); wrapper->blend_mode.set(image->blend_mode.get()); image->blend_mode.set(renderer::BlendMode::Normal); parse_image_position(args, wrapper->transform.get()); layers.push_back(wrapper.get()); wrapper->shapes.insert(std::move(image)); args.shape_parent->insert(std::move(wrapper)); return; } else { parse_image_position(args, image->transform.get()); } } args.shape_parent->insert(std::move(image)); } struct TextStyle { QString family = "sans-serif"; int weight = QFont::Normal; QFont::Style style = QFont::StyleNormal; qreal line_spacing = 0; qreal size = 64; bool keep_space = false; QPointF pos; qreal anchor = 0; }; TextStyle parse_text_style(const ParseFuncArgs& args, const TextStyle& parent) { TextStyle out = parent; Style style = parse_style(args.element, args.parent_style); if ( style.contains("font-family") ) out.family = style["font-family"]; if ( style.contains("font-style") ) { QString slant = style["font-style"]; if ( slant == "normal" ) out.style = QFont::StyleNormal; else if ( slant == "italic" ) out.style = QFont::StyleItalic; else if ( slant == "oblique" ) out.style = QFont::StyleOblique; } if ( style.contains("font-size") ) { QString size = style["font-size"]; static const std::map size_names = { {{"xx-small"}, {8}}, {{"x-small"}, {16}}, {{"small"}, {32}}, {{"medium"}, {64}}, {{"large"}, {128}}, {{"x-large"}, {256}}, {{"xx-large"}, {512}}, }; if ( size == "smaller" ) out.size /= 2; else if ( size == "larger" ) out.size *= 2; else if ( size_names.count(size) ) out.size = size_names.at(size); else out.size = parse_unit(size); } if ( style.contains("font-weight") ) { QString weight = style["font-weight"]; if ( weight == "bold" ) out.weight = 700; else if ( weight == "normal" ) out.weight = 400; else if ( weight == "bolder" ) out.weight = qMin(1000, out.weight + 100); else if ( weight == "lighter") out.weight = qMax(1, out.weight - 100); else out.weight = weight.toInt(); } if ( style.contains("line-height") ) out.line_spacing = parse_unit(style["line-height"]); if ( args.element.hasAttribute("xml:space") ) out.keep_space = args.element.attribute("xml:space") == "preserve"; if ( args.element.hasAttribute("x") ) out.pos.setX(len_attr(args.element, "x", 0)); if ( args.element.hasAttribute("y") ) out.pos.setY(len_attr(args.element, "y", 0)); if ( style.contains("text-anchor") ) { QString anchor = style["text-anchor"]; if ( anchor == "start" ) out.anchor = 0; else if ( anchor == "middle" ) out.anchor = 0.5; else if ( anchor == "end" ) out.anchor = 1; } return out; } QString trim_text(const QString& text) { QString trimmed = text.simplified(); if ( !text.isEmpty() && text.back().isSpace() ) trimmed += ' '; return trimmed; } void apply_text_style(model::Font* font, const TextStyle& style) { font->family.set(style.family); font->size.set(unit_convert(style.size, "px", "pt")); QFont qfont; qfont.setFamily(style.family); qfont.setWeight(QFont::Weight(WeightConverter::convert(style.weight, WeightConverter::css, WeightConverter::qt))); qfont.setStyle(style.style); QString style_string = QFontDatabase::styleString(qfont); font->style.set(style_string); } void adjust_text_position(model::TextShape* shape, const TextStyle& style) { if ( !shape ) return; if ( style.anchor != 0 ) { shape->position.set( shape->position.get() + QPointF(-style.anchor * shape->local_bounding_rect(document->current_time()).width(), 0) ); for ( auto& keyframe : shape->position.mutable_range() ) { QPointF offset(-style.anchor * shape->local_bounding_rect(keyframe.time()).width(), 0); keyframe.set(keyframe.get() + offset); } } } QPointF parse_text_element(const ParseFuncArgs& args, const TextStyle& parent_style) { TextStyle style = parse_text_style(args, parent_style); Style css_style = parse_style(args.element, args.parent_style); auto anim = parse_animated(args.element); model::TextShape* last = nullptr; QPointF offset; QPointF pos = style.pos; QString text; for ( const auto & child : ItemCountRange(args.element.childNodes()) ) { ParseFuncArgs child_args = {child.toElement(), args.shape_parent, css_style, args.in_group}; if ( child.isElement() ) { adjust_text_position(last, style); last = nullptr; style.pos = pos + offset; offset = parse_text_element(child_args, style); } else if ( child.isText() || child.isCDATASection() ) { text += child.toCharacterData().data(); if ( !last ) { ShapeCollection shapes; last = push(shapes); last->position.set(pos + offset); apply_text_style(last->font.get(), style); for ( const auto& kf : anim.joined({"x", "y"}) ) { last->position.set_keyframe( kf.time, offset + QPointF(kf.values[0].vector()[0], kf.values[1].vector()[0]) )->set_transition(kf.transition); } add_shapes(child_args, std::move(shapes)); } last->text.set(style.keep_space ? text : trim_text(text)); offset = last->offset_to_next_character(); } } adjust_text_position(last, style); return offset; } void parseshape_text(const ParseFuncArgs& args) { parse_text_element(args, {}); } void parse_metadata() { auto meta = dom.elementsByTagNameNS(xmlns.at("cc"), "Work"); if ( meta.count() == 0 ) return; auto work = query_element({"metadata", "RDF", "Work"}, dom.documentElement()); document->info().author = query({"creator", "Agent", "title"}, work); document->info().description = query({"description"}, work); for ( const auto& domnode : ItemCountRange(query_element({"subject", "Bag"}, work).childNodes()) ) { if ( domnode.isElement() ) { auto child = domnode.toElement(); if ( child.tagName() == "li" ) document->info().keywords.push_back(child.text()); } } } GroupMode group_mode; std::vector css_blocks; QDir default_asset_path; static const std::map shape_parsers; static const QRegularExpression transform_re; static const QRegularExpression url_re; }; const std::map glaxnimate::io::svg::SvgParser::Private::shape_parsers = { {"g", &glaxnimate::io::svg::SvgParser::Private::parseshape_g}, {"rect", &glaxnimate::io::svg::SvgParser::Private::parseshape_rect}, {"ellipse", &glaxnimate::io::svg::SvgParser::Private::parseshape_ellipse}, {"circle", &glaxnimate::io::svg::SvgParser::Private::parseshape_circle}, {"line", &glaxnimate::io::svg::SvgParser::Private::parseshape_line}, {"polyline",&glaxnimate::io::svg::SvgParser::Private::parseshape_polyline}, {"polygon", &glaxnimate::io::svg::SvgParser::Private::parseshape_polygon}, {"path", &glaxnimate::io::svg::SvgParser::Private::parseshape_path}, {"use", &glaxnimate::io::svg::SvgParser::Private::parseshape_use}, {"image", &glaxnimate::io::svg::SvgParser::Private::parseshape_image}, {"text", &glaxnimate::io::svg::SvgParser::Private::parseshape_text}, }; const QRegularExpression glaxnimate::io::svg::detail::SvgParserPrivate::unit_re{R"(([-+]?(?:[0-9]*\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?)([a-z]*))"}; const QRegularExpression glaxnimate::io::svg::SvgParser::Private::transform_re{R"(([a-zA-Z]+)\s*\(([^\)]*)\))"}; const QRegularExpression glaxnimate::io::svg::SvgParser::Private::url_re{R"(url\s*\(\s*(#[-a-zA-Z0-9_]+)\s*\)\s*)"}; const QRegularExpression glaxnimate::io::svg::detail::AnimateParser::separator{"\\s*,\\s*|\\s+"}; const QRegularExpression glaxnimate::io::svg::detail::AnimateParser::clock_re{R"((?:(?:(?[0-9]+):)?(?:(?[0-9]{2}):)?(?[0-9]+(?:\.[0-9]+)?))|(?:(?[0-9]+(?:\.[0-9]+)?)(?h|min|s|ms)))"}; const QRegularExpression glaxnimate::io::svg::detail::AnimateParser::frame_separator_re{"\\s*;\\s*"}; glaxnimate::io::svg::SvgParser::SvgParser( QIODevice* device, GroupMode group_mode, model::Document* document, const std::function& on_warning, ImportExport* io, QSize forced_size, model::FrameTime default_time, QDir default_asset_path ) : d(std::make_unique(document, on_warning, io, forced_size, default_time, group_mode, default_asset_path)) { d->load(device); } glaxnimate::io::svg::SvgParser::~SvgParser() { } glaxnimate::io::mime::DeserializedData glaxnimate::io::svg::SvgParser::parse_to_objects() { glaxnimate::io::mime::DeserializedData data; data.initialize_data(); d->parse(data.document.get()); return data; } void glaxnimate::io::svg::SvgParser::parse_to_document() { d->parse(); } static qreal hex(const QString& s, int start, int size) { return QStringView{s}.mid(start, size).toInt(nullptr, 16) / (size == 2 ? 255.0 : 15.0); } QColor glaxnimate::io::svg::parse_color(const QString& string) { if ( string.isEmpty() ) return {}; // #fff #112233 if ( string[0] == '#' ) { if ( string.size() == 4 || string.size() == 5 ) { qreal alpha = string.size() == 4 ? 1. : hex(string, 4, 1); return QColor::fromRgbF(hex(string, 1, 1), hex(string, 2, 1), hex(string, 3, 1), alpha); } else if ( string.size() == 7 || string.size() == 9 ) { qreal alpha = string.size() == 7 ? 1. : hex(string, 7, 2); return QColor::fromRgbF(hex(string, 1, 2), hex(string, 3, 2), hex(string, 5, 2), alpha); } return QColor(); } // transparent if ( string == "transparent" || string == "none" ) return QColor(0, 0, 0, 0); QRegularExpressionMatch match; // rgba(123, 123, 123, 0.7) static QRegularExpression rgba{R"(^rgba\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.eE]+)\s*\)$)"}; match = rgba.match(string); if ( match.hasMatch() ) return QColor(match.captured(1).toInt(), match.captured(2).toInt(), match.captured(3).toInt(), match.captured(4).toDouble() * 255); // rgb(123, 123, 123) static QRegularExpression rgb{R"(^rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$)"}; match = rgb.match(string); if ( match.hasMatch() ) return QColor(match.captured(1).toInt(), match.captured(2).toInt(), match.captured(3).toInt()); // rgba(60%, 30%, 20%, 0.7) static QRegularExpression rgba_pc{R"(^rgba\s*\(\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)\s*\)$)"}; match = rgba_pc.match(string); if ( match.hasMatch() ) return QColor::fromRgbF(match.captured(1).toDouble() / 100, match.captured(2).toDouble() / 100, match.captured(3).toDouble() / 100, match.captured(4).toDouble()); // rgb(60%, 30%, 20%) static QRegularExpression rgb_pc{R"(^rgb\s*\(\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*\)$)"}; match = rgb_pc.match(string); if ( match.hasMatch() ) return QColor::fromRgbF(match.captured(1).toDouble() / 100, match.captured(2).toDouble() / 100, match.captured(3).toDouble() / 100); // hsl(60, 30%, 20%) static QRegularExpression hsl{R"(^hsl\s*\(\s*([0-9.eE]+)\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*\)$)"}; match = rgb_pc.match(string); if ( match.hasMatch() ) return QColor::fromHslF(match.captured(1).toDouble() / 360, match.captured(2).toDouble() / 100, match.captured(3).toDouble() / 100); // hsla(60, 30%, 20%, 0.7) static QRegularExpression hsla{R"(^hsla\s*\(\s*([0-9.eE]+)\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)\s*\)$)"}; match = rgb_pc.match(string); if ( match.hasMatch() ) return QColor::fromHslF(match.captured(1).toDouble() / 360, match.captured(2).toDouble() / 100, match.captured(3).toDouble() / 100, match.captured(4).toDouble()); // red return QColor(string); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/postscript_format.cpp000664 001750 001750 00000004142 15165022620 035276 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/cairo/postscript_format.hpp" #include #include "glaxnimate/model/assets/composition.hpp" #include "cairo_renderer.hpp" #include "glaxnimate/app_info.hpp" QStringList glaxnimate::cairo::PostScriptFormat::extensions(Direction) const { return {QStringLiteral("ps"), QStringLiteral("eps")}; } cairo_status_t write_to_io(void *closure, const unsigned char *data, unsigned int length) { ((QIODevice*)closure)->write((const char*)data, length); return CAIRO_STATUS_SUCCESS; } static void add_comment(cairo_surface_t *surface, const QString& string) { cairo_ps_surface_dsc_comment(surface, string.toStdString().c_str()); } bool glaxnimate::cairo::PostScriptFormat::on_save_static(QIODevice &dev, const QString &, model::Composition *comp, model::FrameTime time, const QVariantMap &) { CairoRenderer renderer(10); auto surface = cairo_ps_surface_create_for_stream(&write_to_io, &dev, comp->width.get(), comp->height.get()); if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) { error(i18n("Could not create PostScript surface: %1", QString(cairo_status_to_string(cairo_surface_status(surface))))); return false; } // EPS cairo_ps_surface_set_eps(surface, true); add_comment(surface, QStringLiteral("%%BoundingBox: 0 0 %1 %2").arg(comp->width.get()).arg(comp->height.get())); auto& info = glaxnimate::AppInfo::instance(); add_comment(surface, QStringLiteral("%%Creator: %1 %2").arg(info.name()).arg(info.version())); add_comment(surface, QStringLiteral("%%Title: %1").arg(comp->name.get())); renderer.set_cairo_surface(surface, comp->width.get(), comp->height.get()); renderer.render_start(); comp->paint(&renderer, time, model::VisualNode::Render); renderer.render_end(); return true; } std::unique_ptr glaxnimate::cairo::PostScriptFormat::save_settings(model::Composition*) const { // TODO level, embedded? return {}; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/asset.cpp000664 001750 001750 00000000254 15165022620 032653 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/asset.hpp" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/base.cpp000664 001750 001750 00000007574 15165022620 030467 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/base.hpp" #include "glaxnimate/model/assets/assets.hpp" QString glaxnimate::io::ImportExport::name_filter(io::ImportExport::Direction direction) const { QString ext_str; for ( const QString& ext : extensions(direction) ) { ext_str += "*." + ext + " "; } if ( ext_str.isEmpty() ) return {}; ext_str.resize(ext_str.size() - 1); //: Open/Save file dialog file filter eg: "Text files (.txt)" return i18n("%1 (%2)", name(), ext_str); } QByteArray glaxnimate::io::ImportExport::save(model::Composition* comp, const QVariantMap& setting_values, const QString& filename) { QByteArray data; QBuffer file(&data); file.open(QIODevice::WriteOnly); QVariantMap clean_setting_values = setting_values; if ( auto settings = save_settings(comp) ) { for ( const auto& setting : *settings ) clean_setting_values[setting.slug] = setting.get_variant(clean_setting_values); } if ( !save(file, filename, comp, clean_setting_values) ) return {}; return data; } bool glaxnimate::io::ImportExport::save ( QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& setting_values ) { if ( document->assets()->compositions->values.empty() ) return false; return save(file, filename, document->assets()->compositions->values[0], setting_values); } bool glaxnimate::io::ImportExport::open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& setting_values) { if ( !file.isOpen() && auto_open() ) if ( !file.open(QIODevice::ReadOnly) ) return false; bool ok = on_open(file, filename, document, setting_values); Q_EMIT completed(ok); return ok; } bool glaxnimate::io::ImportExport::save(QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& setting_values) { if ( !file.isOpen() && auto_open() ) if ( !file.open(QIODevice::WriteOnly) ) return false; bool ok = on_save(file, filename, comp, setting_values); Q_EMIT completed(ok); return ok; } bool glaxnimate::io::ImportExport::save_static(QIODevice &file, const QString &filename, model::Composition *comp, model::FrameTime time, const QVariantMap &setting_values) { if ( !file.isOpen() && auto_open() ) if ( !file.open(QIODevice::WriteOnly) ) return false; bool ok = on_save_static(file, filename, comp, time, setting_values); Q_EMIT completed(ok); return ok; } QByteArray glaxnimate::io::ImportExport::save_static(model::Composition *comp, double time, const QVariantMap &setting_values, const QString &filename) { QByteArray data; QBuffer file(&data); file.open(QIODevice::WriteOnly); QVariantMap clean_setting_values = setting_values; if ( auto settings = save_settings(comp) ) { for ( const auto& setting : *settings ) clean_setting_values[setting.slug] = setting.get_variant(clean_setting_values); } if ( !save_static(file, filename, comp, time, clean_setting_values) ) return {}; return data; } bool glaxnimate::io::ImportExport::load(model::Document* document, const QByteArray& data, const QVariantMap& setting_values, const QString& filename) { if ( !document ) return false; QBuffer file(const_cast(&data)); file.open(QIODevice::ReadOnly); QVariantMap clean_setting_values = setting_values; if ( auto settings = open_settings() ) { for ( const auto& setting : *settings ) clean_setting_values[setting.slug] = setting.get_variant(clean_setting_values); } return open(file, filename, document, clean_setting_values); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/animatable_path.hpp000664 001750 001750 00000002775 15165022620 035341 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/model/animation/animatable.hpp" #include "glaxnimate/math/bezier/bezier.hpp" namespace glaxnimate::model { namespace detail { // Intermediare non-templated class so Q_OBJECT works class AnimatedPropertyBezier : public detail::AnimatedProperty { Q_OBJECT public: AnimatedPropertyBezier(Object* object, const utils::LazyLocalizedString& name, PropertyCallback emitter = {}) : detail::AnimatedProperty(object, name, {}, std::move(emitter)) {} int size() const { return value_.size(); } bool closed() const { return value_.closed(); } void set_closed(bool closed); Q_INVOKABLE void split_segment(int index, qreal factor); Q_INVOKABLE void remove_point(int index); void remove_points(const std::set& indices); /** * \brief Extends all keyframe values to match \p target * * Each keyframe adds nodes from \p target to have at least \p target.size() nodes */ void extend(const math::bezier::Bezier& target, bool at_end); }; } // namespace detail template<> class AnimatedProperty : public detail::AnimatedPropertyBezier { public: using detail::AnimatedPropertyBezier::AnimatedPropertyBezier; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/script/registrar_common.hpp000664 001750 001750 00000000714 15165022620 034016 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/utils/qstring_exception.hpp" #include "glaxnimate/utils/i18n.hpp" #include "glaxnimate/log/log.hpp" namespace glaxnimate::script { class ScriptError: public utils::QStringException { public: using QStringException::QStringException; }; } // namespace glaxnimate::script mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/log/log.hpp000664 001750 001750 00000003161 15165022620 030501 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/log/logger.hpp" namespace glaxnimate::log { class LogStream { public: LogStream(const QString& source, const QString& detail = "", Severity severity = Warning) : source(source), detail(detail), severity(severity) {} ~LogStream() { if ( !message.isEmpty() ) Logger::instance().log({severity, source, detail, message, QDateTime::currentDateTime()}); } template LogStream& operator<<(T&& item) { if ( !message.isEmpty() ) str << ' '; str << std::forward(item); return *this; } private: QString source; QString detail; Severity severity; QString message; QTextStream str{&message}; }; class Log { public: Log(const QString& source, const QString& detail="") : source(source), detail(detail) {} const Log& log(const QString& message, Severity severity = Warning) const { Logger::instance().log({severity, source, detail, message, QDateTime::currentDateTime()}); return *this; } void operator()(const QString& message, Severity severity = Warning) { log(message, severity); } void set_detail(const QString& detail) { this->detail = detail; } LogStream stream(Severity severity = Warning) const { return LogStream(source, detail, severity); } private: QString source; QString detail; }; } // namespace glaxnimate::log mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/script/glaxnimate_model.hpp000664 001750 001750 00000015464 15165022620 033765 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/command/object_list_commands.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/composable/layer.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/shapes/shapes/rect.hpp" #include "glaxnimate/model/shapes/shapes/ellipse.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/model/shapes/style/fill.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" #include "glaxnimate/model/shapes/modifiers/repeater.hpp" #include "glaxnimate/model/shapes/modifiers/trim.hpp" #include "glaxnimate/model/shapes/modifiers/inflate_deflate.hpp" #include "glaxnimate/model/shapes/modifiers/round_corners.hpp" #include "glaxnimate/model/shapes/modifiers/offset_path.hpp" #include "glaxnimate/model/shapes/modifiers/zig_zag.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/assets/named_color.hpp" #include "glaxnimate/model/assets/composition.hpp" #include "glaxnimate/script/register_machinery.hpp" namespace glaxnimate::script { template class AddShapeBase { public: using PtrMem = PropT Owner::*; AddShapeBase(PtrMem p) noexcept : ptr(p) {} protected: ItemT* create(model::Document* doc, PropT& prop, const QString& clsname, int index) const { auto obj = model::Factory::static_build(clsname, doc); if ( !obj ) return nullptr; auto cast = obj->cast(); if ( !cast ) { delete obj; return nullptr; } if constexpr ( std::is_base_of_v ) doc->set_best_name(static_cast(cast)); else cast->name.set(cast->type_name_human()); doc->push_command(new command::AddObject(&prop, std::unique_ptr(cast), index)); return cast; } PtrMem ptr; }; template class AddShapeName : public AddShapeBase { public: using AddShapeBase::AddShapeBase; ItemT* operator() (Owner* owner, const QString& clsname, int index = -1) const { return this->create(owner->document(), owner->*(this->ptr), clsname, index); } }; template class AddShapeClone { public: using PtrMem = PropT Owner::*; AddShapeClone(PtrMem p) noexcept : ptr(p) {} ItemT* operator() (Owner* owner, ItemT* object, int index = -1) const { if ( !object ) return nullptr; std::unique_ptr clone(static_cast(object->clone().release())); if ( clone->document() != owner->document() ) clone->transfer(owner->document()); auto ptr = clone.get(); owner->push_command(new command::AddObject(&(owner->*(this->ptr)), std::move(clone), index)); return ptr; } private: PtrMem ptr; }; template void register_top_level(const typename Reg::module& model) { // TODO define Document, Object, DocumentNode, Composition register_from_meta(model); register_from_meta(model); register_from_meta(model); register_from_meta(model); register_from_meta(model); } template void register_assets(const typename Reg::module& defs) { // TODO define Asset register_from_meta(defs); register_constructible(defs); register_constructible(defs); register_constructible(defs, enums{}); register_constructible(defs); register_from_meta(defs); register_from_meta(defs); register_from_meta(defs); register_from_meta(defs); register_from_meta(defs); register_from_meta(defs); register_from_meta(defs); register_from_meta(defs); } template void register_shapes(const typename Reg::module& shapes) { // TODO define ShapeElement register_from_meta(shapes); register_from_meta(shapes); register_from_meta(shapes); register_from_meta(shapes, enums{}); register_constructible(shapes); register_constructible(shapes); register_constructible(shapes, enums{}); register_constructible(shapes); auto cls_group = register_constructible(shapes); Reg::define_add_shape(cls_group); register_constructible(shapes); register_constructible(shapes); register_constructible(shapes); register_constructible(shapes, enums{}); register_constructible(shapes, enums{}); register_constructible(shapes); register_from_meta(shapes); register_constructible(shapes); register_constructible(shapes); register_constructible(shapes); register_constructible(shapes); register_constructible(shapes); } } // namespace glaxnimate::script src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/avd/avd_renderer.hpp000664 001750 001750 00000001246 15165022620 036300 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include namespace glaxnimate::model { class Composition; } // namespace glaxnimate::model namespace glaxnimate::io::avd { class AvdRenderer { public: AvdRenderer(const std::function& on_warning); ~AvdRenderer(); void render(model::Composition* comp); QDomElement graphics(); QDomDocument single_file(); private: class Private; std::unique_ptr d; }; } // namespace glaxnimate::io::avd mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/group.cpp000664 001750 001750 00000005175 15165022620 035004 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/composable/group.hpp" #include #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/shapes/style/styler.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Group) void glaxnimate::model::Group::add_shapes(glaxnimate::model::FrameTime t, math::bezier::MultiBezier & bez, const QTransform& parent_transform) const { QTransform trans = transform.get()->transform_matrix(t) * parent_transform; for ( const auto& ch : utils::Range(shapes.begin(), shapes.past_first_modifier()) ) { ch->add_shapes(t, bez, trans); } } QRectF glaxnimate::model::Group::local_bounding_rect(FrameTime t) const { if ( shapes.empty() ) return owner_composition()->rect(); return shapes.bounding_rect(t); } QTransform glaxnimate::model::Group::local_transform_matrix(glaxnimate::model::FrameTime t) const { return transform.get()->transform_matrix(t); } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Group::to_painter_path_impl(glaxnimate::model::FrameTime t) const { glaxnimate::math::bezier::MultiBezier path; for ( const auto& ch : utils::Range(shapes.begin(), shapes.past_first_modifier()) ) { if ( ch->is_instance() || ch->is_instance() ) path.append(ch->to_clip(t)); } return path; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Group::to_clip(FrameTime t) const { return to_painter_path(t).transformed(transform.get()->transform_matrix(t)); } std::unique_ptr glaxnimate::model::Group::to_path() const { auto clone = std::make_unique(document()); for ( BaseProperty* prop : properties() ) { if ( prop != &shapes ) clone->get_property(prop->name())->assign_from(prop); } for ( const auto& shape : shapes ) { clone->shapes.insert(shape->to_path()); if ( shape->is_instance() ) break; } return clone; } void glaxnimate::model::Group::on_graphics_changed() { ShapeElement::on_graphics_changed(); for ( const auto& shape : shapes ) { if ( shape->is_instance() ) shape->on_graphics_changed(); } } void glaxnimate::model::Group::on_composition_changed(model::Composition*, model::Composition* new_comp) { for ( const auto& shape : shapes ) { shape->refresh_owner_composition(new_comp); } } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/glaxnimate_importer.cpp000664 001750 001750 00000002575 15165022620 035675 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/glaxnimate/glaxnimate_format.hpp" #include "glaxnimate/io/glaxnimate/import_state.hpp" #include "glaxnimate/model/assets/assets.hpp" using namespace glaxnimate; bool io::glaxnimate::GlaxnimateFormat::on_open ( QIODevice& file, const QString&, model::Document* document, const QVariantMap& ) { QJsonDocument jdoc; try { jdoc = QJsonDocument::fromJson(file.readAll()); } catch ( const QJsonParseError& err ) { error(i18n("Could not parse JSON: %1", err.errorString())); return false; } if ( !jdoc.isObject() ) { error(i18n("No JSON object found")); return false; } QJsonObject top_level = jdoc.object(); int document_version = top_level["format"].toObject()["format_version"].toInt(0); if ( document_version > format_version ) warning(i18n("Opening a file from a newer version of Glaxnimate")); detail::ImportState state(this, document, document_version); state.load_document(top_level); if ( document->assets()->compositions->values.empty() ) { document->assets()->compositions->values.insert(std::make_unique(document)); error(i18n("Missing composition")); return false; } return true; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/ellipse_solver.hpp000664 001750 001750 00000002175 15165022620 033123 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/math/bezier/bezier.hpp" namespace glaxnimate::math { class EllipseSolver { public: /** * \param center 2D vector, center of the ellipse * \param radii 2D vector, x/y radius of the ellipse * \param xrot Angle between the main axis of the ellipse and the x axis (in radians) */ EllipseSolver(const QPointF& center, const QPointF& radii, qreal xrot); QPointF point(qreal t) const; QPointF derivative(qreal t) const; bezier::Bezier to_bezier(qreal anglestart, qreal angle_delta); static bezier::Bezier from_svg_arc( QPointF start, qreal rx, qreal ry, qreal xrot, bool large, bool sweep, QPointF dest ); private: static qreal _alpha(qreal step); static QPointF _matrix_mul(qreal phi, const QPointF p, qreal sin_mul=1); static qreal _angle(const QPointF& u, const QPointF& v); QPointF center; QPointF radii; qreal xrot; }; } // namespace glaxnimate::math mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/comp_graph.hpp000664 001750 001750 00000004006 15165022620 032355 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include namespace glaxnimate::model { class Composition; class PreCompLayer; class Document; /** * \brief Dependency graph between compositions. * * The graph is a directed acyclic graph, rooted in the main composition of a document. * I might not be connected as you could have comps not used anywhere. * * This graph is used to avoid cyclical dependencies. */ class CompGraph { public: /** * \brief Adds a composition and scans it for existing layers */ void add_composition(model::Composition* comp); /** * \brief Remove a composition from the graph */ void remove_composition(model::Composition* comp); /** * \brief Registers \p layer to be a layer in \p comp. */ void add_connection(model::Composition* comp, model::PreCompLayer* layer); /** * \brief Registers \p layer to no longer be a layer in \p comp. */ void remove_connection(model::Composition* comp, model::PreCompLayer* layer); /** * \brief Returns a list of composition used by \p comp, */ std::vector children(model::Composition* comp) const; /** * \brief Returns whether starting from \p ancestor you can find a path to \p descendant using precomp layers. * * A comp is considered being ancestor of itself. */ bool is_ancestor_of(model::Composition* ancestor, model::Composition* descendant) const; /** * \brief Returns a list of compositions that can be added as a child of \p ancestor. * * Basically all the precomps in \p document that are not ancestors of \p ancestor. */ std::vector possible_descendants(model::Composition* ancestor, model::Document* document) const; private: std::unordered_map> layers; }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/aep/string_decoder.cpp000664 001750 001750 00000001343 15165022620 036617 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include "glaxnimate/module/extraformats/aep/string_decoder.hpp" #include QString glaxnimate::io::aep::decode_string(const QByteArray& data) { auto encoding = QStringConverter::encodingForData(data); if ( encoding ) return QStringDecoder(*encoding).decode(data); return QStringDecoder(QStringConverter::Utf8).decode(data); } QString glaxnimate::io::aep::decode_utf16(const QByteArray& data, bool big_endian) { auto encoding = big_endian ? QStringConverter::Utf16BE : QStringConverter::Utf16LE; return QStringDecoder(encoding).decode(data); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/solver.hpp000664 001750 001750 00000033025 15165022620 032664 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/math/vector.hpp" #include "glaxnimate/math/polynomial.hpp" #include namespace glaxnimate::math::bezier { template class CubicBezierSolver { public: using vector_type = Vec; using scalar = scalar_type; using argument_type = scalar; using bounding_box_type = std::pair; constexpr CubicBezierSolver(const std::array& points) noexcept : points_(points) { rebuild_coeff(); } constexpr CubicBezierSolver(Vec p0, Vec p1, Vec p2, Vec p3) noexcept : CubicBezierSolver{{p0, p1, p2, p3}} {} /** * \brief Finds the point along the bezier curve * \param t between 0 and 1 */ constexpr Vec solve(argument_type t) const noexcept { return ((a_ * t + b_ ) * t + c_ ) * t + d_; } constexpr scalar solve_component(argument_type t, int component) const noexcept { scalar a = detail::get(a_, component); scalar b = detail::get(b_, component); scalar c = detail::get(c_, component); scalar d = detail::get(d_, component); return ((a * t + b ) * t + c ) * t + d; } constexpr scalar derivative(argument_type factor, int component) const noexcept { scalar a = detail::get(a_, component); scalar b = detail::get(b_, component); scalar c = detail::get(c_, component); return (3 * a * factor + 2 * b) * factor + c; } /** * \brief Finds the tangent of the point on the bezier * \param factor between 0 and 1 * \return Angle in radians */ scalar tangent_angle(argument_type factor) const { return std::atan2(derivative(factor, 1), derivative(factor, 0)); } /** * \brief Finds the normal of the point on the bezier * \param factor between 0 and 1 * \return Angle in radians */ scalar normal_angle(argument_type factor) const { return std::atan2(derivative(factor, 0), derivative(factor, 1)); } constexpr const std::array& points() const noexcept { return points_; } // constexpr std::array& points() noexcept // { // return points_; // } template constexpr void set(const Vec& v) noexcept { points_[i] = v; rebuild_coeff(); } /** * \brief Splits a bezier * \param factor value between 0 and 1 determining the split position * \return Two vectors for the two resulting cubic beziers */ std::pair, std::array> split(argument_type factor) const { // linear if ( points_[0] == points_[1] && points_[2] == points_[3] ) { Vec mid = lerp(points_[0], points_[3], factor); return { {points_[0], points_[0], mid, mid}, {mid, mid, points_[3], points_[3]}, }; } Vec p01 = lerp(points_[0], points_[1], factor); Vec p12 = lerp(points_[1], points_[2], factor); Vec p23 = lerp(points_[2], points_[3], factor); Vec p012 = lerp(p01, p12, factor); Vec p123 = lerp(p12, p23, factor); Vec p0123 = lerp(p012, p123, factor); return { {points_[0], p01, p012, p0123}, {p0123, p123, p23, points_[3]} }; } bounding_box_type bounds() const { std::vector solutions; for ( int i = 0; i < detail::VecSize::value; i++ ) { bounds_solve(3 * detail::get(a_, i), 2 * detail::get(b_, i), detail::get(c_, i), solutions); } std::vector boundary_points; //Add Begin and end point not the control points! boundary_points.push_back(points_[0]); boundary_points.push_back(points_[3]); for ( scalar e : solutions ) boundary_points.push_back(solve(e)); Vec min; Vec max; for ( int i = 0; i < detail::VecSize::value; i++ ) { scalar cmin = std::numeric_limits::max(); scalar cmax = std::numeric_limits::lowest(); for ( const Vec& p : boundary_points ) { if ( detail::get(p, i) < cmin ) cmin = detail::get(p, i); if ( detail::get(p, i) > cmax ) cmax = detail::get(p, i); } detail::get(max, i) = cmax; detail::get(min, i) = cmin; } return {min, max}; } /** * \brief Returns the \p t for the min/max points for the given component * * \note The returned value is sorted so that \p first <= \p second */ std::pair extrema(int component) const { std::vector solutions; bounds_solve(3 * detail::get(a_, component), 2 * detail::get(b_, component), detail::get(c_, component), solutions); // No solution: end points have the extrema if ( solutions.size() == 0 ) return {0, 1}; // One solution: one of the end points has an extreme if ( solutions.size() == 1 ) { auto val = solve_component(solutions[0], component); // The end point is a minimum if ( detail::get(points_[0], component) < val ) { // Last point is min if ( detail::get(points_[3], component) < detail::get(points_[0], component) ) return {solutions[0], 1}; // First point is min return {0, solutions[0]}; } // Last point is min else if ( detail::get(points_[3], component) < val ) { return {solutions[0], 1}; } // end point is a maximum else { // First point is max if ( detail::get(points_[0], component) > val ) return {0, solutions[0]}; // Last point is max return {solutions[0], 1}; } } if ( solutions[0] > solutions[1] ) return {solutions[1], solutions[0]}; return {solutions[0], solutions[1]}; } /** * \brief Return inflection points for a 2D bezier */ std::vector inflection_points() const { auto denom = detail::get(a_, 1) * detail::get(b_, 0) - detail::get(a_, 0) * detail::get(b_, 1); if ( qFuzzyIsNull(denom) ) return {}; auto t_cusp = -0.5 * (detail::get(a_, 1) * detail::get(c_, 0) - detail::get(a_, 0) * detail::get(c_, 1)) / denom; auto square = t_cusp * t_cusp - 1./3. * (detail::get(b_, 1) * detail::get(c_, 0) - detail::get(b_, 0) * detail::get(c_, 1)) / denom; if ( square < 0 ) return {}; auto root = std::sqrt(square); if ( qFuzzyIsNull(root) ) { if ( is_valid_inflection(t_cusp) ) return {t_cusp}; return {}; } std::vector roots; roots.reserve(2); if ( is_valid_inflection(t_cusp - root) ) roots.push_back(t_cusp - root); if ( is_valid_inflection(t_cusp + root) ) roots.push_back(t_cusp + root); return roots; } std::vector> intersections( const CubicBezierSolver& other, int max_count = 1, scalar tolerance = 2, int max_recursion = 10 ) const { std::vector> intersections; intersects_impl(IntersectData(*this), IntersectData(other), max_count, tolerance, intersections, 0, max_recursion); return intersections; } /** * \brief Returns the t corresponding to the given value for a component. * \returns `t` or -1 in case of no root has been found */ scalar t_at_value(scalar value, int comp = 0) const { auto roots = math::cubic_roots( detail::get(a_, comp), detail::get(b_, comp), detail::get(c_, comp), detail::get(d_, comp) - value ); for ( auto root : roots ) { if ( valid_solution(root) ) return root; } return -1; } private: static constexpr bool is_valid_inflection(scalar root) noexcept { return root > 0 && root < 1; } // You get these terms by expanding the Bezier definition and re-arranging them as a polynomial in t // B(t) = a t^3 + b t^2 + c t + d static constexpr scalar a(const scalar& k0, const scalar& k1, const scalar& k2, const scalar& k3) noexcept { return -k0 + k1*3 + k2 * -3 + k3; } static constexpr scalar b(const scalar& k0, const scalar& k1, const scalar& k2) noexcept { return k0 * 3 + k1 * -6 + k2 * 3; } static constexpr scalar c(const scalar& k0, const scalar& k1) noexcept { return k0 * -3 + k1 * 3; } static constexpr scalar d(const scalar& k0) noexcept { return k0; } static void bounds_solve(scalar a, scalar b, scalar c, std::vector& solutions) { scalar d = b * b - 4. * a * c; // no real solution if ( d < 0 ) return; if ( qFuzzyIsNull(a) ) { // linear case add_bounds_solution(-c / b, solutions); } else { add_bounds_solution((-b + std::sqrt(d)) / (2 * a), solutions); if ( d != 0 ) add_bounds_solution((-b - std::sqrt(d)) / (2 * a), solutions); } } static bool valid_solution(scalar& root) { if ( root >= 0 && root <= 1 ) return true; if ( qFuzzyIsNull(root) ) { root = 0; return true; } if ( qFuzzyCompare(root, 1) ) { root = 1; return true; } return false; } static void add_bounds_solution(scalar solution, std::vector& solutions) { if ( valid_solution(solution) ) solutions.push_back(solution); } struct IntersectData { IntersectData( const CubicBezierSolver& solver, const bounding_box_type& box, argument_type t1, argument_type t2 ) : bez(solver), width(box.second.x() - box.first.x()), height(box.second.y() - box.first.y()), center((box.first + box.second) / 2), t1(t1), t2(t2), t((t1 + t2) / 2) {} IntersectData(const CubicBezierSolver& solver) : IntersectData(solver, solver.bounds(), 0, 1) {} std::pair split() const { auto split = bez.split(0.5); return { IntersectData(split.first), IntersectData(split.second) }; } bool intersects(const IntersectData& other) const { return std::abs(center.x() - other.center.x()) * 2 < width + other.width && std::abs(center.y() - other.center.y()) * 2 < height + other.height; } CubicBezierSolver bez; scalar width; scalar height; Vec center; argument_type t1, t2, t; }; static void intersects_impl( const IntersectData& d1, const IntersectData& d2, std::size_t max_count, scalar tolerance, std::vector>& intersections, int depth, int max_recursion ) { if ( !d1.intersects(d2) ) return; if ( depth >= max_recursion || (d1.width <= tolerance && d1.height <= tolerance && d2.width <= tolerance && d2.height <= tolerance) ) { intersections.emplace_back(d1.t, d2.t); return; } auto d1s = d1.split(); auto d2s = d2.split(); std::array, 4> splits = {{ {d1s.first, d2s.first }, {d1s.first, d2s.second}, {d1s.second, d2s.first }, {d1s.second, d2s.second} }}; for ( const auto& p : splits ) { intersects_impl(p.first, p.second, max_count, tolerance, intersections, depth + 1, max_recursion); if ( intersections.size() >= max_count ) return; } } void rebuild_coeff() { for ( int component = 0; component < detail::VecSize::value; component++ ) { detail::get(a_, component) = a( detail::get(points_[0], component), detail::get(points_[1], component), detail::get(points_[2], component), detail::get(points_[3], component) ); detail::get(b_, component) = b( detail::get(points_[0], component), detail::get(points_[1], component), detail::get(points_[2], component) ); detail::get(c_, component) = c( detail::get(points_[0], component), detail::get(points_[1], component) ); detail::get(d_, component) = d( detail::get(points_[0], component) ); } } std::array points_; // Polynomial coefficients (a t^3 + b t^2 + c t + d = 0) Vec a_, b_, c_, d_; }; } // namespace glaxnimate::math::bezier mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/log/listener_file.hpp000664 001750 001750 00000001563 15165022620 032550 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/log/logger.hpp" namespace glaxnimate::log { class ListenerFile: public LogListener { public: explicit ListenerFile(const QString& filename) : file(filename) { file.open(QFile::WriteOnly|QFile::Text); } protected: void on_line(const LogLine& line) override { QString log; log += line.time.toString(Qt::ISODate); log += ' '; log += Logger::severity_name(line.severity); log += ' '; log += line.source; log += ' '; log += line.source_detail; log += ' '; log += line.message; log += '\n'; file.write(log.toUtf8()); file.flush(); } private: QFile file; }; } // namespace glaxnimate::log mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shape.hpp000664 001750 001750 00000015517 15165022620 032632 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/document_node.hpp" #include "glaxnimate/math/bezier/bezier.hpp" #include "glaxnimate/model/property/object_list_property.hpp" namespace glaxnimate::model { using ShapeListProperty = ObjectListProperty; class Composition; template class PathCache { public: bool is_dirty(FrameTime time) const { return time != cached_time || dirty; } void mark_dirty() { dirty = true; } const T& path() const { return cached_path; } void set_path(FrameTime time, const T& path) { cached_time = time; dirty = false; cached_path = path; } private: bool dirty = true; T cached_path = {}; FrameTime cached_time = 0; }; /** * \brief Base class for all shape elements */ class ShapeElement : public VisualNode { Q_OBJECT public: explicit ShapeElement(model::Document* document); ~ShapeElement(); int docnode_child_count() const override { return 0; } DocumentNode* docnode_child(int) const override { return nullptr; } int docnode_child_index(DocumentNode*) const override { return -1; } /** * \brief Index within its parent */ int position() const; virtual void add_shapes(FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const = 0; math::bezier::MultiBezier shapes(FrameTime t) const; ShapeListProperty* owner() const; Composition* owner_composition() const; void clear_owner(); virtual glaxnimate::math::bezier::MultiBezier to_clip(FrameTime t) const; glaxnimate::math::bezier::MultiBezier to_painter_path(FrameTime t) const; virtual std::unique_ptr to_path() const; Q_SIGNALS: void position_updated(); void siblings_changed(); protected: const ShapeListProperty& siblings() const; void on_property_changed(const BaseProperty* prop, const QVariant& value) override; void on_parent_changed(model::DocumentNode* old_parent, model::DocumentNode* new_parent) override; void refresh_owner_composition(glaxnimate::model::Composition* comp); virtual void on_composition_changed(model::Composition* old_comp, model::Composition* new_comp) { Q_UNUSED(old_comp); Q_UNUSED(new_comp); } virtual glaxnimate::math::bezier::MultiBezier to_painter_path_impl(FrameTime t) const = 0; void on_graphics_changed() override; private: void set_position(ShapeListProperty* property, int pos); class Private; std::unique_ptr d; friend ShapeListProperty; friend class Group; }; template<> class ObjectListProperty : public detail::ObjectListProperty { public: using detail::ObjectListProperty::ObjectListProperty; /** * \brief End iterator for a range that includes a modifier then stops */ iterator past_first_modifier() const; QRectF bounding_rect(FrameTime t) const; protected: void update_pos(int index) { int i; for ( i = size() - 1; i >= index; i-- ) objects[i]->set_position(this, i); for ( ; i >= 0; i-- ) objects[i]->siblings_changed(); } void on_insert(int index) override { update_pos(index); } void on_remove(int index) override { update_pos(index); } void on_move(int index_a, int index_b) override { if ( index_b < index_a ) std::swap(index_a, index_b); for ( int i = index_a; i <= index_b; i++ ) objects[i]->set_position(this, i); for ( int i = 0; i <= index_b; i++ ) objects[i]->siblings_changed(); } }; class Path; /** * \brief Classes that define shapes on their own (but not necessarily style) */ class Shape : public ShapeElement { Q_OBJECT GLAXNIMATE_PROPERTY(bool, reversed, false, {}, {}, PropertyTraits::Visual|PropertyTraits::Hidden) public: using ShapeElement::ShapeElement; virtual math::bezier::Bezier to_bezier(FrameTime t) const = 0; void add_shapes(FrameTime t, math::bezier::MultiBezier & bez, const QTransform& transform) const override; std::unique_ptr to_path() const override; protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(FrameTime t) const override; }; /** * \brief Base class for types that perform operations on their sibling shapes */ class ShapeOperator : public ShapeElement { Q_OBJECT public: ShapeOperator(model::Document* doc); math::bezier::MultiBezier collect_shapes(FrameTime t, const QTransform& transform) const; math::bezier::MultiBezier collect_shapes_from(const std::vector& shapes, FrameTime t, const QTransform& transform) const; const std::vector& affected() const { return affected_elements; } protected: virtual void do_collect_shapes(const std::vector& shapes, FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const; virtual bool skip_stylers() const { return true; } void on_graphics_changed() override; private Q_SLOTS: void update_affected(); Q_SIGNALS: void shape_changed(); private: std::vector affected_elements; mutable PathCache bezier_cache; }; /** * \brief Base class for elements that modify other shapes */ class Modifier : public ShapeOperator { Q_OBJECT public: using ShapeOperator::ShapeOperator; void add_shapes(FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const override; QRectF local_bounding_rect(FrameTime t) const override; virtual math::bezier::MultiBezier process(FrameTime t, const math::bezier::MultiBezier& mbez) const = 0; protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(FrameTime t) const override; /** * \brief Whether to process on the whole thing (or individual objects) */ virtual bool process_collected() const = 0; void do_collect_shapes(const std::vector& shapes, FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const override; virtual bool skip_stylers() const override { return false; } }; /** * \brief CRTP to override some methods using static functions * (so said methods can be accessed with no object) */ template class StaticOverrides : public Base { public: using Ctor = StaticOverrides; using Base::Base; QIcon tree_icon() const override { return Derived::static_tree_icon(); } QString type_name_human() const override { return Derived::static_type_name_human(); } static QString static_class_name() { return detail::naked_type_name(); } }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/lottie_format.hpp000664 001750 001750 00000002377 15165022620 033726 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/io/base.hpp" #include "glaxnimate/io/io_registry.hpp" namespace glaxnimate::io::lottie { class LottieFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "lottie"; } QString name() const override { return i18n("Lottie Animation"); } QStringList extensions(Direction) const override { return {"lot", "json"}; } bool can_save() const override { return true; } bool can_open() const override { return true; } std::unique_ptr save_settings(model::Composition*) const override; QCborMap to_json(model::Composition* comp, bool strip = false, bool strip_raster = false, const QVariantMap& settings = {}); bool load_json(const QByteArray& data, model::Document* document); private: bool on_save(QIODevice& file, const QString& filename, model::Composition* comp, const QVariantMap& setting_values) override; bool on_open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& setting_values) override; }; } // namespace glaxnimate::io::lottie mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/polynomial.hpp000664 001750 001750 00000001005 15165022620 032246 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::math { /** * \brief Returns the real roots of * a x^3 + b x^2 + c x + d = 0 */ std::vector cubic_roots(double a, double b, double c, double d); /** * \brief Returns the real roots of * a x^2 + b x + c = 0 */ std::vector quadratic_roots(double a, double b, double c); } // namespace glaxnimate::math mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/object.cpp000664 001750 001750 00000011421 15165022620 031476 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/object.hpp" #include #include "glaxnimate/model/property/property.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/log/log.hpp" #include "glaxnimate/model/animation/meta_animatable.hpp" class glaxnimate::model::Object::Private { public: Private(Object* parent, Document* document) : document(document), animation_group(parent) {} std::unordered_map props; std::vector prop_order; Document* document; FrameTime current_time = 0; MetaAnimatable animation_group; }; glaxnimate::model::Object::Object(Document* document) : d(std::make_unique(this, document)) { if ( document && thread() != document->thread() ) moveToThread(document->thread()); } glaxnimate::model::Object::~Object() = default; void glaxnimate::model::Object::assign_from(const glaxnimate::model::Object* other) { other->clone_into(this); } void glaxnimate::model::Object::transfer(glaxnimate::model::Document* document) { if ( thread() != document->thread() ) moveToThread(document->thread()); on_transfer(document); d->document = document; for ( auto prop: d->prop_order ) prop->transfer(document); } void glaxnimate::model::Object::clone_into(glaxnimate::model::Object* dest) const { if ( dest->metaObject() != metaObject() ) { log::Log log("Object", type_name()); log.stream(log::Error) << "trying to clone into" << dest->type_name() << "from" << type_name(); log.stream(log::Info) << "make sure clone_covariant is implemented for" << type_name() << "or use GLAXNIMATE_OBJECT"; return; } for ( BaseProperty* prop : d->prop_order ) dest->get_property(prop->name())->assign_from(prop); } void glaxnimate::model::Object::property_value_changed(const BaseProperty* prop, const QVariant& value) { on_property_changed(prop, value); Q_EMIT property_changed(prop, value); if ( prop->traits().flags & PropertyTraits::Visual ) { d->document->graphics_invalidated(); Q_EMIT visual_property_changed(prop, value); } } void glaxnimate::model::Object::add_property(glaxnimate::model::BaseProperty* prop) { d->props[prop->name()] = prop; d->prop_order.push_back(prop); } QVariant glaxnimate::model::Object::get(const QString& property) const { auto it = d->props.find(property); if ( it == d->props.end() ) return QVariant{}; return it->second->value(); } glaxnimate::model::BaseProperty * glaxnimate::model::Object::get_property ( const QString& property ) { auto it = d->props.find(property); if ( it == d->props.end() ) return nullptr; return it->second; } bool glaxnimate::model::Object::set(const QString& property, const QVariant& value) { auto it = d->props.find(property); if ( it == d->props.end() ) return false; return it->second->set_value(value); } bool glaxnimate::model::Object::has ( const QString& property ) const { return d->props.find(property) != d->props.end(); } const std::vector& glaxnimate::model::Object::properties() const { return d->prop_order; } QString glaxnimate::model::Object::type_name() const { return detail::naked_type_name(metaObject()->className()); } QString glaxnimate::model::detail::naked_type_name(QString class_name) { int ns = class_name.lastIndexOf(":"); if ( ns != -1 ) class_name = class_name.mid(ns+1); return class_name; } glaxnimate::model::Document * glaxnimate::model::Object::document() const { return d->document; } void glaxnimate::model::Object::push_command(QUndoCommand* cmd) { d->document->push_command(cmd); } bool glaxnimate::model::Object::set_undoable ( const QString& property, const QVariant& value ) { auto it = d->props.find(property); if ( it != d->props.end() ) return it->second->set_undoable(value); return false; } void glaxnimate::model::Object::set_time(glaxnimate::model::FrameTime t) { d->current_time = t; for ( auto prop: d->prop_order ) prop->set_time(t); } glaxnimate::model::FrameTime glaxnimate::model::Object::time() const { return d->current_time; } void glaxnimate::model::Object::stretch_time(qreal multiplier) { for ( const auto& prop : d->prop_order ) prop->stretch_time(multiplier); d->current_time *= multiplier; } glaxnimate::model::MetaAnimatable *glaxnimate::model::Object::grouped_animations_ptr() const { return &d->animation_group; } glaxnimate::model::MetaAnimatable &glaxnimate::model::Object::grouped_animations() { return d->animation_group; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/thorvg/thorvg_module.hpp000664 001750 001750 00000000732 15165022620 034614 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/module.hpp" namespace glaxnimate::thorvg { class Module : public module::Module { public: Module() : module::Module(i18n("ThorVG Renderer")) {} std::vector components() const override; protected: void initialize() override; }; } // namespace glaxnimate::thorvg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_renderer.hpp000664 001750 001750 00000002435 15165022620 033035 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/shapes/shape.hpp" namespace glaxnimate::model { class EmbeddedFont; } // namespace glaxnimate::model namespace glaxnimate::io::svg { enum AnimationType { NotAnimated, SMIL }; enum class CssFontType { None, Embedded, FontFace, Link, }; class SvgRenderer { public: SvgRenderer(AnimationType animated, CssFontType font_type); ~SvgRenderer(); void write_composition(model::Composition* comp, model::FrameTime t); void write_main(model::Composition* comp, model::FrameTime t); void write_shape(model::ShapeElement* shape, model::FrameTime t); void write_node(model::DocumentNode* node, model::FrameTime t); QDomDocument dom() const; void write(QIODevice* device, bool indent); static CssFontType suggested_type(model::EmbeddedFont* font); private: class Private; std::unique_ptr d; }; /** * \brief Converts a multi bezier into path data * \returns pair of [path data, sodipodi nodetypes] */ std::pair path_data(const math::bezier::MultiBezier& shape); } // namespace glaxnimate::io::svg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/cairo/cairo_module.cpp000664 001750 001750 00000001372 15165022620 034160 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "cairo_module.hpp" #include #include "postscript_format.hpp" #include "pdf_format.hpp" #include "cairo_renderer.hpp" std::vector glaxnimate::cairo::Module::components() const { return { {QStringLiteral("cairo"), {}, cairo_version_string(), QStringLiteral("https://www.cairographics.org/"), "LGPL"} }; } void glaxnimate::cairo::Module::initialize() { register_io_classes(); renderer::RendererRegistry::instance().register_factory(QStringLiteral("Cairo"), [](int q){ return std::make_unique(q); }); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/bezier_length.hpp000664 001750 001750 00000002656 15165022620 034201 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/math/bezier/bezier.hpp" namespace glaxnimate::math::bezier { class LengthData { public: struct SplitInfo { int index = 0; qreal ratio = 0; qreal length = 0; const LengthData* child = nullptr; SplitInfo descend() const { return child->at_ratio(ratio); } }; explicit LengthData(const Solver& segment, int steps); explicit LengthData(const Bezier& bez, int steps); explicit LengthData(const MultiBezier& mbez, int steps); SplitInfo at_ratio(qreal ratio) const; SplitInfo at_length(qreal length) const; /** * \brief Returns the length such that * `at_length(length).ratio == ratio` */ qreal from_ratio(qreal ratio) const; qreal length() const noexcept; /** * \returns The length at which the child at \p index starts */ qreal child_start(int index) const; /** * \returns The length at which the child at \p index ends */ qreal child_end(int index) const; private: LengthData(qreal t, qreal length, qreal cumulative_length); qreal t_ = 0; qreal length_ = 0; qreal cumulative_length_ = 0; std::vector children_; bool leaf_ = false; }; } // namespace glaxnimate::math::bezier mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/000775 001750 001750 00000000000 15165022620 030043 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/bezier/cubic_struts.hpp000664 001750 001750 00000002002 15165022620 034052 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/math/bezier/segment.hpp" namespace glaxnimate::math::bezier { struct BezierStruts { QPointF B; ///< Point on a bezier segment qreal t; ///< Bezier parameter in (0,1) (should never be 0 or 1) QPointF e1; ///< Linear handles in de casteljau lerp(e1, e2, t) = B QPointF e2; }; QPointF get_quadratic_handle(const math::bezier::BezierSegment& segment, const QPointF& B, qreal t); math::bezier::BezierSegment cubic_segment_from_struts( const math::bezier::BezierSegment& segment, const BezierStruts& struts ); BezierStruts cubic_struts_idealized(const math::bezier::BezierSegment& segment, const QPointF& B); struct ProjectResult; BezierStruts cubic_struts_projection( const math::bezier::BezierSegment& segment, const QPointF& B, const math::bezier::ProjectResult& projection ); } // namespace glaxnimate::math::bezier src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/option_list_property.hpp000664 001750 001750 00000004613 15165022620 036356 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/property/property.hpp" #define GLAXNIMATE_PROPERTY_OPTIONS(type, name, defval, container, ...) \ public: \ OptionListProperty name{this, kli18n(#name), defval, __VA_ARGS__}; \ GLAXNIMATE_PROPERTY_IMPL(type, name) \ Q_PROPERTY(QVariantList name##_options READ name##_options) \ QVariantList name##_options() const { return name.value_options(); } \ // macro end namespace glaxnimate::model { class OptionListPropertyBase : public BaseProperty { Q_GADGET public: enum OptionListFlags { NoFlags = 0, LaxValues = 1, FontCombo = 2, }; using BaseProperty::BaseProperty; virtual ~OptionListPropertyBase() = default; virtual QVariantList value_options() const = 0; int option_list_flags() const { return option_flags; } protected: int option_flags; }; template class OptionListProperty : public detail::PropertyTemplate { public: OptionListProperty( Object* obj, const utils::LazyLocalizedString& name, Type default_value, PropertyCallback option_list, PropertyCallback emitter = {}, PropertyCallback validator = {}, PropertyTraits::Flags flags = PropertyTraits::Visual, int option_flags = 0 ) : detail::PropertyTemplate( obj, name, std::move(default_value), std::move(emitter), std::move(validator), PropertyTraits::Flags(flags|PropertyTraits::OptionList) ), option_list(std::move(option_list)) { this->option_flags = option_flags; } Container options() const { return option_list(this->object()); } QVariantList value_options() const override { QVariantList list; for ( const auto& value : option_list(this->object()) ) list.push_back(QVariant::fromValue(value)); return list; } private: PropertyCallback option_list; }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/avd/avd_format.cpp000664 001750 001750 00000002550 15165022620 035754 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/avd/avd_format.hpp" #include "glaxnimate/module/extraformats/avd/avd_parser.hpp" #include "glaxnimate/io/svg/parse_error.hpp" #include "glaxnimate/module/extraformats/avd/avd_renderer.hpp" bool glaxnimate::io::avd::AvdFormat::on_open(QIODevice& file, const QString& filename, model::Document* document, const QVariantMap& options) { auto on_error = [this](const QString& s){warning(s);}; try { QSize forced_size = options["forced_size"].toSize(); model::FrameTime default_time = options["default_time"].toFloat(); auto resource_path = QFileInfo(filename).dir(); AvdParser(&file, resource_path, document, on_error, this, forced_size, default_time).parse_to_document(); return true; } catch ( const svg::SvgParseError& err ) { error(err.formatted(QFileInfo(filename).baseName())); return false; } } bool glaxnimate::io::avd::AvdFormat::on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap&) { auto on_error = [this](const QString& s){warning(s);}; AvdRenderer rend(on_error); rend.render(comp); auto dom = rend.single_file(); file.write(dom.toByteArray(4)); return true; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/style/stroke.cpp000664 001750 001750 00000003772 15165022620 034174 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/style/stroke.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Stroke) void glaxnimate::model::Stroke::on_paint(renderer::Renderer* p, glaxnimate::model::FrameTime t, glaxnimate::model::VisualNode::PaintMode, glaxnimate::model::Modifier* modifier) const { QPen pen(brush(t), width.get_at(t)); pen.setCapStyle(Qt::PenCapStyle(cap.get())); pen.setJoinStyle(Qt::PenJoinStyle(join.get())); pen.setMiterLimit(miter_limit.get()); p->set_stroke({pen, opacity.get_at(t)}); math::bezier::MultiBezier bez; if ( modifier ) bez = modifier->collect_shapes_from(affected(), t, {}); else bez = collect_shapes(t, {}); p->draw_path(bez); } void glaxnimate::model::Stroke::set_pen_style ( const QPen& pen_style ) { color.set(pen_style.color()); width.set(pen_style.width()); cap.set(glaxnimate::model::Stroke::Cap(pen_style.capStyle())); join.set(glaxnimate::model::Stroke::Join(pen_style.joinStyle())); miter_limit.set(pen_style.miterLimit()); } void glaxnimate::model::Stroke::set_pen_style_undoable(const QPen& pen_style) { color.set_undoable(pen_style.color()); width.set_undoable(pen_style.width()); cap.set_undoable(QVariant::fromValue(glaxnimate::model::Stroke::Cap(pen_style.capStyle()))); join.set_undoable(QVariant::fromValue(glaxnimate::model::Stroke::Join(pen_style.joinStyle()))); miter_limit.set_undoable(pen_style.miterLimit()); } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Stroke::to_painter_path_impl(glaxnimate::model::FrameTime t) const { QPainterPathStroker s; s.setWidth(width.get_at(t)); s.setCapStyle(Qt::PenCapStyle(cap.get())); s.setJoinStyle(Qt::PenJoinStyle(join.get())); s.setMiterLimit(miter_limit.get()); glaxnimate::math::bezier::MultiBezier bez; bez.append(s.createStroke(collect_shapes(t, {}).painter_path())); return bez; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/text.cpp000664 001750 001750 00000040712 15165022620 033767 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/shapes/text.hpp" #include #include #include #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/command/undo_macro_guard.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/custom_font.hpp" #include "glaxnimate/math/bezier/bezier_length.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Font) GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::TextShape) class glaxnimate::model::Font::Private { public: QStringList styles; QFont query; QRawFont raw; QRawFont raw_scaled; QFontMetricsF metrics; Private() : raw(QRawFont::fromFont(query)), metrics(query) { #ifdef Q_OS_ANDROID query.setPointSizeF(32); #endif // query.setKerning(false); upscaled_raw(); } void update_data() { // disable kerning because QRawFont doesn't handle kerning properly // query.setKerning(false); raw = QRawFont::fromFont(query); // Dynamic fonts might have weird names if ( !raw.familyName().startsWith(query.family()) ) { QString family = query.family(); QFont new_query = query; new_query.setFamily(family + ' ' + query.styleName()); auto new_raw = QRawFont::fromFont(new_query); if ( new_raw.familyName().startsWith(family) ) { query = new_query; raw = new_raw; } } metrics = QFontMetricsF(query); upscaled_raw(); } static const QStringList& default_styles() { static QStringList styles; if ( styles.empty() ) { auto meta = QMetaEnum::fromType(); for ( int i = 0; i < meta.keyCount(); i++ ) { QString key = meta.key(i); for ( const char* style : {"", " Italic", " Oblique"} ) { styles.push_back(key + style); } } } return styles; } void refresh_styles(Font* parent) { if ( !raw.familyName().startsWith(query.family()) ) { styles = default_styles(); } else { styles = QFontDatabase::styles(parent->family.get()); if ( !parent->valid_style(parent->style.get()) && !styles.empty() ) parent->style.set(styles[0]); } } // QRawFont::pathForGlyph doesn't work well, so we work around it void upscaled_raw() { QFont font = query; #ifndef Q_OS_ANDROID font.setPointSizeF(qMin(4000., font.pointSizeF() * 1000)); #endif raw_scaled = QRawFont::fromFont(font); } glaxnimate::math::bezier::MultiBezier path_for_glyph(quint32 glyph, bool fix_paint) { QPainterPath path = raw_scaled.pathForGlyph(glyph); if ( fix_paint ) path = path.simplified(); if ( raw_scaled.pixelSize() == 0 ) return path; glaxnimate::math::bezier::MultiBezier dest; qreal mult = raw.pixelSize() / raw_scaled.pixelSize(); std::array data; int data_i = 0; for ( int i = 0; i < path.elementCount(); i++ ) { auto element = path.elementAt(i); QPointF p = QPointF(element) * mult; switch ( element.type ) { case QPainterPath::MoveToElement: dest.move_to(p); break; case QPainterPath::LineToElement: dest.line_to(p); break; case QPainterPath::CurveToElement: data_i = 0; data[0] = p; break; case QPainterPath::CurveToDataElement: ++data_i; data[data_i] = p; if ( data_i == 2 ) { dest.cubic_to(data[0], data[1], data[2]); data_i = -1; } break; } } return dest; } }; glaxnimate::model::Font::Font(glaxnimate::model::Document* doc) : Object(doc), d(std::make_unique()) { family.set(d->raw.familyName()); style.set(d->raw.styleName()); size.set(d->query.pointSize()); d->refresh_styles(this); on_transfer(doc); } glaxnimate::model::Font::~Font() = default; void glaxnimate::model::Font::refresh_data ( bool update_styles ) { d->query = CustomFontDatabase::instance().font(family.get(), style.get(), size.get()); d->update_data(); if ( update_styles ) d->refresh_styles(this); Q_EMIT font_changed(); } void glaxnimate::model::Font::on_font_changed() { refresh_data(false); } void glaxnimate::model::Font::on_transfer ( model::Document* doc ) { if ( document() ) disconnect(document()->assets()->fonts.get(), nullptr, this, nullptr); if ( doc ) { connect(doc->assets()->fonts.get(), &FontList::font_added, this, [this]{ refresh_data(true); document()->graphics_invalidated(); }); } } void glaxnimate::model::Font::on_family_changed() { refresh_data(true); } bool glaxnimate::model::Font::valid_style(const QString& style) { return d->styles.contains(style); } const QFont & glaxnimate::model::Font::query() const { return d->query; } const QRawFont & glaxnimate::model::Font::raw_font() const { return d->raw; } QStringList glaxnimate::model::Font::styles() const { return d->styles; } const QFontMetricsF & glaxnimate::model::Font::metrics() const { return d->metrics; } QString glaxnimate::model::Font::type_name_human() const { return i18n("Font"); } glaxnimate::math::bezier::MultiBezier glaxnimate::model::Font::path_for_glyph(quint32 glyph, glaxnimate::model::Font::CharDataCache& cache, bool fix_paint) const { auto it = cache.find(glyph); if ( it != cache.end() ) return it->second; glaxnimate::math::bezier::MultiBezier path = d->path_for_glyph(glyph, fix_paint); cache.emplace(glyph, path); return path; } void glaxnimate::model::Font::from_qfont(const QFont& f) { command::UndoMacroGuard g(i18n("Change Font"), document()); QFontInfo finfo(f); family.set_undoable(finfo.family()); style.set_undoable(finfo.styleName()); size.set_undoable(f.pointSizeF()); } glaxnimate::model::Font::ParagraphData glaxnimate::model::Font::layout(const QString& text) const { glaxnimate::model::Font::ParagraphData para_data; auto lines = text.split('\n'); QTextLayout layout(text, d->query, nullptr); QTextOption option; option.setUseDesignMetrics(true); layout.setTextOption(option); layout.beginLayout(); for ( const auto& line_size : lines ) { QTextLine line = layout.createLine(); if ( !line.isValid() ) break; line.setNumColumns(line_size.size()); line.setLeadingIncluded(true); } layout.endLayout(); qreal line_y = 0; qreal yoff = -d->metrics.ascent(); for ( int ln = 0; ln < layout.lineCount(); ln++ ) { QTextLine line = layout.lineAt(ln); auto& line_data = para_data.emplace_back(); line_data.baseline = QPointF(0, line_y); line_data.bounds = line.rect(); line_data.text = lines[ln]; QPointF baseline(0, line_y + yoff); for ( const auto& run : line.glyphRuns() ) { auto glyphs = run.glyphIndexes(); line_data.glyphs.reserve(line_data.glyphs.size() + glyphs.size()); auto positions = run.positions(); for ( int i = 0; i < glyphs.size(); i++ ) { line_data.glyphs.push_back({ glyphs[i], positions[i] + baseline }); } } line_data.advance = QPointF(line.cursorToX(lines[ln].size()), 0); line_y += line_spacing(); } // QRawFont way: for some reason it ignores KernedAdvances /* qreal line_y = 0; for ( const auto& line : text.split('\n') ) { auto glyphs = d->raw.glyphIndexesForString(line); auto advances = d->raw.advancesForGlyphIndexes(glyphs, QRawFont::UseDesignMetrics|QRawFont::KernedAdvances); auto& line_data = para_data.emplace_back(); line_data.glyphs.reserve(glyphs.size()); line_data.text = line; line_data.baseline = line_data.advance = QPointF(0, line_y); for ( int i = 0; i < glyphs.size(); i++ ) { line_data.glyphs.push_back({ glyphs[i], line_data.advance, }); line_data.advance += advances[i]; } line_y += line_spacing(); } */ return para_data; } qreal glaxnimate::model::Font::line_spacing() const { // for some reason QTextLayout ignores leading() return line_spacing_unscaled() * line_height.get(); } qreal glaxnimate::model::Font::line_spacing_unscaled() const { // for some reason QTextLayout ignores leading() return d->metrics.ascent() + d->metrics.descent(); } QStringList glaxnimate::model::Font::families() const { return QFontDatabase::families(); } QList glaxnimate::model::Font::standard_sizes() const { auto list = QFontDatabase::standardSizes(); int actual = d->query.pointSize(); auto it = std::upper_bound(list.begin(), list.end(), actual); if ( it == list.begin() || *(it-1) != actual ) list.insert(it, actual); return list; } glaxnimate::model::TextShape::TextShape(glaxnimate::model::Document* document) : ShapeElement(document) { connect(font.get(), &Font::font_changed, this, &TextShape::on_font_changed); } void glaxnimate::model::TextShape::on_text_changed() { shape_cache.clear(); propagate_bounding_rect_changed(); } void glaxnimate::model::TextShape::on_font_changed() { cache.clear(); on_text_changed(); } const glaxnimate::math::bezier::MultiBezier & glaxnimate::model::TextShape::untranslated_path(FrameTime t) const { if ( shape_cache.empty() ) { if ( path.get() ) { QString txt = text.get(); txt.replace('\n', ' '); auto bezier = path->shapes(t); const int length_steps = 5; math::bezier::LengthData length_data(bezier, length_steps); for ( const auto& line : font->layout(txt) ) { for ( const auto& glyph : line.glyphs ) { qreal x = path_offset.get_at(t) + glyph.position.x(); if ( x > length_data.length() || x < 0 ) continue; auto glyph_shape = font->path_for_glyph(glyph.glyph, cache, true); auto glyph_rect = glyph_shape.bounding_box(); auto start1 = length_data.at_length(x); auto start2 = start1.descend(); auto start_p = bezier.beziers()[start1.index].split_segment_point(start2.index, start2.ratio); auto end1 = length_data.at_length(x + glyph_rect.width()); auto end2 = end1.descend(); auto end_p = bezier.beziers()[end1.index].split_segment_point(end2.index, end2.ratio); QTransform mat; mat.translate(start_p.pos.x(), start_p.pos.y()); mat.rotate(qRadiansToDegrees(math::atan2(end_p.pos.y() - start_p.pos.y(), end_p.pos.x() - start_p.pos.x()))); shape_cache += glyph_shape.transformed(mat); } } } else { for ( const auto& line : font->layout(text.get()) ) for ( const auto& glyph : line.glyphs ) shape_cache += font->path_for_glyph(glyph.glyph, cache, true).translated(glyph.position); } } return shape_cache; } void glaxnimate::model::TextShape::add_shapes(glaxnimate::model::FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const { if ( !transform.isIdentity() ) { auto mb = shape_data(t); mb.transform(transform); bez.append(mb); } else { bez.append(shape_data(t)); } } glaxnimate::math::bezier::MultiBezier glaxnimate::model::TextShape::to_painter_path_impl(glaxnimate::model::FrameTime) const { return {}; } glaxnimate::math::bezier::MultiBezier glaxnimate::model::TextShape::shape_data(FrameTime t) const { // Ignore position if we have a path, it can still be moved from the group if ( path.get() ) return untranslated_path(t); QPointF pos = position.get_at(t); return untranslated_path(t).translated(pos); } QIcon glaxnimate::model::TextShape::tree_icon() const { return QIcon::fromTheme("font"); } QRectF glaxnimate::model::TextShape::local_bounding_rect(glaxnimate::model::FrameTime t) const { return shape_data(t).bounding_box(); } QString glaxnimate::model::TextShape::type_name_human() const { return i18n("Text"); } std::unique_ptr glaxnimate::model::TextShape::to_path() const { auto group = std::make_unique(document()); group->name.set(name.get()); group->group_color.set(group_color.get()); group->visible.set(visible.get()); Font::CharDataCache local_cache; for ( const auto& line : font->layout(text.get()) ) { auto line_group = std::make_unique(document()); line_group->name.set(line.text); for ( const auto& glyph : line.glyphs ) { math::bezier::MultiBezier bez = font->path_for_glyph(glyph.glyph, local_cache, false).translated(glyph.position); if ( bez.beziers().size() == 1 ) { auto path = std::make_unique(document()); path->shape.set(bez.beziers()[0]); line_group->shapes.insert(std::move(path), 0); } else if ( bez.beziers().size() > 1 ) { auto glyph_group = std::make_unique(document()); for ( const auto& sub : bez.beziers() ) { auto path = std::make_unique(document()); path->shape.set(sub); glyph_group->shapes.insert(std::move(path), 0); } line_group->shapes.insert(std::move(glyph_group), 0); } } group->shapes.insert(std::move(line_group), 0); } group->set_time(time()); if ( position.animated() ) { for ( const auto& kf : position ) { group->transform->position.set_keyframe(kf.time(), kf.get())->set_transition(kf.transition()); // group->transform->anchor_point.set_keyframe(kf.time(), kf.get())->set_transition(kf.transition()); } } group->transform->position.set(position.get()); // group->transform->anchor_point.set(position.get()); return group; } QPointF glaxnimate::model::TextShape::offset_to_next_character() const { auto layout = font->layout(text.get()); if ( layout.empty() ) return {}; return layout.back().advance; } std::vector glaxnimate::model::TextShape::valid_paths() const { std::vector shapes; shapes.push_back(nullptr); for ( const auto& sib : *owner() ) if ( sib.get() != this ) shapes.push_back(sib.get()); return shapes; } bool glaxnimate::model::TextShape::is_valid_path(glaxnimate::model::DocumentNode* node) const { if ( node == nullptr ) return true; if ( node == this ) return false; if ( auto shape = node->cast() ) return shape->owner_composition() == owner_composition(); return false; } void glaxnimate::model::TextShape::path_changed(glaxnimate::model::ShapeElement* new_path, glaxnimate::model::ShapeElement* old_path) { on_text_changed(); if ( old_path ) disconnect(old_path, nullptr, this, nullptr); if ( new_path ) { connect(new_path, &Object::visual_property_changed, this, &TextShape::on_text_changed); connect(new_path, &VisualNode::bounding_rect_changed, this, &TextShape::on_text_changed); } } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/zig_zag.hpp000664 001750 001750 00000001632 15165022620 035136 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/modifiers/path_modifier.hpp" namespace glaxnimate::model { class ZigZag : public StaticOverrides { GLAXNIMATE_OBJECT(ZigZag) GLAXNIMATE_ANIMATABLE(float, amplitude, 10) GLAXNIMATE_ANIMATABLE(float, frequency, 10, {}, 0) public: enum Style { Saw = 1, Wave = 2 }; Q_ENUM(Style) GLAXNIMATE_PROPERTY(Style, style, Saw, nullptr, nullptr, PropertyTraits::Visual) public: using Ctor::Ctor; static QIcon static_tree_icon(); static QString static_type_name_human(); math::bezier::MultiBezier process(FrameTime t, const math::bezier::MultiBezier& mbez) const override; protected: bool process_collected() const override; }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/property/reference_property.hpp000664 001750 001750 00000011023 15165022620 035742 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/document_node.hpp" #include "glaxnimate/model/property/property.hpp" #define GLAXNIMATE_PROPERTY_REFERENCE(type, name, ...) \ public: \ ReferenceProperty name{this, kli18n(#name), __VA_ARGS__}; \ type* get_##name() const { return name.get(); } \ bool set_##name(type* v) \ { \ return name.set_undoable(QVariant::fromValue(v)); \ } \ private: \ Q_PROPERTY(type* name READ get_##name WRITE set_##name) \ Q_CLASSINFO(#name, "property ref " #type) \ // macro end namespace glaxnimate::model { class ReferencePropertyBase : public BaseProperty { Q_GADGET public: ReferencePropertyBase( Object* obj, const utils::LazyLocalizedString& name, PropertyCallback> valid_options, PropertyCallback is_valid_option, PropertyTraits::Flags flags = PropertyTraits::Visual) : BaseProperty(obj, name, PropertyTraits{PropertyTraits::ObjectReference, flags}), valid_options_(std::move(valid_options)), is_valid_option_(std::move(is_valid_option)) { } bool valid_value(const QVariant & v) const override { return is_valid_option_(object(), v.value()); } std::vector valid_options() const { return valid_options_(object()); } bool is_valid_option(DocumentNode* ptr) const { return is_valid_option_(object(), ptr); } void set_time(FrameTime) override {} void transfer(Document*) override; virtual bool set_ref(model::DocumentNode* t) = 0; virtual model::DocumentNode* get_ref() const = 0; private: PropertyCallback> valid_options_; PropertyCallback is_valid_option_; protected: // static void remove_user(ReferencePropertyBase*, void*) {} // static void add_user(ReferencePropertyBase*, void*) {} static void remove_user(ReferencePropertyBase* prop, model::DocumentNode* obj) { obj->remove_user(prop); } static void add_user(ReferencePropertyBase* prop, model::DocumentNode* obj) { obj->add_user(prop); } }; template class ReferenceProperty : public ReferencePropertyBase { public: using value_type = Type*; ReferenceProperty( Object* obj, const utils::LazyLocalizedString& name, PropertyCallback> valid_options, PropertyCallback is_valid_option, PropertyCallback on_changed = {}, PropertyTraits::Flags flags = PropertyTraits::Visual) : ReferencePropertyBase(obj, name, std::move(valid_options), std::move(is_valid_option), flags), on_changed(std::move(on_changed)) { } bool set(Type* value) { if ( !is_valid_option(value) ) return false; set_force(value); return true; } void set_force(Type* value) { auto old = value_; value_ = value; value_changed(); if ( old ) remove_user(this, old); if ( value ) add_user(this, value); on_changed(object(), value_, old); } Type* get() const { return value_; } QVariant value() const override { if ( !value_ ) return {}; return QVariant::fromValue(value_); } bool set_value(const QVariant& val) override { if ( val.isNull() ) return set(nullptr); if ( auto v = detail::variant_cast(val) ) return set(*v); return true; } Type* operator->() const { return value_; } bool set_ref(model::DocumentNode* t) override { if ( !t ) { set_force(nullptr); return true; } if ( auto p = qobject_cast(t) ) return set(p); return false; } model::DocumentNode* get_ref() const override { return value_; } private: Type* value_ = nullptr; PropertyCallback on_changed; }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_loader.cpp000664 001750 001750 00000100401 15165022620 036312 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/extraformats/rive/rive_loader.hpp" #include "glaxnimate/io/animated_properties.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/shapes/shapes/rect.hpp" #include "glaxnimate/model/shapes/shapes/ellipse.hpp" #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/shapes/style/fill.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" using namespace glaxnimate; using namespace glaxnimate::io; using namespace glaxnimate::io::rive; namespace { struct Artboard { Artboard() = default; Artboard(Object* first, Object* last) : object(first), children(object), child_count(last - first + 1) {} Object* operator->() const { return object; } Object* object = nullptr; Object* children = nullptr; Identifier child_count = 0; VarUint timeline_duration = 0; VarUint keyframe_timeline_duration = 0; model::Composition* comp = nullptr; QSizeF size; }; template T load_property_get_keyframe(const detail::JoinedPropertyKeyframe& kf, std::size_t index); template<> Float32 load_property_get_keyframe(const detail::JoinedPropertyKeyframe& kf, std::size_t index) { return kf.values[index].vector()[0]; } template<> VarUint load_property_get_keyframe(const detail::JoinedPropertyKeyframe& kf, std::size_t index) { return kf.values[index].vector()[0]; } template<> QColor load_property_get_keyframe(const detail::JoinedPropertyKeyframe& kf, std::size_t index) { return kf.values[index].color(); } template void load_property_impl(Object* rive, PropT& property, const detail::AnimatedProperties& animations, const std::array& names, T... defvals, const Func& value_func, std::index_sequence) { property.set(value_func(rive->get(names[Ind], defvals)...)); for ( const auto& kf : animations.joined(std::vector(names.begin(), names.end())) ) property.set_keyframe(kf.time, value_func(load_property_get_keyframe(kf, Ind)...))->set_transition(kf.transition); } template void load_property(Object* rive, PropT& property, const detail::AnimatedProperties& animations, const std::array& names, T... defvals, const Func& value_func) { load_property_impl(rive, property, animations, names, defvals..., value_func, std::index_sequence_for{}); } template void load_property_vector_impl(Object* rive, PropT& property, const detail::AnimatedProperties& animations, const std::array& names, T... defvals, const Func& value_func, std::index_sequence) { property.set( std::get( value_func(rive->get(names[Ind], defvals)...) ) ); for ( const auto& kf : animations.joined(std::vector(names.begin(), names.end())) ) property.set_keyframe(kf.time, std::get(value_func(load_property_get_keyframe(kf, Ind)...)) )->set_transition(kf.transition); } template void expand(T...) {} template void load_properties_impl(Object* rive, const std::tuple& properties, const detail::AnimatedProperties& animations, const std::array& names, T... defvals, const Func& value_func, std::index_sequence ind, std::index_sequence) { expand( (load_property_vector_impl( rive, *std::get(properties), animations, names, defvals..., value_func, ind ), 0) ... ); } template void load_properties( Object* rive, std::tuple properties, const detail::AnimatedProperties& animations, const std::array& names, T... defvals, const Func& value_func) { load_properties_impl( rive, properties, animations, names, defvals..., value_func, std::index_sequence_for{}, std::index_sequence_for{}); } template void load_property(Object* rive, PropT& property, const detail::AnimatedProperties& animations, const char* name, T defval = {}) { property.set(rive->get(name, defval)); for ( const auto& kf : animations.joined({name}) ) property.set_keyframe(kf.time, load_property_get_keyframe(kf, 0))->set_transition(kf.transition); } QPointF make_point(Float32 x, Float32 y) { return QPointF(x, y); } struct Asset { Object* object = nullptr; model::Asset* asset = nullptr; }; struct LoadCotext { void new_artboard(Object* object) { artboards[object] = Artboard(object, &objects.back()); artboard = &artboards[object]; artboards_id.push_back(artboard); artboard->comp = document->assets()->compositions->values.insert(std::make_unique(document)); artboard->size = QSizeF( object->get("width"), object->get("height") ); } Object* artboard_child(Identifier id) const { if ( artboard && id < artboard->child_count ) return artboard->children + id; return nullptr; } LoadCotext(RiveFormat* format, model::Document* document) : document(document), format(format) { main = document->assets()->compositions->values.insert(std::make_unique(document)); } void preprocess_object(Object* object) { if ( object->type().id == TypeId::Artboard ) { new_artboard(object); } else if ( object->type().id == TypeId::KeyedObject ) { if ( !artboard ) { format->warning(i18n("Unexpected Keyed Object")); return; } auto id = object->get("objectId", artboard->child_count); keyed_object = artboard_child(id); keyed_property = nullptr; if ( !keyed_object ) { format->warning(i18n("Invalid Keyed Object id %1", id)); return; } } else if ( object->type().id == TypeId::KeyedProperty ) { if ( !keyed_object ) { format->warning(i18n("Unexpected Keyed Property")); return; } auto id = object->get("propertyKey"); auto prop = keyed_object->type().property(id); if ( !prop ) { format->warning(i18n("Unknown Keyed Property id %1", id)); return; } keyed_object->animations().push_back({prop, {}}); keyed_property = &keyed_object->animations().back(); } else if ( object->type().id == TypeId::LinearAnimation ) { if ( !artboard ) { format->warning(i18n("Unexpected Animation")); return; } auto duration = object->get("duration"); if ( duration > artboard->timeline_duration ) artboard->timeline_duration = duration; } else if ( object->type().id == TypeId::ImageAsset ) { assets.push_back({object, load_image_asset(object)}); } else if ( object->type().id == TypeId::FileAssetContents ) { if ( assets.empty() ) { format->warning(i18n("Unexpected Asset Contents")); return; } auto data = object->get("bytes"); if ( data.isEmpty() ) return; if ( auto img = qobject_cast(assets.back().asset) ) { if ( !img->from_raw_data(data) ) format->warning(i18n("Invalid Image Data")); } } else if ( object->has_type(TypeId::Asset) ) { assets.push_back({object, nullptr}); } else if ( object->has_type(TypeId::KeyFrame) ) { if ( !keyed_property ) { format->warning(i18n("Unexpected Keyframe")); return; } auto frame = object->get("duration"); if ( frame > artboard->keyframe_timeline_duration ) artboard->keyframe_timeline_duration = frame; keyed_property->keyframes.push_back(object); } else if ( object->has("parentId") ) { auto parent_id = object->get("parentId"); auto parent = artboard_child(parent_id); if ( !parent ) format->warning(i18n("Could not find parent with id %1", parent_id)); else parent->children().push_back(object); } } void process_object(Object* object) { if ( object->type().id == TypeId::Artboard ) { process_artboard(object); } } void process_artboard(Object* object) { const auto& artboard = artboards.at(object); artboard.comp->name.set(object->get("name")); add_shapes(object, artboard.comp->shapes); auto precomp_layer = std::make_unique(document); precomp_layer->name.set(artboard.comp->name.get()); precomp_layer->size.set(artboard.size.toSize()); detail::AnimatedProperties animations = load_animations(object); load_transform(object, precomp_layer->transform.get(), animations, QRectF(QPointF(0, 0), artboard.size)); precomp_layer->opacity.set(object->get("opacity", 1)); precomp_layer->composition.set(artboard.comp); float last_frame = artboard.timeline_duration == 0 ? artboard.keyframe_timeline_duration : artboard.timeline_duration; main->animation->last_frame.set(qMax(main->animation->last_frame.get(), last_frame)); if ( document->assets()->compositions->values.size() == 1 ) { main->width.set(precomp_layer->size.get().width()); main->height.set(precomp_layer->size.get().height()); } main->shapes.insert(std::move(precomp_layer)); } void add_shapes(Object* parent, model::ObjectListProperty& prop) { std::vector> shapes; for ( Object* child : parent->children() ) { if ( child == parent ) { format->error(i18n("Parent circular reference detected")); continue; } auto shape = load_shape(child); if ( shape ) { if ( child->has_type(TypeId::Node) ) shapes.emplace_back(std::move(shape)); else prop.insert(std::move(shape)); } } for ( auto it = shapes.rbegin(); it != shapes.rend(); ++it ) prop.insert(std::move(*it)); } std::unique_ptr load_shape_layer(Object* shape, const detail::AnimatedProperties& animations) { auto layer = std::make_unique(document); load_shape_group(shape, layer.get(), animations); return layer; } void load_transform(Object* rive, model::Transform* transform, const detail::AnimatedProperties& animations, const QRectF& bbox) { load_property(rive, transform->position, animations, {"x", "y"}, 0, 0, &make_point); if ( rive->type().property("originX") ) { load_property(rive, transform->anchor_point, animations, {"originX", "originY"}, 0.5, 0.5, [&bbox](Float32 ox, Float32 oy){ return QPointF( math::lerp(bbox.left(), bbox.right(), ox), math::lerp(bbox.top(), bbox.bottom(), oy) ); } ); } /*load_properties( rive, std::make_tuple(&transform->position, &transform->anchor_point), animations, {"x", "y", "originX", "originY"}, 0, 0, 0.5, 0.5, [&bbox] ( Float32 x, Float32 y, Float32 ox, Float32 oy ) { QPointF anchor( math::lerp(bbox.left(), bbox.right(), ox), math::lerp(bbox.top(), bbox.bottom(), oy) ); return std::make_tuple(QPointF(x, y) - anchor, anchor); } );*/ load_property(rive, transform->rotation, animations, "rotation"); load_property(rive, transform->scale, animations, {"scaleX", "scaleX"}, 1, 1, [](Float32 x, Float32 y){ return QVector2D(x, y); }); } void load_shape_group(Object* shape, model::Group* group, const detail::AnimatedProperties& animations) { load_property(shape, group->opacity, animations, "opacity", 1); group->name.set(shape->get("name")); add_shapes(shape, group->shapes); auto box = group->local_bounding_rect(0); load_transform(shape, group->transform.get(), animations, box); } std::unique_ptr load_shape(Object* object) { detail::AnimatedProperties animations = load_animations(object); switch ( object->type().id ) { case TypeId::Shape: case TypeId::Node: return load_shape_layer(object, animations); case TypeId::Rectangle: return load_rectangle(object, animations); case TypeId::Ellipse: return load_ellipse(object, animations); case TypeId::Fill: return load_fill(object, animations); case TypeId::Stroke: return load_stroke(object, animations); case TypeId::Polygon: return load_polygon(object, animations, model::PolyStar::Polygon); case TypeId::Star: return load_polygon(object, animations, model::PolyStar::Star); case TypeId::Triangle: return load_triangle(object, animations); case TypeId::PointsPath: return load_path(object, animations); case TypeId::NestedArtboard: return load_precomp(object, animations); case TypeId::Image: return load_image(object, animations); case TypeId::TrimPath: case TypeId::Bone: case TypeId::RootBone: case TypeId::ClippingShape: case TypeId::Text: /// \todo default: return {}; } } std::unique_ptr load_rectangle(Object* object, const detail::AnimatedProperties& animations) { auto group = std::make_unique(document); auto shape = std::make_unique(document); shape->name.set(object->get("name")); load_property(object, shape->rounded, animations, {"cornerRadiusTL", "cornerRadiusBL", "cornerRadiusBR", "cornerRadiusTR"}, 0, 0, 0, 0, [](Float32 tl, Float32 bl, Float32 br, Float32 tr){ return (tl + bl + br + tr) / 4; } ); load_property(object, shape->size, animations, {"width", "height"}, 0, 0, [](Float32 x, Float32 y){ return QSizeF(x, y); }); group->shapes.insert(std::move(shape)); load_shape_group(object, group.get(), animations); return group; } std::unique_ptr load_ellipse(Object* object, const detail::AnimatedProperties& animations) { auto group = std::make_unique(document); auto shape = std::make_unique(document); shape->name.set(object->get("name")); load_property(object, shape->size, animations, {"width", "height"}, 0, 0, [](Float32 x, Float32 y){ return QSizeF(x, y); }); group->shapes.insert(std::move(shape)); load_shape_group(object, group.get(), animations); return group; } std::unique_ptr load_fill(Object* object, const detail::AnimatedProperties& animations) { auto shape = std::make_unique(document); load_styler(object, shape.get(), animations); /// \todo fillRule return shape; } std::unique_ptr load_stroke(Object* object, const detail::AnimatedProperties& animations) { auto shape = std::make_unique(document); load_styler(object, shape.get(), animations); load_property(object, shape->width, animations, "thickness"); /// \todo cap + join return shape; } void load_styler(Object* object, model::Styler* shape, const detail::AnimatedProperties& animations) { shape->name.set(object->get("name")); shape->visible.set(object->get("isVisible", true)); load_property(object, shape->opacity, animations, "opacity", 1); for ( const auto& child : object->children() ) { if ( child->type().id == TypeId::SolidColor ) load_property(child, shape->color, load_animations(child), "colorValue", QColor("#747474")); else if ( child->type().id == TypeId::LinearGradient ) shape->use.set(load_gradient(child, model::Gradient::Linear)); else if ( child->type().id == TypeId::RadialGradient ) shape->use.set(load_gradient(child, model::Gradient::Radial)); } } model::Gradient* load_gradient(Object* object, model::Gradient::GradientType type) { auto colors = std::make_unique(document); colors->name.set(object->get("name")); auto colors_ptr = colors.get(); document->assets()->gradient_colors->values.insert(std::move(colors)); auto gradient = std::make_unique(document); gradient->name.set(object->get("name")); gradient->colors.set(colors_ptr); gradient->type.set(type); auto animations = load_animations(object); load_property(object, gradient->start_point, animations, {"startX", "startY"}, 0, 0, &make_point); load_property(object, gradient->end_point, animations, {"endX", "endY"}, 0, 0, &make_point); /// \todo color animations QGradientStops stops; for ( const auto& child : object->children() ) { if ( child->type().id == TypeId::GradientStop ) { stops.push_back({ child->get("position"), child->get("colorValue"), }); } } colors_ptr->colors.set(stops); auto ptr = gradient.get(); document->assets()->gradients->values.insert(std::move(gradient)); return ptr; } detail::AnimatedProperties load_animations(Object* object) { using namespace glaxnimate::io::detail; AnimatedProperties props; for ( const auto& anim : object->animations() ) { AnimatedProperty& prop = props.properties[anim.property->name]; for ( auto kf : anim.keyframes ) { model::KeyframeTransition transition; /// \todo prop.keyframes.push_back({ kf->get("frame", 0), ValueVariant(kf->get_variant("value")), transition }); } } return props; } std::unique_ptr load_polygon(Object* object, const detail::AnimatedProperties& animations, model::PolyStar::StarType type) { auto group = std::make_unique(document); load_shape_group(object, group.get(), animations); auto shape = std::make_unique(document); shape->name.set(object->get("name")); shape->type.set(type); /// \todo cornerRadius load_property(object, shape->points, animations, "points", 5); shape->outer_radius.set(100); load_property(object, shape->inner_radius, animations, {"innerRadius"}, 0.5, [](Float32 pc){ return pc * 100; }); load_property(object, shape->points, animations, "points", 5); load_property(object, group->transform->scale, animations, {"scaleX", "scaleY", "width", "height"}, 1, 1, 0, 0, [](Float32 sx, Float32 sy, Float32 w, Float32 h){ return QVector2D(w / 200 * sx, h / 200 * sy); }); group->shapes.insert(std::move(shape)); return group; } std::unique_ptr load_triangle(Object* object, const detail::AnimatedProperties& animations) { auto group = std::make_unique(document); auto shape = std::make_unique(document); shape->name.set(object->get("name")); load_property(object, shape->shape, animations, {"width", "height"}, 0, 0, [](Float32 w, Float32 h){ math::bezier::Bezier path; path.add_point({-w/2, h/2}); path.add_point({0, -h/2}); path.add_point({w/2, h/2}); path.close(); return path; }); group->shapes.insert(std::move(shape)); load_shape_group(object, group.get(), animations); return group; } std::unique_ptr load_path(Object* object, const detail::AnimatedProperties& animations) { auto shape = std::make_unique(document); shape->name.set(object->get("name")); bool closed = object->get("isClosed"); shape->closed.set(closed); math::bezier::Bezier bez; for ( const auto& child : object->children() ) { math::bezier::Point p; p.pos = QPointF(child->get("x", 0), child->get("y", 0)); if ( child->type().id == TypeId::CubicMirroredVertex ) { p.type = math::bezier::Symmetrical; auto tangent = math::from_polar( child->get("distance"), child->get("rotation") ); p.tan_in = p.pos - tangent; p.tan_out = p.pos + tangent; } else if ( child->type().id == TypeId::CubicAsymmetricVertex ) { p.type = math::bezier::Smooth; p.tan_in = p.pos - math::from_polar( child->get("inDistance"), child->get("rotation") ); p.tan_out = p.pos + math::from_polar( child->get("outDistance"), child->get("rotation") ); } else if ( child->type().id == TypeId::CubicDetachedVertex ) { p.type = math::bezier::Corner; p.tan_in = p.pos + math::from_polar( child->get("inDistance"), child->get("inRotation") ); p.tan_out = p.pos + math::from_polar( child->get("outDistance"), child->get("outRotation") ); } else if ( child->type().id == TypeId::StraightVertex ) { p.type = math::bezier::Corner; p.tan_in = p.tan_out = p.pos; } else { continue; } bez.push_back(p); } bez.set_closed(closed); /// \todo animation Q_UNUSED(animations); shape->shape.set(bez); return shape; } std::unique_ptr load_precomp(Object* object, const detail::AnimatedProperties& animations) { auto shape = std::make_unique(document); shape->name.set(object->get("name")); load_property(object, shape->opacity, animations, "opacity", 1); QRectF box; if ( object->has("artboardId") ) { auto id = object->get("artboardId"); shape->size.set(artboards_id[id]->size); shape->composition.set(artboards_id[id]->comp); box.setSize(artboards_id[id]->size); } load_transform(object, shape->transform.get(), animations, box); return shape; } model::Bitmap* load_image_asset(Object* object) { auto image = std::make_unique(document); image->filename.set(object->get("name")); image->width.set(object->get("width")); image->height.set(object->get("height")); auto ptr = image.get(); document->assets()->images->values.insert(std::move(image)); return ptr; } std::unique_ptr load_image(Object* object, const detail::AnimatedProperties& animations) { auto shape = std::make_unique(document); shape->name.set(object->get("name")); auto id = object->get("assetId"); QSizeF size; if ( auto bmp = qobject_cast(assets[id].asset) ) { size = bmp->size(); shape->transform->anchor_point.set(QPointF( bmp->width.get() / 2., bmp->height.get() / 2. )); shape->image.set(bmp); } load_transform(object, shape->transform.get(), animations, {QPointF(0, 0), size}); return shape; } model::Document* document = nullptr; std::map artboards; std::vector objects; Artboard* artboard = nullptr; Object* keyed_object = nullptr; PropertyAnimation* keyed_property = nullptr; RiveFormat* format = nullptr; std::vector artboards_id; std::vector assets; model::Composition* main = nullptr; }; } // namespace RiveLoader::RiveLoader(BinaryInputStream& stream, RiveFormat* format) : stream(stream), format(format) { extra_props = read_property_table(); QObject::connect(&types, &TypeSystem::type_not_found, [format](int type){ format->error(i18n("Unknown object of type %1", int(type))); }); if ( stream.has_error() ) format->error(i18n("Could not read property table")); } std::vector RiveLoader::load_object_list() { if ( stream.has_error() ) return {}; std::vector objects; while ( !stream.has_error() && !stream.eof() ) objects.emplace_back(read_object()); return objects; } bool RiveLoader::load_document(model::Document* document) { if ( stream.has_error() ) return false; LoadCotext context(format, document); while ( !stream.has_error() && !stream.eof() ) if ( auto obj = read_object() ) context.objects.emplace_back(std::move(obj)); for ( auto& object : context.objects ) context.preprocess_object(&object); for ( auto& object : context.objects ) context.process_object(&object); return true; } Object RiveLoader::read_object() { auto type_id = TypeId(stream.read_uint_leb128()); if ( stream.has_error() ) { format->error(i18n("Could not load object type ID")); return {}; } Object obj = types.object(type_id); if ( !obj ) return {}; while ( true ) { Identifier prop_id = stream.read_uint_leb128(); if ( stream.has_error() ) { format->error(i18n( "Could not load property ID in %1 (%2)", int(type_id), obj.definition()->name )); return {}; } if ( prop_id == 0 ) break; auto prop_def = obj.type().property(prop_id); if ( !prop_def ) { auto unknown_it = extra_props.find(prop_id); if ( unknown_it == extra_props.end() ) { format->error(i18n( "Unknown property %1 of %2 (%3)", prop_id, int(type_id), obj.definition()->name )); return {}; } else { format->warning(i18n( "Skipping unknown property %1 of %2 (%3)", prop_id, int(type_id), obj.definition()->name )); } } else { obj.set(prop_def, read_property_value(prop_def->type)); if ( stream.has_error() ) { format->error(i18n( "Error loading property %1 (%2) of %3 (%4)", prop_id, prop_def->name, int(type_id), obj.definition()->name )); return {}; } } } return obj; } QVariant RiveLoader::read_property_value(PropertyType type) { switch ( type ) { case PropertyType::Bool: return bool(stream.next()); case PropertyType::Bytes: return read_raw_string(); case PropertyType::String: return read_string_utf8(); case PropertyType::VarUint: return QVariant::fromValue(stream.read_uint_leb128()); case PropertyType::Float: return stream.read_float32_le(); case PropertyType::Color: return QColor::fromRgba(stream.read_uint32_le()); } return {}; } PropertyTable RiveLoader::read_property_table() { std::vector props; while ( true ) { VarUint id = stream.read_uint_leb128(); if ( stream.has_error() ) return {}; if ( id == 0 ) break; props.push_back(id); } quint32 current_int = 0; quint32 bit = 8; PropertyTable table; for ( auto id : props ) { if ( bit == 8 ) { current_int = stream.read_uint32_le(); if ( stream.has_error() ) return {}; bit = 0; } int type = (current_int >> bit) & 3; if ( type == 0 ) table[id] = PropertyType::VarUint; else if ( type == 1 ) table[id] = PropertyType::String; else if ( type == 2 ) table[id] = PropertyType::Float; else if ( type == 3 ) table[id] = PropertyType::Color; bit += 2; } return table; } void RiveLoader::skip_value(glaxnimate::io::rive::PropertyType type) { switch ( type ) { case PropertyType::Bool: case PropertyType::VarUint: stream.read_uint_leb128(); break; case PropertyType::Bytes: case PropertyType::String: read_raw_string(); break; case PropertyType::Float: stream.read_float32_le(); break; case PropertyType::Color: stream.read_uint32_le(); break; } } const PropertyTable& RiveLoader::extra_properties() const { return extra_props; } QByteArray RiveLoader::read_raw_string() { auto size = stream.read_uint_leb128(); if ( stream.has_error() ) return {}; return stream.read(size); } QString RiveLoader::read_string_utf8() { return QString::fromUtf8(read_raw_string()); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/style/fill.hpp000664 001750 001750 00000002304 15165022620 033606 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/shapes/style/styler.hpp" #include "glaxnimate/model/animation/animatable.hpp" namespace glaxnimate::model { class Fill : public StaticOverrides { GLAXNIMATE_OBJECT(Fill) public: enum Rule { NonZero = Qt::WindingFill, EvenOdd = Qt::OddEvenFill, }; private: Q_ENUM(Rule); GLAXNIMATE_PROPERTY(Rule, fill_rule, NonZero, nullptr, nullptr, PropertyTraits::Visual) public: using Ctor::Ctor; QRectF local_bounding_rect(FrameTime t) const override { return collect_shapes(t, {}).bounding_box(); } static QIcon static_tree_icon() { return QIcon::fromTheme("format-fill-color"); } static QString static_type_name_human() { return i18n("Fill"); } protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(FrameTime t) const override; void on_paint(renderer::Renderer* p, FrameTime t, PaintMode, model::Modifier* modifier) const override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/lottie_private_common.hpp000664 001750 001750 00000025327 15165022620 035460 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/log/log.hpp" #include "glaxnimate/model/shapes/composable/group.hpp" #include "glaxnimate/model/shapes/composable/layer.hpp" #include "glaxnimate/model/shapes/composable/precomp_layer.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/shapes/shapes/rect.hpp" #include "glaxnimate/model/shapes/shapes/ellipse.hpp" #include "glaxnimate/model/shapes/shapes/path.hpp" #include "glaxnimate/model/shapes/shapes/polystar.hpp" #include "glaxnimate/model/shapes/shapes/text.hpp" #include "glaxnimate/model/shapes/style/fill.hpp" #include "glaxnimate/model/shapes/style/stroke.hpp" #include "glaxnimate/model/shapes/modifiers/repeater.hpp" #include "glaxnimate/io/lottie/lottie_format.hpp" namespace glaxnimate::io::lottie::detail { class ValueTransform { public: virtual ~ValueTransform() {} virtual QVariant to_lottie(const QVariant& v, model::FrameTime) const = 0; virtual QVariant from_lottie(const QVariant& v, model::FrameTime) const = 0; }; class FloatMult : public ValueTransform { public: explicit FloatMult(float factor) : factor(factor) {} QVariant to_lottie(const QVariant& v, model::FrameTime) const override { return v.toFloat() * factor; } QVariant from_lottie(const QVariant& v, model::FrameTime) const override { return v.toFloat() / factor; } private: float factor; }; class EnumMap : public ValueTransform { public: EnumMap(QMap values) : values(std::move(values)) {} QVariant to_lottie(const QVariant& v, model::FrameTime) const override { return values[v.toInt()]; } QVariant from_lottie(const QVariant& v, model::FrameTime) const override { return values.key(v.toInt()); } QMap values; }; class IntBool : public ValueTransform { public: QVariant to_lottie(const QVariant& v, model::FrameTime) const override { return int(v.toBool()); } QVariant from_lottie(const QVariant& v, model::FrameTime) const override { return bool(v.toInt()); } }; class GradientLoad : public ValueTransform { public: GradientLoad(int count) : count(count) {} QVariant to_lottie(const QVariant&, model::FrameTime) const override { return {}; } QVariant from_lottie(const QVariant& v, model::FrameTime) const override { auto vlist = v.toList(); if ( vlist.size() < count * 4 ) return {}; QGradientStops s; s.reserve(count); bool alpha = vlist.size() >= count * 6; for ( int i = 0; i < count; i++ ) { s.push_back({ vlist[i*4].toDouble(), QColor::fromRgbF( vlist[i*4+1].toDouble(), vlist[i*4+2].toDouble(), vlist[i*4+3].toDouble(), alpha ? vlist[count*4+2*i+1].toDouble() : 1 ) }); } return QVariant::fromValue(s); } int count = 0; }; class TransformFunc { public: template>> TransformFunc(const T& t) : trans(std::make_shared(t)) {} TransformFunc() {} TransformFunc(TransformFunc&&) = default; TransformFunc(const TransformFunc&) = default; QVariant to_lottie(const QVariant& v, model::FrameTime t) const { if ( !trans ) return v; return trans->to_lottie(v, t); } QVariant from_lottie(const QVariant& v, model::FrameTime t) const { if ( !trans ) return v; return trans->from_lottie(v, t); } private: std::shared_ptr trans; }; enum FieldMode { Auto, AnimatedToStatic, Ignored, Custom }; struct FieldInfo { QString name; QString lottie; bool essential; FieldMode mode; TransformFunc transform; FieldInfo(const char* lottie, const char* name, TransformFunc transform = {}, bool essential = true) : name(name), lottie(lottie), essential(essential), mode(Auto), transform(std::move(transform)) {} FieldInfo(const char* lottie, FieldMode mode = Ignored) : lottie(lottie), essential(false), mode(mode) {} FieldInfo(const char* lottie, const char* name, FieldMode mode, bool essential = true) : name(name), lottie(lottie), essential(essential), mode(mode) {} }; // static mapping data const QMap> fields = { {"DocumentNode", { FieldInfo{"nm", "name", {}, false}, FieldInfo{"mn"}, FieldInfo{"uid", "uuid", {}, false}, }}, {"Composition", { FieldInfo("layers", Custom), FieldInfo("id", Custom), FieldInfo{"op", Custom}, FieldInfo{"ip", Custom}, FieldInfo("v", Custom), FieldInfo{"fr", "fps"}, FieldInfo{"w", "width"}, FieldInfo{"h", "height"}, FieldInfo("ddd"), FieldInfo("assets"), FieldInfo("comps"), FieldInfo("fonts"), FieldInfo("chars"), FieldInfo("markers"), FieldInfo("motion_blur"), FieldInfo("tgs"), FieldInfo("meta", Custom), FieldInfo("props"), FieldInfo("slots"), }}, // Layer is converted explicitly {"__Layer__", { FieldInfo{"op", Custom}, FieldInfo{"ip", Custom}, FieldInfo("ddd"), FieldInfo("hd", Custom), FieldInfo("ty", Custom), FieldInfo("parent", Custom), FieldInfo("sr"), FieldInfo{"st", Custom}, FieldInfo("tt"), FieldInfo("td"), FieldInfo("tp"), FieldInfo("ind", Custom), FieldInfo("cl"), FieldInfo("ln"), FieldInfo("hasMasks", Custom), FieldInfo("masksProperties", Custom), FieldInfo("ef"), FieldInfo("bounds"), // old, no longer there FieldInfo("ct"), }}, {"Composable", { FieldInfo("ks", Custom), FieldInfo("bm"), FieldInfo("ao", Custom), }}, {"Transform", { FieldInfo{"a", "anchor_point"}, FieldInfo("px", Custom), FieldInfo("py", Custom), FieldInfo("pz", Custom), FieldInfo{"p", "position"}, FieldInfo{"s", "scale"}, FieldInfo{"r", "rotation"}, FieldInfo("rx", Custom), FieldInfo("ry", Custom), FieldInfo("rz", Custom), FieldInfo("o"), FieldInfo("sk"), FieldInfo("sa"), FieldInfo("nm"), }}, {"ShapeElement", { FieldInfo{"ty", Custom}, FieldInfo{"ix"}, FieldInfo{"cix"}, FieldInfo{"bm"}, FieldInfo{"hd", Custom}, }}, {"Shape", { FieldInfo{"d", "reversed", EnumMap{{ {1, 3}, {0, 1}, }}}, FieldInfo{"closed", Custom}, // Old attribute }}, {"Rect", { FieldInfo{"p", "position"}, FieldInfo{"s", "size"}, FieldInfo{"r", "rounded"}, }}, {"Ellipse", { FieldInfo{"p", "position"}, FieldInfo{"s", "size"}, }}, {"Path", { FieldInfo{"ks", "shape"}, FieldInfo{"ind"}, }}, {"PolyStar", { FieldInfo{"p", "position"}, FieldInfo{"or", "outer_radius"}, FieldInfo{"ir", "inner_radius"}, FieldInfo{"is", "inner_roundness", FloatMult(100)}, FieldInfo{"os", "outer_roundness", FloatMult(100)}, FieldInfo{"r", "angle"}, FieldInfo{"pt", "points"}, FieldInfo{"sy", "type"}, }}, {"Group", { FieldInfo{"np"}, FieldInfo{"it", Custom}, }}, {"Styler", { FieldInfo{"o", "opacity", FloatMult(100)}, FieldInfo{"c", Custom}, FieldInfo{"fillEnabled", Custom}, }}, {"Fill", { FieldInfo{"r", "fill_rule", EnumMap{{ {model::Fill::NonZero, 1}, {model::Fill::EvenOdd, 2}, }}}, }}, {"Stroke", { FieldInfo{"lc", "cap", EnumMap{{ {model::Stroke::ButtCap, 1}, {model::Stroke::RoundCap, 2}, {model::Stroke::SquareCap, 3}, }}}, FieldInfo{"lj", "join", EnumMap{{ {model::Stroke::MiterJoin, 1}, {model::Stroke::RoundJoin, 2}, {model::Stroke::BevelJoin, 3}, }}}, FieldInfo{"ml", "miter_limit"}, FieldInfo{"w", "width"}, FieldInfo{"d"}, }}, {"Bitmap", { FieldInfo{"h", "height"}, FieldInfo{"w", "width"}, FieldInfo{"id", Custom}, FieldInfo{"p", Custom}, FieldInfo{"u", Custom}, FieldInfo{"e", Custom}, }}, {"Gradient", { FieldInfo{"s", "start_point"}, FieldInfo{"e", "end_point"}, FieldInfo{"t", "type"}, FieldInfo{"h", Custom}, FieldInfo{"a", "angle"}, FieldInfo{"g", Custom}, }}, {"PreCompLayer", { FieldInfo{"refId", Custom}, FieldInfo{"w", Custom}, FieldInfo{"h", Custom}, FieldInfo{"tm"}, }}, {"Repeater", { FieldInfo{"c", "copies"}, FieldInfo{"o"}, FieldInfo{"m"}, FieldInfo{"tr", Custom}, }}, {"Trim", { FieldInfo{"s", "start", FloatMult(100)}, FieldInfo{"e", "end", FloatMult(100)}, FieldInfo{"o", "offset", FloatMult(360)}, FieldInfo{"m", "multiple"}, }}, {"InflateDeflate", { FieldInfo{"a", "amount", FloatMult(100)}, }}, {"RoundCorners", { FieldInfo{"r", "radius"}, }}, {"OffsetPath", { FieldInfo{"a", "amount"}, FieldInfo{"lj", "join", EnumMap{{ {model::Stroke::MiterJoin, 1}, {model::Stroke::RoundJoin, 2}, {model::Stroke::BevelJoin, 3}, }}}, FieldInfo{"ml", "miter_limit"}, }}, {"ZigZag", { FieldInfo{"s", "amplitude"}, FieldInfo{"r", "frequency"}, FieldInfo{"pt", "style", AnimatedToStatic}, }}, }; const QMap shape_types = { {"Rect", "rc"}, {"PolyStar", "sr"}, {"Ellipse", "el"}, {"Path", "sh"}, {"Group", "gr"}, {"Layer", "gr"}, {"Fill", "fl"}, {"Stroke", "st"}, // "gf" (Gradient Fill) and "gs" (Gradient Stroke) are handled by fill/stroke // "tr" is not a shape but a property of groups // "mm" (Merge), "tw" (Twist), are not supported by lottie {"Trim", "tm"}, {"Repeater", "rp"}, {"RoundCorners", "rd"}, {"InflateDeflate", "pb"}, {"OffsetPath", "op"}, {"ZigZag", "zz"}, }; const QMap shape_types_repeat = { {"gf", "Fill"}, {"gs", "Stroke"}, }; const QMap unsupported_layers = { {6, "Audio"}, {7, "Pholder Video"}, {8, "Image Sequence"}, {9, "Video"}, }; } // namespace glaxnimate::io::lottie::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/ellipse.cpp000664 001750 001750 00000001620 15165022620 034433 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/shapes/ellipse.hpp" #include "glaxnimate/math/bezier/shapes.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Ellipse) QIcon glaxnimate::model::Ellipse::tree_icon() const { return QIcon::fromTheme("draw-ellipse"); } QString glaxnimate::model::Ellipse::type_name_human() const { return i18n("Ellipse"); } glaxnimate::math::bezier::Bezier glaxnimate::model::Ellipse::to_bezier(FrameTime t) const { auto bezier = math::bezier::ellipse(position.get_at(t), size.get_at(t)); if ( reversed.get() ) bezier.reverse(); return bezier; } QRectF glaxnimate::model::Ellipse::local_bounding_rect(FrameTime t) const { QSizeF sz = size.get_at(t); return QRectF(position.get_at(t) - QPointF(sz.width()/2, sz.height()/2), sz); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/composable.cpp000664 001750 001750 00000001714 15165022620 035710 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2025 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/shapes/composable/composable.hpp" using namespace glaxnimate::model; Composable::Composable(Document *document) : ShapeElement(document) { connect(transform.get(), &Object::property_changed, this, &Composable::on_transform_matrix_changed); } void glaxnimate::model::Composable::on_paint(renderer::Renderer* painter, glaxnimate::model::FrameTime time, glaxnimate::model::VisualNode::PaintMode, glaxnimate::model::Modifier*) const { painter->set_blend_mode(blend_mode.get()); painter->set_opacity(opacity.get_at(time)); } void Composable::on_transform_matrix_changed() { propagate_bounding_rect_changed(); Q_EMIT local_transform_matrix_changed(local_transform_matrix(time())); propagate_transform_matrix_changed(transform_matrix(time()), group_transform_matrix(time())); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation_container.cpp000664 001750 001750 00000003716 15165022620 034261 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/animation_container.hpp" #include "glaxnimate/model/factory.hpp" #include "glaxnimate/model/document.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::AnimationContainer) bool glaxnimate::model::AnimationContainer::time_visible(glaxnimate::model::FrameTime time) const { return first_frame.get() <= time && time < last_frame.get(); } bool glaxnimate::model::AnimationContainer::time_visible() const { return time_visible(time()); } void glaxnimate::model::AnimationContainer::set_time(glaxnimate::model::FrameTime t) { bool old_visible = time_visible(); Object::set_time(t); bool new_visible = time_visible(); if ( old_visible != new_visible ) { Q_EMIT time_visible_changed(new_visible); Q_EMIT document()->graphics_invalidated(); } } void glaxnimate::model::AnimationContainer::on_first_frame_changed(float x) { Q_EMIT time_visible_changed(time_visible()); Q_EMIT first_frame_changed(x); } void glaxnimate::model::AnimationContainer::on_last_frame_changed(float x) { Q_EMIT time_visible_changed(time_visible()); Q_EMIT last_frame_changed(x); } float glaxnimate::model::AnimationContainer::duration() const { return last_frame.get() - first_frame.get(); } QString glaxnimate::model::AnimationContainer::type_name_human() const { return i18n("Animation Timing"); } void glaxnimate::model::AnimationContainer::stretch_time(qreal multiplier) { Object::stretch_time(multiplier); first_frame.set(first_frame.get() * multiplier); last_frame.set(last_frame.get() * multiplier); } bool glaxnimate::model::AnimationContainer::validate_first_frame(int f) const { return f >= 0 && (last_frame.get() == -1 || f < last_frame.get()); } bool glaxnimate::model::AnimationContainer::validate_last_frame(int f) const { return f >= 0 && f > first_frame.get(); } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/rive/rive_format.hpp000664 001750 001750 00000001720 15165022620 036345 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/io/base.hpp" namespace glaxnimate::io::rive { class RiveFormat : public ImportExport { Q_OBJECT public: static constexpr const int format_version = 7; QString slug() const override { return "rive"; } QString name() const override { return i18n("Rive Animation"); } QStringList extensions(Direction) const override { return {"riv"}; } bool can_save() const override { return true; } bool can_open() const override { return true; } QJsonDocument to_json(const QByteArray& binary_data); protected: bool on_save(QIODevice& file, const QString&, model::Composition* comp, const QVariantMap&) override; bool on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&) override; }; } // namespace glaxnimate::io::rive src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/extraformats/extraformats_module.hpp000664 001750 001750 00000000744 15165022620 037154 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/module/module.hpp" namespace glaxnimate::extraformats { class Module : public module::Module { public: Module() : module::Module(i18n("Extra Formats")) {} std::vector components() const override; protected: void initialize() override; }; } // namespace glaxnimate::extraformats mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/raster/raster_format.cpp000664 001750 001750 00000004242 15165022620 033712 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/raster/raster_format.hpp" #include #include "glaxnimate/io/io_registry.hpp" #include "glaxnimate/model/shapes/composable/image.hpp" #include "glaxnimate/model/assets/assets.hpp" QStringList glaxnimate::io::raster::RasterFormat::extensions(Direction direction) const { QStringList formats; for ( const auto& fmt : QImageReader::supportedImageFormats() ) if ( (direction == FrameExport || (fmt != "gif" && fmt != "webp")) && fmt != "svg" ) formats << QString::fromUtf8(fmt); return formats; } bool glaxnimate::io::raster::RasterFormat::on_open(QIODevice& dev, const QString& filename, model::Document* document, const QVariantMap& settings) { auto main = document->assets()->add_comp_no_undo(); main->animation->last_frame.set(main->fps.get()); model::FrameTime default_time = settings["default_time"].toFloat(); main->animation->last_frame.set(default_time == 0 ? default_time : 180); auto bmp = document->assets()->images->values.insert(std::make_unique(document)); if ( auto file = qobject_cast(&dev) ) bmp->filename.set(file->fileName()); else bmp->data.set(dev.readAll()); auto img = std::make_unique(document); img->image.set(bmp); QPointF p(bmp->pixmap().width() / 2.0, bmp->pixmap().height() / 2.0); if ( !filename.isEmpty() ) img->name.set(QFileInfo(filename).baseName()); img->transform->anchor_point.set(p); img->transform->position.set(p); main->shapes.insert(std::move(img)); main->width.set(bmp->pixmap().width()); main->height.set(bmp->pixmap().height()); return !bmp->pixmap().isNull(); } bool glaxnimate::io::raster::RasterFormat::on_save_static(QIODevice &file, const QString &filename, model::Composition *comp, model::FrameTime time, const QVariantMap &) { QString format = QFileInfo(filename).suffix(); if ( format.isEmpty() ) format = QStringLiteral("png"); return comp->render_image(time).save(&file, format.toStdString().c_str()); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/gradient.cpp000664 001750 001750 00000021033 15165022620 033327 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/gradient.hpp" #include #include "glaxnimate/model/document.hpp" #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/command/object_list_commands.hpp" #include "glaxnimate/command/animation_commands.hpp" #include "glaxnimate/command/undo_macro_guard.hpp" #include "glaxnimate/utils/sort_gradient.hpp" using namespace glaxnimate; GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::GradientColors) GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Gradient) template<> std::optional glaxnimate::model::detail::variant_cast(const QVariant& val) { if ( !val.canConvert() ) { if ( val.canConvert() ) { QGradientStops stops; for ( auto stop : val.toList() ) { if ( stop.canConvert() ) { stops.push_back(stop.value()); } else if ( stop.canConvert() ) { auto sl = stop.toList(); if ( sl.size() == 2 && sl[0].canConvert() && sl[1].canConvert() ) stops.push_back({sl[0].toDouble(), sl[1].value()}); } } return stops; } return {}; } QVariant converted = val; if ( !converted.convert(QMetaType::fromType()) ) return {}; return converted.value(); } template<> QGradientStops math::lerp(const QGradientStops& a, const QGradientStops& b, double factor) { if ( a.size() != b.size() ) return factor >= 1 ? b : a; QGradientStops mix; mix.reserve(a.size()); for ( int i = 0; i < a.size(); i++ ) mix.push_back({ math::lerp(a[i].first, b[i].first, factor), math::lerp(a[i].second, b[i].second, factor) }); return mix; } QString glaxnimate::model::GradientColors::type_name_human() const { return i18n("Gradient"); } QIcon glaxnimate::model::GradientColors::instance_icon() const { QPixmap icon(32, 32); QPainter p(&icon); QLinearGradient g(0, 0, icon.width(), 0); g.setStops(colors.get()); p.fillRect(icon.rect(), g); return icon; } bool glaxnimate::model::GradientColors::remove_if_unused(bool clean_lists) { if ( clean_lists && users().empty() ) { document()->push_command(new command::RemoveObject( this, &document()->assets()->gradient_colors->values )); return true; } return false; } static QVariant split_gradient(QGradientStops colors, int index, float factor, const QColor& new_color) { int before = index; int after = index+1; if ( after >= colors.size() ) { before = colors.size() - 2; after = colors.size() - 1; } colors.push_back({ math::lerp(colors[before].first, colors[after].first, factor), new_color.isValid() ? new_color : math::lerp(colors[before].second, colors[after].second, 0.5) }); utils::sort_gradient(colors); return QVariant::fromValue(colors); } void glaxnimate::model::GradientColors::split_segment(int segment_index, float factor, const QColor& new_color) { command::UndoMacroGuard guard(i18n("Add color to %1", name.get()), document()); if ( segment_index < 0 ) segment_index = 0; if ( !colors.animated() ) { colors.set_undoable(split_gradient(colors.get(), segment_index, factor, new_color)); } else { for ( const auto& kf : colors ) document()->push_command(colors.command_add_smooth_keyframe( kf.time(), split_gradient(kf.get(), segment_index, factor, new_color) )); } } void glaxnimate::model::GradientColors::remove_stop(int index) { command::UndoMacroGuard guard(i18n("Remove color from %1", name.get()), document()); if ( index < 0 ) index = 0; if ( !colors.animated() ) { auto stops = colors.get(); stops.erase(std::min(stops.begin() + index, stops.end())); colors.set_undoable(QVariant::fromValue(stops)); } else { for ( const auto& kf : colors ) { auto stops = kf.get(); stops.erase(std::min(stops.begin() + index, stops.end())); document()->push_command(colors.command_add_smooth_keyframe( kf.time(), QVariant::fromValue(stops) )); } } } std::vector glaxnimate::model::Gradient::valid_refs() const { return document()->assets()->gradient_colors->values.valid_reference_values(false); } bool glaxnimate::model::Gradient::is_valid_ref ( glaxnimate::model::DocumentNode* node ) const { return document()->assets()->gradient_colors->values.is_valid_reference_value(node, true); } void glaxnimate::model::Gradient::on_ref_visual_changed() { Q_EMIT style_changed(); } void glaxnimate::model::Gradient::on_ref_changed ( glaxnimate::model::GradientColors* new_ref, glaxnimate::model::GradientColors* old_ref ) { if ( old_ref ) disconnect(old_ref, &GradientColors::colors_changed, this, &Gradient::on_ref_visual_changed); if ( new_ref ) { connect(new_ref, &GradientColors::colors_changed, this, &Gradient::on_ref_visual_changed); } else { detach(); } colors_changed_from(old_ref, new_ref); } QString glaxnimate::model::Gradient::type_name_human() const { return i18n("%1 Gradient", gradient_type_name(type.get())); } QBrush glaxnimate::model::Gradient::brush_style ( glaxnimate::model::FrameTime t ) const { if ( type.get() == Radial ) { QRadialGradient g(start_point.get_at(t), radius(t), highlight.get_at(t)); if ( colors.get() ) g.setStops(colors->colors.get_at(t)); g.setSpread(QGradient::PadSpread); return g; } else if ( type.get() == Conical ) { auto start = start_point.get_at(t); auto angle = -this->angle.get(); QConicalGradient g(start, angle); if ( colors.get() ) { auto stops = colors->colors.get_at(t); // Qt conic gradients go counter-clockwise for ( auto& stop : stops ) stop.first = 1 - stop.first; std::reverse(stops.begin(), stops.end()); g.setStops(stops); } return g; } else { QLinearGradient g(start_point.get_at(t), end_point.get_at(t)); if ( colors.get() ) g.setStops(colors->colors.get_at(t)); g.setSpread(QGradient::PadSpread); return g; } } QBrush glaxnimate::model::Gradient::constrained_brush_style(FrameTime t, const QRectF& bounds) const { if ( type.get() == Radial ) { QRadialGradient g(bounds.center(), bounds.width() / 2); if ( colors.get() ) g.setStops(colors->colors.get_at(t)); return g; } else if ( type.get() == Conical ) { QConicalGradient g(bounds.center(), 02); if ( colors.get() ) g.setStops(colors->colors.get_at(t)); return g; } else { QLinearGradient g(bounds.topLeft(), bounds.topRight()); if ( colors.get() ) g.setStops(colors->colors.get_at(t)); return g; } } void glaxnimate::model::Gradient::fill_icon(QPixmap& icon) const { QPainter p(&icon); p.fillRect(icon.rect(), constrained_brush_style(time(), icon.rect())); } qreal glaxnimate::model::Gradient::radius(glaxnimate::model::FrameTime t) const { return math::length(start_point.get_at(t) - end_point.get_at(t)); } QString glaxnimate::model::Gradient::gradient_type_name(GradientType t) { switch ( t ) { case Linear: return i18n("Linear"); case Radial: return i18n("Radial"); case Conical: return i18n("Conical"); } return {}; } void glaxnimate::model::Gradient::on_property_changed(const glaxnimate::model::BaseProperty*, const QVariant&) { Q_EMIT style_changed(); } bool glaxnimate::model::Gradient::remove_if_unused(bool) { if ( users().empty() ) { colors.set_undoable(QVariant::fromValue((glaxnimate::model::GradientColors*)nullptr)); document()->push_command(new command::RemoveObject( this, &document()->assets()->gradients->values )); return true; } return false; } src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/glaxnimate_html_format.cpp000664 001750 001750 00000002133 15165022620 036336 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/glaxnimate/glaxnimate_html_format.hpp" #include "glaxnimate/io/glaxnimate/glaxnimate_format.hpp" #include "glaxnimate/io/lottie/lottie_html_format.hpp" #include "glaxnimate/io/lottie/cbor_write_json.hpp" bool glaxnimate::io::glaxnimate::GlaxnimateHtmlFormat::on_save( QIODevice& file, const QString&, model::Composition* comp, const QVariantMap& ) { file.write(lottie::LottieHtmlFormat::html_head(this, comp, "" )); file.write(R"( )"); return true; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/settings/custom_settings_group.hpp000664 001750 001750 00000002444 15165022620 035450 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include namespace glaxnimate::settings { class CustomSettingsGroupBase { public: virtual ~CustomSettingsGroupBase() = default; virtual QString slug() const = 0; virtual QString label() const = 0; virtual QIcon icon() const = 0; virtual void load(QSettings& settings) = 0; virtual void save(QSettings& settings) = 0; virtual bool has_visible_settings() const { return true; } virtual QVariant get_variant(const QString& setting) const { Q_UNUSED(setting); return {}; } virtual bool set_variant(const QString& setting_slug, const QVariant& value) { Q_UNUSED(setting_slug); Q_UNUSED(value); return false; } virtual QVariant get_default(const QString& setting_slug) const { Q_UNUSED(setting_slug); return {}; } virtual QVariant define(const QString& setting_slug, const QVariant& default_value) { Q_UNUSED(setting_slug); Q_UNUSED(default_value); return {}; } }; using CustomSettingsGroup = std::unique_ptr; } // namespace glaxnimate::settings src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/keyframe_transition.hpp000664 001750 001750 00000005573 15165022620 036225 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/math/bezier/solver.hpp" #include #include namespace glaxnimate::model { /** * \brief Describes the easing between two keyframes */ class KeyframeTransition { Q_GADGET public: enum Descriptive { Custom, Hold, NoValue, Linear, Ease, Fast, Overshoot, }; static constexpr const int max_descriptive = Overshoot; enum class Special { Normal, //!< Handled by points Hold, //!< Value doesn't change NoValue,//!< There is no value to interpolate }; Q_ENUM(Descriptive) KeyframeTransition() = default; KeyframeTransition(const QPointF& before_handle, const QPointF& after_handle, Special special = Special::Normal); explicit KeyframeTransition(Descriptive before, Descriptive after); explicit KeyframeTransition(Descriptive descriptive); KeyframeTransition(Special special); const math::bezier::CubicBezierSolver& bezier() const { return bezier_; } bool hold() const { return special_ == Special::Hold; } Special special() const { return special_; } Descriptive before_descriptive() const; Descriptive after_descriptive() const; QPointF before() const { return bezier_.points()[1]; } QPointF after() const { return bezier_.points()[2]; } /** * \brief Get interpolation factor * \param ratio in [0, 1]. Determines the time ratio (0 = before, 1 = after) * \return A value in [0, 1]: the corresponding interpolation factor * * If the bezier is defined as B(t) = (x,y). This gives y given x. */ double lerp_factor(double ratio) const; /** * \brief Get the bezier parameter at the given time * \param ratio in [0, 1]. Determines the time ratio (0 = before, 1 = after) * \return A value in [0, 1]: the corresponding bezier parameter * * If the bezier is defined as B(t) = (x,y). This gives t given x. */ double bezier_parameter(double ratio) const; void set_hold(bool hold); void set_before_descriptive(Descriptive d); void set_after_descriptive(Descriptive d); void set_handles(const QPointF& before, const QPointF& after); void set_before(const QPointF& before); void set_after(const QPointF& after); /** * \brief Split the transition at \p x * \return The transitions before/after the split */ std::pair split(double x) const; std::pair split_t(double t) const; private: math::bezier::CubicBezierSolver bezier_ { QPointF(0, 0), QPointF(0, 0), QPointF(1, 1), QPointF(1, 1) }; Special special_ = Special::Normal; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/math/math.hpp000664 001750 001750 00000004261 15165022620 031023 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include namespace glaxnimate::math { constexpr const qreal pi = 3.14159265358979323846; constexpr const qreal tau = pi*2; constexpr const qreal sqrt_2 = M_SQRT2; constexpr const qreal ellipse_bezier = 0.5519150244935105707435627; using std::sqrt; using std::sin; using std::cos; using std::tan; using std::acos; using std::asin; using std::atan; using std::atan2; using std::pow; template constexpr Numeric sign(Numeric x) noexcept { return x < 0 ? -1 : 1; } constexpr qreal rad2deg(qreal rad) noexcept { return rad / pi * 180; } constexpr qreal deg2rad(qreal rad) noexcept { return rad * pi / 180; } template Numeric fmod(Numeric x, Numeric y) { return x < 0 ? std::fmod(std::fmod(x, y) + y, y) : std::fmod(x, y) ; } template constexpr inline const T & min(const T &a, const T &b) noexcept { return (a < b) ? a : b; } template constexpr inline const T & max(const T &a, const T &b) noexcept { return (a < b) ? b : a; } template constexpr inline const T &bound(const T &vmin, const T &val, const T &vmax) noexcept { return max(vmin, min(vmax, val)); } template constexpr inline T abs(T t) noexcept { return t < 0 ? -t : t; } /** * \brief Reverses linear interpolation * \param a First value interpolated from * \param b Second value interpolated from * \param c Interpolation result * \pre a < b && a <= c <= b * \returns Factor \p f so that lerp(a, b, f) == c */ template constexpr qreal unlerp(const T& a, const T& b, const T& c) { return qreal(c-a) / qreal(b-a); } template constexpr T lerp(const T& a, const T& b, double factor) { return a * (1-factor) + b * factor; } inline qreal sum_squared() { return 0; } template inline qreal sum_squared(H head, T... args) { return qreal(head) * head + sum_squared(args...); } template inline qreal hypot(T... args) { return sqrt(sum_squared(args...)); } } // namespace glaxnimate::math mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/svg_format.hpp000664 001750 001750 00000002257 15165022620 032521 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/io/base.hpp" #include "glaxnimate/io/io_registry.hpp" namespace glaxnimate::io::svg { class SvgFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "svg"; } QString name() const override { return i18n("SVG"); } QStringList extensions(Direction direction) const override; bool can_save() const override { return true; } bool can_open() const override { return true; } bool can_save_static() const override { return true; } std::unique_ptr save_settings(model::Composition*) const override; protected: bool on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&) override; bool on_save(QIODevice & file, const QString & filename, model::Composition* comp, const QVariantMap & setting_values) override; bool on_save_static(QIODevice &file, const QString &filename, model::Composition *comp, model::FrameTime time, const QVariantMap &setting_values) override; }; } // namespace glaxnimate::io::svg mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/custom_font.hpp000664 001750 001750 00000003557 15165022620 032610 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include "glaxnimate/utils/qstring_hash.hpp" namespace glaxnimate::model { class CustomFont; enum class FontFileFormat { Unknown, TrueType, OpenType, Woff2, Woff }; class CustomFontDatabase : public QObject { Q_OBJECT public: static CustomFontDatabase& instance(); static FontFileFormat font_data_format(const QByteArray& data); CustomFont add_font(const QString& name_alias, const QByteArray& ttf_data); CustomFont get_font(int database_index); std::vector fonts() const; QFont font(const QString& family, const QString& style_name, qreal size) const; std::unordered_map> aliases() const; private: CustomFontDatabase(); ~CustomFontDatabase(); CustomFontDatabase(const CustomFontDatabase&) = delete; CustomFontDatabase& operator=(const CustomFontDatabase&) = delete; class Private; class CustomFontData; std::unique_ptr d; using DataPtr = std::shared_ptr; friend CustomFont; }; class CustomFont { public: explicit CustomFont(int database_index); CustomFont(CustomFontDatabase::DataPtr d); CustomFont(); ~CustomFont(); bool is_valid() const; QString family() const; QString style_name() const; int database_index() const; QFont font(int size) const; const QRawFont& raw_font() const; QByteArray data() const; const QString& source_url() const; const QString& css_url() const; void set_source_url(const QString& url); void set_css_url(const QString& url); private: CustomFontDatabase::DataPtr d; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/command/undo_macro_guard.hpp000664 001750 001750 00000003131 15165022620 034062 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/document.hpp" namespace glaxnimate::command { class UndoMacroGuard { public: UndoMacroGuard() noexcept : document(nullptr) {}; UndoMacroGuard(const QString& name, model::Document* document, bool start_macro = true) : name(name), document(document) { if ( start_macro ) start(); } UndoMacroGuard(const UndoMacroGuard&) = delete; UndoMacroGuard& operator=(const UndoMacroGuard&) = delete; UndoMacroGuard(UndoMacroGuard&& other) noexcept : name(std::move(other.name)), document(other.document), end_macro(other.end_macro) { other.document = nullptr; other.end_macro = false; } UndoMacroGuard& operator=(UndoMacroGuard&& other) noexcept { std::swap(name, other.name); std::swap(document, other.document); std::swap(end_macro, other.end_macro); return *this; } ~UndoMacroGuard() { finish(); } void start() { if ( !end_macro ) { end_macro = true; document->undo_stack().beginMacro(name); } } void finish() { if ( end_macro ) { end_macro = false; document->undo_stack().endMacro(); } } bool started() const noexcept { return end_macro; } private: QString name; model::Document* document; bool end_macro = false; }; } // namespace glaxnimate::command mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/animatable.cpp000664 001750 001750 00000034351 15165022620 034313 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/animation/animatable.hpp" #include "glaxnimate/command/animation_commands.hpp" #include "glaxnimate/model/object.hpp" #include "glaxnimate/math/bezier/segment.hpp" #include "glaxnimate/model/animation/meta_animatable.hpp" class glaxnimate::model::Keyframe::PointKeyframeSplitter : public KeyframeSplitter { public: const Keyframe* self; const Keyframe* other; math::bezier::CubicBezierSolver solver; math::bezier::LengthData len; QPointF tan_in; math::bezier::Point point_before; math::bezier::Point point_mid; qreal prev_split = 0; bool linear; PointKeyframeSplitter(const Keyframe* self, const Keyframe* other) : self(self), other(other), solver(self->bezier_solver(*other)), len(solver, 20), tan_in(self->point_.tan_in), linear(self->is_linear()) { } void step(const QPointF& p) override { if ( linear ) return; // TODO: would need the ability to access other keyframes to properly handle values outside [0, 1] qreal split = math::bound(0., p.y(), 1.); auto beziers = solver.split((split - prev_split) / (1 - prev_split)); prev_split = split; solver = beziers.second; point_before = math::bezier::Point (beziers.first[0], tan_in, beziers.first[1]); point_mid = math::bezier::Point (beziers.first[3], beziers.first[2], beziers.second[1]); tan_in = beziers.second[2]; } std::unique_ptr left(const QPointF& p) const override { if ( linear ) { return std::make_unique( math::lerp(self->time(), other->time(), p.x()), math::lerp(self->get(), other->get(), p.y()) ); } return std::make_unique(math::lerp(self->time(), other->time(), p.x()), point_before); } std::unique_ptr right(const QPointF& p) const override { if ( linear ) { return std::make_unique( math::lerp(self->time(), other->time(), p.x()), math::lerp(self->get(), other->get(), p.y()) ); } return std::make_unique(math::lerp(self->time(), other->time(), p.x()), point_mid); } std::unique_ptr last() const override { if ( linear ) return other->clone(); math::bezier::Point point_after = other->point(); point_after.tan_in = tan_in; return std::make_unique(other->time(), point_after); } }; std::unique_ptr glaxnimate::model::Keyframe::splitter(const KeyframeBase* other) const { return std::make_unique(this, static_cast*>(other)); } bool glaxnimate::model::AnimatedPropertyBase::assign_from(const model::BaseProperty* prop) { if ( prop->traits().flags != traits().flags || prop->traits().type != traits().type ) return false; const AnimatedPropertyBase* other = static_cast(prop); clear_keyframes(); if ( !other->animated() ) return set_value(other->value()); for ( const auto& kf_other : other->keyframe_range() ) { KeyframeBase* kf = set_keyframe(kf_other.time(), kf_other.value()); if ( kf ) kf->set_transition(kf_other.transition()); } return true; } glaxnimate::model::AnimatedPropertyBase::AnimatedPropertyBase(Object *object, const utils::LazyLocalizedString &name, PropertyTraits traits) : BaseProperty(object, name, traits) { object->grouped_animations().add_animatable(this); } bool glaxnimate::model::AnimatedPropertyBase::set_undoable(const QVariant& val, bool commit) { if ( !valid_value(val) ) return false; object()->push_command(new command::SetMultipleAnimated( i18n("Update %1", name()), {this}, {value()}, {val}, commit, time(), object()->document()->record_to_keyframe() )); return true; } glaxnimate::model::AnimatedPropertyBase::MidTransition glaxnimate::model::AnimatableBase::mid_transition(model::FrameTime time) const { const KeyframeBase* kf_during = this->keyframe_containing(time); if ( !kf_during ) return {MidTransition::Invalid, static_value(), {}, {}}; auto before_time = kf_during->time(); if ( before_time >= time ) return {MidTransition::SingleKeyframe, kf_during->value(), {}, kf_during->transition(),}; const KeyframeBase* kf_after = this->keyframe_after(time); if ( !kf_after ) return {MidTransition::SingleKeyframe, kf_during->value(), kf_during->transition(), {},}; auto after_time = kf_after->time(); if ( after_time <= time ) return { MidTransition::SingleKeyframe, kf_after->value(), kf_during->transition(), kf_after->transition(), }; qreal x = math::unlerp(before_time, after_time, time); return do_mid_transition(kf_during, kf_after, x); } glaxnimate::model::AnimatedPropertyBase::MidTransition glaxnimate::model::AnimatableBase::do_mid_transition( const model::KeyframeBase* kf_during, const model::KeyframeBase* kf_after, qreal x ) const { const auto& beftrans = kf_during->transition(); if ( beftrans.hold() || (beftrans.before() == QPointF(0, 0) && beftrans.after() == QPointF(1,1)) ) return {MidTransition::Middle, kf_during->value(), beftrans, beftrans}; qreal t = beftrans.bezier_parameter(x); if ( t <= 0 ) { KeyframeTransition from_previous = {{}, {1, 1}}; if ( auto kf_before = keyframe_before(kf_during->time()) ) from_previous = kf_before->transition(); return {MidTransition::SingleKeyframe, kf_during->value(), from_previous, beftrans}; } else if ( t >= 1 ) { return {MidTransition::SingleKeyframe, kf_during->value(), beftrans, kf_after->transition(),}; } model::AnimatedPropertyBase::MidTransition mt; mt.type = MidTransition::Middle; mt.value = do_mid_transition_value(kf_during, kf_after, x); std::tie(mt.from_previous, mt.to_next) = beftrans.split(x); return mt; } void glaxnimate::model::detail::AnimatedPropertyPosition::split_segment(int index, qreal factor) { if ( keyframes_.size() < 2 ) return; auto before = bezier(); auto after = before; after.split_segment(index, factor); auto parent = std::make_unique(i18n("Split Segment")); FrameTime time = 0; QVariant value; if ( index <= 0 && factor <= 0 ) { time = keyframes_.begin()->time(); value = keyframes_.begin()->value(); } else if ( index >= int(keyframes_.size()) - 1 && factor >= 1 ) { auto back = keyframes_.end(); --back; time = back->time(); value = back->value(); } else { auto iter = keyframes_.begin(); std::advance(iter, index); auto kf_before = iter; ++iter; auto kf_after = iter; value = kf_before->lerp(*kf_after, factor); // Reverse length.at_ratio() to get the correct time at which the transition is equal to `value` math::bezier::Solver segment(kf_before->get(), kf_before->point().tan_out, kf_after->point().tan_in, kf_after->get()); math::bezier::LengthData length(segment, 20); qreal time_factor = qFuzzyIsNull(length.length()) ? 0 : length.from_ratio(factor) / length.length(); time = qRound(math::lerp(kf_before->time(), kf_after->time(), time_factor)); } parent->add_command( std::make_unique(this, time, value, true, nullptr), 0, 0 ); parent->add_command( std::make_unique(this, before, after, true), 1, 1 ); object()->push_command(parent.release()); } bool glaxnimate::model::detail::AnimatedPropertyPosition::set_bezier(math::bezier::Bezier bezier) { bezier.add_close_point(); // TODO if sizes don't match, re-arrange keyframes based on // how far keyframes are in the bezier // eg: a point at 50% of the length will result in a keyframe // at time (keyframes[0].time + keyframes[-1].time) / 2 if ( bezier.size() != int(keyframes_.size()) ) return false; int i = 0; for ( auto kf = keyframes_.begin(); kf != keyframes_.end(); ++kf ) { kf->set_point(bezier[i]); on_keyframe_updated(kf); i++; } value_ = get_at_impl(time()); emitter(this->object(), value_); Q_EMIT bezier_set(bezier); return true; } void glaxnimate::model::detail::AnimatedPropertyPosition::remove_points(const std::set& indices) { auto parent = std::make_unique(i18n("Remove Nodes")); auto before = bezier(); auto after = before.removed_points(indices); int order = 0; int index = 0; auto iter = keyframes_.begin(); while ( iter != keyframes_.end() ) { if ( indices.count(index) ) { auto time = iter->time(); ++iter; parent->add_command(std::make_unique(this, time, nullptr), -order, order); ++order; } else { ++iter; } ++index; } object()->push_command(parent.release()); } glaxnimate::math::bezier::Bezier glaxnimate::model::detail::AnimatedPropertyPosition::bezier() const { math::bezier::Bezier bez; for ( const auto& kf : keyframes_ ) bez.push_back(kf.point()); return bez; } glaxnimate::model::detail::AnimatedPropertyPosition::keyframe_type* glaxnimate::model::detail::AnimatedPropertyPosition::set_keyframe( FrameTime time, const QVariant& val, SetKeyframeInfo* info, bool force_insert ) { if ( val.userType() == QMetaType::QPointF ) return detail::AnimatedProperty::set_keyframe(time, val.value(), info, force_insert); if ( auto v = detail::variant_cast(val) ) { auto kf = detail::AnimatedProperty::set_keyframe(time, v->pos, info, force_insert); kf->set_point(*v); Q_EMIT bezier_set(bezier()); return kf; } // We accept a bezier here so it can be used with SetMultipleAnimated if ( auto v = detail::variant_cast(val) ) { set_bezier(*v); return nullptr; } return nullptr; } glaxnimate::model::detail::AnimatedPropertyPosition::keyframe_type* glaxnimate::model::detail::AnimatedPropertyPosition::set_keyframe( FrameTime time, reference value, SetKeyframeInfo* info, bool force_insert ) { return detail::AnimatedProperty::set_keyframe(time, value, info, force_insert); } bool glaxnimate::model::detail::AnimatedPropertyPosition::set_value(const QVariant& val) { if ( auto v = detail::variant_cast(val) ) return detail::AnimatedProperty::set(*v); if ( auto v = detail::variant_cast(val) ) return set_bezier(*v); return false; } bool glaxnimate::model::detail::AnimatedPropertyPosition::valid_value(const QVariant& val) const { if ( detail::variant_cast(val) || detail::variant_cast(val) ) return true; return false; } QUndoCommand* glaxnimate::model::detail::AnimatedPropertyPosition::command_add_smooth_keyframe(FrameTime time, const QVariant& val, bool commit, QUndoCommand* parent_cmd) { // TODO test this if ( !commit ) return new command::SetKeyframe(this, time, val, false, parent_cmd); auto parent = std::make_unique(i18n("Add Keyframe"), parent_cmd); auto value = val.isNull() ? QVariant(value_) : val; parent->add_command(std::make_unique(this, time, value, true, nullptr), 0, 0); int count = keyframes_.size(); if ( value.userType() == QMetaType::QPointF && count >= 2 ) { auto first = keyframes_.find_best(time); if ( first->time() < time ) { auto second = first; ++second; if ( second != keyframes_.end() && (!first->is_linear() || second->is_linear()) ) { double scaled_time = (time - first->time()) / (second->time() - first->time()); auto factor = first->transition().lerp_factor(scaled_time); auto solver = first->bezier_solver(*second); math::bezier::LengthData len(solver, 20); auto t = len.at_ratio(factor).ratio; auto split = solver.split(t); auto before = bezier(); auto after = before; int index = std::distance(keyframes_.begin(), first); after[index].tan_out = split.first[1]; after[index+1].tan_in = split.second[2]; math::bezier::Point p(split.first[3], split.first[2], split.second[1]); p.translate_to(value.value()); after.insert_point(index+1, p); parent->add_command(std::make_unique(this, before, after, true), 1, 1); } } } return parent.release(); } std::optional glaxnimate::model::detail::AnimatedPropertyPosition::derivative_at(glaxnimate::model::FrameTime time) const { int count = keyframe_count(); if ( count < 2 ) return {}; auto kf_before = keyframes_.find_best(time); auto kf_after = kf_before; ++kf_after; qreal factor = 1; if ( kf_after == keyframes_.end() ) { kf_after = kf_before; --kf_before; } else { factor = math::unlerp(kf_before->time(), kf_after->time(), time); } const math::bezier::Point& point_before = kf_before->point(); const math::bezier::Point& point_after = kf_after->point(); math::bezier::CubicBezierSolver solver(point_before.pos, point_before.tan_out, point_after.tan_in, point_after.pos); return QPointF( solver.derivative(factor, 0), solver.derivative(factor, 1) ); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/options.hpp000664 001750 001750 00000000655 15165022620 031246 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include namespace glaxnimate::io { class ImportExport; struct Options { ImportExport* format = nullptr; QDir path; QString filename; QVariantMap settings; void clear() { *this = {}; } }; } // namespace glaxnimate::io mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/asset_base.hpp000664 001750 001750 00000001252 15165022620 033651 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/utils/pseudo_mutex.hpp" namespace glaxnimate::model { class ReferencePropertyBase; class DocumentNode; class AssetBase { public: using User = ReferencePropertyBase; virtual ~AssetBase() {} /** * \brief Removes the asset if it isn't needed * \param clean_lists when \b true, remove even if the asset is in a useful list * \return Whether it has been removed */ virtual bool remove_if_unused(bool clean_lists) = 0; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/utils.hpp000664 001750 001750 00000000653 15165022620 030711 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/animation/animatable.hpp" namespace glaxnimate::io { /** * \brief Splits keyframes to avoid overshooting [0-1] easing values */ std::vector> split_keyframes(model::AnimatableBase* prop); } // namespace glaxnimate::io src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/precomp_layer.hpp000664 001750 001750 00000004211 15165022620 036425 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/property/reference_property.hpp" #include "glaxnimate/model/stretchable_time.hpp" #include "glaxnimate/model/shapes/composable/composable.hpp" #include "glaxnimate/model/assets/composition.hpp" namespace glaxnimate::model { class PreCompLayer : public Composable { GLAXNIMATE_OBJECT(PreCompLayer) GLAXNIMATE_SUBOBJECT(StretchableTime, timing) GLAXNIMATE_PROPERTY_REFERENCE(Composition, composition, &PreCompLayer::valid_precomps, &PreCompLayer::is_valid_precomp, &PreCompLayer::composition_changed) GLAXNIMATE_PROPERTY(QSizeF, size, {}) GLAXNIMATE_PROPERTY(bool, unbounded, false, {}, {}, PropertyTraits::Visual) public: using Composable::Composable; QIcon tree_icon() const override; QString type_name_human() const override; void set_time(FrameTime t) override; /** * \brief Returns the (frame) time relative to this layer * * Useful for stretching / remapping etc. * Always use this to get animated property values, * even if currently it doesn't do anything */ FrameTime relative_time(FrameTime time) const; QRectF local_bounding_rect(FrameTime t) const override; QTransform local_transform_matrix(model::FrameTime t) const override; void add_shapes(model::FrameTime t, math::bezier::MultiBezier & bez, const QTransform& transform) const override; glaxnimate::math::bezier::MultiBezier to_clip(model::FrameTime t) const override; Q_SIGNALS: void opacity_changed(float op); void composition_changed(); protected: glaxnimate::math::bezier::MultiBezier to_painter_path_impl(model::FrameTime t) const override; void on_paint(renderer::Renderer*, FrameTime, PaintMode, model::Modifier*) const override; void on_composition_changed(model::Composition* old_comp, model::Composition* new_comp) override; private: std::vector valid_precomps() const; bool is_valid_precomp(DocumentNode* node) const; void refresh_owner_composition(); }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/keyframe_base.cpp000664 001750 001750 00000003206 15165022620 035006 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/animation/keyframe_base.hpp" std::vector> glaxnimate::model::KeyframeBase::split(const KeyframeBase* other, std::vector splits) const { std::vector> kfs; if ( transition().hold() ) { kfs.push_back(clone()); kfs.push_back(other->clone()); return kfs; } auto splitter = this->splitter(other); kfs.reserve(splits.size()+2); qreal prev_split = 0; const KeyframeBase* to_split = this; std::unique_ptr split_right; QPointF old_p; for ( qreal split : splits ) { // Skip zeros if ( qFuzzyIsNull(split) ) continue; qreal split_ratio = (split - prev_split) / (1 - prev_split); prev_split = split; auto transitions = to_split->transition().split_t(split_ratio); // split_ratio is t // p.x() is time lerp // p.y() is value lerp QPointF p = transition().bezier().solve(split); splitter->step(p); auto split_left = splitter->left(old_p); split_left->set_transition(transitions.first); old_p = p; split_right = splitter->right(p); split_right->set_transition(transitions.second); to_split = split_right.get(); kfs.push_back(std::move(split_left)); } kfs.push_back(std::move(split_right)); kfs.push_back(splitter->last()); kfs.back()->set_transition(other->transition()); return kfs; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/glaxnimate/glaxnimate_mime.hpp000664 001750 001750 00000001672 15165022620 035044 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/io/mime/mime_serializer.hpp" #include "glaxnimate/io/glaxnimate/glaxnimate_format.hpp" namespace glaxnimate::io::glaxnimate { class GlaxnimateMime : public io::mime::MimeSerializer { public: QString slug() const override { return "glaxnimate"; } QString name() const override { return i18n("Glaxnimate Animation"); } QStringList mime_types() const override; QByteArray serialize(const std::vector& objects) const override; io::mime::DeserializedData deserialize(const QByteArray& data) const override; bool can_deserialize() const override { return true; } static QJsonDocument serialize_json(const std::vector& objects); }; } // namespace glaxnimate::io::glaxnimate mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/animation/meta_animatable.hpp000664 001750 001750 00000011621 15165022620 035321 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/model/animation/animatable_base.hpp" namespace glaxnimate::model { class Object; class MetaKeyframe : public KeyframeBase { public: MetaKeyframe(FrameTime time, std::set external = {}) : KeyframeBase(time), external_(std::move(external)) {} const std::set& external() const { return external_; } bool empty() const { return external_.empty(); } int size() const { return external_.size(); } void add_property(AnimatableBase* prop) { external_.insert(prop); } void remove_property(AnimatableBase* prop) { external_.erase(prop); } void set_transition(const KeyframeTransition &trans) override; // dummy QVariant value() const override { return {}; } bool set_value(const QVariant& ) override { return false; } KeyframeTransition transition() const override { return KeyframeTransition::Special::NoValue; } private: std::set external_; protected: std::unique_ptr do_clone() const override; class MetaKeyframeSplitter : public KeyframeSplitter { public: MetaKeyframeSplitter(const MetaKeyframe* a, const MetaKeyframe* b) : a(a), b(b) {} void step(const QPointF&) override {} std::unique_ptr left(const QPointF& p) const override { return std::make_unique( math::lerp(a->time(), b->time(), p.x()), a->external_ ); } std::unique_ptr right(const QPointF& p) const override { return std::make_unique( math::lerp(a->time(), b->time(), p.x()), a->external_ ); } std::unique_ptr last() const override { return b->clone(); } const MetaKeyframe* a; const MetaKeyframe* b; }; std::unique_ptr splitter(const KeyframeBase* other) const override { return std::make_unique(this, static_cast(other)); } }; /** * @brief Animatable that tracks other animatables * * Used to group keyframes within an object */ class MetaAnimatable : public detail::AnimatableImpl { Q_OBJECT private: using container = KeyframeContainer; public: explicit MetaAnimatable(Object* object); ~MetaAnimatable(); Object* object() const; void add_animatable(model::AnimatableBase* prop); const std::vector& animatables() const; int animatable_flags() const override { return IsWritable|IsMeta; } QUndoCommand* command_add_smooth_keyframe(FrameTime time, const QVariant& value, bool commit = true, QUndoCommand* parent = nullptr) override; QUndoCommand* command_remove_keyframe(FrameTime time, QUndoCommand* parent = nullptr) override; QUndoCommand* command_clear_keyframes(QUndoCommand* parent = nullptr) override; QUndoCommand* command_set_transition(model::FrameTime time, const model::KeyframeTransition& transition, QUndoCommand* parent = nullptr) override; QUndoCommand* command_set_transition_side( model::FrameTime time, model::KeyframeTransition::Descriptive desc, const QPointF& point, bool before_transition, QUndoCommand* parent = nullptr ) override; QUndoCommand* command_move_keyframe(model::FrameTime time_before, model::FrameTime time_after, QUndoCommand* parent = nullptr) override; // Renamed to avoid clashing with BaseProperty // void clear_keyframes() override; // bool remove_keyframe_at_time(FrameTime time) override; // void set_transition(FrameTime time, const KeyframeTransition& transition) override; // void set_transition_before(FrameTime time, const KeyframeTransition& transition) override; // MoveResult move_keyframe(FrameTime from_time, FrameTime to_time) override; // KeyframeBase* set_keyframe(FrameTime time, const QVariant&, SetKeyframeInfo* info, bool) override; QString visual_name() const override; bool value_mismatch() const override { return false; } QVariant value_at_time(FrameTime) const override { return {}; } QVariant static_value() const override { return {}; } bool set_static_value(const QVariant& ) override { return false; } QVariant do_mid_transition_value(const KeyframeBase*, const KeyframeBase*, qreal) const override { return {}; } private Q_SLOTS: void external_keyframe_added(FrameTime time); void external_keyframe_removed(FrameTime time); void external_keyframe_moved(FrameTime from_time, FrameTime to_time); private: class Private; std::unique_ptr d; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/repeater.hpp000664 001750 001750 00000002233 15165022620 035311 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/shape.hpp" #include "glaxnimate/model/transform.hpp" #include "glaxnimate/model/property/sub_object_property.hpp" namespace glaxnimate::model { class Repeater : public StaticOverrides { GLAXNIMATE_OBJECT(Repeater) GLAXNIMATE_SUBOBJECT(Transform, transform) GLAXNIMATE_ANIMATABLE(int, copies, 1) GLAXNIMATE_ANIMATABLE(float, start_opacity, 1, {}, 0, 1, PropertyTraits::Percent) GLAXNIMATE_ANIMATABLE(float, end_opacity, 1, {}, 0, 1, PropertyTraits::Percent) public: using Ctor::Ctor; static QIcon static_tree_icon(); static QString static_type_name_human(); std::unique_ptr to_path() const override; int max_copies() const; protected: math::bezier::MultiBezier process(FrameTime t, const math::bezier::MultiBezier& mbez) const override; void on_paint(renderer::Renderer* p, FrameTime t, PaintMode, model::Modifier*) const override; bool process_collected() const override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/log/listener_store.hpp000664 001750 001750 00000001011 15165022620 032751 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/log/logger.hpp" namespace glaxnimate::log { class ListenerStore: public LogListener { public: const std::vector& lines() const { return lines_; } protected: void on_line(const LogLine& line) override { lines_.push_back(line); } private: std::vector lines_; }; } // namespace glaxnimate::log mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/raster/spritesheet_format.hpp000664 001750 001750 00000001722 15165022620 034756 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include "glaxnimate/io/base.hpp" #include "glaxnimate/io/io_registry.hpp" namespace glaxnimate::io::raster { class SpritesheetFormat : public ImportExport { Q_OBJECT public: QString slug() const override { return "spritesheet"; } QString name() const override { return i18n("Sprite Sheet"); } QStringList extensions(Direction) const override; std::unique_ptr save_settings(model::Composition* comp) const override; bool can_save() const override { return true; } bool can_open() const override { return false; } int priority() const override { return -2; } protected: bool on_save(QIODevice & file, const QString & filename, model::Composition* comp, const QVariantMap & setting_values) override; }; } // namespace glaxnimate::io::raster mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/factory.cpp000664 001750 001750 00000000517 15165022620 031703 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/factory.hpp" glaxnimate::model::Object* glaxnimate::model::Factory::static_build(const QString& name, glaxnimate::model::Document* doc) { return instance().build(name, doc); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/assets/assets.cpp000664 001750 001750 00000017555 15165022620 033052 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/assets/assets.hpp" #include "glaxnimate/model/document.hpp" #include "glaxnimate/command/object_list_commands.hpp" GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::NamedColorList) GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::GradientColorsList) GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::GradientList) GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::BitmapList) GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::CompositionList) GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::FontList) GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Assets) void glaxnimate::model::NamedColorList::on_added(glaxnimate::model::NamedColor* color, int position) { connect(color, &Object::property_changed, this, [position, color, this]{ Q_EMIT color_changed(position, color); }); Ctor::on_added(color, position); Q_EMIT color_added(position, color); } void glaxnimate::model::NamedColorList::on_removed(glaxnimate::model::NamedColor* color, int position) { disconnect(color, nullptr, this, nullptr); Ctor::on_removed(color, position); Q_EMIT color_removed(position, color); } QIcon glaxnimate::model::NamedColorList::tree_icon() const { return QIcon::fromTheme("paint-swatch"); } QIcon glaxnimate::model::GradientColorsList::tree_icon() const { return QIcon::fromTheme("paint-gradient-linear"); } QIcon glaxnimate::model::GradientList::tree_icon() const { return QIcon::fromTheme("gradient"); } QIcon glaxnimate::model::BitmapList::tree_icon() const { return QIcon::fromTheme("folder-images"); } QIcon glaxnimate::model::CompositionList::tree_icon() const { return QIcon::fromTheme("folder-videos"); } void glaxnimate::model::CompositionList::on_added(glaxnimate::model::Composition* obj, int position) { obj->attach(); document()->comp_graph().add_composition(obj); Q_EMIT docnode_child_add_end(obj, position); Q_EMIT precomp_added(obj, position); } void glaxnimate::model::CompositionList::on_removed(glaxnimate::model::Composition* obj, int position) { obj->detach(); document()->comp_graph().remove_composition(obj); Q_EMIT docnode_child_remove_end(obj, position); } void glaxnimate::model::FontList::on_added ( model::EmbeddedFont* obj, int position ) { obj->attach(); Q_EMIT docnode_child_add_end(obj, position); Q_EMIT font_added(obj); } glaxnimate::model::NamedColor* glaxnimate::model::Assets::add_color(const QColor& color, const QString& name) { auto ptr = std::make_unique(document()); ptr->color.set(color); ptr->name.set(name); auto raw = ptr.get(); push_command(new command::AddObject(&colors->values, std::move(ptr), colors->values.size())); return raw; } glaxnimate::model::Bitmap * glaxnimate::model::Assets::add_image_file(const QString& filename, bool embed) { auto image = std::make_unique(document()); image->filename.set(filename); if ( image->pixmap().isNull() ) return nullptr; image->embed(embed); auto ptr = image.get(); push_command(new command::AddObject(&images->values, std::move(image), images->values.size())); return ptr; } glaxnimate::model::Bitmap * glaxnimate::model::Assets::add_image(const QImage& qimage, const QString& store_as) { auto image = std::make_unique(document()); image->set_pixmap(qimage, store_as); auto ptr = image.get(); push_command(new command::AddObject(&images->values, std::move(image), images->values.size())); return ptr; } glaxnimate::model::GradientColors* glaxnimate::model::Assets::add_gradient_colors(int index) { glaxnimate::model::GradientColors *ptr = new glaxnimate::model::GradientColors(document()); ptr->name.set(ptr->type_name_human()); push_command(new command::AddObject(&gradient_colors->values, std::unique_ptr(ptr), index)); return ptr; } glaxnimate::model::Gradient* glaxnimate::model::Assets::add_gradient(int index) { glaxnimate::model::Gradient *ptr = new glaxnimate::model::Gradient(document()); ptr->name.set(ptr->type_name_human()); push_command(new command::AddObject(&gradients->values, std::unique_ptr(ptr), index)); return ptr; } glaxnimate::model::Composition* glaxnimate::model::Assets::add_composition() { auto comp = std::make_unique(document()); auto ptr = comp.get(); push_command(new command::AddObject(&compositions->values, std::move(comp), compositions->values.size())); return ptr; } glaxnimate::model::Composition* glaxnimate::model::Assets::add_comp_no_undo() { auto comp = std::make_unique(document()); return compositions->values.insert(std::move(comp)); } QIcon glaxnimate::model::Assets::tree_icon() const { return QIcon::fromTheme("folder-stash"); } QIcon glaxnimate::model::Assets::instance_icon() const { return tree_icon(); } glaxnimate::model::DocumentNode* glaxnimate::model::detail::defs(glaxnimate::model::Document* doc) { return doc->assets(); } glaxnimate::model::DocumentNode * glaxnimate::model::Assets::docnode_parent() const { return nullptr; } int glaxnimate::model::Assets::docnode_child_count() const { return 6; } glaxnimate::model::DocumentNode * glaxnimate::model::Assets::docnode_child(int index) const { switch ( index ) { case 0: return const_cast(static_cast(colors.get())); case 1: return const_cast(static_cast(images.get())); case 2: return const_cast(static_cast(gradient_colors.get())); case 3: return const_cast(static_cast(gradients.get())); case 4: return const_cast(static_cast(compositions.get())); case 5: return const_cast(static_cast(fonts.get())); default: return nullptr; } } int glaxnimate::model::Assets::docnode_child_index(glaxnimate::model::DocumentNode* dn) const { if ( dn == colors.get() ) return 0; if ( dn == images.get() ) return 1; if ( dn == gradient_colors.get() ) return 2; if ( dn == gradients.get() ) return 3; if ( dn == compositions.get() ) return 4; if ( dn == fonts.get() ) return 5; return -1; } glaxnimate::model::EmbeddedFont* glaxnimate::model::Assets::add_font(const QByteArray& ttf_data) { auto font = std::make_unique(document()); font->data.set(ttf_data); if ( auto old = font_by_index(font->database_index()) ) return old; auto ptr = font.get(); push_command(new command::AddObject(&fonts->values, std::move(font), fonts->values.size())); return ptr; } glaxnimate::model::EmbeddedFont* glaxnimate::model::Assets::add_font(const CustomFont& custom_font) { if ( auto old = font_by_index(custom_font.database_index()) ) return old; auto font = std::make_unique(document(), custom_font); auto ptr = font.get(); push_command(new command::AddObject(&fonts->values, std::move(font), fonts->values.size())); return ptr; } glaxnimate::model::EmbeddedFont * glaxnimate::model::Assets::font_by_index(int database_index) const { for ( const auto& font : fonts->values ) if ( font->database_index() == database_index ) return font.get(); return nullptr; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/shapes/rect.hpp000664 001750 001750 00000001673 15165022620 033750 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/shape.hpp" namespace glaxnimate::model { class Rect : public Shape { GLAXNIMATE_OBJECT(Rect) GLAXNIMATE_ANIMATABLE(QPointF, position, QPointF()) GLAXNIMATE_ANIMATABLE(QSizeF, size, QSizeF()) GLAXNIMATE_ANIMATABLE(float, rounded, 0, {}, 0) public: using Shape::Shape; QIcon tree_icon() const override { return QIcon::fromTheme("draw-rectangle"); } QString type_name_human() const override { return i18n("Rectangle"); } math::bezier::Bezier to_bezier(FrameTime t) const override; QRectF local_bounding_rect(FrameTime t) const override { QSizeF sz = size.get_at(t); return QRectF(position.get_at(t) - QPointF(sz.width()/2, sz.height()/2), sz); } }; } // namespace glaxnimate::model src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/inflate_deflate.hpp000664 001750 001750 00000001354 15165022620 036534 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/modifiers/path_modifier.hpp" namespace glaxnimate::model { class InflateDeflate : public StaticOverrides { GLAXNIMATE_OBJECT(InflateDeflate) GLAXNIMATE_ANIMATABLE(float, amount, 0, {}, -1, 1, PropertyTraits::Percent) public: using Ctor::Ctor; static QIcon static_tree_icon(); static QString static_type_name_human(); math::bezier::MultiBezier process(FrameTime t, const math::bezier::MultiBezier& mbez) const override; protected: bool process_collected() const override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/composable/000775 001750 001750 00000000000 15165022620 033134 5ustar00ddennedyddennedy000000 000000 src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/shapes/modifiers/path_modifier.hpp000664 001750 001750 00000001150 15165022620 036232 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "glaxnimate/model/shapes/shape.hpp" namespace glaxnimate::model { /** * \brief Base class for modifiers than only alter the bezier points */ class PathModifier : public Modifier { Q_OBJECT public: using Modifier::Modifier; std::unique_ptr to_path() const override; protected: void on_paint(renderer::Renderer* painter, FrameTime t, PaintMode mode, model::Modifier* modifier) const override; }; } // namespace glaxnimate::model mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/binary_types.hpp000664 001750 001750 00000000422 15165022620 032253 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include namespace glaxnimate::io { using VarUint = std::uint64_t; using Float32 = float; } // namespace glaxnimate::io mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/gzip/gzip-zlib.cpp000664 001750 001750 00000016231 15165022620 033301 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "gzip.hpp" #include #include #include #include #include "glaxnimate/utils/i18n.hpp" using namespace glaxnimate; namespace { class Gzipper { public: static const int chunk_size = 0x4000; using Buffer = std::array; struct BufferView { const char* data; std::size_t size; }; explicit Gzipper(const gzip::ErrorFunc& on_error) : on_error(on_error) { zip_stream.zalloc = Z_NULL; zip_stream.zfree = Z_NULL; zip_stream.opaque = Z_NULL; } void add_data(const QByteArray& data) { add_data(data.data(), data.size()); } void add_data(const char* data, std::size_t size) { zip_stream.next_in = (Bytef*) data; zip_stream.avail_in = size; zip_stream.avail_out = 0; } bool finished() const { return zip_stream.avail_out != 0; } bool inflate_init() { process_fn = &inflate; end_fn = &inflateEnd; op = "inflate"; return zlib_check("inflateInit2", inflateInit2(&zip_stream, 16|MAX_WBITS)); } BufferView process() { zip_stream.avail_out = chunk_size; zip_stream.next_out = buffer.data(); zlib_check(op, process_fn(&zip_stream, Z_FINISH)); return {(const char*)buffer.data(), chunk_size - zip_stream.avail_out}; } bool end() { return zlib_check(op, end_fn(&zip_stream), "End"); } bool deflate_init(int level) { process_fn = &deflate; end_fn = &deflateEnd; op = "deflate"; return zlib_check("deflateInit2", deflateInit2(&zip_stream, level, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY)); } void log_error(const QString& msg) { if ( on_error ) on_error(msg); } private: bool zlib_check(const char* func, int result, const char* extra = "") { if ( result >= 0 || result == Z_BUF_ERROR ) return true; log_error(i18n("ZLib %1%2 returned %3", func, extra, result)); return false; } z_stream zip_stream; gzip::ErrorFunc on_error; Buffer buffer; int (*process_fn)(z_streamp, int); int (*end_fn)(z_streamp); const char* op; }; } // namespace bool gzip::compress(const QByteArray& data, QIODevice& output, const gzip::ErrorFunc& on_error, int level, quint32* compressed_size) { Gzipper gz(on_error); if ( !gz.deflate_init(level) ) return false; gz.add_data(data); quint32 total_size = 0; while ( !gz.finished() ) { auto bv = gz.process(); output.write(bv.data, bv.size); total_size += bv.size; } if ( compressed_size ) *compressed_size = total_size; return gz.end(); } bool gzip::decompress(QIODevice& input, QByteArray& output, const gzip::ErrorFunc& on_error) { Gzipper gz(on_error); if ( !gz.inflate_init() ) return false; while ( true ) { QByteArray data = input.read(Gzipper::chunk_size); if ( data.isEmpty() ) break; gz.add_data(data); while ( !gz.finished() ) { auto bv = gz.process(); output.append(bv.data, bv.size); } } return gz.end(); } bool gzip::decompress(const QByteArray& input, QByteArray& output, const gzip::ErrorFunc& on_error) { Gzipper gz(on_error); if ( !gz.inflate_init() ) return false; gz.add_data(input); while ( !gz.finished() ) { auto bv = gz.process(); output.append(bv.data, bv.size); } return gz.end(); } class gzip::GzipStream::Private { public: Private(QIODevice* target, const ErrorFunc& ef) : zipper(ef), target(target) {} Gzipper zipper; QIODevice* target; QIODevice::OpenMode mode = QIODevice::NotOpen; qint64 total_size = 0; QByteArray buffer; void _memcpy(char* dest, const char* src, std::size_t size) { std::memcpy(dest, src, size); } }; gzip::GzipStream::GzipStream(QIODevice* target, const gzip::ErrorFunc& on_error) : d(std::make_unique(target, on_error)) {} gzip::GzipStream::~GzipStream() { if ( d->mode != NotOpen ) d->zipper.end(); } bool gzip::GzipStream::open(QIODevice::OpenMode mode) { if ( d->mode != NotOpen ) { QString error = "Gzip stream already open"; setErrorString(error); return false; } if ( mode == ReadOnly ) { d->zipper.inflate_init(); d->mode = ReadOnly; setOpenMode(d->mode); return true; } if ( mode == WriteOnly ) { d->zipper.deflate_init(9); d->mode = WriteOnly; setOpenMode(d->mode); return true; } setErrorString("Unsupported open mode for Gzip stream"); return false; } bool gzip::GzipStream::atEnd() const { return d->target->atEnd() && d->buffer.isEmpty(); } qint64 gzip::GzipStream::writeData(const char* data, qint64 len) { if ( d->mode != WriteOnly ) { setErrorString("Gzip stream not open for writing"); return -1; } d->zipper.add_data(data, len); while ( !d->zipper.finished() ) { auto bv = d->zipper.process(); d->target->write(bv.data, bv.size); d->total_size += bv.size; } return len; } qint64 gzip::GzipStream::readData(char* data, qint64 maxlen) { if ( d->mode != ReadOnly ) { setErrorString("Gzip stream not open for reading"); return -1; } if ( maxlen <= 0 ) return 0; qint64 read = 0; if ( !d->buffer.isEmpty() ) { if ( d->buffer.size() < maxlen ) { d->_memcpy(data, d->buffer.data(), d->buffer.size()); maxlen -= d->buffer.size(); data += d->buffer.size(); read += d->buffer.size(); d->buffer.clear(); } else { d->_memcpy(data, d->buffer.data(), maxlen); d->buffer = d->buffer.mid(maxlen); return maxlen; } } while ( read < maxlen ) { QByteArray buf = d->target->read(Gzipper::chunk_size); if ( buf.isEmpty() ) break; d->zipper.add_data(buf); while ( !d->zipper.finished() ) { auto bv = d->zipper.process(); if ( qint64(read + bv.size) >= maxlen ) { auto delta = maxlen - read; d->_memcpy(data + read, bv.data, delta); d->buffer = QByteArray(bv.data + delta, bv.size - delta); read = maxlen; while ( !d->zipper.finished() ) { bv = d->zipper.process(); d->buffer += QByteArray(bv.data, bv.size); } break; } else { d->_memcpy(data + read, bv.data, bv.size); read += bv.size; } } } d->total_size += read; return read; } qint64 gzip::GzipStream::ouput_size() const { return d->total_size; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/script/register_impl.hpp000664 001750 001750 00000026333 15165022620 033316 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "glaxnimate/script/registrar_common.hpp" #include "glaxnimate/math/bezier/bezier.hpp" #include "glaxnimate/model/animation/keyframe_transition.hpp" namespace glaxnimate::script { template const char* type_name() { return QMetaType(qMetaTypeId()).name(); } template struct meta_2_cpp_s; template struct cpp_2_meta_s; #define TYPE_NAME(Type) //template<> const char* type_name() { return #Type; } #define SETUP_TYPE(MetaInt, Type) \ TYPE_NAME(Type) \ template<> struct meta_2_cpp_s { using type = Type; }; \ template<> struct cpp_2_meta_s { static constexpr const int value = MetaInt; }; template using meta_2_cpp = typename meta_2_cpp_s::type; template constexpr const int cpp_2_meta = cpp_2_meta_s::value; SETUP_TYPE(QMetaType::Int, int) SETUP_TYPE(QMetaType::Bool, bool) SETUP_TYPE(QMetaType::Double, double) SETUP_TYPE(QMetaType::Float, float) SETUP_TYPE(QMetaType::UInt, unsigned int) SETUP_TYPE(QMetaType::Long, long) SETUP_TYPE(QMetaType::LongLong, long long) SETUP_TYPE(QMetaType::Short, short) SETUP_TYPE(QMetaType::ULong, unsigned long) SETUP_TYPE(QMetaType::ULongLong, unsigned long long) SETUP_TYPE(QMetaType::UShort, unsigned short) SETUP_TYPE(QMetaType::QString, QString) SETUP_TYPE(QMetaType::QColor, QColor) SETUP_TYPE(QMetaType::QUuid, QUuid) SETUP_TYPE(QMetaType::QObjectStar, QObject*) SETUP_TYPE(QMetaType::QVariantList, QVariantList) SETUP_TYPE(QMetaType::QVariant, QVariant) SETUP_TYPE(QMetaType::QStringList, QStringList) SETUP_TYPE(QMetaType::QVariantMap, QVariantMap) SETUP_TYPE(QMetaType::QVariantHash, QVariantHash) SETUP_TYPE(QMetaType::QPointF, QPointF) SETUP_TYPE(QMetaType::QSizeF, QSizeF) SETUP_TYPE(QMetaType::QSize, QSize) SETUP_TYPE(QMetaType::QVector2D, QVector2D) SETUP_TYPE(QMetaType::QRectF, QRectF) SETUP_TYPE(QMetaType::QByteArray, QByteArray) SETUP_TYPE(QMetaType::QDateTime, QDateTime) SETUP_TYPE(QMetaType::QDate, QDate) SETUP_TYPE(QMetaType::QTime, QTime) SETUP_TYPE(QMetaType::QImage, QImage) // If you add stuff here, remember to add it to supported_types too TYPE_NAME(std::vector) using supported_types = std::integer_sequence; using supported_custom_types = std::tuple< model::KeyframeTransition, math::bezier::Point, math::bezier::Bezier >; template class Func, class RetT, class... FuncArgs> bool custom_type_dispatch_impl_step(int meta_type, RetT& ret, FuncArgs&&... args) { if ( meta_type != qMetaTypeId() ) return false; ret = Func::do_the_thing(std::forward(args)...); return true; }; template class Func, class RetT, class... FuncArgs, std::size_t... i> bool custom_type_dispatch_impl(int meta_type, RetT& ret, std::index_sequence, FuncArgs&&... args) { return (custom_type_dispatch_impl_step, Func>(meta_type, ret, std::forward(args)...)||...); } template class Func, class RetT, class... FuncArgs> bool type_dispatch_impl_step(int meta_type, RetT& ret, FuncArgs&&... args) { if ( meta_type != i ) return false; ret = Func>::do_the_thing(std::forward(args)...); return true; }; template class Func, class RetT, class... FuncArgs, int... i> bool type_dispatch_impl(int meta_type, RetT& ret, std::integer_sequence, FuncArgs&&... args) { return (type_dispatch_impl_step(meta_type, ret, std::forward(args)...)||...); } template class Func, class RetT, class... FuncArgs> RetT type_dispatch(int meta_type, FuncArgs&&... args) { if ( meta_type >= QMetaType::User ) { if ( QMetaType(meta_type).flags() & QMetaType::IsEnumeration ) return Func::do_the_thing(std::forward(args)...); if ( QMetaType(meta_type).flags() & QMetaType::PointerToQObject ) return Func::do_the_thing(std::forward(args)...); RetT ret; custom_type_dispatch_impl(meta_type, ret, std::make_index_sequence>{}, std::forward(args)...); return ret; } RetT ret; type_dispatch_impl(meta_type, ret, supported_types(), std::forward(args)...); return ret; } template class Func, class RetT, class... FuncArgs> static RetT type_dispatch_maybe_void(int meta_type, FuncArgs&&... args) { if ( meta_type == QMetaType::Void ) return Func::do_the_thing(std::forward(args)...); return type_dispatch(meta_type, std::forward(args)...); } class ArgumentBuffer { public: ArgumentBuffer(const QMetaMethod& method) : method(method) {} ArgumentBuffer(const ArgumentBuffer&) = delete; ArgumentBuffer& operator=(const ArgumentBuffer&) = delete; ~ArgumentBuffer() { for ( int i = 0; i < destructors_used; i++) { destructors[i]->destruct(); delete destructors[i]; } } template const char* object_type_name(const CppType&) { return type_name(); } std::string object_type_name(QObject* value) { std::string s = value->metaObject()->className(); std::string target = method.parameterTypes()[arguments].toStdString(); if ( !target.empty() && target.back() == '*' ) { target.pop_back(); if ( s != target ) { for ( auto mo = value->metaObject()->superClass(); mo; mo = mo->superClass() ) { std::string moname = mo->className(); if ( moname == target ) return target + "*"; } } } return s + "*"; } template CppType* allocate(const CppType& value) { if ( avail() < int(sizeof(CppType)) ) throw ScriptError(i18n("Cannot allocate argument")); CppType* addr = new (next_mem()) CppType; buffer_used += sizeof(CppType); names[arguments] = object_type_name(value); generic_args[arguments] = { names[arguments].c_str(), addr }; ensure_destruction(addr); arguments += 1; *addr = value; return addr; } template void allocate_return_type(const char* name) { if ( avail() < int(sizeof(CppType)) ) throw ScriptError(i18n("Cannot allocate return value")); CppType* addr = new (next_mem()) CppType; buffer_used += sizeof(CppType); ret = { name, addr }; ensure_destruction(addr); ret_addr = addr; } template CppType return_value() { return *static_cast(ret_addr); } const QGenericArgument& arg(int i) const { return generic_args[i]; } const QGenericReturnArgument& return_arg() const { return ret; } private: class Destructor { public: Destructor() = default; Destructor(const Destructor&) = delete; Destructor& operator=(const Destructor&) = delete; virtual ~Destructor() = default; virtual void destruct() const = 0; }; template struct DestructorImpl : public Destructor { DestructorImpl(CppType* addr) : addr(addr) {} void destruct() const override { addr->~CppType(); } CppType* addr; }; int arguments = 0; int buffer_used = 0; std::array buffer; std::array destructors; std::array generic_args; std::array names; QGenericReturnArgument ret; void* ret_addr = nullptr; QMetaMethod method; int destructors_used = 0; int avail() { return buffer.size() - buffer_used; } void* next_mem() { return buffer.data() + buffer_used; } template std::enable_if_t> ensure_destruction(CppType*) {} template std::enable_if_t> ensure_destruction(CppType* addr) { destructors[destructors_used] = new DestructorImpl(addr); destructors_used++; } }; template<> inline void ArgumentBuffer::allocate_return_type(const char*){} template<> inline void ArgumentBuffer::return_value(){} template struct AllocateReturn { static bool do_the_thing(script::ArgumentBuffer& buf, const char* name) { buf.allocate_return_type(name); return true; } }; template QVariant qvariant_from_cpp(const T& t) { return QVariant::fromValue(t); } template T qvariant_to_cpp(const QVariant& v) { return v.value(); } template<> inline QVariant qvariant_from_cpp(const std::string& t) { return QString::fromStdString(t); } template<> inline std::string qvariant_to_cpp(const QVariant& v) { return v.toString().toStdString(); } template<> inline QVariant qvariant_from_cpp(const QVariant& t) { return t; } template<> inline QVariant qvariant_to_cpp(const QVariant& v) { return v; } template<> inline void qvariant_to_cpp(const QVariant&) {} template<> QVariant inline qvariant_from_cpp>(const std::vector& t) { QVariantList list; for ( QObject* obj : t ) list.push_back(QVariant::fromValue(obj)); return list; } template<> inline std::vector qvariant_to_cpp>(const QVariant& v) { std::vector objects; for ( const QVariant& vi : v.toList() ) objects.push_back(vi.value()); return objects; } } // namespace glaxnimate::script mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/module/video/video_format.cpp000664 001750 001750 00000063137 15165022620 034214 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/module/video/video_format.hpp" #include #include #include #include extern "C" { #include #include #include #include } #include "glaxnimate/utils/qstring_exception.hpp" #include "glaxnimate/log/log.hpp" #include "glaxnimate/model/assets/composition.hpp" namespace glaxnimate::av { template QString to_str(Callback callback, Args... args) { char buf[max_size] = {0}; return callback(buf, args...); } QString err2str(int errnum) { return to_str(&av_make_error_string, AV_ERROR_MAX_STRING_SIZE, errnum); } class Error: public utils::QStringException<>{ using Ctor::Ctor; }; template class CGuard { public: CGuard(Callback callback, Object object) : callback(callback), object(object) {} ~CGuard() { callback(object); } private: Callback callback; Object object; }; // a wrapper around a single output AVStream struct OutputStream { OutputStream( AVFormatContext *oc, AVCodecID codec_id ) { format_context = oc; // find the encoder codec = (AVCodec*)avcodec_find_encoder(codec_id); if ( !codec ) throw av::Error(i18n("Could not find encoder for '%1'", avcodec_get_name(codec_id))); stream = avformat_new_stream(oc, nullptr); if (!stream) throw av::Error(i18n("Could not allocate stream")); stream->id = oc->nb_streams-1; codec_context = avcodec_alloc_context3(codec); if ( !codec_context ) throw av::Error(i18n("Could not alloc an encoding context")); // Some formats want stream headers to be separate. if (oc->oformat->flags & AVFMT_GLOBALHEADER) codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } ~OutputStream() { avcodec_free_context(&codec_context); av_frame_free(&frame); av_frame_free(&tmp_frame); sws_freeContext(sws_context); } int read_packets() { int ret = 0; while ( ret >= 0 ) { AVPacket pkt; memset(&pkt, 0, sizeof(AVPacket)); ret = avcodec_receive_packet(codec_context, &pkt); if ( ret == AVERROR(EAGAIN) || ret == AVERROR_EOF ) break; else if (ret < 0) throw av::Error(i18n("Error encoding a frame: %1", av::err2str(ret))); // rescale output packet timestamp values from codec to stream timebase av_packet_rescale_ts(&pkt, codec_context->time_base, stream->time_base); pkt.stream_index = stream->index; // Write the compressed frame to the media file. ret = av_interleaved_write_frame(format_context, &pkt); av_packet_unref(&pkt); if (ret < 0) throw av::Error(i18n("Error while writing output packet: %1", av::err2str(ret))); } return ret; } int write_frame(AVFrame *frame) { // send the frame to the encoder int ret = avcodec_send_frame(codec_context, frame); if ( ret < 0 ) throw av::Error(i18n("Error sending a frame to the encoder: %1", av::err2str(ret))); return read_packets(); } int flush_frames() { return write_frame(nullptr); } AVStream *stream = nullptr; AVCodecContext *codec_context = nullptr; // pts of the next frame that will be generated int64_t next_pts = 0; AVFrame *frame = nullptr; AVFrame *tmp_frame = nullptr; SwsContext *sws_context = nullptr; AVFormatContext *format_context = nullptr; AVCodec *codec = nullptr; }; class DictWrapper { public: class Item { public: Item(AVDictionary** av_dict, QByteArray key) : av_dict(av_dict), key(std::move(key)), value(nullptr) {} operator const char*() const { return get(); } const char* get() const { if ( value == nullptr ) { if ( auto entry = av_dict_get(*av_dict, key.data(), nullptr, 0) ) value = entry->value; } return value; } void set(const char* text) { int ret = av_dict_set(av_dict, key.data(), text, 0); if ( ret >= 0 ) value = nullptr; else throw Error(i18n("Could not set dict key `%1`: %2", QString(key), err2str(ret))); } void set(const QString& s) { set(s.toUtf8().data()); } void set(int64_t v) { int ret = av_dict_set_int(av_dict, key.data(), v, 0); if ( ret >= 0 ) value = nullptr; else throw Error(i18n("Could not set dict key `%1`: %2", QString(key), err2str(ret))); } Item& operator=(const char* text) { set(text); return *this; } Item& operator=(const QString& text) { set(text); return *this; } Item& operator=(int64_t v) { set(v); return *this; } private: AVDictionary** av_dict; QByteArray key; mutable const char* value; }; DictWrapper(AVDictionary** av_dict) : av_dict(av_dict) {} Item operator[](const QString& key) { return Item(av_dict, key.toUtf8()); } int size() const { return av_dict_count(*av_dict); } void erase(const QString& key) { int ret = av_dict_set(av_dict, key.toUtf8().data(), nullptr, 0); if ( ret < 0 ) throw Error(i18n("Could not erase dict key `%1`: %2", key, err2str(ret))); } private: AVDictionary** av_dict; }; class Dict : public DictWrapper { public: Dict() : DictWrapper(&local_dict) {} Dict(Dict&& other) : Dict() { std::swap(local_dict, other.local_dict); } Dict(const Dict& other) : Dict() { int ret = av_dict_copy(&local_dict, other.local_dict, 0); if ( ret < 0 ) throw Error(i18n("Could not copy dict: %1", err2str(ret))); } Dict& operator=(Dict&& other) { std::swap(local_dict, other.local_dict); return *this; } Dict& operator=(const Dict& other) { int ret = av_dict_copy(&local_dict, other.local_dict, 0); if ( ret < 0 ) throw Error(i18n("Could not copy dict `%1`: %2", err2str(ret))); return *this; } ~Dict() { av_dict_free(&local_dict); } AVDictionary** dict() { return &local_dict; } private: AVDictionary* local_dict = nullptr; }; class Video { public: static AVFrame *alloc_picture(AVPixelFormat pix_fmt, int width, int height) { AVFrame *picture; int ret; picture = av_frame_alloc(); if (!picture) return nullptr; picture->format = pix_fmt; picture->width = width; picture->height = height; // allocate the buffers for the frame data ret = av_frame_get_buffer(picture, 0); if (ret < 0) throw av::Error(i18n("Could not allocate frame data.")); return picture; } static std::pair image_format(QImage::Format format) { switch ( format ) { case QImage::Format_Invalid: default: return {AV_PIX_FMT_NONE, QImage::Format_Invalid}; case QImage::Format_Mono: case QImage::Format_MonoLSB: return {AV_PIX_FMT_MONOBLACK, format}; case QImage::Format_Indexed8: return {AV_PIX_FMT_ARGB, QImage::Format_RGB32}; #ifdef Q_LITTLE_ENDIAN case QImage::Format_RGB32: return {AV_PIX_FMT_0BGR, format}; case QImage::Format_ARGB32: return {AV_PIX_FMT_BGRA, format}; case QImage::Format_ARGB32_Premultiplied: return {AV_PIX_FMT_BGRA, format}; #else case QImage::Format_RGB32: return {AV_PIX_FMT_0RGB, format}; case QImage::Format_ARGB32: return {AV_PIX_FMT_ARGB, format}; case QImage::Format_ARGB32_Premultiplied: return {AV_PIX_FMT_ARGB, format}; #endif case QImage::Format_RGB16: return {AV_PIX_FMT_RGB565LE, format}; case QImage::Format_RGB555: return {AV_PIX_FMT_RGB555LE, format}; case QImage::Format_RGB888: return {AV_PIX_FMT_RGB24, format}; case QImage::Format_RGBX8888: return {AV_PIX_FMT_RGB0, format}; case QImage::Format_RGBA8888: case QImage::Format_RGBA8888_Premultiplied: return {AV_PIX_FMT_RGBA, format}; case QImage::Format_Alpha8: case QImage::Format_Grayscale8: return {AV_PIX_FMT_GRAY8, format}; case QImage::Format_RGBA64_Premultiplied: case QImage::Format_ARGB8555_Premultiplied: case QImage::Format_ARGB8565_Premultiplied: case QImage::Format_ARGB6666_Premultiplied: case QImage::Format_ARGB4444_Premultiplied: case QImage::Format_A2RGB30_Premultiplied: case QImage::Format_A2BGR30_Premultiplied: return {AV_PIX_FMT_ARGB, QImage::Format_ARGB32}; case QImage::Format_RGB30: case QImage::Format_RGB444: case QImage::Format_RGB666: case QImage::Format_BGR30: case QImage::Format_RGBX64: case QImage::Format_RGBA64: return {AV_PIX_FMT_RGB24, QImage::Format_RGB888}; } } static AVPixelFormat best_pixel_format(const AVPixelFormat* pix_fmts) { static const std::array preferred = { // RGBA and similar (no conversion) AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA, AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA, AV_PIX_FMT_RGBA64BE, AV_PIX_FMT_RGBA64LE, AV_PIX_FMT_BGRA64BE, AV_PIX_FMT_BGRA64LE, // YUV + Alpha AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA420P, // RGB AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, }; if ( !pix_fmts ) return AV_PIX_FMT_NONE; std::set supported; for ( auto it = pix_fmts; it && *it != AV_PIX_FMT_NONE; ++it ) supported.insert(*it); for ( auto pref : preferred ) if ( supported.count(pref) ) return pref; return *pix_fmts; } Video(AVFormatContext *oc, Dict options, AVCodecID codec_id, int64_t bit_rate, int width, int height, int fps) : ost(oc, codec_id) { if ( ost.codec->type != AVMEDIA_TYPE_VIDEO ) throw Error(i18n("No video codec")); ost.codec_context->codec_id = codec_id; ost.codec_context->bit_rate = bit_rate; // Resolution must be a multiple of two ost.codec_context->width = width; if ( ost.codec_context->width % 2 ) ost.codec_context->width -= 1; ost.codec_context->height = height; if ( ost.codec_context->height % 2 ) ost.codec_context->height -= 1; // timebase: This is the fundamental unit of time (in seconds) in terms // of which frame timestamps are represented. For fixed-fps content, // timebase should be 1/framerate and timestamp increments should be // identical to 1. ost.stream->time_base = AVRational{ 1, fps }; ost.codec_context->time_base = ost.stream->time_base; // Q_EMIT one intra frame every twelve frames at most ost.codec_context->gop_size = 12; // get_format() for some reason returns an invalid value #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(61, 19, 100) const AVPixelFormat* pix_fmts = ost.codec->pix_fmts; #else const AVPixelFormat* pix_fmts; int num_pix_fmts = 0; avcodec_get_supported_config(ost.codec_context, nullptr, AV_CODEC_CONFIG_PIX_FORMAT, 0, (const void**)&pix_fmts, &num_pix_fmts); #endif ost.codec_context->pix_fmt = best_pixel_format(pix_fmts); if ( ost.codec_context->pix_fmt == AV_PIX_FMT_NONE ) throw av::Error(i18n("Could not determine pixel format")); // // just for testing, we also add B-frames // if ( ost.codec_context->codec_id == AV_CODEC_ID_MPEG2VIDEO ) // ost.codec_context->max_b_frames = 2; // This suppresses a YUV-related warning if ( codec_id == AV_CODEC_ID_WEBP ) options[QStringLiteral("lossless")] = "1"; int ret; // open the codec ret = avcodec_open2(ost.codec_context, ost.codec, options.dict()); if (ret < 0) throw av::Error(i18n("Could not open video codec: %1", av::err2str(ret))); // allocate and init a re-usable frame ost.frame = alloc_picture(ost.codec_context->pix_fmt, ost.codec_context->width, ost.codec_context->height); if (!ost.frame) throw av::Error(i18n("Could not allocate video frame")); ost.tmp_frame = nullptr; /* copy the stream parameters to the muxer */ ret = avcodec_parameters_from_context(ost.stream->codecpar, ost.codec_context); if (ret < 0) throw av::Error(i18n("Could not copy the stream parameters")); } static void fill_image(AVFrame *pict, const QImage& image) { for ( int y = 0; y < image.height(); y++) { auto line = image.constScanLine(y); for ( int x = 0; x < image.bytesPerLine(); x++ ) { pict->data[0][y * pict->linesize[0] + x] = line[x]; } } } AVFrame *get_video_frame(QImage image) { // when we pass a frame to the encoder, it may keep a reference to it // internally; make sure we do not overwrite it here if ( av_frame_make_writable(ost.frame) < 0 ) throw av::Error(i18n("Error while creating video frame")); auto format = image_format(image.format()); if ( format.first == AV_PIX_FMT_NONE ) { image = QImage(ost.codec_context->width, ost.codec_context->height, QImage::Format_RGB888); format.first = AV_PIX_FMT_RGB24; } else if ( format.second != image.format() ) { image = image.convertToFormat(format.second); } if ( ost.codec_context->pix_fmt != format.first || image.width() != ost.codec_context->width || image.height() != ost.codec_context->height ) { if (!ost.sws_context) { ost.sws_context = sws_getContext( image.width(), image.height(), format.first, ost.codec_context->width, ost.codec_context->height, ost.codec_context->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr ); if (!ost.sws_context) throw av::Error(i18n("Could not initialize the conversion context")); } if ( !ost.tmp_frame ) { ost.tmp_frame = alloc_picture(format.first, image.width(), image.height()); if (!ost.tmp_frame) throw av::Error(i18n("Could not allocate temporary picture")); } fill_image(ost.tmp_frame, image); sws_scale(ost.sws_context, (const uint8_t * const *) ost.tmp_frame->data, ost.tmp_frame->linesize, 0, ost.codec_context->height, ost.frame->data, ost.frame->linesize); } else { fill_image(ost.frame, image); } ost.frame->pts = ost.next_pts++; return ost.frame; } void write_video_frame(const QImage& image) { ost.write_frame(get_video_frame(image)); } void flush() { ost.flush_frames(); } private: OutputStream ost; }; class Logger { private: struct LogData { std::mutex mutex; video::VideoFormat* format = nullptr; int log_level; static LogData& instance() { static LogData instance; return instance; } static void static_callback(void *, int level, const char *fmt, va_list vl) { instance().callback(level, fmt, vl); } void setup(video::VideoFormat* format, int log_level) { auto guard = std::lock_guard(mutex); this->format = format; this->log_level = log_level; av_log_set_callback(&LogData::static_callback); } void teardown() { auto guard = std::lock_guard(mutex); av_log_set_callback(&av_log_default_callback); format = nullptr; } void callback(int level, const char *fmt, va_list vl) { auto guard = std::lock_guard(mutex); if ( level > log_level ) return; char buffer[1024]; std::vsprintf(buffer, fmt, vl); QString msg(buffer); if ( msg.endsWith('\n') ) msg.remove(msg.size()-1, 1); if ( level > AV_LOG_WARNING ) { format->information(msg); } else if ( level == AV_LOG_WARNING ) { log::Log("libav").log(msg); format->warning(msg); } else { log::Log("libav").log(msg, log::Error); format->error(msg); } } }; public: Logger(video::VideoFormat* format) : Logger(format, av_log_get_level()) {} Logger(video::VideoFormat* format, int log_level) { level = av_log_get_level(); av_log_set_level(log_level); LogData::instance().setup(format, log_level); } ~Logger() { LogData::instance().teardown(); av_log_set_level(level); } private: int level; }; class DeviceIo { public: DeviceIo(QIODevice* device, int block_size = 4*1024) { buffer = (unsigned char*)av_malloc(block_size); context_ = avio_alloc_context( buffer, block_size, 1, device, &DeviceIo::read_packet, &DeviceIo::write_packet, &DeviceIo::seek ); } ~DeviceIo() { avio_context_free(&context_); av_free(buffer); } AVIOContext* context() const noexcept { return context_; } private: static int read_packet(void *opaque, uint8_t *buf, int buf_size) { QIODevice* device = (QIODevice*)opaque; return device->read((char*)buf, buf_size); } #if LIBAVFORMAT_VERSION_MAJOR >= 61 static int write_packet(void *opaque, const uint8_t *buf, int buf_size) #else static int write_packet(void *opaque, uint8_t *buf, int buf_size) #endif { QIODevice* device = (QIODevice*)opaque; return device->write((char*)buf, buf_size); } static int64_t seek(void *opaque, int64_t offset, int whence) { QIODevice* device = (QIODevice*)opaque; switch ( whence ) { case SEEK_SET: return device->seek(offset); case SEEK_CUR: return device->seek(offset + device->pos()); case SEEK_END: device->readAll(); return device->seek(offset + device->pos()); } return 0; } AVIOContext* context_; unsigned char * buffer; }; } // namespace glaxnimate::av static QStringList out_ext; static bool format_skip(const AVOutputFormat* format) { static std::set blacklisted = { /*"webp",*/ "gif", "ico" }; return blacklisted.count(format->name) || format->video_codec == AV_CODEC_ID_NONE || format->flags & (AVFMT_NOFILE|AVFMT_NEEDNUMBER) ; } static void get_formats() { out_ext.push_back("mp4"); void* opaque = nullptr; while ( auto format = av_muxer_iterate(&opaque) ) { if ( format_skip(format) ) continue; out_ext += QString(format->extensions).remove(' ').split(',', Qt::SkipEmptyParts); } std::sort(out_ext.begin(), out_ext.end()); } QStringList glaxnimate::video::VideoFormat::extensions(Direction) const { if ( out_ext.empty() ) get_formats(); return out_ext; } bool glaxnimate::video::VideoFormat::on_save(QIODevice& dev, const QString& name, model::Composition* comp, const QVariantMap& settings) { try { av::Logger logger(this, settings["verbose"].toBool() ? AV_LOG_INFO : AV_LOG_WARNING); auto filename = name.toUtf8(); // allocate the output media context AVFormatContext *oc; avformat_alloc_output_context2(&oc, nullptr, nullptr, filename.data()); if ( !oc ) { warning(i18n("Could not deduce output format from file extension: using MPEG.")); avformat_alloc_output_context2(&oc, nullptr, "mpeg", filename.data()); if ( !oc ) { error(i18n("Could not find output format")); return false; } } // see https://libav.org/documentation/doxygen/master/group__metadata__api.html av::DictWrapper metadata(&oc->metadata); auto document = comp->document(); metadata["title"] = comp->name.get(); if ( !document->info().author.isEmpty() ) metadata["artist"] = document->info().author; if ( !document->info().description.isEmpty() ) metadata["comment"] = document->info().description; for ( auto it = document->metadata().begin(); it != document->metadata().end(); ++it ) metadata[it.key()] = it->toString(); av::CGuard guard(&avformat_free_context, oc); // Add the audio and video streams using the given (or default) // format codecs and initialize the codecs. AVCodecID codec_id = oc->oformat->video_codec; if ( codec_id == AV_CODEC_ID_NONE ) { error(i18n("No video codec")); return false; } // Options av::Dict opt; opt["crf"] = 23; if ( codec_id == AV_CODEC_ID_H264 ) { opt["profile"] = "high"; opt["preset"] = "veryslow"; opt["tune"] = "animation"; } for ( auto it = settings.begin(); it != settings.end(); ++it ) { if ( it.key().startsWith("ffmpeg:") ) { auto value = it->toString(); opt[it.key().mid(7)] = value; } } // Now that all the parameters are set, we can open the audio and // video codecs and allocate the necessary encode buffers. int width = settings["width"].toInt(); if ( width == 0 ) width = comp->width.get(); int height = settings["height"].toInt(); if ( height == 0 ) height = comp->height.get(); int fps = qRound(comp->fps.get()); av::Video video(oc, opt, codec_id, 7000000, width, height, fps); // log format info av_dump_format(oc, 0, filename.constData(), 1); // open the output file, if needed av::DeviceIo io(&dev); oc->pb = io.context(); // Write the stream header, if any int ret = avformat_write_header(oc, opt.dict()); if ( ret < 0 ) { error(i18n("Error occurred when opening output file: %1", av::err2str(ret))); return false; } auto first_frame = comp->animation->first_frame.get(); auto last_frame = comp->animation->last_frame.get(); QColor background = settings["background"].value(); Q_EMIT progress_max_changed(last_frame - first_frame); for ( int i = first_frame; i < last_frame; i++ ) { video.write_video_frame(comp->render_image(i, {width, height}, background)); Q_EMIT progress(i - first_frame); } video.flush(); // Write the trailer, if any. The trailer must be written before you // close the CodecContexts open when you wrote the header; otherwise // av_write_trailer() may try to use memory that was freed on // av_codec_close(). av_write_trailer(oc); return true; } catch ( const av::Error& e ) { error(e.message()); return false; } } std::unique_ptr glaxnimate::video::VideoFormat::save_settings(model::Composition* comp) const { return std::make_unique(glaxnimate::settings::SettingList{ // slug label description default min max glaxnimate::settings::Setting{"background", i18n("Background"), i18n("Background color"), QColor(0, 0, 0, 0)}, glaxnimate::settings::Setting{"width", i18n("Width"), i18n("If not 0, it will overwrite the size"), int(comp->width.get()), 0, 99999}, glaxnimate::settings::Setting{"height", i18n("Height"), i18n("If not 0, it will overwrite the size"), int(comp->height.get()), 0, 99999}, glaxnimate::settings::Setting{"verbose", i18n("Verbose"), i18n("Show verbose information on the conversion"), false}, }); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/svg/detail.cpp000664 001750 001750 00000004064 15165022620 031605 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/svg/detail.hpp" const std::map glaxnimate::io::svg::detail::xmlns = { {"osb", "http://www.openswatchbook.org/uri/2009/osb"}, {"dc", "http://purl.org/dc/elements/1.1/"}, {"cc", "http://creativecommons.org/ns#"}, {"rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"}, {"svg", "http://www.w3.org/2000/svg"}, {"sodipodi", "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"}, {"inkscape", "http://www.inkscape.org/namespaces/inkscape"}, {"xlink", "http://www.w3.org/1999/xlink"}, {"android", "http://schemas.android.com/apk/res/android"}, {"aapt", "http://schemas.android.com/aapt"}, }; const std::unordered_set glaxnimate::io::svg::detail::css_atrrs = { "fill", "alignment-baseline", "baseline-shift", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-rendering", "cursor", "direction", "display", "dominant-baseline", "fill-opacity", "fill-rule", "filter", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "glyph-orientation-horizontal", "glyph-orientation-vertical", "image-rendering", "letter-spacing", "lighting-color", "marker-end", "marker-mid", "marker-start", "mask", "mask-type", "opacity", "overflow", "paint-order", "pointer-events", "shape-rendering", "stop-color", "stop-opacity", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-anchor", "text-decoration", "text-overflow", "text-rendering", "unicode-bidi", "vector-effect", "visibility", "white-space", "word-spacing", "writing-mode" }; mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/lottie/lottie_exporter.hpp000664 001750 001750 00000070011 15165022620 034274 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include "glaxnimate/io/lottie/cbor_write_json.hpp" #include "glaxnimate/io/lottie/lottie_private_common.hpp" #include "glaxnimate/model/animation/join_animatables.hpp" #include "glaxnimate/app_info.hpp" #include "glaxnimate/io/utils.hpp" namespace glaxnimate::io::lottie::detail { inline QLatin1String operator ""_l(const char* c, std::size_t sz) { return QLatin1String(c, sz); } // inline QString debug_value(QCborValue v) // { // QCborMap map; // map["v"_l] = v; // return QString(cbor_write_json(map, false)); // } class LottieExporterState { static constexpr const char* version = "5.13.0"; public: explicit LottieExporterState(ImportExport* format, model::Composition* comp, bool strip, bool strip_raster, const QVariantMap& settings ) : format(format), main(comp), document(comp->document()), strip(strip), strip_raster( strip_raster ), auto_embed(settings["auto_embed"].toBool()), duplicate_masks(settings["duplicate_masks"].toBool()) {} QCborMap to_json() { return convert_main(main); } void convert_animation_container(model::AnimationContainer* animation, QCborMap& json) { json["ip"_l] = animation->first_frame.get(); json["op"_l] = animation->last_frame.get(); } void convert_composition(model::Composition* composition, QCborMap& json) { QCborArray layers; for ( const auto& layer : composition->shapes ) { if ( !strip || layer->visible.get() ) { wrap_to_layer.clear(); shape_is_layer(layer.get()); convert_as_layer(layer.get(), layers, nullptr, true, {}, composition->animation.get()); } } json["layers"_l] = layers; } QCborMap convert_main(model::Composition* animation) { layer_indices.clear(); QCborMap json; json["v"_l] = version; convert_animation_container(animation->animation.get(), json); convert_object_basic(animation, json); json["assets"_l] = convert_assets(animation); convert_composition(animation, json); if ( !strip ) convert_meta(json); return json; } void convert_meta(QCborMap& json) { QCborMap meta; meta["g"_l] = QString("%1 %2").arg(AppInfo::instance().name(), AppInfo::instance().version()); if ( !document->info().description.isEmpty() ) meta["d"_l] = document->info().description; if ( !document->info().author.isEmpty() ) meta["a"_l] = document->info().author; if ( !document->info().keywords.isEmpty() ) { QCborArray k; for ( const auto& kw : document->info().keywords ) k.push_back(kw); meta["k"_l] = k; } json["meta"_l] = meta; } int layer_index(model::DocumentNode* layer) { if ( !layer ) return -1; if ( !layer_indices.contains(layer->uuid.get()) ) return generate_layer_index(layer); return layer_indices[layer->uuid.get()]; } int generate_layer_index(model::DocumentNode* layer) { int index = next_index++; layer_indices[layer->uuid.get()] = index; return index; } struct MatteData { int type; int parent; MatteData() : type(-1), parent(-1) {}; bool has_matte() const { return parent != -1 && type != -1; } }; QCborMap convert_as_layer( model::ShapeElement* shape, QCborArray& output, model::ShapeElement* parent, bool push, const MatteData& matte, model::AnimationContainer* animation ) { QCborMap json; json["ddd"_l] = 0; int index = layer_index(shape); json["ind"_l] = index; json["st"_l] = 0; json["ty"_l] = 3; if ( !shape->visible.get() ) { if ( strip ) return {}; json["hd"_l] = true; } if ( !strip ) { json["nm"_l] = shape->name.get(); json["uid"_l] = shape->uuid.get().toString(); } if ( parent ) json["parent"_l] = layer_index(parent); if ( matte.has_matte() ) { json["tt"_l] = matte.type; json["tp"_l] = matte.parent; } if ( auto layer = shape->cast() ) { if ( !layer->render.get() ) return {}; convert_normal_layer(layer, json, output, push); if ( layer->mask->has_mask() ) push = false; } else if ( auto image = shape->cast() ) { convert_image_layer(image, json, animation); } else if ( auto precomp = shape->cast() ) { convert_precomp_layer(precomp, json, animation); } else if ( auto group = shape->cast() ) { group_as_layer(group, json, animation, output); } else { shape_as_layer(shape, json, animation); } if ( push ) output.push_front(json); return json; } void convert_normal_layer(model::Layer* layer, QCborMap& json, QCborArray& output, bool push) { auto animation = layer->animation.get(); convert_animation_container(animation, json); convert_object_properties(layer, fields["__Layer__"], json); convert_composable(layer, json); if ( layer->shapes.empty() ) { return; } bool all_shapes = !contains_layer.count(layer); if ( all_shapes && !layer->mask->has_mask() ) { json["ty"_l] = 4; json["shapes"_l] = convert_shapes(layer->shapes, false); } else { model::ShapeElement* matte_obj = nullptr; QCborMap mask; int i = 0; MatteData matte; if ( layer->mask->has_mask() && !layer->shapes.empty() ) { if ( layer->shapes[0]->visible.get() ) { matte_obj = layer->shapes[0]; mask = convert_as_layer(matte_obj, output, layer, false, {}, animation); if ( !mask.isEmpty() ) { mask["td"_l] = 1; // mask["hd"_l] = 1; matte.parent = mask["ind"_l].toInteger(); matte.type = convert_matte_type(layer->mask.get()); } } i = 1; } if ( all_shapes ) { json["ty"_l] = 4; json["tt"_l] = matte.type; json["tp"_l] = matte.parent; json["shapes"_l] = convert_shapes(layer->shapes, false, 1); if ( push ) output.push_front(json); } else { if ( push ) output.push_front(json); for ( ; i < layer->shapes.size(); i++ ) { convert_as_layer(layer->shapes[i], output, layer, true, matte, animation); if ( duplicate_masks && matte_obj ) { matte.parent = generate_layer_index(matte_obj); output.push_front(mask); } } } if ( matte_obj && (!duplicate_masks || layer->shapes.size() == 1) ) output.push_front(mask); } } void group_as_layer(model::Group* group, QCborMap& json, model::AnimationContainer* animation, QCborArray& output) { if ( contains_layer.count(group) ) { convert_composable(group, json); for ( const auto& child : group->shapes ) convert_as_layer(child.get(), output, group, true, {}, animation); } else { shape_as_layer(group, json, animation); } } void shape_as_layer(model::ShapeElement* shape, QCborMap& json, model::AnimationContainer* animation) { convert_animation_container(animation, json); json["ty"_l] = 4; if ( auto grp = shape->cast() ) { convert_composable(grp, json); json["shapes"_l] = convert_shapes(grp->shapes, false); } else { json["ks"_l] = QCborMap(); QCborArray shapes; shapes.push_back(convert_shape(shape, false)); json["shapes"_l] = shapes; } } enum class LayerType { Shape, Layer, Image, PreComp }; LayerType layer_type(model::ShapeElement* shape) { auto meta = shape->metaObject(); if ( meta->inherits(&model::Layer::staticMetaObject) ) return LayerType::Layer; if ( meta->inherits(&model::Image::staticMetaObject) ) return LayerType::Image; if ( meta->inherits(&model::PreCompLayer::staticMetaObject) ) return LayerType::PreComp; return LayerType::Shape; } void convert_composable(model::Composable* grp, QCborMap& json) { convert_object_properties(grp, fields["Composable"], json); QCborMap transform; convert_transform(grp->transform.get(), &grp->opacity, transform); json["ks"_l] = transform; if ( grp->transform->auto_orient.get() ) json["ao"_l] = 1; if ( grp->blend_mode.get() != renderer::BlendMode::Normal ) json["bm"_l] = int(grp->blend_mode.get()); } int convert_matte_type(model::MaskSettings* mask) { switch ( mask->mask.get() ) { case model::MaskSettings::NoMask: return 0; case model::MaskSettings::Alpha: return mask->inverted.get() ? 2 : 1; case model::MaskSettings::Luma: return mask->inverted.get() ? 4 : 3; } return -1; } void convert_transform(model::Transform* tf, model::AnimatedPropertyBase* opacity, QCborMap& json) { convert_object_basic(tf, json); if ( opacity ) json["o"_l] = convert_animated(opacity, FloatMult(100)); else json["o"_l] = fake_animated(100); } QCborArray point_to_lottie(const QPointF& vv) { return QCborArray{vv.x(), vv.y()}; } QCborValue value_from_variant(const QVariant& v) { switch ( v.userType() ) { case QMetaType::QPointF: return point_to_lottie(v.toPointF()); case QMetaType::QVector2D: { auto vv = v.value() * 100; return QCborArray{vv.x(), vv.y()}; } case QMetaType::QSizeF: { auto vv = v.toSizeF(); return QCborArray{vv.width(), vv.height()}; } case QMetaType::QColor: { auto vv = v.value().toRgb(); return QCborArray{vv.redF(), vv.greenF(), vv.blueF()}; } case QMetaType::QUuid: return v.toString(); } if ( v.userType() == qMetaTypeId() ) { math::bezier::Bezier bezier = v.value(); QCborMap jsbez; jsbez["c"_l] = bezier.closed(); QCborArray pos, tan_in, tan_out; for ( const auto& p : bezier ) { pos.push_back(point_to_lottie(p.pos)); tan_in.push_back(point_to_lottie(p.tan_in - p.pos)); tan_out.push_back(point_to_lottie(p.tan_out - p.pos)); } jsbez["v"_l] = pos; jsbez["i"_l] = tan_in; jsbez["o"_l] = tan_out; return jsbez; } else if ( v.userType() == qMetaTypeId() ) { return point_to_lottie(v.value().pos); } else if ( v.userType() == qMetaTypeId() ) { QCborArray weird_ass_representation; auto gradient = v.value(); bool alpha = false; for ( const auto& stop : gradient ) { weird_ass_representation.push_back(stop.first); weird_ass_representation.push_back(stop.second.redF()); weird_ass_representation.push_back(stop.second.greenF()); weird_ass_representation.push_back(stop.second.blueF()); alpha = alpha || stop.second.alpha() != 0; } if ( alpha ) { for ( const auto& stop : gradient ) { weird_ass_representation.push_back(stop.first); weird_ass_representation.push_back(stop.second.alphaF()); } } return weird_ass_representation; } else if ( v.userType() >= QMetaType::User && v.canConvert() ) { return v.toInt(); } return QCborValue::fromVariant(v); } void convert_object_from_meta(model::Object* obj, const QMetaObject* mo, QCborMap& json_obj) { if ( auto super = mo->superClass() ) convert_object_from_meta(obj, super, json_obj); auto it = fields.find(model::detail::naked_type_name(mo)); if ( it != fields.end() ) convert_object_properties(obj, *it, json_obj); } void convert_object_basic(model::Object* obj, QCborMap& json_obj) { convert_object_from_meta(obj, obj->metaObject(), json_obj); } void convert_object_properties(model::Object* obj, const QVector& fields, QCborMap& json_obj) { for ( const auto& field : fields ) { if ( field.mode != Auto || (strip && !field.essential) ) continue; model::BaseProperty * prop = obj->get_property(field.name); if ( !prop ) { logger.stream() << field.name << "is not a property"; continue; } if ( prop->traits().flags & model::PropertyTraits::Animated ) { json_obj[field.lottie] = convert_animated(static_cast(prop), field.transform); } else { json_obj[field.lottie] = value_from_variant(field.transform.to_lottie(prop->value(), 0)); } } } QCborValue keyframe_value_from_variant(const QVariant& v) { auto cb = value_from_variant(v); if ( cb.isArray() ) return cb; return QCborArray{cb}; } void populate_keyframe(QCborMap& jkf, const QVariant& value, model::FrameTime time, const model::KeyframeTransition& transition, bool is_last) { QCborValue kf_value = keyframe_value_from_variant(value); jkf["t"_l] = time; jkf["s"_l] = kf_value; if ( !is_last ) { if ( transition.hold() ) { jkf["h"_l] = 1; } else { jkf["h"_l] = 0; jkf["o"_l] = keyframe_bezier_handle(transition.before()); jkf["i"_l] = keyframe_bezier_handle(transition.after()); } } } QCborMap convert_animated( model::AnimatedPropertyBase* prop, const TransformFunc& transform_values ) { return convert_animated(prop, transform_values, prop->traits().type == model::PropertyTraits::Point); } QCborMap convert_animated( model::JoinedAnimatable* prop, const TransformFunc& transform_values ) { return convert_animated(prop, transform_values, false); } QCborMap convert_animated( model::AnimatableBase* prop, const TransformFunc& transform_values, bool position ) { QCborMap jobj; if ( prop->keyframe_count() > 1 ) { jobj["a"_l] = 1; std::vector> split_kfs = split_keyframes(prop); QCborArray keyframes; QCborMap jkf; for ( int i = 0, e = split_kfs.size(); i < e; i++ ) { auto kf = split_kfs[i].get(); QVariant v = transform_values.to_lottie(kf->value(), kf->time()); if ( i != 0 ) { if ( position ) { auto pkf = static_cast*>(kf); jkf["ti"_l] = point_to_lottie(pkf->point().tan_in - pkf->get()); } keyframes.push_back(jkf); } jkf.clear(); populate_keyframe(jkf, v, kf->time(), kf->transition(), i == e - 1); if ( position ) { auto pkf = static_cast*>(kf); jkf["to"_l] = point_to_lottie(pkf->point().tan_out - pkf->get()); } } if ( position ) jkf.remove("to"_l); keyframes.push_back(jkf); jobj["k"_l] = keyframes; } else { jobj["a"_l] = 0; QVariant v = transform_values.to_lottie(prop->static_value(), 0); jobj["k"_l] = value_from_variant(v); } return jobj; } QCborMap keyframe_bezier_handle(const QPointF& p) { QCborMap jobj; QCborArray x; x.push_back(p.x()); QCborArray y; y.push_back(p.y()); jobj["x"_l] = x; jobj["y"_l] = y; return jobj; } static std::pair radial_highlight(const QPointF& s, const QPointF& e, const QPointF& h) { auto de = e - s; auto dh = h - s; auto dist = math::hypot(de.x(), de.y()); qreal val_h = qFuzzyIsNull(dist) ? 0 : math::hypot(dh.x(), dh.y()) / dist * 100; qreal val_a = math::rad2deg(math::atan2(dh.y(), dh.x()) - math::atan2(de.y(), de.x())); return {val_h, val_a}; } void convert_styler(model::Styler* shape, QCborMap& jsh) { auto used = shape->use.get(); auto gradient = qobject_cast(used); if ( !gradient || !gradient->colors.get() ) { auto color_prop = &shape->color; if ( auto color = qobject_cast(used) ) color_prop = &color->color; jsh["c"_l] = convert_animated(color_prop, {}); auto join_func = [](const std::vector& args) -> QVariant { return args[0].value().alphaF() * args[1].toFloat() * 100; }; model::JoinedAnimatable join({color_prop, &shape->opacity}, join_func); jsh["o"_l] = convert_animated(&join, {}); return; } convert_object_basic(gradient, jsh); if ( shape->type_name() == "Fill" ) jsh["ty"_l] = "gf"; else jsh["ty"_l] = "gs"; if ( gradient->type.get() == model::Gradient::Radial ) { model::JoinAnimatables highlight({&gradient->start_point, &gradient->end_point, &gradient->highlight}); QCborMap prop_a; QCborMap prop_h; if ( highlight.animated() ) { prop_a["a"_l] = 1; prop_h["a"_l] = 1; QCborArray k_a; QCborArray k_h; for ( std::size_t i = 0; i < highlight.keyframes().size(); i++ ) { const auto& kf = highlight.keyframes()[i]; QCborMap kf_a; QCborMap kf_h; qreal val_h, val_a; std::tie(val_h, val_a) = radial_highlight(kf.values[0].toPointF(), kf.values[1].toPointF(), kf.values[2].toPointF()); populate_keyframe(kf_h, val_h, kf.time, kf.transition(), i + 1 == highlight.keyframes().size()); populate_keyframe(kf_a, val_a, kf.time, kf.transition(), i + 1 == highlight.keyframes().size()); k_a.push_back(kf_a); k_h.push_back(kf_h); } prop_a["k"_l] = k_a; prop_h["k"_l] = k_h; } else { prop_a["a"_l] = 0; prop_h["a"_l] = 0; qreal val_h, val_a; std::tie(val_h, val_a) = radial_highlight(gradient->start_point.get(), gradient->end_point.get(), gradient->highlight.get()); prop_a["k"_l] = val_a; prop_h["k"_l] = val_h; } jsh["a"_l] = prop_a; jsh["h"_l] = prop_h; } auto colors = gradient->colors.get(); QCborMap jcolors; jcolors["p"_l] = colors->colors.get().size(); jcolors["k"_l] = convert_animated(&colors->colors, {}); jsh["g"_l] = jcolors; } QCborMap convert_shape(model::ShapeElement* shape, bool force_hidden) { if ( auto text = shape->cast() ) { auto conv = text->to_path(); return convert_shape(conv.get(), force_hidden || !shape->visible.get()); } QCborMap jsh; jsh["ty"_l] = shape_types[shape->type_name()]; // jsh["d"] = 0; if ( force_hidden || !shape->visible.get() ) jsh["hd"_l] = true; convert_object_basic(shape, jsh); if ( auto gr = qobject_cast(shape) ) { auto shapes = convert_shapes(gr->shapes, force_hidden || !gr->visible.get()); QCborMap transform; transform["ty"_l] = "tr"; convert_transform(gr->transform.get(), &gr->opacity, transform); shapes.push_back(transform); jsh["it"_l] = shapes; } else if ( auto styler = shape->cast() ) { convert_styler(styler, jsh); } else if ( auto polystar = shape->cast() ) { if ( polystar->type.get() == model::PolyStar::Polygon ) { jsh.remove("is"_l); jsh.remove("ir"_l); } } else if ( auto styler = shape->cast() ) { QCborMap transform; convert_transform(styler->transform.get(), nullptr, transform); transform.remove("o"_l); transform["so"_l] = convert_animated(&styler->start_opacity, FloatMult(100)); transform["eo"_l] = convert_animated(&styler->end_opacity, FloatMult(100)); jsh["o"_l] = fake_animated(0); jsh["m"_l] = 1; jsh["tr"_l] = transform; } else if ( !shape->is_instance() ) { format->warning(i18n("%1 is an unsupported shape of type %2", shape->object_name(), shape->type_name_human())); } return jsh; } QCborMap fake_animated(const QCborValue& val) { QCborMap fake; fake["a"_l] = 0; fake["k"_l] = val; return fake; } QCborArray convert_shapes(const model::ShapeListProperty& shapes, bool force_hidden, int skip = 0) { QCborArray jshapes; for ( const auto& shape : shapes ) { if ( skip > 0 ) { --skip; continue; } if ( shape->is_instance() ) format->warning(i18n("Images cannot be grouped with other shapes, they must be inside a layer")); else if ( shape->is_instance() ) format->warning(i18n("Composition layers cannot be grouped with other shapes, they must be inside a layer")); else if ( !strip || shape->visible.get() ) jshapes.push_front(convert_shape(shape.get(), force_hidden)); } return jshapes; } QCborArray convert_assets(model::Composition* animation) { QCborArray assets; if ( !strip_raster ) { for ( const auto& bmp : document->assets()->images->values ) { if ( auto_embed && !bmp->embedded() ) { auto clone = bmp->clone_covariant(); clone->embed(true); assets.push_back(convert_bitmat(clone.get())); } else { assets.push_back(convert_bitmat(bmp.get())); } } } for ( const auto& comp : document->assets()->compositions->values ) { if ( comp.get() != animation ) assets.push_back(convert_precomp(comp.get())); } return assets; } QCborMap convert_bitmat(model::Bitmap* bmp) { QCborMap out; convert_object_basic(bmp, out); out["id"_l] = bmp->uuid.get().toString(); out["e"_l] = int(bmp->embedded()); if ( bmp->embedded() ) { out["u"_l] = ""; out["p"_l] = bmp->to_url().toString(); } else { auto finfo = bmp->file_info(); out["u"_l] = finfo.absolutePath(); out["p"_l] = finfo.fileName(); } return out; } void convert_image_layer(model::Image* image, QCborMap& json, model::AnimationContainer* animation) { convert_animation_container(animation, json); if ( !strip_raster ) json["ty"_l] = 2; convert_composable(image, json); if ( !strip_raster && image->image.get() ) json["refId"_l] = image->image->uuid.get().toString(); } QCborMap convert_precomp(model::Composition* comp) { QCborMap out; convert_object_basic(comp, out); out["id"_l] = comp->uuid.get().toString(); convert_composition(comp, out); return out; } void convert_precomp_layer(model::PreCompLayer* layer, QCborMap& json, model::AnimationContainer* animation) { json["ty"_l] = 0; json["ind"_l] = layer_index(layer); convert_animation_container(animation, json); json["st"_l] = layer->timing->start_time.get(); json["sr"_l] = layer->timing->stretch.get(); convert_composable(layer, json); if ( layer->composition.get() ) json["refId"_l] = layer->composition->uuid.get().toString(); json["w"_l] = layer->size.get().width(); json["h"_l] = layer->size.get().height(); } bool group_is_layer(model::Group* shape) { bool force_layer = false; if ( shape->transform->auto_orient.get() ) force_layer = true; if ( shape->cast() ) force_layer = true; bool child_layer = false; for ( const auto& child : shape->shapes ) if ( shape_is_layer(child.get()) ) child_layer = true; if ( child_layer ) { force_layer = true; contains_layer.insert(shape); } if ( force_layer ) wrap_to_layer.insert(shape); return force_layer; } bool shape_is_layer(model::ShapeElement* shape) { if ( shape->cast() ) { if ( auto g = shape->cast() ) return group_is_layer(g); return true; } return false; } ImportExport* format; model::Composition* main; model::Document* document; bool strip; QMap layer_indices; int next_index = 1; log::Log logger{"Lottie Export"}; model::Layer* mask = nullptr; bool strip_raster; bool auto_embed; bool duplicate_masks = false; std::unordered_set wrap_to_layer; std::unordered_set contains_layer; }; } // namespace glaxnimate::io::lottie::detail mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/model/transform.cpp000664 001750 001750 00000004264 15165022620 032252 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/model/transform.hpp" #include "glaxnimate/math/math.hpp" namespace { QTransform make_transform( const QPointF& anchor_point, const QPointF& position, double rotation, QVector2D scale, const std::optional& pos_derivative ) { QTransform trans; trans.translate(position.x(), position.y()); trans.rotate(rotation); if ( pos_derivative ) trans.rotate(glaxnimate::math::rad2deg(glaxnimate::math::atan2(pos_derivative->y(), pos_derivative->x()))); trans.scale(scale.x(), scale.y()); trans.translate(-anchor_point.x(), -anchor_point.y()); return trans; } } // namespace GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Transform) QTransform glaxnimate::model::Transform::transform_matrix(FrameTime f) const { return transform_matrix_with_anchor(f, anchor_point.get_at(f)); } QTransform glaxnimate::model::Transform::transform_matrix_with_anchor(FrameTime f, const QPointF& anchor) const { std::optional pos_derivative; if ( auto_orient.get() ) pos_derivative = position.derivative_at(f); return make_transform( anchor, position.get_at(f), rotation.get_at(f), scale.get_at(f), pos_derivative ); } void glaxnimate::model::Transform::set_transform_matrix(const QTransform& t) { qreal a = t.m11(); qreal b = t.m12(); qreal c = t.m21(); qreal d = t.m22(); qreal tx = t.m31(); qreal ty = t.m32(); position.set(QPointF(tx, ty)); qreal delta = a * d - b * c; qreal sx = 1; qreal sy = 1; if ( a != 0 || b != 0 ) { qreal r = math::hypot(a, b); rotation.set(-math::rad2deg(-math::sign(b) * math::acos(a/r))); sx = r; sy = delta / r; } else { qreal r = math::hypot(c, d); rotation.set(-math::rad2deg(math::pi / 2 + math::sign(d) * math::acos(c / r))); sx = delta / r; sy = r; } scale.set(QVector2D(sx, sy)); } void glaxnimate::model::Transform::copy(glaxnimate::model::Transform* other) { other->clone_into(this); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/settings/setting.hpp000664 001750 001750 00000007320 15165022620 032455 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include "glaxnimate/utils/i18n.hpp" namespace glaxnimate::settings { struct Setting { enum Type { Internal, Info, Bool, Int, Float, String, Color, }; Setting(QString slug, QString label, QString description) : type(Info), slug(std::move(slug)), label(std::move(label)), description(std::move(description)) {} Setting(QString slug, QString label, QString description, bool default_value) : type(Bool), slug(std::move(slug)), label(std::move(label)), description(std::move(description)), default_value(default_value) {} Setting(QString slug, QString label, QString description, int default_value, int min, int max) : type(Int), slug(std::move(slug)), label(std::move(label)), description(std::move(description)), default_value(default_value), min(min), max(max) {} Setting(QString slug, QString label, QString description, float default_value, float min, float max) : type(Float), slug(std::move(slug)), label(std::move(label)), description(std::move(description)), default_value(default_value), min(min), max(max) {} Setting(QString slug, QString label, QString description, const QString& default_value) : type(String), slug(std::move(slug)), label(std::move(label)), description(std::move(description)), default_value(default_value) {} Setting(QString slug, QString label, QString description, Type type, QVariant default_value, QVariantMap choices = {}, std::function side_effects = {} ) : type(type), slug(std::move(slug)), label(std::move(label)), description(std::move(description)), default_value(std::move(default_value)), choices(std::move(choices)), side_effects(std::move(side_effects)) {} Setting(QString slug, QString label, QString description, const QColor& default_value) : type(Color), slug(std::move(slug)), label(std::move(label)), description(std::move(description)), default_value(QVariant::fromValue(default_value)) {} QVariant get_variant(const QVariantMap& map) const { auto it = map.find(slug); if ( it != map.end() && valid_variant(*it) ) return *it; return default_value; } template CastType get(const QVariantMap& map) const { return get_variant(map).value(); } bool valid_variant(const QVariant& v) const { switch ( type ) { case Info: case Internal: return true; case Bool: return v.canConvert(); case Int: return v.canConvert(); case Float: return v.canConvert(); case String: return v.canConvert(); case Color: return v.canConvert(); default: return false; } } Type type; QString slug; QString label; QString description; QVariant default_value; float min = -1; float max = -1; QVariantMap choices; std::function side_effects; }; using SettingList = std::vector; } // namespace glaxnimate::settings mlt-7.38.0/src/modules/glaxnimate/glaxnimate/src/core/glaxnimate/io/binary_stream.cpp000664 001750 001750 00000006160 15165022620 032402 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "glaxnimate/io/binary_stream.hpp" #include #include glaxnimate::io::BinaryInputStream::BinaryInputStream(QIODevice* file): BinaryInputStream(file->readAll()) { } glaxnimate::io::BinaryInputStream::BinaryInputStream(QByteArray data): data(std::move(data)), data_start(this->data.data()), data_end(data_start + this->data.size()) { } void glaxnimate::io::BinaryInputStream::on_overflow() { error = true; } bool glaxnimate::io::BinaryInputStream::eof() const { return data_start >= data_end; } bool glaxnimate::io::BinaryInputStream::has_error() const { return error; } QByteArray glaxnimate::io::BinaryInputStream::read(qint64 max_size) { if ( data_start + max_size < data_end ) { data_start += max_size; return QByteArray(data_start - max_size, max_size); } on_overflow(); return {}; } quint8 glaxnimate::io::BinaryInputStream::next() { if ( data_start < data_end ) { ++data_start; return data_start[-1]; } on_overflow(); return {}; } quint32 glaxnimate::io::BinaryInputStream::read_uint32_le() { static_assert(sizeof(quint32) == 4); auto data = read(4); if ( data.size() == 4 ) { return qFromLittleEndian(data.data()); } else { return 0; } } glaxnimate::io::Float32 glaxnimate::io::BinaryInputStream::read_float32_le() { static_assert(sizeof(Float32) == 4); auto data = read(4); if ( data.size() == 4 ) { return qFromLittleEndian(data.data()); } else { on_overflow(); return 0; } } glaxnimate::io::VarUint glaxnimate::io::BinaryInputStream::read_uint_leb128() { VarUint result = 0; VarUint shift = 0; while (true) { quint8 byte = next(); if ( error ) return 0; result |= VarUint(byte & 0x7f) << shift; if ( !(byte & 0x80) ) return result; shift += 7; } } glaxnimate::io::BinaryOutputStream::BinaryOutputStream(QIODevice* file) : file(file) { } void glaxnimate::io::BinaryOutputStream::write(const QByteArray& data) { file->write(data); } void glaxnimate::io::BinaryOutputStream::write_byte(quint8 v) { file->putChar(v); } void glaxnimate::io::BinaryOutputStream::write_float32_le(glaxnimate::io::Float32 v) { static_assert(sizeof(Float32) == 4); std::array data; qToLittleEndian(v, data.data()); file->write((const char*)data.data(), 4); } void glaxnimate::io::BinaryOutputStream::write_uint32_le(quint32 v) { static_assert(sizeof(quint32) == 4); std::array data; qToLittleEndian(v, data.data()); file->write((const char*)data.data(), 4); } void glaxnimate::io::BinaryOutputStream::write_uint_leb128(glaxnimate::io::VarUint v) { while ( true ) { quint8 byte = v & 0x7f; v >>= 7; if ( v == 0 ) { write_byte(byte); break; } write_byte(byte | 0x80); } } lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-bigint64array.inc.h000664 001750 001750 00000001722 15164251010 053464 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * BigInt64Array description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 8 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_BIGINT64_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_BIGINT64ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-dataview-prototype.inc.h000664 001750 001750 00000007224 15164251010 052465 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * DataView.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_DATAVIEW /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 24.2.3 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_DATAVIEW, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.2.4.21 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_DATAVIEW_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_GET_FLOAT_32_UL, ECMA_DATAVIEW_PROTOTYPE_GET_FLOAT32, 2, 1) #if JERRY_NUMBER_TYPE_FLOAT64 ROUTINE (LIT_MAGIC_STRING_GET_FLOAT_64_UL, ECMA_DATAVIEW_PROTOTYPE_GET_FLOAT64, 2, 1) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ ROUTINE (LIT_MAGIC_STRING_GET_INT8_UL, ECMA_DATAVIEW_PROTOTYPE_GET_INT8, 1, 1) ROUTINE (LIT_MAGIC_STRING_GET_INT16_UL, ECMA_DATAVIEW_PROTOTYPE_GET_INT16, 2, 1) ROUTINE (LIT_MAGIC_STRING_GET_INT32_UL, ECMA_DATAVIEW_PROTOTYPE_GET_INT32, 2, 1) ROUTINE (LIT_MAGIC_STRING_GET_UINT8_UL, ECMA_DATAVIEW_PROTOTYPE_GET_UINT8, 2, 1) ROUTINE (LIT_MAGIC_STRING_GET_UINT16_UL, ECMA_DATAVIEW_PROTOTYPE_GET_UINT16, 2, 1) ROUTINE (LIT_MAGIC_STRING_GET_UINT32_UL, ECMA_DATAVIEW_PROTOTYPE_GET_UINT32, 2, 1) ROUTINE (LIT_MAGIC_STRING_SET_FLOAT_32_UL, ECMA_DATAVIEW_PROTOTYPE_SET_FLOAT32, 2, 2) #if JERRY_NUMBER_TYPE_FLOAT64 ROUTINE (LIT_MAGIC_STRING_SET_FLOAT_64_UL, ECMA_DATAVIEW_PROTOTYPE_SET_FLOAT64, 2, 2) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ ROUTINE (LIT_MAGIC_STRING_SET_INT8_UL, ECMA_DATAVIEW_PROTOTYPE_SET_INT8, 1, 2) ROUTINE (LIT_MAGIC_STRING_SET_INT16_UL, ECMA_DATAVIEW_PROTOTYPE_SET_INT16, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_INT32_UL, ECMA_DATAVIEW_PROTOTYPE_SET_INT32, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_UINT8_UL, ECMA_DATAVIEW_PROTOTYPE_SET_UINT8, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_UINT16_UL, ECMA_DATAVIEW_PROTOTYPE_SET_UINT16, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_UINT32_UL, ECMA_DATAVIEW_PROTOTYPE_SET_UINT32, 2, 2) #if JERRY_BUILTIN_BIGINT ROUTINE (LIT_MAGIC_STRING_GET_BIGINT64, ECMA_DATAVIEW_PROTOTYPE_GET_BIGINT64, 2, 1) ROUTINE (LIT_MAGIC_STRING_GET_BIGUINT64, ECMA_DATAVIEW_PROTOTYPE_GET_BIGUINT64, 2, 1) ROUTINE (LIT_MAGIC_STRING_SET_BIGINT64, ECMA_DATAVIEW_PROTOTYPE_SET_BIGINT64, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_BIGUINT64, ECMA_DATAVIEW_PROTOTYPE_SET_BIGUINT64, 2, 2) #endif /* JERRY_BUILTIN_BIGINT */ /* ECMA-262 v6, 24.2.4.1 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BUFFER, ECMA_DATAVIEW_PROTOTYPE_BUFFER_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 24.2.4.2 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BYTE_LENGTH_UL, ECMA_DATAVIEW_PROTOTYPE_BYTE_LENGTH_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 24.2.4.3 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BYTE_OFFSET_UL, ECMA_DATAVIEW_PROTOTYPE_BYTE_OFFSET_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_DATAVIEW */ #include "ecma-builtin-helpers-macro-undefs.inc.h" lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-iterator-prototype.inc.h000664 001750 001750 00000002017 15164251010 053620 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %AsyncIteratorPrototype% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_GLOBAL_SYMBOL_ASYNC_ITERATOR, ECMA_BUILTIN_ASYNC_ITERATOR_PROTOTYPE_OBJECT_ASYNC_ITERATOR, 0, 0) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/error/error.h000664 001750 001750 00000032051 15164251010 037303 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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 literal 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); /////////////////////////////////////////////////////////////////////////////// // ValidateErrorCode //! Error codes when validating. /*! \ingroup RAPIDJSON_ERRORS \see GenericSchemaValidator */ enum ValidateErrorCode { kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set. kValidateErrorNone = 0, //!< No error. kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value. kValidateErrorMaximum, //!< Number is greater than the 'maximum' value. kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value. kValidateErrorMinimum, //!< Number is less than the 'minimum' value. kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value. kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value. kValidateErrorMinLength, //!< String is longer than the 'maxLength' value. kValidateErrorPattern, //!< String does not match the 'pattern' regular expression. kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value. kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value. kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true. kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema. kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value. kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value. kValidateErrorRequired, //!< Object is missing one or more members required by the schema. kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema. kValidateErrorPatternProperties, //!< See other errors. kValidateErrorDependencies, //!< Object has missing property or schema dependencies. kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values. kValidateErrorType, //!< Property has a type that is not allowed by the schema. kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'. kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'. kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading }; //! Function pointer type of GetValidateError(). /*! \ingroup RAPIDJSON_ERRORS This is the prototype for \c GetValidateError_X(), where \c X is a locale. User can dynamically change locale in runtime, e.g.: \code GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode()); \endcode */ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode); /////////////////////////////////////////////////////////////////////////////// // SchemaErrorCode //! Error codes when validating. /*! \ingroup RAPIDJSON_ERRORS \see GenericSchemaValidator */ enum SchemaErrorCode { kSchemaErrorNone = 0, //!< No error. kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer kSchemaErrorRefInvalid, //!< $ref must not be an empty string kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document kSchemaErrorRefCyclical, //!< $ref is cyclical kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties' kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly' }; //! Function pointer type of GetSchemaError(). /*! \ingroup RAPIDJSON_ERRORS This is the prototype for \c GetSchemaError_X(), where \c X is a locale. User can dynamically change locale in runtime, e.g.: \code GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode()); \endcode */ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode); /////////////////////////////////////////////////////////////////////////////// // PointerParseErrorCode //! Error code of JSON pointer 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 }; //! Function pointer type of GetPointerParseError(). /*! \ingroup RAPIDJSON_ERRORS This is the prototype for \c GetPointerParseError_X(), where \c X is a locale. User can dynamically change locale in runtime, e.g.: \code GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode()); \endcode */ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode); RAPIDJSON_NAMESPACE_END #ifdef __clang__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_ERROR_ERROR_H_ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int32array.cpp000664 001750 001750 00000004344 15164251010 052563 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-int32array.inc.h" #define BUILTIN_UNDERSCORED_ID int32array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup int32array ECMA Int32Array object built-in * @{ */ /** * Handle calling [[Call]] of Int32Array * * @return ecma value */ ecma_value_t ecma_builtin_int32array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_INT32_ARRAY_REQUIRES_NEW); } /* ecma_builtin_int32array_dispatch_call */ /** * Handle calling [[Construct]] of Int32Array * * @return ecma value */ ecma_value_t ecma_builtin_int32array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_INT32_ARRAY); } /* ecma_builtin_int32array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-array-object.h000664 001750 001750 00000007652 15164251010 046057 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_ARRAY_OBJECT_H #define ECMA_ARRAY_OBJECT_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaarrayobject ECMA Array object related routines * @{ */ /** * Attributes of fast access mode arrays: * * - The internal property is replaced with a buffer which directly stores the values * - Whenever any operation would change the following attributes of the array it should be converted back to normal * - All properties must be enumerable configurable writable data properties * - The prototype must be Array.prototype * - [[Extensible]] internal property must be true * - 'length' property of the array must be writable * * - The conversion is also required when a property is set if: * - The property name is not an array index * - The new hole count of the array would reach ECMA_FAST_ARRAY_MAX_NEW_HOLES_COUNT */ /** * Maximum number of new array holes in a fast mode access array. * If the number of new holes exceeds this limit, the array is converted back * to normal property list based array. */ #define ECMA_FAST_ARRAY_MAX_NEW_HOLES_COUNT 32 /** * Bitshift index for fast array hole count representation */ #define ECMA_FAST_ARRAY_HOLE_SHIFT 8 /** * This number represents 1 array hole in underlying buffer of a fast access mode array */ #define ECMA_FAST_ARRAY_HOLE_ONE (1 << ECMA_FAST_ARRAY_HOLE_SHIFT) /** * Maximum number of array holes in a fast access mode array */ #define ECMA_FAST_ARRAY_MAX_HOLE_COUNT (1 << 16) ecma_object_t *ecma_op_new_array_object (uint32_t length); ecma_object_t *ecma_op_new_array_object_from_length (ecma_length_t length); ecma_value_t ecma_op_new_array_object_from_buffer (const ecma_value_t *args_p, uint32_t length); ecma_value_t ecma_op_new_array_object_from_collection (ecma_collection_t *collection_p, bool unref_objects); bool ecma_op_object_is_fast_array (ecma_object_t *object_p); bool ecma_op_array_is_fast_array (ecma_extended_object_t *array_p); uint32_t ecma_fast_array_get_hole_count (ecma_object_t *obj_p); ecma_value_t *ecma_fast_array_extend (ecma_object_t *object_p, uint32_t new_length); bool ecma_fast_array_set_property (ecma_object_t *object_p, uint32_t index, ecma_value_t value); bool ecma_array_object_delete_property (ecma_object_t *object_p, ecma_string_t *property_name_p); uint32_t ecma_delete_fast_array_properties (ecma_object_t *object_p, uint32_t new_length); ecma_collection_t *ecma_fast_array_object_own_property_keys (ecma_object_t *object_p, jerry_property_filter_t filter); void ecma_fast_array_convert_to_normal (ecma_object_t *object_p); ecma_object_t *ecma_op_array_species_create (ecma_object_t *original_array_p, ecma_length_t length); ecma_value_t ecma_op_create_array_iterator (ecma_object_t *obj_p, ecma_iterator_kind_t kind); ecma_value_t ecma_op_array_object_set_length (ecma_object_t *object_p, ecma_value_t new_value, uint16_t flags); ecma_value_t ecma_op_array_object_define_own_property (ecma_object_t *object_p, ecma_string_t *property_name_p, const ecma_property_descriptor_t *property_desc_p); uint32_t ecma_array_get_length (ecma_object_t *array_p); ecma_value_t ecma_array_object_to_string (ecma_value_t this_arg); /** * @} * @} */ #endif /* !ECMA_ARRAY_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/webp/000775 001750 001750 00000000000 15164251010 033327 5ustar00ddennedyddennedy000000 000000 thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-dataview-object.cpp000664 001750 001750 00000027576 15164251010 047107 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-dataview-object.h" #include "ecma-arraybuffer-object.h" #include "ecma-bigint.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-shared-arraybuffer-object.h" #include "ecma-typedarray-object.h" #include "jcontext.h" #if JERRY_BUILTIN_DATAVIEW /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmadataviewobject ECMA builtin DataView helper functions * @{ */ /** * Handle calling [[Construct]] of built-in DataView like objects * * See also: * ECMA-262 v11, 24.3.2.1 * * @return created DataView object as an ecma-value - if success * raised error - otherwise */ ecma_value_t ecma_op_dataview_create (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p)); ecma_value_t buffer = arguments_list_len > 0 ? arguments_list_p[0] : ECMA_VALUE_UNDEFINED; /* 2. */ if (!ecma_is_value_object (buffer)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_BUFFER_NOT_OBJECT); } ecma_object_t *buffer_p = ecma_get_object_from_value (buffer); if (!(ecma_object_class_is (buffer_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (buffer_p))) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_BUFFER_NOT_ARRAY_OR_SHARED_BUFFER); } /* 3. */ ecma_number_t offset = 0; if (arguments_list_len > 1) { ecma_value_t offset_value = ecma_op_to_index (arguments_list_p[1], &offset); if (ECMA_IS_VALUE_ERROR (offset_value)) { return offset_value; } } /* 4. */ if (ecma_arraybuffer_is_detached (buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } /* 5. */ ecma_number_t buffer_byte_length = ecma_arraybuffer_get_length (buffer_p); /* 6. */ if (offset > buffer_byte_length) { return ecma_raise_range_error (ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER); } /* 7. */ uint32_t view_byte_length; if (arguments_list_len > 2 && !ecma_is_value_undefined (arguments_list_p[2])) { /* 8.a */ ecma_number_t byte_length_to_index; ecma_value_t byte_length_value = ecma_op_to_index (arguments_list_p[2], &byte_length_to_index); if (ECMA_IS_VALUE_ERROR (byte_length_value)) { return byte_length_value; } /* 8.b */ if (offset + byte_length_to_index > buffer_byte_length) { return ecma_raise_range_error (ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER); } JERRY_ASSERT (byte_length_to_index <= UINT32_MAX); view_byte_length = (uint32_t) byte_length_to_index; } else { /* 7.a */ view_byte_length = (uint32_t) (buffer_byte_length - offset); } /* 9. */ ecma_object_t *prototype_obj_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_DATAVIEW_PROTOTYPE); if (JERRY_UNLIKELY (prototype_obj_p == NULL)) { return ECMA_VALUE_ERROR; } /* 10. */ if (ecma_arraybuffer_is_detached (buffer_p)) { ecma_deref_object (prototype_obj_p); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } /* 9. */ /* It must happen after 10., because uninitialized object can't be destroyed properly. */ ecma_object_t *object_p = ecma_create_object (prototype_obj_p, sizeof (ecma_dataview_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_deref_object (prototype_obj_p); /* 11 - 14. */ ecma_dataview_object_t *dataview_obj_p = (ecma_dataview_object_t *) object_p; dataview_obj_p->header.u.cls.type = ECMA_OBJECT_CLASS_DATAVIEW; dataview_obj_p->header.u.cls.u3.length = view_byte_length; dataview_obj_p->buffer_p = buffer_p; dataview_obj_p->byte_offset = (uint32_t) offset; return ecma_make_object_value (object_p); } /* ecma_op_dataview_create */ /** * Get the DataView object pointer * * Note: * If the function returns with NULL, the error object has * already set, and the caller must return with ECMA_VALUE_ERROR * * @return pointer to the dataView if this_arg is a valid dataView object * NULL otherwise */ ecma_dataview_object_t * ecma_op_dataview_get_object (ecma_value_t this_arg) /**< this argument */ { if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_DATAVIEW)) { return (ecma_dataview_object_t *) object_p; } } ecma_raise_type_error (ECMA_ERR_EXPECTED_A_DATAVIEW_OBJECT); return NULL; } /* ecma_op_dataview_get_object */ /** * Helper union to specify the system's endianness */ typedef union { uint32_t number; /**< for write numeric data */ char data[sizeof (uint32_t)]; /**< for read numeric data */ } ecma_dataview_endianness_check_t; /** * Helper function to check the current system endianness * * @return true - if the current system has little endian byteorder * false - otherwise */ static bool ecma_dataview_check_little_endian (void) { ecma_dataview_endianness_check_t checker; checker.number = 0x01; return checker.data[0] == 0x01; } /* ecma_dataview_check_little_endian */ /** * Helper function for swap bytes if the system's endianness * does not match with the requested endianness. */ static void ecma_dataview_swap_order (bool system_is_little_endian, /**< true - if the system has little endian byteorder * false - otherwise */ bool is_little_endian, /**< true - if little endian byteorder is requested * false - otherwise */ uint32_t element_size, /**< element size byte according to the Table 49.*/ lit_utf8_byte_t *block_p) /**< data block */ { if (system_is_little_endian ^ is_little_endian) { for (uint32_t i = 0; i < element_size / 2; i++) { lit_utf8_byte_t tmp = block_p[i]; block_p[i] = block_p[element_size - i - 1]; block_p[element_size - i - 1] = tmp; } } } /* ecma_dataview_swap_order */ /** * GetViewValue and SetViewValue abstract operation * * See also: * ECMA-262 v11, 24.3.1.1 * ECMA-262 v11, 24.3.1.2 * * @return ecma value */ ecma_value_t ecma_op_dataview_get_set_view_value (ecma_value_t view, /**< the operation's 'view' argument */ ecma_value_t request_index, /**< the operation's 'requestIndex' argument */ ecma_value_t is_little_endian_value, /**< the operation's * 'isLittleEndian' argument */ ecma_value_t value_to_set, /**< the operation's 'value' argument */ ecma_typedarray_type_t id) /**< the operation's 'type' argument */ { /* 1 - 2. */ ecma_dataview_object_t *view_p = ecma_op_dataview_get_object (view); if (JERRY_UNLIKELY (view_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *buffer_p = view_p->buffer_p; JERRY_ASSERT (ecma_object_class_is (buffer_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (buffer_p)); /* 3. */ ecma_number_t get_index; ecma_value_t number_index_value = ecma_op_to_index (request_index, &get_index); if (ECMA_IS_VALUE_ERROR (number_index_value)) { return number_index_value; } /* SetViewValue 4 - 5. */ if (!ecma_is_value_empty (value_to_set)) { #if JERRY_BUILTIN_BIGINT if (ECMA_TYPEDARRAY_IS_BIGINT_TYPE (id)) { value_to_set = ecma_bigint_to_bigint (value_to_set, true); if (ECMA_IS_VALUE_ERROR (value_to_set)) { return value_to_set; } } else #endif /* JERRY_BUILTIN_BIGINT */ { ecma_number_t value_to_set_number; ecma_value_t value = ecma_op_to_number (value_to_set, &value_to_set_number); if (ECMA_IS_VALUE_ERROR (value)) { return value; } value_to_set = ecma_make_number_value (value_to_set_number); } } /* GetViewValue 4., SetViewValue 6. */ bool is_little_endian = ecma_op_to_boolean (is_little_endian_value); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (buffer_p)) { ecma_free_value (value_to_set); return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (buffer_p)) { ecma_free_value (value_to_set); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } /* GetViewValue 7., SetViewValue 9. */ uint32_t view_offset = view_p->byte_offset; /* GetViewValue 8., SetViewValue 10. */ uint32_t view_size = view_p->header.u.cls.u3.length; /* GetViewValue 9., SetViewValue 11. */ uint8_t element_size = (uint8_t) (1 << (ecma_typedarray_helper_get_shift_size (id))); /* GetViewValue 10., SetViewValue 12. */ if (get_index + element_size > (ecma_number_t) view_size) { ecma_free_value (value_to_set); return ecma_raise_range_error (ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER); } if (ECMA_ARRAYBUFFER_LAZY_ALLOC (buffer_p)) { ecma_free_value (value_to_set); return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (buffer_p)) { ecma_free_value (value_to_set); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } /* GetViewValue 11., SetViewValue 13. */ bool system_is_little_endian = ecma_dataview_check_little_endian (); ecma_typedarray_info_t info; info.id = id; info.length = view_size; info.shift = ecma_typedarray_helper_get_shift_size (id); info.element_size = element_size; info.offset = view_p->byte_offset; info.array_buffer_p = buffer_p; /* GetViewValue 12. */ uint8_t *block_p = ecma_arraybuffer_get_buffer (buffer_p) + (uint32_t) get_index + view_offset; if (ecma_is_value_empty (value_to_set)) { JERRY_VLA (lit_utf8_byte_t, swap_block_p, element_size); memcpy (swap_block_p, block_p, element_size * sizeof (lit_utf8_byte_t)); ecma_dataview_swap_order (system_is_little_endian, is_little_endian, element_size, swap_block_p); ecma_typedarray_getter_fn_t typedarray_getter_cb = ecma_get_typedarray_getter_fn (info.id); return typedarray_getter_cb (swap_block_p); } if (!ecma_number_is_nan (get_index) && get_index <= 0) { get_index = 0; } /* SetViewValue 14. */ ecma_typedarray_setter_fn_t typedarray_setter_cb = ecma_get_typedarray_setter_fn (info.id); ecma_value_t set_element = typedarray_setter_cb (block_p, value_to_set); ecma_free_value (value_to_set); if (ECMA_IS_VALUE_ERROR (set_element)) { return set_element; } ecma_dataview_swap_order (system_is_little_endian, is_little_endian, element_size, block_p); return ECMA_VALUE_UNDEFINED; } /* ecma_op_dataview_get_set_view_value */ /** * Check if the value is dataview * * @return true - if value is a DataView object * false - otherwise */ bool ecma_is_dataview (ecma_value_t value) /**< the target need to be checked */ { if (!ecma_is_value_object (value)) { return false; } return ecma_object_class_is (ecma_get_object_from_value (value), ECMA_OBJECT_CLASS_DATAVIEW); } /* ecma_is_dataview */ /** * @} * @} */ #endif /* JERRY_BUILTIN_DATAVIEW */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/regression/000775 001750 001750 00000000000 15164251010 032354 5ustar00ddennedyddennedy000000 000000 jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int32array-prototype.inc.h000664 001750 001750 00000001616 15164251010 055042 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Int32Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 4 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_INT32ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieModifier.cpp000664 001750 001750 00000034226 15164251010 037031 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgLottieModifier.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static bool _colinear(const Point* p) { return tvg::zero(*p - *(p + 1)) && tvg::zero(*(p + 2) - *(p + 3)); } static Point _roundCorner(RenderPath& out, Point& prev, Point& curr, Point& next, float r) { auto lenPrev = length(prev - curr); auto rPrev = lenPrev > 0.0f ? 0.5f * std::min(lenPrev * 0.5f, r) / lenPrev : 0.0f; auto lenNext = length(next - curr); auto rNext = lenNext > 0.0f ? 0.5f * std::min(lenNext * 0.5f, r) / lenNext : 0.0f; auto dPrev = rPrev * (curr - prev); auto dNext = rNext * (curr - next); out.lineTo(curr - 2.0f * dPrev); auto ret = curr - 2.0f * dNext; out.cubicTo(curr - dPrev, curr - dNext, ret); return ret; } static bool _intersect(Line& line1, Line& line2, Point& intersection, bool& inside) { if (tvg::zero(line1.pt2 - line2.pt1)) { intersection = line1.pt2; inside = true; return true; } constexpr float epsilon = 1e-3f; float denom = (line1.pt2.x - line1.pt1.x) * (line2.pt2.y - line2.pt1.y) - (line1.pt2.y - line1.pt1.y) * (line2.pt2.x - line2.pt1.x); if (fabsf(denom) < epsilon) return false; float t = ((line2.pt1.x - line1.pt1.x) * (line2.pt2.y - line2.pt1.y) - (line2.pt1.y - line1.pt1.y) * (line2.pt2.x - line2.pt1.x)) / denom; float u = ((line2.pt1.x - line1.pt1.x) * (line1.pt2.y - line1.pt1.y) - (line2.pt1.y - line1.pt1.y) * (line1.pt2.x - line1.pt1.x)) / denom; intersection.x = line1.pt1.x + t * (line1.pt2.x - line1.pt1.x); intersection.y = line1.pt1.y + t * (line1.pt2.y - line1.pt1.y); inside = t >= -epsilon && t <= 1.0f + epsilon && u >= -epsilon && u <= 1.0f + epsilon; return true; } static Line _offset(Point& p1, Point& p2, float offset) { auto scaledNormal = normal(p1, p2) * offset; return {p1 + scaledNormal, p2 + scaledNormal}; } static bool _clockwise(Point* pts, uint32_t n) { auto area = 0.0f; for (uint32_t i = 0; i < n - 1; i++) { area += cross(pts[i], pts[i + 1]); } area += cross(pts[n - 1], pts[0]);; return area < 0.0f; } void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoOutIndex, bool nextClose) { bool inside{}; Point intersect{}; if (_intersect(line, nextLine, intersect, inside)) { if (inside) { if (nextClose) out.pts[movetoOutIndex] = intersect; out.pts.push(intersect); } else { out.pts.push(line.pt2); if (join == StrokeJoin::Round) { out.cubicTo((line.pt2 + intersect) * 0.5f, (nextLine.pt1 + intersect) * 0.5f, nextLine.pt1); } else if (join == StrokeJoin::Miter) { auto norm = normal(line.pt1, line.pt2); auto nextNorm = normal(nextLine.pt1, nextLine.pt2); auto miterDirection = (norm + nextNorm) / length(norm + nextNorm); if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) out.lineTo(intersect); out.lineTo(nextLine.pt1); } else out.lineTo(nextLine.pt1); } } else out.pts.push(line.pt2); } void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated) { if (tvg::zero(inPts[curPt - 1] - inPts[curPt])) { ++curPt; return; } if (inCmds[curCmd - 1] != PathCommand::LineTo) state.line = _offset(inPts[curPt - 1], inPts[curPt], offset); if (state.moveto) { state.movetoOutIndex = out.pts.count; out.moveTo(state.line.pt1); state.firstLine = state.line; state.moveto = false; } auto nonDegeneratedCubic = [&](uint32_t cmd, uint32_t pt) { return inCmds[cmd] == PathCommand::CubicTo && !tvg::zero(inPts[pt] - inPts[pt + 1]) && !tvg::zero(inPts[pt + 2] - inPts[pt + 3]); }; out.cmds.push(PathCommand::LineTo); if (curCmd + 1 == inCmdsCnt || inCmds[curCmd + 1] == PathCommand::MoveTo || nonDegeneratedCubic(curCmd + 1, curPt + degenerated)) { out.pts.push(state.line.pt2); ++curPt; return; } Line nextLine = state.firstLine; if (inCmds[curCmd + 1] == PathCommand::LineTo) nextLine = _offset(inPts[curPt + degenerated], inPts[curPt + 1 + degenerated], offset); else if (inCmds[curCmd + 1] == PathCommand::CubicTo) nextLine = _offset(inPts[curPt + 1 + degenerated], inPts[curPt + 2 + degenerated], offset); else if (inCmds[curCmd + 1] == PathCommand::Close && !tvg::zero(inPts[curPt + degenerated] - inPts[state.movetoInIndex + degenerated])) nextLine = _offset(inPts[curPt + degenerated], inPts[state.movetoInIndex + degenerated], offset); corner(out, state.line, nextLine, state.movetoOutIndex, inCmds[curCmd + 1] == PathCommand::Close); state.line = nextLine; ++curPt; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) { buffer->clear(); auto& path = (next) ? *buffer : out; path.cmds.reserve(inCmdsCnt * 2); path.pts.reserve((uint32_t)(inPtsCnt * 1.5)); auto pivot = path.pts.count; uint32_t startIndex = 0; auto rounded = false; Point roundTo; for (uint32_t iCmds = 0, iPts = 0; iCmds < inCmdsCnt; ++iCmds) { switch (inCmds[iCmds]) { case PathCommand::MoveTo: { startIndex = path.pts.count; path.moveTo(inPts[iPts++]); break; } case PathCommand::CubicTo: { if (iCmds < inCmdsCnt - 1 && _colinear(inPts + iPts - 1)) { auto& prev = inPts[iPts - 1]; auto& curr = inPts[iPts + 2]; if (inCmds[iCmds + 1] == PathCommand::CubicTo && _colinear(inPts + iPts + 2)) { roundTo = _roundCorner(path, prev, curr, inPts[iPts + 5], r); iPts += 3; rounded = true; continue; } else if (inCmds[iCmds + 1] == PathCommand::Close) { roundTo = _roundCorner(path, prev, curr, inPts[2], r); path.pts[startIndex] = path.pts.last(); iPts += 3; rounded = true; continue; } } path.cubicTo(rounded ? roundTo : inPts[iPts], inPts[iPts + 1], inPts[iPts + 2]); iPts += 3; break; } case PathCommand::Close: { path.close(); break; } default: break; } rounded = false; } if (transform) { for (auto i = pivot; i < path.pts.count; ++i) { path.pts[i] *= *transform; } } if (next) return next->modifyPath(path.cmds.data, path.cmds.count, path.pts.data, path.pts.count, transform, out); return true; } bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) { constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f; buffer->clear(); auto& path = (next) ? *buffer : out; auto len = length(in.pts[1] - in.pts[2]); auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * std::min(len * 0.5f, this->r) / len : 0.0f; if (hasRoundness) { path.cmds.grow((uint32_t)(1.5 * in.cmds.count)); path.pts.grow((uint32_t)(4.5 * in.cmds.count)); int start = 3 * tvg::zero(outerRoundness); path.moveTo(in.pts[start]); for (uint32_t i = 1 + start; i < in.pts.count; i += 6) { auto& prev = in.pts[i]; auto& curr = in.pts[i + 2]; auto& next = (i < in.pts.count - start) ? in.pts[i + 4] : in.pts[2]; auto& nextCtrl = (i < in.pts.count - start) ? in.pts[i + 5] : in.pts[3]; auto dNext = r * (curr - next); auto dPrev = r * (curr - prev); auto p0 = curr - 2.0f * dPrev; auto p1 = curr - dPrev; auto p2 = curr - dNext; auto p3 = curr - 2.0f * dNext; path.cubicTo(prev, p0, p0); path.cubicTo(p1, p2, p3); path.cubicTo(p3, next, nextCtrl); } } else { path.cmds.grow(2 * in.cmds.count); path.pts.grow(4 * in.cmds.count); auto dPrev = r * (in.pts[1] - in.pts[0]); auto p = in.pts[0] + 2.0f * dPrev; path.moveTo(p); for (uint32_t i = 1; i < in.pts.count; ++i) { auto& curr = in.pts[i]; auto& next = (i == in.pts.count - 1) ? in.pts[1] : in.pts[i + 1]; auto dNext = r * (curr - next); auto p0 = curr - 2.0f * dPrev; auto p1 = curr - dPrev; auto p2 = curr - dNext; auto p3 = curr - 2.0f * dNext; path.lineTo(p0); path.cubicTo(p1, p2, p3); dPrev = -1.0f * dNext; } } path.cmds.push(PathCommand::Close); if (next) return next->modifyPolystar(path, out, outerRoundness, hasRoundness); return true; } bool LottieRoundnessModifier::modifyRect(Point& size, float& r) { r = std::min(this->r, std::max(size.x, size.y) * 0.5f); return true; } bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, TVG_UNUSED Matrix* transform, RenderPath& out) { if (next) TVGERR("LOTTIE", "Offset has a next modifier?"); out.cmds.reserve(inCmdsCnt * 2); out.pts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2)); Array stack{5}; State state; auto offset = _clockwise(inPts, inPtsCnt) ? this->offset : -this->offset; auto threshold = 1.0f / fabsf(offset) + 1.0f; for (uint32_t iCmd = 0, iPt = 0; iCmd < inCmdsCnt; ++iCmd) { if (inCmds[iCmd] == PathCommand::MoveTo) { state.moveto = true; state.movetoInIndex = iPt++; } else if (inCmds[iCmd] == PathCommand::LineTo) { line(out, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, false); } else if (inCmds[iCmd] == PathCommand::CubicTo) { //cubic degenerated to a line if (tvg::zero(inPts[iPt - 1] - inPts[iPt]) || tvg::zero(inPts[iPt + 1] - inPts[iPt + 2])) { ++iPt; line(out, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, true); ++iPt; continue; } stack.push({inPts[iPt - 1], inPts[iPt], inPts[iPt + 1], inPts[iPt + 2]}); while (!stack.empty()) { auto& bezier = stack.last(); auto len = tvg::length(bezier.start - bezier.ctrl1) + tvg::length(bezier.ctrl1 - bezier.ctrl2) + tvg::length(bezier.ctrl2 - bezier.end); if (len > threshold * bezier.length()) { Bezier next; bezier.split(0.5f, next); stack.push(next); continue; } stack.pop(); auto line1 = _offset(bezier.start, bezier.ctrl1, offset); auto line2 = _offset(bezier.ctrl1, bezier.ctrl2, offset); auto line3 = _offset(bezier.ctrl2, bezier.end, offset); if (state.moveto) { state.movetoOutIndex = out.pts.count; out.moveTo(line1.pt1); state.firstLine = line1; state.moveto = false; } bool inside{}; Point intersect{}; _intersect(line1, line2, intersect, inside); out.pts.push(intersect); _intersect(line2, line3, intersect, inside); out.pts.push(intersect); out.pts.push(line3.pt2); out.cmds.push(PathCommand::CubicTo); } iPt += 3; } else { if (!tvg::zero(inPts[iPt - 1] - inPts[state.movetoInIndex])) { out.cmds.push(PathCommand::LineTo); corner(out, state.line, state.firstLine, state.movetoOutIndex, true); } out.cmds.push(PathCommand::Close); } } return true; } bool LottieOffsetModifier::modifyPolystar(RenderPath& in, RenderPath& out, TVG_UNUSED float, TVG_UNUSED bool) { return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, nullptr, out); } bool LottieOffsetModifier::modifyRect(RenderPath& in, RenderPath& out) { return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, nullptr, out); } bool LottieOffsetModifier::modifyEllipse(Point& radius) { radius.x += offset; radius.y += offset; return true; } thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-typedarray-object.h000664 001750 001750 00000011650 15164251010 047116 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_TYPEDARRAY_OBJECT_H #define ECMA_TYPEDARRAY_OBJECT_H #include "ecma-builtins.h" #include "ecma-globals.h" #if JERRY_BUILTIN_TYPEDARRAY /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmatypedarrayobject ECMA TypedArray object related routines * @{ */ uint8_t ecma_typedarray_helper_get_shift_size (ecma_typedarray_type_t typedarray_id); lit_magic_string_id_t ecma_get_typedarray_magic_string_id (ecma_typedarray_type_t typedarray_id); ecma_typedarray_getter_fn_t ecma_get_typedarray_getter_fn (ecma_typedarray_type_t typedarray_id); ecma_typedarray_setter_fn_t ecma_get_typedarray_setter_fn (ecma_typedarray_type_t typedarray_id); ecma_value_t ecma_get_typedarray_element (ecma_typedarray_info_t *info_p, uint32_t index); ecma_value_t ecma_set_typedarray_element (ecma_typedarray_info_t *info_p, ecma_value_t value, uint32_t index); bool ecma_typedarray_helper_is_typedarray (ecma_builtin_id_t builtin_id); ecma_typedarray_type_t ecma_get_typedarray_id (ecma_object_t *obj_p); ecma_builtin_id_t ecma_typedarray_helper_get_prototype_id (ecma_typedarray_type_t typedarray_id); ecma_builtin_id_t ecma_typedarray_helper_get_constructor_id (ecma_typedarray_type_t typedarray_id); ecma_typedarray_type_t ecma_typedarray_helper_builtin_to_typedarray_id (ecma_builtin_id_t builtin_id); ecma_value_t ecma_op_typedarray_from (ecma_value_t this_val, ecma_value_t source_val, ecma_value_t mapfn_val, ecma_value_t this_arg); uint32_t ecma_typedarray_get_length (ecma_object_t *typedarray_p); uint32_t ecma_typedarray_get_offset (ecma_object_t *typedarray_p); uint8_t *ecma_typedarray_get_buffer (ecma_typedarray_info_t *info_p); uint8_t ecma_typedarray_get_element_size_shift (ecma_object_t *typedarray_p); ecma_object_t *ecma_typedarray_get_arraybuffer (ecma_object_t *typedarray_p); ecma_value_t ecma_op_create_typedarray (const ecma_value_t *arguments_list_p, uint32_t arguments_list_len, ecma_object_t *proto_p, uint8_t element_size_shift, ecma_typedarray_type_t typedarray_id); ecma_value_t ecma_typedarray_iterators_helper (ecma_value_t this_arg, ecma_iterator_kind_t kind); bool ecma_object_is_typedarray (ecma_object_t *obj_p); bool ecma_is_typedarray (ecma_value_t target); bool ecma_typedarray_is_element_index (ecma_string_t *property_name_p); void ecma_op_typedarray_list_lazy_property_names (ecma_object_t *obj_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p, jerry_property_filter_t filter); ecma_value_t ecma_op_typedarray_define_own_property (ecma_object_t *obj_p, ecma_string_t *property_name_p, const ecma_property_descriptor_t *property_desc_p); ecma_value_t ecma_op_create_typedarray_with_type_and_length (ecma_typedarray_type_t typedarray_id, uint32_t array_length); ecma_typedarray_info_t ecma_typedarray_get_info (ecma_object_t *typedarray_p); ecma_value_t ecma_typedarray_create_object_with_length (uint32_t array_length, ecma_object_t *src_arraybuffer_p, ecma_object_t *proto_p, uint8_t element_size_shift, ecma_typedarray_type_t typedarray_id); ecma_value_t ecma_typedarray_create_object_with_object (ecma_value_t items_val, ecma_object_t *proto_p, uint8_t element_size_shift, ecma_typedarray_type_t typedarray_id); ecma_value_t ecma_typedarray_create (ecma_object_t *constructor_p, ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_value_t ecma_typedarray_species_create (ecma_value_t this_arg, ecma_value_t *length, uint32_t arguments_list_len); /** * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ #endif /* !ECMA_TYPEDARRAY_OBJECT_H */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator.inc.h000664 001750 001750 00000002432 15164251010 051713 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %AsyncGenerator% built-in description (AsyncGeneratorFunction.prototype) */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* ECMA-262 v6, 25.3.2.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_ASYNC_GENERATOR_FUNCTION, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.3.2.3.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.3.2.3.3 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-boolean.inc.h000664 001750 001750 00000002372 15164251010 050234 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Boolean description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_BOOLEAN /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.6.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_BOOLEAN_PROTOTYPE, ECMA_PROPERTY_FIXED) /* Number properties: * (property name, object pointer getter) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_BOOLEAN_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_BOOLEAN */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testSwEngine.cpp000664 001750 001750 00000037443 15164251010 033332 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; #ifdef THORVG_SW_RASTER_SUPPORT TEST_CASE("Basic draw", "[tvgSwEngine]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888S) == Result::Success); std::vector masks; masks.push_back(MaskMethod::None); masks.push_back(MaskMethod::Alpha); masks.push_back(MaskMethod::InvAlpha); masks.push_back(MaskMethod::Luma); masks.push_back(MaskMethod::InvLuma); masks.push_back(MaskMethod::Add); masks.push_back(MaskMethod::Subtract); masks.push_back(MaskMethod::Intersect); masks.push_back(MaskMethod::Difference); masks.push_back(MaskMethod::Lighten); masks.push_back(MaskMethod::Darken); std::vector methods; methods.push_back(BlendMethod::Normal); methods.push_back(BlendMethod::Multiply); methods.push_back(BlendMethod::Screen); methods.push_back(BlendMethod::Overlay); methods.push_back(BlendMethod::Darken); methods.push_back(BlendMethod::Lighten); methods.push_back(BlendMethod::ColorDodge); methods.push_back(BlendMethod::ColorBurn); methods.push_back(BlendMethod::HardLight); methods.push_back(BlendMethod::SoftLight); methods.push_back(BlendMethod::Difference); methods.push_back(BlendMethod::Hue); methods.push_back(BlendMethod::Saturation); methods.push_back(BlendMethod::Color); methods.push_back(BlendMethod::Luminosity); methods.push_back(BlendMethod::Add); methods.push_back(BlendMethod::Composition); auto mask = []() { auto mask = Shape::gen(); mask->appendRect(0, 10, 20, 30, 5, 5); mask->opacity(127); mask->fill(255, 255, 255); return mask; }; for (auto method : methods) { for (auto maskOp : masks) { //Arc Line auto shape1 = Shape::gen(); REQUIRE(shape1->strokeFill(255, 255, 255, 255) == Result::Success); REQUIRE(shape1->strokeWidth(2) == Result::Success); REQUIRE(shape1->blend(method) == Result::Success); if (maskOp != MaskMethod::None) REQUIRE(shape1->mask(mask(), maskOp) == Result::Success); REQUIRE(canvas->add(shape1) == Result::Success); //Cubic auto shape2 = Shape::gen(); REQUIRE(shape2->moveTo(50, 25) == Result::Success); REQUIRE(shape2->cubicTo(62, 25, 75, 38, 75, 50) == Result::Success); REQUIRE(shape2->close() == Result::Success); REQUIRE(shape2->strokeFill(255, 0, 0, 125) == Result::Success); REQUIRE(shape2->strokeWidth(1) == Result::Success); REQUIRE(shape2->blend(method) == Result::Success); if (maskOp != MaskMethod::None) REQUIRE(shape2->mask(mask(), maskOp) == Result::Success); REQUIRE(canvas->add(shape2) == Result::Success); //Fill auto shape3 = Shape::gen(); REQUIRE(shape3->moveTo(0, 0) == Result::Success); REQUIRE(shape3->lineTo(20, 0) == Result::Success); REQUIRE(shape3->lineTo(20, 20) == Result::Success); REQUIRE(shape3->lineTo(0, 20) == Result::Success); REQUIRE(shape3->close() == Result::Success); REQUIRE(shape3->fill(255, 255, 255) == Result::Success); REQUIRE(shape3->blend(method) == Result::Success); if (maskOp != MaskMethod::None) REQUIRE(shape3->mask(mask(), maskOp) == Result::Success); REQUIRE(canvas->add(shape3) == Result::Success); //Dashed Line shape auto shape4 = Shape::gen(); float dashPattern[2] = {2.5f, 5.0f}; REQUIRE(shape4->moveTo(0, 0) == Result::Success); REQUIRE(shape4->lineTo(25, 25) == Result::Success); REQUIRE(shape4->cubicTo(50, 50, 75, -75, 50, 100) == Result::Success); REQUIRE(shape4->close() == Result::Success); REQUIRE(shape4->fill(255, 255, 255) == Result::Success); REQUIRE(shape4->strokeFill(255, 0, 0, 255) == Result::Success); REQUIRE(shape4->strokeWidth(2) == Result::Success); REQUIRE(shape4->strokeDash(dashPattern, 2) == Result::Success); REQUIRE(shape4->strokeCap(StrokeCap::Round) == Result::Success); REQUIRE(shape4->blend(method) == Result::Success); if (maskOp != MaskMethod::None) REQUIRE(shape4->mask(mask(), maskOp) == Result::Success); REQUIRE(canvas->add(shape4) == Result::Success); } } REQUIRE(canvas->draw(true) == Result::Success); REQUIRE(canvas->sync() == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Image Draw", "[tvgSwEngine]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); //raw image ifstream file(TEST_DIR"/rawimage_200x300.raw"); if (!file.is_open()) return; auto data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300)); file.read(reinterpret_cast(data), sizeof (uint32_t) * 200 * 300); file.close(); std::vector masks; masks.push_back(MaskMethod::None); masks.push_back(MaskMethod::Alpha); masks.push_back(MaskMethod::InvAlpha); masks.push_back(MaskMethod::Luma); masks.push_back(MaskMethod::InvLuma); masks.push_back(MaskMethod::Add); masks.push_back(MaskMethod::Subtract); masks.push_back(MaskMethod::Intersect); masks.push_back(MaskMethod::Difference); masks.push_back(MaskMethod::Lighten); masks.push_back(MaskMethod::Darken); std::vector methods; methods.push_back(BlendMethod::Normal); methods.push_back(BlendMethod::Multiply); methods.push_back(BlendMethod::Screen); methods.push_back(BlendMethod::Overlay); methods.push_back(BlendMethod::Darken); methods.push_back(BlendMethod::Lighten); methods.push_back(BlendMethod::ColorDodge); methods.push_back(BlendMethod::ColorBurn); methods.push_back(BlendMethod::HardLight); methods.push_back(BlendMethod::SoftLight); methods.push_back(BlendMethod::Difference); methods.push_back(BlendMethod::Hue); methods.push_back(BlendMethod::Saturation); methods.push_back(BlendMethod::Color); methods.push_back(BlendMethod::Luminosity); methods.push_back(BlendMethod::Add); methods.push_back(BlendMethod::Composition); auto mask = []() { auto mask = Shape::gen(); mask->appendRect(0, 10, 20, 30, 5, 5); mask->fill(255, 255, 255); return mask; }; for (auto method : methods) { for (auto maskOp : masks) { //Non-transformed images auto picture = Picture::gen(); REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success); REQUIRE(picture->blend(method) == Result::Success); if (maskOp != MaskMethod::None) REQUIRE(picture->mask(mask(), maskOp) == Result::Success); REQUIRE(canvas->add(picture) == Result::Success); //Clipped images auto picture2 = picture->duplicate(); REQUIRE(picture2->clip(mask()) == Result::Success); REQUIRE(canvas->add(picture2) == Result::Success); // Transformed images auto picture3 = picture->duplicate(); REQUIRE(picture3->rotate(45) == Result::Success); REQUIRE(canvas->add(picture3) == Result::Success); //Up-scaled Image auto picture4 = picture->duplicate(); REQUIRE(picture4->scale(2.0f) == Result::Success); REQUIRE(canvas->add(picture4) == Result::Success); //Down-scaled Image auto picture5 = picture->duplicate(); REQUIRE(picture5->scale(0.25f) == Result::Success); REQUIRE(canvas->add(picture5) == Result::Success); //Direct Clipped image auto picture6 = Picture::gen(); REQUIRE(picture6->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success); REQUIRE(picture6->clip(mask()) == Result::Success); REQUIRE(picture6->blend(method) == Result::Success); REQUIRE(canvas->add(picture6) == Result::Success); //Scaled Clipped image auto picture7 = picture6->duplicate(); REQUIRE(picture7->scale(2.0f) == Result::Success); REQUIRE(canvas->add(picture7) == Result::Success); } } REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); free(data); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Filling Draw", "[tvgSwEngine]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); std::vector masks; masks.push_back(MaskMethod::None); masks.push_back(MaskMethod::Alpha); masks.push_back(MaskMethod::InvAlpha); masks.push_back(MaskMethod::Luma); masks.push_back(MaskMethod::InvLuma); masks.push_back(MaskMethod::Add); masks.push_back(MaskMethod::Subtract); masks.push_back(MaskMethod::Intersect); masks.push_back(MaskMethod::Difference); masks.push_back(MaskMethod::Lighten); masks.push_back(MaskMethod::Darken); std::vector methods; methods.push_back(BlendMethod::Normal); methods.push_back(BlendMethod::Multiply); methods.push_back(BlendMethod::Screen); methods.push_back(BlendMethod::Overlay); methods.push_back(BlendMethod::Darken); methods.push_back(BlendMethod::Lighten); methods.push_back(BlendMethod::ColorDodge); methods.push_back(BlendMethod::ColorBurn); methods.push_back(BlendMethod::HardLight); methods.push_back(BlendMethod::SoftLight); methods.push_back(BlendMethod::Difference); methods.push_back(BlendMethod::Hue); methods.push_back(BlendMethod::Saturation); methods.push_back(BlendMethod::Color); methods.push_back(BlendMethod::Luminosity); methods.push_back(BlendMethod::Add); methods.push_back(BlendMethod::Composition); auto mask = []() { auto mask = Shape::gen(); mask->appendRect(10, 10, 20, 30, 5, 5); mask->opacity(127); mask->fill(255, 255, 255); return mask; }; Fill::ColorStop cs[4] = { {0.1f, 0, 0, 0, 0}, {0.2f, 50, 25, 50, 25}, {0.5f, 100, 100, 100, 125}, {0.9f, 255, 255, 255, 255} }; for (auto method : methods) { for (auto maskOp : masks) { //Linear Gradient auto linear = LinearGradient::gen(); REQUIRE(linear->colorStops(cs, 4) == Result::Success); REQUIRE(linear->spread(FillSpread::Repeat) == Result::Success); REQUIRE(linear->linear(0.0f, 0.0f, 100.0f, 120.0f) == Result::Success); auto shape = Shape::gen(); REQUIRE(shape->appendRect(0, 0, 50, 50, 5, 5) == Result::Success); REQUIRE(shape->fill(linear) == Result::Success); REQUIRE(shape->blend(method) == Result::Success); if (maskOp != MaskMethod::None) REQUIRE(shape->mask(mask(), maskOp) == Result::Success); REQUIRE(canvas->add(shape) == Result::Success); //Radial Gradient auto radial = RadialGradient::gen(); REQUIRE(radial->colorStops(cs, 4) == Result::Success); REQUIRE(radial->spread(FillSpread::Pad) == Result::Success); REQUIRE(radial->radial(50.0f, 50.0f, 50.0f, 50.0f, 50.0f, 0.0f) == Result::Success); auto shape2 = Shape::gen(); REQUIRE(shape2->appendRect(50, 0, 50, 50) == Result::Success); REQUIRE(shape2->fill(radial) == Result::Success); REQUIRE(shape2->blend(method) == Result::Success); if (maskOp != MaskMethod::None) REQUIRE(shape2->mask(mask(), maskOp) == Result::Success); REQUIRE(canvas->add(shape2) == Result::Success); } } REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Image Rotation", "[tvgSwEngine]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); const uint32_t cw = 960; const uint32_t ch = 960; vector buffer(static_cast(cw) * ch); REQUIRE(canvas->target(buffer.data(), cw, ch, cw, ColorSpace::ARGB8888) == Result::Success); auto picture = Picture::gen(); REQUIRE(picture); ifstream file(TEST_DIR"/rawimage_250x375.raw"); if (!file.is_open()) return; auto data = (uint32_t*)malloc(sizeof(uint32_t) * (250*375)); file.read(reinterpret_cast(data), sizeof (uint32_t) * 250 * 375); file.close(); REQUIRE(picture->load(data, 250, 375, ColorSpace::ARGB8888, false) == Result::Success); REQUIRE(picture->size(240, 240) == Result::Success); REQUIRE(picture->transform({0.572866f, -4.431353f, 336.605835f, 5.198910f, -0.386219f, 30.710693f, 0.0f, 0.0f, 1.0f}) == Result::Success); REQUIRE(canvas->add(picture) == Result::Success); REQUIRE(canvas->draw(true) == Result::Success); REQUIRE(canvas->sync() == Result::Success); free(data); } REQUIRE(Initializer::term() == Result::Success); } #endifmlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testPicture.cpp000664 001750 001750 00000033352 15164251010 033221 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; TEST_CASE("Picture Creation", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); REQUIRE(picture->type() == Type::Picture); Paint::rel(picture); } TEST_CASE("Load RAW Data", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); ifstream file(TEST_DIR"/rawimage_200x300.raw"); if (!file.is_open()) return; auto data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300)); file.read(reinterpret_cast(data), sizeof (uint32_t) * 200 * 300); file.close(); //Negative cases REQUIRE(picture->load(nullptr, 200, 300, ColorSpace::ARGB8888, false) == Result::InvalidArguments); REQUIRE(picture->load(data, 0, 0, ColorSpace::ARGB8888, false) == Result::InvalidArguments); REQUIRE(picture->load(data, 200, 0, ColorSpace::ARGB8888, false) == Result::InvalidArguments); REQUIRE(picture->load(data, 0, 300, ColorSpace::ARGB8888, false) == Result::InvalidArguments); //Positive cases REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success); REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, true) == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 200); REQUIRE(h == 300); Paint::rel(picture); free(data); } TEST_CASE("Picture Size", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); float w, h; REQUIRE(picture->size(&w, &h) == Result::InsufficientCondition); //Primary ifstream file(TEST_DIR"/rawimage_200x300.raw"); if (!file.is_open()) return; auto data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300)); file.read(reinterpret_cast(data), sizeof (uint32_t) * 200 * 300); file.close(); REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success); REQUIRE(picture->size(nullptr, nullptr) == Result::Success); REQUIRE(picture->size(100, 100) == Result::Success); REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 100); REQUIRE(h == 100); free(data); //Secondary ifstream file2(TEST_DIR"/rawimage_250x375.raw"); if (!file2.is_open()) return; data = (uint32_t*)malloc(sizeof(uint32_t) * (250*375)); file2.read(reinterpret_cast(data), sizeof (uint32_t) * 250 * 375); file2.close(); REQUIRE(picture->load(data, 250, 375, ColorSpace::ARGB8888, false) == Result::Success); REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(picture->size(w, h) == Result::Success); free(data); Paint::rel(picture); } TEST_CASE("Picture Origin", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); float w, h; REQUIRE(picture->size(&w, &h) == Result::InsufficientCondition); //Primary ifstream file(TEST_DIR"/rawimage_200x300.raw"); if (!file.is_open()) return; auto data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300)); file.read(reinterpret_cast(data), sizeof (uint32_t) * 200 * 300); file.close(); REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success); REQUIRE(picture->origin(0.0f, 0.0f) == Result::Success); REQUIRE(picture->origin(0.5f, 0.5f) == Result::Success); REQUIRE(picture->origin(1.0f, 1.0f) == Result::Success); REQUIRE(picture->origin(-1.0f, -1.0f) == Result::Success); free(data); Paint::rel(picture); } TEST_CASE("Picture Resolver", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); float w, h; REQUIRE(picture->size(&w, &h) == Result::InsufficientCondition); //Primary ifstream file(TEST_DIR"/rawimage_200x300.raw"); if (!file.is_open()) return; auto data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300)); file.read(reinterpret_cast(data), sizeof (uint32_t) * 200 * 300); file.close(); auto resolver = [](Paint* paint, const char* src, void *data) -> bool { return false; }; REQUIRE(picture->resolver(resolver, nullptr) == Result::Success); REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success); free(data); Paint::rel(picture); } TEST_CASE("Picture Duplication", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); //Primary ifstream file(TEST_DIR"/rawimage_200x300.raw"); if (!file.is_open()) return; auto data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300)); file.read(reinterpret_cast(data), sizeof (uint32_t) * 200 * 300); file.close(); REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success); REQUIRE(picture->size(100, 100) == Result::Success); auto dup = (Picture*)picture->duplicate(); REQUIRE(dup); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 100); REQUIRE(h == 100); free(data); Paint::rel(dup); Paint::rel(picture); } #ifdef THORVG_SVG_LOADER_SUPPORT TEST_CASE("Load SVG file", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); //Invalid file REQUIRE(picture->load("invalid.svg") == Result::InvalidArguments); //Load Svg file REQUIRE(picture->load(TEST_DIR"/test1.svg") == Result::Success); REQUIRE(picture->load(TEST_DIR"/test2.svg") == Result::Success); REQUIRE(picture->load(TEST_DIR"/test3.svg") == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); Paint::rel(picture); } TEST_CASE("Load SVG Data", "[tvgPicture]") { static const char* svg = ""; auto picture = Picture::gen(); REQUIRE(picture); //Negative cases REQUIRE(picture->load(nullptr, 100, "") == Result::InvalidArguments); REQUIRE(picture->load(svg, 0, "") == Result::InvalidArguments); //Positive cases REQUIRE(picture->load(svg, strlen(svg), "svg") == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 1000); REQUIRE(h == 1000); Paint::rel(picture); } #endif #ifdef THORVG_PNG_LOADER_SUPPORT TEST_CASE("Load PNG file from path", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); //Invalid file REQUIRE(picture->load("invalid.png") == Result::InvalidArguments); REQUIRE(picture->load(TEST_DIR"/test.png") == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 512); REQUIRE(h == 512); Paint::rel(picture); } TEST_CASE("Load PNG file from data", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); //Open file ifstream file(TEST_DIR"/test.png", ios::in | ios::binary); REQUIRE(file.is_open()); auto size = sizeof(uint32_t) * (1000*1000); auto data = (char*)malloc(size); file.read(data, size); file.close(); REQUIRE(picture->load(data, size, "") == Result::Success); REQUIRE(picture->load(data, size, "png", "", true) == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 512); REQUIRE(h == 512); free(data); Paint::rel(picture); } TEST_CASE("Load PNG file and render", "[tvgPicture]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); auto picture = Picture::gen(); REQUIRE(picture); REQUIRE(picture->load(TEST_DIR"/test.png") == Result::Success); REQUIRE(picture->opacity(192) == Result::Success); REQUIRE(picture->scale(5.0) == Result::Success); REQUIRE(canvas->add(picture) == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } #endif #ifdef THORVG_JPG_LOADER_SUPPORT TEST_CASE("Load JPG file from path", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); //Invalid file REQUIRE(picture->load("invalid.jpg") == Result::InvalidArguments); REQUIRE(picture->load(TEST_DIR"/test.jpg") == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 512); REQUIRE(h == 512); Paint::rel(picture); } TEST_CASE("Load JPG file from data", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); //Open file ifstream file(TEST_DIR"/test.jpg", ios::in | ios::binary); REQUIRE(file.is_open()); auto begin = file.tellg(); file.seekg(0, ios::end); auto size = file.tellg() - begin; auto data = (char*)malloc(size); file.seekg(0, ios::beg); file.read(data, size); file.close(); REQUIRE(picture->load(data, size, "") == Result::Success); REQUIRE(picture->load(data, size, "jpg", "", true) == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 512); REQUIRE(h == 512); free(data); Paint::rel(picture); } TEST_CASE("Load JPG file and render", "[tvgPicture]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); auto picture = Picture::gen(); REQUIRE(picture); REQUIRE(picture->load(TEST_DIR"/test.jpg") == Result::Success); REQUIRE(canvas->add(picture) == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } #endif #ifdef THORVG_WEBP_LOADER_SUPPORT TEST_CASE("Load WEBP file from path", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); //Invalid file REQUIRE(picture->load("invalid.webp") == Result::InvalidArguments); REQUIRE(picture->load(TEST_DIR"/test.webp") == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 512); REQUIRE(h == 512); Paint::rel(picture); } TEST_CASE("Load WEBP file from data", "[tvgPicture]") { auto picture = Picture::gen(); REQUIRE(picture); //Open file ifstream file(TEST_DIR"/test.webp", ios::in | ios::binary); REQUIRE(file.is_open()); auto size = sizeof(uint32_t) * (1000*1000); auto data = (char*)malloc(size); file.read(data, size); file.close(); REQUIRE(picture->load(data, size, "") == Result::Success); REQUIRE(picture->load(data, size, "webp", "", true) == Result::Success); float w, h; REQUIRE(picture->size(&w, &h) == Result::Success); REQUIRE(w == 512); REQUIRE(h == 512); free(data); Paint::rel(picture); } TEST_CASE("Load WEBP file and render", "[tvgPicture]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); auto picture = Picture::gen(); REQUIRE(picture); REQUIRE(picture->load(TEST_DIR"/test.webp") == Result::Success); REQUIRE(picture->opacity(192) == Result::Success); REQUIRE(picture->scale(5.0) == Result::Success); REQUIRE(canvas->add(picture) == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } #endif src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp000664 001750 001750 00000116375 15164251010 036443 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" /* to include math.h before cstring */ #include "tvgShape.h" #include "tvgCompressor.h" #include "tvgFill.h" #include "tvgStr.h" #include "tvgShape.h" #include "tvgSvgLoaderCommon.h" #include "tvgSvgSceneBuilder.h" #include "tvgSvgPath.h" #include "tvgSvgUtil.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform); static Scene* _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth); static inline bool _isGroupType(SvgNodeType type) { if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath || type == SvgNodeType::Symbol || type == SvgNodeType::Filter) return true; return false; } //According to: https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits (the last paragraph) //a stroke width should be ignored for bounding box calculations static Box _bounds(Paint* paint) { float x, y, w, h; paint->bounds(&x, &y, &w, &h); return {x, y, w, h}; } static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf) { gradTransf->e13 = gradTransf->e13 * mBBox->e11 + mBBox->e13; gradTransf->e12 *= mBBox->e11; gradTransf->e11 *= mBBox->e11; gradTransf->e23 = gradTransf->e23 * mBBox->e22 + mBBox->e23; gradTransf->e22 *= mBBox->e22; gradTransf->e21 *= mBBox->e22; } static LinearGradient* _applyLinearGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) { Fill::ColorStop* stops; auto fillGrad = LinearGradient::gen(); auto isTransform = (g->transform ? true : false); auto& finalTransform = fillGrad->transform(); if (isTransform) finalTransform = *g->transform; if (g->userSpace) { g->linear->x1 = g->linear->x1 * vBox.w; g->linear->y1 = g->linear->y1 * vBox.h; g->linear->x2 = g->linear->x2 * vBox.w; g->linear->y2 = g->linear->y2 * vBox.h; } else { Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1}; if (isTransform) _transformMultiply(&m, &finalTransform); else finalTransform = m; } fillGrad->linear(g->linear->x1, g->linear->y1, g->linear->x2, g->linear->y2); fillGrad->spread(g->spread); //Update the stops if (g->stops.count == 0) return fillGrad; stops = tvg::malloc(g->stops.count * sizeof(Fill::ColorStop)); auto prevOffset = 0.0f; for (uint32_t i = 0; i < g->stops.count; ++i) { auto colorStop = &g->stops[i]; //Use premultiplied color stops[i].r = colorStop->r; stops[i].g = colorStop->g; stops[i].b = colorStop->b; stops[i].a = static_cast((colorStop->a * opacity) / 255); stops[i].offset = colorStop->offset; //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes if (colorStop->offset < prevOffset) stops[i].offset = prevOffset; else if (colorStop->offset > 1) stops[i].offset = 1; prevOffset = stops[i].offset; } fillGrad->colorStops(stops, g->stops.count); tvg::free(stops); return fillGrad; } static RadialGradient* _applyRadialGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) { Fill::ColorStop *stops; auto fillGrad = RadialGradient::gen(); auto isTransform = (g->transform ? true : false); auto& finalTransform = fillGrad->transform(); if (isTransform) finalTransform = *g->transform; if (g->userSpace) { //The radius scaling is done according to the Units section: //https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html g->radial->cx = g->radial->cx * vBox.w; g->radial->cy = g->radial->cy * vBox.h; g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f); g->radial->fx = g->radial->fx * vBox.w; g->radial->fy = g->radial->fy * vBox.h; g->radial->fr = g->radial->fr * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f); } else { Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1}; if (isTransform) _transformMultiply(&m, &finalTransform); else finalTransform = m; } fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr); fillGrad->spread(g->spread); //Update the stops if (g->stops.count == 0) return fillGrad; stops = tvg::malloc(g->stops.count * sizeof(Fill::ColorStop)); auto prevOffset = 0.0f; for (uint32_t i = 0; i < g->stops.count; ++i) { auto colorStop = &g->stops[i]; //Use premultiplied color stops[i].r = colorStop->r; stops[i].g = colorStop->g; stops[i].b = colorStop->b; stops[i].a = static_cast((colorStop->a * opacity) / 255); stops[i].offset = colorStop->offset; //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes if (colorStop->offset < prevOffset) stops[i].offset = prevOffset; else if (colorStop->offset > 1) stops[i].offset = 1; prevOffset = stops[i].offset; } fillGrad->colorStops(stops, g->stops.count); tvg::free(stops); return fillGrad; } static void _appendRect(Shape* shape, float x, float y, float w, float h, float rx, float ry) { auto halfW = w * 0.5f; auto halfH = h * 0.5f; //clamping cornerRadius by minimum size if (rx > halfW) rx = halfW; if (ry > halfH) ry = halfH; if (rx == 0 && ry == 0) { to(shape)->grow(5, 4); shape->moveTo(x, y); shape->lineTo(x + w, y); shape->lineTo(x + w, y + h); shape->lineTo(x, y + h); shape->close(); } else { auto hrx = rx * PATH_KAPPA; auto hry = ry * PATH_KAPPA; to(shape)->grow(10, 17); shape->moveTo(x + rx, y); shape->lineTo(x + w - rx, y); shape->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry); shape->lineTo(x + w, y + h - ry); shape->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h); shape->lineTo(x + rx, y + h); shape->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); shape->lineTo(x, y + ry); shape->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y); shape->close(); } } static void _appendCircle(Shape* shape, float cx, float cy, float rx, float ry) { auto rxKappa = rx * PATH_KAPPA; auto ryKappa = ry * PATH_KAPPA; to(shape)->grow(6, 13); shape->moveTo(cx + rx, cy); shape->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry); shape->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy); shape->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry); shape->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); shape->close(); } static bool _appendClipChild(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath) { //The SVG standard allows only for 'use' nodes that point directly to a basic shape. if (node->type == SvgNodeType::Use) { if (node->child.count != 1) return false; auto child = *(node->child.data); auto finalTransform = tvg::identity(); if (node->transform) finalTransform = *node->transform; if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { finalTransform *= {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; } if (child->transform) finalTransform *= *child->transform; return _appendClipShape(loaderData, child, shape, vBox, svgPath, tvg::identity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform); } return _appendClipShape(loaderData, node, shape, vBox, svgPath, nullptr); } static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const SvgNode* compNode, SvgNodeType type) { auto m = tvg::identity(); //The initial mask transformation ignored according to the SVG standard. if (node->transform && type != SvgNodeType::Mask) { m = *node->transform; } if (compNode->transform) { m *= *compNode->transform; } if (!compNode->node.clip.userSpace) { auto bbox = _bounds(paint); m *= {bbox.w, 0, bbox.x, 0, bbox.h, bbox.y, 0, 0, 1}; } return m; } static bool _applyClip(SvgLoaderData& loaderData, Paint* paint, const SvgNode* node, const SvgNode* clipNode, const Box& vBox, const string& svgPath) { node->style->clipPath.applying = true; auto clipper = Shape::gen(); auto valid = false; //Composite only when valid shapes exist ARRAY_FOREACH(p, clipNode->child) { if (_appendClipChild(loaderData, *p, clipper, vBox, svgPath)) valid = true; } if (valid) { Matrix finalTransform = _compositionTransform(paint, node, clipNode, SvgNodeType::ClipPath); clipper->transform(finalTransform); paint->clip(clipper); } else { Paint::rel(clipper); } node->style->clipPath.applying = false; return valid; } static Paint* _applyComposition(SvgLoaderData& loaderData, Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath) { if (node->style->clipPath.applying || node->style->mask.applying) { TVGLOG("SVG", "Multiple composition tried! Check out circular dependency?"); return paint; } auto clipNode = node->style->clipPath.node; auto maskNode = node->style->mask.node; if (!clipNode && !maskNode) return paint; if ((clipNode && clipNode->child.empty()) || (maskNode && maskNode->child.empty())) { Paint::rel(paint); return nullptr; } auto scene = Scene::gen(); scene->add(paint); if (clipNode) { if (!_applyClip(loaderData, scene, node, clipNode, vBox, svgPath)) { Paint::rel(scene); return nullptr; } } /* Mask */ if (maskNode) { node->style->mask.applying = true; if (auto mask = _sceneBuildHelper(loaderData, maskNode, vBox, svgPath, true, 0)) { if (!maskNode->node.mask.userSpace) { Matrix finalTransform = _compositionTransform(paint, node, maskNode, SvgNodeType::Mask); mask->transform(finalTransform); } else if (node->transform) { mask->transform(*node->transform); } scene->mask(mask, maskNode->node.mask.type == SvgMaskType::Luminance ? MaskMethod::Luma: MaskMethod::Alpha); } node->style->mask.applying = false; } return scene; } static Paint* _applyFilter(SvgLoaderData& loaderData, Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath) { auto filterNode = node->style->filter.node; if (!filterNode || filterNode->child.count == 0) return paint; auto& filter = filterNode->node.filter; auto scene = Scene::gen(); auto bbox = _bounds(paint); Box clipBox = filter.filterUserSpace ? filter.box : Box{bbox.x + filter.box.x * bbox.w, bbox.y + filter.box.y * bbox.h, filter.box.w * bbox.w, filter.box.h * bbox.h}; auto primitiveUserSpace = filter.primitiveUserSpace; auto sx = paint->transform().e11; auto sy = paint->transform().e22; auto child = filterNode->child.data; for (uint32_t i = 0; i < filterNode->child.count; ++i, ++child) { if ((*child)->type == SvgNodeType::GaussianBlur) { auto& gauss = (*child)->node.gaussianBlur; auto direction = gauss.stdDevX > 0.0f ? (gauss.stdDevY > 0.0f ? 0 : 1) : (gauss.stdDevY > 0.0f ? 2 : -1); if (direction == -1) continue; auto stdDevX = gauss.stdDevX; auto stdDevY = gauss.stdDevY; if (gauss.hasBox) { auto gaussBox = gauss.box; auto isPercent = gauss.isPercentage; if (primitiveUserSpace) { if (isPercent[0]) gaussBox.x *= loaderData.svgParse->global.w; if (isPercent[1]) gaussBox.y *= loaderData.svgParse->global.h; if (isPercent[2]) gaussBox.w *= loaderData.svgParse->global.w; if (isPercent[3]) gaussBox.h *= loaderData.svgParse->global.h; } else { stdDevX *= bbox.w; stdDevY *= bbox.h; if (isPercent[0]) gaussBox.x = bbox.x + gauss.box.x * bbox.w; if (isPercent[1]) gaussBox.y = bbox.y + gauss.box.y * bbox.h; if (isPercent[2]) gaussBox.w *= bbox.w; if (isPercent[3]) gaussBox.h *= bbox.h; } clipBox.intersect(gaussBox); } else if (!primitiveUserSpace) { stdDevX *= bbox.w; stdDevY *= bbox.h; } scene->add(SceneEffect::GaussianBlur, (double)(1.25f * (direction == 2 ? stdDevY * sy : stdDevX * sx)), direction, gauss.edgeModeWrap, 55); } } scene->add(paint); auto clip = Shape::gen(); clip->appendRect(clipBox.x, clipBox.y, clipBox.w, clipBox.h); scene->clip(clip); return scene; } static Paint* _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip) { SvgStyleProperty* style = node->style; //Clip transformation is applied directly to the path in the _appendClipShape function if (node->type == SvgNodeType::Doc || !node->style->display) return vg; //If fill property is nullptr then do nothing if (style->fill.paint.none) { //Do nothing } else if (style->fill.paint.gradient) { auto bBox = style->fill.paint.gradient->userSpace ? vBox : _bounds(vg); if (style->fill.paint.gradient->type == SvgGradientType::Linear) { vg->fill(_applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity)); } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { vg->fill(_applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity)); } } else if (style->fill.paint.url) { TVGLOG("SVG", "The fill's url not supported."); } else if (style->fill.paint.curColor) { //Apply the current style color vg->fill(style->color.r, style->color.g, style->color.b, style->fill.opacity); } else { //Apply the fill color vg->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b, style->fill.opacity); } vg->fillRule(style->fill.fillRule); vg->order(!style->paintOrder); vg->opacity(style->opacity); if (node->type == SvgNodeType::G || node->type == SvgNodeType::Use) return vg; //Apply the stroke style property vg->strokeWidth(style->stroke.width); vg->strokeCap(style->stroke.cap); vg->strokeJoin(style->stroke.join); vg->strokeMiterlimit(style->stroke.miterlimit); vg->strokeDash(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset); //If stroke property is nullptr then do nothing if (style->stroke.paint.none) { vg->strokeWidth(0.0f); } else if (style->stroke.paint.gradient) { auto bBox = style->stroke.paint.gradient->userSpace ? vBox : _bounds(vg); if (style->stroke.paint.gradient->type == SvgGradientType::Linear) { vg->strokeFill(_applyLinearGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity)); } else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) { vg->strokeFill(_applyRadialGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity)); } } else if (style->stroke.paint.url) { //TODO: Apply the color pointed by url TVGLOG("SVG", "The stroke's url not supported."); } else if (style->stroke.paint.curColor) { //Apply the current style color vg->strokeFill(style->color.r, style->color.g, style->color.b, style->stroke.opacity); } else { //Apply the stroke color vg->strokeFill(style->stroke.paint.color.r, style->stroke.paint.color.g, style->stroke.paint.color.b, style->stroke.opacity); } //apply transform after the local space shape bbox for gradient acquisition if (node->transform && !clip) vg->transform(*node->transform); auto p = _applyFilter(loaderData, vg, node, vBox, svgPath); return _applyComposition(loaderData, p, node, vBox, svgPath); } static bool _recognizeShape(SvgNode* node, Shape* shape) { switch (node->type) { case SvgNodeType::Path: { if (node->node.path.path) { if (!svgPathToShape(node->node.path.path, to(shape)->rs.path)) { TVGERR("SVG", "Invalid path information."); return false; } } break; } case SvgNodeType::Ellipse: { _appendCircle(shape, node->node.ellipse.cx, node->node.ellipse.cy, node->node.ellipse.rx, node->node.ellipse.ry); break; } case SvgNodeType::Polygon: { if (node->node.polygon.pts.count < 2) break; auto pts = node->node.polygon.pts.begin(); shape->moveTo(pts[0], pts[1]); for (pts += 2; pts < node->node.polygon.pts.end(); pts += 2) { shape->lineTo(pts[0], pts[1]); } shape->close(); break; } case SvgNodeType::Polyline: { if (node->node.polyline.pts.count < 2) break; auto pts = node->node.polyline.pts.begin(); shape->moveTo(pts[0], pts[1]); for (pts += 2; pts < node->node.polyline.pts.end(); pts += 2) { shape->lineTo(pts[0], pts[1]); } break; } case SvgNodeType::Circle: { _appendCircle(shape, node->node.circle.cx, node->node.circle.cy, node->node.circle.r, node->node.circle.r); break; } case SvgNodeType::Rect: { _appendRect(shape, node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, node->node.rect.rx, node->node.rect.ry); break; } case SvgNodeType::Line: { shape->moveTo(node->node.line.x1, node->node.line.y1); shape->lineTo(node->node.line.x2, node->node.line.y2); break; } default: { return false; } } return true; } static Paint* _shapeBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath) { auto shape = Shape::gen(); if (!_recognizeShape(node, shape)) return nullptr; return _applyProperty(loaderData, node, shape, vBox, svgPath, false); } static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform) { uint32_t currentPtsCnt; shape->path(nullptr, nullptr, nullptr, ¤tPtsCnt); if (!_recognizeShape(node, shape)) return false; //The 'transform' matrix has higher priority than the node->transform, since it already contains it auto m = transform ? transform : (node->transform ? node->transform : nullptr); if (m) { const Point *pts; uint32_t ptsCnt; shape->path(nullptr, nullptr, &pts, &ptsCnt); auto p = const_cast(pts) + currentPtsCnt; while (currentPtsCnt++ < ptsCnt) { *p *= *m; ++p; } } //Apply Clip Chaining if (auto clipNode = node->style->clipPath.node) { if (clipNode->child.count == 0) return false; if (node->style->clipPath.applying) { TVGLOG("SVG", "Multiple composition tried! Check out circular dependency?"); return false; } return _applyClip(loaderData, shape, node, clipNode, vBox, svgPath); } return true; } enum class imageMimeTypeEncoding { base64 = 0x1, utf8 = 0x2 }; constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return static_cast(static_cast(a) | static_cast(b)); } constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return (static_cast(a) & static_cast(b)); } static constexpr struct { const char* name; int sz; imageMimeTypeEncoding encoding; } imageMimeTypes[] = { {"jpeg", sizeof("jpeg"), imageMimeTypeEncoding::base64}, {"png", sizeof("png"), imageMimeTypeEncoding::base64}, {"webp", sizeof("webp"), imageMimeTypeEncoding::base64}, {"svg+xml", sizeof("svg+xml"), imageMimeTypeEncoding::base64 | imageMimeTypeEncoding::utf8}, }; static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mimetype, imageMimeTypeEncoding* encoding) { if (strncmp(*href, "image/", sizeof("image/") - 1)) return false; //not allowed mime type *href += sizeof("image/") - 1; //RFC2397 data:[][;base64], //mediatype := [ type "/" subtype ] *( ";" parameter ) //parameter := attribute "=" value for (unsigned int i = 0; i < sizeof(imageMimeTypes) / sizeof(imageMimeTypes[0]); i++) { if (strncmp(*href, imageMimeTypes[i].name, imageMimeTypes[i].sz - 1)) continue; *href += imageMimeTypes[i].sz - 1; *mimetype = imageMimeTypes[i].name; while (**href && **href != ',') { while (**href && **href != ';') ++(*href); if (!**href) return false; ++(*href); if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::base64) { if (!strncmp(*href, "base64,", sizeof("base64,") - 1)) { *href += sizeof("base64,") - 1; *encoding = imageMimeTypeEncoding::base64; return true; //valid base64 } } if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8) { if (!strncmp(*href, "utf8,", sizeof("utf8,") - 1)) { *href += sizeof("utf8,") - 1; *encoding = imageMimeTypeEncoding::utf8; return true; //valid utf8 } } } //no encoding defined if (**href == ',' && (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8)) { ++(*href); *encoding = imageMimeTypeEncoding::utf8; return true; //allow no encoding defined if utf8 expected } return false; } return false; } #include "tvgTaskScheduler.h" static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath) { if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr; auto picture = Picture::gen(); const char* href = node->node.image.href; if (!strncmp(href, "data:", sizeof("data:") - 1)) { href += sizeof("data:") - 1; const char* mimetype; imageMimeTypeEncoding encoding; if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding char *decoded = nullptr; if (encoding == imageMimeTypeEncoding::base64) { auto size = b64Decode(href, strlen(href), &decoded); if (picture->load(decoded, size, mimetype) != Result::Success) { tvg::free(decoded); return nullptr; } } else { auto size = svgUtilURLDecode(href, &decoded); if (picture->load(decoded, size, mimetype) != Result::Success) { tvg::free(decoded); return nullptr; } } loaderData.images.push(decoded); } else { if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1; //TODO: protect against recursive svg image loading //Temporarily disable embedded svg: const char *dot = strrchr(href, '.'); if (dot && STR_AS(dot, ".svg")) { TVGLOG("SVG", "Embedded svg file is disabled."); return nullptr; } string imagePath = href; if (strncmp(href, "/", 1)) { auto last = svgPath.find_last_of("/"); imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath; } if (picture->load(imagePath.c_str()) != Result::Success) { return nullptr; } } float w, h; Matrix m; if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) { auto sx = node->node.image.w / w; auto sy = node->node.image.h / h; m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1}; } else { m = tvg::identity(); } if (node->transform) m = *node->transform * m; picture->transform(m); auto p = _applyFilter(loaderData, picture, node, vBox, svgPath); return _applyComposition(loaderData, p, node, vBox, svgPath); } static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, float width, float height, const Box& box) { auto sx = width / box.w; auto sy = height / box.h; auto tvx = box.x * sx; auto tvy = box.y * sy; if (align == AspectRatioAlign::None) return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; //Scale if (meetOrSlice == AspectRatioMeetOrSlice::Meet) { if (sx < sy) sy = sx; else sx = sy; } else { if (sx < sy) sx = sy; else sy = sx; } //Align tvx = box.x * sx; tvy = box.y * sy; auto tvw = box.w * sx; auto tvh = box.h * sy; switch (align) { case AspectRatioAlign::XMinYMin: { break; } case AspectRatioAlign::XMidYMin: { tvx -= (width - tvw) * 0.5f; break; } case AspectRatioAlign::XMaxYMin: { tvx -= width - tvw; break; } case AspectRatioAlign::XMinYMid: { tvy -= (height - tvh) * 0.5f; break; } case AspectRatioAlign::XMidYMid: { tvx -= (width - tvw) * 0.5f; tvy -= (height - tvh) * 0.5f; break; } case AspectRatioAlign::XMaxYMid: { tvx -= width - tvw; tvy -= (height - tvh) * 0.5f; break; } case AspectRatioAlign::XMinYMax: { tvy -= height - tvh; break; } case AspectRatioAlign::XMidYMax: { tvx -= (width - tvw) * 0.5f; tvy -= height - tvh; break; } case AspectRatioAlign::XMaxYMax: { tvx -= width - tvw; tvy -= height - tvh; break; } default: { break; } } return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; } static Scene* _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth) { auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1); // mUseTransform = mUseTransform * mTranslate auto mUseTransform = tvg::identity(); if (node->transform) mUseTransform = *node->transform; if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; mUseTransform *= mTranslate; } if (node->node.use.symbol) { auto symbol = node->node.use.symbol->node.symbol; auto width = (symbol.hasWidth ? symbol.w : vBox.w); if (node->node.use.isWidthSet) width = node->node.use.w; auto height = (symbol.hasHeight ? symbol.h : vBox.h);; if (node->node.use.isHeightSet) height = node->node.use.h; auto vw = (symbol.hasViewBox ? symbol.vw : width); auto vh = (symbol.hasViewBox ? symbol.vh : height); auto mViewBox = tvg::identity(); if ((!tvg::equal(width, vw) || !tvg::equal(height, vh)) && vw > 0 && vh > 0) { Box box = {symbol.vx, symbol.vy, vw, vh}; mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box); } else if (!tvg::zero(symbol.vx) || !tvg::zero(symbol.vy)) { mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1}; } // mSceneTransform = mUseTransform * mSymbolTransform * mViewBox Matrix mSceneTransform = mViewBox; if (node->node.use.symbol->transform) { mSceneTransform = *node->node.use.symbol->transform * mViewBox; } mSceneTransform = mUseTransform * mSceneTransform; scene->transform(mSceneTransform); if (!node->node.use.symbol->node.symbol.overflowVisible) { auto viewBoxClip = Shape::gen(); viewBoxClip->appendRect(0, 0, width, height); // mClipTransform = mUseTransform * mSymbolTransform Matrix mClipTransform = mUseTransform; if (node->node.use.symbol->transform) { mClipTransform = mUseTransform * *node->node.use.symbol->transform; } viewBoxClip->transform(mClipTransform); auto clippingLayer = Scene::gen(); clippingLayer->clip(viewBoxClip); clippingLayer->add(scene); return clippingLayer; } return scene; } if (auto clipper = PAINT(scene)->clipper) { auto& clipTransform = clipper->transform(); Matrix inv; if (node->transform && inverse(node->transform, &inv)) clipTransform = inv * clipTransform; clipTransform = mUseTransform * clipTransform ; } scene->transform(mUseTransform); return scene; } static void _applyTextFill(SvgStyleProperty* style, Text* text, const Box& vBox) { //If fill property is nullptr then do nothing if (style->fill.paint.none) { //Do nothing } else if (style->fill.paint.gradient) { auto bBox = style->fill.paint.gradient->userSpace ? vBox : _bounds(text); if (style->fill.paint.gradient->type == SvgGradientType::Linear) { text->fill(_applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity)); } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { text->fill(_applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity)); } } else if (style->fill.paint.url) { //TODO: Apply the color pointed by url TVGLOG("SVG", "The fill's url not supported."); } else if (style->fill.paint.curColor) { //Apply the current style color text->fill(style->color.r, style->color.g, style->color.b); text->opacity(style->fill.opacity); } else { //Apply the fill color text->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b); text->opacity(style->fill.opacity); } } static char* _processText(const char* text, SvgXmlSpace space) { if (!text) return nullptr; auto len = strlen(text); auto processed = (char*)tvg::malloc(len + 1); auto dst = processed; auto src = text; if (space == SvgXmlSpace::Preserve) { while (*src) { if (*src == '\n' || *src == '\t' || *src == '\r') *dst++ = ' '; else *dst++ = *src; src++; } *dst = '\0'; } else { auto spaceFound = false; src = svgUtilSkipWhiteSpace(src, nullptr); while (*src) { if (isspace((unsigned char)*src)) { if (!spaceFound) { *dst++ = ' '; spaceFound = true; } } else { *dst++ = *src; spaceFound = false; } src++; } dst = (char*)svgUtilUnskipWhiteSpace(dst, processed); *dst = '\0'; } return processed; } static Paint* _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath) { auto textNode = &node->node.text; if (!textNode->text) return nullptr; auto text = Text::gen(); Matrix textTransform; if (node->transform) textTransform = *node->transform; else textTransform = tvg::identity(); translateR(&textTransform, {node->node.text.x, node->node.text.y - textNode->fontSize}); text->transform(textTransform); //TODO: handle def values of font and size as used in a system? auto size = textNode->fontSize * 0.75f; //1 pt = 1/72; 1 in = 96 px; -> 72/96 = 0.75 if (text->font(textNode->fontFamily) != Result::Success) { text->font(nullptr); //fallback to any available font } text->size(size); // Handle xml:space auto xmlSpace = node->xmlSpace; auto parent = node->parent; while (xmlSpace == SvgXmlSpace::None && parent) { xmlSpace = parent->xmlSpace; parent = parent->parent; } if (xmlSpace == SvgXmlSpace::None) xmlSpace = SvgXmlSpace::Default; auto processedText = _processText(textNode->text, xmlSpace); text->text(processedText); tvg::free(processedText); _applyTextFill(node->style, text, vBox); auto p = _applyFilter(loaderData, text, node, vBox, svgPath); return _applyComposition(loaderData, p, node, vBox, svgPath); } static Scene* _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth) { /* Exception handling: Prevent invalid SVG data input. The size is the arbitrary value, we need an experimental size. */ if (depth > 2192) { TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); return nullptr; } if (!_isGroupType(node->type) && !mask) return nullptr; auto scene = Scene::gen(); // For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper() if (!mask && node->transform && node->type != SvgNodeType::Symbol && node->type != SvgNodeType::Use) { scene->transform(*node->transform); } if (!node->style->display || node->style->opacity == 0) return scene; ARRAY_FOREACH(p, node->child) { auto child = *p; if (_isGroupType(child->type)) { if (child->type == SvgNodeType::Use) scene->add(_useBuildHelper(loaderData, child, vBox, svgPath, depth + 1)); else if (!(child->type == SvgNodeType::Symbol && node->type != SvgNodeType::Use)) scene->add(_sceneBuildHelper(loaderData, child, vBox, svgPath, false, depth + 1)); if (child->id) scene->id = djb2Encode(child->id); } else { Paint* paint = nullptr; if (child->type == SvgNodeType::Image) paint = _imageBuildHelper(loaderData, child, vBox, svgPath); else if (child->type == SvgNodeType::Text) paint = _textBuildHelper(loaderData, child, vBox, svgPath); else if (child->type != SvgNodeType::Mask) paint = _shapeBuildHelper(loaderData, child, vBox, svgPath); if (paint) { if (child->id) paint->id = djb2Encode(child->id); scene->add(paint); } } } scene->opacity(node->style->opacity); auto p = _applyFilter(loaderData, scene, node, vBox, svgPath); return static_cast(_applyComposition(loaderData, p, node, vBox, svgPath)); } static void _updateInvalidViewSize(Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag) { auto useW = (viewFlag & SvgViewFlag::Width); auto useH = (viewFlag & SvgViewFlag::Height); auto bbox = _bounds(scene); if (!useW && !useH) { vBox = bbox; } else { vBox.w = useW ? w : bbox.w; vBox.h = useH ? h : bbox.h; } //the size would have 1x1 or percentage values. if (!useW) w *= vBox.w; if (!useH) h *= vBox.h; } static void _loadFonts(Array& fonts) { if (fonts.empty()) return; static constexpr struct { const char* prefix; size_t len; } prefixes[] = { {"data:font/ttf;base64,", sizeof("data:font/ttf;base64,") - 1}, {"data:application/font-ttf;base64,", sizeof("data:application/font-ttf;base64,") - 1} }; ARRAY_FOREACH(p, fonts) { if (!p->name) continue; size_t shift = 0; for (const auto& prefix : prefixes) { if (p->srcLen > prefix.len && !memcmp(p->src, prefix.prefix, prefix.len)) { shift = prefix.len; break; } } if (shift == 0) { TVGLOG("SVG", "The embedded font \"%s\" data not loaded properly.", p->name); continue; } auto size = b64Decode(p->src + shift, p->srcLen - shift, &p->decoded); if (Text::load(p->name, p->decoded, size) != Result::Success) TVGERR("SVG", "Error while loading the ttf font named \"%s\".", p->name); } } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag) { //TODO: aspect ratio is valid only if viewBox was set if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr; _loadFonts(loaderData.fonts); auto docNode = _sceneBuildHelper(loaderData, loaderData.doc, vBox, svgPath, false, 0); if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode, vBox, w, h, viewFlag); if (!tvg::equal(w, vBox.w) || !tvg::equal(h, vBox.h)) { Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox); docNode->transform(m); } else if (!tvg::zero(vBox.x) || !tvg::zero(vBox.y)) { docNode->translate(-vBox.x, -vBox.y); } auto viewBoxClip = Shape::gen(); viewBoxClip->appendRect(0, 0, w, h); auto clippingLayer = Scene::gen(); clippingLayer->clip(viewBoxClip); clippingLayer->add(docNode); loaderData.doc->node.doc.vbox = vBox; loaderData.doc->node.doc.w = w; loaderData.doc->node.doc.h = h; auto root = Scene::gen(); root->add(clippingLayer); return root; } src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-urierror-prototype.inc.h000664 001750 001750 00000002455 15164251010 052533 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * UriError.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.7.8 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_URI_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.9 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_URI_ERROR_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.10 */ STRING_VALUE (LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/CONTRIBUTING.md000664 001750 001750 00000007715 15164251010 031460 0ustar00ddennedyddennedy000000 000000 We always appreciate your contribution. ThorVG doesn't expect patches to be perfect; instead, we value contributions that make ThorVG better than before. This page outlines the ThorVG contribution format.

## Coding Convention Please read the [ThorVG Coding Convention Guidance](https://github.com/thorvg/thorvg/wiki/Coding-Convention) before writing your code. ## Reviewers ThorVG uses GitHub infrastructure to automatically assign code reviewers for your changes. To see the full list of reviewers, please refer to the [CODEOWNERS](https://github.com/thorvg/thorvg/blob/main/CODEOWNERS) file.
## Self Test & Verification After updating the ThorVG code, please ensure your changes don't break the library. We recommend conducting unit tests. You can easily run them using the following build commands:
` $meson setup build -Dtests=true -Dloaders="all" -Dsavers="all" -Dbindings="capi" -Dtools="all" -Dlog=true `
` $ninja -C build test `

Please make it sure running all tests and no any fail case.

Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
## Commit Message [Module][Feature]: [Title] [Description] - [Module] refers to the sub-module primarily affected by your change. Most of the time, this indicates the name of a sub-folder. This also suggests who might need to review your patch. If your change doesn't belong to any sub-modules, you can either replace this with a suitable name or skip it. The name should be written entirely in lowercase letters. - e.g., build, doc, infra, common, sw_engine, gl_engine, svg_loader, wasm, svg2png... - [Feature] indicates the primary function or feature you modified. Typically, this represents a class or file name. This is an optional. - e.g., canvas, shape, paint, scene, picture, task-scheduler, loader, builder, ... - [Title] provides a brief description of your change and should be encapsulated in a single sentence. - e.g., "Fixed a typo." - e.g., "Addressed compile warnings." - e.g., "Refactored code." - e.g., "Resolved a rendering bug causing overlapped shapes to display improperly." - [Description] doesn't have a strict format. However, it should detail what you accomplished in this patch as comprehensively as possible. If you've resolved bugs, include the following details: - The type of bug. - Steps to reproduce it. - The root cause. - The solution applied. For new features or functions, cover: - The nature of the feature. - Full API specifications (if there are any API additions). - Its necessity or rationale. - Any conditions or restrictions. - Relevant examples or references. Finally, include any related issue ticket numbers in this section, if applicable. Here's a sample commit message for clarity: - renderer/paint: Introduced path clipping feature We've added a new method, Paint::composite(), to support various composite behaviors. This allows paints to interact with other paint instances based on the chosen composite method. Initially, we're introducing the "ClipPath" method for path-based clipping. targetPaint->composite(srcPaint, CompositeMethod::ClipPath); Note: If the source paint lacks path information, clipping may not produce the desired effect. API Additions: enum CompositeMethod {None = 0, ClipPath}; Result Paint::composite(std::unique_ptr target, CompositeMethod method) const noexcept; Examples: Introduced ClipPath References: [Provide any relevant links, such as screenshots.] Issues: [Link to the issue] ## Pull Request Once you've submitted a pull request (PR), please ensure the following checklist is completed: - Reviewers: Check the Reviewers List. - Assignees: You should be assigned. - Labels: Mark the appropriate Patch Purpose. - Automated Integration Test: All must pass.

loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-arraybuffer-prototype.cpp000664 001750 001750 00000006535 15164251010 052760 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-arraybuffer-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jrt-libc-includes.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-arraybuffer-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID arraybuffer_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup arraybufferprototype ECMA ArrayBuffer.prototype object built-in * @{ */ /** * The ArrayBuffer.prototype.bytelength accessor * * See also: * ES2015, 24.1.4.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_arraybuffer_prototype_bytelength_getter (ecma_value_t this_arg) /**< this argument */ { if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER)) { if (ecma_arraybuffer_is_detached (object_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint32_t len = ecma_arraybuffer_get_length (object_p); return ecma_make_uint32_value (len); } } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_ARRAY_BUFFER_OBJECT); } /* ecma_builtin_arraybuffer_prototype_bytelength_getter */ /** * The ArrayBuffer.prototype object's 'slice' routine * * See also: * ECMA-262 v11, 24.1.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_arraybuffer_prototype_object_slice (ecma_value_t this_arg, /**< this argument */ const ecma_value_t *argument_list_p, /**< arguments list */ uint32_t arguments_number) /**< number of arguments */ { if (!ecma_is_value_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *object_p = ecma_get_object_from_value (this_arg); /* 2. */ if (!ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_ARRAY_BUFFER_OBJECT); } return ecma_builtin_arraybuffer_slice (this_arg, argument_list_p, arguments_number); } /* ecma_builtin_arraybuffer_prototype_object_slice */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/cursorstreamwrapper.h000664 001750 001750 00000004324 15164251010 041155 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules// 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. // // 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 > class CursorStreamWrapper : public GenericStreamWrapper { public: typedef typename Encoding::Ch Ch; CursorStreamWrapper(InputStream& is): GenericStreamWrapper(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_ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-date.cpp000664 001750 001750 00000057523 15164251010 047325 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "jcontext.h" #include "lit-char-helpers.h" #if JERRY_BUILTIN_DATE #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_DATE_ROUTINE_START = 0, ECMA_DATE_ROUTINE_PARSE, ECMA_DATE_ROUTINE_UTC, ECMA_DATE_ROUTINE_NOW, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-date.inc.h" #define BUILTIN_UNDERSCORED_ID date #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup date ECMA Date object built-in * @{ */ /** * Encode minimum/maximum limits * * See: ecma_date_parse_date_chars * * @param min: 8 bits unsigned number * @param max: 24 bits unsigned number */ #define ECMA_DATE_LIMIT(min, max) (min << 24 | max) /** * Decode the minimum value from the encoded limit */ #define ECMA_DATE_LIMIT_MIN(limit) (limit >> 24) /** * Decode the maximum value from the encoded limit */ #define ECMA_DATE_LIMIT_MAX(limit) (limit & ((1 << 24) - 1)) /** * Helper function to try to parse a part of a date string * * @return NaN if cannot read from string, ToNumber() otherwise */ static ecma_number_t ecma_date_parse_date_chars (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ const lit_utf8_byte_t *str_end_p, /**< pointer to the end of the string */ uint32_t num_of_chars, /**< number of characters to read and convert */ uint32_t limit) /**< minimum/maximum valid value */ { JERRY_ASSERT (num_of_chars > 0 && num_of_chars <= 6); if (*str_p + num_of_chars > str_end_p) { return ecma_number_make_nan (); } str_end_p = *str_p + num_of_chars; uint32_t num = 0; while (num_of_chars--) { lit_utf8_byte_t c = **str_p; if (!lit_char_is_decimal_digit (c)) { return ecma_number_make_nan (); } num = (num * 10) + (uint32_t) ((c - LIT_CHAR_0)); (*str_p)++; } if (num >= ECMA_DATE_LIMIT_MIN (limit) && num <= ECMA_DATE_LIMIT_MAX (limit)) { return (ecma_number_t) num; } return ecma_number_make_nan (); } /* ecma_date_parse_date_chars */ /** * Helper function to try to parse a special character (+,-,T,Z,:,.) in a date string * * @return true if the first character is same as the expected, false otherwise */ static bool ecma_date_parse_special_char (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ const lit_utf8_byte_t *str_end_p, /**< pointer to the end of the string */ ecma_char_t expected_char) /**< expected character */ { if ((*str_p < str_end_p) && (**str_p == expected_char)) { (*str_p)++; return true; } return false; } /* ecma_date_parse_special_char */ static inline bool ecma_date_check_two_chars (const lit_utf8_byte_t *str_p, /**< pointer to the cesu8 string */ const lit_utf8_byte_t *str_end_p, /**< pointer to the end of the string */ ecma_char_t expected_char1, /**< first expected character */ ecma_char_t expected_char2) /**< second expected character */ { return (str_p < str_end_p && (*str_p == expected_char1 || *str_p == expected_char2)); } /* ecma_date_check_two_chars */ /** * Helper function to try to parse a 4-5-6 digit year with optional negative sign in a date string * * Date.prototype.toString() and Date.prototype.toUTCString() emits year * in this format and Date.parse() should parse this format too. * * @return the parsed year or NaN. */ static ecma_number_t ecma_date_parse_year (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ const lit_utf8_byte_t *str_end_p) /**< pointer to the end of the string */ { bool is_year_sign_negative = ecma_date_parse_special_char (str_p, str_end_p, LIT_CHAR_MINUS); const lit_utf8_byte_t *str_start_p = *str_p; int32_t parsed_year = 0; while ((str_start_p - *str_p < 6) && (str_start_p < str_end_p) && lit_char_is_decimal_digit (*str_start_p)) { parsed_year = 10 * parsed_year + *str_start_p - LIT_CHAR_0; str_start_p++; } if (str_start_p - *str_p >= 4) { *str_p = str_start_p; return is_year_sign_negative ? -parsed_year : parsed_year; } return ecma_number_make_nan (); } /* ecma_date_parse_year */ /** * Helper function to try to parse a day name in a date string * Valid day names: Sun, Mon, Tue, Wed, Thu, Fri, Sat * See also: * ECMA-262 v9, 20.3.4.41.2 Table 46 * * @return true if the string starts with a valid day name, false otherwise */ static bool ecma_date_parse_day_name (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ const lit_utf8_byte_t *str_end_p) /**< pointer to the end of the string */ { if (*str_p + 3 < str_end_p) { for (uint32_t i = 0; i < 7; i++) { if (!memcmp (day_names_p[i], *str_p, 3)) { (*str_p) += 3; return true; } } } return false; } /* ecma_date_parse_day_name */ /** * Helper function to try to parse a month name in a date string * Valid month names: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec * See also: * ECMA-262 v9, 20.3.4.41.2 Table 47 * * @return number of the month if the string starts with a valid month name, 0 otherwise */ static uint32_t ecma_date_parse_month_name (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ const lit_utf8_byte_t *str_end_p) /**< pointer to the end of the string */ { if (*str_p + 3 < str_end_p) { for (uint32_t i = 0; i < 12; i++) { if (!memcmp (month_names_p[i], *str_p, 3)) { (*str_p) += 3; return (i + 1); } } } return 0; } /* ecma_date_parse_month_name */ /** * Calculate MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)) for Date constructor and UTC * * See also: * ECMA-262 v11, 20.4.3.4 * * @return false - if the operation fails * true - otherwise */ static bool ecma_date_construct_helper (const ecma_value_t *args, /**< arguments passed to the Date constructor */ uint32_t args_len, /**< number of arguments */ ecma_number_t *tv_p) /**< [out] time value */ { ecma_number_t date_nums[7] = { ECMA_NUMBER_ZERO, /* year */ ECMA_NUMBER_ZERO, /* month */ ECMA_NUMBER_ONE, /* date */ ECMA_NUMBER_ZERO, /* hours */ ECMA_NUMBER_ZERO, /* minutes */ ECMA_NUMBER_ZERO, /* seconds */ ECMA_NUMBER_ZERO /* milliseconds */ }; args_len = JERRY_MIN (args_len, sizeof (date_nums) / sizeof (date_nums[0])); /* 1-7. */ for (uint32_t i = 0; i < args_len; i++) { ecma_value_t status = ecma_op_to_number (args[i], date_nums + i); if (ECMA_IS_VALUE_ERROR (status)) { return false; } } /* 8. */ if (!ecma_number_is_nan (date_nums[0])) { /* 9.a */ ecma_number_t yi = ecma_number_trunc (date_nums[0]); /* 9.b */ if (yi >= 0 && yi <= 99) { date_nums[0] = 1900 + yi; } } /* 10. */ *tv_p = ecma_date_make_date (ecma_date_make_day (date_nums[0], date_nums[1], date_nums[2]), ecma_date_make_time (date_nums[3], date_nums[4], date_nums[5], date_nums[6])); return true; } /* ecma_date_construct_helper */ /** * Helper function used by ecma_builtin_date_parse * * See also: * ECMA-262 v5, 15.9.4.2 Date.parse (string) * ECMA-262 v5, 15.9.1.15 Date Time String Format * * @return the parsed date as ecma_number_t or NaN otherwise */ static ecma_number_t ecma_builtin_date_parse_basic (const lit_utf8_byte_t *date_str_curr_p, /**< date string start */ const lit_utf8_byte_t *date_str_end_p) /**< date string end */ { /* 1. read year */ uint32_t year_digits = 4; uint32_t year_limit = 9999; bool is_year_sign_negative = false; if (ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_MINUS, LIT_CHAR_PLUS)) { is_year_sign_negative = (*date_str_curr_p++ == LIT_CHAR_MINUS); year_digits = 6; year_limit = 999999; } ecma_number_t year = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, year_digits, ECMA_DATE_LIMIT (0, year_limit)); if (is_year_sign_negative) { year = -year; } if (ecma_number_is_nan (year)) { return year; } ecma_number_t month = ECMA_NUMBER_ONE; ecma_number_t day = ECMA_NUMBER_ONE; ecma_number_t time = ECMA_NUMBER_ZERO; /* 2. read month if any */ if (ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_MINUS, LIT_CHAR_SLASH)) { lit_utf8_byte_t separator = *date_str_curr_p++; month = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (1, 12)); /* 3. read day if any */ if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, separator)) { day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (1, 31)); } } bool is_utc = true; /* 4. read time if any */ if (ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_T, LIT_CHAR_SP)) { date_str_curr_p++; ecma_number_t hours = ECMA_NUMBER_ZERO; ecma_number_t minutes = ECMA_NUMBER_ZERO; ecma_number_t seconds = ECMA_NUMBER_ZERO; ecma_number_t milliseconds = ECMA_NUMBER_ZERO; /* 'HH:mm' must present */ if (date_str_end_p - date_str_curr_p < 5) { return ecma_number_make_nan (); } /* 4.1 read hours and minutes */ hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 24)); if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) { return ecma_number_make_nan (); } minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); /* 4.2 read seconds if any */ if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) { seconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); /* 4.3 read milliseconds if any */ if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_DOT)) { milliseconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 3, ECMA_DATE_LIMIT (0, 999)); } } if (hours == 24 && (minutes != 0 || seconds != 0 || milliseconds != 0)) { return ecma_number_make_nan (); } time = ecma_date_make_time (hours, minutes, seconds, milliseconds); if (ecma_number_is_nan (time)) { return time; } /* 4.4 read timezone if any */ if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_Z)) { if ((date_str_end_p - date_str_curr_p) == 6 && (*date_str_curr_p == LIT_CHAR_MINUS || *date_str_curr_p == LIT_CHAR_PLUS)) { bool is_timezone_sign_negative = (*date_str_curr_p++ == LIT_CHAR_MINUS); /* read hours and minutes */ hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 24)); if (hours == 24) { hours = ECMA_NUMBER_ZERO; } if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) { return ecma_number_make_nan (); } minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); ecma_number_t timezone_offset = ecma_date_make_time (hours, minutes, ECMA_NUMBER_ZERO, ECMA_NUMBER_ZERO); time += is_timezone_sign_negative ? timezone_offset : -timezone_offset; } else { is_utc = false; } } } if (date_str_curr_p < date_str_end_p) { return ecma_number_make_nan (); } ecma_number_t date = ecma_date_make_day (year, month - 1, day); ecma_number_t result_date = ecma_date_make_date (date, time); if (!is_utc) { result_date = ecma_date_utc (result_date); } return result_date; } /* ecma_builtin_date_parse_basic */ /** * Helper function used by ecma_builtin_date_parse * * See also: * ECMA-262 v5, 15.9.4.2 Date.parse (string) * ECMA-262 v9, 20.3.4.41 Date.prototype.toString () * ECMA-262 v9, 20.3.4.43 Date.prototype.toUTCString () * * Used by: ecma_builtin_date_parse * * @return the parsed date as ecma_number_t or NaN otherwise */ static ecma_number_t ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p, const lit_utf8_byte_t *date_str_end_p) { const ecma_number_t nan = ecma_number_make_nan (); if (!ecma_date_parse_day_name (&date_str_curr_p, date_str_end_p)) { return nan; } const bool is_toUTCString_format = ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COMMA); if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_SP)) { return nan; } ecma_number_t month = 0; ecma_number_t day = 0; if (is_toUTCString_format) { day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 31)); if (ecma_number_is_nan (day)) { return nan; } if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_SP)) { return nan; } month = ecma_date_parse_month_name (&date_str_curr_p, date_str_end_p); if (month == 0) { return ecma_number_make_nan (); } } else { month = ecma_date_parse_month_name (&date_str_curr_p, date_str_end_p); if (month == 0) { return nan; } if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_SP)) { return nan; } day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 31)); if (ecma_number_is_nan (day)) { return nan; } } if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_SP)) { return nan; } ecma_number_t year = ecma_date_parse_year (&date_str_curr_p, date_str_end_p); if (ecma_number_is_nan (year)) { return nan; } if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_SP)) { return nan; } ecma_number_t hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 24)); if (ecma_number_is_nan (hours)) { return nan; } if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) { return nan; } ecma_number_t minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); if (ecma_number_is_nan (minutes)) { return nan; } if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) { return nan; } ecma_number_t seconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); if (ecma_number_is_nan (seconds)) { return nan; } if (hours == 24 && (minutes != 0 || seconds != 0)) { return nan; } const char gmt_p[] = " GMT"; if (date_str_end_p - date_str_curr_p < 4 || memcmp (date_str_curr_p, gmt_p, 4) != 0) { return nan; } date_str_curr_p += 4; ecma_number_t time = ecma_date_make_time (hours, minutes, seconds, 0); if (!is_toUTCString_format) { if (!ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_MINUS, LIT_CHAR_PLUS)) { return nan; } bool is_timezone_sign_negative = (*date_str_curr_p++ == LIT_CHAR_MINUS); hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 24)); if (ecma_number_is_nan (hours)) { return nan; } if (hours == 24) { hours = ECMA_NUMBER_ZERO; } minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); if (ecma_number_is_nan (minutes)) { return nan; } ecma_number_t timezone_offset = ecma_date_make_time (hours, minutes, ECMA_NUMBER_ZERO, ECMA_NUMBER_ZERO); time += is_timezone_sign_negative ? timezone_offset : -timezone_offset; } if (date_str_curr_p < date_str_end_p) { return nan; } ecma_number_t date = ecma_date_make_day (year, month - 1, day); return ecma_date_make_date (date, time); } /* ecma_builtin_date_parse_toString_formats */ /** * The Date object's 'parse' routine * * See also: * ECMA-262 v5, 15.9.4.2 Date.parse (string) * ECMA-262 v5, 15.9.1.15 Date Time String Format * ECMA-262 v9, 20.3.4.41 Date.prototype.toString () * ECMA-262 v9, 20.3.4.43 Date.prototype.toUTCString () * * @return parsed time */ static ecma_number_t ecma_builtin_date_parse (ecma_string_t *string_p) /**< string */ { ECMA_STRING_TO_UTF8_STRING (string_p, str_p, str_size); const lit_utf8_byte_t *date_str_curr_p = str_p; const lit_utf8_byte_t *date_str_end_p = str_p + str_size; /* try to parse date string as ISO string - ECMA-262 v5, 15.9.1.15 */ ecma_number_t tv = ecma_builtin_date_parse_basic (date_str_curr_p, date_str_end_p); if (ecma_number_is_nan (tv)) { /* try to parse date string in Date.prototype.toString() or toUTCString() format */ tv = ecma_builtin_date_parse_toString_formats (date_str_curr_p, date_str_end_p); } ECMA_FINALIZE_UTF8_STRING (str_p, str_size); return tv; } /* ecma_builtin_date_parse */ /** * The Date object's 'UTC' routine * * See also: * ECMA-262 v5, 15.9.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_date_utc (const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { if (args_number < 1) { return ecma_make_nan_value (); } ecma_number_t tv; if (!ecma_date_construct_helper (args, args_number, &tv)) { return ECMA_VALUE_ERROR; } return ecma_make_number_value ((ecma_number_t) ecma_date_time_clip (tv)); } /* ecma_builtin_date_utc */ /** * Helper method to get the current time * * @return ecma_number_t */ static ecma_number_t ecma_builtin_date_now_helper (void) { return floor (DOUBLE_TO_ECMA_NUMBER_T (jerry_port_current_time ())); } /* ecma_builtin_date_now_helper */ /** * Construct a date object with the given [[DateValue]] * * Note: New target must be a valid object * * @return ECMA_VALUE_ERROR - if the operation fails * constructed date object - otherwise */ static ecma_value_t ecma_builtin_date_create (ecma_number_t tv) { JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p) != NULL); ecma_object_t *prototype_obj_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_DATE_PROTOTYPE); if (JERRY_UNLIKELY (prototype_obj_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *obj_p = ecma_create_object (prototype_obj_p, sizeof (ecma_date_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_deref_object (prototype_obj_p); ecma_date_object_t *date_object_p = (ecma_date_object_t *) obj_p; date_object_p->header.u.cls.type = ECMA_OBJECT_CLASS_DATE; date_object_p->header.u.cls.u1.date_flags = ECMA_DATE_TZA_NONE; date_object_p->header.u.cls.u3.tza = 0; date_object_p->date_value = tv; return ecma_make_object_value (obj_p); } /* ecma_builtin_date_create */ /** * Handle calling [[Call]] of built-in Date object * * See also: * ECMA-262 v5, 15.9.2.1 * * @return ecma value */ ecma_value_t ecma_builtin_date_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_UNUSED (arguments_list_p); JERRY_UNUSED (arguments_list_len); return ecma_date_value_to_string (ecma_builtin_date_now_helper ()); } /* ecma_builtin_date_dispatch_call */ /** * Handle calling [[Construct]] of built-in Date object * * See also: * ECMA-262 v5, 15.9.3.1 * ECMA-262 v11, 20.4.2 * * @return ecma value */ ecma_value_t ecma_builtin_date_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { /* 20.4.2.3 */ if (arguments_list_len == 0) { return ecma_builtin_date_create (ecma_builtin_date_now_helper ()); } ecma_number_t tv; /* 20.4.2.2 */ if (arguments_list_len == 1) { ecma_value_t argument = arguments_list_p[0]; /* 4.a */ if (ecma_is_value_object (argument) && ecma_object_class_is (ecma_get_object_from_value (argument), ECMA_OBJECT_CLASS_DATE)) { return ecma_builtin_date_create (((ecma_date_object_t *) ecma_get_object_from_value (argument))->date_value); } /* 4.b */ ecma_value_t primitive = ecma_op_to_primitive (argument, ECMA_PREFERRED_TYPE_NO); if (ECMA_IS_VALUE_ERROR (primitive)) { return primitive; } if (ecma_is_value_string (primitive)) { ecma_string_t *prim_str_p = ecma_get_string_from_value (primitive); tv = ecma_builtin_date_parse (prim_str_p); ecma_deref_ecma_string (prim_str_p); } else { ecma_value_t prim_value = ecma_op_to_number (primitive, &tv); ecma_free_value (primitive); if (ECMA_IS_VALUE_ERROR (prim_value)) { return prim_value; } } } /* 20.4.2.1 */ else if (ecma_date_construct_helper (arguments_list_p, arguments_list_len, &tv)) { tv = ecma_date_utc (tv); } else { return ECMA_VALUE_ERROR; } return ecma_builtin_date_create (ecma_date_time_clip (tv)); } /* ecma_builtin_date_dispatch_construct */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_date_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (this_arg); switch (builtin_routine_id) { case ECMA_DATE_ROUTINE_NOW: { return ecma_make_number_value (ecma_builtin_date_now_helper ()); } case ECMA_DATE_ROUTINE_UTC: { return ecma_builtin_date_utc (arguments_list_p, arguments_number); } case ECMA_DATE_ROUTINE_PARSE: { if (arguments_number < 1) { return ecma_make_nan_value (); } ecma_string_t *str_p = ecma_op_to_string (arguments_list_p[0]); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_make_number_value (ecma_date_time_clip (ecma_builtin_date_parse (str_p))); ecma_deref_ecma_string (str_p); return result; } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_date_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_DATE */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/000775 001750 001750 00000000000 15164251010 040224 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/srcglaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/000775 001750 001750 00000000000 15164251010 041136 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modulesexternal/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-error-messages.ini000664 001750 001750 00000063630 15164251010 045510 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate# Copyright JS Foundation and other contributors, http://js.foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # 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. [ECMA_ERROR_MESSAGES] ECMA_ERR_CANNOT_ACCESS_CALLER_CALLEE_ARGUMENTS = "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them" ECMA_ERR_PARAMETER_REJECT_MUST_BE_CALLABLE = "'reject' parameter must be callable" ECMA_ERR_PARAMETER_RESOLVE_MUST_BE_CALLABLE = "'resolve' parameter must be callable" ECMA_ERR_PROMISE_RESOLVE_ITSELF = "A promise cannot be resolved with itself" ECMA_ERR_ACCESSOR_WRITABLE = "Accessors cannot be writable" ECMA_ERR_ARGUMENT_BUFFER_NOT_ARRAY_OR_SHARED_BUFFER = "Argument 'buffer' is not an ArrayBuffer or SharedArrayBuffer" ECMA_ERR_ARGUMENT_BUFFER_NOT_OBJECT = "Argument 'buffer' is not an object" ECMA_ERR_ARGUMENT_THIS_NOT_ARRAY_BUFFER_OBJECT = "Argument 'this' is not an ArrayBuffer object" ECMA_ERR_ARGUMENT_THIS_NOT_BOOLEAN_OBJECT = "Argument 'this' is not a Boolean object" ECMA_ERR_ARGUMENT_THIS_NOT_DATE_OBJECT = "Argument 'this' is not a Date object" ECMA_ERR_ARGUMENT_THIS_NOT_PROMISE = "Argument 'this' is not a Promise" ECMA_ERR_ARGUMENT_THIS_NOT_SHARED_ARRAY_BUFFER = "Argument 'this' is not a SharedArrayBuffer object" ECMA_ERR_ARGUMENT_THIS_NOT_TYPED_ARRAY = "Argument 'this' is not a TypedArray" ECMA_ERR_ARGUMENT_THIS_NOT_CONSTRUCTOR = "Argument 'this' is not a constructor" ECMA_ERR_ARGUMENT_THIS_NOT_FUNCTION = "Argument 'this' is not a function" ECMA_ERR_ARGUMENT_THIS_NOT_GENERATOR_OBJECT = "Argument 'this' is not a generator object" ECMA_ERR_ARGUMENT_THIS_NOT_NUMBER = "Argument 'this' is not a number or a Number object" ECMA_ERR_ARGUMENT_THIS_NOT_STRING_OBJECT = "Argument 'this' is not a string or a String object" ECMA_ERR_ARGUMENT_THIS_NOT_REG_EXP = "Argument 'this' is not a valid RegExp" ECMA_ERR_ARGUMENT_THIS_NOT_REG_EXP_OBJECT = "Argument 'this' is not a valid RegExp object" ECMA_ERR_ARGUMENT_THIS_NOT_SHARED_ARRAY_BUFFER_OBJECT = "Argument 'this' is not an SharedArrayBuffer object" ECMA_ERR_ARGUMENT_THIS_NOT_ASYNC_GENERATOR = "Argument 'this' is not an async generator object" ECMA_ERR_ARGUMENT_THIS_NOT_ITERATOR = "Argument 'this' is not an iterator" ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT = "Argument 'this' is not an object" ECMA_ERR_ARGUMENT_THIS_NOT_SYMBOL = "Argument 'this' must be a Symbol" ECMA_ERR_ARGUMENT_CANNOT_CONVERT_TO_OBJECT = "Argument cannot be converted to an object" ECMA_ERR_ARGUMENT_NOT_SHARED_ARRAY_BUFFER = "Argument is not SharedArrayBuffer" ECMA_ERR_ARGUMENT_NOT_ARRAY_BUFFER = "Argument is not an ArrayBuffer" ECMA_ERR_ARGUMENT_NOT_SUPPORTED = "Argument is not supported" ECMA_ERR_ARRAY_BUFFER_DETACHED = "ArrayBuffer has already been detached" ECMA_ERR_ARRAY_BUFFER_RETURNED_THIS_FROM_CONSTRUCTOR = "ArrayBuffer subclass returned this from species constructor" ECMA_ERR_BIGINT_SERIALIZED = "BigInt cannot be serialized" ECMA_ERR_BIGINT_ZERO_DIVISION = "BigInt division by zero" ECMA_ERR_BIGINT_FUNCTION_NOT_CONSTRUCTOR = "BigInt function is not a constructor" ECMA_ERR_BIGINT_VALUE_EXPECTED = "BigInt value expected" ECMA_ERR_BINDING_CANNOT_SET = "Binding cannot be set" ECMA_ERR_BINDING_NOT_EXIST_OR_UNINITIALIZED = "Binding does not exist or is uninitialised" ECMA_ERR_BUILTIN_ROUTINES_HAVE_NO_CONSTRUCTOR = "Built-in routines have no constructor" ECMA_ERR_CALLBACK_RESULT_NOT_MODULE = "Callback result must be a module" ECMA_ERR_ALLOCATE_ARRAY_BUFFER = "Cannot allocate memory for ArrayBuffer" ECMA_ERR_ALLOCATE_BIGINT_VALUE = "Cannot allocate memory for a BigInt value" ECMA_ERR_ALLOCATE_BIGINT_STRING = "Cannot allocate memory for a string representation of a BigInt value" ECMA_ERR_CONVERT_BIGINT_TO_NUMBER = "Cannot convert a BigInt value to a number" ECMA_ERR_CONVERT_SYMBOL_TO_NUMBER = "Cannot convert a Symbol value to a number" ECMA_ERR_CONVERT_SYMBOL_TO_STRING = "Cannot convert a Symbol value to a string" ECMA_ERR_CANNOT_CREATE_PROXY = "Cannot create Proxy with a non-object target or handler" ECMA_ERR_INVOKE_NULLABLE_SUPER_METHOD = "Cannot invoke nullable super method" ECMA_ERR_LINK_TO_MODULE_IN_ERROR_STATE = "Cannot link to a module which is in error state" ECMA_ERR_SET_EXTENSIBLE_PROPERTY = "Cannot set [[Extensible]] property of object" ECMA_ERR_SET_PROTOTYPE = "Cannot set [[Prototype]]" ECMA_ERR_CLASS_CONSTRUCTOR_REQUIRES_NEW = "Class constructor requires 'new'" ECMA_ERR_CLASS_EXTENDS_NOT_CONSTRUCTOR = "Class extends value is not a constructor or null" ECMA_ERR_COMPARE_FUNC_NOT_CALLABLE = "Compare function is not callable" ECMA_ERR_CONSTANT_BINDINGS_CANNOT_BE_REASSIGNED = "Constant bindings cannot be reassigned" ECMA_ERR_TYPEDARRAY_SMALLER_THAN_FILTER_CALL_RESULT = "Constructed TypedArray is smaller than filter call result" ECMA_ERR_CONSTRUCTED_OBJECT_IS_NOT_TYPEDARRAY = "Constructed object is not TypedArray" ECMA_ERR_CONSTRUCTOR_ARRAYBUFFER_REQUIRES_NEW = "Constructor ArrayBuffer requires 'new'" ECMA_ERR_CONSTRUCTOR_BIGINT64_ARRAY_REQUIRES_NEW = "Constructor BigInt64Array requires 'new'" ECMA_ERR_CONSTRUCTOR_BIG_UINT64_ARRAY_REQUIRES_NEW = "Constructor BigUInt64Array requires 'new'" ECMA_ERR_CONSTRUCTOR_DATAVIEW_REQUIRES_NEW = "Constructor DataView requires 'new'" ECMA_ERR_CONSTRUCTOR_FLOAT32_ARRAY_REQUIRES_NEW = "Constructor Float32Array requires 'new'" ECMA_ERR_CONSTRUCTOR_FLOAT64_ARRAY_REQUIRES_NEW = "Constructor Float64Array requires 'new'" ECMA_ERR_CONSTRUCTOR_INT16_ARRAY_REQUIRES_NEW = "Constructor Int16Array requires 'new'" ECMA_ERR_CONSTRUCTOR_INT32_ARRAY_REQUIRES_NEW = "Constructor Int32Array requires 'new'" ECMA_ERR_CONSTRUCTOR_INT8_ARRAY_REQUIRES_NEW = "Constructor Int8Array requires 'new'" ECMA_ERR_CONSTRUCTOR_MAP_REQUIRES_NEW = "Constructor Map requires 'new'" ECMA_ERR_CONSTRUCTOR_PROMISE_REQUIRES_NEW = "Constructor Promise requires 'new'" ECMA_ERR_CONSTRUCTOR_PROXY_REQUIRES_NEW = "Constructor Proxy requires 'new'" ECMA_ERR_CONSTRUCTOR_SET_REQUIRES_NEW = "Constructor Set requires 'new'" ECMA_ERR_CONSTRUCTOR_SHAREDARRAYBUFFER_REQUIRES_NEW = "Constructor SharedArrayBuffer requires 'new'" ECMA_ERR_CONSTRUCTOR_UINT16_ARRAY_REQUIRES_NEW = "Constructor Uint16Array requires 'new'" ECMA_ERR_CONSTRUCTOR_UINT32_ARRAY_REQUIRES_NEW = "Constructor Uint32Array requires 'new'" ECMA_ERR_CONSTRUCTOR_UINT8_ARRAY_REQUIRES_NEW = "Constructor Uint8Array requires 'new'" ECMA_ERR_CONSTRUCTOR_UINT8_CLAMPED_ARRAY_REQUIRES_NEW = "Constructor Uint8ClampedArray requires 'new'" ECMA_ERR_CONSTRUCTOR_WEAKMAP_REQUIRES_NEW = "Constructor WeakMap requires 'new'" ECMA_ERR_CONSTRUCTOR_WEAKREF_REQUIRES_NEW = "Constructor WeakRef requires 'new'." ECMA_ERR_CONSTRUCTOR_WEAKSET_REQUIRES_NEW = "Constructor WeakSet requires 'new'" ECMA_ERR_CONSTRUCTOR_NOT_AN_OBJECT = "Constructor must be an object" ECMA_ERR_CONTAINER_IS_NOT_A_CONTAINER_OBJECT = "Container is not a container object." ECMA_ERR_CONTAINER_IS_NOT_AN_OBJECT = "Container is not an object." ECMA_ERR_DATE_MUST_BE_A_FINITE_NUMBER = "Date must be a finite number" ECMA_ERR_DERIVED_ARRAY_BUFFER_CTOR_BUFFER_TOO_SMALL = "Derived ArrayBuffer constructor created a too small buffer" ECMA_ERR_DERIVED_CTOR_RETURN_NOR_OBJECT_OR_UNDEFINED = "Derived constructors may only return object or undefined" ECMA_ERR_INVALID_CODE_POINT_ERROR = "Error: Invalid code point" ECMA_ERR_EXPECTED_A_DATAVIEW_OBJECT = "Expected a DataView object" ECMA_ERR_EXPECTED_A_CONFIGURABLE_PROPERTY = "Expected a configurable property" ECMA_ERR_EXPECTED_A_FUNCTION_OBJECT = "Expected a function object" ECMA_ERR_EXPECTED_AN_ARRAYBUFFER = "Expected an ArrayBuffer" ECMA_ERR_EXPECTED_AN_OBJECT = "Expected an object" ECMA_ERR_FIRST_ARGUMENT_IS_NOT_A_REALM = "First argument is not a realm" ECMA_ERR_FIRST_PARAMETER_MUST_BE_CALLABLE = "First parameter must be callable" ECMA_ERR_FRACTION_DIGITS_OUT_OF_RANGE = "Fraction digits must be between 0 and 100" ECMA_ERR_FUNCTION_ADD_ORSET_IS_NOT_CALLABLE = "Function add/set is not callable" ECMA_ERR_FUNCTION_INDEX_IS_HIGHER_THAN_MAXIMUM = "Function index is higher than maximum" ECMA_ERR_FUNCTION_PROTOTYPE_NOT_A_CONSTRUCTOR = "Function.prototype is not a constructor" ECMA_ERR_GENERATOR_IS_CURRENTLY_UNDER_EXECUTION = "Generator is currently under execution" ECMA_ERR_GETTER_IS_NOT_CALLABLE = "Getter is not callable" ECMA_ERR_GIVEN_PROPERTY_IS_A_NON_CONFIGURABLE = "Given property is a non-configurable data property on the proxy target" ECMA_ERR_HANDLER_CANNOT_BE_NULL = "Handler cannot be null" ECMA_ERR_IMPORTED_BINDING_SHADOWS_LOCAL_VARIABLE = "Imported binding shadows local variable" ECMA_ERR_INCOMPATIBLE_TYPEDARRAY_TYPES = "Incompatible TypedArray types" ECMA_ERR_INCORRECT_TYPE_FOR_TYPEDARRAY = "Incorrect type for TypedArray" ECMA_ERR_INCORRECT_RETURN_PROXY_GET_TRAP = "Incorrect value is returned by a Proxy 'get' trap" ECMA_ERR_INCORRECT_RETURN_PROXY_SET_TRAP = "Incorrect value is returned by a Proxy 'set' trap" ECMA_ERR_INFINITY_OR_NAN_CANNOT_BE_CONVERTED_TO_BIGINT = "Infinity or NaN cannot be converted to BigInt" ECMA_ERR_INITIAL_VALUE_CANNOT_BE_UNDEFINED = "Initial value cannot be undefined" ECMA_ERR_INVALID_ARRAYBUFFER_LENGTH = "Invalid ArrayBuffer length" ECMA_ERR_INVALID_JSON_FORMAT = "Invalid JSON format" ECMA_ERR_INVALID_REGEXP_FLAGS = "Invalid RegExp flags" ECMA_ERR_INVALID_SHARED_ARRAYBUFFER_LENGTH = "Invalid Shared ArrayBuffer length" ECMA_ERR_INVALID_TYPEDARRAY_LENGTH = "Invalid TypedArray length" ECMA_ERR_INVALID_UTF8_CHARACTER = "Invalid UTF8 character" ECMA_ERR_INVALID_UTF8_CODEPOINT = "Invalid UTF8 codepoint" ECMA_ERR_INVALID_UTF8_STRING = "Invalid UTF8 string" ECMA_ERR_INVALID_ENCODING = "Invalid encoding" ECMA_ERR_INVALID_ARGUMENT = "Invalid argument" ECMA_ERR_INVALID_ARGUMENT_TYPE_IN_TOPRIMITIVE = "Invalid argument type in toPrimitive" ECMA_ERR_INVALID_CAPABILITY = "Invalid capability" ECMA_ERR_INVALID_CHARACTER_CLASS = "Invalid character class" ECMA_ERR_INVALID_CODE_POINT = "Invalid code point" ECMA_ERR_INVALID_CONTAINER_TYPE = "Invalid container type" ECMA_ERR_INVALID_CONTROL_ESCAPE_SEQUENCE = "Invalid control escape sequence" ECMA_ERR_INVALID_COUNT_VALUE = "Invalid count value" ECMA_ERR_INVALID_ESCAPE = "Invalid escape" ECMA_ERR_INVALID_ESCAPE_SEQUENCE = "Invalid escape sequence" ECMA_ERR_INVALID_GROUP = "Invalid group" ECMA_ERR_INVALID_HEX_ESCAPE_SEQUENCE = "Invalid hex escape sequence" ECMA_ERR_INVALID_HEXADECIMAL_VALUE = "Invalid hexadecimal value" ECMA_ERR_INVALID_LENGTH = "Invalid length" ECMA_ERR_INVALID_NEW_ARRAY_LENGTH = "Invalid new Array length" ECMA_ERR_INVALID_OFFSET = "Invalid offset" ECMA_ERR_INVALID_OR_OUT_OF_RANGE_INDEX = "Invalid or out-of-range index" ECMA_ERR_INVALID_QUANTIFIER = "Invalid quantifier" ECMA_ERR_INVALID_RANGE_OF_INDEX = "Invalid range of index" ECMA_ERR_INVALID_SCOPE_CHAIN_INDEX_FOR_EVAL = "Invalid scope chain index for eval" ECMA_ERR_INVALID_SPECIES_CONSTRUCTOR = "Invalid species constructor" ECMA_ERR_INVALID_STRING_ = "Invalid string length" ECMA_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE = "Invalid unicode escape sequence" ECMA_ERR_ITERATOR_NEXT_IS_NOT_CALLABLE = "Iterator 'next' is not callable" ECMA_ERR_ITERATOR_RETURN_RESULT_IS_NOT_OBJECT = "Iterator 'return' result is not object" ECMA_ERR_ITERATOR_THROW_IS_NOT_AVAILABLE = "Iterator 'throw' is not available" ECMA_ERR_ITERATOR_IS_NOT_AN_OBJECT = "Iterator is not an object" ECMA_ERR_ITERATOR_IS_NOT_CALLABLE = "Iterator is not callable" ECMA_ERR_ITERATOR_RESULT_IS_NOT_AN_OBJECT = "Iterator result is not an object" ECMA_ERR_ITERATOR_VALUE_IS_NOT_AN_OBJECT = "Iterator value is not an object" ECMA_ERR_JSON_STRING_PARSE_ERROR = "JSON string parse error" ECMA_ERR_JSON_STRINGIFY_ERROR = "JSON stringify error" ECMA_ERR_KEY_MUST_BE_AN_OBJECT = "Key must be an object" ECMA_ERR_LONE_QUANTIFIER_BRACKET = "Lone quantifier bracket" ECMA_ERR_MAXIMUM_TYPEDARRAY_SIZE_IS_REACHED = "Maximum TypedArray size is reached" ECMA_ERR_MAXIMUM_STRING_LENGTH_IS_REACHED = "Maximum string length is reached" ECMA_ERR_MISSING_ARRAY_ELEMENT = "Missing Array element" ECMA_ERR_MODULE_CANNOT_BE_INSTANTIATED = "Module cannot be instantiated" ECMA_ERR_MODULE_EXPORTS_MUST_BE_STRING_VALUES = "Module exports must be string values" ECMA_ERR_MODULE_EXPORTS_MUST_BE_VALID_IDENTIFIERS = "Module exports must be valid identifiers" ECMA_ERR_MODULE_IS_IN_ERROR_STATE = "Module is in error state" ECMA_ERR_MODULE_MUST_BE_IN_LINKED_STATE = "Module must be in linked state" ECMA_ERR_MODULE_MUST_BE_IN_UNLINKED_STATE = "Module must be in unlinked state" ECMA_ERR_CALL_SUPER_CONSTRUCTOR_DERIVED_CLASS_BEFORE_THIS = "Must call super constructor in derived class before accessing 'this' or returning from it" ECMA_ERR_NAMESPACE_OBJECT_IS_NOT_AVAILABLE = "Namespace object is not available" ECMA_ERR_NEGATIVE_EXPONENT_IS_NOT_ALLOWED_FOR_BIGINTS = "Negative exponent is not allowed for BigInts" ECMA_ERR_NO_SOURCE_ARGUMENT = "No source argument" ECMA_ERR_NOTHING_TO_REPEAT = "Nothing to repeat" ECMA_ERR_OBJECT_CANNOT_BE_FROZEN = "Object cannot be frozen" ECMA_ERR_OBJECT_CANNOT_BE_SEALED = "Object cannot be sealed" ECMA_ERR_OBJECT_EXPECTED = "Object expected" ECMA_ERR_OBJECT_IS_NOT_A_TYPEDARRAY = "Object is not a TypedArray" ECMA_ERR_ONLY_INTEGER_NUMBERS_CAN_BE_CONVERTED_TO_BIGINT = "Only integer numbers can be converted to BigInt" ECMA_ERR_OPERATOR_DELETE_RETURNED_FALSE_IN_STRICT_MODE = "Operator delete returned false in strict mode" ECMA_ERR_PASSED_ARGUMENT_IS_NOT_A_REALM = "Passed argument is not a realm" ECMA_ERR_PRECISION_DIGITS_MUST_BE_BETWEEN_IN_RANGE = "Precision digits must be between 1 and 100" ECMA_ERR_PROMISE_ALL_REMAINING_ELEMENTS_LIMIT_REACHED = "Promise.all remaining elements limit reached" ECMA_ERR_PROPERTY_PROTOTYPE_IS_NOT_AN_OBJECT = "Property 'prototype' is not an object or null" ECMA_ERR_PROPERTY_NAME_IS_NEITHER_SYMBOL_NOR_STRING = "Property name is neither Symbol nor string" ECMA_ERR_PROXY_PROPERTY_NOT_CONFIGURABLE_NOT_HAVE_GETTER = "Property of a Proxy is non-configurable and does not have a getter function" ECMA_ERR_PROTOTYPE_FROM_REVOKED_PROXY_IS_INVALID = "Prototype from revoked Proxy is invalid" ECMA_ERR_PROTOTYPE_IS_NEITHER_OBJECT_NOR_NULL = "Prototype is neither object nor null" ECMA_ERR_PROXY_HANDLER_IS_NULL_FOR_ISARRAY_OPERATION = "Proxy handler is null for 'isArray' operation" ECMA_ERR_PROXY_IS_NOT_SUPPORTED = "Proxy is not supported" ECMA_ERR_TARGET_NOT_EXTENSIBLE_NOT_RETURNED_ITS_PROTOTYPE = "Proxy target is non-extensible, but the trap did not return its actual prototype" ECMA_ERR_PROXY_TRAP_RETURNED_FALSISH = "Proxy trap returned falsish" ECMA_ERR_PUSHING_TOO_HIGH_ELEMENT = "Pushing element over 2**53-1 length is disallowed" ECMA_ERR_MIN_GREATER_THAN_MAX = "Quantifier error: min > max" ECMA_ERR_RADIX_IS_OUT_OF_RANGE = "Radix must be between 2 and 36" ECMA_ERR_RANGE_OUT_OF_ORDER_IN_CHARACTER_CLASS = "Range out of order in character class" ECMA_ERR_REALM_IS_NOT_AVAILABLE = "Realm is not available" ECMA_ERR_REALMS_ARE_DISABLED = "Realms are disabled" ECMA_ERR_REDUCE_OF_EMPTY_ARRAY_WITH_NO_INITIAL_VALUE = "Reduce of empty Array with no initial value" ECMA_ERR_REFLECT_EXPECTS_AN_OBJECT_AS_SECOND_ARGUMENT = "Reflect.construct expects an object as second argument" ECMA_ERR_REGEXP_ARGUMENT_SHOULD_HAVE_GLOBAL_FLAG = "RegExp argument should have global flag" ECMA_ERR_REGEXP_IS_NOT_SUPPORTED = "RegExp is not supported" ECMA_ERR_REJECT_MUST_BE_UNDEFINED = "Reject must be undefined" ECMA_ERR_REQUEST_IS_NOT_AVAILABLE = "Request is not available" ECMA_ERR_RESOLVE_METHOD_MUST_BE_CALLABLE = "Resolve method must be callable" ECMA_ERR_RESOLVE_MUST_BE_UNDEFINED = "Resolve must be undefined" ECMA_ERR_RESULT_OF_DEFAULTVALUE_IS_INVALID = "Result of [[DefaultValue]] is invalid" ECMA_ERR_RETURN_VALUE_IS_NOT_AN_ARRAYBUFFER_OBJECT = "Return value is not an ArrayBuffer object" ECMA_ERR_RETURN_VALUE_OF_EXEC_MUST_BE_AN_OBJECT_OR_NULL = "Return value of 'exec' must be an object or null" ECMA_ERR_RIGHT_VALUE_OF_IN_MUST_BE_AN_OBJECT = "Right value of 'in' must be an object" ECMA_ERR_RIGHT_VALUE_OF_INSTANCEOF_MUST_BE_AN_OBJECT = "Right value of 'instanceof' must be an object" ECMA_ERR_SEARCH_STRING_CANNOT_BE_OF_TYPE_REGEXP = "Search string can't be of type: RegExp" ECMA_ERR_SECOND_ARGUMENT_MUST_BE_AN_OBJECT = "Second argument must be an object" ECMA_ERR_SPECIES_MUST_BE_A_CONSTRUCTOR = "Species must be a constructor" ECMA_ERR_STACK_LIMIT_EXCEEDED = "Stack limit exceeded" ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER = "Start offset is outside the bounds of the buffer" ECMA_ERR_STATIC_SNAPSHOTS_ARE_NOT_ENABLED = "Static snapshots are not enabled" ECMA_ERR_STATIC_SNAPSHOTS_CANNOT_BE_COPIED_INTO_MEMORY = "Static snapshots cannot be copied into memory" ECMA_ERR_STRING_CANNOT_BE_CONVERTED_TO_BIGINT_VALUE = "String cannot be converted to BigInt value" ECMA_ERR_SUPER_BINDING_MUST_BE_A_CONSTRUCTOR = "Super binding must be a constructor" ECMA_ERR_SUPER_CONSTRUCTOR_MAY_ONLY_BE_CALLED_ONCE = "Super constructor may only be called once" ECMA_ERR_SYMBOL_IS_NOT_A_CONSTRUCTOR = "Symbol is not a constructor" ECMA_ERR_TARGET_IS_NOT_OBJECT = "Target is not Object" ECMA_ERR_TARGET_IS_NOT_WEAKREF = "Target is not weakRef" ECMA_ERR_TARGET_NOT_EXTENSIBLE = "Target not extensible" ECMA_ERR_TARGET_NOT_EXTENSIBLE_DIFFERENT_PROTOTYPE_RETURNED = "Target object is non-extensible and trap returned different prototype" ECMA_ERR_THE_MAPFN_ARGUMENT_IS_NOT_CALLABLE = "The 'mapfn' argument is not callable" ECMA_ERR_THE_GIVEN_ARGUMENT_IS_NOT_A_SYMBOL = "The given argument is not a Symbol" ECMA_ERR_TARGET_PROPERTY_CONFIGURE_ACCESSOR_WITHOUT_SETTER = "The property of a Proxy target is a non configurable accessor without a setter" ECMA_ERR_THE_REQUESTED_PROPERTY_UPDATE_CANNOT_BE_PERFORMED = "The requested property update cannot be performed" ECMA_ERR_THE_STRUCTURE_IS_CYCLICAL = "The structure is cyclical" ECMA_ERR_THE_TWO_DESCRIPTORS_ARE_INCOMPATIBLE = "The two descriptors are incompatible" ECMA_ERR_TOO_MANY_ARGUMENTS_DECLARED_FOR_FUNCTION_APPLY = "Too many arguments declared for Function.apply" ECMA_ERR_TRAP_IS_NEITHER_AN_OBJECT_NOR_UNDEFINED = "Trap is neither an object nor undefined" ECMA_ERR_TRAP_MUST_RETURN_WITH_AN_OBJECT = "Trap must return with an object" ECMA_ERR_TRAP_RESULT_NOT_INCLUDE_ALL_CONFIGURABLE_KEYS = "Trap result did not include all configurable keys" ECMA_ERR_TRAP_RESULT_NOT_INCLUDE_ALL_NON_CONFIGURABLE_KEYS = "Trap result did not include all non-configurable keys" ECMA_ERR_TRAP_RESULT_NOT_REFLECT_TARGET_EXTENSIBILITY = "Trap result does not reflect extensibility of Proxy target" ECMA_ERR_TRAP_RESULT_NOT_REFLECT_TARGET_INEXTENSIBILITY = "Trap result does not reflect inextensibility of Proxy target" ECMA_ERR_TRAP_EXTRA_KEYS_FOR_A_NON_EXTENSIBLE_TARGET = "Trap returned extra keys for a non-extensible Proxy target" ECMA_ERR_TRAP_FALSISH_PROPERTY_TARGET_NOT_EXTENSIBLE = "Trap returned falsish for property but the proxy target is not extensible" ECMA_ERR_TRAP_FALSISH_PROPERTY_NON_CONFIGURABLE = "Trap returned falsish for property which exists in the proxy target as non-configurable" ECMA_ERR_TRAP_RETURNED_NEITHER_OBJECT_NOR_NULL = "Trap returned neither object nor null" ECMA_ERR_TRAP_TRUISH_ADDING_PROPERTY_NON_EXTENSIBLE_TARGET = "Trap returned truish for adding property to the non-extensible target" ECMA_ERR_TRAP_TRUISH_ADD_PROPERTY_INCOMPATIBLE_OTHER_PROP = "Trap returned truish for adding property that is incompatible with the existing property in the target" ECMA_ERR_TRAP_TRUISH_DEFINING_NON_EXISTENT_PROPERTY = "Trap returned truish for defining non-configurable property which is nonexistent in the target" ECMA_ERR_TRAP_TRUISH_PROPERTY_NON_CONFIGURABLE = "Trap returned truish for property which is non-configurable in the proxy target" ECMA_ERR_TRAP_TRUISH_TARGET_NOT_EXTENSIBLE = "Trap returned truish for target is not extensible" ECMA_ERR_TRAP_WITH_DUPLICATED_ENTRIES = "Trap returned with duplicated entries" ECMA_ERR_TYPEDARRAY_INTRINSIC_CALLED_BY_NEW_EXPRESSION = "TypedArray intrinsic cannot be called by a 'new' expression" ECMA_ERR_TYPEDARRAY_INTRINSIC_DIRECTLY_CALLED = "TypedArray intrinsic cannot be directly called" ECMA_ERR_CONTENTTYPE_RETURNED_TYPEDARRAY_NOT_MATCH_SOURCE = "TypedArray returned by [[ContentType]] does not match source" ECMA_ERR_UNARY_PLUS_IS_NOT_ALLOWED_FOR_BIGINTS = "Unary plus is not allowed for BigInts" ECMA_ERR_UNDEFINED_REFERENCE = "Undefined reference" ECMA_ERR_UNEXPECTED_END_OF_PATTERN = "Unexpected end of pattern" ECMA_ERR_UNICODE_SURROGATE_PAIR_MISSING = "Unicode surrogate pair missing" ECMA_ERR_UNMATCHED_CLOSE_BRACKET = "Unmatched close bracket" ECMA_ERR_UNSHIFT_TOO_HIGH = "Unshift elements over 2**53-1 length is disallowed" ECMA_ERR_UNSIGNED_RIGHT_SHIFT_IS_NOT_ALLOWED_FOR_BIGINTS = "Unsigned right shift is not allowed for BigInts" ECMA_ERR_UNSUPPORTED_BINARY_OPERATION = "Unsupported binary operation" ECMA_ERR_UNSUPPORTED_CONTAINER_OPERATION = "Unsupported container operation" ECMA_ERR_UNSUPPORTED_SNAPSHOT_EXEC_FLAGS_ARE_SPECIFIED = "Unsupported snapshot exec flags are specified" ECMA_ERR_UNTERMINATED_CHARACTER_CLASS = "Unterminated character class" ECMA_ERR_UNTERMINATED_GROUP = "Unterminated group" ECMA_ERR_VALUE_CANNOT_BE_CONVERTED_TO_BIGINT = "Value cannot be converted to BigInt" ECMA_ERR_VALUE_FOR_CLASS_HERITAGE_IS_NOT_A_CONSTRUCTOR = "Value for class heritage is not a constructor" ECMA_ERR_VALUE_RECEIVED_BY_FOR_ASYNC_OF_IS_NOT_OBJECT = "Value received by for-async-of is not object" ECMA_ERR_VALUE_RECEIVED_BY_YIELD_IS_NOT_OBJECT = "Value received by yield* is not object" ECMA_ERR_WEAKREF_TARGET_MUST_BE_AN_OBJECT = "WeakRef target must be an object" ECMA_ERR_METHOD_RETURN_IS_NOT_CALLABLE = "method 'return' is not callable" ECMA_ERR_VALUE_MSG = "Argument cannot be marked as error" ECMA_ERR_WRONG_ARGS_MSG = "This type of argument is not allowed" ECMA_ERR_PARSER_NOT_SUPPORTED = "Source code parsing is disabled" ECMA_ERR_JSON_NOT_SUPPORTED = "JSON support is disabled" ECMA_ERR_TYPED_ARRAY_NOT_SUPPORTED = "TypedArray support is disabled" ECMA_ERR_SHARED_ARRAYBUFFER_NOT_SUPPORTED = "SharedArrayBuffer support is disabled" ECMA_ERR_DATA_VIEW_NOT_SUPPORTED = "DataView support is disabled" ECMA_ERR_BIGINT_NOT_SUPPORTED = "BigInt support is disabled" ECMA_ERR_CONTAINER_NOT_SUPPORTED = "Container support is disabled" ECMA_ERR_NOT_MODULE = "Argument is not a module" ECMA_ERR_UNKNOWN_EXPORT = "Native module export not found" ECMA_ERR_MODULE_NOT_SUPPORTED = "Module support is disabled" ECMA_ERR_CALLBACK_IS_NOT_CALLABLE = "Callback function is not callable" ECMA_ERR_ARRAYBUFFER_IS_DETACHED = "ArrayBuffer has been detached" ECMA_ERR_CANNOT_CONVERT_TO_OBJECT = "Cannot convert undefined or null to object" ECMA_ERR_CLASS_IS_NON_CONFIGURABLE = "Prototype property of a class is non-configurable" ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT = "Argument is not an object" ECMA_ERR_ARGUMENT_IS_NOT_A_PROXY = "Argument is not a Proxy object" ECMA_ERR_TARGET_IS_NOT_A_CONSTRUCTOR = "Target is not a constructor" ECMA_ERR_ARGUMENT_IS_NOT_AN_REGEXP = "Argument 'this' is not a RegExp object" ECMA_ERR_INVALID_ARRAY_LENGTH = "Invalid Array length" ECMA_ERR_LOCAL_VARIABLE_IS_REDECLARED = "Local variable is redeclared" ECMA_ERR_EXPECTED_A_FUNCTION = "Expected a function" ECMA_ERR_CLASS_CONSTRUCTOR_NEW = "Class constructor cannot be invoked without 'new'" ECMA_ERR_LET_CONST_NOT_INITIALIZED = "Variables declared by let/const must be initialized before reading their value" ECMA_ERR_MAXIMUM_SNAPSHOT_SIZE = "Maximum snapshot size reached" ECMA_ERR_REGULAR_EXPRESSION_NOT_SUPPORTED = "Regular expression literals are not supported" ECMA_ERR_SNAPSHOT_BUFFER_SMALL = "Snapshot buffer too small" ECMA_ERR_SNAPSHOT_UNSUPPORTED_COMPILED_CODE = "Unsupported compiled code" ECMA_ERR_SNAPSHOT_FLAG_NOT_SUPPORTED = "Unsupported generate snapshot flags specified" ECMA_ERR_SNAPSHOT_SAVE_DISABLED = "Snapshot generation is disabled" ECMA_ERR_SNAPSHOT_EXEC_DISABLED = "Snapshot execution is disabled" ECMA_ERR_CANNOT_ALLOCATE_MEMORY_LITERALS = "Cannot allocate memory for literals" ECMA_ERR_TAGGED_TEMPLATE_LITERALS = "Unsupported feature: tagged template literals" ECMA_ERR_CONTAINER_NEEDED = "Value is not a Container or Iterator" ECMA_ERR_INCORRECT_TYPE_CALL = "Operator called on incorrect container type" ECMA_ERR_INVALID_TYPE_FOR_CONSTRUCTOR_CALL = "Invalid type for constructor call" ECMA_ERR_SCRIPT_GLOBAL_FUNCTIONS_INVOKE_WITH_NEW = "Script (global) functions cannot be invoked with 'new'" ECMA_ERR_GENERATOR_FUNCTIONS_INVOKE_WITH_NEW = "Generator functions cannot be invoked with 'new'" ECMA_ERR_ASYNC_FUNCTIONS_INVOKE_WITH_NEW = "Async functions cannot be invoked with 'new'" ECMA_ERR_ASYNC_GENERATOR_FUNCTIONS_INVOKE_WITH_NEW = "Async generator functions cannot be invoked with 'new'" ECMA_ERR_ACCESSOR_FUNCTIONS_INVOKE_WITH_NEW = "Accessor functions cannot be invoked with 'new'" ECMA_ERR_METHODS_INVOKE_WITH_NEW = "Methods cannot be invoked with 'new'" ECMA_ERR_ARROW_FUNCTIONS_INVOKE_WITH_NEW = "Arrow functions cannot be invoked with 'new'" ECMA_ERR_ASYNC_ARROW_FUNCTIONS_INVOKE_WITH_NEW = "Async arrow functions cannot be invoked with 'new'" ECMA_ERR_PROXY_TARGET_IS_NOT_A_CONSTRUCTOR = "Proxy target is not a constructor" ECMA_ERR_MAXIMUM_CALL_STACK_SIZE_EXCEEDED = "Maximum call stack size exceeded" ECMA_ERR_INVALID_SNAPSHOT_FORMAT = "Invalid snapshot format" ECMA_ERR_INVALID_SNAPSHOT_VERSION_OR_FEATURES = "Invalid snapshot version or unsupported features present" ECMA_ERR_RECEIVER_MUST_BE_AN_OBJECT = "Receiver must be an object" ECMA_ERR_CANNOT_DECLARE_SAME_PRIVATE_FIELD_TWICE = "Cannot declare same private field twice" ECMA_ERR_CANNOT_WRITE_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT = "Cannot write private member to an object whose class did not declare it" ECMA_ERR_PRIVATE_METHOD_IS_NOT_WRITABLE = "Private method is not writable" ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_SETTER = "Private field was defined without a setter" ECMA_ERR_CANNOT_READ_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT = "Cannot read private member to an object whose class did not declare it" ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_GETTER = "Private field was defined without a getter" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/frame.cpp000664 001750 001750 00000060516 15164251010 034733 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Frame-reconstruction function. Memory allocation. // // Author: Skal (pascal.massimino@gmail.com) #include "tvgCommon.h" #include "./vp8i.h" #include "../utils/utils.h" #define ALIGN_CST (32 - 1) #define DO_ALIGN(PTR) ((uintptr_t)((PTR) + ALIGN_CST) & ~ALIGN_CST) //------------------------------------------------------------------------------ // Main reconstruction function. static const int kScan[16] = { 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS, 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS, 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS }; static int CheckMode(int mb_x, int mb_y, int mode) { if (mode == B_DC_PRED) { if (mb_x == 0) { return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT; } else { return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED; } } return mode; } static void Copy32b(uint8_t* const dst, const uint8_t* const src) { memcpy(dst, src, 4); } static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src, uint8_t* const dst) { switch (bits >> 30) { case 3: VP8Transform(src, dst, 0); break; case 2: VP8TransformAC3(src, dst); break; case 1: VP8TransformDC(src, dst); break; default: break; } } static void DoUVTransform(uint32_t bits, const int16_t* const src, uint8_t* const dst) { if (bits & 0xff) { // any non-zero coeff at all? if (bits & 0xaa) { // any non-zero AC coefficient? VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V } else { VP8TransformDCUV(src, dst); } } } static void ReconstructRow(const VP8Decoder* const dec, const VP8ThreadContext* ctx) { int j; int mb_x; const int mb_y = ctx->mb_y_; const int cache_id = ctx->id_; uint8_t* const y_dst = dec->yuv_b_ + Y_OFF; uint8_t* const u_dst = dec->yuv_b_ + U_OFF; uint8_t* const v_dst = dec->yuv_b_ + V_OFF; // Initialize left-most block. for (j = 0; j < 16; ++j) { y_dst[j * BPS - 1] = 129; } for (j = 0; j < 8; ++j) { u_dst[j * BPS - 1] = 129; v_dst[j * BPS - 1] = 129; } // Init top-left sample on left column too. if (mb_y > 0) { y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129; } else { // we only need to do this init once at block (0,0). // Afterward, it remains valid for the whole topmost row. memset(y_dst - BPS - 1, 127, 16 + 4 + 1); memset(u_dst - BPS - 1, 127, 8 + 1); memset(v_dst - BPS - 1, 127, 8 + 1); } // Reconstruct one row. for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { const VP8MBData* const block = ctx->mb_data_ + mb_x; // Rotate in the left samples from previously decoded block. We move four // pixels at a time for alignment reason, and because of in-loop filter. if (mb_x > 0) { for (j = -1; j < 16; ++j) { Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]); } for (j = -1; j < 8; ++j) { Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]); Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]); } } { // bring top samples into the cache VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x; const int16_t* const coeffs = block->coeffs_; uint32_t bits = block->non_zero_y_; int n; if (mb_y > 0) { memcpy(y_dst - BPS, top_yuv[0].y, 16); memcpy(u_dst - BPS, top_yuv[0].u, 8); memcpy(v_dst - BPS, top_yuv[0].v, 8); } // predict and add residuals if (block->is_i4x4_) { // 4x4 uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16); if (mb_y > 0) { if (mb_x >= dec->mb_w_ - 1) { // on rightmost border memset(top_right, top_yuv[0].y[15], sizeof(*top_right)); } else { memcpy(top_right, top_yuv[1].y, sizeof(*top_right)); } } // replicate the top-right pixels below top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0]; // predict and add residuals for all 4x4 blocks in turn. for (n = 0; n < 16; ++n, bits <<= 2) { uint8_t* const dst = y_dst + kScan[n]; VP8PredLuma4[block->imodes_[n]](dst); DoTransform(bits, coeffs + n * 16, dst); } } else { // 16x16 const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]); VP8PredLuma16[pred_func](y_dst); if (bits != 0) { for (n = 0; n < 16; ++n, bits <<= 2) { DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]); } } } { // Chroma const uint32_t bits_uv = block->non_zero_uv_; const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_); VP8PredChroma8[pred_func](u_dst); VP8PredChroma8[pred_func](v_dst); DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst); DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst); } // stash away top samples for next block if (mb_y < dec->mb_h_ - 1) { memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16); memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8); memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8); } } // Transfer reconstructed samples from yuv_b_ cache to final destination. { const int y_offset = cache_id * 16 * dec->cache_y_stride_; const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset; uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset; uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset; for (j = 0; j < 16; ++j) { memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16); } for (j = 0; j < 8; ++j) { memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8); memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8); } } } } //------------------------------------------------------------------------------ // Filtering // kFilterExtraRows[] = How many extra lines are needed on the MB boundary // for caching, given a filtering level. // Simple filter: up to 2 luma samples are read and 1 is written. // Complex filter: up to 4 luma samples are read and 3 are written. Same for // U/V, so it's 8 samples total (because of the 2x upsampling). static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 }; static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) { const VP8ThreadContext* const ctx = &dec->thread_ctx_; const int cache_id = ctx->id_; const int y_bps = dec->cache_y_stride_; const VP8FInfo* const f_info = ctx->f_info_ + mb_x; uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16; const int ilevel = f_info->f_ilevel_; const int limit = f_info->f_limit_; if (limit == 0) { return; } assert(limit >= 3); if (dec->filter_type_ == 1) { // simple if (mb_x > 0) { VP8SimpleHFilter16(y_dst, y_bps, limit + 4); } if (f_info->f_inner_) { VP8SimpleHFilter16i(y_dst, y_bps, limit); } if (mb_y > 0) { VP8SimpleVFilter16(y_dst, y_bps, limit + 4); } if (f_info->f_inner_) { VP8SimpleVFilter16i(y_dst, y_bps, limit); } } else { // complex const int uv_bps = dec->cache_uv_stride_; uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8; uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8; const int hev_thresh = f_info->hev_thresh_; if (mb_x > 0) { VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); } if (f_info->f_inner_) { VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh); VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh); } if (mb_y > 0) { VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); } if (f_info->f_inner_) { VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh); VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh); } } } // Filter the decoded macroblock row (if needed) static void FilterRow(const VP8Decoder* const dec) { int mb_x; const int mb_y = dec->thread_ctx_.mb_y_; assert(dec->thread_ctx_.filter_row_); for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) { DoFilter(dec, mb_x, mb_y); } } //------------------------------------------------------------------------------ // Precompute the filtering strength for each segment and each i4x4/i16x16 mode. static void PrecomputeFilterStrengths(VP8Decoder* const dec) { if (dec->filter_type_ > 0) { int s; const VP8FilterHeader* const hdr = &dec->filter_hdr_; for (s = 0; s < NUM_MB_SEGMENTS; ++s) { int i4x4; // First, compute the initial level int base_level; if (dec->segment_hdr_.use_segment_) { base_level = dec->segment_hdr_.filter_strength_[s]; if (!dec->segment_hdr_.absolute_delta_) { base_level += hdr->level_; } } else { base_level = hdr->level_; } for (i4x4 = 0; i4x4 <= 1; ++i4x4) { VP8FInfo* const info = &dec->fstrengths_[s][i4x4]; int level = base_level; if (hdr->use_lf_delta_) { level += hdr->ref_lf_delta_[0]; if (i4x4) { level += hdr->mode_lf_delta_[0]; } } level = (level < 0) ? 0 : (level > 63) ? 63 : level; if (level > 0) { int ilevel = level; if (hdr->sharpness_ > 0) { if (hdr->sharpness_ > 4) { ilevel >>= 2; } else { ilevel >>= 1; } if (ilevel > 9 - hdr->sharpness_) { ilevel = 9 - hdr->sharpness_; } } if (ilevel < 1) ilevel = 1; info->f_ilevel_ = ilevel; info->f_limit_ = 2 * level + ilevel; info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0; } else { info->f_limit_ = 0; // no filtering } info->f_inner_ = i4x4; } } } } //------------------------------------------------------------------------------ // Dithering #define DITHER_AMP_TAB_SIZE 12 static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = { // roughly, it's dqm->uv_mat_[1] 8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1 }; void VP8InitDithering(const WebPDecoderOptions* const options, VP8Decoder* const dec) { assert(dec != NULL); if (options != NULL) { const int d = options->dithering_strength; const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1; const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100); if (f > 0) { int s; int all_amp = 0; for (s = 0; s < NUM_MB_SEGMENTS; ++s) { VP8QuantMatrix* const dqm = &dec->dqm_[s]; if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) { // TODO(skal): should we specially dither more for uv_quant_ < 0? const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_; dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3; } all_amp |= dqm->dither_; } if (all_amp != 0) { VP8InitRandom(&dec->dithering_rg_, 1.0f); dec->dither_ = 1; } } // potentially allow alpha dithering dec->alpha_dithering_ = options->alpha_dithering_strength; if (dec->alpha_dithering_ > 100) { dec->alpha_dithering_ = 100; } else if (dec->alpha_dithering_ < 0) { dec->alpha_dithering_ = 0; } } } // minimal amp that will provide a non-zero dithering effect #define MIN_DITHER_AMP 4 #define DITHER_DESCALE 4 #define DITHER_DESCALE_ROUNDER (1 << (DITHER_DESCALE - 1)) #define DITHER_AMP_BITS 8 #define DITHER_AMP_CENTER (1 << DITHER_AMP_BITS) static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) { int i, j; for (j = 0; j < 8; ++j) { for (i = 0; i < 8; ++i) { // TODO: could be made faster with SSE2 const int bits = VP8RandomBits2(rg, DITHER_AMP_BITS + 1, amp) - DITHER_AMP_CENTER; // Convert to range: [-2,2] for dither=50, [-4,4] for dither=100 const int delta = (bits + DITHER_DESCALE_ROUNDER) >> DITHER_DESCALE; const int v = (int)dst[i] + delta; dst[i] = (v < 0) ? 0 : (v > 255) ? 255u : (uint8_t)v; } dst += bps; } } static void DitherRow(VP8Decoder* const dec) { int mb_x; assert(dec->dither_); for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) { const VP8ThreadContext* const ctx = &dec->thread_ctx_; const VP8MBData* const data = ctx->mb_data_ + mb_x; const int cache_id = ctx->id_; const int uv_bps = dec->cache_uv_stride_; if (data->dither_ >= MIN_DITHER_AMP) { uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8; uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8; Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_); Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_); } } } //------------------------------------------------------------------------------ // This function is called after a row of macroblocks is finished decoding. // It also takes into account the following restrictions: // * In case of in-loop filtering, we must hold off sending some of the bottom // pixels as they are yet unfiltered. They will be when the next macroblock // row is decoded. Meanwhile, we must preserve them by rotating them in the // cache area. This doesn't hold for the very bottom row of the uncropped // picture of course. // * we must clip the remaining pixels against the cropping area. The VP8Io // struct must have the following fields set correctly before calling put(): #define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB // Finalize and transmit a complete row. Return false in case of user-abort. static int FinishRow(VP8Decoder* const dec, VP8Io* const io) { int ok = 1; const VP8ThreadContext* const ctx = &dec->thread_ctx_; const int cache_id = ctx->id_; const int extra_y_rows = kFilterExtraRows[dec->filter_type_]; const int ysize = extra_y_rows * dec->cache_y_stride_; const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_; const int y_offset = cache_id * 16 * dec->cache_y_stride_; const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; uint8_t* const ydst = dec->cache_y_ - ysize + y_offset; uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset; uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset; const int mb_y = ctx->mb_y_; const int is_first_row = (mb_y == 0); const int is_last_row = (mb_y >= dec->br_mb_y_ - 1); if (ctx->filter_row_) { FilterRow(dec); } if (dec->dither_) { DitherRow(dec); } if (io->put != NULL) { int y_start = MACROBLOCK_VPOS(mb_y); int y_end = MACROBLOCK_VPOS(mb_y + 1); if (!is_first_row) { y_start -= extra_y_rows; io->y = ydst; io->u = udst; io->v = vdst; } else { io->y = dec->cache_y_ + y_offset; io->u = dec->cache_u_ + uv_offset; io->v = dec->cache_v_ + uv_offset; } if (!is_last_row) { y_end -= extra_y_rows; } if (y_end > io->crop_bottom) { y_end = io->crop_bottom; // make sure we don't overflow on last row. } io->a = NULL; if (dec->alpha_data_ != NULL && y_start < y_end) { // TODO(skal): testing presence of alpha with dec->alpha_data_ is not a // good idea. io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start); if (io->a == NULL) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "Could not decode alpha data."); } } if (y_start < io->crop_top) { const int delta_y = io->crop_top - y_start; y_start = io->crop_top; assert(!(delta_y & 1)); io->y += dec->cache_y_stride_ * delta_y; io->u += dec->cache_uv_stride_ * (delta_y >> 1); io->v += dec->cache_uv_stride_ * (delta_y >> 1); if (io->a != NULL) { io->a += io->width * delta_y; } } if (y_start < y_end) { io->y += io->crop_left; io->u += io->crop_left >> 1; io->v += io->crop_left >> 1; if (io->a != NULL) { io->a += io->crop_left; } io->mb_y = y_start - io->crop_top; io->mb_w = io->crop_right - io->crop_left; io->mb_h = y_end - y_start; ok = io->put(io); } } // rotate top samples if needed if (cache_id + 1 == dec->num_caches_) { if (!is_last_row) { memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize); memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize); memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize); } } return ok; } #undef MACROBLOCK_VPOS //------------------------------------------------------------------------------ int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) { int ok = 1; VP8ThreadContext* const ctx = &dec->thread_ctx_; const int filter_row = (dec->filter_type_ > 0) && (dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_); // ctx->id_ and ctx->f_info_ are already set ctx->mb_y_ = dec->mb_y_; ctx->filter_row_ = filter_row; ReconstructRow(dec, ctx); ok = FinishRow(dec, io); return ok; } //------------------------------------------------------------------------------ // Finish setting up the decoding parameter once user's setup() is called. VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) { // Call setup() first. This may trigger additional decoding features on 'io'. // Note: Afterward, we must call teardown() no matter what. if (io->setup != NULL && !io->setup(io)) { VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed"); return dec->status_; } // Disable filtering per user request if (io->bypass_filtering) { dec->filter_type_ = 0; } // TODO(skal): filter type / strength / sharpness forcing // Define the area where we can skip in-loop filtering, in case of cropping. // // 'Simple' filter reads two luma samples outside of the macroblock // and filters one. It doesn't filter the chroma samples. Hence, we can // avoid doing the in-loop filtering before crop_top/crop_left position. // For the 'Complex' filter, 3 samples are read and up to 3 are filtered. // Means: there's a dependency chain that goes all the way up to the // top-left corner of the picture (MB #0). We must filter all the previous // macroblocks. // TODO(skal): add an 'approximate_decoding' option, that won't produce // a 1:1 bit-exactness for complex filtering? { const int extra_pixels = kFilterExtraRows[dec->filter_type_]; if (dec->filter_type_ == 2) { // For complex filter, we need to preserve the dependency chain. dec->tl_mb_x_ = 0; dec->tl_mb_y_ = 0; } else { // For simple filter, we can filter only the cropped region. // We include 'extra_pixels' on the other side of the boundary, since // vertical or horizontal filtering of the previous macroblock can // modify some abutting pixels. dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4; dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4; if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0; if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0; } // We need some 'extra' pixels on the right/bottom. dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4; dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4; if (dec->br_mb_x_ > dec->mb_w_) { dec->br_mb_x_ = dec->mb_w_; } if (dec->br_mb_y_ > dec->mb_h_) { dec->br_mb_y_ = dec->mb_h_; } } PrecomputeFilterStrengths(dec); return VP8_STATUS_OK; } int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) { int ok = 1; if (io->teardown != NULL) { io->teardown(io); } return ok; } #define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case // Initialize multi/single-thread worker static int InitThreadContext(VP8Decoder* const dec) { dec->cache_id_ = 0; dec->num_caches_ = ST_CACHE_LINES; return 1; } #undef ST_CACHE_LINES //------------------------------------------------------------------------------ // Memory setup static int AllocateMemory(VP8Decoder* const dec) { const int num_caches = dec->num_caches_; const int mb_w = dec->mb_w_; // Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise. const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t); const size_t top_size = sizeof(VP8TopSamples) * mb_w; const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB); const size_t f_info_size = (dec->filter_type_ > 0) ? mb_w * sizeof(VP8FInfo) : 0; const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_); const size_t mb_data_size = mb_w * sizeof(*dec->mb_data_); const size_t cache_height = (16 * num_caches + kFilterExtraRows[dec->filter_type_]) * 3 / 2; const size_t cache_size = top_size * cache_height; // alpha_size is the only one that scales as width x height. const uint64_t alpha_size = (dec->alpha_data_ != NULL) ? (uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL; const uint64_t needed = (uint64_t)intra_pred_mode_size + top_size + mb_info_size + f_info_size + yuv_size + mb_data_size + cache_size + alpha_size + ALIGN_CST; uint8_t* mem; if (needed != (size_t)needed) return 0; // check for overflow if (needed > dec->mem_size_) { tvg::free(dec->mem_); dec->mem_size_ = 0; dec->mem_ = tvg::malloc(needed * sizeof(uint8_t)); if (dec->mem_ == NULL) { return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, "no memory during frame initialization."); } // down-cast is ok, thanks to WebPSafeAlloc() above. dec->mem_size_ = (size_t)needed; } mem = (uint8_t*)dec->mem_; dec->intra_t_ = (uint8_t*)mem; mem += intra_pred_mode_size; dec->yuv_t_ = (VP8TopSamples*)mem; mem += top_size; dec->mb_info_ = ((VP8MB*)mem) + 1; mem += mb_info_size; dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL; mem += f_info_size; dec->thread_ctx_.id_ = 0; dec->thread_ctx_.f_info_ = dec->f_info_; mem = (uint8_t*)DO_ALIGN(mem); assert((yuv_size & ALIGN_CST) == 0); dec->yuv_b_ = (uint8_t*)mem; mem += yuv_size; dec->mb_data_ = (VP8MBData*)mem; dec->thread_ctx_.mb_data_ = (VP8MBData*)mem; mem += mb_data_size; dec->cache_y_stride_ = 16 * mb_w; dec->cache_uv_stride_ = 8 * mb_w; { const int extra_rows = kFilterExtraRows[dec->filter_type_]; const int extra_y = extra_rows * dec->cache_y_stride_; const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_; dec->cache_y_ = ((uint8_t*)mem) + extra_y; dec->cache_u_ = dec->cache_y_ + 16 * num_caches * dec->cache_y_stride_ + extra_uv; dec->cache_v_ = dec->cache_u_ + 8 * num_caches * dec->cache_uv_stride_ + extra_uv; dec->cache_id_ = 0; } mem += cache_size; // alpha plane dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL; mem += alpha_size; assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_); // note: left/top-info is initialized once for all. memset(dec->mb_info_ - 1, 0, mb_info_size); VP8InitScanline(dec); // initialize left too. // initialize top memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size); return 1; } static void InitIo(VP8Decoder* const dec, VP8Io* io) { // prepare 'io' io->mb_y = 0; io->y = dec->cache_y_; io->u = dec->cache_u_; io->v = dec->cache_v_; io->y_stride = dec->cache_y_stride_; io->uv_stride = dec->cache_uv_stride_; io->a = NULL; } int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) { if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_. if (!AllocateMemory(dec)) return 0; InitIo(dec, io); VP8DspInit(); // Init critical function pointers and look-up tables. return 1; } //------------------------------------------------------------------------------ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator.cpp000664 001750 001750 00000002067 15164251010 051502 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-globals.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-generator.inc.h" #define BUILTIN_UNDERSCORED_ID async_generator #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup asyncgenerator ECMA AsyncGenerator object built-in * @{ */ /** * @} * @} * @} */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-set-iterator-prototype.cpp000664 001750 001750 00000006157 15164251010 053072 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-container-object.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BUILTIN_SET_ITERATOR_PROTOTYPE_ROUTINE_START = 0, ECMA_BUILTIN_SET_ITERATOR_PROTOTYPE_OBJECT_NEXT, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-set-iterator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID set_iterator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup %setiteratorprototype% ECMA %SetIteratorPrototype% object built-in * @{ */ /** * The %SetIteratorPrototype% object's 'next' routine * * See also: * ECMA-262 v6, 23.2.5.2.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object, if success * error - otherwise */ static ecma_value_t ecma_builtin_set_iterator_prototype_object_next (ecma_value_t this_val) /**< this argument */ { return ecma_op_container_iterator_next (this_val, ECMA_OBJECT_CLASS_SET_ITERATOR); } /* ecma_builtin_set_iterator_prototype_object_next */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_set_iterator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide *routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_list, arguments_number); switch (builtin_routine_id) { case ECMA_BUILTIN_SET_ITERATOR_PROTOTYPE_OBJECT_NEXT: { return ecma_builtin_set_iterator_prototype_object_next (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_set_iterator_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/bit_reader.h000664 001750 001750 00000014611 15164251010 035727 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Boolean decoder // // Author: Skal (pascal.massimino@gmail.com) // Vikas Arora (vikaas.arora@gmail.com) #ifndef WEBP_UTILS_BIT_READER_H_ #define WEBP_UTILS_BIT_READER_H_ #include #ifdef _MSC_VER #include // _byteswap_ulong #endif #include "../webp/types.h" #ifdef __cplusplus extern "C" { #endif // The Boolean decoder needs to maintain infinite precision on the value_ field. // However, since range_ is only 8bit, we only need an active window of 8 bits // for value_. Left bits (MSB) gets zeroed and shifted away when value_ falls // below 128, range_ is updated, and fresh bits read from the bitstream are // brought in as LSB. To avoid reading the fresh bits one by one (slow), we // cache BITS of them ahead. The total of (BITS + 8) bits must fit into a // natural register (with type bit_t). To fetch BITS bits from bitstream we // use a type lbit_t. // // BITS can be any multiple of 8 from 8 to 56 (inclusive). // Pick values that fit natural register size. #if defined(__i386__) || defined(_M_IX86) // x86 32bit #define BITS 24 #elif defined(__x86_64__) || defined(_M_X64) // x86 64bit #define BITS 56 #elif defined(__arm__) || defined(_M_ARM) // ARM #define BITS 24 #elif defined(__mips__) // MIPS #define BITS 24 #else // reasonable default #define BITS 24 // TODO(skal): test aarch64 and find the proper BITS value. #endif //------------------------------------------------------------------------------ // Derived types and constants: // bit_t = natural register type for storing 'value_' (which is BITS+8 bits) // range_t = register for 'range_' (which is 8bits only) #if (BITS > 24) typedef uint64_t bit_t; #else typedef uint32_t bit_t; #endif typedef uint32_t range_t; //------------------------------------------------------------------------------ // Bitreader typedef struct VP8BitReader VP8BitReader; struct VP8BitReader { // boolean decoder (keep the field ordering as is!) bit_t value_; // current value range_t range_; // current range minus 1. In [127, 254] interval. int bits_; // number of valid bits left // read buffer const uint8_t* buf_; // next byte to be read const uint8_t* buf_end_; // end of read buffer int eof_; // true if input is exhausted }; // Initialize the bit reader and the boolean decoder. void VP8InitBitReader(VP8BitReader* const br, const uint8_t* const start, const uint8_t* const end); // Update internal pointers to displace the byte buffer by the // relative offset 'offset'. void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset); // return the next value made of 'num_bits' bits uint32_t VP8GetValue(VP8BitReader* const br, int num_bits); static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) { return VP8GetValue(br, 1); } // return the next value with sign-extension. int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits); // bit_reader_inl.h will implement the following methods: // static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) // static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) // and should be included by the .c files that actually need them. // This is to avoid recompiling the whole library whenever this file is touched, // and also allowing platform-specific ad-hoc hacks. // ----------------------------------------------------------------------------- // Bitreader for lossless format // maximum number of bits (inclusive) the bit-reader can handle: #define VP8L_MAX_NUM_BIT_READ 24 #define VP8L_LBITS 64 // Number of bits prefetched (= bit-size of vp8l_val_t). #define VP8L_WBITS 32 // Minimum number of bytes ready after VP8LFillBitWindow. typedef uint64_t vp8l_val_t; // right now, this bit-reader can only use 64bit. typedef struct { vp8l_val_t val_; // pre-fetched bits const uint8_t* buf_; // input byte buffer size_t len_; // buffer length size_t pos_; // byte position in buf_ int bit_pos_; // current bit-reading position in val_ int eos_; // true if a bit was read past the end of buffer } VP8LBitReader; void VP8LInitBitReader(VP8LBitReader* const br, const uint8_t* const start, size_t length); // Sets a new data buffer. void VP8LBitReaderSetBuffer(VP8LBitReader* const br, const uint8_t* const buffer, size_t length); // Reads the specified number of bits from read buffer. // Flags an error in case end_of_stream or n_bits is more than the allowed limit // of VP8L_MAX_NUM_BIT_READ (inclusive). // Flags eos_ if this read attempt is going to cross the read buffer. uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits); // Return the prefetched bits, so they can be looked up. static WEBP_INLINE uint32_t VP8LPrefetchBits(VP8LBitReader* const br) { return (uint32_t)(br->val_ >> (br->bit_pos_ & (VP8L_LBITS - 1))); } // Returns true if there was an attempt at reading bit past the end of // the buffer. Doesn't set br->eos_ flag. static WEBP_INLINE int VP8LIsEndOfStream(const VP8LBitReader* const br) { assert(br->pos_ <= br->len_); return br->eos_ || ((br->pos_ == br->len_) && (br->bit_pos_ > VP8L_LBITS)); } // For jumping over a number of bits in the bit stream when accessed with // VP8LPrefetchBits and VP8LFillBitWindow. static WEBP_INLINE void VP8LSetBitPos(VP8LBitReader* const br, int val) { br->bit_pos_ = val; br->eos_ = VP8LIsEndOfStream(br); } // Advances the read buffer by 4 bytes to make room for reading next 32 bits. // Speed critical, but infrequent part of the code can be non-inlined. extern void VP8LDoFillBitWindow(VP8LBitReader* const br); static WEBP_INLINE void VP8LFillBitWindow(VP8LBitReader* const br) { if (br->bit_pos_ >= VP8L_WBITS) VP8LDoFillBitWindow(br); } #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_UTILS_BIT_READER_H_ */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-error-prototype.cpp000664 001750 001750 00000012170 15164251010 051571 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #include "lit-char-helpers.h" #include "lit-magic-strings.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_ERROR_PROTOTYPE_ROUTINE_START = 0, ECMA_ERROR_PROTOTYPE_ROUTINE_TO_STRING, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-error-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID error_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup errorprototype ECMA Error.prototype object built-in * @{ */ /** * Helper method to get a property value from an error object * * @return ecma_string_t */ static ecma_string_t * ecma_builtin_error_prototype_object_to_string_helper (ecma_object_t *obj_p, /**< error object */ lit_magic_string_id_t property_id, /**< property id */ lit_magic_string_id_t default_value) /**< default prop value */ { ecma_value_t prop_value = ecma_op_object_get_by_magic_id (obj_p, property_id); if (ECMA_IS_VALUE_ERROR (prop_value)) { return NULL; } if (ecma_is_value_undefined (prop_value)) { return ecma_get_magic_string (default_value); } ecma_string_t *ret_str_p = ecma_op_to_string (prop_value); ecma_free_value (prop_value); return ret_str_p; } /* ecma_builtin_error_prototype_object_to_string_helper */ /** * The Error.prototype object's 'toString' routine * * See also: * ECMA-262 v5, 15.11.4.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_error_prototype_object_to_string (ecma_value_t this_arg) /**< this argument */ { /* 2. */ if (!ecma_is_value_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (this_arg); ecma_string_t *name_string_p = ecma_builtin_error_prototype_object_to_string_helper (obj_p, LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_ERROR_UL); if (JERRY_UNLIKELY (name_string_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_string_t *msg_string_p = ecma_builtin_error_prototype_object_to_string_helper (obj_p, LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY); if (JERRY_UNLIKELY (msg_string_p == NULL)) { ecma_deref_ecma_string (name_string_p); return ECMA_VALUE_ERROR; } if (ecma_string_is_empty (name_string_p)) { return ecma_make_string_value (msg_string_p); } if (ecma_string_is_empty (msg_string_p)) { return ecma_make_string_value (name_string_p); } ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (name_string_p); ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) ": ", 2); ecma_stringbuilder_append (&builder, msg_string_p); ecma_deref_ecma_string (name_string_p); ecma_deref_ecma_string (msg_string_p); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_error_prototype_object_to_string */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_error_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments passed to * routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_number, arguments_list_p); switch (builtin_routine_id) { case ECMA_ERROR_PROTOTYPE_ROUTINE_TO_STRING: { return ecma_builtin_error_prototype_object_to_string (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_error_prototype_dispatch_routine */ /** * @} * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-util.cpp000664 001750 001750 00000062370 15164251010 044624 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-helpers.h" #include "js-parser-internal.h" #if JERRY_PARSER /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_utils Utility * @{ */ /**********************************************************************/ /* Emitting byte codes */ /**********************************************************************/ /** * Append two bytes to the cbc stream. */ static void parser_emit_two_bytes (parser_context_t *context_p, /**< context */ uint8_t first_byte, /**< first byte */ uint8_t second_byte) /**< second byte */ { uint32_t last_position = context_p->byte_code.last_position; if (last_position + 2 <= PARSER_CBC_STREAM_PAGE_SIZE) { parser_mem_page_t *page_p = context_p->byte_code.last_p; page_p->bytes[last_position] = first_byte; page_p->bytes[last_position + 1] = second_byte; context_p->byte_code.last_position = last_position + 2; } else if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE) { parser_mem_page_t *page_p; parser_cbc_stream_alloc_page (context_p, &context_p->byte_code); page_p = context_p->byte_code.last_p; page_p->bytes[0] = first_byte; page_p->bytes[1] = second_byte; context_p->byte_code.last_position = 2; } else { context_p->byte_code.last_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] = first_byte; parser_cbc_stream_alloc_page (context_p, &context_p->byte_code); context_p->byte_code.last_p->bytes[0] = second_byte; context_p->byte_code.last_position = 1; } } /* parser_emit_two_bytes */ /** * Append byte to the end of the current byte code stream. * * @param context_p parser context * @param byte byte */ #define PARSER_APPEND_TO_BYTE_CODE(context_p, byte) \ if ((context_p)->byte_code.last_position >= PARSER_CBC_STREAM_PAGE_SIZE) \ { \ parser_cbc_stream_alloc_page ((context_p), &(context_p)->byte_code); \ } \ (context_p)->byte_code.last_p->bytes[(context_p)->byte_code.last_position++] = (uint8_t) (byte) #if JERRY_PARSER_DUMP_BYTE_CODE /** * Print literal corresponding to the current index */ static void parser_print_literal (parser_context_t *context_p, /**< context */ uint16_t literal_index) /**< index of literal */ { parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p; parser_scope_stack_t *scope_stack_end_p = scope_stack_p + context_p->scope_stack_top; bool in_scope_literal = false; while (scope_stack_p < scope_stack_end_p) { scope_stack_end_p--; if (scope_stack_end_p->map_from == PARSER_SCOPE_STACK_FUNC) { if (literal_index == scope_stack_end_p->map_to) { in_scope_literal = true; break; } } else if (literal_index == scanner_decode_map_to (scope_stack_end_p)) { in_scope_literal = true; break; } } if (literal_index < PARSER_REGISTER_START) { JERRY_DEBUG_MSG (in_scope_literal ? " IDX:%d->" : " idx:%d->", literal_index); lexer_literal_t *literal_p = PARSER_GET_LITERAL (literal_index); util_print_literal (literal_p); return; } if (!in_scope_literal) { JERRY_DEBUG_MSG (" reg:%d", (int) (literal_index - PARSER_REGISTER_START)); return; } JERRY_DEBUG_MSG (" REG:%d->", (int) (literal_index - PARSER_REGISTER_START)); lexer_literal_t *literal_p = PARSER_GET_LITERAL (scope_stack_end_p->map_from); util_print_literal (literal_p); } /* parser_print_literal */ #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ /** * Append the current byte code to the stream */ void parser_flush_cbc (parser_context_t *context_p) /**< context */ { uint8_t flags; uint16_t last_opcode = context_p->last_cbc_opcode; if (last_opcode == PARSER_CBC_UNAVAILABLE) { return; } context_p->status_flags |= PARSER_NO_END_LABEL; if (PARSER_IS_BASIC_OPCODE (last_opcode)) { cbc_opcode_t opcode = (cbc_opcode_t) last_opcode; JERRY_ASSERT (opcode < CBC_END); flags = cbc_flags[opcode]; PARSER_APPEND_TO_BYTE_CODE (context_p, opcode); context_p->byte_code_size++; } else { cbc_ext_opcode_t opcode = (cbc_ext_opcode_t) PARSER_GET_EXT_OPCODE (last_opcode); JERRY_ASSERT (opcode < CBC_EXT_END); flags = cbc_ext_flags[opcode]; parser_emit_two_bytes (context_p, CBC_EXT_OPCODE, (uint8_t) opcode); context_p->byte_code_size += 2; } JERRY_ASSERT ((flags >> CBC_STACK_ADJUST_SHIFT) >= CBC_STACK_ADJUST_BASE || (CBC_STACK_ADJUST_BASE - (flags >> CBC_STACK_ADJUST_SHIFT)) <= context_p->stack_depth); PARSER_PLUS_EQUAL_U16 (context_p->stack_depth, CBC_STACK_ADJUST_VALUE (flags)); if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) { uint16_t literal_index = context_p->last_cbc.literal_index; parser_emit_two_bytes (context_p, (uint8_t) (literal_index & 0xff), (uint8_t) (literal_index >> 8)); context_p->byte_code_size += 2; } if (flags & CBC_HAS_LITERAL_ARG2) { uint16_t literal_index = context_p->last_cbc.value; parser_emit_two_bytes (context_p, (uint8_t) (literal_index & 0xff), (uint8_t) (literal_index >> 8)); context_p->byte_code_size += 2; if (!(flags & CBC_HAS_LITERAL_ARG)) { literal_index = context_p->last_cbc.third_literal_index; parser_emit_two_bytes (context_p, (uint8_t) (literal_index & 0xff), (uint8_t) (literal_index >> 8)); context_p->byte_code_size += 2; } } if (flags & CBC_HAS_BYTE_ARG) { uint8_t byte_argument = (uint8_t) context_p->last_cbc.value; JERRY_ASSERT (context_p->last_cbc.value <= CBC_MAXIMUM_BYTE_VALUE); if (flags & CBC_POP_STACK_BYTE_ARG) { JERRY_ASSERT (context_p->stack_depth >= byte_argument); PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, byte_argument); } PARSER_APPEND_TO_BYTE_CODE (context_p, byte_argument); context_p->byte_code_size++; } #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG (" [%3d] %s", (int) context_p->stack_depth, PARSER_IS_BASIC_OPCODE (last_opcode) ? cbc_names[last_opcode] : cbc_ext_names[PARSER_GET_EXT_OPCODE (last_opcode)]); if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) { parser_print_literal (context_p, context_p->last_cbc.literal_index); } if (flags & CBC_HAS_LITERAL_ARG2) { parser_print_literal (context_p, context_p->last_cbc.value); if (!(flags & CBC_HAS_LITERAL_ARG)) { parser_print_literal (context_p, context_p->last_cbc.third_literal_index); } } if (flags & CBC_HAS_BYTE_ARG) { if (last_opcode == CBC_PUSH_NUMBER_POS_BYTE || last_opcode == CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE) { JERRY_DEBUG_MSG (" number:%d", (int) context_p->last_cbc.value + 1); } else if (last_opcode == CBC_PUSH_NUMBER_NEG_BYTE || last_opcode == CBC_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE) { JERRY_DEBUG_MSG (" number:%d", -((int) context_p->last_cbc.value + 1)); } else { JERRY_DEBUG_MSG (" byte_arg:%d", (int) context_p->last_cbc.value); } } JERRY_DEBUG_MSG ("\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ if (context_p->stack_depth > context_p->stack_limit) { context_p->stack_limit = context_p->stack_depth; if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } /* parser_flush_cbc */ /** * Append a byte code */ void parser_emit_cbc (parser_context_t *context_p, /**< context */ uint16_t opcode) /**< opcode */ { JERRY_ASSERT (PARSER_ARGS_EQ (opcode, 0)); if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { parser_flush_cbc (context_p); } context_p->last_cbc_opcode = opcode; } /* parser_emit_cbc */ /** * Append a byte code with a literal argument */ void parser_emit_cbc_literal (parser_context_t *context_p, /**< context */ uint16_t opcode, /**< opcode */ uint16_t literal_index) /**< literal index */ { JERRY_ASSERT (PARSER_ARGS_EQ (opcode, CBC_HAS_LITERAL_ARG)); if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { parser_flush_cbc (context_p); } context_p->last_cbc_opcode = opcode; context_p->last_cbc.literal_index = literal_index; context_p->last_cbc.literal_type = LEXER_UNUSED_LITERAL; context_p->last_cbc.literal_keyword_type = LEXER_EOS; } /* parser_emit_cbc_literal */ /** * Append a byte code with a literal and value argument */ void parser_emit_cbc_literal_value (parser_context_t *context_p, /**< context */ uint16_t opcode, /**< opcode */ uint16_t literal_index, /**< literal index */ uint16_t value) /**< value */ { JERRY_ASSERT (PARSER_ARGS_EQ (opcode, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { parser_flush_cbc (context_p); } context_p->last_cbc_opcode = opcode; context_p->last_cbc.literal_index = literal_index; context_p->last_cbc.literal_type = LEXER_UNUSED_LITERAL; context_p->last_cbc.literal_keyword_type = LEXER_EOS; context_p->last_cbc.value = value; } /* parser_emit_cbc_literal_value */ /** * Append a byte code with the current literal argument */ void parser_emit_cbc_literal_from_token (parser_context_t *context_p, /**< context */ uint16_t opcode) /**< opcode */ { JERRY_ASSERT (PARSER_ARGS_EQ (opcode, CBC_HAS_LITERAL_ARG)); if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { parser_flush_cbc (context_p); } context_p->last_cbc_opcode = opcode; context_p->last_cbc.literal_index = context_p->lit_object.index; context_p->last_cbc.literal_type = context_p->token.lit_location.type; context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type; } /* parser_emit_cbc_literal_from_token */ /** * Append a byte code with a call argument */ void parser_emit_cbc_call (parser_context_t *context_p, /**< context */ uint16_t opcode, /**< opcode */ size_t call_arguments) /**< number of arguments */ { JERRY_ASSERT (PARSER_ARGS_EQ (opcode, CBC_HAS_BYTE_ARG)); JERRY_ASSERT (call_arguments <= CBC_MAXIMUM_BYTE_VALUE); if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { parser_flush_cbc (context_p); } context_p->last_cbc_opcode = opcode; context_p->last_cbc.value = (uint16_t) call_arguments; } /* parser_emit_cbc_call */ /** * Append a push number 1/2 byte code */ void parser_emit_cbc_push_number (parser_context_t *context_p, /**< context */ bool is_negative_number) /**< sign is negative */ { uint16_t value = context_p->lit_object.index; uint16_t lit_value = PARSER_INVALID_LITERAL_INDEX; if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { lit_value = context_p->last_cbc.literal_index; } else { if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; lit_value = context_p->last_cbc.value; } else if (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; lit_value = context_p->last_cbc.third_literal_index; } parser_flush_cbc (context_p); } } if (value == 0) { if (lit_value == PARSER_INVALID_LITERAL_INDEX) { context_p->last_cbc_opcode = CBC_PUSH_NUMBER_0; return; } context_p->last_cbc_opcode = CBC_PUSH_LITERAL_PUSH_NUMBER_0; context_p->last_cbc.literal_index = lit_value; return; } uint16_t opcode; if (lit_value == PARSER_INVALID_LITERAL_INDEX) { opcode = (is_negative_number ? CBC_PUSH_NUMBER_NEG_BYTE : CBC_PUSH_NUMBER_POS_BYTE); JERRY_ASSERT (CBC_STACK_ADJUST_VALUE (PARSER_GET_FLAGS (opcode)) == 1); } else { opcode = (is_negative_number ? CBC_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE); JERRY_ASSERT (CBC_STACK_ADJUST_VALUE (PARSER_GET_FLAGS (opcode)) == 2); context_p->last_cbc.literal_index = lit_value; } JERRY_ASSERT (value > 0 && value <= CBC_PUSH_NUMBER_BYTE_RANGE_END); context_p->last_cbc_opcode = opcode; context_p->last_cbc.value = (uint16_t) (value - 1); } /* parser_emit_cbc_push_number */ /** * Append a byte code with a branch argument */ void parser_emit_cbc_forward_branch (parser_context_t *context_p, /**< context */ uint16_t opcode, /**< opcode */ parser_branch_t *branch_p) /**< branch result */ { uint8_t flags; uint32_t extra_byte_code_increase; if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { parser_flush_cbc (context_p); } context_p->status_flags |= PARSER_NO_END_LABEL; if (PARSER_IS_BASIC_OPCODE (opcode)) { JERRY_ASSERT (opcode < CBC_END); flags = cbc_flags[opcode]; extra_byte_code_increase = 0; } else { PARSER_APPEND_TO_BYTE_CODE (context_p, CBC_EXT_OPCODE); opcode = (uint16_t) PARSER_GET_EXT_OPCODE (opcode); JERRY_ASSERT (opcode < CBC_EXT_END); flags = cbc_ext_flags[opcode]; extra_byte_code_increase = 1; } JERRY_ASSERT (flags & CBC_HAS_BRANCH_ARG); JERRY_ASSERT (CBC_BRANCH_IS_FORWARD (flags)); JERRY_ASSERT (CBC_BRANCH_OFFSET_LENGTH (opcode) == 1); /* Branch opcodes never push anything onto the stack. */ JERRY_ASSERT ((flags >> CBC_STACK_ADJUST_SHIFT) >= CBC_STACK_ADJUST_BASE || (CBC_STACK_ADJUST_BASE - (flags >> CBC_STACK_ADJUST_SHIFT)) <= context_p->stack_depth); PARSER_PLUS_EQUAL_U16 (context_p->stack_depth, CBC_STACK_ADJUST_VALUE (flags)); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG (" [%3d] %s\n", (int) context_p->stack_depth, extra_byte_code_increase == 0 ? cbc_names[opcode] : cbc_ext_names[opcode]); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ PARSER_PLUS_EQUAL_U16 (opcode, PARSER_MAX_BRANCH_LENGTH - 1); parser_emit_two_bytes (context_p, (uint8_t) opcode, 0); branch_p->page_p = context_p->byte_code.last_p; branch_p->offset = (context_p->byte_code.last_position - 1) | (context_p->byte_code_size << 8); context_p->byte_code_size += extra_byte_code_increase; #if PARSER_MAXIMUM_CODE_SIZE <= UINT16_MAX PARSER_APPEND_TO_BYTE_CODE (context_p, 0); #else /* PARSER_MAXIMUM_CODE_SIZE > UINT16_MAX */ parser_emit_two_bytes (context_p, 0, 0); #endif /* PARSER_MAXIMUM_CODE_SIZE <= UINT16_MAX */ context_p->byte_code_size += PARSER_MAX_BRANCH_LENGTH + 1; if (context_p->stack_depth > context_p->stack_limit) { context_p->stack_limit = context_p->stack_depth; if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } } /* parser_emit_cbc_forward_branch */ /** * Append a branch byte code and create an item. * * @return newly created parser branch node */ parser_branch_node_t * parser_emit_cbc_forward_branch_item (parser_context_t *context_p, /**< context */ uint16_t opcode, /**< opcode */ parser_branch_node_t *next_p) /**< next branch */ { parser_branch_t branch; parser_branch_node_t *new_item; /* Since byte code insertion may throw an out-of-memory error, * the branch is constructed locally, and copied later. */ parser_emit_cbc_forward_branch (context_p, opcode, &branch); new_item = (parser_branch_node_t *) parser_malloc (context_p, sizeof (parser_branch_node_t)); new_item->branch = branch; new_item->next_p = next_p; return new_item; } /* parser_emit_cbc_forward_branch_item */ /** * Append a byte code with a branch argument */ void parser_emit_cbc_backward_branch (parser_context_t *context_p, /**< context */ uint16_t opcode, /**< opcode */ uint32_t offset) /**< destination offset */ { uint8_t flags; #if JERRY_PARSER_DUMP_BYTE_CODE const char *name; #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { parser_flush_cbc (context_p); } context_p->status_flags |= PARSER_NO_END_LABEL; offset = context_p->byte_code_size - offset; if (PARSER_IS_BASIC_OPCODE (opcode)) { JERRY_ASSERT (opcode < CBC_END); flags = cbc_flags[opcode]; #if JERRY_PARSER_DUMP_BYTE_CODE name = cbc_names[opcode]; #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ } else { PARSER_APPEND_TO_BYTE_CODE (context_p, CBC_EXT_OPCODE); opcode = (uint16_t) PARSER_GET_EXT_OPCODE (opcode); JERRY_ASSERT (opcode < CBC_EXT_END); flags = cbc_ext_flags[opcode]; context_p->byte_code_size++; #if JERRY_PARSER_DUMP_BYTE_CODE name = cbc_ext_names[opcode]; #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ } JERRY_ASSERT (flags & CBC_HAS_BRANCH_ARG); JERRY_ASSERT (CBC_BRANCH_IS_BACKWARD (flags)); JERRY_ASSERT (CBC_BRANCH_OFFSET_LENGTH (opcode) == 1); JERRY_ASSERT (offset <= context_p->byte_code_size); /* Branch opcodes never push anything onto the stack. */ JERRY_ASSERT ((flags >> CBC_STACK_ADJUST_SHIFT) >= CBC_STACK_ADJUST_BASE || (CBC_STACK_ADJUST_BASE - (flags >> CBC_STACK_ADJUST_SHIFT)) <= context_p->stack_depth); PARSER_PLUS_EQUAL_U16 (context_p->stack_depth, CBC_STACK_ADJUST_VALUE (flags)); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG (" [%3d] %s\n", (int) context_p->stack_depth, name); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ context_p->byte_code_size += 2; #if PARSER_MAXIMUM_CODE_SIZE > UINT16_MAX if (offset > UINT16_MAX) { opcode++; context_p->byte_code_size++; } #endif /* PARSER_MAXIMUM_CODE_SIZE > UINT16_MAX */ if (offset > UINT8_MAX) { opcode++; context_p->byte_code_size++; } PARSER_APPEND_TO_BYTE_CODE (context_p, (uint8_t) opcode); #if PARSER_MAXIMUM_CODE_SIZE > UINT16_MAX if (offset > UINT16_MAX) { PARSER_APPEND_TO_BYTE_CODE (context_p, offset >> 16); } #endif /* PARSER_MAXIMUM_CODE_SIZE > UINT16_MAX */ if (offset > UINT8_MAX) { PARSER_APPEND_TO_BYTE_CODE (context_p, (offset >> 8) & 0xff); } PARSER_APPEND_TO_BYTE_CODE (context_p, offset & 0xff); } /* parser_emit_cbc_backward_branch */ #undef PARSER_CHECK_LAST_POSITION #undef PARSER_APPEND_TO_BYTE_CODE /** * Helper function for parser. * * @return a new string based on encode. */ ecma_string_t * parser_new_ecma_string_from_literal (lexer_literal_t *literal_p) /**< literal */ { JERRY_ASSERT (literal_p != NULL); ecma_string_t *new_string = NULL; if (literal_p->status_flags & LEXER_FLAG_ASCII) { new_string = ecma_new_ecma_string_from_ascii (literal_p->u.char_p, literal_p->prop.length); } else { new_string = ecma_new_ecma_string_from_utf8 (literal_p->u.char_p, literal_p->prop.length); } return new_string; } /* parser_new_ecma_string_from_literal */ /** * Set a branch to the current byte code position */ void parser_set_branch_to_current_position (parser_context_t *context_p, /**< context */ parser_branch_t *branch_p) /**< branch result */ { uint32_t delta; size_t offset; parser_mem_page_t *page_p = branch_p->page_p; if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { parser_flush_cbc (context_p); } context_p->status_flags &= (uint32_t) ~PARSER_NO_END_LABEL; JERRY_ASSERT (context_p->byte_code_size > (branch_p->offset >> 8)); delta = context_p->byte_code_size - (branch_p->offset >> 8); offset = (branch_p->offset & CBC_LOWER_SEVEN_BIT_MASK); JERRY_ASSERT (delta <= PARSER_MAXIMUM_CODE_SIZE); #if PARSER_MAXIMUM_CODE_SIZE <= UINT16_MAX page_p->bytes[offset++] = (uint8_t) (delta >> 8); if (offset >= PARSER_CBC_STREAM_PAGE_SIZE) { page_p = page_p->next_p; offset = 0; } #else /* PARSER_MAXIMUM_CODE_SIZE > UINT16_MAX */ page_p->bytes[offset++] = (uint8_t) (delta >> 16); if (offset >= PARSER_CBC_STREAM_PAGE_SIZE) { page_p = page_p->next_p; offset = 0; } page_p->bytes[offset++] = (uint8_t) ((delta >> 8) & 0xff); if (offset >= PARSER_CBC_STREAM_PAGE_SIZE) { page_p = page_p->next_p; offset = 0; } #endif /* PARSER_MAXIMUM_CODE_SIZE <= UINT16_MAX */ page_p->bytes[offset] = delta & 0xff; } /* parser_set_branch_to_current_position */ /** * Set breaks to the current byte code position */ void parser_set_breaks_to_current_position (parser_context_t *context_p, /**< context */ parser_branch_node_t *current_p) /**< branch list */ { while (current_p != NULL) { parser_branch_node_t *next_p = current_p->next_p; if (!(current_p->branch.offset & CBC_HIGHEST_BIT_MASK)) { parser_set_branch_to_current_position (context_p, ¤t_p->branch); } parser_free (current_p, sizeof (parser_branch_node_t)); current_p = next_p; } } /* parser_set_breaks_to_current_position */ /** * Set continues to the current byte code position */ void parser_set_continues_to_current_position (parser_context_t *context_p, /**< context */ parser_branch_node_t *current_p) /**< branch list */ { while (current_p != NULL) { if (current_p->branch.offset & CBC_HIGHEST_BIT_MASK) { parser_set_branch_to_current_position (context_p, ¤t_p->branch); } current_p = current_p->next_p; } } /* parser_set_continues_to_current_position */ /** * Return the size of internal record corresponding to a class field * * @return internal record size */ static size_t parser_get_class_field_info_size (uint8_t class_field_type) /**< class field type */ { if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) { return sizeof (scanner_range_t) + 1; } if (class_field_type & PARSER_CLASS_FIELD_NORMAL) { return sizeof (scanner_location_t) + 1; } return 1; } /* parser_get_class_field_info_size */ /** * Reverse the field list of a class */ void parser_reverse_class_fields (parser_context_t *context_p, /**< context */ size_t fields_size) /**< size of consumed memory */ { uint8_t *data_p = (uint8_t *) parser_malloc (context_p, fields_size); uint8_t *data_end_p = data_p + fields_size; uint8_t *current_p = data_p; bool has_fields = false; parser_stack_iterator_t iterator; JERRY_ASSERT (!(context_p->stack_top_uint8 & PARSER_CLASS_FIELD_END)); parser_stack_iterator_init (context_p, &iterator); do { uint8_t class_field_type = parser_stack_iterator_read_uint8 (&iterator); size_t info_size = parser_get_class_field_info_size (class_field_type); parser_stack_iterator_read (&iterator, current_p, info_size); parser_stack_iterator_skip (&iterator, info_size); current_p += info_size; if (!(class_field_type & PARSER_CLASS_FIELD_STATIC)) { has_fields = true; context_p->stack_top_uint8 = class_field_type; } } while (current_p < data_end_p); parser_stack_iterator_init (context_p, &iterator); current_p = data_end_p; bool has_static_fields = false; if (has_fields) { do { uint8_t class_field_type = current_p[-1]; size_t info_size = parser_get_class_field_info_size (class_field_type); if (!(class_field_type & PARSER_CLASS_FIELD_STATIC)) { current_p -= info_size; parser_stack_iterator_write (&iterator, current_p, info_size); parser_stack_iterator_skip (&iterator, info_size); continue; } if (!has_static_fields) { has_static_fields = true; current_p[-1] |= PARSER_CLASS_FIELD_END; } current_p -= info_size; } while (current_p > data_p); } else { /* All class fields are static. */ has_static_fields = true; JERRY_ASSERT (data_end_p[-1] & PARSER_CLASS_FIELD_STATIC); context_p->stack_top_uint8 = data_end_p[-1]; } if (has_static_fields) { current_p = data_end_p; do { uint8_t class_field_type = current_p[-1]; size_t info_size = parser_get_class_field_info_size (class_field_type); current_p -= info_size; if (class_field_type & PARSER_CLASS_FIELD_STATIC) { parser_stack_iterator_write (&iterator, current_p, info_size); parser_stack_iterator_skip (&iterator, info_size); } } while (current_p > data_p); } parser_free (data_p, fields_size); } /* parser_reverse_class_fields */ /** * @} * @} * @} */ #endif /* JERRY_PARSER */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgCompositor.h000664 001750 001750 00000014244 15164251010 037030 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_COMPOSITOR_H_ #define _TVG_WG_COMPOSITOR_H_ #include "tvgWgRenderTarget.h" #include "tvgWgRenderData.h" struct WgCompose: RenderCompositor { BlendMethod blend{}; RenderRegion aabb{}; CompositionFlag flags{}; bool masked{}; // indicate if composition allocates more than one render target }; class WgCompositor { private: // pipelines WgPipelines pipelines{}; // stage buffers WgStageBufferGeometry stageBufferGeometry{}; WgStageBufferUniform stageBufferPaint; // global stencil/depth buffer handles WGPUTexture texDepthStencil{}; WGPUTextureView texViewDepthStencil{}; WGPUTexture texDepthStencilMS{}; WGPUTextureView texViewDepthStencilMS{}; // global view matrix handles WGPUBuffer bufferViewMat{}; WGPUBindGroup bindGroupViewMat{}; // opacity value pool WGPUBuffer bufferOpacities[256]{}; WGPUBindGroup bindGroupOpacities[256]{}; // current render pass handles WGPURenderPassEncoder renderPassEncoder{}; WGPUCommandEncoder commandEncoder{}; WgRenderTarget* currentTarget{}; // intermediate render targets WgRenderTarget targetTemp0; WgRenderTarget targetTemp1; WGPUBindGroup bindGroupStorageTemp{}; // composition and blend geometries WgMeshData meshDataBlit; // render target dimensions uint32_t width{}; uint32_t height{}; // viewport utilities RenderRegion shrinkRenderRegion(const RenderRegion& rect); void copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src); void copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src, const RenderRegion& region); // base meshes draw void drawMesh(WgContext& context, WgMeshData* meshData); void drawMeshImage(WgContext& context, WgMeshData* meshData); // shapes void drawShape(WgContext& context, WgRenderDataShape* renderData); void blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod); void clipShape(WgContext& context, WgRenderDataShape* renderData); // strokes void drawStrokes(WgContext& context, WgRenderDataShape* renderData); void blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod); void clipStrokes(WgContext& context, WgRenderDataShape* renderData); // images void drawImage(WgContext& context, WgRenderDataPicture* renderData); void blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod); void clipImage(WgContext& context, WgRenderDataPicture* renderData); // scenes void drawScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose); void blendScene(WgContext& context, WgRenderTarget* src, WgCompose* compose); // the renderer prioritizes clipping with the stroke over the shape's fill void markupClipPath(WgContext& context, WgRenderDataShape* renderData); void renderClipPath(WgContext& context, WgRenderDataPaint* paint); void clearClipPath(WgContext& context, WgRenderDataPaint* paint); public: void initialize(WgContext& context, uint32_t width, uint32_t height); void initPools(WgContext& context); void release(WgContext& context); void releasePools(WgContext& context); void resize(WgContext& context, uint32_t width, uint32_t height); // render passes workflow void beginRenderPassMS(WGPUCommandEncoder encoder, WgRenderTarget* target, bool clear, WGPUColor clearColor = { 0.0, 0.0, 0.0, 0.0 }); void beginRenderPass(WGPUCommandEncoder encoder, WgRenderTarget* target); void endRenderPass(); // stage buffers operations void reset(WgContext& context); void flush(WgContext& context); // request shapes for drawing (staging) void requestShape(WgRenderDataShape* renderData); void requestImage(WgRenderDataPicture* renderData); // render shapes, images and scenes void renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod); void renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod); void renderScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose); void composeScene(WgContext& context, WgRenderTarget* src, WgRenderTarget* mask, WgCompose* compose); // blit render target to texture view (f.e. screen buffer) void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderTarget* src, WGPUTextureView dstView); // effects bool gaussianBlur(WgContext& context, WgRenderTarget* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose); bool dropShadow(WgContext& context, WgRenderTarget* dst, const RenderEffectDropShadow* params, const WgCompose* compose); bool fillEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectFill* params, const WgCompose* compose); bool tintEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTint* params, const WgCompose* compose); bool tritoneEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTritone* params, const WgCompose* compose); }; #endif // _TVG_WG_COMPOSITOR_H_ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieInterpolator.cpp000664 001750 001750 00000012164 15164251010 037752 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "tvgStr.h" #include "tvgMath.h" #include "tvgLottieInterpolator.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #define NEWTON_MIN_SLOPE 0.02f #define NEWTON_ITERATIONS 4 #define SUBDIVISION_PRECISION 0.0000001f #define SUBDIVISION_MAX_ITERATIONS 10 static inline float _constA(float aA1, float aA2) { return 1.0f - 3.0f * aA2 + 3.0f * aA1; } static inline float _constB(float aA1, float aA2) { return 3.0f * aA2 - 6.0f * aA1; } static inline float _constC(float aA1) { return 3.0f * aA1; } static inline float _getSlope(float t, float aA1, float aA2) { return 3.0f * _constA(aA1, aA2) * t * t + 2.0f * _constB(aA1, aA2) * t + _constC(aA1); } static inline float _calcBezier(float t, float aA1, float aA2) { return ((_constA(aA1, aA2) * t + _constB(aA1, aA2)) * t + _constC(aA1)) * t; } float LottieInterpolator::getTForX(float aX) { //Find interval where t lies auto intervalStart = 0.0f; auto currentSample = &samples[1]; auto lastSample = &samples[SPLINE_TABLE_SIZE - 1]; for (; currentSample != lastSample && *currentSample <= aX; ++currentSample) { intervalStart += SAMPLE_STEP_SIZE; } --currentSample; // t now lies between *currentSample and *currentSample+1 // Interpolate to provide an initial guess for t auto dist = (aX - *currentSample) / (*(currentSample + 1) - *currentSample); auto guessForT = intervalStart + dist * SAMPLE_STEP_SIZE; // 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. auto initialSlope = _getSlope(guessForT, outTangent.x, inTangent.x); if (initialSlope >= NEWTON_MIN_SLOPE) return NewtonRaphsonIterate(aX, guessForT); else if (initialSlope == 0.0f) return guessForT; else return binarySubdivide(aX, intervalStart, intervalStart + SAMPLE_STEP_SIZE); } float LottieInterpolator::binarySubdivide(float aX, float aA, float aB) { float x, t; int i = 0; do { t = aA + (aB - aA) / 2.0f; x = _calcBezier(t, outTangent.x, inTangent.x) - aX; if (x > 0.0f) aB = t; else aA = t; } while (fabsf(x) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); return t; } float LottieInterpolator::NewtonRaphsonIterate(float aX, float aGuessT) { // 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 auto currentX = _calcBezier(aGuessT, outTangent.x, inTangent.x) - aX; auto currentSlope = _getSlope(aGuessT, outTangent.x, inTangent.x); if (currentSlope == 0.0f) return aGuessT; aGuessT -= currentX / currentSlope; } return aGuessT; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ float LottieInterpolator::progress(float t) { if (outTangent.x == outTangent.y && inTangent.x == inTangent.y) return t; return _calcBezier(getTForX(t), outTangent.y, inTangent.y); } void LottieInterpolator::set(const char* key, Point& inTangent, Point& outTangent) { if (key) this->key = duplicate(key); this->inTangent = inTangent; this->outTangent = outTangent; if (outTangent.x == outTangent.y && inTangent.x == inTangent.y) return; //calculates sample values for (int i = 0; i < SPLINE_TABLE_SIZE; ++i) { samples[i] = _calcBezier(float(i) * SAMPLE_STEP_SIZE, outTangent.x, inTangent.x); } }external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-scanner-ops.cpp000664 001750 001750 00000042476 15164251010 044612 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "js-parser-internal.h" #include "js-scanner-internal.h" #include "lit-char-helpers.h" #if JERRY_PARSER /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_scanner Scanner * @{ */ /** * Add the "async" literal to the literal pool. */ void scanner_add_async_literal (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { lexer_lit_location_t async_literal; JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC); parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &async_literal, sizeof (lexer_lit_location_t)); lexer_lit_location_t *lit_location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &async_literal); lit_location_p->type |= SCANNER_LITERAL_IS_USED; if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) { lit_location_p->type |= SCANNER_LITERAL_NO_REG; } } /* scanner_add_async_literal */ /** * Init scanning the body of an arrow function. */ static void scanner_check_arrow_body (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { lexer_next_token (context_p); scanner_context_p->active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ARROW; if (context_p->token.type != LEXER_LEFT_BRACE) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; parser_stack_push_uint8 (context_p, SCAN_STACK_ARROW_EXPRESSION); return; } lexer_next_token (context_p); parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_ARROW); scanner_check_directives (context_p, scanner_context_p); } /* scanner_check_arrow_body */ /** * Process arrow function with argument list. */ void scanner_check_arrow (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); if (context_p->token.type != LEXER_ARROW || (context_p->token.flags & LEXER_WAS_NEWLINE)) { if (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC) { scanner_add_async_literal (context_p, scanner_context_p); } scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; scanner_pop_literal_pool (context_p, scanner_context_p); return; } if (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC) { parser_stack_pop (context_p, NULL, sizeof (lexer_lit_location_t) + 1); } scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; uint16_t status_flags = literal_pool_p->status_flags; bool is_async_arrow = (status_flags & SCANNER_LITERAL_POOL_MAY_ASYNC_ARROW) != 0; status_flags |= SCANNER_LITERAL_POOL_ARROW_FLAGS; status_flags &= (uint16_t) ~(SCANNER_LITERAL_POOL_IN_WITH | SCANNER_LITERAL_POOL_GENERATOR | SCANNER_LITERAL_POOL_ASYNC); context_p->status_flags &= (uint32_t) ~(PARSER_IS_GENERATOR_FUNCTION | PARSER_IS_ASYNC_FUNCTION); if (is_async_arrow) { status_flags |= SCANNER_LITERAL_POOL_ASYNC; context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION; } literal_pool_p->status_flags = status_flags; scanner_filter_arguments (context_p, scanner_context_p); scanner_check_arrow_body (context_p, scanner_context_p); } /* scanner_check_arrow */ /** * Process arrow function with a single argument. */ void scanner_scan_simple_arrow (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ const uint8_t *source_p) /**< identifier end position */ { uint16_t status_flags = SCANNER_LITERAL_POOL_ARROW_FLAGS; context_p->status_flags &= (uint32_t) ~(PARSER_IS_GENERATOR_FUNCTION | PARSER_IS_ASYNC_FUNCTION); if (scanner_context_p->async_source_p != NULL) { JERRY_ASSERT (scanner_context_p->async_source_p == source_p); status_flags |= SCANNER_LITERAL_POOL_ASYNC; context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION; } scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, status_flags); literal_pool_p->source_p = source_p; lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); location_p->type |= SCANNER_LITERAL_IS_ARG; /* Skip the => token, which size is two. */ context_p->source_p += 2; PARSER_PLUS_EQUAL_LC (context_p->column, 2); context_p->token.flags = (uint8_t) (context_p->token.flags & ~LEXER_NO_SKIP_SPACES); scanner_check_arrow_body (context_p, scanner_context_p); } /* scanner_scan_simple_arrow */ /** * Process the next argument of a might-be arrow function. */ void scanner_check_arrow_arg (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_ARROW_ARGUMENTS); const uint8_t *source_p = context_p->source_p; bool process_arrow = false; scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; if (context_p->token.type == LEXER_THREE_DOTS) { lexer_next_token (context_p); } switch (context_p->token.type) { case LEXER_RIGHT_PAREN: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; return; } case LEXER_LITERAL: { if (context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { break; } scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; if (lexer_check_arrow (context_p)) { process_arrow = true; break; } lexer_lit_location_t *argument_literal_p = scanner_append_argument (context_p, scanner_context_p); scanner_detect_eval_call (context_p, scanner_context_p); lexer_next_token (context_p); if (context_p->token.type == LEXER_COMMA || context_p->token.type == LEXER_RIGHT_PAREN) { return; } if (context_p->token.type != LEXER_ASSIGN) { break; } if (argument_literal_p->type & SCANNER_LITERAL_IS_USED) { JERRY_ASSERT (argument_literal_p->type & SCANNER_LITERAL_EARLY_CREATE); return; } scanner_binding_literal_t binding_literal; binding_literal.literal_p = argument_literal_p; parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); return; } case LEXER_LEFT_SQUARE: case LEXER_LEFT_BRACE: { scanner_append_hole (context_p, scanner_context_p); scanner_push_destructuring_pattern (context_p, scanner_context_p, SCANNER_BINDING_ARROW_ARG, false); if (context_p->token.type == LEXER_LEFT_BRACE) { parser_stack_push_uint8 (context_p, 0); parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; return; } parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); scanner_context_p->mode = SCAN_MODE_BINDING; lexer_next_token (context_p); return; } } scanner_pop_literal_pool (context_p, scanner_context_p); parser_stack_pop_uint8 (context_p); if (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC) { scanner_add_async_literal (context_p, scanner_context_p); } parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); if (process_arrow) { scanner_scan_simple_arrow (context_p, scanner_context_p, source_p); } } /* scanner_check_arrow_arg */ /** * Detect async functions. * * @return true, if async is followed by a function keyword, false otherwise */ bool scanner_check_async_function (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { JERRY_ASSERT (lexer_token_is_async (context_p)); JERRY_ASSERT (scanner_context_p->mode == SCAN_MODE_PRIMARY_EXPRESSION || scanner_context_p->mode == SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW); JERRY_ASSERT (scanner_context_p->async_source_p != NULL); lexer_lit_location_t async_literal = context_p->token.lit_location; lexer_next_token (context_p); if (!(context_p->token.flags & LEXER_WAS_NEWLINE)) { if (context_p->token.type == LEXER_KEYW_FUNCTION) { return true; } if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { if (!lexer_check_arrow (context_p)) { scanner_raise_error (context_p); } scanner_scan_simple_arrow (context_p, scanner_context_p, scanner_context_p->async_source_p); scanner_context_p->async_source_p = NULL; return false; } if (context_p->token.type == LEXER_LEFT_PAREN) { parser_stack_push (context_p, &async_literal, sizeof (lexer_lit_location_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_USE_ASYNC); return false; } } lexer_lit_location_t *lit_location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &async_literal); lit_location_p->type |= SCANNER_LITERAL_IS_USED; if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) { lit_location_p->type |= SCANNER_LITERAL_NO_REG; } scanner_context_p->async_source_p = NULL; scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return false; } /* scanner_check_async_function */ /** * Check whether the statement of an if/else construct is a function statement. */ void scanner_check_function_after_if (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { lexer_next_token (context_p); scanner_context_p->mode = SCAN_MODE_STATEMENT; if (JERRY_UNLIKELY (context_p->token.type == LEXER_KEYW_FUNCTION)) { scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); literal_pool_p->source_p = context_p->source_p; parser_stack_push_uint8 (context_p, SCAN_STACK_PRIVATE_BLOCK); } } /* scanner_check_function_after_if */ #if JERRY_MODULE_SYSTEM /** * Check whether the next token is meta. */ void scanner_check_import_meta (parser_context_t *context_p) /**< context */ { lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL || context_p->token.keyword_type != LEXER_KEYW_META || (context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { scanner_raise_error (context_p); } lexer_next_token (context_p); context_p->global_status_flags |= ECMA_PARSE_INTERNAL_HAS_IMPORT_META; } /* scanner_check_import_meta */ #endif /* JERRY_MODULE_SYSTEM */ /** * Arrow types for scanner_scan_bracket() function. */ typedef enum { SCANNER_SCAN_BRACKET_NO_ARROW, /**< not an arrow function */ SCANNER_SCAN_BRACKET_SIMPLE_ARROW, /**< simple arrow function */ SCANNER_SCAN_BRACKET_ARROW_WITH_ONE_ARG, /**< arrow function with one argument */ } scanner_scan_bracket_arrow_type_t; /** * Scan bracketed expressions. */ void scanner_scan_bracket (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { size_t depth = 0; const uint8_t *arrow_source_p; const uint8_t *async_source_p = NULL; scanner_scan_bracket_arrow_type_t arrow_type = SCANNER_SCAN_BRACKET_NO_ARROW; JERRY_ASSERT (context_p->token.type == LEXER_LEFT_PAREN); do { arrow_source_p = context_p->source_p; depth++; lexer_next_token (context_p); } while (context_p->token.type == LEXER_LEFT_PAREN); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; switch (context_p->token.type) { case LEXER_LITERAL: { if (context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { arrow_source_p = NULL; break; } const uint8_t *source_p = context_p->source_p; if (lexer_check_arrow (context_p)) { arrow_source_p = source_p; arrow_type = SCANNER_SCAN_BRACKET_SIMPLE_ARROW; break; } size_t total_depth = depth; while (depth > 0 && lexer_check_next_character (context_p, LIT_CHAR_RIGHT_PAREN)) { lexer_consume_next_character (context_p); depth--; } if (context_p->token.keyword_type == LEXER_KEYW_EVAL && lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { /* A function call cannot be an eval function. */ arrow_source_p = NULL; const uint16_t flags = (uint16_t) (SCANNER_LITERAL_POOL_CAN_EVAL | SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE); scanner_context_p->active_literal_pool_p->status_flags |= flags; break; } if (total_depth == depth) { if (lexer_check_arrow_param (context_p)) { JERRY_ASSERT (depth > 0); depth--; break; } if (JERRY_UNLIKELY (lexer_token_is_async (context_p))) { async_source_p = source_p; } } if (depth == total_depth - 1 && lexer_check_arrow (context_p)) { arrow_type = SCANNER_SCAN_BRACKET_ARROW_WITH_ONE_ARG; break; } if (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC) { scanner_add_async_literal (context_p, scanner_context_p); } arrow_source_p = NULL; break; } case LEXER_THREE_DOTS: case LEXER_LEFT_SQUARE: case LEXER_LEFT_BRACE: case LEXER_RIGHT_PAREN: { JERRY_ASSERT (depth > 0); depth--; break; } default: { arrow_source_p = NULL; break; } } if (JERRY_UNLIKELY (scanner_context_p->async_source_p != NULL) && (arrow_source_p == NULL || depth > 0)) { scanner_context_p->async_source_p = NULL; } while (depth > 0) { parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); depth--; } if (arrow_source_p != NULL) { JERRY_ASSERT (async_source_p == NULL); if (arrow_type == SCANNER_SCAN_BRACKET_SIMPLE_ARROW) { scanner_scan_simple_arrow (context_p, scanner_context_p, arrow_source_p); return; } parser_stack_push_uint8 (context_p, SCAN_STACK_ARROW_ARGUMENTS); uint16_t status_flags = 0; if (JERRY_UNLIKELY (scanner_context_p->async_source_p != NULL)) { status_flags |= SCANNER_LITERAL_POOL_MAY_ASYNC_ARROW; arrow_source_p = scanner_context_p->async_source_p; scanner_context_p->async_source_p = NULL; } scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, status_flags); literal_pool_p->source_p = arrow_source_p; if (arrow_type == SCANNER_SCAN_BRACKET_ARROW_WITH_ONE_ARG) { scanner_append_argument (context_p, scanner_context_p); scanner_detect_eval_call (context_p, scanner_context_p); context_p->token.type = LEXER_RIGHT_PAREN; scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; } else if (context_p->token.type == LEXER_RIGHT_PAREN) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; } else { scanner_check_arrow_arg (context_p, scanner_context_p); } } else if (JERRY_UNLIKELY (async_source_p != NULL)) { scanner_context_p->async_source_p = async_source_p; scanner_check_async_function (context_p, scanner_context_p); } } /* scanner_scan_bracket */ /** * Check directives before a source block. */ void scanner_check_directives (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; while (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_STRING_LITERAL) { bool is_use_strict = false; if (lexer_string_is_use_strict (context_p) && !(context_p->status_flags & PARSER_IS_STRICT)) { is_use_strict = true; context_p->status_flags |= PARSER_IS_STRICT; } lexer_next_token (context_p); if (!lexer_string_is_directive (context_p)) { if (is_use_strict) { context_p->status_flags &= (uint32_t) ~PARSER_IS_STRICT; } /* The string is part of an expression statement. */ scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; break; } if (is_use_strict) { scanner_context_p->active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_IS_STRICT; } if (context_p->token.type == LEXER_SEMICOLON) { lexer_next_token (context_p); } } } /* scanner_check_directives */ /** * @} * @} * @} */ #endif /* JERRY_PARSER */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-alloc.cpp000664 001750 001750 00000014031 15164251010 043636 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "jmem.h" #include "jrt.h" JERRY_STATIC_ASSERT ((sizeof (ecma_property_value_t) == sizeof (ecma_value_t)), size_of_ecma_property_value_t_must_be_equal_to_size_of_ecma_value_t); JERRY_STATIC_ASSERT (((sizeof (ecma_property_value_t) - 1) & sizeof (ecma_property_value_t)) == 0, size_of_ecma_property_value_t_must_be_power_of_2); JERRY_STATIC_ASSERT ((sizeof (ecma_extended_object_t) - sizeof (ecma_object_t) <= sizeof (uint64_t)), size_of_ecma_extended_object_part_must_be_less_than_or_equal_to_8_bytes); /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaalloc Routines for allocation/freeing memory for ECMA data types * @{ */ /** * Implementation of routines for allocation/freeing memory for ECMA data types. * * All allocation routines from this module have the same structure: * 1. Try to allocate memory. * 2. If allocation was successful, return pointer to the allocated block. * 3. Run garbage collection. * 4. Try to allocate memory. * 5. If allocation was successful, return pointer to the allocated block; * else - shutdown engine. */ /** * Allocate memory for ecma-number * * @return pointer to allocated memory */ ecma_number_t * ecma_alloc_number (void) { return (ecma_number_t *) jmem_pools_alloc (sizeof (ecma_number_t)); } /* ecma_alloc_number */ /** * Dealloc memory from an ecma-number * * @return void */ void ecma_dealloc_number (ecma_number_t *number_p) /**< number to be freed */ { jmem_pools_free ((uint8_t *) number_p, sizeof (ecma_number_t)); } /* ecma_dealloc_number */ /** * Allocate memory for ecma-object * * @return pointer to allocated memory */ ecma_object_t * ecma_alloc_object (void) { return (ecma_object_t *) jmem_pools_alloc (sizeof (ecma_object_t)); } /* ecma_alloc_object */ /** * Dealloc memory from an ecma-object * * @return void */ void ecma_dealloc_object (ecma_object_t *object_p) /**< object to be freed */ { jmem_pools_free (object_p, sizeof (ecma_object_t)); } /* ecma_dealloc_object */ /** * Allocate memory for extended object * * @return pointer to allocated memory */ ecma_extended_object_t * ecma_alloc_extended_object (size_t size) /**< size of object */ { return (ecma_extended_object_t *) jmem_heap_alloc_block (size); } /* ecma_alloc_extended_object */ /** * Dealloc memory of an extended object * * @return void */ void ecma_dealloc_extended_object (ecma_object_t *object_p, /**< extended object */ size_t size) /**< size of object */ { jmem_heap_free_block (object_p, size); } /* ecma_dealloc_extended_object */ /** * Allocate memory for ecma-string descriptor * * @return pointer to allocated memory */ ecma_string_t * ecma_alloc_string (void) { return (ecma_string_t *) jmem_pools_alloc (sizeof (ecma_string_t)); } /* ecma_alloc_string */ /** * Dealloc memory from ecma-string descriptor * * @return void */ void ecma_dealloc_string (ecma_string_t *string_p) /**< string to be freed */ { jmem_pools_free (string_p, sizeof (ecma_string_t)); } /* ecma_dealloc_string */ /** * Allocate memory for extended ecma-string descriptor * * @return pointer to allocated memory */ ecma_extended_string_t * ecma_alloc_extended_string (void) { return (ecma_extended_string_t *) jmem_heap_alloc_block (sizeof (ecma_extended_string_t)); } /* ecma_alloc_extended_string */ /** * Dealloc memory from extended ecma-string descriptor * * @return void */ void ecma_dealloc_extended_string (ecma_extended_string_t *ext_string_p) /**< extended string to be freed */ { jmem_heap_free_block (ext_string_p, sizeof (ecma_extended_string_t)); } /* ecma_dealloc_extended_string */ /** * Allocate memory for external ecma-string descriptor * * @return pointer to allocated memory */ ecma_external_string_t * ecma_alloc_external_string (void) { return (ecma_external_string_t *) jmem_heap_alloc_block (sizeof (ecma_external_string_t)); } /* ecma_alloc_external_string */ /** * Dealloc memory from external ecma-string descriptor * * @return void */ void ecma_dealloc_external_string (ecma_external_string_t *ext_string_p) /**< external string to be freed */ { jmem_heap_free_block (ext_string_p, sizeof (ecma_external_string_t)); } /* ecma_dealloc_external_string */ /** * Allocate memory for an string with character data * * @return pointer to allocated memory */ ecma_string_t * ecma_alloc_string_buffer (size_t size) /**< size of string */ { return (ecma_string_t *) jmem_heap_alloc_block (size); } /* ecma_alloc_string_buffer */ /** * Dealloc memory of a string with character data * * @return void */ void ecma_dealloc_string_buffer (ecma_string_t *string_p, /**< string with data */ size_t size) /**< size of string */ { jmem_heap_free_block (string_p, size); } /* ecma_dealloc_string_buffer */ /** * Allocate memory for ecma-property pair * * @return pointer to allocated memory */ ecma_property_pair_t * ecma_alloc_property_pair (void) { return (ecma_property_pair_t *) jmem_heap_alloc_block (sizeof (ecma_property_pair_t)); } /* ecma_alloc_property_pair */ /** * Dealloc memory of an ecma-property * * @return void */ void ecma_dealloc_property_pair (ecma_property_pair_t *property_pair_p) /**< property pair to be freed */ { jmem_heap_free_block (property_pair_p, sizeof (ecma_property_pair_t)); } /* ecma_dealloc_property_pair */ /** * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/meson.build000664 001750 001750 00000000470 15164251010 044250 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimatesource_file = [ 're-bytecode.h', 're-compiler-context.h', 're-compiler.h', 're-parser.h', 're-token.h', 're-bytecode.cpp', 're-compiler.cpp', 're-parser.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test11.lot000664 001750 001750 00000110140 15164251010 034044 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":30,"ip":0,"op":111,"w":1080,"h":1080,"fonts":{"list":[{"fName":"Arial","fFamily":"Arial","fStyle":"Regular","fPath":"fonts/Arial.ttf","fWeight":"normal","ascent":100,"origin":3},{"fName":"Roboto-Bold","fFamily":"Roboto","fStyle":"Bold","fPath":"fonts/Roboto-Bold.ttf","fWeight":"bold","ascent":95,"origin":1}]},"layers":[{"ty":4,"shapes":[{"ty":"rc","s":{"a":0,"k":[100,100]}},{"ty":"fl","c":{"a":0,"k":[1,0,0,1]}}],"ip":0,"op":60,"ind":1,"nm":"Shape Layer from test28"},{"ty":4,"ks":{"p":{"a":1,"k":[{"t":0,"s":[100,100],"e":[300,300],"i":{"x":[0],"y":[0]},"o":{"x":[0],"y":[0]}},{"t":30,"s":[300,300]}]},"s":{"a":1,"k":[{"t":0,"s":[100,100],"e":[200,200],"i":{"x":[1],"y":[1]},"o":{"x":[1],"y":[1]}},{"t":30,"s":[200,200]}]}},"shapes":[{"ty":"rc","s":{"a":0,"k":[50,50]}},{"ty":"fl","c":{"a":0,"k":[1,0,0,1]}}],"ip":0,"op":60,"ind":2,"nm":"Shape Layer from test30"},{"ind":3,"ty":4,"nm":"B","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":58,"s":[349.032]}],"sid":"ball_rotation"},"p":{"s":true,"x":{"a":0,"k":540},"y":{"a":1,"k":[{"i":{"x":[0.67],"y":[0.343]},"o":{"x":[0.33],"y":[0]},"t":0,"s":[152]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.33],"y":[0]},"t":15,"s":[915]},{"i":{"x":[0.67],"y":[0.343]},"o":{"x":[0.33],"y":[0]},"t":30,"s":[152]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.33],"y":[0]},"t":45,"s":[915]},{"t":60,"s":[152]}]}},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[270,270],"sid":"ball_size"},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[135]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":30,"s":[33.75]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[33.75]},{"t":60,"s":[135]}]},"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.961,0.761,0.267,1],"sid":"ball_color"},"o":{"a":0,"k":100,"sid":"ball_opacity"},"r":1,"nm":"C"}],"ip":0,"op":61,"st":0},{"ind":4,"ty":4,"nm":"B","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[540,540,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[1080,1080],"sid":"bg_size"},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"R"},{"ty":"fl","c":{"a":0,"k":[0.153,0.153,0.153,1],"sid":"bg_color"},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":61,"st":0},{"ddd":0,"ind":5,"ty":4,"nm":"pointer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.908],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":52,"s":[80]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":57,"s":[80]},{"i":{"x":[0.485],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":71,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.481],"y":[0]},"t":90,"s":[-26]},{"t":101,"s":[-1]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[235.313,572.313,0],"to":[-11.563,5.688,0],"ti":[5.813,-4.188,0]},{"i":{"x":0.36,"y":1},"o":{"x":0.902,"y":0},"t":57,"s":[203.313,588.563,0],"to":[-0.313,6.438,0],"ti":[-317.313,6.938,0]},{"i":{"x":0.215,"y":0.938},"o":{"x":0.659,"y":0},"t":71,"s":[705.813,508.063,0],"to":[14.563,-36.438,0],"ti":[0,0,0]},{"i":{"x":0.632,"y":1},"o":{"x":0.597,"y":0.342},"t":90,"s":[280.313,475.063,0],"to":[0,0,0],"ti":[0,0,0]},{"t":101,"s":[314.813,475.813,0]}],"ix":2,"l":2},"a":{"a":0,"k":[2.813,44.813,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":52,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-41.084,-15.705],[-41.145,-14.544],[-7.154,71.891],[-6.833,71.872],[45,72.716]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":57,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-3.258,72.832],[-2.938,72.813],[47.75,60]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":71,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-14.872,75.604],[-14.551,75.585],[30.964,100.71]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-40.428,-8.459],[-40.489,-7.298],[-14.677,84.044],[-14.356,84.025],[39.594,68.759]],"c":true}]},{"t":101,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-40.428,-8.459],[-40.489,-7.298],[-6.003,81.32],[-5.682,81.301],[46.375,81.38]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.505882352941,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"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":"Shape 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":52,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-41.021,-15.58],[-44.404,108.758],[-6.646,71.809]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":57,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-53.5,97],[-2.75,72.75]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":71,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-43.334,122.294],[-14.364,75.523]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":90,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-40.366,-8.334],[-47.623,112.553],[-14.169,83.962]],"c":true}]},{"t":101,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-40.366,-8.334],[-42.737,119.139],[-5.495,81.238]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.705882352941,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":52,"op":180,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"pointer","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9,"s":[0]},{"i":{"x":[0.908],"y":[1]},"o":{"x":[0.626],"y":[0]},"t":20,"s":[33]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":36,"s":[80]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":44,"s":[80]},{"i":{"x":[0.908],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":52,"s":[80]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":57,"s":[80]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":71,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[-26]},{"t":101,"s":[-1]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":2,"s":[314.813,486.813,0],"to":[2.917,7.833,0],"ti":[-2.917,-7.833,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":5,"s":[332.313,533.813,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.132,"y":1},"o":{"x":0.141,"y":0},"t":9,"s":[332.313,533.813,0],"to":[-15.917,-72.333,0],"ti":[-129.083,228.333,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[326.813,117.813,0],"to":[86.124,364.639,0],"ti":[38.917,-50.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":36,"s":[207.563,587.563,0],"to":[6.676,-4.486,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":44,"s":[235.313,572.313,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[235.313,572.313,0],"to":[0,0,0],"ti":[-78.417,10.708,0]},{"i":{"x":0.36,"y":1},"o":{"x":0.902,"y":0},"t":57,"s":[203.313,588.563,0],"to":[78.417,-10.708,0],"ti":[-317.313,6.938,0]},{"i":{"x":0.36,"y":1},"o":{"x":0.333,"y":0},"t":71,"s":[705.813,508.063,0],"to":[317.313,-6.938,0],"ti":[0,0,0]},{"i":{"x":0.36,"y":1},"o":{"x":0.333,"y":0},"t":90,"s":[280.313,475.063,0],"to":[0,0,0],"ti":[0,0,0]},{"t":101,"s":[314.813,475.813,0]}],"ix":2,"l":2},"a":{"a":0,"k":[2.813,44.813,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":2,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-41.563,-18.875],[-41.623,-17.714],[-4.258,71.332],[-3.938,71.313],[47.25,70]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-3.258,72.832],[-2.938,72.813],[47.75,60]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":9,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-3.258,72.832],[-2.938,72.813],[47.75,60]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-14.872,75.604],[-14.551,75.585],[30.964,100.71]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":31,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-3.258,72.832],[-2.938,72.813],[47.75,60]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":39,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-3.258,72.832],[-2.938,72.813],[47.75,60]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-41.084,-15.705],[-41.145,-14.544],[-7.154,71.891],[-6.833,71.872],[45,72.716]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":52,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-41.084,-15.705],[-41.145,-14.544],[-7.154,71.891],[-6.833,71.872],[45,72.716]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":57,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-3.258,72.832],[-2.938,72.813],[47.75,60]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":71,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-39.563,-11.375],[-39.623,-10.214],[-14.872,75.604],[-14.551,75.585],[30.964,100.71]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-40.428,-8.459],[-40.489,-7.298],[-14.677,84.044],[-14.356,84.025],[39.594,68.759]],"c":true}]},{"t":101,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-40.428,-8.459],[-40.489,-7.298],[-6.003,81.32],[-5.682,81.301],[46.375,81.38]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.505882352941,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"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":"Shape 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":2,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-41.5,-18.75],[-41.5,108.5],[-3.75,71.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":5,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-53.5,97],[-2.75,72.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":9,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-53.5,97],[-2.75,72.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-43.334,122.294],[-14.364,75.523]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":31,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-53.5,97],[-2.75,72.75]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":39,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-53.5,97],[-2.75,72.75]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-41.021,-15.58],[-44.404,108.758],[-6.646,71.809]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":52,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-41.021,-15.58],[-44.404,108.758],[-6.646,71.809]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":57,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-53.5,97],[-2.75,72.75]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":71,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.5,-11.25],[-43.334,122.294],[-14.364,75.523]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":90,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-40.366,-8.334],[-47.623,112.553],[-14.169,83.962]],"c":true}]},{"t":101,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-40.366,-8.334],[-42.737,119.139],[-5.495,81.238]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.705882352941,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":52,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"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":[87.813,251.5,0],"ix":2,"l":2},"a":{"a":0,"k":[-223.625,-190.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[33.25,33.25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.705882352941,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-222.625,248.125],"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":"Rectangle 4","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[33.25,33.25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.705882352941,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[214.375,248.125],"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":"Rectangle 3","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[33.25,33.25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.705882352941,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[214.375,-190.125],"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":"Rectangle 2","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[33.25,33.25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.705882352941,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-223.625,-190.125],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"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":[312,442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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.333,"y":0},"t":12,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[45.805,56.777],[0.912,-0.274],[12.788,-7.87],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-6.695,-8.223],[-1.003,0.046],[-31.447,18.046],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[-43.555,-248.027],[-63.941,-271.266],[-95.303,-218.046],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[0,0],[41.134,72.459],[1.636,-0.491],[19.603,-15.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-5.995,-10.401],[-1.8,0.082],[-44.397,35.285],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[-29.816,-287.451],[-47.975,-319.894],[-84.603,-254.785],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[0,0],[39.515,100.049],[2.339,-0.702],[20.389,-18.025],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-5.315,-12.516],[-2.573,0.117],[-57.735,55.653],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[-14.515,-311.549],[-30.512,-352.932],[-68.023,-278.267],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[33.997,120.659],[3.015,-0.904],[22.564,-22.25],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-4.663,-14.547],[-3.316,0.151],[-70.546,75.216],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[-2.497,-326.159],[-14.917,-378.628],[-58.064,-295.75],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":17,"s":[{"i":[[0,0],[29.323,128.568],[3.652,-1.096],[22.82,-24.306],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-4.047,-16.464],[-4.017,0.183],[-82.631,93.67],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[8.282,-332.401],[-2.66,-392.167],[-54.283,-307.231],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,0],[19.435,145.298],[5,-1.5],[23.363,-28.656],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-2.744,-20.518],[-5.5,0.25],[-108.195,132.709],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[17.065,-323.798],[9.25,-399],[-60.305,-309.709],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":22,"s":[{"i":[[0,0],[22.637,132.221],[5.131,-1.369],[22.269,-26.077],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-3.322,-18.66],[-5.587,0.228],[-103.249,120.751],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[22.067,-305.304],[18.106,-381.799],[-48.769,-294.008],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23,"s":[{"i":[[0,0],[27.165,113.731],[5.316,-1.184],[22.586,-21.344],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-4.139,-16.034],[-5.71,0.197],[-103.664,93.156],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[27.419,-283.075],[21,-350.258],[-44.336,-275.156],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":24,"s":[{"i":[[0,0],[22.786,71.935],[5.497,-1.003],[20.023,-17.655],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-2.214,-12.065],[-5.831,0.167],[-95.656,78.179],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[38.214,-256.935],[30.494,-320.295],[-28.273,-255.595],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[24.013,47.801],[5.675,-0.825],[24.585,-14.669],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-6.737,-12.449],[-5.95,0.138],[-77.915,45.331],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[48.487,-228.801],[37.646,-278.627],[-17.835,-238.331],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[13.128,21.942],[3.635,1.236],[27.63,-9.863],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-7.372,-8.308],[-6.065,0.109],[-73.62,24.637],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[53.622,-212.942],[42.865,-238.736],[-12.88,-216.637],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[10.197,0.906],[2.982,0.207],[22.176,-3.647],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-8.553,-1.594],[-6.175,0.081],[-70.824,12.103],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[45.053,-193.406],[23.018,-199.207],[-24.176,-192.353],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[0,0],[23.708,-3.089],[4.111,0.141],[17.789,0.812],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-8.292,1.411],[-6.28,0.055],[-63.211,1.312],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[38.042,-179.286],[12.463,-177.538],[-32.039,-178.562],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[{"i":[[0,0],[56.177,-4.75],[6.5,0],[10.811,0.946],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-9.373,0.793],[-6.5,0],[-51.443,-4.5],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[22.823,-151.75],[-6.75,-150],[-45.557,-152.5],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":33,"s":[{"i":[[0,0],[56.177,-4.75],[6.5,0],[10.811,0.946],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-9.373,0.793],[-6.5,0],[-51.443,-4.5],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[22.823,-151.75],[-6.75,-150],[-45.557,-152.5],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":35,"s":[{"i":[[0,0],[56.24,2.691],[6.5,0],[8.807,-0.5],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-10.448,-0.5],[-6.5,0],[-51.363,2.916],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[24.698,-197.25],[-5,-198.25],[-46.057,-196.75],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":37,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":65,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":66,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-18.621,28.961],[1.142,14.464],[9.193,19.161]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[13.005,-20.226],[-1.5,-19],[-16.747,-34.907]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[274.121,82.539],[299,37],[274.247,-8.593]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":67,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-116.621,105.961],[-0.427,14.502],[20.753,15.593]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[17.797,-16.17],[0.5,-17],[-136.72,-102.725]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[332.621,79.039],[386.5,31],[346.247,-12.593]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":68,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-156.121,90.961],[-0.427,14.502],[27.253,11.093]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[20.777,-12.105],[0.5,-17],[-186.985,-76.108]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[363.121,73.039],[431,31],[385.247,-0.093]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":69,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-156.121,90.961],[2.5,7],[27.253,11.093]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[20.777,-12.105],[-3.2,-8.959],[-186.985,-76.108]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[378.621,73.039],[447,34],[397.747,6.907]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":70,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-156.121,90.961],[2.5,7],[27.253,11.093]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[20.777,-12.105],[-3.2,-8.959],[-186.985,-76.108]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[388.621,72.039],[458,33.5],[407.747,10.407]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":71,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-156.121,90.961],[1,6.25],[27.253,11.093]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[20.777,-12.105],[-1.131,-7.069],[-186.985,-76.108]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[391.621,68.039],[462,33.5],[410.247,13.907]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":72,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-156.121,90.961],[1,6.25],[27.253,11.093]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[20.777,-12.105],[-1.131,-7.069],[-186.985,-76.108]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[392.121,66.539],[460.5,29.5],[404.247,11.157]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":73,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-156.121,90.961],[1.132,6.227],[27.253,11.093]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[20.777,-12.105],[-1,-5.5],[-186.985,-76.108]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[388.871,67.039],[455,19],[397.247,3.407]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":75,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-109.871,123.961],[1.132,6.227],[22.253,6.093]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[15.094,-17.029],[-1,-5.5],[-194.715,-53.311]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[384.871,59.039],[423.75,-9],[375.747,-18.593]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":76,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-72.871,103.961],[1.132,6.227],[22.128,6.53]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[13.061,-18.634],[-1,-5.5],[-115.747,-34.157]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[361.871,52.539],[395.5,-17.75],[348.747,-21.843]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-27.371,70.461],[1.132,6.227],[8.74,1.426]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[8.24,-21.212],[-1,-5.5],[-62.247,-10.157]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[349.371,55.539],[358.25,-25.25],[323.247,-25.343]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":78,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-27.871,64.961],[3.559,5.234],[6.968,5.465]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[8.972,-20.912],[-4.25,-6.25],[-53.747,-42.157]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[315.871,61.039],[330.25,-17.25],[291.247,-47.343]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":79,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-23.871,66.961],[3.559,5.234],[5.572,6.883]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[7.641,-21.435],[-4.25,-6.25],[-27.247,-33.657]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[262.871,67.039],[274.25,-5.25],[249.747,-40.843]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":80,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[27.301,65.637],[2.153,5.952],[-1.638,8.703]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-11.871,-28.539],[-4.25,-11.75],[9.253,-49.157]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[180.871,89.539],[157.75,25.75],[155.247,-37.343]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":81,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[27.301,65.637],[2.153,5.952],[-1.638,8.703]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-11.871,-28.539],[-4.25,-11.75],[9.253,-49.157]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[180.871,89.539],[157.75,25.75],[155.247,-37.343]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":82,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[27.301,65.637],[0.699,6.291],[-2.6,8.465]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-11.871,-28.539],[-1.75,-15.75],[15.253,-49.657]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[146.371,111.039],[127.25,40.75],[134.747,-26.343]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":83,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[27.301,65.637],[0.699,6.291],[-2.6,8.465]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-11.871,-28.539],[-1.75,-15.75],[15.253,-49.657]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[146.371,111.039],[127.25,40.75],[134.747,-26.343]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":84,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[30.629,64.461],[0.699,6.291],[-2.6,8.465]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-13.266,-27.918],[-1.75,-15.75],[15.253,-49.657]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[137.871,111.039],[121.75,41.25],[130.747,-25.343]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":86,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[30.629,64.461],[0.699,6.291],[-2.6,8.465]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-13.266,-27.918],[-1.75,-15.75],[15.253,-49.657]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[137.871,111.039],[121.75,41.25],[130.747,-25.343]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":87,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[30.629,64.461],[0.699,6.291],[-2.6,8.465]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-13.266,-27.918],[-1.75,-15.75],[15.253,-49.657]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[141.371,111.539],[125.25,41.75],[134.247,-24.843]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":88,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[30.629,64.461],[0.699,6.291],[-2.6,8.465]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-13.266,-27.918],[-1.75,-15.75],[15.253,-49.657]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[141.371,111.539],[125.25,41.75],[134.247,-24.843]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":89,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[6.364,73.251],[0.25,14.5],[-0.502,8.841]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-2.871,-33.039],[-0.341,-19.749],[3.003,-52.907]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[200.871,116.039],[194.75,44.5],[197.497,-21.593]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":91,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[6.364,73.251],[0.25,14.5],[-0.502,8.841]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-2.871,-33.039],[-0.341,-19.749],[3.003,-52.907]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[200.871,116.039],[194.75,44.5],[197.497,-21.593]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":93,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[221,34.834],[221.5,8.25],[220.5,-23.928]],"c":true}]},{"t":95,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[214,-190],[4.323,-190],[-9.5,-190],[-44.057,-190],[-225,-190],[-223,247.5],[214,247.5],[214,34.834],[214,9],[214,-23.428]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.870588235294,0.699884691425,0.017070357005,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0},{"ty":4,"nm":"Round Join Test","ip":0,"op":60,"shapes":[{"ty":"rc","nm":"Rectangle","p":{"a":0,"k":[200,200]},"s":{"a":0,"k":[150,150]},"r":{"a":0,"k":0}},{"ty":"st","nm":"Stroke with Round","c":{"a":0,"k":[0,1,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":20},"lc":2,"lj":2,"ml":4}],"ind":9}],"nm":"Basic Shapes Set 2 Tests","slots":{"ball_color":{"p":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0,0.176,0.867]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0.867,0,0.533]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0.867,0,0.533]},{"t":51,"s":[0,0.867,0.255]}]}},"ball_opacity":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[0],"t":0},{"s":[100],"t":100}]}},"bg_color":{"p":{"a":0,"k":[1,1,1,1]}}},"meta":{"g":"LottieFiles AE ","a":"","k":"","d":"","tc":""},"ddd":0,"markers":[],"assets":[]}thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-objects-general.h000664 001750 001750 00000003757 15164251010 046543 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_OBJECTS_GENERAL_H #define ECMA_OBJECTS_GENERAL_H #include "ecma-conversion.h" #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaobjectsinternalops ECMA objects' operations * @{ */ ecma_object_t *ecma_op_create_object_object_noarg (void); ecma_object_t *ecma_op_create_object_object_noarg_and_set_prototype (ecma_object_t *object_prototype_p); ecma_value_t ecma_op_general_object_delete (ecma_object_t *obj_p, ecma_string_t *property_name_p, bool is_throw); ecma_value_t ecma_op_general_object_default_value (ecma_object_t *obj_p, ecma_preferred_type_hint_t hint); ecma_value_t ecma_op_general_object_ordinary_value (ecma_object_t *obj_p, ecma_preferred_type_hint_t hint); ecma_value_t ecma_op_general_object_define_own_property (ecma_object_t *object_p, ecma_string_t *property_name_p, const ecma_property_descriptor_t *property_desc_p); void ecma_op_to_complete_property_descriptor (ecma_property_descriptor_t *desc_p); bool ecma_op_is_compatible_property_descriptor (const ecma_property_descriptor_t *desc_p, const ecma_property_descriptor_t *current_p, bool is_extensible); /** * @} * @} */ #endif /* !ECMA_OBJECTS_GENERAL_H */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-map.cpp000664 001750 001750 00000004712 15164251010 047155 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-container-object.h" #include "ecma-exceptions.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-map.inc.h" #define BUILTIN_UNDERSCORED_ID map #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup map ECMA Map object built-in * @{ */ /** * Handle calling [[Call]] of built-in Map object * * @return ecma value */ ecma_value_t ecma_builtin_map_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_MAP_REQUIRES_NEW); } /* ecma_builtin_map_dispatch_call */ /** * Handle calling [[Construct]] of built-in Map object * * @return ecma value */ ecma_value_t ecma_builtin_map_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_op_container_create (arguments_list_p, arguments_list_len, LIT_MAGIC_STRING_MAP_UL, ECMA_BUILTIN_ID_MAP_PROTOTYPE); } /* ecma_builtin_map_dispatch_construct */ /** * 23.1.2.2 get Map [ @@species ] accessor * * @return ecma_value * returned value must be freed with ecma_free_value */ ecma_value_t ecma_builtin_map_species_get (ecma_value_t this_value) /**< This Value */ { return ecma_copy_value (this_value); } /* ecma_builtin_map_species_get */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/000775 001750 001750 00000000000 15164251010 031435 5ustar00ddennedyddennedy000000 000000 external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-statm.cpp000664 001750 001750 00000320277 15164251010 045002 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "js-parser-internal.h" #if JERRY_PARSER #include "ecma-helpers.h" #include "jcontext.h" #include "lit-char-helpers.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_stmt Statement parser * @{ */ /** * Parser statement types. * * When a new statement is added, the following * arrays must be updated as well: * - statement_lengths[] * - parser_statement_flags[] */ typedef enum { PARSER_STATEMENT_START, PARSER_STATEMENT_BLOCK, PARSER_STATEMENT_BLOCK_SCOPE, PARSER_STATEMENT_PRIVATE_SCOPE, PARSER_STATEMENT_BLOCK_CONTEXT, PARSER_STATEMENT_PRIVATE_CONTEXT, PARSER_STATEMENT_LABEL, PARSER_STATEMENT_IF, PARSER_STATEMENT_ELSE, PARSER_STATEMENT_SWITCH, PARSER_STATEMENT_SWITCH_NO_DEFAULT, PARSER_STATEMENT_DO_WHILE, PARSER_STATEMENT_WHILE, PARSER_STATEMENT_FOR, PARSER_STATEMENT_FOR_IN, PARSER_STATEMENT_FOR_OF, PARSER_STATEMENT_FOR_AWAIT_OF, PARSER_STATEMENT_WITH, PARSER_STATEMENT_TRY, } parser_statement_type_t; /** * Parser statement type flags. */ typedef enum { PARSER_STATM_NO_OPTS = 0, /**< no options */ PARSER_STATM_SINGLE_STATM = (1 << 0), /**< statement can form single statement context */ PARSER_STATM_HAS_BLOCK = (1 << 1), /**< statement always has a code block */ PARSER_STATM_BREAK_TARGET = (1 << 2), /**< break target statement */ PARSER_STATM_CONTINUE_TARGET = (1 << 3), /**< continue target statement */ PARSER_STATM_CONTEXT_BREAK = (1 << 4), /**< uses another instruction form when crosses their borders */ } parser_statement_flags_t; /** * Parser statement attributes. * Note: the order of the attributes must be keep in sync with parser_statement_type_t */ static const uint8_t parser_statement_flags[] = { /* PARSER_STATEMENT_START */ PARSER_STATM_HAS_BLOCK, /* PARSER_STATEMENT_BLOCK, */ PARSER_STATM_HAS_BLOCK, /* PARSER_STATEMENT_BLOCK_SCOPE, */ PARSER_STATM_HAS_BLOCK, /* PARSER_STATEMENT_PRIVATE_SCOPE, */ PARSER_STATM_NO_OPTS, /* PARSER_STATEMENT_BLOCK_CONTEXT, */ PARSER_STATM_HAS_BLOCK | PARSER_STATM_CONTEXT_BREAK, /* PARSER_STATEMENT_PRIVATE_CONTEXT, */ PARSER_STATM_CONTEXT_BREAK, /* PARSER_STATEMENT_LABEL */ PARSER_STATM_SINGLE_STATM, /* PARSER_STATEMENT_IF */ PARSER_STATM_SINGLE_STATM, /* PARSER_STATEMENT_ELSE */ PARSER_STATM_SINGLE_STATM, /* PARSER_STATEMENT_SWITCH */ PARSER_STATM_HAS_BLOCK | PARSER_STATM_BREAK_TARGET, /* PARSER_STATEMENT_SWITCH_NO_DEFAULT */ PARSER_STATM_HAS_BLOCK | PARSER_STATM_BREAK_TARGET, /* PARSER_STATEMENT_DO_WHILE */ PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM, /* PARSER_STATEMENT_WHILE */ PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM, /* PARSER_STATEMENT_FOR */ PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM, /* PARSER_STATEMENT_FOR_IN */ PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM | PARSER_STATM_CONTEXT_BREAK, /* PARSER_STATEMENT_FOR_OF */ PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM | PARSER_STATM_CONTEXT_BREAK, /* PARSER_STATEMENT_FOR_AWAIT_OF */ PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM | PARSER_STATM_CONTEXT_BREAK, /* PARSER_STATEMENT_WITH */ PARSER_STATM_CONTEXT_BREAK | PARSER_STATM_SINGLE_STATM, /* PARSER_STATEMENT_TRY */ PARSER_STATM_HAS_BLOCK | PARSER_STATM_CONTEXT_BREAK }; /** * Block statement. */ typedef struct { uint16_t scope_stack_top; /**< preserved top of scope stack */ uint16_t scope_stack_reg_top; /**< preserved top register of scope stack */ } parser_block_statement_t; /** * Context of block statement. */ typedef struct { parser_branch_t branch; /**< branch to the end */ } parser_block_context_t; /** * Loop statement. */ typedef struct { parser_branch_node_t *branch_list_p; /**< list of breaks and continues targeting this statement */ } parser_loop_statement_t; /** * Label statement. */ typedef struct { lexer_lit_location_t label_ident; /**< name of the label */ parser_branch_node_t *break_list_p; /**< list of breaks targeting this label */ } parser_label_statement_t; /** * If/else statement. */ typedef struct { parser_branch_t branch; /**< branch to the end */ } parser_if_else_statement_t; /** * Switch statement. */ typedef struct { parser_branch_t default_branch; /**< branch to the default case */ parser_branch_node_t *branch_list_p; /**< branches of case statements */ } parser_switch_statement_t; /** * Do-while statement. */ typedef struct { uint32_t start_offset; /**< start byte code offset */ } parser_do_while_statement_t; /** * While statement. */ typedef struct { parser_branch_t branch; /**< branch to the end */ scanner_location_t condition_location; /**< condition part */ uint32_t start_offset; /**< start byte code offset */ } parser_while_statement_t; /** * For statement. */ typedef struct { parser_branch_t branch; /**< branch to the end */ scanner_location_t condition_location; /**< condition part */ scanner_location_t expression_location; /**< expression part */ uint32_t start_offset; /**< start byte code offset */ } parser_for_statement_t; /** * For-in statement. */ typedef struct { parser_branch_t branch; /**< branch to the end */ uint32_t start_offset; /**< start byte code offset */ } parser_for_in_of_statement_t; /** * With statement. */ typedef struct { parser_branch_t branch; /**< branch to the end */ } parser_with_statement_t; /** * Lexer token types. */ typedef enum { parser_try_block, /**< try block */ parser_catch_block, /**< catch block */ parser_finally_block, /**< finally block */ } parser_try_block_type_t; /** * Try statement. */ typedef struct { parser_try_block_type_t type; /**< current block type */ uint16_t scope_stack_top; /**< current top of scope stack */ uint16_t scope_stack_reg_top; /**< current top register of scope stack */ parser_branch_t branch; /**< branch to the end of the current block */ } parser_try_statement_t; /** * Returns the data consumed by a statement. It can be used * to skip undesired frames on the stack during frame search. * * @return size consumed by a statement. */ static inline size_t parser_statement_length (uint8_t type) /**< type of statement */ { static const uint8_t statement_lengths[] = { /* PARSER_STATEMENT_BLOCK */ 1, /* PARSER_STATEMENT_BLOCK_SCOPE */ (uint8_t) (sizeof (parser_block_statement_t) + 1), /* PARSER_STATEMENT_PRIVATE_SCOPE */ (uint8_t) (sizeof (parser_block_statement_t) + 1), /* PARSER_STATEMENT_BLOCK_CONTEXT */ (uint8_t) (sizeof (parser_block_statement_t) + sizeof (parser_block_context_t) + 1), /* PARSER_STATEMENT_PRIVATE_CONTEXT */ (uint8_t) (sizeof (parser_block_statement_t) + sizeof (parser_block_context_t) + 1), /* PARSER_STATEMENT_LABEL */ (uint8_t) (sizeof (parser_label_statement_t) + 1), /* PARSER_STATEMENT_IF */ (uint8_t) (sizeof (parser_if_else_statement_t) + 1), /* PARSER_STATEMENT_ELSE */ (uint8_t) (sizeof (parser_if_else_statement_t) + 1), /* PARSER_STATEMENT_SWITCH */ (uint8_t) (sizeof (parser_switch_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_SWITCH_NO_DEFAULT */ (uint8_t) (sizeof (parser_switch_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_DO_WHILE */ (uint8_t) (sizeof (parser_do_while_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_WHILE */ (uint8_t) (sizeof (parser_while_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_FOR */ (uint8_t) (sizeof (parser_for_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_FOR_IN */ (uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_FOR_OF */ (uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_FOR_AWAIT_OF */ (uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_WITH */ (uint8_t) (sizeof (parser_with_statement_t) + 1 + 1), /* PARSER_STATEMENT_TRY */ (uint8_t) (sizeof (parser_try_statement_t) + 1), }; JERRY_ASSERT (type >= PARSER_STATEMENT_BLOCK && type <= PARSER_STATEMENT_TRY); return statement_lengths[type - PARSER_STATEMENT_BLOCK]; } /* parser_statement_length */ /** * Parse expression enclosed in parens. */ static inline void parser_parse_enclosed_expr (parser_context_t *context_p) /**< context */ { lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_PAREN) { parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } lexer_next_token (context_p); } /* parser_parse_enclosed_expr */ /** * Create a block context. * * @return true - when a context is created, false - otherwise */ static bool parser_push_block_context (parser_context_t *context_p, /**< context */ bool is_private) /**< is private (bound to a statement) context */ { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); parser_block_statement_t block_statement; block_statement.scope_stack_top = context_p->scope_stack_top; block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; bool is_context_needed = false; if (scanner_is_context_needed (context_p, PARSER_CHECK_BLOCK_CONTEXT)) { parser_block_context_t block_context; #ifndef JERRY_NDEBUG PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ parser_emit_cbc_forward_branch (context_p, CBC_BLOCK_CREATE_CONTEXT, &block_context.branch); parser_stack_push (context_p, &block_context, sizeof (parser_block_context_t)); is_context_needed = true; } scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t)); uint8_t statement_type; if (is_private) { statement_type = (is_context_needed ? PARSER_STATEMENT_PRIVATE_CONTEXT : PARSER_STATEMENT_PRIVATE_SCOPE); } else { statement_type = (is_context_needed ? PARSER_STATEMENT_BLOCK_CONTEXT : PARSER_STATEMENT_BLOCK_SCOPE); } parser_stack_push_uint8 (context_p, statement_type); return is_context_needed; } /* parser_push_block_context */ /** * Pop block context. */ static void parser_pop_block_context (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_SCOPE || context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE || context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT || context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT); uint8_t type = context_p->stack_top_uint8; parser_block_statement_t block_statement; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t)); context_p->scope_stack_top = block_statement.scope_stack_top; context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top; if (type == PARSER_STATEMENT_BLOCK_CONTEXT || type == PARSER_STATEMENT_PRIVATE_CONTEXT) { PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); #ifndef JERRY_NDEBUG PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ parser_block_context_t block_context; parser_stack_pop (context_p, &block_context, sizeof (parser_block_context_t)); parser_emit_cbc (context_p, CBC_CONTEXT_END); parser_set_branch_to_current_position (context_p, &block_context.branch); } parser_stack_iterator_init (context_p, &context_p->last_statement); } /* parser_pop_block_context */ /** * Validate lexical context for a declaration. */ static void parser_validate_lexical_context (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_LET || context_p->token.type == LEXER_KEYW_CONST || context_p->token.type == LEXER_KEYW_CLASS); if (parser_statement_flags[context_p->stack_top_uint8] & PARSER_STATM_SINGLE_STATM) { parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT); } } /* parser_validate_lexical_context */ /** * Parse var statement. */ static void parser_parse_var_statement (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_VAR || context_p->token.type == LEXER_KEYW_LET || context_p->token.type == LEXER_KEYW_CONST); uint8_t declaration_type = context_p->token.type; if (declaration_type != LEXER_KEYW_VAR) { parser_validate_lexical_context (context_p); } while (true) { if (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE)) { parser_pattern_flags_t flags = PARSER_PATTERN_BINDING; if (declaration_type == LEXER_KEYW_LET) { flags = (parser_pattern_flags_t) ((int) flags | (int) PARSER_PATTERN_LET); } else if (declaration_type == LEXER_KEYW_CONST) { flags = (parser_pattern_flags_t) ((int) flags | (int) PARSER_PATTERN_CONST); } parser_parse_initializer_by_next_char (context_p, flags); } else { lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); #if JERRY_MODULE_SYSTEM parser_module_append_export_name (context_p); #endif /* JERRY_MODULE_SYSTEM */ if (declaration_type != LEXER_KEYW_VAR && context_p->token.keyword_type == LEXER_KEYW_LET) { parser_raise_error (context_p, PARSER_ERR_LEXICAL_LET_BINDING); } if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } lexer_next_token (context_p); if (context_p->token.type == LEXER_ASSIGN) { uint16_t index = context_p->lit_object.index; lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); cbc_opcode_t opcode = CBC_ASSIGN_SET_IDENT; uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); if (function_literal_index == PARSER_ANONYMOUS_CLASS) { uint16_t name_index = scanner_save_literal (context_p, index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index); } else if (function_literal_index < PARSER_NAMED_FUNCTION) { parser_set_function_name (context_p, function_literal_index, index, 0); } if (declaration_type != LEXER_KEYW_VAR && (index < PARSER_REGISTER_START)) { opcode = CBC_INIT_LET; if (scanner_literal_is_created (context_p, index)) { opcode = CBC_ASSIGN_LET_CONST; } else if (declaration_type == LEXER_KEYW_CONST) { opcode = CBC_INIT_CONST; } } parser_emit_cbc_literal (context_p, (uint16_t) opcode, index); } else if (declaration_type == LEXER_KEYW_LET) { parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); uint16_t index = context_p->lit_object.index; cbc_opcode_t opcode = CBC_MOV_IDENT; if (index < PARSER_REGISTER_START) { opcode = (scanner_literal_is_created (context_p, index) ? CBC_ASSIGN_LET_CONST : CBC_INIT_LET); } parser_emit_cbc_literal (context_p, (uint16_t) opcode, index); } else if (declaration_type == LEXER_KEYW_CONST) { parser_raise_error (context_p, PARSER_ERR_MISSING_ASSIGN_AFTER_CONST); } } if (context_p->token.type != LEXER_COMMA) { break; } } #if JERRY_MODULE_SYSTEM context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); #endif /* JERRY_MODULE_SYSTEM */ } /* parser_parse_var_statement */ /** * Parse function statement. */ static void parser_parse_function_statement (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FUNCTION); bool is_single_statement = (parser_statement_flags[context_p->stack_top_uint8] & PARSER_STATM_SINGLE_STATM) != 0; if (JERRY_UNLIKELY (is_single_statement)) { if (context_p->status_flags & PARSER_IS_STRICT) { parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT); } if (context_p->stack_top_uint8 == PARSER_STATEMENT_IF || context_p->stack_top_uint8 == PARSER_STATEMENT_ELSE) { /* There must be a parser error later if this check fails. */ if (context_p->next_scanner_info_p->source_p == context_p->source_p) { parser_push_block_context (context_p, true); } } else if (context_p->stack_top_uint8 == PARSER_STATEMENT_LABEL) { parser_stack_iterator_t iterator; parser_stack_iterator_init (context_p, &iterator); parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t) + 1); while (true) { uint8_t type = parser_stack_iterator_read_uint8 (&iterator); if (type == PARSER_STATEMENT_LABEL) { parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t) + 1); continue; } if (parser_statement_flags[type] & PARSER_STATM_HAS_BLOCK) { break; } parser_raise_error (context_p, PARSER_ERR_LABELLED_FUNC_NOT_IN_BLOCK); } } else { parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT); } } bool is_generator_function = false; if (lexer_consume_generator (context_p)) { if (is_single_statement) { parser_raise_error (context_p, PARSER_ERR_GENERATOR_IN_SINGLE_STATEMENT_POS); } is_generator_function = true; } lexer_expect_identifier (context_p, LEXER_NEW_IDENT_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); if (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED) { parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } uint16_t function_name_index = context_p->lit_object.index; #if JERRY_MODULE_SYSTEM parser_module_append_export_name (context_p); context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); #endif /* JERRY_MODULE_SYSTEM */ uint32_t status_flags = PARSER_FUNCTION_CLOSURE; if (context_p->token.keyword_type >= LEXER_FIRST_NON_STRICT_ARGUMENTS) { status_flags |= PARSER_HAS_NON_STRICT_ARG; } if (is_generator_function) { status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } if (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC) { status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } JERRY_ASSERT (context_p->scope_stack_top >= 2); parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top - 2; uint16_t literal_index = context_p->lit_object.index; while (literal_index != scope_stack_p->map_from) { scope_stack_p--; JERRY_ASSERT (scope_stack_p >= context_p->scope_stack_p); } JERRY_ASSERT (scope_stack_p[1].map_from == PARSER_SCOPE_STACK_FUNC); if (!(context_p->status_flags & PARSER_IS_STRICT) && (scope_stack_p >= context_p->scope_stack_p + context_p->scope_stack_global_end)) { bool copy_value = true; parser_scope_stack_t *stack_p = context_p->scope_stack_p; while (stack_p < scope_stack_p) { if (literal_index == stack_p->map_from && (stack_p->map_to & PARSER_SCOPE_STACK_NO_FUNCTION_COPY)) { copy_value = false; break; } stack_p++; } if (copy_value) { stack_p = context_p->scope_stack_p; while (stack_p < scope_stack_p) { if (literal_index == stack_p->map_from) { JERRY_ASSERT (!(stack_p->map_to & PARSER_SCOPE_STACK_NO_FUNCTION_COPY)); uint16_t map_to = scanner_decode_map_to (stack_p); uint16_t opcode = ((map_to >= PARSER_REGISTER_START) ? CBC_ASSIGN_LITERAL_SET_IDENT : CBC_COPY_TO_GLOBAL); parser_emit_cbc_literal_value (context_p, opcode, scanner_decode_map_to (scope_stack_p), map_to); break; } stack_p++; } parser_flush_cbc (context_p); } if (JERRY_UNLIKELY (context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE || context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT)) { parser_pop_block_context (context_p); } } lexer_literal_t *literal_p = PARSER_GET_LITERAL ((size_t) scope_stack_p[1].map_to); JERRY_ASSERT ((literal_p->type == LEXER_UNUSED_LITERAL || literal_p->type == LEXER_FUNCTION_LITERAL) && literal_p->status_flags == 0); ecma_compiled_code_t *compiled_code_p = parser_parse_function (context_p, status_flags); if (literal_p->type == LEXER_FUNCTION_LITERAL) { ecma_bytecode_deref (literal_p->u.bytecode_p); } literal_p->u.bytecode_p = compiled_code_p; literal_p->type = LEXER_FUNCTION_LITERAL; parser_compiled_code_set_function_name (context_p, compiled_code_p, function_name_index, 0); lexer_next_token (context_p); } /* parser_parse_function_statement */ /** * Parse if statement (starting part). */ static void parser_parse_if_statement_start (parser_context_t *context_p) /**< context */ { parser_if_else_statement_t if_statement; parser_parse_enclosed_expr (context_p); parser_emit_cbc_forward_branch (context_p, CBC_BRANCH_IF_FALSE_FORWARD, &if_statement.branch); parser_stack_push (context_p, &if_statement, sizeof (parser_if_else_statement_t)); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_IF); parser_stack_iterator_init (context_p, &context_p->last_statement); } /* parser_parse_if_statement_start */ /** * Parse if statement (ending part). * * @return true - if parsing an 'else' statement * false - otherwise */ static bool parser_parse_if_statement_end (parser_context_t *context_p) /**< context */ { parser_if_else_statement_t if_statement; parser_if_else_statement_t else_statement; parser_stack_iterator_t iterator; JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_IF); if (context_p->token.type != LEXER_KEYW_ELSE) { parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &if_statement, sizeof (parser_if_else_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); parser_set_branch_to_current_position (context_p, &if_statement.branch); return false; } parser_stack_change_last_uint8 (context_p, PARSER_STATEMENT_ELSE); parser_stack_iterator_init (context_p, &iterator); parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &if_statement, sizeof (parser_if_else_statement_t)); parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &else_statement.branch); parser_set_branch_to_current_position (context_p, &if_statement.branch); parser_stack_iterator_write (&iterator, &else_statement, sizeof (parser_if_else_statement_t)); lexer_next_token (context_p); return true; } /* parser_parse_if_statement_end */ /** * Parse with statement (starting part). */ static void parser_parse_with_statement_start (parser_context_t *context_p) /**< context */ { parser_with_statement_t with_statement; if (context_p->status_flags & PARSER_IS_STRICT) { parser_raise_error (context_p, PARSER_ERR_WITH_NOT_ALLOWED); } parser_parse_enclosed_expr (context_p); #ifndef JERRY_NDEBUG PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ uint8_t inside_with = (context_p->status_flags & PARSER_INSIDE_WITH) != 0; context_p->status_flags |= PARSER_INSIDE_WITH; parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_WITH_CREATE_CONTEXT, &with_statement.branch); parser_stack_push (context_p, &with_statement, sizeof (parser_with_statement_t)); parser_stack_push_uint8 (context_p, inside_with); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_WITH); parser_stack_iterator_init (context_p, &context_p->last_statement); } /* parser_parse_with_statement_start */ /** * Parse with statement (ending part). */ static void parser_parse_with_statement_end (parser_context_t *context_p) /**< context */ { parser_with_statement_t with_statement; JERRY_ASSERT (context_p->status_flags & PARSER_INSIDE_WITH); parser_stack_pop_uint8 (context_p); if (!context_p->stack_top_uint8) { context_p->status_flags &= (uint32_t) ~PARSER_INSIDE_WITH; } parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &with_statement, sizeof (parser_with_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); parser_flush_cbc (context_p); PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); #ifndef JERRY_NDEBUG PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ parser_emit_cbc (context_p, CBC_CONTEXT_END); parser_set_branch_to_current_position (context_p, &with_statement.branch); } /* parser_parse_with_statement_end */ /** * Parse do-while statement (ending part). */ static void parser_parse_do_while_statement_end (parser_context_t *context_p) /**< context */ { parser_loop_statement_t loop; JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_DO_WHILE); if (context_p->token.type != LEXER_KEYW_WHILE) { parser_raise_error (context_p, PARSER_ERR_WHILE_EXPECTED); } parser_stack_iterator_t iterator; parser_stack_iterator_init (context_p, &iterator); parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); parser_set_continues_to_current_position (context_p, loop.branch_list_p); JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p); parser_parse_enclosed_expr (context_p); if (context_p->last_cbc_opcode != CBC_PUSH_FALSE) { cbc_opcode_t opcode = CBC_BRANCH_IF_TRUE_BACKWARD; if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_BRANCH_IF_FALSE_BACKWARD; } else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_JUMP_BACKWARD; } parser_do_while_statement_t do_while_statement; parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t)); parser_stack_iterator_read (&iterator, &do_while_statement, sizeof (parser_do_while_statement_t)); parser_emit_cbc_backward_branch (context_p, (uint16_t) opcode, do_while_statement.start_offset); } else { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } parser_stack_pop (context_p, NULL, 1 + sizeof (parser_loop_statement_t) + sizeof (parser_do_while_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); parser_set_breaks_to_current_position (context_p, loop.branch_list_p); } /* parser_parse_do_while_statement_end */ /** * Parse while statement (starting part). */ static void parser_parse_while_statement_start (parser_context_t *context_p) /**< context */ { parser_while_statement_t while_statement; parser_loop_statement_t loop; JERRY_ASSERT (context_p->token.type == LEXER_KEYW_WHILE); lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_PAREN) { parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p || context_p->next_scanner_info_p->type == SCANNER_TYPE_WHILE); if (context_p->next_scanner_info_p->source_p != context_p->source_p) { /* The prescanner couldn't find the end of the while condition. */ lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); JERRY_ASSERT (context_p->token.type != LEXER_RIGHT_PAREN); parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &while_statement.branch); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); while_statement.start_offset = context_p->byte_code_size; scanner_get_location (&while_statement.condition_location, context_p); scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); scanner_release_next (context_p, sizeof (scanner_location_info_t)); scanner_seek (context_p); lexer_next_token (context_p); loop.branch_list_p = NULL; parser_stack_push (context_p, &while_statement, sizeof (parser_while_statement_t)); parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_WHILE); parser_stack_iterator_init (context_p, &context_p->last_statement); } /* parser_parse_while_statement_start */ /** * Parse while statement (ending part). */ static void JERRY_ATTR_NOINLINE parser_parse_while_statement_end (parser_context_t *context_p) /**< context */ { parser_while_statement_t while_statement; parser_loop_statement_t loop; lexer_token_t current_token; scanner_location_t location; cbc_opcode_t opcode; JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_WHILE); parser_stack_iterator_t iterator; parser_stack_iterator_init (context_p, &iterator); parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t)); parser_stack_iterator_read (&iterator, &while_statement, sizeof (parser_while_statement_t)); scanner_get_location (&location, context_p); current_token = context_p->token; parser_set_branch_to_current_position (context_p, &while_statement.branch); parser_set_continues_to_current_position (context_p, loop.branch_list_p); scanner_set_location (context_p, &while_statement.condition_location); scanner_seek (context_p); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } opcode = CBC_BRANCH_IF_TRUE_BACKWARD; if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_BRANCH_IF_FALSE_BACKWARD; } else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_JUMP_BACKWARD; } parser_stack_pop (context_p, NULL, 1 + sizeof (parser_loop_statement_t) + sizeof (parser_while_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); parser_emit_cbc_backward_branch (context_p, (uint16_t) opcode, while_statement.start_offset); parser_set_breaks_to_current_position (context_p, loop.branch_list_p); /* Calling scanner_seek is unnecessary because all * info blocks inside the while statement should be processed. */ scanner_set_location (context_p, &location); context_p->token = current_token; } /* parser_parse_while_statement_end */ /** * Check whether the opcode is a valid LeftHandSide expression * and convert it back to an assignment. * * @return the compatible assignment opcode */ static uint16_t parser_check_left_hand_side_expression (parser_context_t *context_p, /**< context */ uint16_t opcode) /**< opcode to check */ { if (opcode == CBC_PUSH_LITERAL && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; return CBC_ASSIGN_SET_IDENT; } else if (opcode == CBC_PUSH_PROP) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; return CBC_ASSIGN; } else if (opcode == CBC_PUSH_PROP_LITERAL) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; return CBC_ASSIGN_PROP_LITERAL; } else if (opcode == CBC_PUSH_PROP_LITERAL_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; return CBC_ASSIGN; } else if (opcode == CBC_PUSH_PROP_THIS_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL; return CBC_ASSIGN; } else { /* Invalid LeftHandSide expression. */ parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_FOR_LOOP); return CBC_ASSIGN; } return opcode; } /* parser_check_left_hand_side_expression */ /** * Parse for statement (starting part). */ static void parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ { parser_loop_statement_t loop; JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FOR); lexer_next_token (context_p); bool is_for_await = false; if (context_p->token.type == LEXER_KEYW_AWAIT) { if (JERRY_UNLIKELY (context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } lexer_next_token (context_p); is_for_await = true; } if (context_p->token.type != LEXER_LEFT_PAREN) { if (context_p->token.type == LEXER_LITERAL && context_p->token.keyword_type == LEXER_KEYW_AWAIT && !(context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { parser_raise_error (context_p, PARSER_ERR_FOR_AWAIT_NO_ASYNC); } parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } if (context_p->next_scanner_info_p->source_p == context_p->source_p) { parser_for_in_of_statement_t for_in_of_statement; scanner_location_t start_location, end_location; JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN || context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_OF); bool is_for_in = (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN); end_location = ((scanner_location_info_t *) context_p->next_scanner_info_p)->location; scanner_release_next (context_p, sizeof (scanner_location_info_t)); scanner_get_location (&start_location, context_p); lexer_next_token (context_p); uint8_t token_type = LEXER_EOS; bool has_context = false; if (context_p->token.type == LEXER_KEYW_VAR || context_p->token.type == LEXER_KEYW_LET || context_p->token.type == LEXER_KEYW_CONST) { token_type = context_p->token.type; has_context = context_p->next_scanner_info_p->source_p == context_p->source_p; JERRY_ASSERT (!has_context || context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); scanner_get_location (&start_location, context_p); /* TODO: remove this after the pre-scanner supports strict mode detection. */ if (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION) { scanner_release_next (context_p, sizeof (scanner_info_t)); } } else if (context_p->token.type == LEXER_LITERAL && lexer_token_is_let (context_p)) { if (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION) { scanner_release_next (context_p, sizeof (scanner_info_t)); } else { token_type = LEXER_KEYW_LET; has_context = (context_p->next_scanner_info_p->source_p == context_p->source_p); scanner_get_location (&start_location, context_p); } } if (has_context) { has_context = parser_push_block_context (context_p, true); } scanner_set_location (context_p, &end_location); /* The length of both 'in' and 'of' is two. */ const uint8_t *source_end_p = context_p->source_p - 2; scanner_seek (context_p); if (is_for_in && is_for_await) { context_p->token.line = context_p->line; context_p->token.column = context_p->column - 2; parser_raise_error (context_p, PARSER_ERR_FOR_AWAIT_NO_OF); } lexer_next_token (context_p); int options = is_for_in ? PARSE_EXPR : PARSE_EXPR_NO_COMMA; parser_parse_expression (context_p, options); if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } #ifndef JERRY_NDEBUG PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, is_for_in ? PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION : PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ cbc_ext_opcode_t init_opcode = CBC_EXT_FOR_IN_INIT; if (!is_for_in) { init_opcode = is_for_await ? CBC_EXT_FOR_AWAIT_OF_INIT : CBC_EXT_FOR_OF_INIT; } parser_emit_cbc_ext_forward_branch (context_p, init_opcode, &for_in_of_statement.branch); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); for_in_of_statement.start_offset = context_p->byte_code_size; if (has_context) { parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_CONTEXT); } /* The expression parser must not read the 'in' or 'of' tokens. */ scanner_get_location (&end_location, context_p); scanner_set_location (context_p, &start_location); const uint8_t *original_source_end_p = context_p->source_end_p; context_p->source_end_p = source_end_p; scanner_seek (context_p); if (token_type == LEXER_EOS) { lexer_next_token (context_p); if (context_p->token.type == LEXER_LEFT_SQUARE || context_p->token.type == LEXER_LEFT_BRACE) { token_type = context_p->token.type; } } switch (token_type) { case LEXER_KEYW_LET: case LEXER_KEYW_CONST: case LEXER_KEYW_VAR: { if (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE)) { parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT : CBC_EXT_FOR_OF_GET_NEXT); parser_pattern_flags_t flags = (parser_pattern_flags_t ) ((unsigned int) PARSER_PATTERN_BINDING | (unsigned int) PARSER_PATTERN_TARGET_ON_STACK); if (context_p->next_scanner_info_p->source_p == (context_p->source_p + 1)) { if (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER) { scanner_release_next (context_p, sizeof (scanner_location_info_t)); } else { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS); if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { flags = (parser_pattern_flags_t) ((int) flags | (int) PARSER_PATTERN_HAS_REST_ELEMENT); } scanner_release_next (context_p, sizeof (scanner_info_t)); } } if (token_type == LEXER_KEYW_LET) { flags = (parser_pattern_flags_t) ((int) flags | (int) PARSER_PATTERN_LET); } else if (token_type == LEXER_KEYW_CONST) { flags = (parser_pattern_flags_t) ((int) flags | (int) PARSER_PATTERN_CONST); } parser_parse_initializer_by_next_char (context_p, flags); break; } lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); if (context_p->token.keyword_type == LEXER_KEYW_LET && token_type != LEXER_KEYW_VAR) { parser_raise_error (context_p, PARSER_ERR_LEXICAL_LET_BINDING); } JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); uint16_t literal_index = context_p->lit_object.index; lexer_next_token (context_p); if (context_p->token.type == LEXER_ASSIGN) { if ((context_p->status_flags & PARSER_IS_STRICT) || !is_for_in) { parser_raise_error (context_p, PARSER_ERR_FOR_IN_OF_DECLARATION); } parser_branch_t branch; /* Initialiser is never executed. */ parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &branch); lexer_next_token (context_p); parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA); parser_set_branch_to_current_position (context_p, &branch); } parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT : CBC_EXT_FOR_OF_GET_NEXT); #ifndef JERRY_NDEBUG if (literal_index < PARSER_REGISTER_START && has_context && !scanner_literal_is_created (context_p, literal_index)) { context_p->global_status_flags |= ECMA_PARSE_INTERNAL_FOR_IN_OFF_CONTEXT_ERROR; } #endif /* !JERRY_NDEBUG */ uint16_t opcode = (has_context ? CBC_ASSIGN_LET_CONST : CBC_ASSIGN_SET_IDENT); parser_emit_cbc_literal (context_p, opcode, literal_index); break; } case LEXER_LEFT_BRACE: case LEXER_LEFT_SQUARE: { if (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS && (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_DESTRUCTURING_FOR)) { parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT : CBC_EXT_FOR_OF_GET_NEXT); uint32_t flags = PARSER_PATTERN_TARGET_ON_STACK; if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { flags |= PARSER_PATTERN_HAS_REST_ELEMENT; } scanner_release_next (context_p, sizeof (scanner_info_t)); parser_parse_initializer (context_p, (parser_pattern_flags_t) flags); /* Pop the value returned by GET_NEXT. */ parser_emit_cbc (context_p, CBC_POP); break; } /* FALLTHRU */ } default: { uint16_t opcode; parser_parse_expression (context_p, PARSE_EXPR_LEFT_HAND_SIDE); opcode = context_p->last_cbc_opcode; /* The CBC_EXT_FOR_IN_CREATE_CONTEXT flushed the opcode combiner. */ JERRY_ASSERT (opcode != CBC_PUSH_TWO_LITERALS && opcode != CBC_PUSH_THREE_LITERALS); opcode = parser_check_left_hand_side_expression (context_p, opcode); parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT : CBC_EXT_FOR_OF_GET_NEXT); parser_flush_cbc (context_p); context_p->last_cbc_opcode = opcode; break; } } if (context_p->token.type != LEXER_EOS) { parser_raise_error (context_p, is_for_in ? PARSER_ERR_IN_EXPECTED : PARSER_ERR_OF_EXPECTED); } parser_flush_cbc (context_p); scanner_set_location (context_p, &end_location); context_p->source_end_p = original_source_end_p; lexer_next_token (context_p); loop.branch_list_p = NULL; parser_stack_push (context_p, &for_in_of_statement, sizeof (parser_for_in_of_statement_t)); parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); uint8_t for_type = PARSER_STATEMENT_FOR_IN; if (!is_for_in) { for_type = is_for_await ? PARSER_STATEMENT_FOR_AWAIT_OF : PARSER_STATEMENT_FOR_OF; } parser_stack_push_uint8 (context_p, for_type); parser_stack_iterator_init (context_p, &context_p->last_statement); return; } lexer_next_token (context_p); if (context_p->token.type != LEXER_SEMICOLON) { const uint8_t *source_p = context_p->source_p; switch (context_p->token.type) { case LEXER_LITERAL: { if (!lexer_token_is_let (context_p)) { parser_parse_expression_statement (context_p, PARSE_EXPR); break; } /* FALLTHRU */ } case LEXER_KEYW_LET: { if (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type != SCANNER_TYPE_BLOCK) { if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION) { scanner_release_next (context_p, sizeof (scanner_info_t)); } parser_parse_expression_statement (context_p, PARSE_EXPR); break; } context_p->token.type = LEXER_KEYW_LET; /* FALLTHRU */ } case LEXER_KEYW_CONST: { if (context_p->next_scanner_info_p->source_p == source_p) { parser_push_block_context (context_p, true); } /* FALLTHRU */ } case LEXER_KEYW_VAR: { parser_parse_var_statement (context_p); break; } default: { parser_parse_expression_statement (context_p, PARSE_EXPR); break; } } if (context_p->token.type != LEXER_SEMICOLON) { parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } } if (is_for_await) { parser_raise_error (context_p, PARSER_ERR_FOR_AWAIT_NO_OF); } JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p || context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR); if (context_p->next_scanner_info_p->source_p != context_p->source_p || ((scanner_for_info_t *) context_p->next_scanner_info_p)->end_location.source_p == NULL) { if (context_p->next_scanner_info_p->source_p == context_p->source_p) { /* Even though the scanning is failed, there might be valid statements * inside the for statement which depend on scanner info blocks. */ scanner_release_next (context_p, sizeof (scanner_for_info_t)); } /* The prescanner couldn't find the second semicolon or the closing parentheses. */ lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_SEMICOLON) { parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } lexer_next_token (context_p); parser_parse_expression_statement (context_p, PARSE_EXPR); JERRY_ASSERT (context_p->token.type != LEXER_RIGHT_PAREN); parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } parser_for_statement_t for_statement; scanner_for_info_t *for_info_p = (scanner_for_info_t *) context_p->next_scanner_info_p; parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &for_statement.branch); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); for_statement.start_offset = context_p->byte_code_size; scanner_get_location (&for_statement.condition_location, context_p); for_statement.expression_location = for_info_p->expression_location; scanner_set_location (context_p, &for_info_p->end_location); scanner_release_next (context_p, sizeof (scanner_for_info_t)); scanner_seek (context_p); lexer_next_token (context_p); loop.branch_list_p = NULL; parser_stack_push (context_p, &for_statement, sizeof (parser_for_statement_t)); parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR); parser_stack_iterator_init (context_p, &context_p->last_statement); } /* parser_parse_for_statement_start */ /** * Parse for statement (ending part). */ static void JERRY_ATTR_NOINLINE parser_parse_for_statement_end (parser_context_t *context_p) /**< context */ { parser_for_statement_t for_statement; parser_loop_statement_t loop; lexer_token_t current_token; scanner_location_t location; cbc_opcode_t opcode; JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR); parser_stack_iterator_t iterator; parser_stack_iterator_init (context_p, &iterator); parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t)); parser_stack_iterator_read (&iterator, &for_statement, sizeof (parser_for_statement_t)); bool has_block_context = false; uint8_t next_statement_type; parser_stack_iterator_skip (&iterator, sizeof (parser_for_statement_t)); parser_stack_iterator_read (&iterator, &next_statement_type, 1); if (next_statement_type == PARSER_STATEMENT_PRIVATE_CONTEXT) { has_block_context = true; } scanner_get_location (&location, context_p); current_token = context_p->token; scanner_set_location (context_p, &for_statement.expression_location); scanner_seek (context_p); lexer_next_token (context_p); parser_set_continues_to_current_position (context_p, loop.branch_list_p); if (has_block_context) { parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_FULL_CONTEXT); } if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_parse_expression_statement (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } } parser_set_branch_to_current_position (context_p, &for_statement.branch); scanner_set_location (context_p, &for_statement.condition_location); scanner_seek (context_p); lexer_next_token (context_p); if (context_p->token.type != LEXER_SEMICOLON) { parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_SEMICOLON) { parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } opcode = CBC_BRANCH_IF_TRUE_BACKWARD; if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_BRANCH_IF_FALSE_BACKWARD; } else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_JUMP_BACKWARD; } } else { opcode = CBC_JUMP_BACKWARD; } parser_stack_pop (context_p, NULL, 1 + sizeof (parser_loop_statement_t) + sizeof (parser_for_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); parser_emit_cbc_backward_branch (context_p, (uint16_t) opcode, for_statement.start_offset); parser_set_breaks_to_current_position (context_p, loop.branch_list_p); if (context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE || context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT) { parser_pop_block_context (context_p); } /* Calling scanner_seek is unnecessary because all * info blocks inside the for statement should be processed. */ scanner_set_location (context_p, &location); context_p->token = current_token; } /* parser_parse_for_statement_end */ /** * Parse switch statement (starting part). */ static void JERRY_ATTR_NOINLINE parser_parse_switch_statement_start (parser_context_t *context_p) /**< context */ { parser_switch_statement_t switch_statement; parser_loop_statement_t loop; parser_stack_iterator_t iterator; scanner_location_t start_location; bool switch_case_was_found; bool default_case_was_found; parser_branch_node_t *case_branches_p = NULL; JERRY_ASSERT (context_p->token.type == LEXER_KEYW_SWITCH); parser_parse_enclosed_expr (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } if (context_p->next_scanner_info_p->source_p == context_p->source_p - 1) { parser_push_block_context (context_p, true); } JERRY_ASSERT (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_SWITCH); scanner_case_info_t *case_info_p = ((scanner_switch_info_t *) context_p->next_scanner_info_p)->case_p; scanner_set_active (context_p); if (case_info_p == NULL) { lexer_next_token (context_p); if (context_p->token.type == LEXER_RIGHT_BRACE) { scanner_release_active (context_p, sizeof (scanner_switch_info_t)); parser_emit_cbc (context_p, CBC_POP); parser_flush_cbc (context_p); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); parser_stack_iterator_init (context_p, &context_p->last_statement); return; } parser_raise_error (context_p, PARSER_ERR_INVALID_SWITCH); } scanner_get_location (&start_location, context_p); /* The reason of using an iterator is error management. If an error * occur, parser_free_jumps() free all data. However, the branches * created by parser_emit_cbc_forward_branch_item() would not be freed. * To free these branches, the current switch data is always stored * on the stack. If any change happens, this data is updated. Updates * are done using the iterator. */ switch_statement.branch_list_p = NULL; loop.branch_list_p = NULL; parser_stack_push (context_p, &switch_statement, sizeof (parser_switch_statement_t)); parser_stack_iterator_init (context_p, &iterator); parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_SWITCH); parser_stack_iterator_init (context_p, &context_p->last_statement); switch_case_was_found = false; default_case_was_found = false; do { scanner_set_location (context_p, &case_info_p->location); scanner_seek (context_p); case_info_p = case_info_p->next_p; /* The last letter of case and default is 'e' and 't' respectively. */ JERRY_ASSERT (context_p->source_p[-1] == LIT_CHAR_LOWERCASE_E || context_p->source_p[-1] == LIT_CHAR_LOWERCASE_T); bool is_default = context_p->source_p[-1] == LIT_CHAR_LOWERCASE_T; lexer_next_token (context_p); if (is_default) { if (default_case_was_found) { parser_raise_error (context_p, PARSER_ERR_MULTIPLE_DEFAULTS_NOT_ALLOWED); } if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } default_case_was_found = true; continue; } switch_case_was_found = true; parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } uint16_t opcode = CBC_BRANCH_IF_STRICT_EQUAL; if (case_info_p == NULL || (case_info_p->next_p == NULL && case_info_p->location.source_p[-1] == LIT_CHAR_LOWERCASE_T)) { /* There are no more 'case' statements in the switch. */ parser_emit_cbc (context_p, CBC_STRICT_EQUAL); opcode = CBC_BRANCH_IF_TRUE_FORWARD; } parser_branch_node_t *new_case_p = parser_emit_cbc_forward_branch_item (context_p, opcode, NULL); if (case_branches_p == NULL) { switch_statement.branch_list_p = new_case_p; parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); } else { case_branches_p->next_p = new_case_p; } case_branches_p = new_case_p; } while (case_info_p != NULL); JERRY_ASSERT (switch_case_was_found || default_case_was_found); if (!switch_case_was_found) { /* There was no case statement, so the expression result * of the switch must be popped from the stack */ parser_emit_cbc (context_p, CBC_POP); } parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &switch_statement.default_branch); parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); if (!default_case_was_found) { parser_stack_change_last_uint8 (context_p, PARSER_STATEMENT_SWITCH_NO_DEFAULT); } scanner_release_switch_cases (((scanner_switch_info_t *) context_p->active_scanner_info_p)->case_p); scanner_release_active (context_p, sizeof (scanner_switch_info_t)); scanner_set_location (context_p, &start_location); scanner_seek (context_p); lexer_next_token (context_p); } /* parser_parse_switch_statement_start */ /** * Parse try statement (ending part). */ static void parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ { parser_try_statement_t try_statement; parser_stack_iterator_t iterator; JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_TRY); parser_stack_iterator_init (context_p, &iterator); parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &try_statement, sizeof (parser_try_statement_t)); context_p->scope_stack_top = try_statement.scope_stack_top; context_p->scope_stack_reg_top = try_statement.scope_stack_reg_top; lexer_next_token (context_p); if (try_statement.type == parser_finally_block) { parser_flush_cbc (context_p); PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_FINALLY_CONTEXT_STACK_ALLOCATION); #ifndef JERRY_NDEBUG PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_FINALLY_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ parser_emit_cbc (context_p, CBC_CONTEXT_END); parser_set_branch_to_current_position (context_p, &try_statement.branch); } else { parser_set_branch_to_current_position (context_p, &try_statement.branch); if (try_statement.type == parser_catch_block) { if (context_p->token.type != LEXER_KEYW_FINALLY) { parser_flush_cbc (context_p); PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); #ifndef JERRY_NDEBUG PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ parser_emit_cbc (context_p, CBC_CONTEXT_END); parser_flush_cbc (context_p); try_statement.type = parser_finally_block; } } else { JERRY_ASSERT (try_statement.type == parser_try_block); if (context_p->token.type != LEXER_KEYW_CATCH && context_p->token.type != LEXER_KEYW_FINALLY) { parser_raise_error (context_p, PARSER_ERR_CATCH_FINALLY_EXPECTED); } } } if (try_statement.type == parser_finally_block) { parser_stack_pop (context_p, NULL, (uint32_t) (sizeof (parser_try_statement_t) + 1)); parser_stack_iterator_init (context_p, &context_p->last_statement); return; } if (context_p->token.type == LEXER_KEYW_CATCH) { lexer_next_token (context_p); try_statement.type = parser_catch_block; parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_CATCH, &try_statement.branch); try_statement.scope_stack_top = context_p->scope_stack_top; try_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; #ifndef JERRY_NDEBUG bool block_found = false; #endif /* !JERRY_NDEBUG */ if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); #ifndef JERRY_NDEBUG block_found = true; #endif /* !JERRY_NDEBUG */ if (scanner_is_context_needed (context_p, PARSER_CHECK_BLOCK_CONTEXT)) { parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); } scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); } if (context_p->token.type == LEXER_LEFT_PAREN) { if (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE)) { parser_pattern_flags_t flags = (parser_pattern_flags_t) ((unsigned int) PARSER_PATTERN_BINDING | (unsigned int) PARSER_PATTERN_TARGET_ON_STACK | (unsigned int) PARSER_PATTERN_LET); parser_parse_initializer_by_next_char (context_p, flags); } else { lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); uint16_t literal_index = context_p->lit_object.index; parser_emit_cbc_literal (context_p, (literal_index >= PARSER_REGISTER_START) ? CBC_ASSIGN_SET_IDENT : CBC_ASSIGN_LET_CONST, literal_index); lexer_next_token (context_p); #ifndef JERRY_NDEBUG JERRY_ASSERT (block_found); #endif /* !JERRY_NDEBUG */ } if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } } else if (context_p->token.type == LEXER_LEFT_BRACE) { parser_emit_cbc (context_p, CBC_POP); } else { parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } parser_flush_cbc (context_p); } else { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FINALLY); lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } #ifndef JERRY_NDEBUG PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ try_statement.type = parser_finally_block; parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_FINALLY, &try_statement.branch); if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); if (scanner_is_context_needed (context_p, PARSER_CHECK_BLOCK_CONTEXT)) { parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); } scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); } } lexer_next_token (context_p); parser_stack_iterator_write (&iterator, &try_statement, sizeof (parser_try_statement_t)); } /* parser_parse_try_statement_end */ /** * Parse default statement. */ static void parser_parse_default_statement (parser_context_t *context_p) /**< context */ { parser_stack_iterator_t iterator; parser_switch_statement_t switch_statement; if (context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH && context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH_NO_DEFAULT) { parser_raise_error (context_p, PARSER_ERR_DEFAULT_NOT_IN_SWITCH); } lexer_next_token (context_p); /* Already checked in parser_parse_switch_statement_start. */ JERRY_ASSERT (context_p->token.type == LEXER_COLON); lexer_next_token (context_p); parser_stack_iterator_init (context_p, &iterator); parser_stack_iterator_skip (&iterator, 1 + sizeof (parser_loop_statement_t)); parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); parser_set_branch_to_current_position (context_p, &switch_statement.default_branch); } /* parser_parse_default_statement */ /** * Parse case statement. */ static void parser_parse_case_statement (parser_context_t *context_p) /**< context */ { parser_stack_iterator_t iterator; parser_switch_statement_t switch_statement; parser_branch_node_t *branch_p; if (context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH && context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH_NO_DEFAULT) { parser_raise_error (context_p, PARSER_ERR_CASE_NOT_IN_SWITCH); } if (context_p->next_scanner_info_p->source_p != context_p->source_p) { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); JERRY_ASSERT (context_p->token.type != LEXER_COLON); parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CASE); scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); scanner_release_next (context_p, sizeof (scanner_location_info_t)); scanner_seek (context_p); lexer_next_token (context_p); parser_stack_iterator_init (context_p, &iterator); parser_stack_iterator_skip (&iterator, 1 + sizeof (parser_loop_statement_t)); parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); /* Free memory after the case statement is found. */ branch_p = switch_statement.branch_list_p; JERRY_ASSERT (branch_p != NULL); switch_statement.branch_list_p = branch_p->next_p; parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); parser_set_branch_to_current_position (context_p, &branch_p->branch); parser_free (branch_p, sizeof (parser_branch_node_t)); } /* parser_parse_case_statement */ /** * Parse break statement. */ static void parser_parse_break_statement (parser_context_t *context_p) /**< context */ { parser_stack_iterator_t iterator; cbc_opcode_t opcode = CBC_JUMP_FORWARD; lexer_next_token (context_p); parser_stack_iterator_init (context_p, &iterator); if (!(context_p->token.flags & LEXER_WAS_NEWLINE) && context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { /* The label with the same name is searched on the stack. */ while (true) { uint8_t type = parser_stack_iterator_read_uint8 (&iterator); if (type == PARSER_STATEMENT_START) { parser_raise_error (context_p, PARSER_ERR_INVALID_BREAK_LABEL); } if (parser_statement_flags[type] & PARSER_STATM_CONTEXT_BREAK) { opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; } if (type == PARSER_STATEMENT_LABEL) { parser_label_statement_t label_statement; parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); if (lexer_current_is_literal (context_p, &label_statement.label_ident)) { label_statement.break_list_p = parser_emit_cbc_forward_branch_item (context_p, (uint16_t) opcode, label_statement.break_list_p); parser_stack_iterator_write (&iterator, &label_statement, sizeof (parser_label_statement_t)); lexer_next_token (context_p); return; } parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); } else { parser_stack_iterator_skip (&iterator, parser_statement_length (type)); } } } /* The first switch or loop statement is searched. */ while (true) { uint8_t type = parser_stack_iterator_read_uint8 (&iterator); if (type == PARSER_STATEMENT_START) { parser_raise_error (context_p, PARSER_ERR_INVALID_BREAK); } if (parser_statement_flags[type] & PARSER_STATM_CONTEXT_BREAK) { opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; } if (parser_statement_flags[type] & PARSER_STATM_BREAK_TARGET) { parser_loop_statement_t loop; parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p, (uint16_t) opcode, loop.branch_list_p); parser_stack_iterator_write (&iterator, &loop, sizeof (parser_loop_statement_t)); return; } parser_stack_iterator_skip (&iterator, parser_statement_length (type)); } } /* parser_parse_break_statement */ /** * Parse continue statement. */ static void parser_parse_continue_statement (parser_context_t *context_p) /**< context */ { parser_stack_iterator_t iterator; cbc_opcode_t opcode = CBC_JUMP_FORWARD; lexer_next_token (context_p); parser_stack_iterator_init (context_p, &iterator); if (!(context_p->token.flags & LEXER_WAS_NEWLINE) && context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { parser_stack_iterator_t loop_iterator; loop_iterator.current_p = NULL; /* The label with the same name is searched on the stack. */ while (true) { uint8_t type = parser_stack_iterator_read_uint8 (&iterator); if (type == PARSER_STATEMENT_START) { parser_raise_error (context_p, PARSER_ERR_INVALID_CONTINUE_LABEL); } /* Only those labels are checked, whose are label of a loop. */ if (loop_iterator.current_p != NULL && type == PARSER_STATEMENT_LABEL) { parser_label_statement_t label_statement; parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); if (lexer_current_is_literal (context_p, &label_statement.label_ident)) { parser_loop_statement_t loop; parser_stack_iterator_skip (&loop_iterator, 1); parser_stack_iterator_read (&loop_iterator, &loop, sizeof (parser_loop_statement_t)); loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p, (uint16_t) opcode, loop.branch_list_p); loop.branch_list_p->branch.offset |= CBC_HIGHEST_BIT_MASK; parser_stack_iterator_write (&loop_iterator, &loop, sizeof (parser_loop_statement_t)); lexer_next_token (context_p); return; } parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); continue; } if (parser_statement_flags[type] & PARSER_STATM_CONTEXT_BREAK) { opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; } const bool is_private_scope = (type == PARSER_STATEMENT_PRIVATE_SCOPE || type == PARSER_STATEMENT_PRIVATE_CONTEXT); if (parser_statement_flags[type] & PARSER_STATM_CONTINUE_TARGET) { loop_iterator = iterator; } else if (!is_private_scope) { loop_iterator.current_p = NULL; } parser_stack_iterator_skip (&iterator, parser_statement_length (type)); } } /* The first loop statement is searched. */ while (true) { uint8_t type = parser_stack_iterator_read_uint8 (&iterator); if (type == PARSER_STATEMENT_START) { parser_raise_error (context_p, PARSER_ERR_INVALID_CONTINUE); } if (parser_statement_flags[type] & PARSER_STATM_CONTINUE_TARGET) { parser_loop_statement_t loop; parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p, (uint16_t) opcode, loop.branch_list_p); loop.branch_list_p->branch.offset |= CBC_HIGHEST_BIT_MASK; parser_stack_iterator_write (&iterator, &loop, sizeof (parser_loop_statement_t)); return; } if (parser_statement_flags[type] & PARSER_STATM_CONTEXT_BREAK) { opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; } parser_stack_iterator_skip (&iterator, parser_statement_length (type)); } } /* parser_parse_continue_statement */ #if JERRY_MODULE_SYSTEM /** * Parse import statement. * Note: See 15.2.2 */ static void parser_parse_import_statement (parser_context_t *context_p) /**< parser context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_IMPORT); JERRY_ASSERT (context_p->module_names_p == NULL); if (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_PAREN, LIT_CHAR_DOT)) { if (context_p->status_flags & PARSER_IS_FUNCTION) { parser_parse_expression_statement (context_p, PARSE_EXPR); return; } parser_parse_block_expression (context_p, PARSE_EXPR); return; } parser_module_check_request_place (context_p); lexer_next_token (context_p); /* Check for a ModuleSpecifier*/ if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_STRING_LITERAL) { if (!(context_p->token.type == LEXER_LEFT_BRACE || context_p->token.type == LEXER_MULTIPLY || (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL))) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_MULTIPLY_LITERAL_EXPECTED); } if (context_p->token.type == LEXER_LITERAL) { /* Handle ImportedDefaultBinding */ lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); ecma_string_t *local_name_p = parser_new_ecma_string_from_literal (context_p->lit_object.literal_p); if (parser_module_check_duplicate_import (context_p, local_name_p)) { ecma_deref_ecma_string (local_name_p); parser_raise_error (context_p, PARSER_ERR_DUPLICATED_IMPORT_BINDING); } ecma_string_t *import_name_p = ecma_get_magic_string (LIT_MAGIC_STRING_DEFAULT); parser_module_add_names_to_node (context_p, import_name_p, local_name_p); ecma_deref_ecma_string (local_name_p); ecma_deref_ecma_string (import_name_p); lexer_next_token (context_p); if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); if (context_p->token.type != LEXER_MULTIPLY && context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_MULTIPLY_EXPECTED); } } else if (!lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_FROM_COMMA_EXPECTED); } } if (context_p->token.type == LEXER_MULTIPLY) { /* NameSpaceImport */ lexer_next_token (context_p); if (!lexer_token_is_identifier (context_p, "as", 2)) { parser_raise_error (context_p, PARSER_ERR_AS_EXPECTED); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); ecma_string_t *local_name_p = parser_new_ecma_string_from_literal (context_p->lit_object.literal_p); if (parser_module_check_duplicate_import (context_p, local_name_p)) { ecma_deref_ecma_string (local_name_p); parser_raise_error (context_p, PARSER_ERR_DUPLICATED_IMPORT_BINDING); } ecma_string_t *import_name_p = ecma_get_magic_string (LIT_MAGIC_STRING_ASTERISK_CHAR); parser_module_add_names_to_node (context_p, import_name_p, local_name_p); ecma_deref_ecma_string (local_name_p); ecma_deref_ecma_string (import_name_p); lexer_next_token (context_p); } else if (context_p->token.type == LEXER_LEFT_BRACE) { /* Handle NamedImports */ parser_module_parse_import_clause (context_p); } if (!lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_FROM_EXPECTED); } lexer_next_token (context_p); } parser_module_handle_module_specifier (context_p, NULL); } /* parser_parse_import_statement */ /** * Parse export statement. * * @return true - if function of class statement was found * false - otherwise */ static bool parser_parse_export_statement (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_EXPORT); JERRY_ASSERT (context_p->module_names_p == NULL); parser_module_check_request_place (context_p); bool consume_last_statement = false; lexer_next_token (context_p); switch (context_p->token.type) { case LEXER_KEYW_DEFAULT: { scanner_location_t location; scanner_get_location (&location, context_p); context_p->status_flags |= PARSER_MODULE_STORE_IDENT; lexer_next_token (context_p); if (context_p->token.type == LEXER_LITERAL && lexer_token_is_async (context_p) && context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION) { lexer_next_token (context_p); } if (context_p->token.type == LEXER_KEYW_CLASS) { context_p->status_flags |= PARSER_MODULE_DEFAULT_CLASS_OR_FUNC; parser_parse_class (context_p, true); consume_last_statement = true; } else if (context_p->token.type == LEXER_KEYW_FUNCTION) { context_p->status_flags |= PARSER_MODULE_DEFAULT_CLASS_OR_FUNC; parser_parse_function_statement (context_p); consume_last_statement = true; } else { /* Assignment expression */ scanner_set_location (context_p, &location); /* 15.2.3.5 Use the synthetic name '*default*' as the identifier. */ lexer_construct_literal_object (context_p, &lexer_default_literal, lexer_default_literal.type); context_p->token.lit_location.type = LEXER_IDENT_LITERAL; parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); /* Do not overwrite this identifier. */ context_p->status_flags &= (uint32_t) ~PARSER_MODULE_STORE_IDENT; context_p->module_identifier_lit_p = context_p->lit_object.literal_p; /* Fake an assignment to the default identifier */ context_p->token.type = LEXER_ASSIGN; parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); } ecma_string_t *name_p = parser_new_ecma_string_from_literal (context_p->module_identifier_lit_p); ecma_string_t *export_name_p = ecma_get_magic_string (LIT_MAGIC_STRING_DEFAULT); if (parser_module_check_duplicate_export (context_p, export_name_p)) { ecma_deref_ecma_string (name_p); ecma_deref_ecma_string (export_name_p); parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); } parser_module_add_names_to_node (context_p, export_name_p, name_p); ecma_deref_ecma_string (name_p); ecma_deref_ecma_string (export_name_p); break; } case LEXER_MULTIPLY: { lexer_next_token (context_p); ecma_module_node_t **target_node_list_p = &(JERRY_CONTEXT (module_current_p)->star_exports_p); if (lexer_token_is_identifier (context_p, "as", 2)) { target_node_list_p = &(JERRY_CONTEXT (module_current_p)->indirect_exports_p); lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); lexer_literal_t *literal_p = PARSER_GET_LITERAL (context_p->lit_object.index); ecma_string_t *export_name_p = parser_new_ecma_string_from_literal (literal_p); if (parser_module_check_duplicate_export (context_p, export_name_p)) { ecma_deref_ecma_string (export_name_p); parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); } ecma_string_t *local_name_p = ecma_get_magic_string (LIT_MAGIC_STRING_ASTERISK_CHAR); parser_module_add_names_to_node (context_p, export_name_p, local_name_p); ecma_deref_ecma_string (export_name_p); lexer_next_token (context_p); } if (!lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_FROM_EXPECTED); } lexer_next_token (context_p); parser_module_handle_module_specifier (context_p, target_node_list_p); return false; } case LEXER_KEYW_VAR: case LEXER_KEYW_LET: case LEXER_KEYW_CONST: { context_p->status_flags |= PARSER_MODULE_STORE_IDENT; parser_parse_var_statement (context_p); break; } case LEXER_KEYW_CLASS: { context_p->status_flags |= PARSER_MODULE_STORE_IDENT; parser_parse_class (context_p, true); consume_last_statement = true; break; } case LEXER_KEYW_FUNCTION: { context_p->status_flags |= PARSER_MODULE_STORE_IDENT; parser_parse_function_statement (context_p); consume_last_statement = true; break; } case LEXER_LEFT_BRACE: { parser_module_parse_export_clause (context_p); if (lexer_token_is_identifier (context_p, "from", 4)) { lexer_next_token (context_p); parser_module_handle_module_specifier (context_p, &(JERRY_CONTEXT (module_current_p)->indirect_exports_p)); return false; } break; } default: { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_MULTIPLY_LITERAL_EXPECTED); break; } } context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_DEFAULT_CLASS_OR_FUNC | PARSER_MODULE_STORE_IDENT); parser_module_append_names (context_p, &(JERRY_CONTEXT (module_current_p)->local_exports_p)); return consume_last_statement; } /* parser_parse_export_statement */ #endif /* JERRY_MODULE_SYSTEM */ /** * Parse label statement. */ static void parser_parse_label (parser_context_t *context_p) /**< context */ { parser_stack_iterator_t iterator; parser_label_statement_t label_statement; parser_stack_iterator_init (context_p, &iterator); while (true) { uint8_t type = parser_stack_iterator_read_uint8 (&iterator); if (type == PARSER_STATEMENT_START) { break; } if (type == PARSER_STATEMENT_LABEL) { parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); if (lexer_current_is_literal (context_p, &label_statement.label_ident)) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_LABEL); } } else { parser_stack_iterator_skip (&iterator, parser_statement_length (type)); } } label_statement.label_ident = context_p->token.lit_location; label_statement.break_list_p = NULL; parser_stack_push (context_p, &label_statement, sizeof (parser_label_statement_t)); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_LABEL); parser_stack_iterator_init (context_p, &context_p->last_statement); } /* parser_parse_label */ /** * Strict mode types for statement parsing. */ typedef enum { PARSER_USE_STRICT_NOT_FOUND = 0, /**< 'use strict' directive is not found */ PARSER_USE_STRICT_FOUND = 1, /**< 'use strict' directive is found but strict mode has already been enabled */ PARSER_USE_STRICT_SET = 2, /**< strict mode is enabled after 'use strict' directive is found */ } parser_strict_mode_type_t; /** * Parse statements. */ void parser_parse_statements (parser_context_t *context_p) /**< context */ { /* Statement parsing cannot be nested. */ JERRY_ASSERT (context_p->last_statement.current_p == NULL); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_START); parser_stack_iterator_init (context_p, &context_p->last_statement); while (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_STRING_LITERAL) { lexer_lit_location_t lit_location; parser_strict_mode_type_t strict_mode = PARSER_USE_STRICT_NOT_FOUND; JERRY_ASSERT (context_p->stack_depth <= 1); #ifndef JERRY_NDEBUG JERRY_ASSERT (context_p->context_stack_depth == context_p->stack_depth); #endif /* !JERRY_NDEBUG */ if (lexer_string_is_use_strict (context_p)) { strict_mode = PARSER_USE_STRICT_FOUND; if (!(context_p->status_flags & PARSER_IS_STRICT)) { /* The next token should be parsed in strict mode. */ context_p->status_flags |= PARSER_IS_STRICT; strict_mode = PARSER_USE_STRICT_SET; } } lit_location = context_p->token.lit_location; lexer_next_token (context_p); if (!lexer_string_is_directive (context_p)) { /* The string is part of an expression statement. */ if (strict_mode == PARSER_USE_STRICT_SET) { context_p->status_flags &= (uint32_t) ~PARSER_IS_STRICT; } lexer_construct_literal_object (context_p, &lit_location, LEXER_STRING_LITERAL); parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); /* The extra_value is used for saving the token. */ context_p->token.extra_value = context_p->token.type; context_p->token.type = LEXER_EXPRESSION_START; break; } #if JERRY_PARSER_DUMP_BYTE_CODE if (strict_mode == PARSER_USE_STRICT_SET && context_p->is_show_opcodes) { JERRY_DEBUG_MSG (" Note: switch to strict mode\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ if (strict_mode != PARSER_USE_STRICT_NOT_FOUND && (context_p->status_flags & PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT)) { parser_raise_error (context_p, PARSER_ERR_USE_STRICT_NOT_ALLOWED); } if (context_p->token.type == LEXER_SEMICOLON) { lexer_next_token (context_p); } /* The last directive prologue can be the result of the script. */ if (!(context_p->status_flags & PARSER_IS_FUNCTION) && (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_STRING_LITERAL)) { lexer_construct_literal_object (context_p, &lit_location, LEXER_STRING_LITERAL); parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); parser_emit_cbc (context_p, CBC_POP_BLOCK); parser_flush_cbc (context_p); break; } } if (context_p->status_flags & PARSER_IS_STRICT && context_p->status_flags & PARSER_HAS_NON_STRICT_ARG) { parser_raise_error (context_p, PARSER_ERR_NON_STRICT_ARG_DEFINITION); } while (context_p->token.type != LEXER_EOS || context_p->stack_top_uint8 != PARSER_STATEMENT_START) { #ifndef JERRY_NDEBUG JERRY_ASSERT (context_p->stack_depth == context_p->context_stack_depth); #endif /* !JERRY_NDEBUG */ JERRY_ASSERT (context_p->stack_top_uint8 != PARSER_STATEMENT_PRIVATE_SCOPE && context_p->stack_top_uint8 != PARSER_STATEMENT_PRIVATE_CONTEXT); switch (context_p->token.type) { case LEXER_SEMICOLON: { break; } case LEXER_RIGHT_BRACE: { if (parser_statement_flags[context_p->stack_top_uint8] & PARSER_STATM_SINGLE_STATM) { parser_raise_error (context_p, PARSER_ERR_STATEMENT_EXPECTED); } break; } case LEXER_LEFT_BRACE: { if (context_p->next_scanner_info_p->source_p == context_p->source_p) { parser_push_block_context (context_p, false); } else { parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); } parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); continue; } case LEXER_KEYW_VAR: case LEXER_KEYW_LET: case LEXER_KEYW_CONST: { parser_parse_var_statement (context_p); break; } case LEXER_KEYW_CLASS: { parser_validate_lexical_context (context_p); parser_parse_class (context_p, true); goto consume_last_statement; } #if JERRY_MODULE_SYSTEM case LEXER_KEYW_IMPORT: { parser_parse_import_statement (context_p); break; } case LEXER_KEYW_EXPORT: { if (parser_parse_export_statement (context_p)) { goto consume_last_statement; } break; } #endif /* JERRY_MODULE_SYSTEM */ case LEXER_KEYW_FUNCTION: { parser_parse_function_statement (context_p); goto consume_last_statement; } case LEXER_KEYW_IF: { parser_parse_if_statement_start (context_p); continue; } case LEXER_KEYW_SWITCH: { parser_parse_switch_statement_start (context_p); continue; } case LEXER_KEYW_DO: { parser_do_while_statement_t do_while_statement; parser_loop_statement_t loop; JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); do_while_statement.start_offset = context_p->byte_code_size; loop.branch_list_p = NULL; parser_stack_push (context_p, &do_while_statement, sizeof (parser_do_while_statement_t)); parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_DO_WHILE); parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); continue; } case LEXER_KEYW_WHILE: { parser_parse_while_statement_start (context_p); continue; } case LEXER_KEYW_FOR: { parser_parse_for_statement_start (context_p); continue; } case LEXER_KEYW_WITH: { parser_parse_with_statement_start (context_p); continue; } case LEXER_KEYW_TRY: { parser_try_statement_t try_statement; lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } #ifndef JERRY_NDEBUG PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ try_statement.type = parser_try_block; parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_TRY_CREATE_CONTEXT, &try_statement.branch); try_statement.scope_stack_top = context_p->scope_stack_top; try_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); if (scanner_is_context_needed (context_p, PARSER_CHECK_BLOCK_CONTEXT)) { parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); } scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); } parser_stack_push (context_p, &try_statement, sizeof (parser_try_statement_t)); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_TRY); parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); continue; } case LEXER_KEYW_DEFAULT: { parser_parse_default_statement (context_p); continue; } case LEXER_KEYW_CASE: { parser_parse_case_statement (context_p); continue; } case LEXER_KEYW_BREAK: { parser_parse_break_statement (context_p); break; } case LEXER_KEYW_CONTINUE: { parser_parse_continue_statement (context_p); break; } case LEXER_KEYW_THROW: { lexer_next_token (context_p); if (context_p->token.flags & LEXER_WAS_NEWLINE) { parser_raise_error (context_p, PARSER_ERR_EXPRESSION_EXPECTED); } parser_parse_expression (context_p, PARSE_EXPR); parser_emit_cbc (context_p, CBC_THROW); break; } case LEXER_KEYW_RETURN: { if (!(context_p->status_flags & PARSER_IS_FUNCTION)) { parser_raise_error (context_p, PARSER_ERR_INVALID_RETURN); } if (context_p->status_flags & PARSER_IS_CLASS_STATIC_BLOCK) { parser_raise_error (context_p, PARSER_ERR_INVALID_RETURN); } lexer_next_token (context_p); if ((context_p->token.flags & LEXER_WAS_NEWLINE) || context_p->token.type == LEXER_SEMICOLON || context_p->token.type == LEXER_EOS || context_p->token.type == LEXER_RIGHT_BRACE) { if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_UNDEFINED); break; } parser_emit_cbc (context_p, CBC_RETURN_FUNCTION_END); break; } parser_parse_expression (context_p, PARSE_EXPR); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL; break; } parser_emit_cbc (context_p, CBC_RETURN); break; } case LEXER_KEYW_DEBUGGER: { lexer_next_token (context_p); break; } case LEXER_LITERAL: { if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { if (JERRY_UNLIKELY (lexer_check_next_character (context_p, LIT_CHAR_COLON))) { parser_parse_label (context_p); lexer_consume_next_character (context_p); lexer_next_token (context_p); continue; } if (JERRY_UNLIKELY (lexer_token_is_let (context_p))) { if (context_p->next_scanner_info_p->source_p == context_p->source_p) { if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION) { scanner_release_next (context_p, sizeof (scanner_info_t)); } if (context_p->status_flags & PARSER_IS_FUNCTION) { parser_parse_expression_statement (context_p, PARSE_EXPR); break; } parser_parse_block_expression (context_p, PARSE_EXPR); break; } context_p->token.type = LEXER_KEYW_LET; parser_parse_var_statement (context_p); break; } if (JERRY_UNLIKELY (lexer_token_is_async (context_p)) && context_p->next_scanner_info_p->source_p == context_p->source_p) { bool is_statement = true; if (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION) { is_statement = (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_STATEMENT) != 0; JERRY_ASSERT (!is_statement || (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC)); } else { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_ASYNC_FUNCTION); scanner_release_next (context_p, sizeof (scanner_info_t)); } if (is_statement) { if (parser_statement_flags[context_p->stack_top_uint8] & PARSER_STATM_SINGLE_STATM) { parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT); } lexer_next_token (context_p); JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FUNCTION); continue; } } } /* FALLTHRU */ } default: { int options = PARSE_EXPR; if (context_p->token.type == LEXER_EXPRESSION_START) { /* Restore the token type form the extra_value. */ context_p->token.type = context_p->token.extra_value; options |= PARSE_EXPR_HAS_LITERAL; } if (context_p->status_flags & PARSER_IS_FUNCTION) { parser_parse_expression_statement (context_p, options); } else { parser_parse_block_expression (context_p, options); } break; } } parser_flush_cbc (context_p); if (context_p->token.type == LEXER_RIGHT_BRACE) { if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK) { parser_stack_pop_uint8 (context_p); parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); } else if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_SCOPE || context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT) { parser_pop_block_context (context_p); lexer_next_token (context_p); } else if (context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH || context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH_NO_DEFAULT) { int has_default = (context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH); parser_loop_statement_t loop; parser_switch_statement_t switch_statement; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t)); parser_stack_pop (context_p, &switch_statement, sizeof (parser_switch_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); JERRY_ASSERT (switch_statement.branch_list_p == NULL); if (!has_default) { parser_set_branch_to_current_position (context_p, &switch_statement.default_branch); } parser_set_breaks_to_current_position (context_p, loop.branch_list_p); lexer_next_token (context_p); if (context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE || context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT) { parser_pop_block_context (context_p); } } else if (context_p->stack_top_uint8 == PARSER_STATEMENT_TRY) { parser_parse_try_statement_end (context_p); } else if (context_p->stack_top_uint8 == PARSER_STATEMENT_START) { if (context_p->status_flags & PARSER_IS_CLOSURE) { parser_stack_pop_uint8 (context_p); context_p->last_statement.current_p = NULL; /* There is no lexer_next_token here, since the * next token belongs to the parent context. */ return; } parser_raise_error (context_p, PARSER_ERR_INVALID_RIGHT_SQUARE); } } else if (context_p->token.type == LEXER_SEMICOLON) { lexer_next_token (context_p); } else if (context_p->token.type != LEXER_EOS && !(context_p->token.flags & LEXER_WAS_NEWLINE)) { parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } consume_last_statement: while (true) { switch (context_p->stack_top_uint8) { case PARSER_STATEMENT_LABEL: { parser_label_statement_t label; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &label, sizeof (parser_label_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); parser_set_breaks_to_current_position (context_p, label.break_list_p); continue; } case PARSER_STATEMENT_IF: { if (parser_parse_if_statement_end (context_p)) { break; } continue; } case PARSER_STATEMENT_ELSE: { parser_if_else_statement_t else_statement; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &else_statement, sizeof (parser_if_else_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); parser_set_branch_to_current_position (context_p, &else_statement.branch); continue; } case PARSER_STATEMENT_DO_WHILE: { parser_parse_do_while_statement_end (context_p); if (context_p->token.type == LEXER_SEMICOLON) { lexer_next_token (context_p); } continue; } case PARSER_STATEMENT_WHILE: { parser_parse_while_statement_end (context_p); continue; } case PARSER_STATEMENT_FOR: { parser_parse_for_statement_end (context_p); continue; } case PARSER_STATEMENT_FOR_IN: case PARSER_STATEMENT_FOR_OF: case PARSER_STATEMENT_FOR_AWAIT_OF: { parser_for_in_of_statement_t for_in_of_statement; parser_loop_statement_t loop; uint8_t for_type = context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t)); parser_stack_pop (context_p, &for_in_of_statement, sizeof (parser_for_in_of_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); parser_set_continues_to_current_position (context_p, loop.branch_list_p); parser_flush_cbc (context_p); uint16_t stack_allocation = PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; if (for_type != PARSER_STATEMENT_FOR_IN) { stack_allocation = (for_type == PARSER_STATEMENT_FOR_OF ? PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION : PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION); } PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, stack_allocation); #ifndef JERRY_NDEBUG PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, stack_allocation); #endif /* !JERRY_NDEBUG */ cbc_ext_opcode_t opcode = CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT; if (for_type != PARSER_STATEMENT_FOR_IN) { opcode = (for_type == PARSER_STATEMENT_FOR_OF ? CBC_EXT_BRANCH_IF_FOR_OF_HAS_NEXT : CBC_EXT_BRANCH_IF_FOR_AWAIT_OF_HAS_NEXT); } parser_emit_cbc_ext_backward_branch (context_p, opcode, for_in_of_statement.start_offset); parser_set_breaks_to_current_position (context_p, loop.branch_list_p); parser_set_branch_to_current_position (context_p, &for_in_of_statement.branch); if (context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE || context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT) { parser_pop_block_context (context_p); } continue; } case PARSER_STATEMENT_WITH: { parser_parse_with_statement_end (context_p); continue; } default: { break; } } break; } } parser_stack_pop_uint8 (context_p); context_p->last_statement.current_p = NULL; if (context_p->status_flags & PARSER_IS_CLOSURE) { parser_raise_error (context_p, PARSER_ERR_STATEMENT_EXPECTED); } } /* parser_parse_statements */ /** * Free jumps stored on the stack if a parse error is occurred. */ void JERRY_ATTR_NOINLINE parser_free_jumps (parser_stack_iterator_t iterator) /**< iterator position */ { while (true) { uint8_t type = parser_stack_iterator_read_uint8 (&iterator); parser_branch_node_t *branch_list_p = NULL; switch (type) { case PARSER_STATEMENT_START: { return; } case PARSER_STATEMENT_LABEL: { parser_label_statement_t label; parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &label, sizeof (parser_label_statement_t)); parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); branch_list_p = label.break_list_p; break; } case PARSER_STATEMENT_SWITCH: case PARSER_STATEMENT_SWITCH_NO_DEFAULT: { parser_switch_statement_t switch_statement; parser_loop_statement_t loop; parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t)); parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); parser_stack_iterator_skip (&iterator, sizeof (parser_switch_statement_t)); branch_list_p = switch_statement.branch_list_p; while (branch_list_p != NULL) { parser_branch_node_t *next_p = branch_list_p->next_p; parser_free (branch_list_p, sizeof (parser_branch_node_t)); branch_list_p = next_p; } branch_list_p = loop.branch_list_p; break; } case PARSER_STATEMENT_DO_WHILE: case PARSER_STATEMENT_WHILE: case PARSER_STATEMENT_FOR: case PARSER_STATEMENT_FOR_IN: case PARSER_STATEMENT_FOR_OF: { parser_loop_statement_t loop; parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); parser_stack_iterator_skip (&iterator, parser_statement_length (type) - 1); branch_list_p = loop.branch_list_p; break; } default: { parser_stack_iterator_skip (&iterator, parser_statement_length (type)); continue; } } while (branch_list_p != NULL) { parser_branch_node_t *next_p = branch_list_p->next_p; parser_free (branch_list_p, sizeof (parser_branch_node_t)); branch_list_p = next_p; } } } /* parser_free_jumps */ /** * @} * @} * @} */ #endif /* JERRY_PARSER */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test5.lot000664 001750 001750 00000025362 15164251010 034002 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":30,"ip":0,"op":90,"w":400,"h":400,"nm":"Basic Shapes Tests","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Rectangle Shape","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[75,75,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[60,40]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":5},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Ellipse Shape","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[225,75,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[50,50]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"fl","c":{"a":0,"k":[0,1,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Polystar Shape","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[150,225,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":30},"ir":{"a":0,"k":15},"nm":"Polystar"},{"ty":"fl","c":{"a":0,"k":[0,0,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Custom Path with Stroke","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-40,-40],[40,-40],[40,40],[-40,40]],"c":true}},"nm":"Path"},{"ty":"st","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":5},"lc":2,"lj":2,"ml":4,"d":[{"n":"d","nm":"dash","v":{"a":0,"k":10}},{"n":"g","nm":"gap","v":{"a":0,"k":5}},{"n":"o","nm":"offset","v":{"a":0,"k":0}}],"nm":"Dashed Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.8,0.4,1]},"o":{"a":0,"k":50},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Grouped Shapes","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":60,"s":[360]}]},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40]},"p":{"a":0,"k":[20,0]},"r":{"a":0,"k":5},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[1,0.3,0.3,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}],"nm":"Group 1"},{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[30,30]},"p":{"a":0,"k":[-20,0]},"nm":"Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.3,0.3,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}],"nm":"Group 2"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Expression Driven Position","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[150,150,0],"x":"var t = time; [150 + Math.sin(t * 2) * 50, 150 + Math.cos(t * 2) * 50]"},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[30,30]},"p":{"a":0,"k":[0,0]},"nm":"Circle"},{"ty":"fl","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Expression Driven Rotation","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0,"x":"time * 100"},"p":{"a":0,"k":[150,150,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[60,10]},"p":{"a":0,"k":[30,0]},"r":{"a":0,"k":0},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[0,0,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Expression Driven Scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[150,220,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100],"x":"var s = 100 + Math.sin(time * 3) * 50; [s, s]"}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":1,"pt":{"a":0,"k":5},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":30},"os":{"a":0,"k":0},"ir":{"a":0,"k":15},"is":{"a":0,"k":0},"nm":"Star"},{"ty":"fl","c":{"a":0,"k":[0,1,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Expression with Variables","sr":1,"ks":{"o":{"a":0,"k":100,"x":"var minOpacity = 30; var maxOpacity = 100; var cycle = Math.sin(time * 2); minOpacity + (maxOpacity - minOpacity) * (cycle + 1) / 2"},"r":{"a":0,"k":0},"p":{"a":0,"k":[80,80,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":5},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[1,1,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Star with 5.3 Points","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","sy":1,"pt":{"a":0,"k":5.3},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[50]},"os":{"a":0,"k":[0]},"ir":{"a":0,"k":[25]},"is":{"a":0,"k":[0]},"nm":"Partial Star 5.3"},{"ty":"fl","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Star with 6.7 Points and Roundness","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"p":{"a":0,"k":[300,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","sy":1,"pt":{"a":0,"k":6.7},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[40]},"os":{"a":1,"k":[{"t":0,"s":[0],"e":[50]},{"t":45,"s":[50],"e":[0]},{"t":90,"s":[0]}]},"ir":{"a":0,"k":[20]},"is":{"a":1,"k":[{"t":0,"s":[0],"e":[30]},{"t":45,"s":[30],"e":[0]},{"t":90,"s":[0]}]},"nm":"Partial Star 6.7 with Roundness"},{"ty":"fl","c":{"a":0,"k":[0,1,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Star with 8.25 Points","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,300,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","sy":1,"pt":{"a":1,"k":[{"t":0,"s":[3.5],"e":[8.25]},{"t":45,"s":[8.25],"e":[3.5]},{"t":90,"s":[3.5]}]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[45]},"os":{"a":0,"k":[10]},"ir":{"a":0,"k":[22]},"is":{"a":0,"k":[5]},"nm":"Animated Partial Star"},{"ty":"fl","c":{"a":0,"k":[0,0,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Polygon with 5.5 Points","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[180]},{"t":90,"s":[180]}]},"p":{"a":0,"k":[300,300,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","sy":2,"pt":{"a":0,"k":5.5},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[35]},"os":{"a":0,"k":[20]},"nm":"Partial Polygon 5.5"},{"ty":"st","c":{"a":0,"k":[1,1,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":3},"lc":2,"lj":2,"ml":4,"nm":"Stroke"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Mergeable Group with Non-Uniform Scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","bm":0,"it":[{"ty":"rc","d":1,"s":{"a":0,"k":[60,60]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":5},"nm":"Rectangle"},{"ty":"st","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":10},"lc":2,"lj":2,"ml":4,"nm":"Stroke"},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"t":0,"s":[100,100],"e":[200,100]},{"t":45,"s":[200,100],"e":[100,200]},{"t":90,"s":[100,200]}]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Mergeable Group"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Nested Groups with Transforms","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[300,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","bm":0,"it":[{"ty":"gr","bm":0,"it":[{"ty":"el","d":1,"s":{"a":0,"k":[40,40]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"fl","c":{"a":0,"k":[0,1,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"},{"ty":"tr","p":{"a":0,"k":[15,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,150]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Inner Transform"}],"nm":"Inner Group"},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"t":0,"s":[100,100],"e":[150,80]},{"t":90,"s":[150,80]}]},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[180]},{"t":90,"s":[180]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Outer Transform"}],"nm":"Outer Group"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Group with Skew Transform","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,300,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","bm":0,"it":[{"ty":"rc","d":1,"s":{"a":0,"k":[50,50]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"Rectangle"},{"ty":"st","c":{"a":0,"k":[0,0,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke"},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":1,"k":[{"t":0,"s":[0],"e":[45]},{"t":45,"s":[45],"e":[0]},{"t":90,"s":[0]}]},"sa":{"a":1,"k":[{"t":0,"s":[0],"e":[90]},{"t":45,"s":[90],"e":[0]},{"t":90,"s":[0]}]},"nm":"Skew Transform"}],"nm":"Skewed Group"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Multiple Shapes with Individual Transforms","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[300,300,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","bm":0,"it":[{"ty":"rc","d":1,"s":{"a":0,"k":[30,30]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":5},"nm":"Rect 1"},{"ty":"tr","p":{"a":0,"k":[-20,-20]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[90]},{"t":90,"s":[90]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform 1"}],"nm":"Shape 1"},{"ty":"gr","bm":0,"it":[{"ty":"el","d":1,"s":{"a":0,"k":[25,25]},"p":{"a":0,"k":[0,0]},"nm":"Circle 2"},{"ty":"tr","p":{"a":0,"k":[20,20]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"t":0,"s":[100,100],"e":[150,150]},{"t":90,"s":[150,150]}]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform 2"}],"nm":"Shape 2"},{"ty":"fl","c":{"a":0,"k":[1,1,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0}]}lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-internal-routines-template.inc.h000664 001750 001750 00000032371 15164251010 054112 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 BUILTIN_UNDERSCORED_ID #error "Please, define BUILTIN_UNDERSCORED_ID" #endif /* !BUILTIN_UNDERSCORED_ID */ #ifndef BUILTIN_INC_HEADER_NAME #error "Please, define BUILTIN_INC_HEADER_NAME" #endif /* !BUILTIN_INC_HEADER_NAME */ #include "ecma-objects.h" #define PASTE__(x, y) x##y #define PASTE_(x, y) PASTE__ (x, y) #define PASTE(x, y) PASTE_ (x, y) #define PROPERTY_DESCRIPTOR_LIST_NAME PASTE (PASTE (ecma_builtin_, BUILTIN_UNDERSCORED_ID), _property_descriptor_list) #define DISPATCH_ROUTINE_ROUTINE_NAME PASTE (PASTE (ecma_builtin_, BUILTIN_UNDERSCORED_ID), _dispatch_routine) #ifndef BUILTIN_CUSTOM_DISPATCH #define ROUTINE_ARG(n) , ecma_value_t arg##n #define ROUTINE_ARG_LIST_0 ecma_value_t this_arg #define ROUTINE_ARG_LIST_1 ROUTINE_ARG_LIST_0 ROUTINE_ARG (1) #define ROUTINE_ARG_LIST_2 ROUTINE_ARG_LIST_1 ROUTINE_ARG (2) #define ROUTINE_ARG_LIST_3 ROUTINE_ARG_LIST_2 ROUTINE_ARG (3) #define ROUTINE_ARG_LIST_NON_FIXED ROUTINE_ARG_LIST_0, const ecma_value_t *arguments_list_p, uint32_t arguments_list_len #define ROUTINE(name, c_function_name, args_number, length_prop_value) \ static ecma_value_t c_function_name (ROUTINE_ARG_LIST_##args_number); #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) \ static ecma_value_t c_function_name (ROUTINE_ARG_LIST_##args_number); #define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) \ static ecma_value_t c_function_name (ROUTINE_ARG_LIST_##args_number); #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \ static ecma_value_t c_getter_func_name (ROUTINE_ARG_LIST_0); \ static ecma_value_t c_setter_func_name (ROUTINE_ARG_LIST_1); #define ACCESSOR_READ_ONLY(name, c_getter_func_name, prop_attributes) \ static ecma_value_t c_getter_func_name (ROUTINE_ARG_LIST_0); #include BUILTIN_INC_HEADER_NAME #undef ROUTINE_ARG_LIST_NON_FIXED #undef ROUTINE_ARG_LIST_3 #undef ROUTINE_ARG_LIST_2 #undef ROUTINE_ARG_LIST_1 #undef ROUTINE_ARG_LIST_0 #undef ROUTINE_ARG /** * List of built-in routine identifiers. */ enum { PASTE (ECMA_ROUTINE_START_, BUILTIN_UNDERSCORED_ID) = 0, #define ROUTINE(name, c_function_name, args_number, length_prop_value) ECMA_ROUTINE_##name##c_function_name, #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) \ ECMA_ROUTINE_##name##c_function_name, #define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) \ ECMA_ROUTINE_##name##c_function_name, #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \ ECMA_ACCESSOR_##name##c_getter_func_name, ECMA_ACCESSOR_##name##c_setter_func_name, #define ACCESSOR_READ_ONLY(name, c_getter_func_name, prop_attributes) ECMA_ACCESSOR_##name##c_getter_func_name, #include BUILTIN_INC_HEADER_NAME }; #endif /* !BUILTIN_CUSTOM_DISPATCH */ /** * Built-in property list of the built-in object. */ const ecma_builtin_property_descriptor_t PROPERTY_DESCRIPTOR_LIST_NAME[] = { #ifndef BUILTIN_CUSTOM_DISPATCH #define ROUTINE(name, c_function_name, args_number, length_prop_value) \ { name, \ ECMA_BUILTIN_PROPERTY_ROUTINE, \ ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_WRITABLE, \ ECMA_ROUTINE_VALUE (ECMA_ROUTINE_##name##c_function_name, length_prop_value) }, #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) \ { name, \ ECMA_BUILTIN_PROPERTY_ROUTINE, \ ECMA_PROPERTY_BUILT_IN_CONFIGURABLE, \ ECMA_ROUTINE_VALUE (ECMA_ROUTINE_##name##c_function_name, length_prop_value) }, #define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, prop_attributes) \ { name, \ ECMA_BUILTIN_PROPERTY_ROUTINE, \ (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, \ ECMA_ROUTINE_VALUE (ECMA_ROUTINE_##name##c_function_name, length_prop_value) }, #define ACCESSOR_READ_ONLY(name, c_getter_func_name, prop_attributes) \ { name, \ ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_ONLY, \ (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, \ ECMA_ACCESSOR_##name##c_getter_func_name }, #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \ { name, \ ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_WRITE, \ (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, \ ECMA_ACCESSOR_READ_WRITE (ECMA_ACCESSOR_##name##c_getter_func_name, ECMA_ACCESSOR_##name##c_setter_func_name) }, #else /* BUILTIN_CUSTOM_DISPATCH */ #define ROUTINE(name, c_function_name, args_number, length_prop_value) \ { name, \ ECMA_BUILTIN_PROPERTY_ROUTINE, \ ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_WRITABLE, \ ECMA_ROUTINE_VALUE (c_function_name, length_prop_value) }, #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) \ { name, \ ECMA_BUILTIN_PROPERTY_ROUTINE, \ ECMA_PROPERTY_BUILT_IN_CONFIGURABLE, \ ECMA_ROUTINE_VALUE (c_function_name, length_prop_value) }, #define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, prop_attributes) \ { name, \ ECMA_BUILTIN_PROPERTY_ROUTINE, \ (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, \ ECMA_ROUTINE_VALUE (c_function_name, length_prop_value) }, #define ACCESSOR_READ_ONLY(name, c_getter_func_name, prop_attributes) \ { name, \ ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_ONLY, \ (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, \ c_getter_func_name }, #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \ { name, \ ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_WRITE, \ (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, \ ECMA_ACCESSOR_READ_WRITE (c_getter_func_name, c_setter_func_name) }, #endif /* !BUILTIN_CUSTOM_DISPATCH */ #define OBJECT_VALUE(name, obj_builtin_id, prop_attributes) \ { name, ECMA_BUILTIN_PROPERTY_OBJECT, (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, obj_builtin_id }, #define SIMPLE_VALUE(name, simple_value, prop_attributes) \ { name, ECMA_BUILTIN_PROPERTY_SIMPLE, (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, simple_value }, #define NUMBER_VALUE(name, number_value, prop_attributes) \ { name, ECMA_BUILTIN_PROPERTY_NUMBER, (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, number_value }, #define STRING_VALUE(name, magic_string_id, prop_attributes) \ { name, ECMA_BUILTIN_PROPERTY_STRING, (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, magic_string_id }, #define SYMBOL_VALUE(name, symbol) { name, ECMA_BUILTIN_PROPERTY_SYMBOL, ECMA_PROPERTY_BUILT_IN_FIXED, symbol }, #define INTRINSIC_PROPERTY(name, magic_string_id, prop_attributes) \ { name, ECMA_BUILTIN_PROPERTY_INTRINSIC_PROPERTY, (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, magic_string_id }, #define ACCESSOR_BUILTIN_FUNCTION(name, getter_builtin_id, setter_builtin_id, prop_attributes) \ { name, \ ECMA_BUILTIN_PROPERTY_ACCESSOR_BUILTIN_FUNCTION, \ (prop_attributes) | ECMA_PROPERTY_FLAG_BUILT_IN, \ ECMA_ACCESSOR_READ_WRITE (getter_builtin_id, setter_builtin_id) }, #include BUILTIN_INC_HEADER_NAME { LIT_MAGIC_STRING__COUNT, ECMA_BUILTIN_PROPERTY_END, 0, 0 } }; #ifndef BUILTIN_CUSTOM_DISPATCH /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t DISPATCH_ROUTINE_ROUTINE_NAME (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg_value, /**< 'this' argument value */ const ecma_value_t arguments_list[], /**< list of arguments passed to routine */ uint32_t arguments_number) /**< length of * arguments' list */ { /* the arguments may be unused for some built-ins */ JERRY_UNUSED (this_arg_value); JERRY_UNUSED (arguments_list); JERRY_UNUSED (arguments_number); switch (builtin_routine_id) { #define ROUTINE_ARG(n) (arguments_list[n - 1]) #define ROUTINE_ARG_LIST_0 #define ROUTINE_ARG_LIST_1 , ROUTINE_ARG (1) #define ROUTINE_ARG_LIST_2 ROUTINE_ARG_LIST_1, ROUTINE_ARG (2) #define ROUTINE_ARG_LIST_3 ROUTINE_ARG_LIST_2, ROUTINE_ARG (3) #define ROUTINE_ARG_LIST_NON_FIXED , arguments_list, arguments_number #define ROUTINE(name, c_function_name, args_number, length_prop_value) \ case ECMA_ROUTINE_##name##c_function_name: \ { \ return c_function_name (this_arg_value ROUTINE_ARG_LIST_##args_number); \ } #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) \ case ECMA_ROUTINE_##name##c_function_name: \ { \ return c_function_name (this_arg_value ROUTINE_ARG_LIST_##args_number); \ } #define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) \ case ECMA_ROUTINE_##name##c_function_name: \ { \ return c_function_name (this_arg_value ROUTINE_ARG_LIST_##args_number); \ } #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \ case ECMA_ACCESSOR_##name##c_getter_func_name: \ { \ return c_getter_func_name (this_arg_value); \ } \ case ECMA_ACCESSOR_##name##c_setter_func_name: \ { \ return c_setter_func_name (this_arg_value ROUTINE_ARG_LIST_1); \ } #define ACCESSOR_READ_ONLY(name, c_getter_func_name, prop_attributes) \ case ECMA_ACCESSOR_##name##c_getter_func_name: \ { \ return c_getter_func_name (this_arg_value); \ } #include BUILTIN_INC_HEADER_NAME #undef ROUTINE_ARG #undef ROUTINE_ARG_LIST_0 #undef ROUTINE_ARG_LIST_1 #undef ROUTINE_ARG_LIST_2 #undef ROUTINE_ARG_LIST_3 #undef ROUTINE_ARG_LIST_NON_FIXED default: { JERRY_UNREACHABLE (); } } } /* DISPATCH_ROUTINE_ROUTINE_NAME */ #endif /* !BUILTIN_CUSTOM_DISPATCH */ #undef BUILTIN_INC_HEADER_NAME #undef BUILTIN_CUSTOM_DISPATCH #undef BUILTIN_UNDERSCORED_ID #undef DISPATCH_ROUTINE_ROUTINE_NAME #undef ECMA_BUILTIN_PROPERTY_NAME_INDEX #undef PASTE__ #undef PASTE_ #undef PASTE #undef PROPERTY_DESCRIPTOR_LIST_NAME glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jmem/jmem-heap.cpp000664 001750 001750 00000057237 15164251010 042634 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /** * Heap implementation */ #include "ecma-gc.h" #include "jcontext.h" #include "jmem.h" #include "jrt-bit-fields.h" #include "jrt-libc-includes.h" #define JMEM_ALLOCATOR_INTERNAL #include "jmem-allocator-internal.h" /** \addtogroup mem Memory allocation * @{ * * \addtogroup heap Heap * @{ */ #if !JERRY_SYSTEM_ALLOCATOR /** * End of list marker. */ #define JMEM_HEAP_END_OF_LIST ((uint32_t) 0xffffffff) /** * @{ */ #ifdef ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY /* In this case we simply store the pointer, since it fits anyway. */ #define JMEM_HEAP_GET_OFFSET_FROM_ADDR(p) ((uint32_t) (p)) #define JMEM_HEAP_GET_ADDR_FROM_OFFSET(u) ((jmem_heap_free_t *) (u)) #else /* !ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ /** * Get heap offset from address */ #define JMEM_HEAP_GET_OFFSET_FROM_ADDR(p) ((uint32_t) ((uint8_t *) (p) -JERRY_HEAP_CONTEXT (area))) /** * Get heap address from offset */ #define JMEM_HEAP_GET_ADDR_FROM_OFFSET(u) ((jmem_heap_free_t *) (JERRY_HEAP_CONTEXT (area) + (u))) #endif /* ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ /** * @} */ /** * Get end of region * * @return pointer to the end of the region */ static inline jmem_heap_free_t * JERRY_ATTR_PURE jmem_heap_get_region_end (jmem_heap_free_t *curr_p) /**< current region */ { return (jmem_heap_free_t *) ((uint8_t *) curr_p + curr_p->size); } /* jmem_heap_get_region_end */ #endif /* !JERRY_SYSTEM_ALLOCATOR */ /** * Startup initialization of heap */ void jmem_heap_init (void) { #if !JERRY_SYSTEM_ALLOCATOR JERRY_ASSERT ((uintptr_t) JERRY_HEAP_CONTEXT (area) % JMEM_ALIGNMENT == 0); JERRY_CONTEXT (jmem_heap_limit) = CONFIG_GC_LIMIT; jmem_heap_free_t *const region_p = (jmem_heap_free_t *) JERRY_HEAP_CONTEXT (area); region_p->size = JMEM_HEAP_AREA_SIZE; region_p->next_offset = JMEM_HEAP_END_OF_LIST; JERRY_HEAP_CONTEXT (first).size = 0; JERRY_HEAP_CONTEXT (first).next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (region_p); JERRY_CONTEXT (jmem_heap_list_skip_p) = &JERRY_HEAP_CONTEXT (first); JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t)); JMEM_VALGRIND_NOACCESS_SPACE (JERRY_HEAP_CONTEXT (area), JMEM_HEAP_AREA_SIZE); #endif /* !JERRY_SYSTEM_ALLOCATOR */ JMEM_HEAP_STAT_INIT (); } /* jmem_heap_init */ /** * Finalize heap */ void jmem_heap_finalize (void) { JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_allocated_size) == 0); #if !JERRY_SYSTEM_ALLOCATOR JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), JMEM_HEAP_SIZE); #endif /* !JERRY_SYSTEM_ALLOCATOR */ } /* jmem_heap_finalize */ /** * Allocation of memory region. * * See also: * jmem_heap_alloc_block * * @return pointer to allocated memory block - if allocation is successful, * NULL - if there is not enough memory. */ static void *JERRY_ATTR_HOT jmem_heap_alloc (const size_t size) /**< size of requested block */ { #if !JERRY_SYSTEM_ALLOCATOR /* Align size. */ const size_t required_size = ((size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT) * JMEM_ALIGNMENT; jmem_heap_free_t *data_space_p = NULL; JMEM_VALGRIND_DEFINED_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t)); /* Fast path for 8 byte chunks, first region is guaranteed to be sufficient. */ if (required_size == JMEM_ALIGNMENT && JERRY_LIKELY (JERRY_HEAP_CONTEXT (first).next_offset != JMEM_HEAP_END_OF_LIST)) { data_space_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (JERRY_HEAP_CONTEXT (first).next_offset); JERRY_ASSERT (jmem_is_heap_pointer (data_space_p)); JMEM_VALGRIND_DEFINED_SPACE (data_space_p, sizeof (jmem_heap_free_t)); JERRY_CONTEXT (jmem_heap_allocated_size) += JMEM_ALIGNMENT; if (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit)) { JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT; } if (data_space_p->size == JMEM_ALIGNMENT) { JERRY_HEAP_CONTEXT (first).next_offset = data_space_p->next_offset; } else { JERRY_ASSERT (data_space_p->size > JMEM_ALIGNMENT); jmem_heap_free_t *remaining_p; remaining_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (JERRY_HEAP_CONTEXT (first).next_offset) + 1; JMEM_VALGRIND_DEFINED_SPACE (remaining_p, sizeof (jmem_heap_free_t)); remaining_p->size = data_space_p->size - JMEM_ALIGNMENT; remaining_p->next_offset = data_space_p->next_offset; JMEM_VALGRIND_NOACCESS_SPACE (remaining_p, sizeof (jmem_heap_free_t)); JERRY_HEAP_CONTEXT (first).next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (remaining_p); } JMEM_VALGRIND_NOACCESS_SPACE (data_space_p, sizeof (jmem_heap_free_t)); if (JERRY_UNLIKELY (data_space_p == JERRY_CONTEXT (jmem_heap_list_skip_p))) { JERRY_CONTEXT (jmem_heap_list_skip_p) = JMEM_HEAP_GET_ADDR_FROM_OFFSET (JERRY_HEAP_CONTEXT (first).next_offset); } } /* Slow path for larger regions. */ else { uint32_t current_offset = JERRY_HEAP_CONTEXT (first).next_offset; jmem_heap_free_t *prev_p = &JERRY_HEAP_CONTEXT (first); while (JERRY_LIKELY (current_offset != JMEM_HEAP_END_OF_LIST)) { jmem_heap_free_t *current_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (current_offset); JERRY_ASSERT (jmem_is_heap_pointer (current_p)); JMEM_VALGRIND_DEFINED_SPACE (current_p, sizeof (jmem_heap_free_t)); const uint32_t next_offset = current_p->next_offset; JERRY_ASSERT (next_offset == JMEM_HEAP_END_OF_LIST || jmem_is_heap_pointer (JMEM_HEAP_GET_ADDR_FROM_OFFSET (next_offset))); if (current_p->size >= required_size) { /* Region is sufficiently big, store address. */ data_space_p = current_p; /* Region was larger than necessary. */ if (current_p->size > required_size) { /* Get address of remaining space. */ jmem_heap_free_t *const remaining_p = (jmem_heap_free_t *) ((uint8_t *) current_p + required_size); /* Update metadata. */ JMEM_VALGRIND_DEFINED_SPACE (remaining_p, sizeof (jmem_heap_free_t)); remaining_p->size = current_p->size - (uint32_t) required_size; remaining_p->next_offset = next_offset; JMEM_VALGRIND_NOACCESS_SPACE (remaining_p, sizeof (jmem_heap_free_t)); /* Update list. */ JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t)); prev_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (remaining_p); JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); } /* Block is an exact fit. */ else { /* Remove the region from the list. */ JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t)); prev_p->next_offset = next_offset; JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); } JERRY_CONTEXT (jmem_heap_list_skip_p) = prev_p; /* Found enough space. */ JERRY_CONTEXT (jmem_heap_allocated_size) += required_size; while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit)) { JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT; } break; } JMEM_VALGRIND_NOACCESS_SPACE (current_p, sizeof (jmem_heap_free_t)); /* Next in list. */ prev_p = current_p; current_offset = next_offset; } } JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t)); JERRY_ASSERT ((uintptr_t) data_space_p % JMEM_ALIGNMENT == 0); JMEM_VALGRIND_MALLOCLIKE_SPACE (data_space_p, size); return (void *) data_space_p; #else /* JERRY_SYSTEM_ALLOCATOR */ JERRY_CONTEXT (jmem_heap_allocated_size) += size; while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit)) { JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT; } return malloc (size); #endif /* !JERRY_SYSTEM_ALLOCATOR */ } /* jmem_heap_alloc */ /** * Allocation of memory block, reclaiming memory if the request cannot be fulfilled. * * Note: * Each failed allocation attempt tries to reclaim memory with an increasing pressure, * up to 'max_pressure', or until a sufficient memory block is found. When JMEM_PRESSURE_FULL * is reached, the engine is terminated with JERRY_FATAL_OUT_OF_MEMORY. The `max_pressure` argument * can be used to limit the maximum pressure, and prevent the engine from terminating. * * @return NULL, if the required memory size is 0 or not enough memory * pointer to the allocated memory block, if allocation is successful */ static void * jmem_heap_gc_and_alloc_block (const size_t size, /**< required memory size */ jmem_pressure_t max_pressure) /**< pressure limit */ { if (JERRY_UNLIKELY (size == 0)) { return NULL; } jmem_pressure_t pressure = JMEM_PRESSURE_NONE; if (JERRY_CONTEXT (jmem_heap_allocated_size) + size >= JERRY_CONTEXT (jmem_heap_limit)) { pressure = JMEM_PRESSURE_LOW; ecma_free_unused_memory (pressure); } void *data_space_p = jmem_heap_alloc (size); while (JERRY_UNLIKELY (data_space_p == NULL) && JERRY_LIKELY (pressure < max_pressure)) { auto v = (int)(pressure); pressure = (jmem_pressure_t)(++v); ecma_free_unused_memory (pressure); data_space_p = jmem_heap_alloc (size); } return data_space_p; } /* jmem_heap_gc_and_alloc_block */ /** * Internal method for allocating a memory block. * * @return NULL, if the required memory size is 0 or not enough memory, or * pointer to the allocated memory block, if allocation is successful */ void *JERRY_ATTR_HOT jmem_heap_alloc_block_internal (const size_t size) /**< required memory size */ { return jmem_heap_gc_and_alloc_block (size, JMEM_PRESSURE_FULL); } /* jmem_heap_alloc_block_internal */ /** * Allocation of memory block, reclaiming unused memory if there is not enough. * * Note: * If a sufficiently sized block can't be found, the engine will be terminated with JERRY_FATAL_OUT_OF_MEMORY. * * @return NULL, if the required memory is 0 * pointer to allocated memory block, otherwise */ void *JERRY_ATTR_HOT jmem_heap_alloc_block (const size_t size) /**< required memory size */ { void *block_p = jmem_heap_gc_and_alloc_block (size, JMEM_PRESSURE_FULL); JMEM_HEAP_STAT_ALLOC (size); return block_p; } /* jmem_heap_alloc_block */ /** * Allocation of memory block, reclaiming unused memory if there is not enough. * * Note: * If a sufficiently sized block can't be found, NULL will be returned. * * @return NULL, if the required memory size is 0 * also NULL, if the allocation has failed * pointer to the allocated memory block, otherwise */ void *JERRY_ATTR_HOT jmem_heap_alloc_block_null_on_error (const size_t size) /**< required memory size */ { void *block_p = jmem_heap_gc_and_alloc_block (size, JMEM_PRESSURE_HIGH); return block_p; } /* jmem_heap_alloc_block_null_on_error */ #if !JERRY_SYSTEM_ALLOCATOR /** * Finds the block in the free block list which precedes the argument block * * @return pointer to the preceding block */ static jmem_heap_free_t * jmem_heap_find_prev (const jmem_heap_free_t *const block_p) /**< which memory block's predecessor we're looking for */ { const jmem_heap_free_t *prev_p; if (block_p > JERRY_CONTEXT (jmem_heap_list_skip_p)) { prev_p = JERRY_CONTEXT (jmem_heap_list_skip_p); } else { prev_p = &JERRY_HEAP_CONTEXT (first); } JERRY_ASSERT (jmem_is_heap_pointer (block_p)); const uint32_t block_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (block_p); JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t)); /* Find position of region in the list. */ while (prev_p->next_offset < block_offset) { const jmem_heap_free_t *const next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset); JERRY_ASSERT (jmem_is_heap_pointer (next_p)); JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t)); JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); prev_p = next_p; } JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); return (jmem_heap_free_t *) prev_p; } /* jmem_heap_find_prev */ /** * Inserts the block into the free chain after a specified block. * * Note: * 'jmem_heap_find_prev' can and should be used to find the previous free block */ static void jmem_heap_insert_block (jmem_heap_free_t *block_p, /**< block to insert */ jmem_heap_free_t *prev_p, /**< the free block after which to insert 'block_p' */ const size_t size) /**< size of the inserted block */ { JERRY_ASSERT ((uintptr_t) block_p % JMEM_ALIGNMENT == 0); JERRY_ASSERT (size % JMEM_ALIGNMENT == 0); JMEM_VALGRIND_NOACCESS_SPACE (block_p, size); JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t)); jmem_heap_free_t *next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset); JMEM_VALGRIND_DEFINED_SPACE (block_p, sizeof (jmem_heap_free_t)); JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t)); const uint32_t block_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (block_p); /* Update prev. */ if (jmem_heap_get_region_end (prev_p) == block_p) { /* Can be merged. */ prev_p->size += (uint32_t) size; JMEM_VALGRIND_NOACCESS_SPACE (block_p, sizeof (jmem_heap_free_t)); block_p = prev_p; } else { block_p->size = (uint32_t) size; prev_p->next_offset = block_offset; } /* Update next. */ if (jmem_heap_get_region_end (block_p) == next_p) { /* Can be merged. */ block_p->size += next_p->size; block_p->next_offset = next_p->next_offset; } else { block_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (next_p); } JERRY_CONTEXT (jmem_heap_list_skip_p) = prev_p; JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); JMEM_VALGRIND_NOACCESS_SPACE (block_p, sizeof (jmem_heap_free_t)); JMEM_VALGRIND_NOACCESS_SPACE (next_p, sizeof (jmem_heap_free_t)); } /* jmem_heap_insert_block */ #endif /* !JERRY_SYSTEM_ALLOCATOR */ /** * Internal method for freeing a memory block. * * @return void */ void JERRY_ATTR_HOT jmem_heap_free_block_internal (void *ptr, /**< pointer to beginning of data space of the block */ const size_t size /**< size of allocated region */) { JERRY_ASSERT (size > 0); JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_limit) >= JERRY_CONTEXT (jmem_heap_allocated_size)); JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_allocated_size) > 0); #if !JERRY_SYSTEM_ALLOCATOR /* checking that ptr points to the heap */ JERRY_ASSERT (jmem_is_heap_pointer (ptr)); JERRY_ASSERT ((uintptr_t) ptr % JMEM_ALIGNMENT == 0); const size_t aligned_size = (size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT; jmem_heap_free_t *const block_p = (jmem_heap_free_t *) ptr; jmem_heap_free_t *const prev_p = jmem_heap_find_prev (block_p); jmem_heap_insert_block (block_p, prev_p, aligned_size); JERRY_CONTEXT (jmem_heap_allocated_size) -= aligned_size; JMEM_VALGRIND_FREELIKE_SPACE (ptr); #else /* JERRY_SYSTEM_ALLOCATOR */ JERRY_CONTEXT (jmem_heap_allocated_size) -= size; free (ptr); #endif /* !JERRY_SYSTEM_ALLOCATOR */ while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit)) { JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT; } JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_limit) >= JERRY_CONTEXT (jmem_heap_allocated_size)); } /* jmem_heap_free_block_internal */ /** * Reallocates the memory region pointed to by 'ptr', changing the size of the allocated region. * * @return pointer to the reallocated region */ void *JERRY_ATTR_HOT jmem_heap_realloc_block (void *ptr, /**< memory region to reallocate */ const size_t old_size, /**< current size of the region */ const size_t new_size) /**< desired new size */ { #if !JERRY_SYSTEM_ALLOCATOR JERRY_ASSERT (jmem_is_heap_pointer (ptr)); JERRY_ASSERT ((uintptr_t) ptr % JMEM_ALIGNMENT == 0); JERRY_ASSERT (old_size != 0); JERRY_ASSERT (new_size != 0); jmem_heap_free_t *const block_p = (jmem_heap_free_t *) ptr; const size_t aligned_new_size = (new_size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT; const size_t aligned_old_size = (old_size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT; if (aligned_old_size == aligned_new_size) { JMEM_VALGRIND_RESIZE_SPACE (block_p, old_size, new_size); JMEM_HEAP_STAT_FREE (old_size); JMEM_HEAP_STAT_ALLOC (new_size); return block_p; } if (aligned_new_size < aligned_old_size) { JMEM_VALGRIND_RESIZE_SPACE (block_p, old_size, new_size); JMEM_HEAP_STAT_FREE (old_size); JMEM_HEAP_STAT_ALLOC (new_size); jmem_heap_insert_block ((jmem_heap_free_t *) ((uint8_t *) block_p + aligned_new_size), jmem_heap_find_prev (block_p), aligned_old_size - aligned_new_size); JERRY_CONTEXT (jmem_heap_allocated_size) -= (aligned_old_size - aligned_new_size); while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit)) { JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT; } return block_p; } void *ret_block_p = NULL; const size_t required_size = aligned_new_size - aligned_old_size; if (JERRY_CONTEXT (jmem_heap_allocated_size) + required_size >= JERRY_CONTEXT (jmem_heap_limit)) { ecma_free_unused_memory (JMEM_PRESSURE_LOW); } jmem_heap_free_t *prev_p = jmem_heap_find_prev (block_p); JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t)); jmem_heap_free_t *const next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset); /* Check if block can be extended at the end */ if (((jmem_heap_free_t *) ((uint8_t *) block_p + aligned_old_size)) == next_p) { JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t)); if (required_size <= next_p->size) { /* Block can be extended, update the list. */ if (required_size == next_p->size) { prev_p->next_offset = next_p->next_offset; } else { jmem_heap_free_t *const new_next_p = (jmem_heap_free_t *) ((uint8_t *) next_p + required_size); JMEM_VALGRIND_DEFINED_SPACE (new_next_p, sizeof (jmem_heap_free_t)); new_next_p->next_offset = next_p->next_offset; new_next_p->size = (uint32_t) (next_p->size - required_size); JMEM_VALGRIND_NOACCESS_SPACE (new_next_p, sizeof (jmem_heap_free_t)); prev_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (new_next_p); } /* next_p will be marked as undefined space. */ JMEM_VALGRIND_RESIZE_SPACE (block_p, old_size, new_size); ret_block_p = block_p; } else { JMEM_VALGRIND_NOACCESS_SPACE (next_p, sizeof (jmem_heap_free_t)); } JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); } /* * Check if block can be extended at the front. * This is less optimal because we need to copy the data, but still better than allocating a new block. */ else if (jmem_heap_get_region_end (prev_p) == block_p) { if (required_size <= prev_p->size) { if (required_size == prev_p->size) { JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); prev_p = jmem_heap_find_prev (prev_p); JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t)); prev_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (next_p); } else { prev_p->size = (uint32_t) (prev_p->size - required_size); } JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); ret_block_p = (uint8_t *) block_p - required_size; /* Mark the new block as undefined so that we are able to write to it. */ JMEM_VALGRIND_UNDEFINED_SPACE (ret_block_p, old_size); /* The blocks are likely to overlap, so mark the old block as defined memory again. */ JMEM_VALGRIND_DEFINED_SPACE (block_p, old_size); memmove (ret_block_p, block_p, old_size); JMEM_VALGRIND_FREELIKE_SPACE (block_p); JMEM_VALGRIND_MALLOCLIKE_SPACE (ret_block_p, new_size); JMEM_VALGRIND_DEFINED_SPACE (ret_block_p, old_size); } else { JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t)); } } if (ret_block_p != NULL) { /* Managed to extend the block. Update memory usage and the skip pointer. */ JERRY_CONTEXT (jmem_heap_list_skip_p) = prev_p; JERRY_CONTEXT (jmem_heap_allocated_size) += required_size; while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit)) { JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT; } } else { /* Could not extend block. Allocate new region and copy the data. */ /* jmem_heap_alloc_block_internal will adjust the allocated_size, but insert_block will not, so we reduce it here first, so that the limit calculation remains consistent. */ JERRY_CONTEXT (jmem_heap_allocated_size) -= aligned_old_size; ret_block_p = jmem_heap_alloc_block_internal (new_size); /* jmem_heap_alloc_block_internal may trigger garbage collection, which can create new free blocks * in the heap structure, so we need to look up the previous block again. */ prev_p = jmem_heap_find_prev (block_p); memcpy (ret_block_p, block_p, old_size); jmem_heap_insert_block (block_p, prev_p, aligned_old_size); /* jmem_heap_alloc_block_internal will call JMEM_VALGRIND_MALLOCLIKE_SPACE */ JMEM_VALGRIND_FREELIKE_SPACE (block_p); } JMEM_HEAP_STAT_FREE (old_size); JMEM_HEAP_STAT_ALLOC (new_size); return ret_block_p; #else /* JERRY_SYSTEM_ALLOCATOR */ const size_t required_size = new_size - old_size; if (JERRY_CONTEXT (jmem_heap_allocated_size) + required_size >= JERRY_CONTEXT (jmem_heap_limit)) { ecma_free_unused_memory (JMEM_PRESSURE_LOW); } JERRY_CONTEXT (jmem_heap_allocated_size) += required_size; while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit)) { JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT; } while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit)) { JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT; } JMEM_HEAP_STAT_FREE (old_size); JMEM_HEAP_STAT_ALLOC (new_size); return realloc (ptr, new_size); #endif /* !JERRY_SYSTEM_ALLOCATOR */ } /* jmem_heap_realloc_block */ /** * Free memory block * * @return void */ void JERRY_ATTR_HOT jmem_heap_free_block (void *ptr, /**< pointer to beginning of data space of the block */ const size_t size) /**< size of allocated region */ { jmem_heap_free_block_internal (ptr, size); JMEM_HEAP_STAT_FREE (size); return; } /* jmem_heap_free_block */ #ifndef JERRY_NDEBUG /** * Check whether the pointer points to the heap * * Note: * the routine should be used only for assertion checks * * @return true - if pointer points to the heap, * false - otherwise */ bool jmem_is_heap_pointer (const void *pointer) /**< pointer */ { #if !JERRY_SYSTEM_ALLOCATOR return ((uint8_t *) pointer >= JERRY_HEAP_CONTEXT (area) && (uint8_t *) pointer <= (JERRY_HEAP_CONTEXT (area) + JMEM_HEAP_AREA_SIZE)); #else /* JERRY_SYSTEM_ALLOCATOR */ JERRY_UNUSED (pointer); return true; #endif /* !JERRY_SYSTEM_ALLOCATOR */ } /* jmem_is_heap_pointer */ #endif /* !JERRY_NDEBUG */ /** * @} * @} */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-aggregateerror-prototype.cpp000664 001750 001750 00000002202 15164251010 053433 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-aggregateerror-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID aggregate_error_prototype #include "ecma-builtin-internal-routines-template.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgFill.cpp000664 001750 001750 00000007344 15164251010 033735 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgFill.h" /************************************************************************/ /* Fill Class Implementation */ /************************************************************************/ Fill::Fill() = default; Fill::~Fill() = default; Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept { return pImpl->update(colorStops, cnt); } uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept { if (colorStops) *colorStops = pImpl->colorStops; return pImpl->cnt; } Result Fill::spread(FillSpread s) noexcept { pImpl->spread = s; return Result::Success; } FillSpread Fill::spread() const noexcept { return pImpl->spread; } Result Fill::transform(const Matrix& m) noexcept { pImpl->transform = m; return Result::Success; } Matrix& Fill::transform() const noexcept { return pImpl->transform; } Fill* Fill::duplicate() const noexcept { if (type() == Type::LinearGradient) return CONST_LINEAR(this)->duplicate(); else if (type() == Type::RadialGradient) return CONST_RADIAL(this)->duplicate(); return nullptr; } /************************************************************************/ /* RadialGradient Class Implementation */ /************************************************************************/ RadialGradient::RadialGradient() = default; Result RadialGradient::radial(float cx, float cy, float r, float fx, float fy, float fr) noexcept { return RADIAL(this)->radial(cx, cy, r, fx, fy, fr); } Result RadialGradient::radial(float* cx, float* cy, float* r, float* fx, float* fy, float* fr) const noexcept { return CONST_RADIAL(this)->radial(cx, cy, r, fx, fy, fr); } RadialGradient* RadialGradient::gen() noexcept { return new RadialGradientImpl; } Type RadialGradient::type() const noexcept { return Type::RadialGradient; } /************************************************************************/ /* LinearGradient Class Implementation */ /************************************************************************/ LinearGradient::LinearGradient() = default; Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept { return LINEAR(this)->linear(x1, y1, x2, y2); } Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept { return CONST_LINEAR(this)->linear(x1, y1, x2, y2); } LinearGradient* LinearGradient::gen() noexcept { return new LinearGradientImpl; } Type LinearGradient::type() const noexcept { return Type::LinearGradient; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testLottie.cpp000664 001750 001750 00000027632 15164251010 033052 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2024 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include #ifdef THORVG_LOTTIE_LOADER_SUPPORT #include #endif #include #include #include "catch.hpp" using namespace tvg; using namespace std; #ifdef THORVG_LOTTIE_LOADER_SUPPORT TEST_CASE("Lottie Coverages", "[tvgLottie]") { REQUIRE(Initializer::init() == Result::Success); { #define TEST_CNT 10 const char* names[TEST_CNT] = { "test3.lot", "test4.lot", "test5.lot", "test6.lot", "test7.lot", "test8.lot", "test9.lot", "test10.lot", "test11.lot", "test12.lot" }; auto animation = unique_ptr(Animation::gen()); REQUIRE(animation); auto picture = animation->picture(); for (int i = 0; i < TEST_CNT; ++i) { char buf[100]; snprintf(buf, sizeof(buf), TEST_DIR"/%s", names[i]); REQUIRE(picture->load(buf) == Result::Success); REQUIRE(animation->frame(0.0f) == Result::InsufficientCondition); REQUIRE(animation->frame(animation->totalFrame() * 0.5f) == Result::Success); REQUIRE(animation->frame(animation->totalFrame()) == Result::Success); } } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Lottie Slot", "[tvgLottie]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = unique_ptr(LottieAnimation::gen()); REQUIRE(animation); auto picture = animation->picture(); //Slot Test 1 const char* slotJson = R"({"gradient_fill":{"p":{"p":2,"k":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}}})"; //Negative: slot generation before loaded REQUIRE(animation->gen(slotJson) == 0); REQUIRE(picture->load(TEST_DIR"/slot.lot") == Result::Success); auto id = animation->gen(slotJson); REQUIRE(id > 0); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->apply(id) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->apply(id) == Result::Success); REQUIRE(animation->gen("") == 0); REQUIRE(animation->del(id) == Result::Success); //Slot Test 2 const char* slotJson2 = R"({"lottie-icon-outline":{"p":{"a":0,"k":[1,1,0]}},"lottie-icon-solid":{"p":{"a":0,"k":[0,0,1]}}})"; auto id2 = animation->gen(slotJson2); REQUIRE(id2 > 0); REQUIRE(animation->apply(id2) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->apply(id2) == Result::Success); REQUIRE(animation->del(id2) == Result::Success); //Slot Test 3 (Transform) const char* positionSlot = R"({"transform_id":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[100,100],"t":0},{"s":[200,300],"t":100}]}}})"; auto id3 = animation->gen(positionSlot); REQUIRE(id3 > 0); REQUIRE(animation->apply(id3) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->del(id3) == Result::Success); const char* scaleSlot = R"({"transform_id":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[0,0],"t":0},{"s":[100,100],"t":100}]}}})"; auto id4 = animation->gen(scaleSlot); REQUIRE(id4 > 0); REQUIRE(animation->apply(id4) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->del(id4) == Result::Success); const char* rotationSlot = R"({"transform_id":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[0],"t":0},{"s":[180],"t":100}]}}})"; auto id5 = animation->gen(rotationSlot); REQUIRE(id5 > 0); REQUIRE(animation->apply(id5) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->del(id5) == Result::Success); const char* opacitySlot = R"({"transform_id":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[0],"t":0},{"s":[100],"t":100}]}}})"; auto id6 = animation->gen(opacitySlot); REQUIRE(id6 > 0); REQUIRE(animation->apply(id6) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->del(id6) == Result::Success); //Slot Test 4: Expression const char* expressionSlot = R"({"rect_rotation":{"p":{"x":"var $bm_rt = time * 360;"}},"rect_scale":{"p":{"x":"var $bm_rt = [];$bm_rt[0] = value[0] + Math.cos(2 * Math.PI * time) * 100;$bm_rt[1] = value[1];"}},"rect_position":{"p":{"x":"var $bm_rt = [];$bm_rt[0] = value[0] + Math.cos(2 * Math.PI * time) * 100;$bm_rt[1] = value[1];"}}})"; auto id7 = animation->gen(expressionSlot); REQUIRE(id7 > 0); REQUIRE(animation->apply(id7) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->del(id7) == Result::Success); //Slot Test 5: Text const char* textSlot = R"({"text_doc":{"p":{"k":[{"s":{"f":"Ubuntu Light Italic","t":"ThorVG!","j":0,"s":48,"fc":[1,1,1]},"t":0}]}}})"; auto id8 = animation->gen(textSlot); REQUIRE(id8 > 0); REQUIRE(animation->apply(id8) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->del(id8) == Result::Success); //Slot Test 6: Image const char* imageSlot = R"({"path_img":{"p":{"id":"image_0","w":200,"h":300,"u":"images/","p":"logo.png","e":0}}})"; auto id9 = animation->gen(imageSlot); REQUIRE(id9 > 0); REQUIRE(animation->apply(id9) == Result::Success); REQUIRE(animation->apply(0) == Result::Success); REQUIRE(animation->del(id9) == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Lottie Marker", "[tvgLottie]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = unique_ptr(LottieAnimation::gen()); REQUIRE(animation); auto picture = animation->picture(); //Set marker name before loaded REQUIRE(animation->segment("sectionC") == Result::InsufficientCondition); //Animation load REQUIRE(picture->load(TEST_DIR"/segment.lot") == Result::Success); //Set marker REQUIRE(animation->segment("sectionA") == Result::Success); //Set marker by invalid name REQUIRE(animation->segment("") == Result::InvalidArguments); //Get marker count REQUIRE(animation->markersCnt() == 3); //Get marker name by index REQUIRE(!strcmp(animation->marker(1), "sectionB")); //Get marker name by invalid index REQUIRE(animation->marker(-1) == nullptr); REQUIRE(animation->segment(nullptr) == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Lottie Tween", "[tvgLottie]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = unique_ptr(LottieAnimation::gen()); REQUIRE(animation); auto picture = animation->picture(); REQUIRE(animation->tween(0.0f, 10.0f, 0.5f) == Result::InsufficientCondition); REQUIRE(picture->load(TEST_DIR"/test.lot") == Result::Success); //Set initial frame to avoid frame difference being too small REQUIRE(animation->frame(5.0f) == Result::Success); //Tween between frames with different progress values REQUIRE(animation->tween(0.0f, 10.0f, 0.5f) == Result::Success); REQUIRE(animation->tween(10.0f, 20.0f, 0.0f) == Result::Success); REQUIRE(animation->tween(20.0f, 30.0f, 1.0f) == Result::Success); //Tween with different frame ranges REQUIRE(animation->tween(10.0f, 50.0f, 0.25f) == Result::Success); REQUIRE(animation->tween(50.0f, 100.0f, 0.75f) == Result::Success); //Tween between distant frames REQUIRE(animation->tween(0.0f, 100.0f, 0.5f) == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Lottie Quality", "[tvgLottie]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = unique_ptr(LottieAnimation::gen()); REQUIRE(animation); auto picture = animation->picture(); REQUIRE(animation->quality(50) == Result::InsufficientCondition); REQUIRE(picture->load(TEST_DIR"/test.lot") == Result::Success); //Set quality with minimum value REQUIRE(animation->quality(0) == Result::Success); //Set quality with default value REQUIRE(animation->quality(50) == Result::Success); //Set quality with maximum value REQUIRE(animation->quality(100) == Result::Success); //Set quality with various values REQUIRE(animation->quality(25) == Result::Success); REQUIRE(animation->quality(75) == Result::Success); //Set quality with invalid value (> 100) REQUIRE(animation->quality(101) == Result::InvalidArguments); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Lottie Asset Resolver", "[tvgLottie]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = unique_ptr(LottieAnimation::gen()); REQUIRE(animation); auto picture = animation->picture(); auto resolver = [](Paint* p, const char* src, void* data) -> bool { if (p->type() == Type::Picture) { string resolvedPath = string(TEST_DIR) + "/image/test.png"; auto ret = static_cast(p)->load(resolvedPath.c_str()); return (ret == Result::Success); } else if (p->type() == Type::Text) { string fontPath = string(TEST_DIR) + "/font/Arial.ttf"; if (Text::load(fontPath.c_str()) != Result::Success) return false; auto ret = static_cast(p)->font("Arial"); return (ret == Result::Success); } return false; }; // Test unset resolver REQUIRE(picture->resolver(resolver, nullptr) == Result::Success); REQUIRE(picture->resolver(nullptr, nullptr) == Result::Success); //Resolver Test (Image and Font) REQUIRE(picture->resolver(resolver, nullptr) == Result::Success); REQUIRE(picture->load(TEST_DIR"/resolver.json") == Result::Success); REQUIRE(animation->frame(animation->totalFrame() * 0.5f) == Result::Success); //Test that setting/unsetting resolver after load REQUIRE(picture->resolver(resolver, nullptr) == Result::InsufficientCondition); REQUIRE(picture->resolver(nullptr, nullptr) == Result::InsufficientCondition); } REQUIRE(Initializer::term() == Result::Success); } #endifsrc/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp000664 001750 001750 00000205664 15164251010 036525 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgRender.h" #include "tvgSwCommon.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ constexpr auto DOWN_SCALE_TOLERANCE = 0.5f; struct FillLinear { void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a) { fillLinear(fill, dst, y, x, len, op, a); } void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) { fillLinear(fill, dst, y, x, len, cmp, op, a); } void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, uint8_t a) { fillLinear(fill, dst, y, x, len, op, a); } void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity) { fillLinear(fill, dst, y, x, len, cmp, alpha, csize, opacity); } void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, SwBlender op2, uint8_t a) { fillLinear(fill, dst, y, x, len, op, op2, a); } }; struct FillRadial { void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a) { fillRadial(fill, dst, y, x, len, op, a); } void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) { fillRadial(fill, dst, y, x, len, cmp, op, a); } void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, uint8_t a) { fillRadial(fill, dst, y, x, len, op, a); } void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity) { fillRadial(fill, dst, y, x, len, cmp, alpha, csize, opacity); } void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, SwBlender op2, uint8_t a) { fillRadial(fill, dst, y, x, len, op, op2, a); } }; static inline uint8_t _alpha(uint8_t* a) { return *a; } static inline uint8_t _ialpha(uint8_t* a) { return ~(*a); } static inline uint8_t _abgrLuma(uint8_t* c) { auto v = *(uint32_t*)c; return ((((v&0xff)*54) + (((v>>8)&0xff)*182) + (((v>>16)&0xff)*19))) >> 8; //0.2126*R + 0.7152*G + 0.0722*B } static inline uint8_t _argbLuma(uint8_t* c) { auto v = *(uint32_t*)c; return ((((v&0xff)*19) + (((v>>8)&0xff)*182) + (((v>>16)&0xff)*54))) >> 8; //0.0722*B + 0.7152*G + 0.2126*R } static inline uint8_t _abgrInvLuma(uint8_t* c) { return ~_abgrLuma(c); } static inline uint8_t _argbInvLuma(uint8_t* c) { return ~_argbLuma(c); } static inline uint32_t _abgrJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return (a << 24 | b << 16 | g << 8 | r); } static inline uint32_t _argbJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return (a << 24 | r << 16 | g << 8 | b); } static inline bool _blending(const SwSurface* surface) { return (surface->blender) ? true : false; } /* OPTIMIZE_ME: Probably, we can separate masking(8bits) / composition(32bits) This would help to enhance the performance by avoiding the unnecessary matting from the composition */ static inline bool _compositing(const SwSurface* surface) { if (!surface->compositor || surface->compositor->method == MaskMethod::None) return false; return true; } static inline bool _matting(const SwSurface* surface) { if ((int)surface->compositor->method < (int)MaskMethod::Add) return true; else return false; } static inline uint8_t _opMaskNone(uint8_t s, TVG_UNUSED uint8_t d, TVG_UNUSED uint8_t a) { return s; } static inline uint8_t _opMaskAdd(uint8_t s, uint8_t d, uint8_t a) { return s + MULTIPLY(d, a); } static inline uint8_t _opMaskSubtract(uint8_t s, uint8_t d, TVG_UNUSED uint8_t a) { return MULTIPLY(s, 255 - d); } static inline uint8_t _opMaskIntersect(uint8_t s, uint8_t d, TVG_UNUSED uint8_t a) { return MULTIPLY(s, d); } static inline uint8_t _opMaskDifference(uint8_t s, uint8_t d, uint8_t a) { return MULTIPLY(s, 255 - d) + MULTIPLY(d, a); } static inline uint8_t _opMaskLighten(uint8_t s, uint8_t d, uint8_t a) { return (s > d) ? s : d; } static inline uint8_t _opMaskDarken(uint8_t s, uint8_t d, uint8_t a) { return (s < d) ? s : d; } static inline bool _direct(MaskMethod method) { if (method == MaskMethod::Subtract || method == MaskMethod::Intersect || method == MaskMethod::Darken) return true; return false; } static inline SwMask _getMaskOp(MaskMethod method) { switch (method) { case MaskMethod::Add: return _opMaskAdd; case MaskMethod::Subtract: return _opMaskSubtract; case MaskMethod::Difference: return _opMaskDifference; case MaskMethod::Intersect: return _opMaskIntersect; case MaskMethod::Lighten: return _opMaskLighten; case MaskMethod::Darken: return _opMaskDarken; default: return nullptr; } } static bool _compositeMaskImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox) { auto dbuffer = &surface->buf8[bbox.min.y * surface->stride + bbox.min.x]; auto sbuffer = image.buf8 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox); for (auto y = bbox.min.y; y < bbox.max.y; ++y) { auto dst = dbuffer; auto src = sbuffer; for (auto x = bbox.min.x; x < bbox.max.x; x++, dst++, src++) { *dst = *src + MULTIPLY(*dst, ~*src); } dbuffer += surface->stride; sbuffer += image.stride; } return true; } #include "tvgSwRasterTexmap.h" #include "tvgSwRasterC.h" #include "tvgSwRasterAvx.h" #include "tvgSwRasterNeon.h" static inline uint32_t _sampleSize(float scale) { auto sampleSize = static_cast(0.5f / scale); if (sampleSize == 0) sampleSize = 1; return sampleSize; } //Bilinear Interpolation //OPTIMIZE_ME: Skip the function pointer access static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, TVG_UNUSED int32_t miny, TVG_UNUSED int32_t maxy, TVG_UNUSED int32_t n) { auto rx = (size_t)(sx); auto ry = (size_t)(sy); auto rx2 = rx + 1; if (rx2 >= w) rx2 = w - 1; auto ry2 = ry + 1; if (ry2 >= h) ry2 = h - 1; auto dx = (sx > 0.0f) ? static_cast((sx - rx) * 255.0f) : 0; auto dy = (sy > 0.0f) ? static_cast((sy - ry) * 255.0f) : 0; auto c1 = img[rx + ry * w]; auto c2 = img[rx2 + ry * w]; auto c3 = img[rx + ry2 * w]; auto c4 = img[rx2 + ry2 * w]; return INTERPOLATE(INTERPOLATE(c4, c3, dx), INTERPOLATE(c2, c1, dx), dy); } //2n x 2n Mean Kernel //OPTIMIZE_ME: Skip the function pointer access static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, float sx, TVG_UNUSED float sy, int32_t miny, int32_t maxy, int32_t n) { size_t c[4] = {0, 0, 0, 0}; int32_t minx = (int32_t)sx - n; if (minx < 0) minx = 0; int32_t maxx = (int32_t)sx + n; if (maxx >= (int32_t)w) maxx = w; int32_t inc = (n / 2) + 1; n = 0; auto src = img + minx + miny * stride; for (auto y = miny; y < maxy; y += inc) { auto p = src; for (auto x = minx; x < maxx; x += inc, p += inc) { c[0] += A(*p); c[1] += C1(*p); c[2] += C2(*p); c[3] += C3(*p); ++n; } src += (stride * inc); } c[0] /= n; c[1] /= n; c[2] /= n; c[3] /= n; return (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; } /************************************************************************/ /* Rect */ /************************************************************************/ static bool _rasterCompositeMaskedRect(SwSurface* surface, const RenderRegion& bbox, SwMask maskOp, uint8_t a) { auto cstride = surface->compositor->image.stride; auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * cstride + bbox.min.x); //compositor buffer auto ialpha = 255 - a; for (uint32_t y = 0; y < bbox.h(); ++y) { auto cmp = cbuffer; for (uint32_t x = 0; x < bbox.w(); ++x, ++cmp) { *cmp = maskOp(a, *cmp, ialpha); } cbuffer += cstride; } return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox); } static bool _rasterDirectMaskedRect(SwSurface* surface, const RenderRegion& bbox, SwMask maskOp, uint8_t a) { auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x); //compositor buffer auto dbuffer = surface->buf8 + (bbox.min.y * surface->stride + bbox.min.x); //destination buffer for (uint32_t y = 0; y < bbox.h(); ++y) { auto cmp = cbuffer; auto dst = dbuffer; for (uint32_t x = 0; x < bbox.w(); ++x, ++cmp, ++dst) { auto tmp = maskOp(a, *cmp, 0); //not use alpha. *dst = tmp + MULTIPLY(*dst, ~tmp); } cbuffer += surface->compositor->image.stride; dbuffer += surface->stride; } return true; } static bool _rasterMaskedRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { //8bit masking channels composition if (surface->channelSize != sizeof(uint8_t)) return false; TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %d %d %d %d]", (int)surface->compositor->method, bbox.min.x, bbox.min.y, bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y); auto maskOp = _getMaskOp(surface->compositor->method); if (_direct(surface->compositor->method)) return _rasterDirectMaskedRect(surface, bbox, maskOp, c.a); else return _rasterCompositeMaskedRect(surface, bbox, maskOp, c.a); return false; } static bool _rasterMattedRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8 + ((bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize); //compositor buffer auto alpha = surface->alpha(surface->compositor->method); TVGLOG("SW_ENGINE", "Matted(%d) Rect [Region: %u %u %u %u]", (int)surface->compositor->method, bbox.x(), bbox.y(), bbox.w(), bbox.h()); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; for (uint32_t y = 0; y < bbox.h(); ++y) { auto dst = &buffer[y * surface->stride]; auto cmp = &cbuffer[y * surface->compositor->image.stride * csize]; for (uint32_t x = 0; x < bbox.w(); ++x, ++dst, cmp += csize) { auto tmp = ALPHA_BLEND(color, alpha(cmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } //8bits grayscale } else if (surface->channelSize == sizeof(uint8_t)) { auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x; for (uint32_t y = 0; y < bbox.h(); ++y) { auto dst = &buffer[y * surface->stride]; auto cmp = &cbuffer[y * surface->compositor->image.stride * csize]; for (uint32_t x = 0; x < bbox.w(); ++x, ++dst, cmp += csize) { *dst = INTERPOLATE8(c.a, *dst, alpha(cmp)); } } } return true; } static bool _rasterBlendingRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { if (surface->channelSize != sizeof(uint32_t)) return false; auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; for (uint32_t y = 0; y < bbox.h(); ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) { *dst = surface->blender(color, *dst); } } return true; } static bool _rasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { #if defined(THORVG_AVX_VECTOR_SUPPORT) return avxRasterTranslucentRect(surface, bbox, c); #elif defined(THORVG_NEON_VECTOR_SUPPORT) return neonRasterTranslucentRect(surface, bbox, c); #else return cRasterTranslucentRect(surface, bbox, c); #endif } static bool _rasterSolidRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, 255); auto buffer = surface->buf32 + (bbox.min.y * surface->stride); for (uint32_t y = 0; y < bbox.h(); ++y) { rasterPixel32(buffer + y * surface->stride, color, bbox.min.x, bbox.w()); } return true; } //8bits grayscale if (surface->channelSize == sizeof(uint8_t)) { for (uint32_t y = 0; y < bbox.h(); ++y) { rasterGrayscale8(surface->buf8, 255, (y + bbox.min.y) * surface->stride + bbox.min.x, bbox.w()); } return true; } return false; } static bool _rasterRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { if (_compositing(surface)) { if (_matting(surface)) return _rasterMattedRect(surface, bbox, c); else return _rasterMaskedRect(surface, bbox, c); } else if (_blending(surface)) { return _rasterBlendingRect(surface, bbox, c); } else { if (c.a == 255) return _rasterSolidRect(surface, bbox, c); else return _rasterTranslucentRect(surface, bbox, c); } return false; } /************************************************************************/ /* Rle */ /************************************************************************/ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, SwMask maskOp, uint8_t a) { auto cbuffer = surface->compositor->image.buf8; auto cstride = surface->compositor->image.stride; const SwSpan* end; int32_t x, len; uint8_t src; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto cmp = &cbuffer[span->y * cstride + x]; if (span->coverage == 255) src = a; else src = MULTIPLY(a, span->coverage); auto ialpha = 255 - src; for (auto x = 0; x < len; ++x, ++cmp) { *cmp = maskOp(src, *cmp, ialpha); } } return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox); } static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, SwMask maskOp, uint8_t a) { auto cbuffer = surface->compositor->image.buf8; auto cstride = surface->compositor->image.stride; const SwSpan* end; int32_t x, len; uint8_t src; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto cmp = &cbuffer[span->y * cstride + x]; auto dst = &surface->buf8[span->y * surface->stride + x]; if (span->coverage == 255) src = a; else src = MULTIPLY(a, span->coverage); for (auto x = 0; x < len; ++x, ++cmp, ++dst) { auto tmp = maskOp(src, *cmp, 0); //not use alpha *dst = tmp + MULTIPLY(*dst, ~tmp); } } return true; } static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method); //8bit masking channels composition if (surface->channelSize != sizeof(uint8_t)) return false; auto maskOp = _getMaskOp(surface->compositor->method); if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, bbox, maskOp, c.a); else return _rasterCompositeMaskedRle(surface, rle, bbox, maskOp, c.a); return false; } static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method); auto cbuffer = surface->compositor->image.buf8; auto csize = surface->compositor->image.channelSize; auto alpha = surface->alpha(surface->compositor->method); const SwSpan* end; int32_t x, len; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { uint32_t src; auto color = surface->join(c.r, c.g, c.b, c.a); for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf32[span->y * surface->stride + x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + x) * csize]; if (span->coverage == 255) src = color; else src = ALPHA_BLEND(color, span->coverage); for (auto x = 0; x < len; ++x, ++dst, cmp += csize) { auto tmp = ALPHA_BLEND(src, alpha(cmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { uint8_t src; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf8[span->y * surface->stride + x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + x) * csize]; if (span->coverage == 255) src = c.a; else src = MULTIPLY(c.a, span->coverage); for (auto x = 0; x < len; ++x, ++dst, cmp += csize) { *dst = INTERPOLATE8(src, *dst, alpha(cmp)); } } } return true; } static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { if (surface->channelSize != sizeof(uint32_t)) return false; auto color = surface->join(c.r, c.g, c.b, c.a); const SwSpan* end; int32_t x, len; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf32[span->y * surface->stride + x]; if (span->coverage == 255) { for (auto x = 0; x < len; ++x, ++dst) { *dst = surface->blender(color, *dst); } } else { for (auto x = 0; x < len; ++x, ++dst) { *dst = INTERPOLATE(surface->blender(color, *dst), *dst, span->coverage); } } } return true; } static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { #if defined(THORVG_AVX_VECTOR_SUPPORT) return avxRasterTranslucentRle(surface, rle, bbox, c); #elif defined(THORVG_NEON_VECTOR_SUPPORT) return neonRasterTranslucentRle(surface, rle, bbox, c); #else return cRasterTranslucentRle(surface, rle, bbox, c); #endif } static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { const SwSpan* end; int32_t x, len; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, 255); for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; if (span->coverage == 255) rasterPixel32(surface->buf32 + span->y * surface->stride, color, x, len); else { auto dst = &surface->buf32[span->y * surface->stride + x]; auto src = ALPHA_BLEND(color, span->coverage); auto ialpha = 255 - span->coverage; for (auto x = 0; x < len; ++x, ++dst) { *dst = src + ALPHA_BLEND(*dst, ialpha); } } } //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; if (span->coverage == 255) rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + x, len); else { auto dst = &surface->buf8[span->y * surface->stride + x]; auto ialpha = 255 - span->coverage; for (auto x = 0; x < len; ++x, ++dst) { *dst = span->coverage + MULTIPLY(*dst, ialpha); } } } } return true; } static bool _rasterRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { if (!rle || rle->invalid()) return false; if (_compositing(surface)) { if (_matting(surface)) return _rasterMattedRle(surface, rle, bbox, c); else return _rasterMaskedRle(surface, rle, bbox, c); } else if (_blending(surface)) { return _rasterBlendingRle(surface, rle, bbox, c); } else { if (c.a == 255) return _rasterSolidRle(surface, rle, bbox, c); else return _rasterTranslucentRle(surface, rle, bbox, c); } return false; } /************************************************************************/ /* RLE Scaled Image */ /************************************************************************/ #define SCALED_IMAGE_RANGE_Y(y) \ auto sy = (y) * itransform->e22 + itransform->e23 - 0.49f; \ if (sy <= -0.5f || (uint32_t)(sy + 0.5f) >= image.h) continue; \ if (scaleMethod == _interpDownScaler) { \ auto my = (int32_t)nearbyint(sy); \ miny = my - (int32_t)sampleSize; \ if (miny < 0) miny = 0; \ maxy = my + (int32_t)sampleSize; \ if (maxy >= (int32_t)image.h) maxy = (int32_t)image.h; \ } #define SCALED_IMAGE_RANGE_X \ auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \ if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image.w) continue; \ static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) { TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method); return false; } static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) { TVGLOG("SW_ENGINE", "Scaled Matted(%d) Rle Image", (int)surface->compositor->method); auto csize = surface->compositor->image.channelSize; auto alpha = surface->alpha(surface->compositor->method); auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; ARRAY_FOREACH(span, image.rle->spans) { SCALED_IMAGE_RANGE_Y(span->y) auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; auto a = MULTIPLY(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { SCALED_IMAGE_RANGE_X auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a)); *dst = src + ALPHA_BLEND(*dst, IA(src)); } } return true; } static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) { auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; ARRAY_FOREACH(span, image.rle->spans) { SCALED_IMAGE_RANGE_Y(span->y) auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto alpha = MULTIPLY(span->coverage, opacity); if (alpha == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { SCALED_IMAGE_RANGE_X auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); *dst = INTERPOLATE(surface->blender(rasterUnpremultiply(src), *dst), *dst, A(src)); } } else { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { SCALED_IMAGE_RANGE_X auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); *dst = INTERPOLATE(surface->blender(rasterUnpremultiply(src), *dst), *dst, MULTIPLY(alpha, A(src))); } } } return true; } static bool _rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) { auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; ARRAY_FOREACH(span, image.rle->spans) { SCALED_IMAGE_RANGE_Y(span->y) auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto alpha = MULTIPLY(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { SCALED_IMAGE_RANGE_X auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); if (alpha < 255) src = ALPHA_BLEND(src, alpha); *dst = src + ALPHA_BLEND(*dst, IA(src)); } } return true; } /************************************************************************/ /* RLE Direct Image */ /************************************************************************/ static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) { TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method); auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8; auto alpha = surface->alpha(surface->compositor->method); const SwSpan* end; int32_t x, len; for (auto span = image.rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf32[span->y * surface->stride + x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + x) * csize]; auto img = image.buf32 + (span->y + image.oy) * image.stride + (x + image.ox); auto a = MULTIPLY(span->coverage, opacity); if (a == 255) { for (auto x = 0; x < len; ++x, ++dst, ++img, cmp += csize) { auto tmp = ALPHA_BLEND(*img, alpha(cmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } else { for (auto x = 0; x < len; ++x, ++dst, ++img, cmp += csize) { auto tmp = ALPHA_BLEND(*img, MULTIPLY(a, alpha(cmp))); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } } return true; } static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) { const SwSpan* end; int32_t x, len; for (auto span = image.rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf32[span->y * surface->stride + x]; auto src = image.buf32 + (span->y + image.oy) * image.stride + (x + image.ox); auto alpha = MULTIPLY(span->coverage, opacity); if (alpha == 255) { for (auto x = 0; x < len; ++x, ++dst, ++src) { *dst = surface->blender(rasterUnpremultiply(*src), *dst); } } else { for (auto x = 0; x < len; ++x, ++dst, ++src) { *dst = INTERPOLATE(surface->blender(rasterUnpremultiply(*src), *dst), *dst, MULTIPLY(alpha, A(*src))); } } } return true; } static bool _rasterDirectRleImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) { const SwSpan* end; int32_t x, len; for (auto span = image.rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf32[span->y * surface->stride + x]; auto img = image.buf32 + (span->y + image.oy) * image.stride + (x + image.ox); auto alpha = MULTIPLY(span->coverage, opacity); rasterTranslucentPixel32(dst, img, len, alpha); } return true; } static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) { TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method); return false; } /************************************************************************/ /*Scaled Image */ /************************************************************************/ static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) { TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!"); return false; } static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale scaled matted image!"); return false; } auto dbuffer = surface->buf32 + (bbox.min.y * surface->stride + bbox.min.x); auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; auto alpha = surface->alpha(surface->compositor->method); TVGLOG("SW_ENGINE", "Scaled Matted(%d) Image [Region: %d %d %d %d]", (int)surface->compositor->method, bbox.min.x, bbox.min.y, bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y); auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; for (auto y = bbox.min.y; y < bbox.max.y; ++y) { SCALED_IMAGE_RANGE_Y(y) auto dst = dbuffer; auto cmp = cbuffer; for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst, cmp += csize) { SCALED_IMAGE_RANGE_X auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp))); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } dbuffer += surface->stride; cbuffer += surface->compositor->image.stride * csize; } return true; } static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale scaled blending image!"); return false; } auto dbuffer = surface->buf32 + (bbox.min.y * surface->stride + bbox.min.x); auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; for (auto y = bbox.min.y; y < bbox.max.y; ++y, dbuffer += surface->stride) { SCALED_IMAGE_RANGE_Y(y) auto dst = dbuffer; for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) { SCALED_IMAGE_RANGE_X auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); *dst = INTERPOLATE(surface->blender(rasterUnpremultiply(src), *dst), *dst, MULTIPLY(opacity, A(src))); } } return true; } static bool _rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) { auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto buffer = surface->buf32 + (bbox.min.y * surface->stride + bbox.min.x); for (auto y = bbox.min.y; y < bbox.max.y; ++y, buffer += surface->stride) { SCALED_IMAGE_RANGE_Y(y) auto dst = buffer; for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) { SCALED_IMAGE_RANGE_X auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); if (opacity < 255) src = ALPHA_BLEND(src, opacity); *dst = src + ALPHA_BLEND(*dst, IA(src)); } } } else if (surface->channelSize == sizeof(uint8_t)) { auto buffer = surface->buf8 + (bbox.min.y * surface->stride + bbox.min.x); for (auto y = bbox.min.y; y < bbox.max.y; ++y, buffer += surface->stride) { SCALED_IMAGE_RANGE_Y(y) auto dst = buffer; for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) { SCALED_IMAGE_RANGE_X auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); *dst = MULTIPLY(A(src), opacity); } } } return true; } /************************************************************************/ /* Direct Image */ /************************************************************************/ static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity) { TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image"); return false; } static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity) { auto csize = surface->compositor->image.channelSize; auto alpha = surface->alpha(surface->compositor->method); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox); auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; //compositor buffer TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %u %u %u %u]", (int)surface->compositor->method, bbox.x(), bbox.y(), bbox.w(), bbox.h()); //32 bits if (surface->channelSize == sizeof(uint32_t)) { auto dbuffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) { auto cmp = cbuffer; auto src = sbuffer; if (opacity == 255) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) { auto tmp = ALPHA_BLEND(*src, alpha(cmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } else { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) { auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp))); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } cbuffer += surface->compositor->image.stride * csize; } //8 bits } else if (surface->channelSize == sizeof(uint8_t)) { auto dbuffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x; for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) { auto cmp = cbuffer; auto src = sbuffer; if (opacity == 255) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) { auto tmp = MULTIPLY(A(*src), alpha(cmp)); *dst = tmp + MULTIPLY(*dst, 255 - tmp); } } else { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) { auto tmp = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp))); *dst = tmp + MULTIPLY(*dst, 255 - tmp); } } cbuffer += surface->compositor->image.stride * csize; } } return true; } static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale image!"); return false; } auto dbuffer = &surface->buf32[bbox.min.y * surface->stride + bbox.min.x]; auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox); for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) { auto src = sbuffer; if (opacity == 255) { for (auto dst = dbuffer; dst < dbuffer + w; dst++, src++) { *dst = INTERPOLATE(surface->blender(rasterUnpremultiply(*src), *dst), *dst, A(*src)); } } else { for (auto dst = dbuffer; dst < dbuffer + w; dst++, src++) { *dst = INTERPOLATE(surface->blender(rasterUnpremultiply(*src), *dst), *dst, MULTIPLY(opacity, A(*src))); } } } return true; } static bool _rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity) { auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto dbuffer = &surface->buf32[bbox.min.y * surface->stride + bbox.min.x]; for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) { rasterTranslucentPixel32(dbuffer, sbuffer, w, opacity); } //8bits grayscale //32 -> 8 direct converting seems an avoidable stage. maybe draw to a masking image after an intermediate scene. Can get rid of this? } else if (surface->channelSize == sizeof(uint8_t)) { auto dbuffer = &surface->buf8[bbox.min.y * surface->stride + bbox.min.x]; for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) { auto src = sbuffer; if (opacity == 255) { for (auto dst = dbuffer; dst < dbuffer + w; dst++, src++) { *dst = A(*src) + MULTIPLY(*dst, IA(*src)); } } else { for (auto dst = dbuffer; dst < dbuffer + w; dst++, src++) { *dst = INTERPOLATE8(A(*src), *dst, opacity); } } } } return true; } static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale image!"); return false; } auto csize = surface->compositor->image.channelSize; auto alpha = surface->alpha(surface->compositor->method); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox); auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; //compositor buffer auto dbuffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) { auto cmp = cbuffer; auto src = sbuffer; if (opacity == 255) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) { *dst = INTERPOLATE(surface->blender(*src, *dst), *dst, MULTIPLY(A(*src), alpha(cmp))); } } else { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) { *dst = INTERPOLATE(surface->blender(*src, *dst), *dst, MULTIPLY(MULTIPLY(A(*src), alpha(cmp)), opacity)); } } cbuffer += surface->compositor->image.stride * csize; } return true; } /************************************************************************/ /* Rect Gradient */ /************************************************************************/ template static bool _rasterCompositeGradientMaskedRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill, SwMask maskOp) { auto cstride = surface->compositor->image.stride; auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * cstride + bbox.min.x); for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, cbuffer, bbox.min.y + y, bbox.min.x, bbox.w(), maskOp, 255); cbuffer += surface->stride; } return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox); } template static bool _rasterDirectGradientMaskedRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill, SwMask maskOp) { auto cstride = surface->compositor->image.stride; auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * cstride + bbox.min.x); auto dbuffer = surface->buf8 + (bbox.min.y * surface->stride + bbox.min.x); for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, dbuffer, bbox.min.y + y, bbox.min.x, bbox.w(), cbuffer, maskOp, 255); cbuffer += cstride; dbuffer += surface->stride; } return true; } template static bool _rasterGradientMaskedRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill) { auto method = surface->compositor->method; TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %d %d %d %d]", (int)method, bbox.min.x, bbox.min.y, bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y); auto maskOp = _getMaskOp(method); if (_direct(method)) return _rasterDirectGradientMaskedRect(surface, bbox, fill, maskOp); else return _rasterCompositeGradientMaskedRect(surface, bbox, fill, maskOp); return false; } template static bool _rasterGradientMattedRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill) { auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; auto alpha = surface->alpha(surface->compositor->method); TVGLOG("SW_ENGINE", "Matted(%d) Gradient [Region: %u %u %u %u]", (int)surface->compositor->method, bbox.x(), bbox.y(), bbox.w(), bbox.h()); for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, buffer, bbox.min.y + y, bbox.min.x, bbox.w(), cbuffer, alpha, csize, 255); buffer += surface->stride; cbuffer += surface->stride * csize; } return true; } template static bool _rasterBlendingGradientRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill) { auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; if (fill->translucent) { for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, buffer + y * surface->stride, bbox.min.y + y, bbox.min.x, bbox.w(), opBlendPreNormal, surface->blender, 255); } } else { for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, buffer + y * surface->stride, bbox.min.y + y, bbox.min.x, bbox.w(), opBlendSrcOver, surface->blender, 255); } } return true; } template static bool _rasterTranslucentGradientRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill) { //32 bits if (surface->channelSize == sizeof(uint32_t)) { auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, buffer, bbox.min.y + y, bbox.min.x, bbox.w(), opBlendPreNormal, 255); buffer += surface->stride; } //8 bits } else if (surface->channelSize == sizeof(uint8_t)) { auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x; for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, buffer, bbox.min.y + y, bbox.min.x, bbox.w(), _opMaskAdd, 255); buffer += surface->stride; } } return true; } template static bool _rasterSolidGradientRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill) { //32 bits if (surface->channelSize == sizeof(uint32_t)) { auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, buffer, bbox.min.y + y, bbox.min.x, bbox.w(), opBlendSrcOver, 255); buffer += surface->stride; } //8 bits } else if (surface->channelSize == sizeof(uint8_t)) { auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x; for (uint32_t y = 0; y < bbox.h(); ++y) { fillMethod()(fill, buffer, bbox.min.y + y, bbox.min.x, bbox.w(), _opMaskNone, 255); buffer += surface->stride; } } return true; } static bool _rasterLinearGradientRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill) { if (_compositing(surface)) { if (_matting(surface)) return _rasterGradientMattedRect(surface, bbox, fill); else return _rasterGradientMaskedRect(surface, bbox, fill); } else if (_blending(surface)) { return _rasterBlendingGradientRect(surface, bbox, fill); } else { if (fill->translucent) return _rasterTranslucentGradientRect(surface, bbox, fill); else _rasterSolidGradientRect(surface, bbox, fill); } return false; } static bool _rasterRadialGradientRect(SwSurface* surface, const RenderRegion& bbox, const SwFill* fill) { if (_compositing(surface)) { if (_matting(surface)) return _rasterGradientMattedRect(surface, bbox, fill); else return _rasterGradientMaskedRect(surface, bbox, fill); } else if (_blending(surface)) { return _rasterBlendingGradientRect(surface, bbox, fill); } else { if (fill->translucent) return _rasterTranslucentGradientRect(surface, bbox, fill); else _rasterSolidGradientRect(surface, bbox, fill); } return false; } /************************************************************************/ /* Rle Gradient */ /************************************************************************/ template static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill, SwMask maskOp) { auto span = rle->data(); auto cstride = surface->compositor->image.stride; auto cbuffer = surface->compositor->image.buf8; for (uint32_t i = 0; i < rle->size(); ++i, ++span) { auto cmp = &cbuffer[span->y * cstride + span->x]; fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage); } return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox); } template static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill, SwMask maskOp) { auto span = rle->data(); auto cstride = surface->compositor->image.stride; auto cbuffer = surface->compositor->image.buf8; auto dbuffer = surface->buf8; for (uint32_t i = 0; i < rle->size(); ++i, ++span) { auto cmp = &cbuffer[span->y * cstride + span->x]; auto dst = &dbuffer[span->y * surface->stride + span->x]; fillMethod()(fill, dst, span->y, span->x, span->len, cmp, maskOp, span->coverage); } return true; } template static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { auto method = surface->compositor->method; TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)method); auto maskOp = _getMaskOp(method); if (_direct(method)) return _rasterDirectGradientMaskedRle(surface, rle, fill, maskOp); else return _rasterCompositeGradientMaskedRle(surface, rle, fill, maskOp); return false; } template static bool _rasterGradientMattedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { TVGLOG("SW_ENGINE", "Matted(%d) Rle Linear Gradient", (int)surface->compositor->method); auto span = rle->data(); auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8; auto alpha = surface->alpha(surface->compositor->method); for (uint32_t i = 0; i < rle->size(); ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; fillMethod()(fill, dst, span->y, span->x, span->len, cmp, alpha, csize, span->coverage); } return true; } template static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { auto span = rle->data(); for (uint32_t i = 0; i < rle->size(); ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, surface->blender, span->coverage); } return true; } template static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { auto span = rle->data(); //32 bits if (surface->channelSize == sizeof(uint32_t)) { for (uint32_t i = 0; i < rle->size(); ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, 255); else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendNormal, span->coverage); } //8 bits } else if (surface->channelSize == sizeof(uint8_t)) { for (uint32_t i = 0; i < rle->size(); ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage); } } return true; } template static bool _rasterSolidGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { auto span = rle->data(); //32 bits if (surface->channelSize == sizeof(uint32_t)) { for (uint32_t i = 0; i < rle->size(); ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendSrcOver, 255); else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendInterp, span->coverage); } //8 bits } else if (surface->channelSize == sizeof(uint8_t)) { for (uint32_t i = 0; i < rle->size(); ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskNone, 255); else fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage); } } return true; } static bool _rasterLinearGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { if (_compositing(surface)) { if (_matting(surface)) return _rasterGradientMattedRle(surface, rle, fill); else return _rasterGradientMaskedRle(surface, rle, fill); } else if (_blending(surface)) { return _rasterBlendingGradientRle(surface, rle, fill); } else { if (fill->translucent) return _rasterTranslucentGradientRle(surface, rle, fill); else return _rasterSolidGradientRle(surface, rle, fill); } return false; } static bool _rasterRadialGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { if (_compositing(surface)) { if (_matting(surface)) return _rasterGradientMattedRle(surface, rle, fill); else return _rasterGradientMaskedRle(surface, rle, fill); } else if (_blending(surface)) { return _rasterBlendingGradientRle(surface, rle, fill); } else { if (fill->translucent) return _rasterTranslucentGradientRle(surface, rle, fill); else return _rasterSolidGradientRle(surface, rle, fill); } return false; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity) { //TODO: Support SIMD accelerations cRasterTranslucentPixels(dst, src, len, opacity); } void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity) { //TODO: Support SIMD accelerations cRasterPixels(dst, src, len, opacity); } void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len) { #if defined(THORVG_AVX_VECTOR_SUPPORT) avxRasterGrayscale8(dst, val, offset, len); #elif defined(THORVG_NEON_VECTOR_SUPPORT) neonRasterGrayscale8(dst, val, offset, len); #else cRasterPixels(dst, val, offset, len); #endif } void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) { #if defined(THORVG_AVX_VECTOR_SUPPORT) avxRasterPixel32(dst, val, offset, len); #elif defined(THORVG_NEON_VECTOR_SUPPORT) neonRasterPixel32(dst, val, offset, len); #else cRasterPixels(dst, val, offset, len); #endif } bool rasterCompositor(SwSurface* surface) { //See MaskMethod, Alpha:1, InvAlpha:2, Luma:3, InvLuma:4 surface->alphas[0] = _alpha; surface->alphas[1] = _ialpha; if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) { surface->join = _abgrJoin; surface->alphas[2] = _abgrLuma; surface->alphas[3] = _abgrInvLuma; } else if (surface->cs == ColorSpace::ARGB8888 || surface->cs == ColorSpace::ARGB8888S) { surface->join = _argbJoin; surface->alphas[2] = _argbLuma; surface->alphas[3] = _argbInvLuma; } else { TVGERR("SW_ENGINE", "Unsupported Colorspace(%d) is expected!", (int)surface->cs); return false; } return true; } bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { if (!surface || !surface->buf32 || surface->stride == 0 || surface->w == 0 || surface->h == 0) return false; //32 bits if (surface->channelSize == sizeof(uint32_t)) { uint32_t val = 0; //full clear if (w == surface->stride) { rasterPixel32(surface->buf32, val, surface->stride * y, w * h); //partial clear } else { for (uint32_t i = 0; i < h; i++) { rasterPixel32(surface->buf32, val, (surface->stride * y + x) + (surface->stride * i), w); } } //8 bits } else if (surface->channelSize == sizeof(uint8_t)) { //full clear if (w == surface->stride) { rasterGrayscale8(surface->buf8, 0x00, surface->stride * y, w * h); //partial clear } else { for (uint32_t i = 0; i < h; i++) { rasterGrayscale8(surface->buf8, 0x00, (surface->stride * y + x) + (surface->stride * i), w); } } } return true; } uint32_t rasterUnpremultiply(uint32_t data) { auto a = A(data); if (a == 255 || a == 0) return data; uint8_t r = std::min(C1(data) * 255u / a, 255u); uint8_t g = std::min(C2(data) * 255u / a, 255u); uint8_t b = std::min(C3(data) * 255u / a, 255u); return JOIN(a, r, g, b); } void rasterUnpremultiply(RenderSurface* surface) { if (surface->channelSize != sizeof(uint32_t)) return; TVGLOG("SW_ENGINE", "Unpremultiply [Size: %d x %d]", surface->w, surface->h); //OPTIMIZE_ME: +SIMD for (uint32_t y = 0; y < surface->h; y++) { auto buffer = surface->buf32 + surface->stride * y; for (uint32_t x = 0; x < surface->w; ++x) { buffer[x] = rasterUnpremultiply(buffer[x]); } } surface->premultiplied = false; } void rasterPremultiply(RenderSurface* surface) { ScopedLock lock(surface->key); if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return; surface->premultiplied = true; TVGLOG("SW_ENGINE", "Premultiply [Size: %d x %d]", surface->w, surface->h); //OPTIMIZE_ME: +SIMD auto buffer = surface->buf32; for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { auto dst = buffer; for (uint32_t x = 0; x < surface->w; ++x, ++dst) { auto c = *dst; if (A(c) == 255) continue; *dst = PREMULTIPLY(c, A(c)); } } } bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity) { Matrix itransform; if (!inverse(&transform, &itransform)) return true; if (_compositing(surface)) { if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, bbox, opacity); else return _rasterScaledMaskedImage(surface, image, &itransform, bbox, opacity); } else if (_blending(surface)) { return _rasterScaledBlendingImage(surface, image, &itransform, bbox, opacity); } else { return _rasterScaledImage(surface, image, &itransform, bbox, opacity); } return false; } bool rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) { //calculate an actual drawing image size auto w = std::min(bbox.max.x - bbox.min.x, int32_t(image.w) - (bbox.min.x + image.ox)); auto h = std::min(bbox.max.y - bbox.min.y, int32_t(image.h) - (bbox.min.y + image.oy)); if (_compositing(surface)) { if (_matting(surface)) { if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, bbox, w, h, opacity); else return _rasterDirectMattedImage(surface, image, bbox, w, h, opacity); } else return _rasterDirectMaskedImage(surface, image, bbox, w, h, opacity); } else if (_blending(surface)) { return _rasterDirectBlendingImage(surface, image, bbox, w, h, opacity); } else { return _rasterDirectImage(surface, image, bbox, w, h, opacity); } return false; } bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported scaled rle image!"); return false; } Matrix itransform; if (!inverse(&transform, &itransform)) return true; if (_compositing(surface)) { if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, bbox, opacity); else return _rasterScaledMaskedRleImage(surface, image, &itransform, bbox, opacity); } else if (_blending(surface)) { return _rasterScaledBlendingRleImage(surface, image, &itransform, bbox, opacity); } else { return _rasterScaledRleImage(surface, image, &itransform, bbox, opacity); } return false; } bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale rle image!"); return false; } if (_compositing(surface)) { if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, bbox, opacity); else return _rasterDirectMaskedRleImage(surface, image, bbox, opacity); } else if (_blending(surface)) { return _rasterDirectBlendingRleImage(surface, image, bbox, opacity); } else { return _rasterDirectRleImage(surface, image, bbox, opacity); } return false; } bool rasterGradientShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity) { if (!shape->fill) return false; if (auto color = fillFetchSolid(shape->fill, fdata)) { auto a = MULTIPLY(color->a, opacity); RenderColor c = {color->r, color->g, color->b, a}; return a > 0 ? rasterShape(surface, shape, bbox, c) : true; } auto type = fdata->type(); if (shape->fastTrack) { if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, bbox, shape->fill); else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, bbox, shape->fill); } else if (shape->rle && shape->rle->valid()) { if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->rle, shape->fill); else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->rle, shape->fill); } return false; } bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity) { if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle || shape->strokeRle->invalid()) return false; if (auto color = fillFetchSolid(shape->stroke->fill, fdata)) { RenderColor c = {color->r, color->g, color->b, color->a}; c.a = MULTIPLY(c.a, opacity); return c.a > 0 ? rasterStroke(surface, shape, bbox, c) : true; } auto type = fdata->type(); if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); return false; } bool rasterShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c) { if (c.a < 255) { c.r = MULTIPLY(c.r, c.a); c.g = MULTIPLY(c.g, c.a); c.b = MULTIPLY(c.b, c.a); } if (shape->fastTrack) return _rasterRect(surface, bbox, c); else return _rasterRle(surface, shape->rle, bbox, c); } bool rasterStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c) { if (c.a < 255) { c.r = MULTIPLY(c.r, c.a); c.g = MULTIPLY(c.g, c.a); c.b = MULTIPLY(c.b, c.a); } return _rasterRle(surface, shape->strokeRle, bbox, c); } bool rasterConvertCS(RenderSurface* surface, ColorSpace to) { ScopedLock lock(surface->key); if (surface->cs == to) return true; //TODO: Support SIMD accelerations auto from = surface->cs; if (((from == ColorSpace::ABGR8888) || (from == ColorSpace::ABGR8888S)) && ((to == ColorSpace::ARGB8888) || (to == ColorSpace::ARGB8888S))) { surface->cs = to; return cRasterABGRtoARGB(surface); } if (((from == ColorSpace::ARGB8888) || (from == ColorSpace::ARGB8888S)) && ((to == ColorSpace::ABGR8888) || (to == ColorSpace::ABGR8888S))) { surface->cs = to; return cRasterARGBtoABGR(surface); } return false; } //TODO: SIMD OPTIMIZATION? void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const RenderRegion& bbox, bool flipped) { constexpr int32_t BLOCK = 8; //experimental decision if (flipped) { src += ((bbox.min.x * stride) + bbox.min.y); dst += ((bbox.min.y * stride) + bbox.min.x); } else { src += ((bbox.min.y * stride) + bbox.min.x); dst += ((bbox.min.x * stride) + bbox.min.y); } #pragma omp parallel for for (int32_t x = 0; x < w; x += BLOCK) { auto bx = std::min(w, x + BLOCK) - x; auto in = &src[x]; auto out = &dst[x * stride]; for (int32_t y = 0; y < h; y += BLOCK) { auto p = &in[y * stride]; auto q = &out[y]; auto by = std::min(h, y + BLOCK) - y; for (int32_t xx = 0; xx < bx; ++xx) { for (int32_t yy = 0; yy < by; ++yy) { *q = *p; p += stride; ++q; } p += 1 - by * stride; q += stride - by; } } } } //TODO: can be moved in tvgColor void rasterRGB2HSL(uint8_t r, uint8_t g, uint8_t b, float* h, float* s, float* l) { auto rf = r / 255.0f; auto gf = g / 255.0f; auto bf = b / 255.0f; auto maxVal = std::max(std::max(rf, gf), bf); auto minVal = std::min(std::min(rf, gf), bf); auto delta = maxVal - minVal; //lightness float t = 0.0f; if (l || s) { t = (maxVal + minVal) * 0.5f; if (l) *l = t; } if (tvg::zero(delta)) { if (h) *h = 0.0f; if (s) *s = 0.0f; } else { //saturation if (s) { *s = (t < 0.5f) ? (delta / (maxVal + minVal)) : (delta / (2.0f - maxVal - minVal)); } //hue if (h) { if (maxVal == rf) *h = (gf - bf) / delta + (gf < bf ? 6.0f : 0.0f); else if (maxVal == gf) *h = (bf - rf) / delta + 2.0f; else *h = (rf - gf) / delta + 4.0f; *h *= 60.0f; //directly convert to degrees } } } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/bindings/meson.build000664 001750 001750 00000000104 15164251010 033736 0ustar00ddennedyddennedy000000 000000 if get_option('bindings').contains('capi') subdir('capi') endif loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-arraybuffer-prototype.inc.h000664 001750 001750 00000003136 15164251010 053167 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * ArrayBuffer.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_TYPEDARRAY /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_ARRAYBUFFER, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* Readonly accessor properties */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BYTE_LENGTH_UL, ecma_builtin_arraybuffer_prototype_bytelength_getter, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 24.1.4.4 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_ARRAY_BUFFER_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_SLICE, ecma_builtin_arraybuffer_prototype_object_slice, NON_FIXED, 2) #endif /* JERRY_BUILTIN_TYPEDARRAY */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-boolean-prototype.inc.h000664 001750 001750 00000002512 15164251010 052273 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Boolean.prototype description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_BOOLEAN /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.6.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_BOOLEAN, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_BOOLEAN_PROTOTYPE_ROUTINE_TO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_VALUE_OF_UL, ECMA_BOOLEAN_PROTOTYPE_ROUTINE_VALUE_OF, 0, 0) #endif /* JERRY_BUILTIN_BOOLEAN */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/error/000775 001750 001750 00000000000 15164251010 036000 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-map-prototype.inc.h000664 001750 001750 00000004466 15164251010 051443 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Map.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 23.1.3.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_MAP, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.1.3.13 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_MAP_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_CLEAR, ECMA_CONTAINER_ROUTINE_CLEAR, 0, 0) ROUTINE (LIT_MAGIC_STRING_DELETE, ECMA_CONTAINER_ROUTINE_DELETE, 1, 1) ROUTINE (LIT_MAGIC_STRING_FOR_EACH_UL, ECMA_CONTAINER_ROUTINE_FOREACH, 2, 1) ROUTINE (LIT_MAGIC_STRING_GET, ECMA_CONTAINER_ROUTINE_GET, 1, 1) ROUTINE (LIT_MAGIC_STRING_HAS, ECMA_CONTAINER_ROUTINE_HAS, 1, 1) ROUTINE (LIT_MAGIC_STRING_SET, ECMA_CONTAINER_ROUTINE_SET, 2, 2) ROUTINE (LIT_MAGIC_STRING_VALUES, ECMA_CONTAINER_ROUTINE_VALUES, 0, 0) ROUTINE (LIT_MAGIC_STRING_KEYS, ECMA_CONTAINER_ROUTINE_KEYS, 0, 0) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_ENTRIES, LIT_INTERNAL_MAGIC_STRING_MAP_PROTOTYPE_ENTRIES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_GLOBAL_SYMBOL_ITERATOR, LIT_INTERNAL_MAGIC_STRING_MAP_PROTOTYPE_ENTRIES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.1.3.10 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_SIZE, ECMA_CONTAINER_ROUTINE_SIZE_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/re-compiler.cpp000664 001750 001750 00000012124 15164251010 045027 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "re-compiler.h" #include "ecma-exceptions.h" #include "ecma-helpers.h" #include "ecma-regexp-object.h" #include "jcontext.h" #include "jmem.h" #include "jrt-libc-includes.h" #include "lit-char-helpers.h" #include "re-bytecode.h" #include "re-compiler-context.h" #include "re-parser.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup parser Parser * @{ * * \addtogroup regexparser Regular expression * @{ * * \addtogroup regexparser_compiler Compiler * @{ */ /** * Search for the given pattern in the RegExp cache. * * @return pointer to bytecode if found * NULL - otherwise */ static re_compiled_code_t * re_cache_lookup (ecma_string_t *pattern_str_p, /**< pattern string */ uint16_t flags) /**< flags */ { re_compiled_code_t **cache_p = JERRY_CONTEXT (re_cache); for (uint8_t idx = 0u; idx < RE_CACHE_SIZE; idx++) { re_compiled_code_t *cached_bytecode_p = cache_p[idx]; if (cached_bytecode_p == NULL) { break; } ecma_string_t *cached_pattern_str_p = ecma_get_string_from_value (cached_bytecode_p->source); if (cached_bytecode_p->header.status_flags == flags && ecma_compare_ecma_strings (cached_pattern_str_p, pattern_str_p)) { return cached_bytecode_p; } } return NULL; } /* re_cache_lookup */ /** * Run garbage collection in RegExp cache. */ void re_cache_gc (void) { re_compiled_code_t **cache_p = JERRY_CONTEXT (re_cache); for (uint32_t i = 0u; i < RE_CACHE_SIZE; i++) { const re_compiled_code_t *cached_bytecode_p = cache_p[i]; if (cached_bytecode_p == NULL) { break; } ecma_bytecode_deref ((ecma_compiled_code_t *) cached_bytecode_p); cache_p[i] = NULL; } JERRY_CONTEXT (re_cache_idx) = 0; } /* re_cache_gc */ /** * Compilation of RegExp bytecode * * @return pointer to bytecode if compilation was successful * NULL - otherwise */ re_compiled_code_t * re_compile_bytecode (ecma_string_t *pattern_str_p, /**< pattern */ uint16_t flags) /**< flags */ { re_compiled_code_t *cached_bytecode_p = re_cache_lookup (pattern_str_p, flags); if (cached_bytecode_p != NULL) { ecma_bytecode_ref ((ecma_compiled_code_t *) cached_bytecode_p); return cached_bytecode_p; } re_compiler_ctx_t re_ctx; re_ctx.flags = flags; re_ctx.captures_count = 1; re_ctx.non_captures_count = 0; re_initialize_regexp_bytecode (&re_ctx); ECMA_STRING_TO_UTF8_STRING (pattern_str_p, pattern_start_p, pattern_start_size); re_ctx.input_start_p = pattern_start_p; re_ctx.input_curr_p = (lit_utf8_byte_t *) pattern_start_p; re_ctx.input_end_p = pattern_start_p + pattern_start_size; re_ctx.groups_count = -1; /* Parse RegExp pattern */ ecma_value_t result = re_parse_alternative (&re_ctx, true); ECMA_FINALIZE_UTF8_STRING (pattern_start_p, pattern_start_size); if (ECMA_IS_VALUE_ERROR (result)) { /* Compilation failed, free bytecode. */ jmem_heap_free_block (re_ctx.bytecode_start_p, re_ctx.bytecode_size); return NULL; } /* Align bytecode size to JMEM_ALIGNMENT so that it can be stored in the bytecode header. */ const uint32_t final_size = JERRY_ALIGNUP (re_ctx.bytecode_size, JMEM_ALIGNMENT); re_compiled_code_t *re_compiled_code_p = (re_compiled_code_t *) jmem_heap_realloc_block (re_ctx.bytecode_start_p, re_ctx.bytecode_size, final_size); /* Bytecoded will be inserted into the cache and returned to the caller, so refcount is implicitly set to 2. */ re_compiled_code_p->header.refs = 2; re_compiled_code_p->header.size = (uint16_t) (final_size >> JMEM_ALIGNMENT_LOG); re_compiled_code_p->header.status_flags = re_ctx.flags; ecma_ref_ecma_string (pattern_str_p); re_compiled_code_p->source = ecma_make_string_value (pattern_str_p); re_compiled_code_p->captures_count = re_ctx.captures_count; re_compiled_code_p->non_captures_count = re_ctx.non_captures_count; #if JERRY_REGEXP_DUMP_BYTE_CODE if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_SHOW_REGEXP_OPCODES) { re_dump_bytecode (&re_ctx); } #endif /* JERRY_REGEXP_DUMP_BYTE_CODE */ uint8_t cache_idx = JERRY_CONTEXT (re_cache_idx); if (JERRY_CONTEXT (re_cache)[cache_idx] != NULL) { ecma_bytecode_deref ((ecma_compiled_code_t *) JERRY_CONTEXT (re_cache)[cache_idx]); } JERRY_CONTEXT (re_cache)[cache_idx] = re_compiled_code_p; JERRY_CONTEXT (re_cache_idx) = (uint8_t) (cache_idx + 1) % RE_CACHE_SIZE; return re_compiled_code_p; } /* re_compile_bytecode */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/000775 001750 001750 00000000000 15164251010 040613 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modulesmlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/buffer.cpp000664 001750 001750 00000017156 15164251010 035114 0ustar00ddennedyddennedy000000 000000 // Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Everything about WebPDecBuffer // // Author: Skal (pascal.massimino@gmail.com) #include "tvgCommon.h" #include "./vp8i.h" #include "./webpi.h" #include "../utils/utils.h" //------------------------------------------------------------------------------ // WebPDecBuffer // Number of bytes per pixel for the different color-spaces. static const int kModeBpp[MODE_LAST] = { 3, 4, 3, 4, 4, 2, 2, 4, 4, 4, 2, // pre-multiplied modes 1, 1 }; // Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE. // Convert to an integer to handle both the unsigned/signed enum cases // without the need for casting to remove type limit warnings. static int IsValidColorspace(int webp_csp_mode) { return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST); } static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) { int ok = 1; const WEBP_CSP_MODE mode = buffer->colorspace; const int width = buffer->width; const int height = buffer->height; if (!IsValidColorspace(mode)) { ok = 0; } else if (!WebPIsRGBMode(mode)) { // YUV checks const WebPYUVABuffer* const buf = &buffer->u.YUVA; const int y_stride = abs(buf->y_stride); const int u_stride = abs(buf->u_stride); const int v_stride = abs(buf->v_stride); const int a_stride = abs(buf->a_stride); const uint64_t y_size = (uint64_t)y_stride * height; const uint64_t u_size = (uint64_t)u_stride * ((height + 1) / 2); const uint64_t v_size = (uint64_t)v_stride * ((height + 1) / 2); const uint64_t a_size = (uint64_t)a_stride * height; ok &= (y_size <= buf->y_size); ok &= (u_size <= buf->u_size); ok &= (v_size <= buf->v_size); ok &= (y_stride >= width); ok &= (u_stride >= (width + 1) / 2); ok &= (v_stride >= (width + 1) / 2); ok &= (buf->y != NULL); ok &= (buf->u != NULL); ok &= (buf->v != NULL); if (mode == MODE_YUVA) { ok &= (a_stride >= width); ok &= (a_size <= buf->a_size); ok &= (buf->a != NULL); } } else { // RGB checks const WebPRGBABuffer* const buf = &buffer->u.RGBA; const int stride = abs(buf->stride); const uint64_t size = (uint64_t)stride * height; ok &= (size <= buf->size); ok &= (stride >= width * kModeBpp[mode]); ok &= (buf->rgba != NULL); } return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM; } static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { const int w = buffer->width; const int h = buffer->height; const WEBP_CSP_MODE mode = buffer->colorspace; if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) { return VP8_STATUS_INVALID_PARAM; } if (!buffer->is_external_memory && buffer->private_memory == NULL) { uint8_t* output; int uv_stride = 0, a_stride = 0; uint64_t uv_size = 0, a_size = 0, total_size; // We need memory and it hasn't been allocated yet. // => initialize output buffer, now that dimensions are known. const int stride = w * kModeBpp[mode]; const uint64_t size = (uint64_t)stride * h; if (!WebPIsRGBMode(mode)) { uv_stride = (w + 1) / 2; uv_size = (uint64_t)uv_stride * ((h + 1) / 2); if (mode == MODE_YUVA) { a_stride = w; a_size = (uint64_t)a_stride * h; } } total_size = size + 2 * uv_size + a_size; // Security/sanity checks output = tvg::malloc(total_size * sizeof(*output)); if (output == NULL) { return VP8_STATUS_OUT_OF_MEMORY; } buffer->private_memory = output; if (!WebPIsRGBMode(mode)) { // YUVA initialization WebPYUVABuffer* const buf = &buffer->u.YUVA; buf->y = output; buf->y_stride = stride; buf->y_size = (size_t)size; buf->u = output + size; buf->u_stride = uv_stride; buf->u_size = (size_t)uv_size; buf->v = output + size + uv_size; buf->v_stride = uv_stride; buf->v_size = (size_t)uv_size; if (mode == MODE_YUVA) { buf->a = output + size + 2 * uv_size; } buf->a_size = (size_t)a_size; buf->a_stride = a_stride; } else { // RGBA initialization WebPRGBABuffer* const buf = &buffer->u.RGBA; buf->rgba = output; buf->stride = stride; buf->size = (size_t)size; } } return CheckDecBuffer(buffer); } VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) { if (buffer == NULL) { return VP8_STATUS_INVALID_PARAM; } if (WebPIsRGBMode(buffer->colorspace)) { WebPRGBABuffer* const buf = &buffer->u.RGBA; buf->rgba += (buffer->height - 1) * buf->stride; buf->stride = -buf->stride; } else { WebPYUVABuffer* const buf = &buffer->u.YUVA; const int H = buffer->height; buf->y += (H - 1) * buf->y_stride; buf->y_stride = -buf->y_stride; buf->u += ((H - 1) >> 1) * buf->u_stride; buf->u_stride = -buf->u_stride; buf->v += ((H - 1) >> 1) * buf->v_stride; buf->v_stride = -buf->v_stride; if (buf->a != NULL) { buf->a += (H - 1) * buf->a_stride; buf->a_stride = -buf->a_stride; } } return VP8_STATUS_OK; } VP8StatusCode WebPAllocateDecBuffer(int w, int h, const WebPDecoderOptions* const options, WebPDecBuffer* const out) { VP8StatusCode status; if (out == NULL || w <= 0 || h <= 0) { return VP8_STATUS_INVALID_PARAM; } if (options != NULL) { // First, apply options if there is any. if (options->use_cropping) { const int cw = options->crop_width; const int ch = options->crop_height; const int x = options->crop_left & ~1; const int y = options->crop_top & ~1; if (x < 0 || y < 0 || cw <= 0 || ch <= 0 || x + cw > w || y + ch > h) { return VP8_STATUS_INVALID_PARAM; // out of frame boundary. } w = cw; h = ch; } if (options->use_scaling) { if (options->scaled_width <= 0 || options->scaled_height <= 0) { return VP8_STATUS_INVALID_PARAM; } w = options->scaled_width; h = options->scaled_height; } } out->width = w; out->height = h; // Then, allocate buffer for real. status = AllocateBuffer(out); if (status != VP8_STATUS_OK) return status; // Use the stride trick if vertical flip is needed. if (options != NULL && options->flip) { status = WebPFlipBuffer(out); } return status; } //------------------------------------------------------------------------------ // constructors / destructors int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { return 0; // version mismatch } if (buffer == NULL) return 0; memset(buffer, 0, sizeof(*buffer)); return 1; } void WebPFreeDecBuffer(WebPDecBuffer* buffer) { if (buffer != NULL) { if (!buffer->is_external_memory) { tvg::free(buffer->private_memory); } buffer->private_memory = NULL; } } void WebPCopyDecBuffer(const WebPDecBuffer* const src, WebPDecBuffer* const dst) { if (src != NULL && dst != NULL) { *dst = *src; if (src->private_memory != NULL) { dst->is_external_memory = 1; // dst buffer doesn't own the memory. dst->private_memory = NULL; } } } //------------------------------------------------------------------------------ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/diyfp.h000664 001750 001750 00000026474 15164251010 037764 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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" #include "clzll.h" #include #if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include #if !defined(_ARM64EC_) #pragma intrinsic(_umul128) #else #pragma comment(lib,"softintrin") #endif #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((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 defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) __extension__ typedef unsigned __int128 uint128; uint128 p = static_cast(f) * static_cast(rhs.f); uint64_t h = static_cast(p >> 64); uint64_t l = static_cast(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 { int s = static_cast(clzll(f)); return DiyFp(f << s, e - s); } 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; RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); if (e < kDpDenormalExponent) { // Underflow. return 0.0; } if (e >= kDpMaxExponent) { // Overflow. return std::numeric_limits::infinity(); } const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : static_cast(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 }; RAPIDJSON_ASSERT(index < 87); return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); } inline DiyFp GetCachedPower(int e, int* K) { //int k = static_cast(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(dk); if (dk - k > 0.0) k++; unsigned index = static_cast((k >> 3) + 1); *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table return GetCachedPowerByIndex(index); } inline DiyFp GetCachedPower10(int exp, int *outExp) { RAPIDJSON_ASSERT(exp >= -348); unsigned index = static_cast(exp + 348) / 8u; *outExp = -348 + static_cast(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_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/000775 001750 001750 00000000000 15164251010 030355 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/savers/gif/000775 001750 001750 00000000000 15164251010 032054 5ustar00ddennedyddennedy000000 000000 glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/include/jerry-config.h000664 001750 001750 00000020407 15164251010 043514 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JERRYSCRIPT_CONFIG_H #define JERRYSCRIPT_CONFIG_H // @JERRY_BUILD_CFG@ /** * Built-in configurations * * Allowed values for built-in defines: * 0: Disable the given built-in. * 1: Enable the given built-in. */ /* * By default all built-ins are enabled if they are not defined. */ #ifndef JERRY_BUILTINS #define JERRY_BUILTINS 1 #endif /* !defined (JERRY_BUILTINS) */ #ifndef JERRY_BUILTIN_ARRAY #define JERRY_BUILTIN_ARRAY JERRY_BUILTINS #endif /* !defined (JERRY_BUILTIN_ARRAY) */ #ifndef JERRY_BUILTIN_BOOLEAN #define JERRY_BUILTIN_BOOLEAN JERRY_BUILTINS #endif /* !defined (JERRY_BUILTIN_BOOLEAN) */ #ifndef JERRY_BUILTIN_MATH #define JERRY_BUILTIN_MATH JERRY_BUILTINS #endif /* !defined (JERRY_BUILTIN_MATH) */ #ifndef JERRY_BUILTIN_NUMBER #define JERRY_BUILTIN_NUMBER JERRY_BUILTINS #endif /* !defined (JERRY_BUILTIN_NUMBER) */ #ifndef JERRY_BUILTIN_STRING #define JERRY_BUILTIN_STRING JERRY_BUILTINS #endif /* !defined (JERRY_BUILTIN_STRING) */ #ifndef JERRY_BUILTIN_CONTAINER #define JERRY_BUILTIN_CONTAINER JERRY_BUILTINS #endif /* !defined (JERRY_BUILTIN_CONTAINER) */ #ifndef JERRY_BUILTIN_TYPEDARRAY #define JERRY_BUILTIN_TYPEDARRAY JERRY_BUILTINS #endif /* !defined (JERRY_BUILTIN_TYPEDARRAY) */ /** * Engine internal and misc configurations. */ /** * Enable/Disable built-in error messages for error objects. * * Allowed values: * 0: Disable error messages. * 1: Enable error message. * * Default value: 0 */ #ifndef JERRY_ERROR_MESSAGES #define JERRY_ERROR_MESSAGES 0 #endif /* !defined (JERRY_ERROR_MESSAGES) */ /** * Enable/Disable external context. * * Allowed values: * 0: Disable external context. * 1: Enable external context support. * * Default value: 0 */ #ifndef JERRY_EXTERNAL_CONTEXT #define JERRY_EXTERNAL_CONTEXT 0 #endif /* !defined (JERRY_EXTERNAL_CONTEXT) */ /** * Maximum size of heap in kilobytes * * Default value: 512 KiB */ #ifndef JERRY_GLOBAL_HEAP_SIZE #define JERRY_GLOBAL_HEAP_SIZE (512) #endif /* !defined (JERRY_GLOBAL_HEAP_SIZE) */ /** * The allowed heap usage limit until next garbage collection, in bytes. * * If value is 0, the default is 1/32 of JERRY_HEAP_SIZE */ #ifndef JERRY_GC_LIMIT #define JERRY_GC_LIMIT 0 #endif /* !defined (JERRY_GC_LIMIT) */ /** * Maximum stack usage size in kilobytes * * Note: This feature cannot be used when 'detect_stack_use_after_return=1' ASAN option is enabled. * For more detailed description: * - https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn#compatibility * * Default value: 0, unlimited */ #ifndef JERRY_STACK_LIMIT #define JERRY_STACK_LIMIT (0) #endif /* !defined (JERRY_STACK_LIMIT) */ /** * Maximum depth of recursion during GC mark phase * * Default value: 8 */ #ifndef JERRY_GC_MARK_LIMIT #define JERRY_GC_MARK_LIMIT (8) #endif /* !defined (JERRY_GC_MARK_LIMIT) */ /** * Enable/Disable property lookup cache. * * Allowed values: * 0: Disable lookup cache. * 1: Enable lookup cache. * * Default value: 1 */ #ifndef JERRY_LCACHE #define JERRY_LCACHE 1 #endif /* !defined (JERRY_LCACHE) */ /** * Use 32-bit/64-bit float for ecma-numbers * This option is for expert use only! * * Allowed values: * 1: use 64-bit floating point number mode * 0: use 32-bit floating point number mode * * Default value: 1 */ #ifndef JERRY_NUMBER_TYPE_FLOAT64 #define JERRY_NUMBER_TYPE_FLOAT64 1 #endif /* !defined (JERRY_NUMBER_TYPE_FLOAT64 */ /** * Enable/Disable the JavaScript parser. * * Allowed values: * 0: Disable the JavaScript parser and all related functionality. * 1: Enable the JavaScript parser. * * Default value: 1 */ #ifndef JERRY_PARSER #define JERRY_PARSER 1 #endif /* !defined (JERRY_PARSER) */ /** * Enable/Disable JerryScript byte code dump functions during parsing. * To dump the JerryScript byte code the engine must be initialized with opcodes * display flag. This option does not influence RegExp byte code dumps. * * Allowed values: * 0: Disable all bytecode dump functions. * 1: Enable bytecode dump functions. * * Default value: 0 */ #ifndef JERRY_PARSER_DUMP_BYTE_CODE #define JERRY_PARSER_DUMP_BYTE_CODE 0 #endif /* defined (JERRY_PARSER_DUMP_BYTE_CODE) */ /** * Enable/Disable ECMA property hashmap. * * Allowed values: * 0: Disable property hashmap. * 1: Enable property hashmap. * * Default value: 1 */ #ifndef JERRY_PROPERTY_HASHMAP #define JERRY_PROPERTY_HASHMAP 0 #endif /* !defined (JERRY_PROPERTY_HASHMAP) */ /** * Enable/Disable byte code dump functions for RegExp objects. * To dump the RegExp byte code the engine must be initialized with * regexp opcodes display flag. This option does not influence the * JerryScript byte code dumps. * * Allowed values: * 0: Disable all bytecode dump functions. * 1: Enable bytecode dump functions. * * Default value: 0 */ #ifndef JERRY_REGEXP_DUMP_BYTE_CODE #define JERRY_REGEXP_DUMP_BYTE_CODE 0 #endif /* !defined (JERRY_REGEXP_DUMP_BYTE_CODE) */ /** * Enable/Disable the snapshot execution functions. * * Allowed values: * 0: Disable snapshot execution. * 1: Enable snapshot execution. * * Default value: 0 */ #ifndef JERRY_SNAPSHOT_EXEC #define JERRY_SNAPSHOT_EXEC 0 #endif /* !defined (JERRY_SNAPSHOT_EXEC) */ /** * Enable/Disable the snapshot save functions. * * Allowed values: * 0: Disable snapshot save functions. * 1: Enable snapshot save functions. */ #ifndef JERRY_SNAPSHOT_SAVE #define JERRY_SNAPSHOT_SAVE 0 #endif /* !defined (JERRY_SNAPSHOT_SAVE) */ /** * Enable/Disable usage of system allocator. * * Allowed values: * 0: Disable usage of system allocator. * 1: Enable usage of system allocator. * * Default value: 0 */ #ifndef JERRY_SYSTEM_ALLOCATOR #define JERRY_SYSTEM_ALLOCATOR 0 #endif /* !defined (JERRY_SYSTEM_ALLOCATOR) */ /** * Enables/disables the unicode case conversion in the engine. * By default Unicode case conversion is enabled. */ #ifndef JERRY_UNICODE_CASE_CONVERSION #define JERRY_UNICODE_CASE_CONVERSION 0 #endif /* !defined (JERRY_UNICODE_CASE_CONVERSION) */ /** * Enable/Disable the vm execution stop callback function. * * Allowed values: * 0: Disable vm exec stop callback support. * 1: Enable vm exec stop callback support. */ #ifndef JERRY_VM_HALT #define JERRY_VM_HALT 0 #endif /* !defined (JERRY_VM_HALT) */ /** * Enable/Disable the vm throw callback function. * * Allowed values: * 0: Disable vm throw callback support. * 1: Enable vm throw callback support. */ #ifndef JERRY_VM_THROW #define JERRY_VM_THROW 0 #endif /* !defined (JERRY_VM_THROW) */ /** * Advanced section configurations. */ /** * Allow configuring attributes on a few constant data inside the engine. * * One of the main usages: * Normally compilers store const(ant)s in ROM. Thus saving RAM. * But if your compiler does not support it then the directive below can force it. * * For the moment it is mainly meant for the following targets: * - ESP8266 * * Example configuration for moving (some) constants into a given section: * # define JERRY_ATTR_CONST_DATA __attribute__((section(".rodata.const"))) */ #ifndef JERRY_ATTR_CONST_DATA #define JERRY_ATTR_CONST_DATA #endif /* !defined (JERRY_ATTR_CONST_DATA) */ /** * The JERRY_ATTR_GLOBAL_HEAP allows adding extra attributes for the Jerry global heap. * * Example on how to move the global heap into its own section: * #define JERRY_ATTR_GLOBAL_HEAP __attribute__((section(".text.globalheap"))) */ #ifndef JERRY_ATTR_GLOBAL_HEAP #define JERRY_ATTR_GLOBAL_HEAP #endif /* !defined (JERRY_ATTR_GLOBAL_HEAP) */ /** * Source name related types into a single guard */ #if JERRY_ERROR_MESSAGES #define JERRY_SOURCE_NAME 1 #else /* !(JERRY_ERROR_MESSAGES) */ #define JERRY_SOURCE_NAME 0 #endif /* JERRY_ERROR_MESSAGES */ #endif /* !JERRYSCRIPT_CONFIG_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/memorybuffer.h000664 001750 001750 00000004753 15164251010 037533 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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 struct GenericMemoryBuffer { typedef char Ch; // byte GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} void Put(Ch c) { *stack_.template Push() = c; } void Flush() {} void Clear() { stack_.Clear(); } void ShrinkToFit() { stack_.ShrinkToFit(); } Ch* Push(size_t count) { return stack_.template Push(count); } void Pop(size_t count) { stack_.template Pop(count); } const Ch* GetBuffer() const { return stack_.template Bottom(); } size_t GetSize() const { return stack_.GetSize(); } static const size_t kDefaultCapacity = 256; mutable internal::Stack 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(n), c, n * sizeof(c)); } RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_MEMORYBUFFER_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.gitignore000664 001750 001750 00000000116 15164251010 031203 0ustar00ddennedyddennedy000000 000000 build builddir doc .vscode .idea *.swp *.tvg *.tmp *.code-workspace .DS_Store glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/biginteger.h000664 001750 001750 00000022114 15164251010 040753 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules// 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. // // 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(__INTEL_COMPILER) && defined(_M_AMD64) #include // for _umul128 #if !defined(_ARM64EC_) #pragma intrinsic(_umul128) #else #pragma comment(lib,"softintrin") #endif #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; } template BigInteger(const Ch* 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_ + offset, digits_, 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(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: template void AppendDecimal64(const Ch* begin, const Ch* end) { uint64_t u = ParseUint64(begin, end); if (IsZero()) *this = u; else { unsigned exp = static_cast(end - begin); (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u } } void PushBack(Type digit) { RAPIDJSON_ASSERT(count_ < kCapacity); digits_[count_++] = digit; } template static uint64_t ParseUint64(const Ch* begin, const Ch* end) { uint64_t r = 0; for (const Ch* p = begin; p != end; ++p) { RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9')); r = r * 10u + static_cast(*p - Ch('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 defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) __extension__ typedef unsigned __int128 uint128; uint128 p = static_cast(a) * static_cast(b); p += k; *outHigh = static_cast(p >> 64); return static_cast(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(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_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieAnimation.cpp000664 001750 001750 00000010620 15164251010 037202 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgCommon.h" #include "thorvg_lottie.h" #include "tvgLottieLoader.h" #include "tvgAnimation.h" LottieAnimation::LottieAnimation() = default; LottieAnimation::~LottieAnimation() = default; uint32_t LottieAnimation::gen(const char* slot) noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return 0; return static_cast(loader)->gen(slot); } Result LottieAnimation::apply(uint32_t id) noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (static_cast(loader)->apply(id)) { PAINT(pImpl->picture)->mark(RenderUpdateFlag::All); return Result::Success; } return Result::InvalidArguments; } Result LottieAnimation::del(uint32_t id) noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (static_cast(loader)->del(id)) { PAINT(pImpl->picture)->mark(RenderUpdateFlag::All); return Result::Success; } return Result::InvalidArguments; } Result LottieAnimation::segment(const char* marker) noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (!marker) { static_cast(loader)->segment(0.0f, FLT_MAX); return Result::Success; } float begin, end; if (!static_cast(loader)->segment(marker, begin, end)) return Result::InvalidArguments; return Animation::segment(begin, end); } Result LottieAnimation::tween(float from, float to, float progress) noexcept { auto loader = tvg::to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (!static_cast(loader)->tween(from, to, progress)) return Result::InsufficientCondition; PAINT(pImpl->picture)->mark(RenderUpdateFlag::All); return Result::Success; } uint32_t LottieAnimation::markersCnt() noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return 0; return static_cast(loader)->markersCnt(); } const char* LottieAnimation::marker(uint32_t idx) noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return nullptr; return static_cast(loader)->markers(idx); } Result LottieAnimation::assign(const char* layer, uint32_t ix, const char* var, float val) { if (!layer || !var) return Result::InvalidArguments; auto loader = to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (static_cast(loader)->assign(layer, ix, var, val)) { PAINT(pImpl->picture)->mark(RenderUpdateFlag::All); return Result::Success; } return Result::NonSupport; } Result LottieAnimation::quality(uint8_t value) noexcept { if (value > 100) return Result::InvalidArguments; auto loader = to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (!static_cast(loader)->quality(value)) return Result::InsufficientCondition; return Result::Success; } LottieAnimation* LottieAnimation::gen() noexcept { return new LottieAnimation; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/meson.build000664 001750 001750 00000001601 15164251010 033752 0ustar00ddennedyddennedy000000 000000 engine_dep = [] if sw_engine subdir('sw_engine') endif if gl_engine subdir('gl_engine') endif if wg_engine subdir('wg_engine') endif source_file = [ 'tvgAnimation.h', 'tvgCanvas.h', 'tvgFill.h', 'tvgFrameModule.h', 'tvgLoader.h', 'tvgLoadModule.h', 'tvgPicture.h', 'tvgRender.h', 'tvgIteratorAccessor.h', 'tvgSaveModule.h', 'tvgScene.h', 'tvgShape.h', 'tvgTaskScheduler.h', 'tvgText.h', 'tvgAccessor.cpp', 'tvgAnimation.cpp', 'tvgCanvas.cpp', 'tvgFill.cpp', 'tvgInitializer.cpp', 'tvgLoader.cpp', 'tvgPaint.cpp', 'tvgPicture.cpp', 'tvgRender.cpp', 'tvgSaver.cpp', 'tvgScene.cpp', 'tvgShape.cpp', 'tvgTaskScheduler.cpp', 'tvgText.cpp' ] common_dep = declare_dependency( dependencies : engine_dep, include_directories : include_directories('.'), sources : source_file ) src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h000664 001750 001750 00000047634 15164251010 037352 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ namespace tvg { struct Vertex { Point pt; Point uv; }; struct Polygon { Vertex vertex[3]; }; //Careful! Shared resource, No support threading static float dudx, dvdx; static float dxdya, dxdyb, dudya, dvdya; static float xa, xb, ua, va; static inline int32_t _modf(float v) { return 255 - ((int(v * 256.0f)) & 255); } static uint8_t _feathering(int iru, int irv, int ar, int ab, int sw, int sh) { if (irv == 1) { if (iru == 1) return 255 - MULTIPLY(ar, ab); else if (iru == sw) return MULTIPLY(ar, 255 - ab); return 255 - ab; } else if (irv == sh) { if (iru == 1) return MULTIPLY(255 - ar, ab); else if (iru == sw) return MULTIPLY(ar, ab); return ab; } else { if (iru == 1) return 255 - ar; else if (iru == sw) return ar; } return 255; } static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, uint8_t opacity, bool needAA) { TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()"); return false; } static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, uint8_t opacity, bool needAA) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); return; } auto _dudx = dudx, _dvdx = dvdx; auto _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; auto _xa = xa, _xb = xb, _ua = ua, _va = va; auto sbuf = image.buf32; auto dbuf = surface->buf32; auto sw = static_cast(image.w); auto sh = static_cast(image.h); int32_t x1, x2, x, y, ar, ab, iru, irv, px; int32_t vv = 0, uu = 0; float dx, u, v; uint32_t* buf; if (yStart < bbox.min.y) yStart = bbox.min.y; if (yEnd > bbox.max.y) yEnd = bbox.max.y; y = yStart; while (y < yEnd) { x1 = std::max((int32_t)_xa, bbox.min.x); x2 = std::min((int32_t)_xb, bbox.max.x); //Range allowed if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) { //Perform subtexel pre-stepping on UV dx = 1 - (_xa - x1); u = _ua + dx * _dudx; v = _va + dx * _dvdx; buf = dbuf + ((y * surface->stride) + x1); x = x1; //Draw horizontal line while (x++ < x2) { uu = (int) u; vv = (int) v; if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue; ar = _modf(u); ab = _modf(v); iru = uu + 1; irv = vv + 1; px = *(sbuf + (vv * image.stride) + uu); // horizontal interpolate if (iru < sw) { int px2 = *(sbuf + (vv * image.stride) + iru); px = INTERPOLATE(px, px2, ar); } // vertical interpolate if (irv < sh) { int px2 = *(sbuf + (irv * image.stride) + uu); // horizontal interpolate if (iru < sw) { int px3 = *(sbuf + (irv * image.stride) + iru); px2 = INTERPOLATE(px2, px3, ar); } px = INTERPOLATE(px, px2, ab); } // anti-aliasing if (needAA) { auto feather = _feathering(iru, irv, ar, ab, sw, sh); if (feather < 255) px = ALPHA_BLEND(px, feather); } *buf = INTERPOLATE(surface->blender(rasterUnpremultiply(px), *buf), *buf, MULTIPLY(opacity, A(px))); ++buf; //Step UV horizontally u += _dudx; v += _dvdx; } } //Step along both edges _xa += _dxdya; _xb += _dxdyb; _ua += _dudya; _va += _dvdya; ++y; } xa = _xa; xb = _xb; ua = _ua; va = _va; } static void _rasterPolygonImageSegment32(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, uint8_t opacity, bool matting, bool needAA) { auto _dudx = dudx, _dvdx = dvdx; auto _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; auto _xa = xa, _xb = xb, _ua = ua, _va = va; auto sbuf = image.buf32; auto dbuf = surface->buf32; auto sw = static_cast(image.w); auto sh = static_cast(image.h); int32_t x1, x2, x, y, ar, ab, iru, irv, px; int32_t vv = 0, uu = 0; float dx, u, v; uint32_t* buf; auto fullOpacity = (opacity == 255); //for matting(composition) auto csize = matting ? surface->compositor->image.channelSize: 0; auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr; uint8_t* cmp = nullptr; if (yStart < bbox.min.y) yStart = bbox.min.y; if (yEnd > bbox.max.y) yEnd = bbox.max.y; y = yStart; while (y < yEnd) { x1 = std::max((int32_t)_xa, bbox.min.x); x2 = std::min((int32_t)_xb, bbox.max.x); //Range allowed if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) { //Perform subtexel pre-stepping on UV dx = 1 - (_xa - x1); u = _ua + dx * _dudx; v = _va + dx * _dvdx; buf = dbuf + ((y * surface->stride) + x1); x = x1; if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize]; //Draw horizontal line while (x++ < x2) { uu = (int) u; vv = (int) v; if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue; ar = _modf(u); ab = _modf(v); iru = uu + 1; irv = vv + 1; px = *(sbuf + (vv * image.stride) + uu); // horizontal interpolate if (iru < sw) { int px2 = *(sbuf + (vv * image.stride) + iru); px = INTERPOLATE(px, px2, ar); } // vertical interpolate if (irv < sh) { int px2 = *(sbuf + (irv * image.stride) + uu); // horizontal interpolate if (iru < sw) { int px3 = *(sbuf + (irv * image.stride) + iru); px2 = INTERPOLATE(px2, px3, ar); } px = INTERPOLATE(px, px2, ab); } uint32_t src; if (matting) { auto a = alpha(cmp); src = fullOpacity ? ALPHA_BLEND(px, a) : ALPHA_BLEND(px, MULTIPLY(opacity, a)); cmp += csize; } else { src = fullOpacity ? px : ALPHA_BLEND(px, opacity); } // anti-aliasing if (needAA) { auto feather = _feathering(iru, irv, ar, ab, sw, sh); if (feather < 255) src = ALPHA_BLEND(src, feather); } *buf = src + ALPHA_BLEND(*buf, IA(src)); ++buf; //Step UV horizontally u += _dudx; v += _dvdx; } } //Step along both edges _xa += _dxdya; _xb += _dxdyb; _ua += _dudya; _va += _dvdya; ++y; } xa = _xa; xb = _xb; ua = _ua; va = _va; } // no anti-aliasing, no interpolation for the fastest cheap masking static void _rasterPolygonImageSegment8(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, uint8_t opacity, TVG_UNUSED bool needAA) { auto _dudx = dudx, _dvdx = dvdx; auto _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; auto _xa = xa, _xb = xb, _ua = ua, _va = va; auto sbuf = image.buf32; auto dbuf = surface->buf8; int32_t x1, x2, x, y; float dx, u, v; uint8_t* buf; uint8_t px; if (yStart < bbox.min.y) yStart = bbox.min.y; if (yEnd > bbox.max.y) yEnd = bbox.max.y; y = yStart; while (y < yEnd) { x1 = std::max((int32_t)_xa, bbox.min.x); x2 = std::min((int32_t)_xb, bbox.max.x); //Range allowed if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) { //Perform subtexel pre-stepping on UV dx = 1 - (_xa - x1); u = _ua + dx * _dudx; v = _va + dx * _dvdx; buf = dbuf + ((y * surface->stride) + x1); x = x1; //Draw horizontal line while (x++ < x2) { auto uu = (int) u; auto vv = (int) v; if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue; px = A(*(sbuf + (vv * image.stride) + uu)); *buf = MULTIPLY(px, opacity); ++buf; //Step UV horizontally u += _dudx; v += _dvdx; } } //Step along both edges _xa += _dxdya; _xb += _dxdyb; _ua += _dudya; _va += _dvdya; ++y; } xa = _xa; xb = _xb; ua = _ua; va = _va; } static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, uint8_t opacity, bool matting, bool needAA) { if (surface->channelSize == sizeof(uint32_t)) _rasterPolygonImageSegment32(surface, image, bbox, yStart, yEnd, opacity, matting, needAA); else if (surface->channelSize == sizeof(uint8_t)) _rasterPolygonImageSegment8(surface, image, bbox, yStart, yEnd, opacity, needAA); } /* This mapping algorithm is based on Mikael Kalms's. */ static void _rasterPolygonImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, Polygon& polygon, uint8_t opacity, bool needAA) { float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x}; float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y}; float u[3] = {polygon.vertex[0].uv.x, polygon.vertex[1].uv.x, polygon.vertex[2].uv.x}; float v[3] = {polygon.vertex[0].uv.y, polygon.vertex[1].uv.y, polygon.vertex[2].uv.y}; float off_y; float dxdy[3] = {0.0f, 0.0f, 0.0f}; auto upper = false; //Sort the vertices in ascending Y order if (y[0] > y[1]) { std::swap(x[0], x[1]); std::swap(y[0], y[1]); std::swap(u[0], u[1]); std::swap(v[0], v[1]); } if (y[0] > y[2]) { std::swap(x[0], x[2]); std::swap(y[0], y[2]); std::swap(u[0], u[2]); std::swap(v[0], v[2]); } if (y[1] > y[2]) { std::swap(x[1], x[2]); std::swap(y[1], y[2]); std::swap(u[1], u[2]); std::swap(v[1], v[2]); } //Y indexes int yi[3] = {(int)y[0], (int)y[1], (int)y[2]}; //Skip drawing if it's too thin to cover any pixels at all. if ((yi[0] == yi[1] && yi[0] == yi[2]) || ((int) x[0] == (int) x[1] && (int) x[0] == (int) x[2])) return; //Calculate horizontal and vertical increments for UV axes (these calcs are certainly not optimal, although they're stable (handles any dy being 0) auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0])); //Skip poly if it's an infinitely thin line if (tvg::zero(denom)) return; denom = 1 / denom; //Reciprocal for speeding up dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom; dvdx = ((v[2] - v[0]) * (y[1] - y[0]) - (v[1] - v[0]) * (y[2] - y[0])) * denom; auto dudy = ((u[1] - u[0]) * (x[2] - x[0]) - (u[2] - u[0]) * (x[1] - x[0])) * denom; auto dvdy = ((v[1] - v[0]) * (x[2] - x[0]) - (v[2] - v[0]) * (x[1] - x[0])) * denom; //Calculate X-slopes along the edges if (y[1] > y[0]) dxdy[0] = (x[1] - x[0]) / (y[1] - y[0]); if (y[2] > y[0]) dxdy[1] = (x[2] - x[0]) / (y[2] - y[0]); if (y[2] > y[1]) dxdy[2] = (x[2] - x[1]) / (y[2] - y[1]); //Determine which side of the polygon the longer edge is on auto side = (dxdy[1] > dxdy[0]) ? true : false; if (tvg::equal(y[0], y[1])) side = x[0] > x[1]; if (tvg::equal(y[1], y[2])) side = x[2] > x[1]; auto compositing = _compositing(surface); //Composition required auto blending = _blending(surface); //Blending required //Longer edge is on the left side if (!side) { //Calculate slopes along left edge dxdya = dxdy[1]; dudya = dxdya * dudx + dudy; dvdya = dxdya * dvdx + dvdy; //Perform subpixel pre-stepping along left edge auto dy = 1.0f - (y[0] - yi[0]); xa = x[0] + dy * dxdya; ua = u[0] + dy * dudya; va = v[0] + dy * dvdya; //Draw upper segment if possibly visible if (yi[0] < yi[1]) { off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0; xa += (off_y * dxdya); ua += (off_y * dudya); va += (off_y * dvdya); // Set right edge X-slope and perform subpixel pre-stepping dxdyb = dxdy[0]; xb = x[0] + dy * dxdyb + (off_y * dxdyb); if (compositing) { if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], opacity, true, needAA); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], opacity, needAA); } else if (blending) { _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], opacity, needAA); } else { _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], opacity, false, needAA); } upper = true; } //Draw lower segment if possibly visible if (yi[1] < yi[2]) { off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0; if (!upper) { xa += (off_y * dxdya); ua += (off_y * dudya); va += (off_y * dvdya); } // Set right edge X-slope and perform subpixel pre-stepping dxdyb = dxdy[2]; xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb); if (compositing) { if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], opacity, true, needAA); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], opacity, needAA); } else if (blending) { _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], opacity, needAA); } else { _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], opacity, false, needAA); } } //Longer edge is on the right side } else { //Set right edge X-slope and perform subpixel pre-stepping dxdyb = dxdy[1]; auto dy = 1.0f - (y[0] - yi[0]); xb = x[0] + dy * dxdyb; //Draw upper segment if possibly visible if (yi[0] < yi[1]) { off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0; xb += (off_y *dxdyb); // Set slopes along left edge and perform subpixel pre-stepping dxdya = dxdy[0]; dudya = dxdya * dudx + dudy; dvdya = dxdya * dvdx + dvdy; xa = x[0] + dy * dxdya + (off_y * dxdya); ua = u[0] + dy * dudya + (off_y * dudya); va = v[0] + dy * dvdya + (off_y * dvdya); if (compositing) { if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], opacity, true, needAA); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], opacity, needAA); } else if (blending) { _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], opacity, needAA); } else { _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], opacity, false, needAA); } upper = true; } //Draw lower segment if possibly visible if (yi[1] < yi[2]) { off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0; if (!upper) xb += (off_y *dxdyb); // Set slopes along left edge and perform subpixel pre-stepping dxdya = dxdy[2]; dudya = dxdya * dudx + dudy; dvdya = dxdya * dvdx + dvdy; dy = 1 - (y[1] - yi[1]); xa = x[1] + dy * dxdya + (off_y * dxdya); ua = u[1] + dy * dudya + (off_y * dudya); va = v[1] + dy * dvdya + (off_y * dvdya); if (compositing) { if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], opacity, true, needAA); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], opacity, needAA); } else if (blending) { _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], opacity, needAA); } else { _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], opacity, false, needAA); } } } } } //namespace /* 2 triangles constructs 1 mesh. below figure illustrates vert[4] index info. If you need better quality, please divide a mesh by more number of triangles. 0 -- 1 | / | | / | 3 -- 2 */ bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity) { //Prepare vertices. Shift XY coordinates to match the sub-pixeling technique. Vertex vertices[4]; vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}}; vertices[1] = {{float(image.w), 0.0f}, {float(image.w), 0.0f}}; vertices[2] = {{float(image.w), float(image.h)}, {float(image.w), float(image.h)}}; vertices[3] = {{0.0f, float(image.h)}, {0.0f, float(image.h)}}; float ys = FLT_MAX, ye = -1.0f; for (int i = 0; i < 4; i++) { vertices[i].pt *= transform; if (vertices[i].pt.y < ys) ys = vertices[i].pt.y; if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; } auto needAA = rightAngle(transform) ? false : true; tvg::Polygon polygon; //Draw the first polygon polygon.vertex[0] = vertices[0]; polygon.vertex[1] = vertices[1]; polygon.vertex[2] = vertices[3]; _rasterPolygonImage(surface, image, bbox, polygon, opacity, needAA); //Draw the second polygon polygon.vertex[0] = vertices[1]; polygon.vertex[1] = vertices[2]; polygon.vertex[2] = vertices[3]; _rasterPolygonImage(surface, image, bbox, polygon, opacity, needAA); #if 0 if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) { _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); } #endif return true; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgCanvas.h000664 001750 001750 00000011774 15164251010 033731 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_CANVAS_H_ #define _TVG_CANVAS_H_ #include "tvgPaint.h" enum Status : uint8_t {Synced = 0, Painting, Updating, Drawing, Damaged}; struct Canvas::Impl { Scene* scene; RenderMethod* renderer; RenderRegion vport = {{0, 0}, {INT32_MAX, INT32_MAX}}; Status status = Status::Synced; Impl() : scene(Scene::gen()) { scene->ref(); } ~Impl() { //make it sure any deferred jobs renderer->sync(); scene->unref(); if (renderer->unref() == 0) delete(renderer); } Result add(Paint* target, Paint* at) { if (PAINT(target)->renderer && PAINT(target)->renderer != renderer) { TVGERR("RENDERER", "Target paint(%p) is already owned by a different renderer.", target); return Result::InsufficientCondition; } //You cannot add paints during rendering. if (status == Status::Drawing) { TVGLOG("RENDERER", "add() was called during drawing."); return Result::InsufficientCondition; } status = Status::Painting; return scene->add(target, at); } Result remove(Paint* paint) { if (status == Status::Drawing) { TVGLOG("RENDERER", "remove() was called during drawing."); return Result::InsufficientCondition; } status = Status::Painting; return scene->remove(paint); } Result update() { if (status == Status::Updating) return Result::Success; if (status == Status::Drawing) { TVGLOG("RENDERER", "update() was called during drawing."); return Result::InsufficientCondition; } Array clips; auto flag = RenderUpdateFlag::None; //TODO: All is too harsh, can be optimized. if (status == Status::Damaged) flag = RenderUpdateFlag::All; if (!renderer->preUpdate()) return Result::InsufficientCondition; auto m = tvg::identity(); PAINT(scene)->update(renderer, m, clips, 255, flag); if (!renderer->postUpdate()) return Result::InsufficientCondition; status = Status::Updating; return Result::Success; } Result draw(bool clear) { if (status == Status::Drawing) { TVGLOG("RENDERER", "draw() was called multiple times."); return Result::InsufficientCondition; } if (status == Status::Painting || status == Status::Damaged) update(); if (status != Status::Updating) return Result::InsufficientCondition; if (clear && !renderer->clear()) return Result::InsufficientCondition; if (!renderer->preRender()) return Result::InsufficientCondition; if (!PAINT(scene)->render(renderer) || !renderer->postRender()) return Result::InsufficientCondition; status = Status::Drawing; return Result::Success; } Result sync() { if (status == Status::Synced) return Result::Success; if (renderer->sync()) { status = Status::Synced; return Result::Success; } return Result::Unknown; } Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) { if (status == Status::Synced || status == Status::Damaged) { RenderRegion val = {{x, y}, {x + w, y + h}}; //intersect if the target buffer is already set. auto surface = renderer->mainSurface(); if (surface && surface->w > 0 && surface->h > 0) { val.intersect({{0, 0}, {(int32_t)surface->w, (int32_t)surface->h}}); } if (vport == val) return Result::Success; renderer->viewport(val); vport = val; status = Status::Damaged; return Result::Success; } TVGLOG("RENDERER", "viewport() is only allowed after sync."); return Result::InsufficientCondition; } }; #endif /* _TVG_CANVAS_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/meson.build000664 001750 001750 00000001152 15164251010 035652 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0source_file = [ 'tvgSwCommon.h', 'tvgSwRasterC.h', 'tvgSwRasterAvx.h', 'tvgSwRasterNeon.h', 'tvgSwRasterTexmap.h', 'tvgSwFill.cpp', 'tvgSwImage.cpp', 'tvgSwMath.cpp', 'tvgSwMemPool.cpp', 'tvgSwPostEffect.cpp', 'tvgSwRenderer.h', 'tvgSwRaster.cpp', 'tvgSwRenderer.cpp', 'tvgSwRle.cpp', 'tvgSwShape.cpp', 'tvgSwStroke.cpp', ] omp_dep = [] if openmp omp_dep = dependency('openmp', required: false) endif engine_dep += [declare_dependency( include_directories : include_directories('.'), dependencies : omp_dep, sources : source_file )]thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-promise.cpp000664 001750 001750 00000036563 15164251010 050067 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-handlers.h" #include "ecma-builtin-helpers.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-iterator-object.h" #include "ecma-number-object.h" #include "ecma-promise-object.h" #include "jcontext.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_PROMISE_ROUTINE_START = 0, ECMA_PROMISE_ROUTINE_REJECT, ECMA_PROMISE_ROUTINE_RESOLVE, ECMA_PROMISE_ROUTINE_RACE, ECMA_PROMISE_ROUTINE_ALL, ECMA_PROMISE_ROUTINE_ALLSETTLED, ECMA_PROMISE_ROUTINE_ANY, ECMA_PROMISE_ROUTINE_SPECIES_GET }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-promise.inc.h" #define BUILTIN_UNDERSCORED_ID promise #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup promise ECMA Promise object built-in * @{ */ /** * Runtime Semantics: PerformPromiseRace. * * See also: * ES2020 25.6.4.4.1 * * @return ecma value of the new promise. * Returned value must be freed with ecma_free_value. */ static inline ecma_value_t ecma_builtin_promise_perform_race (ecma_value_t iterator, /**< the iterator for race */ ecma_value_t next_method, /**< next method */ ecma_object_t *capability_obj_p, /**< PromiseCapability record */ ecma_value_t ctor, /**< Constructor value */ bool *done_p) /**< [out] iteratorRecord[[done]] */ { JERRY_ASSERT (ecma_is_value_object (iterator)); JERRY_ASSERT (ecma_object_class_is (capability_obj_p, ECMA_OBJECT_CLASS_PROMISE_CAPABILITY)); JERRY_ASSERT (ecma_is_constructor (ctor)); ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) capability_obj_p; ecma_value_t resolve = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (ctor), LIT_MAGIC_STRING_RESOLVE); if (ECMA_IS_VALUE_ERROR (resolve)) { return resolve; } if (!ecma_op_is_callable (resolve)) { ecma_free_value (resolve); return ecma_raise_type_error (ECMA_ERR_RESOLVE_METHOD_MUST_BE_CALLABLE); } ecma_object_t *resolve_func_p = ecma_get_object_from_value (resolve); ecma_value_t ret_value = ECMA_VALUE_ERROR; /* 5. */ while (true) { /* a. */ ecma_value_t next = ecma_op_iterator_step (iterator, next_method); /* b, c. */ if (ECMA_IS_VALUE_ERROR (next)) { goto done; } /* d. */ if (ecma_is_value_false (next)) { /* ii. */ ret_value = ecma_copy_value (capability_p->header.u.cls.u3.promise); goto done; } /* e. */ ecma_value_t next_val = ecma_op_iterator_value (next); ecma_free_value (next); /* f, g. */ if (ECMA_IS_VALUE_ERROR (next_val)) { goto done; } /* h. */ ecma_value_t next_promise = ecma_op_function_call (resolve_func_p, ctor, &next_val, 1); ecma_free_value (next_val); if (ECMA_IS_VALUE_ERROR (next_promise)) { goto exit; } /* i. */ ecma_value_t args[2] = { capability_p->resolve, capability_p->reject }; ecma_value_t result = ecma_op_invoke_by_magic_id (next_promise, LIT_MAGIC_STRING_THEN, args, 2); ecma_free_value (next_promise); if (ECMA_IS_VALUE_ERROR (result)) { goto exit; } ecma_free_value (result); } done: *done_p = true; exit: ecma_deref_object (resolve_func_p); return ret_value; } /* ecma_builtin_promise_perform_race */ /** * Runtime Semantics: Perform Promise all, allSettled or any. * * See also: * ES2020 25.6.4.1.1 * * @return ecma value of the new promise. * Returned value must be freed with ecma_free_value. */ static inline ecma_value_t ecma_builtin_promise_perform (ecma_value_t iterator, /**< iteratorRecord */ ecma_value_t next_method, /**< next method */ ecma_object_t *capability_obj_p, /**< PromiseCapability record */ ecma_value_t ctor, /**< the caller of Promise.all */ uint8_t builtin_routine_id, /**< built-in wide routine identifier */ bool *done_p) /**< [out] iteratorRecord[[done]] */ { /* 1. - 2. */ JERRY_ASSERT (ecma_object_class_is (capability_obj_p, ECMA_OBJECT_CLASS_PROMISE_CAPABILITY)); JERRY_ASSERT (ecma_is_constructor (ctor)); ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) capability_obj_p; ecma_value_t resolve = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (ctor), LIT_MAGIC_STRING_RESOLVE); if (ECMA_IS_VALUE_ERROR (resolve)) { return resolve; } if (!ecma_op_is_callable (resolve)) { ecma_free_value (resolve); return ecma_raise_type_error (ECMA_ERR_RESOLVE_METHOD_MUST_BE_CALLABLE); } ecma_object_t *resolve_func_p = ecma_get_object_from_value (resolve); /* 3. */ ecma_object_t *values_array_obj_p = ecma_op_new_array_object (0); ecma_value_t values_array = ecma_make_object_value (values_array_obj_p); /* 4. */ ecma_value_t remaining = ecma_op_create_number_object (ecma_make_integer_value (1)); /* 5. */ uint32_t idx = 0; ecma_value_t ret_value = ECMA_VALUE_ERROR; /* 6. */ while (true) { /* a. */ ecma_value_t next = ecma_op_iterator_step (iterator, next_method); /* b. - c. */ if (ECMA_IS_VALUE_ERROR (next)) { goto done; } /* d. */ if (ecma_is_value_false (next)) { /* ii. - iii. */ if (ecma_promise_remaining_inc_or_dec (remaining, false) == 0) { if (builtin_routine_id == ECMA_PROMISE_ROUTINE_ANY) { ret_value = ecma_raise_aggregate_error (values_array, ECMA_VALUE_UNDEFINED); goto done; } /* 2. */ ecma_value_t resolve_result = ecma_op_function_call (ecma_get_object_from_value (capability_p->resolve), ECMA_VALUE_UNDEFINED, &values_array, 1); /* 3. */ if (ECMA_IS_VALUE_ERROR (resolve_result)) { goto done; } ecma_free_value (resolve_result); } /* iv. */ ret_value = ecma_copy_value (capability_p->header.u.cls.u3.promise); goto done; } /* e. */ ecma_value_t next_value = ecma_op_iterator_value (next); ecma_free_value (next); /* f. - g. */ if (ECMA_IS_VALUE_ERROR (next_value)) { goto done; } /* h. */ ecma_builtin_helper_def_prop_by_index (values_array_obj_p, idx, ECMA_VALUE_UNDEFINED, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); /* i. */ ecma_value_t next_promise = ecma_op_function_call (resolve_func_p, ctor, &next_value, 1); ecma_free_value (next_value); /* j. */ if (ECMA_IS_VALUE_ERROR (next_promise)) { goto exit; } if (JERRY_UNLIKELY (idx == UINT32_MAX - 1)) { ecma_raise_range_error (ECMA_ERR_PROMISE_ALL_REMAINING_ELEMENTS_LIMIT_REACHED); goto exit; } idx++; ecma_value_t args[2]; ecma_object_t *executor_func_p = NULL; if (builtin_routine_id != ECMA_PROMISE_ROUTINE_ANY) { /* k. */ executor_func_p = ecma_op_create_native_handler (ECMA_NATIVE_HANDLER_PROMISE_ALL_HELPER, sizeof (ecma_promise_all_executor_t)); ecma_promise_all_executor_t *executor_p = (ecma_promise_all_executor_t *) executor_func_p; /* m. + t. */ executor_p->index = idx; /* n. */ executor_p->values = values_array; /* o. */ executor_p->capability = ecma_make_object_value (capability_obj_p); /* p. */ executor_p->remaining_elements = remaining; uint8_t executor_type = ECMA_PROMISE_ALL_RESOLVE << ECMA_NATIVE_HANDLER_COMMON_FLAGS_SHIFT; if (builtin_routine_id == ECMA_PROMISE_ROUTINE_ALLSETTLED) { executor_type = ECMA_PROMISE_ALLSETTLED_RESOLVE << ECMA_NATIVE_HANDLER_COMMON_FLAGS_SHIFT; } executor_p->header.u.built_in.u2.routine_flags |= executor_type; args[0] = ecma_make_object_value (executor_func_p); } else { args[0] = capability_p->resolve; } /* q. */ ecma_promise_remaining_inc_or_dec (remaining, true); ecma_value_t result; if (builtin_routine_id != ECMA_PROMISE_ROUTINE_ALL) { uint8_t executor_type = ECMA_PROMISE_ALLSETTLED_REJECT << ECMA_NATIVE_HANDLER_COMMON_FLAGS_SHIFT; if (builtin_routine_id == ECMA_PROMISE_ROUTINE_ANY) { executor_type = ECMA_PROMISE_ANY_REJECT << ECMA_NATIVE_HANDLER_COMMON_FLAGS_SHIFT; } ecma_object_t *reject_func_p = ecma_op_create_native_handler (ECMA_NATIVE_HANDLER_PROMISE_ALL_HELPER, sizeof (ecma_promise_all_executor_t)); ecma_promise_all_executor_t *reject_p = (ecma_promise_all_executor_t *) reject_func_p; reject_p->index = idx; reject_p->values = values_array; reject_p->capability = ecma_make_object_value (capability_obj_p); reject_p->remaining_elements = remaining; reject_p->header.u.built_in.u2.routine_flags |= executor_type; args[1] = ecma_make_object_value (reject_func_p); result = ecma_op_invoke_by_magic_id (next_promise, LIT_MAGIC_STRING_THEN, args, 2); ecma_deref_object (reject_func_p); } else { args[1] = capability_p->reject; result = ecma_op_invoke_by_magic_id (next_promise, LIT_MAGIC_STRING_THEN, args, 2); } ecma_free_value (next_promise); if (builtin_routine_id != ECMA_PROMISE_ROUTINE_ANY) { ecma_deref_object (executor_func_p); } /* s. */ if (ECMA_IS_VALUE_ERROR (result)) { goto exit; } ecma_free_value (result); } done: *done_p = true; exit: ecma_free_value (remaining); ecma_deref_object (values_array_obj_p); ecma_deref_object (resolve_func_p); return ret_value; } /* ecma_builtin_promise_perform */ /** * The common function for Promise.race, Promise.all, Promise.any and Promise.allSettled. * * @return ecma value of the new promise. * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_promise_helper (ecma_value_t this_arg, /**< 'this' argument */ ecma_value_t iterable, /**< the items to be resolved */ uint8_t builtin_routine_id) /**< built-in wide routine identifier */ { ecma_object_t *capability_obj_p = ecma_promise_new_capability (this_arg, ECMA_VALUE_UNDEFINED); if (JERRY_UNLIKELY (capability_obj_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t next_method; ecma_value_t iterator = ecma_op_get_iterator (iterable, ECMA_VALUE_SYNC_ITERATOR, &next_method); ecma_value_t ret = ECMA_VALUE_ERROR; if (ECMA_IS_VALUE_ERROR (ecma_op_if_abrupt_reject_promise (&iterator, capability_obj_p))) { ecma_free_value (next_method); ecma_deref_object (capability_obj_p); return iterator; } bool is_done = false; if (builtin_routine_id == ECMA_PROMISE_ROUTINE_RACE) { ret = ecma_builtin_promise_perform_race (iterator, next_method, capability_obj_p, this_arg, &is_done); } else { ret = ecma_builtin_promise_perform (iterator, next_method, capability_obj_p, this_arg, builtin_routine_id, &is_done); } if (ECMA_IS_VALUE_ERROR (ret)) { if (!is_done) { ret = ecma_op_iterator_close (iterator); } ecma_op_if_abrupt_reject_promise (&ret, capability_obj_p); } ecma_free_value (iterator); ecma_free_value (next_method); ecma_deref_object (capability_obj_p); return ret; } /* ecma_builtin_promise_helper */ /** * Handle calling [[Call]] of built-in Promise object. * * ES2015 25.4.3 Promise is not intended to be called * as a function and will throw an exception when called * in that manner. * * @return ecma value */ ecma_value_t ecma_builtin_promise_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_PROMISE_REQUIRES_NEW); } /* ecma_builtin_promise_dispatch_call */ /** * Handle calling [[Construct]] of built-in Promise object. * * @return ecma value */ ecma_value_t ecma_builtin_promise_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); if (arguments_list_len == 0 || !ecma_op_is_callable (arguments_list_p[0])) { return ecma_raise_type_error (ECMA_ERR_FIRST_PARAMETER_MUST_BE_CALLABLE); } return ecma_op_create_promise_object (arguments_list_p[0], ECMA_VALUE_UNDEFINED, JERRY_CONTEXT (current_new_target_p)); } /* ecma_builtin_promise_dispatch_construct */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_promise_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); switch (builtin_routine_id) { case ECMA_PROMISE_ROUTINE_REJECT: case ECMA_PROMISE_ROUTINE_RESOLVE: { bool is_resolve = (builtin_routine_id == ECMA_PROMISE_ROUTINE_RESOLVE); return ecma_promise_reject_or_resolve (this_arg, arguments_list_p[0], is_resolve); } case ECMA_PROMISE_ROUTINE_RACE: case ECMA_PROMISE_ROUTINE_ALL: case ECMA_PROMISE_ROUTINE_ALLSETTLED: case ECMA_PROMISE_ROUTINE_ANY: { return ecma_builtin_promise_helper (this_arg, arguments_list_p[0], builtin_routine_id); } case ECMA_PROMISE_ROUTINE_SPECIES_GET: { return ecma_copy_value (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_promise_dispatch_routine */ /** * @} * @} * @} */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-char-helpers.cpp000664 001750 001750 00000104313 15164251010 043760 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "lit-char-helpers.h" #include "ecma-helpers.h" #include "jerry-config.h" #include "lit-strings.h" #include "lit-unicode-ranges-sup.inc.h" #include "lit-unicode-ranges.inc.h" #if JERRY_UNICODE_CASE_CONVERSION #include "lit-unicode-conversions-sup.inc.h" #include "lit-unicode-conversions.inc.h" #include "lit-unicode-folding.inc.h" #endif /* JERRY_UNICODE_CASE_CONVERSION */ #define NUM_OF_ELEMENTS(array) (sizeof (array) / sizeof ((array)[0])) /** * Binary search algorithm that searches the a * character in the given char array. * * @return true - if the character is in the given array * false - otherwise */ #define LIT_SEARCH_CHAR_IN_ARRAY_FN(function_name, char_type, array_type) \ static bool function_name (char_type c, /**< code unit */ \ const array_type *array, /**< array */ \ int size_of_array) /**< length of the array */ \ { \ int bottom = 0; \ int top = size_of_array - 1; \ \ while (bottom <= top) \ { \ int middle = (bottom + top) / 2; \ char_type current = array[middle]; \ \ if (current == c) \ { \ return true; \ } \ \ if (c < current) \ { \ top = middle - 1; \ } \ else \ { \ bottom = middle + 1; \ } \ } \ \ return false; \ } /* __function_name */ LIT_SEARCH_CHAR_IN_ARRAY_FN (lit_search_char_in_array, ecma_char_t, uint16_t) LIT_SEARCH_CHAR_IN_ARRAY_FN (lit_search_codepoint_in_array, lit_code_point_t, uint32_t) /** * Binary search algorithm that searches a character in the given intervals. * Intervals specified by two arrays. The first one contains the starting points * of the intervals, the second one contains the length of them. * * @return true - if the character is included (inclusively) in one of the intervals in the given array * false - otherwise */ #define LIT_SEARCH_CHAR_IN_INTERVAL_ARRAY_FN(function_name, char_type, array_type, interval_type) \ static bool function_name (char_type c, /**< code unit */ \ const array_type *array_sp, /**< array of interval starting points */ \ const interval_type *lengths, /**< array of interval lengths */ \ int size_of_array) /**< length of the array */ \ { \ int bottom = 0; \ int top = size_of_array - 1; \ \ while (bottom <= top) \ { \ int middle = (bottom + top) / 2; \ char_type current_sp = array_sp[middle]; \ \ if (current_sp <= c && c <= current_sp + lengths[middle]) \ { \ return true; \ } \ \ if (c > current_sp) \ { \ bottom = middle + 1; \ } \ else \ { \ top = middle - 1; \ } \ } \ \ return false; \ } /* function_name */ LIT_SEARCH_CHAR_IN_INTERVAL_ARRAY_FN (lit_search_char_in_interval_array, ecma_char_t, uint16_t, uint8_t) LIT_SEARCH_CHAR_IN_INTERVAL_ARRAY_FN (lit_search_codepoint_in_interval_array, lit_code_point_t, uint32_t, uint16_t) /** * Check if specified character is one of the Whitespace characters including those that fall into * "Space, Separator" ("Zs") Unicode character category or one of the Line Terminator characters. * * @return true - if the character is one of characters, listed in ECMA-262 v5, Table 2, * false - otherwise */ bool lit_char_is_white_space (lit_code_point_t c) /**< code point */ { if (c <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { return (c == LIT_CHAR_SP || (c >= LIT_CHAR_TAB && c <= LIT_CHAR_CR)); } if (c == LIT_CHAR_BOM || c == LIT_CHAR_LS || c == LIT_CHAR_PS) { return true; } return (c <= LIT_UTF16_CODE_UNIT_MAX && ((c >= lit_unicode_white_space_interval_starts[0] && c <= lit_unicode_white_space_interval_starts[0] + lit_unicode_white_space_interval_lengths[0]) || lit_search_char_in_array ((ecma_char_t) c, lit_unicode_white_space_chars, NUM_OF_ELEMENTS (lit_unicode_white_space_chars)))); } /* lit_char_is_white_space */ /** * Check if specified character is one of LineTerminator characters * * @return true - if the character is one of characters, listed in ECMA-262 v5, Table 3, * false - otherwise */ bool lit_char_is_line_terminator (ecma_char_t c) /**< code unit */ { return (c == LIT_CHAR_LF || c == LIT_CHAR_CR || c == LIT_CHAR_LS || c == LIT_CHAR_PS); } /* lit_char_is_line_terminator */ /** * Check if specified character is a Unicode ID_Start * * See also: * ECMA-262 v1, 11.6: UnicodeIDStart * * @return true - if the codepoint has Unicode property "ID_Start" * false - otherwise */ static bool lit_char_is_unicode_id_start (lit_code_point_t code_point) /**< code unit */ { if (JERRY_UNLIKELY (code_point >= LIT_UTF8_4_BYTE_CODE_POINT_MIN)) { return (lit_search_codepoint_in_interval_array (code_point, lit_unicode_id_start_interval_starts_sup, lit_unicode_id_start_interval_lengths_sup, NUM_OF_ELEMENTS (lit_unicode_id_start_interval_starts_sup)) || lit_search_codepoint_in_array (code_point, lit_unicode_id_start_chars_sup, NUM_OF_ELEMENTS (lit_unicode_id_start_chars_sup))); } ecma_char_t c = (ecma_char_t) code_point; return (lit_search_char_in_interval_array (c, lit_unicode_id_start_interval_starts, lit_unicode_id_start_interval_lengths, NUM_OF_ELEMENTS (lit_unicode_id_start_interval_starts)) || lit_search_char_in_array (c, lit_unicode_id_start_chars, NUM_OF_ELEMENTS (lit_unicode_id_start_chars))); } /* lit_char_is_unicode_id_start */ /** * Check if specified character is a Unicode ID_Continue * * See also: * ECMA-262 v1, 11.6: UnicodeIDContinue * * @return true - if the codepoint has Unicode property "ID_Continue" * false - otherwise */ static bool lit_char_is_unicode_id_continue (lit_code_point_t code_point) /**< code unit */ { /* Each ID_Start codepoint is ID_Continue as well. */ if (lit_char_is_unicode_id_start (code_point)) { return true; } if (JERRY_UNLIKELY (code_point >= LIT_UTF8_4_BYTE_CODE_POINT_MIN)) { return (lit_search_codepoint_in_interval_array (code_point, lit_unicode_id_continue_interval_starts_sup, lit_unicode_id_continue_interval_lengths_sup, NUM_OF_ELEMENTS (lit_unicode_id_continue_interval_starts_sup)) || lit_search_codepoint_in_array (code_point, lit_unicode_id_continue_chars_sup, NUM_OF_ELEMENTS (lit_unicode_id_continue_chars_sup))); } ecma_char_t c = (ecma_char_t) code_point; return ( lit_search_char_in_interval_array (c, lit_unicode_id_continue_interval_starts, lit_unicode_id_continue_interval_lengths, NUM_OF_ELEMENTS (lit_unicode_id_continue_interval_starts)) || lit_search_char_in_array (c, lit_unicode_id_continue_chars, NUM_OF_ELEMENTS (lit_unicode_id_continue_chars))); } /* lit_char_is_unicode_id_continue */ /** * Checks whether the character is a valid identifier start. * * @return true if it is. */ bool lit_code_point_is_identifier_start (lit_code_point_t code_point) /**< code point */ { /* Fast path for ASCII-defined letters. */ if (code_point <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { return ((LEXER_TO_ASCII_LOWERCASE (code_point) >= LIT_CHAR_LOWERCASE_A && LEXER_TO_ASCII_LOWERCASE (code_point) <= LIT_CHAR_LOWERCASE_Z) || code_point == LIT_CHAR_DOLLAR_SIGN || code_point == LIT_CHAR_UNDERSCORE); } return lit_char_is_unicode_id_start (code_point); } /* lit_code_point_is_identifier_start */ /** * Checks whether the character is a valid identifier part. * * @return true if it is. */ bool lit_code_point_is_identifier_part (lit_code_point_t code_point) /**< code point */ { /* Fast path for ASCII-defined letters. */ if (code_point <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { return ((LEXER_TO_ASCII_LOWERCASE (code_point) >= LIT_CHAR_LOWERCASE_A && LEXER_TO_ASCII_LOWERCASE (code_point) <= LIT_CHAR_LOWERCASE_Z) || (code_point >= LIT_CHAR_0 && code_point <= LIT_CHAR_9) || code_point == LIT_CHAR_DOLLAR_SIGN || code_point == LIT_CHAR_UNDERSCORE); } return lit_char_is_unicode_id_continue (code_point); } /* lit_code_point_is_identifier_part */ /** * Check if specified character is one of OctalDigit characters (ECMA-262 v5, B.1.2) * * @return true / false */ bool lit_char_is_octal_digit (ecma_char_t c) /**< code unit */ { return (c >= LIT_CHAR_ASCII_OCTAL_DIGITS_BEGIN && c <= LIT_CHAR_ASCII_OCTAL_DIGITS_END); } /* lit_char_is_octal_digit */ /** * Check if specified character is one of DecimalDigit characters (ECMA-262 v5, 7.8.3) * * @return true / false */ bool lit_char_is_decimal_digit (ecma_char_t c) /**< code unit */ { return (c >= LIT_CHAR_ASCII_DIGITS_BEGIN && c <= LIT_CHAR_ASCII_DIGITS_END); } /* lit_char_is_decimal_digit */ /** * Check if specified character is one of HexDigit characters (ECMA-262 v5, 7.8.3) * * @return true / false */ bool lit_char_is_hex_digit (ecma_char_t c) /**< code unit */ { return ((c >= LIT_CHAR_ASCII_DIGITS_BEGIN && c <= LIT_CHAR_ASCII_DIGITS_END) || (LEXER_TO_ASCII_LOWERCASE (c) >= LIT_CHAR_ASCII_LOWERCASE_LETTERS_HEX_BEGIN && LEXER_TO_ASCII_LOWERCASE (c) <= LIT_CHAR_ASCII_LOWERCASE_LETTERS_HEX_END)); } /* lit_char_is_hex_digit */ /** * Check if specified character is one of BinaryDigits characters (ECMA-262 v6, 11.8.3) * * @return true / false */ bool lit_char_is_binary_digit (ecma_char_t c) /** code unit */ { return (c == LIT_CHAR_0 || c == LIT_CHAR_1); } /* lit_char_is_binary_digit */ /** * @return radix value */ uint8_t lit_char_to_radix (lit_utf8_byte_t c) /** code unit */ { switch (LEXER_TO_ASCII_LOWERCASE (c)) { case LIT_CHAR_LOWERCASE_X: { return 16; } case LIT_CHAR_LOWERCASE_O: { return 8; } case LIT_CHAR_LOWERCASE_B: { return 2; } default: { return 10; } } } /* lit_char_to_radix */ /** * UnicodeEscape abstract method * * See also: ECMA-262 v10, 24.5.2.3 */ void lit_char_unicode_escape (ecma_stringbuilder_t *builder_p, /**< stringbuilder to append */ ecma_char_t c) /**< code unit to convert */ { ecma_stringbuilder_append_raw (builder_p, (lit_utf8_byte_t *) "\\u", 2); for (int8_t i = 3; i >= 0; i--) { int32_t result_char = (c >> (i * 4)) & 0xF; ecma_stringbuilder_append_byte ( builder_p, (lit_utf8_byte_t) (result_char + (result_char <= 9 ? LIT_CHAR_0 : (LIT_CHAR_LOWERCASE_A - 10)))); } } /* lit_char_unicode_escape */ /** * Convert a HexDigit character to its numeric value, as defined in ECMA-262 v5, 7.8.3 * * @return digit value, corresponding to the hex char */ uint32_t lit_char_hex_to_int (ecma_char_t c) /**< code unit, corresponding to * one of HexDigit characters */ { JERRY_ASSERT (lit_char_is_hex_digit (c)); if (c >= LIT_CHAR_ASCII_DIGITS_BEGIN && c <= LIT_CHAR_ASCII_DIGITS_END) { return (uint32_t) (c - LIT_CHAR_ASCII_DIGITS_BEGIN); } const uint32_t hex_offset = 10 - (LIT_CHAR_LOWERCASE_A % 32); return (c % 32) + hex_offset; } /* lit_char_hex_to_int */ /** * Converts a character to UTF8 bytes. * * @return length of the UTF8 representation. */ size_t lit_code_point_to_cesu8_bytes (uint8_t *dst_p, /**< destination buffer */ lit_code_point_t code_point) /**< code point */ { if (code_point < LIT_UTF8_2_BYTE_CODE_POINT_MIN) { /* 00000000 0xxxxxxx -> 0xxxxxxx */ dst_p[0] = (uint8_t) code_point; return 1; } if (code_point < LIT_UTF8_3_BYTE_CODE_POINT_MIN) { /* 00000yyy yyxxxxxx -> 110yyyyy 10xxxxxx */ dst_p[0] = (uint8_t) (LIT_UTF8_2_BYTE_MARKER | ((code_point >> 6) & LIT_UTF8_LAST_5_BITS_MASK)); dst_p[1] = (uint8_t) (LIT_UTF8_EXTRA_BYTE_MARKER | (code_point & LIT_UTF8_LAST_6_BITS_MASK)); return 2; } if (code_point < LIT_UTF8_4_BYTE_CODE_POINT_MIN) { /* zzzzyyyy yyxxxxxx -> 1110zzzz 10yyyyyy 10xxxxxx */ dst_p[0] = (uint8_t) (LIT_UTF8_3_BYTE_MARKER | ((code_point >> 12) & LIT_UTF8_LAST_4_BITS_MASK)); dst_p[1] = (uint8_t) (LIT_UTF8_EXTRA_BYTE_MARKER | ((code_point >> 6) & LIT_UTF8_LAST_6_BITS_MASK)); dst_p[2] = (uint8_t) (LIT_UTF8_EXTRA_BYTE_MARKER | (code_point & LIT_UTF8_LAST_6_BITS_MASK)); return 3; } JERRY_ASSERT (code_point <= LIT_UNICODE_CODE_POINT_MAX); code_point -= LIT_UTF8_4_BYTE_CODE_POINT_MIN; dst_p[0] = (uint8_t) (LIT_UTF8_3_BYTE_MARKER | 0xd); dst_p[1] = (uint8_t) (LIT_UTF8_EXTRA_BYTE_MARKER | 0x20 | ((code_point >> 16) & LIT_UTF8_LAST_4_BITS_MASK)); dst_p[2] = (uint8_t) (LIT_UTF8_EXTRA_BYTE_MARKER | ((code_point >> 10) & LIT_UTF8_LAST_6_BITS_MASK)); dst_p[3] = (uint8_t) (LIT_UTF8_3_BYTE_MARKER | 0xd); dst_p[4] = (uint8_t) (LIT_UTF8_EXTRA_BYTE_MARKER | 0x30 | ((code_point >> 6) & LIT_UTF8_LAST_4_BITS_MASK)); dst_p[5] = (uint8_t) (LIT_UTF8_EXTRA_BYTE_MARKER | (code_point & LIT_UTF8_LAST_6_BITS_MASK)); return 3 * 2; } /* lit_code_point_to_cesu8_bytes */ /** * Returns the length of the UTF8 representation of a character. * * @return length of the UTF8 representation. */ size_t lit_code_point_get_cesu8_length (lit_code_point_t code_point) /**< code point */ { if (code_point < LIT_UTF8_2_BYTE_CODE_POINT_MIN) { /* 00000000 0xxxxxxx */ return 1; } if (code_point < LIT_UTF8_3_BYTE_CODE_POINT_MIN) { /* 00000yyy yyxxxxxx */ return 2; } if (code_point < LIT_UTF8_4_BYTE_CODE_POINT_MIN) { /* zzzzyyyy yyxxxxxx */ return 3; } /* high + low surrogate */ return 2 * 3; } /* lit_code_point_get_cesu8_length */ /** * Convert a four byte long utf8 character to two three byte long cesu8 characters */ void lit_four_byte_utf8_char_to_cesu8 (uint8_t *dst_p, /**< destination buffer */ const uint8_t *source_p) /**< source buffer */ { lit_code_point_t code_point = ((((uint32_t) source_p[0]) & LIT_UTF8_LAST_3_BITS_MASK) << 18); code_point |= ((((uint32_t) source_p[1]) & LIT_UTF8_LAST_6_BITS_MASK) << 12); code_point |= ((((uint32_t) source_p[2]) & LIT_UTF8_LAST_6_BITS_MASK) << 6); code_point |= (((uint32_t) source_p[3]) & LIT_UTF8_LAST_6_BITS_MASK); lit_code_point_to_cesu8_bytes (dst_p, code_point); } /* lit_four_byte_utf8_char_to_cesu8 */ /** * Lookup hex digits in a buffer * * @return UINT32_MAX - if next 'lookup' number of characters do not form a valid hex number * value of hex number, otherwise */ uint32_t lit_char_hex_lookup (const lit_utf8_byte_t *buf_p, /**< buffer */ const lit_utf8_byte_t *const buf_end_p, /**< buffer end */ uint32_t lookup) /**< size of lookup */ { JERRY_ASSERT (lookup <= 4); if (JERRY_UNLIKELY (buf_p + lookup > buf_end_p)) { return UINT32_MAX; } uint32_t value = 0; while (lookup--) { lit_utf8_byte_t ch = *buf_p++; if (!lit_char_is_hex_digit (ch)) { return UINT32_MAX; } value <<= 4; value += lit_char_hex_to_int (ch); } JERRY_ASSERT (value <= LIT_UTF16_CODE_UNIT_MAX); return value; } /* lit_char_hex_lookup */ /** * Parse a decimal number with the value clamped to UINT32_MAX. * * @returns uint32_t number */ uint32_t lit_parse_decimal (const lit_utf8_byte_t **buffer_p, /**< [in/out] character buffer */ const lit_utf8_byte_t *buffer_end_p) /**< buffer end */ { const lit_utf8_byte_t *current_p = *buffer_p; JERRY_ASSERT (lit_char_is_decimal_digit (*current_p)); uint32_t value = (uint32_t) (*current_p++ - LIT_CHAR_0); while (current_p < buffer_end_p && lit_char_is_decimal_digit (*current_p)) { const uint32_t digit = (uint32_t) (*current_p++ - LIT_CHAR_0); uint32_t new_value = value * 10 + digit; if (JERRY_UNLIKELY (value > UINT32_MAX / 10) || JERRY_UNLIKELY (new_value < value)) { value = UINT32_MAX; continue; } value = new_value; } *buffer_p = current_p; return value; } /* lit_parse_decimal */ /** * Check if specified character is a word character (part of IsWordChar abstract operation) * * See also: ECMA-262 v5, 15.10.2.6 (IsWordChar) * * @return true - if the character is a word character * false - otherwise */ bool lit_char_is_word_char (lit_code_point_t c) /**< code point */ { return ((c >= LIT_CHAR_ASCII_LOWERCASE_LETTERS_BEGIN && c <= LIT_CHAR_ASCII_LOWERCASE_LETTERS_END) || (c >= LIT_CHAR_ASCII_UPPERCASE_LETTERS_BEGIN && c <= LIT_CHAR_ASCII_UPPERCASE_LETTERS_END) || (c >= LIT_CHAR_ASCII_DIGITS_BEGIN && c <= LIT_CHAR_ASCII_DIGITS_END) || c == LIT_CHAR_UNDERSCORE); } /* lit_char_is_word_char */ #if JERRY_UNICODE_CASE_CONVERSION /** * Check if the specified character is in one of those tables which contain bidirectional conversions. * * @return codepoint of the converted character if it is found the tables * LIT_INVALID_CP - otherwise. */ static lit_code_point_t lit_search_in_bidirectional_conversion_tables (lit_code_point_t cp, /**< code point */ bool is_lowercase) /**< is lowercase conversion */ { /* 1, Check if the specified character is part of the lit_unicode_character_case_ranges_{sup} table. */ int number_of_case_ranges; bool is_supplementary = cp > LIT_UTF16_CODE_UNIT_MAX; if (is_supplementary) { number_of_case_ranges = NUM_OF_ELEMENTS (lit_unicode_character_case_ranges_sup); } else { number_of_case_ranges = NUM_OF_ELEMENTS (lit_unicode_character_case_ranges); } int conv_counter = 0; for (int i = 0; i < number_of_case_ranges; i++) { if (i % 2 == 0 && i > 0) { conv_counter++; } size_t range_length; lit_code_point_t start_point; if (is_supplementary) { range_length = lit_unicode_character_case_range_lengths_sup[conv_counter]; start_point = lit_unicode_character_case_ranges_sup[i]; } else { range_length = lit_unicode_character_case_range_lengths[conv_counter]; start_point = lit_unicode_character_case_ranges[i]; } if (start_point > cp || cp >= start_point + range_length) { continue; } uint32_t char_dist = (uint32_t) cp - start_point; int offset; if (i % 2 == 0) { if (!is_lowercase) { return cp; } offset = i + 1; } else { if (is_lowercase) { return cp; } offset = i - 1; } if (is_supplementary) { start_point = lit_unicode_character_case_ranges_sup[offset]; } else { start_point = lit_unicode_character_case_ranges[offset]; } return (lit_code_point_t) (start_point + char_dist); } /* Note: After this point based on the latest unicode standard(13.0.0.6) no conversion characters are defined for supplementary planes */ if (is_supplementary) { return cp; } /* 2, Check if the specified character is part of the character_pair_ranges table. */ int bottom = 0; int top = NUM_OF_ELEMENTS (lit_unicode_character_pair_ranges) - 1; while (bottom <= top) { int middle = (bottom + top) / 2; lit_code_point_t current_sp = lit_unicode_character_pair_ranges[middle]; if (current_sp <= cp && cp < current_sp + lit_unicode_character_pair_range_lengths[middle]) { uint32_t char_dist = (uint32_t) (cp - current_sp); if ((cp - current_sp) % 2 == 0) { return is_lowercase ? (lit_code_point_t) (current_sp + char_dist + 1) : cp; } return is_lowercase ? cp : (lit_code_point_t) (current_sp + char_dist - 1); } if (cp > current_sp) { bottom = middle + 1; } else { top = middle - 1; } } /* 3, Check if the specified character is part of the character_pairs table. */ int number_of_character_pairs = NUM_OF_ELEMENTS (lit_unicode_character_pairs); for (int i = 0; i < number_of_character_pairs; i++) { if (cp != lit_unicode_character_pairs[i]) { continue; } if (i % 2 == 0) { return is_lowercase ? lit_unicode_character_pairs[i + 1] : cp; } return is_lowercase ? cp : lit_unicode_character_pairs[i - 1]; } return LIT_INVALID_CP; } /* lit_search_in_bidirectional_conversion_tables */ /** * Check if the specified character is in the given conversion table. * * @return LIT_MULTIPLE_CU if the converted character consist more than a single code unit * converted code point - otherwise */ static lit_code_point_t lit_search_in_conversion_table (ecma_char_t character, /**< code unit */ ecma_stringbuilder_t *builder_p, /**< string builder */ const ecma_char_t *array, /**< array */ const uint8_t *counters) /**< case_values counter */ { int end_point = 0; for (int i = 0; i < 3; i++) { int start_point = end_point; int size_of_case_value = i + 1; end_point += counters[i] * (size_of_case_value + 1); int bottom = start_point; int top = end_point - size_of_case_value; while (bottom <= top) { int middle = (bottom + top) / 2; middle -= ((middle - bottom) % (size_of_case_value + 1)); ecma_char_t current = array[middle]; if (current == character) { if (builder_p != NULL) { ecma_stringbuilder_append_char (builder_p, array[middle + 1]); if (size_of_case_value > 1) { ecma_stringbuilder_append_char (builder_p, array[middle + 2]); } if (size_of_case_value > 2) { ecma_stringbuilder_append_char (builder_p, array[middle + 3]); } } return size_of_case_value == 1 ? array[middle + 1] : LIT_MULTIPLE_CU; } if (character < current) { top = middle - (size_of_case_value + 1); } else { bottom = middle + (size_of_case_value + 1); } } } if (builder_p != NULL) { ecma_stringbuilder_append_char (builder_p, character); } return (lit_code_point_t) character; } /* lit_search_in_conversion_table */ #endif /* JERRY_UNICODE_CASE_CONVERSION */ /** * Append the converted lowercase codeunit sequence of an a given codepoint into the stringbuilder if it is present. * * @return LIT_MULTIPLE_CU if the converted codepoint consist more than a single code unit * converted code point - otherwise */ lit_code_point_t lit_char_to_lower_case (lit_code_point_t cp, /**< code point */ ecma_stringbuilder_t *builder_p) /**< string builder */ { if (cp <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { if (cp >= LIT_CHAR_UPPERCASE_A && cp <= LIT_CHAR_UPPERCASE_Z) { cp = (lit_utf8_byte_t) (cp + (LIT_CHAR_LOWERCASE_A - LIT_CHAR_UPPERCASE_A)); } if (builder_p != NULL) { ecma_stringbuilder_append_byte (builder_p, (lit_utf8_byte_t) cp); } return cp; } #if JERRY_UNICODE_CASE_CONVERSION lit_code_point_t lowercase_cp = lit_search_in_bidirectional_conversion_tables (cp, true); if (lowercase_cp != LIT_INVALID_CP) { if (builder_p != NULL) { ecma_stringbuilder_append_codepoint (builder_p, lowercase_cp); } return lowercase_cp; } JERRY_ASSERT (cp < LIT_UTF8_4_BYTE_CODE_POINT_MIN); int num_of_lowercase_ranges = NUM_OF_ELEMENTS (lit_unicode_lower_case_ranges); for (int i = 0, j = 0; i < num_of_lowercase_ranges; i += 2, j++) { JERRY_ASSERT (lit_unicode_lower_case_range_lengths[j] > 0); uint32_t range_length = (uint32_t) (lit_unicode_lower_case_range_lengths[j] - 1); lit_code_point_t start_point = lit_unicode_lower_case_ranges[i]; if (start_point <= cp && cp <= start_point + range_length) { lowercase_cp = lit_unicode_lower_case_ranges[i + 1] + (cp - start_point); if (builder_p != NULL) { ecma_stringbuilder_append_codepoint (builder_p, lowercase_cp); } return lowercase_cp; } } return lit_search_in_conversion_table ((ecma_char_t) cp, builder_p, lit_unicode_lower_case_conversions, lit_unicode_lower_case_conversion_counters); #else /* !JERRY_UNICODE_CASE_CONVERSION */ if (builder_p != NULL) { ecma_stringbuilder_append_codepoint (builder_p, cp); } return cp; #endif /* JERRY_UNICODE_CASE_CONVERSION */ } /* lit_char_to_lower_case */ /** * Append the converted uppercase codeunit sequence of an a given codepoint into the stringbuilder if it is present. * * @return LIT_MULTIPLE_CU if the converted codepoint consist more than a single code unit * converted code point - otherwise */ lit_code_point_t lit_char_to_upper_case (lit_code_point_t cp, /**< code point */ ecma_stringbuilder_t *builder_p) /**< string builder */ { if (cp <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { if (cp >= LIT_CHAR_LOWERCASE_A && cp <= LIT_CHAR_LOWERCASE_Z) { cp = (lit_utf8_byte_t) (cp - (LIT_CHAR_LOWERCASE_A - LIT_CHAR_UPPERCASE_A)); } if (builder_p != NULL) { ecma_stringbuilder_append_byte (builder_p, (lit_utf8_byte_t) cp); } return cp; } #if JERRY_UNICODE_CASE_CONVERSION lit_code_point_t uppercase_cp = lit_search_in_bidirectional_conversion_tables (cp, false); if (uppercase_cp != LIT_INVALID_CP) { if (builder_p != NULL) { ecma_stringbuilder_append_codepoint (builder_p, uppercase_cp); } return uppercase_cp; } int num_of_upper_case_special_ranges = NUM_OF_ELEMENTS (lit_unicode_upper_case_special_ranges); for (int i = 0, j = 0; i < num_of_upper_case_special_ranges; i += 3, j++) { uint32_t range_length = lit_unicode_upper_case_special_range_lengths[j]; ecma_char_t start_point = lit_unicode_upper_case_special_ranges[i]; if (start_point <= cp && cp <= start_point + range_length) { if (builder_p != NULL) { uppercase_cp = lit_unicode_upper_case_special_ranges[i + 1] + (cp - start_point); ecma_stringbuilder_append_codepoint (builder_p, uppercase_cp); ecma_stringbuilder_append_codepoint (builder_p, lit_unicode_upper_case_special_ranges[i + 2]); } return LIT_MULTIPLE_CU; } } return lit_search_in_conversion_table ((ecma_char_t) cp, builder_p, lit_unicode_upper_case_conversions, lit_unicode_upper_case_conversion_counters); #else /* !JERRY_UNICODE_CASE_CONVERSION */ if (builder_p != NULL) { ecma_stringbuilder_append_codepoint (builder_p, cp); } return cp; #endif /* JERRY_UNICODE_CASE_CONVERSION */ } /* lit_char_to_upper_case */ /* * Look up whether the character should be folded to the lowercase variant. * * @return true, if character should be lowercased * false, otherwise */ bool lit_char_fold_to_lower (lit_code_point_t cp) /**< code point */ { #if JERRY_UNICODE_CASE_CONVERSION return (cp <= LIT_UTF8_1_BYTE_CODE_POINT_MAX || cp > LIT_UTF16_CODE_UNIT_MAX || (!lit_search_char_in_interval_array ((ecma_char_t) cp, lit_unicode_folding_skip_to_lower_interval_starts, lit_unicode_folding_skip_to_lower_interval_lengths, NUM_OF_ELEMENTS (lit_unicode_folding_skip_to_lower_interval_starts)) && !lit_search_char_in_array ((ecma_char_t) cp, lit_unicode_folding_skip_to_lower_chars, NUM_OF_ELEMENTS (lit_unicode_folding_skip_to_lower_chars)))); #else /* !JERRY_UNICODE_CASE_CONVERSION */ return true; #endif /* JERRY_UNICODE_CASE_CONVERSION */ } /* lit_char_fold_to_lower */ /* * Look up whether the character should be folded to the uppercase variant. * * @return true, if character should be uppercased * false, otherwise */ bool lit_char_fold_to_upper (lit_code_point_t cp) /**< code point */ { #if JERRY_UNICODE_CASE_CONVERSION return (cp > LIT_UTF8_1_BYTE_CODE_POINT_MAX && cp <= LIT_UTF16_CODE_UNIT_MAX && (lit_search_char_in_interval_array ((ecma_char_t) cp, lit_unicode_folding_to_upper_interval_starts, lit_unicode_folding_to_upper_interval_lengths, NUM_OF_ELEMENTS (lit_unicode_folding_to_upper_interval_starts)) || lit_search_char_in_array ((ecma_char_t) cp, lit_unicode_folding_to_upper_chars, NUM_OF_ELEMENTS (lit_unicode_folding_to_upper_chars)))); #else /* !JERRY_UNICODE_CASE_CONVERSION */ return false; #endif /* JERRY_UNICODE_CASE_CONVERSION */ } /* lit_char_fold_to_upper */ /** * Helper method to find a specific character in a string * * Used by: * ecma_builtin_string_prototype_object_replace_helper * * @return true - if the given character is in the string * false - otherwise */ bool lit_find_char_in_string (ecma_string_t *str_p, /**< source string */ lit_utf8_byte_t c) /**< character to find*/ { ECMA_STRING_TO_UTF8_STRING (str_p, start_p, start_size); const lit_utf8_byte_t *str_curr_p = start_p; const lit_utf8_byte_t *str_end_p = start_p + start_size; bool have_char = false; while (str_curr_p < str_end_p) { if (*str_curr_p++ == c) { have_char = true; break; } } ECMA_FINALIZE_UTF8_STRING (start_p, start_size); return have_char; } /* lit_find_char_in_string */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgPath.h000664 001750 001750 00000002430 15164251010 034501 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SVG_PATH_H_ #define _TVG_SVG_PATH_H_ #include "tvgRender.h" bool svgPathToShape(const char* svgPath, RenderPath& out); #endif //_TVG_SVG_PATH_H_ lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-biguint64array.cpp000664 001750 001750 00000004530 15164251010 053434 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_BUILTIN_BIGINT #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-biguint64array.inc.h" #define BUILTIN_UNDERSCORED_ID biguint64array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup biguint64array ECMA BigUInt64Array object built-in * @{ */ /** * Handle calling [[Call]] of BigUInt64Array * * @return ecma value */ ecma_value_t ecma_builtin_biguint64array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_BIG_UINT64_ARRAY_REQUIRES_NEW); } /* ecma_builtin_biguint64array_dispatch_call */ /** * Handle calling [[Construct]] of BigUInt64Array * * @return ecma value */ ecma_value_t ecma_builtin_biguint64array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_BIGUINT64_ARRAY); } /* ecma_builtin_biguint64array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_BIGINT */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-function.inc.h000664 001750 001750 00000002432 15164251010 051552 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %AsyncFunction% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* ECMA-262 v11, 25.7.2 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_ASYNC_FUNCTION_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v11, 25.7.2.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v10, 25.7.2.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_ASYNC_FUNCTION_PROTOTYPE, ECMA_PROPERTY_FIXED) /* ECMA-262 v11, 25.7.3.2 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_ASYNC_FUNCTION_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jmem/jmem.h000664 001750 001750 00000022044 15164251010 041352 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JMEM_H #define JMEM_H #include "jrt.h" /** \addtogroup mem Memory allocation * @{ * * \addtogroup heap Heap * @{ */ /** * Logarithm of required alignment for allocated units/blocks */ #define JMEM_ALIGNMENT_LOG 3 /** * Representation of NULL value for compressed pointers */ #define JMEM_CP_NULL ((jmem_cpointer_t) 0) /** * Required alignment for allocated units/blocks */ #define JMEM_ALIGNMENT (1u << JMEM_ALIGNMENT_LOG) /** * Pointer value can be directly stored without compression */ #if UINTPTR_MAX <= UINT32_MAX #define JMEM_CAN_STORE_POINTER_VALUE_DIRECTLY #endif /* UINTPTR_MAX <= UINT32_MAX */ /** * Mask for tag part in jmem_cpointer_tag_t */ #define JMEM_TAG_MASK 0x7u /** * Shift for tag part in jmem_cpointer_tag_t */ #define JMEM_TAG_SHIFT 3 /** * Bit mask for tag part in jmem_cpointer_tag_t */ enum { JMEM_FIRST_TAG_BIT_MASK = (1u << 0), /**< first tag bit mask **/ JMEM_SECOND_TAG_BIT_MASK = (1u << 1), /**< second tag bit mask **/ JMEM_THIRD_TAG_BIT_MASK = (1u << 2), /**< third tag bit mask **/ }; /** * Compressed pointer representations * * 16 bit representation: * The jmem_cpointer_t is defined as uint16_t * and it can contain any sixteen bit value. * * 32 bit representation: * The jmem_cpointer_t is defined as uint32_t. * The lower JMEM_ALIGNMENT_LOG bits must be zero. * The other bits can have any value. * * The 16 bit representation always encodes an offset from * a heap base. The 32 bit representation currently encodes * raw 32 bit JMEM_ALIGNMENT aligned pointers on 32 bit systems. * This can be extended to encode a 32 bit offset from a heap * base on 64 bit systems in the future. There are no plans * to support more than 4G address space for JerryScript. */ /** * Compressed pointer */ typedef uint16_t jmem_cpointer_t; /** * Compressed pointer with tag value */ typedef uint32_t jmem_cpointer_tag_t; /** * Memory usage pressure for reclaiming unused memory. * * Each failed allocation will try to reclaim memory with increasing pressure, * until enough memory is freed to fulfill the allocation request. * * If not enough memory is freed and JMEM_PRESSURE_FULL is reached, * then the engine is shut down with JERRY_FATAL_OUT_OF_MEMORY. */ typedef enum { JMEM_PRESSURE_NONE, /**< no memory pressure */ JMEM_PRESSURE_LOW, /**< low memory pressure */ JMEM_PRESSURE_HIGH, /**< high memory pressure */ JMEM_PRESSURE_FULL, /**< memory full */ } jmem_pressure_t; /** * Node for free chunk list */ typedef struct jmem_pools_chunk_t { struct jmem_pools_chunk_t *next_p; /**< pointer to next pool chunk */ } jmem_pools_chunk_t; /** * Free region node */ typedef struct { uint32_t next_offset; /**< Offset of next region in list */ uint32_t size; /**< Size of region */ } jmem_heap_free_t; void jmem_init (void); void jmem_finalize (void); void *jmem_heap_alloc_block (const size_t size); void *jmem_heap_alloc_block_null_on_error (const size_t size); void *jmem_heap_realloc_block (void *ptr, const size_t old_size, const size_t new_size); void jmem_heap_free_block (void *ptr, const size_t size); jmem_cpointer_t JERRY_ATTR_PURE jmem_compress_pointer (const void *pointer_p); void *JERRY_ATTR_PURE jmem_decompress_pointer (uintptr_t compressed_pointer); /** * Define a local array variable and allocate memory for the array on the heap. * * If requested number of elements is zero, assign NULL to the variable. * * Warning: * if there is not enough memory on the heap, shutdown engine with JERRY_FATAL_OUT_OF_MEMORY. */ #define JMEM_DEFINE_LOCAL_ARRAY(var_name, number, type) \ { \ size_t var_name##___size = (size_t) (number) * sizeof (type); \ type *var_name = (type *) (jmem_heap_alloc_block (var_name##___size)); /** * Free the previously defined local array variable, freeing corresponding block on the heap, * if it was allocated (i.e. if the array's size was non-zero). */ #define JMEM_FINALIZE_LOCAL_ARRAY(var_name) \ if (var_name != NULL) \ { \ JERRY_ASSERT (var_name##___size != 0); \ \ jmem_heap_free_block (var_name, var_name##___size); \ } \ else \ { \ JERRY_ASSERT (var_name##___size == 0); \ } \ } /** * Get value of pointer from specified non-null compressed pointer value */ #define JMEM_CP_GET_NON_NULL_POINTER(type, cp_value) ((type *) (jmem_decompress_pointer (cp_value))) /** * Get value of pointer from specified compressed pointer value */ #define JMEM_CP_GET_POINTER(type, cp_value) \ (((JERRY_UNLIKELY ((cp_value) == JMEM_CP_NULL)) ? NULL : JMEM_CP_GET_NON_NULL_POINTER (type, cp_value))) /** * Set value of non-null compressed pointer so that it will correspond * to specified non_compressed_pointer */ #define JMEM_CP_SET_NON_NULL_POINTER(cp_value, non_compressed_pointer) \ (cp_value) = jmem_compress_pointer (non_compressed_pointer) /** * Set value of compressed pointer so that it will correspond * to specified non_compressed_pointer */ #define JMEM_CP_SET_POINTER(cp_value, non_compressed_pointer) \ do \ { \ void *ptr_value = (void *) non_compressed_pointer; \ \ if (JERRY_UNLIKELY ((ptr_value) == NULL)) \ { \ (cp_value) = JMEM_CP_NULL; \ } \ else \ { \ JMEM_CP_SET_NON_NULL_POINTER (cp_value, ptr_value); \ } \ } while (false); /** * Set value of pointer-tag value so that it will correspond * to specified non_compressed_pointer along with tag */ #define JMEM_CP_SET_NON_NULL_POINTER_TAG(cp_value, pointer, tag) \ do \ { \ JERRY_ASSERT ((uintptr_t) tag < (uintptr_t) (JMEM_ALIGNMENT)); \ jmem_cpointer_tag_t compressed_ptr = jmem_compress_pointer (pointer); \ (cp_value) = (jmem_cpointer_tag_t) ((compressed_ptr << JMEM_TAG_SHIFT) | tag); \ } while (false); /** * Extract value of pointer from specified pointer-tag value */ #define JMEM_CP_GET_NON_NULL_POINTER_FROM_POINTER_TAG(type, cp_value) \ ((type *) (jmem_decompress_pointer ((cp_value & ~JMEM_TAG_MASK) >> JMEM_TAG_SHIFT))) /** * Extract tag bits from pointer-tag value */ #define JMEM_CP_GET_POINTER_TAG_BITS(cp_value) \ (cp_value & (JMEM_FIRST_TAG_BIT_MASK | JMEM_SECOND_TAG_BIT_MASK | JMEM_THIRD_TAG_BIT_MASK)) /** * Get value of each tag from specified pointer-tag value */ #define JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG(cp_value) \ (cp_value & JMEM_FIRST_TAG_BIT_MASK) /**< get first tag bit \ **/ #define JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG(cp_value) \ (cp_value & JMEM_SECOND_TAG_BIT_MASK) /**< get second tag bit **/ #define JMEM_CP_GET_THIRD_BIT_FROM_POINTER_TAG(cp_value) \ (cp_value & JMEM_THIRD_TAG_BIT_MASK) /**< get third tag bit \ **/ /** * Set value of each tag to specified pointer-tag value */ #define JMEM_CP_SET_FIRST_BIT_TO_POINTER_TAG(cp_value) \ (cp_value) = (cp_value | JMEM_FIRST_TAG_BIT_MASK) /**< set first tag bit **/ #define JMEM_CP_SET_SECOND_BIT_TO_POINTER_TAG(cp_value) \ (cp_value) = (cp_value | JMEM_SECOND_TAG_BIT_MASK) /**< set second tag bit **/ #define JMEM_CP_SET_THIRD_BIT_TO_POINTER_TAG(cp_value) \ (cp_value) = (cp_value | JMEM_THIRD_TAG_BIT_MASK) /**< set third tag bit **/ /** * @} * \addtogroup poolman Memory pool manager * @{ */ void *jmem_pools_alloc (size_t size); void jmem_pools_free (void *chunk_p, size_t size); void jmem_pools_collect_empty (void); /** * @} * @} */ #endif /* !JMEM_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/meson.build000664 001750 001750 00000000076 15164251010 042371 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesubdir('base') subdir('builtin-objects') subdir('operations') src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/webp/format_constants.h000664 001750 001750 00000007532 15164251010 037014 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Internal header for constants related to WebP file format. // // Author: Urvang (urvang@google.com) #ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_ #define WEBP_WEBP_FORMAT_CONSTANTS_H_ // Create fourcc of the chunk from the chunk tag characters. #define MKFOURCC(a, b, c, d) ((uint32_t)(a) | (b) << 8 | (c) << 16 | (d) << 24) // VP8 related constants. #define VP8_SIGNATURE 0x9d012a // Signature in VP8 data. #define VP8_MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition #define VP8_MAX_PARTITION_SIZE (1 << 24) // max size for token partition #define VP8_FRAME_HEADER_SIZE 10 // Size of the frame header within VP8 data. // VP8L related constants. #define VP8L_SIGNATURE_SIZE 1 // VP8L signature size. #define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte. #define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store // width and height. #define VP8L_VERSION_BITS 3 // 3 bits reserved for version. #define VP8L_VERSION 0 // version 0 #define VP8L_FRAME_HEADER_SIZE 5 // Size of the VP8L frame header. #define MAX_PALETTE_SIZE 256 #define MAX_CACHE_BITS 11 #define HUFFMAN_CODES_PER_META_CODE 5 #define ARGB_BLACK 0xff000000 #define DEFAULT_CODE_LENGTH 8 #define MAX_ALLOWED_CODE_LENGTH 15 #define NUM_LITERAL_CODES 256 #define NUM_LENGTH_CODES 24 #define NUM_DISTANCE_CODES 40 #define CODE_LENGTH_CODES 19 #define MIN_HUFFMAN_BITS 2 // min number of Huffman bits #define MAX_HUFFMAN_BITS 9 // max number of Huffman bits #define TRANSFORM_PRESENT 1 // The bit to be written when next data // to be read is a transform. #define NUM_TRANSFORMS 4 // Maximum number of allowed transform // in a bitstream. typedef enum { PREDICTOR_TRANSFORM = 0, CROSS_COLOR_TRANSFORM = 1, SUBTRACT_GREEN = 2, COLOR_INDEXING_TRANSFORM = 3 } VP8LImageTransformType; // Alpha related constants. #define ALPHA_HEADER_LEN 1 #define ALPHA_NO_COMPRESSION 0 #define ALPHA_LOSSLESS_COMPRESSION 1 #define ALPHA_PREPROCESSED_LEVELS 1 // Mux related constants. #define TAG_SIZE 4 // Size of a chunk tag (e.g. "VP8L"). #define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size. #define CHUNK_HEADER_SIZE 8 // Size of a chunk header. #define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP"). #define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk. #define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk. #define FRGM_CHUNK_SIZE 6 // Size of a FRGM chunk. #define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk. #define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height. #define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height. #define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count #define MAX_DURATION (1 << 24) // maximum duration #define MAX_POSITION_OFFSET (1 << 24) // maximum frame/fragment x/y offset // Maximum chunk payload is such that adding the header and padding won't // overflow a uint32_t. #define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1) #endif /* WEBP_WEBP_FORMAT_CONSTANTS_H_ */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint16array.inc.h000664 001750 001750 00000001714 15164251010 053165 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Uint16Array description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 2 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_UINT16_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_UINT16ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-handlers.inc.h000664 001750 001750 00000003167 15164251010 050420 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_PROMISE_RESOLVE, ecma_promise_resolve_handler, 1) ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_PROMISE_REJECT, ecma_promise_reject_handler, 1) ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_PROMISE_THEN_FINALLY, ecma_promise_then_finally_cb, 1) ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_PROMISE_CATCH_FINALLY, ecma_promise_catch_finally_cb, 1) ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_PROMISE_ALL_HELPER, ecma_promise_all_or_all_settled_handler_cb, 1) ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_PROMISE_CAPABILITY_EXECUTOR, ecma_op_get_capabilities_executor_cb, 2) ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_ASYNC_FROM_SYNC_ITERATOR_UNWRAP, ecma_async_from_sync_iterator_unwrap_cb, 1) #if JERRY_BUILTIN_PROXY ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_PROXY_REVOKE, ecma_proxy_revoke_cb, 0) #endif /* JERRY_BUILTIN_PROXY */ ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_VALUE_THUNK, ecma_value_thunk_helper_cb, 0) ECMA_NATIVE_HANDLER (ECMA_NATIVE_HANDLER_VALUE_THROWER, ecma_value_thunk_thrower_cb, 0) thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-evalerror.inc.h000664 001750 001750 00000002446 15164251010 050620 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * EvalError built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_EVAL_ERROR_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_EVAL_ERROR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakset.inc.h000664 001750 001750 00000002523 15164251010 050256 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * WeakSet built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER /* Number properties: * (property name, number value, writable, enumerable, configurable) */ /* ECMA-262 v6, 23.4.2 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 23.4 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_WEAKSET_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 23.4.2.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_WEAKSET_PROTOTYPE, ECMA_PROPERTY_FIXED) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-dataview-object.h000664 001750 001750 00000003046 15164251010 046536 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_DATAVIEW_OBJECT_H #define ECMA_DATAVIEW_OBJECT_H #include "ecma-globals.h" #if JERRY_BUILTIN_DATAVIEW /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmadataviewobject ECMA builtin DataView helper functions * @{ */ ecma_value_t ecma_op_dataview_create (const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_dataview_object_t *ecma_op_dataview_get_object (ecma_value_t this_arg); ecma_value_t ecma_op_dataview_get_set_view_value (ecma_value_t view, ecma_value_t request_index, ecma_value_t little_endian, ecma_value_t value_to_set, ecma_typedarray_type_t id); bool ecma_is_dataview (ecma_value_t value); /** * @} * @} */ #endif /* JERRY_BUILTIN_DATAVIEW */ #endif /* !ECMA_DATAVIEW_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/io.cpp000664 001750 001750 00000032374 15164251010 034251 0ustar00ddennedyddennedy000000 000000 // Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // functions for sample output. // // Author: Skal (pascal.massimino@gmail.com) #include #include "tvgCommon.h" #include "../dec/vp8i.h" #include "./webpi.h" #include "../dsp/dsp.h" #include "../dsp/yuv.h" #include "../utils/utils.h" // Point-sampling U/V sampler. static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) { WebPDecBuffer* const output = p->output; WebPRGBABuffer* const buf = &output->u.RGBA; uint8_t* const dst = buf->rgba + io->mb_y * buf->stride; WebPSamplerProcessPlane(io->y, io->y_stride, io->u, io->v, io->uv_stride, dst, buf->stride, io->mb_w, io->mb_h, WebPSamplers[output->colorspace]); return io->mb_h; } //------------------------------------------------------------------------------ // Fancy upsampling #ifdef FANCY_UPSAMPLING static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) { int num_lines_out = io->mb_h; // a priori guess const WebPRGBABuffer* const buf = &p->output->u.RGBA; uint8_t* dst = buf->rgba + io->mb_y * buf->stride; WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace]; const uint8_t* cur_y = io->y; const uint8_t* cur_u = io->u; const uint8_t* cur_v = io->v; const uint8_t* top_u = p->tmp_u; const uint8_t* top_v = p->tmp_v; int y = io->mb_y; const int y_end = io->mb_y + io->mb_h; const int mb_w = io->mb_w; const int uv_w = (mb_w + 1) / 2; if (y == 0) { // First line is special cased. We mirror the u/v samples at boundary. upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w); } else { // We can finish the left-over line from previous call. upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v, dst - buf->stride, dst, mb_w); ++num_lines_out; } // Loop over each output pairs of row. for (; y + 2 < y_end; y += 2) { top_u = cur_u; top_v = cur_v; cur_u += io->uv_stride; cur_v += io->uv_stride; dst += 2 * buf->stride; cur_y += 2 * io->y_stride; upsample(cur_y - io->y_stride, cur_y, top_u, top_v, cur_u, cur_v, dst - buf->stride, dst, mb_w); } // move to last row cur_y += io->y_stride; if (io->crop_top + y_end < io->crop_bottom) { // Save the unfinished samples for next call (as we're not done yet). memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y)); memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u)); memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v)); // The fancy upsampler leaves a row unfinished behind // (except for the very last row) num_lines_out--; } else { // Process the very last row of even-sized picture if (!(y_end & 1)) { upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst + buf->stride, NULL, mb_w); } } return num_lines_out; } #endif /* FANCY_UPSAMPLING */ //------------------------------------------------------------------------------ static int GetAlphaSourceRow(const VP8Io* const io, const uint8_t** alpha, int* const num_rows) { int start_y = io->mb_y; *num_rows = io->mb_h; // Compensate for the 1-line delay of the fancy upscaler. // This is similar to EmitFancyRGB(). if (io->fancy_upsampling) { if (start_y == 0) { // We don't process the last row yet. It'll be done during the next call. --*num_rows; } else { --start_y; // Fortunately, *alpha data is persistent, so we can go back // one row and finish alpha blending, now that the fancy upscaler // completed the YUV->RGB interpolation. *alpha -= io->width; } if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) { // If it's the very last call, we process all the remaining rows! *num_rows = io->crop_bottom - io->crop_top - start_y; } } return start_y; } static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { const uint8_t* alpha = io->a; if (alpha != NULL) { const int mb_w = io->mb_w; const WEBP_CSP_MODE colorspace = p->output->colorspace; const int alpha_first = (colorspace == MODE_ARGB || colorspace == MODE_Argb); const WebPRGBABuffer* const buf = &p->output->u.RGBA; int num_rows; const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3); const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w, num_rows, dst, buf->stride); // has_alpha is true if there's non-trivial alpha to premultiply with. if (has_alpha && WebPIsPremultipliedMode(colorspace)) { WebPApplyAlphaMultiply(base_rgba, alpha_first, mb_w, num_rows, buf->stride); } } return 0; } //------------------------------------------------------------------------------ // RGBA rescaling static int ExportRGB(WebPDecParams* const p, int y_pos) { const WebPYUV444Converter convert = WebPYUV444Converters[p->output->colorspace]; const WebPRGBABuffer* const buf = &p->output->u.RGBA; uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride; int num_lines_out = 0; // For RGB rescaling, because of the YUV420, current scan position // U/V can be +1/-1 line from the Y one. Hence the double test. while (WebPRescalerHasPendingOutput(&p->scaler_y) && WebPRescalerHasPendingOutput(&p->scaler_u)) { assert(p->last_y + y_pos + num_lines_out < p->output->height); assert(p->scaler_u.y_accum == p->scaler_v.y_accum); WebPRescalerExportRow(&p->scaler_y, 0); WebPRescalerExportRow(&p->scaler_u, 0); WebPRescalerExportRow(&p->scaler_v, 0); convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst, dst, p->scaler_y.dst_width); dst += buf->stride; ++num_lines_out; } return num_lines_out; } static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) { const int mb_h = io->mb_h; const int uv_mb_h = (mb_h + 1) >> 1; int j = 0, uv_j = 0; int num_lines_out = 0; while (j < mb_h) { const int y_lines_in = WebPRescalerImport(&p->scaler_y, mb_h - j, io->y + j * io->y_stride, io->y_stride); const int u_lines_in = WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j, io->u + uv_j * io->uv_stride, io->uv_stride); const int v_lines_in = WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j, io->v + uv_j * io->uv_stride, io->uv_stride); (void)v_lines_in; // remove a gcc warning assert(u_lines_in == v_lines_in); j += y_lines_in; uv_j += u_lines_in; num_lines_out += ExportRGB(p, num_lines_out); } return num_lines_out; } static int ExportAlpha(WebPDecParams* const p, int y_pos) { const WebPRGBABuffer* const buf = &p->output->u.RGBA; uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride; const WEBP_CSP_MODE colorspace = p->output->colorspace; const int alpha_first = (colorspace == MODE_ARGB || colorspace == MODE_Argb); uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); int num_lines_out = 0; const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); uint32_t alpha_mask = 0xff; const int width = p->scaler_a.dst_width; while (WebPRescalerHasPendingOutput(&p->scaler_a)) { int i; assert(p->last_y + y_pos + num_lines_out < p->output->height); WebPRescalerExportRow(&p->scaler_a, 0); for (i = 0; i < width; ++i) { const uint32_t alpha_value = p->scaler_a.dst[i]; dst[4 * i] = alpha_value; alpha_mask &= alpha_value; } dst += buf->stride; ++num_lines_out; } if (is_premult_alpha && alpha_mask != 0xff) { WebPApplyAlphaMultiply(base_rgba, alpha_first, width, num_lines_out, buf->stride); } return num_lines_out; } static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { if (io->a != NULL) { WebPRescaler* const scaler = &p->scaler_a; int j = 0; int pos = 0; while (j < io->mb_h) { j += WebPRescalerImport(scaler, io->mb_h - j, io->a + j * io->width, io->width); pos += p->emit_alpha_row(p, pos); } } return 0; } static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) { const int has_alpha = WebPIsAlphaMode(p->output->colorspace); const int out_width = io->scaled_width; const int out_height = io->scaled_height; const int uv_in_width = (io->mb_w + 1) >> 1; const int uv_in_height = (io->mb_h + 1) >> 1; const size_t work_size = 2 * out_width; // scratch memory for one rescaler int32_t* work; // rescalers work area uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion size_t tmp_size1, tmp_size2, total_size; tmp_size1 = 3 * work_size; tmp_size2 = 3 * out_width; if (has_alpha) { tmp_size1 += work_size; tmp_size2 += out_width; } total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp); p->memory = tvg::calloc(1ULL, total_size); if (p->memory == NULL) { return 0; // memory error } work = (int32_t*)p->memory; tmp = (uint8_t*)(work + tmp_size1); WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h, tmp + 0 * out_width, out_width, out_height, 0, 1, io->mb_w, out_width, io->mb_h, out_height, work + 0 * work_size); WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height, tmp + 1 * out_width, out_width, out_height, 0, 1, io->mb_w, 2 * out_width, io->mb_h, 2 * out_height, work + 1 * work_size); WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height, tmp + 2 * out_width, out_width, out_height, 0, 1, io->mb_w, 2 * out_width, io->mb_h, 2 * out_height, work + 2 * work_size); p->emit = EmitRescaledRGB; WebPInitYUV444Converters(); if (has_alpha) { WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h, tmp + 3 * out_width, out_width, out_height, 0, 1, io->mb_w, out_width, io->mb_h, out_height, work + 3 * work_size); p->emit_alpha = EmitRescaledAlphaRGB; p->emit_alpha_row = ExportAlpha; WebPInitAlphaProcessing(); } return 1; } //------------------------------------------------------------------------------ // Default custom functions static int CustomSetup(VP8Io* io) { WebPDecParams* const p = (WebPDecParams*)io->opaque; const WEBP_CSP_MODE colorspace = p->output->colorspace; const int is_rgb = WebPIsRGBMode(colorspace); const int is_alpha = WebPIsAlphaMode(colorspace); p->memory = NULL; p->emit = NULL; p->emit_alpha = NULL; p->emit_alpha_row = NULL; if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) { return 0; } if (is_alpha && WebPIsPremultipliedMode(colorspace)) { WebPInitUpsamplers(); } if (io->use_scaling) { const int ok = InitRGBRescaler(io, p); if (!ok) { return 0; // memory error } } else { if (is_rgb) { p->emit = EmitSampledRGB; // default if (io->fancy_upsampling) { #ifdef FANCY_UPSAMPLING const int uv_width = (io->mb_w + 1) >> 1; p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width)); if (p->memory == NULL) { return 0; // memory error. } p->tmp_y = (uint8_t*)p->memory; p->tmp_u = p->tmp_y + io->mb_w; p->tmp_v = p->tmp_u + uv_width; p->emit = EmitFancyRGB; WebPInitUpsamplers(); #endif } else { WebPInitSamplers(); } } if (is_alpha) { // need transparency output p->emit_alpha = EmitAlphaRGB; if (is_rgb) WebPInitAlphaProcessing(); } } if (is_rgb) { VP8YUVInit(); } return 1; } //------------------------------------------------------------------------------ static int CustomPut(const VP8Io* io) { WebPDecParams* const p = (WebPDecParams*)io->opaque; const int mb_w = io->mb_w; const int mb_h = io->mb_h; int num_lines_out; assert(!(io->mb_y & 1)); if (mb_w <= 0 || mb_h <= 0) { return 0; } num_lines_out = p->emit(io, p); if (p->emit_alpha != NULL) { p->emit_alpha(io, p); } p->last_y += num_lines_out; return 1; } //------------------------------------------------------------------------------ static void CustomTeardown(const VP8Io* io) { WebPDecParams* const p = (WebPDecParams*)io->opaque; tvg::free(p->memory); p->memory = NULL; } //------------------------------------------------------------------------------ // Main entry point void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) { io->put = CustomPut; io->setup = CustomSetup; io->teardown = CustomTeardown; io->opaque = params; } //------------------------------------------------------------------------------ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-get-put-value.cpp000664 001750 001750 00000022755 15164251010 046530 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /** * Implementation of ECMA GetValue and PutValue */ #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "ecma-reference.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup lexicalenvironment Lexical environment * @{ */ /** * GetValue operation part * * See also: ECMA-262 v5, 8.7.1, sections 3 and 5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_get_value_lex_env_base (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_object_t **ref_base_lex_env_p, /**< [out] reference's base (lexical environment) */ ecma_string_t *name_p) /**< variable name */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); while (true) { switch (ecma_get_lex_env_type (lex_env_p)) { case ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE: { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); if (property_p != NULL) { *ref_base_lex_env_p = lex_env_p; ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (JERRY_UNLIKELY (property_value_p->value == ECMA_VALUE_UNINITIALIZED)) { return ecma_raise_reference_error (ECMA_ERR_LET_CONST_NOT_INITIALIZED); } return ecma_fast_copy_value (property_value_p->value); } break; } case ECMA_LEXICAL_ENVIRONMENT_CLASS: { #if JERRY_MODULE_SYSTEM if (ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); if (property_p != NULL) { *ref_base_lex_env_p = lex_env_p; ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (!(*property_p & ECMA_PROPERTY_FLAG_DATA)) { property_value_p = ecma_get_property_value_from_named_reference (property_value_p); } if (JERRY_UNLIKELY (property_value_p->value == ECMA_VALUE_UNINITIALIZED)) { return ecma_raise_reference_error (ECMA_ERR_LET_CONST_NOT_INITIALIZED); } return ecma_fast_copy_value (property_value_p->value); } } #endif /* JERRY_MODULE_SYSTEM */ break; } default: { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_value_t result = ecma_op_object_bound_environment_resolve_reference_value (lex_env_p, name_p); if (ecma_is_value_found (result)) { /* Note: the result may contains ECMA_VALUE_ERROR */ *ref_base_lex_env_p = lex_env_p; return result; } break; } } if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) { break; } lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } *ref_base_lex_env_p = NULL; #if JERRY_ERROR_MESSAGES return ecma_raise_standard_error_with_format (JERRY_ERROR_REFERENCE, "% is not defined", ecma_make_string_value (name_p)); #else /* JERRY_ERROR_MESSAGES */ return ecma_raise_reference_error (ECMA_ERR_EMPTY); #endif /* JERRY_ERROR_MESSAGES */ } /* ecma_op_get_value_lex_env_base */ /** * GetValue operation part (object base). * * See also: ECMA-262 v5, 8.7.1, section 4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_get_value_object_base (ecma_value_t base_value, /**< base value */ ecma_string_t *property_name_p) /**< property name */ { ecma_object_t *obj_p; if (JERRY_UNLIKELY (ecma_is_value_object (base_value))) { obj_p = ecma_get_object_from_value (base_value); } else { ecma_builtin_id_t id = ECMA_BUILTIN_ID_OBJECT_PROTOTYPE; if (JERRY_LIKELY (ecma_is_value_string (base_value))) { ecma_string_t *string_p = ecma_get_string_from_value (base_value); if (ecma_string_is_length (property_name_p)) { return ecma_make_uint32_value (ecma_string_get_length (string_p)); } uint32_t index = ecma_string_get_array_index (property_name_p); if (index != ECMA_STRING_NOT_ARRAY_INDEX && index < ecma_string_get_length (string_p)) { ecma_char_t char_at_idx = ecma_string_get_char_at_pos (string_p, index); return ecma_make_string_value (ecma_new_ecma_string_from_code_unit (char_at_idx)); } #if JERRY_BUILTIN_STRING id = ECMA_BUILTIN_ID_STRING_PROTOTYPE; #endif /* JERRY_BUILTIN_STRING */ } else if (ecma_is_value_number (base_value)) { #if JERRY_BUILTIN_NUMBER id = ECMA_BUILTIN_ID_NUMBER_PROTOTYPE; #endif /* JERRY_BUILTIN_NUMBER */ } else if (ecma_is_value_symbol (base_value)) { id = ECMA_BUILTIN_ID_SYMBOL_PROTOTYPE; } #if JERRY_BUILTIN_BIGINT else if (ecma_is_value_bigint (base_value)) { id = ECMA_BUILTIN_ID_BIGINT_PROTOTYPE; } #endif /* JERRY_BUILTIN_BIGINT */ else { JERRY_ASSERT (ecma_is_value_boolean (base_value)); #if JERRY_BUILTIN_BOOLEAN id = ECMA_BUILTIN_ID_BOOLEAN_PROTOTYPE; #endif /* JERRY_BUILTIN_BOOLEAN */ } obj_p = ecma_builtin_get (id); } return ecma_op_object_get_with_receiver (obj_p, property_name_p, base_value); } /* ecma_op_get_value_object_base */ /** * PutValue operation part * * See also: ECMA-262 v5, 8.7.2, sections 3 and 5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_put_value_lex_env_base (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p, /**< variable name */ bool is_strict, /**< flag indicating strict mode */ ecma_value_t value) /**< ECMA-value */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); while (true) { switch (ecma_get_lex_env_type (lex_env_p)) { case ECMA_LEXICAL_ENVIRONMENT_CLASS: { if (!ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { break; } /* FALLTHRU */ } case ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE: { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); if (property_p != NULL) { ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); JERRY_ASSERT (!(*property_p & ECMA_PROPERTY_FLAG_WRITABLE) || (*property_p & ECMA_PROPERTY_FLAG_DATA)); if ((*property_p & ECMA_PROPERTY_FLAG_WRITABLE) && property_value_p->value != ECMA_VALUE_UNINITIALIZED) { ecma_named_data_property_assign_value (lex_env_p, property_value_p, value); return ECMA_VALUE_EMPTY; } return ecma_op_raise_set_binding_error (property_p, is_strict); } break; } default: { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); ecma_value_t has_property = ecma_op_object_has_property (binding_obj_p, name_p); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (has_property)) { return has_property; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_true (has_property)) { ecma_value_t completion = ecma_op_object_put (binding_obj_p, name_p, value, is_strict); if (ECMA_IS_VALUE_ERROR (completion)) { return completion; } JERRY_ASSERT (ecma_is_value_boolean (completion)); return ECMA_VALUE_EMPTY; } break; } } if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) { break; } lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); if (is_strict) { #if JERRY_ERROR_MESSAGES return ecma_raise_standard_error_with_format (JERRY_ERROR_REFERENCE, "% is not defined", ecma_make_string_value (name_p)); #else /* !JERRY_ERROR_MESSAGES */ return ecma_raise_reference_error (ECMA_ERR_EMPTY); #endif /* JERRY_ERROR_MESSAGES */ } ecma_op_object_put (ecma_get_lex_env_binding_object (lex_env_p), name_p, value, false); return ECMA_VALUE_EMPTY; } /* ecma_op_put_value_lex_env_base */ /** * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-reference.cpp000664 001750 001750 00000027600 15164251010 045761 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-reference.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lcache.h" #include "ecma-lex-env.h" #include "ecma-objects.h" #include "ecma-proxy-object.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup references ECMA-Reference * @{ */ /** * Resolve syntactic reference. * * @return ECMA_OBJECT_POINTER_ERROR - if the operation fails * pointer to lexical environment - if the reference's base is resolved successfully, * NULL - otherwise. */ ecma_object_t * ecma_op_resolve_reference_base (ecma_object_t *lex_env_p, /**< starting lexical environment */ ecma_string_t *name_p) /**< identifier's name */ { JERRY_ASSERT (lex_env_p != NULL); while (true) { ecma_value_t has_binding = ecma_op_has_binding (lex_env_p, name_p); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (has_binding)) { return ECMA_OBJECT_POINTER_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_true (has_binding)) { return lex_env_p; } if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) { return NULL; } lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } } /* ecma_op_resolve_reference_base */ /** * Check if the passed lexical environment is a global lexical environment * * @return true - if the lexical environment is a global lexical environment * false - otherwise */ static inline bool ecma_op_is_global_environment (ecma_object_t *lex_env_p) /**< lexical environment */ { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); #if JERRY_BUILTIN_REALMS JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL || (ecma_make_object_value (ecma_get_lex_env_binding_object (lex_env_p)) == ((ecma_global_object_t *) ecma_builtin_get_global ())->this_binding)); #else /* !JERRY_BUILTIN_REALMS */ JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL || ecma_get_lex_env_binding_object (lex_env_p) == ecma_builtin_get_global ()); #endif /* JERRY_BUILTIN_REALMS */ return lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL; } /* ecma_op_is_global_environment */ /** * Perform GetThisEnvironment and GetSuperBase operations * * See also: ECMAScript v6, 8.1.1.3.5 * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_UNDEFINED - if the home object is null * value of the [[HomeObject]].[[Prototype]] internal slot - otherwise */ ecma_value_t ecma_op_resolve_super_base (ecma_object_t *lex_env_p) /**< starting lexical environment */ { while (true) { JERRY_ASSERT (lex_env_p != NULL); if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && !ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { ecma_object_t *home_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u1.home_object_cp); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (home_p)) { return ecma_proxy_object_get_prototype_of (home_p); } #endif /* JERRY_BUILTIN_PROXY */ jmem_cpointer_t proto_cp = ecma_op_ordinary_object_get_prototype_of (home_p); if (proto_cp == JMEM_CP_NULL) { return ECMA_VALUE_NULL; } ecma_object_t *proto_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp); ecma_ref_object (proto_p); return ecma_make_object_value (proto_p); } if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) { break; } lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } return ECMA_VALUE_UNDEFINED; } /* ecma_op_resolve_super_base */ /** * Helper method for HasBindig operation * * See also: * ECMA-262 v6, 8.1.1.2.1 steps 7-9; * * @return ECMA_VALUE_TRUE - if the property is unscopable * ECMA_VALUE_FALSE - if a the property is not unscopable * ECMA_VALUE_ERROR - otherwise */ static ecma_value_t ecma_op_is_prop_unscopable (ecma_object_t *binding_obj_p, /**< binding object */ ecma_string_t *prop_name_p) /**< property's name */ { ecma_value_t unscopables = ecma_op_object_get_by_symbol_id (binding_obj_p, LIT_GLOBAL_SYMBOL_UNSCOPABLES); if (ECMA_IS_VALUE_ERROR (unscopables)) { return unscopables; } if (ecma_is_value_object (unscopables)) { ecma_object_t *unscopables_obj_p = ecma_get_object_from_value (unscopables); ecma_value_t get_unscopables_value = ecma_op_object_get (unscopables_obj_p, prop_name_p); ecma_deref_object (unscopables_obj_p); if (ECMA_IS_VALUE_ERROR (get_unscopables_value)) { return get_unscopables_value; } bool is_blocked = ecma_op_to_boolean (get_unscopables_value); ecma_free_value (get_unscopables_value); return ecma_make_boolean_value (is_blocked); } ecma_free_value (unscopables); return ECMA_VALUE_FALSE; } /* ecma_op_is_prop_unscopable */ /** * Helper method for HasBindig operation * * See also: * ECMA-262 v6, 8.1.1.2.1 steps 7-9; * * @return ECMA_VALUE_TRUE - if the property is unscopable * ECMA_VALUE_FALSE - if a the property is not unscopable * ECMA_VALUE_ERROR - otherwise */ /** * Resolve value corresponding to the given object environment reference. * * Note: the steps are already include the HasBindig operation steps * * See also: * ECMA-262 v6, 8.1.1.2.1 * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_NOT_FOUND - if the binding not exists or blocked via @@unscopables * result of the binding - otherwise */ ecma_value_t ecma_op_object_bound_environment_resolve_reference_value (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p) /**< variable name */ { ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); ecma_value_t found_binding; #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (binding_obj_p)) { found_binding = ecma_proxy_object_has (binding_obj_p, name_p); if (!ecma_is_value_true (found_binding)) { return ECMA_IS_VALUE_ERROR (found_binding) ? found_binding : ECMA_VALUE_NOT_FOUND; } } else { #endif /* JERRY_BUILTIN_PROXY */ found_binding = ecma_op_object_find (binding_obj_p, name_p); if (ECMA_IS_VALUE_ERROR (found_binding) || !ecma_is_value_found (found_binding)) { return found_binding; } if (JERRY_LIKELY (ecma_op_is_global_environment (lex_env_p))) { return found_binding; } #if JERRY_BUILTIN_PROXY } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t blocked = ecma_op_is_prop_unscopable (binding_obj_p, name_p); if (ecma_is_value_false (blocked)) { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (binding_obj_p)) { return ecma_proxy_object_get (binding_obj_p, name_p, ecma_make_object_value (binding_obj_p)); } #endif /* JERRY_BUILTIN_PROXY */ return found_binding; } #if JERRY_BUILTIN_PROXY if (!ECMA_OBJECT_IS_PROXY (binding_obj_p)) { ecma_free_value (found_binding); } #endif /* JERRY_BUILTIN_PROXY */ return ECMA_IS_VALUE_ERROR (blocked) ? blocked : ECMA_VALUE_NOT_FOUND; } /* ecma_op_object_bound_environment_resolve_reference_value */ /** * Resolve value corresponding to reference. * * @return value of the reference */ ecma_value_t ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical environment */ ecma_string_t *name_p) /**< identifier's name */ { JERRY_ASSERT (lex_env_p != NULL); while (true) { switch (ecma_get_lex_env_type (lex_env_p)) { case ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE: { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); if (property_p == NULL) { break; } ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (JERRY_UNLIKELY (property_value_p->value == ECMA_VALUE_UNINITIALIZED)) { return ecma_raise_reference_error (ECMA_ERR_LET_CONST_NOT_INITIALIZED); } return ecma_fast_copy_value (property_value_p->value); } case ECMA_LEXICAL_ENVIRONMENT_CLASS: { #if JERRY_MODULE_SYSTEM if (ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); if (property_p == NULL) { break; } ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (!(*property_p & ECMA_PROPERTY_FLAG_DATA)) { property_value_p = ecma_get_property_value_from_named_reference (property_value_p); } if (JERRY_UNLIKELY (property_value_p->value == ECMA_VALUE_UNINITIALIZED)) { return ecma_raise_reference_error (ECMA_ERR_LET_CONST_NOT_INITIALIZED); } return ecma_fast_copy_value (property_value_p->value); } #endif /* JERRY_MODULE_SYSTEM */ break; } default: { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); if (ecma_op_is_global_environment (lex_env_p)) { #if JERRY_LCACHE ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); ecma_property_t *property_p = ecma_lcache_lookup (binding_obj_p, name_p); if (property_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); ecma_property_value_t *prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (*property_p & ECMA_PROPERTY_FLAG_DATA) { return ecma_fast_copy_value (prop_value_p->value); } ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (prop_value_p); if (get_set_pair_p->getter_cp == JMEM_CP_NULL) { return ECMA_VALUE_UNDEFINED; } ecma_object_t *getter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp); ecma_value_t base_value = ecma_make_object_value (binding_obj_p); return ecma_op_function_call (getter_p, base_value, NULL, 0); } #endif /* JERRY_LCACHE */ } ecma_value_t result = ecma_op_object_bound_environment_resolve_reference_value (lex_env_p, name_p); if (ecma_is_value_found (result)) { /* Note: the result may contains ECMA_VALUE_ERROR */ return result; } break; } } if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) { break; } lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } #if JERRY_ERROR_MESSAGES ecma_value_t name_val = ecma_make_string_value (name_p); ecma_value_t error_value = ecma_raise_standard_error_with_format (JERRY_ERROR_REFERENCE, "% is not defined", name_val); #else /* JERRY_ERROR_MESSAGES */ ecma_value_t error_value = ecma_raise_reference_error (ECMA_ERR_EMPTY); #endif /* !JERRY_ERROR_MESSAGES */ return error_value; } /* ecma_op_resolve_reference_value */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgPath.cpp000664 001750 001750 00000032556 15164251010 035050 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++. #include #include #include "tvgMath.h" #include "tvgSvgLoaderCommon.h" #include "tvgSvgPath.h" #include "tvgStr.h" #include "tvgSvgUtil.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static char* _skipComma(const char* content) { content = svgUtilSkipWhiteSpace(content, nullptr); if (*content == ',') return (char*)content + 1; return (char*)content; } static bool _parseNumber(char** content, float* number) { char* end = NULL; *number = toFloat(*content, &end); //If the start of string is not number if ((*content) == end) return false; //Skip comma if any *content = _skipComma(end); return true; } static bool _parseFlag(char** content, int* number) { char* end = NULL; if (*(*content) != '0' && *(*content) != '1') return false; *number = *(*content) - '0'; *content += 1; end = *content; *content = _skipComma(end); return true; } //Some helpful stuff is available here: //http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes void _pathAppendArcTo(RenderPath& out, Point& cur, Point& curCtl, const Point& next, Point radius, float angle, bool largeArc, bool sweep) { auto start = cur; auto cosPhi = cosf(angle); auto sinPhi = sinf(angle); auto d2 = (start - next) * 0.5f; auto x1p = cosPhi * d2.x + sinPhi * d2.y; auto y1p = cosPhi * d2.y - sinPhi * d2.x; auto x1p2 = x1p * x1p; auto y1p2 = y1p * y1p; auto radius2 = Point{radius.x * radius.x, radius.y * radius.y}; auto lambda = (x1p2 / radius2.x) + (y1p2 / radius2.y); //Correction of out-of-range radii, see F6.6.2 (step 4) if (lambda > 1.0f) { //See F6.6.3 radius *= sqrtf(lambda); radius2 = {radius.x * radius.x, radius.y * radius.y}; } Point cp, center; auto c = (radius2.x * radius2.y) - (radius2.x * y1p2) - (radius2.y * x1p2); //Check if there is no possible solution //(i.e. we can't do a square root of a negative value) if (c < 0.0f) { //Scale uniformly until we have a single solution //(see F6.2) i.e. when c == 0.0 radius *= sqrtf(1.0f - c / (radius2.x * radius2.y)); radius2 = {radius.x * radius.x, radius.y * radius.y}; //Step 2 (F6.5.2) - simplified since c == 0.0 cp = {0.0f, 0.0f}; //Step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0 center = {0.0f, 0.0f}; } else { //Complete c calculation c = sqrtf(c / ((radius2.x * y1p2) + (radius2.y * x1p2))); //Inverse sign if Fa == Fs if (largeArc == sweep) c = -c; //Step 2 (F6.5.2) cp = c * Point{(radius.x * y1p / radius.y), (-radius.y * x1p / radius.x)}; //Step 3 (F6.5.3 first part) center = {cosPhi * cp.x - sinPhi * cp.y, sinPhi * cp.x + cosPhi * cp.y}; } //Step 3 (F6.5.3 second part) we now have the center point of the ellipse center += (start + next) * 0.5f; //Step 4 (F6.5.4) //We don't use arccos (as per w3c doc), see //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm //Note: atan2 (0.0, 1.0) == 0.0 auto at = tvg::atan2(((y1p - cp.y) / radius.y), ((x1p - cp.x) / radius.x)); auto theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at; auto nat = tvg::atan2(((-y1p - cp.y) / radius.y), ((-x1p - cp.x) / radius.x)); auto deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at; if (sweep) { //Ensure delta theta < 0 or else add 360 degrees if (deltaTheta < 0.0f) deltaTheta += 2.0f * MATH_PI; } else { //Ensure delta theta > 0 or else subtract 360 degrees if (deltaTheta > 0.0f) deltaTheta -= 2.0f * MATH_PI; } //Add several cubic bezier to approximate the arc //(smaller than 90 degrees) //We add one extra segment because we want something //Smaller than 90deg (i.e. not 90 itself) auto segments = int(fabsf(deltaTheta / MATH_PI2) + 1.0f); auto delta = deltaTheta / segments; //http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13) auto bcp = 4.0f / 3.0f * (1.0f - cosf(delta / 2.0f)) / sinf(delta / 2.0f); auto cosPhiR = Point{cosPhi * radius.x, cosPhi * radius.y}; auto sinPhiR = Point{sinPhi * radius.x, sinPhi * radius.y}; auto cosTheta1 = cosf(theta1); auto sinTheta1 = sinf(theta1); for (int i = 0; i < segments; ++i) { //End angle (for this segment) = current + delta auto theta2 = theta1 + delta; auto cosTheta2 = cosf(theta2); auto sinTheta2 = sinf(theta2); //First control point (based on start point sx,sy) auto c1 = start + Point{-bcp * (cosPhiR.x * sinTheta1 + sinPhiR.y * cosTheta1), bcp * (cosPhiR.y * cosTheta1 - sinPhiR.x * sinTheta1)}; //End point (for this segment) auto e = center + Point{cosPhiR.x * cosTheta2 - sinPhiR.y * sinTheta2, sinPhiR.x * cosTheta2 + cosPhiR.y * sinTheta2}; //Second control point (based on end point ex,ey) curCtl = e + Point{bcp * (cosPhiR.x * sinTheta2 + sinPhiR.y * cosTheta2), bcp * (sinPhiR.x * sinTheta2 - cosPhiR.y * cosTheta2)}; cur = e; out.cubicTo(c1, curCtl, cur); //Next start point is the current end point (same for angle) start = e; theta1 = theta2; //Avoid recomputations cosTheta1 = cosTheta2; sinTheta1 = sinTheta2; } } static int _numberCount(char cmd) { int count = 0; switch (cmd) { case 'M': case 'm': case 'L': case 'l': case 'T': case 't': { count = 2; break; } case 'C': case 'c': case 'E': case 'e': { count = 6; break; } case 'H': case 'h': case 'V': case 'v': { count = 1; break; } case 'S': case 's': case 'Q': case 'q': { count = 4; break; } case 'A': case 'a': { count = 7; break; } default: break; } return count; } static bool _processCommand(RenderPath& out, char cmd, float* arr, int count, Point& cur, Point& curCtl, Point& start, bool& quadratic, bool& closed) { switch (cmd) { case 'm': case 'l': case 'c': case 's': case 'q': case 't': { for (int i = 0; i < count - 1; i += 2) { arr[i] = arr[i] + cur.x; arr[i + 1] = arr[i + 1] + cur.y; } break; } case 'h': { arr[0] = arr[0] + cur.x; break; } case 'v': { arr[0] = arr[0] + cur.y; break; } case 'a': { arr[5] = arr[5] + cur.x; arr[6] = arr[6] + cur.y; break; } default: break; } switch (cmd) { case 'm': case 'M': { start = cur = {arr[0], arr[1]}; out.moveTo(cur); break; } case 'l': case 'L': { cur = {arr[0], arr[1]}; out.lineTo(cur); break; } case 'c': case 'C': { curCtl = {arr[2], arr[3]}; cur = {arr[4], arr[5]}; out.cubicTo({arr[0], arr[1]}, curCtl, cur); quadratic = false; break; } case 's': case 'S': { Point ctrl; if ((out.cmds.count > 1) && (out.cmds.last() == PathCommand::CubicTo) && !quadratic) { ctrl = 2 * cur - curCtl; } else { ctrl = cur; } curCtl = {arr[0], arr[1]}; cur = {arr[2], arr[3]}; out.cubicTo(ctrl, curCtl, cur); quadratic = false; break; } case 'q': case 'Q': { auto ctrl1 = (cur + 2 * Point{arr[0], arr[1]}) * (1.0f / 3.0f); auto ctrl2 = (Point{arr[2], arr[3]} + 2 * Point{arr[0], arr[1]}) * (1.0f / 3.0f); curCtl = {arr[0], arr[1]}; cur = {arr[2], arr[3]}; out.cubicTo(ctrl1, ctrl2, cur); quadratic = true; break; } case 't': case 'T': { Point ctrl; if ((out.cmds.count > 1) && (out.cmds.last() == PathCommand::CubicTo) && quadratic) { ctrl = 2 * cur - curCtl; } else { ctrl = cur; } auto ctrl1 = (cur + 2 * ctrl) * (1.0f / 3.0f); auto ctrl2 = (Point{arr[0], arr[1]} + 2 * ctrl) * (1.0f / 3.0f); curCtl = {ctrl.x, ctrl.y}; cur = {arr[0], arr[1]}; out.cubicTo(ctrl1, ctrl2, cur); quadratic = true; break; } case 'h': case 'H': { out.lineTo({arr[0], cur.y}); cur.x = arr[0]; break; } case 'v': case 'V': { out.lineTo({cur.x, arr[0]}); cur.y = arr[0]; break; } case 'z': case 'Z': { out.close(); cur = start; closed = true; break; } case 'a': case 'A': { if (tvg::zero(arr[0]) || tvg::zero(arr[1])) { cur = {arr[5], arr[6]}; out.lineTo(cur); } else if (!tvg::equal(cur.x, arr[5]) || !tvg::equal(cur.y, arr[6])) { _pathAppendArcTo(out, cur, curCtl, {arr[5], arr[6]}, {fabsf(arr[0]), fabsf(arr[1])}, deg2rad(arr[2]), arr[3], arr[4]); cur = curCtl = {arr[5], arr[6]}; quadratic = false; } break; } default: return false; } return true; } static char* _nextCommand(char* path, char* cmd, float* arr, int* count, bool* closed) { int large, sweep; path = _skipComma(path); if (isalpha(*path)) { *cmd = *path; path++; *count = _numberCount(*cmd); } else { if (*cmd == 'm') *cmd = 'l'; else if (*cmd == 'M') *cmd = 'L'; else { if (*closed) return nullptr; } } if (*count == 7) { //Special case for arc command if (_parseNumber(&path, &arr[0])) { if (_parseNumber(&path, &arr[1])) { if (_parseNumber(&path, &arr[2])) { if (_parseFlag(&path, &large)) { if (_parseFlag(&path, &sweep)) { if (_parseNumber(&path, &arr[5])) { if (_parseNumber(&path, &arr[6])) { arr[3] = (float)large; arr[4] = (float)sweep; return path; } } } } } } } *count = 0; return nullptr; } for (int i = 0; i < *count; i++) { if (!_parseNumber(&path, &arr[i])) { *count = 0; return nullptr; } path = _skipComma(path); } return path; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool svgPathToShape(const char* svgPath, RenderPath& out) { float numberArray[7]; int numberCount = 0; Point cur = {0, 0}; Point curCtl = {0, 0}; Point start = {0, 0}; char cmd = 0; auto path = (char*)svgPath; auto lastCmds = out.cmds.count; auto isQuadratic = false; auto closed = false; while ((path[0] != '\0')) { path = _nextCommand(path, &cmd, numberArray, &numberCount, &closed); if (!path) break; closed = false; if (!_processCommand(out, cmd, numberArray, numberCount, cur, curCtl, start, isQuadratic, closed)) break; } if (out.cmds.count > lastCmds && out.cmds[lastCmds] != PathCommand::MoveTo) return false; return true; } thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function.cpp000664 001750 001750 00000004345 15164251010 050227 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-conversion.h" #include "ecma-eval.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-lex-env.h" #include "js-parser.h" #include "lit-magic-strings.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-function.inc.h" #define BUILTIN_UNDERSCORED_ID function #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup function ECMA Function object built-in * @{ */ /** * Handle calling [[Call]] of built-in Function object * * @return ecma value */ ecma_value_t ecma_builtin_function_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_builtin_function_dispatch_construct (arguments_list_p, arguments_list_len); } /* ecma_builtin_function_dispatch_call */ /** * Handle calling [[Construct]] of built-in Function object * * See also: * ECMA-262 v5, 15.3. * * @return ecma value */ ecma_value_t ecma_builtin_function_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_op_create_dynamic_function (arguments_list_p, arguments_list_len, ECMA_PARSE_NO_OPTS); } /* ecma_builtin_function_dispatch_construct */ /** * @} * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-object.h000664 001750 001750 00000002635 15164251010 047315 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BUILTIN_OBJECT_H #define ECMA_BUILTIN_OBJECT_H #include "ecma-globals.h" ecma_value_t ecma_builtin_object_object_get_prototype_of (ecma_object_t *obj_p); ecma_value_t ecma_builtin_object_object_set_prototype_of (ecma_value_t arg1, ecma_value_t arg2); ecma_value_t ecma_builtin_object_object_set_proto (ecma_value_t arg1, ecma_value_t arg2); ecma_value_t ecma_builtin_object_object_prevent_extensions (ecma_object_t *obj_p); ecma_value_t ecma_builtin_object_object_is_extensible (ecma_object_t *obj_p); ecma_value_t ecma_builtin_object_object_get_own_property_descriptor (ecma_object_t *obj_p, ecma_string_t *name_str_p); ecma_value_t ecma_builtin_object_object_define_property (ecma_object_t *obj_p, ecma_string_t *name_str_p, ecma_value_t arg3); #endif /* !ECMA_BUILTIN_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/lottie2gif/meson.build000664 001750 001750 00000000361 15164251010 034567 0ustar00ddennedyddennedy000000 000000 lottie2gif_src = files('lottie2gif.cpp') executable('tvg-lottie2gif', lottie2gif_src, include_directories : headers, cpp_args : compiler_flags, install : true, link_with : thorvg_lib) src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/quant_levels_dec.h000664 001750 001750 00000002264 15164251010 037145 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2013 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Alpha plane de-quantization utility // // Author: Vikas Arora (vikasa@google.com) #ifndef WEBP_UTILS_QUANT_LEVELS_DEC_H_ #define WEBP_UTILS_QUANT_LEVELS_DEC_H_ #include "../webp/types.h" #ifdef __cplusplus extern "C" { #endif // Apply post-processing to input 'data' of size 'width'x'height' assuming that // the source was quantized to a reduced number of levels. // Strength is in [0..100] and controls the amount of dithering applied. // Returns false in case of error (data is NULL, invalid parameters, // malloc failure, ...). int WebPDequantizeLevels(uint8_t* const data, int width, int height, int strength); #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_UTILS_QUANT_LEVELS_DEC_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/regression/check_same_image_size.py000664 001750 001750 00000002513 15164251010 037126 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0import os import subprocess import sys if ( len(sys.argv) != 5 or not sys.argv[1].endswith(".svg") or not os.path.isfile(sys.argv[1]) or not os.path.isfile(sys.argv[2]) ): print('Proper usage "python app.py AA.svg /path/to/svg2png 400 100"') print('Proper usage "python app.py SVG_FILE SVG_PNG_PATH SIZE_IMAGE NUMBER_OF_TRIES"') raise ValueError( "POSSIBLE_PROBLEM - Missing or invalid input file or missing path to svg2png" ) try_number = int(sys.argv[4]) image_size = sys.argv[3] svg2png_path = sys.argv[2] image_input = sys.argv[1] image_output = image_input.replace(".svg", ".png") args = [svg2png_path, image_input, "-r", f"{image_size}x{image_size}"] sizes: dict = {} for i in range(try_number + 1): # if i % 100 == 0: # print(f"{i + 1}/{try_number + 1}") subprocess.call(args, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) new_size = os.path.getsize(image_output) if new_size in sizes: sizes[new_size] += 1 else: sizes[new_size] = 1 sizes = dict(sorted(sizes.items(), key=lambda item: item[1], reverse=True)) if len(sizes) == 1: print(f"Not found problem with generating images, sizes - {str(sizes)}") else: print( f"POSSIBLE_PROBLEM - Converting svg to png is not reproducible - file sizes {str(sizes)}", file=sys.stderr, ) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testText.cpp000664 001750 001750 00000025754 15164251010 032541 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; #ifdef THORVG_TTF_LOADER_SUPPORT TEST_CASE("Text Creation", "[tvgText]") { auto text = Text::gen(); REQUIRE(text); REQUIRE(text->type() == Type::Text); Paint::rel(text); } TEST_CASE("Load TTF Data from a file", "[tvgText]") { Initializer::init(); { auto text = Text::gen(); REQUIRE(text); REQUIRE(Text::unload(TEST_DIR"/invalid.ttf") == Result::InsufficientCondition); REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(Text::load(TEST_DIR"/invalid.ttf") == Result::InvalidArguments); REQUIRE(Text::unload(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(Text::load("") == Result::InvalidArguments); REQUIRE(Text::load(TEST_DIR"/NanumGothicCoding.ttf") == Result::Success); Paint::rel(text); } Initializer::term(); } TEST_CASE("Load TTF Data from a memory", "[tvgText]") { Initializer::init(); { ifstream file(TEST_DIR"/Arial.ttf", ios::binary); REQUIRE(file.is_open()); file.seekg(0, std::ios::end); auto size = file.tellg(); file.seekg(0, std::ios::beg); auto data = (char*)malloc(size); REQUIRE(data); file.read(data, size); file.close(); auto text = Text::gen(); REQUIRE(text); static const char* svg = ""; //load REQUIRE(Text::load(nullptr, data, size) == Result::InvalidArguments); REQUIRE(Text::load("Arial", data, 0) == Result::InvalidArguments); REQUIRE(Text::load("Arial", data, 0) == Result::InvalidArguments); REQUIRE(Text::load("ArialSvg", svg, strlen(svg), "unknown") == Result::NonSupport); REQUIRE(Text::load("ArialUnknown", data, size, "unknown") == Result::Success); REQUIRE(Text::load("ArialTtf", data, size, "ttf", true) == Result::Success); REQUIRE(Text::load("Arial", data, size, "") == Result::Success); //unload REQUIRE(Text::load("invalid", nullptr, 0) == Result::InsufficientCondition); REQUIRE(Text::load("ArialSvg", nullptr, 0) == Result::InsufficientCondition); REQUIRE(Text::load("ArialUnknown", nullptr, 0) == Result::Success); REQUIRE(Text::load("ArialTtf", nullptr, 0) == Result::Success); REQUIRE(Text::load("Arial", nullptr, 111) == Result::Success); free(data); Paint::rel(text); } Initializer::term(); } TEST_CASE("Text Font", "[tvgText]") { Initializer::init(); { auto text = Text::gen(); REQUIRE(text); REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(80) == Result::Success); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(1) == Result::Success); REQUIRE(text->size(50) == Result::Success); REQUIRE(text->font(nullptr) == Result::Success); REQUIRE(text->font("InvalidFont") == Result::InsufficientCondition); Paint::rel(text); } Initializer::term(); } TEST_CASE("Text Basic", "[tvgText]") { Initializer::init(); { auto canvas = unique_ptr(SwCanvas::gen()); uint32_t buffer[100*100]; canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888); auto text = Text::gen(); REQUIRE(text); REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(80) == Result::Success); REQUIRE(text->text(nullptr) == Result::Success); REQUIRE(text->text("") == Result::Success); REQUIRE(text->text("ABCDEFGHIJIKLMOPQRSTUVWXYZ") == Result::Success); REQUIRE(text->text("THORVG Text") == Result::Success); REQUIRE(text->fill(255, 255, 255) == Result::Success); REQUIRE(canvas->add(text) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } Initializer::term(); } TEST_CASE("Text with composite glyphs", "[tvgText]") { Initializer::init(); { auto canvas = unique_ptr(SwCanvas::gen()); uint32_t buffer[100*100]; canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888); auto text = Text::gen(); REQUIRE(text); REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(80) == Result::Success); REQUIRE(text->text("\xc5\xbb\x6f\xc5\x82\xc4\x85\x64\xc5\xba \xc8\xab") == Result::Success); REQUIRE(text->fill(255, 255, 255) == Result::Success); REQUIRE(canvas->add(text) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } Initializer::term(); } TEST_CASE("Text Styles", "[tvgText]") { Initializer::init(); { auto canvas = unique_ptr(SwCanvas::gen()); uint32_t buffer[100*100]; canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888); auto text = Text::gen(); REQUIRE(text); REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(80) == Result::Success); REQUIRE(text->text("ThorVG Test\n Text!") == Result::Success); REQUIRE(text->fill(255, 255, 255) == Result::Success); REQUIRE(text->outline(0, 0, 0, 0) == Result::Success); REQUIRE(text->outline(3, 255, 255, 255) == Result::Success); REQUIRE(text->outline(0, 0, 0, 0) == Result::Success); REQUIRE(text->italic(-10.0f) == Result::Success); REQUIRE(text->italic(10000.0f) == Result::Success); REQUIRE(text->italic(0.0) == Result::Success); REQUIRE(text->italic(0.18f) == Result::Success); REQUIRE(canvas->add(text) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } Initializer::term(); } TEST_CASE("Text Layout", "[tvgText]") { Initializer::init(); { auto canvas = unique_ptr(SwCanvas::gen()); uint32_t buffer[100*100]; canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888); auto text = Text::gen(); REQUIRE(text); REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(80) == Result::Success); REQUIRE(text->fill(255, 255, 255) == Result::Success); REQUIRE(text->text("ThorVG Test\n Text!") == Result::Success); REQUIRE(text->align(0.0f, 0.0f) == Result::Success); REQUIRE(text->align(0.5f, 0.5f) == Result::Success); REQUIRE(text->align(1.0f, 1.0f) == Result::Success); REQUIRE(text->align(2.0f, 2.0f) == Result::Success); REQUIRE(text->align(-1.0f, -1.0f) == Result::Success); REQUIRE(text->layout(0, 0) == Result::Success); REQUIRE(text->layout(-100, -100) == Result::Success); REQUIRE(text->layout(100, 100) == Result::Success); REQUIRE(canvas->add(text) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } Initializer::term(); } TEST_CASE("Text Wrap Mode", "[tvgText]") { Initializer::init(); { auto canvas = unique_ptr(SwCanvas::gen()); uint32_t buffer[100*100]; canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888); auto text = Text::gen(); REQUIRE(text); REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(80) == Result::Success); REQUIRE(text->fill(255, 255, 255) == Result::Success); REQUIRE(text->align(0.5f, 0.5f) == Result::Success); REQUIRE(text->text("Very Long Long Text ThorVG Test\n ABCDEFGHIJKLMNOPRSTU!") == Result::Success); REQUIRE(text->layout(100, 100) == Result::Success); REQUIRE(canvas->add(text) == Result::Success); REQUIRE(text->wrap(TextWrap::Character) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->sync() == Result::Success); REQUIRE(text->wrap(TextWrap::Word) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->sync() == Result::Success); REQUIRE(text->wrap(TextWrap::Smart) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->sync() == Result::Success); REQUIRE(text->wrap(TextWrap::Ellipsis) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } Initializer::term(); } TEST_CASE("Text Spacing", "[tvgText]") { Initializer::init(); { auto canvas = unique_ptr(SwCanvas::gen()); uint32_t buffer[100*100]; canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888); auto text = Text::gen(); REQUIRE(text); REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(80) == Result::Success); REQUIRE(text->text("\xc5\xbb\x6f\xc5\x82\xc4\x85\x64\xc5\xba \xc8\xab") == Result::Success); REQUIRE(text->spacing(-1.0f, -1.0f) == Result::InvalidArguments); REQUIRE(text->spacing(0.0f, 0.0f) == Result::Success); REQUIRE(text->spacing(1.5f, 1.5f) == Result::Success); REQUIRE(text->spacing(2.0f, 2.0f) == Result::Success); REQUIRE(canvas->add(text) == Result::Success); } Initializer::term(); } #endifexternal/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/re-parser.cpp000664 001750 001750 00000111356 15164251010 044520 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "re-parser.h" #include "ecma-exceptions.h" #include "ecma-globals.h" #include "jcontext.h" #include "jrt-libc-includes.h" #include "lit-char-helpers.h" #include "re-compiler.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup parser Parser * @{ * * \addtogroup regexparser Regular expression * @{ * * \addtogroup regexparser_parser Parser * @{ */ /** * Get the start opcode for the current group. * * @return RegExp opcode */ static re_opcode_t re_get_group_start_opcode (bool is_capturing) /**< is capturing group */ { return (is_capturing) ? RE_OP_CAPTURING_GROUP_START : RE_OP_NON_CAPTURING_GROUP_START; } /* re_get_group_start_opcode */ /** * Get the end opcode for the current group. * * @return RegExp opcode */ static re_opcode_t re_get_group_end_opcode (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context */ bool is_capturing) /**< is capturing group */ { if (is_capturing) { if (re_ctx_p->token.greedy) { return RE_OP_GREEDY_CAPTURING_GROUP_END; } return RE_OP_LAZY_CAPTURING_GROUP_END; } if (re_ctx_p->token.greedy) { return RE_OP_GREEDY_NON_CAPTURING_GROUP_END; } return RE_OP_LAZY_NON_CAPTURING_GROUP_END; } /* re_get_group_end_opcode */ /** * Enclose the given bytecode to a group. */ static void re_insert_into_group (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context */ uint32_t group_start_offset, /**< offset of group start */ uint32_t idx, /**< index of group */ uint32_t capture_start, /**< index of first nested capture */ bool is_capturing) /**< is capturing group */ { uint32_t qmin = re_ctx_p->token.qmin; uint32_t qmax = re_ctx_p->token.qmax; if (JERRY_UNLIKELY (!is_capturing && re_bytecode_size (re_ctx_p) == group_start_offset)) { return; } if (qmin == 0) { re_insert_value (re_ctx_p, group_start_offset, re_bytecode_size (re_ctx_p) - group_start_offset); } re_insert_value (re_ctx_p, group_start_offset, qmin); re_insert_value (re_ctx_p, group_start_offset, re_ctx_p->captures_count - capture_start); if (!is_capturing) { re_insert_value (re_ctx_p, group_start_offset, capture_start); } else { JERRY_ASSERT (idx == capture_start); } re_insert_value (re_ctx_p, group_start_offset, idx); re_insert_opcode (re_ctx_p, group_start_offset, re_get_group_start_opcode (is_capturing)); re_append_opcode (re_ctx_p, re_get_group_end_opcode (re_ctx_p, is_capturing)); re_append_value (re_ctx_p, idx); re_append_value (re_ctx_p, qmin); re_append_value (re_ctx_p, qmax + RE_QMAX_OFFSET); } /* re_insert_into_group */ /** * Insert simple atom iterator. */ static void re_insert_atom_iterator (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context */ uint32_t start_offset) /**< atom start offset */ { const uint32_t qmin = re_ctx_p->token.qmin; const uint32_t qmax = re_ctx_p->token.qmax; if (qmin == 1 && qmax == 1) { return; } re_append_opcode (re_ctx_p, RE_OP_ITERATOR_END); re_insert_value (re_ctx_p, start_offset, re_bytecode_size (re_ctx_p) - start_offset); re_insert_value (re_ctx_p, start_offset, qmax + RE_QMAX_OFFSET); re_insert_value (re_ctx_p, start_offset, qmin); re_insert_opcode (re_ctx_p, start_offset, re_ctx_p->token.greedy ? RE_OP_GREEDY_ITERATOR : RE_OP_LAZY_ITERATOR); } /* re_insert_atom_iterator */ /** * Insert a lookahead assertion. */ static void re_insert_assertion_lookahead (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context */ uint32_t start_offset, /**< atom start offset */ uint32_t capture_start, /**< index of first nested capture */ bool negative) /** lookahead type */ { const uint32_t qmin = re_ctx_p->token.qmin; re_append_opcode (re_ctx_p, RE_OP_ASSERT_END); re_insert_value (re_ctx_p, start_offset, re_bytecode_size (re_ctx_p) - start_offset); /* We need to clear nested capturing group results when a negative assertion or the tail after a positive assertion * does not match, so we store the begin and end index of nested capturing groups. */ re_insert_value (re_ctx_p, start_offset, re_ctx_p->captures_count - capture_start); re_insert_value (re_ctx_p, start_offset, capture_start); /* Lookaheads always result in zero length matches, which means iterations will always stop on the first match. * This allows us to not have to deal with iterations beyond one. Either qmin == 0 which will implicitly match, * or qmin > 0, in which case the first iteration will decide whether the assertion matches depending on whether * the iteration matched or not. This also allows us to ignore qmax entirely. */ re_insert_byte (re_ctx_p, start_offset, (uint8_t) JERRY_MIN (qmin, 1)); const re_opcode_t opcode = (negative) ? RE_OP_ASSERT_LOOKAHEAD_NEG : RE_OP_ASSERT_LOOKAHEAD_POS; re_insert_opcode (re_ctx_p, start_offset, opcode); } /* re_insert_assertion_lookahead */ /** * Consume non greedy (question mark) character if present. */ static void re_parse_lazy_char (re_compiler_ctx_t *re_ctx_p) /**< RegExp parser context */ { if (re_ctx_p->input_curr_p < re_ctx_p->input_end_p && *re_ctx_p->input_curr_p == LIT_CHAR_QUESTION) { re_ctx_p->input_curr_p++; re_ctx_p->token.greedy = false; return; } re_ctx_p->token.greedy = true; } /* re_parse_lazy_char */ /** * Parse a max 3 digit long octal number from the input string, with a decimal value less than 256. * * @return value of the octal number */ static uint32_t re_parse_octal (re_compiler_ctx_t *re_ctx_p) /**< RegExp parser context */ { JERRY_ASSERT (re_ctx_p->input_curr_p < re_ctx_p->input_end_p); JERRY_ASSERT (lit_char_is_octal_digit (*re_ctx_p->input_curr_p)); uint32_t value = (uint32_t) (*re_ctx_p->input_curr_p++) - LIT_CHAR_0; if (re_ctx_p->input_curr_p < re_ctx_p->input_end_p && lit_char_is_octal_digit (*re_ctx_p->input_curr_p)) { value = value * 8 + (*re_ctx_p->input_curr_p++) - LIT_CHAR_0; } if (re_ctx_p->input_curr_p < re_ctx_p->input_end_p && lit_char_is_octal_digit (*re_ctx_p->input_curr_p)) { const uint32_t new_value = value * 8 + (*re_ctx_p->input_curr_p) - LIT_CHAR_0; if (new_value <= RE_MAX_OCTAL_VALUE) { value = new_value; re_ctx_p->input_curr_p++; } } return value; } /* re_parse_octal */ /** * Check that the currently parsed quantifier is valid. * * @return ECMA_VALUE_ERROR, if quantifier is invalid * ECMA_VALUE_EMPTY, otherwise */ static ecma_value_t re_check_quantifier (re_compiler_ctx_t *re_ctx_p) { if (re_ctx_p->token.qmin > re_ctx_p->token.qmax) { /* ECMA-262 v5.1 15.10.2.5 */ return ecma_raise_syntax_error (ECMA_ERR_MIN_GREATER_THAN_MAX); } return ECMA_VALUE_EMPTY; } /* re_check_quantifier */ /** * Parse RegExp quantifier. * * @return ECMA_VALUE_TRUE - if parsed successfully * ECMA_VALUE_FALSE - otherwise */ static ecma_value_t re_parse_quantifier (re_compiler_ctx_t *re_ctx_p) /**< RegExp compiler context */ { if (re_ctx_p->input_curr_p < re_ctx_p->input_end_p) { switch (*re_ctx_p->input_curr_p) { case LIT_CHAR_QUESTION: { re_ctx_p->input_curr_p++; re_ctx_p->token.qmin = 0; re_ctx_p->token.qmax = 1; re_parse_lazy_char (re_ctx_p); return ECMA_VALUE_TRUE; } case LIT_CHAR_ASTERISK: { re_ctx_p->input_curr_p++; re_ctx_p->token.qmin = 0; re_ctx_p->token.qmax = RE_INFINITY; re_parse_lazy_char (re_ctx_p); return ECMA_VALUE_TRUE; } case LIT_CHAR_PLUS: { re_ctx_p->input_curr_p++; re_ctx_p->token.qmin = 1; re_ctx_p->token.qmax = RE_INFINITY; re_parse_lazy_char (re_ctx_p); return ECMA_VALUE_TRUE; } case LIT_CHAR_LEFT_BRACE: { const lit_utf8_byte_t *current_p = re_ctx_p->input_curr_p + 1; uint32_t qmin = 0; uint32_t qmax = RE_INFINITY; if (current_p >= re_ctx_p->input_end_p) { break; } if (!lit_char_is_decimal_digit (*current_p)) { break; } qmin = lit_parse_decimal (¤t_p, re_ctx_p->input_end_p); if (current_p >= re_ctx_p->input_end_p) { break; } lit_utf8_byte_t ch = *current_p++; if (ch == LIT_CHAR_RIGHT_BRACE) { qmax = qmin; } else if (ch == LIT_CHAR_COMMA) { if (current_p >= re_ctx_p->input_end_p) { break; } if (lit_char_is_decimal_digit (*current_p)) { qmax = lit_parse_decimal (¤t_p, re_ctx_p->input_end_p); } if (current_p >= re_ctx_p->input_end_p || *current_p++ != LIT_CHAR_RIGHT_BRACE) { break; } } else { break; } re_ctx_p->token.qmin = qmin; re_ctx_p->token.qmax = qmax; re_ctx_p->input_curr_p = current_p; re_parse_lazy_char (re_ctx_p); return ECMA_VALUE_TRUE; } default: { break; } } } re_ctx_p->token.qmin = 1; re_ctx_p->token.qmax = 1; re_ctx_p->token.greedy = true; return ECMA_VALUE_FALSE; } /* re_parse_quantifier */ /** * Count the number of groups in the current pattern. */ static void re_count_groups (re_compiler_ctx_t *re_ctx_p) /**< RegExp compiler context */ { bool is_char_class = 0; re_ctx_p->groups_count = 0; const lit_utf8_byte_t *curr_p = re_ctx_p->input_start_p; while (curr_p < re_ctx_p->input_end_p) { switch (*curr_p++) { case LIT_CHAR_BACKSLASH: { if (curr_p < re_ctx_p->input_end_p) { lit_utf8_incr (&curr_p); } break; } case LIT_CHAR_LEFT_SQUARE: { is_char_class = true; break; } case LIT_CHAR_RIGHT_SQUARE: { is_char_class = false; break; } case LIT_CHAR_LEFT_PAREN: { if (curr_p < re_ctx_p->input_end_p && *curr_p != LIT_CHAR_QUESTION && !is_char_class) { re_ctx_p->groups_count++; } break; } } } } /* re_count_groups */ /** * Check if a code point is a Syntax character * * @return true, if syntax character * false, otherwise */ static bool re_is_syntax_char (lit_code_point_t cp) /**< code point */ { return (cp == LIT_CHAR_CIRCUMFLEX || cp == LIT_CHAR_DOLLAR_SIGN || cp == LIT_CHAR_BACKSLASH || cp == LIT_CHAR_DOT || cp == LIT_CHAR_ASTERISK || cp == LIT_CHAR_PLUS || cp == LIT_CHAR_QUESTION || cp == LIT_CHAR_LEFT_PAREN || cp == LIT_CHAR_RIGHT_PAREN || cp == LIT_CHAR_LEFT_SQUARE || cp == LIT_CHAR_RIGHT_SQUARE || cp == LIT_CHAR_LEFT_BRACE || cp == LIT_CHAR_RIGHT_BRACE || cp == LIT_CHAR_VLINE); } /* re_is_syntax_char */ /** * Parse a Character Escape or a Character Class Escape. * * @return ECMA_VALUE_EMPTY, if parsed successfully * ECMA_VALUE_ERROR, otherwise */ static ecma_value_t re_parse_char_escape (re_compiler_ctx_t *re_ctx_p) /**< RegExp compiler context */ { JERRY_ASSERT (re_ctx_p->input_curr_p < re_ctx_p->input_end_p); re_ctx_p->token.type = RE_TOK_CHAR; if (lit_char_is_decimal_digit (*re_ctx_p->input_curr_p)) { /* NULL code point escape, only valid if there are no following digits. */ if (*re_ctx_p->input_curr_p == LIT_CHAR_0 && (re_ctx_p->input_curr_p + 1 >= re_ctx_p->input_end_p || !lit_char_is_decimal_digit (re_ctx_p->input_curr_p[1]))) { re_ctx_p->input_curr_p++; re_ctx_p->token.value = LIT_UNICODE_CODE_POINT_NULL; return ECMA_VALUE_EMPTY; } if (re_ctx_p->flags & RE_FLAG_UNICODE) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_ESCAPE_SEQUENCE); } /* Legacy octal escape sequence */ if (lit_char_is_octal_digit (*re_ctx_p->input_curr_p)) { re_ctx_p->token.value = re_parse_octal (re_ctx_p); return ECMA_VALUE_EMPTY; } /* Identity escape */ re_ctx_p->token.value = *re_ctx_p->input_curr_p++; return ECMA_VALUE_EMPTY; } lit_code_point_t ch = lit_cesu8_read_next (&re_ctx_p->input_curr_p); switch (ch) { /* Character Class escapes */ case LIT_CHAR_LOWERCASE_D: { re_ctx_p->token.type = RE_TOK_CLASS_ESCAPE; re_ctx_p->token.value = RE_ESCAPE_DIGIT; break; } case LIT_CHAR_UPPERCASE_D: { re_ctx_p->token.type = RE_TOK_CLASS_ESCAPE; re_ctx_p->token.value = RE_ESCAPE_NOT_DIGIT; break; } case LIT_CHAR_LOWERCASE_S: { re_ctx_p->token.type = RE_TOK_CLASS_ESCAPE; re_ctx_p->token.value = RE_ESCAPE_WHITESPACE; break; } case LIT_CHAR_UPPERCASE_S: { re_ctx_p->token.type = RE_TOK_CLASS_ESCAPE; re_ctx_p->token.value = RE_ESCAPE_NOT_WHITESPACE; break; } case LIT_CHAR_LOWERCASE_W: { re_ctx_p->token.type = RE_TOK_CLASS_ESCAPE; re_ctx_p->token.value = RE_ESCAPE_WORD_CHAR; break; } case LIT_CHAR_UPPERCASE_W: { re_ctx_p->token.type = RE_TOK_CLASS_ESCAPE; re_ctx_p->token.value = RE_ESCAPE_NOT_WORD_CHAR; break; } /* Control escapes */ case LIT_CHAR_LOWERCASE_F: { re_ctx_p->token.value = LIT_CHAR_FF; break; } case LIT_CHAR_LOWERCASE_N: { re_ctx_p->token.value = LIT_CHAR_LF; break; } case LIT_CHAR_LOWERCASE_R: { re_ctx_p->token.value = LIT_CHAR_CR; break; } case LIT_CHAR_LOWERCASE_T: { re_ctx_p->token.value = LIT_CHAR_TAB; break; } case LIT_CHAR_LOWERCASE_V: { re_ctx_p->token.value = LIT_CHAR_VTAB; break; } /* Control letter */ case LIT_CHAR_LOWERCASE_C: { if (re_ctx_p->input_curr_p < re_ctx_p->input_end_p) { ch = *re_ctx_p->input_curr_p; if ((ch >= LIT_CHAR_ASCII_UPPERCASE_LETTERS_BEGIN && ch <= LIT_CHAR_ASCII_UPPERCASE_LETTERS_END) || (ch >= LIT_CHAR_ASCII_LOWERCASE_LETTERS_BEGIN && ch <= LIT_CHAR_ASCII_LOWERCASE_LETTERS_END)) { re_ctx_p->token.value = (ch % 32); re_ctx_p->input_curr_p++; break; } } if (re_ctx_p->flags & RE_FLAG_UNICODE) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_CONTROL_ESCAPE_SEQUENCE); } re_ctx_p->token.value = LIT_CHAR_BACKSLASH; re_ctx_p->input_curr_p--; break; } /* Hex escape */ case LIT_CHAR_LOWERCASE_X: { uint32_t hex_value = lit_char_hex_lookup (re_ctx_p->input_curr_p, re_ctx_p->input_end_p, 2); if (hex_value != UINT32_MAX) { re_ctx_p->token.value = hex_value; re_ctx_p->input_curr_p += 2; break; } if (re_ctx_p->flags & RE_FLAG_UNICODE) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_HEX_ESCAPE_SEQUENCE); } re_ctx_p->token.value = LIT_CHAR_LOWERCASE_X; break; } /* Unicode escape */ case LIT_CHAR_LOWERCASE_U: { uint32_t hex_value = lit_char_hex_lookup (re_ctx_p->input_curr_p, re_ctx_p->input_end_p, 4); if (hex_value != UINT32_MAX) { re_ctx_p->token.value = hex_value; re_ctx_p->input_curr_p += 4; if (re_ctx_p->flags & RE_FLAG_UNICODE && lit_is_code_point_utf16_high_surrogate (re_ctx_p->token.value) && re_ctx_p->input_curr_p + 6 <= re_ctx_p->input_end_p && re_ctx_p->input_curr_p[0] == '\\' && re_ctx_p->input_curr_p[1] == 'u') { hex_value = lit_char_hex_lookup (re_ctx_p->input_curr_p + 2, re_ctx_p->input_end_p, 4); if (lit_is_code_point_utf16_low_surrogate (hex_value)) { re_ctx_p->token.value = lit_convert_surrogate_pair_to_code_point ((ecma_char_t) re_ctx_p->token.value, (ecma_char_t) hex_value); re_ctx_p->input_curr_p += 6; } } break; } if (re_ctx_p->flags & RE_FLAG_UNICODE) { if (re_ctx_p->input_curr_p + 1 < re_ctx_p->input_end_p && re_ctx_p->input_curr_p[0] == LIT_CHAR_LEFT_BRACE && lit_char_is_hex_digit (re_ctx_p->input_curr_p[1])) { lit_code_point_t cp = lit_char_hex_to_int (re_ctx_p->input_curr_p[1]); re_ctx_p->input_curr_p += 2; while (re_ctx_p->input_curr_p < re_ctx_p->input_end_p && lit_char_is_hex_digit (*re_ctx_p->input_curr_p)) { cp = cp * 16 + lit_char_hex_to_int (*re_ctx_p->input_curr_p++); if (JERRY_UNLIKELY (cp > LIT_UNICODE_CODE_POINT_MAX)) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE); } } if (re_ctx_p->input_curr_p < re_ctx_p->input_end_p && *re_ctx_p->input_curr_p == LIT_CHAR_RIGHT_BRACE) { re_ctx_p->input_curr_p++; re_ctx_p->token.value = cp; break; } } return ecma_raise_syntax_error (ECMA_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE); } re_ctx_p->token.value = LIT_CHAR_LOWERCASE_U; break; } /* Identity escape */ default: { /* Must be '/', or one of SyntaxCharacter */ if (re_ctx_p->flags & RE_FLAG_UNICODE && ch != LIT_CHAR_SLASH && !re_is_syntax_char (ch)) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_ESCAPE); } re_ctx_p->token.value = ch; } } return ECMA_VALUE_EMPTY; } /* re_parse_char_escape */ /** * Read the input pattern and parse the next token for the RegExp compiler * * @return empty ecma value - if parsed successfully * error ecma value - otherwise * * Returned value must be freed with ecma_free_value */ static ecma_value_t re_parse_next_token (re_compiler_ctx_t *re_ctx_p) /**< RegExp compiler context */ { if (re_ctx_p->input_curr_p >= re_ctx_p->input_end_p) { re_ctx_p->token.type = RE_TOK_EOF; return ECMA_VALUE_EMPTY; } ecma_char_t ch = lit_cesu8_read_next (&re_ctx_p->input_curr_p); switch (ch) { case LIT_CHAR_CIRCUMFLEX: { re_ctx_p->token.type = RE_TOK_ASSERT_START; return ECMA_VALUE_EMPTY; } case LIT_CHAR_DOLLAR_SIGN: { re_ctx_p->token.type = RE_TOK_ASSERT_END; return ECMA_VALUE_EMPTY; } case LIT_CHAR_VLINE: { re_ctx_p->token.type = RE_TOK_ALTERNATIVE; return ECMA_VALUE_EMPTY; } case LIT_CHAR_DOT: { re_ctx_p->token.type = RE_TOK_PERIOD; /* Check quantifier */ break; } case LIT_CHAR_BACKSLASH: { if (re_ctx_p->input_curr_p >= re_ctx_p->input_end_p) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_ESCAPE); } /* DecimalEscape, Backreferences cannot start with a zero digit. */ if (*re_ctx_p->input_curr_p > LIT_CHAR_0 && *re_ctx_p->input_curr_p <= LIT_CHAR_9) { const lit_utf8_byte_t *digits_p = re_ctx_p->input_curr_p; const uint32_t value = lit_parse_decimal (&digits_p, re_ctx_p->input_end_p); if (re_ctx_p->groups_count < 0) { re_count_groups (re_ctx_p); } if (value <= (uint32_t) re_ctx_p->groups_count) { /* Valid backreference */ re_ctx_p->input_curr_p = digits_p; re_ctx_p->token.type = RE_TOK_BACKREFERENCE; re_ctx_p->token.value = value; /* Check quantifier */ break; } } if (*re_ctx_p->input_curr_p == LIT_CHAR_LOWERCASE_B) { re_ctx_p->input_curr_p++; re_ctx_p->token.type = RE_TOK_ASSERT_WORD_BOUNDARY; return ECMA_VALUE_EMPTY; } else if (*re_ctx_p->input_curr_p == LIT_CHAR_UPPERCASE_B) { re_ctx_p->input_curr_p++; re_ctx_p->token.type = RE_TOK_ASSERT_NOT_WORD_BOUNDARY; return ECMA_VALUE_EMPTY; } const ecma_value_t parse_result = re_parse_char_escape (re_ctx_p); if (ECMA_IS_VALUE_ERROR (parse_result)) { return parse_result; } /* Check quantifier */ break; } case LIT_CHAR_LEFT_PAREN: { if (re_ctx_p->input_curr_p >= re_ctx_p->input_end_p) { return ecma_raise_syntax_error (ECMA_ERR_UNTERMINATED_GROUP); } if (*re_ctx_p->input_curr_p == LIT_CHAR_QUESTION) { re_ctx_p->input_curr_p++; if (re_ctx_p->input_curr_p >= re_ctx_p->input_end_p) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_GROUP); } ch = *re_ctx_p->input_curr_p++; if (ch == LIT_CHAR_EQUALS) { re_ctx_p->token.type = RE_TOK_ASSERT_LOOKAHEAD; re_ctx_p->token.value = false; } else if (ch == LIT_CHAR_EXCLAMATION) { re_ctx_p->token.type = RE_TOK_ASSERT_LOOKAHEAD; re_ctx_p->token.value = true; } else if (ch == LIT_CHAR_COLON) { re_ctx_p->token.type = RE_TOK_START_NON_CAPTURE_GROUP; } else { return ecma_raise_syntax_error (ECMA_ERR_INVALID_GROUP); } } else { re_ctx_p->token.type = RE_TOK_START_CAPTURE_GROUP; } return ECMA_VALUE_EMPTY; } case LIT_CHAR_RIGHT_PAREN: { re_ctx_p->token.type = RE_TOK_END_GROUP; return ECMA_VALUE_EMPTY; } case LIT_CHAR_LEFT_SQUARE: { re_ctx_p->token.type = RE_TOK_CHAR_CLASS; if (re_ctx_p->input_curr_p >= re_ctx_p->input_end_p) { return ecma_raise_syntax_error (ECMA_ERR_UNTERMINATED_CHARACTER_CLASS); } return ECMA_VALUE_EMPTY; } case LIT_CHAR_QUESTION: case LIT_CHAR_ASTERISK: case LIT_CHAR_PLUS: { return ecma_raise_syntax_error (ECMA_ERR_INVALID_QUANTIFIER); } case LIT_CHAR_LEFT_BRACE: { re_ctx_p->input_curr_p--; if (ecma_is_value_true (re_parse_quantifier (re_ctx_p))) { return ecma_raise_syntax_error (ECMA_ERR_NOTHING_TO_REPEAT); } if (re_ctx_p->flags & RE_FLAG_UNICODE) { return ecma_raise_syntax_error (ECMA_ERR_LONE_QUANTIFIER_BRACKET); } re_ctx_p->input_curr_p++; re_ctx_p->token.type = RE_TOK_CHAR; re_ctx_p->token.value = ch; /* Check quantifier */ break; } case LIT_CHAR_RIGHT_SQUARE: case LIT_CHAR_RIGHT_BRACE: { if (re_ctx_p->flags & RE_FLAG_UNICODE) { return ecma_raise_syntax_error (ECMA_ERR_LONE_QUANTIFIER_BRACKET); } /* FALLTHRU */ } default: { re_ctx_p->token.type = RE_TOK_CHAR; re_ctx_p->token.value = ch; if (re_ctx_p->flags & RE_FLAG_UNICODE && lit_is_code_point_utf16_high_surrogate (ch) && re_ctx_p->input_curr_p < re_ctx_p->input_end_p) { const ecma_char_t next = lit_cesu8_peek_next (re_ctx_p->input_curr_p); if (lit_is_code_point_utf16_low_surrogate (next)) { re_ctx_p->token.value = lit_convert_surrogate_pair_to_code_point (ch, next); re_ctx_p->input_curr_p += LIT_UTF8_MAX_BYTES_IN_CODE_UNIT; } } /* Check quantifier */ break; } } re_parse_quantifier (re_ctx_p); return re_check_quantifier (re_ctx_p); } /* re_parse_next_token */ /** * Append a character class range to the bytecode. */ static void re_class_add_range (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context */ lit_code_point_t start, /**< range begin */ lit_code_point_t end) /**< range end */ { if (re_ctx_p->flags & RE_FLAG_IGNORE_CASE) { start = ecma_regexp_canonicalize_char (start, re_ctx_p->flags & RE_FLAG_UNICODE); end = ecma_regexp_canonicalize_char (end, re_ctx_p->flags & RE_FLAG_UNICODE); } re_append_char (re_ctx_p, start); re_append_char (re_ctx_p, end); } /* re_class_add_range */ /** * Add a single character to the character class */ static void re_class_add_char (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context */ uint32_t class_offset, /**< character class bytecode offset*/ lit_code_point_t cp) /**< code point */ { if (re_ctx_p->flags & RE_FLAG_IGNORE_CASE) { cp = ecma_regexp_canonicalize_char (cp, re_ctx_p->flags & RE_FLAG_UNICODE); } re_insert_char (re_ctx_p, class_offset, cp); } /* re_class_add_char */ /** * Invalid character code point */ #define RE_INVALID_CP 0xFFFFFFFF /** * Read the input pattern and parse the range of character class * * @return empty ecma value - if parsed successfully * error ecma value - otherwise * * Returned value must be freed with ecma_free_value */ static ecma_value_t re_parse_char_class (re_compiler_ctx_t *re_ctx_p) /**< RegExp compiler context */ { static const uint8_t escape_flags[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20 }; const uint32_t class_offset = re_bytecode_size (re_ctx_p); uint8_t found_escape_flags = 0; uint8_t out_class_flags = 0; uint32_t range_count = 0; uint32_t char_count = 0; bool is_range = false; JERRY_ASSERT (re_ctx_p->input_curr_p < re_ctx_p->input_end_p); if (*re_ctx_p->input_curr_p == LIT_CHAR_CIRCUMFLEX) { re_ctx_p->input_curr_p++; out_class_flags |= RE_CLASS_INVERT; } lit_code_point_t start = RE_INVALID_CP; while (true) { if (re_ctx_p->input_curr_p >= re_ctx_p->input_end_p) { return ecma_raise_syntax_error (ECMA_ERR_UNTERMINATED_CHARACTER_CLASS); } if (*re_ctx_p->input_curr_p == LIT_CHAR_RIGHT_SQUARE) { if (is_range) { if (start != RE_INVALID_CP) { re_class_add_char (re_ctx_p, class_offset, start); char_count++; } re_class_add_char (re_ctx_p, class_offset, LIT_CHAR_MINUS); char_count++; } re_ctx_p->input_curr_p++; break; } JERRY_ASSERT (re_ctx_p->input_curr_p < re_ctx_p->input_end_p); lit_code_point_t current; if (*re_ctx_p->input_curr_p == LIT_CHAR_BACKSLASH) { re_ctx_p->input_curr_p++; if (re_ctx_p->input_curr_p >= re_ctx_p->input_end_p) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_ESCAPE); } if (*re_ctx_p->input_curr_p == LIT_CHAR_LOWERCASE_B) { re_ctx_p->input_curr_p++; current = LIT_CHAR_BS; } else if (*re_ctx_p->input_curr_p == LIT_CHAR_MINUS) { re_ctx_p->input_curr_p++; current = LIT_CHAR_MINUS; } else if ((re_ctx_p->flags & RE_FLAG_UNICODE) == 0 && *re_ctx_p->input_curr_p == LIT_CHAR_LOWERCASE_C && re_ctx_p->input_curr_p + 1 < re_ctx_p->input_end_p && (lit_char_is_decimal_digit (*(re_ctx_p->input_curr_p + 1)) || *(re_ctx_p->input_curr_p + 1) == LIT_CHAR_UNDERSCORE)) { current = ((uint8_t) * (re_ctx_p->input_curr_p + 1) % 32); re_ctx_p->input_curr_p += 2; } else { if (ECMA_IS_VALUE_ERROR (re_parse_char_escape (re_ctx_p))) { return ECMA_VALUE_ERROR; } if (re_ctx_p->token.type == RE_TOK_CLASS_ESCAPE) { const uint8_t escape = (uint8_t) re_ctx_p->token.value; found_escape_flags |= escape_flags[escape]; current = RE_INVALID_CP; } else { JERRY_ASSERT (re_ctx_p->token.type == RE_TOK_CHAR); current = re_ctx_p->token.value; } } } else if (re_ctx_p->flags & RE_FLAG_UNICODE) { current = ecma_regexp_unicode_advance (&re_ctx_p->input_curr_p, re_ctx_p->input_end_p); } else { current = lit_cesu8_read_next (&re_ctx_p->input_curr_p); } if (is_range) { is_range = false; if (start != RE_INVALID_CP && current != RE_INVALID_CP) { if (start > current) { return ecma_raise_syntax_error (ECMA_ERR_RANGE_OUT_OF_ORDER_IN_CHARACTER_CLASS); } re_class_add_range (re_ctx_p, start, current); range_count++; continue; } if (re_ctx_p->flags & RE_FLAG_UNICODE) { return ecma_raise_syntax_error (ECMA_ERR_INVALID_CHARACTER_CLASS); } if (start != RE_INVALID_CP) { re_class_add_char (re_ctx_p, class_offset, start); char_count++; } else if (current != RE_INVALID_CP) { re_class_add_char (re_ctx_p, class_offset, current); char_count++; } re_class_add_char (re_ctx_p, class_offset, LIT_CHAR_MINUS); char_count++; continue; } if (re_ctx_p->input_curr_p < re_ctx_p->input_end_p && *re_ctx_p->input_curr_p == LIT_CHAR_MINUS) { re_ctx_p->input_curr_p++; start = current; is_range = true; continue; } if (current != RE_INVALID_CP) { re_class_add_char (re_ctx_p, class_offset, current); char_count++; } } uint8_t escape_count = 0; for (ecma_class_escape_t escape = RE_ESCAPE__START; escape < RE_ESCAPE__COUNT; escape = (ecma_class_escape_t)((int) escape + 1)) { if (found_escape_flags & escape_flags[escape]) { re_insert_byte (re_ctx_p, class_offset, (uint8_t) escape); escape_count++; } } if (range_count > 0) { re_insert_value (re_ctx_p, class_offset, range_count); out_class_flags |= RE_CLASS_HAS_RANGES; } if (char_count > 0) { re_insert_value (re_ctx_p, class_offset, char_count); out_class_flags |= RE_CLASS_HAS_CHARS; } JERRY_ASSERT (escape_count <= RE_CLASS_ESCAPE_COUNT_MASK); out_class_flags |= escape_count; re_insert_byte (re_ctx_p, class_offset, out_class_flags); re_insert_opcode (re_ctx_p, class_offset, RE_OP_CHAR_CLASS); re_parse_quantifier (re_ctx_p); return re_check_quantifier (re_ctx_p); } /* re_parse_char_class */ /** * Parse alternatives * * @return empty ecma value - if alternative was successfully parsed * error ecma value - otherwise * * Returned value must be freed with ecma_free_value */ ecma_value_t re_parse_alternative (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context */ bool expect_eof) /**< expect end of file */ { ECMA_CHECK_STACK_USAGE (); uint32_t alternative_offset = re_bytecode_size (re_ctx_p); bool first_alternative = true; while (true) { ecma_value_t next_token_result = re_parse_next_token (re_ctx_p); if (ECMA_IS_VALUE_ERROR (next_token_result)) { return next_token_result; } JERRY_ASSERT (ecma_is_value_empty (next_token_result)); uint32_t atom_offset = re_bytecode_size (re_ctx_p); switch (re_ctx_p->token.type) { case RE_TOK_START_CAPTURE_GROUP: { const uint32_t idx = re_ctx_p->captures_count++; const uint32_t capture_start = idx; ecma_value_t result = re_parse_alternative (re_ctx_p, false); if (ECMA_IS_VALUE_ERROR (result)) { return result; } re_parse_quantifier (re_ctx_p); if (ECMA_IS_VALUE_ERROR (re_check_quantifier (re_ctx_p))) { return ECMA_VALUE_ERROR; } re_insert_into_group (re_ctx_p, atom_offset, idx, capture_start, true); break; } case RE_TOK_START_NON_CAPTURE_GROUP: { const uint32_t idx = re_ctx_p->non_captures_count++; const uint32_t capture_start = re_ctx_p->captures_count; ecma_value_t result = re_parse_alternative (re_ctx_p, false); if (ECMA_IS_VALUE_ERROR (result)) { return result; } re_parse_quantifier (re_ctx_p); if (ECMA_IS_VALUE_ERROR (re_check_quantifier (re_ctx_p))) { return ECMA_VALUE_ERROR; } re_insert_into_group (re_ctx_p, atom_offset, idx, capture_start, false); break; } case RE_TOK_PERIOD: { re_append_opcode (re_ctx_p, (re_ctx_p->flags & RE_FLAG_UNICODE) ? RE_OP_UNICODE_PERIOD : RE_OP_PERIOD); re_insert_atom_iterator (re_ctx_p, atom_offset); break; } case RE_TOK_ALTERNATIVE: { re_insert_value (re_ctx_p, alternative_offset, re_bytecode_size (re_ctx_p) - alternative_offset); re_insert_opcode (re_ctx_p, alternative_offset, first_alternative ? RE_OP_ALTERNATIVE_START : RE_OP_ALTERNATIVE_NEXT); alternative_offset = re_bytecode_size (re_ctx_p); first_alternative = false; break; } case RE_TOK_ASSERT_START: { re_append_opcode (re_ctx_p, RE_OP_ASSERT_LINE_START); break; } case RE_TOK_ASSERT_END: { re_append_opcode (re_ctx_p, RE_OP_ASSERT_LINE_END); break; } case RE_TOK_ASSERT_WORD_BOUNDARY: { re_append_opcode (re_ctx_p, RE_OP_ASSERT_WORD_BOUNDARY); break; } case RE_TOK_ASSERT_NOT_WORD_BOUNDARY: { re_append_opcode (re_ctx_p, RE_OP_ASSERT_NOT_WORD_BOUNDARY); break; } case RE_TOK_ASSERT_LOOKAHEAD: { const uint32_t start_capture_count = re_ctx_p->captures_count; const bool is_negative = !!re_ctx_p->token.value; ecma_value_t result = re_parse_alternative (re_ctx_p, false); if (ECMA_IS_VALUE_ERROR (result)) { return result; } if (re_ctx_p->flags & RE_FLAG_UNICODE) { re_ctx_p->token.qmin = 1; re_ctx_p->token.qmax = 1; re_ctx_p->token.greedy = true; } else { re_parse_quantifier (re_ctx_p); if (ECMA_IS_VALUE_ERROR (re_check_quantifier (re_ctx_p))) { return ECMA_VALUE_ERROR; } } re_insert_assertion_lookahead (re_ctx_p, atom_offset, start_capture_count, is_negative); break; } case RE_TOK_BACKREFERENCE: { const uint32_t backref_idx = re_ctx_p->token.value; re_append_opcode (re_ctx_p, RE_OP_BACKREFERENCE); re_append_value (re_ctx_p, backref_idx); if (re_ctx_p->token.qmin != 1 || re_ctx_p->token.qmax != 1) { const uint32_t group_idx = re_ctx_p->non_captures_count++; re_insert_into_group (re_ctx_p, atom_offset, group_idx, re_ctx_p->captures_count, false); } break; } case RE_TOK_CLASS_ESCAPE: { const ecma_class_escape_t escape = (ecma_class_escape_t) re_ctx_p->token.value; re_append_opcode (re_ctx_p, RE_OP_CLASS_ESCAPE); re_append_byte (re_ctx_p, (uint8_t) escape); re_insert_atom_iterator (re_ctx_p, atom_offset); break; } case RE_TOK_CHAR_CLASS: { ecma_value_t result = re_parse_char_class (re_ctx_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } re_insert_atom_iterator (re_ctx_p, atom_offset); break; } case RE_TOK_END_GROUP: { if (expect_eof) { return ecma_raise_syntax_error (ECMA_ERR_UNMATCHED_CLOSE_BRACKET); } if (!first_alternative) { re_insert_value (re_ctx_p, alternative_offset, re_bytecode_size (re_ctx_p) - alternative_offset); re_insert_opcode (re_ctx_p, alternative_offset, RE_OP_ALTERNATIVE_NEXT); } return ECMA_VALUE_EMPTY; } case RE_TOK_EOF: { if (!expect_eof) { return ecma_raise_syntax_error (ECMA_ERR_UNEXPECTED_END_OF_PATTERN); } if (!first_alternative) { re_insert_value (re_ctx_p, alternative_offset, re_bytecode_size (re_ctx_p) - alternative_offset); re_insert_opcode (re_ctx_p, alternative_offset, RE_OP_ALTERNATIVE_NEXT); } re_append_opcode (re_ctx_p, RE_OP_EOF); return ECMA_VALUE_EMPTY; } default: { JERRY_ASSERT (re_ctx_p->token.type == RE_TOK_CHAR); lit_code_point_t ch = re_ctx_p->token.value; if (ch <= LIT_UTF8_1_BYTE_CODE_POINT_MAX && (re_ctx_p->flags & RE_FLAG_IGNORE_CASE) == 0) { re_append_opcode (re_ctx_p, RE_OP_BYTE); re_append_byte (re_ctx_p, (uint8_t) ch); re_insert_atom_iterator (re_ctx_p, atom_offset); break; } if (re_ctx_p->flags & RE_FLAG_IGNORE_CASE) { ch = ecma_regexp_canonicalize_char (ch, re_ctx_p->flags & RE_FLAG_UNICODE); } re_append_opcode (re_ctx_p, RE_OP_CHAR); re_append_char (re_ctx_p, ch); re_insert_atom_iterator (re_ctx_p, atom_offset); break; } } } return ECMA_VALUE_EMPTY; } /* re_parse_alternative */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/savers/gif/meson.build000664 001750 001750 00000000341 15164251010 034214 0ustar00ddennedyddennedy000000 000000 source_file = [ 'tvgGifEncoder.h', 'tvgGifSaver.h', 'tvgGifEncoder.cpp', 'tvgGifSaver.cpp', ] subsaver_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-atomics.inc.h000664 001750 001750 00000003740 15164251010 050254 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Atomics built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ATOMICS /* ECMA-262 v11, 24.4.14 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_ATOMICS_U, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_ADD, ECMA_ATOMICS_ROUTINE_ADD, 3, 3) ROUTINE (LIT_MAGIC_STRING_ATOMICS_AND, ECMA_ATOMICS_ROUTINE_AND, 3, 3) ROUTINE (LIT_MAGIC_STRING_ATOMICS_COMPAREEXCHANGE, ECMA_ATOMICS_ROUTINE_COMPAREEXCHANGE, 4, 4) ROUTINE (LIT_MAGIC_STRING_ATOMICS_EXCHANGE, ECMA_ATOMICS_ROUTINE_EXCHANGE, 3, 3) ROUTINE (LIT_MAGIC_STRING_ATOMICS_ISLOCKFREE, ECMA_ATOMICS_ROUTINE_ISLOCKFREE, 1, 1) ROUTINE (LIT_MAGIC_STRING_ATOMICS_LOAD, ECMA_ATOMICS_ROUTINE_LOAD, 2, 2) ROUTINE (LIT_MAGIC_STRING_ATOMICS_OR, ECMA_ATOMICS_ROUTINE_OR, 3, 3) ROUTINE (LIT_MAGIC_STRING_ATOMICS_STORE, ECMA_ATOMICS_ROUTINE_STORE, 3, 3) ROUTINE (LIT_MAGIC_STRING_ATOMICS_SUB, ECMA_ATOMICS_ROUTINE_SUB, 3, 3) ROUTINE (LIT_MAGIC_STRING_ATOMICS_WAIT, ECMA_ATOMICS_ROUTINE_WAIT, 4, 4) ROUTINE (LIT_MAGIC_STRING_ATOMICS_NOTIFY, ECMA_ATOMICS_ROUTINE_NOTIFY, 3, 3) ROUTINE (LIT_MAGIC_STRING_ATOMICS_XOR, ECMA_ATOMICS_ROUTINE_XOR, 3, 3) #endif /* JERRY_BUILTIN_ATOMICS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-big-uint.h000664 001750 001750 00000012326 15164251010 045205 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BIG_UINT_H #define ECMA_BIG_UINT_H #include "ecma-globals.h" #if JERRY_BUILTIN_BIGINT /** * Limit of BigUInt memory allocation in JerryScript. */ #define ECMA_BIGINT_MAX_SIZE 0x10000 /** * Unsigned type which can hold two digits. */ typedef uint64_t ecma_bigint_two_digits_t; /** * Shift used by left/right shifting of a value. */ #define ECMA_BIGINT_DIGIT_SHIFT 5 /** * Return with the digits of a BigInt value. */ #define ECMA_BIGINT_GET_DIGITS(value_p, offset) \ ((ecma_bigint_digit_t *) (((uint8_t *) (value_p)) + sizeof (ecma_extended_primitive_t) + (offset))) /** * Return with the digits of a BigInt value. */ #define ECMA_BIGINT_GET_LAST_DIGIT(value_p, size) *ECMA_BIGINT_GET_DIGITS (value_p, size - sizeof (ecma_bigint_digit_t)) /** * Returns true if size is an odd number. */ #define ECMA_BIGINT_SIZE_IS_ODD(size) (((size) & sizeof (ecma_bigint_digit_t)) != 0) /** * Returns a two digit value where the high digit is set to the passed digit. */ #define ECMA_BIGINT_HIGH_DIGIT(digit) (((ecma_bigint_two_digits_t) digit) << (8 * sizeof (ecma_bigint_digit_t))) /** * Tells whether a number (usually a digit or uint32_t value) is an odd number. */ #define ECMA_BIGINT_NUMBER_IS_ODD(number) ((number & 0x1) != 0) /** * Bitwise operation types. */ typedef enum { ECMA_BIG_UINT_BITWISE_AND, /**< bitwise 'and' operation */ ECMA_BIG_UINT_BITWISE_OR, /**< bitwise 'or' operation */ ECMA_BIG_UINT_BITWISE_XOR, /**< bitwise 'xor' operation */ ECMA_BIG_UINT_BITWISE_AND_NOT, /**< bitwise 'and not' operation */ } ecma_big_uint_bitwise_operation_types_t; /** * Returns with the type of the operation. */ #define ECMA_BIGINT_BITWISE_GET_OPERATION_TYPE(operation_and_options) ((operation_and_options) &0xf) /** * Options for bitwise operations. */ typedef enum { ECMA_BIG_UINT_BITWISE_DECREASE_LEFT = (1 << 4), /**< subtract 1 from left value */ ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT = (1 << 5), /**< subtract 1 from right value */ ECMA_BIG_UINT_BITWISE_INCREASE_RESULT = (1 << 6), /**< add 1 to the result */ } ecma_big_uint_bitwise_options_t; /** * Subtract 1 from both left and right values. */ #define ECMA_BIG_UINT_BITWISE_DECREASE_BOTH (ECMA_BIG_UINT_BITWISE_DECREASE_LEFT | ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT) ecma_extended_primitive_t *ecma_bigint_create (uint32_t size); ecma_extended_primitive_t *ecma_big_uint_extend (ecma_extended_primitive_t *value_p, ecma_bigint_digit_t digit); ecma_bigint_digit_t ecma_big_uint_count_leading_zero (ecma_bigint_digit_t digit); int ecma_big_uint_compare (ecma_extended_primitive_t *left_value_p, ecma_extended_primitive_t *right_value_p); ecma_extended_primitive_t * ecma_big_uint_mul_digit (ecma_extended_primitive_t *value_p, ecma_bigint_digit_t mul, ecma_bigint_digit_t add); uint8_t *ecma_big_uint_to_string (ecma_extended_primitive_t *value_p, uint32_t radix, uint32_t *char_start_p, uint32_t *char_size_p); ecma_extended_primitive_t *ecma_big_uint_increase (ecma_extended_primitive_t *value_p); ecma_extended_primitive_t *ecma_big_uint_decrease (ecma_extended_primitive_t *value_p); ecma_extended_primitive_t *ecma_big_uint_add (ecma_extended_primitive_t *left_value_p, ecma_extended_primitive_t *right_value_p); ecma_extended_primitive_t *ecma_big_uint_sub (ecma_extended_primitive_t *left_value_p, ecma_extended_primitive_t *right_value_p); ecma_extended_primitive_t *ecma_big_uint_mul (ecma_extended_primitive_t *left_value_p, ecma_extended_primitive_t *right_value_p); ecma_extended_primitive_t *ecma_big_uint_div_mod (ecma_extended_primitive_t *dividend_value_p, ecma_extended_primitive_t *divisor_value_p, bool is_mod); ecma_extended_primitive_t *ecma_big_uint_shift_left (ecma_extended_primitive_t *left_value_p, uint32_t right_value); ecma_extended_primitive_t * ecma_big_uint_shift_right (ecma_extended_primitive_t *left_value_p, uint32_t right_value, bool increase_result); ecma_extended_primitive_t *ecma_big_uint_pow (ecma_extended_primitive_t *left_value_p, uint32_t right_value); ecma_extended_primitive_t *ecma_big_uint_bitwise_op (uint32_t operation_and_options, ecma_extended_primitive_t *left_value_p, ecma_extended_primitive_t *right_value_p); #endif /* JERRY_BUILTIN_BIGINT */ #endif /* ECMA_BIG_UINT_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-rangeerror.inc.h000664 001750 001750 00000002450 15164251010 050760 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * RangeError built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_RANGE_ERROR_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_RANGE_ERROR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-strings.cpp000664 001750 001750 00000070661 15164251010 043104 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "lit-strings.h" #include "jrt-libc-includes.h" #define LIT_UTF8_SURROGATE_MARKER 0xed /**< utf8 surrogate marker */ #define LIT_UTF8_HIGH_SURROGATE_MIN 0xa0 /**< utf8 high surrogate minimum */ #define LIT_UTF8_HIGH_SURROGATE_MAX 0xaf /**< utf8 high surrogate maximum */ #define LIT_UTF8_LOW_SURROGATE_MIN 0xb0 /**< utf8 low surrogate minimum */ #define LIT_UTF8_LOW_SURROGATE_MAX 0xbf /**< utf8 low surrogate maximum */ #define LIT_UTF8_1_BYTE_MAX 0xf4 /**< utf8 one byte max */ #define LIT_UTF8_2_BYTE_MAX 0x8f /**< utf8 two byte max */ #define LIT_UTF8_VALID_TWO_BYTE_START 0xc2 /**< utf8 two byte start */ /** * Validate utf-8 string * * NOTE: * Isolated surrogates are allowed. * * @return true if utf-8 string is well-formed * false otherwise */ bool lit_is_valid_utf8_string (const lit_utf8_byte_t *utf8_buf_p, /**< utf-8 string */ lit_utf8_size_t buf_size, /**< string size */ bool is_strict) /**< true if surrogate pairs are not allowed */ { const unsigned char *end = buf_size + utf8_buf_p; const unsigned char *idx = (const unsigned char *) utf8_buf_p; while (idx < end) { const uint8_t first_byte = *idx++; if (first_byte < LIT_UTF8_EXTRA_BYTE_MARKER) { continue; } if (first_byte < LIT_UTF8_VALID_TWO_BYTE_START || idx >= end) { return false; } const uint8_t second_byte = *idx++; if ((second_byte & LIT_UTF8_EXTRA_BYTE_MASK) != LIT_UTF8_EXTRA_BYTE_MARKER) { return false; } if (first_byte < LIT_UTF8_3_BYTE_MARKER) { continue; } if (idx >= end || (*idx++ & LIT_UTF8_EXTRA_BYTE_MASK) != LIT_UTF8_EXTRA_BYTE_MARKER) { return false; } if (first_byte < LIT_UTF8_4_BYTE_MARKER) { if (first_byte == LIT_UTF8_3_BYTE_MARKER && (second_byte & LIT_UTF8_2_BYTE_MASK) == LIT_UTF8_EXTRA_BYTE_MARKER) { return false; } if (is_strict && first_byte == LIT_UTF8_SURROGATE_MARKER && second_byte >= LIT_UTF8_HIGH_SURROGATE_MIN && second_byte <= LIT_UTF8_HIGH_SURROGATE_MAX && idx + 3 <= end && idx[0] == LIT_UTF8_SURROGATE_MARKER && idx[1] >= LIT_UTF8_LOW_SURROGATE_MIN && idx[1] <= LIT_UTF8_LOW_SURROGATE_MAX) { return false; } continue; } if (idx >= end || first_byte > LIT_UTF8_1_BYTE_MAX || (first_byte == LIT_UTF8_4_BYTE_MARKER && second_byte <= LIT_UTF8_EXTRA_BYTE_MARKER) || (first_byte == LIT_UTF8_1_BYTE_MAX && second_byte > LIT_UTF8_2_BYTE_MAX) || (*idx++ & LIT_UTF8_EXTRA_BYTE_MASK) != LIT_UTF8_EXTRA_BYTE_MARKER) { return false; } } return true; } /* lit_is_valid_utf8_string */ /** * Validate cesu-8 string * * @return true if cesu-8 string is well-formed * false otherwise */ bool lit_is_valid_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, /**< cesu-8 string */ lit_utf8_size_t buf_size) /**< string size */ { lit_utf8_size_t idx = 0; while (idx < buf_size) { lit_utf8_byte_t c = cesu8_buf_p[idx++]; if ((c & LIT_UTF8_1_BYTE_MASK) == LIT_UTF8_1_BYTE_MARKER) { continue; } lit_code_point_t code_point = 0; lit_code_point_t min_code_point = 0; lit_utf8_size_t extra_bytes_count; if ((c & LIT_UTF8_2_BYTE_MASK) == LIT_UTF8_2_BYTE_MARKER) { extra_bytes_count = 1; min_code_point = LIT_UTF8_2_BYTE_CODE_POINT_MIN; code_point = ((uint32_t) (c & LIT_UTF8_LAST_5_BITS_MASK)); } else if ((c & LIT_UTF8_3_BYTE_MASK) == LIT_UTF8_3_BYTE_MARKER) { extra_bytes_count = 2; min_code_point = LIT_UTF8_3_BYTE_CODE_POINT_MIN; code_point = ((uint32_t) (c & LIT_UTF8_LAST_4_BITS_MASK)); } else { return false; } if (idx + extra_bytes_count > buf_size) { /* cesu-8 string breaks in the middle */ return false; } for (lit_utf8_size_t offset = 0; offset < extra_bytes_count; ++offset) { c = cesu8_buf_p[idx + offset]; if ((c & LIT_UTF8_EXTRA_BYTE_MASK) != LIT_UTF8_EXTRA_BYTE_MARKER) { /* invalid continuation byte */ return false; } code_point <<= LIT_UTF8_BITS_IN_EXTRA_BYTES; code_point |= (c & LIT_UTF8_LAST_6_BITS_MASK); } if (code_point < min_code_point) { /* cesu-8 string doesn't encode valid unicode code point */ return false; } idx += extra_bytes_count; } return true; } /* lit_is_valid_cesu8_string */ /** * Check if the code point is UTF-16 low surrogate * * @return true / false */ bool lit_is_code_point_utf16_low_surrogate (lit_code_point_t code_point) /**< code point */ { return LIT_UTF16_LOW_SURROGATE_MIN <= code_point && code_point <= LIT_UTF16_LOW_SURROGATE_MAX; } /* lit_is_code_point_utf16_low_surrogate */ /** * Check if the code point is UTF-16 high surrogate * * @return true / false */ bool lit_is_code_point_utf16_high_surrogate (lit_code_point_t code_point) /**< code point */ { return LIT_UTF16_HIGH_SURROGATE_MIN <= code_point && code_point <= LIT_UTF16_HIGH_SURROGATE_MAX; } /* lit_is_code_point_utf16_high_surrogate */ /** * Represents code point (>0xFFFF) as surrogate pair and returns its lower part * * @return lower code_unit of the surrogate pair */ static ecma_char_t convert_code_point_to_low_surrogate (lit_code_point_t code_point) /**< code point, should be > 0xFFFF */ { JERRY_ASSERT (code_point > LIT_UTF16_CODE_UNIT_MAX); ecma_char_t code_unit_bits; code_unit_bits = (ecma_char_t) (code_point & LIT_UTF16_LAST_10_BITS_MASK); return (ecma_char_t) (LIT_UTF16_LOW_SURROGATE_MARKER | code_unit_bits); } /* convert_code_point_to_low_surrogate */ /** * Represents code point (>0xFFFF) as surrogate pair and returns its higher part * * @return higher code_unit of the surrogate pair */ static ecma_char_t convert_code_point_to_high_surrogate (lit_code_point_t code_point) /**< code point, should be > 0xFFFF */ { JERRY_ASSERT (code_point > LIT_UTF16_CODE_UNIT_MAX); JERRY_ASSERT (code_point <= LIT_UNICODE_CODE_POINT_MAX); ecma_char_t code_unit_bits; code_unit_bits = (ecma_char_t) ((code_point - LIT_UTF16_FIRST_SURROGATE_CODE_POINT) >> LIT_UTF16_BITS_IN_SURROGATE); return (LIT_UTF16_HIGH_SURROGATE_MARKER | code_unit_bits); } /* convert_code_point_to_high_surrogate */ /** * UTF16 Encoding method for a code point * * See also: * ECMA-262 v6, 10.1.1 * * @return uint8_t, the number of returning code points */ uint8_t lit_utf16_encode_code_point (lit_code_point_t cp, /**< the code point we encode */ ecma_char_t *cu_p) /**< result of the encoding */ { if (cp <= LIT_UTF16_CODE_UNIT_MAX) { cu_p[0] = (ecma_char_t) cp; return 1; } cu_p[0] = convert_code_point_to_high_surrogate (cp); cu_p[1] = convert_code_point_to_low_surrogate (cp); return 2; } /* lit_utf16_encode_code_point */ /** * Calculate size of a zero-terminated utf-8 string * * NOTE: * - string cannot be NULL * - string should not contain zero characters in the middle * * @return size of a string */ lit_utf8_size_t lit_zt_utf8_string_size (const lit_utf8_byte_t *utf8_str_p) /**< zero-terminated utf-8 string */ { JERRY_ASSERT (utf8_str_p != NULL); return (lit_utf8_size_t) strlen ((const char *) utf8_str_p); } /* lit_zt_utf8_string_size */ /** * Calculate length of a cesu-8 encoded string * * @return UTF-16 code units count */ lit_utf8_size_t lit_utf8_string_length (const lit_utf8_byte_t *utf8_buf_p, /**< utf-8 string */ lit_utf8_size_t utf8_buf_size) /**< string size */ { lit_utf8_size_t length = 0; lit_utf8_size_t size = 0; while (size < utf8_buf_size) { size += lit_get_unicode_char_size_by_utf8_first_byte (*(utf8_buf_p + size)); length++; } JERRY_ASSERT (size == utf8_buf_size); return length; } /* lit_utf8_string_length */ /** * Calculate the required size of an utf-8 encoded string from cesu-8 encoded string * * @return size of an utf-8 encoded string */ lit_utf8_size_t lit_get_utf8_size_of_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, /**< cesu-8 string */ lit_utf8_size_t cesu8_buf_size) /**< string size */ { lit_utf8_size_t offset = 0; lit_utf8_size_t utf8_buf_size = cesu8_buf_size; ecma_char_t prev_ch = 0; while (offset < cesu8_buf_size) { ecma_char_t ch; offset += lit_read_code_unit_from_cesu8 (cesu8_buf_p + offset, &ch); if (lit_is_code_point_utf16_low_surrogate (ch) && lit_is_code_point_utf16_high_surrogate (prev_ch)) { utf8_buf_size -= 2; } prev_ch = ch; } JERRY_ASSERT (offset == cesu8_buf_size); return utf8_buf_size; } /* lit_get_utf8_size_of_cesu8_string */ /** * Calculate length of an utf-8 encoded string from cesu-8 encoded string * * @return length of an utf-8 encoded string */ lit_utf8_size_t lit_get_utf8_length_of_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, /**< cesu-8 string */ lit_utf8_size_t cesu8_buf_size) /**< string size */ { lit_utf8_size_t offset = 0; lit_utf8_size_t utf8_length = 0; ecma_char_t prev_ch = 0; while (offset < cesu8_buf_size) { ecma_char_t ch; offset += lit_read_code_unit_from_cesu8 (cesu8_buf_p + offset, &ch); if (!lit_is_code_point_utf16_low_surrogate (ch) || !lit_is_code_point_utf16_high_surrogate (prev_ch)) { utf8_length++; } prev_ch = ch; } JERRY_ASSERT (offset == cesu8_buf_size); return utf8_length; } /* lit_get_utf8_length_of_cesu8_string */ /** * Decodes a unicode code point from non-empty utf-8-encoded buffer * * @return number of bytes occupied by code point in the string */ lit_utf8_size_t lit_read_code_point_from_utf8 (const lit_utf8_byte_t *buf_p, /**< buffer with characters */ lit_utf8_size_t buf_size, /**< size of the buffer in bytes */ lit_code_point_t *code_point) /**< [out] code point */ { JERRY_ASSERT (buf_p && buf_size); lit_utf8_byte_t c = buf_p[0]; if ((c & LIT_UTF8_1_BYTE_MASK) == LIT_UTF8_1_BYTE_MARKER) { *code_point = (lit_code_point_t) (c & LIT_UTF8_LAST_7_BITS_MASK); return 1; } lit_code_point_t ret = LIT_UNICODE_CODE_POINT_NULL; lit_utf8_size_t bytes_count = 0; if ((c & LIT_UTF8_2_BYTE_MASK) == LIT_UTF8_2_BYTE_MARKER) { bytes_count = 2; ret = ((lit_code_point_t) (c & LIT_UTF8_LAST_5_BITS_MASK)); } else if ((c & LIT_UTF8_3_BYTE_MASK) == LIT_UTF8_3_BYTE_MARKER) { bytes_count = 3; ret = ((lit_code_point_t) (c & LIT_UTF8_LAST_4_BITS_MASK)); } else { JERRY_ASSERT ((c & LIT_UTF8_4_BYTE_MASK) == LIT_UTF8_4_BYTE_MARKER); bytes_count = 4; ret = ((lit_code_point_t) (c & LIT_UTF8_LAST_3_BITS_MASK)); } JERRY_ASSERT (buf_size >= bytes_count); for (uint32_t i = 1; i < bytes_count; ++i) { ret <<= LIT_UTF8_BITS_IN_EXTRA_BYTES; ret |= (buf_p[i] & LIT_UTF8_LAST_6_BITS_MASK); } *code_point = ret; return bytes_count; } /* lit_read_code_point_from_utf8 */ /** * Decodes a unicode code unit from non-empty cesu-8-encoded buffer * * @return number of bytes occupied by code point in the string */ lit_utf8_size_t lit_read_code_unit_from_cesu8 (const lit_utf8_byte_t *buf_p, /**< buffer with characters */ ecma_char_t *code_unit) /**< [out] code unit */ { JERRY_ASSERT (buf_p); lit_utf8_byte_t c = buf_p[0]; if ((c & LIT_UTF8_1_BYTE_MASK) == LIT_UTF8_1_BYTE_MARKER) { *code_unit = (ecma_char_t) (c & LIT_UTF8_LAST_7_BITS_MASK); return 1; } lit_code_point_t ret = LIT_UNICODE_CODE_POINT_NULL; lit_utf8_size_t bytes_count; if ((c & LIT_UTF8_2_BYTE_MASK) == LIT_UTF8_2_BYTE_MARKER) { bytes_count = 2; ret = ((lit_code_point_t) (c & LIT_UTF8_LAST_5_BITS_MASK)); } else { JERRY_ASSERT ((c & LIT_UTF8_3_BYTE_MASK) == LIT_UTF8_3_BYTE_MARKER); bytes_count = 3; ret = ((lit_code_point_t) (c & LIT_UTF8_LAST_4_BITS_MASK)); } for (uint32_t i = 1; i < bytes_count; ++i) { ret <<= LIT_UTF8_BITS_IN_EXTRA_BYTES; ret |= (buf_p[i] & LIT_UTF8_LAST_6_BITS_MASK); } JERRY_ASSERT (ret <= LIT_UTF16_CODE_UNIT_MAX); *code_unit = (ecma_char_t) ret; return bytes_count; } /* lit_read_code_unit_from_cesu8 */ /** * Decodes a unicode code point from non-empty cesu-8-encoded buffer * * @return number of bytes occupied by code point in the string */ lit_utf8_size_t lit_read_code_point_from_cesu8 (const lit_utf8_byte_t *buf_p, /**< buffer with characters */ const lit_utf8_byte_t *buf_end_p, /**< buffer end */ lit_code_point_t *code_point) /**< [out] code point */ { ecma_char_t code_unit; lit_utf8_size_t size = lit_read_code_unit_from_cesu8 (buf_p, &code_unit); JERRY_ASSERT (buf_p + size <= buf_end_p); if (lit_is_code_point_utf16_high_surrogate (code_unit)) { buf_p += size; if (buf_p < buf_end_p) { ecma_char_t next_code_unit; lit_utf8_size_t next_size = lit_read_code_unit_from_cesu8 (buf_p, &next_code_unit); if (lit_is_code_point_utf16_low_surrogate (next_code_unit)) { JERRY_ASSERT (buf_p + next_size <= buf_end_p); *code_point = lit_convert_surrogate_pair_to_code_point (code_unit, next_code_unit); return size + next_size; } } } *code_point = code_unit; return size; } /* lit_read_code_point_from_cesu8 */ /** * Decodes a unicode code unit from non-empty cesu-8-encoded buffer * * @return number of bytes occupied by code point in the string */ lit_utf8_size_t lit_read_prev_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, /**< buffer with characters */ ecma_char_t *code_point) /**< [out] code point */ { JERRY_ASSERT (buf_p); lit_utf8_decr (&buf_p); return lit_read_code_unit_from_cesu8 (buf_p, code_point); } /* lit_read_prev_code_unit_from_utf8 */ /** * Decodes a unicode code unit from non-empty cesu-8-encoded buffer * * @return next code unit */ ecma_char_t lit_cesu8_read_next (const lit_utf8_byte_t **buf_p) /**< [in,out] buffer with characters */ { JERRY_ASSERT (*buf_p); ecma_char_t ch; *buf_p += lit_read_code_unit_from_cesu8 (*buf_p, &ch); return ch; } /* lit_cesu8_read_next */ /** * Decodes a unicode code unit from non-empty cesu-8-encoded buffer * * @return previous code unit */ ecma_char_t lit_cesu8_read_prev (const lit_utf8_byte_t **buf_p) /**< [in,out] buffer with characters */ { JERRY_ASSERT (*buf_p); ecma_char_t ch; lit_utf8_decr (buf_p); lit_read_code_unit_from_cesu8 (*buf_p, &ch); return ch; } /* lit_cesu8_read_prev */ /** * Decodes a unicode code unit from non-empty cesu-8-encoded buffer * * @return next code unit */ ecma_char_t JERRY_ATTR_NOINLINE lit_cesu8_peek_next (const lit_utf8_byte_t *buf_p) /**< [in,out] buffer with characters */ { JERRY_ASSERT (buf_p != NULL); ecma_char_t ch; lit_read_code_unit_from_cesu8 (buf_p, &ch); return ch; } /* lit_cesu8_peek_next */ /** * Decodes a unicode code unit from non-empty cesu-8-encoded buffer * * @return previous code unit */ ecma_char_t JERRY_ATTR_NOINLINE lit_cesu8_peek_prev (const lit_utf8_byte_t *buf_p) /**< [in,out] buffer with characters */ { JERRY_ASSERT (buf_p != NULL); ecma_char_t ch; lit_read_prev_code_unit_from_utf8 (buf_p, &ch); return ch; } /* lit_cesu8_peek_prev */ /** * Increase cesu-8 encoded string pointer by one code unit. */ void lit_utf8_incr (const lit_utf8_byte_t **buf_p) /**< [in,out] buffer with characters */ { JERRY_ASSERT (*buf_p); *buf_p += lit_get_unicode_char_size_by_utf8_first_byte (**buf_p); } /* lit_utf8_incr */ /** * Decrease cesu-8 encoded string pointer by one code unit. */ void lit_utf8_decr (const lit_utf8_byte_t **buf_p) /**< [in,out] buffer with characters */ { JERRY_ASSERT (*buf_p); const lit_utf8_byte_t *current_p = *buf_p; do { current_p--; } while ((*current_p & LIT_UTF8_EXTRA_BYTE_MASK) == LIT_UTF8_EXTRA_BYTE_MARKER); *buf_p = current_p; } /* lit_utf8_decr */ /** * Calc hash using the specified hash_basis. * * NOTE: * This is implementation of FNV-1a hash function, which is released into public domain. * Constants used, are carefully picked primes by the authors. * More info: http://www.isthe.com/chongo/tech/comp/fnv/ * * @return ecma-string's hash */ lit_string_hash_t lit_utf8_string_hash_combine (lit_string_hash_t hash_basis, /**< hash to be combined with */ const lit_utf8_byte_t *utf8_buf_p, /**< characters buffer */ lit_utf8_size_t utf8_buf_size) /**< number of characters in the buffer */ { JERRY_ASSERT (utf8_buf_p != NULL || utf8_buf_size == 0); uint32_t hash = hash_basis; for (uint32_t i = 0; i < utf8_buf_size; i++) { /* 16777619 is 32 bit FNV_prime = 2^24 + 2^8 + 0x93 = 16777619 */ hash = (hash ^ utf8_buf_p[i]) * 16777619; } return (lit_string_hash_t) hash; } /* lit_utf8_string_hash_combine */ /** * Calculate hash from the buffer. * * @return ecma-string's hash */ lit_string_hash_t lit_utf8_string_calc_hash (const lit_utf8_byte_t *utf8_buf_p, /**< characters buffer */ lit_utf8_size_t utf8_buf_size) /**< number of characters in the buffer */ { JERRY_ASSERT (utf8_buf_p != NULL || utf8_buf_size == 0); /* 32 bit offset_basis for FNV = 2166136261 */ return lit_utf8_string_hash_combine ((lit_string_hash_t) 2166136261, utf8_buf_p, utf8_buf_size); } /* lit_utf8_string_calc_hash */ /** * Return code unit at the specified position in string * * NOTE: * code_unit_offset should be less than string's length * * @return code unit value */ ecma_char_t lit_utf8_string_code_unit_at (const lit_utf8_byte_t *utf8_buf_p, /**< utf-8 string */ lit_utf8_size_t utf8_buf_size, /**< string size in bytes */ lit_utf8_size_t code_unit_offset) /**< offset of a code_unit */ { lit_utf8_byte_t *current_p = (lit_utf8_byte_t *) utf8_buf_p; ecma_char_t code_unit; do { JERRY_ASSERT (current_p < utf8_buf_p + utf8_buf_size); current_p += lit_read_code_unit_from_cesu8 (current_p, &code_unit); } while (code_unit_offset--); return code_unit; } /* lit_utf8_string_code_unit_at */ /** * Get CESU-8 encoded size of character * * @return number of bytes occupied in CESU-8 */ lit_utf8_size_t lit_get_unicode_char_size_by_utf8_first_byte (const lit_utf8_byte_t first_byte) /**< buffer with characters */ { if ((first_byte & LIT_UTF8_1_BYTE_MASK) == LIT_UTF8_1_BYTE_MARKER) { return 1; } else if ((first_byte & LIT_UTF8_2_BYTE_MASK) == LIT_UTF8_2_BYTE_MARKER) { return 2; } else { JERRY_ASSERT ((first_byte & LIT_UTF8_3_BYTE_MASK) == LIT_UTF8_3_BYTE_MARKER); return 3; } } /* lit_get_unicode_char_size_by_utf8_first_byte */ /** * Convert code unit to cesu-8 representation * * @return byte count required to represent the code unit */ lit_utf8_size_t lit_code_unit_to_utf8 (ecma_char_t code_unit, /**< code unit */ lit_utf8_byte_t *buf_p) /**< buffer where to store the result and its size * should be at least LIT_UTF8_MAX_BYTES_IN_CODE_UNIT */ { if (code_unit <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { buf_p[0] = (lit_utf8_byte_t) code_unit; return 1; } else if (code_unit <= LIT_UTF8_2_BYTE_CODE_POINT_MAX) { uint32_t code_unit_bits = code_unit; lit_utf8_byte_t second_byte_bits = (lit_utf8_byte_t) (code_unit_bits & LIT_UTF8_LAST_6_BITS_MASK); code_unit_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t first_byte_bits = (lit_utf8_byte_t) (code_unit_bits & LIT_UTF8_LAST_5_BITS_MASK); JERRY_ASSERT (first_byte_bits == code_unit_bits); buf_p[0] = LIT_UTF8_2_BYTE_MARKER | first_byte_bits; buf_p[1] = LIT_UTF8_EXTRA_BYTE_MARKER | second_byte_bits; return 2; } else { uint32_t code_unit_bits = code_unit; lit_utf8_byte_t third_byte_bits = (lit_utf8_byte_t) (code_unit_bits & LIT_UTF8_LAST_6_BITS_MASK); code_unit_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t second_byte_bits = (lit_utf8_byte_t) (code_unit_bits & LIT_UTF8_LAST_6_BITS_MASK); code_unit_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t first_byte_bits = (lit_utf8_byte_t) (code_unit_bits & LIT_UTF8_LAST_4_BITS_MASK); JERRY_ASSERT (first_byte_bits == code_unit_bits); buf_p[0] = LIT_UTF8_3_BYTE_MARKER | first_byte_bits; buf_p[1] = LIT_UTF8_EXTRA_BYTE_MARKER | second_byte_bits; buf_p[2] = LIT_UTF8_EXTRA_BYTE_MARKER | third_byte_bits; return 3; } } /* lit_code_unit_to_utf8 */ /** * Convert code point to cesu-8 representation * * @return byte count required to represent the code point */ lit_utf8_size_t lit_code_point_to_cesu8 (lit_code_point_t code_point, /**< code point */ lit_utf8_byte_t *buf) /**< buffer where to store the result, * its size should be at least 6 bytes */ { if (code_point <= LIT_UTF16_CODE_UNIT_MAX) { return lit_code_unit_to_utf8 ((ecma_char_t) code_point, buf); } else { lit_utf8_size_t offset = lit_code_unit_to_utf8 (convert_code_point_to_high_surrogate (code_point), buf); offset += lit_code_unit_to_utf8 (convert_code_point_to_low_surrogate (code_point), buf + offset); return offset; } } /* lit_code_point_to_cesu8 */ /** * Convert code point to utf-8 representation * * @return byte count required to represent the code point */ lit_utf8_size_t lit_code_point_to_utf8 (lit_code_point_t code_point, /**< code point */ lit_utf8_byte_t *buf) /**< buffer where to store the result, * its size should be at least 4 bytes */ { if (code_point <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { buf[0] = (lit_utf8_byte_t) code_point; return 1; } else if (code_point <= LIT_UTF8_2_BYTE_CODE_POINT_MAX) { uint32_t code_point_bits = code_point; lit_utf8_byte_t second_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_6_BITS_MASK); code_point_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t first_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_5_BITS_MASK); JERRY_ASSERT (first_byte_bits == code_point_bits); buf[0] = LIT_UTF8_2_BYTE_MARKER | first_byte_bits; buf[1] = LIT_UTF8_EXTRA_BYTE_MARKER | second_byte_bits; return 2; } else if (code_point <= LIT_UTF8_3_BYTE_CODE_POINT_MAX) { uint32_t code_point_bits = code_point; lit_utf8_byte_t third_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_6_BITS_MASK); code_point_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t second_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_6_BITS_MASK); code_point_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t first_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_4_BITS_MASK); JERRY_ASSERT (first_byte_bits == code_point_bits); buf[0] = LIT_UTF8_3_BYTE_MARKER | first_byte_bits; buf[1] = LIT_UTF8_EXTRA_BYTE_MARKER | second_byte_bits; buf[2] = LIT_UTF8_EXTRA_BYTE_MARKER | third_byte_bits; return 3; } else { JERRY_ASSERT (code_point <= LIT_UTF8_4_BYTE_CODE_POINT_MAX); uint32_t code_point_bits = code_point; lit_utf8_byte_t fourth_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_6_BITS_MASK); code_point_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t third_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_6_BITS_MASK); code_point_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t second_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_6_BITS_MASK); code_point_bits >>= LIT_UTF8_BITS_IN_EXTRA_BYTES; lit_utf8_byte_t first_byte_bits = (lit_utf8_byte_t) (code_point_bits & LIT_UTF8_LAST_3_BITS_MASK); JERRY_ASSERT (first_byte_bits == code_point_bits); buf[0] = LIT_UTF8_4_BYTE_MARKER | first_byte_bits; buf[1] = LIT_UTF8_EXTRA_BYTE_MARKER | second_byte_bits; buf[2] = LIT_UTF8_EXTRA_BYTE_MARKER | third_byte_bits; buf[3] = LIT_UTF8_EXTRA_BYTE_MARKER | fourth_byte_bits; return 4; } } /* lit_code_point_to_utf8 */ /** * Convert cesu-8 string to an utf-8 string and put it into the buffer. * String will be truncated to fit the buffer. * * @return number of bytes copied to the buffer. */ lit_utf8_size_t lit_convert_cesu8_string_to_utf8_string (const lit_utf8_byte_t *cesu8_string_p, /**< cesu-8 string */ lit_utf8_size_t cesu8_size, /**< size of cesu-8 string */ lit_utf8_byte_t *utf8_string_p, /**< destination utf-8 buffer pointer * (can be NULL if buffer_size == 0) */ lit_utf8_size_t utf8_size) /**< size of utf-8 buffer */ { const lit_utf8_byte_t *cesu8_cursor_p = cesu8_string_p; const lit_utf8_byte_t *cesu8_end_p = cesu8_string_p + cesu8_size; lit_utf8_byte_t *utf8_cursor_p = utf8_string_p; lit_utf8_byte_t *utf8_end_p = utf8_string_p + utf8_size; while (cesu8_cursor_p < cesu8_end_p) { lit_code_point_t cp; lit_utf8_size_t read_size = lit_read_code_point_from_cesu8 (cesu8_cursor_p, cesu8_end_p, &cp); lit_utf8_size_t encoded_size = (cp >= LIT_UTF16_FIRST_SURROGATE_CODE_POINT) ? 4 : read_size; if (utf8_cursor_p + encoded_size > utf8_end_p) { break; } if (cp >= LIT_UTF16_FIRST_SURROGATE_CODE_POINT) { lit_code_point_to_utf8 (cp, utf8_cursor_p); } else { memcpy (utf8_cursor_p, cesu8_cursor_p, encoded_size); } utf8_cursor_p += encoded_size; cesu8_cursor_p += read_size; } JERRY_ASSERT (cesu8_cursor_p <= cesu8_end_p); JERRY_ASSERT (utf8_cursor_p <= utf8_end_p); return (lit_utf8_byte_t) (utf8_cursor_p - utf8_string_p); } /* lit_convert_cesu8_string_to_utf8_string */ /** * Convert surrogate pair to code point * * @return code point */ lit_code_point_t lit_convert_surrogate_pair_to_code_point (ecma_char_t high_surrogate, /**< high surrogate code point */ ecma_char_t low_surrogate) /**< low surrogate code point */ { JERRY_ASSERT (lit_is_code_point_utf16_high_surrogate (high_surrogate)); JERRY_ASSERT (lit_is_code_point_utf16_low_surrogate (low_surrogate)); lit_code_point_t code_point; code_point = (uint16_t) (high_surrogate - LIT_UTF16_HIGH_SURROGATE_MIN); code_point <<= LIT_UTF16_BITS_IN_SURROGATE; code_point += LIT_UTF16_FIRST_SURROGATE_CODE_POINT; code_point |= (uint16_t) (low_surrogate - LIT_UTF16_LOW_SURROGATE_MIN); return code_point; } /* lit_convert_surrogate_pair_to_code_point */ /** * Relational compare of cesu-8 strings * * First string is less than second string if: * - strings are not equal; * - first string is prefix of second or is lexicographically less than second. * * @return true - if first string is less than second string, * false - otherwise */ bool lit_compare_utf8_strings_relational (const lit_utf8_byte_t *string1_p, /**< utf-8 string */ lit_utf8_size_t string1_size, /**< string size */ const lit_utf8_byte_t *string2_p, /**< utf-8 string */ lit_utf8_size_t string2_size) /**< string size */ { lit_utf8_byte_t *string1_pos = (lit_utf8_byte_t *) string1_p; lit_utf8_byte_t *string2_pos = (lit_utf8_byte_t *) string2_p; const lit_utf8_byte_t *string1_end_p = string1_p + string1_size; const lit_utf8_byte_t *string2_end_p = string2_p + string2_size; while (string1_pos < string1_end_p && string2_pos < string2_end_p) { ecma_char_t ch1, ch2; string1_pos += lit_read_code_unit_from_cesu8 (string1_pos, &ch1); string2_pos += lit_read_code_unit_from_cesu8 (string2_pos, &ch2); if (ch1 < ch2) { return true; } else if (ch1 > ch2) { return false; } } return (string1_pos >= string1_end_p && string2_pos < string2_end_p); } /* lit_compare_utf8_strings_relational */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/000775 001750 001750 00000000000 15164251010 033160 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/savers/000775 001750 001750 00000000000 15164251010 031307 5ustar00ddennedyddennedy000000 000000 glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/api/jerryscript.cpp000664 001750 001750 00000044145 15164251010 043164 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "jerryscript.h" #include #include "ecma-eval.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-init-finalize.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "jcontext.h" /** \addtogroup jerry Jerry engine interface * @{ */ /** * Turn on API availability * * @return void */ static inline void jerry_api_enable (void) { #ifndef JERRY_NDEBUG JERRY_CONTEXT (status_flags) |= ECMA_STATUS_API_ENABLED; #endif /* JERRY_NDEBUG */ } /* jerry_make_api_available */ /** * Turn off API availability * * @return void */ static inline void jerry_api_disable (void) { #ifndef JERRY_NDEBUG JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_API_ENABLED; #endif /* JERRY_NDEBUG */ } /* jerry_make_api_unavailable */ /** * Create an API compatible return value. * * @return return value for Jerry API functions */ static jerry_value_t jerry_return (const jerry_value_t value) /**< return value */ { if (ECMA_IS_VALUE_ERROR (value)) { return ecma_create_exception_from_context (); } return value; } /* jerry_return */ /** * Jerry engine initialization */ void jerry_init (jerry_init_flag_t flags) /**< combination of Jerry flags */ { #if JERRY_EXTERNAL_CONTEXT size_t total_size = jerry_port_context_alloc (sizeof (jerry_context_t)); JERRY_UNUSED (total_size); #endif /* JERRY_EXTERNAL_CONTEXT */ jerry_context_t *context_p = &JERRY_CONTEXT_STRUCT; memset (context_p, 0, sizeof (jerry_context_t)); #if JERRY_EXTERNAL_CONTEXT && !JERRY_SYSTEM_ALLOCATOR uint32_t heap_start_offset = JERRY_ALIGNUP (sizeof (jerry_context_t), JMEM_ALIGNMENT); uint8_t *heap_p = ((uint8_t *) context_p) + heap_start_offset; uint32_t heap_size = JERRY_ALIGNDOWN (total_size - heap_start_offset, JMEM_ALIGNMENT); JERRY_ASSERT (heap_p + heap_size <= ((uint8_t *) context_p) + total_size); context_p->heap_p = (jmem_heap_t *) heap_p; context_p->heap_size = heap_size; #endif /* JERRY_EXTERNAL_CONTEXT && !JERRY_SYSTEM_ALLOCATOR */ JERRY_CONTEXT (jerry_init_flags) = flags; jerry_api_enable (); jmem_init (); ecma_init (); } /* jerry_init */ /** * Terminate Jerry engine */ void jerry_cleanup (void) { for (jerry_context_data_header_t *this_p = JERRY_CONTEXT (context_data_p); this_p != NULL; this_p = this_p->next_p) { if (this_p->manager_p->deinit_cb) { void *data = (this_p->manager_p->bytes_needed > 0) ? JERRY_CONTEXT_DATA_HEADER_USER_DATA (this_p) : NULL; this_p->manager_p->deinit_cb (data); } } ecma_free_all_enqueued_jobs (); ecma_finalize (); jerry_api_disable (); for (jerry_context_data_header_t *this_p = JERRY_CONTEXT (context_data_p), *next_p = NULL; this_p != NULL; this_p = next_p) { next_p = this_p->next_p; if (this_p->manager_p->finalize_cb) { void *data = (this_p->manager_p->bytes_needed > 0) ? JERRY_CONTEXT_DATA_HEADER_USER_DATA (this_p) : NULL; this_p->manager_p->finalize_cb (data); } jmem_heap_free_block (this_p, sizeof (jerry_context_data_header_t) + this_p->manager_p->bytes_needed); } jmem_finalize (); #if JERRY_EXTERNAL_CONTEXT jerry_port_context_free (); #endif /* JERRY_EXTERNAL_CONTEXT */ } /* jerry_cleanup */ /** * Perform eval * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return result of eval, may be error value. */ jerry_value_t jerry_eval (const jerry_char_t *source_p, /**< source code */ size_t source_size, /**< length of source code */ uint32_t flags) /**< jerry_parse_opts_t flags */ { parser_source_char_t source_char; source_char.source_p = source_p; source_char.source_size = source_size; return jerry_return (ecma_op_eval_chars_buffer ((void *) &source_char, flags)); } /* jerry_eval */ /** * Get global object * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return api value of global object */ jerry_value_t jerry_current_realm (void) { ecma_object_t *global_obj_p = ecma_builtin_get_global (); ecma_ref_object (global_obj_p); return ecma_make_object_value (global_obj_p); } /* jerry_current_realm */ /** * Check if the specified value is an error or abort value. * * @return true - if the specified value is an error value, * false - otherwise */ bool jerry_value_is_exception (const jerry_value_t value) /**< api value */ { return ecma_is_value_exception (value); } /* jerry_value_is_exception */ /** * Check if the specified value is number. * * @return true - if the specified value is number, * false - otherwise */ bool jerry_value_is_number (const jerry_value_t value) /**< api value */ { return ecma_is_value_number (value); } /* jerry_value_is_number */ /** * Check if the specified value is object. * * @return true - if the specified value is object, * false - otherwise */ bool jerry_value_is_object (const jerry_value_t value) /**< api value */ { return ecma_is_value_object (value); } /* jerry_value_is_object */ /** * Check if the specified value is string. * * @return true - if the specified value is string, * false - otherwise */ bool jerry_value_is_string (const jerry_value_t value) /**< api value */ { return ecma_is_value_string (value); } /* jerry_value_is_string */ /** * Check if the specified value is undefined. * * @return true - if the specified value is undefined, * false - otherwise */ bool jerry_value_is_undefined (const jerry_value_t value) /**< api value */ { return ecma_is_value_undefined (value); } /* jerry_value_is_undefined */ /** * Get number from the specified value as a double. * * @return stored number as double */ float jerry_value_as_number (const jerry_value_t value) /**< api value */ { return (float) ecma_get_number_from_value (value); } /* jerry_value_as_number */ /** * Call ToObject operation on the api value. * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return converted object value - if success * thrown error - otherwise */ jerry_value_t jerry_value_to_object (const jerry_value_t value) /**< input value */ { return jerry_return (ecma_op_to_object (value)); } /* jerry_value_to_object */ /** * Call the ToString ecma builtin operation on the api value. * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return converted string value - if success * thrown error - otherwise */ jerry_value_t jerry_value_to_string (const jerry_value_t value) /**< input value */ { ecma_string_t *str_p = ecma_op_to_string (value); return ecma_make_string_value (str_p); } /* jerry_value_to_string */ /** * Convert any number to int32 number. * * Note: * For non-number values 0 is returned. * * @return int32 representation of the number. */ int32_t jerry_value_as_int32 (const jerry_value_t value) /**< input value */ { return ecma_number_to_int32 (ecma_get_number_from_value (value)); } /* jerry_value_as_int32 */ /** * Convert any number to uint32 number. * * Note: * For non-number values 0 is returned. * * @return uint32 representation of the number. */ uint32_t jerry_value_as_uint32 (const jerry_value_t value) /**< input value */ { return ecma_number_to_uint32 (ecma_get_number_from_value (value)); } /* jerry_value_as_uint32 */ /** * Release ownership of the argument value */ void jerry_value_free (jerry_value_t value) /**< value */ { ecma_free_value (value); } /* jerry_value_free */ /** * Create a jerry_value_t representing a boolean value from the given boolean parameter. * * @return value of the created boolean */ jerry_value_t jerry_boolean (bool value) /**< bool value from which a jerry_value_t will be created */ { return ecma_make_boolean_value (value); } /* jerry_boolean */ /** * Create an external function object * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return value of the constructed function object */ jerry_value_t jerry_function_external (jerry_external_handler_t handler) /**< native handler * for the function */ { ecma_object_t *func_obj_p = ecma_op_create_external_function_object (handler); return ecma_make_object_value (func_obj_p); } /* jerry_function_external */ /** * Creates a jerry_value_t representing a number value. * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return jerry_value_t created from the given double argument. */ jerry_value_t jerry_number (float value) /**< double value from which a jerry_value_t will be created */ { return ecma_make_number_value ((ecma_number_t) value); } /* jerry_number */ /** * Creates a jerry_value_t representing an undefined value. * * @return value of undefined */ jerry_value_t jerry_undefined (void) { return ECMA_VALUE_UNDEFINED; } /* jerry_undefined */ /** * Create new JavaScript object, like with new Object(). * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return value of the created object */ jerry_value_t jerry_object (void) { return ecma_make_object_value (ecma_op_create_object_object_noarg ()); } /* jerry_object */ /** * Create string value from the input zero-terminated ASCII string. * * @return created string */ jerry_value_t jerry_string_sz (const char *str_p) /**< pointer to string */ { const jerry_char_t *data_p = (const jerry_char_t *) str_p; return jerry_string (data_p, lit_zt_utf8_string_size (data_p), JERRY_ENCODING_CESU8); } /* jerry_string_sz */ /** * Create a string value from the input buffer using the specified encoding. * The content of the buffer is assumed to be valid in the specified encoding, it's the callers responsibility to * validate the input. * * See also: jerry_validate_string * * @return created string */ jerry_value_t jerry_string (const jerry_char_t *buffer_p, /**< pointer to buffer */ jerry_size_t buffer_size, /**< buffer size */ jerry_encoding_t encoding) /**< buffer encoding */ { ecma_string_t *ecma_str_p = NULL; //JERRY_ASSERT (jerry_validate_string (buffer_p, buffer_size, encoding)); switch (encoding) { case JERRY_ENCODING_CESU8: { ecma_str_p = ecma_new_ecma_string_from_utf8 (buffer_p, buffer_size); break; } case JERRY_ENCODING_UTF8: { ecma_str_p = ecma_new_ecma_string_from_utf8_converted_to_cesu8 (buffer_p, buffer_size); break; } default: { return jerry_undefined(); } } return ecma_make_string_value (ecma_str_p); } /* jerry_string */ /** * Get length of a string value * * @return number of characters in the string * 0 - if value is not a string */ jerry_length_t jerry_string_length (const jerry_value_t value) /**< input string */ { return ecma_string_get_length (ecma_get_string_from_value (value)); } /* jerry_string_length */ /** * Copy the characters of a string into the specified buffer using the specified encoding. The string is truncated to * fit the buffer. If the value is not a string, nothing will be copied to the buffer. * * @return number of bytes copied to the buffer */ jerry_size_t jerry_string_to_buffer (const jerry_value_t value, /**< input string value */ jerry_encoding_t encoding, /**< output encoding */ jerry_char_t *buffer_p, /**< [out] output characters buffer */ jerry_size_t buffer_size) /**< size of output buffer */ { ecma_string_t *str_p = ecma_get_string_from_value (value); return ecma_string_copy_to_buffer (str_p, (lit_utf8_byte_t *) buffer_p, buffer_size, encoding); } /* jerry_string_to_char_buffer */ /** * Get value of a property to the specified object with the given name. * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return value of the property - if success * value marked with error flag - otherwise */ jerry_value_t jerry_object_get (const jerry_value_t object, /**< object value */ const jerry_value_t key) /**< property name (string value) */ { jerry_value_t ret_value = ecma_op_object_get (ecma_get_object_from_value (object), ecma_get_prop_name_from_value (key)); return jerry_return (ret_value); } /* jerry_object_get */ /** * Get value of a property to the specified object with the given name. * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return value of the property - if success * value marked with error flag - otherwise */ jerry_value_t jerry_object_get_sz (const jerry_value_t object, /**< object value */ const char *key_p) /**< property key */ { jerry_value_t key_str = jerry_string_sz (key_p); jerry_value_t result = jerry_object_get (object, key_str); ecma_free_value (key_str); return result; } /* jerry_object_get */ /** * Get value by an index from the specified object. * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return value of the property specified by the index - if success * value marked with error flag - otherwise */ jerry_value_t jerry_object_get_index (const jerry_value_t object, /**< object value */ uint32_t index) /**< index to be written */ { ecma_value_t ret_value = ecma_op_object_get_by_index (ecma_get_object_from_value (object), index); return jerry_return (ret_value); } /* jerry_object_get_index */ /** * Set a property to the specified object with the given name. * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return true value - if the operation was successful * value marked with error flag - otherwise */ jerry_value_t jerry_object_set (jerry_value_t object, /**< object value */ const jerry_value_t key, /**< property name (string value) */ const jerry_value_t value) /**< value to set */ { return jerry_return (ecma_op_object_put (ecma_get_object_from_value (object), ecma_get_prop_name_from_value (key), value, true)); } /* jerry_object_set */ /** * Set a property to the specified object with the given name. * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return true value - if the operation was successful * value marked with error flag - otherwise */ jerry_value_t jerry_object_set_sz (jerry_value_t object, /**< object value */ const char *key_p, /**< property key */ const jerry_value_t value) /**< value to set */ { jerry_value_t key_str = jerry_string_sz (key_p); jerry_value_t result = jerry_object_set (object, key_str, value); ecma_free_value (key_str); return result; } /* jerry_object_set */ /** * Set indexed value in the specified object * * Note: * returned value must be freed with jerry_value_free, when it is no longer needed. * * @return true value - if the operation was successful * value marked with error flag - otherwise */ jerry_value_t jerry_object_set_index (jerry_value_t object, /**< object value */ uint32_t index, /**< index to be written */ const jerry_value_t value) /**< value to set */ { ecma_value_t ret_value = ecma_op_object_put_by_index (ecma_get_object_from_value (object), index, value, true); return jerry_return (ret_value); } /* jerry_object_set_index */ /** * Get native pointer and its type information, associated with the given native type info. * * Note: * If native pointer is present, its type information is returned in out_native_pointer_p * * @return found native pointer, * or NULL */ void * jerry_object_get_native_ptr (const jerry_value_t object, /**< object to get native pointer from */ const jerry_object_native_info_t *native_info_p) /**< the type info * of the native pointer */ { if (!ecma_is_value_object (object)) { return NULL; } ecma_object_t *obj_p = ecma_get_object_from_value (object); ecma_native_pointer_t *native_pointer_p = ecma_get_native_pointer_value (obj_p, (jerry_object_native_info_t *) native_info_p); if (native_pointer_p == NULL) { return NULL; } return native_pointer_p->native_p; } /* jerry_object_get_native_ptr */ /** * Set native pointer and an optional type info for the specified object. * * * Note: * If native pointer was already set for the object, its value is updated. * * Note: * If a non-NULL free callback is specified in the native type info, * it will be called by the garbage collector when the object is freed. * Referred values by this method must have at least 1 reference. (Correct API usage satisfies this condition) * The type info always overwrites the previous value, so passing * a NULL value deletes the current type info. */ void jerry_object_set_native_ptr (jerry_value_t object, /**< object to set native pointer in */ const jerry_object_native_info_t *native_info_p, /**< object's native type info */ void *native_pointer_p) /**< native pointer */ { if (ecma_is_value_object (object)) { ecma_object_t *object_p = ecma_get_object_from_value (object); ecma_create_native_pointer_property (object_p, native_pointer_p, native_info_p); } } /* jerry_object_set_native_ptr */ /** * @} */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/000775 001750 001750 00000000000 15164251010 042407 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatelottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int32array-prototype.cpp000664 001750 001750 00000002222 15164251010 054617 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-int32array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID int32array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup int32arrayprototype ECMA Int32Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-evalerror-prototype.inc.h000664 001750 001750 00000002460 15164251010 052657 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * EvalError.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.7.8 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_EVAL_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.9 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_EVAL_ERROR_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.10 */ STRING_VALUE (LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-module.cpp000664 001750 001750 00000130142 15164251010 044033 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-module.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" #include "ecma-objects.h" #include "jcontext.h" #include "lit-char-helpers.h" #include "vm.h" #if JERRY_MODULE_SYSTEM /** * Type of the result returned by ecma_module_resolve_export. */ typedef enum { ECMA_MODULE_RESOLVE_NOT_FOUND, /**< reference not found */ ECMA_MODULE_RESOLVE_CIRCULAR, /**< only circular references are found */ ECMA_MODULE_RESOLVE_ERROR, /**< module in error state is encountered */ ECMA_MODULE_RESOLVE_AMBIGUOUS, /**< reference is ambiguous */ ECMA_MODULE_RESOLVE_FOUND, /**< reference found */ } ecma_module_resolve_result_type_t; /** * A record that stores the result of ecma_module_resolve_export. */ typedef struct { ecma_module_resolve_result_type_t result_type; /**< result type */ ecma_value_t result; /**< result value */ } ecma_module_resolve_result_t; /** * This flag is set in the result if the value is a namespace object. */ #define ECMA_MODULE_NAMESPACE_RESULT_FLAG 0x2 /** * Initialize context variables for the root module. * * @return new module */ ecma_module_t * ecma_module_create (void) { JERRY_ASSERT (JERRY_CONTEXT (module_current_p) == NULL); ecma_object_t *obj_p = ecma_create_object (NULL, sizeof (ecma_module_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_MODULE; ext_object_p->u.cls.u1.module_state = JERRY_MODULE_STATE_UNLINKED; ext_object_p->u.cls.u2.module_flags = 0; ecma_module_t *module_p = (ecma_module_t *) obj_p; module_p->scope_p = NULL; module_p->namespace_object_p = NULL; module_p->imports_p = NULL; module_p->local_exports_p = NULL; module_p->indirect_exports_p = NULL; module_p->star_exports_p = NULL; module_p->u.compiled_code_p = NULL; return module_p; } /* ecma_module_create */ /** * Cleanup context variables for the root module. */ void ecma_module_cleanup_context (void) { ecma_deref_object ((ecma_object_t *) JERRY_CONTEXT (module_current_p)); #ifndef JERRY_NDEBUG JERRY_CONTEXT (module_current_p) = NULL; #endif /* JERRY_NDEBUG */ } /* ecma_module_cleanup_context */ /** * Sets module state to error. */ static void ecma_module_set_error_state (ecma_module_t *module_p) /**< module */ { module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_ERROR; if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL && !jcontext_has_pending_abort ()) { jerry_value_t exception = jcontext_take_exception (); JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_ERROR, ecma_make_object_value (&module_p->header.object), exception, JERRY_CONTEXT (module_state_changed_callback_user_p)); jcontext_raise_exception (exception); } } /* ecma_module_set_error_state */ /** * Gets the internal module pointer of a module * * @return module pointer */ static inline ecma_module_t * ecma_module_get_from_object (ecma_value_t module_val) /**< module */ { JERRY_ASSERT (ecma_is_value_object (module_val)); ecma_object_t *object_p = ecma_get_object_from_value (module_val); JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_MODULE)); return (ecma_module_t *) object_p; } /* ecma_module_get_from_object */ /** * Cleans up a list of module names. */ void ecma_module_release_module_names (ecma_module_names_t *module_name_p) /**< first module name */ { while (module_name_p != NULL) { ecma_module_names_t *next_p = module_name_p->next_p; ecma_deref_ecma_string (module_name_p->imex_name_p); ecma_deref_ecma_string (module_name_p->local_name_p); jmem_heap_free_block (module_name_p, sizeof (ecma_module_names_t)); module_name_p = next_p; } } /* ecma_module_release_module_names */ /** * Cleans up a list of module nodes. */ static void ecma_module_release_module_nodes (ecma_module_node_t *module_node_p, /**< first module node */ bool is_import) /**< free path variable */ { while (module_node_p != NULL) { ecma_module_node_t *next_p = module_node_p->next_p; ecma_module_release_module_names (module_node_p->module_names_p); if (is_import && ecma_is_value_string (module_node_p->u.path_or_module)) { ecma_deref_ecma_string (ecma_get_string_from_value (module_node_p->u.path_or_module)); } jmem_heap_free_block (module_node_p, sizeof (ecma_module_node_t)); module_node_p = next_p; } } /* ecma_module_release_module_nodes */ /** * Creates a new resolve set item from a {module, export_name} pair. * * @return new resolve set item */ static ecma_module_resolve_set_t * ecma_module_resolve_set_create (ecma_module_t *const module_p, /**< module */ ecma_string_t *const export_name_p) /**< export name */ { ecma_module_resolve_set_t *new_p; new_p = (ecma_module_resolve_set_t *) jmem_heap_alloc_block (sizeof (ecma_module_resolve_set_t)); new_p->next_p = NULL; new_p->module_p = module_p; new_p->name_p = export_name_p; return new_p; } /* ecma_module_resolve_set_create */ /** * Appends a {module, export_name} record into a resolve set. * * @return true - if the record is appended successfully * false - otherwise */ static bool ecma_module_resolve_set_append (ecma_module_resolve_set_t *set_p, /**< resolve set */ ecma_module_t *const module_p, /**< module */ ecma_string_t *const export_name_p) /**< export name */ { JERRY_ASSERT (set_p != NULL); ecma_module_resolve_set_t *current_p = set_p; while (true) { if (current_p->module_p == module_p && ecma_compare_ecma_strings (current_p->name_p, export_name_p)) { return false; } ecma_module_resolve_set_t *next_p = current_p->next_p; if (next_p == NULL) { current_p->next_p = ecma_module_resolve_set_create (module_p, export_name_p); return true; } current_p = next_p; } } /* ecma_module_resolve_set_append */ /** * Cleans up contents of a resolve set. */ static void ecma_module_resolve_set_cleanup (ecma_module_resolve_set_t *set_p) /**< resolve set */ { while (set_p != NULL) { ecma_module_resolve_set_t *next_p = set_p->next_p; jmem_heap_free_block (set_p, sizeof (ecma_module_resolve_set_t)); set_p = next_p; } } /* ecma_module_resolve_set_cleanup */ /** * Throws the appropriate error based on the resolve result * * @return error value */ static ecma_value_t ecma_module_resolve_throw (ecma_module_resolve_result_t *resolve_result_p, /**< resolve result */ ecma_string_t *name_p) /**< referenced value */ { #if JERRY_ERROR_MESSAGES ecma_value_t name_val = ecma_make_string_value (name_p); const char *msg_p; switch (resolve_result_p->result_type) { case ECMA_MODULE_RESOLVE_CIRCULAR: { msg_p = "Detected cycle while resolving name '%' (module)"; break; } case ECMA_MODULE_RESOLVE_AMBIGUOUS: { msg_p = "Name '%' is ambiguous (module)"; break; } default: { JERRY_ASSERT (resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND || resolve_result_p->result_type == ECMA_MODULE_RESOLVE_ERROR); msg_p = "Name '%' is not found (module)"; break; } } return ecma_raise_standard_error_with_format (JERRY_ERROR_SYNTAX, msg_p, name_val); #else /* JERRY_ERROR_MESSAGES */ JERRY_UNUSED (resolve_result_p); JERRY_UNUSED (name_p); return ecma_raise_syntax_error (ECMA_ERR_EMPTY); #endif /* !JERRY_ERROR_MESSAGES */ } /* ecma_module_resolve_throw */ /** * Updates the resolve record with the passed type/value pair * * @return true - if the record is updated successfully * false - otherwise */ static bool ecma_module_resolve_update (ecma_module_resolve_result_t *resolve_result_p, /**< [in,out] resolve result */ ecma_value_t result) /**< result value */ { JERRY_ASSERT (resolve_result_p->result_type != ECMA_MODULE_RESOLVE_AMBIGUOUS && resolve_result_p->result_type != ECMA_MODULE_RESOLVE_ERROR); if (resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND || resolve_result_p->result_type == ECMA_MODULE_RESOLVE_CIRCULAR) { resolve_result_p->result_type = ECMA_MODULE_RESOLVE_FOUND; resolve_result_p->result = result; return true; } JERRY_ASSERT (resolve_result_p->result_type == ECMA_MODULE_RESOLVE_FOUND); if (resolve_result_p->result == result) { return true; } resolve_result_p->result_type = ECMA_MODULE_RESOLVE_AMBIGUOUS; return false; } /* ecma_module_resolve_update */ /** * Finds the reference in the imported bindings. * * Note: * This function is needed because the namespace object is created before the imports are connected * * @return true - if the record is updated successfully * false - otherwise */ static bool ecma_module_resolve_import (ecma_module_resolve_result_t *resolve_result_p, /**< [in,out] resolve result */ ecma_module_resolve_set_t *resolve_set_p, /**< resolve set */ ecma_module_t *module_p, /**< base module */ ecma_string_t *local_name_p) /**< local name */ { ecma_module_node_t *import_node_p = module_p->imports_p; while (true) { JERRY_ASSERT (import_node_p != NULL); for (ecma_module_names_t *import_names_p = import_node_p->module_names_p; import_names_p != NULL; import_names_p = import_names_p->next_p) { if (ecma_compare_ecma_strings (local_name_p, import_names_p->local_name_p)) { ecma_module_t *imported_module_p = ecma_module_get_from_object (import_node_p->u.path_or_module); if (ecma_compare_ecma_string_to_magic_id (import_names_p->imex_name_p, LIT_MAGIC_STRING_ASTERISK_CHAR)) { /* Namespace import. */ ecma_value_t ns = ecma_make_object_value (imported_module_p->namespace_object_p); JERRY_ASSERT (ns & ECMA_MODULE_NAMESPACE_RESULT_FLAG); return ecma_module_resolve_update (resolve_result_p, ns); } if (!ecma_module_resolve_set_append (resolve_set_p, imported_module_p, import_names_p->imex_name_p) && resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND) { resolve_result_p->result_type = ECMA_MODULE_RESOLVE_CIRCULAR; } return true; } } import_node_p = import_node_p->next_p; } } /* ecma_module_resolve_import */ /** * Resolves which module satisfies an export based from a specific module in the import tree. * * Note: See ES11 15.2.1.17.3 */ static void ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */ ecma_string_t *const export_name_p, /**< export name */ ecma_module_resolve_result_t *resolve_result_p) /**< [out] resolve result */ { ecma_module_resolve_set_t *resolve_set_p = ecma_module_resolve_set_create (module_p, export_name_p); ecma_module_resolve_set_t *current_set_p = resolve_set_p; ecma_module_node_t *star_export_p; resolve_result_p->result_type = ECMA_MODULE_RESOLVE_NOT_FOUND; resolve_result_p->result = ECMA_VALUE_UNDEFINED; do { ecma_module_t *current_module_p = current_set_p->module_p; ecma_string_t *current_export_name_p = current_set_p->name_p; if (current_module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_ERROR) { resolve_result_p->result_type = ECMA_MODULE_RESOLVE_ERROR; goto exit; } if (current_module_p->header.u.cls.u2.module_flags & ECMA_MODULE_HAS_NAMESPACE) { ecma_property_t *property_p = ecma_find_named_property (current_module_p->namespace_object_p, current_export_name_p); if (property_p != NULL) { ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); JERRY_ASSERT ( (!(*property_p & ECMA_PROPERTY_FLAG_DATA) && !(property_value_p->value & ECMA_MODULE_NAMESPACE_RESULT_FLAG)) || ((*property_p & ECMA_PROPERTY_FLAG_DATA) && ecma_is_value_object (property_value_p->value) && ecma_object_class_is (ecma_get_object_from_value (property_value_p->value), ECMA_OBJECT_CLASS_MODULE_NAMESPACE))); if (!ecma_module_resolve_update (resolve_result_p, property_value_p->value)) { goto exit; } goto next_iteration; } } else { /* 6. */ ecma_module_names_t *export_names_p = current_module_p->local_exports_p; while (export_names_p != NULL) { if (ecma_compare_ecma_strings (current_export_name_p, export_names_p->imex_name_p)) { ecma_property_t *property_p = ecma_find_named_property (current_module_p->scope_p, export_names_p->local_name_p); if (property_p != NULL) { ecma_value_t reference = ecma_property_to_reference (property_p); JERRY_ASSERT (!(reference & ECMA_MODULE_NAMESPACE_RESULT_FLAG)); if (!ecma_module_resolve_update (resolve_result_p, reference)) { goto exit; } } else if (!ecma_module_resolve_import (resolve_result_p, resolve_set_p, current_module_p, export_names_p->local_name_p)) { goto exit; } goto next_iteration; } export_names_p = export_names_p->next_p; } /* 7. */ ecma_module_node_t *indirect_export_p = current_module_p->indirect_exports_p; while (indirect_export_p != NULL) { export_names_p = indirect_export_p->module_names_p; while (export_names_p != NULL) { if (ecma_compare_ecma_strings (current_export_name_p, export_names_p->imex_name_p)) { ecma_module_t *target_module_p = ecma_module_get_from_object (*indirect_export_p->u.module_object_p); if (ecma_compare_ecma_string_to_magic_id (export_names_p->local_name_p, LIT_MAGIC_STRING_ASTERISK_CHAR)) { /* Namespace export. */ ecma_value_t ns = ecma_make_object_value (target_module_p->namespace_object_p); JERRY_ASSERT (ns & ECMA_MODULE_NAMESPACE_RESULT_FLAG); if (!ecma_module_resolve_update (resolve_result_p, ns)) { goto exit; } } else if (!ecma_module_resolve_set_append (resolve_set_p, target_module_p, export_names_p->local_name_p) && resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND) { resolve_result_p->result_type = ECMA_MODULE_RESOLVE_CIRCULAR; } goto next_iteration; } export_names_p = export_names_p->next_p; } indirect_export_p = indirect_export_p->next_p; } } /* 8. */ if (ecma_compare_ecma_string_to_magic_id (current_export_name_p, LIT_MAGIC_STRING_DEFAULT)) { goto exit; } /* 10. */ star_export_p = current_module_p->star_exports_p; while (star_export_p != NULL) { JERRY_ASSERT (star_export_p->module_names_p == NULL); ecma_module_t *target_module_p = ecma_module_get_from_object (*star_export_p->u.module_object_p); if (!ecma_module_resolve_set_append (resolve_set_p, target_module_p, current_export_name_p) && resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND) { resolve_result_p->result_type = ECMA_MODULE_RESOLVE_CIRCULAR; } star_export_p = star_export_p->next_p; } next_iteration: current_set_p = current_set_p->next_p; } while (current_set_p != NULL); exit: ecma_module_resolve_set_cleanup (resolve_set_p); } /* ecma_module_resolve_export */ /** * Evaluates an EcmaScript module. * * @return ECMA_VALUE_ERROR - if an error occurred * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t ecma_module_evaluate (ecma_module_t *module_p) /**< module */ { if (module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_ERROR) { return ecma_raise_range_error (ECMA_ERR_MODULE_IS_IN_ERROR_STATE); } if (module_p->header.u.cls.u1.module_state >= JERRY_MODULE_STATE_EVALUATING) { return ECMA_VALUE_EMPTY; } JERRY_ASSERT (module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKED); JERRY_ASSERT (module_p->scope_p != NULL); module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_EVALUATING; ecma_value_t ret_value; if (module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE) { ret_value = ECMA_VALUE_UNDEFINED; if (module_p->u.callback) { ret_value = module_p->u.callback (ecma_make_object_value (&module_p->header.object)); if (JERRY_UNLIKELY (ecma_is_value_exception (ret_value))) { ecma_throw_exception (ret_value); ret_value = ECMA_VALUE_ERROR; } } } else { ret_value = vm_run_module (module_p); } if (JERRY_LIKELY (!ECMA_IS_VALUE_ERROR (ret_value))) { module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_EVALUATED; if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL) { JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_EVALUATED, ecma_make_object_value (&module_p->header.object), ret_value, JERRY_CONTEXT (module_state_changed_callback_user_p)); } } else { ecma_module_set_error_state (module_p); } if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE)) { ecma_bytecode_deref (module_p->u.compiled_code_p); } module_p->u.compiled_code_p = NULL; return ret_value; } /* ecma_module_evaluate */ /** * Resolves an export and adds it to the modules namespace object, if the export name is not yet handled. * Note: See 15.2.1.16.2 and 15.2.1.18 * * @return ECMA_VALUE_ERROR - if an error occurred * ECMA_VALUE_EMPTY - otherwise */ static ecma_value_t ecma_module_namespace_object_add_export_if_needed (ecma_collection_t *properties_p, /**< collection of properties */ ecma_module_t *module_p, /**< module */ ecma_string_t *export_name_p, /**< export name */ bool allow_default) /**< allow default export */ { if (!allow_default) { if (ecma_compare_ecma_string_to_magic_id (export_name_p, LIT_MAGIC_STRING_DEFAULT)) { return ECMA_VALUE_EMPTY; } /* No need to check duplications before star exports are processed. */ ecma_value_t *buffer_p = properties_p->buffer_p; ecma_value_t *buffer_end_p = properties_p->buffer_p + properties_p->item_count; while (buffer_p < buffer_end_p) { if (ecma_compare_ecma_strings (ecma_get_string_from_value (*buffer_p), export_name_p)) { return ECMA_VALUE_EMPTY; } buffer_p += 2; } } ecma_module_resolve_result_t resolve_result; ecma_module_resolve_export (module_p, export_name_p, &resolve_result); if (resolve_result.result_type == ECMA_MODULE_RESOLVE_AMBIGUOUS) { return ECMA_VALUE_EMPTY; } if (resolve_result.result_type != ECMA_MODULE_RESOLVE_FOUND) { return ecma_module_resolve_throw (&resolve_result, export_name_p); } ecma_collection_push_back (properties_p, ecma_make_string_value (export_name_p)); ecma_collection_push_back (properties_p, resolve_result.result); return ECMA_VALUE_EMPTY; } /* ecma_module_namespace_object_add_export_if_needed */ /** * Helper routine for heapsort algorithm. */ static void ecma_module_heap_sort_shift_down (ecma_value_t *buffer_p, /**< array of items */ uint32_t item_count, /**< number of items */ uint32_t item_index) /**< index of updated item */ { while (true) { uint32_t highest_index = item_index; uint32_t current_index = (item_index << 1) + 2; if (current_index >= item_count) { return; } if (ecma_compare_ecma_strings_relational (ecma_get_string_from_value (buffer_p[highest_index]), ecma_get_string_from_value (buffer_p[current_index]))) { highest_index = current_index; } current_index += 2; if (current_index < item_count && ecma_compare_ecma_strings_relational (ecma_get_string_from_value (buffer_p[highest_index]), ecma_get_string_from_value (buffer_p[current_index]))) { highest_index = current_index; } if (highest_index == item_index) { return; } ecma_value_t tmp = buffer_p[highest_index]; buffer_p[highest_index] = buffer_p[item_index]; buffer_p[item_index] = tmp; tmp = buffer_p[highest_index + 1]; buffer_p[highest_index + 1] = buffer_p[item_index + 1]; buffer_p[item_index + 1] = tmp; item_index = highest_index; } } /* ecma_module_heap_sort_shift_down */ /** * Creates a namespace object for a module. * Note: See 15.2.1.18 * * @return ECMA_VALUE_ERROR - if an error occurred * ECMA_VALUE_EMPTY - otherwise */ static ecma_value_t ecma_module_create_namespace_object (ecma_module_t *module_p) /**< module */ { JERRY_ASSERT (module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING); JERRY_ASSERT (module_p->namespace_object_p != NULL); JERRY_ASSERT (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_HAS_NAMESPACE)); ecma_module_resolve_set_t *resolve_set_p; resolve_set_p = ecma_module_resolve_set_create (module_p, ecma_get_magic_string (LIT_MAGIC_STRING_ASTERISK_CHAR)); /* The properties collection stores name / result item pairs. Name is always * a string, and result can be a property reference or namespace object. */ ecma_module_resolve_set_t *current_set_p = resolve_set_p; ecma_collection_t *properties_p = ecma_new_collection (); ecma_value_t result = ECMA_VALUE_EMPTY; bool allow_default = true; ecma_value_t *buffer_p; uint32_t item_count; ecma_value_t *buffer_end_p; do { ecma_module_t *current_module_p = current_set_p->module_p; if (current_module_p->header.u.cls.u2.module_flags & ECMA_MODULE_HAS_NAMESPACE) { JERRY_ASSERT (!allow_default); jmem_cpointer_t prop_iter_cp = current_module_p->namespace_object_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prop_iter_cp = prop_iter_p->next_property_cp; } } #endif /* JERRY_PROPERTY_HASHMAP */ while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if (!ECMA_PROPERTY_IS_RAW (prop_iter_p->types[i])) { continue; } ecma_string_t *name_p = ecma_string_from_property_name (prop_iter_p->types[i], prop_pair_p->names_cp[i]); result = ecma_module_namespace_object_add_export_if_needed (properties_p, module_p, name_p, false); ecma_deref_ecma_string (name_p); if (ECMA_IS_VALUE_ERROR (result)) { goto exit; } } prop_iter_cp = prop_iter_p->next_property_cp; } } else { ecma_module_names_t *export_names_p = current_module_p->local_exports_p; if (export_names_p != NULL) { do { result = ecma_module_namespace_object_add_export_if_needed (properties_p, module_p, export_names_p->imex_name_p, allow_default); if (ECMA_IS_VALUE_ERROR (result)) { goto exit; } export_names_p = export_names_p->next_p; } while (export_names_p != NULL); } ecma_module_node_t *indirect_export_p = current_module_p->indirect_exports_p; while (indirect_export_p != NULL) { export_names_p = indirect_export_p->module_names_p; while (export_names_p != NULL) { result = ecma_module_namespace_object_add_export_if_needed (properties_p, module_p, export_names_p->imex_name_p, allow_default); if (ECMA_IS_VALUE_ERROR (result)) { goto exit; } export_names_p = export_names_p->next_p; } indirect_export_p = indirect_export_p->next_p; } } allow_default = false; ecma_module_node_t *star_export_p = current_module_p->star_exports_p; while (star_export_p != NULL) { JERRY_ASSERT (star_export_p->module_names_p == NULL); /* Circular imports are ignored */ ecma_module_resolve_set_append (resolve_set_p, ecma_module_get_from_object (*star_export_p->u.module_object_p), ecma_get_magic_string (LIT_MAGIC_STRING_ASTERISK_CHAR)); star_export_p = star_export_p->next_p; } current_set_p = current_set_p->next_p; } while (current_set_p != NULL); buffer_p = properties_p->buffer_p; item_count = properties_p->item_count; buffer_end_p = properties_p->buffer_p + item_count; if (item_count >= 4) { /* Sort items with heapsort if at least two items are stored in the buffer. */ uint32_t end = (item_count >> 1) & ~(uint32_t) 0x1; do { end -= 2; ecma_module_heap_sort_shift_down (buffer_p, item_count, end); } while (end > 0); end = item_count - 2; do { ecma_value_t tmp = buffer_p[end]; buffer_p[end] = buffer_p[0]; buffer_p[0] = tmp; tmp = buffer_p[end + 1]; buffer_p[end + 1] = buffer_p[1]; buffer_p[1] = tmp; ecma_module_heap_sort_shift_down (buffer_p, end, 0); end -= 2; } while (end > 0); } buffer_end_p = properties_p->buffer_p + item_count; while (buffer_p < buffer_end_p) { if (buffer_p[1] & ECMA_MODULE_NAMESPACE_RESULT_FLAG) { ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (module_p->namespace_object_p, ecma_get_string_from_value (buffer_p[0]), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE, NULL); property_value_p->value = buffer_p[1]; } else { ecma_create_named_reference_property (module_p->namespace_object_p, ecma_get_string_from_value (buffer_p[0]), buffer_p[1]); } buffer_p += 2; } module_p->header.u.cls.u2.module_flags |= ECMA_MODULE_HAS_NAMESPACE; ecma_module_release_module_names (module_p->local_exports_p); module_p->local_exports_p = NULL; ecma_module_release_module_nodes (module_p->indirect_exports_p, false); module_p->indirect_exports_p = NULL; exit: /* Clean up. */ ecma_module_resolve_set_cleanup (resolve_set_p); ecma_collection_destroy (properties_p); return result; } /* ecma_module_create_namespace_object */ /** * Connects imported values to the current module scope. * * @return ECMA_VALUE_ERROR - if an error occurred * ECMA_VALUE_EMPTY - otherwise */ static ecma_value_t ecma_module_connect_imports (ecma_module_t *module_p) { ecma_object_t *local_env_p = module_p->scope_p; JERRY_ASSERT (ecma_is_lexical_environment (local_env_p)); ecma_module_node_t *import_node_p = module_p->imports_p; /* Check that the imported bindings don't exist yet. */ while (import_node_p != NULL) { ecma_module_names_t *import_names_p = import_node_p->module_names_p; JERRY_ASSERT (local_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK); while (import_names_p != NULL) { ecma_property_t *binding_p = ecma_find_named_property (local_env_p, import_names_p->local_name_p); if (binding_p != NULL) { return ecma_raise_syntax_error (ECMA_ERR_IMPORTED_BINDING_SHADOWS_LOCAL_VARIABLE); } import_names_p = import_names_p->next_p; } import_node_p = import_node_p->next_p; } import_node_p = module_p->imports_p; /* Resolve imports and create local bindings. */ while (import_node_p != NULL) { ecma_module_names_t *import_names_p = import_node_p->module_names_p; ecma_module_t *imported_module_p = ecma_module_get_from_object (import_node_p->u.path_or_module); while (import_names_p != NULL) { if (ecma_compare_ecma_string_to_magic_id (import_names_p->imex_name_p, LIT_MAGIC_STRING_ASTERISK_CHAR)) { /* Namespace import. */ ecma_property_value_t *value_p; value_p = ecma_create_named_data_property (module_p->scope_p, import_names_p->local_name_p, ECMA_PROPERTY_FIXED, NULL); value_p->value = ecma_make_object_value (imported_module_p->namespace_object_p); } else { ecma_module_resolve_result_t resolve_result; ecma_module_resolve_export (imported_module_p, import_names_p->imex_name_p, &resolve_result); if (resolve_result.result_type != ECMA_MODULE_RESOLVE_FOUND) { return ecma_module_resolve_throw (&resolve_result, import_names_p->imex_name_p); } if (resolve_result.result & ECMA_MODULE_NAMESPACE_RESULT_FLAG) { ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (module_p->scope_p, import_names_p->local_name_p, ECMA_PROPERTY_FIXED, NULL); property_value_p->value = resolve_result.result; } else { ecma_create_named_reference_property (module_p->scope_p, import_names_p->local_name_p, resolve_result.result); } } import_names_p = import_names_p->next_p; } ecma_module_release_module_names (import_node_p->module_names_p); import_node_p->module_names_p = NULL; import_node_p = import_node_p->next_p; } return ECMA_VALUE_EMPTY; } /* ecma_module_connect_imports */ /** * Initialize the current module by creating the local binding for the imported variables * and verifying indirect exports. * * @return ECMA_VALUE_ERROR - if an error occurred * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t ecma_module_initialize (ecma_module_t *module_p) /**< module */ { ecma_module_node_t *import_node_p = module_p->imports_p; while (import_node_p != NULL) { /* Module is evaluated even if it is used only in export-from statements. */ ecma_value_t result = ecma_module_evaluate (ecma_module_get_from_object (import_node_p->u.path_or_module)); if (ECMA_IS_VALUE_ERROR (result)) { return result; } ecma_free_value (result); import_node_p = import_node_p->next_p; } return ECMA_VALUE_EMPTY; } /* ecma_module_initialize */ /** * Gets the internal module pointer of a module * * @return module pointer - if module_val is a valid module, * NULL - otherwise */ ecma_module_t * ecma_module_get_resolved_module (ecma_value_t module_val) /**< module */ { if (!ecma_is_value_object (module_val)) { return NULL; } ecma_object_t *object_p = ecma_get_object_from_value (module_val); if (!ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_MODULE)) { return NULL; } return (ecma_module_t *) object_p; } /* ecma_module_get_resolved_module */ /** * A module stack for depth-first search */ typedef struct ecma_module_stack_item_t { struct ecma_module_stack_item_t *prev_p; /**< prev in the stack */ struct ecma_module_stack_item_t *parent_p; /**< parent item in the stack */ ecma_module_t *module_p; /**< currently processed module */ ecma_module_node_t *node_p; /**< currently processed node */ uint32_t dfs_index; /**< dfs index (ES2020 15.2.1.16) */ } ecma_module_stack_item_t; /** * Link module dependencies * * @return ECMA_VALUE_ERROR - if an error occurred * ECMA_VALUE_UNDEFINED - otherwise */ ecma_value_t ecma_module_link (ecma_module_t *module_p, /**< root module */ jerry_module_resolve_cb_t callback, /**< resolve module callback */ void *user_p) /**< pointer passed to the resolve callback */ { if (module_p->header.u.cls.u1.module_state != JERRY_MODULE_STATE_UNLINKED) { return ecma_raise_type_error (ECMA_ERR_MODULE_MUST_BE_IN_UNLINKED_STATE); } module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_LINKING; uint32_t dfs_index = 0; ecma_module_stack_item_t *last_p; ecma_module_node_t *node_p; last_p = (ecma_module_stack_item_t *) jmem_heap_alloc_block (sizeof (ecma_module_stack_item_t)); last_p->prev_p = NULL; last_p->parent_p = NULL; last_p->module_p = module_p; last_p->node_p = module_p->imports_p; last_p->dfs_index = dfs_index; module_p->header.u.cls.u3.dfs_ancestor_index = dfs_index; ecma_value_t module_val = ecma_make_object_value (&module_p->header.object); ecma_module_stack_item_t *current_p = last_p; restart: /* Entering into processing new node phase. Resolve dependencies first. */ node_p = current_p->node_p; JERRY_ASSERT (ecma_module_get_from_object (module_val)->imports_p == node_p); while (node_p != NULL) { ecma_module_t *resolved_module_p; if (!ecma_is_value_object (node_p->u.path_or_module)) { JERRY_ASSERT (ecma_is_value_string (node_p->u.path_or_module)); ecma_value_t resolve_result = callback (node_p->u.path_or_module, module_val, user_p); if (JERRY_UNLIKELY (ecma_is_value_exception (resolve_result))) { ecma_throw_exception (resolve_result); goto error; } resolved_module_p = ecma_module_get_resolved_module (resolve_result); if (resolved_module_p == NULL) { ecma_free_value (resolve_result); ecma_raise_type_error (ECMA_ERR_CALLBACK_RESULT_NOT_MODULE); goto error; } ecma_deref_ecma_string (ecma_get_string_from_value (node_p->u.path_or_module)); node_p->u.path_or_module = resolve_result; ecma_deref_object (ecma_get_object_from_value (resolve_result)); } else { resolved_module_p = ecma_module_get_from_object (node_p->u.path_or_module); } if (resolved_module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_ERROR) { ecma_raise_type_error (ECMA_ERR_LINK_TO_MODULE_IN_ERROR_STATE); goto error; } node_p = node_p->next_p; } /* Find next unlinked node, or return to parent */ while (true) { ecma_module_t *current_module_p = current_p->module_p; node_p = current_p->node_p; while (node_p != NULL) { module_p = ecma_module_get_from_object (node_p->u.path_or_module); if (module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_UNLINKED) { current_p->node_p = node_p->next_p; module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_LINKING; ecma_module_stack_item_t *item_p; item_p = (ecma_module_stack_item_t *) jmem_heap_alloc_block (sizeof (ecma_module_stack_item_t)); dfs_index++; item_p->prev_p = last_p; item_p->parent_p = current_p; item_p->module_p = module_p; item_p->node_p = module_p->imports_p; item_p->dfs_index = dfs_index; module_p->header.u.cls.u3.dfs_ancestor_index = dfs_index; last_p = item_p; current_p = item_p; module_val = node_p->u.path_or_module; goto restart; } if (module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING) { uint32_t dfs_ancestor_index = module_p->header.u.cls.u3.dfs_ancestor_index; if (dfs_ancestor_index < current_module_p->header.u.cls.u3.dfs_ancestor_index) { current_module_p->header.u.cls.u3.dfs_ancestor_index = dfs_ancestor_index; } } node_p = node_p->next_p; } if (current_module_p->scope_p == NULL) { JERRY_ASSERT (!(current_module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE)); /* Initialize scope for handling circular references. */ ecma_value_t result = vm_init_module_scope (current_module_p); if (ECMA_IS_VALUE_ERROR (result)) { ecma_module_set_error_state (current_module_p); goto error; } JERRY_ASSERT (result == ECMA_VALUE_EMPTY); } if (current_module_p->namespace_object_p == NULL) { ecma_object_t *namespace_object_p = ecma_create_object (NULL, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); namespace_object_p->type_flags_refs &= (ecma_object_descriptor_t) ~ECMA_OBJECT_FLAG_EXTENSIBLE; ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) namespace_object_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_MODULE_NAMESPACE; ECMA_SET_INTERNAL_VALUE_POINTER (ext_object_p->u.cls.u3.value, module_p); current_module_p->namespace_object_p = namespace_object_p; ecma_deref_object (namespace_object_p); } if (current_module_p->header.u.cls.u3.dfs_ancestor_index != current_p->dfs_index) { current_p = current_p->parent_p; JERRY_ASSERT (current_p != NULL); uint32_t dfs_ancestor_index = current_module_p->header.u.cls.u3.dfs_ancestor_index; if (dfs_ancestor_index < current_p->module_p->header.u.cls.u3.dfs_ancestor_index) { current_p->module_p->header.u.cls.u3.dfs_ancestor_index = dfs_ancestor_index; } continue; } ecma_module_stack_item_t *end_p = current_p->prev_p; current_p = current_p->parent_p; ecma_module_stack_item_t *iterator_p = last_p; do { JERRY_ASSERT (iterator_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING); if (ECMA_IS_VALUE_ERROR (ecma_module_create_namespace_object (iterator_p->module_p))) { ecma_module_set_error_state (iterator_p->module_p); goto error; } iterator_p = iterator_p->prev_p; } while (iterator_p != end_p); iterator_p = last_p; do { JERRY_ASSERT (iterator_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING); if (ECMA_IS_VALUE_ERROR (ecma_module_connect_imports (iterator_p->module_p))) { ecma_module_set_error_state (iterator_p->module_p); goto error; } iterator_p = iterator_p->prev_p; } while (iterator_p != end_p); do { ecma_module_stack_item_t *prev_p = last_p->prev_p; JERRY_ASSERT (last_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING); last_p->module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_LINKED; if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL) { JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_LINKED, ecma_make_object_value (&last_p->module_p->header.object), ECMA_VALUE_UNDEFINED, JERRY_CONTEXT (module_state_changed_callback_user_p)); } jmem_heap_free_block (last_p, sizeof (ecma_module_stack_item_t)); last_p = prev_p; } while (last_p != end_p); if (current_p == NULL) { return ECMA_VALUE_TRUE; } } error: JERRY_ASSERT (last_p != NULL); do { ecma_module_stack_item_t *prev_p = last_p->prev_p; if (last_p->module_p->header.u.cls.u1.module_state != JERRY_MODULE_STATE_ERROR) { JERRY_ASSERT (last_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING); last_p->module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_UNLINKED; } jmem_heap_free_block (last_p, sizeof (ecma_module_stack_item_t)); last_p = prev_p; } while (last_p != NULL); return ECMA_VALUE_ERROR; } /* ecma_module_link */ /** * Compute the result of 'import()' calls * * @return promise object representing the result of the operation */ ecma_value_t ecma_module_import (ecma_value_t specifier, /**< module specifier */ ecma_value_t user_value) /**< user value assigned to the script */ { ecma_string_t *specifier_p = ecma_op_to_string (specifier); ecma_module_t *module_p; if (JERRY_UNLIKELY (specifier_p == NULL)) { goto error; } if (JERRY_CONTEXT (module_import_callback_p) == NULL) { ecma_deref_ecma_string (specifier_p); goto error_module_instantiate; } jerry_value_t result; result = JERRY_CONTEXT (module_import_callback_p) (ecma_make_string_value (specifier_p), user_value, JERRY_CONTEXT (module_import_callback_user_p)); ecma_deref_ecma_string (specifier_p); if (JERRY_UNLIKELY (ecma_is_value_exception (result))) { ecma_throw_exception (result); goto error; } if (ecma_is_value_object (result) && ecma_is_promise (ecma_get_object_from_value (result))) { return result; } module_p = ecma_module_get_resolved_module (result); if (module_p == NULL) { ecma_free_value (result); goto error_module_instantiate; } if (module_p->header.u.cls.u1.module_state != JERRY_MODULE_STATE_EVALUATED) { ecma_deref_object (&module_p->header.object); goto error_module_instantiate; } result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_VALUE_UNDEFINED, NULL); ecma_fulfill_promise (result, ecma_make_object_value (module_p->namespace_object_p)); ecma_deref_object (&module_p->header.object); return result; error_module_instantiate: ecma_raise_range_error (ECMA_ERR_MODULE_CANNOT_BE_INSTANTIATED); error: if (jcontext_has_pending_abort ()) { return ECMA_VALUE_ERROR; } ecma_value_t exception = jcontext_take_exception (); ecma_value_t promise = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_VALUE_UNDEFINED, NULL); ecma_reject_promise (promise, exception); ecma_free_value (exception); return promise; } /* ecma_module_import */ /** * Cleans up and releases a module structure including all referenced modules. */ void ecma_module_release_module (ecma_module_t *module_p) /**< module */ { jerry_module_state_t state = (jerry_module_state_t) module_p->header.u.cls.u1.module_state; JERRY_ASSERT (state != JERRY_MODULE_STATE_INVALID); #ifndef JERRY_NDEBUG module_p->scope_p = NULL; module_p->namespace_object_p = NULL; #endif /* JERRY_NDEBUG */ ecma_module_release_module_names (module_p->local_exports_p); if (module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE) { return; } ecma_module_release_module_nodes (module_p->imports_p, true); ecma_module_release_module_nodes (module_p->indirect_exports_p, false); ecma_module_release_module_nodes (module_p->star_exports_p, false); if (module_p->u.compiled_code_p != NULL) { ecma_bytecode_deref (module_p->u.compiled_code_p); #ifndef JERRY_NDEBUG module_p->u.compiled_code_p = NULL; #endif /* JERRY_NDEBUG */ } } /* ecma_module_release_module */ #endif /* JERRY_MODULE_SYSTEM */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakmap-prototype.inc.h000664 001750 001750 00000003102 15164251010 052275 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * WeakMap.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 23.3.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_WEAKMAP, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.3.3.6 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_WEAKMAP_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_DELETE, ECMA_CONTAINER_ROUTINE_DELETE_WEAK, 1, 1) ROUTINE (LIT_MAGIC_STRING_GET, ECMA_CONTAINER_ROUTINE_GET, 1, 1) ROUTINE (LIT_MAGIC_STRING_HAS, ECMA_CONTAINER_ROUTINE_HAS, 1, 1) ROUTINE (LIT_MAGIC_STRING_SET, ECMA_CONTAINER_ROUTINE_SET, 2, 2) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-bigint-prototype.inc.h000664 001750 001750 00000003055 15164251010 052133 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * BigInt.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_BIGINT /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v11, 20.2.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_BIGINT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v11, 20.2.3.5 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_BIGINT_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_BIGINT_PROTOTYPE_TO_STRING, NON_FIXED, 0) ROUTINE (LIT_MAGIC_STRING_VALUE_OF_UL, ECMA_BIGINT_PROTOTYPE_VALUE_OF, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, ECMA_BIGINT_PROTOTYPE_TO_LOCALE_STRING, 0, 0) #endif /* JERRY_BUILTIN_BIGINT */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgCommon.h000664 001750 001750 00000006656 15164251010 033433 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_COMMON_H_ #define _TVG_COMMON_H_ #ifdef _WIN32 #if defined(WINAPI_ENTRY) #if (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) #include #endif #elif !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) #include #endif #endif #include #include #include #include #include "config.h" #include "thorvg.h" using namespace std; using namespace tvg; //for MSVC Compat #ifdef _MSC_VER #define TVG_UNUSED #define strncasecmp _strnicmp #define strcasecmp _stricmp #define strtok_r strtok_s #else #define TVG_UNUSED __attribute__ ((__unused__)) #endif // Portable 'fallthrough' attribute #if __has_cpp_attribute(fallthrough) #ifdef _MSC_VER #define TVG_FALLTHROUGH [[fallthrough]]; #else #define TVG_FALLTHROUGH __attribute__ ((fallthrough)); #endif #else #define TVG_FALLTHROUGH #endif #if defined(_MSC_VER) && defined(__clang__) #define strncpy strncpy_s #endif void* operator new(std::size_t size); void operator delete(void* ptr) noexcept; namespace tvg { enum class FileType { Png = 0, Jpg, Webp, Svg, Lot, Ttf, Raw, Gif, Unknown }; #ifdef THORVG_LOG_ENABLED constexpr auto ErrorColor = "\033[31m"; //red constexpr auto ErrorBgColor = "\033[41m";//bg red constexpr auto LogColor = "\033[32m"; //green constexpr auto LogBgColor = "\033[42m"; //bg green constexpr auto GreyColor = "\033[90m"; //grey constexpr auto ResetColors = "\033[0m"; //default #define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__) #define TVGLOG(tag, fmt, ...) fprintf(stdout, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__) #else #define TVGERR(...) do {} while(0) #define TVGLOG(...) do {} while(0) #endif template static inline T* to(const Paint* p) { return static_cast(const_cast(p)); } uint16_t THORVG_VERSION_NUMBER(); extern int engineInit; } #include "tvgAllocator.h" #endif //_TVG_COMMON_H_ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-extended-info.h000664 001750 001750 00000002725 15164251010 044751 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_EXTENDED_INFO_H #define ECMA_EXTENDED_INFO_H /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaextendedinfo Extended info * @{ */ #include "ecma-globals.h" /** * Vlq encoding: flag which is set for all bytes except the last one. */ #define ECMA_EXTENDED_INFO_VLQ_CONTINUE 0x80 /** * Vlq encoding: mask to decode the number fragment. */ #define ECMA_EXTENDED_INFO_VLQ_MASK 0x7f /** * Vlq encoding: number of bits stored in a byte. */ #define ECMA_EXTENDED_INFO_VLQ_SHIFT 7 uint32_t ecma_extended_info_decode_vlq (uint8_t **buffer_p); void ecma_extended_info_encode_vlq (uint8_t **buffer_p, uint32_t value); uint32_t ecma_extended_info_get_encoded_length (uint32_t value); uint8_t *ecma_compiled_code_resolve_extended_info (const ecma_compiled_code_t *bytecode_header_p); /** * @} * @} */ #endif /* !ECMA_EXTENDED_INFO_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/webp/types.h000664 001750 001750 00000002610 15164251010 034643 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Common types // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_WEBP_TYPES_H_ #define WEBP_WEBP_TYPES_H_ #include // for size_t #ifndef _MSC_VER #include #if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define WEBP_INLINE inline #else #define WEBP_INLINE #endif #else typedef signed char int8_t; typedef unsigned char uint8_t; typedef signed short int16_t; typedef unsigned short uint16_t; typedef signed int int32_t; typedef unsigned int uint32_t; typedef unsigned long long int uint64_t; typedef long long int int64_t; #define WEBP_INLINE __forceinline #endif /* _MSC_VER */ #ifndef WEBP_EXTERN # define WEBP_EXTERN(type) extern type #endif /* WEBP_EXTERN */ // Macro to check ABI compatibility (same major revision number) #define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8)) #endif /* WEBP_WEBP_TYPES_H_ */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGl.h000664 001750 001750 00000276346 15164251010 035017 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2025 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_H_ #define _TVG_GL_H_ #ifdef __EMSCRIPTEN__ #include #include #define GL_CHECK(stmt) stmt #else //__EMSCRIPTEN__ #if defined (THORVG_GL_TARGET_GLES) #define TVG_REQUIRE_GL_MAJOR_VER 3 #define TVG_REQUIRE_GL_MINOR_VER 0 #else #define TVG_REQUIRE_GL_MAJOR_VER 3 #define TVG_REQUIRE_GL_MINOR_VER 3 #endif #include "tvgCommon.h" #ifdef _DEBUG #define GL_CHECK(stmt) stmt; assert(glGetError() == GL_NO_ERROR); #else #define GL_CHECK(stmt) stmt #endif #ifdef _WIN64 typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; typedef signed long long int khronos_ssize_t; typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif typedef signed char khronos_int8_t; typedef unsigned char khronos_uint8_t; typedef signed short int khronos_int16_t; typedef unsigned short int khronos_uint16_t; typedef float khronos_float_t; typedef khronos_intptr_t GLintptr; typedef khronos_ssize_t GLsizeiptr; #ifndef GL_VERSION_1_0 #define GL_VERSION_1_0 1 typedef void GLvoid; typedef unsigned int GLenum; typedef khronos_float_t GLfloat; typedef int GLint; typedef int GLsizei; typedef unsigned int GLbitfield; typedef double GLdouble; typedef unsigned int GLuint; typedef unsigned char GLboolean; typedef khronos_uint8_t GLubyte; #define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_FALSE 0 #define GL_TRUE 1 #define GL_POINTS 0x0000 #define GL_LINES 0x0001 #define GL_LINE_LOOP 0x0002 #define GL_LINE_STRIP 0x0003 #define GL_TRIANGLES 0x0004 #define GL_TRIANGLE_STRIP 0x0005 #define GL_TRIANGLE_FAN 0x0006 #define GL_QUADS 0x0007 #define GL_NEVER 0x0200 #define GL_LESS 0x0201 #define GL_EQUAL 0x0202 #define GL_LEQUAL 0x0203 #define GL_GREATER 0x0204 #define GL_NOTEQUAL 0x0205 #define GL_GEQUAL 0x0206 #define GL_ALWAYS 0x0207 #define GL_ZERO 0 #define GL_ONE 1 #define GL_SRC_COLOR 0x0300 #define GL_ONE_MINUS_SRC_COLOR 0x0301 #define GL_SRC_ALPHA 0x0302 #define GL_ONE_MINUS_SRC_ALPHA 0x0303 #define GL_DST_ALPHA 0x0304 #define GL_ONE_MINUS_DST_ALPHA 0x0305 #define GL_DST_COLOR 0x0306 #define GL_ONE_MINUS_DST_COLOR 0x0307 #define GL_SRC_ALPHA_SATURATE 0x0308 #define GL_NONE 0 #define GL_FRONT_LEFT 0x0400 #define GL_FRONT_RIGHT 0x0401 #define GL_BACK_LEFT 0x0402 #define GL_BACK_RIGHT 0x0403 #define GL_FRONT 0x0404 #define GL_BACK 0x0405 #define GL_LEFT 0x0406 #define GL_RIGHT 0x0407 #define GL_FRONT_AND_BACK 0x0408 #define GL_NO_ERROR 0 #define GL_INVALID_ENUM 0x0500 #define GL_INVALID_VALUE 0x0501 #define GL_INVALID_OPERATION 0x0502 #define GL_OUT_OF_MEMORY 0x0505 #define GL_CW 0x0900 #define GL_CCW 0x0901 #define GL_POINT_SIZE 0x0B11 #define GL_POINT_SIZE_RANGE 0x0B12 #define GL_POINT_SIZE_GRANULARITY 0x0B13 #define GL_LINE_SMOOTH 0x0B20 #define GL_LINE_WIDTH 0x0B21 #define GL_LINE_WIDTH_RANGE 0x0B22 #define GL_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_POLYGON_MODE 0x0B40 #define GL_POLYGON_SMOOTH 0x0B41 #define GL_CULL_FACE 0x0B44 #define GL_CULL_FACE_MODE 0x0B45 #define GL_FRONT_FACE 0x0B46 #define GL_DEPTH_RANGE 0x0B70 #define GL_DEPTH_TEST 0x0B71 #define GL_DEPTH_WRITEMASK 0x0B72 #define GL_DEPTH_CLEAR_VALUE 0x0B73 #define GL_DEPTH_FUNC 0x0B74 #define GL_STENCIL_TEST 0x0B90 #define GL_STENCIL_CLEAR_VALUE 0x0B91 #define GL_STENCIL_FUNC 0x0B92 #define GL_STENCIL_VALUE_MASK 0x0B93 #define GL_STENCIL_FAIL 0x0B94 #define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 #define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 #define GL_STENCIL_REF 0x0B97 #define GL_STENCIL_WRITEMASK 0x0B98 #define GL_VIEWPORT 0x0BA2 #define GL_DITHER 0x0BD0 #define GL_BLEND_DST 0x0BE0 #define GL_BLEND_SRC 0x0BE1 #define GL_BLEND 0x0BE2 #define GL_LOGIC_OP_MODE 0x0BF0 #define GL_DRAW_BUFFER 0x0C01 #define GL_READ_BUFFER 0x0C02 #define GL_SCISSOR_BOX 0x0C10 #define GL_SCISSOR_TEST 0x0C11 #define GL_COLOR_CLEAR_VALUE 0x0C22 #define GL_COLOR_WRITEMASK 0x0C23 #define GL_DOUBLEBUFFER 0x0C32 #define GL_STEREO 0x0C33 #define GL_LINE_SMOOTH_HINT 0x0C52 #define GL_POLYGON_SMOOTH_HINT 0x0C53 #define GL_UNPACK_SWAP_BYTES 0x0CF0 #define GL_UNPACK_LSB_FIRST 0x0CF1 #define GL_UNPACK_ROW_LENGTH 0x0CF2 #define GL_UNPACK_SKIP_ROWS 0x0CF3 #define GL_UNPACK_SKIP_PIXELS 0x0CF4 #define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_PACK_SWAP_BYTES 0x0D00 #define GL_PACK_LSB_FIRST 0x0D01 #define GL_PACK_ROW_LENGTH 0x0D02 #define GL_PACK_SKIP_ROWS 0x0D03 #define GL_PACK_SKIP_PIXELS 0x0D04 #define GL_PACK_ALIGNMENT 0x0D05 #define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_MAX_VIEWPORT_DIMS 0x0D3A #define GL_SUBPIXEL_BITS 0x0D50 #define GL_TEXTURE_1D 0x0DE0 #define GL_TEXTURE_2D 0x0DE1 #define GL_TEXTURE_WIDTH 0x1000 #define GL_TEXTURE_HEIGHT 0x1001 #define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_DONT_CARE 0x1100 #define GL_FASTEST 0x1101 #define GL_NICEST 0x1102 #define GL_BYTE 0x1400 #define GL_UNSIGNED_BYTE 0x1401 #define GL_SHORT 0x1402 #define GL_UNSIGNED_SHORT 0x1403 #define GL_INT 0x1404 #define GL_UNSIGNED_INT 0x1405 #define GL_FLOAT 0x1406 #define GL_STACK_OVERFLOW 0x0503 #define GL_STACK_UNDERFLOW 0x0504 #define GL_CLEAR 0x1500 #define GL_AND 0x1501 #define GL_AND_REVERSE 0x1502 #define GL_COPY 0x1503 #define GL_AND_INVERTED 0x1504 #define GL_NOOP 0x1505 #define GL_XOR 0x1506 #define GL_OR 0x1507 #define GL_NOR 0x1508 #define GL_EQUIV 0x1509 #define GL_INVERT 0x150A #define GL_OR_REVERSE 0x150B #define GL_COPY_INVERTED 0x150C #define GL_OR_INVERTED 0x150D #define GL_NAND 0x150E #define GL_SET 0x150F #define GL_TEXTURE 0x1702 #define GL_COLOR 0x1800 #define GL_DEPTH 0x1801 #define GL_STENCIL 0x1802 #define GL_STENCIL_INDEX 0x1901 #define GL_DEPTH_COMPONENT 0x1902 #define GL_RED 0x1903 #define GL_GREEN 0x1904 #define GL_BLUE 0x1905 #define GL_ALPHA 0x1906 #define GL_RGB 0x1907 #define GL_RGBA 0x1908 #define GL_POINT 0x1B00 #define GL_LINE 0x1B01 #define GL_FILL 0x1B02 #define GL_KEEP 0x1E00 #define GL_REPLACE 0x1E01 #define GL_INCR 0x1E02 #define GL_DECR 0x1E03 #define GL_VENDOR 0x1F00 #define GL_RENDERER 0x1F01 #define GL_VERSION 0x1F02 #define GL_EXTENSIONS 0x1F03 #define GL_NEAREST 0x2600 #define GL_LINEAR 0x2601 #define GL_NEAREST_MIPMAP_NEAREST 0x2700 #define GL_LINEAR_MIPMAP_NEAREST 0x2701 #define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MIN_FILTER 0x2801 #define GL_TEXTURE_WRAP_S 0x2802 #define GL_TEXTURE_WRAP_T 0x2803 #define GL_REPEAT 0x2901 typedef void (*PFNGLCULLFACEPROC)(GLenum mode); typedef void (*PFNGLFRONTFACEPROC)(GLenum mode); typedef void (*PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); typedef void (*PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); typedef void (*PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (*PFNGLDRAWBUFFERPROC)(GLenum buf); typedef void (*PFNGLCLEARPROC)(GLbitfield mask); typedef void (*PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); typedef void (*PFNGLCLEARSTENCILPROC)(GLint s); typedef void (*PFNGLCLEARDEPTHPROC)(GLdouble depth); typedef void (*PFNGLCLEARDEPTHFPROC)(GLdouble depth); // GLES typedef void (*PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); typedef void (*PFNGLDEPTHMASKPROC)(GLboolean flag); typedef void (*PFNGLDISABLEPROC)(GLenum cap); typedef void (*PFNGLENABLEPROC)(GLenum cap); typedef void (*PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); typedef void (*PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); typedef void (*PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); typedef void (*PFNGLDEPTHFUNCPROC)(GLenum func); typedef GLenum (*PFNGLGETERRORPROC)(void); typedef void (*PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data); typedef const GLubyte *(*PFNGLGETSTRINGPROC) (GLenum name); typedef void (*PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); //typedef void (*PFNGLHINTPROC)(GLenum target, GLenum mode); //typedef void (*PFNGLLINEWIDTHPROC)(GLfloat width); //typedef void (*PFNGLPOINTSIZEPROC)(GLfloat size); //typedef void (*PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); //typedef void (*PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); //typedef void (*PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params); //typedef void (*PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params); //typedef void (*PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); //typedef void (*PFNGLSTENCILMASKPROC) (GLuint mask); //typedef void (*PFNGLFINISHPROC)(void); //typedef void (*PFNGLFLUSHPROC)(void); //typedef void (*PFNGLLOGICOPPROC)(GLenum opcode); //typedef void (*PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); //typedef void (*PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); //typedef void (*PFNGLREADBUFFERPROC)(GLenum src); //typedef void (*PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); //typedef void (*PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data); //typedef void (*PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data); //typedef void (*PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data); //typedef void (*PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void *pixels); //typedef void (*PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params); //typedef void (*PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); //typedef void (*PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat *params); //typedef void (*PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint *params); //typedef GLboolean (*PFNGLISENABLEDPROC)(GLenum cap); //typedef void (*PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); #endif /* GL_VERSION_1_0 */ #ifndef GL_VERSION_1_1 #define GL_VERSION_1_1 1 typedef khronos_float_t GLclampf; typedef double GLclampd; #define GL_COLOR_LOGIC_OP 0x0BF2 #define GL_POLYGON_OFFSET_UNITS 0x2A00 #define GL_POLYGON_OFFSET_POINT 0x2A01 #define GL_POLYGON_OFFSET_LINE 0x2A02 #define GL_POLYGON_OFFSET_FILL 0x8037 #define GL_POLYGON_OFFSET_FACTOR 0x8038 #define GL_TEXTURE_BINDING_1D 0x8068 #define GL_TEXTURE_BINDING_2D 0x8069 #define GL_TEXTURE_INTERNAL_FORMAT 0x1003 #define GL_TEXTURE_RED_SIZE 0x805C #define GL_TEXTURE_GREEN_SIZE 0x805D #define GL_TEXTURE_BLUE_SIZE 0x805E #define GL_TEXTURE_ALPHA_SIZE 0x805F #define GL_DOUBLE 0x140A #define GL_PROXY_TEXTURE_1D 0x8063 #define GL_PROXY_TEXTURE_2D 0x8064 #define GL_R3_G3_B2 0x2A10 #define GL_RGB4 0x804F #define GL_RGB5 0x8050 #define GL_RGB8 0x8051 #define GL_RGB10 0x8052 #define GL_RGB12 0x8053 #define GL_RGB16 0x8054 #define GL_RGBA2 0x8055 #define GL_RGBA4 0x8056 #define GL_RGB5_A1 0x8057 #define GL_RGBA8 0x8058 #define GL_RGB10_A2 0x8059 #define GL_RGBA12 0x805A #define GL_RGBA16 0x805B #define GL_VERTEX_ARRAY 0x8074 typedef void (*PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices); typedef void (*PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); typedef void (*PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures); typedef void (*PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures); //typedef void (*PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); //typedef void (*PFNGLGETPOINTERVPROC)(GLenum pname, void **params); //typedef void (*PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); //typedef void (*PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); //typedef void (*PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); //typedef void (*PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); //typedef void (*PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); //typedef void (*PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); //typedef void (*PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); //typedef GLboolean (*PFNGLISTEXTUREPROC)(GLuint texture); #endif /* GL_VERSION_1_1 */ #ifndef GL_VERSION_1_2 #define GL_VERSION_1_2 1 #define GL_UNSIGNED_BYTE_3_3_2 0x8032 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_INT_8_8_8_8 0x8035 #define GL_UNSIGNED_INT_10_10_10_2 0x8036 #define GL_TEXTURE_BINDING_3D 0x806A #define GL_PACK_SKIP_IMAGES 0x806B #define GL_PACK_IMAGE_HEIGHT 0x806C #define GL_UNPACK_SKIP_IMAGES 0x806D #define GL_UNPACK_IMAGE_HEIGHT 0x806E #define GL_TEXTURE_3D 0x806F #define GL_PROXY_TEXTURE_3D 0x8070 #define GL_TEXTURE_DEPTH 0x8071 #define GL_TEXTURE_WRAP_R 0x8072 #define GL_MAX_3D_TEXTURE_SIZE 0x8073 #define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 #define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 #define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #define GL_BGR 0x80E0 #define GL_BGRA 0x80E1 #define GL_MAX_ELEMENTS_VERTICES 0x80E8 #define GL_MAX_ELEMENTS_INDICES 0x80E9 #define GL_CLAMP_TO_EDGE 0x812F #define GL_TEXTURE_MIN_LOD 0x813A #define GL_TEXTURE_MAX_LOD 0x813B #define GL_TEXTURE_BASE_LEVEL 0x813C #define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 #define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 #define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 #define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E //typedef void (*PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); //typedef void (*PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); //typedef void (*PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); //typedef void (*PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); #endif /* GL_VERSION_1_2 */ #ifndef GL_VERSION_1_3 #define GL_VERSION_1_3 1 #define GL_TEXTURE0 0x84C0 #define GL_TEXTURE1 0x84C1 #define GL_TEXTURE2 0x84C2 #define GL_TEXTURE3 0x84C3 #define GL_TEXTURE4 0x84C4 #define GL_TEXTURE5 0x84C5 #define GL_TEXTURE6 0x84C6 #define GL_TEXTURE7 0x84C7 #define GL_TEXTURE8 0x84C8 #define GL_TEXTURE9 0x84C9 #define GL_TEXTURE10 0x84CA #define GL_TEXTURE11 0x84CB #define GL_TEXTURE12 0x84CC #define GL_TEXTURE13 0x84CD #define GL_TEXTURE14 0x84CE #define GL_TEXTURE15 0x84CF #define GL_TEXTURE16 0x84D0 #define GL_TEXTURE17 0x84D1 #define GL_TEXTURE18 0x84D2 #define GL_TEXTURE19 0x84D3 #define GL_TEXTURE20 0x84D4 #define GL_TEXTURE21 0x84D5 #define GL_TEXTURE22 0x84D6 #define GL_TEXTURE23 0x84D7 #define GL_TEXTURE24 0x84D8 #define GL_TEXTURE25 0x84D9 #define GL_TEXTURE26 0x84DA #define GL_TEXTURE27 0x84DB #define GL_TEXTURE28 0x84DC #define GL_TEXTURE29 0x84DD #define GL_TEXTURE30 0x84DE #define GL_TEXTURE31 0x84DF #define GL_ACTIVE_TEXTURE 0x84E0 #define GL_MULTISAMPLE 0x809D #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E #define GL_SAMPLE_ALPHA_TO_ONE 0x809F #define GL_SAMPLE_COVERAGE 0x80A0 #define GL_SAMPLE_BUFFERS 0x80A8 #define GL_SAMPLES 0x80A9 #define GL_SAMPLE_COVERAGE_VALUE 0x80AA #define GL_SAMPLE_COVERAGE_INVERT 0x80AB #define GL_TEXTURE_CUBE_MAP 0x8513 #define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A #define GL_PROXY_TEXTURE_CUBE_MAP 0x851B #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C #define GL_COMPRESSED_RGB 0x84ED #define GL_COMPRESSED_RGBA 0x84EE #define GL_TEXTURE_COMPRESSION_HINT 0x84EF #define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 #define GL_TEXTURE_COMPRESSED 0x86A1 #define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 #define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 #define GL_CLAMP_TO_BORDER 0x812D typedef void (*PFNGLACTIVETEXTUREPROC)(GLenum texture); //typedef void (*PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); //typedef void (*PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); //typedef void (*PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); //typedef void (*PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); //typedef void (*PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); //typedef void (*PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); //typedef void (*PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); //typedef void (*PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void *img); #endif /* GL_VERSION_1_3 */ #ifndef GL_VERSION_1_4 #define GL_VERSION_1_4 1 #define GL_BLEND_DST_RGB 0x80C8 #define GL_BLEND_SRC_RGB 0x80C9 #define GL_BLEND_DST_ALPHA 0x80CA #define GL_BLEND_SRC_ALPHA 0x80CB #define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_DEPTH_COMPONENT24 0x81A6 #define GL_DEPTH_COMPONENT32 0x81A7 #define GL_MIRRORED_REPEAT 0x8370 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD #define GL_TEXTURE_LOD_BIAS 0x8501 #define GL_INCR_WRAP 0x8507 #define GL_DECR_WRAP 0x8508 #define GL_TEXTURE_DEPTH_SIZE 0x884A #define GL_TEXTURE_COMPARE_MODE 0x884C #define GL_TEXTURE_COMPARE_FUNC 0x884D #define GL_BLEND_COLOR 0x8005 #define GL_BLEND_EQUATION 0x8009 #define GL_CONSTANT_COLOR 0x8001 #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 #define GL_CONSTANT_ALPHA 0x8003 #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 #define GL_FUNC_ADD 0x8006 #define GL_FUNC_REVERSE_SUBTRACT 0x800B #define GL_FUNC_SUBTRACT 0x800A #define GL_MIN 0x8007 #define GL_MAX 0x8008 typedef void (*PFNGLBLENDEQUATIONPROC)(GLenum mode); //typedef void (*PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); //typedef void (*PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); //typedef void (*PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); //typedef void (*PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); //typedef void (*PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat *params); //typedef void (*PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); //typedef void (*PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint *params); //typedef void (*PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); #endif /* GL_VERSION_1_4 */ #ifndef GL_VERSION_1_5 #define GL_VERSION_1_5 1 typedef khronos_ssize_t GLsizeiptr; typedef khronos_intptr_t GLintptr; #define GL_BUFFER_SIZE 0x8764 #define GL_BUFFER_USAGE 0x8765 #define GL_QUERY_COUNTER_BITS 0x8864 #define GL_CURRENT_QUERY 0x8865 #define GL_QUERY_RESULT 0x8866 #define GL_QUERY_RESULT_AVAILABLE 0x8867 #define GL_ARRAY_BUFFER 0x8892 #define GL_ELEMENT_ARRAY_BUFFER 0x8893 #define GL_ARRAY_BUFFER_BINDING 0x8894 #define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F #define GL_READ_ONLY 0x88B8 #define GL_WRITE_ONLY 0x88B9 #define GL_READ_WRITE 0x88BA #define GL_BUFFER_ACCESS 0x88BB #define GL_BUFFER_MAPPED 0x88BC #define GL_BUFFER_MAP_POINTER 0x88BD #define GL_STREAM_DRAW 0x88E0 #define GL_STREAM_READ 0x88E1 #define GL_STREAM_COPY 0x88E2 #define GL_STATIC_DRAW 0x88E4 #define GL_STATIC_READ 0x88E5 #define GL_STATIC_COPY 0x88E6 #define GL_DYNAMIC_DRAW 0x88E8 #define GL_DYNAMIC_READ 0x88E9 #define GL_DYNAMIC_COPY 0x88EA #define GL_SAMPLES_PASSED 0x8914 #define GL_SRC1_ALPHA 0x8589 typedef void (*PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); typedef void (*PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers); typedef void (*PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers); typedef void (*PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage); //typedef void (*PFNGLGENQUERIESPROC)(GLsizei n, GLuint *ids); //typedef void (*PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint *ids); //typedef GLboolean (*PFNGLISQUERYPROC)(GLuint id); //typedef void (*PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); //typedef void (*PFNGLENDQUERYPROC)(GLenum target); //typedef void (*PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint *params); //typedef void (*PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint *params); //typedef void (*PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params); //typedef GLboolean (*PFNGLISBUFFERPROC)(GLuint buffer); //typedef void (*PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); //typedef void (*PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void *data); //typedef void *(*PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); //typedef GLboolean (*PFNGLUNMAPBUFFERPROC)(GLenum target); //typedef void (*PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); //typedef void (*PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params); #endif /* GL_VERSION_1_5 */ #ifndef GL_VERSION_2_0 #define GL_VERSION_2_0 1 typedef char GLchar; typedef khronos_int16_t GLshort; typedef khronos_int8_t GLbyte; typedef khronos_uint16_t GLushort; #define GL_BLEND_EQUATION_RGB 0x8009 #define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 #define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 #define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 #define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 #define GL_CURRENT_VERTEX_ATTRIB 0x8626 #define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 #define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 #define GL_STENCIL_BACK_FUNC 0x8800 #define GL_STENCIL_BACK_FAIL 0x8801 #define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 #define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 #define GL_MAX_DRAW_BUFFERS 0x8824 #define GL_DRAW_BUFFER0 0x8825 #define GL_DRAW_BUFFER1 0x8826 #define GL_DRAW_BUFFER2 0x8827 #define GL_DRAW_BUFFER3 0x8828 #define GL_DRAW_BUFFER4 0x8829 #define GL_DRAW_BUFFER5 0x882A #define GL_DRAW_BUFFER6 0x882B #define GL_DRAW_BUFFER7 0x882C #define GL_DRAW_BUFFER8 0x882D #define GL_DRAW_BUFFER9 0x882E #define GL_DRAW_BUFFER10 0x882F #define GL_DRAW_BUFFER11 0x8830 #define GL_DRAW_BUFFER12 0x8831 #define GL_DRAW_BUFFER13 0x8832 #define GL_DRAW_BUFFER14 0x8833 #define GL_DRAW_BUFFER15 0x8834 #define GL_BLEND_EQUATION_ALPHA 0x883D #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 #define GL_FRAGMENT_SHADER 0x8B30 #define GL_VERTEX_SHADER 0x8B31 #define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A #define GL_MAX_VARYING_FLOATS 0x8B4B #define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D #define GL_SHADER_TYPE 0x8B4F #define GL_FLOAT_VEC2 0x8B50 #define GL_FLOAT_VEC3 0x8B51 #define GL_FLOAT_VEC4 0x8B52 #define GL_INT_VEC2 0x8B53 #define GL_INT_VEC3 0x8B54 #define GL_INT_VEC4 0x8B55 #define GL_BOOL 0x8B56 #define GL_BOOL_VEC2 0x8B57 #define GL_BOOL_VEC3 0x8B58 #define GL_BOOL_VEC4 0x8B59 #define GL_FLOAT_MAT2 0x8B5A #define GL_FLOAT_MAT3 0x8B5B #define GL_FLOAT_MAT4 0x8B5C #define GL_SAMPLER_1D 0x8B5D #define GL_SAMPLER_2D 0x8B5E #define GL_SAMPLER_3D 0x8B5F #define GL_SAMPLER_CUBE 0x8B60 #define GL_SAMPLER_1D_SHADOW 0x8B61 #define GL_SAMPLER_2D_SHADOW 0x8B62 #define GL_DELETE_STATUS 0x8B80 #define GL_COMPILE_STATUS 0x8B81 #define GL_LINK_STATUS 0x8B82 #define GL_VALIDATE_STATUS 0x8B83 #define GL_INFO_LOG_LENGTH 0x8B84 #define GL_ATTACHED_SHADERS 0x8B85 #define GL_ACTIVE_UNIFORMS 0x8B86 #define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 #define GL_SHADER_SOURCE_LENGTH 0x8B88 #define GL_ACTIVE_ATTRIBUTES 0x8B89 #define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B #define GL_SHADING_LANGUAGE_VERSION 0x8B8C #define GL_CURRENT_PROGRAM 0x8B8D #define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 #define GL_LOWER_LEFT 0x8CA1 #define GL_UPPER_LEFT 0x8CA2 #define GL_STENCIL_BACK_REF 0x8CA3 #define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 #define GL_STENCIL_BACK_WRITEMASK 0x8CA5 typedef void (*PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs); typedef void (*PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); typedef void (*PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); typedef void (*PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); typedef void (*PFNGLCOMPILESHADERPROC)(GLuint shader); typedef GLuint (*PFNGLCREATEPROGRAMPROC)(void); typedef GLuint (*PFNGLCREATESHADERPROC)(GLenum type); typedef void (*PFNGLDELETEPROGRAMPROC)(GLuint program); typedef void (*PFNGLDELETESHADERPROC)(GLuint shader); typedef void (*PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); typedef void (*PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); typedef GLint (*PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name); typedef void (*PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params); typedef void (*PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); typedef void (*PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params); typedef void (*PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); typedef GLint (*PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name); typedef void (*PFNGLLINKPROGRAMPROC)(GLuint program); typedef void (*PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); typedef void (*PFNGLUSEPROGRAMPROC)(GLuint program); typedef void (*PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); typedef void (*PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value); typedef void (*PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value); typedef void (*PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value); typedef void (*PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value); typedef void (*PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value); typedef void (*PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value); typedef void (*PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value); typedef void (*PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value); // typedef void (*PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (*PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); //typedef void (*PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); //typedef void (*PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); //typedef void (*PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name); //typedef void (*PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); //typedef void (*PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); //typedef void (*PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); //typedef void (*PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); //typedef void (*PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); //typedef void (*PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params); //typedef void (*PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params); //typedef void (*PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble *params); //typedef void (*PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params); //typedef void (*PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params); //typedef void (*PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer); //typedef GLboolean (*PFNGLISPROGRAMPROC)(GLuint program); //typedef GLboolean (*PFNGLISSHADERPROC)(GLuint shader); //typedef void (*PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); //typedef void (*PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); //typedef void (*PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); //typedef void (*PFNGLUNIFORM1IPROC)(GLint location, GLint v0); //typedef void (*PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); //typedef void (*PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); //typedef void (*PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); //typedef void (*PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (*PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); //typedef void (*PFNGLVALIDATEPROGRAMPROC)(GLuint program); //typedef void (*PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); //typedef void (*PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble *v); //typedef void (*PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); //typedef void (*PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v); //typedef void (*PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); //typedef void (*PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort *v); //typedef void (*PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); //typedef void (*PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble *v); //typedef void (*PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); //typedef void (*PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v); //typedef void (*PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); //typedef void (*PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort *v); //typedef void (*PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); //typedef void (*PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble *v); //typedef void (*PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); //typedef void (*PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v); //typedef void (*PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); //typedef void (*PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort *v); //typedef void (*PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte *v); //typedef void (*PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint *v); //typedef void (*PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort *v); //typedef void (*PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); //typedef void (*PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte *v); //typedef void (*PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint *v); //typedef void (*PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort *v); //typedef void (*PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte *v); //typedef void (*PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); //typedef void (*PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble *v); //typedef void (*PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); //typedef void (*PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v); //typedef void (*PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint *v); //typedef void (*PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); //typedef void (*PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort *v); //typedef void (*PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte *v); //typedef void (*PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint *v); //typedef void (*PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort *v); #endif /* GL_VERSION_2_0 */ #ifndef GL_VERSION_2_1 #define GL_VERSION_2_1 1 #define GL_PIXEL_PACK_BUFFER 0x88EB #define GL_PIXEL_UNPACK_BUFFER 0x88EC #define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED #define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF #define GL_FLOAT_MAT2x3 0x8B65 #define GL_FLOAT_MAT2x4 0x8B66 #define GL_FLOAT_MAT3x2 0x8B67 #define GL_FLOAT_MAT3x4 0x8B68 #define GL_FLOAT_MAT4x2 0x8B69 #define GL_FLOAT_MAT4x3 0x8B6A #define GL_SRGB 0x8C40 #define GL_SRGB8 0x8C41 #define GL_SRGB_ALPHA 0x8C42 #define GL_SRGB8_ALPHA8 0x8C43 #define GL_COMPRESSED_SRGB 0x8C48 #define GL_COMPRESSED_SRGB_ALPHA 0x8C49 //typedef void (*PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); //typedef void (*PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); //typedef void (*PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); //typedef void (*PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); //typedef void (*PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); //typedef void (*PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); #endif /* GL_VERSION_2_1 */ #ifndef GL_VERSION_3_0 #define GL_VERSION_3_0 1 typedef khronos_uint16_t GLhalf; #define GL_COMPARE_REF_TO_TEXTURE 0x884E #define GL_CLIP_DISTANCE0 0x3000 #define GL_CLIP_DISTANCE1 0x3001 #define GL_CLIP_DISTANCE2 0x3002 #define GL_CLIP_DISTANCE3 0x3003 #define GL_CLIP_DISTANCE4 0x3004 #define GL_CLIP_DISTANCE5 0x3005 #define GL_CLIP_DISTANCE6 0x3006 #define GL_CLIP_DISTANCE7 0x3007 #define GL_MAX_CLIP_DISTANCES 0x0D32 #define GL_MAJOR_VERSION 0x821B #define GL_MINOR_VERSION 0x821C #define GL_NUM_EXTENSIONS 0x821D #define GL_CONTEXT_FLAGS 0x821E #define GL_COMPRESSED_RED 0x8225 #define GL_COMPRESSED_RG 0x8226 #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 #define GL_RGBA32F 0x8814 #define GL_RGB32F 0x8815 #define GL_RGBA16F 0x881A #define GL_RGB16F 0x881B #define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF #define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 #define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 #define GL_CLAMP_READ_COLOR 0x891C #define GL_FIXED_ONLY 0x891D #define GL_MAX_VARYING_COMPONENTS 0x8B4B #define GL_TEXTURE_1D_ARRAY 0x8C18 #define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 #define GL_TEXTURE_2D_ARRAY 0x8C1A #define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B #define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C #define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D #define GL_R11F_G11F_B10F 0x8C3A #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B #define GL_RGB9_E5 0x8C3D #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E #define GL_TEXTURE_SHARED_SIZE 0x8C3F #define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 #define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 #define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 #define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 #define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 #define GL_PRIMITIVES_GENERATED 0x8C87 #define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 #define GL_RASTERIZER_DISCARD 0x8C89 #define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B #define GL_INTERLEAVED_ATTRIBS 0x8C8C #define GL_SEPARATE_ATTRIBS 0x8C8D #define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E #define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F #define GL_RGBA32UI 0x8D70 #define GL_RGB32UI 0x8D71 #define GL_RGBA16UI 0x8D76 #define GL_RGB16UI 0x8D77 #define GL_RGBA8UI 0x8D7C #define GL_RGB8UI 0x8D7D #define GL_RGBA32I 0x8D82 #define GL_RGB32I 0x8D83 #define GL_RGBA16I 0x8D88 #define GL_RGB16I 0x8D89 #define GL_RGBA8I 0x8D8E #define GL_RGB8I 0x8D8F #define GL_RED_INTEGER 0x8D94 #define GL_GREEN_INTEGER 0x8D95 #define GL_BLUE_INTEGER 0x8D96 #define GL_RGB_INTEGER 0x8D98 #define GL_RGBA_INTEGER 0x8D99 #define GL_BGR_INTEGER 0x8D9A #define GL_BGRA_INTEGER 0x8D9B #define GL_SAMPLER_1D_ARRAY 0x8DC0 #define GL_SAMPLER_2D_ARRAY 0x8DC1 #define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 #define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 #define GL_SAMPLER_CUBE_SHADOW 0x8DC5 #define GL_UNSIGNED_INT_VEC2 0x8DC6 #define GL_UNSIGNED_INT_VEC3 0x8DC7 #define GL_UNSIGNED_INT_VEC4 0x8DC8 #define GL_INT_SAMPLER_1D 0x8DC9 #define GL_INT_SAMPLER_2D 0x8DCA #define GL_INT_SAMPLER_3D 0x8DCB #define GL_INT_SAMPLER_CUBE 0x8DCC #define GL_INT_SAMPLER_1D_ARRAY 0x8DCE #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF #define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 #define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 #define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 #define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 #define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 #define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 #define GL_QUERY_WAIT 0x8E13 #define GL_QUERY_NO_WAIT 0x8E14 #define GL_QUERY_BY_REGION_WAIT 0x8E15 #define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 #define GL_BUFFER_ACCESS_FLAGS 0x911F #define GL_BUFFER_MAP_LENGTH 0x9120 #define GL_BUFFER_MAP_OFFSET 0x9121 #define GL_DEPTH_COMPONENT32F 0x8CAC #define GL_DEPTH32F_STENCIL8 0x8CAD #define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 #define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 #define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 #define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 #define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 #define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 #define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 #define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 #define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 #define GL_FRAMEBUFFER_DEFAULT 0x8218 #define GL_FRAMEBUFFER_UNDEFINED 0x8219 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A #define GL_MAX_RENDERBUFFER_SIZE 0x84E8 #define GL_DEPTH_STENCIL 0x84F9 #define GL_UNSIGNED_INT_24_8 0x84FA #define GL_DEPTH24_STENCIL8 0x88F0 #define GL_TEXTURE_STENCIL_SIZE 0x88F1 #define GL_TEXTURE_RED_TYPE 0x8C10 #define GL_TEXTURE_GREEN_TYPE 0x8C11 #define GL_TEXTURE_BLUE_TYPE 0x8C12 #define GL_TEXTURE_ALPHA_TYPE 0x8C13 #define GL_TEXTURE_DEPTH_TYPE 0x8C16 #define GL_UNSIGNED_NORMALIZED 0x8C17 #define GL_FRAMEBUFFER_BINDING 0x8CA6 #define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 #define GL_RENDERBUFFER_BINDING 0x8CA7 #define GL_READ_FRAMEBUFFER 0x8CA8 #define GL_DRAW_FRAMEBUFFER 0x8CA9 #define GL_READ_FRAMEBUFFER_BINDING 0x8CAA #define GL_RENDERBUFFER_SAMPLES 0x8CAB #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD #define GL_MAX_COLOR_ATTACHMENTS 0x8CDF #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_COLOR_ATTACHMENT1 0x8CE1 #define GL_COLOR_ATTACHMENT2 0x8CE2 #define GL_COLOR_ATTACHMENT3 0x8CE3 #define GL_COLOR_ATTACHMENT4 0x8CE4 #define GL_COLOR_ATTACHMENT5 0x8CE5 #define GL_COLOR_ATTACHMENT6 0x8CE6 #define GL_COLOR_ATTACHMENT7 0x8CE7 #define GL_COLOR_ATTACHMENT8 0x8CE8 #define GL_COLOR_ATTACHMENT9 0x8CE9 #define GL_COLOR_ATTACHMENT10 0x8CEA #define GL_COLOR_ATTACHMENT11 0x8CEB #define GL_COLOR_ATTACHMENT12 0x8CEC #define GL_COLOR_ATTACHMENT13 0x8CED #define GL_COLOR_ATTACHMENT14 0x8CEE #define GL_COLOR_ATTACHMENT15 0x8CEF #define GL_COLOR_ATTACHMENT16 0x8CF0 #define GL_COLOR_ATTACHMENT17 0x8CF1 #define GL_COLOR_ATTACHMENT18 0x8CF2 #define GL_COLOR_ATTACHMENT19 0x8CF3 #define GL_COLOR_ATTACHMENT20 0x8CF4 #define GL_COLOR_ATTACHMENT21 0x8CF5 #define GL_COLOR_ATTACHMENT22 0x8CF6 #define GL_COLOR_ATTACHMENT23 0x8CF7 #define GL_COLOR_ATTACHMENT24 0x8CF8 #define GL_COLOR_ATTACHMENT25 0x8CF9 #define GL_COLOR_ATTACHMENT26 0x8CFA #define GL_COLOR_ATTACHMENT27 0x8CFB #define GL_COLOR_ATTACHMENT28 0x8CFC #define GL_COLOR_ATTACHMENT29 0x8CFD #define GL_COLOR_ATTACHMENT30 0x8CFE #define GL_COLOR_ATTACHMENT31 0x8CFF #define GL_DEPTH_ATTACHMENT 0x8D00 #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_FRAMEBUFFER 0x8D40 #define GL_RENDERBUFFER 0x8D41 #define GL_RENDERBUFFER_WIDTH 0x8D42 #define GL_RENDERBUFFER_HEIGHT 0x8D43 #define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 #define GL_STENCIL_INDEX1 0x8D46 #define GL_STENCIL_INDEX4 0x8D47 #define GL_STENCIL_INDEX8 0x8D48 #define GL_STENCIL_INDEX16 0x8D49 #define GL_RENDERBUFFER_RED_SIZE 0x8D50 #define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 #define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 #define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 #define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 #define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #define GL_MAX_SAMPLES 0x8D57 #define GL_FRAMEBUFFER_SRGB 0x8DB9 #define GL_HALF_FLOAT 0x140B #define GL_MAP_READ_BIT 0x0001 #define GL_MAP_WRITE_BIT 0x0002 #define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 #define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 #define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 #define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 #define GL_COMPRESSED_RED_RGTC1 0x8DBB #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC #define GL_COMPRESSED_RG_RGTC2 0x8DBD #define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE #define GL_RG 0x8227 #define GL_RG_INTEGER 0x8228 #define GL_R8 0x8229 #define GL_R16 0x822A #define GL_RG8 0x822B #define GL_RG16 0x822C #define GL_R16F 0x822D #define GL_R32F 0x822E #define GL_RG16F 0x822F #define GL_RG32F 0x8230 #define GL_R8I 0x8231 #define GL_R8UI 0x8232 #define GL_R16I 0x8233 #define GL_R16UI 0x8234 #define GL_R32I 0x8235 #define GL_R32UI 0x8236 #define GL_RG8I 0x8237 #define GL_RG8UI 0x8238 #define GL_RG16I 0x8239 #define GL_RG16UI 0x823A #define GL_RG32I 0x823B #define GL_RG32UI 0x823C #define GL_VERTEX_ARRAY_BINDING 0x85B5 typedef void (*PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (*PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); typedef void (*PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers); typedef void (*PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers); typedef void (*PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments); // GLES typedef void (*PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); typedef void (*PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers); typedef void (*PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers); typedef void (*PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (*PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef void (*PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); typedef void (*PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); typedef void (*PFNGLBINDVERTEXARRAYPROC)(GLuint array); typedef void (*PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint *arrays); typedef void (*PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays); //typedef void (*PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); //typedef void (*PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean *data); //typedef void (*PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint *data); //typedef void (*PFNGLENABLEIPROC)(GLenum target, GLuint index); //typedef void (*PFNGLDISABLEIPROC)(GLenum target, GLuint index); //typedef GLboolean (*PFNGLISENABLEDIPROC)(GLenum target, GLuint index); //typedef void (*PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); //typedef void (*PFNGLENDTRANSFORMFEEDBACKPROC)(void); //typedef void (*PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); //typedef void (*PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); //typedef void (*PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); //typedef void (*PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); //typedef void (*PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); //typedef void (*PFNGLENDCONDITIONALRENDERPROC)(void); //typedef void (*PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); //typedef void (*PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint *params); //typedef void (*PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint *params); //typedef void (*PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); //typedef void (*PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); //typedef void (*PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); //typedef void (*PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); //typedef void (*PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); //typedef void (*PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); //typedef void (*PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); //typedef void (*PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); //typedef void (*PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint *v); //typedef void (*PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint *v); //typedef void (*PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint *v); //typedef void (*PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v); //typedef void (*PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint *v); //typedef void (*PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint *v); //typedef void (*PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint *v); //typedef void (*PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v); //typedef void (*PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte *v); //typedef void (*PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort *v); //typedef void (*PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte *v); //typedef void (*PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort *v); //typedef void (*PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params); //typedef void (*PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar *name); //typedef GLint (*PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar *name); //typedef void (*PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); //typedef void (*PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); //typedef void (*PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); //typedef void (*PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); //typedef void (*PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint *value); //typedef void (*PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint *value); //typedef void (*PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint *value); //typedef void (*PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint *value); //typedef void (*PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint *params); //typedef void (*PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint *params); //typedef void (*PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint *params); //typedef void (*PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint *params); //typedef void (*PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint *value); //typedef void (*PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint *value); //typedef void (*PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat *value); //typedef void (*PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); //typedef const GLubyte *(*PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); //typedef GLboolean (*PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); //typedef void (*PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); //typedef void (*PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); //typedef GLboolean (*PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); //typedef GLenum (*PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); //typedef void (*PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); //typedef void (*PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); //typedef void (*PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params); //typedef void (*PFNGLGENERATEMIPMAPPROC)(GLenum target); //typedef void (*PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); //typedef void *(*PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); //typedef void (*PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); //typedef GLboolean (*PFNGLISVERTEXARRAYPROC)(GLuint array); #endif /* GL_VERSION_3_0 */ #ifndef GL_VERSION_3_1 #define GL_VERSION_3_1 1 #define GL_SAMPLER_2D_RECT 0x8B63 #define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 #define GL_SAMPLER_BUFFER 0x8DC2 #define GL_INT_SAMPLER_2D_RECT 0x8DCD #define GL_INT_SAMPLER_BUFFER 0x8DD0 #define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 #define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 #define GL_TEXTURE_BUFFER 0x8C2A #define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B #define GL_TEXTURE_BINDING_BUFFER 0x8C2C #define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D #define GL_TEXTURE_RECTANGLE 0x84F5 #define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 #define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 #define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 #define GL_R8_SNORM 0x8F94 #define GL_RG8_SNORM 0x8F95 #define GL_RGB8_SNORM 0x8F96 #define GL_RGBA8_SNORM 0x8F97 #define GL_R16_SNORM 0x8F98 #define GL_RG16_SNORM 0x8F99 #define GL_RGB16_SNORM 0x8F9A #define GL_RGBA16_SNORM 0x8F9B #define GL_SIGNED_NORMALIZED 0x8F9C #define GL_PRIMITIVE_RESTART 0x8F9D #define GL_PRIMITIVE_RESTART_INDEX 0x8F9E #define GL_COPY_READ_BUFFER 0x8F36 #define GL_COPY_WRITE_BUFFER 0x8F37 #define GL_UNIFORM_BUFFER 0x8A11 #define GL_UNIFORM_BUFFER_BINDING 0x8A28 #define GL_UNIFORM_BUFFER_START 0x8A29 #define GL_UNIFORM_BUFFER_SIZE 0x8A2A #define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B #define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C #define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D #define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E #define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F #define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 #define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 #define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 #define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 #define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 #define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 #define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 #define GL_UNIFORM_TYPE 0x8A37 #define GL_UNIFORM_SIZE 0x8A38 #define GL_UNIFORM_NAME_LENGTH 0x8A39 #define GL_UNIFORM_BLOCK_INDEX 0x8A3A #define GL_UNIFORM_OFFSET 0x8A3B #define GL_UNIFORM_ARRAY_STRIDE 0x8A3C #define GL_UNIFORM_MATRIX_STRIDE 0x8A3D #define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E #define GL_UNIFORM_BLOCK_BINDING 0x8A3F #define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 #define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 #define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 #define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 #define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 #define GL_INVALID_INDEX 0xFFFFFFFFu typedef GLuint (*PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar *uniformBlockName); typedef void (*PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); //typedef void (*PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); //typedef void (*PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); //typedef void (*PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); //typedef void (*PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); //typedef void (*PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); //typedef void (*PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); //typedef void (*PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); //typedef void (*PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); //typedef void (*PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); //typedef void (*PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); #endif /* GL_VERSION_3_1 */ #if defined(_WIN32) && !defined(__CYGWIN__) && defined(THORVG_GL_TARGET_GL) typedef HGLRC (WINAPI *PFNWGLGETCURRENTCONTEXTPROC)(void); typedef BOOL (WINAPI *PFNWGLMAKECURRENTPROC)(HDC, HGLRC); #endif #if defined(THORVG_GL_TARGET_GLES) typedef void* EGLDisplay; typedef void* EGLSurface; typedef void* EGLContext; typedef EGLContext (*PFNEGLGETCURRENTCONTEXTPROC)(void); typedef unsigned int (*PFNEGLMAKECURRENTPROC)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); #endif //GL_VERSION_1_0 extern PFNGLCULLFACEPROC glCullFace; extern PFNGLFRONTFACEPROC glFrontFace; extern PFNGLSCISSORPROC glScissor; extern PFNGLTEXPARAMETERIPROC glTexParameteri; extern PFNGLTEXIMAGE2DPROC glTexImage2D; extern PFNGLDRAWBUFFERPROC glDrawBuffer; extern PFNGLDRAWBUFFERSPROC glDrawBuffers; extern PFNGLCLEARPROC glClear; extern PFNGLCLEARCOLORPROC glClearColor; extern PFNGLCLEARSTENCILPROC glClearStencil; extern PFNGLCLEARDEPTHPROC glClearDepth; extern PFNGLCLEARDEPTHFPROC glClearDepthf; // GLES extern PFNGLCOLORMASKPROC glColorMask; extern PFNGLDEPTHMASKPROC glDepthMask; extern PFNGLDISABLEPROC glDisable; extern PFNGLENABLEPROC glEnable; extern PFNGLBLENDFUNCPROC glBlendFunc; extern PFNGLSTENCILFUNCPROC glStencilFunc; extern PFNGLSTENCILOPPROC glStencilOp; extern PFNGLDEPTHFUNCPROC glDepthFunc; extern PFNGLGETERRORPROC glGetError; extern PFNGLGETINTEGERVPROC glGetIntegerv; extern PFNGLGETSTRINGPROC glGetString; extern PFNGLVIEWPORTPROC glViewport; //extern PFNGLHINTPROC glHint; //extern PFNGLLINEWIDTHPROC glLineWidth; //extern PFNGLPOINTSIZEPROC glPointSize; //extern PFNGLPOLYGONMODEPROC glPolygonMode; //extern PFNGLTEXPARAMETERFPROC glTexParameterf; //extern PFNGLTEXPARAMETERFVPROC glTexParameterfv; //extern PFNGLTEXPARAMETERIVPROC glTexParameteriv; //extern PFNGLTEXIMAGE1DPROC glTexImage1D; //extern PFNGLSTENCILMASKPROC glStencilMask; //extern PFNGLFINISHPROC glFinish; //extern PFNGLFLUSHPROC glFlush; //extern PFNGLLOGICOPPROC glLogicOp; //extern PFNGLPIXELSTOREFPROC glPixelStoref; //extern PFNGLPIXELSTOREIPROC glPixelStorei; //extern PFNGLREADBUFFERPROC glReadBuffer; //extern PFNGLREADPIXELSPROC glReadPixels; //extern PFNGLGETBOOLEANVPROC glGetBooleanv; //extern PFNGLGETDOUBLEVPROC glGetDoublev; //extern PFNGLGETFLOATVPROC glGetFloatv; //extern PFNGLGETTEXIMAGEPROC glGetTexImage; //extern PFNGLGETTEXPARAMETERFVPROC glGetTexParameterfv; //extern PFNGLGETTEXPARAMETERIVPROC glGetTexParameteriv; //extern PFNGLGETTEXLEVELPARAMETERFVPROC glGetTexLevelParameterfv; //extern PFNGLGETTEXLEVELPARAMETERIVPROC glGetTexLevelParameteriv; //extern PFNGLISENABLEDPROC glIsEnabled; //extern PFNGLDEPTHRANGEPROC glDepthRange; //GL_VERSION_1_1 extern PFNGLDRAWELEMENTSPROC glDrawElements; extern PFNGLBINDTEXTUREPROC glBindTexture; extern PFNGLDELETETEXTURESPROC glDeleteTextures; extern PFNGLGENTEXTURESPROC glGenTextures; //extern PFNGLDRAWARRAYSPROC glDrawArrays; //extern PFNGLGETPOINTERVPROC glGetPointerv; //extern PFNGLPOLYGONOFFSETPROC glPolygonOffset; //extern PFNGLCOPYTEXIMAGE1DPROC glCopyTexImage1D; //extern PFNGLCOPYTEXIMAGE2DPROC glCopyTexImage2D; //extern PFNGLCOPYTEXSUBIMAGE1DPROC glCopyTexSubImage1D; //extern PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D; //extern PFNGLTEXSUBIMAGE1DPROC glTexSubImage1D; //extern PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D; //extern PFNGLISTEXTUREPROC glIsTexture; //GL_VERSION_1_2 //extern PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; //extern PFNGLTEXIMAGE3DPROC glTexImage3D; //extern PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; //extern PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D; //GL_VERSION_1_3 extern PFNGLACTIVETEXTUREPROC glActiveTexture; //extern PFNGLSAMPLECOVERAGEPROC glSampleCoverage; //extern PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D; //extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D; //extern PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D; //extern PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D; //extern PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D; //extern PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D; //extern PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage; //GL_VERSION_1_4 extern PFNGLBLENDEQUATIONPROC glBlendEquation; //extern PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate; //extern PFNGLMULTIDRAWARRAYSPROC glMultiDrawArrays; //extern PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements; //extern PFNGLPOINTPARAMETERFPROC glPointParameterf; //extern PFNGLPOINTPARAMETERFVPROC glPointParameterfv; //extern PFNGLPOINTPARAMETERIPROC glPointParameteri; //extern PFNGLPOINTPARAMETERIVPROC glPointParameteriv; //extern PFNGLBLENDCOLORPROC glBlendColor; //GL_VERSION_1_5 extern PFNGLBINDBUFFERPROC glBindBuffer; extern PFNGLDELETEBUFFERSPROC glDeleteBuffers; extern PFNGLGENBUFFERSPROC glGenBuffers; extern PFNGLBUFFERDATAPROC glBufferData; //extern PFNGLGENQUERIESPROC glGenQueries; //extern PFNGLDELETEQUERIESPROC glDeleteQueries; //extern PFNGLISQUERYPROC glIsQuery; //extern PFNGLBEGINQUERYPROC glBeginQuery; //extern PFNGLENDQUERYPROC glEndQuery; //extern PFNGLGETQUERYIVPROC glGetQueryiv; //extern PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv; //extern PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv; //extern PFNGLISBUFFERPROC glIsBuffer; //extern PFNGLBUFFERSUBDATAPROC glBufferSubData; //extern PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData; //extern PFNGLMAPBUFFERPROC glMapBuffer; //extern PFNGLUNMAPBUFFERPROC glUnmapBuffer; //extern PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv; //extern PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv; // GL_VERSION_2_0 extern PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate; extern PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate; extern PFNGLATTACHSHADERPROC glAttachShader; extern PFNGLCOMPILESHADERPROC glCompileShader; extern PFNGLCREATEPROGRAMPROC glCreateProgram; extern PFNGLCREATESHADERPROC glCreateShader; extern PFNGLDELETEPROGRAMPROC glDeleteProgram; extern PFNGLDELETESHADERPROC glDeleteShader; extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; extern PFNGLGETPROGRAMIVPROC glGetProgramiv; extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; extern PFNGLGETSHADERIVPROC glGetShaderiv; extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; extern PFNGLLINKPROGRAMPROC glLinkProgram; extern PFNGLSHADERSOURCEPROC glShaderSource; extern PFNGLUSEPROGRAMPROC glUseProgram; extern PFNGLUNIFORM1FPROC glUniform1f; extern PFNGLUNIFORM1FVPROC glUniform1fv; extern PFNGLUNIFORM2FVPROC glUniform2fv; extern PFNGLUNIFORM3FVPROC glUniform3fv; extern PFNGLUNIFORM4FVPROC glUniform4fv; extern PFNGLUNIFORM1IVPROC glUniform1iv; extern PFNGLUNIFORM2IVPROC glUniform2iv; extern PFNGLUNIFORM3IVPROC glUniform3iv; extern PFNGLUNIFORM4IVPROC glUniform4iv; // extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; //extern PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate; //extern PFNGLDRAWBUFFERSPROC glDrawBuffers; //extern PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate; //extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; //extern PFNGLDETACHSHADERPROC glDetachShader; //extern PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib; //extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform; //extern PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders; //extern PFNGLGETSHADERSOURCEPROC glGetShaderSource; //extern PFNGLGETUNIFORMFVPROC glGetUniformfv; //extern PFNGLGETUNIFORMIVPROC glGetUniformiv; //extern PFNGLGETVERTEXATTRIBDVPROC glGetVertexAttribdv; //extern PFNGLGETVERTEXATTRIBFVPROC glGetVertexAttribfv; //extern PFNGLGETVERTEXATTRIBIVPROC glGetVertexAttribiv; //extern PFNGLGETVERTEXATTRIBPOINTERVPROC glGetVertexAttribPointerv; //extern PFNGLISPROGRAMPROC glIsProgram; //extern PFNGLISSHADERPROC glIsShader; //extern PFNGLUNIFORM2FPROC glUniform2f; //extern PFNGLUNIFORM3FPROC glUniform3f; //extern PFNGLUNIFORM4FPROC glUniform4f; //extern PFNGLUNIFORM1IPROC glUniform1i; //extern PFNGLUNIFORM2IPROC glUniform2i; //extern PFNGLUNIFORM3IPROC glUniform3i; //extern PFNGLUNIFORM4IPROC glUniform4i; //extern PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv; extern PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv; //extern PFNGLVALIDATEPROGRAMPROC glValidateProgram; //extern PFNGLVERTEXATTRIB1DPROC glVertexAttrib1d; //extern PFNGLVERTEXATTRIB1DVPROC glVertexAttrib1dv; //extern PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f; //extern PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv; //extern PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s; //extern PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv; //extern PFNGLVERTEXATTRIB2DPROC glVertexAttrib2d; //extern PFNGLVERTEXATTRIB2DVPROC glVertexAttrib2dv; //extern PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f; //extern PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv; //extern PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s; //extern PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv; //extern PFNGLVERTEXATTRIB3DPROC glVertexAttrib3d; //extern PFNGLVERTEXATTRIB3DVPROC glVertexAttrib3dv; //extern PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f; //extern PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv; //extern PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s; //extern PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv; //extern PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv; //extern PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv; //extern PFNGLVERTEXATTRIB4NSVPROC glVertexAttrib4Nsv; //extern PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub; //extern PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv; //extern PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv; //extern PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv; //extern PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv; //extern PFNGLVERTEXATTRIB4DPROC glVertexAttrib4d; //extern PFNGLVERTEXATTRIB4DVPROC glVertexAttrib4dv; //extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f; //extern PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv; //extern PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv; //extern PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s; //extern PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv; //extern PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv; //extern PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv; //extern PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv; //GL_VERSION_2_1 //extern PFNGLUNIFORMMATRIX2X3FVPROC glUniformMatrix2x3fv; //extern PFNGLUNIFORMMATRIX3X2FVPROC glUniformMatrix3x2fv; //extern PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv; //extern PFNGLUNIFORMMATRIX4X2FVPROC glUniformMatrix4x2fv; //extern PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fv; //extern PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv; //GL_VERSION_3_0 extern PFNGLBINDBUFFERRANGEPROC glBindBufferRange; extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; extern PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer; extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer; extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample; extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray; extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; //extern PFNGLCOLORMASKIPROC glColorMaski; //extern PFNGLGETBOOLEANI_VPROC glGetBooleani_v; //extern PFNGLGETINTEGERI_VPROC glGetIntegeri_v; //extern PFNGLENABLEIPROC glEnablei; //extern PFNGLDISABLEIPROC glDisablei; //extern PFNGLISENABLEDIPROC glIsEnabledi; //extern PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback; //extern PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback; //extern PFNGLBINDBUFFERBASEPROC glBindBufferBase; //extern PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings; //extern PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glGetTransformFeedbackVarying; //extern PFNGLCLAMPCOLORPROC glClampColor; //extern PFNGLBEGINCONDITIONALRENDERPROC glBeginConditionalRender; //extern PFNGLENDCONDITIONALRENDERPROC glEndConditionalRender; //extern PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer; //extern PFNGLGETVERTEXATTRIBIIVPROC glGetVertexAttribIiv; //extern PFNGLGETVERTEXATTRIBIUIVPROC glGetVertexAttribIuiv; //extern PFNGLVERTEXATTRIBI1IPROC glVertexAttribI1i; //extern PFNGLVERTEXATTRIBI2IPROC glVertexAttribI2i; //extern PFNGLVERTEXATTRIBI3IPROC glVertexAttribI3i; //extern PFNGLVERTEXATTRIBI4IPROC glVertexAttribI4i; //extern PFNGLVERTEXATTRIBI1UIPROC glVertexAttribI1ui; //extern PFNGLVERTEXATTRIBI2UIPROC glVertexAttribI2ui; //extern PFNGLVERTEXATTRIBI3UIPROC glVertexAttribI3ui; //extern PFNGLVERTEXATTRIBI4UIPROC glVertexAttribI4ui; //extern PFNGLVERTEXATTRIBI1IVPROC glVertexAttribI1iv; //extern PFNGLVERTEXATTRIBI2IVPROC glVertexAttribI2iv; //extern PFNGLVERTEXATTRIBI3IVPROC glVertexAttribI3iv; //extern PFNGLVERTEXATTRIBI4IVPROC glVertexAttribI4iv; //extern PFNGLVERTEXATTRIBI1UIVPROC glVertexAttribI1uiv; //extern PFNGLVERTEXATTRIBI2UIVPROC glVertexAttribI2uiv; //extern PFNGLVERTEXATTRIBI3UIVPROC glVertexAttribI3uiv; //extern PFNGLVERTEXATTRIBI4UIVPROC glVertexAttribI4uiv; //extern PFNGLVERTEXATTRIBI4BVPROC glVertexAttribI4bv; //extern PFNGLVERTEXATTRIBI4SVPROC glVertexAttribI4sv; //extern PFNGLVERTEXATTRIBI4UBVPROC glVertexAttribI4ubv; //extern PFNGLVERTEXATTRIBI4USVPROC glVertexAttribI4usv; //extern PFNGLGETUNIFORMUIVPROC glGetUniformuiv; //extern PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation; //extern PFNGLGETFRAGDATALOCATIONPROC glGetFragDataLocation; //extern PFNGLUNIFORM1UIPROC glUniform1ui; //extern PFNGLUNIFORM2UIPROC glUniform2ui; //extern PFNGLUNIFORM3UIPROC glUniform3ui; //extern PFNGLUNIFORM4UIPROC glUniform4ui; //extern PFNGLUNIFORM1UIVPROC glUniform1uiv; //extern PFNGLUNIFORM2UIVPROC glUniform2uiv; //extern PFNGLUNIFORM3UIVPROC glUniform3uiv; //extern PFNGLUNIFORM4UIVPROC glUniform4uiv; //extern PFNGLTEXPARAMETERIIVPROC glTexParameterIiv; //extern PFNGLTEXPARAMETERIUIVPROC glTexParameterIuiv; //extern PFNGLGETTEXPARAMETERIIVPROC glGetTexParameterIiv; //extern PFNGLGETTEXPARAMETERIUIVPROC glGetTexParameterIuiv; //extern PFNGLCLEARBUFFERIVPROC glClearBufferiv; //extern PFNGLCLEARBUFFERUIVPROC glClearBufferuiv; //extern PFNGLCLEARBUFFERFVPROC glClearBufferfv; //extern PFNGLCLEARBUFFERFIPROC glClearBufferfi; //extern PFNGLGETSTRINGIPROC glGetStringi; //extern PFNGLISRENDERBUFFERPROC glIsRenderbuffer; //extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; //extern PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv; //extern PFNGLISFRAMEBUFFERPROC glIsFramebuffer; //extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; //extern PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D; //extern PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D; //extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv; //extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap; //extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; //extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange; //extern PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange; //extern PFNGLISVERTEXARRAYPROC glIsVertexArray; //GL_VERSION_3_1 extern PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex; extern PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding; //extern PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced; //extern PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced; //extern PFNGLTEXBUFFERPROC glTexBuffer; //extern PFNGLPRIMITIVERESTARTINDEXPROC glPrimitiveRestartIndex; //extern PFNGLCOPYBUFFERSUBDATAPROC glCopyBufferSubData; //extern PFNGLGETUNIFORMINDICESPROC glGetUniformIndices; //extern PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv; //extern PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName; //extern PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv; //extern PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName; #if defined(_WIN32) && !defined(__CYGWIN__) && defined(THORVG_GL_TARGET_GL) extern PFNWGLGETCURRENTCONTEXTPROC tvgWglGetCurrentContext; extern PFNWGLMAKECURRENTPROC tvgWglMakeCurrent; #endif #if defined(THORVG_GL_TARGET_GLES) extern PFNEGLGETCURRENTCONTEXTPROC tvgEglGetCurrentContext; extern PFNEGLMAKECURRENTPROC tvgEglMakeCurrent; #endif #endif // __EMSCRIPTEN__ bool glInit(); bool glTerm(); #endif // _TVG_GL_H_loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-undefs.inc.h000664 001750 001750 00000001637 15164251010 052643 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ #undef SIMPLE_VALUE #undef NUMBER_VALUE #undef STRING_VALUE #undef SYMBOL_VALUE #undef INTRINSIC_PROPERTY #undef ACCESSOR_BUILTIN_FUNCTION_OBJECT #undef OBJECT_VALUE #undef ROUTINE #undef ROUTINE_CONFIGURABLE_ONLY #undef ROUTINE_WITH_FLAGS #undef ACCESSOR_READ_WRITE #undef ACCESSOR_READ_ONLY jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int16array-prototype.inc.h000664 001750 001750 00000001616 15164251010 055044 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Int16Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 2 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_INT16ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.inc.h000664 001750 001750 00000011112 15164251010 055220 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %TypedArrayPrototype% description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_TYPEDARRAY /* ES2015 22.2.3.4 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_TYPEDARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* Readonly accessor properties */ /* ES2015 22.2.3.1 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BUFFER, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BUFFER_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ES2015 22.2.3.2 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BYTE_LENGTH_UL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BYTELENGTH_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ES2015 22.2.3.3 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BYTE_OFFSET_UL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BYTEOFFSET_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ES2015 22.2.3.17 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_LENGTH, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_LENGTH_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 23.1.3.13 */ ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_TO_STRING_TAG_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_COPY_WITHIN, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_COPY_WITHIN, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_ENTRIES, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_ENTRIES, 0, 0) ROUTINE (LIT_MAGIC_STRING_EVERY, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_EVERY, 2, 1) ROUTINE (LIT_MAGIC_STRING_FILL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FILL, 3, 1) ROUTINE (LIT_MAGIC_STRING_FILTER, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FILTER, 2, 1) ROUTINE (LIT_MAGIC_STRING_FIND, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FIND, 2, 1) ROUTINE (LIT_MAGIC_STRING_FIND_INDEX, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FIND_INDEX, 2, 1) ROUTINE (LIT_MAGIC_STRING_FOR_EACH_UL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FOR_EACH, 2, 1) ROUTINE (LIT_MAGIC_STRING_INCLUDES, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_INCLUDES, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_AT, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_AT, 1, 1) ROUTINE (LIT_MAGIC_STRING_INDEX_OF_UL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_INDEX_OF, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_JOIN, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_JOIN, 1, 1) ROUTINE (LIT_MAGIC_STRING_KEYS, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_KEYS, 0, 0) ROUTINE (LIT_MAGIC_STRING_LAST_INDEX_OF_UL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_LAST_INDEX_OF, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_MAP, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_MAP, 2, 1) ROUTINE (LIT_MAGIC_STRING_REDUCE, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REDUCE, 2, 1) ROUTINE (LIT_MAGIC_STRING_REDUCE_RIGHT_UL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REDUCE_RIGHT, 2, 1) ROUTINE (LIT_MAGIC_STRING_REVERSE, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REVERSE, 0, 0) ROUTINE (LIT_MAGIC_STRING_SET, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SET, 2, 1) ROUTINE (LIT_MAGIC_STRING_SLICE, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SLICE, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_SOME, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SOME, 2, 1) ROUTINE (LIT_MAGIC_STRING_SORT, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SORT, 1, 1) ROUTINE (LIT_MAGIC_STRING_SUBARRAY, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SUBARRAY, 2, 2) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_TO_LOCALE_STRING, 0, 0) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TO_STRING_UL, LIT_MAGIC_STRING_TO_STRING_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_VALUES, LIT_INTERNAL_MAGIC_STRING_TYPEDARRAY_PROTOTYPE_VALUES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_GLOBAL_SYMBOL_ITERATOR, LIT_INTERNAL_MAGIC_STRING_TYPEDARRAY_PROTOTYPE_VALUES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_TYPEDARRAY */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-shared-arraybuffer.inc.h000664 001750 001750 00000003166 15164251010 052373 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * SharedArrayBuffer built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_SHAREDARRAYBUFFER /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_SHARED_ARRAY_BUFFER_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ /* ES11 24.1.3.3 */ ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_SPECIES, ecma_builtin_shared_arraybuffer_species_get, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-number-object.cpp000664 001750 001750 00000005201 15164251010 046550 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-number-object.h" #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmanumberobject ECMA Number object related routines * @{ */ /** * Number object creation operation. * * See also: ECMA-262 v5, 15.7.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_number_object (ecma_value_t arg) /**< argument passed to the Number constructor */ { ecma_number_t num; ecma_value_t conv_to_num_completion = ecma_op_to_number (arg, &num); if (ECMA_IS_VALUE_ERROR (conv_to_num_completion)) { return conv_to_num_completion; } conv_to_num_completion = ecma_make_number_value (num); ecma_builtin_id_t proto_id; #if JERRY_BUILTIN_NUMBER proto_id = ECMA_BUILTIN_ID_NUMBER_PROTOTYPE; #else /* JERRY_BUILTIN_NUMBER */ proto_id = ECMA_BUILTIN_ID_OBJECT_PROTOTYPE; #endif /* JERRY_BUILTIN_NUMBER */ ecma_object_t *prototype_obj_p = ecma_builtin_get (proto_id); ecma_object_t *new_target = JERRY_CONTEXT (current_new_target_p); if (new_target) { prototype_obj_p = ecma_op_get_prototype_from_constructor (new_target, proto_id); if (JERRY_UNLIKELY (prototype_obj_p == NULL)) { return ECMA_VALUE_ERROR; } } ecma_object_t *object_p = ecma_create_object (prototype_obj_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_NUMBER; /* Pass reference (no need to free conv_to_num_completion). */ ext_object_p->u.cls.u3.value = conv_to_num_completion; if (new_target) { ecma_deref_object (prototype_obj_p); } return ecma_make_object_value (object_p); } /* ecma_op_create_number_object */ /** * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-extended-info.cpp000664 001750 001750 00000006413 15164251010 045302 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-extended-info.h" #include "ecma-helpers.h" #include "byte-code.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaextendedinfo Extended info * @{ */ /** * Decodes an uint32_t number, and updates the buffer position. * * @return the decoded value */ uint32_t ecma_extended_info_decode_vlq (uint8_t **buffer_p) /**< [in/out] target buffer */ { uint8_t *source_p = *buffer_p; uint32_t value = 0; do { source_p--; value = (value << ECMA_EXTENDED_INFO_VLQ_SHIFT) | (*source_p & ECMA_EXTENDED_INFO_VLQ_MASK); } while (*source_p & ECMA_EXTENDED_INFO_VLQ_CONTINUE); *buffer_p = source_p; return value; } /* ecma_extended_info_decode_vlq */ /** * Encodes an uint32_t number into a buffer. */ void ecma_extended_info_encode_vlq (uint8_t **buffer_p, /**< target buffer */ uint32_t value) /**< encoded value */ { uint8_t *destination_p = *buffer_p - 1; if (value <= ECMA_EXTENDED_INFO_VLQ_MASK) { *destination_p = (uint8_t) value; *buffer_p = destination_p; return; } uint32_t length = 0; uint32_t current_value = value >> ECMA_EXTENDED_INFO_VLQ_SHIFT; do { current_value >>= ECMA_EXTENDED_INFO_VLQ_SHIFT; length++; } while (current_value > 0); destination_p -= length; *buffer_p = destination_p; do { *destination_p++ = (uint8_t) (value | ECMA_EXTENDED_INFO_VLQ_CONTINUE); value >>= ECMA_EXTENDED_INFO_VLQ_SHIFT; } while (value > 0); **buffer_p &= ECMA_EXTENDED_INFO_VLQ_MASK; } /* ecma_extended_info_encode_vlq */ /** * Gets the encoded length of a number. * * @return encoded length */ uint32_t ecma_extended_info_get_encoded_length (uint32_t value) /**< encoded value */ { uint32_t length = 0; do { value >>= ECMA_EXTENDED_INFO_VLQ_SHIFT; length++; } while (value > 0); return length; } /* ecma_extended_info_get_encoded_length */ /** * Get the extended info from a byte code * * @return pointer to the extended info */ uint8_t * ecma_compiled_code_resolve_extended_info (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */ { JERRY_ASSERT (bytecode_header_p != NULL); JERRY_ASSERT (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO); ecma_value_t *base_p = ecma_compiled_code_resolve_arguments_start (bytecode_header_p); if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR) { base_p--; } if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS) { base_p--; } JERRY_ASSERT (((uint8_t *) base_p)[-1] != 0); return ((uint8_t *) base_p) - 1; } /* ecma_compiled_code_resolve_extended_info */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/bindings/capi/meson.build000664 001750 001750 00000000375 15164251010 034664 0ustar00ddennedyddennedy000000 000000 source_file = [ 'tvgCapi.cpp', ] thorvg_lib_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] install_headers('thorvg_capi.h', subdir: 'thorvg-' + vmaj) headers += include_directories('.') glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/byte-code.h000664 001750 001750 00000244736 15164251010 043273 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 BYTE_CODE_H #define BYTE_CODE_H #include "ecma-globals.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_bytecode Bytecode * @{ */ /** * Compact byte code (CBC) is a byte code representation * of EcmaScript which is designed for low memory * environments. Most opcodes are only one or sometimes * two byte long so the CBC provides a small binary size. * * The execution engine of CBC is a stack machine, where * the maximum stack size is known in advance for each * function. */ /** * Byte code flags. Only the lower 5 bit can be used * since the stack change is encoded in the upper * three bits for each instruction between -4 and 3 * (except for call / construct opcodes). */ #define CBC_STACK_ADJUST_BASE 4 #define CBC_STACK_ADJUST_SHIFT 5 #define CBC_STACK_ADJUST_VALUE(value) (((value) >> CBC_STACK_ADJUST_SHIFT) - CBC_STACK_ADJUST_BASE) #define CBC_NO_FLAG 0x00u #define CBC_HAS_LITERAL_ARG 0x01u #define CBC_HAS_LITERAL_ARG2 0x02u #define CBC_HAS_BYTE_ARG 0x04u #define CBC_HAS_BRANCH_ARG 0x08u /* These flags are shared */ #define CBC_FORWARD_BRANCH_ARG 0x10u #define CBC_POP_STACK_BYTE_ARG 0x10u #define CBC_ARG_TYPES (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2 | CBC_HAS_BYTE_ARG | CBC_HAS_BRANCH_ARG) #define CBC_HAS_POP_STACK_BYTE_ARG (CBC_HAS_BYTE_ARG | CBC_POP_STACK_BYTE_ARG) /** * CBC_NO_RESULT_OPERATION for ext opcodes */ #define CBC_EXT_NO_RESULT_OPERATION(opcode) \ ((opcode) >= PARSER_TO_EXT_OPCODE (CBC_EXT_ASSIGN_SUPER) \ && (opcode) <= PARSER_TO_EXT_OPCODE (CBC_EXT_SPREAD_CALL_PROP_BLOCK)) /* Debug macro. */ #define CBC_ARGS_EQ(op, types) ((cbc_flags[op] & CBC_ARG_TYPES) == (types)) /* Debug macro. */ #define CBC_SAME_ARGS(op1, op2) \ (CBC_EXT_NO_RESULT_OPERATION (op1) ? ((cbc_ext_flags[PARSER_GET_EXT_OPCODE (op1)] & CBC_ARG_TYPES) \ == (cbc_ext_flags[PARSER_GET_EXT_OPCODE (op2)] & CBC_ARG_TYPES)) \ : ((cbc_flags[op1] & CBC_ARG_TYPES) == (cbc_flags[op2] & CBC_ARG_TYPES))) #define CBC_UNARY_OPERATION(name, group) \ CBC_OPCODE (name, CBC_NO_FLAG, 0, (VM_OC_##group) | VM_OC_GET_STACK | VM_OC_PUT_STACK) \ CBC_OPCODE (name##_LITERAL, CBC_HAS_LITERAL_ARG, 1, (VM_OC_##group) | VM_OC_GET_LITERAL | VM_OC_PUT_STACK) #define CBC_BINARY_OPERATION(name, group) \ CBC_OPCODE (name, CBC_NO_FLAG, -1, (VM_OC_##group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \ CBC_OPCODE (name##_RIGHT_LITERAL, \ CBC_HAS_LITERAL_ARG, \ 0, \ (VM_OC_##group) | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (name##_TWO_LITERALS, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 1, \ (VM_OC_##group) | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) #define CBC_UNARY_LVALUE_OPERATION(name, group) \ CBC_OPCODE (name, CBC_NO_FLAG, -2, (VM_OC_PROP_##group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE) \ CBC_OPCODE (name##_PUSH_RESULT, \ CBC_NO_FLAG, \ -1, \ (VM_OC_PROP_##group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ CBC_OPCODE (name##_BLOCK, \ CBC_NO_FLAG, \ -2, \ (VM_OC_PROP_##group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ CBC_OPCODE (name##_IDENT, CBC_HAS_LITERAL_ARG, 0, (VM_OC_##group) | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT) \ CBC_OPCODE (name##_IDENT_PUSH_RESULT, \ CBC_HAS_LITERAL_ARG, \ 1, \ (VM_OC_##group) | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT | VM_OC_PUT_STACK) \ CBC_OPCODE (name##_IDENT_BLOCK, \ CBC_HAS_LITERAL_ARG, \ 0, \ (VM_OC_##group) | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT | VM_OC_PUT_BLOCK) #define CBC_UNARY_LVALUE_WITH_IDENT 3 #define CBC_BINARY_WITH_LITERAL 1 #define CBC_BINARY_WITH_TWO_LITERALS 2 /** * Several opcodes (mostly call and assignment opcodes) have * two forms: one which does not push a return value onto * the stack, and another which does. The reason is that * the return value of these opcodes are often not used * and the first form provides smaller byte code. * * The following rules must be kept by the code generator: * - only the opcode without return value can be emitted * by the code generator * - the first form can be converted to the second form * by adding 1 to the opcode * - after the conversion the opcode must be immediately * flushed, so no further changes are possible * * Hence CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode) * cannot be true for an opcode which has a result */ #define CBC_NO_RESULT_OPERATION(opcode) \ (((opcode) >= CBC_PRE_INCR && (opcode) < CBC_END) || CBC_EXT_NO_RESULT_OPERATION ((opcode))) /** * Branch instructions are organized in group of 8 opcodes. * - 1st opcode: unused, can be used for other purpose * - 2nd opcode: forward branch with 1 byte offset * - 3rd opcode: forward branch with 2 byte offset * - 4th opcode: forward branch with 3 byte offset * - 5th opcode: unused, can be used for other purpose * - 6th opcode: backward branch with 1 byte offset * - 7th opcode: backward branch with 2 byte offset * - 8th opcode: backward branch with 3 byte offset * * Reasons: * The branch_opcode & 0x3 tells the length in bytes of the offset * If branch offset & 0x4 == 0, it is a forward branch. Otherwise * it is backward. * * The offset bytes are encoded in higher to lower order. */ #define CBC_FORWARD_BRANCH(name, stack, vm_oc) \ CBC_OPCODE (name, CBC_HAS_BRANCH_ARG | CBC_FORWARD_BRANCH_ARG, stack, (vm_oc) | VM_OC_GET_BRANCH) \ CBC_OPCODE (name##_2, CBC_HAS_BRANCH_ARG | CBC_FORWARD_BRANCH_ARG, stack, (vm_oc) | VM_OC_GET_BRANCH) \ CBC_OPCODE (name##_3, CBC_HAS_BRANCH_ARG | CBC_FORWARD_BRANCH_ARG, stack, (vm_oc) | VM_OC_GET_BRANCH) #define CBC_BACKWARD_BRANCH(name, stack, vm_oc) \ CBC_OPCODE (name, CBC_HAS_BRANCH_ARG, stack, (vm_oc) | VM_OC_GET_BRANCH | VM_OC_BACKWARD_BRANCH) \ CBC_OPCODE (name##_2, CBC_HAS_BRANCH_ARG, stack, (vm_oc) | VM_OC_GET_BRANCH | VM_OC_BACKWARD_BRANCH) \ CBC_OPCODE (name##_3, CBC_HAS_BRANCH_ARG, stack, (vm_oc) | VM_OC_GET_BRANCH | VM_OC_BACKWARD_BRANCH) #define CBC_BRANCH_OFFSET_LENGTH(opcode) ((opcode) &0x3) #define CBC_BRANCH_IS_BACKWARD(flags) (!((flags) &CBC_FORWARD_BRANCH_ARG)) #define CBC_BRANCH_IS_FORWARD(flags) ((flags) &CBC_FORWARD_BRANCH_ARG) /* Stack consumption of opcodes with context. */ /* PARSER_TRY_CONTEXT_STACK_ALLOCATION must be <= 3 */ #define PARSER_TRY_CONTEXT_STACK_ALLOCATION 1 /* PARSER_FINALLY_CONTEXT_STACK_ALLOCATION must be <= 3 */ #define PARSER_FINALLY_CONTEXT_STACK_ALLOCATION 2 /* PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION 4 /* PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION 4 /* PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION 4 /* PARSER_WITH_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_WITH_CONTEXT_STACK_ALLOCATION 1 /* PARSER_BLOCK_CONTEXT_STACK_ALLOCATION must be <= 3 */ #define PARSER_BLOCK_CONTEXT_STACK_ALLOCATION 1 /* PARSER_ITERATOR_CONTEXT_STACK_ALLOCATION must be <= 3 */ #define PARSER_ITERATOR_CONTEXT_STACK_ALLOCATION 3 /* PARSER_OBJECT_INITIALIZER_CONTEXT_STACK_ALLOCATION must be <= 2 */ #define PARSER_OBJ_INIT_CONTEXT_STACK_ALLOCATION 1 /* PARSER_OBJECT_INITIALIZER_CONTEXT_STACK_ALLOCATION must be <= 2 */ #define PARSER_OBJ_INIT_REST_CONTEXT_STACK_ALLOCATION 2 /** * Extra stack consumption for finally context. */ #define PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION \ (PARSER_FINALLY_CONTEXT_STACK_ALLOCATION - PARSER_TRY_CONTEXT_STACK_ALLOCATION) #define PARSER_STATIC_PRIVATE_TO_PRIVATE_OFFSET (CBC_EXT_COLLECT_PRIVATE_STATIC_FIELD - CBC_EXT_COLLECT_PRIVATE_FIELD) /** * Opcode definitions. */ #define CBC_OPCODE_LIST \ /* Branch opcodes first. Some other opcodes are mixed. */ \ CBC_OPCODE (CBC_EXT_OPCODE, CBC_NO_FLAG, 0, VM_OC_NONE) \ CBC_FORWARD_BRANCH (CBC_JUMP_FORWARD, 0, VM_OC_JUMP) \ CBC_OPCODE (CBC_POP, CBC_NO_FLAG, -1, VM_OC_POP) \ CBC_BACKWARD_BRANCH (CBC_JUMP_BACKWARD, 0, VM_OC_JUMP) \ CBC_OPCODE (CBC_POP_BLOCK, CBC_NO_FLAG, -1, VM_OC_POP_BLOCK | VM_OC_PUT_BLOCK) \ CBC_FORWARD_BRANCH (CBC_BRANCH_IF_TRUE_FORWARD, -1, VM_OC_BRANCH_IF_TRUE) \ CBC_OPCODE (CBC_THROW, CBC_NO_FLAG, -1, VM_OC_THROW | VM_OC_GET_STACK) \ CBC_BACKWARD_BRANCH (CBC_BRANCH_IF_TRUE_BACKWARD, -1, VM_OC_BRANCH_IF_TRUE) \ CBC_OPCODE (CBC_CONTEXT_END, CBC_NO_FLAG, 0, VM_OC_CONTEXT_END) \ CBC_FORWARD_BRANCH (CBC_BRANCH_IF_FALSE_FORWARD, -1, VM_OC_BRANCH_IF_FALSE) \ CBC_OPCODE (CBC_CREATE_OBJECT, CBC_NO_FLAG, 1, VM_OC_PUSH_OBJECT | VM_OC_PUT_STACK) \ CBC_BACKWARD_BRANCH (CBC_BRANCH_IF_FALSE_BACKWARD, -1, VM_OC_BRANCH_IF_FALSE) \ CBC_OPCODE (CBC_SET_PROPERTY, \ CBC_HAS_LITERAL_ARG, \ -1, \ VM_OC_SET_PROPERTY | VM_OC_NON_STATIC_FLAG | VM_OC_GET_STACK_LITERAL) \ CBC_FORWARD_BRANCH (CBC_JUMP_FORWARD_EXIT_CONTEXT, 0, VM_OC_JUMP_AND_EXIT_CONTEXT) \ CBC_OPCODE (CBC_CREATE_ARRAY, CBC_NO_FLAG, 1, VM_OC_PUSH_ARRAY | VM_OC_PUT_STACK) \ CBC_FORWARD_BRANCH (CBC_BRANCH_IF_LOGICAL_TRUE, -1, VM_OC_BRANCH_IF_LOGICAL_TRUE) \ CBC_OPCODE (CBC_ARRAY_APPEND, CBC_HAS_POP_STACK_BYTE_ARG, 0, VM_OC_APPEND_ARRAY) \ CBC_FORWARD_BRANCH (CBC_BRANCH_IF_LOGICAL_FALSE, -1, VM_OC_BRANCH_IF_LOGICAL_FALSE) \ CBC_OPCODE (CBC_PUSH_ELISION, CBC_NO_FLAG, 1, VM_OC_PUSH_ELISON | VM_OC_PUT_STACK) \ CBC_FORWARD_BRANCH (CBC_BRANCH_IF_STRICT_EQUAL, -1, VM_OC_BRANCH_IF_STRICT_EQUAL) \ CBC_OPCODE (CBC_PUSH_NULL, CBC_NO_FLAG, 1, VM_OC_PUSH_NULL | VM_OC_PUT_STACK) \ CBC_FORWARD_BRANCH (CBC_BLOCK_CREATE_CONTEXT, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION, VM_OC_BLOCK_CREATE_CONTEXT) \ \ /* Basic opcodes. Note: These 4 opcodes must me in this order */ \ CBC_OPCODE (CBC_PUSH_LITERAL, CBC_HAS_LITERAL_ARG, 1, VM_OC_PUSH | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_PUSH_TWO_LITERALS, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 2, \ VM_OC_PUSH_TWO | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_PUSH_THIS_LITERAL, CBC_HAS_LITERAL_ARG, 2, VM_OC_PUSH_TWO | VM_OC_GET_THIS_LITERAL) \ CBC_OPCODE (CBC_PUSH_THREE_LITERALS, CBC_HAS_LITERAL_ARG2, 3, VM_OC_PUSH_THREE | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_PUSH_UNDEFINED, CBC_NO_FLAG, 1, VM_OC_PUSH_UNDEFINED | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_TRUE, CBC_NO_FLAG, 1, VM_OC_PUSH_TRUE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_FALSE, CBC_NO_FLAG, 1, VM_OC_PUSH_FALSE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_THIS, CBC_NO_FLAG, 1, VM_OC_PUSH_THIS | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_NUMBER_0, CBC_NO_FLAG, 1, VM_OC_PUSH_0 | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_NUMBER_POS_BYTE, CBC_HAS_BYTE_ARG, 1, VM_OC_PUSH_POS_BYTE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_NUMBER_NEG_BYTE, CBC_HAS_BYTE_ARG, 1, VM_OC_PUSH_NEG_BYTE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_LITERAL_PUSH_NUMBER_0, CBC_HAS_LITERAL_ARG, 2, VM_OC_PUSH_LIT_0 | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE, \ CBC_HAS_LITERAL_ARG | CBC_HAS_BYTE_ARG, \ 2, \ VM_OC_PUSH_LIT_POS_BYTE | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE, \ CBC_HAS_LITERAL_ARG | CBC_HAS_BYTE_ARG, \ 2, \ VM_OC_PUSH_LIT_NEG_BYTE | VM_OC_GET_LITERAL) \ /* Note: These 4 opcodes must me in this order */ \ CBC_OPCODE (CBC_PUSH_PROP, CBC_NO_FLAG, -1, VM_OC_PROP_GET | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_PROP_LITERAL, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_PROP_GET | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_PROP_LITERAL_LITERAL, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 1, \ VM_OC_PROP_GET | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_PROP_THIS_LITERAL, \ CBC_HAS_LITERAL_ARG, \ 1, \ VM_OC_PROP_GET | VM_OC_GET_THIS_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_IDENT_REFERENCE, CBC_HAS_LITERAL_ARG, 3, VM_OC_IDENT_REFERENCE | VM_OC_PUT_STACK) \ /* Note: These 4 opcodes must me in this order */ \ CBC_OPCODE (CBC_PUSH_PROP_REFERENCE, CBC_NO_FLAG, 1, VM_OC_PROP_REFERENCE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_PROP_LITERAL_REFERENCE, \ CBC_HAS_LITERAL_ARG, \ 2, \ VM_OC_PROP_REFERENCE | VM_OC_GET_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 3, \ VM_OC_PROP_REFERENCE | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_PROP_THIS_LITERAL_REFERENCE, \ CBC_HAS_LITERAL_ARG, \ 3, \ VM_OC_PROP_REFERENCE | VM_OC_GET_THIS_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_NEW, CBC_HAS_POP_STACK_BYTE_ARG, 0, VM_OC_NEW | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_NEW0, CBC_NO_FLAG, 0, VM_OC_NEW | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_NEW1, CBC_NO_FLAG, -1, VM_OC_NEW | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EVAL, CBC_NO_FLAG, 0, VM_OC_EVAL) \ CBC_OPCODE (CBC_CHECK_VAR, CBC_HAS_LITERAL_ARG, 0, VM_OC_CHECK_VAR) \ CBC_OPCODE (CBC_CHECK_LET, CBC_HAS_LITERAL_ARG, 0, VM_OC_CHECK_LET) \ CBC_OPCODE (CBC_CREATE_VAR, CBC_HAS_LITERAL_ARG, 0, VM_OC_CREATE_BINDING) \ CBC_OPCODE (CBC_CREATE_LET, CBC_HAS_LITERAL_ARG, 0, VM_OC_CREATE_BINDING) \ CBC_OPCODE (CBC_CREATE_CONST, CBC_HAS_LITERAL_ARG, 0, VM_OC_CREATE_BINDING) \ CBC_OPCODE (CBC_CREATE_LOCAL, CBC_HAS_LITERAL_ARG, 0, VM_OC_CREATE_BINDING) \ CBC_OPCODE (CBC_INIT_ARG_OR_CATCH, CBC_HAS_LITERAL_ARG, -1, VM_OC_INIT_BINDING) \ CBC_OPCODE (CBC_INIT_LET, CBC_HAS_LITERAL_ARG, -1, VM_OC_INIT_BINDING) \ CBC_OPCODE (CBC_INIT_CONST, CBC_HAS_LITERAL_ARG, -1, VM_OC_INIT_BINDING) \ CBC_OPCODE (CBC_INIT_ARG_OR_FUNC, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, VM_OC_INIT_ARG_OR_FUNC) \ CBC_OPCODE (CBC_CREATE_VAR_EVAL, CBC_HAS_LITERAL_ARG, 0, VM_OC_VAR_EVAL) \ CBC_OPCODE (CBC_CREATE_VAR_FUNC_EVAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, VM_OC_VAR_EVAL) \ CBC_OPCODE (CBC_SET_VAR_FUNC, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT) \ CBC_OPCODE (CBC_SET_BYTECODE_PTR, CBC_NO_FLAG, 0, VM_OC_SET_BYTECODE_PTR) \ CBC_OPCODE (CBC_RETURN, CBC_NO_FLAG, -1, VM_OC_RETURN | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_RETURN_FUNCTION_END, CBC_NO_FLAG, 0, VM_OC_RETURN_FUNCTION_END) \ CBC_OPCODE (CBC_RETURN_WITH_LITERAL, CBC_HAS_LITERAL_ARG, 0, VM_OC_RETURN | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_SET_LITERAL_PROPERTY, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_SET_PROPERTY | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_COPY_TO_GLOBAL, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_COPY_TO_GLOBAL | VM_OC_GET_LITERAL) \ \ /* Unary opcodes. */ \ CBC_UNARY_OPERATION (CBC_PLUS, PLUS) \ CBC_UNARY_OPERATION (CBC_NEGATE, MINUS) \ CBC_UNARY_OPERATION (CBC_LOGICAL_NOT, NOT) \ CBC_UNARY_OPERATION (CBC_BIT_NOT, BIT_NOT) \ CBC_UNARY_OPERATION (CBC_VOID, VOID) \ CBC_OPCODE (CBC_TYPEOF, CBC_NO_FLAG, 0, VM_OC_TYPEOF | VM_OC_GET_STACK | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_TYPEOF_IDENT, CBC_HAS_LITERAL_ARG, 1, VM_OC_TYPEOF_IDENT | VM_OC_PUT_STACK) \ \ /* Binary opcodes. */ \ CBC_BINARY_OPERATION (CBC_BIT_OR, BIT_OR) \ CBC_BINARY_OPERATION (CBC_BIT_XOR, BIT_XOR) \ CBC_BINARY_OPERATION (CBC_BIT_AND, BIT_AND) \ CBC_BINARY_OPERATION (CBC_EQUAL, EQUAL) \ CBC_BINARY_OPERATION (CBC_NOT_EQUAL, NOT_EQUAL) \ CBC_BINARY_OPERATION (CBC_STRICT_EQUAL, STRICT_EQUAL) \ CBC_BINARY_OPERATION (CBC_STRICT_NOT_EQUAL, STRICT_NOT_EQUAL) \ CBC_BINARY_OPERATION (CBC_LESS, LESS) \ CBC_BINARY_OPERATION (CBC_GREATER, GREATER) \ CBC_BINARY_OPERATION (CBC_LESS_EQUAL, LESS_EQUAL) \ CBC_BINARY_OPERATION (CBC_GREATER_EQUAL, GREATER_EQUAL) \ CBC_BINARY_OPERATION (CBC_IN, IN) \ CBC_BINARY_OPERATION (CBC_INSTANCEOF, INSTANCEOF) \ CBC_BINARY_OPERATION (CBC_LEFT_SHIFT, LEFT_SHIFT) \ CBC_BINARY_OPERATION (CBC_RIGHT_SHIFT, RIGHT_SHIFT) \ CBC_BINARY_OPERATION (CBC_UNS_RIGHT_SHIFT, UNS_RIGHT_SHIFT) \ CBC_BINARY_OPERATION (CBC_ADD, ADD) \ CBC_BINARY_OPERATION (CBC_SUBTRACT, SUB) \ CBC_BINARY_OPERATION (CBC_MULTIPLY, MUL) \ CBC_BINARY_OPERATION (CBC_DIVIDE, DIV) \ CBC_BINARY_OPERATION (CBC_MODULO, MOD) \ CBC_BINARY_OPERATION (CBC_EXPONENTIATION, EXP) \ \ /* Unary lvalue opcodes. */ \ CBC_OPCODE (CBC_DELETE_PUSH_RESULT, CBC_NO_FLAG, -1, VM_OC_PROP_DELETE | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_DELETE_IDENT_PUSH_RESULT, CBC_HAS_LITERAL_ARG, 1, VM_OC_DELETE | VM_OC_PUT_STACK) \ CBC_UNARY_LVALUE_OPERATION (CBC_PRE_INCR, PRE_INCR) \ CBC_UNARY_LVALUE_OPERATION (CBC_PRE_DECR, PRE_DECR) \ CBC_UNARY_LVALUE_OPERATION (CBC_POST_INCR, POST_INCR) \ CBC_UNARY_LVALUE_OPERATION (CBC_POST_DECR, POST_DECR) \ \ /* Call opcodes. */ \ CBC_OPCODE (CBC_CALL, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_CALL) \ CBC_OPCODE (CBC_CALL_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, 0, VM_OC_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_CALL | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_CALL_PROP, CBC_HAS_POP_STACK_BYTE_ARG, -3, VM_OC_CALL) \ CBC_OPCODE (CBC_CALL_PROP_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, -2, VM_OC_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_CALL_PROP_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -3, VM_OC_CALL | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_CALL0, CBC_NO_FLAG, -1, VM_OC_CALL) \ CBC_OPCODE (CBC_CALL0_PUSH_RESULT, CBC_NO_FLAG, 0, VM_OC_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_CALL0_BLOCK, CBC_NO_FLAG, -1, VM_OC_CALL | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_CALL0_PROP, CBC_NO_FLAG, -3, VM_OC_CALL) \ CBC_OPCODE (CBC_CALL0_PROP_PUSH_RESULT, CBC_NO_FLAG, -2, VM_OC_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_CALL0_PROP_BLOCK, CBC_NO_FLAG, -3, VM_OC_CALL | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_CALL1, CBC_NO_FLAG, -2, VM_OC_CALL) \ CBC_OPCODE (CBC_CALL1_PUSH_RESULT, CBC_NO_FLAG, -1, VM_OC_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_CALL1_BLOCK, CBC_NO_FLAG, -2, VM_OC_CALL | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_CALL1_PROP, CBC_NO_FLAG, -4, VM_OC_CALL) \ CBC_OPCODE (CBC_CALL1_PROP_PUSH_RESULT, CBC_NO_FLAG, -3, VM_OC_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_CALL1_PROP_BLOCK, CBC_NO_FLAG, -4, VM_OC_CALL | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_CALL2, CBC_NO_FLAG, -3, VM_OC_CALL) \ CBC_OPCODE (CBC_CALL2_PUSH_RESULT, CBC_NO_FLAG, -2, VM_OC_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_CALL2_BLOCK, CBC_NO_FLAG, -3, VM_OC_CALL | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_CALL2_PROP, CBC_NO_FLAG, -4, VM_OC_CALL) \ CBC_OPCODE (CBC_CALL2_PROP_PUSH_RESULT, CBC_NO_FLAG, -3, VM_OC_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_CALL2_PROP_BLOCK, CBC_NO_FLAG, -4, VM_OC_CALL | VM_OC_PUT_BLOCK) \ \ /* Binary assignment opcodes. */ \ CBC_OPCODE (CBC_ASSIGN, CBC_NO_FLAG, -3, VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_REFERENCE) \ CBC_OPCODE (CBC_ASSIGN_PUSH_RESULT, \ CBC_NO_FLAG, \ -2, \ VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_ASSIGN_BLOCK, \ CBC_NO_FLAG, \ -3, \ VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_ASSIGN_SET_IDENT, CBC_HAS_LITERAL_ARG, -1, VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_IDENT) \ CBC_OPCODE (CBC_ASSIGN_SET_IDENT_PUSH_RESULT, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_IDENT | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_ASSIGN_SET_IDENT_BLOCK, \ CBC_HAS_LITERAL_ARG, \ -1, \ VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_IDENT | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_ASSIGN_LITERAL_SET_IDENT, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT) \ CBC_OPCODE (CBC_ASSIGN_LITERAL_SET_IDENT_PUSH_RESULT, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 1, \ VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_ASSIGN_LITERAL_SET_IDENT_BLOCK, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_ASSIGN_PROP_LITERAL, \ CBC_HAS_LITERAL_ARG, \ -2, \ VM_OC_ASSIGN_PROP | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE) \ CBC_OPCODE (CBC_ASSIGN_PROP_LITERAL_PUSH_RESULT, \ CBC_HAS_LITERAL_ARG, \ -1, \ VM_OC_ASSIGN_PROP | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_ASSIGN_PROP_LITERAL_BLOCK, \ CBC_HAS_LITERAL_ARG, \ -2, \ VM_OC_ASSIGN_PROP | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_ASSIGN_PROP_THIS_LITERAL, \ CBC_HAS_LITERAL_ARG, \ -1, \ VM_OC_ASSIGN_PROP_THIS | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE) \ CBC_OPCODE (CBC_ASSIGN_PROP_THIS_LITERAL_PUSH_RESULT, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_ASSIGN_PROP_THIS | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_ASSIGN_PROP_THIS_LITERAL_BLOCK, \ CBC_HAS_LITERAL_ARG, \ -1, \ VM_OC_ASSIGN_PROP_THIS | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_MOV_IDENT, CBC_HAS_LITERAL_ARG, -1, VM_OC_MOV_IDENT | VM_OC_GET_STACK | VM_OC_PUT_IDENT) \ CBC_OPCODE (CBC_ASSIGN_LET_CONST, CBC_HAS_LITERAL_ARG, -1, VM_OC_ASSIGN_LET_CONST | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_ASSIGN_LET_CONST_LITERAL, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_ASSIGN_LET_CONST | VM_OC_GET_LITERAL) \ \ /* Last opcode (not a real opcode). */ \ CBC_OPCODE (CBC_END, CBC_NO_FLAG, 0, VM_OC_NONE) /* All EXT branches are statement block end * marks, so they are always forward branches. */ #define CBC_EXT_OPCODE_LIST \ /* Branch opcodes first. Some other opcodes are mixed. */ \ CBC_OPCODE (CBC_EXT_NOP, CBC_NO_FLAG, 0, VM_OC_NONE) \ CBC_FORWARD_BRANCH (CBC_EXT_WITH_CREATE_CONTEXT, -1 + PARSER_WITH_CONTEXT_STACK_ALLOCATION, VM_OC_WITH) \ CBC_OPCODE (CBC_EXT_FOR_IN_GET_NEXT, CBC_NO_FLAG, 1, VM_OC_FOR_IN_GET_NEXT | VM_OC_PUT_STACK) \ CBC_FORWARD_BRANCH (CBC_EXT_FOR_IN_INIT, -1 + PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_IN_INIT) \ CBC_OPCODE (CBC_EXT_SET_GETTER, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_SET_GETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \ CBC_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT, 0, VM_OC_FOR_IN_HAS_NEXT) \ CBC_OPCODE (CBC_EXT_FOR_OF_GET_NEXT, CBC_NO_FLAG, 1, VM_OC_FOR_OF_GET_NEXT | VM_OC_PUT_STACK) \ CBC_FORWARD_BRANCH (CBC_EXT_FOR_OF_INIT, -1 + PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_OF_INIT) \ CBC_OPCODE (CBC_EXT_PUSH_NAMED_FUNC_EXPRESSION, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 1, \ VM_OC_PUSH_NAMED_FUNC_EXPR | VM_OC_GET_LITERAL_LITERAL) \ CBC_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_OF_HAS_NEXT, 0, VM_OC_FOR_OF_HAS_NEXT) \ CBC_OPCODE (CBC_EXT_CLONE_CONTEXT, CBC_NO_FLAG, 0, VM_OC_CLONE_CONTEXT) \ CBC_FORWARD_BRANCH (CBC_EXT_FOR_AWAIT_OF_INIT, \ -1 + PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION, \ VM_OC_FOR_AWAIT_OF_INIT) \ CBC_OPCODE (CBC_EXT_CLONE_FULL_CONTEXT, CBC_NO_FLAG, 0, VM_OC_CLONE_CONTEXT) \ CBC_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_AWAIT_OF_HAS_NEXT, 0, VM_OC_FOR_AWAIT_OF_HAS_NEXT) \ CBC_OPCODE (CBC_EXT_SET_SETTER, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_SET_SETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \ CBC_FORWARD_BRANCH (CBC_EXT_TRY_CREATE_CONTEXT, PARSER_TRY_CONTEXT_STACK_ALLOCATION, VM_OC_TRY) \ CBC_OPCODE (CBC_EXT_TRY_CREATE_ENV, CBC_NO_FLAG, 0, VM_OC_BLOCK_CREATE_CONTEXT) \ CBC_FORWARD_BRANCH (CBC_EXT_CATCH, 1, VM_OC_CATCH) \ CBC_OPCODE (CBC_EXT_RESOLVE_BASE, CBC_NO_FLAG, 0, VM_OC_RESOLVE_BASE_FOR_CALL) \ CBC_FORWARD_BRANCH (CBC_EXT_FINALLY, PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION, VM_OC_FINALLY) \ CBC_OPCODE (CBC_EXT_INITIALIZER_PUSH_PROP, CBC_NO_FLAG, 0, VM_OC_INITIALIZER_PUSH_PROP) \ CBC_FORWARD_BRANCH (CBC_EXT_DEFAULT_INITIALIZER, -1, VM_OC_DEFAULT_INITIALIZER) \ CBC_OPCODE (CBC_EXT_ERROR, CBC_NO_FLAG, 0, VM_OC_ERROR) \ CBC_FORWARD_BRANCH (CBC_EXT_BRANCH_IF_NULLISH, -1, VM_OC_BRANCH_IF_NULLISH) \ \ /* Basic opcodes. */ \ CBC_OPCODE (CBC_EXT_POP_REFERENCE, CBC_NO_FLAG, -2, VM_OC_POP_REFERENCE) \ CBC_OPCODE (CBC_EXT_CREATE_ARGUMENTS, CBC_HAS_LITERAL_ARG, 0, VM_OC_CREATE_ARGUMENTS) \ CBC_OPCODE (CBC_EXT_CREATE_VAR_EVAL, CBC_HAS_LITERAL_ARG, 0, VM_OC_EXT_VAR_EVAL) \ CBC_OPCODE (CBC_EXT_CREATE_VAR_FUNC_EVAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, VM_OC_EXT_VAR_EVAL) \ CBC_OPCODE (CBC_EXT_COPY_FROM_ARG, CBC_HAS_LITERAL_ARG, 0, VM_OC_COPY_FROM_ARG) \ CBC_OPCODE (CBC_EXT_PUSH_REST_OBJECT, CBC_NO_FLAG, 1, VM_OC_PUSH_REST_OBJECT) \ CBC_OPCODE (CBC_EXT_MODULE_IMPORT, CBC_NO_FLAG, 0, VM_OC_MODULE_IMPORT) \ CBC_OPCODE (CBC_EXT_MODULE_IMPORT_META, CBC_NO_FLAG, 1, VM_OC_MODULE_IMPORT_META) \ CBC_OPCODE (CBC_EXT_STRING_CONCAT, CBC_NO_FLAG, -1, VM_OC_STRING_CONCAT | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_STRING_CONCAT | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 1, \ VM_OC_STRING_CONCAT | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, CBC_HAS_BYTE_ARG, 1, VM_OC_GET_TEMPLATE_OBJECT | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_THROW_REFERENCE_ERROR, CBC_NO_FLAG, 1, VM_OC_THROW_REFERENCE_ERROR) \ CBC_OPCODE (CBC_EXT_THROW_ASSIGN_CONST_ERROR, CBC_NO_FLAG, 0, VM_OC_THROW_CONST_ERROR) \ CBC_OPCODE (CBC_EXT_REQUIRE_OBJECT_COERCIBLE, CBC_NO_FLAG, 0, VM_OC_REQUIRE_OBJECT_COERCIBLE) \ CBC_OPCODE (CBC_EXT_COPY_DATA_PROPERTIES, CBC_NO_FLAG, -1, VM_OC_COPY_DATA_PROPERTIES) \ CBC_OPCODE (CBC_EXT_SET_FUNCTION_NAME, CBC_HAS_LITERAL_ARG, 0, VM_OC_SET_FUNCTION_NAME | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_CLASS_NAME, CBC_HAS_LITERAL_ARG, 0, VM_OC_SET_FUNCTION_NAME) \ CBC_OPCODE (CBC_EXT_SET_COMPUTED_FUNCTION_NAME, CBC_NO_FLAG, 0, VM_OC_SET_FUNCTION_NAME) \ CBC_OPCODE (CBC_EXT_SET_COMPUTED_GETTER_NAME, CBC_NO_FLAG, 0, VM_OC_SET_FUNCTION_NAME) \ CBC_OPCODE (CBC_EXT_SET_COMPUTED_SETTER_NAME, CBC_NO_FLAG, 0, VM_OC_SET_FUNCTION_NAME) \ \ /* Computed / class property related opcodes. */ \ CBC_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY, \ CBC_NO_FLAG, \ -2, \ VM_OC_SET_COMPUTED_PROPERTY | VM_OC_NON_STATIC_FLAG | VM_OC_GET_STACK_STACK) \ CBC_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL, \ CBC_HAS_LITERAL_ARG, \ -1, \ VM_OC_SET_COMPUTED_PROPERTY | VM_OC_NON_STATIC_FLAG | VM_OC_GET_STACK_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_COMPUTED_GETTER, \ CBC_NO_FLAG, \ -2, \ VM_OC_SET_GETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_STACK_STACK) \ CBC_OPCODE (CBC_EXT_SET_COMPUTED_SETTER, \ CBC_NO_FLAG, \ -2, \ VM_OC_SET_SETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_STACK_STACK) \ CBC_OPCODE (CBC_EXT_SET_STATIC_PROPERTY, CBC_HAS_LITERAL_ARG, -1, VM_OC_SET_PROPERTY | VM_OC_GET_STACK_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_STATIC_PROPERTY_LITERAL, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_SET_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_STATIC_COMPUTED_PROPERTY, \ CBC_NO_FLAG, \ -2, \ VM_OC_SET_COMPUTED_PROPERTY | VM_OC_GET_STACK_STACK) \ CBC_OPCODE (CBC_EXT_SET_STATIC_GETTER, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_SET_GETTER | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_STATIC_SETTER, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_SET_SETTER | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_STATIC_COMPUTED_GETTER, CBC_NO_FLAG, -2, VM_OC_SET_GETTER | VM_OC_GET_STACK_STACK) \ CBC_OPCODE (CBC_EXT_SET_STATIC_COMPUTED_SETTER, CBC_NO_FLAG, -2, VM_OC_SET_SETTER | VM_OC_GET_STACK_STACK) \ CBC_OPCODE (CBC_EXT_SET__PROTO__, CBC_NO_FLAG, -1, VM_OC_SET__PROTO__ | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_PUSH_STATIC_FIELD_FUNC, \ CBC_HAS_LITERAL_ARG, \ 1, \ VM_OC_PUSH_STATIC_FIELD_FUNC | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_PUSH_STATIC_COMPUTED_FIELD_FUNC, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_PUSH_STATIC_FIELD_FUNC | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_ADD_COMPUTED_FIELD, CBC_NO_FLAG, -1, VM_OC_ADD_COMPUTED_FIELD | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_ADD_STATIC_COMPUTED_FIELD, CBC_NO_FLAG, -1, VM_OC_ADD_COMPUTED_FIELD | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_CLASS_CALL_STATIC_BLOCK, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_CLASS_CALL_STATIC_BLOCK | VM_OC_GET_LITERAL) \ /* Class private property related opcodes */ \ CBC_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_REFERENCE, \ CBC_HAS_LITERAL_ARG, \ 2, \ VM_OC_PRIVATE_PROP_REFERENCE | VM_OC_GET_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_PRIVATE_PROP_GET | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_IN, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_PRIVATE_IN | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_PRIVATE_FIELD_ADD, CBC_HAS_LITERAL_ARG, -1, VM_OC_PRIVATE_FIELD_ADD | VM_OC_GET_STACK_LITERAL) \ /* These 8 opcodes must be in this order */ \ CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_FIELD, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_METHOD, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_GETTER, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_SETTER, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_FIELD, \ CBC_HAS_LITERAL_ARG, \ 0, \ VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_METHOD, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_GETTER, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_SETTER, \ CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ 0, \ VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ /* Class related opcodes. */ \ CBC_OPCODE (CBC_EXT_PUSH_NAMED_CLASS_ENV, CBC_HAS_LITERAL_ARG, 1, VM_OC_PUSH_CLASS_ENVIRONMENT) \ CBC_OPCODE (CBC_EXT_DEFINE_FIELD, CBC_HAS_LITERAL_ARG, -1, VM_OC_DEFINE_FIELD | VM_OC_GET_STACK_LITERAL) \ CBC_OPCODE (CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR, CBC_NO_FLAG, 1, VM_OC_PUSH_IMPLICIT_CTOR | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR_HERITAGE, CBC_NO_FLAG, 1, VM_OC_PUSH_IMPLICIT_CTOR | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_INIT_CLASS, CBC_NO_FLAG, 0, VM_OC_INIT_CLASS | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_FINALIZE_NAMED_CLASS, CBC_HAS_LITERAL_ARG, -2, VM_OC_FINALIZE_CLASS) \ CBC_OPCODE (CBC_EXT_FINALIZE_ANONYMOUS_CLASS, CBC_NO_FLAG, -2, VM_OC_FINALIZE_CLASS) \ CBC_OPCODE (CBC_EXT_SET_FIELD_INIT, CBC_HAS_LITERAL_ARG, 0, VM_OC_SET_FIELD_INIT | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_RUN_FIELD_INIT, CBC_NO_FLAG, 0, VM_OC_RUN_FIELD_INIT) \ CBC_OPCODE (CBC_EXT_RUN_STATIC_FIELD_INIT, CBC_NO_FLAG, -1, VM_OC_RUN_STATIC_FIELD_INIT) \ CBC_OPCODE (CBC_EXT_SET_NEXT_COMPUTED_FIELD_ANONYMOUS_FUNC, \ CBC_NO_FLAG, \ -1, \ VM_OC_SET_NEXT_COMPUTED_FIELD | VM_OC_PUT_REFERENCE) \ CBC_OPCODE (CBC_EXT_SET_NEXT_COMPUTED_FIELD, CBC_NO_FLAG, -1, VM_OC_SET_NEXT_COMPUTED_FIELD | VM_OC_PUT_REFERENCE) \ CBC_OPCODE (CBC_EXT_PUSH_SUPER, CBC_NO_FLAG, 1, VM_OC_NONE) \ CBC_OPCODE (CBC_EXT_PUSH_SUPER_CONSTRUCTOR, CBC_NO_FLAG, 1, VM_OC_PUSH_SUPER_CONSTRUCTOR) \ CBC_OPCODE (CBC_EXT_PUSH_SUPER_PROP, CBC_NO_FLAG, 0, VM_OC_SUPER_REFERENCE | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_SUPER_PROP_REFERENCE, CBC_NO_FLAG, 2, VM_OC_SUPER_REFERENCE | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL, CBC_HAS_LITERAL_ARG, 1, VM_OC_SUPER_REFERENCE | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_REFERENCE, CBC_HAS_LITERAL_ARG, 3, VM_OC_SUPER_REFERENCE | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE, CBC_NO_FLAG, 1, VM_OC_SUPER_REFERENCE | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_ASSIGNMENT_REFERENCE, \ CBC_HAS_LITERAL_ARG, \ 2, \ VM_OC_SUPER_REFERENCE | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT, CBC_NO_FLAG, 0, VM_OC_SET_HOME_OBJECT) \ CBC_OPCODE (CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED, CBC_NO_FLAG, 0, VM_OC_SET_HOME_OBJECT) \ CBC_OPCODE (CBC_EXT_PUSH_OBJECT_SUPER_ENVIRONMENT, CBC_NO_FLAG, 1, VM_OC_OBJECT_LITERAL_HOME_ENV) \ CBC_OPCODE (CBC_EXT_POP_OBJECT_SUPER_ENVIRONMENT, CBC_NO_FLAG, -1, VM_OC_OBJECT_LITERAL_HOME_ENV) \ CBC_OPCODE (CBC_EXT_RESOLVE_LEXICAL_THIS, CBC_NO_FLAG, 1, VM_OC_RESOLVE_LEXICAL_THIS | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_LOCAL_EVAL, CBC_HAS_BYTE_ARG, 0, VM_OC_LOCAL_EVAL) \ CBC_OPCODE (CBC_EXT_ASSIGN_SUPER, CBC_NO_FLAG, -3, VM_OC_ASSIGN_SUPER) \ CBC_OPCODE (CBC_EXT_ASSIGN_SUPER_PUSH_RESULT, CBC_NO_FLAG, -2, VM_OC_ASSIGN_SUPER | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_ASSIGN_SUPER_BLOCK, CBC_NO_FLAG, -3, VM_OC_ASSIGN_SUPER | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_EXT_ASSIGN_PRIVATE, CBC_NO_FLAG, -3, VM_OC_ASSIGN_PRIVATE) \ CBC_OPCODE (CBC_EXT_ASSIGN_PRIVATE_PUSH_RESULT, CBC_NO_FLAG, -2, VM_OC_ASSIGN_PRIVATE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_ASSIGN_PRIVATE_BLOCK, CBC_NO_FLAG, -3, VM_OC_ASSIGN_PRIVATE | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_EXT_SUPER_CALL, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_SUPER_CALL) \ CBC_OPCODE (CBC_EXT_SUPER_CALL_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, 0, VM_OC_SUPER_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_SUPER_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_SUPER_CALL | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_EXT_SPREAD_SUPER_CALL, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_SUPER_CALL) \ CBC_OPCODE (CBC_EXT_SPREAD_SUPER_CALL_PUSH_RESULT, \ CBC_HAS_POP_STACK_BYTE_ARG, \ 0, \ VM_OC_SUPER_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_SPREAD_SUPER_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_SUPER_CALL | VM_OC_PUT_BLOCK) \ \ /* Spread / rest operation related opcodes. */ \ CBC_OPCODE (CBC_EXT_SPREAD_CALL, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_SPREAD_ARGUMENTS) \ CBC_OPCODE (CBC_EXT_SPREAD_CALL_PUSH_RESULT, \ CBC_HAS_POP_STACK_BYTE_ARG, \ 0, \ VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_SPREAD_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_EXT_SPREAD_CALL_PROP, CBC_HAS_POP_STACK_BYTE_ARG, -3, VM_OC_SPREAD_ARGUMENTS) \ CBC_OPCODE (CBC_EXT_SPREAD_CALL_PROP_PUSH_RESULT, \ CBC_HAS_POP_STACK_BYTE_ARG, \ -2, \ VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_SPREAD_CALL_PROP_BLOCK, \ CBC_HAS_POP_STACK_BYTE_ARG, \ -3, \ VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_EXT_PUSH_SPREAD_ELEMENT, CBC_NO_FLAG, 1, VM_OC_PUSH_SPREAD_ELEMENT) \ CBC_OPCODE (CBC_EXT_SPREAD_ARRAY_APPEND, CBC_HAS_POP_STACK_BYTE_ARG, 0, VM_OC_APPEND_ARRAY) \ CBC_OPCODE (CBC_EXT_REST_INITIALIZER, CBC_NO_FLAG, 1, VM_OC_REST_INITIALIZER) \ CBC_OPCODE (CBC_EXT_INITIALIZER_PUSH_PROP_LITERAL, \ CBC_HAS_LITERAL_ARG, \ 1, \ VM_OC_INITIALIZER_PUSH_PROP | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_SPREAD_NEW, CBC_HAS_POP_STACK_BYTE_ARG, 0, VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_STACK) \ \ /* Iterator related opcodes. */ \ CBC_OPCODE (CBC_EXT_ITERATOR_CONTEXT_CREATE, \ CBC_NO_FLAG, \ PARSER_ITERATOR_CONTEXT_STACK_ALLOCATION, \ VM_OC_ITERATOR_CONTEXT_CREATE) \ CBC_OPCODE (CBC_EXT_ITERATOR_CONTEXT_END, \ CBC_NO_FLAG, \ -PARSER_ITERATOR_CONTEXT_STACK_ALLOCATION, \ VM_OC_ITERATOR_CONTEXT_END) \ CBC_OPCODE (CBC_EXT_ITERATOR_STEP, CBC_NO_FLAG, 1, VM_OC_ITERATOR_STEP) \ \ /* Object initializer related opcodes. */ \ CBC_OPCODE (CBC_EXT_OBJ_INIT_CONTEXT_CREATE, \ CBC_NO_FLAG, \ PARSER_OBJ_INIT_CONTEXT_STACK_ALLOCATION, \ VM_OC_OBJ_INIT_CONTEXT_CREATE) \ CBC_OPCODE (CBC_EXT_OBJ_INIT_REST_CONTEXT_CREATE, \ CBC_NO_FLAG, \ PARSER_OBJ_INIT_REST_CONTEXT_STACK_ALLOCATION, \ VM_OC_OBJ_INIT_CONTEXT_CREATE) \ CBC_OPCODE (CBC_EXT_OBJ_INIT_PUSH_REST, CBC_NO_FLAG, 1, VM_OC_OBJ_INIT_PUSH_REST) \ CBC_OPCODE (CBC_EXT_OBJ_INIT_CONTEXT_END, \ CBC_NO_FLAG, \ -PARSER_OBJ_INIT_CONTEXT_STACK_ALLOCATION, \ VM_OC_OBJ_INIT_CONTEXT_END) \ CBC_OPCODE (CBC_EXT_INITIALIZER_PUSH_NAME, CBC_NO_FLAG, 0, VM_OC_INITIALIZER_PUSH_NAME | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_INITIALIZER_PUSH_NAME_LITERAL, \ CBC_HAS_LITERAL_ARG, \ 1, \ VM_OC_INITIALIZER_PUSH_NAME | VM_OC_GET_LITERAL) \ \ /* Executable object related opcodes. */ \ CBC_OPCODE (CBC_EXT_CREATE_GENERATOR, CBC_NO_FLAG, 1, VM_OC_CREATE_GENERATOR) \ CBC_OPCODE (CBC_EXT_YIELD, CBC_NO_FLAG, 0, VM_OC_YIELD) \ CBC_OPCODE (CBC_EXT_YIELD_ITERATOR, CBC_NO_FLAG, 0, VM_OC_YIELD) \ CBC_OPCODE (CBC_EXT_ASYNC_YIELD, CBC_NO_FLAG, 0, VM_OC_ASYNC_YIELD) \ CBC_OPCODE (CBC_EXT_ASYNC_YIELD_ITERATOR, CBC_NO_FLAG, 0, VM_OC_ASYNC_YIELD_ITERATOR) \ CBC_OPCODE (CBC_EXT_AWAIT, CBC_NO_FLAG, 0, VM_OC_AWAIT) \ CBC_OPCODE (CBC_EXT_GENERATOR_AWAIT, CBC_NO_FLAG, 0, VM_OC_GENERATOR_AWAIT) \ CBC_OPCODE (CBC_EXT_ASYNC_EXIT, CBC_NO_FLAG, 0, VM_OC_ASYNC_EXIT) \ CBC_OPCODE (CBC_EXT_RETURN, CBC_NO_FLAG, -1, VM_OC_EXT_RETURN | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_RETURN_UNDEFINED, CBC_NO_FLAG, 0, VM_OC_EXT_RETURN) \ CBC_OPCODE (CBC_EXT_PUSH_NEW_TARGET, CBC_NO_FLAG, 1, VM_OC_PUSH_NEW_TARGET | VM_OC_PUT_STACK) \ \ /* Last opcode (not a real opcode). */ \ CBC_OPCODE (CBC_EXT_END, CBC_NO_FLAG, 0, VM_OC_NONE) #define CBC_MAXIMUM_BYTE_VALUE 255 #define CBC_MAXIMUM_SMALL_VALUE 510 #define CBC_MAXIMUM_FULL_VALUE 32767 #define CBC_PUSH_NUMBER_BYTE_RANGE_END 256 #define CBC_HIGHEST_BIT_MASK 0x80 #define CBC_LOWER_SEVEN_BIT_MASK 0x7f /** * Literal encoding limit when full literal encoding mode is enabled */ #define CBC_FULL_LITERAL_ENCODING_LIMIT 128 /** * Literal encoding delta when full literal encoding mode is enabled */ #define CBC_FULL_LITERAL_ENCODING_DELTA 0x8000 /** * Literal encoding limit when full literal encoding mode is disabled */ #define CBC_SMALL_LITERAL_ENCODING_LIMIT 255 /** * Literal encoding delta when full literal encoding mode is disabled */ #define CBC_SMALL_LITERAL_ENCODING_DELTA 0xfe01 /** * Literal indices belong to one of the following groups: * * 0 <= index < argument_end : arguments * argument_end <= index < register_end : registers * register_end <= index < ident_end : identifiers * ident_end <= index < const_literal_end : constant literals * const_literal_end <= index < literal_end : template literals */ /** * Compiled byte code arguments. */ typedef struct { ecma_compiled_code_t header; /**< compiled code header */ uint8_t stack_limit; /**< maximum number of values stored on the stack */ uint8_t argument_end; /**< number of arguments expected by the function */ ecma_value_t script_value; /**< script value */ uint8_t register_end; /**< end position of the register group */ uint8_t ident_end; /**< end position of the identifier group */ uint8_t const_literal_end; /**< end position of the const literal group */ uint8_t literal_end; /**< end position of the literal group */ } cbc_uint8_arguments_t; /** * Compiled byte code arguments. */ typedef struct { ecma_compiled_code_t header; /**< compiled code header */ uint16_t stack_limit; /**< maximum number of values stored on the stack */ ecma_value_t script_value; /**< script value */ uint16_t argument_end; /**< number of arguments expected by the function */ uint16_t register_end; /**< end position of the register group */ uint16_t ident_end; /**< end position of the identifier group */ uint16_t const_literal_end; /**< end position of the const literal group */ uint16_t literal_end; /**< end position of the literal group */ uint16_t padding; /**< an unused value */ } cbc_uint16_arguments_t; /** * Compact byte code status flags. */ typedef enum { CBC_CODE_FLAGS_FULL_LITERAL_ENCODING = (1u << 0), /**< full literal encoding mode is enabled */ CBC_CODE_FLAGS_UINT16_ARGUMENTS = (1u << 1), /**< compiled code data is cbc_uint16_arguments_t */ CBC_CODE_FLAGS_STRICT_MODE = (1u << 2), /**< strict mode is enabled */ CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED = (1u << 3), /**< mapped arguments object must be constructed */ CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED = (1u << 4), /**< no need to create a lexical environment */ CBC_CODE_FLAGS_HAS_EXTENDED_INFO = (1u << 5), /**< this function has extended info block */ CBC_CODE_FLAGS_HAS_TAGGED_LITERALS = (1u << 6), /**< this function has tagged template literal list */ CBC_CODE_FLAGS_HAS_LINE_INFO = (1u << 7), /**< this function has line info block */ CBC_CODE_FLAGS_STATIC_FUNCTION = (1u << 8), /**< this function is a static snapshot function */ CBC_CODE_FLAGS_DEBUGGER_IGNORE = (1u << 9), /**< this function should be ignored by debugger */ CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED = (1u << 10), /**< compiled code needs a lexical block */ /* Bits from bit 12 is reserved for function types (see CBC_FUNCTION_TYPE_SHIFT). * Note: the last bits are used for type flags because < and >= operators can be used to check a range of types without decoding the actual type. */ } cbc_code_flags_t; /** * Optional byte code fields. These fields are stored in a reversed * order from the end of the byte code data. * * Value fields: * - when CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED is set: * argument_end number of argument names encoded as strings * - when function type is not CBC_FUNCTION_CONSTRUCTOR: * function name encoded as string * - when CBC_CODE_FLAGS_HAS_TAGGED_LITERALS is set: * pointer to the tagged template collection encoded as value * * Byte fields when CBC_CODE_FLAGS_HAS_EXTENDED_INFO is set: * - always available: * a byte which contains a combination of CBC_EXTENDED_CODE_FLAGS bits * - when CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH is set: * a vlq encoded default value for function length * - when CBC_EXTENDED_CODE_FLAGS_HAS_SOURCE_CODE_RANGE is set: * a pair of vlq encoded values, representing the start and size of the range */ /** * Compact byte code function types. */ typedef enum { /* The first type must be regular expression (see CBC_IS_FUNCTION) */ CBC_REGULAR_EXPRESSION, /**< regular expression literal */ CBC_FUNCTION_NORMAL, /**< function without special properties */ CBC_FUNCTION_CONSTRUCTOR, /**< constructor function */ /* The following functions cannot be constructed (see CBC_FUNCTION_IS_CONSTRUCTABLE) */ CBC_FUNCTION_SCRIPT, /**< script (global) function */ CBC_FUNCTION_GENERATOR, /**< generator function */ CBC_FUNCTION_ASYNC_GENERATOR, /**< async generator function */ /* The following functions has no prototype (see CBC_FUNCTION_HAS_PROTOTYPE) */ CBC_FUNCTION_ACCESSOR, /**< property accessor function */ CBC_FUNCTION_ASYNC, /**< async function */ CBC_FUNCTION_METHOD, /**< method */ /* The following functions are arrow function (see CBC_FUNCTION_IS_ARROW) */ CBC_FUNCTION_ARROW, /**< arrow function */ CBC_FUNCTION_ASYNC_ARROW, /**< arrow function */ } cbc_code_function_types_t; /** * Shift for getting / setting the function type of a byte code. */ #define CBC_FUNCTION_TYPE_SHIFT 12 /** * Compute function type bits in code flags. */ #define CBC_FUNCTION_TO_TYPE_BITS(name) ((name) << CBC_FUNCTION_TYPE_SHIFT) /** * Get function type from code flags. */ #define CBC_FUNCTION_GET_TYPE(flags) ((uint16_t) ((flags) >> CBC_FUNCTION_TYPE_SHIFT)) /** * Checks whether the byte code is a function or a regular expression. */ #define CBC_IS_FUNCTION(flags) ((flags) >= (CBC_FUNCTION_NORMAL << CBC_FUNCTION_TYPE_SHIFT)) /** * Checks whether the function can be constructed with new operator. */ #define CBC_FUNCTION_IS_CONSTRUCTABLE(flags) ((flags) < (CBC_FUNCTION_GENERATOR << CBC_FUNCTION_TYPE_SHIFT)) /** * Checks whether the function has prototype property. */ #define CBC_FUNCTION_HAS_PROTOTYPE(flags) ((flags) < (CBC_FUNCTION_ACCESSOR << CBC_FUNCTION_TYPE_SHIFT)) /** * Checks whether the function is an arrow function. */ #define CBC_FUNCTION_IS_ARROW(flags) ((flags) >= (CBC_FUNCTION_ARROW << CBC_FUNCTION_TYPE_SHIFT)) /** * Compact byte code extended status flags. */ typedef enum { CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH = (1u << 0), /**< has argument length */ CBC_EXTENDED_CODE_FLAGS_HAS_SOURCE_CODE_RANGE = (1u << 1), /**< has source code range (start, end) */ CBC_EXTENDED_CODE_FLAGS_SOURCE_CODE_IN_ARGUMENTS = (1u << 2), /**< source code range is inside * the function arguments */ } cbc_extended_code_flags_t; /** * Shared script data. */ typedef enum { CBC_SCRIPT_HAS_USER_VALUE = (1 << 0), /**< script has user value */ CBC_SCRIPT_USER_VALUE_IS_OBJECT = (1 << 1), /**< user value is object */ CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS = (1 << 2), /**< script is a function with arguments source code */ CBC_SCRIPT_HAS_IMPORT_META = (1 << 3), /**< script is a module with import.meta object */ CBC_SCRIPT_IS_EVAL_CODE = (1 << 4), /**< script is compiled by eval like (eval, new Function, etc.) expression */ } cbc_script_type; /** * Value for increasing or decreasing the script reference counter. */ #define CBC_SCRIPT_REF_ONE 0x20 /** * Maximum value of script reference counter. */ #define CBC_SCRIPT_REF_MAX (UINT32_MAX - CBC_SCRIPT_REF_ONE + 1) /** * Sets the type of a script using the user_value. */ #define CBC_SCRIPT_SET_TYPE(script_p, user_value, ref_count) \ do \ { \ (script_p)->refs_and_type = (ref_count); \ if ((user_value) != ECMA_VALUE_EMPTY) \ { \ (script_p)->refs_and_type |= CBC_SCRIPT_HAS_USER_VALUE; \ if (ecma_is_value_object (user_value)) \ { \ (script_p)->refs_and_type |= CBC_SCRIPT_USER_VALUE_IS_OBJECT; \ } \ } \ } while (false) /** * Shared script data. */ typedef struct { #if JERRY_BUILTIN_REALMS ecma_object_t *realm_p; /**< realm object */ #endif /* JERRY_BUILTIN_REALMS */ uint32_t refs_and_type; /**< reference counter and type of the function */ #if JERRY_SOURCE_NAME ecma_value_t source_name; /**< source name */ #endif /* JERRY_SOURCE_NAME */ } cbc_script_t; /** * Get the array of optional values assigned to a script. * * First value: user value * Second value: function arguments value */ #define CBC_SCRIPT_GET_OPTIONAL_VALUES(script_p) ((ecma_value_t *) ((script_p) + 1)) /** * Get user value. */ #define CBC_SCRIPT_GET_USER_VALUE(script_p) (CBC_SCRIPT_GET_OPTIONAL_VALUES (script_p)[0]) /** * Get function arguments. */ #define CBC_SCRIPT_GET_FUNCTION_ARGUMENTS(script_p, type) \ (CBC_SCRIPT_GET_OPTIONAL_VALUES (script_p)[((type) &CBC_SCRIPT_HAS_USER_VALUE) ? 1 : 0]) /** * Get import.meta object. */ #define CBC_SCRIPT_GET_IMPORT_META(script_p, type) \ (CBC_SCRIPT_GET_OPTIONAL_VALUES (script_p)[((type) &CBC_SCRIPT_HAS_USER_VALUE) ? 1 : 0]) #define CBC_OPCODE(arg1, arg2, arg3, arg4) arg1, /** * Opcode list. */ typedef enum { CBC_OPCODE_LIST /**< list of opcodes */ } cbc_opcode_t; /** * Extended opcode list. */ typedef enum { CBC_EXT_OPCODE_LIST /**< list extended opcodes */ } cbc_ext_opcode_t; #undef CBC_OPCODE /** * Opcode flags. */ extern const uint8_t cbc_flags[]; extern const uint8_t cbc_ext_flags[]; #if JERRY_PARSER_DUMP_BYTE_CODE /** * Opcode names for debugging. */ extern const char *const cbc_names[]; extern const char *const cbc_ext_names[]; #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ /** * @} * @} * @} */ #endif /* !BYTE_CODE_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/000775 001750 001750 00000000000 15164251010 030555 5ustar00ddennedyddennedy000000 000000 src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/thorvg_lottie.h000664 001750 001750 00000013502 15164251010 035721 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0#ifndef _THORVG_LOTTIE_H_ #define _THORVG_LOTTIE_H_ #include "thorvg.h" namespace tvg { /** * @class LottieAnimation * * @brief The LottieAnimation class enables control of advanced Lottie features. * * This class extends the Animation and has additional interfaces. * * @see Animation * * @since 0.15 */ class TVG_API LottieAnimation final : public Animation { public: ~LottieAnimation() override; /** * @brief Specifies a segment by marker. * * Markers are used to control animation playback by specifying start and end points, * eliminating the need to know the exact frame numbers. * Generally, markers are designated at the design level, * meaning the callers must know the marker name in advance to use it. * * @param[in] marker The name of the segment marker. * * @retval Result::InsufficientCondition If the animation is not loaded. * @retval Result::NonSupport When it's not animatable. * * @note If a @c marker is specified, the previously set segment will be disregarded. * @note Set @c nullptr to reset the specified segment. * @see Animation::segment(float begin, float end) * @since 1.0 */ Result segment(const char* marker) noexcept; /** * @brief Interpolates between two frames over a specified duration. * * This method performs tweening, a process of generating intermediate frame * between @p from and @p to based on the given @p progress. * * @param[in] from The start frame number of the interpolation. * @param[in] to The end frame number of the interpolation. * @param[in] progress The current progress of the interpolation (range: 0.0 to 1.0). * * @retval Result::InsufficientCondition In case the animation is not loaded. * * @since 1.0 */ Result tween(float from, float to, float progress) noexcept; /** * @brief Gets the marker count of the animation. * * @retval The count of the markers, zero if there is no marker. * * @see LottieAnimation::marker() * @since 1.0 */ uint32_t markersCnt() noexcept; /** * @brief Gets the marker name by a given index. * * @param[in] idx The index of the animation marker, starts from 0. * * @retval The name of marker when succeed, @c nullptr otherwise. * * @see LottieAnimation::markersCnt() * @since 1.0 */ const char* marker(uint32_t idx) noexcept; /** * @brief Updates the value of an expression variable for a specific layer. * * This function sets the value of a specified expression variable within a particular layer. * It is useful for dynamically changing the properties of a layer at runtime. * * @param[in] layer The name of the layer containing the variable to be updated. * @param[in] ix The property index of the variable within the layer. * @param[in] var The name of the variable to be updated. * @param[in] val The new value to assign to the variable. * * @retval Result::InsufficientCondition If the animation is not loaded. * @retval Result::InvalidArguments When the given parameter is invalid. * @retval Result::NonSupport When neither the layer nor the property is found in the current animation. * * @note Experimental API */ Result assign(const char* layer, uint32_t ix, const char* var, float val); /** * @brief Creates a new slot based on the given Lottie slot data. * * This function parses the provided JSON-formatted slot data and generates * a new slot for animation control. The returned slot ID can be used to apply * or delete the slot later. * * @param[in] slot A JSON string representing the Lottie slot data. * * @return A unique, non-zero slot ID on success. Returns @c 0 if the slot generation fails. * * @see apply(uint32_t id) * @see del(uint32_t id) * * @since 1.0 */ uint32_t gen(const char* slot) noexcept; /** * @brief Applies a previously generated slot to the animation. * * This function applies the animation parameters defined by a slot. * If the provided slot ID is 0, all previously applied slots will be reset. * * @param[in] id The ID of the slot to apply. Use 0 to reset all slots. * * @retval Result::InvalidArguments If the animation is not loaded or the slot ID is invalid. * * @see gen(const char* slot) * * @since 1.0 */ Result apply(uint32_t id) noexcept; /** * @brief Deletes a previously generated slot. * * This function removes a slot by its ID. * * @param[in] id The ID of the slot to delete. Retrieve the ID from gen(). * * @retval Result::InvalidArguments If the animation is not loaded or the slot ID is invalid. * * @note This function should be paired with gen. * @see gen(const char* slot) * * @since 1.0 */ Result del(uint32_t id) noexcept; /** * @brief Sets the quality level for Lottie effects. * * This function controls the rendering quality of effects like blur, shadows, etc. * Lower values prioritize performance while higher values prioritize quality. * * @param[in] value The quality level (0-100). 0 represents lowest quality/best performance, * 100 represents highest quality/lowest performance, default is 50. * * @retval Result::InsufficientCondition If the animation is not loaded. * * @since 1.0 */ Result quality(uint8_t value) noexcept; /** * @brief Creates a new LottieAnimation object. * * @return A new LottieAnimation object. * * @since 0.15 */ static LottieAnimation* gen() noexcept; _TVG_DECLARE_PRIVATE(LottieAnimation); }; } //namespace #endif //_THORVG_LOTTIE_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/build_ubuntu.yml000664 001750 001750 00000005022 15164251010 035756 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0name: Ubuntu on: pull_request: branches: - main push: branches: - main permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: true - name: Install Packages run: | sudo apt-get update sudo apt-get install meson ninja-build libturbojpeg0-dev libpng-dev libwebp-dev libgles-dev libsdl2-dev - name: Build run: | meson setup build -Dlog=true -Dengines="sw, gl" -Dloaders=all -Dsavers=all -Dbindings=capi -Dtools=all sudo ninja -C build install compact_test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: true - name: Install Packages run: | sudo apt-get update sudo apt-get install meson ninja-build libgles-dev - name: Build run: | meson setup build -Dlog=true -Dengines="sw, gl" -Dloaders=all -Dsavers=all -Dstatic=true -Dthreads=false sudo ninja -C build install unit_test: runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - uses: actions/checkout@v4 with: submodules: true - name: Install Packages run: | sudo apt-get update sudo apt-get install meson ninja-build libgtest-dev libasan5 valgrind curl jq software-properties-common libturbojpeg0-dev libpng-dev libwebp-dev libgles-dev - name: Build run: | meson setup build -Dloaders="all" -Dengines="sw, gl" -Dsavers="all" -Dbindings="capi" -Dtests=true --errorlogs sudo ninja -C build install test - uses: actions/upload-artifact@v4 with: name: UnitTestReport path: build/meson-logs/testlog.txt - name: Run memcheck Script(valgrind) run: | export PATH=$PATH:~/.local/bin/ chmod +x "${GITHUB_WORKSPACE}/.github/workflows/memcheck_valgrind.sh" "${GITHUB_WORKSPACE}/.github/workflows/memcheck_valgrind.sh" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Build & Run memcheck Script(ASAN) run: | sudo rm -rf ./build meson setup build -Db_sanitize="address,undefined" -Dloaders="all" -Dsavers="all" -Dtests="true" -Dbindings="capi" sudo ninja -C build install export PATH=$PATH:~/.local/bin/ chmod +x "${GITHUB_WORKSPACE}/.github/workflows/memcheck_asan.sh" "${GITHUB_WORKSPACE}/.github/workflows/memcheck_asan.sh" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/000775 001750 001750 00000000000 15165022620 024377 5ustar00ddennedyddennedy000000 000000 src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/dec_clip_tables.cpp000664 001750 001750 00000150300 15164251010 036700 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2014 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Clipping tables for filtering // // Author: Skal (pascal.massimino@gmail.com) #include "./dsp.h" //#define USE_STATIC_TABLES // undefine to have run-time table initialization #ifdef USE_STATIC_TABLES static const uint8_t abs0[255 + 255 + 1] = { (uint8_t)0xff, (uint8_t)0xfe, (uint8_t)0xfd, (uint8_t)0xfc, (uint8_t)0xfb, (uint8_t)0xfa, (uint8_t)0xf9, (uint8_t)0xf8, (uint8_t)0xf7, (uint8_t)0xf6, (uint8_t)0xf5, (uint8_t)0xf4, (uint8_t)0xf3, (uint8_t)0xf2, (uint8_t)0xf1, (uint8_t)0xf0, (uint8_t)0xef, (uint8_t)0xee, (uint8_t)0xed, (uint8_t)0xec, (uint8_t)0xeb, (uint8_t)0xea, (uint8_t)0xe9, (uint8_t)0xe8, (uint8_t)0xe7, (uint8_t)0xe6, (uint8_t)0xe5, (uint8_t)0xe4, (uint8_t)0xe3, (uint8_t)0xe2, (uint8_t)0xe1, (uint8_t)0xe0, (uint8_t)0xdf, (uint8_t)0xde, (uint8_t)0xdd, (uint8_t)0xdc, (uint8_t)0xdb, (uint8_t)0xda, (uint8_t)0xd9, (uint8_t)0xd8, (uint8_t)0xd7, (uint8_t)0xd6, (uint8_t)0xd5, (uint8_t)0xd4, (uint8_t)0xd3, (uint8_t)0xd2, (uint8_t)0xd1, (uint8_t)0xd0, (uint8_t)0xcf, (uint8_t)0xce, (uint8_t)0xcd, (uint8_t)0xcc, (uint8_t)0xcb, (uint8_t)0xca, (uint8_t)0xc9, (uint8_t)0xc8, (uint8_t)0xc7, (uint8_t)0xc6, (uint8_t)0xc5, (uint8_t)0xc4, (uint8_t)0xc3, (uint8_t)0xc2, (uint8_t)0xc1, (uint8_t)0xc0, (uint8_t)0xbf, (uint8_t)0xbe, (uint8_t)0xbd, (uint8_t)0xbc, (uint8_t)0xbb, (uint8_t)0xba, (uint8_t)0xb9, (uint8_t)0xb8, (uint8_t)0xb7, (uint8_t)0xb6, (uint8_t)0xb5, (uint8_t)0xb4, (uint8_t)0xb3, (uint8_t)0xb2, (uint8_t)0xb1, (uint8_t)0xb0, (uint8_t)0xaf, (uint8_t)0xae, (uint8_t)0xad, (uint8_t)0xac, (uint8_t)0xab, (uint8_t)0xaa, (uint8_t)0xa9, (uint8_t)0xa8, (uint8_t)0xa7, (uint8_t)0xa6, (uint8_t)0xa5, (uint8_t)0xa4, (uint8_t)0xa3, (uint8_t)0xa2, (uint8_t)0xa1, (uint8_t)0xa0, (uint8_t)0x9f, (uint8_t)0x9e, (uint8_t)0x9d, (uint8_t)0x9c, (uint8_t)0x9b, (uint8_t)0x9a, (uint8_t)0x99, (uint8_t)0x98, (uint8_t)0x97, (uint8_t)0x96, (uint8_t)0x95, (uint8_t)0x94, (uint8_t)0x93, (uint8_t)0x92, (uint8_t)0x91, (uint8_t)0x90, (uint8_t)0x8f, (uint8_t)0x8e, (uint8_t)0x8d, (uint8_t)0x8c, (uint8_t)0x8b, (uint8_t)0x8a, (uint8_t)0x89, (uint8_t)0x88, (uint8_t)0x87, (uint8_t)0x86, (uint8_t)0x85, (uint8_t)0x84, (uint8_t)0x83, (uint8_t)0x82, (uint8_t)0x81, (uint8_t)0x80, (uint8_t)0x7f, (uint8_t)0x7e, (uint8_t)0x7d, (uint8_t)0x7c, (uint8_t)0x7b, (uint8_t)0x7a, (uint8_t)0x79, (uint8_t)0x78, (uint8_t)0x77, (uint8_t)0x76, (uint8_t)0x75, (uint8_t)0x74, (uint8_t)0x73, (uint8_t)0x72, (uint8_t)0x71, (uint8_t)0x70, (uint8_t)0x6f, (uint8_t)0x6e, (uint8_t)0x6d, (uint8_t)0x6c, (uint8_t)0x6b, (uint8_t)0x6a, (uint8_t)0x69, (uint8_t)0x68, (uint8_t)0x67, (uint8_t)0x66, (uint8_t)0x65, (uint8_t)0x64, (uint8_t)0x63, (uint8_t)0x62, (uint8_t)0x61, (uint8_t)0x60, (uint8_t)0x5f, (uint8_t)0x5e, (uint8_t)0x5d, (uint8_t)0x5c, (uint8_t)0x5b, (uint8_t)0x5a, (uint8_t)0x59, (uint8_t)0x58, (uint8_t)0x57, (uint8_t)0x56, (uint8_t)0x55, (uint8_t)0x54, (uint8_t)0x53, (uint8_t)0x52, (uint8_t)0x51, (uint8_t)0x50, (uint8_t)0x4f, (uint8_t)0x4e, (uint8_t)0x4d, (uint8_t)0x4c, (uint8_t)0x4b, (uint8_t)0x4a, (uint8_t)0x49, (uint8_t)0x48, (uint8_t)0x47, (uint8_t)0x46, (uint8_t)0x45, (uint8_t)0x44, (uint8_t)0x43, (uint8_t)0x42, (uint8_t)0x41, (uint8_t)0x40, (uint8_t)0x3f, (uint8_t)0x3e, (uint8_t)0x3d, (uint8_t)0x3c, (uint8_t)0x3b, (uint8_t)0x3a, (uint8_t)0x39, (uint8_t)0x38, (uint8_t)0x37, (uint8_t)0x36, (uint8_t)0x35, (uint8_t)0x34, (uint8_t)0x33, (uint8_t)0x32, (uint8_t)0x31, (uint8_t)0x30, (uint8_t)0x2f, (uint8_t)0x2e, (uint8_t)0x2d, (uint8_t)0x2c, (uint8_t)0x2b, (uint8_t)0x2a, (uint8_t)0x29, (uint8_t)0x28, (uint8_t)0x27, (uint8_t)0x26, (uint8_t)0x25, (uint8_t)0x24, (uint8_t)0x23, (uint8_t)0x22, (uint8_t)0x21, (uint8_t)0x20, (uint8_t)0x1f, (uint8_t)0x1e, (uint8_t)0x1d, (uint8_t)0x1c, (uint8_t)0x1b, (uint8_t)0x1a, (uint8_t)0x19, (uint8_t)0x18, (uint8_t)0x17, (uint8_t)0x16, (uint8_t)0x15, (uint8_t)0x14, (uint8_t)0x13, (uint8_t)0x12, (uint8_t)0x11, (uint8_t)0x10, (uint8_t)0x0f, (uint8_t)0x0e, (uint8_t)0x0d, (uint8_t)0x0c, (uint8_t)0x0b, (uint8_t)0x0a, (uint8_t)0x09, (uint8_t)0x08, (uint8_t)0x07, (uint8_t)0x06, (uint8_t)0x05, (uint8_t)0x04, (uint8_t)0x03, (uint8_t)0x02, (uint8_t)0x01, (uint8_t)0x00, (uint8_t)0x01, (uint8_t)0x02, (uint8_t)0x03, (uint8_t)0x04, (uint8_t)0x05, (uint8_t)0x06, (uint8_t)0x07, (uint8_t)0x08, (uint8_t)0x09, (uint8_t)0x0a, (uint8_t)0x0b, (uint8_t)0x0c, (uint8_t)0x0d, (uint8_t)0x0e, (uint8_t)0x0f, (uint8_t)0x10, (uint8_t)0x11, (uint8_t)0x12, (uint8_t)0x13, (uint8_t)0x14, (uint8_t)0x15, (uint8_t)0x16, (uint8_t)0x17, (uint8_t)0x18, (uint8_t)0x19, (uint8_t)0x1a, (uint8_t)0x1b, (uint8_t)0x1c, (uint8_t)0x1d, (uint8_t)0x1e, (uint8_t)0x1f, (uint8_t)0x20, (uint8_t)0x21, (uint8_t)0x22, (uint8_t)0x23, (uint8_t)0x24, (uint8_t)0x25, (uint8_t)0x26, (uint8_t)0x27, (uint8_t)0x28, (uint8_t)0x29, (uint8_t)0x2a, (uint8_t)0x2b, (uint8_t)0x2c, (uint8_t)0x2d, (uint8_t)0x2e, (uint8_t)0x2f, (uint8_t)0x30, (uint8_t)0x31, (uint8_t)0x32, (uint8_t)0x33, (uint8_t)0x34, (uint8_t)0x35, (uint8_t)0x36, (uint8_t)0x37, (uint8_t)0x38, (uint8_t)0x39, (uint8_t)0x3a, (uint8_t)0x3b, (uint8_t)0x3c, (uint8_t)0x3d, (uint8_t)0x3e, (uint8_t)0x3f, (uint8_t)0x40, (uint8_t)0x41, (uint8_t)0x42, (uint8_t)0x43, (uint8_t)0x44, (uint8_t)0x45, (uint8_t)0x46, (uint8_t)0x47, (uint8_t)0x48, (uint8_t)0x49, (uint8_t)0x4a, (uint8_t)0x4b, (uint8_t)0x4c, (uint8_t)0x4d, (uint8_t)0x4e, (uint8_t)0x4f, (uint8_t)0x50, (uint8_t)0x51, (uint8_t)0x52, (uint8_t)0x53, (uint8_t)0x54, (uint8_t)0x55, (uint8_t)0x56, (uint8_t)0x57, (uint8_t)0x58, (uint8_t)0x59, (uint8_t)0x5a, (uint8_t)0x5b, (uint8_t)0x5c, (uint8_t)0x5d, (uint8_t)0x5e, (uint8_t)0x5f, (uint8_t)0x60, (uint8_t)0x61, (uint8_t)0x62, (uint8_t)0x63, (uint8_t)0x64, (uint8_t)0x65, (uint8_t)0x66, (uint8_t)0x67, (uint8_t)0x68, (uint8_t)0x69, (uint8_t)0x6a, (uint8_t)0x6b, (uint8_t)0x6c, (uint8_t)0x6d, (uint8_t)0x6e, (uint8_t)0x6f, (uint8_t)0x70, (uint8_t)0x71, (uint8_t)0x72, (uint8_t)0x73, (uint8_t)0x74, (uint8_t)0x75, (uint8_t)0x76, (uint8_t)0x77, (uint8_t)0x78, (uint8_t)0x79, (uint8_t)0x7a, (uint8_t)0x7b, (uint8_t)0x7c, (uint8_t)0x7d, (uint8_t)0x7e, (uint8_t)0x7f, (uint8_t)0x80, (uint8_t)0x81, (uint8_t)0x82, (uint8_t)0x83, (uint8_t)0x84, (uint8_t)0x85, (uint8_t)0x86, (uint8_t)0x87, (uint8_t)0x88, (uint8_t)0x89, (uint8_t)0x8a, (uint8_t)0x8b, (uint8_t)0x8c, (uint8_t)0x8d, (uint8_t)0x8e, (uint8_t)0x8f, (uint8_t)0x90, (uint8_t)0x91, (uint8_t)0x92, (uint8_t)0x93, (uint8_t)0x94, (uint8_t)0x95, (uint8_t)0x96, (uint8_t)0x97, (uint8_t)0x98, (uint8_t)0x99, (uint8_t)0x9a, (uint8_t)0x9b, (uint8_t)0x9c, (uint8_t)0x9d, (uint8_t)0x9e, (uint8_t)0x9f, (uint8_t)0xa0, (uint8_t)0xa1, (uint8_t)0xa2, (uint8_t)0xa3, (uint8_t)0xa4, (uint8_t)0xa5, (uint8_t)0xa6, (uint8_t)0xa7, (uint8_t)0xa8, (uint8_t)0xa9, (uint8_t)0xaa, (uint8_t)0xab, (uint8_t)0xac, (uint8_t)0xad, (uint8_t)0xae, (uint8_t)0xaf, (uint8_t)0xb0, (uint8_t)0xb1, (uint8_t)0xb2, (uint8_t)0xb3, (uint8_t)0xb4, (uint8_t)0xb5, (uint8_t)0xb6, (uint8_t)0xb7, (uint8_t)0xb8, (uint8_t)0xb9, (uint8_t)0xba, (uint8_t)0xbb, (uint8_t)0xbc, (uint8_t)0xbd, (uint8_t)0xbe, (uint8_t)0xbf, (uint8_t)0xc0, (uint8_t)0xc1, (uint8_t)0xc2, (uint8_t)0xc3, (uint8_t)0xc4, (uint8_t)0xc5, (uint8_t)0xc6, (uint8_t)0xc7, (uint8_t)0xc8, (uint8_t)0xc9, (uint8_t)0xca, (uint8_t)0xcb, (uint8_t)0xcc, (uint8_t)0xcd, (uint8_t)0xce, (uint8_t)0xcf, (uint8_t)0xd0, (uint8_t)0xd1, (uint8_t)0xd2, (uint8_t)0xd3, (uint8_t)0xd4, (uint8_t)0xd5, (uint8_t)0xd6, (uint8_t)0xd7, (uint8_t)0xd8, (uint8_t)0xd9, (uint8_t)0xda, (uint8_t)0xdb, (uint8_t)0xdc, (uint8_t)0xdd, (uint8_t)0xde, (uint8_t)0xdf, (uint8_t)0xe0, (uint8_t)0xe1, (uint8_t)0xe2, (uint8_t)0xe3, (uint8_t)0xe4, (uint8_t)0xe5, (uint8_t)0xe6, (uint8_t)0xe7, (uint8_t)0xe8, (uint8_t)0xe9, (uint8_t)0xea, (uint8_t)0xeb, (uint8_t)0xec, (uint8_t)0xed, (uint8_t)0xee, (uint8_t)0xef, (uint8_t)0xf0, (uint8_t)0xf1, (uint8_t)0xf2, (uint8_t)0xf3, (uint8_t)0xf4, (uint8_t)0xf5, (uint8_t)0xf6, (uint8_t)0xf7, (uint8_t)0xf8, (uint8_t)0xf9, (uint8_t)0xfa, (uint8_t)0xfb, (uint8_t)0xfc, (uint8_t)0xfd, (uint8_t)0xfe, (uint8_t)0xff }; static const int8_t sclip1[1020 + 1020 + 1] = { (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x80, (int8_t)0x81, (int8_t)0x82, (int8_t)0x83, (int8_t)0x84, (int8_t)0x85, (int8_t)0x86, (int8_t)0x87, (int8_t)0x88, (int8_t)0x89, (int8_t)0x8a, (int8_t)0x8b, (int8_t)0x8c, (int8_t)0x8d, (int8_t)0x8e, (int8_t)0x8f, (int8_t)0x90, (int8_t)0x91, (int8_t)0x92, (int8_t)0x93, (int8_t)0x94, (int8_t)0x95, (int8_t)0x96, (int8_t)0x97, (int8_t)0x98, (int8_t)0x99, (int8_t)0x9a, (int8_t)0x9b, (int8_t)0x9c, (int8_t)0x9d, (int8_t)0x9e, (int8_t)0x9f, (int8_t)0xa0, (int8_t)0xa1, (int8_t)0xa2, (int8_t)0xa3, (int8_t)0xa4, (int8_t)0xa5, (int8_t)0xa6, (int8_t)0xa7, (int8_t)0xa8, (int8_t)0xa9, (int8_t)0xaa, (int8_t)0xab, (int8_t)0xac, (int8_t)0xad, (int8_t)0xae, (int8_t)0xaf, (int8_t)0xb0, (int8_t)0xb1, (int8_t)0xb2, (int8_t)0xb3, (int8_t)0xb4, (int8_t)0xb5, (int8_t)0xb6, (int8_t)0xb7, (int8_t)0xb8, (int8_t)0xb9, (int8_t)0xba, (int8_t)0xbb, (int8_t)0xbc, (int8_t)0xbd, (int8_t)0xbe, (int8_t)0xbf, (int8_t)0xc0, (int8_t)0xc1, (int8_t)0xc2, (int8_t)0xc3, (int8_t)0xc4, (int8_t)0xc5, (int8_t)0xc6, (int8_t)0xc7, (int8_t)0xc8, (int8_t)0xc9, (int8_t)0xca, (int8_t)0xcb, (int8_t)0xcc, (int8_t)0xcd, (int8_t)0xce, (int8_t)0xcf, (int8_t)0xd0, (int8_t)0xd1, (int8_t)0xd2, (int8_t)0xd3, (int8_t)0xd4, (int8_t)0xd5, (int8_t)0xd6, (int8_t)0xd7, (int8_t)0xd8, (int8_t)0xd9, (int8_t)0xda, (int8_t)0xdb, (int8_t)0xdc, (int8_t)0xdd, (int8_t)0xde, (int8_t)0xdf, (int8_t)0xe0, (int8_t)0xe1, (int8_t)0xe2, (int8_t)0xe3, (int8_t)0xe4, (int8_t)0xe5, (int8_t)0xe6, (int8_t)0xe7, (int8_t)0xe8, (int8_t)0xe9, (int8_t)0xea, (int8_t)0xeb, (int8_t)0xec, (int8_t)0xed, (int8_t)0xee, (int8_t)0xef, (int8_t)0xf0, (int8_t)0xf1, (int8_t)0xf2, (int8_t)0xf3, (int8_t)0xf4, (int8_t)0xf5, (int8_t)0xf6, (int8_t)0xf7, (int8_t)0xf8, (int8_t)0xf9, (int8_t)0xfa, (int8_t)0xfb, (int8_t)0xfc, (int8_t)0xfd, (int8_t)0xfe, (int8_t)0xff, (int8_t)0x00, (int8_t)0x01, (int8_t)0x02, (int8_t)0x03, (int8_t)0x04, (int8_t)0x05, (int8_t)0x06, (int8_t)0x07, (int8_t)0x08, (int8_t)0x09, (int8_t)0x0a, (int8_t)0x0b, (int8_t)0x0c, (int8_t)0x0d, (int8_t)0x0e, (int8_t)0x0f, (int8_t)0x10, (int8_t)0x11, (int8_t)0x12, (int8_t)0x13, (int8_t)0x14, (int8_t)0x15, (int8_t)0x16, (int8_t)0x17, (int8_t)0x18, (int8_t)0x19, (int8_t)0x1a, (int8_t)0x1b, (int8_t)0x1c, (int8_t)0x1d, (int8_t)0x1e, (int8_t)0x1f, (int8_t)0x20, (int8_t)0x21, (int8_t)0x22, (int8_t)0x23, (int8_t)0x24, (int8_t)0x25, (int8_t)0x26, (int8_t)0x27, (int8_t)0x28, (int8_t)0x29, (int8_t)0x2a, (int8_t)0x2b, (int8_t)0x2c, (int8_t)0x2d, (int8_t)0x2e, (int8_t)0x2f, (int8_t)0x30, (int8_t)0x31, (int8_t)0x32, (int8_t)0x33, (int8_t)0x34, (int8_t)0x35, (int8_t)0x36, (int8_t)0x37, (int8_t)0x38, (int8_t)0x39, (int8_t)0x3a, (int8_t)0x3b, (int8_t)0x3c, (int8_t)0x3d, (int8_t)0x3e, (int8_t)0x3f, (int8_t)0x40, (int8_t)0x41, (int8_t)0x42, (int8_t)0x43, (int8_t)0x44, (int8_t)0x45, (int8_t)0x46, (int8_t)0x47, (int8_t)0x48, (int8_t)0x49, (int8_t)0x4a, (int8_t)0x4b, (int8_t)0x4c, (int8_t)0x4d, (int8_t)0x4e, (int8_t)0x4f, (int8_t)0x50, (int8_t)0x51, (int8_t)0x52, (int8_t)0x53, (int8_t)0x54, (int8_t)0x55, (int8_t)0x56, (int8_t)0x57, (int8_t)0x58, (int8_t)0x59, (int8_t)0x5a, (int8_t)0x5b, (int8_t)0x5c, (int8_t)0x5d, (int8_t)0x5e, (int8_t)0x5f, (int8_t)0x60, (int8_t)0x61, (int8_t)0x62, (int8_t)0x63, (int8_t)0x64, (int8_t)0x65, (int8_t)0x66, (int8_t)0x67, (int8_t)0x68, (int8_t)0x69, (int8_t)0x6a, (int8_t)0x6b, (int8_t)0x6c, (int8_t)0x6d, (int8_t)0x6e, (int8_t)0x6f, (int8_t)0x70, (int8_t)0x71, (int8_t)0x72, (int8_t)0x73, (int8_t)0x74, (int8_t)0x75, (int8_t)0x76, (int8_t)0x77, (int8_t)0x78, (int8_t)0x79, (int8_t)0x7a, (int8_t)0x7b, (int8_t)0x7c, (int8_t)0x7d, (int8_t)0x7e, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f, (int8_t)0x7f }; static const int8_t sclip2[112 + 112 + 1] = { (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf0, (int8_t)0xf1, (int8_t)0xf2, (int8_t)0xf3, (int8_t)0xf4, (int8_t)0xf5, (int8_t)0xf6, (int8_t)0xf7, (int8_t)0xf8, (int8_t)0xf9, (int8_t)0xfa, (int8_t)0xfb, (int8_t)0xfc, (int8_t)0xfd, (int8_t)0xfe, (int8_t)0xff, (int8_t)0x00, (int8_t)0x01, (int8_t)0x02, (int8_t)0x03, (int8_t)0x04, (int8_t)0x05, (int8_t)0x06, (int8_t)0x07, (int8_t)0x08, (int8_t)0x09, (int8_t)0x0a, (int8_t)0x0b, (int8_t)0x0c, (int8_t)0x0d, (int8_t)0x0e, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f, (int8_t)0x0f }; static const uint8_t clip1[255 + 511 + 1] = { (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x00, (uint8_t)0x01, (uint8_t)0x02, (uint8_t)0x03, (uint8_t)0x04, (uint8_t)0x05, (uint8_t)0x06, (uint8_t)0x07, (uint8_t)0x08, (uint8_t)0x09, (uint8_t)0x0a, (uint8_t)0x0b, (uint8_t)0x0c, (uint8_t)0x0d, (uint8_t)0x0e, (uint8_t)0x0f, (uint8_t)0x10, (uint8_t)0x11, (uint8_t)0x12, (uint8_t)0x13, (uint8_t)0x14, (uint8_t)0x15, (uint8_t)0x16, (uint8_t)0x17, (uint8_t)0x18, (uint8_t)0x19, (uint8_t)0x1a, (uint8_t)0x1b, (uint8_t)0x1c, (uint8_t)0x1d, (uint8_t)0x1e, (uint8_t)0x1f, (uint8_t)0x20, (uint8_t)0x21, (uint8_t)0x22, (uint8_t)0x23, (uint8_t)0x24, (uint8_t)0x25, (uint8_t)0x26, (uint8_t)0x27, (uint8_t)0x28, (uint8_t)0x29, (uint8_t)0x2a, (uint8_t)0x2b, (uint8_t)0x2c, (uint8_t)0x2d, (uint8_t)0x2e, (uint8_t)0x2f, (uint8_t)0x30, (uint8_t)0x31, (uint8_t)0x32, (uint8_t)0x33, (uint8_t)0x34, (uint8_t)0x35, (uint8_t)0x36, (uint8_t)0x37, (uint8_t)0x38, (uint8_t)0x39, (uint8_t)0x3a, (uint8_t)0x3b, (uint8_t)0x3c, (uint8_t)0x3d, (uint8_t)0x3e, (uint8_t)0x3f, (uint8_t)0x40, (uint8_t)0x41, (uint8_t)0x42, (uint8_t)0x43, (uint8_t)0x44, (uint8_t)0x45, (uint8_t)0x46, (uint8_t)0x47, (uint8_t)0x48, (uint8_t)0x49, (uint8_t)0x4a, (uint8_t)0x4b, (uint8_t)0x4c, (uint8_t)0x4d, (uint8_t)0x4e, (uint8_t)0x4f, (uint8_t)0x50, (uint8_t)0x51, (uint8_t)0x52, (uint8_t)0x53, (uint8_t)0x54, (uint8_t)0x55, (uint8_t)0x56, (uint8_t)0x57, (uint8_t)0x58, (uint8_t)0x59, (uint8_t)0x5a, (uint8_t)0x5b, (uint8_t)0x5c, (uint8_t)0x5d, (uint8_t)0x5e, (uint8_t)0x5f, (uint8_t)0x60, (uint8_t)0x61, (uint8_t)0x62, (uint8_t)0x63, (uint8_t)0x64, (uint8_t)0x65, (uint8_t)0x66, (uint8_t)0x67, (uint8_t)0x68, (uint8_t)0x69, (uint8_t)0x6a, (uint8_t)0x6b, (uint8_t)0x6c, (uint8_t)0x6d, (uint8_t)0x6e, (uint8_t)0x6f, (uint8_t)0x70, (uint8_t)0x71, (uint8_t)0x72, (uint8_t)0x73, (uint8_t)0x74, (uint8_t)0x75, (uint8_t)0x76, (uint8_t)0x77, (uint8_t)0x78, (uint8_t)0x79, (uint8_t)0x7a, (uint8_t)0x7b, (uint8_t)0x7c, (uint8_t)0x7d, (uint8_t)0x7e, (uint8_t)0x7f, (uint8_t)0x80, (uint8_t)0x81, (uint8_t)0x82, (uint8_t)0x83, (uint8_t)0x84, (uint8_t)0x85, (uint8_t)0x86, (uint8_t)0x87, (uint8_t)0x88, (uint8_t)0x89, (uint8_t)0x8a, (uint8_t)0x8b, (uint8_t)0x8c, (uint8_t)0x8d, (uint8_t)0x8e, (uint8_t)0x8f, (uint8_t)0x90, (uint8_t)0x91, (uint8_t)0x92, (uint8_t)0x93, (uint8_t)0x94, (uint8_t)0x95, (uint8_t)0x96, (uint8_t)0x97, (uint8_t)0x98, (uint8_t)0x99, (uint8_t)0x9a, (uint8_t)0x9b, (uint8_t)0x9c, (uint8_t)0x9d, (uint8_t)0x9e, (uint8_t)0x9f, (uint8_t)0xa0, (uint8_t)0xa1, (uint8_t)0xa2, (uint8_t)0xa3, (uint8_t)0xa4, (uint8_t)0xa5, (uint8_t)0xa6, (uint8_t)0xa7, (uint8_t)0xa8, (uint8_t)0xa9, (uint8_t)0xaa, (uint8_t)0xab, (uint8_t)0xac, (uint8_t)0xad, (uint8_t)0xae, (uint8_t)0xaf, (uint8_t)0xb0, (uint8_t)0xb1, (uint8_t)0xb2, (uint8_t)0xb3, (uint8_t)0xb4, (uint8_t)0xb5, (uint8_t)0xb6, (uint8_t)0xb7, (uint8_t)0xb8, (uint8_t)0xb9, (uint8_t)0xba, (uint8_t)0xbb, (uint8_t)0xbc, (uint8_t)0xbd, (uint8_t)0xbe, (uint8_t)0xbf, (uint8_t)0xc0, (uint8_t)0xc1, (uint8_t)0xc2, (uint8_t)0xc3, (uint8_t)0xc4, (uint8_t)0xc5, (uint8_t)0xc6, (uint8_t)0xc7, (uint8_t)0xc8, (uint8_t)0xc9, (uint8_t)0xca, (uint8_t)0xcb, (uint8_t)0xcc, (uint8_t)0xcd, (uint8_t)0xce, (uint8_t)0xcf, (uint8_t)0xd0, (uint8_t)0xd1, (uint8_t)0xd2, (uint8_t)0xd3, (uint8_t)0xd4, (uint8_t)0xd5, (uint8_t)0xd6, (uint8_t)0xd7, (uint8_t)0xd8, (uint8_t)0xd9, (uint8_t)0xda, (uint8_t)0xdb, (uint8_t)0xdc, (uint8_t)0xdd, (uint8_t)0xde, (uint8_t)0xdf, (uint8_t)0xe0, (uint8_t)0xe1, (uint8_t)0xe2, (uint8_t)0xe3, (uint8_t)0xe4, (uint8_t)0xe5, (uint8_t)0xe6, (uint8_t)0xe7, (uint8_t)0xe8, (uint8_t)0xe9, (uint8_t)0xea, (uint8_t)0xeb, (uint8_t)0xec, (uint8_t)0xed, (uint8_t)0xee, (uint8_t)0xef, (uint8_t)0xf0, (uint8_t)0xf1, (uint8_t)0xf2, (uint8_t)0xf3, (uint8_t)0xf4, (uint8_t)0xf5, (uint8_t)0xf6, (uint8_t)0xf7, (uint8_t)0xf8, (uint8_t)0xf9, (uint8_t)0xfa, (uint8_t)0xfb, (uint8_t)0xfc, (uint8_t)0xfd, (uint8_t)0xfe, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff, (uint8_t)0xff }; #else // uninitialized tables static uint8_t abs0[255 + 255 + 1]; static int8_t sclip1[1020 + 1020 + 1]; static int8_t sclip2[112 + 112 + 1]; static uint8_t clip1[255 + 511 + 1]; // We declare this variable 'volatile' to prevent instruction reordering // and make sure it's set to true _last_ (so as to be thread-safe) static volatile int tables_ok = 0; #endif const int8_t* const VP8ksclip1 = &sclip1[1020]; const int8_t* const VP8ksclip2 = &sclip2[112]; const uint8_t* const VP8kclip1 = &clip1[255]; const uint8_t* const VP8kabs0 = &abs0[255]; WEBP_TSAN_IGNORE_FUNCTION void VP8InitClipTables(void) { #if !defined(USE_STATIC_TABLES) int i; if (!tables_ok) { for (i = -255; i <= 255; ++i) { abs0[255 + i] = (i < 0) ? -i : i; } for (i = -1020; i <= 1020; ++i) { sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i; } for (i = -112; i <= 112; ++i) { sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i; } for (i = -255; i <= 255 + 255; ++i) { clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i; } tables_ok = 1; } #endif // USE_STATIC_TABLES } src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/meson.build000664 001750 001750 00000000647 15164251010 035624 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0source_file = [ 'bit_reader.h', 'bit_reader_inl.h', 'color_cache.h', 'endian_inl.h', 'huffman.h', 'quant_levels_dec.h', 'random.h', 'rescaler.h', 'utils.h', 'bit_reader.cpp', 'color_cache.cpp', 'huffman.cpp', 'quant_levels_dec.cpp', 'random.cpp', 'rescaler.cpp' ] webp_deb += [declare_dependency( include_directories : include_directories('.'), sources : source_file )]thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-regexp-object.h000664 001750 001750 00000015366 15164251010 046234 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_REGEXP_OBJECT_H #define ECMA_REGEXP_OBJECT_H #include "ecma-globals.h" #include "re-compiler.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaregexpobject ECMA RegExp object related routines * @{ */ /** * RegExp flags * Note: * This enum has to be kept in sync with jerry_regexp_flags_t. */ typedef enum { RE_FLAG_EMPTY = 0u, /* Empty RegExp flags */ RE_FLAG_GLOBAL = (1u << 1), /**< ECMA-262 v5, 15.10.7.2 */ RE_FLAG_IGNORE_CASE = (1u << 2), /**< ECMA-262 v5, 15.10.7.3 */ RE_FLAG_MULTILINE = (1u << 3), /**< ECMA-262 v5, 15.10.7.4 */ RE_FLAG_STICKY = (1u << 4), /**< ECMA-262 v6, 21.2.5.12 */ RE_FLAG_UNICODE = (1u << 5), /**< ECMA-262 v6, 21.2.5.15 */ RE_FLAG_DOTALL = (1u << 6) /**< ECMA-262 v9, 21.2.5.3 */ /* Bits from bit 13 is reserved for function types (see CBC_FUNCTION_TYPE_SHIFT). */ } ecma_regexp_flags_t; /** * Class escapes */ typedef enum { RE_ESCAPE__START, /**< escapes start */ RE_ESCAPE_DIGIT = RE_ESCAPE__START, /**< digit */ RE_ESCAPE_NOT_DIGIT, /**< not digit */ RE_ESCAPE_WORD_CHAR, /**< word char */ RE_ESCAPE_NOT_WORD_CHAR, /**< not word char */ RE_ESCAPE_WHITESPACE, /**< whitespace */ RE_ESCAPE_NOT_WHITESPACE, /**< not whitespace */ RE_ESCAPE__COUNT, /**< escape count */ } ecma_class_escape_t; /** * Character class flags escape count mask size. */ #define RE_CLASS_ESCAPE_COUNT_MASK_SIZE (3u) /** * Character class flags escape count mask. */ #define RE_CLASS_ESCAPE_COUNT_MASK ((1 << RE_CLASS_ESCAPE_COUNT_MASK_SIZE) - 1u) /** * Character class flags that are present in the upper bits of the class flags byte, while the 3 least significant bits * hold a value that contains the number of class escapes present in the character class. */ typedef enum { RE_CLASS_HAS_CHARS = (1 << 5), /**< contains individual characters */ RE_CLASS_HAS_RANGES = (1 << 6), /**< contains character ranges */ RE_CLASS_INVERT = (1 << 7), /**< inverted */ } ecma_char_class_flags_t; /** * Structure for matching capturing groups and storing their result */ typedef struct { const lit_utf8_byte_t *begin_p; /**< capture start pointer */ const lit_utf8_byte_t *end_p; /**< capture end pointer */ const uint8_t *bc_p; /**< group bytecode pointer */ uint32_t iterator; /**< iteration counter */ uint32_t subcapture_count; /**< number of nested capturing groups */ } ecma_regexp_capture_t; /** * Structure for matching non-capturing groups */ typedef struct { const lit_utf8_byte_t *begin_p; /**< substring start pointer */ const uint8_t *bc_p; /**< group bytecode pointer */ uint32_t iterator; /**< iteration counter */ uint32_t subcapture_start; /**< first nested capturing group index */ uint32_t subcapture_count; /**< number of nested capturing groups */ } ecma_regexp_non_capture_t; /** * Check if an ecma_regexp_capture_t contains a defined capture */ #define ECMA_RE_IS_CAPTURE_DEFINED(c) ((c)->begin_p != NULL) ecma_value_t ecma_regexp_get_capture_value (const ecma_regexp_capture_t *const capture_p); #if (JERRY_STACK_LIMIT != 0) /** * Value used ase result when stack limit is reached */ #define ECMA_RE_OUT_OF_STACK ((const lit_utf8_byte_t *) UINTPTR_MAX) /** * Checks if the stack limit has been reached during regexp matching */ #define ECMA_RE_STACK_LIMIT_REACHED(p) (JERRY_UNLIKELY (p == ECMA_RE_OUT_OF_STACK)) #else /* JERRY_STACK_LIMIT == 0 */ #define ECMA_RE_STACK_LIMIT_REACHED(p) (false) #endif /* JERRY_STACK_LIMIT != 0 */ /** * Offset applied to qmax when encoded into the bytecode. * * It's common for qmax to be Infinity, which is represented a UINT32_MAX. By applying the offset we are able to store * it in a single byte az zero. */ #define RE_QMAX_OFFSET 1 /** * RegExp executor context */ typedef struct { const lit_utf8_byte_t *input_start_p; /**< start of input string */ const lit_utf8_byte_t *input_end_p; /**< end of input string */ uint32_t captures_count; /**< number of capture groups */ uint32_t non_captures_count; /**< number of non-capture groups */ ecma_regexp_capture_t *captures_p; /**< capturing groups */ ecma_regexp_non_capture_t *non_captures_p; /**< non-capturing groups */ uint16_t flags; /**< RegExp flags */ uint8_t char_size; /**< size of encoded characters */ } ecma_regexp_ctx_t; /** * RegExpStringIterator object internal slots * * See also: * ECMA-262 v11, 21.2.7.2 */ typedef struct { ecma_extended_object_t header; /**< extended object part */ ecma_value_t iterating_regexp; /**< [[IteratingRegExp]] internal slot */ ecma_value_t iterated_string; /**< [[IteratedString]] internal slot */ } ecma_regexp_string_iterator_t; lit_code_point_t ecma_regexp_unicode_advance (const lit_utf8_byte_t **str_p, const lit_utf8_byte_t *end_p); ecma_object_t *ecma_op_regexp_alloc (ecma_object_t *new_target_obj_p); ecma_value_t ecma_regexp_exec_helper (ecma_object_t *regexp_object_p, ecma_string_t *input_string_p); ecma_string_t *ecma_regexp_read_pattern_str_helper (ecma_value_t pattern_arg); lit_code_point_t ecma_regexp_canonicalize_char (lit_code_point_t ch, bool unicode); ecma_value_t ecma_regexp_parse_flags (ecma_string_t *flags_str_p, uint16_t *flags_p); void ecma_regexp_create_and_initialize_props (ecma_object_t *re_object_p, ecma_string_t *source_p, uint16_t flags); ecma_value_t ecma_regexp_replace_helper (ecma_value_t this_arg, ecma_value_t string_arg, ecma_value_t replace_arg); ecma_value_t ecma_regexp_search_helper (ecma_value_t regexp_arg, ecma_value_t string_arg); ecma_value_t ecma_regexp_split_helper (ecma_value_t this_arg, ecma_value_t string_arg, ecma_value_t limit_arg); ecma_value_t ecma_regexp_match_helper (ecma_value_t this_arg, ecma_value_t string_arg); ecma_value_t ecma_op_regexp_exec (ecma_value_t this_arg, ecma_string_t *str_p); ecma_value_t ecma_op_create_regexp_from_bytecode (ecma_object_t *regexp_obj_p, re_compiled_code_t *bc_p); ecma_value_t ecma_op_create_regexp_from_pattern (ecma_object_t *regexp_obj_p, ecma_value_t pattern_value, ecma_value_t flags_value); ecma_value_t ecma_op_create_regexp_with_flags (ecma_object_t *regexp_obj_p, ecma_value_t pattern_value, uint16_t flags); /** * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ #endif /* !ECMA_REGEXP_OBJECT_H */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/re-compiler-context.h000664 001750 001750 00000003163 15164251010 046161 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 RE_COMPILER_CONTEXT_H #define RE_COMPILER_CONTEXT_H #include "re-token.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup parser Parser * @{ * * \addtogroup regexparser Regular expression * @{ * * \addtogroup regexparser_compiler Compiler * @{ */ /** * RegExp compiler context */ typedef struct { const lit_utf8_byte_t *input_start_p; /**< start of input pattern */ const lit_utf8_byte_t *input_curr_p; /**< current position in input pattern */ const lit_utf8_byte_t *input_end_p; /**< end of input pattern */ uint8_t *bytecode_start_p; /**< start of bytecode block */ size_t bytecode_size; /**< size of bytecode */ uint32_t captures_count; /**< number of capture groups */ uint32_t non_captures_count; /**< number of non-capture groups */ int groups_count; /**< number of groups */ uint16_t flags; /**< RegExp flags */ re_token_t token; /**< current token */ } re_compiler_ctx_t; /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ #endif /* !RE_COMPILER_CONTEXT_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlRenderTask.cpp000664 001750 001750 00000060074 15164251010 037243 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlRenderTask.h" #include "tvgGlProgram.h" #include "tvgGlRenderPass.h" /************************************************************************/ /* GlRenderTask Class Implementation */ /************************************************************************/ GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(program) { mVertexLayout.push(other->mVertexLayout); mViewport = other->mViewport; mIndexOffset = other->mIndexOffset; mIndexCount = other->mIndexCount; mViewMatrix = other->mViewMatrix; mUseViewMatrix = other->mUseViewMatrix; } void GlRenderTask::run() { // bind shader mProgram->load(); int32_t dLoc = mProgram->getUniformLocation("uDepth"); if (dLoc >= 0) { // fixme: prevent compiler warning: macro expands to multiple statements [-Wmultistatement-macros] GL_CHECK(glUniform1f(dLoc, mDrawDepth)); } int32_t vLoc = mProgram->getUniformLocation("uViewMatrix"); if (vLoc >= 0) { const auto& viewMatrix = mUseViewMatrix ? mViewMatrix : tvg::identity(); float viewMat3[9]; getMatrix3(viewMatrix, viewMat3); GL_CHECK(glUniformMatrix3fv(vLoc, 1, GL_FALSE, viewMat3)); } // setup scissor rect GL_CHECK(glScissor(mViewport.sx(), mViewport.sy(), mViewport.sw(), mViewport.sh())); // setup attribute layout for (uint32_t i = 0; i < mVertexLayout.count; i++) { const auto &layout = mVertexLayout[i]; GL_CHECK(glEnableVertexAttribArray(layout.index)); GL_CHECK(glVertexAttribPointer(layout.index, layout.size, GL_FLOAT, GL_FALSE, layout.stride, reinterpret_cast(layout.offset))); } // binding uniforms for (uint32_t i = 0; i < mBindingResources.count; i++) { const auto& binding = mBindingResources[i]; if (binding.type == GlBindingType::kTexture) { GL_CHECK(glActiveTexture(GL_TEXTURE0 + binding.bindPoint)); GL_CHECK(glBindTexture(GL_TEXTURE_2D, binding.gBufferId)); mProgram->setUniform1Value(binding.location, 1, (int32_t*)&binding.bindPoint); } else if (binding.type == GlBindingType::kUniformBuffer) { GL_CHECK(glUniformBlockBinding(mProgram->getProgramId(), binding.location, binding.bindPoint)); GL_CHECK(glBindBufferRange(GL_UNIFORM_BUFFER, binding.bindPoint, binding.gBufferId, binding.bufferOffset, binding.bufferRange)); } } GL_CHECK(glDrawElements(GL_TRIANGLES, mIndexCount, GL_UNSIGNED_INT, reinterpret_cast(mIndexOffset))); // setup attribute layout for (uint32_t i = 0; i < mVertexLayout.count; i++) { const auto &layout = mVertexLayout[i]; GL_CHECK(glDisableVertexAttribArray(layout.index)); } } void GlRenderTask::addVertexLayout(const GlVertexLayout &layout) { mVertexLayout.push(layout); } void GlRenderTask::addBindResource(const GlBindingResource &binding) { mBindingResources.push(binding); } void GlRenderTask::setDrawRange(uint32_t offset, uint32_t count) { mIndexOffset = offset; mIndexCount = count; } void GlRenderTask::setViewport(const RenderRegion &viewport) { mViewport = viewport; if (mViewport.max.x < mViewport.min.x) mViewport.max.x = mViewport.min.x; if (mViewport.max.y < mViewport.min.y) mViewport.max.y = mViewport.min.y; } /************************************************************************/ /* GlStencilCoverTask Class Implementation */ /************************************************************************/ GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode) :GlRenderTask(nullptr), mStencilTask(stencil), mCoverTask(cover), mStencilMode(mode) { } GlStencilCoverTask::~GlStencilCoverTask() { delete mStencilTask; delete mCoverTask; } void GlStencilCoverTask::run() { GL_CHECK(glEnable(GL_STENCIL_TEST)); if (mStencilMode == GlStencilMode::Stroke) { GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x1, 0xFF)); GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); } else { GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x0, 0xFF)); GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x0, 0xFF)); GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); } GL_CHECK(glColorMask(0, 0, 0, 0)); mStencilTask->run(); if (mStencilMode == GlStencilMode::FillEvenOdd) { GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x00, 0x01)); GL_CHECK(glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE)); } else { GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); } GL_CHECK(glColorMask(1, 1, 1, 1)); mCoverTask->run(); GL_CHECK(glDisable(GL_STENCIL_TEST)); } void GlStencilCoverTask::normalizeDrawDepth(int32_t maxDepth) { mCoverTask->normalizeDrawDepth(maxDepth); mStencilTask->normalizeDrawDepth(maxDepth); } /************************************************************************/ /* GlComposeTask Class Implementation */ /************************************************************************/ GlComposeTask::GlComposeTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array&& tasks) :GlRenderTask(program) ,mTargetFbo(target), mFbo(fbo), mTasks() { mTasks.push(tasks); tasks.clear(); } GlComposeTask::~GlComposeTask() { ARRAY_FOREACH(p, mTasks) delete(*p); mTasks.clear(); } void GlComposeTask::run() { GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getSelfFbo())); // we must clear all area of fbo GL_CHECK(glViewport(0, 0, mFbo->width, mFbo->height)); GL_CHECK(glScissor(0, 0, mFbo->width, mFbo->height)); GL_CHECK(glClearColor(0, 0, 0, 0)); GL_CHECK(glClearStencil(0)); #ifdef THORVG_GL_TARGET_GLES GL_CHECK(glClearDepthf(0.0)); #else GL_CHECK(glClearDepth(0.0)); #endif GL_CHECK(glDepthMask(1)); GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); GL_CHECK(glDepthMask(0)); GL_CHECK(glViewport(0, 0, mRenderWidth, mRenderHeight)); GL_CHECK(glScissor(0, 0, mRenderWidth, mRenderHeight)); ARRAY_FOREACH(p, mTasks) { (*p)->run(); } #if defined(THORVG_GL_TARGET_GLES) // only OpenGLES has tiled base framebuffer and discard function GLenum attachments[2] = {GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT }; GL_CHECK(glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments)); #endif // reset scissor box GL_CHECK(glScissor(0, 0, mFbo->width, mFbo->height)); onResolve(); } GLuint GlComposeTask::getSelfFbo() { return mFbo->fbo; } GLuint GlComposeTask::getResolveFboId() { return mFbo->resolvedFbo; } void GlComposeTask::onResolve() { GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, getSelfFbo())); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getResolveFboId())); GL_CHECK(glBlitFramebuffer(0, 0, mRenderWidth, mRenderHeight, 0, 0, mRenderWidth, mRenderHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST)); } /************************************************************************/ /* GlBlitTask Class Implementation */ /************************************************************************/ GlBlitTask::GlBlitTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array&& tasks) : GlComposeTask(program, target, fbo, std::move(tasks)), mColorTex(fbo->colorTex) { } void GlBlitTask::run() { GlComposeTask::run(); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo())); GL_CHECK(glViewport(mTargetViewport.x(), mTargetViewport.y(), mTargetViewport.w(), mTargetViewport.h())); if (mClearBuffer) { GL_CHECK(glClearColor(0, 0, 0, 0)); GL_CHECK(glClear(GL_COLOR_BUFFER_BIT)); } GL_CHECK(glDisable(GL_DEPTH_TEST)); // make sure the blending is correct GL_CHECK(glEnable(GL_BLEND)); GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); GlRenderTask::run(); } /************************************************************************/ /* GlDrawBlitTask Class Implementation */ /************************************************************************/ GlDrawBlitTask::GlDrawBlitTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array&& tasks) : GlComposeTask(program, target, fbo, std::move(tasks)) { } GlDrawBlitTask::~GlDrawBlitTask() { if (mPrevTask) delete mPrevTask; } void GlDrawBlitTask::run() { if (mPrevTask) mPrevTask->run(); GlComposeTask::run(); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo())); GL_CHECK(glViewport(0, 0, mParentWidth, mParentHeight)); GL_CHECK(glScissor(0, 0, mParentWidth, mParentHeight)); GlRenderTask::run(); } /************************************************************************/ /* GlSceneBlendTask Class Implementation */ /************************************************************************/ GlSceneBlendTask::GlSceneBlendTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array&& tasks) : GlComposeTask(program, target, fbo, std::move(tasks)) { } GlSceneBlendTask::~GlSceneBlendTask() { } void GlSceneBlendTask::run() { GlComposeTask::run(); const auto& vp = getViewport(); const auto width = mSrcFbo->width; const auto height = mSrcFbo->height; if (width <= 0 || height <= 0) return; GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mSrcFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->resolvedFbo)); #if defined(THORVG_GL_TARGET_GL) const auto& srcVp = mSrcFbo->viewport; // Copy current target into dstCopyFbo for blending. GL_CHECK(glViewport(0, 0, srcVp.w(), srcVp.h())); GL_CHECK(glScissor(0, 0, srcVp.w(), srcVp.h())); GL_CHECK(glBlitFramebuffer(vp.min.x, vp.min.y, vp.max.x, vp.max.y, 0, 0, vp.w(), vp.h(), GL_COLOR_BUFFER_BIT, GL_LINEAR)); #else // TODO: create partial buffer when MSAA is disabled GL_CHECK(glViewport(0, 0, width, height)); GL_CHECK(glScissor(vp.min.x, vp.min.y, vp.w(), vp.h())); GL_CHECK(glBlitFramebuffer(vp.min.x, vp.min.y, vp.max.x, vp.max.y, vp.min.x, vp.min.y, vp.max.x, vp.max.y, GL_COLOR_BUFFER_BIT, GL_NEAREST)); #endif GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo())); GL_CHECK(glViewport(0, 0, mParentWidth, mParentHeight)); GL_CHECK(glScissor(0, 0, mParentWidth, mParentHeight)); GL_CHECK(glDisable(GL_DEPTH_TEST)); GL_CHECK(glBlendFunc(GL_ONE, GL_ZERO)); GlRenderTask::run(); GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); GL_CHECK(glEnable(GL_DEPTH_TEST)); } /************************************************************************/ /* GlClipTask Class Implementation */ /************************************************************************/ GlClipTask::GlClipTask(GlRenderTask* clip, GlRenderTask* mask) :GlRenderTask(nullptr), mClipTask(clip), mMaskTask(mask) {} GlClipTask::~GlClipTask() { delete mClipTask; delete mMaskTask; } void GlClipTask::run() { GL_CHECK(glEnable(GL_STENCIL_TEST)); GL_CHECK(glColorMask(0, 0, 0, 0)); // draw clip path as normal stencil mask GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x1, 0xFF)); GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x1, 0xFF)); GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); mClipTask->run(); // draw clip mask GL_CHECK(glDepthMask(1)); GL_CHECK(glStencilFunc(GL_EQUAL, 0x0, 0xFF)); GL_CHECK(glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE)); mMaskTask->run(); GL_CHECK(glColorMask(1, 1, 1, 1)); GL_CHECK(glDepthMask(0)); GL_CHECK(glDisable(GL_STENCIL_TEST)); } void GlClipTask::normalizeDrawDepth(int32_t maxDepth) { mClipTask->normalizeDrawDepth(maxDepth); mMaskTask->normalizeDrawDepth(maxDepth); } /************************************************************************/ /* GlDirectBlendTask Class Implementation */ /************************************************************************/ GlDirectBlendTask::GlDirectBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, const RenderRegion& copyRegion) : GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mCopyRegion(copyRegion) { } void GlDirectBlendTask::run() { auto width = mCopyRegion.w(); auto height = mCopyRegion.h(); if (width <= 0 || height <= 0) return; auto x = mCopyRegion.sx(); auto y = mCopyRegion.sy(); const auto fboW = mDstFbo->width; const auto fboH = mDstFbo->height; if (fboW <= 0 || fboH <= 0) return; GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->resolvedFbo)); #if defined(THORVG_GL_TARGET_GL) GL_CHECK(glViewport(0, 0, width, height)); GL_CHECK(glScissor(0, 0, width, height)); GL_CHECK(glBlitFramebuffer(x, y, x + width, y + height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR)); #else // TODO: create partial buffer when MSAA is disabled GL_CHECK(glViewport(0, 0, fboW, fboH)); GL_CHECK(glScissor(x, y, width, height)); GL_CHECK(glBlitFramebuffer(x, y, x + width, y + height, x, y, x + width, y + height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); #endif GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->fbo)); const auto& dstVp = mDstFbo->viewport; GL_CHECK(glViewport(0, 0, dstVp.w(), dstVp.h())); GL_CHECK(glBlendFunc(GL_ONE, GL_ZERO)); GlRenderTask::run(); GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); } /************************************************************************/ /* GlComplexBlendTask Class Implementation */ /************************************************************************/ GlComplexBlendTask::GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask) : GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mStencilTask(stencilTask), mComposeTask(composeTask) { } GlComplexBlendTask::~GlComplexBlendTask() { delete mStencilTask; delete mComposeTask; } void GlComplexBlendTask::run() { mComposeTask->run(); const auto& vp = getViewport(); const auto width = mDstFbo->width; const auto height = mDstFbo->height; if (width <= 0 || height <= 0) return; GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->resolvedFbo)); #if defined(THORVG_GL_TARGET_GL) const auto& dstVp = mDstFbo->viewport; // copy the current fbo to the dstCopyFbo GL_CHECK(glViewport(0, 0, dstVp.w(), dstVp.h())); GL_CHECK(glScissor(0, 0, dstVp.w(), dstVp.h())); GL_CHECK(glBlitFramebuffer(vp.min.x, vp.min.y, vp.max.x, vp.max.y, 0, 0, vp.w(), vp.h(), GL_COLOR_BUFFER_BIT, GL_LINEAR)); #else // TODO: create partial buffer when MSAA is disabled GL_CHECK(glViewport(0, 0, width, height)); GL_CHECK(glScissor(vp.min.x, vp.min.y, vp.w(), vp.h())); GL_CHECK(glBlitFramebuffer(vp.min.x, vp.min.y, vp.max.x, vp.max.y, vp.min.x, vp.min.y, vp.max.x, vp.max.y, GL_COLOR_BUFFER_BIT, GL_NEAREST)); #endif GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glEnable(GL_STENCIL_TEST)); GL_CHECK(glColorMask(0, 0, 0, 0)); GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x0, 0xFF)); GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x0, 0xFF)); GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); mStencilTask->run(); GL_CHECK(glColorMask(1, 1, 1, 1)); GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); GL_CHECK(glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE)); GL_CHECK(glBlendFunc(GL_ONE, GL_ZERO)); GlRenderTask::run(); GL_CHECK(glDisable(GL_STENCIL_TEST)); GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); } void GlComplexBlendTask::normalizeDrawDepth(int32_t maxDepth) { mStencilTask->normalizeDrawDepth(maxDepth); GlRenderTask::normalizeDrawDepth(maxDepth); } /************************************************************************/ /* GlGaussianBlurTask Class Implementation */ /************************************************************************/ void GlGaussianBlurTask::run() { const auto vp = getViewport(); const auto width = mDstFbo->width; const auto height = mDstFbo->height; // get targets handles GLuint dstCopyTexId0 = mDstCopyFbo0->colorTex; GLuint dstCopyTexId1 = mDstCopyFbo1->colorTex; // get programs properties GlProgram* programHorz = horzTask->getProgram(); GlProgram* programVert = vertTask->getProgram(); GLint horzSrcTextureLoc = programHorz->getUniformLocation("uSrcTexture"); GLint vertSrcTextureLoc = programVert->getUniformLocation("uSrcTexture"); GL_CHECK(glViewport(0, 0, width, height)); GL_CHECK(glScissor(0, 0, width, height)); // we need to make a full copy of dst to intermediate buffers to be sure that they don’t contain prev data. GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo0->resolvedFbo)); GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glDisable(GL_BLEND)); if (effect->direction == 0) { GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo1->resolvedFbo)); GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); // horizontal blur GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstCopyFbo1->resolvedFbo)); horzTask->setViewport(vp); horzTask->addBindResource({ 0, dstCopyTexId0, horzSrcTextureLoc }); horzTask->run(); // vertical blur GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->fbo)); vertTask->setViewport(vp); vertTask->addBindResource({ 0, dstCopyTexId1, vertSrcTextureLoc }); vertTask->run(); } // horizontal else if (effect->direction == 1) { horzTask->setViewport(vp); horzTask->addBindResource({ 0, dstCopyTexId0, horzSrcTextureLoc }); horzTask->run(); } // vertical else if (effect->direction == 2) { vertTask->setViewport(vp); vertTask->addBindResource({ 0, dstCopyTexId0, vertSrcTextureLoc }); vertTask->run(); } GL_CHECK(glEnable(GL_BLEND)); } /************************************************************************/ /* GlEffectDropShadowTask Class Implementation */ /************************************************************************/ void GlEffectDropShadowTask::run() { const auto vp = getViewport(); const auto width = mDstFbo->width; const auto height = mDstFbo->height; // get targets handles GLuint dstCopyTexId0 = mDstCopyFbo0->colorTex; GLuint dstCopyTexId1 = mDstCopyFbo1->colorTex; // get programs properties GlProgram* programHorz = horzTask->getProgram(); GlProgram* programVert = vertTask->getProgram(); GLint horzSrcTextureLoc = programHorz->getUniformLocation("uSrcTexture"); GLint vertSrcTextureLoc = programVert->getUniformLocation("uSrcTexture"); GLint srcTextureLoc = getProgram()->getUniformLocation("uSrcTexture"); GLint blrTextureLoc = getProgram()->getUniformLocation("uBlrTexture"); addBindResource({ 0, dstCopyTexId0, srcTextureLoc }); addBindResource({ 1, dstCopyTexId1, blrTextureLoc }); GL_CHECK(glViewport(0, 0, width, height)); GL_CHECK(glScissor(0, 0, width, height)); // we need to make a full copy of dst to intermediate buffers to be sure that they don’t contain prev data. GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo0->resolvedFbo)); GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo1->resolvedFbo)); GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); GL_CHECK(glDisable(GL_BLEND)); // when sigma is 0, no blur is applied, and the original image is used directly as the shadow. if (!tvg::zero(effect->sigma)) { // horizontal blur GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstCopyFbo0->resolvedFbo)); horzTask->setViewport(vp); horzTask->addBindResource({ 0, dstCopyTexId1, horzSrcTextureLoc }); horzTask->run(); // vertical blur GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstCopyFbo1->resolvedFbo)); vertTask->setViewport(vp); vertTask->addBindResource({ 0, dstCopyTexId0, vertSrcTextureLoc }); vertTask->run(); // copy original image to intermediate buffer GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo0->resolvedFbo)); GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); } // run drop shadow effect GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->fbo)); GlRenderTask::run(); GL_CHECK(glEnable(GL_BLEND)); } /************************************************************************/ /* GlEffectColorTransformTask Class Implementation */ /************************************************************************/ void GlEffectColorTransformTask::run() { const auto width = mDstFbo->width; const auto height = mDstFbo->height; // get targets handles and pass to shader GLuint dstCopyTexId = mDstCopyFbo->colorTex; GLint srcTextureLoc = getProgram()->getUniformLocation("uSrcTexture"); addBindResource({ 0, dstCopyTexId, srcTextureLoc }); GL_CHECK(glViewport(0, 0, width, height)); GL_CHECK(glScissor(0, 0, width, height)); // we need to make a full copy of dst to intermediate buffers to be sure that they don’t contain prev data. GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->fbo)); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->resolvedFbo)); GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->fbo)); // run transform GL_CHECK(glDisable(GL_BLEND)); GlRenderTask::run(); GL_CHECK(glEnable(GL_BLEND)); } glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/common.h000664 001750 001750 00000010435 15164251010 042673 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 COMMON_H #define COMMON_H #include #include #include #include #include /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_utils Utility * @{ */ #include "ecma-globals.h" #include "ecma-regexp-object.h" #include "jerry-config.h" #include "jmem.h" /* Immediate management. */ /** * Literal types. * * The LEXER_UNUSED_LITERAL type is internal and * used for various purposes. */ typedef enum { /* The LEXER_IS_IDENT_OR_STRING macro must be updated if the order is changed. */ LEXER_IDENT_LITERAL = 0, /**< identifier literal */ LEXER_STRING_LITERAL = 1, /**< string literal */ LEXER_NUMBER_LITERAL = 2, /**< number literal */ LEXER_FUNCTION_LITERAL = 3, /**< function literal */ LEXER_REGEXP_LITERAL = 4, /**< regexp literal */ LEXER_UNUSED_LITERAL = 5, /**< unused literal, can only be used by the byte code generator. */ LEXER_NEW_IDENT_LITERAL = 6, /**< new local variable, can only be used by the byte code generator. */ } lexer_literal_type_t; /** * Checks whether the literal type is identifier or string. */ #define LEXER_IS_IDENT_OR_STRING(literal_type) ((literal_type) <= LEXER_STRING_LITERAL) /** * Flag bits for status_flags member of lexer_literal_t. */ typedef enum { LEXER_FLAG_USED = (1 << 0), /**< this local identifier needs to be stored in the constant pool */ LEXER_FLAG_FUNCTION_ARGUMENT = (1 << 1), /**< this local identifier is a function argument */ LEXER_FLAG_SOURCE_PTR = (1 << 2), /**< the literal is directly referenced in the source code * (no need to allocate memory) */ LEXER_FLAG_LATE_INIT = (1 << 3), /**< initialize this variable after the byte code is freed */ LEXER_FLAG_ASCII = (1 << 4), /**< the literal contains only ascii characters */ LEXER_FLAG_GLOBAL = (1 << 5), /**< this local identifier is not a let or const declaration */ } lexer_literal_status_flags_t; /** * Type of property length. */ typedef uint16_t prop_length_t; /** * Literal data. */ typedef struct { union { ecma_value_t value; /**< literal value (not processed by the parser) */ const uint8_t *char_p; /**< character value */ ecma_compiled_code_t *bytecode_p; /**< compiled function or regexp pointer */ uint32_t source_data; /**< encoded source literal */ } u; #if JERRY_PARSER_DUMP_BYTE_CODE struct #else /* !JERRY_PARSER_DUMP_BYTE_CODE */ union #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ { prop_length_t length; /**< length of ident / string literal */ uint16_t index; /**< real index during post processing */ } prop; uint8_t type; /**< type of the literal */ uint8_t status_flags; /**< status flags */ } lexer_literal_t; void util_free_literal (lexer_literal_t *literal_p); #if JERRY_PARSER_DUMP_BYTE_CODE void util_print_literal (lexer_literal_t *); #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ /** * Source code line counter type. */ typedef uint32_t parser_line_counter_t; /** * Source code as character data. */ typedef struct { const uint8_t *source_p; /**< valid UTF-8 source code */ size_t source_size; /**< size of the source code */ } parser_source_char_t; /* TRY/CATCH block */ #define PARSER_TRY_CONTEXT(context_name) jmp_buf context_name #define PARSER_THROW(context_name) longjmp (context_name, 1); #define PARSER_TRY(context_name) \ { \ if (!setjmp (context_name)) \ { #define PARSER_CATCH \ } \ else \ { #define PARSER_TRY_END \ } \ } /** * @} * @} * @} */ #endif /* !COMMON_H */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-property-hashmap.h000664 001750 001750 00000005734 15164251010 045526 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_PROPERTY_HASHMAP_H #define ECMA_PROPERTY_HASHMAP_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmapropertyhashmap Property hashmap * @{ */ /** * Recommended minimum number of items in a property cache. */ #define ECMA_PROPERTY_HASHMAP_MINIMUM_SIZE 32 /** * Property hash. */ typedef struct { ecma_property_header_t header; /**< header of the property */ uint32_t max_property_count; /**< maximum property count (power of 2) */ uint32_t null_count; /**< number of NULLs in the map */ uint32_t unused_count; /**< number of unused entries in the map */ /* * The hash is followed by max_property_count ecma_cpointer_t * compressed pointers and (max_property_count + 7) / 8 bytes * which stores a flag for each compressed pointer. * * If the compressed pointer is equal to ECMA_NULL_POINTER * - flag is cleared if the entry is NULL * - flag is set if the entry is deleted * * If the compressed pointer is not equal to ECMA_NULL_POINTER * - flag is cleared if the first entry of a property pair is referenced * - flag is set if the second entry of a property pair is referenced */ } ecma_property_hashmap_t; #if JERRY_PROPERTY_HASHMAP /** * Simple ecma values */ typedef enum { ECMA_PROPERTY_HASHMAP_DELETE_NO_HASHMAP, /**< object has no hashmap */ ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP, /**< object has hashmap */ ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP, /**< hashmap should be recreated */ } ecma_property_hashmap_delete_status; void ecma_property_hashmap_create (ecma_object_t *object_p); void ecma_property_hashmap_free (ecma_object_t *object_p); void ecma_property_hashmap_insert (ecma_object_t *object_p, ecma_string_t *name_p, ecma_property_pair_t *property_pair_p, int property_index); ecma_property_hashmap_delete_status ecma_property_hashmap_delete (ecma_object_t *object_p, jmem_cpointer_t name_cp, ecma_property_t *property_p); ecma_property_t *ecma_property_hashmap_find (ecma_property_hashmap_t *hashmap_p, ecma_string_t *name_p, jmem_cpointer_t *property_real_name_cp); #endif /* JERRY_PROPERTY_HASHMAP */ /** * @} * @} */ #endif /* !ECMA_PROPERTY_HASHMAP_H */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-property-hashmap.cpp000664 001750 001750 00000040546 15164251010 046061 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-property-hashmap.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "jcontext.h" #include "jrt-libc-includes.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmapropertyhashmap Property hashmap * @{ */ #if JERRY_PROPERTY_HASHMAP /** * Compute the total size of the property hashmap. */ #define ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE(max_property_count) \ (sizeof (ecma_property_hashmap_t) + (max_property_count * sizeof (jmem_cpointer_t)) + (max_property_count >> 3)) /** * Number of items in the stepping table. */ #define ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS 8 /** * Stepping values for searching items in the hashmap. */ static const uint8_t ecma_property_hashmap_steps[ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS] JERRY_ATTR_CONST_DATA = { 3, 5, 7, 11, 13, 17, 19, 23 }; /** * Get the value of a bit in a bitmap. */ #define ECMA_PROPERTY_HASHMAP_GET_BIT(byte_p, index) ((byte_p)[(index) >> 3] & (1 << ((index) &0x7))) /** * Clear the value of a bit in a bitmap. */ #define ECMA_PROPERTY_HASHMAP_CLEAR_BIT(byte_p, index) \ ((byte_p)[(index) >> 3] = (uint8_t) ((byte_p)[(index) >> 3] & ~(1 << ((index) &0x7)))) /** * Set the value of a bit in a bitmap. */ #define ECMA_PROPERTY_HASHMAP_SET_BIT(byte_p, index) \ ((byte_p)[(index) >> 3] = (uint8_t) ((byte_p)[(index) >> 3] | (1 << ((index) &0x7)))) /** * Create a new property hashmap for the object. * The object must not have a property hashmap. */ void ecma_property_hashmap_create (ecma_object_t *object_p) /**< object */ { if (JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) != ECMA_PROP_HASHMAP_ALLOC_ON) { return; } jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp; if (prop_iter_cp == JMEM_CP_NULL) { return; } uint32_t named_property_count = 0; while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if (prop_iter_p->types[i] != ECMA_PROPERTY_TYPE_DELETED) { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[i])); named_property_count++; } } prop_iter_cp = prop_iter_p->next_property_cp; } if (named_property_count < (ECMA_PROPERTY_HASHMAP_MINIMUM_SIZE / 2)) { return; } /* The max_property_count must be power of 2. */ uint32_t max_property_count = ECMA_PROPERTY_HASHMAP_MINIMUM_SIZE; /* At least 1/3 items must be NULL. */ while (max_property_count < (named_property_count + (named_property_count >> 1))) { max_property_count <<= 1; } size_t total_size = ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE (max_property_count); ecma_property_hashmap_t *hashmap_p = (ecma_property_hashmap_t *) jmem_heap_alloc_block_null_on_error (total_size); if (hashmap_p == NULL) { return; } memset (hashmap_p, 0, total_size); hashmap_p->header.types[0] = ECMA_PROPERTY_TYPE_HASHMAP; hashmap_p->header.next_property_cp = object_p->u1.property_list_cp; hashmap_p->max_property_count = max_property_count; hashmap_p->null_count = max_property_count - named_property_count; hashmap_p->unused_count = max_property_count - named_property_count; jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1); uint8_t *bits_p = (uint8_t *) (pair_list_p + max_property_count); uint32_t mask = max_property_count - 1; prop_iter_cp = object_p->u1.property_list_cp; ECMA_SET_NON_NULL_POINTER (object_p->u1.property_list_cp, hashmap_p); while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if (prop_iter_p->types[i] == ECMA_PROPERTY_TYPE_DELETED) { continue; } JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[i])); ecma_property_pair_t *property_pair_p = (ecma_property_pair_t *) prop_iter_p; uint32_t entry_index = ecma_string_get_property_name_hash (prop_iter_p->types[i], property_pair_p->names_cp[i]); uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)]; entry_index &= mask; #ifndef JERRY_NDEBUG /* Because max_property_count (power of 2) and step (a prime * number) are relative primes, all entries of the hashmap are * visited exactly once before the start entry index is reached * again. Furthermore because at least one NULL is present in * the hashmap, the while loop must be terminated before the * the starting index is reached again. */ uint32_t start_entry_index = entry_index; #endif /* !JERRY_NDEBUG */ while (pair_list_p[entry_index] != ECMA_NULL_POINTER) { entry_index = (entry_index + step) & mask; #ifndef JERRY_NDEBUG JERRY_ASSERT (entry_index != start_entry_index); #endif /* !JERRY_NDEBUG */ } ECMA_SET_NON_NULL_POINTER (pair_list_p[entry_index], property_pair_p); if (i != 0) { ECMA_PROPERTY_HASHMAP_SET_BIT (bits_p, entry_index); } } prop_iter_cp = prop_iter_p->next_property_cp; } } /* ecma_property_hashmap_create */ /** * Free the hashmap of the object. * The object must have a property hashmap. */ void ecma_property_hashmap_free (ecma_object_t *object_p) /**< object */ { /* Property hash must be exists and must be the first property. */ JERRY_ASSERT (object_p->u1.property_list_cp != JMEM_CP_NULL); ecma_property_header_t *property_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, object_p->u1.property_list_cp); JERRY_ASSERT (property_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP); ecma_property_hashmap_t *hashmap_p = (ecma_property_hashmap_t *) property_p; object_p->u1.property_list_cp = property_p->next_property_cp; jmem_heap_free_block (hashmap_p, ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE (hashmap_p->max_property_count)); } /* ecma_property_hashmap_free */ /** * Insert named property into the hashmap. */ void ecma_property_hashmap_insert (ecma_object_t *object_p, /**< object */ ecma_string_t *name_p, /**< name of the property */ ecma_property_pair_t *property_pair_p, /**< property pair */ int property_index) /**< property index in the pair (0 or 1) */ { JERRY_ASSERT (property_pair_p != NULL); ecma_property_hashmap_t *hashmap_p = ECMA_GET_NON_NULL_POINTER (ecma_property_hashmap_t, object_p->u1.property_list_cp); JERRY_ASSERT (hashmap_p->header.types[0] == ECMA_PROPERTY_TYPE_HASHMAP); /* The NULLs are reduced below 1/8 of the hashmap. */ if (hashmap_p->null_count < (hashmap_p->max_property_count >> 3)) { ecma_property_hashmap_free (object_p); ecma_property_hashmap_create (object_p); return; } JERRY_ASSERT (property_index < ECMA_PROPERTY_PAIR_ITEM_COUNT); uint32_t entry_index = ecma_string_hash (name_p); uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)]; uint32_t mask = hashmap_p->max_property_count - 1; entry_index &= mask; #ifndef JERRY_NDEBUG /* See the comment for this variable in ecma_property_hashmap_create. */ uint32_t start_entry_index = entry_index; #endif /* !JERRY_NDEBUG */ jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1); while (pair_list_p[entry_index] != ECMA_NULL_POINTER) { entry_index = (entry_index + step) & mask; #ifndef JERRY_NDEBUG JERRY_ASSERT (entry_index != start_entry_index); #endif /* !JERRY_NDEBUG */ } ECMA_SET_NON_NULL_POINTER (pair_list_p[entry_index], property_pair_p); uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count); bits_p += (entry_index >> 3); mask = (uint32_t) (1 << (entry_index & 0x7)); if (!(*bits_p & mask)) { /* Deleted entries also has ECMA_NULL_POINTER * value, but they are not NULL values. */ hashmap_p->null_count--; JERRY_ASSERT (hashmap_p->null_count > 0); } hashmap_p->unused_count--; JERRY_ASSERT (hashmap_p->unused_count > 0); if (property_index == 0) { *bits_p = (uint8_t) ((*bits_p) & ~mask); } else { *bits_p = (uint8_t) ((*bits_p) | mask); } } /* ecma_property_hashmap_insert */ /** * Delete named property from the hashmap. * * @return ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP if hashmap should be recreated * ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP otherwise */ ecma_property_hashmap_delete_status ecma_property_hashmap_delete (ecma_object_t *object_p, /**< object */ jmem_cpointer_t name_cp, /**< property name */ ecma_property_t *property_p) /**< property */ { ecma_property_hashmap_t *hashmap_p = ECMA_GET_NON_NULL_POINTER (ecma_property_hashmap_t, object_p->u1.property_list_cp); JERRY_ASSERT (hashmap_p->header.types[0] == ECMA_PROPERTY_TYPE_HASHMAP); hashmap_p->unused_count++; /* The NULLs are above 3/4 of the hashmap. */ if (hashmap_p->unused_count > ((hashmap_p->max_property_count * 3) >> 2)) { return ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP; } uint32_t entry_index = ecma_string_get_property_name_hash (*property_p, name_cp); uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)]; uint32_t mask = hashmap_p->max_property_count - 1; jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1); uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count); entry_index &= mask; #ifndef JERRY_NDEBUG /* See the comment for this variable in ecma_property_hashmap_create. */ uint32_t start_entry_index = entry_index; #endif /* !JERRY_NDEBUG */ while (true) { if (pair_list_p[entry_index] != ECMA_NULL_POINTER) { size_t offset = 0; if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)) { offset = 1; } ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t, pair_list_p[entry_index]); if ((property_pair_p->header.types + offset) == property_p) { JERRY_ASSERT (property_pair_p->names_cp[offset] == name_cp); pair_list_p[entry_index] = ECMA_NULL_POINTER; ECMA_PROPERTY_HASHMAP_SET_BIT (bits_p, entry_index); return ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP; } } else { /* Must be a deleted entry. */ JERRY_ASSERT (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)); } entry_index = (entry_index + step) & mask; #ifndef JERRY_NDEBUG JERRY_ASSERT (entry_index != start_entry_index); #endif /* !JERRY_NDEBUG */ } } /* ecma_property_hashmap_delete */ /** * Find a named property. * * @return pointer to the property if found or NULL otherwise */ ecma_property_t * ecma_property_hashmap_find (ecma_property_hashmap_t *hashmap_p, /**< hashmap */ ecma_string_t *name_p, /**< property name */ jmem_cpointer_t *property_real_name_cp) /**< [out] property real name */ { #ifndef JERRY_NDEBUG /* A sanity check in debug mode: a named property must be present * in both the property hashmap and in the property chain, or missing * from both data collection. The following code checks the property * chain, and sets the property_found variable. */ bool property_found = false; jmem_cpointer_t prop_iter_cp = hashmap_p->header.next_property_cp; while (prop_iter_cp != JMEM_CP_NULL && !property_found) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[i])) { if (ecma_string_compare_to_property_name (prop_iter_p->types[i], prop_pair_p->names_cp[i], name_p)) { /* Property is found */ property_found = true; break; } } } prop_iter_cp = prop_iter_p->next_property_cp; } #endif /* !JERRY_NDEBUG */ uint32_t entry_index = ecma_string_hash (name_p); uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)]; uint32_t mask = hashmap_p->max_property_count - 1; jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1); uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count); entry_index &= mask; #ifndef JERRY_NDEBUG /* See the comment for this variable in ecma_property_hashmap_create. */ uint32_t start_entry_index = entry_index; #endif /* !JERRY_NDEBUG */ if (ECMA_IS_DIRECT_STRING (name_p)) { ecma_property_t prop_name_type = (ecma_property_t) ECMA_GET_DIRECT_STRING_TYPE (name_p); jmem_cpointer_t property_name_cp = (jmem_cpointer_t) ECMA_GET_DIRECT_STRING_VALUE (name_p); JERRY_ASSERT (prop_name_type > 0); while (true) { if (pair_list_p[entry_index] != ECMA_NULL_POINTER) { size_t offset = 0; if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)) { offset = 1; } ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t, pair_list_p[entry_index]); ecma_property_t *property_p = property_pair_p->header.types + offset; JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*property_p)); if (property_pair_p->names_cp[offset] == property_name_cp && ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == prop_name_type) { #ifndef JERRY_NDEBUG JERRY_ASSERT (property_found); #endif /* !JERRY_NDEBUG */ *property_real_name_cp = property_name_cp; return property_p; } } else { if (!ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)) { #ifndef JERRY_NDEBUG JERRY_ASSERT (!property_found); #endif /* !JERRY_NDEBUG */ return NULL; } /* Otherwise it is a deleted entry. */ } entry_index = (entry_index + step) & mask; #ifndef JERRY_NDEBUG JERRY_ASSERT (entry_index != start_entry_index); #endif /* !JERRY_NDEBUG */ } } while (true) { if (pair_list_p[entry_index] != ECMA_NULL_POINTER) { size_t offset = 0; if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)) { offset = 1; } ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t, pair_list_p[entry_index]); ecma_property_t *property_p = property_pair_p->header.types + offset; JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*property_p)); if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_PTR) { ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, property_pair_p->names_cp[offset]); if (ecma_compare_ecma_non_direct_strings (prop_name_p, name_p)) { #ifndef JERRY_NDEBUG JERRY_ASSERT (property_found); #endif /* !JERRY_NDEBUG */ *property_real_name_cp = property_pair_p->names_cp[offset]; return property_p; } } } else { if (!ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)) { #ifndef JERRY_NDEBUG JERRY_ASSERT (!property_found); #endif /* !JERRY_NDEBUG */ return NULL; } /* Otherwise it is a deleted entry. */ } entry_index = (entry_index + step) & mask; #ifndef JERRY_NDEBUG JERRY_ASSERT (entry_index != start_entry_index); #endif /* !JERRY_NDEBUG */ } } /* ecma_property_hashmap_find */ #endif /* JERRY_PROPERTY_HASHMAP */ /** * @} * @} */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.inc.h000664 001750 001750 00000010461 15164251010 052164 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * String.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_STRING /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.5.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_STRING, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* Number properties: * (property name, number value) */ /* ECMA-262 v5, 15.5.4 (String.prototype is itself a String object whose value is an empty String), 15.5.5.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FIXED) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_STRING_PROTOTYPE_TO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_VALUE_OF_UL, ECMA_STRING_PROTOTYPE_VALUE_OF, 0, 0) ROUTINE (LIT_MAGIC_STRING_CONCAT, ECMA_STRING_PROTOTYPE_CONCAT, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_SLICE, ECMA_STRING_PROTOTYPE_SLICE, 2, 2) ROUTINE (LIT_MAGIC_STRING_AT, ECMA_STRING_PROTOTYPE_AT, 1, 1) ROUTINE (LIT_MAGIC_STRING_INDEX_OF_UL, ECMA_STRING_PROTOTYPE_INDEX_OF, 2, 1) ROUTINE (LIT_MAGIC_STRING_LAST_INDEX_OF_UL, ECMA_STRING_PROTOTYPE_LAST_INDEX_OF, 2, 1) ROUTINE (LIT_MAGIC_STRING_CHAR_AT_UL, ECMA_STRING_PROTOTYPE_CHAR_AT, 1, 1) ROUTINE (LIT_MAGIC_STRING_CHAR_CODE_AT_UL, ECMA_STRING_PROTOTYPE_CHAR_CODE_AT, 1, 1) ROUTINE (LIT_MAGIC_STRING_LOCALE_COMPARE_UL, ECMA_STRING_PROTOTYPE_LOCALE_COMPARE, 1, 1) #if JERRY_BUILTIN_REGEXP ROUTINE (LIT_MAGIC_STRING_MATCH, ECMA_STRING_PROTOTYPE_MATCH, 1, 1) ROUTINE (LIT_MAGIC_STRING_REPLACE, ECMA_STRING_PROTOTYPE_REPLACE, 2, 2) ROUTINE (LIT_MAGIC_STRING_REPLACE_ALL, ECMA_STRING_PROTOTYPE_REPLACE_ALL, 2, 2) ROUTINE (LIT_MAGIC_STRING_SEARCH, ECMA_STRING_PROTOTYPE_SEARCH, 1, 1) #endif /* JERRY_BUILTIN_REGEXP */ ROUTINE (LIT_MAGIC_STRING_SPLIT, ECMA_STRING_PROTOTYPE_SPLIT, 2, 2) ROUTINE (LIT_MAGIC_STRING_SUBSTRING, ECMA_STRING_PROTOTYPE_SUBSTRING, 2, 2) ROUTINE (LIT_MAGIC_STRING_TO_LOWER_CASE_UL, ECMA_STRING_PROTOTYPE_TO_LOWER_CASE, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_LOWER_CASE_UL, ECMA_STRING_PROTOTYPE_TO_LOCAL_LOWER_CASE, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_UPPER_CASE_UL, ECMA_STRING_PROTOTYPE_TO_UPPER_CASE, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_UPPER_CASE_UL, ECMA_STRING_PROTOTYPE_TO_LOCAL_UPPER_CASE, 0, 0) ROUTINE (LIT_MAGIC_STRING_TRIM, ECMA_STRING_PROTOTYPE_TRIM, 0, 0) #if JERRY_BUILTIN_ANNEXB ROUTINE (LIT_MAGIC_STRING_SUBSTR, ECMA_STRING_PROTOTYPE_SUBSTR, 2, 2) #endif /* JERRY_BUILTIN_ANNEXB */ ROUTINE (LIT_MAGIC_STRING_REPEAT, ECMA_STRING_PROTOTYPE_REPEAT, 1, 1) ROUTINE (LIT_MAGIC_STRING_STARTS_WITH, ECMA_STRING_PROTOTYPE_STARTS_WITH, 2, 1) ROUTINE (LIT_MAGIC_STRING_INCLUDES, ECMA_STRING_PROTOTYPE_INCLUDES, 2, 1) ROUTINE (LIT_MAGIC_STRING_ENDS_WITH, ECMA_STRING_PROTOTYPE_ENDS_WITH, 2, 1) ROUTINE (LIT_MAGIC_STRING_CODE_POINT_AT, ECMA_STRING_PROTOTYPE_CODE_POINT_AT, 1, 1) ROUTINE (LIT_MAGIC_STRING_PAD_START, ECMA_STRING_PROTOTYPE_PAD_START, 2, 1) ROUTINE (LIT_MAGIC_STRING_PAD_END, ECMA_STRING_PROTOTYPE_PAD_END, 2, 1) ROUTINE (LIT_GLOBAL_SYMBOL_ITERATOR, ECMA_STRING_PROTOTYPE_ITERATOR, 0, 0) ROUTINE (LIT_MAGIC_STRING_MATCH_ALL, ECMA_STRING_PROTOTYPE_MATCH_ALL, 1, 1) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TRIM_START, LIT_MAGIC_STRING_TRIM_START, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TRIM_LEFT, LIT_MAGIC_STRING_TRIM_START, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TRIM_END, LIT_MAGIC_STRING_TRIM_END, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TRIM_RIGHT, LIT_MAGIC_STRING_TRIM_END, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_STRING */ #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-unicode-conversions.inc.h000664 001750 001750 00000020656 15164251010 045623 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is automatically generated by the gen-unicode.py script * from UnicodeData.txt and SpecialCasing.txt files. Do not edit! */ /* Contains start points of character case ranges (these are bidirectional conversions). */ static const uint16_t lit_unicode_character_case_ranges[] JERRY_ATTR_CONST_DATA = { 0x00c0, 0x00e0, 0x00d8, 0x00f8, 0x0189, 0x0256, 0x01b1, 0x028a, 0x0388, 0x03ad, 0x038e, 0x03cd, 0x0391, 0x03b1, 0x03a3, 0x03c3, 0x03fd, 0x037b, 0x0400, 0x0450, 0x0410, 0x0430, 0x0531, 0x0561, 0x10a0, 0x2d00, 0x13a0, 0xab70, 0x13f0, 0x13f8, 0x1c90, 0x10d0, 0x1cbd, 0x10fd, 0x1f08, 0x1f00, 0x1f18, 0x1f10, 0x1f28, 0x1f20, 0x1f38, 0x1f30, 0x1f48, 0x1f40, 0x1f68, 0x1f60, 0x1fb8, 0x1fb0, 0x1fba, 0x1f70, 0x1fc8, 0x1f72, 0x1fd8, 0x1fd0, 0x1fda, 0x1f76, 0x1fe8, 0x1fe0, 0x1fea, 0x1f7a, 0x1ff8, 0x1f78, 0x1ffa, 0x1f7c, 0x2160, 0x2170, 0x24b6, 0x24d0, 0x2c00, 0x2c30, 0x2c7e, 0x023f, 0xff21, 0xff41 }; /* Interval lengths of start points in `character_case_ranges` table. */ static const uint8_t lit_unicode_character_case_range_lengths[] JERRY_ATTR_CONST_DATA = { 0x0017, 0x0007, 0x0002, 0x0002, 0x0003, 0x0002, 0x0011, 0x0009, 0x0003, 0x0010, 0x0020, 0x0026, 0x0026, 0x0050, 0x0006, 0x002b, 0x0003, 0x0008, 0x0006, 0x0008, 0x0008, 0x0006, 0x0008, 0x0002, 0x0002, 0x0004, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0010, 0x001a, 0x002f, 0x0002, 0x001a }; /* Contains the start points of bidirectional conversion ranges. */ static const uint16_t lit_unicode_character_pair_ranges[] JERRY_ATTR_CONST_DATA = { 0x0100, 0x0132, 0x0139, 0x014a, 0x0179, 0x0182, 0x0187, 0x018b, 0x0191, 0x0198, 0x01a0, 0x01a7, 0x01ac, 0x01af, 0x01b3, 0x01b8, 0x01bc, 0x01cd, 0x01de, 0x01f4, 0x01f8, 0x0222, 0x023b, 0x0241, 0x0246, 0x0370, 0x0376, 0x03d8, 0x03f7, 0x03fa, 0x0460, 0x048a, 0x04c1, 0x04d0, 0x1e00, 0x1ea0, 0x2183, 0x2c60, 0x2c67, 0x2c72, 0x2c75, 0x2c80, 0x2ceb, 0x2cf2, 0xa640, 0xa680, 0xa722, 0xa732, 0xa779, 0xa77e, 0xa78b, 0xa790, 0xa796, 0xa7b4, 0xa7c2, 0xa7c7, 0xa7f5 }; /* Interval lengths of start points in `character_pair_ranges` table. */ static const uint8_t lit_unicode_character_pair_range_lengths[] JERRY_ATTR_CONST_DATA = { 0x0030, 0x0006, 0x0010, 0x002e, 0x0006, 0x0004, 0x0002, 0x0002, 0x0002, 0x0002, 0x0006, 0x0002, 0x0002, 0x0002, 0x0004, 0x0002, 0x0002, 0x0010, 0x0012, 0x0002, 0x0028, 0x0012, 0x0002, 0x0002, 0x000a, 0x0004, 0x0002, 0x0018, 0x0002, 0x0002, 0x0022, 0x0036, 0x000e, 0x0060, 0x0096, 0x0060, 0x0002, 0x0002, 0x0006, 0x0002, 0x0002, 0x0064, 0x0004, 0x0002, 0x002e, 0x001c, 0x000e, 0x003e, 0x0004, 0x000a, 0x0002, 0x0004, 0x0014, 0x000c, 0x0002, 0x0004, 0x0002 }; /* Contains lower/upper case bidirectional conversion pairs. */ static const uint16_t lit_unicode_character_pairs[] JERRY_ATTR_CONST_DATA = { 0x0178, 0x00ff, 0x0181, 0x0253, 0x0186, 0x0254, 0x018e, 0x01dd, 0x018f, 0x0259, 0x0190, 0x025b, 0x0193, 0x0260, 0x0194, 0x0263, 0x0196, 0x0269, 0x0197, 0x0268, 0x019c, 0x026f, 0x019d, 0x0272, 0x019f, 0x0275, 0x01a6, 0x0280, 0x01a9, 0x0283, 0x01ae, 0x0288, 0x01b7, 0x0292, 0x01c4, 0x01c6, 0x01c7, 0x01c9, 0x01ca, 0x01cc, 0x01f1, 0x01f3, 0x01f6, 0x0195, 0x01f7, 0x01bf, 0x0220, 0x019e, 0x023a, 0x2c65, 0x023d, 0x019a, 0x023e, 0x2c66, 0x0243, 0x0180, 0x0244, 0x0289, 0x0245, 0x028c, 0x037f, 0x03f3, 0x0386, 0x03ac, 0x038c, 0x03cc, 0x03cf, 0x03d7, 0x03f9, 0x03f2, 0x04c0, 0x04cf, 0x10c7, 0x2d27, 0x10cd, 0x2d2d, 0x1f59, 0x1f51, 0x1f5b, 0x1f53, 0x1f5d, 0x1f55, 0x1f5f, 0x1f57, 0x1fec, 0x1fe5, 0x2132, 0x214e, 0x2c62, 0x026b, 0x2c63, 0x1d7d, 0x2c64, 0x027d, 0x2c6d, 0x0251, 0x2c6e, 0x0271, 0x2c6f, 0x0250, 0x2c70, 0x0252, 0xa77d, 0x1d79, 0xa78d, 0x0265, 0xa7aa, 0x0266, 0xa7ab, 0x025c, 0xa7ac, 0x0261, 0xa7ad, 0x026c, 0xa7ae, 0x026a, 0xa7b0, 0x029e, 0xa7b1, 0x0287, 0xa7b2, 0x029d, 0xa7b3, 0xab53, 0xa7c4, 0xa794, 0xa7c5, 0x0282, 0xa7c6, 0x1d8e }; /* Contains start points of one-to-two uppercase ranges where the second character * is always the same. */ static const uint16_t lit_unicode_upper_case_special_ranges[] JERRY_ATTR_CONST_DATA = { 0x1f80, 0x1f08, 0x0399, 0x1f88, 0x1f08, 0x0399, 0x1f90, 0x1f28, 0x0399, 0x1f98, 0x1f28, 0x0399, 0x1fa0, 0x1f68, 0x0399, 0x1fa8, 0x1f68, 0x0399 }; /* Interval lengths for start points in `upper_case_special_ranges` table. */ static const uint8_t lit_unicode_upper_case_special_range_lengths[] JERRY_ATTR_CONST_DATA = { 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007 }; /* Contains start points of lowercase ranges. */ static const uint16_t lit_unicode_lower_case_ranges[] JERRY_ATTR_CONST_DATA = { 0x1f88, 0x1f80, 0x1f98, 0x1f90, 0x1fa8, 0x1fa0 }; /* Interval lengths for start points in `lower_case_ranges` table. */ static const uint8_t lit_unicode_lower_case_range_lengths[] JERRY_ATTR_CONST_DATA = { 0x0008, 0x0008, 0x0008 }; /* The remaining lowercase conversions. The lowercase variant can be one-to-three character long. */ static const uint16_t lit_unicode_lower_case_conversions[] JERRY_ATTR_CONST_DATA = { 0x01c5, 0x01c6, 0x01c8, 0x01c9, 0x01cb, 0x01cc, 0x01f2, 0x01f3, 0x03f4, 0x03b8, 0x1e9e, 0x00df, 0x1fbc, 0x1fb3, 0x1fcc, 0x1fc3, 0x1ffc, 0x1ff3, 0x2126, 0x03c9, 0x212a, 0x006b, 0x212b, 0x00e5, 0x0130, 0x0069, 0x0307 }; /* Number of one-to-one, one-to-two, and one-to-three lowercase conversions. */ static const uint8_t lit_unicode_lower_case_conversion_counters[] JERRY_ATTR_CONST_DATA = { 0x000c, 0x0001, 0x0000 }; /* The remaining uppercase conversions. The uppercase variant can be one-to-three character long. */ static const uint16_t lit_unicode_upper_case_conversions[] JERRY_ATTR_CONST_DATA = { 0x00b5, 0x039c, 0x0131, 0x0049, 0x017f, 0x0053, 0x01c5, 0x01c4, 0x01c8, 0x01c7, 0x01cb, 0x01ca, 0x01f2, 0x01f1, 0x0345, 0x0399, 0x03c2, 0x03a3, 0x03d0, 0x0392, 0x03d1, 0x0398, 0x03d5, 0x03a6, 0x03d6, 0x03a0, 0x03f0, 0x039a, 0x03f1, 0x03a1, 0x03f5, 0x0395, 0x1c80, 0x0412, 0x1c81, 0x0414, 0x1c82, 0x041e, 0x1c83, 0x0421, 0x1c84, 0x0422, 0x1c85, 0x0422, 0x1c86, 0x042a, 0x1c87, 0x0462, 0x1c88, 0xa64a, 0x1e9b, 0x1e60, 0x1fbe, 0x0399, 0x00df, 0x0053, 0x0053, 0x0149, 0x02bc, 0x004e, 0x01f0, 0x004a, 0x030c, 0x0587, 0x0535, 0x0552, 0x1e96, 0x0048, 0x0331, 0x1e97, 0x0054, 0x0308, 0x1e98, 0x0057, 0x030a, 0x1e99, 0x0059, 0x030a, 0x1e9a, 0x0041, 0x02be, 0x1f50, 0x03a5, 0x0313, 0x1f87, 0x1f0f, 0x0399, 0x1f8f, 0x1f0f, 0x0399, 0x1f97, 0x1f2f, 0x0399, 0x1f9f, 0x1f2f, 0x0399, 0x1fa7, 0x1f6f, 0x0399, 0x1faf, 0x1f6f, 0x0399, 0x1fb2, 0x1fba, 0x0399, 0x1fb3, 0x0391, 0x0399, 0x1fb4, 0x0386, 0x0399, 0x1fb6, 0x0391, 0x0342, 0x1fbc, 0x0391, 0x0399, 0x1fc2, 0x1fca, 0x0399, 0x1fc3, 0x0397, 0x0399, 0x1fc4, 0x0389, 0x0399, 0x1fc6, 0x0397, 0x0342, 0x1fcc, 0x0397, 0x0399, 0x1fd6, 0x0399, 0x0342, 0x1fe4, 0x03a1, 0x0313, 0x1fe6, 0x03a5, 0x0342, 0x1ff2, 0x1ffa, 0x0399, 0x1ff3, 0x03a9, 0x0399, 0x1ff4, 0x038f, 0x0399, 0x1ff6, 0x03a9, 0x0342, 0x1ffc, 0x03a9, 0x0399, 0xfb00, 0x0046, 0x0046, 0xfb01, 0x0046, 0x0049, 0xfb02, 0x0046, 0x004c, 0xfb05, 0x0053, 0x0054, 0xfb06, 0x0053, 0x0054, 0xfb13, 0x0544, 0x0546, 0xfb14, 0x0544, 0x0535, 0xfb15, 0x0544, 0x053b, 0xfb16, 0x054e, 0x0546, 0xfb17, 0x0544, 0x053d, 0x0390, 0x0399, 0x0308, 0x0301, 0x03b0, 0x03a5, 0x0308, 0x0301, 0x1f52, 0x03a5, 0x0313, 0x0300, 0x1f54, 0x03a5, 0x0313, 0x0301, 0x1f56, 0x03a5, 0x0313, 0x0342, 0x1fb7, 0x0391, 0x0342, 0x0399, 0x1fc7, 0x0397, 0x0342, 0x0399, 0x1fd2, 0x0399, 0x0308, 0x0300, 0x1fd3, 0x0399, 0x0308, 0x0301, 0x1fd7, 0x0399, 0x0308, 0x0342, 0x1fe2, 0x03a5, 0x0308, 0x0300, 0x1fe3, 0x03a5, 0x0308, 0x0301, 0x1fe7, 0x03a5, 0x0308, 0x0342, 0x1ff7, 0x03a9, 0x0342, 0x0399, 0xfb03, 0x0046, 0x0046, 0x0049, 0xfb04, 0x0046, 0x0046, 0x004c }; /* Number of one-to-one, one-to-two, and one-to-three uppercase conversions. */ static const uint8_t lit_unicode_upper_case_conversion_counters[] JERRY_ATTR_CONST_DATA = { 0x001b, 0x002c, 0x0010 }; src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-symbol-prototype.cpp000664 001750 001750 00000006345 15164251010 051754 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-symbol-object.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_SYMBOL_PROTOTYPE_ROUTINE_START = 0, ECMA_SYMBOL_PROTOTYPE_VALUE_OF, /**< ECMA-262 v11, 19.4.3.4 */ ECMA_SYMBOL_PROTOTYPE_TO_PRIMITIVE, /**< ECMA-262 v11, 19.4.3.5 */ ECMA_SYMBOL_PROTOTYPE_TO_STRING, /**< ECMA-262 v11, 19.4.3.3 */ ECMA_SYMBOL_PROTOTYPE_DESCRIPTION, /**< ECMA-262 v11, 19.4.3.2 */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-symbol-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID symbol_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup symbolprototype ECMA Symbol prototype object built-in * @{ */ /** * Dispatcher of the Symbol built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_symbol_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_list, arguments_number); ecma_value_t sym = ecma_symbol_this_value (this_arg); if (ECMA_IS_VALUE_ERROR (sym)) { return sym; } if (builtin_routine_id < ECMA_SYMBOL_PROTOTYPE_TO_STRING) { return ecma_copy_value (sym); } if (builtin_routine_id == ECMA_SYMBOL_PROTOTYPE_TO_STRING) { return ecma_get_symbol_descriptive_string (sym); } JERRY_ASSERT (builtin_routine_id == ECMA_SYMBOL_PROTOTYPE_DESCRIPTION); ecma_string_t *symbol_p = ecma_get_symbol_from_value (sym); ecma_value_t desc = ecma_get_symbol_description (symbol_p); if (ecma_is_value_undefined (desc)) { return desc; } ecma_string_t *desc_p = ecma_get_string_from_value (desc); ecma_ref_ecma_string (desc_p); return desc; } /* ecma_builtin_symbol_prototype_dispatch_routine */ /** * @} * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-function-object.cpp000664 001750 001750 00000222722 15164251010 047116 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-function-object.h" #include "ecma-alloc.h" #include "ecma-builtin-handlers.h" #include "ecma-builtin-helpers.h" #include "ecma-errors.h" #include "ecma-exceptions.h" #include "ecma-extended-info.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "ecma-promise-object.h" #include "ecma-proxy-object.h" #include "ecma-symbol-object.h" #include "jcontext.h" #include "lit-char-helpers.h" #include "opcodes.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmafunctionobject ECMA Function object related routines * @{ */ /** * SetFunctionName operation * * See also: ECMAScript v6, 9.2.1.1 * * @return source name as ecma-string */ ecma_value_t ecma_op_function_form_name (ecma_string_t *prop_name_p, /**< property name */ char *prefix_p, /**< prefix */ lit_utf8_size_t prefix_size) /**< prefix length */ { /* 4. */ if (ecma_prop_name_is_symbol (prop_name_p)) { /* .a */ ecma_value_t string_desc = ecma_get_symbol_description (prop_name_p); /* .b */ if (ecma_is_value_undefined (string_desc)) { prop_name_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } /* .c */ else { ecma_string_t *string_desc_p = ecma_get_string_from_value (string_desc); ecma_stringbuilder_t builder = ecma_stringbuilder_create_raw ((lit_utf8_byte_t *) "[", 1); ecma_stringbuilder_append (&builder, string_desc_p); ecma_stringbuilder_append_byte (&builder, (lit_utf8_byte_t) LIT_CHAR_RIGHT_SQUARE); prop_name_p = ecma_stringbuilder_finalize (&builder); } } else { ecma_ref_ecma_string (prop_name_p); } /* 5. */ if (JERRY_UNLIKELY (prefix_p != NULL)) { ecma_stringbuilder_t builder = ecma_stringbuilder_create_raw ((lit_utf8_byte_t *) prefix_p, prefix_size); ecma_stringbuilder_append (&builder, prop_name_p); ecma_deref_ecma_string (prop_name_p); prop_name_p = ecma_stringbuilder_finalize (&builder); } return ecma_make_string_value (prop_name_p); } /* ecma_op_function_form_name */ #if JERRY_BUILTIN_PROXY /** * IsCallable operation for proxy object. * * @return true - if the given proxy object is callable; * false - otherwise */ bool ecma_op_proxy_object_is_callable (ecma_object_t *obj_p) /**< ecma object */ { JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); return (obj_p->u2.prototype_cp & ECMA_PROXY_IS_CALLABLE) != 0; } /* ecma_op_proxy_object_is_callable */ #endif /* JERRY_BUILTIN_PROXY */ /** * IsCallable operation. * * See also: ECMA-262 v5, 9.11 * * @return true - if the given object is callable; * false - otherwise */ bool ecma_op_object_is_callable (ecma_object_t *obj_p) /**< ecma object */ { JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); const ecma_object_type_t type = ecma_get_object_type (obj_p); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_TYPE_IS_PROXY (type)) { return ecma_op_proxy_object_is_callable (obj_p); } #endif /* JERRY_BUILTIN_PROXY */ return type >= ECMA_OBJECT_TYPE_FUNCTION; } /* ecma_op_object_is_callable */ /** * IsCallable operation. * * See also: ECMA-262 v5, 9.11 * * @return true - if value is callable object; * false - otherwise */ bool ecma_op_is_callable (ecma_value_t value) /**< ecma value */ { return (ecma_is_value_object (value) && ecma_op_object_is_callable (ecma_get_object_from_value (value))); } /* ecma_op_is_callable */ /** * Implement IsConstructor abstract operation. * * * @return ECMA_IS_VALID_CONSTRUCTOR - if object is a valid for constructor call * ecma_error_msg_t id of error - otherwise */ ecma_error_msg_t ecma_object_check_constructor (ecma_object_t *obj_p) /**< ecma object */ { JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); ecma_object_type_t type = ecma_get_object_type (obj_p); if (JERRY_UNLIKELY (type < ECMA_OBJECT_TYPE_PROXY)) { return ECMA_ERR_INVALID_TYPE_FOR_CONSTRUCTOR_CALL; } while (JERRY_UNLIKELY (type == ECMA_OBJECT_TYPE_BOUND_FUNCTION)) { ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) obj_p; obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, bound_func_p->header.u.bound_function.target_function); type = ecma_get_object_type (obj_p); } if (JERRY_LIKELY (type == ECMA_OBJECT_TYPE_FUNCTION)) { const ecma_compiled_code_t *byte_code_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) obj_p); if (!CBC_FUNCTION_IS_CONSTRUCTABLE (byte_code_p->status_flags)) { #if JERRY_ERROR_MESSAGES switch (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags)) { case CBC_FUNCTION_SCRIPT: { return ECMA_ERR_SCRIPT_GLOBAL_FUNCTIONS_INVOKE_WITH_NEW; } case CBC_FUNCTION_GENERATOR: { return ECMA_ERR_GENERATOR_FUNCTIONS_INVOKE_WITH_NEW; } case CBC_FUNCTION_ASYNC: { return ECMA_ERR_ASYNC_FUNCTIONS_INVOKE_WITH_NEW; } case CBC_FUNCTION_ASYNC_GENERATOR: { return ECMA_ERR_ASYNC_GENERATOR_FUNCTIONS_INVOKE_WITH_NEW; } case CBC_FUNCTION_ACCESSOR: { return ECMA_ERR_ACCESSOR_FUNCTIONS_INVOKE_WITH_NEW; } case CBC_FUNCTION_METHOD: { return ECMA_ERR_METHODS_INVOKE_WITH_NEW; } case CBC_FUNCTION_ARROW: { return ECMA_ERR_ARROW_FUNCTIONS_INVOKE_WITH_NEW; } default: { JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags) == CBC_FUNCTION_ASYNC_ARROW); return ECMA_ERR_ASYNC_ARROW_FUNCTIONS_INVOKE_WITH_NEW; } } #else /* !JERRY_ERROR_MESSAGES */ return ECMA_ERR_EMPTY; #endif /* JERRY_ERROR_MESSAGES */ } return ECMA_IS_VALID_CONSTRUCTOR; } #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_TYPE_IS_PROXY (type)) { if (!(obj_p->u2.prototype_cp & ECMA_PROXY_IS_CONSTRUCTABLE)) { return ECMA_ERR_PROXY_TARGET_IS_NOT_A_CONSTRUCTOR; } return ECMA_IS_VALID_CONSTRUCTOR; } #endif /* JERRY_BUILTIN_PROXY */ JERRY_ASSERT (type == ECMA_OBJECT_TYPE_NATIVE_FUNCTION || type == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION || type == ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION); if (type == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION) { if (ecma_builtin_function_is_routine (obj_p)) { return ECMA_ERR_BUILTIN_ROUTINES_HAVE_NO_CONSTRUCTOR; } JERRY_ASSERT (((ecma_extended_object_t *) obj_p)->u.built_in.id != ECMA_BUILTIN_ID_HANDLER); } return ECMA_IS_VALID_CONSTRUCTOR; } /* ecma_object_check_constructor */ /** * Implement IsConstructor abstract operation. * * @return ECMA_IS_VALID_CONSTRUCTOR - if the input value is a constructor. * ecma_error_msg_t id of error - otherwise */ ecma_error_msg_t ecma_check_constructor (ecma_value_t value) /**< ecma object */ { if (!ecma_is_value_object (value)) { return ECMA_ERR_INVALID_TYPE_FOR_CONSTRUCTOR_CALL; } return ecma_object_check_constructor (ecma_get_object_from_value (value)); } /* ecma_check_constructor */ /** * Checks whether the given object implements [[Construct]]. * * @return true - if the given object is constructor; * false - otherwise */ bool ecma_object_is_constructor (ecma_object_t *obj_p) /**< ecma object */ { return ecma_object_check_constructor (obj_p) == ECMA_IS_VALID_CONSTRUCTOR; } /* ecma_object_is_constructor */ /** * Checks whether the value is Object that implements [[Construct]]. * * @return true - if value is constructor object; * false - otherwise */ bool ecma_is_constructor (ecma_value_t value) /**< ecma value */ { return (ecma_is_value_object (value) && ecma_object_is_constructor (ecma_get_object_from_value (value))); } /* ecma_is_constructor */ /** * Helper method to count and convert the arguments for the Function/GeneratorFunction constructor call. * * See also: * ECMA 262 v5.1 15.3.2.1 steps 5.a-d * ECMA 262 v6 19.2.1.1.1 steps 8 * * @return ecma value - concatenated arguments as a string. * Returned value must be freed with ecma_free_value. */ static ecma_string_t * ecma_op_create_dynamic_function_arguments_helper (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); if (arguments_list_len <= 1) { return ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } ecma_string_t *str_p = ecma_op_to_string (arguments_list_p[0]); if (JERRY_UNLIKELY (str_p == NULL)) { return str_p; } if (arguments_list_len == 2) { return str_p; } ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (str_p); ecma_deref_ecma_string (str_p); for (uint32_t idx = 1; idx < arguments_list_len - 1; idx++) { str_p = ecma_op_to_string (arguments_list_p[idx]); if (JERRY_UNLIKELY (str_p == NULL)) { ecma_stringbuilder_destroy (&builder); return str_p; } ecma_stringbuilder_append_char (&builder, LIT_CHAR_COMMA); ecma_stringbuilder_append (&builder, str_p); ecma_deref_ecma_string (str_p); } return ecma_stringbuilder_finalize (&builder); } /* ecma_op_create_dynamic_function_arguments_helper */ /** * Function object creation operation. * * See also: ECMA-262 v5, 13.2 * * @return pointer to newly created Function object */ static ecma_object_t * ecma_op_create_function_object (ecma_object_t *scope_p, /**< function's scope */ const ecma_compiled_code_t *bytecode_data_p, /**< byte-code array */ ecma_builtin_id_t proto_id) /**< builtin id of the prototype object */ { JERRY_ASSERT (ecma_is_lexical_environment (scope_p)); /* 1., 4., 13. */ ecma_object_t *prototype_obj_p = ecma_builtin_get (proto_id); size_t function_object_size = sizeof (ecma_extended_object_t); #if JERRY_SNAPSHOT_EXEC if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION) { function_object_size = sizeof (ecma_static_function_t); } #endif /* JERRY_SNAPSHOT_EXEC */ ecma_object_t *func_p = ecma_create_object (prototype_obj_p, function_object_size, ECMA_OBJECT_TYPE_FUNCTION); /* 2., 6., 7., 8. */ /* * We don't setup [[Get]], [[Call]], [[Construct]], [[HasInstance]] for each function object. * Instead we set the object's type to ECMA_OBJECT_TYPE_FUNCTION * that defines which version of the routine should be used on demand. */ /* 3. */ /* * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_FUNCTION type. * * See also: ecma_object_get_class_name */ ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) func_p; /* 9. */ ECMA_SET_NON_NULL_POINTER_TAG (ext_func_p->u.function.scope_cp, scope_p, 0); /* 10., 11., 12. */ #if JERRY_SNAPSHOT_EXEC if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION) { ext_func_p->u.function.bytecode_cp = JMEM_CP_NULL; ((ecma_static_function_t *) func_p)->bytecode_p = bytecode_data_p; } else #endif /* JERRY_SNAPSHOT_EXEC */ { ECMA_SET_INTERNAL_VALUE_POINTER (ext_func_p->u.function.bytecode_cp, bytecode_data_p); ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_data_p); } /* 14., 15., 16., 17., 18. */ /* * 'length' and 'prototype' properties are instantiated lazily * * See also: ecma_op_function_try_to_lazy_instantiate_property */ return func_p; } /* ecma_op_create_function_object */ /** * CreateDynamicFunction operation * * See also: * ECMA-262 v5, 15.3. * ECMA-262 v6, 19.2.1.1 * * @return ECMA_VALUE_ERROR - if the operation fails * constructed function object - otherwise */ ecma_value_t ecma_op_create_dynamic_function (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len, /**< number of arguments */ ecma_parse_opts_t parse_opts) /**< parse options */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_string_t *arguments_str_p = ecma_op_create_dynamic_function_arguments_helper (arguments_list_p, arguments_list_len); if (JERRY_UNLIKELY (arguments_str_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_string_t *function_body_str_p; if (arguments_list_len > 0) { function_body_str_p = ecma_op_to_string (arguments_list_p[arguments_list_len - 1]); if (JERRY_UNLIKELY (function_body_str_p == NULL)) { ecma_deref_ecma_string (arguments_str_p); return ECMA_VALUE_ERROR; } } else { /* Very unlikely code path, not optimized. */ function_body_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } ecma_value_t source[2]; source[0] = ecma_make_string_value (function_body_str_p); source[1] = ecma_make_string_value (arguments_str_p); parse_opts = (ecma_parse_opts_t) ((int) parse_opts | ((int) ECMA_PARSE_HAS_SOURCE_VALUE | (int) ECMA_PARSE_HAS_ARGUMENT_LIST_VALUE)); ecma_compiled_code_t *bytecode_p = parser_parse_script ((void *) source, parse_opts, NULL); ecma_deref_ecma_string (arguments_str_p); ecma_deref_ecma_string (function_body_str_p); if (JERRY_UNLIKELY (bytecode_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t *func_name_p; func_name_p = ecma_compiled_code_resolve_function_name ((const ecma_compiled_code_t *) bytecode_p); *func_name_p = ecma_make_magic_string_value (LIT_MAGIC_STRING_ANONYMOUS); ecma_object_t *global_object_p = ecma_builtin_get_global (); #if JERRY_BUILTIN_REALMS JERRY_ASSERT (global_object_p == (ecma_object_t *) ecma_op_function_get_realm (bytecode_p)); #endif /* JERRY_BUILTIN_REALMS */ ecma_object_t *global_env_p = ecma_get_global_environment (global_object_p); ecma_builtin_id_t fallback_proto = ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE; ecma_object_t *new_target_p = JERRY_CONTEXT (current_new_target_p); ecma_builtin_id_t fallback_ctor = ECMA_BUILTIN_ID_FUNCTION; if (JERRY_UNLIKELY (parse_opts & (ECMA_PARSE_GENERATOR_FUNCTION | ECMA_PARSE_ASYNC_FUNCTION))) { fallback_proto = ECMA_BUILTIN_ID_ASYNC_GENERATOR; fallback_ctor = ECMA_BUILTIN_ID_ASYNC_GENERATOR_FUNCTION; if (!(parse_opts & ECMA_PARSE_GENERATOR_FUNCTION)) { fallback_proto = ECMA_BUILTIN_ID_ASYNC_FUNCTION_PROTOTYPE; fallback_ctor = ECMA_BUILTIN_ID_ASYNC_FUNCTION; } else if (!(parse_opts & ECMA_PARSE_ASYNC_FUNCTION)) { fallback_proto = ECMA_BUILTIN_ID_GENERATOR; fallback_ctor = ECMA_BUILTIN_ID_GENERATOR_FUNCTION; } } if (new_target_p == NULL) { new_target_p = ecma_builtin_get (fallback_ctor); } ecma_object_t *proto = ecma_op_get_prototype_from_constructor (new_target_p, fallback_proto); if (JERRY_UNLIKELY (proto == NULL)) { ecma_bytecode_deref (bytecode_p); return ECMA_VALUE_ERROR; } ecma_object_t *func_obj_p = ecma_op_create_function_object (global_env_p, bytecode_p, fallback_proto); ECMA_SET_NON_NULL_POINTER (func_obj_p->u2.prototype_cp, proto); ecma_deref_object (proto); ecma_bytecode_deref (bytecode_p); return ecma_make_object_value (func_obj_p); } /* ecma_op_create_dynamic_function */ /** * Function object creation operation. * * See also: ECMA-262 v5, 13.2 * * @return pointer to newly created Function object */ ecma_object_t * ecma_op_create_simple_function_object (ecma_object_t *scope_p, /**< function's scope */ const ecma_compiled_code_t *bytecode_data_p) /**< byte-code array */ { return ecma_op_create_function_object (scope_p, bytecode_data_p, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); } /* ecma_op_create_simple_function_object */ /** * Create a function object with the appropriate prototype. * * @return pointer to newly created Function object */ ecma_object_t * ecma_op_create_any_function_object (ecma_object_t *scope_p, /**< function's scope */ const ecma_compiled_code_t *bytecode_data_p) /**< byte-code array */ { ecma_builtin_id_t proto_id; switch (CBC_FUNCTION_GET_TYPE (bytecode_data_p->status_flags)) { case CBC_FUNCTION_GENERATOR: { proto_id = ECMA_BUILTIN_ID_GENERATOR; break; } case CBC_FUNCTION_ASYNC: { proto_id = ECMA_BUILTIN_ID_ASYNC_FUNCTION_PROTOTYPE; break; } case CBC_FUNCTION_ASYNC_GENERATOR: { proto_id = ECMA_BUILTIN_ID_ASYNC_GENERATOR; break; } default: { proto_id = ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE; break; } } return ecma_op_create_function_object (scope_p, bytecode_data_p, proto_id); } /* ecma_op_create_any_function_object */ /** * Arrow function object creation operation. * * See also: ES2015, 9.2.12 * * @return pointer to newly created Function object */ ecma_object_t * ecma_op_create_arrow_function_object (ecma_object_t *scope_p, /**< function's scope */ const ecma_compiled_code_t *bytecode_data_p, /**< byte-code array */ ecma_value_t this_binding) /**< value of 'this' binding */ { ecma_object_t *prototype_obj_p; if (CBC_FUNCTION_GET_TYPE (bytecode_data_p->status_flags) == CBC_FUNCTION_ARROW) { prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); } else { JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (bytecode_data_p->status_flags) == CBC_FUNCTION_ASYNC_ARROW); prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_ASYNC_FUNCTION_PROTOTYPE); } size_t arrow_function_object_size = sizeof (ecma_arrow_function_t); #if JERRY_SNAPSHOT_EXEC if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION) { arrow_function_object_size = sizeof (ecma_static_arrow_function_t); } #endif /* JERRY_SNAPSHOT_EXEC */ ecma_object_t *func_p = ecma_create_object (prototype_obj_p, arrow_function_object_size, ECMA_OBJECT_TYPE_FUNCTION); ecma_arrow_function_t *arrow_func_p = (ecma_arrow_function_t *) func_p; ECMA_SET_NON_NULL_POINTER_TAG (arrow_func_p->header.u.function.scope_cp, scope_p, 0); #if JERRY_SNAPSHOT_EXEC if ((bytecode_data_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) { arrow_func_p->header.u.function.bytecode_cp = ECMA_NULL_POINTER; ((ecma_static_arrow_function_t *) func_p)->bytecode_p = bytecode_data_p; } else { #endif /* JERRY_SNAPSHOT_EXEC */ ECMA_SET_INTERNAL_VALUE_POINTER (arrow_func_p->header.u.function.bytecode_cp, bytecode_data_p); ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_data_p); #if JERRY_SNAPSHOT_EXEC } #endif /* JERRY_SNAPSHOT_EXEC */ arrow_func_p->this_binding = ecma_copy_value_if_not_object (this_binding); arrow_func_p->new_target = ECMA_VALUE_UNDEFINED; if (JERRY_CONTEXT (current_new_target_p) != NULL) { arrow_func_p->new_target = ecma_make_object_value (JERRY_CONTEXT (current_new_target_p)); } return func_p; } /* ecma_op_create_arrow_function_object */ /** * External function object creation operation. * * Note: * external function object is implementation-defined object type * that represent functions implemented in native code, using Embedding API * * @return pointer to newly created external function object */ ecma_object_t * ecma_op_create_external_function_object (ecma_native_handler_t handler_cb) /**< pointer to external native handler */ { ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); ecma_object_t *function_obj_p = ecma_create_object (prototype_obj_p, sizeof (ecma_native_function_t), ECMA_OBJECT_TYPE_NATIVE_FUNCTION); /* * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_NATIVE_FUNCTION type. * * See also: ecma_object_get_class_name */ ecma_native_function_t *native_function_p = (ecma_native_function_t *) function_obj_p; #if JERRY_BUILTIN_REALMS ECMA_SET_INTERNAL_VALUE_POINTER (native_function_p->realm_value, ecma_builtin_get_global ()); #endif /* JERRY_BUILTIN_REALMS */ native_function_p->native_handler_cb = handler_cb; return function_obj_p; } /* ecma_op_create_external_function_object */ /** * Create built-in native handler object. * * @return pointer to newly created native handler object */ ecma_object_t * ecma_op_create_native_handler (ecma_native_handler_id_t id, /**< handler id */ size_t object_size) /**< created object size */ { ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); ecma_object_t *function_obj_p = ecma_create_object (prototype_obj_p, object_size, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) function_obj_p; ext_func_obj_p->u.built_in.id = ECMA_BUILTIN_ID_HANDLER; ext_func_obj_p->u.built_in.routine_id = (uint8_t) id; ext_func_obj_p->u.built_in.u2.routine_flags = ECMA_NATIVE_HANDLER_FLAGS_NONE; #if JERRY_BUILTIN_REALMS ECMA_SET_INTERNAL_VALUE_POINTER (ext_func_obj_p->u.built_in.realm_value, ecma_builtin_get_global ()); #endif /* JERRY_BUILTIN_REALMS */ return function_obj_p; } /* ecma_op_create_native_handler */ /** * Get compiled code of a function object. * * @return compiled code */ const ecma_compiled_code_t * ecma_op_function_get_compiled_code (ecma_extended_object_t *function_p) /**< function pointer */ { #if JERRY_SNAPSHOT_EXEC if (JERRY_LIKELY (function_p->u.function.bytecode_cp != ECMA_NULL_POINTER)) { return ECMA_GET_INTERNAL_VALUE_POINTER (const ecma_compiled_code_t, function_p->u.function.bytecode_cp); } return ((ecma_static_function_t *) function_p)->bytecode_p; #else /* !JERRY_SNAPSHOT_EXEC */ return ECMA_GET_INTERNAL_VALUE_POINTER (const ecma_compiled_code_t, function_p->u.function.bytecode_cp); #endif /* JERRY_SNAPSHOT_EXEC */ } /* ecma_op_function_get_compiled_code */ #if JERRY_BUILTIN_REALMS /** * Get realm from a byte code. * * Note: * Does not increase the reference counter. * * @return pointer to realm (global) object */ ecma_global_object_t * ecma_op_function_get_realm (const ecma_compiled_code_t *bytecode_header_p) /**< byte code header */ { #if JERRY_SNAPSHOT_EXEC if (JERRY_UNLIKELY (bytecode_header_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) { return (ecma_global_object_t *) ecma_builtin_get_global (); } #endif /* JERRY_SNAPSHOT_EXEC */ ecma_value_t script_value = ((cbc_uint8_arguments_t *) bytecode_header_p)->script_value; cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); return (ecma_global_object_t *) script_p->realm_p; } /* ecma_op_function_get_realm */ /** * Get realm from a function * * Note: * Does not increase the reference counter. * * @return realm (global) object */ ecma_global_object_t * ecma_op_function_get_function_realm (ecma_object_t *func_obj_p) /**< function object */ { while (true) { ecma_object_type_t type = ecma_get_object_type (func_obj_p); if (type == ECMA_OBJECT_TYPE_FUNCTION) { ecma_extended_object_t *ext_function_obj_p = (ecma_extended_object_t *) func_obj_p; const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_function_obj_p); return ecma_op_function_get_realm (bytecode_data_p); } if (type == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION) { ecma_extended_object_t *ext_function_obj_p = (ecma_extended_object_t *) func_obj_p; return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, ext_function_obj_p->u.built_in.realm_value); } if (type == ECMA_OBJECT_TYPE_NATIVE_FUNCTION) { ecma_native_function_t *native_function_p = (ecma_native_function_t *) func_obj_p; return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, native_function_p->realm_value); } if (type == ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION) { ecma_value_t script_value = ((ecma_extended_object_t *) func_obj_p)->u.constructor_function.script_value; cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); return (ecma_global_object_t *) script_p->realm_p; } #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (func_obj_p)) { ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) func_obj_p; if (ecma_is_value_null (proxy_obj_p->handler)) { ecma_raise_type_error (ECMA_ERR_PROTOTYPE_FROM_REVOKED_PROXY_IS_INVALID); return NULL; } func_obj_p = ecma_get_object_from_value (proxy_obj_p->target); continue; } #endif /* JERRY_BUILTIN_PROXY */ JERRY_ASSERT (type == ECMA_OBJECT_TYPE_BOUND_FUNCTION); ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) func_obj_p; func_obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, bound_func_p->header.u.bound_function.target_function); } } /* ecma_op_function_get_function_realm */ #endif /* JERRY_BUILTIN_REALMS */ /** * 15.3.5.3 implementation of [[HasInstance]] for Function objects * * @return true/false - if arguments are valid * error - otherwise * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_function_has_instance (ecma_object_t *func_obj_p, /**< Function object */ ecma_value_t value) /**< argument 'V' */ { JERRY_ASSERT (func_obj_p != NULL && !ecma_is_lexical_environment (func_obj_p)); if (!ecma_is_value_object (value)) { return ECMA_VALUE_FALSE; } while (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION) { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); /* 1. 3. */ ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) func_obj_p; func_obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, bound_func_p->header.u.bound_function.target_function); } JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION || ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION || ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION || ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_NATIVE_FUNCTION || ECMA_OBJECT_IS_PROXY (func_obj_p)); ecma_object_t *v_obj_p = ecma_get_object_from_value (value); ecma_value_t prototype_obj_value = ecma_op_object_get_by_magic_id (func_obj_p, LIT_MAGIC_STRING_PROTOTYPE); if (ECMA_IS_VALUE_ERROR (prototype_obj_value)) { return prototype_obj_value; } if (!ecma_is_value_object (prototype_obj_value)) { ecma_free_value (prototype_obj_value); return ecma_raise_type_error (ECMA_ERR_OBJECT_EXPECTED); } ecma_object_t *prototype_obj_p = ecma_get_object_from_value (prototype_obj_value); JERRY_ASSERT (prototype_obj_p != NULL); #if JERRY_BUILTIN_PROXY ecma_value_t result = ECMA_VALUE_ERROR; #else /* !JERRY_BUILTIN_PROXY */ ecma_value_t result = ECMA_VALUE_FALSE; #endif /* JERRY_BUILTIN_PROXY */ ecma_ref_object (v_obj_p); while (true) { ecma_object_t *current_proto_p = ecma_op_object_get_prototype_of (v_obj_p); ecma_deref_object (v_obj_p); if (current_proto_p == NULL) { #if JERRY_BUILTIN_PROXY result = ECMA_VALUE_FALSE; #endif /* JERRY_BUILTIN_PROXY */ break; } else if (current_proto_p == ECMA_OBJECT_POINTER_ERROR) { break; } if (current_proto_p == prototype_obj_p) { ecma_deref_object (current_proto_p); result = ECMA_VALUE_TRUE; break; } /* Advance up on prototype chain. */ v_obj_p = current_proto_p; } ecma_deref_object (prototype_obj_p); return result; } /* ecma_op_function_has_instance */ /** * GetSuperConstructor operation for class methods * * See also: ECMAScript v6, 12.3.5.2 * * @return ECMA_VALUE_ERROR - if the operation fails * super constructor - otherwise */ ecma_value_t ecma_op_function_get_super_constructor (ecma_object_t *func_obj_p) /**< function object */ { ecma_object_t *super_ctor_p = ecma_op_object_get_prototype_of (func_obj_p); if (JERRY_UNLIKELY (super_ctor_p == ECMA_OBJECT_POINTER_ERROR)) { return ECMA_VALUE_ERROR; } else if (super_ctor_p == NULL || !ecma_object_is_constructor (super_ctor_p)) { if (super_ctor_p != NULL) { ecma_deref_object (super_ctor_p); } return ecma_raise_type_error (ECMA_ERR_SUPER_BINDING_MUST_BE_A_CONSTRUCTOR); } return ecma_make_object_value (super_ctor_p); } /* ecma_op_function_get_super_constructor */ /** * Ordinary internal method: GetPrototypeFromConstructor (constructor, intrinsicDefaultProto) * * See also: * - ECMAScript v6, 9.1.15 * - ECMAScript v10, 9.1.14 * * @return NULL - if the operation fail (exception on the global context is raised) * pointer to the prototype object - otherwise */ ecma_object_t * ecma_op_get_prototype_from_constructor (ecma_object_t *ctor_obj_p, /**< constructor to get prototype from */ ecma_builtin_id_t default_proto_id) /**< intrinsicDefaultProto */ { JERRY_ASSERT (ecma_op_object_is_callable (ctor_obj_p)); JERRY_ASSERT (default_proto_id < ECMA_BUILTIN_ID__COUNT); ecma_value_t proto = ecma_op_object_get_by_magic_id (ctor_obj_p, LIT_MAGIC_STRING_PROTOTYPE); if (ECMA_IS_VALUE_ERROR (proto)) { return NULL; } ecma_object_t *proto_obj_p; if (!ecma_is_value_object (proto)) { ecma_free_value (proto); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (ctor_obj_p)) { ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) ctor_obj_p; if (ecma_is_value_null (proxy_obj_p->handler)) { ecma_raise_type_error (ECMA_ERR_PROTOTYPE_FROM_REVOKED_PROXY_IS_INVALID); return NULL; } } #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_REALMS proto_obj_p = ecma_builtin_get_from_realm (ecma_op_function_get_function_realm (ctor_obj_p), default_proto_id); #else /* !JERRY_BUILTIN_REALMS */ proto_obj_p = ecma_builtin_get (default_proto_id); #endif /* JERRY_BUILTIN_REALMS */ ecma_ref_object (proto_obj_p); } else { proto_obj_p = ecma_get_object_from_value (proto); } return proto_obj_p; } /* ecma_op_get_prototype_from_constructor */ /** * Perform a JavaScript class function object method call. * * The input function object should be a JavaScript class constructor * * @return the result of the function call. */ static ecma_value_t JERRY_ATTR_NOINLINE ecma_op_function_call_constructor (vm_frame_ctx_shared_args_t *shared_args_p, /**< shared data */ ecma_object_t *scope_p, /**< lexical environment to use */ ecma_value_t this_binding) /**< value of 'ThisBinding' */ { shared_args_p->header.status_flags |= VM_FRAME_CTX_SHARED_NON_ARROW_FUNC; ecma_value_t ret_value; ecma_extended_object_t *ext_func_p; if (JERRY_CONTEXT (current_new_target_p) == NULL) { ret_value = ecma_raise_type_error (ECMA_ERR_CLASS_CONSTRUCTOR_REQUIRES_NEW); goto exit; } ext_func_p = (ecma_extended_object_t *) shared_args_p->header.function_object_p; if (ECMA_GET_THIRD_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)) { this_binding = ECMA_VALUE_UNINITIALIZED; } ecma_op_create_environment_record (scope_p, this_binding, shared_args_p->header.function_object_p); #if JERRY_BUILTIN_REALMS ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); JERRY_CONTEXT (global_object_p) = ecma_op_function_get_realm (shared_args_p->header.bytecode_header_p); #endif /* JERRY_BUILTIN_REALMS */ ret_value = vm_run (&shared_args_p->header, this_binding, scope_p); #if JERRY_BUILTIN_REALMS JERRY_CONTEXT (global_object_p) = saved_global_object_p; #endif /* JERRY_BUILTIN_REALMS */ /* ECMAScript v6, 9.2.2.13 */ if (JERRY_UNLIKELY (this_binding == ECMA_VALUE_UNINITIALIZED)) { if (!ECMA_IS_VALUE_ERROR (ret_value) && !ecma_is_value_object (ret_value)) { if (!ecma_is_value_undefined (ret_value)) { ecma_free_value (ret_value); ret_value = ecma_raise_type_error (ECMA_ERR_DERIVED_CTOR_RETURN_NOR_OBJECT_OR_UNDEFINED); } else { ret_value = ecma_op_get_this_binding (scope_p); } } } exit: if (JERRY_UNLIKELY (shared_args_p->header.status_flags & VM_FRAME_CTX_SHARED_FREE_LOCAL_ENV)) { ecma_deref_object (scope_p); } return ret_value; } /* ecma_op_function_call_constructor */ /** * Perform a JavaScript function object method call. * * The input function object should be a pure JavaScript method * * @return the result of the function call. */ static ecma_value_t ecma_op_function_call_simple (ecma_object_t *func_obj_p, /**< Function object */ ecma_value_t this_binding, /**< 'this' argument's value */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION); vm_frame_ctx_shared_args_t shared_args; shared_args.header.status_flags = VM_FRAME_CTX_SHARED_HAS_ARG_LIST; shared_args.header.function_object_p = func_obj_p; shared_args.arg_list_p = arguments_list_p; shared_args.arg_list_len = arguments_list_len; /* Entering Function Code (ECMA-262 v5, 10.4.3) */ ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) func_obj_p; ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_func_p->u.function.scope_cp); /* 8. */ const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p); uint16_t status_flags = bytecode_data_p->status_flags; shared_args.header.bytecode_header_p = bytecode_data_p; #if JERRY_BUILTIN_REALMS ecma_global_object_t *realm_p = ecma_op_function_get_realm (bytecode_data_p); #endif /* JERRY_BUILTIN_REALMS */ /* 5. */ if (!(status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED)) { shared_args.header.status_flags |= VM_FRAME_CTX_SHARED_FREE_LOCAL_ENV; scope_p = ecma_create_decl_lex_env (scope_p); } /* 1. */ switch (CBC_FUNCTION_GET_TYPE (status_flags)) { case CBC_FUNCTION_CONSTRUCTOR: { return ecma_op_function_call_constructor (&shared_args, scope_p, this_binding); } case CBC_FUNCTION_ARROW: { ecma_arrow_function_t *arrow_func_p = (ecma_arrow_function_t *) func_obj_p; if (ecma_is_value_undefined (arrow_func_p->new_target)) { JERRY_CONTEXT (current_new_target_p) = NULL; } else { JERRY_CONTEXT (current_new_target_p) = ecma_get_object_from_value (arrow_func_p->new_target); } this_binding = arrow_func_p->this_binding; if (JERRY_UNLIKELY (this_binding == ECMA_VALUE_UNINITIALIZED)) { ecma_environment_record_t *env_record_p = ecma_op_get_environment_record (scope_p); JERRY_ASSERT (env_record_p); this_binding = env_record_p->this_binding; } break; } default: { shared_args.header.status_flags |= VM_FRAME_CTX_SHARED_NON_ARROW_FUNC; if (status_flags & CBC_CODE_FLAGS_STRICT_MODE) { break; } if (ecma_is_value_undefined (this_binding) || ecma_is_value_null (this_binding)) { /* 2. */ #if JERRY_BUILTIN_REALMS this_binding = realm_p->this_binding; #else /* !JERRY_BUILTIN_REALMS */ this_binding = ecma_make_object_value (ecma_builtin_get_global ()); #endif /* JERRY_BUILTIN_REALMS */ } else if (!ecma_is_value_object (this_binding)) { /* 3., 4. */ this_binding = ecma_op_to_object (this_binding); shared_args.header.status_flags |= VM_FRAME_CTX_SHARED_FREE_THIS; JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (this_binding)); } break; } } #if JERRY_BUILTIN_REALMS ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); JERRY_CONTEXT (global_object_p) = realm_p; #endif /* JERRY_BUILTIN_REALMS */ ecma_value_t ret_value = vm_run (&shared_args.header, this_binding, scope_p); #if JERRY_BUILTIN_REALMS JERRY_CONTEXT (global_object_p) = saved_global_object_p; #endif /* JERRY_BUILTIN_REALMS */ if (JERRY_UNLIKELY (shared_args.header.status_flags & VM_FRAME_CTX_SHARED_FREE_LOCAL_ENV)) { ecma_deref_object (scope_p); } if (JERRY_UNLIKELY (shared_args.header.status_flags & VM_FRAME_CTX_SHARED_FREE_THIS)) { ecma_free_value (this_binding); } return ret_value; } /* ecma_op_function_call_simple */ /** * Perform a built-in method call. * * @return the result of the function call. */ static ecma_value_t JERRY_ATTR_NOINLINE ecma_op_function_call_native_built_in (ecma_object_t *func_obj_p, /**< Function object */ ecma_value_t this_arg_value, /**< 'this' argument's value */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); #if JERRY_BUILTIN_REALMS ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p; JERRY_CONTEXT (global_object_p) = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, ext_func_obj_p->u.built_in.realm_value); #endif /* JERRY_BUILTIN_REALMS */ ecma_value_t ret_value = ecma_builtin_dispatch_call (func_obj_p, this_arg_value, arguments_list_p, arguments_list_len); #if JERRY_BUILTIN_REALMS JERRY_CONTEXT (global_object_p) = saved_global_object_p; #endif /* JERRY_BUILTIN_REALMS */ return ret_value; } /* ecma_op_function_call_native_built_in */ /** * Perform a native C method call which was registered via the API. * * @return the result of the function call. */ static ecma_value_t JERRY_ATTR_NOINLINE ecma_op_function_call_native (ecma_object_t *func_obj_p, /**< Function object */ ecma_value_t this_arg_value, /**< 'this' argument's value */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_NATIVE_FUNCTION); ecma_native_function_t *native_function_p = (ecma_native_function_t *) func_obj_p; #if JERRY_BUILTIN_REALMS ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); JERRY_CONTEXT (global_object_p) = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, native_function_p->realm_value); #endif /* JERRY_BUILTIN_REALMS */ jerry_call_info_t call_info; call_info.function = ecma_make_object_value (func_obj_p); call_info.this_value = this_arg_value; ecma_object_t *new_target_p = JERRY_CONTEXT (current_new_target_p); call_info.new_target = (new_target_p == NULL) ? ECMA_VALUE_UNDEFINED : ecma_make_object_value (new_target_p); JERRY_ASSERT (native_function_p->native_handler_cb != NULL); ecma_value_t ret_value = native_function_p->native_handler_cb (&call_info, arguments_list_p, arguments_list_len); #if JERRY_BUILTIN_REALMS JERRY_CONTEXT (global_object_p) = saved_global_object_p; #endif /* JERRY_BUILTIN_REALMS */ if (JERRY_UNLIKELY (ecma_is_value_exception (ret_value))) { ecma_throw_exception (ret_value); return ECMA_VALUE_ERROR; } return ret_value; } /* ecma_op_function_call_native */ /** * Append the bound arguments into the given collection * * Note: * - The whole bound chain is resolved * - The first element of the collection contains the bounded this value * * @return target function of the bound function */ JERRY_ATTR_NOINLINE static ecma_object_t * ecma_op_bound_function_get_argument_list (ecma_object_t *func_obj_p, /**< bound function object */ ecma_collection_t *list_p) /**< list of arguments */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) func_obj_p; func_obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, bound_func_p->header.u.bound_function.target_function); ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this; uint32_t args_length = 1; if (ecma_is_value_integer_number (args_len_or_this)) { args_length = (uint32_t) ecma_get_integer_from_value (args_len_or_this); } /* 5. */ if (args_length != 1) { const ecma_value_t *args_p = (const ecma_value_t *) (bound_func_p + 1); list_p->buffer_p[0] = *args_p; if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION) { func_obj_p = ecma_op_bound_function_get_argument_list (func_obj_p, list_p); } ecma_collection_append (list_p, args_p + 1, args_length - 1); } else { list_p->buffer_p[0] = args_len_or_this; } return func_obj_p; } /* ecma_op_bound_function_get_argument_list */ /** * [[Call]] internal method for bound function objects * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t JERRY_ATTR_NOINLINE ecma_op_function_call_bound (ecma_object_t *func_obj_p, /**< Function object */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_DIRECT_EVAL; ecma_collection_t *bound_arg_list_p = ecma_new_collection (); ecma_collection_push_back (bound_arg_list_p, ECMA_VALUE_EMPTY); ecma_object_t *target_obj_p = ecma_op_bound_function_get_argument_list (func_obj_p, bound_arg_list_p); ecma_collection_append (bound_arg_list_p, arguments_list_p, arguments_list_len); JERRY_ASSERT (!ecma_is_value_empty (bound_arg_list_p->buffer_p[0])); ecma_value_t ret_value = ecma_op_function_call (target_obj_p, bound_arg_list_p->buffer_p[0], bound_arg_list_p->buffer_p + 1, (uint32_t) (bound_arg_list_p->item_count - 1)); ecma_collection_destroy (bound_arg_list_p); return ret_value; } /* ecma_op_function_call_bound */ /** * General [[Call]] implementation * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_function_validated_call (ecma_value_t callee, /**< callee */ ecma_value_t this_arg_value, /**< 'this' argument's value */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { if (!ecma_is_value_object (callee)) { return ecma_raise_type_error (ECMA_ERR_EXPECTED_A_FUNCTION); } return ecma_op_function_call (ecma_get_object_from_value (callee), this_arg_value, arguments_list_p, arguments_list_len); } /* ecma_op_function_validated_call */ /** * General [[Call]] implementation * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ ecma_value_t this_arg_value, /**< 'this' argument's value */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (func_obj_p != NULL && !ecma_is_lexical_environment (func_obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target_p); if (JERRY_UNLIKELY (!(JERRY_CONTEXT (status_flags) & ECMA_STATUS_DIRECT_EVAL))) { JERRY_CONTEXT (current_new_target_p) = NULL; } ecma_value_t result; switch (ecma_get_object_type (func_obj_p)) { case ECMA_OBJECT_TYPE_FUNCTION: { result = ecma_op_function_call_simple (func_obj_p, this_arg_value, arguments_list_p, arguments_list_len); break; } case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { result = ecma_op_function_call_native_built_in (func_obj_p, this_arg_value, arguments_list_p, arguments_list_len); break; } #if JERRY_BUILTIN_PROXY case ECMA_OBJECT_TYPE_PROXY: { result = ecma_proxy_object_call (func_obj_p, this_arg_value, arguments_list_p, arguments_list_len); break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION: { result = ecma_raise_type_error (ECMA_ERR_CLASS_CONSTRUCTOR_NEW); break; } case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { result = ecma_op_function_call_native (func_obj_p, this_arg_value, arguments_list_p, arguments_list_len); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { result = ecma_op_function_call_bound (func_obj_p, arguments_list_p, arguments_list_len); break; } default: { result = ecma_raise_type_error (ECMA_ERR_EXPECTED_A_FUNCTION); break; } } JERRY_CONTEXT (current_new_target_p) = old_new_target_p; return result; } /* ecma_op_function_call */ /** * [[Construct]] internal method for ECMAScript function objects * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_op_function_construct_simple (ecma_object_t *func_obj_p, /**< Function object */ ecma_object_t *new_target_p, /**< new target */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION); ecma_object_t *new_this_obj_p = NULL; ecma_value_t this_arg; ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p; /* 5. */ if (!ECMA_GET_THIRD_BIT_FROM_POINTER_TAG (ext_func_obj_p->u.function.scope_cp)) { /* 5.a */ ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (new_target_p, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); /* 5.b */ if (JERRY_UNLIKELY (proto_p == NULL)) { return ECMA_VALUE_ERROR; } new_this_obj_p = ecma_create_object (proto_p, 0, ECMA_OBJECT_TYPE_GENERAL); ecma_deref_object (proto_p); this_arg = ecma_make_object_value (new_this_obj_p); } else { this_arg = ECMA_VALUE_UNDEFINED; } /* 6. */ ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target_p); JERRY_CONTEXT (current_new_target_p) = new_target_p; ecma_value_t ret_value = ecma_op_function_call_simple (func_obj_p, this_arg, arguments_list_p, arguments_list_len); JERRY_CONTEXT (current_new_target_p) = old_new_target_p; /* 13.a */ if (ECMA_IS_VALUE_ERROR (ret_value) || ecma_is_value_object (ret_value)) { if (new_this_obj_p != NULL) { ecma_deref_object (new_this_obj_p); } return ret_value; } /* 13.b */ ecma_free_value (ret_value); return this_arg; } /* ecma_op_function_construct_simple */ /** * [[Construct]] internal method for built-in function objects * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_op_function_construct_built_in (ecma_object_t *func_obj_p, /**< Function object */ ecma_object_t *new_target_p, /**< new target */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_UNUSED (new_target_p); JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); #if JERRY_BUILTIN_REALMS ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); ecma_value_t realm_value = ((ecma_extended_object_t *) func_obj_p)->u.built_in.realm_value; JERRY_CONTEXT (global_object_p) = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, realm_value); #endif /* JERRY_BUILTIN_REALMS */ ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target_p); JERRY_CONTEXT (current_new_target_p) = new_target_p; ecma_value_t ret_value = ecma_builtin_dispatch_construct (func_obj_p, arguments_list_p, arguments_list_len); JERRY_CONTEXT (current_new_target_p) = old_new_target; #if JERRY_BUILTIN_REALMS JERRY_CONTEXT (global_object_p) = saved_global_object_p; #endif /* JERRY_BUILTIN_REALMS */ return ret_value; } /* ecma_op_function_construct_built_in */ /** * [[Construct]] internal method for bound function objects * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t JERRY_ATTR_NOINLINE ecma_op_function_construct_bound (ecma_object_t *func_obj_p, /**< Function object */ ecma_object_t *new_target_p, /**< new target */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); ecma_collection_t *bound_arg_list_p = ecma_new_collection (); ecma_collection_push_back (bound_arg_list_p, ECMA_VALUE_EMPTY); ecma_object_t *target_obj_p = ecma_op_bound_function_get_argument_list (func_obj_p, bound_arg_list_p); ecma_collection_append (bound_arg_list_p, arguments_list_p, arguments_list_len); if (func_obj_p == new_target_p) { new_target_p = target_obj_p; } ecma_value_t ret_value = ecma_op_function_construct (target_obj_p, new_target_p, bound_arg_list_p->buffer_p + 1, (uint32_t) (bound_arg_list_p->item_count - 1)); ecma_collection_destroy (bound_arg_list_p); return ret_value; } /* ecma_op_function_construct_bound */ /** * [[Construct]] internal method for class implicit constructor objects * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_op_function_construct_constructor (ecma_object_t *func_obj_p, /**< Function object */ ecma_object_t *new_target_p, /**< new target */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION); ecma_extended_object_t *constructor_object_p = (ecma_extended_object_t *) func_obj_p; if (!(constructor_object_p->u.constructor_function.flags & ECMA_CONSTRUCTOR_FUNCTION_HAS_HERITAGE)) { ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (new_target_p, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); if (JERRY_UNLIKELY (proto_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *new_this_object_p = ecma_create_object (proto_p, 0, ECMA_OBJECT_TYPE_GENERAL); ecma_deref_object (proto_p); jerry_value_t new_this_value = ecma_make_object_value (new_this_object_p); jerry_value_t ret_value = opfunc_init_class_fields (func_obj_p, new_this_value); if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_deref_object (new_this_object_p); return ret_value; } return new_this_value; } ecma_value_t super_ctor = ecma_op_function_get_super_constructor (func_obj_p); if (ECMA_IS_VALUE_ERROR (super_ctor)) { return super_ctor; } ecma_object_t *super_ctor_p = ecma_get_object_from_value (super_ctor); ecma_value_t result = ecma_op_function_construct (super_ctor_p, new_target_p, arguments_list_p, arguments_list_len); ecma_deref_object (super_ctor_p); if (ecma_is_value_object (result)) { ecma_value_t fields_value = opfunc_init_class_fields (func_obj_p, result); if (ECMA_IS_VALUE_ERROR (fields_value)) { ecma_free_value (result); return fields_value; } } return result; } /* ecma_op_function_construct_constructor */ /** * [[Construct]] internal method for external function objects * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_op_function_construct_native (ecma_object_t *func_obj_p, /**< Function object */ ecma_object_t *new_target_p, /**< new target */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_NATIVE_FUNCTION); ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (new_target_p, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); if (JERRY_UNLIKELY (proto_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *new_this_obj_p = ecma_create_object (proto_p, 0, ECMA_OBJECT_TYPE_GENERAL); ecma_value_t this_arg = ecma_make_object_value (new_this_obj_p); ecma_deref_object (proto_p); ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target_p); JERRY_CONTEXT (current_new_target_p) = new_target_p; ecma_value_t ret_value = ecma_op_function_call_native (func_obj_p, this_arg, arguments_list_p, arguments_list_len); JERRY_CONTEXT (current_new_target_p) = old_new_target_p; if (ECMA_IS_VALUE_ERROR (ret_value) || ecma_is_value_object (ret_value)) { ecma_deref_object (new_this_obj_p); return ret_value; } ecma_free_value (ret_value); return this_arg; } /* ecma_op_function_construct_native */ /** * General [[Construct]] implementation function objects * * See also: ECMAScript v6, 9.2.2 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ ecma_object_t *new_target_p, /**< new target */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< length of arguments list */ { JERRY_ASSERT (func_obj_p != NULL && !ecma_is_lexical_environment (func_obj_p)); ECMA_CHECK_STACK_USAGE (); switch (ecma_get_object_type (func_obj_p)) { case ECMA_OBJECT_TYPE_FUNCTION: { return ecma_op_function_construct_simple (func_obj_p, new_target_p, arguments_list_p, arguments_list_len); } case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { return ecma_op_function_construct_built_in (func_obj_p, new_target_p, arguments_list_p, arguments_list_len); } #if JERRY_BUILTIN_PROXY case ECMA_OBJECT_TYPE_PROXY: { return ecma_proxy_object_construct (func_obj_p, new_target_p, arguments_list_p, arguments_list_len); } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION: { return ecma_op_function_construct_constructor (func_obj_p, new_target_p, arguments_list_p, arguments_list_len); } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { return ecma_op_function_construct_bound (func_obj_p, new_target_p, arguments_list_p, arguments_list_len); } case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { return ecma_op_function_construct_native (func_obj_p, new_target_p, arguments_list_p, arguments_list_len); } default: { JERRY_UNREACHABLE (); } } return ECMA_VALUE_UNDEFINED; } /* ecma_op_function_construct */ /** * Lazy instantiation of 'prototype' property for non-builtin and external functions * * @return pointer to newly instantiated property */ static ecma_property_t * ecma_op_lazy_instantiate_prototype_object (ecma_object_t *object_p) /**< the function object */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_FUNCTION || ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_NATIVE_FUNCTION); #if JERRY_BUILTIN_REALMS ecma_global_object_t *global_object_p; if (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_FUNCTION) { const ecma_compiled_code_t *bytecode_data_p; bytecode_data_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) object_p); global_object_p = ecma_op_function_get_realm (bytecode_data_p); } else { ecma_native_function_t *native_function_p = (ecma_native_function_t *) object_p; global_object_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, native_function_p->realm_value); } #endif /* JERRY_BUILTIN_REALMS */ /* ECMA-262 v5, 13.2, 16-18 */ ecma_object_t *proto_object_p = NULL; bool init_constructor = true; if (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_FUNCTION) { const ecma_compiled_code_t *byte_code_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) object_p); if (!CBC_FUNCTION_HAS_PROTOTYPE (byte_code_p->status_flags)) { return NULL; } if (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags) == CBC_FUNCTION_GENERATOR) { ecma_object_t *prototype_p; #if JERRY_BUILTIN_REALMS prototype_p = ecma_builtin_get_from_realm (global_object_p, ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE); #else /* !JERRY_BUILTIN_REALMS */ prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE); #endif /* JERRY_BUILTIN_REALMS */ proto_object_p = ecma_create_object (prototype_p, 0, ECMA_OBJECT_TYPE_GENERAL); init_constructor = false; } if (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags) == CBC_FUNCTION_ASYNC_GENERATOR) { ecma_object_t *prototype_p; #if JERRY_BUILTIN_REALMS prototype_p = ecma_builtin_get_from_realm (global_object_p, ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE); #else /* !JERRY_BUILTIN_REALMS */ prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE); #endif /* JERRY_BUILTIN_REALMS */ proto_object_p = ecma_create_object (prototype_p, 0, ECMA_OBJECT_TYPE_GENERAL); init_constructor = false; } } if (proto_object_p == NULL) { ecma_object_t *prototype_p; #if JERRY_BUILTIN_REALMS prototype_p = ecma_builtin_get_from_realm (global_object_p, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); #else /* !JERRY_BUILTIN_REALMS */ prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); #endif /* JERRY_BUILTIN_REALMS */ proto_object_p = ecma_op_create_object_object_noarg_and_set_prototype (prototype_p); } /* 17. */ if (init_constructor) { ecma_property_value_t *constructor_prop_value_p; constructor_prop_value_p = ecma_create_named_data_property (proto_object_p, ecma_get_magic_string (LIT_MAGIC_STRING_CONSTRUCTOR), ECMA_PROPERTY_CONFIGURABLE_WRITABLE, NULL); constructor_prop_value_p->value = ecma_make_object_value (object_p); } /* 18. */ ecma_property_t *prototype_prop_p; ecma_property_value_t *prototype_prop_value_p; prototype_prop_value_p = ecma_create_named_data_property (object_p, ecma_get_magic_string (LIT_MAGIC_STRING_PROTOTYPE), ECMA_PROPERTY_BUILT_IN_WRITABLE, &prototype_prop_p); prototype_prop_value_p->value = ecma_make_object_value (proto_object_p); ecma_deref_object (proto_object_p); return prototype_prop_p; } /* ecma_op_lazy_instantiate_prototype_object */ /** * Lazy instantiation of non-builtin ecma function object's properties * * Warning: * Only non-configurable properties could be instantiated lazily in this function, * as configurable properties could be deleted and it would be incorrect * to reinstantiate them in the function in second time. * * @return pointer to newly instantiated property, if a property was instantiated, * NULL - otherwise */ ecma_property_t * ecma_op_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, /**< the function object */ ecma_string_t *property_name_p) /**< property name */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_FUNCTION); if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH)) { ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; if (ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)) { return NULL; } /* Initialize 'length' property */ const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p); uint32_t len; if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_data_p; len = args_p->argument_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_data_p; len = args_p->argument_end; } if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO) { uint8_t *extended_info_p = ecma_compiled_code_resolve_extended_info (bytecode_data_p); if (*extended_info_p & CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH) { len = ecma_extended_info_decode_vlq (&extended_info_p); } } ecma_property_t *value_prop_p; ecma_property_value_t *value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE, &value_prop_p); value_p->value = ecma_make_uint32_value (len); return value_prop_p; } if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_NAME)) { ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; if (ECMA_GET_SECOND_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)) { return NULL; } const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p); if (CBC_FUNCTION_GET_TYPE (bytecode_data_p->status_flags) == CBC_FUNCTION_CONSTRUCTOR) { return NULL; } ecma_value_t value = *ecma_compiled_code_resolve_function_name (bytecode_data_p); JERRY_ASSERT (ecma_is_value_string (value)); /* Initialize 'name' property */ ecma_property_t *value_prop_p; ecma_property_value_t *value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE, &value_prop_p); value_p->value = ecma_copy_value (value); return value_prop_p; } if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_PROTOTYPE) && ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_FUNCTION) { return ecma_op_lazy_instantiate_prototype_object (object_p); } const bool is_arguments = ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_ARGUMENTS); if (is_arguments || ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_CALLER)) { const ecma_compiled_code_t *bytecode_data_p; bytecode_data_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) object_p); if (!(bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) && CBC_FUNCTION_GET_TYPE (bytecode_data_p->status_flags) == CBC_FUNCTION_NORMAL) { ecma_property_t *value_prop_p; /* The property_name_p argument contains the name. */ ecma_property_value_t *value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_FIXED, &value_prop_p); value_p->value = is_arguments ? ECMA_VALUE_NULL : ECMA_VALUE_UNDEFINED; return value_prop_p; } } return NULL; } /* ecma_op_function_try_to_lazy_instantiate_property */ /** * Create specification defined non-configurable properties for external functions. * * See also: * ECMA-262 v5, 15.3.4.5 * * @return pointer property, if one was instantiated, * NULL - otherwise. */ ecma_property_t * ecma_op_external_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property's name */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_NATIVE_FUNCTION); if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_PROTOTYPE)) { return ecma_op_lazy_instantiate_prototype_object (object_p); } return NULL; } /* ecma_op_external_function_try_to_lazy_instantiate_property */ /** * Create specification defined non-configurable properties for bound functions. * * See also: * ECMA-262 v5, 15.3.4.5 * * @return pointer property, if one was instantiated, * NULL - otherwise. */ ecma_property_t * ecma_op_bound_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property's name */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); if (ecma_string_is_length (property_name_p)) { ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p; ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this; ecma_number_t length = 0; ecma_integer_value_t args_length = 1; uint8_t length_attributes; if (ecma_is_value_integer_number (args_len_or_this)) { args_length = ecma_get_integer_from_value (args_len_or_this); } if (ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (bound_func_p->header.u.bound_function.target_function)) { return NULL; } length_attributes = ECMA_PROPERTY_BUILT_IN_CONFIGURABLE; length = ecma_get_number_from_value (bound_func_p->target_length) - (args_length - 1); if (length < 0) { length = 0; } ecma_property_t *len_prop_p; ecma_property_value_t *len_prop_value_p = ecma_create_named_data_property (object_p, property_name_p, length_attributes, &len_prop_p); len_prop_value_p->value = ecma_make_number_value (length); return len_prop_p; } return NULL; } /* ecma_op_bound_function_try_to_lazy_instantiate_property */ /** * Delete configurable properties of functions. */ void ecma_op_function_delete_built_in_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property name */ { ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH)) { JERRY_ASSERT (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)); ECMA_SET_FIRST_BIT_TO_POINTER_TAG (ext_func_p->u.function.scope_cp); return; } JERRY_ASSERT (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_NAME)); JERRY_ASSERT (!ECMA_GET_SECOND_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)); ECMA_SET_SECOND_BIT_TO_POINTER_TAG (ext_func_p->u.function.scope_cp); } /* ecma_op_function_delete_built_in_property */ /** * Delete configurable properties of bound functions. */ void ecma_op_bound_function_delete_built_in_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property name */ { JERRY_UNUSED (property_name_p); ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p; JERRY_ASSERT (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH)); JERRY_ASSERT (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (bound_func_p->header.u.bound_function.target_function)); ECMA_SET_FIRST_BIT_TO_POINTER_TAG (bound_func_p->header.u.bound_function.target_function); } /* ecma_op_bound_function_delete_built_in_property */ /** * List names of a Function object's lazy instantiated properties, * adding them to corresponding string collections * * See also: * ecma_op_function_try_to_lazy_instantiate_property */ void ecma_op_function_list_lazy_property_names (ecma_object_t *object_p, /**< functionobject */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< property name filter options */ { if (filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS) { return; } const ecma_compiled_code_t *bytecode_data_p; bytecode_data_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) object_p); ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; if (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)) { /* Uninitialized 'length' property is non-enumerable (ECMA-262 v6, 19.2.4.1) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); prop_counter_p->string_named_props++; } if (CBC_FUNCTION_GET_TYPE (bytecode_data_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR && !ECMA_GET_SECOND_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)) { /* Uninitialized 'name' property is non-enumerable (ECMA-262 v6, 19.2.4.2) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_NAME)); prop_counter_p->string_named_props++; } if (!CBC_FUNCTION_HAS_PROTOTYPE (bytecode_data_p->status_flags) || (CBC_FUNCTION_GET_TYPE (bytecode_data_p->status_flags) == CBC_FUNCTION_CONSTRUCTOR)) { return; } if (!(bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE)) { /* 'arguments' property is non-enumerable (ECMA-262 v5, 13.2.5) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_ARGUMENTS)); /* 'caller' property is non-enumerable (ECMA-262 v5, 13.2.5) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_CALLER)); prop_counter_p->string_named_props += 2; } /* 'prototype' property is non-enumerable (ECMA-262 v5, 13.2.18) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_PROTOTYPE)); prop_counter_p->string_named_props++; } /* ecma_op_function_list_lazy_property_names */ /** * List names of an External Function object's lazy instantiated properties, * adding them to corresponding string collections * * See also: * ecma_op_external_function_try_to_lazy_instantiate_property */ void ecma_op_external_function_list_lazy_property_names (ecma_object_t *object_p, /**< function object */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< property name * filter options */ { JERRY_UNUSED (object_p); if (filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS) { return; } /* 'prototype' property is non-enumerable (ECMA-262 v5, 13.2.18) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_PROTOTYPE)); prop_counter_p->string_named_props++; } /* ecma_op_external_function_list_lazy_property_names */ /** * List names of a Bound Function object's lazy instantiated properties, * adding them to corresponding string collections * * See also: * ecma_op_bound_function_try_to_lazy_instantiate_property */ void ecma_op_bound_function_list_lazy_property_names (ecma_object_t *object_p, /**< bound function object*/ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< property name filter options */ { if (filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS) { return; } /* Uninitialized 'length' property is non-enumerable (ECMA-262 v6, 19.2.4.1) */ ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p; if (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (bound_func_p->header.u.bound_function.target_function)) { ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); prop_counter_p->string_named_props++; } /* 'caller' property is non-enumerable (ECMA-262 v5, 13.2.5) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_CALLER)); /* 'arguments' property is non-enumerable (ECMA-262 v5, 13.2.5) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_ARGUMENTS)); prop_counter_p->string_named_props += 2; } /* ecma_op_bound_function_list_lazy_property_names */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgUtil.h000664 001750 001750 00000002747 15164251010 034535 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SVG_UTIL_H_ #define _TVG_SVG_UTIL_H_ #include "tvgCommon.h" const char* svgUtilSkipWhiteSpace(const char* itr, const char* itrEnd); const char* svgUtilUnskipWhiteSpace(const char* itr, const char* itrStart); const char* svgUtilSkipWhiteSpaceAndComma(const char* content); size_t svgUtilURLDecode(const char *src, char** dst); #endif //_TVG_SVG_UTIL_H_ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-iterator-object.cpp000664 001750 001750 00000046074 15164251010 047126 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-iterator-object.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaiteratorobject ECMA iterator object related routines * @{ */ /** * Implementation of 'CreateArrayFromList' specialized for iterators * * See also: * ECMA-262 v6, 7.3.16. * * Note: * Returned value must be freed with ecma_free_value. * * @return new array object */ ecma_value_t ecma_create_array_from_iter_element (ecma_value_t value, /**< value */ ecma_value_t index_value) /**< iterator index */ { auto new_array_p = ecma_op_new_array_object (0); ecma_builtin_helper_def_prop_by_index (new_array_p, 0, index_value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_builtin_helper_def_prop_by_index (new_array_p, 1, value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); return ecma_make_object_value (new_array_p); } /** * CreateIterResultObject operation * * See also: * ECMA-262 v6, 7.4.7. * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object */ ecma_value_t ecma_create_iter_result_object (ecma_value_t value, /**< value */ ecma_value_t done) /**< ECMA_VALUE_{TRUE,FALSE} based * on the iterator index */ { /* 1. */ JERRY_ASSERT (ecma_is_value_boolean (done)); /* 2. */ ecma_object_t *object_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE), 0, ECMA_OBJECT_TYPE_GENERAL); /* 3. */ ecma_property_value_t *prop_value_p; prop_value_p = ecma_create_named_data_property (object_p, ecma_get_magic_string (LIT_MAGIC_STRING_VALUE), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); prop_value_p->value = ecma_copy_value_if_not_object (value); /* 4. */ prop_value_p = ecma_create_named_data_property (object_p, ecma_get_magic_string (LIT_MAGIC_STRING_DONE), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); prop_value_p->value = done; /* 5. */ return ecma_make_object_value (object_p); } /* ecma_create_iter_result_object */ /** * General iterator object creation operation. * * See also: ECMA-262 v6, 21.1.5.1, 22.1.5.1, 23.1.5.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator object */ ecma_value_t ecma_op_create_iterator_object (ecma_value_t iterated_value, /**< value from create iterator */ ecma_object_t *prototype_obj_p, /**< prototype object */ ecma_object_class_type_t iterator_type, /**< iterator type */ ecma_iterator_kind_t kind) /**< iterator kind*/ { /* 1. */ #ifdef JERRY_BUILTIN_REGEXP JERRY_ASSERT (iterator_type == ECMA_OBJECT_CLASS_ARRAY_ITERATOR || iterator_type == ECMA_OBJECT_CLASS_SET_ITERATOR || iterator_type == ECMA_OBJECT_CLASS_MAP_ITERATOR || iterator_type == ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR || iterator_type == ECMA_OBJECT_CLASS_STRING_ITERATOR); #else JERRY_ASSERT (iterator_type == ECMA_OBJECT_CLASS_ARRAY_ITERATOR || iterator_type == ECMA_OBJECT_CLASS_SET_ITERATOR || iterator_type == ECMA_OBJECT_CLASS_MAP_ITERATOR || iterator_type == ECMA_OBJECT_CLASS_STRING_ITERATOR); #endif /* 2. */ ecma_object_t *object_p = ecma_create_object (prototype_obj_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; ext_obj_p->u.cls.type = (uint8_t) iterator_type; /* 3. */ ext_obj_p->u.cls.u3.iterated_value = iterated_value; /* 4. */ ext_obj_p->u.cls.u2.iterator_index = 0; /* 5. */ ext_obj_p->u.cls.u1.iterator_kind = (uint8_t) kind; /* 6. */ return ecma_make_object_value (object_p); } /* ecma_op_create_iterator_object */ /** * GetIterator operation * * See also: ECMA-262 v10, 7.4.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator object - if success * raised error - otherwise */ ecma_value_t ecma_op_get_iterator (ecma_value_t value, /**< value to get iterator from */ ecma_value_t method, /**< provided method argument */ ecma_value_t *next_method_p) /**< [out] next method */ { JERRY_ASSERT (next_method_p != NULL); *next_method_p = ECMA_VALUE_UNDEFINED; /* 1. */ if (ECMA_IS_VALUE_ERROR (value)) { return value; } bool use_default_method = false; /* 2. */ if (method == ECMA_VALUE_SYNC_ITERATOR) { /* 2.a */ use_default_method = true; method = ecma_op_get_method_by_symbol_id (value, LIT_GLOBAL_SYMBOL_ITERATOR); /* 2.b */ if (ECMA_IS_VALUE_ERROR (method)) { return method; } } /* 3.a */ else if (method == ECMA_VALUE_ASYNC_ITERATOR) { use_default_method = true; /* 3.a.i */ method = ecma_op_get_method_by_symbol_id (value, LIT_GLOBAL_SYMBOL_ASYNC_ITERATOR); if (ECMA_IS_VALUE_ERROR (method)) { return method; } /* 3.a.ii */ if (ecma_is_value_undefined (method)) { method = ecma_op_get_method_by_symbol_id (value, LIT_GLOBAL_SYMBOL_ITERATOR); if (ECMA_IS_VALUE_ERROR (method)) { return method; } ecma_value_t sync_next_method; ecma_value_t sync_iterator = ecma_op_get_iterator (value, method, &sync_next_method); if (ECMA_IS_VALUE_ERROR (sync_iterator)) { ecma_free_value (method); return sync_iterator; } ecma_value_t async_iterator = ecma_op_create_async_from_sync_iterator (sync_iterator, sync_next_method, next_method_p); ecma_free_value (method); ecma_free_value (sync_iterator); ecma_free_value (sync_next_method); return async_iterator; } } /* 3. */ ecma_value_t iterator = ecma_op_function_validated_call (method, value, NULL, 0); if (use_default_method) { ecma_free_value (method); } /* 4. */ if (ECMA_IS_VALUE_ERROR (iterator)) { return iterator; } /* 5. */ if (!ecma_is_value_object (iterator)) { ecma_free_value (iterator); return ecma_raise_type_error (ECMA_ERR_ITERATOR_IS_NOT_AN_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (iterator); ecma_value_t next_method = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_NEXT); if (ECMA_IS_VALUE_ERROR (next_method)) { ecma_free_value (iterator); return next_method; } if (ecma_op_is_callable (next_method)) { *next_method_p = next_method; } else { ecma_free_value (next_method); } /* 6. */ return iterator; } /* ecma_op_get_iterator */ /** * IteratorNext operation * * See also: ECMA-262 v10, 7.4.2 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object - if success * raised error - otherwise */ ecma_value_t ecma_op_iterator_next (ecma_value_t iterator, /**< iterator value */ ecma_value_t next_method, /**< next method */ ecma_value_t value) /**< the routines's value argument */ { JERRY_ASSERT (ecma_is_value_object (iterator)); /* 1 - 2. */ if (next_method == ECMA_VALUE_UNDEFINED) { return ecma_raise_type_error (ECMA_ERR_ITERATOR_NEXT_IS_NOT_CALLABLE); } ecma_object_t *next_method_obj_p = ecma_get_object_from_value (next_method); bool has_value = !ecma_is_value_empty (value); if (has_value) { return ecma_op_function_call (next_method_obj_p, iterator, &value, 1); } return ecma_op_function_call (next_method_obj_p, iterator, NULL, 0); } /* ecma_op_iterator_next */ /** * IteratorReturn operation * * See also: ECMA-262 v6, 14.4.14 (last part) * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object - if success * raised error - otherwise */ static ecma_value_t ecma_op_iterator_return (ecma_value_t iterator, /**< iterator value */ ecma_value_t value) /**< the routines's value argument */ { JERRY_ASSERT (ecma_is_value_object (iterator)); ecma_object_t *obj_p = ecma_get_object_from_value (iterator); ecma_value_t func_return = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_RETURN); if (ECMA_IS_VALUE_ERROR (func_return)) { return func_return; } if (func_return == ECMA_VALUE_UNDEFINED) { return ecma_create_iter_result_object (value, ECMA_VALUE_TRUE); } ecma_value_t result = ecma_op_function_validated_call (func_return, iterator, &value, 1); ecma_free_value (func_return); return result; } /* ecma_op_iterator_return */ /** * IteratorThrow operation * * See also: ECMA-262 v6, 14.4.14 (last part) * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object - if success * raised error - otherwise */ static ecma_value_t ecma_op_iterator_throw (ecma_value_t iterator, /**< iterator value */ ecma_value_t value) /**< the routines's value argument */ { JERRY_ASSERT (ecma_is_value_object (iterator)); ecma_object_t *obj_p = ecma_get_object_from_value (iterator); ecma_value_t func_throw = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_THROW); if (ECMA_IS_VALUE_ERROR (func_throw)) { return func_throw; } if (func_throw == ECMA_VALUE_UNDEFINED) { ecma_value_t result = ecma_op_iterator_close (iterator); if (ECMA_IS_VALUE_ERROR (result)) { return result; } ecma_free_value (result); return ecma_raise_type_error (ECMA_ERR_ITERATOR_THROW_IS_NOT_AVAILABLE); } ecma_value_t result = ecma_op_function_validated_call (func_throw, iterator, &value, 1); ecma_free_value (func_throw); return result; } /* ecma_op_iterator_throw */ /** * IteratorComplete operation * * See also: ECMA-262 v10, 7.4.3 * * @return true/false - whether the iteration ended */ ecma_value_t ecma_op_iterator_complete (ecma_value_t iter_result) /**< iterator value */ { /* 1. */ JERRY_ASSERT (ecma_is_value_object (iter_result)); /* 2. */ ecma_object_t *obj_p = ecma_get_object_from_value (iter_result); ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE); if (ECMA_IS_VALUE_ERROR (done)) { return done; } ecma_value_t res = ecma_make_boolean_value (ecma_op_to_boolean (done)); ecma_free_value (done); return res; } /* ecma_op_iterator_complete */ /** * IteratorValue operation * * See also: ECMA-262 v6, 7.4.4 * * Note: * Returned value must be freed with ecma_free_value. * * @return value of the iterator result object */ ecma_value_t ecma_op_iterator_value (ecma_value_t iter_result) /**< iterator value */ { /* 1. */ JERRY_ASSERT (ecma_is_value_object (iter_result)); /* 2. */ ecma_object_t *obj_p = ecma_get_object_from_value (iter_result); return ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_VALUE); } /* ecma_op_iterator_value */ /** * IteratorClose operation * * See also: ECMA-262 v6, 7.4.6 * * @return ECMA_VALUE_EMPTY - if "return" is successfully invoked, * and the operation is called with normal completion * ECMA_VALUE_ERROR - otherwise */ ecma_value_t ecma_op_iterator_close (ecma_value_t iterator) /**< iterator value */ { /* 1. */ JERRY_ASSERT (ecma_is_value_object (iterator)); /* 2. */ ecma_value_t completion = ECMA_VALUE_EMPTY; if (jcontext_has_pending_exception ()) { completion = jcontext_take_exception (); } /* 3. */ ecma_value_t return_method = ecma_op_get_method_by_magic_id (iterator, LIT_MAGIC_STRING_RETURN); /* 4. */ if (ECMA_IS_VALUE_ERROR (return_method)) { ecma_free_value (completion); return return_method; } /* 5. */ if (ecma_is_value_undefined (return_method)) { if (ecma_is_value_empty (completion)) { return ECMA_VALUE_UNDEFINED; } jcontext_raise_exception (completion); return ECMA_VALUE_ERROR; } /* 6. */ ecma_object_t *return_obj_p = ecma_get_object_from_value (return_method); ecma_value_t inner_result = ecma_op_function_call (return_obj_p, iterator, NULL, 0); ecma_deref_object (return_obj_p); /* 7. */ if (!ecma_is_value_empty (completion)) { if (ECMA_IS_VALUE_ERROR (inner_result)) { jcontext_release_exception (); } else { ecma_free_value (inner_result); } jcontext_raise_exception (completion); return ECMA_VALUE_ERROR; } /* 8. */ if (ECMA_IS_VALUE_ERROR (inner_result)) { ecma_free_value (completion); return inner_result; } /* 9. */ bool is_object = ecma_is_value_object (inner_result); ecma_free_value (inner_result); if (!is_object) { ecma_free_value (completion); return ecma_raise_type_error (ECMA_ERR_METHOD_RETURN_IS_NOT_CALLABLE); } /* 10. */ if (ecma_is_value_empty (completion)) { return ECMA_VALUE_UNDEFINED; } jcontext_raise_exception (completion); return ECMA_VALUE_ERROR; } /* ecma_op_iterator_close */ /** * IteratorStep operation * * See also: ECMA-262 v6, 7.4.5 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator object or ECMA_VALUE_FALSE - if success * raised error - otherwise */ ecma_value_t ecma_op_iterator_step (ecma_value_t iterator, /**< iterator value */ ecma_value_t next_method) /**< next method */ { /* 1. */ ecma_value_t result = ecma_op_iterator_next (iterator, next_method, ECMA_VALUE_EMPTY); /* 2. */ if (ECMA_IS_VALUE_ERROR (result)) { return result; } if (!ecma_is_value_object (result)) { ecma_free_value (result); return ecma_raise_type_error (ECMA_ERR_ITERATOR_RESULT_IS_NOT_AN_OBJECT); } /* 3. */ ecma_object_t *obj_p = ecma_get_object_from_value (result); ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE); /* 4. */ if (ECMA_IS_VALUE_ERROR (done)) { ecma_free_value (result); return done; } bool is_done = ecma_op_to_boolean (done); ecma_free_value (done); /* 5. */ if (is_done) { ecma_free_value (result); return ECMA_VALUE_FALSE; } /* 6. */ return result; } /* ecma_op_iterator_step */ /** * Perform a command specified by the command argument * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator object - if success * raised error - otherwise */ ecma_value_t ecma_op_iterator_do (ecma_iterator_command_type_t command, /**< command to be executed */ ecma_value_t iterator, /**< iterator object */ ecma_value_t next_method, /**< next method */ ecma_value_t value, /**< the routines's value argument */ bool *done_p) /**< it contains the logical value of the done property */ { ecma_value_t result; if (command == ECMA_ITERATOR_NEXT) { result = ecma_op_iterator_next (iterator, next_method, value); } else if (command == ECMA_ITERATOR_THROW) { result = ecma_op_iterator_throw (iterator, value); } else { JERRY_ASSERT (command == ECMA_ITERATOR_RETURN); result = ecma_op_iterator_return (iterator, value); } if (ECMA_IS_VALUE_ERROR (result)) { return result; } if (!ecma_is_value_object (result)) { ecma_free_value (result); return ecma_raise_type_error (ECMA_ERR_ITERATOR_RESULT_IS_NOT_AN_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (result); ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE); if (ECMA_IS_VALUE_ERROR (done)) { ecma_free_value (result); return done; } *done_p = ecma_op_to_boolean (done); ecma_free_value (done); return result; } /* ecma_op_iterator_do */ /** * CreateAsyncFromSyncIterator operation * * See also: ECMA-262 v10, 25.1.4.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return async from sync iterator object */ ecma_value_t ecma_op_create_async_from_sync_iterator (ecma_value_t sync_iterator, /**< sync iterator */ ecma_value_t sync_next_method, /**< sync iterator next method */ ecma_value_t *async_next_method_p) /**< [out] async next method */ { JERRY_ASSERT (ecma_is_value_object (sync_iterator)); JERRY_ASSERT (ecma_is_value_object (sync_next_method) || ecma_is_value_undefined (sync_next_method)); /* 1. */ ecma_object_t *obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE), sizeof (ecma_async_from_sync_iterator_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_async_from_sync_iterator_object_t *ext_obj_p = (ecma_async_from_sync_iterator_object_t *) obj_p; /* 2. */ ext_obj_p->sync_next_method = sync_next_method; ext_obj_p->header.u.cls.u3.sync_iterator = sync_iterator; ext_obj_p->header.u.cls.type = ECMA_OBJECT_CLASS_ASYNC_FROM_SYNC_ITERATOR; /* 3. */ *async_next_method_p = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_NEXT); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (*async_next_method_p)); /* 4. */ return ecma_make_object_value (obj_p); } /* ecma_op_create_async_from_sync_iterator */ /** * Async-from-Sync Iterator Value Unwrap Functions * * See also: ES11 25.1.4.2.4 * * @return iterator result object */ ecma_value_t ecma_async_from_sync_iterator_unwrap_cb (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { ecma_extended_object_t *unwrap_p = (ecma_extended_object_t *) function_obj_p; /* 2. */ ecma_value_t arg = args_count > 0 ? args_p[0] : ECMA_VALUE_UNDEFINED; ecma_value_t done = ecma_make_boolean_value (unwrap_p->u.built_in.u2.routine_flags >> ECMA_NATIVE_HANDLER_COMMON_FLAGS_SHIFT); return ecma_create_iter_result_object (arg, done); } /* ecma_async_from_sync_iterator_unwrap_cb */ /** * @} * @} */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-typeerror.inc.h000664 001750 001750 00000002445 15164251010 050651 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * TypeError built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_TYPE_ERROR_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_TYPE_ERROR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/filewritestream.h000664 001750 001750 00000006065 15164251010 040235 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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 #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(unreachable-code) #endif RAPIDJSON_NAMESPACE_BEGIN //! Wrapper of C file stream for output using fwrite(). /*! \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(bufferEnd_ - current_); while (n > avail) { std::memset(current_, c, avail); current_ += avail; Flush(); n -= avail; avail = static_cast(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(current_ - buffer_), fp_); if (result < static_cast(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_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/tree.cpp000664 001750 001750 00000054220 15164251010 034573 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Coding trees and probas // // Author: Skal (pascal.massimino@gmail.com) #include "./vp8i.h" #include "../utils/bit_reader_inl.h" #define USE_GENERIC_TREE #ifdef USE_GENERIC_TREE static const int8_t kYModesIntra4[18] = { -B_DC_PRED, 1, -B_TM_PRED, 2, -B_VE_PRED, 3, 4, 6, -B_HE_PRED, 5, -B_RD_PRED, -B_VR_PRED, -B_LD_PRED, 7, -B_VL_PRED, 8, -B_HD_PRED, -B_HU_PRED }; #endif //------------------------------------------------------------------------------ // Default probabilities // Paragraph 13.5 static const uint8_t CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } }, { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 }, { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 }, { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 } }, { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 }, { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 }, { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 }, }, { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 }, { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 }, { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 }, }, { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 }, { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 }, { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 } }, { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 }, { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 }, { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 } }, { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 }, { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 }, { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 } }, { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } } }, { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 }, { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 }, { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 } }, { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 }, { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 }, { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 } }, { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 }, { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 }, { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 } }, { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 }, { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 }, { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 } }, { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 }, { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 }, { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 } }, { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 }, { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 }, { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 } }, { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 }, { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 }, { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 } }, { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 }, { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 } } }, { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 }, { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 }, { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 } }, { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 }, { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 }, { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 } }, { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 }, { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 }, { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 } }, { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 }, { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 }, { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 } }, { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 }, { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } }, { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 } }, { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 }, { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 }, { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 } }, { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } } }, { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 }, { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 }, { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 } }, { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 }, { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 }, { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 } }, { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 }, { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 }, { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 } }, { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 }, { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 }, { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 } }, { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 }, { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 }, { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 } }, { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 }, { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 }, { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 } }, { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 }, { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 }, { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 } }, { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } } } }; // Paragraph 11.5 static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = { { { 231, 120, 48, 89, 115, 113, 120, 152, 112 }, { 152, 179, 64, 126, 170, 118, 46, 70, 95 }, { 175, 69, 143, 80, 85, 82, 72, 155, 103 }, { 56, 58, 10, 171, 218, 189, 17, 13, 152 }, { 114, 26, 17, 163, 44, 195, 21, 10, 173 }, { 121, 24, 80, 195, 26, 62, 44, 64, 85 }, { 144, 71, 10, 38, 171, 213, 144, 34, 26 }, { 170, 46, 55, 19, 136, 160, 33, 206, 71 }, { 63, 20, 8, 114, 114, 208, 12, 9, 226 }, { 81, 40, 11, 96, 182, 84, 29, 16, 36 } }, { { 134, 183, 89, 137, 98, 101, 106, 165, 148 }, { 72, 187, 100, 130, 157, 111, 32, 75, 80 }, { 66, 102, 167, 99, 74, 62, 40, 234, 128 }, { 41, 53, 9, 178, 241, 141, 26, 8, 107 }, { 74, 43, 26, 146, 73, 166, 49, 23, 157 }, { 65, 38, 105, 160, 51, 52, 31, 115, 128 }, { 104, 79, 12, 27, 217, 255, 87, 17, 7 }, { 87, 68, 71, 44, 114, 51, 15, 186, 23 }, { 47, 41, 14, 110, 182, 183, 21, 17, 194 }, { 66, 45, 25, 102, 197, 189, 23, 18, 22 } }, { { 88, 88, 147, 150, 42, 46, 45, 196, 205 }, { 43, 97, 183, 117, 85, 38, 35, 179, 61 }, { 39, 53, 200, 87, 26, 21, 43, 232, 171 }, { 56, 34, 51, 104, 114, 102, 29, 93, 77 }, { 39, 28, 85, 171, 58, 165, 90, 98, 64 }, { 34, 22, 116, 206, 23, 34, 43, 166, 73 }, { 107, 54, 32, 26, 51, 1, 81, 43, 31 }, { 68, 25, 106, 22, 64, 171, 36, 225, 114 }, { 34, 19, 21, 102, 132, 188, 16, 76, 124 }, { 62, 18, 78, 95, 85, 57, 50, 48, 51 } }, { { 193, 101, 35, 159, 215, 111, 89, 46, 111 }, { 60, 148, 31, 172, 219, 228, 21, 18, 111 }, { 112, 113, 77, 85, 179, 255, 38, 120, 114 }, { 40, 42, 1, 196, 245, 209, 10, 25, 109 }, { 88, 43, 29, 140, 166, 213, 37, 43, 154 }, { 61, 63, 30, 155, 67, 45, 68, 1, 209 }, { 100, 80, 8, 43, 154, 1, 51, 26, 71 }, { 142, 78, 78, 16, 255, 128, 34, 197, 171 }, { 41, 40, 5, 102, 211, 183, 4, 1, 221 }, { 51, 50, 17, 168, 209, 192, 23, 25, 82 } }, { { 138, 31, 36, 171, 27, 166, 38, 44, 229 }, { 67, 87, 58, 169, 82, 115, 26, 59, 179 }, { 63, 59, 90, 180, 59, 166, 93, 73, 154 }, { 40, 40, 21, 116, 143, 209, 34, 39, 175 }, { 47, 15, 16, 183, 34, 223, 49, 45, 183 }, { 46, 17, 33, 183, 6, 98, 15, 32, 183 }, { 57, 46, 22, 24, 128, 1, 54, 17, 37 }, { 65, 32, 73, 115, 28, 128, 23, 128, 205 }, { 40, 3, 9, 115, 51, 192, 18, 6, 223 }, { 87, 37, 9, 115, 59, 77, 64, 21, 47 } }, { { 104, 55, 44, 218, 9, 54, 53, 130, 226 }, { 64, 90, 70, 205, 40, 41, 23, 26, 57 }, { 54, 57, 112, 184, 5, 41, 38, 166, 213 }, { 30, 34, 26, 133, 152, 116, 10, 32, 134 }, { 39, 19, 53, 221, 26, 114, 32, 73, 255 }, { 31, 9, 65, 234, 2, 15, 1, 118, 73 }, { 75, 32, 12, 51, 192, 255, 160, 43, 51 }, { 88, 31, 35, 67, 102, 85, 55, 186, 85 }, { 56, 21, 23, 111, 59, 205, 45, 37, 192 }, { 55, 38, 70, 124, 73, 102, 1, 34, 98 } }, { { 125, 98, 42, 88, 104, 85, 117, 175, 82 }, { 95, 84, 53, 89, 128, 100, 113, 101, 45 }, { 75, 79, 123, 47, 51, 128, 81, 171, 1 }, { 57, 17, 5, 71, 102, 57, 53, 41, 49 }, { 38, 33, 13, 121, 57, 73, 26, 1, 85 }, { 41, 10, 67, 138, 77, 110, 90, 47, 114 }, { 115, 21, 2, 10, 102, 255, 166, 23, 6 }, { 101, 29, 16, 10, 85, 128, 101, 196, 26 }, { 57, 18, 10, 102, 102, 213, 34, 20, 43 }, { 117, 20, 15, 36, 163, 128, 68, 1, 26 } }, { { 102, 61, 71, 37, 34, 53, 31, 243, 192 }, { 69, 60, 71, 38, 73, 119, 28, 222, 37 }, { 68, 45, 128, 34, 1, 47, 11, 245, 171 }, { 62, 17, 19, 70, 146, 85, 55, 62, 70 }, { 37, 43, 37, 154, 100, 163, 85, 160, 1 }, { 63, 9, 92, 136, 28, 64, 32, 201, 85 }, { 75, 15, 9, 9, 64, 255, 184, 119, 16 }, { 86, 6, 28, 5, 64, 255, 25, 248, 1 }, { 56, 8, 17, 132, 137, 255, 55, 116, 128 }, { 58, 15, 20, 82, 135, 57, 26, 121, 40 } }, { { 164, 50, 31, 137, 154, 133, 25, 35, 218 }, { 51, 103, 44, 131, 131, 123, 31, 6, 158 }, { 86, 40, 64, 135, 148, 224, 45, 183, 128 }, { 22, 26, 17, 131, 240, 154, 14, 1, 209 }, { 45, 16, 21, 91, 64, 222, 7, 1, 197 }, { 56, 21, 39, 155, 60, 138, 23, 102, 213 }, { 83, 12, 13, 54, 192, 255, 68, 47, 28 }, { 85, 26, 85, 85, 128, 128, 32, 146, 171 }, { 18, 11, 7, 63, 144, 171, 4, 4, 246 }, { 35, 27, 10, 146, 174, 171, 12, 26, 128 } }, { { 190, 80, 35, 99, 180, 80, 126, 54, 45 }, { 85, 126, 47, 87, 176, 51, 41, 20, 32 }, { 101, 75, 128, 139, 118, 146, 116, 128, 85 }, { 56, 41, 15, 176, 236, 85, 37, 9, 62 }, { 71, 30, 17, 119, 118, 255, 17, 18, 138 }, { 101, 38, 60, 138, 55, 70, 43, 26, 142 }, { 146, 36, 19, 30, 171, 255, 97, 27, 20 }, { 138, 45, 61, 62, 219, 1, 81, 188, 64 }, { 32, 41, 20, 117, 151, 142, 20, 21, 163 }, { 112, 19, 12, 61, 195, 128, 48, 4, 24 } } }; void VP8ResetProba(VP8Proba* const proba) { memset(proba->segments_, 255u, sizeof(proba->segments_)); // proba->bands_[][] is initialized later } static void ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec, int mb_x) { uint8_t* const top = dec->intra_t_ + 4 * mb_x; uint8_t* const left = dec->intra_l_; VP8MBData* const block = dec->mb_data_ + mb_x; // Note: we don't save segment map (yet), as we don't expect // to decode more than 1 keyframe. if (dec->segment_hdr_.update_map_) { // Hardcoded tree parsing block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ? VP8GetBit(br, dec->proba_.segments_[1]) : 2 + VP8GetBit(br, dec->proba_.segments_[2]); } else { block->segment_ = 0; // default for intra } if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_); block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first if (!block->is_i4x4_) { // Hardcoded 16x16 intra-mode decision tree. const int ymode = VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED) : (VP8GetBit(br, 163) ? V_PRED : DC_PRED); block->imodes_[0] = ymode; memset(top, ymode, 4 * sizeof(*top)); memset(left, ymode, 4 * sizeof(*left)); } else { uint8_t* modes = block->imodes_; int y; for (y = 0; y < 4; ++y) { int ymode = left[y]; int x; for (x = 0; x < 4; ++x) { const uint8_t* const prob = kBModesProba[top[x]][ymode]; #ifdef USE_GENERIC_TREE // Generic tree-parsing int i = kYModesIntra4[VP8GetBit(br, prob[0])]; while (i > 0) { i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])]; } ymode = -i; #else // Hardcoded tree parsing ymode = !VP8GetBit(br, prob[0]) ? B_DC_PRED : !VP8GetBit(br, prob[1]) ? B_TM_PRED : !VP8GetBit(br, prob[2]) ? B_VE_PRED : !VP8GetBit(br, prob[3]) ? (!VP8GetBit(br, prob[4]) ? B_HE_PRED : (!VP8GetBit(br, prob[5]) ? B_RD_PRED : B_VR_PRED)) : (!VP8GetBit(br, prob[6]) ? B_LD_PRED : (!VP8GetBit(br, prob[7]) ? B_VL_PRED : (!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED))); #endif // USE_GENERIC_TREE top[x] = ymode; } memcpy(modes, top, 4 * sizeof(*top)); modes += 4; left[y] = ymode; } } // Hardcoded UVMode decision tree block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED : !VP8GetBit(br, 114) ? V_PRED : VP8GetBit(br, 183) ? TM_PRED : H_PRED; } int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) { int mb_x; for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { ParseIntraMode(br, dec, mb_x); } return !dec->br_.eof_; } //------------------------------------------------------------------------------ // Paragraph 13 static const uint8_t CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 }, { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } } }, { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 }, { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 } }, { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } } }, { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 }, { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 }, { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 } }, { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } } }, { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 }, { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } } } }; // Paragraph 9.9 static const int kBands[16 + 1] = { 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0 // extra entry as sentinel }; void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) { VP8Proba* const proba = &dec->proba_; int t, b, c, p; for (t = 0; t < NUM_TYPES; ++t) { for (b = 0; b < NUM_BANDS; ++b) { for (c = 0; c < NUM_CTX; ++c) { for (p = 0; p < NUM_PROBAS; ++p) { const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ? VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p]; proba->bands_[t][b].probas_[c][p] = v; } } } for (b = 0; b < 16 + 1; ++b) { proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]]; } } dec->use_skip_proba_ = VP8Get(br); if (dec->use_skip_proba_) { dec->skip_p_ = VP8GetValue(br, 8); } } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgInlist.h000664 001750 001750 00000006236 15164251010 033437 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_INLIST_H_ #define _TVG_INLIST_H_ namespace tvg { //NOTE: declare this in your list item #define INLIST_ITEM(T) \ T* prev; \ T* next template struct Inlist { T* head = nullptr; T* tail = nullptr; ~Inlist() { free(); } void free() { while (head) { auto t = head; head = t->next; delete(t); } head = tail = nullptr; } void back(T* element) { if (tail) { tail->next = element; element->prev = tail; element->next = nullptr; tail = element; } else { head = tail = element; element->prev = nullptr; element->next = nullptr; } } void front(T* element) { if (head) { head->prev = element; element->prev = nullptr; element->next = head; head = element; } else { head = tail = element; element->prev = nullptr; element->next = nullptr; } } T* back() { if (!tail) return nullptr; auto t = tail; tail = t->prev; if (!tail) head = nullptr; return t; } T* front() { if (!head) return nullptr; auto t = head; head = t->next; if (!head) tail = nullptr; return t; } void remove(T* element) { if (element->prev) element->prev->next = element->next; if (element->next) element->next->prev = element->prev; if (element == head) head = element->next; if (element == tail) tail = element->prev; } bool empty() const { return head ? false : true; } }; #define INLIST_FOREACH(inlist, cur) \ for (auto cur = inlist.head; cur; cur = cur->next) #define INLIST_SAFE_FOREACH(inlist, cur) \ auto cur = inlist.head; \ auto next = cur ? cur->next : nullptr; \ for (; cur; cur = next, next = (cur ? cur->next : nullptr)) } #endif // _TVG_INLIST_H_ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-function.cpp000664 001750 001750 00000005025 15164251010 053322 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-globals.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #include "ecma-function-object.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-generator-function.inc.h" #define BUILTIN_UNDERSCORED_ID async_generator_function #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup asyncgeneratorfunction ECMA AsyncGeneratorFunction object built-in * @{ */ /** * Handle calling [[Call]] of built-in AsyncGeneratorFunction object * * @return constructed async generator function object - if success * raised error otherwise */ ecma_value_t ecma_builtin_async_generator_function_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_op_create_dynamic_function (arguments_list_p, arguments_list_len, (ecma_parse_opts_t)(((unsigned int) ECMA_PARSE_GENERATOR_FUNCTION) | ((unsigned int) ECMA_PARSE_ASYNC_FUNCTION))); } /* ecma_builtin_async_generator_function_dispatch_call */ /** * Handle calling [[Construct]] of built-in AsyncGeneratorFunction object * * @return constructed async generator function object - if success * raised error otherwise */ ecma_value_t ecma_builtin_async_generator_function_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_async_generator_function_dispatch_call (arguments_list_p, arguments_list_len); } /* ecma_builtin_async_generator_function_dispatch_construct */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/cpu.cpp000664 001750 001750 00000007563 15164251010 034466 0ustar00ddennedyddennedy000000 000000 // Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // CPU detection // // Author: Christian Duvivier (cduvivier@google.com) #include "./dsp.h" //------------------------------------------------------------------------------ // SSE2 detection. // // apple/darwin gcc-4.0.1 defines __PIC__, but not __pic__ with -fPIC. #if (defined(__pic__) || defined(__PIC__)) && defined(__i386__) static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { __asm__ volatile ( "mov %%ebx, %%edi\n" "cpuid\n" "xchg %%edi, %%ebx\n" : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) : "a"(info_type), "c"(0)); } #elif defined(__i386__) || defined(__x86_64__) static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { __asm__ volatile ( "cpuid\n" : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) : "a"(info_type), "c"(0)); } #elif (defined(_M_X64) || defined(_M_IX86)) && \ defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 150030729 // >= VS2008 SP1 #include #define GetCPUInfo(info, type) __cpuidex(info, type, 0) // set ecx=0 #elif defined(WEBP_MSC_SSE2) #define GetCPUInfo __cpuid #endif // NaCl has no support for xgetbv or the raw opcode. #if !defined(__native_client__) && (defined(__i386__) || defined(__x86_64__)) static WEBP_INLINE uint64_t xgetbv(void) { const uint32_t ecx = 0; uint32_t eax, edx; // Use the raw opcode for xgetbv for compatibility with older toolchains. __asm__ volatile ( ".byte 0x0f, 0x01, 0xd0\n" : "=a"(eax), "=d"(edx) : "c" (ecx)); return ((uint64_t)edx << 32) | eax; } #elif (defined(_M_X64) || defined(_M_IX86)) && \ defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219 // >= VS2010 SP1 #include #define xgetbv() _xgetbv(0) #elif defined(_MSC_VER) && defined(_M_IX86) static WEBP_INLINE uint64_t xgetbv(void) { uint32_t eax_, edx_; __asm { xor ecx, ecx // ecx = 0 // Use the raw opcode for xgetbv for compatibility with older toolchains. __asm _emit 0x0f __asm _emit 0x01 __asm _emit 0xd0 mov eax_, eax mov edx_, edx } return ((uint64_t)edx_ << 32) | eax_; } #else #define xgetbv() 0U // no AVX for older x64 or unrecognized toolchains. #endif #if defined(__i386__) || defined(__x86_64__) || defined(WEBP_MSC_SSE2) static int x86CPUInfo(CPUFeature feature) { int cpu_info[4]; GetCPUInfo(cpu_info, 1); if (feature == kSSE2) { return 0 != (cpu_info[3] & 0x04000000); } return 0; } VP8CPUInfo VP8GetCPUInfo = x86CPUInfo; #elif defined(WEBP_ANDROID_NEON) // NB: needs to be before generic NEON test. static int AndroidCPUInfo(CPUFeature feature) { const AndroidCpuFamily cpu_family = android_getCpuFamily(); const uint64_t cpu_features = android_getCpuFeatures(); if (feature == kNEON) { return (cpu_family == ANDROID_CPU_FAMILY_ARM && 0 != (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON)); } return 0; } VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo; #elif defined(WEBP_USE_NEON) // define a dummy function to enable turning off NEON at runtime by setting // VP8DecGetCPUInfo = NULL static int armCPUInfo(CPUFeature feature) { (void)feature; return 1; } VP8CPUInfo VP8GetCPUInfo = armCPUInfo; #elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) static int mipsCPUInfo(CPUFeature feature) { if ((feature == kMIPS32) || (feature == kMIPSdspR2)) { return 1; } else { return 0; } } VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo; #else VP8CPUInfo VP8GetCPUInfo = NULL; #endif lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint8array-prototype.cpp000664 001750 001750 00000002222 15164251010 054727 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-uint8array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID uint8array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup uint8arrayprototype ECMA Uint8Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/NanumGothicCoding.ttf000664 001750 001750 00012463750 15164251010 036230 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0`OS/2` h`cmapS:MTcvt |fpgmX6@sgasp'*gglyfZ Ð'head,6hhea QZ$$hmtx*99"locaP@-,9dmaxpP _H nameo*c`Vpost;;*g prepd`<p_<Ċ/p8! 8N9NXT3  x2 )SAND@ 8   7 y)F.Y*9M*5,t(''EB68LJ%9c>We3>%<%8A2 #<F0>6#)7*<R)XE&>*>$/D3<*$-:h|G1[5-bFWuvuuu<<33ddeeRR|Lnj[[~~j::oooo22QQ2r|'E22($K?#uTeenBnneDDQAAAAT"K<\Q[OOg%Q-q@"!?u,7u8u#!G! + T %mGO](vv2TTp&l'FKaFFw\wFFwWYfj(KffTRNyBJL<9%cW3>=%9<(#% G+3<>%X/<[*#>*#< efqIg]^cbO_af_`em_f|oahYTuf[muZZuM]THHWOQESXWfJLKJ_]]_N[iXVJWKLMmYcT W 4e*Wl>999999999999999999999999999999999999999999999999999999999999999999999#*R X'E G>3> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<7 t_I^ocJSflhlp^cwsu```[YvwlfkeWWfYdVKfK`c}{lll# hq l ( ,  -Vbbbbbbbbbbbbbbbbb]bb`bbbbbVbbbbb]b`m}}}||}}{}}}}}}db`bbbbbi}}}}|}}{}}}ab`bb<<<<<<<<<<<<<?IIIIIIII?JJJJJMLLLLLLL<<<<<<<<<<<<<<<<<<ITLLLL4DITLLLLL<<<<<<<<<<<<<ItxxxxxxxxxxxHGGGJJGGGGGJ?GGGGGGGGHGJV[[[[[[[[[DGGGGGV[[[X[?<<<<<<<<<<<?IIII?MMMRRRRR<<<<<<<<<<<ILLL4DDDDDDILLLL<<<<<<<<<<<<<`eeeeeee<<<<<<<<<<<?IIIIMMMMM<<<<<<<<<<<<<<<<IL4IQLLL<<<<<<<<<<<<<<<<<<IMM}{oooooooo}qooooooooqo<<<<<<<<<<<<<?II?JIIMMMM<<<<<<<<<IL4DILLLLL<<<<<<<<<<<<<<<Iopppppppppfssssssssy{{{{{{{{{{{fssssssssy{<<<<<?I?II<<<<<<<4ILLLLQ<<<<<<<<IIIIIy{{{{{{|joooooooo|jnoooooojooo<<<<<<<<?IIJIMMMMMM<<<<<<<<<<<<<<IL4ILQQLLL<<<<<<<<<<<<<<<<<<<}jooooooooo}jooooooooj<<<<<<<<<<?IIIIMMMMM<<<<<<<<<<<<<<<<<<<ILLLL4IQQ<<<<<<<<<<jooooooooojooooooooojo<<<<<<<<<?II?JIMMMMM<<<<<<<<<<<<<<<ILL4IQQQQ<<<<<<<<<<<<<]jjjjjjjjjj^gggggggg]jjboooooooo^gboooooo<<<<<<<I<<<<<<<<<<<<<<<<boooooooLPPKPQQQPPPPPQELIKLKILLLPKQPPPPEIKLLRWWXUW[[[WUUWWWELIKLKILLRXU[WVUWWEIKL<<<<<<<<<<<?IIII?HHHHHIMMMMM<<<<<<<<<<<<<<<<<<<<IL4DBDDDIQQQQQQQ<<<<<<<<<<<<<<<_QQQTTQQPQQ,889788887(000000081=><<====(000<<<<<<<<<37700=GIEKK<<<<<<<<EL0EL<<<<<<<<<>III1=><==>=jrqttttttqqqqqtqXeeeeeeeejqqttqqqqttXeeepxvzxzzzxxxvxxxxxXeeeeeeepxxuzzzxxxvxxzxzXeeeeee<<<<<<<<<<<<<?IIIIIIII?JJJJJIMMMMMMM<<<<<<<<<<<<<<<<<<ITLLTLTT4DBDDDDIQQQQQQQ<<<<<<<<<<<<<<<<<<<<<<<<IMMMM}vvyyyyvwvvvwvM\^\\ZZ\\^\\\ASOTSQSSSM\^\Z\\AOT[cebbcceccASOTSQSS[]bccccG<<<<<<<<<<<<?IIIII>JJIMMMMMM<<<<<<<<<<<<<<IT4IQQQQQL<<<<<<<<<<<<efhfeegfiffeg,C6?<<C?:CC333533333,6C%H;AHD?HH33%H<<<<<<<<<6III'BBKMMM<<<<<<<<ILLI<<<<<%H;AHDHHAU\_[Y\[_\\\Y[\[[[\[[U_[Y\\[psmposppY[\[[[Z[[spY\[<<<<<<<<?IIIIMMMMMM<<<<<<<<<<IL4BJQQLLLL<<<<<<<<<<<<<_pspmmpospYijjjjbblqqoqqqqqYjj]}}}}}}{}}pqqoqqqq]}}}}{}}n<<<<<<<<?IIIII?JML<<<<<<<<<ILLL4DILLLLLLL<<<<<<<<<<<]}}}}}{}}joooooooo}jooooooojo<<<<<<<<<?I?IMMM<<<<<<<<<IL4ILQQQQQ<<<<<<<<<<<<<<IMMMMFIIIIIIIIIII<AAAAAAAAFIPSSSSSSSS<AAAAAAAPSSSSSS<AAA<<<<<<<<?IIM<<<<<<<<<<<<<<<ITIQQQQ<<<<<<<<<<<<PSSSSSSSNNNNNNNNNIZZZZZZZZNNNffggfggfIZZZZZZZNffgfggffIZZZ<<<<<<<<<<?IIIII?JJJJIMMMMMM<<<<<<<<<<<<<ILTTT4DBDDIQQQQQQQ<<<<<<<<<<<<<<<<<<<IOQQQQ]ffgfggf66DD11    DDVV?PP@@PR/5PekgegioggRNRg8<TLTeu<TToVTH`HTTYTFNHDV<aPVX[X[YgcXgRXXig]PX]iPPeeeeddddee^^iiYYCCVVppvv``ZZxxaaff*-LMuKKa%%KKNNKKmm>>IAjM;M9B74.>>xxhhhHHxxuuuuAADCCCCCCCCCCCCCCCCCCC<-DDDDDDDDDDDDDDDDDDDD99999999999999999999<<<<<<<<<<<<<<<<<<<<<<<<<<99999999999999999999999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99999999999999999999ZZZZZZZZZZGGGGGGGGGGyv{u_|jiuR9999zQ*~l6hhhFFjjMB@BB44B@{{oo::ro::AAhL45FFAhVV_Uo:::CjjkChlFUdnInnDKDo99999999999999999999999999b97bbbb+J3uz:4I{:::XXXoo<<<99999999999999922  ????222222JJ2J22HHFFGGFFFF22223311LL3322UU22II22pp22eeUTfeUUWWUUWWUUJL66333>PP33KKMM2kvlekjQx{  deMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF<<<<<<<<<<<FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF<<<<LL3222232222222MMMM772FFPPFFPPMM//EE55NNMM<<MM<<WWUUWWUUFFLLhhDDFFFFFFFEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD]]]]]]]IIIIIII]]]]]]]eellCCwxxxLFccccccccccbbb_b_______bb_b_b_ccccccccccccccccccccccbbbb_b_______bbbbbbb_b_b_|||||||||||||bbbb_b_______bb_b_b_||||||||||||||||bbbb_b_______bbbbb_b_b_;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHJJJJJJJJJJJJJJJJJJJJJJMMMMMMMMMMMMMMMMMMMM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UUUMUMMMMMMMUUUUUMUMUMCCCCCCCCCCCCCCCCCCCCCCCCCCUUUMUMMMMMMMUUUUMUMUM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;NNNNNNNNNNNNNNNNNNNNNNNNNNN||||||||||||||||DDGDGGGGGGDDGDDGCCCCCCCCCCCCCCCCCCCDDDDGDGGGGGGGDDDDDDDGDGDG;CCCCCCCCCCCCCCCCCCCCCCCCCCCDD\D\\\\\\\DD\D\D\CCCCCCCCCCCCCCCCCCCCCCDDDD\D\\\\\\\DDDDD\DD\CCCCCCCCCCCCCCCCCCCCCCCCCCC;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHNNJNNNNNNNNNNNNNNNNNNNNNNQQQQNQNNNNNNNQQQQNQNQN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UUUMUMUMMMMMMMUUUUUMUMUMCCCCCCCCCCCCCCCCCCCCCUUUUMUMMMMMMMUUUUUMUMUM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;INNNNNINIIIIIIIINNNNNNNININIDDD\D\\\\\\\DDD\D\D\u;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHH@NNNJNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCCCCCCPPPPMPMMMMMMMPPPPPMPMPM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;NNNNNNNNNNNNNNNNNNNNNNNNNnnnnnnnnnnnnnnnnnnnusnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHHHJJJJJNJNNNNNNNNJJJJJJNJNJNNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCCCCCPPPPMPMMMMMMMPPPPMPMPM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;NNNNNINIIIIIIIINNNNNNNININIqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrnqqqqqqqqqqqqqqqqqqqqqqqqqqqgrrrrrrrrrrrrrrrrrrrrrrrrrrrqqq{q{{{{{qq{q{qrrrrrrrrrrrrrrrrrrrqqq{q{q{{{{{{{{qqqqqq{q{q{grrrrrrrrrrrrrrrrrrrrrrrrrrr;;;;;;;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHHHHJJJJJJJJJJJJJJJJJJJJJJJJJJJNNNNNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IMMMMMMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCCCCCCMMMMMMMMMMMMMMMMMMMMMM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IIIININNNNNNNIIIIINININqqqq{q{{{{{{{qqq{q{q{nnnnnnnnnnnnnnnnnnnknnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn;;;;;;;;;;;;;;;;;;;;JJJJHJHHHHHHHHJJJHJHHJHJH@JJJJJJJJJJJJJJJJJJJJJJJJJJQQQQNQNNNNNNNQQNNQNQN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCCCCCCMMMPMPPPPPPPMMMMPMPMP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IIIINININNNNNNNNIIIIIIINININunnnnnnnnnnnnnnnnnnujnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn;;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHH@NNNJNJNJJJJJJJJNNNNNNNJNJNJNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCCCCCCPPPPMPMMMMMMMPPPPPPPMPMPM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;INNNNNINIIIIIIIINNNNNNNININIunnnnnnnnnnnnnnnnnnujnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn;;;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHHHJJJJJJJJJJJJJJJJJJJJJJJJJJNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCCCCCCMMMPMPPPPPPPMMMMMMPMPMP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;INNNNNNNNNNNNNNNNNNNNNNNNNNNiiiiiiiiiiiiiiiiifffffffffffffffffffiiiiiiiiiiiiiiiiiiiiiiiii^fffffffffffffffffffffffffffiiinnnnnnnniiinininffffffffffffffffffffffffffiinininnnnnnnniininin^fffffffffffffffffffffffffff;;;;;;;;;;;;;;;;;;;;;@HHHHHHHHHHHHHHHHHHHHHHHHHHH@JJJJJJJJJJJJJJJJJJJJJJJJJJJNNNNNNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IMMMMMMMMMMMMMMMMMMMMMMMMMMM4CCCCCCCCCCCCCCCCCCCCCCCCCCCIMMMPMPMPPPPPPPPMMMMMMMPMPMP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IIIIIININNNNNNNNIIIIIIINININiiininnnnnnniiinininOOPPPPPPOOPOOPMMMJMJJJJJJJMMJMJMJOOOPOPPPPPPPOOOPOPOPMMMMJMJJJJJJJMMMMMJMJMJV\\\\\\VV\V\\MMMJMJJJJJJJMMJMJMJVVV\V\\\\\\\VV\V\V\MMMMJMJJJJJJJMMMMMMJMJMJ;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCPPPMPMMMMMMMPPPMPMPM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IIIIIIIIIIIIIIIIIIININIIIIIIVVV\\\\\\\VVV\V\\88787777777888787811111111111111111111,88898787777777788888878787'111111111111111111111111111888;8;;;;;;888;8;8;11111111111111111111111111888>8;8;;;;;;;;8888888;8;8;'11111111111111111111111111;;;;;;;;;;;;;;;;;;;777F7FFFFFFFF777777F7F7F<<<5<C<CCCCCCCC<<<<<<C<C<C7777F7FFFFFFF77777F7F7F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMFMFFFFFFFFMMMMMMFMFMF===<=B=BBBBBBBB=======B=B=BOOOOFOFFFFFFFFOOOOOOOFOFOF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7777F7FFFFFFF777777F7F7F888;8;;;;;;;888;8;8;rrruuuurrurueeeeeeeeeeeeeeeeeeerrruruuuuuurrrurreeeeeeeeeeeeeeeeeeeeeeeewwyyyyyyyyyeeeeeeeeeeeeeeeeeeeewwywyyyyywyweeeeeeeeeeeeeeeeeeeee;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHJJJJJJJJJJJJJJJJJJJJJJNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCMMMPMPPPPPPPMMMPMPMP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;NNNNNNNNNNNNNNNNNNNNNNNwwwywyyyywywyy]]]YYYYYY]Y]Y]YTTTUTUUUUUUUTTUTUTU]]]]YYYYYYY]]]]]Y]Y]YTTTTUTUUUUUUUTTTTTTTUTUTUcccbcbbbbbbccbcbcbTTTUTUUUUUUUTTTUTUTUccccbcbbbbbbbcccbcbcbTTTOTUTUUUUUUUUTTTTTTTUTUTU;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHJJJJJJJJJJJJJJJJJJJJJJJJJNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCCCCCCPPPMPMMMMMMMPPPPMPMPM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IIIIIININNNNNNNNIIINININININcccbbbbbbbccbcbCCCC;;;;;;CC;C;C;2224244444442242424CCCC;C;;;;;;;;CCCCCC;C;C;3222224244444444222222242424CCC@C@@@@@@@CC@C@C@22222424444444422222242424CCC;C@C@@@@@@@@CCCCCC@C@C@3222224244444444222222242424;;;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHHAAA5ABABBBBBBBBAAAAAABABABNNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMM5???<?C?CCCCCCCC???????C?C?CMMMPMMMMMMMMMMMMMMMMMMMMMMM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5IIIIIIIIIIIIIIIIIIIIIIIIIIICCC@C@@@@@@@CCC@C@C\\\\XXXXXXX\X\X\X\\\\\\\\\\\\\\\\\\\\\\\\XXXXXXX\\\\\X\X\XY\\\]\\\\\\\\\\\\\\\\\\\\\\\ooomommmmmmmoomomom\\\\\\\\\\\\\\\\\\\\oooomommmmmmmmoooooomomom\\\\\\\\\\\\\\\\\\\\\\\\\;;;;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHH@JJJJJNJNNNNNNNNJJJJJJJNJNJNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMTCCCCCCCCCCCCCCCCCCCCCCCCCCMMMMPMPPPPPPPMMMPMPMP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IIIINININNNNNNNNIIIIIIINININooommmmmmmooomomomkkkkkkkkkkkkkkkkkkkkqqqnqnnnnnnnqqnqnqnkkkkkkkkkkkkkkkkkkkkkkkkklqqqqqnqnnnnnnnnqqqqqqqnqnqn||||||||||||||||||qqqnqnnnnnnnqqqnqnqn||||||||||||||||||||qqqqqnqnnnnnnnnqqqqqqqnqnqn;;;;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHJJJJJJJJJJJJJJJJJJJJJJJJJJQQQQQNQNNNNNNNQQQQQQQNQNQN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMCCCCCCCCCCCCCCCCCCCCCCCCCCMMMMMMMMMMMMMMMMMMMM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;INNNNNINIIIIIIIINNNNNNNININI||||||||||||||||||||nnnnnnnnnnnnnnnnnnnknnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn;;;;;;;;;;;;;;;;;;;JJJJHJHHHHHHHHJJJHJHHHJHJHJJJJJJJJJJJJJJJJJJJJJJJJJJJQQQQNQNNNNNNNNQQQQNNQNQN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMTCCCCCCCCCCCCCCCCCCCCCCCCCCCMMMPMPPPPPPPMMMMPMPMP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IIIININNNNNNNIIIIINININIIIIIIIIIIIIIIIIAAAAAAAAAAAAAAAAAAAIISIIIIIIIIIIIIIIIIIIIIIII<AAAAAAAAAAAAAAAAAAAAAAAAAAASSSSSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAASSSSSSSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAA;;;;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHHHHH@JJJJJJJJJJJJJJJJJJJJJJJJJJJNNNNNNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MMMMMMMMMMMMMMMMMMMMMMMMMM4CCCCCCCCCCCCCCCCCCCCCCCCCCCMMMMMMMMMMMMMMMMMMMMMMM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;INNNNNNNNNNNNNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSSSSSSNNNNNNNNNNNNNNNNNNNYYYYYYYYYYYYYYYYYYYNNNNNNNNNNNNNNNNNNNNNNNNNNIYYYYYYYYYYYYYYYYYYYYYYYYYYYfffgfggggggfffgfgfgYYYYYYYYYYYYYYYYYYYYfffgfgggggggffgfgfgYYYYYYYYYYYYYYYYYYYYYYYY;;;;;;;;;;;;;;;;;;HHHHHHHHHHHHHHHHHHHHHHJJJJJJJJJJJJJJJJJJJJJJJNNNNNNNNNNNNNNNNNNNNN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UUUUTUTTTTTTTUUUUUTUTUTCCCCCCCCCCCCCCCCCCCCCCCPPPPPPPPPPPPPPPPPPPP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;PPPPPPPPPPPPPPPPPPPPPPfffgfgggggggfffgfgfg~Wcccu]`b`b`_`````mF|#|#|#7;;;;cy@HHHHs@JJJNMMMM;;;;;;;;;i;;IUMMj4CCC IUMM;;;;;;;;;;;INNNNFDDG;xCxCxCxxxppxpTX1\# AAG  ;;;;M@`HnH`HHI@WNWWNNQQNQ;;;;essMKddfM;;;;INNIN;;;;HHHHoJNNNN;;;;;;;;;m;;IMMMy4CCCIPPM;;;;;;;;;;;NNNNsnnn@nvJvIInIInqqqgrrry?<{Q{PBW^H;;;;Z@}HHlHQ@YJYYJINNN;;;;oMM`qqqMP;;;;IIIN{knnn)<7A;;;jJHHH@uuJuJQNNN;;;;;;;l;IMMMm4C|CC IMPP;;;;;;;;INNI|jnnn##;m;m;~jnnn;nvvv;MmCP;^iii^fffcnn_xyM```nMVddcPMOKPGMIJQVU\};bzHy~CHJHHN;}vQ__cC;I,897'1111T]>];^ \{{l [;YW_37@FH0@<5CNN;\\\aFOQF0=<B;IirruXeeeo)w.uy" ;uH]kkkN;M`wCwCwCP;NN]]YATOU\ceb;;QlylIWWX;;mzBfff;;,C6;3224%;@rljk5PKP"A5BtkjjP]]c5??<HV\^XY\]\\BoIsI mIIVVV ;;;jnnHnnHH_TJ`TNNN;;;;xMTTqClqC PP;;;;INIYkkklqqn^|||;;|@r@JJN;;;;;;;IMMkIMM;;;;;;;I|knnnBTnrGISI<AAAPSSSNd`dBQQQdmxzOYYYNNNNIY`YY`NQfdfdgdWrrr   ;;;U@nHnHnHV@\\J\JINNN;;;;;;;;kIrUrMrTUE4``C`CIPPPP;;;;;;;;IPPP// (ww)KJK. ~'38BKSg5OQ      " & 0 6 < B t  !! !!!"!&!+!T!^!i!y!!!!!!!!!""" " """"" ""","."7"="C"E"H"S"Z"b"g"s"w"{""""""""###$n$s$$$$$%%K%%%%%%%%%%%%%&&&&/&@&B&a&e&j&m&o'''='V''0000 060001c1d12221292{22222233333333333ݬ@J\o/ܮ+P[exܯ'(C]{`w˲ 3HIdāĔĝĸĽ (ŪŴalUpȩȼ,5P}Ɉɞ)*Li}ʅʘʙʼ"Aexz˜˝˸˹ v̘-9:\y͈͉͔ͱ6Xή9Tύϡϱ-alЂФ 0m|щѠ,I\eҀF| @E\ԕԩԪ<Ցեզ.8]guփמ<=^N7F>FE>=QZYZDU߷&?ߊ;]cmSޘޥvtOR,݄}ޅVaSP~#_gk p54#G΁μιγ.xVxQwwwwwwwwwwwwuwgU$wfwZw2wwTwvvvvvvvv{RHt=Ht<t4HCt3t-ssssspEsoscEsbsVsTsNEsMsEsCsAs8s4srr<r"rrqqqqq@qqq@iqqqqq?q?q?jq?OqqqiqVqSqG>3qFq>=q=q4q1qq?@!AB&CDEFGHIJ" "L"K"S!$$efgl!vw    $j$k!h$rde`ab~KLMOQRSTghxyz{|}Pf_"Vc>?$u$v@$w$xABCD$y$z${$|$}EFGHIJKL$~MNOPQ$$R$$$S$$$$$$$TU$VWX$$$$$$YZ$$[$$$\]$^$$$$$$_$$$`$$$abc$$d$$ef$g$$$$$hi$jklm$$nopq$$$r$$$s$$$$$$$tu$vwx$$$$$$yz{$|$$}~$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%% % % % % %%%%%%%%%%%%%%%%%%% %!%"%#%$%%%&%'%(%)%*%+%,%-%.%/%0%1%2%3%4%5%6%7%8%9%:%;%<%=%>%?%@%A%B%C%D%E%F%G%H%I%J%K%L%M%N%O%P%Q%R%S%T%U%V%W%X%Y%Z%[%\%]%^%_%`%a%b%c%d%e%f%g%h%i%j%k%l%m%n%o%p%q%r%s%t%u%v%w%x%y%z%{%|%}%~%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&& & & & & &&&&&&&&&&&&&&&&&&& &!&"&#&$&%&&&'&(&)&*&+&,&-&.&/&0&1&2&3&4&5&6&7&8&9&:&;&<&=&>&n&o&p&q&r&s&t&u&v&w&x&y  &z&{&|&}&~&  && &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& !"&&&&#$&&&&&&%&'&&&&&&()*''''''+'' ' ,' ' ' -'''''''./'''0''''''123'9':4';'<'=5'>'?'@'A'B'C678'D9'E:;'F'G'H'I'J<'K'L'M'N'O'P'Q=>?']'^'_'`'a'b@A'c'dB'e'f'gC'h'i'j'k'l'm'nDE'o'pF'q'r's't'u'v'wG'x'y'zH'{'|'}I'~''''''JKMN''O'P'Q'R''''STU'V'W'''XYZ''['''\''''''']^'_'`''''''abc'd''efgh'''''ij'klmno'p'qrs''t'''u''''''(vw(xyz(((((({|(( }( ( ( ~( (((((((((((:(;(<(=(>(?(@(A(B(C(D(E(F(G(H(I(J(K(L(M(N(O(P(Q(R(S(T(U(V(W(X(Y(Z([(\(](^(_(`(a(b(c(d(e(f(g(h(i(j(k(l(m(n(o(p(q((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((()))))))))) ) ) )) )!)")#)$)%)A)B)C)D)E)F)G)H)I)J)K)L)M)X)Y)Z)[)\)])^)_)`)a)b)c)d)e)f)g)h)i)j)k)l)m)n)o)p)q)r)s)t)u)v)w)x)y)z){)|)})~))))))))))))))))))))))))))))))))))))))))))))))))))))    ) ))********** * * * * ********** ******!*** "*!*"*##$%*/*0*1*2*3*4&*5*6*7'()*O*P**Q*R+,*S-*T.*U*V*W/0*X1*Y2*Z3*[4*\*]5*^*_*`6*a*b*c79*******:***;***<*******=>*?********@AB**C***D*******EF*G*H******IJ*******KL++++++ M+ + + N+ ++O++++++++P+Q+R++++++S+ +!+"T+#+$+%U+&+'+(+)+*+++,V+-+.+/+0W+1+2+3+4+5+6XY+7+8Z+9+:[\+;]+<+=+>+?+@^_+A`+Ba+C+D+E+F+G+Hbcd+d+ee+f+gfg+h+i+j+k+l+m+nhi+ojklm+p+q+r+s+tno+u+vp+w+x+yq+z+{+|+}+~++rs+tuv+++++wxy++z+++{+++++++|}+~+++++++++++++++++++++++++++++++++++,+,,,-,.,/,<,=,>,?,@,A,B,C,D,E,F,G,H,w,x,y,,,,,,,,,,,,,,,,,,,,,,----------+-,---.-/-0-1-2-3-4-5-6-7-8-9-:-;-<-=->-?-@-A-B-C-D-E-F-G-H-I-J-K-V-W-X-Y-Z-[-\-]-^-_-`-a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-{-|-}-~------------------------------------------------------------------------------. . . . . ................... .!.".#.$.%.&.'.(.S.T.U.V.W.X.Y .Z.[.\ .].^._ .`.a.b.c.d.e.f  .g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.{.|.}.~................................. !"..#...$.......%..&.'......()..*...+.......,-.../......01..2...3....///45/6/78//9:;/)/*/+/,/-/.//?@/0A/1B/2/3/4/5/6/7CD/8/9E/:FGHIJ/;//?KL/@M/ANO/B/CP/DQRS/E/FT/G/H/IU/J/K/L/M/N/O/PVW/QXYZ[/R/S/T/U/V\]/W/X/Y/Z/[/\^`a//b///c/d/////ef/g/hi////jkl//m///n///////op/qrs//////tu//v///wxyz/{////|}~/////////////////////0$0%0&0'0(0)0*0+0,0-0.0/000102030405060708090:0;0<0=0>0?0@0A0B0C0D0E0F0G0H0I0J0K0L0M0N0O0P0Q0R0S0T0U0V0W0X0Y0Z0[0\0]0^0_0`0a0b0c0d0e0f0g0h0i0j0k0l0m0n0o0p0q0r0s0t0u000000000000000000000000000000000000000000000000000000001111111111 1 1 1 1 1111111111111111111 1!1"1#1V1W1X1Y1Z1[1\1]1^1_1`1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1r1s1t1u1v1w1x1y1z1{1|1}1~11111111111111111111111111111 111111 111  1111111 111111111111111111122222222 2!2"2#2$2% 2&!2'2(2)"#2*$2+2,2-2.2/2021%&2=2>2?2@2A2B2C'()2_2`*2a2b2c+,2p2q2r2s2t2u-2v2w2x.2y2z2{/2|2}2~222202212222222234225222622222227829:;22<222=2>22222?@2A2BCD2222EF22G222H2I22222JK2LMN22222OPQ22R222S2222222TU2VWX222222YZ\]3'3(^3)3*_`3+3,3-3.3/3031a3233bcd343536373839ef3N3O3P3Q3R3Sghij3bklmno33p333q3333333rs333tw444444xy44z4 4 4 {4 4 44444|44}4~444444444444444444444444444444444444444444444444444444444444444444444444444444444444445555555555 5 5 5 5 5555555555555555555 5!5"5#5$5%5&5'5(5)5*5+5,5-5.5/505152535455565758595:5;5<5=5>5?5@5A5B5C5D5E5F5G5H5I5J5K5L5M5N5O5P5Q5R5S5T5U5b5c5d5e5f5g5h5i5j5k5l5m5n5o5p5q5r5s5t5u5v5w5x5y5z5{5|5}555555555555555555555555555555555555555555555555555555555555555555555555555555555 5 5 5 5  5  5555555 55 555 5666666 6666 6 6 6 6 66  66 666 6666666  6 6 666 6!6"6#  6$6%6&6'6(6) 6*6+6,6-6.6/60 6162 63 646566676869  6:6; 6<6=6> !6?6@6A6B6C6D " #6E $6F % & '6i6j (6k6l ) *6m6n6o6p6q6r + , -6s .6t /6u6v6w6x 06y 1 26z 3 46{6|6} 56~666666 6 766 8 966666 : ; <66 =666 >6666666 ? @66 A B D E66 F666 G6 H66666 I J66 K L666666 M666 N666 O Q R7:7; S7<7= T U7> V7?7@7A7B7C W X7D7E7F Y7G7H7I7J7K7L Z [7M7N \ ]7^7_7`7a7b7c7d ^ _7x7y7z7{7|7}7~ `777 a777 b7777777 c d f g77 h777 i7777777 j k777 l777777 m n7777777 o p777 q r8*8+8,8-8.8/ s t8081 u828384 v85 w86878889 x y z {8D8E8F |8G8H8I }8J8K8L8M8N8O8P ~  8\8] 8^8_8` 8a8b8c8d8e8f8g 8h 8i 8j8k8l8m8n8o 8p8q 8r 8s8t8u8v 8w 8x8y8z 8{ 8|8} 8~88 8888888 8 888888 88 888 88 8888 8 8 888 8 888 888 88888888 88 8 88888 8 8 8 88 888 8888888 8 8 888888 8 888 8 8888 888 888 888 8888888 8 8888888 88 888 8 88 9 9 9 9999 99 9 9 9 9 9 99999 9 999999 99 9'9( 9) 9*9+9,9-9.9/ 9091 929394 95969798999:9; 9< 9= 9>9?9@9A9B9C  9D9E 9F9G9H 9I9J9K9L9M9N9O  9P 9Q 9R9S9T9U9V9W 9X9Y 9Z9[9\   9]9^9_9`9a  9b 9c 9d9e9f9g9h9i  9j9k 9l9m9n 9o9p9q9r9s9t9u  9v9w  9x9y9z9{9|9}  9~9 999 9999999 ! "999 #999999 $ %99 &999 '9999999 ( )9 *9 +999999 , -99 .999 /9999999 0 19 29 39 49999 5 699 7999 899999 99 : ;9 <9 = > ? @ A B C D999 E999 F9999999 G99 H99999999 I J99 K999 L M N9999 O P Q9 R S T U999 V9 W X99 Y9 Z [ \9 ]99999 ^ _9 ` a b c99999 d e99 f999 g::::::: h i: j k l:: : : : : m n:: o: p: q::::::: r:::: s:::: :!:" t:#:$:% u:&:':( v w x:<:= y:>:?:@ z:A {:B:C:D:E:F | }:G ~:H  :I:J:K:L:M :N:O :P:Q:R :S:T:U:V:W:X:Y :Z :[ :\:]:^:_:`:a :b:c:d :e:f:g :h:i:j:k:l:m:n :o:p :q:r:s:t:u:v :: ::: : ::::: : : ::: :::::: :::::::: : : :::::: :::::: ::: ::: ::::::: : : :::::: :: :::::: :: ;;; ;;;;; ; ; ; ; ; ; ;; ;#;$;%;&;';(;) ;E;F ;G;H;I ;J;K;L;M;N;O;P ;Q ;R;S;T;U;V;W;X;Y ;Z;[;\ ;];^;_ ;`;a;b;c;d;e;f ;r;s ;t;u;v ;w;x;y;z;{;|;} ;~ ; ;; ;; ; ;;;;; ; ; ;; ; ;; ; ; ;; ;;;; ; ;;;;;; ;; ;;; ;;;;;;; ; ;;;;;; ;;; < < < < < <<<<<<< < <<<<<< <1<2<3<4<5<6  ======= ? @= A B C====== D=== E= F= G======= H==== I J K>> L>>> M>>>> > > > N O> P Q R>>>>>> S T>> U>>> V>>>>>>> W X> Y>! Z>">#>$>%>&>' [>(>)>* \ ]>:>;><>=>>>?>@ ^>A>B>C _ `>T>U>V>W>X>Y a b>Z>[ c>\>]>^ d>_>`>a>b>c>d>e e f>f g>g h>h>i>j>k>l>m i>n>o>p j>q>r>s k m>>> n>>> o>>>>>>> p q> r> s>>>>>> t v w>> x>>> y>>>>>>> z {> |> }>>>>>> ~ >>>>>>> >?? ??? ??? ?? ?!?"?#?$?% ?& ?' ?(?)?*?+?,?- ?.?/?0 ?1?2?3 ?4?5?6?7?8?9?: ?;? ???@?A?B?C?D ?E?F ?G?H?I ?J?K?L?M?N?O?P ?Q ?R ?u?v ?w?x ?y?z?{?|?}?~ ? ? ?????? ?? ??? ??????? ? ? ?????? ?? ??? ??????? ? ?????? ?? ?? ??????? ? ?????? ?? ??? ??????@ @ @ @@@@@@ @ @ @ @ @ @ @@@@@@@ @ @@@@@@ @8@9 @:@;@< @=@>@?@@@A@B@C @D @E @F@G@H@I@J@K @L@M @N@O@P @Q@R@S@T@U@V@W @X@Y@Z@[ @\@]@^@_@`@a @v@w@x@y@z@{ @|@}@~@@@@ @@ @@@ @@@@@@@ @ @ @@@@@@ @@@ @@@ @@@@@@ @@@@@@ @@ @@@ @@@@AAA A A AAAAA A A A A AAA AAAAAAA A#A$ A%A&A' A(A)A*A+A,A-A. A/A0A1   ATAU AVAWAX AYAZA[A\A]A^A_  A` Aa AbAcAdAeAfAg AhAi AjAkAl  AmAnAoApAqAr  As   AtAuAvAwAxAy  AzA{ A|A}A~ AAAAAAA  A   AAAAAA  !AA "AAA #A $AAAAA % &A ' ( )AAAAAA * +AA ,AAA -AAAAAAA . /A 0A 1AAAAAA 2AAA 3 4AAAAABB 5BBB 6 7 8BB 9BBB :B!B"B#B$B%B&B' ; B*B+B,B- ?B. @B/B0B1 A CBdBeBf D EBu FBvBwBxByBzB{ G H IBB JBBB KBBBBBBB L MB NB OBBBBBB P QBBBBBBB R S TBB UBBB VBBBBBBB W XBBB YBBBBBB ZBBB [BBB \BBBBBCC ]CCCC ^CCCC C C _ `C C aCC b cC dCCCCC e fC gCCCCCCCC hCC C! iC"C#C$ jC%C&C'C(C)C*C+ k l m nC6C7 oC8C9C: pC;CC?C@CA q rCB sCC tCDCECFCGCHCI u v wCJ xCKCLCM yCN zCOCPCQCRCS { |CT } ~ CUCVCW CXCY CZC[ C\C]C^ C_C`CaCbCcCdCe Cf CgChCiCjCkCl CC CCC CCCCCCC C CCCCCC CC CCC CCCCCCC C C CCCCCC CCC CCC CCCCCCC CC CCCCCC CCCCCCC CCCCCCCC C CCCCCCCC CC CCC CCCCDDD D D DDDDD D DADBDC D[D\D] D^D_D` DaDbDcDdDeDfDgDh Di DjDkDlDmDnDoDpDq DrDs DtDu Dv DwDxDyDzD{ D| D} D~DDDDD DDD DDD DDDDDDD DD DDDDDDDD DDD DDD DDDDDDD DD D DDDDDD DDD DDD DDDDDDD D EE EEE EEE E!E"E#E$ E% E& E'E(E)E*E+E, E-E. E/E0E1 E2E3E4E5 E6E7 E8 E9 E:E;EE? E@EA EBECED EEEFEGEHEIEJEK EL EMENEOEPEQER EE EEE E EEEEE E E EEEEEE EE EEE EEEEEEE E E EEEEEE  EE EEE EEEEEEE  E EEEEEE EEE EEE EEEEEEEE   EE EEE EEEE EE  E E EEE EE  EE EEE  E EEEFFF !FF " #F $FFFFFF % &FF 'FFF (FF F!F"F#F$F%F& )F' *F( +F)F*F+F,F-F. ,F/F0F1 -F2F3F4 .F5F6F7F8F9F:F;F< /F= 0F>F?F@FAFBFCFDFE 1 2FFFG 3FHFIFJ 4FKFLFMFN 5FOFP 6FQFR 7FS 8FTFUFVFWFXFY 9FZF[F\ :F]F^F_ ;F`FaFbFcFdFeFf ?FqFr @FsFtFu A BFFFFFF C DFF EFFF FFFFFFFF G HF IF JFFFFFF K LFF MFFF NFFFFFFF OFF PF QFFFFFF R SFF TF U V W XFFFFFF Y ZF [F \FFF ]FF ^FFF _FFF `FFFFFFF a bFFF cFFFFFF d eFF fFFF gFFFFFFF h iF jF keC N:N;NN?N@NANBNCNDNENFNGNHNINJNKNLNMNNNONPNQNRNSNTNUNV  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_NW!Fbk!5" jmnturs"SF!!hɸ,KPXYD_^-, EiD`-,*!-, F%FRX#Y Id F had%F hadRX#eY/ SXi TX!@Yi TX!@eYY:-, F%FRX#Y F jad%F jadRX#Y/-,K &PXQXD@DY!! EPXD!YY-, EiD` E}iD`-,*-,K &SX@Y &SX#!#Y &SX#!#Y &SX#!#Y &SX#!@#Y &SX%EPX#!#!%E#!#!Y!YD- ,KSXED!!Y-+++PG7(+VG7(+\G7(++ E}iD<E@9)_Z  ~ *  ] -xo8{ : !"#$$%&N&'j((W)F)++-H.z/0W1 12H23W34M45"556795:;<=^? ?@B>BC4D{E>FGI-IKLMHMNbNOPVQQRKRToTVpWWWWXYzZ[u\\\]]{]^^s^_s``T```aIaabbpbcc[d)def$fkfhijjk*lnoqrXstuAuuwwAwxz|}E}2@H9vW>U+7O*}Y`:hH[Ev kczw> Eƃ*ɖѻ^ݬoFU$Hr!cIKs  F 2  ,I)!hzeDC b!!A!"@"#{$.%&()4*++,,-q-.g.0v11e113L4]5A6a7/79:::#>@-AVBCDEhF%FGCGHIAJJDK K/KLlMNOFQ:QRSU<VXZZ[]``acdPe9fgh|ijkklemnanopApq9qrtrtu<v.vwNxbyz{4{x|6|u}~'q%2ePB0wMh]Ys0;~=^åĉ5ŭxUȧɌ/ʦ*zLͼZόОҤ+7԰՝ iؔp݅lYi|^+{B{:rn,TZFdl2:G@|zP%ax%u      v  r  j  W`[|!$O&(t*%,c. /25369;>@CqDG$JLINQTJVQXuZ\]^`ajbacdfgMh%j)klnoqqsJtvwy+z|~^]\iP/Z8 {K}D:nQiʁ΂ѿӓ;ػ&(?%:=n^x ) q &4(8& C"$0'x),o.0V246849;<>2@2BEGJKMNO8QbQRSUWYFZ[\]_9`b8cegVikm]oyqrt{vxz{|C~8^{)JOxc,#Wǟm ԶObެx\Ro*fuEhvk   P \HC n!"#m$~%t&'c(j*a+.102S57;-<>@gABDEhFGJLP(ReSVeXY[0\^a-cdefhk\lqn7pr`u%vwx)xyyzzz{T{|}o~~m vk:| 9%2S?2*sX_.r@_WB:nQ%#Zo7N"asj  1ȎBˋR-:twe`ԤEWq `-A:4I}JY5^W ; <2s] "$s%')k+,5-.162"3L46T79:;a=> ?-@ACuE3FHlIL?NOPRSUgVWY&Z\I^Cacgmi4klnpr8tux?{}2\K^{m}b87g:_F©ĴƻTo_ҟO0[ܒ޾ DA?"DHLw} P  bT1d2 "$&(+M-d.024 5h7+8:<>@xBDFHAJKMOQS[U~WZA\8^9` adgj5lFnkpszuxqz|<o{3AKrt:  X,. MU˻η҈ԹR۩ 61W8~LT #  9 Uv{!"t$%&()+$,. 0{2<3<45796; ;=>@ ACFGI4JLNPQST6UWPXZ@\y^?_6`acefiJklmnopqs7uvxy{|~3IQuX^l-{|\IJ2eOa;^ɍʖs(=1іUד\_ܾVߍgvJ"{0     ?`g "d#o$&')e+,.013A579:S?@ByDGHHJDKMOPRTSVWZ[`] ^`b?cdefhik&lmoqLsudwyz|~}~3J73I0;c{*N,Uoɫ˭MЮ ӴWhduRG0[7'9? 2          z 5   " $ ' ); + -^ 0 2 5 7 8 :n ; =v ?f A! B D FP HM J M OY P R T U W YR [^ \ ^ _| ac c d f h j l n^ q rN s u w yq {\ }  C p W X    G a E N > 3 m  Y ã  ƚ   ͵ ӻ ե ڕ +   b ) ' * X  x 8  H   b ` B    i  e  /  " ! $ %1 & (8 *: ,' . / 1 3y 5| 7 8$ 9| : < > ? A" B C E< Fy I J L O Q S T V# W Y^ Z ]a ^t _ a4 c d fL g j~ ll m o) p r t u v w y |, }  W # 5 W  [   2 | 2 w < t  # S 5   } ƒ Ǧ  ̸ v / Ռ Q ܑ  ! K I ~ M 5    Q ! @ T    N    +     ", # %b ' *q -. . 0j 1 3 5 6 89 9 ; =K ? @ Cw E GB H~ J K Mq No O P Rd T V XH Y [c ]7 ^ _ a\ b dS f5 g i kE m o p r` s u wj y { }  e = 3 U  s @ M 4 % V \ /  s r  µ  Ǥ c ̢ ^ Е һ  Ւ ׉ Z ! * . ߑ  M / ]   G < 1  L    M   @  !" #b &C ( * , // 0 2 4 6 90 ;i = @ B D G H K M P RI U X' Y \W ^ ab d f i k m p rp t v yP { }  ; $ n .   B ; P \ ' 9  p  Z ĸ ƈ  p 2   0 ۷ 2 C  = )  K  Y     G  _ ]  }   >    r ! " $ & ' )^ +6 , .` 0 2 46 5 8% 9 ; =y ?0 A[ C EH G% I K M Pn Q Sn U\ W Y, Z \ ]} ^ ` bM d( e h i j l m oH p re s u w% x zJ { ~-  & D $ m 8 Y  `  /  I  A  * &   D w  ׌ ڢ  c 1 b I  1  yA  %ceevrP I"$&)+-0247"9Q;=E>@fACEGIKNPRTWdXoY[K]K_c}epg\iZkn3prtvx{}rsN LS*-2yf,K9t† Lʒ;"c3KF[?d(UT01   |8!#+%i(F*a-0J2479;>;@BEH8IKPM%OR-U*X[]y_a?ce`gsiknq5sv1yH{~!$a(OM^{6Kg;*ҏՂvڿN?Wnt76 t O 83 #6%'*+-012469;<=U>@lBD2FIMOQSVXZ\]^`;c(df|hjlimp.qs]tuwEy z|B}L<qUvi k'l?(__w J2N3 zÍ<fOҊOAtڌu޾Bfz+! K*  D+093r!#[$"%A&W(2)+{,./013g579:;=>@tACZE+G*JLP+RTVY\ ]_adfAhjm|oqsvwya{T}9w7F L6Vwvhœ @nՎ{ڔ~<80d{E`8WkZA )   W #$'()*,.0y1446,7y9":<>r@_BD2EjFInJL~NOQ}RTVX_Z\^^:`befhjloqsktvwy^z|~yCTh"C1LS AN×eǕɚ˟dϋҶS\5)޺Vr4]n 8 &  _ ApW U"%'q),9.0y258:; =@BSDFHJAL$N,P RTVX\Z]`c7fh;jlorurx{1}" MRH/W wu56Um'>'#c£jɈѨpןٮސv- r?AY% V < Ihe$F "$T&(*,./1s365b6,78:1< >~@NBCDFNHLJ KM NOQSsU'VXZ)[^_ae gijmprsuwty{~8P~7S91_R=;`>Jp3krW3͘`ѢNz؛prUPg.uVUDB   G#~H!#%g'*.03 5l80:6<? ADGfJMPREUFWAY[^brdgj lnq[svy|y29dET_)Z/hdėʄkՓؕ X@fPivN  +[B"B%?(,9.1H37":$=?BDFI7KMPSU?W^Y,\^`cegjem#o"q[svy|M#Hjy0 -.yW;O.sEKZus. X~K/_HF(ŗǯQk̨>Ϝ?,=ӤFS՛#|ؾٻ۶S)߃ EfZq!w*0X@{s 7e/Go ZHi'yzU  j Mz:`D=;if 1 i !;!"##d$Z%%&&`'X'()*-++,G,,-5-q.13'47s78:;n<>!>?I@@A7AB$BCEpG;H-IKLNPRSUWXZ\]`be9eefGfguhnj lmroBqarWtvxza|n~8@yTFJJ cO9wB3<j}u̫e39՝GهۆWROAmxT p$+EX  : osAlfCT  !"#b$F%a%&()Y*+3,-i.0O13$467'8n9:>@BqDEFHIKMNQqSUVVWVWX5YYZ[\M\]_ `a=ab>c>cd]deefgg3gh{ioij jklmoqvstvwz{~2T"T~W]~cdXmG39_$_|mȝl&'C$0W}ٿ/a.Fq R%5G4Y  !"#%)++",--U.01U122o6I9 >3>?N?@DiFFGCJKN/PRSTlU!X[N[\p^_Q`:a9cNdCeBjEnqgsw@wx`yzI{c|}~h%%S`*M|&"@VEv5&kj 4"X;>\1k `VԽQ׎׹ؘEڄN݉+ޙ(tߚ'[] # ]XYA w4r-B(Hdz  W !^!"-"#?#$&(*+.k112 23~4^52556z77I7y788X889F99:!:u:;;];?@&AABC2CDDDDEFGyHAHHI:IJL;LMN OnP{QqRRSTU UVgVWX:YZ[]s^_m`'`ahabc,dfng{ijkQmnoEp2pqPrsu+w yy{w|l}A}~I@USM*G91{ ;̧d/*j,Bp mr n5Guk ^ " BcB "$w&f(z*-./:1468<>@D FJBLP&SV2YW]A_9a!c<eggikmpruvy|~X!oA:-JXbp >u(j^Q!O-/yEWCQzjG>Zi9 N  pX9 !%)H*4+,,-5-s-..V.//0"0`012134l535667 78B89K99;1;;?@~AfBDmE}FFFG5G{H(HI8IJKLtMVNOO5PPQVQR.RS"SSTfTUPUVFZ^abVghkmps!uxXxz{Q|5}[~`Z fxl $2|L 1k, I{xKõźǤ˺̛0Ъѿԯ#Ճv-װ4kء"Eg٫4Vrێ۪6Rn܊ܦ2Nj݆ݢݾ.Jfނޞ޺*Fb~ߚ߶ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf  * F b ~      & B ^ z      " > Z v       : V r       6 R n     2Nj.Jf$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt   8 T p     !!4!P!l!!!!!""0"L"h"""""##,#H#d#####$ $($D$`$|$$$$%%$%@%\%x%%%%&& &<&X&t&&&&'''8'T'p'''''((4(P(l((((())0)L)h)))))**,*H*d*****+ +(+D+`+|++++,,$,@,\,x,,,,-- -<-X-t----...8.T.p.....//4/P/l/////0000L0h0000011,1H1d111112 2(2D2`2|222233$3@3\3x333344 4<4X4t444455585T5p555556646P6l6666677*7F7b7~77778 8&8B8^8z888899"9>9Z9v9999:::::V:r:::::;;6;R;n;;;;;<<2>*>F>b>~>>>>? ?&?B?^?z????@@"@>@Z@v@@@@AAA:AVArAAAAABB0BLBhBBBBBCC,CHCdCCCCCD D(DDD`D|DDDDEE$E@E\ExEEEEFF F<FXFtFFFFGGG8GTGpGGGGGHH4HPHlHHHHHII0ILIhIIIIIJJ,JHJdJJJJJK K(KDK`K|KKKKLL$L@L\LxLLLLMM M<MXMtMMMMNNN8NTNpNNNNNOO4OPOlOOOOOPP0PLPhPPPPPQQ,QHQdQQQQQRR"R>RZRvRRRRSSS:SVSrSSSSSTT6TRTnTTTTTUU2UNUjUUUUUVV.VJVfVVVVVWW*WFWbW~WWWWX X&XBX^XzXXXXYY"Y>YZYvYYYYZZZ:ZVZrZZZZZ[[6[R[n[[[[[\\2\N\j\\\\\]].]J]f]]]]]^^*^F^b^~^^^^_ _&_B_^_z____``"`>`Z`v````aaa:aVaraaaaabb6bRbnbbbbbcc2cNcjcccccdd.dJdfdddddee*eFebe~eeeef f&fBf^fzffffgg"g>gZgvgggghhh:hVhrhhhhhii6iRiniiiiijj2jNjjjjjjjkk.kJkfkkkkkll*lFlbl~llllm m&mBm^mzmmmmnn"n>nZnvnnnnooo:oVorooooopp6pRpnpppppqq2qNqjqqqqqrr.rJrfrrrrrss$s@s\sxsssstt t<tXttttttuuu8uTupuuuuuvv4vPvlvvvvvww0wLwhwwwwwxx,xHxdxxxxxy y(yDy`y|yyyyzz$z@z\zxzzzz{{ {<{X{t{{{{|||8|T|p|||||}}4}P}l}}}}}~~0~L~h~~~~~,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (DZv:Vr6Rn2Nd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Lh,Hd (D`|$@\x <Xt¬8TpÌâþ.JfĂĞĺ*Fb~ŚŶ &B^zƖƲ">ZvǒǮ:VrȎȪ6RnɊɦ2Njʆʢʾ.Jf˂˞˺$@\x̰̔ <Xt͐ͬ8TpΌΨ4PlψϤ0LhЄРм,HdрќѸ (D`|ҘҴ$@\xӔӰ <XtԐԬ8TpՌը4Plֈ֤0Lhׄנ׼,Hd؀؜ظ (D`|٘ٴ$@\xڔڰ <Xtې۬8Tj܆ܢܾ.Jf݂ݞݺ*Fb~ޚ޶ &B^zߖ߲">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &<Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt2Nj.Jf*Fb~ &B^z">Zv:Vr  6 R n       2 N j       . J f       * F b ~      & B ^ z    ">Zp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh&B^z  " > Z v    !!!:!V!r!!!!!""6"R"n"""""##2#N#j#####$$.$J$f$$$$$%%*%F%b%~%%%%& &&&B&^&z&&&&''"'>'Z'v''''(((:(V(r((((())6)R)n)))))**2*N*j*****++.+J+f+++++,,*,F,b,~,,,,- -&-B-^-z----..".>.Z.v....///:/V/r/////0060R0n000001121N1j1111122.2J2f2222233*3F3b3~33334 4&4B4^4z444455"5>5Z5v555556646P6l666667707L7h7777788,8H8d888889 9(9D9`9|9999::$:@:\:x::::;; ;<;X;t;;;;<<<8>0>L>h>>>>>??,?H?d?????@ @(@D@`@|@@@@AAA:AVArAAAAABB6BRBnBBBBBCC2CNCjCCCCCDD.DJDfDDDDDEE*EFEbE~EEEEF F&FBF^FzFFFFGG"G>GZGvGGGGHHH:HVHrHHHHHII6IRInIIIIIJJ2JNJjJJJJJKK.KJKfKKKKKLL*LFLbL~LLLLM M&MBM^MzMMMMNN"N>NZNvNNNNOOO:OVOrOOOOOPP6PRPnPPPPPQQ2QNQjQQQQQRR.RJRfRRRRRSS*SFSbS~SSSST T&TBT^TzTTTTUU"U>UZUvUUUUVVV:VVVrVVVVVWW6WRWnWWWWWXX2XNXjXXXXXYY.YJYfYYYYYZZ*ZFZbZ~ZZZZ[ [&[B[^[z[[[[\\"\>\Z\v\\\\]]]:]V]r]]]]]^^6^R^n^^^^^__2_N_j_____``.`J`f`````aa*aFaba~aaaab b&bBb^bzbbbbcc"c>cZcvccccddd:dVdrdddddee6eReneeeeeff2fNfjfffffgg.gJgfggggghh$h@h\hxhhhhii i<iXitiiiijjj8jTjpjjjjjkk4kPklkkkkkll0lLlhlllllmm,mHmdmmmmmn n(nDn`n|nnnnoo$o@o\oxoooopp p<pXptppppqqq8qTqpqqqqqrr4rPrlrrrrrss0sLsbs~sssst t&tBt^tzttttuu"u>uZuvuuuuvvv:vVvrvvvvvww6wRwnwwwwwxx2xNxjxxxxxyy.yJyfyyyyyzz*zFzbz~zzzz{ {&{B{^{z{{{{||"|>|Z|v||||}}}:}V}r}}}}}~~0~L~h~~~~~,Hd (D`|$@\x <Xt2Nj.Jf*Fb~ <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh&B^z">Zv:Vr0Lh,Hd (D`|$:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh„ ¼,HdÀÜø (D`|ĘĴ$@\xŔŰ <XtƐƬ8TpnjǨ4PlȈȤ0LhɄɠɼ,Hdʀʜʸ (D`|˘˴$@\x̰̔ <Xt͐ͬ8TjΆ΢ξ.JfςϞϺ*Fb~Кж &B^zіѲ">ZvҒҮ:VrӎӪ6RnԊԦ2NjՆբվ.Jfւֺ֞*Fbxהװ <Xtؐج8Tpٌ٨4Pfڂڞں*Fb~ۚ۶ &B^zܖܲ">Zvݒݮ:Vrގު6Rnߊߦ2Nj.Jf*@\x <Xt8Tp4Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|  $ @ \ x       < X t       8 T p       4 P l       0 L h     ,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt   8 T p     !!4!P!l!!!!!""0"L"h"""""##,#H#d#####$ $($D$`$|$$$$%%$%@%\%x%%%%&& &<&X&t&&&&'''8'T'p'''''((4(P(l((((())0)L)h)))))**,*H*d*****+ +(+D+`+|++++,,$,@,\,x,,,,-- -<-X-t----...8.T.p.....//4/P/l/////0000L0h0000011,1H1d111112 2(2D2`2|222233$3@3\3x333344 4<4X4t444455585T5p555556646P6l666667707L7h7777788,8H8d888889 9(9D9`9|9999::$:@:\:x::::;; ;<;X;t;;;;<<<8>0>L>h>>>>>??,?H?d?????@ @(@D@`@|@@@@AA$A@A\AxAAAABB B<BXBtBBBBCCC8CTCpCCCCCDD4DPDlDDDDDEE0ELEhEEEEEFF,FHFdFFFFFG G(GDG`G|GGGGHH$H@H\HxHHHHII I<IXItIIIIJJJ8JTJpJJJJJKK4KPKlKKKKKLL0LLLhLLLLLMM,MHMdMMMMMN N(NDN`N|NNNNOO$O@O\OxOOOOPP P<PXPtPPPPQQQ8QTQpQQQQQRR4RPRlRRRRRSS0SLShSSSSSTT,THTdTTTTTU U(UDU`U|UUUUVV$V@V\VxVVVVWW W<WXWtWWWWXXX8XTXpXXXXXYY4YPYlYYYYYZZ0ZLZhZZZZZ[[,[H[d[[[[[\ \(\D\`\|\\\\]]$]@]\]x]]]]^^ ^<^X^t^^^^___8_T_p_____``4`P`l`````aa*aFaba~aaaab b&bBb^bzbbbbcc"c>cZcvccccddd:dVdrdddddee6eReneeeeeff2fNfjfffffgg.gJgfggggghh*hFhbh~hhhhi i&iBi^iziiiijj"j>jZjvjjjjkkk:kVkrkkkkkll0lLlhlllllmm,mHmdmmmmmn n(nDn`n|nnnnoo$o@o\oxoooopp p<pXptppppqqq8qTqpqqqqqrr4rPrlrrrrrss0sLshssssstt,tHtdtttttu u(uDu`u|uuuuvv$v@v\vxvvvvvww6wRwnwwwwwxx2xNxjxxxxxyy.yJyfyyyyyzz*zFzbz~zzzz{ {&{B{^{z{{{{||"|>|Z|v||||}}}:}V}r}}}}}~~6~R~n~~~~~2Nj.Jf*Fb~ &B^z">Zv4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &B^z">Zv:Vr6Rn2Nj.Jf*Fb~ &BXt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt¬8TpÌè4PlĈĤ0LhńŠż,HdƀƜƸ (D`|ǘǴ$@\xȔȰ <Xtɐɬ8Tpʌʨ4Pf˂˞˺*Fb~̶̚ &B^z͖Ͳ">ZvΒή:VrώϪ6RnЊЦ2NjцѢѾ.Jf҂ҞҺ*Fb~ӚӶ &B^zԖԲ">ZvՒծ:Vr֎֪6Rhׄנ׼,Hd؀؜ظ (D`|٘ٴ$@\xڔڰ <Xtې۬8Tp܌ܨ4Pl݈ݤ0Lhބޠ޼,Hd߀ߜ߸ (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp4Pl0Lh,Hd (D`|$@\x <Xt8Tp  . J f  * F b ~      & B ^ z      " > Z v       : V r       6 R n       2 N j       . J f       * F b ~     & B ^ z  " > Z v   : V r  0 L h  , H d  ( D ` |      $ @ \ x       < X t       8 T p       4 P l       0 L h       , H d       ( D ` |      $ @ \ x       < X t       8 T p       4 P l       0 L h       , H d       ( D ` |      $ @ \ x       < X t       8 T p       4 P l ! !0 !L !h ! ! ! ! ! " ", "H "d " " " " " # #( #D #` #| # # # # $ $$ $@ $\ $x $ $ $ $ % % %< %X %t % % % % & & &8 &T &p & & & & & ' '4 'P 'l ' ' ' ' ' ( (0 (L (h ( ( ( ( ( ) ), )H )d ) ) ) ) ) * *( *D *` *| * * * * + +$ +@ +\ +x + + + + , , ,< ,X ,t , , , , - - -8 -T -p - - - - - . .4 .P .l . . . . . / /0 /L /h / / / / / 0 0, 0H 0d 0 0 0 0 0 1 1( 1D 1` 1| 1 1 1 1 2 2$ 2@ 2\ 2x 2 2 2 2 3 3 3< 3X 3t 3 3 3 3 4 4 48 4T 4p 4 4 4 4 4 5 54 5P 5l 5 5 5 5 5 6 60 6L 6h 6 6 6 6 6 7 7, 7H 7d 7 7 7 7 7 8 8( 8D 8` 8| 8 8 8 8 9 9$ 9@ 9\ 9x 9 9 9 9 : : :< :X :t : : : : ; ; ;8 ;T ;p ; ; ; ; ; < <4  >, >H >d > > > > > ? ?( ?D ?` ?| ? ? ? ? @ @$ @@ @V @r @ @ @ @ @ A A6 AR An A A A A A B B2 BN Bj B B B B B C C. CJ Cf C C C C C D D* DF Db D~ D D D D E E& EB E^ Ez E E E E F F" F> FZ Fv F F F F G G G: GV Gr G G G G G H H6 HR Hn H H H H H I I2 IN Ij I I I I I J J. JJ Jf J J J J J K K* KF Kb K~ K K K K L L& LB L^ Lz L L L L M M" M> MZ Mv M M M M N N N: NV Nr N N N N N O O6 OR On O O O O O P P2 PN Pj P P P P P Q Q. QJ Qf Q Q Q Q Q R R$ R@ R\ Rx R R R R S S S< SX St S S S S T T T8 TT Tp T T T T T U U4 UP Ul U U U U U V V0 VL Vh V V V V V W W, WH Wd W W W W W X X( XD X` X| X X X X Y Y$ Y@ Y\ Yx Y Y Y Y Z Z Z< ZX Zt Z Z Z Z [ [ [8 [T [p [ [ [ [ [ \ \4 \P \l \ \ \ \ \ ] ]0 ]L ]h ] ] ] ] ] ^ ^, ^H ^d ^ ^ ^ ^ ^ _ _" _> _Z _v _ _ _ _ ` ` `: `V `r ` ` ` ` ` a a6 aR an a a a a a b b2 bN bj b b b b b c c. cJ cf c c c c c d d* dF db d~ d d d d e e& eB e^ ez e e e e f f" f> fZ fv f f f f g g g: gV gr g g g g g h h6 hR hn h h h h h i i, iH id i i i i i j j( jD j` j| j j j j k k$ k@ k\ kx k k k k l l l< lX lt l l l l m m m8 mT mp m m m m m n n4 nP nl n n n n n o o0 oL oh o o o o o p p, pH pd p p p p p q q( qD q` q| q q q q r r$ r@ r\ rx r r r r s s s< sX st s s s s t t t8 tT tp t t t t t u u4 uP uf u u u u u v v* vF vb v~ v v v v w w& wB w^ wz w w w w x x" x> xZ xv x x x x y y y: yV yr y y y y y z z6 zR zn z z z z z { {2 {N {j { { { { { | |. |J |f | | | | | } }* }F }b }~ } } } } ~ ~& ~B ~^ ~z ~ ~ ~ ~  " > Z v       : V r  6 R n  2 N j  . J f  * F b ~ & B ^ z  " > Z v   : V r  6 R n  2 N j  . J f  * F b ~ & B ^ z  " > Z v   : V r  6 R n  2 N j  . J f  * F b ~ & B ^ z  " > Z v   : V r  6 R n  2 N j  . J f  * F b ~ & B ^ z  " > Z v   : V r  6 R n  2 N j   h 6 v C Y p V  S + a s q  I 3 ƃ s Ș L ? ̂ y ϐ  Ҋ 1  p T ޺ ߊ  r u E d &  W X   d " G N T H 0 s P!!!!!!! A! ! ! !!r!!!I!3!!p! !!(!!+!!m!*!!!!!!\!!!! !!!!p!"!#!#!&"!&!'!(!*!,b!. !/!1|!4!4!5!7Q!7!9!:q!; !;!""t" " "!"!""]"#"#"$"%V"' "'"(")o"*"+1","- "."/"1"1"3"3"5"6 "6"8"9";u"<">j"@"AW"Bb"C;"D"D"E"Fp"G "G"H"J"K"M^"N"O"P"Q"R"S"T"U"W"X"Y"Y"Z>"Z"Z"["\O"\"]"^e"^"_K"_"`D"`"a"a"a"b"b"cr"c"dZ"d"e0"e"f;"f"gq"h "h"i"i"j"j"k"lR"m"mo"m"n<"n"o "oz"o"pH"r^"sN"t"v{"xG"y"{"}x"""""Y"T""y""""["v"""I")"""v""""""6"""%""Y".""""6""y""K"""}">""|"""b"""f"V""m"(""U"."""h""""_""{""s"""0"U"""^""f"" "&"„"þ""E"9"Ƚ"z"d"B"""Ԍ"l":"E" "h""w" ""#"L"ݨ"";"d"""#"I"""q"""x"'""""$"g""""-""n""":""p""z"""""""""""(""^""b""e"%"""\"#A##%###`##U##9#q##U### e# # I# # # R# # 6# ####&#######$#>#<#T#u##6#Y###-## =# #!X#"a#"###$A#%B#%#&#'#(3#)#*#+#,#-#.#/#0#1#2#3#4#5#7#8A#9#9#:#;n#<8#<#=#>#?#@#A#B#C#E#F#GY#Hb#Id#Je#Kg#Li#M#N#O #ON#O#O#P#Q#RD#R#S#T#U2#V#W#Wq#W#Y#Z#[#\#]e#^#^#__#`H#`#a#b#c#d#d#e9#e#fo#g #g#h#h#i#j\#k#k#l#m#n"#n#oG#p#q|#q#rC#s]#tN#t#v#w4#x$#y#{K#}#~#|#_#####L#####p#####p#x#a#Y######T#####x##X#P###Q#I##n#U##y##e#>###>#C###y# ##ñ#f#q#H###\# #̞#͏##p##ϕ#Q#B#Ѳ##:#+#ԛ# ###N##q##\#1#۹#r#c#ޙ#߄##7#######d####H##+##l#W##z######.######\#a#$$[$A$$$ $ h$ 6$A$$$$~$$$U$A$W$$$$t$"$$$.$q$$$$f$ $ $!$!B$!$"[$#5$$$$$%$&$'m$(C$)$)~$*X$*$+$+$,$-*$-$.g$/$/$0>$0$1B$1$2E$3$3$4$5G$6$6$7$8$9$9$:$;$&$>$?$@$AT$B5$C$C$D$E$F$G^$H5$I $I$J$K$Lg$L$Mv$NM$O$$O$P$Q$Rh$S1$S$T$U$V^$V$W$X$Yo$Y$Z$[$\K$]"$]$^$_$`$aC$a$b$c$dT$e%$e$f$g$hq$i:$j}$kR$l $l$m$n$p$p$q$ry$s$u9$v$w$y\$z$|$}$~$?$$1$$$]$$$w$$3$$$S$$#$$$C$$$\$$:$$$$ $$$$$$$$$t$h$b$$$Y$$$G$$n$$$q$$$f$$$v$$*$$)$$$F$${$$‘$ $$p$$v$$d$ǁ$$[$$$̴$Ώ$$$Ӵ$e$#$$ى$$$%$$X$$ߏ$*$$R$$$D$$$$5$H$U$_$m$$$$$$$$$$$$}$$$$$$$ $'%%%%_%%x%%Z%% p% % ^% %R%%%%%%%o%%%%%d%%$%%K%%!6%"%$4%%%&%( %)%+%,|%-R%.%/b%0%2"%3%5 %6%8%9%:%<2%=%>%@%A@%B%C%E%F%F%G%HN%I%I%Jf%K %K%LN%L%M%NP%N%O%PQ%P%Q%RG%S%S%Tr%U$%U%VL%V%Wq%X%X%Yt%Z%Z%[%\+%\%]r%^"%^%_q%`%`%a%b)%b%co%d%d%e5%e%fz%f%g%hU%i}%j%k%m*%nF%ou%p%q%s%tI%t%u%v>%v%x9%yz%z%{%} %~+%S%s%%%%%%L%%%%%%%J%`%%%%%%a%%%%t%7%%%9%%U%%z%%%%B%]%!%%% %%%E%$% %%%E%%%%%%l%%Q%&%%C%%%%+%%[%%u%%%À%>% %%V%ƺ%{%7% %{%% %%b%w%%%I%%%%o%R% %%%%b%*%% %%V%%%%N%%%s%4%a%<%%%%%%%%%7%%]%%j%#%%%j%%K%&&&&a&;&!& & &|&&@&,&&p&S&6&&t&#&&&&&&&<& &&$& n&!&!&#&#g&$0&$&%&&&'&(&*&+N&,J&-&/k&1:&3J&4&6&9&:c&;B&<&=&>&?&@ &@&A[&CG&DH&E}&F+&Fn&F&F&G&Hb&I&J,&KA&LA&M6&N&N&O&P&Q&R&S&T&V&W6&X&YP&Z&\m&]&^&`+&a&b&d&e@&f&g&h&j"&k&l&m=&m&or&o&q$&q&r&sF&t&uV&u&w(&xf&y&z&{&}&~\&&&)&y&&y&&Q&&&X&&I&&&&&&A&r&[&G&3&&9&D&&&&X&&&?&&&)&&U&&&&&E&&i&&&(&&T&&&&&N&&p&&&&&&&9&j&w&z&&&A&&&{&]&&&&J&&&y&4&&®&h&J&&ſ&y&3&&Ȩ&b&C&&˷&&&E&Щ&j&җ&ԙ&ճ&׵&ٻ&,&%&ܰ&m&L&2&&&&e&3&& &i&&x& &&&:&&0&&&&&&&(&&&'l''|'5''''''^' T' ' ' ' 'F'''''2'''B'''''''9''d'H'!''"a'$2'%9'&''')'*','-'/U'1 '2'4U'6 '7':'<)'<'>j'?'AX'B'D'E'F'G'H'IE'J'J'K'L'M'N#'O'O'P'Q'Ry'S'S'Ti'U'U'W'X'X'Z'Z'[O']']'^'^~'_,'a 'b)'d 'eL'e'fq'g-'h'j'k<'m'nm'n'o'p'q'r's't~'u'u'wW'x{'z'{'|'~'''/'s''''E'''Q''''''''''Y''' '3'' 'J' ''b''f''''''''8'''B''''y''' / /и/ ܺ999ܺ 9 9 9+ +01!!!%!3oseJIJ0a+A&6FVfv ]A]/+012##"&'546632#"&546     +  q? // и/  +и 01#"&55463323#"&55463323  3 2  2     O_/ / +B+ иии%и -и/иB7иBK01#37##32####"&77###"&77#"&5546337#"&5546337663323766332325RQ XOW Q[  b^  f $ P % R               7}EKPN&+ +F+&и/  и +и3и@AFF]AF)F9FIFYFiFyFFFFFFF ]Hи LAN&N6NFNVNfNvNNNNNNN ]ANN]P&9///H/9P/901%##"&55##"'&'&&7766327&&'&5476675463322'&&4'6650@  $$" 6C "493:%  ?6  ;"27Da)8ggN5-G  A & "5<@19  5   ":AC8 >T77/;Kk4(+ +0++A]A)9IYiy ]A&6FVfv ]A]A00]A0)090I0Y0i0y0000000 ]A4&464F4V4f4v4444444 ]A44]+,2++8#+01%#"&'&5476324#"3276#"&'&5476324#"3276''&676 7##6 &FG& 7VU,-g 7##6 &FG& 7VU,- f    KK,-JL,00+Mpp/##K,-JL,00+Mpp/##.  P7-+=+MF+A]A)9IYiy ]A&6FVfv ]A]4-94/ '-M9C-M9AFF]AF)F9FIFYFiFyFFFFFFF ]MR)+9+014'&#"66763>7##"&''"'&5&7&&'&567663266'463320" $ !x$"'$/ 5-AeN3:  468')+, &( %@  ):B-' o=S29dvQ -,@+!)>F)!3^. 3># + +01#"&55463327  7   qlk+A&6FVfv ]A] / /////01#"'&&54676332b"[XX[#PNjtskkrqkk+A]A)9IYiy ]/ /////01"76654&'&4332# NONN"[WW[rkjrkstj.2'/01"''''&&77'&&7766'&63326676 6 % a1  h   '&Q)  /"@!  "J#  /% ?    49#? + и// + и 01####"&55#"&55463354633232 ' #%  $y9`/EX/>Y01%2##"77663Q 1o    )[ +01#!"&55463!2 z   M,  , 6 [+A]A)9IYiy ] +014632#"&$##$($$$$F //012##"&7663  *      .  !/и/A]Ap]  A ]Ap ] "+++01%#"5543327#"32#"47632. . eB'9mj;h]b{{1~Y<#+///901##"5''&4776332<)6 !N.  *,o +A ]A ) 9 I Y i y ].&++01".554776'6'&'"''&76632!#E "+*++2901&77632765&##"5543327654&'&&#"''&76632#"&HF$G-= A;Y5!> !D!XBF,+>?$%%U2!K$  "+D % $L,  $*2O?+(9,I !?+и// +и  01%##"55#"&554676633232##*( 2mmx  [!M.Ѹ// //и/A ]A ) 9 I Y i y ]и/"и"/ &и&/ ( *и*/(0,++$+"$901&554327674#"&5463!2##632#"&Z #8B574$ " ,3Q>?GK`=  *+/E  &!?]<6:VhD> *#4ӻ+$+A&6FVfv ]A]A$$]A$)$9$I$Y$i$y$$$$$$$ ]-61++ (+01&&#"67632#"'&'476326'&#&3276-c=4!+;B4J;54'&#"X-c>3!+;B4J;76 ~~  'TVU'    &   'UWV' '+ +01#!"&55463!2#!"&55463!2 v    v   ,  , ,  , 'f&54667>764'.5546&''UVU' | 9'VWV'  N 'z  z E,8"+06+A0&060F0V0f0v0000000 ]A00]609/ A]A)9IYiy ]EX)/) >Y-3+)A]A(8HXhx ]01##"&5&7667667654&'"5'4766322#"&546  )  L<C* (G Y6> ,/"#-2  =2)(1?  ,  *.  Zy>Q+  ++A]A)9IYiy ]A & 6 F V f v ]A ] -ܺ -9'93A>&>6>F>V>f>v>>>>>>> ]A>>]H9\DK+U9+/+$+/ и // 9кH/9014'&#"326767#"'#"&'&547632673327654&'&&#"532673#"&'&&5&76321  $ .2.*! *,A+2?&@&N51?*9M8#oE6X"32'&&#"3275#"5543 H;[G'$Eb94!  5*A55H(. G 4]QJ~b7 %  -Ph=BhM+#9Q// и /ии/// /+01##"5###"5433234332----4c1++ +и015433#"554332##32#!"ccYYc".""">q!C"//"и/++ 01723276765#"554332#"5743y  3. 5"4W9+ //// 9 901##"5433276332##"&'?,,' 6 4 _?  e +/+01%2#!".54332.#v3,k-//-и/и/$9.///(/99$901##"5##"&'&'&'##"54633276332& M 7M&KD D L[L $LL >W / / и/и/ !//// 9901#"5463324332##"&''t(+ (5+ XGR%˸/ /A ]A ) 9 I Y i y ]и/A&6FVfv ]A]+ +01#"322#"֏)ddw%%<  //A]A)9IYiy ] и/ и! /++014&##32>##"546332##KGx3:C7 , n>@%CZ2a8M .Z~57X4D5%K//A]A)9IYiy ]и/9 и /A&6FVfv ]A]++9 01##"''#"322#"q9p֏)F oddw%%8 34//A]A)9IYiy ]4"и"/и.5//)++и/014.##32>##"'&&'&'####"54>332o+<<8:)?2w41-.- 9S7 %/ ',""6H YQ0!~ 5B-32! A:׸;/ /;и/и/A ]A ) 9 I Y i y ])A)&)6)F)V)f)v))))))) ]A))] 3< 8+$+01&776632654&'.54>32'&&#"#"&P :*@Q+%!HB1!9P*3" 26!/+ 9/%7S,)=! .G7 6*1F.-E/  ( **&&-9$.G/ #+/+01##"5#"5543!2#.A^""2Y / / и/  и/и/! //+0173276654332#"'&54332{"(55(- %#9GG9H0 J(++7'2;Z,,8z  /// 90166332##"&'&63327667 .7. q q   *1/ //&/99!901##"''##"'&6332766332766332 F /4 F N( 2 0 7+ 7'! OO  ZZ 7ZZ !'/ ///9901##"'##"&7&6332763325  53~~ 3  Z4  #-+9/ // 901##"5&63326332.0  0  6 ~<+ +01".5547!"5543!2!2#W ":$+ &" #PQ -+ܸ ++01"&546332##"332#H  I   F //012##"&'&63|  *      PQ 1 + ++012##"&5546332654&##"&55463>H  I h  8  H// /901##"&'##"&73D\ ]P +01#!"&55463!2 0      $o //01##".''&6332c   /  ;   0<=/7/и=0и0/A&6FVfv ]A]7"и"/(79>EX/>Y ++6+:A::]A:(:8:H:X:h:x::::::: ]01%#&32>5%&55476636##"55#"'&&546763354&#"r8L!$$#., "G)W\$V:V/9;9 96+C ;) 9H"' hM, G/5824W3C>-.//.и/A]A)9IYiy ] и$и$/ //EX / >Y)+ A]A(8HXhx ]0143326632#"'##"5%6.#"32>>'G2^h6K.c)'N "2%%2! !2%%2! '2^5`S,`I K@&&@KIA&&AI6)g+A&6FVfv ]A]"+ +01#".54>32'&&#"32676,":[D"$B[:.  +*I.-I*( -)Ha89`F)" !8K+'K;$  $#-.//. и /и A&6FVfv ]A]$и$///EX/>Y+)A))]A)()8)H)X)h)x))))))) ]01##"55#".54632433232>54.#"')c.K6h^2G'!2%%2! !2%%2"  I`,S`5^2'* )IA&&AIK@&&@K) , +  и /EX"/">Y+  +"A]A(8HXhx ]01%4&'&#"32676#"&547632##AA%,8(#<  ;*k~>AW6G2@:;>9 K/ . ynRM&'Cs 7)E+ и#/( ++и"01'&&#"32####"5#"55433547632 %;'yy]/ #;Q3j,m0H*:)=>/4/ и />и/4и/4"*A*&*6*F*V*f*v******* ]A**]"?EX'/'>Y9+/+'A'7GWgw ]A]01&7743267#".54>32'4332#"&32>54.#"V  %@DRH-+G33G+-O%=L5B !2##1! !1##1"  - gc++!>\57^F$1%= :nG$ C@#;CE:##:E<!Y"//"и/к9#/// +901##"54.#"##"543326632(!*7F((A'Y^0C)NE%rtR#+ EX/ >Y+ + и!A!!]A!(!8!H!X!h!x!!!!!!! ]01%2#!"55433#"5543324632#"&ug!!!!""y"WY!!!!):S -*+%+%EX/ >YEX(/(>Y#+ A ]A ( 8 H X h x ]( ܸ(A'7GWgw ]A]014632#"&23276765#"554332#"5743!!!!  l!!!!4. 5"4X+ + / //901%##"&''##"54332776332 , D(( 3 Q! E) ++ + 01%2#!"55433#"554332j""c"m&78/и/и/1и/+Ap+]A+] +9A]Ap]+9+&9/(/4/ + .й 014&'433266326672##"54#"##"5&#"##"5*%*%-&+&, &5 $5&K*1+ %<)!@2B% 2 0H(1PK>#_$//$и/ и/ и/ 9%//  +01##"54&#"##"54&'&63326632(2<5H( $E-XT\MUVh+08+)nd*(")//A]A)9IYiy ]) и / A & 6 F V f v ]A ]*EX$/$>Y+$A]A(8HXhx ]01%4&'&#"327667#"&'&&547632&AD#,;E"EN22N19e3M3'L<<L'+L==L+7a!'++'$^7qHQ'*I><-.//.и/A]A)9IYiy ] и$и$/ //+)+014332632#"&'##"56.#"32>>')c.K6h^2G'N!2%%2! !2%%2"  I`,S`5^2$ IA&&AIK@&&@K$<-./$/и/$$9. и /$и/ A&6FVfv ]A]//)+ +01##"55#"&54>325433232>54.#"'G2^h6K.c)' "2%%2! !2%%2! $2^5`S,`I K@&&@KIA&&AI/)=) + )9+$+ + 01%2##"55433#"554332632&'&#0;My=s;/   ,2R0""y"b~ $U4TD9R://A]A)9IYiy ]:и/"A"&"6"F"V"f"v""""""" ]A""] и /(и(/+5и5/8и8/+;EX/>Y0+ A ]A ( 8 H X h x ]01732654&'&&'&&54>32'&&#"#".'&776[@D9O, "A!&#0E'$<   6!y '&=%,!"9J&%!!9(!7 2&   R!8$<$ '3O&E+и/++и01#"'&5#"554335433232##3276*8[ * R)% *5$[K <"e#// и/и/ 9# и / $// +01##"55#"5433232>'54332$L4(i2*(S<5&!9L*!4* ///  901##"'&633266332"1,  ,  ^ )1/%// /99 901##"''##"&'&63326332766332 ; ,+7 L( . //' - ( H;;  ExHEV $'/// /9901##"''##"&77'&6332763322  22 qq 2    -;&//EX/>YEX$/$>YA'7GWgw ]A]$901&7743267667&&63326332#"&9  #  -w+~+ , %  @)z@ ` .:++01"&5547!"5543!2!2#V-I'+ s! hSx 61,+и,&.+#+01"'&554'&&'&&554676765547#6332##"332#=/  '!2'!1]]1# "%:;&  &=;$# ct"%uc  i % +и //01#"54332#"54332****~~|S 61&+и&+$+.+0123##"&55463325547&554##"&55463/  '!2'!1]]1#  "%:;&  &=;$# ct"%uc   ++016766'&#"''&&7632&;(  7Q/:2'<(  7Q9[(E   L*(E   MGQ \;/G/S//'/M+Mк;9;9 к ;9;9и и,иM4к5;9M@иMX01&&'######"&'###"&'#"&554633&&'&633236676633236676633232  [ q( Uh(Vr  h8#9=i `+:00:+*IkRIjR ^^X^^W  <_=>`??a? 1 P +01!5!z 0h';O2F+ +<'+'и/A&6FVfv ]A]<(A2&262F2V2f2v2222222 ]A22]<Q7A+K-+'++ܸ"01%#".54>32#&&#"32>774.#"32>7#".54>32#5D'7R77R7&D4"E A3'8$$8'.$0VuEFvU00UvFEuV048cPPc88cPPc8.I3)F]45^F),B+4FvV00VvFEuV00VuEQa77aQQb77b[e++ +9// //+ии01###5!#5#'#330\0+/55 *4V6 -+ иии01%2#"&46!2#"&46!2#"&46 6!(!!(!!(!!(!!(!!(![X //015S`V_,ø // и/A]A)9IYiy ]ܸA&6FVfv ]A]+ +01".54>3274&#"326'''';.##..##.''''k#//##//- ]+A]A)9IYiy ] +01#"&54632-"!!"L "" Ѹ//и/A&6FVfv ]A]A]A)9IYiy ]  +и 01#"&54632#"&54632M"!!""!!"L "" ""S~ #q$/и/ A@ ] A@]% +и ии !014632#"&%4632#"&%4632#"&--L5Z ͸//и/A&6FVfv ]A]A]A)9IYiy ]  +и 01#"&54632#"&54632"!!" "!!" "" ""-////01#!#}}55bU +01!5!$:FR +01!5!\4Lg 7//и/////01#3#38888@Wf //017{o!{+ +01#"&'&&#"'>323267 "(,+= F(%6) "(,+= @(%6S"#"#./016#"&'&676676)" %  Y "#+;  ,./01&54632&5546766)" %  s "#+;  ,us1 //016#"&'&6766766#"&'&6766761)" %  )" %  Y "#+;  ,# "#+;  ,vt1 //01&54632&55467667&54632&5546766)" %  )" %  s "#+;  ,# "#+;  ,uns+//01'7sn$x$uuns+//01'7'7s$uJu$u| //01Roe<| //01'7D,P!!>V5`#]oCD~o\"R| +/ /01!>54.'!5V>!!>V5D"\o~DCo]#R 93E1+и1&"/ ++'и/01%#!"&55463!2'#"&55#"&55463354633232##9 5          3   J        >.(62"''"''&477.'&&77627ƾ111(Ǿ02127>'+A]A)9IYiy ]и"%++ +01#!"&55463!2'#"&54632#"&546327      D    J !|? //+ +ии и01%!'7#537!5!73#!!401#(300#! 44 42 /+01!5!'52d9%%L#2!!&  /+01!5!'7&d 9%%[#!#KL//L8и8/A]A)9IYiy ]$8$98A&6FVfv ]A].8$9B8$9$M)/3/G+и/3 и /G=01.#"32>7%4.#"32>53#".'#".54>32>32) ,7#?LHB#7, `$2=Q$$*22#*/A'$;0& #2@)!>21?!)@1$ &0;$'A/92*N?@M)14(JF#5$'4$B2(43*-D-,C.*35(2B|RM #$/и/A/]A]A]Ap]A/]A]A]Ap]  % +!+и 017#"&54632#"&54632#"&54632_  //A]A)9IYiy ] и/A&6FVfv ]A] ++014&#"3267#".54>32A- -- -''''f .. .. '''': //01#7:[////01#73#7J[[: 4@ݺ;+5+A5&565F5V5f5v5555555 ]A55]5A&6FVfv ]A]*4+>+%+ 8+/014&#"3265#".54>32#&&#"32>7#"&54632A!$#!'?V4CgE$$EfC+Q@+;aA:R33Q;)A/c4&%54%&5[%%%%e7Z?"4ZwDCwZ42J0GK1Nc22cN13E)%44%%44"#//#и/A]A)9IYiy ]9 A & 6 F V f v ]A ]// + ++01%'4&#"326#'##37#"&54632Xee!##!EKKEK44&%44%&4%%%%^$%44%%44 3ػ+A&6FVfv ]A]/#/EX/>Y +ܺ+#9.A.'.7.G.W.g.w....... ]A..]013&#"%#"''7&&54>327#&'32>7k-<;T4 &p(@X5:01$2EF$GhDF;*#.!+< #$/*B06.Ma3#F?58[@#ki*aEvW2 [bK1:#)3F* M-+:++34+A&6FVfv ]A]39A:&:6:F:V:f:v::::::: ]A::]: и /(+:9H+07++(%+ и/ 904ܸ(=и%?01%&&#"326%#"&'#"&546324.'#53&&54>32#4&#"!!32>7&")l-!!X4 =33'#".54>7'{%22%%22%h 8)/>##?.)8 42&&22&&2V.;!#?..?#!;./2۸3/!и!/A]A]A]A]A0]A0]A]A]A]A]! и+0и+4/&++ии2014.#"32>!#!5!5.54>32!|%22%%22%$ 8).?##>/)8 *2&&22&&2L&-;!#?//?#!;-"H/EX/>Y01!!5!"9(/?80+/EX/>Yи01!!5!3!?*&*# + и01.#"#6632 %2=%%>1& 9%{_az%**EUUELd6y+A&6FVfv ]A] +2)+!+$!901&&#"3267>77#"&54>326.#"'>32W  :--  F'BV/(:'++#++9A>&>6>F>V>f>v>>>>>>> ]A>>]>A##]A#)#9#I#Y#i#y####### ].+9>17и#8и8/EX/>Y +4;+ +01&&#"3264&'#"&5332654&'.546&&54632#4&#"4#6 !8?(&C74E8&('/$/. '?66<7"'3&,"<-'%**'%(0*, C'?I90%$(+#K.131.Q+B=;) *(&*I$025*Z #/;A +$*+06+A$&$6$F$V$f$v$$$$$$$ ]A$$]$и*и/A & 6 F V f v ]A ]A66]A6)696I6Y6i6y6666666 ]0= +-'+93+3и/901%#"&54632'#"&54632'77#"&54632#"&54632%"!!"#""#  """"""""+!!!!""""   """"!!!![ Q/ // + 9к 9 9 9 01#'#7'%%'!0GGPPgg7bb|ْߋָ߱*[ #///9901%'!gg7bbָ*~i'"(//A]A)9IYiy ]( и /A&6FVfv ]A])EX/ >Y#+A]A(8HXhx ]01#".54>324.#"32>i;dLMd<Y01#".54>32i;dLMd<7#".54>324.#"32>7#".54>32"kQQk>>kQQk>,.P<"">kQQk>>k:q //01 tFF+F:q //01 FF+FH?//и/ ++01!!!"X\&H //01!!HX&oyj/EX/>Y01%!< joyj/EX/>Y01!!yjoyT/+01! Y999901'#7jI{IQ9 H+/EX/>Y999901'3I{2 / ///+017!'7!'ITIII,8++01%!5!5!5!8xx&&[ '/// /9 9015576? I; E[ '/// / 9  901'7'767,9 0<rvd / +01!''7!v{n0MV= D?|kBC//A]A)9IYiy ]C"и"/1A1&161F1V1f1v1111111 ]A11]D//'.+4 и /.и/'>01#"&'732>54.#"#".54>32&&#"32>7>32k/A'5%42$$2=P% #5I1!>21?!!6)?LHB)>. '1<%'A0:$B2'44(KG=8&-D-,C. N??N$4;7*2AEC1+A&6FVfv ]A]"/,/@+и/, и /@601.#"32>%&&#"3267#".'#".54>32>32 !+8#?JGB#8+!4)=P$%*1#-$;1& $0@)!>20?!*@0$ &1;$292*N?@M)1 JF#5$  '43*-D-,C.*35(E@ #$/и/A/]A]A] A/]A]A]% ++и!01%#"&54632#"&54632#"&54632'x'!g+A&6FVfv ]A]++01&&#"#"'732>766766320=, 0> t2_Vj9&0;2_Wj9&0?;!CD/;/Dи/A&6FVfv ]A]A;;]A;);9;I;Y;i;y;;;;;;; ];!и!/;*++%и0и6иA01&&#"#"'732>76676632&&#"#"'732>766766320=, 0> 0=, 0> t2_Vj9&0;2_Wj9&0?2_Vj9&0;2_Wj9&0?&QH3++  ++01%!".54>3!!"!!3!Q75X@$$D_<N5Q7s7Q5&%Ge@@eF&& :O/&/O: &QH3 + ++ +01#!5!2>7!5!.#!5!2Q$@X575Q7s7Q5N<_D$7@eG%& :O/&/O: &&FeTg} + и/A&6FVfv ]A]+++01!5!'!".54>3!!"3!TC75Y@$%C`<N$k:Z>>Z:$Tg+и/A]A)9IYiy ]++  +01!5!#!5!254#!5!2QC$@Y57N<`C%$Y>Z:$$:Z&QHg+A&6FVfv ]A]+  +01%!".54>3!!"3!Q75X@$$D_<N8S88S8&%Ge@@eF&&#?V33V?#&QHg +A ]A ) 9 I Y i y ]++01#!5!2>54.#!5!2Q$@X578S88S8N<_D$7@eG%&#?V33V?#&&Fe=// и /  //+01%#".5332>53&Fe@@eG%&#?V33V?#&5X@$$D_<N8S88S8=//и/ / /+01#4.#"#4>32&#?V33V?#&%Ge@@eF&8S88S8N<_D$$@Y4/K3/EX/>YEX/>Y901%'3/""'(:/N///901#7/("=:E`+/+01#!5!%^#2/&  //+ +01'7!5!7'!5!'7}OO|+|NN{2/&#/// / ++01'!!7'7!'7!'7OiOOv}}|]|+NNNN||{{' 3/EX/ >YEX/ >Y +01!#3!7z3JGQ6Q6 D+EX / >Y++ 01!5!!5!!5!'xp(:::H //01#7HQc+ +01#"&'&&#"'66323267#I-1&*> !O%1.&C! $(N///01'77gg\\$N / / +01#"&'73267'eDDe'"T<32@, ,, ,''''j -- -- ''''%9 [+A&6FVfv ]A] +01#"&546329((((i''((:U ̻ +A ]A ) 9 I Y i y ]/EX/>Y+ A ' 7 G W g w ]A ]901#"&'732654&#"'736632U=4. ""#(  %y'&  gM)BHa +A & 6 F V f v ]A ] /+01#"&54673267H/#"$##z'*4( $y+A]A)9IYiy ]и/и//+01"&&5546332##"&54632    C ?*6++1+A11]A1)191I1Y1i1y1111111 ]1+9/A]A)9IYiy ]A&6FVfv ]A]'+4.+01%476767654633232676#"'&#"&54632?0- )   L<C* (G Y6>t7676632 *$")'%07( ."&1/=, ) +#-/>  9*,; 4>9:*A5=9 <vuk$3! 2$nG&0;tl$20$pI&0? (EX/ >Y +01!55!!!j (.A#886 R // и///EX / >Yиии 01#####5!6BBhpp:usx7۸8//A]A)9IYiy ]8*и*/ A & 6 F V f v ]A ]9//7//%/!+3+014.#"32>'#"&''7&&5467'766327+Kd99dK++Kd99dK+s$ +r@>m+#'!v"t,tBEw-|,9dK++Kd99dK++Kddr'^49c){#|*0,&t#r*h<6a)k!h,16/p< !ۺ+ ++A&6FVfv ]A]A & 6 F V f v ]A ]/+!+ ++014&#"3267#"&54632!!!#!>!##! 4&%44%&4*A[%%%%%44%%44?9T';GKWcaR+L+ B+<+2^+X(+A]A)9IYiy ]A & 6 F V f v ]A ]A&6FVfv ]A]A((]A()(9(I(Y(i(y((((((( ]H<9JR9AL&L6LFLVLfLvLLLLLLL ]ALL]A^^]A^)^9^I^Y^i^y^^^^^^^ ]XeH/I/?+U+EO+Oи/E#и#/O-и-/7и?[иEa01%4.#"32>4.#"32>4.#"32>'#"&54632##"&54632#"&54632KL?;;>>;<>"R.?:<>><;>L?:<>><;>4''44''4o4''44''44''44''4[bb[[bb[bb[[bbT[bb[[bb)E+//999901  +j|)//901)jX)=+//999901J,IpG| )//901),| ;7eEf//A]A)9IYiy ]f\и\/A&6FVfv ]A]8@\89T\89ga/#J+.=+=3и.и# и /#&и&/@=.9JGиG/JMиM/T=.9=W01%4.'32>3232676'&&'&63232>7#"&'#"&'&7>7#".54>7+9?AA?9+ $("       "($ ,$3 8&  95  &8 3$;^u99u^;CMTTP##PTTMC(#!K--K!#(!7(031120(7!+yFFy;-')////)9)901%#"&'#"&'&7>7#".54>7;$3 8&  95  &8 3$;^u99u^;!7(031120(7!+yFFyR#AB//A]A)9IYiy ]B2и2/A&6FVfv ]A]$:2$9C+/EX7/7 >YEX=/= >YA]A(8HXhx ]7 A ]A ( 8 H X h x ]к:7 9014.#"#"&'.#">7.54>326632%%:'%&':%;Zl00lZ;- 5GNP$$PNG5 0@%@WW@%@0";,%,!!,%,;";11B0fhf_U##U_fhf0*H4T``T4HR3/EX/ >YEX/ >Y901.54>326632R 5GNP$$PNG5 0@%@WW@%@00fhf_U##U_fhf0*H4T``T4He\-+y@+++Jo+]+A]A)9IYiy ] 9A&6FVfv ]A]#9A]]]A])]9]I]Y]i]y]]]]]]] ]jAoo]Ao)o9oIoYoioyooooooo ]Ay&y6yFyVyfyvyyyyyyy ]Ayy]~A&6FVfv ]A]+Et++Xb+ 9и/и/и/#9(иX2иXgиbии01%#".'###"&'&&54>7#".54>32676&'.54>32766324.#"#"&54>54.#"#".#"32>32327654.5463232>->'0*!     !*   !*0'>-+=$!<" 2A$$A2 "7#".54>32676&'.54>3276632->'0*!     !*   !*0'>-+=$!<" 2A$$A2 "324.#"32>7#".54>32*9 8**8 9*8_HH`77`HH_8'=jQPj>>jPQj=+ 9**9 9**9 H_88_HH`77`HQj==jQQj==jBx / /01  MO)  Nh ? //  и / + +01%!!!!!4hdCe&nz o +A]A)9IYiy ]  ++014.#2>7#".54>32S8_HH_8'=jQPj>>jPQj=+H_8C7`HQj==jQQj==jnz g+A&6FVfv ]A] ++01"3#".54>32H_88_H>jPQj==jQPj>8_HH`7^Qj==jQQj==j3h #/;GS_kw+7CO[gs'3?KOSOQ++ +06+`f++++PL+A&6FVfv ]A]A & 6 F V f v ]A ]ии $и*A0&060F0V0f0v0000000 ]A00]<иBи0Hи6Nи TиZA`&`6`F`V`f`v``````` ]A``]lиrи`xиf~и0и6и иA]A)9IYiy ]ииии`иfи0и6и иA]A)9IYiy ]иA]A)9IYiy ]ииии` иf&и0,и62и 8и>иDиJиPиVи \иbиhиnиtиzи`иfи0и6иии0и6иииии`иfиии`иfииииии ииии"и(и.и4и:и@иFиPULP+SM+ +MG+!++E?++uo+и и'и!-и3и 9иKи!Qи?WиE]иcи iи{и!и?иEиoиuии ии!и?иEиoиuииии ии и и!и?иEиo#иu)и/и5и;иAиSи!YиG_иMeи?kиEqиowиu}иииии?иEиGиMиoиuиииииoиuиGиMииииии иGиMии%и+и1иG7иM=иGCиMI017#"&54632#"&54632'#"&54632#"&54632#"&54632'#"&54632#"&54632'#"&54632#"&54632#"&54632#"&54632'#"&54632'#"&54632#"&54632#"&54632#"&54632'#"&54632'#"&54632'#"&54632#"&54632#"&54632#"&54632'#"&54632'#"&54632'#"&54632'#"&54632'#"&54632'#"&54632#"&54632#"&54632#"&54632'#"&54632'#"&54632'#"&54632#"&54632#"&54632#"&54632'#"&54632'#"&54632#"&54632#"&54632#"&54632'#"&54632#"&54632'#"&54632#"&54632#"&54632'#"&54632#"&54632!!!  ^  ^  ^  ^      ^        ]  ^      x  _  ]  ^  w  +  3  ^  ]  _  ]  ^  ^  3  +  w  ]  _  ]  w      ]  _        ]      ]  ^  ^  ^   Z    S    h    h  S        S  S    l    S  S  S  ~    !  S  S  S  S  R  S  !    ~  S  S  R    l    S  R        R  h    i    S    [!h ۸ // и/иии и ииииииии!++++++  + +015!%5!%5!%5!%5!%5!%5!!!GZZZZZZZ)CCcDDdHHhGGgBBbIIiDD~h ϻ++ ++ ++++!++ииии и и иииии01#3#3#3#3#3#3#!!DIBGHDDe)[[[[[[[! h !"//"и/9к99 к 9 9 к9к99к99к99#+!+ии иии01####57535353!!.uvBw˛vvrrv9|vvwAvrvvr! h !߸"//"и/9к9к 9 ик9к9к9# + + и и иии01'!!!75#5#5'#5#!!=BrruvwB)Bvvvvvqwʚ:2h #'+/37;?CGKOSW[_cgkosw{߻+ ++'+;$+S8+oP+l+ии и ииииии и и"и$(и'*и,и.и0и 2и4и6и8<и;>и$@и'BиDиFиHи JиLиNиPTиSVи8Xи;Zи$\и'^и`иbиdи fиhиjиPpиSrи8tи;vи$xи'zи|и~ии иlиoиPиSи8и;и$и'иииlиoиPиSи8и;и$и'иlиoиPиSи8и;иlиoиPиSиlиoиlиoиl+j++hM++L5+ +4!+lиlи и и иlииииииl$и%и(и)и,и-и 0и!1иl8и9и<и=и@иAи Dи!Eи4Hи5IиlPиQиTиUиXиYи \и!]и4`и5aиLdиMeиmиpиqи tи!uи4xи5yиL|иM}иhиjиии и!и4и5иLиMиhиjиии4и5иLиMиhиjи и!иLиMиhиjи4и5иhиjиLиMиhиj015#75#5#75#'5#5#75#'5#'5#5#75#'5#'5#'5#5#75#'5#'5#'5#'5#5#75#'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#75#!!GGGGGGGGGGGGvGGGGGGGGGGGGG@GGGGGGGGGGGGGGAGGGGGGGGGwGGGGGGGGh)GGeGGeGGeGGeGGGGeGGeGGeGGGGeGGeGGeGGeGGlGGeGGeGGeGGeGGeGGGGeGGeGGeGGeGGeGGeGGGGGGeGGeGGeGGeGGGGGGeGGeGGeGGlGGGGeGGeGGGGGGeGGGGGGeGGeGG)h #&)-159=ADGJMQUY]aehkosw{~/H/и/H9999к9%к&9'9)9CкD9E9G9K9L9Hfкg9i9j9H|к}99999Hк9E+L+EиEиE'иLiиLиL01'7'7'''''''''''''''7''''''''''''75#'''''''''5#'''''''5#''7#5!!=DC+@C;D!@;@;<;;;;C7632>54.'&6%'&&7>&'&&6676676&'&&766&&'&&6676654'&&667>&'&&66766546@lOOpC2CF  ,1(;^w<F 65/ # B)#F;, .H:/0  #+2%;8=%;0%'   8;:'>73'  F9#.+2D]'QS +=+<++A]A)9IYiy ]A & 6 F V f v ]A ]:и:/ ?и?/и/и/(++u+z<++#ܸuкu9u9и/и/01%4.#"32>7#".54>3226&&'4.'.'&&7##5#366&&'.#">76.7>323267632%5#"&'&&'&&'&>'&&'#!"&'&>74>7>766'&'&&667>32(66((66(.>##>..>##>.  " // "  Tcf'4h]K--( /!!/ (--**%*2  p   2*%Ujy;;yjU6((66''6#>..>##>..> %'&  3X TT X3  &'% R#29 92#        gAAo   9/% /.(  (./ %/9   &,....,&D]{U+B+r++r9r9-и-/XиX/01%4.#"32>5#"&'&&'&&'&>'&&'#!"&'&>74>7>766'&'&&667>32(66((66(E**%*2  p   2*%Ujy;;yjU6((66''6AAo   9/% /.(  (./ %/9   &,....,&X::9+9и/9;и:+1B++и/и/и/~иBи/B"и1,и,/8и8/;и;/^B19~zиz/99B19Bи/и/и/1и/01%#5#"&'&&'&67"&'&7&&5463'".'.54>7>327535&''&&'."7>766&&'&&'#&&'2766762%"36765#%6&'&##"#3326&&#&"76'4&'&&33326766XEF;/2.  #(##(( ' 8IQ&;96DD/2- !     %  &-    GS-v>,1*3.)$(   !0: 9' ";  !                   &     й+  &      X: T'+T%и%/T)+ +r+ и/r ии/и/$и$/*и*/ 16и6/P 9TиT/g9l9 vиv/ 9 19и/и/1и/01#2#"&'&&'##537>32&'&'&&'&"&7647'3&4'&27&''&767&&767&&7663'"&'&54>7667&&7667&766%4&'&&#667665#%4&'"&##"3326766&'&""67676&'&332726X ('##(#  .20;FED69;&QI8 (  ! -2/SG    -&  %   1,>v-%M83*|(   0! :90 ;"   "        7        &      0 .   +  ~    A//и/и///+01###".54>336|65O55N4n3F))F3 ?+и // +и 01###53533$$~v"i+и и и // ++ии и01###535#53533#3$$;H""~~"Q8 p +/EX/>Y9999999 901'7'IIIAD 3//999901'''7>o-AJ3//9999015>-nAD 3//9999015>n-AJ3//999901'777>-oT$%//%и/ A]A)9IYiy ] к9/ + +01%4.#">573>32H ) @5"L0@GJ!?!0"* ."#)0L7%8W?+ !%4R+/01%&'&&667673R $'% %0&s+8&&! !"12//A]A)9IYiy ]2#и#/#и/*и*/'/01&7>54.'&'&&667674632<  "&6< $'% %0 +=&S? 9  &1;"!.!+8&&!   !%>6/);*//*'и'/ии)+)/015&'&>76675&'&>7667%& !!  1& !!  1:J:#<     K#<     VK!7-.+++и/.9к.9 .979 +3'+++и-01#"'&'732767#5!#'#373%&'&&#&3#547672}~vs!N\\zdg oTThE)T,`7fb||{nj/-}|_]sHJ`bHūw_9 XVH|{OM<[:NE++;F+ ;9A&6FVfv ]A];9$9*;90и0/A;9AFF]AF)F9FIFYFiFyFFFFFFF ]I;9;P /I//@/21++к$ 9* 91401.54>73!#!5!'&&'&&''667667#5!##>54&'3-  -+) )c=/1^1#0 L0]!+W3*+U$^8 -+) -=+- *ixDCxi*889>% .Dxi*YEX/ >Y#+43+-+3и3к34901%4.#"32>##33#".54>32#53 ..  .. 5/5 .q+='&>++>&'=+PKK1Q: :Q11Q: :Q]]8+A]A)9IYiy ](и(/=)и)/A3&363F3V3f3v3333333 ]A33]AHH]AH)H9HIHYHiHyHHHHHHH ]>R>SиS/>W8+UR+#.+M+8и/C01%4.#"32>%#".54>32#.#"32>7#".54>32#53 --  -- 1B)4P66O4!?2!+%.-?)(@- 3&*=&&=++=&&=*PKK1Q: :Q11Q: :Q:@hJ(9dOOd9;V8)A,6Yq:;pY6"+fg+&A&6FVfv ]A]&и/-ܸUXкYf9H_и_/fkEXW/W>Y ++0)+и=и>и)BиHиIи)OиUиVи0\и0bиfиgиh01%3265#53#"&5#"&54>76654&#"#66323267#4&#"#4.#"#366326632#53"3"$,8==J 7,)9*:!*#-#B3' %% $  $$ 0"!. .#/3O==  ) --XL=90*.;BI2=& :19?YT&@28[=F2D'Y0%2E(W,:6/-8W^=O9<PTX  +=+,-+ ++UV+A&6FVfv ]A]  к U9,/к0 U96и6/ GиQRиR/UZEX / >YEX./.>YEX / >YL+WV+ 9 9.A]A(8HXhx ]9иVиLRиVиR и&иV,иR-к0 993к6 9&BиVQиWS01%#"&&'&'#36632#4&#"#4.#"#3663266324.#"32>#53#53YF;  $$ 5';F&& $! %% 1#!/ /#04 #& &# Z??>>ڀb0A~[=F2D'Y0%2E(W,:6/-8W^\-P<#"YEX3/3 >Y!+++ +3  ии01%.#"###5!32>73#".54>32#3"/.!-5-!0* 1YH(B/-B-0D+11"C3 3B$jp:'+J7$0Wb%Ge@7dJ,.Oj;$y+и/A]A)9IYiy ] и //+012##"&'546632#"&546   s\? // и/ +и 01##"&5546332##"&5546332 &  &  %  %      UgB/L//$/+H+и ии)и1и5иH=иHQ01#37##32####"&77###"&77#"&554633667#"&554633766332376633232J  ] U] _  f c  k $  %  W *O) *O)       )O*      %}EJP'H&+ +K+&и/  и +и3и@и FAH&H6HFHVHfHvHHHHHHH ]AHH]J&9AKK]AK)K9KIKYKiKyKKKKKKK ]MкN&9/0@+@4+и+/@FиF/01%##"&55##"'&'&&77663327&&'&5476675463322'&&'4'6670J  '/ 7L +<;5>)  I6  8#)=9nnh35S70E@ $ 0*7BL28  4  )8fA;Z3882[Z4:32S9-+=+PI+A]A)9IYiy ]A&6FVfv ]A] /-P94и4/C-P9AII]AI)I9IIIYIiIyIIIIIII ]PU)+9+014'&#"667632>7##"&''#"'&547&&'&547663256674633212+&0$$3 /=$,53E40+ > FYeCJ  @F&H169<.3_2  (*)3 !0Q* ))/G:;zb{{1~e#+///901##"5''&4776332e)6 !.  ,g +A ]A ) 9 I Y i y ]&++01".57477654'&#"''&76632!2#/ (':QX 3R+`9@$ I/#E#^/  g<('- !04`%X0Z3&N&"@8+A]A)9IYiy ] и /8.и./>+*++2901&7767654##"5543327654&'&&#"''&76632'&&*K2O6?4 k<[OABpJW9 Q/0,>%L4=52 bR(%0=}jFGbhv%/+01##"&7!"5543!2 2#!!'K9 4+E+A]A)9IYiy ]A & 6 F V f v ]A ]:4 9:/E9/A]A)9IYiy ](84(9G4(9$.+@+014'&#"67664'&&'&32767#"&'&&54767&546766322,;@,02/  @J2:* '<94FH19F)&!V21U#&*4(S*& I)0LE)A6 9! 94* #M7) !5=''"'B1NM.N1&Ku*A0ThX$0-.//A]A)9IYiy ]и/.$и$/A&6FVfv ]A]&и&/*,+(+  +014'&#"#3276&776326767#"'&547632!"*.LQ.-)+OR..3 Fh!2=x\>OABr.N5930PU/..0Z#  73Tb/=~oBEi+-` w+A&6FVfv ]A] и+ +014632#"&4632#"&""""""""'""""T""""<` e+A]A)9IYiy ]+ +014632#"&2##"77663""""a B* / '""""z  "h%'.554>76 ~~  'TVU'    &   'UWV' + +01#!"&55463!2#!"&55463!2      -  - +  + !f&54667>764'.5546&!'UVU' | 9'VWV'  N 'z  z ?,8˻+06+A0&060F0V0f0v0000000 ]A00]609/ A]A)9IYiy ]-3+)+01##"&547667667654&#"5'4766322#"&546!  )   L<C* (G Y6>;0()/6  A2')*5014'&#"326766%#"'#"'&5476326673327654'&#"3273#"&'&&547632c"94:)##BXJOK D'>))EGkP& D] >-1]Z}\YY\bA4rX602bkK59?/32'&&#"327#"5543  0i9Sf95cY8S/-R3GsQ+.SqDo/e 3a\Tf9 %  /TvGMxQ*#Q// и /ии/// /+01##"5!##"54332!4332---{-Q:  +//01#"54332 00+8ur +/ +01&&5543254332#"& o-(/Y 2 $ Z6;9+ //// 9 901##"543326332##"&'b,,58 6 Es} g8 +/+01%2#!".54332.#iur*g+//+и/#9,////&/99#901##"5##"&'##"5463326332r-J;0& --K  L>w]: iy W/ /и/и/ //// 9901#"5463324332##"&'-. y,=+ yD|<+,//A]A)9IYiy ], и / A & 6 F V f v ]A ] #и#/)и)/-+&+014&'&&#"3267667#"'&54676632 #a=>a# $b<=a# IPWXR(&-NO}-'(G?p-1551-p??q-0651-q?dllgS47=<96  //A]A)9IYiy ] и/ и /++014&##32>##"546332'#JHQ0M7 - n>@%BZ3GM ;-57g4Q7K<.///A]A)9IYiy ]/и/A&6FVfv ]A] и /&и&/(.(90/#+ +. 9014'&#"3276##"''#"'&'&76632=E|}E==E}|E=9v VQO-NN~-OI/GYeeYYeeY tkdk7==7kBr-.E 34//A]A)9IYiy ]4"и"/и.//)++014.##32>##"'&&'&'###"54>332n-H6gi)E2w4-< 'T- 9S7$/*;%&5# ^w#) 8P16.% #:Ӹ;/ /;и/и/A ]A ) 9 I Y i y ])A)&)6)F)V)f)v))))))) ]A))] 3 8+$+01&776632654&'.54>32'&&#"#"&3 #G.I]-(#XP6(CV-'<#5)!;+#3;!>0)DY0,E! .PI:09G/0I1  '  0!.(!(2<'2N6 #+/+01##"5#"5543!2#.{h""= / / и/ ! //+01%3276654332#"'&54332/17\^7.0-IihI]._7==K0*Io#77E1 /// 90166332##"&'&63326 -72 ~??? PK 1 + ++012##"&5546332654&##"&554638H  I h  8  ///901##"&'##"&73".\-!Dp\[ P +01#!"&55463!2       4M //01##".''&6332D   f  Gr!<=/7/и=0и0/A&6FVfv ]A]7"и"/(79 ++:+6+ %01%#&32>5%&55476632##"55#"'&&546763354&#"8W**2#;, #O/d\$c:e/9;H 9C-P ;)+ 4M2&_`/M38;5=X?B ,-//A]A)9IYiy ]-и/ и&/$++01%6.#"32>43326632#"'##"5&?.)=(+;#.?&w'\98U:x6'*P@'#>R.-S>%'@R))18gN/fN+)g+A&6FVfv ]A]"+ +01#".54>32'&&#"326768'>aC$&Gc>!6 03M43I/#./,Lh<=gK+ !54.#"##"55#"'&5324332V?,(=))<(U,'6xk<7<]'/M$&*AQ'0R<"IMKfRL~0&; ,I-//-и/  и'+"+  +014&'&#"32676#"&547632#%FH-0C)(F !H*{~?Be8R3 )@;<F!92M4 -  NO((Gw T)E+ и#/( ++и"01'&&#"32####"5#"55433547632   ;nn'aa];R0+)m0H :9://и/:#и#/A&6FVfv ]A]+и+/2EX7/7>Y.+ +7A'7GWgw ]A](01%4&#"32>&7743267#".54>32'4332#"&XKQR*:"#>-  &F)[[Z71T>$6V=5Q*&!A`>-Dktlu+H35H , o`-.$B^9CiH&#-6pAiI' !Q"//"и/к9/// +901##"54.#"##"543326632( .RW((S>aZ40C)NE rt$ u +A ]A ) 9 I Y i y ] 9/ /+01#"543324632#"& ,,U+:V$+A]A)9IYiy ] 9 /EX/>YEX/>Y"+A'7GWgw ]A]01&5543267654332#"&4632#"& (}K 2 t%++ /// 901%##"543326332##"&'N((/, X     +//01#"54332 ,,+`71+&+++и/1и/ 19+&99/(/4/ + .й 014&'433266326632##"54#"##"54#"##"5%N9>b#!=F}?`#>/R @! R/0RA"R0;e&*..*&e;yMW+,O</0//A]A)9IYiy ]0и/ и/ 9  ( +++ %01%4.#"32>43326632#"&'##"&5&<-)<'WI&;)~&_?;S57R6=X& )M<%";N,tw!>V/K,6*Id9>jN-/*</0//0!и!/A&6FVfv ]A]и/!/9)и)/+& +01%32654.#"##"55#".54>32'4332W(;&JW(<)-<% &X>5R84S;?`&6V>!wt-N:"$:cI*6,Km{ = + и/и/ 9 /+01&#"##"54&'4332632lJ$'%1_ I9Y$-b~ *G9://A]A)9IYiy ]:и/"A"&"6"F"V"f"v""""""" ]A""] и /(и(/+5и5/7и7/0+ +01%32654&'&&'&&54>32'&&#"#".'&776\=A6F'A$"0@$&8  2#t% >$) 6F%# 5-1; %8& \$F)(:& %O&E+и/++и01#"'&5#"554335433232##3276*)Q^^ * ooH# ,5$[?Y "]#// и/и/ 9# и /// +01##"55#"5433232>'4332$WB(&=*(S<5NĪ!9L*n!4 ///  901##"'&6332663321-  ,  6 ]*5//&// /99 901##"'##"&'&6332667633266766332 ; %J%7 . %G%/&G& ( ts  8ss=vv '/// /9901##"''##"&7'&6332763328  27 y{ 1    ;)//EX/>YEX'/'>YA'7GWgw ]A]'901&7743267667&&'&63326676332#"&5  #  -X-/%C%+ , "  @)zz |fe .(++01"&5547!"5543!2!2#DBa<+  ^vSr 61,+и,&.+#+01"'&554'&&'&&554676765547#6332##"332#7/   !2'!1]]1# "%:;&  &=;$# ct"%uc  i  +//01#"54332 ,,jvSr 61&+и&+$+.+0123##"&55463325547&554##"&55463/   !2'!1]]1#  "%:;&  &=;$# ct"%uc  2 +01!5!|V  +/+01!2##"54&#!"&5546|/)   #)X$V" +e,/ /и/ , и /и/ -//+и$0132##"54&##"&5546!32##"54&##"&5546/)  /)   #)X$#)X$NN'=>//A]A)9IYiy ]9&и&/>5и5/,и,/50,9+01''&7>'462'&'&%46332##"54&##"&5G0  )=(. *4K+ /)  0  /R&#)X] +/+01%!"&543323!2/) p ]#)M$XZ1V9E>+H;++$+;7и7/HMиM/01'.''''&67>76&##"554332#"##&&54332322676676 ..  13"O**J;,  <  #C/)  ! '   . .'  -6)N"   GHH! " +4')")9"K7+ 1=KX!+8+2+A]A)9IYiy ]A22]A2)292I2Y2i2y2222222 ];+$+TL+G>+ 5+и/Lи/$)и)/01%#"&54632#"##&&54332322676676%4&#"3267!"&55463!2'#"&554332WHNNMO$:* #C/)  ! '  0--11--0Sl V ?NK@BL%4")9" f)10*(11& ' k% $ e!++ +01%#!"&5463!2#!"3!2 n, ,   s** !  e%@A//и/ и /A&и&/5*и*//#/ +#и/# *и0и8и8/01%##"&546332##"332'&&546332##">76% , ,    /.2-! ,  +'& s** !  '** $   U1a2//2и/*и/и/".и./.++ %+01%#!"&55463!26554&#!"&55463!2#!"3!2 h/#-1 #- a#*i*W$i*[ VBX4+,+HM+!и!/HZ%// /J/DQ+/+Qи/ :7и7/:=и=/ KиK/01%###.55463326554&##"&5546332##"33267667632##"54&##"&5546  A!,l !,m  2/$  uk*Y#g*[ t#)W WRbD!+<)+V+ ^+!1и1/ d/5/[ +6-+&?+6и/ и/ и[Gи[KиK/-SиS/0146332##".5##".55463326554&##"&5546332##"332677>76"332654&#,r,r6">!,h !,i7 w [ **) k*Y#g*[  D   O]gO,+G4+_++_ и,<и'432'&&'&  9!,q !,r/ E.(=* -  *8 (Au k*Y#g*[  9X KdzD  52(),L9  F,!V >b0 +(+@M+ и/@[//I/RW+\?+Iи/\и/Wи/R!и?+и+/ID3и3/D7и7/01%##".55463326554&##"&5546332##"332677>767332##"&546332##"32#6">!,h !,i7 _  , ,   u k*Y#g*[  p *,*  p  U8`dR/+J7+b+a+/?и?/f/C//'/)/4M+;и/;и/и*и*/DиD/UиU/;aиa/c01#"&55463!2##32#!"&554633##".55463326554&##"&5546332##"33267667672#3(/ ))0 3L #@!,v !,w6  SS ! m*[#i*]  AKB+ O[iv A+9&+V+P+A]A)9IYiy ].и./APP]AP)P9PIPYPiPyPPPPPPP ]E+rj+3*+e\+#<+ S+Y01%#"&54632##".55463326554&##"&5546332##"33267667672%4&#"3267!"&55463!2'#"&5543320WHNNMO$:*  !D!,s !,t:   0--11--0IX L ?NK@BL%4 m*[#i*]  D)10*(11& ' k% $ b#g$//$и/и/и/  и /и/ ++01463!2#!".5"3!26554&# ,D ,] " **   Z%g&//&и/и/ ии/$ // ++01%".546332!546332#%3!2655Q,R, , " ZP&؄ X%AK++C++=6+ и$иC3и6Jи=M ////9/ ++&и4иBи F01%".5463323546332#'332655".5463323546332#'332655%% ,} [ %%,~ [ Xf&݌ e&݌ NB %Ng+"+ ܸиии и"6//8/ ++01332655".5463323546332#%''&67>'432'&&'& [ %% ,E.(=* -  *8 (A5 f&9X KdzD  52(),L9  F,!A  *a+A]A)9IYiy ]9/01''&7>'4632'.'& #b>  6Y?!. ET, &OE49f( "]hk1<<((K? "9D$;NU\RL+ALL]AL)L9LILYLiLyLLLLLLL ]LR901&'.'&'''&76767&'&&'&'''&67>74626766746328= 44  S7  4% & 2h1F. +  +  + 7,  # $.324.#"32>&D]65[B&&B[52\F)J1A#$@0/@&#A142W@$$@V39Y> !>Y7)>**?(&?,->7:1 +01%''&7>76&#!"5543!2'&'&7Q  JyfY+  '+ 52%LN& Q[A+S.  *LOV2 # &9/*% # 4$@Yf+2и801''&67>76&##"554332676676&##"554332'.'&'''&6767&'&&'&k 44 "KE7 8  /$; 4  43 55*\* %(2# 6- HPQ (1 # 0`# ''&5 %   $)7c * )>#N.:/4+&+01%'&'&'&''&76676&#!"5543!22##"5543G =>==81;U J  '-1  $  %!&%C# 7D # !-(L  L-+/ ++0154&#!"&55463!2##"55!"5543  /) E$#) ! [ #3+ +++013!2#!"&5463!2#!"!2#@  f, ,   f *$*  k ! Y#[$//$и/+ +иии"01#3#"&55463!2##32#!"&554633GZ Vt xB  " "    C /?@/0/@&и&/8A8&868F8V8f8v8888888 ]A88]и/A00]A0)090I0Y0i0y0000000 ]0=!+ +++3+01!"&55463!2!"&55463!2#".54>324&#"326p  ~ @!:P./O:!!:O/.P:!DTA 7((7 AT#"r ! %=++=%%=++<'39)(:tc'+ // +01##"5433232#)) 0R~l"TtA// ии/ // +014332##"5###"54332R&&&&mwG~tcE+иܸи // ++0132##32####"54332ʊ  ))  ~Tt[ //и и/ии  //++015#74332##"5###"54332R&&&& MGptx'+ // +014332##"5#"55433)) { $ &t = //  и / // +01##"543324332##"5#"5543&&&& ~~a $ lt{9 +и //++01#"554334332##"5#"554336 ))  #  : # 't 'O"++"'ии'// ++01##"54332#"554335#"554334332##"5&& &&~~ # # F?# +/ + 01%46332!2#!"&55463+~  {3 ""Kt'=o>/7/>и/ 72(и2?4/:/$+)0+$и$ и /и/01%46332636676"'"&554632732####"54332-+G>!>@E(NQM J) ))v   #  ""E~at&B +6;+-(+(3и6Aи-D*/0/%+'4+%и/% и /и/%#и#/01%4633267676"'"&55463324332##"5###"54332I+(&8-9==@B@ :C1&&&&&  # "|`Gte%1a2/+/2и/ +&3./(/!+!и/! и /и/01%4633267676"'"&55463267##"54332+46I>CQNQM JQ'))w   #  "~F?#I$/ /$и/  //+и 01%4633234633232#!"&55463:++  {2 2 ""F#+/+ 01##"&5!"&55463!2#+ > xr ""wtq)=g>/6/>и/6*и61?-/3/*7++ и / и /01##"&5'"&554632>7>764332##"55#"5543+*VN QUO#RRJ  DOB))  `" "  c " \t 1Ei +@E++E9//92+#0+0 и /0и/# и /01##"54332##"&5'"&55463676676#"554334332##"5&&+01A1 :~9=m;  68 &&~~ b" "  " =wtq 5a6//6и/ 7//' + и/ и/'"и"/01##"54332##"&5#'"&554632>7>76q))`+*VN QUOMSQ!  GPB~~ ^" " F#E$//$и/ //#+# и#01##"&5###"&5#"&55463!2#++ > xk k ""F5 +01#!"&55463!2  > (""wtq *? + ,//&+и/&!и!/01#"543326'"&5546332667>c))  DQSVN E)TO%SQH~d#  " t  +//01#"54332))~b!$:u;/,/;и/ и /,3)и)///// /(/6)+) и6и/6и/01%#"##&&54332322676676##"&54332332  #C/)  ! '  6 /)  z")I.#)Ib@wA//A+и+/2(и(//$/'/ +'и/' .и5и5/:и:/01%##"&546332##"332#"##&&54332322676676 , ,     #C/)  ! '  s** !  ")INF&HI//A]A)9IYiy ]и/9I5и5/<2и2/?2+2/и//01%&'&'''&7>'462%##&&543323676676K+G0  )=(. *4 4/) ('  [/Q'-;i% ]lq3  14'**N> )")IV 2-&++)+и/01%'#!"&&6762##&&543323676676TT  4/) ('   ")IWNAj=4++sx+]W+A&6FVfv ]A]и!и!/+AWW]AW)W9WIWYWiWyWWWWWWW ]GW]9]8 +o|+/+ и/|и/o%и%/G/9 uиu/01%##".55463326554&##"&5546332##"3327667672&&'&'''&7>546246332##"54&##"&5]# *!,5v !,6  - 9  /%,  $,  /$  w k*Y#g*[  9>% &;^ Qk}?  76,(*H; #)[W+B^4+,+XI+!и!/%/: +\E+"+NS+/+ и/:7и7/:=и=/"M01%###.55463326554&##"&5546332##"332676676##"&546332##"332  A!,l !,m  2E , ,   uk*Y#g*[ !** !  YN]aP-+!$+++xr+ A!&!6!F!V!f!v!!!!!!! ]A!!]$5и-=и=/!GArr]Ar)r9rIrYriryrrrrrrr ]crx9иx'+B9+2K++B и /Bи/'$и$/SиS/cK2901%".5463323546332#'##".55463326554&##"&5546332##"3327667672&&'&'''&7>5462%332655'U' ,# *!,5v !,6  + 8  -#,  "+  P % Os&( k*Y#g*[  9>%#;^ Qk}?  87*(*H;  V2 PST/'/Tи/Bи//и//':NиN////4++$=+и/QEиE/QGиQKиK/01%#!"&&6762###.55463326554&##"&5546332##"332676676'*   A!,l !,m  2TTs xk*Y#g*[ E9R^n=D!+<)+\+ V+A]A)9IYiy ]!1и1/AVV]AV)V9VIVYViVyVVVVVVV ] p5/_/S+J+Y+&?+и/_f-и-/_6и6/JGиG/JMиM/01%".54632'###.55463326554&##"&5546332##"3326766762654&#"2#!"&554634&L?@N%3  A!,l !,m  2)'&*(''  E*:#KQOM#:*0k*Y#g*[ 5--66--5""O#?I+ +A)+;4+A1и4Hи;K/-/7/D$+2@+-и/Dи/-#-80146332##".5"332654&#".5463323546332#'332655,r,rV [ '' , ` **:   wm& ID:J>+ F+-'+A'']A')'9'I'Y'i'y''''''' ]'-9C +;+*и*/0146332##".5&'&'''&7>'462"332654&#,r,rsK+G0  )=(. *4 [ **H/Q'-;i% ]lq3  14'**N>    V-#&4[5//5и/  /*/+*и/*$и/0146332##".5"332654&#'#!"&&6762,r,rV [ pTT **:    m#3>߻+4$+,9+ +A4&464F4V4f4v4444444 ]A44]A99]A9)999I9Y9i9y9999999 ]6/++)<+ +01463!2#!".57"3!26554&#4>32#".732654&#" ,V ,] 4 %3BFEC3%;P&**&')&**   1"G:;G"/I%$#''O1;w3+-&+ +3#и&:и=6+*+$2+*и/ и /*0146332##"54&##"&5".5463323546332#'332655 /$  '' , ` #)Wsm& O&%A++;,+ и$ //0/>)+16++)и1 и /1и/> и /01%".5463323546332#'332655##"&546332##"332 '' , `  , ,   Om& *2* !  NcDZdջ\++71+MR+\ A11]A1)191I1Y1i1y1111111 ]!179cиMf_+IV+[+I и /Iи/OиO/017".5463323546332#%&&'&'''&7>546246332##"54&##"&5332655's' ,p- 9  /%,  $,  $ /$  H C Os&>% &;^ Qk}?  76,(*H; #)[ fN7^hջ`++SM+1"+` AMM]AM)M9MIMYMiMyMMMMMMM ]=MS9gc+',+_+' и /'и/иc4и4/017".5463323546332#%##"&546332##"332&&'&'''&7>5462%332655's' ,u , ,y n  * 6  ,!,  "/  8 C Os&*2* !  !>% ;^ Qk}?  98WGJ*  OWLVYW//Wи/N иU //Q+JC+M+017".5463323546332#'.''''&67>76&##"554332332655'' ,  ..  13"O**J;,  < ` Om&P'* .'  -6)N"   GHH! " + M#?IA++ -+A и ;иH$)+27+=+@+)и/2 и /2и/$DиD/01%".5463323546332#%##332##"&546332##"32332655'' ,  , ,   ` Om& s *8*  r  s6@8 +++2++A&6FVfv ]A]A]A)9IYiy ]8(и+?$/./ +)7++;+01%4>32#".732654&#"'".546332!546332#%3!2655m%3BFEC3%;P&**&')W,R, , " Q1"G:;G"/I%$#''db&e ey6@\f_8 ++^F+XQ+F^9/A]A)9IYiy ]A&6FVfv ]A]8(к+9+/2+?и^NиQeиXh$/./J/T/ +)7++;+Aи)Oи7]и;a01%4>32#".732654&#"'".5463323546332#'332655".5463323546332#'332655j%3BFEC3%;P&**&')*( ,~ Y (* , Y Q1"G:;G"/I%$#''ml&f fml&f fN&<=/4/=и/A&6FVfv ]A]и/94/>+8+01%&'&'''&7>'46246332##"54&##"&50K+G0  )=(. *4) /$  [/Q'-;i% ]lq3  14'**N> #)WLJ'=>///>и/A&6FVfv ]A]и/9/6,и,/:++01%&'&'''&7>'462%##"&54332332J' G0  )=(.  &1I /)  Y.R$+ 9f% Zhn3  /2$'*N> #)ILDCD//D0и0/6A6&666F6V6f6v6666666 ]A66]и/ 069+ +01%##"&546332##"332&'&'''&7>'462D , ,   J' G0  )=(.  &1s** !  ;.R$+ 9f% Zhn3  /2$'*N> LCCM60+E++E A6&666F6V6f6v6666666 ]A66] 069LиO //3/H+D+01%".5463323546332#'&'&'''&7>'4627332655~'' ,J' G0  )=(.  &1k ` Om& .R$+ 9f% Zhn3  /2$'*N>  jL}/WgJD+AJ&J6JFJVJfJvJJJJJJJ ]AJJ]4DJ9-&+01'.'&5''&676676&##"554332&'&'''&7>'462 ..  13$%*Uj <J' G0  )=(.  &1'* .'  -6'&'"  @B " +.R$+ 9f% Zhn3  /2$'*N> (V +01%#!"&&6762o||A  T=';O#++K-+7A+A&6FVfv ]A]A#&#6#F#V#f#v####### ]A##]A--]A-)-9-I-Y-i-y------- ]AAA]AA)A9AIAYAiAyAAAAAAA ]7Q+ +(и 2и<иF01%".54>32'2>54.#"".54>32'2>54.#"<"6&$6#$6&&6#  ! "6&$6#$6&&6#  ! T%=O*-O:"!:O.*O=%=.88//88.=%=O*-O:"!:O.*O=%=.88//88.)?0ѻ+,#+ +A&6FVfv ]A]A ]A ) 9 I Y i y ]+(+,#и#/0132>54.#"#".54>35463322A2A""@2-C+,C-)EY/1YD(%"#>T24T754633232>54.#"&'&'''&7>'462.A()B-'34(() * !* aK+G0  )=(. *4+L:"";L*-J6!22!6J-6))66++6/Q'-;i% ]lq3  14'**N> =G+03Aɻ+,#+ +A&6FVfv ]A]A&6FVfv ]A]+17+(+0132>54.#"#".54>7546332'#!"&&6762() * !* .A()B-'34(TT 6))66++6+L:"";L*-J6!22!6J l/3>}0 +(1+1 09 /46++ +%<++ и и0и 201#!"&5546335#"&55463!2##324>32#".35#32654&#" xZ VtR%3BFEC3%/ P&**&')    " 1"G:;G"/#I%$#''A[+ *6CQamhZ+Rb+1#+++A##]A#)#9#I#Y#i#y####### ]A++]A+)+9+I+Y+i+y+++++++ ]AR&R6RFRVRfRvRRRRRRR ]ARR]Ah&h6hFhVhfhvhhhhhhh ]Ahh]o4 + ++&.+9и @иFиNи Wи&]и.eи4k01##"&554332#!"&55463!2#"&546324&#"326##"&554332#!"&55463!2#"&546324&#"326  @ D (6 FLJH 6(B.%%//%%.  @ D (6 FLJH 6(B.%%//%%. % & 4%K@BL%4 )10*(11X % & 4%K@BL%4 )10*(11;'5Ѹ6/ /6и/A&6FVfv ]A]A ]A ) 9 I Y i y ] +3*+#+01%32>54.#"#".54>327#!"&55463!2T,:9-(<');(&?R+-R>%%>Q-0S=#&  !5%%5!4''4/K56K.0K54L . KtSӻI@+$O+ + ܸиииU //<5+++ и /$и$/50и0/<>и>/<@и@/DиD/<Iи<Kи<MиM/01%####"5433232##32%2676"'"&5546322346332236767463 ))  f98!>@E(NQM %$'+'(&~"   #  "* ! ftKOһC:+ G+++ ииLиMиQ//EX / >Y81+M+ и /1,и,/8:и:/8CиC/8EиE/8GиG/ N01##"5433234332##"5#2676"'"&55463346332676746335#&&&&6,9==@B@ 5>+"XG"  # "  t`/;g'+++05+0=8/2/+и/и/'и)и)/012676"'"&55463233463326767463##"54332KA9CQNQM J( +',-@))   #  "- # ~tg5Qw + +6;+;Cи;Kи6SN/8/C<+KD+1!+!и/!%и%/016##"&5###"&5#'"&554632>7>##"55#"554335#"554334332  DF+'+'H KMI#LJD) )"  w  x"  " i " ft0LX + +1:+MR+16>и:Bи6FиMZU/O/>7+F?+."+"и/"и/"$и.+и+/016##"&5###"&5'"&554636766##"55#"554335#"554334332##"54332K  +$+A1 :~9=m&p pp p&&&" ~ | r"1v " l " ~tg3?w+ +49+4A7>##"54332  87+58"+'H KMIGKK))k"  g Z" ~&s ]+A&6FVfv ]A] +014632#"&ATt //и/A&6FVfv ]A] // +014632#"&##"54332T@))AV~Q+и/и//EX/>YEX/ >Y01#37#53==??(aeR //и/и/ и / и / и ///EX/>YEX/>YEX / >YEX/ >Y  и 01#3#3%#53#53====????(aeee 1/и/A/]A]и/ и / A/ ]A ] и/и/ и/ и/ // /EX/>YEX/>YEX/>YEX / >YEX/ >YEX/ >Y иии01#3#3'#53#3'#53#535====??==????(ae6aeee + и / и ///EX/>YEX/>YEX/>YEX / >Y 9 01#3!#3%#53 ==D@]??(Cae3/EX/>YEX/>Y901#3лDB> + и / и ///EX/>YEX/>YEX / >YEX / >Y 9 01#3#37#53sDA==??Cae7 / /и/  и/и/ и/ и/ // /EX/>YEX/>YEX / >YEX / >YEX/ >YEX/ >Y9и01#3#3#3'#53#53DAtt;;;;====Caeeea /и/ A ]A/ ? ]A@ ]A ]  A]A/?]A]A@]и/и/ и/ и/и/и/ // //EX/>YEX/>YEX / >YEX / >YEX/>YEX/ >YEX/ >YEX/ >Y9иии01#3#3#3#3%#53#53#53D?qq888888::::::Caeeeee+и/и////EX/>YEX / >YEX / >YEX/ >Y9 9901#3#'#'373#53==FFIxwI??(We A//EX/>YEX / >Y9901#'#'373FFII("+/EX/ >Y01#3BB(NQ//и///EX/ >YEX/ >Y01#3#3BB BB(6 /и/A/]A] A/ ]A ] ///EX/ >YEX/ >YEX / >Y01#3#3#39BBBBBB(66; R+//EX/ >YEX/ >YEX/ >Y 901#3!#3BBLRE(6|3/EX/ >YEX/ >Y901#3NG6; Z+ //EX/ >YEX/ >YEX / >Y901#3#3RDBB6|6X / /и/  // /EX/ >YEX/ >YEX / >YEX / >Y901#3#3#35XD@@@@6{66yn +  ++// //EX/ >YEX/ >YEX / >YEX / >YEX/ >Y901#3#3#3#3ҁXAkk>>>>>>6{666S`+///EX/ >YEX / >YEX / >Y9 901#3##33BB|OONN(67n\# A//EX/ >YEX / >Y9901##33OONN(7n\#/// +01%##"&''###"76332SVX7/ 03 / = X  B! ++A]A)9IYiy ] и9#++ + 9014'#3264&##327!#"&546332dA8G[la$0JZmLOf@:Eu|XJ+/ +01##"5463!2#!-4" / +01"76332#! = ֏+ p^L3++ ++01"&5463!2#!32##!2#h7,u#$$<+ +01".5547!"5543!2!2#W ":$+ &" #9Q// и /ии/// /+01##"5###"5433234332----4%ո/ /A ]A ) 9 I Y i y ]и/A&6FVfv ]A]+ ++01#"322#""554332#֐L)ddw%%""c1++ +и015433#"554332##32#!"ccYYc"."""W9+ //// 9 901##"5433276332##"&'?,,' 6 4 _?   /// 901##"76332##"&' 1 = 1 .  p 3,k-//-и/и/$9.///(/99$901##"5##"&'&'&'##"54633276332& M 7M&KD D L[L $LL >W / / и/и/ !//// 9901#"5463324332##"&''t(+ (5+ XGR= #!++ +012#!"55432#!"55432#!"5543+7##$$$$%˸/ /A ]A ) 9 I Y i y ]и/A&6FVfv ]A]+ +01#"322#"֏)ddw%%9=// и // /+01##"5###"543!2--e]<  //A]A)9IYiy ] и/ и! /++014&##32>##"546332##KGx3:C7 , n>@%CZ2a8M .Z~57X4D5(++017!2#!"&7&63!2#!   Wq$F/ ##+/+01##"5#"5543!2#.A^""#-+9/ // 901##"5&63326332.0  0  6 ~ '(/ и /A]AO]Aq ии!AO!]A!!]A!q и$и)//01##"55&&546754332'6&'66.XeeX.XffX@88@9@88@\ tkkt _^ tllt PQ T QOOQ Q!'/ ///9901##"'##"&7&6332763325  53~~ 3  Z4  /c*#++ +ии1//&//+и/0143322>554332###"55".5543323,* +3D(,(D2+ )q3%0J11J0%3%1 ++++'+A]A)9IYiy ]A & 6 F V f v ]A ]и/'.и./'3++" +и+015>54&#"#"554335&&54>3232#!& L??L &]/68L,,L7 6/]+ 0B/^ee^/B0 "qd=`B##B`=dq"  4+A&6FVfv ]A] +"+и/ 2и2/01%4&#"3266#"'#"&54>3276332326>763:763:s ! !9D*XZ0C' 4& )N  dlld`uqF  -$\0/BdD"+9w BGG<#C$)+4+A]A)9IYiy ]$ и4<7)<94E&+/++79AC&9012654.#"32>54.##"5543##"54>32#"')#)-,!!3"$B_&/C*+E2& "2H.T6Y>B+"'4}*5"5&  )H5 3A!6J,9"&K;%2:3L4//A]A)9IYiy ]4'и'/'9'A&6FVfv ]A]3'95/./EX"/">Y"9 A ' 7 G W g w ]A ]3"9014&''32>66332#".54677&332W""!!*2 -",-: :-,"- 22B+33+B# # G6M#%8%%8%#M6G +F 3+)+A]A)9IYiy ]A & 6 F V f v ]A ] 83)9 =и=/)H.+B+B01%4.#"32>##"'&&#"#".54>7.54>32"6&&4 "4!"5$& *$#)".. "4"5N22L3.Y%+ +7A]A(8HXhx ]%/ 97>01&&#"32#32766332#".54>7&&54>32##"&o?)1@'4#;*$-h) hJ'G6 "%%1@#;1% (`"'+" !  !& U JE&:')#=&$5#-<:9@://A]A)9IYiy ]:и///9и/'A'&'6'F'V'f'v''''''' ]A'']/;EX2/2>Y+2A'7GWgw ]A]0132654.'.54>7#"554332#"&'&776( 2&,E1+CP& (SE,-;"TFG9;  u   1A'7mf\&  $'Zbj7'2@7<:  # ><#[$//$и/ и/ и/ 9%/  +01##"54&#"##"54&'&63326632(2<5H( $E-XT\MUVh+08+)nd% Y/ /и/ и и/+ + +01#"322!"!&։ )ddwJ+ +/+01%#"5433232654332;9t((34:vX/ + / ///901%##"&''##"54332776332 , D((3 Q! /$ //+012##"'##"&7.'&&##"55463q%+ ,}+ ' (7 ` -)   ! <<,k-//-и/ и и /%и%/&9.//+)01##"5433232>'54332##"55#"&'((*.%($C($=r%@/ 8L,!4N<0)&/// 901"554326332##"'&&'&& n,1~ !  &  [:Mz$+B*+ +A&6FVfv ]A]A]A)9IYiy ]'$ 9/$ 9AB&B6BFBVBfBvBBBBBBB ]ABB] OEX/>Y6/+A'7GWgw ]A]01#"'&77632654&'.5467&&54>7#"554332B!;-<9XY#/1%   #$4@&A0WK$' ($ I '5#66 :6, !  (;(Ma-*!   " *(")//A]A)9IYiy ]) и / A & 6 F V f v ]A ]*EX$/$>Y+$A]A(8HXhx ]01%4&'&#"327667#"&'&&547632&AD#,;E"EN22N19e3M3'L<<L'+L==L+7a!'++'$^7qHQ'*I#(K)/ /)и/ #& +!+! и!01%6#"&5###"5#"5543!2##326 *.(&3`(EY /  4)*7gU !!><-./ /.и/'и/A ]A ) 9 I Y i y ]  /*/+$+017326766'6.#"4>32#"&'##"5 2%%2  4%%2 F3N61K145]3F'K && KH>**>H"1`L//Mc5^ON2$ *23//A]A)9IYiy ]3$и$/ A & 6 F V f v ]A ]и/$9.и./4+01%4.#"32>7&&##".54>7>!6&&5!!5&&6!G-('3O66O2"4$HOM!I<((7543324.'>%0F.(.F00F.(.F0D----0YG00GY00YG00GY0A8*m*9@@9**8A</' +$+ и$,01%332##"&''##"7'&&##"554332766332F  *,31 1 .@-,22 +x . !&$ G! ( <=>/3и3/3 A ]A ]A 0 ] A]A]A 0]"(и -и"? //:/*/'+ и'.014332665546766332##"55.554&'&6332`"&4$+*9!&!9*,.aA X&&Aa. 0_:&3!!3&;_/  F}G/;и;/; A_ ]A ] A_]A]-4 9H2+ и260132655433232>54.'&&776#"'#".54>766 ' !! 1&AA$1 !!>?@!2A'6++6'A2!@?=  $IJJ%6T;EE;U6%JJI$ ; +01!5!8  +/EX/>Y01#38;$+EX/>Y+01!#!8;$+EX/>Y+01#!5! +/+01!5!3 +/+01!3!8 0+/EX/>Y+01!#3!8;,+EX/>Y+01!#!5!8 0+/EX/>Y+01#!5!3 +/+01!5!3!8 H+и /EX/>Y +и 01!#!5!3!E +01!5!68 "+/EX/>Y01#3668E(+EX/>Y+01!#!'6) 8E(+EX/>Y+01#!5!6'6 +/+01!5!366 +/+01!3!6%8 4+/EX/>Y+01!#3!'66)%8E0+EX/>Y+01!#!5!'6')68 4+/EX/>Y+01#!5!36'66 #+/+01!5!3!66%8 L+и /EX/>Y +и 01!#!5!3!'6'6)6%8 4+/EX/>Y+01!#3!2LL8P0+EX/>Y+01!#!5!&0H8 4+/EX/>Y+01#!5!3L2L$ #+/+01!5!3!&H08 L+и /EX/>Y +и 01!#!5!3!&&0H08 4+/EX/>Y+01!#3!&&008>0+EX/>Y+01!#!5!2L2$8 4+/EX/>Y+01#!5!3&&H #+/+01!5!3!L$8 L+и /EX/>Y +и 01!#!5!3!2L2L$8>(+EX/>Y+01#!5!L2$8P(+EX/>Y+01#!5!&H +/+01!5!3L$ +/+01!5!3&H +/+01!3!L +/+01!3!&08>(+EX/>Y+01!#!2L8P(+EX/>Y+01!#!&08 @+9//EX/>Y +01!##3!&L8 @+9//EX/>Y +01!#33!2L&8 @+9//EX/>Y +01!##3!&L008 @+9//EX/>Y +01!#33!2L&008 @+9//EX/>Y+01##!5!3&L, $8 @+9//EX/>Y+01#!5!33L2&$ 8 @+9//EX/>Y+01##!5!3&L, H8 @+9//EX/>Y+01#!5!33L2&H 8P 2+EX/>Y ++01!#!5!!&H8P 2+EX/>Y+ +01!#!5!5!&0$8P 2+EX/>Y ++01!#!5!!2L2H8P 2+EX/>Y+ +01!#!5!5!2L20$ %+/+ +01!!5!3! &H %+/ ++01!5!5!3! &$0 %+/+ +01!!5!3! LH %+/ ++01!5!5!3! L$08 F+и /EX/>Y+ +01!#!5!3!&&L8 F+и /EX/>Y ++01!#!5!3!&&0$08 P + 9//EX/>Y +и 01!#!5!3!&L$8 P+9/ /EX/>Y +и 01!#!5!3!2L2&$8 J + 9//EX/>Y+ +01!#!5!3!&LH8 J + 9//EX/>Y ++01!#!5!3!&L0$08 J+9/ /EX/>Y+ +01!#!5!3!2L2&H8 J+9/ /EX/>Y ++01!#!5!3!2L2&0$08 P + 9//EX/>Y +и 01!#!5!3!&L0H08 P+9/ /EX/>Y +и 01!#!5!3!2L2&0H08 F+и /EX/>Y+ +01!#!5!3!2L2LH8 F+и /EX/>Y ++01!#!5!3!2L2L0$0ea ,T+U//A]A)9IYiy ]UHиH/ A & 6 F V f v ]A ]- 2и2/HAиA/-V"/P+5>+5и/>и/>59>к!>595)и)/014&#"66%3267#"'#"&''327%32>7#"&''667&67>32J,  ?U  &361, 624 "16J* -;-"3 %1 $/ $ 'I=.\UKPC &!&fl%MGtf+*-#A;9*  +$bdA8$(.f 6^@"#+++ R+7+A]A)9IYiy ]"%к&#79.9 <иY?H+Z+)+и/)3014&#"66#4&#"#4.#"#36632>32%32>7#"&''667&67>32I,  ?U3)))4 ).441,  %60_6J* -;-"3 %1 $/ $ 'I=.\UKP%[?EWOY1$VQM&6%$Z[Gtf+*-#A;9*  +$bdA8$(.qv!8`t ,+"#+T+9+#A & 6 F V f v ]A ]A]A)9IYiy ]#6и>и>/ATT]AT)T9TITYTiTyTTTTTTT ]TMиM/9bEX7/7 >Y'+\+1+AиA/'JиJ/01%4.#"32>4&#"66#5#".54>32332>7#"&''667&67>32'=+'8%$8'-=&_,  ?U8X:3M33M32,!8_6J* -;-"3 %1 $/ (O>''>O((O>''>Or 'I=.\UKP%b4=,Kc77cK,)({Gtf+*-#A;9*  +$bdA8$(.I 5߸6//A]A)9IYiy ]6)и)/ A & 6 F V f v ]A ] и/)"и"/+1+014&#"66732>7#"&''667&67>32g,  ?U86J* -;-"3 %1 $/ $ 'I=.\UKPtGtf+*-#A;9*  +$bdA8$(.V A3+ 5++A]A)9IYiy ]9к99 и/A55]A5)595I5Y5i5y5555555 ]5.и./CEX/ >YEX/>Y"++=++"9014&#"66##37332>7#"&''667&67>32,  ?UB[77K 6J* -;-"3 %1 $/ $ 'I=.\UKP%\B Gtf+*-#A;9*  +$bdA8$(.g!CD/*/Dи/A&6FVfv ]A]A**]A*)*9*I*Y*i*y******* ]*;!+ + ܸ!"и%и /и2и6и@01%#".54>32#&&#"3267!#".54>32#&&#"3267 ^K0J43J0Ec 8@2$6$%5#0D ^K0J43J0Ec 8@2$6$%5#0D[c(Hd=:dI*`U8H#YEX>/>>Ysl+^W+>A]A(8HXhx ]к9!9&и/и//8к@9FкK9P01#4&#"#4&#"#367>32>32#4&#"#4&#"#367>32>32#"&'332654##532654&#"#6632/"$ 1!%11 3$/"$ 1!%11 3$+(!"  "#& ([@DWOYBCURK%$Ta[@DWOYBCURK%$Ta$#&!  (^+Mu.E4++  +Uj+p+pи/иpNиN/4N9! 9AE&E6EFEVEfEvEEEEEEE ]AEE]pc[и[/sp9pwEX/>YJ/+mf+XQ++и/&и9и@01#4&#"#4&#"#367>32>32#".54>32#&&#"3267#"&'332654##532654&#"#66320#% 2"&22  #W ^K0J43J0Ec 8@2$6$%5#0D+(!"  "#& ([ACWOYBCURK%$,E0[c(Hd=:dI*`U8H#YJC+5.+'+к9'!к$901#4.#"#4.#"#366326632#"&'332654##532654&#"#6632:&, ;"#/ :: F75B C8JFB+(!"  "#& ([1"2D'Y0%2E(W*<8-)YEXM/M >YEXZ/Z >YEX/>YEX5/5>YWP+B;+A]A(8HXhx ]кZ9!Z9&к.Z94Z969]Z901#4&#"#4&#"#367>32>32##373#"&'332654##532654&#"#6632~0#% 2"&22  #W?W44Gp+(!"  "#& ([ACWOYBCURK%$,E0\B.$#&!  (f&@23++  ++к39 93'ܸ2-и37/ //2/EX=/= >YEX@/@ >YEX/>YEX./.>YEX6/6>YA]A(8HXhx ]к=9=9#и=*A**]A*(*8*H*X*h*x******* ]01и4и501#4&#"#4&#"#36632>32&&#"3###5354>32f6(*$:"'+::0,  #:-  %]]=UU + ([ACUPYCBQRK"8$$Ta10*65159"5$b&@%45+'(++  ++к59 947к859/ //'/4/EX/>YEX6/6>YA]A(8HXhx ]к99#и.к89;01#4&#"#4&#"#36632>32#4.#"#366325&*!9!&*88., #9+6#+ 66 =.(6 ([BBUPYCBQRK"8$$TaC$9(/A&Y-;4J-Oa+Iܻ+  ++к9! 9?/EX/>YEXA/A>YEXI/I>Y+E<+и/?9!?9&иE0и0/<7и7/9<E9><E901#4&#"#4&#"#367>32>32%3267#"'#"''3267/"$ 1!%11  "f %-4%2413( ([@DWOYCBURK$$,E1C & &0fl3_)S[+  ++<=+34+*++к*9! 9<?к@*9K439*U/ //*/3/YEX>/>>YA]A(8HXhx ]к9!9&и/и//8к@9FкK9P01#4&#"#4&#"#367>32>32#4&#"#4&#"#367>32>32/"$ 1!%11 3$/"$ 1!%11 3$([@DWOYBCURK%$Ta[@DWOYBCURK%$Taa+MֻE4++  ++к49! 9AE&E6EFEVEfEvEEEEEEE ]AEE]EX/>YJ/++и/&и9и@01#4&#"#4&#"#367>32>32#".54>32#&&#"32670#% 2"&22  #W ^K0J43J0Ec 8@2$6$%5#0D([ACWOYBCURK%$,E0[c(Hd=:dI*`U8H#YEX/>YEX5/5>YA]A(8HXhx ]к29!29&к.294296901#4&#"#4&#"#367>32>32##3730#% 2"&22  #W?W44G([ACWOYBCURK%$,E0\B_)Sp+  ++<=+34+ic+иi*и*/*9! 9i,<?к@*9K439iTиSиS/cUиU/oci9ir/ //*/3/YEX>/>>Yf`+pT+>A]A(8HXhx ]к9!9&и/и//8к@9FкK9P01#4&#"#4&#"#367>32>32#4&#"#4&#"#367>32>32'#4>76654&#"#&6323/"$ 1!%11 3$/"$ 1!%11 3$  !"&0Z([@DWOYBCURK%$Ta[@DWOYBCURK%$Ta 5(- , `+Mj8E4++  +c]+cи/cк4c9! 9cNи+и+/AE&E6EFEVEfEvEEEEEEE ]AEE]]OиO/cWi]c9clEX/>YJ/+`Z+jN++и/&и9и@01#4&#"#4&#"#367>32>32#".54>32#&&#"3267#4>76654&#"#&63230#% 2"&22  #W ^K0J43J0Ec 8@2$6$%5#0D  !"&0Z([ACWOYBCURK%$,E0[c(Hd=:dI*`U8H#Y=7+G++'+к9'!к$901#4.#"#4.#"#3663266327#4>76654&#"#&6323:&, ;"#/ :: F75B C8JF8  !"&0Z([1"2D'Y0%2E(W*<8-)YEXA/A >YEXM/M >YEX/>YEX5/5>YJD+T8+A]A(8HXhx ]кA9!A9&к.A94A96901#4&#"#4&#"#367>32>32##373%#4>76654&#"#&63230#% 2"&22  #W?W44Gk  !"&0Z([ACWOYBCURK%$,E0\B 5(- , m{*V ++9+PA+Aик P9A1и1/A99]A9)999I9Y9i9y9999999 ]H99H/GPXEX/ >Y 4+T.+%+.и.ииDи%K01%3265#4.#"#3>32#"&5#".54>76654&#"#66323271J24-@S4 )1#55%++<&  # PA5'$>U2 !?1-C5`J 9,   ) --XLB!9*/A&&5I-0*.;"5$2=& :19?YT&@2_90UV/@/иVKиK/ A & 6 F V f v ]A ]Kи/@1@Sи1WEX(/(>YEX0/0>YEXT/T>YEX4/4>YP+-"+ F+-и/"и/"-9%"-9"9и9/4=A='=7=G=W=g=w======= ]A==]A4T9S4T901%4.#"32>3267#"'#"&''327#".'332655#".54>3253U 1" 1!BA 1!Ž  $03.* 3/2 /U_!:-2A/F;"+*D00D)0G 3'J:# 7I*Zo5H C &!&fl%Fk|&;)9/]^)$#B^;7]D'8(Qf9$N`ʻ78+./+%&+Y++A]A)9IYiy ]"и7:к;89F/.9OEX#/#>YEX9/9>YEX/>Y\+ A ' 7 G W g w ]A ]#99*A**]A*(*8*H*X*h*x******* ]3йAк"#9;#9F#9AKи3T01%#".'332655#".54>3253#4&#"#4&#"#367>32>324.#"32>U_!:-2A/F;"+*D00D)0G 3b/"$ 1!%11 3$k 1" 1!BA 1! k|&;)9/]^)$#B^;7]D'8(Q[@DWOYBCURK%$TaN'J:# 7I*Zo5H|9kBջ+ 8+-+-к99к99A88]A8)898I8Y8i8y8888888 ]-@иDEX/ >YEX/>YEXA/A>YEX!/!>Y 3+AA]A(8HXhx ]!9!9!9==9!*A*'*7*G*W*g*w******* ]A**].!901%4.#"32>##373#".'332655#".54>32538 1" 1!BA 1!?W44G=U_!:-2A/F;"+*D00D)0G 3'J:# 7I*Zo5H\Bk|&;)9/]^)$#B^;7]D'8(Q5 $++$ܺ$9$9к$9 $9ии$EX/ >YEX / >YEX/>YEX/>Y"+"9 и /иии"$и$/01##373#".5#53533#327+@Y55H( VV;cc(\B ,!f55!ox0[_P(+>+UF+\]+FA&6FVfv ]A]A(&(6(F(V(f(v((((((( ]A((]F6и6/M>9M/L[\9\aEX^/^ >Y-+#+- и /-39и#IиPиLܸ3\01%3265#".54>32#&&#"3267#"&5#".54>76654&#"#6632327#3 )?*-&6E N?)?++>):S+8*. /);  C8,!4H*5*&8+S>0$ \,,  ) --XL[c(Hd=:dI*`U8H#YEXg/g >YEX/>Y8+'.+8 и /8к.'9>иDи.Rи'YиUܸe01%3265##373#".54>32#&&#"3267#"&5#"&54>76654&#"#6632327#3+8'0a+r<''1y[8.. .*<#%   '* /($5&4%$%=-$ I&&  ) ZXL\B[c(Hd=:dI*`U8H#L3>9IPEXYEXC/C >Y.+B+8+$+C!B)и*L$901%4.#"32>4&##32>4##32>#5#".54>323##32"52#'6!MEuu5(}uu /1O0-D--D--O1j\%A03-6>(O>''>O((O>''>OHG"6o#. b7:,Kc77cK,>3(ha)D4:U _Y(,\}+  ++65+MN+к<NM9Z'5>S.:-,5+#9$D@1&.1)*++;&*-2$1"$1t?,) ,AB* ?D./## !07-&+#!T"RVs+  ++,++lf+кl9 и/Dfl9D/eиe/#;+,9;/A;;]A;);9;I;Y;i;y;;;;;;; ]DC;JlWиfXиX/l`rfl9luT/V//(++ic+sW+>G+Gи/G и /иGи/TV9кTV901#4&#"#4&#"#366326632#"&'332654&''.54632#&&#"'#4>76654&#"#&6323,$,& ++&##"%$0%'5>S.:-,5+#9$D@1&.1)*++;&tZ  !"&0Z*-(C9)-C:t>.(!+:? * ?D./## !07-&+#!R 5(- , u9r*` ++65+OP+ A&6FVfv ]A]"к<PO9YEX/>Y&++!9#!90и9и&JиS01%4.#"32>7#".'#36632#".'332654.''.54632#&&#"$4 $7&$9( 4$43G+0'44N9+G3t->%$>.4E44@ "C*!OL :+4;114 F,!-P<#"(6;1&  /`O.  ++%$+>?+ к+?>9+/A++]A+)+9+I+Y+i+y+++++++ ] 96$%96/A66]A6)696I6Y6i6y6666666 ]EQEX / >Y(++9иB01#4&#"#3>32#".'332654.''.54632#&&#"4491#44&,+<%->%$>.4E44@ "C*!OL :+4;114 F,!(CHS/A&Y'4J-);%-E/?B11 . BM*>(6;1&  /faT/*)+CD+0DC90/A00]A0)090I0Y0i0y0000000 ];)*9;/A;;]A;);9;I;Y;i;y;;;;;;; ]JV/>G+-$+-и/$ и / $-9$и/-и/-9013267#"'#"&''327#".'332654.''.54632#&&#"+  %250+ 504 "1->%$>.4E44@ "C*!OL :+4;114 F,!C &!&fl%);%-E/?B11 . BM*>(6;1&  /[)[V+  ++32+JK+к9KJ99/A99]A9)999I9Y9i9y9999999 ]**9! 9B239B/ABB]AB)B9BIBYBiByBBBBBBB ]Q*]EX/>Y6/++и/&иEиN01#4&#"#4&#"#367>32>32#"&'332654&''.54632#&&#"4&((6 %.66" $8-y+;#E\1A21=0(@)KH6*18/.2B*([ACWOYBCURK%$Ta);%W_?B11*&  . BM*>(6;1&  /m9{*1[2//A]A)9IYiy ]2 и / и"и/и//# /9.и./EX!/!>YEX+/+ >YEX./. >YEX/>Y+.A]A(8HXhx ]+9#+9&0+901%4.#"32>7#".'#36632#3$3 $8&$9) 3$42G+0'44N:+G2B<-P<#"YEX/ >YEX/ >YA]A(8HXhx ]9901#4&#"#3>32#34491#44&,+<%B<(CHS/A&Y'4J-~6Za&}/EX/ >YEX#/# >Y+9и/и/99"9%901#3267#"'#"&''327'3B{  %250+ 504 "17<6e &!&fl% Z+2+  ++к9# 9/9/ //-/EX/>YEX,/, >YEX/// >YA]A(8HXhx ]к,9#,9(к1,901#4.#"#4&#"#367>32>32#33  $)44  "7'@;([ 2!WOYBCURK%$Tal6ur +// /EX/ >YEX / >YEX/ >YEX / >Y99 9901##373#3@Y55HHB<(\B6M //и/ 9////EX/ >YEX / >YEX / >YEX/ >Y99 9901###33!#34~9~4UyzUE<(llh6]9+34//A]A)9IYiy ]4!и!/  и #к$!919EX2/2 >YEX"/">YEX / >Y+'+.+ 29$ 291,01%4.#"32>%#"&'#36632#'##30 $4# 5) 0kffg2B%5H44D:)C06>>68-P<#"YEX/>Y++#901% #4&#"#3>32#'##3ff408/!44$*+:#6>>68nCGT/A&Y'4J-Ha)/EX(/( >Y#++#9#9#и/ #9 и /#9#9'(901%#'##7#"'#"''326732673ffP6>>6 '4%2413( 1 8n(&0fl3C H14++ +к9)9/////EX/ >YEX/>Y2+$+ и /9)9$.01#'##3#4&#"#4&#"#367>32>324==46/"$ 1!%11 3$Zcc(6[@DWOYBCURK%$TaFnW  + // /EX / >YEX/ >YEX/>Y+  99 9 901%#'###3733 ffP6>>bY55H8n\BO9$1h2//A]A)9IYiy ]2и/ ик9EX/>YEX%/% >YEX+/+ >YEX./. >YEX/>Y++A]A(8HXhx ]%9%9"(+9-%90%901%4.#"32>7#"&'#36632##33!0 $5" !5) 0!4`U5H44D:V_^@RR@]8FKKLF-P<#"YEX/ >YEX / >YEX#/# >Y A]A(8HXhx ]9 9"9%901#4&#"#3>32##33408/!44$*+:#cCVVCb;IOOPI(CGT/A&Y'4J-~6sggEa+/EX/ >YEX%/% >YEX(/( >Y+99 и /и/99$9'9*901##3267#"'#"''3267'33^@RR@; %-4%2413( 8FKKLF6sq & &0fl3ggS :#!"++ +!$к%" 909// //!/EX#/#>YEX/ >YEX/ >YEX / >YA]A(8HXhx ]99 9к%9+09501##33#4.#"#4&#"#367>32>32\?PP?[7DJIKDk. #0 %00 !6sgg6[ 2!WOYBCURK%$,E0X +// //EX/ >YEX / >YEX/ >YEX/ >YEX / >Y99 999901##373##33@Y55H\?PP?[6EJIKE(\B6sffW Ҹ//и/ 9/////EX/ >YEX / >YEX / >YEX/ >YEX/ >Y99 999901###33!##330u4t/NopOU9JI:U3>DDE@(llh6sfff //и/и EX/ >YEX / >YEX/>Y + + и и01#!#3!3!5!5!!;<<;K#(\46351bJ !++  + 9 9к 9  9и EX/ >YEX/ >YEX/ >YEX / >YEX/>Y ++ии ии01##373###333!5#533].}=++50//0!ƺ(ZB\46351bL "+++ +"+ "9ии"и/"$EX/ >YEX / >YEX/ >YEX/ >YEX/>Y!++к9к9и ии01###33###333#5#533+\/[*FWWG+**+鲧ٶ(llh6\46351bK,8B8 +,)+12+-.+B=+)и/,и/A&6FVfv ]A]14и.6иB?и?/BDEX3/3 >YEX7/7 >YEX>/>>YA++,)+6/+A:и:-и:1и><01#'#".54>32#.#"32>5#53###333#5#533',@**@,BI, %./ )+**+鲧ٶ(!6'6cSRc6r{@3!6Yq:=rW4'F`9:\46351bJ+  + + и EX/ >YEX/ >YEX/ >YEX/>Y+ +ииии и01###5!###333#5#533y~4}/-,,-hp:6\46351b 1(+"+ + +A&6FVfv ]A] 3 +-+  и и!01353#56654.#"#5335.54>32 3D&u5Y_:R3-Q<$_Y5u&D3+Kg<YEX / >Y+*+9 +*и*и*-и+и*01##373%353#56654.#"#5335.54>32++&5+*/+++   9A&&&6&F&V&f&v&&&&&&& ]A&&]&,@EX/ >YEX / >Y,++:!++и+и+к +,9+.и,и+01###33353#56654.#"#5335.54>320e3d/M``N)6^*HL,A($?/LH*^6)!:Q00Q:!(llh8YB, yu[.S?$$?S.[uߧy ,BY8YEX3/3 >YEX/>Y+&+./+39#393+01%4.#"32>7#".'#36632!!!#!$3 $8&$9) 3$42G+0'44N:+G2<{-P<#"YEX"/" >Y++"9"01#4&#"#3>32!!!#!94>!4$99'/0?'<{(CHS/A&Y'4J-D:Na)t&'+&!/EX(/( >Y+#$+и/ и / 99( 013267#"'#"&''3267%!!!#! %-4') 413"<xC & &fl:[<PTE,-+ ++ G+=+QR+A]A)9IYiy ],/к0-Q96 9AGG]AG)G9GIGYGiGyGGGGGGG ]QVEXS/S >YEX./.>YB+L+и&иL3иL901%4.#"32>#4&#"#4.#"#366326632#".54>32#3*))*,', ++*%#+-'5+Q(9$#:((:#$9(P,,1Q: :Q11Q: :Q[=F2D'Y0%2E(W*<6/-8U`\Y4+G+4иG!и*и=01%4.#"32>#".54>32#&&#"3267#5#".54>323J$9)%4!!4%*:# YG-G21G-!9-5=0#2"#2!.@5T40H00H01T5(O>''>O((O>''>O+[c(Hd=:dI*/C*8H#3(X !1Zn()+=+TE+d+[\+EA&6FVfv ]A]\и/)"ܸ(+к,)"9Add]Ad)d9dIdYdidyddddddd ]d2и2/E7и7/L=9L/KZ)[9\lи[pEXm/m >YEX*/*>YEX1/1>Y_+i+ и /\(и\4и_:иHиiO01%3265%4.#"32>%&#3>#"&5#"&54>76654&#"#6632327#5#".54>323 +<%M5Bh +) * + 1$//#+E  >95D1F,,-(3.MA0# >.X*;$%;),:.  )ZXL:(O>''>O((O>''>O2F(v/"0*-3253#"&'332654&''.54632#&&#"%#"&5#"&54>76654&#"#663232?4.#"32>'&#3663265I 4$ $# H!-5G'3&%-$1:60D(*##%  2  %!+) 0&!. 3253#"&'332654&''.54632#&&#"%#"&5#"&54>76654&#"#663232?4.#"32>'#4676654&#"#&6323%&#3663265= 4$ $# @!-5G'3&%-$1:60D(*##%  2  %!+) 0&!. 8Zav  "+Q *2% $)4;(54(( ;A+-!"-5;6%(!  #'  "0,&2 l*!!**""* "# 0$)( &?'~  /*.5E߻  +$%+<=+%$9/A]A)9IYiy ] 9/+=6ܸ<?к@=69EX>/>>YEXE/E>Y+(+01%#".'332654.''.54632#&&#"&#3>->%$>.4E44@ "C*!OL :+4;114 F,!$?099 .: d);%-E/?B11 . BM*>(6;1&  /2F(v/"_"O++>?+I8+8A&6FVfv ]A]и8(и(/0и0/IQEX/ >Y ++D;++ %и>и>/01%32654&##3267###32#"&5#".54>76654&#"#>32327 .F02*1+;#6)  ) --XL6JHHJakk0*.;"5$2=& :19?,A+&@2W "-Xܻ+()+#+RC+CA]A)9IYiy ];#9;/A;;]A;);9;I;Y;i;y;;;;;;; ](кR9R9кR9!R9C3и3/#JиJ/RZEX/>YEX / >YEX#/#>YEX/ >YEX*/* >Y 6+MF+&+* 0и0к!FM90(иIиI/01%32654&##326##373%###32#"&5#".54>76654&#"#6632327I "2!A,60(WW(0/}?++4C8_+7D 7.%+;")#,'D3(   ) ZXL6JHHL\Bakk0*.;"5$2=& :19?YT&@2K #.W++)*+$+QB+BA]A)9IYiy ]:$9:/A::]A:):9:I:Y:i:y::::::: ])к!Q9B4и4/$IиI/QYEX/ >YEX"/" >YEX+/+ >Y 7+LE+'++ 1и1и1и1)иHиH/01%32654&##326###33###32#"&5#"&54>76654&#"#6632327X / >*3.&II&.)]/^)DZZD@5Q)z4A 4,&9)8 '!)&A0&   ) ZXL6JHHLllhakk0*.;BI2=& :19?YT&@2L CNw6#+C@+IJ+D+qb+bA]A)9IYiy ]ZD9Z/AZZ]AZ)Z9ZIZYZiZyZZZZZZZ ]Iи@и/C+и+/A6&666F6V6f6v6666666 ]A66]bTиT/Diиi/qyEXK/K >Y;+(1+le+CH+; и /KCи/;H@и@/IиQиWиChиh/01%32654&##326#'#".54>32#.#"32>5#53%###32#"&5#"&54>76654&#"#6632327W / >*3.&II&.',@**@,BI, %./ )@5Q)z4A 4,&9)8 '!)&A0&   ) ZXL6JHHL!6'6cSRc6r{@3!6Yq:=rW4'F`9:akk0*.;BI2=& :19?YT&@2M 7Q8//A]A)9IYiy ]8+и+/* и!*-к.+!9!9EX/ >YEX/ >YEX/ >YEX,/, >Y&+,A]A(8HXhx ]*к,9*и301%4.#"32>##33#"&'#3>32i 2$&6""6'$2 Y=MM=X5BGGHB.E.0O11 &+.E.(O>''>O((O>''>O6sgg87cK,:7b*,KcU0++++!к"9(92////EX/ >YEX / >Y- + к"9-%к(901#3#4.#"#4.#"#36632>32::7$' 8!, 77 =42:)F>(6[1"2D'Y0%2E(W*<8-$Z["j+///EX/ >YEX / >YEX / >Y9 9901#3#'#'373::#FFII(6m9{ (9J-+4 +B+JI+A4&464F4V4f4v4444444 ]A44]4иIи)7-J9I;к<-J9ABB]AB)B9BIBYBiByBBBBBBB ]JLEXI/I>YEX./. >YEX:/:>Y$?+,+E++. 79<:.9H:.901%4&##32>4##32>4.#"32>##32##"&5463253MEuu5(}uu /$6$ 2""2 (7#j\%A03-6>4M5WccW:I4HG"6o#..P<"#YEX>/>>YEX1/1>YEX4/4>Y+$+,)+1-951-917A7'777G7W7g7w7777777 ]A77]@1-901#'#".54>32#.#"32>5#53%#"&'532>773$VB6U;;U6'E6$.)8#0E,+E1)?+} -'  :y(EO6cSRc6YEX:/:>Y+ ++01%#".7332>54''.54>32#.#"#3sc0Q<"53B$5(s|."0H0*J7!5)6CJ1%#6%m>;xx^k>^?6J-$5#b%''9*'I7"9S4'=+O@21 + -=">Tw3?\?-+89+ T+@M+MA-&-6-F-V-f-v------- ]A--]4@96@98;к<@9>@9ATT]AT)T9TITYTiTyTTTTTTT ]MZи@^/JC+!(+ Q+W+0+>W901%4.#"326'#".54>32#&&#"3267#'#373#"&'332655#"&5463253q  &('&"|?3!2#"3 0B()## /0!+z&f5$$,lb5;(<#%+")4>?3)#}-#!-7CEi ?C1E)(D2B9&0)6BT2,"7ttAK,3#99#QIEV"1'*q!+!иܸ%ܸ)/ /+)++ и /и!01"&55###"76;2##32##32#3k0 /+ |mmZ,  #$$X +,/%/A%%]A%)%9%I%Y%i%y%%%%%%% ], и /и%и/ "*и-#+)+ +и  01##"&5#"55433463322##364'&##(+)`6kz~CB<<6NE-[z0*)' g_e  ~OCWLM/B/иM:и:/A&6FVfv ]A]:и/:и/B#B4и4/JиJ/+E+ 7+013265"5543!2#66323276"#"&5#"&54>76654&#"##"A #5"%.:9D3* 70+>->$+$)    30   R2. (&+-&$!  +/0//0и/ и"и $и*и,и .и1/'/ //+,#+ иии-01"554335433235433232####"5###"55#----8"````"4}}}4 (M+%+ + * #++и#014332##"723265#"554332#"5743400F8r31.8."ML4eu ++A]A)9IYiy ]/++01%2#!".543324632#"&.X!!!!#v6!!!!$A+9и#и$ / +016!2#!".555546774332YBI.  d#%  ):#+),//, и /&A&&&6&F&V&f&v&&&&&&& ]A&&]и и/A]A)9IYiy ]и/ 9! 9) 9-+ $+к $ 9 к901##"77&532766332#"'724&'"&k   ).I0   '1O/} ": #/RXd*MX.%7Y#X0J5V",* %!+#+и#A!&!6!F!V!f!v!!!!!!! ]A!!]+ ++и и#и#/$01"'"36332##32##32#|mm,UV#$$U-W +,//A]A)9IYiy ],и/A&6FVfv ]A]  и /%и%/ -% ++ +014&#"3267#".54>32"5543!2#i:00::00:*'7!!7''7!!7'9<+*+!+'+' A!&!6!F!V!f!v!!!!!!! ]A!!]-//+$01#"5463324332#"&5543323265't(+ (E?G?(! &+ 4U!R>IA6".&R9r-ӻ$+++A]A)9IYiy ]A&6FVfv ]A]/+) ++01%#!5!4.#"32>7#".54>32By@nTTn@@nTTn@$Fx\\xFFx\\xF=n7#".54>324B@nTTn@@nTTn@$Fx\\xFFx\\xFhTn@@nTTn@@nT\xFFx\\xFFx9r/ݻ&+++A]A)9IYiy ]A&6FVfv ]A]1!++ +++01%!!!!74.#"32>7#".54>32(v@nTTn@@nTTn@$Fx\\xFFx\\xF_;Tn@@nTTn@@nT\xFFx\\xFFx9r 3*+ ++  +A ]A ) 9 I Y i y ]A&6FVfv ]A] 5%+/+ +++01%!5!5!5!!!74.#"32>7#".54>32$uu@nTTn@@nTTn@$Fx\\xFFx\\xFUw;톝Tn@@nTTn@@nT\xFFx\\xFFx9r 3*+ ++  +иA ]A ) 9 I Y i y ]A&6FVfv ]A] 5%+/++ +01%!#5!#!4.#"32>7#".54>32BBΨ@nTTn@@nTTn@$Fx\\xFFx\\xFXTn@@nTTn@@nT\xFFx\\xFFx9r#7.+ ++$+ии и A]A)9IYiy ]A&6FVfv ]A]$9)+3++ +01%5!#5!#3!534.#"32>7#".54>32BBBJB@nTTn@@nTTn@$Fx\\xFFx\\xFXTn@@nTTn@@nT\xFFx\\xFFx9r-AݸB//B8и8/A]A)9IYiy ].8.98$A$&$6$F$V$f$v$$$$$$$ ]A$$].C)3+=+01%.''>574.#"32>7#".54>32)K?12=F$-4ZD'A 5EQ)}@nTTn@@nTTn@$Fx\\xFFx\\xF^6?G$,K?42JcT ;.SH8Tn@@nTTn@@nT\xFFx\\xFFx9r';Os2F+ ++<(+A]A)9IYiy ]A & 6 F V f v ]A ]A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-++#+014.#"32>7#".54>324.#"32>7#".54>32-A()@--@((A-A=Y::Y==Y::Y=@nTTn@@nTTn@$Fx\\xFFx\\xF0"<--<"#<--<#.R=$$=R.-R=$$=R0Tn@@nTTn@@nT\xFFx\\xFFx9r1EF//F<и7#5!#74.#"32>7#".54>32(J@26?D"+.WD,6FP'z@nTTn@@nTTn@$Fx\\xFFx\\xFB5?G#-K=/0?RhD<<,RE7Tn@@nTTn@@nT\xFFx\\xFFx9r!5I J/"/J@и@/A""]A")"9"I"Y"i"y""""""" ]"6@69 @69@69@,A,&,6,F,V,f,v,,,,,,, ]A,,]6K1;+E'+!++01%.''>7#5!##534.#"32>7#".54>32$H@56@E"+.WC,6DM'@nTTn@@nTTn@$Fx\\xFFx\\xFI )4=!'A5*17EU3;;  (E7+;Tn@@nTTn@@nT\xFFx\\xFFx9r%90++&+ A]A)9IYiy ]A&6FVfv ]A]&;!++5+++01%#5'>75!5!4.#"32>7#".54>32B*fkh+,lpl,zȴ@nTTn@@nTTn@$Fx\\xFFx\\xF@ C 7#".54>32(oo@nTTn@@nTTn@$Fx\\xFFx\\xF]7#".54>32{AA| @nTTn@@nTTn@$Fx\\xFFx\\xF[;7;Tn@@nTTn@@nT\xFFx\\xFFx9r #7K.B++ +8$+A]A)9IYiy ]A&6FVfv ]A]A$$]A$)$9$I$Y$i$y$$$$$$$ ]A.&.6.F.V.f.v....... ]A..]8M3=+G)+ +# +++01%4&#"3267#"&54>327!5!'!5!4.#"32>7#".54>32IIIIIIII@hjji5O55O4K:@nTTn@@nTTn@$Fx\\xFFx\\xF-66,-44-ASSA 7))7<>:Tn@@nTTn@@nT\xFFx\\xFFx9r*>!5+ ++++A]A)9IYiy ]A!&!6!F!V!f!v!!!!!!! ]A!!]+@&0+:+ ++01'>7#5!##334.#"32>7#".54>32"6Uj4+aR7.i..ii@nTTn@@nTTn@$Fx\\xFFx\\xFVnR)D_{L,},Tn@@nTTn@@nT\xFFx\\xFFx9r ';2+++(+A]A)9IYiy ]A&6FVfv ]A](=#-+7++ +01%#32677##334.#"32>7#".54>32f T`i60T?i..ic@nTTn@@nTTn@$Fx\\xFFx\\xF  yG}+Tn@@nTTn@@nT\xFFx\\xFFx9r ';2+++(+A]A)9IYiy ]A&6FVfv ]A](=#-+7+++ +01%#!#2677##334.#"32>7#".54>32\Gd.JEi..if@nTTn@@nTTn@$Fx\\xFFx\\xF,r}/Tn@@nTTn@@nT\xFFx\\xFFx9r-A$8++ ++.+ и /A]A)9IYiy ]A$&$6$F$V$f$v$$$$$$$ ]A$$].C)3+=+ ++ +и/01%#!5!5!!2677##334.#"32>7#".54>32d(OXf?2NWi..ic@nTTn@@nTTn@$Fx\\xFFx\\xF , }-Tn@@nTTn@@nT\xFFx\\xFFx9r#7.+++  +$+  A]A)9IYiy ]A&6FVfv ]A]$9)+3++++01%#!!##334.#"32>7#".54>32- i..ie@nTTn@@nTTn@$Fx\\xFFx\\xF5+}2Tn@@nTTn@@nT\xFFx\\xFFx9r ';2++++(+и/ иA]A)9IYiy ]A&6FVfv ]A](=#-+7++ + +01%5#!3353##334.#"32>7#".54>3201 i..id@nTTn@@nTTn@$Fx\\xFFx\\xF,},Tn@@nTTn@@nT\xFFx\\xFFx9r!5I,@+++6"+9A""]A")"9"I"Y"i"y""""""" ]A,&,6,F,V,f,v,,,,,,, ]A,,]6K1;+E'+!+01%.''>57##334.#"32>7#".54>32*$+4%*A.6&3i..id@nTTn@@nTTn@$Fx\\xFFx\\xF4=@D?8(QgyB')UOC}6Tn@@nTTn@@nT\xFFx\\xFFx9r ';O2F++ +"#+<(+A]A)9IYiy ]A&6FVfv ]A]"%A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-++ +' +014&#"3267#".54>32##334.#"32>7#".54>32?33AA33?1*<&&=**=&&<*i..id@nTTn@@nTTn@$Fx\\xFFx\\xFIUVVUUVVU0O99O00O99OH}1Tn@@nTTn@@nT\xFFx\\xFFx9r%9M+0D++ !+:&+9 D:9и/D:9 #A&&]A&)&9&I&Y&i&y&&&&&&& ]A0&060F0V0f0v0000000 ]A00]:O5?+I+++%+%901%.''>5#5!#7##334.#"32>7#".54>3284+,7&$A3]-=$i..ib@nTTn@@nTTn@$Fx\\xFFx\\xFu4=AA>8%CVi<,, (QKA},Tn@@nTTn@@nT\xFFx\\xFFx9r!)=Q4H+$%+>*+H>9 H>9$'A**]A*)*9*I*Y*i*y******* ]A4&464F4V4f4v4444444 ]A44]>S9C+M/+!++)"+и/01%.''>7#5!##53##334.#"32>7#".54>32:1+$ !.8$#D6!p"0:d-i..id@nTTn@@nTTn@$Fx\\xFFx\\xF]-47641#;M\5,- !C<4,}3Tn@@nTTn@@nT\xFFx\\xFFx9r"6J-A++7#+ A79A79 A##]A#)#9#I#Y#i#y####### ]A-&-6-F-V-f-v------- ]A--]7L2<+F(+++"+" и /01'>7'>7667#5##334.#"32>7#".54>3207Rg4"E@7-u2BB? %i..id@nTTn@@nTTn@$Fx\\xFFx\\xF^mN)-;H, .5,},Tn@@nTTn@@nT\xFFx\\xFFx9r-A $8+++.+ иA]A)9IYiy ]A$&$6$F$V$f$v$$$$$$$ ]A$$].C)3+=+++  + и/01%#!!3#2677##334.#"32>7#".54>32`-SW`9=OGi..id@nTTn@@nTTn@$Fx\\xFFx\\xF ,*w}-Tn@@nTTn@@nT\xFFx\\xFFx9r!5I,@++6"+A""]A")"9"I"Y"i"y""""""" ]A,&,6,F,V,f,v,,,,,,, ]A,,]6K1;+E'++!+01%'667'76677667!5!##334.#"32>7#".54>32i$irn( !:4.37\i..id@nTTn@@nTTn@$Fx\\xFFx\\xF /-,}.Tn@@nTTn@@nT\xFFx\\xFFx9r'+/7K_BV+ ++23+L8+A]A)9IYiy ]A & 6 F V f v ]A ]25A88]A8)898I8Y8i8y8888888 ]AB&B6BFBVBfBvBBBBBBB ]ABB]LaGQ+[=++/,++(+#+0и0/#6и6/01%4.#"32>7#".54>327!5!'#53##334.#"32>7#".54>32 %$ $% 1+43))34+0yl;i..ig@nTTn@@nTTn@$Fx\\xFFx\\xF  $2 2##1  1,A-}.Tn@@nTTn@@nT\xFFx\\xFFx9r>Rf_I]++8'+S?+'A&6FVfv ]A]'и/.9./-A??]A?)?9?I?Y?i?y??????? ]AI&I6IFIVIfIvIIIIIII ]AII]ShNX+bD+ +;+3*+0132>5#"&5#".54>76654&#"#>32326774.#"32>7#".54>32i0J24-6'd  # OB5'#?U2 ?1-B5.>$ 9, @nTTn@@nTTn@$Fx\\xFFx\\xFK #&'$4 *#'2- *4  2*16&8$ 7+!Tn@@nTTn@@nT\xFFx\\xFFx9r(<PG3G+++=)+A]A)9IYiy ] и к!G=9A))]A)))9)I)Y)i)y))))))) ]A3&363F3V3f3v3333333 ]A33]=R8B+L.++$+01%4.#"32>7#"&'#3663274.#"32>7#".54>32"5%)9$#9*%5"40H14S55S11H0@nTTn@@nTTn@$Fx\\xFFx\\xF"D5!!5D""C6!!6C"/T?&1/Tc+6&@U#Tn@@nTTn@@nT\xFFx\\xFFx9r#7K#.B++8$+A&6FVfv ]A]A$$]A$)$9$I$Y$i$y$$$$$$$ ]A.&.6.F.V.f.v....... ]A..]8M3=+G)+ + +01%#".54>32#&&#"3267%4.#"32>7#".54>32 ]L0I32J0"=.4B3$7%%6#2G@nTTn@@nTTn@$Fx\\xFFx\\xFNT"=V42U?$)9$0=3E&)E2@5CTn@@nTTn@@nT\xFFx\\xFFx9r(<P=3G+ ++=)+A & 6 F V f v ]A ]&A))]A)))9)I)Y)i)y))))))) ]A3&363F3V3f3v3333333 ]A33]=R8B+L.++#+01%4.#"32>#5#".54>32534.#"32>7#".54>32l$9(&5""5&*9#44S31I00I10S4@nTTn@@nTTn@$Fx\\xFFx\\xF"C6!!6C""C6!!6CS.2&@T//U@&6+Tn@@nTTn@@nT\xFFx\\xFFx9r $8L/C+ +$+9%+  ии/$и/A%%]A%)%9%I%Y%i%y%%%%%%% ]A/&/6/F/V/f/v/////// ]A//]9N4>+H*+++  +014.#"32673#"&54>3274.#"32>7#".54>32w$3 1#$3!:D 3^NVi/H03I-@nTTn@@nTTn@$Fx\\xFFx\\xFR9,,9.%?/A0KSzo/U@%(CZ3 Tn@@nTTn@@nT\xFFx\\xFFx9r+?"6+  +,+ ܸ и A]A)9IYiy ]A"&"6"F"V"f"v""""""" ]A""],A'1+;++ +  иии/01&&#"3###53546324.#"32>7#".54>32R  PP4JJ'8  9@nTTn@@nTTn@$Fx\\xFFx\\xF*#/,t,2;=Tn@@nTTn@@nT\xFFx\\xFFx9r6J^AU+ *+!+K7+!A & 6 F V f v ]A ]!4A77]A7)797I7Y7i7y7777777 ]AA&A6AFAVAfAvAAAAAAA ]AAA]K`EX/>YFP+Z<+/+ '+A'7GWgw ]A]014.#"32>#".'332655#"&54>32534.#"32>7#".54>32y"4$"4$HE"3#4Yd"=05E1J?%-Zg2G,-$4@nTTn@@nTTn@$Fx\\xFFx\\xFo!?1/?$M^.=\j 3#0(OP# td/P;! FTn@@nTTn@@nT\xFFx\\xFFx9r/C&:+ ++0+ к:09A]A)9IYiy ]A&&&6&F&V&f&v&&&&&&& ]A&&]0E+5+?!++01#4.#"#3>3274.#"32>7#".54>324 )2#44&++<&@nTTn@@nTTn@$Fx\\xFFx\\xF1$)7!c ->'Tn@@nTTn@@nT\xFFx\\xFFx9r/&+++и/и/A]A)9IYiy ]A&6FVfv ]A]1!++ +01#37#534.#"32>7#".54>32 4466}@nTTn@@nTTn@$Fx\\xFFx\\xF SWTn@@nTTn@@nT\xFFx\\xFFx9r'6:ٻ +(4++A]A)9IYiy ]A&6FVfv ]A](7и7/48и8/#++01#".54>324.#"32>#"'53326537#53Fx\\xFFx\\xF$@nTTn@@nTTn@&1  577-\xFFx\\xFFx\Tn@@nTTn@@n47-SW9r 3*++  +* 9к* 9 * 9A ]A ) 9 I Y i y ]A&6FVfv ]A] 5%+/+01%#'#373%4.#"32>7#".54>32>V55I@nTTn@@nTTn@$Fx\\xFFx\\xFOcԤTn@@nTTn@@nT\xFFx\\xFFx9r+ɻ"+++A]A)9IYiy ]A&6FVfv ]A]-+' +01#34.#"32>7#".54>32 33@nTTn@@nTTn@$Fx\\xFFx\\xFcTn@@nTTn@@nT\xFFx\\xFFx9r&:N1E++  ++;'+кE;9 9A'']A')'9'I'Y'i'y''''''' ]A1&161F1V1f1v1111111 ]A11];P6@+J,+#+и#01%#4&#"#4.#"#3663266324.#"32>7#".54>32"406,4 #A=44G20F D4EIi@nTTn@@nTTn@$Fx\\xFFx\\xFP(5;+:!&*ZEK&2.)'0KPNTn@@nTTn@@nT\xFFx\\xFFx9r-A$8+  ++.+ к8.9A]A)9IYiy ]A$&$6$F$V$f$v$$$$$$$ ]A$$].C)3+=++01%#4&#"#3>324.#"32>7#".54>324691#44&,+=%@nTTn@@nTTn@$Fx\\xFFx\\xFW=G)7!L! ->'FTn@@nTTn@@nT\xFFx\\xFFx9r';Os2F+ ++<(+A]A)9IYiy ]A & 6 F V f v ]A ]A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-++#+014.#"32>7#".54>324.#"32>7#".54>32'7!"8''8"!7'45J./K55K/.J5@nTTn@@nTTn@$Fx\\xFFx\\xF/*E11E**E11E*3V>"">V33V>##>V5Tn@@nTTn@@nT\xFFx\\xFFx9r$8LG/C+++9%+A]A)9IYiy ] икC99A%%]A%)%9%I%Y%i%y%%%%%%% ]A/&/6/F/V/f/v/////// ]A//]9N4>+H*+"++014.#"32>7#"&'#366324.#"32>7#".54>32%4 $7'$9( 4%4iX4S44N:Xi@nTTn@@nTTn@$Fx\\xFFx\\xF['D33E'$C5 3E&m{6*AT*7|Tn@@nTTn@@nT\xFFx\\xFFx9r(<PG3G+ +('+=)+'A & 6 F V f v ]A ]'кG=9A))]A)))9)I)Y)i)y))))))) ]A3&363F3V3f3v3333333 ]A33]=R8B+L.+#++014.#"32>#5#".54>32534.#"32>7#".54>32k'8$ 3$$3 )9$55S6+G22G+:O5@nTTn@@nTTn@$Fx\\xFFx\\xF]'E33D'&E3 5C*6 7#".54>32f 8)44 Q;%@nTTn@@nTTn@$Fx\\xFFx\\xF+;#e0<Tn@@nTTn@@nT\xFFx\\xFFx9r1EYU<P+ + !+F2+! 9/A]A)9IYiy ] 9/'A22]A2)292I2Y2i2y2222222 ]A<&<6<F<V<f<v<<<<<<< ]A<<]F[AK+U7+ +$+01%#"&'332654&''.54632#&&#"74.#"32>7#".54>32-?%Hb4E64@3+C+!OM!:+5;214 F- @nTTn@@nTTn@$Fx\\xFFx\\xF#3 KP68**$! '8C$5#/2)! (?Tn@@nTTn@@nT\xFFx\\xFFx9r'A +;0++A]A)9IYiy ]A&6FVfv ]A]04и;6и0Aܸ8#++>++41+47и1901#".54>324.#"32>#".5#53533#3327Fx\\xFFx\\xF$@nTTn@@nTTn@  $ LL4WW -\xFFx\\xFFx\Tn@@nTTn@@n %3,,9r)= 4+ ++*+A]A)9IYiy ]A & 6 F V f v ]A ]*?%/+9++01%#5#"&5332>5534.#"32>7#".54>325 K8OJ47;0!5@nTTn@@nTTn@$Fx\\xFFx\\xFIN(2UQ?;.<#Tn@@nTTn@@nT\xFFx\\xFFx9r.////%и%/A]A)9IYiy ]%9%9%9%A&6FVfv ]A]0 +* +01#34.#"32>7#".54>32;8@nTTn@@nTTn@$Fx\\xFFx\\xFGTn@@nTTn@@nT\xFFx\\xFFx9r 45/ /5+и+/A ]A ) 9 I Y i y ] !+!9+!9+!9+!9 +!9+A&6FVfv ]A]!6&+0+01##334.#"32>7#".54>32&7#".54>32<z@nTTn@@nTTn@$Fx\\xFFx\\xFNѩTn@@nTTn@@nT\xFFx\\xFFx9r&:;//;1и1/A]A)9IYiy ]'1'91'91'91A&6FVfv ]A]'<",+6+ +01#"&'53267734.#"32>7#".54>32-*7 @nTTn@@nTTn@$Fx\\xFFx\\xF+ %)-$+rTn@@nTTn@@nT\xFFx\\xFFx9r 12/ /2(и(/A ]A ) 9 I Y i y ] (9(9(A&6FVfv ]A]3#+-+ ++01%!5!5!!74.#"32>7#".54>32C@nTTn@@nTTn@$Fx\\xFFx\\xFR+a,)Tn@@nTTn@@nT\xFFx\\xFFx9r0ӻ'++ +A ]A ) 9 I Y i y ]A&6FVfv ]A]2"+,++01%##566734.#"32>7#".54>322v=<'v@nTTn@@nTTn@$Fx\\xFFx\\xF !56Tn@@nTTn@@nT\xFFx\\xFFx9r';OE2F++ +<(+и/A ]A ) 9 I Y i y ]&и&/A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-+ +'+01%!>76654&#"#&>32!74.#"32>7#".54>32 4F+38B9 .2-B,%>-%0 7,+@nTTn@@nTTn@$Fx\\xFFx\\xF$0H;5#H36>+:+J5);P(,++Tn@@nTTn@@nT\xFFx\\xFFx9r2FZm=Q+  +-+G3+-ܹA]A)9IYiy ]% 9%/$0QG9A33]A3)393I3Y3i3y3333333 ]A=&=6=F=V=f=v======= ]A==]G\BL+V8++*!++0901%#".7332654&##532>54&#"#&>3274.#"32>7#".54>324E%)E31%2-(-;R<(D1NE0B H:Tn@@nTTn@@nT\xFFx\\xFFx9r !5,+ +"+ иA]A)9IYiy ]A&6FVfv ]A]"7'+1+ + и01%##5#53374.#"32>7#".54>32IN00N@nTTn@@nTTn@$Fx\\xFFx\\xF.//~[Tn@@nTTn@@nT\xFFx\\xFFx9r(<PA3G+  ++=)+A]A)9IYiy ]#G=9A))]A)))9)I)Y)i)y))))))) ]A3&363F3V3f3v3333333 ]A33]=R8B+L.++ !+&+01%#".'332>54.#"#!#663274.#"32>7#".54>323E('A01$0 3$$4&?-3"@"Va@nTTn@@nTTn@$Fx\\xFFx\\xF*F4-A(0"*9 7(!$+/j Tn@@nTTn@@nT\xFFx\\xFFx9r2FZ}=Q++++G3+A]A)9IYiy ]A+&+6+F+V+f+v+++++++ ]A++]A33]A3)393I3Y3i3y3333333 ]A=&=6=F=V=f=v======= ]A==]G\BL+V8+ +&+.+01%4&#"32>7#".54>32#&&#"663274.#"32>7#".54>32yH;7!5!4.#"32>7#".54>321K69)=I&_@nTTn@@nTTn@$Fx\\xFFx\\xF/tKXh'/Tn@@nTTn@@nT\xFFx\\xFFx9r#G[oRf+ .+@+\H+@A & 6 F V f v ]A ]A]A)9IYiy ] 6ܹ$3f\9Cf\9AHH]AH)H9HIHYHiHyHHHHHHH ]AR&R6RFRVRfRvRRRRRRR ]ARR]\qEX)/)>YWa+kM+;++)A'7GWgw ]A]39C901%4.#"32>4&#"32>#".54>7&&54>3274.#"32>7#".54>32&33&&33%>66> ** I2F()E2)%3*>''>*2&)@nTTn@@nTTn@$Fx\\xFFx\\xF../!!/1+::+'')@,,@)0( D.4''4.A(0eTn@@nTTn@@nT\xFFx\\xFFx9r0DX};O+ '+!+E1+A & 6 F V f v ]A ]A!!]A!)!9!I!Y!i!y!!!!!!! ]A11]A1)191I1Y1i1y1111111 ]A;&;6;F;V;f;v;;;;;;; ]A;;]EZ@J+T6++,+ $+014.#"32>#"&'332>5#"&54>324.#"32>7#".54>32z"20!G;1#=b_KW1@3%6" K7Ra/B).H2@nTTn@@nTTn@$Fx\\xFFx\\xF5))7CS)8EOB,:$D^;):fZ*G3EpRTn@@nTTn@@nT\xFFx\\xFFx9r0DX;O++ '++E1+A]A)9IYiy ]A & 6 F V f v ]A ]A11]A1)191I1Y1i1y1111111 ]A;&;6;F;V;f;v;;;;;;; ]A;;]EZ@J+T6+"+,++"и/014.#"32>##56673#".54>324.#"32>7#".54>32 ,+ +, 2q=7''=++=''=++='@nTTn@@nTTn@$Fx\\xFFx\\xF/2YB''BY23YC''CY 35BjK))KjABjK((KjCTn@@nTTn@@nT\xFFx\\xFFx9r%90++  +&+A]A)9IYiy ]A&6FVfv ]A]&;!++5++ и 01%##56673##566734.#"32>7#".54>322v=<'2v==&@nTTn@@nTTn@$Fx\\xFFx\\xF!34!34Tn@@nTTn@@nT\xFFx\\xFFx9r,@Te7K+++#+A-+# и /A]A)9IYiy ]+и+/A--]A-)-9-I-Y-i-y------- ]A7&767F7V7f7v7777777 ]A77]AV<F+P2+, + ++ и/01%##56673!>76654&#"#&>32!74.#"32>7#".54>32f/j68$/?'-1:4) .(<'CW",9Q @nTTn@@nTTn@$Fx\\xFFx\\xF"44/G:5"E26;*8+H4QK#7/',S+Tn@@nTTn@@nT\xFFx\\xFFx9r9MaDX+++4%+N:+4ܹ A%%]A%)%9%I%Y%i%y%%%%%%% ],7XN9A::]A:):9:I:Y:i:y::::::: ]AD&D6DFDVDfDvDDDDDDD ]ADD]NcIS+]?++1(++ +и/7 901%##56673#".7332654&##532>54&#"#4>3274.#"32>7#".54>32d0d70%/>"%?-/E56GC1&&#555:-):$FR0#06@nTTn@@nTTn@$Fx\\xFFx\\xF 35t*>)0F.EK><9;/&-9O<(C1ND.@ G>Tn@@nTTn@@nT\xFFx\\xFFx9r *>!5+++++ иA]A)9IYiy ]A!&!6!F!V!f!v!!!!!!! ]A!!]+@&0+:+ ++и 01%##56673##5#53374.#"32>7#".54>32l/e71%F,,F@nTTn@@nTTn@$Fx\\xFFx\\xF(ض 35Y/yaTn@@nTTn@@nT\xFFx\\xFFx9r-AU]8L+++ +B.+A]A)9IYiy ](LB9A..]A.).9.I.Y.i.y....... ]A8&868F8V8f8v8888888 ]A88]BW=G+Q3++%&++++%01%##56673#".'332>54&#"#3#663274.#"32>7#".54>32j/e71%.>$#<+/B2.B9#7+/6NX@nTTn@@nTTn@$Fx\\xFFx\\xF 35)E3,@'76676654&#"#&6323'#5#5>73!+ )'"+:73",7G+)& 0* ('#%*;5'u+G $)A?*1&! "(.)8F #( -2"$,OS)?@+F9+ +'+'ܹA]A)9IYiy ]*@9AF&F6FFFVFfFvFFFFFFF ]AFF]F-и-/N@?9'U +C<++"+O-+*901!#"&7332654&##532654&#"#4632#>76676654&#"#&6323%'$",7G+)& 0* ('#%*;5'!+  )'"+: .$  !5*73n;,);p+G $*ZZ*2" >ջ1+9*+ + иA9&969F9V9f9v9999999 ]A99]9!ܹ<1 9/4-+ +'$++ ик<$'901%5##5#5733'#"&7332654&##532654&#"#4632n;,); ",7G+)& 0* ('#%*;5'*ZZ* 2)A?*1&! "(.)8F #( - 6:E5;<+!+1 +1A ]A ) 9 I Y i y ]'ܹA!!]A!)!9!I!Y!i!y!!!!!!! ]$<94<9<DиD/1GD/ +?>++,+$949014&#"326'4&#"3265#"&5467&&54>32'#5#5>731"!0."#1%*)(;".@> '(!v+H %"$###$$ *?03 '# #' /2"! 48eBX+`Q+ +/ +/A ]A ) 9 I Y i y ]%ܹA ]A ) 9 I Y i y ]#X92X9A`&`6`F`V`f`v``````` ]A``]`Hܹ9cX9/g +[T++NK+*+E>+#929cKN9014&#"326'4&#"326#"&5467&54>32'#"&7332654&##532654&#"#46321"!/."#0%))(:".@=, '( ",7G+)& 0* ('#%*;5'"$###$$ *?03 6# #' /2)A?*1&! "(.)8F #( -  6:[DC+;J+!+1 +1A ]A ) 9 I Y i y ]'ܹA!!]A!)!9!I!Y!i!y!!!!!!! ]$C94C9A;&;6;F;V;f;v;;;;;;; ]A;;]DRиR/VC91] +ST+YM++,+G@+$949VMY9014&#"326'4&#"3265#"&5467&&54>32'#"&'332654&#"#73#66321"!0."#1%*)(;".@> '(!".2F., &-/$&&#!9B"$###$$ *?03 '# #' /2, =8%*2##1(^ G 6:I/@A+!+1 +1A ]A ) 9 I Y i y ]'ܹA!!]A!)!9!I!Y!i!y!!!!!!! ]$A94A9FA91K +IF++,+$949014&#"326'4&#"3265#"&5467&&54>32'%#>7#531"!0."#1%*)(;".@> '(!/*0!2""$###$$ *?03 '# #' /2D9Of>.PKJ'+KZeiP:+Z+[+ Z9Z#ܸ+и+/Z1и1/2Z9ZCAP&P6PFPVPfPvPPPPPPP ]APP]eиe/gEX / >YEX/>YU5+[+CL+Uи/5)к+5U9 IAII]AI(I8IHIXIhIxIIIIIII ]`01554766326632##32676#"'##"55#"'&&5467>3354.#"#&32>574.#"D ,*/ -)3 "% $2$ ) %%8 %&   (x 93  z' $#5OZ%@7% .  ' "824W ,F;!,=A4<9)+:;#)=(>// ии>"и"/" 9(и"*A*&*6*F*V*f*v******* ]A**]4и4/ ? /EX%/%>Y/++ ии%9A99]A9(989H9X9h9x9999999 ]01"554335433232####"55#".54632532>54.#"'  ')c.K6h^2G!2%%2! !2%%2" $"I I" I`,S`5^2'IA&&AIK@&&@K*2FG/3/ и /3и/3Gи/&9=A=&=6=F=V=f=v======= ]A==]HEX#/#>YB+#8A88]A8(888H8X8h8x8888888 ]&#8901&77676#".54>32&&'''&77&&4.#"32>  )  '053O66O33O6#;&  ' !5&&6!!6&&5!c   >x 5aI,,Ia55`J+9   t!I<(( +';+6+A&6FVfv ]A] 9/6,'0и6@EX/ >YEX/ >YEX9/9>Y4-+4иA]A(8HXhx ]!и9$ܸ9)A)')7)G)W)g)w))))))) ]A))]01##"54332'4632#"&%4632#"&23265#"554332#"5543|,,\!!!!:!!!!oA)t~!!!!!!!!4_:,"4X/ + / ///901%##"&''##"54332776332 , D((3 Q! '# ++A]A)9IYiy ]%+ +!+ 01%2#!"55433#"5543324632#"&yjC!!!!""c"m7!!!!E)W+ ܺ9и(и) +&+ 01632#!"554335546775#"554332vt{j  I""&A  E"'3?@/4/@ и /-A-&-6-F-V-f-v------- ]A--]и/ A44]A4)494I4Y4i4y4444444 ]4и/4и/и/1 98 9AEX/>YEX/>Y;#+#и(A((]A(((8(H(X(h(x((((((( ]01##"77&'&547632766332#"'&'"&4'&'32766k   . 19e3&  1.N22'yD# #Y %.E"/B#-/7qHQ  FGj7a!'+ <L'+&"&'&$=L /DO)@+5+E+ 59-59A@&@6@F@V@f@v@@@@@@@ ]A@@]OиO/QEX / >YEX/>Y0+E+0и*и ;A;;]A;(;8;H;X;h;x;;;;;;; ]J01".54>326632##32676#"&''2>554.#"%4.#"#3!!3#%42%)3 "% $&;4$ $;,Ia55`J+/(&15OZ%@7% .  ,)&/<'54.##"554332654.#"##"54>32#!3"$BA#)-,&/C*+E2& "3L4+ " )22$  >B+"'4 )H5 3A!6J*6#H:$><01//1и/A]A)9IYiy ] и%и%/ 2//++ +99014332636#"&'##"56.&326766>'(d.L445]3F'N 4$$3  2%%2 r `0Od3^ON2$ J?))?JK && K3O6o+ии$и5/'1+++иии#017"554335#"554335433232##32##3276#"'&55Xk * R)% *8[""dK 5$[7><0 + ++A & 6 F V f v ]A ] $и$/*и*/+ 9 +-+01#"&55433232654&#"##"54&'&6332632E?G?(! &2<5H( $/[XT=>IA6".&,\MUVh+08Tnd #<m=//=и/ и/ и/ 9>*///  +*901##"54&#"##"54&'&63326632&54632&5546766(2<5H( $E-XT)" %  \MUVh+08+)ndm "#+;  ,<[) ++!+9A & 6 F V f v ]A ]99A!!]A!)!9!I!Y!i!y!!!!!!! ]&9+/&///+01&&54673%#!5!#>54.'38==8+''Ff=8+' '+8=UU54.'38==8+''F=8+' '+8=UU54.'38==8+''b=8+' '+8=UUՕU54.'38==8+'' ab=8+' '+8=UUU54.'38==8+''FF=8+' '+8=UU54.'38==8+''FFFZF=8+' '+8=UU/5/>и/A55]A5)595I5Y5i5y5555555 ]5,,9 A & 6 F V f v ]A ],9,90,9:,9,?////:/901&&54673%.''>57#>54.'38==8+''+OB34@J&/6`F)E 7IU+=8+' '+8=UU:9ACC]AC)C9CICYCiCyCCCCCCC ]H:9:M/=//H/++5+014.#"32>&&54673#".54>32#>54.'3/D*+D/0C**D/8==8+''!?^==^?!!?^==^?!=8+' '+8=.$?//?$%?//?RUU090C/>//3/$#+9#&01&&54673%.''>7#5!#7#>54.'38==8+''*NB57BH$-0[H.9IT)=8+' '+8=UU7#5!#!5!#>54.'38==8+''&LC78CI$-1ZG.9HQ):=8+' '+8=UU>  *H;->U75!5!#>54.'38==8+''F,kpm..qvq.g=8+' '+8=UU54.'38==8+''[[=8+' '+8=UUU54.'38==8+''4́DD=8+' '+8=UU  F>U327!5!'!5!#>54.'3LMMLLMML$8==8+''noon8R87S8NV 2=8+' '+8=/99./77UU7!5!##33#>54.'38==8+''Q>bz<"2o^@\n55nd=8+' '+8=UU77##33#>54.'38==8+''%`ny>70b^U$m55mb=8+' '+8=UU54.'38==8+'')`jq9[UPm55me=8+' '+8=UU77##33#>54.'38==8+''.[evH*`-WX\2m55m\=8+' '+8=UU54.'3O8==8+''FZ)m55ma=8+' '+8=cUU54.'3Y8==8+''L88%m55m_=8+' '+8=UU5%##33#>54.'38==8+''<1("1,<"m55mb=8+' '+8=UU:9ACC]AC)C9CICYCiCyCCCCCCC ]H:9:M /=/-+ #+92+ܸH014&#"326&&54673#".54>32##33#>54.'3H;;KK;;H8==8+''U0F+,F00F,+F0n44nb=8+' '+8=JbccbbbbsUU5#5!#7##33#>54.'38==8+''i<2##3?",)K:#3F*m55m]=8+' '+8=UU7#5!##53##33#>54.'38==8+''q93( '4@#)(N>''8B sOn44nZ=8+' '+8=UU7'>7667!5##33#>54.'38==8+''^>`v<#'PI@=@A!KMH lm55ma=8+' '+8=UU!2,U54.'38==8+''3adnBm"[Qm55mb=8+' '+8=UU54.'38==8+'')y~/ &B; 5;?#"pm55mT=8+' '+8=UU+=:+5+BиB/5HиH/01%4.#"32>&&54673#".54>327!5!'#53##33#>54.'3 + * * + 8==8+''Y1<:0/;<17>|^m55m^=8+' '+8=$$%%UU+9)90132>5&&54673%#"&5#".54>76654.#"#>3232677#>54.'3q4O580"9*@8==8+''  %UF 9*&C[6"" ,0F82B'"=/  =8+' '+8=T  &))'7#VUU//I/++6+.93901%4.#"32>&&54673#"&'#366327#>54.'3%9'+=&&=,'9% 8==8+''3N47Y99X44N3=8+' '+8=%H9##9H%$H9$$9HUU32#&&#"3267%#>54.'38==8+'' dQ3O66O3Kh 8 G6';'(:%6K %=8+' '+8=UU&&54673%#5#".54>323#>54.'3w'<+(9%%9(,=&:8==8+''8Y65N33N51*" 8=8+' '+8=%H9##9H%$H9$$9HUU327#>54.'3&6""5%8==8+''&7#>I 7eS.K63M37M1=8+' '+8=R<0/54.'38==8+''  "VV7PP*; G=8+' '+8=UU&&54673#".'332655#"&54>3253#>54.'3$8'$8'MJ$8%'8==8+''_l$A3"9J4OD'1`n6L/0'8=8+' '+8=k$C5 3C&Sd1BUU327#>54.'38==8+''7 , !6%88'//@(=8+' '+8=UU54.'38==8+''588::=8+' '+8=UU54.'3&&54673%#"'53326537#53=8+' '+8=8==8+''C)4  9;;,U54.'38==8+''B\99N=8+' '+8=UU54.'38==8+''277=8+' '+8=UU/7+$к.971к4901&&54673%#4&#"#4.#"#366326632#>54.'38==8+''\73;/7 %#3!77K63K H8KMt=8+' '+8=UU32#>54.'38==8+''8;< 4&88)//A(=8+' '+8=UU:9ACC]AC)C9CICYCiCyCCCCCCC ]H:9:M/=//H/++5+014.#"32>&&54673#".54>32#>54.'3*:$$<**<$$:*8==8+''8P12Q88Q21P8=8+' '+8=/-J55J--J55JYUU/6+)+.939014.#"32>&&54673#".'#36632#>54.'3(8"&;)'<+"8(8==8+''p_3+88S>/M6=8+' '+8=Y*I7 7J*&H9" 7I+UU/5+++(989014.#"32>&&54673%#5#".54>3253#>54.'3)<&"7''7"+>&08==8+''9Y9/K66K/>T9=8+' '+8=X*J7 7I*)I7 "9H)UU54.'38==8+''"<,77W?4=8+' '+8=UU54.'38==8+''1C('B17J98D6.H.$UR#>/9?557"K/#=8+' '+8=UU+9<-,'# *54.'3&&54673%#".5#53533#3327=8+' '+8=8==8+''~  & RR7]] ,U53#>54.'38==8+''9 P54.'38==8+''?<)=8+' '+8=UU54.'38==8+''\App?;vo@pv=8+' '+8=UU54.'38==8+''@ADC=8+' '+8=UU773#>54.'38==8+''1- ;=8+' '+8=UU54.'38==8+''#Z2=8+' '+8=UU54.'38==8+''<7CC+=8+' '+8=UU:9ACC]AC)C9CICYCiCyCCCCCCC ]H:9:M/=//H/EX/>Y)+801&&54673%!>76654&#"#&>32!7#>54.'38==8+''s#9N/8=H?#3!71I0(D2)5#=0I=8+' '+8=UU4,000U54.##532>54&#"#&>327#>54.'38==8+''"9L)-L86)7!!8*'5..,#AA 3#63G,Ud;*;A=8+' '+8=UU54.'3,ߜ8==8+''V55V=8+' '+8=LUU//I/ +01+6*+3901&&54673#".'332>54.#"#!!66327#>54.'38==8+'' 7L,+H45S=#8''9"*F281%F%/K4=8+' '+8=UU#$<,$(I48Q=U&&54673#".54>32#&&#"66327#>54.'3OABN%6""6%#8==8+''4J-2O6 :N/+A-5C9(;(S8-I5=8+' '+8=KVXJ!;-.7!5!#>54.'38==8+''6S:#>.BQ)=8+' '+8=UU4&#"32>&&54673#".54>7&&54>327#>54.'3*77*)8 8)D<+ 6+39014.#"32>&&54673#"&'332>5#"&54>32#>54.'3%7""5%NA!5'+8==8+''lhS_6G8);&S<[j3I-2O7=8+' '+8=!;--+*)+GиQܸи4&и&/014.#"32>&&546737##56673#".54>32#>54.'30!!//!!08==8+''7}C>++D//C,,C//D+=8+' '+8='7bJ**Jb78bI++IblUUYEX/>Yи01&&546737##56673##56673#>54.'38==8+''7CC+07CC *=8+' '+8=UU7>54&#"#&>32!7#>54.'38==8+''}3u<='!3F+'@9 -3-A+%>-%0?X'=8+' '+8=UU54&#"#4>327#>54.'38==8+''5n<6)4D%)E24$1WB,J5UK3F N@U54.'38==8+''4o<6)M00M=8+' '+8=FUU54&#"#!#66327#>54.'38==8+''4o<6)3D''B04I73"I>'=/3"<"*D0=8+' '+8=UU'>54.'&&'#".5467467&''7667667667667 <7-!%cS(7! * :% 3 $5VJ&48# < 0I#!S0!-;TP='E3)P' $$$%gVo2 "-6' =b+ (  !'>54.'&&'#".54>7667&''27667667667667 #7(/3:T5$6E( 6'E.> +D ' 3J..C"J) I9].Vz$:" Ieac,.1`e "$*+*.~-WK8B *8D#1( L{8 4  +&JF;#H.A0K =-F!<& ! s /+01%#"&'.73267&&'7=5F ':'0(# R-3&'"Xbj4 Z<$!+ ?732'&&'7k]Aea $??<? 0378);%z/_*$.35${FA?_,%"-U  !7FA%+ A8@C=////+=99*98=901"&'.'&&'&&#"'667667'>766767&&'7@   685<+&X8?\+CC@C C,8.7Z#IO'/ ("*%:D.+,vU? 2!L7   4$( L7?;76667&&'76%O&$ CGBL!;0oCJt7#TUR T)R2?2 !^;/`'$#h3S 1+//IS%>78g V A*]?)  A#-3' LT'+E*+ +*и/A&6FVfv ]A]' 9A]A)9IYiy ]AE&E6EFEVEfEvEEEEEEE ]AEE]Eи/Eи/*-и*4и*7и7/E;и;/EBиB/7//"/H+79"и/;79EH9M7901%3265%'6654.#"#".5467&65''67&&'66766327&&'7h3)  v,<%"DB"(DK"0'N@"1 5$D".1 -G"$?0 =! D#:  d94( 7 L:! @\*!),[" :#/-A 8&7 /# %87/V@NV̻G+9D+ +9A ]A ) 9 I Y i y ]A9&969F9V9f9v9999999 ]A99]9и/9и/Dи/D и /AG&G6GFGVGfGvGGGGGGG ]AGG]%G9D(и(/9/и//96и6/DA//EX+/+ >Y<+LL9%+9/+99<9D+9O+901%'6654.#"#".5467&55''67&&'667663264'326&&'76L..VS(2^X(';2"_P(> B*X*<B*7[--N:!-<0  M)*W#&JA1 G ^I' Ns!5(+39r+%$Q=8U$ I0  G  #<',E(S;*H"  |&I&?%tt<D+A]A)9IYiy ]/ / /EX4/4 >Y;+ 49' 49+и+/8;901&''7667>5&&#"'667667'6676676676676&&'7z  =9  '( &  (3$+j;J& ?!.>%$8'K&% B0$2\ZRTM.8U$JOI''i/;C^  V 5a.#M4Ek2&j?_<DJQ+A]A)9IYiy ]O// //:+ O9&O9*и*/7:901&''7667>54&#"'667667'6676676676676&&'7'&'77&&'7Z  ?8  (3&  &-!*k;K) !; /B$%@* J ! CDC/#2]2)461&.:0YQUN/7T #IPH'&k/;C` T 5].$J4OEk1%k@7"6-6N+A&6FVfv ]A]*949/>/  +# 9 )4>9=>901&&#"67".54632'&''667&&''667&&'&&'7667667 G.$EE.p50&"6B ( >iC\S4^;1E{97uD -J#&E,  Z #5$;$ 0GEn*A)- (*P.PACMT$/; - 8  " 8#  =  ,AMU\+A&6FVfv ]A])939Y//  +# 9 (3Y9<Y901&&#"67".54632&&''667&&''667&&'&&'76676677&&'77&'7H0%FD/p52$"6B ' >hC^O5^;*k7uC .H#&F*   Y $4#<# .GH&.,--En*A)- (*P-QACM/I/&< ,9    7$  >  +J5. <./!I[+A&6FVfv ]A]//01&&'.5467667W2t4  9j*I266 @{1gj;  B^4!JG@  @]'/[ +A&6FVfv ]A]//01.'.5467667&&'77&&'7798 :g*I264  ?z2V'-0(/g5cXL  @^4!JG@  @]4/ 5/!G&BO>2++и/ 29A]A)9IYiy ]и/и/и/ и /#и#/2)A>&>6>F>V>f>v>>>>>>> ]A>>]82>9A2)9/ /+ 9и/8 9A 901'>754'''26754'&&'667.54676677G6X 4)Q/; }M5a3\)=-B Z 3 Z$=j]T%1#Ycl5( B6 - #O2  rW "NSQ&QL )&&_b]&G5~^?GN O/+/O и /A&6FVfv ]A]и/ 9+ 9и/% 9+2и2/+5и5/8 9<K/$//.+$K9$K9%$K9.+и+/8$K901%&&54676657%'>75''267&4'&&'6677&&'77&'7 A#\ 3/N 2(P.9 ;_)2]4[#75%/--,;d+ DLQL)$'_a]'H4~ .<>j]T%1$Ycj5@D'5+ (Z6 $6/! >-/ U#] +A&6FVfv ]A]+01%#"&'&&546732>7.7662y4Hk)5<(ym@HI"[%agk/*chi. ?'2$2   B a$,4a +A&6FVfv ]A]1/+01#"&'&&546732>7.766&&'77&&'74w4Hk*48' ylAFJ#]%agj/)chh-&/,%/ >(3 $3   A -6," 50<+A&6FVfv ]A],96//  +#69 +ܺ56901&&#"67.54632.''667&&'&&'7667$W8)X-E0n61$!6B!' BgA\S4_A# Ao10i?  W !4W% S;B*4**Q-Q@ES =;=! > !/! S0G<DK+A&6FVfv ]A]%9H//+%$+H9.H901%&&#"67.54632&&''667&&'&&'7667&&'77&'7L/o7/$!4B!&Af@]P017!)=?n53g>  U 0M!" N1(X*;'.- ,-t*3 **Q-PAES @vA > ".  Q0 K S; 5. =,/ ! + и/ и/A&6FVfv ]A]/+01%#".7>&'&&'3267?W2O6[ 2#G~3[aU!&'&&'3267&'77&&'7=Z1N5^ 3$E0M!./,&-\aV!LM/"/A""]A")"9"I"Y"i"y""""""" ];и/Mи/GAG&G6GFGVGfGvGGGGGGG ]AGG]G9"и"$и$/.и./"1и1/"4и4/7;9;8и"?7//;%+J+B+%и/79J9"B9%.)и)/;1и1/;>и>/01&&'667#".54>32655'667667&&5&&'66&&#"326I;O P9T>P &$.#&1#RV" !2d;\X/ % * )5U!>@>8j)>SH %5 $=+ ":S &;&&#$1:ox<HPXAY/!/9и/Yи/CAC&C6CFCVCfCvCCCCCCC ]ACC]C9!и!$и$/.и./!1и1/!4и4/799!=M/ /F+9%+@+%и/ M9F9!@9)%9991и1/7 M99<и7#".546324655'667667&&'&&'62&&#"326&&'7&&'7;L )tV4+#&#.#N:#QX!#2f<[Y0% -) )%-}'.5V <<>rV? !+6$ &4 KU  <R!%$5&&-040!C6."cUV/J/Rи/Rи/V@и@/ROиO/ @O9Jи/Jи/@Dи/@.и./@1и1/@=и=/DGN/ &+R+и/и/&#и#/5R9RJиJ/01&''276677'.'&&7665'667667&4'&&'66754&'663~N{ 3"Co=  ?5[9j6&:,!9V&! ! ]?pAXDg"g$4":L,8&4N$79 !T G)(T2 C0)'R4 x1*`:JW_gӸh/P/Tи/Tи/h@и@/ @T9Pи/@Gи@/и//@2и2/@>и>/GEd/!'+T+и/и/6T9TJиJ/TWиW/01&&''276677'.'&&7665'667667&''&&'6674&5&&'66'&&'77&&'7N*iC 2#>i;  =5]8h7&:,!3N$!![/)a:D 5/! 6.! 0+A&6FVfv ]A]'/ +"+0+и/-09017.54>7'667'266766 EnL(nt;jP/"3;$UXW' Od.NFB!,`\W$&'HIH&qVE0CP%:=Z/G2(KB1 1^L Y>9ET2$b08@+A&6FVfv ]A]'/ +"+0+и/-09017.54>7'667'2667667&&'77&&'7FnL'ns:jP/"3:$VWW& Nc.NFB!,`]V%%'HHH'rVI'..%.E0CP%:=Z/G2(KB1 1^L Y>9ET2$Q4/ 4."C3;=,//+ ,9,9,90,901#&&'&&'77667'>7#'26766766'667&&5680b5BQ .F)/\<@'N1R-'"1B&M.W '5AKE>1   t*0itK? &A& B+  >  f2:BI3+//++9+9/+901#&&'&&'77667'667#'26766766'667&&56'&&'77&'70b5AP* E*.]=?*M2P6I1A'M-X%5@IF?(.- ,-1    t*`? &A& B+  >  h6-! @,-#):w+A]A)9IYiy ]%/+6+0и0/01%''7>54&#"'667''676656&'6676632)1Vs@"#D5 F? 2L!*W2Y@h)5|E:0@6J? 0!M L?tE%#'?[9AIw+A]A)9IYiy ]F/+5+/и//01%''7>54&#"'667''676656&'676632&&'77&&'72UrA$"B5!D@;g1? 1K!)X2[R5~E  ;d5)F4(/+%.o7N2H,7>91A6I? / B,J>uF&#)=6.! 5/!"a +A ]A ) 9 I Y i y ]/+01%'6676654&#"'667>32, #l8*2K-;F<GTg>"#0KKG$E6 0N / @ M/;C %O .C^%a+A]A)9IYiy ]/!+01'67>54&#"'667>32^2(_'#Q "'18CX- `  :SS")/y +A ]A ) 9 I Y i y ]-//+-9-901'6676654&#"'667>325&'77&'7(3(-F9B^ 6IWL$VjO/-B%]]X#0WB&(44&)4;.(=a$*9Rc;DW - `  :T8"&: 6@s'y+A&6FVfv ]A]/'/ '9'9'901&.54>7'667>77667@0>&B3*G[1*7lV5,E3hZ( 1N]4h4b) 8GV--J6"[ 6M`5&HIJ& )V  fs$,3y+A&6FVfv ]A]/$/ $9$9$901&.54>7'667>7&&'77&'70>%B2*G[1)7kV5+E2hY) 2%=(./1. 8GV--J6"[ 6M`5&HIJ& )V6-" <.0 #a+A&6FVfv ]A]/ +01#"&5467.'66767)m4@5 a H:FvK'0EV:Q^Bn-#WYX$1O&5R6K`;( A%-4a+A&6FVfv ]A]1/"+01#"&5467.'66767&&'77&'7/56?5 ] H:FuJ%1DU*&/, ,-;Q^Bn.#VZW%2P'4R6K`;(  5/ ?,/!K"@PXY/N/и/и/Yи/Nи/Nи/и/FAF&F6FFFVFfFvFFFFFFF ]AFF]9F9=F9@9NAиA/9/I +A+01&&'#".54>7&&'&&''667''66766766766732>565&&'7<1%53+!:R1P:X Ea#Q1F(M#/7#M*  L /=-:! 1@*!$S8. ,0!!2!/D!+ -<7" aQ,8Y> $B)*=,  " "#$ 0)GA&=08 +A&6FVfv ]A] 94ܺ49//- +90901%.54676677'&&'&&'73667&&566VA [ 31k7@T * F).g?+G^RY l|Q]hv)w/ /A ]A ) 9 I Y i y ]и/w-и-/XAX&X6XFXVXfXvXXXXXXX ]AXX]5и5/ o9-o9B-o9 FиF/U-o9^-o9d-o9B/t+[(+tl01&&'#".54>3266'.'&&'#".54>7&&'766766764'%&&'326>&&#"326C (S$)#!+8'(BV106  >!,! $6 *R2ZItQ-'' !10>#  5#-(  2/($) (.H1W?A *1+9 6<@,L9'#  + "D))C]6#;!P)O87K"*,&S"'C.64:   h `G-1++Z"+A&6FVfv ]A]A""]A")"9"I"Y"i"y""""""" ]-*и*/:1-91AиA/1EиE/-IиI/P1-9ZbE/./ ++U'+1.E9:.E9I.E9P.E901%&&#"326&&'#".54>326654.#"&&7'>7'66756&'667>32!+,& 4%&V+-%)07- 0#5$SHeH+h8E1 EOZ5 L Q!+ ?2 00X+<;,1HX(%G%'aQBC//C9и9/A&6FVfv ]A]A]A)9IYiy ]99'99D/ 4+>$+$и/4 9$'и'/013267>76'>54.#"#".54>32?jL+  )! |"3K2#@^:  8?C!  4%AoQ;u^: 1Nh?+#"?DM0)6\M>C2BL'/WB&N}\ *?H N_5!IsW (X$+5+R:+A::]A:):9:I:Y:i:y::::::: ]:A&6FVfv ]A]и/A$&$6$F$V$f$v$$$$$$$ ]A$$]$9'9R,и,/:8и5=и=/:Aи:DиD/RHиH/RKиK/RUиU/G/0+8+>:+:RиR/01%32>7%.54676677&&'#".54676'''674&'&&'667JMC, ?Z 4H0N:3*od>d+5b8V+> H-8h(y)#&y7&'77&'7H1%34(rd>f)5b8U*= F-8e*.@ Z 3+LC*   +..!,-#:, + /"9Co8<A!* - +eL};88(wR !NQS&QL )$)ad`(I4b)#%?,/! =.0!pw2O]kwK?+a+-+rV+Pl+-и/-и/и/ии/!?P9-"и"/-%и-/и//?69и9/AK&K6KFKVKfKvKKKKKKK ]AKK]E?K9N?69AVV]AV)V9VIVYViVyVVVVVVV ]^и^/Aa&a6aFaVafavaaaaaaa ]Aaa]iиi/All]Al)l9lIlYlilylllllll ]Pyd +Yo+uS+^+01&&'#".54675"''67&&'&&'667%.54676677#"&5463232>7654&#"326H0$34'pd?f*5c8U,= E.8f)? Y 4z2$$44$  NC,  3!""! #;+ /":B?") !, U;  M }::8(;d* !NRQ'PL +%(ae`(I4'77'&5!(#%>$$$$W1Ǹ2/*/A**]A*)*9*I*Y*i*y******* ]2и/9A&6FVfv ]A]./# +01&&'#".5467'6732>54&'7!; -BX7/O;!<548OJ%>,%:*(=0" XYEOC=xjP.'G`9XUNAI!M(^4<CǸD/+/A++]A+)+9+I+Y+i+y+++++++ ]Dи/9A&6FVfv ]A]@/$ +01&&'#".5467'66732>54&'7'&&'77&'7; -BW80O: :529Bd(K&=,%;((?0! W.8"%/.!+,KC=xjP.'Ga8XUN !54&'7#"&54632i# #v< -BW8/O; ;538Bd'K%>,&:)'?1! W-8"[1%%11%&0%%%%C=wjP/(Fa8XUN!;!Ygn6-O9"$=NUS$>J!&LD;&77&'44wq'/<a+A]A)9IYiy ]9/+01%&&'7667#"&''32654.'7&&'7'7&&'7|))&<H 2%*,7&:!/%(+E5$~v. "$@4.-",*X< !#%V3'!2!r$:*S,&FIF 9Ij,E!'B+ *M 2# su %-9@Ha+A]A)9IYiy ]E/+01%&&'7667#"&''32654.'7&&'7'7&&'7&'77&&'7x'S ;G 1%,,7':"0''+G5%}x/""@>(-, ,)X?#,/-%-J"W4% 1!p$;)Q ,%FHF 9Hj+G 'A,8L 0" 2%$11$%2$$$$"K!W3&!2!p%:*R ,%EHF :Hk,E!'A,9K 0" )%66%(66`d/ +01%.'.#"'67>32b8xiU nOB5 3>= I|dCE@weG  55' Bu'`_B%-*// +01%.'.#"'667>32'&'77&&'7b8wiU pMD' 2>= Iz,05&. 1_CF>wdH 55&  Bu%:!%. )`^U )5ո6//A]A)9IYiy ]60и0/A&6FVfv ]A]*ܸ7 /3+!-+- 014&#"326.'.#"'67>32#"&546320 ## 28wiU pMD52>= Iz;2%$10%%2$$%%CF>vdG  55&  Bt%D&66&(66]<YeQUI+]+6Z+6и/Zи/Zи/и/Zи/6.и69и9/I@CиC/AU&U6UFUVUfUvUUUUUUU ]AUU]OIU9XI@9A]&]6]F]V]f]v]]]]]]] ]A]]]*/` +'#+Z+#.и./01&&'#".5467&&'&''67&&'&4'&''67667%.5467767732>7&M2$35)pg;a*1`6.I& bO 0/D K3:o*B W 3*LC*  %=  +1"8D*I.A*4 ?H  M2S*<(7&&'77&&'7M2M<5)qf8`*2\6.H"XB&-; D,:o*>Z 5#KC+  %//%/%=E4@0"8D*I/A*5 ?F  K1T*<(;c, "NQS%PL )#)be_'I3d(#$q5/ 6.!Y=Yeq}UJ+i+Zr+x`+7f+7и/7и/fи/fи/fи/7.и71и7:и:/JADиD/AU&U6UFUVUfUvUUUUUUU ]AUU]OJU9XJA9A``]A`)`9`I`Y`i`y``````` ]Ai&i6iFiVifiviiiiiii ]Aii]Arr]Ar)r9rIrYriryrrrrrrr ]Z*/l +cu+f+{]+Ouc901&&'#".5467&4'&''7&&'54'&''67667%.5467677#"&5463232>74&#"326K4%14*od;^+2\5.H#ZC'*< 7TZ 32%$22$%2}LC-  f !! %= +0"8D*I/A*5 ?F&G2  K1T*<(;c, "NQS%PL3#)be_'I3S&66&%66$(#$&&%% BPQF+?N+?и/?и/Nи/Nи/Nи/N&и&/N)и)/N:ܺ,:9?-и-/?0и?6иNCиC/AF&F6FFFVFfFvFFFFFFF ]AFF],/I +C+& +Cи/ #и#/&$и$/ 6и6/7и7/01&&'#".54>75"''67&55''74&5&&'667667%32>746-_;'3<3 $?W59\& -`8Eo,_WA !. A/  M } K D1N+#"%vr E +A&6FVfv ]A]1//+1919(19,1951901326766&&''67&&'#"&54>7667'2>766766'_K`! =(7#EI08f=.X'6+H\3"7R*#"IF:B7!8a:V.M%I8!!D7Gt53]Bi*IK<,G6"8`4 T (0N!- * .a7;!c U]x+B*+*и/A&6FVfv ]A]AB&B6BFBVBfBvBBBBBBB ]ABB]B8и8/89*'и'/*1и*4и4/789B;и;/EX7/7 >YM+ +1*+$+ 9'$9*-и-/1.и./1;и;/*BиB/01%&&#"326'.7#"&54>3266'&''7&&'&&'66732>76677&&'7K   $&:OY&$K; 2>")!*E "D*[':?( 3A//( l<$&N '!+*46R17$E: E?&>,(>+: $4% C?a#.u8%,:&/=*?%wp T!:+&+:9A&6FVfv ]A]:9:9A&&]A&)&9&I&Y&i&y&&&&&&& ]2:9F:9VN/ / 5+ N9 N9 N92 N9F N901%.'326>'>54&'&&'#".54>7&&'76676676'J  '$"76H# '?O),'@.y<&+)5"!@#+  + 5 .]; N '*-9V'++1Q !#$(54854&'7(BU-8R4 A )  *: 9&e &52J 'A >M 9* 4'Th.K3$AY4%  !   -K/>  *"E/L*I. T  &G4'5$8''L, Iy+A]A)9IYiy ]@/%/E+ +,%@901#"&'732>54&#"&&'&&''667667&&'7667&&'76632,2< )3  = 'C'%G%CD)"02" (B *B(,+&&5+,;#< %1* !!WJ;_?  1Q!#L/&=6I#7l{Iy+A]A)9IYiy ]@/%/E+ +,%@901#"&'732>54&#"&&'&&''667667&&'7667&&'76632{(?K") # 0&8' ! V 0Q/.\.VU2*@D,$1T)2R/%:72!.!B5!f8H+R+#93#'+*i_'Jv(X ;c)!+`:$0J'G^*E;IK-$+A+<+$9$9и/$A-&-6-F-V-f-v------- ]A--]2и2/A6и6/A9и9/A<<]A<)<9<I<Y<i<y<<<<<<< ]ADиD/GA95//5959-59A59G5901'667&'766754'.5467>7&&'4.'66md(>*='  &C6) 0   J3AI%C^TF ) C;[v*B'6%-" -F ,'7 ,9B 91:<6=~HDFA#G:*5, cE(" o-5'ZHAQ1(+G+B+ (9(9A]A)9IYiy ]и/("A1&161F1V1f1v1111111 ]A11]6иG:и:/G=ABB]AB)B9BIBYBiByBBBBBBB ]GJиJ/MG99// 9999199G99M9901'667&&'766754&'.5467>7&&'4.'>H!@^>2)Q#5*45 % .RE3 ? _FUa. X74.'&&'667%326764-]<'2;2">X5W?a0/aAF8PZ,."*,H" * /$0%8vhS , R<M <<D1N+""%<(01//A]A)9IYiy ]1и/9!9-/$+01%''7>54&#"'66'6&'6632&&'71VrA""C6!DC:f4?X YEX'/' >Y9901&&54676657%'667>54&'7H JJ,(>,I& A{49:8q,1C(Ia]=|31 '_ei09sEH{! ?@/!/@и/A&6FVfv ]A]A!!]A!)!9!I!Y!i!y!!!!!!! ]!  9/ 98 9A4/ ++;&+8&;901&&#"3267#".54>326654.#"'667'676632V@>. /L^/=3" )9Y# %7#$PPKFZH"W=.'`4:B'L;$ 2?#*7aI+ 4$):>(F ;-3J.2OqX 4.Q 7RfN2+F+A]A)9IYiy ] F9 /A ]A ) 9 I Y i y ]A2&262F2V2f2v2222222 ]A22]2и/2и/к!29(и(/+и+/.и./2/и//;29 C/EX./. >YI+@+.9!.92.9;.9N.901%#".546764&&#"&47'667'6674676&'667>3232>7*X2$  9EP(*:6N ,?$M*U  ! HTS4?   RIJ!*LL  OGfI,i7E 0@lM (!,@02$3:=7%::FN2"/ & +A ]A ) 9 I Y i y ]/EX/ >Y" +999 "901%'6676654&#"'667'676632 8BY .NE$QQJDYH$X<.&`49E&K;$dTE,I[3K.4OpY5,O 6P*CD//A]A)9IYiy ]D-и-/1и/1и/-и/-к -19-'и'/-*и*/:-19E-//? +-9 ?91-9:-901%'654.#"&&5'667'6674656&'667>32/?##.8e2>++@%4<"A 8EK!'>+2M;* <+3*0-S:#pY& !4|Z :  "5' '*2*(4HWCD//A]A)9IYiy ]Dи/и/и/"и"/)и-и-/.и./1и1/:9E-//? +-9" ?91-9:-901%'654.#".67'>7'66756&'667>32W!:N-.&8%E~8L 3:.(#*A&L*U  "GQR%2M5=bJ5P7>4":8hKPXT1X!0FO[6M P!-@1 3#2:3*)CYP So>+ +++>9A&6FVfv ]A]A&6FVfv ]A](>9A++]A+)+9+I+Y+i+y+++++++ ]F 9J>9Q 9UM/+9+JF+%+Q0+03и3/01326766&#"327#"&54>326654.#"##".54>7667'667!6(##,HZ.7J ',?5ZC'F8-  )LmE8g& _|.>([DF>$& !W!8`E(7<()4*@! :-_B-'1,cZH.L) F)*bAsI g&+.+S6+A]A)9IYiy ]A&6FVfv ]A]и/#&S91.9A66]A6)696I6Y6i6y6666666 ]@.9M&S9 ///G/d+1X+D9+++d#X19= G9@9D9M9D99P^d901%4&#"66&&#"&&#"'667>7&&54>322>54&#"'667'6676632#"&'6632.! M6 S0#A E3,7< %-4*9)HDCIF>f#'`3@ #*328I3D$$ =!&3W(8m-0?Q%16FD9"*; !  ''8$04QI<*N OD #.!OK"F8$ 1#.'N;)N +A & 6 F V f v ]A ] и/; 97/2,+H%+,/и//20и0/2;и;/,BиB/E%H901#4&'67&'.54>7&&#"'667#"''7667676676632667)1Y2O3(1Sk;C~4#4H) />Y*F#K%87G  Q6D'[5  )/&F5O &&9&''& [ (<86%4WS%2ZD $0% C !7' 82$ L62EX/ >Y1+(+%(901%#"&'.'&&#"'667667667663232>7L)07 #48><N/[+"; P04.'( *!! i%B0+ 432 (KlB(RYEE#+aic! .i/,81+и/ /+01'66766553667!5!2BQ-&3 -'. FFQD#E>17TB4/4#VNHOT?9<1+и//+01'>7>553667!5!<ATe8eO9)  URe%O@+XOB6E!% (7K0\b$hII!+//901#'667667 P/F<`* k>&R'l]*77 @P6+/EX/ >Y9 901#'667667&k?RJv7)#He1c45DE Nb Q  ++ + 9/ +  и ܸ 01'665!#5353&FgB0uCHhx[$<0hh!; ++ +/ + 01'>5!#353!3\P7IrQ+XSX,ȕn-NYy`  1 + ++ и01%!53#5!#3 =>>.E! 1 + ++ и01%!5!#5!#!E^$(%.J`II E + и/++и01#'655'667#5!533IN25EB*K8 DQ8(?Xv$7&Q;~~<E + и/++и01#'65'>7!5!533<-G2ABQO4.\UO!QU%. P/i,GAP]1H##y$//$и/ 9 и"и"/%!/ //+ !9и01'67>5#'665#5353#  XG=<  )H_89uzS(adcVC!NXmw3Ryg*BF։Hk}#+2}3//3и/(и(/ (9 и"0/ //+ 09и01'67>5#'665#5353%&&'77&&'7 WF;;  (G_7,-(// +901'>7!'>73(%iT:Ly^.\.:-TH;F [v+F!gN7[#=LV[+(*#e '$// +$901'>7!'>73&&'77&'7K9Lw_,[.<-SJ;H (.("1/ܷWE!gN7\">LUZ+&,"0,:(+Z' +//+ 01#'665#'>7!Z(Fa98|p&P'>+QC3J `n,BL9\$9 V^b-%7*W (' +%//+ 01#'665#'>7!7&'77&&'7 (Fa98|q'H'>+QC5G  +-.&,_l,AM4V#9!V^b-$=,6<,, 3, ( -+/+ +01%#5!5!!5!Y4=.KdH` 1+//+ +01#5!5!!5!7&&'77&&'7Z7=@*/(*..KcH"0,0,U/ / и / и/ и и и///+9 иии01#'>7##5#53533533U~t=7##5#535335337&&'77&&'7 |u=;T5QQO &-/'+T:%arBFP 2/ 3/ E/ / 901&&'7%'>7%&'75 K'']$1E1I08J$#K"(@-:#":~r_RTl{:,E1.3 H"* /'/ '901&&'7%'>7%&&'7&'77&&'71 J( &\&0E2I2@&$$K!-,/(,'@/9#";}q_QSl{;+#<06 !>,. 4/ 1F/+01%.''>7!5!,17KO4JnSsL3&>z+B@:T*G`wAI\*Y2.x?Z'$//+01%.''>7!5!&'77&&'7.16LO3InToN3&=z+"1/*)- C@;U*Fav@I[*X3.vA9,,1,D'+ +#и %$/+01'66767#".'.55'753%D*294+K'0;]:~7 /3- U[!CB=5&b?R- X&.$I$Nf)08+ !+!%и '5/+01'66767#".'.55'753%7&'77&&'7*395-K  ;^:}7 .2, WXV..*'-!CB=5'a?Q X'."H$N29,, 1-/t //901&&'7%'>70?"6&%! UmJDZeCw5j'',05^8B5PY #/!/ !901&&'7%'>7&&'77&'7>!5%%! QjKBYaD*0(36'o5i''+14`7B6Q0+;(9))$/ /)+ $901&&''67&&'7667!'>73)#[<0J(HH:5+=#1G,]-;,UH<GMK 5G6!Gf&G@ .6.>sE7[#=LUZ*'+"d)085/ /)+ 5901&&''67&&'7667!'>737&'77&&'7!\<.K&FH:3):$0F.X.=,TI;G #1/)*.MJ 5F5!Eg%F@ .5.>sD7Z#=LVY+(*$<',1,LI +  и//++ и 01!'665!5!5'667!L$B]:8unAAb)c%*^;B@{oa%B?rF 9+#D  V"*I +  и//++ и 01!'665!5!5'667!7&'77&&'7%B\8;un;@c*d#(_9 2/**-C@{pa%BAsF :+"C  D9*+/- // 901&&'7%'>7&&'7 2'38HsX?9 $K'M'X4'n~A$J&K(Jw/ / 901&&'7%'>7&&'7 (<4"^sO@^lN ?&p0_4%b4\4C/N1\2&`5K#+ /(/ (901&&'7%'>7&&'7%&&'77&&'7 ';3 VmO@]dG A%b*0)(-j0_4&b3X2C/M1\1'_41* 0,@- +/++ 01!'>5!5!'!5!@7V>65!5!'!5!&&'77&&'77V=7=N,xW*/*).qNte,C#WfuAEH61+/+K 1+//9901%&&'#3u(oBQQD1'EP?* 1+//9901%&&'#3'&'77&&'7=)mBRRB1#20*)-&GP@)>&*/,ML + и/EX/ >Y+ и01!'>5!5!53!M5W?6>O,8P+Uzf,@$\n}FE01;++01%!5!!5!1{]=0SfR { /+01&&''667&&'7667!5! P94NEF.FO4MB-K$"$U5+@q7QM.Q&F&T0Or*F#oL%;87&BzFDN7+и/+01#'>7!53533&&'7bCPJ~A$T{`oPv"W+3`)2m5F*5HAVd1E9*L.B+  //01'>7 fNA\wWfZ´5H2N?a  //01'>7&&'70=I'@+G9+\B7Cm#2sum+>*`hj4Sba&Vt`9 # //01'>7&&'7&&'77&&'7/>H'@+G9,5Fl$()/)'-1stk*<*afj3Waa%Vr1,1.c8 3Ǹ4//A]A)9IYiy ]4*и*/A&6FVfv ]A] ܸ5//+ %+014&#"326'>7&&'7#".54>32j" "$/>G(?*G9+\B7Bm#I     V%%$$X0ssl*=(afk4Vaa$Us""!!~$#+/+01#".'.536672>7JKF .2- VrI,(ajo6$0T^g0'(.S4D1(! -  <$+2#+0/+01#".'.536672>7&&'77&&'7JIG /1- UrH,(aio6%1T_d/ &6&+(;#,&0R4C1(# -  1!<1$:< 0@(+9+1+A]A)9IYiy ] и /(к019A99]A9)999I9Y9i9y9999999 ]1B++<+ 6+014&#"326#".'.536672>7#"&54632 !!!!tJIG /1- UrI+(`io6%1S^e/$33$  $$%%f'/S2C0("-  [ 5%&7""X/ +01'>7!5!"WvX3PnM gu1G"mQF}k%//+ и 01'>7!5!3&&'77&&'7VuW3OnMr).*'. gv0F"mQE2,0*{l &߸'//A]A)9IYiy ]'!и!/A&6FVfv ]A]ܸ(/$+ ++и/014&#"326'>7!5!7#"&54632R#""#VuW4PnL2"%21&"2''%%fu1G"mPEX%77%&66lQ{ //01%.'&&'"'6676672I1ssl+  &S0:Bc* /mw|?QMUT$  +^=9=c1 'OOL"lL{C") '//01%.'&&'"'6676672&&'77&&'7I1ssl+ &S0:Bc* ^-2)-9.LMST$ ,\=8>c0 LE.&.8lK{V &405//A]A)9IYiy ]5-и-/A&6FVfv ]A]'ܸ и /'6 /EX/>Y0+*A**]A*(*8*H*X*h*x******* ] 014&#"326.'&'"'667632#"&54632,"$$"1ssl+ &S0:Bc* /mw|?43#$22$%%''lMTR$+]<8>b0 &OOJ"h%66%'6!W#E +и /++и01'667%!'65!5!53!&&'7bM/<0M%:+11SF-2,S '?~49-ov0)*p?i#+2I +и //++ии/01'667%!'65!5!53!&&'7&&'77&'7PL.>0L$;*1/TF+/,U F04''42#>39-o;mz"0MGL?v0)*p?0%7"'h %-;+4+.+A]A)9IYiy ]!и#A44]A4)494I4Y4i4y4444444 ].=+9+ 1+!+и!$и$/014&#"326'667%!'65!5!53!&&'7#"&54>32N""""L/>1MCR2.RF+1*V#2##4#2%%##?~38.ov0(*p?"'55'!5?%/+01&&'7667!5!?.`(H/yK#D'R|+BZI 18R.4. 5RC# ! //01.'7'&&'7'.'7$_ed+,ghd)5N!MMG>DI#LKD05-" B (3&A>!"B!i{ +//+ и /901&'#'2>7667&&'7 DM&&G?1V!xR}f/*70*&3 Z:A)x/C +@GS1!} // 901&&''667&&'7667!Q7&7D- SR;_L(D%%K/4IS@H$;C:$^.M(Y 13-!K:Cx$S+и"++$+и$и!01!67#".'.55#535#5!#!C1oI !W%+-& P1 R&.HEE // 901'667'7'7%G,) 1NEI @  k8f&' J:;u%<%;L" /EX/ >Y 901'>7'7'7%L\82"^X]'T+?D12%,5"E7-H/G  ++ 01!5!!5!3ht!;)=*a3  ++ 01%!5!!5!3a&'*JwHA ?+и /+  + +01#5!5!5!5!5!5!MqZ.#>>9 E ?+и /+  + +01#5!5!5!5!5!5! [0K:!/IHH / +  +01'667!5!'!5!IbxF1+%{mDuc%DAЁHG 3/ /и/ ///01%#3'>53sSSTw<>V5SU:&er|>h 9// и ///901'>553'3>7V+G540>" Q)N\j8>S3RC6Gyj^,8$T_k:4f\Q=h"HKR-r  +// 901'36670sLLWN1_SHLM4\5 O //и  и /и //+ +01%!#5!#![\5lw..@ C/ / и / 9 / + 01'665!#5;\?0t`Cpd'3?✳9/ /и/ / +01'>5!#%LuO6Ig@^Rڧ}2F(p`2W// и /иии//++ии и ии/01%5###5!535#5!533#32Y^b}YHHIJXXB+EX/>Y +и01!5!3>7!5!!X8'X%:/'T1^IG69-39#CDb1t/+  +01'667!5!667!5!Jq_/n?[ C/rx/I3mH*J2E:v //901'>7&&'7:#Yfnok.1I2CT*(/.+(UTRG9PZt~;$*R 1 &*qv#? ++ + // + 01'>5!#353%&&'77&&'72[N7GsQ+WSXH.0'-1)ȕn-NYy_- (- & e // и/ 9и/ ++и01#'67>5#'665#5353  I7./ oa+_XD4{t^: CUa-B77n; '+//+01#'665#'>7!dj1cV@#3"?3):bYO0>ǂ.E+AIM&+ ' &//EX / >Y+01%#'!#3zGQQGJn //A]A)9IYiy ]и/ и EX/ >Y++01%4&##32>7#!!!32_W%C1B"A\9qsHG"6(4L1:b (+# +#A ]A ) 9 I Y i y ]и&9EX/ >Y++ &901%4&##32>4##32>5#!!2_W%C2(:']"A\9$.Q=#?:CPHG"6o#.4L1)D4:U _ (+/EX/ >Y01!#!qChpVS d!//!и/"EX/ >Y +  й 01%!#&&#!"#7>5!3 &, *)r!( )8G))$V8i`T";G!1 Si~G"' D + EX/ >Y ++01!!!!!!&oy':: r +  ++ EX/ >Y + ++ и01!!!!!!#53#53'pxKKKK(::LLLl{1++,+и/и/и/!EX/ >Y1++!+и и1 и!и&и'01"&'####52>54.#52333>3"3{F>F7U89T8?gJ,G>G-Kg?8T89T87T:.TsEEtS.:+T|P54.##532>54&#"#&>32(BV.3T=!=,=%%A0,;"441'JL$8&=7O24O6B0?M5P5YEX / >Y9901##353BcBBB(+<< //и/к9 и////EX/ >YEX / >Y+9901##353'#"&'73267BcBBBH55H9--9(+<YEX / >Y901##33P}BB}U(r~|F//и/ /EX/ >Y01#!'>5!B %I>"7@(Nq]$.!QeyI: // и/ 9///EX/ >YEX / >Y99 901###33:BBBbb(llr k // и/и и //EX/ >YEX / >Y +01#!#3!3BBBsB(\4*'˸(//A]A)9IYiy ](и/ A & 6 F V f v ]A ])+#+014.#"32>7#".54>32#?[79Z?"!?[97[?#B/SqCDrR..RrDCqS/=EtS..StEEtS..TsEZ_01_ZZ^11^J//и/ //EX/ >Y01#!#!BtB(p//A]A)9IYiy ]и/и /EX/ >Y +014&##3267###!2UEEUBp^B.L6JHHJak4L)o +A&6FVfv ]A]$++01%#".54>32#.#"32>7,Hc=NwO((OvN2]J1B &6C&D_<;_D0L6!@hJ(9dOOd9;V8)A,6Yq:;pY6"Yи01###5!Bhp:;EX/ >YEX/ >Y + и/01#"&'532>77351 #A+05_O& 0$+ ++A&6FVfv ]A]A]A)9IYiy ]и/ и/ )и+и2/EX*/* >Y01%4.'>53#5.54>753.N9 8N. :P//O: >+Jf:>:dJ**Id:>:fK+ 6I--I6!-J6 ^ 6J-9]C)``)D\88\D)^^)C] A//EX/ >YEX / >Y9901##33OONN(7n\#V"_/ /и/  /EX/ >YEX / >Y +01#&&#!3!33", *)/B|B)$;Gp"'(m//и/9 и /EX / >YEX/ >Y+01##".5332>53=!,33G+=1#$:)>(&5I-L!9*/A&-' /и/A]A]A]AO]AP]Aq AO ]A ]A ]A ]A qAP ] EX/ >YEX/ >YEX / >Y + 01!33333'BBB(ppVU/и/ A ]A ]A ]A ]A qAP ]   A ]A ]A ]A ]AP ]A q/EX/ >YEX / >YEX/ >Y + и01#&&#!333333U, *)BBB)$;Gpp"'> //A]A)9IYiy ]и/ и EX/ >Y++01%4&##32>7###5332_W%C1B"A\9sHG"6(4L1:b0 +  ++A ]A ) 9 I Y i y ]иEX/ >YEX/ >Y++01%4&##32>57##332#3W_W%C1C"A\9BsCCHG"6'4L1bf / /и/A ]A ) 9 I Y i y ]  и EX/ >Y++01%4&##32>57#!332_W%C1C"A\9BsHG"6'4L1b#(?+и/ +$++$01#"&'332>7!5!.#"#>32#'NvN{B vaA\;p:Y?&F;* C 5N^2NuN'=Od9`l1Ri8:6dM/%:)7O49dY, !+ %++A]A)9IYiy ]%и/ #и.EX"/" >Y+(+%+014.#"32>7#".'##3366322F))F22F))F2B$C^:8\C&>>o:^C$=EtS..StEEtS..TsEZ^1.YU1^ *$+*+A&6FVfv ]A] и!$*9*, //EX)/) >Y +)! 901#"3##"#>7>7&&54>3!KL)75B/6  I  .$AG":L+:OMB!3#=$;J%B/424,_F0H. ?+8'+'A&6FVfv ]A]'и/.9./- +<+3*+3-01%32>5#"&5#".54>76654&#"#>323267g9X:>4%?-v  *]M#=.*Ic:&&I:5M> 6H+%C2   ) --*=&0*.;"5$2=& :19?,A+&@256//A]A)9IYiy ]6и/ A & 6 F V f v ]A ])и)//9(/+1+/1901%4.#"32>7#".54>7>7632.A''A..A''A.=!=X66X=!/>#%;0*/W720<6X=!1Q: :Q11Q: :Q1Y++#901%4&##3264.##32>5#!!2BVKN 1'(1 XbsdX+55En00- QF8G*>A,(+/EX/>Y01!#!=2VB `!//!и/EX/>Y +  й 01%!#&&#!"#7>55!3", *)!( )5>! )$8odT;G!1 SkGSE"' (e)//)и/  ии/(и/+#+  +01%.#"32>73#".54>32*;%%9)*<'!6(=n[2S; 7T873#".54>32#53#53*;%%9)*<'!6(=n[2S; 7T8Y5++%+ ии5и% и*и+01".'##5##52>54.#523533>3"3;4U=#<==#=U4'A/.A(2S<%===%=R3'A/.A(7%C]88]C%5 :Q11Q: 5#?X55X?#5 :Q11Q:  >  +7"+7A""]A")"9"I"Y"i"y""""""" ]- 9-/,: 9+2'++:901%#".7332>54.##532>54.#"#4>32(BU-,R?&90< !@2,;"442%*65.!9#:L)+M9!-9/"k+=&*G6)4 + # 9  %1#-B,#6&,< , w // и/к9 //EX/>YEX / >Y9901##353===9=(y<=< //и/к9 ии/////EX/>YEX / >Y+9901##353'#"&'73267===9=KH55H9--9(y<=YEX / >Y901##3%3Ie==T(\F//и/ /EX/>Y01#!'>55!= ?7#-7 (1O~hV'(D[zSg+ // и/ 9///EX/>YEX / >Y99 901###33+=>=SS(UUD c // и/и //EX/>YEX / >Y +01#5!#3!53===_=('Ǹ(//A]A)9IYiy ](и/ A & 6 F V f v ]A ]+#+01%4.#"32>7#".54>32.A''A..A''A.=!=X66X=!!=X66X=!1Q: :Q11Q: :Q1Y01#!#!==(2 9*]+//A]A)9IYiy ]+ и / и"к# 9EX!/!>YEX/>Y!A]A(8HXhx ]A'7GWgw ]A]#!9&01%4.#"32>7#".'#36632+=%*B-*C/%=+= ;S38." ==[D3S; -P<#"32#&&#"32>7lX8W;;V8(G6$= N;+@+-?)3'[c(Hd=:dI*/C*8H#Yи01###5!>269[EX/>YEX/>YEX/>YEX/>Y к901#"&'532>77351 A,/53OC2 F<4++!+A&6FVfv ]A]A]A)9IYiy ])и+и<и>к?4!9!H*/EX=/= >Y&+9+ ик)&9&/к?999B01&&#"3267%4.#"32>7#"&'#5#".54>32536632 'A..A' .A'  'A.=!=X6  = 6X=!!=X6 =  6X=! :Q11Q: 1Q: [ :Q1YEX / >Y9901#'#'373FFII(V_/ /и/  /EX/>YEX / >Y +01#&&#!3!33* ,)u=@=)$;G2E"'-s//и/9 и //EX / >YEX/>Y+ 901#5#".55332>5538!,33E*81#$:)9(&5I-!9*/A&" f++ +EX/>YEX/>YEX / >Y + 01!33333"===(22VPz+  + +/EX/>YEX / >YEX/>Y + и01#&&#!333333P, *)===)$;G22E"'  / /и/A ]A ) 9 I Y i y ]  и EX/>Y++01%4.##32657###5332 7+KC>0N9〼9N0n$ -3'9%6%8 + ++A]A)9IYiy ]иEX/>YEX/>Y++01%4.##3267##332#39 7+KC=0N9=9M0==n$ -4'9%%8 / /и/A ]A ) 9 I Y i y ]  EX/>Y++01%4.##32657##332 7+KA>.N9=o[n$ -3'9%J$C+ + ++  01%#"&'33267!5!.#"#>32:V8Xp= V9MW*;';Q >%7H(8V:=dH(^[?EmX6'E3C8*A-*Id8/!"+ &++A]A)9IYiy ]&и/!$и$/1EX#/#>Y+++&+01%4.#"32>53#".'##33>32*>''>+*>''>*>9U64Q9 >=!9P36U:1Q: :Q11Q: :Q1Y + 901%5#"3#5#"#66767&&54633?A@?=94 F [3AbU+33*2<B"<'j =:OAVt15 + 0%/"/+'.+01''&7>76&#!"5543!2#"5433232##:g\  U]6  I/)) {V1 # ,qM # #~" bp-CS"+"1и7иAиE/4/+&+9@+01''&676676#!"5543!2!2##"554&#!"&5546%#"5433232## ĺ K0"/) $ /)) N ! Bi # "#)$4"b0FYG///$иG5и5/<2и2//H!/@1++&-+01"''&67>76#!"5543!2#"5433232##!"&5543323!2 ˱ I{]<  K0")) /)  J! R`l9 # ""#)$b|3IJ/7/Jи/и/ и /7H и /H=иHK:/+1*+ +?F+01#!"&55463!2#!"3!2''&676676#!"5543!2#"5433232##6 , , 5  ĺ K0")) v** !  N ! Bi # "9"bq1J`a//aи/*и/и/".и./Nи"Tи"^и"bQ/.+HA+ %+V]++01#!"&55463!26554&#!"&55463!2#!"3!2''&67$76#!"5543!2#"5433232##5  /#-+ #-o 8abC$ K0#)) #*K*9$K*= BpdY* !  # "c"bpB\r4+,+w`+!и!/w}w/ /z/c/ZS+/+ho+t+и/t%и :7и7/:=и=/ {и{/01###.55463326554&##"&5546332##"332676676''&676676#!"5543!2#"5433232##32##"54&##"&5546  'N!, !, '% 2 6`d K0#)) /$  qN*9$I*: Al^V+ ! F] # "S".#) #bq-mǻ_<+WD++q+<LиL/wиt/ ++$+AZ+y+QH+Qи/ 3и3/ 6иbиb/gиg/H01%46332##".5''&676676#!"5543!2##".55463326554&##"&5546332##"332677>76#"5433232##"3326554&# ,, 6`d K0#96 %K!, !,D" )))  t **Al^V+ ! F] # " N*9$I*<  p"k   b|#;QR//Rи/и/и/  и /и/?и Eи Oи SB/ +92++GN+017463!2#!".57"3!26554&#''&676676#!"5543!2#"5433232## , ,]]  ĺ K0")) **   N ! Bi # ";"b|4>TU//Uи/6и/6 ии/=иBиHиRиVE/9+2++5+JQ+01".546332!546332#&''&676676#!"5543!23!26557#"5433232##0,, ,ĺ  K0"  7)) jh&'N! Ck # "sk k"bh4Ukuͻm++JD+m ADD]AD)D9DIDYDiDyDDDDDDD ]8DJ9YDJ9Y/j_иtиjw\/p+2++l+ah+01".5463323546332#&''&676676#!"5543!2&&'''&766'462#"5433232##332655'' ,gĺ  K0"S=NI6  RQ. UAn))   hc&9N! Ck # "&N00S* :e  *?f$ "f fbwCY5XG+XMиX[J/+OV+01"''&67>76#!"5543!2'.'''&67>7632#"5433232## ˱ I{]<  K0"2c 7cSC?Rb8CpS0 0 )) J! R`l9 # " Lb ".;D$$E<0 % '4632''&676676#!"5543!2#"5433232##d?YM11NYA b[ 0 "- .#/ ZU ĺ K0")) ![56Y U34T#$ 0e  @9/ :HR)   P' (N ! Bi # "H"bp'@V +UD+DA&6FVfv ]A]UJиUXG/#+>7++LS+AиA/01%#".54>324.#"32>&''&676676#!"5543!2#"5433232##,.Oi32''&676676#!"5543!2#"5433232##4.#"32>7!"&55463!2'#"&5546332%=R--S>%%>R--R>% 6`d K0#)) m*9 9**9 9* L -/  // .Am`V+ ! F_ # "Q"   l]td6g++ иии8 //4,++0134332##"5###"54332''&7>76&##"55463!2&&&&  CnP/  -%kOG}k +gtD $&bpd3IJ/>/Jи/и>и/>9и> и />5и5/9K/;/1*+5B++01354332##"55###"54332''&676676##"5543!2!2##"554&#!"&5546&&&& 0" /)  ]B ! 6j # "#)$b6L[B;+++и иN/F7+4-++0134332##"5###"54332&''&676676##"5543!2!"&5543323!2&&&& 4UyQ  0"|/)  w=ZdK{iY'  @u # "$)$`qy1Mf*+BG+94+4и/и/9!иB2и4?и9h6/.+d]+ %+3@++01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332''&67>76##"5543!2y /#-  #-O &&&&?jP4 0!#*F*4$F*8 $µN  COX. # "b|d#?Wy+49++&++ и&и/4$и&1и+Y(/ +UN++%2+01%463!2#!".57"3!26554&#354332##"55###"54332''&676676##"5543!2 , ,=]  d&&&& 0"x**   XB ! 6j # "b|d7OYQ+,1+#+Q ии/#и,и)иXиX/#[ /T+MF+P+*+01".546332!546332#354332##"55###"54332''&676676##"5543!23!2655[,, ,u&&&& 0"  jh&(RB ! 6j # "tf fbw6__`//`и/и иFиF/a/4-++01354332##"5###"54332&''&676676##"5543!2'.'''&67>7632&&&& 4UyQ  0"N2c 7cSC?Rb8CpS0 0 cK{iY'  @u # " Lb ".;D$$E<0 % K+01&&''&&'''&6766'4327>'4632354332##"55###"54332''&676676##"5543!2?YM11NYA b[ 0 "- .#/ ZU &&&& 0"![56Y U34T#$ 0e  @9/ :HR)   P'  XB ! 6j # "bpj'C[û+B++94+9 и /A&6FVfv ]A]4и/B1и4?и9]6/+YR+#+3@+0174>32#".732>54.#"%#"54332354332##"55#''&676676##"5543!2.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#M&&&& 0"'+C/.C,,E..E)--..B ! 6j # "Vt;S + иܸи//92+ ++01#"5433232##32##''&7>76&#!"5543!2))  :g\  U]6  I/~  V1 # ,qM # #bp7Mq6+6!и1ܸ&и6+и6<иBи6O/?/+#*+9F+-4+01''&676676#!"5543!2#"5433232##32##!2##"554&#!"&5546 ĺ K0"))  /) $ N ! Bi # "=!x!i7#)$b:PgF?+9+9$и4ܸ)и9.и9R!/J;++&-+07+01"''&67>76#!"5543!2#"5433232##32##!"&5543323!2 ˱ I{]<  K0"))  /)  J! R`l9 # "!!#)$bq1Jj*+"+и/Nи"TиdܸYи"^и"hи"lQ/.+HA+ %+V]++`g+01#!"&55463!26554&#!"&55463!2#!"3!2''&67$76#!"5543!2#"5433232##32##5  /#-+ #-o 8abC$ K0#))  #*K*9$K*= BpdY* !  # "U!i!gbwCcS]G+GbMи]RиbWи]eJ/+OV+Y`+01"''&67>76#!"5543!2'.'''&67>7632#"5433232##32## ˱ I{]<  K0"2c 7cSC?Rb8CpS0 0 ))  J! R`l9 # " Lb ".;D$$E<0 % 7+LS++V]+AиA/01%#".54>324.#"32>&''&676676#!"5543!2#"5433232##32##,.Oi76&##"55463!235#&&&&  CnP/  -%WG}k +gtD $&|b6LPuB;+++и иMиNиR/F7+4-+O+N+01354332##"55###"54332&''&676676##"5543!2!"&5543323!235#&&&& 4UyQ  0"|/)  ;wȪZdK{iY'  @u # "$)$`qy1Mfj*+BG+94+4и/и/9!иB2и4?иBgи4hи9l6/EXg/g>Y.+d]+ %+3i++g@01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332''&67>76##"5543!235#y /#-  #-O &&&&?jP4 0!#*F*4$F*8 ۅ*viN  COX. # "emtC/A+ и1 //-&+ +014332##"5#"5543'''&7>76&#!"5543!2)) :g\  U]6  Q/Z $ V1 # ,qM # #}pC-AS"+".и4и":иC/1/+&+.;+01''&676676#!"5543!2!2##"554&#!"&554654332##"55#"5543;ĺ K0"/) $ )) N ! Bi # " #)$D $ }b-C]D// &иD2и2/9/и// E/=.++'+01''&67>76#!"5543!24332##"5#"5543!"&5543323!2;˱I{]<  K0")) " /)  J ! Q`l9 # "+ $ #)$}|Y3G{H/5/Hи/и/ и /5: и5@и:I7/+1*+ +4A+01#!"&55463!2#!"3!2''&676676#!"5543!254332##"55#"5543Y , , 5  ĺ K0")) v** !  N ! Bi # "  $ |qX1K_`//`и/*и/и/".и./Lи"RиXи"aO/.+IB+ %+LY++01#!"&55463!26554&#!"&55463!2#!"3!2''&676676#!"5543!254332##"55#"5543X  /#-+ #-o 7`d K0#)) #*K*9$K*= @pdZ+ ! Eb # "0 $ |qD-m_<+WD++u+u и /<LиL/nиn/zиz/~и~/uq/ ++$+AZ+n{+QH+Qи/ 3и3/ 6иbиb/gиg/H01%46332##".5''&676676#!"5543!2##".55463326554&##"&5546332##"332677>7654332##"55#"5543"3326554&##,, 8ab K0#:6 %K!, !,D" )) H t **Cn_U* ! Cb # " N*9$I*<  g. $   }|C#;OP//Pи/и/и/  и /и/<и BиHи Q?/ +92++<I+01%463!2#!".57"3!26554&#''&676676#!"5543!254332##"55#"5543 , ,]]  ĺ K0")) **   N ! Bi # "  $ }|C5?ST//Tи/7и/7 ии/>и@иFиLиUC/:+3,+6+@M+01".546332!546332#''&676676#!"5543!23!265554332##"55#"5543R,, , 9aa K0"  )) jh&'BtfZ) ! Ed # "tl l/ $ {wBV5ID+DOиIXF/+CP+01''&67>76#!"5543!2'.'''&67>76324332##"55#"55439˱I{]<  K0"2c 7cSC?Rb8CpS0 0 )) J ! Q`l9 # " Lb ".;D$$E<0 % '4632''&676676#!"5543!254332##"55#"5543?YM11NYA b[ 0 "- .#/ ZU ĺ K0")) ![56Y U34T#$ 0e  @9/ :HR)   P' (N ! Bi # " $ }pK'?ST/A/T и /Aи/ A&6FVfv ]A]AFALиFUC/#+=6++@M+01%#".54>324.#"32>''&676676#!"5543!254332##"55#"5543K.Oi9+9Dи>Q;/+5.+ +8E+и иLи N01!"&5546335#"&55463!2##32''&676676#!"5543!254332##"55#"554335#k n Ajĺ K0")) t ! !  N ! Bi # " $ }Ue/CWesN +D+ADD]AD)D9DIDYDiDyDDDDDDD ]1D91/6132''&67>76#!"5543!254332##"55#"55434.#"32>7!"&55463!2'#"&5546332%=R--S>%%>R--R>% 4`fVW2  K0#)) *9 9**9 9* L -/  // .>i]V, ! #HLS/ # "* $    ldtd,8M+3 +и3-3:5///*"++0134332##"5#''&67>76&##"55463!2##"54332&&U CmP0   -%u&&s/V)*ᬂ  *guD $&~b)?K]5.+ +@E+.@9и@MH/9*+' ++0134332##"5#&''&676676##"5543!2!"&5543323!2'##"54332!&&+^  0"|/)  &&VG@i-  @u # "$)$`qy1\h*+<7+]b+bи/и/]!к5]97Bи]je/.+ZS+ %+6C++01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55#''&67>76##"5543!2##"54332y /#-  #-O s&&X?jP4 0!s&&#*F*4$F*8  =DsT  COX. # "`b|d;KW{?++LQ+L кL9#иQGиG/LYT/D +92+<+$+01%463!2#!".5354332##"55#''&676676##"5543!2"3!26554##"54332 , ,:&&Y 0"  ]&&x**K<"ÀO ! 6j # "   b|dDNZF+&!+OT+F иTи/OкO9!,иTMиM/O\W/I+B;+E+ -+01".546332!546332#354332##"55#''&676676##"5543!23!26557##"54332[,, ,&&Y 0"  E&&jh&!$AFQ ! 6j # "tf fbw*S_W`/Y/`и/ иYT:и:/Ta\/(!++01354332##"55#&''&676676##"5543!2'.'''&67>7632##"54332&&+`  0"N2c 7cSC?Rb8CpS0 0  &&*L"Bj/  @u # " Lb ".;D$$E<0 % '4632##"54332&&Y 0"?YM11NYA b[ 0 "- .#/ ZU ,&&K<-O ! 6j # "![56Y U34T#$ 0e  @9/ :HR)   P' bpj;O[Ż<++PU+P и / 9#A<&<6<F<V<f<v<<<<<<< ]A<<]UFиF/P]X/A+92+K+$+0174>32#".354332##"55#''&676676##"5543!232>54.#"%##"54332.SqD>pV22Vp>DqS. &&Z 0"#?V3-VB))BV-3V?#&&'+C/.C,,E..EL?!~M ! 6j # "h--.. itC2c+ ииии4 //0)+++01354332##"5!"5543!5#''&7>76&#!"5543!2, )) *  U]6  I/{53m # J # ,qM # #}pC0Fe +ии 5и;и H/8/.'++2?++01354332##"55#"554335#''&676676#!"5543!2!2##"554&#!"&55465)) * E0"/) $ m # yb= ! Bi # " #)$}pC0F\]/Q/и/QL иQи/Qи/];и;/6L^/8/N/.'++2?++2Gи?U01354332##"55#"554335#''&676676#!"5543!232##"554&##"&5546!32##"554&##"&55465)) * E0"/$  B/$  o # w^? ! Aj # "#)$#)$}b2HI//I7и7/ 7 9ии7>4и4/ J/B3+0)+++01354332##"55#"554335#''&67>76#!"5543!2!"&5543323!25)) /Iy[:  E0" /)  )* # a6 ! Q`l9 # "#)$}|T1MN//N8и8/ 4и4/849ии8G5и5/G<и76#!"5543!2'.'''&67>76323 )) .Iy[:  E0" 2c 7cSC?Rb8CpS0 0 # # e9 ! Q`l9 # " Lb ".;D$$E<0 % '46326)) + F0"K?YM11NYA b[ 0 "- .#/ ZU \ # ya< ! Bi # "![56Y U34T#$ 0e  @9/ :HR)   P' }pKEY߸Z/F/Z и /Fи/FF$и$/F,и,/ PAP&P6PFPVPfPvPPPPPPP ]APP][/U+C<+-+K+,%+!и!/01%#".54>32354332##"55#"554335#''&676676#!"5543!24.#"32>K.OiZxM E0#"  , , 5   .\ # x.SKE! ! Cd # "LP **  O ! atd4@_+; +ии;5;B=/7/2*+++01354332##"5#"554335#''&67>76&##"55463!2##"54332&& I CmP0   -%x&&q-M #  *guD $&~b1GSo=6+ +HM+6H9ииHUP/A2+/(+++01354332##"55#"554335#&''&676676##"5543!2!"&5543323!2'##"54332 && (y  0"|/)  &&)& # ]:  @u # "$)$`qy1co*+;6+di+iи/и/d!к4d96Aи6Iиdql/.+aZ+ %+5J++IB+01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55#"554335#''&67>76##"5543!2##"54332y /#-  #-O s&& L?jP4 0!s&&#*F*4$F*8 |GW # pm  COX. # "cb|dLVbN+% +W\+N и\и/WкW9 +и 3и\UиU/Wd_/Q+JC+M+4+3,+01".546332!546332#354332##"55#"554335#''&676676##"5543!23!26557##"54332[,, ,&& ({ 0"  E&&jh&!FM # o`4 ! 6j # "tf fbw1Zfig/`/gи/ ии`[AиA/[hc//(+++01354332##"55#"554335#&''&676676##"5543!2'.'''&67>7632##"54332 && '}  0"N2c 7cSC?Rb8CpS0 0  &&%a # {b=  @u # " Lb ".;D$$E<0 % '&&#!"&5546  d+  (   A""3  )fnp3 'quk!$54&#!"&5546  e)/)   (    "" #)$@ DB= ?CA$<:PF?++ 0+A00]A0)090I0Y0i0y0000000 ]?7и7/ RJ;+3+ +01!2#!"&55463!546332%!2''&7>54&#!"&5546!"&5543323!2  e) (    d/)  "" HHB DHF$')$<|5V+.+<L+< и /ALL]AL)L9LILYLiLyLLLLLLL ]SиS/<X+7O+ +3&+&-01#!"&55463!2#!"3!2!2#!"&55463!546332%!2''&7>54&#!"&5546, , ,     e) (    v** !  e"" DB= ?CA$54&#!"&55464 /#-  #-Q   e) (    z#*H*7#G*; "" +YF++v+A]A)9IYiy ]v9/ &FY9&/.и>NиN/  +q+C\+SJ+++Sи/%и 5и5/ 8иdиd/J01%46332##".5!2#!"&55463!546332##".55463326554&##"&5546332##"33267667672!2''&7>54&#!"&5546"3326554&#,,  e)  (O!, !,E'   (     | **"" Q P*;$K*>  X 54&#!"&5546&&'''&67>'432  e)   %J!, !,A$  (    9WR9(A. -  .9 8"" Q P*;$K*>  X 32!2''&7>54&#!"&5546!"&55463!24&#"326#"&554332  )R!, !,G)    e)Y*:$$:**:$$:* (    X X0--11--0  q P*;$K*> "" , !,, !+ 54&#!"&5546 , ,>]    e) (    w**   "" DB= ?CA$и>/]и]/Fb:+AY+6+3&+&-01".546332!546332#!2#!"&55463!5463323!2655!2''&7>54&#!"&5546,, ,  e)   (    lk&"" f f A@; =@?$_`/U/`и/,и,/AUU]AU)U9UIUYUiUyUUUUUUU ]UEa@X+Y+ =+ 01!2#!"&55463!546332'&&'''&67>7632!2''&7>54&#!"&5546  e)# -p t+*nCqR1 0  (    ""  DZ " mEGr % 9HP' : HGA CHE$324.#"32>!2#!"&55463!546332%!2''&7>54&#!"&5546*8Yp76oZ88Zo68oY8I/HT$&TF..FT&%TG/  e) (    &1F,,F10F--F2!..! . . "" DB= ?CA$76&#!"5543!2!2#!"&55463!546332%!2''&7>54&#!"&5546L2LLF  R[]'+\\Y'  ?}) F Ӗ  e) (    j2  ! "  +6>  # K"" A@; =@?$?t?`ua// иa/и//8b//AY+*#+ +#и/*/и*;и;/01#"5433232##6"'"&55463226346332263>!2''&7>'&&#!"&5546)) CQNQM 27;+#PPK w (  ~y"E$ #  "  , )VWT% %VWS#$Ij1Gd˻/(+NZ+ + 5и;иEAZZ]AZ)Z9ZIZYZiZyZZZZZZZ ]f/8/I]++=D+(+(/иDUиU/017!2##"554&#!"&5546%6##"&5546335433266#"5433232##!2''&766'&&#!"&5546/)   A} )Y)) {m( #)$"  #l" 3s< 2k>$I2Heͻ>7+O[++ и>)и)/>0A[[]A[)[9[I[Y[i[y[[[[[[[ ]g/B3+J^+) + +)0иVиV/01%#"5433232##'6##"&5546335433266!"&5543323!2!2''&766'&&#!"&5546)) w  A} )Y/)  Xm( Z"" #Y$)$I 3z< 2r>$Iq1Md*+KD+kw+"+и/Rи"Xи"bAww]Aw)w9wIwYwiwywwwwwww ]"U/.+fz+ %++Za+D;+DKиaqиq/01#!"&55463!26554&#!"&55463!2#!"3!26##"&5546335433266#"&5433232##!2''&766'&&#!"&55465 /#- #-[ A} )Y)) {m( "*>*."<*1 "  #r " 3h< 2`>$IqSoE"+mf+=*++t+и/"2и2/zиw/ ++'@+7.+|+f]+7и/ и/ иHиH/MиM/fmии/.01%46332##".5##".55463326554&##"&5546332##"332677>766##"&5546335433266#"&5433232##!2''&766'&&#!"&5546"3326554&# ,,6 'M!, !,F$  A} )Y)) {m(  t **) ?**$:*-  "  #u " 3f< 2^>$   I|#@Vuۻ+>7+]k+ +Dи Jи TAkk]Ak)k9kIkYkikykkkkkkk ] wG/ +Xn++LS+7.+7>иScиc/017463!2#!".57"3!26554&#'6##"&5546335433266#"5433232##!2''&7>'&&#!"&5546 , ,I]    A} )Y)) {m(  r**   " #q" 3c< .14$I|%AXu+?8+_k++ и$иFиLиVAkk]Ak)k9kIkYkikykkkkkkk ]wI/ +Zn++NU+8/+8?иUfиf/01".546332!546332#%3!26556##"&5546335433266#"&5433232##!2''&766'&&#!"&5546,, ,B  3 A} )Y)) {m( ]`&^ ^H"  #[ " 3j< 2b>$Iv"?Ur=6+\h+TC+6T9TIAhh]Ah)h9hIhYhihyhhhhhhh ]TtF/Wk+KR+6-+6=01%'&&'''&7>743276##"&5546335433266#"5433232##!2''&766'&&#!"&5546. &o v,*pHsQ+ 0 y  A} )Y)) {m(   :_"  iABk" %9DJ% " #@"& 3v< 2n>$Ip'DZy +B;+ao+YH+HA&6FVfv ]A]YNAoo]Ao)o9oIoYoioyooooooo ]Y{K/#+\r++PW+;2+;BиWgиg/01%#".54>324.#"32>6##"&5546335433266#"5433232##!2''&7>'&&#!"&5546-2Sl;@nP--Pn@;lS2G%?T.3T;!!;T3.T?%=  A} )Y)) {m(  *B--B**A++A-++))d" #" 3c< .14$?tdCcӻ>5+JY+++и AYY]AY)Y9YIYYYiYyYYYYYYY ]e/ /E\+A++++&и&/A2и2/A5и5/0134332##"5###"543326"'"&5546374633226366!2''&7>'&&#!"&5546&&&&9==@B@ &V++;p#  )  |`G # "   # %VYW&$VXU#$J}1Sp QJ+Zf+++и иJ и /Q&и&/AZ&Z6ZFZVZfZvZZZZZZZ ]AZZ]r/++Ui+Q@++@;и;/@=и=/QGиG/QIиI/MиM/0134332##"5###"54332!"&5543323!26#'"&55463335433266!2#'&766'&&#!"&5546&&&&/)  7>:~9 *,X):p  &  z5a$)$ # " 6{8 0x<$Jqy1Mn!*+u+BG+94+4и/и/9!иB2и4?кe*9e/lAu&u6uFuVufuvuuuuuuu ]Auu]96/.+p+ %++3@+l[+[XиX/lbиb/ldиd/01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"543326#'"&55463335433266!2#'&&766'&&#!"&5546y /#- #-@ &&&&9=>:8 *,X)9q   %   #*B*(#9*5 vĸ # "q 0o+ +h1$J|d7Ab9+iu+,1+#+9 ии/#и,и)и@и@/Y99Y/`Auu]Au)u9uIuYuiuyuuuuuuu ]# /<+dx+8+*+`O+OLиL/`VиV/`XиX/01".546332!546332#354332##"55###"543323!26556#'"&55463335433266!2#'&&766'&&#!"&5546E,, ,u&&&&  9=>:8 *,X)9q   %   ec&Ba aD # "x 0l+ +e1$Jp=Yzxq+!++NS+4++E@+A++]A+)+9+I+Y+i+y+++++++ ]+49A!&!6!F!V!f!v!!!!!!! ]A!!] !9!и/41и1/N>и@KA&6FVfv ]A]EB/|+?L+g/+/и/gdиd/gxnиn/xpиp/01&&''&'''&67>'432766'4632354332##"55###"543326#'"&55463335433266!2#'&&766'&&#!"&5546BXM5m,YC 4J, 0 ". =D/ -@' &&&&9=>:8 *,X)9q   %   R10O  =\/L%$ 7BR4  $62)'X  #A9. Ȱ # "y 0l+ +e1$Jum'CdI+kw+8=+/*+A&6FVfv ]A]*и/8(и*5к[9[/bAww]Aw)w9wIwYwiwywwwwwww ]/,/+fz+#+)6+bQ+QNиN/bXиX/bZиZ/0174>32#".732>54.#"354332##"55###"543326#'"&55463335433266!2#'&&766'&&#!"&5546.TtF@sW33Ws@FtT.H%BX3.YE**EY.3XB%g&&&&9=>:8 *,X)9q   %   '*A--B*+B--B(,,--ֵ # "y 0l+ +e1$MtC 5VgW// W%и%/. X//7O+ +и/ %и 1и1/01#"543326"'"&55463226346332263>!2''&7>'&&#!"&55465))CQNQM 27;+#POKw (  ~$ #  "  + )VWT% %VWS#$LjD1NZ/(+8D+YR+Yи/R и /ADD]AD)D9DIDYDiDyDDDDDDD ]Y\/U/3G++(+(/017!2##"554&#!"&5546%6##"&5546335433266!2''&766'&&#!"&5546#"54332/)   A} )Y2m( ))#)$"  # 3s< 2k>$Lb2>[(!+EQ+=6+(и/(AQQ]AQ)Q9QIQYQiQyQQQQQQQ ]=]9/,+@T+ +016##"&5546335433266!"&5543323!2'#"54332!2''&766'&&#!"&5546  A} )Y/)  #))em( 5" #Y$)$cz 3z< 2r>$LqY1Mjw*+T`+vo+oи/и/v!и!/*DиD/*KA``]A`)`9`I`Y`i`y``````` ]vyr/.+Oc+ %++D;+DK01#!"&55463!26554&#!"&55463!2#!"3!26##"&5546335433266!2''&766'&&#!"&5546#"&54332Y /#- #-V A} )Y2m( ))"*>*."<*1 "  #~ 3h< 2`>$ L|D#@_kѻ+GU+jc+j и /cи/7и7/>AUU]AU)U9UIUYUiUyUUUUUUU ]jmf/ +BX++7.+7>017463!2#!".57"3!26554&#'6##"&5546335433266!2''&7>'&&#!"&5546#"54332 , ,I]  1  A} )Y1m(  ))r**   " # 3c< .14$ L|C%A^kٻ+HT++ и8ии$и?ATT]AT)T9TITYTiTyTTTTTTT ]cиiиmf/ +CW++8/+8?01".546332!546332#%3!26556##"&5546335433266!2''&766'&&#!"&5546#"&54332?,, ,B  I A} )Y2m( ))]`&^ ^H"  # 3j< 2b>$%  Lv"?\h=6+FR+g`+ARR]AR)R9RIRYRiRyRRRRRRR ]RF9gjc/AU+6-+6=01%'&&'''&7>743276##"&5546335433266!2''&766'&&#!"&5546#"54332Q &o v,*pHsQ+ 0 c  A} )Y1m( ))  :_"  iABk" %9DJ% " # 3v< 2n>$@LpP'Dco  +KY+ng+gA&6FVfv ]A];и;/BAYY]AY)Y9YIYYYiYyYYYYYYY ]nqj/#+F\++;2+;B01%#".54>324.#"32>6##"&5546335433266!2''&7>'&&#!"&5546#"54332P2Sl;@nP--Pn@;lS2G%?T.3T;!!;T3.T?%S  A} )Y1m(  )) *B--B**A++A-++))d" # 3c< .14$<#C + +*9+A99]A9)999I9Y9i9y9999999 ]*E!+%<+! и!01%#!"&55463346332346332!2!2''&7>'&&#!"&5546  ++B   (  A""3 3  0mni,#lto'$<#DZPI+++*:+A::]A:):9:I:Y:i:y::::::: ]*\TE+%=+ +и01!2#!"&5546335463323546332%!2''&7>54&#!"&5546!"&5543323!2[B  ++r (   i/)  ""  HHB DHF$')$54&#!"&55464 /#-  #-Q B  ++r (   z#*H*7#G*; ""  54&#!"&5546,, ,yB  ++   (   lk&""  f f A@; =@?$7632!2''&7>54&#!"&5546  ++B j -p t+*nCqR1 0  (   :""   DZ " mEGr % 9HP' : FE@ BFC$'&&#!"&5546 w+| i    N^"q "'\\R$  /  x/)   { y  " !+EED"#)$"AC@<1G=6+#+ +A]A)9IYiy ]6и/ 9 IA2+ ++$и,014&#!"5543!232#!##"&55!"&55463!>!"&5543323!2  /  +s t S/)   " !+NTP" "LQM')$<|Cǻ%2+7+ +A]A)9IYiy ]2и/ и/ E).+ + ++6и8и@014&#!"5543!232#!32#!"3!2#!"&5546335!"&55463!66  / x   , ,{ y  " !+<9" !  !**"9+YF+t+ FY9 /> и />NиN/tzt5/8/w/$+C\+q~+++ +и+и~JиJ/qRи8e8xиx/01#!##"&55!"&55463!66'4&#!"5543!232##".55463326554&##"&5546332##"3326766767232##"54&##"&5546 x+{ {   /  [  'N!, !,C&  /$  "} }"6r7 " !+:x7 T*?$O*D  M#),#+YF+'+Yzиz/и/Y и /A]A)9IYiy ]>NиN/'9/A]A)9IYiy ]p'иpu+e8+$+++SJ++++ +и+и85и5/BиB/\и\/01#!##"&55!"&55463!66'4&#!"5543!232##".55463326554&##"&5546332##"33267667672%#".54>327!"&55463!24&#"326#"&554332 x+{ {   /  F  )R!, !,G)  _*:$$:**:$$:* X X0--11--0  " "6r7 " !+:x7 V*A$Q*D  *, !,, !+{& ' '&$$% $ <|;K@*+/+!G+Gи/*и/! и /!MD$+ +.<++.и0и8014&#!"5543!232#!32#!".5546335!"&55463!66"3!26554&#  / x ,> ,{ y D   " !+<9"**"9   01".546332!546332#4&#!"5543!232#!##"&55!"&55463!663!2655,, ,  /  x+{ z ,  $qp&+ " !+;9" "9}j j75!"&55463!>  /  x-p t+(q?lR3{ u  " !+LQN"Db " tGJw  % 4CK&"JNK75!"&55463!664.#"32>  / x7eL-5Wq=76&#!"554335!"&55463!66  /  x^O4 ONG  S\^(+[[X'  B{' F { {  " !+98"d5  ! $  /:?  # "8{ItCCWɻ +<++KP+A++]A+)+9+I+Y+i+y+++++++ ]A+<9PDиKYG/M/7.+DQ++ и /и/AMG9016##"&5'"&554632>7667>'&&#!"&55463!2664332##"55#"5543  @K+ GD< QUO/  a )Ig)) r"   _"KMI$ JNI F " TjD?Ui+5+]b+A55]A5)595I5Y5i5y5555555 ] 59]DиD/bJиJ/bVи]kG/Y/8+AN+/`+Vc+ GY9/и/#и#/01!26676##"&55"'"&554632>766766'&&#!"&5546!2##"554&#!"&5546%4332##"55#"5543Y :  AK+CA< LQL9  V/)   ))  3f/"   "0o/$#)$8 " L[BVlb[++8+JO+A88]A8)898I8Y8i8y8888888 ] 89OCиJnF/_X+;+CP+++и/_и/$и$/Xf01!26676##"&55'"&554632>7667>'&&#!"&55464332##"55#"5543!"&5543323!2Y )G  @M+FC; PTN4  [)) /)   >@>  !   "BD@$5M| $ #)$LqW1s6*+>E+n]+{+и/и/{!и!/A]]]A])]9]I]Y]i]y]]]]]]] ]q]n9tи{w/EX%/%>Y.+i`++WF+t+% F=и=/FKиK/WZиZ/q%w9W~01#!"&55463!26554&#!"&55463!2#!"3!26##"&55'"&554632>766766'&&#!"&55463!2664332##"55#"5543W /#- #-X   HLL$+FD= OSO8  Y #Aa)) #*7*%$7*) B"   "0q0$ 3i/y" " LoAz ++<++A++]A+)+9+I+Y+i+y+++++++ ]?+<9E9M 9{и~/7.+l+%+{+ и /и/%(и(/lZиZ/016##"&55'"&554632>766766'&&#!"&55463!266&&''&&'''&6766'432766'46324332##"55#"5543  !KOO$+C@; OSO8  Y #A>^N68L5eY 0 C6=D/ 0>$ )) "   "0q0$ 3i/L3+J H+W? " +oJ  7['k@   81)   I " LuXUi}KV+ '+P?+qv+q` A??]A?)?9?I?Y?i?y??????? ]S?P9AV&V6VFVVVfVvVVVVVVV ]AVV]vjиqm/[+KB+e+9#+jw+9(и/(-и-/9<и32#".6##"&55'"&554632>766766'&&#!"&55463!26632>54.#"4332##"55#"55430UsD>rX55Xr>DsU0  !JMM$+DB< OSO8  Y #A(CX0+WF--FW+0XC()) *=))>*+>))>"   "0q0$ 3i/F( () )G " 4td?S_ +8'+@E+TY+A8&868F8V8f8v8888888 ]A88]='89EMиTaV/\/3*+MF++ и /и/и/"и"/=V\9016##"&5'"&5546367667>'&&#!"&55463!266##"55#"554334332##"54332  0\:+9o+ :~9%  # )& &&&n"   b"EGF$ GIC> " ~Dv9\p| +4#+]b+qv+A4&464F4V4f4v4444444 ]A44]7#49Kq9bjиq~y//&+jc++и/и/и/ и /016##"&55'"&554636766766'&&##"&55463!266'&&'''&7>74327##"55#"554334332##"54332 /T3+r\ 9n:-    I &o v,*pHsQ+ 0 f& &&&   "0n0$ 0h0  :_"  iABk" %9DJ% ? " eItCCOϻ +<++NG+A++]A+)+9+I+Y+i+y+++++++ ]A+<9NQD/J/7.+#+ и /и/#и/#&и&/ADJ9016##"&5'"&554632>7667>'&&#!"&55463!266#"54332  AH+ JG@ QUO3  a #E))k"  ^"KNK$ LOI~TjC?Uaջ+5+EJ+A55]A5)595I5Y5i5y5555555 ] 59JYиE_иEc\/G/8+AN+/+ G\9/и/#и#/01!26676##"&55'"&554632>766766'&&#!"&5546!2##"554&#!"&5546%#"54332Y 4  ?H+ED? LQL?!  K/)  B)) 3f."   "0n/$#)$8L[BXdѻNG++8+c\+A88]A8)898I8Y8i8y8888888 ] 89cf_/KD+;+++и/Kи/$и$/DR01!26676##"&55'"&554632>7667>'&&#!"&5546!"&5543323!2'#"54332Y &C  >K+ HF> PTN9  /)  )) >?>  !   "AD@$#)$ULqW1q}*+<C+l[+|u+uи/и/|!и!/A[[]A[)[9[I[Y[i[y[[[[[[[ ]o[l9|x/.+g^+ %++U?+UD;и;/DIиI/01#!"&55463!26554&#!"&55463!2#!"3!26##"&55'"&554632>766766'&&#!"&55463!266#"54332W /#- #-X   >F+HGA OSO=  Y !;))#*<**$<*. B"   "0p0$ 3h/L|DScoW+%+N=+ng+n и /A==]A=)=9=I=Y=i=y======= ]Q=N9g_и_/nqj/\ +I@+T+:!+:&и/&+и+/&2:7и7/:QиQ/017463!2#!".56##"&55'"&554632>766766'&&#!"&55463!266"3!26554#"54332 , ,E  @G+GE? MTP=  Y ;  N))f**"  "0s/$ 4i0   pL|Dq]+&-+VE+pi+] иiи/pи/AEE]AE)E9EIEYEiEyEEEEEEE ]YEV9idиd/psl/`+QH+\+?)+?.%и%/.3и3/01".5546332!546332#6##"&55'"&554632>766766'&&#!"&55463!2663!26557#"54332;,, ,A  >F+HGA OSO=  Y !; 6))]`&c"   "0p0$ 3h/JT T LvBeq+a+pi+Aaa]Aa)a9aIaYaiayaaaaaaa ] a9a8и8/Ta9psl/;+++и/$и$/01!26676##"&55'"&554632>7667>'&&#!"&5546'&&'''&7>7432#"54332Y &C  ?L+ FD; PTN9   &o v,*pHsQ+ 0 )) <=;  !   "?@>$  :_"  iABk" %9DJ% R'&&#!"&5546 + + i    N]"k k "'ZZP$<;QG@+&-+#+ +A]A)9IYiy ] 9 SK<+ ++$и.и6014&#!"5543!232####"&55###"&55!"&55463!>!"&5543323!2  /  ++ s T/)   " !+LQN"  "JNJ')$'&&#!"&5546   m   Sckg]#""VfphX$!2##"554&#!"&5546  /   v /)   " !+HIG""EFD@#)$<'=>//A]A)9IYiy ]>-и-/и -2 ?7(+ ++"01&&#!"5543!232#!"&55463!>!"&5543323!2  /   w N/(   " !+T\X"""VWN ')$<|'CD//A]A)9IYiy ]D.и./и/ *и*/.=+и+/=2и2/ 5и5/ AиA/ EA*+ +38++"014&#!"5543!232#!"&55463!>#!"&55463!2#!"3!2  /   y N , ,    " !+HJG""EGD** !  01#!"&55463!26554&#!"&55463!2#!"3!2#!"&55463!>54&#!"5543!2324 /#-  #-Q x  y   /  z#*W*H#X*J  ""==; " !+:@B"3!26554&# , ,>  /   y E  ** " !+HJG""EGD   01".546332!546332#4&#!"5543!232#!"&55463!>3!2655,, ,  /   |-  $qp&+ " !+EIG""EFCj j'.'''&67>7432  /   u @Q^4 6bUD@Sb7DqS/ 0  " !+MSO""KPLI #B7+  !.;E%&G<0 % =MV+ 324&#!"5543!232#!"&55463!>4.#"32>*8Yp76oZ88Zo68oY8L  /   y /HT$&TF..FT&%TG/&1F,,F10F--FM " !+HJG""EGD!..! . .ItC9EŸF/=/F/и//A&6FVfv ]A] /9=DG:/@/2+(+ :@9и/(#и#/(*и*/01!26676'"&554633266767>'4&#!"&5546#"54332a 'F  DQSVN E)TO/1    ))  fok% #  "$elk*$~ttC '/ + )//%+01#"54332''&7>76&#!"5543!25)):g\  U]6  Q/~V1 # ,qM # #xpC !9A + ии ;//70+ +01%#"54332!2##"554&#!"&5546''&676676#!"5543!25))/) $ 0ĺ K0"5#)$ЋN ! Bi # "xb !;K76#!"5543!25)) /)  ˱I{]<  K0"t~#)$J ! Q`l9 # "x|Y #?i@// @*и*/9'и'/9.и./ 1и A/=&+!+/4+01%#"54332''&676676#!"5543!2#!"&55463!2#!"3!25))ĺ K0" , , 5   N ! Bi # "** !  xqX $VW// W,и,/O)и)/4и,<и76"3326554#"54332#,, 8ab K0#66 %K!, !,D" w t H))**Cn_U* ! Cb # " N*9$I*<     o1x|C /GH// H и /#и/и/ ии/#и/+и I/(+E>+ +01%#"54332463!2#!".57"3!26554&#''&676676#!"5543!25)) , ,]]  ĺ K0" {**   N ! Bi # "x|C '1KL// Lи/) и /)ии #и'и'/0и M/, +IB+(+01#"54332".546332!546332#%3!2655''&676676#!"5543!25)),, ,U  9aa K0"!/[jh&l lgBtfZ) ! Ed # "xwBN#MF+MPI/+01''&67>76#!"5543!2'.'''&67>7632#"543326˱I{]<  K0"2c 7cSC?Rb8CpS0 0 ))J ! Q`l9 # " Lb ".;D$$E<0 % 324.#"32>''&676676#!"5543!25)).OiV5  `t   sh  `c   /~"T6  c d  [~ #Gp+E_e,+ ии)//A:++!(+:VиA\017!2##"554&#!"&5546%#"5433232##''&76676&##"554332''&7>76&##"554332/) $ /)) st  ed x  |h  1H1  0 #)$4"sW  Kf aB  =AG( "Gp/I_uv// иvTиT/O$и$/aиa/dиjиj/w/Q/g/+$+KX+ +$@и+FиK`иXn01%#"5433232##''&76676&##"554332''&7>76&##"55433232##"554&##"&5546!32##"554&##"&5546)) st  ed x  |h  1H1  0 /$  B/$  "sW  Kf aB  =AG( "#)$#)$G+E_!+,7++ A77]A7)797I7Y7i7y7777777 ]a/%+A:+ +:VиA\01%#"5433232##!"&5543323!2''&76676&##"554332''&76676&##"554332)) /)  wp  ak x $=V5  Zh  /l"#)$vT  La -]ZS"  6R #Jq1Gc|}//}и/*и/и/".и./5и";и"Eи"~8/.+_X+ %+=D++Xsи_y01#!"&55463!26554&#!"&55463!2#!"3!2#"5433232##''&676676&##"554332''&676676&##"5543325  /#-+ #-o #)) sv fa  x  um Wi  0 #*K*9$K*= "gR FY TB  1}@ "JqSiy׻E"+=*+m+hW+"2и2/h]иhvhZ/r ++'@+_f+7.+7и/ и/ иrHиH/rMиM/.jии01%46332##".5##".55463326554&##"&5546332##"332677>76#"5433232##"3326554&#''&676676&##"554332''&676676&##"554332 ,,6 %K!, !,D" )))  t |sv fa  x  um Wi  0 **) N*9$I*:  p"m   gR FY TB  1}@ "G|#9Smn//nи/и/и/  и /и/'и -и 7и o*/ +OH++/6+HdиOj017463!2#!".57"3!26554#"5433232##''&76676&##"554332''&7>76&##"554332 , ,]]  O)) st  ed x  |h  1H1  0 **   o"sW  Kf aB  =AG( "G|%<Riӻ+&0++ и$A00]A0)090I0Y0i0y0000000 ]@иFиPиkC/ +:3++HO+3`и:f01".546332!546332#%3!2655''&76676&##"554332#"5433232##''&76676&##"5543320,, ,V    df x /)) D  `a  0 jh&k kb  Ic #b"y <}M "Gw(>YsMZ+-?+AM&M6MFMVMfMvMMMMMMM ]AMM]ZM9-=2//WP+4;+PjиWp01%'.'''&67>7632#"5433232##''&76676&##"554332''&76676&##"554332.2c 7cSC?Rb8CpS0 0 )) !=U6 cg x /$>V4  ^i   / Lb ".;D$$E<0 % '432766'4632#"5433232##''&76676&##"554332''&7>76&##"554332d>\/ii-XC 2H- 0 "- :E/  2?  ])) st  ed x  |h  1H1  0 T8d@ ;b0P$$ 8EV6  ':3+ %]  %$F:-  "sW  Kf aB  =AG( "Gp'=Wq +<+++A&6FVfv ]A]<1и<s./#+SL++3:+(и(/LhиSn01%#".54>324.#"32>7#"5433232##''&76676&##"554332''&7>76&##"554332,.Oi  5~A "G1IeJ!+2=+++и A==]A=)=9=I=Y=i=y======= ]g/++G@++@\иGb0134332##"5###"54332!"&5543323!2''&76674&##"554332''&7>76&##"554332o&&o&&/)  ic  XY \ l/"9K-  $>/ x /z8U$)$sL  Ba #%,ZTL    5~A "G|d7AYs9+BM+,1+#+9 ии/#и,и)и@и@/AMM]AM)M9MIMYMiMyMMMMMMM ]#u /<+WP+8+*+PjиWp01".546332!546332#354332##"55###"543323!2655''&76674&##"554332''&7>76&##"554332[,, ,eo&&o&&  if  [W \ l/ l`  )=+ x 0jh&(Gf fcmI AT #%Y=  48?& "GwD[w˺O\+E++ иии0\9AO&O6OFOVOfOvOOOOOOO ]AOO]y/YR++RnиYt01354332##"5###"54332'.'''&67>7632''&7676&##"554332''&7>76&##"554332o&&o&&q2c 7cSC?Rb8CpS0 0 #l`   \ l/"8L-  '?/ x / Lb ".;D$$E<0 % +L2L29A & 6 F V f v ]A ]  9 и/L*и*/20и0/L<и>Iи*WиW/C@/mf+=J+fиm01&&''&'''&67>'432766'4632354332##"55###"54332''&76676&##"554332''&76676&##"554332>\/ii-XC 2H- 0 "- :E/  2?  o&&o&&ic  UV \ l k`  QZ  x 0 T8d@ ;b0P$$ 8EV6  ':3+ %]  %$F:-   _rH <^ U>  5~A "Gpj'C]u߻+B++94+9 и /A&6FVfv ]A]4и/B1и4?и^и^/9w6/+YR+#+3@+RlиYr0174>32#".732>54.#"%#"54332354332##"55#''&76676&##"554332''&76676&##"554332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#]&&o&&oic  UV \ l k`  QZ  x 0 '+C/.C,,E..E)--..rH <^ U>  5~A "Ht;S-<+ + иܸиA-&-6-F-V-f-v------- ]A--]//70+ ++0Jи7P01#"5433232##32##''&7676&##"554332''&76676&##"554332))  %>V5  `t   sh  `c   /~  T6  c d  [~ #Gp5Oi6+ иܸии$и*и+/'/KD+ +!.++D`иKf01%#"5433232##32##!2##"554&#!"&5546''&76676&##"554332''&7>76&##"554332))  /) $ ust  ed x  |h  1H1  0 !x!l4#)$sW  Kf aB  =AG( "Jq1Qm*+"+и/5и";иKܸ@и"Eи"Oи"8/.+ib+ %+=D++GN+b}иi01#!"&55463!26554&#!"&55463!2#!"3!2#"5433232##32##''&676676&##"554332''&676676&##"5543325  /#-+ #-o #))  sv fa  x  um Wi  0 #*K*9$K*= !i!_zgR FY TB  1}@ "VtC+C,++A,,]A,),9,I,Y,i,y,,,,,,, ] , 9иE //' ++ :и'@0134332##"5#''&7676&##"554332''&76676&##"554332n)) ^<  `t   sh  `c   /z9m5aX>  c d  [~ #[pC)?Yk+ и .и4и[/1/%++8++Pи%V01354332##"55#''&76676&##"554332!2##"554&#!"&5546''&7>76&##"554332l));  ed x /) $ r |h  1H1  0 D?r_  Kf #)$aB  =AG( "[pC)CYop/e/и/e eи/pNиN/I%и%/ ^и^/ q/K/a/%+ER++:и%@иEZиRh01354332##"55#''&76676&##"554332''&7>76&##"55433232##"554&##"&5546!32##"554&##"&5546l));~  ed x  |h  1H1  0 ~/$  B/$  H=r]  Kf aB  =AG( "#)$#)$[b)?Y5.++ +A]A)9IYiy ]9и [/9*+%++Pи%V0134332##"5#''&76676&##"554332!"&5543323!2''&76676&##"554332l));w  ak x /)  $=V5  Zh  /OI0sZ  La #)$-]ZS"  6R #[qX1]v˸w//wи/*и/и/".и./4.95и";иAи*IиI/*LиL/"x8/.+YR+ %+5B++RmиYs01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55#''&676676&##"554332''&676676&##"554332X  /#-+ #-o )); fa  u  um Wi  0 #*K*9$K*= ;44lZ  FY # TB  1y@ # "[|C#Mgh//hи/и/и/  и /и/& 9'и -и3и i*/ +IB++'4+B^иId01%463!2#!".57"3!26554&#354332##"55#''&76676&##"554332''&7>76&##"554332 , ,]]  z))9  ed x  |h  1H1  0 **   G? o_  Kf aB  =AG( "[|CCMdݻE+7++E A77]A7)797I7Y7i7y7777777 ]79и%и+иLиf"/H+A:+D+,+:[иAa01".546332!546332#354332##"55#''&76676&##"5543323!2655''&76676&##"554332R,, ,));  df x /  z  `a  0 jh&!D</q^  Ic #zl la <}M "[w)RlS++ S 9A&6FVfv ]A]>S9n/' ++ cи'i01354332##"55#''&76676&##"554332'.'''&67>7632''&76676&##"554332l))]< cg x /.2c 7cSC?Rb8CpS0 0 $>V4  ^i   /DE?p- Jf # Lb ".;D$$E<0 % '432766'4632''&7>76&##"554332l))8  ed x >\/ii-XC 2H- 0 "- :E/  2?   |h  1H1  0 E?oa  Kf T8d@ ;b0P$$ 8EV6  ':3+ %]  %$F:-  &aB  =AG( "[pK=Qk͸l/>/l и />и/>>#и#/ HAH&H6HFHVHfHvHHHHHHH ]AHH]m/M+92+C+$+2bи9h01%#".54>32354332##"55#''&76676&##"5543324.#"32>''&7>76&##"554332K.Oi  5~A "MG(>JfK.++ +?D+A]A)9IYiy ]9и?hG/8)+&++]и&c01354332##"5#''&76674&##"554332!"&5543323!2'##"54332''&7>76&##"554332! j&&3q  XY \ l/N/)  &&"9K-  $>/ x /~"@ !uW  Ba #$)$,ZTL    5~A "LGw'P\x]++W +WQ]Q9A&6FVfv ]A]<]Q9WzY/%++oи%u01354332##"55#''&7676&##"554332'.'''&67>7632##"54332''&7>76&##"554332!m&&Q4   \ l/ 2c 7cSC?Rb8CpS0 0  &&"8L-  '?/ x /FB8b)   #% Lb ".;D$$E<0 % 32#".732>54.#"354332##"55#''&76676&##"554332##"54332''&76676&##"554332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#p&&6g  UV \ lB&& k`  QZ  x 0 '+C/.C,,E..E)--..MF?(eJ <^ WpU>  5~A "VtC3K%4++ иииA44]A4)494I4Y4i4y4444444 ]M ///(+++(Bи/H01354332##"5#"554335#''&7676&##"554332''&76676&##"554332n)) sQ  `t   sh  `c   /z.] # }T  c d  [~ #[b3Icǻ?8+%+ +и/иA%%]A%)%9%I%Y%i%y%%%%%%% ] e/C4+/(+++(Zи/`01354332##"55#"554335#''&76676&##"554332!"&5543323!2''&76676&##"554332l)) lQ  ak x /)  $=V5  Zh  /  # S=  La #)$-]ZS"  6R #[qX1gӸ//и/*и/и/".и./6и"<иBиJи*SиS/*VиV/"9/.+c\+ %+6K++JC+\wиc}01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55#"554335#''&676676&##"554332''&676676&##"554332X  /#-+ #-o )) mZ fa  u  um Wi  0 #*K*9$K*=  0] # {K?  FY # TB  1y@ # "[w2[uǺ&\++ иииA&&&6&F&V&f&v&&&&&&& ]A&&]G\&9w/0)+++)lи0r01354332##"55#"554335#''&76676&##"554332'.'''&67>7632''&76676&##"554332l)) mN cg x /12c 7cSC?Rb8CpS0 0 $>V4  ^i   /  # S; Jf # Lb ".;D$$E<0 % '432766'4632''&7>76&##"554332l)) /  ed u >\/ii-XC 2H- 0 "- :E/  2?   |h  1H1  0  b # w  Kf # T8d@ ;b0P$$ 8EV6  ':3+ %]  %$F:-  &aB M+ + /+A//]A/)/9/I/Y/i/y/////// ]A>&>6>F>V>f>v>>>>>>> ]A>>] Y+2+ и:и2P01%#!"&55463!46332!232''&7>'&&##"&5546!32''&7>54&##"&5546  +x K  &  /" $#  A""  z "\a^$%bbX"')&^`]& (ZYV%"'&&##"&5546!|  )/)  6  $  /-*    "" #)$; 4q00s,"')9u8  255"</Oj%+T`++6E+AEE]AE)E9EIEYEiEyEEEEEEE ]LиL/AT&T6TFTVTfTvTTTTTTT ]ATT]6l)+1H+ +и1PиHc01!2#!"&55463!546332!"&5543323!232''&7>'&&##"&5546!32''&766'&&##"&5546!|  )/)   $  /-* %( "" ')$f <>99:8"')89 ,r4"<`-QmB;+r}++Xc+Acc]Ac)c9cIcYcicyccccccc ]cX9/A]A)9IYiy ]$9$/A$$]A$)$9$I$Y$i$y$$$$$$$ ]jиj/Ar&r6rFrVrfrvrrrrrrr ]Arr]$+F7+Sf+)+++ + и73и3/>иFJиJ/Snиf01!2#!"&55463!546332#".54>32###"&54332332667667632''&766'&&##"&5546!32''&766'&&##"&5546!"&55463!24&#"326#"&554332!|  ))*:$$:**:$$:**,#/) !*& )   $  *"*-  %( r e0--11--0  "" , !,, !+i#) % j 4d00e-"#-1l<  ,f)"v& ' '&$$% $ '&&##"&5546 , ,>]  |  )/  $  /-*    v**   "" 4q00s,"')9u8  255"иQ.XиX/A`&`6`F`V`f`v``````` ]A``]w:+AT+6+3%+%-иA\иTn01".546332!546332#!2#!"&55463!5463323!265532''&766'&&##"&5546!32''&766'&&##"&5546,, ,|  )    $  /.)  #* kj&"" e e 4d00e-"')0r7  *d-"^x cn+ET++,9ATT]AT)T9TITYTiTyTTTTTTT ][и[/Ac&c6cFcVcfcvccccccc ]Acc]Ez@W+X+ =+ и@_иWqиXr01!2#!"&55463!546332'&&'''&67>763232''&7>'&&##"&5546!32''&766'&&##"&5546!|  )  -p t+*nCqR1 0 % $  /-*  %( ""  DZ " mEGr % 9HP' 5 <>99:8"')89  ,r4"324.#"32>!2#!"&55463!546332'32''&766'&&##"&5546!32''&7>'&&##"&5546*8Yp76oZ88Zo68oY8I/HT$&TF..FT&%TG/|  )/  $  /-*    &1F,,F10F--F2!..! . . "" 4q00s,"')9u8  255"76&#!"5543!2!2#!"&55463!546332'32''&766'&&##"&5546!32''&7>'&&##"&5546L2LLF  R[]'+\\Y'  ?}) F Ӗ|  )/  $  /0'    j2  ! "  +6>  # N"" 4d00e-"')/v4  130"76&#!"5543!2!2#!"&55463!546332'32''&766'&&##"&5546!32''&766'&&##"&5546#!"5543!2H- HJI!  #X^\'Kc   Oq) J %0|  )/  $  /X  (%  ='  $  3 )-/ ! #ϓ"" 7c*.`0"')fm  ,\+"?t<\xݻan+8/++ Aa&a6aFaVafavaaaaaaa ]Aaa]z//>U+8#+ +#и/8*и*/8/и// 3и3/>]иUq01#"5433232##6"'"&554632267546332>32''&7>4'&&##"&5546!32''&76674&##"&5546)) CQNQM ?FF+!GEA  $  /!  **  ~y"E' #  "  #  MQP!JNO#"')#UUQ CN"Ij3IfN\+1(+ + 7и=иGAN&N6NFNVNfNvNNNNNNN ]ANN]/:/K_++?F+,+(1и1/KgиFpиp/_x017!2##"554&#!"&5546%6##"&55463!54633266#"5433232##32''&76654&##"&5546!32''&76'&&##"&5546/)   A} +K)) Q/!  *&   /  $  #)$"  # h"')<=: 0d3"')0i9ff"Iq1Ofû*+MD+"+и/Tи"Zи"dи*k*yиy/"W/.+h|+ %++\c+H;+;DMиM/hи|01#!"&55463!26554&#!"&55463!2#!"3!26##"&55463!54633266#"&5433232##32''&76654&##"&5546!32''&7>4'&&##"&55465 /#- #-[ A} +K)) Q/ !  ('   /  $ "*>*."<*1 "  # m "')278 .Y&"')/c*.10"Ip=[qcv+YP+4++p_+A++]A+)+9+I+Y+i+y+++++++ ]+49Av&v6vFvVvfvvvvvvvvv ]Avv]v9/! !9и/41и1/peиpb/s+gn+G/+/и/GPYиY/иfܸsиnи/01&&''&'''&67>'432766'46326##"&55463!54633266#"5433232##32''&76654&##"&5546!32''&76'&&##"&5546XBXM5m,YC 4J, 0 ". =D/ -@'  A} +K)) Q/!  *&   /  $  R10O  =\/L%$ 7BR4  $62)'X  #A9. "  # f"')<=: 0d3"')0i9ff"Ip'E[x +C:+ZI+IA&6FVfv ]A]ZOкn 9n/`ZL/#+]q++QX+:1+:CиC/]yиXи/q01%#".54>324.#"32>6##"&55463!54633266#"5433232##32''&76654&##"&5546!32''&76'&&##"&5546-2Sl;@nP--Pn@;lS2G%?T.3T;!!;T3.T?%> A} +K)) Q/!  *&   /  $  *B--B**A++A-++))V"  # v"')<=: 0d3"')0i9ff"?td@`|ey+9+++и иer~/ /BY+6/++6и//*и*/69иBaиYu0134332##"5###"543326676"'"&55463754633232''&7>4'&&##"&5546#32''&76674&##"&5546r&&r&&4i&54&##"&5546332''&766'&&##"&5546o&&o&&H/)  z3i'=BB:8 1k3+x/    l | $ o  #)$5 # " ')350 ),*" .0,%]("Mum'Cf;k++8=+/*+k*и/8(и*5иkEk_и_/A]A)9IYiy ]и//,/+h~+#+)6+cU+U\EиE/URиR/\_иhи6и/~0174>32#".732>54.#"354332##"55###"543326676#'"&554637546332%32''&7>54&##"&5546332''&766'&&##"&5546.TtF@sW33Ws@FtT.H%BX3.YE**EY.3XB%wo&&o&&3i'=BB:8 1k3+x/    l | $ o '*A--B*+B--B(,,--ֵ # " ')350 ),*" .0,%]("MtC 3SoXl+ ,+ +Xe q//5L+' +' и / и/',и,/5TиLh01#"54332>76"'"&55463226754633232''&7>4'&&##"&5546!32''&76674&##"&55465))`!GE@ BRNQM ?FG+*  $  /!  **  ~ $  " C  MQP!JNO#"')#UUQ CN"Rb4A\v{*#++@9+R#*9R/F@x'&&##"&5546Y /#- #-V  A}  +I))*."<*1 # # t ')278 .Y&"'),`+-/0"R|D#BNk+@7+MF+M и /Fи/a9a/SMI/ +Pd++;.+.7@и@/Plиd}017463!2#!".57"3!26554&#'6##"&55463!54633266#"5433232''&76654&##"&5546!32''&76'&&##"&5546 , ,I]  6  A}  +I))324.#"32>6##"&55463!54633266#"5433232''&76654&##"&5546!32''&76'&&##"&5546P2Sl;@nP--Pn@;lS2G%?T.3T;!!;T3.T?%X  A}  +I))'&&##"&5546!32''&7>54&##"&5546  C++ S  &  /$1 " 0!  A""  z !Y_]&"^`Y"')%[^]((XWP "'4&##"&5546   w+|   .     %TWS$"n "RI"#-&TUS%"VYU""+#+ +Gи+и-и 5и Oи>X013232#!32##"554&#!"&55463!5!"&55463!>54&##"&5546332!>54&##"&5546  x/)   { *   /     CHF"#)$"DHD"')EGCGKE"<Oe[T+:++E+A:&:6:F:V:f:v::::::: ]A::]::и/?+:9AEE]AE)E9EIEYEiEyEEEEEEE ]g_P+7.+ +7ии %и ?и.H013232#!##"&55!"&55463!>54&##"&5546332!>54&##"&5546!"&5543323!2  +s )   /    6/)   JSP" "NRN"')NRLOVN"')$54&##"&5546  x#-Q /#-  { *  /    9=="T*G ##*T*D#"7z5"')9w5;?>"<`Kû\+wd+C2+wи/и/w и /dи/,dw9A22]A2)292I2Y2i2y2222222 ]\lиl/2C9/A]A)9IYiy ]+V+&+++qh+I++I,и ܸиIи,и5и&=иVSиS/`и`/zиz/01#!##"&55!"&55463!66'4&##"&5546332!>54&##"&554633232##".55463326554&##"&5546332##"33267667672%#".54>327!"&55463!24&#"326#"&554332 x+{ +  /     F  )R!, !,G)  _*:$$:**:$$:* X X0--11--0  " "6z4"')9v4;>=" 9<= V*A$Q*D  *, !,, !+{& ' '&$$% $ <|]m5b%+H9+i+Si9S/ASS]AS)S9SISYSiSySSSSSSS ]AH&H6HFHVHfHvHHHHHHH ]AHH]*9H9*/of+E<+)^+ +Eи)и+и 3и Mи<V013232#!32#!".5546335!"&55463!>54&##"&5546332!>54&##"&5546"3!26554&#  x ,> ,{ *   /       AGD"**"BEC"')ACBFHC"   54&##"&55463!2655,, ,  x+{ ,  /    $qp& ;A@" "93"')68=>="=j j75!"&55463!>54&##"&5546332!>54&##"&5546  x-p t+(q?lR3{ +   /     EML"Db " tGJw  % 4CK&"JLG"')GLHIOJ"75!"&55463!>54&##"&5546332!>54&##"&5546332324.#"32> x7eL-5Wq=76&#!"554335!"&55463!66'4&##"&5546332!>54&##"&554633232#!32O4 ONG  S\^(+[[X'  C{' F { ,  /     xt5  ! $  /:?  # "93"')68=>=" ;A@"ItC_s7&+gl+A7&767F7V7f7v7777777 ]A77]7 7и/<&79]&g9l`иguc/i/2)+`m+!+ и /и/!и/!<и'&&##"&5546332667667>'&&##"&5546332664332##"5#"5543  @K+ GD< R+     (@!    7b)) o"   \"!QQL"FMQ'HLJ"CIK!B " LqW1(*+=D++и/и/!и!/Y*9Y/joYj99и/EX%/%>Y.+e\++oA++% oE<и'&&##"&554633266766766'&&##"&5546332664332##"55#"5543W /#- #-X  HLL$+FD=  `0    F$  4\)) #*7*%$7*) D "   ";;5"6990o,"265  " Lo^-9(++A9&969F9V9f9v9999999 ]A99](99/ >(99\(9b(9j 9и/4+++>++ и /и/> и />#и#/>@и@/+Iи4Qиwиw/016##"&55'"&5546367>'&&##"&55463326766766'&&##"&554633266&&''&&'''&6766'432766'46324332##"55#"5543  !KOO$+C@;  _0   "F$  4>^N68L5eY 0 C6=D/ 0>$ )) "   "<;6"69;0p-"276L3+J H+W? " +oJ  7['k@   81)   D " LuXrCs+ '++} As&s6sFsVsfsvsssssss ]Ass]s<и32#".6##"&55'"&5546367>'&&##"&55463326766766'&&##"&55463326632>54.#"4332##"55#"55430UsD>rX55Xr>DsU0  !JMM$+DB<  _0   "F$  4(CX0+WF--FW+0XC()) *=))>*+>))>"   "<;6"69;0p-"276E( () )G " 4tdYmy +RA+Z_+ns+AAA]AA)A9AIAYAiAyAAAAAAA ]WAR9_gиn{p/v/.%+g`+9+ и /и/9и/9и/%Dи.LкWpv9016##"&5'"&554632766'&&##"&55463327667>'&&##"&554633266##"55#"554334332##"54332<  0v?+:q, A!x   -?! j w $& &&&f"   V"L<"FJK$KJE"@HJ!E " )~DjdQg{/ +L=+hm+|+A/&/6/F/V/f/v/////// ]A//] /9/ 2 /9A==]A=)=9=I=Y=i=y======= ]O=L9|Vи\и\/=qиq/muи|Y//,#+S`+5+un+5и/и/5и/5и/52и2/#@и,HкOY9jиj/016##"&55'"&55463766'&&##"&5546332327667>'&&##"&554633266!2##"554&#!"&5546%##"55#"554334332##"543327 -p6+'&&##"&5546332627667>'&&##"&554633266##"55#"554334332!"&5543323!2'##"54332< -q5+=n0 CC p |+   C" _ k+! !& &/)  &&   ";;7"13u1992"!/,l1p " #)$Dqy1maR+~o+++Rи/и/!Aa&a6aFaVafavaaaaaaa ]Aaa]a)и)/BRa9B/;dRa9Aoo]Ao)o9oIoYoioyooooooo ]o~9oи/и/.+^U+ %+++Urи^z01#!"&55463!26554&#!"&55463!2#!"3!26##"&55'"&55463766'&&##"&5546332327667>'&&##"&554633266##"55#"554334332##"54332y /#- #-9  /w6+;j/ ECm y+   E# ` l+!  & &&&#*=*##4*0 @   "1k*"10d.32,"!/*\. "  D|ddth4+`Q+uz++4и/ к$4h9$/hCиC/F4h9AQQ]AQ)Q9QIQYQiQyQQQQQQQ ]cQ`9pиp/Q~и~/zи/m +@7+e+{+7Tи@\017463!2#!".56##"&55'"&55463766'&&##"&5546332627667>'&&##"&55463326"3!26554&#'##"55#"554334332##"54332 , ,'I .s6+'&&##"&554633263!2655'##"55#"554334332##"54332>,, , .p5+'&&##"&55463326&''&&'''&6766'432766'4632##"55#"554334332##"54332; -r5+'&&##"&5546332667667>'&&##"&554633266#"54332  AH+ JG@ X-     $E&   3))j"  ]"!PQK"FNQ&HLK"CJL!~L[]sib++~w+ b~9i3и3/Ib~9~z/f_+?6+K+?ии/fи/$и$/K.и./KIиI/6Vи_m01326676##"&55'"&5546367>'&&##"&554633267667>'&&##"&5546!"&5543323!2'#"54332 3  '&&##"&55463326766766'&&##"&554633266#"54332W /#- #-X   >F+HGA "d3   !L&   /))#*A*/$A*3 @"  } z"<<7"6:;0q-"286$L|Dqۻu+%++ и /:u9:/Ko 9}и}//z +F=+r+S!+S&и/&+и+/S2и2/S5и5/SPиP/=\иFd017463!2#!".56##"&55'"&55463267>'&&##"&554633226766766'&&##"&554633266"3!26554#"54332 , ,E  @G+GE? !e3     #J%   /  N))f**"  "<;7"69;0r-"377   t L|Dxz+&-++z ии/и/zBиB/X9v9и//}+NE+y+Z)+Z.%и%/.3и3/Z=и=/ZXиX/EcиNk01".5546332!546332#6##"&55'"&5546367>'&&##"&55463326766766'&&##"&5546332663!26557#"54332;,, ,A  >F+HGA "d3   !L&   / 6))]`&`"   "<<7"6:;0q-"286LT T'4&##"&5546   ++   .     %TWS$"n n "RI"#-&TUS%"VYU""<IJ/?/A??]A?)?9?I?Y?i?y??????? ]J#и#/2A2&262F2V2f2v2222222 ]A22]#7и7/K+/&+/ии7и&B013232#!"&55463!>54&##"&5546332!>54&##"&5546      /  >    Ndqk]""67"')-E]jpdO"013232#!"&55463!>54&##"&5546332!>54&##"&5546!2##"554&#!"&5546   +   /    /)   DKI""GIF"')FIFIMF"#)$<E[QJ+0!+;+A0&060F0V0f0v0000000 ]A00]5!09A;;]A;);9;I;Y;i;y;;;;;;; ]]UF+-$+ +-и и 5и$>013232#!"&55463!>54&##"&5546332!>54&##"&5546!"&5543323!2   )   /    2/(   NXV""SXR"')RWRT[S"')$<`EY}ng+0!+P+;+A0&060F0V0f0v0000000 ]A00]5!09A;;]A;);9;I;Y;i;y;;;;;;; ];9/A]A)9IYiy ]FAPP]AP)P9PIPYPiPyPPPPPPP ]K+rc+-$+U+~++ +-и и 5и$>иc_и_/jиrvиv/013232#!"&55463!>54&##"&5546332!>54&##"&5546#".54>32###"&543323326676676%!"&55463!24&#"326#"&554332   )    /    O*:$$:**:$$:*)-%/) !*& ' pr e0--11--0   EMK""IKF"')FJGGKG", !,, !+j#) % & ' '&$$% $ 54&##"&5546#!"&55463!26554&#!"&55463!2#!"3!2   *  /   9 /#-  #-Q  :?>""7~5"')9{554&##"&554646332##".5##".55463326554&##"&5546332##"33267667672"3326554&#   *  /   ,,  (O!, !,E'  w |  :@?""75"')9}5=A@"**) _*J$Z*M  '   <`C˻wT+o\++9+\!и!/3\o9A99]A9)999I9Y9i9y9999999 ]Tdиd/99/A]A)9IYiy ]A]A)9IYiy ]+{N+-$+++i`+ ++-и и 3и$<иNKиK/XиX/rиr/013232#!"&55463!>54&##"&5546332!>54&##"&5546##".55463326554&##"&5546332##"33267667672%#".54>324&#"3267!"&55463!2'#"&554332   *   /     )R!, !,G)  _);&&;((;&&;)B-00..00-MX L  ;@@"">>="')95=BA" `*K$[*N  /-#".-#"-+*((& ' t% $ <|Yi]+D5+ e+Oe 9O/AOO]AO)O9OIOYOiOyOOOOOOO ]AD&D6DFDVDfDvDDDDDDD ]ADD]I5D9kb +A8+Z+ '+Aи /и Iи8R017463!2#!".53232#!"&55463!>54&##"&5546332!>54&##"&5546"3!26554&# , ,>:   *   /      **2 EJH""FJF"')GIEIMG"   54&##"&5546332!>54&##"&55463!2655,, ,   *   /     $qp&~ EJH""FIF"')GHEILG"@j j013232#!"&55463!>54&##"&5546332!>54&##"&5546'.'''&67>7432   )   /    @Q^4 6bUD@Sb7DqS/ 0  JRN""LQL"')LPKLSO" #B7+  !.;E%&G<0 % =MV+ 323232#!"&55463!>54&##"&5546332!>54&##"&55464.#"32>*6Xp:9pX66Xp9;pX5   *   /    .FT')TE,,ET)'UF-+1G//G10H//G FLJ""HKH"')IKFJOI"5!11!!//013232#!"&55463!>54&##"&5546332!>54&##"&5546!3!2#!"&5463!2#!"!2   4   /       , ,    =BB""AB>"')>@?=??"+Z **  Y " `tC '?(+ + A((]A()(9(I(Y(i(y((((((( ]A//#+6и#<01#"54332''&7676&##"554332''&76676&##"5543325))%>V5  `t   sh  `c   /~T6  c d  [~ #epC !;U_"+ иииW//70+ +0Lи7R01%#"54332!2##"554&#!"&5546''&76676&##"554332''&7>76&##"5543325))/) $ fst  ed x  |h  1H1  0 6#)$sW  Kf aB  =AG( "eb !;U+"-+ +A--]A-)-9-I-Y-i-y------- ] W/ +70+0Lи7R01%#"54332!"&5543323!2''&76676&##"554332''&76676&##"5543325))/)  wp  ak x $=V5  Zh  /t~#)$vT  La -]ZS"  6R #eqX =Yrs// sи/6и/и#и#/ -и :и:/ t/:+UN+1+(+NiиUo01#"54332#!"&55463!26554&#!"&55463!2#!"3!2''&676676&##"554332''&676676&##"5543325))  /#-+ #-o sv fa  u  um Wi  0 *8S#*K*9$K*= gR  FY # TB  1y@ # "e|C /Icd// d и /#и/и/ ии/#и/+и e/(+E>+ +>ZиE`01%#"54332463!2#!".57"3!26554&#''&76676&##"554332''&7>76&##"5543325)) , ,]]  pst  ed x  |h  1H1  0  }**   sW  Kf aB  =AG( "e|C '1H_)+2<+ +)ии #и0A<<]A<)<9<I<Y<i<y<<<<<<< ] a/, +F?+(+?VиF\01#"54332".546332!546332#%3!2655''&76676&##"554332''&76676&##"5543325)),, ,U    df x /  `a  0 &4Vjh&l la  Ic #% <}M "ew(C]i7D+b)+A7&767F7V7f7v7777777 ]A77]D79bhbkd/A:+:TиAZ01%'.'''&67>7632''&76676&##"554332''&76676&##"554332#"54332<2c 7cSC?Rb8CpS0 0 8!=U6 cg x /$>V4  ^i   /)) Lb ".;D$$E<0 % 324.#"32>''&76676&##"554332''&7>76&##"5543325)).Oi+%,+ +1Fи>T01676'&&54332#"5433232##32##"554&##"&5546!32##"554&##"&5546^^&hrt1-") ,)) /$  B/$     '*C "9#)'#)'1GS+<7+0+0%и0I"/A2+'.++01676'&&54332#"5433232##!"&5543323!2^^  'jrr/-") ,)) /)  X    '*` "#)$|5Ki3,++J9+J и /J?иJM76676'&&54332#"5433232##"3326554&# ,,6 %K!, !,D" ^_'krr/-") ,))  t **) N*9$I*<     '*3 "k   |#=Sm;4++ +Aи Gи Qи UD/ ++IP+$1+017463!2#!".57"3!26554&#%676'&&54332#"5433232## , ,]]  1^^&hrt1-") ,)) **      '*L "|%?U}=6+++ и$иCиIиSиWF/ ++KR+&3+01".546332!546332#%3!2655676'&&54332#"5433232##0,, ,V  ^^&hrt1-") ,)) jh&k kO   '*E i"w(BX]Y/F/Y9и9/FW9W99@WLиWZI/NU+)6+01%'.'''&67>7632%676'&&54332#"5433232##.2c 7cSC?Rb8CpS0 0 ^^&hrt1-") ,))  Lb ".;D$$E<0 % '4632676'&&54332#"5433232##a?YM11NYA b[ 0 "- .#/ ZU ^^&hrt1-") ,)) ![56Y U34T#$ 0e  @9/ :HR)   P'     '*L }"p'AW?8+ +VE+EA&6FVfv ]A]VKиVYH/#++MT+(5+BиB/01%#".54>324.#"32>676'&&54332#"5433232##,.OiUJиUXG/$+LS+'4+01%'&&'''&76676&#!"5543!2%676'&&54332#"5433232##-Q@@= @LDT Vv P^^&hrt1-") ,)) `-.  " 1 7  c< # 2   '*E "a+E[gyh/I/h<и32676'&&54332#"5433232##4.#"32>7!"&55463!2'#"&5546332%=R--S>%%>R--R>%^_'krr/-") ,)) m*9 9**9 9* L -/  // .   '*0 "   ltd3U1*+++и и5/ /'++0134332##"5###"54332676'&&54332&&&&HUF?Y-") lPG'*) pd1IwG@++!&+и&и/!и& и /!K/#/*++2=+01354332##"55###"54332!2##"554&#!"&5546'676'&&54332&&&&k /)  UF?Y-") #)$'*D 1IeG@+' +++и иK/+++2=+0134332##"5###"54332!"&5543323!2676'&&54332&&&&/)  zUF?Y-")  ~9S$)$'*W qy1Mec\+*+BG+94+4и/и/9!иB2и4?и9g6/.+ %+3@++NY+01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332676'&&54332y /#-  #-O &&&&LUF?Y-") #*F*4$F*8 *ƹ'*< |d#?WUN++49++&++ и&и/4$и&1и+Y(/ ++%2+@K+01%463!2#!".57"3!26554&#354332##"55###"54332676'&&54332 , ,=]  d&&&&LUF?Y-") x**   '*D |d7AYWP+9+,1+#+9 ии/#и,и)и@и@/#[ /<+8+*+BM+01".546332!546332#354332##"55###"543323!2655676'&&54332[,, ,u&&&&  UF?Y-") jh&(Gf fY'*; wD\[ZS+++и к0S9^/+EP+01354332##"5###"54332'.'''&67>7632%676'&&54332&&&&a2c 7cSC?Rb8CpS0 0 UF?Y-")  Lb ".;D$$E<0 % K+d/+/и/dY01&&''&&'''&6766'4327>'4632354332##"55###"54332676'&&54332?YM11NYA b[ 0 "- .#/ ZU &&&&LUF?Y-") ![56Y U34T#$ 0e  @9/ :HR)   P'  '*D pj'C[ͻYR++B++94+9 и /A&6FVfv ]A]4и/B1и4?и9]6/+#+3@+DO+0174>32#".732>54.#"%#"54332354332##"55#%676'&&54332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#M&&&&LUF?Y-") '+C/.C,,E..E)--..w'*D t9a70++ иܸии;// -+ ++01#"5433232##32##676'&&54332))  `^%kvu--") ~  S  (* p9O+8+8#и3ܸ(и8-и8>иDиLиL/8Q /A/%,+;H+/6+6и/ 01676'&&54332#"5433232##32##!2##"554&#!"&5546^^&hrt1-") ,))  /) $ y   '*L !x!u7#)$;Qq92+G@++ иܸииS/K<+ + /++01%#"5433232##32##%676'&&54332!"&5543323!2))  ^^  'jrr/-") X/)  s!!    '*` %#)$q1KkIB+*+jO+Oи/и/j!и!/jUиOeܸZиj_иjmR/.+ %+W^++ah+2?+01#!"&55463!26554&#!"&55463!2#!"3!2676'&&54332#"5433232##32##6  /#-+ #-o ^_'krr/-") ,))  #*K*9$K*=    '*6 ww!j!u|#=];4++ +Aи GиWܸLи Qи [и _D/ +IP++SZ+Z$и$/1017463!2#!".57"3!26554&#%676'&&54332#"5433232##32## , ,]]  1^^&hrt1-") ,))  **      '*L !x!rp'Gaӻ_X+ +F+++A&6FVfv ]A]F1и+Aܸ6иF;иFc./#+3:++=D+DHиH/U01%#".54>324.#"32>7#"5433232##32##%676'&&54332,.Oi76'&&54332)) 1ige-)ltt0-") !! $    '* pC/CyD/$/Dи/$и/$$0и6и$<иE!/3/0=+(+ +01676'&&54332!2##"554&#!"&554654332##"5!"5543^^&hrt1-") /) $ )) y   '*L #)$ $ h-NdŻ+TY+C=+Cи/C C&и&/'A==]A=)=9=I=Y=i=y======= ]1=C9Cf/'+P]+ +01676'&&54332%54332##"55!"5543&&'''&766'46232##"554&##"&5546^^&hrt1-") )) =NI6  RO. UAe/$  |   '*L ˸ $ -&N00S* :e  *?i$ P#)$b/ES-&+;4++ иG/?0+ +#+0154332##"5!"5543676'&&54332!"&5543323!2)) ^^  'jrr/-") e /)  K $     '*a &#)$qX1K_IB+*+"+и/Lи"RиXи"aO/.+ %+LY++2?+01#!"&55463!26554&#!"&55463!2#!"3!2676'&&54332%54332##"55!"5543X  /#-+ #-o c\&iss0-") )) #*K*9$K*=    '*6 6 $ qDSmkd+=*++u+u и /"dk9"/2и2/"Enиn/zиz/=~и~/uq/ +'@+n{+7.+Ta+7и/ и/ иHиH/MиM/.01%46332##".5##".55463326554&##"&5546332##"332677>76676'&&54332%54332##"55!"5543"3326554&##,,6 %K!, !,D" c\&iss0-") ))  t **) N*9$I*<     '*6 1 $ V   iC]wun+G4++~y+ иyи/~к,nu9,/<ии DиJи SA/ +>K++$1+01%463!2#!".57"3!26554&#%676'&&54332%54332##"5!"5543 , ,]]  ^^&hrt1-") )) **      '*L γ  $ |C%?S}=6+++ и$и@иFиLиUC/ ++@M+&3+01".546332!546332#%3!2655676'&&54332%54332##"55!"5543R,, ,U  ^^&hrt1-") )) jh&l lN   '*E Ǯ/ $ w(BV]W/D/W9и9/DI9I99@DOиIXF/CP+)6+01%'.'''&67>7632%676'&&54332%54332##"5!"554382c 7cSC?Rb8CpS0 0 ^^&hrt1-") ))  Lb ".;D$$E<0 % '4632676'&&54332%54332##"55!"5543?YM11NYA b[ 0 "- .#/ ZU o^^&hrt1-") )) ![56Y U34T#$ 0e  @9/ :HR)   P'     '*L γ $ pK'AU?8+ +HC+Cи/A&6FVfv ]A]CNиHWE/#+BO++(5+01%#".54>324.#"32>676'&&54332%54332##"5!"5543K.Oi32676'&&54332%54332##"55!"55434.#"32>7!"&55463!2'#"&5546332%=R--S>%%>R--R>%c\&iss0-") )) *9 9**9 9* L -/  // .   '*6 , $ t   ltd7^5.+++ и9//EX/>Y ++ 014332##"5#"5543##"54332676'&&54332q&& &&}UF?Y-")  $ ~'** pd-AMk+$+4/+BG+BиG и //:иBO/J/.;++!+01%!2##"554&#!"&5546'676'&&54332%54332##"5#"5543##"54332 /)  $UF?Y-") && &&#)$'*D ѡ $ )AM]?8+++BG+ иBOJ/#+ +*5+0154332##"5#"5543!"&5543323!2676'&&54332##"54332q&& /)  pUF?Y-") &&<b $ J$)$'*] qy1I]iG@+*+PK+^c+cи/и/^!иKVи^kf/.+ %+JW++2=+01#!"&55463!26554&#!"&55463!2#!"3!2676'&&54332%54332##"55#"5543##"54332y /#-  #-O lUF?Y-") && &&#*F*4$F*8 '*< ʔF $ |d#;O[{92++B=+PU+P иUи/=HиP]X/ +<I++$/+01%463!2#!".57"3!26554&#%676'&&54332%54332##"55#"5543##"54332 , ,:]  UF?Y-") && &&x**   '*D ͠& $ |d%=Q];4++D?+RW+ иWи/RиW$и$/?JиR_Z/ ++>K+&1+01".546332!546332#%3!2655676'&&54332%54332##"55#"5543##"54332[,, ,=  UF?Y-") && &&jh&f fY'*; ƙF $ w(@T`S>7+GB+UZ+7U9BMиUb]/AN+)4+01%'.'''&67>7632%676'&&54332%54332##"55#"5543##"54332B2c 7cSC?Rb8CpS0 0 UF?Y-") && && Lb ".;D$$E<0 % '4632676'&&54332%54332##"55#"5543##"54332?YM11NYA b[ 0 "- .#/ ZU ZUF?Y-") && &&![56Y U34T#$ 0e  @9/ :HR)   P'  '*D Ϟ+ $ pj'?S_Ż=6++FA+TY+T и /A&6FVfv ]A]Yи/ALиTa\/+@M+#+(3+0174>32#".732>54.#"676'&&54332%54332##"55#"5543##"54332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#cUF?Y-") && &&'+C/.C,,E..E)--..>'*D Ϟ* $ tC5a6// ии6,и,/37/ /+)+ +0154332##"5!"5543!5!"5543676'&&54332))  iX(jst1-") = #  #   '* pC9OP// ииP0и0/7;и;/>иDиQ/A/EX/ >Y;H+ +-+0154332##"55#"554335#"5543>76#'&&54332!2##"554&#!"&5546)) +]]Y(  (afd+-") %/) $ ` # ^ #     '*T #)$b9Oe70+E>++ ииQ/I:++ +-+0154332##"5#"554335#"5543>76#'&&54332!"&5543323!2)) +]]Y(  (afd+-") m /)  o{/ # q #     '*~ C#)$qX1Mkib+*+"+и/2и"8и>иFи"m5/.+2G+ %+F?++N_+01#!"&55463!26554&#!"&55463!2#!"3!254332##"55#"554335#"5543>76#'&&54332X  /#-+ #-o Z)) +]]Y(  (afd+-") #*K*9$K*= R7 # T #     '*F |C#?][T++ +$и *и0и8и _'/EX$/$ >Y ++81+@Q+$901%463!2#!".57"3!26554&#54332##"55#"554335#"5543>76#'&&54332 , ,]]  )) +]]Y(  (afd+-") **   `  # ^ #     '*T |C7A_]V+9++9 ии"и(и0и@иa/<+1+8+0)+BS+01".546332!546332#54332##"55#"554335#"55433!2655>76#'&&54332R,, ,))  +]]Y(  (afd+-") jh&)[/ # [ # l lC    '*O p<Xvhtm++4++A++]A+)+9+I+Y+i+y+++++++ ]+49A&6FVfv ]A] 9и/41и1/4=и=/4C4IиI/>Jи4QиQ/>Rи4x@/EX=/= >YQJ+j/+/и/=RjY01&&''&&'''&6766'4327>'463254332##"55#"554335#"5543>76#'&&54332?YM11NYA b[ 0 "- .#/ ZU )) +]]Y(  (afd+-") ![56Y U34T#$ 0e  @9/ :HR)   P' ,` # ^ #     '*T pK'Caһ_X+ +.)+)и/A&6FVfv ]A])4и)<и.c+/EX(/( >Y#++<5+DU+(=01%#".54>324.#"32>54332##"55#"554335#"5543>76#'&&54332K.Oi76#'&&54332 $ /))) +]]Y(  (afd+-")  #  # D~'*! 3IUo1*+?8++JO+ ииJWR/C4++ +'+0154332##"5#"554335#"5543676'&&54332!"&5543323!2'##"54332q&& UEFW-") /)  &&a # k #   '*t >$)$<)S*//*и/и// +#+и01!!2#!"&55463!5#"&543323!2,  /)   :""');$+7++&и(017463!2#!".5!!2#!"&55463!5#"&5543323!2"3!26554&# , ,>\  /)    b**|""|')$   7632"  /)   -p t+*nCqR1 0 ""')$ DZ " mEGr % 9HP' 32!!2#!"&55463!5#"&5543323!24.#"32>*@^m-EE@1@^m,-m_?  /)  F6LQQK55KQRK63@$  %2"2?$ $@}""')$P!''! ((<|IM?8+J +"3+K+;/+ +C +#*+и и#2и 4иJи L01!"&5546335#"&55463!2##32!!2#!"&55463!5#"&5543323!235#1z w ^p  /)  T   " " D|""|')$+#+i`+ +и01!!2#!"&55463!5#"&5543323!2#".54>327!"&55463!24.#"32>!"&55463!2"  /)  I(@S*+SA((AR+*SA(V t-88--88-  'e""e')$..-.|  ?t8N.'+"+M<+MBиMP?/9/"+2+DK+"и/и/"и/#01#>76"'"&554632675#"&543323!2#"5433232##F!HGC"NPP$LRN '432766'4632#"5433232##4H:  Qd &i/) <  ?WL7o+XB 6I, 0 D; ?B/ VP R)) w" #x')$P/-J" >V,K$$ 8@K.   3d){S  !Bf"  w"ItC8Dq' +4+C<+CF?/9/4+*+ и /4и/4и/201%6"'"&554632675#"&543323!2##>#"54332!NST(QUP >EG n/) \ "MLF)) #  "')&$ ~Mb.DPi+:3+,+OH+ORK/>/+ +"+*и,и,/016##"&55463!5#"&5543323!2##66!"&5543323!2'#"54332  Xg j/) < L/)  "))"" #')$ k$)$hMtX1`lQJ+^E+"+Qи/Qи/и/Q*dи"jи"ng/.+ %+TG++E<+G\иE^и^/01#!"&55463!26554&#!"&55463!2#!"3!26##"&55463!5#"&5543323!2##66#"54332X /#- #-[  Xg j/) < L))"*D*4"B*7 " #n')$l a$M|DBR^3,+@'+]V+3и/] и /3FVNиN/]`Y/K +C+6)+'+)>и'@и@/017463!2#!".56##"&55463!5#"&5543323!2##66"3!26554#"54332 , ,I  Xg j/) < L  N))d**" #x')$v   pM|CJT`;4+H/++;и/;L иSиXи^иb[/O+K+>1+/&+1Fи/HиH/&UиU/01".5546332!546332#6##"&55463!5#"&5543323!2##663!26557#"54332?,, ,@  Xg j/) < L  7))Z]&" #|')$z U UMv-P\_+++[T+?[9[^W/!+ +)и+и+/016##"&55463!5#"&5543323!2##66'&&'''&7>7432#"54332  Xg j/) < LE &o v,*pHsQ+ 0 ))5#  #')$ }  :_"  iABk" %9DJ% #V<)-Y+*+++/ +#+ии*и,01#!2#!"&55463!5#"&543323!235#,  :/)   a:""');$7632'35#"  2/)   -p t+*nCqR1 0 ""')$ DZ " mEGr % 9HP' s32#!2#!"&55463!5#"&5543323!24.#"32>35#*@^m-EE@1@^m,-m_?  2/)  F6LQQK55KQRK63@$  %2"2?$ $@""')$N!''! ((K')$<|/EF/ /F4и4/;и/; и / ;1и1/7/-+$+?0+ + и 01#!"&5546335!"&55463!2#!32#!"3!2!"&5543323!2, , ,{ R x   /)  v**z""z !  ')$]  _/)  `*z""z*   _')$75!"&55463!2#!!"&5543323!2)-p t+(q?lR3{ R x/)  sDb " tGJw  % 4CK&g""n9')$75!"&55463!2#!4.#"32>!"&5543323!2*@_m,EE@26Ra,{ R x,bR5I6MQQK66KQQM6A/)  3?#  $2"-<$u""u$<.!&&! ''')$ItC;QoG@+%+ + иS // +7&+K<+&и/&+и+/01%4332##"55#"554376##"&5'"&554632>7>'!"&5543323!2)) {  @K+ GD< QUO#RRJQ/) K } " "   6" [')$Lo8Lty+W^+@E+y@9 ^W9E9и@7>'!"&5543323!2>^N68L5eY 0 C6=D/ 0>$ )) x AJ+BA: OTO#NNJT/) 3 L3+J H+W? " +oJ  7['k@   81)   0 " z"   "B')$4tdAWMF+*1+++ иY// +?2+QB+2)и)/25и5/?<и7>'!"&543323!25))  ?E+!LH> QUO#TSJJ/) K ~"  '" h')$Q[ 2H^}TM+/+ +T7и7/T> `/;4+XI+; и /!ܹ.и/4B01%#"54332%"&55'"&554632>7>76#!"&5543323!2!"&5543323!25))8 LJB MSN!NPL 7>'!"&5543323!2#"54332W /#- #-X   @G+GE? MTP'SPJL/) I ))#*2* $2*$  " o l"D')$L|D#KamWP+.5+le+Wи/l и /Weи/loh/ ++G1+[L+G6-и-/6;и;/GBиB/017463!2#!".57"3!26554&#6##"&55'"&554632>7>'!"&5543323!2#"54332 , ,E]  0  @G+GE? MTP'SPJL/) I ))H** g g 5" ~ {"E')$/L|C%McoYR+07++Yи/Y и$иgиmиqj/ ++]N+I3+I8/и//8=и=/IDиD/01".5546332!546332#%3!26556##"&55'"&554632>7>'!"&5543323!2#"54332:,, ,= G  @G+GE? MTP'SPJL/) I ))ZX&Q Qx" ~ {"B')$]  _/)  S`*}""}*   _')$}!"&5543323!225#66*@_m,EE@2G3 R -"I6MQQK66KQQM6A/)  = > 3?#  $2"4@"" !*!&&! ''')$<%%+/ ++01%#!"&55463!2'!"&543323!2  R /)   A""')H$и>/2BиB/MCиC/2QиQ/2YI/>+5+QB+,#+ +01#!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!2!"&5543323!2  R z /#-  #-Q /)  ~""#*J*:#J*= 7')%$N*C  6')%g""#)*#$N*A  4')%e""   <|#3IJ///J8и8/?и//и///и/?и/4и4/?5и5/CиC/K;/,+$+C4+ +01#!"&55463!2463!2#!".57"3!26554&#!"&5543323!2  R  , ,>]  _/)  @""**   D')$7632%!"&5543323!2#!"&55463!2 -p t+*nCqR1 0  /)   R  DZ " mEGr % 9HP' ')$r""324.#"32>  R /)  2AEEEEA22AEDEF@2I6MQQK66KQQM6@""p')$c"3''3"!3&&4"", ,""+ +76&#!"5543!2!"&5543323!2#!"&55463!2L3 HIF  !V[\'QV  Js$ F Ӗ/)   R `/  !  !6 +37  # ')$q"#<|5EIm+$+F +G+'/+ +/ +C:+и иFи H01!"&5546335#"&55463!2##32!"&5543323!2#!"&55463!235#1z w ^p/)   R  ! ! :')$u""pItC ,BOC// C1и1/8.и./ D//(+<-+01#"543326'"&554632>7>'!"&543323!25))  !NST(SVN QUO#WVNM/) \ ~3"  " ')?$MW !7OW-&+ +-и/- Q/ +KB+1"+01%#"54332!"&5543323!2!"&5543323!26##"&5546332>5))/)  /) < i  Xg 9upf_m$)$ ')$Z" #MtX =SkIB+ +Iи/иI#и#/$и$/ -иI6 m/:+1+(+M>+g^+01#"54332#!"&55463!26554&#!"&55463!2#!"3!2!"&5543323!26##"&5546332>5)) /#- #-[ /) < i  Xg 9upf$j"*D*4"B*7 ,')$E" #tC '=(// (и/% )// +01#"54332>76'&&543325))1opk."k|8-") ~S   (* pC )?c@// @ и /'+и+/ .и4и A/1/+8+ +01%#"54332%>76'&&54332!2##"554&#!"&55465))0lmi-  $nz{2-") /) $     '*L #)$b !?A=6++ + A/ +"3+01%#"54332!"&5543323!2>76'&&543325))/)  0lmi-  $nz{2-") |v#)$    '*` qX =YqWP+6+ +и#и#/ -и [/:+1+(+>M+01#"54332#!"&55463!26554&#!"&55463!2#!"3!2>76'&&543325))  /#-+ #-o 1kli.%my{2-") &4W#*K*9$K*=    '*6 qDSo׻mf+=*+s++ и /"fm9"/2и2/"E{и{//x +'@+7.+Tc+7и/ и/ иxHиH/xMиM/.p01%46332##".5##".55463326554&##"&5546332##"332677>76>76'&&54332"3326554#"54332#,,6 %K!, !,D" 1kli.%my{2-")  t H))**) N*9$I*<     '*4   m/|C )=M[' +A*+ + 3иIи O/F7+->+ +01%#"54332%>76'&&54332463!2#!".57"3!26554&#5))0lmi-  $nz{2-")  , ,]]      '*L **   |C '1MkKD+)+ +)ии #и0и O/, +(+2A+01#"54332".546332!546332#%3!2655>76'&&543325)),, ,U  2mlh,#ly|4-") !/[jh&l lN   '*E w(FRKS/J/S=и=/JQ=Q9=DQTM/):+01%'.'''&67>7632%>76'&&54332#"5433282c 7cSC?Rb8CpS0 0 0lmi-  $nz{2-") J)) Lb ".;D$$E<0 % 324.#"32>7#"54332%>76'&&54332K.Oi +?+LE+LOH/+ + 1+и и>и @01!"&5546335#"&55463!2##32>76'&&5433235#%#"54332a n Aj0lmi-  $nz{2-") )) ! !     '*L 8o}t#9a:/'/: и /и/'8-и8;*/$/++/6+01%'&&5463!2#!">76#"5433232##%q}z--! ,X  0kmi/))   (** $ c   ~"*p"8NO/-/O и /и/-$и$/-(-<и(Bи(Lи(P*/?/+$1+DK++01'&&5463!2#!"3676!2##"554&#!"&5546%#"5433232##!m|}1-! ,U  g^ T/) $ /)) g  (** $  #)$8"p"8Nd +>C+7&+7,иKиK/7Sи&YиY/7f)/@/V/+:G+.5++:OиG]01'&&5463!2#!"3676#"5433232##32##"554&##"&5546!32##"554&##"&5546!m|}1-! ,U  g^ ~)) /$  B/$  r  (** $  "=#)'#)'$:P] +F?+9(+9.и9R+/J;++07++01'&&5463!2#!">76#"5433232##!"&5543323!2(pzx.-! ,U  .ili0 ~)) /)  A   (*/* $   "$)$|@Vs5&++UD+U и /UJиUXG/++0+ +LS+8#+01#!"&55463!2#!"3!2'&&5463!2#!"32>76#"5433232##6 , , 5  !m|}1-! ,U  3ljf- ~)) v** !    (** $    "q1WmL=+*+l[+[и/и/l!и!/laиlo^/.+BG+ %+cj++O:+01#!"&55463!26554&#!"&55463!2#!"3!2'&&55463!2#!">76#"5433232##6  /#-+ #-o %p{y--! ,T  0ijh/ )) #*K*9$K*=    (** $     "p$g}ӻ +Q>+k+6 96/FиF/6Yn/-/0//+;T+sz+++BиB/Jи0_\и\/_bиb/0и/01'&&55463!2#!">76###.55463326554&##"&5546332##"332676676#"5433232##32##"54&##"&5546%p{y--! ,T  0ikh/   'N!, !, '% 2)) /$    (** $     N*9$I*< m".#) #q8x-+bO+|++G-9G/WиW/Gjи/ +#(+Le++\S+0+\и/ >и>/ Aиmиm/rиr/Sy01%46332##".5'&&55463!2#!">76##".55463326554&##"&5546332##"332677>76"3326554#"5433232## ,,%p{y--! ,T  0ikh/ 6 %K!, !,D" w t >)) **  (** $     N*9$I*<     o"i@5&+lY+++ ии/и/Q&59Q/aиa/Qtии///K++0+Vo+++f]+8#+f и /fи/KHиH/wиw/01".5463323546332#'&&55463!2#!">76##".55463326554&##"&5546332##"33267667672#"5433232##332655N'' ,=%p{y--! ,T  0ikh/   $J!,~ !,?$  ,))  o 1tr&)  (** $      L*7$G*:  m"t t`$fz +P=+q+~+5 95/EиE/5X~9/A]A)9IYiy ]gAqq]Aq)q9qIqYqiqyqqqqqqq ]ии/g/l+\/++v+++JA+++/,и,/v9и9/SиS/01'&&55463!2#!">76##".55463326554&##"&5546332##"33267667672%#".54>32#"5433232##!"&55463!24&#"326#"&554332%p{y--! ,T  0ikh/   &L!, !,B%  W*:$$:**:$$:*5)) 2X X0--11--0    (** $      V*A$Q*D  *, !,, !+M"& ' '&$$% $ |#H^=.++]L+] и /Lи/]Rи]`O/ +38++T[+@++017463!2#!".57"3!26554&#''&&5463!2#!"32>76#"5433232## , ,]]  *!m|}1-! ,U  3ljf- ~)) **    (** $    "|AKa6'+C+`O+C иOи/`и/OJиJ/`Uи`cR/F+,1+B+W^+9$+01".546332!546332#'&&5463!2#!">763!26557#"5433232##1,, ,= %p{y--! ,T  0ikh/   6)) jh&   (** $     l l"w$Mcwd/Q/d и /и/Qb9 b9>и>/bWиbeT/+Y`++01'&&5463!2#!"32>76'.'''&67>7632#"5433232##!m|}1-! ,U  3ljf- f2c 7cSC?Rb8CpS0 0 )) Z  (*!* $     Lb ".;D$$E<0 % '4632'&&5463!2#!"32>76#"5433232##a?YM11NYA b[ 0 "- .#/ ZU !m|}1-! ,U  3ljf- ~)) ![56Y U34T#$ 0e  @9/ :HR)   P'   (** $    "p'LbA2+ +aP+PA&6FVfv ]A]aVиadS/#+7<++X_+D/+01%#".54>324.#"32>'&&5463!2#!"32>76#"5433232##,.Oi76#"5433232##-Q@@= @LDT Vv P %p{y--! ,T  0ikh/ ~)) `-.  " 1 7  c< # 2   (** $     "a+Qgst/U/t7и7/Uf7f97Fи/Fи/F;и;/f[иfuX/<A+' +]d+rk+I4+01%'.'''&76676&#!"5543!2'&&55463!2#!">76#"5433232####"554332!N-GIH  $Y]Z&GU  [  O '* %p{y--! ,T  0ijh/ )) y()  !  ) Q0 ! !Z   (** $     "&U9Ocq#.+Z +P+APP]AP)P9PIPYPiPyPPPPPPP ]=P9=/NCAZ&Z6ZFZVZfZvZZZZZZZ ]AZZ]N@/_+$)+U+EL+md+{r+1+{:и:/01#".54>32'&&55463!2#!">76#"5433232##4.#"32>7!"&55463!2'#"&5546332%=R--S>%%>R--R>%b%p{y--! ,T  0ijh/ )) m*9 9**9 9* L -/  // .   (** $     "   l{td;_3$+++и и=/ /).+6!++0134332##"5###"54332'&&5463!2#!"676&&&&rF_-! ,  WNlPG(** $ j  opd;Q2#++AF+иFи/AиF и /AS/C/(-+=J++5 +01354332##"55###"54332'&&5463!2#!"3676!2##"554&#!"&5546&&&&-! ,  PD  /)  o %(** $  #)$o;Qo2#+G@+++и иS/K<+(-++5 +0134332##"5###"54332'&&5463!2#!"3676!"&5543323!2&&&&-! ,  PD Z/)   zAWU %(*&* $  #$)$oqy1MmdU+*+BG+94+4и/и/9!иB2и4?и9o6/.+Z_+ %+3@++gR+01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332'&&5463!2#!"3676y /#-  #-O &&&&-! ,  MH #*F*4$F*8 ( %(** $  o|d#?_VG++49++&++ и&и/4$и&1и+a(/ +LQ++%2+YD+01%463!2#!".57"3!26554&#354332##"55###"54332'&&5463!2#!"3676 , ,=]  d&&&&-! ,  PD x**   t %(** $  o|d7XbO@+Z+,1+#+Z ии/#и,и)иaиa/#d /]+EJ+Y+*+R=+01".546332!546332#354332##"55###"54332'&&5463!2#!"36763!2655[,, ,u&&&&K\-! ,  QE   jh&( (* * $  f fow;de2#+++и кP#9f/(-++5 +01354332##"5###"54332'&&5463!2#!"3676'.'''&67>7632&&&&-! ,  PD ,2c 7cSC?Rb8CpS0 0 b %(** $   Lb ".;D$$E<0 % K+r]+01&&''&&'''&6766'4327>'4632354332##"55###"54332'&&5463!2#!"3676?YM11NYA b[ 0 "- .#/ ZU &&&&-! ,  NH ![56Y U34T#$ 0e  @9/ :HR)   P'  t %(** $  opj'Cc׻ZK++B++94+9 и /A&6FVfv ]A]4и/B1и4?и9e6/+PU+#+3@+]H+0174>32#".732>54.#"%#"54332354332##"55#''&&5463!2#!"3676.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#M&&&&-! ,  PD '+C/.C,,E..E)--..\ %(** $  }t#Ck +B'+B-и'=ܸ2иB7иBE*/$/++/6+9@+01%'&&5463!2#!">76#"5433232##32##%q}z--! ,X  0kmi/))    (** $ c   ~  tC#7e8/%/8 и /и/%*%0и*9'/-/++$1+01%'&&5463!2#!"32>7674332##"5!"5543"p~1-! ,X  7sne*[))  (** $ _   3 $ pC"8LM/-/M и /и/-$и$/-(-9и(?и-Eи(N*//T/+8E+#0++8MиE[01'&&5463!2#!"676754332##"55!"554332##"554&##"&5546!32##"554&##"&5546&hrs1-! ,Q  `_ v)) /$  B/$  Z (** $    $ #)$#)$b"6L] +B;+)$+$/и)N&/F7++#0++01'&&5463!2#!"676754332##"5!"5543!"&5543323!2&ist1-! ,R  `_ s)) Z /)  6 (*/* $   {h $ g#)$|S>Rs5&++E@+E и /@KиETB/++0+ +?L+8#+01#!"&55463!2#!"3!2'&&5463!2#!"676754332##"55!"5543S , , 5  &hqs1-! ,Q  `_ w)) v** !   (** $     $ qX1ThK<+*+"+и/Uи"[иaи"jX/.+AF+ %+Ub++N9+01#!"&55463!26554&#!"&55463!2#!"3!2'&&5463!2#!"676754332##"55!"5543X /#-* #-o &hqs1-! ,Q  `_ w)) #*K*9$K*=  (* * $   7 $ qD6v-+`M++~+~ и /E-9E/UиU/Ehwиw/и/`и/~z/ +#(+Jc+w+ZQ+0+Zи/ <и7654332##"55!"5543"3326554&##,,h&hqs1-! ,Q  `_ 6 %K!, !,D" ))  t ** (** $    N*9$I*<  2 $ p   iC>5&+jW+++ ии/и5OиO/5_и_/O`и`/5rиjи/и///I++0+Tm+++d[+8#+d и /dи/IFиF/uиu/01".5463323546332#'&&5463!2#!"676##".55463326554&##"&5546332##"3326766767254332##"55!"5543332655o'' ,l&hqs1-! ,Q  `_ {  $J!,~ !,?$  )) k o 1tr&  (** $    L*7$G*:  - $ t t|C#FZw=.++ +Gи MиSи \J/ +38++GT+@++01%463!2#!".57"3!26554&#''&&5463!2#!"676754332##"55!"5543 , ,]]  [&hqs1-! ,Q  `_ w)) **    (** $    $ |C>H\5&+@++@ иGиIиOиUи^L/C++0+?+IV+8#+01".546332!546332#'&&5463!2#!"6763!265554332##"55!"5543U,, ,l&hqs1-! ,Q  `_   )) jm& (** $   l l1 $ w"K_w`/M/` и /и/MR7 R9<и763254332##"5!"5543&ist1-! ,R  `_ Q2c 7cSC?Rb8CpS0 0 )) B (*#* $    Lb ".;D$$E<0 % 324.#"32>'&&5463!2#!"676754332##"55!"5543K.Oi/++"/+01%'&&5463!2#!"67674332##"5#"5543##"543321#U]a/-! ,  WNE&& && (** $ j  2 $ ~opd5IUu+<7+JO+J$иO*и*/7BиJW'/R/ +!.+6C++01'&&5463!2#!"3676!2##"554&#!"&554654332##"55#"5543##"54332-! ,  PD  /)  h&& &&^ %(** $  #)$t $ o3IUg+?8+&!+JO+!,иJWR/C4+ + -++01'&&5463!2#!"3676754332##"5#"5543!"&5543323!2'##"54332-! ,  PD [&& /)  &&D %(*&* $  J $ j$)$oqy1QeqH9+*+XS+fk+kи/и/f!иS^иfsn/.+>C+ %+R_++K6+01#!"&55463!26554&#!"&55463!2#!"3!2'&&5463!2#!"3676754332##"55#"5543##"54332y /#-  #-O -! ,  MH [&& &&#*F*4$F*8  %(** $  K $ o|d#CWc:+++JE+X]+X и]и/EPиXe`/ +05++DQ+=(+01%463!2#!".57"3!26554&#''&&5463!2#!"3676754332##"55#"5543##"54332 , ,:]  -! ,  PD [&& &&x**   %(** $   $ o|d<FZf3$+>+MH+[`+> и`и/[и`EиE/HSи[hc/A+).+=+GT+6!+01".546332!546332#'&&5463!2#!"36763!265554332##"55#"5543##"54332[,, ,K\-! ,  QE   && &&jh& (* * $  f fF $ owH\h]+OJ+]b+4]9JUи]je/ +IV++01'&&5463!2#!"3676'.'''&67>763254332##"55#"5543##"54332-! ,  PD .2c 7cSC?Rb8CpS0 0 +&& &&L %(** $   Lb ".;D$$E<0 % YVA+NIN]j01&&''&&'''&6766'4327>'4632'&&5463!2#!"3676754332##"55#"5543##"54332?YM11NYA b[ 0 "- .#/ ZU -! ,  NH [&& &&![56Y U34T#$ 0e  @9/ :HR)   P'  %(** $  + $ opj'G[gϻ>/++NI+\a+\ и /A&6FVfv ]A]aи/ITи\id/+49+HU+#+A,+0174>32#".732>54.#"'&&5463!2#!"3676754332##"55#"5543##"54332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#-! ,  PD [&& &&'+C/.C,,E..E)--.. %(** $  ! $  tC?s@// ии@&и&/5*и*/A/ /+0+8#++ +0154332##"5#"554335#"5543'&&5463!2#!"32>76)) "p~1-! ,X  7sne** # #  (** $ X   b">To +JC+)$+$/и$7и)V&/N?++#8++70+01'&&5463!2#!"67654332##"5#"554335#"5543!"&5543323!2&ist1-! ,R  `_ s)) 5 /)  , (*9* $   { # i # #)$qX1TpK<+*+"+и/Uи"[иaиiи"rX/.+AF+ %+Uj++ib+N9+01#!"&55463!26554&#!"&55463!2#!"3!2'&&5463!2#!"67654332##"55#"554335#"5543X /#-* #-o &hqs1-! ,Q  `_ w)) #*G*5$G*9  (** $   |/ # S # p<_{uVG++4++A++]A+)+9+I+Y+i+y+++++++ ]+49A&6FVfv ]A] 9и/41и1/4`и`/4f4lиl/amи4tиt/auи4}c/LQ+`u+tm+D/+/и/DYusQ{01&&''&&'''&6766'4327>'4632'&&5463!2#!"67654332##"55#"554335#"5543?YM11NYA b[ 0 "- .#/ ZU &hqs1-! ,Q  `_ w)) ![56Y U34T#$ 0e  @9/ :HR)   P'  (*&* $     # [ # pK'Jf˻A2+ +QL+Lи/A&6FVfv ]A]LWиL_иQhN/#+7<+K`++_X+D/+01%#".54>324.#"32>'&&5463!2#!"67654332##"55#"554335#"5543K.OiC+ ии>K@/F/+0+8#++ +0154332##"5#"554335#"5543'&&5463!2#!"676##"54332q&& #U]a/-! ,  WN8&&q # # ~ (** $ ^  ~o <R^y+HA+'"+SX+"-и"5иS`[/L=+ +!6++5.+01'&&5463!2#!"367654332##"55#"554335#"5543!"&5543323!2'##"54332K^-! ,  PH [&& /)  &&&(*?* $    # g # $$)$</e0//0и/)и/)и/ +$+-+и01#!!2#!"&55463!5#"&5463!2#!"3!20   , ,   6""** !  +-+ +и01#!!2#!"&55463!5#"&55463!2#!"3!2!2##"554&#!"&5546(   , ,   /)  ""** !  #)$</E{F//F4и4/и/4;(и(/;1и1/?0+$+-+ +и01#!!2#!"&55463!5#"&55463!2#!"3!2!"&5543323!2(   , ,   /)  ""** !  ')$<|/KL//L6и6/и/6E(и(/E3и3/E:и:/I2+$+;@+-+ +и01#!!2#!"&55463!5#"&55463!2#!"3!2#!"&55463!2#!"3!2(   , ,    , ,   ""** !  ** ! ~ '432)   , ,      #H!, !,># 9WS8(A. -  -9 k""k** ! { { N*9$I*<  2]:@]! ;PjD  ,(I<+   <|CSG.+)+6P+.и/6 и /6и/G<иb   , ,     \**""** !  } { { 7632  , ,    j -p t+*nCqR1 0 &""** !  !z DZ " mEGr % 9HP' 327#!"&55463!5#"&55463!2#!"3!2#!!24.#"32>*@^m-EE@1@^m,-m_?  , ,    6LQQK55KQRK63@$  %2"2?$ $@""** !  !!''! ((76&#!"5543!2'#!"5543!2)   , ,   HH- HJI!  #X^\'Kc   Oq) J %0y ^""^** ! x %'  $  3 )-/ ! #T8)+$+SB+SHиSVE/?/.3+$+<+$и/и/$и/%и<IиQ01##>76"'"&554632675#"&55463!2#!"3!2#"5433232##O !HGC"NPP$LRN /EX4/4>Y]N+',++CJ+4и/ии01##6676##"&554633275#"&55463!2#!"3!2#"5433232##!"&5543323!2G H:  Qd &s, ,U  K )) /)  " #** !  "$)$It1iݻcT+6O+~m+cи/mи/cи/и/~!и!/c*~sи~p/.+Y^+ %++g4+u|+LC+L6и6/LOи4P01#!"&55463!26554&#!"&55463!2#!"3!2##6676##"&554633275#"&55463!2#!"3!2#"5433232##6 /#- #-[ H:  Qd &t, ,U  K )) "*D*4"B*7 >i" #j** ! h "?tdTN?+ :+++и иV/ /DI+:0++R+: и /0-и-/:7и7/;0134332##"5###"54332##6676"'"&5546375#"&55463!2#!"3!2&&&& 7^8":9=$8x; +d6Y, ,&   lPGU  # "** !  Jp8q 2#+++yt+<#y9ZCZ9RиR/ZWиW/rиtиyv/EX-/- >Ys+6+f+и/и/ии ܸ-(-5ܸfTиT/и/01##6676#'"&55463275#"&55463!2#!"3!2&&''&'''&67>'432766'4632354332##"55###"54332 4e*9=>:8 .d0Y, ,   ?WL7o+XB 6I, 0 D; ?B/ VP &&&&y # "z** ! x P/-J" >V,K$$ 8@K.   3d){S  !Bf"  ҺItC>J{/ +:+IB+ILE/?/%*+:+2+ и /:и/:и/801%6"'"&554632675#"&55463!2#!"3!2##>#"54332!NST(QUP AHJ , ,  u "JJD)) #  "** !  ! ~Ib<R^6'+HA+"+]V+]`Y/EX9/9>YE>+,1++9и/и/и/"и"/#и$и>L01##6676"##"&554633275#"&55463!2#!"3!2!"&5543323!2'#"54332k EBJK '/5h " #j** ! h )M|DJZfD5+N+0+e^+e и /^VиV/eha/S +:?+K+H+.%+.и/.0и0/1017463!2#!".5##6676##"&554633275#"&55463!2#!"3!2"3!26554#"54332 , ,Iv O6  Xg s, ,i  _   N))d**u # #v** !    qM|CPZfJ;+R+ 6++R иYи^иdиha/U+@E+Q+N+6-+6 и /7и-[и[/01".5546332!546332###6676##"&55463!5#"&55463!2#!"3!23!26557#"54332?,, , L9  Xg s, ,i  _  7))Z]&Nu " #w** ! | :U UMv3Vb~-++aZ+Ea9ad]/EX0/0>Y#(++0и/и01##6676##"&55463!5#"&55463!2#!"3!2'&&'''&7>7432#"54332k L9  Xg t, ,i  _ 2 &o v,*pHsQ+ 0 ))| #  #~** !    :_"  iABk" %9DJ% #V</3_)+0+1+ +$+-+ии0и201##32#!"&5546335#"&5463!2#!"3!235#0   <, ,   W9""** !  b , ,     `*z""z* ** !  }   75!"&55463!2#!#!"&55463!2#!"3!2)-p t+(q?lR3{ R x , ,   sDb " tGJw  % 4CK&g""n9** !  AEE]AE)E9EIEYEiEyEEEEEEE ]T+49+J+B+++"и$01%#".54>75!"&55463!2#!#!"&55463!2#!"3!24.#"32>*@_m,EE@26Ra,{ R x,bR5 , ,   G6MQQK66KQQM6 3?#  $2"-<$u""u$<** !  !&&! ''ItC/Wy)+:A+ + иY //$+ +SB+-+B9и9/BGиG/01%4332##"55#"55437#!"&55463!2#!"3!26##"&5'"&554632>7>))  , ,a  W ^  @K+ GD< QUO#RRJw " ** !  m"   6" Lo8ThN?+sz+\a+?\9 zs9aUи\X/EX;/;>YDI+v*+*и/IQܸ;R;Uܹb;A]A(8HXhx ]й{rиr/{и/01&&''&&'''&6766'432766'4632#!"&55463!2#!"3!24332##"55#"554376##"&55"'"&554632>7>>^N68L5eY 0 C6=D/ 0>$  , ,K  A )) x AJ+BA: OTO#NNJL3+J H+W? " +oJ  7['k@   81)   y** ! b 0 " z"   "4td;]5&+FM+++ и_//+0+ +[N+9"+NEиE/NQиQ/[XиX/01##"55#"554334332##"54332#!"&55463!2#!"3!26##"&5'"&554636766& &&& , ,&   ,  .h7+9o+ :~9?uX " W~** !  u"   !" Dvm'CWw=.++DI+x}+A&6FVfv ]A]}и/IQкh9h/ax/+38+#+ue+A*+QJ+ui`и`/dиd/ikиk/urиr/0174>32#".732>54.#"#!"&55463!2##"3!2##"55#"5543343326##"&55'"&554636766##"543326Zu>9s]::]s9>uZ6J/HX)$XK33KX$)XH/ , ,   & & ._1+s[ 9n:Bm&& )9##9**:##:'##$$**  h " "    " <ItC'Og+29+&+&Q"// +K:++:1и1/:?и?/01#!"&55463!2#!"3!2#"543326##"&5'"&554632>7>S , ,a  W ))  ?E+!LH> QUO#TSJm** !  ~"  "" L[1=e+' +HO+<5+<g8/$+ +aL+++aPGиG/$KиK/PUиU/a\и\/01#!"&55463!2#!"3!2!"&5543323!2'#"543326##"&55'"&554632>7>^ , ,`  V /)  ))  7>#"54332W /#- #-X , ,`  V Y  @G+GE? MTP'SPJ))#*2* $2*$ N** ! f f" n k"L|D7Aiuջ1"+9+LS+tm+9 иmи/tи/m@и@/twp/<+',+8+5+eO+O и /Oи/eTKиK/TYиY/e`и`/01".5546332!546332##!"&55463!2#!"3!23!26556##"&55'"&554632>7>#"54332;,, , , ,`  V  H  @G+GE? MTP'SPJ))ZX&p** ! f Q Q" x u"Lv>fr}+IP+qj+-q9qtm/ +bL++bQHиH/QVиV/b]и]/01#!"&55463!2#!"3!2'&&'''&7>743276##"&55'"&554632>7>#"54332^ , ,`  V  &o v,*pHsQ+ 0 d  BH+EB< MTP'SPJ))** !    :_"  iABk" %9DJ% "  "RLvL'Ckw=.+NU+vo+=и/=oи/vyr/+38+#+gm+A*+gVMиM/mQиQ/V[и[/gbиb/0174>32#".732>54.#"#!"&55463!2#!"3!26##"&55'"&554632>7>#"543322Vp>8oX77Xo8>pV2G,ET)$TH00HT$)TE,7 , ,`  V Y  @G+GE> MTP'SPJ)) '8%$8((9%%9%""# #** ! z c" q n"? R +/)  ** !  h ""  ')$*.#h""h>*1 <** !  h<|'CSWG.+T+U+6P+.и/6и/6(и(/G<и'25#66*@_m,EE@2G3 R -" , ,   J6MQQK66KQQM6= > 3?#  $2"4@"" !*** !  !&&! ''{|<++%+ + +)+01%#!"&55463!2'#!"&5463!2#!"3!2  R | , ,   A""*!* !    ""   <|#3OP/0/P:и:/и/:Iи/0и/0Bи/0и/Iи/I'и'/B4и4/B6и6/I7и7/I>и>/BMиM/BQ,+?D+$+M6+ +01#!"&55463!2463!2#!".57"3!26554&##!"&55463!2#!"3!2  R  , ,>]  e , ,   @""**   N** !  7632%#!"&55463!2( , ,    -p t+*nCqR1 0   R ** !   DZ " mEGr % 9HP' w""324.#"32>( , ,    R 2AEEEEA22AEDEF@2I6MQQK66KQQM6** !  """3''3"!3&&4"", ,""+ +ItC ,HeI// I3и3/B0и0/B7и7/ J//8=+(+F/+01#"543326'"&554632>7>'#!"&5463!2#!"3!25))  !NST(SVN QUO#WVN0 z, ,  u ~3"  " ** !  tC 1O2// 2и/'и/ 3//"+*+01#"54332'&&5463!2#!"3>765))IT\XO-! ,X  /rws1~D (** $ _   pC !DuE//  и / ииE,и,/;0и0/ F//16+ +>)+01%#"54332!2##"554&#!"&5546%'&&5463!2#!"6765))/) $ &ist1-! ,U  `_ 7#)$ (** $   b !DK;,++ + F/ +16+>)+01%#"54332!"&5543323!2'&&5463!2#!"6765)) /)  &jtu1-! ,U  `_ m{#)$ (*/* $   |S .Ja%+D5+ + <и7632#"54332&jtv1-! ,V  `_ X2c 7cSC?Rb8CpS0 0 ))J (** $    Lb ".;D$$E<0 % '4632'&&5463!2#!"676#"54332?YM11NYA b[ 0 "- .#/ ZU &ist1-! ,U  `_ ))![56Y U34T#$ 0e  @9/ :HR)   P'  (* * $   pK .BV%+M9+ +CиC/AM&M6MFMVMfMvMMMMMMM ]AMM] X/R4+ +>H+(+01%#"54332''&&5463!2#!"676#".54>324.#"32>5))&ist1-! ,U  `_ .Oi+,++ иW//"'+.+ +;и"Bи'Hи.Pи.SиS/01#"5433232###"&546332##"32676'&&546332##"676)) s h{) , 6i* &O$," ,   D"~"*:(!* ! ^  .%** ! h pp5VlN?+-+ + Zи`иjиn/]/#(++bi+/+<и#Cи(Iи/Q017!2##"554&#!"&5546%#"&546332##"32676'&&546332##"676#"5433232##/) $  2p?) , 6g* &N#," ,  !F)) #)$ (!* !   1%** !  s"p5KldU+A:+-++ иn/E6+#(+ +/+Rи#Yи(_и/g01%#"5433232##'#"&546332##"32676!"&5543323!2'&&546332##"676)) v 2p?) , 6g*/)  ! &N#," ,  !Fv|" (!-* !   *$)$%*#* !  pq1Qrj[+*+I:+v+vи/и/!и!/|иy/.+?D+ %+~++K7+7Xи?_иDeиKm01#!"&55463!26554&#!"&55463!2#!"3!2#"&546332##"32676'&&5546332##"676#"5433232##6  /#-+ #-o  2p?) , 6g* &N#," ,  !F)) #*K*9$K*=  (!* !   .%** !  `"p|#Cdz\M++;,+yh+y и /hи/ynиy|k/ +16++pw+=)+)Jи1Qи6Wи=_017463!2#!".57"3!26554&#'#"&546332##"32676'&&546332##"676#"5433232## , ,]]   2p?) , 6g* &N#," ,  !F)) **    (!* !   .%** !  x"p|%Ef|^O++=.++ и$иjиpиzи~m/ +38++ry+?+++Lи3Sи8Yи?a01".546332!546332#%3!2655#"&546332##"32676'&&5546332##"676#"5433232##0,, ,V  1 2p?) , 6g* &N#," ,  !F)) jg&l l; (! * !   .%** !  Y"pw(H^}wh+@1+]L+h]9]Rи]O/6;+T[+B.+.eи6lи;rиBz01%'.'''&67>76327#"&546332##"32676#"5433232##%'&&546332##"676.2c 7cSC?Rb8CpS0 0 z 2p?) , 6g*n)) D &N#," ,  !F Lb ".;D$$E<0 % '4632#"&546332##"32676'&&546332##"676#"5433232##a?YM11NYA b[ 0 "- .#/ ZU  2p?) , 6g* &N#," ,  !F)) ![56Y U34T#$ 0e  @9/ :HR)   P'  (!* !   .%** !  k"pp'G]~vg+ +?0+\K+KA&6FVfv ]A]\Qи\N/#+5:++SZ+A-+-dи5kи:qиAy01%#".54>324.#"32>#"&546332##"32676#"5433232##%'&&546332##"676,.Oi32#"&5546332##"32676'&&5546332##"676#"5433232##4.#"32>7!"&55463!2'#"&5546332%=R--S>%%>R--R>%P 2p?) , 6g* &N#," ,  !F)) m*9 9**9 9* L -/  // . (!* !   .%** !  {"   lftd<]UF+3$+++и и_/ /).+6!++!Cи)Jи.Pи6[и[/0134332##"5###"54332&546332##"32676'&&546332##"676f&&f&&k*T,) ,| r "L$  H-! ,y o 9lPG #** ! g  '(** ! n spd1Rsk\+J;++!&+и&и/!и& и /!u/#/@E+*++L8+8Yи@`иEfиLn01354332##"55###"54332!2##"554&#!"&5546%'&&546332##"676'&&546332##"676f&&f&&R /)  A&M1) ,z p#H @-! ,n d 4#)$ &!* !  "&* * !  s1Rsk\+' +++и и';и;/'Ju/++@E++L8+8Yи@`иEfиLn0134332##"5###"54332!"&5543323!2'&&546332##"676'&&546332##"676f&&f&&/)  &M1) ,z p#H @-! ,n d 4 wBX$)$ &!'* !  "&** !  sqy1Mnٻx+*+BG+94+4и/и/9!иB2и4?и*WиW/*f96/.+\a+ %+3@++hT+Tuи\|иaиh01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332'&&546332##"676'&&5546332##"676y /#-  #-O f&&f&&r&M1) ,z p#H @-! ,n d 4#*F*4$F*8 +~ &!* !  "&** !  s|d#?`ûyj++49++&++ и&и/4$и&1иIиI/X+(/ +NS++%2+ZF+FgиNnиStиZ|01%463!2#!".57"3!26554&#354332##"55###"54332'&&546332##"676'&&546332##"676 , ,=]  Kf&&f&&r&M1) ,z p#H @-! ,n d 4x**   f &!* !  "&* * !  s|d7Ab߻{l+9+,1+#+9 и9KиK/и/и/#и,и)и@и@/9Z# /<+PU+8+*+\H+HiиPpиUvи\~01".546332!546332#354332##"55###"543323!2655'&&546332##"676'&&546332##"676[,, ,\f&&f&&  &M1) ,z p#H @-! ,n d 4jh&(ǺGf f, &!* !  "&** !  swDe~o+]N+++и к0o9/SX++_K+KlиSsиXyи_01354332##"5###"54332'.'''&67>76327'&&546332##"676'&&546332##"676f&&f&&z2c 7cSC?Rb8CpS0 0 &M1) ,z p#H @-! ,n d 4$ Lb ".;D$$E<0 % K+_/+/и/_s_иgиlиs01&&''&&'''&6766'4327>'4632354332##"55###"54332'&&546332##"676'&&546332##"676?YM11NYA b[ 0 "- .#/ ZU f&&f&&r&M1) ,z p#H @-! ,n d 4![56Y U34T#$ 0e  @9/ :HR)   P'  f &!* !  "&* * !  spj'Cd}n++\M+B++94+9 и /A&6FVfv ]A]4и/B1и4?и96/+RW+#+3@+^J+JkиRrиWxи^0174>32#".732>54.#"%#"54332354332##"55#''&&546332##"676'&&546332##"676.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#f&&f&&fr&M1) ,z p#H @-! ,n d 4'+C/.C,,E..E)--..K &!* !  "&* * !  ytC3TwL=++++ иV/ /!&+-+ +:и!Aи&Gи-O014332##"5#"5543#"&546332##"32676'&&546332##"676)) o 2r?) , 6i* &P#," ,  !H3 $  (!* ! ]  .%** ! g {pC5IjbS+-+ + 6и<и Bиl/9/#(++6C+/+Pи#Wи(]и/e01%!2##"554&#!"&5546%#"&546332##"32676754332##"55#"5543'&&546332##"676 /) $  2p?) , 6g*O))  &N#," ,  !F#)$ (!* !     $ %** !  {b3IjbS+?8++++ иl/C4+!&+ +-+Pи!Wи&]и-e0154332##"5#"5543#"&546332##"32676!"&5543323!2'&&546332##"676)) j 2p?) , 6g*/)   &N#," ,  !F{g $  (!-* !   *#)$%*#* !  {qX1Qe~o+*+I:+"+и/Rи"Xи^и"U/.+?D+ %+R_++K7+7lи?sиDyиK01#!"&55463!26554&#!"&55463!2#!"3!2#"&546332##"32676754332##"55#"5543'&&5546332##"676X /#-* #-o  2p?) , 6g*O))  &N#," ,  !F#*K*9$K*=  (!* !   5 $ %** !  {qDSs%+E"+k\++{+{ и /*\k9*/"2и2/*=tиt/и/{w/ +af+'@+t+7.+mY+7и/ и/ иHиH/MиM/Yиaиfиmи.01%46332##".5##".55463326554&##"&5546332##"332677>76#"&546332##"32676754332##"55#"5543'&&5546332##"676"3326554&##,,6 %K!, !,D"  2p?) , 6g*O))  &N#," ,  !F t **) N*9$I*<   (!* !   6 $ %** !     {iC]}/+O,+uf+++ ии/к4fu94/,<и76327#"&546332##"32676754332##"5#"5543'&&546332##"676;2c 7cSC?Rb8CpS0 0 x 2p?) , 6g*O))  &N#," ,  !F Lb ".;D$$E<0 % '4632#"&546332##"32676754332##"55#"5543'&&546332##"676?YM11NYA b[ 0 "- .#/ ZU  2p?) , 6g*O))  &N#," ,  !F![56Y U34T#$ 0e  @9/ :HR)   P'  (!* !    $ %** !  {pK'G[|te+ +?0+NI+Iи/A&6FVfv ]A]ITиN~K/#+5:+HU++A-+-bи5iи:oиAw01%#".54>324.#"32>#"&546332##"32676754332##"55#"5543'&&546332##"676K.Oi32#"&5546332##"32676754332##"55#"5543'&&5546332##"6764.#"32>7!"&55463!2'#"&5546332%=R--S>%%>R--R>%Q 2p?) , 6g*O))  &N#," ,  !FT*9 9**9 9* L -/  // . (!* !   , $ %** !  C   lftd@aYJ+7(+++ иc//-2+:%+ +%Gи-Nи2Tи:_и_/014332##"5#"5543##"54332&546332##"32676'&&546332##"676&& j&&*T,) ,| r "L$  H-! ,y o 9+ $ ~ #** ! f  &(** ! m spd6WkwO@+.+^Y+lq+lиq и /Ydиly/t/$)++Xe+0+=и$Dи)Jи0R01%!2##"554&#!"&5546%'&&546332##"676'&&546332##"676%54332##"55#"5543##"54332 /)  A&M1) ,z p#H @-! ,n d 4I&&y W&&#)$ &!* !  "&* * !  ! $ s)JVwo`+ ++KP+"и 3и3/ BKyS/+8=+#+D0+0]и8dи=jиDr01!"&5543323!254332##"5#"5543'&&546332##"676##"54332'&&546332##"676p/)  &&y =&M1) ,z p#H &&@-! ,n d 4z$)$@ $  &!'* !  1&** !  sqy1Rsѻk\+*+zu++и/и/!и*;и;/*Juи/.+@E+ %+t++L8+8Yи@`иEfиLn01#!"&55463!26554&#!"&55463!2#!"3!2'&&546332##"676'&&5546332##"676%54332##"55#"5543##"54332y /#-  #-O &M1) ,z p#H @-! ,n d 4I&&y W&&#*F*4$F*8  &!* !  "&** !  J $ s|d#Dey]N++<-+lg+z+z ии/grиz/ +27++fs+>*+*Kи2Rи7Xи>`01%463!2#!".57"3!26554&#''&&546332##"676'&&546332##"676%54332##"55#"5543##"54332 , ,:]  &M1) ,z p#H @-! ,n d 4I&&y W&&x**    &!* !  "&* * !  ! $ s|d%Fg{׻_P++ni+|+ и/и//и/и/|и$и$/>itи|/ +49++hu+@,+,Mи4Tи9Zи@b01".546332!546332#%3!2655'&&546332##"676'&&546332##"676%54332##"55#"5543##"54332[,, ,=  &M1) ,z p#H @-! ,n d 4I&&y W&&jh&f f, &!* !  "&** !  ĵF $ sw(I]~vg+A2+Q$++$Q9QKVи$ZиZ//7<+JW+C/+/dи7kи<qиCy01%'.'''&67>76327'&&546332##"676754332##"55#"5543'&&546332##"676##"54332B2c 7cSC?Rb8CpS0 0 &M1) ,z p#H A&&y @-! ,n d 4'&& Lb ".;D$$E<0 % YEXq/q >Y/+WC+/и/PKCdиKkиPrиWyиP01&&''&&'''&6766'4327>'4632'&&546332##"676'&&546332##"676%54332##"55#"5543##"54332?YM11NYA b[ 0 "- .#/ ZU &M1) ,z p#H @-! ,n d 4I&&y W&&![56Y U34T#$ 0e  @9/ :HR)   P'  &!* !  "&* * !  + $ spj'Hi}aR++@1+pk+~+~ и /A&6FVfv ]A]и/kvи~/+6;+jw+#+B.+.Oи6Vи;\иBd0174>32#".732>54.#"'&&546332##"676'&&546332##"676%54332##"55#"5543##"54332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#&M1) ,z p#H @-! ,n d 4I&&y W&&'+C/.C,,E..E)--.. &!* !  "&* * !  ' $ ytC;\TE+3$++ иии^/ /).+5!++ +!Bи)Iи.Oи5W0154332##"5#"554335#"5543#"&546332##"32676'&&546332##"676)) Z 2r?) , 6i* &P#," ,  !H1 # # u (!* ! V  .%** ! ` {p<Xx˻++DM+MD9A&6FVfv ]A] 9и/+MD9+/A++]A+)+9+I+Y+i+y+++++++ ]41и1/4=и=/D>4IиI/>Jи4QиQ/>RиMUкa9a/pи/D@/fk+=R+QJ+^/+Kr+/и/RPkX^иfиkиr01&&''&&'''&6766'4327>'463254332##"55#"554335#"5543#"&546332##"32676'&&546332##"676?YM11NYA b[ 0 "- .#/ ZU )) O 2p?) , 6g* &N#," ,  !F![56Y U34T#$ 0e  @9/ :HR)   P'   # [ #  (! * !   .%** !  <5VM>++ 9 //EиE/ +%*+2;+и;и/%Bи*Hи2PиP/01!2#!"&55463!546332%##"&546332##"332'&&546332##"676  +8 , ,    6{;-! ,  4p, "" .**    '** !   иjLиL/UqиZwиbи/01#!"&55463!26554&#!"&55463!2#!"3!2#!"&55463!54332!2'##"&5546332##"332'&&5546332##"764 /#-  #-Q x  ) q , ,    hs-! ,  hW z#*O*5#E*B ""PP{**  q '*|* ! q  324.#"32>!2#!"&55463!546332%##"&5546332##"332'&&5546332##"76*@^m-EE@1@^m,-m_?I6LQQK55KQRK6  +& , ,    hs-! ,  hW 3@$  %2"2?$ $@3!''! ((k""k "**   '** !   ?t>Z|߻te+:1++ кE1:9E/T1lиl/~//JO+5%+Wb+W и /bи/% и /%:,и,/:1и1/bAиA/JiиOoиWwиw/01#"5433232##6"'"&55463267546332>'##"&5546332##"332'&&5546332##"3676)) "NPP$LRN и>/DS2iиi/{/ /IN+6(++V_+(%и%/(;/и//;2и2/_@и@/IfиNlиVtиt/0134332##"5###"543326'"&554637546332667##"&5546332##"332'&&5546332##"3676l&&l&& <~D8x; +d6+:g) , , y   A -! ,m c 2lPG#  "  **   (** !   ItC 4Prj[+0'+ +;'09;/J'bиb/ t//@E+0+MX+и/0"и"/0'и'/X7и7/@_иEeиMmиm/01#"543326"'"&55463267546332>'##"&5546332##"332'&&5546332##"36765))!NST(QUP AHJ +"JJD , ,    ),--! ,  #H~ #  "  **   '** !  Ib!Heۻ~o+ +#A+ +PA#9P/_ /+UZ+E7+cL+7>#и#/70и0/75и5/>AиA/LlиUsиZyиcиcи/01!"&5543323!2'#"543326676"##"&554633275463327##"&5546332##"332'&&5546332##"76S/)  #))gDBJK '/5++ 9 //EиE//%*+ +2;+и;и/%Bи*Hи2PиP/01"&5!"&55463!2#!###"&5546332##"332'&&5546332##"676| R w= , ,    6s;-! ,  4h,  ="" **    '** !   L/9L/[pиp/6B+QV+ 9+^f+0+%+0и-иfHиH/QmиVsи^{и{/01#!"&55463!26554&#!"&5546335!"&55463!2#!32#!"3!2##"&5546332##"332'&&5546332##"764 /#-  { R x#-Q  , ,    hs-! ,  hW z#*@*0#b""b@*3 =**   '** !   <`AUo3+++v+B+vи/ и /+\LиL/[и[/+cиc/39/+и/cи/vи/A]A)9IYiy ]\и/BG+7 +{+Q+++%++ и/Qи/.и./%_и_/mܹZdиrиr/{иии/01##".55463326554&##"&5546332##"33267667672%#".54>32#!##"&55!"&55463!2'##"&5546332##"332'&&5546332##"76!"&55463!24&#"326#"&554332  )R!, !,G)  _*:$$:**:$$:*` x+{ R q , ,    hs-! ,  hW yX X0--11--0  q M*8$H*;  *, !,, !+"Y Y"b**   '** !   N& ' '&$$% $ <|'7Srǻj[++3+[j9/+>9>/Mbиb/t0!+CH+(+PX+ +ииX:и:/C_иHeиPmиm/01746335!"&55463!2#!32#!".57"3!26554&###"&5546332##"332'&&5546332##"76 ,{ R x ,>]  x , ,    hs-! ,  hW `*z""z*   b**   '** !   75!"&55463!2#!4.#"32>##"&5546332##"332'&&5546332##"76*@_m,EE@26Ra,{ R x,bR5I6MQQK66KQQM6Z , ,    hs-! ,  hW  3?#  $2"-<$u""u$<.!&&! ''**   '** !   4tdA]}uf+*1+++ кH1*9H/PܸBиB/HW1mиm///MR+ +?2+}c+2)и)/25и5/?<и7>'##"&5546332##"332'&&5546332##"36765))  ?E+!LH> QUO#TSJ , ,    ),--! ,  #H~"  '" ^**   '** !  L[ 3If~o+?8++ +Q9Q/`vиv/ /<5+V[+/+dM+/и/<и/#и#//*и*/5CMlиVsи[yиdи/dи/01%#"543326##"&55'"&554632>7>!"&5543323!2##"&5546332##"332'&&5546332##"765))  CQ""  "%#)$5 )** !   %** !  LqW1Yv+*+<C+z+zи/и/!и!/aC<9a/p}/EX%/%>Y.+fk++t]+U?+% UD;и;/DIиI/UPиP/]иfиkиtи/tи/01#!"&55463!26554&#!"&55463!2#!"3!26##"&55'"&554632>7>'##"&5546332##"332#"54332%'&&5546332##"76W /#- #-X   @G+GE? MTP'SPJ  -! ,  ))5 KF," ,  @>#*5*#$5*'  " h e"K )*u* ! l  %*u* ! j L|D#KWt}++.5+VO+V и /Oи/_5.9_/nVR/ +di++G1+r[+G6-и-/6;и;/GBиB/[zиdиiиrи/rи/017463!2#!".57"3!26554&#6##"&55'"&554632>7>#"54332'##"&5546332##"332'&&5546332##"76 , ,E]  0  @G+GE> MTP'SPJ)) -! ,   GJ-! ,  @>]** | |  " b _" )** !   &** ! } L|C%Mjv++07++ и$кU709U/dnиtиq/ +Z_++hQ+I3+3и/I8/и//8=и=/IDиD/Q|иZи_иhи/hи/01".5546332!546332#%3!26556##"&55'"&554632>7>'##"&5546332##"332#"54332%'&&5546332##"76:,, ,= G  @G+GE? MTP'SPJ  -! ,  ))5 KF," ,  @>Z]&Q Q" x u"J )*u* ! k  %*u* ! j QvL'NZw++K++YR+A&6FVfv ]A]Rи/b+K9b/qYU/+gl+#+=N+u^+=J,и,/OиO/^}иgиlиuи/uи/0174>32#".732>54.#"7"&55'"&554632>7>76##"54332##"&5546332##"332'&&5546332##"762Vp>8oX77Xo8>pV2G,ET)$TH00HT$)TE,@ FD< MSN!NPL @H)) -! ,   KF," ,  @> '8%$8((9%%9%""# # i"  m I( )** !   %** ! } <+LiM//%M4и4/C + +(1+1и/8и >и(FиF/01%#!"&55463!2'##"&546332##"332'&&546332##"676  R ` , ,    6{;-! ,  4p, A""**    '** !   F+ +F(и(/1Mи6Sи>[и[/01#!"&55463!2!2##"554&#!"&5546##"&5546332##"332'&&5546332##"76  R /)   , ,    hs-! ,  hW =""#)$**   '** !   <%A`{XI+;,+IX9/ +16+>F+#+F(и(/1Mи6Sи>[и[/01!"&5543323!2#!"&55463!2'##"&5546332##"332'&&5546332##"761/)  v  R q , ,    hs-! ,  hW z')$""**   '** !   <|+Gf^O+A2+O^9/%)+7<+ +DL+ +L.и./7Sи<YиDaиa/01#!"&55463!2#!"&55463!2#!"3!2##"&5546332##"332'&&5546332##"76  R , ,    , ,    hs-! ,  hW ?""E** !  **   '** !   +MR+5+Zb+,#+ +bDиD/MiиRoиZwиw/01#!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!2##"&5546332##"332'&&5546332##"76  R x /#-  #-Q  , ,    hs-! ,  hW ""#*J*:#J*= J**   '** !   <|#3OnfW+I:+/+Wf9/'p,+?D+$+LT+ +T6и6/?[иDaиLiиi/01#!"&55463!2463!2#!".57"3!26554&###"&5546332##"332'&&5546332##"76  R  , ,>]  x , ,    hs-! ,  hW D""**   P**   '** !   7632%#!"&55463!2'##"&5546332##"332'&&5546332##"76 -p t+*nCqR1 0   R q , ,    hs-! ,  hW  DZ " mEGr % 9HP' z""**   '** !   ItC ,HjwbS+B3+ + l//8=+(+EP+P/и//8Wи=]иEeиe/01#"543326'"&554632>7>'##"&546332##"332'&&5546332##"36765))  !NST(SVN QUO#WVN , ,    ),--! ,  #H~3"  " * *   '** !  Ib!>]|VG+ +8)+ + ~/+.3+vo+<%+%Dи.Kи3Qи<YиY/<[и[/ohиh/omиm/vzиz/01!"&5543323!2'#"54332##"&5546332##"332'&&5546332##"766"##"&55463326766S/)  #)) -! ,   KF," ,  @>KJK '/5C+ %++L5+g`+`YиY/`^и^/gkиk/5sи>zиCиLи/Lи/01#!"&55463!26554&#!"&55463!2#!"3!2##"&5546332##"3326"##"&55463326766''&&5546332##"76#"54332W /#- #-X  -! ,  /JK '/5))#*:*($:*, 8 )** !  y  " l %** !  I|D#@LkջdU++:++KD+K и /Dи/KG/ +05++>'+}+'Rи0Yи5_и>gиg/>iиi/}vиv/}{и{/и/017463!2#!".57"3!26554&###"&5546332##"332#"54332%'&&5546332##"766"##"&55463326766 , ,E]  W -! ,  ))5 KF," ,  @>KJK '/5Z]&Q Q )** ! y ~  " } %** ! x ytC ,Le +D5++$++N!/'/++2и9и?иF01%'&&546332##"676#"54332#"&546332##"32676o &P#," ,  !H)) 2r?) , 6i*%** ! g ~9 (!* ! ]  {b !Bbu:+++ZK+ + d/ +05+=(+(Hи0Oи5Uи=\01%#"54332!"&5543323!2'&&546332##"676#"&546332##"326765))/)   &N#," ,  !FJ 2p?) , 6g*m{#)$%*#* !    (!-* !   {qX1=^~VG+*+vg+"+и/5и";и"8/.+LQ+ %++YD+DdиLkиQqиYx01#!"&55463!26554&#!"&55463!2#!"3!2#"54332%'&&5546332##"676#"&546332##"32676X /#-* #-o #)), &N#," ,  !FJ 2p?) , 6g*#*K*9$K*= 5D%** !    (!* !   {|C /PpH9+# +hY+ + и+и r/(+>C+ +K6+6Vи>]иCcиKj01#"54332463!2#!".57"3!26554&#%'&&546332##"676#"&546332##"326765)) , ,]]   &N#," ,  !FJ 2p?) , 6g***   %** !    (!* !   {|C '1RrJ;+)+j[+ +)ии #и0и t/, +@E+(+M8+8Xи@_иEeиMl01#"54332".546332!546332#%3!2655'&&546332##"676#"&546332##"326765)),, ,X  q &N#," ,  !FJ 2p?) , 6g*#1Yjh&l l'%** !    (! * !   {w(HiukaR+@1+tm+Rt9twp/6;+B.+.Oи6Vи;\иBd01%'.'''&67>76327#"&546332##"32676'&&546332##"676#"5433282c 7cSC?Rb8CpS0 0 { 2p?) , 6g* &N#," ,  !F)) Lb ".;D$$E<0 % 324.#"32>'&&546332##"676#"&546332##"326765)).OiиIL;/5/+(+!+@G+017&&55463326554&#!"&55463!2##"3676#"5433232##-#- F#-wg5uxx)) Y!**&* !   ~"*p2H^$+ +8=+и/=Lи8Rи8\и8`:/O/+4A++T[+&+01"&55463326554&#!"5543!2##"32676!2##"554&#!"&5546%#"5433232##-#- =#-vh  ^d/) $ /)) #*v*Z # p*e i#)$7"2H^}$+TM+ +G6+и/G<иG`9/XI+++&+>E+017"&55463326554&#!"5543!2##"36676#"5433232##!"&5543323!2-#- =#-we  ])) /)  #**l # *w "$)$q1dzV5+*+N=+"+и/5DиD/hи"nи"xи"|k/.+HA+ %+:Q+pw++X2+01#!"&55463!26554&#!"&55463!2#!"3!2"&55463326554&#!"5543!2##"32676#"5433232##5  /#-+ #-o -#- =#-vh  ^)) #*D*2$D*6 #*f*J # `*U $"|FVl8+J+0+ R+&и&/RZи `и jи n]/O +*#+G+3+bi+:+017463!2#!".5"&55463326554&#!"5543!2##"32676"3!26554#"5433232## , ,]-#- =#-ve  [   O)) i**W#*v*Z # p*e    r "|NXn@+P+8'++P и.и./Wи\иbиlиp_/S+2++O+$;+dk+B+01".546332!546332#"&55463326554&#!"5543!2##"326763!26557#"5433232##0,, ,-#- =#-ve  [%  7))  ec&#*q*U # k*` \ \"y2Vl$+ +kZ+и/D 9k`иkn]/EXa/a>Y++&+ai017"&55463326554&#!"5543!2##"36676'&&'&''&7>7432#"5433232##-#- =#-we  ] &o u,*p HsQ+ 0 )) #**g # }*r D  :_#  i?Dj%9DJ% G"m2n$+SK+ +r+и/59AS&S6SFSVSfSvSSSSSSS ]ASS]=KS9SPиP/rcиc/rfиf/xиu/++z+a+&aMиM/01"&55463326554&#!"5543!2##"32676&'&'&&'''&67>'432766'4632#"5433232##-#- =#-ve  [/,l9JXE6I+ 0  0 =A/ WU ])) #*v*Z # p*e _EcYK"L,.M& " 8AK-   :7/-|M   Hm$ "pFZp׻8+Q +0+o^+^&и&/AQ&Q6QFQVQfQvQQQQQQQ ]AQQ]odиora/V+*#+L+3+fm+:+01%#".54>32%"&55463326554&#!"5543!2##"326764.#"32>7#"5433232##,.PiP+0Q9 9Q0+P>$-)) )?**?))>**>#*v*Z # p*e * *( ("q2Xn}$+ +m\+и/6m9mbиmp_/+VO++dk+&+01"&55463326554&#!"5543!2##"32676'&&'''&76676&#!"5543!2'#"5433232##-#- =#-ve  [;(6}7 KFBY [v P)) #*v*Z # p*e (#  " *4 !S6  # 2g"|RhlD#+i +<++j+gV+#2и2/g\иgnY/+6/+ +(?+^e+F +и иiи k01!"&5546335#"&55463!2##32"&55463326554&#!"5543!2##"32676#"5433232##35#M n Ajv-#- =#-ve  [))  ! ! #*v*Z # p*e 1 "ϼU2F\j~7$+u=+ +3k+и/Akk]Ak)k9kIkYkikykkkkkkk ]Jk39J/[PAu&u6uFuVufuvuuuuuuu ]Auu][M/z8++Bp+f]++RY++&+01"&55463326554&#!"5543!2##"32676#".54>32#"5433232##!"&55463!24.#"32>#"&5546332-#- =#-vh  ^'?Q*+Q@''?Q+*R?')) $ L,77++77, A#*f*J # `*U , ,, ,,"  jtdNB+:'+++и и/и//P/ /4++D+$=++0134332##"5###"54332&&55463326554&##"&55463!2##"3676&&&&-#-  #-OWVlPGp!**&* ! opd1Mc&+ +BG+SX+и/B2иX3и3/S9иX?и?/Se6/U/+O\+!+(+2и2/!@и@/017&&55463326554&##"&55463!2##"3676354332##"55###"54332!2##"554&#!"&5546-#- #-L&&&&k /)  !*}*j&*l"! #)$oMcB+YR+:'+++и и/и//e/]N+4++$=+D++0134332##"5###"54332&&55463326554&##"&55463!2##"3676!"&5543323!2&&&&-#- #-L_/)  jEm!**q&*s"! $)$oqy1cûX5+*+P=+ty+kf+fи/и/k!и5EиE/tdиfqиkh/.+JA+ %+:S+er++Z2+01#!"&55463!26554&#!"&55463!2#!"3!2&&55463326554&##"&55463!2##"3676%354332##"55###"54332y /#-  #-O B-#- #-I&&&&#*D*2$D*6 !*a*N&d*P  ! %o|dEUq:+I+2+fk+]X+] и'и'/XQиQ/fVиXcи]sZ/N +,#+F+5+<+VиV/5dиd/01%463!2#!".5&&55463326554&##"&55463!2##"3676"3!26554&#354332##"55###"54332 , ,=N-#- #-LU  d&&&&_**;!*}*j&*l"!  ~ ~ o|dMisB+k+:'+^c+UP+k иPи/Uи/и//^NиP[иPrиr/UuR/n+4++j+$=+O\+D+01".546332!546332#&&55463326554&##"&55463!2##"3676%354332##"55###"543323!2655[,, ,-#- #-L&&&&  `^&!*s*`&v*b"! Ǻ=\ \oy1Mq{&+ +BG+94+и/B2и4?к_999s6/+!+(+017&&55463326554&##"&55463!2##"367634332##"5###"54332'&&'&''&7>7432-#- #-L&&&&a &o u,*p HsQ+ 0 !**o&*q"!     :_#  i?Dj%9DJ% om1m/&+RJ+~+up+AR&R6RFRVRfRvRRRRRRR ]ARR] JR9 /и/ 4u9<JR9ROиO/~nиp{иur/+!+(+ܸnиn/oиo/!|и|/"}и}/и/017&&55463326554&##"&55463!2##"3676&'&'&&'''&67>'432766'4632354332##"55###"54332-#- #-L{/,l9JXE6I+ 0  0 =A/ WU &&&&!*}*j&*l"! |EcYK"L,.M& " 8AK-   :7/-|M   Hm$ onkEYu:+P +2+jo+a\+aи/'и'/\FиF/AP&P6PFPVPfPvPPPPPPP ]APP]jZи\gиaw^/U+,#+K+5+<+ZиZ/5hиh/01%#".54>32%&&55463326554&##"&55463!2##"36764.#"32>354332##"55###"54332k4Wqи8NܸCиSHиSV5/;/+(+@G+JQ+!+017&&55463326554&#!"&55463!2##"3676#"5433232##32##-#- F#-wg5uxx))  Y!**&* !   ~  p2Rh$+ +Q6+и/Q<и6LܸAиQFиQWи6]иQj9/Z/+>E+Ta++HO+&+01"&55463326554&#!"5543!2##"32676#"5433232##32##!2##"554&#!"&5546-#- =#-vh  ^))  /) $ #*v*Z # p*e 5 !x!:#)$2Rh$+^W+ +Q6+и/Q<и6LܸAиQFиQj9/bS++>E++&+HO+017"&55463326554&#!"5543!2##"36676#"5433232##32##!"&5543323!2-#- =#-we  ]))  /)  #**l # *w !!$)$y2Rv$+ +Q6+и/Q<и6LܸAиQFкd 9Qx9/+>E++&+GP+Eи/017"&55463326554&#!"5543!2##"36676#"5433232##32##''&&'&''&7>7432-#- =#-we  ]))   &o u,*p HsQ+ 0 #**g # }*r JG!!  :_#  i?Dj%9DJ% pFZz8+Q +0+y^+^&и&/AQ&Q6QFQVQfQvQQQQQQQ ]AQQ]ydи^tܸiиynиy|a/V+*#+en+L+3+pw+:+01%#".54>32%"&55463326554&#!"5543!2##"326764.#"32>7#"5433232##32##,.PiP+0Q9 9Q0+P>$-))  )?**?))>**>#*v*Z # p*e * *( (!x!tC2F{&+ +94+и/4?и9H6//P/+8E++MZ+&+01&&55463326554&#!"5543!2##"32>76!2##"554&#!"&554654332##"55#"5543-#- #-.TQS,  0WV[/) $ )) "*v*Z # p*e   j#)$i $ b2F\}$+RK+ +94+и/4?и9^6/VG+++3@+&+017"&55463326554&#!"5543!2##"366764332##"5#"5543!"&5543323!2-#- #-aS  X))  /)  #**l # *w !    gP $ #)$qX1h|V5+*+N=+"+и/5DиD/iи"oиuи"~l/.+HA+ %+:Q+iv++X2+01#!"&55463!26554&#!"&55463!2#!"3!2&&55463326554&#!"5543!2##"32>76%54332##"55#"5543X  /#-+ #-o -#- #-.TQS,  0WV[)) #*D*2$D*6 "*f*J # `*U   ( $ |CHXl8+L+0+ T+&и&/TYи _иTeи n\/Q +*#+I+Yf+:+01%463!2#!".5&&55463326554&#!"5543!2##"32>76"3!26554&#54332##"55#"5543 , ,]/-#- #-/UQQ,  T   )) i**W"*v*Z # p*e      $ |CR\p@+T+8'++T и.и./[и]иcиiиr`/W+2++S+]j+B+01".546332!546332#&&55463326554&#!"5543!2##"32>763!265554332##"55#"5543S,, ,-#- #-.SQS-  .UW\>  )) `_&"*q*U # k*`   \ \ $ y2Vj$+ +]X+и/D]9RиR/Xcи]lZ/++Wd+&+017"&55463326554&#!"5543!2##"36676'&&'&''&7>74324332##"55#"5543-#- #-aS  U &o u,*p HsQ+ 0 )) #**g # }*r! @  :_#  i?Dj%9DJ% 7  $ m4p$+UM+ +wr+и/7w9AU&U6UFUVUfUvUUUUUUU ]AUU]?MU9URиR/r}иwt/+q~+c+&cOиO/&ܸ01&&55463326554&#!"5543!2##"32>76&'&'&&'''&67>'432766'463254332##"55#"5543-#- #-/UQQ,  T?/,l9JXE6I+ 0  0 =A/ WU )) "*v*Z # p*e  `EcYK"L,.M& " 8AK-   :7/-|M   Hm$  $ pLH\pϻ8+S +0+c^+&и&/^IиI/AS&S6SFSVSfSvSSSSSSS ]ASS]^iиcr`/X+*#+N+]j+:+01%#".54>32%&&55463326554&#!"5543!2##"32>764.#"32>54332##"55#"5543L.PiP+0Q9 9Q0+P>$)) )?**?))>**>"*v*Z # p*e  * *( (" $ U\6J^l;$+wA+ +7m+и/Amm]Am)m9mImYmimymmmmmmm ]Lm79L/QLWAw&w6wFwVwfwvwwwwwww ]Aww]QN/|<++Fr+h_++KX++&+01&&55463326554&#!"5543!2##"32>76#".54>3254332##"55#"5543!"&55463!24.#"32>#"&5546332-#- #-.TQS,  0WV['?Q*+Q@''?Q+*R?' ))  L,77++77, A"*f*J # `*U   , ,, ,A" $ 7  jtd2FR&+ +94+GL+и/4?иGTI/O/+(+3@+3и/@!и!/017&&55463326554&##"&5546332##"326764332##"5#"5543##"54332-#- #-QKUg&& u&&_!**&* " PC $ ~npd2H\h&+ +OJ+]b+и/]7иb=и=/JUи]j:/e/+4A+!+(+017&&55463326554&##"&5546332##"32676!2##"554&#!"&554654332##"55#"5543##"54332-#- #-KSK /)  h&&{ n&&!*}*j&*l ! Q#)$o  $ o2F\h&+RK+ +94+]b+и/4?и]je/VG++!+(+3и3/!@и@/017&&55463326554&##"&5546332##"3267654332##"5#"5543!"&5543323!2'##"54332-#- #-QNH_&&{ z/)  &&!**q&*s ! 8 $ $)$oqy1dxɻX5+*+P=+kf+y~+~и/и/y!и5EиE/fqиy/.+JA+ %+:S++Z2+:eиe/Srиr/01#!"&55463!26554&#!"&55463!2#!"3!2&&55463326554&##"&5546332##"32676%54332##"55#"5543##"54332y /#-  #-O B-#- #-QMI`&&{ n&&#*D*2$D*6 !*a*N&d*P ! E $ o|dFVjv:+J+2+]X+kp+k и'и'/pRиR/Xcиkxs/O +,#+G+5+<+WиW/5dиd/01%463!2#!".5&&55463326554&##"&5546332##"32676"3!26554&#54332##"55#"5543##"54332 , ,=N-#- #-KSKW  &&{ n&&_**;!*}*j&*l !  ~ ~  $ o|dNXlxӻB+P+:'+_Z+mr+P иrи/mи/и//rWиW/Zeиmzu/S+4++O+$=+D+$YиY/bиb/=fиf/01".546332!546332#&&55463326554&##"&5546332##"326763!265554332##"55#"5543##"54332[,, ,-#- #-KSKF  &&{ n&&`^&!*s*`&v*b ! \ \4 $ oy2Vjv&+ +]X+kp+и/Dk9Xcиkxs/+!+(+`и`/017&&55463326554&##"&5546332##"32676'&&'&''&7>743254332##"55#"5543##"54332-#- #-QNH0 &o u,*p HsQ+ 0 -&&{ n&&!**q&*s ! 1  :_#  i?Dj%9DJ% E $ Wonk'ZnzN++ +F3+a\+ot+oи/tи/A&6FVfv ]A]+;и;/\gиo|w/#+@7++0I+P(+0[иIh01%#".54>324.#"32>&&55463326554&##"&5546332##"3267654332##"55#"5543##"54332k4Wq7654332##"55#"554335#"5543!2##"554&#!"&5546-#- #-/UQQ,  T)) /) $  "*z*^ # t*i  p # h # *#)$b5Qg$+]V+ +<7+и/7Bи7Jи<i9/aR++6K++&+JC+017&&55463326554&#!"5543!2##"3>7654332##"55#"554335#"5543!"&5543323!2-#- #-0WUT, -WYa))  /)  "**q # *   ~s # v # 1#)$qX1h˻V5+*+N=+"+и/5DиD/iи"oиuи}и"l/.+HA+ %+i~+:Q++}v+X2+Q|и|/01#!"&55463!26554&#!"&55463!2#!"3!2&&55463326554&#!"5543!2##"32>7654332##"55#"554335#"5543X  /#-+ #-o -#- #-.TQS,  0WV[)) #*D*2$D*6 "*j*N # d*Y   ]k# # e # |CHXt8+L+0+ T+&и&/TYи _иTeиTmи v\/Q +*#+Yn+I+3+mf+:+01%463!2#!".5&&55463326554&#!"5543!2##"32>76"3!26554&#54332##"55#"554335#"5543 , ,]/-#- #-/UQQ,  T   )) i**O"*z*^ # t*i      # l # |CRnx@+p+8'++p и.и./SиYи_иgиwиzV/s+2++o+Sh+$;+g`+B+01".546332!546332#&&55463326554&#!"5543!2##"32>7654332##"55#"554335#"55433!2655S,, ,-#- #-.SQS-  .UW\))  `^&"*u*Y # o*d   b| # g # \ \y4Pt$+ +;6+и/6Aи6Iкb;9pиp/;v8/+5J++&+IB+017"&55463326554&#!"5543!2##"3667654332##"55#"554335#"5543'&&'&''&7>7432-#- #-YV  .WX[))  &o u,*p HsQ+ 0 #**j # *u!  p # w # U  :_#  i?Dj%9DJ% m4p$+UM+ +wr+и/7w9AU&U6UFUVUfUvUUUUUUU ]AUU]?MU9URиR/r}иrиwt/+q++c+~+ܸ&cOиO/01&&55463326554&#!"5543!2##"32>76&'&'&&'''&67>'432766'463254332##"55#"554335#"5543-#- #-/UQQ,  T?/,l9JXE6I+ 0  0 =A/ WU )) "*z*^ # t*i  hEcYK"L,.M& " 8AK-   :7/-|M   Hm$  # p # pLH\x8+S +0+c^+&и&/^IиI/AS&S6SFSVSfSvSSSSSSS ]ASS]^iи^qиcz`/X+*#+]r+N+3+qj+:+01%#".54>32%&&55463326554&#!"5543!2##"32>764.#"32>54332##"55#"554335#"5543L.PiP+0Q9 9Q0+P>$)) )?**?))>**>"*z*^ # t*i  * *( ( # p # jtd2NZ&+ +:C+OT+и/:4?и4GиCKиO\W/Q/+(+3H+G@+!+017&&55463326554&##"&5546332##"3267654332##"5#"554335#"5543##"54332-#- #-QKUg&&y yy l&&_!**&* " b # # e~o4Pfr&+\U+ +<E+gl+и/<6Aи6IиEMиgto/`Q++5J+!+(+IB+017&&55463326554&##"&5546332##"32>7654332##"55#"554335#"5543!"&5543323!2'##"54332-#- #-*KGE%D[&&y yy x/)  &&!**q&*s  !  t # y # 0$)$o|dOYuB+Q+:'+aj+v{+Q и{и/vи/и//{XиX/a[fи[nиjrиv~/EX / >YEXm/m>YT+4++P+Zo+$=+E+cиc/mg01".546332!546332#&&55463326554&##"&5546332##"6763!265554332##"55#"554335#"5543##"54332[,, ,-#- #- KFF?  &&y yy l&&`^&!*s*`&v*b   !  \ \To3 # h # oy4Xt&+ +`i+uz+и/Fi`9`ZeиZmиiqиu}/+Yn+!+(+mf+bиb/017&&55463326554&##"&5546332##"32>76'&&'&''&7>743254332##"55#"554335#"5543##"54332-#- #-*IGF&G/ &o u,*p HsQ+ 0 -&&y yy l&&!**q&*s  !  1  :_#  i?Dj%9DJ%  # v # NW<EŸF/и/A]AO]A]A] A ]A ]AO ]A ]и  (5 G6=++++ #+6и301%5#"&55463!26554&#!"&55463!2#!"3!2#!!2#!"&55463/#- #-=   N#*}+h&}+l %""++KP+P#и#/+иK5и5/K]M/0'+ 9+GT+B+ +и01#!!2#!"&55463!5#"&55463!26554&#!"&55463!2#!"3!2!2##"554&#!"&5546-   /#- #-W /)  d""d#*F*7$I*9 #)$<E[QJ++6#+Jи/J+и+/Q=и=/6]NG+0'+ 9+ +B+ииGU01#!!2#!"&55463!5#"&55463!26554&#!"&55463!2#!"3!2!"&5543323!22   /#- #-V /)  zk""k#*O+B$R+D ')$]  ]""]#*F*7$I*9 ** w w <|EakcK++]V+Kи/V#и#/K+и+/]5и5/c=и=/cSиVjи]mfF+0'+Tb+ 9+B+ +и01#!!2#!"&55463!5#"&55463!26554&#!"&55463!2#!"3!2".5546332!546332#%3!2655-   /#- #-W ,, ,7  a""a#*<*-$?*/ \[&X X+Ih+6#+AI&I6IFIVIfIvIIIIIII ]AII]Iиhи/+иIFиF/ZhI96n0'+ 9+ k+B+ ииии'9A01#!!2#!"&55463!5#"&55463!26554&#!"&55463!2#!"3!2'&&'''&67>7432-   /#- #-W 6J^= |))tItO+ 0 \""\#*I*:$L*<   3,# !!hADl  % 7BJ' 324.#"32>-   /#- #-W 2AEEEEA22AEDEF@2I7MQRK66KRQL7d""d#*F*7$I*9 X!1##1! 1##1! && %%?tTjM*+%+E2+iX+*:иi^иilU/[/?6+%+/H+Q+%и/и/% и /&иQgиg/`01##>76"'"&554632675#"&55463!26554&#!"&55463!2#!"3!2#"5433232##Z !HGC"NPP$LRN ++bQ+#3иbWиb{T/le+8/+(A++Y`+J+и/ииes01##6676##"&554633275#"&55463!26554&#!"&55463!2#!"3!2#"5433232##!"&5543323!2R H:  Qd %t/#- N#-W )) /)  rk" #l#*H*4%H*8 "$)$IqauɻZ7+2+R?+y+yZ и /7GиZlи|/q+LC+<U+g++^+/&+/и//2и301%#".54>32##6676##"&554633275#"&55463!26554&#!"&55463!2#!"3!24.#"32>7#"5433232##-3Tl9>mQ//Qm>9lT3 H:  Qd %t/#- N#-W &@S-2S<""543276654632354332##"55###"54332 4e*9=>:8 .d0Y/#- #- .J9s)VB 3G+ 0 E? E;/ PV &&&&_ # "`#*6*"%6*& @Y)I#BN,H#$ 2=J0   5f!+tN  C]&  ҺItCT`M*+%+E2+_X+*:и_b[/U/?6+%+/H+Q+%и/и/% и /&01##>76"'"&554632675#"&55463!26554&#!"&55463!2#!"3!2#"54332j "MKG!NST(QUP >FG y/#-  d#-m ))  #  "#*c+N &c+R 8~MbJ`lC +VO++;(+kd+ 0иkng/SL+5,+%>++G+и/иLZ01##6676##"&55463!5#"&55463!26554&#!"&55463!2#!"3!2!"&5543323!2'#"54332p M9  Xg p/#- \#-e /)  #))n " #p#*A*-%A*1 $)$iMtYJ|ǻC +uR++;(++ 0иZиZ/Rbиb/lиl//yN+5,+Wp+%>+g^+G++и/01##6676##"&55463!5#"&55463!26554&#!"&55463!2#!"3!2#!"&55463!26554&#!"&55463!2#!"3!2#"54332p M9  Xg p/#- \#-e /#- #-[ $))W " #Y#*-*%-* "*<*,":*/ gM|D^nzW4+b+/+O<+yr+y и /4Dиrjиj/y|u/g +I@+_+9R+/&+[+/и/0017463!2#!".5##6676##"&55463!5#"&55463!26554&#!"&55463!2#!"3!2"3!26554#"54332 , ,I{ M9  Yf p/#- \#-e   N))X**d " #f#*7*#%7*'  w w n!M|Cfp|Ż_<+h+!7+WD++h и<Lиoиtиzи~w/k+QH+g+AZ+c+7.+7!и!/8и.qиq/01".5546332!546332###6676##"&55463!5#"&55463!26554&#!"&55463!2#!"3!23!26557#"54332?,, , M9  Xg p/#- \#-e  7))Z]&%U " #W#*7*#%7*' `U UMyJq};+H+3 +|u+(к]|9|x/-$+6+ +>+FиHиH/016##"&55463!5#"&55463!26554&#!"&55463!2#!"3!2##66'&&'&''&766766543#"54332  Xg p/#- \#-e MECOX,m9-q } 0))/" #]#*?*+%?*/ $[ 6*! _KDg%pN[MqF^r~W4+i +/+O<+}v+}и/4Dиv_и_/Ai&i6iFiVifiviiiiiii ]Aii]}y/n+I@+9R+d+[+/&+/и/001%#".54>32##6676##"&55463!5#"&55463!26554&#!"&55463!2#!"3!24.#"32>7#"54332F3Tl9>mQ//Qm>9lT3 M9  Yf o/#- \#-e &@S-2S<""+m+6#+n++кZ696r0'+ 9+ k+B+ ииии'9Amиnиoиp01##32#!"&55463!5#"&55463!26554&#!"&55463!2#!"3!2'&&'''&67>7432'35#-   5/#- #-V 6J^= |))tItO+ 0 _""_#*F+9$I+;   3,# !!hADl  % 7BJ' i_324.#"32>35#-   4/#- #-W 2AEEEEA22AEDEF@2I7MQRK66KRQL7d""d#*E*6$H*8 V!1##1! 1##1! && %%9d7+.+7F01#!"&55463!26554&#!"&55463!2#!"3!2"&5!"&55463!2#!#; /#-  #-A | R wU#*`*T%h*P  "" 7+.+7F01#!"&55463!26554&#!"&55463!2#!"3!2"&55!"&55463!2#!#!"&5543323!2- /#- #-W s R !/)  #*H*9$K*; z "" ')$g /#- #-W   P*g""g*#*A*2$D*4  o o +#и/>и/+и+/#3и EиE/ hиpи>sl+8/+h+(A+ZN+J+ZSb01".5546332!546332##!"&55463!26554&#!"&55463!2#!"3!2"&55!"&55463!2#!#3!2655,, ,Y /#- #-W { R x  [Z&/#*=*.$@*0  h""h yT T*/$A*1 75!"&55463!2'#!"&55463!26554&#!"&55463!2#!"3!24.#"32> x,bR5@_m,EE@26Ra,{ R  /#- #-W L6MQQK66KQQM6="b$<-3?#  $2"-<$b"f#*=*.$@*0 A!&&! ''ItC1Em*+PW+"+9>+и>2и9o;/5/+2?+ %+iX+.+XOиO/X]и]/idиd/01#!"&55463!26554&#!"&55463!2#!"3!24332##"55#"554376##"&5"'"&5546332667>b /#-  ^#-g )) {  EH+ GD< E)UO$SQJQ#*S+> &S+B  " "   " Lo1i}߻*++qv+"+к5q9<9vjиqm/+ %+\+jw+.++ %-\JиJ/и/и/и/01#!"&55463326554&#!"&55463!2##"3!2&&''&&'''&6766'432766746324332##"55#"554376##"&55"'"&554632>7>R /#-  G#-I 1EX.l9K\BdX 0  @8B;/  ZK )) x AJ+BA: OTO#NNJ#*3+ &3+" K-OAD+*I  " *iE  3U+_<   <_ " " k"  l i"4td1EQs*+\c+"+27+FK+и7?иFuN/H/+?8+ %+qd+.+d[и[/dgиg/qnиn/01#!"&55463326554&#!"&55463!2##"3!2##"55#"554334332##"543326##"&5'"&554636766 /#-  "#-# & &&&  .h7+9o+ :~9?uh#*D+/ &D+3  " `~"   " ItC1=e*+HO+"+<5+и<g8/2/+ %+aP+.+PGиG/PUиU/a\и\/01#!"&55463!26554&#!"&55463!2#!"3!2#"543326##"&5'"&554632>7>l /#-  ^#-g ))  ?H+ JG@ QTP#SQJT$*Q+= &R+@ ~"   " LjC1Go{* +RY+"+7<+ и*DиD/<sи7yи7}9/v/+%+3@+kU+.+kZQиQ/Z_и_/kfиf/01#!".55463!26554&#!"&55463!2#!"3!2!2##"554&#!"&5546%6##"&55"'"&554632>7>#"54332^ #-  Q/ #-P /)    OTO$ONI))3+ &+3+" #)$"  o m"2Q[1=dz* +aA+"+<5+ и*iиi/*p<|8/mf++S>+%+.+S`BиB/ft01#!".55463!26554&#!"&55463!2#!"3!2#"54332%"&55'"&554632>7>76#!"&5543323!2^ #-  Q/ #-P ))8 LJB MSN!NPL #"54332W /#- #-X #-  Q/ #-P Q AE+DD> OSL#OOK))#*+*$+* 0+ &+0+ a  [ X" L|CEU}ɻ>+`g+6%+ Q+>и/-и>IQи и /N +2)+F+"9+yc+B+yh_и_/hmиm/ytиt/017463!2#!".5#!".55463!26554&#!"&55463!2#!"3!2"3!26554&#6##"&55"'"&554632>7>#"54332 , ,En #-  Q/ #-P   9  OTO$ONI))?*|*3+ &+3+" u ^ ^ %"  t r"7Ly1X* +cj+"++ кD"9SиS//+%+|f+.+|kbиb/kpиp/|wиw/01#!".55463!26554&#!"&55463!2#!"3!2'&&'&''&76676654376##"&55"'"&5546332667>#"54332^ #-  Q/ #-P COX,m9-q } 0f  DH+EB< A'TP$PPM))7+" &+7+& 6*! _KDg%pN"  "\LvLEY߻>+dk+6%++>9/-иFPиP//K+2)+"9+U+}+B+}lcиc/gиg/lqиq/}xиx/и/0174>32#".#!".55463!26554&#!"&55463!2#!"3!232>54.#"6##"&55"'"&554632>7>#"543323Up=8oY77Yo8=pU3~ #-  Q/ #-P -FT'"TI11IT"'TF-  OTO$ONI)) &7##7''8##83+ &+3+" 1!!""O"  m k"Q7+.+7Fи7P01#!"&55463!26554&#!"&55463!2#!"3!2"&5#"&55463!2####"&5##; /#-  #-A  R +U#*`*T%h*P  ""  2+ %+.+>7Fи2Kи7P01#!"&55463!26554&#!"&55463!2#!"3!2"&55!"&55463!2####"&55##!"&5543323!2- /#- #-W < R +/)  #*H*9$K*;  ""  ')$g /#- #-W   P*i""i*#*A*2$D*4  o o +#и/>и/+и+/#3и EиE/ rиzи>}v+8/+r+(A+ZN+J+ZSbиNgиSl01".5546332!546332##!"&55463!26554&#!"&55463!2#!"3!2"&55#"&55463!2####"&55##3!2655,, ,Y /#- #-W  R +k  \[&/#*=*.$@*0  c""c cc ~T T2+.+>7Fи2Kи7P01#!"&55463!26554&#!"&55463!2#!"3!2"&55#"&55463!2####"&55!#7'&&'''&67667432- /#- #-W  R + +p x&'x 0 #*>*/$A*1  ""  )  9I !`;;_! % *oK '25!66 1B@_m,EE@2@0 R  /#- #-W L6MQQK66KQQM6A""C:"{?33?#  $2"2>|"i#*=*.$@*0 A!&&! ''ij<AoB//Bи/:и/'и2>и>/2C +,#+>+5+01%#!"&55463!2'#!"&55463!26554&#!"&55463!2#!"3!2  R o /#- #-= A""#*}+h&}+l +^U+5+Ng+,#+pE+ +01#!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!2#!"&55463!26554&#!"&55463!2#!"3!2  R w /#-  #-Q  /#- #-W W"")#*;*&#6*. #*6*)$:+* <|#3ef/C/f;и;/и/;^и/Cи/CV и /C и /^и/^и/Cи/V7и7/;KиVbиb/Vg +PG+@Y++b7+1(+017463!2#!".57"3!26554#!"&55463!2'#!"&55463!26554&#!"&55463!2#!"3!2 , ,>]    R  /#- #-W \** } } ""h#*F*7$I*9 <|A]g׸h/R/YиhGиG/и/Rи/G'и'/Y1и1/G_9и9/Y>и>/_BиB/_OиR]и]/RfиYibB+,#+P^+5+>+ +01#!"&55463!2'#!"&55463!26554&#!"&55463!2#!"3!2".546332!546332#%3!2655  R  /#- #-W ,, ,7  <""i#*<*-$?*/ ^]&Z Z7432%#!"&55463!2- /#- #-W 6J^= |))tItO+ 0   R #*I*:$L*<   3,# !!hADl  % 7BJ' b""324.#"32>#!"&55463!2- /#- #-W 2AEEEEA22AEDEF@2I7MQRK66KRQL7  R #*F*7$I*9 X!1##1! 1##1!"(("!((8""76&#!"5543!27#!"&55463!2- /#- #-W M*b@>  T]^+Fh [e F Ӕo  R #*F*7$I*9 +)  !  + (+, " ""-+ + ии%4и4/ X//81+ +*A+H"+01%#"54332!2##"554&#!"&5546'"&55463326554&#!"5543!2##"32>765))/) $ --#- =#-;lgd4  ^8#)$p#*v*Z # p*e  b !SkF%++>-+ +%4и4/ U/ +81+*A+H"+01%#"54332!"&5543323!2"&55463326554&#!"5543!2##"326765)) /)  -#- =#-ql adr#)$b#**l # *w qX =pbA+6+ZI+ +и#и#/ -иAPиP/ r/:+TM+1+F]+(+d>+01#"54332#!"&55463!26554&#!"&55463!2#!"3!2"&55463326554&#!"5543!2##"326765))  /#-+ #-o -#- =#-sj  a k#*A*/$A*3 #*m*Q # g*\ |C /dT3+# +L;+ + и+и3BиB/ f/(+F?+ +8O+V0+01%#"54332463!2#!".57"3!26554&#%"&55463326554&#!"5543!2##"32>765)) , ,]]  -#- =#-;lgd4  ^ **   #*v*Z # p*e  |C >Zd0+\D+(+ +и/\LиNи Vиcи f/_?+"+M[+++2 +01#"54332%"&55463326554&#!"5543!2##"36676".546332!546332#%3!26555))-#- =#-yc  [,, ,V   #*q*U # k*` R`^&\ \y1Uaa$+ +`Y+и/C 9`c\/++&+017"&55463326554&#!"5543!2##"32676'&&'&''&7>7432#"54332-#- =#-ph h &o u,*p HsQ+ 0 ))#**h # ~*s >  :_#  i?Dj%9DJ% EpL 3hǻX7+*+P?+ + и /A*&*6*F*V*f*v******* ]A**]7FиF/ j//+JC+%+<S+Z4+01%#"54332#".54>324.#"32>"&55463326554&#!"5543!2##"32>765)) .PiP+0Q9 9Q0+P>$-#- =#-;lgd4  ^)?**?))>**>,* *( ( #*v*Z # p*e  }t#9W+ +8'+8-и8;*/$/+ +/6+01463!2#!".5"3!2654&##"5433232##} ,& ,]  4)) |*E* c  ~"*p#9Oq+ +).+.=и)Cи)Mи)Q+/@/+%2+EL+ +01463!2#!".5"332654&#!2##"554&#!"&5546%#"5433232## , ,]  /) $ /)) **,   #)$8"#9Og+E>+ +8'+8-и8Q*/I:++/6+ +01463!2#!".5"332654&##"5433232##!"&5543323!2 , ,]  <)) /)  **@   |"$)$e#7Mq+ +L;+;L9/A]A)9IYiy ]$. 9./A..]A.).9.I.Y.i.y....... ]LAи[и[/b. и/Lи/$>/)+fW++3+{r+CJ++ + 8и8/WSиS/fjиj/01463!2#!".5"3326554&##".54>32#"5433232#####"&5543323326676676%!"&55463!24&#"326#"&554332 , ,]  u*:$$:**:$$:*9)) &*$/)  '$ & Xl ^0--11--0 **(   6, !,, !+A"P#) $ & ' )(&&% $ |#?U}+9*+ +TC+T1и1/TIиTWF/=&++/4+KR+ +01463!2#!".5"332654&##!"&55463!2#!"3!2#"5433232## , ,]  ` , , 5  $)) **,   ** !  O"qEUkI+>+ Q+6#++и+/#Yи6_и6iи6m\/B+F+ 9+ah+0'+N +01463!2#!".5#!"&55463!26554&#!"&55463!2#!"3!2"3326554&##"5433232## , ,  /#-+ #-o   <)) **#*K*9$K*=    w"p#f|+P=+j+P Pи/595/EиE/5Xm/,////+:S+ry+~+ +AиA/~Iи/^[и[/^aиa//и/01463!2#!".5"3326554&####.55463326554&##"&5546332##"332676676#"5433232##32##"54&##"&5546 , ,]  1  'N!, !, '% 2)) /$  **   N*9$I*< r"3#) #q'7w++aN+ 3++3 9/F+9F/VиV/Fi{и/!+(+Kd++[R+0 +[и/!=и=/!@иlиl/qиq/Rx01463!2#!".546332##".5"3326554&###".55463326554&##"&5546332##"332677>76"3326554#"5433232## , ,,,  6 %K!, !,D" w t >)) ****   N*9$I*<     o"|'7G];+++C+ 3+3Kи Qи [и _N/0 +8+(+SZ+@!+017463!2#!".5463!2#!".5"3!26554&#"332654&##"5433232## , ,]] , ,  "  <)) ****      S"|/?I_3+A+ ;++$+A!и$Hи$Mи+Sи+]и+aP/D+0+"@+U\+8 +01463!2#!".5".546332!546332#"3326554&#3!26557#"5433232## , ,,, ,    7)) **jg&2   l l"w#Lbi+ +aP+8 9HиH/aVиadS/+X_+ +01463!2#!".5"332654&#'.'''&67>7632#"5433232## , ,]  X2c 7cSC?Rb8CpS0 0 )) **1   + Lb ".;D$$E<0 % 32"332654&#4.#"32>7#"5433232## , ,.Oi/]+(+#S+CJ+kb+yp+0 +y8и8/01463!2#!".5#".54>32"3326554&##"5433232##4.#"32>7!"&55463!2'#"&5546332 , ,r%=R--S>%%>R--R>%  <)) m*9 9**9 9* L **+/  // .   j"   ljtd/?i3+ ;+$)++$и!иA//0+8 +"+0146332##".5%34332##"5###"54332"332654&#j , ,9&&&&$  w*M*lPGm k  opd/?U3+ ;+$)+EJ+$иJи/EиJ!и!/EW/G/0+AN+"+8 +0146332##".5%354332##"55###"54332"3326554&#!2##"554&#!"&5546o , ,4&&&&)   /)  **R   #)$o/?Uy3+KD+ ;+$)++$и!иW/O@+0+"+8 +0146332##".5%34332##"5###"54332"332654&#!"&5543323!2o , ,4&&&&)  /)  **wEZY   $)$oqyEaqe+>+ m+V[+MH+H#и#/+и+/M5иVFиHSиMsJ/B+b+ 9+GT+0'+j +0146332##".5#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332"3326554&#o , , /#-  #-O &&&&)  **#*F*4$F*8 +C   o|d'7ScW+++_+HM+?:+? и:3и3/H8и:Eи?e+T)+01".546332!546332#46332##".5%354332##"55###"54332"3326554&#3!2655[,, ,W , ,4&&&&)  9  jh&!**(G   f fow/?ho3+ ;+$)++$и!кT9j/0+"+8 +0146332##".5%354332##"5###"54332"332654&#'.'''&67>7632o , ,4&&&&)  2c 7cSC?Rb8CpS0 0 **X   2 Lb ".;D$$E<0 % '4632354332##"55###"54332"3326554&#o , ,?YM11NYA b[ 0 "- .#/ ZU &&&&)  **![56Y U34T#$ 0e  @9/ :HR)   P'  M   opj';Wg[+(+c+V?+MH+M и /A(&(6(F(V(f(v((((((( ]A((]H2и2/VEиHSиMiJ/-+X+7+GT+`!+0174>32#".46332##".532>54.#"%#"54332354332##"55#"3326554&#.SqD>pV22Vp>DqS. , ,#?V3-VB))BV-3V?#M&&&&)  '+C/.C,,E..E**--..   oq;Wgy[+ c+LQ+C>+C9L<и>IиCi@/X+92+=J+` +0146332##".5'&&'''&76676&#!"5543!2354332##"55###"54332"3326554&#o , ,F/CE@ GPBU Hc P&&&&)  **. " +1 %]3 # 1K   }t#Cu+ +B'+B-и'=ܸ2иB7иBE*/$/+ +/6+9@+01463!2#!".5"3!2654&##"5433232##32##} ,& ,]  4))  |*E* c  ~  p#CY+ +B'+B-и'=ܸ2иB7иBHи'NиB[*/K/+/6+ER+9@+ +@и/01463!2#!".5"332654&##"5433232##32##!2##"554&#!"&5546 , ,]  <))  /) $ **,   X!x!h8#)$qEUuI+>+ Q+6#++и+/#Yи6_и#oܸdи6iи6sи6w\/B+F+ 9+ah+0'+kr+N +01463!2#!".5#!"&55463!26554&#!"&55463!2#!"3!2"3326554&##"5433232##32## , ,  /#-+ #-o   <))  **#*K*9$K*=    {!m!Wp'7Kk߻++B+ 3+jO+OAB&B6BFBVBfBvBBBBBBB ]ABB]jUиOeܸZиj_иjmR/G+(+W^+#=+ah+0 +01463!2#!".5#".54>32"332654&#4.#"32>7#"5433232##32## , ,.OiиK/;/$+5B+ +,+01463!2354332##"55##!".5"332654&#!2##"554&#!"&5546 ,)) ,]  /) $ *qt*,   #)$b#3Io(+?8+/+"+ии"K/C4+$++, +01%#"5##!".5463!2354332"332654&#!"&5543323!25) , ,)  x/)  tT}*5*|%   #)$qX#UeY+N++a+ + и и 3и+;и;/Eиg/R'+V+0I+ +@7+^+01463!2354332##"55##!".5#!"&55463!26554&#!"&55463!2#!"3!2"3326554&# ,)) , /#-* #-o   *k3l*#*K*9$K*=    qD#7G;+q^+C++ и и /и/$C9$/-и-/V;9V/fиf/Vy$/1+8+[t+ +kb+@+k'и'/1MиM/1Pи|и|/и/b01463!2354332##"55##!".546332##".5"3326554&###".55463326554&##"&5546332##"332677>76"3326554&# ,)) ,,,  ,6 %K!, !,D" w t *k.f***   N*9$I*<     |C#7GWK+;$+S+ + и и-и CиY/@1+H+'8+ +P+01463!2354332##"55##!".5463!2#!".57"3!26554&#"332654&# ,)) ,{ , ,]]    *v o***      |C#?OYC+Q)+K+ + и иQ1и 3и;и Xи[/T$+@+2P+ +H+01463!2354332##"55##!".5".546332!546332#"3326554&#3!2655 ,)) ,,, ,    *l/j*jh&2   l lw#3\q'+/+ + и кH/9/XиX/^/$+ +,+01463!2354332##"55##!".5"332654&#'.'''&67>7632 ,)) ,]  ^2c 7cSC?Rb8CpS0 0 *}m*1   + Lb ".;D$$E<0 % 32"332654&#4.#"32> ,)) ,.Oi32"3326554&#4.#"32>7!"&55463!2'#"&5546332 ,)) ,y%=R--S>%%>R--R>%  *9 9**9 9* L *n+U*+/  // .      ljtd#3?i'+/+ +49+ и и4A6/и>/JW;/R/$+5B++, +01##"55###".546332354332"3326554&#!2##"554&#!"&5546%##"54332& , ,&   /)  [&& k**nR   #)$Ao#3IUy(+?8+/++JO+ииJWR/C4+$++, +01%##"5###".546332354332"332654&#!"&5543323!2'##"54332& , ,&  /)  &&:r*-*Y   $)$oqy#UeqZ+N++a++fk+ииk3и3/+;и;/fEиfsn/R'+V+0I++@7+^ +01##"55###".546332354332#!"&55463!26554&#!"&55463!2#!"3!2"3326554&###"54332& , ,& /#-  #-O S  &&5i**i#*F*4$F*8    |o|d#7GWcL+;$+S++X]+ииX-и]CиC/Xe`/@1+H+'8++P +01##"55###".546332354332463!2#!".57"3!26554&#"3326554&###"54332& , ,&S , ,:]    &&g**r**      bo|d#?OYeC+Q)+K+ +Z_+ и иQ1и_3и3/Z;и_XиX/Zgb/T$+@+2P+ +H+0146332354332##"55###".5".546332!546332#"3326554&#3!26557##"54332o ,&& ,,, ,  <  E&&*mFe*jh&,   f fow#3\ho(+/++]b+икH]9]je/$++, +01%##"55###".546332354332"332654&#'.'''&67>7632##"54332& , ,&  2c 7cSC?Rb8CpS0 0  &&j*"*|X   2 Lb ".;D$$E<0 % и>/ i01##"55###".546332354332&&''&&'''&6766'4327>'4632"3326554&###"54332& , ,&?YM11NYA b[ 0 "- .#/ ZU A  &&h**q![56Y U34T#$ 0e  @9/ :HR)   P' .   kopj#7K[gP+8$+W++\a+ии\.и./A8&868F8V8f8v8888888 ]A88]aBиB/\id/=3+L++)G+T +01##"55###".5463323543324>32#".732>54.#""3326554&###"54332& , ,&K.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#|  &&h**q8+C/.C,,E..E)--.._   U tC#37y'+/+ + и и4и 5и9//$+,+ 6+5+01463!2354332##"5##!".5"332654&#35# ,)) ,]  ]|*G@* c  pC#3IM'+/+ + и и8и >иJи KиO/;/$+ L+5B+K+,+01463!2354332##"55##!".5"332654&#!2##"554&#!"&554635# ,)) ,]  /) $ 2*$*7   #)$ub#3IM(+?8+/+"+ииJиKи"O/C4+$+L+, +K+01%#"55##!".5463!2354332"332654&#!"&5543323!235#5) , ,)  x /)  {*5*$!   #)$)qW#UeiY+N++a+ + и и 3и+;и;/Eиfи gиk/R'+V+0I+ h+@7+g+^+01463!2354332##"55##!".5#!"&55463!26554&#!"&55463!2#!"3!2"3326554&#35# ,)) ,  /#-* #-o   ]*z7o*#*K*9$K*=    jw#3\`'+/+ + и кH/9/XиX/]и ^иb/$+ _+^+,+01463!2354332##"55##!".5"332654&#'.'''&67>7632'35# ,)) ,]  ^2c 7cSC?Rb8CpS0 0 *#*?   * Lb ".;D$$E<0 % '463235# ,)) ,]  ?YM11NYA b[ 0 "- .#/ ZU *%m *<   ![56Y U34T#$ 0e  @9/ :HR)   P' RvpK#7G[_<+R.+C+"+ииHиH/AR&R6RFRVRfRvRRRRRRR ]ARR]\и]и"a/W)+8+^+3M+]+@ +3и/01%#"55##!".5463!2354332#".54>32"332654&#4.#"32>35#5) , ,).Oi'43232##"554&##"&5546 , ,  ]  ?VVA 4G, , -<$0/%  **""   O01P& 9CR6  '!<5+  ,#)$<'7MyC<+ + 3+<и/C+и+/ OG8+(+0 ++и !01463!2##!2#!"&55463!5#".57"3!26554&#!"&5543323!2 , ,  ]  p/)  **""   ')$ '   b  b  **""`**   ~ ~ 7632 , ,  ]   -p t+*nCqR1 0 **""    DZ " mEGr % 9HP' +9S+F ++и !01463!2##!2#!"&55463!5#".5#".54>32"3!26554&#4.#"32> , ,  _@^m-EE@1@^m,-m_?   6LQQK55KQRK6**""3@$  %2"2?$ $@{   R!''! ((?t6F\:+/+ B+[J+[Pи[^G/M/7+/#+? +RY+/и/#и//*и*/ 001463!2##>76"'"&554632675#".5"3!26554&##"5433232## ,) ,l!HGC"NPP$LRN '432766'4632"3326554&##"5433232## , ,_J<  Qd o?WL7o+XB 6I, 0 D; ?B/ VP   2)) **h" #iP/-J" >V,K$$ 8@K.   3d){S  !Bf"  6   < "Iq,@TdzX+%+ `+yh+h-X7и7/XKynиy|k/P2+U+<F+]x+%+%и/01463!2##6676##"&5546335#".5#".54>324.#"32>"3326554&##"5433232## , ,_J<  Qd o3Tl9>mQ//Qm>9lT3G&@S-2S<""76"'"&554632675#".5"3!26554&##"54332 ,) ,k"MKG!NST(QUP >FG y]  =))v**  #  "   ~Mb,<R^}0+HA+%+ 8+]V+]`Y/L=+-+%+5 +%и/ &01463!2##6676##"&55463!5#".57"3326554&#!"&5543323!2'#"54332 ,! ,lM9  Xg p]  U/)  #))** " #   $)$_MtX,^nzb+W4+%+ j+O<+4DиD/<rиOxиO|u/[0+_+9R+I@+g +%+%и/ &01463!2##6676##"&55463!5#".5#!"&55463!26554&#!"&55463!2#!"3!2"3326554&##"54332 ,! ,lM9  Xg p /#- #-[   7))**[ " #]u"*D*4"B*7    f*M|C,HXbnL+Z2+%+ T+D=+Z:и=aи=fиDlиDpi/]-+I+;Y+Q +%+%и/ &иcиc/01463!2##6676##"&55463!5#".5".546332!546332#"3326554&#3!26557#"54332 ,! ,lM9  Xg p,, ,    7))**j " #l`c&1   zW WMv,<_ks0+%+ 8+jc+Nj9jmf/-+5 +%+%и/ &01463!2##6676##"&55463!5#".57"3326554&#'&&'''&7>7432#"54332 ,! ,lM9  Xg p]  ; &o v,*pHsQ+ 0 ))**o " #q     :_"  iABk" %9DJ% IMqF,@TdpX+K7+%+ `+oh+o-и-/hAиA/AK&K6KFKVKfKvKKKKKKK ]AKK]ork/P2+U+<F+] +%+%и/ &01463!2##6676##"&55463!5#".5#".54>324.#"32>"3326554&##"54332 ,! ,lM9  Xg p3Tl9>mQ//Qm>9lT3G&@S-2S<""7632'35# , ,5  2]   -p t+*nCqR1 0 **""    DZ " mEGr % 9HP' o/и/'AO']A']A']A']A]AO]A]A]и/  и /': ?$/+0)+ +)801463!2#!".57"3!26554&#"&5!"&55463!2#!# , ,+]  | R w**   ="" +)6+F ++)и&01463!2#!".5!5!"&55463!2#!32##"554&#!"&5546"3!26554&# , ,F { R x/)  h  **z""z#)$   ++ +0)+)801463!2#!".57"3!26554&#"&55!"&55463!2#!#!"&5543323!2 , ,F]  s R !/)  **   | "" ')$<|CSH+2+ O+и/ 5и5/H<и , ,F]  c  `*z""z***     75!"&55463!2#!4&#!"3!265F , -p t+(q?lR3{ R x h  **Db " tGJw  % 4CK&g""n  75!"&55463!2#!463!2#!".57"3!26554&#4.#"32>*@_m,EE@26Ra,{ R x,bR5 , ,F]  !6MQQK66KQQM6 3?#  $2"-<$u""u$<y**   N!&&! ''C+38+T +(!+3и!001463!2#!".5#!"&5546335!"&55463!2#!!2#!"!2#!3!2"3!26554&# , ,F_ , ,{ R x       **k**b""b  D " F '   32"3!26554&#!"&5546335!"&55463!2#!32!"&55463!24.#"32> , ,F(@S*+SA((AR+*SA(K  C f{ R xh `.::..::.** , , , ,   !L""L d ItC'7_++BI+ 3+ + иa//(+!+[J+0 +JAиA/JOиO/01463!2#!".54332##"55#"5543"3!26554&#6##"&5'"&554632>7> ,# ,l))     @K+ GD< QUO#RRJ** "    "   6" L['7`vû++BI+ 3+ + и+eиe/+lx/ib+(+!+0 +iEиE/\ܹMAиA/MJиJ/MPи\WиW/bp01463!2#!".54332##"55#"5543"3326554&#6##"&55"'"&554632>7>!"&5543323!2 , ,]))    @H+"C!= OTO#NNJ/)  ** ;i $    "   "'#)~$LqWEYۻ+>+dk+ +MR+R#и#/+и+/M5и5/RFиMI/B++ 9+0'+ +}g+FS+}lcиc/lqиq/}xиx/01463!2#!".5#!"&55463!26554&#!"&55463!2#!"3!24332##"55#"554376##"&55'"&554632>7>"3326554&# , , /#- #-X Y)) x ?H+EC> OTO#NNJp  *{*M#*-*$-*  " u"   |" ] ] L}C/Ck{ͻo+}+NU+&w++} и0и6и<ии3/+l+|+t)+gQ+0=+gVMиM/V[и[/gbиb/01".5546332!546332#463!2#!".54332##"55#"554376##"&55'"&554632>7>"3326554&#3!2655;,, , , ,])) x ?H+EC> OTO#NNJp    QO&)*}* " t"   " _ _ fF FLy'Nvz+Y`+ + + к: 9/w+r\+ +!+raXиX/afиf/rmиm/01463!2#!".54332##"55#"5543'&&'&''&76676654376##"&55'"&554632>7>"3326554&# , ,])) COX,m9-q } 0s ?H+EE> OTO#NNJp  **Y $ 6*! _KDg%pN"   " p p 4td'3Ce7+NU+ ?++(-+!и(g0/*/4+!+cV+< +VMиM/VYиY/c`и`/0146332##".5##"55#"554334332##"54332"3326554&#6##"&5'"&554636766n , ,E& &&&g    .h7+9o+ :~9?u|** " W~y   "   !" ItC#/Wq+:A+ +.'+.Y*/$/+SB+ +B9и9/BGиG/01463!2#!".57"3!26554&##"543326##"&5'"&554632>7> ,# ,]  ;))  ?E+!LH> QUO#TSJ**   ~"  '" Q[#/Vl+b[+S3+ +.'+.n*/_X++ +_0и0/EܹR4и4/Xf01463!2#!".57"3326554&##"54332%"&55'"&554632>7>76#!"&5543323!2 ,! ,]  ;))8 LJB MSN!NPL +_f+ Q++#и#/+и+/5и5//B+F+ 9+0'+N +xb+xg^и^/glиl/01463!2#!".5#!"&55463!26554&#!"&55463!2#!"3!2"3326554&#6##"&55'"&554632>7>#"54332 ,! , /#- #-X    @H+ GD< MSN!NPL))**V#*2* $2*$  f f   o k"*.#h""h>*1 **   h<|';K[_O;+\+]+2X+;и/2и/O?и?/XGиG/2aT!++<+L+D5+ +ии\и^01746335#"&55463!2##32#!".5463!2#!".57"3!26554&#"3!26554&#%35# ,& R * ,8 , ,@]  ]  ]*{""{***   | | <{+EV+5+^O+,#+ +01#!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!2463!2#!".57"3!26554&#  R x /#-  #-Q , ,E]  s"" #*H*8#H*; **   <|#3GWX/S/X4и4/и/S>и/4K'и'/S/и//K7и7/S8и8/SAиA/KBиB/>YPA+$+7H+,+ +01#!"&55463!2463!2#!".57"3!26554&#463!2#!".57"3!26554&#  R  , ,E]   , ,>]  @""m**   **   7632%#!"&55463!2 , ,E]   -p t+*nCqR1 0   R **    DZ " mEGr % 9HP' }""tC /E# +++ + 1// +(+01#"54332463!2#!".5"3!2654&#5))I ,& ,]  ~*E* c  pC /E_# +++ + 4и:и G/7/ +1>+(+01%#"54332463!2#!".5"332654&#!2##"554&#!"&55465))J , ,]  /) $  **,   #)$b#/EU+;4+ +.'+.G*/?0++ +01463!2#!".5"332654&##"54332!"&5543323!2 , ,]  Z))/)  **@   ~#)$|T /Kk# +E6+++ + =и=/ M/I2+ +;@+(+01#"54332463!2#!".5"332654&##!"&55463!2#!"3!25))J , ,]  y , , 4  **,   ** !  qX#/a+Z7+ +.'+'?и7GиG/.Qи.c*/^3++<U+LC+ +01463!2#!".5"3326554&##"54332#!"&55463!26554&#!"&55463!2#!"3!2 , ,]  Z))  /#-+ #-o **   v3X#*K*9$K*= qD'7w++aN+ 3++3 9/и/F+9F/VиV/Fi{и//!+(+Kd+[R+0 +[и/!=и=/!@иlиl/qиq/Rx01463!2#!".546332##".5"3326554&###".55463326554&##"&5546332##"332677>76"3326554#"54332 , ,,,  ,6 %K!, !,D" w t H))****   N*9$I*<     o1|C /CSoG0+# +:O+ + и+и U/(+3D+ +L=+01%#"54332463!2#!".57"3!26554&#463!2#!".5"332654&#5)) , ,]]   , ,]   **   **,   }C /KU# +M5+++ +M=и?и GиTи W/P0+ +>L+(+01#"54332463!2#!".5"3326554&#".546332!546332#%3!26555))J , ,]  ,, ,V  '5|**   jh&k kw#LXW+ +WP+8 9HиH/WZS/+ +01463!2#!".5"332654&#'.'''&67>7632#"54332 , ,]  ^2c 7cSC?Rb8CpS0 0 ))**,   ( Lb ".;D$$E<0 % и>/01463!2#!".5"332654&#&&''&&'''&6766'4327>'4632#"54332 , ,]  ?YM11NYA b[ 0 "- .#/ ZU Z))**,   ![56Y U34T#$ 0e  @9/ :HR)   P' pK /CW# +N:+++ +DиD/AN&N6NFNVNfNvNNNNNNN ]ANN] Y/S5+ +?I+(+01%#"54332463!2#!".5"332654&##".54>324.#"32>5))J , ,]  p.Oi++O и#3и3/Vи+[и>aи>kи>o^/J+(A+N+cj+8/+R+01".546332!546332##!"&55463!26554&#!"&55463!2#!"3!2332655#"5433232##+$+ ,M  /#-+ #-o  ")) O^&.#*K*9$K*= }{ {"p]s}u+G4+a+u иGи/G-u9-/<и9>/NиN/>as|ии/x)+C\+++SJ++Sи/)5и5/)8иxdиd/xiиi/Jp01".546332!546332#46332##".5##".55463326554&##"&5546332##"332677>76"3326554#"5433232##%332655+$+ ,",,6 %K!, !,D" w t >))   TY&**) N*9$I*<     o"y yi7y+cP++}+ к!9!/)и3и3/8и8/H9H/XиX/Hkии//B+Mf+*+++]T++]%и%/]/и//B?и?/nиn/01".546332!546332#".5463323546332#'##".55463326554&##"&5546332##"33267667672#"5433232##332655332655+$+ ,h'' ,  $J!,~ !,?$  *))   U o TY&1tr&& L*7$G*:  q"y yt t|/?I_A+3++$+ ;+A!и$Hи;Mи Sи ]и aP/8 +"@+0+U\+D+@TиT/017463!2#!".5".546332!546332#"3!26554&#332655#"5433232## , ,]+$+ ,    ")) **qh&    !|7AKaC!+9+3,++9 иC)и@и,JиOиUи_иcR/<+8+*B+W^+F+01".546332!546332#".546332!546332#3!2655332655#"5433232##0,, ,+$+ ,   ")) jg&\&l l "wDNdyF++cR+F к09@и@/MиcXиcfU/E+Za+I+01".546332!546332#'.'''&67>7632332655#"5433232##+#+ ,G2c 7cSC?Rb8CpS0 0  #)) ~{&R Lb ".;D$$E<0 % 32%".546332!546332#4.#"32>7#"5433232##332655,.OiY<+*+8017".5463323546332#34332##"5###"54332332655++ ,&&&&  |&lPG opd7MWO++,1+=B+O и,иBи/=#иB)и)/Vи=Y /?/N+9F+*+R+01".5463323546332#%354332##"55###"54332!2##"554&#!"&5546332655++ ,&&&&k /)  V  6b&#)$Q o7MWO+C<++,1+#+O и,и)иVи#Y /G8+N+*+R+Nи/01".5463323546332#%34332##"5###"54332!"&5543323!2332655++ ,&&&&/)  >  v&w>Z$)$l oqyMisk+F#++^c+UP+k иP+и+/#3и3/U=и^NиP[иrиUuR/J+(A+j+O\+8/+n+01".5463323546332##!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332332655++ , /#-  #-O &&&&  LT&1#*F*4$F*8 -{ {o|d/?[e]+3++$+PU+GB+G и]!иB;и;/P@иBMи$dиGgD/8 +"\+0+AN+`+\@и@/01%463!2#!".5".5463323546332#"3!26554&#354332##"55###"54332332655 , ,=O++ ,9  d&&&&  x**xb&   ߂ o|d7S]g_!+U+3,+HM+?:+U и:и/?и_)иH8и:Eи:\и\/,fи?i7632332655++ ,&&&&a2c 7cSC?Rb8CpS0 0 o  r&  Lb ".;D$$E<0 % '4632354332##"55###"54332332655++ ,?YM11NYA b[ 0 "- .#/ ZU &&&&  ;b&=![56Y U34T#$ 0e  @9/ :HR)   P'  ߂ opj/C_ia+0++$+^G+UP+U и /a!A0&060F0V0f0v0000000 ]A00]P:и:/^MиP[и$hиUkR/5+"`+?+O\+d+`NиN/0174>32#".".5463323546332#32>54.#"%#"54332354332##"55#332655.SqD>pV22Vp>DqS.D++ ,W#?V3-VB))BV-3V?#M&&&&  '+C/.C,,E..E@b&--..  oqv?[e]++++PU+GB+] иG2и2/9иG=и=/P@иBMиdиGgD/"'+;+\+AN+05+`+01".5463323546332#!3!2#!"&55463!2#!"!2354332##"55###"54332332655++ ,  , ,   &&&&  LT&K **  I " ){ {t;E=++:+= и:%и5ܸ*и:/иDи:G/"/@+'.+18+<+017".546332!546332##"5433232##32##332655+)+ ,.))    z&~  8 p;Q[S++:+S и:%и5ܸ*и:/и:@иFиZи:]"/C/'.+R+=J+18+8VиV/01".546332!546332##"5433232##32##!2##"554&#!"&5546332655+#+ ,+))  /) $   /h&5!x!h8#)$E ;Q[S+G@++:+S и:%и5ܸ*и:/иZи:]"/K<+'.+R+V+18+01".546332!546332##"5433232##32##!"&5543323!2332655+#+ ,+))  /)    o&~!!#)$o |7Aak˻c!+9+3,++9 иc)и@иEиAиKи[ܸPиUи_и,jиmH/<+8+MT+*b+W^+^fиf/01".546332!546332#".546332!546332#3!26557#"5433232##32##%3326550,, ,+$+ ,  7))    jh&\&l l!o!Y tC+5o-+5++- и5и%и'и7/"/0+,+&+017".546332!54633234332##"5##332655+,+)) ,  z8 &2 pC+AKC+++C и%и'и0и6иJиM/3/B+-:+&+F+01".546332!546332354332##"55##!2##"554&#!"&5546332655+#+)) ,/) $ 5  /h{&m#)$E p+AKC+70+++C и%и'иJиM/;,+B+&+F+01".546332!546332354332##"5##!"&5543323!2332655+#+)) ,u/)  k   Y&z#)$o |S+GQI+A2+++I и%и'и9и9/PиS/E.+H+7<+&+L+01".546332!546332354332##"55###!"&55463!2#!"3!2332655+#+)) ,g , , 5  ~  4h &V** !  a qX+]g_+V3+g++_ иgи%и'и;и3CиC/Mиi/Z/+8Q+^+&+H?+b+01".546332!546332354332##"55###!"&55463!26554&#!"&55463!2#!"3!2332655+#+)) ,l /#-* #-o y  O^4n&.#*K*9$K*= }{ {qD+?'+iV++ + иии/%и%/'к,9,/ 5и5/N9N/^и^/Nq, /9+Sl++&+cZ++c/и//9EиE/9Hиtиt/yиy/Z01".546332!546332354332##"55##46332##".5##".55463326554&##"&5546332##"332677>76"3326554&#332655+#+)) ,7,,6 %K!, !,D" w t   TY1i&**) N*9$I*<     ly y|C+?OYQ+C,+Y++Q иYи%и'и5иKи[/H9+P+/@+&+T+01".546332!546332354332##"55##463!2#!".57"3!26554&#332655+#+)) , , ,]]    4hv&**    |C+GQ[S+I1+[++S и[и%и'иI9и;иCиPи]/L,+:H+R+&+V+01".546332!546332354332##"55##".546332!546332#%3!2655332655+#+)) ,,, ,V   H\4q&4jh&l l w+T^V+^++V и^и%и'к@^9^PиP/`/U+&+Y+01".546332!546332354332##"5##'.'''&67>7632332655+#+)) ,M2c 7cSC?Rb8CpS0 0  &K Lb ".;D$$E<0 % 324.#"32>332655+#+)) ,_.OiY<+&+8017".546332354633234332##"5####"54332332655++&& ,&&J  |9Ҿ&~ opd+AKWC+K++LQ+C иKи%и'иL0иQ6и6/LY3/T/B+-:+&+F+01".5463323546332354332##"55##!2##"554&#!"&5546332655##"54332++&& , /)  V  &&6b l&|#)$Q o+AMWO+70+W++BG+O иWи%и'иBYJ/;,+N+&+R+01".5463323546332354332##"5##!"&5543323!2'##"54332332655++&& ,/)  &&O  v8{&s$)$ o|r+GQ]I+A2+Q++RW+I иQи%и'иR9и9/R_Z/E.+H+7<+&+L+01".5463323546332354332##"55###!"&55463!2#!"3!2332655##"54332++&& , , ,   A  &&;b%g&O** !  e oqy+]gs_+V3+g++hm+_ иgи%и'иm;и;/3CиC/hMиhup/Z/+8Q+^+&+H?+b+01".5463323546332354332##"55###!"&55463!26554&#!"&55463!2#!"3!2332655##"54332++&& , /#-  #-O :  &&LTHa&1#*F*4$F*8 z{ {o|d?OYeQ+C+,Y+3.+Z_+Z иQ!иY#и.9и,;и_KиK/Zgb/H +"P+@+-:+T+01%463!2#!".5".5463323546332354332##"55##"3!26554&#332655##"54332 , ,:L++&& ,6    &&x**xbg&    o|dGQ[gS!+I+4[+;6+\a+I иaи/\иS)и[+и6Aи4CиaPиP/\id/L+H+*R+5B+V+01".546332!546332#".5463323546332354332##"55##3!2655332655##"54332[,, ,++&& ,D   &&jh&XFi&f f} }ow+T`jb+j++UZ+b иjи%и'к@U9Ul]/a+&+e+01".5463323546332354332##"55##'.'''&67>7632##"54332332655++&& ,2c 7cSC?Rb8CpS0 0  &&O  r }&N Lb ".;D$$E<0 % '4632332655##"54332++&& ,?YM11NYA b[ 0 "- .#/ ZU (  &&;b+g&=![56Y U34T#$ 0e  @9/ :HR)   P'  opj?S]iU+@+,]+3.+^c+^ и /U!и]#и.9и,;A@&@6@F@V@f@v@@@@@@@ ]A@@]cJиJ/^kf/E+"T+-:+O+X+0174>32#".".5463323546332354332##"55##32>54.#"332655##"54332.SqD>pV22Vp>DqS.D++&& ,Q#?V3-VB))BV-3V?#  &&'+C/.C,,E..E@b!g&--..͂  tC+59-+9++- и9и%и9'и4и7и;/"/0+8+,+7&+017".546332!546332354332##"5##33265535#+,+)) ,  DzG&2 ppC+AKOC+O++C иOи%иO'и0и6иJиMиQ/3/N+B+-:+M&+F+01".546332!546332354332##"55##!2##"554&#!"&554633265535#+$+)) ,/) $ 5  D,kp{&j#)$? Cup+5KO-+A:+O++- иOи%иO'и4иMиQ/E6+N+0+M&+Nи/,01".546332!546332354332##"55##%332655!"&5543323!235#+$+)) ,  l/)  w'& #)$"qX+]gkɻ_+V3+k++_ иkи%иk'и;и3CиC/Mиfиiиm/Z/+8Q+j+^+H?+i&+b+01".546332!546332354332##"55###!"&55463!26554&#!"&55463!2#!"3!233265535#+$+)) ,k /#-* #-o y  DIds8l&4#*K*9$K*= z~ ~;g|C+GQ[_ûS+I1+_++S и_и%и_'иI9и;иCиPиZи]иa/L,+:H+^+R+]&+V+01".546332!546332354332##"55##".546332!546332#%3!265533265535#+$+)) ,,, ,V   DBb n4^&:jh&l lۂ Jow+T^bV+b++V иbи%иb'к@b9PиP/]и`иd/a+`&+Y+aи/U01".546332!546332354332##"55##'.'''&67>763233265535#+#+)) ,O2c 7cSC?Rb8CpS0 0  Dt$&G Lb ".;D$$E<0 % '463233265535#+#+)) ,?YM11NYA b[ 0 "- .#/ ZU C  D*rtu&N![56Y U34T#$ 0e  @9/ :HR)   P'  GrpK+?S]aU+J6+a++U иaи%иa'и@и@/AJ&J6JFJVJfJvJJJJJJJ ]AJJ]\и_иc/O1+`+T+;E+_&+X+;"и"/01".546332!546332354332##"55###".54>324.#"32>33265535#+#+)) ,_.OiY<+D+C&+8017".5463323546332354332##"5####"5433233265535#++&& ,&&J  D|R>&~ ko+AMW[O+70+[++BG+O и[и%и['иVиYиB]J/;,+[+R+Y&+[йN01".5463323546332354332##"55##!"&5543323!2'##"5433233265535#++&& ,/)  &&O  D m$&}$)$ E</9ϸ:/и/A]A]AO]Aq1и/1!и$A$]AO$]A$]A$q+$8и+;/'/ +"0+5/+и/01!2#!"&55463!5#".546332!546332#%3!2655  ++ ,'  ""w& YEX4/4>Y"0+;H+ +4иии/и;PиH^01!2#!"&55463!5#".546332!546332#%3!265532##"554&##"&5546!32##"554&##"&5546  ++ ,B  /$  L/$  h""hml&_ _J#)$#)$</9OD+++$+D!и!/D0и0/$8и>и>/+Q/'/I:+"0+ +5/+и/01!2#!"&55463!5#".546332!546332#%3!2655!"&5543323!2  ++ ,B  Z/)  ~u""u%vu&h hM')$]  N  c""cqp&** ~ ~ c cV+"L+Q/+ +и/01!2#!"&55463!5#".546332!546332#".546332!546332#3!26553!2655  ++ ,B,, ,=  4  ["#[ml&ba&_ _\ \76323!2655  ++ , -p t+*nCqR1 0  g""gqp& DZ " mEGr % 9HP' c c324.#"32>3!2655  ++ ,[@^m-EE@1@^m,-m_?I6LQQK55KQRK60  c""cqp&w3@$  %2"2?$ $@3!''! ((`c c?t>T^V(+!+:3+SB+V0иSHи3]иS`?/E/!+1U+Z>+JQ+!и/и/!и/>"01>76"'"&554632675#".546332!546332##"5433232##3!2655!HGC"NPP$LRN /]N+*d++CJ+i7+и/и7016676##"&554633275#".546332!546332##"5433232##!"&5543323!2332655H:  Qd %r+#+ ,)) /)   x" #yml&"$)$_ _Ip7p3!+YQ+3,+t+AY&Y6YFYVYfYvYYYYYYY ]AYY]QY9/)к;!9BQY9YVиV/zи,иw/*+e+|+7+и/и7иeSиS/016676##"&554633275#".546332!546332#&&''&'''&67>'432766'4632#"5433232##332655H:  Qd %u+#+ ,h?WL7o+XB 6I, 0 D; ?B/ VP R))   _" #` ih&P/-J" >V,K$$ 8@K.   3d){S  !Bf"  r "d[ [?td8T^V"++4-+IN+@;+V*иI9и;Fи-]и@`C/=/++U+Z8+и/и/и/8иZGиG/:016676"'"&5546375#".5463323546332#734332##"5###"54332332655X7^8":9=$8x; +d6Z++ ,&&&&  "  # "M&rlPGw wJp8qC"+++yt+*иZ,и,/Z4<"y9CZ9RиR/ZWиW/rиtиZи/-иyv/EX5/5>YEX/>Y++s+f+5и/и/иии8иfTиT/и/016676#'"&55463275#".55463323546332#&&''&'''&67>'432766'4632354332##"55###"54332332655`4e*9=>:8 .d0Z++ ,?WL7o+XB 6I, 0 D; ?B/ VP &&&&  ` # "aa`&P/-J" >V,K$$ 8@K.   3d){S  !Bf"  ҺS SItC>JTL(+!+:3+IB+L0и3SиIVE/?/!+1K+P>+!и/и/!и/>"01>76"'"&554632675#".546332!546332##"543323!2655"MKG!NST(QUP >FG z+9+ ,,))     #  "W&W~} MjD6LXbZ ++2++WP+Z(иW;и;/PAиA/ZIиI/+aиWd>/S/)Y+8E+^6++и/и/6016676##"&554633275#".546332!546332#!2##"554&#!"&5546%#"543323!2655O6  Xg t+1+ ,/)  A))  ` # #a ih&#)$4n[ [Mb4JV`X+@9++0)+UN+X&и)_иUbQ/D5+'W++\4+и/4016676##"&55463!5#".546332!546332#!"&5543323!2'#"543323!2655M9  Xg q+1+ ,D/)  #))  w " #yml&$)$__ _MtY4fr|Żt+_<++0)+qj+t&иjDиD/<LиL/qVиV/){иq~m/c8+AZ+'s+QH+x4++и/4016676##"&55463!5#".5546332!546332##!"&55463!26554&#!"&55463!2#!"3!2#"543323!2655M9  Xg q+1+ ,J /#- #-[ $))  X " #Z[Z&"*D*4"B*7 t$KM MM|DJZfph4+N+-+F?+e^+e и /h<и^VиV/?oиera/S +=g+K+lJ++"++и/+-и-/J.017463!2#!".56676##"&554633275#".546332!546332#"3!26554#"543323!2655 , ,IO6  Xg t+1+ ,  N))  d**^ # #_ ih&   tf[ [M|C4PZfpûh+R:++0)+LE+h&иRBиEYиE^иLdи)oиLra/U5+CQ+'g+l4++и/4и[и[/016676##"&55463!5#".546332!546332#".546332!546332#%3!26557#"543323!2655M9  Xg q+1+ ,,, ,>  7))  ` " #bed&`c&W WCW W</9=1+:+;++$+1!и$8и+?/'/ +"0+5/+и/и:и/<0132#!"&5546335#".546332!546332#%3!265535#  ;++ ,'  ""u& M</9OSD+P+Q++$+D!и!/D0и0/$8и>и>/+U/'/B;+"0+ +5/+и/и;IPи/R0132#!"&5546335#".546332!546332#%3!2655!"&5543323!235#  .++ ,B  Z/)  T~{""{%vu&h hM')$}{иA ///6+(!+:+!001".546332!546332#"&5!"&55463!2#!#3!2655++ ,| R w  z>& 9"" r r+J+&+1и.01".546332!546332#!5!"&55463!2#!32##"554&#!"&55463!2655++ , { R x/)  Q  dc&u""u#)$V V<5?UKD+2++DK9/7 и>иW //O@+6+:+(!+!001".546332!546332#"&55!"&55463!2#!#3!2655!"&5543323!2++ ,s R   Z/)  gf& "" )c c4')$<|KUM#+:'++#и/M и=и=/MDиD/TиW //I+L+;@+P+0)+;&и)801".546332!546332##!"&5546335!"&55463!2#!32#!"3!23!2655++ ,] , ,{ R x    dc&**u""u !  V VR++ ,T  N  `*u""u*dc&   V V75!"&55463!2#!3!2655++ ,-p t+(q?lR3{ R x  gf&Db " tGJw  % 4CK&g""nY Y75!"&55463!2#!".546332!546332#4.#"32>3!2655*@_m,EE@26Ra,{ R x,bR5++ ,6MQQK66KQQM60  3?#  $2"-<$u""u$<zdc&X!&&! ''qV V7>%3!2655+3+ ,)) {  @K+ GD< QUO#RRJ@  g'wv&{ " "   6" i iLqW1Ma7+*+ls+IB+UZ+Zи/и/U!и!/?иZNиBиUQ/.+ %+@++2+o+N[+tkиk/tyиy/и/01#!"&55463!26554&#!"&55463!2#!"3!2".5546332!546332#4332##"55#"554376##"&55'"&554632>7>%332655W /#- #-X +)+ ,)) x ?H+EC> OTO#NNJW  #*-*$-* AKJ& " u"  } z"= =LoTh+sz+\a++ к\9'zs9aUии\X/EX/ >YvF++Ub+F4и4/hܸ{rиr/{и/и/01".5546332!546332#&&''&&'''&6766'432766'46324332##"55#"554376##"&55"'"&554632>7>%332655+)+ ,>^N68L5eY 0 C6=D/ 0>$ )) x AJ+BA: OTO#NNJW  ML&L3+J H+W? " +oJ  7['k@   81)   0 " z"   "? ?4td/;]g_+FM++!+05+_ и!)иfи0i8/2/^+)"+[N+b+NEиE/NQиQ/[XиX/01".5463323546332###"55#"554334332##"543326##"&5'"&554636766%332655++ ,& &&&  .h7+9o+ :~9?u  oml&9 " R~+"   !" _ _ItC'OYQ+29++&+Q иXи&["//P+K:+T+:1и1/:?и?/01".546332!546332##"543326##"&5'"&554632>7>3!2655+3+ ,*))  ?E+!LH> QUO#TSJG  ]1|{&~"  "" n nQjC1Xdnf+U5++!&+f иf.и./&\и!bиmи!p#/_/e+*+GX+i+GT6и6/01".5546332!546332#!2##"554&#!"&55467"&55'"&554632>7>76#%#"543323!2655+1+ ,/)  t FD< MSN!NPL @H))  XW&#)$! p"  t 4J JQ[BXdnf+NG+?++c\+f иmиcp_/KD+e+i+Kи/1ܹ> и /DR01".5546332!546332#"&55'"&554632>7>76#!"&5543323!2'#"543323!2655+1+ , LJB MSN!NPL 7>#"543323!2655W /#- #-X +1+ , @H+ GD< MSN!NPL))  #*2* $2*$ 7ON&1  o k"{A AQvL/Cjvϻx+gG++$+un+xи/x!и"и"/x0n:и:/$иuq/5+"w+?+Yl+{+lDиD/YfHиH/kиk/0174>32#".".5546332!546332#32>54.#"7"&55'"&554632>7>76##"543323!26552Vp>8oX77Xo8>pV2 +1+ ,,ET)$TH00HT$)TE,@ FD< MSN!NPL @H))  '8%$8((9%%9XW&R""# # p"  t IJ J& 9"" 9 r r<?I_UN+<+29++NU9/A иHиa //YJ+@+D+(!+!0и!:01".546332!546332#"&55!"&55463!2####"&55##3!2655!"&5543323!2++ , R +  [/)  gf& ""  c c4')$R++ ,T  N  `*u""u*dc&   V VJu7+7AM&M6MFMVMfMvMMMMMMM ]AMM], M9,/X4и7_и>k0/:/R+5W+aH+['++ иe01%#".54675#"&55463!2##".546332!546332#4.#"32>3!265525#66*@_m,EE@2G3 R -"++ ,6MQQK66KQQM60  = > 3?#  $2"4@"" !*dc&X!&&! ''qV VP}~<+5m6/ /6и/-и/-и ' 4и'7/#/ +,+0+01%#!"&55463!2%".546332!546332#%3!2655  R \++ ,'  A""x& +5+P^+,#+bB+ +01#!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!2".5546332!546332#%3!2655  R x /#-  #-Q ++ ,B  o""#*H*4#D*; #]\&O O<|#3OYݸZ/D/Z9и9/и/9Qи/Dи/DKи/Dи/Qи/Q'и'/D/и//Q4и4/QAиDXиK[=/G/,+BP+$+T4+ +01#!"&55463!2463!2#!".57"3!26554&#".546332!546332#%3!2655  R  , ,>]  U++ ,B  2""** ~ ~ 0qp&c c76323!2655#!"&55463!2++ , -p t+*nCqR1 0   R qp& DZ " mEGr % 9HP' c c""tD '1U)+#+ +)и0и 3//, +(+01#"54332".546332!546332#3!26556))+6+ ,  ~&2 pC !=Go?'+92+ + ии?/и2Fи I//0>+ +B"+01%#"54332!2##"554&#!"&5546'".546332!546332#%3326555))/) $ .+#+ ,  6#)$mh&؅ p !=Ge?'++92+ +?/и2Fи I/ +0>+B"+01%#"54332!"&5543323!2".546332!546332#%3326555))/)  r+#+ ,  t~#)$o& qX =Yc[C+6+UN+ +и#и#/ -и[KиNbи e/:+1+LZ+(+^>+01#"54332#!"&55463!26554&#!"&55463!2#!"3!2".546332!546332#%3326555)) /#-* #-o +#+ ,  '5V#*K*9$K*= ^&{ {qD/o +YF+++ к9/%и%/>9>/NиN/>as{и{/и/x)+C\++SJ++Sи/)5и5/)8иxdиd/xiиi/Jp01".546332!546332#46332##".5##".55463326554&##"&5546332##"332677>76"3326554&#332655#"54332+#+ ,7,,6 %K!, !,D" w t   A))TY&**) N*9$I*<     ly y1|C /KUM5+# +G@+ + и+иM=и@Tи W/(+>L+ +P0+01%#"54332463!2#!".57"3!26554&#%".546332!546332#%3326555)) , ,]]  +#+ ,   }**   h&؅ |C '1MWO7+)+IB+ +)ии #и0иO?иBVи Y/, +(+@N+R2+01#"54332".546332!546332#%3!2655".546332!546332#%3326555)),, ,V  +#+ ,  &4Vjh&l l \& wDNZcF++YR+F к09@иMиY\U/E+I+01".546332!546332#'.'''&67>7632332655#"54332+#+ ,L2c 7cSC?Rb8CpS0 0  A))w&K Lb ".;D$$E<0 % 324.#"32>".546332!546332#%3326555)).Oi/8/\+*N+CJ+*ии\RиNX017".5463323546332#3".5463323546332##"5433232##332655!332655'p% ,%p' ,))  @ f @ & & ~"*9  jp7McmwŻe++o!+3,+=B+e иo)иBQи=Wи=aиlи,vи=y?/T/d+9F+Y`+h+и*иdnиhr01".5463323546332#3".5463323546332#!2##"554&#!"&5546%#"5433232##3326553332655'f% ,%f' ,/) $ /))  6 6 /b& b& m#)$8"  j7McmwŻe+YR+o!+3,+L;+e кRY9/o)иLAиlи,vиLy>/]N+d+CJ+h+и*иdnиhr01".5463323546332#3".5463323546332##"5433232##!"&5543323!23326553332655'f% ,%f' ,)) /)   g 6 6 u& u& ~"#)$r  jq1Mi7+*+S+e^+"+и/?кB*9B/I[иmи"sи"}и^иBи"p/.+ %+\+u|++2+\@и2Nии01#!"&55463!26554&#!"&55463!2#!"3!2".5463323546332#3".5463323546332##"5433232##%332655!3326555  /#-+ #-o 'f% ,%f' ,))  6 z 6 #*K*9$K*= P&P&*"  jq/Ks5+G@++++ и /!к$9$/+=иZиZ/>и>/bиb/jиj/Zkиk/tиt/}и@и$и/ +_x+>++of++oи/>"и0и QиQ/ Tии/и/fии01%46332##".5".5463323546332#!".5463323546332###".55463326554&##"&5546332##"332677>76"3326554#"5433232##%3326553332655,,+%f' ,'f% ,6 %K!, !,D" w t F))  6 6 **P&P&> N*9$I*<     r"  j|#?[q{})++sE+WP+ +}1к494/;sMи_и eи oиPzи4и b/ +Nr++gn+$+N2и$@иrfиf/vиr|иgи/017463!2#!".57"3!26554&#%".5463323546332#3".5463323546332##"5433232##332655!332655 , ,]]  'f% ,%f' ,))  6 z 6 **   b& b& 6!  j|7S]s}!+U+u=+OH++U и)к,U9,/3uEи\иaиgиqиH|и,иd/X+T+Ft+ip++F*и8иxиt~01".546332!546332#".5463323546332#3".5463323546332#3!26557#"5433232##%332655!3326550,, ,'f% ,%f' ,  7))  6 z 6 jg&R&R&l l"  jw7`vɻx++!+ud+3,+x и)кLu9ujии,иug/w+ls+{+и*иwkиk/wи{иlи/01".5463323546332#3".5463323546332#'.'''&67>7632#"5433232##3326553332655'f% ,%f' , 2c 7cSC?Rb8CpS0 0 ))  6 6 x&x&P Lb ".;D$$E<0 % '4632".5463323546332#!".5463323546332##"5433232##332655!332655a?YM11NYA b[ 0 "- .#/ ZU o%f' ,'f% ,))  6 z 6 ![56Y U34T#$ 0e  @9/ :HR)   P' b& b& 2"  jp'C_uC-+ +wI+[T+tc+cA&6FVfv ]A]5к8 98/?wQиtiиT~и8иtf/#+Rv++kr+(+R6и(Dи`и`/vjиj/zиvиkи/01%#".54>324.#"32>".5463323546332#3".5463323546332##"5433232##332655!332655,.Oi+W^+v+++>"и0иLиL/и01#".54>32".5463323546332#3".5463323546332##"5433232##4.#"32>7!"&55463!2'#"&5546332332655!332655%=R--S>%%>R--R>%'f% ,%f' ,)) m*9 9**9 9* L  6 z 6 -/  // .eJ&J&="   lM  ^td7S]gU!+3,+_=+OH+++и иU)и_Eи,\иHfиi/ /X+*T++8и*FиT^иXb0134332##"5###"54332".5463323546332#3".5463323546332#3326553332655f&&f&&'O% ,%O' , "  lPG&&"  gpd7Sis}u!+3,+k=+OH++Y^+и^и/Yи^ и /u)иkEиHrи,|иY/[/Fj+Ub++x+jи/F*и8иxnиjtи}и}/01354332##"55###"54332".5463323546332#3".5463323546332#!2##"554&#!"&5546332655!332655f&&f&&'O% ,%O' , /)    FZ&Z&{#)$T  g7Sis}u!+3,+k=+OH+++и иu)иkEкX,39X/_Hrи,|и/cT+Fj++x+jи/F*и8иxnиjtи}и}/0134332##"5###"54332".5463323546332#3".5463323546332#!"&5543323!2332655!332655f&&f&&'O% ,%O' ,/)  1   {;V |&|&w$)$s  gqy1Mi7+IB+S+e^+z+ql+BI9/lи/и/q!и*?и[иzjиlwи^иBиqn/.+ %+\+kx++2+\@и2Nии01#!"&55463!26554&#!"&55463!2#!"3!2".5463323546332#3".5463323546332#7354332##"55###"54332332655!332655y /#-  #-O 9'O% ,%O' ,f&&f&&   #*F*4$F*8 J&J&+Ÿρ  g|d#?[wa+sl+yE+WP+49++&+ls9/+ и&и/4$и&1иyMиiиPиlи+(/ +Nx++%2+|@+x$и$/@\иNjиxи|и%и/01%463!2#!".57"3!26554&#354332##"55###"54332".5463323546332#!".5463323546332#7332655!332655 , ,=]  Kf&&f&&%O' ,'O% ,   x**   KZ&Z&ى  g|d7Soy=+OH+{!+3,+di+[V+HO9/q иVи/[и{)иEиdTиVaиVxиx/,иHи[X/t+p+*z+Ub+~+8и*Fиzи~01".546332!546332#".5463323546332#!".5463323546332#%354332##"55###"543323!2655332655!332655[,, ,%O' ,'O% ,f&&f&&    jh&P&P&(ķGf f  gw7S|û~!+3,+=+OH+++и и~)иEкhHO9,иHи/*}+++8и*Fи}и01354332##"5###"54332".5463323546332#3".5463323546332#'.'''&67>76323326553332655f&&f&&'O% ,%O' ,Y2c 7cSC?Rb8CpS0 0 c   $x&x&J Lb ".;D$$E<0 % '4632".5463323546332#!".5463323546332#%354332##"55###"543323326553332655?YM11NYA b[ 0 "- .#/ ZU #%O' ,'O% ,f&&f&&   ![56Y U34T#$ 0e  @9/ :HR)   P' Z&Z& ܉  gpj'C_{e+wp+}I+[T+B++94+9 и /p4и/B1и4?и}QиmиTиpи96/+R|+#+3@+D+|2и2/D`иRnи|ии3и/0174>32#".732>54.#"%#"54332354332##"55#%".5463323546332#!".5463323546332#7332655!332655.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#f&&f&&f%O' ,'O% ,   '+C/.C,,E..E)--..6Z&Z&ى  ]t7WakɻY++c!+3,+V;+Y иc)иVAи;QܸFиVKи`и,jиVm8/>/\+CJ+MT+X+и*иXbи\f017".5463323546332#3".5463323546332##"5433232##32##3326553332655'p% ,%p' ,))  } @ @ & & ~  9  jp7Wmwy++o!+3,+V;+y иo)иVAи;QܸFиVKиV\и;bи,vииV>/_/CJ+*n+Yf+MT+|+*ии|rиnx01".5463323546332#3".5463323546332##"5433232##32##!2##"554&#!"&5546332655!332655'f% ,%f' ,))  /) $ 6 z 6 /b& b& 5!x!h8#)$J  j|#?[{ E++})+;4+ +}1иMкP9P/W_и eиuܸjи oи yи4иPи b/ +gn+2|++qx+xи/$@и2Nи|иxи/017463!2#!".57"3!26554&#'".5463323546332#!".5463323546332##"5433232##32##332655!332655 , ,]]  %f' ,'f% ,))   6 z 6 **   b& b& 9!x!j  btC+GQ[I1+C<+S+++S и%и'иI9и<PиZи]/"/V+:H+&+:и,иVLиHR01%".546332354633234332##"5##!".5463323546332#3326553332655%p')) ,}'p% ,b @ @ 1& & ,  opC+G]gqͻi1+C<+_+++_ и%и'иi9иLиRиfи<pиs/O/^+IV+&+b+,и:и^hиbl01".5463323546332354332##"55##!".5463323546332#!2##"554&#!"&5546332655!332655%f')) ,'f% ,/) $ 6 z 6 /b& b& m#)$J  ob+G]gqûi1+DM+_+++_ и%и'иi9иD<fи<pиs/WH+^+&+b+,и:и^hиbl01".5463323546332354332##"5##!".5463323546332#!"&5543323!2332655!332655%f')) ,'f% ,L/)  w 6 z 6 Y& u& #)$r  o|R+GcmwoM+`3+e+++e и%и'и9и9/`@и@/_AиA/oUи`XlиXvиy/E.+d+7<+&+h+HиVиdnиhr01".5463323546332354332##"55###!"&55463!2#!"3!2".5463323546332#7332655!332655%f')) ,) , , 5  h'f% , 6 z 6 4b ~& V** !  b& ݍ  oqX+]yc+03+{+++{ и%и'и;и3CиC/Mи0UиU/0uиVиV/kи0nиnи/Z/+8Q+z+&+H?+~+^иlиzи~01".5463323546332354332##"55###!"&55463!26554&#!"&55463!2#!"3!2".5463323546332#7332655!332655%f')) ,/ /#-* #-o b'f% , 6 z 6 QP4s&,#*K*9$K*= P&Ԅ  o|C?OkuwU+h+m+,#+3.+3 иm!и.9и,;иhCиC/gDиD/.Kиw]иh`#tи`~и30/H +"l+@+-:+p+Pи"^иlvиpz01%463!2#!".5".5463323546332354332##"55##"3!26554&#%".5463323546332#7332655!332655 , ,]%f')) ,  'f% , 6 z 6 **qb ~&   b& ݍ  ow+Gpzr1+C<+|+++| ии%и'иr9к\19<yи/:q+&++:и,иuиq{01".5463323546332354332##"5##!".5463323546332#'.'''&67>76323326553332655%f')) ,'f% ,22c 7cSC?Rb8CpS0 0 t 6 6 x &x&P Lb ".;D$$E<0 % '4632".5463323546332#7332655!332655%f')) ,U?YM11NYA b[ 0 "- .#/ ZU 5'f% , 6 z 6 4b~& D![56Y U34T#$ 0e  @9/ :HR)   P' b& ݍ  opK?Soy{Y+l +q+,#+4@+q!и@-и-/@9и9/,;иlJиJ/{aиld#xиdи40/O+"p+-:+E+t+Tи"bиpzиt~01%#".54>32%".5463323546332354332##"55##4.#"32>".5463323546332#7332655!332655K.Oi32#".".5463323546332354332##"55##32>54.#"'".5463323546332###"543323326553332655.SqD>pV22Vp>DqS.%Q'L&&L ,#?V3-VB))BV-3V?#'Q% ,z&&C ! ! '+C/.C,,E..E?Z%v&--..Z&?  btC+GQ[_S1+C<+I+_++I и_и%и_'иS9иPи<Zи]иa/"/L+^+]&+^и/,и^:и:/;иHRиLV01%".5463323546332354332##"5##!".5463323546332#332655!33265535#%p')) ,}'p% , @ f @ jC& & ,  hopC+G]gqui1+C<+_+u++_ иuи%иu'иi9иLиRиfи<pиsиw/O/t+IV+s&+b+tи/,иt:и:/;и^hиbl01".5463323546332354332##"55##!".5463323546332#!2##"554&#!"&5546332655!33265535#%f')) ,'f% ,/) $ 6 z 6 `/b[ & b& m#)$J  7632332655!33265535#%f')) ,'f% ,22c 7cSC?Rb8CpS0 0 l 6 z 6 `xo&x&P Lb ".;D$$E<0 % '4632".5463323546332#7332655!33265535#%f')) ,^?YM11NYA b[ 0 "- .#/ ZU ,'f% , 6 z 6 `4be & D![56Y U34T#$ 0e  @9/ :HR)   P' b& ݍ  32%".5463323546332354332##"55##4.#"32>".5463323546332#'332655333265535#K.OiYEXt/t>Y*f+Q^+t*ии7иHиH/IиI/и/I<ܹMDиfp01".5463323546332#3".5463323546332##!"&55463!54332!2!2##"554&#!"&5546332655!332655%% ,%%,  ) /)   ~ ~ pn&om&y""\\#)$d dd d<7Oeoyg++3,+g иL!и!/Lq)иEиE/!KиK/Tg9T/[3bиb/nи,xи3{ //%///_P+f+I<+Iи/Iи/*и7и<MDиjfpиjt01".5463323546332#3".5463323546332##!"&55463!54332!2!"&5543323!23326553332655%% ,%%,  ) {/)  ~ ~  us&tr&""ss')$i ii i )\Z&[Y&k""NNP PP P<| /K[s}E++$+ W+9/!и+p5и5/pu=к@W 9@/GO+iиi/5oиo/@|и$иG/'/9/C/EXx/x>YEX/>YT +>t+L+>"и0иKиlиl/mиm//и//m`ܹqhиt~017463!2#!".5".5463323546332#3".5463323546332#"3!26554#!"&55463!54332!2332655!332655 , ,>-%% ,%%,5    ) ~ ~ _**pn&om& ~ ~ ""[['d dd dYEX/>Y]+$z+S+~2иMиrиr/sиs/1и1/$@иsfܹwnиz01%#".54>32".5463323546332#3".5463323546332#4.#"32>#!"&55463!54332!23326553332655*@^m-EE@1@^m,-m_?%% ,%%, 6LQQK55KQRK6  ) ; ~ ~ 3@$  %2"2?$ $@_pn&om&n!''! ((:""[['d dd dItC7Clv߻n!+3,+x++B;+x иn)к_,39_/h,uииB>/8/cS+*m+{+*ииSNиN/ShZиZ/h_и_/{qиmw01".5463323546332#!".5463323546332##"543326"'"&55463267546332>3326553332655%|%,u%|% ,))!NST(QUP >FG +"MKG L L %K&K&O~ #  "  m  <7[eoѻg+SJ++]!+9T+3,+g и])и,dиnи3q //%///YA+*\+j+*ииA:IиYNи:Sиj`и\f01".5463323546332#3".5463323546332#32#!"&5546335463323546332332655!332655%% ,%%,G  ++c   #l&k&9""    32".5463323546332#3".5463323546332#4.#"32>32#!"&5546335463323546332'332655!332655*@^m-EE@1@^m,-m_?%% ,%%, 6LQQK55KQRK66  **^ ~ ~ 3@$  %2"2?$ $@rig&hf&[!''! ((\""\ \\ X XX X&=& 9"" t tt tSи/oиVиrиyO/Y/k/u/B+ 9+T~+0+F+%+0и-иFbиTpи~и01#!"&55463!26554&#!"&5546335!"&55463!2#!32#!"3!2".55463323546332#3".55463323546332#%33265533326555 /#-  { R x#-Q %% ,%%, ~ ~ z#*;*+#f""f;*. )\Z&[Y&P PP P<| '7Soyq=+OH+3+=q9/Oи/OYиY/и/+qEи{aкd39d/kHxиdиkA/K/]/g/0!+Fp+(+t8+ +ии8TиFbиpzиt~01746335!"&55463!2#!32#!".57"3!26554&#".5463323546332#3".5463323546332#%3326553332655 ,{ R x ,>]  0%% ,%%, ~ ~ `*u""u*   Ghf&ge&\ \\ \h+l0+$+,и0Lи>Zиhrиlv01%'&&'''&67>75!"&55463!2#!".5463323546332#3".5463323546332#%3326553332655)-p t+(q?lR3{ R x%% ,%%, ~ ~ sDb " tGJw  % 4CK&g""n$lj& ki&` `` `75!"&55463!2#!".5463323546332#3".5463323546332#4.#"32>3326553332655*@_m,EE@26Ra,{ R x,bR5%% ,%%, 6MQQK66KQQM6 ~ ~ 3?#  $2"-<$u""u$<xhf&ge&Z!&&! ''s\ \\ \&=&t tt t'25!66332655!332655*@_m,EE@2@0 R 1B%% ,%%, 6MQQK66KQQM6A""Cx ~ ~ 3?#  $2"2>""?rhf&ge&Z!&&! ''\ \\ \<+5Q[-+' +S;+MF+-и 4иSCиFZиM]/#/?/I/ +,+0+6иDи,Rи0V01%#!"&55463!2%".5463323546332#'332655".5463323546332#'332655  R :%% ,  %%,  A""z& y& <%AKgq˻C++=6+iQ+c\++C9/ cи/C3и6JиiYи\pиcs//9/U/_/+4B+#+F&+&Lи4ZиBhиFl01!"&5543323!2#!"&55463!2%".5463323546332#'332655".5463323546332#'3326551/)  v  R G%% , ~ %%, ~ z')$""q us&i itr&i i+P^+5+bB+,#+ +BhиPvи^иb01#!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!2".55463323546332#'332655".55463323546332#'332655  R x /#-  #-Q %% , ~ %%, ~ t"" #*F*6#F*9 )\Z&P P[Y&P P<| #3OYuQ9+KD+w_+/+9Q9/'QAиDXиwgкj/9j/qj~иq=/G/c/m/EXT/T>YEXz/z>Y,+BP+$+ +T4ZиBhиZuиPv01#!"&55463!2463!2#!".57"3!26554&#".5463323546332#'332655".5463323546332#'332655  R  , ,>]  0%% , ~ %%, ~ 4""** ~ ~ 8pn&d dom&d d+ +B"+"Hи0Vи>dиBh01%#"54332!2##"554&#!"&5546'".5463323546332#'332655".5463323546332#'3326555))/) $ L'p% ,b @ %p' ,d @  :#)$mb& ݍ b& ݍ oa !=Gcm?'++eM+_X+ +?/к292/92FиeUиXlи o/ +0>+B"+"Hи0Vи>dиBh01%#"54332!"&5543323!2".5463323546332#'332655".5463323546332#'3326555))/)  c'p% ,b @ %p' ,d @ t~#)$u&  u&  oqX =Yc[C+6+i+{t+ +и#и#/ -и[KкN69N/UNbиqиtи /:+1+LZ+(+^>+>dиLrиZи^01#"54332#!"&55463!26554&#!"&55463!2#!"3!2".5463323546332#'332655".5463323546332#'3326555)) /#-* #-o b'p% ,b @ %p' ,d @ )7T#*K*9$K*= P&Ԅ P&Ԅ o|C /KUq{ͻM5+# +s[+mf+ + и+иM=к@ #9@/G@Tиscиfzи }/(+>L+ +P0+0Vи>dиLrиPv01%#"54332463!2#!".57"3!26554&#%".5463323546332#'332655".5463323546332#'3326555)) , ,]]  'p% ,b @ %p' ,d @  **   b& ݍ b& ݍ o|C '1MWs}ݻO7+)+u]+oh+ +)ии #и0иO?кB)9B/IBVиueиh|и /, +(+@N+R2+2Xи@fиNtиRx01#"54332".546332!546332#%3!2655".5463323546332#'332655".5463323546332#'3326555)),, ,V  'p% ,b @ %p' ,d @ &4Vjh&l lR&Յ R&Յ ow7`lvx++n!+3,+kd+x иn)кLk9,uииkg/*m+{+*ии{qиmw01".5463323546332#3".5463323546332#'.'''&67>7632#"54332332655!332655'p% ,%p' ,2c 7cSC?Rb8CpS0 0 )) @ f @ x&x&P Lb ".;D$$E<0 % 324.#"32>".5463323546332#'332655".5463323546332#'3326555)).Oi'/-/29+92901'.'''&67>54632#"5433232## *9G( ;70 hH =Z9/ )) By8(YXS!  CJN$[B :h~"*Pp$:PɸQ///Q и /A&6FVfv ]A] 9/&и&//*/>и*Dи*Nи*R,/A/&3+FM+MF901'.'''&6766'432!2##"554&#!"&5546%#"5433232## +:E& ;<6!pK wx / /) $ /)) I?*LB5 ! )8C$L|1! Iމ#)$9"Pc#9Yo+_d+E?+A&6FVfv ]A]9A??]A?)?9?I?Y?i?y??????? ]'?E9'/8-кT?E98q*/[h+/6+6/901'.'''&6766'432#"5433232##&766'462'&&''32##"554&##"&5546 *9F( !@8.@ uz / ))  RO. UA+3:+01'.'''&67>'432#"5433232##!"&5543323!2  -=H& ?=4"oK 6Y?! / )) /)  'J#)QI; !4?G!Q1! !bvF~"#)$P|@V+<+UD+U и /A&6FVfv ]A]/<9UJиUXG/+ +LS+/SL901#!"&55463!2#!"3!2'.'''&6766'432#"5433232##6 , , 5  V +:E& ;<6!pK wx / )) v** !  ?I?*LB5 ! )8C$L|1! Iމ"Qq1Xnӻ*+2S+"+и/A2&262F2V2f2v2222222 ]A22]ES29\и"bи"lи"p_/.+ %+dk++Ekd901#!"&55463!26554&#!"&55463!2#!"3!2'.'''&7>54632#"5433232##5  /#-+ #-o V .;E$  @;1 gK  >X7/ )) #*K*9$K*= OF54632#"5433232##32##"54&##"&5546  'N!, !, '% 2w .;E$  @;1 gK  >X7/ )) /$  qN*9$I*< @F76'.'''&7>54632"3326554#"5433232## ,,6 %K!, !,D" ^ .;E$  @;1 gK  >X7/  t >)) **) N*9$I*<  @F+8++> A&6FVfv ]A],89EиJиPиZи^M/A+=+RY+,YR901".546332!546332#'&&'''&6764323!26557#"5433232##0,, ,sP Dp nQ  / b  7)) jh&p!=N' !%p9Gw5!  Bl l"Pw#Lbc/P/cи/A&6FVfv ]A]9Pa8a9VиadS/X_+_X901'.'''&6766'432'.'''&67>7632#"5433232##uN >9/!pK wx / 2c 7cSC?Rb8CpS0 0 )) %F R' !/8>L|1! Iމ Lb ".;D$$E<0 % '4632'.'''&6766'432#"5433232##g?YM11NYA b[ 0 "- .#/ ZU  +:E& ;<6!pK wx / )) ![56Y U34T#$ 0e  @9/ :HR)   P' vI?*LB5 ! )8C$L|1! Iމ"Pp'Lb +(H+aP+P A&6FVfv ]A]A(&(6(F(V(f(v((((((( ]A((];H(9aVиadS/+#+X_+;_X9MиM/0174>32#".732>54.#"'.'''&6766'432#"5433232##*LjA54632#"5433232##"  , , 5 k .;E$  @;1 gK  >X7/ )) P **  O ! F54632&&&& .=$ /*# XB  5K0, lPG'Br3)WYU% >CDS? 2psLpd1Uǻ2P++!&+ и& и /!и&и/A2&262F2V2f2v2222222 ]A22]BP29!W/#/*+ +B 901%#"54332354332##"55#!2##"554&#!"&5546'&&'"''&7>7&632&&&&k /)  b (3<  7b W=  3J2/ C#)$ LE#D<1 ""j9=m2 (Xcn> I=SyIB+++и к8BI98/*BI9U/M>++*90134332##"5###"54332'&&'"''&7>'&632!"&5543323!2&&&&fE :a [;  1N5/ /)  {4ZPJK* &n>Bt1 'alu= $)$Kqy1Mq*+BG+94+4и/и/9!иB2и4?кl*9l/N^*99s6/.+ %+3@++^@3901#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332!'&&'"''&7>5&632y /#-  #-O &&&& e@A[Y8  .K6/ #*F*4$F*8 )!C!Fs   '_77&632 , ,=]  d&&&& (3<  7b W=  3J2/ x**   LE#D<1 ""j9=m2 (Xcn> K|d7Ab9+,1+#+9 ии/#и,и)и@и@/]99]/BR99#d /<+8+*+R*901".546332!546332#354332##"55###"543323!2655'.'"''&766'&632[,, ,u&&&&  LcB 50);v  bk/ jh&(Gf fIBDw$ +38uc Nm IwDeE`+++и к0`9AE&E6EFEVEfEvEEEEEEE ]AEE]T`E9g/+T901354332##"5###"54332'.'''&67>7632'&&'"''&766'&632&&&&X2c 7cSC?Rb8CpS0 0  eH @]\<  dk/  Lb ".;D$$E<0 % '4632354332##"55###"54332'&&'"''&7>7&632?YM11NYA b[ 0 "- .#/ ZU &&&& (3<  7b W=  3J2/ ![56Y U34T#$ 0e  @9/ :HR)   P'  LE#D<1 ""j9=m2 (Xcn> Lpj'Cg+B++94+9 и /A&6FVfv ]A]4и/B1и4?кb9b/DT99i6/+#+3@+T@390174>32#".732>54.#"%#"54332354332##"55#'&&'"''&7>7&632.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#M&&&& (3<  7b W=  3J2/ '+C/.C,,E..E)--..LE#D<1 ""j9=m2 (Xcn> LtF A++ иܸиA & 6 F V f v ]A ]3A 9H// ++3901#"5433232##32##'.'''&67>54632))  c *9G( ;70 hH =Z9/ ~  YBy8(YXS!  CJN$[B :hPp$DZ˻ +C(+A&6FVfv ]A] 9C.и(>ܸ3иC8иCIи(OиC\+/L/07+FS+:A+L+901'.'''&6766'432#"5433232##32##!2##"554&#!"&5546 +:E& ;<6!pK wx / ))  /) $ I?*LB5 ! )8C$L|1! Iމ!x!m3#)$KG]SL+ C++ иܸиA & 6 F V f v ]A ]4C 9_/WH+ ++01%#"5433232##32##'.'''&67>'432!"&5543323!2))  h  -=H& ?=4"oK 6Y?! / /)  t~!!l'J#)QI; !4?G!Q1! !bvF#)$Qq1Xx*+2S+w\+\и/и/w!и!/A2&262F2V2f2v2222222 ]A22]ES29wbи\rܸgиwlиwz_/.+ %+dk++nu+01#!"&55463!26554&#!"&55463!2#!"3!2'.'''&7>54632#"5433232##32##6  /#-+ #-o U .;E$  @;1 gK  >X7/ ))  #*K*9$K*= OF+8++> A&6FVfv ]A],89EиJиFиPи`ܸUиZиdиhM/A+=+RY+\c+01".546332!546332#'&&'''&6764323!26557#"5433232##32##0,, ,sP Dp nQ  / b  7))  jh&p!=N' !%p9Gw5!  Bl l!o!WPw#Ll+kP+A&6FVfv ]A]9Pfܺ8f9kVиf[иk`иknS/X_+bi+01'.'''&6766'432'.'''&67>7632#"5433232##32##uN >9/!pK wx / 2c 7cSC?Rb8CpS0 0 ))  %F R' !/8>L|1! Iމ Lb ".;D$$E<0 % 32#".732>54.#"'.'''&6766'432#"5433232##32##*LjA5463235#&&&& .=$ /*# XB  5K0, GWG'Br3)WYU% >CDS? 2ps>I=SWIB+++и к8BI98/*BI9TиUиY/M>+V+U+01354332##"55###"54332'&&'"''&7>'&632!"&5543323!235#&&&&fE :a [;  1N5/ /)  ;wȪZPJK* &n>Bt1 'alu= $)$Kqy1Mquػ*+BG+94+4и/и/9!иB2и4?кl*9l/N^*9Brи4sи9w6/EXr/r>Y.+ %+3t++r@^r6901#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332!'&&'"''&7>5&63235#y /#-  #-O &&&& e@A[Y8  .K6/ 8#*F*4$F*8 ۅ*vi!C!Fs   '_77&63235# , ,=&&&&  d (3<  7b W=  3J2/ 8x**r   \LE#D<1 ""j9=m2 (Xcn> rLpj/Cgk0+.+&:+& и /.и:и/:+и+/A0&060F0V0f0v0000000 ]A00]b09b/DT09.hи:iиi/&m"/5+j+?+i,+(и(/0174>32#".#"54332354332##"55#32>54.#"'&&'"''&7>7&63235#.SqD>pV22Vp>DqS.&&&&#?V3-VB))BV-3V?## (3<  7b W=  3J2/ 8'+C/.C,,E..E m--..LE#D<1 ""j9=m2 (Xcn> rRtC:;// и;5и5/A&6FVfv ]A]'59<// +'901#"5#"554334332'.'''&67>546325) )F *9G( ;70 hH =Z9/  $ 8YBy8(YXS!  CJN$[B :hWpC$:NɸO///O и /A&6FVfv ]A] 9/&и&//*/;и*Aи/Gи*P>/,/;H+&3+,>901'.'''&6766'432!2##"554&#!"&554654332##"55#"5543 +:E& ;<6!pK wx / /) $ )) I?*LB5 ! )8C$L|1! Iމ#)$x $ WpC"6Lb׻+<A+*X+A&6FVfv ]A]9X#и#/X/и//*QиQ/*d>/T/&/#0+8E+>&98MиE[01'.'''&6766'43254332##"55#"554332##"554&##"&5546!32##"554&##"&5546sS "A7-B o / k)) /$  B/$  $D O( !07;d! D؏ $ #)$#)$Xh"6Wm +]b+LF+A&6FVfv ]A]9L#и#/L)L/и//$0AFF]AF)F9FIFYFiFyFFFFFFF ]:FL9Lo&/#0+Yf+01'.'''&6766'43254332##"55#"5543&&'''&766'46232##"554&##"&5546sS "A7-B o / k)) H=NI6  RO. UAb/$  $D O( !07;d! D؏ $ ;&N00S* :e  *?i$ P#)$Ua';QgG@+.)+#@G9#/#9)4и.S+/K<+(5+5(901'.'''&67>'43254332##"5#"5543!"&5543323!2  -=H& ?=4"oK 6Y?! / l)) " /)  'J#)QI; !4?G!Q1! !bvFa $ g#)$W|S@T{+GB+G и //9<иBMиGVD/+ +AN+/NA901#!"&55463!2#!"3!2'.'''&6766'43254332##"55#"5543S , , 5  @ +:E& ;<6!pK wx / k)) v** !  ?I?*LB5 ! )8C$L|1! Iމ $ [qX1Xl*+"+и/*2E*29*SиS/\иdи"jи"ng/.+ %+d]++E]d901#!"&55463!26554&#!"&55463!2#!"3!2'.'''&7>54632#"55#"5543354332X  /#-+ #-o =2<@  ?:3 gJ  >X7/ ) )#*K*9$K*= OJ<#D9,  " *4:76'.'''&7>54632#"55#"5543354332"3326554&##,,6 %K!, !,D" m .;E$  @;1 gK  >X7/ ) ) t **) N*9$I*<  @F54632#"55#"5543354332332655o'' ,  $J!,~ !,?$  r .;E$  @;1 gK  >X7/ ) ) o 1tr&& L*7$G*:  @F++> и>8и8/и/>,>9EиGиMиSи\J/A+=+GT+,TG901".546332!546332#'&&'''&6764323!265554332##"55#"5543R,, ,sP Dp nQ  / E  )) jh&p!=N' !%p9Gw5!  Bl l/ $ Uw#L`a/N/aи/A&6FVfv ]A]9NS8S9NYиSbP/MZ+ZM901'.'''&6766'432'.'''&67>763254332##"5#"5543uN >9/!pK wx / 2c 7cSC?Rb8CpS0 0 )) %F R' !/8>L|1! Iމ Lb ".;D$$E<0 % '4632'.'''&6766'43254332##"55#"5543?YM11NYA b[ 0 "- .#/ ZU  +:E& ;<6!pK wx / k)) ![56Y U34T#$ 0e  @9/ :HR)   P' vI?*LB5 ! )8C$L|1! Iމ $ WpK'L`ѻ +SN+Nи/A&6FVfv ]A](;(9HиH/NYиSbP/#+MZ++;ZM9VиV/01%#".54>324.#"32>'.'''&6766'43254332##"5#"5543K.Oi546324332##"5#"5543##"54332\ .=$ /*# XB  5K0, && &&Br3)WYU% >CDS? 2ps9 $ ~Lpd9MY4+@;+NS+NиS и /A&6FVfv ]A]&49;FиN[/V/+:G+&G:901%!2##"554&#!"&5546'&&'"''&7>7&63254332##"55#"5543##"54332 /)  b (3<  7b W=  3J2/ && &&#)$ LE#D<1 ""j9=m2 (Xcn>  $ I!7KWq-&+>9+LQ+&-9/&-99DиLYT/1"+8E+E8901'&&'"''&7>'&632!"&5543323!254332##"5#"5543##"54332kfE :a [;  1N5/ /)  && &&PJK* &n>Bt1 'alu= $)$G $ ^Kqy1Uiu*+\W+jo+oи/и/j!кP*9P/2B*9Wbиjwr/.+ %+Vc++BcV901#!"&55463!26554&#!"&55463!2#!"3!2'&&'"''&7>5&63254332##"55#"5543##"54332y /#-  #-O  e@A[Y8  .K6/ && &&#*F*4$F*8 O!C!Fs   '_77&63254332##"55#"5543##"54332 , ,:]  d (3<  7b W=  3J2/ && &&x**   \LE#D<1 ""j9=m2 (Xcn> $ $ K|d%FZf+MH+[`+ и`и/[и`$и$/A9A/&69HSи[hc/ ++GT+6TG901".546332!546332#%3!2655'.'"''&766'&63254332##"55#"5543##"54332[,, ,=  LcB 50);v  bk/ && &&jh&f fIBDw$ +38uc Nm F $ Iw(I]i)D+PK+^c+D^9A)&)6)F)V)f)v))))))) ]A))]8D)9KVи^kf/JW+8WJ901%'.'''&67>7632'&&'"''&766'&63254332##"55#"5543##"54332H2c 7cSC?Rb8CpS0 0  eH @]\<  dk/ && && Lb ".;D$$E<0 % '4632'&&'"''&7>7&63254332##"55#"5543##"54332?YM11NYA b[ 0 "- .#/ ZU  (3<  7b W=  3J2/ && &&![56Y U34T#$ 0e  @9/ :HR)   P' kLE#D<1 ""j9=m2 (Xcn> + $ Lpj'K_kٻ+RM+`e+` и /A&6FVfv ]A]eи/F9F/(89MXи`mh/+LY+#+8YL90174>32#".732>54.#"'&&'"''&7>7&63254332##"55#"5543##"54332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?## (3<  7b W=  3J2/ && &&'+C/.C,,E..E)--..LE#D<1 ""j9=m2 (Xcn> ! $  RtC@A/ /и  иA;и;/A&6FVfv ]A].;9B/ /++. 9015#"5543354332##"5#"5543'.'''&67>54632 ))  *9E& :60@ =Z9/ ] #  # pv(VUO   AIL$~ :hXpC>T׸U// ииU:и:/A&6FVfv ]A]-:9@и@/CиIиV/F/+@M+ +-F90154332##"55#"554335#"5543''.'''&6766'432!2##"554&#!"&5546)) tG 8:5"jF wx / /) $ ~ # o # g\LMz# ! (5?#Eq.! Iމ#)$UaAWoMF++ ик=FM9=//=9Y/QB++ +0154332##"55#"554335#"5543''.'''&67>'432!"&5543323!2))  *6?" 75/F 6Y?! /  /)  c # v # }`Q&KD8  0иFи*Na*N9*oиo/"v5/.+ %+2G+F?++01#!"&55463!26554&#!"&55463!2#!"3!254332##"55#"554335#"5543''.'''&7>54632X  /#-+ #-o Z)) .9A! >81 eF  >X7/ #*K*9$K*= f6 # [ # USD=5+ " )179a- 'S[f:W|C#?d+ +$и *и0и8и@S@9`и f'/ +$9++81+01%463!2#!".57"3!26554&#54332##"55#"554335#"5543''.'''&6766'432 , ,]]  ))  +9B$ 9;5"jG wx / **    # o # lZK'H=2 ! (6A#Fr.! IމV|C7Yc[++[ и[UиU/и/и"и(и0и[8J[89bиe/^+Z+1+0)+01".546332!546332#54332##"55#"554335#"5543''.'''&6764323!2655R,, ,))  pK !>6,A  / E  jh& x/| # g # d*L#H{% !.58a!  Bl lUw?hi// ииi;и;/A&6FVfv ]A].;9T;9j/+ +0154332##"55#"554335#"5543''.'''&6766'432'.'''&67>7632))  oK =8/"jG wx / 2c 7cSC?Rb8CpS0 0 r # o # n-R&Mz% !-6<Gr.! Iމ Lb ".;D$$E<0 % Jи4QиQ/>RкlyY94@/SP+J/+/и/lPS901&&''&&'''&6766'4327>'463254332##"55#"554335#"5543''.'''&6766'432?YM11NYA b[ 0 "- .#/ ZU ))  ,8A# 895#iE wx / ![56Y U34T#$ 0e  @9/ :HR)   P'  ~ # o # l_N&E;0 ! (4?#Eo-! IމWpK'Chٻ +.)+)и/A&6FVfv ]A])4и)<иDWD9dиd/.j+/#+(=++<5+1и1/01%#".54>324.#"32>54332##"55#"554335#"5543''.'''&6766'432K.Oi54632##"54332q&&  .=$ /*# XB  5K0, &&"p # # Br3)WYU% >CDS? 2ps~I>T`JC++UZ+ иик9CJ99/+CJ9Ub]/N?++ +0154332##"55#"554335#"5543''&&'"''&7>'&632!"&5543323!2'##"54332q&& ~ cA 7^ X9  1N5/ /)  &&}r # s # W-U)Fx' $i:?m0 'alu= $)$Kqy1Tp|*+\e+qv+vи/и/q!кO*9O/2A*9\VaиViиemиq~y/.+ %+Uj++ib+01#!"&55463!26554&#!"&55463!2#!"3!2'&&'"''&7>5&63254332##"55#"554335#"5543##"54332y /#-  #-O ^:32#".732>54.#"'&&'"''&7>7&63254332##"55#"554335#"5543##"54332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?## (19  5_ 9q  3J2/ && {&&'+C/.C,,E..E)--..XN!@7. " e7q\ (Xcn> oq% # j # y <?E+и//9;и;/=/ +01!2#!"&55463!546332'&&'''&6766763  +ETa3 p5/m - ;""  0SD3 "(^^) # 6 YEX4/4>YBO+ܸи 01#!"&55463!54332!2'.'''&67>76!2##"554&#!"&5546  ) k>Qa5 5bTE,nCpS2 - /)  8"# >5)  " ,8B#Hv  $ ;IR)#)$YEX4/4>YBO+ܸи иBWиOe01#!"&55463!54332!2'.'''&67>7632##"554&##"&5546!32##"554&##"&5546  ) k>Qa5 5bTE,nCpS2 - /$  L/$  :"# >5)  " ,8B#Hv  $ ;IR)#)$#)$<@VW/ /и/и/. 9 <и76!"&5543323!2  ) k>Qa5 5bTE,nCpS2 - /)   "# >5)  " ,8B#Hv  $ ;IR)')$01#!"&55463!26554&#!"&55463!2#!"3!2#!"&55463!54332!2'&&'''&67>7624 /#-  #-Q x  ) k'o s,*qHrQ. - z#*E*+#;*8 "# 762"3326554&#,,  'N!, !,D&    ) k'o s,*qHrQ. - L z **) N*9$I*<  "#~~ YEXX/X>Y ++4(ܸ)и(90017463!2#!".57"3!26554#!"&55463!54332!2'.'''&67>76 , ,>]    ) k>Qa5 5bTE,nCpS2 - _** ~ ~ "# >5)  " ,8B#Hv  $ ;IR)7623!2655,, ,  ) k BOY- 3cVD(}IoN/ -  ba&"#   !;/"  " +9B!Eh* $ 4>M0 '\ \76'&&'''&67>7632  ) k>Qa5 5bTE,nCpS2 -  -p t+*nCqR1 0 '"" >5)  " ,8B#Hv  $ ;IR) DZ " mEGr % 9HP' 7+%+A&6FVfv ]A]A%%]A%)%9%I%Y%i%y%%%%%%% ]>BиB/X7>97fиf/lEX:/:>YEX^/^>Y + +:.ܸ/и.?601".54>32"32>54.%#!"&55463!54332!2'.'''&67>76EE@1@^m,-m_?@^m-QK55KQRK66LQ  ) k>Qa5 5bTE,nCpS2 -  %2"2?$ $@23@$ '! (( !'"" >5)  " ,8B#Hv  $ ;IR))) svQ #A9/=  mt.  "NPP$LRN 743/)   A} +K))  hL@o&eL ;S5 . #)$"  # h"*$8Q #S5.X-" !@BG' I+Lk!+Nd++ иN,и,/=dN9dGиG/m/%+d[+ +dNиN/ hиh/01%#"5433232##!"&5543323!2'.'''&766'46326676##"&55463!546332)) /)  emH;72> ms. E8  Qd +_"$)$c*=d # #-5kW ! =S" # Iq1Of*+L+eT+Tи/и/e!и!/DиD/eZкwL9eW//.+ %++\c+D;+\HиH/DMиM/01#!"&55463!26554&#!"&55463!2#!"3!26##"&55463!54633266#"&5433232##'&&'''&7667436 /#- #-[ A} +K)) lN Ek"gL jm . "*>*."<*1 "  #| {k "7X #O35V#%+S Iq'=\}߻ +>y+<+++A&6FVfv ]A]<1иyUиU/>]и]/ly>9<./#++3:+YL+LU?и?/01%#".54>324.#"32>7#"5433232##%6676##"&55463!546332'&&'''&7>743-3Tl9>mQ//Qm>9lT3G&@S-2S<"":8 .d0)4e5{\$)$Z$#@a "W73Z*" :P D # "Hqy1Mk*+BG+94+4и/и/9!иB2и4?к*9/NиN/]*9gиg/96/.+ %+3@++y+yDиyvиv/yи/и/@и/01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332%'&&'''&7667436#'"&55463275433266y /#-  #-O &&&&]G?a5 i_ . 9=>:8 .d0)4e#*?*-$?*1 κ &6O #G-VK" 6yO m # "xwH|d#?^׻+49++&++ и&и/4$и&1кv9v/}@и@/O9vZиZ/+(/ ++%2+2yиy/lܸiиi/l}sиs/}vиv/01%463!2#!".57"3!26554&#354332##"55###"54332'&&'''&766'436#'"&55463275433266 , ,=]  d&&&&_@9b ]I m^ . 9=>:8 .d0)4el**   $!:W #O0-R," =N ] # "~Hv>]~|u+++и к-u9|?и?/Nu|9uYиY//+xk+khиh/k|rиr/|uиu/0134332##"5###"54332'&&'''&7>7432'&&'''&7667436#'"&55463275433266&&&&j &o v,*pHsQ+ 0 ^C8d ^F j_ . 9=>:8 .d0)4e   :_"  iABk" %9DJ% !%!?\ #T62X*" '432766'4632354332##"55###"54332'&&'''&766'436#'"&55463275433266?WL7o+XB 6I, 0 D; ?B/ VP &&&&_@9b ]I m^ . 9=>:8 .d0)4eP/-J" >V,K$$ 8@K.   3d){S  !Bf"  Һ$!:W #O0-R," =N ] # "~ItC 4WX// X'и'/08и8/G'090VиV/ Y//0+и/0"и"/0'и'/G901#"543326"'"&55463267546332>'.'''&766'46325))!NST(QUP >FG +"MKGvQ #A9/=  mt. ~ #  "  51@t" "*27v_ EbMb!?`o +=5+ +[5=9[/@Q5=9 b/+8,+,5=и=/01!"&5543323!2'#"54332'6##"&55463!5433266'.'''&766'4632S/)  #))  Xg ' MmH;72> ms. z$)$]" # *=d # #-5kW ! =SMqY1Oo{*+Ik+zs+sи/и/z!и!/`kI9z}m/v/.+ %++E9+9BJиJ/01#!"&55463!26554&#!"&55463!2#!"3!2##"&55463!543326676'&&'''&766743#"54332Y /#- #-[  Xg ' M9 lN Ek"gL jm . ))"*>*."<*1 " #|{ 7X #O35V#%+S M|D#Cdp+@`+oh+o и /hи/S`@9ork/ ++<.+.79и9/7AиA/017463!2#!".57"3!26554&#'6##"&554633275433266'&&'''&7>743#"54332 , ,I]  0  Xg ' P hL@o&eL ;S5 . ))d**   # # *$8Q #S5.X-" !@BG'  M|C%Camֻ+>6++ и$кR6>9eиkиo_/h/EX9/9>YEXL/L>YEXO/O>Y ++9-ܹ6>и>/RO_9-bиb/cиc/01".546332!546332#%3!2655##"&55463!543326676'&&'''&766743#"54332;,, ,>  8 Xg ' M9  @Fm#dK qi . ))`c&W W+" # )$r1 #Q0-V-" ?L Mv"?`lm/d/m\и\/5и5/dk5k9\<@и@/<BиB/O\<9<_и_/kng/8,+,5=и=/01%'&&'''&7>743276##"&55463!5433266'&&'''&7>743#"543329 &o v,*pHsQ+ 0 |  Xg ' L hL@o&eL ;S5 . ))  :_"  iABk" %9DJ% v#  # *$8Q #S5.X-" !@BG' V<EOF//F и /5 9C/+ и01%#!"&5546335433235433232'&&'''&6766763  )) kETa3 p5/m - A"" 0SD3 "(^^) # 6 76!2##"554&#!"&5546  )) k>Qa5 5bTE,nCpS2 - /)  ."# >5)  " ,8B#Hv  $ ;IR)#)$<H^[TM+ ++6M9XI++ ии01#!"&55463354332354332!2'.'''&67>76!"&5543323!2  )) k>Qa5 5bTE,nCpS2 - /)   "# >5)  " ,8B#Hv  $ ;IR)')$иKBиOF01#!"&55463!26554&#!"&55463!2#!"3!2#!"&55463354332354332!2'&&'''&67>7624 /#-  #-Q x  )) k'o s,*qHrQ. - z#*E*+#;*8 "#pppp 76 , ,>]    )) k>Qa5 5bTE,nCpS2 - _** ~ ~ "# >5)  " ,8B#Hv  $ ;IR)YEX4/4>Yk+g+4 ܸ!и 9(и9001".546332!546332##!"&55463354332354332!2'.'''&67>7623!2655,, ,  )) k BOY- 3cVD(}IoN/ -  ba&"#   !;/"  " +9B!Eh* $ 4>M0 '\ \76'&&'''&67>7632  )) k>Qa5 5bTE,nCpS2 -  -p t+*nCqR1 0 #"" >5)  " ,8B#Hv  $ ;IR) DZ " mEGr % 9HP' 7+F?+%+A&6FVfv ]A]A%%]A%)%9%I%Y%i%y%%%%%%% ]`9t + +C.+.G6иC:иG>01".54>32"32>54.%#!"&55463354332354332!2'.'''&67>76EE@1@^m,-m_?@^m-QK55KQRK66LQ  )) k>Qa5 5bTE,nCpS2 -  %2"2?$ $@23@$ '! (( !'"" >5)  " ,8B#Hv  $ ;IR)763| R wBVd5 5hZI+yAvZ7 - F"" ~   !C;.  "1@J&N}0 # IX`/ 762 { R x/)  W BOY- 3cVD(}IoN/ -""#)$R   !;/"  " +9B!Eh* $ 4>M0 <BX}Y//и/и/09>и>/@и@/YGиG/NDиD/RC+ +01%"&55!"&55463!2#!#'.'''&67>76!"&5543323!2s R >Qa5 5bTE,nCpS2 - /)  1 ""  >5)  " ,8B#Hv  $ ;IR)')$<| /Z[/ /[и/)и/) и / 0и0/3и3/G 9 UиU/-+$+ + и 01#!"&5546335!"&55463!2#!32#!"3!2'.'''&67>762, , ,{ R x    BOY- 3cVD(}IoN/ -v**"" !  N   !;/"  " +9B!Eh* $ 4>M0 762 ,{ R x ,>]   BOY- 3cVD(}IoN/ -`*""*      !;/"  " +9B!Eh* $ 4>M0 763!2655,, ,{ R x=Qa4 p-,oFrR/ -  ]\&% "" ^  ;1'  "lCEo $ 8EL'V V75!"&55463!2#!'.'''&67>76)-p t+(q?lR3{ R x>Qa5 2bVF-mDrR1 - sDb " tGJw  % 4CK&n""ur >5)  " ,9B!Iu $ 75!"&55463!2#!"32>54.'.'''&67>762EE@26Ra,{ R x,bR5@_m,QK66KQQM66MQ  BOY- 3cVD(}IoN/ - $2"-<${""{$<-3?# &! '' !&   !;/"  " +9B!Eh* $ 4>M0 76&#!"5543!27#!"554335!"&55463!2#!32'&&'&&'''&676676W;< #SWW'  Hx,J (/Dh{ R x`m&j m+V -! $  1"   '+ # !#Z""Z  76235#1z w { R xp BOY- 3cVD(}IoN/ -"" ! ! }   !;/"  " +9B!Eh* $ 4>M0 ItC;[\/ /и \%и%/>и>/K%9ZиZ/]/ / +7&+&и/&+и+/K 901%4332##"55#"554376##"&5'"&554632>7>'&&'''&76654632)) {  @K+ GD< QUO#RRJ tP Ht!<  qn.  " "   ." 1)Bm" " b8o` E\Lo8Lt/E/^и^/E@^@9^W ^W9и/E9к^W9@7>'&&'''&76743>^N68L5eY 0 C6=D/ 0>$ )) x AJ+BA: OTO#NNJjN Ej#hL  . L3+J H+W? " +oJ  7['k@   81)   0 " z"   "M8X #P35U$ %X 4tdAa*1+++ и*BиB/P1*91\и\/c// +?2+2)и)/25и5/?<и bZ . /b2+9i. 9o9BstM " $)$80%  R24U" 0T    " Dqy1Ef*+27++и/и/!и7?к*9/NVиV/Ngиg/vN9//.+ %++dS+?8+dWNиN/WZиZ/daиa/01#!"&55463!26554&#!"&55463!2#!"3!2##"55#"5543343326##"&55'"&554636766'&&'''&766543##"54332y /#- #-9 & & .a0+;j/ 9n:Bs{X? 6TX@ aZ . &&#*9*#0*, f "    " B4P D-*J " ,pE  D|d#7Wu+$)+v{+v и{и/)1кq9q/[@кgq[9v~/ ++UE+1*+UI@и@/IKиK/URиR/017463!2#!".57"3!26554&#'##"55#"5543343326##"&55'"&554636766'&&'''&766743##"54332 , ,']  T& & 0c2+s[ 9n:Bs{WB 4V6~ ^Y . &&F** e e  "    " ]8T J/^?" +rV 2Dvm';Gg+(-+<A+A&6FVfv ]A]Aи/-5к9/PwP9<D/+#+eU+5.+U>и>/eYPиP/Y[и[/ebиb/0174>32#".732>54.#"%##"55#"554334332##"543326##"&55'"&554636766'&&'''&7667436Zu>9s]::]s9>uZ6J/HX)$XK33KX$)XH/& &&& 0e2+q[ 9n:Bs{WB 4V6~ ^Y . )9##9**:##:'##$$ " F   " ]8T J/^?" +rV ItC 3S}T// Tи/6и6/C9RиR/ U///+и/#и#/C901#"543326##"&5'"&554632>7>'&&'''&766546325))  AH+ JG@ QUOMSQ tP Ht!<  qn. ~"  ." 1)Bm" " b8o` E\QjC<Zfg/ /и/ g&и&/?и?/L&9YиY/ ^иdиha//+8"+8'и/',и,/La9017!2##"554&#!"&55466##"&55'"&554632>7>'&&'''&76743#"54332/)   ?F+ HF= MSN!NPLjN Ej#hL  . ))#)$    "l8X #P35U$ %X 4Q[ 2Hj>7+.e+ +eи/.IиI/We.9 l/;4+; и /!ܹ.и/4B01%#"54332%"&55'"&554632>7>76#!"&5543323!2'&&'&''&7>746325))8 LJB MSN!NPL Y.++R<+% RA8и8/AFиF/h%u901#!"&55463!26554&#!"&55463!2#!"3!2##"&55'"&554632>7>76'&&'''&766543#"54332W /#- #-X  @H+ GD< MSN!NPLoH Cl#gN ml . ))#*4*"$4*&   k g"Y!;[ #Q44U%&-N  Q|D#Jht+-4+sl+s и /lи/Z4-9svo/ ++F0+F5,и,/5:и:/017463!2#!".57"3!26554&#6##"&55'"&554632>7>'&&'''&76743#"54332 ,!,E]  8 @H+ FD< MSN!NPLjN Ej#hL  . ))H** g g <  ~ z"l8X #P35U$ %X -Q|CBLjvD+?++D иKк\?9nиtиxq/G+C+1B+B и /1> и /01".5546332!546332#"&55'"&554632>7>76#3!2655'&&'''&76743#"54332;,, ,p FD< MSN!NPL @H]  jN Ej#hL  . ))VY&: {"   N N8X #P35U$ %X Qv"Ikwq,3+vo+3v9f3,9f/JX3,9vyr/E/+E4+и+/49и9/01%'&&'''&7>743276##"&55'"&554632>7>'&&'&''&7>74632#"54332Q &o v,*pHsQ+ 0 \ BJ+EA9 MSN!NPL pEEo#fL  8Q3. ))  :_"  iABk" %9DJ%    "}(';W $R00Y," ?CI)\QvL'Nlxϻ+K++wp+A&6FVfv ]A]pи/^+K9wzs/+#+=n+n(и(/=J,и,/mиm/0174>32#".732>54.#"7"&55'"&554632>7>76#'&&'''&76743#"543322Vp>8oX77Xo8>pV2G,ET)$TH00HT$)TE,@ FD< MSN!NPL @H"jN Ej#hL  . )) '8%$8((9%%9%""# # {"   I8X #P35U$ %X I763? R +BVd5 5hZI+yAvZ7 - F"" F ~   !C;.  "1@J&N}0 # IX`/ 76235#p R (/)  W BOY- 3cVD(}IoN/ -""#)$R   !;/"  " +9B!Eh* $ 4>M0 *5 ##*B*.#l"~ 76235# ,# R ' ,>]   BOY- 3cVD(}IoN/ -`*""*      !;/"  " +9B!Eh* $ 4>M0 76'&&'''&67667432* R +>Qa5 2bVF-mDrR1 -  &m t*&y . x ""   >5)  " ,9B!Iu $ 54.'.'''&67>7625!6632EE@2@0 R 1B@_m,QK66KQQM66MQ  BOY- 3cVD(}IoN/ -Y"CA $2"2>""?33?# &! '' !&   !;/"  " +9B!Eh* $ 4>M0 <*:(/8/+01'.'''&67>763#!"&55463!2GT_2 7eWHDTb6CrW7 -   R  1XH5 !:IW/0ZM> # Qev> e""N-.3+.P0/*7+LC+01'.'''&67>76!2##"554&#!"&5546%#!"&55463!2>Qa5 5bTE,nCpS2 - /)    R  >5)  " ,8B#Hv  $ ;IR)#)$"#<(>N!4-+8)+LC+01'.'''&67>76!"&5543323!2#!"&55463!2>Qa5 5bTE,nCpS2 - /)   y  R  >5)  " ,8B#Hv  $ ;IR)')$"#762#!"&55463!24 /#-  #-Q 'o s,*qHrQ. -   R z#*B*2#B*5 [ 762#!"&55463!232##"54&##"&5546   'N!, !,C&  'o s,*qHrQ. -   R k/$  q J*9$G*<  W 76#!"&55463!2 , ,>]  >Qa5 5bTE,nCpS2 -   R i**    >5)  " ,8B#Hv  $ ;IR)O"#762#!"&55463!2,, ,7   BOY- 3cVD(}IoN/ -  R lk&f f   !;/"  " +9B!Eh* $ 4>M0 e"#76'&&'''&67>7632%#!"&55463!2>Qa5 5bTE,nCpS2 -  -p t+*nCqR1 0   R  >5)  " ,8B#Hv  $ ;IR) DZ " mEGr % 9HP' ""32"32>54.'.'''&67>76#!"&55463!2EEA22AEDEF@22AEEQK66KQQM66MQ >Qa5 5bTE,nCpS2 -   R '3"!3&&4!"3' ,""+ +"", q >5)  " ,8B#Hv  $ ;IR)J""_tC&23/*/3!и!/A&6FVfv ]A]!9*14-/'/'-901'.'''&67>54632#"54332 *9G( ;70 hH =Z9/ ))By8(YXS!  CJN$[B :h~QpC$:FG///G и /A&6FVfv ]A] 9/&и&//*/>и*Dи*HA/,/&3+,A901'.'''&6766'432!2##"554&#!"&5546%#"54332 +:E& ;<6!pK wx / /) $ /))I?*LB5 ! )8C$L|1! Iމ#)$7Qa'=IK3,+HA+#,39#/#9HKD/7(+01'.'''&67>'432!"&5543323!2'#"54332  -=H& ?=4"oK 6Y?! /  /)  "))'J#)QI; !4?G!Q1! !bvF#)$~Q|X@Lc+KD+K и //9<и54632#"54332X  /#-+ #-o 6 .;E$  @;1 gK  >X7/ ))#*K*9$K*= OF32'.'''&7>54632#"54332!"&55463!24&#"326#"&554332  &L!, !,B%  W*:$$:**:$$:*& .;E$  @;1 gK  >X7/ ))0X X0--11--0  q V*A$Q*D  *, !,, !+F++> и>8и8/и/>,>9EиJиPиTM/A+=+01".546332!546332#'&&'''&6764323!26557#"54332S,, ,sP Dp nQ  / ?  7))jh&o!=N' !%p9Gw5!  Bk k/Pw#LXY/P/Yи/A&6FVfv ]A]9PW8W9ZS/01'.'''&6766'432'.'''&67>7632#"54332uN >9/!pK wx / 2c 7cSC?Rb8CpS0 0 ))%F R' !/8>L|1! Iމ Lb ".;D$$E<0 % 324.#"32>'.'''&6766'432#"54332K.Oi543266'432#"5433232##W: ,(! O7 ! ."5E/ ! 2D) ) 8#1+ ) ))͌K6 59:NG  ==#U.Bz8  =~N~u3l+h{~"*8p=Si/*+8+CH+A88]A8)898I8Y8i8y8888888 ]89A/&/6/F/V/f/v/////// ]A//]*/9HWиC]иCgиCkE/Z/?L+_f+f_9f_901'&&'''&767&&'''&7>74326674632!2##"554&#!"&5546%#"5433232##\64TS9  +! 6I1  /A) '  9&((' /) $ /)) eQ3[  N'74326674632#"5433232##&766'462'&&''32##"554&##"&5546\64TS9  +! 6I1  /A) '  9&((' ))  RO. UA=NI6 /$  eQ3[  N'7&632674632#"5433232##6  /#-+ #-o Z84UR6  +""4H3  /@) & 9(M& )) #*K*9$K*= QSM3Z  O(74326674632#"5433232## , ,]]  \64TS9  +! 6I1  /A) '  9&((' )) **   YeQ3[  N'7432>746323!26557#"5433232##1,, ,\65SS9 (! 4H2  /@) '  8& '  6)) jh&kbP3Y  M':h3 $'; /T*! (MS_;  *L#*GBHP.Kl l"8p=z/*+]V+8+ri+A88]A8)898I8Y8i8y8888888 ]89A/&/6/F/V/f/v/////// ]A//]*/9Aii]Ai)i9iIiYiiiyiiiiiii ]Air9A]&]6]F]V]f]v]]]]]]] ]A]]]IV]9][и[/roиo/r~иr/01'&&'''&767&&'''&7>74326674632&&''&&'''&6766'4327>'4632#"5433232##\64TS9  +! 6I1  /A) '  9&((' J?YM11NYA b[ 0 "- .#/ ZU ])) eQ3[  N'+L+zi+i ALL]AL)L9LILYLiLyLLLLLLL ]"L9AR&R6RFRVRfRvRRRRRRR ]ARR]0>R9RCиC/zoиz}l/W+a+qx+"xq90xq9fиf/0174>32#".'&&'''&767&&'''&7>7432667463232>54.#"%#"5433232##*LjA32'&&'''&767&&'''&7>7&632674632#"5433232##4.#"32>7!"&55463!2'#"&5546332%=R--S>%%>R--R>%Z84UR6  +""4H3  /@) & 9(M& )) m*9 9**9 9* L -/  // .SM3Z  O( J(pl  ns*)'YyEE><3  0pd9Uk+,&+4+JO+[`+A&6FVfv ]A] 49A,&,6,F,V,f,v,,,,,,, ]A,,]&,9J:и`;и;/[Aи`GиG/[m>/]/Wd+;H+ ]>9H;901'&&'"''&767&'''&7>7&6326674632354332##"55###"54332!2##"554&#!"&5546F'  #B/\  )0#0T  ,=' ' .%"& f&&f&&R /)  jX/I  C"aY *%%9XE! 'MS[6 XM#;;Z#)$0XnJD+S+++и A&6FVfv ]A]*D9AJ&J6JFJVJfJvJJJJJJJ ]AJJ]]DJ9]/dp/hY++990134332##"5###"54332'&&'"''&7667&&'''&7667&6326674632!"&5543323!2f&&f&&K)  #HC*  "./M  OO ' 0$'& /)  q>`:n52Q  H$9f* +6aD Ou 1`-$BE_$)$0qy1m[`Z+2h+~+up+A`&`6`F`V`f`v``````` ]A``]Z`9/pи/и/u!и*A2&262F2V2f2v2222222 ]A22]@h29MZ`9~nиp{иur/.+ %+o|++@|o9M|o901#!"&55463!26554&#!"&55463!2#!"3!2'&&'"''&7667&'''&7>7&6326674632354332##"55###"54332y /#-  #-O iE&  $B/]  "-%.W  ,=' ' -$"& f&&f&&#*F*4$F*8 C3Z+/G  A![] '%8SH! 'JOW5 UG#::T.0|d#]y;PJ+$X+ns+e`+AP&P6PFPVPfPvPPPPPPP ]APP]JP9/e и`и/A$&$6$F$V$f$v$$$$$$$ ]A$$]1X$9=JP9n^и`kиe{b/ ++_l+=l_901%463!2#!".57"3!26554&#'&&'"''&767&'''&7>7&6326674632354332##"55###"54332 , ,=]  F'  #B/\  )0#0T  ,=' ' .%"& f&&f&&x**   WjX/I  C"aY *%%9XE! 'MS[6 XM#;;Z0|dZvGKE+U+kp+b]+AK&K6KFKVKfKvKKKKKKK ]AKK]EK9/x и]и/bA&6FVfv ]A])U98EK9k[и]hи]и/b_/{+w+\i+8i\901".546332!546332#'&&'"''&7667&&'''&7>7&632>74632354332##"55###"543323!2655[,, ,F'  #BE.  !+.U  +=' ' - & f&&f&&  jh&cgY/H  B#2Z- '/VF! 'KQ[6 VM#;=DK-(Gf f0p<vIic+++~y+449A&6FVfv ]A] 9и/+и+/41и1/q9q/=J9Ai&i6iFiVifiviiiiiii ]Aii]Vci9wиyи+и/~{/01&&''&&'''&6766'4327>'4632'&&'"''&767&'''&7>7&6326674632354332##"55###"54332?YM11NYA b[ 0 "- .#/ ZU RF'  #B/\  )0#0T  ,=' ' .%"& f&&f&&![56Y U34T#$ 0e  @9/ :HR)   P' fjX/I  C"aY *%%9XE! 'MS[6 XM#;;Z 0pj'a}?TN+(\+|e+sn+AT&T6TFTVTfTvTTTTTTT ]ATT]NT9/s и /nи/A(&(6(F(V(f(v((((((( ]A((]5\(9ANT9|kиnyиsp/+#+mz+Azm90174>32#".732>54.#"'&&'"''&767&'''&7>7&6326674632#"54332354332##"55#.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#F'  #B/\  )0#0T  ,=' ' .%"& &&f&&f'+C/.C,,E..E)--..jX/I  C"aY *%%9XE! 'MS[6 XM#;;Z$8pQe3R>+L+i+i ALL]AL)L9LILYLiLyLLLLLLL ]"L9AR&R6RFRVRfRvRRRRRRR ]ARR]0>R9RCиC/oиiܸtиyиl/W+qx+a+{+fиf/0174>32#".'&&'''&767&&'''&7>7432667463232>54.#"%#"5433232##32##*LjAYEXK/K>YAO9AO97AO9E01'&'.'''&767&&'''&7>54763266'47632#"5#"554334332W: ,(! O7 ! ."5E/ ! 2D)) 8#1+) ) )͌K6 59:NG  ==#U.Bz8  =~N~u3l+h{ $ $=pC=Sg/*+8+CH+A88]A8)898I8Y8i8y8888888 ]89A/&/6/F/V/f/v/////// ]A//]*/9*PиP/HTиCZиH`иCiW/E/Ta+?L+EW9EW901'&&'''&767&&'''&7>74326674632!2##"554&#!"&554654332##"5#"5543#\64TS9  +! 6I1  /A) '  9&((' /) $ )) eQ3[  N'a<Pf0*+7+C>+A77]A7)797I7Y7i7y7777777 ]79A0&060F0V0f0v0000000 ]A00]*09>IкU*09U/\Ch@/`Q+=J+01'&&'''&7667&&'''&7667&63267463254332##"5#"5543!"&5543323!2$ '/44ZS6  *%8F.  SW ' <&P' )) /)  k\60'   V)Ds10=#6]'  L} YS-Ks $ X#)$7&63267463254332##"5#"5543X  /#-+ #-o Z84UR6  +""4H3  /@) & 9(M& )) #*K*9$K*= QSM3Z  O(7&632674632##".55463326554&##"&5546332##"332677>7654332##"5#"5543"3326554&##,,Z84UR6  +""4H3  /@) & 9(M& 6 %K!, !,D" ))  t **9SM3Z  O(7432667463254332##"5#"5543 , ,]]  \64TS9  +! 6I1  /A) '  9&((' )) **   YeQ3[  N'7432>746323!265554332##"55#"5543R,, ,\65SS9  (! 4H2  /@) '  8& '  )) jh&kbP3Y  M':h3 $'; /T*! (MS_;  *L#*GBHP.Kl l/ $ =p=z/*+]V+8+ri+A88]A8)898I8Y8i8y8888888 ]89A/&/6/F/V/f/v/////// ]A//]*/9Aii]Ai)i9iIiYiiiyiiiiiii ]Air9A]&]6]F]V]f]v]]]]]]] ]A]]]IV]9][и[/roиo/r{и{/rrи/|иr~/01'&&'''&767&&'''&7>74326674632&&''&&'''&6766'4327>'463254332##"55#"5543#\64TS9  +! 6I1  /A) '  9&((' d?YM11NYA b[ 0 "- .#/ ZU )) eQ3[  N'+L+lg+AC&C6CFCVCfCvCCCCCCC ]ACC] >C9 /ALL]AL)L9LILYLiLyLLLLLLL ]"L90>C9gRиR/ \grиl{i/a+fs+W+oиo/01%#".54>32'&&'''&767&&'''&7>743266746324.#"32>54332##"5#"5543K.Oi J(pl  ns*)'YyEE><3  ") $ ~0<Rfr.(+7+YT+gl+A&6FVfv ]A](g9A.&.6.F.V.f.v....... ]A..]A(.9A/HT_иgto/L=+S`+01'&&'"''&7667&&'''&7667&6326674632!"&5543323!254332##"5#"5543##"54332K)  #HC*  "./M  OO ' 0$'& /)  &&k I&&:n52Q  H$9f* +6aD Ou 1`-$BE_$)$Q $ T0qy1m?`Z+2h+to++A`&`6`F`V`f`v``````` ]A``]Z`9/и/и/!и*A2&262F2V2f2v2222222 ]A22]@h29MZ`9ozи/.+ %+n{++01#!"&55463!26554&#!"&55463!2#!"3!2'&&'"''&7667&'''&7>7&632667463254332##"55#"5543##"54332y /#-  #-O iE&  $B/]  "-%.W  ,=' ' -$"& &&k I&&#*F*4$F*8 C3Z+/G  A![] '%8SH! 'JOW5 UG#::TI $ 0<Xnz/.(+7+DM+ot+A&6FVfv ]A](o9A.&.6.F.V.f.v....... ]A..]D>Iи>QиMUк^(.9^/co|w/hY+=R+QJ+JQ9JQ901'&&'"''&7667&&'''&7667&632667463254332##"5#"554335#"5543!"&5543323!2'##"54332K)  #HC*  "./M  OO ' 0$'& &&e ee O/)  &&:n52Q  H$9f* +6aD Ou 1`-$BE_j # a # $)$<A[!./?/YK+KDS01'&&'''&&7667&&'''&7667&6326674632!2#!"&55463!546332.9@!  9k"eL  #GfH  rh )  ',93*   +0-+OD8 " &sFH=  [6Ct/# Gr . =80B^"" m g< 9 gC aq ) F*9>)  ) )  /#@7,%a99^& ='7^! 0[6O-lJ#ll"h$#)<AYo5e^+0/?/iZ+SF+FWN01'&&'''&47667&&'''&67667&6326674632#!"&55463!54332!2!"&5543323!2 ,9B# ?l7 : gB aq ) F*>=)  ) ~/)   $B9,'bm g< 9 gC aq ) F*9>)  ) v** !  W#@7,%a99^& ='7^! 0[6O-lJ#jj"kb<  5 fB `o) D)8>(  ) z#*D*(#8*7 ] !>5) "]76V% 9$4X  .W3I*fES"#SSkb<  5 fB `o) D)8>(   *R!, !,H)    )   **N !>5) "]76V% 9$5Y .W3I*fE N*,$<*<  "#VV   <|#f~y+ +wp+5 9B 9E 9 U/d/ ++tg+gxo017463!2#!".57"3!26554&#'&&'''&47667&&'''&67667&6326674632!"&55463!54332!2 , ,>]   +9C# >m g< 9 gC aq ) F*9>)  ) e**   #@7,%a99^& ='7^! 0[6O-lJ#bb")  ) _  ba&#?6+$'.59\& =&6]  0Z5M,kH<"#ZZd d32'&&'''&47667&&'''&67667&6326674632"32>54.%#!"&55463!54332!2EE@1@^m,-m_?@^m +9C# >m g< 9 gC aq ) F*9>) QK55KQRK66LQ  )  %2"2?$ $@23@$ #@7,%a99^& ='7^! 0[6O-lJq'! (( !'""dd3tBX/F/tиt/FWtW9Lиt}WI/C/}h+NU+CI9!CI9hcиc/}oиo/}tиt/01&'.'''&767&&'''&67>7426674632#"5433232##6"'"&5546326754633666 !-2 '&" V<  3#91m.B+ (  "!"( ))  "NPP$LRN ?FH +A99<7-   !).Cv- *-A(qN  MUY,  0/.*% :E~y"E# #  "  7j@Vl/K/и/и/KFF9KBиB/KZиF`иFjиFH/]/BO+bi+H]9 H]9iи/vܹи/01'&&'''&4767&&'"''&7>7626676632!2##"554&#!"&5546%#"5433232##'6##"&55463!54633266 &/2&VO> (!/O9,@, (  8 $( /)  B)) x B}  +I$4,  F33[( !/.U# ?DI$  #"'=-b0#)$4"o"  #` _7BXnwd]+p+WF+]W9 ]d9WLиWI/hY+}+NU+}pиp/01'&&'''&4767&&'"''&7>7626676632#"5433232##!"&5543323!26676##"&55463!546332  %-/#SO= -!6N9,?+ (   "( )) /)  fC{7  Qd  +(/(A/5]( $0"-R" '432766'4632'&&'''&4767&&'"''&7>7626676632#"5433232##%6676##"&55463!546332Y?WL7o+XB 6I, 0 D; ?B/ VP &02&WP> *!/P9-A, )  8!$()) E8  Qd +P/-J" >V,K$$ 8@K.   3d){S  !Bf"  v$2* D02W&--R! >@$8x; 0q>+2[lPG::>^ #I2=q.&/7 OH  ;U  :(G=< # " =p8Tѻ+IN+@;+@99/! !9и/I9и;Fкd@9!и/@=/:G+-+-и/и/иܸи/01&&''&'''&67>'432766'4632354332##"55###"54332'&&'''&&7667&&'''&67>746266746326#'"&5546375433266?WL7o+XB 6I, 0 D; ?B/ VP g&&g&&C$  :K1 !%G' "9*'  - %'S?FE:8 3p5)3jP/-J" >V,K$$ 8@K.   3d){S  !Bf"  ׿ ,0P?&0S  .,G  8AG" %%=+c0p # "XV GtCBNwǻsj+=+MF+A==]A=)=9=I=Y=i=y======= ]=9MyC/I/n^+CI9!CI9^YиY/^seиe/sjиj/01&'.'''&767&&'''&67>7426674632#"543326"'"&55463667546332>+ )14 ,,(W<  3#91m.B+ (  "!"(  ))"TXV$LRN EMN +!FE@??4.$  ! %,Dw- *-A(qN  MUY,  0/.*% :E~$ #  " IbCYeqOH+{+d]+Hd9 HO9d`/SD+~p+py{и{/yи/01'&&'''&4767&&'"''&7>7626676632!"&5543323!2'#"54332'6##"&554633275433266$  %.0$TO= -!8M9,?+ (  #( -/)  #))  Xg ' G)0*B06_( $1"-S" >DH#  #&0e.$)$]" #cb EqY1s*+++и/и/!и!/D!9P!9S*9/.+ %++~+~и/и/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'''&47667&&'"''&7>76266766326##"&554633275433266#"54332Y /#- #-[ &02&WP> %/P9 -A, )  8!$(  Xg ' F))"*>*."<*1 W!/(@./R$ +*M  9?B!  $9)Y,p" #a` W"K|D#h+~++ и /и/7 9F9/ ++s+s|~и~/|и/017463!2#!".57"3!26554&#'.'''&47667&&'"''&7>76266766326##"&554633275433266#"54332 , ,I]   &02*)&P? $/P:-A, (   !$)  Xg ' K))d**   y$%3,! #*3Z'  /.T# >DH$  ## -a0[# #a` e K|C`jb+++b к09<9?b9iиии/e+a+u+u~и/~и/uи/01".546332!546332#'.'''&47667&&'"''&7>76266766323!26556##"&554633275433266#"54332;,, ,&02*)&P? $/O:-A, (  9!$)  G  Xg ' H))`c&w#1* !(0U&  -,Q! ;AE"  !%:*]-/W W4" #cc E<Aab/W/^и/W^9bOиO/VOV9/иV4и4/W<и ]6Bu/# Gr . ?9/D[b""/A/MF+A>9 A>9FU01'&&'''&47667&&'''&67667&6326674632"&5!"&55463!2#!# ,9D#  >hfP  CfE og + N*96+ | R w$$F>4 # "gи>/;>9HиH/n/[/j/"+ +ик;j9Hj9Kj9017!5!"&55463!2#!32##"554&#!"&5546'&&'''&47677&&'''&67667&6326674632 { R x/)   +9B# =khC   @ gC aq ) O,86) ""#)$[#?6+%^8?g( B,6\! 0[9S1pH<B\r1ha+1/@/l]+OH+HW01'&&'''&47667&&'''&67667&6326674632"&55!"&55463!2#!#!"&5543323!2 +9B# ?j iA   B gB aq ) N,79) s R !/)  $B9,'`:Bl) D.9^# 3_4* #Y6;b$  ?(5X .~W8N-iC<|'jz}n+v++99F9I9|Y/h/s!+k+ +и01746335!"&55463!2#!32#!".5'&&'''&47677&&'''&67667&6326674632"3!26554&# ,{ R x ,> +9B# =khC   @ gC aq ) O,86) m  `*""*?#?6+%^8?g( B,6\! 0[9S1pHa   5* $\675!"&55463!2#!'&&'''&47677&&'''&67667&6326674632"32>54.EE@26Ra,{ R x,bR5@_m +9B# =khC   @ gC aq ) O,86) QK66KQQM66MQ $2"-<${""{$<-3?# #?6+%^8?g( B,6\! 0[9S1pHd&! '' !&EtCAU}~/N/~gиg/и/NII9g`9и9/NBиIE/K/BO+yh+KE9KE9h_и_/hmиm/01&'.'''&767&&'''&67>74266746324332##"55#"554376##"&5'"&554632>7>"Z6 *+' R; 1!9K7-@* '  "! ' )) {  @K+ GD< QUO#RRJ==6P  $*?n*%(?&3X%  HPS)  /+&! 8zAy " "   6" Lo8}߸//и/и/9 9и/и/M9PиP/uиu/~и/*+~++*и/и/и/и/01&&''&&'''&6766'432766'4632'.'''&4767&&'"''&7>76266766324332##"55#"554376##"&55'"&554632>7>=^M68L5eY 0 C6=D/ 0=$ &.2)(%L> *4N9,@+ (   ( )) x ?H+EC> OTO#NNJL2*KH+W? " +oJ  7['k@   80(   #/) '1V% -+O  9@D"  #+]-1+ " z"   "0td;O[fm+<A+PU+m-и-/-P9AIиPR/X/IB+{n+RX9RX9neиe/nqиq/{xиx/01'&&'''&767&'''&67667426674632##"55#"554334332##"543326##"&5'"&5546367> D' =Q8  -!-B. NT '   )#'( & &&&  -x;+%D&  ;U  1&C5q< " N~8"   *"EtCAMuv/E/v_и_/EL_L9_XLwB/H/q`+BH9BH9`WиW/`eиe/qlиl/01&'.'''&767&&'''&67>7426674632#"543326##"&5'"&5546332667>"Z6 *+' R; 1!9K7-@* '  "! ' ))  7626676632#"543326##"&55'"&554632>7>!"&5543323!2,&.1$TL= "7N9,?+ (   !( ))  54.75!6632EE@2@0 R 1B@_m +9B# =khC   @ gC aq ) O,86) QK66KQQM66MQe"CA $2"2>""?33?# #?6+%^8?g( B,6\! 0[9S1pHd&! '' !&4<AQ./?/OF+01'&&'''&&7667&&'''&7667&6326674632#!"&55463!2.8@!  9k"eO "#JeH  rg )  !(,63*  R 2.,ND8 " &qFJ> ]6Bu/# Gr . ?9/D[b""4* #Y6;b$  ?(5X .~W8N-iCg"#4* #Y6;b$  ?(5X .~W8N-iC P*2$B*>  "#   <`?[sP+++kX+P9Xk9 P9P`и`/A]A)9IYiy ]A]A)9IYiy ]//=/+wJ+++e\+++JGиG/TиT/nиn/01'&&'''&47667&&'''&67667&6326674632##".55463326554&##"&5546332##"33267667672".54>32#!"&55463!2!"&55463!2"32654&7#"&554332 *8B$ 4* #Y6;b$  ?(5X .~W8N-iC Q*<$L*?  O!,, !+, "#& ' R&$$'% $ <|#fvw//wи/и/и/  и /и/5 9B 9E 9 xU/d/ ++pg+017463!2#!".57"3!26554&#'&&'''&47677&&'''&67667&6326674632!"&55463!2 , ,>]    +9B# =khC   @ gC aq ) O,86)  R }**   z#?6+%^8?g( B,6\! 0[9S1pH#"5* $\6tCBNno/F/o=и=/A&6FVfv ]A]=9FMpC/I/j^+CI9CI9^YиY/jeиe/01&'.'''&7667&&'''&67>7426674632#"543326"'"&55463667> \7 *+( S<  *8L7-B* (  ""!' ))!NST(QUP NTR#RTNC>9V " '-Cv- *C'6^'  MTY,  4-)" 7626676632!"&5543323!2'#"54332'6"##"&55463326766,&.1$TL= "7N9,?+ (   !( /)  ))JK '/576266766326"##"&55463326766#"54332Y /#- #-T %/1$SL= "7M9 ,?* (   !( JK '/5B!  "*\-  " s)I|D#gs/k/и/и/kи/kr и /k и /и/kи/6 9<и7626676632#"54332'6"##"&55463326766 , ,I]   %.1$TL= "7N9,?+ (   !( ))JK '/554763266'47632#"54332W: ,(! O7 ! ."5E/ ! 2D)) 8#1+) ))͌K6 59:NG  ==#U.Bz8  =~N~u3l+h{~=pC=S_ /*+8+CH+A88]A8)898I8Y8i8y8888888 ]89A/&/6/F/V/f/v/////// ]A//]*/9*PиP/HWиC]иCaZ/E/?L+EZ9EZ901'&&'''&767&&'''&7>74326674632!2##"554&#!"&5546%#"54332#\64TS9  +! 6I1  /A) '  9&((' /) $ /))eQ3[  N'a<R^0*+7+]V+A77]A7)797I7Y7i7y7777777 ]79A0&060F0V0f0v0000000 ]A00]*09A*09A/H]`Y/L=+01'&&'''&7667&&'''&7667&632674632!"&5543323!2'#"54332$ '/44ZS6  *%8F.  SW ' <&P' ./)  "))k\60'   V)Ds10=#6]'  L} YS-K#)$7&632674632#"54332X  /#-+ #-o Z84UR6  +""4H3  /@) & 9(M& ))#*K*9$K*= QSM3Z  O(74326674632#"54332 , ,]]  \64TS9  +! 6I1  /A) '  9&((' ))**   YeQ3[  N'7432>746323!26557#"54332R,, ,\65SS9  (! 4H2  /@) '  8& '  7))jh&kbP3Y  M':h3 $'; /T*! (MS_;  *L#*GBHP.Kl l/>w<eq׻0*+7+pi+A77]A7)797I7Y7i7y7777777 ]79A0&060F0V0f0v0000000 ]A00]*09Q79psl/01'&&'''&7667&&'''&7667&632674632'.'''&67>7632#"54332$ '/44[S7  *%8F.  SW ' <&P' 2c 7cSC?Rb8CpS0 0 ))j\70'   W)Et20=#6]'  L} YS-K Lb ".;D$$E<0 % +L+pi+AC&C6CFCVCfCvCCCCCCC ]ACC] >C9 /ALL]AL)L9LILYLiLyLLLLLLL ]"L90>C9iRиR/ \psl/a+W+01%#".54>32'&&'''&767&&'''&7>743266746324.#"32>7#"54332K.Oi324.#"32>#"5433232##F:[@B[77[B@[:G(@.0?&&@0.@()) 8r\;:]r8;s\88[s;-\J..J\-+\K00K\ ~"*rp'=S ++-2+A]A)9IYiy ]A&6FVfv ]A]2Aи-Gи-Qи-U//D/+)6+IP+#+01#".54>324.#"32>!2##"554&#!"&5546%#"5433232##J$?W42V?##?V20VA'F.="#<,+<%"=./) $ /)) 2S<"!++3:+#+01#".54>324.#"32>#"5433232##!"&5543323!2I$@W31V?$$?V1.VB(F.="#;-,<$"=.)) /)  4W>#"?V57X> =X7*A++A*(@--@~"$)$tt'Oe! +yr++dS+A]A)9IYiy ]A&6FVfv ]A]dYиdV/|o++[b+MF+#+PиP/okиk/|и/01#".54>324.#"32>'&&'''&676676&##"554332'#"5433232###"##&&554332322676676F$@V10T?$$?T0.UA'D.=!#;,,;$!=.3 M$ #M$R*Sn <>))  %F/) "# '   3T<"!=T36U; ;V5(=**=(&=,,=,*%@ C"(D  2w= " ,j"b")"te';Qu +f_++P?+A]A)9IYiy ]A&6FVfv ]A]?P9/A]A)9IYiy ](2PEиsиs/Pи/(B/-+j[++7+v+GN++#+[WиW/jnиn/01#".54>324.#"32>#".54>32#"5433232#####"&5543323326676676%!"&55463!24&#"326#"&554332F$@V10T?$$?T0.UA'D.=!#;,,;$!=.I*:$$:**:$$:*9)) &($/) &# & Xl ^0--11--0 3T<"!=T36U; ;V5(=**=(&=,,=, !,, !+A"P#) $ & ' )(&&% $ tqEYo#P +>+F+6#++и+/AFF]AF)F9FIFYFiFyFFFFFFF ]AP&P6PFPVPfPvPPPPPPP ]APP]#]и6cи6mи6q`/B+K+ 9+el+0'+U+01#".54>32#!"&55463!26554&#!"&55463!2#!"3!24.#"32>#"5433232##F%@V10S?$$?S0-UB(  /#-+ #-o .< !;++:# <.)) /O:! 9P03Q88Q,#*K*9$K*= %:&&:&$9)(:"tp'j +TA++n+A]A)9IYiy ]A&6FVfv ]A]9и9/IиI/9JиJ/\и/q/0/3//+>W+v}++#+EиE/Mи3b_и_/beиe/3и/01#".54>324.#"32>###.55463326554&##"&5546332##"332676676#"5433232##32##"54&##"&5546F%@V10S?$$?S0-UB(F.< !;++:# <.  'N!, !, '% 2)) /$  /O:! 9P03Q88Q2%:&&:&$9)(:N*9$I*< q"2#) #tq';{72 +eR+(++и/A2&262F2V2f2v2222222 ]A22]J 29J/ZиZ/Jmи/!+-+Oh++_V+7+_и/!AиA/!Dиpиp/uиu/V|01#".54>3246332##".54.#"32>##".55463326554&##"&5546332##"332677>76"3326554#"5433232##F%@V10S?$$?S0-UB(<,, .< !;++:# <.6 %K!, !,D" w t >)) /O:! 9P03Q88QU**g%:&&:&$9)(: N*9$I*<     n"t`'i}ۻ +S@+t++и/A&6FVfv ]A]8 98/HиH/8[9/A]A)9IYiy ]jAtt]At)t9tItYtityttttttt ]ии/j/o+_2++y+++MD++#+2/и//y<и324.#"32>##".55463326554&##"&5546332##"33267667672%#".54>32#"5433232##!"&55463!24&#"326#"&554332F'AV.-T?&&?T-+UC)F/<:,+:!32"3!26554&#4.#"32>#"5433232## , ,]d$?W42V?##?V20VA'  .="#<,+<%"=.)) **E2S<"!32".546332!546332#4.#"32>3!26557#"5433232##I&@W10U@%%@U0-VC(,, ,/324.#"32>'.'''&67>7632#"5433232##I$@W31V?$$?V1.VB(F.="#;-,<$"=.52c 7cSC?Rb8CpS0 0 )) 3U=""=U36V< 324.#"32>&&''&&'''&6766'4327>'4632#"5433232##I$?W42V?##?V20VA'F.="#<,+<%"=.]?YM11NYA b[ 0 "- .#/ ZU U)) 2S<"!32#".54>324.#"32>4.#"32>7#"5433232##I$?W42V?##?V20VA'.Oi32!3!2#!"&55463!2#!"!24.#"32>#"5433232##F%@V10S?$$?S0-UB("  , , 5 .< !;++:# <.)) /O:! 9P03Q88QP **  O !  %:&&:&$9)(:"q|3GKa'> +H+4+.I+`O+A44]A4)494I4Y4i4y4444444 ]A>&>6>F>V>f>v>>>>>>> ]A>>]`Uи`cR//+9+&+W^+C+/и,и/HиJ01#".54>32!"&5546335#"&55463!2##324.#"32>35#%#"5433232##I$?W42V?##?V20VA' n Aj.="#<,+<%"=.cr)) 2S<"! ! ! (=))=(&<,,34332##"5###"543324.#"32>*-N:"";N--L8 9L&&&&$7%'5!!5%%7$%MtNMtM''MtNNsM%lPG.XF+,GX,,YH-,GZepd/?U8+0+$)+EJ+A&6FVfv ]A]$иJи/EиJ!и!/A8&868F8V8f8v8888888 ]A88]EW/G/5+AN+"+; +012#".54>354332##"55###"543324.#"32>!2##"554&#!"&5546+$I:$$;I$$G8"#8G&&&&$2=CF:2$ /)  8W<;W99W<+[LM],?#)$e/AW8+MF+0+$)++A&6FVfv ]A]$и!A8&868F8V8f8v8888888 ]A88]Y/QB+5+"+= +012#".54>34332##"5###"543324.#"32>!"&5543323!2+$H:%%;H$$F8##9F&&&&$2+b+V[+MH+A&6FVfv ]A]H#и#/+и+/M5иVFиHSAh&h6hFhVhfhvhhhhhhh ]Ahh]MqJ/B+e+ 9+GT+0'+k +012#".54>#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"543324&#"32>+%H:$$:I%$G8""8Gs /#-  #-O &&&&M:=CF:1$6U;:U77U;;T6#*F*4$F*8 -NVYKK\,>e|d'7Sc#\#+++T+HM+?:+? A&6FVfv ]A]:3и3/H8и:EA\&\6\F\V\f\v\\\\\\\ ]A\\]?e"3!26554&#354332##"55###"543324.#"32> , ,="$I:$$;I$$G8"#8G_  d&&&&$2=CF:2$x**(8W<;W99W<+[LM],?e|d/KYcCR++[+!L+@E+72+[ и2и/7A!&!6!F!V!f!v!!!!!!! ]A!!]@0и2=AR&R6RFRVRfRvRRRRRRR ]ARR]2bиb/7e4/^+O+Z+1>+U&+&BиB/01".546332!546332#2#".54>354332##"55###"543324&#"32>3!2655[,, ,%H:$$:I%$G8""8G&&&&M:=CF:1$\  jh&s6U;:U77U;;T6(NVYKK\,>Lf few/Aj8+0+$)++A&6FVfv ]A]$и!A8&868F8V8f8v8888888 ]A88]V9l/5+"+= +012#".54>354332##"5###"543324.#"32>'.'''&67>7632+$H;$%:I$$G7##8G&&&&$2=Y;;Y>>Y9"(@-^N'A.-A Lb ".;D$$E<0 % &&''&&'''&6766'4327>'4632354332##"55###"543324.#"32>+$I:$$;I$$G8"#8G?YM11NYA b[ 0 "- .#/ ZU &&&&$2=CF:2$8W<;W99W<+[LM],?epj';Wgm`#+(+X+V?+MH+M и /A&6FVfv ]A]A(&(6(F(V(f(v((((((( ]A((]H2и2/VEиHSA`&`6`F`V`f`v``````` ]A``]MiJ/-+]+7+GT+c+0174>32#".2#".54>32>54.#"%#"54332354332##"55#'4.#"32>.SqD>pV22Vp>DqS.-$I:$$;I$$G8"#8GA#?V3-VB))BV-3V?#M&&&&$2=CF:2$'+C/.C,,E..E8W<;W99W<+[LM],?jt'G ++F++A]A)9IYiy ]A&6FVfv ]A]F1и+Aܸ6иF;иFI./(/+#+3:+=D+01#".54>324.#"32>#"5433232##32##F:[@B[77[B@[:G(@.0?&&@0.@())  8r\;:]r8;s\88[s;-\J..J\-+\K00K\ ~  qp'G] ++F++A]A)9IYiy ]A&6FVfv ]A]F1и+Aܸ6иF;иFLи+RиF_./O/+3:+IV+=D+#+01#".54>324.#"32>#"5433232##32##!2##"554&#!"&5546I$?W42V?##?V20VA'F.="#<,+<%"=.))  /) $ 2S<"!324.#"32>#"5433232##32##!"&5543323!2I$@W31V?$$?V1.VB(F.="#;-,<$"=.))  /)  4W>#"?V57X> =X7*A++A*(@--@~!!$)$tqEYyIP +>+F+x]+]#и#/+и+/x5и5/AFF]AF)F9FIFYFiFyFFFFFFF ]AP&P6PFPVPfPvPPPPPPP ]APP]xcи]sܸhиxmиx{`/B+K+ 9+el+0'+ov+U+01#".54>32#!"&55463!26554&#!"&55463!2#!"3!24.#"32>#"5433232##32##F%@V10S?$$?S0-UB(  /#-+ #-o .< !;++:# <.))  /O:! 9P03Q88Q,#*K*9$K*= %:&&:&$9)(:!i!`ti/C: +o\+!0++!и/0и/#и#/+и+/A:&:6:F:V:f:v::::::: ]A::]T :9T/dиd/Twиܸии!ии///N+5+Yr+"++i`++?+iи/i'и'/NKиK/zиz/01#".54>32".5463323546332#4.#"32>##".55463326554&##"&5546332##"33267667672#"5433232##32##332655F%@V10S?$$?S0-UB('' ,.< !;++:# <.  $J!,~ !,?$  ,))   o /O:! 9P03Q88Q1tr&%:&&:&$9)(: L*7$G*:  q!i!ht tq|'7Kk+B+++8+ 3+A88]A8)898I8Y8i8y8888888 ]AB&B6BFBVBfBvBBBBBBB ]ABB]3Oи Uи3eܸZи _и iи mR/0 +#=+W^+(+ah+G+017463!2#!".5#".54>32"3!26554&#4.#"32>#"5433232##32## , ,]d$?W42V?##?V20VA'  .="#<,+<%"=.))  **E2S<"!32".546332!546332#4.#"32>3!26557#"5433232##32##I&@W10U@%%@U0-VC(,, ,/324.#"32>'.'''&67>7632#"5433232##32##I$@W31V?$$?V1.VB(F.="#;-,<$"=.+2c 7cSC?Rb8CpS0 0 ))  3U=""=U36V< 32#".54>324.#"32>4.#"32>7#"5433232##32##I$?W42V?##?V20VA'.Oi32!3!2#!"&55463!2#!"!24.#"32>#"5433232##32##F%@V10S?$$?S0-UB("  , , 5 .< !;++:# <.))  /O:! 9P03Q88QP **  O !  %:&&:&$9)(:!i!ctU';[o}׻2 +f+(+\+A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]A\\]A\)\9\I\Y\i\y\\\\\\\ ]?\9?/ZEи?UܸJиZOAf&f6fFfVfffvfffffff ]Aff]ZB/k+-+#a+GN+yp+QX+~+7+<и32#".54>324.#"32>#"5433232##32##4.#"32>7!"&55463!2'#"&5546332F%@V10S?$$?S0-UB(%=R--S>%%>R--R>%.< !;++:# <.))  m*9 9**9 9* L #.M9 8N/2O77O~/  // .2%7&&8%#8''8!n!w   lXtd/CG:+0+$)++A&6FVfv ]A]$и!A:&:6:F:V:f:v::::::: ]A::]$DиEиI//5+? +F+E"+012#".54>34332##"5###"543324.#"32>35#*-N:"";N--L8 9L&&&&$7%'5!!5%%7$%MtNMtM''MtNNsM%WG.XF+,GX,,YH-,GZ\e/AW['8+MF+0+$)++A&6FVfv ]A]$и!A8&868F8V8f8v8888888 ]A88]$XиYи]/QB+5+Z+= +="и"/Y012#".54>354332##"55###"543324.#"32>!"&5543323!235#+$H:%%;H$$F8##9F&&&&$2+b+V[+MH+A&6FVfv ]A]H#и#/+и+/M5иVFиHSAh&h6hFhVhfhvhhhhhhh ]Ahh]VpиHqиMuJ/EXp/p>YB+e+ 9+Gr+0'+k +pT012#".54>#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"543324&#"32>35#+%H:$$:I%$G8""8Gs /#-  #-O &&&&M:=CF:1$6U;:U77U;;T6#*F*4$F*8 ۅ'yqNVYKK\,>ee|d/KYcg]R++[+!L+@E+72+[ и2и/7A!&!6!F!V!f!v!!!!!!! ]A!!]@0и2=AR&R6RFRVRfRvRRRRRRR ]ARR]2bиb/@dи2eи7i4/^+O+Z+1f+e>+U&+&BиB/01".546332!546332#2#".54>354332##"55###"543324&#"32>3!265535#[,, ,%H:$$:I%$G8""8G&&&&M:=CF:1$\  |jh&s6U;:U77U;;T6}(fYNVYKK\,>Lf fnptC"6-+#+!+иA-&-6-F-V-f-v------- ]A--]!8//(+2 ++01#"5##".54>32343324.#"32>5):W/(+8E++2 +01%#"55##".54>323543324.#"32>!2##"554&#!"&55465))?R/1V@%%@V1+PA,)/="#<-,=$"=//) $ ,G2 ;Q24S91G.'<((<'%;**;#)$va"6L-+B;+!+A-&-6-F-V-f-v------- ]A--]!N/F7+(++2 +01%#"5##".54>323543324.#"32>!"&5543323!25)(>R/1V?$$?V1+PA*).="#;-,<$"=.J /)  tU/L6"?V57X> 4M1*A++A*(@--@#)$zt"6_ѻ-+sl+!+A-&-6-F-V-f-v------- ]A--]!/vi+(++]V+2 + и/ieиe/v{и{/01#"55##".54>32354332%4.#"32>'&&'''&676676&##"554332#"##&&5543323226766765)'>Q.0T?$$?T0*O?+).=!#;,,;$!=.;  M$ #M$R*Sn < %E/) "" '   -K5!=T36U; 3I/.(=**=(&=,,=+%A C"(D  2w= " ,")"x|S"6R-+L=+!+A-&-6-F-V-f-v------- ]A--]!DиD/!T/P9+(+BG++2 +01%#"55##".54>323543324.#"32>#!"&55463!2#!"3!25))?S.1V@%%@V1+Q@,)/="#<-,=$"=/E , , 5  +G2 ;Q24S92G. '<((<'%;**;** !  zqX"Thӻ_+M*+!+и2и*:и:/!DA_&_6_F_V_f_v_______ ]A__]!j/Q&+Z+/H++?6+d +01#"55##".54>32354332#!"&55463!26554&#!"&55463!2#!"3!24.#"32>5)(>N-0S>$$>S0*N?*)  /#-+ #-o -; !:+*:# ;-&(D2!:O/2P92F+4W#*K*9$K*= $9''9%#9)(:zpC"6y--+kH+cP++и/и/ иc#A-&-6-F-V-f-v------- ]A--]HXиX//?/B//(+Mf++{+2 +TиT/{\иBqnиn/qtиt/Bи/01#"55##".54>323543324.#"32>###.55463326554&##"&5546332##"33267667632##"54&##"&55465)(>N-0S>$$>S0*N?*)-; !:+*:# ;-  'N!, !, '% 2/$  (D2!:O/2P92F+-$9''9%#9)(:N*9$I*< ?#) #zqD"6JKA+ta+#+!+и/и/!,и,/AA&A6AFAVAfAvAAAAAAA ]AAA]AYиY/Aiиi/Yjиj/A|!/0+<+^w++ne+F +n&и&/0PиP/0Sии/и/e01#"55##".54>3235433246332##".54.#"32>##".55463326554&##"&5546332##"332677>76"3326554&#5)(>N-0S>$$>S0*N?*),,!-; !:+*:# ;-6 %K!, !,D" w t  (D2!:O/2P92F+.z**g$9''9%#9)(: N*9$I*<     x|C"6FZQ+:#+!+и!,иBAQ&Q6QFQVQfQvQQQQQQQ ]AQQ]!\/?0+L+&7++V +01%#"55##".54>32354332463!2#!".57"3!26554&#4.#"32>5))?Q.1V@%%@V1+RA+) , ,]]  /="#<-,=$"=/*F1 ;Q24S93I/ ~**   '<((<'%;**;x|C">R\ͻI+T(+!+иT0и2и!:AI&I6IFIVIfIvIIIIIII ]AII][и!^/W#+D+1S++N +01#"55##".54>32354332".546332!546332#4.#"32>3!26555))?Q-0U@%%@U0*PA+),, ,/Rs}EI+u(+:3+hb+hи/hи/иh!u0и:?и?/AI&I6IFIVIfIvIIIIIII ]AII]Abb]Ab)b9bIbYbibybbbbbbb ]Vbh93|иh/x#+D+1t++N +01#"55##".54>32354332".5463323546332#4.#"32>&&'''&766'462%3326555))?Q-0U@%%@U0*PA+)'' ,Q/323543324.#"32>'.'''&67>76325)(>Q.1V?$$?V1,QA)).="#;-,<$"=.82c 7cSC?Rb8CpS0 0 ,H4"=U36V< 5M18*?**?*'?,,? Lb ".;D$$E<0 % 32354332%4.#"32>&&''&&'''&6766'4327>'46325))?S.1V@%%@V1+Q@,)/="#<-,=$"=/}?YM11NYA b[ 0 "- .#/ ZU  +G2 ;Q24S92G.'<((<'%;**;![56Y U34T#$ 0e  @9/ :HR)   P' xpK"6J^A+U-+!K+Kи/Kи/AA&A6AFAVAfAvAAAAAAA ]AAA]AU&U6UFUVUfUvUUUUUUU ]AUU]!`/Z(+<++2P+F +01%#"55##".54>32354332#".54>324.#"32>4.#"32>5))?Q.1V@%%@V1+RA+).Oi323543324.#"32>'&&'''&76676&#!"5543!25))?Q.1V@%%@V1+RA+)/="#<-,=$"=/-Q@@= @LDT Vv P*F1 ;Q24S93I/'<((<'%;**;w-.  " 1 7  c< # 2xpC"6T߸U//Uи/и!-A-&-6-F-V-f-v------- ]A--]8иEиE/!HиNи!VK//(+8O++E<+2 +01#"55##".54>32354332%4.#"32>!54&#!"&55463!2##"55!"5545))?Q-0U@%%@U0*PA+)/#+H+5.++R +>+и.;и>Wи.Y01#"55##".54>32354332!"&5546335#"&55463!2##324.#"32>35#5))?Q.1V@%%@V1+RA+)- n Aj/="#<-,=$"=/K*F1 ;Q24S93I/| ! ! '<((<'%;**;Xtd"6B-+#++7<+иA-&-6-F-V-f-v------- ]A--]7D9/?/(+2 ++01##"5##".54>32343324.#"32>##"54332&q$:K*-L8 9L-+K:#q&$7%'5!!5%%7$&&XEgE"'MtNNsM%"FjH9.XF+,GX,,YH-,GZ~epd"2HT++ +IN+A+&+6+F+V+f+v+++++++ ]A++]I7иN=и=/IV:/Q/(+4A++.+012354332##"55##".54>4.#"32>!2##"554&#!"&5546%##"54332+"D9&|&&}'8D"$G8"#8G$2=CF:2$ /)  [&&1L43K19W<+[LM],?#)$De"4JVû++@9+#+ +KP+ии/A+&+6+F+V+f+v+++++++ ]A++]KXS/D5+(++0+012354332##"5##".54>4.#"32>!"&5543323!2'##"54332+"D9'|&&}'9C!$F8##9F$2#!"&55463!26554&#!"&55463!2#!"3!24&#"32>##"54332+"D9&|&&}'8D"$G8""8Gs /#-  #-O 8M:=CF:1$&&0J3H2I07U;;T6#*F*4$F*8 vNVYKK\,>e|d6FVb˻O2+:+ +W\+W и&и\BиB/AO&O6OFOVOfOvOOOOOOO ]AOO]Wd_/? +L+7+'+R-+01%463!2#!".52354332##"55##".54>"3!26554&#4.#"32>##"54332 , ,:%"D8'|&&}&9D"$G8"#8G\  $2=CF:2$&&x**(1K4%3L19W<+[LM],?e|d">LVbE+N(+ +W\+иN0и\2и2/W:AE&E6EFEVEfEvEEEEEEE ]AEE]\UиU/Wd_/Q#+B+1M++H+и/012354332##"55##".54>".546332!546332#4&#"32>3!26557##"54332+"E8&|&&}'8D"$G8""8GU,, ,M:=CF:1$\  E&&0J3F2I/7U;;T6jh&NVYKK\,>Lf few"4]i++ +^c+A+&+6+F+V+f+v+++++++ ]A++]I^9^kf/(++0+012354332##"55##".54>4.#"32>'.'''&67>7632##"54332+!D9&}&&}&9D"$G7##8G$2>Y9(@-^N'A.-A Lb ".;D$$E<0 % 4>32#".732>54.#"4.#"32>##"54332+"D9&|&&}'9C"$G8"#8G .SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#i$2=CF:2$&&1L5'3K09W<+[LM],?ptC"6=-+7#+!+A##]A#)#9#I#Y#i#y####### ]A-&-6-F-V-f-v------- ]A--]9#79:и!?//(+2 +;+:+01#"5##".54>323543324.#"32>735#5)&3@'B[77[B'?3&)(@.0?&&@0.@(G`6':]r8;s\8&4-\J..J\-+\K00K\*;9;xpC2HO#) +I++A]A)9IYiy ]A)&)6)F)V)f)v))))))) ]A))]7и=кKI9LкNI9Q/:/$+M+4A+L+.+01%#"55##".54>323543324.#"32>!2##"554&#!"&554635#5) f=1V@%%@V1>p )/="#<-,=$"=//) $ N z*. ;Q24S962'<((<'%;**;#)$A&%xpC2H^e_) +8=+T+Tи/Tи/=89/A]A)9IYiy ]A)&)6)F)V)f)v))))))) ]A))]MиM/_a_9Tbиb/d_9g/:/P/$+c+4A+b+.+4IиAW01%#"55##".54>323543324.#"32>32##"554&##"&5546!32##"554&##"&554635#5) f=1V@%%@V1>p )/="#<-,=$"=//$  B/$   ~*. ;Q24S962'<((<'%;**;#)$#)$F&%ua2HO) +>7+I++A]A)9IYiy ]A)&)6)F)V)f)v))))))) ]A))]KI9LкNI9Q/B3+$+M+.+L+01%#"55##".54>323543324.#"32>!"&5543323!235#5)!a:2V?%%?V2=k!)/="#<-,=$"=/G /)   y(-"?V57X> 52|*A++A*(@--@#)$n2*$zqXPdkI[ +I&+eQ++и.и&6и6/@AQQ]AQ)Q9QIQYQiQyQQQQQQQ ]A[&[6[F[V[f[v[[[[[[[ ]A[[]gQe9hкjQe9m/M"+V++D+i+;2+h+`+01#"55##".54>32354332#!"&55463!26554&#!"&55463!2#!"3!24.#"32>735#5) `90S>$$>S0=l )  /#-+ #-o -; !:+*:# ;-FĽ(c'-!:O/2P972q6U#*K*9$K*= $9''9%#9)(:"&$zqD2Fӻ= +p]+3++и/и/A33]A3)393I3Y3i3y3333333 ]39/(и(/A=&=6=F=V=f=v======= ]A==]=UиU/=eиe/Ufиf/=x39и/39/,+8+Zs++ja++B+j"и"/,LиL/,Oи{и{/и/a01#"55##".54>3235433246332##".54.#"32>##".55463326554&##"&5546332##"332677>76"3326554&#35#5) `90S>$$>S0=l ),,!-; !:+*:# ;-6 %K!, !,D" w t Ľ#h'-!:O/2P972q1}**g$9''9%#9)(: N*9$I*<     j&$ziC:NE +_+zg+;+6/+/и//и/6A;;]A;);9;I;Y;i;y;;;;;;; ]$;9$/,AE&E6EFEVEfEvEEEEEEE ]AEE]_oиo/;9/и/;9/и6//Y+@+d}++-+tk++J+t(и(/t2и2/YVиV/и/01#"55##".54>32354332".5463323546332#4.#"32>##".55463326554&##"&5546332##"3326766767235#3326555) `90S>$$>S0=l )'' ,-; !:+*:# ;-   $J!,~ !,?$  AĽ o n'-!:O/2P972q+L1tr&$9''9%#9)(: L*7$G*:  o&$t tx|C2BV^)M +6+WC++и(и>ACC]AC)C9CICYCiCyCCCCCCC ]AM&M6MFMVMfMvMMMMMMM ]AMM]YCW9Zи`/;,+H+[+"3+Z+R+01%#"55##".54>32354332463!2#!".57"3!26554&#4.#"32>735#5)!b<1V@%%@V1@q ) , ,]]  /="#<-,=$"=/Fx', ;Q24S985 |**   '<((<'%;**;$,$ x|C:NX`9E +P$+Y;++иP,и.и6A;;]A;);9;I;Y;i;y;;;;;;; ]AE&E6EFEVEfEvEEEEEEE ]AEE]Wк[;Y9\иb/S+@+-O+]+\+J+01#"55##".54>32354332".546332!546332#4.#"32>3!265535#5) f<0U@%%@U0?q ),, ,/32354332".5463323546332#4.#"32>&&'''&766'462%33265535#5) f<0U@%%@U0?q )'' ,Q/323543324.#"32>'.'''&67>763235#5) e=1V?$$?V1?o ).="#;-,<$"=.82c 7cSC?Rb8CpS0 0 ,0"=U36V< 86.*?**?*'?,,? Lb ".;D$$E<0 % KR9RPиP/gdиd/rp9gsиs/tкup9gx/$+t+b+s+.bMиM/tr01#"55##".54>32354332%4.#"32>&&''&&'''&6766'4327>'463235#5) b:1V@%%@V1>n!)/="#<-,=$"=/~?YM11NYA b[ 0 "- .#/ ZU  b&* ;Q24S942'<((<'%;**;![56Y U34T#$ 0e  @9/ :HR)   P' +(xpK2FZa= +Q)+[3++A33]A3)393I3Y3i3y3333333 ]A=&=6=F=V=f=v======= ]A==]GиG/AQ&Q6QFQVQfQvQQQQQQQ ]AQQ]]3[9^к`3[9c/V$+8+_+.L+^+B+.и/01%#"55##".54>32354332#".54>324.#"32>4.#"32>35#5) c;1V@%%@V1!#<--<$!>/$=P+0P9 9P0+P=$ ', ;Q24S930|+D//D+*C/.C'<((<'%;**;@..--,$zqOBV]AM +!.+WC++и5и5/!+[+38+Z+R+01#"55##".54>32354332!3!2#!"&55463!2#!"!24.#"32>735#5) `90S>$$>S0=l )"  , , 5 -; !:+*:# ;-FĽ&e'-!:O/2P972q4P **  O !  $9''9%#9)(:"&$x|>RV^CI +S(+W?+9T++A??]A?)?9?I?Y?i?y??????? ]AI&I6IFIVIfIvIIIIIII ]AII]Y?W9Zи`/:+D+[+1*+Z+N+:'и*7и:Sи*U01%#"55##".54>32354332!"&5546335#"&55463!2##324.#"32>35#35#5)!b<1V@%%@V1@q )- n Aj/="#<-,=$"=/Ky', ;Q24S985  ! ! '<((<'%;**;,$ zUb2FZho}= +Q)+i3+G+AGG]AG)G9GIGYGiGyGGGGGGG ]G9/иA33]A3)393I3Y3i3y3333333 ]A=&=6=F=V=f=v======= ]A==]AQ&Q6QFQVQfQvQQQQQQQ ]AQQ]k3i9lкn3i9/V$+8+m+.L+d[+l+yp+B+yи/01#"55##".54>32354332#".54>324.#"32>4.#"32>7!"&55463!235##"&55463325) `90S>$$>S0=l )6%=R--S>%%>R--R>%-; !:+*:# ;-*9 9**9 9* LĽ_ w'-!:O/2P972q"/  // .-$9''9%#9)(:   &$Xtd2>F)+?++38+A)&)6)F)V)f)v))))))) ]A))]A?&?6?F?V?f?v??????? ]A??]Bи3H5/;/$+. +C+B+01##"5##".54>323543324.#"32>##"5433235#&a;-L8 9L-?f&$7%'5!!5%%7$&& |xXEDB'MtNNsM%KM.XF+,GX,,YH-,GZ~A30e0FRZ#'+<5+S+ +GL+A'&'6'F'V'f'v''''''' ]A'']AS&S6SFSVSfSvSSSSSSS ]ASS]US9VкXS9G\O/@1+$+W+,+V+012354332##"55##".54>4.#"32>!"&5543323!2'##"5433235#+/_&&`0$F8##9F$2#!"&55463!26554&#!"&55463!2#!"3!24&#"32>##"5433235#+3a&&`3$G8""8Gs /#-  #-O 8M:=CF:1$&&26bIc527U;;T6#*F*4$F*8 vNVYKK\,>$|e|d2BR^e7K+6+_C+ +SX+иS(иX>и>/AK&K6KFKVKfKvKKKKKKK ]AKK]A_&_6_F_V_f_v_______ ]A__]aC_9bиSg[/;,+H+c+"3+b+N+012354332##"55##".54>463!2#!".57"3!26554&#4.#"32>##"5433235#+3a&&^0$G8"#8G , ,:]  $2=CF:2$&& 39p&m209W<+[LM],?(!e|d:HR^gMA+J$+_;+ +SX+иJ,иX.и./S6AA&A6AFAVAfAvAAAAAAA ]AAA]XQиQ/A_&_6_F_V_f_v_______ ]A__]cиSi[/M+>+-I+d+c+D+ и /012354332##"55##".54>".546332!546332#4&#"32>3!26557##"5433235#+4b&&a1$G8""8GU,, ,M:=CF:1$\  E&&38kF^417U;;T6jh&NVYKK\,>Lf f {ew0Yem'+f+ +Z_+A'&'6'F'V'f'v''''''' ]A'']EZ9Af&f6fFfVfffvfffffff ]Aff]jиZob/$+k+,+j+012354332##"55##".54>4.#"32>'.'''&67>7632##"5433235#+/]&&_0$G7##8G$2>Y9(@-^N'A.-A Lb ".;D$$E<0 % &&''&&'''&67>'432766'46324.#"32>##"5433235#+3a&&\/$G8"#8G?]/j5MXD 3I. 1 "- ;E/  3?!$2=CF:2$&& 38o%j0.9W<+[LM],?,!<&:,+# +6+A,&,6,F,V,f,v,,,,,,, ]A,,]A66]A6)696I6Y6i6y6666666 ]<$+'+1"+$ и"01%#!"&55463!5.54>32!2"32>54.  1eS4=_q34r]=4Rf2 F#UK33KU#"UK44KUA""6P8=U44U=8Q5T$>00>##>00>$32!2"32>54.!2##"554&#!"&5546  -bP4?]m./m]>5Qb. H RI33IR RJ33JR/)  0""x(>.1B&(A.+>(x*!!**!!*#)$<&:P,+# +6+A,&,6,F,V,f,v,,,,,,, ]A,,]A66]A6)696I6Y6i6y6666666 ]REX1/1>YJ;+'+$+$ и1"01#!"&55463!5.54>32!2"32>54.!"&5543323!2  /aP3=]m01n\<3Qc/ H!TI33IT!!SJ33IT/)  ""|+A.6E()D3/A+|-%%--%%-')$и>/HиH/A^&^6^F^V^f^v^^^^^^^ ]A^^]^PиP/nU*+Y+3L+C:+c+$+$ и"и"/01#!"&55463!5.54>32!2#!"&55463!26554&#!"&55463!2#!"3!2"32>54.  -aP3=\k/0k\<3O`. x /#-  #-Q RH22HR RI22IRo""K'?04A% %B40?&L#*J*0#@*= 3+##++##+32!2"32>54.##".55463326554&##"&5546332##"3326766767232##"54&##"&5546  -aP3=\k/0k\<3O`. G RH22HR RI22IR  'N!, !,C&  /$  e""U'?04A% %B40?&Vq+##++##+ L*7$G*<  =#)#32!246332##".5"32>54.##".55463326554&##"&5546332##"33267667672"3326554&#  -aP3=\k/0k\<3O`. c,, RH22HR RI22IR  'N!, !,D&  w z e""U'?04A% %B40?&V**$+##++##+ N*9$I*<     32!2"32>54.##".55463326554&##"&5546332##"3326767672&&'''&67>'432  -aP3=\k/0k\<3O`. G RH22HR RI22IR5   #H!, !,># 9WS8(A. -  -9 g""T&?04A% %B40?&To+##++##+ N*9$I*<  2]:@]! ;PjD  ,(I<+   <`&:|nK+fS+6+f и /Kn9/f#и/"и"/,A66]A6)696I6Y6i6y6666666 ]K[и[/69/A]A)9IYiy ]}#и/}+rE+'+++1+`W++$+$ и"и"/EBиB/OиO/iиi/01#!"&55463!5.54>32!2"32>54.##".55463326554&##"&5546332##"33267667672%#".54>327!"&55463!24&#"326#"&554332  -aP3=\k/0k\<3O`. G RH22HR RI22IR   )R!, !,G)  _*:$$:**:$$:* X X0--11--0  m""M'?04A% %B40?&Ni+##++##+ Q*<$L*?  *, !,, !+{& ' '&$$% $ <|&:N^ɻ@+# +0J+A@&@6@F@V@f@v@@@@@@@ ]A@@]@RиR/JZиZ/0`W4+;+*O+E"+$+$ и"01#!"&55463!5.54>32!2463!2#!".5"32>54."3!26554&#  -bP4?]m./m]>5Qb.  , ,>. RI33IR RJ33JR  /""z(>.1B&(A.+>(z***!!**!!* ~ ~ R+H4и4/R6и6/HWиW/R_и_/>b['+C+5W+M"+$+$ и"01#!"&55463!5.54>32!2".546332!546332#"32>54.3!2655  -bP4?]m./m]>5Qb. e,, , RI33IR RJ33JR  A"#i(>.1B&(A.+>(i3ba&P*!!**!!*`\ \32!2"32>54.'&&'''&67>7632  -bP4?]m./m]>5Qb. H RI33IR RJ33JR -p t+*nCqR1 0 )""}(>.1B&(A.+>(}*!!**!!* DZ " mEGr % 9HP' 32!2#".54>32"32>54.4.#"32>  -bP4?]m./m]>5Qb. @^m-EE@1@^m,-m_? RI33IR RJ33JR6LQQK55KQRK6.""{(>.1B&(A.+>({3@$  %2"2?$ $@*!!**!!*G!''! ((32!2'.'&''&7>76&#!"5543!2"32>54.#!"5543!2  -aP3=\k/0k\<3O`. H- HJI!  #X^\'Kc   Oq) J %0 RH22HR RI22IRr t""G&?04A% %B40?&G'  $  3 )-/ ! #+##++##+%?t5I_@!+1++6+^M+A66]A6)696I6Y6i6y6666666 ]A@&@6@F@V@f@v@@@@@@@ ]A@@]^Sи^aP/J/&;+1+U\+ и /1и/1и/01%6"'"&554632675.54>32>4.#"32>#"5433232##"NPP$LRN ;BE-K7$?W20WB'5J,!IHEt/>!#<-,=$!>/))  #  "%:K+4R89R3*I9& m&9'':%$9)*9~y"EIj+?Uk#6"++,+EJ+A,,]A,),9,I,Y,i,y,,,,,,, ]A6&666F6V6f6v6666666 ]A66]6RиR/JYиE_иEiиEmG/\/'1+AN+ah+;+и/016676##"&5546335.54>324.#"32>!2##"554&#!"&5546%#"5433232##K3G-L=  Qd +F1 ;R30S>#D,: 9**9!:,/)  B)) C :.\" #]-:!(@--A&*)*)#)$6"I+?Uk-6"+aZ++,+TC+A,,]A,),9,I,Y,i,y,,,,,,, ]A6&666F6V6f6v6666666 ]A66]TIиTmF/eV+'1++;S+Sи/и/Sи/;JиJ/016676##"&5546335.54>324.#"32>#"5433232##!"&5543323!2K2H-L=  Qd ,F1;S31S=#D,: !9**9" :, )) /)  4"<0 s" #t /=")C//C(++,+D"$)$Iq)[o]f"++\+s+Af&f6fFfVfffvfffffff ]Aff]f1и1/s9и9/fAиA/1BиB/KиK/fTA\\]A\)\9\I\Y\i\y\\\\\\\ ]yиv/X-+%a+6O+F=+{+k+и/016676##"&5546335.54632#!"&55463!26554&#!"&55463!2#!"3!24.#"32>#"5433232##K2H-L=  Qd ,F1yg1S=# /#- #-[ ,: !9**9" :, )) X8-T" #U,8MZ+>"*>*."<*1 ( '(("I|?Ocy3Z6+0+P+ K+AZ&Z6ZFZVZfZvZZZZZZZ ]AZZ]Zи/ZCAPP]AP)P9PIPYPiPyPPPPPPP ]Kgи mи wи {j/H +;U+@+ov+_'+'0и/017463!2#!".56676##"&5546335.54>32"3!26554&#4.#"32>#"5433232## , ,Iz3G-L=  Qd +F1 ;R30S>#  ,: 9**9!:, )) `** :.a" #b-:!(@--A   *)*)"I|+G[e{KR"++H+C<+AR&R6RFRVRfRvRRRRRRR ]ARR]R1и1/R]9AHH]AH)H9HIHYHiHyHHHHHHH ]<dи<iиCoиCyиC}l/`,+'M+:\+qx+W+и/fиf/016676##"&5546335.54>32".546332!546332#4.#"32>3!26557#"5433232##K2H-L=  Qd ,E2 :S30T=#,, ,-:!9**9":-  7)) P 9-`" #a-9 '?,->`c&)))(W W"Iv+?bx6"++,+wf+A,,]A,),9,I,Y,i,y,,,,,,, ]A6&666F6V6f6v6666666 ]A66]Q,9wlиwzi/'1++;+и/и;mиm/u016676##"&5546335.54>324.#"32>'&&'''&7>7432#"5433232##K2H-L=  Qd ,F1;S31S=#D,: !9**9" :,2 &o v,*pHsQ+ 0 )) ?"<0 k" #l /=")C//C(++,+  :_"  iABk" %9DJ% P"Ip+dxo"+ME+e+|+AM&M6MFMVMfMvMMMMMMM ]AMM]EM9//"96EM9MJиJ/Aee]Ae)e9eIeYeieyeeeeeee ]Ao&o6oFoVofovooooooo ]Aoo]и/'j+Y++и/YGиG/jt016676##"&5546335.54>32&&''&'''&67>'432766'46324.#"32>#"5433232##K3G-L=  Qd +F1 ;R30S>#?WL7o+XB 6I, 0 D; ?B/ VP ,: 9**9!:, )) H :.a" #b-:!(@--A P/-J" >V,K$$ 8@K.   3d){S  !Bf"  *)*) "Iq+?Sg}g^"+J6++T+|k+k,AJ&J6JFJVJfJvJJJJJJJ ]AJJ]ATT]AT)T9TITYTiTyTTTTTTT ]A^&^6^F^V^f^v^^^^^^^ ]A^^]|qи|n/O1+'Y+;E+sz+c+и/016676##"&5546335.54>32#".54>324.#"32>4.#"32>#"5433232##K3G-L=  Qd +F1 ;R30S>#3Tl9>mQ//Qm>9lT3G&@S-2S<""+ и/и/ и/016676"'"&5546375.54>3234332##"5###"543324.#"32>-?&7^8":9=$8x; +d6(C0 8L-+M:#&&&&)55'Q?5)'D6$  # "#7G)1N56MlPG#5$$5#BN&5Jjd/KasEl&+ +b+@E+QV+@0иV1и1/Q7иV=и=/Abb]Ab)b9bIbYbibybbbbbbb ]Al&l6lFlVlflvlllllll ]All]Qu4/S/+g+MZ+1>+o+и/и/ 016676#'"&55463275.54>32354332##"55###"54332!2##"554&#!"&55464.#"32>,='5f*9=>:8 -c0(@-5K.+L8 &&&&X/)  '42&N<4'C7-Y # "Y-: '?,->c#)$( (3:)J}0L^tZW'+jc+M+AF+83+!cj9!/A1и3>AMM]AM)M9MIMYMiMyMMMMMMM ]AW&W6WFWVWfWvWWWWWWW ]AWW]8v5/EX1/1>Yn_+,R+Z+и/и/и/!и1?016676#'"&55463275.54>3234332##"5###"543324.#"32>!"&5543323!2-?'6i*7>:~9 -`/&?-5K-+L8 &&&&'42&N<4'/)  )!<. i # "j.<")B//Bx&s+h,+6?+v$)$J|dCSoz:+G+p+di+[V+[ и /4G94/VOиO/dTиVaApp]Ap)p9pIpYpipyppppppp ]Az&z6zFzVzfzvzzzzzzz ]Azz][X/EX}/}>YL +?u+D+Ub+}'A'']A'('8'H'X'h'x''''''' ]*й1и/14017463!2#!".56676#'"&55463275.54>32"3!26554&#354332##"55###"543324.#"32> , ,1 ,='5f*9=>:8 -c0(@-5K.+L8   c&&&&'42&N<4'd**7-Y # "Y-: '?,->   ( (3:)Jv0Lo5z'+!+p+AF+83+A&6FVfv ]A]A1и3>к^'89Az&z6zFzVzfzvzzzzzzz ]Azz]85/,u+2?+}+и/и/и/!016676#'"&55463275.54>3234332##"5###"54332'&&'''&7>74324.#"32>-?'6i*7>:8 -`/&?-5K-+L8 &&&&j &o v,*pHsQ+ 0 w'42&N<4'-!<. ] # "^.<")B//Bk   :_"  iABk" %9DJ% o,+6?+JumCWs~:+D+t+hm+_Z+AD&D6DFDVDfDvDDDDDDD ]ADD]4D94/ZNиN/hXиZeAtt]At)t9tItYtityttttttt ]A~&~6~F~V~f~v~~~~~~~ ]A~~]_\/EX/>YI+?y+S+Yf+'A'']A'('8'H'X'h'x''''''' ]*й1и/14и*jиk0174>32#".6676#'"&55463275.54>3232>54.#"354332##"55###"543324.#"32>.TtF@sW33Ws@FtT.,='5f*9=>:8 -c0(@-5K.+L8 %BX3.YE**EY.3XB%g&&&&'42&N<4''*A--B*+B--BL7-Y # "Y-: '?,->,,--( (3:)ItC5IU@,+&+6+TM+A66]A6)696I6Y6i6y6666666 ]A@&@6@F@V@f@v@@@@@@@ ]A@@]TWJ/P/1;+&+&и/и/&!и!/01>76"'"&554632675.54>324.#"32>#"54332]6J,#OOJ!NST(QUP ;BE-K6$?W20WB'F/>!#<-,=$!>/))*J9&  #  "&9K+4R89R2&9'':%$9)*9~MjD-AWc%8$++.+b[+A..]A.).9.I.Y.i.y....... ]A8&868F8V8f8v8888888 ]A88]bFиF/[LиL/8TиT/beI/^/)3+CP+=+и/и/016676##"&554633275.54>324.#"32>!2##"554&#!"&5546%#"54332d2F,O6  Xg ,H2 ;R30S>#D,: 9**9!:,/)  A))C :-] # #].;!(@--A&*)*)#)$7 Mb+?Ua6"+KD++,+`Y+A,,]A,),9,I,Y,i,y,,,,,,, ]A6&666F6V6f6v6666666 ]A66]`c\/O@+'1+;+и/016676##"&55463!5.54>324.#"32>!"&5543323!2'#"54332d2H-M9  Xg ,F2;S31S=#D,: !9**9" :,3/)  #))/"<0 m " #o /=")C//C(++,+p$)$bMqY)[o{3f"+T1++\+zs+s9и9/1AиA/zKиK/A\\]A\)\9\I\Y\i\y\\\\\\\ ]Af&f6fFfVfffvfffffff ]Aff]z}v/X-+%a+6O+F=+k+и/016676##"&55463!5.54632#!"&55463!26554&#!"&55463!2#!"3!24.#"32>#"54332d2H-M9  Xg ,F2yg1S=# /#- #-[ ,: !9**9" :,))X8,T " #V,8MZ+>"*>*."<*1 ( '((M|DAQeq)\8+E+2+R+pi+p и /iMиM/ARR]AR)R9RIRYRiRyRRRRRRR ]A\&\6\F\V\f\v\\\\\\\ ]A\\]psl/J +=W+B+a'+'0и/02и2/017463!2#!".56676##"&554633275.54>32"3!26554&#4.#"32>#"54332 , ,Io2F,O6  Xg ,H2 ;R30S>#  ,: 9**9!:,))d** :-` # #`.;!(@--A   *)*)M|C+G[eq-R"+]1++H+C<+]9AHH]AH)H9HIHYHiHyHHHHHHH ]AR&R6RFRVRfRvRRRRRRR ]ARR]<dи<iиCoиCsl/`,+'M+:\+W+и/f016676##"&55463!5.54>32".546332!546332#4.#"32>3!26557#"54332d2G-M9  Xg ,F2 :S30T=#,, ,-:!9**9":-  7))O 9-^ " #`-9 '?,->`c&)))(W WMv+?bn6"++,+mf+A,,]A,),9,I,Y,i,y,,,,,,, ]A6&666F6V6f6v6666666 ]A66]Q"m9mpi/'1+;+и/016676##"&55463!5.54>324.#"32>'&&'''&7>7432#"54332d2H-M9  Xg ,F2;S31S=#D,: !9**9" :, &o v,*pHsQ+ 0 ))?"<0 ` " #b /=")C//C(++,+  :_"  iABk" %9DJ% IMqF+?Sgsc^"+J6++T+rk+r,и,/k@и@/AJ&J6JFJVJfJvJJJJJJJ ]AJJ]ATT]AT)T9TITYTiTyTTTTTTT ]A^&^6^F^V^f^v^^^^^^^ ]A^^]run/O1+'Y+;E+c+и/016676##"&55463!5.54>32#".54>324.#"32>4.#"32>#"54332d2H,M9  Xg ,F1 ;R30S>#3Tl9>mQ//Qm>9lT3G&@S-2S<""32"32>54.32#!"&554633546332!5463323q_==_q34r]==]r4#UK33KU#!VK44KV  *)*/3Q;;Q33Q;;Q3k !;..:! !:..;! k""  /#+8E+-M+ +  и Q01#!"&5546335&&54>3232"32>54.!2##"554&#!"&5546"&'!5  0A?]m./m]>A1 H RI33IR RJ33JR/)  8C!!B/""B31B&(A.1B*!!**!!*#)$<"6LUN(+Q +R+2+A(&(6(F(V(f(v((((((( ]A((]A22]A2)292I2Y2i2y2222222 ]WEX-/->YF7+#+ +  и-MAMM]AM(M8MHMXMhMxMMMMMMM ] Q01#!"&5546335&&54>3232"32>54.!"&5543323!2"&'!5  1@=]m01n\3232#!"&55463!26554&#!"&55463!2#!"3!2"32>54."&'!5  /?=\k/0k\<>/ x /#-  #-Q RH22HR RI22IRB !Bm""lC54A% %B46Bl#*M*3#C*@ 3+##++##+XX<|"6JZcӻ<+_ +`+,F+A<&<6<F<V<f<v<<<<<<< ]A<<]<NиN/FVиV/,eS0+7+&K+A[+ +  и _01#!"&5546335&&54>3232463!2#!".5"32>54."3!26554&#"&'!5  0A?]m./m]>A1  , ,>. RI33IR RJ33JR  C!!B2""B31B&(A.1B***!!**!!* ~ ~ KR\eD)+a +b+:N+D0и0/N2и2/DSиS/N[и[/:gW#+?+1S+I]+ +  и a01#!"&5546335&&54>3232".546332!546332#"32>54.3!2655"&'!5  0A?]m./m]>A1 e,, , RI33IR RJ33JR  C!!B@"#B31B&(A.1B4ba&Q*!!**!!*_\ \wx3232"32>54.'&&'''&67>7632'"&'!5  0A?]m./m]>A1 H RI33IR RJ33JR -p t+*nCqR1 0 #C!!B&""B31B&(A.1B*!!**!!* DZ " mEGr % 9HP' и>/AMM]AM)M9MIMYMiMyMMMMMMM ]MHиH/#k\(+9+4R+Ca+ +  и e01#!"&5546335&&54>3232#".54>32"32>54.4.#"32>"&'!5  0A?]m./m]>A1 @^m-EE@1@^m,-m_? RI33IR RJ33JR6LQQK55KQRK6C!!B(""B31B&(A.1B3@$  %2"2?$ $@*!!**!!*G!''! ((++#+A&6FVfv ]A]A##]A#)#9#I#Y#i#y####### ]C(/ +4-++-<01".54>32"32>54."&5!"&55463!2#!#3q_==_q34r]==]r4#UK33KU#"UK44KU8| R wu/L66L//L66L/N 5**4 4**5 G"" +)6+H++)и&01".54>32!5!"&55463!2#!32##"554&#!"&5546"32>54.0m]==]m01n\<=\m { R x/)  4!SH22HS!!RI22IS&B43B%(@00A(y""y#)$!*""**""*<'AWL+>++#+Lи/A##]A#)#9#I#Y#i#y####### ]FиF/YQB+ ++4-+-<01".54>32"32>54."&55!"&55463!2#!#!"&5543323!20m]==]m01n\<=\m1!SH22HS!!RI22IS/s R !/)  (F66E(*D23D*' -%%. .%%- o "" ')$32"32>54. x#-Q /#-  { R G/l\<<\l/1l[;<[l0!RH11HR! RH22HRx"d@*3 ##*@*0#d"C'B54B&&C45B',##++##,32"32>54.##".55463326554&##"&5546332##"33267667672"&55!"&55463!2#!#32##"54&##"&5546/l\<<\l/1l[;<[l0!RH11HR! RH22HR  )Q!, !,F(  *{ R x/$  'B54B&&C45B',##++##, D*/$?*4  3 X""X #) #3246332##".5"32>54.##".55463326554&##"&5546332##"33267667672#!##"&55!"&55463!2"3326554&#/l\<<\l/1l[;<[l,,!RH11HR! RH22HR   'N!, !,D&   x+{ R  z 'B54B&&C45B'**#,##++##, D*/$?*2  "b b"   <|';O_A-++7K+AA&A6AFAVAfAvAAAAAAA ]AAA]AKK]AK)K9KIKYKiKyKKKKKKK ]ASиS/K[и[/7aX!+2<+P+F(+ +и01746335!"&55463!2#!32#!".5".54>32"32>54."3!26554&# ,{ R x ,>.0m]==]m01n\<=\m1!SH22HS!!RI22IS  `*z""z*&B43B%(@00A(*""**""*   32".5546332!546332#"32>54."&55!"&55463!2#!#3!26550m]==]m01n\<=\m,, ,!SH22HS!!RI22IS7{ R x  &B33A%'A/0@(]\&P*""**""* y""y {V V32'&&'''&67>75!"&55463!2#!"32>54.0m]==]m01n\<=\m-p t+(q?lR3{ R x'!SH22HS!!RI22IS(D65E')C22D)Db " tGJw  % 4CK&i""pA-$$--$$-75!"&55463!2#!".54>32"32>54.4.#"32>*@_m,EE@26Ra,{ R x,bR50m]==]m01n\<=\m1!SH22HS!!RI22IS6MQQK66KQQM6 3?#  $2"-<$u""u$<z&B43B%(@00A(*""**""*D!&&! ''ItC';c2 +FM+(+ + A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]e//-+!+_N+7+NEиE/NSиS/01#".54>324332##"55#"55434.#"32>6##"&5'"&554632>7>^$@X43V?$$?V30WB')) />"$<-,=%">/  @K+ GD< QUO#RRJ"'E45E'+G32G5~ " 0!!00##0"   6" TjD';Qy) +vU++:++A]A)9IYiy ]A&6FVfv ]A]+3и:@и@/+FиF/:{C/6/+=J+gy+#+3,+guVиV/01#".54>324.#"32>#"55#"554334332!2##"554&#!"&55467"&55"'"&554632>7>76#W$?S.-R=$$=R-+R@'B.:8-,9:. ) )/)  oCA< LQL NOL  AKY$<,+=$(>**>'( (( (u " 1#)$# " "   L['9aw+2 +mf+^=+(+ + A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]y/jc+-+Oa+!+5+j:и:/O]>и>/cq01#".54>324332##"55#"55434.#"32>"&55'"&554632>7>76#!"&5543323!2W"=T10R<""+x+F+af+f#и#/+и+/a5и5/AFF]AF)F9FIFYFiFyFFFFFFF ]AP&P6PFPVPfPvPPPPPPP ]APP]fZиa]/B+K+ 9+0'+U+{+Zg+wиw/и/и/01#".54>32#!"&55463!26554&#!"&55463!2#!"3!24.#"32>4332##"55#"554376##"&55'"&554632>7>W$=S0.R=##=R.,R@& /#- #-X /:9-,9:/)) x ?H+EC> OTO#NNJh"9*)9#':((;#*-*$-* % %& % " u"   "T|D'7K_9B+++c+8+SX+S и /X3и3/A88]A8)898I8Y8i8y8888888 ]AB&B6BFBVBfBvBBBBBBB ]ABB]XLиSO/0 +#=+(+u+G+LY+udиd/017463!2#!".5#".54>32"3!26554&#4.#"32>4332##"55#"5543"&55"'"&554632>7>76# , ,Dg$?S.-R=$$=R-+R@'  .:8-,9:.)) CA< LQL NOL  AK8*u*$<,+=$(>**> W W ( (( (% " z " "   L}C/CWYN&++bi+D++ и0и6и324332##"55#"55434.#"32>6##"&55'"&554632>7>3!2655;,, ,$=S0.R=##=R.,R@&)) /:9-,9:/ ?H+EC> OTO#NNJ  QO&"9*)9#':((; " +% %& %"   "mF FToL`tCW +x+M+hm+ h9x91и1/AMM]AM)M9MIMYMiMyMMMMMMM ]AW&W6WFWVWfWvWWWWWWW ]AWW]maиhd/R+>+an+R\ܹ>,и,/tܸyиy/01#".54>32&&''&&'''&6766'432766'46324.#"32>4332##"55#"5543"&55"'"&554632>7>76#W$?S.-R=$$=R-+R@'/>^N68L5eY 0 C6=D/ 0>$ .:8-,9:.)) B@: LQL NOL  BLY$<,+=$(>**>L3+J H+W? " +oJ  7['k@   81)   ( (( (& " d r" "  w TvL';OcZ+(+g+P+CH+A(&(6(F(V(f(v((((((( ]A((]H2и2/H32#".#".54>3232>54.#"%4332##"55#"55434.#"32>"&55"'"&554632>7>76#2Vp>8oX77Xo8>pV2w$?S.-R=$$=R-+R@',ET)$TH00HT$)TE,)) .:8-,9:.CA< LQL NOL  AK '8%$8((9%%9t$<,+=$(>**>""# #8 " 7( (( (t " "   4td'3Eg1> +PW+4++(-+A44]A4)494I4Y4i4y4444444 ]4и/!A>&>6>F>V>f>v>>>>>>> ]A>>](i0/*/9+!+eX+A+XOиO/X[и[/ebиb/01#".54>32##"55#"554334332##"543324.#"32>6##"&5'"&5546367668M-,K77K,*K:"& &&&X'42&M=4'd  .h7+9o+ :~9?u)E43F).G11Hg " W~ 0! 132!2##"554&#!"&5546%##"55#"5543343326##"&55'"&554636766'4.#"32>##"543323I-,G22G,)H5/)  & & 0c2+s[ 9n:Bs,%0/#I80%&&Q8**7 #9((9#)$I# "    " # #-2#5Bp'=I[|KT +3,+J++>C+!AJJ]AJ)J9JIJYJiJyJJJJJJJ ]AT&T6TFTVTfTvTTTTTTT ]ATT]l,39l/e>~F/0)+O+zi+!+W+)7zmdиd/mpиp/zwиw/01#".54>32##"55#"554334332!"&5543323!2'##"543324.#"32>6##"&55'"&5546367663I-,G22G,)H5& &/)  &&W$//"H7/$d/b2+9i. 9o9Bs:!;,,;!%=*+< ] " $)$& &07&   " Dqy1EYz<+*+2{+FK++и/и/!A2&262F2V2f2v2222222 ]A22]KSкj*9j/cA&6FVfv ]A]/.+A+ %++7+xg+SL+AVиV/xkbиb/knиn/xuиu/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>32##"55#"5543343326##"&55'"&554636766'4.#"32>##"54332y /#- #-9 3I-,G22G,)H5& & .a0+;j/ 9n:Bs,%0/#I80%&&#*9*#0*, 5''4!6&%6 "    "    ).  D|d'7Kk}av+++l+8=+~+~ и3и3/=Eк\+9\/UAll]Al)l9lIlYlilylllllll ]Av&v6vFvVvfvvvvvvvvv ]Avv]~/0 +#q+(+iY+y+E>+i]TиT/]_и_/ifиf/017463!2#!".5#".54>32"3!26554&#'##"55#"5543343326##"&55'"&554636766'4.#"32>##"54332 , ,' 3I-,G22G,)H5  T& & ._1+s[ 9n:Bm&%0/#I80%&&F**8**7 #9((9 e e  "    " # #-2#1D|d/9Mny&+1+o+:?++1 ии/и8и8/?Gк^19^/WAoo]Ao)o9oIoYoioyooooooo ]Ay&y6yFyVyfyvyyyyyyy ]Ayy]/4++t+0+|!+l[+G@++JиJ/l_VиV/_bиb/liиi/01".5546332!546332##".54>323!2655'##"55#"5543343326##"&55'"&554636766'4.#"32>##"54332>,, ,3I-,G22G,)H5  l& & ._0+:h- 9n:Bm&%0/#I80%&&PO&6))6"8('8L L "    " " "+1 "Dvm';Oo{û+(+|+<A+pu+A(&(6(F(V(f(v((((((( ]A((]u2и2/AIк`(9`/YA||]A|)|9|I|Y|i|y||||||| ]A&6FVfv ]A]px/-+#+7+ms++IB+maXиX/\и\/s]и]/acиc/mjиj/rиr/0174>32#".#".54>3232>54.#"%##"55#"5543343326##"&55'"&554636766##"543324.#"32>6Zu>9s]::]s9>uZ63I-,G22G,)H5/HX)$XK33KX$)XH/& & 0c2+s[ 9n:Bs{&&Y%0/#I80% )9##9**:##:n8**7 #9((9##$$# "    " K# #-2#ItC'3[  +>E++2++A]A)9IYiy ]A&6FVfv ]A]2]./(/+WF+#+F=и=/FKиK/WRиR/01#".54>324.#"32>#"543326##"&5'"&554632>7>h$@X43V?$$?V30WB'F/>"$<-,=%">/))  AH+ JG@ QUOMSQ"'E45E'+G32G*0!!00##0m~"  '" QjC'=dp +GN++-2+A]A)9IYiy ]A&6FVfv ]A]2hи-nи-r//k/+)6+`J+#+`OFиF/OTиT/01#".54>324.#"32>!2##"554&#!"&55466##"&55'"&554632>7>#"54332a$?S.-R=$$=R-+R@'B.:8-,9:./)   ?F+ HF= MSN!NPL))Y$<,+=$(>**>'( (( (F#)$   ":Q[%1Gn% +=6+QX++0)+A]A)9IYiy ]A&6FVfv ]A]0p,/:3++jU+!+3AjYPиP/:TиT/Y^и^/01#".54>324.#"32>#"54332!"&5543323!26##"&55'"&554632>7>a"=T10R<""Y.+AK++U7+|f+% |kbиb/kpиp/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>324.#"32>6##"&55'"&554632>7>#"54332W /#- #-X $=S0.R=##=R.,R@&B/:9-,9:/ ?F+ HF= MSN!NPL))#*4*"$4*& "9*)9#':((;%% %& %  j e"Q|D'7Kr~3B+++U\+8+}v+} и /v3и3/A88]A8)898I8Y8i8y8888888 ]AB&B6BFBVBfBvBBBBBBB ]ABB]}y/0 +#=+(+nX+G+n]TиT/]bиb/017463!2#!".5#".54>32"3!26554&#4.#"32>6##"&55'"&554632>7>#"54332 , ,Dq$?S.-R=$$=R-+R@'  .:8-,9:. ?F+ HF= MSN!NPL))H**$<,+=$(>**> g g ( (( (  } x".Q}C/Cjt;:&+l+MT+0++l A00]A0)090I0Y0i0y0000000 ]A:&:6:F:V:f:v::::::: ]A::]sиxи~и{/o++5+k+?!+fP+fULиL/UZиZ/01".5546332!546332##".54>324.#"32>6##"&55'"&554632>7>3!26557#"54332;,, ,$=S0.R=##=R.,R@&B/:9-,9:/ ?F+ HF= MSN!NPL  7))YW&"9*)9#':((;%% %& %   z"|N NQv6Jq}A +T[+7+|u+A77]A7)797I7Y7i7y7777777 ]%79AA&A6AFAVAfAvAAAAAAA ]AAA]|x/<+mW+F+m\SиS/\aиa/01#".54>32'&&'''&7>74324.#"32>6##"&55'"&554632>7>#"54332a$?S.-R=$$=R-+R@' &o v,*pHsQ+ 0 3.:8-,9:. BJ+EA9 MSN!NPL))J$<,+=$(>**>?  :_"  iABk" %9DJ% ( (( (   "\QvL';Ov?F+sS+<+z+AF&F6FFFVFfFvFFFFFFF ]AFF]Fи/F(z2и2/A<<]A<)<9<I<Y<i<y<<<<<<< ]}/-+#A+7+ex+K+xPиP/erTиT/wиw/0174>32#".#".54>3232>54.#"4.#"32>"&55'"&554632>7>76##"543322Vp>8oX77Xo8>pV2$?S.-R=$$=R-+R@',ET)$TH00HT$)TE,.:8-,9:. FD< MSN!NPL @H)) '8%$8((9%%9t$<,+=$(>**>""# #5( (( (p v"  z IE+#+A&6FVfv ]A]A##]A#)#9#I#Y#i#y####### ]M(/A/ +4-++-<и-F01".54>32"32>54."&5#"&55463!2####"&5##3q_==_q34r]==]r4#UK33KU#"UK44KU R +u/L66L//L66L/N 5**4 4**5 D"" D +)6+H++)и&и)RиT01".54>3235#"&55463!2##32##"554&#!"&5546"32>54.35#0m]==]m01n\<=\m{ R //)  =!SH22HS!!RI22IS&B43B%(@00A(""#)$.*""**""*ғ<'KaV+H++>E+#+A##]A#)#9#I#Y#i#y####### ]PиP/c[L+ ++4-+-<и-F01".54>32"32>54."&55!"&55463!2####"&55##!"&5543323!20m]==]m01n\<=\m1!SH22HS!!RI22IS R +/)  (F66E(*D23D*' -%%. .%%- ~ ""  ')$32"32>54.35#7 /#- e R &#-K  /l\<<\l/1l[;<[l0!RH11HR! RH22HRz#*>*.#h""h>*1 'B54B&&C45B',##++##,h<|';O_c5A-+`+a+7K+-и/AA&A6AFAVAfAvAAAAAAA ]AAA]AKK]AK)K9KIKYKiKyKKKKKKK ]ASиS/K[и[/7eX!+2<+P+F(+ +ии`иb01746335#"&55463!2##32#!".5".54>32"32>54."3!26554&#%35# ,& R * ,810m]==]m01n\<=\m1!SH22HS!!RI22IS  ]*{""{*&B43B%(@00A(*""**""* | | <{32".5546332!546332#"32>54."&55#"&55463!2####"&55##3!26550m]==]m01n\<=\m,, ,!SH22HS!!RI22IS R +g  &B33A%'A/0@(]\&P*""**""* y""y yy V Vk+#+A&6FVfv ]A]A##]A#)#9#I#Y#i#y####### ]>E_k>9qEX/>Y +4(+A]A(8HXhx ]4-<и(Aи-F01".54>32"32>54."&55#"&55463!2####"&55!#7'&&'''&676674320m]==]m01n\<=\m1!SH22HS!!RI22IS R + &m t*&y . (D65E')C22D)"-$$--$$- ""  -  :X !fA?h! % )K 32"32>54.4.#"32>'25#66*@_m,EE@2C2 R , 0m]==]m01n\<=\m1!SH22HS!!RI22IS6MQQK66KQQM6@ !@ 3?#  $2"3@""  *&B43B%(@00A(*""**""*D!&&! ''32'.'&''&67>76&#!"5543!2"32>54."&55#"&55463!2####"&55!#%##"554332/l\<<\l/1l[;<[lD*;< #SWW'  Hx,J (/!RH11HR! RH22HR R +%'B54B&&C45B'e# $  1"   '+ # !,##++##, h""h hh <'7ݸ8/#/8и/A##]A#)#9#I#Y#i#y####### ]#A&6FVfv ]A]95,+ ++01".54>32"32>54.#!"&55463!25q^<<^q56q]<<]q6$VJ22JV$#UK33KU  R 7U<%%>//?%""32"32>54.  R /)  62n[<<[n23n[;;\m3#SH00HS#"SH11HS@""#)$(E33C)*C/0C*# ,! , , !, <#7M͸N/3/Nи/A33]A3)393I3Y3i3y3333333 ]3B)и)/8и8/B9и9/GиG/OG8+$+.+ +01#!"&55463!2%".54>32"32>54.!"&5543323!2  R H0m]==]m01n\<<\n1 RH22HR RH22HR/)  /""e*E36E()D33E*( .%%. .%%. ')$32"32>54.#!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!2/l\<<\l/1l[;<[l0 QH11HQ QH12HQ  R x /#-  #-Q 'B54B&&C45B',##++##,"" #*H*8#H*; ++/иdи/m&и&/d,и,/SиS//~и~//иdи/01".54>32!"&554633#"&55463!2##32"32>54.##".55463326554&##"&5546332##"33267667672#!"&55463!23#/l\<<\l/1l[;<[l2 :6 <07 QH11HQ QH12HQ)  (O!, !,E'    R ZZ'B54B&&C45B' ! ! e,##++##, P*;$K*>  ""7<|#3G[\/W/\9и9/MAM&M6MFMVMfMvMMMMMMM ]AMM]и/AWW]AW)W9WIWYWiWyWWWWWWW ]Wи/WC] +>H++R4+1(+017463!2#!".57"3!26554#!"&55463!2%".54>32"32>54. , ,>]    R F2n[<<[n23n[;;\m3#SH00HS#"SH11HSi**   ""Y(E33C)*C/0C*# ,! , , !, 32"32>54.  R e,, ,7  2n[<<[n23n[;;\m3#SH00HS#"SH11HSK"#)ba&\ \(C21C(*B./B* + + + + Y +QK+A]A(8HXhx ]YQZ01".54>32"32>54.'&&'''&67>7632%#!"&55463!22n[<<[n23n[;;\m3#SH00HS#"SH11HS -p t+*nCqR1 0   R (E33C)*C/0C*$ ,!!, ,!!,  DZ " mEGr % 9HP' z""Yc?+$+MY+ +.A]A(8HXhx ]01#!"&55463!2%".54>32"32>54.#".54>324.#"32>  R F2n[<<[n23n[;;\m3#SH00HS#"SH11HS2AEEEEA22AEDEF@2I6MQQK66KQQM6;""`(E33C)*C/0C*$ ,!!, ,!!, T"3''3"!3&&4"", ,""+ +32'.'&''&67>76&#!"5543!2"32>54.#!"&55463!22n[<<[n23n[;;\mL2LLF  R[]'+\\Y'  ?}) F Ӗ#SH00HS#"SH11HS  R (C21C(*B./B*2  ! "  +6>  #  + + + + "#32'.'&''&67>76&#!"5543!2"32>54.#!"&55463!2#!"5543!2/l\<<\l/1l[;<[lF,=A  !QUT%Md  Rn& J K" QH11HQ QH12HQ  R  'B54B&&C45B'|(  $  2  '-1 ! 5t,##++##,""~32!54&#!"&55463!2##"55!"554"32>54.#!"&55463!22n[<<[n23n[;;\m  /).#SH00HS#"SH11HS  R (C21C(*B./B*K$#)w !  + + + + "#32!3!2#!"&55463!2#!"!2"32>54.#!"&55463!2/l\<<\l/1l[;<[l  , ,   QH11HQ QH12HQ  R 'B54B&&C45B'[N **  L " ,##++##,""<|3GW[t9+X+.Y+C+)и)/A9&969F9V9f9v9999999 ]A99]ACC]AC)C9CICYCiCyCCCCCCC ]]EX>/>>Y/+ 4+&+UL+>A]A(8HXhx ]/и,и/XиZ01".54>32!"&5546335#"&55463!2##32"32>54.#!"&55463!235#2n[<<[n23n[;;\m z w ^p#SH00HS#"SH11HS  R (E33C)*C/0C* ! ! I ,!!, ,!!, {""q32#!"&55463!2"32>54.!"&55463!2!"&55463!2#".54>324.#"32>/l\<<\l/1l[;<[l  R G QH11HQ QH12HQv ' te(@S*+SA((AR+*SA(I-88--88-'B54B&&C45B'J""^,##++##,h  ..-.ItC 1Rۻ*+  + +A ]A ) 9 I Y i y ]A*&*6*F*V*f*v******* ]A**] T//%+N=+-+01#"54332#".54>324.#"32>6'"&554632>7>5))$@X43V?$$?V30WB'F/>"$<-[K">/  !NST(SVN QUO#WVN~~2S))=)MY*>"  " Mb #7KaB.+WP+$8+ +A88]A8)898I8Y8i8y8888888 ]AB&B6BFBVBfBvBBBBBBB ]ABB] c/[L+3=++G)+01%#"54332'6##"&5546332>'#".54>324.#"32>!"&5543323!25))  Xg 9wri0;U53S;;S31S=#D,: !9**9" :,3/)  gu" #%A11B%)C//C(++,+u$)$MqY #Ug{#r`+N++Vh+ +3и3/+;и;/ EиE/Ahh]Ah)h9hIhYhihyhhhhhhh ]Ar&r6rFrVrfrvrrrrrrr ]Arr] }/R'+cm+0I+@7+w[++01#"54332'6##"&5546332>#!"&55463!26554&#!"&55463!2#!"3!2#".546324.#"32>5))  Xg 9upf /#- #-[ ;U53S;yg1S=#D,: !9**9" :,)k" #"*D*4"B*7 "<--="MZ+>%( '((M|D #7G[o fR+;$+H\+ + -и-/CиC/A\\]A\)\9\I\Y\i\y\\\\\\\ ]Af&f6fFfVfffvfffffff ]Aff] q/@1+Wa+'8+kM++01%#"54332'6##"&5546332>463!2#!".57"3!26554&##".54>324.#"32>5))  Xg =wocf , ,I]   #D,: 9**9!:, x# #**   #?//?$(@--A&*)*)Mv'JVn ++UN+A]A)9IYiy ]A&6FVfv ]A]9 U9UpQ/+#+ja+01#".54>324.#"32>'&&'''&7>7432#"54332'6##"&5546332>d;U53S;;S31S=#D,: !9**9" :, &o v,*pHsQ+ 0 ))  Xg 9upf?%A11B%)C//C(++,+  :_"  iABk" %9DJ% I" #}tC'3ѻ ++2++A]A)9IYiy ]A&6FVfv ]A]25(/./+#+01#".54>324.#"32>#"54332Y:[@B[77[B@[:G(@.0?&&@0.@(#))8r\;:]r8;s\88[s;.^K//K^.,]M11M]~vpC !5I@,+"6+ + иA66]A6)696I6Y6i6y6666666 ]A@&@6@F@V@f@v@@@@@@@ ]A@@] K//1;+ +E'+01%#"54332!2##"554&#!"&5546#".54>324.#"32>5))/) $ H$@W31V?$$?V1.VB(F.="#;-,<$"=. :#)$A2S<"!324.#"32>5)) /)  $@W31V?$$?V1.VB(F.="#;-,<$"=.t~#)$s4W>#"?V57X> =X7*A++A*(@--@yqX 3e*+^;+  + +A ]A ) 9 I Y i y ]A*&*6*F*V*f*v******* ]A**]Cи;KиK/ Uи g/b7+%+@Y+PG+/+01#"54332'#".54>324.#"32>#!"&55463!26554&#!"&55463!2#!"3!25))%@V10S?$$?S0-UB(F.< !;++:# <.S  /#-+ #-o +9/O:! 9P03Q88Q2%:&&:&$9)(:#*K*9$K*= ypC'j% +\9+TA++TTи/A&6FVfv ]A]9IиI/oиuиu/0/3/r//+>W+ly+#+yEиE/lMи3b_и_/beиe/3sиs/01#".54>324.#"32>###.55463326554&##"&5546332##"33267667632##"54&##"&5546%#"54332K%@V10S?$$?S0-UB(F.< !;++:# <.  'N!, !, '% 2/$  ))/O:! 9P03Q88Q2%:&&:&$9)(:N*9$I*> A#) #0,yqD';{2 +eR+(++A((]A()(9(I(Y(i(y((((((( ](9/и/A2&262F2V2f2v2222222 ]A22]2JиJ/2ZиZ/J[и[/2mи//!+-+Oh+_V+7+_и/!AиA/!Dиpиp/uиu/V|01#".54>3246332##".54.#"32>##".55463326554&##"&5546332##"332677>76"3326554#"54332K%@V10S?$$?S0-UB((,,.< !;++:# <.6 %K!, !,D" w t H))/O:! 9P03Q88QU**g%:&&:&$9)(: N*9$I*<     l.y`|'i} +[8+S@+++A]A)9IYiy ]A&6FVfv ]A]8HиH/9/A]A)9IYiy ]jt9t/Att]At)t9tItYtityttttttt ]и/j/o+_2++y++MD++#+2/и//y<и324.#"32>##".55463326554&##"&5546332##"33267667672%#".54>32#"54332!"&55463!24&#"326#"&554332K'AV.-T?&&?T-+UC)F/<:,+:!324.#"32>5)) , ,]]  $@W31V?$$?V1.VB(F.="#;-,<$"=. **   }2S<"!324.#"32>".546332!546332#%3!26555))&@W10U@%%@U0-VC(F/324.#"32>'.'''&67>7632#"54332N$@W31V?$$?V1.VB(F.="#;-,<$"=.82c 7cSC?Rb8CpS0 0 ))3U=""=U36V< 324.#"32>&&''&&'''&6766'4327>'4632#"54332N$@W31V?$$?V1.VB(F.="#;-,<$"=.~?YM11NYA b[ 0 "- .#/ ZU X))2S<"!+*+4H+ + и /A*&*6*F*V*f*v******* ]A**]AHH]AH)H9HIHYHiHyHHHHHHH ]AR&R6RFRVRfRvRRRRRRR ]ARR] ]//+CM+%+W9+01%#"54332#".54>324.#"32>#".54>324.#"32>5)).Oi324.#"32>'&&'''&76676&#!"5543!2'#"54332O&@W10U@%%@U0-VC(F/ +H+4+.I+VO+A44]A4)494I4Y4i4y4444444 ]A>&>6>F>V>f>v>>>>>>> ]A>>]VYR//+9+&+C+/и,и/HиJ01#".54>32!"&5546335#"&55463!2##324.#"32>35#%#"54332N$@W31V?$$?V1.VB( n Aj.="#;-,<$"=.Hu))2S<"! ! ! (=))=(&<,,7- ~"*\p+CYS38+8Gи3Mи3Wи3[5/J/)"+-<+OV+01'.'''&676676&#!"5543!2!2##"554&#!"&5546%#"5433232##0!#g; 365:H 3  >4) $ /)) 'N&$B # $*9_ # >i # %$9"^(>TYU/,/=2иUCиC/J@и@/=V//N?+&+4;+01'&&'''&676676&#!"5543!2#"5433232##!"&5543323!22!)i3  3i-:G v8  @4)) /)  w)S)/S "X1=h # 32#"5433232#####"&5543323326676676%!"&55463!24&#"326#"&554332 . &e6 7i)7N 0  >4*:$$:**:$$:*9)) &($/) &# & _r e0--11--0  %J%'C #F)6]"# Ba # %, !,, !+F"P#) $ & ' '&$$% $ \|G]^/K/^и/и/ и /K\ и /\Qи\_N/+E>+ +SZ+01#!"&55463!2#!"3!2'.'''&676676&#!"5543!2#"5433232##6 , , 5  0!#g; 365:H 3  >4)) v** !  'N&$B # $*9_ # >i # %@"Zq1`vw/d/wи/*и/dи/и/du!и!/ujиuxg/.+^W+ %+ls++01#!"&55463!26554&#!"&55463!2#!"3!2'.'''&67>76&#!"5543!2#"5433232##6  /#-+ #-o G033 8627N ?q_G  @4)) #*K*9$K*= EK&" # &(1V$ AMT+ # %e"ZqBǻtQ+lY+++Qaиa/и/ +@9+Vo++f]+fи/ HиH/ Kиwиw/|и|/]01%46332##".5'.'''&67>76&#!"5543!2##".55463326554&##"&5546332##"332677>76"3326554#"5433232## ,,G033 8627N ?q_G  @4>6 %K!, !,D" w t >)) **EK&" # &(1V$ AMT+ # % N*9$I*<     r"\|#Oef/S/fи/и/Sи/Sd и /S и /и/Sи/dYиdgV/ +MF++[b+017463!2#!".57"3!26554&#'.'''&676676&#!"5543!2#"5433232## , ,]]  0!#g; 365:H 3  >4)) **   'N&$B # $*9_ # >i # %B"\|EOef//fи/Gи/G ии/NиSиYиcиgV/J+C<+F+[b+01".546332!546332#'&&'''&676676&#!"5543!23!26557#"5433232##0,, , . &e6 7i)7N 0  >4  7)) jh&%J%'C #F)6]"# Ba # %wl l"^w'Pf5eT+eZиehW/%+\c+01'&&'''&676676&#!"5543!2'.'''&67>7632#"5433232##B(c6  /j-9J 4  @42c 7cSC?Rb8CpS0 0 )) |LP.N "X/;d"# Bl # %  Lb ".;D$$E<0 % '4632'.'''&676676&#!"5543!2#"5433232##h?YM11NYA b[ 0 "- .#/ ZU 0!#g; 365:H 3  >4)) ![56Y U34T#$ 0e  @9/ :HR)   P' 'N&$B # $*9_ # >i # %L"\p'Si +hW+WA&6FVfv ]A]h]иhkZ/#+QJ++_f+01%#".54>324.#"32>'.'''&676676&#!"5543!2#"5433232##,.Oi4)) '+D//D+*C/.C...--'N&$B # $*9_ # >i # %* "\q&Pf?eT+eZиehW/NG+$+\c+01%'&&'''&76676&#!"5543!2'&&'''&676676&#!"5543!2#"5433232##-Q@@= @LDT Vv P . &e6 7i)7N 0  >4)) `-.  " 1 7  c< # 2%J%'C #F)6]"# Ba # %<"AtcGaH//Hи/и иI //E>++. 90134332##"5###"54332'.'''&676676&##"5543!2~&&~&&+\7/,'.uG }& 4lPG9k4<|3 59:G~5 b # "SpdCYZ/N/Zи/ иN и /NIиNи/NEиE/I[/K/A:+ER+ +01%#"54332354332##"55#'&&'''&7>76&##"5543!2!2##"554&#!"&5546&&&&(#L1 3K"'eD  76##"5543!2!"&5543323!2&&&& 2N&N  ?^G4  4,'Q.K/)  lCj F/\Y  .TTY3 # %-0V'1F h$)$Tqy1Mv*+BG+94+4и/и/9!иB2и4?и9x6/.+kd+ %+3@++01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332'&&'''&67>76&##"5543!2y /#-  #-O &&&& 1I!'hG>[F5  4!'!I/#*F*4$F*8 +g @'*O-  &DFM0 # &,(D '= S|d#?gy+49++&++ и&и/4$и&1и+i(/ +e^++%2+01%463!2#!".57"3!26554&#354332##"55###"54332'&&'''&7>76&##"5543!2 , ,=]  d&&&&(#L1 3K"'eD  76##"5543!23!2655[,, ,u&&&& 2I!'gE  =^G4  4("I0  jh&(X !@()Q-  &HKP. # %-)H#(> f fSwCl_m//mи/и иIиI/n/A:++01354332##"5###"54332'&&'''&7>76&##"5543!2'.'''&67>7632&&&&(#L1 3K"'eD  K+01&&''&&'''&6766'4327>'4632354332##"55###"54332'&&'''&7>76&##"5543!2?YM11NYA b[ 0 "- .#/ ZU &&&&(#L1 3K"'eD  32#".732>54.#"%#"54332354332##"55#'&&'''&7>76&##"5543!2.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#M&&&&(#L1 3K"'eD  7- ~  \p+KaqJ/+J5и/Eܸ:иJ?иJPи/VиJc2/S/)"+7>+MZ+@I+01'.'''&676676&#!"5543!2#"5433232##32##!2##"554&#!"&55460!#g; 365:H 3  >4))  /) $ 'N&$B # $*9_ # >i # %@!x!j6#)$^(H^gTM+G,+G2и,Bܸ7иG<иG`//XI+&+4;+>E+01'&&'''&676676&#!"5543!2#"5433232##32##!"&5543323!22!)i3  3i-:G v8  @4))  /)  w)S)/S "X1=h # 32#"5433232##32#####"&5543323326676676%!"&55463!24&#"326#"&554332 . &e6 7i)7N 0  >4*:$$:**:$$:*9))  &($/) &# & _r e0--11--0  %J%'C #F)6]"# Ba # %, !,, !+F!m!DP#) $ & ' '&$$% $ Zq1`*+d+dи/и/!и!/jиdzܸoиtиg/.+^W+ %+ls++v}+01#!"&55463!26554&#!"&55463!2#!"3!2'.'''&67>76&#!"5543!2#"5433232##32##6  /#-+ #-o G033 8627N ?q_G  @4))  #*K*9$K*= EK&" # &(1V$ AMT+ # %a!m!\\|#Oo+nS+n и /Sи/nYиSiܸ^иncиnqV/ +MF+[b++el+017463!2#!".57"3!26554&#'.'''&676676&#!"5543!2#"5433232##32## , ,]]  0!#g; 365:H 3  >4))  **   'N&$B # $*9_ # >i # %B!x!m\p'Ss+rW+W A&6FVfv ]A]r]иWmܸbиrgиruZ/+QJ+_f+#+ip+0174>32#".732>54.#"'.'''&676676&#!"5543!2#"5433232##32##*LjA4))  '*C/.C++D//D(--..N'N&$B # $*9_ # >i # %( !{!zAtdGK{L//Lи/и иHиIиM //E>+J+I+. 90134332##"5###"54332'.'''&676676&##"5543!235#&&&&+\7/,'.uG }& 4WG9k4<|3 59:G~5 b # "xOAW[uMF+++и иXиYи]/QB+6/+Z+Y+01354332##"55###"54332'&&'''&7>76##"5543!2!"&5543323!235#&&&& 2N&N  ?^G4  4,'Q.K/)  ;ep F/\Y  .TTY3 # %-0V'1F h$)$Tqy1Mvz˻*+BG+94+4и/и/9!иB2и4?иBwи4xи9|6/EXS/S>YEXw/w>Y.+kd+ %+3y++w@01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332'&&'''&67>76&##"5543!2735#y /#-  #-O &&&& 1I!'hG>[F5  4!'!I/#*F*4$F*8 ۅ+uhg @'*O-  &DFM0 # &,(D '= ee[tC,@C?0+08и?B-/;/+81+-;901%'.'''&676676&#!"5543!2#"5#"554334332j 6422R # H4'G444) )/59?s9 q~ # $.k >7-  $ 8cpC+AUS16+6Bи1Hи6Nи1W3/E/)"+-:+BO+01'.'''&676676&#!"5543!2!2##"554&#!"&554654332##"55#"5543#3#"d; 3659F 3  @4/) $ )) )P'$A $ $*8\# >i # %#)$` $ eb(<R]S/*//*5иSAиA/H>и>//T,/L=+&+)6+01'&&'''&676676&#!"5543!254332##"5#"5543!"&5543323!2#4#(i3  3i-9E v8  @4)) /)  w*V*/S "X176&#!"5543!254332##"55#"5543X  /#-+ #-o 2%033 8726M ?q_G  @4)) #*K*9$K*= #L&&" # &(0S$ AMT+ # %3 $ bqDCٻuR+mZ+++ и /Rbиb/и/и// +A:+Wp++g^+gи/ IиI/ Lиxиx/}и}/^01%46332##".5'.'''&67>76&#!"5543!2##".55463326554&##"&5546332##"332677>7654332##"55#"5543"3326554&##,, 2%033 8726M ?q_G  @4-6 %K!, !,D" )) 8 t **#L&&" # &(0S$ AMT+ # % N*9$I*<  0 $ v   c|C#Ocd//dи/и/и/  и /и/Pи Vи\и eS/ +MF++P]+01%463!2#!".57"3!26554&#'.'''&676676&#!"5543!254332##"55#"5543 , ,]]  3#"d; 3659F 3  @4)) **   )P'$A $ $*8\# >i # % $ c|CDNbc//cи/Fи/F ии/MиOиUи[иdR/I+B;+E+O\+01".546332!546332#'&&'''&676676&#!"5543!23!265554332##"55#"5543R,, ,D&e6 7i)7K 0  >4  )) jh&ML'C #F)4[!# Ba # %wl l/ $ ew(Qe5XS+S^иXgU/&+R_+01'&&'''&676676&#!"5543!2'.'''&67>763254332##"5#"5543#2!)i3  3i-:G v8  @42c 7cSC?Rb8CpS0 0 )) )S)/S "X1=h # 324.#"32>'.'''&676676&#!"5543!254332##"55#"5543G.Oii # % $ cq&Oc?VQ+Q\иVeS/MF+$+P]+01%'&&'''&76676&#!"5543!2'&&'''&676676&#!"5543!254332##"55#"5543-Q@@= @LDT Vv PD&e6 7i)7K 0  >4)) `-.  " 1 7  c< # 2ML'C #F)4[!# Ba # % $ Atd-COYP/I/P/и//4/:иIDQL/F/+$+.;+FL901'.'''&676676&##"5543!24332##"5#"&55463##"54332,)030.*-tE }& 4&& &&m:o6?<5 289E|4 b # "/"~Spd'=Q]i^/W/R,иW2и2/^?и?/D?JиR_//Z/%+)6+QL+01'&&'''&7>76&##"5543!2!2##"554&#!"&554654332##"55#"5543##"54332*#M2 4K#'dC  76##"5543!2!"&5543323!254332##"5#"5543##"54332  3O&N}  ?^G4  4%8(Q/K/)  && && !G0[X  .TTY3 # %-aO2F m#)$q% $ Tqy1Znz*+a\+ot+tи/и/o!и\gиo|w/.+OH+ %+[h++01#!"&55463!26554&#!"&55463!2#!"3!2'&&'''&67>76&##"5543!2754332##"55#"5543##"54332y /#-  #-O  2I"'hE>[F5  4!("I0N&& &&#*F*4$F*8  !@()N,  &DFM0 # &,*E (> ̲G $ S|d#K_kq+RM+`e+` иeи/MXи`mh/ +IB++LY+01%463!2#!".57"3!26554&#'&&'''&7>76&##"5543!254332##"55#"5543##"54332 , ,:]  *#M2 4K#'dC  76##"5543!23!265554332##"55#"5543##"54332[,, , 2I!'gE  =^G4  4("I0  && &&jh& !@()Q-  &HKP. # %-)H#(> f fF $ Sw'PdpSq/j/e7и7/qRиR/WR]иerm/%+Q^+01'&&'''&7>76&##"5543!2'.'''&67>763254332##"55#"5543##"54332*#M2 4K#'dC  32#".732>54.#"'&&'''&7>76&##"5543!254332##"55#"5543##"54332.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#*#M2 4K#'dC  /8/+5.+-B+>8901%'.'''&676676&#!"5543!275#"5543354332##"5#"5543j 6422R # H4'G444} )) /59?s9 q~ # $.k >7-  #  # ]b(DZgPI+09+0*5и*=и9Aи0\,/TE+&+)>+=6+01'&&'''&676676&#!"5543!254332##"55#"554335#"5543!"&5543323!29&(e2  2e-8B v8  @4)) /)  w-\--O "V09^# 76&#!"5543!254332##"55#"554335#"5543X  /#-+ #-o 8'/11 6515J ?q_G  @4)) #*K*9$K*= %P)%  # $'-O$ AMT+ # %6r4y # k # c|C#Ok+W`+W иWPи\иdи`hиWmS/ +MF+Pe++d]+01%463!2#!".57"3!26554&#'.'''&676676&#!"5543!254332##"55#"554335#"5543 , ,]]  :&"d: 3658B 3  @4)) **   ,W*#A $ $*4V# >i # %D  # h # c|CEOkG+W`+G иWWиNиPи\иdи`hиWmS/J+C<+F+Pe+d]+01".546332!546332#'&&'''&676676&#!"5543!23!265554332##"55#"554335#"5543R,, ,:'&e5 7i*5~F 0  >4  )) jh&*V*&C #F*0R# Ba # %wl lG}/v # h # cp<hk+py+yp9A&6FVfv ]A] 9и/+yp9+/A++]A+)+9+I+Y+i+y+++++++ ]41и1/4iиi/pj4uиu/jvи4}и}/j~иyиpl/\U+i~+v/+/и/~|v}01&&''&&'''&6766'4327>'4632'.'''&676676&#!"5543!254332##"55#"554335#"5543?YM11NYA b[ 0 "- .#/ ZU  3658B 3  @4:&"d:)) ![56Y U34T#$ 0e  @9/ :HR)   P'  $*4V# >i # %-,W*#A & # h # cpG'Soϻ +[d+[и/[Uи/A&6FVfv ]A]U`иUhиdlи[qW/#+QJ+Ti++ha+01%#".54>324.#"32>'.'''&676676&#!"5543!254332##"55#"554335#"5543G.Oii # %D # h # Gtd(DPk09+EJ+0*5и*=и9AиERM/G/&+)>+=6+6=901'&&'''&76676&##"5543!254332##"5#"554335#"5543##"54332< &,/ .Q*kE  } 4&& s&&qzl?=5 -l6=t; e # $vu # # Y~<)C)+<+,3++,;01'&&'''&7>76&#!"5543!2!2#!"&55463!546332TJJJd  Ywf2 R ', HT@F   +; F+/M"  ?EN/ # &H<#=9"" и>/PK>K9>-PGиG/K]M/)"+GT+C5+5.=01'.'&''&7>76&#!"5543!2!2#!"&55463!546332!2##"554&#!"&55464YEHH  "TWU$O^   Ev#P M  +/)  1. !  !3 *49  # 5"" #)$<-E[U\/;/B\JиJ/QGиG/UF+,%+?2+2C:01'.'&''&7>766#!"5543!2#!"&55463!54332!2!"&5543323!2L0KLF  Q[\(,]ZU$  <}' P ɖr  ) ~/)   2  ! # +7? # ""x')$766#!"5543!2!"&55463!54332!2'/#-  #-Q OK0IHB  Q\`)RD   Cr# P (-  ) #*E*3#C*8 #(  !   *   ")- # %"mm"7+Xq+h_++hи/ JиJ/ Mиyиy/и_01%46332##".5'.'&''&7>766#!"5543!2##".55463326554&##"&5546332##"33267667672!"&55463!54332!2"3326554&#,,K0IHB  Q\`)RD   Cr# P (-   'N!, !,D&   )  z **(  !   *   ")- # % J*5$E*8  "mm"   <|?OigC+Qb+ K+ 9 kH +=6+@+gY+YRa017463!2#!".5'.'&''&7>76&#!"5543!2"3!26554&#!2#!"&55463!546332 , ,>4YEHH  "TWU$O^   Ev#P M    +Z**1. !  !3 *49  # 5 y y X"" +b+_Q+QJY01".5546332!546332#'.'&''&7>76&#!"5543!2!2#!"&55463!5463323!2655,, ,2ODEB  "QTS#Jl   Kq"P M  +  ^]&/(  !  2 '05  # 6}""} XX Xи>/-FиF/Xf-9)"+5i+"Cܺ"C95.=01'.'&''&7>76&#!"5543!2!2#!"&55463!546332'&&'''&67>76324YEHH  "TWU$O^   Ev#P M  + -p t+*nCqR1 0 1. !  !3 *49  # 5""  DZ " mEGr % 9HP' 32'.'&''&7>76&#!"5543!24.#"32>!2#!"&55463!546332*@^m-EE@1@^m,-m_?N4YEHH  "TWU$O^   Ev#P M,6LQQK55KQRK6  +3@$  %2"2?$ $@P1. !  !3 *49  # 5N!''! ((͆"" 76&#!"5543!2!2#!"&55463!546332I.?A  "UZZ'Ma  U F O82ODEB  "QTS#Jl   Kq"P M  +Y-$  !  3  ]9  # 4 /(  !  2 '05  # 6}""} 76&#!"5543!2'.'&''&7>766#!"5543!2!"&55463!54332!2#!"5543!2H- HJI!  #X^\'Kc   Oq) J %0!K0IHB  Q\`)RD   Cr# P (-  )  ='  $  3 )-/ ! #H(  !   *   ")- # %"mm"D7+n+d[+}+PC+CTK01#".54>32'.'&''&7>766#!"5543!2!"&55463!54332!2!"&55463!24.#"32>!"&55463!2(@S*+SA((AR+*SA(K0IHB  Q\`)RD   Cr# P (-  ) n t-88--88-  '1..-.(  !   *   ")- # %"mm"  ?t(>gh/,/hZиZ/,=Z=92иZc=i//)/$+cN+4;+)/9NIиI/cUиU/cZиZ/;^и^/01'&&'''&676676&#!"5543!2#"5433232##6"'"&55463267546332>*B%0s6  9=76&#!"5543!2!2##"554&#!"&5546%#"5433232##'6##"&55463!5463326649"0h1 9{?<9 -ed\# N$ /)  B))   Yh +F|+$ !+ 2   '07 ! "#)$4"o" #v tIq3[q,++"+и/6"9_и"eи"oи"b/0+WP+ '++gn+|+gJиJ/|и/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'''&676676&#!"5543!2#"5433232##'6##"&55463!54332665 /#- [ /@2g/   9~@=9 pB N$ ))   Yh )F|"*9*+"C , /&  "  %+ ![4 ! "g"f" #jhI|GQnI+le++I к9Pиrиxииu/L+C<+H+z+h\+\elиl/\oиo/01".546332!546332#'&&'''&67>76&#!"5543!23!26556##"&55463!5433266#"5433232##,, ,:#1i0 ;|?<}8 .heZ  N$  =  Qd )E)) `c& +! !)2   (04 ! "W W7" #mlI"Iv)Lb}/P/vиv/Pava9;va9Vиv}aS/%+ym+X_+mv}и}/01'&&'''&676676&#!"5543!2'&&'''&7>7432#"5433232##'6##"&55463!54332664;$3i0 9{A;|8 iH N$  &o v,*pHsQ+ 0 ))   Qd )Et,& !. 5 ! d9 ! "+  :_"  iABk" %9DJ% P"" #wvIq'Siѻ +k+hW+WA&6FVfv ]A]+ 9h]иhZ/#+OH++_f+x+xkиk/01%#".54>324.#"32>'&&'''&67>76&#!"5543!2#"5433232##%6676##"&55463!546332-3Tl9>mQ//Qm>9lT3G&@S-2S<""tdGjh_+++и к_9l/ /C<+cU++ 9URиR/Uh\и\/h_и_/0134332##"5###"54332'&&'''&67>76&#!"5543!26"'"&55463754633266&&&&; *b, *p42l9-]YQ  :#/":9=$8x; +d6+7^lPG8/ 5%>  5?D! # #6 # " Jp8T+IN+@;+@9! !9и/!и/I9и;FкW!9@=/EXv/v >Y:G+-+-и/KиK/vܺWv9v}и/и/и/01&&''&'''&67>'432766'4632354332##"55###"54332'.'''&67>76&#!"5543!26#'"&55463275433266?WL7o+XB 6I, 0 D; ?B/ VP &&&&';&T, 2541f/ +XQH &$ 19=>:8 .d0)4eP/-J" >V,K$$ 8@K.   3d){S  !Bf"  p'*!  1 )/2 ! " # "tsJum'Cp+8=+/*+A&6FVfv ]A]*и/8(и*5кF/99//,/+le+#+)6+~+~{и{/~и/и/0174>32#".732>54.#"354332##"55###"54332'.'''&67>76&#!"5543!26#'"&55463275433266.TtF@sW33Ws@FtT.H%BX3.YE**EY.3XB%g&&&&';&T, 2541f/ +XQH &$ 19=>:8 .d0)4e'*A--B*+B--B(,,--p'*!  1 )/2 ! " # "poItC*6_`/./`RиR/.5R59R[5a+/1/&+[F++19FAиA/[MиM/[RиR/01'&&'''&67>76&#!"5543!2#"543326"'"&55463267546332>>D&1u7  :>=~; 4lh\# b$ ))!NST(QUP >FG +"MKGS;4 ;#)C  8AF" # "~ #  "  Mb*@Lje6/+h`+KD+/K9KlG/:++&+cW+W`hиh/01'&&'''&67>76&#!"5543!2!"&5543323!2'#"54332'6##"&55463!5433266>="0m2  ;{:9z;4kbU N$ /)  #))  Xg ' Ms2, 3"8 !06: ! "$)$]" #xw MqY3[x,+vo+|+|и/и/!и!/6!9/0+WP+ '++rf+fovиv/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'''&676676&#!"5543!26##"&55463!5433266#"54332Y /#- [ /@2g/   9~@=9 pB N$ g  Xg )N))"*9*+"C , /&  "  %+ ![4 ! "" #jh ]M|D#Oo{+me+zs+z и /sи/' 9z}v/ +KD++hZ+Zceиe/cmиm/017463!2#!".57"3!26554&#'&&'''&67>76&#!"5543!26##"&554633275433266#"54332 , ,I]  9"0h1 9{?<9 -ed\# N$ g  Xg ' P))d**   +$ !+ 2   '07 ! "# #tt e M|CGQo{I+me++I к9Pиsиyи}v/L+C<+H+h\+\emиm/\pиp/01".546332!546332#'&&'''&67>76&#!"5543!23!26556##"&55463!5433266#"54332;,, ,9"0h1 9{?<9 -ed\# N$  G  Xg ' M))`c& +$ !+ 2   '07 ! "W W4" #po HMv*Mjvow/n/w`и`/nu`u9<`u9`huxq/&+cW+W`hиh/01'&&'''&67>76&#!"5543!2'&&'''&7>743276##"&55463!5433266#"54332>="0m2  ;{:9z;4kbU N$  &o v,*pHsQ+ 0 {  Xg ' L))2, 3"8 !06: ! "  :_"  iABk" %9DJ% x#  #po VMqF'Sq}ͻ +og+|u+|и/uи/A&6FVfv ]A]+ 9|x/#+OH++j^+^goиo/01%#".54>324.#"32>'&&'''&67>76&#!"5543!26##"&55463!5433266#"54332F3Tl9>mQ//Qm>9lT3G&@S-2S<""76&#!"5543!232#!"&5546335463323546332HT@F JJJd  Ywf2 R ', H  **dH<#=$ F+/M"  ?EN/ # &""  76&#!"5543!2#!"&55463354332354332!2!2##"554&#!"&55464YEHH  "TWU$O^   Ev#P M  )) /)  1. !  !3 *49  # 5h""rrrr#)$<-MceYR+B;+JC+RJ9]N+,%+G2+2K:иG>иKB01'.'&''&7>766#!"5543!2#!"&55463354332354332!2!"&5543323!2L0KLF  Q[\(,]ZU$  <}' P ɖr  )) ~/)   2  ! # +7? # ""}')$32'.'&''&7>76&#!"5543!24.#"32>#!"&55463354332354332!2*@^m-EE@1@^m,-m_?N4YEHH  "TWU$O^   Ev#P M,6LQQK55KQRK6  )) 3@$  %2"2?$ $@P1. !  !3 *49  # 5N!''! ((:""rrrr01'&&'''&7>76&#!"5543!2"&5!"&55463!2#!#WNNH_ Hr, R '- BZ?G x| R w =&%A 5=C"  # %;42 D"" 76&#!"5543!2 { R x/)  4YEHH  "TWU$O^   Ev#P M""#)$1. !  !3 *49  # 5<)CYQZ/-/@ZHиH/OEиE/SD+' +6/+/>01'.'&''&7>76&#!"5543!2"&55!"&55463!2#!#!"&5543323!2H*B@  !SWX%   Rm'P Ms R !/)  0'  !  @0/5: # 7{ "" ')$766#!"5543!2 x#-Q /#-  { R K0HHA Q\`)RD  Cr# P (- |"h@*3 ##*@*0#h"( !   *   ")- # %766#!"5543!2##".55463326554&##"&5546332##"33267667672"&55!"&55463!2#!#32##"54&##"&5546K0HHA Q\`)RD  Cr# P (-   )Q!, !,F(  *{ R x/$  ( !   *   ")- # % D*/$?*4  3 a""a #) #7+Xq+h_+hи/hи/и/ JиJ/ Mиyиy/ܹи_01%46332##".5'.'''&7>766#!"5543!2##".55463326554&##"&5546332##"33267667672#!##"&55!"&55463!2"3326554&#,,K0HHA Q\`)RD  Cr# P (-   'N!, !,D&   x+{ R  z **( !   *   ")- # % D*/$?*2  "k k"   <|'SckW++_+*9e\!+QJ+T+ +и01746335!"&55463!2#!32#!".5'.'&''&7>76&#!"5543!2"3!26554&# ,{ R x ,>4YEHH  "TWU$O^   Ev#P M  `*""*1. !  !3 *49  # 5   76&#!"5543!2"&55!"&55463!2#!#3!2655,, ,D,DGG  $TVS$Ke   Ir$P M{ R x  ]\&/ !  2 )27  # 5 "" {V V75!"&55463!2#!'.'&''&7>76&#!"5543!2)-p t+(q?lR3{ R x4YEHH  "TWU$O^   Ev#P MsDb " tGJw  % 4CK&n""u1. !  !3 *49  # 575!"&55463!2'.'&''&7>76&#!"5543!24.#"32> x-aR5@^m-EE@15Rb,{ R 4YEHH  "TWU$O^   Ev#P M,6LQQK55KQRK6\"z%<.3@$  %2"-=%z"11. !  !3 *49  # 5N!''! ((ItC>fg/ /и gPиP/P9Ih/ /:3+ +bQ+ 9QHиH/QVиV/01%4332##"55#"5543'&&'''&67>76&#!"5543!26##"&5'"&554632>7>)) = -i:  ?z:=|9 /eb[$ N$ k  >H+ JG@ QUO#RRJw " 5% ,'<  /;C# # ""   :" To8dx/q/|и|/ql|l9| |9и/и/<|l9qeиlh/`Y+*+er++*и/}и}/01&&''&&'''&6766'432766'4632'&&'''&67>76&#!"5543!24332##"55#"5543"&55"'"&554632>7>76#>^N68L5eY 0 C6=D/ 0>$ 9"-[0 ;l<8w7 .b_V# N$ )) B@: LQL NOL  BLL3+J H+W? " +oJ  7['k@   81)   * !!.   $-4 ! "% " ^ n" "  s 4td;GiRY++<A+ кY<9<k>/D/70+ +gZ+>D9ZQиQ/Z]и]/gdиd/01##"55#"554334332'&&'''&67676&#!"5543!2##"543326##"&5'"&554636766& &3+Y4  ;a33j6  &$ }&&  .h7+9o+ :~9?uX " W.& , %>  S # " ~+"   !" ItC(4\o]/,/]8и8/,3#8398Y3^)///+JX+#)/9X9и9/01'&&'''&67>76&#!"5543!2#"54332%"&5'"&554632>7>76# @~7>#"543329|97w; hI N$ 7 &f4^/)   ?F+ HF= MSN!NPL)) ".  X9 ! "*#)$   ~"4Q[)5\rha+?F+4-+$a494t0/e^++XC+XG>и>/eBиB/GLиL/^l01'&&'''&676676&#!"5543!2#"543326##"&55'"&554632>7>!"&5543323!2<;9u6o> N$ Y.+NG++~h+% V%9~mdиd/mrиr/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'''&676676&#!"5543!26##"&55'"&554632>7>#"54332X /#- #-X 7u73uB oC N$ 4&^0 ?F+ HF= MSN!NPL))#*4*"$4*& = ) !Q6 ! ") `  c ^"Q|C#Mt+W^+ +H 9xи ~и {/ +@9++pZ+p_VиV/_dиd/017463!2#!".57"3!26554&#'&&'''&676676&#!"5543!26##"&55'"&554632>7>#"54332 , ,D]  Z9|97w; hI N$ 7 &f4 ?F+ HF= MSN!NPL))H** g g  ".  X9 ! "*m  } x"+Q}CElvn+OV++n к@9uиzии}/q+81+m+hR+R и /Rи/hWNиN/W\и\/01".5546332!546332#'&&'''&676676&#!"5543!26##"&55'"&554632>7>3!26557#"54332;,, ,k9|97w; hI N$ 7 &f4 ?F+ HF= MSN!NPL  7))WU&W ".  X9 ! "*e  s n"L LLv)Lt/x/^и^/x$^9;^9^W{/+pZ+p_VиV/_dиd/pkиk/01'&&'''&676676&#!"5543!2'&&'''&7>743276##"&55'"&554632>7>#"54332~9z95o7r? N$ ?$'g4; &o v,*pHsQ+ 0 d  @G+GE> MTP'SPJ)) "1  'd; ! "2  :_"  iABk" %9DJ% "  "L76&#!"5543!2? R +NNH_ Hr, R '- BZ?G  D"" D  =&%A 5=C"  # %;42<)McYYR+J-+@G+R@9]N+' +6/+/>и/H01'.'&''&7>76&#!"5543!2"&55!"&55463!2####"&55##!"&5543323!2H*B@  !SWX%   Rm'P MZ R +/)  0'  !  @0/5: # 7 ""  ')$766#!"5543!235# ##-Q /#-  b R K0HHA Q\`)RD  Cr# P (- |"h@*3 ##*@*0#h"( !   *   ")- # %:h<|'ScgW+d+e+_+*9i\!+QJ+T+ +ииdиf01746335#"&55463!2##32#!".5'.'&''&7>76&#!"5543!2"3!26554&#%35# ,# R ' ,>4YEHH  "TWU$O^   Ev#P M  `*""*1. !  !3 *49  # 5   <<*:8/++01'&&'''&7>76#!"5543!2#!"&55463!2TPNG^   Xuf3R ', R[JON$ J  R " I..N# "DIR2 # &SA#! ""76&#!"5543!2!2##"554&#!"&5546%#!"&55463!24YEHH  "TWU$O^   Ev#P M/)    R 1. !  !3 *49  # 5#)$""<-CS+92+=.+,%+QH+01'.'&''&7>766#!"5543!2!"&5543323!2#!"&55463!2L0KLF  Q[\(,]ZU$  <}' P ɖ /)   y  R 2  ! # +7? # ')$""766#!"5543!2!"&55463!2'/#-  #-Q OK0IHB  Q\`)RD   Cr# P (-  R #*H*6#F*; #(  !   *   ")- # %""<|?O_`/K/`и/Cи/Kи/K K и /Cи/Kи/ 9 aH +=6+@+]T+017463!2#!".5'.'&''&7>76&#!"5543!2"3!26554#!"&55463!2 , ,>4YEHH  "TWU$O^   Ev#P M    R f**1. !  !3 *49  # 5   ""+H+_V+01".546332!546332#'.'&''&7>76&#!"5543!23!2655#!"&55463!2,, ,2ODEB  "QTS#Jl   Kq"P M   R a`&/(  !  2 '05  # 6h[ [*""76&#!"5543!2'&&'''&67>7632%#!"&55463!24YEHH  "TWU$O^   Ev#P M -p t+*nCqR1 0   R 1. !  !3 *49  # 5 DZ " mEGr % 9HP' }""+M+i`+01%#".54>32'.'&''&7>76&#!"5543!24.#"32>#!"&55463!2*2AEEEEA22AEDEF@2N4YEHH  "TWU$O^   Ev#P M,6MQQK66KQQM6  R "3''3"!3&&4Y1. !  !3 *49  # 5V", ,""+ +W""etC,8170+7:-/3/+-3901%'.'''&676676&#!"5543!2#"54332t 6422R # H4'G444))/59?s9 q~ # $.k >7- ~fpC+AMA16+6Eи1Kи1OH/3/)"+-:+01'.'''&676676&#!"5543!2!2##"554&#!"&5546%#"54332&0!"d; 365:H 3  @4/) $ /))'N&#B $ $*9_ # >i # %#)$8ha(>JKK/B/K-и-/4*и*/BILE/8)+&+01'&&'''&676676&#!"5543!2!"&5543323!2'#"54332&2!)i3  3i-:G v8  @4 /)  "))w)S)/S "X1=h # + +01#!"&55463!2#!"3!2'.'''&676676&#!"5543!2#"54332S , , 5  0!"d; 365:H 3  @4))v** !  'N&#B $ $*9_ # >i # %BeqX1`lm//mи/*и/и/".и./dи"jи"ng/.+^W+ %++01#!"&55463!26554&#!"&55463!2#!"3!2'.'''&67>76&#!"5543!2#"54332X  /#-+ #-o G033 8627N ?q_G  @4))#*K*9$K*= EK&" # &(1V$ AMT+ # %e7eqDBûtQ+lY+++ и /Qaиa/и// +@9+Vo+f]+fи/ HиH/ Kиwиw/|и|/]01%46332##".5'.'''&67>76&#!"5543!2##".55463326554&##"&5546332##"332677>76"3326554#"54332#,,G033 8627N ?q_G  @406 %K!, !,D" w t H))**EK&" # &(1V$ AMT+ # % N*9$I*<     q3g|C#O[\//\и/и/и/  и /и/Sи Yи ]V/ +MF++01%463!2#!".57"3!26554&#'.'''&676676&#!"5543!2#"54332 , ,]]  0!"d; 365:H 3  @4))**   'N&#B $ $*9_ # >i # %= f|CEO[\//\и/Gи/G ии/NиSиYи]V/J+C<+F+01".546332!546332#'&&'''&676676&#!"5543!23!26557#"54332S,, , . &e6 7i)7N 0  >4  7))jh&%J%'C #F)6]"# Ba # %wl l2iw)R^#]V+]`Y/' +01'&&'''&676676&#!"5543!2'.'''&67>7632#"54332'1 -f4 2k/9G w8  @42c 7cSC?Rb8CpS0 0 )))T*7U !]6Am## ?t # % Lb ".;D$$E<0 % 324.#"32>'.'''&676676&#!"5543!2#"54332G.Oii # %&fq&R^-]V+]`Y/PI+$+01%'&&'''&76676&#!"5543!2'&&'''&676676&#!"5543!2#"54332-Q@@= @LDT Vv P 0!034 8h+6N -  @4))`-.  " 1 7  c< # 2&M&%   $D+5]## C] # %:eqO#R^_/V/_и/V]и/] и / и /и/]и/и]`Y/ +PI+++01%!3!2#!"&55463!2#!"!2'.'''&67>76&#!"5543!2#"54332,"  , , 5 G033 8627N ?q_G  @4))P **  O ! EK&" # &(1V$ AMT+ # %e7g|uKO[kL +M+ZS+Z]V/+IB+ +и иLи N01!"&5546335#"&55463!2##32'.'''&676676&#!"5543!235#%#"54332f n Aj0!"d; 365:H 3  @4~|)) ! ! 'N&#B $ $*9_ # >i # %u,tPf]eT+eZиehQ/W/(!+\c+QW9c\9!8и(>01%&&'''&7667&&'''&7>76&##"5543326676&##"554332&#"5433232##'L&`7  8!>O0  *H7% / !  $-=  / ")-))  `/L8  F&M(?q.  'eor4 %,TV++(MW %,9q976.~"*CpQg}wW\+\kиWqиW{иWY/n/+$+S`+sz+Yn9zs9$<и+B01'&&'''&7667&&'''&7>76&##"5543326676&##"554332!2##"554&#!"&5546%#"5433232## &G#Y/  *#?J.  '=/#    B&$0 x G'1/) $ /)) Z<"и|и|/и/01'&&'''&7667&&'''&76676&##"554332676&##"554332#".54>32#"5433232#####"&5543323326676676%!"&55463!24&#"326#"&554332 &E U3  )#>M1  Nc   A&Jx  F&*:$$:**:$$:*9)) &)$/)  &# & _r e0--11--0  e;!6f*  (;!*U*  ?*> X, !,, !+F"P#) $ & ' '&$$% $  i" 1tr&& L*7$G*:  q"t tC|eu/q/и/iи/qи/q q и /iи/ 9i и /(i9iGиG/qyи и и |/n +?8+f++(98Pи?V017463!2#!".5'&&'''&7667&&'''&7>76&##"5543326676&##"554332"3!26554#"5433232## , ,] &G#Y/  *#?J.  '=/#    B&$0 x G'  O)) **<"8b  Nc   A&&0 x  F&s  7)) jh&@+= l l":wPyO}+и/,%++9%<и,B01'&&'''&7667&&'''&7>76&##"5543326676&##"554332'.'''&67>7632#"5433232## &H!W0  -&AP.  'B5%    E'#0 x  H(2c 7cSC?Rb8CpS0 0 )) D=#?q+  2>#0]' ILL$  .1$E9s6 BL-@  Lb ".;D$$E<0 % 76&##"5543326676&##"554332&&''&&'''&6766'4327>'4632#"5433232## &G#Y/  *#?J.  '=/#    B&$0 x G'?YM11NYA b[ 0 "- .#/ ZU ])) _<"32#".'&&'''&7667&&'''&7>76&##"5543326676&##"55433232>54.#"%#"5433232##*LjA76&##"5543326676&##"554332&&f&&f !! M.  8.8C+ &@0q   7%-Z k  + $% fGlP &)?x9  DNC!5f0 )bgi1  $PR!MJJ  vo/,&  "3pdMi/t/cиc/^NиtOиO/toUиt[и[/tkиk/oR/q/*#+kx+O\+qR9\O9#9и*?01'&&'''&7667&&'''&76676&##"554332676&##"5543327354332##"55###"54332!2##"554&#!"&5546N <!U1 03A*  KUp   8 D] n#<"]f&&f&&R /)  a.7e'  10#F" 9}@  1*5c` AJ!1 #)$3Okwp+`e+WR+pW9w>и>/`PиR]иWT/{l++$+Q^+^Q9$;и+A01'&&'''&7667&&'"''&7>76&##"5543326676&##"554332734332##"5###"54332!"&5543323!2O !?!T. 243Q  #<.p   : * ] n %@$\f&&f&&/)  70 =k&  82VF GKL  6563l7 MS$5 iFe$)$5qy1*+++и/и/!к89*nиn/ии/.+[T+ %+++E9Tkи[q01#!"&55463!26554&#!"&55463!2#!"3!2'&&'''&7667&'''&76676&##"5543326676&##"5543327354332##"55###"54332y /#-  #-O  : W4 29-B,  HVp   7& ] n:!]f&&f&&#*F*4$F*8 ,4`)  .*6#C  2r>  ('5*Z1  ? / )3|daqe++yt+y кy9ePиP/tmиm/rиtиyv/j +>7+b+s+(s97Mи>S01%463!2#!".5'&&'''&7667&&'''&76676&##"554332676&##"554332"3!26554&#354332##"55###"54332 , ,=E <!U1 03A*  KUp   8 D] n#<"  Kf&&f&&x**.7e'  10#F" 9}@  1*5c` AJ!1    3|dj+{+rm+ иmи/rк"r9XиX/{kиmxиmи/ro/+E>++ly+0yl9>UиE[01".546332!546332#'&&'''&7667&&'''&76676&##"5543326676&##"5543327354332##"55###"543323!2655[,, , ; V2 03/V  NSp   7"(] n;"]f&&f&&  jh&/5b)  01CE =t<  /(30^.  C# 1 (Gf f3wPly/S/fиf/aQиSXS^иX|и|/XU/+$+R_+_R9$<и+B01'&&'''&7667&&'''&7>76&##"5543326676&##"5543327354332##"5###"54332'.'''&67>7632O ?!T/ 34B+  #<.p   9 ) ] n #>$\f&&f&&z2c 7cSC?Rb8CpS0 0 F0 '4632354332##"55###"54332N <!U1 03A*  KUp   8 D] n#<"*?YM11NYA b[ 0 "- .#/ ZU f&&f&&f.7e'  10#F" 9}@  1*5c` AJ!1 ![56Y U34T#$ 0e  @9/ :HR)   P'  3pjaub+y++ и / 9Ab&b6bFbVbfbvbbbbbbb ]Abb]lиl/ии/g+>7+q++(97Mи>S0174>32#".'&&'''&7667&&'''&76676&##"554332676&##"55433232>54.#"%#"54332354332##"55#.SqD>pV22Vp>DqS.P <!U1 03A*  KUp   8 D] n#<"#?V3-VB))BV-3V?#f&&f&&f'+C/.C,,E..Ek.7e'  10#F" 9}@  1*5c` AJ!1 --..,tOo{nS+nYиSiܸ^иncиnqP/V/' +[b+dm+PV9PV9 7и'=01%&&'''&767&&'''&7>76&##"5543326676&##"554332&#"5433232##32##'L&`7  >4!>O0  *H7% / !  $-=  / ")-))  `/L8  >NM(?q.  'dor4 ! %,TV++(MW ! %,9q976.~  6Oo{t+nS+Siܺti9t{9nYиi^иncиnV/p+*#+[b+el+#;и*A01'&&'''&767&&'''&76676&##"5543326676&##"554332#"5433232##32##!"&5543323!2 (J"W0  0+&C Q*  Hp  F(#4 x  $I*[))  /)  0A$@t- +;@#4_% 9X  6$D9z< GP0B !!$)$Cpeyf+}+} }ܺ9Af&f6fFfVfffvfffffff ]Aff](f9иии/k+?8++u++8Pи?V0174>32#".'&&'''&7667&&'''&7>76&##"5543326676&##"55433232>54.#"%#"5433232##32##*LjA01%&&'''&7667&&'''&7>76&##"5543326676&##"554332&#"5#"554334332'L&`7  8!>O0  *H7% / !  $-=  / ")-) )`/L8  F&M(?q.  'eor4 %,TV++(MW %,9q976.$ $ HpCQg{wW\+\hиWnи\tиW}k/Y/+$+hu+S`+Yk9Yk9$<и+B01'&&'''&7667&&'''&7>76&##"5543326676&##"554332!2##"554&#!"&554654332##"5#"5543 &G#Y/  *#?J.  '=/#    B&$0 x G'O/) $ )) Z<" ˤ7 $ H|Ceu۸/q/и/iи/qи/q q и /iи/ 9iи/(i98и8/qvи |иqи y/n +?8+v+f+8Pи?V01%463!2#!".5'&&'''&7667&&'''&7>76&##"5543326676&##"554332"3!26554&#54332##"5#"5543 , ,] &G#Y/  *#?J.  '=/#    B&$0 x G'  )) **<"8b  Nc   A&&0 x  F&  )) jh&@+= l l5 $ ?wPyE{+{и}/,%+z+%<и,B01'&&'''&7667&&'''&7>76&##"5543326676&##"554332'.'''&67>763254332##"5#"5543 &H!W0  -&AP.  'B5%    E'#0 x  H(z2c 7cSC?Rb8CpS0 0 )) D=#?q+  2>#0]' ILL$  .1$E9s6 BL-@  Lb ".;D$$E<0 % 76&##"5543326676&##"554332&&''&&'''&6766'4327>'463254332##"55#"5543 &G#Y/  *#?J.  '=/#    B&$0 x G'?YM11NYA b[ 0 "- .#/ ZU )) _<"32'&&'''&7667&&'''&7>76&##"5543326676&##"5543324.#"32>54332##"5#"5543K.Oi76&##"5543326676&##"5543324332##"5#"5543##"543324 !" :]  :+7C+ &@0q   6#-V g  $& G&&g E&& '*r  FLB!5f0 )bgi1  $PR!LIK  9q8/.&  "$! $ ~3pj`tջa+{v++ и / 9Aa&a6aFaVafavaaaaaaa ]Aaa]kиk/vи/f+=6+u+p+6Lи=R0174>32#".'&&'''&7667&&'''&76676&##"554332676&##"55433232>54.#"54332##"55#"5543##"54332.SqD>pV22Vp>DqS.K ;!S/ 03/U  KUp   9A[ l<# #?V3-VB))BV-3V?#>&&` >&&'+C/.C,,E..Eb1 7d&  10FF 9}@  1*6ab !H$#5 --..& $  %tCPl{Wj+WR]иjaиReиWnZ/T/(!+Qf+e^+^e9ZT9!8и(>01%&&'''&7667&&'''&7>76&##"5543326676&##"554332&54332##"5#"554335#"5543'L&`7  8!>O0  *H7% / !  $-=  / ")-v)) `/L8  F&M(?q.  'eor4 %,TV++(MW %,9q976. # # HpPmpi++i9T9Ap&p6pFpVpfpvppppppp ]App]\ip9pnиn/|9|/A||]A|)|9|I|Y|i|y||||||| ]и/иии/+$+++99$<и+Bиܸkиk/01'&&'''&7667&&'''&7>76&##"5543326676&##"554332&&''&&'''&6766'4327>'463254332##"55#"554335#"5543 &E#X.  *#?J.  '=/#    B&$0 x  "E&?YM11NYA b[ 0 "- .#/ ZU ))t tt _;!76&##"5546332!"&55463!54332!2#"554&#!"&55463!2  D#8J 8 --+-.-#[?  :[ 'OF8 y )~ )  /  ,"H-  ,&(    9  ,K1  )/2   #TT"i$#)<Rjw/`/gи/4`g9oиo/vlиl/zk+ +dW+ IиPиWh_016676&##"554332'&&'''&67667&&'''&67>76&##"5546332#!"&55463!54332!2!"&5543323!2 G&0G 8*--.\#$[=   ?-rB 'OF8 x  )~ ~/)     / K/  ,&(   4#>   .(D  +26  $"#]]q')$+ %+++>wиE~и01#!"&55463!26554&#!"&55463!2#!"3!26676&##"554332'.'''&67667&&'''&676676&##"5546332#!"&55463!54332!24 /#-  #-Q @!3G  8++( +-,Iy  6T [# y  )~ z#*E*+#;*8   (=( ,&  =;  '@3 M0  i"#BB<|fvwj++ r+I9 o +' +g+w+ ]и'dиw017463!2#!".56676&##"554332'.'''&67677&&'''&67>76&##"5546332"3!26554!"&55463!54332!2 , ,>  D#8J 8 --+-.-#[?  :[ 'OF8 y   )~ _**  ,"H-  ,&(    9  ,K1  )/2  u ~ ~ #SS"76&##"5546332#!"&55463!54332!23!2655,, ,  D#6J 8 --+ -.-#[?  :[ 'OF8 y  )~ _  ba&#  - H/  ,&(   9  ,K1  )/2  E"#LL\ \76&##"5546332!2#!"&55463!546332'&&'''&67>7632  D#6J 8 --+  -.-#[?  :[ 'OF8 y~  +  -p t+*nCqR1 0   - H/  ,&(  9  ,K1  )/2  \""\  DZ " mEGr % 9HP' 326676&##"554332'.'''&67677&&'''&67>76&##"55463324.#"32>#!"&55463!54332!2*@^m-EE@1@^m,-m_?  D#8J 8 --+  -.-#[?  :[ 'OF8 y6LQQK55KQRK6  )~ 3@$  %2"2?$ $@T  ,"H-  ,&(  9  ,K1  )/2  !''! ((C""QQ76&#!"5543!2#!"&55463!54332!2#!"5543!2?!3G  8 *+(  +-,#`?  7T [# yH- HJI!  #X^\'Kc   Oq) J %0  )~    (=( ,&  =  '@3 M0  ?'  $  3 )-/ ! #""==q6tLb/P/xиx/и/x?и?/xGx9PaVиaS/M/ +n+X_+ "и(кGMS9nkиk/uиu/xиx/01''&&76676&##"5543326676&##"554332'&&'"''&767&&'#"5433232##6'"&554636754633666_ Sl 0G *7 1& !"# "D$\5  *$:!W}))  MNII AA+BV 2u7 $*&&3+X* #+6:  0-S! /*J~y"E #  "   IjMcyǸ/X/и/ ии/19XOиO/XSXgиSmиSwиSj/U/ +O\+ov++1vo9 DиJии/016676&##"554332'&&'''&67667&&'''&676676&##"554332!2##"554&#!"&5546%#"5433232##'6##"&5546335433266Y :$9  u 4= >!K  2!Q* >d 9/)  B))   A} ) K  & D ''!!! )&@  %&F &a2 )#)$4"x" #LLIt1ϻ*+++и/и/!и!/c9и/.+D=+ %+++=vиD|ии/ܹи/и/01#!"&55463!26554&#!"&55463!2#!"3!26676&##"554332'&&'''&67667&&'''&676676&##"554332#"5433232##'6##"&5546332754332666 /#- #-[ # 8(=  u 5 5 9H(  +P- Ea 9))   Qd &) H"*D*4"B*7 $ < (&  %5  < %O+ )U"c" #JJIpMog++Ao&o6oFoVofovooooooo ]Aoo]go9/19Qg9Xgo9olиl/и/ +{++19 DиJи{iиi/и/и/016676&##"554332'&&'''&67667&&'''&676676&##"554332&&''&'''&67>'432766'4632#"5433232##'6##"&554633275433266Y :$9  u 4= >!K  2!Q* >d 9?WL7o+XB 6I, 0 D; ?B/ VP R))   Qd &) H  & D ''!!! )&@  %&F &a2 )P/-J" >V,K$$ 8@K.   3d){S  !Bf"  r "b" #QQ'tdk+++и и/ //(+y++(bи/hиyvиv/yи/и/0134332##"5###"5433266764##"554332'&&'''&67667&&'''&67>76&##"5543326"'"&55463754633266g&&g&&v  6#,  Y p #E B8_ %3?M :3*  | 8">>@$8x; 1r?+1ZlPG$.)U- @:(  *LJ .K5 27; ( # " Bp8T+IN+@;+@99/! !9и/I9и;Fиcиc/9@=/g`+:G+-+-и/KиK/G:9`иgии/и/и/01&&''&'''&67>'432766'4632354332##"55###"543326676&##"554332'&&'''&6767&'''&676676&##"5543326#'"&554632754332>~?WL7o+XB 6I, 0 D; ?B/ VP g&&g&&w ,/ -  ` w4 3 4A# ('D& >O q 9?FE:8 1j3)860P/-J" >V,K$$ 8@K.   3d){S  !Bf"  ׿n  &;  ''    #8  #7 &L,  ) # "USBtCLX/P/tиt/}Gt}9PWM/S/ +xh+ "и(кGMS9hcиc/h}oиo/}tиt/01''&&76676&##"5543326676&##"554332'&&'"''&767&&'#"543326"'"&55463667546332>k Sl 0G *7 1& !"# "D$\5  *$:!W))!NST(QUP DKL +!HFBV 2u7 $*&&3+X* #+6:  0-S! /*J~ #  "  KbNdpZS++oh+29ok/^O+ +{+ EиKи{и/и/01>76&##"554332'&&'''&67667&&'''&676676&##"554332!"&5543323!2'#"54332'6##"&554633275433266e8% u 4"B D J#  0$X* Gm 9/)  #))  Xg ' Iz* &&" ''#-- 1%C  (&H +g0 )$)$[" #XX MtY1*+++и/и/!и!/c9/.+D=+ %+++=vиD|ии/и/01#!"&55463!26554&#!"&55463!2#!"3!26676&##"554332'&&'''&67667&&'''&676676&##"5543326##"&554633275433266#"54332Y /#- #-[ 8(=  u 5 5 9H(  +P- Ea 9:  Xg ' H))"*D*4"B*7 $ < (&  %5  < %O+ )" #FF _$M|Daqe+++ и /E9mиm//j +' +b+|+ Xи'^и|и/и/017463!2#!".56676&##"554332'&&'''&67667&&'''&676676&##"554332"3!26554&#'6##"&554633275433266#"54332 , ,In :$9  u 4= >!K  2!Q* >d 9+  0  Xg ' K))d**  & D ''!!! )&@  %&F &a2 )   # #FE gM|Cisk+++k к&9rиии/n+@9+j+~+9Oи@Uи~и/и/~и/01".546332!546332#''&67667&&'''&676676&##"5543326676&##"554332'&&'3!26556##"&554633275433266#"54332;,, , 1!R+ B` 9 ;%6  u 4 ; =I  G  Xg ' H))`c&@   #%B )V2 )0  % ? ''!& *!=QW W8" #PO P326676&##"554332'.'''&67677&&'''&67>76&##"55463324.#"32>32#!"&554633546332!546332*@^m-,m^@@^m,-m_?  D#8J 8 --+  -.-#[?  :[ 'OF8 y6LQQK55KQRK6(  ++1># #>10># #=R  ,"H-  ,&(  9  ,K1  )/2  "(("!))k""k kk 76&##"55463326676&##"554332'&&'"&5!"&55463!2#!#  )$$" )k@0M=/ <O#4F 7 QZ ,W)P| R wX   -T&  69<#  +3%B0_-  ))4B!8 ` D"" 76&##"5546332 { R x/)    E%c6 8--+-.-&c@   =[ 'OF8 y""#)$  -AX  ,&,$  %A    .K1  )/2  <Pj/T/)и)/Tg2Tg9oиo/HиH/oNиN/ovlиl/zk+ +]V+ GиNиVe01676&##"554332'&&'''&67667&&'''&67>76&##"5546332"&55!"&55463!2#!#!"&5543323!2 G&W6 8+--.Z#Ry "F-rB 'OF8 xs R !/)    /B]  ,&.$   3U>   2(D  +26  F "" ')$76&##"5546332"3!26554&# ,{ R x ,>  E%c6 8--+-.-&c@   =[ 'OF8 y  `*|""|*  -AX  ,&,$  %A    .K1  )/2  v   76&##"5546332"&55!"&55463!2#!#3!2655,, ,  E%c6 8--+-.-&c@   =[ 'OF8 y&{ R x  ]\&#  -AX  ,&,$  %A    .K1  )/2   "" {V V75!"&55463!2#!676&##"554332'.'''&67667&&'''&67>76&##"55463324.#"32>*@_m,EE@26Ra,{ R x,bR5  E%c6 8--+-.-&c@   =[ 'OF8 y6MQQK66KQQM6 3?#  $2"-<$x""x$<]  -AX  ,&,$  %A    .K1  )/2  !&&! ''ItCOc/\/uиu/n и /nи/1un9\Pи\WS/Y/ +P]+v+1YS9 FиLиvmиm/v{и{/016676&##"554332'&&'''&67667&&'''&67>76&##"5543324332##"55#"554376##"&5'"&554632>7>g >!6 | 4"H #C#T- $7$U+ =7- 9})) {  @K+ GD< QUO#RRJh/$O( ''/12  2,R   -+K 16; )w " "   6" LoK//и/и/и/&и&//9O9W9eиe/iиi/и/ +v+++ BиHиvdиd/и/и/и/и/016676&##"554332'&&'''&6767&&'''&676676&##"554332&&''&&'''&6766'432766'46324332##"55#"554376##"&55"'"&554632>7>a 4$;  p 4 : <"L   + O* >_ 9>^N68L5eY 0 C6=D/ 0>$ )) x AJ+BA; OTO#NNJ%? ''$ +#<  "#; &O2 )L3+J H+W? " +oJ  7['k@   81)   # " {"   "LvL_s`++{+"кC9A`&`6`F`V`f`v``````` ]A``]jиj/tи{w/e+&+o++t+Vи&\и}и}/и/и/и/0174>32#".6676&##"554332'&&'''&6767&&'''&676676&##"55433232>54.#"%4332##"55#"554376##"&55"'"&554632>7>2Vp>8oX77Xo8>pV2 4$;  p 4 : <"L   + O* >_ 9T,ET)$TH00HT$)TE,)) x @I+CB; OTO#NNJ '8%$8((9%%9%? ''$ +#<  "#; &O2 )K""# #4 " "   "ItCO[/S/mиm/f и /fи/m*и*/1mf9SZP/V/ +n+1PV9 FиLиneиe/nsиs/016676&##"554332'&&'''&67667&&'''&67>76&##"554332#"543326##"&5'"&554632>7>g >!6 | 4"H #C#T- $7$U+ =7- 9))  >H+ JG@ QUO#RRJh/$O( ''/12  2,R   -+K 16; )~"   ," 76&##"55463326676&##"554332'&&'"&5#"&55463!2####"&5##  )$$" )k@0M=/ <%&&3F 7 QZ ,W)P R +X   -T&  67<# "+3$  /_, " ))4B!8 ` D"" D <Rb'`W+ + HиO016676&##"554332'&&'''&7667.'''&676676&##"5546332#!"&55463!2 %()0O   $*_2  /`+0p6  1&'%*g5U) <  R b&-&# 4u9 "  C!*=C&Aq% ( #$4`#  7Q "*""<|hx/t/и/lи/tи/t t и /lи/J 9 q +)"+i+y+"_и)f017463!2#!".56676&##"554332'.'''&6767&&'''&67>76&##"5546332"3!26554!"&55463!2 , ,>  F%/H 8-., -.-)a?  C.p? )OE6 y   R i**  - M0  ,&) )A    0(A )04     #"76&##"5546332'&&'''&67>7632%#!"&55463!2J'-H 8./, ..-+c> "E-p= (ND6 y -p t+*nCqR1 0   R 1#Q/  ,&+ .G  4*D +26   DZ " mEGr % 9HP' ""326676&##"554332'.'''&6767&&'''&67>76&##"55463324.#"32>#!"&55463!2*2AEEEEA22AEDEF@2  F%/H 8-., -.-)a?  C.p? )OE6 y6MQQK66KQQM6  R "3''3"!3&&4]  - M0  ,&) )A    0(A )04  #", ,""+ +Q""%tCP\K[T+[^Q/W/(!+QW9QW9!8и(>01%&&'''&7667&&'''&7>76&##"5543326676&##"554332&#"54332'L&`7  8!>O0  *H7% / !  $-=  / ")-))`/L8  F&M(?q.  'eor4 %,TV++(MW %,9q976.~HpCQgseW\+\kиWqиWun/Y/+$+S`+Yn9Yn9$<и+B01'&&'''&7667&&'''&7>76&##"5543326676&##"554332!2##"554&#!"&5546%#"54332 &G#Y/  *#?J.  '=/#    B&$0 x G'O/) $ /))Z<"  h7H|Ceuɸ/q/и/iи/qи/q q и /iи/ 9iи/(i98и8/qyи и |/n +?8+f+8Pи?V01%463!2#!".5'&&'''&7667&&'''&7>76&##"5543326676&##"554332"3!26554#"54332 , ,] &G#Y/  *#?J.  '=/#    B&$0 x G'  O))**<"8b  Nc   A&&0 x  F&  7))jh&@+= l l/HpKey߸/}/ и /} 9 pAp&p6pFpVpfpvppppppp ]App]и/( p9 8и8/}fиf//u+?8+k+8Pи?V01%#".54>32'&&'''&7667&&'''&7>76&##"5543326676&##"5543324.#"32>7#"54332K.Oi76&##"5543326676&##"554332'&&'''&76676&#!"5543!2'#"54332 &G#Y/  *#?J.  '=/#    B&$0 x G'`-Q@@= @LDT Vv P))_<"32#"543324.#"32>7!"&55463!2'#"&5546332 &E%%>R--R>%/))*9 9**9 9* L u B/  // .   lUt*@LC?.+?4и?N+/1/KD+(!+6=+01'.'''&676676&#!"5543!2#"5433232####"5543321 245  6537My:  H4)) -T(2,$  ! '.2;b% ! :x # %Q~"*^  \p)?Ua]/4+4Cи/Iи/Sи/c1/F/`Y+++8+KR+01'&&'''&7>76&#!"5543!2!2##"554&#!"&5546%#"5433232####"554332i 6i03~N  FjTC  J4%2 -_/p/) $ /)) <'*L!$  =?G+  # (*$C #5 #)$2"  _)?Uacb/-/>3иbDиD/KAиA/>c0/O@+`Y++5<+01%'&&'''&676676&#!"5543!2#"5433232##!"&5543323!2##"554332_ 3f.6H r>  J4+6*^.)) /)  A)0M $ 6_  # ,*(G &< "$)$a  [e(<RvYg`+3+Q@+@Q9/A]A)9IYiy ])A33]A3)393I3Y3i3y3333333 ]QFиQи/)C//.+k\++8++w+HO++DиD/\XиX/koиo/01'&&'''&67>76&#!"5543!2#".54>32#"5433232#####"&5543323326676676%!"&55463!24&#"326##"554332#"&554332d 2b-8N 5h^P  J4%$:*Y- *:$$:**:$$:*9)) &)$/)  &# & _r e0--11--0A ';&-M $ 6@J(  # (*@9"4 *** )>"[#) $ & ' "! ?  % $ Yq1Yo{˸|//|и/*и/и/".и./]и"cи"mиtиt/yиy/"}`/y/.+zs+ %+OH++el+zaиa/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'"''&676676&#!"5543!2#"5433232####"5543325  /#-+ #-o  1`-9Q w=  J4%"8)W+)) #*@*,$>*2 :%-J% *xP  # (*<7!3 E"  \|#Mcop//pи/и/и/  и /и/Qи Wи aиhиh/mиm/ qT/ +ng+B;++Y`+017463!2#!".57"3!26554&#''&&'''&7>76&#!"5543!2#"5433232####"554332 , ,]]  Z 6i03~N  FjTC  J4%2 -_/)) a**   <'*L!$  =?G+  # (*$C #5 O"  [|BLbno//oи/Dи/D ии/KиPиVи`иgиg/lиl/pS/G+mf+C+81+X_+01".546332!546332#'&&'''&76676&#!"5543!23!26557#"5433232####"5543320,, ,k 5g/4N ~9  J4%&=,^/   7)) a_&>'+M"$ 6U # (*E=$7 X X"  _v%Lbn?aP+aVиapS/mf++X_+01%'&&'''&676676&#!"5543!2'&'''&67>7667663#"5433232####"554332h6j03}M4  J4'19,d0E 0] r.k@cK4/ ))  C*.L" " 54327654632'&&'''&7>76&#!"5543!2#"5433232####"554332k.K6p(YD3F, 0 C9 |/  UQ  6i03~N  FjTC  J4%2 -_/)) G],O'BW-N% " 5324.#"32>'&&'''&7>76&#!"5543!2#"5433232####"554332$.Oi76&#!"5543!2#"5433232####"554332?)7< NK} Nv R 6i03~N  FjTC  J4%2 -_/)) O,  " (6)  X6 # .<'*L!$  =?G+  # (*$C #5 M"  YtcGSaT//Tи/и иU //RK+E>++0134332##"5###"54332"'.'"''&67>76##"5543!2'##"554332~&&~&&-'[- ,,+-j: 6_N9   4IlPG-U(3W &,/6\$  "U[^- # %  [pdDZfg/O/gи/ иO и /OJиOи/OFиF/Jh/L/e^+B;+FS+ +01%#"54332354332##"55#&'&&'''&676676&##"5543!2!2##"554&#!"&5546##"554332&&&& 1#J& #Q$0sB l0  2& /)  3<6#8 "A#+H# .xK # (_#)$:  \BXdeNG+++и иf/RC+c\+@9++0134332##"5###"54332'&&'''&67676&##"5543!2!"&5543323!2##"554332&&&&"6%O&&T"0p= s  4$u/)  4_IvB>(@  E$0M# Z # 'D$)$V  [qy1Mw*+BG+94+4и/и/9!иB2и4?и96/.+{+ %+un+3@++JиJ/01#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332&'&&'''&67>76&##"5543!2'##"554332y /#-  #-O &&&&.!H$ "N#_ /\PB  4$R#*@*,$>*2 }Ǻ910 "9 N3$ /9A" # '  [|d#?ht+49++&++ и&и/4$и&1и+v(/ +sl+f_++%2+s<01%463!2#!".57"3!26554&#354332##"55###"54332&'&&'''&676676&##"5543!2'##"554332 , ,=]  d&&&& 1#J& #Q$0sB l0  4$R_** ~ ~ <6#8 "A#+H# .xK # '  [|d7`jvb+,1+#+b ии/#и,и)иiиi/#x /e+un+a+^W+*+u4и4/01".546332!546332#354332##"55###"54332&'&&'''&676676&##"5543!23!2655##"554332[,, ,u&&&&/"I% #P#0tC j/  4$  `^&Ǻ<2"7 "@#*G# -tI # '\ \  \vCjviw//wи/и иRиR/x/un+A:++0134332##"5###"54332'&&'''&67676&##"5543!2'&'''&67>7667663##"554332&&&&+#O%&Q"1q> s  4$F 0] r.k@cK4/   @'>  D#0O# Z # 'e @W  ".Bk  % +3=%(  [m7S|ջ +HM+?:+?9A & 6 F V f v ]A ]  9 и/H8и:Eи }и}/?54327654632354332##"55###"54332&'&&'''&676676&##"5543!2'##"554332.K6p(YD3F, 0 C9 |/  UQ &&&& 1#J& #Q$0sB l0  4$RG],O'BW-N% " 53234332##"55###"543324.#"32>&'&&'''&676676&##"5543!2'##"554332n2Us@FsS--SsF@sU2&&&&)DX.4X@$$@X4.XD) 1#J& #Q$0sB l0  4$R%<**<%$;**;!' '% %5<6#8 "A#+H# .xK # '  UtJVm+ иܸииX//UN+HA+ ++A и /01#"5433232##32##'.'''&676676&#!"5543!2'##"554332))  1 245  6537My:  H4E~  -T(2,$  ! '.2;b% ! :x # %  _)I_kqUN+H-+H3и-Cܸ8иH=иHm0/YJ+jc++5<+?F+01%'&&'''&676676&#!"5543!2#"5433232##32##!"&5543323!2##"554332_ 3f.6H r>  J4+6*^.))  /)  A)0M $ 6_  # ,*(G &< !!$)$a  [e(<\qj+3+[@+@[9/A]A)9IYiy ])A33]A3)393I3Y3i3y3333333 ][Fи@VܸKи[Pиjи/[и/)C//.+uf++8++HO+++RY+DиD/fbиb/uyиy/01'&&'''&67>76&#!"5543!2#".54>32#"5433232##32#####"&5543323326676676%!"&55463!24&#"326##"554332#"&554332d 2b-8N 5h^P  J4%$:*Y- *:$$:**:$$:*9))  &($/) &# & _r e0--11--0A ';&-M $ 6@J(  # (*@9"4 *** )>!f!O[#) $ & ' "! ?  % $ Yq1Yy*+"+и/]и"cиsܸhи"mи"wи"`//.+}+ %+OH+el++ov+aиa/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'"''&676676&#!"5543!2#"5433232##32####"5543325  /#-+ #-o  1`-9Q w=  J4%"8)W+))  #*@*,$>*2 :%-J% *xP  # (*<7!3 E!m!r  \|#Mmy+ +Qи Wиgܸ\и aи kи {T/ +xq+B;++Y`+cj+017463!2#!".57"3!26554&#''&&'''&7>76&#!"5543!2#"5433232##32####"554332 , ,]]  Z 6i03~N  FjTC  J4%2 -_/))  a**   <'*L!$  =?G+  # (*$C #5 N!x!}  \q'Qq}ٻ +pU+pи/Uи/A&6FVfv ]A]p[иUkܸ`иpeиpX/#+|u+F?+]d++gn+01%#".54>324.#"32>'&&'''&7>76&#!"5543!2#"5433232##32####"554332$.OiR^n.3+3?и.Eи3Kи.`B/0/EX?/?>Y]V++*7+?L01%'&&'''&7>76&#!"5543!2!2##"554&#!"&55464332##"55#"5543##"554332o 4f/2yK  FjTC  J4%+D+_-/) $ )) p>((G $  =?G+  # (*NB$7 v#)$:" $   sb';Q]g^/)/.)4и^@и@/G=и=/._+/K<+\U++(5+01%'&&'''&676676&#!"5543!274332##"5#"5543!"&5543323!2##"554332i 4g/h r>  J4%2D+`/)) /)  B*Z<$ 6_ # (*YG'= 8t $ #)$a  mqX1Ymyz//zи/*и/и/".и./Zи"`иfи"{]/w/.+xq+ %+OH++Zg+x^и^/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'"''&676676&#!"5543!274332##"55#"5543##"554332X  /#-+ #-o  1a-7M w=  J4%%?)W+)) p#*@*,$>*2 ;%*E% *xP  # (*A76&#!"5543!274332##"55#"5543##"554332 , ,]]  w 4f/2yK  FjTC  J4%+D+_-)) pa**   >((G $  =?G+  # (*NB$7  $   o|CBL`lm//mи/Dи/D ии/KиMиSиYиnP/G+kd+C+70+MZ+01".546332!546332#'&&'''&76676&#!"5543!23!26554332##"55#"5543##"554332S,, , 5f/e ~9  J4%5!+^.   )) pa_&>'S@$ 6U # (*&D $6 X X $   sv$K_k?SX+XLиSmO/jc++LY+01%'&&'''&676676&#!"5543!2'&'''&67>76676634332##"55#"5543##"554332r6h0d4  J4'3>,a0H 0] r.k@cK4/ )) p B*V@ " 54327654632'&&'''&7>76&#!"5543!274332##"55#"5543##"554332.K6p(YD3F, 0 C9 |/  UQ  4f/2yK  FjTC  J4%+D+_-)) pG],O'BW-N% " 5((G $  =?G+  # (*NB$7  $   pqJ'PdpǸq/]/Xи/q и /]и/ A&6FVfv ]A]]QиXrT/#+oh+F?++Q^+01%#".54>324.#"32>'&&'''&7>76&#!"5543!274332##"55#"5543##"554332J.Oi((G $  =?G+  # (*NB$7  $   Ytd+AMYYZ/G/Z-и-/2-8иGB[J/D/XQ+)"+,9+01"'.'"''&67>76##"5543!24332##"5#"&55463##"54332##"554332-'[- ,,+-j: 6_N9   4&&{ n&&=-U(3W &,/6\$  "U[^- # %h"~6  [pd(>T`lm/X/_-иX3и3/mKиK/?иKF_n0/[/kd+&+*7+?L+kBиB/01&'&&'''&676676&##"5543!2!2##"554&#!"&55464332##"55#"&55463#"54332##"554332"7"K& #S#+h? g0  2& /)  h&&v [&&>'@<"6 "?#%C# 1uK # (U#)$;"  \&<R^j]HA+-(+SX+(3иSl[/L=+ib+$+'4+01'&&'''&676676&##"5543!24332##"5#"&55463!"&5543323!2'##"54332##"554332%<#N%&R"Wq g.  4$&&v u/)  &&>KC&;  A#R<# 9N # '-"$)$$  [qy1Zp|*+bg+qv+vи/и/q!иg[иqy/.++ %+XQ++[h+^и^/01#!"&55463!26554&#!"&55463!2#!"3!2&'&&'''&676676&##"5543!254332##"55#"&55463##"54332##"554332y /#-  #-O b 7!I$ #P#*g@ f,  4$&&v i&&>#*@*,$>*2 <9. "8!"@$ -uA # 'g>"  [|d#Lbnz+TY+ch+c иhи/YMиc|k/ +yr+JC++MZ+yP01%463!2#!".57"3!26554&#&'&&'''&676676&##"5543!24332##"55#"&55463##"54332##"554332 , ,=]  "7"K& #S#+h? g0  2&&&v i&&>_** ~ ~ @<"6 "?#%C# 1uK # (p  "  [|dEOeq}G+W\+fk+G иkи/fиkNиN/\Pиfn/J+|u+F+C<+P]+|SиS/01".546332!546332#&'&&'''&67>76&##"5543!23!26554332##"55#"&55463##"54332##"554332[,, , 9"K& #S$V{ 6WH;  2&  &&v i&&>`^&=<"6 "@#K:# 59@%  # (\ \/"  Zv'Ndp|]}/j/e6и6/}TиT/OT^иe~m/{t+%+]V+01'&&'''&676676&##"5543!2'&'''&67>76676637##"55#"&5546334332##"54332##"554332"2$P&&T"-h< k-  4$V 0] r.k@cK4/ r&v v&&&>B='>  C#.M" ;P # 'e @W  ".Bk  % +3=%+"d$  [qn'Pfr~ +X]+gl+lи/A&6FVfv ]A]]Qиgo/#+}v+NG++Q^+}T01%#".54>324.#"32>&'&&'''&676676&##"5543!24332##"55#"&55463##"54332##"554332n2Us@FsS--SsF@sU2H)DX.4X@$$@X4.XD)"7"K& #S#+h? g0  2&&&v i&&>%<**<%$;**;(' '% %5@<"6 "?#%C# 1uK # (p ".  [tCBNa+ иииP //MF+@9++ +014332##"5#"554335#"5543''&&'''&676676&#!"5543!2'##"554332)) ?$D*i2  /l,6Jt:  H4E\ # # ]W6Z a3;c$ " :| # %  sb'CYeyOH+/8+/)4и)<и8@и/g+/SD+d]++<5+(и(/=01%'&&'''&676676&#!"5543!254332##"55#"554335#"5543!"&5543323!2##"554332i 4g/h r>  J4%2D+`/)) /)  B*Z<$ 6_ # (*YG'= <n # # S#)$a  pm7`| +hq+qh9A & 6 F V f v ]A ]  9 и/hb-и-/bmиbuиqyиhd/+VO+n++wt++и/Oaиa/nuav01&'&'&'''&67>54327654632'&&'''&7>76&#!"5543!254332##"55#"554335#"5543'##"554332.K6p(YD3F, 0 C9 |/  UQ  4f/2yK  FjTC  J4%+D+_-)) |G],O'BW-N% " 5((G $  =?G+  # (*NB$7 +e # q #  Ytd*FR^k2;+GL+2,7и,?и;CиG`O/I/]V+(!++@+?8+01"'.'"''&67>76##"5543!24332##"5#"554335#"5543##"54332##"554332#:'[- ,,+-h9 6_N9   4&&s ss f&&=]P3V &+/6Z#  "U[^- # %4L # # ~6  \&BXdpNG+.7+Y^+.(3и(;и7?иYra/RC+oh+$+;4+'и'/<01'&&'''&676676&##"5543!254332##"55#"554335#"5543!"&5543323!2'##"54332##"554332%<#N%&R"Wq g.  4$&&k kk j/)  &&>KC&;  A#R<# 9N # ' # q # `$)$$  [qn'Plx +Xa+mr+rи/A&6FVfv ]A]XR]иReиaiиmu/#+|+NG++e^+GQиQ/TиQf01%#".54>324.#"32>&'&&'''&676676&##"5543!254332##"55#"554335#"5543##"54332##"554332n2Us@GsR--RsG@sU2H)DX.4X@$$@X4.XD)"7"K& #S#+h? g0  2&&&i ii \&&>%<**<%$;**;(' '% %5@<"6 "?#%C# 1uK # (z # Z # +  <*DP7,=+B4+OH+)"+4-<01'.'''&676676&#!"5543!2!2#!"&55463!546332#!"5543!2S5 LLG T_a(MV dO Ε  +n ; " #8 " !oK  # ""   76&#!"5543!2#!"&55463!54332!2!2##"554&#!"&5546#!"5543!29L8>  OUU'GS F|qi4r N"  ) /)  4)$ "  + " !%- # 7""ff#)$u  <'?Uagb/5/<bDиD/KAиA/HA+`Y+%+9,+,=4иAO01'.'''&676676&#!"5543!2#!"&55463!54332!2!"&5543323!2#!"5543!28R;<   OWX(HN fr Q  ) /)  &(( "  -  " M4  # ;""__')$s  76&#!"5543!2#!"&55463!54332!2#!"5543!25 /#-  #-Q m=#>@@ S\Z$FT <|k$r /5  ) z#*5*"#2*(     " ! """SS  <|>NfrqB+c\+ J+ 9 tG +qj+<5+?+`S+Sd[017463!2#!".5'.'''&67>76&#!"5543!2"3!26554#!"&55463!54332!2#!"5543!2 , ,>9L8>  OUU'GS F|qi4r N"    ) P**q)$ "  + " !%- # 7 q q ""__  <|H`jvb+]V++b к9iиxe+un+a+D=+ZM+M^U01".5546332!546332#'.'''&67>76&#!"5543!2#!"&55463!54332!23!2655#!"5543!2,, ,6G7~<  LRT&HX I~pg2r '0  ) _  ZVU&&""  ) "  $* # #""[[R R  76&#!"5543!2#!"&55463!54332!2'&&'''&67667432#!"5543!29L8>  OUU'GS F|qi4r N"  ) h &m t*&y . l4)$ "  + " !%- # 7""jjp  :X !fA?h! % )K T  32'.'''&67>76&#!"5543!24.#"32>#!"&55463!54332!2#!"5543!2*2AEEEEA22AED,l_A\9L8>  OUU'GS F|qi4r N"$7MQRK66KRQL7  ) /##/."  "<)$ "  + " !%- # 7$$##$""ii  ?t;cop// иpVиV/V9_q//ng+ZI+92+ +9IDиD/I_PиP/_TиT/_VиV/01#"5433232##'&&'&''&76676&#!"5543!26"'"&554633767546332>##"554332)) 9#*f/-+D>9>3и>0/HA+}v+%+l`+5<+AO`ipиp/01'&&'&''&7>76&#!"5543!2#"5433232##!"&5543323!26##"&55463!5433266##"554332++@-l0   7A9w;  -b_U! I%( )) /)   Qd  )D}v*'%#  (,"  "*1 ! ""$)$" #ed  Iq1]sٻ*++ra+aи/и/r!и!/5!9rgиrd//.++ %+YR++ip+p>и>/eиe/pи/~ܹи/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'&''&7>76&#!"5543!2#"5433232##'6##"&55463!5433266##"5543326 /#- #-[ 1&f3  4?;{9  .b]U" I%( ))   Ks  )F~y"*1*"-*$ ! ##"  #* ! ""c" #^]  Iq'Si +}+hW+WA&6FVfv ]A]+ 9h]иhZ/#++OH++t+_f+4и4/t}и/01%#".54>324.#"32>'&&'&''&7>76&#!"5543!2#"5433232##'6##"&55463!5433266##"554332-3Tl9>mQ//Qm>9lT3G&@S-2S<""##"554332B9#*f/7+un+%+dV+7EV_aиa/_hиh/01'&&'&''&7>76&#!"5543!2#"54332!"&5543323!26##"&554633275433266##"5543325+@-l0   7A9w;  -b_U! I%( ))/)   Xg )I*'%#  (,"  "*1 ! "Y$)$" #ih   MqX1]|*+zs+"+и/5"9и"и"//.++ %+YR++vh+hqsиs/qzиz/и/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'&''&7>76&#!"5543!26##"&554633275433266#"54332##"554332X /#- #-[ 1&f3  4?;{9  .b]U" I%( o  Xg )I))"*1*"-*$ ! ##"  #* ! "" #^] [  M|C#Lkw+ib+ +& 9oи uи r/ +{+HA++eW+e/и//W`bиb/`iиi/017463!2#!".57"3!26554&#'&&'&''&76676&#!"5543!26##"&554633275433266#"54332##"554332 , ,I]  *6&d4  <}:3yF  jA I%( o  Xg )L))Y** x x % #!'" J. ! "# #b` Y  M|CGQp|I+ng++I к9Pиtиzиw/L++H+C<+j\+j(и(/\egиg/enиn/01".5546332!546332#'&&'&''&7>76&#!"5543!23!26556##"&554633275433266#"54332##"554332;,, ,/&c3  9~;3zF  2e]Q I%(   G  Xg )L))Y\&! #&"  #) ! "T T# #\Z ?   Mv}+Nmy/q/dиd/qxdx9?dx9dkxt/}+' +gY+Ybdиd/bkиk/01'&&'&''&7>76&#!"5543!2'&&'''&766743276##"&554633275433266#"54332##"55433253-i-  6~A7z?  1c]R! I%(  @PX+  j6%&|  Xg )I)),##&*"  ', ! "U  0) !`E;\( %%oH" #cb e]  MqG'Po{ +mf+zs+zи/sи/A&6FVfv ]A]* 9zv/#++LE++i[+i3и3/[dfиf/dmиm/01%#".54>324.#"32>'&&'&''&76676&#!"5543!26##"&554633275433266#"54332##"554332G3Tl9>mQ//Qm>9lT3G&@S-2S<""76&#!"5543!2"3!26554#!"&55463354332354332!2#!"5543!2 , ,>C(9? !OVV'EU G|qi3r N"    )) F**q& "  + " !%- # 7 g g ""[[[[  766#!"5543!2"&5!"&55463!2#!##!"5543!2N2GHD  PXY'SO  <}( P ˖| R wz #4  $  $9   *6> # ""   76&#!"5543!2'#!"5543!2{ R x/)  A&8;  OTT&EW I~qf2r N"n""n#)$$"  ) "  $) # 7  <(BXdoe/,/eGиG/и/,?GNDиD/RC+c\+5)+&+5.=01'.'''&676676&#!"5543!2"&55!"&55463!2#!#!"&5543323!2#!"5543!26P;< NVX(ET hr Qs R !/)  &'%"  + " I2 # ; "" ')$s  +/+6+и/I696B+z+ 9+rk+0+NU+%+0и-01#!"&55463!26554&#!"&5546335!"&55463!2#!32#!"3!2'.'&''&7>76&#!"5543!2'#!"5543!24 /$,  | R x#-Q m@&AB@ !Y`\#KD  <}k!r )7hz*>*!#_""_1*(    ! " $  <|'RbnuV++^+*9p[!+mf+PI+S+ +и01746335!"&55463!2#!32#!".5'.'''&67>76&#!"5543!2"3!26554&##!"5543!2 ,{ R x ,>6K7<  OTT'DY G|pi4r N"  BJ*k""k*q&#"  ) "  $* # 7 i i   76&#!"5543!2"&55!"&55463!2#!#3!2655#!"5543!2,, ,6F3|A #PSQ$ H}qh2r '0{ R x  ZUT&& " - "  "' # #F j""j sN N  76&#!"5543!2'#!"5543!2O }(&y { R x,r6K7<  OTT'DY G|pi4r N""g?Bk! % &yDd""e@R &#"  ) "  $* # 7  75!"&55463!2'.'''&67>76&#!"5543!24.#"32>#!"5543!2 x,bR5@_m,EE@26Ra,{ R 6K7<  OTT'DY G|pi4r N"$6MQQK66KQQM6`+"Z#9+0<# "0 *9$Z"&#"  ) "  $* # 7%%$$  ItC9amn/ /и nKиK/K9Do/ /le+ +70+]L+ 9LCиC/LQиQ/01%4332##"55#"5543'&&'''&76676&#!"5543!26##"&5'"&554632>7>##"554332)) /S*c'  6w10n?{?  T;$U  BF+ IG? QUO$SQJe " 78  +2 ! +]9 # 0"   "   Lo7]q͸/j/и/jee9| |9и/:e9|Dиj^иea/+YR+*+^k++*и/{и{/и/и/01&&''&&'''&6766'43276674632'&&'&''&76676&#!"5543!24332##"55#"554376##"&55"'"&554632>7>##"554332EX.l9K\BdX 0  @8B;/  ZK &4FS   7c5b  e> I%( )) x AJ+BA: OTO#NNJK-OAD+*I  " *iE  3U+_<   <_ ##  ) " A- ! " " l"  g d"  4tdFhtQX+++ к#X9v//sl+ +D=+fY+#9YPиP/Y\и\/fcиc/01##"55#"554334332##"54332'&&'''&76676&##"5543!26##"&5'"&554636766##"554332& &&&z2%O$  .S/*_9c;  ;$%  .h7+9o+ :~9?uOX " k~/ ". ! "V5 # 0"   "   Bp%9O[{E>+&++PU+>P9+3кl>E9l/ePX/B;++#+yi+3,+;Iymdиd/moиo/yvиv/01'&&'&''&76676&##"5543!2##"55#"554334332!"&5543323!2'##"543326##"&55'"&554632766##"554332%>N/S0(Z6O/  C& &/)  &&/c1+r^ 9o9Bt>'"  $" H( ! 4C "  $)$^   |"  JtC%1Yef/)/fCиC/)0C09C<0g&/,/d]+#+UD+&,9D;и;/DIиI/01'&&'''&76676&#!"5543!2#"543326##"&5'"&554632>7>##"554332B/S*c'  6w10n?{?  T;$))  AE+ KI@ QUO$SQJ78  +2 ! +]9 # 0@~"   "   Q[*6]swib+Z:+5.+%b5951/f_+~w++L7+LY;и;/_m01'&&'&''&7>76&#!"5543!2#"54332%"&55'"&554632>7>76#!"&5543323!2##"554332q :g96q8  -]YR" I%( 4)Q- ))8 LJB MSN!NPL #"54332##"554332X /#- #-X -$M&  am8t9  ^E I%( g AE+DD> OSL#OOK))#*+*$+*   #%"" A1 ! "  W T"   L|C#Ks+V]+ +F 9wи }и z/ ++>7++oY+o^UиU/^cиc/ojиj/017463!2#!".57"3!26554&#'&&'&''&7676&#!"5543!26##"&55"'"&554632>7>#"54332##"554332 , ,E]  w6`6a  у I%( ,#J* 2  OTO$ONI))9*v* X X Z," .] ! "  _"  m k"<;  L}CDlvn+OV++n к9uиzии}/q++m+@9+hR+hWNиN/W\и\/hcиc/01".5546332!546332#'&&'&''&76676&#!"5543!26##"&55'"&554632>7>3!26557#"54332##"554332;,, ,-HQ  6c70rE  e> I%( e =E+GGA OTO#NNJ  7))PN&!##" D- ! ""  r o"E E  Lv)Lt/x/^и^/x$^9=^9^W{/++pZ+p_VиV/_dиd/pkиk/01'&&'&''&76676&#!"5543!2'&&'''&766743276##"&55"'"&554632>7>#"54332##"554332n7b63s?  dB I%( 0%M* * @PX+  j6%&Z 32#".732>54.#"'&&'&''&7676&#!"5543!26##"&55"'"&554632>7>#"54332##"5543323Up=8oY77Yo8=pU3G-FT'"TI11IT"'TF-H6`6a  у I%( ,#J* 2 766#!"5543!2"&5#"&55463!2####"&5###!"5543!2N2GHD  PXY'SO  <}( P ˖ R + #4  $  $9   *6> # ""    <(LbnwXQ+I,+?F+Q?9,i\M+mf+5)+&+5.=и)Bи.G01'.'''&676676&#!"5543!2"&55!"&55463!2####"&55##!"&5543323!2#!"5543!26P;< NVX(ET hr Qf R +/)  &'%"  + " I2 # ; ""  ')$s  +w+/x+6+и/I696B+~+ 9+rk+0+NU+%+0и-и0wиy01#!"&55463!26554&#!"&5546335#"&55463!2##32#!"3!2'.'&''&7>76&#!"5543!235##!"5543!24 /$,  b R ##-Q m@&AB@ !Y`\#KD  <}k!r )7z*=*"#_""_2*'    ! " $}_  <|'RbfrV+c+d+^+*9t[!+qj+PI+S+ +ииcиe01746335#"&55463!2##32#!".5'.'''&67>76&#!"5543!2"3!26554&#%35##!"5543!2 ,# R ' ,>6K7<  OTT'DY G|pi4r N"  J*k""k*q&#"  ) "  $* # 7 i i 76&#!"5543!24.#"32>'25!66#!"5543!2 1B@_m,EE@2@0 R 6K7<  OTU&EX G|pi4r N"$6MQQK6)351QM6A""C+"v=00<# "0 /<x"&#"  ) "  $* # 7 '' &de]  <*:F!8/+E>+)"+01'.'''&676$76&#!"5543!2#!"&55463!2#!"5543!2T6 LMH  S^_(LXaO Εq  R  > " "&@ ! *{K  # ""  76&#!"5543!2!2##"554&#!"&5546%#!"&55463!2#!"5543!2D&8?  PUV'FS G|pi4r N"/)    R 4' "  , "  &, # 74#)$""  <(8NZ5D=+H9+YR+&+6-+01'.'''&676676&#!"5543!2#!"&55463!2!"&5543323!2#!"5543!28R;< OWX(HN fr Q  R /)  &(( "  -  " M4  # ;""')$s  YEXF/F>Y.+}v+ %+^W++pg+:AAAA]AA(A8AHAXAhAxAAAAAAA ]01#!"&55463!26554&#!"&55463!2#!"3!2'.'&''&7>76&#!"5543!2#!"&55463!2#!"5543!25 /#-  #-Q m=#>@@ S\Z$FT <|k$r /5  R z#*4*'#7*'     " ! """  <|?O_kl/K/lи/Cи/Kи/K K и /Cи/ 9 mH +jc+=6+@+]T+017463!2#!".5'.'''&67>76&#!"5543!2"3!26554#!"&55463!2#!"5543!2 , ,>D&8?  PUV'FS G|pi4r N"    R X**q' "  , "  &, # 7 y y ""  <|HXbno//oи/Zи/Z ии/9aиp]+mf+Y+D=+VM+01".5546332!546332#'.'''&67>76&#!"5543!2#!"&55463!23!2655#!"5543!2,, ,6G7~<  LRT&HX I~pg2r '0  R _  ZYX&&""  ) "  $* # #""U U  76&#!"5543!2'&&'''&67667432%#!"&55463!2#!"5543!2D&8?  PUV'FS G|pi4r N" &m t*&y .   R 4' "  , "  &, # 7B  :X !fA?h! % )K p""  +M+i`+01%#".54>32'.'''&67>76&#!"5543!24.#"32>#!"&55463!2#!"5543!2*2AEEEEA22AEDEF@2\D&8?  PUV'FS G|pi4r N"$7MQRK66KRQL7  R  !1##1! 1##1' "  , "  &, # 7 && %%9""  _tC*6B15.+5D+/1/A:+(!+01'.'''&676676&#!"5543!2#"54332##"5543321 245  6537My:  H4))-T(2,$  ! '.2;b% ! :x # %Q~^  ppC)?KWK/4+4Cи/Iи/YF/1/VO+++8+01'&&'''&7>76&#!"5543!2!2##"554&#!"&5546%#"54332##"554332} 6i03~N  FjTC  J4%2 -_//) $ /))<'*L!$  =?G+  # (*$C #5 #)$8  sb'=IUUV/A/V,и,/3)и)/AHWD/7(+TM++01%'&&'''&676676&#!"5543!2!"&5543323!2'#"54332##"554332s 4g/i r>  J4%.?+a//)  #))B*`@$ 6_ # (*QD(= #)$q  p|SEQ]^/I/Pи/^и/и/ и /P и /Pи/P_L/+\U+:3+ +01#!"&55463!2#!"3!2'&&'''&7>76&#!"5543!2#"54332##"554332S , , 5   6i03~N  FjTC  J4%2 -_/))v** !  `<'*L!$  =?G+  # (*$C #5 M  mqX1Yeqr//rи/*и/и/".и./]и"cи"s`/o/.+pi+ %+OH++paиa/01#!"&55463!26554&#!"&55463!2#!"3!2'&&'"''&676676&#!"5543!2#"54332##"554332X  /#-+ #-o  1`-9Q w=  J4%"8)W+))#*@*,$>*2 :%-J% *xP  # (*<7!3 >  mpC'jϻ\9+TA++9IиI/oиuиu///0/3/r/+>W++ly+yEиE/lMи3b_и_/beиe/3sиs/и/01'&&'"''&676676&#!"5543!2###.55463326554&##"&5546332##"33267667632##"54&##"&5546%#"54332##"554332w 1`-9Q w=  J4%"8)W+h  'N!, !, '% 2/$  ))/:%-J% *xP  # (*<7!3 @D*/$?*4 -#) #3  p|C#MYef//fи/и/и/  и /и/Qи Wи gT/ +d]+B;++01%463!2#!".57"3!26554&#''&&'''&7>76&#!"5543!2#"54332##"554332 , ,]]  i 6i03~N  FjTC  J4%2 -_/))a**   <'*L!$  =?G+  # (*$C #5 J  o|CBLXde//eи/Dи/D ии/KиPиVиfS/G+c\+C+81+01".546332!546332#'&&'''&76676&#!"5543!23!26557#"54332##"554332S,, ,z 5g/4N ~9  J4%&=,^/   7))a_&>'+M"$ 6U # (*E=$7 X X  sv%LXd-WP+WfS/c\++01%'&&'''&676676&#!"5543!2'&'''&67>7667663#"54332##"554332|6j03}M4  J4'19,d0R 0] r.k@cK4/ )) C*.L" " 324.#"32>'&&'''&7>76&#!"5543!2#"54332##"554332J.Oi72>7676&#!"5543!2''&7667##"554#"5433232##~@=5   D/:g\  m- ;= )) FM # #%V~1 # 9X " ~"ip1G]7<+<Kи7Qи7[и7_9/N/+3@+-+SZ+и/$-9-'и'/-*и*/012>72>7676#!"5543!2''&67667##"554!2##"554&#!"&5546%#"5433232##@=5   K0"ĺc. ;= p/) $ /)) +. # "%N ! )e; " #)$4"j2H^͸_/6/_MиM/6G%MG9M+и+/G<иMTJиJ/G`9/XI++.+>E+и/>%и%/.(и(/.+и+/>,и,/.=и=/012>72>7676#!"5543!2"''&67667##"554#"5433232##!"&5543323!2@=5   K0"˱ ^0 ;= )) /)  +1 # "%J! &qC " ["#)$jq1dz{//{и/*и/и/".и./W.9hи"nи"xи"|k/.+G@+ %+pw++p2и2/p7и7/wWиW/wZиZ/w]и]/w`и`/01#!"&55463!26554&#!"&55463!2#!"3!22>72>7676#!"5543!2''&6767##"554#"5433232##5  /#-+ #-o P@=5  K0# 8abZ ;= )) #*L*8$J*> %* # "%BpdY* ! Rk " "j|#Ukl//lи/и/и/  и /и/H 9Yи _и iи m\/ +92++$Q+ah+$)и)/HQ$9QKиK/QNиN/017463!2#!".57"3!26554&#2>72>7676#!"5543!2''&67667##"554#"5433232## , ,]]  @=5   K0"ĺc. ;= )) **   *- # "%N ! )e; " "j|OYo׸p//pи/Qи/Q ии/B9Xи]иcиmиq`/T+2++P+el+eиe!и!/Ble9lHиH/lKиK/01".546332!546332#2>72>76676#!"5543!2&''&67667##"5543!26557#"5433232##0,, ,@=5  K0"ĺ f- ;=  7)) jh&+ # "%N! *]7 " k k"bw4]sra+rgиrud/+0+ip+и/ и /'090)и)/0-и-/0h012>72>7676#!"5543!2"''&67667###"554'.'''&67>7632#"5433232##@=5   K0" ;a_ g- ;= 2c 7cSC?Rb8CpS0 0 )) .5 # "%Nm\(! *hB "  Lb ".;D$$E<0 % 7+(W++gn+(-и-/NW(9WQиQ/WTиT/\и\/01%#".54>324.#"32>2>72>76676#!"5543!2&''&67667##"554#"5433232##,.Oi76676&##"55463!2''&7667###"554&&&&D"9D@и@/01354332##"55###"54332267263767676##"5543!2''&6767###"554!2##"554&#!"&5546&&&&M9`/XI+-&+D++и/"и"/D>и>/DAиA/0134332##"5###"54332267667676##"5543!2&''&6767##"554!"&5543323!2&&&&+ %+2\+bo++25и5/\VиV/\XиX/01#!"&55463!26554&#!"&55463!2#!"3!22672>7676##"5543!2''&67667###"554354332##"55###"54332y /#-  #-O P9YIиKVиKmиm/PpM/i+0)+e+"D+JW+"и/"и/>D"9D@и@/01".546332!546332#267263767676##"5543!2''&6767###"554354332##"55###"543323!2655[,, ,q76676##"5543!2&''&67667###"554'.'''&67>7632&&&&'4632354332##"55###"54332267263767676##"5543!2''&6767###"554?YM11NYA b[ 0 "- .#/ ZU &&&&32#".732>54.#"%#"54332354332##"55#267263767676##"5543!2''&6767###"554.SqD>pV22Vp>DqS.J#?V3-VB))BV-3V?#M&&&&72>7676&#!"5543!2''&7667##"554#"5433232##32##~@=5   D/:g\  m- ;= ))  FM # #%V~1 # 9X " ~  jp1QgP5+P;и5Kܸ@иPEиPVи5\иPi8/Y/+=D+S`+-+GN+Dи/$-9-'и'/-*и*/012>72>7676#!"5543!2''&67667##"554#"5433232##32##!2##"554&#!"&5546@=5   K0"ĺc. ;= ))  /) $ +. # "%N ! )e; " !}!h8#)$jp'[{ +z_+_A&6FVfv ]A]_uܺN u9zeиujиzoиz}b/#+>7+gn+(W++qx+n-и-/NW(9WQиQ/WTиT/\и\/01%#".54>324.#"32>2>72>76676#!"5543!2&''&67667##"554#"5433232##32##,.Oi72>7676&#!"5543!2''&7667##"5544332##"5#"5543@=5   D/:g\  m- ;= )) FM # #%V~1 # 9X " &Z $ }pC1G[7<+<Hи7Nи<Tи7]9/K/+3@+-+HU+и/$-9-&и&/-*и*/012>72>7676#!"5543!2''&67667###"554!2##"554&#!"&554654332##"55#"5543@=5   K0"ĺc. ;= |/) $ )) +. # "%N ! )e; " #)$D $ }b2F\]/4/]KиK/49%K99K+и+/4?иKRHиH/9^6/VG++.+3@+и/%.9.(и(/.+и+/012>72>76676#!"5543!2''&67667##"5544332##"5#"5543!"&5543323!2@=5  K0"˱\0 ;= o)) " /)   1 # "%J ! &lB " $+ $ #)$}|Y1MaѸb/O/b8и8/OT$8T98*и*/8G5и5/G<и72>7676#!"5543!2''&67667###"554#!"&55463!2#!"3!254332##"55#"5543@=5   K0"ĺc. ;=  , , 5  [)) $)/ # "%N ! )e; " f** !  a $ }qX1fz{//{и/*и/и/".и./Y.9_и_/gи"mиsи"|j/.+HA+ %+2b+gt++27и7/Yb29b[и[/b_и_/01#!"&55463!26554&#!"&55463!2#!"3!22>72>76676#!"5543!2''&67667###"55454332##"55#"5543X /#-0 #-u D@=5  K0# 8abd- ;= o)) #*L*8$J*> ( " "%BpdY* ! )^6 " 8 # }|D#Uij/W/jи/и/Wи/W\ и /W и /и/Wи/H 9Wbи\kY/ +92++$Q+Vc+$)и)/HQ$9QJиJ/QNиN/01%463!2#!".57"3!26554&#2>72>7676#!"5543!2''&67667###"55454332##"55#"5543 , ,]]  @=5   K0"ĺc. ;= o)) **   )/ # "%N ! )e; "   $ }|CNXlٸm//mи/Pи/P ии/A9WиYи_иeиn\/S+1*+O+J+Yf+!и!/AJ9JCиC/JGиG/01".546332!546332#2>72>7676#!"5543!2''&6767###"5543!265554332##"55#"5543R,, ,@=5   K0" 9aaY ;=  )) jh&)/ # "%BtfZ) ! Pj " l l2 $ {w2[omb]+]hиbq_/+.+\i+и/%.9.'и'/.+и+/012>72>76676#!"5543!2''&67667###"554'.'''&67>76324332##"55#"5543@=5  K0"˱^0 ;= 2c 7cSC?Rb8CpS0 0 )) / # "%J ! &pB "  Lb ".;D$$E<0 % 72>7676#!"5543!2''&67667###"554&&''&&'''&6766'4327>'463254332##"55#"5543@=5   K0"ĺc. ;= ?YM11NYA b[ 0 "- .#/ ZU )) $)/ # "%N ! )e; " T![56Y U34T#$ 0e  @9/ :HR)   P'  $ }pK'Ymn/[/n и /[и/ A&6FVfv ]A][`L `9[fи`o]/#+=6+(U++Zg+(-и-/LU(9UNиN/URиR/01%#".54>324.#"32>2>72>7676#!"5543!2''&67667###"55454332##"55#"5543K.Oi7676&##"55463!2##"54332 "&&+_  W( 0Z6 K+'"++"'9"'9"и/'*и*/01354332##"55#''&6767###"55432672>7676##"5543!2!2##"554&#!"&5546%##"54332&&ZP 0Z6 6+%+6>906>962и2/>;и;/>AиA/01%463!2#!".5354332##"55#''&6767###"5543267263767676##"5543!2"3!26554##"54332 , ,:&&ZR 0Z6 7676##"5543!2'.'''&67>7632##"54332&&+b N 0Z6 6+a+$+6>906>962и2/>;и;/>AиA/0174>32#".354332##"55#''&6767###"5543267263767676##"5543!232>54.#"%##"54332.SqD>pV22Vp>DqS.&&-^R 0Z6 72>7676&#!"5543!2 )) ,  m- ;= @=5   D/{;7c # yH # 9X " FM # #}bI_ϸ`//`NиN/ N 9ик&N 9N,и,/NUKиK/ a/YJ+G@++4/++&/49/(и(//,и,/49и9/01354332##"55#"554335#''&67667###"55432>7667676#!"5543!2!"&5543323!2; )) 0[0 ;= @=5 /  K0" /)  * # a6 ! %j? " 16 # "#)$}qXI{|//|QиQ/aиa/ xиx/ax9ик$ax9QtNиN/Yи kи }/xM+G@+Vo++2-+f]++$-29-&и&/-*и*/7и7/01354332##"55#"554335#''&6767###"55432>72>7676#!"5543!2#!"&55463!26554&#!"&55463!2#!"3!2;)) )Z ;= @=5   K0#  /#-+ #-o 4Y # sZA ! Oh " '/ " "#*L*8$J*> }|D_op/l/pи/cи/lи/l и /l и /cи/ 9lи/l$и$/l,и,/: 9q/h +]V+-+`+,%+-HиH/C:HC9<и72>7676#!"5543!2"3!26554&# , ,]2)) -a. ;= @=5   K0"  ** e # ya< ! (b9 " // # "   }|Cgqr//rи/iи/i ии/9и%и+и3кA9pиs"/l+e^+h+4+3,+4OиO/JAOJ9CиC/JGиG/4TиT/4WиW/01".546332!546332#354332##"55#"554335#''&67667###"55432>7267767676#!"5543!23!2655R,, , )) +a-;= @=5    K0"  jh&'"3H # uY= ! (\3 " .. # "tl l{wLu +ии w/JC++4/++&/49/(и(//,и,/49и9/01354332##"55#"554335#''&67667###"55432>72>76676#!"5543!2'.'''&67>76329 )) 0^0 ;= @=5  K0"2c 7cSC?Rb8CpS0 0 &} # }d8 ! &pB " / # " Lb ".;D$$E<0 % и>/{fиf/01354332##"55#"554335#''&67667###"55432>72>7676#!"5543!2&&''&&'''&6766'4327>'4632;)) -a. ;= @=5   K0"E?YM11NYA b[ 0 "- .#/ ZU \ # ya< ! (b9 " // # "![56Y U34T#$ 0e  @9/ :HR)   P' }pK_s-t/`/t и /`и/``$и$/`,и,/: 9 jAj&j6jFjVjfjvjjjjjjj ]Ajj]u/o+]V+-+e+,%+!и!/-HиH/C:HC9<и32354332##"55#"554335#''&67667###"55432>72>7676#!"5543!24.#"32>K.Oi7676&##"55463!2##"54332&& G  W( 0X6 272>764'&&#!"&55463!2''&&7667#"##"554!2#!"&55463!5463322GKA4 /96   )15,9DKF/ 0  d+-S!$ +nsn*  "d8 " "" Y+6+Wd+и/62и2/REܹ>M013266272>76654&#!"&55463!2''&7667###"554!2#!"&55463!546332!2##"554&#!"&5546t$JB3 &12    (-,#cn3M (  e)/)  F.$ DC= 0 " zw""w #)$<;Uk߻aZ+=N++A]A)9IYiy ] и /Zи/Z9и9/meV++6+SF+и/62и2/F>M013266272>76654&#!"&55463!2''&7667###"554!2#!"&55463!546332!"&5543323!2t$JB3 &12    ( -,$cn3M (  e)>/)  <0$ KLE =" " "" ')$76654&#!"&55463!2''&7667###"554!2#!"&55463!5463324 /#-  #-Q t$JB3 &12   ( -+$cn3M (  e)z#*B*2#B*5 #$ ;1 4 " n""n <|#_y ]+ar+C/+]и/C и /A//]A/)/9/I/Y/i/y/////// ]/и/2и2//QиQ/C{EXv/v>Y +>5+%Z++%*и*/ZVиV/viܹbq017463!2#!".57"3!26554&#3266272>76654&#!"&55463!2''&7667###"554!2#!"&55463!546332 , ,>]  t$JB3 &12    (-,#cn3M (  e)i**   1$ FF@ 2 " u~""~ 76654&#!"&55463!2''&7667###"554!2#!"&55463!5463323!2655,, ,t$JB3 &12    (-,#cn3M (  e)   ed&-$ CA< 0 " ow""w K^ ^RFܸ>M013266272>76654&#!"&55463!2''&7667###"554!2#!"&55463!546332'&&'''&67>7632t$JB3 &12    ( -,#cn3M (  e)# -p t+*nCqR1 0 76$ JMH 8 " x""  DZ " mEGr % 9HP' 323266272>76654&#!"&55463!2''&7667###"5544.#"32>!2#!"&55463!546332*@^m-EE@1@^m,-m_?t$JB3 &02    (-,#cn3M 6LQQK55KQRK6  e)3@$  %2"2?$ $@3$ IHB 4 " !''! ((Յ"" ?t:Pxy/>/ODиyjиj/sYиY/OzA/;/+e^+6+FM+и/61и1/^YиY/ejиetиt/012>72>76&'&&#!"&55463!2''&7667##"554#"5433232##6"'"&5546322635463323>$ )XYU% "N( " ~y"E$ #  "  Ij8Nd~w+ +>C+A ]A ) 9 I Y i y ]  и )и)/CRи>Xи>bи>@/U/+2+:G+Yb+zn+и/2.и./24иnw~и~/012>72>76'&&#!"&55463!2''&7667#"#"554!2##"554&#!"&5546%#"5433232##'6##"&55463354332667;: !() m ( (&7: 6/)  B))  A} )RB"#$ 0z? 1 " j#)$2"p"  #I9Oe[T+y+ +N=+A ]A ) 9 I Y i y ]  и *и*/NCиN@/_P++3+|p+EL+и/3/и//35иpyи/012>72>76'&&#!"&55463!2''&767#"#"554#"5433232##!"&5543323!26##"&55463354332667;: !() m ( (&7: s)) /)   A} )R($$$ :?B 17 " 1"$)$" #Iq1i3*+|+O>+"+и/A>>]A>)>9>I>Y>i>y>>>>>>> ]><и>ZиZ/и"и"и"/.+JA+ %+7c+++s+72и2/VиV/c_и_/ceиs|01#!"&55463!26554&#!"&55463!2#!"3!22>72>76'&&#!"&55463!2''&767#"#"5546##"&5546335433266#"&5433232##5 /#- #-[ w7;: !() m '(%7:  A} )Q)) "*;*+"9*. "$ 3n<., " "  #zzf "I|#[q++A0+ +A00]A0)090I0Y0i0y0000000 ]0.и0LиL/_и eи oи b/ +<3++)U+gn+|+)$и$/nHиH/UQиQ/UWи|017463!2#!".57"3!26554&#2>72>76'&&#!"&55463!2''&767#"#"554#"5433232##'6##"&5546335433266 , ,I]  7;: "') m ( (&7: s))   A} )R`**   #$ 3n< )1 " "q" #Iq'_u] ++.U+tc+cA&6FVfv ]A]AUU]AU)U9UIUYUiUyUUUUUUU ]U9и9/USиtiиtf/#+)X++NB+kr++r5и5/B>и>/BDиNIиI/01%#".54>324.#"32>!2''&767#"#"55432>72>76'&&#!"&5546#"5433232##'6##"&5546335433266-3Tl9>mQ//Qm>9lT3G&@S-2S<""76'&&#!"&55463!2''&7667##"5546"'"&55463754633226366&&&&*M. "'' +  ) '&''* 9==@B@ &V++;p|`G@3$ %VYW&"Q* "  # "  Jum'C|l+`O+8=+/*+A&6FVfv ]A]*и/8(и*5иOLиL/Onиn/9//,/EX/>Y+[R+Gx+#+)6+x(и(/:ܸи/и;иGDиD/nxG9xsиs/и/и/0174>32#".732>54.#"354332##"55###"54332672>7655&&#!"&55463!2#'&&7667##"5546#'"&55463275433266.TtF@sW33Ws@FtT.H%BX3.YE**EY.3XB%g&&&&)G. $%   % $"%$( 9=>:8 -`/)6i'*A--B*+B--B(,,--ֵ$ 796 , "  # "MtC:Fpq/>/Eq`и`/ilиl/ErA/;/+[T+6+и/61и1/TOиO/[`и[lиl/012>72>76&'&&#!"&55463!2''&7667##"554#"543326"'"&554632263546332263>$ )XYU% "N) " ~$ #  "  LqY1k!*+~+P?++и/и/!и!/A??]A?)?9?I?Y?i?y??????? ]?<и72>754'&&#!"&55463!2''&7667#"#"5546##"&5546335433266#"&54332Y /#- #-[ ]7;: #)* m()' 7:  A} )U))"*;*+"9*. $ 3r< / " "  #vvh <=aͻTK+^U+ -+A--]A-)-9-I-Y-i-y------- ]- и /-и/ c_B++8+и/82и2/_Jи_T0132>72>764'&&#!"&55463!2''&7667#"##"554#!"&5546335463323546332!23 JNE6 )43   ( 00(;GOI0   ++8 ,O$ 0mni,#i: " l""  272>766'&&#!"&5546 w+| i 20'9DKF/ 2GKA4 *57  N^" ~""T- " 'C$76  /  x/)   { y --$cn3M 2GJB3 &12 " !+LNK"#)$"D$ " 0<LbXQ+#+ J+AJJ]AJ)J9JIJYJiJyJJJJJJJ ]Jи/Qи/J0и0/ d\M+ +@9++$и,и95и5/@EиE/014&#!"5543!232#!##"&55!"&55463!667###"55433266272>766!"&5543323!2  /  +s q -+#cn3M t$JB3 %02S/)   " !+T\V" "Q, " 2')$76  /  x#-Q /#-  { z--$cn3M t$JB3 &12 " !+IKI"tJ*= ##*J*:#t"@! " 0<|Yiϻ^,+1+"f+fи/,и/" и /f>и>/,JиJ/fXи"kb&+ +NG+0Z++0и2и:иGCиC/NSиS/014&#!"5543!232#!32#!".5546335!"&55463!667###"55433266272>76"3!26554&#  /  x ,> ,{ y --$cn3M t$JB3 &12E   " !+OSN"**"G& " 6   7653!2655,, ,  /  x+{ z .-$cn3M 2GJB3 '12-  lk&+ " !+IKH" "A" " e e75!"&55463!667###"55433266272>766  /  x -p t+(q?lR3{ t -,#cn3M t$JB3 %02 " !+TZV"Db " tGJw  % 4CK&"M* " 475!"&55463!667###"55433266272>7664.#"32>  /  x-bQ5?^m.-m^?5Qb-{ y -,$cn3M t$JB3 &125KRQK44KQRK5 " !+OSP"(>-2B''B2->'"D% " 4 * * ) )ItC^r +WF+fk+)f9AFF]AF)F9FIFYFiFyFFFFFFF ]FCиC/\FW9k_иftb/h/RI+_l+>2+#+ и /и/#&и&/)2>92.и./24и>9и9/\hb9016##"&5'"&554632>7667667#"#"55432>72>766'&&#!"&55463!2664332##"55#"5543  @K+ GD< QUO/(&9< 9=< #)) a )Ig)) W"   K"L) " 3$ RUPZ " L[\p|u++di+ ud96ud9i]иd`/yr+U+K?+]j+0+и/yи/$и$/0+и+/03и3/6?K9?;и;/?AиKFиF/r01!26676##"&55"'"&554632>7667667#"#"55432>72>76'&&#!"&55464332##"55#"5543!"&5543323!2Y *I  AM+FC; PTN3 $"9< 9=< $& [)) /)   DJF  !   "C# " 2#$ Dp $ #)$LqX1h*+JQ+8+"+и/A]A)9IYiy ];89iиi/ии"ии"/EX%/%>Y.+3+~r++cM++% ;%9cRIиI/RWиW/cfиf/rnиn/rtи~yиy/01#!"&55463!26554&#!"&55463!2#!"3!2!26676##"&55'"&554632>7667667#"#"55432>72>76'&&#!"&55464332##"55#"5543X /#- #-X PY $A  !JMM$+DB< OSO8 %#9< 9=< %' [)) #*4*"$4*& L 5t1"  "3 " "$j " LvMt}u+ '+m\++A\\]A\)\9\I\Y\i\y\\\\\\\ ]\?и?/\YиY/r\m9Au&u6uFuVufuvuuuuuuu ]Auu]и/z+h_++TH+9#++9(и/(-и-/9<и32#".6##"&55'"&554632>7667667#"#"55432>72>766'&&#!"&55463!26632>54.#"4332##"55#"55432Vp>8oX77Xo8>pV2  !JMM$+DB< OSO8 %#9< 9=< %& Y#A,ET)$TH00HT$)TE,)) '8%$8((9%%9"   "8 " %$ =><W""# #: " 4tdUiu +N=+V[+jo+=:и:/AN&N6NFNVNfNvNNNNNNN ]ANN]S=N9[cиjwl/r/I@+c\+5-++ и /и/и/"и"/-*и*/52и2/SиS/016##"&5'"&5546367667667##"5543672>766'&&#!"&55463!266##"55#"554334332##"54332  0\:+9o+ :~9' 0W5 *^. !! & )& &&&["   S"C# " 1$ MOIQ " >~DvmeyMf+z++Af&f6fFfVfffvfffffff ]Aff]$f9$/79c9pиp/и/k+YP+u+G?+1++1%и/ и /%'и'/1.и./14и4/7?G9?<и32#".6##"&55'"&5546367667667##"55436726766'&&#!"&55463!26632>54.#"%##"55#"554334332##"543326Zu>9s]::]s9>uZ6= 0e3+s[ 9n:6  0W5 *^.?  /HX)$XK33KX$)XH/& &&& )9##9**:##:   ";  " %$ @A>n##$$ " GItC^j +WF+ib+AFF]AF)F9FIFYFiFyFFFFFFF ]FCиC/\FW9il_/e/RI+>2+#+ и /и/#&и&/2.и./24и>9и9/\_e9016##"&5'"&554632>7667667#"#"55432>72>766'&&#!"&55463!266#"54332  >H+ JG@ QUO3)( 9< 9=< &+* a &C))T"   H"N* " 2$ RWQ%~LjC_u+ +XG+ej+AGG]AG)G9GIGYGiGyGGGGGGG ]G+и+/GEк]GX9jyиeиe|/g/SJ+an+@4+(+( и /и/ (%и%/40и0/46и@;и;/(]и]/016##"&55'"&554632>7667667#"#"55432>72>76'&&#!"&55463!266!2##"554&#!"&5546%#"54332  FIJ"+IG@ MTP< '&9< 9=< #') Y =#)$6(L[]sӻib+ +~w+)b~9[b~9~z/f_+QH+>2+f и /#ܹ и /и/#и/#&и&/)2>92.и./24и>9и9/_m016##"&55'"&554632>7667667#"#"55432>72>76'&&#!"&55463!266!"&5543323!2'#"54332  ?F+GF? MTP9 &$9< 9=< !&( Y #@/)  ))"  "C$ " /#$ EHD#)$SLqX1\*+>E+y+"+и/Ayy]Ay)y9yIyYyiyyyyyyyyy ]y]и]/ywкy9и"и"/EX%/%>Y.+|+rf++ZA+RF+% F=и=/FKиK/ZWиW/fbиb/fhиrmиm/Zи/01#!"&55463!26554&#!"&55463!2#!"3!26##"&55'"&554632>7667667#"#"55432>72>76'&&#!"&55463!266#"54332X /#- #-X   FIJ"+IG@ MTP< '%9< 9=< "(( Y !<))#*4*"$4*& !"z w"<  " #$ 63L|DsCw+ '+l[++ и /A[[]A[)[9[I[Y[i[y[[[[[[[ ][?и?/[Yкq[l9и//| +g^+t+TH+<#+<(и/(-и-/(4<9и9/HDиD/HJиTOиO/<qиq/017463!2#!".56##"&55'"&554632>7667667#"#"55432>72>76'&&#!"&55463!266"3!26554#"54332 , ,D  FIJ"+IG@ MTP; '&9< 9=< #') Y !<  N))O**" ">  " '$ ??> n n t$L}C{K}+(/+tc++} Acc]Ac)c9cIcYcicyccccccc ]cGиG/caкyct9иии/+of+|+\P+D++D0'и'/05и5/0<DAиA/PLиL/PRи\WиW/Dyиy/01".5546332!546332#6##"&55'"&554632>7667667#"#"55432>72>76'&&#!"&55463!2663!26557#"54332;,, ,@  FIJ"+IG@ MTP< '&9< 9=< #') Y <  7))[Y&7"y v"; " '$ =>=qP PLv]' +V|++A||]A|)|9|I|Y|i|y||||||| ]|)и)/|CиC/|EиE/[|V9o|V9/QH+# +>2+# и /и/#и/#&и&/)2>92.и./24и>9и9/016##"&55'"&554632>7667667#"#"55432>72>76'&&#!"&55463!266'&&'''&7>7432#"54332  BH+EB< MTP8 &$9< 9=< !&( Y #AL &o v,*pHsQ+ 0 ))"  "D% " /#$ GHE  :_"  iABk" %9DJ% KLvLqr+%+jY++AYY]AY)Y9YIYYYiYyYYYYYYY ]Y=и=/YWкoYj9Ar&r6rFrVrfrvrrrrrrr ]Arr]|и|//w+e\++RF+:+:&и/&+и+/&2:7и7/FBиB/FHиRMиM/:oиo/и/0174>32#".6##"&55'"&554632>7667667#"#"55432>72>76'&&#!"&55463!26632>54.#"%#"543322Vp>8oX77Xo8>pV2  @G+GE> MTP; '&9< 9=< #') Y !=,ET)$TH00HT$)TE,)) '8%$8((9%%9"  "A# " )$ AD@m""# #IYEX6/6>YEXYT+ +и%и -и<CIиI/01!232####"&5###"&5#"&55463!667#"##"554332>272>766'&&#!"&5546 ++ i /-$:GNG0 3 IMD5 '24  N^" x x"#V- " (E$<Vlb[+&-+#+ T+ATT]AT)T9TITYTiTyTTTTTTT ]Tи/T:и:/ nfW+ +JC++$и.и6иC?и?/JOиO/014&#!"5543!232####"&55###"&55!"&55463!667###"55433266272>766!"&5543323!2  /  ++ q -+#cn3M t$JB3 %02T/)   " !+T\V"  "Q, " 2')$7635#  /  ##-Q /#-  b z--$cn3M t$JB3 &12 " !+HIH"tJ*= ##*J*:#t">  " 0nt<|Yim^,+j1+k+"f+fи/,и/" и /f>и>/,JиJ/fXи"ob&+ +NG+0Z++0и2и:иGCиC/NSиS/0jиl014&#!"5543!232##32#!".5546335#"&55463!667###"55433266272>76"3!26554&#%35#  /  ' ,> ,# y --$cn3M t$JB3 &12E   " !+OSN"**"G& " 6   <<K>+A>>]A>)>9>I>Y>i>y>>>>>>> ]>#и#/>AиA/M+D+2-+и-(и(/29и9/01!232#!"&55463!>7##"554332>272>766'&&#!"&5546   m /1(coe 2GKA4 ,54  Udnj_#""?MV, " @n!$76654&#!"&5546!2##"554&#!"&5546   t -+#cn3M 2GJB3 &01   /)   SYR""J* " ":$#)$<G]^/=/A==]A=)=9=I=Y=i=y======= ]^LиL/+и+/LS/и//=:и:/LDиD/SIиI/_WH+@+/(+ + и($и$//5и5/01!232#!"&55463!667###"554332626272>76654&#!"&5546!"&5543323!2   q -+#cn3M 2GJB3 &/2   d/)   ^d]""X1 " &B$')$76654&#!"&5546#!"&55463!26554&#!"&55463!2#!"3!2   { .-$cn3M t$JB3 '12   g /#-  #-Q  HJF""># " /$#*N*>#N*A <|Zj3k/M/kWиW/и/W^и/AMM]AM)M9MIMYMiMyMMMMMMM ]Mи/M и /M и /^и/Mи/M3и3/MPиP/Mfиlc +S+[+C<+ '+ /и<8и8/CHиH/017463!2#!".5!232#!"&55463!667###"55433266272>76654&#!"&5546"3!26554&# , ,>    { .-$cn3M t$JB3 &12   V  s**7 U[T""N, " #;$   76654&#!"&55463!2655,, ,  ~ /-%cn3M t$JB3 '12   >  &rq& QVO""H) "  8$>k k32!232#!"&55463!667###"55433266272>76654&#!"&55464.#"32>*@^m-EE@11@EE-m_?   { .-$cn3M t$JB3 &12   6LQQK55KQRK63B'&4"!4&'A W]V""P- " $=$$, ,$#, ,]tC2>[7+7=7@3/9/+.+и/&.9.)и)/012>72>7676&#!"5543!2''&7667##"554#"54332@=6   S/>m\  m1" ;>= ))FM # #%V~1 # 9X " ~}pC3IUm9>+>Mи9Sи9W;/P/+5B+/+и/'/9/*и*/012>72>76676#!"5543!2''&67667##"554!2##"554&#!"&5546%#"54332C@9  U0" :cdm- >A@ |/) $ /))- # "%Gyh[* ! -c9 " #)$8}b2HTU/L/U7и7/LS&7S97>4и4/SVO/B3++.+и/&.9.(и(/012>72>7676#!"5543!2''&67667###"554!"&5543323!2'#"54332C@9   U0" =e`h. >A@  /)  #)) .4 # "%Jo_( ! +j? " y#)$~}qX1cop//pи/*и/и/".и./W.9gи"mи"qj/.+G@+ %+2_++27и7/W_29_YиY/01#!"&55463!26554&#!"&55463!2#!"3!22>72>7676#!"5543!2''&6767###"554#"54332X /#-0 #-u DC@9  U0# 7bgW >A@ ))#*L*8$J*> '* " "%BobZ, ! Vg " 5}|D#WcǸd/[/dи/и/[и/[b и /[ и /и/[и/K 9be^/ +:3++$S+$)и)/KS$9SMиM/01%463!2#!".57"3!26554&#2>72>76676#!"5543!2''&67667###"554#"54332 , ,]]  C@9  U0" ;ebk- >A@ ))**   . # "%Gyi[) ! ,_9 " }|COYef//fи/Qи/Q ии/C9Xи]иcиg`/T+2++P+K+!и!/CK9KFиF/01".546332!546332#2>72>76676#!"5543!2''&67667##"5543!26557#"54332R,, ,C@9  U0" 7cgm, >A@  7))jh&- # "%DsdY+ ! -[3 " l l4{w/XdOc\+cf_/+++и/#+9+%и%/012>72>7676#!"5543!2''&6767###"554'.'''&67>7632#"54332C@9   U0"ɽ^ >A@ 2c 7cSC?Rb8CpS0 0 ))-1 # "%O ! U "  Lb ".;D$$E<0 % 7+(W++(-и-/OW(9WRиR/01%#".54>324.#"32>2>72>76676#!"5543!2''&67667##"554#"54332K.OiA@ ))'+D//D+*C/.C...--, # "%Gyi[) ! -c9 " }t6LM/:/Mи/!и!/+и:K@иKN=/7/"'+ +,+BI+и/,1и1/01"'#3>76'&&5463!2#!"326676#"5433232##PRM2 -hib''msp+-! ,X q&RQ)) z   (** $ " ~"*p6Lbc/A/cи/!и!/+иA8и8/A<APи<Vи<`и<d>/S/"'+8E+1+X_+ +и/1,и,/X-и-/01"'#3>76'&&5463!2#!"326676!2##"554&#!"&5546%#"5433232##IIF* ,aa]( )dji.-! ,C e"JI/) $ /)) y    '*8* $ t" #)$4"6Lb+XQ+K:++иK@иKd=/\M+"'+,+BI+ +и/,1и1/01"'#3>76'&&5463!2#!"326676#"5433232##!"&5543323!2IIF* ,aa]( )dji.-! ,C e"JI)) /)      '*N* $ " "$)$q1h~9O+*+"+и/9]иlи"rи"|и"o/.+TY+ %+^8+t{++<L+82и2/^cиc/01#!"&55463!26554&#!"&55463!2#!"3!2"'#3>76'&&5463!2#!"326676#"5433232##5  /#-+ #-o IIF* ,aa]( )dji.-! ,C e"JI)) #*D*2$D*6 `i    '** $ d" "p6y+cP+}++кH9H/XиX/Hk?/B///"'+Mf+1+++ +и/1,и,/TиT/\иBqnиn/qtиt/Bи/01"'#3>76'&&5463!2#!"326676###.55463326554&##"&5546332##"332676676#"5433232##32##"54&##"&5546IIF* ,aa]( )dji.-! ,C e"JI   'N!, !, '% 2)) /$  g    '** $ b" K*6$F*9 j"1#)#|JZp1+N+ V+?иV^и dи nи ra/S +6;+K+@+fm+.+и/@EиE/017463!2#!".5"'#3>76'&&5463!2#!"326676"3!26554#"5433232## , ,]!IIF* ,aa]( )dji.-! ,C e"JI  O)) i**&y    '*8* $ t"   p"|R\r#9+T++T и#Gи[и`иfиpиtc/W+>C+S+M+ho+&6+!и!/MHиH/hIиI/01".546332!546332#"'#3>76'&&5463!2#!"3266763!26557#"5433232##0,, ,IIF* ,aa]( )dji.-! ,C e"JI  7))  ec&uv    '*2* $ q" 9\ \"y6Zpq/^/qи/!и!/+и^oHo9KиK/odиora/"'+,+fm+ +и/,1и1/01"'#3>76'&&5463!2#!"326676'&&'&''&7>7432#"5433232##IIF* ,aa]( )dji.-! ,C e"JI  &o u,*p HsQ+ 0 ))     '*D* $ z"   :_#  i?Dj%9DJ% A"m6r+WO+v++к99AW&W6WFWVWfWvWWWWWWW ]AWW]AOW9WTиT/vgиg/vjиj/|иy/"'+,+~+e+и/ ,1и1/eQиQ/01"'#3>76'&&5463!2#!"326676&'&'&&'''&67>'432766'4632#"5433232##IIF* ,aa]( )dji.-! ,C e"JIW/,l9JXE6I+ 0  0 =A/ WU _)) y    '*8* $ t" EcYK"L,.M& " 8AK-   :7/-|M   Hm$ "pJ^t1+U +sb+b?AU&U6UFUVUfUvUUUUUUU ]AUU]shиsve/Z+6;+P+@+jq+.+и/@EиE/01%#".54>32"'#3>76'&&5463!2#!"3266764.#"32>7#"5433232##,.PiP+0Q9 9Q0+P>$-)) )?**?))>**>y    '*8* $ t" ** *( ("jtdM!5+++и и!CиO/ /:?+$2+H++и/HDиD/0134332##"5###"54332'#3676'&&5463!2#!"32676&&&&<{3/ UK([^^*-! , /3{<hLG  (** $ " opd1Mc+BG+SX+'иB2иX3и3/S9иX?и?/Se6/U/#+O\+,+3@++и/,(и(/3)и)/01'#3>76'&&5463!2#!"326767354332##"55###"54332!2##"554&#!"&55466k99 +OLM) P_-! , 99m3&&&&k /)  y   '*8* $ t" ,#)$oMc!5+YR+++и и!Cиe/]N+:?+E++$2+и/EHиH/0134332##"5###"54332'#3>76'&&5463!2#!"32676!"&5543323!2&&&&6k99 +OLM) P_-! , 99m3/)  `Kq   '*F* $ {" $)$oqy1cͻ7K+*+ty+kf+fи/и/k!и7Yиtdиfqиkh/.+PU+ %+[5+er++:H+52и2/[^и^/01#!"&55463!26554&#!"&55463!2#!"3!2'#3>76'&&5463!2#!"326767354332##"55###"54332y /#-  #-O N6k99 +OLM) P_-! , 99m3&&&&#*D*2$D*6 ^e   '** $ `" "*o|dEUq-+I+fk+]X+] и;иXQиQ/fVиXcи]sZ/N +27+F+V+*+и/@<и76'&&5463!2#!"32676"3!26554&#354332##"55###"54332 , ,=6k99 +OLM) P_-! , 99m3n  d&&&&_**y   '*8* $ t" ~ ~ o|dMis˻!5+k+^c+UP+k иPи/Uи!Cи^NиP[иPrиr/UuR/n+:?+j+E+O\+$2+и/EHиH/01".546332!546332#'#3>76'&&5463!2#!"326767354332##"55###"543323!2655[,, ,6k99 +OLM) P_-! , 99m3&&&&  `^&ru   '*0* $ p" %Ǻ=\ \oy1Mq+BG+94+'иB2и4?к_999s6/#+,+3@++и/,(и(/01'#3>76'&&5463!2#!"32676734332##"5###"54332'&&'&''&7>74326k99 +OLM) P_-! , 99m3&&&&a &o u,*p HsQ+ 0    '*F* $ {" *  :_#  i?Dj%9DJ% om1m+RJ+~+up+'к4u9AR&R6RFRVRfRvRRRRRRR ]ARR]<JR9ROиO/~nиp{иur/#++#nܹи/}ܸи/n(и(/01'#3>76'&&5463!2#!"32676&'&'&&'''&67>'432766'4632354332##"55###"543326k99 +OLM) P_-! , 99m3/,l9JXE6I+ 0  0 =A/ WU &&&&y   '*8* $ t" EcYK"L,.M& " 8AK-   :7/-|M   Hm$ onkEYu-+P +jo+a\+aи/;и\FиF/AP&P6PFPVPfPvPPPPPPP ]APP]jZи\gиaw^/U+27+K+Z+*+и/@<и32'#3>76'&&5463!2#!"326764.#"32>354332##"55###"54332k4Wq76'&&5463!2#!"326676#"5433232##32##PRM2 -hib''msp+-! ,X q&RQ))  z   (** $ " ~  pJ^~1+U +}b+b?AU&U6UFUVUfUvUUUUUUU ]AUU]}hиbxܸmи}rи}e/Z+6;+jq+P+@+t{+.+и/@EиE/01%#".54>32"'#3>76'&&5463!2#!"3266764.#"32>7#"5433232##32##,.PiP+0Q9 9Q0+P>$-))  )?**?))>**>y    '*8* $ t" ** *( (!x!tC5IJ/9/Jи/и/)и9Aи9HKD/6/ %+ +A:+:и/:и/A.и./A0и0/01"'#3676'&&5463!2#!"3277676#"5#"554334332CD@* TW *dhg--! ,: b "D"8) )   (** $ "  $ BpC2H\ø]/=/]и/и/'и=4и4/=8=Iи8Oи=Uи8^:/L/#+4A+IV++Vи/Vи/I(и(/I-и-/01'#3676'&&5463!2#!"326676!2##"554&#!"&554654332##"55#"5543;5* YQ )[`a.-! ,( a?A/) $ )) y   '*8* $ t" #)$t $ b2F\+RK+94+'и4?и9^6/VG+#+3@++@и/3(и3-и-/01'#3676'&&5463!2#!"326676%54332##"5#"5543!"&5543323!2;5* YQ )[`a.-! ,( a?A)) /)     '*N* $ " 2qU $ #)$qX1dx7K+*+"+и/7Yиeи"kиqи"zh/.+PU+ %+er++:H+r2и2/r5и5/eZиZ/e_01#!"&55463!26554&#!"&55463!2#!"3!2'#3676'&&5463!2#!"326676%54332##"55#"5543X  /#-+ #-o ;5* XQ )[`a.-! ,( a?A)) #*D*2$D*6 `i   '** $ d" 8& $ qDF'-+r_+++ и /;кW-9W/gиg/Wzи/и// +27+\u++lc+*+lи/и/и/<и74324332##"5#"5543;5* XQ )[`a.-! ,( a?A? &o u,*p HsQ+ 0 ))    '*D* $ z"   :_#  i?Dj%9DJ% ? $ m2n+SK+up+'к5u9AS&S6SFSVSfSvSSSSSSS ]ASS]=KS9SPиP/p{иur/#+o|+a+|и/}ܸи/#ܸ(и(/o)и)/-и-/aMиM/01'#3676'&&5463!2#!"326676&'&'&&'''&67>'432766'463254332##"55#"5543;5* YQ )[`a.-! ,( a?A/,l9JXE6I+ 0  0 =A/ WU )) y   '*8* $ t" EcYK"L,.M& " 8AK-   :7/-|M   Hm$  $ pLFZn-+Q +a\+;и\GиG/AQ&Q6QFQVQfQvQQQQQQQ ]AQQ]\gиap^/V+27+L+[h+*+hи/[<и32'#3676'&&5463!2#!"3266764.#"32>54332##"55#"5543L.PiP+0Q9 9Q0+P>$)) )?**?))>**>y   '*8* $ t" ** *( (" $ jtd0FR+16+GL+%и6@иGTI/O/!++@7+&+7и/&+и+/01'#3676'&&5463!2#!"326676##"5#"&5546334332##"54332;Y2/ UKQU-! , _.3& &&& (** $ " &"-~opd4J^j+QL+_d+)и_9иd?и?/LWи_l76'&&5463!2##"326676!2##"554&#!"&554654332##"55#"5543##"54332,+.9 +KIJ) P_-! , l,+ /)  h&&{ n&&y   '*8* $ t" #)$c  $ o3G]i+SL+:5+^c+'и5@и^kf/WH+#+,+4A++и/,(и(/,.и./01##3>76'&&5463!2##"3277676754332##"5#"5543!"&5543323!2'##"54332/X59 +KIJ) P_-! , m+&&{ z/)  &&   '*F* $ {" #3 $ $)$oqy1fzٻ9M+*+mh+{+и/и/{!и9[иhsи{/.+RW+ %+z2++<J+27и7/zt8и8/z\и\/g]и]/01#!"&55463!26554&#!"&55463!2#!"3!2'#3>76'&&5463!2##"326676754332##"55#"5543##"54332y /#-  #-O 0,+.9 +KIJ) P_-! , l,+&&{ n&&#*D*2$D*6 ^e   '** $ `" -G $ o|dHXlx/+L+_Z+mr+m и=иrTиT/Zeиmzu/Q +49+I+li+,+iи/iиiC>и>/lf01%463!2#!".5'#3>76'&&5463!2##"326676"3!26554&#54332##"55#"5543##"54332 , ,=,+.9 +KIJ) P_-! , l,+P  &&{ n&&_**y   '*8* $ t" ~ ~  $ o|dQ[o{Ż%9+S+b]+pu+S иuи/pи%GиuZиZ/]hиp}x/V+>C+R+\i+(6+i!и!/i$и$/\HиH/01".546332!546332###"'3>76'&&5463!2##"366763!265554332##"55#"5543##"54332[,, ,,+. +KIJ) P_-! , 95T2a  &&{ n&&`^&ru   '*0* $ q" <\ \0 $ oy4Xlx+_Z+mr+)кFm9Zeиmzu/ %+/+Yf+ +и//*и*/01'#3>76'&&5463!2##"326676'&&'&''&7>743254332##"55#"5543##"54332,+.9 +KIJ) P_-! , l,+ &o u,*p HsQ+ 0 ,&&{ n&&   '*F* $ {"   :_#  i?Dj%9DJ% 2 $ Wonk'\p|/C+ +c^+qv+qи/vи/A&6FVfv ]A]/Qи^iиq~y/#+HM++pm+2@+m(и(/m-иmWRиR/pj01%#".54>324.#"32>'#3>76'&&5463!2##"326676754332##"55#"5543##"54332k4Wq+и/+&и&/01'#3676'&&5463!2#!"326676%54332##"55#"554335#"5543!"&5543323!2;5* ZP Qe-! ,( a?A))  /)      &*N* $ " s # # '#)$m2n+SK+up+'к5u9AS&S6SFSVSfSvSSSSSSS ]ASS]=KS9SPиP/p{иpиur/#+o+a+|+#-ܹи/-(и(/aMиM/01'#3676'&&5463!2#!"326676&'&'&&'''&67>'432766'463254332##"55#"554335#"5543;5* YQ )[`a.-! ,( a?A/,l9JXE6I+ 0  0 =A/ WU )) y   '*8* $ t" EcYK"L,.M& " 8AK-   :7/-|M   Hm$   # n # jtd1MY+9B+NS+&и93>и3FиBJиN[P/V/"+ +2G+,+F?+и/,'и'/01##3676'&&5463!2##"326676754332##"5#"554335#"5543##"543321-^ UKQU-! , ^-1&& v&& (** $ " q # # ^~o3Oeq+[T+;D+fk+'и;5@и5HиDLиfsn/_P+#+4I+,++HA+и/,(и(/,.и./01##3>76'&&5463!2##"3277676754332##"55#"554335#"5543!"&5543323!2'##"54332/X59 +MJI' M`-! , m+&& /)   &&   '*F* $ {"  # # #$)$<7{8/ /8и/% и /%и/%и 01++( +#+1и .01%!"&55463!5#"&5463!2#!"!2#!3!2#!!2 , ,     "*E* ! v " w !"]  g""g**  E ! G ** w w <|7S]U=++OH+Oи/=и/O!и!/U(и(/U0и0/UEиH\иO_X8+$+FT+*/+5+ +и01#!!2#!"&55463!5#"&55463!2#!"!2#!3!2".5546332!546332#%3!2655&   , ,     ,, ,7  b""b**  @ ! B \[&X X7432&   , ,     6J^= |))tItO+ 0 \""\**  M ! O   3,# !!hADl  % 7BJ' 324.#"32>    , ,  @_l-,l_@@_l,-m^@M4JQQI33IQQJ4*G m""m**  E ! /;! ";-.:" !;/####<|7W[1+XA++RY+1(S8+$+*/+JC+5+ +ииS@иCPиSXиCZ01#!!2#!"&55463!5#"&55463!2#!"!2#!3!2!"&5546335#"&55463!2##32%35#&   , ,     z w ^p:g""g**  E ! G  ! ! ;?tF\@)+$+[J+@7и[Pи[^G/M/.3+$+9>+D+RY+$и/и/$и/%01##>76"'"&554632675#"&5463!2#!"!2#!3!2#"5433232##X !HGC"NPP$LRN   # "* * ! Y " Z ItCFR@)+$+QJ+@7иQTM/G/.3+$+9>+D+$и/и/$и/%01##>76"'"&554632675#"&5463!2#!"!2#!3!2#"54332h "MKG!NST(QUP >FG z, ,u  n k ))%  #  "** ! \ " ] /~Mb<R^6+HA++]V+6-и]`Y/E>+$)+/4++:+и/и>L01##6676##"&55463!5#"&55463!2#!"!2#!3!2!"&5543323!2'#"54332V M9  Xg g, ,K  D A /)  #))r " #t** ! C " D $)$]My<co{6++ng+6-кOn9nqj/$)+/4++:+и/01##6676##"&55463!5#"&55463!2#!"!2#!3!2'&&'&''&766766543#"54332V M9  Xg g, ,K  D A COX,m9-q } 0))f " #h** ! > " ? 6*! _KDg%pN[MqFPdpJ3+.+oh+oи/J и /JAи BиB/hQиQ/J[ork/`+8=+CH+V+N+.%+.и//01%#".54>32##6676##"&55463!5#"&55463!2#!"!2#!3!24.#"32>7#"54332F3Tl9>mQ//Qm>9lT3 M9  Yf f, ,K  D A &@S-2S<""/'/>и/и/и/и':$/ ++0)++)801!"&5463!2#!"!2#!3!2"&5!"&55463!2#!##, ,    | R wL** ! b " c !. "" +++0)+ +)801!3!2#!"&55463!2#!"!2"&55!"&55463!2#!#!"&5543323!2  , ,  s R !/)  L **  J !  "" ')$?  , ,    P*g""g*gG **  E ! 1 o o 75!"&55463!2'!3!2#!"&55463!2#!"!24.#"32> x,bR5@_m,EE@26Ra,{ R   , ,  16MQQK66KQQM67"\$<-3?#  $2"-<$\"G **  E ! !&&! ''ItC#7_+BI++0+и0$и+a-/'/+$1++[J+ +JAиA/JOиO/01!3!2#!"&55463!2#!"!24332##"55#"554376##"&5'"&554632>7>3 W , ,a  Z)) {  EH+ GD< QUO$SQJN ** ! M "  " "   !" Lo#[oݻ+z+ch+к'c9.z9h\иc_/++}N+ +\i+ +N<и7>+ A , ,K  DKEX.l9K\BdX 0  @8B;/  ZK )) x AJ+BA: OTO#NNJ22 ** ! 1 " EK-OAD+*I  " *iE  3U+_<   <_ " " o"  p m"4td#7Ce+NU+$)+8=+и)1и8g@/:/+1*++cV+ +VMиM/VYиY/c`и`/01!3!2#!"&55463!2#!"!2##"55#"554334332##"543326##"&5'"&554636766  , ,  & &&&  .h7+9o+ :~97>= W , ,a  Z))  >H+ JG@ QTP#SQJN ** ! L # ~~"   " LjC#9am+DK+).+и6и6/.eи)kи)o+/h/++%2+]G+ +]LCиC/LQиQ/]XиX/01!3!2#!"&55463!2#!"!2!2##"554&#!"&5546%6##"&55"'"&554632>7>#"543325 A , ,K  D/)    OTO$ONI))22 ** ! 1 " [#)$"  l j"-Q[#/El+OV+.'+и5и4и:.n*/81++hS++ +1?hWNиN/W\и\/hcиc/01!3!2#!"&55463!2#!"!2#"54332!"&5543323!26##"&55"'"&554632667>5 A , ,K  D)) /)   =B+IJD OSL#OOK 9 ** ! 8 " &T#)$   "QqW1U|4A+_f++4ии/4и/и/!и!/4*4OиPи/.+FK+ %+Q2++xb+8=+xg^и^/glиl/xsиs/01#!"&55463!26554&#!"&55463!2#!"3!2!3!2#!"&55463!2#!"!26##"&55"'"&554632667>#"54332W /#- #-X A , ,K  Dm AE+DD> OSL#OOK))#*+*$+* 2 ** ! 1 "   [ X" Q|C7Gnzû#+QX+ C+и1и2и;Crи xи |u/@ +(-+8+3+jT++jYPиP/Y^и^/jeиe/017463!2#!".5!3!2#!"&55463!2#!"!2"3!26554&#6##"&55"'"&554632>7>#"54332 , ,EE A , ,K  D  7 AH+FC: MSN!NPL))?*|*o2 ** ! 1 "  ^ ^ (  u q"7Q}C?fp|++IP++иh и9и:иoиtиzи~w/k+05+g+;+bL+"'+L и /Lи/bQHиH/QVиV/b]и]/01".5546332!546332#!3!2#!"&55463!2#!"!26##"&55'"&554632>7>3!26557#"54332;,, , A , ,K  Dm ?F+ HF= MSN!NPL  7))TR&- ** ! , "   l h"I IQvL7Kr~ɻ#+U\+}v+#9/1и8vBиB/}y/=+(-+3+G+nt++n]TиT/tXиX/]bиb/niиi/0174>32#".!3!2#!"&55463!2#!"!232>54.#"6##"&55"'"&5546332667>#"543323Up=8oY77Yo8=pU3U A , ,K  D-FT'"TI11IT"'TF- AH+FC: A'SN!NPL)) &7##7''8##8P2 ** ! 1 " !!""T  i e"K?  , ,    P*i""i*gG **  E ! 1 o o +D%+]N++0+"+иa01###".54675#"&55463!2'!3!2#!"&55463!2#!"!24.#"32>'25!66 1B@_m,EE@2@0 R   , ,  16MQQK66KQQM6A""C8"y?33?#  $2"2>z"G **  E ! !&&! ''gh<#3=+1(+ +++01!"&5463!2#!"!2#!3!2#!"&55463!2#, ,    |  R *E* ! v " w !""+01!3!2#!"&55463!2#!"!2!2##"554&#!"&5546%#!"&55463!2  , ,  /)    R &K **  I ! k#)$""<#3Ik?8+?и/8и/?и/<5+++1(+ +5C01!3!2#!"&55463!2#!"!2#!"&55463!2!"&5543323!2  , ,    R /)  V **  T ! ""')$<|#?Om9*+9и/*и/9и/=&+++/4+ +MD+01!3!2#!"&55463!2#!"!2#!"&55463!2#!"3!2#!"&55463!2  , ,   , ,    R &K **  I ! d** ! { {""и>/4EиE/"Hи4Oи"g.+FK+ %+Q2++8=+cZ+01#!"&55463!26554&#!"&55463!2#!"3!2!3!2#!"&55463!2#!"!2#!"&55463!25 /#-  #-Q 0  , ,    R z#*;*&#6*. ? **  = ! ""?  , ,      R \**aM **  K ! E } } ""<|?IYZ//Z+и+/ и/+и/и/ и/"и"/$и$/'и'/2и2/ 9и9/ @иHи[D+05+@+;+"'+WN+01".546332!546332#!3!2#!"&55463!2#!"!23!2655#!"&55463!2,, ,1  , ,     R ^]&B **  @ ! Z Z""7432%#!"&55463!2  , ,  6J^= |))tItO+ 0   R O **  M !   3,# !!hADl  % 7BJ' b""ItC#/PwQ/'/Qи/ и /и/и'.R*/$/+L;++ +01!3!2#!"&5463!2#!"!2#"543326'"&554632>7>G u z, ,  m))  !NST(SVN QUO#WVN\ !** ! c " ~3"  " MW#/E]+.'+и4и4/и/;._*/81+++YP+ +1?01!3!2#!"&55463!2#!"!2#"54332!"&5543323!26##"&5546332>5 A , ,K  D))/)   Xg 9wri D ** ! C " D^$)$" #MtY1Umyû4A+xq+4и/qи/4и/и/x!и!/4*4OиPиP/x{t/.+FK+ %+Q2++8=+i`+01#!"&55463!26554&#!"&55463!2#!"3!2!3!2#!"&55463!2#!"!26##"&5546332>#"54332Y /#- #-[ A , ,K  Du  Xg 9upf))"*=*-";*0 1 ** ! 0 " " #bM|D7GSk#+RK+и/R и /1и2и2/;KCиC/RmN/@ +(-+8+3+g^++017463!2#!".5!3!2#!"&55463!2#!"!2"3!26554#"54332'6##"&5546332> , ,I@ A , ,K  D  N))  Xg =wocP**\: ** ! 9 " < o o s$y# #M}C?Iam+++иA и9и:иHиeиkиoh/EX!/!>YD+05+@+;+]T+!'01".5546332!546332#!3!2#!"&55463!2#!"!23!26556##"&5546332>#"54332;,, , A , ,K  D  G  Xg =woc))VT&5 ** ! 4 " K K=# #l tC6ByC/:/Cи/!и!/+и:AD=/7/"'+ +,+и/,1и1/01'#3>76'&&5463!2#!"326676#"54332LPL. *fjh+ *mtp--! ,X q&PK ))    (** $ " ~pC5KWX/@/Xи/!и!/+и@7и7/@;@Oи;Uи;Y=/R/"'+7D+,+ +и/,1и1/01##3>76'&&5463!2#!"326672!2##"554&#!"&5546%#"54332DMJ/ ,_^[( )bgg.-! ,< n$KE /) $ /))y    '*8* $ t " #)$2b5KWu+A:+VO++иVYR/E6+"'+,+ +и/,1и1/01##3>76'&&5463!2#!"326672!"&5543323!2'#"54332DMJ/ ,_^[( )bgg.-! ,< n$KE 5 /)  #))    '*N* $  " #)$rqX1gs9O+*+"+и/9]иkи"qи"un/.+TY+ %+^8++<L+82и2/^cиc/01#!"&55463!26554&#!"&55463!2#!"3!2##3>76'&&5463!2#!"326672#"54332X  /#-+ #-o DMJ/ ,_^[( )bgg.-! ,< n$KE ))#*A*/$A*3 `i    '** $ d "  |CIYe1+M+ U+?иU]и cи g`/R +6;+J+@+.+и/@EиE/01%463!2#!".5##3>76'&&5463!2#!"326672"3!26554#"54332 , ,] DMJ/ ,_^[( )bgg.-! ,< n$KE   O))i**&y    '*8* $ t "   v|CQ[g#9+S++S и#GиZи_иeиib/V+>C+R+H"+&6+"и/HMиM/01".546332!546332###3>76'&&5463!2#!"3266723!26557#"54332S,, ,DMJ/ ,_^[( )bgg.-! ,< n$KE   7))`^&{s    '*,* $ n " 3\ \y5Yef/]/fи/!и!/+и]dGd9JиJ/dg`/"'+,+ +и/,1и1/01##3>76'&&5463!2#!"326672'&&'&''&7>7432#"54332DMJ/ ,_^[( )bgg.-! ,< n$KE  &o u,*p HsQ+ 0 ))    '*D* $ z "   :_#  i?Dj%9DJ% EpLI]iѻ1+T +ha+?иaJиJ/AT&T6TFTVTfTvTTTTTTT ]ATT]hkd/Y+6;+O+@+.+и/@EиE/01%#".54>32##3>76'&&5463!2#!"3266724.#"32>7#"54332L.PiP+0Q9 9Q0+P>$0)))?**?))>**>y    '*8* $ t " ** *( (Ft,5K3+*0+J9+J?иJM/U/k/$+O\+CJ+5+ и /5и/5и/*и5/и//3иOdи\r016"'"&5546327#"&55463!2##66767#26#"5433232##32##"554&##"&5546!32##"554&##"&5546  EJM$INK ;!l P#@(3)) /$  B/$  !  - !  $q"=#)'#)'I.7Mc5+,2+L;+LAкR59R/YLe>/]N+$+CJ+5+ и /5и/5и/*и5/и//3016"'"&5546327#"&55463!2##66767#26#"5433232##!"&5543323!2  EJM$INK ;!l P#@(3)) /)  n!  G !  >"$)$Iq.`ig+,d+Q>+6g96/FиF/6Y>mиQsиQ}иQp/]2+$+;T+u|+KB+g+ и /gи/gи/*иgaиa/e016"'"&5546327#"&55463!2##66#!"&55463!26554&#!"&55463!2#!"3!2767#26#"5433232##  EJM$INK ;!l P#@  /#-+ #-o <(3)) !  ' !  #*K*9$K*= c"Iq.BMtQ+lY++Qt9/Yl9/,/и//+и+/Qaиa/и,/<+$+Vo++f]++ и /и/и/*иf2и2/<HиH/<Kиwиw/|и|/и/и]016"'"&5546327#"&55463!2##6646332##".5##".55463326554&##"&5546332##"332677>76767#26#"5433232##"3326554&#  EJM$INK ;!l P#@^,,6 %K!, !,D" x(3))  t !  # !  **) N*9$I*<  n"m   I|BR[q˻Y/+@V+ N+/Y9/FN_и eи oи sb/K +81+C+gn+Y%+% и /Y,и,/Y/и//1>иYSиS/1W017463!2#!".56"'"&5546327#"&55463!2##66"3!26554&#%767#26#"5433232## , ,]  EJM$INK ;!l P#@  (3)) **!  4 !    +v"I|JT]s׻[7+HX++7[9/L иSиaиgиqиud/O+@9+K+ip+[-+-(и(/[4и4/[7и7/9Fи[UиU/9Y01".546332!546332#6"'"&5546327#"&55463!2##663!2655767#26#"5433232##0,, ,O  EJM$INK ;!l P#@  (3)) jh&(!  + !  l lL"d"Iw.W`v^+,[+ud+Cu9ujиuxg/$+ls+^+ и /^и/^и/*и^XиX/\016"'"&5546327#"&55463!2##66'.'''&67>7632'767#26#"5433232##  EJM$INK ;!l P#@:2c 7cSC?Rb8CpS0 0 (3)) w!  H !   Lb ".;D$$E<0 % '4632767#26#"5433232##  EJM$INK ;!l P#@?YM11NYA b[ 0 "- .#/ ZU (3)) !  4 !  ![56Y U34T#$ 0e  @9/ :HR)   P' +g"IpBV_uѺ\4+@Z+tc+c 4\9 /\/ Mtiиtwf/R+81+H+kr+]%+% и /],и,/]/и//1>и]WиW/1[01%#".54>326"'"&5546327#"&55463!2##664.#"32>767#26#"5433232##,.OiY9>/1pEиE/1LиpPиP/peиpsb/5:+$+N/+gn+CH+Y+ и /Yи/Yи/*иYSиS/W016"'"&5546327#"&55463!2##66!3!2#!"&55463!2#!"!2767#26#"5433232##  EJM$INK ;!l P#@"  , , 5 P(3)) !  , !  vP **  O ! s#_"+0++9)и+6и0O-/3/+E +*7+ и/Eи/Eи/$иEHиH/IиEKиK/01%6'"&5546333#"&55463!2##66734332##"5###"54332667#26 7mI8; 3Z A/&&&& % #  ! ! F]AG;Apd+G]fd+)a+<A+MR+<,иR-и-/M3иR9и9/Mh0/O/!+IV+-:+^+ и /^и/^и/'и^)и)/^aиa/bи^dиd/016'"&554637#"&55463!2##667354332##"55###"54332!2##"554&#!"&55467627#66 !745 8~; .V @-&&&&k /)  6)"  0 ! #)$)A+G]fѻd+SL+)a+<A+3.+<,и.9и3h0/WH+!+-:+^+ и /^и/^и/'и^)и)/^aиa/bи^dиd/016'"&554637#"&55463!2##66734332##"5###"54332!"&5543323!2627#66 !745 8~; .V @-&&&&/)  )h"  G ! w8Z$)$@Aqy+]y+V3+)}+ns+e`+`;и;/3CиC/eMиn^и`kиeb/Z/+!+8Q+_l+H?+z+ и /zи/zи/'иz)и)/z}и}/~иzи/016'"&554637#"&55463!2##66#!"&55463!26554&#!"&55463!2#!"3!2354332##"55###"54332627#66 !745 8~; .V @-y /#-  #-O &&&&)"  & ! #*F*4$F*8 +A|d?Oktr,+C+=o+`e+WR+W иRKиK/`PиR]иWvT/H +5.+@+Q^+l"+"и/l)и)/l,и,/.;иl=и=/loиo/.pиlrиr/01%463!2#!".56'"&554637#"&55463!2##66"3!26554&#354332##"55###"54332627#66 , ,= !745 8~; .V @-  d&&&&)x**"  0 !    )A|dGcmvt4+e+Eq+X]+OJ+e иJи/OиXHиJUиJlиl/OxL/h+=6+d+IV+n*+*'и'/n1и1/n4и4/6CиnEиE/nqиq/6rиntиt/01".546332!546332#6'"&554637#"&55463!2##667354332##"55###"543323!2655627#66[,, , !745 8~; .V @-&&&&  )jh&"  ( ! (Gf fP!Aw+Gpyǻw+)t+<A+3.+<,и.9к\393{0/!+-:+q+ и /qи/qи/'иq)и)/qtиt/uиqwиw/016'"&554637#"&55463!2##667354332##"5###"54332'.'''&67>7632%627#66 !745 8~; .V @-&&&&a2c 7cSC?Rb8CpS0 0 )r"  ? !  Lb ".;D$$E<0 % '4632354332##"55###"54332627#66 !745 8~; .V @-?YM11NYA b[ 0 "- .#/ ZU &&&&)"  0 ! ![56Y U34T#$ 0e  @9/ :HR)   P'  )Apj?Sox9v,+@+=s+nW+e`+e и /A@&@6@F@V@f@v@@@@@@@ ]A@@]`JиJ/n]и`kиezb/E+5.+O+_l+p"+"и/p)и)/p,и,/.;иp=и=/psиs/.tиpvиv/0174>32#".6'"&554637#"&55463!2##6632>54.#"%#"54332354332##"55#%627#66.SqD>pV22Vp>DqS. !745 8~; .V @-#?V3-VB))BV-3V?#M&&&&)'+C/.C,,E..E"  0 ! --..m)Ft,5U3+*0+T9+T?и9OܸDиTIиTW/) $ q$7)) !  8 !  #)$1 $ Sb*2F\1+(.+94+4?кK19K/R9^6/VG+ +3@++ и /&и+и+/.и.//и1016"'"&554633#"&55463!2##66667#2%54332##"5#"5543!"&5543323!2}  9IHLJ sl S >$7)) /)  e!  S !  L` $ h#)$SqX*\dxݻc+(`+M:+2c92/BиB/2U:eиMkи:qиMzh/Y.+ +7P+er+G>++ и /&и]и]/`и`/aиc016"'"&554633#"&55463!2##66#!"&55463!26554&#!"&55463!2#!"3!2667#2%54332##"55#"5543}  9IHLJ sl S > /#-* #-o $7)) !  & !  #*K*9$K*= ó8 $ S|C>NVjǻU++<R+ J++U9/BJWи ]иJcи lZ/G +4-+?+Wd+*#+#и/-:и*OиO/*RиR/-Sи*U01%463!2#!".56"'"&554633#"&55463!2##66"3!26554&#%667#2%54332##"55#"5543 , ,]u  9IHLJ sl S >  $7)) **!  8 !    1  $ S|CFPXlӻW3+DT++3W9/H иOиYи_иeиn\/K+<5+G+Yf+2+++&и&/5Bи2QиQ/2TиT/5Uи2W01".546332!546332#6"'"&554633#"&55463!2##663!2655667#2%54332##"55#"5543R,, ,z  9IHLJ sl S >  y$7)) jh&+!  $ !  l lQ/ $ Sw*S[oZ+(W+b]+?b9]hиbq_/ +\i++ и /&иTиT/WиW/XиZ016"'"&554633#"&55463!2##66'.'''&67>7632'667#2%54332##"5#"5543}  9IHLJ sl S >'2c 7cSC?Rb8CpS0 0 $7)) v!  E !   Lb ".;D$$E<0 %  $ Sp*gomn+JC+(k+_V+AVV]AV)V9VIVYViVyVVVVVVV ].V_9AJ&J6JFJVJfJvJJJJJJJ ]AJJ]6CJ9JHиH/_\и\/vq|и_s/ +p}+Z+ и /&иZEиE/hиh/kиk/lиn016"'"&554633#"&55463!2##66&&''&&'''&6766'4327>'4632667#2%54332##"55#"5543}  9IHLJ sl S >"?YM11NYA b[ 0 "- .#/ ZU $7)) !  8 !  ![56Y U34T#$ 0e  @9/ :HR)   P' 1 $ SpK>RZnǻY++<V+a\+ +Y9 /\?и?/ I\gиap^/N+4-+[h+D+*#+#и/-:и*SиS/*VиV/-Wи*Y01%#".54>326"'"&554633#"&55463!2##664.#"32>667#2%54332##"5#"5543K.Oi$=P+0P9 9P0+P=$s$7)) '+D//D+*C/.C:!  8 !  ..--k1 $ 7632%627#66%54332##"55#"5543##"54332 !745 8~; .V @-B2c 7cSC?Rb8CpS0 0 )F&&l _&&r"  ? !  Lb ".;D$$E<0 % 32#".6'"&554637#"&55463!2##6632>54.#"627#66%54332##"55#"5543##"54332.SqD>pV22Vp>DqS. !745 8~; .V @-#?V3-VB))BV-3V?# )F&&l _&&'+C/.C,,E..E"  0 ! --...)& $  PtC-IQͻP++M+5>+5/:и/Bи>Fи5S1/7/#+J +.C+B;+  и / и/Jи/Jи/)иJMиM/NиJPиP/01%6##"'"&5546333#"&55463!2##6654332##"5#"554335#"5543667#2FKO$G&J$ =j R#B)) )3 # ! ! 7t # # x+Sb*FOeѻM+(J+2;+2,7и,?и;CиMTиT/M[2g./_P+ ++@+M+?8+ и /Mи/&иMGиG/MJиJ/K016"'"&554633#"&55463!2##6654332##"5#"554335#"5543267#62!"&5543323!2}  9IHLJ sl S >)) $0 /)  W!  [ !  # k # VD#)$SqW*\x+(|+dm+292/d:2BиB/dLи2U:]и:iи:qиmuиd`/Y.+ +7P+]r+G>+qj++ и /и/&иyиy/|и|/}016"'"&554633#"&55463!2##66#!"&55463!26554&#!"&55463!2#!"3!254332##"55#"554335#"5543267#62}  9IHLJ sl S >  /#-* #-o Y)) $0!  0 ! #*K*9$K*= q9 # \ # +S|C>Njsq++<n+V_++q9/V иBVJOиJ[иJcи_gиVuR/G +4-+Od+?+c\+q#+#и/q*и*/-:иqkиk/qnиn/-o01%463!2#!".56"'"&554633#"&55463!2##66"3!26554&#54332##"55#"554335#"5543267#62 , ,]u  9IHLJ sl S >  )) $0**!  F !     # e # AS|CHRnwu5+Fr+Zc+5u9/J иZZиQиSи_иgиckиZyV/M+>7+Sh+I+g`+2+++&и&/24и4/7Dи2oиo/2rиr/7sи2u01".546332!546332#6"'"&5546333#"&55463!2##663!265554332##"55#"554335#"5543627#66U,, ,z  8KGLL >l S >  )) $0jh&!  3 ! l lQs1 # ] # -Sp*gٻ+JC+(+ox+.xo9AJ&J6JFJVJfJvJJJJJJJ ]AJJ]6CJ9JHиH/Vxo9V/AVV]AV)V9VIVYViVyVVVVVVV ]_\и\/_hиh/oi_tиt/iuи_|и|/i}иxиok/ +h}+|u+Z+ и /и/&иZEиE/}{&и/и/и&016"'"&554633#"&55463!2##66&&''&&'''&6766'4327>'463254332##"55#"554335#"5543267#62}  9IHLJ sl S >&?YM11NYA b[ 0 "- .#/ ZU )) $0x!  F ! ![56Y U34T#$ 0e  @9/ :HR)   P'   # f # ASpK>Rnwu0+r+Zc+ 0u9 /ZT?и?/ IT_иTgиckиZyV/N+:+Sh+D+g`+u(+(#и#/u/и//1иuoиo/urиr/s01%#".54>32#6676"'"&554633#"&55463!24.#"32>54332##"55#"554335#"5543267#62K.Oi  9IHLJ sl $=P+0P9 9P0+P=$)) $0'+D//D+*C/.CU!  F ! }..--y # f # A+=6+ и/Qи/Qи/$иQTиT/UиQWиW/01%6'"&5546333#"&55463!2##6654332##"5#"554335#"5543##"54332667#26 7mI8; 3Z F0t&&j jj W&&% #  ! ! F]z # # V~;Aqy)[w~{+T1+'x+cl++9и9/1AиA/Kиc]hи]pиltи/X-++6O+\q+F=+pi+ +  и /и/%и'и'/xиx/yи{и{/016'"&554633#"&55463!2##66#!"&55463!26554&#!"&55463!2#!"3!254332##"55#"554335#"5543#726##"54332 9i@8~; -V E/z /#-  #-O &&o oo Q&&"  , ! #*F*4$F*8 fL} # [ # &XA|dGQmvt4+I+Eq+Yb+w|+I и|и/wи|PиP/YS^иSfиbjиw/L+=6+H+Rg+f_+1*+*'и'/14и4/6Cи1EиE/1nиn/1qиq/6rи1tиt/01".546332!546332#6'"&554633#"&55463!2##663!265554332##"55#"554335#"5543627#66##"54332[,, , 457"99 .V E.  &&o oo  )9&&jh&"  . ! f fNkI{ # [ # (SAw)Rnvu+'r+ZN+w|+>NZ9ZT_иNcиc/TgиNkиk/w/+Sh+g`+o +  и /oи/oи/%иo'и'/orиr/sиouиu/016'"&554633#"&55463!2##66'.'''&67>763254332##"55#"554335#"5543627#2##"54332 8j?6< .V E/C2c 7cSC?Rb8CpS0 0 3&&o oo  (N&&f"   H !  Lb ".;D$$E<0 % +9)и)/9O;/&+5B+/+ +ии/и,и/JиL01!!2#!"&55463!5!"&5546335#"&55463!2##32!2##"554&#!"&554635#"   {g @bv/)  "" ! ! #)$T<37MC<+4++.5+<"и"/EX/>YEX./.>YEX4/4>YG8+&+ +.иии,и601!!2#!"&55463!5!"&5546335#"&55463!2##32%35#!"&5543323!2"   {g @bvH/)  "" ! ! ;')$S+4"и"/>)и)/>]PA+&+7H+/+ +ии/и,и/XиZ01!!2#!"&55463!5!"&5546335#"&55463!2##32463!2#!".57"3!26554&#35#"   {g @bv , ,>]  |""| ! ! ** ~ ~ 763235#"   {g @bv -p t+*nCqR1 0 |""|  ! !  DZ " mEGr % 9HP' *324.#"32>35#"   {g @bv@^m-EE@1@^m,-m_?I6LQQK55KQRK6|""| ! ! ^3@$  %2"2?$ $@3!''! ((?tBX\Y,+"+=Z+WF+WLиW^C/I/5.+"+>+NU+"и/и/"и/#и>+и.;и>Yи.[01#>76"'"&554632675#"&554633#"&55463!2##32#"5433232##3#x!HGC"NPP$LRN 324.#"32>7#"5433232##35#nH:  Qd % iU L`3Tl9>mQ//Qm>9lT3G&@S-2S<""+"и/и/"и/#и>+и.;и>Oи.Q01#>76"'"&554632675#"&554633#"&55463!2##32#"543323#"MKG!NST(QUP >FG  hT Th))2  #  "  ! ! B~ Mb8NZ^["++3\+YR+="[9=/DY`U/H9++$++4+и/и4!и$1и4[и$]01#6676##"&55463!5#"&5546335#"&55463!2##32!"&5543323!2'#"5433235#zM9  Xg  iU L`/)  #))諫 " # ! ! $)$`w<37;4+9+49/5995/.8 +&+/+ии/и,и/4и6и8и:01#32#!"&5546335#"&554633#"&55463!2##32%!!35#6   k hfz$ ."") ! ! ;)<37MQC<+4+O+49/<"и"/5O95/.NG8+&+ +/+ии/и,и/4и6иNиP01#32#!"&5546335#"&5546335#"&55463!2##32%35#!"&5543323!235#"~   ua @\pB/)  K"" ! ! ;')$ d""d ! ! #*G*7#G*: nd763235#35#"|   ua @\p -p t+*nCqR1 0 "" ! !  DZ " mEGr % 9HP' 0p+9и/9O;/ +5B++*#+и и5 и#2иJи L01!"&5546335#"&55463!2##32!5!"&55463!2#!32##"554&#!"&554635#" {g @bv { R x/)   ! ! s""s#)$O<9OSE>+P +6#+Q+>и/I:+ ++,%+и и%4иPи R01!"&5546335#"&55463!2##32"&55!"&55463!2#!#!"&5543323!235#" {g @bvs R !/)  C ! ! "" ')$Y<|OSI&+P +>++Q+&и/M"+ +?D++4-+и и?*и-<иPи R01!"&5546335#"&55463!2##32#!"&5546335!"&55463!2#!32#!"3!235#" {g @bv , ,{ R x   M ! ! **s""s !  Lиjиj/ܹzии иP01!"&5546335#"&55463!2##3246332##".5##".55463326554&##"&5546332##"33267667672#!##"&55!"&55463!2%35#"3326554&#" {g @bv,,  'N!, !,D&   x+{ R  z  ! ! **) D*/$?*2  "\ \"   <|'GW[K+X1++BY+S+6и6/=и=/]P!+:3+H+C(+ +ииC0и3@иCXи3Z01746335!"&55463!2#!32#!".5!"&5546335#"&55463!2##32"3!26554&#35# ,{ R x ,>\ {g @bv  `*r""r* ! !   75!"&55463!2#!35#" {g @bv u*'vEnO/} R v-n ! !  oHGv  ! 7AH$h""kEZ I75!"&55463!2#!!"&5546335#"&55463!2##324.#"32>35#*@_m,EE@26Ra,{ R x,bR5 {g @bvP6MQQK66KQQM6 3?#  $2"-<$m""m$< ! ! P!&&! '' ItC3[_\ +>E+]+',+, и'a)/#/ + -+WF++и иF=и=/FKиK/\и ^01!"&5546335#"&55463!2##324332##"55#"554376##"&5'"&554632>7>%35#n cO Ma)) {  ?K+ GD< QUO!PQMn ! !  " "   7" TvL3G[+_+.+OT+и/4и4/T>и>/THиOK/9+&+C+q+/+HU+/и,иQиQ/q~`и`/~eиe/qlиl//и0174>32#".!"&5546335#"&55463!2##3232>54.#"%4332##"55#"5543"&55"'"&5546332667>76#35#2Vp>8oX77Xo8>pV2 aM K_,ET)$TH00HT$)TE,)) BB; B&QL!NPK @Kn '8%$8((9%%9 ! ! E""# #5 " l ~" "   LItC+SWT +6=+U+*#+*Y&/ / +O>++и и>5и5/>CиC/Tи V01!"&5546335#"&55463!2##32#"543326##"&5'"&554632>7>%35#x cO Ma))  ?F+!LH> QUO#TSJd ! ! ~"  '"Q[+RhliX+O/+j+*#+*n&/[T+ ++и и[,и,/AܹN0и0/A<и7>76#!"&5543323!235#p aM K_))8 LJB MSN!OPL 7>#"5433235#W /#- #-X  aM K_. BE+ GE< MRN#PPK))꣣#*2* $2*$ @ ! ! <  n k"%Q|D3Cjvzջw+MT+.x+un+и/u и /n?и?/u|q/< +&+4+fP+/+/и,иfULиL/UZиZ/faиa//wиy017463!2#!".5!"&5546335#"&55463!2##32"3!26554&#6##"&55"'"&554632>7>#"5433235# , ,D aM K_  7 ?G+ GE= MSN!OPL))꣣H** ! ! g g 1  { w"+9QvBiuyv +LS+w+tm+1 t9t{p/ +eO++и иeTKиK/TYиY/e`и`/vи x01!"&5546335#"&55463!2##32'&&'''&7>743276##"&55"'"&554632>7>#"5433235#s aM K_2 &o v,*pHsQ+ 0 ^ BJ+EA9 MSN!OPL))꣣ ! !   :_"  iABk" %9DJ%    "H?иDи F01!"&554633#"&55463!2##32"&5#"&55463!2####"&5!#!!6p q hl R +y  ! !  ="" = : <CY]OH+Z +[+Hи/# Z9#/5и5/[=и=/#@SD+ ++,%+и и%4и%>иZи \01!"&5546335#"&55463!2##32"&55!"&55463!2####"&55##!"&5543323!235#" {g @bv8 R +/)  B ! ! ""  ')$\*.#d""d>*1 - ! ! ;d<|'GW[_K+X1+BY+S+1иBи/6и6/=и=/X\и\/Y]иaP!+:3+H+C(+ +ииC0и3@иCXи3Zи\и^01746335#"&55463!2##32#!".5!"&5546335#"&55463!2##32"3!26554&#35#35# ,& R * ,8_ {g @bv  ]*v""v* ! ! | | vиhи j01!"&5546335#"&55463!2##32"&55#"&55463!2####"&55!#7'&&'''&6766743235#" {g @bv R + &m t*&y .  ! ! ""  -  :X !fA?h! % )K =35#25#66*@_m,EE@2G3 R -" {g @bvP6MQQK66KQQM6y= > 3?#  $2"4@"" !* ! ! P!&&! '' {|<#3i4/!/4 и /!  1(+ ++и и и "01!"&554633#"&55463!2##32%!!#!"&55463!26p q hl*=  R A ! ! ;A""<#3Iu?8+  +!+8и/C4+ ++1(+и и и "01!"&5546335#"&55463!2##32%35##!"&55463!2!"&5543323!2" {g @bvH3  R {/)   ! ! ;d""d')$\ {g @bv    R i** ! !   ""7632%#!"&55463!2%35#" {g @bv -p t+*nCqR1 0   R  ! !  DZ " mEGr % 9HP' }""PtC,5A3+*0+@9+@C6/SVO/B3+ ++ и /&и+и+/.и.//и1016"'"&554633#"&55463!2##66667#2!"&5543323!2'#"54332  9IHLJ sl S!=%7/)  #))d!  T !  M?#)$~SqX*\dp˻c+(`+M:+2c92/BиB/2U:hиMnиMrk/Y.+ +7P+G>++ и /&и]и]/`и`/aиc016"'"&554633#"&55463!2##66#!"&55463!26554&#!"&55463!2#!"3!2667#2#"54332  9IHLJ sl S!=  /#-+ #-o %7))!  & !  #*K*9$K*= `8S|C>NVbU++<R+ J++U9/BJZи `и d]/G +4-+?+*#+#и/-:и*OиO/*RиR/-Sи*U01%463!2#!".56"'"&554633#"&55463!2##66"3!26554&#%667#2#"54332 , ,]x  9IHLJ sl S!=  %7))**!  8 !    1nS}CFPXdW3+DT++3W9/H иOи\иbиf_/K+<5+G+2+++&и&/5Bи2QиQ/2TиT/5Uи2W01".546332!546332#6"'"&554633#"&55463!2##663!2655667#2#"54332S,, ,w  9IHLJ sl S!=  y%7))jh&*!  $ !  k kQf2Sw*S[gZ+(W+f_+?f9fib/ ++ и /&иTиT/WиW/XиZ016"'"&554633#"&55463!2##66'.'''&67>7632'667#2#"54332  9IHLJ sl S!=+2c 7cSC?Rb8CpS0 0 %7))v!  E !   Lb ".;D$$E<0 % +SpK>RZfY++<V+e^+ +Y9 /^?и?/ Ieha/N+4-+D+*#+#и/-:и*SиS/*VиV/-Wи*Y01%#".54>326"'"&554633#"&55463!2##664.#"32>667#2#"54332K.Oi+/+&+01%#".54>32#"5433232##4.#"32>!"&55463!2'!"&55463!2H!:P./O:!!:O/.P:!)) (7 7((7 7( ( )F54G)*G44H[~"/}2%%21%$2'  " "!Np'=Sao) ++-2+A]A)9IYiy ]A&6FVfv ]A]:и:/2Aи-Gи-Qиmиm/-q//D/kb+]T+)6++IP+#+01#".54>324.#"32>!2##"554&#!"&5546%#"5433232##!"&55463!2'!"&55463!2F!9O./N: :N/.O9!C(6 6((6 6(/) $ /))  ( t$;,,<#$<*+<#''''#)$11   " z"!N)=Sao4 +IB+*+(+(A**]A*)*9*I*Y*i*y******* ]A4&464F4V4f4v4444444 ]A44]*mиm/(q/F?+kb+]T+9+/+&+?M01#".54>32#"5433232##4.#"32>!"&5543323!2!"&55463!2'!"&55463!2? 7L,,L77L,,L7 )) %32&&23%C/)   ( S$@00@$$?//?" ))**H#)$  " #" Nq1EYo}xP<+*+2F+n]+]и/и/n!и!/AFF]AF)F9FIFYFiFyFFFFFFF ]AP&P6PFPVPfPvPPPPPPP ]APP]ncиFи/n`//EX%/%>Y.+~+yp+AK++el+U7+% 7ZиZ/aиa/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>324.#"32>#"5433232##!"&55463!2'!"&55463!26  /#-+ #-o !:N..O9!!9O..N:!C'7 6((6 7'))  ( #*6*&$8*( "9))9""9))8"$ $$ $"b  " r"!NpSw +=*+qx++A&6FVfv ]A]" 9"/2и2/"EqUиqcиxи/////_/EX@/@>YEXT/T>Y++}+hm+++_и/@&m.и./h6и_ZHиH/ZMиM/&rиsии/и/01#".54>32##".55463326554&##"&5546332##"332677>76%#332##"&5546332##"324.#"32>#"5433232##!"&55463!2'!"&55463!2F!:N..O9!!9O..N:!P6 %J!, !,B" .  , ,  '7 6((6 7'))  ( "9))9""9))8 :*%$5*(  R= **  <  $ $$ $"c  " r"!N|'7Kao}5B+++8+`O+` и /O3и3/A88]A8)898I8Y8i8y8888888 ]AB&B6BFBVBfBvBBBBBBB ]ABB]`Uи8{и{/`R/0 +yp+kb+(+#=+W^+G+017463!2#!".5#".54>32"3!26554&#4.#"32>#"5433232##!"&55463!2'!"&55463!2 , ,]`!9O./N: :N/.O9!  (6 6((6 6())  ( N**$;,,<#$<*+< m m  '''')"  " z"!N|/9I_m{=B&+1+:++1 и8A::]A:):9:I:Y:i:y::::::: ]AB&B6BFBVBfBvBBBBBBB ]ABB]MиSи]и:yиy/}P/4+wn+0+i`++=+U\+G!+01".546332!546332##".54>323!26554&#"326#"5433232##!"&55463!2'!"&55463!20,, ,!9O./N: :N/.O9!  T@ 6((6 @T))  ( \Z&":**:"":)*9x\ \f/3 $%5_"R  " r"!Nv:P`n| W +Q+O>+AQQ]AQ)Q9QIQYQiQyQQQQQQQ ]$Q9ODAW&W6WFWVWfWvWWWWWWW ]AWW]Qzиz/O~A/xo+ja+T+\+FM+01#".54>32'&'''&67>7667663#"5433232##%4&#"32>7!"&55463!2'!"&55463!2F!9O./N: :N/.O9! 0] r.k@cK4/ )) T@@U(6 6( ( i$>,-=$%=,,= @W  ".Bk  % +3=%T"1982''  " ~"!Nq';OesmF+2 +<+dS+SA2&262F2V2f2v2222222 ]A22]A<<]A<)<9<I<Y<i<y<<<<<<< ]AF&F6FFFVFfFvFFFFFFF ]AFF]dYи<и/dV/7+}t+of+-+#A+[b+K+01%#".54>32#".54>324.#"32>4.#"32>#"5433232##!"&55463!2'!"&55463!2,.Oi324.#"32>!"&55463!2'#"&5546332&&&&2G+,G22G,+G2B!.+I+,+O+01#".54>32#"5433234332##"55#!2##"554&#!"&554674&#"3267!"&55463!2'#"&55463321G-.G00G.-G1&&&&k /)  ????????P] s v9,,98,+9 D#)$);:*(==  " w"!Z/EYguEP +;4+F+$)++A&6FVfv ]A]$и!AP&P6PFPVPfPvPPPPPPP ]APP]Plиl/Fsиs/w/81+qh+cZ+U+K+"+Kи/1?01#".54>32734332##"5###"54332!"&5543323!24.#"32>7!"&55463!2'#"&55463320G./G00G/.G0&&&&/)  F / /  / / P] s c =./< !<..Y.++{r+Ae++GT+m7+% 7PиP/^и^/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>327354332##"55###"543324&#"32>7!"&55463!2'#"&5546332y /#-  #-O 0G./G00G/.G0&&&&>>>?/.R] s #*6*#$5*( 7**66**6Y Ȼ#65$!!  " l"!Z|d'CS_m{gZ+G+T+8=+/*+/ A&6FVfv ]A]8(и*5и*OиO/AZ&Z6ZFZVZfZvZZZZZZZ ]AZZ]Zrиr/Tyиy//},/L +wn+i`+D+#W+)6+]+:и:/w@и@/01%463!2#!".5#".54>32734332##"55###"54332"3!26554&#4&#"3267!"&55463!2'#"&5546332 , ,=1G-.G00G.-G1&&&&  ????????P] s Q**9,,98,+9cm p p );:*(==  " w"!Z|d/KUao}{\&+M+V+@E+72+M и2и/7A&6FVfv ]A]@0и2=и2TиT/A\&\6\F\V\f\v\\\\\\\ ]A\\]\tиt/V{и{/74/P+yp+L+kb++Y+1>+_!+!:и:/yHиH/01".5546332!546332##".54>327354332##"55###"543323!26554&#"3267!"&55463!2'#"&5546332[,, ,1G-.G00G.-G1&&&&  ????????P] s [Y&7**77**7^ ɼ8W Wm'87(&::  " o"!Zv/Vjx3a +W+$)++A&6FVfv ]A]$и!к@ 9Aa&a6aFaVafavaaaaaaa ]Aaa]a}и}/Wи//y+tk+\+f+"+,и,/01#".54>32734332##"5###"54332'&'''&67>7667663'4.#"32>7!"&55463!2'#"&55463320G./G00G/.G0&&&&Y 0] r.k@cK4/  / /  / / P] s m =./< !<..<_  @W  ".Bk  % +3=%''''  " z"!Zm/jvq +QI+$)++AQ&Q6QFQVQfQvQQQQQQQ ]AQQ]Q$и!к3 9<IQ9QNиN/Qkиk/Aq&q6qFqVqfqvqqqqqqq ]Aqq]qи/Qи/kи//+w+n+"+^+и/,и,/^KиK/t01#".54>327354332##"55###"54332&&'&'&&'''&67>743276654632"4&#"3267!"&55463!2'#"&55463321G-.G00G.-G1&&&&BVL69J14E+ 0 B<?=/  VR  ????????P] s {9,,98,+9o#R0*O*"J+UI " 2:D-  9b%3nB  Bg# );:*(==  " w"!Zqg'CWcq^+N +X+8=+/*+/и/A&6FVfv ]A]8(и*5и*DиD/AN&N6NFNVNfNvNNNNNNN ]ANN]A^&^6^F^V^f^v^^^^^^^ ]A^^]^vиv/X}и}//,/S+{r+md+I+#[+)6+a+:и{@и@/01%#".54>32#".54>327354332##"55###"543324.#"32>4&#"3267!"&55463!2'#"&5546332g3Tn +4+2+2и-ܸ"и2'A44]A4)494I4Y4i4y4444444 ]A>&>6>F>V>f>v>>>>>>> ]A>>]>ZиZ/4aиa/2e//_V+C+QH+&+)0+9+01%#".54>32#"5433232##32##4.#"32>!"&55463!2'!"&55463!2H!:P./O:!!:O/.P:!))  (7 7((7 7( ( )F54G)*G44H[~  }2%%21%$2'  " "!Nq';[o}f+2 +\+Z?+?A2&262F2V2f2v2222222 ]A22]ZEи?UܸJиZOA\\]A\)\9\I\Y\i\y\\\\\\\ ]Af&f6fFfVfffvfffffff ]Aff]\и/ZB/7+~+yp+-+GN+#a+k+QX+pFиF/01%#".54>32#".54>324.#"32>7#"5433232##32##%4.#"32>7!"&55463!2'!"&55463!2,.Oi3274332##"5#"55434.#"32>!"&55463!2'!"&55463!2H!:P./O:!!:O/.P:!)) \(7 7((7 7( ( )F54G)*G44Ha $ 2%%21%$2'  " "!fpC)=Q_m%H +>++*и0и6A>>]A>)>9>I>Y>i>y>>>>>>> ]AH&H6HFHVHfHvHHHHHHH ]AHH]Hdиd/>kиo/-/i`+[R+"+C+*7+M+01#".54>32!2##"554&#!"&55464332##"55#"55434.#"32>7!"&55463!2'!"&55463!2L 9M-.M8 8M.-M9 /) $ )) h'56''65'~   t$;,,<#$<*+<#)$Y $ u''''  " z"!fb';Q_m#2 +G@+(++ A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22](kиk/o/D=+i`+[R+7+-+-и/!=K01#".54>3274332##"5#"55434.#"32>!"&5543323!2!"&55463!2'!"&55463!2I 7L,,L77L,,L7 )) m%32&&23%N/)     S$@00@$$?//?J?S2 $ n))**H#)$  " "!gqX1EYm{|P<+*+2F+"+и/AFF]AF)F9FIFYFiFyFFFFFFF ]AP&P6PFPVPfPvPPPPPPP ]APP]Zи"`иfиPи/Fи/"]//EX%/%>Y.+|+wn+AK++Zg+U7+% ^и^/7cиc/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>324.#"32>754332##"55#"55437!"&55463!2'!"&55463!2X  /#-+ #-o 8L--M7 7M--L8 A'45''54'))    #*6*&$8*( "9))9""9))8"$ $$ $  $ V  " r"!gqC'i} +S@+~++A~~]A~)~9~I~Y~i~y~~~~~~~ ]~9/~(и(/A&6FVfv ]A]8 98/HиH/8[jиj/pиvиv/zиz/и/~и/m//!++=V+++MD+jw++Mи/!/и//!2и^и^/nиn/sиs/D01#".54>3246332##".5##".55463326554&##"&5546332##"3326766767254332##"55#"55434.#"32>"3326554&#!"&55463!2'!"&55463!2I 8L--M7 7M--L8 ',,  %K!, !,A$  )) j'45''54'p t c   "9))9""9))8**) ?* $0*+  S  $ o$ $$ $     " r"!f|C'7K_m{AB+++8+ 3+A88]A8)898I8Y8i8y8888888 ]AB&B6BFBVBfBvBBBBBBB ]ABB]3Lи Rи3XиBrиr/8yи }O/0 +wn+i`+(+#=+G+LY+UиU/01%463!2#!".5#".54>32"3!26554&#4.#"32>74332##"55#"55437!"&55463!2'!"&55463!2 , ,]D 9M-.M8 8M.-M9   '56''65'))    N**$;,,<#$<*+< m m  '''' $ Z  " z"!g|C/9Mao}MD&+1+:++1 и8A::]A:):9:I:Y:i:y::::::: ]AD&D6DFDVDfDvDDDDDDD ]ADD]NиTиZиDtиt/:{иQ/4+yp+0+kb++?+I!+?NиN/[01".5546332!546332##".54>323!26554.#"32>74332##"55#"55437!"&55463!2'!"&55463!2S,, , 8N-.M8 8M.-N8  '56''65'))    YW&#:**:##:)*:uY Yi$ %%%x  $ e  " q"!gv:Nbp~%Y +O+A<+и/AOO]AO)O9OIOYOiOyOOOOOOO ]$O9<GAY&Y6YFYVYfYvYYYYYYY ]AYY]Yuиu/O|иA>/zq+lc+T+^+T;и;/H01#".54>32'&'''&67>76676634332##"55#"55434.#"32>7!"&55463!2'!"&55463!2M 8N--N8 8N--N8  0] r.k@cK4/ )) g'56''65'~   i$>,-=$%=,,= @W  ".Bk  % +3=%#) $ n('''  " ~"!fqJ';OcqZ+2 +P+B=+Bи/=(и(/A2&262F2V2f2v2222222 ]A22]=HAPP]AP)P9PIPYPiPyPPPPPPP ]AZ&Z6ZFZVZfZvZZZZZZZ ]AZZ]Zvиv/P}иB?/7+{r+md+-+#U+<I+_+01%#".54>32#".54>324.#"32>4332##"55#"55434.#"32>7!"&55463!2'!"&55463!2J.Oi3234332##"5###"543324.#"32>!"&55463!2'#"&55463322G+,G22G,:W&&rb&&\!.32!2##"554&#!"&55464332##"55#"&554634&#"326#"54332!"&55463!2'#"&55463321G-.G00G.-G1 /)  h&&c S????????&&] s v9,,98,+9#)$O "m);:*(==*x  " w"!Z)?K_m{=V +5.+L++@E+A&6FVfv ]A] и%и%/AV&V6VFVVVfVvVVVVVVV ]AVV]Vrиr/Lyиy/@}H/2++wn+i`+[+Q+!++901#".54>3274332##"5#"&55463!"&5543323!2'##"543324.#"32>7!"&55463!2'#"&55463320G./G00G/.G0v&&c b/)  &&W / /  / / P] s c =./< !<..Y.++x+A_++FS+g7+% IиI/7nиn/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>32754332##"55#"&554634&#"32>##"54332!"&55463!2'#"&5546332y /#-  #-O 0G./G00G/.G0v&&c U>>>?/.&&] s #*6*#$5*( 7**66**6b*"$76%!!|  " l"!Z|d'7MYeskT+++N+>9+Z_+Z A&6FVfv ]A]_3и3/9DиIиI/AT&T6TFTVTfTvTTTTTTT ]ATT]Txиx/Nи/Zb/0 +}t+of+(+#Q+8E+W+};и;/AиA/01%463!2#!".5#".54>32"3!26554&#54332##"55#"&554634&#"326##"54332!"&55463!2'#"&5546332 , ,=1G-.G00G.-G1  &&c S????????&&] s Q**9,,98,+9 p p "y);:*(=={(  " w"!Z|d/9Kam{sB&+1+:+RM+bg+1 иgи/bA&6FVfv ]A]g8и8/AB&B6BFBVBfBvBBBBBBB ]ABB]MXи]и]/Bи/:и/bj/4+|+0+wn++?+LY+G!+OиO/01".5546332!546332##".54>323!26554.#"32>754332##"55#"&55463##"54332!"&55463!2'#"&5546332[,, ,0G./G00G/.G0   / @> / / &&c V&&] s [Y&8,+88+,7yW Wi$;'##/"  " o"!Zv:Ndp~<E +;+V[+ej+A&6FVfv ]A]$ e9AE&E6EFEVEfEvEEEEEEE ]AEE][Oи`и`/Eи/;и/em/EXO/O>Y+zq+@+J+O\01#".54>32'&'''&67>7667663'4.#"32>74332##"55#"&55463##"54332!"&55463!2'#"&55463320G./G00G/.G0N 0] r.k@cK4/  / /  / / &&c V&&] s h =./< !<..< @W  ".Bk  % +3=%''''"h  " |"!Zqg';Q]iwX+2 +R+B=+^c+^и/A&6FVfv ]A]c(и(/A2&262F2V2f2v2222222 ]A22]=HиMиM/AX&X6XFXVXfXvXXXXXXX ]AXX]X|и|/Rи/^f/7+x+sj+-+#U+<I+[+?и?/01%#".54>32#".54>324.#"32>54332##"55#"&554634&#"326##"54332!"&55463!2'#"&5546332g3Tn3274332##"5#"554335#"55434.#"32>!"&55463!2'!"&55463!2H!:P./O:!!:O/.P:!)) b(7 7((7 7(n  )F54G)*G44HO/ # # 2%%21%$2'  " "!fpC/EYguKP +F+$+ и(и$,и4и5и:AFF]AF)F9FIFYFiFyFFFFFFF ]AP&P6PFPVPfPvPPPPPPP ]APP]Plиl/Fsиw7//qh+cZ+1>+K+)+U+(!+01#".54>32754332##"55#"554335#"5543!2##"554&#!"&5546%4.#"32>7!"&55463!2'!"&55463!2L 9M-.M8 8M.-M9 ))~ ~~ /) $ '56''65'o   t$;,,<#$<*+3274332##"55#"554335#"55434.#"32>!"&5543323!2!"&55463!2'!"&55463!2I 7L,,L77L,,L7 ))~ ~~ {%32&&23%N /)     S$@00@$$?//?wS # # ))**H#)$  " "!gqX1Eaul<+*+2b+MV+Mи/M!иFиRиZиV^Abb]Ab)b9bIbYbibybbbbbbb ]Al&l6lFlVlflvlllllll ]All]lи/bи/MI//EX%/%>Y.++v+Ag++F[+q7+ZS+% JиJ/7OиO/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>32754332##"55#"554335#"55434.#"32>7!"&55463!2'!"&55463!2X  /#-+ #-o 8L--M7 7M--L8 ))~ ~~ x'45''54'r   #*6*&$8*( "9))9""9))8` = # Y # $ $$ $  " r"!f|C'7SguS^+++T+?H+? и?38и3Dи3LиHPATT]AT)T9TITYTiTyTTTTTTT ]A^&^6^F^V^f^v^^^^^^^ ]A^^]^zиz/Tи?;/0 +v+qh+(+#Y+8M+c+LE+01%463!2#!".5#".54>32"3!26554&#54332##"55#"554335#"55434.#"32>7!"&55463!2'!"&55463!2 , ,]D 9M-.M8 8M.-M9   ))~ ~~ v'56''65'o   N**$;,,<#$<*+< m m [ # f # ''''  " z"!g|C/KUiwg`&+M+V+7@+M и77и0и<иDи@HиTAVV]AV)V9VIVYViVyVVVVVVV ]A`&`6`F`V`f`v``````` ]A``]`|и|/Vи73/P+x+L+sj++[+0E+e!+D=+01".5546332!546332##".54>32754332##"55#"554335#"55433!26554.#"32>7!"&55463!2'!"&55463!2S,, , 8N-.M8 8M.-N8 ))~ ~~  '56''65'o   YW&#:**:##:)*:`2 # ^ # Y Yi$ %%%  " q"!gv/Vjx;a +W+$+ и(и$,и0и0/AWW]AW)W9WIWYWiWyWWWWWWW ]@W9Aa&a6aFaVafavaaaaaaa ]Aaa]a}и}/Wи/y+tk+\+)+f+(!+01#".54>3274332##"55#"554335#"5543'&'''&67>7667663'4.#"32>7!"&55463!2'!"&55463!2M 8N--N8 8N--N8 ))~ ~~ 0 0] r.k@cK4/ E'56''65'o   i$>,-=$%=,,=rg # { # @W  ".Bk  % +3=%('''  " ~"!fmNj~u +5-+k+V_+_V9A5&565F5V5f5v5555555 ]A55] -5952и2/VPDиD/P[иPcи_gAkk]Ak)k9kIkYkikykkkkkkk ]Au&u6uFuVufuvuuuuuuu ]Auu]uи/kиVR/++Od+B+c\+B/и//XиX/dbjpz01#".54>32&&'&'&&'''&67>743276654632"54332##"55#"554335#"55434.#"32>7!"&55463!2'!"&55463!2L 9M-.M8 8M.-M9 DBVL69J14E+ 0 B<?=/  VR  ))~ ~~ v'56''65'o   y$;,,<#$<*+<#R0*O*"J+UI " 2:D-  9b%3nB  Bg# P # b # ''''  " z"!fqJ';Wkyb+2 +X+CL+Cи/C=(и(/A2&262F2V2f2v2222222 ]A22]=Hи=PиLTAXX]AX)X9XIXYXiXyXXXXXXX ]Ab&b6bFbVbfbvbbbbbbb ]Abb]b~и~/XиC?/7+z+ul+-+#]+<Q+g+PI+01%#".54>32#".54>324.#"32>54332##"55#"554335#"55434.#"32>7!"&55463!2'!"&55463!2J.Oi3274332##"5#"554335#"5543##"543324.#"32>!"&55463!2'#"&55463322F*+F22F+*F2&&Z Za N&&Q .32754332##"55#"554335#"5543!"&5543323!2'##"543324.#"32>7!"&55463!2'#"&5546332.D,-D..D-,D.&&Y Yb [/)  &&H-<;--_] s c =./< !<..Y.++~+Aq++F[+y7+ZS+% IиI/7OиO/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>32754332##"55#"554335#"5543##"543324&#"32>7!"&55463!2'#"&5546332y /#-  #-O r.D,-D..D-,D.&&W W_ L&&F;::<,,a] s #*6*#$5*( 7**66**6c$@ # P # $76%!!  " l"!Z|d/9Ugsq^&+1+V+AR+hm+1 иmи/hA&6FVfv ]A]m8и8/A;Fи;NA^&^6^F^V^f^v^^^^^^^ ]A^^]^и/hp/4++0+}t++[+:O+c!+NG+=и=/01".5546332!546332##".54>323!265554332##"55#"554335#"55434.#"32>##"54332!"&55463!2'#"&5546332[,, ,.D,-D..D-,D.  &&S S^ m-<;--&&] s [Y&8,+88+,7yW W'4 # T # $;'##s  " o"!<&2@L-++'+A'']A')'9'I'Y'i'y''''''' ]A-&-6-F-V-f-v------- ]A--]+KD+0+<3+"*+и01!2#!"&55463!5.54>324&#"326!"&55463!2'#!"5543!2 >  "  Yib+(5+"B++L+ииYQ01!2#!"&55463!5.54>32!2##"554&#!"&55464.#"32>7!"&55463!2'##"554332!;S2  2R;!&E_99_E&/)  1C((D11D((C1X .#<""<".1#"1#)$C    " <&:P^jϻF?++'+?F9/A'']A')'9'I'Y'i'y''''''' ]1C<+ib+ZQ+",++6+ии<J01!2#!"&55463!5.54>324.#"32>!"&5543323!2!"&55463!2'##"554332!:R1  3S32#!"&55463!26554&#!"&55463!2#!"3!2!"&55463!24&#"326##"554332 ;S3  gy%C`;:`D%A /#-  #-Q X dRReeRRd*):"":?0+*#*&*#)*   C32##".55463326554&##"&5546332##"332677>76%#332##"&5546332##"32!"&55463!24&#"326##"554332 ;S3  gy%C`;:`D%6 'M!, !,E$ 2  , ,  X dRReeRRd*)>"">?0+* 7*"$2*%  O: **  9  |  C<| &:J^lx>'+# +K+'>9/AKK]AK)K9KIKYKiKyKKKKKKK ]FK9F/1U1zEXg/g >YC4+wp+*;+P+$+Z"+$ и"иg_01%#!"&55463!5.54>32!2463!2#!".57"3!26554&#4.#"32>7!"&55463!2'##"554332  2R;!&E_99_E&!;S2  , ,>]  1C((D11D((C1X ""<".1#"1.#<*{* _ _ r    " <{&BVdnzf,+C+,f9/f4ACC]AC)C9CICYCiCyCCCCCCC ]7C97/>M7mи>|i'+yr+5e+`W+H+$+R"+$ и"01%#!"&55463!5.54>32!2".5546332!546332#4.#"32>7!"&55463!23!2655##"554332  2R;!&E_99_E&!;S2 e,, ,,1C((D11D((C1X   ^"";!-/""/-";vFE&M    " B BYyr+b+K+ +$ и"и #иR\ܸiais01%#!"&55463!5.54>32!2'&&'''&676674324.#"32>7!"&55463!2'##"554332  2R;!&E_99_E&!;S2 h AQ\/ x)&} . 1C((D11D((C1X ""A".1#"1.#Aa  2) !`=8_& % *rI     " RftI3+# +S+AI&I6IFIVIfIvIIIIIII ]AII]3I9/ASS]AS)S9SISYSiSySSSSSSS ]?S9?/A??]A?)?9?I?Y?i?y??????? ]']'EXo/o >YN,+x+:D+X+$+b"+$ и"иog01%#!"&55463!5.54>32!2#".54>324.#"32>4.#"32>7!"&55463!2'##"554332  2R;!&E_99_E&!;S2 @^m-EE@11@EE-m_?I6LQQK55KQRK691C((D11D((C1X "">".1#"1.#>-9!  !--!  !8.""!!    " Y+0+mf+RI+ Z+9>++`+ии%C01!2#!"&55463!5&&54>32!3!2#!"&55463!2#!"!2!"&55463!24&#"326##"554332 ;S3  gy%C`;:`D%  , ,   4X dRReeRRd*)6""6?0+*0 **  / " r  D?t3IYgu5R*+$+J+H7+H=AJJ]AJ)J9JIJYJiJyJJJJJJJ ]AR&R6RFRVRfRvRRRRRRR ]ARR]Rlиl/Hw:/4/qh+W+cZ+/M+?F+$и/и/$и/01>76"'"&554636675.54>32#"5433232##4&#"326!"&55463!2'!"&55463!2C0C("JGBCQNQM =CE)C0!:O/.P:!)) TA 7((7 ATu   i"8*h  &  "j+9!%=++<~y"E36('8 ! # }"!Ij-CYiwEb+++%Z+B1+B7иBHи1NAZZ]AZ)Z9ZIZYZiZyZZZZZZZ ]Ab&b6bFbVbfbvbbbbbbb ]Abb]BK/4/x+sj+ER+ ]+ +9@+g+и/*и+и+/016##"&554633275.54>3266#"5433232##!2##"554&#!"&55464&#"3267!"&55463!2'#"&5546332  Qd )C1!:O/.P:!0C'@})) /)  @TA 7((7 ATm    D!,/!!/,!B ZC"2#)$?#%  '  hI-CYesA`+++%Z+B1+B7A`&`6`F`V`f`v``````` ]A``]`HиH/`OAZZ]AZ)Z9ZIZYZiZyZZZZZZZ ]B4/LE+}t+of+c + ]+9@+ и/+и+/ES01%6##"&554633275.54>3266#"5433232##!"&5543323!24&#"3267!"&55463!2'#"&5546332  Qd *D09P10P9 /C)D)) /)  RCCSSCCRm    R$/3$%3/$O "#)|w$$%+*&$--  lIk-_u+%v+tc+A&6FVfv ]A]595/c=и=/5EиE/tOиO/5XtiAvv]Av)v9vIvYvivyvvvvvvv ]tf//EX/ >Y\1++:S+ {+JA+kr+ ++и/*и+и+/gиg/016##"&554633275.54>3266#!"&55463!26554&#!"&55463!2#!"3!2#"5433232##%4.#"32>7!"&55463!2'#"&5546332  Qd &B3$-+>3Acc]Ac)c9cIcYcicyccccccc ]Sc#9Ai&i6iFiVifiviiiiiii ]Aii]>0/}+xo+f+ +5<+l+и/&и'и'/01%6##"&554633275&&54>3266#"5433232##''&&'''&766744&#"3267!"&55463!2'#"&5546332  Qd Uc9P10P9 cRE))  CMT*  o2&&)SEETTEESj    BH50#$04H@ {w"   3& !_C<[( % oM#++#"--  oIiAUk{Yt8+2+l+jY+YAt&t6tFtVtftvttttttt ]Att] 8t9 /Lj_All]Al)l9lIlYlilylllllll ]j\/Q++|+G+=o+0'+ah+y3+3и0и/02и2/01#".54>326676##"&554633275.54>324.#"32>7#"5433232##4&#"3267!"&55463!2'#"&5546332-3Tl9>mQ//Qm>9lT30C'E;  Qd )C1!:O/.P:!&@S-2S<""C+50+>.и0;AJJ]AJ)J9JIJYJiJyJJJJJJJ ]AP&P6PFPVPfPvPPPPPPP ]APP]5u2/8/of+U+aX+)M+/<+и/и/и/016676"'"&5546375.54>3234332##"5###"543324&#"3267!"&55463!2'#"&5546332YF?^0":9=$8x; +d6#;*4G**G4&&&&J88K#08JEh s d@T o  & "r)5 $:**:}aGu/430%5! # u"!Jjd/Kam{h&+ +b+@E+72+A&6FVfv ]A]@0и2=и7Pи2VиV/Ah&h6hFhVhfhvhhhhhhh ]Ahh]hи/bи/7S/4/|+wn+MZ++e++1>+и/и/ иBиB/HиH/k016676#'"&55463275.54>32734332##"55###"54332!2##"554&#!"&55464&#"3267!"&55463!2'#"&5546332&66e*9=>:8 -c0!6'1B''B0&&&&X/)  B33CC33B_\  +!<  ! =!+0""0 #)$A#&&#"((  eJ}+G]iwud+SL+%^+<A+3.+LS9/A%&%6%F%V%f%v%%%%%%% ]A%%])<,и.9Ad&d6dFdVdfdvddddddd ]Add]^и/30/PI+x+sj+g + a+-:+  и / )и/)и/P>и>/IW01%6#'"&55463275&&54>3266734332##"5###"54332!"&5543323!24&#"3267!"&55463!2'#"&5546332#9=>:8 -b0BQ1B''B0R@6f&&&&/)  ?D45DD54D\\   ! EH62##26HD FX&$)$&**&%,,  mJl-Ilxqs+++%m+>C+50+A%&%6%F%V%f%v%%%%%%% ]A%%]>.и0;к]59%hиh/As&s6sFsVsfsvsssssss ]Ass]sи/mи/52/+y+ p+v +/<+  и / +и/+и/FиF/01%6#'"&55463275&&54>3266734332##"5###"54332'&&'''&766744&#"3267!"&55463!2'#"&5546332#9=>:8 -`0AP1B''B0'7 6h&&&&j CMT*  o2&&D34DD43D]\   ! > G62$#2.#=+x!<  3& !_C<[( % oM&))&%++  eJimC_sz:+`+t+TY+Lj+A&6FVfv ]A]A`&`6`F`V`f`v``````` ]A``]4`94/TDиjEиE/jQиQ/Az&z6zFzVzfzvzzzzzzz ]Azz]zи/tи/LH/e+++o+?w+}'+ER+'4и/'*и41и1/'VиV/\и\/014>32#".6676#'"&5546375.54>32734332##"5###"5433232>54.#"4&#"3267!"&55463!2'#"&5546332.TtF@sW33Ws@FtT.&66e*9=>:8 -c0!6'1B''B0&&&&%BX3.YE**EY.3XB%sB33CC33B_\  "5%%6"#6&&6+!D  ! E!+0""0     ! !#&&#"((  eItC5AQ_m#J!+1++B+@9+ABB]AB)B9BIBYBiByBBBBBBB ]AJ&J6JFJVJfJvJJJJJJJ ]AJJ]Jdиd/@o32>#"543324&#"326!"&55463!2'!"&55463!2$PRS%QUP AHJ )C0!:O/.P:!0C(JJF))TA 7((7 ATu    &  "j+9!%=++<%"8*h ~36('8 ! # }"!MjC-CO_m{3X$++P+38+8Gи3MAPP]AP)P9PIPYPiPyPPPPPPP ]AX&X6XFXVXfXvXXXXXXX ]AXX]3}J/5/wn+i`+/<+)S++]+ии/и/016676##"&554633275.54>32!2##"554&#!"&5546%#"543324&#"3267!"&55463!2'#"&5546332a0C'EB  Xg )C1!:O/.P:!/)  B))TA 7((7 ATm  ,!B ! D!,/!!/#)$8=#%  '  hMb-9O[iw/V$++P+81+AV&V6VFVVVfVvVVVVVVV ]AVV]V>и>/VEAPP]AP)P9PIPYPiPyPPPPPPP ]8y4/B;+sj+e\+Y+)S+и/и/;I016676##"&554633275.54>32#"54332!"&5543323!24&#"3267!"&55463!2'#"&5546332a/C)K7  Xg *D09P10P9 ))/)  RCCSSCCRm  /$Q  R$/3$%3T#)}$$%+*&$--  lMkY-_kv$+l+jc+Av&v6vFvVvfvvvvvvvvv ]Avv]v5и5/c=и=/vEиE/5FиF/jOиO/vXAll]Al)l9lIlYlilylllllll ]jf//EX/ >Y\1++:S+)q+JA++{+и/и/иgиg/016676##"&554633275.54>32#!"&55463!26554&#!"&55463!2#!"3!2#"54332%4.#"32>7!"&55463!2'#"&5546332a3D%L9  Xg %A1$32663!26557#"54332%4.#"3267!"&55463!2'#"&5546332?,, ,@  Xg )C1!:O/.P:!0C'E  7))(7 7((7 ATm  NQ&! > *-  -*< I I  %  iMl)LXdr_+'+#Y+WP+=W9AYY]AY)Y9YIYYYiYyYYYYYYY ]A_&_6_F_V_f_v_______ ]A__]WS/|s+ne+\+ +b+и/&и'и'/016##"&554633275&&54>3266'&&'''&76674#"543324&#"3267!"&55463!2'#"&5546332  Xg Uc9P10P9 cRE2 CMT*  o2&&))SEETTEESj  ! ?H50#$04H= p  3& !_C<[( % oMx1#++#"--  mMiFAUaqWj8+2+b+`Y+`и/Aj&j6jFjVjfjvjjjjjjj ]Ajj]j и /YBиB/jLAbb]Ab)b9bIbYbibybbbbbbb ]`\/Q++{r+G+=e+0'+o3+3и0и/02и2/01#".54>326676##"&554633275.54>324.#"32>7#"543324&#"3267!"&55463!2'#"&5546332F3Tl9>mQ//Qm>9lT30C'EB  Xg )C1!:O/.P:!&@S-2S<"" +0+8098/A88]A8)898I8Y8i8y8888888 ]A>&>6>F>V>f>v>>>>>>> ]A>>]& >9&//0R5+\U+MD+;+5и/%и5*и+и+//иA01#".54>3232#!"&554633546332!546332'4&#"3267!"&55463!2'#!"5543!2%Eb==bE%%Eb==bE%8  **cXXddXXcL z$;,,<#$<*+324.#"32>!"&5543323!2!"&55463!2"&'35##"554332#   #%&E_99_E&L1C((D11D((C1/)   X !;8I2u""r3 4$$3      ')|{$  " YZYQ&+yr+^U+f+?6+ +l{+иH.0132#!"&5546335&&54>32#!"&55463!26554&#!"&55463!2#!"3!2!"&55463!24&#"326##"554332"&'35#   #%%C`;:`D%A /#-  #-Q X dRReeRRd*!;=)Y!!V++*#*+*#.*   BYAB<|">R`jv~ b(+{+:3+(b9/I и /?{9?/A??]A?)?9?I?Y?i?y??????? ]b0и3iиIz:EX[/[ >Ye#+un+1a+D+ +Nw+  и[S z01%#!"&5546335&&54>3232".5546332!546332#4.#"32>7!"&55463!23!2655##"554332"'35  #%&E_99_E&#  e,, ,,1C((D11D((C1X   ^C3>""^0/""/.`wFE&L    " B B6 FGYun+^+G+  и zи иzиNXܸe]eozw01%#!"&5546335&&54>3232'&&'''&676674324.#"32>7!"&55463!2'##"554332"'35  #%&E_99_E&#  h AQ\/ x)&} . 1C((D11D((C1X C3>""e11#"10ga  2) !`=8_& % *rI     " + LM32"&5!"&55463!2#!#4.#"32>7!"&55463!2'#!"5543!2'Fb:;aF''Fa;:bF'| R w2D**E11E**D2L !5&&5!!5&&5  "" !        "  +A>>]A>)>9>I>Y>i>y>>>>>>> ]>*9/AH&H6HFHVHfHvHHHHHHH ]AHH]m/EXZ/Z >Yjc+"+9C+ +M/+ииZR017!5!"&55463!2#!32##"554&#!"&5546#".54>324.#"32>7!"&55463!2'##"554332{ R x/)  8&E_99_E&&E_99_E&H2E((E22E((E2X t`""`#)$R1"#00""0    " <-CWeqܻ92+*+D+ 299 /ADD]AD)D9DIDYDiDyDDDDDDD ]NEX`/` >Y6/+pi+ +I+S+ (и/=`X01#".54>32"&55!"&55463!2#!#!"&5543323!24.#"32>7!"&55463!2'##"554332&E_99_E&&E_99_E&s R !/)  1C((D11D((C1X 3$$33$$2< "" ')$8    " +/+Fh+Ahh]Ah)h9hIhYhihyhhhhhhh ]hF9/и/6P>9P/n6B+~w+ 9+cZ+Uk+0+%+qK+0и-01#!"&55463!26554&#!"&5546335!"&55463!2#!32#!"3!2#".54>327!"&55463!24&#"326##"5543325 /#-  { R x#-Q A%D`:;`C%%C`;:`D%RX dRReeRRd*z#*$*#O""O%* 2+++*z  D32##".55463326554&##"&5546332##"332677>76%#332##"&5546332##"32%"&55!"&55463!2#!#!"&55463!24&#"326##"554332%D`:;`C%%C`;:`D%6 &M!, !,E# 3  , ,  y R zYD!+xq+<+7Q+ +[-+ииh`01746335!"&55463!2#!32#!".5#".54>32"3!26554&#4.#"32>7!"&55463!2'##"554332 ,{ R x ,>.&E_99_E&&E_99_E&/  2E((E22E((E2X 6*W""Ws*1"#00""0^ U U     " Yqj+@J++T6+#иaY01'&&'''&676675!"&55463!2#!#".54>324.#"32>7!"&55463!2'##"554332O x)%}| R w AQ\/k&E_99_E&&E_99_E&H2E((E22E((E2X `=8_& % 'eAY""_  2) &1"#00""0    " YJ+{t+@+6T++^,+ и"иkc01#".54>75!"&55463!2#!#".54>324.#"32>4.#"32>7!"&55463!2'##"554332*@^m-,m^@5Rb,{ R x-aR56&E_99_E&&E_99_E&6LQQK55KQRK652E((E22E((E2X  *5 5*%3 U""U 31"#00""0    " ItC'O[iw;V +29+P+ + APP]AP)P9PIPYPiPyPPPPPPP ]AV&V6VFVVVfVvVVVVVVV ]AVV]Vnиn/Puиu/y//sj+!+e\+S+K:+Y+:1и1/:?и?/01#".54>324332##"55#"554376##"&55'"&554632>7>'4&#"3267!"&55463!2'!"&55463!2\!:P./O:!!:O/.P:!)) {  BE+ JG? QUO$SQJS@@TT@@Sw   "7''7!"6''7 " %   " */.+*00! # p"!L['=eqIl +bA+f+ + Al&l6lFlVlflvlllllll ]All], l9,/3Aff]Af)f9fIfYfifyfffffff ]/0)++{r+Se+!+i+o+)7SaBиB/XиX/01#".54>324332##"55#"5543!"&5543323!2%"&55'"&554632>7>76#4&#"3267!"&55463!2'#"&5546332W 9P01P99P10P9 )) /)   HF> PTN#VVM  >KRCCSSCCRm  .""..!"-N4D " #)z$   #   p'& %%  jTkY1EY<+)+dk+2+MR+A&6FVfv ]A]и/Rи/и/M!и!/RFA]A)9IYiy ]MI//.++ %++A++}g+FS+7+JиJ/7ZиZ/}lcиc/lqиq/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>324332##"55#"554376##"&55'"&554632>7>'!"&55463!24.#"32>'#"&5546332Y /#- #-S $Y0 ++(+#w+]L+8E++]PUиU/]aиa/fиf/Poиo/017463!2#!".5#".54>32"3!265544332##"55#"5543"&55"'"&5546332667>76#4&#"3267!"&55463!2'#"&5546332 , ,Dg!:P./O:!!:O/.P:!  )) ED? B&QL"QPI  ?H{TA 7((7 ATm  )*k*+  ,+,E M M  f g   l M" #  \TiF';Ox+Za+y+CH+Cи/A&6FVfv ]A] и /H(и(/2HY7++-+#|+p^+<I++^EиE/PиP/pebиb/ehиptиt/01#".54>32#".54>324.#"32>4332##"55#"554376##"&55"'"&5546332667>'4&#"3267!"&55463!2'#"&5546332F1SlE+V++(-+AVV]AV)V9VIVYViVyVVVVVVV ]Vи/!A\&\6\F\V\f\v\\\\\\\ ]A\\](0/*/{r+md+!+Y+SF+a+F=и=/FIиI/SPиP/01#".54>32##"55#"554334332##"543326##"&55'"&554636766'4&#"3267!"&55463!2'#"&55463324H**H55H**H4& &&&  .h7+9o+ :~9?u+J99K$08Kd2   3$%33$$3 " x~$   "'*)( ,! # k"!Djd)=Ijx +SZ+y+*/+>C+A&6FVfv ]A]>иCи//7A&6FVfv ]A]и/yи/>F//+tk+"+|+hW+70++:и:/h[RиR/[^и^/heиe/01#".54>32!2##"554&#!"&5546%##"55#"554334332##"543326##"&55'"&554636766!"&55463!24&#"326#"&5546332/A&'@//@'&A//)  & &&& .d3+9h- 9n:?w)\ t>11??11> )(((~#)$@  Ya  s o   Bp'3Iiw~ +?8+x++(-+A&6FVfv ]A]!кZ8?9Z/SA~&~6~F~V~f~v~~~~~~~ ]A~~]~и/xи/(0/4++sj+gW+{+!++;и4Cg[RиR/[]и]/gdиd/01#".54>32##"55#"554334332##"54332!"&5543323!26##"&55'"&554636766!"&55463!24&#"326#"&5546332/A%&@//@&%A/& &&&/)  /b2+s] 9o9Bv)\ qA12@@21A +  ,+,o=  +P$)so$   x t $  "" %%Dky1EYe<+*+2+FK+Z_+_и/и/Z!A2&262F2V2f2v2222222 ]A22]KSкv*9v/oA&6FVfv ]A]и/и/Zb/.++ %++A++s+7+SL+wnиn/wzиz/и/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>32##"55#"554334332##"543326##"&55'"&554637667!"&55463!24&#"326'#"&5546332y /#- #-4" v/A&'@//@'&A/& &&& *h2+9i- 8p9@r-\ s>22??22> #*'* "* c%%%%   %   j f   | Dim';O[|2++(+CH+PU+A&6FVfv ]A]Uи/A(&(6(F(V(f(v((((((( ]A((]H<кl9l/eA&6FVfv ]A]и/и/PX/++}+#+7+zi+<I+-+?и?/iRиR/zmdиd/mpиp/zwиw/014>32#".732>54.#"#".54>324332##"55#"5543##"543326##"&55'"&554636766!"&55463!24&#"326#"&55463323XvB22??22>  #3"!3$$4""4!)(((  ]i^  { w  ItCGWesP +*1+H++AHH]AH)H9HIHYHiHyHHHHHHH ]AP&P6PFPVPfPvPPPPPPP ]APP]Pjиj/u//of+aX+K+C2+U+2)и)/27и7/01#".54>32#"543326##"&5'"&554632>7>'4&#"3267!"&55463!2'!"&55463!2\!:P./O:!!:O/.P:!))  AE+ KJ@ QUO$SQJTA 7((7 ATu   "7''7!"6''7~%   " ,/ "" 1! # p"!QjC)5\n|Lg +?F+]++-и3A]]]A])]9]I]Y]i]y]]]]]]] ]Ag&g6gFgVgfgvggggggg ]Agg]0//EXw/w >Y}+"+b+XC+l+XG>и>/GLиL/XSиS/wo01#".54>32!2##"554&#!"&5546%#"54332'6##"&55#"&55463>7>'4.#"3267!"&55463!2'#"&5546332a!:P./O:!!:O/.P:!/)  B)) >E+GHA OSL MOMq(7 7((7 ATm  -!!.-  .x#)$8R  d a  $  ]Q[5\hv7c +?F+]++Ac&c6cFcVcfcvccccccc ]Acc]$ c9$/+A]]]A])]9]I]Y]i]y]]]]]]] ]/(!+w+ri+XC+`+f+!/XG>и>/GLиL/01#".54>32#"54332!"&5543323!26##"&55'"&554632>7>'4&#"3267!"&55463!2'#"&5546332a 9P01P99P10P9 )) /)   >E+ JG@ MSN!NPLsRCCSSCCRm  0##00"#/[@#)}x$   }!('" ))  dQkY1Elx<+OV+2+wp+A&6FVfv ]A]и/pи/и/и/w!и!/*A]A)9IYiy ]ws//.++ %+y+A++hS+7+hWNиN/W\и\/hcиc/tиt/01#!"&55463!26554&#!"&55463!2#!"3!2#".54>326##"&55'"&554632>7>#"54332!"&55463!24.#"32>'#"&5546332Y /#- #-S $E+ IH? MSN!NPL)) {)66))66)# "*(* &* Z*)* )  U S    QwD'7Cj|pu+MT+k+B;+Au&u6uFuVufuvuuuuuuu ]Auu]uи/B и /u+;3и3/Akk]Ak)k9kIkYkikykkkkkkk ]B>/EX/ >Y0 ++(+#p+fQ+z+fULиL/UZиZ/faиa/}017463!2#!".5#".54>32"3!26554#"54332'6##"&55"'"&554632>7>'4.#"3267!"&55463!2'#"&5546332 , ,Dq!:P./O:!!:O/.P:!  N)) 8G+ IH? MSN!NPLs(7 7((7 ATm  ,*n*-!!.-  .G P P sH  ^ Z   %  ZQrC/9`lzc&+CJ+{++A&6FVfv ]A]и/1 и8иdиjA{{]A{){9{I{Y{i{y{{{{{{{ ]g/4++0+vm++~+\G+!+\KBиB/KPиP/\WиW/01".5546332!546332##".54>323!26556##"&55'"&554632>7>#"54332!"&55463!24&#"32>'#"&5546332?,, ,!:P./O:!!:O/.P:!  O 32'&&'''&76674#"54332'6##"&55"'"&554632>7>'4&#"3267!"&55463!2'#"&5546332a 9P01P99P10P9  CMT*  o2&&)) ;I+ HE= MSN!NPLsRCCSSCCRm  0##00"#/  3& !_C<[( % oMo   | !)(" ))  aQiF';Gn|y+QX+o+F?+Fи/Ay&y6yFyVyfyvyyyyyyy ]Ayy]y и /?(и(/y2Aoo]Ao)o9oIoYoioyooooooo ]FB/EX/ >Y7++-+#t+jT+~+T<и32#".54>324.#"32>7#"54332'6##"&55'"&554632>7>'4.#"3267!"&55463!2'#"&5546332F1Sl MSN!NPLs(7 7((7 ATm   #5##5##4!!4-!!.-  . g  ` ]  % Y32"&5#"&55463!2####"&5##4.#"32>7!"&55463!2'#!"5543!2'Fb:;aF''Fa;:bF'E R +B2D**E11E**D2L !5&&5!!5&&5 ""  !        "  и>/H`q/EXZ/Z >Yng+"+9C+ +M/+ииZR`иb01735#"&55463!2##32##"554&#!"&5546#".54>324.#"32>7!"&55463!235###"554332v R %/)  8&E_99_E&&E_99_E&H2E((E22E((E2X 'wa""a#)$O1"#00""0    " a'<7Mao{C<+4+*1+N1*9N/ANN]AN)N9NINYNiNyNNNNNNN ] <C9 /Xvиv/@9+zs+ +kb+S+]+ (и-и2и9G01#".54>32"&55!"&55463!2####"&55##!"&5543323!24.#"32>7!"&55463!2'##"554332&E_99_E&&E_99_E&u R +/)  1C((D11D((C1X 2##22"#1G ""  ')$6    " +/u+6+и/P>9P/nи/hu/9h/Ahh]Ah)h9hIhYhihyhhhhhhh ]Fnt6B+{+ 9+cZ+Uk+0+%+qK+0и-и0tиv01#!"&55463!26554&#!"&5546335#"&55463!2##32#!"3!2#".54>327!"&55463!24&#"32635###"5543325 /#-  d R !#-Q A%D`:;`C%%C`;:`D%RX dRReeRRdz#*$*#S""S%* 2+++*z  S<| ';K_mq}ܻ?+o+G+2?92/Vи/(LиL/VnEXh/h >YD!+|u+<+7Q+ +[-+ииh`nиp01746335#"&55463!2##32#!".5#".54>32"3!26554&#4.#"32>7!"&55463!235###"554332 ,% R % ,>.&E_99_E&&E_99_E&/  2E((E22E((E2X '7*^""^t*1"#00""0_ V V     " ^#Y+ +c+m+ (и-и2иzr01#".54>32"&55#"&55463!2####"&55!#7'&&'''&676674324.#"32>7!"&55463!2'##"554332&E_99_E&&E_99_E&6 R + AQ\/ x)&} . 2E((E22E((E2X 1"#00""0 j""j jj ,  2) !`=8_& % *rI '    " YF+y+m<+2P++Z(+иg_q01#".54675#"&55463!2###".54>324.#"32>4.#"32>7!"&55463!225!66##"554332*@^m-,m^@B0 R 0A6&E_99_E&&E_99_E&6LQQK55KQRK652E((E22E((E2X C"!B *5 5**5q""q61"#00""0    " aa<)=IJ/>/A>>]A>)>9>I>Y>i>y>>>>>>> ]*J4и4/DAD&D6DFDVDfDvDDDDDDD ]ADD]*K ++G/+%+9A+01%#!"&55463!2#!"5543!2!"&55463!2#".54>324&#"326  R L a%Eb==bE%%Eb==bE%NcXXddXXcA""  " &?..?&&@--@%3=<43>>YXQ+9F+)+ +3+bZ01%#!"&55463!2'#".54>324.#"32>!2##"554&#!"&5546##"554332!"&55463!2  R &E_99_E&&E_99_E&L1C((D11D((C1/)  X ""2"#11#"1  #)$  " <%9MYg +&:+0 90/A::]A:):9:I:Y:i:y::::::: ]D&i+XQ+cZ+5?+#+I++01!"&5543323!2#!"&55463!2'#".54>324.#"32>##"554332!"&55463!2./)   y  R &E_99_E&&E_99_E&L1C((D11D((C1+X z')$R"" 4$$4 4$$3   `  " <`!1?Ko}`Y++@++` и /A]A)9IYiy ]A@@]A@)@9@I@Y@i@y@@@@@@@ ]`FA]A)9IYiy ]EX/>Y+dU++;2+yp+C++/&+I+A'7GWgw ]A]UQиQ/dhиh/01#".54>32#".54632#!"&55463!2!"&55463!24&#"326###"&5543323326676676%!"&55463!2##"5543324&#"3267#"&554332%D`:;`C%%C`;:`D%UWH$:*VHHWc  R fX dRReeRRd)-%/) !*& ' tr 1--00--1 +++*-=&-==>""5   #) % & ' % $ <| +?S_mл%+,@+6%96/A@@]A@)@9@I@Y@i@y@@@@@@@ ]J,oEXh/h >Y)+^W+ +;E+ +O1+h`01%#!"&55463!2#!"&55463!2#!"3!2#".54>324.#"32>##"554332!"&55463!2  R , ,   8&E_99_E&&E_99_E&L1C((D11D((C1+X ""*{*  _ 2"#11#"1  V  " Y>+zs+KB+_g+,#+ +mU+501#!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!2!"&55463!2#".54>324&#"326##"554332  R w /#-  #-Q X a%D`:;`C%%C`;:`D%MdRReeRRd*(""X#*)*#,*  +++*  D32##".55463326554&##"&5546332##"33267667672#!"&55463!2!"&55463!24&#"32632##"554&##"&5546##"554332%D`:;`C%%C`;:`D%  'N!, !,C&    R fX dRReeRRd/$  k+++* :*"$2**  ""B   #)#J<| #3G[gu+4H+AHH]AH)H9HIHYHiHyHHHHHHH ]H49/ >9>/R wEXp/p >Y +f_++CM+1(+W9+ph017463!2#!".57"3!26554#!"&55463!2'#".54>324.#"32>##"554332!"&55463!2 , ,>]    R &E_99_E&&E_99_E&L1C((D11D((C1+X >*{* _ _ ""2"#11#"1  V  " <|'7S]iwU=++ =U9 /A]A)9IYiy ]UEкH9H/OH\иOyX8+ha+FT+sj++5,+#+01#".54>324.#"32>#!"&55463!2".5546332!546332#%3!2655##"554332!"&55463!2&E_99_E&&E_99_E&L1C((D11D((C1  R e,, ,7  ^X 0""0/""/  ""jJI&F F  " Yvo+_+>8+F+>GOYܸf^fp01#".54>32'&&'''&67667432%#!"&55463!2%4.#"32>7!"&55463!2'##"554332&E_99_E&&E_99_E& AQ\/ x)&} .   R 1C((D11D((C1X 2"#11#"1  2) !`=8_& % *rI Z""    " R+ARR]AR)R9RIRYRiRyRRRRRRR ](R>9(/A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]H29H/\EXz/z >Y7+pi+#-+MW+ +aC+zr01%#!"&55463!2#".54>324.#"32>#".54>324.#"32>##"554332!"&55463!2  R @^m-EE@11@EE-m_?I6LQQK55KQ253)&E_99_E&&E_99_E&L1C((D11D((C1+X ""-9!  !--!  !8.""! 2"#11#"1  V  " Y +le+QH+3Y++E<+_)+01!!3!2#!"&55463!2#!"!2#".54>32#!"&55463!2!"&55463!24&#"326##"554332  , ,   %D`:;`C%%C`;:`D%  R fX dRReeRRd*2 **  1 " +++*""B   DItC /;[g6&+0+f_+A6&666F6V6f6v6666666 ]A66]6и/A00]A0)090I0Y0i0y0000000 ]0 и /fi\/b/ +WK++9!++3+KFиF/WRиR/01!"&55463!2!"&55463!2#".54>324&#"3266"'"&55463667>#"54332  { D 9N..M9 9M..N9 CR@?SS?@R!NST(QUP NTR#SSN))"!! # &=-,>%&>,->%5:963<< &  " ~Ob ';Ge{+B2+(<+ +A<<]A<)<9<I<Y<i<y<<<<<<< ]AB&B6BFBVBfBvBBBBBBB ]ABB]Bjиj/Bq }/ng+ +#+7?+aP+E-+PUиU/a\и\/gu01%#"54332#"&5546332!"&55463!2#".54>324&#"3266'"&554632>7>!"&5543323!25))  8 9P01P99P10P9 DRCCSSCCR EMRTL NSO!NPL/)  CQo  2&&23$%3%+*&$--   #)}$QkY 3Sao*+  + +A ]A ) 9 I Y i y ]A*&*6*F*V*f*v******* ]A**]*wиw/и/*и/wи/ и/* /\/s+]T+|+kb+%++O>+/+>и/]и/>CиC/OJиJ/01%#"54332'#".54>324.#"32>6'"&554632>7>#"&5546332!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!25))$324&#"32>#"&5546332!"&55463!26'"&554632>7>5)) , ,D]  !:P./O:!!:O/.P:!DTA 7((7 7("   !LPP%RSK MSN!NPLD{*z* \ \ 1##11"#1%(  d   QrC ';MisfD2+(<+ +A<<]A<)<9<I<Y<i<y<<<<<<< ]AD&D6DFDVDfDvDDDDDDD ]ADD]DSиS/Dk[и]и eиrи /EX"/" >YnN+ +\j+7?+~+I-+~и/"~и/и/01%#"54332#"&5546332!"&55463!2#".54>324&#"32>".5546332!546332#%3!26556'"&554632>7>5))  8!:P./O:!!:O/.P:!DTA 7((7 7(,, ,B  O "MPO$RSK MSN!NPLa  /!"//!!/#%  PS&K K! QiF ';Mm-D2+(<+ +A<<]A<)<9<I<Y<i<y<<<<<<< ]AD&D6DFDVDfDvDDDDDDD ]ADD] nиn/Dxиx/и/D /s+ +#+}+7?+iX+I-+01%#"54332#"&5546332!"&55463!2#".54>324&#"32>6'"&554632>7>#".54>324.#"32>5))  8!:P./O:!!:O/.P:!DTA 7((7 7( !LPP%RSK MSN!NPL3Tl9>mQ//Qm>9lT3G&@S-2S<""324.#"32>5))  (K!:P./O:!!:O/.P:!D(7 7((7 7(~0"!  " )F54G)*G44H(2%%21%$2fpC ';OeF2+(<+ +AF&F6FFFVFfFvFFFFFFF ]AFF]Fи/A<<]A<)<9<I<Y<i<y<<<<<<< ]<и/ TиZи g/W/ +#+Q^+7A+K-+01%#"54332!"&55463!2!"&55463!2#".54>324.#"32>!2##"554&#!"&55465))  ~ C!9O./N: :N/.O9!C(6 6((6 6(/) $ *"!z  " $;,,<#$<*+<#''''#)$fb !/=Qe\H++>R+ +A\&\6\F\V\f\v\\\\\\\ ]A\\]\&и&/ARR]AR)R9RIRYRiRyRRRRRRR ]R-и-/ g/ ++"+90+aC+MW+ 01%#"54332!"&5543323!2!"&55463!2!"&55463!2#".54>324.#"32>5)) /)    ~ E 9N..N9 9N..N9 C'6 5((5 6'AO#)$5"!  " %@00@%$@00@$,  ,,  ,gqX -AOR8$+zW+.+ +A8&868F8V8f8v8888888 ]A88]8и/A..]A.).9.I.Y.i.y....... ]_иWgиg/ qи //EXu/u>Y~S+ +KB+)3+lc+=+и/u[01#"54332!"&55463!2#".54>324.#"32>7!"&55463!2#!"&55463!26554&#!"&55463!2#!"3!25))  K!:N..O9!!9O..N:!C'7 6((6 7'w   /#-+ #-o "!"9))9""9))8"$ $$ $  " ##*6*&$8*( f|C ';Ocs3F2+gP+(<+ +AF&F6FFFVFfFvFFFFFFF ]AFF]Fи/A<<]A<)<9<I<Y<i<y<<<<<<< ]<и/ Yиoи u/l]+ +#+Sd+7A+K-+-и/01%#"54332!"&55463!2!"&55463!2#".54>324.#"32>463!2#!".57"3!26554&#5))  ~ C!9O./N: :N/.O9!C(6 6((6 6( , ,]]  #"!z  " $;,,<#$<*+<#''''** m m g|C /=Kgq7(+iQ+  + +A ]A ) 9 I Y i y ]A(&(6(F(V(f(v((((((( ]A((](4и4/ ;и;/iYи[и cиpи s/lL+90+Zh+G>+#+-+01#"54332'#".54>324&#"326!"&55463!2!"&55463!2".5546332!546332#%3!26555))!9O./N: :N/.O9!CT@ 6((6 @T  ~ ,, ,V  ":**:"":)*9"/3 $%5e"!o  " "YW&Y Ygv:JVdrA +;+UN+и/A;;]A;);9;I;Y;i;y;;;;;;; ]$;9AA&A6AFAVAfAvAAAAAAA ]AAA]Aiиi/;pиp/UtQ/ne+`W+>+F+01#".54>32'&'''&67>7667663'4&#"32>#"54332!"&55463!2'!"&55463!2U!9O./N: :N/.O9! 0] r.k@cK4/ =T@@U(6 6(#))   i$>,-=$%=,,= @W  ".Bk  % +3=%1982''Y  " ~"!fqJ ';OcwuF2+nZ+(<+ +AF&F6FFFVFfFvFFFFFFF ]AFF]Fи/A<<]A<)<9<I<Y<i<y<<<<<<< ]<и/ PиP/dиd/An&n6nFnVnfnvnnnnnnn ]Ann] y/sU+ +#+_i+7A+K-+01%#"54332!"&55463!2!"&55463!2#".54>324.#"32>#".54>324.#"32>5))  ~ C!9O./N: :N/.O9!C(6 6((6 6(9.Oi3274&#"326'''';.##..##.''''k#//##//+-` w+A&6FVfv ]A] и+ +014632#"&4632#"&""""""""'""""T"""" +//01##"54332,,%8! "+/EX/>Y01##"54332,,g++012#!"&554632#!"&55463Y  6    6   -  -  +  + c@M%&676674'&76762"'  "&$L  >'$Q  ~*RQ*J/ -%UV&L. 6FD//+01'7!'!Z$x$u6//+01!'7!$$uJD@///01'YeSDDp///017PPR1E // /01'%'%AMMMZ1_ // /0177====\ ?^+/+015!5!}q +/+013! `rq Ba ? // и/ܸ + +0135!!5!5!*drҠ4ͮq  ? //  и /ܸ ++01#!5!7!!P*r.4ͮqD? / /+017!.#"D`#]oCD~o\"5V>!!>V5D / /+01!32>7#]oCD~o\"D5V>!!>V5Lg 7//и/////01#3#38888@$y+и/A]A)9IYiy ] и //+012##"&'546634632#"&   BVC! +01&76632'.#"n HʂH 3]]b78a]]  XkkX  &4  4V +016#"&'&632>z HʂH 3]]b78a]]  XkkX  &4  4./01&54632&5546766)" %  s "#+;  ,8- ]+A&6FVfv ]A] +014632#"&""""q""""o- λ+A&6FVfv ]A] иEX/ >Y+ A ]A ( 8 H X h x ]014632#"&4632#"&""""""""k""""T""""< +A]A)9IYiy ]EX/ >Y+ A ]A ( 8 H X h x ]014632#"&2##"77663""""a B* / h""""z   ///901%'3*)*!CgX7//и/////01#3#38888<l ///901#7*)C?,8˻+06+A0&060F0V0f0v0000000 ]A00]609/ A]A)9IYiy ]-3+)+01##"&547667667654&#"5'4766322#"&546!  )   L<C* (G Y6>;0()/6  A2')*5YEX/>Yܸй01463!2##"&554&#!"##"&5P#  =  +H  IP6E // и/!+ и01#!"&55463323!265546332    IH  I8! "+/EX/>Y01##"54332,,@D@6m7/#/7и/#)9.8/2/ /&+к) 9&+014763327667663323325##"&554##"'##"##"&5@"%:;&  &=;$# ct"%uc  /   !2'!1]]1# S< +017"5543!2#,,@6e7/./7и/$)98/2/ /,+и,&к) 901##"##"&'&'&##"'&554633233263325546332"%:;&  &=;$# ct"%uc  /   !2'!1]]1# 8! "+/EX/>Y01##"54332,,v -+и/и///01#53#'377  /N8 2?//и/+и01#53#53++R//Pc +++ии и ииииииии01#37#3##7##7#537#53733733#RVFBJ'U'CGBF'V+F}//+R J'1+D +@A+Dи1AD и "и *и 7иD98/!/7 +7:и D01%4&'&&'26766"##5&&'35&&'&&5467663532#4&#j#' # 6FJ'>/632'7+6'  + /#' 'BKNfaJR7+'>JJB+?J;/v/;?Kw 6+0+$F+@+A & 6 F V f v ]A ]A]A)9IYiy ]A0&060F0V0f0v0000000 ]A00]<6@9>6@9AFF]AF)F9FIFYFiFyFFFFFFF ]0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и !и 'и-EX/>Y*+A'7GWgw ]A]014&'&&#"3267667#"&'&&54676632_++  ++#6$66$663f'++'f36f##++##f6Jq+'++'+qJGq+'..'+q0g,+/EX/>Y+01%##566730'R3#:;k|n.¸// /A ]A ) 9 I Y i y ]"и//и/и/и/ и/-и-/EX/>Y+-01%!667667676654&#"#46766323|;# 2+'  '// '$7N#;#'+>?;#/N>+#?+++/gn@,  +;(+A((]A()(9(I(Y(i(y((((((( ](;9/A]A)9IYiy ]> 9EX/>Y8++A'7GWgw ]A]01%#"&'&&5332676654&'&&##532676654&#"#467663223# ' + # # /'# '/7F+'//BK2':3/* /3;:*KQK6F Jeg ` + и /EX/>Y + к 9 901%##5#533 ;'';<33fgg0q+A]A)9IYiy ]+%&+++01%#"&'&&5332676654&'&&#"#3#663223' ''  + /#ί / ./NF+3?#;#<3 Ji~n?@/./@!и!/ A & 6 F V f v ]A ]. $и$/.*к7! 9EX/>Y'1+:+A'7GWgw ]A]01%4&'&&#"3267667#"&'&&54676632#&&#"6632W ##; ' '/#66#+#2'/  : #/#;RJ:>3J '#RJn+'*: /?+'3V6'>Foxg0+EX/>Y+01#7#5!x' 443gn)S 6+K+A]A)9IYiy ]K9/A]A)9IYiy ]A & 6 F V f v ]A ]?6 9?/*<6*9N6*9EX0/0>YE+$+0A'7GWgw ]A]<$9N$901%4&'&&#"3267664&#"326766#"&'&&5467667&&54676632Y '' ''.#'/ #   ;36 ' 6++ 33#33C/;;/+  +/FF/3 J2 :: 2G3gnAPB/./B6и6/ A & 6 F V f v ]A ]A..]A.).9.I.Y.i.y....... ]..и 3и3/ 9и9/.?EX/>Y<+0+(A('(7(G(W(g(w((((((( ]A((]014&'&&#"326766#"&'33267665#"&'&&54676632Q + #  # + /6:B' #+ J#//#6#::#?;GJq+'+RJ+'#'f:eF3/J'#}h1+и++01%#535#53????hY] ?+и/ܸ и /  +01%5665#535#53; ???we' #]]R //01 ('#N++01%!5!5!5!KK/u/R //01'7'z#cggz/0//0и/A]A)9IYiy ] и-/*+01#53#4676676654&'&&#"#467663277m#  #/ '6/N2G#/3'?/:#+ : +N;8vdeFY+ $++A]A)9IYiy ]A & 6 F V f v ]A ]Y9-93и:AF&F6FFFVFfFvFFFFFFF ]AFF]P9LS+_@+ +*+и 6и6/P 9014&#"3267667#"&'#"&'&&5467663273367674&'&&#"32673#"&'&&54676632* #   +   /# /#: >>'NV7'NJ#+?i#+#C#3/ F3Y# ' +/V##/3: G2V3R#/''e;:e+#/>7JN.'/uFGq/*/#'#a<n 3 /EX/>YEX/>Y+01%#'##3>KJ+33+<lTn ++& +&A ]A ) 9 I Y i y ]и)9EX/>Y ++)901%4&##3267664##326766##32m>7}}' b}}# :3'3'''6>? +Y+014&'&&##3267667##32qC#VV#C#]RR]:Ca $]Flen D + EX/>Y++ 01%!!#3#3l2.usn :+EX/>Y ++01#3##3sӿ+<+l<z34//4 и /$A$&$6$F$V$f$v$$$$$$$ ]A$$]0*++30+01%#5#"&'&&54676632#&&'&&#"3267665#53# N;JJ$:#. :6'3u:F.+/qJGu*+/N36.+#^32b''/# V/.Tn c // и/и / /EX/>YEX/>Y +01%###333''''1l  n"+/EX/>Y01%#3 ++lik1/ /и// +01%#"&7332653iB37>'+#'+faieRJGNTn S+/ /EX/>YEX/>Y9901%##33/J''2@ilGoyn(+/EX/>Y01%!33y'lVn // и/ 9/ /EX/>YEX/>YEX/>Y99 901%###33'em':ef6lTn i // и/и///EX/>YEX/>Y9901%##33/'+' l Hz/Ǹ0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]+*+014&'&&#"3267667#"&'&&54676632x2//2'> #::# >:7e#''''#e7:f'#''''b:Ny'*++*'yNNu'++++'u`n//A]A)9IYiy ]и/и EX/>Y+ +014&##3267###32]2/uu/2+F7'+B?C>RZlBHz45//A]A)9IYiy ]5)и)/ A & 6 F V f v ]A ])9)9 9!)9 /#+/+014&'&&#"3267'7667'#"&'&&54676632x3//#++'23/+#::# >6;e '''' e;:e$#+B#F#iBN|+F'Q2/'*yJNu+'//'+uTn!-Ӹ./"/A""]A")"9"I"Y"i"y""""""" ]и. и / "и" 9и/ &EX/>YEX / >Y%+( + (901%&55'&'&&###324&##326766i '/ *' 6+3  3>l>'?R2R'7F+TzD  ++A]A)9IYiy ]" 9"/8+(5+01%#"&'&&5332676654''&&'&&54676632#&&'&&#":$>'3 #YR' 2'7'+27  Z* 'BQ7/>/V3##>J+7B6' 7Yn0+EX/>Y+01###5!+5<:2TkA//и и / //+01%#"&'&&5332676653>''?'/+'3V####V36JJ6{Fn&//EX/>Y901#3/nl)Nn a +  // /EX/>YEX/>Y99 901##33J3'*3J+:+'3:n"lHn A/ /EX/>YEX/>Y9901%##3733xy3.rq.<0Dn:+9//EX/>Y901#3++nf4Vn 0EX/>Y+и01%!5#5!! % 722"#+//01#3#3"]]22[ +B<n ///EX/>YEX/>Y+ии иик 9к 9ик  901#####533333337#7#73':$3$3$61!+2'5'!5u,p)J-++++#'+//01#53#53#^33^[#+a///901#'#33]b3# P[ +01!5!G+,, //01#'3,'J6,VD "+>+++A & 6 F V f v ]A ]+к2" 92/1 +8.+01%#3267665"#"&5#"&'&&54676676654&#"#4676632327C '6+''N C./C#2#'7'2/   +'7+##7 /+2 3+36'67*Xk.///A]A)9IYiy ]/#и#/" ии/"%к&#9,и,/$/+)+01%4&'&&#"3267667#"&'#36632f . /  / . *?'#B''F'?#BB#$B$$B$3R#'2/Vi/7'$R[+w +A&6FVfv ]A]ܸ+и+/%++01%#"&'&&54676632#&&#"3267667 J?>>/+7+//' NV#V33U #:'3>B''F*Xk.///и/#и#/ A & 6 F V f v ]A ]  и / &и&/,-/+)+01%4&'&&#"326766#5#"&'&&546766323f  //  //  ** B$'>>' F *#BB#$B$$BV/2'#R3.R$'7/[ /3 #+  +)+  +01%4&'&&#"32676673#"&'&&54676632a+''+ ' J> :7'':;;+'>'JV Y3/V##'#^/FnR  + и EX / >Y++ и 01#"3###53546766332F  ??'::<''/+n+3+Yf@ 5+(+(A & 6 F V f v ]A ]!5 9!/"(>EX///>Y%+;+/A'7GWgw ]A]01%4&'&&#"326766#"&'&&5332655#"&'&&5467663253c '+ 76+ +;'3+:#?/ '>:'?+B>#Nb;/J 3#/+RR# J7/Q#/#Fgnk // и/к9/EX/>YEX/>Y+901%#4&'&&#"#36676632/  ' ++'#3/6#l >'n2+и/EX/>Y01%#35#53''''VYrc % + и  //01##5326535#53$+++%27+RZcn Y+/EX/>YEX/>Y99 901%#'#373/B++;Nl~Ӡn"+/EX/>Y01%#3''lX_+++к99///+ и01#&#"#4#"#36632663223/>$$ '+# ,q,qJ'33&"7JRgC// и / к 9/ /+01#4&#"#36676632/+/' ++' #/ ;J;#I# ?+R/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и 'и-и-/+*+01%4&'&&#"3267667#"&'&&54676632j/++/+> #>># >+FF+'FF'3V##V33U ## UXf23//A]A)9IYiy ]и/3и/к9и/'/!+-+01%#"&'&'&'#366324&'&&#"3267667'' ++ >''7' +/  3+ 6R # EU+6#Y;+BF+#B#FXf12/0/и2&и&/ A & 6 F V f v ]A ]0и01&19 #и )/,+ +01%4&'&&#"326766#5#"&'&&5467663253f /++/  ** ''66''? *+FB+#F#B # R6;Y#6+UB'+ // 901&#3667663B+ ''':#d'i~<  +)*+*)9/A]A)9IYiy ] 9 /0+#-+01%#"&'&&5332654&'&&''&&'&&54632#4&#"~32+7'*3  /# >?3./+'  7 p 2>'3:++ #7B2#.3+#    +FDE +  и/+  + и 01##"&'&&5#53533#327F ::'CC '4++g=//и/  //+01#5#"&53326766553/ :'?>+//' / R+7ZR ?:>#]///901#3#/ifDvP 5//// /99 901##33J3'*3F+2/+/2DvzzX '/// /9901#'#7'3732jm/}3a^/} ֫]f////901##"'5326766773'# /in ''++oi~  ++01!5#533~ +f++2&1"+ и&и"//01#"&554�#"332g//:>/?>3++$.  /'+//01#3''[ 2&3 + и и"//01"##53265546746&5&&554&##53232>:///'/  .$++3>?PR} +01!5!GR+Pn!+ + 901&&#"'66323267#"&+3#+7#      g //01#'e+/+01##!9]e~P+/+01!5339/e+/+01##!Jje~P+/+01!533J<d+/+01##!qzd~P+/+01!533q`rd / ++/ ++015#3##!e4ͮq*drd~P /++ /+ +01##!533e4qg*`re] /// /01#3#3 3ut3s3ut3s1111e] /// /01#3#3 u3ts3u3ts3,1111^^ //01#30/32^^ //01#30/,32T[R+A&6FVfv ]A]01%'&&54676H SSSS ?DD  3[[3  2PPT[R +A ]A ) 9 I Y i y ]01&76654&'&6TSS ?DD? SS,[3  2PP2  3i~d[+A&6FVfv ]A]/ /01.54>7t:bG((Gb: 2S54.'7~(Gb: 2S7 U\[U$=,+=$W/I43I/(D22E(]yy])^lwA@wl`(*ju|<<|uj*$\lzCCzl\$Yk%ø&//& и /A&6FVfv ]A]A]A)9IYiy ]///#/01'>54.'7'>54&'763J.(D22D(.J3X[U$=+]HU[,<|uj*$\lzCCzl\$*ju|7UZZU%<++<%9[>!!>[94T< >xoa((gu~A@ug(&aox<54.'7'>54.'7J!>[94T< ![ZU%<++<%UZ,A~ug(%box<>xoa(\Vl+ݺ+&+++ к +9A&6FVfv ]A]+ A&&&6&F&V&f&v&&&&&&& ]A&&]/ /  9 901.54>75.54>7|2Q9 :Q28bH))Ha9KuQ**QuK1O87P2@F_rA@s^F2QlHHlQ"YuLLuZbFZl<75>54.'5v)Hb82Q: 9R19aH)*QuK2P78O1KuQ*,HlQ2F^s@Ar_F2QlHLuYbF[l<54.'&6ckX  &4  4&  Xk,H 3]]a87b]]3 Hpw !ϸ"//A]A)9IYiy ]"и/ A & 6 F V f v ]A ]//9901&&54>7.54>7bPK&:(bkkw2_I--I_23&&3h[z=kb\.FʼnXHkVVkH+`js>>sj`pw#ϸ$//A]A)9IYiy ]$и/A&6FVfv ]A]//9 9014&'667>54.'Rkb(:&&:(bk%-I_23&&32_I-,F.\bk==kb\.FʼnVkH+`js>>sj`+Hkv|r+//013vA@@A`XywXv|r+//01#6654&'3rA@@AXwyX`|q ++A]A)9IYiy ] + +01&&5467#!I5//5H'A==AfZttZ`XywX`|q + +A&6FVfv ]A] ++01#!6654&'!?5//5A==A'f$ZttZXwyXZg+// и /ܸ / /01'7'7%%o2) )Xuu#,JjJxZg+// и /ܸ //01%'%'7'7s)) P#uu#rJxxJxmp-+ܸ++01#3#3p~%xmp1+++01#53#53p%4%am+++01#"&'&&54676633#"33* *J  J##   @  am+ ++01##532676654&'&&##532 *J  J* 0#      #f#$//A]A)9IYiy ]$и/A&6FVfv ]A] //01.54>7'&&5467f9++9,I66I,r~~r,HMMHEO]j<7'6654&'7`6J+8,,8+J6"~r,HMMH,r~,BwfR N]j<и>/NиN/W/F/F9.F9>F9NF901'&&'#"'&&'&&'&&'&5467665.54>&&'&&66326676!         !!)  )!      -w8-I22I-8w-        #.$$.#  -u3x+Ax&x6xFxVxfxvxxxxxxx ]Axx]x2ܺ29x9&x9x+9к>x9FкNx9\29dкl292qк29/V/V9V9&V9>V9NV9\V9lV9V901%'&&'#"&&67667.546624&'&547>54.'&547665"&&54>&&'&&66326676"'&&'66762!) (  )! %    % !)  ( )! %    % <  !(:#!       &&       !#:(!       &&     LOP/4и4/ܸ4A]A@]AqA`]A ]A]A]ܸܸ4ܸи/,и,/<ии>/Fи0NиN/vZиZ/bиvjиj/)}oиvи//T/01%&'&&'#"&7667&&54664&'&547>54.'&547>5&&5466&&'&63266766&'&&'66766           :3**3   3**3   4bG0+и/*и*/A00]A0)090I0Y0i0y0000000 ]6и6/B097677677.'.766&'&'&&6632&&'&''&'&'#"&&67>?#677>         $   ` @       $        <  [Q"En \+C+01%.'.766&'&'&&6632.'.766&'&'&&6632&&'&''&'&'#"&&67>5&&'&''&'&'#"&&67>5#677>#677>&'&>767767&'&>767767          S  $   $   Z            $P    $         ]          ^ C       [^&9L%.T+++T.901%&'&>7677677.'.766&'&'&&6632&&'&''&'&'#"&&67>77.'.766&'&'&&6632.'.766&'&'&&6632&&'&''&'&'#"&&67>5&&'&''&'&'#"&&67>5'#677>#677>#677>&'&>767767&'&>767767         $              S  $   $    Z               $        [    $    $                  e         us1 /,/01'&&'&&76632''&&'&&766321  % ")  % ")s#,  ;+#" #,  ;+#" ./01'&&'&&76632  % ")s#,  ;+#" KZ =+и // +и 01##5#53533 K +01!5!Ran /// /01''7'77{{||{{|{{||{{|%h 78/ /8(и(/A&6FVfv ]A]A ]A ) 9 I Y i y ]  (90(9+-+ ии#и-301&&#"32674&#"3267#"&'#".54>326632 -++-+- -+!$&66&$$&66&$)%""%)"%))%"%**%&**&%h89//A]A)9IYiy ]9и/+A+&+6+F+V+f+v+++++++ ]A++] +4+и4"и(и .01#"&'732654&#"#".54>32&#"32676632$! ++-<&$$!++,:($%  %""%*-%1%& %""%(0#2&KFA+ и /++и 01!5!5##5#53533R FKF A+и / + +и 01##5#535335!5! RΈNi//9901'%%7  Ni//9901%% ? KJ$EX/>Y+01!5!5!5!RRQK[U/ /EX/>YEX/>Y +ܸии и01#'7#537#5373#3%!{$!d ZQa XQ6R+A&6FVfv ]A]01'.54>760 ** ,/:B%%B:/217! 6236R +A ]A ) 9 I Y i y ]01&7>54.'&66*  *%B:/326 !712/:B@<O7+7и/7*и*/179// /"/1 901#"&'&&554&'.54>766554676632@ #    "    !  r  r   u    u @<//,/./ .901#"54>76655476767&'&'&554'&&5432@  "      #    r   u  u   r #+//01#3#3#__77j#+//01#53#53#_77_mQ{G//и/и/и///+01#54&'&&#"#5467632{  #/ #>>#Q!3 3 '=##='mK{G//и/ и / и / //+01#"&'&&553326766553{0 0 /#  '==' 3 3!>}g+A&6FVfv ]A]++01#"&'&&54676633#"33'>>'!33 }0 0 .1 >}!+++01#"&'&&54676633#"!!33'>>'!3J3 }0 0 " % IA  ++01!57'5!!!SA"&> #+ и/и/A&6FVfv ]A]!/ +01#"&54632#"&54&'&&54632     ]  4403305AǸ //A]A)9IYiy ] и/ A & 6 F V f v ]A ]++014.#"32>7#"&54632i ,""- -""- >U^^UU^^U+CxZ55ZxCDxZ44ZxDɸ+//01#3==[ j~///и///01%#3#3====XK M )++ + //01#3#3#3<<;;<<"$e ;$,k-//-&и&/$и$и+&+9,%+()++,и)01#326'&&'7##&&'&'!!#39F "(  '`73&4q66h ! /   X5#(588M[  + ++ии и и//+01%5##'#5'#35353^V2`8`<=/G-#;!:FB(u+A]A)9IYiy ]/ /+ и 01###7373276654&'&&'&&'7Z*?+ p?>AK * 1T !8 R.#$G 4 G7+//+01&&'&&'7#5267671 9(&%! !-2"'a=>?e)@*^+M'"/=!(F"1Q!&2 %)?w49://A]A)9IYiy ]))9:и/и0!//EX/>Y/+!901&&'&&'#5&H5'733276654&'&&'7##"1"=2 /. 'T % 0G L&]*,<o/%& 8 *#G ~10-+. ?+и // +и 01###5333==m8h>y   +/ / / /01.54>7#3N=dH''Hd=[!!MkVVkMb>y+/ // /01#3_!!J'Hd==dH'bOVkMbMk./01&54632&5546766)" %  s "#+;  ,xxo+//01'7ob&A&bxxo+//01%'7'7o &bWb&h.>N^G/J/ J9J9J97J9BJ9OJ901%#"&&67667767>&&'.766&&'&&''&&'&&6632&'&>766  z! }"  #{   " "$     X     Z     %# Z      h.=M]m}// 99(979>9N9f9v9999901#"&&67667#"&&6766767>67>&&'.766&&'.766&&'&&'&&'&&'&&'&&6632&&'&&6632&'&>766&'&>766    z! ! }" "  # #{     " " "$ "$                    Z     "     \%# %# x      4      h.>N^G/J/ J9J9J97J9BJ9OJ901#"&&67667767>&&'.766&&'&&''&&'&&6632&'&>766  z! }"  #{   " "$     X     Z     %# Z      Hm/?O_G/[/[9[9 [98[9@[9S[901667>#"&&67667'&&'.766&&'&&''&'&>7667&&'&&6632 #(! +"  !*#! #(.      #$)+D   o   P   c**$# H/?O_o5//5959-59=59@59P59h59p595959595901%667>667>#"&&67667#"&&67667&&'.766&&'.766&&'&&''&'&>766&'&>766&&'&&''&&'&&6632&&'&&6632 #(! #(!  +"  +"  !*#! #(! #(!*#            #$)+ #$)+   %   [   P   >   V   **$#**$#M //01#73S`_ø // и/A]A)9IYiy ]ܸA&6FVfv ]A]+ +01".54>3274&#"326'''';.##..##.''''k#//##//Z] #$//A]A)9IYiy ]и/$и/и/и A & 6 F V f v ]A ] ////01&&5467.54>7'22'#//#:""  8ii84bb4>HV54WH>:DQ11QD:Z]#$//$ и /A&6FVfv ]A]A]A)9IYiy ]и/ и  и!и!////!/01'>54.'7'6654&'7#  #R2'#//#'2+5VH>:DQ11QD:>HW4i84bb48xo] #$//A]A)9IYiy ]и/$и/и/и A & 6 F V f v ]A ] ////01&&5467.54>7'22'#//#m4((4.$$.8ii84bb4:K]87^K:7EU11TF7xo]#$//$ и /A&6FVfv ]A]A]A)9IYiy ]и/ и  и!и!////!/01'>54.'7'6654&'7(4/$$/4(R2'#//#'2+8]K:7FT11UE7:K^7i84bb48LjR+A&6FVfv ]A]01%'&&54676D LRRL 555 9bb9 ESSLjR +A ]A ) 9 I Y i y ]01&76654&'&6LRL 5555 LR,b9 ESSE 9usZ//A]A)9IYiy ] и /EX / >Y + 01%&&5467#3+-33- >JJ>0SS0 \6ee6usZ// и /A&6FVfv ]A] EX / >Y+ 01%##6654&'3S-33->JJ>0SS0 6ee6usZ"+/EX/>Y013u>JJ>\6ee6usZ"+/EX/>Y01#6654&'3s>JJ>6ee6AlR+A&6FVfv ]A]01'.54>76  :xc??cx:  /gW99Wg BlfflB Fj]]jGAlR +A ]A ) 9 I Y i y ]01&7>54.'&6?cx:  .hW99Wh.  :xc?,flB Gj]]jF BlD|?HI-+HD+?+HAܸ?J$+7+DC+014&'&&#!"3!2676657#!"&'.5467>3!2##52673 //  *N* 77  "# 0 #"  Y.> |Q,   ,0 0 #!  !# @77X*'C}-=M6E+" +M.+"и/A ]A ) 9 I Y i y ],EM9MO:A+J1+-++01%!5>76654.#"#&676632!4&#!"3!265#!"&5463!2HTXN< '4)J5 g**,h0@hJ' 3BLIBs0<<00<D<0#DHHDDHJHDm *5:6-   /'>L-; #-240' 36633663?II?N?II?C}<L\ͻET+5+\=+5A]A)9IYiy ]8T\9\^IP+Y@++0$++0'ܺ8901%#"&'&&7332>54.#52654.#"#&>766324&#!"3!265#!"&5463!2'KoI3g+.& j 8H!&9%"!2$ *:!6? $836633663?II?N?II?C} -g%+ +-+ ии-/!+*+ + и01%5##5!5334&#!"3!265#!"&5463!2*tplY0<<00<D<0#DHHDDHJHD[[zo36633663?II?N?II?C}+;K4C++K,+A]A)9IYiy ]"CK9KM8?+H/+ + +'+"'901%#"&'&&7332>54.#"'!!>324&#!"3!265#!"&5463!2Ud&' i 3G((C1!9L*0Y@c -<*-\I/f0<<00<D<0#DHHDDHJHDhfQ8&.("-  5qv 1M[36633663?II?N?II?C} 4DT=L+-++T5+A]A)9IYiy ]A-&-6-F-V-f-v------- ]A--]TVAH+Q8+ +(+2+-2901%4&#"3267#".54>32.#">324&#!"3!265#!"&5463!2CQ?Q$7'UQs%HgBFkI% JwWCZ7j .&F7# "2C,xxw0<<00<D<0#DHHDDHJHD14'0%6.+J7 /Oi;By]7'9C&.M; j936633663?II?N?II?C}.[////&и&/. &.9&.0"+++ +01#>7!5!4&#!"3!265#!"&5463!2'J>, ;FK!c0<<00<D<0#DHHDDHJHD/n}KP}e&r36633663?II?N?II?C} ;K[=DS+&+6 +[<+6A&6FVfv ]A]A ]A ) 9 I Y i y ], )&99 69[]HO+X?+ #+1++)99901%4&#"3264.#"32>#"&5467&&54>324&#!"3!265#!"&5463!2XNNY[LK[%34&&43%1/" >Y88Y>!"/1~0<<00<D<0#DHHDDHJHD6,,6,223  XggX,L:%#<--<#%:Lk36633663?II?N?II?C}2BR;J+)+#+R3+A&6FVfv ]A]A##]A#)#9#I#Y#i#y####### ]RT?F+O6+ +.+ &+#& 901.#"32>#".'73265#"&54>3274&#!"3!265#!"&5463!2)7!QP(6 5,#Ey`CW6 i F##52673#".54>3274&#!"3!265#!"&5463!2%$$%rB#5 S/K45H..H54K/J0<<00<D<0#DHHDDHJHD4-N;!!;N--N;!!;N9 !DsV00VsDCqP--Pq36633663?II?N?II?C}!1)++  +1+13EX/>YEX / >YEX"/">YEX)/)>Y%+.++ и 01!##52673##526734&#!"3!265#!"&5463!2rB#5 S@rB#5 S0<<00<D<0#DHHDDHJHD9 !9 !36633663?II?N?II?C}/?O=8G++'+O0+' и /A]A)9IYiy ]GO9.GO9OQEX/>YEX / >YEX8/8>YEX?/?>YEX@/@>YEXG/G>Y<C+L3+$++$ܸ./01%##52673!5>76654&#"#&4667663234&#!"3!265#!"&5463!2`hB#5 IPL;+&!\  E!WV 6CG]0<<00<D<0#DHHDDHJHD9 !eOUO  &(!*/( XLSSI36633663?II?N?II?C}>N^GV++7!+^?+7 7$:!79^`KR+[B++2&+! +&й2)ܺ: !901%##52673#"&'&&7332>54춮#"#&>766324&#!"3!265#!"&5463!2diB#5 J2G,!?" X 3/% F887K1& V  B";./(&U0<<00<D<0#DHHDDHJHD9 !X(B0R84@$9;I*8F60+# $:*1B&7}36633663?II?N?II?C} &6{.+++6+ ии68#*+3++ +и 01%5##52673##5#5334&#!"3!265#!"&5463!2{liB#5 JYcMYK0<<00<D<0#DHHDDHJHD乹9 !7Vyl36633663?II?N?II?C}2BR;J++ +R3+A]A)9IYiy ]+JR9RTEX/>YEXC/C>YEXJ/J>Y?F+O6++()+."+)(и/+".901!##52673#"&'&&7332>54&#"'!#66324&#!"3!265#!"&5463!2_iB#5 J7L.*=X %%;*&A' 7 '%C3O0<<00<D<0#DHHDDHJHD9 !g/I4HM+  ++&+U6+&и/A&6FVfv ]A].и./UWBI+R9+ +")+1+)и/"&ܺ.1901%4&#"326##52673#".54>32#4&#"66324&#!"3!265#!"&5463!2(%,%1#(iB#5 JfQ;J*6L-RO]""% 0"?0Q0<<00<D<0#DHHDDHJHD->cY;9 !hcj3Up=X{N$gR/5 1; .OZ36633663?II?N?II?C}%5q-++  +5+-5957")+2+++и/01%##52673#>7#5!74&#!"3!265#!"&5463!2fiB#5 JAF o+6UV0<<00<D<0#DHHDDHJHD9 !`dF{p1b36633663?II?N?II?C}  @P`IX++++; +`A+A ]A ) 9 I Y i y ] ;9/A]A)9IYiy ]A++]A+)+9+I+Y+i+y+++++++ ]1+91/A11]A1)191I1Y1i1y1111111 ]!.+9> ;9`bMT+]D+ &+6+++.9>901%4&#"3264&#"326##52673#".5467&&54>324&#!"3!265#!"&5463!21""10#"1 ****iB#5 J2C%%B25'!&-:!!:-&!'5N0<<00<D<0#DHHDDHJHD#11##005****_9 ![(B//B(4M>0#9))9#0>Mq36633663?II?N?II?C}?HY +@A+Q8+ Y++8(и(/Y1AQ&Q6QFQVQfQvQQQQQQQ ]AQQ][EX/>YEX/>YEX@/@>Y+ +(%+;N+T3+%,13T9NDиD/C01%#!"&5463!24&#!"3!265#"&'732>7#".54632##526734.#"326765DHHDDHJHD#0<<00<D<0W-M+H*+*и/H6и6/014.#"32>%3!5>7654&#"&676632#".54>3274&#!"3!265#!"&5463!2 2>>CB6 ""\>XKr'A11?%%?11A'00<<00<D<0#DHHDDHJHD7-N;!!;N--N;!!;NNPIdcOTM!$.(3MWDsV00VsDCqP--Pq36633663?II?N?II?<7 ++//+01####".54>33(!W!#?01J3.D+'A/O%++////01#3#366n44O%++////01#3#366n44$ K+иии + ++01#535#535#53$aaaaaa2bbb-X {//и/и и и + +ии и 01%#53#53%#53#53aa-aaaa-aaXbbbbbbD|&A95++A+AC#,+9+01%#"&5&'&67766%4&#!"3!265#!"&'.543!2  q   ;1m6428%3=4# %-,'qp  63i1;84_#4$$/-$#!48D|4C^<R+)+^5+)и/A]A)9IYiy ]1R^9^`@I+V8+$+2+01%#!"&'4>7667>54&#"&&7>32!24&#!"3!265#!"&'.543!2s 'V<7+JKSW9T:1S<"#=Q.DSnw;1m6428%3=4# %-,'n "$!8 -!-8C5  92"%8&4A,E-63i1;84_#4$$/-$#!48D|BQlͺJ`+=*+lC+=A**]A*)*9*I*Y*i*y******* ]@`l9lnNW+dF+:/++$+и/@$901%#".'&6632>54.'&&&46676654.#"&&766324&#!"3!265#!"&'.543!2*EW-9S7 Q7 A5!!+0MU9&."9+5Qo_aq7)7=k;1m6428%3=4# %-,'(>*)3 8' -"#- -  2DEG3= K563i1;84_#4$$/-$#!48D|!0Ku)?++K"+ииKM-6+C%+ ++и01%###"&55!"&'&6766324&#!"3!265#!"&'.543!2) & -) &p;1m6428%3=4# %-,'=PP.G u63i1;84_#4$$/-$#!48D|<KfDZ+5%++f=+A]A)9IYiy ]fhHQ+^@+*0++8+58901#".'&6632>54&#"'&&77663!2#!"66324&#!"3!265#!"&'.543!2%?U/@V7 &.49/HNDM!IYE+N:#m;1m6428%3=4# %-,'2K3!./ $ 1#@F!  " .G63i1;84_#4$$/-$#!48D|?NiG]+++i@+A]A)9IYiy ]A&6FVfv ]A]6и6/ikKT+aC+.+ +;+014&#"32>7#"&54>32&'.#"6766324&#!"3!265#!"&'.543!2mTH8G)/7"@1C'AV/x{!@^>*B3% '0 ,?+G31R'+4++01%'&67!"&5463!274&#!"3!265#!"&'.543!2i  4!q;1m6428%3=4# %-,'T   $!463i1;84_#4$$/-$#!48D| ?Ni9G]+&+8 +i@+8A&6FVfv ]A]A ]A ) 9 I Y i y ].+]i9;]i9ikKT+aC+3+ !+++9;901%4&#"3264.#"326#".54>7&&54>324&#!"3!265#!"&'.543!2|We`[XccY!$;+,;$UEKNe%D]98]C&$-,05T=''>/"3% ?+3))3+? %3M63i1;84_#4$$/-$#!48D|APkI_+ 8++kB+A]A)9IYiy ]A & 6 F V f v ]A ].и./kmMV+cE+=+&+3+014.#"32>#"&'&>32>7665#".54>3274&#!"3!265#!"&'.543!2o0C(9++9 7F)K<_CQf!&0!/A*G3/R<#%?S-@_>g;1m6428%3=4# %-,'%3 #2! , &)6FrP,3   *6-E0+F2?d63i1;84_#4$$/-$#!48D|)=Qn"+R\+H4+*>++A>>]A>)>9>I>Y>i>y>>>>>>> ]AH&H6HFHVHfHvHHHHHHH ]AHH]p&++9C+M/+/WиW/01%#!"&'.543!2'4&#!"3!265'#".54>324.#"32>#".5'&&667766%3=4# %-,';1m64287,D11D++D11D,@ , , , ,  3  V  #4$$/-$#!48N63i1;84;gK+-Mf88fM--Mf8/Q<##32#".5'&&667766%3=4# %-,';1m64287':$(8'/uj[:6*.H3%?-> 3  V  #4$$/-$#!48N63i1;84#8/(&%' "F{90O3-!+  95%%9c K z   D|)o׺"+pz+fQ++fA*AQQ]AQ)Q9QIQYQiQyQQQQQQQ ]k9&++aT+>/+LG+GDиD/kGL901%#!"&'.543!2'4&#!"3!265'#".'&6632654&'&&&&667654&#"&&7>32#".5'&&667766%3=4# %-,';1m64284 5F&'?/!'9H0%98"E7026  *<(#>. !H 3  V  #4$$/-$#!48N63i1;84)?+&2$ 4:/9 M!00  ,%"3!( #0 K z   D|)Heh"+IS+/5++5*/Dи5fи6gкh9j&++hN+F-+-6иhE01%#!"&'.543!2'4&#!"3!265'###"&55#"&'&676632#".5'&&667766%3=4# %-,';1m6428/ !   A 3  V K #4$$/-$#!48N63i1;84d QQ.F i K z   )D|)dѺ"+eo+9O+*A++AAA]AA)A9AIAYAiAyAAAAAAA ]9^и^/_O99&++TZ+</+bD+_Db901%#!"&'.543!2'4&#!"3!265'#".'&6632>54&#"'&&7766332##"6632#".5'&&667766%3=4# %-,';1m642853B$-A+ 53-#97&  #75NdB 3  V  #4$$/-$#!48N63i1;84.J4'0 -.%432&'.#"66324&#"32>#".5'&&667766%3=4# %-,';1m642812A"-H15H+"4%  "-?;"=.@<:.@..!~ 3  V  #4$$/-$#!48N63i1;84)G3CpS>hK)  *2 &-F19D#,?D!0j K z   D|)>[M"+?I++39]&++;3+01%#!"&'.543!2'4&#!"3!265'&&667!"&'&63!2#".5'&&667766%3=4# %-,';1m6428 /O 3  V  #4$$/-$#!48N63i1;84   ##O K z   D|)IUr?"+V`+P4+Ds++DJ*79AP&P6PFPVPfPvPPPPPPP ]APP]P:G9Ass]As)s9sIsYsisysssssss ]}&++?x+S/+M+7M9GM901%#!"&'.543!2'4&#!"3!265'#".5467&&54>324&#"326#".5'&&6677664.#"326%3=4# %-,';1m6428//J44J/7-".+B..B+."-7@H;324&#"32>#".5'&&667766%3=4# %-,';1m64280/I3BM 8#=: D=:/0B'a_A@>)!:2%4 3  V  #4$$/-$#!48N63i1;84HtP+1%  *"M=!%.C,*G5IBJ 1!?A"* K z   D|FZigbx+9$+ Q+G+[+A]A)9IYiy ]A & 6 F V f v ]A ]A9&969F9V9f9v9999999 ]A99]9и/Cx9fo+|^+V+DL+D'иV6и6/014.#"32>##"7>7>54&#"&&7>3232%#".54>3274&#!"3!265#!"&'.543!2     . &)$(# !6*?H-$  C#8)-9 "8))8#+;1m6428%3=4# %-,']1RiM++Mi>>iM++Mi63i1;84_#4$$/-$#!489r++01##3#".54>326X 57YFx\\xFFx\\xF*'X$H\xFFx\\xFFx9r(<) +! 9.+8+014.#"34>32!5#>766#".54>32*8 )9% f 07;<3.*Fx\\xFFx\\xF,=%(1/( $$  -[YR#cj!PTT&*\xFFx\\xFFx9r2FI&+ и /,и,/08+B+!)+ +01%4&'&&'6676654&#"34632#2#"&5#326%#".54>32 JD&:&c "% "gCRLO#Fx\\xFFx\\xF$?  -$YT4H,4(%*%]"00.+YfT\xFFx\\xFFx9r !+ ++01%#75##335%#".54>32YY=ofeFx\\xFFx\\xFat`\xFFx\\xFFx9r&:M+ ии/",+6 + +++01%4&#"737!36632#"&5#32>%#".54>32:>#" b  k$;-4= Fx\\xFFx\\xF`j sj 3*$1 7+)G4(E[\xFFx\\xFFx9r -AO+ии/)3+="+ ++01%#"&'566324.#"54632354&#"32>%#".54>320f - *eFRJOCO'>+Fx\\xFFx\\xF=0 ,?6J/r* $$)KXYUHO1P\xFFx\\xFFx9r ++015!33#".54>32ӵrFx\\xFFx\\xF XlH\xFFx\\xFFx9r ;Ou +A & 6 F V f v ]A ]6A+K$+ ++01%#"&54632'#"&546324&'66554.#"32>5%#".54>32%q$*55+$(9$$9(!Fx\\xFFx\\xF-22-,44*%%*.""&74b--b47&`0$$0\xFFx\\xFFx9r *>O+и!и!/'0+:++ +"01#"&546324&#"3267#"55#3265%#".54>32!mNJKU"- '/eJNJO!Fx\\xFFx\\xFe ,9=. NLbp:K,r*#I %KXYU\xFFx\\xFFx9r'0EO. +(+1:++ /(++$ܸ-01#".54>32'4.#"3265##3%#"&&54>32Fx\\xFFx\\xF!5($5#!6(BJF.3Ln -\xFFx\\xFFxB8**79+MOb "Q1+ 9r%Y++ +ܸ'+!+!ܸи и 01##3!##3#".54>32F.3LF.3LIFx\\xFFx\\xF b "Q1b "Q19\xFFx\\xFFx9r,@7++-+7A]A)9IYiy ] к 7-9+и+/-B,2+<+,и<01##3!5#>76654.#"34632#".54>32F.3Lǒ&$! "0*5 _ &02Fx\\xFFx\\xF\ "Q7bBMT, ).:" "8I':/%$WXO]1\xFFx\\xFFx9r:NۺE+1+*+;+E;9A]A)9IYiy ]1и/'ܸ0и0/;P6@+J+'-+&+Jܸ&'001##3%4&'&&'676654&#"34632#2#"&5#32>7#".54>32F.3L C=H>a 2%c 2%5&Fx\\xFFx\\xF` "Q3> '/ XI\a-.(&"Y%45&"2-C-'B\xFFx\\xFFx9r * !+++ܸк9!9! ܸ ܸи,+&+и/&ܸи/01%#7##3%5##3357#".54>32{SSF.3L0eVFx\\xFFx\\xF<[ "Q8Zv^\xFFx\\xFFx9r/C:+++0+:ܸ0 ܸ'и(и(/0E5+?+$+'+ +и/?ܸ5+01##3%4&#"737#36632#"&5#32>7#".54>32F.3L,= ]    c;N*5 Fx\\xFFx\\xF \ "Q7imkd/(&-*+[_ 9N\xFFx\\xFFx9r:NE+3+ +;*+Eܸ*и/ "и)и;P6@+J/+ +&+JܸJܸ)01%#"&554632##3%4.#"5463234.#"32>7#".54>32    F.3L#1  ^&0E<32F.3Lj=Fx\\xFFx\\xF \ "Q7 Ng7\xFFx\\xFFx9r CWN+9+ +D&+A]A)9IYiy ]Nܺ#ND95ND9DY>I+S,++Sܸи/01%#"54632'#"&54632##3%4&'66554.#"32>57#".54>32, F.3L 1%$0 &11&Fx\\xFFx\\xF12c/11)&&))++R "QA'38)4-##-4)83'c#. .#\xFFx\\xFFx9r8LC+ ++9+Cܸ(и/и// 0и0/9N5>+H+%,+ +HܸHܸ%/01#".54>32##34&#"3267#"&5#32657#".54>32"    F.3L32'4.#"34632!5#66766%4.#"32657#".54>32  /,6 [+W;/ 3%$3  4&F?Fx\\xFFx\\xF   "  !'4 "7H'3<x/M\6D%H 7()74%GP\xFFx\\xFFx<[.G//&//и/A&&]A&)&9&I&Y&i&y&&&&&&& ]&999 9A&6FVfv ]A]999!9+90 /+// /+ 9 901% &&54673%#'!#3#>54.'3uyy8==8+''4@II@B=8+' '+8=IeUU4##32>&&54673#!!27#>54.'3UN";,$4# 8==8+''xg)I684.5LVNU:9ACC]AC)C9CICYCiCyCCCCCCC ]H:9:M4>+H*+>иHи4*!01&&54673#".54>32#&&#"32>77#>54.'38==8+''#(CZ7GkI%%IlF-UD-;iE=W77V>+E3=8+' '+8=UU&&54673##32#>54.'3=`CC`=8==8+''*Ջ=8+' '+8=,GfAAfvUU54.'38==8+''VRn=8+' '+8=UU54.'38==8+'')<=8+' '+8=UUP +1+>;+?H+A1&161F1V1f1v1111111 ]A11]19A & 6 F V f v ]A ]19;и/C?9AHH]AH)H9HIHYHiHyHHHHHHH ]M?9?R6+!,+>;+,ܸ6!'ܸBиM01&&54673%#'#".54>32#.#"32>5#5!#>54.'38==8+'',lUDkK''KkD1WD. <3G-=W88W>4P6=8+' '+8=UUG1X|JJ{Y16P59.1Of45eP1#?W34U54.'38==8+''<;;r<=8+' '+8=UU54.'38==8+''4;;=8+' '+8=UU54.'38==8+''ZX^V<??6;;=8+' '+8=UU54.'38==8+''Hp<<WL=8+' '+8=UU54.'38==8+''o;V=8+' '+8=UU54.'38==8+''7;:;WW=8+' '+8=UU54.'38==8+''C;Cl;=8+' '+8=UU&9ACC]AC)C9CICYCiCyCCCCCCC ]H&9:M>+H+>иHи+5014.#"32>&&54673#".54>32#>54.'3"iM,,Mi>hK)*KgnUU54.'3L>>L8==8+''eU;Ue=8+' '+8=BAAUU32#>54.'3"iM,,Mi>hK)#C%H&noUUP+)*+9"+?H+A""]A")"9"I"Y"i"y""""""" ]"и/)к?9A&6FVfv ]A]?9"14?9>?9C?9AHH]AH)H9HIHYHiHyHHHHHHH ]M?9?R/M/ /B/,+'+4'9> 9014&##32>&&54673%.'.###!2#>54.'3DC1% 8==8+''1*;'E4?:*  =8+' '+8=;E .UU54''.54>32#.#"7#>54.'38==8+'' !>W65\B%;!7J(!;-3&6O5/R>$<-<"JQ7(OR=8+' '+8=UU54.'38==8+'';=8+' '+8=UU53#>54.'38==8+'' :\AA\:;/D++D.<=8+' '+8=UU54.'38==8+''G?=8+' '+8=UU54.'38==8+''dH{{H>ouPwn=8+' '+8=UU54.'38==8+''GGFF=8+' '+8=UU54.'38==8+'' ;C=8+' '+8=UU54.'38==8+''f=8+' '+8=UU7#".54>32bnnj:CC:<y@nTTn@@nTTn@$Fx\\xFFx\\xF ,涶JTn@@nTTn@@nT\xFFx\\xFFx9r &:Ng1E++! +;'+!A ]A ) 9 I Y i y ]и$E;9A'']A')'9'I'Y'i'y''''''' ]A1&161F1V1f1v1111111 ]A11];P6@+J,++++$901%4&##32>4&##32>##3274.#"32>7#".54>32NG6(A?!0Km^&B23/6A@nTTn@@nTTn@$Fx\\xFFx\\xF;; ,-63%UPJ!9*0ENBTn@@nTTn@@nT\xFFx\\xFFx9r)=Q#4H+ +>*+A&6FVfv ]A]A**]A*)*9*I*Y*i*y******* ]A4&464F4V4f4v4444444 ]A44]>S9C+M/+$++01%#".54>32#.#"32>774.#"32>7#".54>32&*6!.88O32P8'?.@nTTn@@nTTn@$Fx\\xFFx\\xF5U7#".54>32lzvvzl6~~@nTTn@@nTTn@$Fx\\xFFx\\xF(ttJTn@@nTTn@@nT\xFFx\\xFFx9r 3*+ +  + A ]A ) 9 I Y i y ]A&6FVfv ]A] 5%+/+ +++01%!!!!!!74.#"32>7#".54>32|~5N@nTTn@@nTTn@$Fx\\xFFx\\xFI/0Tn@@nTTn@@nT\xFFx\\xFFx9r 1(++ +A ]A ) 9 I Y i y ]A&6FVfv ]A]3#+-+ ++01!!!#!4.#"32>7#".54>326b@nTTn@@nTTn@$Fx\\xFFx\\xF/ITn@@nTTn@@nT\xFFx\\xFFx9r,@T7K+ +,)+A-+)и/A&6FVfv ]A]A--]A-)-9-I-Y-i-y------- ]A7&767F7V7f7v7777777 ]A77]AVEX/>Y<F+P2++,)+$A$'$7$G$W$g$w$$$$$$$ ]A$$]01%#'#".54>32#.#"32>5#534.#"32>7#".54>32)cM>bD$$Db>-O>)6/A(8O33O9/H2@nTTn@@nTTn@$Fx\\xFFx\\xF m8A,QpDCqP-1H04*,I\00]H- :N/0 Tn@@nTTn@@nT\xFFx\\xFFx9r 3*+++  +и A ]A ) 9 I Y i y ]A&6FVfv ]A] 5%+/+ +01%#!#3!534.#"32>7#".54>32666Q6@nTTn@@nTTn@$Fx\\xFFx\\xF JTn@@nTTn@@nT\xFFx\\xFFx9r+q,/"и"/A0]A]A0]A]"-+' +01%#34.#"32>7#".54>3266|@nTTn@@nTTn@$Fx\\xFFx\\xFJTn@@nTTn@@nT\xFFx\\xFFx9r#74.++ +$+A]A)9IYiy ]A&6FVfv ]A]$9EX/>Y)+3+ A ' 7 G W g w ]A ]01%#"&73326534.#"32>7#".54>32ySOUO699166@nTTn@@nTTn@$Fx\\xFFx\\xFb\b`KGCHTn@@nTTn@@nT\xFFx\\xFFx9r 3*++  +* 9к* 9 * 9A ]A ) 9 I Y i y ]A&6FVfv ]A] 5%+/+01%##334.#"32>7#".54>32Bg669F@nTTn@@nTTn@$Fx\\xFFx\\xF0hJ82Tn@@nTTn@@nT\xFFx\\xFFx9r-ӻ$+++A]A)9IYiy ]A&6FVfv ]A]/+) ++01%!3!74.#"32>7#".54>3267@nTTn@@nTTn@$Fx\\xFFx\\xFJTn@@nTTn@@nT\xFFx\\xFFx9r 4ݻ++++! + +!9A ]A ) 9 I Y i y ]A&6FVfv ]A]!6&+0+01%###334.#"32>7#".54>32 767QQ@nTTn@@nTTn@$Fx\\xFFx\\xF ? Tn@@nTTn@@nT\xFFx\\xFFx9r 1ӻ(+++ +A ]A ) 9 I Y i y ]A&6FVfv ]A]3#+-+01%##334.#"32>7#".54>32>6=L6@nTTn@@nTTn@$Fx\\xFFx\\xFJTn@@nTTn@@nT\xFFx\\xFFx9r';Oʻ2F+ ++<(+A]A)9IYiy ]A & 6 F V f v ]A ]A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<QEX/>Y7A+K-+#+A'7GWgw ]A]014.#"32>7#".54>324.#"32>7#".54>327M-/L76M/-M76)G_78`F((F`87_G)@nTTn@@nTTn@$Fx\\xFFx\\xF09_C&&C_99_C&&D^9JrM((MrJJrM((MrMTn@@nTTn@@nT\xFFx\\xFFx9r';52++ +(+A]A)9IYiy ]A]A)9IYiy ]A&6FVfv ]A](=#-+7++ +014&##3267###324.#"32>7#".54>32F88F6[N6M\@nTTn@@nTTn@$Fx\\xFFx\\xF<;;=OYIXTn@@nTTn@@nT\xFFx\\xFFx9r.BVۻ9M+ %++C/+A]A)9IYiy ]A & 6 F V f v ]A ]A//]A/)/9/I/Y/i/y/////// ]A9&969F9V9f9v9999999 ]A99]CXEX/>YEX / >Y>H+R4+*+ A'7GWgw ]A]014.#"3267'7667'#".54>324.#"32>7#".54>327M-/L76M/6CF $6-&JN?R8`G((G`87_G)@nTTn@@nTTn@$Fx\\xFFx\\xF/9^D&&D^99_D&>!A#c>Nu&D#I*(MrJIrN((NrKTn@@nTTn@@nT\xFFx\\xFFx9r *>Ri5I++%+?++A]A)9IYiy ]и/и I?9*I?9A++]A+)+9+I+Y+i+y+++++++ ]A5&565F5V5f5v5555555 ]A55]?T:D+N0+++ 9014&##32>&&'.###!24.#"32>7#".54>32>=," +'6#?/96%  @nTTn@@nTTn@$Fx\\xFFx\\xF6?*v7<0J&:(9O $+**' Tn@@nTTn@@nT\xFFx\\xFFx9r4H\U?S+ +%&+I5+&%9/A]A)9IYiy ] 9/.A55]A5)595I5Y5i5y5555555 ]A?&?6?F?V?f?v??????? ]A??]I^DN+X:++ ++01%#".7332>54''.54>32#.#"74.#"32>7#".54>32sd0S="63C%6(t|.#1H0+K8!6)7CJ2$HJ@nTTn@@nTTn@$Fx\\xFFx\\xFMX3M4-<$+Q /" <..D+ 2#@5)) #GKTn@@nTTn@@nT\xFFx\\xFFx9r/ۻ&+++A]A)9IYiy ]A&6FVfv ]A]1!++ ++01###5!4.#"32>7#".54>32ξ6@nTTn@@nTTn@$Fx\\xFFx\\xF0Tn@@nTTn@@nT\xFFx\\xFFx9r)=ݻ 4+  ++*+A]A)9IYiy ]A & 6 F V f v ]A ]*?%/+9++01%#".53326534.#"32>7#".54>325T;;T57XNOW7@nTTn@@nTTn@$Fx\\xFFx\\xF/Q<#"7#".54>32@:@nTTn@@nTTn@$Fx\\xFFx\\xF4J Tn@@nTTn@@nT\xFFx\\xFFx9r"67//A]A)9IYiy ]7-и-/#-#9-#9 -#9- A & 6 F V f v ]A ]-#9-#9-#9!-#9#8(+2+014&'##32>&&#"33#".54>32'#BB&(@nTTn@\8``9&v|I}vFx\\xFFx\\xF-Bv2 "3xDTn@@nVEPPE\xFFx\\xFFx9r 34/ /4*и*/A ]A ) 9 I Y i y ]  * 9* 9* 9* 9* 9 * 9*A&6FVfv ]A] 5%+/+01%#'#3734.#"32>7#".54>32AA@@x@nTTn@@nTTn@$Fx\\xFFx\\xF,Tn@@nTTn@@nT\xFFx\\xFFx9r01/'и'/A0]A] A0 ]A ] 9'99' 2"+,+01#534.#"32>7#".54>326=@nTTn@@nTTn@$Fx\\xFFx\\xF0W#Tn@@nTTn@@nT\xFFx\\xFFx9r 12/ /2(и(/A ]A ) 9 I Y i y ] (9(9(A&6FVfv ]A]3#+-+ ++01%!5!5!!74.#"32>7#".54>32Jpz@nTTn@@nTTn@$Fx\\xFFx\\xF101Tn@@nTTn@@nT\xFFx\\xFFxF O ++  +   + ++01##52673!!!(\\-E ;Y;\ D&< ZF*.2.0+! +/++A ]A ) 9 I Y i y ])0/9/4+/+2,+*++01!5>7>54.#"#>32!!!!& $-0&K;$$/'3 `4\J,P<##;P- =Z;\a!79># * "*E>+.B*0LB="*  ZF?ͻ+6$++6A$$]A$)$9$I$Y$i$y$$$$$$$ ];9++ +1&+!+ 1+ܺ;!901!!!%#".'332654&##532654#"#>32\!$=R.Pa7c$<-:JF8GE5:,4 [/YK.R=$&,%Z*A-"4?$ 40/=P10_!" <>/*>(#3"#:F g+ ++ ии++ + и01%##5!533!!!8XY*kX;\.Rmqt ZF.26Ż24++3/+A]A)9IYiy ]%43938/3+60+ +"#+*+*%*901%#".'332>54.#"#!!>32!!!8Z@@T4e/#*6! ,-!i %7)3M4;\-P<"/:&40& _T 8L ZF3B+;+4++A;&;6;F;V;f;v;;;;;;; ]A;;];)и)/*;9A44]A4)494I4Y4i4y4444444 ]++@ +"+/6+ܺ*6/901!!!#".54>32#.#">324#"326\!{;V8@_>&E^8BS0a 0(6H +>*2O6Y'8' $6&KLZ0O9+QsHZO%'45 74$   4G8  &<,AF e// и /  9 9    +++01#!5!!!!]נ;\ TE ZF'OSW=SU+ 2+F+TP+FA & 6 F V f v ]A ]A]A)9IYiy ] <(7UT9KUT9TYPT+WQ+-+A+#+7#9K#901%4.#"32>4.#"32>#".54>7.54>32!!!-;!"<,+<%"<- (7! 7((7!7*f$B^90[G+$)$%AV00VA%$)$;\**+ +:$ %% %1F--F1%7'!. (>++>( .!'7 ZF3C+<*++ 9A<&<6<F<V<f<v<<<<<<< ]A<<]++ +/9+?%+  %?901!!!#".'33267>55#".54>32.#"32>\!&E^8BS0a 0(6H ,=)2O6;V9@_?]$6&KLAD%7(ZZO%'44 74$  5H*0O9+Qs %=,A5BG!F  $($&++++%!+A]A)9IYiy ]A&6FVfv ]A] %*!%+("+ ++й014&#"326##52673#"&54632!!!29922992|XH*68\bb[\aa]Q;\,}vv}}uu=%˦ ZFq++ ++ +++ и 01##52673##52673!!!XH*68OXH*68;\ =%=%@ ZF59=ٻ9;++*+:6+A]A)9IYiy ]4;:9:?6:+=7+5 +%+ и/и/%01##52673!5>7>54.#"#4>32!!!!JSE*.8*-&4!$$'U %H<'C0%4 #  P;\=%a$)':32%(+ DD3-E.'B=32!!!PSE*.80D(CO* V,'485*51**2)#)S 'J@!=0#O;\ =%:*A.&7>$3495P60)3$=;+(=)#2#"7 ZF + +++  ии +++ +к9 01%##52673##5#533!!!SE*.8?PW?Q;\=%$]o ZF59=9;++(+ +:6+A]A)9IYiy ](-и-/(.и./:?6:+=7+++,++1#++и/.#1901##52673#".5332>54.#"#!#6632!!!NSE*.8-F.;I(X$) #"S C4%+>(T;\ =%g*P?'#39 )40$ `P!9J ZF 9=A3=?++++>:+A]A)9IYiy ]A&6FVfv ]A] 0и0/>C:>+A;+ +(+5+(и/#ܺ05901%4&#"326##52673#"&532#4.#">32!!!*/3<6>3.pSE*.8^WnZ;E# Z# !.!;+Q;\9B-$QaM=%`eq3(64 (%?  0J ZFy+++ 9 9++ ++и/01##52673#!5!!!!SSE*.8SzP;\ =%DP= ZF!*RVZYVX+*&+ 5+I+WS+IA & 6 F V f v ]A ]A]A)9IYiy ] ?*#+:XW9NXW9W\SW+ZT+0+D+&%++:9N901%4.#"32>4&#"326##52673#".54>7.54>32!!!#,,#"-,#943:(0=tSE*.84J+*I6!2D((D2!J;\**+ *;/540$ 6`=%B-D..D-"5'!. %>++>% .!'5 ZF >BF?BD++7++C?+A]A)9IYiy ]A&6FVfv ]A]-и-/CH?C+F@+ +:+ 2+и/и/%-2 901&&#"326##52673#".5332>766'#".54632!!!5=% */3@wSE*.8bc;E# Z#!-!<-cR9L/J;\qQa$29B.=%Ę(64 (%=  0J3dr+T{* ZF4@DHaDF+$+;+5+EA+A]A)9IYiy ]A&6FVfv ]A]FE9A;;]A;);9;I;Y;i;y;;;;;;; ]EJAE+HB+8+>'+'и/>,ܸ>2и2/014&#"3263!5>76654&#"#4>32#"&54632!!!11%1 " $5 V<4EJqGZZFFZZG4;\2|s;Z>>Z:r&BDF"(DRR %-0?q0$(%)E=*P ZF + + +01##3!!2OcZ\}FPiZF,0U1/ /1.и./ 0ܺ.09.,ܸи/02-+0+015!>7>54.#"3>32!! (O>&&?R-8O6n3-9C#9J'2-$ \ d  =DN1-F0,5/#&'*3"<88#hxZF@DQ7B+D +DܺBD9DF<A+D+)1+&+01%4.'>54.#"34>32##32#".5#32>!!'!%?W16M4k2,$3 'EF-#D44>! s5bQ=]?!\)7#!3)*A+&.,% $!!# ]&0/"A=+0BZF W//и/ܺ99 ܸ ++01%#75##!35!!.fy8o\dyZF*.[!,+. +.ܺ ,.9.0&,+. ++ + +01%4.#"7!5!3>32#".5#32>!!";Q/ 2$QOv )7GB>$2n4ZH:[@"\5T9eJ<;B <:,9PZF59s://:7и7/6ܺ769&и&/7.ܸ6;16+9)++"+%01%#".5>324.#"&6766323&&#"32>!!&/8, $0 EAf#=S/$7)  F:<7lze7^E'~54.#"32>!!QEBTRDDRJ8/$E==E# =[;9Z?!#)F_55^G)\3.10-32,' #-*"6'!0 "?..?" 0!'6"2H..HZF7;o7#".5#32>!! !. @TO=&8%i!!+88**88+J56W8.L88K--K88L.j\*qyyqp{{Vo %G:KwT,,TwKKyV..VyZFa++ +ܸ ++ иܸ и01##3!##3!!J56WJ56W\ %G  %G jZF154+'++5 +4ܸ (и(/*957(2+5 +4+(и/ и/01##34&#"34>32!5#667>!!]K4(H8[[+=* c%#+ 4'+)! } ,":,t\~#'GXe'00) )&-).6A+-'X]* %BCEZFEIH+<+1 +I+HܸI HI9-ܸIKG+I+H+.6+%++GA01##3%4.'>54.#"34>32##32#".5#32>!!^K4(HC  /@&.@+ d% &'%'77)+"#(d (MA.J5h\~#'G)7#!3&'@.%-+% #3#+4_39# !# C:'-DZF  +++99 ܸ и+++ии/01%#7##3%5##335!!K4(HJ32#".5#326!!WJ5*JJ*C0- ] *03. f+M>`Xf\~#'G(K;$_<@I:=:*}ZFDHG+;+ +E+A ]A ) 9 I Y i y ]Gܸ #и#/0и0/1и1/EJF+G+ +++Gܸ06иF@01%#".'6632##3%4.#"467>3234.#"32>!! !(  5*-+}J5*JE/A&&# _$JB-I4/M9/E.j\+!0A&)D~#'G/G/ ,' 78++X\AlN+#>UZFk+ + +ܺ9+ ++и и/01##35!!3!!gJ5*J>g\~#'G,PdnZF$HLC"K+?+ +L-+A ]A ) 9 I Y i y ] 9/A]A)9IYiy ]ܸK$ܸL%*KL97:KL9LNJ+L2+K+ +K!ܸи/ܸJD01%#".54632'#"&54632##3%4.'6654.#"32>!!*(":99:6+,58))8J5*JS(3G(-G2(7L..L7]\' ',;;/).*)0/~#'G#6( @9%A00A%;? )6#,E//EZF BFE+!++F+A]A)9IYiy ]Eܸ+и+/8и8/!9и9/FH@C+F+E+&3+ и /ܸ&801#"&54632##34.#"32>7#".5#326!! 5)-+1-% |J5*JN.N;0F-/@&&"!_ )<,bh_\i+F63G0B\~#'G`@mO,"32%4.#"34>32!5#>7>4.#"32>!! &9'5<\ 3/$   &q $B66B% EA:D" T\?$VL3.HX*!J>))>I{6*(=H $ %!mI2+ad"&"EB@|;nT34Vo:6yhD7_Z9r1-/#+014.'&667732>5%#".54>32: #  } uFx\\xFFx\\xF  (  vx \xFFx\\xFFx9r7Ku8+A]A)9IYiy ]и/8M4=+G+01%4&#!>7>54.#"67>323!267#".54>32~ 6Q:%D5*F[1?_B)  /B.8-)4RpC"Fx\\xFFx\\xFK$&% (4!0A(1> '!?BA\xFFx\\xFFx9r@TA+AA]A)9IYiy ]AV<F+P +'/+"+01%4&'6654&#"67>32#"32#"&'&&32>7#".54>32 4,*{CV6 '<,8.8U99[@"`ZPS"=\C8bH*Fx\\xFFx\\xF6A35KM(-   ! "0+.; 8.*C\xFFx\\xFFx9r%9e&+A]A)9IYiy ]&;5/++01%#74&##4.'&33326553267#".54>32f  0h Fx\\xFFx\\xF* 3mm\xFFx\\xFFx9r@TA+AA]A)9IYiy ]AV<F+P+%/+ +/01%4&#"7663!2654&#!"326767>32#"&'&&32>7#".54>32w3J?#/    *.B*-=#CW " (>W9:`E%Fx\\xFFx\\xF^h ]%&   *+(% 4H\xFFx\\xFFx9r:NO//A]A)9IYiy ]OEиE/;ܺE;9E4A4&464F4V4f4v4444444 ]A44];P6@+J/+ +"+01%#".'>324.#"467>32664'.#"!2>7#".54>32WQ,B-.C/VLV#B\90F2  )4#*S, %*6@&AiK(7_F)Fx\\xFFx\\xF39 +!6//D- : ", &)KkC.G\xFFx\\xFFx9r)] +A&6FVfv ]A]/%+01&&#!"3!676&#".54>32 &Q %  Fx\\xFFx\\xF  "\xFFx\\xFFx9r =Q4H+>"+>ܺH>9A""]A")"9"I"Y"i"y""""""" ]H*ܺ/H>9A4&464F4V4f4v4444444 ]A44]>S9C+M'+ ++01%#"&54632'#"&546324.'>54.#"32>7#".54>32Ya[__[[_QQNTQQNTr$  &BZ4s  $)Je<''>\xFFx\\xFFx9r 7KݸL//A]A)9IYiy ]LBиB/A&6FVfv ]A]8ܺ B89M3=+G+(+ +01#"&546324&#"32>7#"&'&"32>7#".54>32.C/UMWQXYl9`F($A\90E2  (5#*R- "gPAjJ(Fx\\xFFx\\xF!7039D/G/.E- :", &#3)KkB\xFFx\\xFFx9r#/KŻ0+$*++A]A)9IYiy ]A**]A*)*9*I*Y*i*y******* ]++GиG/01#".54>324&#"32>'#"&546324&&'&67732>Fx\\xFFx\\xFLKKL&8&%9&K"**""**" h  , -\xFFx\\xFFxdKiCCiKbjjbbkj   E 9r/K1+/+++G01#".54>324&&'&67732>%4&&'&67732>Fx\\xFFx\\xF h  , L h  , -\xFFx\\xFFx   E    E 9rEa}W ++A]A)9IYiy ]:и:/6++]01#".54>32'4&#"67>32332654&##667>4&&'&67732>Fx\\xFFx\\xFRL2A( 4&*;#& $G8#T h  , -\xFFx\\xFFx5AM&5: $- $&*/45 826;   E 9rUqg ++ܺ 9A]A)9IYiy ]Q++<F+.6+64и4/g6.9Qmиm/01#".54>324&'6654.#"67>32#"332#"&'&&32>4&&'&67732>Fx\\xFFx\\xF$+;#'7$&+#   0),-  SG0A)S h  , -\xFFx\\xFFx73 40$6$&*  (,) 4.0*?L2=Z   E 9r1MPQC ++-кO 9P 9/I+I+и+/01#".54>324&##4&'&3372553264&&'&67732>%#7Fx\\xFFx\\xFw7   $ "9; h  , %-\xFFx\\xFFx/" W)Wb   E 9rTpúf +MU+B+%+A%%]A%)%9%I%Y%i%y%%%%%%% ]M.и./M/и//P+++=E++f9Plиl/01#".54>324.#"76332>54.##"676676632#"&'&&32>4&&'&67732>Fx\\xFFx\\xF(7 ,/ %!  #,30  JB(?,T h  , -\xFFx\\xFFx/E.` %    -"4A *;4G_   E 9r8TdJ +39+U_++A]A)9IYiy ]A__]A_)_9_I_Y_i_y_______ ]_и/6+.+bZ+!+J!96PиP/01#".54>324.#"&7663266'.#"3264&&'&67732>%#".'6632Fx\\xFFx\\xF,:&4  1!+( %03G.aQKUT h  , b '  3.+)-\xFFx\\xFFx2D*%39%&  &,PoB}z`0   E + +6&29r+G%= +-+C++01#".54>32'66'&&##"3367'4&&'&67732>Fx\\xFFx\\xF  h  , -\xFFx\\xFFxF&   r    E 9r7ScoeI +.8+TZ++ܺ 9A]A)9IYiy ]8&) 9AZZ]AZ)Z9ZIZYZiZyZZZZZZZ ]jZT9j/Ajj]Aj)j9jIjYjijyjjjjjjj ]d3+!+_W+mg+Igm93OиO/01#".54>324.'6654.#"32>4&&'&67732>%#"&54>32'#"&54632Fx\\xFFx\\xF &=,*<' ,D00D,D h  , o5308 '' -+*./))/-\xFFx\\xFFx.!7.4((4.7!.7++7L   E w&33&!!)+*)(,+9r8TdJ +:+U[++A]A)9IYiy ]U$и$/A[[]A[)[9[I[Y[i[y[[[[[[[ ]6++!)+`X+JX`96PиP/01#".54>324&#"3267#"&'&&3264&&'&67732>#"&54>32Fx\\xFFx\\xF]Q$<,,:&4  .$+(%0eXK h  , k 3.+) ' -\xFFx\\xFFx=~y.@&2E+%3=%&  &i   E ")50+ +69r ;Oc-F++P<+A]A)9IYiy ]A]A)9IYiy ] и /A<<]A<)<9<I<Y<i<y<<<<<<< ]PeKU+_A+Aи/K8и8/01#"&546324##4>7>54&#"67>323326%4.#"32>7#".54>32"**""**"& $9(HK*8$ &$EG  (R&9%&8&&8&%9&lFx\\xFFx\\xF5biibbji##744AG-4  (" N<046%KiCCiKKiBBiC\xFFx\\xFFxy'O+#и%$/+ +'+ и'"01%#"&'&&#"'66323267##5#53533 #(#9 @ ;K'%5 "<<88O$061O/ / 9 901%.'>72ZZ2-rz{66{zr-,UJ<7.'71Z2-rz{66{zr-26YEX / >Y901%.''>7'L@33@L'$,UJ<7 ,UJ<3232>7'%%7 "+1)N#),))%  "+0)O#(T*% ^ ;       j**7y#))/+ +01%#"&'.#"'>3232>7'%%7* "+1)N#),))%  "+0)O#(T*% # ;       5**T{+и/и/A&6FVfv ]A]++++01!5!5!5!'!".5463!!"3!TCC(3S%>Dl6%6:c)#7#MSn IWY^(dem(o JWY`(edm6 J/EX / >Y + 9 9  9 901%5%%5%!5!5%%6|||o(ii(0$+(ii6 J/EX/ >Y +99 9 901%%!5!55%%56|||#(ii*$(ii(6 G// +  +999901%5%%5%!5!5!5!5%%6|0|||0\%]]%("/"%]]6 G// +  +999901%%!5!5!5!55%%56|0|||0'%]]"/"%]]%6 /++01!5!5!5!55%%5%%6|||0|0 "C"%]]%/%]]6 /++01!5!5!5!5%%5%%56|||0|0 "C"<%]]3%]]%Dm+ и /99 // 9 9 9 9014&'667567&'5/*7),+$@Z71'%06[A$6H43~FAxgR2(%2SgxCR +++ +& ++01%!5!5!5!'#"&'.#"'>3232>7RCC! "+1)N#),))%  "+0)O#(T*% C'i'       R'!++" +01%!5!'#"&'.#"'>3232>7RC! "+1)N#),))%  "+0)O#(T*% '       v0#G++<1+ +B)+01%#"&'.#"'>3232>77#"&'.#"'>3232>70 "+1)N#),))%  "+0)O#(T*%  "+1)N#),))%  "+0)O#(T*%               "H/EX/>Y01!!5!7"(9!180+EX/>Y+01!#!5!1&z#1 T/ / и /   //EX/>Y+01!!5!5!5!%#3#31zz####!@!=11 , /EX/>Y+01!!5!5!5!''1zzx"xx!w!@!A *A Z'+/>+-+ ++,(+A]A)9IYiy ]A & 6 F V f v ]A ],1EX/>Y(,+/)+#+A'7GWgw ]A]014.#"32>7#".54>32!!!"7%&7$$7&%7"^ =X88Y= =Y88X= 4,-M7 7M--M7 7M-@nQ..Qn@@nQ..QnG 2Z048i46+-+51+-  -9!и!/5:15+82+7"+",01%##"&5433"5546267667#"546332##32!!!E        (4   U 2ZIMQMO+9+N+A]A)9IYiy ]CON9DиNJNSJN+QK+D+4+01#"55!#"&554>7>54&#"&''&&764>32!5432!!! 6l_ 6'H<+F2L  #A^;.T@&)BS+*6"f4  $DEI) ',2%  V  &-&,E0+A3*   2ZdhlŻhj+[8+ie+[#A88]A8)898I8Y8i8y8888888 ]`8[9inei+lf++V=+6(+`(6901%#".''&6776632>54.###"5546323254.#"&''&&76>32!!!-Lb52RA. = )7B&>7&".xtn+7+G2M  #A];0XD(& ]43K1  a  (% p G    U  !' *?() / 2Z26:68+,+7+, и /к879-и2ܸ737<EX / >YEX/>YEX/>Y 7+:4+ܸ,и-ии- ии:*и7301%#"55#32##"&54335!#"55#"546332##35432!!!Fa  q  bZ4r dd [z  2ZQUYUW+"+A""]A")"9"I"Y"i"y""""""" ]>"9>/Yܺ5WY9>IкLWY9YRY[RV+YS++=J+O%+5%O9L%O901%#".''&6776632>54&#"''&&767#"5463!5432#"55!6632!!! *Ib97T@- X '3<#!@2^R7Gn  E3`43Q8# [  "2!>C'R    p 2ZIMQMO+$8+.+NJ+A]A)9IYiy ]A$&$6$F$V$f$v$$$$$$$ ]A$$]NSJN+QK+3+=+)+01%4.#"32>'&77.#">32#".54>32766!!!*6&9+)<)!8) "/ 3T< %3F-.T@&)E[1EmJ'/RqB-C2&k4.#+,$ .  "=T1$/F-3T<"1Sn>HvT/   2Z!%)o%'+&"+'&9'&9'ܸи&+'+)#++ и'"0132##"5433!#"554632!5432!!!   'gb4   R 2ZAEIEG+ *+;+FB+A]A)9IYiy ];9/A]A)9IYiy ]A & 6 F V f v ]A ]1* 91/ /GF9=GF9FKBF+IC+%+6++/9=901%4.#"32>4&#"326#".54>7&54>32!!!*D11D**D11D*MSSMJVVJ},Mh<#"&''&6776632>7#".54>32!!!%@17,XK(9&~/SqBLr!G U0AU1 "/>%6^F((D[3FlJ&W4+).==$)IDvV1* ` %2GN#/G04S;/Ro< 2G';Oo2F+ ++<(+A]A)9IYiy ]A & 6 F V f v ]A ]A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-++#+014.#"32>7#".54>324.#"32>7#".54>32I!!!!&BW11WB&&BW11WB&>jQQj>>jQQj>$CuYYuCCuYYuC,:]@##@]::^B$$B^:KrN''NrKKrN''NrJQj>>jQQj>>jQYuCCuYYuCCuG,@Tѻ7K+$+A-+A--]A-)-9-I-Y-i-y------- ]A7&767F7V7f7v7777777 ]A77]AV<F+P2+P01%#!"&54676676654&#"&54676676674.#"32>7#".54>32 6 ,,.T  6 >jQQj>>jQQj>$CuYYuCCuYYuC*  +   \  Qj>>jQQj>>jQYuCCuYYuCCuGBVjMa+#+-+WC+A]A)9IYiy ]A&6FVfv ]A]7#9ACC]AC)C9CICYCiCyCCCCCCC ]AM&M6MFMVMfMvMMMMMMM ]AMM]WlR\+fH+8+(+( 01%#!"&&676676676654&#"#"&54>3232>76674.#"32>7#".54>32(>& ; 1! $,+ 9N-.T@' :P/    >jQQj>>jQQj>$CuYYuCCuYYuC/ @!9#H'.0  -'&7*'?-#=86  pQj>>jQQj>>jQYuCCuYYuCCuG'w, +\d+nT++A]A)9IYiy ]A&6FVfv ]A]nB(ATT]AT)T9TITYTiTyTTTTTTT ]A\&\6\F\V\f\v\\\\\\\ ]A\\]sTn9EX-/->Y#++iW+-5A5'575G5W5g5w5555555 ]A55]-=A='=7=G=W=g=w======= ]A==]i_01#".54>324.#"32>#"&'&&663232>54&'&&546276654&#"#".54>32CuYYuCCuYYuC$>jQQj>>jQQj>,FV*>f ""# ), $$ $".&'  4R95K0+072$-YuCCuYYuCCuYQj>>jQQj>>j:-?' 0* $ ,"5E8-6,' ,#(6 /!&>G2FZ=Q+/+G3+ܸ/иA33]A3)393I3Y3i3y3333333 ]A=&=6=F=V=f=v======= ]A==]G\BL+V8+0+0и001%5####"&54676655#"&'&67>323274.#"32>7#".54>32ڵ L   &  J >jQQj>>jQQj>$CuYYuCCuYYuC >  > S    ^Qj>>jQQj>>jQYuCCuYYuCCuGK_sVj+ ++`L+A&6FVfv ]A]A]A)9IYiy ]- 9-/AD 9ALL]AL)L9LILYLiLyLLLLLLL ]AV&V6VFVVVfVvVVVVVVV ]AVV]`u[e+oQ+ +0<+G+DG901%#".5463232>54&#"'.67>7662>76#663274.#"32>7#".54>32.K^0$D6!(!*#!!;@!* ANX, &ILC D;.R?%>jQQj>>jQQj>$CuYYuCCuYYuC5P4#/$$' 4(AP !=<>"1 %O$2G?Qj>>jQQj>>jQYuCCuYYuCCuG :NbEY++ +O;+A]A)9IYiy ]A&6FVfv ]A]3и3/A;;]A;);9;I;Y;i;y;;;;;;; ]AE&E6EFEVEfEvEEEEEEE ]AEE]OdJT+^@+ +-+6+%ܺ36901%4&#"3267#".54>32#".'&&#"663274.#"32>7#".54>32H2--22--2$=Q.A^?9Yl4):$ 6 C&3L2>jQQj>>jQQj>$CuYYuCCuYYuC@HH@@GGL.I3/Pk>jQQj>>jQYuCCuYYuCCuG)=QR/*/RHиH/A**]A*)*9*I*Y*i*y******* ]*>H>9H4A4&464F4V4f4v4444444 ]A44]>S9C+M/+'+01#".54>7667#"'&&77663!24.#"32>7#".54>32U( - +!'G+!0I >jQQj>>jQQj>$CuYYuCCuYYuC $Z+cB,#  &6H.6V-   #Qj>>jQQj>>jQYuCCuYYuCCuG%K_sVj+ 0+B+`L+A]A)9IYiy ]B9/A]A)9IYiy ]A & 6 F V f v ]A ] 8&589GB9ALL]AL)L9LILYLiLyLLLLLLL ]AV&V6VFVVVfVvVVVVVVV ]AVV]`u[e+oQ+++=+01%4&'&&'3264.#"66#".54>7&&54>3274.#"32>7#".54>32p %R#   ,=: '' N%%$AX35X@$#.45!9L,(I9"',>jQQj>>jQQj>$CuYYuCCuYYuC) &*3q" ) 2)C2'<( 1$C6%=,&4!&1>QQj>>jQQj>>jQYuCCuYYuCCuG 6J^AU+-+ +K7+A]A)9IYiy ]A&6FVfv ]A]%и%/A77]A7)797I7Y7i7y7777777 ]AA&A6AFAVAfAvAAAAAAA ]AAA]K`FP+Z<++2+ (+%( 9014&#"326#"&546323267665#".54>324.#"32>7#".54>32]0**00**0*Kh>J[%$"F)+E1:S59\?">jQQj>>jQQj>$CuYYuCCuYYuC@HHB?FGOZ1/. *! `9"4I,+J5)Hb]Qj>>jQQj>>jQYuCCuYYuCCuy[n  +A]A)9IYiy ] //+9901%#'!#3#>54.'3yy@II@B>8+' '+8>IU4##32>#!!27#>54.'3?UN";,$4#Sxg)I6848+' '+8>A@0Jt)]X%>.5LVNU32#&&#"32>77#>54.'3(CZ7GkI%%IlF-UD-;iE=W77V>+E3>8+' '+8>:^B$4ZzGGzZ45M3KQ1Pf45eP16J+RU7##32#>54.'3M=`CC`=;Ջ>8+' '+8>,GfAAfGU54.'3EVRn>8+' '+8>54U54.'33)<>8+' '+8>/4ɈU  +,)+-6+)и/A&6FVfv ]A]1 -9A66]A6)696I6Y6i6y6666666 ]; -9-@0/;/$++,)+0;901#'#".54>32#.#"32>5#5!#>54.'3,lUDkK''KkD1WD. <3G-=W88W>4P6>8+' '+8>w>G1X|JJ{Y16P59.1Of45eP1#?W34U54.'3r<;;r<>8+' '+8>9LjU54.'38;;>8+' '+8>U54.'3 ZX^V<??6;;7>8+' '+8>kdjjSLIOU54.'3Hp<<WL>8+' '+8>MqV7U54.'3=o;V>8+' '+8> U54.'3565OO>8+' '+8>  BU54.'3tC;Cl;>8+' '+8> !ĈU7#".54>32#>54.'3"iM,,Mi>8+' '+8>-?hK))Kh?>hK)*Kg>Q}T,,U|QQ}U,,U}RU54.'3.L>>L8+' '+8>BAABWaaψU32#>54.'3y"iM,,Mi>8+' '+8>1?hK))Kh?>hK)#C%H&nCU*K&P.,T}QQ}U,,U}VU++'+-6+A]A)9IYiy ]и/и"-9,-91-9A66]A6)696I6Y6i6y6666666 ];-9-@;/0/++"9,0;9014&##32>.'.###!2#>54.'3.DC1% 1*;'E4?:*  >8+' '+8>;E .P  "B4!*@,?U '/..+ GU54''.54>32#.#"7#>54.'3o!>W65\B%;!7J(!;-3&6O5/R>$<-<"JQ7(OR>8+' '+8>*B/8T91B( 0Y!##3&#B23K/#8&G:,. 'LQU54.'3g;>8+' '+8>N5؈U53#>54.'3p:\AA\:;/D++D.<>8+' '+8>3YB&&BY3r8O11O8̈U54.'3G?>8+' '+8>`}@̈U54.'3H{{H>ouPwn>8+' '+8>Y3y))ӈU54.'3GGFF>8+' '+8>I: U54.'3;C>8+' '+8>U y?׈U54.'3lf>8+' '+8>656 U5#"&5#".54>76654&#"#>323267%#>54.'33N47/!9)i% TE 7(%BY5!"B4* 70B&!=-  >8+' '+8>O  %()&7",%*4/!-6" 4, )(:' ":-#U7#"&'#366327#>54.'3$8'*<&&<,'8$62L36X77W33L2>8+' '+8>$G8##8G$$G8##8G$1YC(41X-9(CZ#U32#&&#"3267%#>54.'31 aP2N55N3$?1!7F5'9'':$4KJ>8+' '+8>QY#AZ74ZB&*<&3@ 6H(,H5C8FU#5#".54>323#>54.'3&<*'8$$8',<&77W63M22M33W7#>8+' '+8>$G8##8G$$G8##8GX14(CY11ZC(9- U32%#>54.'3&5!!4%&5#=H 6dQ-J52K36L0>8+' '+8>L;..; 0'C1E2NX!?[:2YD'*G_5U54.'3%'&#"3###5354632>8+' '+8> !TT7MM*; ,U#".'332655#".54>3253#>54.'3#7&$7%KI$6$7]j$@2!8I3NB'//K55K.0&7>8+' '+8>v#B4 1C%Qd0A`p"5%3*SU%  ;T51U=# IۈU32%#>54.'37 ,!4%77'..@'>8+' '+8>"3%*;"".C(U54.'357799>8+' '+8>X[ˆU54.'3#"&'53326537#53>8+' '+8>S(3  799,U54.'37A[77L>8+' '+8> Sn߬U54.'3566>8+' '+8>ĈU54.'3729.7 %"2 77J53I H6IM>8+' '+8>=87?-=#6+!->$N'51*)2NULU32#>54.'378< 3&77(/.?'>8+' '+8>B#@K*;"P#/B)AU7#".54>32#>54.'3):#$:)):$#:)77N11O77O11N7>8+' '+8>1,I44I,,I44I,6ZA$$AZ66ZB$$BZ;U7#"&'#36632#>54.'3'6"&:)&<*"6'75K.7W77R=.K5>8+' '+8>_(H6 6H)&G7"6H)9[?":+_X,:"?\lU#5#".54>3253#>54.'3);&"6&&6"+<&77W8.J55J.=S7>8+' '+8>c)H6 6H()H6"7G+:"?[99\?":,XU54.'3";+77U>R>8+' '+8>->%j2@U54.'30B'&A17I87C5-G-#SQ"=.7?447"J.">8+' '+8>%4"(>*9;,,&" );F&8$15+# *MU54.'3  & OO7\\m>8+' '+8>  'C//U553#>54.'37 O;SN7:>!2#7>8+' '+8>8R*5YV.B>0@$܈U54.'34>;H>8+' '+8>0kԈU54.'3?on?:tn?nt>8+' '+8>0yssֈU54.'3;??B~}A>8+' '+8>Dݲ U773#>54.'360- :@>8+' '+8>E'+0 .{U M +  9 9 // ++01%!5!5!!%#>54.'3T-(>8+' '+8>G.s/,U32F0""00""0-#./""/.#,"11""00".""..##.G //01#7G'GR+и01#73#7'G'G ++иии 01#73#73#7Ā'G'G'G9r#2FM=++,+ и#и#/8+B%+,и,/01%5#667>54&#"34>32!33>7#".54>32E4(P<#7%. %/1*)#9*`>5$5,=(Fx\\xFFx\\xF /,S-)09#MS5J+9+<73E#6C 9AFF]AF)F9FIFYFiFyFFFFFFF ]S 9`u4e+oK+ +(+(и/Kи/(SܸCиe[01%#"&54632'#"&546324.#"34>32!5#667>4.#6654.#"32>7#".54>32@21@A00B8''71-.1'5%9&/ &05,+$;+#G6*!X$ -%5!!5%. *7,;""<,hFx\\xFFx\\xF>@@>>>>3553+887&<)6J+9,=63E#632%4.#"34>32!5#667>4.#"3267#"&'#32>7#".54>32 :117 &('4$7&/ &02+*#:+(H5) U*=&"7(SD-; +*2/J?'=*kFx\\xFFx\\xFCSQD6))5&;)5J+9,=64F#6324&'6654&#"3>32##32#"&7#32>%4.#"32>7#".54>32 ++ ++ 5.!1PC"8'/&3/!$$/<@33?0,;# <-V(;()<((<)(;(`Fx\\xFFx\\xF-3ZC&&CZ33ZC&&CZ>G B0EP2E(4&;-'/=9=BNG/H1+@CkL))LlCBkL))LlB\xFFx\\xFFxznw%8+A&6FVfv ]A]@ܺ_89_/iܺ _i9-89-/#089Z89n_i9(/d/+s +9 9093и=к@9 EиsUкZ(d9n(d901766&&#"6632#"&'#".5467#".54>324.#"&'&&54>32.54>32>32n )&$?0 +##!    !##+ 0?$&) '52.)  ).25'< ;.1& 2:    :!(""(!:    :2 &1.; 2&!+"$*)F33F)*$"+!&2QCQ+AC&C6CFCVCfCvCCCCCCC ]ACC]C=!=!9=/ܸ=7к8C=9HC=9CIиC_bQC9/@/EX/>YEX/>Y,2+$+ +A]A(8HXhx ]и/$7и7/$HиH/2Nи,Tи$\иbиb/eиkи qи016632#".76.#"2#6632#".5##"&54>7##"&54632>5"&5463&&#"#"&54632654&'&&54>323.'&>323>32(3#  %6   '    '   6% $ %"  #3(,6P<,  $. .$  ,и>/NиN/W/F/F9.F9>F9NF901'&&'#"'&&'&&'&&'&5467665.54>&&'&&66326676!         !!)  )!      -w8-I22I-8w-        #.$$.#  fO'4+g+A&6FVfv ]A]ܸܸи/,и,/<ии>/Fи0NиN/vZиZ/bиvjиj/)}oиvи/ܸܸи/и/A]A)9IYiy ]и/9/A]A)9IYiy ]ии/ии/и/ии/и и ///T//01%&'&&'#"&7667&&54664&'&547>54.'&547>5&&5466&&'&63266766&'&&'66766&'&&'#"&7667&&54664&'&547>54.'&547>5&&5466&&'&63266766&'&&'66766                      :3**3   3**3   3**3   3**3   l}|L1++c~+A&6FVfv ]A]#19#/ܸи/ܸ)и)/9и9/EиE/A~~]A~)~9~I~Y~i~y~~~~~~~ ]p~c9p/App]Ap)p9pIpYpipyppppppp ]MܸcUиU/chܸvиv/hи/cи/A&6FVfv ]A]ܸܸи/ܸи/и/и/M/01&'&&'"&5.'&&5467665&&5466&&'&63266766&'&&'"&5.'&&5467665&&5466&&'&63266766&'&&'"&54&'&&5467665&&5466&&'&63266766                          2P@ @P2   3**3   2P@ @P2   3**3   :@ ?:   3**3 6cB+LR+ARR]AR)R9RIRYRiRyRRRRRRR ]3RL93/A33]A3)393I3Y3i3y3333333 ]A&6FVfv ]A]RL9и/"B9"/(B:и:/="(9B]%+GX+ +=+.GO01%4.#"3267##".5463232>54.5463.54>32#"&5467&&#"h(8: )8: I7<'1@#*@-/$,ALA,7<' 0@#(A-0$,ALA,8-!7-!!04 $+#2"$.  */6@L/04 $,!2#%- ,/4?L++ии и ܸ и / и ии иииܸ/ /01%5#5#5575575375377X_IJKhr #/;GT/EXB/B >YB9B9#B9$B90B9<B9017>&&''#"&&6771'.766'&>77'&&6632x  H7 5G  { H   78   H W  3   4   H   32   J  h #/;GG/B/B9B9#B9$B90B9<B9017>&&''#"&&6771'.766'&>77'&&6632x  H7 5G  { H   78   H x  3   4   H   32   J  h #/;GS_kwA/EX/ >YA9A9A9*A9;A9GA9HA9TA9`A9lA9xA9A901%7>7>&&''&&''#"&&677#"&&677'.766'.766'&>7'&>77'&&6632'&&6632x  H7   H7 5G  5G  {  H   7H   78   H8   H    3   3   4 7  4   H  H      2   W2   J  uJ  R #/;GS_kw5/A/~//5~95~95~9*5~9;5~9G5~9H5~9T5~9`5~9l5~9x5~95~9017>7>&&''&&''#"&&677#"&&6771'.766'.766'&>7%'&>77'&&6632'&&6632  H7   H7 b5G  5G    H   H   78   8   H K x  3   3   4   4   HI  H   3   32   2   J  IJ  R #/;GS_kwY/e/EX/ >YY9Y9Y9*Y96Y9BY9SY9_Y9kY9lY9xY9Y9Y9Y9Y9Y9Y9Y901%7>7>7>&&''&&''&&''#"&&677#"&&677#"&&6771'.766'.766'.766'&>7'&>7%'&>7%'&&6632'&&6632'&&6632  H7   H7   H7 5G  5G  5G     H   H    H   78   8   8        3   3   3   4 7  4   4   H  HI  H   3      32   W2   2   J  uJ  J  Fn #/;GG6//696969*69069B6901&'&>7#"&&6771'.766&&''''&&66327>H  !s ^     E]!   ]G ,B   D!!^     D R_!! C   F #/;GS_kw/~/~9~9~9*~90~9B~9H~9_~9`~9r~9x~9~901%&'&>7#"&&6771'.766&&''''&&66327>&'&>7#"&&6771'.766&&''''&&66327>H  !s ^     E]!   ]G H  !s ^     E]!   ]G GB   D!!^     D R_!! C  \B   D!!^     D R_!! C   jn~ #/;GS_kw6/~//Y/696969*69069B69H69_69`69r69x696901&'&>7#"&&6771'.766&&''''&&66327>&'&>7#"&&6771'.766&&''''&&66327>H  !s ^     E]!   ]G H  !s ^     E]!   ]G ,B   D!!^     D R_!! C  mB   D!!^     D R_!! C  j~ #/;GS_kw/Y//999*909B9H9_9`9r9x9999999901%&'&>7#"&&6771'.766&&''''&&66327>&'&>7#"&&6771'.766&&''&'&>7'&&6632#"&&6777>'.766&&''''&&66327>H  !s ^     E]!   ]G H  !s ^     E]! vH  !;   ]G ^     E]!   ]G GB   D!!^     D R_!! C  mB   D!!^     D B   R_!! !!^ C  \     D R_!! C  @ #/;GG//999*909<901'&&6632#"&&6?7>&&''''.766&'&>7     %0f&&" #&&e-$0$ "%&fe&&" $0,1*+12++2*\"!]*v)!\\!ba+ и / / 9 9 9  9 9 901%'#'7'737!!M+и ////// +9999 ик901%'#5'7#53'75373#!}~qq~}!~tt~}#}}#cY +A&6FVfv ]A]+01#".54>32c((((1((((B| [ // 9 9 9 9 9 9 9 901%!''7  98ONzcӲXӱP@}  //01'% !#qqLM,"nuQB{ //01%!% zuONgzPB| //и/и/ и и  и//+ +999901%!7!''7  9e8ONzcӲcӱP{U//9901U=~$tu{U//990155U=~,O&$] 3//999 901d/Y{__] 3/ / 9 9 9  901'7'7/4,BB`4t<ǻ+<;+;и/<9A&6FVfv ]A]9<9;=кc<9<9<9<9<9<9<2++#++и/и/ܸи/и#и/#и/2-и-/:и:/=и=/OиO/c#9и/99и/и/и/и/01%#5#"&'&&'&67"&'&7&&5463'&#'&&'.54>7>327535&''&&'.7>766&&'&&'#&&'276676%"67665#%6&'&##"#23326&&'&"76'4&'&&266766W XJ;?:' ,2,1 3($1G\f0$KID$UU #;?9*     .$'" 09  '   Xk$8N8>6@Fa/2'( *<  H  I%0 ,J#  **& "       $         $ /   (   6   /      4t<'W(+W&и&/W*3++ + + и/ и/ܸи/и/%и%/+и+/38и8/EиE/S 9WиW/m9r9 xи |и|/ 9и/и/и/ии/01"2#"&'&&'##367>32&'&&'&&'&&7647'3&4'&67&'&&7667&&767&&7663'"&'&54>7667&&7667&&766%4&'&&#2>7665#%4&'"&##"3326366&'&"676676&'&6366(3 1,2, ':?;JX WU$DIK$0f\G1$  *9?;# +8F,   '  90 "&$.   M>8N8.3:#@62J '( <* H %I ; #J, '**      " $  C        / $    ;   ;   7  "    B| [//9999 9 99901%!'''7  98ONzcӲӱP@}  //01'7'7'% ervv!#qqLM,tttt"nuQh //и/ܺ9к9 к9EX/ >Y+ии01! !!!7$DSV%56RC`26h/EX/ >Y01 !!PZt.Zh f  +++ + EX/ >Y +++ 01%!!!!!!zgVT [{l // и /  9к 9 к 9  ++и 01!!!!;@_ >AmA/{l ++ܺ9к 9  9 к9+++ к 9к901%!!!!!Ƈ@_ >º:AmA/h + +++ ++EX/ >Y+ + +++01%5#!!!!!!!!j ,O5 PW,5  W oy';Os2F+ ++<(+A]A)9IYiy ]A & 6 F V f v ]A ]A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-++#+014.#"32>7#".54>324.#"32>7#".54>32}&21&&12&H!9L++L9!!9L++L9!r3XuCCuX33XuCCuX3B=jQQj==jQQj=-2&&21&&1+L9!!9L++L9!!9L+CuX33XuCCuX33XuCQj==jQQj==joy 3G[o'Rf+*>++ +4 +\H+A]A)9IYiy ]A&6FVfv ]A]A ]A ) 9 I Y i y ]A*&*6*F*V*f*v******* ]A**]AHH]AH)H9HIHYHiHyHHHHHHH ]AR&R6RFRVRfRvRRRRRRR ]ARR]\qWa+kM+/9+C%+ ++014&#"3267#".54>324.#"32>7#".54>324.#"32>7#".54>32H1##11##1$!++!!++!a";O--O;"";O--O;"$(E\44\E((E\44\E(f8`IJ`88`JI`8"=jQQj==jQQj=-#11##11#+!!++!!+-O;"";O--O;"";O-4\E((E\44\E((E\4I`88`IJ`88`JQj==jQQj==j:r 7I8//8,и,/A]A)9IYiy ] , 9 , 9 , 9,A&6FVfv ]A], 9, 9, 9, 9, 9, 9 9'+3 ++ к901732>7%%4.'667#".54>32K[j99j[KSl>>lSd 9Qbq=\xE 9Qbq=[xFa/L66L/eBoS0Z)SoB)Z0=qbQ9 Ex\=qbQ9 Fx:r 45//5)и)/A]A)9IYiy ])9 )9 )9)A&6FVfv ]A])9)9)96$+0 + 01732>7%4.'667#".54>32K[j99j[KSl> >lSd 9Qbq=\xE 9Qbq=[xFa/L66L/eBoS0Z)SoB)Z0=qbQ9 Ex\=qbQ9 Fxru //01%#!#35!3u  c /// /01%''7'77cvoy';˸324.#"32>7#".54>32(E\44\E((E\44\E(f8`IJ`88`JI`8"=jQQj==jQQj=-4\E((E\44\E((E\4I`88`IJ`88`JQj==jQQj==j:r+˸,//A]A)9IYiy ], и / A & 6 F V f v ]A ]-+'+014.#"32>7#".54>32AoTToAAoTToA! 9Qbq=\xE 9Qbq=[xF,ToAAoTToAAoT=qbQ9 Ex\=qbQ9 Fx:r+˸,//A]A)9IYiy ], и / A & 6 F V f v ]A ]-+'+014.#"32>7#".54>32u=hOPh==hPOh=9 9Qbq=\xE 9Qbq=[xF,Oh==hOPh==hP=qbQ9 Ex\=qbQ9 FxAz?//и/ ++01!!!s2fS3dAy  / /01'7'7'7'7'7MNMh  / /01%'7''7%'7'7<G>:vL=eC++A&6FVfv ]A]и/A&6FVfv ]A]F9O9Wкu9999и/++и/fиf/99901&'.'"'&4667667&&'&&7666766%&&'.'667.'&&>.'.'&&'&&'667>766766.''667667&&'>7>'4&#"326hX$@4' cI-A2( /2 '4C,%:0)fIXj -  ?%N) +"2"1' &3"-l3 ;51  :51 %N) +"2"1'HD-l7)VWU'9T>, L9-RNK&    #  L9-RNK&    #(WWU'9T>,$##$v 0 '3B,HdeIQi   1 #3E0I^# '3?%LoK9-RNK&     )VWU'9T>, (VWV'9T>,  K9-RNK&    $  :51%O) +"2!1(GE-kN %N) +"2"1(HD-l3 ;50t$$%%4m )6DR_m{#/&/)/01'6677677'667667&##"'6632&'&&'7&&'&&'7&6'4&77'67767'667667&&'&&'7'&''&&'7&&'&&'7'667667&#"'77627'667667'&&'&''7'66'6&'7       H  :   N a      N             s  6   l #   :       "   <  8    O      $    ~  5k !%)-39=AEIMQUY]agϺ+++++&'+45+ и иииии"и#и*и+и.и.//и//1и1/4?ܸ7и7/:и:/;и4>иBиB/CиC/4Fи?Gи>IиJиKиK/4Nи?Oи>QиRиR/SиS/4Vи?Wи>Yи&Zи'[и[/4^и?_и>aи4bиb/?cиc/5eиe/>gиg/4i/////&/4/EX$/$>YEXX/X>Y3.+  +-*+++и/48и/ и/4ии ии ии  и$"'и (и315и 6и.:и3<и2=и >и @и.Bи3Dи2EиFиHи.Jи3Lи2MиNиPи.Rи3Tи2Uи"VиWи.Zи3\и2]и*^и-`и1bи.dи3fи2g01#533#53'#53#53%#53#53#53#53#53#53#537##53#5353#53#53#53#53#53#53#53#53'#53#537#5#53xB#KK5KKJJ?LJJII+"#B:B#MJJKK(eKKJJJJ#AB#gIKzJJJc$A# IKJ{JJ?$F9//и/ܸ ++01!!!=\d ZF+9,//,и/ ܸ+-+&+014&#!"3!2>53#!".54>3!2!,~", *"~* +$!* k0!0 "+ *!"+ + Ay #' / /01'7'7'7'7'7'''''MmmmmmmmmmmmmmmmmNMmmmllnmmmllnmmmh  //01%'''''7''7%'7'7ammmwmmmQmmmQmmm<<GmmmSmmmOmmmummmy>:v V<[y-T/,/%a+01%&7667>7>&'6.'.'&>.'.656676&#"&'&32>7>64'&&'&6767>'&5&&'&&'&5>7>''.'&7676676.'&7676'&&'.'.676#".'&&46763232>7632.'&>7>    $  &&!     "(*!K          !&&  !*(" !"   #+30&$4<@8*  *  .7;.$M /  !! &03,#  L *8@<4$$.;8-        #& k         &$ ?   $(++($     A'5:80" . )/0        &'! "08:4'  1.) .:A8..8A:.    !''   V$Hn //01'&5&&'&&'&5>7>''.'&7676676.'&7676'&&'.'.676#".'&&46763232>7632.'&>7> !"   #+30&$4<@8*  *  .7;.$M /  !! &03,#  L *8@<4$$.;8-   '5:80" . )/0        &'! "08:4'  1.) .:A8..8A:.    !''   _++01%!5!5!5!**^d^UA///9999901!>,XXXXoy';OcɻFZ+2+ +(+P<+A&6FVfv ]A]A]A)9IYiy ]A&6FVfv ]A]A<<]A<)<9<I<Y<i<y<<<<<<< ]AF&F6FFFVFfFvFFFFFFF ]AFF]PeKU+_A+#-+7++01#".54>324.#"32>7#".54>324.#"32>7#".54>32`(''(m";O--O;"";O--O;"$(E\44\E((E\44\E(f8`IJ`88`JI`8"=jQQj==jQQj=-''((-O;"";O--O;"";O-4\E((E\44\E((E\4I`88`IJ`88`JQj==jQQj==j :r &+39=G[seWc+oM+cW9 cW9,cW91cW94cW97cW9:cW9BcW9017&'7&''&&'&&'''&&'&'##73766776765'.#"32>#".54>327yC # )  PM  ^   n%%Kq[[g9?[g9B[g9E[g9I[g9J[g9N[g9S[g901&##%&'&'&&'&'&'&'&&'766774&'33767677#".54>32 l>`F)#"? [ f  a L  'Bg#>)"# # 9Qbq=\xE 9Qbq=[xF_  c? $d           !iC 0 "%"=qbQ9 Ex\=qbQ9 Fx:r '1;SH++< +A&6FVfv ]A]A ]A ) 9 I Y i y ]A&6FVfv ]A]H<99H<9#9(и-и 2и7и<UC+O7+-+и к-9-97(и-201.'>'.'>>.'#".54>32@jO@jPj@Oj@c8bO::Pb87bP::PbPi@Oj@AjO@jq 9Qbq=\xE 9Qbq=[xFmOj@Oj@@jO@ja:Ob77aO::Ob77bP:@jO@jOj@Pj@=qbQ9 Ex\=qbQ9 FxC{';Ot/\/\t9 \t9\t9\t9(\t92\t9<\t9F\t9V\t9b\t9n\t9z\t901>7>.'.'.'.'>7>.''>7'7.'7>7'?95*#+F<5)  $)49@'  )5:F  )5:F, $)49@+F<5)  '?95*#<+$$MHABJM##*+%$MIABJM##*,  )5:F, $)49@+F<5)  '?95*#u'?95*#+F<5) y $)49@'  )5:FBJM""*,$$MIAAKM#$*,$$MIAj~ //и/ܺ9999 9 9ܺ999999 ++01!%%!%!vv&"lz,v&v"l0j~ [/ / 9 9 9 9 9 9 9 901!%%!vv&,v&vk}'3#//#9 #9#9#901'7.'>#&&'56673O>kW?>Wl==lW>?Wkxz(r(zz(r(z,CYl< 9+<+и и и ииии #и 'и +и /и 3и 701##3####7#5375375353535353!!/ / /hk/..S$/B//o0-*I/0/a|0L(G123'12'2Hz04C04' 2k1d!C| #/;GS_kw˸//и/$ܸmܺ0m91и1/$SиS/m`и`/mxиx/mи/$9и/$9#y+Zy#9#[и[/y#9y#9yи/01677>7'&&7>&'.6.'%6666&&'&%.%>&&'&>7%'.67%.7>.'''&&6676&''%>766! "%# +! %&" t!,  !,  d  &&" +! o "%# !U//( )J *4 & ( )./ " 7* "8* )./>)X *4 &  )//( !,  %&" q +!s! "%# V! "%# Q+!  &&" `  !, "8*( )./ *4 & &//( )>(//( ) &  *4>) )./L 7* "h  //01'%''7''7%'7'7}mmm5mmm<<-mmmmmmmy>:vl7{" e++ +  ++// ////// //017#3#3#3#3#3;;;;;;;;;;7Fgmqx{/*/c+ + и/и 7и;и<иCиcJиOиcVи[и hиlиmи nиpиqиrиyи |и}и~кc9012##32####"&'###"&'#"&554633&&'#"&554633&&'&63323667663323667663323667#37##%#''#3&&  Ys  {  K| L|  rU  K ' ## ){zh f3x96 '   v  *5@R&S@5)%  <#  .W0Y 0[ ="vvd$vv32U / / /+ +и  01!%!!!ݑ1ݑ=ݑ> HEXXXXd++ +и и//EX / >YEX/ >Y+и/ и ии01!!#!!5!!3!!V5\&5uJ^,1^qnz 4g!+A]A)9IYiy ]!6&/0+014.#"32>7>32667#".54>32[9bI>o[B 'B- /*,*18 .@& =jQPj>>jPQj=+Ja9)Hb:"I=( .!*#(=J!)Qj==jQQj==jI#/CS;x//T/f/ Tx9Tx98Tx9DTx901%&'&'66766&&'.'64'&&2'.'&&'67666766&&'.''667>7&7.'&&'6>7667 ..007@t/56600/- 650t98G'/t@8/0.. ?t056  /[5/70X6,,6X A661..166A X6,,6X A661..166A  '0t?7/5[?t0' /[506H89..0/8@t/' ;705Z0 65/t A661--166A X6,,6X A661..166A X6,,6nz 4_++A&6FVfv ]A]0/&+01%6.#"#".732>7#".54>32N &B- /*,*18 .@& 9bJ=p\B;>jPQj==jQPj>"J=' .!*#(=J!)Jb8)HbQj==jQQj==jnz 4g!+A]A)9IYiy ]!60/&+014.'&'32>7#".54>32[)Hb:!J=( .!*#(=J!)Ja9=jQQj==jQQj=+=p[B 'B- /*,*18 .@& 9bJQj==jQPj>>jD}A_}"+Q+ ++6`+6A ]A ) 9 I Y i y ] 9"и/69, 9<69 BA&6FVfv ]A]LиL/A&6FVfv ]A]SQ9^69A``]A`)`9`I`Y`i`y``````` ]jкl69A]A)9IYiy ]w9 ~и~/69QкQ9`к9696+'G++o+Q+кQ9'1к<Q9^o9Geкlo9Qyк9ик901%#"&&'&'#".5466767&'.54>32>324.#"327&&54>326%4.#"663232>4'#".5467&#"32>%4.#"#"&'32>%4&#"326";N-+L::L+-N;"5##(("#5";N-*K9%$:K*-N;"5$"(("$5<4G((G44G(%""34G((G4 0 #* ")(G4Q 0 "* #((G44G((G44G(%#"34G((G4- -- -U-N;"7%#))#%7";N-*K:9K*-N;"5G))G5";N-*K9:K(G44G((G4 0 #* ")(G44G(%#"34Gz%""34G((G44G((G4 0 "* #((G44G -- --K+Qw+GA+^,+&R+&иA,,]A,),9,I,Y,i,y,,,,,,, ]A&6FVfv ]A]6AG&G6GFGVGfGvGGGGGGG ]AGG]=AG9KAG9ARR]AR)R9RIRYRiRyRRRRRRR ]^eܸ^lиGиAиGиRк^e9eи^к^e9G&+1++D=+ и!и=Kи1Wк^=D9Dbи=hкl=D9йs={к99и01%#"&'#".5467&&54>3266324.#"6767&'&5463266%4.#"67632#"'&'32>&&#"3267&'&'#"&546326764&'#"&54767&'&'32>(F]41W""W15\F(""(E]52V#"V14]F("##&6676676&''%7"'.'&6266767'&&767'&667'462767676%'&&76676767'4667676726&'&66&'&6%'&66=3> L1 CKU11G2 $% #+1&>,@21RH? *@ !"'*)4A'(2 .""=2(   %  %7  :  4ON4U," .9C&!J,!  '#A91'9" %/*54" )./*+'  J  &%#%j  &%#$ k  oy';Ocwc2+Z(+nP+d+A2&262F2V2f2v2222222 ]A22]APP]AP)P9PIPYPiPyPPPPPPP ]AZ&Z6ZFZVZfZvZZZZZZZ ]AZZ]Add]Ad)d9dIdYdidyddddddd ]#+}+_+U+01%664&'&&"2634.#"32>4.#"32>7664&'&&"264.#"32>74.#"32>4.#"32>664&'&&"26#".54>32u  "%#   #%"""""""""5  "%#   #%",+:!!:++:!!:+""""""""  "%#   #%"=jQQj==jQQj=( #%"  "%#  """"%"""" #%"  "%#  q!:++:!!:++:!""""%"""" #%"  "%#  qQj==jQQj==j9r //01#3#".54>32)@@|Fx\\xFFx\\xFX97#>/\xFFx\\xFFx9r%9++5+01%5!667>54.#"34>32#".54>32`C3'0A'.F.4 1"+8M.!<.A96K% 8?K2\xFFx\\xFFx9r6JK//A]A)9IYiy ]KAиA/7ܺA79A-ܸ7L2<+F ++01%4&'6654&#"3>32##32#".5#32>7#".54>32>9)8_R*D03"0>>!*--2%T?5'45I+'H7!Fx\\xFFx\\xFAL D3HS4H* 7(>0*0/@E(:%2K2,C\xFFx\\xFFx9r !//9901%#5##!35%#".54>32ՅR3 3_Fx\\xFFx\\xF>1j1\xFFx\\xFFx9r(<q)+A]A)9IYiy ])>$.+8 ++01%4&#"735!36632#".'#32>7#".54>32e[$C$6/B)!6&&5"2&43E)*H6Fx\\xFFx\\xF`p1&"*:"!<-$3 *D06J\xFFx\\xFFx9r0Di';+A'&'6'F'V'f'v''''''' ]A''],6+@"++01%#".546324&#"4>323&&#"32>7#".54>32#3!!3$K?>L4hV6O&:&6A3UR-K74L0+G1Fx\\xFFx\\xF9,+9GTRH]i:*9cI*?/DW+NoEWyK!5K\xFFx\\xFFx9r" ++01!!3>7#".54>32;'N@,<"8P3Fx\\xFFx\\xF?2)n\Oz1\xFFx\\xFFx9r#G[\/RиR/A]A]A]A0]ܸ$A$]A$]A$]A0$])999R>ܸ$Hܸ]CM+W1+!+01%#".54>32#".546324.'6654.#"32>7#".54>32'55'(55(!--!A99AM +(5-A))A-6'* 5I++I5Fx\\xFFx\\xF 3""3 1!!1))->>3*D0 7))7 0G *3,C--C\xFFx\\xFFx9r0Dq1+A]A)9IYiy ]1F.6+@+'+01#"&54>324.#"3267#"&'#3267#".54>32%4>J#3 !4$A4M0+E1fV:O%9'5D4[OdhFx\\xFFx\\xF#;*WG 9,+9UvJ 6J-_k=+>dG&=/FS\xFFx\\xFFx9r0Dͻ'+ +1+A ]A ) 9 I Y i y ]A]A)9IYiy ]1F,6+@"+,и/01#".54>32#34.#"32>7#".54>32- - - -);@w)A--A))A--A)Fx\\xFFx\\xF.5_F))F_55]F))F]L86"G)EoN++NpEEoO++OpD\xFFx\\xFFx9r%G&/ /&и/ܸ ܸ'+!+ и 01%#3!#3#".54>32)@@|X)@@|-Fx\\xFFx\\xFL76#GL76#G)\xFFx\\xFFx9r.B9++&+/+A]A)9IYiy ] и //D.4+>+.и/01%#3%5!667>54.#"34>32#".54>32_&:9oU</$+<#)?+1 ,6=30)B2Fx\\xFFx\\xFL76#G1-X.*1:$'>*7L-!:->95H$8=K1%\xFFx\\xFFx9r9MD+0++:+: ܺ 9A]A)9IYiy ])ܸ:O5?+I+#+5и/01%#3%4&'6654&#"36632##32#"&7#32>7#".54>32\'3:j93%3VJ&>+0=887%((4FJ98I10B'$A1Fx\\xFFx\\xFL86"G?K C1GR3F*?S;0'1>32'3:jI//Fx\\xFFx\\xF7 L86"G2s2\xFFx\\xFFx9r-A8+$+. +A ]A ) 9 I Y i y ]8.9.C)3+=+ +01%#3%4&#"735!36632#"&'#32>7#".54>32c'3:j]Q ;!1-:%%&A0Fx\\xFFx\\xFL86"G]p0% VC!:,G?*B/6I\xFFx\\xFFx9r;OF+2+<+A]A)9IYiy ]!F<9<Q7A+K-+&+)и)/01%#".54632#3%4.#"4>323&&#"32>7#".54>32.- B98C{'49j-@&0G"3"190MJ(D2/D,'?-Fx\\xFFx\\xF8+*7ERPL86"G-H39*8`G)=-CU*MnCVvI!5J\xFFx\\xFFx9r++++' +и 01#3!!3>7#".54>32k'3:j#H<*85I.Fx\\xFFx\\xFL86"GL2(nZNz0\xFFx\\xFFx9r(H\ 'S+? ++I/+A]A)9IYiy ]ܸI)ܺ,9A//]A/)/9/I/Y/i/y/////// ] 9<9I^DN+X4++D и /01%#"&54>32'#".54632#3%4&'6654.#"32>7#".54>32J88J#//#'(:33:'3:j<.#1(;%%;(1#.<0B''B0Fx\\xFFx\\xF?CC?0!!0''-::L86"G8U B/7''7/E U8*B--B\xFFx\\xFFx9r7KB+#+8+A]A)9IYiy ])B898M5=+G+&.+5и/01#"&54>32#34.#"3267#"&'#3267#".54>32D97B-/{'49j0E+&?-]M5F !2$0<1SGZ_Fx\\xFFx\\xFEWUE8**7NL76#G*TtH 5I,]j=*=aE%;-ER\xFFx\\xFFx9r7K_9V+%+B+ +L8+A ]A ) 9 I Y i y ]A]A)9IYiy ].и./0и0/A88]A8)898I8Y8i8y8888888 ]LaGQ+[+=01#".54>32%4&#"34>32!5#667>4.#"32>7#".54>32 )( () M;#5$- $-1+("6)D3'P&9%%9&&9%%9&wFx\\xFFx\\xF83[D((D[33[D''D[SMS6J+9,=63G#6+&и&/01%5!667>54.#"34>32#3#".54>32$S<."*;"(>)0 *6;30(@0&.5^Fx\\xFFx\\xF0,U-(08#&<)6I+9+<73G#5и>/H]M+W+'и=014&#"34>32!5#667>%4&#"34>32!5#667>#".54>32P<#6&. %/1*)#9*"F4(BP<#6&. %/1*)#8*#E4(Fx\\xFFx\\xFMS6J+9,=63E#654.#"34>32%4&'6654&#"36632##32#"&7#32>7#".54>32E3(&2#6%- $/1))#8*{2,/L? 5&-100+"",9<10<-)9!8+qFx\\xFFx\\xF/,T,(09#&<)6I+9+<73E#6G B0EO2D(+ +1+4++1к19>49и и.и./+2и4I9+C+1и1/01%#5#667>54.#"34>32%5##3357#".54>32ժJ6*!(6%9'0 '05,+$<,xA.-Fx\\xFFx\\xF-/,T,(0:#&;)5J+9+<64E#632!5#667>4&#"735#36632#"&'#32>7#".54>32Q=#7%. %/2+)#9*'G4( GRG1)*0 )(-;.)6 !9*|Fx\\xFFx\\xFMS5J+9+<73F"6(A-4G\xFFx\\xFFx9r3Vjea+!+M+<+W4+A]A)9IYiy ])и)/*и*/,A44]A4)494I4Y4i4y4444444 ]A<<]A<)<9<I<Y<i<y<<<<<<< ]WlR\+fH+A9+Hи/9и/Aи/01%#".54632%4&#"34>32!5#667>4.#"4>323&&#"32>7#".54>32(& 8009P=#7&/ %/2+)#9+&G4(W)7!*: ,*..D@#<+);&"7(nFx\\xFFx\\xF6))5COMMS6J+9,=63F"6=H00,S-)09,F18(6]E(:-AS)KjASsG 3G\xFFx\\xFFx8i//01%&&"267664&'"&'&&467>'343343i.')?A?:><-343343~9<>:?A?-&$b -+/ ++01!#!5!5!5!`$#c*XW9r 34/*и*/A]A] A ]A ]*  5%+/+ ++01!#!5!5!5!4.#"32>7#".54>32 FZXk@nTTn@@nTTn@$Fx\\xFFx\\xFBAATn@@nTTn@@nT\xFFx\\xFFxH'˸(//A]A)9IYiy ](и/ A & 6 F V f v ]A ])+#+014.#"32>7#".54>32'CZ33ZC''CZ33ZC'%-Mh:;gM--Mg;:hM-,3ZC''CZ33ZC''CZ3;gL--Lg;:hM--MhH'M/+01%!(3jb",,?//и/ ++01%!!!FOF#7r  /+01%!!% k\znzC~Vx j 3 // и/ / /01%'%%%u=O?//и/ ++01%!!!/Rrp#7O?//и/ ++01%!!!Wzrp#OC++01%!!!\z\nbnrp#@C/+01%%![x\obncAOp#Z@C/+01%!%\x[nbncO#ZAC //01%%  NM,Oa + + ++++ +и 01%##5#!3353ͤrp#J a + ++++ ++и 01%5#5##!35!3››m#6y +01%#!"&767  %  l1u /+01!!"&76332'H " BCQ ! E+/+01%!3! # !$'*!//+01#".'&&'332>7667'!,3217;;71'!,7#".54>325$/$/,:cKKc::cKKc:%?mSSm??mSSm?,%-Kc::cKKc::cKSm??mSSm??mb+ո,//A]A)9IYiy ],"и"/A&6FVfv ]A]-+' ++01!5!4.#"32>7#".54>325~,:cKKc::cKKc:%?mSSm??mSSm?%Kc::cKKc::cKSm??mSSm??mb 3˸4/ /A ]A ) 9 I Y i y ]4*и*/A&6FVfv ]A]  5%+/+01''7'774.#"32>7#".54>32ח:cKKc::cKKc:%?mSSm??mSSm?Kc::cKKc::cKSm??mSSm??mb /CD/:и:/A]A]ииA]A]:&0E+5+?!+ ++ +01%#"&54632%!5!%#"&546324.#"32>7#".54>32+    ~   6:cKKc::cKKc:%?mSSm??mSSm?i % Kc::cKKc::cKSm??mSSm??mS  /+01.#"66328AI%&HA8?ZZ? :=(66(@bS/ /++ии к  9к 901%#'!5!'!5!'7!!!@^hgOnwmO Ţ###@bm//+ + +иии к9 ик901%#'!5!'!5!'#53'7!!!!3@:DaD5DHQC+01"666&'&'667667#"&546767&&'&&766767>766766$9 &) 1)=#!(L,    86-=O   ,H&# ?1" BG9%6M!" / #.E2";X$ $<1  % 0.#P - +/+ + 01%!5!5!5!3!#_$.##P\'+//+01#!5!3$*$#.#P3+и /+01%!5!5'7537!#$.#Дؖ@s+и ииии//+ + +иии и и01%!#5!5!5!5!5!5!53!!!!!@#999#<<7##'7#53!5!732ebj1J2&{r!i#!r{+Xmx3H,j}^ U##Z Q{m +A&6FVfv ]A] // ++ и ии01#"33!'7#"&5463373#!Cbe2J1=!r{{r!jmX+H3#^ U}jl{Z Q#xm &o+ и ///&++ +&ииии !и#01!33#"!!'7#"&5463373#3#! 2H/i\d!r{{r!/1(C0aO^ U}jl{Z Q##m $w +и/ &//+" + +иии и и"013267'&&####'7#537#537!5!7321]ee\08{r!10$!r{dP#Paj}^ U###Z Q{*^ / /+01%!5!7*l0 %&:*C // +01'%!5! 6l:%%%b  / /++01!5!5!5! 7%bb?#N#mc%M D//EX/>Y  +9901'%!5!5!5!1bbc.#N#JM/'/EX4/4>YEXG/G>Y>+'и/> иGA]A(8HXhx ]49и!и,и>.и;и>L0132>7%.#"##"&'73267!#".'#53>32&&#"!667>323C:0' V!,5H G,;"2#15E "0B+8-GG-8 3'7F4$/9#";+G5?!)%+ <9% 8*C45/':&%%9' ?44(*7Z^? /+ܸи/ 9 9901&&#"&&'#>32.s?At/9  -J]l;:l]J$*,&@[)'0R:! :Q1f`  + и /EX/>Y++ и01%!5!5!5!'##5#5353344&&*]*櫫% 3jat~+3>+%0+"+>39>39%и и /%(и(/35и5/B~90FкL~90MиM/P~9%Zкg~9tvиv/~9~и/tи/tи/~9"и%и"и%и0и3и0и3/+8+m+++ܸи/8*и*/B9EиGиG/LиL/[и[/gm9sm9m9кm9иии01! 37'&'&&'&475##"&76655##"&766554&'35#"&''34&'337##"&'&&'76655&7667#"''354&'376675#75#5#75#7#"&'&&'&&hm=     A 1  2G _  Q       2RhF"!    AAA111   2R)  -48  IA   ?I    )$ # f    }%l  /_#%  * %%  P77J,,J77J,,  us1JRZ^sy +LO+[\+nk+UV+L,и,/ܸOи/OL9O'к8kn9kBиB/nDиD/UXкbkn9uVU9u/tA&6FVfv ]A]иuиLи/и/zи/t/U/[/)(++ZS+RK+yv+++и/ܸиZ}и}/ ܸи/U9(+к8U9KMܸKOиyPкbU9)lиl/MtиKxиRи/01##535&&546323267.''667#5!#.'&''66553##5#5!##33#3%&&''66553#5#534&#"326#53#53'#539 z"*3''3)!:'-('/ 9Mv t(0  -6 !!PAAL 4!,7 !\#!!# +9ss51%&&$13 =2) !&'# lAMK!=3' 䕕v<! Q6$#.& y zn1JRZ^syK +EB+z+UV+nk+LO+tu+Eи/A&6FVfv ]A]Vи/OL9O'иt*и*/L,и,/8BE9UXиU\и\/YиY/U[bkn9A&6FVfv ]A]tиuиzи/и/t]/xU+)(++ }+ZS+RK++и/иKи/Kи/и/9(+иRMܸKOиM[иUtиxvܸRи/(и/)и/01##535&&546323267%.''667#5!#.'&''66553##5#5!##33'#3&&''66553#5#534&#"326#53#53'#539 z"*3''3)!:-('/ 9Mv t(0  -6 !!P{@@# 4!,7 !\#!!# )9ss1%&&$1x =2) !&'# lAMK!=3' 畕re<! Q6$#.& y+V:r$8PI/E+  +!"++9%+E99 и  ܸи кE99A%%]A%)%9%I%Y%i%y%%%%%%% ]A/&/6/F/V/f/v/////// ]A//]9R4@+L*+$+  ++$9$9 9013#3##667#'665##34.#"32>7#".54>32Dg+*(/c%/(  +AoTToAAoTToA! 9Qbq=\xE 9Qbq=[xF**W:5 }RToAAoTToAAoT=qbQ9 Ex\=qbQ9 Fx4m$2@N[iwۺ m++ +  99 ܸии иииmpиp/ H/K/N/ +++9 9 901%#'665##33#3##667''6677677'667667&##"'6632&'&&'7&&'&&'7&6'4&77'67767'667667&&'&&'7'&''&&'7&&'&&'7'667667&#"'77627'667667'&&'&''7'66'6&'7(  +Dg+*(/c%       H  :   N a      N             5 }R**W%  6   l #   :       "   <  8    O      $    ~  I q// и /и/ и/и/и/ //+и01#53#53#3#3TTTT&L&LNZZZm33 3ݻ +)*+++и/ и/9/A]A)9IYiy ]5+/$+ии/ и //)01#53#53#3#4>76654.#"#4>32 TT]YY&L.&#; )&&2';'=7R5(H6 NZZZm37T&47<#-JA;C'1!0B'5W="0E  //01%'7'7''VQJQOZ%6OBPZ%6O)WWXWW (V ){m+O?#$+++GH++,?+A&6FVfv ]A]9/797/6A??]A?)?9?I?Y?i?y??????? ],Q+)+ии)#ܸBи#Gи)M01#53#53#4>76654&#"#4>32#4>76654&#"#4>32_YYYY ;!66$1 =.H2NZ ;!66$1 =.H2NZNZZZ/*(46;#-I@; B'=D0B'5W="`X/*(46;#-I@; B'=D0B'5W="`9q //01'% EE,EE:r //01 {yyFF,yyF:r //01 ]iFF,iiF:r //01 FF,F>;//и/++01%!!!&vj8iP//и/ EX/ >Y+01!!!E^&^$iP//и/ EX/ >Y+01!!!/v:i/EX/ >Y01!!iJ#5'˸(//A]A)9IYiy ](и/ A & 6 F V f v ]A ])+#+014.#"32>7#".54>32!8L++L8!!8L++L8!9*H`77`H**H`77`H*,+L8!!8L++L8!!8L+7`H**H`77`H**H`X'˸(//A]A)9IYiy ](и/ A & 6 F V f v ]A ])+#+014.#"32>7#".54>32i:eNNe::eNNe:'ApUUpAApUUpA,Ne::eNNe::eNUpAApUUpAApX'˸(//A]A)9IYiy ](и/ A & 6 F V f v ]A ])+#+014.#"32>7#".54>32R7^II^77^II^7>ApUUpAApUUpA,I^77^II^77^IUpAApUUpAApX //01#".54>32ApUUpAApUUpA,UpAApUUpAApoyT/+01!  z|Xjoy_/+01%!5X@j)=+//999901X*+  | )E+//999901% Xj"|;#//9901.'.'>7>7;&:/)'; ")/:&&:/)" ;')/:**+/4oM&A:4/++++/4:A&Mo4/+*0h //01#".54>3202VsAAsV22VsAAsV2,AsV22VsAAsV22VsT //01%!!PP</+01%! !28UeKR}\ //01 hh+66R<[[o+'(++\g+ \9A&6FVfv ]A]\9\9 \9(5и'7и';и;/>\9@кM\9NиPиTкX\9b\9Agg]Ag)g9gIgYgigyggggggg ]j\9\q /j//a/RS+@+ 9 9&@9)@9S2и2/R4иR8иS:и:/>@9SAиA/RMи@UиWкX 901.54>73%&&'#'667#&&'#'>7#53533#535#'6673533#3#7#>54&'3-  -(:..:7.@ 6P;,5\!(& 5 :"--'}5c_)P !)&60 :6O1j ,)( /:), *ixDCxi*ywwy2m388l6**tF$<{)k.JSW'22"]*,}76y?11~1B{+Dxi*73#'667#".'57&&'732>77#>54&'3''7-  -(:..:B 54"Jz)<6, <&)   ,)( /:), )X*ixDCxi*ywwy%0j(3'Da0#1it3Fm5.q;2Pc8"Dxi*YEX/ >Y ++014&##3267#####3332ME^^EM@f^lBBB^fJHHJak\4k9r9MaDX++4#++N:+A]A)9IYiy ]A4&464F4V4f4v4444444 ]A44]A::]A:):9:I:Y:i:y::::::: ]AD&D6DFDVDfDvDDDDDDD ]ADD]NcIS+]?+ +(/++7++и+/01%4&#"32>##56673#".54>32#&&#"663274.#"32>7#".54>32?66>+,/d61%+=%)A-/@&GI-6/ 1 D-I[@nTTn@@nTTn@$Fx\\xFFx\\xFCLNB5((5 35*F2FqQ@hI(Q?+9'C\5(6cTn@@nTTn@@nT\xFFx\\xFFx9r+?"6+++,+6,9A]A)9IYiy ]A"&"6"F"V"f"v""""""" ]A""],A'1+;+++01%##56673#>7!5!4.#"32>7#".54>32q0e71&,E26(9D"<@nTTn@@nTTn@$Fx\\xFFx\\xF  35..tJVi&/Tn@@nTTn@@nT\xFFx\\xFFx9r $FZnQe++/+A+[G+AA&6FVfv ]A]A]A)9IYiy ]7ܹ%4e[9De[9AGG]AG)G9GIGYGiGyGGGGGGG ]AQ&Q6QFQVQfQvQQQQQQQ ]AQQ][pV`+jL+ *+<+ ++*и/49D901%4.#"3264&#"32>##56673#".54>7&&54>3274.#"32>7#".54>32!-3IF65F7017>*%/e70%.>%%>.%!.'8##8'.",9@nTTn@@nTTn@$Fx\\xFFx\\xF.><++>(0' A-4%%4-> QBTn@@nTTn@@nT\xFFx\\xFFx9r9MaDX++ 0+(+N:+A & 6 F V f v ]A ]A((]A()(9(I(Y(i(y((((((( ]A::]A:):9:I:Y:i:y::::::: ]AD&D6DFDVDfDvDDDDDDD ]ADD]NcIS+]?+%+5++ ++и/014.#"32>##56673#"&'33265#".54>324.#"32>7#".54>32-+?4,/e62%ZVCO.:-C< C2$<++<$)B.u@nTTn@@nTTn@$Fx\\xFFx\\xF4((5BP'6"44퇙NA+8t(:2E,*E3EnQTn@@nTTn@@nT\xFFx\\xFFx9r7K_sVj+/0+'+ B+8+`L+A]A)9IYiy ]A&6FVfv ]A]/и/и/ABB]AB)B9BIBYBiByBBBBBBB ]ALL]AL)L9LILYLiLyLLLLLLL ]AV&V6VFVVVfVvVVVVVVV ]AVV]`u[e+oQ+=+5*+*и/=5G014.#"32>%3!>76654&#"#&>32#".54>324.#"32>7#".54>32 &' '& %0A&4 &).+" +#3!8I?$6##6%%6##6$d@nTTn@@nTTn@$Fx\\xFFx\\xF81V@%%@V11W@&&@W!6-&*P*/.E93!C13:)6)F3O?hI((Ig?@gI((IgJTn@@nTTn@@nT\xFFx\\xFFx9r%.BVa9M++ +&'+C/+и/A ]A ) 9 I Y i y ]$A//]A/)/9/I/Y/i/y/////// ]A9&969F9V9f9v9999999 ]A99]CX>H+R4+%+ +*)+&и&/01%!>76654&#"#&>32!##566734.#"32>7#".54>32".=&-183) -';&!7(!+8P/Y2,$@nTTn@@nTTn@$Fx\\xFFx\\xF--D92!D149)6)E3'9$!6-&+R)0 23Tn@@nTTn@@nT\xFFx\\xFFx9r#G[oRf+++?@+$7+\H+A&6FVfv ]A]и и / и /?,и$-и-/@/и//A77]A7)797I7Y7i7y7777777 ]AHH]AH)H9HIHYHiHyHHHHHHH ]AR&R6RFRVRfRvRRRRRRR ]ARR]\qWa+kM+  +!+!ܸ ,и .и:и?и!E013!>76654&#"#&>323!>76654&#"#&>324.#"32>7#".54>32&2B(6!''.-# ,$4!9L2&2B(5!'(.-# ,$4!9Lp@nTTn@@nTTn@$Fx\\xFFx\\xF!7-'*P*--E93!B13:)6)F3OI!7-'*P*--E94"A13:)6)F3OTn@@nTTn@@nT\xFFx\\xFFx9r%Th|_s++ +1G+O@+iU+A&6FVfv ]A]ии/$и$/O7ܹ&A@@]A@)@9@I@Y@i@y@@@@@@@ ]Rsi9AUU]AU)U9UIUYUiUyUUUUUUU ]A_&_6_F_V_f_v_______ ]A__]i~dn+xZ+%++LC+=:+C и /Lи/+4R:=901%!>76654&#"#4>323%#".7332654&##532654&#"#4>3274.#"32>7#".54>32(6!&(/-" +#3!0$&0BG(6 6'*:-/86* 2)--/+$2R++ +2+H4+A&6FVfv ]A]ии/'и2*и,и.и./A44]A4)494I4Y4i4y4444444 ]A>&>6>F>V>f>v>>>>>>> ]A>>]H]CM+W9+(++3)+3и+и+/)-01%!>76654&#"#4>323%##5#53374.#"32>7#".54>32̢**8#)*2.% .%6#3&)3F4>*+>W@nTTn@@nTTn@$Fx\\xFFx\\xF-D93!B149)6)F3'8$"6.&*P*T-nTTn@@nTTn@@nT\xFFx\\xFFx9r#H\pϻSg+++/.+$7+]I+A&6FVfv ]A]и и /A77]A7)797I7Y7i7y7777777 ]Cg]9AII]AI)I9IIIYIiIyIIIIIII ]AS&S6SFSVSfSvSSSSSSS ]ASS]]rXb+lN+ )+?+F:+Fи/)2?A013!>76654&#"#4>32#".'332>54&#"#3#663274.#"32>7#".54>32&2C(7!'(/-# +#4!:M7(64',8+'80/('ٵ.DNg@nTTn@@nTTn@$Fx\\xFFx\\xF!6.&*O*--E93!B059)6*E3O'D1+>&;@(6>O"-fTn@@nTTn@@nT\xFFx\\xFFx9r3Vj~%au++,+#+O>+4+kW+A]A)9IYiy ]A&6FVfv ]A]+ии/,и/A>>]A>)>9>I>Y>i>y>>>>>>> ]AWW]AW)W9WIWYWiWyWWWWWWW ]Aa&a6aFaVafavaaaaaaa ]Aaa]kfp+z\+9+CJ+R+9 J&и&/R+и+/C1и1/01%4&#"32>%3!>76654&#"#4>32#".54>32#&&#"663274.#"32>7#".54>326..5 %& &1D(7!')/-# -$4":LE%5 $8'*9!=@,,') 8'5&Z@nTTn@@nTTn@$Fx\\xFFx\\xF@JK@3''3!6.&*O*..E93!A13:)6)F3O)C1DmO>dG'O=*7%BY3&5.B,Tn@@nTTn@@nT\xFFx\\xFFx9r#2FZs=Q++ +)*+G3+A&6FVfv ]A]ии/"к/QG9A33]A3)393I3Y3i3y3333333 ]A=&=6=F=V=f=v======= ]A==]G\BL+V8+#+1 +)и)/1/01%!>76654&#"#4>323#>7#5!4.#"32>7#".54>32(6!'(/-# ,#4!9L&2A,&:)2#2:n@nTTn@@nTTn@$Fx\\xFFx\\xF-E93!B059)6*E3OI!6.&*O*,qHS~f%/Tn@@nTTn@@nT\xFFx\\xFFx9r =auWl+34+++H+Z +vb+ZA ]A ) 9 I Y i y ]PܹA&6FVfv ]A]3 и /4#и#/>AHH]AH)H9HIHYHiHyHHHHHHH ]Mv9]v9Abb]Ab)b9bIbYbibybbbbbbb ]Al&l6lFlVlflvlllllll ]All]vq{+g+ C+!"+U.++.и/U9и9/M9]01%4&#"3264&#"326%3!>76654&#"#&>32#".54>7&&54>32274.#"32>7#".54>32 ?--?=//=.,+/4&%5(3D)7#)*2.$ ,$6#2%F)8!!8)!,#2 2#+"T@nTTn@@nTTn@$Fx\\xFFx\\xF;;;;;<<@)55)033$!6.&*O*..E94"@13:)6)F3'9'<**<'/& @,2$$2,=)3UTn@@nTTn@@nT\xFFx\\xFFx9r5Th|_s++,+#+ K+6E+iU+AEE]AE)E9EIEYEiEyEEEEEEE ]Eи/A&6FVfv ]A]+и/и/,и/AKK]AK)K9KIKYKiKyKKKKKKK ]AUU]AU)U9UIUYUiUyUUUUUUU ]A_&_6_F_V_f_v_______ ]A__]i~dn+xZ+@9+P+ H+&иP1и1/014.#"326%3!>76654&#"#4>32#"&'332>5#"&54>324.#"32>7#".54>32 &% 4//7'3D)7"((0-# -$4"2%DQK#".7332654&##532>54&#"#&>32%#".54>324.#"32>7#".54>32 () )( +8!8*.<01<8-""-0/2,%5 @L. ,2E&9%'9&&9'%9&O@nTTn@@nTTn@$Fx[\xFFx\[xF-1U?%%?U11U?$$?UD)<).D-CJ>:79- %+8N9&A/LA.? D:?fH''He?@gH''Hg?Tn@@nTTn@@nT\xFFx\\xFFx2C//+01'7!5!'7Y,2C//+01!'7!Y 84+/EX/>Y9901'#'7VC 9 4+/EX/>Y9901%'737C?D w +A&6FVfv ]A] 9//9901'6654&'67'&'  >39C:32503:B93?  ?D +A]A)9IYiy ]и///9 901&&'&67'&'77667 3523:C93> :30m  ?39B?M+A]A)9IYiy ]9//9901%&&'&'667&&7667 >39C:3253 cB93?  m03:?My+A&6FVfv ]A]и// / 9 901676&&6654&'7523:C93>  3l  ?39B:302/& // + +01%!'7!!!}|OO|{NN2N;/ /++ иии01'!!7'7!'7!5!7!5!7!'7O*rv}20*T1/#|+NN|{2NE//++и к 9и01!!!'7!'7!7!!!OOHL20}|]1/z~*yNN|{2/&#/// / ++01'!!7'7!'7!'7OiOOv}}|]|+NNNN||{{2// +01667667!5!&&'&&';%D+  +D%;+-#- ( -#-2//+01!&&'&&'667667! +D%;;%D+  -#--#- J8J+/EX/>Y99 9901&&'&&'#667667- ( -#--# P +D%;;%DJ; J+/EX/>Y9 99901%&&'&&'366766#--#- ( -F+D%;;%D+ P 21/,///& +01667667!&&'&&'667667!&&'&&';%D+  +D%;;%D+ Y +D%;+-#- -#--#- -#-J8 1r,+/EX/>Y9 9999%9+9,901%&&'&&'667667&&'&&'66766#--#- -#--#- -C+D%;;%D+  +D%;;%D+ B 2//9901%!!uġ쬉2//9901'7!!'7ġ,TH8 ,/EX/>Y9901'!'ġPuH9!,/EX/>Y9901%7!7TTuġPF /+ / / 9 901%#55!!D\~]ZF '+/ / 9 901#3!!1\-[RZG}  /+01 3!5!!P]Z$DY\G} / +01#5!#!!P\Z2q\F~/+ // 9 901%#55%#".54>323DuYYuDDuYYuDKYuDDuYYuDDuF~'+ // 9 901#3#".54>32DuYYuDDuYYuD-JJYuDDuYYuDDuF~/ +01 3!5%#".54>32?KDuYYuDDuYYuD#3 YuDDuYYuDDuF~ /+01#5!##".54>32?KDuYYuDDuYYuD3,YuDDuYYuDDu2e)#!&'&&546766!2&3~044)--)o440~3&+'6"W+01D33D10+W$52e)&&'&&6677!".54>3!'.67>-)440~3&&3~044o)-+3D10+W"6'#5$W+01D28),&/EX/>Y &9&901&&''#".5&'.67663200*V#5#&6"V*00 D22DY440~3&&3d~044o*,,*2: ),/EX/>Y9901#"&'&&667664>327>D22D00*V"6#V*00p*,,*o440~d3&&3~0443f'QR//A]A)9IYiy ]R=и=/(ܺ=(9=A&6FVfv ]A]"=(9.=(9L=(9(S#-+M+014&#!7>&'.66766&&''!2>7##&'&&54676632F3ڟ *-,$))$b,-* &- (7T577*//*b775T7(+=>n %)) <--<))% n /!):$9/46 G44G 54/9'93f'QR//A]A)9IYiy ]R=и=/(ܺ=(9=A&6FVfv ]A]=(97=(9C=(9(S7+C+014&'&&!"3!67667&&'&&6677#".54>33'.67>)$,-* 3F ,& *-,b$)/*775T7((7T577b*/+-< ))% n>=!/ n %))<-4G 64/9$:)%9'9/45 G1:'QR//R>и>/ܸ4ܸ>KиK/4QиQ/EX9/9>YN+9A'7GWgw ]A]01&&#"6677326567>&'7&&''#".5&'.676632w<--<)(% n /!==n $)) 44/9&9%)9$9/45 F44F $))$,-) s, F3 )-,774S7((71S477`*..*19'QR/ /R:и:/ܸ Fܸ(и(/:.и./EX+/+>Y@++$A$'$7$G$W$g$w$$$$$$$ ]A$$]0166&&'&&4&#"'.3267#"&'&&667664>327>} ))$ n==!/ n %()<--< F44F 54/9$9)%9&9/44,-) 3F -s )-,$))$*..*`774S17((7S477Lg '5AM˻ +++$+92+H?+HBB9B9A & 6 F V f v ]A ]A&6FVfv ]A]A$$]A$)$9$I$Y$i$y$$$$$$$ ]A22]A2)292I2Y2i2y2222222 ]A??]A?)?9?I?Y?i?y??????? ]HO//KE+01&&54>77&&54677&&54677&&54677#"&54632# -,$+ **;>=  !!!!g&HLS0e;31{_0L@6,vXXv,+&dEEd&%[??[%+#A00A#@''@'0/"*)"L!!!!Lg %3CM+ ++&-+4;+A&6FVfv ]A]A & 6 F V f v ]A ]A&6FVfv ]A]A&&&6&F&V&f&v&&&&&&& ]A&&]A;;]A;);9;I;Y;i;y;;;;;;; ]E;494HMH9M/E/ +01#"&54632'654'7'6654&'7'6654&'7'6654&'76654.'!!!!e  dfg+$** I,- #-!!!!0")*"/0A#@''@#A0Ed&%[??[%&dE_{1,vXXv,6@L0;e0SLH&3P//+901!.'.'>7>7! $Zel77mdX##Xdm77leZ$ t3"    "3t3P/ / + 901'75!5!5'7#Xdm77leZ$  $Zel77mdX,  "3t&t3"  2?&'.&&'.'>7>>7663Vs@BdM;1+=AJ-+Z>=DI"Do[II[oD"ID=>Z+-JA=+1;MdB@sV3,::  %3B)% 56ZL>>LZ65 %)B3%  ;2;&'&>7>7&&&&546667.'.766I[oD#HD=>Y,-JA< #@Zc@sV33Vs@cZ@# =DH#Do[I,>LZ65 %)B3%   :::;   %3B)% 56ZL>U8;,2/EX/>Y 292901%.'.'#"&&67>&''&&667>7b)B3%   ;:::   %3B)% 56ZL>>LZ65 >Y,-JA< #_~c@sV33Vs@c~_# =DH#Do[II[oD#HD=U< ; /(/ (92 (901.'.67666&&'&&6632>7>b% 56ZL>>LZ65 %)B3%   :::;   %3B=DI"Do[II[oD"ID=>Z+-JA="`~c@sV33Vs@c~`"=AJ-+Z>2 //+ +01%!!5!!Yl|4YY2  //+ +01%'!5%!5!7lY|4%%I8 a / / и/ܸ  / /EX/>YEX / >Y9901#'#YlYjI:  a // и/ܸ //EX/>YEX/>Y9 901733%%ljY2 //01%!!2 //015!!5-|Sp8w ,/EX/>Y9901#!#whp8w ,/EX/>Y9901%3!w}|Sh2//+01%!!&^fe2//+01!5!^-  8Y 4+/EX/>Y9901!#!Yf>&8Y 4+/EX/>Y9901%!3Y ^&>e} %+//+901!'7!3e{{e8(+/EX/>Y+015!#!5Q{&{U+/+01!#7#!={{dT:,+ EX/>Y+01'3!5!{UDf82+/EX/>Y+901#!'7! {{e~ +//+01%5!3!5d{'{U9$+EX/>Y+01!3'3!W{{U+ /+01#!5!#7{={W` &/&+/ /+9 901#.'>72>7>5&=-i !" EKGJLD ! Bx3,<$ Zv6~ '-,/0, -1/-+% =[;3vSW8&<+"/EX/>Y + 9"901>7"#4>76$3.'JLD ! Bx3,<$&=-i !" EKGN,1/-+% =[;3vRZv6~ '-,/0,U(g +A&6FVfv ]A] 9/(+01".'.5>7.'3H{k*9J, '-,/0, -1/-+% *G5(hwE}0D,:Y !" EKGJLD ! S7)C/U9(|$+A]A)9IYiy ]$9$*EX/>Y+01%.'4.'.#52>/0,,1/-+% *G5(hwEH{k*9J, '-,@EKGJLD ! R7)C/0E+;X !"W8&D&+&(/EX/>Y +9 9014.'.#.'>72v$<,3xB ! DLJGKE "! i-=&Rv3;[= %+-/1,,0/,-' ~6vZW` &/+// + 9"901%>7"$'.533.'GKE "! i-=&$<,3xB ! DLJ ,0/,-' ~6vZSv3;[= %+-/1-U9(t +A & 6 F V f v ]A ] 9EX/>Y(+01">7.'4>7>3Ewh(5G* %+-/1- ,0/,-' ,J9*k{H/C)7R ! DLJGKE "! X;+E0U(o+A]A)9IYiy ]$9*$/+01.'#52>7>5>7,-' ,J9*k{HEwh(5G* %+-/1,,0/ "! Y:,D0/C)7S ! DLJGKEJn m//и/иܸ ܸк 9 / /+ + 9 901%!55!!)6)|;{{Ln M//ܸи и /ܸ к 9/ / + +01!!5!!5r,`,+m{{kC6L K// и /ܸ ܸ / ++и 01 3!#!#`{Ͱ6;;|6U  K// и /ܸ ܸ/ ++и 01%#!# 3!`{{kAC;R|;3/+01!667667!~ZI T14 B,! (+3/+015!&&'&&'3 41T IZ+( !,B38+и/EX/>Y+9999 к 9 9 9к9999017!7'!'7!7'!'IIIIII+II{{II> / / ܸии/иܸ к 9 к 9 //// ++ 9 901!55!5!!86+YWWP w +/ / 9 9 9 9 9 9  9  9  9  901%%u**X++,== ( P + / / 9 9 9 9 9 9 9 9 9 901%7'%%|*6*+x+;Ơ3{ /+015!3Dr1#;4/CBc&+" 3{  / +01!>7667!~/4;#1rD "+&cBK9 ;+ // 9 9 9  901%%5%%5b2he+"O"K9 C+ //999 901%%%}beh" "M׸//ии/99 9и/9ик9EX/>YEX/>Y ++ +ии и01'#'!#3!7375#!7#3'!35qii{{hMhmhmhihhhn+./fcfM//и/99 к 9и/к9к9EX/>YEX/>Y +++ии и01%#7!#553!'#!'#37!3y{iihMhihmhmnhhh%./ff2p %/ /+ + 901%!'7!7!5!'IIII{:8: / /и/  ܺ 9/ /EX/>YEX/>Y99999 9 9 901'#7'3飣iIzIϵIzk| J  + EX / >Y  ++ и 01%#'!#3#53$GQQGJeen6h^ '+ܻ+" +()+"A ]A ) 9 I Y i y ]и%(9(-EX/ >Y*)++)й %901%4&##32>4##32>#!!2#53c_W%C1(:'\"A\9$.Q<#?9COeeHG"6o#.4L1)D4:U _hvq)- +*++A&6FVfv ]A]*/$+-*++01%#".54>32#.#"32>7#53.Jd=NxQ))QxN2_K3B '8E&D`>=aD0M8#ee@hJ(9dOOd9;V8)A,6Yq:;pY6"Y+й014.##32>7##32#53u DjKKjD B홦ee=OqI""IqOʱhO h +  + и EX/ >Y ++ и 01!!!!!!#53r'pxee(:::h5 ^+  +и EX/ >Y  ++ 01!!!#!#53bKBeeh:6hl|,0 +,)+-.+)и/A&6FVfv ]A]-2$+/.++,)+.и$01#'#".54>32#.#"32>5#5!#531x^LwS,,SwL6aK4 B!9O1Da>>aE9Y<"ee(EO6cSRc6YEX / >Y + + и 01#!#3!3#53BeBBBee(\46h:++EX/ >Y+01#3#53IBBee(6h@V+ ++EX/ >Y++ 01%#"&7332653#535eag`BGEYEX / >Y + и 01##33#53P}BB}Uee(r~|\hH H++ EX/ >Y+й01!3!#53^BB|ee(p:he ++ +  9 EX/ >YEX / >Y+иик 901###33#53BBBbbee(llr6hh e++  + EX/ >YEX/ >Y  + и 01##33#53KlBKBee(]]6hk}'+ӻ ++()+A]A)9IYiy ]A & 6 F V f v ]A ](-++(+#+014.#"32>7#".54>32#53&C^79]C%%B^97^C&B2WtCDuV11VuDCtW2ee=EtS..StEEtS..TsEZ_01_ZZ^11^AhL+ ++A]A)9IYiy ]иEX/ >Y+ +014&##3267###!2#53XUEEUBp^B0.L6eeJHHJak4Lhj~.2׻ %++/0+A]A)9IYiy ]A & 6 F V f v ]A ]/4/ +2/+*+014.#"3267'7667'#".54>32#53&C^79]C%%B^9$AQ&U',B6/Z%_NcDuV11VuDCtW2ee=EtS..StEEtS.K)P*zK^0S*Y41_ZZ^11^Aha *.+%++,+A]A)9IYiy ]и/и +9*+9+0EX/ >Y-,++, и,к 9014&##32>&&'.###!23#53QKK7)  6/B9+L:"FA#.  eeBM#3 B%J;$.H0F_,424/hf:>Ż  +'(+;<+('9/A]A)9IYiy ] 9/0;@ +>;+"-+"(01%#".7332>54''.54>32#.#"#53%Db<;eJ)B%>Q-%A29+^?6J-$5#c$''9*'I7"9S4'=+O@$+ -=h\ X+ + EX/ >Y  + ии01###5!#53Beehp:6hc_  +++EX / >YEX/ >Y++01%#".5332>53#53@gHHfAB4K00K5Bee9cJ**Id9F>W77W>6hi S+ EX/ >YEX/ >Y +к 901#3#53NGee66hQ v + EX/ >YEX/ >YEX / >Y+ик9 901##33#53fPPEYvee6}qq6hxp Q  + EX/ >YEX / >Y + и 01##33#53OONNee(7n\#h{l g+  + 99 EX/ >YEX/ >Y  + 01#3#53BJee](b6hd V  + EX/ >Y  + и 9 01!5!5!!#53^9ee(<T:<:hD>B+8'+?@+'A&6FVfv ]A]'и/.9./->?9?D +<+A@+3*+@A93-ܺ>@A9?01%32>5#"&5#".54>76654&#"#>32327#539W:>4%?-u  *]M#=.)Id:%&I:5M= 6H+%C2 ee  ) --*=&0*.;"5$2=& :19?,A+&@28hD(,++)*+A]A)9IYiy ] и к!)9).EX/ >Y++*+A]A(8HXhx ]*+9*и$01%4.#"32>7#"&'#36632#534'?+/C**C1+?'=7U9''>O((O>''>O(7cK,:7b3>,KchJ%)+&'+A&6FVfv ]A]&+%+)&+ + ܸ 01%#".54>32#&&#"32>7#53XlX8W;;V8(G6$= N;+@+-?)3'/ee[c(Hd=:dI*/C*8H#Y++*+'A]A(8HXhx ]*и*+9#01%4.#"32>#5#".54>323#53'*C/,>''>,1C*==b<9U77U99a=ee(O>''>O((O>''>Ob7:,Kc77cK,>3(6hK (,m +(+)*+  и(и/).+,)+#+  +01%.#"32>73#".54>32#53*;%%9)*<'!6(=n[2S; 7T8YEX/ >YEX/>YEX/>Y+A]A(8HXhx ]  и  и01&&#"3###5354>32#53  %]]=UU + Ceeg0*65159"5$:h9O8< .+#+9:+#A & 6 F V f v ]A ]#6и9>EX7/7>YEX/>Y )+7A]A(8HXhx ]ܸ9и9/и/ A ' 7 G W g w ]A ]$7939:и:/9<01%4.#"32>#".'332655#".54>3253#53'=*(=*TQ(<(=5T;(H7$>Q9WI*54T: Y+иA]A(8HXhx ] и01#4.#"#3>32#53?=1#$:)==!,33F,ee(B!9*/A&&5I-h  m++и/ и /EX/>YEX / >Y+и 01#3#53#53H==eek??(he9 ++и/ и/EX/>YEX/ >YEX/>YEX/>Y+  и01#"&'5332653#53#53s,9  =eez??K<@4#-he> +  + 9к 9  9 EX/ >YEX / >Y + и 01##3%3#53ZIe==Tee(\Bh :++EX/ >Y+01#3#53G==ee(6hd*.+ +++,+к+9$ 9+0EX/>Y-,+,иA]A(8HXhx ], ии,и'!01#4.#"#4.#"#366326632#53= * "0 > %&4!==H;8G GY+и A]A(8HXhx ] и01#4&#"#3>32#539=?B#9)== -43F+ ee(CHS/A&Y'4J-hO'+ӻ ++()+A]A)9IYiy ]A & 6 F V f v ]A ](-++(+#+01%4.#"32>7#".54>32#534.A''A..A''A.=!=X66X=!!=X66X=!ee1Q: :Q11Q: :Q1YEX/>Y.++!A]A(8HXhx ]A'7GWgw ]A]#!9&01%4.#"32>7#".'#36632#536+=%*B-*C/%=+= ;S38." ==[D3S; ee-P<#"YEX/>Y.++)A]A(8HXhx ]A'7GWgw ]A]+.9%01%4.#"32>##".54>3253#53%-A*&<++<&/C*== ".83S; ;S3D[=ee.P<"#YEX/>Y+и и /01&#3>#53&A0==!.;"-ee2F(v/"h?59++$%+67+%$9/A]A)9IYiy ]A+&+6+F+V+f+v+++++++ ]A++]6; +96+(+%01%#".'332654.''.54632#&&#"#53>6I+*I6"=Q>=K(O2'\Z&D3=F9:=%R3'eed);%-E/?B11 . BM*>(6;1&  /hw++ ииܸи /EX / >YEX/>Y+и/  ииии/01#".5#53533#327#53* XX=ffIee. ,!f55!0hC +++иEX / >YEX/>Y++и901#5#"&5332>53#53;=WB\W=AE$8'=ee([/;c`PIE5G(hC S+ EX/>YEX/>Y +к 901#3#53^DB(ee>he  + 9 EX/>YEX/>YEX / >Y+ик9 901##33#53$F{{FAzFzeeNFFhM Q  + EX/>YEX / >Y + и 01#'#'373#53bFFIIee(h9:{+EX/>YEX/>YEX/>YEX/>Y+ к901#"&'532>773#53h51Aee,/53OhA V +  EX/>Y  + и 9 01!5!5!!#53++"#+F(+#к 9 /и"и/#и и# и# ܸ"%иFH4;+C++++ +' ++01%5!#5!#3!53'#!#2>77##3374&#!"3!2657#!"5463!2~m^]]^t'T_l?:!IHDX]]XJ0<;11;:;1CIʌDH6HD*HHT==Y DJ  h`09633672CE:?IECM&6E/=++E'+=E99=E9и/EG3:+B*+++йи/!01%!#5!5!'.'&&'>7#5!#74&#!"3!2657#!"5463!2(^_ "F?42>G% 4U=%'?T0<;11;:;1CIʌDH6HDOw && &.NN.&09633672CE:?IECM +;Jɺ4B++ + #+J,+ 9/ и!и%и#'и)иJL8?+G/+ + +)&++%"+ +01%4&#"3265##"&54632'!!#5#535#53534&#!"3!2657#!"5463!2KIIJJIIKӃ~ts~~st~9^\\\\^0<;11;:;1CIʌDH6HD^''%%)327!5!'#53#5#535#53534&#!"3!2657#!"5463!2EIIEEIIE'%%''%%'%uuuuuuuu)<''<((<''<) }W ^____^}0<;11;:;1CIʌDH6HDQ !!!!9FE89ED)!!+*!!+wDDW,H6H-09633672CE:?IECM #2*+ ++ +2+ии24 '+/++ + +015!!#5#5!'!3!534&#!"3!2657#!"5463!2__C'^]0<;11;:;1CIʌDH6HDIIO@"DD(09633672CE:?IECM$,<K5C++%(+K-+(%99CK9%&*иKM9@+H0+++*'+01!53!'.'&&'>75#5!##5#53534&#!"3!2657#!"5463!2(^z-+%R0 8,`0:]vv]0<;11;:;1CIʌDH6HD$ '+*Q" %3?#&LL%#<0!L009633672CE:?IECM/7GVݺ@N+ +03+V8+0ии0и и к309$ 9$/+$+9NV91и5иVXDK+S;++'&+ +52+&)01%5!#5!#3!53'.'&&'>55#5!##5#53534&#!"3!2657#!"5463!2x]]]]& #&()+( $<,]+9]]0<;11;:;1CIʌDH6HD3??X9:c%)-#% (4= LL :0#UK+09633672CE:?IECM(8G1?++G)+?G99?G9!?G9GI5<+D,++и01%!#5#5!'.'&&'>766774&#!"3!2657#!"5463!2-__! "F?54AH$ 'OF6 \ 6FP|0<;11;:;1CIʌDH6HDN *%-.$)*C1 !9*09633672CE:?IECM '7F0>++ +F(+!и!/FH4;+C++ + ++"+'+'и$01%4&#"3267#"&546327!535#5!!!#!4&#!"3!2657#!"5463!2NHINNIHN^rsss:Y0<;11;:;1CIʌDH6HD\##!!GG`L4KVJ409633672CE:?IECM .m&+++  +.+и и.0#++++ +01%5#!3353#34&#!"3!2657#!"5463!2ѝYY]]0<;11;:;1CIʌDH6HDH妧.09633672CE:?IECM )8"0+ + ++8+ 9/ ииܸи8:&-+5++++ + и/015#!53!'!3353##3374&#!"3!2657#!"5463!2Ȕ)]zYYW^^WJ0<;11;:;1CIʌDH6HDHbbOaaƛ09633672CE:?IECM (8G1?++#$+G)+?G99?G9$!ܸ#&иGI5<+D,++(!+01%.'&&'>55#5!#%##3374&#!"3!2657#!"5463!2\ +(!(. -=&Y'<^]]^40<;11;:;1CIʌDH6HD $/57;71"OZ`*:MM;*[UJ}09633672CE:?IECM'6 .+ ++6+ иܸи68$++3+ ++  ++01%'!#3#677##3374&#!"3!2657#!"5463!2hCɁDH=`]]`C0<;11;:;1CIʌDH6HD~J|Ke09633672CE:?IECM !1@*8+ ++@"+и/ии/и/8@9ܸи@B.5+=%++ + +!+01%5!#5!#!&&'>7#5##3374&#!"3!2657#!"5463!2h]]ų .WF3 LX]]XG0<;11;:;1CIʌDH6HD3qqW w3(!3E,Lӎ{u09633672CE:?IECM '3CR7<J++++R4+A&6FVfv ]A]A&6FVfv ]A](ܸ)и+и-и(/и1иRT@G+O7+#+/0++3(+ +014&#"326#5!5!'#".54>32##33#374&#!"3!2657#!"5463!2/(&0  (/ ^ͳ.?'&@..@&'?. W^^WWWV0<;11;:;1CIʌDH6HD&88&#:AK%>..>%$>-->RwNK@09633672CE:?IECM+/?N8F+"++,-+N0+A]A)9IYiy ]A&6FVfv ]A]и/NP<C+K3++'+ +014&#"32>!53!#".54>32#34&#!"3!2657#!"5463!20+! !" /^s/A('B..B'(A/]]0<;11;:;1CIʌDH6HD'<$$$Qߓv%?//?%%?..?)09633672CE:?IECM#'3;KZϺDR+0++:5+Z<+RZ9RZ9RZ9+.ܸи/58ܸZ\O+W?+-.+;4+'$+и/4+и+/;0и0/-6и.8иOH01%.'&&'>76677!5!%53#667#53#374&#!"3!2657#!"5463!2  !C>4/9> .J7% Y&=TS_6zCz$G  鏘0<;11;:;1CIʌDH6HD+%$*)#"%0  ,)LD Ga bGf09633672CE:?IECM+;J4B++()+%+J,+и кBJ9JL8?+G/++&+  + +&и/ и/013#3##667#4&'3265##!4&#!"3!2657#!"5463!2  W-QN.P!A7( # fQ p0<;11;:;1CIʌDH6HD* HM?FV$2- J{09633672CE:?IECM 8<L[WES+++7+[=+к:9:/9ܸи/:и/9и/: и / ܸиии/#79/7972и2/[]IP+X@+8+<9++ +# ++ ++и/8и#/и 1и3и501%5#7#535#537#53!#5##335#535#53.'73#3#3#534&#!"3!2657#!"5463!2Q>iO>Oܣ 0 '~]0<;11;:;1CIʌDH6HD9ZZD$DG"JK  3  1KJH'09633672CE:?IECM15ET/>L+&+++23+T6+A&6FVfv ]A]A&6FVfv ]A]и/и//иTVBI+Q9+ !++++/+014.#"32>5##5##".54>32353#34&#!"3!2657#!"5463!2CWK <9&6##6&6= KWZZ0<;11;:;1CIʌDH6HDN'C0aR(F53Fy ?H'Ea;;aE&G>n)09633672CE:?IECM)3+cl+UV+~a++Uк9 lc99Vи/V'9-9Aa~9VYܺIVY9MVY9PVY9SVY9flc9i9anи~qкs9{999YиVи++A+na++WV+A9A9Aи/"'A9-A9IA9PVW9WSܺYVW9iVW9nrиn{иa}кVW9VW9015#&&'667##"&55332>7&&'77'&&'&&'7&&'667##53667.5#&&'6655!53&&'73#>77667%#53%4&#!"3!2657#!"5463!29J '% <40&^ s' & +9(%$  8"$   eA  $ *#6V<B  9 H' (o0<;11;:;1CIʌDH6HD 77 #$! E&W)/ +n9 B*8"  $B 7;:]Qx/&lBC&   " J"I $&"  )O!  $9i09633672CE:?IECM-R^n}_gu++! +0?+ST+}_+u}9u}9+TS99u}9?Iи?Mи0PиVиV/}kr+zb+%+^U+<6+XY+O/+.и/ к%9и/и/+6<9<9и9//Jи.KиYLиL//RMиYQиQ/^Sܸ[и[/01%##".55#&&'>5#5!#33267667##4&'3255&&'75#53537#5!3#5#5!4&#!"3!2657#!"5463!28 5$&" "!.1  #+2+(  ;  hhP29U@@jQ0<;11;:;1CIʌDH6HDV">`N>"/;P:MM  /$* 8'gEY:]3E09633672CE:?IECMdl+ij+ef++9'fe9*ji909:9=9Kfe9N9Q9[9n9q9{9~9ji9fиiк99+ +le+g+LM+(+и/*(90и0/LQܺ:LQ9=LQ9MWиeiиi/MmкnLQ9Qqиq/Mwк~LQ9Lиl01%#!"5463!2'4&#!"3!265.''!5&&'>7&&'>73#&&'#&'#5##5!#"&&'#&&'>735#7&&'CIʌDH6HD0<;11;:;1P 9"3`N5    F W $ " ;JU7WTW '   M{2 CE:?IEC09633672(1 #% //<"))%  E!( )!6%/"-.*   JJ) M>HXg}Q_+  +3++gI+_g9_g9_g9+(и(/<_g9B_g9giU\+dL+01#&&'>7'.'&&''7&&77667&&'774&#!"3!2657#!"5463!2  T #7(+$&# f kW , */&310<;11;:;1CIʌDH6HDu (14)T 3Q]^$2 (2:*j9L /a<  G0F5[#=D% )//6!09633672CE:?IEC M .26aqjx+0+/+Q+\+b+и и ии0и/x9$и$/-/97x9@x9Ax9M/9Tx9Zx9nu+}e+V3+@+2++/+  +и/@и/-@9V5и5/@Pи2RиR/3TиT/3XиX/2ZиZ/@\015#5#75#75#'7.'77>77'#537!5!.'7#&&'667#37#5!#3#4&#!"3!2657#!"5463!2zSllllloG C, I7[# \ ;#54Ge1vz0H0<;11;:;1CIʌDH6HDHgg77p33l331 [ %&# P *+&  / +G !    2# #3FF3e <09633672CE:?IECMi+C+u=+o+hd+-++(dh9h/иC?и?/=Aи=Eи=IкR9u`иulиupиorиr/w9dиuи=ииuи=ии+ +RJ+A>+IF+B+h/+m++mDиJMиM/J`и`/Iaиhcи/jиFkиBoиAqи>sииии01%#!"5463!2'4&#!"3!265'#4&'325#&''75#535#535#535#&&'&'2>7&33535#3#3#7667667>555##5#75##5#CIʌDH6HD0<;11;:;1X;' ( +<." ttrr~~4RQD *=Qttqqy*K(*K(CE:?IEC09633672I%9.ZEJH1b# $ Q%DD   7"'M0D  6:<9""""R MSWcĺ+#$+ ++-9?9K9R9 Tи#Vкc9eEX!/!>Y+ +KX+SU+%US9-US9!T01%#!"5463!2'4&#!"3!265#5##5&&'>7.'.'>737!5#>7CIʌDH6HD0<;11;:;1xWW "(>>;1  +SH9E  E<@DW & )'$ CE:?IEC09633672+  .  /7> P Dp"xx 2 !#M #2*++  +2+ ии ии и и24 '+/++ +++01%5!%5!%5!#5!#!4&#!"3!2657#!"5463!2}eUS0<;11;:;1CIʌDH6HDiaabbaa;::k 09633672CE:?IECM=M\FT+ +\>+T\9T\9T\9) 90T\9\^JQ+YA++21+1016677!.'>55!&&'&&'667&&'7#5!74&#!"3!2657#!"5463!2."R2: #  1_--16I^;'=? -&241u0<;11;:;1CIʌDH6HD8GP+{W@WtM N*(&! +B f<D>1s3& 09633672CE:?IECMz1+?@+01+,-+#++, к;9D9@OиO/?Rкc91dи-fи-jи,lиl/,nкo#9y#9,|ии,ии,и-и0и-и0и+ +?+.+z++!+?&и&/+?9?0и0/;?9D?9LиL/ MиM/zNиN/lܸPиP/zSиS/c!9d!9!eиgи hиziиznкol9.{иииии01%#!"5463!2'4&#!"3!265#3#&'&&'#5##&&'#&&'>7#53537>7535#5353&&'73#266=#75#5#75#CIʌDH6HD0<;11;:;1M43 O0M     N  2%KU A %}SR * ' (l1 111O000CE:?IEC09633672,0+  0s   &  .7=?JmN /+ &?,FkH 1 'fX33o22o33o22M :JYCQ+ +Y;+QY9QY9QY9 икQY9$QY9'QY9/и 1и1/ 3к6QY9Y[GN+V>++/,+к9к9$9/3и,501%5!.'.'##5#5&&'>7#5353#4&#!"3!2657#!"5463!2  0    \  2S>(_4HV}0<;11;:;1CIʌDH6HD|#  #%"   76 -AIHIdIKMD09633672CE:?IEC M #*?O^{HV++ ++()+/>+^@+иик9/и и к9/ и /$V^9+V^98)(9^`LS+[C+;++# + )++++и)и/ и) и / ик$ #95и5/;8и8/01%5#75#5#75#75##5##!'!5!7!5!#7#4&'32654&#!"3!2657#!"5463!2^>>>===ORc!((zlR<) * 0<;11;:;1CIʌDH6HD6;;x99x;;x99??HZ!E L4$:. K>09633672CE:?IECM*AQ`ԺJX+-:++`B+X`9X`99X`9(X`92:-95X`9`bEX!/!>YNU+]E+A++)+и)и!A;и+>01%'7##6&'76655#53&&'77#5!7377!&&'>&53534&#!"3!2657#!"5463!29I)`=()  4"*\9:TWM( aW0<;11;:;1CIʌDH6HDd'=$3, H 42@@ Q W :YLE'"RVO:!09633672CE:?IEC M Wcslz+10+<+ +L+d+ии и к019z91-6и:и>иBи DиD/ Fи Iи Mи Qиpw+g+S+B?+:7+>+1<++<и и и к9<.и;/иS5иBFи?Hи>Jи<Lи;Mи:Nи7P015#75#5#75#%&&'7!"&'''75#53335#535#535#5353#3#3#32>&&'74&#!"3!2657#!"5463!2(444222 D"I =["#  ,k_A=3W,)$\ A!D!0<;11;:;1CIʌDH6HD%$$Y$$Y$$Y$$4   D3% $   N8L/FD"A?!A"DF  C09633672CE:?IEC M O[l{+U+,+X+|+U9U9 иик>9AU9UBиB/XDиD/FX9J9SиS/,YиY/i9u9x9++ !+W+++T+D +[P+! 9+и!3к>! 9AиEкF! 9J! 9[RиTXкxT901%#326575#5#75#&&'.'7##.'&&'667#5!#>7'!535#5!#37&&'6677&&'6674&#!"3!2657#!"5463!2N3* p ,5<_0<;11;:;1CIʌDH6HD|"! ..F ;<;% !V'   ! ,. 19><>( 9=;#yG *i-g;209633672CE:?IECMNcj}+78+J}+hi+Sb++Jк%9(939<9H9O9\ih9l97rкs9}J9+ +IJ+JkкlJI901%#!"5463!2'4&#!"3!265&&'>7&&'&&'#&&'>73#%#4&'3265#5>7>5667CIʌDH6HD0<;11;:;1V@4'    M $0 [l97% ' H A   CE:?IEC09633672F?G~8%.114M  1X]WF $6- NC L%2 36$&%)160 D M$co{+Ra+M++K+)+|+ик94M9:aR9M?и?/EM9KPиP/\9g)9++c%+'+*++9*Lи'Nи%P015#75#'>77&&'7%#3##4&'&&'>732655#37#.'>5!.'7.'74&#!"3!2657#!"5463!2||| Y  F)"Gp` .(' K   `T"  "E ^  !!&F!0<;11;:;1CIʌDH6HD$##\%%t@FG! 3RQC#+ B%l1#4( *(%'$  1+^a_, VZU".2.(K ! B $09633672CE:?IECMB+!2++,926к99>9!@иD+ +/(+<9+B+/,и,/3иB5к>9<901%#!"5463!2'4&#!"3!265###4&'32655#5357!5!73CIʌDH6HD0<;11;:;1] #(. &pBCE:?IEC09633672$3. IUnH Q M!4JZi;Sa+++ +$3+iK+ииикai9*к- 98ai9;9@ai9G3$9I 9ikW^+fN+0++J5+++0и/0и/и*и*/0-и-/J7иJ@01%5#75##4&'3255##!#7#4&'32657!53&&'73>7374&#!"3!2657#!"5463!2yyy)(  yQ$sP+( }'>4,vY#S0<;11;:;1CIʌDH6HD..m--$:.: 0+#9, oJ+04%&#DB09633672CE:?IECMϺ+AU+?++++ 9#9&9/929596?99?9=9S9Y9`UA9c9q9s999AииAи+ +++?+=+++#)к2#95к6#9#9и=VиWи\кc#9qкs9и/=01%#!"5463!2'4&#!"3!265#"&&'#&&'767#"3#7&&'7&&''737&&'#&&'>73>73#4&'32655##35#75#CIʌDH6HD0<;11;:;1MR+ / H$ w… 8 ?!G A/! @  B   XKpppCE:?IEC09633672 "z %&& : Z{3  , ))$  0   W$7 . m//`//M pѺy+)8+q+ 999%929=98?и8Cи)Hи)LкQ9V9`9e9f9h9}+t+/+]+EW+h+TQ+T к /9/и/%и2и2/5и5/WCиC/@BиB/IиI/WJиJ/@KиQNиN/hOиVи]XиEZиZ/_иW`иTbиQdиj01%667#3&&'4&'>7&&''7#5#6&'22655'75#5353#737#53'7#5353#3#3#4&#!"3!2657#!"5463!2J&m=5 +x?;71 , M4P;8&%  YNN]FF?[sW,a]d6S 6"0U0<;11;:;1CIʌDH6HD  ($` ++2  &h) $4* \vMtMj 28IQGS4GOI  C1#09633672CE:?IECM'UY]a'+Y#+&V+;L+(9++!9;0к3L;9C9FV&9LOкQ9;SиVZиY\иV^иY`иc+ +6/+%[+T;+^W+Z_+!/6963и3/F/69;LиTNи;Q01%#!"5463!2'4&#!"3!265%'737##4&'33265#&&'667665#53535#5#5#CIʌDH6HD0<;11;:;1"''*!% :>" (,7 VVX\SSSSSCE:?IEC09633672GF`/ R%1'[BFE>a)"$Q=0w5M \lMXXWWM&Nx|_+)8+X+ ++"и$29=98?и8CкE9)Hи)Lкv 9 yи{и#|и }и+ +y!+}z+C@+&~+qT+~'и'/2!y9@KиYܸDиD/EKY9CIи&NиN/qOиO/Clиl/v@C901%#!"5463!2'4&#!"3!265#5##!#6&'3655'75#5353#7%##"&55>733266775#75#CIʌDH6HD0<;11;:;1iTTDT9&%  ZOO^KKM [29:& 7<=  *jCE:?IEC09633672> kH$4* [vM tMj r A =  *  ==99M![kzdr++N++95+=+z\+9к rz9rz9"rz9+0иN3и9Iи5KкLrz9W=9z|ho+w_+R%+01667665#.'>7##".5'757577&'&&'#5332>54&#!"3!2657#!"5463!2-  X    %7$ 72 DV.V9< # Q.   V0<;11;:;1CIʌDH6HD t &/2 I& V]["89%"  Vj A J    09633672CE:?IECMvy+1@+,-++69;91Rкf9i9+ +BR+01%#!"5463!2'4&#!"3!265'"&&'&&'#'.'>52>7667&667%''>77&&'7'&&'7CIʌDH6HD0<;11;:;1E EO $ M!KI@4 AJM&KB$  &+ -%? # @$@$"" @&DCE:?IEC09633672  #sK4q4U.q1d^V# DQ[.  4  ?&;  &--#A   +002M1GOL4' A g% B !M!4FUet^l++ +tV+ ии и /lt9lt9к"lt9/lt9Alt9Flt9Rlt9Ult9tvbi+qY++!+!ииии!и015####.'665#535#5!#3.'>77&'&&'>77&'&&'6674&#!"3!2657#!"5463!25MW4 '3(VVAaBM5?H'  ,PC3- 4=D$ (K@2 Y<G[0<;11;:;1CIʌDH6HDtMR%UY\+  >XMJJ885  4>> 761  0;=%[%  "b,>09633672CE:?IECM(8G1?++G)+?G99?G9и$и$/GI5<+D,++ +и"01%!5!53!7.'&&'>75#5!#4&#!"3!2657#!"5463!2!_$LD6 3BM'5\D*-FZ0<;11;:;1CIʌDH6HD'O ",10,"!5D$MM%D5!09633672CE:?IECM $ ++$+и/$ܸܸи/$ $&+!++++ + 01!5!5!5#5!!3575!#!"5463!2,lg`-`CIʌDH6HDaO NNCE:?IECM'6Ѻ.+$+6#+$и#и/$и. ܸ$и6!#&и68++3#++++ и и и+33"ܸ&01%!5!#!5#35!#535!2>775#5#35#!"5463!2aa``,FJL"AoaV)Z``CIʌDH6HD'J$??  L VOhCE:?IECM-51++9ܺ9&9.74+ ++и/к&901%#!"5463!2.'35!3>7675!!35CIʌDH6HDs5WB(&@Y6&J@46BH#* bCE:?IEC(/RR/( !''!RRM#'3;Jۻ'B+;++J8+8J9 BJ9B0ܺB09+;9J$;.JL?/F/+$+85+4%+ $9и/$95+и+/8,и40и0//901%.'667667'>76675!%535#6675#535##!"5463!26V@(] &9L0 A;16@F" 3$"I&E7CIʌDH6HD%*. 1'##,+&&PPeJ JjJCE:?IECM 6:IU0A++%7+;+7и/A:и/7и/: и /;и/;-7%9-/,ܸ'и'/1и3и-5и;K5?+E9++-+7 +F+  +.+++?$и&и(и*и,и2и2/01%#5375#75#75#5#535#535#7&&'#3#3#5#353355##!"5463!2V??J(B2 R?R CIʌDH6HD4]jFFkFFeII&MLM355  ML#CJJCE:?IECM (k  +++(+(  ܸ  ܸ(*+$ ++01%5#5####!>5#!"5463!28_S`}5 ' CIʌDH6HDPP2Pe:.U]nGCE:?IECM %1@9++,+2 +A&6FVfv ]A]9ܸ,и/ &и,(и,0и2B 5+='+<+,-+& + + !=)ܸ 001#"&54632!!4.#"32>##3#3#!"5463!2-*)..)*-0u/A((A..A((A/`eeeeCIʌDH6HD)<<)(::O%@//@%&A00AFK]KQCE:?IECM3B:+4+:ܸ44 ܸ:*ܸ4D7+?%++ ++/+7и/01%!5!#"&546325!35!5!%4.#"32>#!"5463!2&=,VKKV,=&M!b@>bFFb=>aGFb>CIʌDH6HD4Rp  && &QQ0&&00%%01CE:?IECM :BQ$J+?+>!+C;+>и/; и /Jܺ!>9+J$93JC9>AиCSF+N<+B+!?+ ܸBи/@ܺ@9N"!$к+@93@901%#"&546324&#"326.5535!3>766##3#!"5463!2NLKNNKLNax<]A"xy!<-/?%! ),)*($ `{{CIʌDH6HDY && (( ?P&5?OO0$2< NN A9, &%0*"JOCE:?IECM #'3B#;++.%++,+4(+A&6FVfv ]A]. ;;'ܸ,0и+2и4D8+?-+>&+$!+ + +8(8001%#"&546324.#"32>75!%5####337#!"5463!2#&&#X%9'(9%%9&(9&  ^3^^3CIʌDH6HD*30-,20.=00=<00 N]CIʌDH6HDQ+I5 6I*Ue2Ej"[@J(He==eH(JBLACE:?IECF *>RI5I++  +?++I?9I?9$и$/(I?9A++]A+)+9+I+Y+i+y+++++++ ]A5&565F5V5f5v5555555 ]A55]?T:D+N0++%$+ ++ иии$'01!53!7!5353!'.'&&'2>7#5!#4.#"32>7#".54>32!^9] %JB55BJ$4Q;& }?mSSm??mSSm?CuYYuCCuYYuC ЃLVV"##!%LL+9Sm??mSSm??mSYuCCuYYuCCuF0D#;++ +.'+1+ A]A)9IYiy ]A&6FVfv ]A].)и',ܸ1F#6+@++ + +*и*/ ,и,/01%#35#5!#2>7%4.#"3267#33#667#".54>32p Ucm9:!HHD'?mSSm??mS>o0LZ[[K[CuYYuCCuYYuCn  4I  {Sm??mSSm?" J7eYuCCuYYuCCuF$8/+++"+%+A]A)9IYiy ]A&6FVfv ]A]"и ܸ%:*+4 +++ +01%#!!4.#"3267#33#667#".54>32ӓEZ?mSSm??mS>p0QZ[[N\CuYYuCCuYYuC:FSm??mSSm?# K7gYuCCuYYuCCuF (<3+++&+) +и A ]A ) 9 I Y i y ]A&6FVfv ]A]&!и$ܸ)>.+8++ +#$+01%5#!33534.#"3267#33#667#".54>32ۖVVT?mSSm??mS;k/C[ZZKYCuYYuCCuYYuCFעSm??mSSm? I7eYuCCuYYuCCuF0T +MG+.'++A]A)9IYiy ]A&6FVfv ]A].)и',ܺ1 99GM9A 9#+++,+01#".54>324.#"3267#33#66.'&&'6676655CuYYuCCuYYuC?mSSm??mS>p0VZ[[P_ .' *- !6N _ &.-YuCCuYYuCCuYSm??mSSm?# I63"/57:91" p>>  Z><5+F'FZ2Q+ ++B;+G(+A]A)9IYiy ]A & 6 F V f v ]A ]A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]B=и;@ܸG\7L+V-++#+?@+014.#"32>7#".54>324.#"3267#33#>7#".54>32     U)A--A))A--A)a?mSSm??mS8f-LZ[[*D0CuYYuCCuYYuC@#=--=##>-->#1YC((DY11XC((CYDSm??mSSm?LHVc5YuCCuYYuCCuF =Q+H++;4+>!+H>99H>9A!!]A!)!9!I!Y!i!y!!!!!!! ]A+&+6+F+V+f+v+++++++ ]A++];6и49ܸ>S0C+M&++89+01%.'&&'>55#5!#%4.#"3267#33#667#".54>32X *''+ +<%|N|'9T?mSSm??mS;k/@Z[[JXCuYYuCCuYYuCy #.3696/! MX\)8KK9)WSHSm??mSSm? K7dYuCCuYYuCCuF $AUB/L+!"+?8+B%+LB9"!9LB9"!9/A%%]A%)%9%I%Y%i%y%%%%%%% ]A/&/6/F/V/f/v/////// ]A//]?:и8=ܸBWEX/>Y4G+Q*+$!+<=+и01%.'&&'>55#5!##534.#"3267#33#667#".54>32] +((- (<)U':6?mSSm??mS=n0B[ZZHUCuYYuCCuYYuCr# (.130* " =JO #MM" LG9JSm??mSSm?"K6bYuCCuYYuCCuF-A8++$+.+8.9 8.9A]A)9IYiy ]A&6FVfv ]A]+&и$)ܸ.C 3+=++()+ )(901&&'667'767#54.#"3267#33#667#".54>32. &Lp"?mSSm??mS9h.B[ZZM\CuYYuCCuYYuC0J&nE!P00ISm??mSSm?L6fYuCCuYYuCCuF.B 9+ +,%+/+ A]A)9IYiy ]A&6FVfv ]A],'и%*ܸ/D!4+>+ ++ ++  01%'!#3#2>7%4.#"3267#33#667#".54>32oA}:"IHD)?mSSm??mS9j-AZ^^M[CuYYuCCuYYuCsGyH tSm??mSSm?J6fYuCCuYYuCCuF ,@7+*#+-+A]A)9IYiy ]A&6FVfv ]A]*%и#(ܸ-B2+<+ +'(+01%'7777!5!4.#"3267#33#667#".54>32whYTDSd#h:?mSSm??mS7#".54>32cZ { Zb 5AZoa?mSSm??mSSm?CuYYuCCuYYuCJV `` VrF/G<~CYJ+T6++%"+ии/)J@01%.'&&'>7#5!#3667##334.#"32>7#".54>32 >8- .7= .I4  4Ie>}[A?U[[UX?mSSm??mSSm?CuYYuCCuYYuC$$ () &#*II*#<$)hSm??mSSm??mSYuCCuYYuCCuF +?SU6J++ + #+@,+ 9/ и!и%и#'и)A,,]A,),9,I,Y,i,y,,,,,,, ]A6&666F6V6f6v6666666 ]A66]@U;E+O1+ + +)&++%"+ +01%4&#"3265##"&54632'!!#5#535#53534.#"32>7#".54>32HGGHHGGHzppzzppz0[YYYY[?mSSm??mSSm?CuYYuCCuYYuC\&&$$ :II:7&&#"3267###334&'667#".54>329U,()3~GSm??mS?s1:5XX5MBBMCuYYuCCuYYuCo H)-?mSSm?%!0b ^77^YuCCuYYuCCu F #7;?K_sVj+.+$ +@C+`L+@Aи/A&6FVfv ]A].9/A ]A ) 9 I Y i y ]@и/<ܸ=и=/AEиCGиAIALL]AL)L9LILYLiLyLLLLLLL ]AV&V6VFVVVfVvVVVVVVV ]AVV]`u[e+oQ+ +?<+!+;8+)+3+EB+)@и@/3H01%4&#"3264&#"326#"&54632'#".54>327!5!'#53#5#535#53534.#"32>7#".54>32BHGBBGHB&#$&&$#&qrqqqqrq':&&:'':&&:'rU[\\\\[?mSSm??mSSm?CuYYuCCuYYuCL  8CC68BB())  *tBBd+E5FSm??mSSm??mSYuCCuYYuCCuF '; 2+ ++ +(+иA]A)9IYiy ]A&6FVfv ]A](=#-+7++ + +015!##5#5!'!3!534.#"32>7#".54>32\MA5[Z?mSSm??mSSm?CuYYuCCuYYuCGGM;BBSm??mSSm??mSYuCCuYYuCCuF (CW73N+?<+!$+D)+$!9<?9ND9!"&A))]A)))9)I)Y)i)y))))))) ]A3&363F3V3f3v3333333 ]A33]AND9DY8I+S.+><++&#+и<@A<>901.'&&'>75#5!##5#53534.#"3267!53!667#".54>32S++# &-6*V.9ZssZ?mSSm??mSF}3D[oALCuYYuCCuYYuC## %))%! !$2="%JJ$!;.!JSm??mSSm?,(ُ>7]YuCCuYYuCCuFOc9Z+EF+ +P+CиEA]A)9IYiy ]C иC к 9ZP93FE93/ 9&39.ZP96ZP9A9&969F9V9f9v9999999 ]A99]A 9EHиCJиLкMZP9PeEX / >Y>U+_+C++J+  4к6901%5!%4&'#5#5353&&#"!#.'&&'>55#53267#5!#3!53667#".54>32REZ{{U3|FO6P~*7 !&&()' ";+~;D?mSAt1F[[ZERCuYYuCCuYYuC9==`7H',82J 8/" (,!$ '2; H7XSm?&" 897`YuCCuYYuCCuF(<P3G++=)+G=99G=9A))]A)))9)I)Y)i)y))))))) ]A3&363F3V3f3v3333333 ]A33]=R8B+L.++и01%##5#5!'.'&&'>76674.#"32>7#".54>32#\M !C>33?G"%NC4 Y 5DM?mSSm??mSSm?CuYYuCCuYYuCL )$,,$( (B/ 7)xSm??mSSm??mSYuCCuYYuCCuF ';O2F++ +<(+!и!/A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-+ + ++"+'+'и$01%4&#"3267#"&546327!535#5!!!#34.#"32>7#".54>32LFGKKGFL[}pp}}pp}8vj?mSSm??mSSm?CuYYuCCuYYuCY!! :FF:32ؘWVW?mSSm??mS7#5##5337&&#"3267#4&'667#".54>32(=G  ) * T``To3{ESm??mSBu2:LABKCuYYuCCuYYuC3psN   #HKP-GH&,?mSSm?'#]87\YuCCuYYuCCuF .BY9++++/+и и ܸA]A)9IYiy ]A&6FVfv ]A]'9'/*, 9/D#4+>+)'+ ++ +и/'+,')9015#!3353##334.#"3267!53!667#".54>32͐VV U[[UX?mSSm??mSE}39ZoGSCuYYuCCuYYuCA__DE__xSm??mSSm?,'ܒH7aYuCCuYYuCCuF -AU%8L+  ++B.+LB9LB9%LB9A..]A.).9.I.Y.i.y....... ]A8&868F8V8f8v8888888 ]A88]BW=G+Q3+++ и ܸи/01#5!5!7!53533'.'&&'6676674.#"32>7#".54>32[G[!53!#".54>32#34.#"32>7#".54>32     ! =[h.?'&@--@&'?.ZZ?mSSm??mSSm?CuYYuCCuYYuC####^؏k$=--=$$=--=Sm??mSSm??mSYuCCuYYuCCuF -AR$8++ ++.+и/и/и/A]A)9IYiy ]A$&$6$F$V$f$v$$$$$$$ ]A$$].CEX/>Y)3+=++ ++ܸ и015!!53!7!535#3!53#374.#"32>7#".54>32}w/[v5[[j?mSSm??mSSm?CuYYuCCuYYuC??_qH>??>Sm??mSSm??mSYuCCuYYuCCuF '3G[ú>R++++H4+A&6FVfv ]A]A&6FVfv ]A](ܸ)и+и-и(/и1A44]A4)494I4Y4i4y4444444 ]A>&>6>F>V>f>v>>>>>>> ]A>>]H]CM+W9+#+/0++3(+ +014&#"326#5!5!'#".54>32##33#34.#"32>7#".54>32.'%. '.\->%%>,,>%%>-T\\TTTh?mSSm??mSSm?CuYYuCCuYYuC%66%"8NI$<--<$#<++gSm??mSSm??mSYuCCuYYuCCuF ,@T7K+ ++A-+KA9KA9$KA9A--]A-)-9-I-Y-i-y------- ]A7&767F7V7f7v7777777 ]A77]AV<F+P2++ +и и 01#3#5!535#5!'.'&&'676674.#"32>7#".54>32[M324.#"32>7#".54>32IKLIILKI\\M4;[@@[;;[@@[;?mSSm??mSSm?CuYYuCCuYYuC '' !%%M3''33''3Sm??mSSm??mSYuCCuYYuCCuF  4H!+?+++5!+ 9 / ܸA!!]A!)!9!I!Y!i!y!!!!!!! ]A+&+6+F+V+f+v+++++++ ]A++]5J0:+D&+++ +и/01!53!''7537''66'#5!##334.#"32>7#".54>326[oglV/ U DVZZV\?mSSm??mSSm?CuYYuCCuYYuCzL ?m#&[ GSm??mSSm??mSYuCCuYYuCCuFZ % +N-+3L+]o+++A]A)9IYiy ]-и/" 9A%&%6%F%V%f%v%%%%%%% ]A%%]=9i 9oyиo}и]к9 9 9 9V++7F++ld++[+9ܸ и /"9[/и//.1к=dl9.Mиliиi/[zи/{и{/|и|/}ии/F7901#".54>324&'#5!3#5#5>5#5!#33267667##".55#32>'###4&'53255&&'75#53537&&#"&&'CuYYuCCuYYuC82PV=3:;4+ .  ! 2#$ 1q?Sm?/! 8  ccL/+6TT79-YuCCuYYuCCuYO6Y1A6QQ6,8L6II   !:[J:!$?m|- ( 4&bAU77??7<[!*M_ӻ+"#+F5+++NW+A]A)9IYiy ]5N9A&6FVfv ]A]!N9AF&F6FFFVFfFvFFFFFFF ]AFF]R+9AWW]AW)W9WIWYWiWyWWWWWWW ]\+9Na/\/ R+:A+&%+I+Rи 0%=и=/01%4&#"32>&&546737##56673#".54>32#&&#"66327#>54.'3F;;D /0!8==8+''{4n<6)/C)-G14F*NQ2<4#5#I2(C0=8+' '+8=JTVI :+-9UU7!5!#>54.'38==8+''5o=5*0L6!<-?K%[=8+' '+8=UU&&546737##56673#".54>7&&54>327#>54.'3%12%M<:M<56= *) 8==8+''4o<6)2E()D3($2+=''=*3%(~=8+' '+8=!2""2!BEEi/==/))UU5#".54>32#>54.'3"1/ E9&&546733!>76654&#"#&>32#".54>32#>54.'3 )* *) 8==8+'')4H*9#*-20% 0&9$=Q^(;'';((;'';(c=8+' '+8=16^F))F^66_H))H_`UU=+):и:/Q01&&54673%!>76654&#"#&>32!##56673#>54.'38==8+''W 2C*16>8,2+A*$<-$06* 4b71(=8+' '+8=UU8%K59?-; -M7 +>(%;1)..-5#87ӈU76654&#"#&>323!>76654&#"#&>32#>54.'38==8+'' !)6I+<$++31& 0'9$6(P *7I,;$+,31' 0':$6'u=8+' '+8=UU(%<2*/X-22K?9$I69?-;!-M8 +>U76654&#"#4>323%#".7332654&##532>54&#"#4>327#>54.'38==8+'',<$*+41& /'9$5'!*6Gg-:#;,.@23>;/## ,224/'8"BO2 .4b=8+' '+8=UU($<1+.X-t-B,3J2JPD?и1@и@/JF9AOO]AO)O9OIOYOiOyOOOOOOO ]TF9FY/I/:+ !+E;+Eи!,=и=/;?и T01%&&54673%!>76654&#"#4>32!%##5#5337#>54.'3ݲ8==8+''!.?&-/73( 2(<&!8*",9LSD/0D\=8+' '+8=;UU'%<3*.Y.]1mgU76654&#"#4>32#".'332>54&#"#3#66327#>54.'38==8+''!+7J,<%+,41' 0'9%@UV-;"!:*1=0*=5!4,*3KVh=8+' '+8=UU+"5+aP+F+ir+A]A)9IYiy ]>=9A&6FVfv ]A]!>=9A"&"6"F"V"f"v""""""" ]A""]=*и"+и+/APP]AP)P9PIPYPiPyPPPPPPP ]mi9Arr]Ar)r9rIrYriryrrrrrrr ]wi9i|EXK/K>Yw\+d+K A ' 7 G W g w ]A ]mܸиwиK*\8и8/d=и=/\UCиC/01%4&#"32>&&546733!>76654&#"#4>32#".54>32#&&#"66327#>54.'3!;33:))8==8+'' +6K-<$+-41' 2':%@Sf):#'?+.>%CF01+ - =,":*\=8+' '+8=FQRG8++9UU55#5!#4.#"32>7#".54>32]#JB4 2AK%4[D',DX?mSSm??mSSm?CuYYuCCuYYuC:L!+//+! 4A#KK$B3 #Sm??mSSm??mSYuCCuYYuCCuF'F% +*1+,-++A]A)9IYiy ]A&6FVfv ]A]-;и;/,>и>/,BиB/#+++,+>;+F(++.+0и(2иF401#".54>324.#"32>'#3#5!535#5!667665!5!3CuYYuCCuYYuC?mSSm??mSSm?nZ W-YuCCuYYuCCuYSm??mSSm??mX]I]J* J) #CF #7KC.B+ ++8$+и/ии/и/B89B89ܸ!A$$]A$)$9$I$Y$i$y$$$$$$$ ]A.&.6.F.V.f.v....... ]A..]8M3=+G)++ + +#+01%5!#5!#!&&'>7#5##334.#"32>7#".54>32|]ZZ,JkE-TE0 :UZZU[?mSSm??mSSm?CuYYuCCuYYuC7nnTlCjT@' 2C*J͊oSm??mSSm??mSYuCCuYYuCCuFBV99M++./+++C+и A]A)9IYiy ]%MC91+9A9&969F9V9f9v9999999 ]A99]CX>H+R4++1,+  +,и/,19"и"/013#3##6674&'#4&'3265##!&&#"32>7#".54>32 $*.QN.P!<47( # fQ6RSm??mSSm?CuYYuCCuYYuC& HM?FQ6$2- J{57#5353#667667#".54>327V=$ +| 0s?G~?mSSm?]Na<2P<( ` &@? F"Y9&,CuYYuCCuYYuC'm|?#B"FS!%-dSm??mSg7U 0 *38HpUH'V&1: &>U"3|EYuCCuYYuCCuF,@TE7K+  +A-+KA9KA9KA9 и и  и $к(KA9A--]A-)-9-I-Y-i-y------- ]A7&767F7V7f7v7777777 ]A77]AV<F+P2++$++!и#и%и'01%.'#5&&'>7#535#5353#3#74.#"32>7#".54>326" 466Y%l;#NG7 _ŷ 8GPs?mSSm??mSSm?CuYYuCCuYYuCu/ &.5-Y%. (4;JSJiFJSJ;6)Sm??mSSm??mSYuCCuYYuCCuF1E<++/(+2+<29A]A)9IYiy ]A&6FVfv ]A]+ܸ(,и2G$7+A++,)+01&&'>7>7#54.#"3267##533667#".54>32(Ge? ' )>,?mSSm??mS>p0DZHUCuYYuCCuYYuC7Zyd*$ IRX,ISm??mSSm?# ;I7bYuCCuYYuCCuF';O]2F+++<(+и#ик 9 /и/ и и#и ܸ!и%A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-+++ +' ++01%5!#5!#3!53'#!#2>77##334.#"32>7#".54>32aZ[[Zn'R\i=2 GFBVZZVV?mSSm??mSSm?CuYYuCCuYYuC8EES;Y-7+A#+++ + и/015#!53!'!3353#5#535#53534.#"32>7#".54>32Lj4[qVV\Y[[Y\?mSSm??mSSm?CuYYuCCuYYuC5cc؎Qcd]GOGYSm??mSSm??mSYuCCuYYuCCuF ;CWksNb++>?+XD+A&6FVfv ]A]bX9 bX9(bX9?<ܸ>AADD]AD)D9DIDYDiDyDDDDDDD ]AN&N6NFNVNfNvNNNNNNN ]ANN]XmS]+gI+ ++C<+ <C9>и>/01%4&#"3267#"&54632'.'&&'>766765%##334.#"32>7#".54>32IGGHHGGI[{pp{{pp{  *' !'( &6+W Q#O[[O^?mSSm??mSSm?CuYYuCCuYYuCd%%$$;JJ;;II ' "&+' % %5C)   :b#Sm??mSSm??mSYuCCuYYuCCuF$,L`Q<W+HE+%(+12+M-+(%9EH9WM9Hи/H и /%&*A--]A-)-9-I-Y-i-y------- ]4219A<&<6<F<V<f<v<<<<<<< ]A<<]JWM9MbAR+\7+GE+*'+EIJEG901.'&&'66764747#5#53534&'#3&&#"3267!53!667#".54>32  ! :>S"-VPPVQEXO3zESm??mSI41[}?ICuYYuCCuYYuC6 %#()% ! uJ   !=6*~E`7F&+?mSSm?0,ܒB7[YuCCuYYuCCuF'+?S6J++()+@,+J@9J@9J@9A,,]A,),9,I,Y,i,y,,,,,,, ]A6&666F6V6f6v6666666 ]A66]@U;E+O1++01!53!.'&&'66766767#34.#"32>7#".54>32?Zg *(# %)( &BZ VTZZ?mSSm??mSSm?CuYYuCCuYYuC ׍ ( #(0( % pQ   ?aSm??mSSm??mSYuCCuYYuCCuFAJ^U+ 1+G+?'+K+A]A)9IYiy ]A & 6 F V f v ]A ]A]A)9IYiy ]A&6FVfv ]A]'<и'BиGJиJ/K`#P+Z+,+6+B(+<C+014.#"32>%4.#"3267#5##".54>3235366%5#%#".54>32       ?mSSm??mS-->"#>-->Sm??mSSm?!4?(CY21YC')7GuYuCCuYYuCCuF,CU]m +L8+ZD+-.+*'++A]A)9IYiy ]A&6FVfv ]A].AAL&L6LFLVLfLvLLLLLLL ]ALL].VиZXиX/Z]и]/#++Q3+=I+V/+AW+01#".54>324.#"3267#366#5##".54>323534.#"32>5#CuYYuCCuYYuC?mSSm??mS324&'#5#533&&#"32>7#".54>32)**((**)0\tV,>%KX,<$%=,=WIZzzJ0r?Sm??mSSm?CuYYuCCuYYuC!!""%~hcF, A7+  ,c7)/?t %?mSSm??mSYuCCuYYuCCuF (CWG3N++#$+D)+ND99ND9$!ܸ#&A))]A)))9)I)Y)i)y))))))) ]A3&363F3V3f3v3333333 ]A33];$#9$<и55#5!#%##334.#"3267#5!5!667#".54>32L !%&'(% #:)~O|*8T[[TW?mSSm??mS?q0TN]CuYYuCCuYYuCE$)+!" '3;JJ9/#6jSm??mSSm?$ H7gYuCCuYYuCCuF (<PZ3G++ +=)+G=9G=9"и"/&G=9A))]A)))9)I)Y)i)y))))))) ]A3&363F3V3f3v3333333 ]A33]=REX/>Y8B+L.+#"+ ++ܸ  иии"%01!53!7##5#5!'.'&&'267#5!#4.#"32>7#".54>32-Zy8ZM $H?53AH"doҷx?mSSm??mSSm?CuYYuCCuYYuCц{{Lh !"!9(II*7Sm??mSSm??mSYuCCuYYuCCuF'AG +FC++A]A)9IYiy ]A&6FVfv ]A]EXB/B>Y#++74+A(+A*иBDܸBF01#".54>324.#"32>'!5!667'%66'!5!3!53!CuYYuCCuYYuC?mSSm??mSSm?i T2,[y-YuCCuYYuCCuYSm??mSSm??m#J *GI.00-ۑF*=DX3O+ +)+%+B<+E>+A]A)9IYiy ]A & 6 F V f v ]A ]+<B9A3&363F3V3f3v3333333 ]A33]A>>]A>)>9>I>Y>i>y>>>>>>> ]EZ8J+T.+ +014.#"32>'75&&54>327&&#"3267#4&'667#".54>32$##$~Y>HCuYYuCCuYYuC! !   Lt S;#;**;#;S q&+?mSSm?(#Z88[YuCCuYYuCCuF "&.Oc9Z++)*+P/+A&6FVfv ]A]$9$/#ܸ*'ܸ),A//]A/)/9/I/Y/i/y/////// ]A9&969F9V9f9v9999999 ]A99]A*)9*DиD/)HкM*'9Pe>U+_4+LA+&#+DI+"+HE++ )+) 'и'/.014&#"326'75&&546327'!5!'#53##334.#"3267!5!5!5!!!667#".54>32& && &i3;PEEP82fY?UYYUN?mSSm??mSAu1VVgCOCuYYuCCuYYuC  @(!'))'!' > >|Sm??mSSm?&#?,7^YuCCuYYuCCuF0D ';+++1+ ;19ܸA]A)9IYiy ]A'&'6'F'V'f'v''''''' ]A'']1F,6+@"++++01!53!&&'667667#5##334.#"32>7#".54>32;[j !H^:UZZU^?mSSm??mSSm?CuYYuCCuYYuC ܒ5& bEJ͸Sm??mSSm??mSYuCCuYYuCCuF #7K.B++и/9/и/B ܺB 98ܺB89ܸ!и8$A.&.6.F.V.f.v....... ]A..]8M =+G)++ ++#+=301%!5!5!5!5!!&&'>7#5##334.#"32>7#".54>32ZgF -IgD +QE4 6N[[N[?mSSm??mSSm?CuYYuCCuYYuC0$@$DfGdI5&,>'JWSm??mSSm??mSYuCCuYYuCCuF'3[_  +\]++A]A)9IYiy ]A&6FVfv ]A]]+и+/\/к4 9< 9D 9#++3(++0+/,+01#".54>324.#"32>!5!5!5!!!.'&&'>767765#3CuYYuCCuYYuC?mSSm??mSSm?8`m '($ "') %6,W(8[[-YuCCuYYuCCuYSm??mSSm??m"K(= )!&+$ % #0<%     4/%T@F1o +`i+R+{^++A]A)9IYiy ]A&6FVfv ]A]$ 9=^{9VܺFV9JV9MV9PV9SиS/TиT/ci`9f 9^kи{nкp 9x 9~ 9 9 9Vии/и/ 9R-++=+k^++TS+$=9F=9MST9TPܺVST9fST9koиkxи^zк~ST9ST9=и/=9=901#".54>324.#"6732>''&&'&&'7&&'677##53667.5#&&'6655!53&&'73#>77667##"&55332>7#53&&'7%5#CuYYuCCuYYuC?mSSm?0++N #4HSm?n"# $ 6 #    `=  " (!&R9>  6 E& 'W 92.$Y n% $ '6&7-YuCCuYYuCCuYSm??mSI53>  B*/?m.!    !? 488XMr-%f??$   ! G F "$ &L  "&-)h6 M6d>(455 Fgsw{ɺ +?!+\++]++A]A)9IYiy ]A&6FVfv ]A]+ 9!/и//1 9?3к4 9; 9F]9J 9K 9W 9Z 9_ 9b 9e 9vиv/zиz/~и~/иииC++q+x_++|y+t}+j+]u+Wܺ!W9"j9u+и+/49]8и8/;u]9>j9@W9FW9_Jи_ZиWbиb/eW9hиh/lиl/nиn/rиr/иии01#".54>324.#"&&'66757&&'#3267&&'7#&&'267#!#66#!5#535!35#5#75#5#5##5#75#CuYYuCCuYYuC?mSSm?[L8O  90n324.#"&&'>7#32>'.'&&''7&&554'7667&&'7CuYYuCCuYYuC?mSSm?[L!5&Y  J1t?Sm?| ("%! a fS * '- "10-YuCCuYYuCCuYSm??mSf7? 1MXY" &.2A"%?m0 &07'e6 H /- E-B3U":A%&-,6 F:UtQ +A+;+z+3.+5a++A]A)9IYiy ]A9*к0 98a59A=и=/;?и;Cи;GкN 9AQ&Q6QFQVQfQvQQQQQQQ ]AQQ]T 9Va59\.393cкk 9oA9wи{иz}и}/ 9.ии;иии;иEX-/->YEX3/3>Yr++K+?<+GD+@+x++G+иxBиKHиH/NK93buиDvи@zи?|и<~иuииии01#".54>324.#"2>7"3353665#535#535#535##&&'''4&'325#&&'3265#3#3#76676676655##5#75##5#CuYYuCCuYYuC?mSO6LI= (:M| 9Annllxx/9A82&/ ')8,4IG{nnkks  (G&(G&-YuCCuYYuCCuYSm?82 4 % -7#AA 7VO6H+IBGD/\" !+1--A 2/w4!!!!NF@Th?K_+++UA+ик_U9_U9,_U98_U9?_U9AAA]AA)A9AIAYAiAyAAAAAAA ]AK&K6KFKVKfKvKKKKKKK ]AKK]UjPZ+dF++8+@+@9@901%5#>7#5##5&&'>7&&'.'>737!74.#"32>7#".54>32 $ &&" SS =.;;8 $ 0  *ND7A  A9=4?mSSm??mSSm?CuYYuCCuYYuCbrr / ", / -5; L @k Sm??mSSm??mSYuCCuYYuCCuF;OcFZ+ +P<+ZP9 ZP9ZP9ZP9' 9.ZP9A<<]A<)<9<I<Y<i<y<<<<<<< ]AF&F6FFFVFfFvFFFFFFF ]AFF]PeKU+_A++0/+/016677!&&'>55!&&'&&'667&&'7#53774.#"32>7#".54>32-!h07!!  /[**02EZ8%:< +%02.?mSSm??mSSm?CuYYuCCuYYuC5DK)uR ^T9AA&A6AFAVAfAvAAAAAAA ]AAA]LDJ9TiOY+c7+I+;<+++E++<;9 и /ииик"<;9,I9><;9DI9015#!5!7#5#75#5#75#%4&''4&'3265&&#"!!5!#5##32>7#".54>32€N;;;:::C9:& ) $1uBN6K27@8QK1tASm?CuYYuCCuYYuC;;:Aa88r66r88r66zV7"6, .#'60B&6OU7"&?mSYuCCuYYuCCuF ,@?#7++  +-+ ии ии и к7-97-9A]A)9IYiy ]A#&#6#F#V#f#v####### ]A##]-B(2+<++ +++01%5!%5!%5!#5!#3734.#"32>7#".54>32iTU\ɭ?mSSm??mSSm?CuYYuCCuYYuCqTTNNOOs""$\4Sm??mSSm??mSYuCCuYYuCCuF'>Tglpt +;<+(8+jk+Wf++A]A)9IYiy ]A&6FVfv ]A]3 9B 9E<;9J 9QfW9Skj9j]к`kj98mи;oи8qи;s#++c/+m9+T?+qn+>r+c3и3/c6и6//;иTAиTJи/]и]/c`и`/01#".54>324.#"32>##4&'3255##!%!53&&'73>73#4&'3265#5#75#CuYYuCCuYYuC?mSSm??mSSm?!  sM';2*pU!)% Lssss-YuCCuYYuCCuYSm??mSSm??ml#6+7&F).1%& "A^"6* ]2!,,g++ F)-15IMauoXl+>?+*++:;+23+67+bN+;и/$?>9$/;и/и:и/329/''9:и/и:и/;"и"/*.и+/и;Aи:Gи2Jи3KANN]AN)N9NINYNiNyNNNNNNN ]AX&X6XFXVXfXvXXXXXXX ]AXX]bw]g+qS+ +DC+++I8+1.+%+-*+ииии#и*2и-4иI6ܸ*:и:/8<и6>иI@иCFи.Jи1L01%5#75#33267'5#75###"&55#5!%#535#53#53#5##5##535#5!#3#534.#"32>7#".54>32nnn >mmm''##vvvvuuiQZJiuu?mSSm??mSSm?CuYYuCCuYYuC~!!H$$x 0!!H$$  #,϶12u1 ff::2uSm??mSSm??mSYuCCuYYuCCuF#+}m6+B+,+ 9999A,,]A,),9,I,Y,i,y,,,,,,, ]A6&666F6V6f6v6666666 ]A66]99<9G9BIиBMиRиVк[9`9j9o9p9r9 +1+c+Oa+Z+^[+ 9!ܸ$к9 9 <и7&&''7#5#32666774.#"4&'32655'75#5353#737#53'7#5353#3#3#66#3#".54>32/)r<85. 'I1L8(2yCIw #?mSSm?LA#  UJJXCC<VmR*\}Y{_3O 3+*-g:CuYYuCCuYYuC $*0  $c' #0%)0  %\Sm??mS]8 X pI nIe 05FMCO2CKF?, 5~[^YuCCuYYuCCuFW`dhl \ +dX+a+6G+R4++A]A)9IYiy ]Xd9(4R96+к.G69> 9Aa9GJкL 96OкU4R9A\&\6\F\V\f\v\\\\\\\ ]A\\]_ 9aeиdgиaiиdkEXI/I>YEXP/P>Y0++j+L6+if+eb+%ܺ(090.и./P5>09A09GиHкU09Yj9_0901#".54>324.#"373267'4&'33265#&&'667665#535366'75#75#75#CuYYuCCuYYuC?mSN6%7SDy34 8: &*3 RRSDA:]'!#M:-q3I x{(7d7XQ6P$XgSSSSF ^r!i+Z>+_ +i_9A ]A ) 9 I Y i y ]i_9i_9i_9A!&!6!F!V!f!v!!!!!!! ]A!!]$i_94i_95i_9;i_9>BкF>Z9R>Z9ZUиU/\i_9_t8d+n+[;+FU+B?+$;[9[=иUCиFRиBVи?X01&&'74.#".'7>7'3267!535#535#53.'73#3#3667#".54>32f  B( C9?mSBv29    77'7'7'77'777'7'74.#"32>7#".54>32 D)\:*c++K$ 7! Q. *N"#6 -?*  D4:0O# <8) S1 *G&>'- UD  -4F#0$1 O;  , "GG8 :'*Vy?mSSm??mSSm?CuYYuCCuYYuCh'  .  "  #   + +- ) " F8 L$!%*:"1$' $ '/N; )# !"" " ASm??mSSm??mSYuCCuYYuCCuF 8L`CW+ +M9+WM9WM9WM9 икWM9"WM9%WM9*WM9-и /и// 1к4WM9A99]A9)999I9Y9i9y9999999 ]AC&C6CFCVCfCvCCCCCCC ]ACC]MbHR+\>++-*+к9к9"9-1и*301%5!.'.'##5#5&&'>7#5353#74.#"32>7#".54>32    ~X 0N;&[1ER?mSSm??mSSm?CuYYuCCuYYuCv" !#! |  43 +>EEF_FHIAfSm??mSSm??mSYuCCuYYuCCuF7r +KL+'3+%+s+%9A&6FVfv ]A],3'9K9иK=к> 9G 9W 9\ 9L_кe 9Lhиh/Kkиk/o 9q 9Ass]As)s9sIsYsisysssssss ]v 9'кLK9Lи/!++5+h+;<+8+jܺj9<\и;^и8`иeиfиhlиnиoкvj901#".54>32'&&#"3267##&&'>566#3#7&&'#54.'>7#535#53'7#5353#34&'"3#66#3CuYYuCCuYYuC3}GSm??mS@s1<2  <(Mzii%'  P  $!_kK&Mi]gO/GsLB  !%(1GRQ#-YuCCuYYuCCu)-?mSSm?%"^-[YV(IR*`0G5 - #G0GM GE#GGf]7 ~I7VFTX\`t!k++X?+BU+JUB9J/AJJ]AJ)J9JIJYJiJyJJJJJJJ ]aܸܸ и /9/ и?и/?и/9ии/A!&!6!F!V!f!v!!!!!!! ]A!!])UB9>?X9>/Cܹ/6?X999RJa9UYиX[иU]иX_иav&f+p+A^+]Z+UC+YV+Aи/Vи/Vи/^A9C0иC>014.#"3#3#7'75#535#53267#".55#&&'>5#!#33267366'5#75#75##".54>32?mSQ7HAAT X@@G7??mSBw3<  '"&8)>  -3烃CuYYuCCuYYuC-Sm?<4IIC4_ID6USm?($ /C61 % "+6%v)   5*..g22m00YuCCuYYuCCuF!0^bve;m+bD+OP+c1+ mc9mc9!mc9+mc90mc9A11]A1)191I1Y1i1y1111111 ]A;&;6;F;V;f;v;;;;;;; ]A;;]DAиOJиbRиR/P_иcxZh+r6+GF+DA+FIиDKиAMиAQиD_иF`01%&&'>77&&'>77&&'>74.#"665#535#5!#3###32>%5##".54>3272P?IIS1!/l5#5#75##".54>32,gggNp?HH?RID<.& : 5LK512uBSm? RxTTTBCuYYuCCuYYuC&&X$$" 6[[8((lX75( -44-#'?m&&]((]&&YuCCuYYuCCu F!(/:AGjG% +"+DE+P+-1+)+Eи/кP9A%&%6%F%V%f%v%%%%%%% ]A%%]A))]A)))9)I)Y)i)y))))))) ]2и2/4"9:1-9;1-9A"9EBܸ1HкI 9M 9S 9g 91kкl 9xP9{EB9} 9 9к 9 9 9PиD7+>+k1+A++GB+"A9,A9HиMиOкSBG9gBG9и01#".54>32&&'#535#7664&'665!#3267&&#"##537#3#6677675"'&&'&&'67'&&'7#5#CuYYuCCuYYuCn 9CC9A99Am(3{EEz34KK5h8>  { =     $!   G &*-YuCCuYYuCCu$lA3C)6WV7"U67 '++&r-22- 3$D 1 #;# uQ   "  < C66F,@A#7+++ +-+и и и ииA]A)9IYiy ]A#&#6#F#V#f#v####### ]A##]-B(2+<++ +ии иܸ015#!5##5##5##3534.#"32>7#".54>32ΆeQPQU?mSSm??mSSm?CuYYuCCuYYuCu//GjSm??mSSm??mSYuCCuYYuCCuF![o^fz++N++95+=+p\+9к zp9zp9"zp9.zp9+0иN3и9Iи5KкLzp9W=9A\\]A\)\9\I\Y\i\y\\\\\\\ ]Af&f6fFfVfffvfffffff ]Aff]pEX%/%>Yku+a+%Q01667665#.'>7##".55'757577&'&&'#5332>574.#"32>7#".54>32+ T    $3# 5/ AQ,R69 ! M,   y?mSSm??mSSm?CuYYuCCuYYuC a $-/89% QYV!66#! Re >  F   Sm??mSSm??mSYuCCuYYuCCuF4Hc"?+ ++++2++5+ и ииA]A)9IYiy ]A"&"6"F"V"f"v""""""" ]A""]2-и+0ܸ5J':+D++/0++и/и и /и/01%5#75##3353#33534.#"3267#33#667#".54>32Y&&`L&KҼJ&L?mSSm??mS;k/5[ZZEQCuYYuCCuYYuCIٮ(حSm??mSSm? J7`YuCCuYYuCCuF"5<P+G++:4+=6+G=99G=9#4:9A+&+6+F+V+f+v+++++++ ]A++]A66]A6)696I6Y6i6y6666666 ]=R0B+L&+01%.'&&'>76655&&#"3267#4&'667#".54>32d&-( *. !/&  ^L3zESm??mSBv2OTGGTCuYYuCCuYYuC $056:91" -6< = Z=>n&+?mSSm?(#a77bYuCCuYYuCCuF #Ma7X++ +()+N$+A]A)9IYiy ]A&6FVfv ]A]) и /D9D/C"A$$]A$)$9$I$Y$i$y$$$$$$$ ]/)(9A7&767F7V7f7v7777777 ]A77]?)(9)@и@/CFи)HиH/(JиJ/K)(9Nc<S+]2+ A++H!+ +014&#"3267#".54>325!%4&'#3233&&#"3267#5!#3!53667#".54>320&&//&&0T-?%&>,,>&%?-dPDZ* 3~FSm??mS@s1D[[[EQCuYYuCCuYYuC%22%&33&#;**;##;**;XAA_7])-?mSSm?%";<7`YuCCuYYuCCuF #7Ku.B+++8$+A]A)9IYiy ]A&6FVfv ]A]A$$]A$)$9$I$Y$i$y$$$$$$$ ]A.&.6.F.V.f.v....... ]A..]8M3=+G)+ ++ +014&#"326!5!#".54>324.#"32>7#".54>32MHHPPGIMM1<\@?]<<]@@];?mSSm??mSSm?CuYYuCCuYYuC)55)(55M/<00<<00324.#"32>7#".54>32|#9)QEEQRCHZ[GM49[BB\99[BB[9?mSSm??mSSm?CuYYuCCuYYuCSLL  #$$'K,##,-##-Sm??mSSm??mSYuCCuYYuCCuF"=Q--H++ +>#+H>99H>9A##]A#)#9#I#Y#i#y####### ]A-&-6-F-V-f-v------- ]A--]5 9 6и6/:к;H>9>S2C+M(++:7+01&&'&&'>55#5!##34.#"3267#5!5!667#".54>32^ Q#(+( #=,X,:\\?mSSm??mS9j-=KXCuYYuCCuYYuC6" >5-#! (5=JJ<1$SuSm??mSSm?J7dYuCCuYYuCCuF /C-&:++ +0+и/ и/A]A)9IYiy ]A&&&6&F&V&f&v&&&&&&& ]A&&]0E+5+?!+ ++ + и и ии01%!53533533'!53'7373'!5!4.#"32>7#".54>32Zw[, ^ X Y _?mSSm??mSSm?CuYYuCCuYYuC+JIGSm??mSSm??mSYuCCuYYuCCuF1E<++-&+2+<29A]A)9IYiy ]A&6FVfv ]A]-(и&+ܸ2G"7+A++*++01&&'667>7#54.#"3267#33#>7#".54>32()He?' #)>,?mSSm??mS5++(!+++A]A)9IYiy ]A&6FVfv ]A](#и!&ܸ+@0+:++%&+01%#32>7%4.#"3267#33#667#".54>32gN`p=YEEB4?mSSm??mS9h.HZaaP`CuYYuCCuYYuC  w  ZSm??mSSm?J6hYuCCuYYuCCuF ,@7++(!+-+A]A)9IYiy ]A&6FVfv ]A](#и!&ܸ-B2+<+++%&+01%#!#2>7%4.#"3267#33#>7#".54>32k?}2#JFA-?mSSm??mS;k/JZ]]'?-CuYYuCCuYYuCH  hSm??mSSm? JGT`3YuCCuYYuCCuF #'DX2O++ +B;+E(+A]A)9IYiy ]A&6FVfv ]A]%9%/$A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]B=и;@ܸEZ7J+T-+ +'$+# +?@++01%4&#"3267#".54>327!5!'#534.#"3267#33#667#".54>32+$#++#$+T+<##;,,;##<+|P?mSSm??mS;k/B[ZZKXCuYYuCCuYYuC++ ,, !7''7! 6''6M'KSm??mSSm? O7dYuCCuYYuCCuF#c++ ++ и%+ +ܸ 01%#3!%5##37#".54>32˓ZOZ[[CuYYuCCuYYuC99Jz< YuCCuYYuCCuF ' ++++ии и)+#++#ܸ и01%#53##5#%5##37#".54>32̕XXWMZ[[CuYYuCCuYYuCע)Jz:YuCCuYYuCCuF#+?K(+,++(9,$+&и,A)1+;(+01%&&'&&55667'>76675#5#3#".54>32W6M_ &/! .) (- !Z[[CuYYuCCuYYuCn>=Z = <6- "19:650"JzNYuCCuYYuCCuF'/C:+ +,+0/+A&6FVfv ]A]A&6FVfv ]A]0(/*и0E#5+?,+?ܸ5-01#".54>324.#"32>5##37#".54>32     U(A-.@))@.-A(Z[[CuYYuCCuYYuCQ#>-->#">-->"1YD''CY12YC((CYKz7YuCCuYYuCCuF (<y 3+%+)(+%9%3 93)9)!(#и)>&.+8%+801%.5535!3>76675##37#".54>32O'9'||%<+ +''* [ZZCuYYuCCuYYuCHSW)9KK8)\XM !/6963.#K y2YuCCuYYuCCuF $,@ 7+)+-,+)9)7 97-9)"ܸ7$ܸ-%,'и-B*2+<)+!+<"01%.5535!3>7665#5##37#".54>32M&:'(=( ,('+ K[ZZCuYYuCCuYYuCx:FL #LL$ OJ= ! )131.' #JJJz,YuCCuYYuCCuF,g#+++#9 #9и.+(+(0137675#5#37#".54>32"qK& Z[[CuYYuCCuYYuC32QDHI"}A\[[CuYYuCCuYYuC IxHJzEYuCCuYYuCCuF +c"+ ++ и /и/-+'+' 01%''5!5##37#".54>32mdRDUZk\[Z[CuYYuCCuYYuCoF  )OGGJ z2YuCCuYYuCCuF #'/C#:++,%+0*+A&6FVfv ]A], :ܸ,!:'ܸ0)*.и0E5+?,+$!+ +#+?%ܸ5-01%#"&546324.#"32>75!%5#5##37#".54>32+$#++#$+T+<##;,,;##<+,Z[[CuYYuCCuYYuC ,, ++ 6''6 !7''7MMtKKOz!)YuCCuYYuCCuF #m +++ +ии%+++ܸ и01%#53##5###".54>32ИVVWZCuYYuCCuYYuCע)zGYuCCuYYuCCuF';׺2+$+(&+$и&и$и2 ܸ$и( &"и(= -+7$++++% + и и/ и/- 7ܸ7"01%!5!#!5#35!#535!2>775#5#35#".54>32ZZ[[)BFG!=i\R&UZZCuYYuCCuYYuC5E<;  H  RIqYuCCuYYuCCuF3ݺ*+++++ + и иии и 5%+/++и/и/ܸ и / и /и/и/%01%#53#53##5#%##5#%5##37#".54>32L&&&&K&LL&JZ[[CuYYuCCuYYuCٮ'ٮ(Jz:YuCCuYYuCCuF -$++ܸ$ܸи/ / +)+ +++ +и)ܸи 01!5!5!5#%5#53#!5#3#%#".54>32{ j[Z[CuYYuCCuYYuC? KpH>??>H_YuCCuYYuCCuF1(+++ܸ(ܸи3#+-+++ +-015!5##535!2>775#5#35#".54>32Z2BFG!=i\R&UZZCuYYuCCuYYuC J{  H @L+?YuCCuYYuCCuF*26JA+0+6++73+7ܸ2ܺ29 и /и/A9#A792-и7L<+F5+++5,и,/3015!5#.56674767'>766##3#%#".54>32[-"S>: !  VPPXCuYYuCCuYYuC J>*6=!   Ju ! %)(#%lE?YuCCuYYuCCuF"&:Q;/$/;и/%ܺ%9 и /$'ܸ<&,+6%+01%&&'&&55667'>76##".54>32[6L^  &/! .* (-[CuYYuCCuYYuCn>=Z = <6- "19:650$zDYuCCuYYuCCuF5I+@+ ++6+A&6FVfv ]A]и/A+&+6+F+V+f+v+++++++ ]A++]4и6K0;+E++E&ܸ;501#".54>32#6654&'3##.#"32673#".54>32       aaZZs $.-@*)A-:JqCuYYuCCuYYuCU#>-->#">-->[W)'CY12YC(?4EYuCCuYYuCCuF*.@H  +/9+AD+.+,+AA & 6 F V f v ]A ]A)A/&/6/F/V/f/v/////// ]A//]DGиG/*+-+HA+-и/ܸ%ܸ.01#".54>32##&&#"32673##".54632#6554'3CuYYuCCuYYuCTI <3$5#"4%6; IW   AA-YuCCuYYuCCu|e#".54>32MIGPPHHM;]@@]<<]?@\<CuYYuCCuYYuC(55()55[MM|<00<<00#".54>32{ CRQEEQ)9#HA[ 9[BB[99\BB[9CuYYuCCuYYuCMLY$$# KK-##-,##,YuCCuYYuCCuF/3GE&>+ +2+40+2и/A&6FVfv ]A]0и/2и/>ܸ2и/A&&&6&F&V&f&v&&&&&&& ]A&&]4I9+C!++++ +9и+и/301%!5!#"&54632#!5#35!4.#"32>#%#".54>320&&//&&0[[[d-?%&>,,>&%?-ZCuYYuCCuYYuC;AG&33&%22<;#;**;##;**;]YuCCuYYuCCuF&*>5+#+!+*++(+5 ܺ595+9!%и+@&0+:)+#+:ܸи) и /0*01%&&5535!3>766##3##".54>321D]] 0   |VZZY CuYYuCCuYYuC#b^IIa/VI7 ",2-( yfHzEYuCCuYYuCCuF *> 5+++5ܸ++9и/и/5+959+@0+:+++ +ܸ ии015!5#%5#5##%&&'35!3#>766#".54>32[[A[z. %9N3"HA35?H$CuYYuCCuYYuCKHTTH7*II$ "!"mYuCCuYYuCCuF (<ź3+ +)+3ܸ))9и/и/ 939)>.+8+ ++ +&+ иии&015!5#%5!3357"&'35!3#>766#".54>32Z Z[x.od"HA35?H$CuYYuCCuYYuCKLL{{7*II(9!"! nYuCCuYYuCCuF / &++ +&ܸ и/и/1 !+++ + +и и и 01%5#5##5##%5#7'#'#5!#".54>32[wZ!_ Y X ^CuYYuCCuYYuC(JJIIGGYuCCuYYuCCuF #'3G#>++.%++,+4)+A&6FVfv ]A]. >ܸ>'ܸ,0и+2и4I9+C.+$!+ + +C%ܸ.)и)/9/ܸ9301%#"&546324.#"32>75!75####33#".54>32 $$ Q#5$%5##5$%5" W0WW0CuYYuCCuYYuC(0.*).-*8--89,,9JJuHH FYuCCuYYuCCuF E+ +  ܸ +++01%5!%5!5#%#".54>32 ZCuYYuCCuYYuC3KKJYuCCuYYuCCuF 1(+ +ܸ( ܸ(ܸи/ и3 #+-+ ++ +и/ и /-ܸи015#7'#'#%5!%5#535!5#3#%#".54>32bY { Zb5 ZCuYYuCCuYYuCJV `` VJGGwG;C~;GYuCCuYYuCCuF(0D%;+1/+/19/и/;ܺ;9;191)/+и1F6/@-++%+ ик9@#ܸ@+ܸ/ܸ001%.'35!3>7665#66775#5#35#".54>32-I5!@ 4I.>7. ,8= ?B[~=UZZCuYYuCCuYYuC+"*JJ*"% )(!#$0LlYuCCuYYuCCuF -$++ ++ܸ$ܸ иии/ +)++++) ܸ) ܸ ии/01#535!5###5#%5#5#35#".54>32ΐZWVFUZZCuYYuCCuYYuCI_PJD^^]K+6YuCCuYYuCCuF.]%+++ %9и0 +*+*013>75##37#".54>32,>)# '?eH) Z[[CuYYuCCuYYuC775##37#".54>32MBEEY=p`NaZZCuYYuCCuYYuC  *  Jz:YuCCuYYuCCuF )[ +++и+ +%+%ܸ01%#35!26775##37#".54>32SAFJ#}?]ZZCuYYuCCuYYuC  KH"Jz:YuCCuYYuCCuF- $+++  ии/+)++ +)ܸи/ и/01%#53!3#6775#5#3#".54>32UDHH!sBZ[[CuYYuCCuYYuC  IJzK YuCCuYYuCCuF*.B)9+ +/,+A&6FVfv ]A] 9/.ܸܸ.9$ܸ/D.4+>-++>01#".54>32'56654.#"##".54>32 $##$~YEX;/;>Y+DA+4CS01&&54673%!>76654&#"#4>323#>7#5!#>54.'38==8+'',<$+,41& 1'8%?S *6IJ)@-7&6A)s=8+' '+8=UU76654&#"#&>32#".5467&&54>3227#>54.'3"E22ED33D30/49*):8==8+''"+9J-=&-/73( 0);&!7)g.>$$>-:+!0&7#"7'0!%X=8+' '+8=AAAAABB`-::-677UU+C--C+9V H06((60D,8eU+"5+ _+HY+ir+AYY]AY)Y9YIYYYiYyYYYYYYY ]Yи/>=9A&6FVfv ]A]!>=9A"&"6"F"V"f"v""""""" ]A""]=*и*/"+и+/>-и-/A__]A_)_9_I_Y_i_y_______ ]mi9Arr]Ar)r9rIrYriryrrrrrrr ]wi9i|EXM/M>Yd+ \+MTAT'T7TGTWTgTwTTTTTTT ]ATT]mܸиwܸи8иdCиC/014.#"326&&546733!>76654&#"#4>32#"&'332>5#"&54>32#>54.'3*):33=8==8+''!+7K-=%,,52' 2':% 7)d+@)BM25+ - <0HU):$'@,[=8+' '+8=8++9GTVUUGrO+TF.=&Fc=*>k_,J6 IvZU&&54673#".7332654&##532>54&#"#&>32%#".54>32#>54.'3' , !- -! , 8==8+''0>"$>.3B46B>1%%"15(1):#GS2#07e*>)*?**?*)>*T=8+' '+8=,6^F((F^66]E((E]eUU7#3e7wsi(dd(iswn}‰}+CNR&CC&RNC;r]J]rL//и/иܸܸк9//++9 99901%5#553'#&$'6$737d)hsw77wsh)~}}‰C&RNCCNR&x]r;;r]36( +++ (9 (9 и ии и ии и ии и(и -ܸи+и( и-!и+"и($и-%и+&и(,к2+901>7!#7##7##7##7#53'33'33'33'3!.'FJD!! IHGJI@IJFIIKKJJFKJ@JJGIJ^ !!DJF,0/) *,**,( )/02  //+ +01'!5%!5!7lY|4%-%2 //+ +01%!!5!!Yl|4Y-Y2 //01%5%J+ii\2 //01'7yJJyVnii3 / / 901667&&'667&&'.A#II#A.+yJH88HJ2 //901I#A..A#I+8HJJH82<"#/ /!+и!01'!#5667667#53&&'&&'52!7>B'1g*#==#*g1'B}>,yl;R!(C!!C(!R;l2<"#// + и01%"&'&&'!'7!6676633#*g1'B>>}B'1g*#==#<!R;lyyl;R!(C!!C(2'  /+90153&&'24{nIN'TG'\( #H N*2' / + 901!667267667}GT'NIn{4'*N H# (2+//01%!!  2+//01 !!s ;ff VA//и/ ܸ/+ ܸ01#4.#"3'34>32 ,C--D-yz3P88P3/P9 9P/3YB&&BY3V=//и//+ܸ01'34.#"#4>32y-D--C, 3P88P3 /P9 9P/I3YB&&BY3W()//)#и#/A]A)9IYiy ]#9#A&6FVfv ]A](#9(/+'+015#"##532654.'.546335ׂlYT*6!A4 z||nj-; <0jcnzyYN%9/&*2?*hgV["6,%+5C,`eyW()//)и/A]A)9IYiy ]99$A$&$6$F$V$f$v$$$$$$$ ]A$$]/(++01#"&54>7>54&##'73233|z 4A!6*TYlncj0=;-jn|gh*?2*&/9%NYyye`,C5+%,6"[VW#273/0/ܸ3и/ܸ4++ +01%#".76.#"3'354>3232>553#->$&?' e 22%yy-=!&?) e 1 3%2I0%AZ5+O<#*?)110H2%BZ4B,O;#)?+1M+ +  // ++01%5!7'7'!!yOOwwflFFlYYlM# + // ++01!!'7!5!'7zOyOwfw,4FlF4llVM/// и /ܸ / +01%737'#'(FlkllYO Qw-ӪwGM/// и /ܸ /+01%'#'%737(FlF4YldO Ow-M m//и/иܸ ܸк 9 / /+ + 9 901%5!55!!G!-aaM M//ܸи и /ܸ к 9/ / + +01!!!5!x+-+ԆVU G// и /ܸ / ++и 01%33!#! Ԇarp!QU G// и /ܸ / ++и 01%###%!3 ԆaXr!p7//+01%!!owwܝlYYl7//+01'7!5!'7wow,ll266+4+к69ик 69 и к 69ик696969 496%и4'и6)и4+и6-и4/и6101##'##'##'##'!.'>7!733733733733KIIFJI@IJGHI !!DJFFJD!! _JIGJJ@JKFJJK *,* )/00/) (,*=M'+//9901'#'MllYww:M4+/EX/>Y9901%737MlUwF//+01%!.'>7! *cjk33kjc* "U[V5omf--fmo5V[UF// +01>7!5!.'3kjc* " *cjk*-fmo5V[UU[V5omfPL ;+//999 901.'#>7V[UU[V5omf--fmow y *cjk33kjcPL ;+//9 99901%.'3>5omf--fmo5V[UU[V*cjk33kjc* y F9?+;$/0/+9+$09$0901%5!667667&&'&&'!&&'&&'667667!  #Q,'P&&O',Q$  F  0t=.Z,,[.=s0  "D%&F-[-)L""K(-]-I'#@/?>6=>.V&&W.>~=67667!5!&&'.'vMP,Q#  ,  $Q,'OQ,Z.=t0  "  0s=.[*DS-[-F&%D@#'I-]-(K"&V.>=6>?/0?<6=~>.WPL 8/9//9$и$/ܸ#3/#+01&&'&'6676673.'&&'#667667@-[-SD"K(-]-I'#@D%&Fp66=>.V&&W.>~#Q,PM&O',Q$  o  2  y  0t=.Z,,[.=sPL 9/://:/и// ܸ2!/1+01%#&&'&&'667667&&'&&'3667>@F&%D@#'I-]-(K""L)-[=~>.W&&V.>=6>?/0?<6  o  $Q,'O&&P',QP0s=.[,,Z.=t0  y  Mf& ++!+A]A)9IYiy ]A]A)9IYiy ]!!(// +и ии $01%'7#"&54632#"&54632#"&54632&%%&&%%&&%%&f%%%%%%%%%%%%Mf #&-+ ++A&6FVfv ]A]A & 6 F V f v ]A ]A]A)9IYiy ]%&/%/ +и ии !01#"&54632#"&54632#"&54632%&&%%&&%%&&%+%%%%%%%%%%%%/ #&+A&6FVfv ]A] иии&/ +$!+ ܹ01#"&546325#"&546325#"&546327!73%%%%%%%%%%%%v3&&%%&&%%&&%%/s& +A&6FVfv ]A]и ии !/$++ ܸ01%'%#"&546325#"&546325#"&54632%%%%%%%%%%%%(%%&&%%&&%%&&Em //01%!!|Em //015!!5,A5M //9901#!#C|5M //9901%3!AA|CN m//и/иܸ ܸк 9 / /+ + 9 901%!55!!%e ۠ffN M//ܸи и /ܸ к 9/ / + +01!!5!!5tK+%%>VZ G// и /ܸ / ++и 01 3!#!#۠ f%GGeVZ G// и /ܸ / ++и 01%#!# 3!%f>G?eGMO 1Y' +1++  и'#к$ 93$/1+#+' +01#552>7>57#'72>7>57 0')gvB}}Gv++5  :.051qoc#$. Ts.0M5NssN!=W67V`:<[<qo3H+-n^M8 1j &+% ++и0к1&931/EX%/%>Y0++% 01'"34>7>375"#4>7>35s}Gv++5 F 1')gvB1qoc#$. 9/05YsN!=W67UTr/0M5Nso3H+-n^`;<[<q!r:51qrl*,J6<9-\ $++-+-/EX/>Y% + $+-+и-01#4.'.#2#7'34.'.#52oN-D-.rUTw`'QPNso'9$&Zu^&c2F+"G{45V>!F8N/b}1qrl*,J6r:5M8 1j' ++1+  и'#к$ 1913&/EX/>Y' +#+014.'.#5524.'.#'72} 5++vG}}Bvg)'0  .$#coq150.: U76W=!NssN5M0/rT^n-+H3oq<[<;`MO 1Y %+& ++и0к1%93/& ++0+01'".'.5#375".'.5335s}Bvg)'1 F 5++vG50/9 .$#coq1sN5M0.sTV76W=!Nsq<[<:`^n-+H3o<9-\ %+!+-+-/EX#/#>Y-++% +  и%!01"#7#467>3"3'34>76$3Vr.-D-NssNPQ'`wT^u[%$9'oq+F2c&!>V54{G}}b/N86J,*lrq15:r7>5##52>7>5#7ossNPQ'`wTUr.-D-q+F2cڿ^uZ&$9'oz}}b/N8F!>V54{G5:r6J,*lrq1W` +/ / +01#'72>7>5&=-i𓎎Bx3,<$ Zv6~ =[;3vSW8( +/EX / >Y+015"#4>76$35Bx3,<$&=-iN =[;3vRZv6~U + /+01".'.5#7#3H{k*9J,*G5(hwE}0D,:YS7)C/U9,+EX/>Y +01'34.'.#52*G5(hwEH{k*9J,9R7)C/0E+;XW80+ /EX/>Y +014.'.#'72v$<,3xBi-=&Rv3;[= ~6vZW`  + //+01%5"$'.5335i-=&$<,3xB ~6vZSv3;[= U9$ +EX / >Y+01"3'34>7>3Ewh(5G*,J9*k{H/C)7RX;+E0U+/ +01##52>7>5#7,J9*k{HEwh(5G*Y:,D0/C)7SF+ +  // ++01%5!7'7'!!|J|Ghm||mF# + // ++01!!'7!!'7|+|k+mmWLL /// и /ܸ / +01 737'!'x||m⦑J|pihLK /// и /ܸ /+01%'#' 7!7xmmYW|p|Ghihu:$++и/$ܸ и /и/$и/$SиS/OS9O%и+и+/0и0/D9IS9o$9$9и/$9и/и/и//+и/01%3#536'.54>7>324632666'6''&''&''&&5#".'&&''&676&''&>76&'&"'35"4766'&&'&&#"6765#54&'&#"7666&'&'&'4&#&326766554{           &              9   r    ;FED69;&QI8 ( ('##(#  .20     -&  %      ! -2/S%M83*1,>v-`((R0!   9 :  ;"   "h:N&++и/AN&N6NFNVNfNvNNNNNNN ]ANN]&N9/ и /иNи/Nи&!и!/-и-/2и2/FиF/k&N9и/9A]A)9IYiy ]9Nи/&N9и/и/и/и/&//.+.1и1/.G01&''#"&5#".'.54>76'#53#'6'&&'&425#62766'.7666'&&76667>324676676676&'&'3267>5#4&'&3276657&767666554&'&&#"726{                  &        9   v      #(##(( ' 8IQ&;96DEF;/2.f GS/2- !      %  &-   -v>,1*3.)$((   !@: 9}!   ";  D`<5$++и/$ܸ и /и/$ и /$&и&/$VиV/ܺ'V9-и-/2и2/G9L9'Rкr$9$9и/$9и/и/ии//+*+и/к-*901%3!536&'.54>7>324632666'6''&&''&''&&5#".'&&''&676&''&>76&'&"'35"&7>'4.'&&#"66765#54&5&#"7666&'&&'&'&&#&3267>& "$        / $               H   "   JX WU$DIK$0f\G1$(3 1,2, ':?;    '  90 "&$.     *9?;# +8F.3:#@6>8N822<* '( I  H   #J, '**Dx<'++++A]A)9IYiy ]и/ и /A&6FVfv ]A]и/и/'"и"//и//4и4/HиH/o9и/к99A&6FVfv ]A]и/и/и/и//10++ 903и3/0I01%&''#"&5#".'.54>766'#5!#'6'&&'&625#62766'.7666'&&76667>3246766766676&'&&'3267665#4&'&3274657&76676646&&'&&#"726      $ " (         $ /         H"      ' ,2,1 3($1G\f0$KID$UW XJ;?:Xk$ #;?9*     .$'" 09  '   8N8>6@Fa22 '( *P% H  I**& ,J#  F1E(<+ ++2+ 9/ܸA]A)9IYiy ]A(&(6(F(V(f(v((((((( ]A((]2G-7+A#++ ++ +01!53!'#!#2>7##334.#"32>7#".54>327Zo'Q\h>1 GGBU[[Ua?mSSm??mSSm?CuYYuCCuYYuC ۑ  LH  ׳Sm??mSSm??mSYuCCuYYuCCuF )-5I]@T+ +01+J6+1ии0и/1к TJ9+ 9+/*ܺ+*9TJ91.ܸ03A66]A6)696I6Y6i6y6666666 ]A@&@6@F@V@f@v@@@@@@@ ]A@@]J_EO+Y;+ ++-*+"!+5.+ и!$и5%и%/-2и2/!4и4/01%5!#5!#5!'.'&'>7#5!#'#53##334.#"32>7#".54>32~d[Z))$ #()#8*|S|(5BAVZZVW?mSSm??mSSm?CuYYuCCuYYuC@ZZY_&#'!*"-1GG,'EzSm??mSSm??mSYuCCuYYuCCuF0DX%;O++E1+OE99OE9и/ и /&и&/A11]A1)191I1Y1i1y1111111 ]A;&;6;F;V;f;v;;;;;;; ]A;;]EZ@J+T6++и и /01%!53533'.'&&'>76654.#"32>7#".54>32\  GC;;AA 4XC*^6@G?mSSm??mSSm?CuYYuCCuYYuCDL(!-7"8, )>Q/ 6,GSm??mSSm??mSYuCCuYYuCCuF 9MaɺDX+++N:+A]A)9IYiy ]A&6FVfv ]A]XN9/XN93и3/5и5/7XN9A::]A:):9:I:Y:i:y::::::: ]AD&D6DFDVDfDvDDDDDDD ]ADD]NcIS+]?+ +43++ + и/и3601%4&#"3267##"&54675#5!'.'&&'67#5!#4.#"32>7#".54>32KFGKKGFK[f~op~d\M $H?54@H"enҷx?mSSm??mSSm?CuYYuCCuYYuC_""!!'B532#34.#"32>7#".54>320&%00%&0 ;_j-?$$>-.>#$>.ZZ?mSSm??mSSm?CuYYuCCuYYuC -.!..C"J'!8))8!!6''6ESm??mSSm??mSYuCCuYYuCCuF':AU0L+ ++?9+B;+A]A)9IYiy ]A & 6 F V f v ]A ](9?9A0&060F0V0f0v0000000 ]A00]A;;]A;);9;I;Y;i;y;;;;;;; ]BW5G+Q+++#+014.#"32>7#".54>327&&#"3267#4&'667#".54>32 !   ! W+A,,B++B,,A+3zDSm??mSBw3KQEEQCuYYuCCuYYuCP#=..=#$>..>$2ZC''CY32YB''BY&*?mSSm?($`77`YuCCuYYuCCuF )-1EYѺ<P+ ++F2+9/A]A)9IYiy ]A&6FVfv ]A] 9/A22]A2)292I2Y2i2y2222222 ]A<&<6<F<V<f<v<<<<<<< ]A<<]F[AK+U7++1.+-*+%+ + ии 014&#"326##5##5#5!'#".54>327!5!'#534.#"32>7#".54>32M=?NM>?MZx[M>oy54.#"6766323!267#!"&5463!2^"_M4]E(&D_8BaA"c^VT1? Db- CKHFEIBJD4N"1K;+@*'9A =L@3&3# A%)'! DFCGF@JFDEFVKV+VܸVXBJ+S +)3+!+)&и&/01%4&'6654&#"676632'&76#".'&&32>#!"&5463!2 E?/?n6U?) ]<1A&3,B`X61&&NIG+N+NܺGN9NP:B+K+ +01%4.#"7663!2654&#!"67>32#".'&&32>#!"&5463!2'CY1Oe!P&! ".<'XS"4B<4+ =cH6`I*CKHFEIBJD7R5"' #   PI(9$ *  64&:UDFCGF@JFDE?OUP//PGиG/@ܺG@9G8ܸ@Q;C+L3+$+01%#".'&>324.#"&47>3266'.#"32>#!"&5463!2"9I'?5$/Q?QbL&D^8;P2H2%7,% +:K0GkI%5bK,CKHFEIBJD/;" #9)1- BN6O5!@1 # "%3ZyF7Q~DFCGF@JFDE"+/+016&#!"3!67%#!"&5463!2&,  CKHFEIBJD &)! DFCGF@JFDE AQe8I+Q&+Qܺ#IQ9I0ܺ3IQ9QS=F+M++ ++01%#"&54632#"&'4>324.'6654.#"32>#!"&5463!2fqpehm:Q4&ZUNb)D21D)r)427<_EE`<723)+Mk?@lL+CKHFEIBJD9CC9;B / -88-$$';*I1;//;1I*;'5G+,GfDFCGF@JFDECSiT//TLиL/ܸSܺ(LS9GиG/PиP/SU?H+O+#0+01#".54>324.#"326767#".'&32>#!"&5463!2/P>%B12A#-L7U#GlI3^H+(E]6:P0J6%6,% ,;K-LlE CKHFEIBJD.,3$%:'#:yQrG!9P16O3!>0 " #"2[DFCGF@JFDE#7T?K+8+++ +PиP/01%#!"&5463!24.#"32>'#".54>324&&'&67732>CKHFEIBJD^2O78M00M87O2I 3$$33$$3 \b   ;  DFCGF@JFD@tW44Wt@@tW41VtD5]E((E]56]E''E]   V2 E.K!/+ /G+G*01%#!"&5463!24&&'&>7732>%4&&'&67732>CKHFEIBJD b   : o b   :  DFCGF@JFD   V2    V2 E?^gU+,@++@2и2/69`/+ +/ZиZ/01%#!"&5463!24.#"67>323!2654&#!>7>4&&'&>7732>CKHFEIBJD^3H*;Q5#1"5%4.'>54.#"67>32'&76#".'&&32>#!"&5463!2S a   ; &  5F(-D0+6@O&A@"*7'6 ,'"6G-+O=$[CKHFEIBJD+ U3 $7( !.!%;'*2 6&WB6!/ )!9+1HnDFCGF@JFDE0MPWD+,+,кO9P9 /-+-$иI01%#!"&5463!24&##4&'&33326553264&&'&67732>%#CKHFEIBJDT   'b   ; y DFCGF@JFD8"  4]]V  V2 SENkkb+*P+"+9mg+ %++Ja901%#!"&5463!24.#"766332654&#!"67>32#"&'&&32>4&&'&67732>CKHFEIBJD\6J--! ) "+?A(4:=1I4)L:" b  :  DFCGF@JFDw4O5  %+$  NE#:*43 6- ;To  V2 E=KhO_+4M++99+ /+ +^ 901%#!"&5463!24.#"467>3266'.#"32>'#"&'&>324&&'&67732>CKHFEIBJDX5F'BI$3#'  +;'1RCKHFEIBJDe#2   b   :  DFCGF@JFDF(($    U2 E/;Xl}O+&<++9< #9n++ +93+h^+N^h901%#!"&5463!24&'6654.#"32>'#"&546324&&'&67732>#".54>32CKHFEIBJDU?3&50L54K15&2@6T;'#"&54>324&&'&67732>CKHFEIBJDVmn-K6!4C"EN#3")>XK:S6J$:+9B%/FJJ b   :  DFCGF@JFD";R02L5+$#:)&0  *91\0'JH&8$TB   V2 EK_oV$+ +oL+A ]A ) 9 I Y i y ]$и/AV&V6VFVVVfVvVVVVVVV ]AVV]oqGd+lQ+Q'и'/d[01#".54>324.##67667>54&#"67>32332>4.#"32>#!"&5463!2 $$ $$  +3%SG0=% !(.)/+  q'A//@'%A3.A'PCKHFEIBJD-8]E&&E]87^D&&D^ < 55<&MV&6< )!-7!92.&>4."FxX11XxFGwX11XwDFCGF@JFDD|3//и/ //015!!!'`NNv`D| ? + +  ܸ  + +01%5!5!!!'"`WFFeFF`D| _ + +  ܸ  ܸ  ܸ  + +++01%5!%5!%5!!!'[`AGGGGGG`D|!)-%+++ ++*"+99&и(и*/(*+-#++*"ܸ%01%!3>5332>7&'&553!35!!!`i.,$ .&")' JJ`TYw.  @FN/#.& #(A''`D|+ +9 ܸܸ+ ++ +и  иии01#75#7#735!3#3#!!j &K#$n&`FٌFFFF`D| #G$//$"и"/ܺ"9#ܺ #9% /#+01>7&&'665#5##!!v]F$ ;1% uK_E *5= $J`OH/ =FL&/yK&NF<4J}}J`D|%)$+$&/(+01'5#7667667&&'"&'&&55!!-J .*;@:#) +  &(`nG+J;.K< #;`D| /+01667.'#366!!hJ' Khs2S<"Ѝsh #|`y= Hy"To]JH`D|'+'*+*9#(+* +01%&&'#"&5#5##3667332>!!=* J"3C(' Qj T .! 1#n` C0%#J3UIA Eq( ":`D| ;// и /  ++015!#!!3!!&J J`JJj`D|W//и/и/ и/+++01%5!5!5#!!35!!'bJ J` FFaJJ`D|++ и/и/ܸ+ +++ ܸ ии01%5!%5!5!5#!!35!!'"!J J`DDDDHH`D| ++ и/и/ܸܸ ܸ++++ + ܸи и 015!%5!%5!%5!5#!!35!!'[0J J`==w<7&&'#"&553!35!5!5#!!35!!`k1+! -0/P% JJcJ J`'/D !Y/aE   X=B~~B`D|!%&//&$и$/%$%9и/$%9$%9$%9$и/%'"+%++ + +и  ииии%ܸии  01%#75#7#735!3#3#5!5#!!35!!j "O""j"bJ J`eKK6K77K6K6B~~B`D| )-%,+*+,%9 *9,ܸ**/*/EX/>YEX%/%>Y-!+(+*9 *9ܸии-ܸ#01%667&&'66'5#5##5!5#!!35!!v[I) C]wHdC '3< )JMJ J`/W b3S05/'  :#) (  &(^J J`6ms7]#"3#    RCB~~BD| #'q(//(&и&/и/' '9)$/'+#+'ܸи#и"01%667.'#3665!5#!!35!!jK( Hfy0R=#Ѝ v] &J J`Le "zK3DX:8[* B~~B`D|%156/'/64и4/'54594-57!2+5)+0 +. + и.&и5'ܸ+и /01%&&'#"&55#5##3>7332>5!5#!!35!!=* J bO(F5#T3C 1#J J`8*JJ9?Z  27&'&&55'753%!!!-(&  + )#:@;*. J]aB`n-# 7#535333267!!!=#1 !. T jQ '(C3"J *YB`.:" (qE AIU3J%0C &`D| _++ +и и + + +и 01!#!5!3!!!!&J J `B`J=&`D|i+++ и и++++и 01%!5!!#5!5!53!!!!'cJ J `B` FJ&`D|+  ++ и иEX/>Y++++ и01%!5!'!5!7!#5!5!53!!!!'cA@J J `B`DRDՒH&`D| }+++ии!++++ + + и01!5!'!5!7!5!7!#5!5!53!!!!'c_[-1J J `B`=:<9=F||&`D| (48<8:+%&+++!"+95+"к :99 :99:99%и+,и,/0и0/,1и+2и9>59+<6+#++4)+(+к9и)-и4/01%5#3267#"55#&&'665##5!#!7!#5!5!53!!!!Ë %P/0- !+1kJ`J4J J `B`'X   Ea/Y! D/K=B~~&`D|!%)ͺ%'++&"+'&9'&9 '&99ии&+"&+)#+++!+ +иии и ии!01%#3!537#537#5!#33!#5!5!53!!!!j""j""J J `B`eK66K6K77KB~~&`D| )-1-/++.*+/.9 /.9и!и%и'и.3*.+1++)++и"и)$01%&&'667.'77!535337!#5!5!53!!!!]C )I[) <3' Cd%JJ J `B`|3b W/ '/50S7##"'&&55'753%7!#5!5!53!`(&  ( )#: >*.J]J J `& R    #3"#]7smB~~D| #'+')++($+)(9 )(9и/и!и(-$(++%+#++и#01%&&'667&&'#53!#5!5!53!!!!fH (Kj& ]v #=RJ J `B`Kz" eL *[8:XD3B~~&`D|%15957+++62+769и/)9)/()-и(/и6;26+93+ +1&+ + 9 ии&*и1,01%#"&55#&&'667#535333267!#5!5!53!!!!=#1 C3T#5F(Ob J *J J `B`8% % D<2  Z?9JJ*B~~&`D| s+++и и+++ + +и 01%!#5!5!53!7!5!'!5!!!!&J J cAB`oHDVD/&`]}+  + ++ и и и и++ +++01%5!%5!#5!#!!!!0]--.hAAJm .]"&"$++ +#+$#9 и /и/#(#+& + + ++01%#'65!3#3#'>5!!!!"07.2) p.28 x+w+[{'4F^B!X .]'+/i+-++,(+-,9 9-,9и/-,9,1(,+/)+01'667.''>55'667!!!X 1%)MA0 f$He@1+I`4 7)5.B[ Y?!AKR+a++Wjt36k`O8_\7 .]/37{35+ +40+549 549549"и"/$к/5494904+71+ +01'>7#537.'#'657!!! (7E*);;5T+J>0 &/&3 !/{93.+b`Z$"=Tk@* lHNO# 9+r  'Wr .]"&*&(++'#+('9ии/к('9"('9',EX/>YEX/>Y#'+*$+ии01%.'#'>7!5!5!!!!! *PE5-/?P.,)YN; 2 7M_3;.T?IM%{%LID&IX`+*q*%VUK .]37;79++84+ ик!9'98939898=48+;5++ + +и и ик3 901%'7!5!5#535#5!#3#3''77.''>7!!!'<)tyWH$=)SK<KX/;.Uc+*M**M*sjpj:CF!KH@$AQ^1%HA4] .]}+++и и / и++++и и 01%!5!#5353#!!!!"1K.* ** .I#7 .+  + +$+ и и и A]A)9IYiy ]A&6FVfv ]A]$9)+3++ ++01%5!%5!#5!#!4.#"32>7#".54>320]--?lQQl??lQQl?CtXXtCCtXXtCpAAJQl??lQQl??lQXtCCtXXtCCtI2F)=++ +3+=39 и /и/A]A)9IYiy ]A)&)6)F)V)f)v))))))) ]A))]3H.8+B$+ + ++01%#'65!3#3#'>5!4.#"32>7#".54>32"07.2) p?lQQl??lQQl?CtXXtCCtXXtC:8 x+w+Zv'2C]B!Ql??lQQl??lQXtCCtXXtCCtI';O2F++<(+F<9 9F<9и/F<9A((]A()(9(I(Y(i(y((((((( ]A2&262F2V2f2v2222222 ]A22]<Q7A+K-+01'667.''>55'6674.#"32>7#".54>32Z 1%)MA0 f$He@1+I`4 7)5?lQQl??lQQl?CtXXtCCtXXtCB[ Y?!AKR+a++Wjt36k`O8_\7Ql??lQQl??lQXtCCtXXtCCtI/CW :N+ +D0+ND9 ND9ND9"и"/$к/ND9A00]A0)090I0Y0i0y0000000 ]A:&:6:F:V:f:v::::::: ]A::]DY?I+S5+ +01'>7#537.'#'65774.#"32>7#".54>32 (7E*);;5T+J>0 &/&3 !/{9I?lQQl??lQQl?CtXXtCCtXXtC+b`Z$"=Tk@* lHNO# 9+r  'WrQl??lQQl??lQXtCCtXXtCCtI"6J-A++7#+A79ии/кA79"A79A##]A#)#9#I#Y#i#y####### ]A-&-6-F-V-f-v------- ]A--]7L2<+F(++и01%.'#'>7!5!5!!74.#"32>7#".54>32 *PE5-/?P.,)YN; 2 7M_3M?lQQl??lQQl?CtXXtCCtXXtCN?IM%{%LID&IX`+*q*%VUKQl??lQQl??lQXtCCtXXtCCtI3G[;>R++H4+ ик!9'RH93RH9A44]A4)494I4Y4i4y4444444 ]A>&>6>F>V>f>v>>>>>>> ]A>>]H]CM+W9++ + +и и ик3 901%'7!5!5#535#5!#3#3''77.''>74.#"32>7#".54>32'<)tyWH$=)SK<KX/M?lQQl??lQQl?CtXXtCCtXXtChc+*M**M*sjpj:CF!KH@$AQ^1%HA4327[[7_?lQQl?1,1-2CtXXtCCtXXtC,@LL@Ql??lQH4( ***4IXtCCtXXtCCt]+ + ++ и ии и /++ +++01%5#75##5##!!!!f?WS6.z蕕''641.](,0,.+&++-)+ии/ии/.-9.-9!.-9-2)-+0*+(+++015#75##4&'32655#.'>55!!!!h0@& ) 3-  $6."LLLL +) Qh+8HW341.] +7;?;=+'#+<8+=<9 =<9#'9=<9=<91=<94=<9<A8<+?9+01&&>7.'&&'>55&&'667!!!0    !A:1/;B"/U?%W(BW &"% 6.+f8 144R7#5375>7!!! % 40) 2&. eR"+B0 'T #%&J*25 *8C_6. "5:<% ;dA EPT(<!  &,"#! 68541.]&*.*,+  ++'+,+9,+9,+9 и и+0'++.(++и!01%&&'#&&'>7#5353#!!!  H^Q$-7$%0R>(U*@Tk6.6[-'-,. ( :CDBgBCE=41.]@GQ1+I&++99Iк99!9&*к-939A9D&I9G9M9++++A+A9A9 и%и'и)и+к-A93A9HиI01!!!.'#3#3!53'735#535#5&&'>7'&&'3&&'7667.26  `H9 zw'B,Bn 58[C(T 9GP4-Gk.4&5C  CC^o ~C5 "$ >HI  ;2&64~ $']y+++и и++++и и 01%!535#5353#3!!! [N6.DEE˔41.e+/+01##!7_et+/+01!5337:%l{ / ++/ ++015#3##!]5q0 `lt{ /++ /+ +01##!533]5qo& 0jLX Z//ܸи и /ܸ к 9 /EX/>Y + +01%!!%5!!5-z#y,ԤLX z//и/иܸ ܸк 9 /EX / >Y+ + 9 901%5!55!!88y,,  G// и /ܸ / ++и 0133#!#湤,ozy8 v G// и /ܸ / ++и 01%### 3!,Ԥz8yC////017!7afC~u////01%7!7`euwup//9901'5'5puaWfWx|o//9901'5'5o`XeXxp //01'5pfWx|p4 //01'5p`X //9901#!# ,Zy8L //01'7'OOyq$MiHRUX  !FR +01!5!\4~MG //01#73S`_`ø // и/A]A)9IYiy ]ܸA&6FVfv ]A]+ +01".54>3274&#"326'''';.##..##.v''''k#//##//cr "F"FFch "F"FFcs "F"FFce "F"FFcj "F"FFch "F"FGcq "F"FGcr "F"FGce "F"FGcq "F"FG bqc "G"GGbh "G"GGbs "G"GG_ey "G"GGb|k "G"GG_qc "G"GG _rc "G"GG!_jc "G"GG"_h "G"GG#_qw "G"GG$_rv "G"GG%_eq "G"GG&bh "G"GG)bq "G"GG-_e "G"GG.bqc "G"GG/_qt "G"GG0b| "G"GG1_Uc "G"GG2cr "F"G4Fch "F"G4Fcs "F"G4Fce "F"G6Fc| "F"G4Fcq "F"G6Fcq "F"G6Fcj "F"G6Fch "F"G6Gcq "F"G6Gcr "F"G6Gce "F"G6Gc| "F"G4Gc| "F"G4Gch "F"G4Gcp "F"G4Gcq "F"G4G cc "F"G6G cq "F"G4G cq "F"G6G c| "F"G4GcU "F"G6Gbpc "G"G8Gbqc "G"G8Gbh "G"G8Gbs "G"G8G_ey "G"G:Gb|k "G"G8G_qc "G"G:G _rc "G"G:G!_jc "G"G:G"_h "G"G:G#_qw "G"G:G$_rv "G"G:G%_eq "G"G:G&b|c "G"G8G'b{c "G"G8G(bh "G"G8G)bw "G"G8G*bq "G"G8G+bqh "G"G8G,bq "G"G8G-_e "G"G:G.bqc "G"G8G/_qt "G"G:G0b| "G"G8G1_Uc "G"G:G2|rB "G="G>G@|h "G="G>GA|s "G="G>GE|em "GF"GGGH|qB "GF"GGGK|jA "GF"GGGM|h "GF"GGGN|qX "GF"GGGO|ro "GF"GGGP|ew "GF"GGGQ|h "G="G>GT|c "GF"GGGY|qA "G="G>GZbpb "G"G_Gbqc "G"G_Gbh "G"G_Gbs "G"G_G_ey "G"GaGb|k "G"G_G_qc "G"GaG _rc "G"GaG!_jb "G"GaG"_h "G"GaG#_qw "G"GaG$_rv "G"GaG%_eq "G"GaG&bh "G"G_G)bq "G"G_G-_e "G"GaG.bqc "G"G_G/_qt "G"GaG0b| "G"G_G1_Ub "G"GaG2|h "G="GcGA|s "G="GcGE|em "GF"GeGH|qB "GF"GeGK|qB "GF"GeGL|jA "GF"GeGM|h "GF"GeGN|qX "GF"GeGO|ro "GF"GeGP|ew "GF"GeGQ|h "G="GcGT|q "G="GcGX|c "GF"GeGY|qA "G="GcGZ|{y "G="GcG\|Uc "GF"GeG]bpb "G"GgGbqc "G"GgGbh "G"GgGbs "G"GgG_ey "G"GiGb|k "G"GgG_qc "G"GiG _rc "G"GiG!_jb "G"GiG"_h "G"GiG#_qw "G"GiG$_rv "G"GiG%_eq "G"GiG&b|b "G"GgG'bh "G"GgG)bq "G"GgG+bqh "G"GgG,bq "G"GgG-_e "G"GiG.bqc "G"GgG/_qt "G"GiG0b| "G"GgG1_Ub "G"GiG2;j"Gl"GmGo;h"Gl"GmGp;s"Gl"GmGt;`"Gu"GvGw;q"Gu"GvGz;j"Gu"GvG|;q"Gu"GvG~;r"Gu"GvG;h"Gl"GmG;m"Gl"GmG;f"Gu"GvG;j"Gl"GmG;{"Gu"GvG;|"Gl"GmG;V"Gu"GvGHr "G"GFHh "G"GFHs "G"GFHe "G"GGH| "G"GFHq "G"GGHj "G"GGHg "G"GGHq "G"GGHq "G"GGHe "G"GGHf "G"GGHp "G"GGHq "G"GG Hc "G"GG Hq "G"GG Hr "G"GGH| "G"GGHU "G"GGJjc "G"GGJpc "G"GGJg "G"GGJt "G"GGJfy "G"GGJ|k "G"GGJpc "G"GGJrc "G"GGJjc "G"GGJg "G"GGJqu "G"GGJqv "G"GGJe "G"GGJ|c "G"GGJh "G"GGJw "G"GG*Jq "G"GGJe "G"GG.Jqc "G"GGJqt "G"GGJ{ "G"GGJUj "G"GGMpA "G"GGMg "G"GGMt "G"GGMfV "G"GGM|M "G"GGMpA "G"GGMrA "G"GGMjA "G"GGMg "G"GGMqS "G"GGMr_ "G"GGMel "G"GGMh "G"GGMp "G"GGMq "G"GGMf "G"GGMrA "G"GGMrW "G"GGM|y "G"GGMRV "G"GG;k"Gl"GGn;j"Gl"GGo;h"Gl"GGp;s"Gl"GGt;`"Gu"GGw;|"Gl"GGx;q"Gu"GGz;r"Gu"GG{;j"Gu"GG|;g"Gu"GG};q"Gu"GG~;r"Gu"GG;`"Gu"GG;|"Gl"GG;h"Gl"GG;m"Gl"GG;n"Gl"GG;u"Gl"GG;f"Gu"GG;j"Gl"GG;{"Gu"GG;|"Gl"GG;V"Gu"GG;r"G"GG;`"G"GG;t"G"GG;`"G"GG;j"G"GG;g"G"GG;p"G"GG;q"G"GG;h"G"GG;m"G"GH;g"G"HH;l"G"GH;z"G"GH;|"G"GH ;V"G"HGUpA "H "H GUg "H "H GUt "H "H GMfV "H"HGU|M "H "H GMpA "H"HGMrA "H"HGMjA "H"HGMg "H"HGMqS "H"HGMr_ "H"HGMel "H"HGU{B "H "H GU{B "H "H GUh "H "H GUw "H "H GUq "H "H GMf "H"HGUrA "H "H GMrW "H"HGU|y "H "H GMRV "H"HGClc "H"HHCqc "H"HHCg "H"HHCn "H"HHCt "H"HHCj "H"HHC{k "H"HH Crx "H"HH!Crc "H"HH"Crc "H"HH#Ckb "H"HH$Ci "H"HH%Cqu "H"HH&Cro "H"HH'Cju "H"HH(C|b "H"HH)C|b "H"HH*Cf "H"HH+Cp "H"HH-Cuk "H"HH.Cq "H"HH/Cf "H"HH0Crc "H"HH1Cqt "H"HH2C| "H"HH3CRr "H"HH4UpA "H "H6GUg "H "H6GUt "H "H6GMfV "H"H8GU|M "H "H6GMpA "H"H8GMrA "H"H8GMjA "H"H8GMg "H"H8GMqS "H"H8GMr_ "H"H8GMel "H"H8GUh "H "H6GUp "H "H6GUuK "H "H6GUq "H "H6GMf "H"H8GUrA "H "H6GMrW "H"H8GU|y "H "H6GMRV "H"H8G;j"G"H:G;r"G"H:G;`"G"H:G;t"G"H:G;`"G"H<G;|"G"H:G;q"G"H<G;r"G"H<G;j"G"H<G;g"G"H<G;p"G"H<G;q"G"H<G;e"G"H<G;|"G"H:G;{"G"H:G;h"G"H:G;m"G"H=H;m"G"H:H;m"G"H=H;t"G"H>H;g"G"H?H;l"G"H:H;z"G"H<H;|"G"H:H ;V"G"H?G;j"HA"HBGo;h"HA"HBGp;s"HA"HBGt;`"Gu"HDGw;r"Gu"HDG{;j"Gu"HDG|;g"Gu"HDG};q"Gu"HDG~;r"Gu"HDG;`"Gu"HDG;h"HA"HBG;m"HA"HBG;u"HA"HBG;f"Gu"HDG;j"HA"HBG;{"Gu"HDG;|"HA"HBG;V"Gu"HDGNlA "G"HFGNpA "G"HFGNg "G"HFGNY "G"HGGNt "G"HFGNfV "G"HHGN|M "G"HFGNtU "G"HHGNpA "G"HHGNrA "G"HHGNjA "G"HHGNg "G"HHGNqS "G"HHGNr_ "G"HHGNel "G"HHGN{B "G"HFGN{B "G"HFGNh "G"HFGNw "G"HIGNp "G"HFGNuK "G"HIGNq "G"HFGNf "G"HHGNrA "G"HFGNrW "G"HHGN|y "G"HFGNRV "G"HHG|rB "G="HKG@|h "G="HKGA|s "G="HKGE|em "GF"HNGH|qB "GF"HNGK|jA "GF"HNGM|h "GF"HNGN|qX "GF"HNGO|ro "GF"HNGP|ew "GF"HNGQ|h "G="HKGT|q "G="HKGV|c "GF"HNHP|qA "G="HKGZ|qW "GF"HNG[|Uc "GF"HNG]Dh "HR"FFDs "HR"FFGe "HT"FFD| "HR"FFGq "HT"FFGj "HT"FFGh "HT"FGGq "HT"FGGr "HT"FGGe "HT"FGDh "HR"FGDq "HR"FG Gc "HT"FG Dq "HR"FG D| "HR"FGGU "HT"FGCqc "HW"HXGCh "HW"HXGCs "HW"HXGCey "H["H\GC|k "HW"HXGCqc "H["H\G Crc "H["H\G!Cjc "H["H\G"Ch "H["H\G#Cqw "H["H\G$Crv "H["H\G%Ceq "H["H\G&Ch "HW"HXG)Cq "HW"HXG-Ce "H["H\G.Cqc "HW"HXG/Cqt "H["H\G0C| "HW"HXG1CUc "H["H\G2Dr "HR"G4FDh "HR"G4FD "HS"G5FDs "HR"G4FGe "HT"G6FD| "HR"G4FGq "HT"G6FGq "HT"G6FGj "HT"G6FGh "HT"G6GGq "HT"G6GGr "HT"G6GGe "HT"G6GD| "HR"G4GD| "HR"G4GDh "HR"G4GDw "HR"G4GDp "HR"G4GDq "HR"G4G Dq "HR"G4G Gc "HT"G6G Dq "HR"G4G Gq "HT"G6G D| "HR"G4GGU "HT"G6G;sc "HUH^Cpb "HW"H_GCqc "HW"H_GCh "HW"H_GC} "HY"H`GCs "HW"H_GCey "H["HaGC|k "HW"H_GCqx "H["HaGCqc "H["HaG Crc "H["HaG!Cjc "H["HaG"Ch "H["HaG#Cqw "H["HaG$Crv "H["HaG%Ceq "H["HaG&C|b "HW"H_G'C{c "HW"H_G(Ch "HW"H_G)Cw "HW"HbG*Cq "HW"H_G+Cqh "HW"HbG,Cq "HW"H_G-Ce "H["HaG.Cqc "HW"H_G/Cqt "H["HaG0C| "HW"H_G1CUc "H["HaG2Dh "HR"HdGADs "HR"HdGE\em "Hg"HhGHD|M "HR"HdGI\qB "Hg"HhGK\qB "Hg"HhGL\jA "Hg"HhGM\h "Hg"HhGN\qX "Hg"HhGO\ro "Hg"HhGP\ew "Hg"HhGQDh "HR"HdGTDq "HR"HdGX\c "Hg"HhGYDqB "HR"HdGZ\qW "Hg"HhG[D{y "HR"HdG\\Uc "Hg"HhG]Cqc "HW"HjGCh "HW"HjGCs "HW"HjGCey "H["HlGC|k "HW"HjGCqx "H["HlGCqc "H["HlG Crc "H["HlG!Cjc "H["HlG"Ch "H["HlG#Cqw "H["HlG$Crv "H["HlG%Ceq "H["HlG&C{c "HW"HjG(Ch "HW"HjG)Cq "HW"HjG+Cq "HW"HjG-Ce "H["HlG.Cqc "HW"HjG/Cqt "H["HlG0C| "HW"HjG1CUc "H["HlG2DqB "HR"HnG?DrB "HR"HnG@Dh "HR"HnGADs "HR"HnGE\em "Hg"HpGHD|M "HR"HnGI\qB "Hg"HpGK\qB "Hg"HpGL\jA "Hg"HpGM\h "Hg"HpGN\qX "Hg"HpGO\ro "Hg"HpGP\ew "Hg"HpGQD|B "HR"HnGRD|B "HR"HnGSDh "HR"HnGTDqI "HR"HnGWDq "HR"HnGX\c "Hg"HpGYDqB "HR"HnGZD{y "HR"HnG\\Uc "Hg"HpG]Cpc "HW"HrGCqc "HW"HrGCh "HW"HrGC} "HY"HsGCs "HW"HrGCey "H["HtGC|k "HW"HrGCqx "H["HtGCqc "H["HtG Crc "H["HtG!Cjc "H["HtG"Ch "H["HtG#Cqw "H["HtG$Crv "H["HtG%Ceq "H["HtG&C|c "HW"HrG'C{c "HW"HrG(Ch "HW"HrG)Cw "HW"HuG*Cq "HW"HrG+Cqh "HW"HuG,Cq "HW"HrG-Ce "H["HtG.Cqc "HW"HrG/Cqt "H["HtG0C| "HW"HrG1CUc "H["HtG2;j"Hx"HyGo;h"Hx"HyGp;s"Hx"HyGt;|"Hx"HyGx;q"H|"H}Gz;r"H|"H}G{;j"H|"H}G|;g"H|"H}G};q"H|"H}G~;r"H|"H}G;`"H|"H}G;h"Hx"HyG;m"Hx"HyG;j"Hx"HyG;{"H|"H}G;|"Hx"HyG;V"H|"H}GHr "H"HFHh "H"HFH "H"HFHs "H"HFHe "H"HGH| "H"HFHq "H"HGHr "H"HGHj "H"HGHg "H"HGHq "H"HGHq "H"HGHe "H"HGH| "H"HGH| "H"HGHf "H"HGHw "H"HGHq "H"HG Hc "H"HG Hq "H"HG Hr "H"HGH| "H"HGHU "H"HGNpc "H"HGNg "H"HGJ} "H"GGNt "H"HGNfy "H"HGN|k "H"HGNqw "H"HGNpc "H"HGNrc "H"HGNjb "H"HGNg "H"HGNqu "H"HGNqv "H"HGNe "H"HGN|b "H"HGN|c "H"HGNh "H"HGNw "H"HG*Nq "H"HGNq "H"HGNe "H"HG.Nqc "H"HGNqt "H"HGN{ "H"HGNUj "H"HGQlA "H"HGQpA "H"HGQg "H"HGQt "H"HGNfV "H"HGQ|M "H"HGNpA "H"HGNrA "H"HGNjA "H"HGNg "H"HGNqS "H"HGNr_ "H"HGNel "H"HGQh "H"HGQw "H"HGQp "H"HGQq "H"HGNf "H"HGQrA "H"HGNrW "H"HGQ|y "H"HGNRV "H"HG;k"Hx"HGn;j"Hx"HGo;h"Hx"HGp;"Hz"HGs;s"Hx"HGt;`"H|"HGw;|"Hx"HGx;z"H|"HGy;q"H|"HGz;r"H|"HG{;j"H|"HG|;g"H|"HG};q"H|"HG~;r"H|"HG;`"H|"HG;|"Hx"HG;z"Hx"HG;h"Hx"HG;m"Hx"HG;m"Hx"HG;n"Hx"HG;u"Hx"HG;f"H|"HG;j"Hx"HG;{"H|"HG;|"Hx"HG;V"H|"HG;r"H"GG;`"H"GG;t"H"GG;`"H"GG;|"H"GG;q"H"GG;r"H"GG;j"H"GG;g"H"GG;p"H"GG;q"H"GG;h"H"GG;m"H"GH;g"H"HH;l"H"GH;z"H"GH;|"H"GH ;V"H"HGUlA "H"H GUpA "H"H GUg "H"H GMY "H"HGUt "H"H GMfV "H"HGU|M "H"H GMpA "H"HGMrA "H"HGMjA "H"HGMg "H"HGMqS "H"HGMr_ "H"HGMel "H"HGU{B "H"H GU{B "H"H GUh "H"H GUw "H"H GUq "H"H GMf "H"HGUrA "H"H GMrW "H"HGU|y "H"H GMRV "H"HGCqc "H"HHCg "H"HHCt "H"HHCj "H"HHC{k "H"HH Crc "H"HH"Crc "H"HH#Ckb "H"HH$Ci "H"HH%Cqu "H"HH&Cro "H"HH'Cju "H"HH(Cf "H"HH+Cx "H"HH,Cuk "H"HH.Cq "H"HH/Cf "H"HH0Crc "H"HH1Cqt "H"HH2C| "H"HH3CRr "H"HH4UlA "H"H6GUpA "H"H6GUg "H"H6GUt "H"H6GMfV "H"HGU|M "H"H6GMpA "H"HGMrA "H"HGMjA "H"HGMg "H"HGMqS "H"HGMr_ "H"HGMel "H"HGUh "H"H6GUw "H"H6GUp "H"H6GUuK "H"H6GUq "H"H6GMf "H"HGUrA "H"H6GMrW "H"HGU|y "H"H6GMRV "H"HG;j"H"H:G;r"H"H:G;`"H"H:G;"H"H;G;t"H"H:G;`"H"H<G;|"H"H:G;z"H"H<G;q"H"H<G;r"H"H<G;j"H"H<G;g"H"H<G;p"H"H<G;q"H"H<G;e"H"H<G;|"H"H:G;{"H"H:G;h"H"H:G;m"H"H=H;m"H"H:H;m"H"H=H;t"H"H>H;g"H"H?H;l"H"H:H;z"H"H<H;|"H"H:H ;V"H"H?G;j"Hx"HGo;h"Hx"HGp;s"Hx"HGt;|"Hx"HGx;q"H|"HGz;j"H|"HG|;g"H|"HG};q"H|"HG~;r"H|"HG;h"Hx"HG;m"Hx"HG;u"Hx"HG;f"H|"HG;j"Hx"HG;|"Hx"HG;V"H|"HGIs@ "H~HNlA "H"HGNpA "H"HGNg "H"HGNY "H"HGNt "H"HGIfV "H"HGN|M "H"HGItU "H"HGIpA "H"HGIr@ "H"HGIj@ "H"HGIg "H"HGIqS "H"HGIr_ "H"HGIel "H"HGN{B "H"HGN{B "H"HGNh "H"HGNw "H"HGNp "H"HGNuK "H"HGNq "H"HGIf "H"HGNrA "H"HGIrW "H"HGN|y "H"HGIRV "H"HGDrB "HR"HKG@Dh "HR"HKGADs "HR"HKGE\em "Hg"HNGHD|M "HR"HKGI\qB "Hg"HNGK\qB "Hg"HNGL\jA "Hg"HNGM\h "Hg"HNGN\qX "Hg"HNGO\ro "Hg"HNGP\ew "Hg"HNGQDh "HR"HKGTDq "HR"HKGVDq "HR"HKGX\c "Hg"HNHPDqA "HR"HKGZ\qW "Hg"HNG[D{y "HR"HKG\\Uc "Hg"HNG]h "H"FFs "H"FFe "H"FFj "H"FFh "H"FGq "H"FGr "H"FGe "H"FGh "H"FGq "H"FG | "H"FGqc "H"GGh "H"GGs "H"GGey "H"GG|k "H"GGqc "H"GG rc "H"GG!jc "H"GG"h "H"GG#qw "H"GG$rv "H"GG%eq "H"GG&h "H"GG)q "H"GG-e "H"GG.qc "H"GG/qt "H"GG0| "H"GG1Uc "H"GG2r "H"G4Fh "H"G4Fs "H"G4Fe "H"G6F| "H"G4Fq "H"G6Fq "H"G6Fj "H"G6Fh "H"G6Gq "H"G6Gr "H"G6Ge "H"G6G| "H"G4Gh "H"G4Gw "H"G4Gup "H"G4Gq "H"G4G c "H"G6G q "H"G4G q "H"G6G | "H"G4GU "H"G6Gsc "HG7pc "H"G8Gqc "H"G8Gh "H"G8G} "H"G9Gs "H"G8Gey "H"G:G|k "H"G8Gqx "H"G:Gqc "H"G:G rc "H"G:G!jc "H"G:G"h "H"G:G#qw "H"G:G$rv "H"G:G%eq "H"G:G&|c "H"G8G'{c "H"G8G(h "H"G8G)w "H"G8G*q "H"G8G+qh "H"G8G,q "H"G8G-e "H"G:G.qc "H"G8G/qt "H"G:G0| "H"G8G1Uc "H"G:G2rB "H"HG@s "H"HGEem "H"HGH|M "H"HGIqB "H"HGKh "H"HGNqX "H"HGOro "H"HGPew "H"HGQh "H"HGTq "H"HGXc "H"HGYqB "H"HGZqW "H"HG[{y "H"HG\qc "H"HGh "H"HGs "H"HGey "H"HG|k "H"HGqc "H"HG rc "H"HG!jb "H"HG"h "H"HG#qw "H"HG$rv "H"HG%eq "H"HG&h "H"HG)q "H"HG-e "H"HG.qc "H"HG/qt "H"HG0| "H"HG1Ub "H"HG2rB "H"HG@h "H"HGAs "H"HGEem "H"HGH|M "H"HGIqB "H"HGKqB "H"HGLj@ "H"HGMh "H"HGNqX "H"HGOro "H"HGPew "H"HGQh "H"HGTx "H"HGUq "H"HGXc "H"HGYqW "H"HG[{y "H"HG\Uc "H"HG]pc "H"HGqc "H"HGh "H"HGs "H"HGey "H"HG|k "H"HGqx "H"HGqc "H"HG rc "H"HG!jb "H"HG"h "H"HG#qw "H"HG$rv "H"HG%eq "H"HG&|c "H"HG'{c "H"HG(h "H"HG)w "H"HG*q "H"HG+qh "H"HG,q "H"HG-e "H"HG.qc "H"HG/qt "H"HG0| "H"HG1Ub "H"HG2;r"H"HG;`"H"HG;t"H"HG;e"H"HH;|"H"HG;q"H"HH;i"H"HH;h"H"HH;q"H"HH;r"H"HH;_"H"HH;f"H"HH;m"H"HH;t"H"HH;f"H"HG;l"H"HH;{"H"HHHp "H"HHHp "H"HHHg "H"HHHt "H"HHHe "H"HGH| "H"HHHq "H"HGHr "H"HGHj "H"HGHg "H"HGHq "H"HGHq "H"HGHe "H"HGH| "H"HHH| "H"HHHf "H"HGHw "H"HHHr "H"HHHq "H"HHHc "H"HG Hr "H"HHHr "H"HGH| "H"HHHU "H"HG@sb "HHNjc "H"HGNpc "H"HGNg "H"HGJ} "H"HGNt "H"HGNfy "H"HGN|k "H"HGNqw "H"HGNpc "H"HGNrc "H"HGNjb "H"HGNg "H"HGNqu "H"HGNqv "H"HGNe "H"HGN|b "H"HGN|c "H"HGNh "H"HGNw "H"HG*Nq "H"HGNtk "H"HGNq "H"HGNf "H"HGNqc "H"HGNqt "H"HGN{ "H"HGNUj "H"HGNlA "H"HGNpA "H"HGNg "H"HGNt "H"HGNfV "H"HGN|M "H"HGNpA "H"HGNrA "H"HGNjA "H"HGNg "H"HGNqS "H"HGNr_ "H"HGNel "H"HGNh "H"HGNp "H"HGNuK "H"HGNq "H"HGNf "H"HGNrA "H"HGNrW "H"HGN|y "H"HGNRV "H"HG;r"H"HG;`"H"HG;t"H"HG;e"H"HH;|"H"HG;q"H"HH;r"H"HH;i"H"HH;h"H"HH;q"H"HH;r"H"HH;_"H"HH;|"H"HG;f"H"HH;m"H"HH;t"H"HH;f"H"HG;l"H"HH;{"H"HH;|"H"HH ;V"H"HG;k"I"II;`"I"II;t"I"II ;k"I "I I ;f"I "I I;r"I "I I;k"I "I I;f"I "I I;f"I "I I;q"I "I I;k"I "I I;c"I"II;p"I"II;t"I"II;g"I "IH;j"I"II;z"I "I I ;|"I"II!;V"I "IGMjA "I$"I%I&MqB "I$"I%I'Mg "I$"I%I(MY "I)"I*GMt "I$"I%I+Mld "I,"I-I.M|M "I$"I%I/MqU "I,"I-I0MqA "I,"I-I1MqA "I,"I-I2MkA "I,"I-I3Mi "I,"I-I4MrS "I,"I-I5Mro "I,"I-I6Mlk "I,"I-I7M|B "I$"I%I8M|A "I$"I%I9Mf "I$"I%I:Mw "I$"I%GMuJ "I$"I%I<Mq "I$"I%I=Mf "I,"I-GMrA "I$"I%I>MsW "I,"I-I?M{y "I$"I%I@MRV "I,"I-GClc "IC"IDHCqc "IC"IDHCg "IC"IDHCn "IE"IFHCt "IC"IDHCj "IG"IHHC{k "IC"IDH Crx "IG"IHH!Crc "IG"IHH"Crc "IG"IHH#Ckb "IG"IHH$Ci "IG"IHH%Cqu "IG"IHH&Cro "IG"IHH'Cju "IG"IHH(C|b "IC"IDH)C|b "IC"IDH*Cf "IC"IDH+Cx "IC"IDH,Cp "IC"IDH-Cuk "IC"IDH.Cq "IC"IDH/Cf "IG"IHH0Crc "IC"IDH1Cqt "IG"IHH2C| "IC"IDH3CRr "IG"IHH4PjA "I$"IJI&PqB "I$"IJI'Pg "I$"IJI(Pt "I$"IJI+Mld "I,"ILI.P|M "I$"IJI/MqA "I,"ILI1MqA "I,"ILI2MkA "I,"ILI3Mi "I,"ILI4MrS "I,"ILI5Mro "I,"ILI6Mlk "I,"ILI7Pf "I$"IJI:Pw "I$"IJGPp "I$"IJI;PuJ "I$"IJI<Pq "I$"IJI=Mf "I,"ILGPrA "I$"IJI>MsW "I,"ILI?P{y "I$"IJI@MRV "I,"ILG;k"I"INI;`"I"INI;"I"IOG;t"I"INI ;k"I "IPI ;|"I"INI ;f"I "IPI;r"I "IPI;k"I "IPI;f"I "IPI;f"I "IPI;q"I "IPI;k"I "IPI;c"I"INI;m"I"IQH;p"I"INI;t"I"IRI;g"I "ISH;j"I"INI;z"I "IPI ;|"I"INI!;V"I "ISG;r"IU"IVG;`"IU"IVG;t"IU"IVG;e"H"IXH;|"IU"IVG;i"H"IXH;h"H"IXH;q"H"IXH;r"H"IXH;_"H"IXH;f"IU"IVH;m"IU"IVH;f"H"IXG;l"IU"IVH;{"H"IXH;V"H"IXGNlA "H"IYGNpA "H"IYGNg "H"IYGNt "H"IYGNfV "H"I[GN|M "H"IYGNpA "H"I[GNrA "H"I[GNjA "H"I[GNg "H"I[GNqS "H"I[GNr_ "H"I[GNel "H"I[GN{B "H"IYGN{B "H"IYGNh "H"IYGNw "H"I\GNp "H"IYGNuK "H"I\GNq "H"IYGNf "H"I[GNrA "H"IYGNrW "H"I[GN|y "H"IYGNRV "H"I[GrB "H"HKG@h "H"HKGAs "H"HKGEem "H"HNGH|M "H"HKGIqB "H"HNGKjA "H"HNGMh "H"HNGNqX "H"HNGOro "H"HNGPew "H"HNGQh "H"HKGTq "H"HKGVq "H"HKGXc "H"HNHPqA "H"HKGZqW "H"HNG[Uc "H"HNG]h "I^"FFs "I^"FFe "I`"FFh "I`"FGq "I`"FGr "I`"FGh "I^"FGq "I^"FG q "I`"FG | "I^"FGnqc "Ib"GGnh "Ib"GGns "Ib"GGney "Id"GGn|k "Ib"GGnqc "Id"GG nrc "Id"GG!njc "Id"GG"nh "Id"GG#nqw "Id"GG$nrv "Id"GG%neq "Id"GG&nh "Ib"GG)nq "Ib"GG-ne "Id"GG.nqc "Ib"GG/nqt "Id"GG0n| "Ib"GG1nUc "Id"GG2q "I^"G4Fr "I^"G4Fh "I^"G4F "I_"G5Fs "I^"G4Fe "I`"G6F| "I^"G4Fq "I`"G6Fq "I`"G6Fq "I`"G6Fj "I`"G6Fh "I`"G6Gq "I`"G6Gr "I`"G6Ge "I`"G6G| "I^"G4G| "I^"G4Gh "I^"G4Gw "I^"G4Gup "I^"G4Gq "I^"G4G q "I^"G4G c "I`"G6G q "I^"G4G q "I`"G6G | "I^"G4GU "I`"G6Gssc "IaG7npc "Ib"G8Gnqc "Ib"G8Gnh "Ib"G8Gn} "Ic"G9Gns "Ib"G8Gney "Id"G:Gn|k "Ib"G8Gnqx "Id"G:Gnqc "Id"G:G nrc "Id"G:G!njc "Id"G:G"nh "Id"G:G#nqw "Id"G:G$nrv "Id"G:G%neq "Id"G:G&n|c "Ib"G8G'n{c "Ib"G8G(nh "Ib"G8G)nw "Ib"G8G*nq "Ib"G8G+nqh "Ib"G8G,nq "Ib"G8G-ne "Id"G:G.nqc "Ib"G8G/nqt "Id"G:G0n| "Ib"G8G1nUc "Id"G:G2h "Ig"IhGAs "Ig"IhGEem "Ik"IlGHqB "Ik"IlGKh "Ik"IlGNqX "Ik"IlGOro "Ik"IlGPew "Ik"IlGQh "Ig"IhGTq "Ig"IhGVq "Ig"IhGXqB "Ig"IhGZqW "Ik"IlG[Uc "Ik"IlG]nqc "Ib"InGnh "Ib"InGns "Ib"InGney "Id"IpGn|k "Ib"InGnqc "Id"IpG nrc "Id"IpG!njc "Id"IpG"nh "Id"IpG#nqw "Id"IpG$nrv "Id"IpG%neq "Id"IpG&nh "Ib"InG)nq "Ib"InG-ne "Id"IpG.nqc "Ib"InG/nqt "Id"IpG0n| "Ib"InG1nUc "Id"IpG2qA "Ig"IrG?rB "Ig"IrG@h "Ig"IrGAs "Ig"IrGEem "Ik"ItGH|M "Ig"IrGIqB "Ik"ItGKqB "Ik"ItGLj@ "Ik"ItGMh "Ik"ItGNqX "Ik"ItGOro "Ik"ItGPew "Ik"ItGQ|A "Ig"IrGR|A "Ig"IrGSh "Ig"IrGTx "Ig"IrGUq "Ig"IrGXc "Ik"ItGYqA "Ig"IrGZqW "Ik"ItG[{y "Ig"IrG\Uc "Ik"ItG]npc "Ib"IvGnqc "Ib"IvGnh "Ib"IvGns "Ib"IvGney "Id"IxGn|k "Ib"IvGnqx "Id"IxGnqc "Id"IxG nrc "Id"IxG!njb "Id"IxG"nh "Id"IxG#nqw "Id"IxG$nrv "Id"IxG%neq "Id"IxG&n|c "Ib"IvG'n{c "Ib"IvG(nh "Ib"IvG)nw "Ib"IvG*nq "Ib"IvG+nqh "Ib"IvG,nq "Ib"IvG-ne "Id"IxG.nqc "Ib"IvG/nqt "Id"IxG0n| "Ib"IvG1nUb "Id"IxG2;r"Iz"HG;`"Iz"HG;t"Iz"HG;e "I|"HH;q "I|"HH;i "I|"HH;q "I|"HH;r "I|"HH;_ "I|"HH;f"Iz"HH;m"Iz"HH;t"Iz"HH;l"Iz"HH;|"Iz"HH ;V "I|"HGHp "I"HHHp "I"HHHg "I"HHHt "I"HHHe "I"HGH| "I"HHHq "I"HGHr "I"HGHj "I"HGHg "I"HGHq "I"HGHq "I"HGHe "I"HGH| "I"HHH| "I"HHHf "I"HGHw "I"HHHm "I"HHHr "I"HHHq "I"HHHc "I"HG Hr "I"HHHr "I"HGH| "I"HHHU "I"HGJjc "I"IGJpc "I"IGJg "I"IGJ} "I"HGJt "I"IGNfy "I"HGJ|k "I"IGNqw "I"HGNpc "I"HGNrc "I"HGNjb "I"HGNg "I"HGNqu "I"HGNqv "I"HGNe "I"HGJ|c "I"IGJ|c "I"IGJh "I"IGJw "I"IG*Jtk "I"IGJq "I"IGNf "I"HGJqc "I"IGNqt "I"HGJ{ "I"IGNUj "I"HGNlA "I"HGNpA "I"HGNg "I"HGNt "I"HGNfV "I"HGN|M "I"HGNpA "I"HGNrA "I"HGNjA "I"HGNg "I"HGNqS "I"HGNr_ "I"HGNel "I"HGNh "I"HGNp "I"HGNuK "I"HGNq "I"HGNf "I"HGNrA "I"HGNrW "I"HGN|y "I"HGNRV "I"HG;j"Iz"HG;r"Iz"HG;`"Iz"HG;"I{"HGs;t"Iz"HG;e "I|"HH;|"Iz"HG;{ "I|"HH;q "I|"HH;r "I|"HH;i "I|"HH;h "I|"HH;q "I|"HH;r "I|"HH;_ "I|"HH;|"Iz"HG;{"Iz"HG;f"Iz"HH;m"Iz"HG;m"Iz"HH;m"Iz"HH;t"Iz"HH;f "I|"HG;l"Iz"HH;{ "I|"HH;|"Iz"HH ;V "I|"HG;k"I"II;`"I"II;t"I"II ;k"I"I I ;|"I"II ;f"I"I I;r"I"I I;k"I"I I;f"I"I I;f"I"I I;q"I"I I;k"I"I I;c"I"II;p"I"II;t"I"II;g"I"IH;j"I"II;z"I"I I ;|"I"II!;V"I"IGMjA "I"I%I&MqB "I"I%I'Mg "I"I%I(MY "I"I*GMt "I"I%I+Mld "I"I-I.M|M "I"I%I/MqU "I"I-I0MqA "I"I-I1MqA "I"I-I2MkA "I"I-I3Mi "I"I-I4MrS "I"I-I5Mro "I"I-I6Mlk "I"I-I7M|B "I"I%I8M|A "I"I%I9Mf "I"I%I:Mw "I"I%GMuJ "I"I%I<Mq "I"I%I=Mf "I"I-GMrA "I"I%I>MsW "I"I-I?M{y "I"I%I@MRV "I"I-GClc "I"IDHCqc "I"IDHCg "I"IDHCn "I"IFHCt "I"IDHCj "I"IHHC{k "I"IDH Crx "I"IHH!Crc "I"IHH"Crc "I"IHH#Ckb "I"IHH$Ci "I"IHH%Cqu "I"IHH&Cro "I"IHH'Cju "I"IHH(C|b "I"IDH)C|b "I"IDH*Cf "I"IDH+Cx "I"IDH,Cp "I"IDH-Cq "I"IDH/Cf "I"IHH0Crc "I"IDH1Cqt "I"IHH2C| "I"IDH3CRr "I"IHH4PjA "I"IJI&PqB "I"IJI'Pg "I"IJI(Pt "I"IJI+Mld "I"ILI.P|M "I"IJI/MqA "I"ILI1MqA "I"ILI2MkA "I"ILI3Mi "I"ILI4MrS "I"ILI5Mro "I"ILI6Mlk "I"ILI7P|B "I"IJI8Pf "I"IJI:Pp "I"IJI;Pq "I"IJI=Mf "I"ILGPrA "I"IJI>MsW "I"ILI?P{y "I"IJI@MRV "I"ILG;k"I"INI;k"I"INI;`"I"INI;t"I"INI ;k"I"IPI ;|"I"INI ;f"I"IPI;r"I"IPI;k"I"IPI;f"I"IPI;f"I"IPI;q"I"IPI;k"I"IPI;z"I"INI;c"I"INI;m"I"IQH;p"I"INI;t"I"IRI;g"I"ISH;j"I"INI;z"I"IPI ;|"I"INI!;V"I"ISG;r"I"IVG;`"I"IVG;t"I"IVG;e "I|"IXH;q "I|"IXH;i "I|"IXH;h "I|"IXH;q "I|"IXH;r "I|"IXH;_ "I|"IXH;f"I"IVH;m"I"IVH;t"I"IVH;f "I|"IXG;l"I"IVH;{ "I|"IXH;|"I"IVH ;V "I|"IXGNlA "I"IYGNpA "I"IYGNg "I"IYGNY "I"IZGNt "I"IYGIfV "I"HGN|M "I"IYGItU "I"HGIpA "I"HGIr@ "I"HGIj@ "I"HGIg "I"HGIqS "I"HGIr_ "I"HGIel "I"HGN{B "I"IYGN{B "I"IYGNh "I"IYGNw "I"I\GNp "I"IYGNuK "I"I\GNq "I"IYGIf "I"HGNrA "I"IYGIrW "I"HGN|y "I"IYGIRV "I"HGrB "Ig"HKG@h "Ig"HKGAs "Ig"HKGEem "Ik"HNGHqB "Ik"HNGKqB "Ik"HNGLjA "Ik"HNGMh "Ik"HNGNqX "Ik"HNGOro "Ik"HNGPew "Ik"HNGQh "Ig"HKGTc "Ik"HNHPqA "Ig"HKGZqW "Ik"HNG[{y "Ig"HKG\Uc "Ik"HNG]qr "I"FFqh "I"FFqs "I"FFqe "I"FFq| "I"FFqq "I"FFqq "I"FFqj "I"FFqh "I"FGqq "I"FGqr "I"FGqe "I"FGqh "I"FGqq "I"FG qc "I"FG qq "I"FG qq "I"FG q| "I"FGrqc "I"HXGrh "I"HXGrs "I"HXGrey "I"H\Gr|k "I"HXGrqc "I"H\G rrc "I"H\G!rjc "I"H\G"rh "I"H\G#rqw "I"H\G$rrv "I"H\G%req "I"H\G&rh "I"HXG)rq "I"HXG-re "I"H\G.rqc "I"HXG/rqt "I"H\G0r| "I"HXG1rUc "I"H\G2ns "IG3qq "I"G4Fqr "I"G4Fqh "I"G4Fq "I"G5Fqs "I"G4Fqe "I"G6Fq| "I"G4Fqq "I"G6Fqq "I"G6Fqq "I"G6Fqj "I"G6Fqh "I"G6Gqq "I"G6Gqr "I"G6Gqe "I"G6Gq| "I"G4Gq| "I"G4Gqh "I"G4Gqw "I"G4Gqp "I"G4Gqq "I"G4G qq "I"G4G qc "I"G6G qq "I"G4G qq "I"G6G q| "I"G4GqU "I"G6Ggsc "IH^rpb "I"H_Grqc "I"H_Grh "I"H_Gr} "I"H`Grs "I"H_Grey "I"HaGr|k "I"H_Grqx "I"HaGrqc "I"HaG rrc "I"HaG!rjc "I"HaG"rh "I"HaG#rqw "I"HaG$rrv "I"HaG%req "I"HaG&r|b "I"H_G'r{c "I"H_G(rh "I"H_G)rw "I"HbG*rq "I"H_G+rqh "I"HbG,rq "I"H_G-re "I"HaG.rqc "I"H_G/rqt "I"HaG0r| "I"H_G1rUc "I"HaG2qrB "I"IG@qh "I"IGAqs "I"IGE{em "I"IGHq|M "I"IGI{qB "I"IGK{h "I"IGN{qX "I"IGO{ro "I"IGP{ew "I"IGQqh "I"IGTqq "I"IGX{c "I"IGYqqA "I"IGZ{qW "I"IG[q{y "I"IG\rqc "I"HjGrh "I"HjGrs "I"HjGrey "I"HlGr|k "I"HjGrqc "I"HlG rrc "I"HlG!rjc "I"HlG"rh "I"HlG#rqw "I"HlG$rrv "I"HlG%req "I"HlG&rh "I"HjG)rq "I"HjG-re "I"HlG.rqc "I"HjG/rqt "I"HlG0r| "I"HjG1rUc "I"HlG2qqA "I"IG?qrB "I"IG@qh "I"IGA{` "I"IGDqs "I"IGE{em "I"IGHq|M "I"IGI{qV "I"IGJ{qB "I"IGK{qB "I"IGL{j@ "I"IGM{h "I"IGN{qX "I"IGO{ro "I"IGP{ew "I"IGQq|A "I"IGRq|A "I"IGSqh "I"IGTqx "I"IGUqqI "I"IGWqq "I"IGX{c "I"IGYqqA "I"IGZ{qW "I"IG[q{y "I"IG\{Uc "I"IG]gsb "IHqrpc "I"HrGrqc "I"HrGrh "I"HrGr} "I"HsGrs "I"HrGrey "I"HtGr|k "I"HrGrqx "I"HtGrqc "I"HtG rrc "I"HtG!rjc "I"HtG"rh "I"HtG#rqw "I"HtG$rrv "I"HtG%req "I"HtG&r|c "I"HrG'r{c "I"HrG(rh "I"HrG)rw "I"HuG*rq "I"HrG+rqh "I"HuG,rq "I"HrG-re "I"HtG.rqc "I"HrG/rqt "I"HtG0r| "I"HrG1rUc "I"HtG2;r"I"IG;`"I"IG;t"I"IG;e"I"IH;|"I"IG;q"I"IH;r"I"IH;i"I"IH;h"I"IH;q"I"IH;r"I"IH;_"I"IH;|"I"IG;{"I"IG;f"I"IH;m"I"IG;m"I"IH;t"I"IH;f"I"HG;l"I"IH;{"I"IH;|"I"IH ;V"I"HGHp "I"IHHp "I"IHHg "I"IHH "I"IHHt "I"IHHe "I"IGH| "I"IHHq "I"IGHr "I"IGHj "I"IGHg "I"IGHq "I"IGHq "I"IGHe "I"IGH| "I"IHH| "I"IHHf "I"IGHw "I"IHHm "I"IHHr "I"IHHq "I"IHHc "I"IG Hr "I"IHHr "I"IGH| "I"IHHU "I"IGJjc"I"IGJpc"I"IGJg"I"IGJ} "I"HGJt"I"IGJfy "I"IGJ|k"I"IGJqw "I"IGJpc "I"IGJrc "I"IGJjc "I"IGJg "I"IGJqu "I"IGJqv "I"IGJe "I"IGJ|c"I"IGJ|c"I"IGJh"I"IGJw"I"IG*Jq"I"IGJtk"I"IGJq"I"IGJf "I"IGJqc"I"IGJqt "I"IGJ{"I"IGJUj "I"IGNlA "I"IGNpA "I"IGNg "I"IGNt "I"IGNfV "I"IGN|M "I"IGNtU "I"IGNpA "I"IGNrA "I"IGNjA "I"IGNg "I"IGNqS "I"IGNr_ "I"IGNel "I"IGN{B "I"IGN{B "I"IGNh "I"IGNw "I"IGNp "I"IGNuK "I"IGNq "I"IGNf "I"IGNrA "I"IGNrW "I"IGN|y "I"IGNRV "I"IG;"II;j"I"IG;r"I"IG;`"I"IG;"I"IGs;t"I"IG;e"I"IH;|"I"IG;{"I"IH;q"I"IH;r"I"IH;i"I"IH;h"I"IH;q"I"IH;r"I"IH;_"I"IH;|"I"IG;{"I"IG;f"I"IH;m"I"IG;m"I"IH;m"I"IH;t"I"IH;f"I"HG;l"I"IH;{"I"IH;|"I"IH ;V"I"HG;k"I"II;`"I"II;t"I"II ;k"I"I I ;|"I"II ;f"I"I I;r"I"I I;k"I"I I;f"I"I I;f"I"I I;q"I"I I;z"I"II;c"I"II;m"I"IH;p"I"II;t"I"II;g"I"IH;j"I"II;z"I"I I ;|"I"II!;V"I"IGIs@ "II#MjA "I"II&MqB "I"II'Mg "I"II(MY "I"IGMt "I"II+Mld "I"I-I.M|M "I"II/MqU "I"I-I0MqA "I"I-I1MqA "I"I-I2MkA "I"I-I3Mi "I"I-I4MrS "I"I-I5Mro "I"I-I6Mlk "I"I-I7M|B "I"II8M|A "I"II9Mf "I"II:Mw "I"IGMp "I"II;MuJ "I"II<Mq "I"II=Mf "I"I-GMrA "I"II>MsW "I"I-I?M{y "I"II@MRV "I"I-GClc "I"HHCqc "I"HHCg "I"HHCn "I"IFHCt "I"HHCj "I"IHHC{k "I"HH Crx "I"IHH!Crc "I"IHH"Crc "I"IHH#Ckb "I"IHH$Ci "I"IHH%Cqu "I"IHH&Cro "I"IHH'Cju "I"IHH(C|b "I"HH)C|b "I"HH*Cf "I"HH+Cx "I"HH,Cp "I"HH-Cuk "I"HH.Cq "I"HH/Cf "I"IHH0Crc "I"HH1Cqt "I"IHH2C| "I"HH3CRr "I"IHH4MjA "I"II&MqB "I"II'Mg "I"II(Mt "I"II+Mld "I"ILI.M|M "I"II/MqA "I"ILI1MqA "I"ILI2MkA "I"ILI3Mi "I"ILI4MrS "I"ILI5Mro "I"ILI6Mlk "I"ILI7Mf "I"II:Mw "I"IGMp "I"II;Mq "I"II=Mf "I"ILGMrA "I"II>MsW "I"ILI?M{y "I"II@MRV "I"ILG;y"IIM;k"I"INI;k"I"INI;`"I"INI;"I"IOG;t"I"INI ;k"I"IPI ;|"I"INI ;z"I"IPI;f"I"IPI;r"I"IPI;k"I"IPI;f"I"IPI;f"I"IPI;q"I"IPI;k"I"IPI;|"I"INI;z"I"INI;c"I"INI;m"I"IQH;p"I"INI;l"I"IQI;t"I"IRI;g"I"ISH;j"I"INI;z"I"IPI ;|"I"INI!;V"I"ISG;r"I"IG;`"I"IG;t"I"IG;e"I"IH;q"I"IH;r"I"IH;i"I"IH;h"I"IH;q"I"IH;r"I"IH;_"I"IH;f"I"IH;m"I"IH;m"I"IH;t"I"IH;f"I"IXG;l"I"IH;{"I"IH;|"I"IH ;V"I"IXGIlA "I"IGIpA "I"IGIg "I"IGIt "I"IGNfV "I"IGI|M "I"IGNpA "I"IGNrA "I"IGNjA "I"IGNg "I"IGNqS "I"IGNr_ "I"IGNel "I"IGIh "I"IGIw "I"IGIp "I"IGIuK "I"IGIq "I"IGNf "I"IGIrA "I"IGNrW "I"IGI|y "I"IGNRV "I"IGqqA "I"HKG?qrB "I"HKG@qh "I"HKGAqs "I"HKGE{em "I"HNGHq|M "I"HKGI{qB "I"HNGK{qB "I"HNGL{jA "I"HNGM{h "I"HNGN{qX "I"HNGO{ro "I"HNGP{ew "I"HNGQqh "I"HKGTqq "I"HKGVqq "I"HKGX{c "I"HNHPqqA "I"HKGZ{qW "I"HNG[q{y "I"HKG\{Uc "I"HNG]p "I"IHg "I"IHt "I"IHe "I"IG| "I"IHq "I"IGr "I"IGj "I"IGg "I"IGq "I"IGq "I"IGe "I"IGf "I"IGc "I"IG r "I"IHr "I"IGnpc "I"GGng "I"GGnt "I"GGnfy "I"GGn|k "I"GGnpc "I"GGnrc "I"GGnjc "I"GGng "I"GGnqu "I"GGnqv "I"GGne "I"GGnh "I"GGnq "I"GGne "I"GG.nqc "I"GGnqt "I"GGn{ "I"GGnUj "I"GGp "I"IHg "I"IHt "I"IHe "I"IG| "I"IHr "I"IGq "I"IGr "I"IGj "I"IGg "I"IGq "I"IGq "I"IGe "I"IG| "I"IH| "I"IHf "I"IGm "I"IHq "I"IHc "I"IG r "I"IHr "I"IG| "I"IHU "I"IIksc "IG7njc "I"G8Gnpc "I"G8Gng "I"G8Gn} "I"G9Gnt "I"G8Gnfy "I"G:Gn|k "I"G8Gnqw "I"G:Gnpc "I"G:Gnrc "I"G:Gnjc "I"G:Gng "I"G:Gnqu "I"G:Gnqv "I"G:Gne "I"G:Gn|c "I"G8Gn|c "I"G8Gnh "I"G8Gnw "I"G8G*nq "I"G8Gntk "I"G8Gnq "I"G8Gne "I"G:G.nqc "I"G8Gnqt "I"G:Gn{ "I"G8GnUj "I"G:GrB "I"HdG@h "I"HdGAs "I"HdGEem "I"IGH|M "I"HdGIqB "I"IGKqB "I"IGLjA "I"IGMh "I"IGNqX "I"IGOro "I"IGPew "I"IGQh "I"HdGTq "I"HdGXc "I"IGYqB "I"HdGZqW "I"IG[{y "I"HdG\npc "I"IGng "I"IGnt "I"IGnfy "I"IGn|k "I"IGnpc "I"IGnrc "I"IGnjc "I"IGng "I"IGnqu "I"IGnqv "I"IGne "I"IGnh "I"IGnq "I"IGnq "I"IGne "I"IG.nqc "I"IGnqt "I"IGn{ "I"IGnUj "I"IGrB "I"HnG@h "I"HnGAs "I"HnGEem "I"IGH|M "I"HnGIqB "I"IGKqB "I"IGLjB "I"IGMh "I"IGNqX "I"IGOro "I"IGPew "I"IGQh "I"HnGTq "I"HnGXc "I"IGYqB "I"HnGZqW "I"IG[{y "I"HnG\Uc "I"IG]njc "I"IGnpc "I"IGng "I"IGnt "I"IGnfy "I"JGn|k "I"IGnqw "I"JGnpc "I"JGnrc "I"JGnjc "I"JGng "I"JGnqu "I"JGnqv "I"JGne "I"JGn|c "I"IGnh "I"IGnq "I"IGntk "I"JGnq "I"IGne "I"JG.nqc "I"IGnqt "I"JGn{ "I"IGnUj "I"JG;j"J"JJ;g"J"JJ;t"J"JJ ;e"J "JJ;{"J"JJ;p"J "JJ;r"J "JJ;j"J "JJ;g"J "JJ;q"J "JJ;r"J "JJ;e"J "JJ;f"J"JJ;p"J"JJ;t"J"JJ;f"J "JJ ;k"J"JJ!;{"J "JJ";|"J"JJ#;S"J "JJ$Jp "J&"J'HJp "J&"J'HJg "J&"J'HJt "J&"J'HHl "J+"J,J-J| "J&"J'HHq "J+"J,J.Hp "J+"J,J/Hr "J+"J,J0Hk "J+"J,J1Hi "J+"J,J2Hp "J+"J,J3Hr "J+"J,J4Hl "J+"J,J5J| "J&"J'HJ| "J&"J'HJg "J&"J'J6Hx "J&"J7J8Jm "J&"J'HHq "J&"IHHf "J+"J,J9Jr "J&"J'HHq "J+"J,J:J| "J&"J'HHU "J+"J,I@sb "J;J<Jjc"J="IGJpc"J="IGJg"J="IGJ}"J>"J?GJt"J="IGJfy "J@"JAGJ|k"J="IGJqw "J@"JAGJpc "J@"JAGJrc "J@"JAGJjc "J@"JAGJg "J@"JAGJqu "J@"JAGJqv "J@"JAGJe "J@"JAGJ|c"J="IGJ|c"J="IGJh"J="IGJw"J="IG*Jtk"J="IGJq"J="IGJf "J@"JAJBJqc"J="IGJqt "J@"JAGJ{"J="IGJUj "J@"JAGQjA "J&"JCI&QqB "J&"JCI'Qg "J&"JCI(Qt "J&"JCI+N_ "J+"JEJFQ|M "J&"JCI/NpA "J+"JEJHNrA "J+"JEJINkA "J+"JEJJNf "J+"JEJKNrS "J+"JEJLNqo "J+"JEJMNls "J+"JEJNQf "J&"JCJOQp "J&"JCI;Nq "J&"II=Nf "J+"JEGQrA "J&"JCI>NsW "J+"JEJQQ{y "J&"JCI@NSl "J+"JEJR;j"J"JSJ;j"J"JSJ;g"J"JSJ;t"J"JSJ ;e"J "JUJ;{"J"JSJ;p"J "JUJ;r"J "JUJ;j"J "JUJ;g"J "JUJ;q"J "JUJ;r"J "JUJ;e"J "JUJ;{"J"JSJ;f"J"JSJ;p"J"JSJ;t"J"JSJ;f"J "JUJ ;k"J"JSJ!;{"J "JUJ";|"J"JSJ#;S"J "JUJ$;j"J"JXJZ;f"J"JXJ[;t"J"JXJ_;k"J`"JaI ;|"J"JXJb;f"J`"JaI;r"J`"JaI;k"J`"JaI;f"J`"JaI;f"J`"JaI;q"J`"JaI;k"J`"JaI;c"J"JXI;p"J"JXJf;t"J"JXJh;f"J`"JiJj;k"J"JXJk;z"J`"JaI ;|"J"JXJl;S"J`"JiJ$MlA "Jo"JpJqMrB "Jo"JpJrMf "Jo"JpJsMY "Jt"JuGMu "Jo"JpJvMld "Jw"JxI.M|M "Jo"JpJyMqU "Jw"JxI0MqA "Jw"JxI1MqA "Jw"JxI2MkA "Jw"JxI3Mi "Jw"JxI4MrS "Jw"JxI5Mro "Jw"JxI6Mlk "Jw"JxI7M{B "Jo"JpJzMtB "Jo"JpJ{Mf "Jo"JpI:Mw "Jo"JpGMuJ "Jo"JpJ}Mr "Jo"JpJ~Mf "Jw"JxGMrA "Jo"JpJMsW "Jw"JxI?M|y "Jo"JpJMSl "Jw"JxJRCjc "J"JJCpc "J"JJCg "J"JJCn "J"JJCu "J"JJCl{ "J"JJC|k "J"JJCrx "J"JJCpc "J"JJCqc "J"JJCib "J"JJCi "J"JJCpu "J"JJCro "J"JJClq "J"JJC{b "J"JJC|b "J"JJCf "J"JJCm} "J"JJCp "J"JJCuk "J"JJCq "J"JJCf "J"JJBCpc "J"JJCst "J"JJC| "J"JJCSc "J"JJMrB "Jo"JJrMf "Jo"JJsMu "Jo"JJvPld "Jw"JI.M|M "Jo"JJyPqA "Jw"JI1PqA "Jw"JI2PkA "Jw"JI3Pi "Jw"JI4PrS "Jw"JI5Pro "Jw"JI6Plk "Jw"JI7MtB "Jo"JJ{Mf "Jo"JI:Mp "Jo"JJ|Mr "Jo"JJ~Pf "Jw"JGMrA "Jo"JJPsW "Jw"JI?M|y "Jo"JJPSl "Jw"JJR;j"J"JJZ;f"J"JJ[;t"J"JJ_;k"J`"JI ;|"J"JJb;f"J`"JI;r"J`"JI;k"J`"JI;f"J`"JI;f"J`"JI;q"J`"JI;k"J`"JI;c"J"JI;p"J"JJf;t"J"JJh;f"J`"JJj;k"J"JJk;z"J`"JI ;|"J"JJl;S"J`"JJ$;j"J"JJ;g"J"JJ;t"J"JJ ;e"J "JJ;{"J"JJ;p"J "JJ;r"J "JJ;j"J "JJ;g"J "JJ;q"J "JJ;r"J "JJ;e"J "JJ;f"J"JJ;p"J"JJ;f"J "JJ ;k"J"JJ!;S"J "JJ$Is@ "J%IIjA "J&"JI&IqB "J&"JI'Ig "J&"JI(NY "J("JGIt "J&"JI+N_ "J+"JJFI|M "J&"JI/NqU "J+"JJGNpA "J+"JJHNrA "J+"JJINkA "J+"JJJNf "J+"JJKNrS "J+"JJLNqo "J+"JJMNls "J+"JJNI|B "J&"JI8I|A "J&"JI9If "J&"JJOIw "J&"JGIp "J&"JI;IuJ "J&"JI<Iq "J&"II=Nf "J+"JGIrA "J&"JI>NsW "J+"JJQI{y "J&"JI@NSl "J+"JJRrB "I"JG@h "I"JGAs "I"JGEem "I"JGH|M "I"JGIqB "I"JGKqB "I"JGLjA "I"JGMh "I"JGNqX "I"JGOro "I"JGPew "I"JGQh "I"JGTq "I"JGVq "I"JGXc "I"JHPqA "I"JGZqW "I"JG[{y "I"JG\Uc "I"JG]r "J"FFh "J"FFs "J"FFj "J"FFh "J"FGq "J"FGr "J"FGe "J"FGh "J"FGup "J"FGc "J"FG q "J"FG | "J"FGnqc "J"GGnh "J"GGns "J"GGney "J"GGn|k "J"GGnqc "J"GG nrc "J"GG!njc "J"GG"nh "J"GG#nqw "J"GG$nrv "J"GG%neq "J"GG&nh "J"GG)ne "J"GG.nqc "J"GG/nqt "J"GG0n| "J"GG1nUc "J"GG2r "J"G4Fh "J"G4F "J"G5Fs "J"G4Fe "J"G6F| "J"G4Fq "J"G6Fq "J"G6Fj "J"G6Fh "J"G6Gq "J"G6Gr "J"G6Ge "J"G6G| "J"G4G| "J"G4Gh "J"G4Gw "J"G4Gup "J"G4Gq "J"G4G c "J"G6G q "J"G4G q "J"G6G | "J"G4GU "J"G6Gjsc "JG7npc "J"G8Gnqc "J"G8Gnh "J"G8Gn} "J"G9Gns "J"G8Gney "J"G:Gn|k "J"G8Gnqx "J"G:Gnqc "J"G:G nrc "J"G:G!njc "J"G:G"nh "J"G:G#nqw "J"G:G$nrv "J"G:G%neq "J"G:G&n|c "J"G8G'n{c "J"G8G(nh "J"G8G)nw "J"G8G*nq "J"G8G+nqh "J"G8G,nq "J"G8G-ne "J"G:G.nqc "J"G8G/nqt "J"G:G0n| "J"G8G1nUc "J"G:G2rB "J"JG@h "J"JGAs "J"JGEem "J"JGH|M "J"JGIqB "J"JGKjB "J"JGMh "J"JGNqX "J"JGOro "J"JGPew "J"JGQh "J"JGTq "J"JGVc "J"JGYqB "J"JGZqW "J"JG[{y "J"JG\nqc "J"JGnh "J"JGns "J"JGney "J"JGn|k "J"JGnqc "J"JG nrc "J"JG!njb "J"JG"nh "J"JG#nqw "J"JG$nrv "J"JG%neq "J"JG&nh "J"JG)nq "J"JG-ne "J"JG.nqc "J"JG/nqt "J"JG0n| "J"JG1nUb "J"JG2rB "J"JG@h "J"JGAs "J"JGEem "J"JGH|M "J"JGIqB "J"JGKqB "J"JGLj@ "J"JGMh "J"JGNqX "J"JGOro "J"JGPew "J"JGQ|A "J"JGR|A "J"JGSh "J"JGTq "J"JGXqA "J"JGZqW "J"JG[{y "J"JG\Uc "J"JG]npb "J"JGnqc "J"JGnh "J"JGn}"J"JGns "J"JGney "J"JGn|k "J"JGnqx "J"JGnqc "J"JG nrc "J"JG!njc "J"JG"nh "J"JG#nqw "J"JG$nrv "J"JG%neq "J"JG&n|b "J"JG'n{c "J"JG(nh "J"JG)nw "J"JG*nq "J"JG+nqh "J"JG,nq "J"JG-ne "J"JG.nqc "J"JG/nqt "J"JG0n| "J"JG1nUc "J"JG2;r"J"HG;t"J"HG;e "J"JH;|"J"HG;q "J"JH;i "J"JH;h "J"JH;q "J"JH;r "J"JH;_ "J"JH;f"J"HH;m"J"HH;t"J"HH;f "J"JG;l"J"HH;{ "J"JH;|"J"HH ;V "J"JGHp "J"HHHp "J"HHHg "J"HHHt "J"HHHe "J"HGH| "J"HHHr "J"HGHq "J"HGHr "J"HGHj "J"HGHg "J"HGHq "J"HGHq "J"HGHe "J"HGH| "J"HHH| "J"HHHf "J"HGHw "J"HHHq "J"HHHc "J"HG Hr "J"HHHr "J"HGH| "J"HHHU "J"HG@sb "JHNjc "J"HGNpc "J"HGNg "J"HGJ} "J"HGNt "J"HGJfy "J"IGN|k "J"HGJqw "J"IGJpc "J"IGJrc "J"IGJjc "J"IGJg "J"IGJqu "J"IGJqv "J"IGJe "J"IGN|b "J"HGN|c "J"HGNh "J"HGNw "J"HG*Nq "J"HGNtk "J"HGNq "J"HGJf "J"IGNqc "J"HGJqt "J"IGN{ "J"HGJUj "J"IGNlA "J"HGNpA "J"HGNg "J"HGNt "J"HGNfV "J"HGN|M "J"HGNpA "J"HGNrA "J"HGNjA "J"HGNg "J"HGNqS "J"HGNr_ "J"HGNel "J"HGN{B "J"HGNh "J"HGNp "J"HGNq "J"HGNf "J"HGNrA "J"HGNrW "J"HGN|y "J"HGNRV "J"HG;j"J"HG;r"J"HG;`"J"HG;t"J"HG;e "J"JH;|"J"HG;q "J"JH;r "J"JH;i "J"JH;h "J"JH;q "J"JH;r "J"JH;_ "J"JH;|"J"HG;f"J"HH;m"J"HH;m"J"HH;t"J"HH;f "J"JG;l"J"HH;{ "J"JH;|"J"HH ;V "J"JG;`"J"II;t"J"II ;k"J"I I ;k"J"I I;f"J"I I;f"J"I I;q"J"I I;k"J"I I;c"J"II;p"J"II;t"J"II;g"J"IH;j"J"II;|"J"II!MjA "J"I%I&MqB "J"I%I'Mg "J"I%I(Mt "J"I%I+Mld "J"I-I.M|M "J"I%I/MqA "J"I-I1MqA "J"I-I2MkA "J"I-I3Mi "J"I-I4MrS "J"I-I5Mro "J"I-I6Mlk "J"I-I7M|B "J"I%I8Mf "J"I%I:Mp "J"I%I;MuJ "J"I%I<Mq "J"I%I=Mf "J"I-GMrA "J"I%I>MsW "J"I-I?M{y "J"I%I@MRV "J"I-GClc "J"IDHCqc "J"IDHCg "J"IDHCn "J"IFHCt "J"IDHCj "J"IHHC{k "J"IDH Crx "J"IHH!Crc "J"IHH"Crc "J"IHH#Ckb "J"IHH$Ci "J"IHH%Cqu "J"IHH&Cro "J"IHH'Cju "J"IHH(C|b "J"IDH)C|b "J"IDH*Cf "J"IDH+Cx "J"IDH,Cp "J"IDH-Cuk "J"IDH.Cq "J"IDH/Cf "J"IHH0Crc "J"IDH1Cqt "J"IHH2C| "J"IDH3CRr "J"IHH4PjA "J"IJI&PqB "J"IJI'Pg "J"IJI(Pt "J"IJI+Mld "J"ILI.P|M "J"IJI/MqA "J"ILI1MqA "J"ILI2MkA "J"ILI3Mi "J"ILI4MrS "J"ILI5Mro "J"ILI6Mlk "J"ILI7P|B "J"IJI8P|A "J"IJI9Pf "J"IJI:Pw "J"IJGPp "J"IJI;PuJ "J"IJI<Pq "J"IJI=Mf "J"ILGPrA "J"IJI>MsW "J"ILI?P{y "J"IJI@MRV "J"ILG;k"J"INI;k"J"INI;`"J"INI;t"J"INI ;k"J"IPI ;|"J"INI ;f"J"IPI;r"J"IPI;k"J"IPI;f"J"IPI;f"J"IPI;q"J"IPI;k"J"IPI;z"J"INI;c"J"INI;p"J"INI;l"J"IQI;t"J"IRI;g"J"ISH;j"J"INI;z"J"IPI ;|"J"INI!;V"J"ISG;j"J"IVG;r"J"IVG;`"J"IVG;t"J"IVG;e "J"JH;|"J"IVG;q "J"JH;r "J"JH;i "J"JH;h "J"JH;q "J"JH;r "J"JH;_ "J"JH;{"J"IVG;f"J"IVH;m"J"IVH;m"J"IVH;t"J"IVH;f "J"JG;l"J"IVH;{ "J"JH;|"J"IVH ;V "J"JGIs@ "JINlA "J"IYGNpA "J"IYGNg "J"IYGNY "J"IZGNt "J"IYGIfV "J"HGN|M "J"IYGItU "J"HGIpA "J"HGIr@ "J"HGIj@ "J"HGIg "J"HGIqS "J"HGIr_ "J"HGIel "J"HGN{B "J"IYGN{B "J"IYGNh "J"IYGNw "J"I\GNp "J"IYGNuK "J"I\GNq "J"IYGIf "J"HGNrA "J"IYGIrW "J"HGN|y "J"IYGIRV "J"HGrB "J"HKG@h "J"HKGAs "J"HKGEem "J"HNGHqB "J"HNGKjA "J"HNGMh "J"HNGNqX "J"HNGOro "J"HNGPew "J"HNGQh "J"HKGTq "J"HKGXqA "J"HKGZ{y "J"HKG\Uc "J"HNG]s "J"FFe "J"FFh "J"FGq "J"FGr "J"FGe "J"FGh "J"FGup "J"FGq "J"FG c "J"FG q "J"FG | "J"FGU "J"FGnqc "J"GGnh "J"GGns "J"GGney "J"GGn|k "J"GGnqc "J"GG nrc "J"GG!njc "J"GG"nh "J"GG#nqw "J"GG$nrv "J"GG%neq "J"GG&nh "J"GG)nq "J"GG-ne "J"GG.nqc "J"GG/n| "J"GG1nUc "J"GG2r "J"G4Fh "J"G4Fs "J"G4Fe "J"G6F| "J"G4Fq "J"G6Fq "J"G6Fq "J"G6Fj "J"G6Fh "J"G6Gq "J"G6Gr "J"G6Ge "J"G6G| "J"G4Gh "J"G4Gw "J"G4Gup "J"G4Gq "J"G4G q "J"G4G c "J"G6G q "J"G4G q "J"G6G | "J"G4GU "J"G6Gjsc "JG7npc "J"G8Gnqc "J"G8Gnh "J"G8Gn} "J"G9Gns "J"G8Gney "J"G:Gn|k "J"G8Gnqx "J"G:Gnqc "J"G:G nrc "J"G:G!njc "J"G:G"nh "J"G:G#nqw "J"G:G$nrv "J"G:G%neq "J"G:G&n|c "J"G8G'n{c "J"G8G(nh "J"G8G)nw "J"G8G*nq "J"G8G+nqh "J"G8G,nq "J"G8G-ne "J"G:G.nqc "J"G8G/nqt "J"G:G0n| "J"G8G1nUc "J"G:G2rB "J"JG@h "J"JGAs "J"JGEem "J"JGHqB "J"JGKjB "J"JGMh "J"JGNqX "J"JGOro "J"JGPew "J"JGQh "J"JGTq "J"JGVc "J"JGYqB "J"JGZqW "J"JG[{y "J"JG\Uc "J"JG]nqc "J"JGnh "J"JGns "J"JGney "J"JGnqc "J"JG nrc "J"JG!njc "J"JG"nh "J"JG#nqw "J"JG$nrv "J"JG%neq "J"JG&nh "J"JG)nq "J"JG-ne "J"JG.nqc "J"JG/nqt "J"JG0n| "J"JG1nUc "J"JG2rB "J"JG@h "J"JGAs "J"JGEem "J"JGH|M "J"JGIqB "J"JGKqB "J"JGLj@ "J"JGMh "J"JGNqX "J"JGOro "J"JGPew "J"JGQ|A "J"JGRh "J"JGTq "J"JGXc "J"JGYqA "J"JGZ{y "J"JG\Uc "J"JG]npb "J"JGnqc "J"JGnh "J"JGns "J"JGney "J"JGn|k "J"JGnqx "J"JGnqc "J"JG nrc "J"JG!njc "J"JG"nh "J"JG#nqw "J"JG$nrv "J"JG%neq "J"JG&n|b "J"JG'n{c "J"JG(nh "J"JG)nw "J"JG*nq "J"JG+nqh "J"JG,nq "J"JG-ne "J"JG.nqc "J"JG/nqt "J"JG0n| "J"JG1nUc "J"JG2;`"J"KG;t"J"KG;e"K"JH;|"J"KG;q"K"JH;r"K"JH;i"K"JH;h"K"JH;q"K"JH;r"K"JH;_"K"JH;f"J"KH;m"J"KH;t"J"KH;f"K"IG;l"J"KH;{"K"JH;|"J"KH ;V"K"JGHp "K"HHHp "K"HHHg "K"HHHt "K"HHHe "K"IGH| "K"HHHr "K"IGHq "K"IGHr "K"IGHj "K"IGHg "K"IGHq "K"IGHq "K"IGHe "K"IGH| "K"HHH| "K"HHHf "K"HGHw "K"HHHr "K"HHHq "K"HHHc "K"IG Hr "K"HHHr "K"IGH| "K"HHHU "K"IGJjc "K"IGJpc "K"IGJg "K"IGJ} "K "HGJt "K"IGJfy "K "JAGJ|k "K"IGJqw "K "JAGJpc "K "JAGJrc "K "JAGJjc "K "JAGJg "K "JAGJqu "K "JAGJqv "K "JAGJe "K "JAGJ|c "K"IGJ|c "K"IGJh "K"IGJw "K"IG*Jtk "K"IGJq "K"IGJf "K "JAGJqc "K"IGJqt "K "JAGJ{ "K"IGJUj "K "JAGNpA "K"HGNg "K"HGNt "K"HGNfV "K"IGN|M "K"HGNpA "K"IGNrA "K"IGNjA "K"IGNg "K"IGNqS "K"IGNr_ "K"IGNel "K"IGNh "K"HGNw "K"HGNp "K"HGNuK "K"HGNq "K"HGNf "K"IGNrA "K"HGNrW "K"IGN|y "K"HGNRV "K"IG;j"J"K G;r"J"K G;`"J"K G;t"J"K G;e"K"JH;|"J"K G;{"K"JH;q"K"JH;r"K"JH;i"K"JH;h"K"JH;q"K"JH;r"K"JH;_"K"JH;|"J"K G;{"J"K G;f"J"K H;m"J"K G;m"J"K H;m"J"K H;t"J"K H;f"K"IG;l"J"K H;{"K"JH;|"J"K H ;V"K"JG;k"K "II;`"K "II;t"K "II ;k"K"I I ;k"K"I I;f"K"I I;f"K"I I;q"K"I I;k"K"I I;c"K "II;p"K "II;t"K "II;g"K"IH;j"K "II;V"K"IGMjA "K"II&MqB "K"II'Mg "K"II(MY "K"JuGMt "K"II+Mld "K"KI.M|M "K"II/MqA "K"KI1MqA "K"KI2MkA "K"KI3Mi "K"KI4MrS "K"KI5Mro "K"KI6Mlk "K"KI7M|B "K"II8M|A "K"II9Mf "K"II:Mw "K"IGMuJ "K"II<Mq "K"II=Mf "K"KGMrA "K"II>MsW "K"KI?M{y "K"II@MRV "K"KGClc "K"JHCqc "K"JHCg "K"JHCn "K"JHCt "K"JHCj "K"KHC{k "K"JH Crx "K"KH!Crc "K"KH"Crc "K"KH#Ckb "K"KH$Ci "K"KH%Cqu "K"KH&Cro "K"KH'Cju "K"KH(C|b "K"JH)C|b "K"JH*Cf "K"JH+Cx "K"JH,Cp "K"JH-Cuk "K"JH.Cq "K"JH/Cf "K"KH0Crc "K"JH1Cqt "K"KH2C| "K"JH3CRr "K"KH4MqB "K"II'Mg "K"II(Mt "K"II+Pld "K"KI.M|M "K"II/PqA "K"KI1PqA "K"KI2PkA "K"KI3Pi "K"KI4PrS "K"KI5Pro "K"KI6Plk "K"KI7M|B "K"II8M|A "K"II9Mf "K"II:Mw "K"IGMp "K"II;Mq "K"II=Pf "K"KGMrA "K"II>PsW "K"KI?M{y "K"II@PRV "K"KG;k"K "INI;k"K "INI;`"K "INI;t"K "INI ;k"K"IPI ;|"K "INI ;f"K"IPI;r"K"IPI;k"K"IPI;f"K"IPI;f"K"IPI;q"K"IPI;k"K"IPI;z"K "INI;c"K "INI;p"K "INI;t"K "IRI;g"K"ISH;j"K "INI;z"K"IPI ;|"K "INI!;V"K"ISG;r"K"KG;`"K"KG;t"K"KG;e"K"JH;|"K"KG;q"K"JH;r"K"JH;i"K"JH;h"K"JH;q"K"JH;r"K"JH;_"K"JH;f"K"KH;m"K"KH;m"K"KH;t"K"KH;f"K"IG;l"K"KH;{"K"JH;|"K"KH ;V"K"JGIs@ "KINlA "K"IYGNpA "K"IYGNg "K"IYGNY "K"IZGNt "K"IYGNfV "K"IGN|M "K"IYGNtU "K"IGNpA "K"IGNrA "K"IGNjA "K"IGNg "K"IGNqS "K"IGNr_ "K"IGNel "K"IGN{B "K"IYGN{B "K"IYGNh "K"IYGNw "K"I\GNp "K"IYGNuK "K"I\GNq "K"IYGNf "K"IGNrA "K"IYGNrW "K"IGN|y "K"IYGNRV "K"IGrB "J"HKG@h "J"HKGAs "J"HKGEem "J"HNGH|M "J"HKGIqB "J"HNGKjA "J"HNGMh "J"HNGNqX "J"HNGOro "J"HNGPew "J"HNGQh "J"HKGTq "J"HKGVqA "J"HKGZqW "J"HNG[{y "J"HKG\Uc "J"HNG]ir "K"FFih "K"FFis "K"FFie "K "FFi| "K"FFiq "K "FFij "K "FFih "K "FGiq "K "FGir "K "FGie "K "FGih "K"FGiq "K"FG ic "K "FG iq "K"FG iq "K "FG i| "K"FGfqc "K""HXGfh "K""HXGfs "K""HXGfey "K$"H\Gf|k "K""HXGfqc "K$"H\G frc "K$"H\G!fjc "K$"H\G"fh "K$"H\G#fqw "K$"H\G$frv "K$"H\G%feq "K$"H\G&fh "K""HXG)fq "K""HXG-fe "K$"H\G.fqc "K""HXG/fqt "K$"H\G0f| "K""HXG1fUc "K$"H\G2ir "K"G4Fih "K"G4Fi "K"G5Fis "K"G4Fie "K "G6Fi| "K"G4Fiq "K "G6Fiq "K "G6Fiq "K "G6Fij "K "G6Fih "K "G6Giq "K "G6Gir "K "G6Gie "K "G6Gi| "K"G4Gih "K"G4Giw "K"G4Gip "K"G4Giq "K"G4G iq "K"G4G ic "K "G6G iq "K"G4G iq "K "G6G i| "K"G4GiU "K "G6G^sc "K!H^fpb "K""H_Gfqc "K""H_Gfh "K""H_Gf} "K#"H`Gfs "K""H_Gfey "K$"HaGf|k "K""H_Gfqx "K$"HaGfqc "K$"HaG frc "K$"HaG!fjc "K$"HaG"fh "K$"HaG#fqw "K$"HaG$frv "K$"HaG%feq "K$"HaG&f|b "K""H_G'f{c "K""H_G(fh "K""H_G)fw "K""HbG*fq "K""H_G+fqh "K""HbG,fq "K""H_G-fe "K$"HaG.fqc "K""H_G/fqt "K$"HaG0f| "K""H_G1fUc "K$"HaG2irB "K"IG@ih "K"IGAis "K"IGEnem "K'"IGHnqB "K'"IGKnqB "K'"IGLnjA "K'"IGMnh "K'"IGNnqX "K'"IGOnro "K'"IGPnew "K'"IGQi|A "K"IGSih "K"IGTiq "K"IGXnc "K'"IGYiqA "K"IGZnqW "K'"IG[i{y "K"IG\nUc "K'"IG]fpb "K""HjGfqc "K""HjGfh "K""HjGf} "K#"HkGfs "K""HjGfey "K$"HlGf|k "K""HjGfqx "K$"HlGfqc "K$"HlG frc "K$"HlG!fjc "K$"HlG"fh "K$"HlG#fqw "K$"HlG$frv "K$"HlG%feq "K$"HlG&f|b "K""HjG'f{c "K""HjG(fh "K""HjG)fw "K""HmG*fq "K""HjG+fq "K""HjG-fe "K$"HlG.fqc "K""HjG/fqt "K$"HlG0f| "K""HjG1fUc "K$"HlG2irB "K"IG@ih "K"IGAn` "K&"IGDis "K"IGEnem "K'"IGHi|M "K"IGInqV "K'"IGJnqB "K'"IGKnqB "K'"IGLnj@ "K'"IGMnh "K'"IGNnqX "K'"IGOnro "K'"IGPnew "K'"IGQih "K"IGTiq "K"IGXnc "K'"IGYiqA "K"IGZnqW "K'"IG[i{y "K"IG\nUc "K'"IG]^sb "K!Hqfpc "K""HrGfqc "K""HrGfh "K""HrGf} "K#"HsGfs "K""HrGfey "K$"HtGf|k "K""HrGfqx "K$"HtGfqc "K$"HtG frc "K$"HtG!fjc "K$"HtG"fh "K$"HtG#fqw "K$"HtG$frv "K$"HtG%feq "K$"HtG&f|c "K""HrG'f{c "K""HrG(fh "K""HrG)fw "K""HuG*fq "K""HrG+fqh "K""HuG,fq "K""HrG-fe "K$"HtG.fqc "K""HrG/fqt "K$"HtG0f| "K""HrG1fUc "K$"HtG2;r"K)"IG;`"K)"IG;t"K)"IG;e"K+"IH;|"K)"IG;q"K+"IH;r"K+"IH;i"K+"IH;h"K+"IH;q"K+"IH;r"K+"IH;_"K+"IH;f"K)"IH;m"K)"IG;m"K)"IH;t"K)"IH;f"K+"IG;l"K)"IH;{"K+"IH;|"K)"IH ;V"K+"IG@s "K,IHp "K-"IHHp "K-"IHHg "K-"IHH "K."IHHt "K-"IHHe "K/"IGH| "K-"IHHr "K/"IGHq "K/"IGHr "K/"IGHj "K/"IGHg "K/"IGHq "K/"IGHq "K/"IGHe "K/"IGH| "K-"IHH| "K-"IHHf "K-"IGHw "K-"IHHm "K-"IHHr "K-"IHHq "K-"IHHc "K/"IG Hr "K-"IHHr "K/"IGH| "K-"IHHU "K/"IG@sb "K0IJjc "K1"IGJpc "K1"IGJg "K1"IGJ} "K2"HGJt "K1"IGJfy "K3"JAGJ|k "K1"IGJqw "K3"JAGJpc "K3"JAGJrc "K3"JAGJjc "K3"JAGJg "K3"JAGJqu "K3"JAGJqv "K3"JAGJe "K3"JAGJ|c "K1"IGJ|c "K1"IGJh "K1"IGJw "K1"IG*Jq "K1"IGJtk "K1"IGJq "K1"IGJf "K3"JAGJqc "K1"IGJqt "K3"JAGJ{ "K1"IGJUj "K3"JAGNlA "K-"IGNpA "K-"IGNg "K-"IGNY "K."IGNt "K-"IGNfV "K/"IGN|M "K-"IGNtU "K/"IGNpA "K/"IGNrA "K/"IGNjA "K/"IGNg "K/"IGNqS "K/"IGNr_ "K/"IGNel "K/"IGN{B "K-"IGN{B "K-"IGNh "K-"IGNw "K-"IGNp "K-"IGNuK "K-"IGNq "K-"IGNf "K/"IGNrA "K-"IGNrW "K/"IGN|y "K-"IGNRV "K/"IG;j"K)"IG;r"K)"IG;`"K)"IG;"K*"IGs;t"K)"IG;e"K+"IH;|"K)"IG;{"K+"IH;q"K+"IH;r"K+"IH;i"K+"IH;h"K+"IH;q"K+"IH;r"K+"IH;_"K+"IH;|"K)"IG;{"K)"IG;f"K)"IH;m"K)"IG;m"K)"IH;t"K)"IH;f"K+"IG;l"K)"IH;{"K+"IH;|"K)"IH ;V"K+"IG;k"K5"II;`"K5"II;t"K5"II ;k"K7"I I ;|"K5"II ;f"K7"I I;r"K7"I I;k"K7"I I;f"K7"I I;f"K7"I I;q"K7"I I;k"K7"I I;z"K5"II;c"K5"II;p"K5"II;t"K5"II;g"K7"IH;j"K5"II;z"K7"I I ;|"K5"II!;V"K7"IGIs@ "K8IMjA "K9"II&MqB "K9"II'Mg "K9"II(MY "K:"JuGMt "K9"II+Mld "K;"K<I.M|M "K9"II/MqU "K;"K<I0MqA "K;"K<I1MqA "K;"K<I2MkA "K;"K<I3Mi "K;"K<I4MrS "K;"K<I5Mro "K;"K<I6Mlk "K;"K<I7M|B "K9"II8M|A "K9"II9Mf "K9"II:Mw "K9"IGMp "K9"II;MuJ "K9"II<Mq "K9"II=Mf "K;"K<GMrA "K9"II>MsW "K;"K<I?M{y "K9"II@MRV "K;"K<G4sb "K=JClc "K>"JHCqc "K>"JHCg "K>"JHCn "K?"JHCt "K>"JHCj "K@"KHC{k "K>"JH Crx "K@"KH!Crc "K@"KH"Crc "K@"KH#Ckb "K@"KH$Ci "K@"KH%Cqu "K@"KH&Cro "K@"KH'Cju "K@"KH(C|b "K>"JH)C|b "K>"JH*Cf "K>"JH+Cx "K>"JH,Cp "K>"JH-Cuk "K>"JH.Cq "K>"JH/Cf "K@"KH0Crc "K>"JH1Cqt "K@"KH2C| "K>"JH3CRr "K@"KH4Is@ "K8IMjA "K9"II&MqB "K9"II'Mg "K9"II(PY "K:"JGMt "K9"II+Pld "K;"KAI.M|M "K9"II/PqU "K;"KAI0PqA "K;"KAI1PqA "K;"KAI2PkA "K;"KAI3Pi "K;"KAI4PrS "K;"KAI5Pro "K;"KAI6Plk "K;"KAI7M|B "K9"II8M|A "K9"II9Mf "K9"II:Mw "K9"IGMp "K9"II;MuJ "K9"II<Mq "K9"II=Pf "K;"KAGMrA "K9"II>PsW "K;"KAI?M{y "K9"II@PRV "K;"KAG;k"K5"INI;k"K5"INI;`"K5"INI;"K6"IOG;t"K5"INI ;k"K7"IPI ;|"K5"INI ;z"K7"IPI;f"K7"IPI;r"K7"IPI;k"K7"IPI;f"K7"IPI;f"K7"IPI;q"K7"IPI;k"K7"IPI;|"K5"INI;z"K5"INI;c"K5"INI;m"K5"IQH;p"K5"INI;t"K5"IRI;g"K7"ISH;j"K5"INI;z"K7"IPI ;|"K5"INI!;V"K7"ISG;j"K)"IG;r"K)"IG;`"K)"IG;t"K)"IG;e"K+"IH;|"K)"IG;q"K+"IH;r"K+"IH;i"K+"IH;h"K+"IH;q"K+"IH;r"K+"IH;_"K+"IH;f"K)"IH;m"K)"IG;m"K)"IH;m"K)"IH;t"K)"IH;f"K+"IG;l"K)"IH;{"K+"IH;|"K)"IH ;V"K+"IGIs@ "K,IIlA "K-"IGIpA "K-"IGIg "K-"IGIY "K."IGIt "K-"IGNfV "K/"IGI|M "K-"IGNtU "K/"IGNpA "K/"IGNrA "K/"IGNjA "K/"IGNg "K/"IGNqS "K/"IGNr_ "K/"IGNel "K/"IGI{B "K-"IGI{B "K-"IGIh "K-"IGIw "K-"IGIp "K-"IGIuK "K-"IGIq "K-"IGNf "K/"IGIrA "K-"IGNrW "K/"IGI|y "K-"IGNRV "K/"IGirB "K"HKG@ih "K"HKGAis "K"HKGEnem "K'"HNGHi|M "K"HKGInqB "K'"HNGKnqB "K'"HNGLnjA "K'"HNGMnh "K'"HNGNnqX "K'"HNGOnro "K'"HNGPnew "K'"HNGQih "K"HKGTiq "K"HKGViq "K"HKGXnc "K'"HNHPiqA "K"HKGZnqW "K'"HNG[i{y "K"HKG\nUc "K'"HNG]Or "KC"FFOs "KC"FFPe "KE"FFPj "KE"FFPh "KE"FGPq "KE"FGPr "KE"FGPe "KE"FGOh "KC"FGOq "KC"FG Pc "KE"FG Oq "KC"FG O| "KC"FGPU "KE"FGMpc "KG"GGMg "KG"GGMt "KG"GGJey "KI"GGM|k "KG"GGJqc "KI"GG Jrc "KI"GG!Jjc "KI"GG"Jh "KI"GG#Jqw "KI"GG$Jrv "KI"GG%Jeq "KI"GG&Mh "KG"GG)Mq "KG"GGJe "KI"GG.Mqc "KG"GGJqt "KI"GG0M{ "KG"GGJUj "KI"GGOr "KC"G4FOh "KC"G4FOs "KC"G4FPe "KE"G6FO| "KC"G4FPq "KE"G6FPq "KE"G6FPj "KE"G6FPh "KE"G6GPq "KE"G6GPr "KE"G6GPe "KE"G6GOh "KC"G4GOp "KC"G4GOq "KC"G4G Pc "KE"G6G Oq "KC"G4G Pq "KE"G6G O| "KC"G4GPU "KE"G6GMjc "KG"G8GMpc "KG"G8GMg "KG"G8GMt "KG"G8GJey "KI"G:GM|k "KG"G8GJqc "KI"G:G Jrc "KI"G:G!Jjc "KI"G:G"Jh "KI"G:G#Jqw "KI"G:G$Jrv "KI"G:G%Jeq "KI"G:G&M|c "KG"G8GMh "KG"G8G)Mw "KG"G8G*Mq "KG"G8GMq "KG"G8GJe "KI"G:G.Mqc "KG"G8GJqt "KI"G:G0M{ "KG"G8GJUj "KI"G:GVs "KK"HdGE\em "KM"IGH\qB "KM"IGK\h "KM"IGN\qX "KM"IGO\ro "KM"IGP\ew "KM"IGQVh "KK"HdGTVq "KK"HdGX\c "KM"IGYVqB "KK"HdGZ\qW "KM"IG[\Uc "KM"IG]Mpc "KG"JGMg "KG"JGMt "KG"JGJey "KI"JGM|k "KG"JGJqc "KI"JG Jrc "KI"JG!Jjb "KI"JG"Jh "KI"JG#Jqw "KI"JG$Jrv "KI"JG%Jeq "KI"JG&Mh "KG"JG)Mq "KG"JGJe "KI"JG.Mqc "KG"JGJqt "KI"JG0M{ "KG"JGJUj "KI"JGVrB "KK"HnG@Vh "KK"HnGAVs "KK"HnGE\em "KM"IGHV|M "KK"HnGI\qB "KM"IGK\qB "KM"IGL\jB "KM"IGM\h "KM"IGN\qX "KM"IGO\ro "KM"IGP\ew "KM"IGQVh "KK"HnGTVq "KK"HnGX\c "KM"IGYVqB "KK"HnGZ\qW "KM"IG[V{y "KK"HnG\\Uc "KM"IG]Mjc "KG"JGMpc "KG"JGMg "KG"JGMt "KG"JGJey "KI"JGM|k "KG"JGJqc "KI"JG Jrc "KI"JG!Jjc "KI"JG"Jh "KI"JG#Jqw "KI"JG$Jrv "KI"JG%Jeq "KI"JG&M|b "KG"JGM|c "KG"JGMh "KG"JG)Mw "KG"JG*Mq "KG"JGMq "KG"JGJe "KI"JG.Mqc "KG"JGJqt "KI"JG0M{ "KG"JGJUj "KI"JG;` "KS"HG;t "KS"HG;e"KU"KVH;| "KS"HG;q"KU"KVH;i"KU"KVH;h"KU"KVH;q"KU"KVH;r"KU"KVH;_"KU"KVH;f "KS"HH;m "KS"HH;t "KS"HH;f"KU"KVJ ;l "KS"HH;| "KS"HH ;V"KU"KVKWHp "KY"KZHHg "KY"KZHHt "KY"KZHHe"K\"HGH| "KY"KZHHq"K\"HGHr"K\"HGHj"K\"HGHg"K\"HGHq"K\"HGHq"K\"HGHe"K\"HGH| "KY"KZHH| "KY"KZHHf "KY"KZGHw "KY"HHHm "KY"KZHHq "KY"KZHHf"K\"HK]Hr "KY"KZHHr"K\"HGH| "KY"KZHHU"K\"HGHjc "K_"K`GHpc "K_"K`GHg "K_"K`GHt "K_"K`GHfy "Kb"IGH|k "K_"K`GHpc "Kb"IGHrc "Kb"IGHjc "Kb"IGHg "Kb"IGHqu "Kb"IGHqv "Kb"IGHe "Kb"IGH|c "K_"K`GHh "K_"K`GHtk "K_"K`GHq "K_"K`GHc "Kb"IHPHqc "K_"K`GHqt "Kb"IGH{ "K_"K`GHUj "Kb"IGNjA "KY"KcI&NqB "KY"KcI'Ng "KY"KcI(Nt "KY"KcI+NfV"K\"HGN|M "KY"KcI/NpA"K\"HGNrA"K\"HGNjA"K\"HGNg"K\"HGNqS"K\"HGNr_"K\"HGNel"K\"HGNh "KY"KcGNp "KY"KcI;NuJ "KY"HI<Nq "KY"KcI=Nf"K\"HK]NrA "KY"KcI>NrW"K\"HGN{y "KY"KcI@NSl"K\"HJR;r "KS"HG;` "KS"HG;t "KS"HG;e"KU"KdH;| "KS"HG;q"KU"KdH;r"KU"KdH;i"KU"KdH;h"KU"KdH;q"KU"KdH;r"KU"KdH;_"KU"KdH;f "KS"HH;m "KS"HH;t "KS"HH;f"KU"KdJ ;l "KS"HH;{"KU"KdH;| "KS"HH ;V"KU"KdKW;k "Kf"II;` "Kf"II;t "Kf"II ;k"Kh"I I ;f"Kh"I I;r"Kh"I I;k"Kh"I I;f"Kh"I I;f"Kh"I I;q"Kh"I I;k"Kh"I I;c "Kf"II;p "Kf"II;t "Kf"II;j "Kf"II;S"Kh"IJ$MjA "Kj"I%I&MqB "Kj"I%I'Mg "Kj"I%I(MK "Kk"I*HMt "Kj"I%I+Mld"Kl"I-I.M|M "Kj"I%I/MqU"Kl"I-I0MqA"Kl"I-I1MqA"Kl"I-I2MkA"Kl"I-I3Mi"Kl"I-I4MrS"Kl"I-I5Mro"Kl"I-I6Mlk"Kl"I-I7M|B "Kj"I%I8M|A "Kj"I%I9Mf "Kj"I%I:Mw "Kj"I%HMuJ "Kj"I%I<Mq "Kj"I%I=Mf"Kl"I-K]MrA "Kj"I%I>MsW"Kl"I-I?M{y "Kj"I%I@MSl"Kl"I-JRCpc "Kn"IDJCg "Kn"IDJCu "Kn"IDJCl{ "Kp"KqJC|k "Kn"IDJCpc "Kp"KqJCqc "Kp"KqJCib "Kp"KqJCi "Kp"KqJCpu "Kp"KqJCro "Kp"KqJClq "Kp"KqJC|b "Kn"IDJCf "Kn"IDJCm} "Kn"IDJCp "Kn"IDJCq "Kn"IDJCf "Kp"KqJBCpc "Kn"IDJCst "Kp"KqJC| "Kn"IDJCSc "Kp"KqJPqB "Kj"IJI'Pg "Kj"IJI(Pt "Kj"IJI+Mld"Kl"ILI.P|M "Kj"IJI/MqA"Kl"ILI1MqA"Kl"ILI2MkA"Kl"ILI3Mi"Kl"ILI4MrS"Kl"ILI5Mro"Kl"ILI6Mlk"Kl"ILI7Pf "Kj"IJI:Pp "Kj"IJI;Pq "Kj"IJI=Mf"Kl"ILK]PrA "Kj"IJI>MsW"Kl"ILI?P{y "Kj"IJI@MSl"Kl"ILJR;k "Kf"INI;` "Kf"INI;"Kg"IOG;t "Kf"INI ;k"Kh"IPI ;| "Kf"INI ;f"Kh"IPI;r"Kh"IPI;k"Kh"IPI;f"Kh"IPI;f"Kh"IPI;q"Kh"IPI;k"Kh"IPI;z "Kf"INI;c "Kf"INI;p "Kf"INI;t "Kf"IRI;g"Kh"ISH;j "Kf"INI;z"Kh"IPI ;| "Kf"INI!;S"Kh"ISJ$;r "Kr"IVG;` "Kr"IVG;t "Kr"IVG;e"KU"KsH;| "Kr"IVG;r"KU"KsH;i"KU"KsH;h"KU"KsH;q"KU"KsH;r"KU"KsH;_"KU"KsH;f "Kr"IVH;m "Kr"IVH;t "Kr"IVH;f"KU"KsJ ;l "Kr"IVH;{"KU"KsH;| "Kr"IVH ;V"KU"KsKWIs@ "KXIIjA "KY"KtI&IqB "KY"KtI'Ig "KY"KtI(IK "K["IHIt "KY"KtI+IfV"K\"HGI|M "KY"KtI/ItU"K\"HGIpA"K\"HGIr@"K\"HGIj@"K\"HGIg"K\"HGIqS"K\"HGIr_"K\"HGIel"K\"HGI|B "KY"KtI8I|A "KY"KtI9Ih "KY"KtGNw "KY"I\HIp "KY"KtI;NuJ "KY"I\I<Iq "KY"KtI=If"K\"HK]IrA "KY"KtI>IrW"K\"HGI{y "KY"KtI@ISl"K\"HJRVrB "KK"HKG@Vh "KK"HKGAVs "KK"HKGE\em "KM"HNGH\qB "KM"HNGK\qB "KM"HNGL\jA "KM"HNGM\h "KM"HNGN\qX "KM"HNGO\ro "KM"HNGPVh "KK"HKGTVq "KK"HKGVVq "KK"HKGX\c "KM"HNHPVqA "KK"HKGZ\qW "KM"HNG[\Uc "KM"HNG]8r "Kv"FF8s "Kv"FF7e "Kx"FF8| "Kv"FF7q "Kx"FF7q "Kx"FF7j "Kx"FF7h "Kx"FG7q "Kx"FG7r "Kx"FG7e "Kx"FG8h "Kv"FG8w "Kv"FG8q "Kv"FG 7c "Kx"FG 8q "Kv"FG 7q "Kx"FG 8| "Kv"FG1qc "Kz"HXG1h "Kz"HXG1s "Kz"HXG1ey "K|"H\G1|k "Kz"HXG1qc "K|"H\G 1rc "K|"H\G!1jc "K|"H\G"1h "K|"H\G#1qw "K|"H\G$1rv "K|"H\G%1eq "K|"H\G&1h "Kz"HXG)1w "Kz"H]G*1q "Kz"HXG-1e "K|"H\G.1qc "Kz"HXG/1qt "K|"H\G01| "Kz"HXG11Uc "K|"H\G2,s "KuG38q "Kv"G4F8r "Kv"G4F8h "Kv"G4F9 "Kw"G5F8s "Kv"G4F7e "Kx"G6F8| "Kv"G4F7q "Kx"G6F7q "Kx"G6F7q "Kx"G6F7j "Kx"G6F7h "Kx"G6G7q "Kx"G6G7r "Kx"G6G7e "Kx"G6G8| "Kv"G4G8| "Kv"G4G8h "Kv"G4G8w "Kv"G4G8p "Kv"G4G8q "Kv"G4G 7c "Kx"G6G 8q "Kv"G4G 7q "Kx"G6G 8| "Kv"G4G7U "Kx"G6G'sc "KyH^1pb "Kz"H_G1qc "Kz"H_G1h "Kz"H_G1} "K{"H`G1s "Kz"H_G1ey "K|"HaG1|k "Kz"H_G1qx "K|"HaG1qc "K|"HaG 1rc "K|"HaG!1jc "K|"HaG"1h "K|"HaG#1qw "K|"HaG$1rv "K|"HaG%1eq "K|"HaG&1|b "Kz"H_G'1{c "Kz"H_G(1h "Kz"H_G)1w "Kz"HbG*1q "Kz"H_G+1qh "Kz"HbG,1q "Kz"H_G-1e "K|"HaG.1qc "Kz"H_G/1qt "K|"HaG01| "Kz"H_G11Uc "K|"HaG28rB "Kv"KG@8h "Kv"KGA8s "Kv"KGE;em "K"KGH8|M "Kv"KGI;qB "K"KGK;jA "K"KGM;h "K"KGN;qX "K"KGO;ro "K"KGP;ew "K"KGQ8h "Kv"KGT8x "Kv"KGU8q "Kv"KGX;c "K"KGY8qA "Kv"KGZ;qW "K"KG[8{y "Kv"KG\;Uc "K"KG]1pc "Kz"KG1qc "Kz"KG1h "Kz"KG1s "Kz"KG1ey "K|"KG1|k "Kz"KG1qc "K|"KG 1rc "K|"KG!1jb "K|"KG"1h "K|"KG#1qw "K|"KG$1rv "K|"KG%1eq "K|"KG&1|c "Kz"KG'1{c "Kz"KG(1h "Kz"KG)1w "Kz"HmG*1q "Kz"KG+1qh "Kz"HmG,1q "Kz"KG-1e "K|"KG.1qc "Kz"KG/1qt "K|"KG01| "Kz"KG11Ub "K|"KG21sA "K}K8qA "Kv"KG?8rB "Kv"KG@8h "Kv"KGA>` "K"KGD8s "Kv"KGE;em "K"KGH8|M "Kv"KGI;qV "K"KGJ;qB "K"KGK;qB "K"KGL;jA "K"KGM;h "K"KGN;qX "K"KGO;ro "K"KGP;ew "K"KGQ8|A "Kv"KGR8|A "Kv"KGS8h "Kv"KGT8x "Kv"KGU8q "Kv"KGV8qI "Kv"KGW8q "Kv"KGX;c "K"KGY8qA "Kv"KGZ;qW "K"KG[8{y "Kv"KG\;Uc "K"KG]'sb "KyK1pc "Kz"KG1qc "Kz"KG1h "Kz"KG1s "Kz"KG1ey "K|"KG1|k "Kz"KG1qx "K|"KG1qc "K|"KG 1rc "K|"KG!1jb "K|"KG"1h "K|"KG#1qw "K|"KG$1rv "K|"KG%1eq "K|"KG&1|c "Kz"KG'1{c "Kz"KG(1h "Kz"KG)1w "Kz"HuG*1q "Kz"KG+1qh "Kz"HuG,1q "Kz"KG-1e "K|"KG.1qc "Kz"KG/1qt "K|"KG01| "Kz"KG11Ub "K|"KG2;r "K"IG;` "K"IG;t "K"IG;e"K"IH;q"K"IH;i"K"IH;h"K"IH;q"K"IH;r"K"IH;_"K"IH;f "K"IH;m "K"IG;m "K"IH;t "K"IH;f"K"IG;l "K"IH;{"K"IH;| "K"IH ;V"K"IG7p "K"IH7g "K"IH7t "K"IHFe "K"KG7| "K"IHFr "K"KGFq "K"KGFr "K"KGFj "K"KGFg "K"KGFq "K"KGFq "K"KGFe "K"KG7| "K"IH7| "K"IH7f "K"IG7w "K"IH7r "K"IH7q "K"IHFc "K"KG 7r "K"IHFr "K"KG7| "K"IHFU "K"KGFsW "K"I-I?M{y "K"I%I@FRV "K"I-G=lc "K"IDH=qc "K"IDH=g "K"IDH<n "K"IFH=t "K"IDHBj "K"KqH={k "K"IDH Brx "K"KqH!Brc "K"KqH"Brc "K"KqH#Bkb "K"KqH$Bi "K"KqH%Bqu "K"KqH&Bro "K"KqH'Bju "K"KqH(=|b "K"IDH)=|b "K"IDH*=f "K"IDH+=x "K"IDH,=p "K"IDH-=uk "K"IDH.=q "K"IDH/Bf "K"KqH0=rc "K"IDH1Bqt "K"KqH2=| "K"IDH3BRr "K"KqH4OjA "K"IJI&OqB "K"IJI'Og "K"IJI(Ot "K"IJI+Fld "K"ILI.O|M "K"IJI/FqU "K"ILI0FqA "K"ILI1FqA "K"ILI2FkA "K"ILI3Fi "K"ILI4FrS "K"ILI5Fro "K"ILI6Flk "K"ILI7O|B "K"IJI8O|A "K"IJI9Of "K"IJI:Ow "K"IJGOp "K"IJI;OuJ "K"IJI<Oq "K"IJI=Ff "K"ILGOrA "K"IJI>FsW "K"ILI?O{y "K"IJI@FRV "K"ILG;y"KIM;k "K"INI;k "K"INI;` "K"INI;"K"IOG;t "K"INI ;k"K"IPI ;| "K"INI ;z"K"IPI;f"K"IPI;r"K"IPI;k"K"IPI;f"K"IPI;f"K"IPI;q"K"IPI;k"K"IPI;| "K"INI;z "K"INI;c "K"INI;m "K"IQH;p "K"INI;t "K"IRI;g"K"ISH;j "K"INI;z"K"IPI ;| "K"INI!;V"K"ISG;r "K"IG;` "K"IG;t "K"IG;e"K"IH;| "K"IG;q"K"IH;i"K"IH;h"K"IH;q"K"IH;r"K"IH;f "K"IH;m "K"IG;m "K"IH;m "K"IH;t "K"IH;f"K"IG;l "K"IH;{"K"IH;| "K"IH ;V"K"IG7lA "K"IG7pA "K"IG7g "K"IG7t "K"IGFfV "K"KG7|M "K"IGFpA "K"KGFr@ "K"KGFj@ "K"KGFg "K"KGFqS "K"KGFr_ "K"KGFel "K"KG7{B "K"IG7h "K"IG7w "K"IG7p "K"IG7uK "K"IG7q "K"IGFf "K"KG7rA "K"IGFrW "K"KG7|y "K"IGFRV "K"KG8rB "Kv"HKG@8h "Kv"HKGA8s "Kv"HKGE;em "K"HNGH8|M "Kv"HKGI;qB "K"HNGK;qB "K"HNGL;jA "K"HNGM;h "K"HNGN;qX "K"HNGO;ro "K"HNGP;ew "K"HNGQ8h "Kv"HKGT8q "Kv"HKGV8q "Kv"HKGX;c "K"HNHP8qA "Kv"HKGZ;qW "K"HNG[8{y "Kv"HKG\;Uc "K"HNG]rr "K"FFrh "K"FFr| "K"FFuj "K"FFuh "K"FGuq "K"FGur "K"FGrh "K"FGrq "K"FG uc "K"FG rq "K"FG uU "K"FGeqc "K"GGeh "K"GGes "K"GGeey "K"GGe|k "K"GGeqc "K"GG erc "K"GG!ejc "K"GG"eh "K"GG#eqw "K"GG$erv "K"GG%eeq "K"GG&eh "K"GG)eq "K"GG-ee "K"GG.eqc "K"GG/eqt "K"GG0e| "K"GG1eUc "K"GG2rr "K"G4Frh "K"G4Frs "K"G4Fue "K"G6Fr| "K"G4Fuq "K"G6Fuq "K"G6Fuh "K"G6Guq "K"G6Gur "K"G6Gue "K"G6Grh "K"G4Grp "K"G4Grq "K"G4G uc "K"G6G rq "K"G4G r| "K"G4Gepc "K"G8Geqc "K"G8Geh "K"G8Ges "K"G8Geey "K"G:Ge|k "K"G8Geqc "K"G:G erc "K"G:G!ejc "K"G:G"eh "K"G:G#eqw "K"G:G$erv "K"G:G%eeq "K"G:G&e|c "K"G8G'eh "K"G8G)ew "K"G8G*eq "K"G8G+eqh "K"G8G,eq "K"G8G-ee "K"G:G.eqc "K"G8G/eqt "K"G:G0e| "K"G8G1eUc "K"G:G2wrB "K"KG@wh "K"KGAyem "K"KGHyjA "K"KGMyh "K"KGNyqX "K"KGOyro "K"KGPyew "K"KGQyc "K"KGYyqW "K"KG[yUc "K"KG]eqc "K"JGeh "K"JGes "K"JGeey "K"KGe|k "K"JGeqc "K"KG erc "K"KG!ejb "K"KG"eh "K"KG#eqw "K"KG$erv "K"KG%eeq "K"KG&eh "K"JG)eq "K"JG+eq "K"JG-ee "K"KG.eqc "K"JG/eqt "K"KG0e| "K"JG1eUb "K"KG2wh "K"KGAws "K"KGEyem "K"KGHw|M "K"KGIyqB "K"KGKyh "K"KGNyqX "K"KGOyro "K"KGPyew "K"KGQwq "K"KGXyc "K"KGYwqA "K"KGZepb "K"JGeqc "K"JGeh "K"JGes "K"JGeey "K"KGe|k "K"JGeqc "K"KG erc "K"KG!ejb "K"KG"eh "K"KG#eqw "K"KG$erv "K"KG%eeq "K"KG&eh "K"JG)eqh "K"JG,eq "K"JG-ee "K"KG.eqc "K"JG/eqt "K"KG0e| "K"JG1eUb "K"KG2;r"K"KG;`"K"KG;t"K"KG;e"K"JH;|"K"KG;i"K"JH;q"K"JH;r"K"JH;f"K"KH;m"K"KH;t"K"KH;l"K"KH;{"K"JH;|"K"KH ;V"K"JGHp "K"HHHg "K"HHHt "K"HHHe "K"KGH| "K"HHHq "K"KGHr "K"KGHj "K"KGHg "K"KGHq "K"KGHq "K"KGHe "K"KGHf "K"HGHq "K"HHHc "K"KG Hr "K"HHHr "K"KGH| "K"HHHU "K"KGJpc "K"IGJg "K"IGJt "K"IGJfy "K"IGJ|k "K"IGJqw "K"IGJpc "K"IGJrc "K"IGJjc "K"IGJg "K"IGJqu "K"IGJqv "K"IGJe "K"IGJ|c "K"IGJh "K"IGJq "K"IGJq "K"IGJf "K"IGJqc "K"IGJqt "K"IGJ{ "K"IGJUj "K"IGNpA "K"HGNg "K"HGNt "K"HGNfV "K"KGN|M "K"HGNpA "K"KGNrA "K"KGNjA "K"KGNg "K"KGNqS "K"KGNr_ "K"KGNel "K"KGNh "K"HGNp "K"HGNq "K"HGNf "K"KGNrA "K"HGNrW "K"KGN|y "K"HGNRV "K"KG;r"K"KG;`"K"KG;t"K"KG;e"K"JH;|"K"KG;q"K"JH;r"K"JH;i"K"JH;h"K"JH;q"K"JH;r"K"JH;_"K"JH;f"K"KH;m"K"KH;t"K"KH;f"K"JG;l"K"KH;{"K"JH;|"K"KH ;V"K"JG;k"K"II;`"K"II;t"K"II ;k"K"I I ;|"K"II ;k"K"I I;f"K"I I;f"K"I I;q"K"I I;k"K"I I;c"K"II;p"K"II;t"K"II;g"K"IH;j"K"II;z"K"I I ;|"K"II!;V"K"IGMqB "K"II'Mg "K"II(Mt "K"II+Mld "K"KI.M|M "K"II/MqA "K"KI1MqA "K"KI2MkA "K"KI3Mi "K"KI4MrS "K"KI5Mro "K"KI6Mlk "K"KI7Mf "K"II:Mw "K"IGMq "K"II=Mf "K"KGMrA "K"II>MsW "K"KI?M{y "K"II@MRV "K"KGCqc "K"KHCg "K"KHCt "K"KHCj "K"KHC{k "K"KH Crc "K"KH"Crc "K"KH#Ckb "K"KH$Ci "K"KH%Cqu "K"KH&Cro "K"KH'Cju "K"KH(Cf "K"KH+Cx "K"KH,Cp "K"KH-Cq "K"KH/Cf "K"KH0Crc "K"KH1Cqt "K"KH2C| "K"KH3CRr "K"KH4MqB "K"II'Mg "K"II(Mt "K"II+Pld "K"KI.M|M "K"II/PqA "K"KI1PqA "K"KI2PkA "K"KI3Pi "K"KI4PrS "K"KI5Pro "K"KI6Plk "K"KI7Mf "K"II:Mp "K"II;Mq "K"II=Pf "K"KGMrA "K"II>PsW "K"KI?M{y "K"II@PRV "K"KG;k"K"INI;`"K"INI;t"K"INI ;k"K"IPI ;|"K"INI ;f"K"IPI;r"K"IPI;k"K"IPI;f"K"IPI;f"K"IPI;q"K"IPI;k"K"IPI;c"K"INI;p"K"INI;t"K"IRI;j"K"INI;z"K"IPI ;|"K"INI!;V"K"ISG;r"K"KG;`"K"KG;t"K"KG;e"K"JH;|"K"KG;q"K"JH;r"K"JH;i"K"JH;h"K"JH;q"K"JH;_"K"JH;f"K"KH;m"K"KHNlA "K"IYGNpA "K"IYGNg "K"IYGNt "K"IYGNfV "K"KGN|M "K"IYGNpA "K"KGNrA "K"KGNjA "K"KGNg "K"KGNqS "K"KGNr_ "K"KGNel "K"KGN{B "K"IYGNh "K"IYGNp "K"IYGNuK "K"I\GNq "K"IYGNf "K"KGNrA "K"IYGNrW "K"KGN|y "K"IYGNRV "K"KGwrB "K"HKG@wh "K"HKGAws "K"HKGEyem "K"HNGHw|M "K"HKGIyjA "K"HNGMyh "K"HNGNyqX "K"HNGOyro "K"HNGPwh "K"HKGTyc "K"HNHPwqA "K"HKGZyqW "K"HNG[yUc "K"HNG]]r "K"FF]h "K"FF]s "K"FFYq "K"FFYj "K"FFYh "K"FGYq "K"FGYr "K"FGYe "K"FG]h "K"FGYc "K"FG ]q "K"FG Yq "K"FG ]| "K"FGYU "K"FGTqc "K"GGTh "K"GGTs "K"GGUey "K"GGT|k "K"GGUqc "K"GG Urc "K"GG!Ujc "K"GG"Uh "K"GG#Uqw "K"GG$Urv "K"GG%Ueq "K"GG&Th "K"GG)Tq "K"GG-Ue "K"GG.Tqc "K"GG/Uqt "K"GG0T| "K"GG1UUc "K"GG2]r "K"G4F]h "K"G4F]s "K"G4F]| "K"G4FYq "K"G6FYq "K"G6FYj "K"G6FYh "K"G6GYq "K"G6GYr "K"G6GYe "K"G6G]| "K"G4G]h "K"G4G]w "K"G4G]p "K"G4G]q "K"G4G Yc "K"G6G ]q "K"G4G Yq "K"G6G ]| "K"G4GYU "K"G6GTpc "K"G8GTqc "K"G8GTh "K"G8GTs "K"G8GUey "K"G:GT|k "K"G8GUqc "K"G:G Urc "K"G:G!Ujc "K"G:G"Uh "K"G:G#Uqw "K"G:G$Urv "K"G:G%Ueq "K"G:G&T|c "K"G8G'T{c "K"G8G(Th "K"G8G)Tw "K"G8G*Tq "K"G8G+Tqh "K"G8G,Tq "K"G8G-Ue "K"G:G.Tqc "K"G8G/Uqt "K"G:G0T| "K"G8G1UUc "K"G:G2crB "K"HdG@ch "K"HdGAcs "K"HdGEbem "K"IGHc|M "K"HdGIbqB "K"IGKbjA "K"IGMbh "K"IGNbqX "K"IGObro "K"IGPbew "K"IGQch "K"HdGTcq "K"HdGVbc "K"IGYcqB "K"HdGZbqW "K"IG[c{y "K"HdG\bUc "K"IG]Tqc "K"JGTh "K"JGTs "K"JGUey "K"KGT|k "K"JGUqc "K"KG Urc "K"KG!Ujc "K"KG"Uh "K"KG#Uqw "K"KG$Urv "K"KG%Ueq "K"KG&Th "K"JG)Tq "K"JG+Tq "K"JG-Ue "K"KG.Tqc "K"JG/Uqt "K"KG0T| "K"JG1UUc "K"KG2cqB "K"HnG?crB "K"HnG@ch "K"HnGAcs "K"HnGEbem "K"IGHc|M "K"HnGIbqB "K"IGKbqB "K"IGLbjB "K"IGMbh "K"IGNbqX "K"IGObro "K"IGPbew "K"IGQch "K"HnGTcx "K"HnGUcq "K"HnGXbc "K"IGYcqB "K"HnGZbqW "K"IG[c{y "K"HnG\bUc "K"IG]Tpb "K"JGTqc "K"JGTh "K"JGO} "K"KGTs "K"JGUey "K"KGT|k "K"JGUqx "K"KGUqc "K"KG Urc "K"KG!Ujc "K"KG"Uh "K"KG#Uqw "K"KG$Urv "K"KG%Ueq "K"KG&T|b "K"JG'T{c "K"JG(Th "K"JG)Tw "K"JG*Tq "K"JG+Tqh "K"JG,Tq "K"JG-Ue "K"KG.Tqc "K"JG/Uqt "K"KG0T| "K"JG1UUc "K"KG2;r"K"KG;`"K"KG;t"K"KG;e"K"IH;|"K"KG;q"K"IH;i"K"IH;h"K"IH;q"K"IH;r"K"IH;_"K"IH;f"K"KH;m"K"KH;l"K"KH;{"K"IH;|"K"KH Hp "K"KZHHg "K"KZHH "L"IHHt "K"KZHHe "L"IGH| "K"KZHHq "L"IGHr "L"IGHj "L"IGHg "L"IGHq "L"IGHq "L"IGHe "L"IGH| "K"KZHHf "K"KZGHm "K"KZHHq "K"KZHHc "L"IG Hr "K"KZHHr "L"IGH| "K"KZHHU "L"IGJjc "L"K`GJpc "L"K`GJg "L"K`GJ} "L"HGJt "L"K`GJfy "L"IGJ|k "L"K`GJqw "L"IGJpc "L"IGJrc "L"IGJjc "L"IGJg "L"IGJqu "L"IGJqv "L"IGJe "L"IGJ|c "L"K`GJ|c "L"K`GJh "L"K`GJw "L"K`G*Jq "L"K`GJf "L"IGJqc "L"K`GJqt "L"IGJ{ "L"K`GJUj "L"IGNlA "K"KcGNpA "K"KcGNg "K"KcGNt "K"KcGNfV "L"IGN|M "K"KcGNpA "L"IGNrA "L"IGNjA "L"IGNg "L"IGNqS "L"IGNr_ "L"IGNel "L"IGNh "K"KcGNp "K"KcGNq "K"KcGNf "L"IGNrA "K"KcGNrW "L"IGN|y "K"KcGNRV "L"IG;r"K"LG;`"K"LG;t"K"LG;e"K"IH;|"K"LG;{"K"IH;q"K"IH;r"K"IH;i"K"IH;h"K"IH;q"K"IH;r"K"IH;_"K"IH;|"K"LG;{"K"LG;f"K"LH;m"K"LG;m"K"LH;t"K"HH;f"K"KdG;l"K"LH;{"K"IH;|"K"LH ;V"K"KdG;k"L "II;`"L "II;t"L "II ;k"L "I I ;|"L "II ;k"L "I I;f"L "I I;f"L "I I;q"L "I I;k"L "I I;c"L "II;p"L "II;t"L "II;g"L "IH;j"L "II;z"L "I I ;|"L "II!;V"L "IGMjA "L "I%I&MqB "L "I%I'Mg "L "I%I(MY "L"I*GMt "L "I%I+Mld "L"I-I.M|M "L "I%I/MqU "L"I-I0MqA "L"I-I1MqA "L"I-I2MkA "L"I-I3Mi "L"I-I4MrS "L"I-I5Mro "L"I-I6Mlk "L"I-I7M|B "L "I%I8M|A "L "I%I9Mf "L "I%I:Mw "L "I%GMuJ "L "I%I<Mq "L "I%I=Mf "L"I-GMrA "L "I%I>MsW "L"I-I?M{y "L "I%I@MRV "L"I-GClc "L"IDHCqc "L"IDHCg "L"IDHCn "L"IFHCt "L"IDHCj "L"KqHC{k "L"IDH Crx "L"KqH!Crc "L"KqH"Crc "L"KqH#Ckb "L"KqH$Ci "L"KqH%Cqu "L"KqH&Cro "L"KqH'Cju "L"KqH(C|b "L"IDH)C|b "L"IDH*Cf "L"IDH+Cx "L"IDH,Cp "L"IDH-Cuk "L"IDH.Cq "L"IDH/Cf "L"KqH0Crc "L"IDH1Cqt "L"KqH2C| "L"IDH3CRr "L"KqH4PqB "L "IJI'Pg "L "IJI(Pt "L "IJI+Mld "L"ILI.P|M "L "IJI/MqA "L"ILI1MqA "L"ILI2MkA "L"ILI3Mi "L"ILI4MrS "L"ILI5Mro "L"ILI6Mlk "L"ILI7Pf "L "IJI:Pp "L "IJI;PuJ "L "IJI<Pq "L "IJI=Mf "L"ILGPrA "L "IJI>MsW "L"ILI?P{y "L "IJI@MRV "L"ILG;k"L "INI;k"L "INI;`"L "INI;t"L "INI ;k"L "IPI ;|"L "INI ;f"L "IPI;r"L "IPI;k"L "IPI;f"L "IPI;f"L "IPI;q"L "IPI;k"L "IPI;z"L "INI;c"L "INI;m"L "IQH;p"L "INI;l"L "IQI;t"L "IRI;g"L "ISH;j"L "INI;z"L "IPI ;|"L "INI!;V"L "ISG;r"L"LG;`"L"LG;t"L"LG;e"K"IH;|"L"LG;q"K"IH;r"K"IH;i"K"IH;h"K"IH;q"K"IH;r"K"IH;_"K"IH;f"L"LH;m"L"LH;t"L"IVH;f"K"KsG;l"L"LH;{"K"IH;|"L"LH ;V"K"KsGIs@ "KIIlA "K"KtGIpA "K"KtGIg "K"KtGIY "L"IGIt "K"KtGNfV "L"IGI|M "K"KtGNtU "L"IGNpA "L"IGNrA "L"IGNjA "L"IGNg "L"IGNqS "L"IGNr_ "L"IGNel "L"IGI{B "K"KtGI{B "K"KtGIh "K"KtGNw "K"I\GIp "K"KtGNuK "K"I\GIq "K"KtGNf "L"IGIrA "K"KtGNrW "L"IGI|y "K"KtGNRV "L"IGcrB "K"HKG@ch "K"HKGAcs "K"HKGEbem "K"HNGHbqB "K"HNGKbjA "K"HNGMbh "K"HNGNbqX "K"HNGObro "K"HNGPbew "K"HNGQch "K"HKGTcq "K"HKGVbc "K"HNHPcqA "K"HKGZbUc "K"HNG]Cr "L"FFCh "L"FFCs "L"FFC| "L"FF;q "L"FF;q "L"FF;h "L"FG;q "L"FG;r "L"FG;e "L"FGCh "L"FGCq "L"FG ;c "L"FG Cq "L"FG ;q "L"FG C| "L"FG;U "L"FG2qc "L"HXG2h "L"HXG2s "L"HXG4ey "L"H\G2|k "L"HXG4qc "L"H\G 4rc "L"H\G!4jc "L"H\G"4h "L"H\G#4qw "L"H\G$4rv "L"H\G%4eq "L"H\G&2h "L"HXG)2q "L"HXG-4e "L"H\G.2qc "L"HXG/4qt "L"H\G02| "L"HXG14Uc "L"H\G2Cq "L"G4FCr "L"G4FCh "L"G4FCs "L"G4F;e "L"G6FC| "L"G4F;q "L"G6F;q "L"G6F;q "L"G6F;j "L"G6F;h "L"G6G;q "L"G6G;r "L"G6G;e "L"G6GC| "L"G4GC| "L"G4GCh "L"G4GCw "L"G4GCp "L"G4GCq "L"G4G ;c "L"G6G Cq "L"G4G ;q "L"G6G C| "L"G4G;U "L"G6G3sc "LH^2pb "L"H_G2qc "L"H_G2h "L"H_G2} "L"H`G2s "L"H_G4ey "L"HaG2|k "L"H_G4qx "L"HaG4qc "L"HaG 4rc "L"HaG!4jc "L"HaG"4h "L"HaG#4qw "L"HaG$4rv "L"HaG%4eq "L"HaG&2|b "L"H_G'2{c "L"H_G(2h "L"H_G)2w "L"HbG*2q "L"H_G+2qh "L"HbG,2q "L"H_G-4e "L"HaG.2qc "L"H_G/4qt "L"HaG02| "L"H_G14Uc "L"HaG2CrB "L"KG@Ch "L"KGACs "L"KGE@em "L!"KGHC|M "L"KGI@qB "L!"KGK@qB "L!"KGL@jA "L!"KGM@h "L!"KGN@qX "L!"KGO@ro "L!"KGP@ew "L!"KGQCh "L"KGTCq "L"KGX@c "L!"KGYCqA "L"KGZ@qW "L!"KG[C{y "L"KG\@Uc "L!"KG]2pb "L"L"G2qc "L"L"G2h "L"L"G2} "L"L#G2s "L"L"G4ey "L"KG2|k "L"L"G4qx "L"KG4qc "L"KG 4rc "L"KG!4jb "L"KG"4h "L"KG#4qw "L"KG$4rv "L"KG%4eq "L"KG&2|b "L"L"G'2{c "L"L"G(2h "L"L"G)2w "L"L"G*2q "L"L"G+2q "L"L"G-4e "L"KG.2qc "L"L"G/4qt "L"KG02| "L"L"G14Ub "L"KG2CqA "L"KG?CrB "L"KG@Ch "L"KGA;` "L "KGDCs "L"KGE@em "L!"KGHC|M "L"KGI@qV "L!"KGJ@qB "L!"KGK@qB "L!"KGL@jA "L!"KGM@h "L!"KGN@qX "L!"KGO@ro "L!"KGP@ew "L!"KGQC|A "L"KGRC|A "L"KGSCh "L"KGTCx "L"KGUCqI "L"KGWCq "L"KGX@c "L!"KGYCqA "L"KGZ@qW "L!"KG[C{y "L"KG\@Uc "L!"KG]3sb "LK2pb "L"L$G2qc "L"L$G2h "L"L$G2} "L"L%G2s "L"L$G4ey "L"KG2|k "L"L$G4qx "L"KG4qc "L"KG 4rc "L"KG!4jb "L"KG"4h "L"KG#4qw "L"KG$4rv "L"KG%4eq "L"KG&2|b "L"L$G'2{c "L"L$G(2h "L"L$G)2w "L"L$G*2q "L"L$G+2qh "L"L$G,2q "L"L$G-4e "L"KG.2qc "L"L$G/4qt "L"KG02| "L"L$G14Ub "L"KG2;r"L'"IG;`"L'"IG;t"L'"IG;e"L)"IH;|"L'"IG;q"L)"IH;r"L)"IH;i"L)"IH;h"L)"IH;q"L)"IH;r"L)"IH;_"L)"IH;f"L'"IH;m"L'"IH;t"L'"IH;l"L'"IH;{"L)"IH;|"L'"IH ;V"L)"IGHp "L+"IHHg "L+"IHH "L,"IHHt "L+"IHHe "L-"KGH| "L+"IHHq "L-"KGHr "L-"KGHj "L-"KGHg "L-"KGHq "L-"KGHq "L-"KGHe "L-"KGH| "L+"IHH| "L+"IHHf "L+"IGHw "L+"IHHr "L+"IHHq "L+"IHHc "L-"KG Hr "L+"IHHr "L-"KGH| "L+"IHHU "L-"KGAjc"L/"IGApc"L/"IGAg"L/"IG5} "L0"HGAt"L/"IGBfy "L1"KGA|k"L/"IGBqw "L1"KGBpc "L1"KGBrc "L1"KGBjb "L1"KGBg "L1"KGBqu "L1"KGBqv "L1"KGBe "L1"KGA|c"L/"IGA|c"L/"IGAh"L/"IGAw"L/"IG*Atk"L/"IGAq"L/"IGBf "L1"KGAqc"L/"IGBqt "L1"KGA{"L/"IGBUj "L1"KGNlA "L+"IGNpA "L+"IGNg "L+"IGNt "L+"IGNfV "L-"KGN|M "L+"IGNpA "L-"KGNrA "L-"KGNjA "L-"KGNg "L-"KGNqS "L-"KGNr_ "L-"KGNel "L-"KGNh "L+"IGNw "L+"IGNp "L+"IGNuK "L+"IGNq "L+"IGNf "L-"KGNrA "L+"IGNrW "L-"KGN|y "L+"IGNRV "L-"KG;"L&K;j"L'"IG;r"L'"IG;`"L'"IG;"L("IGs;t"L'"IG;e"L)"IH;|"L'"IG;{"L)"IH;q"L)"IH;r"L)"IH;i"L)"IH;h"L)"IH;q"L)"IH;r"L)"IH;_"L)"IH;|"L'"IG;{"L'"IG;f"L'"IH;m"L'"IG;m"L'"IH;t"L'"IH;f"L)"IG;l"L'"IH;{"L)"IH;|"L'"IH ;V"L)"IG;k"L3"II;`"L3"II;t"L3"II ;k"L5"I I ;|"L3"II ;f"L5"I I;r"L5"I I;k"L5"I I;f"L5"I I;f"L5"I I;q"L5"I I;k"L5"I I;c"L3"II;m"L3"IH;p"L3"II;t"L3"II;g"L5"IH;j"L3"II;z"L5"I I ;|"L3"II!;V"L5"IGMjA "L7"II&MqB "L7"II'Mg "L7"II(MY "L8"I*GMt "L7"II+Mld "L9"I-I.M|M "L7"II/MqU "L9"I-I0MqA "L9"I-I1MqA "L9"I-I2MkA "L9"I-I3Mi "L9"I-I4MrS "L9"I-I5Mro "L9"I-I6Mlk "L9"I-I7M|B "L7"II8M|A "L7"II9Mf "L7"II:Mw "L7"IGMq "L7"II=Mf "L9"I-GMrA "L7"II>MsW "L9"I-I?M{y "L7"II@MRV "L9"I-G5sb "L:L;?lc "L<"IDH?qc "L<"IDH?g "L<"IDH<n "L="IFH?t "L<"IDHCj "L>"KqH?{k "L<"IDH Crx "L>"KqH!Crc "L>"KqH"Crc "L>"KqH#Ckb "L>"KqH$Ci "L>"KqH%Cqu "L>"KqH&Cro "L>"KqH'Cju "L>"KqH(?|b "L<"IDH)?|b "L<"IDH*?f "L<"IDH+?x "L<"IDH,?p "L<"IDH-?uk "L<"IDH.?q "L<"IDH/Cf "L>"KqH0?rc "L<"IDH1Cqt "L>"KqH2?| "L<"IDH3CRr "L>"KqH4MjA "L7"II&MqB "L7"II'Mg "L7"II(PY "L8"IKGMt "L7"II+Mld "L9"ILI.M|M "L7"II/MqU "L9"ILI0MqA "L9"ILI1MqA "L9"ILI2MkA "L9"ILI3Mi "L9"ILI4MrS "L9"ILI5Mro "L9"ILI6Mlk "L9"ILI7M|B "L7"II8M|A "L7"II9Mf "L7"II:Mw "L7"IGMp "L7"II;MuJ "L7"II<Mq "L7"II=Mf "L9"ILGMrA "L7"II>MsW "L9"ILI?M{y "L7"II@MRV "L9"ILG;k"L3"INI;k"L3"INI;`"L3"INI;"L4"IOG;t"L3"INI ;k"L5"IPI ;|"L3"INI ;z"L5"IPI;f"L5"IPI;r"L5"IPI;k"L5"IPI;f"L5"IPI;f"L5"IPI;q"L5"IPI;k"L5"IPI;|"L3"INI;z"L3"INI;c"L3"INI;m"L3"IQH;p"L3"INI;l"L3"IQI;t"L3"IRI;g"L5"ISH;j"L3"INI;z"L5"IPI ;|"L3"INI!;V"L5"ISG;j"L'"IG;r"L'"IG;`"L'"IG;"L("IGs;t"L'"IG;e"L)"IH;|"L'"IG;{"L)"IH;q"L)"IH;r"L)"IH;i"L)"IH;h"L)"IH;q"L)"IH;r"L)"IH;_"L)"IH;{"L'"IG;f"L'"IH;m"L'"IH;t"L'"IH;f"L)"IG;l"L'"IH;{"L)"IH;|"L'"IH ;V"L)"IG5s@ "L*IIlA "L+"IGIpA "L+"IGIg "L+"IGIY "L,"IGIt "L+"IGIfV "L-"KGI|M "L+"IGItU "L-"KGIpA "L-"KGIr@ "L-"KGIj@ "L-"KGIg "L-"KGIqS "L-"KGIr_ "L-"KGIel "L-"KGI{B "L+"IGI{B "L+"IGIh "L+"IGIw "L+"IGIp "L+"IGIuK "L+"IGIq "L+"IGIf "L-"KGIrA "L+"IGIrW "L-"KGI|y "L+"IGIRV "L-"KGCrB "L"HKG@Ch "L"HKGACs "L"HKGE@em "L!"HNGHC|M "L"HKGI@qB "L!"HNGK@qB "L!"HNGL@jA "L!"HNGM@h "L!"HNGN@qX "L!"HNGO@ro "L!"HNGP@ew "L!"HNGQCh "L"HKGTCw "L"HKHOCq "L"HKGV@c "L!"HNHPCqA "L"HKGZ@qW "L!"HNG[C{y "L"HKG\\p "L@"IH\g "L@"IH\t "L@"IH\| "L@"IHXq "LB"FGXr "LB"FGXj "LB"FGXg "LB"FGXq "LB"FGXq "LB"FGXe "LB"FG\f "L@"IGXf "LB"FJ9\r "L@"IHXr "LB"FG\| "L@"IHXU "LB"FI\pc "LD"GG\g "LD"GG\t "LD"GG\fy "LF"GG\|k "LD"GG\pc "LF"GG\rc "LF"GG\jc "LF"GG\g "LF"GG\qu "LF"GG\qv "LF"GG\e "LF"GG\h "LD"GG\q "LD"GG\e "LF"GG.\qc "LD"GG\qt "LF"GG\{ "LD"GG\Uj "LF"GG\p "L@"IH\p "L@"IH\g "L@"IH\t "L@"IH\| "L@"IHXq "LB"G6GXr "LB"G6GXj "LB"G6GXg "LB"G6GXq "LB"G6GXq "LB"G6GXe "LB"G6G\| "L@"IH\f "L@"IG\w "L@"G4H\m "L@"IH\q "L@"IHXf "LB"G6J9\r "L@"IHXr "LB"G6G\| "L@"IHXU "LB"G6IYsc "LCG7\jc "LD"G8G\pc "LD"G8G\g "LD"G8G]} "LE"G9G\t "LD"G8G\fy "LF"G:G\|k "LD"G8G\qw "LF"G:G\pc "LF"G:G\rc "LF"G:G\jc "LF"G:G\g "LF"G:G\qu "LF"G:G\qv "LF"G:G\e "LF"G:G\|c "LD"G8G\|c "LD"G8G\h "LD"G8G\w "LD"G8G*\q "LD"G8G\tk "LD"G8G\q "LD"G8G\e "LF"G:G.\qc "LD"G8G\qt "LF"G:G\{ "LD"G8G\Uj "LF"G:GorB "LI"LJLLog "LI"LJLMot "LI"LJLQmlj "LR"LSLTo|M "LI"LJLUmpB "LR"LSLWmqA "LR"LSLXmjA "LR"LSLYmi "LR"LSLZmrS "LR"LSL[mro "LR"LSL\ml "LR"LSL]of "LI"LJL`oq "LI"LJLdmf "LR"LSLeorA "LI"LJLfmsW "LR"LSLgo|y "LI"LJLhmSi "LR"LSLi\pc "LD"LjG\g "LD"LjG\t "LD"LjG\fy "LF"LlG\|k "LD"LjG\pc "LF"LlG\rc "LF"LlG\jc "LF"LlG\g "LF"LlG\qu "LF"LlG\qv "LF"LlG\e "LF"LlG\h "LD"LjG\q "LD"LjG\q "LD"LjG\e "LF"LlG.\qc "LD"LjG\qt "LF"LlG\{ "LD"LjG\Uj "LF"LlGorB "LI"LnLKorB "LI"LnLLog "LI"LnLMot "LI"LnLQmlj "LR"LpLTo|M "LI"LnLUmqV "LR"LpLVmpB "LR"LpLWmqB "LR"LpLXmjB "LR"LpLYmi "LR"LpLZmrS "LR"LpL[mro "LR"LpL\ml "LR"LpL]o|B "LI"LnL^o|B "LI"LnL_of "LI"LnL`ox "LI"LnLaorH "LI"LnLcoq "LI"LnLdmf "LR"LpLeorB "LI"LnLfmsW "LR"LpLgo|y "LI"LnLhmSi "LR"LpLi\jc "LD"LqG\pc "LD"LqG\g "LD"LqG\t "LD"LqG\fy "LF"LsG\|k "LD"LqG\qw "LF"LsG\pc "LF"LsG\rc "LF"LsG\jb "LF"LsG\g "LF"LsG\qu "LF"LsG\qv "LF"LsG\e "LF"LsG\|c "LD"LqG\|c "LD"LqG\h "LD"LqG\w "LD"LqG*\q "LD"LqG\q "LD"LqG\e "LF"LsG.\qc "LD"LqG\qt "LF"LsG\{ "LD"LqG\Uj "LF"LsG;k "Lu"LvLx;g "Lu"LvLy;u "Lu"LvL};l"L~"LL;| "Lu"LvL;r"L~"LL;r"L~"LL;j"L~"LL;i"L~"LL;p"L~"LL;r"L~"LL;m"L~"LL;f "Lu"LvL;p "Lu"LvL;t "Lu"LvL;f"L~"LL;j "Lu"LvL;z"L~"LL;| "Lu"LvL;S"L~"LLHk "L"LLHq "L"LLHg "L"LLHu "L"LLHl"L"LJ-H| "L"LLHp"L"LJ/Hr"L"LJ0Hk"L"LJ1Hi"L"LJ2Hp"L"LJ3Hr"L"LJ4Hl"L"LJ5H| "L"LLH| "L"LLHg "L"LJ6Hm "L"LLHp "L"LLHq "L"LLHf"L"LK]Hp "L"LLHq"L"LJ:H| "L"LLHS"L"LL@sb "LJ<Jjc "L"LJJpc "L"LJJg "L"LJJ}"L"J?GJu "L"LJNlp "L"KLJ|k "L"LJNrw "L"KLNrc "L"KLNqc "L"KLNjb "L"KLNi "L"KLNpt "L"KLNqv "L"KLNls "L"KLJ{c "L"LJJ|c "L"LJJc "L"LLJw "L"LG*Jp "L"LJJuk "L"LJJq "L"LJNf "L"KJBJpc "L"LJNrt "L"KLJ| "L"LJNSb "L"KLNrA "L"LLKNrB "L"LLLNg "L"LLMNt "L"LLQNlj "L"LLTN|M "L"LLUNpB "L"LLWNqA "L"LLXNjA "L"LLYNi "L"LLZNrS "L"LL[Nro "L"LL\Nl "L"LL]Nf "L"LL`Np "L"LLbNq "L"LLdNf "L"LK]NrA "L"LLfNsW "L"LLgN|y "L"LLhNSi "L"LLi;k "Lu"LLw;k "Lu"LLx;g "Lu"LLy;"Lz"LL|;u "Lu"LL};l"L~"LL;| "Lu"LL;z"L~"LL;r"L~"LL;r"L~"LL;j"L~"LL;i"L~"LL;p"L~"LL;r"L~"LL;m"L~"LL;y "Lu"LL;f "Lu"LL;m "Lu"LL;p "Lu"LL;m "Lu"LL;t "Lu"LL;f"L~"LL;j "Lu"LL;z"L~"LL;| "Lu"LL;S"L~"LL;k "Lu"JXLx;g "Lu"JXLy;u "Lu"JXL};k"L"JaI ;| "Lu"JXL;f"L"JaI;r"L"JaI;k"L"JaI;f"L"JaI;f"L"JaI;q"L"JaI;k"L"JaI;c "Lu"JXI;p "Lu"JXL;t "Lu"JXL;f"L"JiL;j "Lu"JXL;z"L"JaI ;| "Lu"JXL;S"L"JiLMlA "L"JpJqMrB "L"JpJrMf "L"JpJsM_ "L"ILMu "L"JpJvMld "L"LI.M|M "L"JpJyMqU "L"LI0MqA "L"LI1MqA "L"LI2MkA "L"LI3Mi "L"LI4MrS "L"LI5Mro "L"LI6Mlk "L"LI7M{B "L"JpJzMtB "L"JpJ{Mf "L"JpI:Mm "L"JpLMuJ "L"JpJ}Mr "L"JpJ~Mf "L"LJBMrA "L"JpJMsW "L"LI?M|y "L"JpJTSl "L"LJRCjc "L"LLCrb "L"LLCf "L"LLCt "L"LLCl "L"LLC{k "L"LLCkx "L"LLCpc "L"LLCsc "L"LLCib "L"LLCf "L"LLCpu "L"LLCso "L"LLCl "L"LLC{b "L"LLC{b "L"LLCc "L"LLCm} "L"LJCp "L"LLCjk "L"LLCr "L"LLCf "L"LJBCrc "L"LLCqt "L"LLC| "L"LLCOc "L"LLMlA "L"JJqMrB "L"JJrMf "L"JJsMu "L"JJvPld "L"LI.M|M "L"JJyPqA "L"LI1PqA "L"LI2PkA "L"LI3Pi "L"LI4PrS "L"LI5Pro "L"LI6Plk "L"LI7Mf "L"JI:Mp "L"JJ|Mr "L"JJ~Pf "L"LJBMrA "L"JJPsW "L"LI?M|y "L"JJPSl "L"LJR;k "Lu"JLw;k "Lu"JLx;g "Lu"JLy;u "Lu"JL};k"L"JI ;| "Lu"JL;f"L"JI;r"L"JI;k"L"JI;f"L"JI;f"L"JI;q"L"JI;k"L"JI;y "Lu"JL;c "Lu"JI;m "Lu"JL;p "Lu"JL;t "Lu"JL;f"L"JL;j "Lu"JL;z"L"JI ;| "Lu"JL;S"L"JL;k "Lu"LLx;g "Lu"LLy;u "Lu"LL};l"L~"LL;| "Lu"LL;r"L~"LL;r"L~"LL;j"L~"LL;i"L~"LL;p"L~"LL;r"L~"LL;m"L~"LL;f "Lu"LL;p "Lu"LL;t "Lu"LL;f"L~"LL;j "Lu"LL;z"L~"LL;| "Lu"LL;S"L~"LLIs@ "LIIrA "L"LLKIrB "L"LLLIg "L"LLMN_ "L"JLIt "L"LLQNlj "L"LLTI|M "L"LLUNqV "L"LLVNpB "L"LLWNqA "L"LLXNjA "L"LLYNi "L"LLZNrS "L"LL[Nro "L"LL\Nl "L"LL]I|A "L"LL^I|A "L"LL_If "L"LL`Im "L"LLIp "L"LLbIrH "L"LLcIq "L"LLdNf "L"LK]IrA "L"LLfNsW "L"LLgI|y "L"LLhNSi "L"LLiorB "LI"JG@oh "LI"JGAos "LI"JGEmlj "LR"HNLTmqA "LR"HNLXmjA "LR"HNLYmi "LR"HNLZmrS "LR"HNL[mro "LR"HNL\ml "LR"HNL]of "LI"JL`oq "LI"JGVoq "LI"JGXmf "LR"HNLeoqA "LI"JGZmsW "LR"HNLgo{y "LI"JG\mUc "LR"HNG]kr "L"FFkh "L"FFks "L"FFke "L"FFk| "L"FFkq "L"FFkq "L"FFkj "L"FFkh "L"FGkq "L"FGkr "L"FGke "L"FGkh "L"FGkp "L"FGkq "L"FG kc "L"FG kq "L"FG kq "L"FG k| "L"FGkU "L"FGqqc "L"GGqh "L"GGqs "L"GGney "L"GGq|k "L"GGnqc "L"GG nrc "L"GG!njc "L"GG"nh "L"GG#nqw "L"GG$nrv "L"GG%neq "L"GG&qh "L"GG)qq "L"GG-ne "L"GG.qqc "L"GG/nqt "L"GG0q| "L"GG1nUc "L"GG2kr "L"G4Fkh "L"G4Fk "L"G5Fks "L"G4Fke "L"G6Fk| "L"G4Fkq "L"G6Fkq "L"G6Fkq "L"G6Fkj "L"G6Fkh "L"G6Gkq "L"G6Gkr "L"G6Gke "L"G6Gk| "L"G4Gk| "L"G4Gkh "L"G4Gkw "L"G4Gkp "L"G4Gkq "L"G4G kc "L"G6G kq "L"G4G kq "L"G6G k| "L"G4GkU "L"G6Glsc "LG7qpc "L"G8Gqqc "L"G8Gqh "L"G8Gq} "L"G9Gqs "L"G8Gney "L"G:Gq|k "L"G8Gnqx "L"G:Gnqc "L"G:G nrc "L"G:G!njc "L"G:G"nh "L"G:G#nqw "L"G:G$nrv "L"G:G%neq "L"G:G&q|c "L"G8G'q{c "L"G8G(qh "L"G8G)qw "L"G8G*qq "L"G8G+qqh "L"G8G,qq "L"G8G-ne "L"G:G.qqc "L"G8G/nqt "L"G:G0q| "L"G8G1nUc "L"G:G2|rB "L"G>G@|h "L"G>GA|s "L"G>GE|em "M"GGGH|qB "M"GGGK|qB "M"GGGL|jA "M"GGGM|h "M"GGGN|qX "M"GGGO|ro "M"GGGP|ew "M"GGGQ|h "L"G>GT|q "L"G>GX|c "M"GGGY|qA "L"G>GZ|qW "M"GGG[|{y "L"G>G\|Uc "M"GGG]qqc "L"G_Gqh "L"G_Gqs "L"G_Gney "L"GaGq|k "L"G_Gnqc "L"GaG nrc "L"GaG!njb "L"GaG"nh "L"GaG#nqw "L"GaG$nrv "L"GaG%neq "L"GaG&qh "L"G_G)qq "L"G_G+qq "L"G_G-ne "L"GaG.qqc "L"G_G/nqt "L"GaG0q| "L"G_G1nUb "L"GaG2|qA "L"GcG?|rB "L"GcG@|h "L"GcGA|s "L"GcGE|em "M"GeGH||M "L"GcGI|qB "M"GeGK|qB "M"GeGL|jA "M"GeGM|h "M"GeGN|qX "M"GeGO|ro "M"GeGP|ew "M"GeGQ|h "L"GcGT|q "L"GcGX|c "M"GeGY|qA "L"GcGZ|qW "M"GeG[|{y "L"GcG\|Uc "M"GeG]qpb "L"GgGqqc "L"GgGqh "L"GgGq} "L"GhGqs "L"GgGney "L"GiGq|k "L"GgGnqx "L"GiGnqc "L"GiG nrc "L"GiG!njb "L"GiG"nh "L"GiG#nqw "L"GiG$nrv "L"GiG%neq "L"GiG&q|b "L"GgG'q{c "L"GgG(qh "L"GgG)qw "L"GgG*qq "L"GgG+qqh "L"GgG,qq "L"GgG-ne "L"GiG.qqc "L"GgG/nqt "L"GiG0q| "L"GgG1nUb "L"GiG2;r"M"MG;`"M"MG;t"M"MG;e "M"JH;|"M"MG;q "M"JH;r "M"JH;i "M"JH;h "M"JH;q "M"JH;r "M"JH;_ "M"JH;f"M"MH;m"M"MH;t"M"MH;f "M"JG;l"M"MH;{ "M"JH;|"M"MH ;V "M"JGHp "M "HHHg "M "HHHt "M "HHHe "M "HGH| "M "HHHq "M "HGHr "M "HGHj "M "HGHg "M "HGHq "M "HGHq "M "HGHe "M "HGH| "M "HHHf "M "HGHw "M "HGHm "M "HHHq "M "HHHc "M "HG Hr "M "HHHr "M "HGH| "M "HHHU "M "HGJjc "M"MGJpc "M"MGJg "M"MGJ} "M"MGJt "M"MGJfy "M"IGJ|k "M"MGJqw "M"IGJpc "M"IGJrc "M"IGJjc "M"IGJg "M"IGJqu "M"IGJqv "M"IGJe "M"IGJ|c "M"MGJ|c "M"MGJh "M"MGJw "M"MG*Jq "M"MGJq "M"MGJe "M"IG.Jqc "M"MGJqt "M"IGJ{ "M"MGJUj "M"IGQlA "M "HGQpA "M "HGQg "M "HGQY "M "HGQt "M "HGNfV "M "HGQ|M "M "HGNpA "M "HGNrA "M "HGNjA "M "HGNg "M "HGNqS "M "HGNr_ "M "HGNel "M "HGQ{B "M "HGQ{B "M "HGQh "M "HGQw "M "HGQp "M "HGQuK "M "HGQq "M "HGNf "M "HGQrA "M "HGNrW "M "HGQ|y "M "HGNRV "M "HG;j"M"MG;r"M"MG;`"M"MG;"M"HGs;t"M"MG;e "M"JH;|"M"MG;{ "M"JH;q "M"JH;r "M"JH;i "M"JH;h "M"JH;q "M"JH;r "M"JH;_ "M"JH;|"M"MG;{"M"MG;f"M"MH;m"M"MG;m"M"MH;m"M"MH;t"M"MH;f "M"JG;l"M"MH;{ "M"JH;|"M"MH ;V "M"JG;k"M"MI;`"M"MI;t"M"MI ;` "M"MG;|"M"MI ;q "M"MG;r "M"MG;j "M"MG;g "M"MG;p "M"MG;q "M"MG;e "M"MG;h"M"MG;p"M"MI;t"M"MI;g "M"MH;j"M"MI;z "M"MH;|"M"MI!;V "M"MGMjA "M""M#I&MqB "M""M#I'Mg "M""M#I(Mt "M""M#I+MfV "M&"HGM|M "M""M#I/MpA "M&"HGMrA "M&"HGMjA "M&"HGMg "M&"HGMqS "M&"HGMr_ "M&"HGMel "M&"HGM|B "M""M#I8M|A "M""M#I9Mh "M""M#GMw "M""M#GMp "M""M#I;Mq "M""M#I=Mf "M&"HGMrA "M""M#I>MrW "M&"HGM{y "M""M#I@MRV "M&"HGClc "M("HHCqc "M("HHCg "M("HHCn "M)"HHCt "M("HHCj "M*"HHC{k "M("HH Crx "M*"HH!Crc "M*"HH"Crc "M*"HH#Ckb "M*"HH$Ci "M*"HH%Cqu "M*"HH&Cro "M*"HH'Cju "M*"HH(C|b "M("HH)C|b "M("HH*Cf "M("HH+Cx "M("HH,Cp "M("HH-Cq "M("HH/Cf "M*"HH0Crc "M("HH1Cqt "M*"HH2C| "M("HH3CRr "M*"HH4MqB "M""M,I'Mg "M""M,I(Mt "M""M,I+MfV "M&"H8GM|M "M""M,I/MpA "M&"H8GMrA "M&"H8GMjA "M&"H8GMg "M&"H8GMqS "M&"H8GMr_ "M&"H8GMel "M&"H8GMh "M""M,GMp "M""M,I;Mq "M""M,I=Mf "M&"H8GMrA "M""M,I>MrW "M&"H8GM{y "M""M,I@MRV "M&"H8G;k"M"M/I;k"M"M/I;`"M"M/I;t"M"M/I ;` "M"M1G;|"M"M/I ;q "M"M1G;r "M"M1G;j "M"M1G;g "M"M1G;p "M"M1G;q "M"M1G;e "M"M1G;z"M"M/I;h"M"M/G;m"M"M/H;p"M"M/I;l"M"M/I;t"M"M/I;g "M"M2H;j"M"M/I;z "M"M1H;|"M"M/I!;V "M"M2G;r"M4"M5G;`"M4"M5G;t"M4"M5G;e "M"JH;|"M4"M5G;q "M"JH;r "M"JH;i "M"JH;h "M"JH;q "M"JH;r "M"JH;_ "M"JH;f"M4"M5H;m"M4"M5G;m"M4"M5H;t"M4"M5H;f "M"JG;l"M4"M5H;{ "M"JH;|"M4"M5H ;V "M"JGIs@ "MM6NlA "M "HGNpA "M "HGNg "M "HGNY "M "HGNt "M "HGIfV "M "HGN|M "M "HGItU "M "HGIpA "M "HGIr@ "M "HGIj@ "M "HGIg "M "HGIqS "M "HGIr_ "M "HGIel "M "HGN{B "M "HGN{B "M "HGNh "M "HGNw "M "HGNp "M "HGNuK "M "HGNq "M "HGIf "M "HGNrA "M "HGIrW "M "HGN|y "M "HGIRV "M "HG|rB "L"HKG@|h "L"HKGA|s "L"HKGE|em "M"HNGH||M "L"HKGI|qB "M"HNGK|qB "M"HNGL|jA "M"HNGM|h "M"HNGN|qX "M"HNGO|ro "M"HNGP|ew "M"HNGQ|h "L"HKGT|q "L"HKGV|q "L"HKGX|c "M"HNHP|qA "L"HKGZ|qW "M"HNG[|{y "L"HKG\|Uc "M"HNG]p "M8"IHg "M8"IHt "M8"IHe "M:"FG| "M8"IHr "M:"FGj "M:"FGg "M:"FGq "M:"FGq "M:"FGe "M:"FGf "M8"IGq "M8"IHc "M:"FG r "M8"IHr "M:"FG| "M8"IHU "M:"FInpc "M<"GGng "M<"GGnt "M<"GGnfy "M>"GGn|k "M<"GGnpc "M>"GGnrc "M>"GGnjc "M>"GGng "M>"GGnqu "M>"GGnqv "M>"GGne "M>"GGnh "M<"GGnq "M<"GGne "M>"GG.nqc "M<"GGnqt "M>"GGn{ "M<"GGnUj "M>"GGp "M8"IHp "M8"IHg "M8"IH "M9"G5Ft "M8"IHe "M:"G6G| "M8"IHr "M:"G6Gq "M:"G6Gr "M:"G6Gj "M:"G6Gg "M:"G6Gq "M:"G6Gq "M:"G6Ge "M:"G6G| "M8"IH| "M8"IHf "M8"IGw "M8"G4Gm "M8"IHq "M8"IHc "M:"G6G r "M8"IHr "M:"G6G| "M8"IHU "M:"G6Iksc "M;G7njc "M<"G8Gnpc "M<"G8Gng "M<"G8Gn} "M="G9Gnt "M<"G8Gnfy "M>"G:Gn|k "M<"G8Gnqw "M>"G:Gnpc "M>"G:Gnrc "M>"G:Gnjc "M>"G:Gng "M>"G:Gnqu "M>"G:Gnqv "M>"G:Gne "M>"G:Gn|c "M<"G8Gn|c "M<"G8Gnh "M<"G8Gnw "M<"G8G*nq "M<"G8Gntk "M<"G8Gnq "M<"G8Gne "M>"G:G.nqc "M<"G8Gnqt "M>"G:Gn{ "M<"G8GnUj "M>"G:GrB "M@"MAG@h "M@"MAGAs "M@"MAGEem "MC"IGH|M "M@"MAGIqB "MC"IGKjA "MC"IGMh "MC"IGNqX "MC"IGOro "MC"IGPew "MC"IGQh "M@"MAGTq "M@"MAGXc "MC"IGYqB "M@"MAGZqW "MC"IG[{y "M@"MAG\Uc "MC"IG]npc "M<"IGng "M<"IGnt "M<"IGnfy "M>"MEGn|k "M<"IGnpc "M>"MEGnrc "M>"MEGnjc "M>"MEGng "M>"MEGnqu "M>"MEGnqv "M>"MEGne "M>"MEGnh "M<"IGnq "M<"IGnq "M<"IGne "M>"MEG.nqc "M<"IGnqt "M>"MEGn{ "M<"IGnUj "M>"MEGqA "M@"MFG?rB "M@"MFG@h "M@"MFGAs "M@"MFGEem "MC"IGH|M "M@"MFGIqV "MC"IGJqB "MC"IGKqB "MC"IGLjB "MC"IGMh "MC"IGNqX "MC"IGOro "MC"IGPew "MC"IGQ|A "M@"MFGR|A "M@"MFGSh "M@"MFGTx "M@"MFGUqI "M@"MFGWq "M@"MFGXc "MC"IGYqA "M@"MFGZqW "MC"IG[{y "M@"MFG\Uc "MC"IG]njc "M<"IGnpc "M<"IGng "M<"IGnt "M<"IGnfy "M>"MHGn|k "M<"IGnqw "M>"MHGnpc "M>"MHGnrc "M>"MHGnjc "M>"MHGng "M>"MHGnqu "M>"MHGnqv "M>"MHGne "M>"MHGn|c "M<"IGn|c "M<"IGnh "M<"IGnw "M<"JG*nq "M<"IGntk "M<"JGnq "M<"IGne "M>"MHG.nqc "M<"IGnqt "M>"MHGn{ "M<"IGnUj "M>"MHG;j"MJ"JJ;g"MJ"JJ;t"MJ"JJ ;e"ML"JJ;{"MJ"JJ;p"ML"JJ;r"ML"JJ;j"ML"JJ;g"ML"JJ;q"ML"JJ;r"ML"JJ;e"ML"JJ;f"MJ"JJ;p"MJ"JJ;t"MJ"JJ;f"ML"IJ ;k"MJ"JJ!;{"ML"JJ";S"ML"IJ$Jp "MN"J'HJp "MN"J'HJg "MN"J'HJt "MN"J'HHl "MP"J,J-J| "MN"J'HHq "MP"J,J.Hp "MP"J,J/Hr "MP"J,J0Hk "MP"J,J1Hi "MP"J,J2Hp "MP"J,J3Hr "MP"J,J4Hl "MP"J,J5J| "MN"J'HJ| "MN"J'HJg "MN"J'J6Hx "MN"J7J8Jm "MN"J'HHr "MN"J7HHq "MN"IHHf "MP"IJ9Jr "MN"J'HHq "MP"J,J:J| "MN"J'HHU "MP"IIJjc"MR"IGJpc"MR"IGJg"MR"IGJ}"MS"J?GJt"MR"IGJfy "MT"JAGJ|k"MR"IGJqw "MT"JAGJpc "MT"JAGJrc "MT"JAGJjc "MT"JAGJg "MT"JAGJqu "MT"JAGJqv "MT"JAGJe "MT"JAGJ|c"MR"IGJ|c"MR"IGJh"MR"IGJw"MR"IG*Jq"MR"IGJtk"MR"IGJq"MR"IGJf "MT"JAJBJqc"MR"IGJqt "MT"JAGJ{"MR"IGJUj "MT"JAGQjA "MN"JCI&QqB "MN"JCI'Qg "MN"JCI(Qt "MN"JCI+N_ "MP"JEJFQ|M "MN"JCI/NqU "MP"JEJGNpA "MP"JEJHNrA "MP"JEJINkA "MP"JEJJNf "MP"JEJKNrS "MP"JEJLNqo "MP"JEJMNls "MP"JEJNQ|B "MN"JCI8Q|A "MN"JCI9Qf "MN"JCJOQp "MN"JCI;Nq "MN"II=Nf "MP"IGQrA "MN"JCI>NsW "MP"JEJQQ{y "MN"JCI@NSl "MP"IJR;j"MJ"JSJ;j"MJ"JSJ;g"MJ"JSJ;"MK"JTJ ;t"MJ"JSJ ;e"ML"JUJ;{"MJ"JSJ;{"ML"JUJ;p"ML"JUJ;r"ML"JUJ;j"ML"JUJ;g"ML"JUJ;q"ML"JUJ;r"ML"JUJ;e"ML"JUJ;{"MJ"JSJ;{"MJ"JSJ;f"MJ"JSJ;m"MJ"JSJ;p"MJ"JSJ;n"MJ"JSJ;t"MJ"JSJ;f"ML"IJ ;k"MJ"JSJ!;{"ML"JUJ";|"MJ"JSJ#;S"ML"IJ$;j"MJ"JXJZ;f"MJ"JXJ[;t"MJ"JXJ_;k"MW"JaI ;|"MJ"JXJb;f"MW"JaI;r"MW"JaI;k"MW"JaI;f"MW"JaI;f"MW"JaI;q"MW"JaI;k"MW"JaI;c"MJ"JXI;p"MJ"JXJf;t"MJ"JXJh;f"MW"JiJj;k"MJ"JXJk;z"MW"JaI ;|"MJ"JXJl;S"MW"JiJ$MlA "MY"JpJqMrB "MY"JpJrMf "MY"JpJsMY "MZ"JuGMu "MY"JpJvMld "M["JxI.M|M "MY"JpJyMqU "M["JxI0MqA "M["JxI1MqA "M["JxI2MkA "M["JxI3Mi "M["JxI4MrS "M["JxI5Mro "M["JxI6Mlk "M["JxI7M{B "MY"JpJzMtB "MY"JpJ{Mf "MY"JpI:Mw "MY"JpGMuJ "MY"JpJ}Mr "MY"JpJ~Mf "M["JxGMrA "MY"JpJMsW "M["JxI?M|y "MY"JpJTSl "M["LJRCjc "M]"JJCpc "M]"JJCg "M]"JJCn "M^"JJCu "M]"JJCl{ "M_"JJC|k "M]"JJCrx "M_"JJCpc "M_"JJCqc "M_"JJCib "M_"JJCi "M_"JJCpu "M_"JJCro "M_"JJClq "M_"JJC{b "M]"JJC|b "M]"JJCf "M]"JJCm} "M]"JJCp "M]"JJCuk "M]"JJCq "M]"JJCf "M_"JJBCpc "M]"JJCst "M_"JJC| "M]"JJCSc "M_"JJMrB "MY"JJrMf "MY"JJsMu "MY"JJvPld "M["JI.M|M "MY"JJyPqA "M["JI1PqA "M["JI2PkA "M["JI3Pi "M["JI4PrS "M["JI5Pro "M["JI6Plk "M["JI7Mf "MY"JI:Mw "MY"JGMp "MY"JJ|Mr "MY"JJ~Pf "M["JGMrA "MY"JJPsW "M["JI?M|y "MY"JJPSl "M["LJR;j"MJ"JJY;j"MJ"JJZ;f"MJ"JJ[;t"MJ"JJ_;k"MW"JI ;|"MJ"JJb;f"MW"JI;r"MW"JI;k"MW"JI;f"MW"JI;f"MW"JI;q"MW"JI;k"MW"JI;|"MJ"JJd;c"MJ"JI;n"MJ"JJe;p"MJ"JJf;t"MJ"JJh;f"MW"JJj;k"MJ"JJk;z"MW"JI ;|"MJ"JJl;S"MW"JJ$;j"MJ"JJ;g"MJ"JJ;t"MJ"JJ ;e"ML"JJ;p"ML"JJ;j"ML"JJ;g"ML"JJ;q"ML"JJ;r"ML"JJ;e"ML"JJ;f"MJ"JJ;p"MJ"JJ;n"MJ"JJ;t"MJ"JJ;f"ML"IJ ;k"MJ"JJ!;{"ML"JJ";|"MJ"JJ#;S"ML"IJ$IjA "MN"JI&IqB "MN"JI'Ig "MN"JI(It "MN"JI+N_ "MP"JJFI|M "MN"JI/NpA "MP"JJHNrA "MP"JJINkA "MP"JJJNf "MP"JJKNrS "MP"JJLNqo "MP"JJMNls "MP"JJNIf "MN"JJOIw "MN"JGIp "MN"JI;IuJ "MN"JI<Iq "MN"II=Nf "MP"IGIrA "MN"JI>NsW "MP"JJQI{y "MN"JI@NSl "MP"IJRrB "M@"JG@h "M@"JGAs "M@"JGEem "MC"HNGH|M "M@"JGIqB "MC"HNGKqB "MC"HNGLjA "MC"HNGMh "MC"HNGNqX "MC"HNGOro "MC"HNGPew "MC"HNGQh "M@"JGTq "M@"JGVq "M@"JGXc "MC"HNHPqA "M@"JGZqW "MC"HNG[{y "M@"JG\Uc "MC"HNG]Ih "Ma"FFIs "Ma"FFIe "Mc"FFI| "Ma"FFIq "Mc"FFIj "Mc"FFIh "Mc"FGIq "Mc"FGIr "Mc"FGIe "Mc"FGIh "Ma"FGIq "Ma"FG Ic "Mc"FG Iq "Ma"FG I| "Ma"FGIU "Mc"FGAqc "Me"GGAh "Me"GGAs "Me"GGAey "Mg"GGA|k "Me"GGAqc "Mg"GG Arc "Mg"GG!Ajc "Mg"GG"Ah "Mg"GG#Aqw "Mg"GG$Arv "Mg"GG%Aeq "Mg"GG&Ah "Me"GG)Aq "Me"GG-Ae "Mg"GG.Aqc "Me"GG/Aqt "Mg"GG0A| "Me"GG1AUc "Mg"GG2Ir "Ma"G4FIh "Ma"G4FS "Mb"G5FIs "Ma"G4FIe "Mc"G6FI| "Ma"G4FIq "Mc"G6FIq "Mc"G6FIq "Mc"G6FIj "Mc"G6FIh "Mc"G6GIq "Mc"G6GIr "Mc"G6GIe "Mc"G6GI| "Ma"G4GI| "Ma"G4GIh "Ma"G4GIw "Ma"G4GIp "Ma"G4GIq "Ma"G4G Iq "Ma"G4G Ic "Mc"G6G Iq "Ma"G4G Iq "Mc"G6G I| "Ma"G4GIU "Mc"G6GMsW "M"I-I?M{y "M"II@MRV "M"I-G4sb "MJClc "M"JHCqc "M"JHCg "M"JHCn "M"JHCt "M"JHCj "M"JHC{k "M"JH Crx "M"JH!Crc "M"JH"Crc "M"JH#Ckb "M"JH$Ci "M"JH%Cqu "M"JH&Cro "M"JH'Cju "M"JH(C|b "M"JH)C|b "M"JH*Cf "M"JH+Cx "M"JH,Cp "M"JH-Cuk "M"JH.Cq "M"JH/Cf "M"JH0Crc "M"JH1Cqt "M"JH2C| "M"JH3CRr "M"JH4MjA "M"II&MqB "M"II'Mg "M"II(Mt "M"II+Mld "M"ILI.M|M "M"II/MqA "M"ILI1MqA "M"ILI2MkA "M"ILI3Mi "M"ILI4MrS "M"ILI5Mro "M"ILI6Mlk "M"ILI7M|A "M"II9Mf "M"II:Mp "M"II;MuJ "M"II<Mq "M"II=Mf "M"ILGMrA "M"II>MsW "M"ILI?M{y "M"II@MRV "M"ILG;k"M{"INI;k"M{"INI;`"M{"INI;t"M{"INI ;k "M}"IPI ;|"M{"INI ;f "M}"IPI;r "M}"IPI;k "M}"IPI;f "M}"IPI;f "M}"IPI;q "M}"IPI;k "M}"IPI;z"M{"INI;c"M{"INI;p"M{"INI;t"M{"IRI;g "M}"ISH;j"M{"INI;z "M}"IPI ;|"M{"INI!;V "M}"ISG;j"M"IVG;r"M"IVG;`"M"IVG;t"M"IVG;e "Mq"JH;|"M"IVG;q "Mq"JH;r "Mq"JH;i "Mq"JH;h "Mq"JH;q "Mq"JH;r "Mq"JH;_ "Mq"JH;f"M"IVH;m"M"IVH;m"M"IVH;t"M"IVH;f "Mq"IG;l"M"IVH;{ "Mq"JH;|"M"IVH ;V "Mq"IGIs@ "MrINlA "Ms"IYGNpA "Ms"IYGNg "Ms"IYGNY "Mt"IZGNt "Ms"IYGNfV "Mu"IGN|M "Ms"IYGNtU "Mu"IGNpA "Mu"IGNrA "Mu"IGNjA "Mu"IGNg "Mu"IGNqS "Mu"IGNr_ "Mu"IGNel "Mu"IGN{B "Ms"IYGN{B "Ms"IYGNh "Ms"IYGNw "Ms"I\GNp "Ms"IYGNuK "Ms"I\GNq "Ms"IYGNf "Mu"IGNrA "Ms"IYGNrW "Mu"IGN|y "Ms"IYGNRV "Mu"IGSrB "Mi"HKG@Sh "Mi"HKGASs "Mi"HKGESem "Mk"HNGHS|M "Mi"HKGISqB "Mk"HNGKSqB "Mk"HNGLSjA "Mk"HNGMSh "Mk"HNGNSqX "Mk"HNGOSro "Mk"HNGPSew "Mk"HNGQSh "Mi"HKGTSq "Mi"HKGVSq "Mi"HKGXSc "Mk"HNHPSqA "Mi"HKGZSqW "Mk"HNG[S{y "Mi"HKG\SUc "Mk"HNG]Np "M"MMNg "M"MMNt "M"MMN` "M"IMN| "M"MMNq "M"IMNq "M"IMNj "M"IMNi "M"IMNq "M"IMNm "M"IMNc "M"MMNm "M"MMNq "M"MMNf "M"FJ9Nr "M"MMNq "M"IMN| "M"MMNU "M"FIYqc "M"MMYg "M"MMYt "M"MMYfy "M"GGY{k "M"MMYpc "M"GGYrc "M"GGYjc "M"GGYg "M"GGYqu "M"GGYqv "M"GGYe "M"GGYh "M"MGYq "M"MMYe "M"GG.Yrc "M"MMYqt "M"GGY| "M"MMYUj "M"GGNq "M"MMNp "M"MMNg "M"MMN "M"G5HNt "M"MMN` "M"IMN| "M"MMNq "M"IMNq "M"IMNq "M"IMNj "M"IMNi "M"IMNp "M"IMNq "M"IMNm "M"IMN| "M"MMN{ "M"MMNc "M"MMNw "M"IHNm "M"MMNq "M"MMNf "M"G6J9Nr "M"MMNq "M"IMN| "M"MMNU "M"G6IIsc "MG7Yrc "M"MMYqc "M"MMYg "M"MMYn "M"G9HYt "M"MMYfy "M"G:GY{k "M"MMYqw "M"G:GYpc "M"G:GYrc "M"G:GYjc "M"G:GYg "M"G:GYqu "M"G:GYqv "M"G:GYe "M"G:GY|c "M"MMY|c "M"MMYh "M"MGYx "M"MH,Ym "M"MMYre "M"MMYq "M"MMYe "M"G:G.Yrc "M"MMYqt "M"G:GY| "M"MMYUj "M"G:GfrB "M"MLLfg "M"MLMft "M"MLQglj "M"MLTf|M "M"MLUgpB "M"MLWgjB "M"MLYgi "M"MLZgrS "M"ML[gro "M"ML\gl "M"ML]ff "M"ML`fp "M"MLbfq "M"MLdgf "M"MLefrB "M"MLfgsW "M"MLgf|y "M"MLhgSi "M"MLiYqc "M"MMYg "M"MMYt "M"MMYfy "M"MGY{k "M"MMYpc "M"MGYrc "M"MGYjb "M"MGYg "M"MGYqu "M"MGYqv "M"MGYe "M"MGYh "M"MGYm "M"MMYq "M"MMYe "M"MG.Yrc "M"MMYqt "M"MGY| "M"MMYUj "M"MGfrB "M"MLLfg "M"MLMft "M"MLQglj "M"MLTf|M "M"MLUgpB "M"MLWgqB "M"MLXgjB "M"MLYgi "M"MLZgrS "M"ML[gro "M"ML\gl "M"ML]ff "M"ML`fq "M"MLdgf "M"MLefrB "M"MLfgsW "M"MLgf|y "M"MLhgSi "M"MLiYrb "M"MMYqc "M"MMYg "M"MMYt "M"MMYfy "M"MGY{k "M"MMYpc "M"MGYrc "M"MGYjb "M"MGYg "M"MGYqu "M"MGYqv "M"MGYe "M"MGY|b "M"MMYh "M"MGYx "M"MH,Ym "M"MMYre "M"MMYq "M"MMYe "M"MG.Yrc "M"MMYqt "M"MGY| "M"MMYUj "M"MG;j "M"L{JZ;f "M"L{J[;t "M"L{J_;k"M"MI ;| "M"L{Jb;f"M"MI;r"M"MI;k"M"MI;f"M"MI;q"M"MI;k"M"MI;c "M"L{I;p "M"L{Jf;t "M"L{Jh;f"M"ML;k "M"L{Jk;| "M"L{Jl;O"M"MMHq "M"MLHg "M"MLHu "M"MLH` "M"MMH| "M"MLHp "M"MMHr "M"MMHk "M"MMHf "M"MMHp "M"MMHq "M"MMHl "M"MMH| "M"MLH| "M"MLHc "M"MMHp "M"MLHq "M"MLHf "M"MK]Hp "M"MLHr "M"MMH| "M"MLHO "M"MLJpc "M"LJJg "M"LJJu "M"LJJl "M"MLJ|k "M"LJJkx "M"MLJpc "M"MLJsc "M"MLJic "M"MLJf "M"MLJpu "M"MLJso "M"MLJl "M"MLJ{c "M"LJJ|c "M"LJJc "M"LLJp "M"LJJq "M"LJJf "M"MJBJpc "M"LJJqt "M"MLJ| "M"LJJOc "M"MLNqA "M"MMNf "M"MMNu "M"MMNld"M"MI.N|M "M"MMNqA"M"MI1NqA"M"MI2NkA"M"MI3Ni"M"MI4NrS"M"MI5Nro"M"MI6Nlk"M"MI7NrB "M"MMNf "M"MI:Np "M"MMNr "M"MMNf"M"MK]NrA "M"MMNsW"M"MI?N|y "M"MMNSl"M"MJR;j "M"LJY;j "M"LJZ;f "M"LJ[;t "M"LJ_;k"M"MI ;| "M"LJb;f"M"MI;r"M"MI;k"M"MI;f"M"MI;f"M"MI;q"M"MI;k"M"MI;{ "M"LJc;c "M"LI;p "M"LJf;j "M"LJg;t "M"LJh;f"M"ML;k "M"LJk;z"M"MI ;| "M"LJl;O"M"MM;j "M"NJZ;f "M"NJ[;t "M"NJ_;k"N"NI ;| "M"NJb;f"N"NI;r"N"NI;k"N"NI;f"N"NI;q"N"NI;k"N"NI;| "M"NJd;c "M"NI;p "M"NJf;t "M"NJh;f"N"NN ;k "M"NJk;z"N"NI ;| "M"NJl;O"N"NMUkA "N "N NUrB "N "N NUf "N "N NUt "N "N NTld "N"NI.U|M "N "N NTqA "N"NI1Tq@ "N"NI2Tk@ "N"NI3Ti "N"NI4TrS "N"NI5Tro "N"NI6Tlk "N"NI7UrA "N "N NUf "N "N I:Um "N "NLUp "N "N NUr "N "N NTf "N"NJBUrA "N "N NTsW "N"NI?U|y "N "N NTSl "N"NJRCrb "N!"LLCf "N!"LLCt "N!"LLCl "N$"N%LC{k "N!"LLCpc "N$"N%LCsc "N$"N%LCib "N$"N%LCf "N$"N%LCpu "N$"N%LCso "N$"N%LCl "N$"N%LC{b "N!"LLC{b "N!"LLCc "N!"LLCm} "N!"LJCp "N!"LLCr "N!"LLCf "N$"N%JBCrc "N!"LLCqt "N$"N%LC| "N!"LLCOc "N$"N%LPrB "N "N'NPf "N "N'NPt "N "N'NPld "N"N)I.P|M "N "N'NPqA "N"N)I1PqA "N"N)I2PkA "N"N)I3Pi "N"N)I4PrS "N"N)I5Pro "N"N)I6Plk "N"N)I7Pf "N "N'I:Pp "N "N'NPr "N "N'NPf "N"N)JBPrA "N "N'NPsW "N"N)I?P|y "N "N'NPSl "N"N)JR;j "M"N,JZ;f "M"N,J[;t "M"N,J_;k"N"N.I ;| "M"N,Jb;f"N"N.I;r"N"N.I;k"N"N.I;f"N"N.I;f"N"N.I;q"N"N.I;k"N"N.I;| "M"N,Jd;c "M"N,I;p "M"N,Jf;t "M"N,Jh;f"N"N/N ;k "M"N,Jk;z"N"N.I ;| "M"N,Jl;O"N"N/M;j "M"LJZ;f "M"LJ[;t "M"LJ_;r"M"N2I;k"M"N2I;f"M"N2I;f"M"N2I;q"M"N2I;k"M"N2I;c "M"LI;p "M"LJf;t "M"LJh;f"M"N2L;k "M"LJk;| "M"LJl;O"M"N2MPkA "M"N4MPqA "M"N4MPf "M"N4MPu "M"N4MPld "M"N6I.P|M "M"N4MPqA "M"N6I1PqA "M"N6I2PkA "M"N6I3Pi "M"N6I4PrS "M"N6I5Pro "M"N6I6Plk "M"N6I7Pf "M"N4I:Pm "M"N4LPp "M"N4MPr "M"N4MPf "M"N6K]PrA "M"N4MPsW "M"N6I?P|y "M"N4MPSl "M"N6JRfrB "M"N7LLfg "M"N7LMft "M"N7LQglj "M"JLTf|M "M"N7LUgpB "M"JLWgqA "M"JLXgjA "M"JLYgi "M"JLZgrS "M"JL[gro "M"JL\gl "M"JL]ff "M"N7L`fp "M"N7Lbfq "M"N7Ldgf "M"HNLefrA "M"N7LfgsW "M"JLgf|y "M"N7LhgSi "M"HNLi=K+07+  ++ !и #и'и)01#3#3267667#"&'&&'#53&47#536676632&&#"s@(']41]!-91182!"`4:R'E)'@99(A  U)$"_8969:b$()D ?1~ *2S++$/+ +A&6FVfv ]A]A]A)9IYiy ] 9( 9+A//]A/)/9/I/Y/i/y/////// ] 4+ + ++-+(-901"&54632"32654&#'&###323254&#짤뤓ѓ/V7+0/HX`C=**Lk;=K쥦릥ѓΓ|\J@5J  U:X.)W0 +01''&76676676&#!"5543!2925]  V/-6  H.zVKJ1 # ,pCAN # #s / + и / /+0132####"54332 * *!!}c$ +01''&676676#!"5543!2ĺ K0!N Bi # # / +и //+01####"5433232 * *  q# + /+017463!2##"554&#!"&5 .( & ")r+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5 /% /$ #($#(h$:;//A]A)9IYiy ]  и / и/ 9;2и2/-/)6+01&766'4632'&&'&''46332##"554&##"&5 RO.  ',D +@ G0  /$  z 9f  /(;37% 7+M$G"(c$ +01"''&676676676#!"5543!2̰ Jz/.<  K0!J" R/1k9 # # / + и / /+0132####"54332 * *"qK+ /+01#!"&5543323!2K . )  o#(s,SQD=+J9+ +93и3/96и6/JGиG/JMиM/01"'&&'&''''&676676&##"554332#"##&&5543323232636676k *!H$Ro <#% {    #L/) ( '  q #"9  1v> !-2*&# "* c% +01''&67$76#!"5543!2 701bC$ J0"Bp22X* !  $ "! / + и / /+0132####"54332 * *,#ed<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+3$+aY+H?+N+$и/$!и!/36и6/01#"&'&&54676632###"&554332336276676%#!"&55463!24&#"326##"&554332H:#%::%#:  %U.( O# '  f k ]0,-22-,0  "..,+a#*  &)())# % |4!++ +01#!"&55463!2#!"3!24 + +7  v**   q42]3//3и/+и/и/#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!24  /#, ,  #,p #+J*8$L)= q*E[7+/+NS+#и#/N] / /P/=Q+JW+2+Q и /Wи/J'и=:и:/=@и@/01###&&'&&55463326554&##"&5546332##"33267667646332##"54&##"&5   %Q !,   !, )" 2 /$  p N*9$ I): 0#*q)XhI%+A-+\+ d+%5и ja+:1+*D+:и/и/иaLиaPиP/aRиR/1YиY/01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"332672?676"3326554&#, +5 $O",  ",E   x t ) * )   K*; # I)>     j`jûR.+J6+b++bи.>и>/iиl //B//'/C:+3M+a+C и /Cи/'и/'$и$/'eUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"332676676727332655M'',! "K!+~ !,D#  \ n  0ur'%  J* 9#G*<  w whBl{5+-+-и/!и!/-OQ/9 +&+0+ и/9<и  :*)jD  0+$!& 0  :' ]q?Bf3+++`I+и/`W//E/NS+X_+E и /Xи/Sи/N#и_.иEd6и6/d:и:/d<и  /**  Q  S r^bfͻU1+M9+c+d+1AиA/h/E/ /'/*/6P+=и/ и=и/ $и$/ +и+/FиF/XиX/cи=eиe/01%##32#!"&5546335#"&55463!2###"&'&&55463326554&##"&5546332##"3327667672735#W07 96 8 O!, "+ h  VV !   D)3$A)6  eZ C[gu+5+-+bP+D\+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]A\\]A\)\9\I\Y\i\y\\\\\\\ ]DeJ+9 +x+&+sj+V_+ и/Vи/_0и0/01"##"&'&&55463326554&##"&5546332##"33267667672%#"&'&&546766324&#"3267#!"&55463!2'##"&554332   #P!,  !,G!  [:$$::$$:A1,.00.,1S W ?  r  U)C # Q*E  0--+*(()) (< % |%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5A  #!+  ,^    ** | &_'//'и/ и /ии#(// ++01%3!2655"&'&&546332!546332#)  V++ ,7hh ec%h@JB++("+BA""]A")"9"I"Y"i"y""""""" ]9"(9Iи(L /%/E+A+01"&'&&5463323546332#7&766'4632'&&'&''%332655&' ,[ RN-  )+C-A F0   fc & 9f  .(<37% 8 +M$jjwu*(/01%'&&'&'&'''&67667667632-3c >q,-! +/}MBp*)0 0  Ka "7#$)/*.G % =&$U* up@q +A&6FVfv ]A]и/= 9/#/01''&6766766'432766'4632'&'&''&' 5J 1 3" ?B/ %-U  d3 .b o+ S# 6!K.  ! 2)zT  " !-2*&# "* _ +01''&676676676##"5543!2?j'(4 /" C''X. # "`!c I// и /ии//+01##"55###"54332354332c&&&&/ey<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+3$+aY+H?+N+$и/$!и!/36и6/01#"&'&&54676632###"&554332336276676%#!"&55463!24&#"326##"&554332]:$$::$$:  %U-( O# '  g k ]0--11--0  "..,+a#*  &)())# % |k!++ +01#!"&55463!2#!"3!2k ,,   v()   qx2q3//#и/3и/+и/и/#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2x 0$,  #-P $)F+ 3$F(8qcE[7+/+NS+#и#/N] / /P/=Q+JW+2+Q и /Wи/J'и=:и:/=@и@/01###&&'&&55463326554&##"&5546332##"33267667646332##"54&##"&5?   %R!,  !,  )" 2 0$  p N*9$ I): 0#*rcVfH$+@,+Z+ b+$4и h_+90+)C+9ии/и_Kи_QиQ/0WиW/01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"33276676676"3326554&#B +,6 s",   !-  h  w t )))  Q) @#N)C    jb`jûR.+J6+b++bи.>и>/iиl //B//'/C:+3M+a+C и /Cи/'и/'$и$/'eUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"332676676727332655  '( ,! #J +    ,C#  ] n  0ur'%  J* 9#G*<  w whBl5+-+-и/!и!/,Bи-OQ/9 +&+0+ и/9<и  :*)jD  1*$!& 0  :'!]qwBf3+++`I+и/`W//E/NS+X_+E и /Xи/Sи/N#и_.иEd6и6/d:и:/01##"&'&&55463326554&##"&5546332##"332636676676##"&5546332##"32##332.6 #N !,   ,G"  M , ,  p   L*; # I)>  /**  Q  S rvbfٻU1+M9+c+d+1AиA/h/E/ /'/*/6P+=и/ и=и/ $и$/ +и+/FиF/XиX/[и[/cи=eиe/01%##32#!"&5546335#"&55463!2###"&'&&55463326554&##"&5546332##"3327767672735#o.5 95 6 P  -  !,i  UU !   D)3$A)6  eq C[gu+5+-+bP+D\+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]A\\]A\)\9\I\Y\i\y\\\\\\\ ]DeJ+9 +x+&+sj+V_+ и/Vи/_0и0/01"##"&'&&55463326554&##"&5546332##"33267667672%#"&'&&546766324&#"3267#!"&55463!2'##"&554332 ! #Q"+   -H"  Z:$$::$$:A1,-11-,1S V ?  r  U)C # Q*E  0--+*(()) (< % |b%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5d !+ !+>    )( {c &_'//'и/ и /ии#(// ++01%3!2655"&'&&546332!546332#T  <++ ,0a a ed&h@JB++("+BA""]A")"9"I"Y"i"y""""""" ]9"(9Iи(L /%/E+A+01"&'&&5463323546332#7&766'4632'&&'&''%332655% &&-[ RN-  '+D,@ F0   fc & 9f  /(;37% 8 +M$jjw*(/01%'&&'&'&'''&67667667632A2c >q,-! +/}M Bp*)0 0  Ka "7#$)/*.G % =&$U* qFG/ /G и /A&6FVfv ]A]и/и/A ]A ) 9 I Y i y ] ($и$/(&и&/7 (9C 9(H /#/01''&6766'43276676654632'&&'&''&&'&' a[ 0  0./ +-X 2M  E)(C  4$ 0e  #"!59&"R)  L=B) G(+H B(Qqh/˸0/ /0и/A&6FVfv ]A]A ]A ) 9 I Y i y ] $1*++01%32676654&'&&#"4676632#"&'&&G# V2-V" )) "V-2V #I-**pD>o++22++o>Dp**-#--//)DB+.CCq/ +01'&&'&'&'''&76676&#!"5543!2)[.10Y+01##"55###"5433235433235#c&&&&)uhtem00 +01''&76676676&#!"5543!2/925]  U/-7 P/zVKJ1 # ,pCAN # #FsA '+ // +01##"5#"554334332A* * $ B|> +01''&676676#!"5543!29û J0!N Bi # ##A '+ // +01##"55#"5543354332A))  #qA# + /+017463!2##"554&#!"&5 /) & ")rB+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5- 0% /$ #($#(h$:;//A]A)9IYiy ] и/ 9;2и2/- </)6+01&766'4632'&&'&''46332##"554&##"&5 RO. '+D-A G/  / % z 9f  /(;37% 8  +M$G"(|> +01''&676676676#!"5543!29ʱH|/.< J0!I ! Q/1k8# "#A '+ // +01%##"5#"554334332A) ) % `+ /+01#!"&5543323!2`  0* o$'s,SQD=+J9+ +93и3/96и6/JGиG/JMиM/01"'&&'&''''&676676&##"554332#"##&&5543323232636676y *!H$Ro <#% {    #L/) ( '  q #"9  1v> !-2*&# "* |? +01''&676676#!"5543!28 700e J0"@p22Y+ ! Ea $ "#+A '+ // +01##"55#"5543354332A) )9 # em<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+<$+aY+H?+N+$и/$!и!/<2и2/<6и6/01#"&'&&54676632###"&554332336276676%#!"&55463!24&#"326##"&554332R:#$::$#:  %U/)O" '  f l ]0,-11-,0  "..,+a#*  &)())# % |M!++ +01#!"&55463!2#!"3!2M -- 6  v**   qV2]3//3и/+и/и/#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2V  .#, -  #,o #+J*8$L)= qBE[7+/+NS+#и#/N] / /P/=Q+JW+2+Q и /Wи/J'и=:и:/=@и@/01###&&'&&55463326554&##"&5546332##"33267667646332##"54&##"&5    $Q!,    , )" 1 0$ p N*9$ I): 0#*qBYiK'+C/+]+ e+'7и7/ kb+<3+,F+<и/и/ иbNиbRиR/3ZиZ/01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"33267667672"3326554&##+ , $O .   ",C" w s ) * )  K*; # I)>     j@`jûR.+J6+b++bи.>и>/iиl //B//'/C:+3M+a+C и /Cи/'и/'$и$/'eUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"332676676727332655m'&,! "L  ,}  -~C#  ] n  0ur'%  J* 9#G*<  w whBl{5+-+-и/!и!/-OQ/9 +&+0+ и/9<и  :*)jD  0+$!& 0  :'!]qXBf3+++`I+и/`W//E/NS+X_+E и /Xи/Sи/N#и_.иEd6и6/d:и:/01##"&'&&55463326554&##"&5546332##"332636676676##"&5546332##"32##332 6 $N",    -F!  L + +  p   L*; # I)>  /**  Q  S robfٻU1+M9+c+d+1AиA/h/E/ /'/*/6P+=и/ и=и/ $и$/ +и+/FиF/XиX/[и[/cи=eиe/01%##32#!"&5546335#"&55463!2###"&'&&55463326554&##"&5546332##"3327767672735#h.5 95 6 P  -  !,i  UU !   D)3$A)6  ew C[gu+5+-+bP+D\+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]A\\]A\)\9\I\Y\i\y\\\\\\\ ]DeJ+9 +x+&+sj+V_+ и/Vи/_0и0/01"##"&'&&55463326554&##"&5546332##"33267667672%#"&'&&546766324&#"3267#!"&55463!2'##"&554332   #P !, !,G!  [:$$::$$:A1,.00.,1S W ?  r  U)C # Q*E  0--+*(()) (< % |A%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5d  #!+  ,^    ** |A &_'//'и/ и /ии#(// ++01%3!2655"&'&&546332!546332#J  T++!,8i i fc%h@JB++("+BA""]A")"9"I"Y"i"y""""""" ]9"(9Iи(L /%/E+A+01"&'&&5463323546332#7&766'4632'&&'&''%332655&' ,[ RN-  )+C-A F0   fc & 9f  .(<37% 8 +M$jjx*(/01%'&&'&'&'''&67667667632?2c =q-,! ,.}MCo+(0 1 Ma "6#$)0*,H % =&$U* qFG/ /G и /A&6FVfv ]A]и/и/A ]A ) 9 I Y i y ] ($и$/(&и&/7 (9C 9(H /#/01''&6766'432766766'4632'&&'&''&&'&' aZ 0 1// +-W 3M  F((C 4$ 0e  !#"!59&"R)  L=B) G(+H B(QqI/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+01%4&'&&#"3267667#"&'&&54676632$P+/P P/+P$G-('i=@k%%++%%k@=i'(-#//--!-CC-(DDq- +01'&'&'''&76676&#!"5543!2L_<:CR Uv O1!4 &'&Kt  6 !c; $ 22!!    c.:/4++01'&&'&'&'''&76676&#!"5543!22##"5543%c222  Z P '*BX&*+Q"  3 !R/   +'  jqA5 + и /++017463!2##"55!"5543!54&#!"&5 /)  & #){ KqW#3+!+ ++01#!"&55463!2#!"!2#!3!2W + +7  #  ** Q ! M {y#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#Zj m ?b!    Uc/=KL//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/M+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'##"&55463328!99!8H& Q.,S%%Q-.Q&T K .  ..0/  Osc !E"//"и/и#//+01##"5#"&5546334332##"54332& &&&X  )}b E // и/ и!// +01##"55#"5543354332##"54332' '&& # ib E // и/ и!// +01%##"5#"5543354332##"54332' '&&# b E // и/ и!// +01##"55#"5543354332##"54332' '&&2 $ 97sA 9+ и//+ +01##"5#"554335#"5543354332A* *y " # @ 9+ и// ++01##"55#"554335#"5543354332@) ) d # x # xA 9+ и// ++01%##"55#"554335#"5543354332A) ) # # #A 9+ и// ++01##"55#"554335#"5543354332A) )1[ # r " sb 'W(/!/(и/ии!)$//++015#"5543354332##"5#"5543##"54332p %%%%/ # K " R}b 'W(/!/(и/ ии!)$// ++01##"55#"554335#"5543354332##"54332( (''  # a # ~b 'W +!+ и ии)/$/ ++01%##"55#"554335#"5543354332##"54332 ( ( '' # # rb 'W(/!/(и/ ии!)/$/ ++01##"55#"554335#"5543354332##"54332( (''6] # _ # ;*#} +  и /A]A)9IYiy ]и/ %+01463!2''&766766'&&#!"&5   )    )g68n3 &q:;k ;#+/ +012!2#!"&55463!463   c ""2 !#u +A]A)9IYiy ]и/ %/+01463!2#'&76676654&#!"&5   '     D" = @"!@ ;,# +/ + 01546332!2#!"&55463(  h""k# + /+017463!2##"554&#!"&5 /)  #(j +M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5 /$ .$  !)#!)hn#9://A]A)9IYiy ]  и / и/ 9:1и1/,/(5+01&766'4632'&&'&''46332##"554&##"&5 RO/  (,C+A G0  .$  z 9f  /(;37% 8 +M$G"(!#q +A]A)9IYiy ]и/ %+01463!2''&76676654&#!"&5   '     I#%A D%#F; # +/ + 01546332!2#!"&55463)  7!!?+ /+01#!"&5543323!2? /)   m')sm-TQE>+K:+ +:4и4/:7и7/KHиH/KNиN/01"'&&'&''''&676676&##"554332#"##&&5543323232636676Q )!H%Sn ;$% y    $C. )   &  q #"9  1v> !-2*& # "* " e +A]A)9IYiy ] "+01463!2''&76676654&#!"&5   '      q- BX{4+,+KP+ и /KZ/ /M/%+/+ 8%Fи NиN/T01##"&'&&55463326554&##"&5546332##"3326766767246332##"54&##"&5 ! x  ,   !,E%   .$ q  \*L#Z)M  R"*Ar6XhJ&+B.+\+ d+&6и ja+;2++E+;ии/иaMи2Y01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"33267667672"3326554&#+, z!-  !-G$  x z ** )  O)>#L)?     j(`jûR.+J6+b++bи.>и>/iиl //B//'/C:+3M+a+C и /Cи/'и/'$и$/'eUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"332676676727332655U'( ,  "J!+~   - C"   ] n  J&%  W*E # S*H   gBl{6+.+"и"/.BиB/.OQ/: +'+1+ и/:<и#L)?  `gC[iuC5+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DsJ+? +x+&+g^+Vm+ и/Vи/m0и0/?<иq +A&6FVfv ]A]и/: 9//01''&6766766543327654632'&'&'&'&'&'2G / D:|/  )+Qc0 -_Z* S" 6J-?n] J14#  6A IC5A &Bn)/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+01%4&'&&#"3267667#"&'&&54676632/$$T$%T$"//"$T%%T$#/I9+.o76n--99--n68n.+9%"--". ."1FF1/FFup+ +01'&&'&'&'&''&67$76&#!"5543!^ #f574 b G ӝg@V*..X q ?- Vp  " c4-  f}5A6;+#+01'&&'&'&'&''&76676676&#!"5543!22#!"5543l%a321"Ja  OCCr'K&0 +:,'(((L    2!*/  #  oj5 + и /++017463!2##"55!"5543!54&#!"&5 /)   #* U{(#3+!+ ++01#!"&55463!2#!"!2#!3!2( --     w))  I " E |?#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#+p| v ]N !  V@ /=K L//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!29889I( S**T"(("S**S! (dq1..!,/ L y P#u +  и /A]A)9IYiy ]и/+01463!2''&766766'&&#!"&5y z (   )U,+U% %V-+Q$@s ?@/ /и@;и;/A //:-++:и/:и/-*и*/-0и:701####"5433232%2276676676#"'"&55463273463 (( "P)&JCQO((LI)DIE}y,   #  " T i +A]A)9IYiy ]и/+01463!2''&766766'&&#!"&5 q (   2c< .4H 5o6/*/6и/ и /*%0и%7'/-/ +2#+ии/016##"&5546335433236766%####"5433232  @} ):66`9  ))  e" #bT] +A]A)9IYiy ]+01463!2''&766'&&#!"&5 q (  3y= 3r>Hi 6o7/ /и7)и)/ и /)08/ /) ++)2и2/)4и4/01####"54332326##"&55463354332632766  ))   @} ):66`m" # T] +A]A)9IYiy ]+01463!2''"766'&&#!"&5 q (   4g; 1`?H 5s6/*/6и/ и /*$0и$7&/-/ +2"+и/и/016##"&5546335433236766%####"&5433232 A} ):66^;  ))  {#  #V ed<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+3$+aY+H?+N+$и/$!и!/36и6/01#"&'&&54676632#"#"&554332336276676%#!"&55463!24&#"326##"&554332H:#%::%#:   %U.( O# '  f k ]0,-22-,0  ****'Z#)  % (''' $ r32i3//3и/+и/и/#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!23 .#.!  #,e #*=*-"=(1 q*BX6+.+KP+"и"/KZ / /M/'+1+ и/ :'Fи NиN/T01"###"&'&&55463326554&##"&5546332##"3267667646332##"54&##"&5   w !,   !,D% 2 /$  q B*-$?( 2 $'r)WgI%+A-+[+ c+%5и i/9/`+:1+*D+:и/и/и`Lи`RиR/1XиX/01%46332##"&'&&5###"&'&&55463326554&##"&5546332##"332766766767"3326554&#, +5 $O",  ",h  x t )) (  =),#:)/    j_iR.+J6+a++aи.>и>/hиk/ //B/d'+C:+3M+`+C и /Cи/'$и$/dUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"33276676727332655M'',! "K!+~ !,e  \ n  ji '& A*/#=+1  {mmgBlo6+.+"и"/.OQ/: +'+1+ и/:<и*3  =  6*'\D 0)$#,  5$Sq6Bf3+++`I+и/`W//E/cF+NS+Y^+F иYи/Sи/N#и^.и./c6и6/c:и:/01"##"&'&&55463326554&##"&5546332##"332672676776##"&5546332##"32##3326 $N",   ,F"  M , ,  q  C)1#?(2 .)(  H  I q^cgͻU1+M9+d+e+1AиA/i/E/ /'/*/6P+=и/ и=и/ +и+/FиF/XиX/\и\/dи=fиf/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33267667672735#W07 96 8 O!, "+ F  VV    @)0"=)1  eXC[iu55+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DsJ+9 +x+&+g^+V0+ и/9<и)1  )***' % (''' $ f:DG<++<и%ܸC'/?+;+01"&'&&55463323546332#7''&676'432'&'%332655''- , {00]   YS'  a %"|5 3`\c[[H 5o6/*/6и/ и /*%0и%7'/-/ +2#+ии/016##"&5546335433236766%####"5433232  @} ):66`9  ))  e" " br4#3+!+ ++01#!"&55463!2#!"!2#!3!24 + +7  #  ++  F @ s"u +  и /A]A)9IYiy ]и/+01463!2''"766766'4&#!"&5s (  (   %U+/V%%V+,T$@sb Am =+ ++и//=2++=!и!/2/и//2501##"5###"5433234332276676#"'"&554633463b'y%%y' :t.<<A! @bD|   " " a +A]A)9IYiy ]/+01463!2#'&&766'&&#!"&5 $  %    1k+ *f1Jc >;4+ ++и//;*++*$и$/*'и'/;1и1/;3и3/;=и=/01##"55###"543323543326#'"&554633354332676c&z%%z&=>97 8?7*6:<ǰ p # "jc# + /+017463!2##"554&#!"&5 0) #*pc+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5M 0$ /$  $)$$)g%;и>/01%##"5###"54332343326#"'"&5546332754332676c&z%%z&; >98 ;=1. (%'D2  R~ " -3,'A !) a +A]A)9IYiy ]/+01463!2#'&&766'&&#!"&5 $  %     0p* ,f2Jc >};4+ ++и//;*++*'и'/;1и1/;3и3/;=и=/01##"55###"543323543326#'"&554633354332676c&z%%z&=>97 8?7*6:< Ķ  #  "fy<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+<$+aY+H?+N+$и/$!и!/<2и2/<6и6/01#"&'&&54676632###"&554332336672676%#!"&55463!24&#"326##"&554332]:$$::$$:  %U-( O# '  g k ]0--11--0  )***(Y#)  " ('&& # |k!++ +01#!"&55463!2#!"3!2k ,,   v** "  qw2m3//#и/3и/+и/и#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2w 0$,  #/C #+A*)#9*6 pcE[7+/+NS+#и#/N] / /P/@Q+(+2+Q и /@:и:/@<ии>/hиk/ //B/d'+C:+3M+`+C и /Cи/'$и$/dUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"33276676727332655  '( ,! #J +    ,f  ] n  ji '& A*/#=+1  {mmgBl6+.+.и/"и"/-Bи.OQ/: +'+1+ и/:<и*3  =  6*'\D /(&#,  5$SquBf3+++`I+и/`W//E/cF+NS+Y^+F иYи/Sи/N#и^.и./c6и6/c;и;/01"##"&'&&55463326554&##"&5546332##"332677676776##"&5546332##"32##332-6 #O",   -G!   L , ,  q  C)1#?(2 .)(  H  I qvcgٻU1+M9+d+e+1AиA/i/E/ /'/*/6P+=и/ и=и/ +и+/FиF/XиX/\и\/_и_/dи=fиf/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33263667672735#o.5 95 6 P  -  !,G  UU    =*- # ;)0  eC[iu55+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DsJ+9 +x+&+g^+V0+ и/9<и)1  )***' % (''' $ |b%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5d !+ !+>    )( |c &_'//'и/ и /ии#(// ++01%3!2655"&'&&5546332!546332#T  <++ ,'Y Y \Y%h@JB++("+BA""]A")"9"I"Y"i"y""""""" ]9"(9Iи(L /%/E+A+01"&'&&5463323546332#7&766'4632'&&'&''%332655&' ,[ RN-  )+C-A F0   fc & 9f  .(<37% 8 +M$jjqFG/ /G и /A&6FVfv ]A]и/и/A ]A ) 9 I Y i y ] &(и(/&*и*/8 &9B 9&H/#/01"''&6766766'432766'4632'&'&''&'&' 3K /  3!=D/  C+  m1 ._ \.S# 7"S3  '# !!3'X  "%!4 3L P95J  (Btk/˸0/ /0и/A&6FVfv ]A]A ]A ) 9 I Y i y ] $1*++01%32676654&'&&#"4676632#"&'&&:&!!X2/X##**##X/2X!!&G/)*tEAs,+22+,sAEt*)/#++..(BC(-AAq% +01'&'&'''&76676&#!"5543!2UO@B} Nv Q/-R1:EAs 8& "W6 ! ./,*  qc5 + и /++01%463!2##"55!"5543!54&#!"&5 /)   #)t Dqt#3+!+ ++01#!"&55463!2#!"!2#!3!2t ,!!,    ** > " A {#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#dj n @c !   Uj/=KL//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/M+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2:;;:E(!S**T"(("S*+R"(\ _ C&   ('   (~ I NsA 5q6//61и1/7//0#+0и/0и/# и /#&и0-01##"543322276676676#"'"&55463273463A))%#O('JCQO()KI)F}   #  " MA *a+/$/+и/ и /$,'/!/ +и/и/016##"&5546335433236766##"54332A})965`))W!  #a lA# + /+017463!2##"554&#!"&5 /(  ")pA+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5, 0% 0$  $)$$)g%;Y@Q+(+Q и /2@:и:/@<ии>/hиk/ //B/d'+C:+3M+`+C и /Cи/'$и$/dUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"33276676727332655m'&,! "L  ,}  -~e  ] n  ji '& A*/#=+1  {mmgBlo6+.+"и"/.OQ/: +'+1+ и/:<и*3  =  6*'\D 0)$#,  5$SqSBf3+++`I+и/`W//E/cF+NS+Y^+F иYи/Sи/N#и^.и./c6и6/01"##"&'&&55463326554&##"&5546332##"332677676776##"&5546332##"32##332 6 #N !,   !, F"  M , ,  q  C)1#?(2 .)(  H  I r_bfٻU1+M9+c+d+1AиA/h/E/ /'/*/6P+=и/ и=и/ $и$/ +и+/FиF/XиX/[и[/cи=eиe/01%##32#!"&5546335#"&55463!2###"&'&&55463326554&##"&5546332##"3327767672735#X/6 :6 7 O .  "+h   VV !   D)3$A)6  elC[iu55+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DsJ+9 +x+&+g^+V0+ и/9<и)1  )***' % (''' $ {B%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5Q  !,.L n   ** {B &_'//'и/ и /ии#(// ++01%3!2655"&'&&5546332!546332#3 ; ,,,O O X\ &h@JB++("+BA""]A")"9"I"Y"i"y""""""" ]9"(9I /%/E+A+01"&'&&5463323546332#7&766'4632'&&'&''%332655 %',[ RN-  (,C-@ F1    fc & 9f  .(<37% 8 +M$jjMA *a+/$/+и/ и /$,'/!/ +и/и/016##"&5546335433236766##"54332A})965`))W!  #8w%#/01%'&'&'''&76676674328&o \C#*.|MHs((+ 0 9^# %@.5(%&B %;!"K$ p> //01''&6766'432766'4632'&'&''&'&'fX 0  !9=D/  ?' e4 1[V- 4 # +oI 7./&j@ ) &A F5 +=WuK/˸0/ /0и/A&6FVfv ]A]A ]A ) 9 I Y i y ] $1*++01%32676654&'&&#"4676632#"&'&&',$!T($T$%//%$T$(T!$,G3*,n>8n-,77,-n8>n,*3#  ## #&87()99q% +01'&'&'''&76676&#!"5543!2VO?D| Nv R/-Q29DCs 7& "W6 ! ./,*  f2>38+"+01'&&'&'&'&''&676676676&#!"5543!22#!"5543(b431 IEDw,J (/9R ()*W%  1"  !* #   %!  LrA5 + и /++017463!2##"55!"5543!54&#!"&5 /)  & !)k ! 9rW#3+!+ ++01#!"&55463!2#!"!2#!3!2W + +7  #  ++  F @ |y#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#Zj m ?b "   RV/=KL//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/M+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2;::;D( S+*S"''"R*+R"(\ ` F' (( ( H ;#I$//$и// /+и012346332!2#!"&554633463q+ A   2 ""2 ;8"#M$//$и/ + ии0123546332!2#!"&5546335463q+ A  "  "" ; #I$//$и// /+и0123546332!2#!"&5546335463q+ A    !! ;S<#M$//$и/ + ии0123546332!2#!"&5546335463q+ A  <  "" ;! /+01463!2#'&766766'&&#!"&5 '   O` (\/.S ;zT# + /+012#!##"&5!"&55463 w*| T# q  #!i +A]A)9IYiy ] / +014&#!"5543!2#66  / @ " "+E"#B<;# + /+012#!##"&55!"&55463 x+ | "   "j# + /+017463!2##"554&#!"&5 /)  #*r+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5 / $  /%#)&#)`)?#27+2 /.;+01''&67667665432'&'&'46332##"554&##"&5 4G - <$Y2 PN /%  9#S6  '!;*  )> &D #)h!i +A]A)9IYiy ] / +014&#!"5543!2#66766  / @  # !*U-,RO++R;Os# + /+012#!##"&55!"&55463 +s s# #>+ /+01#!"&5543323!2> .'  o&)t{"IE:3+C/++/+и+/C=и=/C@и@/01'&'''&67676&##"554332##"##&&5543323232676676^ A9D_; ;7C y  #C/*   '  p(>D2  R~ " -3+'A !) !i +A]A)9IYiy ] / +014&#!"5543!2#66 / @ ""+?;#L)?     j(`jûR.+J6+b++bи.>и>/iиl //B//'/C:+3M+a+C и /Cи/'и/'$и$/'eUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"332676676727332655U'( ,  "J!+~   - C"   ] n  0ur'%  J* 9#G*<  w wgBl6+.+.и/"и"/-Bи.OQ/: +'+1+ и/:<и*3  =  6*'\D /(&#,  5$Sp=Bf3+++`I+и/`W//E/NS+X_+E и /Xи/Sи/N#и_.иEd6и6/d:и:/01##"&'&&55463326554&##"&5546332##"332636676676##"&5546332##"32##3326 #N!,   ,G"  M , ,  q  D*4# B)7  /*)  J  L qbcgٻU1+M9+d+e+1AиA/i/E/ /'/*/6P+=и/ и=и/ +и+/FиF/XиX/\и\/_и_/dи=fиf/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33267667672735#[17 :5<  'Q"+  - H%  [[    G)6#D)8  eZC[iuA5+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DsJ+9 +x+&+g^+V0+ и/9<ии>/fиf/Vm01##"&'&&55463326554&##"&5546332##"33267677672%#"&'&&546766327#!"&55463!24&#"326##"&554332  #P  ,  ",H!  \:#$;;$#: ` Y0,.11.,0  q  A)/#>)1  )***' % (''' $ |%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5"  , +? m   ** { &o'//'и/ и /ии#&и&/#(// ++01%3!2655"&'&&546332!546332#  7 ** ++^ ^ba%hn@JB++("+BA""]A")"9"I"Y"i"y""""""" ]9"(9I /%/E+A+01"&'&&5463323546332#7&766'4632'&&'&''%332655  '&-[ RO.  ),B,A G0 {  fc & 9f  .(<37% 8 +M$jj;# + /+012#!##"&55!"&55463 x+ | "   "m^-+/01%'&&'&'&'''&67667667632 ,q Bp,--/}JCq)(1 /  Cb "8""&1*)C % 9$$O' imz>q +A&6FVfv ]A]и/: 9//01''&6766766543327654632'&'&'&'&'&'2G / D:|0  *+Qb1 -_[* T" 6J-?n] K24#  6A IC5A &Bm)5˸6//A]A)9IYiy ]6*и*/ A & 6 F V f v ]A ]7!+0+01%4&'&&#"3267667667#"&'&&'&&546766326'%QQ%&55&%Q33IA.0l-E"#?A//m+-l1.@"+  +""(  !3? 2!2? A;# + /+012#!##"&55!"&55463 x+ | "   "tp1 !+01'&&'&'&'&''&676$76&#!"5543!2^ &a331L_ T G N4#<'**U% r  3  \8  # 4. !   ;# + /+012#!##"&55!"&55463 x+ | ""gc2>38+"+01'&&'&'&'&''&676676676&#!"5543!22#!"5543R(c331 IFDw+K).9Q(**V&  4    , #  !&"  Xl5 + и /++017463!2##"55!"5543!54&#!"&5 /)   ")v " Jz( #3+!+ ++01#!"&5463!2#!"!2#!3!2( --     x** U \ |?#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#+p| v ]N "   <I#a +A]A)9IYiy ]/+01463!2##"766766'&&#!"&5 _)   R**M M'(NIs@ @A// иAи/=B///+)<+ <и/<и/)&и&/),и,/01##"55#"554334332"&5"#'"&554632676676676676#@ ) )  )*+NR+)O!R))J  DOFF ! F ^#  "   M m +A]A)9IYiy ]и//+01463!2#'"7667665&&#!"&5 X  )   8t3 8:UA ?@// и@&и&/A/"/ +7,+,'и'/,*и*/74и4/7:и:/01%##"55#"5543343326##"&55###"&5546326766766766A) ) DKCE*&*)'@K))L N'(L9 " "  ! kM#/+01463!2#'"7667665&&#!"&5 X  (   E##B D$#CMNA @A// иA'и'/ B// +;+++и/+(и(/+.и./;8и8/01%##"55#"5543343326##"&55#'"&5546326766766766A) )  "M*(*B+*)+NO*)O"W+*L\l $  !   ! M m +A]A)9IYiy ]и//+01463!2#'"7667665&&#!"&5 X  )   9s4 9:MA ,@A/2/Aи/)2-2:и-B=/+:3+(и/(и/(и/и/и/01%"&55"##"&554632676676676776#%##"55#"554334332c &+*QN*)N$R(*%D DUHA) ) ! "   , " jE#/+01463!2##"766766'&&#!"&5j " '  N%'F E%$G 4sb EqB#+ ++ иG// +2(+($и$/(&и&/2/и//01%4332##"55#"5543##"54332"&5'"&554632676676##p '' &&10A1:}:=l;  6" } a" !  a +A]A)9IYiy ]/+0146332#'"766'&&##"&5   (    7u5 2x3Cb 9E6+ +:?+ и:GB//&=+ += и /&и/и/&#и#/&13и3/014332##"55#"5543"&55'"&55463676676"#%##"54332{&& 7392 9m:B7 B##$''= ! v "     lc# + /+017463!2##"554&#!"&5 0) ")qc+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5M 0$ /$  #'##'g$:;//A]A)9IYiy ]  и / и/ 9;2и2/- <)6+01&7665462'&&'&''46332##"554&##"&5#ZE. IF +3K2  /$  y CxV )=X! ,'L")#) a +A]A)9IYiy ]/+0146332#'"766'&&##"&5   (    7u5 2x3CPb EmB#+ ++ иG// +2&+&$и$/&(и2/и//014332##"55#"5543##"54332"&55'"&55463676676"#p%%''4/92 9m:Ag6 5 L !   "  !  n~+ +01#!"&5543323!2n 0*  m#(t"IE:3+@/++/,и,/@=и=/@CиC/01'&'''&67676&##"554332#"##&&5543323232676676 ?5DbB <4B {  "M/ * * &  p7=(  8p # ,2#4!* a +A]A)9IYiy ]/+0146332##"766'&&##"&5   (    6X4 2Z3Cb 8D5+ +9>+ и9F/A/&<+ +&и/и/&#и#/&13и3/014332##"55#"5543"&55'"&55463676676##%##"54332w&& 6292 9m:B7 F$%# ''i)  ! w "     j<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+3$+aY+H?+N+$и/$!и!/36и6/01#"&'&&54676632"##"&554332332676676%#!"&55463!24&#"3267##"&554332d:$$::$$:  %U-( O# '  g k ]0--11--0  *()('W#*  #%$%% " {k!++ +01#!"&55463!2#!"3!2k ,,   w** ! g  rx2i3//3и/+и/и/#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2x /#,   #-: #)<*##4*0 rcBX4+,+KP+ и /KZ /M/GT+/+ и/Tи/G$и :7и7/:=и=/ NиN/01#"##"&'&&55463326554&##"&5546332##"33267667646332##"54&##"&5?  !,  !, (" 2 0$  p <)*#8)+ #(rcWgH$+@,+[+ c+$4и i/8/EXC/C>Y`+90+9и/и/иC(`Kи`PиP/0XиX/01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"3326776766767"3326554&#B +,6 t",   !-  F  w t s+**   4)" #1' #    kb_iûQ-+I5+a++aи-=и=/hиk //A//&/B9+2L+`+B и /Bи/&и/&$и$/&dTиT/01"&'&&5463323546332#'###"&'&&55463326554&##"&5546332##"332676676727332655  '( ,! o +    ,C#  ] n  fd'$  =*+#9+.  vhhiCm6+.+.и/"и"/-Cи.PR/: +'+1+ и/:=и=/j1901""##"&'&&55463326554&##"&5546332##"3326367672''&6766766'432'&&'&'   N .  ", D  C(? . 9(> 3q  =*+#9+/  =  2))ZC  0($ *  3%QquAe2+*+_H+и/_V//D/MR+X]+Dи/Xи/Rи/M"и]-и-/Dc5и5/c9и9/01##"&'&&55463326554&##"&5546332##"332637676776##"&5546332##"32##332-6 s",   -G!   L , ,  q   ;)) #7( +  /*(  @  A rocgѻU1+M9+d+e+1AиA/i/E/ /'/*/$/6P+=и/ и=и/ +и+/FиF/XиX/\и\/dи=fиf/01%##32#!"&5546335#"&55463!2"#"##"&'&&55463326554&##"&5546332##"33267767672735#h.5 95 6 P  -  !,G  UU !   <) +#9).  juC[iu*5+-+pP+Dj+!APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DEX0/0>Y9J+x+&+g^+Vm+09<иD/  ?& g3 0\W- 5 # +oI 6-/&j@ ) &AF5 +=Wuk/˸0/ /0и/A&6FVfv ]A]A ]A ) 9 I Y i y ] $1*++01%32676654&'&&#"4676632#"&'&&1.$$X(%X%%33%%X%(X$$.K7,.s?9s-0990-s9?s.,7#  #$  $)88**:: q$ +01'&'&'''&7$76&#!"5543!2UPAC *w R.7G/:FAs 4% A_ # -0)#   f%1&+++01'&'&''&67$76&#!"5543!22#!"5543"K).+Ksy &(! "] #  !! /rc5 + и /++01%463!2##"55!"5543!54&#!"&5 /)   #)_ /qt#D+EX/>Y!+ +01#!"&55463!2#!"!2#!3!2t ,!!,    **  7 " : |#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#dj n @cd "     ΟRr/=KL//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/M+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2;::;D( S+*S"''"R*+R"(\ ` F' (( ( H Is@ 89//9и/5://!+и/и/и/!и/!$и$/4и4/01##"54332"&5"'"&554632676676676676#@ )) *)*+NR+)OM+)P" GPA@} ]! "  UA ,89/2/9и/ 2-:5//*+и/и/и/$!и!/$'и'/016##"&55"##"&5546326766766766##"54332 DK>A,&*)'@K))L N'(L))"  !  MNA .:m;/4/;и/ 4/<7/1/)+и/и/и/)&и&/016##"&55"'"&5546326766766766##"54332  "M*(*+*)+NO*)O"W+*L)) !   ! MA +7m8/1/8и/(1,94/+'и/'и/и/и/01%"&55#'"&554632676676676776#%##"54332l&+*QN*)N$R(*%D DUC=)) ! "   -;{Y#I$//$ и / / /+и01"&5###"&5#"&55463!2###s * Q l""l;#M$//$ и / #+и# и01%"&55###"&55#"&55463!2###s +  Q   "" ;F}#I$//$ и / / /+и01%"&55###"&55!"&55463!2###v * Q F   ##  ;#M$//$ и / #+и# и01%"&55###"&55#"&55463!2###r+  Q   ## ;#M$//$ и / #+и# и01%"&55!##"&55#"&55463!2###|, Q    ""  ;#I$//$ и / / /+и01%"&55###"&55#"&55463!2###s +  Q  "" ;#M$//$ и / #+и# и01%"&55!##"&55#"&55463!2### + Q   !! ;N +01%2#!"&55463  N##!i +A]A)9IYiy ] / +014&#!"5543!2#66766  /@  !* K&&IG$&F;] +012#!"&55463  ##;!] +012#!"&55463  ]##;k +012#!"&55463  ##Is@ -O+//%+и/и/%"и"/%(и(/01##"543322#'"&5546332726766766@ ))   DQS*+NF)*(Q$R*'H}#  # NA $3+&!// +и/016##"&55463326766##"54332Wh>v86d))# # NaA $3+&!// +и/016##"&55463326766##"54332Wh>v86d))[# #NA $3+&!// +и/016##"&55463326766##"54332Wh9v78f))# $fNA $3+&!// +и/016##"&55463326766##"54332Wh>v86d))# #*sA +//01##"54332A))}A +//01##"54332A)) A +//01%##"54332A))im+ /+01#!"&5543323!2m .(  o$'!A +//01##"54332A))0w*(/01%'&&'&'&'''&6766766763282c >q-,! ,.}MBo+)/ 0 Ka "7#$)/*.G % =&$U* c.:/4++01'&&'&'&'''&76676&#!"5543!22##"5543%c222  Z P '*BX&*+Q"  3 !R/   +'  jF)_56/&/6 и /A&6FVfv ]A] и/A&&]A&)&9&I&Y&i&y&&&&&&& ]&&и и /&0и0/+)и/01''&76676&##"554332''&7676&##"554332csh  _d  0% U5 " _u  ~  yc  [~  #%TKJ6  c   DX5+)и/01''&76676676&##"554332''&76676&##"554332d |h  1G  0 ss  dd u aB < H( " 'rW  Kg " DX5u&+A]A)9IYiy ]&+)и/01''&76676&##"554332''&76676&##"554332d#W5  Zg  /vp  ak u .\,-T!  6Q #%vS  M_  G)X5+)и/01''&676676&##"554332''&676676&##"554332c tm Vj   0!tv eb  t  TB  1}? ! "&gQ DZ ! ;/23/#/3 и /A&6FVfv ]A] и/A##]A#)#9#I#Y#i#y####### ]#  и /#-и-/+&и,01''&76674&##"554332''&7676&##"554332Bh[  X\  .nf  cd U hrX  T}  $&l  j  xsc I// и /ии//+01##"5###"5433234332c(d''d(QDkC!2+&и,01''&76676&##"554332''&76676&##"554332H k`  P[  v 0hc  TU  Y lT< 5|@  !'qH ! <]   xc I// и /ии//+01##"55###"54332354332c(d''d(C!5u)+A]A)9IYiy ])+,и201''&76676676&##"554332''&76674&##"554332H"L-  $>  v .jb  WX Y l/|+Z**L ;# L% $&sL ! Cb $xcc I// и /ии//+01%##"5###"5433234332c(d''d(r<_C*!2m#+A##]A#)#9#I#Y#i#y####### ]+&и,01''&76676&##"554332''&76676&##"554332H m^  NX v 0ef  ZT Y lQ>  3tB ! 'lL! ?U !  xc I// и /ии//+01##"55###"54332354332c(d''d(*xc I// и /ии//+01%##"55###"54332354332c(d''d(ݹxsc c //  и /ииии!//++01##"5###"543323433235#c(d''d(dd=Dpb c //  и /ииии!//++01##"55###"5433235433235#b%n%%n%nn k_pob c //  и /ииии!//++01%##"55###"5433235433235#b%n%%n%nn}žM~xc c //  и /ииии!//++01##"55###"5433235433235#c(d''d(dd*j]snpb c //  и /ииии!//++01%##"55###"5433235433235#b%n%%n%nnmT)m56/&/6 и /A&6FVfv ]A] и/A&&]A&)&9&I&Y&i&y&&&&&&& ]&&и и /&0и0/+)и/01''&76676&##"554332''&7676&##"554332qsh  _d  0% U5 " _u  }  yc  [~  #%TKJ6  c   B '+ // +01##"55#"5543354332B* * # Xl5u&+A]A)9IYiy ]&+)и/01''&76676&##"554332''&76676&##"554332x$W5  Yh  0up aj u .\,-T!  6Q #%vS  M_  1lA '+ // +01%##"5#"5543354332A) )zM # \)m5+)и/01''&676676&##"554332''&676676&##"554332x ul Vj   0 sv eb  u TB  1}? ! "&gQ DZ ! #%A '+ // +01##"55#"5543354332A) )3 $  sb E // и/ и!// +01##"5#"554334332##"54332 (n n( &&X $ (}b E // и/ и!// +01##"55#"5543354332##"54332'} }'&&# $ ec E // и/ и!// +01%##"5#"5543354332##"54332& &&&P $   c E // и/ и!// +01##"55#"5543354332##"54332( yy (((8 $ >b E // и/ и!// +01##"55#"5543354332##"54332'} }'&&  $ AB 9+ и// ++01##"55#"554335#"5543354332B) ) $ i " AB 9+ и// ++01%##"55#"554335#"5543354332B) ) $ x # G!A E+ ии// ++01##"55#"554335#"5543354332A) )/] $ z $  sb 'W +!+ и ии)$//+ +01##"5#"554335#"5543354332##"54332 (n nn n( &&X_ # $ }c 'W +!+ и ии)$// ++01##"55#"554335#"5543354332##"54332&t tt t&&&!o # d %  fc 'W+!+ и ии)$//++015#"5543354332##"5#"5543##"54332e e&&e C&&` # ~ # #c 'W +!+ и ии)/$/ ++01##"55#"554335#"5543354332##"54332&t tt t&&&Ao $ X # lE c 'W+!+иии)$//++015#"5543354332##"55#"5543##"54332ll (( lJ((Y# q $ @"BC//A]A)9IYiy ]и/  и /и/C;и;/*A*&*6*F*V*f*v******* ]A**] D+&и>0146332''&766766'4&##"&5%46332''&76676654&##"&5   (  . " ($   !\02]#%a1/["')%^00\' )X-+X%;]#+/ +012!2#!"&55463!463x  ]""*9Ѹ://A]A)9IYiy ] :2и2/$A$&$6$F$V$f$v$$$$$$$ ]A$$] ;+ и50146332''&766'&&##"&5%46332''&766766'&&##"&5    %  0-*       4r//t,"'(9u8  37;)# ++ 01546332!2#!"&55463)|  e !!)"<=//  и /и/=5и5/*A*&*6*F*V*f*v******* ]A**] >+&и80146332#'&7667655&&##"&5%46332''&766'&&##"&5   % 0-*  %)  <994!&)88 ,q4; # +/ + 01546332!2#!"&55463)|  ; ##*9Ѹ://A]A)9IYiy ] :2и2/$A$&$6$F$V$f$v$$$$$$$ ]A$$] ;+ и50146332''&766'&&##"&5%46332''&766'&&##"&5    %  , "  #*  7c*/_0!"-76  %c*;L,# ++ 01546332!2#!"&55463)|  ##Md">ٸ?//A]A)9IYiy ]  и /и?7и7/*A*&*6*F*V*f*v******* ]A**]+&и:0146332''&766764'&&##"&5%46332''&7674&##"&5\    &  / !  S    M((P"!I'%P##(("V**P! @s ?@/ /и@;и;/A //8+++и/8и/+(и(/+.и85и8;и;/01####"5433232%26776676#"'"&55463273275463 (( Q&JCQO((LI)NIE}y #  "`h6m /+A & 6 F V f v ]A ]+и20146332#'&76'&&##"&5%46332''&76654&##"&5g 0  &  / "  ('  ((0h9gd#((<9 0d3H 2k3/'/3и/'"-и"4$/*/ +/ + и/и/016##"&55463354332676%####"5433232 @} )/-hm  ))  Z! ${zknu4m!-+A!&!6!F!V!f!v!!!!!!! ]A!!]+и00146332#'&766'&&##"&5%46332''&76654&##"&5w /  '  .0*  %+  &)5j40k2"&);z3 +j3Hh 3g4/ /и4)и)/05 //, ++ )0и0/)2и2/01####"54332326##"&55463354332636  ))   @} )&'h{r# #`h:m$3+A$&$6$F$V$f$v$$$$$$$ ]A$$]+ и60146332''&766764'&&##"&5%46332''&76654&##"&5g 0  & / "  ('  ((/c*.0"((28 /X'H 3g4/-/4и/-(и(50/*/ +&+и/и/016##"&5546335433276732####"54332 Qc ) Fh؋  )) x" #"H 2g3/ /и3(и(//4 //+++(/и//(1и1/01####"54332322##"&55463354332636  ))  @} )/-h"  ${zI ">ٸ?//A]A)9IYiy ]  и /и?7и7/*A*&*6*F*V*f*v******* ]A**]+&и:0146332#'&766764'&&##"&5'46332''&7674&##"&53 ~  & o v. ! T h    L)(P!K&%Q"#')#T**R @sb D @+ ++и//@5++@ и /@"и"/52и2/@<ии>/01##"5###"5433234332267676676"'"&55463275463b'y%%y'< #>=#9w:2:*-QDkL  # ! W@ݸA//A]A)9IYiy ]  и /и/A9и9/'A'&'6'F'V'f'v''''''' ]A'']+#и<0146332#'&766'&&##"&5'46332''&76676654&##"&5: | & n x/   j  .+&\(#((3/ )*Nb D>5+ ++и//9'++'$и$/'*и'@1и1/@3и3/@5и5/@>и>/@BиB/01%##"55###"543323543326#"'"&55463327546332276766b's& &s'8!!B;~89>-+* 3ճ_ $ ! W@ݸA//A]A)9IYiy ]  и /и/A9и9/'A'&'6'F'V'f'v''''''' ]A'']+#и<0146332''"766'&&##"&5'46332''&76676654&##"&5: | & n x/   j  B0%u(!&*F5 -?W@ݸA//A]A)9IYiy ]  и /и/A9и9/'A'&'6'F'V'f'v''''''' ]A'']+#и<0146332''&766'&&##"&5'46332''&76676654&##"&5: | & n x/   j   +&$V' !&*/+ #( Nb C>5+ ++и//9*++*%и%/*'и'/*@1и1/@3и3/@5и5/@>и>/01##"55###"543323543326#'"&5546332754633227766b't''t'5!B!;~89>-+* >1!ͷ # "} zNsA 5q6//6&и&//7//%+и/и%"и%/и//%1и1/01##"543326#"'"&55463273546332676766A)) AQO()KI)s*()&K}%  " QA ,Y-/&/-и/&!.)/#/ +и/и/012676676###"&5546335463##"54332,*5`* @| ** # ! QdA -Y./'/.и/'!/*/#/ +и/и/012636672##"&5546335463##"&54332+*5`* @| ** $ # NA +U,/%/,и/% -(/"/ + ии/016##"&55463!543326766##"54332Wh' &%8f))z! #| }MQA ,Y-/&/-и/&!.)/#/ +и/и/012676672##"&5546335463##"54332,*5`* @| ** # # 8;Y#I$//$и// /+и012354633232#!"&55463!5463+   VY  "" ;%#M$//$и/ + ии012354633232#!"&55463!5463e+  q  "" ;#M$//$и/ + ии012354633232#!"&55463!5463e+  q  " " ;T2#M$//$и/ + ии012354633232#!"&55463!5463n+  g2  "" 22#AٸB//A]A)9IYiy ] B:и:/+A+&+6+F+V+f+v+++++++ ]A++]:4и C+'и=0146332''&76676654&##"&5%46332''&76654&##"&5   &   . )   '\./X# !X./Y$!"-(Y--X% UK,#DݸE//A]A)9IYiy ] и/E=и=/+A+&+6+F+V+f+v+++++++ ]A++] F+'и@0146332''&76676654&##"&5%46332''&76676654&##"&5  )   /&   E'&G G&%F"'(G&$E D#$GZ,#DѸE//A]A)9IYiy ] E=и=/+A+&+6+F+V+f+v+++++++ ]A++] F+'и@0146332''&76676654&##"&5%46332''"76676654&##"&5  '   / '   M++T O++S"')Q++N N()P,#>Ѹ?//A]A)9IYiy ] ?7и7/+A+&+6+F+V+f+v+++++++ ]A++] @+'и:0146332''&76676654&##"&5%46332''&766'4&##"&5  )   /&   ;!!? < A"'(=~5 7}7e*c"E+&иA0146332''&766766'&&##"&5%46332''"766766'&&##"&5e   (      '   J(*T)!Q*)M! I'(N"G&'K se"E+&иA0146332''&766766'&&##"&5%46332''&766766'&&##"&5s    '     %   ;! ?><! 9 ;66]p"E+&иA0146332''&766766'&&##"&5%46332''"766766'&&##"&5  (     %  F%&KI&$G #  C$&FB#"B se"E+&иA0146332''&766766'&&##"&5%46332''&766766'&&##"&5s    '     %  ;! ?>;" 9 :66MA ?@// и@и/<A/,?+ +,;и/;и/;и/,&и&/,)и)/01##"55#"554334332"&55"'"&554633272676676676#A) )" &+*QB(*)N$R(*K  DUHA   "  "   K(B+#и>0146332''&766'&&##"&5746332''"766766'&&##"&5K    &v w   ' h   J&(N&M=#  F''O"L&$Id7q+A&6FVfv ]A] /+и30146332#'&766'&&##"&5746332#'&766766'&&##"&5d }+&o j+"  &  X 26t34u."!/0l373ds @A/6/Aи/A&6FVfv ]A]и/A66]A6)696I6Y6i6y6666666 ]6%и%/6(69и9/+$и<0146332''&766766'&&##"&5746332#'&766766'&&##"&5d |,' m j+"  & \  0=: <;"".8;;6f7m+A&6FVfv ]A]+и30146332''&766'&&##"&5746332#'&766766'&&##"&5f z* %l k,!  % ]  15r2 2t,"!0/k262MA ,8m9/2/9и/)2-:5/,+(и/(и/(и/и/01%"&55"'"&554632676676676776#%##"54332l&+*QN*)N$R(*%D DUC=)) y" !  } );N +01%2#!"&55463  N##;I +012#!"&55463  # #;P +012#!"&55463  P!!;i +012#!"&55463  !!Is@ $0W%*+%2-/'/+ и /и/и/и/01%6'"&5546326726766766##"54332 N(*U'S*+NR+)O!X*+N )) !  " }NA $3+&!// +и/016##"&55463326766##"54332Wh>v86d))s# #} NoA $3+&!// +и/016##"&55463326766##"54332Wh>v86d))U# "I@ *W$+,'/!/+ и / и /и/и/016#"##"&55463!2766##"54332JJ &4;D J ))  ! bNA $3+&!// +и/016###"&55463326766##"54332Wh9v78f))n! #7+/+0173676'&&54332 _]%k:;u,-#) (*=+/+01676'&&54332 _]%i98t2,")   '*K'+/+и/01676#'&&54332 ^_  'i:9q0,")y    (*_[+/+01676'&&54332 d\%j8:s1,")   &*5.+/+017676'&&54332 UD?Y,#) ')?3+/+01676'&&54332 UF?Y-#) ')D)+/+01676'&&54332 UE>Z,#)w '*VM)+/+01676'&&54332 UE>Z,#) &*;3+/+и/и/0173676676'&&54332 1i43e,(k;9u0,")   '*s@ 4+ //EX / >Y01##"5!"5543!4332@) )! # !8'+/+и/01676'&&54332 _]%h:7u2,")   &*LB '+ // +01##"5!"5543!54332B**  $ '+/+и/01676#'&&54332 ^_  'i:9q0,")y    (*_B '+ // +01%##"5!"5543!54332B**t % [+/+01676'&&54332 d\%j8:s1,")   &*5(B '+ // +01##"55!"5543!54332B**6 $ sc R // и/ и!//EX / >Y01##"5#"554334332##"54332& &&&X #}b E // и/ и!// +01##"55#"5543354332##"54332 ( ( '' $ qb E // и/ и!// +01%##"5#"5543354332##"54332 ( ( ''b # ~b E // и/ и!// +01##"55#"5543354332##"54332 ( ( ''4 # :sA 9+ и//+ +01##"5!"5543!5!"5543!54332A)   ) $  "@ 9+ и//+ +01##"55#"554335#"5543354332@( (  $ ^ # a@ 9+ и//+ +01%##"5#"554335#"5543354332@( ( # ` # ~)@ 9+ и//+ +01##"55#"554335#"5543354332@( (7 # T # Ssc 'W(/!/( и /и  и!)$//++015#"5543354332##"5#"5543##"54332p && && #  # }c 'W(/!/(и/ ии!)$//+ +01##"55#"554335#"5543354332##"54332& &'' #J $ \qb 'W(/!/(и/ ии!)$//+ +01%##"5#"554335#"5543354332##"54332% %%% # [ " g*b 'W(/!/(и/ ии!)$//+ +01##"55#"554335#"5543354332##"54332% %%%F $ F " IJ98+ /+01#!"&543323!28 .'  F((; ;I# +/ + 01%546332!2#!"&55463+  M ""/+ /+01#!"&5543323!2/ /)  (( ;# ++ 01546332!2#!"&55463+  : ""/+ /+01#!"&5543323!2/ /)  ') ;# ++ 01546332!2#!"&55463+  7| |""/+ /+01#!"&5543323!2/ /)  &) ;S# ++ 01546332!2#!"&55463+  w w""e\<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+6$+aY+H?+N+$и/$!и!/62и2/01#"&'&&54676632####"&554332336636676%#!"&55463!24&#"326##"&554332@:$%99%$:   %K/) B" '  h k ^0--11--0  "..,+`"*  & )())# % {32e3//3и/и/#+#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!23 0#,    #,R  y#*G+7$ G)< q-BX5+-+KP+!и!/KZ/ /M/9N+GT+0+N и /Tи/G%и9;и;/01#"##"&'&&55463326554&##"&5546332##"332766767246332##"54&##"&5 ! &P  ,   !,k   .$ pR)A#O)C  B#(-r.YiK'+C/+]+ e+'7и7/ k/;/// /<3+,F+<и/!иbNиbRиR/3ZиZ/01%46332##"&'&&5"##"&'&&55463326554&##"&5546332##"33263667672"3326554&#++ $N!-   !-B"  x r )* )R)A#O)D     i(_iR.+J6+a++aи.>и>/hиk/ //B/d'+C:+3M+`+C и /Cи/'$и$/dUиU/01"&'&&5463323546332#'#"##"&'&&55463326554&##"&5546332##"33276676727332655U'( ,  "J!+~   - e   ] n  @|z'%  R* @#N+D  ~ ~hBl5+-+-и/!и!/,Bи-OQ/9 +&+0+ и/9<и  :*)jD  0+$!& 0  :'!]q=Bf3+++`I+и/`W//E/NS+X_+E и /Xи/Sи/N#и_.иEd6и6/d:и:/01##"&'&&55463326554&##"&5546332##"332636676676##"&5546332##"32##3326 #N!,   ,G"  M , ,  p   L*; # I)>  /**  Q  S rbbfT0+L8+c+d+0@и@/h/D/ /'/)/5O+<и/ и<и/ *и*/EиE/WиW/cи<eиe/01%##32#!"&554633#"&55463!2##"&'&&55463326554&##"&5546332##"3326766767273#[17 :5<  y"+  - H%  [[     O)>#L)?  _cBZht+5+-+oO+Ci+!и!/AOO]AO)O9OIOYOiOyOOOOOOO ]Aii]Ai)i9iIiYiiiyiiiiiii ]CrI+9 +w+&+f]+Ul+ и/Uи/l0и0/01####"&'&&55463326554&##"&5546332##"3327667672%#"&'&&546766327#!"&55463!24&#"326##"&554332 'W -   !, u ]9$$::$$9 ^ [0,-11-,0  r  U)C # Q*F  *+,+* % ))((+ % f{:DG<++<и%ܸC'/?+;+01"&'&&55463323546332#7''&676'432'&'%332655  '&- , {00]   YS'  a %"|5 3`\c[[{(#3+!+ ++01#!"&55463!2#!"!2#!3!2( --     w))  B " H IT+ /+01#!"&543323!2T /) [ T&)B+ /+01#!"&5543323!2B .( ; &)H 4k5/)/5и/)$/и$6&/,/ +1"+ и/и/016##"&55463354332636%####"5433232 Qc & (%hr  ))  K" #xp# + /+017463!2##"554&#!"&5 .( & $)p+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5 /% /$ $)$$)g$:;//A]A)9IYiy ]  и / и/ 9;2и2/-)6+01&7665462'&&'&''46332##"554&##"&5ZE. IF +4J2 /%  y CxV *=X! ,'L")#) B+ /+01#!"&5543323!2B .( ; ()H 3[4/ /и4)и)/15 //, ++ )1и1/01####"54332322##"&5546335433276  ))   Qc ' VhO# $K+ /+01#!"&5543323!2K .)  o$)t"IE:3+@/++/,и,/@=и=/@CиC/01'&'''&67676&##"554332#"##&&5543323232676676f ?7BcC ;3A z   #M0*) &  p7=(  8p # ,2#4!* [+ /+01#!"&5543323!2[ .( H &*H 6k7/+/7и/+%1и%8'/./+3#+и/и/012676672##"&55463!5463####"&5433232%#6^) A} N  ))   " $ ; |4!++ +01#!"&55463!2#!"3!24 + +7  v** !  |%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5A  #!+  ,^ l   ** | &_'//'и/ и /ии#(// ++01%3!2655"&'&&5546332!546332#)  V++ ,)Y Y `^'H 4g5/ /и5)и)/16/ /, ++ )1и1/)3и3/01####"54332322##"&55463354332636  ))   Qc & (%h*" #w&$/01%'&'&'''&676674328 %m YE)).N 1 8W !9,;)"%= ! (zM m=q +A&6FVfv ]A]и/9 9//01''&6766766543327654632'&'&'&'&'&'3G 0 E9}/  )+S c1 .^[* R" 6J-?n] I14#  6AIC5A &Br+/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+01%4&'&&#"3267667#"&'&&54676632) !R*.R$$R.*R! )G3*)m9=n'*..*'n=9m)*3 ' ' & &"+==+(==qz% +01'&'&'''&76676&#!"5543!2jUO@B} Nv Q/-R1:EAs 8& "W6 ! ./,*  r5 + и /++017463!2##"55!"5543!54&#!"&5 .(  & !)k ! 9|Z#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#;j o @c "     o;(+EX / >Y+01#!"&543323!2 .(  G')+ /+01#!"&5543323!2 .(   &)+ /+01#!"&5543323!2 .(   ')Jlc >;4+ ++и//7*+7и/*'и'/*;1и1/;3и3/;=и=/01%##"5###"54332343326#'"&554633354332676c&z%%z&=>97 8?K(/. &D #)/+ /+01#!"&5543323!2/ /)  '(;1F# + /+012#!##"&55!"&55463 +s F##t"IE:3+@/++/,и,/@=и=/@CиC/01"'&'''&67676&##"554332#"##&&5543323232636676e A5Bd? ;4A y   #C/)   '  p :@-! ?} !,3'#= #) /+ /+01#!"&5543323!2/ /)  &) ;# + /+012#!##"&55!"&55463 x+ | "p p"k[<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+<$+aY+H?+N+$и/$!и!/<2и2/<6и6/01#"&'&&54676632"##"&554332336676676%#!"&55463!24&#"3267##"&554332>9%$99$%9 (Q.) H% &  r j _/.,11,./  7  ""  "F#*  #  # |)!++ +01#!"&55463!2#!"3!2) ,,   v)*  | z42e3//3и/и/#+#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!24 0#,  $,R  z#*1*$$ 3*& f-CY5+-+LQ+!и!/L[/ /N/9O+HU+0+O и /Uи/H%01##"&'&&55463326554&##"&5546332##"3326766767246332##"554&##"&5 ! &P  ,   !,E%   .$ z 8*$!2+)  #*r6YiK'+C/+]+ e+'7и k/;/b+<3+,F+<и/и/ иbNи3ZиZ/01%46332##"&'&&5####"&'&&55463326554&##"&5546332##"332676676727"3326554&#+, &Q!-  !-G$  x z j**) /)#+)     k(]gO++G3+_++_и+;и;/fиi //?//b$+@7+0J+^+@ и /@и/bRиR/01"&'&&55463323546332#'##"&'&&55463326554&##"&5546332##"332676676727332655U'( , !+~   - C"   ] n  XV'%  /*#++   h[[fB`6+.+.и/"и"/-Bи.KM/: +'+1+ и/:<иYEX:/:>Y>+:01"&'&&55463323546332#7''&6767432'&'%332655  '&- , &t81^  KE% N  (x&  %ne^J J;_# + /+012#!##"&55!"&55463 x+ | _""mp> //01''&6766'432766'4632'&'&''&'&'gX 0  !9=D/  ?' e4 1[V- 4 # +oI 7./&j@ ) &A F5 +=Wl)8˸9//A]A)9IYiy ]9*и*/A&6FVfv ]A]:!+3+01%4&'&&#"3267667#"&'&&'&&546766766326'&Q226'%PQ&'6IA.1l,E!#AA#!E-k1.A "(  !"*  *#3? 2#!2 ?;|# + /+012#!##"&55!"&55463 x+ | |" c c "tp1 !+01'&&'&'&'&''&676$76&#!"5543!2^ &a331L_ T G N4#<'**U% r  3  \8  # 4. !   ;# + /+012#!##"&55!"&55463 x+ | !nn!j5 + и /++017463!2##"55!"5543!54&#!"&5 /)   "*k ?z(#D+EX/>Y!+ +01#!"&55463!2#!"!2#!3!2( --     x**  0 " 1 |?#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#+p| v ]Nx "   S+ /+01#!"&5543323!2S /) K ')Is@ ?s@// и@и/<A// +(;+;и/(%и%/(+и+/01##"55#"554334332"&5####"&554632636676676676#@ ) ) )*+(@R+)O!R))J  DOFF " |3" !  H+ /+01#!"&5543323!2H /* 2 '(MA @A// иAи/=B///@+ +/<и/<и/<и/<)&и&//,и,/01%##"55#"554334332"&55###"&554633272672676676#A) ) '**)BC'*+N"N&(J CPIG/ # !#  jA# + /+017463!2##"554&#!"&5 /(  w#*qB+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5- 0% /$ t#'$#'g$:;//A]A)9IYiy ]  и / и/ 9;2и2/- <)6+01&7665462'&&'&''46332##"554&##"&5  [D. IF +3J2  /%  y CxV )=X! ,'L")#) [+ /+01#!"&5543323!2[ .( H ')M7A ?@// и@и/<A//.?+ +.и/и/(%и%/(+и+/;и;/01%##"55#"554334332"&55##'"&554633276676676676#A) )'**OC'*+N"N&(J CPFDF^ $ d"# t"IE:3+@/++/,и,/@=и=/@CиC/01'&'''&67676&##"554332#"##&&5543323232676676q =7DaB <2B {   #M.) ) &  p7>'  8p # ,2#3!* [+ /+01#!"&5543323!2[ .( H &*MA @A// иAи/=B//@+ +/")&и&/),и,/":и:/"<иYB9+`+B и /Bи/&и/&$и$/L1&dTиT/01"&'&&5463323546332#'###"&'&&55463326554&##"&5546332##"332676676727332655m'&,! p  ,}  -~C#  ] n  _]'%  6*$#2+'  oaaiBl5+-+-и/!и!/-OQ/EX0/0>YEXL/L>YEXZ/Z>Y9 +&+ и/09<и! 3q  9+("6*)  =  +))PE  0'$#  ,$JrS?c0 +(+]F+ и/]T/B/EX+/+>YEX[/[>YKP+Bи/+Pи/K иBa3и3/UиV01#"&'&&55463326554&##"&5546332##"332677676776##"&5546332##"32##332 6 !,   !, F"  M , ,  p   5)##1)$  /**  9  : rocgٻU1+M9+d+e+1AиA/i/E/ /'/*/6P+=и/ и=и/ $и$/ +и+/FиF/XиX/\и\/dи=fиf/01%##32#!"&5546335#"&55463!2####"&'&&55463326554&##"&5546332##"33267767672735#h.5 95 6 P  -  !,G  UU !    /)#+)   lkC[iuW5+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]D?J+x+&+g^+Vm+Jи/J и /Vи/m0и0/?<и8n-+77+-n8>o,+2#  ## #&87()99q% +01'&'&'''&76676&#!"5543!2UO@B} Nv Q/-R1:EAs 8& "W6 ! ./,*  rA5 + и /++017463!2##"55!"5543!54&#!"&5 /)  & ") ^ ! ,sW#3+!+ ++01#!"&55463!2#!"!2#!3!2W + +7  #  **  8 3 {y#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#Zj m ?b_ !   ɛy~+ /+01#!"&5543323!2 -) ') 4sb Aq>#+ ++ иC// +2=+=$и$/=&и&/2/и//01%4332##"55#"5543##"54332"&5'"&55463676676#p '' &&10A1:}:>u9  ^nU # } !! !  + /+01##"&554332332 .( ')Cb B}?#+ ++ иD/2 + +2($и$/(&и&/2/и//(>и>/014332##"55#"5543##"54332"&55'"&55463676676#p%% ''4/92 9m:Bm6 8=%$-  # e7 "    + /+01##"&554332332 .( '(CLb Bq?#+ ++ иD// +2>+>$и$/2/и//><иv86d))[# #e NoB $3+&!// +и/012##"&55463326766##"54332 Wh9v78f((6" #NA $3+&!// +и/016##"&55463326766##"54332Wh9v78f))" #`NA $3+&!// +и/016###"&55463326766##"54332Wh>v86d))[# #Ez%! + ++01%'&&5463!2#!"676676&p>@y-- +W /k67h/ '** # e  0&! +++01'"&5463!2#!"32676676"l>>|1,"!+T  2l55f- k )** $   #- +++и/01'&&5463!2#!"3676&i9:t0-! +R  __ 5 ()/* $   P'! +! ++01'&&55463!2#!"676676%o>=z,-!!,R 0i45i/   '** #    sx4"- +++и/01%'&&5463!2#!"6762#T//`/-!!+  WM (** $ m  n,!++ +01'&&5463!2#!"3676-"!,  PC b &(** $  n!++ +01'&&5463!2#!"3676-"!,  PC B $'*&* $  nB!++ +01'&&5463!2#!"3676-"!,  MH w &'+* #  x%! +++01%#&&5463!2#!"32676676!o?@1. ,W  8r86e+(*+ $ _  s@ '+ // +01##"5!"5543!4332@)  ) % 3##- +++и/01#&&5463!2#!"676%h79s2, ,Q  __ M )*%* $   B '+ // +01##"55!"5543!54332B** %  #- +++и/01'&&5463!2#!"3676&i9:t0-! +R  __ 5 ()/* $   B '+ // +01%##"5!"5543!54332B**S $ D#- +++и/01'"&5463!2#!"676%h79s2, ,Q  __ n )) * #   )B '+ // +01##"55!"5543!54332B**7 $ sb E // и/ и!// +01##"5#"554334332##"54332' '&&X # 0}c E // и/ и!// +01##"55#"5543354332##"54332&&'' $ ic E // и/ и!// +01%##"5#"5543354332##"54332&&''J $ c E // и/ и!// +01##"55#"5543354332##"54332&&''9 $ ?sA 9+ и//+ +01##"5#"554335#"5543354332A) ) # # @ 9+ и// ++01##"55#"554335#"5543354332@( ( # [ $ @ 9+ и//+ +01%##"55#"554335#"5543354332@( ( $ p $ !@ 9+ и// ++01##"55#"554335#"5543354332@( (0 # R " {sb 'W(/!/( и /и  и!)$//++015#"5543354332##"5#"5543##"54332o (( ''R # o # /}c 'W(/!/(и/ ии!)$// ++01##"55#"554335#"5543354332##"54332&&%% $ N $ t  kb 'W(/!/(и/ ии!)$//+ +01%##"5#"554335#"5543354332##"54332% %%% # \ # +b 'W(/!/(и/ ии!)$// ++01##"55#"554335#"5543354332##"54332% %%%J # J # eL(-!++ +01#!"&5463!2#!"3!2- + +   6** !  %!++ +01#!"&55463!2#!"3!2% ,!!,   )) !  %!++ +01#!"&55463!2#!"3!2% ,!!,   **   & !++ +01#!"&55463!2#!"3!2& ,!!,   *) " x 2N!++ +01#!"&55463!2#!"3!2N ,!!,j  ` @*+ !  @s CD/ /иD?и?/E //>/++и/>и/>и//,и,//2и>9и><иY +01#!"&55463!2#!"3!2F +!!+U  J ** !  ]!++ +01#!"&55463!2#!"3!2] , ,_ V ++  q n5!++ +01#!"&55463!2#!"3!2 , ,%  D))   v!++ +01#!"&55463!2#!"3!2 , ,   **  x Jc >;4+ ++и//7*+7й*$и$/*'и'/*=1и1/=3и3/=;и;/01%##"55###"543323543326#'"&554633354332676c&z%%z&=>97 8?I(00<S " !}{v!++ +01#!"&55463!2#!"3!2 , ,   ++ !  !++ +01#!"&55463!2#!"3!2 ,!!,    *h*  O Is@ 9://:5и5/"и"/;//4%+4и/4и/%"и"/%(и4/и42и2/01##"54332267676676#"'"&5546372735463@ )) < !%+))N  N(*U'Q*,OO*)( }G   #   " -!++ +01#!"&55463!2#!"3!2- + +   ))   %!++ +01#!"&55463!2#!"3!2% ,!!,   )) !  %!++ +01#!"&55463!2#!"3!2% ,!!,   +*   %!++ +01#!"&55463!2#!"3!2% ,!!,   ))   hQ!++ +01#!"&55463!2#!"3!2Q +!!+a W v++ "  Is@ ?s@// и@и/<A// +(;+;и/(%и%/(+и+/01##"55#"554334332"&5####"&554632636676676676#@ ) ) )*+(@R+)O!R))J  DOFF " 3" !  T!++ +01#!"&55463!2#!"3!2T + +U  I **  b ]!++ +01#!"&55463!2#!"3!2] , ,_ V () !  ]!++ +01#!"&55463!2#!"3!2] , ,_ V **  e np!++ +01#!"&55463!2#!"3!2 , ,%  ~** !  !++ +01#!"&55463!2#!"3!2 ,!!,   **  h !++ +01#!"&55463!2#!"3!2 ,!!,   *+ t !++ +01#!"&55463!2#!"3!2 ,!!,    *Y*  @ Is@ :y;//;и/7<// +и/ и/ #и#/4и4/6и6/01##"54332"&5'"&554632676676676672#@ )) *V+LR+)O!U*(J  $M(')} & # " %!++ +01#!"&55463!2#!"3!2% ,!!,   ** !  Is@ $0W%*+%2-/'/+ и /и/и/и/01%6'"&5546326726766766##"54332 N(*U'S*+NR+)O!X*+N )) !  " }n?k@//и/@(и(/7+ +%и ,и2и:и=и=/01%#"&546332##"32676'&&546332##"3676 g{)!+ 6h*  &O$,#!,  !C#'!+ ! _  .$*+ ! i q=@_A//и/A)и)/8+ +&и -и3и;01#"&546332##"32676'&&546332##"676 1q>), 6f+ 'M#- +  !Gi ) * !  /$** !  q@kA//и/A)и)/8+ +#и#/&и -и3и;01#"&546332##"32676'&&546332##"676 1q>), 6f+ 'M#- +  !G@ (!,* "  /%*#* "  qM@kA//и/A)и)/8+ +#и#/&и -и3и;01#"&546332##"32676'&&5546332##"676 1q>), 6f+ 'M#- +  !Gx (!* !   .%)* !  gS AoB//и/B*и*/9+ +$и$/'и .и4и?и?/01%&546332##"32676'&&546332##"676Q+S+* +|q  "M#  H,! +y o 9 #** ! g  ''** ! n r6J@_A/ /и/A)и)/8++&и-и3и;01'&&546332##"76'&&546332##"676H&L1*,z oGD@," -m d 4T ' * !  "'* * !  rJ@EA/ /и/A)и)/8+-и301#'&&546332##"376'&&546332##"3676H&L1*,z oGD@," -m d 47 &"&* !  "&** !  rMJ@_A/ /и/A)и)/8++&и-и3и;01'&&546332##"76'&&5546332##"676H&L1*,z oGD@," -m d 4l %!* "  ! %** "  y@kA//и/A)и)/8+ +&и -и3и;и>и>/01%#"&546332##"32676'&&546332##"3676 2r?)!+ 6h* 'O#+#.  "G &"* ^  -%** i ?sA '+ // +01##"5#"554334332A) ) % 3<A '+ // +01##"55#"5543354332A) )  $ {@kA//и/A)и)/8+ +#и#/&и -и3и;01#"&546332##"32676'&&546332##"676 1p?)!+6g* &M$,! ,   F@ (!,* "  /%*#* "  QA '+ // +01%##"5#"5543354332A**E $ {Q@_A//и/A)и)/8+ +&и -и3и;01#"&546332##"32676'&&5546332##"676 1p?)!+6g* &M$,! ,   F} ) *   .$**  P&A '+ // +01##"55#"5543354332A**4 % BsA 9+ и//+ +01##"5#"554335#"5543354332A** # # WA E + и и// ++01##"55#"554335#"5543354332A) ) # \" ^A E + и и//+ +01%##"55#"554335#"5543354332A)) # o # H(@ E + и и// ++01##"55#"554335#"5543354332@ ) ) 6 # H " x,B <c=/'/=и/'6:#+ +#и/:и/ +и101'&&546332##"676##"&546332##"332 6r;- +  4h, e,, O &+* !    1**   ;# +/ + 01%546332!2#!"&55463*  O ""::c;/%/;и/%48!+ +!и/8и/ )и/01'&&5546332##"76##"&5546332##"332 gr/ ,  eY ] ,,   '** !   /*)   ;# ++ 01546332!2#!"&55463+  ;` `!!::c;/%/;и/%48!+ +!и/8и/ )и/01'&&5546332##"76##"&5546332##"332 gr/ ,  eY ] ,,   ')* !   1)*   ;# ++ 01546332!2#!"&55463+  #V V""::c;/%/;и/%48!+ +!и/8и/ )и/01'&&5546332##"376##"&5546332##"332 gr/ ,  eY ] ,,   '*|* ! q  0*)  q ;;#++0154332!2#!"&55463(  wOO! !Z9{">o?/)/? и /)8<%++%и/%и/<и/-и301'&&5546332##"32676##"&5546332##"332V (--"!+  #H* , ,   ]&*+  2 *+   @s DE/ /иE@и@/F //D/++/>и/>и//,и,//2и>9и><и@и@/01####"5433232267676676#"'"&55463727275463 (( (''L#N'(P$K))MJ(('IE}y^  $   "};c//>&и&/5+ +#и *и0и8и;и;/01##"&5546332##"332'&&5546332##"676 .!!+   "I$-" +  !//>&и&/5+ +#и *и0и8и;и;/01##"&5546332##"332'&&5546332##"676s .!!+  !K$,#"+  =! )*o* ! d $*o* ! d H( 3_4/-/4и/-(и(50/*/ +&+ и/012##"&5546335433276732####"54332 Qc ) Fh؋  )) }" #ff" Q=;[+5&+&.ܸ и //"/ +"9и/ *и001#&&5546332##"3676##"&5546332##"332( A!,! +lb 3 + +x  Z)(* "   *()   @sb ? ;+ ++и//?0++0; и /0-и-/;7и7/;9и9/01##"5###"5433234332267676'"&55463275463b'o&&o'2(6; 97 *)*< ҹ A " "^[Y;_+5&+&.ܸ и /8#+ +#и8и/ *и001#&&5546332##"32676##"&5546332##"332$ 5!,!!+aV & ,,| m t (** " {  * **  } Y;c+5&+&.ܸ и /9"+ +"и/9и/ *и001'&&5546332##"3676##"&5546332##"332$ 5!,!!+aV & ,,| m t (*w* l  *)*  l J c ?<5+ ++и//9*+> +9и/* и*'и'/>1и1/>3и3/>5и5/><и97 8?'$(.-:͹n # #xvIs@ 9://:5и5/;//9%+%4и/4и/%"и"/%(и4/и42и2/01##"54332267676676#"'"&5546372735463@ )) E"+))N  N(*U'Q*,OO*)(}   #   " NA +Y,/%/,и/% -(/"/ + и/и/016##"&55463!543326766##"54332Yf' &'6f))A##kjPN]A +Y,//,и/'-//"+'и'/)и)/01%##"543326##"&554633543322766A))Wh( ,*8fk#! #o mNA +Y,/%/,и/% -(/"/ + и/и/012##"&55463!543326766##"54332Wh' &%8f))z" #dcS;#I$//$и// /+и012354633232#!"&5546335463y)  "";#g$/ /$и/  EX/>YEX/>Yܹи и015463323546332!2#!"&55463K*)   =  " ";#M$/ /$и/  +ии 015463323546332!2#!"&55463K*)   )  ##;;#M$/ /$и/  +ии 015463323546332!2#!"&55463F*+  w\ \\ \! !B <c=/'/=и/'6:#+ +#и/:и/ +и101'&&5546332##"676##"&5546332##"332 6r;- +  4h, e,,  &+) !   2 ++   ::c;/%/;и/%48!+ +!и/8и/ )и/01'&&5546332##"76##"&5546332##"332 gr/ ,  eY ] ,,   '*) !   0*)   ::c;/%/;и/%48!+ +!и/8и/ )и/01'&&5546332##"376##"&5546332##"332 gr/ ,  eY ] ,,   '** !   0*)   ::g;//;#и#/2/ / + и/  'и-и5и5/01##"&5546332##"332'&&5546332##"732: ,,    gr/ ,  eY **   '** !   o`">c?/)/? и /)8<%++%и/<и/-и301'&&5546332##"3676##"&5546332##"332k (--"!+  #G* , ,   '*(  /**   ;g//>&и&/5+ +#и *и0и8и;и;/01##"&5546332##"332'&&5546332##"676 .!!+   "I$-" +  !d#  m"   " ;gY+ 01##32####"5433232  * * Mc!a M+ ܸиии!// ++01##32####"5433232  * * $y",Pl3Y4/ /4и/'и/и/ )++"+017&&55463326554&#!"&55463!2##"3676,#,! #-`_[V!*+& * ")sA '+ // +01##"5#"554334332A* * % ET6e7/ /7и/%и/и/ '++ +'*и*/01&&55463326554&#!"5543!2##"32676676-#,  #-.V)(Q+ T"+u)[" n+d "[3Y4/ /4и/%и/и/ '++ +017"&55463326554&#!"5543!2##"36676,#-   $-aR X#(*l $  + w ! ?T9q:/ /:и/%и/и/ '++ +'*и*/7и7/01&&55463326554&#!"5543!2##"32676676-#,  #-.T)'T+ 0W+,Y@!*f*J #  `+T   <#A '+ // +01##"55#"5543354332A) )1 $ sc E // и/ и!// +01##"5#"554334332##"54332&rr&((X $ B}c E // и/ и!// +01##"55#"5543354332##"54332&{ {&&&  % ic E // и/ и!// +01%##"5#"5543354332##"54332&{ {&&&9 $ c E // и/ и!// +01##"55#"5543354332##"54332&{ {&&&3 $ :c E // и/ и!// +01##"55#"5543354332##"54332&{ {&&&  $ 7sA 9+ и//+ +01##"5#"554335#"5543354332A* * # # AB 9+ и// ++01##"55#"554335#"5543354332B) )- # e # jsc 'W+!+иии)$//++015#"5543354332##"5#"5543##"54332px x%%x k&&C # ` # >}c 'h+!+иии)$//EX/>Y+015#"5543354332##"55#"5543##"54332px x%%x k&&j $ o& # \c 'W+!+иии)$//++015#"5543354332##"55#"5543##"54332px x%%x k&&q #  $ c 'W +!+ и ии)/$/ ++01##"55#"554335#"5543354332##"54332&o oo o&&&+ $ [ # f1 b 'W +!+ и ии)$// ++01##"55#"554335#"5543354332##"54332' ffff '&&  # g " y:2e3//3и/+и/и#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2:/". "->")},g '|+ k +2e3//#и/3и/и+#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2+/#.  $-X#*E*6$ I*9 ;#++0154332!2#!"&55463(  !mm""j# + /+017463!2##"554&#!"&5 /)  #)j +M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5 /$ .$  #)&#)gt%;6DcC <2B z  #C/*   '  p7>'  8p # ,2#3!* +2e3//#и/3и/и+#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2+/#.  $-X#(8*(%9*+ ;'#++0154332!2#!"&55463(  b]]""ee<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+6$+aY+H?+N+$!и!/62и2/01#"&'&&54676632#"#"&554332336276676%#!"&55463!24&#"326##"&554332I:$$99$$:   %J0*B" &  g k ]1--00--1  ****'Z#)  $ (''' " {)!++ +01#!"&55463!2#!"3!2) ,,   w++ !  {42e3//3и/и/#+#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!24 0#,  $,R  y#)9+($8)- p-CY5+-+LQ+!и!/L[/ /N/?O+HU+0+O и /Uи/H%и?AиA/01##"&'&&55463326554&##"&5546332##"3326366767246332##"54&##"&5 ! &P  ,   !,E%   .$ q =*- # ;)0  #*r5XhK'+C/+\+ d+'7и7/ j/;/a+<3+,F+<и/и/ иaNи3YиY/01%46332##"&'&&5###"&'&&55463326554&##"&5546332##"33276676727"3326554&#+, &R",   !-m  x y )* )D)3$A)6    j(_iQ-+I5+a++aи-=и=/hиk //A//d&+B9+2L+`+B и /Bи/&$и$/dTиT/01"&'&&5463323546332#'####"&'&&55463326554&##"&5546332##"332676676727332655U'( ,  o!+~   - C"   ] n  &nl'%  E*3# A+6  qqgBl6+.+.и/"и"/-Bи.OQ/: +'+1+ и/:<и*3  =  6*'\D /(&#,  5$Sq=Bf3+++`I+и/`W//E/cF+NS+Y^+F иYи/Sи/N#и^.и./c6и6/c;и;/01"##"&'&&55463326554&##"&5546332##"332677676776##"&5546332##"32##3326 #N!,   ,G"  M , ,  q  C)1#?(2 .)(  H  I rbbfٻU1+M9+c+d+1AиA/h/E/ /'/*/6P+=и/ и=и/ $и$/ +и+/FиF/XиX/[и[/cи=eиe/01%##32#!"&5546335#"&55463!2###"&'&&55463326554&##"&5546332##"3327767672735#[17 :5<  'Q"+  - o  [[ !   D)3$A)6  e]C[iu55+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DsJ+9 +x+&+g^+V0+ и/9<и)1  )***' % (''' $ {%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5"  , +? b w w ** { &o'//'и/ и /ии#&и&/#(// ++01%3!2655"&'&&5546332!546332#  7 ** +"S S YW%f:DG<++<и%ܸC'/?+;+01"&'&&55463323546332#7''&676'432'&'%332655&' , , {00]   YS'  a #!|5 3`\c[[m^)'/01%'&'&'''&67667667632 ,q ZC$-/}JCq)(1 1B[ "#C0;/()C $ 9$$O' jp{< //01''&6766'43276674632'&'&''&'&'dY 0  @8B;0  -,L e0 -]S+ 5 " )jD 2X*^= 901,; A6 (9 Cn);˸38+"+01'&&'&'&'&''&676676676&#!"5543!22#!"5543W'c341 HEDw,K (.9R '**V%  1!   !+ $   %"  Sk5 + и /++017463!2##"55!"5543!54&#!"&5 /)   #)c " 8{(#3+!+ ++01#!"&55463!2#!"!2#!3!2( --     w**  : @ |?#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#+p| v ]Nx "   S1/=K L//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2:;;:E( S**T"(("S**S! (\ _ F& '( '~ KX2Y3//3и/и#+#/и///++ &+01#!"&55463!26554&#!"&55463!2#!"3!2X 0#.  d #,l #*i+T 'j+Y Q2]3//3и/+и/и#/и///++ &+01#!"&55463!26554&#!"&55463!2#!"3!2Q.#- N #,V$*6+#$ 7)( J 4x5// и5)и)/1 6/ /EX/>Y, + )1и1/)3и3/0132####"543326##"&55463354332676  * * Qd ) "$h!@" $nmt`2]3//3и/+и/и#/и///++ &+01#!"&55463!26554&#!"&55463!2#!"3!2` .$- [ #,d #)A+,%A)2Hc 4g5/ /и5)и)/16/ /, ++ )1и1/)3и3/01####"54332326##"&55463354332676  ))   Qc ' %#ho|" #K+ +01#!"&5543323!2K . )  n#*p2n3//3и/+и/и#/и//EX./.>Y+ &+.01#!"&55463!26554&#!"&55463!2#!"3!2p .#- [ #- d $*7+#$ 6*' H 3_4/-/4и/-(и(50/*/ +&+ и/016##"&5546335433276732####"54332 Qc ) Fh؋  )) [" #~"lX<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+<$+aY+N+H?+$и/$!и!/<2и2/<6и6/01#"&'&&54676632#"##"&554332336276676%#!"&55463!24&#"3267##"&554332=:#$::$#:   &U/)O" '  f l ]0,-11-,0  7!  "#  #F$)  #  # q32q3//#и/3и/+и/и/#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!23/#. #-\#*/* !/)#p*E[7+/+NS+#и#/N] / /P/EX2/2>Y@Q+(+Q и /2@:и:/@<иY`+90+9и/и/иC(`Kи`PиP/0XиX/01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"3326776766767"3326554&#, +5 s",  ",E  x t s+**   4)" #1' #    k_iԻQ-+I5+a++aи-=и=/hиk //A//&/EXL/L>YB9+`+B и /Bи/&и/&$и$/L1&dTиT/01"&'&&5463323546332#'###"&'&&55463326554&##"&5546332##"332676676727332655M'',! o!+~ !,D#  \ n  _]'%  6*$#2+'  oaaiBl5+-+-и/!и!/,Bи-OQ/EX0/0>YEXL/L>YEXZ/Z>Y9 +&+ и/09<иYEX^/^>YNS+E и /.Sи/N#иEd6иXиY01##"&'&&55463326554&##"&5546332##"332676676636##"&5546332##"32##3326 $N",   ,F"  M , ,  q   9*)#7)+  /++ ?  @ r^bfͻT0+L8+c+d+0@и@/h /'/)//D/5O+<и/ и<и/ *и*/EиE/WиW/[и[/cи<eиe/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33263667672735#W07 96 8 r!, "+ F  VV    ;)* #8' +  lZC[iuK5+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DEX0/0>YEXm/m>Y9J+x+&+g^+J и /mm1и1/mVJs01###"&'&&55463326554&##"&5546332##"33267667672%#"&'&&546766327#!"&55463!24&#"3267##"&554332   #P!,  !,G!  [:$$::$$: W S1,.00.,1  p6)%#3*'  !  "#  #p #  # g9Cz;++,%+;и,*и*/8%,9B'/EX:/:>Y>+:8:901"&'&&55463323546332#7''&6767432'&'%332655  ''- , &t80]   NI'   N  'x& $neZFFH 4g5/ /и5)и)/16 //, ++ )1и1/)3и3/01####"54332326##"&55463354332676  ))   Qc ' %#h /" #fex+(/01%'&'&'&''&7667665438 C(&W-xXG4!;W } /   6" 8-B5+E#$oNf2>38+"+01'&&'&'&'&''&676676676&#!"5543!22#!"5543{(c341 IFDw+K).9Q(*)V'  1"  !* #   %!  Lq4#3+!+ ++01#!"&55463!2#!"!2#!3!24 + +7  #  *)  ; " 5  2n3//3и/+и/и#/и//EX/ >Y/+ &+01#!"&55463326554&#!"&55463!2##"3!2 0#-   #," !+`+J% _+O@sb A =+ ++и//A/++/= и /="и"//2и=9и9/=;и;/01##"5###"5433234332267676"'"&554633275463b'o&&o' ";@#< @#9w:2:8=QDk   # " u2]3//3и/+и/и#/и///++ &+01#!"&55463326554&#!"&55463!2##"3!2 /#.  #- #+5+"$7*&uo2]3//3и/+и/и#/и///++ &+01#!"&55463326554&#!"&55463!2##"3!2 /#.  #- z#*;+'%<* + Jkc>;4+ ++и//7*++*'и'/*;1и1/;3и3/;=и=/01%##"5###"54332343326#'"&554633354332676c&z%%z&=>97 8?K(/.Y+ &+.01#!"&55463326554&#!"&55463!2##"3!2 /#.  #- !*2+%2) " Jc >;4+ ++и/'/*//+*и/*$и$/*=1и1/=3и3/*7ܸ=;и;/01##"55###"543323543326#'"&554633354332676c&z%%z&=>97 8?K(/.< ` #  fdf%1&+++01'&'&''&67$76&#!"5543!22#!"5543$K/('Tt} '&& &Y#  !  .QA ,Y-/&/-и/&!.)/#/ +и/и/012676676###"&5546335463##"54332,*5`* @| **s! !t$N[A +Y,//,и/'-//"+'и'/)и)/01%##"543326##"&55463!543326766A))Wh' &%8fi" $v uNA +Y,/%/,и/% -(/"/ + и/и/016##"&55463!543326766##"54332Wh' &%8f))[" "{zQ_3AMZZ&+H +B+A ]A ) 9 I Y i y ]ABB]AB)B9BIBYBiByBBBBBBB ]EXE/E>YK+*+XP+?6+E A ' 7 G W g w ]A ]и/и/*-и-/01#"&'&&54632##"&554332332672676%#!"&55463!24&#"326##"&554332]WI#;VGIW   (Y/) R% '  ~ s d0.,22,.0  7,>%.<Y/++& 01#!"&55463!26554&#!"&55463!2#!"3!2U -#.  #-Y  #+5*$"7))pAE[7+/+NS+#и#/N] / /P/EX2/2>Y@Q+(+Q и /2@:и:/@<и*+Y+ a+"2и g/6/EXA/A>Y^+7.+7и/иA&^Iи^NиN/.VиV/01%46332##"&'&&5#"&'&&55463326554&##"&5546332##"3326776766767"3326554&# ,,6",   !, F! x s v* *)  5)## 1($    k@_iԻQ-+I5+a++aи-=и=/hиk //A//&/EXL/L>YB9+`+B и /Bи/&и/&$и$/L1&dTиT/01"&'&&5463323546332#'###"&'&&55463326554&##"&5546332##"332676676727332655m'&,! p  ,}  -~C#  ] n  _]'%  6*$#2+'  oaafB`6+.+.и/"и"/-Bи.KM/: +'+1+ и/:<иYEX[/[>YKP+Bи/+Pи/K иBa3и3/UиV01#"&'&&55463326554&##"&5546332##"332677676776##"&5546332##"32##332 6 !,   !, F"  M , ,  p   5)##1)$  /**  9  : qocgU1+M9+d+e+1AиA/i/E/ /'/*/EXP/P>Y=и/ и=и/ +и+/P5FиF/XиX/\и\/_и_/dи=fиf/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33263667672735#h.5 95 6 P  -  !,G  UU    6*& # 4))  lsC[iuY5+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]D?J+x+&+m+g^+Jи/J и /m0и0/?<и #)z+2e3//#и/3и/и+#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2+/#.  $-X#*H*:$ K*; ;$## + /+012#!##"&55!"&55463 +s #! !>+ /+01#!"&5543323!2> .'  m&*tq"KQ<5+B2++2)и)/2,и,/2/и//B?и?/01'&'''&67676&##"554332""#"&#&&5543323232676676T =:E_W <7; z  #$ /*   '  p/7(! 4T # 8()"* +2e3//#и/3и/и+#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2+/#.  $-X#*2*#%5*& ;o# + /+012#!##"&55!"&55463 x+ | o"] ]"|)!++ +01#!"&55463!2#!"3!2) ,,   v*}*  ` {%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5"  , +? D Y Y *w* | &o'//'и/ и /ии#&и&/#(// ++01%3!2655"&'&&5546332!546332#  7 ** +BB FD%n_%/01'&'&'''&67665432N V?"*,S .  +r%?.5*$(B $ )K  @Q lp~; //01''&6766'43276674632'&'&''&'&'eX 0 @8A</  YL d1 -]T+ 5 " )jD 2X*^= :b,; A6 (9 Cj)/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+014&'&&#"3267667#"&'&&546766326'%QQ%&55&%QR&%6IA.0l-+m//AA//m+-l1.@'  ' %  %!/::/,<<tn! +01'&'&''&63$76&#!"5543!2_ DG ņ>Ey r -7 ,T # d# ;n# + /+012#!##"&55!"&55463 x+ | n"c c"fc2>38+"+01'&&'&'&'&''&676676676&#!"5543!22#!"5543R(c341HFDw+K).9R (**V&  1!   !+ $   %"  Sk5 + и /++017463!2##"55!"5543!54&#!"&5 /)   ~#)Y " -|?o#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#+p| v ]NA } ! } }Ea2e3//#и/3и/+и/и#/и///++ &+01#!"&55463!26554&#!"&55463!2#!"3!2a .#-  ] #.g P"*S+>%R+B Is@ @A// иAи/=B// +)<+<и/<и/)&и&/),и,/01##"55#"554334332"&5##"&554632636676676676#@ ) ) ))*+'AR+)O"T()I  GPB? " ! " Q2a3//#и/3и/+и/и#//++ &+01#!"&55463326554&#!"&55463!2##"3!2Q.#.  G #,H#)4+ '3+!MA @A// иAи/=B//@+ +&+и/и/&)и)//,и,//:<и #)]2e3//#и/3 и /+и/ и#/и///++&+01#!"&'&&55463!26554&#!"&55463!2#!"3!2]  #, P/ $,O  6+" &+7+&M^A @A// иAи/=B// +)+/:+и/и/)&и&/),и,/<и11AEs 5 )b $ -.%   r@5 + и /++017463!2##"55!"5543!54&#!"&5 0(   ")^ ,|y#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#Zj m ?bZ "   •m[2a3//#и3и/+и/и#/и///++ &+01#!"&55463326554&#!"&55463!2##"3!2 0#.   " #-" h#+C+/% C+2 4sb Aq>#+ ++ иC// +2=+=$и$/=&и&/2/и//01%4332##"55#"5543##"54332"&5'"&55463676672#p '' &&10A1:}:>u9  ^n_ " } " "  2r3//#и/3и/+и/и#/EX./.>Y+ &+.01#!"&55463326554&##"&55463!2##"3!2 /#,  #- $*(+ %)*  Cb B?#+ ++ иD/2 + +2($и$/(&и&/2/и//2<(>и>/014332##"55#"5543##"54332"&55'"&554632676676#p%% ''4/92 9m:Bm6 8=%$ " W < q"    ujc# + /+017463!2##"554&#!"&5 0) {$)pc{+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5M 0$ /$  o#)$#)g23//A]A)9IYiy ]  и / 93*и*/% 4/!.+01&76746332'&'&'46332##"554&##"&5  . /r o2-i  0$  x ]"( /d[A#*|2a3//#и/3и/+и/и#//++ &+01#!"&55463326554&#!"&55463!2##"3!2 /#-     #-  #*7,#%7+ 'C?b B?#+ ++ иD// +2(+($и$/(&и&/2/и//2<(>и>/01%4332##"55#"5543##"54332"&55'"&55463276676#p%%  ''  5/839o9At8:>%%{C !  {"  } nd+ +01#!"&5543323!2n 0*  m$)u"KM<5+B,++,/и//,2иB?и?/BEиE/01'&'''&67676&##"554332""#"&#"&5543323232636676 ?8C`K <4B {  "- / * * &  o5<(  9e " -2-$) 2a3//#и/3и/+и/и#//++ &+01#!"&55463326554&##"&55463!2##"3!2 .#.    #- #(&+ %%+Cb Bs?#+ ++ иD/2 + +(и(/$и$/&и&/2/и//014332##"55#"5543##"54332"&55'"&554632676676#t&& ''6292 9m:Bs7 :?##= # L  ^ "   b l{<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+<$+aY+N+H?+$и/$!и!/<2и2/<6и6/01#"&'&&54676632#"##"&554332336276676%#!"&55463!24&#"3267##"&554332_:#$::$#:  %U/)O" '  f l ]0,-11-,0  7!  "#  #F$)  #  # |k!++ +01#!"&55463!2#!"3!2k ,,   v)* ! a rx2z3//3и/+и/и/#/и//#4EX&/&>Y/++& 01#!"&55463!26554&#!"&55463!2#!"3!2x /#,   #-: #)6*#/** pcCY7+/+LQ+#и#/L[ / /N/;O+HU+2+O и /Uи/H'и;>и>/01###&&'&&55463326554&##"&5546332##"32636676746332##"554##"&5?   %R!,  !,F" 2 0$  q 1*!"/)# $) qcWgI%+A-+[+ c+%5и i`+:1+*D+:и/и/и`Lи`RиR/1X01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"332766366367"3326554&#B +,6 $O",   !-  h  w t a( * )  (*# &)    ib`jûR.+J6+b++bи.>и>/iиl //B//e'+C:+3M+a+C и /Cи/'$и$/eUиU/eYиY/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"332636676327332655  '( ,! #J +    ,C#  ] n  \\&& 3*"$ 0) $  n``iBl5+-+-и/!и!/,Bи-OQ/EX0/0>YEXL/L>YEXZ/Z>Y9 +&+ и/09<и 3q  9+("6*)  =  +))PE  0'$#  ,$JpuAe3+++_H+и/_V//D/MR+W^+D и /Wи/Rи/M#и^.иDc6и6/01##"&'&&55463326554&##"&5546332##"33276676636##"&5546332##"32##332-6 #O",   -i   L , ,  q  /* # -)"  /** @  7 rocgU1+M9+d+e+1AиA/i/E/ /'/*/EXP/P>Y=и/ и=и/ $и$/ +и+/P5FиF/XиX/\и\/dи=fиf/01%##32#!"&5546335#"&55463!2##"##"&'&&55463326554&##"&5546332##"33267767672735#h.5 95 6 P  -  !,G  UU !   5)$#2)'  lqC[iuW5+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]D?J+x+&+g^+Vm+Jи/J и /Vи/m0и0/?<и=} # "  MA +78/1/8и/(1,94/++и/ ии/и/и/%'и'/01%"&55##"'"&554632632672776676#%##"54332c'*+NN*+N$'N'H @NDF)) r#  v +6PJA 78//8и/9//5+/"+5и/"и/" и /"%и%//,и,//2и2/01%##"543326##"&55'"&5546326766766766A**DP:8*()*J L**L!O('MXS   !PA +7s8/1/8и/(1,94/++и/и/ и%'и'/01%"&55"#"'"&55463272636676676#%##"54332f &)*N O*)L$N((JGPB?** W# [ . ;z#I$//$ и / / /+и01"&5###"&5#"&55463!2###s * Q   ## ;2#M$//$ и / #+и# и01%"&55###"&55#"&55463!2###s +  Q  [[ [!![ ;1##M$//$ и / #+и# и01%"&55###"&55!"&55463!2###v * Q 1  !! ;o#M$//$ и / #+и# и01%"&55###"&55#"&55463!2###s +  Q  RR R""R ;o#M$//$ и / #+и# и01%"&55!##"&55#"&55463!2###+ Q  VV V##V ;1 +012#!"&55463  1# #;  +012#!"&55463   # #;(d +012#!"&55463  d##I@ +O+//%+и/и/и/%)и)/01%##"543326#"##"&55463326766@ )) JJ &4;D%J#I  "NZA $3+&!// +и/016###"&55463326766##"54332 Wh9w99i))# $N A $3+&!// +и/016##"&55463326766##"54332Wh9v78f))s" #UI@ )C+//$+и/и/$'и'/01%##"543322#"##"&55463!2766@ )) JJ &4 JGM ! A +//01##"54332A))0A +//01##"54332A))?A +//01%##"54332A))|z;%g&/ /&и/и/ и/  и/и/++01"3!2654&#463!2#!"&'&&5  !,%  -  d  * F* 32%g&/ /&и/и/ и/  и/и/++01"332654&#463!2#!"&'&&5  !+  +   * * 2%g&/ /&и/и/ и/  и/и/++01"332654&#463!2#!"&'&&5  !+  +   **I2%g&/ /&и/и/ и/  и/и/++01"3326554&#463!2#!"&'&&5  !+  +   *) j~%g&/ /&и/и/ и/  и/и/++01"332654&#46332##"&'&&5  ,  , m  + O+ n4%g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5 ,  ,    * + n%g&/ /&и/и/ и/  и/и/++01"332654&#46332##"&'&&5 ,  ,    ) * nL%g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5 ,  ,    * *z7%g&/ /&и/и/ и/  и/и/++01"332654&#463!2#!"&'&&5  !+ !+  d  * F* #sA '+ // +01##"5#"554334332A) ) $ Z.7%g&/ /&и/и/ и/  и/и/++01"332654&#463!2#!"&'&&5  +  ,   ** B '+ // +01##"55#"5543354332B* * # 7%g&/ /&и/и/ и/  и/и/++01"332654&#463!2#!"&'&&5  +  ,   ** #A '+ // +01%##"5#"5543354332A))? # I7%g&/ /&и/и/ и/  и/и/++01"3326554&#463!2#!"&'&&5  +  ,   *) %B '+ // +01##"55#"5543354332B* *3 $ sc !E"//"и/и#//+01##"5#"&5546334332##"54332% %&&X  7}b E // и/ и!// +01##"55#"5543354332##"54332( ('' $  ib !E"//"и/и#//+01%##"5#"&55463354332##"54332(   (''1 b E // и/ и!// +01##"55#"5543354332##"54332( (''9 % ?b E // и/ и!// +01##"55#"5543354332##"54332( ('' $  sB 9+ и//+ +01##"5#"554335#"5543354332B* * # # @ 9+ и// ++01##"55#"554335#"5543354332@( ( ~ $ u # @ 9+ и//+ +01%##"55#"554335#"5543354332@( ( # $ )@ 9+ и// ++01##"55#"554335#"5543354332@( (7n $ j # zsc +W + %+и ии -(/"/++01##"5#"&5546335#"&55463354332##"54332%  %&&XC  }b 'W +!+ и ии)$// ++01##"55#"554335#"5543354332##"54332 ( ( ''r " s # ib'W +!+ и ии)/$/+ +01%##"55#"554335#"5543354332#"54336 ( ( '' " u # c 'W+!+иии)$//++015#"5543354332##"55#"5543##"54332p && z&&U # [ F # c 'W+!+иии)$//++015#"5543354332##"55#"5543##"54332p && z&&[ # h& " (&%o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5  !, --    * * %o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5'  ,  ,F    )) %o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5'  ,  ,F    **  %o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5'  ,  ,F    ( ) ;>#++0154332!2#!"&55463(  zi i##0E%g&/ /&и/и/ и/  и/и/++01"3!26554&#463!2#!"&'&&5  !+) !+ x   ** ;%g&/ /&и/и/ и/  и/и/++01"3326554&#463!2#!"&'&&5  !+ +    )* ;%g&/ /&и/и/ и/  и/и/++01"3326554&#463!2#!"&'&&5  !+ +    )( Z%g&/ /&и/и/ и/  и/и/++01"3326554&#463!2#!"&'&&5  ,   ,   *)m1%g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5   ,  , k   **%g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5  ,  ,  n n ) ) %g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5  ,  ,    ** %g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5  ,  ,  n n ) * ;<#M$/ /$и/  +ии 01546332354633232#!"&55463F**  wmmmm!!&%o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5  !, --    ** %o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5'   !,  ,G    ** %o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5'   !,  ,G    * +%o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5'   !,  ,G    )) hL%g&/ /&и/и/ и/  и/и/++01"3!26554&#463!2#!"&'&&5  !,!  -    + + Q%g&/ /&и/и/ и/  и/и/++01"3326554&#463!2#!"&'&&5   , ,  p p ++ Q%g&/ /&и/и/ и/  и/и/++01"3326554&#463!2#!"&'&&5   , ,    ) ) Q%g&/ /&и/и/ и/  и/и/++01"3326554&#463!2#!"&'&&5   , ,  ^ ^ *|*mp%g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5  "+ !,    * * %g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5  "+ !+ q q ** %g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5  "+ !+ } } +) %g&/ /&и/и/ и/  и/и/++01"3326554&#46332##"&'&&5  "+ !+ U U *r* %o&/ /&и/и/ и/  и/и/'++01"3!26554&#463!2#!"&'&&5'  ,  ,F    *) ;F +012#!"&55463  " "~y. &['//'и/ и /ии#// ++01332655"&'&&546332!546332# ,)+ ,   &33 &g'//'и/ и /ии#&и&/// ++01332655"&'&&546332!546332#  +"+!+   h% 2 &g'//'и/ и /ии#&и&/// ++01332655"&'&&546332!546332# * + + q'N3 &g'//'и/ и /ии#&и&/// ++01332655"&'&&546332!546332#  +"+!+z z ]&j| &l'//'и/ и /ии#//EX/>Y +01332655"&'&&5463323546332#  ++ , 'n: &['//'и/ и /ии#// ++01332655"&'&&5463323546332#    ++ , a&n &['//'и/ и /ии#// ++01332655"&'&&5463323546332#    ++ ,  v  %nK &['//'и/ и /ии#// ++01332655"&'&&5463323546332#    ++ ,{ { R'y7 &['//'и/ и /ии#// ++01332655"&'&&546332!546332#  +,+ !+   &37 &['//'и/ и /ии#// ++01332655"&'&&546332!546332#  *"* ,   h% 7 &['//'и/ и /ии#// ++01332655"&'&&546332!546332#  *"* , q'N7 &['//'и/ и /ии#// ++01332655"&'&&546332!546332#  *"* ,z z ]&c !E"//"и/и#//+01##"55#"&55463354332##"54332% %&&6  <!c 'W+!+иии)/$/++015#"5543354332##"55#"5543##"54332p && z&&} # dQ Z # $ &o'//'и/ и /ии#&и&/#(// ++013!2655"&'&&546332!546332#  ( +* +  v% &_'//'и/ и /ии#(// ++013!2655"&'&&546332!546332#  B ++,Nb b rq';# ++ 01546332!2#!"&55463+  6zz""} &_'//'и/ и /ии#(// ++013!2655"&'&&546332!546332#  B ++,8h h %wu& &_'//'и/ и /ии#(// ++013!2655"&'&&5546332!546332#  B ++,lN N  ^] &E &['//'и/ и /ии#// ++013!2655"&'&&546332!546332#     +9+!+  W%; &g'//'и/ и /ии#&и&/// ++01332655"&'&&546332!546332# *!* +GZ Z  ji'; &g'//'и/ и /ии#&и&/// ++01332655"&'&&546332!546332# *!* +8__ lk%Z &g'//'и/ и /ии#&и&/// ++013!2655"&'&&5546332!546332# )/* ,^K K [Y&n! &g'//'и/ и /ии#&и&/// ++01332655"&'&&5463323546332# ,,-v v L&v &u'//'и/ии#//EX/>YEX#/#>Y+ &01332655"&'&&55463323546332#   ))-GS S  ba %v} &O'//'и/ии#// ++01332655"&'&&5463323546332#    *)-*Z Z  kj'v &O'//'и/ии#// ++01332655"&'&&55463323546332#   ))-RPP ]] ';M // и/+ и и01354332!2#!"&55463354332 (  '6kk""ky$ &o'//'и/ и /ии#&и&/#(// ++013!2655"&'&&546332!546332#  ( +* +=r r =& &_'//'и/ и /ии#(// ++013!2655"&'&&546332!546332#  B ++,\VV ed& &_'//'и/ и /ии#(// ++013!2655"&'&&546332!546332#  B ++,Pbb ff% &_'//'и/ и /ии#(// ++013!2655"&'&&5546332!546332#  B ++,tS S  WV &gL &['//'и/ и /ии#// ++013!2655"&'&&546332!546332#    +2, -!h h (xv(Q &l'//'и/ и /ии#//EX/ >Y +01332655"&'&&5546332!546332#   +)+ ,e= = MK&V &g'//'и/ и /ии#&и&/// ++013!2655"&'&&5546332!546332#*.*!+AS S db'Q &['//'и/ и /ии#// ++01332655"&'&&5546332!546332#   +)+ ,o==KI&MA ?@// и@и/<A/.?+ +.и/и/(%и%/(+и+/;и;/01%##"55#"554334332"&55##'"&554633276676676676#A) )"'**OC'*+N"N&(J CPDA  "  y!# } mo &g'//'и/ и /ии#&и&/// ++01332655"&'&&5463323546332#  +, !, ^ ^kj& &O'//'и/ии#// ++01332655"&'&&55463323546332#    **!+F? ?  NM % &u'//'и/ии#//EX/>YEX#/#>Y+ &01332655"&'&&55463323546332#  ** !,:F F UU& &O'//'и/ии#// ++01332655"&'&&55463323546332#  *)!,^;; II&Cb 4@}1+ +5:+ и5B=/%+ +  и /%и/и/%"и"/014332##"'#"5543"&55'"&554632676676#%##"54332t&&  6292 9l9As9 :?F''G% " m p"   t + PA +78/1/8и/(1,94/+++и/и/ и /и/и/%01%"&55#'"&554632676676676676#%##"54332f ()*J L**L!O('MDPCA** k"  o ' &_'//'и/ и /ии#(// ++013!2655"&'&&546332!546332#  B ++,Nb b rq';4 +012#!"&55463  4!!^v &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#332655"&'&&5463323546332# ? c %q'-s ? e&q$ +  &!-  &!i3o &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332# 6 Y %f( !,7 \'e% ,  a& ݌  a& io &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332# 6 Y %f( !,7 \'e% , w%! w%!iPo &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332# 6 Y %f( !,7 \'e% ,% O% Մ O% ^- &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#332655"&'&&5463323546332#  B  %O& + !C'N% - %! %f94 &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332#  B %O' +  D(O&-  Y& ڊ  Y& f4 &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332#  B %O' +  D(O&-  z&  z&fN4 &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332#  B %O' +  D(O&-   J% ҂  J% cz &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#332655"&'&&5463323546332# ? c%q&-t ? f (p%,  &!-  &!nu &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332# 6 X &f'!+ 6 \  (f$ !, w%! w%!nPu &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332# 6 X &f'!+ 6 \  (f$ !,% O% Մ O% I &0M(7+J/++#+ии(?и/Aи#O//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332#P    %&,  %% ,  o%܌  p%7 &0M(7+J/++#+ии(?и/Aи#O//;/E/EX/>YEX+/+>Y+ 'и 1и@и1M01332655"&'&&5463323546332#%332655"&'&&5463323546332#L } $%, }  $%,Vd dnn'd don'7 &0M(7+J/++#+ии(?и/Aи#O//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332#L } $%, }  $%,Aj jur &j j ut&7 &0M(7+J/++#+ии(?и/Aи#O//;/E/ ++'и+и 1и@01332655"&'&&55463323546332#%332655"&'&&55463323546332#L } $%, }  $%,rP P ZY&P P \Y&_$ &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332# L o%|% +j L q%}%,~ ~J&~ ~J&xW &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&55463323546332#%332655"&'&&55463323546332# 3 R!a#, 3 S #a! +SY YTS%Y YTS%yX &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332# 3R!`#- 3S #a! + #M#, ?#M# -YPPII%PPII%` &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&55463323546332#%332655"&'&&55463323546332#  > #M#, ?#M# -=\\ VU '\\ VU '` &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&55463323546332#%332655"&'&&55463323546332#  > #M#, ?#M# -`J J ED&J J ED&yI &0M(7+J/++#+ии(?и/Aи#O//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332#P    %&,  %% , c%p% + > d &p$ !,uu7  %uu7  %i &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&55463323546332#%332655"&'&&55463323546332#3 R"`#, 3S#a" ,YT TNM%T TNM%|i &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332#3 R"`#, 3S#a" ,2g gba&g gba&i &0M(7+J/++#+ии(?и/A//;/E/EX/ >YEX@/@ >Y +'и+и'0и 101332655"&'&&55463323546332#%332655"&'&&55463323546332#3 R"`#, 3S#a" ,oJ JDC%J JDC%MA ?@// и@и/<A/.?+ +(+и/и/(%и%/(+и+/.901%##"55#"554334332"&55###"&554632676672676676#A( )"'**)BN*+N"N&(J CPDA "  z!#  ~ VL &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&5463323546332#%332655"&'&&5463323546332# " F%S$, " E $R$!,kk"uu&kk"uu&d &0M(7+J/++#+ии(?и/A//;/E/EX/>YEX+/+>Y+ 'и 1и@и1M01332655"&'&&55463323546332#%332655"&'&&55463323546332# > $M",  ? !M$ !,CQ QLJ&Q QLJ&d &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&55463323546332#%332655"&'&&55463323546332# > $M",  ? !M$ !,2Y YTS&Y YTS&c &0M(7+J/++#+ии(?и/A//;/E/ ++'и+и 1и@01332655"&'&&55463323546332#%332655"&'&&55463323546332#  >  $M!+  ?#M$ ,bFFA@'FFA@'PA +78/1/8и/(1,94/+++и/и/ и /и/и/%01%"&55#'"&554632676676676676#%##"54332f ()*J L**L!O('MDPCA** k"  o 'M4N+a&+A&&]A&)&9&I&Y&i&y&&&&&&& ]&9)/01'&&'&'&'''&676676654632M- =@ >[, ~k!#/11[%  D&('&"z :JIhO])a%+A%%]A%)%9%I%Y%i%y%%%%%%% ]%9'/01'&&'&'&'''&6766'432 I( @  E~ vx . I@")##: ! /$vR! I܊K`,a(+A((]A()(9(I(Y(i(y((((((( ](9*/01'&&'&'&'''&6766766'432 K)F!  F 5Z!  - KE"#+')?  :##%S !a<:EPB\.a!+A]A)9IYiy ] !9/01'&&'&'&'''&76676654632G "E   aA  >X/   J'`1 4[* &T.,f: E;$ 7 G?.a)+A))]A)))9)I)Y)i)y))))))) ])9,/01&'&&'&'&'''&76676654632\  A( 6T=  6J+ y^021^* K''& K|: 3oEFsM"*a%+A%%]A%)%9%I%Y%i%y%%%%%%% ]%9(/01'&&'&'&'"''&7667667&632j @" "=8r  4I/ ND&!"6 "7 # m^ (X02o; I%(a#+A##]A#)#9#I#Y#i#y####### ]#9&/01'&&'&'&'"''&7667665&632j.5G !> ;n 1M. RJEX. |k%112Z! # D&('&!z  :JIiVd)a%+A%%]A%)%9%I%Y%i%y%%%%%%% ]%9'/01'&&'&'&'''&6766'432 I( @  E~ vx . I@")##: ! /$vR! I܊Ui,a(+A((]A()(9(I(Y(i(y((((((( ](9*/01'&&'&'&'''&6766766'432  H% !F ! E 7X " - LE%$-&'> !:$"&R  a<;E\Be/a!+A]A)9IYiy ] !9/01'&&'&'&'''&76676654632Q "B"`B >Y.   F!_ .! 4[* &T.,f: E;& 1  sb E // и/ и!// +01##"5#"554334332##"54332% %%%X # 7}ib E // и/ и!// +01%##"5#"5543354332##"54332( (''F $ sc 'W+!+ и ии)$//++015#"5543354332##"5#"5543##"54332p| |&&| o&&R " o # /}qb 'W +!+ и ии)$//+ +01%##"5#"554335#"5543354332##"54332% %&& " U " u~}!e+)/01'&'&'''&6766766763G**_2 s\U2.0IBr-*8 , 1U "3 !)JD[<39Z " M11q= X ,'&&'&'&'''&676676676 >)'b4 >t/0! -.|IBp**2 ,   !=)  "8%%* /)+F $ ;%#R)X-'&&'&'&'''&676676676 >)'b4 >u01" -/}ICs(*0 ,  >)  !<'&* /*+C" <$#R(X)'&'&'''&6766766762 &n ZC$*.}MHq*'. ,  и>/ DиD/ HиH/+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!29889I( S**T"(("S**S! (dq&1..!,/ L b/k)/01'&&'&'&'''&766'4632U &G >} ns.    L,R/_P Fc5.#!6 zw" /012'&'&'''&766766743 8N@*^B :T / '%m; #6$))L)# !@ !G' H 4g5/ /и5)и)/16 //, ++ )1и1/)3и3/01####"54332326##"&55463354332676  ))   Qc ' (%h" #uuyz#!/01'&&'&''''&766'4632 19M$H ! >}  nr. -*806 #-!WK " =S~v!/01'&'&'''&7667430:S X?# ;4+ ++и//7*+7и/*'и'/*=1и1/=3и3/=;и;/01%##"55###"543323543326#'"&554633354332676c&z%%z&=>97 8?K(/.<T # "~H#!/01'&&'&''''&766'43c,2D"C 6v ga 0 $!<.5# .#QF" :P H" /01'&'&'''&766743c)1KS9U> j^ . $!1',#3 $F"" 6yO NA +Y,/%/,и/% -(/"/ + и/и/016##"&55463!543322766##"54332Wh' )(6d))O# #Y ;3M // и/ +и и0154332354332!2#!"&55463F' (  ooooo"#}ze.+/01'&&'&'&'''&6766766763A*+d6 8i,.$")ZAv,-7 ,!C- 3!$"$.*_B  H-+`/ X .'&&'&'&'''&6766766762A)'X- )'b4 >t/0! -.|IBp**2 ,  >)  "8%%) /(+F # ;%#S(R%"/01'&&'&'''&67667632&k l+-Y , ;T "cA0&H) # $yS vV~)/01'&&'&'&'"''&766'4632i (H ?|  lt-    M+v/\P E^0-"5  /01'&'&'''&76743/7S W>$ ll . &  "8"%&A'-MQa(/01&'&&'&'&'''&766'4632">9d d[/   <" ~- \B IU+*!,_ /01'&'&'''&766743n(,E  E4 5k ]Z / 5)- 7J4 " +rU_!/01'&&'&'''&7667433n).E  #? 9f bY / !8/4 1Q3! 0T c/01'&'&'''&766543n(/A F24n aZ . 1&+2 D8" ,pE Cb 6Be3+ +7<+ и7D?/&+ +&2и/&#и#/014332##"55#"5543"&55'"&55463676676#%##"54332t&& /1A2 9|:Cv9 ;B,*''Q " q  "     X ,'&&'&'&'''&676676676 >)'b4 >t/0! -.|IBp**2 ,   !=)  "8%%* /)+F $ ;%#R);E +012#!"&55463  !#I@ (K"+*%//+ и / и /и/016###"&55463!2766##"54332JJ &f;D J )) ^  " c ,*EF/A/AAA]AA)A9AIAYAiAyAAAAAAA ]и/A9F.и./3A3&363F3V3f3v3333333 ]A33]и/!.39<.90/C/01'&'&&'&'&'''&767&'&'''&766766543267665432(-= / 4m#.#%/] " 2D *  * ʎG@I9 ;!   ==)5##p  <CAN~t ')(#:=LX8 HI/C/ACC]AC)C9CICYCiCyCCCCCCC ]и/и/C9I4и4/9A9&969F9V9f9v9999999 ]A99]и/9$и$/&4996/F/01'&'&'''&767&'&'''&7667667432676674632 5;/!P7  , !0b  0@ & :%' L@+  %-!!8d0 (']Q! (M++a:  UF*G1rI9EF/ /F@и@/A&6FVfv ]A]A ]A ) 9 I Y i y ] и/"и"/ &и&/ (и(/-и-/@6и6/ /C/0167674632'&&'&'&'''&767&'&'''&7667&6322& 2'  66 8e 1$'4Y  TV & [P-&%r m[&  + {[-5 "jN K}76Em@5+@@9A55]A5)595I5Y5i5y5555555 ]2/C/01'&'&'''&767&'&'''&7667667&63267674632'1<;/"5g  +""E2  .@ %  ' 1& VL,+3  $.""pN !($.P&  (G(&W6 >4)$%dx'>XM%N//NGиG/A&6FVfv ]A]A]A)9IYiy ] G9и/и/и/(к,92и2/=G9GIиI///I/K/016766'47632'&&'&'&'''&767&'&'&'''&76'47632  *  *  )%8  - *T YM+ u ! )*H]QR9:!5 3 98MJ  >?pk  n1TH߸I/C/ACC]AC)C9CICYCiCyCCCCCCC ]и/Cи/I.и./4A4&464F4V4f4v4444444 ]A44]и/. и /1/F/01'&'&'"''&767&&'''&7667667&632676674632   %*  (% $2  '*0T ,> ' % D;# +$&  &,,2/*%.WF!'M**Y7 [J /jE1TFӸG/A/AAA]AA)A9AIAYAiAyAAAAAAA ]AиG*и*/0A0&060F0V0f0v0000000 ]A00]и/*и/-/D/01'&'&'"''&767&'&'''&7667&632676674632"#'*  ('1U  % * OO '   % rj.(+   ) tT+,+%/%  Nt ><"" $5zG1,SI׸J/D/ADD]AD)D9DIDYDiDyDDDDDDD ]Dи/J/и//5A5&565F5V5f5v5555555 ]A55]и//!и!/2/G/01'&'&'"''&767&'&'''&7667667&632676674632  $*  *%%5  (.X ,>'  % >:!*"'  &))23 (&RI!(J('V5 UG .i@1*JK/D/ADD]AD)D9DIDYDiDyDDDDDDD ]и/D9K.и./5A5&565F5V5f5v5555555 ]A55]и/!.59.0и0/?.9DFиF/0/2/F/H/01'&'&&'&'&'''&767&'&'''&7667665476326766'47632'.> / 3n#.#%.] " 2D(  * ʎG@I9 ;!   ==)5"#p  <CAN~t ')(# :=LXTsA <+ и/ /EX/>Y 014332##"5#"5543)) $ $ ]A '+ // +01##"5#"5543354332A)) # >EF/ /F@и@/A&6FVfv ]A]A ]A ) 9 I Y i y ] и/"и"/ &и&/ (и(/-и-/@6и6/ /C/0167674632'&&'&'&'''&767&'&'''&7667&6326&2'  65 8f  0$&3[  TV ' [Q,&%r l[&  + {[/3 "jN K}]A '+ // +01%##"5#"5543354332A))^ $ ;6FG/A/AAA]AA)A9AIAYAiAyAAAAAAA ]и/A9Aи/G/и//5A5&565F5V5f5v5555555 ]A55]и/5и//!и!/2/D/01'&'&'''&767&'&'''&7667667&63267674632 '0=;.#6g ,""F1  .? '  (0% VL,+3  $.""pN !(%.P&  (G(&W6 >4 )#%dx^/A '+ // +01##"55#"5543354332A))= $  sb E // и/ и!// +01##"5#"554334332##"54332(k k(''X % }c E // и/ и!// +01##"55#"5543354332##"54332%q q%&& # !b E // и/ и!// +01##"55#"5543354332##"54332&b b&''< # >\sA Q+ и ии//+ +01##"5#"554335#"5543354332A) ) $ # { A E + и и//+ +01##"55#"554335#"5543354332A) ssss ) # W # l{A E + и и//+ +01%##"5#"554335#"5543354332A) ssss ) # h # ll(A V + и и//EX/ >Y +01##"55#"554335#"5543354332A))6 $ H # [sb '_+!+ и / ии)$//+ +01##"5#"554335#"5543354332##"54332&` `a a&&&X % x $ } c 'W +!+ и ии)$//+ +01##"55#"554335#"5543354332##"54332&k kk k&( ( $ F $ m "b 'W +!+ и ии)$//+ +01##"55#"554335#"5543354332##"54332( ____ (''A $ ; # SD[P 9/N/01'&&'&'&'''&&767&'&'&''''&7667&632676674632 C#  !B 9y "  (   (   8,L" #  'F <"!F"  #" &R' )' %FV 2/G/01"'&&'&'&'''&4767&'&'&'&'"''&766766762676676632 /  I4 *  J3 ,@ )     (   6 &C  $C : C  ! #I$ #!#H 3c4// и4)и)/1 5/ /, +,и/ )1и1/0132####"543326##"&5546335433276  ))  Qc ) Fh"y" #IJ0OP'&'&'''&7677&'&'''&67667426766746323$$P3  .#  . NT &     )  )X-!) 8g*%.#)$&%  ;U  6/3i0 69( @sb A =+ ++и//A2++2; и /2/и//;9и9/;=и=/01##"5###"5433234332267676"'"&55463275463b'o&&o'&!;@#< @#9w:2:6:QDk  # !<SK ,/Q) 3Ȱa # "ONNA !-e./'/.и/'"/*/$/ + и/и/и/016##"&55463327543326766##"54332Wh0/' !!8f))z! #QQK;M // и/ +и и01%54332!5433232#!"&554632)(  M""\bS =/Q/01'&&'&'&'''&4767&'&'&'&'''&67667&63267674632 G$  &E Y?   Y7 ng+  "4+ $ %!"8 # 8 "# 2[)    /Q  ;c#$! #&Wm\ U >/S/01'&&'&'&'''&4767&'&'&'&'''&67667&632676674632 D%$C U4    W7 aq ) ")  !.% 0! -K   )F  0\  "%S1\T >/R/01'&&'&'&'''&4767&'&'&'&'''&67667&63267674632 C%$D!V1    W6 aq *  #5( ".'1!.N   ,G 2^  #LfaT =/R/01'&&'&'&'''&4767&'&'&'&'''&67667&632676674632 C# #AU2   W4 `k '  !( *#- )F   'B .~V "N-F9S /Q/01676674632&'&&'&'&'''&767&'&'&'&'''&6766766742:    ' )1; 0 N3 0" ,`-@ (2-.g4 8:.&-  " 4_$#*  VA G((S) OV 2/G/01'&&'&'&'''&4767&'&'&'&'"''&766766762676676632 / F5 ) G4,@ )   '   6  )G    %D 8 D"  ! #M# $#$QV 2/G/01'&&'&'&'''&4767&'&'&'&'"''&766766762676674632. E3 ( I1-= (   (   6 *I"   &G <# F#  !! &N' &$&FV 2/G/01"'&&'&'&'''&4767&'&'&'&'"''&766766762676676632 /  I4 *  J3 ,@ )     (   6 &C  $C : C  ! #I$ #!#0=OK'&'&'''&767&'&'''&6766742676746323$$2h  ,! A. NT &    1 )  )~.&bU$)#D&  :T  6+ Yb 44( =Q6'&'''&&767&'''&67674626746324?+4a #,0X'  9' &P-H`A 2O1  T ",Ha$!`2<SK'&&'&'&'''&&767&'''&67667667462766746323 #H- #0?" "8' & &  +K $; %;  8!H" &#% &R&+," BK6'&'''&&767&'''"6767462674632- ?&1] )/V '  ? ' -E.9U;  ,H0  S *H['#S*;N +01%2#!"&55463  N##I@ +W %+ -(/"/+ и / и /и/и/016#"##"&55463326766##"54332JJ &4;D%J )) !! niPE/Ǹ0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]+*+014&'&&#"3267667#"&'&&54676632@.0??0.@HZAAZZAAZ-\%%..%%\-+[&&00&&[*8r.,<:..r8;r./66.-sr$I/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-+*+014&'&&#"3267667#"&'&&54676632<""><%"<G% W41W##W10U" ''=='&==%2S"!T25UTr I/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&54676632<""<=#"<G% W30V $$ V0.V! )*@@*(AA&3V!#"!U48X!XuDE/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&54676632ZLM]? V<:WW;U;9VV;:ToPK/Ǹ0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]+*+014&'&&#"3267667#"&'&&54676632@.0?@0.@HZ@BZZB@Z-\%%..%%\-+[&&00&&[*8r.,<:..r8;r./66.-s)sA '+ // +01##"5#"554334332A* * $ Ow.S/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&54676632 <#"==##<G& X30V %% V0.X!!) '<<'%;;$1R!R14ST. A '+ // +01##"55#"5543354332A) ) $ u M/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&54676632="#;<$"=G$ V41V&&V1.V" (*@@*(AA&3V!#"!U48X!XyDG/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&54676632:" 9:"":F%"T2/R %% R//T ($89$#99"-O! O/2O O")A '+ // +01##"55#"5543354332A) )7 $ b !E"//"и/и#//+01##"55#"&55463354332##"54332(   (''6  < sB 9+ и//+ +01##"5#"554335#"5543354332B* *` $ # @ 9+ и// ++01##"55#"554335#"5543354332@( (y $ " (A 9+ и// ++01##"55#"554335#"5543354332A) )6b #  # qb 'h +!+ и ии)/$/EX / >Y+ 01##"55#"554335#"5543354332##"54332 ( ( ''7g # c " u=0/˸0//0и/A&6FVfv ]A]A]A)9IYiy ]*1 +$+01"32676654&'&&"&'&&54676632#U%%33%%U#"U&%44%&U"2r/0<<0/r24q//<0/??/0>T=>TT>=T*/˸0//0и/A&6FVfv ]A]A]A)9IYiy ]*1 +$+01"32676654&'&&"&'&&54676632 R$%33%$R S%$44$%S.m./??/.m./m..??..m *!!*  *!!* A21AA./@;# ++ 01546332!2#!"&55463+  7""*/"0//0и/A&6FVfv ]A]A]A)9IYiy ]*1EX / >Y$+ A]A(8HXhx ]01"32676654&'&&"&'&&54676632!T#&33&#T!!R$&33$$T!0n,0==0,n00n/-==-/n -%$.  .$%- D45ED24D#/˸0//0и/A&6FVfv ]A]A]A)9IYiy ]*1 +$+01"32676654&'&&"&'&&54676632 Q%#22#%Q R#&R/j..<<..j/0k.-<<-.l *##*  *##* A54A A45A uR/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&54676632 =!#=>#!=F%!W41X&&X10W !(%:;$$9: -P""O/4RRI/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&546766329 99 9CS53QQ30S"H))*)#??$(@B{I/߸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/-и-/+*+014&'&&#"3267667#"&'&&546766329! 97"!9CU53RR32R"/**++%AB%)BBI,߸-//A]A)9IYiy ]-$и$/ A & 6 F V f v ]A ]и/*и*/+'+014&'&&#"3267667#"&'&&546329! 97"!9CU53Rxf2R"X) ''  (!<>!NY>H 3_4/-/4и/-(и(50/*/ +&+ и/016##"&5546335433276732####"54332 Qc ' Vh؋  )) x" #]]"]!,-//A]A)9IYiy ]-!и!/ A & 6 F V f v ]A ]и/ и/ $и$/*и*/+'+014&'&&#"3267667#"&'&&5467663254Q>5B N.-LL-+M##55#AO5-KK-1MLk,*-//A]A)9IYiy ]-!и!/ A & 6 F V f v ]A ]*и*/EX/>Y'+A]A(8HXhx ]014&'&&#"3267667#"&'&&5467663233N;3BM/-KK-+L!H( (3:(#==#&?>kw,Ӹ-//A]A)9IYiy ]-!и!/ A & 6 F V f v ]A ]*и*/+'+014&'&&#"3267667#"&'&&5467663233N;3BM/-II-+L!),*6?+$BB$)BBk,Ӹ-//A]A)9IYiy ]-!и!/ A & 6 F V f v ]A ]*и*/+'+014&'&&#"3267667#"&'&&5467663233N;3BM/-KK-+L!X' '08 ' 99 &<;NA +Y,/%/,и/% -(/"/ + и/и/012##"&55463!543326766##"54332Wh' &%8f))z" #dcS;#M$//$и/ + ии012!54633232#!"&5546335463d,  ##s0/˸0//0и/A&6FVfv ]A]A]A)9IYiy ]*1 +$+01"32676654&'&&"&'&&54676632#U%%33%%U#"U&%44%&U"2r/0<<0/r24q//<.-n *""+  +""* B42C@00A*/˸0//0и/A&6FVfv ]A]A]A)9IYiy ]*1 +$+01"32676654&'&&"&'&&54676632!S#$33$#S!!Q%$33$%S0n,0==0,n00n/-=>.-n ,&$/ /$&, E76DD13D#/˸0//0и/A&6FVfv ]A]A]A)9IYiy ]*1 +$+01"32676654&'&&"&'&&54676632!Q$$11$$Q! R%#22#%R /l-.;;.-l/1m,-;<,.l +##*  *##+ A54BC45Ab^/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&54676632>"$;<%">G% W53U %% U30W !)"0/00'D D'+GGU/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+*+014&'&&#"3267667#"&'&&54676632:89:B$T.,R##R,,R 'Y( ((   (%<=%(>>U,-//A]A)9IYiy ]-!и!/ A & 6 F V f v ]A ]и/ и/ $и$/*и*/+'+014&'&&#"3267667#"&'&&54676632;""9VF";B!T2/Q Q/.R!%A.-9C.&AA&*DDU/0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]и !и 'и-и-/+*+014&'&&#"3267667#"&'&&54676632:99:B$S1-R !! R-,S &h$ &&  $"98#':;MA @A// иAи/=B//@+ +/")&и&/),и,/":и:/"<и<CIb Bq?#+ ++ иD// +2>+>$и$/2/и//><и** r" v ) (/"0//0и/A&6FVfv ]A]A]A)9IYiy ]*1EX / >Y$+ A]A(8HXhx ]01"32676654&'&&"&'&&54676632#S$$00$$S#"S#$22$#S"2m.-==-.m22n/-;;/.m ," ,   , ", D42CB/1B;M +012#!"&55463  M##NA $3+&!// +и/016##"&55463326766##"54332Wh9v78f))" #WN^t3 1*+01'&&'&'&'''&676676&#!"5543!2$<? 1>AU # H4pPF+*$!!4:!! <8:; r} " %] 4 #+01'&&'&'&'''&676676&#!"5543!2p < 7A 2  >3* A"? ! 3V# >j $ $.79 # ]/ +01%'&'&'''&676676&#!"5543!2m ::-(8B w8  ?4) ;3)*7\# ;s # $.8=( Y27 &+01'&&'&'&'''&676676676&#!"5543!2p ? 6M ?q//G  >4& ;S$ 0S# B'%U* # $-.2   AU4/ +01%'&&'&''''&676676&##"5543!2;Y }& 3 9 9}B%$# e ` $ !0qg!""? T+/ "+01'&&'&'&'''&76676676&##"5543!2  0 %33D  =W$#6  4*81 % +)+.  )I'%U5 # $.=1#'# O,- +$+01'&&'&''''&76676676##"5543!2$#+4 0&13C  =_$#3  3o=5- ' %,+./  .S+*X3 # $U+*/ -&+01'&&'&'&'''&676676676&##"5543!2 +81(jE>Y#$5  4 8. &$ %*O+  'D"#M0 # %\^3 1*+01'&&'&'&'''&676676&#!"5543!2"$<? 1>AU # H4pPF+*$!!4:!! <8:; r} " %c 1 #+01'&&'&'&'''&676676&#!"5543!2r :8A 3  >4)(8D; 4U$ >i $ %.69( e/ +01%"'&'&'''&676676&#!"5543!2o 8:.(7@ v7 ?4* : 3)*7\# ;s # $.8=(b26 %+01'&&'&'&'''&676676676&#!"5543!2r ? 5CCN @p1.G  ?3% ;M & 0(*# B'%U* # $-.2   sb !E"//"и/и#//+01##"5#"&5546334332##"54332 (   ( ''X ".}hc E // и/ и!// +01%##"5#"5543354332##"54332& &(($ % c E // и/ и!// +01##"55#"5543354332##"54332& &((6 $ ;sb 'W+!+ и ии)$//++015#"5543354332##"5#"5543##"54332p && r (( W " s " *}ib 'W +!+ и ии)$//+ +01%##"55#"554335#"5543354332##"54332 ( ( '' # _ # !c 'W +!+ и ии)$// ++01##"55#"554335#"5543354332##"54332&&((=z $ [ # fAc3 .'+01'&&'&'&'''&76676676&#!"5543!26<:BPL  *\/--DZ   X<;f3 T '+b6.!"$  " )D ! !>##M/  # %k3 #+01'&&'&'&'&''&76676676&#!"5543!2X )i56/ LY  CFGu#RN1'> &,,Y(    / *9  # 4/&#   ;# ++ 01546332!2#!"&55463+  7 ##k5 &+01'&&'&'&'&''&766766766#!"5543!2X !c654798j, ;HF~& RƗ^'%0)--V  +? " d  ;#++0154332!2#!"&55463(  " "`6 #+01'&&'&'&'&''&766766766#!"5543!2Q\554 PC   BDCq$ R(,!B)++Q   (   ". $ %  Q*0 +01'&'&'''&676676&#!"5543!2w>G:59x< {A a#*3 ! A O ! '@  5D#  #+,  l2 "+01'&'&'''&636676676&#!"5543!2=C99:z5 ,e21\# M$ "0&(@< 1  ! &7   " !  yy1 !+01'&'&'''&676676676&#!"5543!2 ?B869s85k00V L$ &1'-A: !4 /9  !%$ l. +01'&'&'''&676676&#!"5543!2 =D;; 9u3 oA M$ (3 ((?8   ' " Z5 !  "'    I%>8 %+01'&&'&'&'"''&676676676&#!"5543!2% ; !0j8,\-,Q :#',; H$>  6E! #  "*'   W.5 %+01'&&'&'&'''&676676676&#!"5543!270c/ +X)'H $$  )  35  0 *2  "  W., +01"'&&'&'&'''&67676&#!"5543!27 Zq l $$  +435  :$  Iw !  "2)  X'* +01'&'''&676676&#!"5543!2aX2YW %%7(*9 03 4.? !  "+   ;#M$/ /$и/  +ии 015463323546332!2#!"&55463K*)   =o oo o" ";#M$/ /$и/  +ии 015463323546332!2#!"&55463K*)   (k kk k""xd1 ,%+01'&'&'''&76676676&#!"5543!2DX&*)V)  ZaF@ GA?r- S ',;5 % & F3 3 C!  # $k. !+01'&&'&''&''&76676676&#!"5543!2X +j441  JFDq"RNbO',-V$  9" (6  $ 6\(  j0 "+01'&&'&'&'&''&76676676&#!"5543!2W &`230  SBBk(Q K 4:S)*(Q%  >//9 # 7-3.  ^6 #+01'&&''&'''&766766766#!"5543!22P^3jj8f, BDCq# R(.J/ )+)Q     ". # %' mc1 !+01'&'&'''&676676676&#!"5543!2 DE73 7o4 .e10\$ M$ -3 %(>E "5  /B# #  "-(  z0 +01'&'&'''&676676676&#!"5543!2t =;388v6 /b//U# M$0%$89 .  $4 !  #  2 -&+01'&'&'''&676676&#!"5543!2<%1 @! BG967m5o> N#u&$   # 1 ! &h; !  "* %+01'&'&'''&676676&#!"5543!2<,<>ABG.,e nC M$ ,& " 1 !Q6 !  "B\3- +01'&&'&'&'''&67676&#!"5543!2 !8 \b  '#  15'&29     @,  S #  "2+ f) +01'&'&'''&67676&##"5543!2>&1S_ r $ .2":  0$  HT "  #'"  f) +01'&'&'''&67676&##"5543!2?+"+Vdw $-2%9 6#  F_  ")# f) +01'&'&'''&67676&##"5543!2>&+Vd w % *.":  .  4V  ""  k3 #+01'&&'&'&'&''&76676676&#!"5543!2X )i56/ LY  CFGu#RN1'> &,,Y(    / *9  # 4/&#   ;Z +012#!"&55463  Z##;B +012#!"&55463  B!!,0h+_иe01676676&##"554332&'&&'&'&'''&76767&'&'&'&'''&76676676&##"554332N   "/  /  / -&]6  4, N/  (J& .k#  -,# !W[ "  /7 (# &6^! )+ MH  PE G$&M   4 _V + JиP017676&##"554332'&'&'''&767&'&'"''&7676&##"5543320 #6 ] o$* )&!R0 9*#/T  o *& P\  02 !0Z&  -0@> d~ %0h+_иe01676676&##"554332&'&&'&'&'''&76767&'&'&'&'''&76676676&##"554332G   "/  /  / -&]6  4, N/  (J& .k01"''&767&'&'''&&76676&##"55433267676&##"554332'&'&'U  *#! ?[ Tk /  = 1 ( ** $V#  D6 2u6  #+&%  HD  ",(,   +JPV2+++Cи2I01'&'&'''&6767&'&'''&676676&##"554332676676&##"554332 % @ A ?c 9  ' u 3 !  7  3 '`2 */5 ('! KY5.+.Fи5L01'&&'&'&'''&6767&'&'''&676676&##"5543326766764##"554332 &>!   E# Gn 9)  t 4  $   #9  8 *g0 ! )06 ! '(" PV/(+(Dи/J01'&'&'''&677&'&'''&676676&##"554332676676&##"554332 #%;J  E$F` 9  % u 5 4/   0 %O+  )0  *  (&   "d[4-+-Dи4J01'&'&'#''&6767&''&''''&676676676&##"554332676764##"554332I '-=` "$ 8D 9)  w 96 V n#( i OL   ?. 3;  )1,'EK  =9 AaU1*+*Aи1G01'&'&'''&6767&'&'''&676676&##"55433267676&##"554332G "=!  2@ ?O q 9  - ` w5   4   2% 'K, (0 1( ''  5SP.'+'>и.D01'&'&'''&6767&'&'''&676676&##"55433267676&##"5543329 !!1I  2@ ?N q 9 / a w3 40   6& %X, )/ 2) &')&  B]T0)+)@и0F01'&'&'''&6767&'&'''&676676&##"55433267676&##"554332C !3"  2@ >O q 9 * ` w4   ,  /& &F+ )1 -) (%   tRZ+PиW0167676&##"554332'&'&'''&767&'&'&'&'''&676676676&##"5546332 G 7" "26 75"N  -& Ic1M0 ; KF" )(54%_U   G;  6;#"+k}_ + Vи]0167676&##"554332'&&'&'&'''&6767&'&'&'&'''&676676676&##"5546332'(>( 7 38 #Y8   Me(N#$8 x1A  +%)!   9   5& )2 jy^ + Uи\01676&##"5543322'&&'&'&'''&6767&'&'&'&'''&676676676&##"5546332 ()V7 756$T1  Nc (N##7  xB\  +'-%   !8   7( ,4 jz\+SиZ0167676&##"554332'&&'&'&'''&6767&'&'&''"''&676676&##"5546332 '*?- 8 3 7Jg Jq ^!  y .= ! -%    ;3   .) L- !PB_70+0Hи7N01'&'&'''&676767&'&'"''&676676676&##"554332676676&##"554332 *'!L) #"!B" =. 9"!. | 4 '  'I  6 2:  )0!E%  ''&)   ]Y6/+/Gи6M01'&&'&'&'''&6767&'&'&'''&676676&##"554332676676&##"554332 # @  9@ ?^ 8 ( o 3 !    2  4& %O3 (00 '' ]P0)+)>и0D01'&'''&6767&'&'&'&'''&676676&##"554332676&##"554332 A1@T  9B ?b 8 "8& o 3 )2QB  ;) %b3 ! )0X  9 % p 3   1   2% 'K0  *0 ,  '' 5I]S-&+&=и-C01'&'&'''&6767&'&'"''&67676&##"55433276676&##"554332E +$ "9' #5K w- o 9/ q 4 $ (5 @7 Og  )0 "=$  '($%  ?sb Aq>#+ ++ иC// +2=+=$и$/=&и&/2/и//01%4332##"55#"5543##"54332"&5'"&55463676676#u '' ''01A/9}9?u8  ]pU # } !! !  ?IW4-+-Eи4K01'&&'&'&'''&6767&'&'''&676676&##"5543326766764##"5543320   *  /A ?F  r : , \ r4   (   +' %@3 (0 9 ''!  <PI)"+"7и)=01'&'''&6767&'''&676676&##"554332676&##"5543325 +;3O -& /B DG r 9A Z o5" 1?8 #4* .O3  (0 CB  $(-( HLY6/+/Gи6M01'&&'&'&'''&6767&'&'&'''&676676&##"554332676676&##"5543322  /  &E9A q 9    \ r5 !   !   $(#9/  )0  ,  ''  VT1=27+!+01%'&&'&'&'''&676676&#!"5543!22##"5543j < 4~Hy9  G4(< s , 5X# ! :w # %->=+  I  \|,8-2+*#+01'&'&'''&76676676&#!"5543!2'2##"5543'>$*76 ;:/+a  Gi**C  I3&l,H?  ""QB$  < F+  # (!!^q*6+0+(!+01'&'&'''&676676&#!"5543!2'2##"5543.>%,53 88.)fq>  I5$mQD"# &$W:# 4^ $ )  Xx-9.3++$+01'&'&'"''&676676&#!"5543!2'2##"5543) !65 9:&$ 6H w=  I5$l80- ! $ '@% *xO " (  Ya71=27+$+01%"'&&'&'&'"''&676676676##"5543!22##"55436,e9 6_'(8  5# '44 r/5W# "U-.], # %,?9*'5! D\--9.3+!+01&'&&'&'&'''&676676&##"5543!22##"5543 /[{ k0  3%1.- ! N1" .xK  # &+=7% ],-9.3++01%'&&'&''''&67676&##"5543!22##"5543 0-h9 t  3%& ".+&+F# Y # '+21!& !!\-0<16+$+01"'&&'&'&'''&676676676&##"5543!22##"5543 /Y 0[()B  3%,.--  E/# /@# # '*91! ! !\I2>38+"+01%'&&'&'&'''&676676&#!"5543!22##"5543i : 4~Gs:  F4 (: i. 7]" " 9z # %,A<+ VBsA '+ // +01##"5#"554334332A** % o*6+0+(!+01'&'&'''&76676676&#!"5543!2'2##"5543-,B$&75 ::,*`  Fj**C  I3%k,NA" $"N@$  < F+  # (!!IA '+ // +01%##"55#"554334332A) ) # rA# + /+017463!2##"554&#!"&5 /) & #)rB+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5- 0% /$ #)$#)g$:;//A]A)9IYiy ]  и / и/ 9;2и2/- <)6+01&7665462'&&'&''46332##"554&##"&5  [D. IF +3J2  /%  y CxV )=X! ,'L")#) s{+7,1+)"+01'&'&'''&676676&#!"5543!2'2##"5543-1E&*73 89.+f s>  H4&lXG"# !& %W:# 4^ $ )  IlA '+ // +01%##"5#"554334332A) )z # 7 _l+ +01#!"&5543323!2_ . )  o#'t"IE:3+@/++/,и,/@=и=/@CиC/01'&'''&67676&##"554332#"##&&5543323232676676q C1DaB <-G {   #M.) ) &  p$0>'  8p # ,2#/!* m-9.3++$+01'&'&'"''&676676&#!"5543!2'2##"5543-) 75 ;8'%6~Hv>  H4&l80-" $'@% *xO " (  IA '+ // +01##"55#"554334332A) )! % lj<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+<$+aY+N+H?+$!и!/<2и2/<6и6/01#"&'&&54676632#"#"&554332336676676%#!"&55463!24&#"3267##"&554332N9$%::%$9  %U/)O" (  h k ^0,.11.,0  7!  ""  "G#*  %  $ |M!++ +01#!"&55463!2#!"3!2M -- 6  w ** ! v qV2n3//3и/+и/и/#4EX&/&>Y/++& 01#!"&55463!26554&#!"&55463!2#!"3!2V  .#, -  #,o $*5*%$8*) pBCY7+/+LQ+#и#/L[ / /N/EX2/2>Y>O+HU+O и /2Uи/H'и>:и:/01###&&'&&55463326554&##"&5546332##"3267667646332##"554&##"&5   $P!,   "+E" 3 0$ q 9*)#7)+ $'qAYiK'+C/+]+ e+'7и7/ k/;/b+<3+,F+<и/и/ иbNиbRиR/3Z01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"332676676727"3326554&# ,,   %O!-    !,B"  x s ~** )  <+#"1*/    j@_iȻR.+J6+a++aи.>и>/hиk //B//EXM/M>Yd'+C:+`+C и /Cи/'$и$/M2dUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"33276676727332655m'&,! "L  ,}  -~e  ] n  cb '& :*($6+*  tf fiBl5+-+-и/!и!/,Bи-OQ/EX0/0>YEXL/L>YEXZ/Z>Y9 +&+ и/09<и 3q  9+("6*)  =  .&+NE  -'&(  +$JrS?c0 +(+]F+ и/]T/B/EX+/+>YEX[/[>YKP+Bи/+Pи/K иBa3и3/UиV01#"&'&&55463326554&##"&5546332##"332677676776##"&5546332##"32##332 6 !,   !, F"  M , ,  p   5)##1)$  /**  9  : robfٻT0+L8+c+d+0@и@/h /'/)//D/5O+<и/ и<и/ *и*/EиE/WиW/[и[/^и^/cи<eиe/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33263767672735#h.5 95 6 s  -  !,G  UU    ;)* #8' +  lC[iuM5+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]D?J+x+&+m+g^+Jи/J и /m0и0/?AиA/mVfиf/Js01"##"&'&&55463326554&##"&5546332##"33263667672%#"&'&&546766327#!"&55463!24&#"3267##"&554332  #Q!,  !+H"  Y9$$99$$9 b Z0,-00-,0  q  5)#$1*%  !  ""  "s ( $ |A%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5d  #!+  ,^ l   ** |A &_'//'и/ и /ии#(// ++01%3!2655"&'&&5546332!546332#K  U++ ,K K SP&f:DG<++<и%ܸC'/?+;+01"&'&&55463323546332#7''&676'432'&'%332655 %(, , {/0]   YS'  a "|5 3`\c[[x1//01%'&'&'''&67667667665663? 0\ wWH5 .,p@@c&%4/ @V  "9/E ( #7 % +<& p; //01''&6766'43276674632'&'&''&'&'dW 0 @8A</  YL d1.]T+ 5 " )jD 2X*^= :b,; A6 (9 CrH/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+01%4&'&&#"3267667#"&'&&54676632$P+0O O0+P$G.''j<@k%%++%%k@38+"+01'&&'&'&'&''&676676676&#!"5543!22#!"5543&c440 HECx,K (.8S '*)W%  1!   !+ $   %"  SrA5 + и /++017463!2##"55!"5543!54&#!"&5 /)  & #)f 6sW#3+!+ ++01#!"&55463!2#!"!2#!3!2W + +7  #  **  8 3 |y#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#Zj m ?br "     ܭSi/=KL//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/M+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2:;;:F) S**T"(("S)+S!)[ _ F& '( '~ Ic !E"//"и/и#//+01%##"55#"&5546334332##"54332%u u%&&# Xc !E"//"и/и#//+01%##"5#"&5546334332##"54332&i i&&&&! zc !E"//"и/и#//+01##"55#"&55463354332##"54332%u u%&&-"1 IsA E + и и//+ +01##"5#"554335#"554334332A) )[ # $ VB E + и и// ++01%##"55#"554335#"5543354332B* * # p # VHB E + и и// ++01%##"55#"554335#"5543354332B* *V # { # VB E + и и// ++01##"55#"554335#"5543354332B* *w # ] " c 'W+!+иии)/$/++015#"5543354332##"55#"5543##"54332|h h''h O&&Z # e $  \b 'W+!+иии)/$/++015#"5543354332##"55#"5543##"54332zb b%%b J''R #  $ b 'W+!+иии)/$/++015#"5543354332##"55#"5543##"54332pi i '' i [&&F $ DX #  a0<16+ +01'&&'&'&'''&676676&#!"5543!22##"5543Vc656JSWV cO Δ[#< $)+)Q  # "  oL  # f$(   jK 5A6;+$+01'&&'&'&'''&676676676&#!"5543!22#!"5543?']221FO F|89g5s M"7-; #(&Q%  *  ! !- # 6)  l;#++0154332!2#!"&55463(  "__!!k# + /+017463!2##"554&#!"&5 /)  #)k +M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5 /$ .$  ") #") ge3#&++& /"/+01&'&''&7674633246332##"554&##"&56o1-i  . -t X /#  4`\A ]# }( #)WK2>38+!+01'&&'&'&'''&676676&#!"5543!22##"5543?#]222EL fs R6-; %&(N#k  )  ! M4  # ;'   r  ;i# ++ 01%546332!2#!"&55463+  ]]""6v+ +01#!"&5543323!26 .(  o&*uq"KY<5+B,++,)и)/,/и//,2иB?и?/BEиE/01'&'''&67676&##"554332""#"&#"&5543323232636676T ?7E`K <4B z  #$ /*   '  o5<(  9e " -2-$) P9E:?+%+01'&&'&'&'&''&76676676&#!"5543!22#!"5543I !Z210!EP =>?j$r .5): "&$K# " "   # ;!!;#++0154332!2#!"&55463(  VRR!!l\<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+6$+aY+N+H?+$и/$!и!/62и2/01#"&'&&54676632#"##"&554332336276676%#!"&55463!24&#"3267##"&554332@:$%99%$:  %K/) B" '  h k ^0--11--0  7!  "#  #F$)  #  # |)!++ +01#!"&55463!2#!"3!2) ,,   v)*  n z42e3//3и/и/#+#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!24 0#,  $,R  z#*4+ #$ 4)) r-@V2+*+IN+и/IX/K/EX-/->YER+-Rи/E"и6LиL/01#"&'&&55463326554&##"&5546332##"3326766767246332##"554&##"&5 !  ,   !,E%   .$ p 5)##1)$  #(r6XhJ&+B.+\+ d+&6и j/:/a+;2++E+;и/и/иaMиaQиQ/2YиY/01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"332636676727"3326554&#+, z!-  !-G$  x z +**  ;)* #8' +    j(_iȻR.+J6+a++aи.>и>/hиk //B//EXM/M>Yd'+C:+`+C и /Cи/'$и$/M2dUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"33276676727332655U'( ,  "J!+~   - e   ] n  cb '& :*($6+*  tf fiBl5+-+-и/!и!/,Bи-OQ/EX0/0>YEXL/L>YEXZ/Z>Y9 +&+ и/09<иYEXm/m>Y?J+x+&+g^+Jи/J и /mm1и1/?AиA/mVJs01##"&'&&55463326554&##"&5546332##"33263667672%#"&'&&546766327#!"&55463!24&#"3267##"&554332  #Q"+   ,H"  Z:#%::%#: Z T0,.11.,0  p 6*%$2*(  !  "#  #m &  & {%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5"  , +? R g g ** y &o'//'и/ и /ии#&и&/#(// ++01%3!2655"&'&&5546332!546332#  7 ** +N N SS &f:DG<++<и%ܸC'/?+D+01"&'&&55463323546332#7''&676'432'&'%332655 &', - z00]   TN'  a #!|5 3`\\TTm_(/01'&'&'''&676674322N [D"#U .  @)(\/9,4!H7 % (tH  2 jp{< //01''&6766'43276674632'&'&''&'&'dY 0  @8B;0  -,L e0 -]S+ 5 " )jD 2X*^= 901,; A6 (9 Cm)8˸9//A]A)9IYiy ]9*и*/ A & 6 F V f v ]A ]:!+3+01%4&'&&#"3267667#"&'&&'&&546766766327'&QQ%'66'%QQ%'7IB!"FE""BB""D,k1.B'  '!%  %"//. ;tn! +01'&'&''&67$76&'!"5543!2_ DG ņBI} r /8 6T  # c& fc%1&+++01'&'&''&63$76&#!"5543!22#!"5543RK).3Hu} (+  "^ $  "! 9  j5 + и /++017463!2##"55!"5543!54&#!"&5 /)   #*_ 2z(#3+!+ ++01#!"&55463!2#!"!2#!3!2( --     x)(  5 " ; |?#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#+p| v ]Nb !   ̝S1/=K L//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2:;;:E( S**T"(("S**S! (\ _ F& '( '~ Kn-9.3++01'&'&'&''&76676&#!"5543!22##"5543{@:1..6@Q;  R<  & ;  # -oC  # ,&,' nz/;05++01'&'&'&''&76676676&#!"5543!232##"5543BH71f 2d0.P I$((8A!  /!  (  !$ G  H 3g4/ /и4)и)/05 //, ++ )0и0/)2и2/01####"54332326##"&55463!54332676  ))   Qc  ) h / " #cak~# + /+017463!2##"554&#!"&5 /(  q#)q+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5 /% /$ v#'##'g}34/ /A ]A ) 9 I Y i y ] 9и/и/4+и+/&/"/+01&'&''&7674633246332##"554&##"&5On2-j  / /s M /$  4`\A ]# }( #)na4@5:+!+01'&'&'&''&76676676&#!"5543!22##"5543 j" $^]|4!++ +01#!"&55463!2#!"3!24 + +7  v+|+ " ` |%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5A  #!+  ,^ H _ _ *}* | &_'//'и/ и /ии#(// ++01%3!2655"&'&&5546332!546332#)  V++ ,? ? FE&H 4g5/ /и5)и)/16 //, ++ )1и1/)3и3/01####"54332326##"&55463354332676  ))   Qc ' '%h < w! "lkm}(&/01%'&'&'''&7667437 C%&U)  }\D*(,|V& 5 !;+9' #9 %oNp< //01''&6766'43276674632'&'&''&'&'dX 0  @8B;0  -,L e0 -]S+ 5 " )jD 2X*^= 901,; A6 (9 Cq+/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+01%4&'&&#"3267667#"&'&&54676632) !R*.R$$R.*R! )G3*)m9=n'*..*'n=9m)*3 & &%   %*::*)::q{$ +01'&'&'''&7$76&#!"5543!2kUOAC +w Q/5H/9FAs 4% A_ # -0)#   p5 + и /++017463!2##"55!"5543!54&#!"&5 .(  & $)Z ! (|Z#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#;j o @cZ "   •SO/=KL//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/M+I@+*+;2+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2;::;D( S+*S"''"R*+R"(\ ` F& '( '}J_B#/$)++01'&'&'&''&7676&#!"5543!22##"55430 @2)/Xr h &;!.65  Tv'3(-++01'&'&'&''&7676&##"5543!22##"5543E1$J  ^ %()+DL  *" 2X !  #% E!!Jc >;4+ ++и//*/+*и/*'и'/*31и1/*7ܸ3;и;/01%##"55###"54332343326#'"&554633354332676c&z%%z&<?97 8?I(-0; ^[`g'3(-++01'&'&'&''&7676&##"5543!22##"5543S4?  ^ %),'DK x "&! 2X !  #'J !! T'3(-++01'&'&'&''&7676&##"5543!22##"5543F1"J  ^ %("%?F  $%" 2N  !   5  lp<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+3$+aY+N+H?+$и/$!и!/36и6/01#"&'&&54676632#"##"&554332336276676%#!"&55463!24&#"3267##"&554332T9%$::$%9  &U/)O" '  h k _/--11--/  7!  "#  #F$)  $  # rw2~3//#и/3и/+и/и#/и//#4EX&/&>Y/++& 01#!"&55463!26554&#!"&55463!2#!"3!2w 0$,  #/C #*8*#0(+rcBX4+,+KP+ и /KZ /M/GT+/+ и/Tи/G$и :7и7/:=и=/ NиN/01#"##"&'&&55463326554&##"&5546332##"33267667646332##"54&##"&5?  !,  !, (" 2 0$  p <)*#8)+ #(qcWgI%+A-+[+ c+%5и i/9/`+:1+*D+:и/и/и`Lи`PиP/`RиR/1XиX/01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"332776766367"3326554&#B +,6 $O",   !-  i  w t ++ *   :))#7)+    jb_iûR.+J6+a++aи.>и>/hиk //B//'/C:+3M+`+C и /Cи/'и/'$и$/'dUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"33276676727332655  '( ,! #J +    ,e  ] n gd'%  <* ,"9*/  wi iiBl5+-+-и/!и!/,Bи-OQ/EX0/0>YEXL/L>YEXZ/Z>Y9 +&+ и/09<и 3q  9+("6*)  =  .&+NE  -'&(  +$JptBf3+++`I+и/`W//E/EX./.>YEX^/^>YNS+E и /.Sи/N#иEd6иXиY01##"&'&&55463326554&##"&5546332##"332676676636##"&5546332##"32##332,6 $N",    -G!  M + +  q   9*)#7)+  /++ ?  @ qvcgٻU1+M9+d+e+1AиA/i/E/ /'/*/6P+=и/ и=и/ +и+/FиF/XиX/\и\/_и_/dи=fиf/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33263667672735#o.5 95 6 P  -  !,G  UU    =*- # ;)0  lsC[iuc5+-+pP+Dj+!и!/APP]AP)P9PIPYPiPyPPPPPPP ]Ajj]Aj)j9jIjYjijyjjjjjjj ]DEX0/0>YEXm/m>Y9J+x+&+g^+Jи/J и /mm1и1/9<иYEXY@+<01"&'&&55463323546332#7''&67674323'&'%332655%(, , %u71^  KE% N  x&  %ne^J Jrt#3+!+ ++01#!"&55463!2#!"!2#!3!2t ,!!,    *) ! = ! A S^/=KL//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/M+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2::::D' S**T!''!R*+S! '\ _ F& '( '~ INA ,e-/&/-и/&!.)/#/ + и/и/и/016###"&55463327543326766##"54332Wh12)#!6d))4! #a`L_^+ +01#!"&5543323!2_ /)  r#*|NA ,e-/&/-и/&!.)/#/ + и/и/и/012##"&55463327543326766##"54332Wh03)8f))R"  "][NNA ,e-/&/-и/&!.)/#/ + и/и/и/016###"&55463327543326766##"54332Wh03)8f))#" #baj J m(&/01%'&'&'''&766743C C&%U*  |\E*)+|V' 5 !;+9' #9 %oN;#M$//$и/ + ии012354633232#!"&5546335463+  mmm!!m;h#M$//$и/ + ии012354633232#!"&5546335463m+   h cc c""c ;M // и/ +и и0154332354332!2#!"&55463F' (  BZZZZ#$,]6B7<+' +01'&&'&'&'&''&6766766766#!"5543!22##"5543M#d553799p3  ;GE}( R ɖ]J. (-,W B   *< # d0    ;z# + /+01%2#!##"&5!"&55463 w*| # #`K1=27+ +01'&&'&'&'''&676676&#!"5543!22##"5543?#]222EQ hs R6*9 %&O'#u  ) " J2" ;'  j  Q7C8=+%+01'&&'&'&'"&''&76676676&#!"5543!222#!"5543I "[00/+"JC k! r )7 #%?H&J    " $   ?x'3(-++01'&'&'''&76676&#!"5543!22##"5543j   k $)%$>P  '! -Q  !";!!Cb By?#+ ++ иD/2 + +2&$и$/&(и2/и//&>и>/01%4332##"55#"5543##"54332"&55'"&55463676676#p%% ''4/92 9m:>x6 8@'&  W X o "  r jcz# + /+017463!2##"554&#!"&5 0) o$'rbj+U,/ /,#и#/- +и  и'01%46332##"554&##"&5%46332##"554&##"&5H 0#  /$  ^"* #"* f3#&++&/"/+01&76'4632'&'"'46332##"554&##"&5 , -w r/.g  /# x d s+ 4][> #)lv -9.3+!+01'&&'&'&'"&''&76676&##"5543!22##"5543 2 &Y3 O/  C."#)2    %" H) ! 3) A  t"KQ<5+B2++2(и(/2,и,/2/и//B?и?/01'&'''&67676&##"554332""#"&#&&5543323232676676 <:F]W < 7= {    #. .) ) &  p/7(! 4T # 8()"* q,)5*/++01'&'&'&''&7676&##"5543!22##"5543 D3$4  _ $)'#+ ++ иC/2 + +2($и$/(&и&/2/и//2<014332##"55#"5543##"54332"&55'"&55463676676#t&& ''6292 9m:Bs7 :?F/ " R " s"    x l<JVc/(+Q +K+A ]A ) 9 I Y i y ]AKK]AK)K9KIKYKiKyKKKKKKK ]T+3$+aY+H?+N+$и/$!и!/36и6/01#"&'&&54676632###"&554332332676676%#!"&55463!24&#"3267##"&554332f:#$::$#:  %U/)O" '  f l ]0,-11-,0  7!  "#  #F$)  %  # {kq!++ +01#!"&55463!2#!"3!2k ,,   w*h* ! J kx2q3//#и/3и/+и/и/#/и//#4/++ &+01#!"&55463!26554&#!"&55463!2#!"3!2x /#- #-5! #*'*! #* pcCY7+/+LQ+#и#/L[ / /N/;O+HU+2+O и /Uи/H'и;>и>/01###&&'&&55463326554&##"&5546332##"32636676746332##"554##"&5?   %R!,  !,F" 2 0$  q **#() $)scWgH$+@,+[+ c+$4и i`+90+)C+9ии/и`Kи`PиP/0XиX/01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"3326776766767"3326554&#B +,6 t",   !-  F  w t [**)   ') ##)    ib`jR.+J6+b++bи.>и>/iиl/ //B/e'+C:+3M+a+C и /Cи/'$и$/eUиU/01"&'&&55463323546332#'##"&'&&55463326554&##"&5546332##"332676676727332655  '( ,! #J +    ,C#  ] n  UT '& +*$(+  fX XfBl6+.+TO+.и/"и"/AOO]AO)O9OIOYOiOyOOOOOOO ]TVиV/iOT9TnQ/: +'+1+ и/:<иY +01%3!2655"&'&&5546332!546332#5  +*!+33 86&c;EԻ=++-%+=A%%]A%)%9%I%Y%i%y%%%%%%% ]-*и*/-/и//:%-9Dи-G'/EXY@+<:<901"&'&&55463323546332#7''&67674323'&'%332655 %'- - $u71^  OJ% N  v(  $me[G Gp; //01''&6766'43276674632'&'&''&'&'eX / @8B</  ZL d1-^T+ 5 " )jD 2X*^= ;b,; A6 (9 Cjkz/˸0/ /0и/A&6FVfv ]A]A ]A ) 9 I Y i y ] $1*++0132676654&'&&#"4676632#"&'&&1+$#X,)W&%//%&W),X#$+K3-+vB<**5  ;;z#I$//$ и / / /+и01"&5###"&5#"&55463!2###s * Q   ## ;0 +012#!"&55463  0" "; +01%2#!"&55463  "";!^ +012#!"&55463  ^!!I@ +O+//%+и/и/и/%)и)/01%##"543326#"##"&55463326766@ )) JJ &4;D%J#I  "NA $3+&!// +и/012##"&55463326766##"54332Wh9v78f))k# #R  I@ )C+//$+и/и/$'и'/01%##"543322#"##"&55463!2766@ )) JJ &4 JGM ! Y0;]92+(+(9и/и/("и"/(%и%/(*и*/01''&766767####"554326766326767277676&#!"5543!2925]  U/ :>?4  C.zVKJ1 # ,pC$' " IN # #k,6s4-+%+ %9и/и/%и/%"и"/%'и'/%)и)/01''&676767###"55432676632676763676#!"5543!2&ûZ  <>@6   K1 N BY " .0 # #k,7]5.+ + 9и/и/ и/ #и#/ &и&/01"''&6766767###"55432672672672727676#!"5543!2&˱ Jz0<>@6   K1 J" R/ ! 36 # #k-6Q4-+%+%9и/%и/%"и"/%'и'/01''&6767###"55432676672676767676#!"5543!2& 800bZ<= @5  J1!Bp22X* ! Ph! ', $ "lP1Q/'++9и/и/и/!и!/01''&766767###"5543267267673676&##"55463!2  Bn( /Z7 5s3,+$+ $9и/и/$и/$!и!/$&и&/$(и(/01''&676767##"55432676632676763676#!"5543!29ûZ :< A5  J0!N BY " -1 # #|>6]4-+%+%9и/и/%и/%"и"/%'и'/01''&6766767#"#"55432676672676363676#!"5543!29ʱH|/ :< A5  J0!I ! Q/ " 24# "|@7]5.+ + 9и/и/ и/ #и#/ &и&/01''&6767#"#"55432676632676763676#!"5543!29 701bZ :<@4   J1"Bo31Z* " Ph )- " #*H ?+  и /?и/?AиA/ JEX1/1>YEX4/4>YEX7/7>YEX:/:>YEXYEX>/>>YD+:A]A(8HXhx ]"и"/$и$/%и%/(и)01463!2''&&76767#####"5543327327327267276776'&&#!"&5   )5>"J#p 2 $H%!: 6     +n::l*  "415 " >8+;L# +/ + 01%546332!2#!"&55463+   M ""!D =+A==]A=)=9=I=Y=i=y======= ]=9и9/=;и;/ F@+5%+%и/% и /%#и#/5+и+/5/и//52и2/57и7/59и9/01463!2''&76767##"###"55433267327267676367654&#!"&5   '3 b782Mu"J": 3      F##@   " ";# +/ + 01546332!2#!"&55463*  M} }!!!@û 9+A99]A9)999I9Y9i9y9999999 ]96и6/ B<+3%+%и/% и /%"и"/3+и+/3/и//35и5/01463!2''"76767######"5543326332676737654&#!"&5   '3 1h82Mu"J"_3    K&'C # "  $" A :+A::]A:):9:I:Y:i:y::::::: ]:6и6/:8и8/ C/=+2"+"и/"и/"и/2(и(/2,и,/2/и//24и4/26и6/01463!2#'&76767######"55433267327263676367654&#!"&5  (3 1h82Mv!K!: 4     :~2 ! !  |N?u 6+  и /6и/68и8/;+3%+%и/%"и"/30и0/35и5/01463!2''&76767###"55432632672676776'&&#!"&5| w '2; > ;! > 3 (X-,U% &+$# ,+#@s >{?/ /и?:и:/)и)/@ //9,++9и/,)и)/,/и9601####"5433232236676676#"'"&554632735463 ((  "P)&JCQO((LI)NIE}y   #  " U9 2+A22]A2)292I2Y2i2y2222222 ]2и/205+. + и/ и/.(и(/.+и+/.0и0/01463!2''&767###"5543267327267676'4&#!"&5 k  ' 2 8: 7: 4  /y@ 02 " "#|T< 3+  и /A33]A3)393I3Y3i3y3333333 ]3и/358+1#+#и/# и /1.и./13и3/01463!2''&767###"5543263267267676'4&#!"&5 k  ' 1 8: 7; 4   9 A 25 " %$U8_ 1+/4+. + .9 и/ и/.+и+/.0и0/01463!2#'&767##"#"55432672672676754&#!"&5 k  ( 2 8: 7: 4  2n<-- # @r>û 5+  и /A55]A5)595I5Y5i5y5555555 ]5и/53и3/57:+-%+%и/%"и"/-*и*/-0и0/01463!2''"76767####"5543267267677656'&&#!"&5r )  )-&+ *K/ .  P*+/V%%+%& " *$@sb Ay =+ ++и//=2++и/=!и!/2/и//2501##"5###"5433234332276676#"'"&5546335463b'y%%y' :t.<<A! @bD|J   " " ; 2+  и /A22]A2)292I2Y2i2y2222222 ]2и/24/7++#+#и/# и /+(и(/+.и./01463!2#'&&767###"55436726767636'&&#!"&5  $  , %& *F- -   65 &/ # Jc >;4+ ++и//EX7/7>Y= ++7 ܸ'и'/*и=1и1/=3и3/=;и;/01%##"55###"543323543326##"'"&554633354332676c&z%%z&=>97 8?E*11<ճW $ !. '+  и /A'']A')'9'I'Y'i'y''''''' ]'и/'%/*+01463!2#'&767"554367676'&&#!"&5 0 ' 1;4+ ++и//7*++*'и'/*=1и1/=3и3/=;и;/01%##"55###"54332343326##"&554633354332676c&z%%z&=>97 8?E*11<; 20 # ": 3+  и /A33]A3)393I3Y3i3y3333333 ]3и/31/6++#+#и/# и /+(и(/+.и./01463!2#'"&767"#"##"5543672676736'&&#!"&5  $ , %& *F- -   ,6 %+ " NsA 5m6//61и1/и/7//0#+0и/# и /#&и0-01##"543322636676676#"'"&554632735463A))*#O('JCQO()KI)H}X   #  " ;>#I$//$и// /+и0123546332!2#!"&5546335463g + 7  >  "" ;#M$//$и/ + ии0123546332!2#!"&5546335463q+ A    "" )!@˻ 9+A99]A9)999I9Y9i9y9999999 ]95и5/97и B<+."+"и/"и/.(и(/.+и+/.2и2/.4и4/01463!2''&76767######"55433272673267673676'&&#!"&5 ' +k"K#o 2$#L `8 "  Oa (/(& " -;zB# + /+012#!##"&5!"&55463 w*| B# }#x!8 7+7и/7и/ :6+ +63и3/$и/$и/$"и"/3*и*/6-и-/3.и./60и0/014&#!"5543!2#6767#"####"554332732726767376  / @3 b872N %!; 3 " ") R,+Q)'' " (;# + /+012#!##"&55!"&55463 x+ | ! ![!8 7+7и/7и/ :2+ +2#и/#и/#!и!/2)и)/2,и,/2/и//24и4/26и6/014&#!"5543!2#6677##"###"5543327327263636376  /?3 b872N %!; 3 # !*Y/0TR.  " ,;Hh# + /+012#!##"&55!"&55463 +s h""! 8 7+A77]A7)797I7Y7i7y7777777 ]7и/7и/75и5/ :5+ +5#и/#и/#!и!/5)и)/4*и*/5,и,/4-и-/5/и//52и2/014&#!"5543!2#677#"####"5543327327267633476  / @3 b872N %!; 3 "#* K'(O&H "  %;# + /+012#!##"&55!"&55463 x+ | " ";# + /+012#!##"&55!"&55463 x+ | " ")H@ 9+A99]A9)999I9Y9i9y9999999 ]96и6/3+<+3$и/$"и"/30и0/35и5/01463!2##"76767####"554326326726767376'&&#!"&5 ` *.88 9= /   Y.-S ((, ! ) Is@ ?@// и@и/<A// +(+.9+и/и/(%и%/(+и+/01##"55#"554334332"&5#'"&554632672676676676#@ ) ) )*+NR+)O!R))J  DOFF " Z J"!   M? 6+  и /6и/68и8//;+3$+$и/$"и"/3-и-/30и0/35и5/01463!2#'"76767#####"554326726726767565&&#!"&5 X  ).89 9; 0   C!$A !! " "MA ?w@// и@и/<A/// +%;+;и/;и/%(и(/01%##"55#"554334332"&55##'"&554632676676676676#A) )&+*QN*)N$R(*K DUKD+ " ! "  ]M?K/4+;+42и2/$и/$!и!/2/и//01463!2#'"7677"##"#"5543267263267676765&&#!"&5 X  ( .8< 9; 0  G&&I $B "  $MEA ?@// и@%и%/A//=+:,+ ,и/,&и&/,(и(/,*и*/:4и4/:7и7/01%##"55#"5543343326##"&55"##"&5546326726766766A) )  DULH+ *)+'>O*)O$W*+JT_ #  !  " M? 6+  и /6и/68и8//;+3%+%и/%"и"/3-и-/30и0/35и5/01463!2#'"76767###"554326726726767565&&#!"&5 X  ).8< 9; 0  =;  " k6: 3+A33]A3)393I3Y3i3y3333333 ]3/и//31и1/*+6+*"и/*'и'/*-и-/*/и//01463!2##"76767###"5543267267676565&&#!"&5k &) * /X4 *]/ *   R**J % " " &|9m 0+  и /0и/02и2//5+*"+"и/*'и'/*-и-/01463!2#'"76767##"55436326767565&&#!"&5  ) '0W4 (_. '   C!#@ ! " "8 1+  и /A11]A1)191I1Y1i1y1111111 ]1и/1.и.//4+)!+!и/)&и&/),и,/01463!2##"76767##"5543632676756'&&#!"&5  '% 0N6 *V. '  9"#6  #  9 2+  и /A22]A2)292I2Y2i2y2222222 ]2/и///5+*"+"и/*'и'/*-и-/01463!2#'"76767###"55436726767565&&#!"&5 ' '0W4 (_. '  0!#,  "  Is@ 78//8и/49//!+и/!и/!$и$/'2и2/3и3/01##"54332"&5"'"&554632676676676676#@ )) *)*+NR+)O!R))J  DO} H !"    MA .:/;/4/;и/+4/<7/01%"&55#'"&554632676276676676#"#%##"54332l (**NN)*P&S((J "N())))  "" &MNA /; sA#3+!+ ++01#!"&55463!2#!"!2#!3!2A +!!+J  C A  ))  C # D T#3+!+ ++01#!"&55463!2#!"!2#!3!2T ,,K  D A ** ! / # 0 n#3+!+ ++01#!"&5463!2#!"!2#!3!2 +!!+, & # ,* * " Y " Y #3+!+ ++01#!"&55463!2##"32##332, ,  ** ! 4 " 5 q#3+!+ ++01#!"&55463!2##"32##332, ,  )) ! < # = #3+!+ ++01#!"&55463!2##"32##332, ,  *+  ) # + L-#3+ ++!+01#!3!2#!"&5463!2#!"!2  + +    c !** " b ~%#3+!+ ++01#!"&55463!2#!"!2#!3!2% ..   ** J ! M %#3+!+ ++01#!"&55463!2#!"!2#!3!2% ..   **  7 ! 9 RS#3+!+ ++01#!"&55463!2#!"!2#!3!2S + +a Z W `** " L # L J#3+!+ ++01#!"&55463!2#!"!2#!3!2J ++K  C @ *+  0 " 2 T#3+!+ ++01#!"&55463!2#!"!2#!3!2T ,,K  D A *+! 7 # 7 T#3+!+ ++01#!"&55463!2#!"!2#!3!2T ,,K  D A ++  0 " 1 rY#3+!+ ++01#!"&55463!2#!"!2#!3!2 +""+    f *+ ! E # G #D+EX / >Y ++ 01#!"&5546332##"32##332 + +    ** ! , " - #3+!+ ++01#!"&5546332##"32##332 + +    ++  0 # 1 #3+!+ ++01#!"&5546332##"32##332 + +    ++ ! " " #  Gy+3{4/./и/4и/11+)+и/1и1и/ и1,и,//01##676##"&5546333#"&55463!267#76fIB6G&%O$I(%-e--'% <  #  "  -I--5{6/0/и/6!и!/33+++и/и3 и /"и3.и./101##676#"'"&554633#"&55463!267#36bPF:  E%%M$H(&Krk '(&    90S *23/-/3и/0++(+и/и+и/и+-и-/.и+0и0/01##676#"'"&554633#"&55463!267#36aR@: 9IG&'I sl$!""    T  MIH-5{6/0/и/6!и!/30+++0и/и/и"и0.и./101##676#"'"&554633#"&55463!267#36bPF:  E%%M$H(&Krk '(&  !  ,#<~7(/0/+/0и/.+&+и/и/и)и)/+и+/,и.01##67676'"&554633#"&55463!267#6 A6lI8; -5Y "$ H #      =A+/,34////и/4 и /2+*+и/и/ и /!и-и-//и//0и201##67676'"&554637#"&55463!267#6 @ 74 8~:*/U * #  !4 -A/,23////и/3 и /2+*+и/и/ и /!и-и-//и//001##67676'"&554637#"&55463!227# @ 74 8~:*/U  # !H @AF/,34////и/4 и /2+*+и/и/ и /!и-и-//и//0и201##67672#'"&554633#"&55463!267#6 @ 74 8~:*/U * " !, $Py+3s4/./и/4и/1.+)+.и/и/ и.,и,//01##676##"&5546333#"&55463!267#76fNE8E%&O$G% "3j *+& <  #  "  -S/(0s1/+/1и/.+&+и/и)и)/+и+/,и.01##676##"&554633#"&55463!267#32aR@: 9IG& sl$!"" ! ; 5S *23/-/3и/0++(+и/и+и/и+-и-/.и+0и0/01##676#"'"&554633#"&55463!267#36aR@: 9IG&'I sl$!""    T  MSI*23/-/3и/0++(+и/и+и/и+-и-/.и+0и0/01##676#"'"&554633#"&55463!267#36aR@: 9IG&'I sl$!""  !  +$ic E // и/ и!// +01%##"5#"5543354332##"54332&k k&&&F $ qb 'W+!+ и ии)$//++015#"5543354332##"5#"5543##"54332pi i '' i [&&Z #  # ~B#W$/!/$и/  ++ии и"01##32#!"&554633#"&55463!2!!/ksq e? ! 6   6/#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#at zf >\ !    /#~$/!/$и/ EX/>YEX/>YEX / >Y+  ии"01##32#!"&5546335#"&55463!235#at zf >\ !   / #W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#at zf >\ !  N1#W$/!/$и/  ++ии и"01##32#!"&554633#"&55463!23#sTh gU  "    d|#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#hJ_ hT    `w#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#dL_ hT   d|#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#hJ_ hT  "  ۮB&5#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#"I]* ^J Гj "    Q#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#7KR T?  "   Q#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#7KR T?  !    Q#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#7KR T?    xB#W$/!/$и/  ++ии и"01##32#!"&554633#"&55463!2!!/ksq e? "    /#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#at zf >\ !  /#~$/!/$и/ EX/>YEX/>YEX / >Y+  ии"01##32#!"&5546335#"&55463!235#at zf >\ !  / #W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#at zf >\ !  dn{#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#hK_bN  !  mt#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#_J_aL !   ްx}#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#iJ_ `L  "  z#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#lJ_aL ԧOg#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#>RT T@  !   Y#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#7Ke M9s{{ "  ްY#~$/!/$и/ EX/>YEX/>YEX / >Y+  ии"01##32#!"&5546335#"&55463!235#7Ke M9s{{ !  Y#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#7Ke M9s{{ !  ͠/#W$/!/$и/  ++ии и"01##32#!"&5546335#"&55463!235#at zf >\ !    N*/=K;L//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/>и>/ AиA/ DиD/ HиH/+I@+;2+*+01%4&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!26!88!6D!P..O!!O..P!K & 3323)GG*+H H   $ N %=K/L//A]A)9IYiy ]L2и2/A&6FVfv ]A]&)и)//и//5и5/;и;/>и>/AиA/HиH/ ,+I@+ +8+01#!"&55463!24&'&&#"3267667#"&'&&54676632#!"&55463!2 &5!66!5C!O..N N..O!E T   (  (''%:<#$<<3" / +и //+01####"5433232 * * q# + /+017463!2##"554&#!"&5 .( & #)p+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5 /% /$ $)$$)g34/ /A ]A ) 9 I Y i y ] 9и/и/4+и+/&/"/+01&'&''&7674633246332##"554&##"&5co2,j  . .t N /$ 4`\A ]# }( #)N 3K'L//A]A)9IYiy ]и/L@и@/(A(&(6(F(V(f(v((((((( ]A((]и/(и/47и7/(=и=/(CиC/IиI/.:++ +F"+01#!"&55463!2'#!"&55463!24&'&&#"3267667#"&'&&54676632 & 5!66!5C!O..N N..O!<  g #d++++&@@&%@@tt"IE:3+@/+ +/,и,/@=и=/@CиC/01%'&'''&67676&##"554332#"##&&5543323232676676/E  E0CcD ;  #L0*) &  L#0$0=(  8p # ,!* N 3K'L//A]A)9IYiy ]и/L@и@/(A(&(6(F(V(f(v((((((( ]A((]и/(и/47и7/(=и=/(CиC/IиI/.:++ +F"+01#!"&55463!2'#!"&55463!24&'&&#"3267667#"&'&&54676632 & 6 66 6C!N..N!!N..N!g  Q!# $$   $"99""98`l3AMZ&+H +B+A ]A ) 9 I Y i y ]ABB]AB)B9BIBYBiByBBBBBBB ]K+3+XP+?6+ E+и/и/3)и)/3-и-/01#"&'&&54632"###"&554332336676676%#!"&55463!24&#"3267##"&554332JWH$:UHHW (Y0)S& '  ~ s d0--11--0  6,>%.<Y/++& 01#!"&55463!26554&#!"&55463!2#!"3!25  /#- , #,p $*5*%$8*) q*CY7+/+LQ+#и#/L[ / /N/EX2/2>Y;O+HU+O и /2Uи/H'и;>и>/01###&&'&&55463326554&##"&5546332##"3267667646332##"554&##"&5   %Q !,   !,F" 2 /$  q 6)%#3)'   ")q)XhI%+A-+\+ d+%5и j/9/a+:1+*D+:и/и/иaLиaPиP/aRиR/1Y01%46332##"&'&&5##"&'&&55463326554&##"&5546332##"3326727766367"3326554&#, +5 $O",  ",E   x t p*+ *   2)" 0)#    j!_iȻR.+J6+a++aи.>и>/hиk //B//EXM/M>Yd'+C:+`+C и /Cи/'$и$/M2dUиU/01"&'&&5463323546332#'##"&'&&55463326554&##"&5546332##"33276676727332655N '( ,  "J!+~   - e   ] n  cb '& :*($6+*  tf fiBl5+-+-и/!и!/,Bи-OQ/EX0/0>YEXL/L>YEXZ/Z>Y9 +&+ и/09<иYEX^/^>YNS+E и /.Sи/N#иEd6и6/d:и:/d<иY=и/ и=и/ +и+/P5FиF/XиX/\и\/dи=fиf/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33263667672735#W07 96 8 O!, "+ F  VV    6*& # 4))  meBZht;5+-+oO+Ci+!и!/AOO]AO)O9OIOYOiOyOOOOOOO ]Aii]Ai)i9iIiYiiiyiiiiiii ]C9I+w+&+Ul+f]+Iи/I и /l0и9;и;/eиIr01##"&'&&55463326554&##"&5546332##"3327767672%#"&'&&546766327#!"&55463!24&#"3267##"&554332  #Q .  !+k  Z:$$99$$: a X0--00--0  p /)#+*!  !  "#  #p %  ' |%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5B  #!+  ,^ Z n n *) { &_'//'и/ и /ии#(// ++01%3!2655"&'&&5546332!546332#)  V++ ,(YY WV%c;Eۻ=++-%+=A%%]A%)%9%I%Y%i%y%%%%%%% ]-*и*/-/и//:%-9Dи-G'/EX:/:>YEXY@+<01"&'&&55463323546332#7''&67674323'&'%332655 %'- - $u71^  KE% N  x&  %ne^J JmAq +A&6FVfv ]A]и/= 9/ /01''&676676674323276654632"'&'&'&'&'&'3F 0 B<@=/  ++R  `0 ._Y* S ! 2E,  9d$2oA  A34$ 2@FJ4? $Br+/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+01%4&'&&#"3267667#"&'&&54676632$P+0PP0+P$G/&(iD0/CG,-GG-,G#=i 2N;22(GF()HH   }%Y #1?@//A]A)9IYiy ]@и/A&6FVfv ]A] 2и2/8и8/ +=4+/&++014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332>?>??>?>AG-.GG.-G^ c |(;:)(>>(9989 W"`c I// и /ии//+01%##"55###"54332354332c&&&&rb# + /+017463!2##"554&#!"&5  / )  ")qc+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5M 0$ /$  #'##'g34/ /A ]A ) 9 I Y i y ] 9и/и/4+и+/&5/"/+01&'&''&7674633246332##"554&##"&5o2-i  . 0s M 0$  4`\A ]" }( #)Y 3KL//A]A)9IYiy ]и/L@и@/(A(&(6(F(V(f(v((((((( ]A((]и/4.:++ +F"+01#!"&55463!2'##"&55463324&'&&#"3267667#"&'&&54676632^ c 0 00 0AG./FF/.GC   Z!&&&& =< <<t"IE:3+@/+ +/,и,/@=и=/@CиC/01%'&'''&67676&##"554332#"##&&5543323232676676X/G  E/DcC ;   #M0*) &  L#0$0=(  8p # ,!* Y -EF//A]A)9IYiy ]и/F:и:/"A"&"6"F"V"f"v""""""" ]A""]и/.(4++ +@+01#!"&55463!2'##"&55463324&#"3267667#"&'&&54676632^ c ?==@/. BG./FF/.Gc   J ""54#  7766{k!++ +01#!"&55463!2#!"3!2k ,,   w** ! t  |b%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5d !+ !+> ] p p * + |c &_'//'и/ и /ии#(// ++01%3!2655"&'&&5546332!546332#T  <++ ,'Y Y \Y%mAq +A&6FVfv ]A]и/> 9/ /01''&676676674323276654632"'&'&'&'&'&' 5D / B=@=.  *+R  a/ ._ Y* S ! 2E,  9d$2oA  @24$ 2@ FJ4?$Bre/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+01%4&'&&#"3267667#"&'&&54676632* "T+.U %% U.+T" *G3**n<@o)*--*)o@и>/ AиA/ DиD/ HиH/+I@+;2+*+01%4&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!26!88!6D!P..O!!O..P!K & 3323)GG*+H H   $ QsA '+ // +01##"5#"554334332A** # f/=KL//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/>и DиD/+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2 4 66 4C"M,.M M.,M"J y(  (''%:<#$<<   Y"dB '+ // +01%##"55#"554334332B* * $ f 3KL//A]A)9IYiy ]и/L@и@/(A(&(6(F(V(f(v((((((( ]A((]и/47и7/(=и=/(CиC/IиI/.:++ +F"+01#!"&55463!2'#!"&55463!24&'&&#"3267667#"&'&&54676632  6 44 6C N.-NN-.N =  h!d,,,,&??&#AAdEB '+ // +01%##"5#"554334332B* *T0 $ >g 3KL//L@и@/(A(&(6(F(V(f(v((((((( ]A((]и/A]A)9IYiy ]47и7/(=и=/(CиC/IиI/.:++ +F"+01#!"&55463!2'#!"&55463!24&'&&#"3267667#"&'&&54676632   6 55 6C!N-/N!!N/-N!g  Q!# $$   $"99""98dB '+ // +01##"55#"5543354332B* *  # sb !E"//"и/и#//+01##"5#"&5546334332##"54332(   ( (( Xo }b !E"//"и/и#//+01##"55#"&55463354332##"54332&dd& (( # b !E"//"и/и#//+01##"55#"&55463354332##"54332&dd& (( -#1 b !E"//"и/и#//+01##"55#"&55463354332##"54332&dd& (( #WsA E+ ии// ++01##"5#"554335#"554334332A) )/ ! # OrB E + и и// ++01%##"55#"554335#"5543354332B*} }} }*b # _ # rNB E + и и// ++01%##"55#"554335#"554334332B*} }} }*\ " z " rB E + и и// ++01##"55#"554335#"5543354332B*} }} }* < # Y " sb '_+!+ и / ии)$// ++01##"5#"554335#"554334332##"54332&S SU U&''X $ { $ G} b 'W +!+ и ии)/$/ ++01%##"55#"554335#"5543354332##"54332' YYYY '&&G " ] #  b '_+!+ и / ии)/$/ ++01##"55#"554335#"5543354332##"54332& XX__ &&&+/ # L # /  b 'W +!+ и ии)/$/ ++01%##"55#"554335#"5543354332##"54332' YYYY '&&Q $ c # 4Z #1=>//A]A)9IYiy ]>и/A&6FVfv ]A] и/и/и/!и!/ ? +27+/&++014&#"3267#"&'&&546766327#!"&55463!2'2##"5543cXXddXXcN&!#b==a"#&&#"a==b#!&aL o3>=42??2&>>&&@?  ;# ++ 01%546332!2#!"&55463+  N ##8S 1I$J//A]A)9IYiy ]J>и>/&A&&&6&F&V&f&v&&&&&&& ]A&&]25и5/&;и;/&AиA/GиG/2KEX / >Y,8++D + 01#!"&55463!2'2##"55434&'&&#"3267667#"&'&&54676632SY8C((CC((CM'!#_98_"#''#"_89_#!'r ! x   0000#S/=IJ//A]A)9IYiy ]J$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/K+>C+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'2##"5543C((CC((CM'!#_98_"#''#"_89_#!'`Y   55 53   y  ;H#++01%54332!2#!"&55463(  ]]""iS #1=>//A]A)9IYiy ]>и/A&6FVfv ]A] и/и/и/!и!/ ? +27+/&++014&#"3267#"&'&&546766327#!"&55463!2'2##"5543cRSddSRcN%"#_:;`!!&&!!`;:_#"%`Y !!,,,+  r;#++0154332!2#!"&55463(  /C C""O1/=K L//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+I@+*+;2+014&'&&#"3267667#"&'&&546766327#!"&55463!2'#!"&55463!2:;;:E( S**T"(("S**S! (\ _ T# $# "o AU)7EF//A]A)9IYiy ]Fи/ A & 6 F V f v ]A ]и/ и/ !и!/'и'/ >и>/+C:+5,+$+014&#"3267#"&'&&546766327#!"&55463!2'#!"&55463!2UA77AUC!O//NN//O!?   g2: )) 95%==%$><  \#@s @A/ /иA<иY+I@+*+:2014&'&&#"3267667#"&'&&546766327#!"&55463!2'##"&55463326556E%O+*N$$N*+O%7 ***  *  NH 3{4/ /и4)и)/05 // /+ и/ ) ,ܸ)0и0/)2и2/01####"54332326##"&55463!54332676  ))   Qc ) !h   !J G`k3AMZ&+H +B+A ]A ) 9 I Y i y ]ABB]AB)B9BIBYBiByBBBBBBB ]K+3+XP+?6+ E+и/3)и)/3-и-/01#"&'&&54632"###"&554332336676676%#!"&55463!24&#"3267##"&554332IWI#:UGIW (X0)R& (  ~ s d0.,11,.0  6,>%.<и>/iиl //B//e'+C:+3M+a+C и /Cи/'$и$/eUиU/01"&'&&55463323546332#'#"##"&'&&55463326554&##"&5546332##"332676676727332655M'',! "K!+~ !,D#  \ n  YW'% 0*#,+!  i[ [fB`o6+.+"и"/.KM/: +'+1+ и/:<иY=и/ и=и/ +и+/P5FиF/XиX/\и\/dи=fиf/01%##32#!"&5546335#"&55463!2##"&'&&55463326554&##"&5546332##"33263667672735#W07 96 8 O!, "+ F  VV    6*& # 4))  ldBZht;4+,+oO+Ci+ и /AOO]AO)O9OIOYOiOyOOOOOOO ]Aii]Ai)i9iIiYiiiyiiiiiii ]C8I+w+%+Ul+f]+Iи/I и /l/и8;и;/eиIr01####"&'&&55463326554&##"&5546332##"33267667672%#"&'&&546766327#!"&55463!24&#"3267##"&554332  t!,  "+G"  [9%$99$%9 a Z/-,11,-/  q /*#,+     ""  "p $  # c;Eۻ=++-%+=A%%]A%)%9%I%Y%i%y%%%%%%% ]-*и*/-/и//:%-9Dи-G'/EX:/:>YEXY@+<01"&'&&55463323546332#7''&67674323'&'%332655 &'- - $u81_  KE% N  x&  %ne^J Jr4#3+!+ ++01#!"&55463!2#!"!2#!3!24 + +7  #  **  9 "4 V &4BC//A]A)9IYiy ]Cи/A&6FVfv ]A])и)/2и2/ +@7+2)+!+014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332J87K.8JAG*)HH)*Gi d a.65/& 51$9:#%::  T#@sb D @+ ++и//2++2@ и /@"и"/25и@<ии>/01##"5###"5433234332227676676"'"&55463275463b'y%%y'< #>=#9w:2:*-cD}w # !z\7 #1?@//A]A)9IYiy ]@и/A&6FVfv ]A] 2и2/8и8/ +=4+/&++014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332B32DD23B@B(&BB&(B.^q #%%#"(("0000  G\ #1?@//A]A)9IYiy ]@и/A&6FVfv ]A] 2и2/ +=4+/&++014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332D54DD45D<B(&BB&(B.^q &**&$--$11 11  OJIc >u;4+ ++и//7*++*'и'/*=1и1/=;и;/01%##"5###"54332343326#'"&554633354332676c&z%%z&; >97 ;=F*0231??13>?@'&@@&'@3^r#!"" **++  BJc <92+ ++и//5*++* и /*'и'/*19и9/1;и;/01%##"55###"543323543326#'"&55463354332676c&z%%z&=>97 (/.<8 ! "QOIs@ 9u://:5и5/;//9%+%4и/%"и"/%(и4/и42и2/01##"54332267676676#"'"&5546372735463@ )) L+))N  N(*U'Q*,OO*)(} s  #  "w NA ,m-/&/-и/&!.)/ + и/и/и/ #и#/016##"&55463327543326766##"54332Wh.0)"!8f))  GD2(k@~# + /+017463!2##"554&#!"&5 0( q#)qA+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5, 0% 0$  w#)##)f3#&++&/"/+01&76'4632'&'"'46332##"554&##"&5 - .v q0.g  / $ x d s+ 4\Z> #)NFA ,e-//-!и!/(.//$+*и/*!и!/*(и(/01%##"543326##"&55463327543326766A))Wh.0)"!8fT b au"KM<5+B,++,/и//,2иB?и?/BEиE/01'&'''&67676&##"554332""#"&#"&5543323232636676k ?8D_K <3B {   #/ /) ( '  o5<(  9e " -2-$) NA*m+/$/+и/$,'/ /!/  ܸи/и/ "и"/012##"&55463!543326766##"54332Wh(%#8f))G! "KI=|M!++ +01#!"&55463!2#!"3!2M -- 6  v)}*  _ rBx%o&/ /&и/и/ и/  и/и/'++01%"3!26554&#463!2#!"&'&&5M  + ,D = \ \ *z* sA &_'//'и/ и /ии#(// ++01%3!2655"&'&&5546332!546332#8  C,, - H HLN%p; //01''&6766'43276674632'&'&''&'&'dW 0 @8A</  YL d1.]T+ 5 " )jD 2X*^= :b,; A6 (9 CkE/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+014&'&&#"3267667#"&'&&54676632) !R*.R%%R.*R! )G4))l:=n')//)'n=:l))4#   #! !%88%&55r$ +01'&'&'''&7$76&#!"5543!2XI;F  v Q/6?21ACs 5 )b $ -.%   r@5 + и /++017463!2##"55!"5543!54&#!"&5 0(   z"*W ' |y#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#Zj m ?bU "    ;#M$//$и/ + ии01%2!54633232#!"&5546335463Q&+   jj j!!j ;^#I$//$и/ + ии01232#!"&55463354633235463   ,^ s""s ss ;#M$//$и/ + ии012354633232#!"&5546335463m+    ee e  e  Z /=IJ//A]A)9IYiy ]J$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/K+>C+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'2##"5543D**EE**DN'##b:;a"#((#"a;:b##'aL    55 "45  !!;z# + /+01%2#!##"&5!"&55463 w*| " ";f# + +012#!##"&55!"&55463 w*| !_ _!6S/=I$J//A]A)9IYiy ]J$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/KEX:/: >Y+>C+*+:2014&'&&#"3267667#"&'&&546766327#!"&55463!2'2##"5543C((CC((CM'!#_98_"#''#"_89_#!'`Y  4432! q  ;# + /+01%2#!##"&55!"&55463 +s !!>w+ +01#!"&5543323!2> .'  o&*hS %=>//A]A)9IYiy ]>2и2/ A & 6 F V f v ]A ]&)и)/ /и// 5и5/;и;/&?#,++ +8+01#!"&55463!2'2##"55434&#"3267#"&'&&54676632SY7cRSddSRcN%"#_:;`!!&&!!`;:_#"%z  r   !!++,*;<# + +012#!##"&55!"&55463 z+y <"OO"m_(/01'&'&'''&676674322N [F #U ,  @)(\/9,4!H7 % (tH   2 ;D# + +012#!##"&55!"&55463 z+y D" DD "fc%1&+++01'&'&''&63$76&#!"5543!22#!"5543RK).3Hu} (+  "^ $  "! 9  k)7EF//A]A)9IYiy ]Fи/ A & 6 F V f v ]A ]и/ и/ !и!/'и'/ >и>/+C:+5,+$+014&#"3267#"&'&&546766327#!"&55463!2'#!"&55463!2U@ 77 @UD!O./NN/.O!?,0 #" 2,"76!"67 M#Is@ @gA// иAи/=B// +,<+<и/,)и)/01##"55#"554334332"&55#'"&554632672676676676#@ ) ) ))*+NR+)O"T()I  GPB> !   "  r^)7EF//A]A)9IYiy ]Fи/ A & 6 F V f v ]A ]и/ и/ !и!/'и'/EX4/4 >Y+C:+$+4,014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332SB77BSE"P/.O!!O./P"7  "    #+,,,  > UA ?@// и@%и%/A/!/ +=*+*&и&/*(и(/*-и-/*74и4/7:и:/01%##"5#"5543343322##"&55"'"&5546332766766766A) ) DK>A,&*)MA&))L N'(L ! \!  XS  k@p# + /+017463!2##"554&#!"&5 0( c#)rBx+M,/ /,#и#/- / /+и'01%46332##"554&##"&5%46332##"554&##"&5- 0% /$ l"* #"* f3#&++&/"/+01&76'4632'&'"'46332##"554&##"&5 - .v q0-h  /$ x d s+ 4][> #)r: #1?@//A]A)9IYiy ]@и/A&6FVfv ]A] и/и/ +=4+/&++014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332RCBRRBCRE!O1/QQ/1O!7  ('$$...- LM0A BC// иC(и(/!D//:%+ +:-)и)/-+и+/-0и0/:7и7/:=и=/01%##"55#"5543343326##"&55"#'"&5546326366766766A) )  !N)))8+*)+NO*)O"W+*L< = ! M9   ! t"KE<5+B2++2,и,/2/и//B?и?/01'&'''&67676&##"554332""#"&#&&5543323232676676m >:F]X ; 8; z   #- 0*) &  p/7(! 4T # 8()"* r /=K L//A]A)9IYiy ]L$и$/ A & 6 F V f v ]A ]и/ !и!/ 'и'/-и-/+I@+;2+*+014&'&&#"3267667#"&'&&546766327#!"&55463!2'##"&55463326556D$N+*N##N*+N$7  (   ()  '{ CT@ @A// иAи/=B///+ и и и/ и/ и/)&и&/),и,/01%##"55#"554334332"&55'"&554632676676676676#@) )+ &*)ML))L N'(K DMA,&*)&AA&))L N'(L* _  r m   p= //01''&6766'43276674632'&'&''&'&'dX /   8B</  +-L e2.]T+ 6 " )jD 0*,*^= 901 ,; A6 (9 CjE~/˸0//A]A)9IYiy ]0$и$/ A & 6 F V f v ]A ]1+*+014&'&&#"3267667#"&'&&54676632'"S+/S##S/+S"'G1)*l<@n''..''n@11AEs 5 )b $ -.%   r@x5 + и /++017463!2##"55!"5543!54&#!"&5 0(   l"* Q |yx#W$/!/$и/  ++ии и"01%##32#!"&5546335#"&55463!235#Zj m ?bI "   E0&4B۸C//A]A)9IYiy ]Cи/A&6FVfv ]A] +@7+2)+!+014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332J98J08KAH**GG**H33  ',+( -(4444   J"4sb Aq>#+ ++ иC// +2=+=$и$/=&и&/2/и//01%4332##"55#"5543##"54332"&55#"&55463676672#p '' &&10A1:}:>u9  ^nvv " } " "  `j! #1?@//A]A)9IYiy ]@и/A&6FVfv ]A] 2и2/8 +=4+/&++014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332=20??02=@@&'@@'&@4] s )(((}  C`R! #1?@//A]A)9IYiy ]@и/A&6FVfv ]A] 2и2/8и8/ +=4+/&++014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332@21@@12@=@%&@@&%@4] s !""!%%*,+-  BC8b B?#+ ++ иD// +2&+&$и$/&(и2/и//2<>и>/01%4332##"55#"5543##"54332"&55'"&55463676676#p%%  ''  50829o9As99?%%#i>  wj      n`! #1?@//A]A)9IYiy ]@и/A&6FVfv ]A] 2и2/8и8/ +=4+/&++014&#"3267#"&'&&546766327#!"&55463!2'##"&5546332>21??12>?@&'@@'&@4] v &  &&   &r  @  Cb B}?#+ ++ иD/2 + +2($и$/(&и&/2/и//(>и>/014332##"55#"5543##"54332"&55'"&55463276676#t&& ''4/92 7n;Bq9 6A&&)  N$ g     j Is@ 8s9//9и/5://'4+4и/4и/4!и/!$и$/01##"54332"&55"'"&554632676636676672#@ )) 0)*+NR+)OM+)P" GP?>}  # PA +78/1/8и/(1,94/++ + и/ и/ и/и/и/%и%/'и'/01%"&55##"&554633276672676676#%##"54332q()*%> A'**L!O('MDP><**Q    U*9 P6A 78//8и/49//'7+!+и/и/и/'!и/!$и$/1и1/3и3/01%##"54332"&55'"&554633276632676676#A**1()*J A'**L!O('MDP>;EN{ !  PA +78/1/8и/(1,94/+++и/и/ и /и/и/%'и'/01%"&55#'"&554632672676676676#%##"54332q()*J L**L!O('MDP><** R   U )PA 78//8и/49/'7+!+и/и/и/'!и/!$и$/1и1/3и3/01%##"54332"&55"'"&554632676676676676#A**0()*J L**L!O('MDP><[`  e;z#I$//$ и / / /+и01"&5###"&5#"&55463!2###s * Q   "" ;a#M$//$ и / #+и# и01%"&55###"&55#"&55463!2###v+  Q a hh h##h ;"#M$//$ и / #+и# и01%"&55###"&55!"&55463!2###v * Q "  "" ;A#M$//$ и / #+и# и01%"&55###"&55#"&55463!2###v+  Q  ee e""e ;A#M$//$ и / #+и# и01%"&55!##"&55#"&55463!2### , Q  GG G""G ;N +01%2#!"&55463  N##; +01%2#!"&55463  ##;6 +012#!"&55463  6""Is@ /K+//*+и/и*$и$/*'и'/01##"543326#"'"&55463726766766@ ))  N(*U'Q*,OO*)Q!!T))N}n #  ! PA ,Q!&+!.)/$++ и /и/и/016'"&5546326726766766##"54332DM Q)*J L**L!O('M**   O2PHA ,]!&+!.)/#/ + и/ и/ и/и/01%6'"&5546326766726766##"54332DM Q)*J L**L!O('M**  "PA ,Y!&+!.)/ //#/$+$и/$и/и/016'"&5546326766766766##"54332DM Q)*J L**L!O('M**K    BA +//01%##"54332A))8?(+EX/>Y+01##&?%8 ?(+EX/>Y+01!## &? +/+013!  %  +/+01!53  %8  "+/EX/>Y01# & ? +01!5 ?%%/e //01#".54>326K,,K66K,,K6*+H55H++H55H;//и/+ +01!4.#"32>(6K,,K66K,,K6(+H55H++H55H/e'˸(//A]A)9IYiy ]( и /A&6FVfv ]A])#++01#".54>324.#"32>6K,,K66K,,K6(+<$$<++<$$<+*+H55H++H55H+#;**;##;**;+;,//,и/-+ +01!4.#"32>'#".54>32(6K,,K66K,,K6(+<$$<++<$$<+(+H55H++H55H+#;**;##;**; /0/&и&/+A+]A+]A +]A]A]A ]& +9+1//!+01%4.#"32>'#".54>7'%22%%22%h 8)/>##?.)8 42&&22&&2V.;!#?..?#!;./ 23/!и!/A]A ]A]A]A ]A]! и+0и+4/&++ии2014.#"32>###535.54>323%22%%22%h$ 8).?##>/)8 *2&&22&&2L&-;!#?//?#!;-(23//A]A)9IYiy ]3$и$/$и/+и+/4(/01&7>54.'&'&&6676674632<  "&6< %&% * +=&S? 9  &1;"!. +8&&!   !%>6/)S*//*'и'/и)')9к')9)+)/015&'&>76675&'&>7667%& !!  1& !!  1E:J:#<     K#<     V(4})/+A)&)6)F)V)f)v))))))) ]A))]/#/2+,+и0177&'#53667'7675373#'#5&'%4&#"326r^^ rt+&* ts^^st,&.tB>33>>33>Yr.%&stts.%,!rtt1==11==8 L+и /EX/>Y+и 013###53 & %% //01-wUy Q+9 9/ / 9 9 9 901'7'7'7ijjis44r44q #/++A&6FVfv ]A]9/ A]A)9IYiy ])9)/$/,/ +и !0174632#"&7##"543324632#"&7##"543324 n5 PI//и/и///+01###".54>336|65O55N4n3F))F3 #+/+013!53  %%8?0+EX/>Y+01###5&?%%8  4+/EX/>Y+01##533 &&%wty3+9//9901#'7' %ij448 4+/EX/>Y+013## & %//+901'7'7!5_44Mji%//+901!'744M%ij2&+/+01!!-&1% 1/ ///+9 901'7'7#'7_4444Mjiijn/EX/>Y01!nV //01!V) E?+6)+/0+A&6FVfv ]A]/9A6&666F6V6f6v6666666 ]A66])69&)69/EиE//GB+,3++&#+ и/9,0ܸ&9и#;017&&#"326%#"&'#"&546324.'#53&&54632#4&#"3#3267   +0=% 5('03$me DK=O?+&,   6 O /BGR&*&,3.-4 ')&$#V([ba^BIG<%R0$0h$20B  "  N " nH nH " " j j "  JB " 0 6 0 N   Copyright $ 2016 NAVER Corporation. All rights reserved. Font designed by Sandoll Communications Inc.NanumGothicCodingRegular1.000;SAND;NanumGothicCoding;161021_01;Version 2.500;PS 1;hotconv 16.6.51;makeotf.lib2.5.65220NanumGothicCoding is a registered trademark of NAVER.NAVER CorporationDokyung Lee; Minjae Kang; Sang-a Kim;www.navercorp.comhttp://www.sandoll.co.krSandoll Communications Inc.T)tĬ; Ǭ; @D;8: mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgAnimation.cpp000664 001750 001750 00000006276 15164251010 034771 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgFrameModule.h" #include "tvgAnimation.h" Animation::~Animation() { delete(pImpl); } Animation::Animation() : pImpl(new Impl) { } Result Animation::frame(float no) noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (!loader->animatable()) return Result::NonSupport; if (static_cast(loader)->frame(no)) { PAINT(pImpl->picture)->mark(RenderUpdateFlag::All); return Result::Success; } return Result::InsufficientCondition; } Picture* Animation::picture() const noexcept { return pImpl->picture; } float Animation::curFrame() const noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return 0; if (!loader->animatable()) return 0; return static_cast(loader)->curFrame(); } float Animation::totalFrame() const noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return 0; if (!loader->animatable()) return 0; return static_cast(loader)->totalFrame(); } float Animation::duration() const noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return 0; if (!loader->animatable()) return 0; return static_cast(loader)->duration(); } Result Animation::segment(float begin, float end) noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (!loader->animatable()) return Result::NonSupport; return static_cast(loader)->segment(begin, end); } Result Animation::segment(float *begin, float *end) noexcept { auto loader = to(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (!loader->animatable()) return Result::NonSupport; if (!begin && !end) return Result::InvalidArguments; static_cast(loader)->segment(begin, end); return Result::Success; } Animation* Animation::gen() noexcept { return new Animation; } external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-exceptions.cpp000664 001750 001750 00000033373 15164251010 046210 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-exceptions.h" #include #include "ecma-array-object.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #include "ecma-symbol-object.h" #include "jcontext.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup exceptions Exceptions * @{ */ /** * Standard ecma-error object constructor. * * Note: * message_string_p can be NULL. * * Note: * calling with JERRY_ERROR_NONE does not make sense thus it will * cause a fault in the system. * * @return pointer to ecma-object representing specified error * with reference counter set to one. */ ecma_object_t * ecma_new_standard_error (jerry_error_t error_type, /**< native error type */ ecma_string_t *message_string_p) /**< message string */ { #if JERRY_BUILTIN_ERRORS ecma_builtin_id_t prototype_id = ECMA_BUILTIN_ID__COUNT; switch (error_type) { case JERRY_ERROR_EVAL: { prototype_id = ECMA_BUILTIN_ID_EVAL_ERROR_PROTOTYPE; break; } case JERRY_ERROR_RANGE: { prototype_id = ECMA_BUILTIN_ID_RANGE_ERROR_PROTOTYPE; break; } case JERRY_ERROR_REFERENCE: { prototype_id = ECMA_BUILTIN_ID_REFERENCE_ERROR_PROTOTYPE; break; } case JERRY_ERROR_TYPE: { prototype_id = ECMA_BUILTIN_ID_TYPE_ERROR_PROTOTYPE; break; } case JERRY_ERROR_AGGREGATE: { prototype_id = ECMA_BUILTIN_ID_AGGREGATE_ERROR_PROTOTYPE; break; } case JERRY_ERROR_URI: { prototype_id = ECMA_BUILTIN_ID_URI_ERROR_PROTOTYPE; break; } case JERRY_ERROR_SYNTAX: { prototype_id = ECMA_BUILTIN_ID_SYNTAX_ERROR_PROTOTYPE; break; } default: { JERRY_ASSERT (error_type == JERRY_ERROR_COMMON); prototype_id = ECMA_BUILTIN_ID_ERROR_PROTOTYPE; break; } } #else /* !JERRY_BUILTIN_ERRORS */ JERRY_UNUSED (error_type); ecma_builtin_id_t prototype_id = ECMA_BUILTIN_ID_ERROR_PROTOTYPE; #endif /* JERRY_BUILTIN_ERRORS */ ecma_object_t *prototype_obj_p = ecma_builtin_get (prototype_id); ecma_object_t *error_object_p = ecma_create_object (prototype_obj_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *extended_object_p = (ecma_extended_object_t *) error_object_p; extended_object_p->u.cls.type = ECMA_OBJECT_CLASS_ERROR; extended_object_p->u.cls.u1.error_type = (uint8_t) error_type; if (message_string_p != NULL) { ecma_property_value_t *prop_value_p; prop_value_p = ecma_create_named_data_property (error_object_p, ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE), ECMA_PROPERTY_CONFIGURABLE_WRITABLE, NULL); ecma_ref_ecma_string (message_string_p); prop_value_p->value = ecma_make_string_value (message_string_p); } /* Avoid calling the decorator function recursively. */ if (JERRY_CONTEXT (error_object_created_callback_p) != NULL && !(JERRY_CONTEXT (status_flags) & ECMA_STATUS_ERROR_UPDATE)) { JERRY_CONTEXT (status_flags) |= ECMA_STATUS_ERROR_UPDATE; JERRY_CONTEXT (error_object_created_callback_p) (ecma_make_object_value (error_object_p), JERRY_CONTEXT (error_object_created_callback_user_p)); JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_ERROR_UPDATE; } return error_object_p; } /* ecma_new_standard_error */ /** * aggregate-error object constructor. * * @return newly constructed aggregate errors */ ecma_value_t ecma_new_aggregate_error (ecma_value_t error_list_val, /**< errors list */ ecma_value_t message_val) /**< message string */ { ecma_object_t *new_error_object_p; if (!ecma_is_value_undefined (message_val)) { ecma_string_t *message_string_p = ecma_op_to_string (message_val); if (JERRY_UNLIKELY (message_string_p == NULL)) { return ECMA_VALUE_ERROR; } new_error_object_p = ecma_new_standard_error (JERRY_ERROR_AGGREGATE, message_string_p); ecma_deref_ecma_string (message_string_p); } else { new_error_object_p = ecma_new_standard_error (JERRY_ERROR_AGGREGATE, NULL); } ecma_value_t using_iterator = ecma_op_get_method_by_symbol_id (error_list_val, LIT_GLOBAL_SYMBOL_ITERATOR); if (ECMA_IS_VALUE_ERROR (using_iterator)) { ecma_deref_object (new_error_object_p); return using_iterator; } if (!ecma_is_value_undefined (using_iterator)) { ecma_value_t next_method; ecma_value_t iterator = ecma_op_get_iterator (error_list_val, using_iterator, &next_method); ecma_free_value (using_iterator); if (ECMA_IS_VALUE_ERROR (iterator)) { ecma_deref_object (new_error_object_p); return iterator; } ecma_collection_t *error_list_p = ecma_new_collection (); ecma_value_t result = ECMA_VALUE_ERROR; while (true) { ecma_value_t next = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (next)) { break; } if (next == ECMA_VALUE_FALSE) { result = ECMA_VALUE_UNDEFINED; break; } /* 8.e.iii */ ecma_value_t next_error = ecma_op_iterator_value (next); ecma_free_value (next); if (ECMA_IS_VALUE_ERROR (next_error)) { break; } ecma_collection_push_back (error_list_p, next_error); } ecma_free_value (iterator); ecma_free_value (next_method); if (ECMA_IS_VALUE_ERROR (result)) { ecma_collection_free (error_list_p); ecma_deref_object (new_error_object_p); return result; } JERRY_ASSERT (ecma_is_value_undefined (result)); ecma_value_t error_list_arr = ecma_op_new_array_object_from_collection (error_list_p, true); ecma_property_value_t *prop_value_p; prop_value_p = ecma_create_named_data_property (new_error_object_p, ecma_get_magic_string (LIT_MAGIC_STRING_ERRORS_UL), ECMA_PROPERTY_CONFIGURABLE_WRITABLE, NULL); prop_value_p->value = error_list_arr; ecma_free_value (error_list_arr); } return ecma_make_object_value (new_error_object_p); } /* ecma_new_aggregate_error */ /** * Return the error type for an Error object. * * @return one of the jerry_error_t value * if it is not an Error object then JERRY_ERROR_NONE will be returned */ jerry_error_t ecma_get_error_type (ecma_object_t *error_object_p) /**< possible error object */ { if (!ecma_object_class_is (error_object_p, ECMA_OBJECT_CLASS_ERROR)) { return JERRY_ERROR_NONE; } return (jerry_error_t) ((ecma_extended_object_t *) error_object_p)->u.cls.u1.error_type; } /* ecma_get_error_type */ /** * Raise a standard ecma-error with the given type and message. * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_standard_error (jerry_error_t error_type, /**< error type */ ecma_error_msg_t msg) /**< error message */ { ecma_object_t *error_obj_p; const lit_utf8_byte_t *str_p = (lit_utf8_byte_t *) ecma_get_error_msg (msg); if (msg != ECMA_ERR_EMPTY) { ecma_string_t *error_msg_p = ecma_new_ecma_external_string_from_cesu8 (str_p, ecma_get_error_size (msg), NULL); error_obj_p = ecma_new_standard_error (error_type, error_msg_p); ecma_deref_ecma_string (error_msg_p); } else { error_obj_p = ecma_new_standard_error (error_type, NULL); } jcontext_raise_exception (ecma_make_object_value (error_obj_p)); return ECMA_VALUE_ERROR; } /* ecma_raise_standard_error */ #if JERRY_ERROR_MESSAGES /** * Raise a standard ecma-error with the given format string and arguments. * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_standard_error_with_format (jerry_error_t error_type, /**< error type */ const char *format, /**< format string */ ...) /**< ecma-values */ { JERRY_ASSERT (format != NULL); ecma_stringbuilder_t builder = ecma_stringbuilder_create (); const char *start_p = format; const char *end_p = format; va_list args; va_start (args, format); while (*end_p) { if (*end_p == '%') { /* Concat template string. */ if (end_p > start_p) { ecma_stringbuilder_append_raw (&builder, (lit_utf8_byte_t *) start_p, (lit_utf8_size_t) (end_p - start_p)); } /* Convert an argument to string without side effects. */ ecma_string_t *arg_string_p; const ecma_value_t arg_val = va_arg (args, ecma_value_t); if (JERRY_UNLIKELY (ecma_is_value_object (arg_val))) { ecma_object_t *arg_object_p = ecma_get_object_from_value (arg_val); lit_magic_string_id_t class_name = ecma_object_get_class_name (arg_object_p); arg_string_p = ecma_get_magic_string (class_name); } else if (ecma_is_value_symbol (arg_val)) { ecma_value_t symbol_desc_value = ecma_get_symbol_descriptive_string (arg_val); arg_string_p = ecma_get_string_from_value (symbol_desc_value); } else { arg_string_p = ecma_op_to_string (arg_val); JERRY_ASSERT (arg_string_p != NULL); } /* Concat argument. */ ecma_stringbuilder_append (&builder, arg_string_p); ecma_deref_ecma_string (arg_string_p); start_p = end_p + 1; } end_p++; } va_end (args); /* Concat reset of template string. */ if (start_p < end_p) { ecma_stringbuilder_append_raw (&builder, (lit_utf8_byte_t *) start_p, (lit_utf8_size_t) (end_p - start_p)); } ecma_string_t *builder_str_p = ecma_stringbuilder_finalize (&builder); ecma_object_t *error_obj_p = ecma_new_standard_error (error_type, builder_str_p); ecma_deref_ecma_string (builder_str_p); jcontext_raise_exception (ecma_make_object_value (error_obj_p)); return ECMA_VALUE_ERROR; } /* ecma_raise_standard_error_with_format */ #endif /* JERRY_ERROR_MESSAGES */ /** * Raise a common error with the given message. * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_common_error (ecma_error_msg_t msg) /**< error message */ { return ecma_raise_standard_error (JERRY_ERROR_COMMON, msg); } /* ecma_raise_common_error */ /** * Raise a RangeError with the given message. * * See also: ECMA-262 v5, 15.11.6.2 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_range_error (ecma_error_msg_t msg) /**< error message */ { return ecma_raise_standard_error (JERRY_ERROR_RANGE, msg); } /* ecma_raise_range_error */ /** * Raise a ReferenceError with the given message. * * See also: ECMA-262 v5, 15.11.6.3 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_reference_error (ecma_error_msg_t msg) /**< error message */ { return ecma_raise_standard_error (JERRY_ERROR_REFERENCE, msg); } /* ecma_raise_reference_error */ /** * Raise a SyntaxError with the given message. * * See also: ECMA-262 v5, 15.11.6.4 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_syntax_error (ecma_error_msg_t msg) /**< error message */ { return ecma_raise_standard_error (JERRY_ERROR_SYNTAX, msg); } /* ecma_raise_syntax_error */ /** * Raise a TypeError with the given message. * * See also: ECMA-262 v5, 15.11.6.5 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_type_error (ecma_error_msg_t msg) /**< error message */ { return ecma_raise_standard_error (JERRY_ERROR_TYPE, msg); } /* ecma_raise_type_error */ /** * Raise a URIError with the given message. * * See also: ECMA-262 v5, 15.11.6.6 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_uri_error (ecma_error_msg_t msg) /**< error message */ { return ecma_raise_standard_error (JERRY_ERROR_URI, msg); } /* ecma_raise_uri_error */ #if (JERRY_STACK_LIMIT != 0) /** * Raise a RangeError with "Maximum call stack size exceeded" message. * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_maximum_callstack_error (void) { return ecma_raise_range_error (ECMA_ERR_MAXIMUM_CALL_STACK_SIZE_EXCEEDED); } /* ecma_raise_maximum_callstack_error */ #endif /* (JERRY_STACK_LIMIT != 0) */ /** * Raise a AggregateError with the given errors and message. * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_raise_aggregate_error (ecma_value_t error_list_val, /**< errors list */ ecma_value_t message_val) /**< error message */ { ecma_value_t aggre_val = ecma_new_aggregate_error (error_list_val, message_val); jcontext_raise_exception (aggre_val); return ECMA_VALUE_ERROR; } /* ecma_raise_aggregate_error */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/000775 001750 001750 00000000000 15164251010 031612 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/README.md000664 001750 001750 00000115064 15164251010 030503 0ustar00ddennedyddennedy000000 000000 [![LFX Health Score](https://insights.linuxfoundation.org/api/badge/health-score?project=thorvg)](https://insights.linuxfoundation.org/project/thorvg) [![CodeFactor](https://www.codefactor.io/repository/github/hermet/thorvg/badge)](https://www.codefactor.io/repository/github/hermet/thorvg) ![BinarySize](https://img.shields.io/badge/Size->150kb-black) [![License](https://img.shields.io/badge/licence-MIT-green.svg?style=flat)](LICENSE) [![Wikipedia](https://img.shields.io/badge/Wikipedia-000000?style=flat&logo=wikipedia&logoColor=white)](https://en.wikipedia.org/wiki/Thor_Vector_Graphics) [![DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/thorvg/thorvg) [![Discord](https://img.shields.io/badge/Community-5865f2?style=flat&logo=discord&logoColor=white)](https://discord.gg/n25xj6J6HM) [![OpenCollective](https://img.shields.io/badge/OpenCollective-84B5FC?style=flat&logo=opencollective&logoColor=white)](https://opencollective.com/thorvg)
[![Build Ubuntu](https://github.com/thorvg/thorvg/actions/workflows/build_ubuntu.yml/badge.svg?branch=main&event=push)](https://github.com/thorvg/thorvg/actions/workflows/build_ubuntu.yml) [![Build Windows](https://github.com/thorvg/thorvg/actions/workflows/build_windows.yml/badge.svg?branch=main&event=push)](https://github.com/thorvg/thorvg/actions/workflows/build_windows.yml) [![Build macOS](https://github.com/thorvg/thorvg/actions/workflows/build_macos.yml/badge.svg?branch=main&event=push)](https://github.com/thorvg/thorvg/actions/workflows/build_macos.yml) [![Build iOS](https://github.com/thorvg/thorvg/actions/workflows/build_ios.yml/badge.svg?branch=main&event=push)](https://github.com/thorvg/thorvg/actions/workflows/build_ios.yml) [![Build Android](https://github.com/thorvg/thorvg/actions/workflows/build_android.yml/badge.svg?branch=main&event=push)](https://github.com/thorvg/thorvg/actions/workflows/build_android.yml)
[![Latest](https://img.shields.io/github/v/release/thorvg/thorvg)](https://github.com/thorvg/thorvg/releases/latest) [![vcpkg](https://img.shields.io/vcpkg/v/thorvg)](https://vcpkg.link/ports/thorvg) [![ArchLinux](https://img.shields.io/aur/version/thorvg?color=orange)](https://aur.archlinux.org/packages/thorvg) [![Conan](https://img.shields.io/conan/v/thorvg)](https://conan.io/center/recipes/thorvg) [![Fedora](https://img.shields.io/fedora/v/thorvg?color=orange)](https://src.fedoraproject.org/rpms/thorvg) [![MSYS2](https://img.shields.io/badge/msys2-Latest-orange)](https://packages.msys2.org/packages/mingw-w64-x86_64-thorvg?repo=mingw64) [![npm](https://img.shields.io/npm/v/@thorvg/lottie-player)](https://www.npmjs.com/package/@thorvg/lottie-player) [![pub](https://img.shields.io/pub/v/thorvg.svg)](https://pub.dev/packages/thorvg) [![Nimble](https://img.shields.io/badge/nimble-Latest-FFE953?color=orange)](https://nimble.directory/pkg/thorvg) [![DUB](https://img.shields.io/badge/dub-latest-orange)](https://code.dlang.org/packages/bindbc-thorvg) [![Homebrew](https://img.shields.io/badge/homebrew-latest-orange)](https://formulae.brew.sh/formula/thorvg)
# ThorVG

**Thor Vector Graphics** is an **open-source** graphics library designed for creating **vector-based scenes and animations**. It combines **high performance** with **lightweight efficiency**, as Thor embodies a dual meaning—_symbolizing both immense strength and lightning-fast agility_. Embracing the philosophy of _simplicity leads to reliability_, the ThorVG project provides **intuitive, user-friendly interfaces** while maintaining a **compact footprint** and **minimal overhead**.

The following primitives are supported by ThorVG:
- **Lines & Shapes**: rectangles, circles, and paths with coordinate control - **Filling**: solid colors, linear & radial gradients, and path clipping - **Stroking**: stroke width, joins, caps, dash patterns, and trimming - **Scene Management**: retainable scene graph and object transformations - **Composition**: various blending and masking - **Text**: unicode characters with horizontal multi-line text layout using scalable fonts (TTF) - **Images**: SVG, JPG, PNG, WebP, and raw bitmaps - **Effects**: blur, drop shadow, fill, tint, tritone and color replacement - **Animations**: Lottie

### Lightweight Design ​ThorVG is designed for a wide range of programs, offering adaptability for integration and use in various applications and systems. It achieves this through a single binary with selectively buildable, modular components in a building block style. This ensures both optimal size and easy maintenance.

The core library of ThorVG maintains a binary size of approximately **150KB**. This is significantly smaller compared to graphics engines designed primarily for desktop environments and offers the following advantages.
- **Memory Efficiency**: Thanks to its low runtime memory usage, ThorVG operates stably even on low-spec systems. - **Fast Boot**: The library loads and initializes quickly, improving the overall startup speed of applications. - **Low Size Deployment:** With its small code and resource footprint, ThorVG is well-suited for embedded systems, IoT devices, and network-constrained environments. ### Broad Portability ThorVG is based on the **C++** standard and provides consistent functionality across various platforms through an abstraction layer that minimizes dependence on specific operating systems or hardware.
- **Extensive Platform Support**: ThorVG supports web platforms, desktop operating systems such as Windows, macOS, and Linux, mobile platforms including Android and iOS, as well as embedded systems like Tizen and RTOS-based environments. - **Microcontroller Support**: ThorVG has been shown to run on microcontrollers like the ESP32, demonstrating its efficiency even within environments with highly limited memory and storage. - **Headless Rendering Support**: ThorVG can perform rendering without a display server, enabling use cases such as server-side graphics processing or offline rendering tools. If your program includes the main renderer, you can seamlessly utilize ThorVG APIs by transitioning drawing contexts between the main renderer and ThorVG. Throughout these API calls, ThorVG effectively serializes drawing commands among volatile paint nodes. Subsequently, it undertakes synchronous or asynchronous rendering via its render-backend engines. Additionally, ThorVG is adept at handling vector images, including formats like SVG and Lottie, and it remains adaptable for accommodating additional popular formats as needed. In the rendering process, the library may generate intermediate frame buffers for scene compositing, though only when essential. The accompanying diagram provides a concise overview of how to effectively incorporate ThorVG within your system.

### CPU Rasterization ThorVG is optimized for CPU-based rasterization, with a strong focus on vector rendering in environments where GPU resources are limited, unavailable, or intentionally avoided. In representative CPU benchmarks, ThorVG demonstrates **an average of ~1.8× faster performance** to a widely-used vector graphics engine across common vector rendering workloads. The advantage is particularly clear in geometry-heavy scenarios such as rectangles, strokes, rotations, and circle rendering. #### Performance Overview

image

#### Test Conditions - Tested with 5k semi-transparent primitives, including shapes, strokes, and images, using alpha blending. - Image filtering was performed using bilinear interpolation. - Test Platform: Apple M1 (macOS 15) - Render size: 2560 × 1440 (2K) for each test case - Versions: ThorVG v1.0.0, Skia v144

### Threading ThorVG incorporates a threading mechanism designed to seamlessly retrieve upcoming scenes without unnecessary delays. It utilizes a finely-tuned task scheduler based on thread pools to handle a variety of tasks, including encoding, decoding, updating, and rendering. This architecture ensures efficient use of multi-core processing.

The task scheduler is carefully designed to abstract complexity, simplify integration, and enhance user convenience. Its use is optional, allowing users to adopt it based on their specific needs.

### Smart Rendering ThorVG supports smart partial rendering, which enables more efficient rendering workflows by updating only the portions of a vector scene that have changed. By internally tracking modified regions, it minimizes unnecessary redraws and optimizes overall performance. This feature provides significant benefits in scenarios such as UI rendering, design tools, or applications where large parts of the scene remain static and only small elements update between frames. In such cases, avoiding full-scene rendering can greatly reduce computational workload and improve energy efficiency—making it particularly valuable on mobile and embedded systems.

The following figure illustrates the geometry changes and highlights the minimal redraw region (outlined in red) that needs to be updated. Only the modified area between the previous and current frames is selectively redrawn, significantly improving performance.

Please note that in highly dynamic content—such as fast-paced games or full-screen animations where nearly all objects change every frame—partial rendering provides little to no benefit and may even introduce minor overhead. In these scenarios, full-scene rendering is typically the better choice. For a practical showcase, visit [this page](https://hermet.github.io/partial-test/) demonstrating a performance comparison of partial rendering using ThorVG's software renderer.
### Render Backends Today, ThorVG provides its own implementation of multiple render-backend engines, allowing you to choose the one that best suits your app and system preferences.
- CPU/SIMD (Software) - OpenGL/ES - WebGL - WebGPU ThorVG is particularly ahead of the curve in the web ecosystem. WebGPU introduces next-generation graphics APIs comparable to Vulkan, offering access to compute shaders and low-overhead GPU control. This enables more aggressive optimization strategies and broader application potential. On top of this, ThorVG fully supports all of its vector rendering features within the WebGPU backend, ensuring a complete and consistent experience across platforms. Furthermore, by abstracting underlying hardware graphics APIs such as Metal, Vulkan, and DirectX, ThorVG guarantees seamless integration across a wide range of systems, regardless of the specific hardware accelerations available.

### Supported Platforms ThorVG is designed to be portable across a wide range of devices, including small IoT devices, embedded systems, mobile platforms, game consoles, desktop environments, and the web. It is actively under development, with continuous efforts to expand support for essential platforms as needed. Currently, the major supported platforms include:

## Contents - [ThorVG](#thorvg) - [Installation](#installation) - [Build and Install](#build-and-install) - [Build with Visual Studio](#build-with-visual-studio) - [Build with Xcode](#build-with-xcode) - [Quick Start](#quick-start) - [SVG](#svg) - [Lottie](#lottie) - [In Practice](#in-practice) - [Canva iOS](#canva-ios) - [dotLottie](#dotlottie) - [Espressif](#espressif) - [Flux Audio](#flux-audio) - [Godot](#godot) - [Lottie Creator](#lottie-creator) - [LVGL](#lvgl) - [Segger](#segger) - [TinyPiXOS](#tinypixos) - [Tizen](#tizen) - [Interactive App](#interactive-app) - [Examples](#examples) - [C++ Examples](#c-examples) - [Playground](#playground) - [Tools](#tools) - [ThorVG Viewer](#thorvg-viewer) - [Lottie to GIF](#lottie-to-gif) - [SVG to PNG](#svg-to-png) - [Related Projects](#related-projects) - [API Bindings](#api-bindings) - [Documentation](#documentation) - [References](#references) - [Dependencies](#dependencies) - [Contributors](#contributors) - [Partners](#partners) - [Sponsors](#sponsors) - [Communication](#communication) [](#contents)
## Installation This section details the steps required to configure the environment for installing ThorVG.

### Build and Install ThorVG supports [meson](https://mesonbuild.com/) build system. Install [meson](http://mesonbuild.com/Getting-meson.html) and [ninja](https://ninja-build.org/) if you don't have them already. Run meson to configure ThorVG in the thorvg root folder. ``` meson setup builddir ``` Run ninja to build & install ThorVG: ``` ninja -C builddir install ``` Regardless of the installation, all build results (symbols, executable) are generated in the builddir folder in thorvg. Some results such as examples won't be installed, you can check More examples section to see how to change it.

Note that some systems might include ThorVG package as a default component. In that case, you can skip this manual installation.
### Build with Visual Studio If you want to create Visual Studio project files, use the command `--backend=vs`. The resulting solution file `thorvg.sln` will be located in the build folder. ``` meson setup builddir --backend=vs ``` ### Build with Xcode If you want to create Xcode project files, use the command `--backend=xcode`. The resulting solution file `thorvg.xcodeproj` will be located in the build folder. ``` meson setup builddir --backend=xcode ``` [Back to contents](#contents)

## Quick Start ThorVG renders vector shapes to a given canvas buffer. The following is a quick start to show you how to use the essential APIs. First, you should initialize the ThorVG engine: ```cpp tvg::Initializer::init(4); //4 threads ``` Then it would be best if you prepared an empty canvas for drawing on it: ```cpp static uint32_t buffer[WIDTH * HEIGHT]; //canvas target buffer auto canvas = tvg::SwCanvas::gen(); //generate a canvas canvas->target(buffer, WIDTH, WIDTH, HEIGHT, tvg::ColorSpace::ARGB8888); //buffer, stride, w, h, Colorspace ``` Next you can draw multiple shapes on the canvas: ```cpp auto rect = tvg::Shape::gen(); //generate a shape rect->appendRect(50, 50, 200, 200, 20, 20); //define it as a rounded rectangle (x, y, w, h, rx, ry) rect->fill(100, 100, 100); //set its color (r, g, b) canvas->add(rect); //add the rectangle to the canvas auto circle = tvg::Shape::gen(); //generate a shape circle->appendCircle(400, 400, 100, 100); //define it as a circle (cx, cy, rx, ry) auto fill = tvg::RadialGradient::gen(); //generate a radial gradient fill->radial(400, 400, 150); //set the radial gradient geometry info (cx, cy, radius) tvg::Fill::ColorStop colorStops[2]; //gradient colors colorStops[0] = {0.0, 255, 255, 255, 255}; //1st color values (offset, r, g, b, a) colorStops[1] = {1.0, 0, 0, 0, 255}; //2nd color values (offset, r, g, b, a) fill->colorStops(colorStops, 2); //set the gradient colors info circle->fill(fill); //set the circle fill canvas->add(circle); //add the circle to the canvas ``` This code generates the following result:

You can also draw you own shapes and use dashed stroking: ```cpp auto path = tvg::Shape::gen(); //generate a path path->moveTo(199, 34); //set sequential path coordinates path->lineTo(253, 143); path->lineTo(374, 160); path->lineTo(287, 244); path->lineTo(307, 365); path->lineTo(199, 309); path->lineTo(97, 365); path->lineTo(112, 245); path->lineTo(26, 161); path->lineTo(146, 143); path->close(); path->fill(150, 150, 255); //path color path->strokeWidth(3); //stroke width path->strokeFill(0, 0, 255); //stroke color path->strokeJoin(tvg::StrokeJoin::Round); //stroke join style path->strokeCap(tvg::StrokeCap::Round); //stroke cap style float pattern[2] = {10, 10}; //stroke dash pattern (line, gap) path->strokeDash(pattern, 2); //set the stroke pattern canvas->add(path); //add the path to the canvas ``` The code generates the following result:

Now begin rendering & finish it at a particular time: ```cpp canvas->draw(); canvas->sync(); ``` Then you can acquire the rendered image from the buffer memory. Lastly, terminate the engine after its usage: ```cpp tvg::Initializer::term(); ``` [Back to contents](#contents)

## SVG ThorVG facilitates [SVG Tiny Specification](https://www.w3.org/TR/SVGTiny12/) rendering via its dedicated SVG interpreter. Adhering to the SVG Tiny Specification, the implementation maintains a lightweight profile, rendering it particularly advantageous for embedded systems. While ThorVG comprehensively adheres to [most of the SVG Tiny specs](https://github.com/thorvg/thorvg/wiki/SVG-Support), certain features remain unsupported within the current framework. These include:
- Animation - Interactivity - Multimedia The figure below highlights ThorVG's SVG rendering capabilities:

The following code snippet shows how to draw SVG image using ThorVG: ```cpp auto picture = tvg::Picture::gen(); //generate a picture picture->load("tiger.svg"); //load a SVG file canvas->add(picture); //add the picture to the canvas ``` The result is:

[Back to contents](#contents)

## Lottie ThorVG supports the most powerful [Lottie Animation features](https://github.com/thorvg/thorvg/wiki/Lottie-Support). Lottie is an industry standard, JSON-based vector animation file format that enables seamless distribution of animations on any platform, akin to shipping static assets. These files are compact and compatible with various devices, scaling up or down without pixelation. With Lottie, you can easily create, edit, test, collaborate, and distribute animations in a user-friendly manner. For more information, please visit [Lottie Animation Community](https://lottie.github.io/)' website.

ThorVG offers great flexibility in building its binary. Besides serving as a general graphics engine, it can be configured as a compact Lottie animation playback library with specific build options: ``` $meson setup builddir -Dloaders="lottie" ``` Alternatively, to support additional bitmap image formats: ``` $meson setup builddir -Dloaders="lottie, png, jpg, webp" ``` Please note that ThorVG supports Lottie Expressions by default. Lottie Expressions are small JavaScript code snippets that can be applied to animated properties in your Lottie animations, evaluating to a single value. This is an advanced feature in the Lottie specification and may impact binary size and performance, especially when targeting small devices such as MCUs. If this feature is not essential for your requirements, you can disable it using the `extra` build option in ThorVG: ``` $meson setup builddir -Dloaders="lottie" -Dextra="" ``` The following code snippet demonstrates how to use ThorVG to play a Lottie animation. ```cpp auto animation = tvg::Animation::gen(); //generate an animation auto picture = animation->picture() //acquire a picture which associated with the animation. picture->load("lottie.json"); //load a Lottie file auto duration = animation->duration(); //figure out the animation duration time in seconds. canvas->add(picture); //add the picture to the canvas ``` First, an animation and a picture are generated. The Lottie file (lottie.json) is loaded into the picture, and then the picture is added to the canvas. The animation frames are controlled using the animation object to play the Lottie animation. Also you might want to know the animation duration time to run your animation loop. ```cpp animation->frame(animation->totalFrame() * progress); //Set a current animation frame to display canvas->update(animation->picture()); //Update the picture to be redrawn. ``` Let's suppose the progress variable determines the position of the animation, ranging from 0 to 1 based on the total duration time of the animation. Adjusting the progress value allows you to control the animation at the desired position. Afterwards, the canvas is updated to redraw the picture with the updated animation frame.

Please check out the [ThorVG Test App](https://thorvg-perf-test.vercel.app/) to see the performance of various Lottie animations powered by ThorVG. If you're working on the frontend, you can also download the ThorVG npm package [here](https://www.npmjs.com/package/@thorvg/lottie-player).
[Back to contents](#contents)

## In Practice ### Canva iOS [Canva](https://www.canva.com), is a popular visual communication platform used by millions worldwide. Known for its intuitive interface and rich design resources, Canva empowers users to create high-quality visual content easily. The iOS app switched from its previous Lottie engine to ThorVG, achieving around 80% faster rendering and 70% lower peak memory usage.

### dotLottie [dotLottie](https://dotlottie.io/) is an open-source format that combines multiple Lottie animations, assets like images and fonts, and supports features such as state machines and interactivity. It uses ZIP compression (.lottie extension) for easy and efficient distribution. As a superset of Lottie, the [dotLottie player](https://github.com/LottieFiles/dotlottie-rs) now runs on ThorVG for rendering.

### Espressif [Espressif Systems](https://www.espressif.com/en) provides [ThorVG as an official component](https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/lcd/gui_solution.html#thorvg-component) within its ESP-IDF (IoT Development Framework), simplifying integration into ESP-IDF projects. This allows developers to easily incorporate ThorVG's rendering capabilities into their applications, particularly on IoT devices powered by Espressif’s ESP32 and ESP32-P4 microcontrollers.

### Flux Audio [Flux Audio](https://www.flux.audio/) is a high-performance audio company focused on premium sound and immersive experiences. They use advanced software for consistent audio processing across devices. ThorVG powers their UI with fast, scalable vector rendering, highlighting its flexibility in modern audio platforms.

### Godot ThorVG is integrated into the Godot game engine to support sleek, high-quality vector-based user interfaces and assets. [Godot](https://www.godotengine.org) is a modern, open-source game engine with a full suite of tools, allowing developers to focus on creating games without reinventing core functionality.

### Lottie Creator [Lottie Creator](https://creator.lottiefiles.com/) is designed to create ultra-lightweight, highly customizable and interactive animations for web, apps and social. Supercharged with AI-based Motion Copilot. ThorVG is powering the Canvas engine behind Lottie Creator — enabling fast and scalable vector graphics rendering across platforms.

### LVGL [LVGL](https://lvgl.io/) is an open-source graphics library specifically designed for embedded systems with limited resources. It is lightweight and highly customizable, providing support for graphical user interfaces (GUIs) on microcontrollers, IoT devices, and other embedded platforms. ThorVG serves as the vector drawing primitives library in the LVGL framework.

### Segger [SEGGER](https://www.segger.com/) Microcontroller provides products for developing and manufacturing embedded systems, including real-time operating systems (RTOS), middleware libraries, debugging and trace probes, and programming tools. [It has adopted ThorVG](https://doc.segger.com/UM03001_emWin.html#GUI_GPU_ThorVG_ThorVG_driver) as a GPU driver to enable high-performance vector rendering for its GUI apps.

### TinyPiXOS [TinyPiXOS](https://www.tinypixos.com/en/) is a lightweight, open-source Linux OS that replaces X11/Wayland with a custom C/C++ graphics stack. It includes minimal window management and GUI tools, all optimized for low resource use. ThorVG handles core GUI rendering, delivering efficient performance and high visual quality in constrained environments.

### Tizen ThorVG has been integrated into the [Tizen](https://www.tizen.org) platform as the vector graphics engine. [NUI](https://docs.tizen.org/application/dotnet/guides/user-interface/nui/overview/) is the name of Tizen UI framework which is written in C#. ThorVG is the backend engine of the [NUI Vector Graphics](https://docs.tizen.org/application/dotnet/guides/user-interface/nui/vectorgraphics/Overview/) which is used for vector primitive drawings and scalable image contents such as SVG and Lottie Animation among the Tizen applications.


Would you like us to showcase your project with ThorVG? Feel free to [open an issue](https://github.com/thorvg/thorvg/issues) or submit a pull request! [Back to contents](#contents)

## Interactive App Check out [Thor Janitor](https://github.com/thorvg/thorvg.janitor), an interactive demo game fully rendered using ThorVG. It renders tens of thousands of objects in real-time with effects like DropShadow and Blur, running stably at 120+ FPS!

[Back to contents](#contents)

## Examples ### C++ Examples A wide range of native sample codes is available in the [thorvg.example](https://github.com/thorvg/thorvg.example) repository to help you understand and work with the ThorVG C++ APIs. ### Playground The [ThorVG Playground](https://www.thorvg.org/playground) is an interactive web-based environment where you can explore various graphic features and instantly see the results in real time. [Back to contents](#contents)

## Tools ### ThorVG Viewer ThorVG provides a resource verification tool for the ThorVG engine. The [ThorVG viewer](https://thorvg.github.io/thorvg.viewer/) enables instant rendering directly in the web browser using the ThorVG WebAssembly binary, allowing real-time editing of vector elements. It does not upload your resources to any external server and supports exporting to formats such as GIF, ensuring that designer copyrights remain protected.

### Lottie to GIF ThorVG provides an executable `tvg-lottie2gif` converter that generates a GIF file from a Lottie file. To use the `tvg-lottie2gif`, you must turn on this feature in the build option: ``` meson setup builddir -Dtools=lottie2gif -Dsavers=gif ``` To use the 'tvg-lottie2gif' converter, you need to provide the 'Lottie files' parameter. This parameter can be a file name with the '.json' extension or a directory name. It also accepts multiple files or directories separated by spaces. If a directory is specified, the converter will search for files with the '.json' extension within that directory and all its subdirectories.

Optionally, you can specify the image resolution in the 'WxH' format, with two numbers separated by an 'x' sign, following the '-r' flag.

Both flags, if provided, are applied to all of the `.json` files. The usage examples of the `tvg-lottie2gif`: ``` Usage: tvg-lottie2gif [Lottie file] or [Lottie folder] [-r resolution] [-f fps] [-b background color] Flags: -r set the output image resolution. -f specifies the frames per second (fps) for the generated animation. -b specifies the base background color (RGB in hex). If not specified, the background color will follow the original content. Examples: $ tvg-lottie2gif input.json $ tvg-lottie2gif input.json -f 30 $ tvg-lottie2gif input.json -r 600x600 -f 30 $ tvg-lottie2gif lottiefolder $ tvg-lottie2gif lottiefolder -r 600x600 $ tvg-lottie2gif lottiefolder -r 600x600 -f 30 -b fa7410 ``` ### SVG to PNG ThorVG provides an executable `tvg-svg2png` converter that generates a PNG file from an SVG file. To use the `tvg-svg2png`, you must turn on this feature in the build option: ``` meson setup builddir -Dtools=svg2png ``` To use the 'tvg-svg2png' converter, you need to provide the 'SVG files' parameter. This parameter can be a file name with the '.svg' extension or a directory name. It also accepts multiple files or directories separated by spaces. If a directory is specified, the converter will search for files with the '.svg' extension within that directory and all its subdirectories.

Optionally, you can specify the image resolution in the 'WxH' format, with two numbers separated by an 'x' sign, following the '-r' flag.

The background color can be set with the `-b` flag. The `bgColor` parameter should be passed as a three-bytes hexadecimal value in the `ffffff` format. The default background is transparent.

Both flags, if provided, are applied to all of the `.svg` files. The usage examples of the `tvg-svg2png`: ``` Usage: tvg-svg2png [SVG files] [-r resolution] [-b bgColor] Flags: -r set the output image resolution. -b set the output image background color. Examples: $ tvg-svg2png input.svg $ tvg-svg2png input.svg -r 200x200 $ tvg-svg2png input.svg -r 200x200 -b ff00ff $ tvg-svg2png input1.svg input2.svg -r 200x200 -b ff00ff $ tvg-svg2png . -r 200x200 ``` [Back to contents](#contents)

## Related Projects ThorVG is designed to be portable and extensible across various platforms. The following projects integrate ThorVG into specific environments or tools:
* [ThorVG Android](https://github.com/thorvg/thorvg.android) – Kotlin-based ThorVG support for Android. * [ThorVG Example](https://github.com/thorvg/thorvg.example) – An example set demonstrates how to use ThorVG's APIs. * [ThorVG Flutter](https://github.com/thorvg/thorvg.flutter) - A Flutter plugin for using ThorVG on Android and iOS. * [ThorVG Janitor](https://github.com/thorvg/thorvg.janitor) - A demo game showcasing ThorVG’s real-time rendering features. * [ThorVG Swift](https://github.com/thorvg/thorvg.swift) - Swift bindings for rendering vector graphics with ThorVG. * [ThorVG Unity](https://github.com/thorvg/thorvg.unity) – ThorVG integration for Unity using C#. * [ThorVG Viewer](https://github.com/thorvg/thorvg.viewer) - A browser-based viewer for ThorVG using WebAssembly. * [ThorVG Web](https://github.com/thorvg/thorvg.web) - WebAssembly-based integration of ThorVG for web apps. [Back to contents](#contents)

## API Bindings Our main development APIs are written in C++, but ThorVG also provides API bindings for C. To enable CAPI binding, you need to activate this feature in the build options: ``` meson setup builddir -Dbindings="capi" ``` [Back to contents](#contents)

## Documentation The ThorVG API documentation is available at [thorvg.org/apis](https://www.thorvg.org/apis), and can also be found directly in this repository via the [C++ API](https://github.com/thorvg/thorvg/blob/main/inc/thorvg.h) and [C API](https://github.com/thorvg/thorvg/blob/main/src/bindings/capi/thorvg_capi.h). For comprehensive and well-structured technical information, please visit the [DeepWiki](https://deepwiki.com/thorvg/thorvg), which offers in-depth guidance on ThorVG's architecture, features, and usage. [Back to contents](#contents)

## References - [Universal Motion Graphics across All Platforms: Unleashing Creativity with ThorVG](https://youtu.be/qhHMycRPQ9M?si=RXAag3Fxm8R7W_I0) - [Canva Enhances iOS Lottie Rendering: 80% Faster and 70% More Efficient with ThorVG](https://lottiefiles.com/blog/working-with-lottie-animations/canva-enhances-ios-rendering-faster-and-efficient-with-thorvg) [Back to contents](#contents)

## Dependencies ThorVG provides flexible image loading capabilities, supporting both static and external loaders. This design ensures that even in environments lacking external libraries, users can rely on built-in static loaders for core functionality. At its core, the ThorVG library is fully self-contained and operates without mandatory external dependencies. However, several optional feature extensions are available, each with its own set of dependencies. The following outlines the dependencies for these optional features: * **GL Engine**: [OpenGL 3.3](https://www.khronos.org/opengl/), [OpenGL ES 3.0](https://www.khronos.org/opengles/), or a browser with [WebGL2](https://www.khronos.org/webgl/) support. * **WG Engine**: [webgpu-native v0.25](https://github.com/gfx-rs/wgpu-native) or a browser with [WebGPU](https://www.w3.org/TR/webgpu/) support. * **PNG Loader** (external): [libpng](https://github.com/pnggroup/libpng) * **JPEG Loader** (external): [libjpeg-turbo](https://github.com/libjpeg-turbo/libjpeg-turbo) * **WebP Loader** (external): [libwebp](https://developers.google.com/speed/webp/download) [Back to contents](#contents)

## Contributors ThorVG stands as a purely open-source initiative. We are grateful to the individuals, organizations, and companies that have contributed to the development of the ThorVG project. The dedicated efforts of the individuals and entities listed below have enabled ThorVG to reach its current state. * [Individuals](https://github.com/thorvg/thorvg/blob/main/CONTRIBUTORS.md) * [Godot](https://godotengine.org/) * [LVGL](https://lvgl.io/) * [Canva Pty Ltd](https://www.canva.com/) [Back to contents](#contents)

## Partners Partners engage in strategic collaboration with ThorVG, working together to shape the future of scalable, high-performance vector graphics. We acknowledge organizations that have supported ThorVG’s journey through development, integration, collaboration, and community involvement.

LottieFiles

Samsung


If you’re interested in partnering with ThorVG, we’d love to hear from you. Please reach out at thorvg@thorvg.org [Back to contents](#contents)

## Sponsors We sincerely thank our financial sponsors for their generous support, which drives the growth and innovation of the ThorVG project. Your contributions help us make ThorVG more powerful, efficient, and accessible for everyone.

LottieFiles


We are also seeking your support to ensure the continued development of the ThorVG project. Your generous donations will help cover operational costs and contribute to the growth of this open-source project. Even a small contribution can make a big difference in securing the future of ThorVG!

* [Open Collective](https://opencollective.com/thorvg) [Back to contents](#contents)

## Communication For real-time conversations and discussions, please join us on [Discord](https://discord.gg/n25xj6J6HM) [Back to contents](#contents) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test2.svg000664 001750 001750 00000012776 15164251010 034005 0ustar00ddennedyddennedy000000 000000 Verify the basic capability to handle the 'path' element, and its data attribute (d) in combination with the straight-line path commands. Two pairs of concentric equilateral triangles are drawn using M and Z. No L commands are used in this test as they are implied after an M or Z command. The shapes are identical, with one stroked and one filled. The fill-mode default of "even-odd" means that the inner triangle is hollow. The rendered picture should match the reference image exactly, except for possible variations in the labelling text (per CSS2 rules). The test uses the 'path' element, as well as basic fill (solid primary colors), stroke (black 1-pixel lines), font-family (Arial) and font-size properties. paths-data-08-t Test that viewer has the basic capability to handle the <path> element and data (d) attribute in combination with the moveto and closepath commands - M and Z. Lines drawn with commands: M and Z stroked filled $Revision: 1.2 $ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgRenderTask.cpp000664 001750 001750 00000010330 15164251010 037257 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgRenderTask.h" #include //*********************************************************************** // WgPaintTask //*********************************************************************** void WgPaintTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) { if (renderData->type() == tvg::Type::Shape) compositor.renderShape(context, (WgRenderDataShape*)renderData, blendMethod); if (renderData->type() == tvg::Type::Picture) compositor.renderImage(context, (WgRenderDataPicture*)renderData, blendMethod); else assert(true); } //*********************************************************************** // WgSceneTask //*********************************************************************** void WgSceneTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) { // begin the render pass for the current scene and clear the target content compositor.beginRenderPassMS(encoder, renderTarget, true); // run all children (scenes and shapes) runChildren(context, compositor, encoder); // we must to end current render pass for current scene compositor.endRenderPass(); // we must to apply effect for current scene if (effect) runEffect(context, compositor, encoder); // there's no point in continuing if the scene has no destination target (e.g., the root scene) if (!renderTargetDst) return; // apply scene blending if (compose->method == MaskMethod::None) { compositor.beginRenderPassMS(encoder, renderTargetDst, false); compositor.renderScene(context, renderTarget, compose); // apply scene composition (for scenes, that have a handle to mask) } else if (renderTargetMsk) { compositor.beginRenderPassMS(encoder, renderTargetDst, false); compositor.composeScene(context, renderTarget, renderTargetMsk, compose); } } void WgSceneTask::runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) { ARRAY_FOREACH(task, children) { WgRenderTask* renderTask = *task; // we need to restore current render pass without clear compositor.beginRenderPassMS(encoder, renderTarget, false); // run children (shape or scene) renderTask->run(context, compositor, encoder); } } void WgSceneTask::runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) { assert(effect); switch (effect->type) { case SceneEffect::GaussianBlur: compositor.gaussianBlur(context, renderTarget, (RenderEffectGaussianBlur*)effect, compose); break; case SceneEffect::DropShadow: compositor.dropShadow(context, renderTarget, (RenderEffectDropShadow*)effect, compose); break; case SceneEffect::Fill: compositor.fillEffect(context, renderTarget, (RenderEffectFill*)effect, compose); break; case SceneEffect::Tint: compositor.tintEffect(context, renderTarget, (RenderEffectTint*)effect, compose); break; case SceneEffect::Tritone : compositor.tritoneEffect(context, renderTarget, (RenderEffectTritone*)effect, compose); break; default: break; } } src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlShader.h000664 001750 001750 00000003005 15164251010 036043 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_SHADER_H_ #define _TVG_GL_SHADER_H_ #include "tvgGlCommon.h" class GlShader { public: GlShader(const char* vertSrc, const char* fragSrc); ~GlShader(); uint32_t getVertexShader(); uint32_t getFragmentShader(); private: uint32_t compileShader(uint32_t type, char* shaderSrc); uint32_t mVtShader; uint32_t mFrShader; }; #endif /* _TVG_GL_SHADER_H_ */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/meson.build000664 001750 001750 00000000605 15164251010 042104 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'opcodes.h', 'vm-defines.h', 'vm-stack.h', 'vm.h', 'opcodes-ecma-arithmetics.cpp', 'opcodes-ecma-bitwise.cpp', 'opcodes-ecma-relational-equality.cpp', 'opcodes.cpp', 'vm-stack.cpp', 'vm-utils.cpp', 'vm.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/CODE_OF_CONDUCT.md000664 001750 001750 00000017451 15164251010 032024 0ustar00ddennedyddennedy000000 000000 # Contributor Covenant Code of Conduct ## Our Pledge We pledge to make our community welcoming, safe, and equitable for all. We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant. ## Encouraged Behaviors While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language. With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including: 1. Respecting the **purpose of our community**, our activities, and our ways of gathering. 2. Engaging **kindly and honestly** with others. 3. Respecting **different viewpoints** and experiences. 4. **Taking responsibility** for our actions and contributions. 5. Gracefully giving and accepting **constructive feedback**. 6. Committing to **repairing harm** when it occurs. 7. Behaving in other ways that promote and sustain the **well-being of our community**. ## Restricted Behaviors We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct. 1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop. 2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people. 3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits. 4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community. 5. **Violating confidentiality.** Sharing or acting on someone's personal or private information without their permission. 6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group. 7. Behaving in other ways that **threaten the well-being** of our community. ### Other Restrictions 1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions. 2. **Failing to credit sources.** Not properly crediting the sources of content you contribute. 3. **Promotional materials.** Sharing unsolicited commercial or marketing content unrelated to ThorVG or the topic of discussion. 4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors. ## Reporting an Issue Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm. When an incident does occur, it is important to report it promptly. To report a violation, please contact **thorvg@thorvg.org**. ThorVG Maintainers take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. ThorVG Maintainers will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution. ## Addressing and Repairing Harm The following enforcement guidelines outline how ThorVG Maintainers may respond to reported violations of this Code of Conduct. The response may vary depending on the severity and impact of the situation. If an investigation by the ThorVG Maintainers finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped. 1) Warning 1) Event: A violation involving a single incident or series of incidents. 2) Consequence: A private, written warning from the ThorVG Maintainers. 3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations. 2) Temporarily Limited Activities 1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation. 2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members. 3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over. 3) Temporary Suspension 1) Event: A pattern of repeated violation which the ThorVG Maintainers have tried to address with warnings, or a single serious violation. 2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions. 3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted. 4) Permanent Ban 1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the ThorVG Maintainers determine there is no way to keep the community safe with this person as a member. 2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior. 3) Repair: There is no possible repair in cases of this severity. This enforcement ladder is intended as a guideline. It does not limit the ability of ThorVG Maintainers to use their discretion and judgment, in keeping with the best interests of our community. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Attribution This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/). This Code of Conduct is distributed under the Creative Commons Attribution 4.0 International (CC-BY-4.0) license. glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-char-helpers.h000664 001750 001750 00000024713 15164251010 043432 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 LIT_CHAR_HELPERS_H #define LIT_CHAR_HELPERS_H #include "ecma-globals.h" #include "lit-globals.h" /** * Invalid character code point */ #define LIT_INVALID_CP 0xFFFFFFFF /** * Result of lit_char_to_lower_case/lit_char_to_upper_case consist more than of a single code unit */ #define LIT_MULTIPLE_CU 0xFFFFFFFE /* * Format control characters (ECMA-262 v5, Table 1) */ #define LIT_CHAR_ZWNJ ((ecma_char_t) 0x200C) /* zero width non-joiner */ #define LIT_CHAR_ZWJ ((ecma_char_t) 0x200D) /* zero width joiner */ #define LIT_CHAR_BOM ((ecma_char_t) 0xFEFF) /* byte order mark */ /* * Whitespace characters (ECMA-262 v5, Table 2) */ #define LIT_CHAR_TAB ((ecma_char_t) 0x0009) /* tab */ #define LIT_CHAR_VTAB ((ecma_char_t) 0x000B) /* vertical tab */ #define LIT_CHAR_FF ((ecma_char_t) 0x000C) /* form feed */ #define LIT_CHAR_SP ((ecma_char_t) 0x0020) /* space */ #define LIT_CHAR_NBSP ((ecma_char_t) 0x00A0) /* no-break space */ #define LIT_CHAR_MVS ((ecma_char_t) 0x180E) /* mongolian vowel separator */ /* LIT_CHAR_BOM is defined above */ bool lit_char_is_white_space (lit_code_point_t c); /* * Line terminator characters (ECMA-262 v5, Table 3) */ #define LIT_CHAR_LF ((ecma_char_t) 0x000A) /* line feed */ #define LIT_CHAR_CR ((ecma_char_t) 0x000D) /* carriage return */ #define LIT_CHAR_LS ((ecma_char_t) 0x2028) /* line separator */ #define LIT_CHAR_PS ((ecma_char_t) 0x2029) /* paragraph separator */ bool lit_char_is_line_terminator (ecma_char_t c); /* * String Single Character Escape Sequences (ECMA-262 v5, Table 4) */ #define LIT_CHAR_BS ((ecma_char_t) 0x0008) /* backspace */ /* LIT_CHAR_TAB is defined above */ /* LIT_CHAR_LF is defined above */ /* LIT_CHAR_VTAB is defined above */ /* LIT_CHAR_FF is defined above */ /* LIT_CHAR_CR is defined above */ #define LIT_CHAR_DOUBLE_QUOTE ((ecma_char_t) '"') /* double quote */ #define LIT_CHAR_SINGLE_QUOTE ((ecma_char_t) '\'') /* single quote */ #define LIT_CHAR_BACKSLASH ((ecma_char_t) '\\') /* reverse solidus (backslash) */ /* * Comment characters (ECMA-262 v5, 7.4) */ #define LIT_CHAR_SLASH ((ecma_char_t) '/') /* solidus */ #define LIT_CHAR_ASTERISK ((ecma_char_t) '*') /* asterisk */ /* * Identifier name characters (ECMA-262 v5, 7.6) */ #define LIT_CHAR_DOLLAR_SIGN ((ecma_char_t) '$') /* dollar sign */ #define LIT_CHAR_UNDERSCORE ((ecma_char_t) '_') /* low line (underscore) */ /* LIT_CHAR_BACKSLASH defined above */ bool lit_code_point_is_identifier_start (lit_code_point_t code_point); bool lit_code_point_is_identifier_part (lit_code_point_t code_point); /* * Punctuator characters (ECMA-262 v5, 7.7) */ #define LIT_CHAR_LEFT_BRACE ((ecma_char_t) '{') /* left curly bracket */ #define LIT_CHAR_RIGHT_BRACE ((ecma_char_t) '}') /* right curly bracket */ #define LIT_CHAR_LEFT_PAREN ((ecma_char_t) '(') /* left parenthesis */ #define LIT_CHAR_RIGHT_PAREN ((ecma_char_t) ')') /* right parenthesis */ #define LIT_CHAR_LEFT_SQUARE ((ecma_char_t) '[') /* left square bracket */ #define LIT_CHAR_RIGHT_SQUARE ((ecma_char_t) ']') /* right square bracket */ #define LIT_CHAR_DOT ((ecma_char_t) '.') /* dot */ #define LIT_CHAR_SEMICOLON ((ecma_char_t) ';') /* semicolon */ #define LIT_CHAR_COMMA ((ecma_char_t) ',') /* comma */ #define LIT_CHAR_LESS_THAN ((ecma_char_t) '<') /* less-than sign */ #define LIT_CHAR_GREATER_THAN ((ecma_char_t) '>') /* greater-than sign */ #define LIT_CHAR_EQUALS ((ecma_char_t) '=') /* equals sign */ #define LIT_CHAR_PLUS ((ecma_char_t) '+') /* plus sign */ #define LIT_CHAR_MINUS ((ecma_char_t) '-') /* hyphen-minus */ /* LIT_CHAR_ASTERISK is defined above */ #define LIT_CHAR_PERCENT ((ecma_char_t) '%') /* percent sign */ #define LIT_CHAR_AMPERSAND ((ecma_char_t) '&') /* ampersand */ #define LIT_CHAR_VLINE ((ecma_char_t) '|') /* vertical line */ #define LIT_CHAR_CIRCUMFLEX ((ecma_char_t) '^') /* circumflex accent */ #define LIT_CHAR_EXCLAMATION ((ecma_char_t) '!') /* exclamation mark */ #define LIT_CHAR_TILDE ((ecma_char_t) '~') /* tilde */ #define LIT_CHAR_QUESTION ((ecma_char_t) '?') /* question mark */ #define LIT_CHAR_COLON ((ecma_char_t) ':') /* colon */ #define LIT_CHAR_HASHMARK ((ecma_char_t) '#') /* hashmark */ /* * Special characters for String.prototype.replace. */ #define LIT_CHAR_GRAVE_ACCENT ((ecma_char_t) '`') /* grave accent */ /** * Uppercase ASCII letters */ #define LIT_CHAR_UPPERCASE_A ((ecma_char_t) 'A') #define LIT_CHAR_UPPERCASE_B ((ecma_char_t) 'B') #define LIT_CHAR_UPPERCASE_C ((ecma_char_t) 'C') #define LIT_CHAR_UPPERCASE_D ((ecma_char_t) 'D') #define LIT_CHAR_UPPERCASE_E ((ecma_char_t) 'E') #define LIT_CHAR_UPPERCASE_F ((ecma_char_t) 'F') #define LIT_CHAR_UPPERCASE_G ((ecma_char_t) 'G') #define LIT_CHAR_UPPERCASE_H ((ecma_char_t) 'H') #define LIT_CHAR_UPPERCASE_I ((ecma_char_t) 'I') #define LIT_CHAR_UPPERCASE_J ((ecma_char_t) 'J') #define LIT_CHAR_UPPERCASE_K ((ecma_char_t) 'K') #define LIT_CHAR_UPPERCASE_L ((ecma_char_t) 'L') #define LIT_CHAR_UPPERCASE_M ((ecma_char_t) 'M') #define LIT_CHAR_UPPERCASE_N ((ecma_char_t) 'N') #define LIT_CHAR_UPPERCASE_O ((ecma_char_t) 'O') #define LIT_CHAR_UPPERCASE_P ((ecma_char_t) 'P') #define LIT_CHAR_UPPERCASE_Q ((ecma_char_t) 'Q') #define LIT_CHAR_UPPERCASE_R ((ecma_char_t) 'R') #define LIT_CHAR_UPPERCASE_S ((ecma_char_t) 'S') #define LIT_CHAR_UPPERCASE_T ((ecma_char_t) 'T') #define LIT_CHAR_UPPERCASE_U ((ecma_char_t) 'U') #define LIT_CHAR_UPPERCASE_V ((ecma_char_t) 'V') #define LIT_CHAR_UPPERCASE_W ((ecma_char_t) 'W') #define LIT_CHAR_UPPERCASE_X ((ecma_char_t) 'X') #define LIT_CHAR_UPPERCASE_Y ((ecma_char_t) 'Y') #define LIT_CHAR_UPPERCASE_Z ((ecma_char_t) 'Z') /** * Lowercase ASCII letters */ #define LIT_CHAR_LOWERCASE_A ((ecma_char_t) 'a') #define LIT_CHAR_LOWERCASE_B ((ecma_char_t) 'b') #define LIT_CHAR_LOWERCASE_C ((ecma_char_t) 'c') #define LIT_CHAR_LOWERCASE_D ((ecma_char_t) 'd') #define LIT_CHAR_LOWERCASE_E ((ecma_char_t) 'e') #define LIT_CHAR_LOWERCASE_F ((ecma_char_t) 'f') #define LIT_CHAR_LOWERCASE_G ((ecma_char_t) 'g') #define LIT_CHAR_LOWERCASE_H ((ecma_char_t) 'h') #define LIT_CHAR_LOWERCASE_I ((ecma_char_t) 'i') #define LIT_CHAR_LOWERCASE_J ((ecma_char_t) 'j') #define LIT_CHAR_LOWERCASE_K ((ecma_char_t) 'k') #define LIT_CHAR_LOWERCASE_L ((ecma_char_t) 'l') #define LIT_CHAR_LOWERCASE_M ((ecma_char_t) 'm') #define LIT_CHAR_LOWERCASE_N ((ecma_char_t) 'n') #define LIT_CHAR_LOWERCASE_O ((ecma_char_t) 'o') #define LIT_CHAR_LOWERCASE_P ((ecma_char_t) 'p') #define LIT_CHAR_LOWERCASE_Q ((ecma_char_t) 'q') #define LIT_CHAR_LOWERCASE_R ((ecma_char_t) 'r') #define LIT_CHAR_LOWERCASE_S ((ecma_char_t) 's') #define LIT_CHAR_LOWERCASE_T ((ecma_char_t) 't') #define LIT_CHAR_LOWERCASE_U ((ecma_char_t) 'u') #define LIT_CHAR_LOWERCASE_V ((ecma_char_t) 'v') #define LIT_CHAR_LOWERCASE_W ((ecma_char_t) 'w') #define LIT_CHAR_LOWERCASE_X ((ecma_char_t) 'x') #define LIT_CHAR_LOWERCASE_Y ((ecma_char_t) 'y') #define LIT_CHAR_LOWERCASE_Z ((ecma_char_t) 'z') /** * ASCII decimal digits */ #define LIT_CHAR_0 ((ecma_char_t) '0') #define LIT_CHAR_1 ((ecma_char_t) '1') #define LIT_CHAR_2 ((ecma_char_t) '2') #define LIT_CHAR_3 ((ecma_char_t) '3') #define LIT_CHAR_4 ((ecma_char_t) '4') #define LIT_CHAR_5 ((ecma_char_t) '5') #define LIT_CHAR_6 ((ecma_char_t) '6') #define LIT_CHAR_7 ((ecma_char_t) '7') #define LIT_CHAR_8 ((ecma_char_t) '8') #define LIT_CHAR_9 ((ecma_char_t) '9') /** * ASCII character ranges */ #define LIT_CHAR_ASCII_UPPERCASE_LETTERS_BEGIN LIT_CHAR_UPPERCASE_A /* uppercase letters range */ #define LIT_CHAR_ASCII_UPPERCASE_LETTERS_END LIT_CHAR_UPPERCASE_Z #define LIT_CHAR_ASCII_LOWERCASE_LETTERS_BEGIN LIT_CHAR_LOWERCASE_A /* lowercase letters range */ #define LIT_CHAR_ASCII_LOWERCASE_LETTERS_END LIT_CHAR_LOWERCASE_Z #define LIT_CHAR_ASCII_UPPERCASE_LETTERS_HEX_BEGIN \ LIT_CHAR_UPPERCASE_A /* uppercase letters for \ * hexadecimal digits range */ #define LIT_CHAR_ASCII_UPPERCASE_LETTERS_HEX_END LIT_CHAR_UPPERCASE_F #define LIT_CHAR_ASCII_LOWERCASE_LETTERS_HEX_BEGIN \ LIT_CHAR_LOWERCASE_A /* lowercase letters for \ * hexadecimal digits range */ #define LIT_CHAR_ASCII_LOWERCASE_LETTERS_HEX_END LIT_CHAR_LOWERCASE_F #define LIT_CHAR_ASCII_OCTAL_DIGITS_BEGIN LIT_CHAR_0 /* octal digits range */ #define LIT_CHAR_ASCII_OCTAL_DIGITS_END LIT_CHAR_7 #define LIT_CHAR_ASCII_DIGITS_BEGIN LIT_CHAR_0 /* decimal digits range */ #define LIT_CHAR_ASCII_DIGITS_END LIT_CHAR_9 #define LEXER_TO_ASCII_LOWERCASE(character) ((character) | LIT_CHAR_SP) bool lit_char_is_octal_digit (ecma_char_t c); bool lit_char_is_decimal_digit (ecma_char_t c); bool lit_char_is_hex_digit (ecma_char_t c); bool lit_char_is_binary_digit (ecma_char_t c); uint8_t lit_char_to_radix (lit_utf8_byte_t c); void lit_char_unicode_escape (ecma_stringbuilder_t *builder_p, ecma_char_t c); uint32_t lit_char_hex_to_int (ecma_char_t c); size_t lit_code_point_to_cesu8_bytes (uint8_t *dst_p, lit_code_point_t code_point); size_t lit_code_point_get_cesu8_length (lit_code_point_t code_point); void lit_four_byte_utf8_char_to_cesu8 (uint8_t *dst_p, const uint8_t *source_p); uint32_t lit_char_hex_lookup (const lit_utf8_byte_t *buf_p, const lit_utf8_byte_t *const buf_end_p, uint32_t lookup); uint32_t lit_parse_decimal (const lit_utf8_byte_t **buffer_p, const lit_utf8_byte_t *const buffer_end_p); bool lit_find_char_in_string (ecma_string_t *str_p, lit_utf8_byte_t c); /** * Null character */ #define LIT_CHAR_NULL ((ecma_char_t) '\0') /* * Part of IsWordChar abstract operation (ECMA-262 v5, 15.10.2.6, step 3) */ bool lit_char_is_word_char (lit_code_point_t c); /* * Utility functions for uppercasing / lowercasing */ lit_code_point_t lit_char_to_lower_case (lit_code_point_t cp, ecma_stringbuilder_t *builder_p); lit_code_point_t lit_char_to_upper_case (lit_code_point_t cp, ecma_stringbuilder_t *builder_p); bool lit_char_fold_to_lower (lit_code_point_t cp); bool lit_char_fold_to_upper (lit_code_point_t cp); #endif /* !LIT_CHAR_HELPERS_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/meson.build000664 001750 001750 00000000025 15164251010 037375 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0subdir('jerry-core') src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_png/tvgPngLoader.cpp000664 001750 001750 00000006632 15164251010 037153 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgPngLoader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ void PngLoader::clear() { png_image_free(image); tvg::free(image); image = nullptr; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ PngLoader::PngLoader() : ImageLoader(FileType::Png) { image = tvg::calloc(1, sizeof(png_image)); image->version = PNG_IMAGE_VERSION; image->opaque = nullptr; } PngLoader::~PngLoader() { clear(); tvg::free(surface.buf32); } bool PngLoader::open(const char* path) { image->opaque = nullptr; if (!png_image_begin_read_from_file(image, path)) return false; w = (float)image->width; h = (float)image->height; return true; } bool PngLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy) { #ifdef THORVG_FILE_IO_SUPPORT image->opaque = nullptr; if (!png_image_begin_read_from_memory(image, data, size)) return false; w = (float)image->width; h = (float)image->height; return true; #else return false; #endif } bool PngLoader::read() { if (!LoadModule::read()) return true; if (w == 0 || h == 0) return false; if (ImageLoader::cs == ColorSpace::ARGB8888 || ImageLoader::cs == ColorSpace::ARGB8888S) { image->format = PNG_FORMAT_BGRA; surface.cs = ColorSpace::ARGB8888S; } else { image->format = PNG_FORMAT_RGBA; surface.cs = ColorSpace::ABGR8888S; } auto buffer = tvg::malloc(PNG_IMAGE_SIZE((*image))); if (!png_image_finish_read(image, NULL, buffer, 0, NULL)) { tvg::free(buffer); return false; } //setup the surface surface.buf32 = reinterpret_cast(buffer); surface.stride = (uint32_t)w; surface.w = (uint32_t)w; surface.h = (uint32_t)h; surface.channelSize = sizeof(uint32_t); //TODO: we can acquire a pre-multiplied image. See "png_structrp" surface.premultiplied = false; clear(); return true; } glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/meson.build000664 001750 001750 00000001671 15164251010 043305 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'ecma-alloc.h', 'ecma-error-messages.inc.h', 'ecma-errors.h', 'ecma-extended-info.h', 'ecma-gc.h', 'ecma-globals.h', 'ecma-helpers-number.h', 'ecma-helpers.h', 'ecma-init-finalize.h', 'ecma-lcache.h', 'ecma-literal-storage.h', 'ecma-module.h', 'ecma-property-hashmap.h', 'ecma-alloc.cpp', 'ecma-errors.cpp', 'ecma-extended-info.cpp', 'ecma-gc.cpp', 'ecma-helpers-collection.cpp', 'ecma-helpers-conversion.cpp', 'ecma-helpers-errol.cpp', 'ecma-helpers-external-pointers.cpp', 'ecma-helpers-number.cpp', 'ecma-helpers-string.cpp', 'ecma-helpers-value.cpp', 'ecma-helpers.cpp', 'ecma-init-finalize.cpp', 'ecma-lcache.cpp', 'ecma-literal-storage.cpp', 'ecma-module.cpp', 'ecma-property-hashmap.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-float64array-prototype.cpp000664 001750 001750 00000002337 15164251010 055146 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-float64array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID float64array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup float64arrayprototype ECMA Float64Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-urierror-prototype.cpp000664 001750 001750 00000002263 15164251010 052313 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-urierror-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID uri_error_prototype #include "ecma-builtin-internal-routines-template.inc.h" #endif /* JERRY_BUILTIN_ERRORS */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgColor.cpp000664 001750 001750 00000004371 15164251010 033604 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgColor.h" namespace tvg { void hsl2rgb(float h, float s, float l, uint8_t& r, uint8_t& g, uint8_t& b) { if (tvg::zero(s)) { r = g = b = (uint8_t)nearbyint(l * 255.0f); return; } if (tvg::equal(h, 360.0f)) { h = 0.0f; } else { h = fmod(h, 360.0f); if (h < 0.0f) h += 360.0f; h /= 60.0f; } auto v = (l <= 0.5f) ? (l * (1.0f + s)) : (l + s - (l * s)); auto p = l + l - v; auto sv = tvg::zero(v) ? 0.0f : (v - p) / v; auto i = static_cast(h); auto f = h - i; auto vsf = v * sv * f; auto t = p + vsf; auto q = v - vsf; float tr, tg, tb; switch (i) { case 0: tr = v; tg = t; tb = p; break; case 1: tr = q; tg = v; tb = p; break; case 2: tr = p; tg = v; tb = t; break; case 3: tr = p; tg = q; tb = v; break; case 4: tr = t; tg = p; tb = v; break; case 5: tr = v; tg = p; tb = q; break; default: tr = tg = tb = 0.0f; break; } r = (uint8_t)nearbyint(tr * 255.0f); g = (uint8_t)nearbyint(tg * 255.0f); b = (uint8_t)nearbyint(tb * 255.0f); } }jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-float32array-prototype.cpp000664 001750 001750 00000002232 15164251010 055133 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-float32array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID float32array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup float32arrayprototype ECMA Float32Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h000664 001750 001750 00000021535 15164251010 047511 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BUILTIN_HELPERS_H #define ECMA_BUILTIN_HELPERS_H #include "ecma-exceptions.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-regexp-object.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltinhelpers ECMA builtin helper operations * @{ */ /** * List of built-in routine identifiers. */ enum { /** These routines must be in this order */ ECMA_REGEXP_PROTOTYPE_ROUTINE_START = 0, ECMA_REGEXP_PROTOTYPE_ROUTINE_EXEC, #if JERRY_BUILTIN_ANNEXB ECMA_REGEXP_PROTOTYPE_ROUTINE_COMPILE, #endif /* JERRY_BUILTIN_ANNEXB */ ECMA_REGEXP_PROTOTYPE_ROUTINE_TEST, ECMA_REGEXP_PROTOTYPE_ROUTINE_TO_STRING, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_SOURCE, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_FLAGS, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_GLOBAL, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_IGNORE_CASE, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_MULTILINE, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_STICKY, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_UNICODE, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_DOT_ALL, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SEARCH, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_REPLACE, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SPLIT, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH_ALL, }; /** * Mode of string index routine. */ typedef enum { /** These routines must be in this order */ ECMA_STRING_LAST_INDEX_OF, /**< String.lastIndexOf: ECMA-262 v5, 15.5.4.8 */ ECMA_STRING_INDEX_OF, /**< String.indexOf: ECMA-262 v5, 15.5.4.7 */ ECMA_STRING_STARTS_WITH, /**< String.startsWith: ECMA-262 v6, 21.1.3.18 */ ECMA_STRING_INCLUDES, /**< String.includes: ECMA-262 v6, 21.1.3.7 */ ECMA_STRING_ENDS_WITH /**< String.includes: ECMA-262 v6, 21.1.3.6 */ } ecma_string_index_of_mode_t; ecma_value_t ecma_builtin_helper_object_to_string (const ecma_value_t this_arg); ecma_string_t *ecma_builtin_helper_get_to_locale_string_at_index (ecma_object_t *obj_p, ecma_length_t index); ecma_value_t ecma_builtin_helper_array_concat_value (ecma_object_t *obj_p, ecma_length_t *length_p, ecma_value_t value); ecma_value_t ecma_builtin_helper_uint32_index_normalize (ecma_value_t arg, uint32_t length, uint32_t *number_p); ecma_value_t ecma_builtin_helper_array_index_normalize (ecma_value_t arg, ecma_length_t length, ecma_length_t *number_p); ecma_value_t ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length, bool nan_to_zero); ecma_value_t ecma_builtin_helper_string_prototype_object_index_of (ecma_string_t *original_str_p, ecma_value_t arg1, ecma_value_t arg2, ecma_string_index_of_mode_t mode); uint32_t ecma_builtin_helper_string_find_index (ecma_string_t *original_str_p, ecma_string_t *search_str_p, uint32_t start_pos); ecma_value_t ecma_builtin_helper_def_prop (ecma_object_t *obj_p, ecma_string_t *name_p, ecma_value_t value, uint32_t opts); ecma_value_t ecma_builtin_helper_def_prop_by_index (ecma_object_t *obj_p, ecma_length_t index, ecma_value_t value, uint32_t opts); ecma_value_t ecma_builtin_helper_calculate_index (ecma_value_t index, ecma_length_t length, ecma_length_t *out_index); /** * Context for replace substitutions */ typedef struct { ecma_stringbuilder_t builder; /**< result string builder */ const lit_utf8_byte_t *string_p; /**< source string */ lit_utf8_size_t string_size; /**< source string size */ const lit_utf8_byte_t *matched_p; /**< matched string */ lit_utf8_size_t matched_size; /**< matched string size */ lit_utf8_size_t match_byte_pos; /**< byte position of the match in the source string */ uint16_t flags; /**< replace flags */ /** * Capture results */ union { #if JERRY_BUILTIN_REGEXP const ecma_regexp_capture_t *captures_p; /**< array of regexp capturing groups */ #endif /* JERRY_BUILTIN_REGEXP */ const ecma_collection_t *collection_p; /**< collection of captured substrings */ } u; uint32_t capture_count; /**< number of captures in the capturing group array */ ecma_string_t *replace_str_p; /**< replacement string */ } ecma_replace_context_t; void ecma_builtin_replace_substitute (ecma_replace_context_t *ctx_p); bool ecma_builtin_is_regexp_exec (ecma_extended_object_t *obj_p); #if JERRY_BUILTIN_DATE /** * Time range defines for helper functions. * * See also: * ECMA-262 v5, 15.9.1.1, 15.9.1.10 */ /** Hours in a day. */ #define ECMA_DATE_HOURS_PER_DAY (24) /** Minutes in an hour. */ #define ECMA_DATE_MINUTES_PER_HOUR (60) /** Seconds in a minute. */ #define ECMA_DATE_SECONDS_PER_MINUTE (60) /** Milliseconds in a second. */ #define ECMA_DATE_MS_PER_SECOND (1000) /** ECMA_DATE_MS_PER_MINUTE == 60000 */ #define ECMA_DATE_MS_PER_MINUTE (ECMA_DATE_MS_PER_SECOND * ECMA_DATE_SECONDS_PER_MINUTE) /** ECMA_DATE_MS_PER_HOUR == 3600000 */ #define ECMA_DATE_MS_PER_HOUR (ECMA_DATE_MS_PER_MINUTE * ECMA_DATE_MINUTES_PER_HOUR) /** ECMA_DATE_MS_PER_DAY == 86400000 */ #define ECMA_DATE_MS_PER_DAY ((ECMA_DATE_MS_PER_HOUR * ECMA_DATE_HOURS_PER_DAY)) #define ECMA_DATE_DAYS_IN_YEAR (365) #define ECMA_DATE_DAYS_IN_LEAP_YEAR (366) /** * This gives a range of 8,640,000,000,000,000 milliseconds * to either side of 01 January, 1970 UTC. */ #define ECMA_DATE_MAX_VALUE 8.64e15 /** * Timezone type. */ typedef enum { ECMA_DATE_UTC, /**< date value is in UTC */ ECMA_DATE_LOCAL /**< date value is in local time */ } ecma_date_timezone_t; /* ecma-builtin-helpers-date.c */ extern const char* day_names_p[7]; extern const char* month_names_p[12]; int32_t ecma_date_day_from_time (ecma_number_t time); int32_t ecma_date_year_from_time (ecma_number_t time); int32_t ecma_date_month_from_time (ecma_number_t time); int32_t ecma_date_date_from_time (ecma_number_t time); int32_t ecma_date_week_day (ecma_number_t time); int32_t ecma_date_hour_from_time (ecma_number_t time); int32_t ecma_date_min_from_time (ecma_number_t time); int32_t ecma_date_sec_from_time (ecma_number_t time); int32_t ecma_date_ms_from_time (ecma_number_t time); int32_t ecma_date_time_in_day_from_time (ecma_number_t time); int32_t ecma_date_local_time_zone_adjustment (ecma_number_t time); ecma_number_t ecma_date_utc (ecma_number_t time); ecma_number_t ecma_date_make_time (ecma_number_t hour, ecma_number_t min, ecma_number_t sec, ecma_number_t ms); ecma_number_t ecma_date_make_day (ecma_number_t year, ecma_number_t month, ecma_number_t date); ecma_number_t ecma_date_make_date (ecma_number_t day, ecma_number_t time); ecma_number_t ecma_date_time_clip (ecma_number_t time); ecma_value_t ecma_date_value_to_string (ecma_number_t datetime_number); ecma_value_t ecma_date_value_to_utc_string (ecma_number_t datetime_number); ecma_value_t ecma_date_value_to_iso_string (ecma_number_t datetime_number); ecma_value_t ecma_date_value_to_date_string (ecma_number_t datetime_number); ecma_value_t ecma_date_value_to_time_string (ecma_number_t datetime_number); #endif /* JERRY_BUILTIN_DATE */ /* ecma-builtin-helper-error.c */ ecma_value_t ecma_builtin_helper_error_dispatch_call (jerry_error_t error_type, const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); /* ecma-builtin-helpers-sort.c */ /** * Comparison callback function header for sorting helper routines. */ typedef ecma_value_t (*ecma_builtin_helper_sort_compare_fn_t) (ecma_value_t lhs, /**< left value */ ecma_value_t rhs, /**< right value */ ecma_value_t compare_func, /**< compare function */ ecma_object_t *array_buffer_p /**< arrayBuffer */); ecma_value_t ecma_builtin_helper_array_merge_sort_helper (ecma_value_t *array_p, uint32_t length, ecma_value_t compare_func, const ecma_builtin_helper_sort_compare_fn_t sort_cb, ecma_object_t *array_buffer_p); /** * @} * @} */ #endif /* !ECMA_BUILTIN_HELPERS_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/vm-utils.cpp000664 001750 001750 00000004216 15164251010 042230 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-array-object.h" #include "ecma-helpers.h" #include "jcontext.h" #include "lit-char-helpers.h" #include "vm.h" /** * Check whether currently executed code is strict mode code * * @return true - current code is executed in strict mode, * false - otherwise */ bool vm_is_strict_mode (void) { JERRY_ASSERT (JERRY_CONTEXT (vm_top_context_p) != NULL); return JERRY_CONTEXT (vm_top_context_p)->status_flags & VM_FRAME_CTX_IS_STRICT; } /* vm_is_strict_mode */ /** * Check whether currently performed call (on top of call-stack) is performed in form, * meeting conditions of 'Direct Call to Eval' (see also: ECMA-262 v5, 15.1.2.1.1) * * Warning: * the function should only be called from implementation * of built-in 'eval' routine of Global object * * @return true - currently performed call is performed through 'eval' identifier, * without 'this' argument, * false - otherwise */ bool vm_is_direct_eval_form_call (void) { return (JERRY_CONTEXT (status_flags) & ECMA_STATUS_DIRECT_EVAL) != 0; } /* vm_is_direct_eval_form_call */ /** * Get backtrace. The backtrace is an array of strings where * each string contains the position of the corresponding frame. * The array length is zero if the backtrace is not available. * * @return array ecma value */ ecma_value_t vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimited */ { JERRY_UNUSED (max_depth); return ecma_make_object_value (ecma_op_new_array_object (0)); } /* vm_get_backtrace */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakmap-prototype.cpp000664 001750 001750 00000004402 15164251010 052064 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-container-object.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH #define BUILTIN_INC_HEADER_NAME "ecma-builtin-weakmap-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID weakmap_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup weakmap ECMA WeakMap object built-in * @{ */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_weakmap_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); return ecma_builtin_container_dispatch_routine (builtin_routine_id, this_arg, arguments_list_p, LIT_MAGIC_STRING_WEAKMAP_UL); } /* ecma_builtin_weakmap_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-tagged-template-literal.cpp000664 001750 001750 00000014426 15164251010 050344 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "js-parser-tagged-template-literal.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "js-lexer.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_tagged_template_literal Tagged template literal * @{ */ /** * Append the cooked and raw string to the corresponding array */ void parser_tagged_template_literal_append_strings (parser_context_t *context_p, /**< parser context */ ecma_object_t *template_obj_p, /**< template object */ ecma_object_t *raw_strings_p, /**< raw strings object */ uint32_t prop_idx) /**< property index to set the values */ { lexer_lit_location_t *lit_loc_p = &context_p->token.lit_location; if (lit_loc_p->length == 0 && !(lit_loc_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { ecma_builtin_helper_def_prop_by_index (template_obj_p, prop_idx, ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY), ECMA_PROPERTY_FLAG_ENUMERABLE); ecma_builtin_helper_def_prop_by_index (raw_strings_p, prop_idx, ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY), ECMA_PROPERTY_FLAG_ENUMERABLE); return; } uint8_t local_byte_array[LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE]; const uint8_t *source_p = lexer_convert_literal_to_chars (context_p, &context_p->token.lit_location, local_byte_array, LEXER_STRING_NO_OPTS); ecma_string_t *raw_str_p; ecma_string_t *cooked_str_p = ((lit_loc_p->status_flags & LEXER_FLAG_ASCII) ? ecma_new_ecma_string_from_ascii (source_p, lit_loc_p->length) : ecma_new_ecma_string_from_utf8 (source_p, lit_loc_p->length)); parser_free_allocated_buffer (context_p); if (lit_loc_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE) { context_p->source_p = context_p->token.lit_location.char_p - 1; lexer_parse_string (context_p, LEXER_STRING_RAW); source_p = lexer_convert_literal_to_chars (context_p, &context_p->token.lit_location, local_byte_array, LEXER_STRING_RAW); raw_str_p = ((lit_loc_p->status_flags & LEXER_FLAG_ASCII) ? ecma_new_ecma_string_from_ascii (source_p, lit_loc_p->length) : ecma_new_ecma_string_from_utf8 (source_p, lit_loc_p->length)); parser_free_allocated_buffer (context_p); } else { ecma_ref_ecma_string (cooked_str_p); raw_str_p = cooked_str_p; } ecma_builtin_helper_def_prop_by_index (template_obj_p, prop_idx, ecma_make_string_value (cooked_str_p), ECMA_PROPERTY_FLAG_ENUMERABLE); ecma_builtin_helper_def_prop_by_index (raw_strings_p, prop_idx, ecma_make_string_value (raw_str_p), ECMA_PROPERTY_FLAG_ENUMERABLE); ecma_deref_ecma_string (cooked_str_p); ecma_deref_ecma_string (raw_str_p); } /* parser_tagged_template_literal_append_strings */ /** * Create new tagged template literal object * * @return pointer to the allocated object */ ecma_object_t * parser_new_tagged_template_literal (ecma_object_t **raw_strings_p) /**< [out] raw strings object */ { ecma_object_t *template_obj_p = ecma_op_new_array_object (0); *raw_strings_p = ecma_op_new_array_object (0); ecma_extended_object_t *template_ext_obj_p = (ecma_extended_object_t *) template_obj_p; ecma_extended_object_t *raw_ext_obj_p = (ecma_extended_object_t *) *raw_strings_p; const uint8_t flags = ECMA_PROPERTY_VIRTUAL | ECMA_PROPERTY_FLAG_WRITABLE | ECMA_FAST_ARRAY_FLAG; JERRY_ASSERT (template_ext_obj_p->u.array.length_prop_and_hole_count == flags); JERRY_ASSERT (raw_ext_obj_p->u.array.length_prop_and_hole_count == flags); template_ext_obj_p->u.array.length_prop_and_hole_count = flags | ECMA_ARRAY_TEMPLATE_LITERAL; raw_ext_obj_p->u.array.length_prop_and_hole_count = flags | ECMA_ARRAY_TEMPLATE_LITERAL; ecma_builtin_helper_def_prop (template_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_RAW), ecma_make_object_value (*raw_strings_p), ECMA_PROPERTY_FIXED); return template_obj_p; } /* parser_new_tagged_template_literal */ /** * Set integrity level of the given template array object to "frozen" */ static void parser_tagged_template_literal_freeze_array (ecma_object_t *obj_p /**< template object */) { JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY); ecma_op_ordinary_object_prevent_extensions (obj_p); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; ext_obj_p->u.array.length_prop_and_hole_count &= (uint32_t) ~ECMA_PROPERTY_FLAG_WRITABLE; } /* parser_tagged_template_literal_freeze_array */ /** * Finalize the tagged template object */ void parser_tagged_template_literal_finalize (ecma_object_t *template_obj_p, /**< template object */ ecma_object_t *raw_strings_p) /**< raw strings object */ { parser_tagged_template_literal_freeze_array (template_obj_p); parser_tagged_template_literal_freeze_array (raw_strings_p); } /* parser_tagged_template_literal_finalize */ /** * @} * @} * @} */ jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint8clampedarray-prototype.cpp000664 001750 001750 00000002256 15164251010 056264 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-uint8clampedarray-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID uint8clampedarray_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup uint8clampedarrayprototype ECMA Uint8ClampedArray.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint32array-prototype.inc.h000664 001750 001750 00000001620 15164251010 055222 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Uint32Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 4 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_UINT32ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jcontext/000775 001750 001750 00000000000 15164251010 041155 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modulesexternal/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-magic-strings.inc.h000664 001750 001750 00000131715 15164251010 044375 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is automatically generated by the gen-strings.py script * from lit-magic-strings.ini. Do not edit! */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING__EMPTY, "") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SPACE_CHAR, " ") #if JERRY_MODULE_SYSTEM LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASTERISK_CHAR, "*") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COMMA_CHAR, ",") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_E_U, "E") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LEFT_SQUARE_CHAR, "[") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR, "]") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PI_U, "PI") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_AT, "at") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS, "is") #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_OF, "of") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_OR, "or") #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LN2_U, "LN2") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MAP_UL, "Map") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NAN, "NaN") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UL, "Set") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UTC_U, "UTC") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ABS, "abs") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_ATOMICS || JERRY_BUILTIN_CONTAINER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ADD, "add") #endif /* JERRY_BUILTIN_ATOMICS \ || JERRY_BUILTIN_CONTAINER */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ALL, "all") #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_AND, "and") #endif /* JERRY_BUILTIN_ATOMICS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ANY, "any") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COS, "cos") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EXP, "exp") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FOR, "for") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET, "get") #if JERRY_BUILTIN_CONTAINER || JERRY_BUILTIN_PROXY || JERRY_BUILTIN_REFLECT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_HAS, "has") #endif /* JERRY_BUILTIN_CONTAINER \ || JERRY_BUILTIN_PROXY \ || JERRY_BUILTIN_REFLECT */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOG, "log") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MAP, "map") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MAX, "max") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MIN, "min") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NOW, "now") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_ARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_POP, "pop") #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_POW, "pow") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RAW, "raw") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET, "set") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SIN, "sin") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_SUB, "sub") #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TAN, "tan") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_XOR, "xor") #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EMPTY_NON_CAPTURE_GROUP, "(?:)") #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DATE_UL, "Date") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LN10_U, "LN10") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MATH_UL, "Math") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NULL_UL, "Null") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ACOS, "acos") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASIN, "asin") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATAN, "atan") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIND, "bind") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CALL, "call") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CBRT, "cbrt") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CEIL, "ceil") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COSH, "cosh") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DONE, "done") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EVAL, "eval") #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EXEC, "exec") #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FILL, "fill") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FIND, "find") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FLAT, "flat") #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FROM, "from") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IMUL, "imul") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_JOIN, "join") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_KEYS, "keys") #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_LOAD, "load") #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOG2, "log2") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NAME, "name") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NEXT, "next") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NULL, "null") #if JERRY_BUILTIN_ARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PUSH, "push") #endif /* JERRY_BUILTIN_ARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RACE, "race") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SEAL, "seal") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SIGN, "sign") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SINH, "sinh") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_CONTAINER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SIZE, "size") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SOME, "some") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SORT, "sort") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SQRT, "sqrt") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TANH, "tanh") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TEST, "test") #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_THEN, "then") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TRIM, "trim") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TRUE, "true") #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_WAIT, "wait") #endif /* JERRY_BUILTIN_ATOMICS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARRAY_UL, "Array") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ERROR_UL, "Error") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOG2E_U, "LOG2E") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_PROXY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PROXY_UL, "Proxy") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SQRT2_U, "SQRT2") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ACOSH, "acosh") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_APPLY, "apply") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASINH, "asinh") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATAN2, "atan2") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATANH, "atanh") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CATCH, "catch") #if JERRY_BUILTIN_CONTAINER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CLEAR, "clear") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CLZ32, "clz32") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_WEAKREF LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEREF, "deref") #endif /* JERRY_BUILTIN_WEAKREF */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EVERY, "every") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EXPM1, "expm1") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FALSE, "false") #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FLAGS, "flags") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FLOOR, "floor") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_HYPOT, "hypot") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INDEX, "index") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INPUT, "input") #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_NAN, "isNaN") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOG10, "log10") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOG1P, "log1p") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MATCH, "match") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PARSE, "parse") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_PROXY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PROXY, "proxy") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ROUND, "round") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_ARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SHIFT, "shift") #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_SHAREDARRAYBUFFER || JERRY_BUILTIN_STRING || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SLICE, "slice") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_SHAREDARRAYBUFFER \ || JERRY_BUILTIN_STRING \ || JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SPLIT, "split") #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_STORE, "store") #endif /* JERRY_BUILTIN_ATOMICS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_THROW, "throw") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TRUNC, "trunc") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_VALUE, "value") #if JERRY_PARSER && JERRY_SOURCE_NAME LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SOURCE_NAME_EVAL, "") #endif /* JERRY_PARSER && JERRY_SOURCE_NAME */ #if JERRY_BUILTIN_BIGINT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIGINT_UL, "BigInt") #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOG10E_U, "LOG10E") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_MODULE_SYSTEM LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MODULE_UL, "Module") #endif /* JERRY_MODULE_SYSTEM */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NUMBER_UL, "Number") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_OBJECT_UL, "Object") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REGEXP_UL, "RegExp") #if JERRY_PARSER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SCRIPT_UL, "Script") #endif /* JERRY_PARSER */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STRING_UL, "String") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SYMBOL_UL, "Symbol") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASSIGN, "assign") #if JERRY_BUILTIN_BIGINT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIGINT, "bigint") #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_DATAVIEW || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BUFFER, "buffer") #endif /* JERRY_BUILTIN_DATAVIEW \ || JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CALLEE, "callee") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CALLER, "caller") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CHAR_AT_UL, "charAt") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CONCAT, "concat") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CREATE, "create") #if JERRY_BUILTIN_CONTAINER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DELETE, "delete") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DOTALL, "dotAll") #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ERRORS_UL, "errors") #if JERRY_BUILTIN_ANNEXB LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ESCAPE, "escape") #endif /* JERRY_BUILTIN_ANNEXB */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FILTER, "filter") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FREEZE, "freeze") #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FROUND, "fround") #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_DAY_UL, "getDay") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GLOBAL, "global") #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_HAS_OWN_UL, "hasOwn") #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_VIEW_UL, "isView") #endif /* JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_KEY_FOR, "keyFor") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LENGTH, "length") #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_NOTIFY, "notify") #endif /* JERRY_BUILTIN_ATOMICS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NUMBER, "number") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_OBJECT, "object") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PAD_END, "padEnd") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RANDOM, "random") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REASON, "reason") #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REDUCE, "reduce") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REJECT, "reject") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REPEAT, "repeat") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RETURN, "return") #if JERRY_BUILTIN_PROXY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REVOKE, "revoke") #endif /* JERRY_BUILTIN_PROXY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SEARCH, "search") #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SOURCE, "source") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_ARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SPLICE, "splice") #endif /* JERRY_BUILTIN_ARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STATUS, "status") #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STICKY, "sticky") #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STRING, "string") #if JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SUBSTR, "substr") #endif /* JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SYMBOL, "symbol") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_JSON_UL, "toJSON") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_VALUES, "values") #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_U, "Atomics") #endif /* JERRY_BUILTIN_ATOMICS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BOOLEAN_UL, "Boolean") #if JERRY_BUILTIN_NUMBER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EPSILON_U, "EPSILON") #endif /* JERRY_BUILTIN_NUMBER */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PROMISE_UL, "Promise") #if JERRY_BUILTIN_REFLECT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REFLECT_UL, "Reflect") #endif /* JERRY_BUILTIN_REFLECT */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SQRT1_2_U, "SQRT1_2") #endif /* JERRY_BUILTIN_MATH */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SYMBOL_DOT_UL, "Symbol.") #if JERRY_BUILTIN_CONTAINER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_WEAKMAP_UL, "WeakMap") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_WEAKREF LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_WEAKREF_UL, "WeakRef") #endif /* JERRY_BUILTIN_WEAKREF */ #if JERRY_BUILTIN_CONTAINER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_WEAKSET_UL, "WeakSet") #endif /* JERRY_BUILTIN_CONTAINER */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BOOLEAN, "boolean") #if JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COMPILE, "compile") #endif /* JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFAULT, "default") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENTRIES, "entries") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FINALLY, "finally") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FLATMAP, "flatMap") #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_CONTAINER || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FOR_EACH_UL, "forEach") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_CONTAINER \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_DATE_UL, "getDate") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_INT8_UL, "getInt8") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_TIME_UL, "getTime") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_YEAR_UL, "getYear") #endif /* JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_STRING || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INDEX_OF_UL, "indexOf") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_STRING \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_ARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_ARRAY_UL, "isArray") #endif /* JERRY_BUILTIN_ARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MESSAGE, "message") #if JERRY_BUILTIN_PROXY || JERRY_BUILTIN_REFLECT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_OWN_KEYS_UL, "ownKeys") #endif /* JERRY_BUILTIN_PROXY \ || JERRY_BUILTIN_REFLECT */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REPLACE, "replace") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RESOLVE, "resolve") #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REVERSE, "reverse") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_DATE_UL, "setDate") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_INT8_UL, "setInt8") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_TIME_UL, "setTime") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_YEAR_UL, "setYear") #endif /* JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SPECIES, "species") #if JERRY_BUILTIN_NUMBER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_FIXED_UL, "toFixed") #endif /* JERRY_BUILTIN_NUMBER */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TRIM_END, "trimEnd") #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UNICODE, "unicode") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_ARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UNSHIFT, "unshift") #endif /* JERRY_BUILTIN_ARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_VALUE_OF_UL, "valueOf") #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DATAVIEW_UL, "DataView") #endif /* JERRY_BUILTIN_DATAVIEW */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_UL, "Function") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INFINITY_UL, "Infinity") #if JERRY_BUILTIN_ERRORS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_URI_ERROR_UL, "URIError") #endif /* JERRY_BUILTIN_ERRORS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_OBJECT_TO_STRING_UL, "[object ") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENDS_WITH, "endsWith") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_EXCHANGE, "exchange") #endif /* JERRY_BUILTIN_ATOMICS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION, "function") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_HOURS_UL, "getHours") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_INT16_UL, "getInt16") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_INT32_UL, "getInt32") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_MONTH_UL, "getMonth") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UINT8_UL, "getUint8") #endif /* JERRY_BUILTIN_DATAVIEW */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INCLUDES, "includes") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_FINITE, "isFinite") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_FROZEN_UL, "isFrozen") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_SEALED_UL, "isSealed") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ITERATOR, "iterator") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MATCH_ALL, "matchAll") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PAD_START, "padStart") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PARSE_INT, "parseInt") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REJECTED, "rejected") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_HOURS_UL, "setHours") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_INT16_UL, "setInt16") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_INT32_UL, "setInt32") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_MONTH_UL, "setMonth") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UINT8_UL, "setUint8") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SUBARRAY, "subarray") #endif /* JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_STRING_UL, "toString") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TRIM_LEFT, "trimLeft") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_ANNEXB LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UNESCAPE, "unescape") #endif /* JERRY_BUILTIN_ANNEXB */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_WRITABLE, "writable") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NEGATIVE_INFINITY_UL, "-Infinity") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARGUMENTS_UL, "Arguments") #if JERRY_BUILTIN_ERRORS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EVAL_ERROR_UL, "EvalError") #endif /* JERRY_BUILTIN_ERRORS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GENERATOR_UL, "Generator") #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INT8_ARRAY_UL, "Int8Array") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_NUMBER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MAX_VALUE_U, "MAX_VALUE") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MIN_VALUE_U, "MIN_VALUE") #endif /* JERRY_BUILTIN_NUMBER */ #if JERRY_BUILTIN_ERRORS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TYPE_ERROR_UL, "TypeError") #endif /* JERRY_BUILTIN_ERRORS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UNDEFINED_UL, "Undefined") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING__PROTO__, "__proto__") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ANONYMOUS, "anonymous") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARGUMENTS, "arguments") #if JERRY_BUILTIN_PROXY || JERRY_BUILTIN_REFLECT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CONSTRUCT, "construct") #endif /* JERRY_BUILTIN_PROXY \ || JERRY_BUILTIN_REFLECT */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DECODE_URI, "decodeURI") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENCODE_URI, "encodeURI") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FIND_INDEX, "findIndex") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FULFILLED, "fulfilled") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_DAY_UL, "getUTCDay") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UINT16_UL, "getUint16") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UINT32_UL, "getUint32") #endif /* JERRY_BUILTIN_DATAVIEW */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_INTEGER, "isInteger") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LASTINDEX_UL, "lastIndex") #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MULTILINE, "multiline") #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PROTOTYPE, "prototype") #if JERRY_BUILTIN_PROXY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REVOCABLE, "revocable") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UINT16_UL, "setUint16") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UINT32_UL, "setUint32") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SUBSTRING, "substring") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TRIM_RIGHT, "trimRight") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TRIM_START, "trimStart") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UNDEFINED, "undefined") #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INT16_ARRAY_UL, "Int16Array") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INT32_ARRAY_UL, "Int32Array") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_ERRORS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RANGE_ERROR_UL, "RangeError") #endif /* JERRY_BUILTIN_ERRORS */ #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TYPED_ARRAY_UL, "TypedArray") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UINT8_ARRAY_UL, "Uint8Array") #endif /* JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ALLSETTLED, "allSettled") #if JERRY_BUILTIN_DATAVIEW || JERRY_BUILTIN_SHAREDARRAYBUFFER || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BYTE_LENGTH_UL, "byteLength") #endif /* JERRY_BUILTIN_DATAVIEW \ || JERRY_BUILTIN_SHAREDARRAYBUFFER \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_DATAVIEW || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BYTE_OFFSET_UL, "byteOffset") #endif /* JERRY_BUILTIN_DATAVIEW \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CHAR_CODE_AT_UL, "charCodeAt") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COPY_WITHIN, "copyWithin") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENUMERABLE, "enumerable") #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_FLOAT_32_UL, "getFloat32") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATAVIEW && JERRY_NUMBER_TYPE_FLOAT64 LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_FLOAT_64_UL, "getFloat64") #endif /* JERRY_BUILTIN_DATAVIEW && JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_MINUTES_UL, "getMinutes") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_SECONDS_UL, "getSeconds") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_DATE_UL, "getUTCDate") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_GLOBAL_THIS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GLOBAL_THIS_UL, "globalThis") #endif /* JERRY_BUILTIN_GLOBAL_THIS */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IGNORECASE_UL, "ignoreCase") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_ISLOCKFREE, "isLockFree") #endif /* JERRY_BUILTIN_ATOMICS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PARSE_FLOAT, "parseFloat") #if JERRY_BUILTIN_REGEXP && JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REPLACE_ALL, "replaceAll") #endif /* JERRY_BUILTIN_REGEXP && JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_FLOAT_32_UL, "setFloat32") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATAVIEW && JERRY_NUMBER_TYPE_FLOAT64 LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_FLOAT_64_UL, "setFloat64") #endif /* JERRY_BUILTIN_DATAVIEW && JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_MINUTES_UL, "setMinutes") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_SECONDS_UL, "setSeconds") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_DATE_UL, "setUTCDate") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STARTS_WITH, "startsWith") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SOURCE_NAME_ANON, "") #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARRAY_BUFFER_UL, "ArrayBuffer") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_ERRORS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SYNTAX_ERROR_UL, "SyntaxError") #endif /* JERRY_BUILTIN_ERRORS */ #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UINT16_ARRAY_UL, "Uint16Array") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UINT32_ARRAY_UL, "Uint32Array") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CODE_POINT_AT, "codePointAt") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CONSTRUCTOR, "constructor") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DESCRIPTION, "description") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_OBJECT_FROM_ENTRIES, "fromEntries") #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_BIGINT64, "getBigInt64") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_FULL_YEAR_UL, "getFullYear") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_HOURS_UL, "getUTCHours") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_MONTH_UL, "getUTCMonth") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_HAS_INSTANCE, "hasInstance") #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_STRING || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LAST_INDEX_OF_UL, "lastIndexOf") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_STRING \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REDUCE_RIGHT_UL, "reduceRight") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_BIGINT64, "setBigInt64") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_FULL_YEAR_UL, "setFullYear") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_HOURS_UL, "setUTCHours") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_MONTH_UL, "setUTCMonth") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_GMT_STRING_UL, "toGMTString") #endif /* JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_ISO_STRING_UL, "toISOString") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_LOWER_CASE_UL, "toLowerCase") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_NUMBER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_PRECISION_UL, "toPrecision") #endif /* JERRY_BUILTIN_NUMBER */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_PRIMITIVE, "toPrimitive") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_STRING_TAG, "toStringTag") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_UTC_STRING_UL, "toUTCString") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_UPPER_CASE_UL, "toUpperCase") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UNSCOPABLES, "unscopables") #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FLOAT32_ARRAY_UL, "Float32Array") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_TYPEDARRAY && JERRY_NUMBER_TYPE_FLOAT64 LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FLOAT64_ARRAY_UL, "Float64Array") #endif /* JERRY_BUILTIN_TYPEDARRAY && JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INVALID_DATE_UL, "Invalid Date") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MAP_ITERATOR_UL, "Map Iterator") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_ITERATOR_UL, "Set Iterator") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CONFIGURABLE, "configurable") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FROM_CHAR_CODE_UL, "fromCharCode") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_BIGUINT64, "getBigUint64") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_DATAVIEW */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_EXTENSIBLE, "isExtensible") #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_BIGUINT64, "setBigUint64") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_DATE_STRING_UL, "toDateString") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_TIME_STRING_UL, "toTimeString") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_FUNCTION_UL, "AsyncFunction") #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIGINT64_ARRAY_UL, "BigInt64Array") #endif /* JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_ITERATOR, "asyncIterator") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FROM_CODE_POINT_UL, "fromCodePoint") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_MINUTES_UL, "getUTCMinutes") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_SECONDS_UL, "getUTCSeconds") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_PROTOTYPE_OF_UL, "isPrototypeOf") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_SAFE_INTEGER, "isSafeInteger") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOCALE_COMPARE_UL, "localeCompare") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_MINUTES_UL, "setUTCMinutes") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_SECONDS_UL, "setUTCSeconds") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_NUMBER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_EXPONENTIAL_UL, "toExponential") #endif /* JERRY_BUILTIN_NUMBER */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_AGGREGATE_ERROR_UL, "AggregateError") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARRAY_ITERATOR_UL, "Array Iterator") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_GENERATOR_UL, "AsyncGenerator") #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIGUINT64_ARRAY_UL, "BigUint64Array") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_ERRORS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REFERENCE_ERROR_UL, "ReferenceError") #endif /* JERRY_BUILTIN_ERRORS */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFINE_PROPERTY_UL, "defineProperty") #if JERRY_BUILTIN_PROXY || JERRY_BUILTIN_REFLECT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DELETE_PROPERTY_UL, "deleteProperty") #endif /* JERRY_BUILTIN_PROXY \ || JERRY_BUILTIN_REFLECT */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_PROTOTYPE_OF_UL, "getPrototypeOf") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_FULL_YEAR_UL, "getUTCFullYear") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_HAS_OWN_PROPERTY_UL, "hasOwnProperty") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_PROTOTYPE_OF_UL, "setPrototypeOf") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_FULL_YEAR_UL, "setUTCFullYear") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, "toLocaleString") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STRING_ITERATOR_UL, "String Iterator") #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ATOMICS_COMPAREEXCHANGE, "compareExchange") #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_MILLISECONDS_UL, "getMilliseconds") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_MILLISECONDS_UL, "setMilliseconds") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_NUMBER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MAX_SAFE_INTEGER_U, "MAX_SAFE_INTEGER") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MIN_SAFE_INTEGER_U, "MIN_SAFE_INTEGER") #endif /* JERRY_BUILTIN_NUMBER */ #if JERRY_BUILTIN_ANNEXB LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFINE_GETTER, "__defineGetter__") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFINE_SETTER, "__defineSetter__") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOOKUP_GETTER, "__lookupGetter__") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOOKUP_SETTER, "__lookupSetter__") #endif /* JERRY_BUILTIN_ANNEXB */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFINE_PROPERTIES_UL, "defineProperties") #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BYTES_PER_ELEMENT_U, "BYTES_PER_ELEMENT") #endif /* JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GENERATOR_FUNCTION_UL, "GeneratorFunction") #if JERRY_BUILTIN_NUMBER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NEGATIVE_INFINITY_U, "NEGATIVE_INFINITY") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_POSITIVE_INFINITY_U, "POSITIVE_INFINITY") #endif /* JERRY_BUILTIN_NUMBER */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SHARED_ARRAY_BUFFER_UL, "SharedArrayBuffer") #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UINT8_CLAMPED_ARRAY_UL, "Uint8ClampedArray") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_TIMEZONE_OFFSET_UL, "getTimezoneOffset") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PREVENT_EXTENSIONS_UL, "preventExtensions") #if JERRY_BUILTIN_STRING LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_LOCALE_LOWER_CASE_UL, "toLocaleLowerCase") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_LOCALE_UPPER_CASE_UL, "toLocaleUpperCase") #endif /* JERRY_BUILTIN_STRING */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DECODE_URI_COMPONENT, "decodeURIComponent") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENCODE_URI_COMPONENT, "encodeURIComponent") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_MILLISECONDS_UL, "getUTCMilliseconds") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_CONCAT_SPREADABLE, "isConcatSpreadable") #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_MILLISECONDS_UL, "setUTCMilliseconds") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_LOCALE_DATE_STRING_UL, "toLocaleDateString") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_LOCALE_TIME_STRING_UL, "toLocaleTimeString") #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL, "getOwnPropertyNames") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL, "propertyIsEnumerable") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL, "getOwnPropertySymbols") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, "AsyncGeneratorFunction") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL, "RegExp String Iterator") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL, "getOwnPropertyDescriptor") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL, "getOwnPropertyDescriptors") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE, "function () { [native code] }") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA, "function () { /* ecmascript */ }") LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (0, LIT_MAGIC_STRING__EMPTY) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (1, LIT_MAGIC_STRING_SPACE_CHAR) #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (2, LIT_MAGIC_STRING_PI_U) #else /* !(JERRY_BUILTIN_MATH) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (2, LIT_MAGIC_STRING_AT) #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_MATH LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (3, LIT_MAGIC_STRING_LN2_U) #else /* !(JERRY_BUILTIN_MATH) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (3, LIT_MAGIC_STRING_MAP_UL) #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (4, LIT_MAGIC_STRING_EMPTY_NON_CAPTURE_GROUP) #else /* !(JERRY_BUILTIN_REGEXP) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (4, LIT_MAGIC_STRING_DATE_UL) #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (5, LIT_MAGIC_STRING_ARRAY_UL) #if JERRY_PARSER && JERRY_SOURCE_NAME LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (6, LIT_MAGIC_STRING_SOURCE_NAME_EVAL) #elif JERRY_BUILTIN_BIGINT LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (6, LIT_MAGIC_STRING_BIGINT_UL) #elif JERRY_BUILTIN_MATH LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (6, LIT_MAGIC_STRING_LOG10E_U) #elif JERRY_MODULE_SYSTEM LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (6, LIT_MAGIC_STRING_MODULE_UL) #else /* !(JERRY_MODULE_SYSTEM) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (6, LIT_MAGIC_STRING_NUMBER_UL) #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_ATOMICS LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (7, LIT_MAGIC_STRING_ATOMICS_U) #else /* !(JERRY_BUILTIN_ATOMICS) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (7, LIT_MAGIC_STRING_BOOLEAN_UL) #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (8, LIT_MAGIC_STRING_DATAVIEW_UL) #else /* !(JERRY_BUILTIN_DATAVIEW) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (8, LIT_MAGIC_STRING_FUNCTION_UL) #endif /* JERRY_BUILTIN_DATAVIEW */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (9, LIT_MAGIC_STRING_NEGATIVE_INFINITY_UL) #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_INT16_ARRAY_UL) #elif JERRY_BUILTIN_ERRORS LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_RANGE_ERROR_UL) #elif JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_TYPED_ARRAY_UL) #else /* !(JERRY_BUILTIN_TYPEDARRAY) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_ALLSETTLED) #endif /* JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (11, LIT_MAGIC_STRING_SOURCE_NAME_ANON) #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (12, LIT_MAGIC_STRING_FLOAT32_ARRAY_UL) #elif JERRY_BUILTIN_TYPEDARRAY && JERRY_NUMBER_TYPE_FLOAT64 LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (12, LIT_MAGIC_STRING_FLOAT64_ARRAY_UL) #elif JERRY_BUILTIN_DATE LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (12, LIT_MAGIC_STRING_INVALID_DATE_UL) #else /* !(JERRY_BUILTIN_DATE) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (12, LIT_MAGIC_STRING_MAP_ITERATOR_UL) #endif /* JERRY_BUILTIN_DATE */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (13, LIT_MAGIC_STRING_ASYNC_FUNCTION_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (14, LIT_MAGIC_STRING_AGGREGATE_ERROR_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (15, LIT_MAGIC_STRING_STRING_ITERATOR_UL) #if JERRY_BUILTIN_NUMBER LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (16, LIT_MAGIC_STRING_MAX_SAFE_INTEGER_U) #elif JERRY_BUILTIN_ANNEXB LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (16, LIT_MAGIC_STRING_DEFINE_GETTER) #else /* !(JERRY_BUILTIN_ANNEXB) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (16, LIT_MAGIC_STRING_DEFINE_PROPERTIES_UL) #endif /* JERRY_BUILTIN_ANNEXB */ #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (17, LIT_MAGIC_STRING_BYTES_PER_ELEMENT_U) #else /* !(JERRY_BUILTIN_TYPEDARRAY) */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (17, LIT_MAGIC_STRING_GENERATOR_FUNCTION_UL) #endif /* JERRY_BUILTIN_TYPEDARRAY */ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (18, LIT_MAGIC_STRING_DECODE_URI_COMPONENT) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (19, LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (20, LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (21, LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (22, LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (23, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (24, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (26, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (27, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (28, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (29, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (30, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (31, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (32, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/inc/000775 001750 001750 00000000000 15164251010 027766 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgXmlParser.cpp000664 001750 001750 00000042326 15164251010 035405 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgStr.h" #include "tvgXmlParser.h" #include "tvgSvgUtil.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ bool _unsupported(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue) { #ifdef THORVG_LOG_ENABLED const auto attributesNum = 6; const struct { const char* tag; bool tagWildcard; //If true, it is assumed that a wildcard is used after the tag. (ex: tagName*) const char* value; } attributes[] = { {"id", false, nullptr}, {"data-name", false, nullptr}, {"overflow", false, "visible"}, {"version", false, nullptr}, {"xmlns", true, nullptr}, {"xml:space", false, nullptr}, }; for (unsigned int i = 0; i < attributesNum; ++i) { if (tvg::equal(tagAttribute, attributes[i].tag)) { if (!attributes[i].value) return false; if (tvg::equal(tagValue, attributes[i].value)) return false; } } return true; #endif return false; } static const char* _xmlFindWhiteSpace(const char* itr, const char* itrEnd) { for (; itr < itrEnd; itr++) { if (isspace((unsigned char)*itr)) break; } return itr; } static const char* _xmlSkipXmlEntities(const char* itr, const char* itrEnd) { auto p = itr; while (itr < itrEnd && *itr == '&') { for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) { if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) { itr += xmlEntityLength[i]; break; } } if (itr == p) break; p = itr; } return itr; } static const char* _xmlUnskipXmlEntities(const char* itr, const char* itrStart) { auto p = itr; while (itr > itrStart && *(itr - 1) == ';') { for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) { if (itr - xmlEntityLength[i] > itrStart && strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) { itr -= xmlEntityLength[i]; break; } } if (itr == p) break; p = itr; } return itr; } static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* itrEnd) { itr = svgUtilSkipWhiteSpace(itr, itrEnd); auto p = itr; while (true) { if (p != (itr = _xmlSkipXmlEntities(itr, itrEnd))) p = itr; else break; if (p != (itr = svgUtilSkipWhiteSpace(itr, itrEnd))) p = itr; else break; } return itr; } static const char* _unskipWhiteSpacesAndXmlEntities(const char* itr, const char* itrStart) { itr = svgUtilUnskipWhiteSpace(itr, itrStart); auto p = itr; while (true) { if (p != (itr = _xmlUnskipXmlEntities(itr, itrStart))) p = itr; else break; if (p != (itr = svgUtilUnskipWhiteSpace(itr, itrStart))) p = itr; else break; } return itr; } static const char* _xmlFindStartTag(const char* itr, const char* itrEnd) { return (const char*)memchr(itr, '<', itrEnd - itr); } static const char* _xmlFindEndTag(const char* itr, const char* itrEnd) { bool insideQuote[2] = {false, false}; // 0: ", 1: ' for (; itr < itrEnd; itr++) { if (*itr == '"' && !insideQuote[1]) insideQuote[0] = !insideQuote[0]; if (*itr == '\'' && !insideQuote[0]) insideQuote[1] = !insideQuote[1]; if (!insideQuote[0] && !insideQuote[1]) { if ((*itr == '>') || (*itr == '<')) return itr; } } return nullptr; } static const char* _xmlFindEndCommentTag(const char* itr, const char* itrEnd) { for (; itr < itrEnd; itr++) { if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2; } return nullptr; } static const char* _xmlFindEndCdataTag(const char* itr, const char* itrEnd) { for (; itr < itrEnd; itr++) { if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2; } return nullptr; } static const char* _xmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd) { for (; itr < itrEnd; itr++) { if (*itr == '>') return itr; } return nullptr; } static XMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff) { toff = 0; if (itr[1] == '/') { toff = 1; return XMLType::Close; } else if (itr[1] == '?') { toff = 1; return XMLType::Processing; } else if (itr[1] == '!') { if ((itr + sizeof("") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) { toff = sizeof("!DOCTYPE") - 1; return XMLType::Doctype; } else if ((itr + sizeof("") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) { toff = sizeof("![CDATA[") - 1; return XMLType::CData; } else if ((itr + sizeof("") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) { toff = sizeof("!--") - 1; return XMLType::Comment; } else if (itr + sizeof("") - 1 < itrEnd) { toff = sizeof("!") - 1; return XMLType::DoctypeChild; } return XMLType::Open; } return XMLType::Open; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ const char* xmlNodeTypeToString(TVG_UNUSED SvgNodeType type) { #ifdef THORVG_LOG_ENABLED static const char* TYPE_NAMES[] = { "Svg", "G", "Defs", "Animation", "Arc", "Circle", "Ellipse", "Image", "Line", "Path", "Polygon", "Polyline", "Rect", "Text", "TextArea", "Tspan", "Use", "Video", "ClipPath", "Mask", "Symbol", "Filter", "GaussianBlur", "Unknown", }; return TYPE_NAMES[(int) type]; #endif return nullptr; } bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName) { #ifdef THORVG_LOG_ENABLED const auto elementsNum = 1; const char* const elements[] = { "title" }; for (unsigned int i = 0; i < elementsNum; ++i) { if (!strncmp(tagName, elements[i], strlen(tagName))) { return true; } } return false; #else return true; #endif } bool xmlParseAttributes(const char* buf, unsigned bufLength, xmlAttributeCb func, const void* data) { const char *itr = buf, *itrEnd = buf + bufLength; char* tmpBuf = tvg::malloc(bufLength + 1); if (!buf || !func || !tmpBuf) goto error; while (itr < itrEnd) { const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd); const char *key, *keyEnd, *value, *valueEnd; char* tval; if (p == itrEnd) goto success; key = p; for (keyEnd = key; keyEnd < itrEnd; keyEnd++) { if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break; } if (keyEnd == itrEnd) goto error; if (keyEnd == key) { // There is no key. This case is invalid, but explores the following syntax. itr = keyEnd + 1; continue; } if (*keyEnd == '=') value = keyEnd + 1; else { value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd); if (!value) goto error; value++; } keyEnd = _xmlUnskipXmlEntities(keyEnd, key); value = _skipWhiteSpacesAndXmlEntities(value, itrEnd); if (value == itrEnd) goto error; if ((*value == '"') || (*value == '\'')) { valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value); if (!valueEnd) goto error; value++; } else { valueEnd = _xmlFindWhiteSpace(value, itrEnd); } itr = valueEnd + 1; value = _skipWhiteSpacesAndXmlEntities(value, itrEnd); valueEnd = _unskipWhiteSpacesAndXmlEntities(valueEnd, value); memcpy(tmpBuf, key, keyEnd - key); tmpBuf[keyEnd - key] = '\0'; tval = tmpBuf + (keyEnd - key) + 1; int i = 0; while (value < valueEnd) { value = _xmlSkipXmlEntities(value, valueEnd); tval[i++] = *value; value++; } tval[i] = '\0'; if (!func((void*)data, tmpBuf, tval)) { if (_unsupported(tmpBuf, tval)) { TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", xmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", tmpBuf, tval ? tval : "NONE"); } } } success: tvg::free(tmpBuf); return true; error: tvg::free(tmpBuf); return false; } bool xmlParse(const char* buf, unsigned bufLength, bool strip, xmlCb func, const void* data) { const char *itr = buf, *itrEnd = buf + bufLength; while (itr < itrEnd) { if (itr[0] == '<') { //Invalid case if (itr + 1 >= itrEnd) return false; size_t toff = 0; XMLType type = _getXMLType(itr, itrEnd, toff); const char* p; if (type == XMLType::CData) p = _xmlFindEndCdataTag(itr + 1 + toff, itrEnd); else if (type == XMLType::DoctypeChild) p = _xmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd); else if (type == XMLType::Comment) p = _xmlFindEndCommentTag(itr + 1 + toff, itrEnd); else p = _xmlFindEndTag(itr + 1 + toff, itrEnd); if (p) { //Invalid case: '<' nested if (*p == '<' && type != XMLType::Doctype) return false; const char *start, *end; start = itr + 1 + toff; end = p; switch (type) { case XMLType::Open: { if (p[-1] == '/') { type = XMLType::OpenEmpty; end--; } break; } case XMLType::CData: { if (!memcmp(p - 2, "]]", 2)) end -= 2; break; } case XMLType::Processing: { if (p[-1] == '?') end--; break; } case XMLType::Comment: { if (!memcmp(p - 2, "--", 2)) end -= 2; break; } default: { break; } } if (strip && (type != XMLType::CData)) { start = _skipWhiteSpacesAndXmlEntities(start, end); end = _unskipWhiteSpacesAndXmlEntities(end, start); } if (!func((void*)data, type, start, (unsigned int)(end - start))) return false; itr = p + 1; } else { return false; } } else { const char *p, *end; if (strip && ((SvgLoaderData*)data)->openedTag != OpenedTagType::Text) { p = itr; p = _skipWhiteSpacesAndXmlEntities(p, itrEnd); if (p) { if (!func((void*)data, XMLType::Ignored, itr, (unsigned int)(p - itr))) return false; itr = p; } } p = _xmlFindStartTag(itr, itrEnd); if (!p) p = itrEnd; end = p; if (strip && ((SvgLoaderData*)data)->openedTag != OpenedTagType::Text) end = _unskipWhiteSpacesAndXmlEntities(end, itr); if (itr != end && !func((void*)data, XMLType::Data, itr, (unsigned int)(end - itr))) return false; if (strip && (end < p) && !func((void*)data, XMLType::Ignored, end, (unsigned int)(p - end))) return false; itr = p; } } return true; } bool xmlParseW3CAttribute(const char* buf, unsigned bufLength, xmlAttributeCb func, const void* data) { if (!buf) return false; const auto end = buf + bufLength; if (buf == end) return true; auto kmem = tvg::malloc(end - buf + 1); auto vmem = tvg::malloc(end - buf + 1); auto key = kmem; auto val = vmem; do { auto sep = (char*)strchr(buf, ':'); auto next = (char*)strchr(buf, ';'); if (auto src = strstr(buf, "src")) {//src tag from css font-face contains extra semicolon if (src < sep) { if (next + 1 < end) next = (char*)strchr(next + 1, ';'); else break; } } if (sep >= end) next = sep = nullptr; if (next >= end) next = nullptr; key[0] = '\0'; val[0] = '\0'; if (sep != nullptr && next == nullptr) { memcpy(key, buf, sep - buf); key[sep - buf] = '\0'; memcpy(val, sep + 1, end - sep - 1); val[end - sep - 1] = '\0'; } else if (sep != nullptr && sep < next) { memcpy(key, buf, sep - buf); key[sep - buf] = '\0'; memcpy(val, sep + 1, next - sep - 1); val[next - sep - 1] = '\0'; } else if (next) { memcpy(key, buf, next - buf); key[next - buf] = '\0'; } if (key[0]) { key = const_cast(svgUtilSkipWhiteSpace(key, key + strlen(key))); key[svgUtilUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0'; val = const_cast(svgUtilSkipWhiteSpace(val, val + strlen(val))); val[svgUtilUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0'; if (!func((void*)data, key, val)) { if (!_unsupported(key, val)) { TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", xmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", key, val ? val : "NONE"); } } } if (!next) break; buf = next + 1; } while (true); tvg::free(kmem); tvg::free(vmem); return true; } /* * Supported formats: * tag {}, .name {}, tag.name{} */ const char* xmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength) { if (!buf) return nullptr; *tag = *name = nullptr; *attrsLength = 0; auto itr = svgUtilSkipWhiteSpace(buf, buf + bufLength); auto itrEnd = (const char*)memchr(buf, '{', bufLength); if (!itrEnd || itr == itrEnd) return nullptr; auto nextElement = (const char*)memchr(itrEnd, '}', bufLength - (itrEnd - buf)); if (!nextElement) return nullptr; *attrs = itrEnd + 1; *attrsLength = nextElement - *attrs; const char *p; itrEnd = svgUtilUnskipWhiteSpace(itrEnd, itr); if (*(itrEnd - 1) == '.') return nullptr; for (p = itr; p < itrEnd; p++) { if (*p == '.') break; } if (p == itr) *tag = duplicate("all"); else *tag = duplicate(itr, p - itr); if (p == itrEnd) *name = nullptr; else *name = duplicate(p + 1, itrEnd - p - 1); return (nextElement ? nextElement + 1 : nullptr); } const char* xmlFindAttributesTag(const char* buf, unsigned bufLength) { const char *itr = buf, *itrEnd = buf + bufLength; for (; itr < itrEnd; itr++) { if (!isspace((unsigned char)*itr)) { //User skip tagname and already gave it the attributes. if (*itr == '=') return buf; } else { itr = _xmlUnskipXmlEntities(itr, buf); if (itr == itrEnd) return nullptr; return itr; } } return nullptr; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/Arial.ttf000664 001750 001750 00001032164 15164251010 033764 0ustar00ddennedyddennedy000000 000000 pDSIG=GDEFQDQ,GSUB[JSTFm*iLTSHsE?E,OS/2 2kVPCLT{>C6VDMXPjJcmapgMpcvt pvm0fpgmw'gdgasp PglyfY τhdmx.oQ|(head9|6hheam$hmtxʢs@sDkern7a96`loca81maxp  nameg<` post;UӐ2prepq\d {_<'*=g:L >NC3:((?c/B 33f zMono@ Q3>@FN >^ >,&|   b. f \RA/2 Iabh E$ a 1i 3 Z.   '7   >  ^  >   , &|   b. f  \   G   Wgu  W  $*-       Typeface The Monotype Corporation plc. Data The Monotype Corporation plc/Type Solutions Inc. 1990-1992. All Rights ReservedContemporary sans serif design, Arial contains more humanist characteristics than many of its predecessors and as such is more in tune with the mood of the last decades of the twentieth century. The overall treatment of curves is softer and fuller than in most industrial style sans serif faces. Terminal strokes are cut on the diagonal which helps to give the face a less mechanical appearance. Arial is an extremely versatile family of typefaces which can be used with equal success for text setting in reports, presentations, magazines etc, and for display use in newspapers, advertising and promotions.Monotype:Arial Regular:Version 2.82 (Microsoft)ArialMTArial Trademark of The Monotype Corporation plc registered in the US Pat & TM Off. and elsewhere.NOTIFICATION OF LICENSE AGREEMENT This typeface is the property of Monotype Typography and its use by you is covered under the terms of a license agreement. You have obtained this typeface software either directly from Monotype or together with software distributed by one of Monotype's licensees. This software is a valuable asset of Monotype. Unless you have entered into a specific license agreement granting you additional rights, your use of this software is limited to your workstation for your own publishing use. You may not copy or distribute this software. If you have any question concerning your rights you should review the license agreement you received with the software or contact Monotype for a copy of the license agreement. Monotype can be contacted at: USA - (847) 718-0400 UK - 01144 01737 765959 http://www.monotype.comMonotype Type Drawing Office - Robin Nicholas, Patricia Saunders 1982http://www.monotype.com/html/mtname/ms_arial.htmlhttp://www.monotype.com/html/mtname/ms_welcome.htmlhttp://www.monotype.com/html/type/license.htmlTypeface The Monotype Corporation plc. Data The Monotype Corporation plc/Type Solutions Inc. 1990-1992. All Rights ReservedContemporary sans serif design, Arial contains more humanist characteristics than many of its predecessors and as such is more in tune with the mood of the last decades of the twentieth century. The overall treatment of curves is softer and fuller than in most industrial style sans serif faces. Terminal strokes are cut on the diagonal which helps to give the face a less mechanical appearance. Arial is an extremely versatile family of typefaces which can be used with equal success for text setting in reports, presentations, magazines etc, and for display use in newspapers, advertising and promotions.Monotype:Arial Regular:Version 2.82 (Microsoft)ArialMTArial Trademark of The Monotype Corporation plc registered in the US Pat & TM Off. and elsewhere.NOTIFICATION OF LICENSE AGREEMENT This typeface is the property of Monotype Typography and its use by you is covered under the terms of a license agreement. You have obtained this typeface software either directly from Monotype or together with software distributed by one of Monotype's licensees. This software is a valuable asset of Monotype. Unless you have entered into a specific license agreement granting you additional rights, your use of this software is limited to your workstation for your own publishing use. You may not copy or distribute this software. If you have any question concerning your rights you should review the license agreement you received with the software or contact Monotype for a copy of the license agreement. Monotype can be contacted at: USA - (847) 718-0400 UK - 01144 01737 765959 http://www.monotype.comMonotype Type Drawing Office - Robin Nicholas, Patricia Saunders 1982http://www.monotype.com/html/mtname/ms_arial.htmlhttp://www.monotype.com/html/mtname/ms_welcome.htmlhttp://www.monotype.com/html/type/license.htmlNormalnyoby ejnnormalStandardNormaaliNormlneNormaleStandaard1KG=K9NavadnothngArrunta R X 688~Y #~ O\_ :Rkmq~    " & . 0 3 : < > D o  !!!!"!&!.!T!^!!"""""""")"+"H"a"e###!%%% %%%%%$%,%4%<%l%%%%%%%%%%%%%%%&<&@&B&`&c&f&k:1 6<>ADOY}b? Y #~Q^ !@`mq~   & * 0 2 9 < > D j  !!!!"!&!.!S![!!"""""""")"+"H"`"d### %%% %%%%%$%,%4%<%P%%%%%%%%%%%%%%%&:&@&B&`&c&e&j: *8>@CFVz^>/wkri+*)(|zvlhL>i3][w}ujy,ߨߖޖޢދަq_0@3$FE<9630)"ۿ۾۷ۥۯEBA$"!>c6dNNR`b^ffPPRN&($ L^bcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,  -   ./0 !"12#3$%&'()*+   4NORPQUVWXST~?Avw|qrsYZ[\]uwvyx}56  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 688~Y #~ O\_ :Rkmq~    " & . 0 3 : < > D o  !!!!"!&!.!T!^!!"""""""")"+"H"a"e###!%%% %%%%%$%,%4%<%l%%%%%%%%%%%%%%%&<&@&B&`&c&f&k:1 6<>ADOY}b? Y #~Q^ !@`mq~   & * 0 2 9 < > D j  !!!!"!&!.!S![!!"""""""")"+"H"`"d### %%% %%%%%$%,%4%<%P%%%%%%%%%%%%%%%&:&@&B&`&c&e&j: *8>@CFVz^>/wkri+*)(|zvlhL>i3][w}ujy,ߨߖޖޢދަq_0@3$FE<9630)"ۿ۾۷ۥۯEBA$"!>c6dNNR`b^ffPPRN&($ L^bcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,  -   ./0 !"12#3$%&'()*+   4NORPQUVWXST~?Avw|qrsYZ[\]uwvyx}56llll<j T  x X JfP ZxXN@@ !"#6#%%&B&((*+,-B.02.23^46x9&:;< ?@B"CVDEG GHI~KL$MRNfO~PQRTUVX`[^]_`abcccd"d^dde(edeef fZffggTggh hPhhhi,ibiijjXjjjk0klFn8oq|qr\tDvxytyz`{~|j~VF`>bX&4 \NNxRj<ZffFx8.jH6fX&ZNF8l~t@6Þ8rĶ8hŘ>xǨ 8jɨʴ"f˖ ̶lͰ.lΠ Bπϼ4pафҜ"VԐF|վdtܮݨ<d `<(6"BbJr&P|>h*nHz bHdB ((hpvxp4>.^"RBrT   0 `      DtD.@xB^,\&V4d4d* !#0%`')<)***:*,r-P.../133344555&565F66(6866707@8*8:8J8Z9f::; ;@;p;;< =R>(? @@A@BZC2CBCRDDEFGHIKKFK~KKL L0MMLNpNNNNOPQQR*RRSSTTTWXlXY"Z@ZZZ[[[[[\]]^B^_`Vaabbccde&fRfgXghizjkkm mooopRpbprpprrrs stpu^vvwwy zzT{{|||}}~~H:x2 NvBv>~h(^ <`pB p2~HL4d(.f(2 \<H*hz$bzB jt< Hæ(ƆưǒǼBl˖@j͔;<fΐBм(R|ҦP\׊ך&l8>޾^0Z,V(R| &PPV @Bz(R|$Nx JtFpBl^t~ J   B Vn~>NdJZn~H  !"l"#$2%&z&'"'2'((()*++-<-L//113:3J4:4J5577 8`8p88:::(:8;<=>@ ABCDDEFHXHhIJLLLLMNNNOOPPRRRRRSU>VjWdWtWWY2Z[[^0`zcehgjbl2mnn@nPn`nnnnno(oRoboroooppp p0p@pPpzppppqqBqlqqqrr>rhrrrss:sdssst t6t`tttuu2u\uuuvv.vXvvvww*wTw~wwwx&xPxzxxxy"yLyvzzZzz{${N{x{{{| |J|t|||}}F}p}}}~~B~l~~~(\xzJ.t(Fn(^`Tjbr**<(:L^p06 `.xHRdrf,`B PZ&L~f"v&N4f@n(^0`>j8p2bP<pj8X ^rz´dpƴhR(ʪ*˼̶͠Βτ(% >u&5#'eS79]9q7$5S+7Ƥc  ) R M_c  S>+. . -%&%&.. ' '9%.7.7$..+.7.7cc0 -&&22222+11%&. . ' ' 'SS.7.7>>>>9]9.7$$.+.7.7.7.79%& ) %% .>u..M'> &)))$1$$$)(*)$)* % >>17 1%$%  u..11M+%11.&1$&'( 77 77$75 11,1$*1%'7&1+9$$7 71    ,    , , ,, # #+ ,,,,,,,,,))))++++22222222##22,,,,#### +9%&%&%&%&%&%&%&%&%&%&%&%& ' ' ' ' ' ' ' '>>9$$$$$$$77ĖĖ%&>9$77777%&.    !" # $!%"&"'#($)&*&+',(-(.*/+0-1-2.3/405061738494:5;5<6=7>8?9@:A;B<C<D=E>F?G@HAIBJBKCLDMFNFOGPHQIRISJTKUMVMWNXOYPZP[Q\S]T^T_U`VaWbWcYdZe[f\g\h]i^j`k`lambncocpdqergsgthuivjwjxkymzn{n|o}p~qqstuvvwxyz{|}}~   !" # $!%"&"'#($)&*&+',(-(.*/+0-1-2.3/405061738494:5;5<6=7>8?9@:A;B<C<D=E>F?G@HAIBJBKCLDMFNFOGPHQIRISJTKUMVMWNXOYPZP[Q\S]T^T_U`VaWbWcYdZe[f\g\h]i^j`k`lambncocpdqergsgthuivjwjxkymzn{n|o}p~qrstuvvwxyz{|}}~   !" # $!%"&"'#($)&*&+',(-(.*/+0-1-2.3/405061738494:5;5<6=7>8?9@:A;B<C<D=E>F?G@HAIBJBKCLDMFNFOGPHQIRISJTKUMVMWNXOYPZP[Q\S]T^T_U`VaWbWcYdZe[f\g\h]i^j`k`lambncocpdqergsgthuivjwjxkymzn{n|o}p~qrstuvvwxyz{|}}~A#/O__o@3@3@jl2@a3@\]2@WY2@MQ2@DI2@:3@142@.B2@',2@%2 2Ap@$&2d 2A d2AJ?/?_?Ӳ792Ӳ+/2Ӳ%2Ӳ2Ӳ2Ҳ) +A0 P`p`p   0@Pв +ϲ&BAƲA /$A/?O_"dA @j@&CI2@ CI2@&:=2@ :=2 &@&2@ 2@&2@ 2@&2@ 2@&z2@ z2@&lv2@ lv2@&dj2@ dj2@&Z_2@ Z_2@&OT2@ OT2$'7Ok Aw0w@wPwwww**@+)*Re~<^+@8@@89@s&%$ 7@!I3@!E3@!AB2@!=>2A!?!!!!!@! "2@!2@"*?2@!.:2oAH/`?_"""/"?"_"""!!o!!!/!?!O!""!!@+HO7 A &A9&%8s542V&, /& 8ʸ&~&}Gke&^s@R&ZHDb@s?^<&50+*V)#U7h@,XO62,!  @+JKKSBKcKb S# QZ#BKKTB8+KR7+KP[XY8+TXCX(YYv??>9FD>9FD>9FD>9FD>9F`D>9F`D+++++++++++++++++++++++KSXY2KSXYKS \XEDEDYX>ERX>DYYKVS \X ED&EDYX ERX DYYKS \X%ED$EDYX %ERX% DYYKS \Xs$ED$$EDYX sERXs DYYKS \X%ED%%EDYXERXDYYK>S \XEDEDYXERXDYYKVS \XED/EDYXERXDYYKS \XEDEDYX ERX DYY+++++++++++++++++++++++++++++++++++++++++eB++;Yc\Ee#E`#Ee`#E`vhb cYEe#E &`bch &aeY#eDc#D ;\Ee#E &`bch &ae\#eD;#D\ETX\@eD;@;E#aDYGP47Ee#E`#Ee`#E`vhb 4PEe#E &`bch &aeP#eD4#D G7Ee#E &`bch &ae7#eDG#D7ETX7@eDG@GE#aDYKSBKPXBYC\XBY CX`!YBp>CX;!~ +Y #B #BCX-A-A +Y#B#BCX~;! +Y#B#B+tusuEiDEiDEiDsssstustu++++tu+++++sssssssssssssssssssssssss+++E@aDstK*SK?QZXE@`DYK:SK?QZX E`DYK.SK:QZXE@`DYK.SK=<;:987543210/.-,+*)('&%$#"!  ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,#p>#pE: -,E#DE#D-, E%EadPQXED!!Y-,Cc#b#B+-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+#Dz-,E+#D-,CXE+#DzEi #D QX+#Dz!zYY-,-,%F`F@aH-,KS \XYXY-, %E#DE#DEe#E %`j #B#hj`a Ry!@E TX#!?#YaDRy@ E TX#!?#YaD-,C#C -,C#C -, C#C -, C#Ce -,C#Ce -,C#Ce -,KRXED!!Y-, %#I@` c RX#%8#%e8c8!!!!!Y-,KdQXEi C`:!!!Y-,%# `#-,%# a#-,%-, ` <<-, a <<-,++**-,CC -,>**-,5-,v##p #E PXaY:/-,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,&&&&Eh:-,&&&&Ehe:-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,KTX8!!Y-,CXY-,CXY-,KTC\ZX8!!Y-,C\X %% d#dadQX%% F`H F`HY !!!!Y-,C\X %% d#dadQX%% F`H F`HY !!!!Y-,KS#KQZX:+!!Y-,KS#KQZX;+!!Y-,KS#KQZC\ZX8!!Y-, KT&KTZ C\ZX8!!Y-,KRX%%I%%Ia TX! CUX%%88Y@TX CTX%8Y CTX%%88%8YYYY!!!!-,F#F`F# F`ab# #ŠpE` PXaFY`h:-&iiiD|ZRRD/W~ "APoLu\7LnpX cc-\ @Wr]g!wM+Le|C]h5G!\M-x ,I?)9Io#o 2@z1UW~~FB/OV)or,11di+ &  sC_a^m8Q[h|ATkhqBBSsX2Q|  !U{{~!""#rw"+5<Yoq22 *<Qaajx *>LQ_jqx !".5BOO^eq *G]ety "&+G_u\ m6>PQ]`E3-_dM?}$x;;N&;MKSj1<ex ~ 90+ P >X!q}E +NT2N7kwdg3|)n*i9$]u MRhm}qyXgV%|2!r\/AMrLjUxiWnTgeRZgn-|{pLFF-S%F>S?("bJmH3NFpyQ hlOa+999^ssIwVXZ||@r9A99sUss<sVssUsMsasSsU99prpsZoVVfV9m97Vs9cV9XV\0V V V)999'6sYsJsPsFsK9sBsssDssH?9$s!(9/WVVhV9csJsJsJsJsJsJPsKsKsKsK99#99 ssDsDsDsDsDsssssI3skssQmL=dN9SdNdMdMs8zd1/-%DrdTs.d3ssVV9cRsSGldN/!VV9s\\sI9lG%VVVVV99999c9c9c9.k:(sV\?)(sIV!Vsrk!kk!s9msB9V\?fPfPsFkVsJVsJGVsKVsKsBsUsss9csD<V\?09$0#ss)()(h9`bUHtHbD).0HkR3OOOjrq"~~~~~~f0 0 *+kUo@:@7?@%UapVsJfPfPVsKVsKVsK9msB9msB9msBss9999999f7Vsss9csD9csDkV\?09 ssssV!VsJD9S9V!sZk""JkWF2xVVXV)9VX 3m9cV0VV a9VHbsk`Hss\\sDV`3z?W`sH`?WW2UdV\997u 1 V@VUkVcN@9cVf0 RV UWUU@UsJ[@sKZ2xxksDUsP&!K+Ek(+0UsKsK?9 @s!kJA-11~~!}3 9iV21-_(P<P<Z<n_s f(d$(ZVZ((WWH-ddddiiiV21-KJK(P<P<Zn_s f(d$(V2P_s<%QCMyFFFFHFFQF5|5.555,5555555B6555F656==ZvZ'ZvZ'2j0FF@FQ sJ]My"VVuxux-Y2jYB=656==656==656==????Z*65'Z*65'OmOm$$ss''  ii''55B6='uxux2j2jZ@@Z&0ZSAZSAFFQFFHFFFFF@0FFc?D[/Ts) grksSrj}_vlX,VsJVsJVsJVsJVsJVsJVsJVsJVsJVsJVsJVsJVsKVsKVsKVsKVsKVsKVsKVsK9c9|9csD9csD9csD9csD9csD9csD9csDc?Dc?Dc?Dc?Dc?Dss[[[[[V!V!V!VsJ99csDsssssU cZkssV UW+EU+csU9`sD,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        "                                                                                                                                                                 "                  &                                                                                                                                                                  &                          *                                                                                                                                                                    *                                   0                                                                                                                                               0                                          6                                                                                           6   :                                                                                          :       @                                        #    !"                  "      "                    ####          @    !B    " "          ! !  !!!                 $!!! !!!!!"#       "! """      #!!   !#              !   $$$$         !B    %J !   & &         % % ! %%#%    %           (%%%$%%%%%&'" !       &%! &&&      '% ""#!% "%(                ##%      ))))       %J               "*T %   +! #!!*    $  ! **!#  %*!*(*  + !!!    ###!   ! !".***)*****+,'  %!!!   !! * *%! *** ####!#!#  #!#   ! !$ ,*$'#! ''!%*#"#  &"*-      %              "'"' *       ####..$$..$$!!         *T$$##  !!!!!!!$$$$$$$$$$ !' !.\# )    /!!$! %!$$!!.   & ! !!$! ""..$!!&#).$.+.  . $$$!!!    !&&&$ !!!!  !!$!! !!$%2...-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"...../0*###" )!!$$$!!   !!$$! !!!!.! .)$ .!.!.! &&&&$'$&# ! %!$!&"   !$ $(! 1.'!*!!%!$!!#"*+$)!.! &%&!# *%.1        %       ## ###### ###########!! ####%+%+################ ##.#############################   !! !! !! &&&&33''33''$$     ## ### ## ### # ##############################################################################.\''&&  $$$$$$$'''''!!''''' $!!!!!*!#$2d&,! 3!!$$!'$!)$'!'$!$!2!!  (# !!$!$'$%%22'$$)&,2!!'2/2 ! 2!!!!!'''$$$ ! $!!***'!$$!!$$!! $$'$$! $$'( 62221$##############################################$2111135.&&!&% ,!$$!!!'''$$  ! $$''$!$$$$2#! !2,' 2#2#2#! ****!'*'*& !!!!$!!)$!'$!!!*%!  $' '!+$!53+ $!!!"!.$$!)$'$!$ &!%!./(,!$3$!")()$&  -)26#    #  * #####  &&&&&&&& &&&&&&&& &&&$$ &&&&)/)/ &&&&&&&&&&&&&&&& &&2&&&&&&&&&&&&&&&&&&&&&&&&&&&&&   $$ $$ $$ ))))77**77**''       && &&& && &&& & &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&2d+!+!** !!!!!!!!!!!!!!!!!!!!  '''''''+!+!+!+!+!$$+!+!+!+!+!!!!! '$$$$$.!$!!!&'6l)0$   7$$''$!*'$-'*$*'$!'$6##!  -' $$'$'*'!((66*'',)0!! "6$$*636 #  5$$$$$***''' $!'#$ ---*$''$$'!'$$ ''*''$!!''!!*+!#;6665' ! !!&"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'!65555!!!791))# )(! 0!!$''$$$***''  $ ''**'$!''''6'# $60*!6'6'6'#  ----$*-*,) $$$$!'$$-'#*'$!!##-(#  '* *$/'$97-"'$#$%$2!''#-'*'$'!")#($13+0#'7' $%,+,"')  1,6:% !   &# * &&&&!  &#  )))))))) ))))))))")))'' )))),2,2" )))))))))))))))) ))6)))))))))))))))))))))))))))))!  " '' '' '' ,,,,;;..;;..++   " "     )) ))) )) ))) ) ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))6l.#.$-- $$$$$$$$$$$$$$$$$$$$  *******.#.#.#.#.#''.$.$.$.$.$###$ *'''''2$'$$$)*:t, 4' " """ ;''**'#-*' /*-'-*'$*':%&#     1  )"''*'*-*    #++: :-) !)0 -4##" $ :''-:7 : &   9'''''---*** '#* &' ""000 - '** ' ' *$*' '  * * - **'$$* * ## -." $& ?:::9*"#"##)$)))))))))))))))))))))))))))))))))))))))))))))*#:9999###;=5,,&",+# 3##' **' ' ' - - - * *   ' * * - - *'#* * * * :)& ' :4-#:):):)&  #0000'-1-0, ''''#*''/*&-*'$$&%0+&" ! ! ! )- -'2*'=;2"%*'&'''6#**"&/*-*'*$%,%+'56.3&*;* !" ' "(  0!/0$*,   5/  :>(!##"##!!  (%.((((!!!#"#!!  (% !  ,,,,,,,, ,,,,,,,,%,,,)),,,,!/6/6%",,,,,,,,,,,,,,,, ,, :,,,,,,,,,,,,,,,,,,,,,,,,,,,,,$  %" ))))))!!!0000@@11@@11""""""""  ..""## %"%" # # # #,, ,,, ,, ,,, , ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,:t2&2'00 ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '   - - - - - - - 2&2&2&2&2&* * 2'2'2'2'2'&&&'  - * * * * * 6'""*  ''', - C2%%<- '%%%%%%%%%%'''%D--00-)40"-%704-40-)0-B+-)%%%"%%%%"9%%%%"%!/ !!'--0-040%%%%%%"%%%%%%%%%%%%%%%%%%$)11C%C40%%%%'!07%3<))'%%%)%%C--4C?%C%!!- %""%C-----444000%-")!0%-!-%''888%4%-"0"0"%%-%-%0)0-%-%%%%0%0%4%00-"))0%0%)!)!%45'%)+%"IC"C"C""B0'('((/*/////////////////////////////////////////////1(CBBBB(((DG=22$,(""21( ;((-%0"0"-%-%-%4%4%4%0%0%"-""%0%0%4%4%0-")0%0%0%0%B/-!-%C<4)B/B/B/-!%(8888-58482----)0--70,40-))-+82-'%%'"%%""'!%& %#04%%%4-:$0-"GD9'+0-,-$-->(00',7040-0)+3+2-=?5;,0D0%&$'%-%%'.%%$%"!7 C*0#"2$%%""=6%!%!CH.&$"((("((&#&% %"/+"""7""$////&&&$"(("(&&% %"/+$%'    ############222###2222222222222+222002222&#$$!!6>"6>"+'222222222222222222%C22222222222222222222222222222)+'000000&#$$&#$$&#$$!!!!77$$77$$JJ99JJ99''''''''$#$#55''((""##+'+'$($($($(22 222 222222 222222222222222222222222222222222222222222222222222222222222222222222222222222C9,9-"88-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%4%4%4%4%4%4%4%9,9,9,9,9,0%0%9-9-9-9-9--!-!-!-%4%0%0%0%0%0%$>-''0%%"%"-"-#-#2%4%K8**C2,**********,,,*L22662.:6&2*=6:2:62-62K11."***&****&?****&*%5$%%,22626:6******&******************(.77K)K:5)))*+%5>):C..,)*).**K22:KG*K)%%1 *&&*L22222:::666*2&.%6*1%2*,,???*:*2&6&6&*)2*2*6.62*2****6*6*:*662&--6*6*.%.%):<+*!.1)&RK&K&K&&I6,-,--5/5555555555555555555555555555555555555555555557-KJJJJ---MOE88(1-&&87!-B--2*6&6&2*2*2*:*:*:*6*6*&2&&*6*6*:*:*62&.6*6*6*6*K51%2*KC:.K5K5K51%*-????2;?:>82222.622=61:62.-11?81+!*)+&!**&&+%"*+$)'5;)*);2A)62&OL@,06212)32E-66,1=6:626-09172EF;B16L6*+(,*2"**!,4)*)*&"%>$+'<>/6'&8)**&&D=*!%)%KP4*)&--,&##--+(*)#$)&40%%%<&&##(4444***)&-,&##-+*)$)&40)#)+ ''''''''''''888'''88888888888880888668888*'((%%=F'=F'0,888888888888888888*K88888888888888888888888888888. 0,666666*'((*'((*'((%%%%>>((>>((RR??RR??,,,,,,,,)"')"';;,,--&&''" 0,0,)-)-)-)-88888 888888888888888888888888888888888888888888888888888888888888888888888888888888888888 K@1@2&??2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*:*:*:*:*:*:*:*@1@1@1@1@16*6*@2@2@2@2@21%1%1%2*:*6*6*6*6*6*)E2,!,!6)*&*&2&2'2'8*:*S>..J7 0..........000.T77<<73A<*7.E>,61**>=%2I227.<*<*7.7.7.A.A.A.<.<.*7+*.<.<.A.A.<7*3<.<.<.<.S;6'7.SJA3S;S;S;6'.2EEEE7AF @D>77773<77E<6A<73468E>60%.-0*%..**0)%./(-,;A-.-A7H-<7*XTG05<767-87M2<<06E-..**KC.$'.)"SY9 /-!*2 21*&&220,/-&(.*:5)))#C**&&,::::///-!*2"1*&&20/-(.*:5-&-0",,,,,,,,,,,,>>>,,,>>>>>>>>>>>>>5>>>;;>>>>/,,,))CM!+CM!+51>>>>>>>>>>>>>>>>>>.S>>>>>>>>>>>>>>>>>>>>>>>>>>>>>3"$$51;;;;;;/,,,/,,,/,,,))))DD,,DD,,[[FF[[FF00000000-%,!-%,!AA0022!!**!!,,%!$$5151-2-2-2-2>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SG6G8+EE7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.A.A.A.A.A.A.A.G6G6G6G6G6<.<.G8G8G8G8G86'6'6'7.A.<.<.<.<.<.-M80$0$<..*.*7*7+7+>.A.\E!33R=$633333333336663]==BB=8HB.=3KBH=HB=9B=[>=8)333.3343/M3334.3-A---6==B=BHB333333.333333333333333%333 18DD\3\HB33335-BL3""GR886233933\==H\W3\3--=3..3]=====HHHBBB3=.8-B3=-=366MMM3H4=.B.B.33=3=3B9B=3=3333B3B3H3BB=.99#B3B38-8-3HI53)9$<3."d\.\.\..ZB68688A:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8\[[[[888^aTEE1<7/.ED)8!Q8!!8!=3B.B.=3=3=3H4H4H3B3B3.=/.3B3C3H3H3B=.8B3B3B3B3[A=-=3\RH8[A[A[A=-37MMMM=HM#GME====8B==KBME=5)325.)33..5-)34,20BH232H=P2B=.a]O6:B=<=2>=U8BB6D=TVIQ*33(6?3323.*-L-50JL:B0/E233"/.SK3(-3-&\c?#42%/7#76/*+77 5142*,3/@;...&L//++1@@@@4442%/7&6/*+7 542,3/@;2*25!&000000000000EEE000EEEEEEEEEEEEE;EEEBBEEEE4011--KV$/KV$/;6EEEEEEEEEEEEEEEEEE3\EEEEEEEEEEEEEEEEEEEEEEEEEEEEE9&((;6BB#BBBB401140114011----LL11LL11eeNNeeNN666666662)0$2)0$II6677$$//$$00#)$((;6;627272727EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE \O/MM=3=3=3=3=3=3=3=3=3=3=3=3=3=3=3=3=3=3=3=3H3H3H3H3H3H3H3OO>O>O>O>=-=-=-=3H3B3B3B3B3B32"U>6(6(B33.3.=.=0=0E3H3dK$88YC!!':!8888888888:::8fCCHHC=NH2C8SHNCNHC>HCcBB=+8!88288782T8887!281G111!!:CCHCHNH8888882888888888888888(888#6=JJd!!7dNG7778:1GR7%%MY==:787=88dCCNd^8d!!711B8!!228!dCCCCCNNNHHH!!!!!!!!!8C2=1H8B1C8::!!!SSS8N7C2H2H287!C8C8H=HC8C8888!H8H8N8H!H!C2>>&H8H8=1=17NP:8->(A72$md2d2d22bH:<:<>BBTKB:-87:2,8822:1-89074GN787NCV6HC2jeU:@HCBC6DC\@LBJC\^OYBHeH895$:8C.88,:E78682.1R194PR?H43K688$32[Q8,171)dkE!!!!!!!!!!!!!&!!!86(3<&<;3..<<#9597.073E@111*O33..6EEEE8886(3<);3..<#997073E@6.7: $)55555555555 5KKK555KKKKKKKKKKKKK@KKKGGKKKK855511Q]'3Q]'3@;KKKKKKKKKKKKKKKKKK8dKKKKKKKKKKKKKKKKKKKKKKKKKKKK K>)++@;GG&GGGG855585558555""""1111RR55RR55nnUUnnUU::::::::6-5'6-5'OO::<<''33""''55&-'++@;@;6<6<6<6>  %@9Ou!  %9  h+]+]+<<<<<<?<<r 88)^  1^ j7q+]]]]]]??.+}<<<10]]]]]]]]]]]]]]]]]]]]]]]]]]%#"'&5467&&5463267&6654&#"326YzkcBϝ-0ReymuE_GIa##MfQcc|SrBøєXt(|[FEh?K_^D"K*5WIYeZ'&@ jq+]?991053.0|Q`=@ '33@^ +]?|G_<@6;;I Y Tk dzz  @  O @ s@!#4 8@ ?_o$+]+<?<c`Ҋ~K|]Ya p@    @0 s@!#4O_os O__?_o+N]q54'#"5432#"&4&#"326p|aS}P66mƏ{z˥tx|}SznLp Vk4Ĝ&8@  << 54&#"'6632531$8wsZX6i$jM:;+b:i`tNJ`loQGW@W! !!%5 3EID!F$FIGVTz)%)& *&)569C%VY[!V)VIYVee%f)vzr$!&PS@ '0 P  HCKC: : +p++M'/H$@P>$$0p?8*$+ #@ 55XW+N]M]]/]???]99910]]%#"&&5463273327654$#"3 $73#"$$'&547!2#"&'&32>54&#"AQYirW9")5Vr}bX3CTdz@ra̶EU T8|qHa@qj@K[h؁?[]a'=P "g~ir啽 ɭ*'LCfAY@c/0gh ` YVPh               p@  @  @ eRP@ P    @+]q]q]q?<?<<.+}ć.+}9999ć10K SKQZX8888Yrq]#3#!!&'3XݫF"3FDZw*@PF#V#f#s iup s  ' '* **))&T%& ,  ]+;\+]<54&&#!&sfgW=8JKFm^&CZ:TYe^3'g`1RfMIo) 8kFRy1fv@`cj 2 cpt  *(* G VWVhk{޲(9@'(9  &J& & c\+N]MN]M??910++]]q]r#"$54$32&&#"326=כC,;3“\m憣1nU-銼Z@& C&    ];\+]<<]?<?rs^sg0pMQOa7" h @'4    ]    @4]  P ` p ;Y+]q+]<<]<<?(7@ * *) **9 67:*I*] ]*j i*`0 ) ((((D+,*499,IH,VY+fi+v +74/$42!_)o))/?@E' 2 )aa%!$[@'@&& &0&&&19@#409999%"/$?18]q]q+]<???9/]qq999910]q]q%#"&546676767654'&#"'>32#&326765@G CCSS``    jiju p  "_o@I0@P`p    $"$ +I$?14+]q]rKS#KQZX8Yrr??9/9/]]10]q]q#"4632&&#"326<r鉭Zj kl‚F@  % 4 D 55WT RSgde c`+<<Kp.$ .:5 KE FIW V g     3%%@`t$?O4P+N]qM]q<<??@dU]] U eko e '1:1AMAQ\Ramaxx P`p @  @ @ '*4  %&4@#403$@$*4?O47+N]+MNq++q+M??9/]*@` , %L E , &,#9 6J FVX h .#,'>#>'L',,6!6)?,F F!E)T!T)ic!c)`,,'!#'(@ 0`p}@A E" 3%3 %@`t%"$?O+,t!4P++N]qM]q<<??]??<10]q]q32676'#"5463253#"&32654&#"f 2Ct}vnэze۠Ꙧ}|zxXQ%2dZ7<ݘjx*@ 4%5E @$4  % @364   N@464p%@364 NGP+]q+<<]q+]q+?#ƹ @A 4%    #  # # %%%%%]%]@3#%?O$%x!GP++N]qrpXdL:&N_bX'l_:xxP>@@1 4y   $ @364   N@464p3%@364 N]q+<<<]q+]q+<<? @]  GHVYgi4::5EKKE\\ R]]Rmm dmmdw  [TT [ lee l  $@$%40 1@#40$@$%4?147+]+q+]]]+??10q]qC\X@ SS bb ]Y7632#"32654&#"D{'v i!>@ -=K?  ) #22Bp ::JJY[ \\jk imk  #++5:FJZ   $  @ `  t33%?OG7+N]qr@ +*;Ky ??K44?DDSScc` )" +95 IF Zi    3%@`t$?Ot!4P++N]qM]q<<????9910]]qq#"466325332654&#",*Uo~q!xsvui;N.C>@;/#4CSft      (" "%@364 NG+]q+<]r???999999ɇ}10]r]336632&#">i?[^>BB;^&qH:'G?`r?>0@{"": J D$V"e"| $, 0K,U2 \\ \ \ \ \ jj j j j j &''&$'$)6$Z Y d&d(t#t$$ (,0 '(&&@9Z'%  & .@?O_oU@@4@4.\l. $@4[$*9**2@&'*4`22?222$ $"?O147+N]qMrN]q+q+M+r??q9/++]qr]qr99910Cy@@'-#,&"  (- !#"# ) (' +++<<+<<+++++*++]q]rq]732654'&'.54676632&&#"#"&?{|x5%ƙOA8*S}Z si|j/Vi}=kreD=#%2INGy(+H{gR\R7# $3A|\ZW$* #&4 @A#&4  +  "" % E E`p j6f+]q<<<??<<993310]++%#"&&5#5373#32LeclM,&@ 4 4@)4+$    3%@364N@464p % @364   NGP+]q+]q+]q+<???<99910Cy@    ++**]+++!5#"&&'&53326653?|^O nQQ;HmO5s1GQS9& ^5"9 @ 9 4444 @ !4(!4 @ "%4"%4 @(.4 (.4 )( & 95 HG VVYX ffii xwwyx w   ,   (& 7 O@ @4@ 4 %  %  /  "@@@  @@ @" +]]]9999?<<!4@J!4)( /99 IFFI O\TTZ Plccj {t{  &)+ 94,9 @'9:  % a+ a + [@," @`t ~Oot!|++N]q!4++++qY]C\X޲9 ޲9 9 = 9 @ 999++++++++Y35#!5!63!(sXOdoyjw^{ 9Q|*{@MG(44 4% 4')** %  %   %:%&:*':&**_i+ph+]<<<<<??9/9910++++q>7>7633#"33#"'.&&'9Ma  1H8&V8hD W]ncA_8b,@T aMdON5Tf= KEkt-.%D6ghPQY2~@v +N]!@@4 dH+!)d+++55Y&$?@ h+)++q55f[v&&"@0 0O0/0000H+)++]q5,&(Tj(@/H+!)++]]q5&1Q7@oO_zH+!)d++]]qqq5c&2@  dH+#!)++]55"&8"@_@4nH+!)+++q55J&D@/;?;;H+;")++q5J&DC@999 H+9")++]5J&D6@&: :0:p::::::::@.24:=A>")+++]qr5J&D @p<<<<bH+?")++]55J&D*@I@4I@ 4IIIдH+G")++]++5J&D@AAAh+A")++q55Po>&F-@/0 H+)++]]qr5K&H@!!! H+!")++]5K&HC&@ 4p H+")++]q+5K&H'@ @;5 @-24  # A$")++r++5K&H@"" dH+%")++]55.&+4@"%4/ZH+")++]++5#&C(@@4@"%4 H+")++]++5h&@  A ")++5 :&@ H+ ")++55&Q$("$4O((H+&")++]+5D'&R@H+")++]5D'&RC&@ 4pH+")++]q+5D'&R @@.24A ")++r+5D'&R@ nH+!")++55D'&R0@/+?+++O++/+?++H+)")++]]]q5&X!@@4O p+]<<<<<<<<?<<<:+N]M<]]{?{@{AsIsJ->?@AHIJ)) )$)"1HC EB?9% ":/'<HCB?:9/'% 6'@-' <>+^2><)O++L"^><8E)OKq+N]MN]M??999910]]732654'&%.5467&&54632&&#"#"&6654'&'&'zifs$>uJxiG:ȥiY\q$87GCI*pPOdm3JI45CQE..FihF3+K[gL`DsAz`c<4,D`-54&#"YЂ$\d-@͠~/2d7Ll ['(g m[kpr3l?  AY6MiƇjH]HhF8(>r99<'PX">_!6@@6,-&')-)0+1 +++ e01/d/t//г&-4/b@.,..,-,+*).012601(3-,+*/))5b77 !@?b@!""".//6O  o   b b;b&T/b.@756!6b J bBb Az+NMNM<<<<??]q<<<]<<9/9/999.++q}910Cy@J<>%$%=&%&%&& %&%>#;,!! !!<%?, !! !!++++++++++++++++++++]q]2#"$54$"32$54$!2#'&'&##32654&&##jjӪ,,Li+1GcHU4$EMrS(G`hk}Ѥէ+/,-p?Y0q&D8$9:3@  036p !$/0..`+8b@O$$o$$$$2b@ ++++b b/b. b!5b'd b<b ;z+NMNM??]]q]10Cy@T37%*)&%&%&& %&%3*57%5!! !!4(26&8 !! !!+++++++++++++++++++++]2#"$54$"32$54$#"&546632&&#"326jjӪ,,T{Ëdw wuOspZhk}Ѥէ+/$}ʄcmJOh@] 95JF  i b@  b;;b b@ 0`ٹ.+]<<<<?<<<<<<999910]]!5!!!33##e|{yyJ/uTJOeȳ44@&4oooOP`S٧+NM?]<10]]]]+++3ޅ=nH@#<<<_o$p+N]M<<<<?<991053353=N@         ?  %  0   %   U >q+<<<<<<9/9/?TX8'l@Owvx % &D dsy%{&%& Uk:%E/)6#  =) &i(h+??9910]]]]]'6632#"&5476%.#"3276F^L{/-ڎ(`A>v}fDKUur@"#l vy+<<?<IlUk{`0H8 F0#A<"Yw=>w=2(,&):N9- C/| |@)ip) ih+]]??10]2#"&546"32654&uQceOPdeȰįȅr~uuzt*@%99EJJF%YVif|zt%&;. H@) +;::+';'''':*:)((::'(/Ox@& Jj, (@((x@)$&J`)) ))++]q]]<]<<?<<<<]q]q?10Cy@ # "%& %&!$ #  ++++++++q]%&'&54$3 %!567>54&#"!5l9W^/PlW5`lP,Hd3cɏiC?`I]:?(&=gxHD>5<J@`=9=HL)O9Z)^9z(@0"4%LCB%DH[VVS%iged#wt&====F6....2: :'' F 2}@ 6%3=%.7@..?...@+5$3+0+@+++@ %"C$  ? O  K47+N]qM]M]q?<]?<9/]9/]10]]#"&'#"&5466767654&#"'>326632!3267!&&#"326762Mh{c–fiWx9iăf;(@sbCXgHz~Om,;jesE`ff`VN~e*MUuN2@FI}*Wvk"'"/LGarU4dg!+J@", %FTd(!"FI LLEK&Zfdf""+,+/"; =8&T-X \^&" !"!!|y!y"#j!i#z zlfm&u;"EJ&-,"9M CIF'z"v#"d mmh"""# @1!}!#"(-(,  % [[@% ($-4-@# 4---- ----$,3+N]qMN]q++]M??999999999.+}<<<<<<<<10C\X޲ 9!޶9""9#@ 9"%9@9++++++Y]]]]qq]]]]]]]]]]]]qqC\X@)"#"#$ ""-]q]qY]qq]7#"''7&'&532&#"32654'&c`k?zi^l;(&R[d4?Nb FVFdԍqPGDDm- *Fʖe?L9*SO&"@7|k|`k]]KRLK:D ')"@!<^l $$<"^"^ v#+]<]?<71$7wrYY6&"nM:;+b:jatOJ`ll& O@#< :8 8:<<  !++]<?/10KSX <F&7igq3x2*-@c$ #+$""%+-// "%!"%&!!5656EFEFVVeevv )-  )-$ #' p    : + p  '>$ >$##> > i/+>>>>i.+<<<<<<?]]10]]]]56323267#"&&#"56323267#"&&#"3j<{EE#A6@R .5@m&W DD#K&K*D-K2D4WWS#_&_*S-gh`$l&l*c-\2T4R[23#;&:*3->224 (% 53/+ 3 %}@ /@(@@3?ĵ0@ 4@!$?O647+N]qM+q]q?<]?<9/]99999910]]]]]%#"46326632!3267#"&32654&#"!&&#"Lzu3@|c +볆G\WM-K vxcd sX]nӦooiĺa~bo[@5 z+N]/M105!sʑ[@ 5 z+<]</105!ʑSZ @\~~llZZ      < < P@/o8< <@4u8<<q+N] @4 u<8' jq+]<<+<<?<?<9910qqqqqqqqqqrq]53'667353'667W+[,65+[,65ѥ;Q)G_Sѥ;Q)G_SQ ~@6{ Zl  <8oP@8'<  y+N]º(z-Q0X[RM`Eb:xLl*1FXQe\H,L9@ 9@uu< 0jKZ+]/10]++#3# ;9\H!4@uup+]<<<<<<<<<<<<<<<</<<<<<<<MHDFJBM7!+5#2-)+/'2 T;QN?QGEILAI9 63$6,*.1&.  +++++++++++++++++++++++++++++++++]34632#"&7327654'&#"4632#"&7327654'&#"4632#"&7327654'&#"@YaOA; +,"<>!-BOA; +-";>!-OA; +,"<>!-6 ǵǺŘj-/.>ǵƹŗk-=>/.>Y,&$@j!@4η A!)d+++5,&(kj*@ @ 4 / _  H+!)++]q+5Y,&$?j!@ 4 H+!)d+++5&(l<@4@ 4@4H+!)++]q+++55,&(Cj(@ o  @  H+ !)++]qq5,&,j+4@"%4/ZH+!)++]++5Y,&,j!@@344@4aH+ !)++++55&,@ H+ !)++556,&,Cj(@@4@"%4 H+!)++]++5c,&2j$@4pH+!)++q+5c,&2j@ !A"!)++5c,&2Cj$@ 4PH+!)++]+5",&8j+@@ 4O/H+!)++]qq+5",&8j@ H'!)++",&8Cj#@@4H+!)++]q+5z&&@ % +]<<??<1033ƴ&I@d<=@ 4dd<h+NM+?<]<<<10#3#XqT@@ :??@  v Mv!z++NEeD?M910Cy@, %&22  222 2+++++<<+++]&763232673#"'&#":9Y>k;# "mT?gC"h>>6#4rr8$/_#@45h+N/M+105!p˔.} KU @ @@ A+]NM?<,+?:IjkLMjkO/@@--@?k[A : OZ@ : vM>Wy+? @^&>k U4Ks u:A@!<r<rp+NM]?<<M[4z-cxYKDT.w"xeV(H@d<=@ 4dd<z+NM+/<]<<<1073#3gn+ z@     @!    T  e]  ;\+]<<<<]?9/9/?<<10573!!Lf5{|ҭ v@N p       E  @N  N GP+]]<<<<?9/9/?<10]5737#>nnss)\&&6(d@ 11H+4!)++]5?&V@ p11H+5")++q5)&&=d@ A!)++5(&]@ A")d++5QYOe~@# v  !++N]<H WXfh 7) $ "%?O!G7+]<<</l*Q {j=m@ u  a '? a@:) )'h+NM<?]99910q]q676$7654&#"'6632!)? %FDBA;-S#99V)+0>/CoivUTK8s=$y!+v@##M'0a)@ '_o?a@) ')&)'),h+NM?]r]9/99910732654&#"#7654&#"'6632#"&! +;GVHW QK<;8?)}xGCYT<N72<n<+%4,:jTkP7VeD]ok *@//,3!?&D!T!(((@!(),+   @ :Ma(b)b@ **' 'c@)":**)i, )D+Wh+<<<<?<<?<<<999999.+}10]]356673676$7654&#"'6632!M6fz>/l=*> %EEBA;-T#9 *Q {j=89W)+0=/BpivUTK8t=#yk @   )+::Vf fv@   @  :d@-' 5_5M  5 h+N]M<<<]<]<?<<<<<<<</lzhh9 *Q {j={l!-8;3/=@'=<% 23:% 1:20M @  0 @/?_o_oa a+@35449677/;9d0/.32@ 88.. 0@;1108:5363.)8N=)"" )0(((@ )")<|f+NM]<<?<<<<<<<<&V*@ 33333@ 43.&D@ 8'H'8 )++Z&&'dH+!!)++5G (@06'SS'bb'56-!: I CEI(Z j - # & C@C@@&H H @1#3   %@`t$?O)4+N]M]<<<<<&H='@`PH+ )++]]]5&&(3d*@ @ 4 / _  H+!)++]q+5K&H@  H'!")++*,&/Rj@  pH' !)++B&Od[+4@"%4/ZH+!)++]++5* h Q@/ee Qe     ;\+N]eclM,5F{"E*t0&&7d$@ @%'4@ 4H+ !)++++5# "@*ol```ppr   !-@$@3 @@$+!  "0@ %E#6f+N]M<<<??<<NM?<99993310]]53'667#"&&5#5373#32EHN-31LeclM,"&8@ h'!)++&X@ A"")++55",&8j @`UH+!)++]]55&X!@ 4dH+ ")+++54),&=j(@@ 4YH+!)+++]]5(&]OiH+")d++]5)&=0j  H+ !)++]5(&]'@ /H+")d++]q586@  ;+N @P "7GVVv u IFFI[TT[h h gy  %*5:o`  @'k@B7"@? O  4!]<q]33????9]10qr]q]3#&'!"326%"32654&F;F;+S}D|&~d^,!eg#ĿH,@;X Z Uh h xEJJUZ '(78E3993[  ko c `coo`~ __ P PP_ZP+k j @'{0@[k@@@$%40 1!@?1 ]]]+3/3/]99]r9]??3?3910q]]]r]]]q!!#"57&'32654&#"!dվ]Rzf@ Aʪܼ˼bc>$@7&_&}}&&!96"uy!!  @ 4? @ p  #@2  @ @"4  `&@O4%]]2/q3/+339/9??9/q3/]33/+3910]q]&#"327&#"327#"&547&54632={kXQxt# opM{h]^6F]nEGam~LSwD>@Y6699EEIISXTRT^ejdddm   $P`p0@P`p @ ?$ @$%4 ?  1]+q]]q???9310]!#"32"32654&߅#K_&|}±.&o@ P ` p O  p@+% p@p J/^]^rq]]]?L<;Ol#67!5!&&'VcM^g;L<;l%-0lea^vSh;5367#&&w^`elɐ/.%L;g^McVOn#67!#&&'56673!&'3^PE}}EP^^PE}}EP-++-,Ug3&'67#&&'5566-,,-+^PE~~DO^^OD~~EPg3&'67#&&'5566!!-,,-+3^PE~~DO^^OD~~EPbjk3!!jded#!F@  #!! "y+NM<32#4'.#"% D|wנE  5o\\s.mE}b]4t?rvLPh6r: j@< %?   %0%   WZ+N<<<<<<<<<</ON?2abu u??e!00!"//"!00!"//$$_dd #/! ! 4&#"326%4&#"3263267'#"'vu/"!00!"//"!00!"/2ba2?NOu u"//"!00!"//"!00_dd_$!FU/;3!!#&&''&&'!5!66776674#"32LfX"4I&P|9bkLpP3BD P BE0$e\$,-U?G5_J`]E2H9 Q>=3TjEjT9F=,-I '!!#!5!"&&546632"3254&&5;4gv~~n;I;yzzyoT&#"&&5432'"3254&&$93NXh v~~TfEK25yzzy:"#"&'!726'#"&54766766!lJ\N1K-TZ!-0IClE`a]c %%__[I;R6S73!!7676654'#"&54632&&5463267632#"&'&'J6Qg=[t^"#"3o|rvt3FG)rmYb(tx=3%X=)?&&'&'&&5463267632bsy.)mnQ=&!4/        ]@   ]/]@ ]  P`p]q<]<<?R>M[wh[|#N>CU.w=vgP~FfW@lOp ~ E  E % N]qr<<<M[uhet"N>CU.w&Q! A/+5]@c;4 ?A il{uru $$/q  /&  V ]<]/]/?@J$4Dr    %@364pE%@364 N 9]q+<<<]q+]q<<9?&U%/\&&6Ld@ 36A2!)++5?&V@ 36A2")++50@7  0     99   / s+]<]<]<<<<9999??<<999/<99<9910]!5!!5!!!!#JHu #&4@^#&4/ + +    3  "" %ENG +]q<<<<3/q993399??<<9/<<10]++#53#5373#3#327#"&&5%@ /I=jslU>Hu"&8d@ !A!)++5&X @@ST4%A")+++q5"&8d@ H+!)++5_&X@  A)++5"&8d@  A!)++5&X شH+")++5V""XX"$@*4:;4!6"JJF!F"XV!f"v" " 4]  @  ]@' &  P`p]$&  4]#;++]]]q9/]q3//]?M[vebyIt۴NG*GT.w=xeFqPO^NW&%ֵ "4'@ 4 4@@4 &5Dw*+ E #%   @pE %"!#0@%$@364$$$N'@464''p'''''%@364 N&GP+]q+]q+]q+<99/2//]<??L\uhbw|~;nS[0N>CU.wJ)yzk !-8@/:{wB\4@';4?%6a@ %/%?%%%+a@    }0a@  ")'.) @()'3)::9  )F9Wh+<<<99NM?<<<<99/]9/9/.+++}10]356673&54632#"&54732654&#"32654&#"M6fz7.l}F33IH67@GPVDFL9 *Q {m=/sPokVs-)j~d244-.7:E5:DE""6BMQ@/Oi&f*{&w*&*B\4@';4?!--:##Ka@ :/:?:::2@a(2a@ ! 0da!@/?_o_oa}Ea@2  7)%'C)5@/=)+'H)/OON)" )0(@' d)")N|h+NMr]9NM9?<l ` ٹ+9<</<<1053'67J^6]un&T(kk[K@ M   1@:M/8)j W+NM<]]10632#"'73254#"#|?M ,+2nHMt uLCO Z@8 @ P `  u@,/4_P/r]+q3/]3/?]q<<103#%3#%3#[u<֭[X@o/0gh ` YVPh               p@ RR@4@ 4T  @ eRP@ P    @ +]q]q]qr++?@'O_  b V b V ??9/]q10!!!!!!#^gyY&&c2"O @4 ]   @4]  P ` p ;Y+]q+]]???10+3!#!~F 3 @s 66   &- 7:? I ij xx            1  ]<<<9??<<9.+}ć.+}10]qrq]!!!55y <!/07F< I;0@i@O I@@`p  0@ 4 4## 44 ]     0 @ @ ` p  ]@ 4 ]]+]q]q9/<<????9/<<10]++q]33663#$uO sb ;5 a@[XYXW [TXXzu \P od v % KKFE   * ; --  ::/Ox@& Jj_ @x@&J` ]q]]]<]<<?<<<<<qui>@`rp %5F   %p  N%@364 N 9]q+<<<]<<9?9V=&)@ N% EG +]<<??<1033&& @:D? ZYii   (8XYJ@M  %%  e     Pp % @364   N ]q+</]9q?<RiE> @dk jj`_bjl blP__ P_9579IFFIVWX Y h x    $@$%40` 1@ #40 %@364 N]q+q+]]]+3/???910]]]]]q32#"'"32654&s#i."}@VQ>"@?')69 FJ   & 7 G v  ! $ @$%40    1$$@#40$$$@$%4?1#]+q+]]]+3/39/3??/10]]]&#"#"'732654&'&&54!2*pp‹|FަCU,:+`nO~Y${#t3%AsK S;69/d&@KDDTSP[cjs{ p  % p  N%@364 N]q+<<]<<?53#.5z05Mδȋ.&gYb ʗDW&$@I&( &9H@&S\]Sdknavzuz$$&&& HH   00@#  P  0 #@@)$%4 0 0 @`1&&@#40&&@#@$%4#?##1%]+q+]]]rq+9/qr99/9/????9/]910r]3326653326543#"'#"5c@p%%q@b۪ab&d~7{cհI"&@ H+ ")++55&#@p A")++]54D'&R@H+")++]5&# ")+W&&@ %' A%")++5&(^  !)+2@*fv" ]J  J@ & 7 T<9/???<9/9910Cy@  66 66++++]!!632#"'732654&#"#!2hLR8c˲!%^X U,&=j@ H+!)++5dv@+;]o )) ) 5;5 ;GKE KVT V Tj wyx   `p T   &&b/ -&  c\+]]]q9/??9/9/9/]10]q]!3 ! 4$32!"Y ^Y 836S Kt1GJ23\6,5&,@ H+ !)++557a- )&=@b&&  H@ !&@$%40 1( :J-']]+9/]<???9/10Cy@,$&&%%#&, !6$!6, 6"%6+++++++++++!2#!!#"'73265!26654&&#!^bɾ+j@Z!0"BBjzW]ohM wc)w`[{&@+    &@$%40 1! ] <]]+9/<<?32"'&#"#&&##"#67&&'&'&#5632kS=OW_  ]-.;@^Y.by`b .OdE?--YN e`P?Ti~iwQ--s&(Rer' k((OwdN&@SN9Fe!uKZt( @P`p ?O_%0@P% H %HH@% &K& ""\(&b&1']]9/??9/9/]9/]9]910Cy@#$6$66#6++++q]]q732654&##526654&#"'!2# N]mo32"'&#"#&&##…lT=OXp  ]-.;JfG.el~gĐwQ--a'{R[  b H@  ] : J b]???10Cy@ & ,,+++!#!#"'73265 +j@Z!0"BBF wc0"+c2!L @4 9   @4]  P ` p ]q+]]??<10+!#!#F 3fv&07 @>fh ] J H@   \\9/]9999??<<<9.+}ć.+}ć10r]r]33#"'53267 g{KmNWGg>~ք#-[R@M !!O!p!!!%++%{ {0 o   ?p &?o@ &0`p@   @    O  /]rD[D(@99 5%9'I F%H'YYU[Q%\' = & #3  & $@$%40 1*@#40***#$@$%4?1)]+]q+]]+???9910q]#"6632#"&!264&#"326 IsߢGDrp(2kTVeeO$ P໹&#@$%/%F#+##++ $$ @$%40    1%%@ #40%%%E$<q+]]]+]??9/9910Cy@3!%% & !   " <+++<++++++++]!2#!326654&##32674&&##k??Kc Ļ!sVDwrBju&3_Lq&^gI3TBGW3W&@+ %E]<??10!!#cQ&ol& @X +  + @    %+?ON+    <]qrrq]9/]q9/]]?<H`&8b@x' %%:/:?:`:p:: ::0:O:::::4;!!!533+)'))'+% !!%"#""#53') %# "7 @?.3 0 H"!! %'5)3/#! " @  (@ %8(@O//"""P"""p""""39]qrq]q<<]qq9999?<<<=eI%W7 QD2b>&@] (U ;P`p    @HH H ! $!$@$%40 1(@[']q]]+9/9??9/]q9/]9/q]910Cy@*$#&!$! "%<+++++++]]q5>54&#"'!2# 732654&&#"rrSJaM=P2~PPл:[[yLVq P\& @8Vg{+ %@364N @464 p  % @364   N ]q+]q+]q+99???DD /,//L^z KK ?{{%   @* H    I %@364 N]q+<]]99?<33'"#&&##VEC5BV_$2G+)DGtp;X=&5B~PmhP!Ic?#&@+3  %@364N@*464@`p%O_op | +9/]]q+q+??10Cy@ &% ++++!#!#"'532665D#lf?RO80&vX2& @r   FJEJ VZ      xx     @H ++      %N? % N ]<]]<9?<<R&d@+ %@364N @464  p  %@364 N]q+]q+]q+?SP>F&&O@/ 0 @ _ + WW@%PP`/]qr??<10]!!#!&_&o!Q&\KiJ)5@jX  77[\ USY Y"Y&U,V.U4jj edj n"n&h(f,e.f4yvy vv #0'33!3-  %$$@$$%40 17@7P7`7777 707@777@#4077*$@$%4?16]+q+]]]]+9/<<???l&&aTYEl& @+  + $@7$%40 1?_ %@364 N]q+<qr]]]+??9/10Cy@  ++++332#!732654&##>l&aTYEl+>S}@ 7N5>@q 4GZ_ PSS_[no eccon"+  $0$ @$%40    1!%@364 N ]q+<]]+9/]q<????9/10]q336632#"'##"32654&yڴZ|{&D*˷̽&@H 4 D [T y $  %    +   %N ($<9?<@:EUUk l ls s { tu_o "0@` @1 4+  $$7$@$%4?14 +N]+MV<L :&@ H+ ")++55Q:M&"@,$+"" ++ +  %@ `   $@$%40 1$$@#40$$ %O_#]q+]]]+9/]q<??<?9/10]32#!!#"'5326532654&##D> 'ohoG(?([k&a﯐G NnSZFk9&@&+ +  %/ $@$%40 1@ #40 % @364   N]q+<q+]]]+9/]<99<?fkl)&FgOTBe@  %5Fz@'+4    % @364   N@464p%@364 N]q+<<]q+]q+/]/?' &<<9/<99?<<1_u=3oZ\%)gvۍG2)1V@ Tc  ̴ 4 2/+3/2/?3//?<10]%3!5!4&&#"'63  BAjX1@Q#/8 4K Y j {  #      @ /]2}/]323/833/99?3??<<<|9/399.+}ć.+}10]#"#76634&&#"'632bI{R;TKIVG=0Ca4o[VY6 f-%.@  ̴ 0]]?+ ; @  3/3/?3??1088]63 #4&&#"(*Yzl+BPV7]@8JJ \\ YY*, ;;    ?  & ]3/]3<3//?3/?3/10]]77326654&#"'63 #"P^cqSd]u\ +]]o<F;@6DTu   ʵ 3/9/???10]!#546776655!3L*46Q31<,%pGHm|?L8ZG5910@CSf  ȳ ??10]!!6324&&#"9bֳb4Ui#sNuQ#P>1@ )6ZZij @?      ?_ 3/]3/9?<<?B H@*/7GSYY S S\\S  &Ƴ&??10]432#"732654&#"Zؔpј6u1%@J5 yyu u ;i  3/]3/9/3339/?<;+\%qHHn{mNL7YI(1B+ ; I @  / 3/]3/?3??1088]63 #4&&#"(B>k \d*%!x@M+/#15==1HHY\Zhjju r tvy! ! ! Ƶ# !"229/?<2 lg&&&&=3oZ\%)mpۜ>%%%%2)1& @ `A)++]51&F(@@$$ $P$$$$$A)++]q5-%&N @  A)++]5@1& @@pA)++]5%F @  @CD4@=5O@ /@4/+qqr++??/10#2#"&5466š%&5&%%q&&+)%;%"n@ $$Pb@1 '  @4O@4̳$ /]+/]q+3/9/??/]10]"#54'&547!5!2#"&&546; %&%0TfjJ1q&&&#1@;&@ (!A)++5%M @ @CD4@=5O@ /  @4 /+qqr++?3/]9/10#2#"&5466š51#5&%%+)#1,(&(h1&@ A)++5PV7&@"`"p"" A)++]5<F&9#!!@ 4!A)+++]5P>1&?@ %A)++5<<1&b' 4A)++]+5Z>B (@!/*\\S 7GSYY S S"@ / @&O&_&&&&&&Ƴ*&)9/]q??9/]qr10]]432#"732654&#"2#"&&546Zؔpј#%&%06u1Y&&&#1nh1(@ "4 4IK[)8=@!!l )  (4U@%@#&4%@4%% /ȳ* )]9/+++?3/?9/3/1088]]++327#"63 #4&&#"2#"&&5466&\-6D?~b %%&'z 5uN%%&%s7 -@MKz//==!@((l   #4>%@+@!#4+@4++& / .]9/+++3/3/?3/?3/9/3/10]]632!"'732654&#"327#"&52#"&546z8q.b¢TR{ 'Bb %0$#105G?Ĭ{I%$00$#1 f%&e@  AG )+q+5h%&3@#@>C4#@374#@4#p##A)++]q+++5(1&@ A)++5d*%!.@Z/0 yjju r tvHHY\Zh+15==1"@((! !%@o,,, 0! Ƶ0 !/229/]9/]?<2 lg=3oZ5k%)mpۜ>$  ///10#$H %@ MM  4@   @ 4ض@??4@44 @4@[\4@&)4@4" 4 @+-4  @ @+-4   @+34 +-4+-4ַ"+34/3/+]+]+2/+]+]+/+3/++++<<3/+++<+</10]r#'7'77'$H1111m1111 _@6 rrr  "()4@ 4/++<<</<<9//10'7!#!'71GQ11b1Q# z@. @ 4r  r  r   ޳()4@ 4 @ 4  +++<<</<9//10+#!'7!#G11Q11C 4 4  4/3/+9/9/3?3/++310#"&54767P?MfX+V!;75Tkp7=76(G660] 4 @ 4 @  4/3/+9/9/33/?2/]3/++310#"&54767#"&54632P?MfX+V!;7C00GF11B5Tkp7=76(G660"/EE/0DBC'3̳4@ 4@4@ 4 %@ 41+(. @@4""(.$/3/3/]3/+9/9/?2/+?9/++10++#"&54654&#"'4774'&'&54632#"&54632$)@2Bn@4AS*@* =J~ KxI41HI43F>/5B,DD"*I51Lt"izBR 48BpYol3IJ24IJy3$ 4!@4@4# 4# #   @ ##&@ 44 /9/+3+3/3/9/9/9/?3/92/9/+9/+910++'6767'&54767632&'&#"60bq] t3(0>PQK1 (4%='0h0%P+(#FZ/:@ 9 %# -3 4339%%## @9 0%5)#0 4005@ )@ 4)) 44/++2/93/+2/+9/99999?2/9/9/2/+9/9910'#67654&#"6763267&'&5476324&#"6V.:GW(  ."&E?'+5&G %A"  /)C6$B % #F7B*/ //4&U&F[E  4 $/<3/<?3/3/+2/10%4764764Z,S4Z,S[/,#Q,(";/-#R+*#Fb1#$/3/?3/104764Z,S1.-#Q,(#H(' !@ 4!!' ' @  '$$ $4$4$4$/+++2/3/2/99?3/9/3/+9910'##6767&'&54632'4&#"6S2J2I5G@!M-*  K& ,}$27H83'Be8()7 ]."F#$/3/?3/10%4764Z,S0,#P+("F(@%&!&@ 4&&! &%%4 4/++3/23/299?9/3/]3/+9/999910#"&'#"&546773276773276573 #** +  &K06 $ 922 $ #8K1 2(+/FQ -  /3/?2/10#"&546324&#"326Q\C66P;6Jb<6MwZWD-# | @  4 /3/99+/3/10&'&'667U0 U#"89&Wle0"D[v_1,.%; @ 4 @ 4 /3/+9/9+??9/10#474'&'&'7":-O(J`O0D#*ZwsԸ~@Z_Qs%! p@ @4@4 4@4@ 4/3/+9/9+3/++???9/9/+910#"'#'&&'7327677!49h 8&L0BC4_jp+ nBHP/OԲCU_.TK)k#]%) 4$$ @ &A   !!'&@ 4&&  @ 4 4/+3/+9/9]33/+9/????9/9/]9/910+#"&'##4'&'73276733273_c9T"hI% (8D4I4!\UUeJ9mL]o%XKFGLD??DY ")=9<3B#W !-.LdMbDG)+$ !HBAM)0 !@@@4 4@ 44/3//2/10+++#"'&547676324'&'&#"326guyFP,2FV\vJPCe]/B,$E?|zq*0[Qbx9B7-)ZIHR&#I@G(o( 4' 4 4 @ #@4##/3/3/+993/??9/+9/9910++&'&'&54767#"'&5465732767G&A!8$0J/0&$.jOcU .$+%=[o:=  N!"7%g3WAnWH%i@ 4&44 4@4@ 4   4 @    G/333/3/333?+??9/++9/10+++#'376)@ǣ$eb? 'YV^3=1o@T4&44 4 4 4  4@   G/333/3/333?+??9/++9/10+++&'#6763$1+ *[tG[WFV]) [/@Z'*. 8 P@ 4    /9/3/9//3//3/10]+#"&54632%##"&54632L76ML77L>mDL87JK66N7NO65JHGz6LL66ON0"9 4 4 @  /3/9/9/3?33/+3/+10'6765&'&54632&;"J*E)1%)K69VUI;9!777-( $<6MP:dgs 4#@2 4 #0qe6kY 4B 4YBGN,9ak*qFU;`kGq 4qqTGkkkN@ 4\ 4 \_QahKn? 4&@ 4?&,P333FGTUn`ah;*n,ah@ 4hh9,n@/3/+999992/q99++99993/q99++?3/]3/+9992/99++999999++10#"''#"'#"&5467'#"&54767&'#"&5463267&'&546327&'&5463267632676324&#"326d,!5JJ vV%4j  ! !$"!.0$VqC!;+!+*",k3=z;E(* 3#'"D6`*<  it8K`A2k۴qF,{@# 4 4! * @4 *@ !* 4.',/3/9/39/+399?9/?+9910++7#"&'#"'&54732654'&'326Bi\AJk0,Y/_=APM(0AWP)0@'1D=+Gжxiw%47_q 3'J|+CYRgJ@J-{#y 4 4  @@4@ 4###"/3/3/39/39++??9/9/9/9910++47"&54676632&&#"3267XGQNK;v-5sK IM1}l_V{-b^hDM-.?>/YJRV_ C26<"8"Xa!!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ FW:ι@ @ 4 @ @ 4 $*783./#*//$88$$*3* @ 4 /.'$#877/#$$/.4. 4.$/++3/23/299/+<3/<?9/3/2/9/99993/+3/3/+3/10476476#"&'#"&5476773276773276573[,S\,S3! #)++   &W.+%P+(#>0%P+(#L06 " 91$$ #8K1 2)-1!!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ %W/?10!5!WW%!!%!! @ !!%!! @ !!%!! @  $($44(4@ 44 4!#"$$%'&((@ o     A "$!##% &('%%A @*  4 */2/+2/9/9/993/993/99???93/]q993/993/9910+++++!"'&547673!54&'7'7'7''7Fr*9|oO6AM, DEJLJM"KN%CT]a#b..G8vA:p27pTVZU; µ44 @ 44 4  @ o   @ @ 4   */2/9/9/+993/993/99??3/]q993/993/9910++++%'7'7''7%!5!4'&'7JLJM"KNKNH8TVZUv>+Q[3M;!!!%!! @ !!%!! @ !!%!! @ !!%!! @ 6N u,04844.48@4) 4* 4y* 413244.57688-/.0@ 400  241335.0-//687@5@ 455 :'*/3/3/33/999]9/+993/993/99?9/99?999/+993/993/9910+]+++++"&#"676323263!3#"'&'&547676'7'7''7L@P4Z(#KB_Ep%Ӕ{F:؏XdOBFJ'F4@4J@4 4 * 4y 4GIHJJA?B@@CEF@DDD84=)%03"4-%%34=DFCEEAHJIGG@BA@?@4_????83@99""3433L))*/3/3/33/999]9/999/]+992/993/99??9/99?99999/]992/992/9910+]+++++#"&54767!3#"'&'&5476767&&#"67632326733'7'7''7{yf WXoF:؏XdUB%(Y$e?n"%SaM3a5()4!:2KUJLJM"KN%Zh':$4%DVll^ (0jyǫdSZ b&T%  8RۑTVZUyk"44"4! ""   "!@ T 4$/9/+9/9/993/993/99?<2/93/993/993/9910+++!5!&'&'&#"676323'7'7''7y%fFWHQS34/DhfEy+TN}Mb@()qzJUel/ *""sVj[Z4bZHnF)"+0s~9=XpTNT&m,!Z%E*ֳ4!4/4,40 4.@ 4;k= 4 44(4(ܳ 4(@ 4A(#A "  78@ (_((( ##8@AA 8G   4 */2/+9/9/9/]99?<TNѫF;@()XG=5!I/-=-{RZ,Q1 3ow%9=XpTNT&m,!)1YC.&Z4bZHnF:#0CA8<%Z9&3T)i? 1dk%m@4 4;k 44ܳ 4@ 4   @@o/_  */3/9/]]]99?TN}Mb@()qzJ%j[Z4bZHnF)"+0s~%d6ܹ/ֳ 4& 4441 45 43@ 4T+T2D+D2; k $ 4 4-4-ܳ 4-@ 4-"-(A ' "@ ---((8*/9/9/9/]99?<TN}Mb@()XG=5!e9&3T)i? 1dk)1YC.&Z4bZHnF)"+0CA8<%Z%,7@04/ 46 4@4 4;k 4*43 2 -... 4ܳ 4@ 4, A   %$ ,@ . 22@( A 9% $ 4$$( */2/+3/9/993/<?TN}Mb@()qzJUel/ *""sVJظ] j[Z4bZHnF)"+0s~9=XpTNT&m,!OYN?j$~FOZ%EP3@I4H 4O 4*ֳ4!4/4,40 4.@ 4;k= 4 44L#K"FGGG"(4(ܳ 4(@ 4A(#A "  78@ G#KK@ (_((( ##8@AA 8R   4 */2/+9/9/9/]993/<?<TNѫF;@()XG=5!I/-=-{RZ,Q1 3owظ] %9=XpTNT&m,!)1YC.&Z4bZHnF:#0CA8<%Z9&3T)i? 1dk˩OYN?j$~FO%m(@! 4' 4@4 4;k 4$#4ܳ 4@ 4  @ ##  @@o/_  **/3/9/]]]993/<?TN}Mb@()qzJaظ] %j[Z4bZHnF)"+0s~/OYN?j$~FO%d6A:A@4? 49 4@ 4/ֳ 4& 4441 45 43@# 4T+T2D+D2; k $ 4 4=(<'788'-4-ܳ 4-@ 4-"-(A ' @ 8(<<""@ ---((C*/9/9/9/]993/<?<TN}Mb@()XG=5!:ظ] e9&3T)i? 1dk)1YC.&Z4bZHnF)"+0CA8<%COYN?j$~FO2;&@4)44*4 4!' A 9' 4 03 @ ,$,@=! 4 $ 9/2/+3/3/99/??+??9910+++&&#"332!"'&54767673276654&##"&54767632 #a2W`X+5PHE`^f".<*?Cx+B71Cs}TePj6 @ 455@ 4,," 45& / @8  4 9/2/+9/9/??+9/9/+3/910+#"32!"'&54767327654&#&&#"&54767633])0Q0{@b(&%9zՏm#s?6I?3:??4HH44:C%%:,@ TKQKKQ @ QQ(?>743!HGG?/344?>4> 4>$/++3/23/2993/2/3/2/999?9/3/2/9/99993/+3/9/3/+99910"'&'#"&'6767&'&54632#"&'#"&547677327677327657'4&#"66#'#9<8L0: G-/   3! #**+   &Q#1  1& "!+U)# K06 " 82$$ #8K2 2)-1( F)1@&'"''"@ @4 @ 4 0*,,0.@**.@%(4.'&&4 4$/++3/23/299/+3/?3/3/++9/3/2/9/999910#"&'#"&5476773276773276574763 #**+  &3Z,S}K/5 " 82# $ !7J1 2),1/-$P+(#!!%!! @ !!%!! @ ///10#!$HbGQ$///105!#QHG !@   /<//<10###!!!HrG !@   ///10!!5!###rHGS@ ////<]10###!H @@  @4   /9=/<<//<+9=/<<<10#'77'$Hb ׶yyx #@    ///103#5!3###HGG)@     ///103#5!#3###HGGG !@   ///<10!!!##HFHS///<10#!$H,l@ /9=///<10##$HSW$'@ /<<//9/9/10##$HHH'88W' @   ///1053#5!#3WGBGGGW'///10#Hnw W$'@ ///10]#'$H'0W'///103!$'wGJ!'@/) !)i.@%) .0@>")> > i@)!?O>>>8H(y+?N]M]<]<M]<]<9999999910]'76327'#"''7&5432654&#"ՋsjitGGtijsGkklkwHHwn}~nwIIwn~}}llk #.48DHLRY`h@wz&S%c%#%3%C%Y=i=)=9=I=YAiA)A9AIAV;f;&;6;F;VCfC&C6CFCfhbdV`f`Y[i[**c*****u***3*C*S*cBB(-Wo]?]O]_]]]'VP(/(?(O((/ OGG23/3g<^P''O'_'' '0'@''R F"OM7K R6JMap999@9P9`999'W0^^'''_'o'@f''''$U-e-%-5-E--S++_oZP$$o!6 5## ! #j0eeo??????O??IJN/MMN1EQ2FN/]/]qr]q]]]q3]]]qr]]]/<<<<]]q]/<<<<]]q9/qq910]]q]]qq]]]]]]]]]]]]]]q]]#5#5!!5!#3#"'73253!5!!5353!#32##!!5!#"&54632#3!5!!334##324##32%#"32dCC?ddV4I(_tC~dYwdC~CddCdYeo|~ddd~C-Od?d ĺ[6.C?d{C~ddCcm !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ S0HH 44 4 /3/</3?310+++&'&'&4767670,h3g9JJ:f5d.l8<"8-}N౞Sy*K]SHW|@!!%!! @ % /|O@#%-.)$%!%%...@ 4!)!@c~9W=@C~~IIPPwccWCk55W==C%$ .--%$@%%c9Zg_@ccwPgg0TFI~}@TLPPT4T@ 4TTF5n0s ww00F/3/3/999/++3/99/9/9/993/2/23/293/2/?<<9/2/<9/9/9/9993/9/3/+2/]9/99993/3/9910&'&'632#"&'#"&5467732677327677"'&'#"&'#"&5467&&547673267&'&547673267&'&54767'3263 6 %%5)%  !** / & " wL=.(80?A{(p5p   C9.3'; $ 'B#:$X.4#72<Q=5-*$9 *jHk 6-- 0#U(()&%2&#;DL<;M!TL4"*"&RR{j70< '#*"%3wrI&.-'* )"+%"To #8 !y3 +@@?4  ?4 @ @ 4'$' @  $''/9/39/2/33//3/??93/+3/9/+99/+910#"'&#"'67632327'6654'&'667)02-c   & d2!54F2!31v ! $  PK(W L hu{,f-rt"%-@+-@?4--'+#"?4""'+@ 4   @--""## @ 4 /2/9/2/+3/33//3/??3/+3/9/+99/+910#"'&&'.'&'733#"'&#"'67632327D)$%  '{' "!b)02-c   & d2!54%70q{'0$xh|2* ! $  V n 44 4%$-0@ *4004$@  -00(%$$ (;/9/39/9/2/3/9/99??2/3+/9993}/+910++47&'&547632&#"326'6654'&'667n*d5;- $+]!2!31f @.#8> 4PK(W L hu{,f-rtV% 6 4,-@ *4--46!@  @ -,@ 4,,--&11 8;2/9/2/+9/2/3/9/99??2/3+/9993}/+10+47&'&547632&#"326#"'&&'.'&'733n*d5;- $+]!̌D)$%  '{' "!f @.#8> 470q{'0$xh|2*xcq *J͹-@ 4@ 4@ 4@ 4@+B2H0:@BBH@4HH0@ 400  %%!  @ @2==E++E006EE(( !L 4 ;/+39/2/2/3/9/99?2/?2/93/+3/+3/99910++++#"'&'&'73276767"'&547632&'&#"47&'&547632&#"326zBF3RAA8{1zmUU+OCL08VW&?')XM*d5;- $+]!a # >1]/j+1pgXfeO`% %13 @.#8> 4xcq-@R4@ 4@ @44  4%$-0 /$$@  (-00(%$$ (;/9/39/3/3/2/9/99?]?93/+]+3/+3/99910+47&'&547632&#"326'6654'&'667E*d5;- $+]!+2!317 @.#8> 4gPK(W L hu{,f-rtY@6@ 4,.4&@ ..4@444 4   @ ,))11"@1@ 41@ 411 @ 4 8;2/9/2/+2/++2/3/9/99??3/+]3/+3/99910+#"'&&'.'&'73347&'&547632&#"326D)$%  '{' "!*d5;- $+]!%70q{'0$xh|2* @.#8> 42q;[> 4&@4)44*4 4Q1Cs}TePk @.#8> 4j6V9 4 @ 4L7N>TIIQ77Q< 4%:Y ,@ 44 4" $*@ $$*@4**@ "' '''.;/2/9/9/2/3/9/99??3/3/+3/99910++!5!4'&'747&'&547632&#"326:KNHl*d5;- $+]!%v>+Q[3M @.#8> 4%:Y R3   /9/39/??910'6654'&'667R2!31$PK(W L hu{,f-rtY%<   @ @ 4 2/9/2/+??10#"'&&'.'&'733D)$%  '{' "!%70q{'0$xh|2* @ 44 4   A  A @"  4 */2/+2/9/9/99???93/9910++!"'&547673!54&'7'7Fr*9|oO6AM, DNJ%CT]a#b..G8vA:p27pT: ]4 4  */2/9/9/99??3/9910+!5!4'&'7'7:KNHfNJ%v>+Q[3MT: , '@ 4%% @ "")"(/3/9/9993/993/99?2/99+3/]993/9910'7'7#"'&54767'74'&'326NKALJ.GI07# !=#xWn/6- 908ːVTGn!=F\NO_T&?1 '#39?=%F%&/ @@ 4'+( . ((.ﵐ& @ @+#('  +1+/9/]999/993/99?2/]2/2/9993/+993/9910]'7'7#"'&'#"&5467&&5476733'32INKALJH7)\3s  !Wd"81<АVT[{\8>VND"*"$>t>DhmC7 %$@ 44 4@ @ 4 A    A @&  4 */2/+2/9/9/993/99???93/+993/9910++'7'7!"'&547673!54&'7%NKALJ`Fr*9|oO6AM, DVTzCT]a#b..G8vA:p27p%%:V{4 4     */2/9/9/993/99??3/]993/9910+'7'7!5!4'&'7NKALJKNHVT*v>+Q[3M%:V% (@ 444@ 4"4 4  @ @ 4 $A #$  $$##A @ *  4*/2/+2/9/9/993/993/99???93/+993/993/9910+++++'7'7'7!"'&547673!54&'7`MJhNKALJ`Fr*9|oO6AM, DcVVTzCT]a#b..G8vA:p27p%%:  @ 44@ 44 4         */2/9/9/993/993/99??3/]993/993/9910++++'7'7'7!5!4'&'74MJhNKALJKNHVVT*v>+Q[3M%: 6N u0@- 4* 4y. 4@ 4 #"$@"$##2 +*/3/3/33/999]9/99?9/99?999/+9910+]++'7"&#"676323263!3#"'&'&547676UM}L@P4Z(#KB_Ep%Ӕ{F:؏XdOB@ 4 * 4y 4A?@B@ 4BB84=)%03"4-%%34=@BA??83@99""3433D))*/3/3/33/999]9/999/99??9/99?99999/+9910+]++#"&54767!3#"'&'&5476767&&#"67632326733'7{yf WXoF:؏XdUB%(Y$e?n"%SaM3a5()4!:2KDMM%Zh':$4%DVll^ (0jyǫdSZ b&T%  8RUyk\  @ T 4/9/+9/9/99?<2/93/9910!5!&'&'&#"676323'7y%fFWHQS34/DhfEy+<KN%O,7J-Ad2m yTyk6N u,u@) 4* 4y* 4  @ .'*/3/3/33/999]?9/99?9910+]++"&#"676323263!3#"'&'&547676L@P4Z(#KB_Ep%Ӕ{F:؏XdO@ 4 * 4y@ 484=)%03"4-%%34=83@99""3433@))*/3/3/33/999]9/99??9/99?999910+]++#"&54767!3#"'&'&5476767&&#"67632326733{yf WXoF:؏XdUB%(Y$e?n"%SaM3a5()4!:2K%Zh':$4%DVll^ (0jyǫdSZ b&T%  8R%yk<  @ T 4/9/+9/?<2/910!5!&'&'&#"676323y%fFWHQS34/DhfEy+<%O,7J-Ad2m %yk6N 0@- 4* 4y. 4 #"$@"$##2 +*/3/3/33/999]9/99?9/99?993/9910+]++'7"&#"676323263!3#"'&'&547676MKL@P4Z(#KB_Ep%Ӕ{F:؏XdOH:%B-&>%)m-U%j%mf @     */39/9/9/99?2/9/3/9910]'7!"&546733!4'&'7NI@9[  *2>H:VB-&>%)m-U%F/pr@ * 4* 4 4 4@ 44 4  :  /32/9/??910++++++#"'&'&'73276654'&'7/^RztKEP=UHGB:~.%C9R',%nvhKH ( RK\OWFJLLVjF/pF/"@ * 4* 4 4 4@ 44 4 : $/32/9/3/99??93/9910++++++'7#"'&'&'73276654'&'7MK9^RztKEP=UHGB:~.%C9R',ȑTnvhKH ( RK\OWFJLLVjF/lWF@ 4@4! 4&4 4AAB:45,BB(55'( :, A145@ 455A ((''''#, @ 4 ABB?AAA=A @H  4*/]2/+2/]9/9/+<2/]9/9/+99??<?9?9/9/99/10++++#"&'###"'&546767327654'&'7327654&'732654'&'7O<[/*!/Z{,9ujt*$6(F-%5S2{_(#(%)K&C/ %!$& \WqBSFMVY6pE|CSdZGAR?Y4;# 7m>-Dq%5l Ro@ 44@ 4L 4*@4- 424 4  @ 4MMNF@A8NN4AA34 %+$F 8+ IM=@A@ 4AAM443333/8@ 4M(NN?MMMIA @ T% $ 4$$(*/]2/+2/]9/9/+<2/]9/9/+999/993/993/99??<?9?9/9/99/3/993/993/9910+++++++'7'7'7#"&'###"'&546767327654'&'7327654&'732654'&'7MJhNKALJO<[/*!/Z{,9ujt*$6(F-%5S2{_(#(%)K&C/ cVVTz!$& \WqBSFMVY6pE|CSdZGAR?Y4;#6'256 466B#&'@ 4''BCCB@ 4BB> I/2/+9/9/+99/+9999/993/993/99?<<?9/9/9/99993/993/993/9910++++'7'7'7#"&'##"'&'##5327654&'73327654&'732654'&'7gMJhNKALJM@\&/#3YA44"20PZQ#:) &.@CK$(*'":!)*A)cVVTz# %  $E:$<\*I%-9:"<^o+&!8> 7m>-Dq%l W1>@44 44 45-- <";2""2A @@  4 */2/+9/3/2/9/9??<?9?9/10+++!!"'&546767327654'&'73276767676324&#"!26 \rjt*$6(F-%5S2wfXaRAJYD?{RHY?aIH`r%hFMVY6pE|CSdZGAR?Y&!Gg4OI17 2&&'l W% >%0[ !))-  -& &2 /9/999?<2/9/9910!"'&'##5327654&'73276767676324&#"!26 N61%2*T\Q#:)#=AXTqzX QBJXE@zQdqpmd%$E;#l 5B̹@44 4 4 4"911!" @& @ ?6""!!&&6A @D  4 */2/+9/3/2/9/99/99??<?9?9/3/9910+++'7!!"'&546767327654'&'73276767676324&#"!26uLJ>\rjt*$6(F-%5S2wfXaRAJYD?{RHY?aIH`rdTlhFMVY6pE|CSdZGAR?Y&!Gg4OI17 2&&'l % )4z@ %  %--1 1 * *6 /9/9999/99?<2/9/993/9910'7!"'&'##5327654&'73276767676324&#"!26LJ N61%2*T\Q#:)#=AXTqzX QBJXE@zQdqpmddTl$E;#9/I  #- 1jC=ibI_NXAE%5,e/k^B_9/I  #- 1jC=ibI_NXAEȑT5,e/k^B_E&#8gM3I;P-8xYsωu]^-d" #i(`*/K1"C8\j/2DAQK]G % n4/  @4 4  /3/3/33/9+99?9/]?]9/910+##53&&54632&&#"66 Es$ś{P"QEn;cN]7K/w8v<b=21y//%mX(j@ $(4(  @$$$P  * /3/2/]9/9/9?<2/+9/9/99910#"'&'##532767'&#"#54763233mACP$CViZZTHRO* (:5qG]%H[%%BE&#8gM3I;P-8xȑTѠYsωu]^-d" #i(`*/K1"C8\j/2DAQK]G % !4@ /   @ 4 4 # /3/3/33/9+999/99?9/]?]9/93/]9910+'7##53&&54632&&#"66eKLFEs$ś{P"QEn;cNȑT@7K/w8v<b=21y//%m,@ ( ,  4 ,@$ (((P$$$$./3/2/]9/9/99/99?<2/+9/9/9993/9910'7#"'&'##532767'&#"#54763233LKACP$CViZZTHRO* (:5qG]%H[ȑT%BFUc,%h/"!)XHNYwQ(W[%~ *0G]jwubW/8)%1m%s%j%t@  # # @  4  !! '/9/99+9/99?2/2/993/9910'7!5!4'#"'&547632&'&#"326MJ)4.#I.528ZzB7*&#X4yVVYN %*Oht$%2-P%`!j@  4    #/9/99/99?2/93/2/9910+]'7!5!267&'&547632'&'&#" XSW>W33s7>Yf5*Z):(OK`~ 2xi]igP'K,L gw5BѳT  4@ 4!@ 41::)@@ @ 6 4&6#-==#A @D  4*/]2/+9/99+9/993/99??99/3/2/993/9910+++]'7'7!"'&54767327654&'#"'&547632'&'&#"326xNKALJz&#A* nfՠ &M'X7C:AYuD: *0-:%-"VTh]PWvxxF6J5C?>9FQ3-(0aqgt>).#$gw%j)@@ 4" '""'  @  4   %%  + /9/99+9/993/99?2/2/993/+993/9910'7'7!5!4'#"'&547632&'&#"326JNKALJ4.#I.528ZzB7*&#X4VTYN %*Oht$%2-P%`%@  4  " ""  ' /9/99/993/99?2/93/2/993/9910+]'7'7!5!267&'&547632'&'&#"5U}ViO{SW>W33s7>Yf5*Z):(ORQ 2xi]igP'K,L s%3(I84 @ /H# /&&&H@ @ 4 2:=>353%'IB@&>2SSQ!@ ('K-J.&D!OiN%mI#v@  4 4! 4   #    4 % /9/+99/9?<9/2/10+++#"'&'##5327676767'&'&'mhCTaJ:yt[G7-=YPCE)7s &0!$:%#=G7.$AXA:iTrG:8/2 !2'>%mIl{v$ 4@4 4 4 A ! @ 4!! A @&  4 */2/+2/9/9/+99??9?3/9910++++'7!"'&546767327654'&'7KHjt*$6(F-0S5($ViqFMVY6pE|CSfXN:Ql{v%:[ 4 4   */2/9/9/99??3/9910+'7!5!4'&'7LJKNH‘Tv>+Q[3M%:6 q=%F'n@ # & &@  #)# /9/]99?2/2/2/99910]#"'&'#"&5467&&5476733'32FH7)\3s  !Wd"81<%{\8>VND"*"$>t>DhmC7 Zp(5C@: 4:-=2-#4##--2 A @=:2)# 40##)00 )@ :@ 4:: 6E /3/9/+9/9/9+99999??9/2/3/+9/999+10#"'&'##53267&'&54677667&54767%4'&#"6764'&'326Z$'')pgGt5C[ZZ)LA c!E,@a{G^..Pz+8#3)%<=2 :GM40.B #/S7=ts+=+)C#J/51Umo)4&"."" 22@ *. &. &. * **6/9/9/9/3/999?<3/3/9910!#"'&57#536767632!%4#"6764'&'326m@-9@xdx#*5:C;/.(+<5[>H(t_\5@#%7EQN/;SdH]Pe@Jl=X7!C_^-i&,OI<nGVxc *u@ 4@ 4@ 4  %%!  (( !, 4 /+39/?2/?2/910+++#"'&'&'73276767"'&547632&'&#"zBF3RAA8{1zmUU+OCL08VW&?')Xa # >1]/j+1pgXfeO`% %13xc25j62V;?Cչ&@4)44*4 4<>?==@BCA!' A 9' 4 0=?<>>ACB@@$3 @ ,$,@E! 4 $ 9/2/+3/3/99/9/993/99??+??99?993/9910+++&&#"332!"'&54767673276654&##"&54767632'7'7 #a2W`X+5PHE`^f".<*?Cx+B71Cs}TePߐVTjN6:>33 @ 4:44,./4,,"@ >79::88;=>><58:799<>=0;; 4;;/& 4 / @ @@ 4@  4 9/2/+9/+9/+9/+]993/99?2/9?]993/]993/]9/+9/10]+++q#"32!"'&54767327654&#&&#"&54767633'7'7])0Q0{@b(&%9zՏm#s?6I+Q[3MDVTr:@DNe0@TVJOce@?4ee_c[WZ?4ZZS_WWc@'SS.<;K.7 4H7#K##@ 4##.KK?.@K@eeOZOZ[[#*7HK@@ ??<<;;  @ 4*E@ @ 4 @ 4 g@**/]3/++9/9/+2/3/93/3/9999993/33//3/???9/9/+]99+99993/3/9/+99/+910]'#656767&'&'&'&#"#"'&'&&5476326765'4674&'276#"'&#"'676323270;"%P&jruF #T!  /)-$ )NEUKIf-/C9,Kw:v2U)02-c   & d2!54F[~F(?A%4# LA[7<+#,'9.=+>#5NFreuJ^x!Bk,+$+/6'f$  ! $  @ >UH44ֳ4)ֳ 4(@ 4[+"@% 4+* 4*J 4)T 4(@ 4?SU@?4UUOSKGJ?4JJCOGGS@CC @ 4  0@ 4>03'&&>@ UU?J??JKK  4@ 4 7- 0033-77W&&/3/2/3/]99/+]+3992/33//3/?3/??9/+9/9/+92/3/9/+99/+910+++++]+++++&'&#"#"'&'&5432#"'&54&5!5$767654&'76733#"'&#"'67632327'8nzF:I,;)HtT3=`KY n!$)02-c   & d2!540}4#,:NX?[eW[n6ct& Q;^{bYT1"sc~6$ ! $  & DNn@Q@, 4TVJ<;K.7 4H7#K##@ 4##.K?K. 4..T?OdfVlTT4Tllf^@ 4^K@ dVaaiOOiTTZ@@ii#*7HK@@ ??<<;;  @ 4*E@ @ 4 @ 4 p@**;/]3/++9/9/+2/3/93/3/9999993/2/3/9/99??+2/3+/999?3/+9/9/+]99+999910]+'#656767&'&'&'&#"#"'&'&&5476326765'4674&'27647&'&547632&#"3260;"%P&jruF #T!  /)-$ )NEUKIf-/C9,Kw:v2U4*d5;- $+]!F[~F(?A%4# LA[7<+#,'9.=+>#5NFreuJ^x!Bk,+$+/6'f$  @.#8> 40  >^RA 4)ֳ 4(@ 4[+"@/ 4+* 4*J 4)T 4(@ 4  @ 4 @ 4> >03'&&>4@ 4DT?VF\DD4D\\VN@ 4N@ TFQQY??YDDJYY  4@ 4 7- 0033-77`&&;/3/2/3/]99/+]+3992/2/3/9/99?+2/3+/9993/++?3/?9/9/+9/+]910+++++]]+++&'&#"#"'&'&5432#"'&54&5!5$767654&'7673347&'&547632&#"326'8nzF:I,;)HtT3=`KY n!$=*d5;- $+]!0}4#,:NX?[eW[n6ct& Q;^{bYT1"sc~6$ @.#8> 4SDNn@Q@ 4TVJdOfVlT^@ffl@4ll@TTT@* 4TT<;K.7 4H7#K##@ 4##.KK?.K@ dVaaiOOiTTZ@@ii#*7HK@@ ??<<;;  @ 4*E@ @ 4 @ 4 p@**;/]3/++9/9/+2/3/93/3/9999993/2/3/9/99???9/9/+]99+99993/+]3/+3/99910]+'#656767&'&'&'&#"#"'&'&&5476326765'4674&'27647&'&547632&#"3260;"%P&jruF #T!  /)-$ )NEUKIf-/C9,Kw:v2U*d5;- $+]!F[~F(?A%4# LA[7<+#,'9.=+>#5NFreuJ^x!Bk,+$+/6'f$ P @.#8> 4A >^_A 444ֳ4)ֳ 4(@ 4[+"@ 4+* 4*J 4)T 4(@ 4T?VF\DN@ VV\@4\\D4D@ 4DD& @ 4  0@ 4>03'&&>@ TFQQY??YDDJYY-&  4@ 4 7- 0033-77`&&;/3/2/3/]99/+]+3999/2/3/9/99?3/??9/+9/9/+93/++3/+3/99910+++++]++++++&'&#"#"'&'&5432#"'&54&5!5$767654&'7673347&'&547632&#"326'8nzF:I,;)HtT3=`KY n!$*d5;- $+]!0}4#,:NX?[eW[n6ct& Q;^{bYT1"sc~6$ @.#8> 4SDN@)TVJ<;K.7 4H7#K##@ 4##.KK?.@K@ #*7HK@@ ??<<;;  @ 4*E@ @ 4 @ 4 P@**/]3/++9/9/+2/3/93/3/999999???9/9/+]99+999910]'#656767&'&'&'&#"#"'&'&&5476326765'4674&'2760;"%P&jruF #T!  /)-$ )NEUKIf-/C9,Kw:v2UF[~F(?A%4# LA[7<+#,'9.=+>#5NFreuJ^x!Bk,+$+/6'f$ A >44ֳ4)ֳ 4(@ 4[+"@- 4+* 4*J 4)T 4(@ 4 @ 4  0@ 4>03'&&>  4@ 4 7- 0033-77@&&/3/2/3/]99/+]+399?3/??9/+9/9/+910+++++]+++++&'&#"#"'&'&5432#"'&54&5!5$767654&'76733'8nzF:I,;)HtT3=`KY n!$0}4#,:NX?[eW[n6ct& Q;^{bYT1"sc~6$!!%!! @ !!%!! @ Fb1F=!!%!! @ !!%!! @ !!%!! @ FQ F!!%!! @ !!%!! @ HFZ!!%!! @ !!%!! @ !!%!! @ FIFWFKF=H@.J0wG!!%!! @ FF[!!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ >@ 4 /3/9/?2/+?10#4'&'&54632#"&54632+9 %F79IH42HH42HCv~:~-=]\72HH23JJ"  /?10#"&54632O76LM58N6ON76ONW *  /3/?2/10#"&54632#"&54632N85LJ78NO75LK68N8NN87NN6ON76ON!!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ !!%!! @ %F/?10!5!FF%%F/?10!5!FF%%:/?10!5!::%c, 4'@4' 4 4 4 4@" 4(  % (,,&&@ 4& !!c-]/]+3/9/99???9/910+++++++3276'&#"%53#"$576!2676'+u߃&4Ŀ;_I[%rIѥ|B[Lly[h18-VD@,y@Y' 4 4 4@6 4)  % (,,&$ $@$%4!?!!1-]+/]3/9/99???9/910++++]327654'&#"%53#"'&763266'YTSYZTSYB&1v䅉ۋiD;lffmʿkfelѥ|BVHA'vm*ZU%Z4 4 @ 4&!@ %%&& &4]&+/<3/9/????9/910]++53# &53327636765$c24gs8$"}V[œFѥ> ~PRO]YcafNl*v&$u 4@,4 4    $$& 3%%@364 N%]q+/<3/9//????9/9910+++53#5#"&&'&533266536765\$\|]P #S:?Uѥ>GnO6r/T9a*vE+, 4S@`/]}?10+#'3/+, 4S@`/]}?10+#'3 #S 4@ 4//@ 4S@`p/]]/+]qr+10+#3 T+A44 4SO`/]+}?10++3#+@+84 4SO`/]+}?10+3#^+s #T4@ 4//@ 4SO`/]/+]qr+10+]3##),@ 4 @4 4%4@ 794@SZ4 @   W@ 4p/]+3/93/}?}++++]qr+9910+7676'&#"'6#55*[? 'i{NVp^ ^'*Ce2p m,q@* 4 @@|4@RU4@KL4@<>4@&64 @4  /3/93/+}?q+++++9/9910+7676'&#"'6#51'T< $b]  AZ?7,@ 4 @4 4%4@ 794@SZ4 @   W@ 4p/]+3/93/}?}++++]qr+9910+7676'&#"'6#55*[? 'i{NVp^ ^'*K]2pgm,q@* 4 @@|4@RU4@KL4@<>4@&64 @4  /3/93/+}?q+++++9/9910+7676'&#"'6#51'T< $b]  AZ?7I;0_ 4 @7 4 4  EE   @ 4?  v v@o/]3//]+qr3/]9/]2/10+++&6323273#"'&#"q[>k;#= ?gCNIf~6W8$_|+ٳK @ 4 4@@ @^5 @OS4 @CE4 @+-4o    @a  / O _  _ o   @5 @jl4 @a5 @\]4 @WY4 @MQ4 @DI4 @:5 @144 @.B4 @',4 @%4 4  /3/}/+++++++++++++]qr3/]q++++3/10++]&6323273#"'&#"q[>k;D= a?gCCN|R[,F,Lr s 4 @ 4 4  E@4!&4E / ? /  @ 4 @6>4?  v v@o/]3//]++qr3/]9/+]2/10+++&6323273#"'&#"sq[>k;#= ?gCN f~6W8$_f= 44 @#(4 /3/?q+10++&'3327673#"'&N;@AC@;OIMp#v#%H$&9O9@4 4/++9?r910#73#ЯC{{(@k;#= ?gCNIf~6W8$_I0k 4 @A 4 4  EE O  @ 4? O  v v@P/]]q3//]+qr3/]9/]2/10+++&6323273#"'&#"q[>k;#= ?gCNIf~6W8$_IM0k 4 @A 4 4  EE O  @ 4? O  v v@P/]]q3//]+qr3/]9/]2/10+++&6323273#"'&#"q[>k;#= ?gCNIf~6W8$_ri@2 4 4 4EE    @ 4 v v4 4o/]++3//+]r3/]9/]2/10+++&763232673#"'&#"s:9Y>k;# "mT?gC"h>>6#4rr8$/Hi@2 4 4 4EE    @ 4 v v4 4o/]++3//+]r3/]9/]2/10+++&763232673#"'&#":9Y>k;# "mT?gC"h>>6#4rr8$/ji@2 4 4 4EE    @ 4 v v4 4o/]++3//+]r3/]9/]2/10+++&763232673#"'&#"k:9Y>k;# "mT?gC"h>>6#4rr8$/}}J}{}}e} b#S 4@ 4//@ 4S@`p/]]/+]qr+10+#3  #T4@ 4//@ 4SO`/]/+]qr+10+]3##`i@2 4 4 4EE    @ 4 v v4 4o/]++3//+]r3/]9/]2/10+++&763232673#"'&#":9Y>k;# "mT?gC"h>>6#4rr8$/}_,}]vC}t}}l9}jX%}V]}},}*|+nK @ 4 4@@ @^5 @OS4 @CE4 @+-4o       /3/}?3/]q++++3/10++]46323273#"'&#"p[>k;D= a?gC@Q|R[,F,LI0k 4 @A 4 4  EE O  @ 4? O  v v@P/]]q3//]+qr3/]9/]2/10+++&6323273#"'&#"q[>k;#= ?gCNIf~6W8$_|4+nK @ 4 4@@ @^5 @OS4 @CE4 @+-4o       /3/}?3/]q++++3/10++]&6323273#"'&#"q[>k;D= a?gCCN|R[,F,LI40k 4 @A 4 4  EE O  @ 4? O  v v@P/]]q3//]+qr3/]9/]2/10+++&6323273#"'&#"q[>k;#= ?gCNIf~6W8$_<&C@5Op  % /]??10]rq33&Y&$}4 5<44A+++5J>&D}@ 99:/7A+]5Y,&$t@ ##"A+]5J &D4&@LpLLPL`LLLLLLʱH++]qr5Y+&$'|q0@*4H+A@(4/+5+5++]q5J#&D'KZ_BB@=4B;H+: :0:p::::::::@.24:=A>/>>_>>@ 4>/+]qr5++]qr5++]5Y+&$'|n'@H+A@(4/+5+5+]q5J#&D'-Y@E?@&34?@4?<H+: :0:p::::::::@.24:=A>/>>_>>@ 4>/+]qr5++]qr5+++5Y,&$'|u1)@4)))(AA@(4/+5+5+]q+5J,&D'tHb@ SOSSS@>4SR;/>>_>>@ 4>/+]qr5++]qr5++]q5Y+&$'|<0@"@ 4@4 AA@(4/+5+5+]++5J&D'ztT@A?O??I:=A: :0:p::::::::@.24:=A>/>>_>>@ 4>/+]qr5++]qr5+]5Yh&$'|}455<44@AA@ (4/+5+5+++5J&D'}C@0@@A/7A: :0:p::::::::@.24:=A>")+++]qr5+]5Y+&$'{q4#AB4#@95##H+A @-4 /+5+5+q++5J#&D'H7@ `HpHH[HH@4HCH+<<h+<")++]5++]q5Y+&$'{n?@#@894#@)14#@ 4@#o####@H+A @-4 /+5+5+q+++5J#&D'\*IPIIC@H+<<h+<")++]5+]5Y,&$'{u;@ 6666*246@!(465AA @-4 /+5+5+++r5J,&D't\B@0PZ`ZZZZZ0ZpZZZZZZZYA<<h+<")++]5+]qr5Y+&$'{P,@###/##-AA @-4 /+5+5+]q5J&D'z+F@ 4FP?9A<<h+<")++]5++5Yf&$'{}45$5<4$4@$%AA @ -4 /+5+5+++5J&D'}&@GGH/7A<<h+<")++]5+]5&(}\@    A+]5K>&H}P`ش A+]5,&(t A+5K &H*@2222221 A+]q5&(|j@  A !)++5K&H@ * A)++5+&('|q0@*4H+A@(4/+5+5++]q5K#&H'LK_((@/4(!H+ @;5 @-24  # A /  _  @ 4 /+]qr5+r++5++]5+&('|n4@%@:5H+A@(4/+5+5+]qr+5K#&H'.J@7%@&34%@4%"H+ @;5 @-24  # A /  _  @ 4 /+]qr5+r++5+++5,&('|u1&@ 4&&&%AA@(4/+5+5+]q+5K,&H'tHQ@ O9999@0498!"A @;5 @-24  # A /  _  @ 4 /+]qr5+r++5++]5+&('|P$@AA@(4/+5+5+q5K&H'ztE@3%O%%/ #A @;5 @-24  # A /  _  @ 4 /+]qr5+r++5+]5h&('|}\$@  AA@ (4/+5+5+]5K&H'}9P&`&@&'A @;5 @-24  # A$")++r++5+]5c,&,t: A+5 &@ O"H++q5&,} A+5|I&L}z@ @mo4O  A+q+5c&2}  A+5D'>&R}  A+5c,&2t8@p00000/A+]q5D' &R*@....-A+]q5c+&2'|qn0&&&@*4&H+!A@(4/+5+5++]q5D'#&R'MD_$$@)4$H+@.24A/_@ 4/+]qr5+r+5++]5c+&2'|n@4@%#@:5###### H+!A@(4/+5+5+]qr+5D'#&R'/C@1!@&34!@4!H+@.24A/_@ 4/+]qr5+r+5+++5c,&2'|u`16@ 46665!A!A@(4/+5+5+]q+5D',&R'tHL@ O55555@*454A@.24A/_@ 4/+]qr5+r+5++]5c+&2'| @#-!A!A@(4/+5+5+5D'&R'zt>@-!O!!+A@.24A/_@ 4/+]qr5+r+5+]5ch&2'|} @$% A!A@ (4/+5+5+5D'&R'})@"# A@.24A ")++r+5+5c,&jj@0o000%H+-!)++]q5D&k!@0O0_000%1H+-")++]q5c,&jCj @ ...%H+-!)++]q5D&kC!@_.o. .0..%H+-")++]q5cE&jt8@PAAAAAA@%%A+]q5D &k*@AAAAA@%%A+]q5c&jQ@ -9%%A-!)++5D&k@ -9%%A-")++5c&j}@ ../A+q5D@&k} ./A+5"&8}p@ OA+q5&&X}@P`p A+]5",&8t@ % A+]5 &X2@P------P-`-p-----@ 4-, A++]q5,&lj)'@95p'/'_'''H+&!)++]r+5&m@O(((E4@.34@$)4/@4@4@ 4/+]+]+q+++++]<++</<<10!5!#53#53u4 4@% 4@ 4?@ 4 @#%4@4 @9 4@4@Os4@>E4@.:4@$%4/@4@4@ 4/+]+]+q+r+++++<++</+]+++9910#7#53#53炇燇44  0444@#%4@ 4 @144 d4 @#%4@ 4@4@ 4? @ 4 @#%4@4@+,4 @: 4@4@Os4@>E4@.:4@$%4/@4@4@ 4/+]+]+q+r++++<+9+<++</+]++++++++99++910#53'#'37#53QȮؓɱbb4 @ @ 4 @ 4@ 4? @#%4@4 @9 4@4@Os4@>E4@.:4@$%4/@4@4@ 4/+]+]+q+r+++++<++</]+++9910#53'#'3#53`ؓU 4@ ]  ??9/9/10!!!##53!Uo¤ e & 4@% N ++  ??9/9/10!3###53!Q||ci[F@817$G$E )9$&& ) )   EDEEDBDE1// ?=??=?=+6 E+ &/1$,76*?= *DF,B) *EF ??9/99999999/99+}99+}99+}99+}10]]]]]##&'&##"7#67&'&'&&56323276767632"&#"3[E].Z|`IBj .d:$6?\WN e])>M$DǖF%L>']_ 3 g9 36#:dki.ZR2-n')T3R<(M~O*;G&3S+'P&B@y#h$&&%)%)  0//%;9;;9;9,5%BA%A?A??>,A, % 9;5+0$&/,4*+*?A,>)+ * BA+ ??9/999999/99+}99+}99+}99+}10]]##&'&##"#67&'&'&&#"532332767633'&3P"0"5IJ4 1ovZ-808 (j)9.)91cd0q%:vB1L '%&NuomI-IQ 10SI)L3 K bB5A'=)c_$2$i'@V% &$&&$g$&$#'  7   ')  ](   $&#  & ??9/999999999]+}9]+}10]]]]##&'&##3276767632"&#"3E\,ZwF%J>']p 4 g9 37"9eki.\R~R+;G'1S)'v&&@O%%#%%#F#"#%&% % &&( %  N'+  #%" + %+ ??9/9999999]]99+}9]+}10##&'&##327676767633'&3v/#5Id0:(,:+_$2K '%&Muom>-IQ 1&5A` I( d^%2$+@f&&$& IYi$$* **) -  ],&$ ! *)       ??99//99/999999]]9/29+}99]]+}]10!#&'#&##3273676767632"&#":/x3EG1x&/76NHYp 4 g9 37"9e_<R~An*X,(G'1S)'&(@pi#!#%!!!!!'%''& * % 0  N)#! + '&  +     ??99//99/999999]rr9/29+}99]]r+}10]!#&'#&##3273676767633'&c#+-!c(,:+_$2K ')")6jpI7&5 D+9` I( dh(0i8@     ]  ??9/10##!#3!33†iMZW&>@" %% % N + +??9/]10##!#3!33W t-)&Fnm W@1    o   &6??9/]9/]+}ć+}10#367m$ "09rHUE9ji& S@1  % %%  $4Dt /?]?9/݇+}ć+}10#367nm.1&i&wmgmh@6          ??9/9/39Ƈ+}9+}910!!#!5!367mkUdaUj"099_UE9ji&i@?& F v &Fv % %  %   + /??9/Ƈ+}ć+}10]]!!#!5!367nBCm.1&ڄ&wmg iI@ViDtI                      /??9/͇+}+}10]]]]##&'#36733ID'47  T"-G'}i $>Vw=I^M&@Q    %%%_o    + /??9/]333]+}+}10###367733I* #.$g-](@74A bW`@5dEU6  ]    ??99//3332229/]9/10]]]!###"'&'&53733673¢xt,(±y xO< >FyocG>E&e@9te% %O_+ ??99//333229/]]]9/10]]!##5#"'&'&533673ndcY^d$! ?,;cW{" 7;bYkt+T/! )8@g[J  ]   ??9/3210]]]!#4'&#"#3$328Gċs,'\s[7a>Ezm&F@+zh% 0 %  N +  ??9/32]]10]]]!#54'&'&#"#3632A:Zfo\c%!x(S0+6&T46:dYkc!@V m \  J bUDw9Ivj   &\#&& c"/   ??99//]3]10]]]]]]]]]]]]]]]! '&5!&'&# '67632!32u u|S8ȟRGL T^~2pkbcU(> |@T8HUey\lJ ( 8 l[cU $"$$ ? O  4!+   ??99//]3]210]]]]]]]]]]]#"'&547!&'&#"'!2!3276({w LVN]v 8VSO `mWChXT`#w@PY""VuTFVy\IYx Y&\%&  c$   ??9/]10]]]]]]]]]]]]]]]]!"'&'&576! &'&#"!3276ڹϧOJMEuאvwۆ~gjTۃxϋD'>!@Y\ l ScdU7G[kH9iXf fUZj$7#$?O4"+  ??9/]]10]]]]]]]]]]]]]]#"'&57632&'&#"!3276'u{놀BYYBjITSH"A'vJeeJano`'(  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !.nullnonmarkingreturnmu1pi1OhmEurodmacron overscoremiddotAbreveabreveAogonekaogonekDcarondcaronDslashEogonekeogonekEcaronecaronLacutelacuteLcaronlcaronLdotldotNacutenacuteNcaronncaron Odblacute odblacuteRacuteracuteRcaronrcaronSacutesacuteTcedillatcedillaTcarontcaronUringuring Udblacute udblacuteZacutezacuteZdotzdotGammaThetaPhialphadeltaepsilonsigmatauphi underscoredbl exclamdbl nsuperiorpeseta arrowleftarrowup arrowright arrowdown arrowboth arrowupdn arrowupdnbse orthogonal intersection equivalencehouse revlogicalnot integraltp integralbtSF100000SF110000SF010000SF030000SF020000SF040000SF080000SF090000SF060000SF070000SF050000SF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000upblockdnblockblocklfblockrtblockltshadeshadedkshade filledbox filledrecttriaguptriagrttriagdntriaglfcircle invbullet invcircle smileface invsmilefacesunfemalemalespadeclubheartdiamond musicalnotemusicalnotedblIJij napostropheminutesecond afii61248 afii61289H22073H18543H18551H18533 openbulletAmacronamacron Ccircumflex ccircumflexCdotcdotEmacronemacronEbreveebreveEdotedot Gcircumflex gcircumflexGdotgdotGcedillagcedilla Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonek Jcircumflex jcircumflexKcedillakcedilla kgreenlandicLcedillalcedillaNcedillancedillaEngengOmacronomacronObreveobreveRcedillarcedilla Scircumflex scircumflexTbartbarUtildeutildeUmacronumacronUbreveubreveUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute anoteleiaWgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave quotereversed radicalex afii08941 estimated oneeighth threeeighths fiveeighths seveneighths commaaccentundercommaaccenttonos dieresistonos Alphatonos EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaDeltaEpsilonZetaEtaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosbetagammazetaetathetaiotakappalambdanuxiomicronrhosigma1upsilonchipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098 afii00208 afii61352sheva hatafsegol hatafpatah hatafqamatshiriqtseresegolpatahqamatsholamqubutsdageshmetegmaqafrafepaseqshindotsindotsofpasuqalefbetgimeldalethevavzayinhettetyodfinalkafkaflamedfinalmemmemfinalnunnunsamekhayinfinalpepe finaltsaditsadiqofreshshintav doublevavvavyod doubleyodgeresh gershayim newsheqelsign vavshindot finalkafshevafinalkafqamats lamedholamlamedholamdageshaltayin shinshindot shinsindotshindageshshindotshindageshsindot alefpatah alefqamats alefmapiq betdagesh gimeldagesh daletdageshhedagesh vavdagesh zayindagesh tetdagesh yoddageshfinalkafdagesh kafdagesh lameddagesh memdagesh nundagesh samekhdagesh finalpedageshpedagesh tsadidagesh qofdagesh reshdagesh shindageshtavdagesvavholambetrafekafrafeperafe aleflamedzerowidthnonjoinerzerowidthjoinerlefttorightmarkrighttoleftmark afii57388 afii57403 afii57407 afii57409 afii57440 afii57451 afii57452 afii57453 afii57454 afii57455 afii57456 afii57457 afii57458 afii57392 afii57393 afii57394 afii57395 afii57396 afii57397 afii57398 afii57399 afii57400 afii57401 afii57381 afii57461 afii63167 afii57459 afii57543 afii57534 afii57494 afii62843 afii62844 afii62845 afii64240 afii64241 afii63954 afii57382 afii64242 afii62881 afii57504 afii57369 afii57370 afii57371 afii57372 afii57373 afii57374 afii57375 afii57391 afii57471 afii57460 afii52258 afii57506 afii62958 afii62956 afii52957 afii57505 afii62889 afii62887 afii62888 afii57507 afii62961 afii62959 afii62960 afii57508 afii62962 afii57567 afii62964 afii52305 afii52306 afii57509 afii62967 afii62965 afii62966 afii57555 afii52364 afii63753 afii63754 afii63759 afii63763 afii63795 afii62891 afii63808 afii62938 afii63810 afii62942 afii62947 afii63813 afii63823 afii63824 afii63833 afii63844 afii62882 afii62883 afii62884 afii62885 afii62886 afii63846 afii63849uni202Auni202Buni202Duni202Euni202Cuni206Euni206F;uni206Auni206Buni206C;uni206DuniF00AuniF00BuniF00CuniF00DuniF00EuniFFFC afii63904 afii63905 afii63906 afii63908 afii63910 afii63912 afii62927 afii63941 afii62939 afii63943 afii62943 afii62946 afii63946 afii62951 afii63948 afii62953 afii63950 afii63951 afii63952 afii63953 afii63956 afii63958 afii63959 afii63960 afii63961 afii64046 afii64058 afii64059 afii64060 afii64061 afii62945 afii64184 afii52399 afii52400 afii62753 afii57411 afii62754 afii57412 afii62755 afii57413 afii62756 afii57414 afii62759 afii62757 afii62758 afii57415 afii62760 afii57416 afii62763 afii62761 afii62762 afii57417 afii62764 afii57418 afii62767 afii62765 afii62766 afii57419 afii62770 afii62768 afii62769 afii57420 afii62773 afii62771 afii62772 afii57421 afii62776 afii62774 afii62775 afii57422 afii62779 afii62777 afii62778 afii57423 afii62780 afii57424 afii62781 afii57425 afii62782 afii57426 afii62783 afii57427 afii62786 afii62784 afii62785 afii57428 afii62789 afii62787 afii62788 afii57429 afii62792 afii62790 afii62791 afii57430 afii62795 afii62793 afii62794 afii57431 afii62798 afii62796 afii62797 afii57432 afii62801 afii62799 afii62800 afii57433 afii62804 afii62802 afii62803 afii57434 afii62807 afii62805 afii62806 afii57441 afii62810 afii62808 afii62809 afii57442 afii62813 afii62811 afii62812 afii57443 afii62816 afii57410 afii62815 afii57444 afii62819 afii62817 afii62818 afii57445 afii62822 afii62820 afii62821 afii57446 afii62825 afii62823 afii62824 afii57447 afii62828 afii57470 afii62827 afii57448 afii62829 afii57449 afii62830 afii57450 afii62833 afii62831 afii62832 afii62834 afii62835 afii62836 afii62837 afii62838 afii62839 afii62840 afii62841 glyph1021 afii57543-2 afii57454-2 afii57451-2 glyph1025 glyph1026 afii57471-2 afii57458-2 afii57457-2 afii57494-2 afii57459-2 afii57455-2 afii57452-2 glyph1034 glyph1035 glyph1036 afii62884-2 afii62881-2 afii62886-2 afii62883-2 afii62885-2 afii62882-2 afii57504-2 afii57456-2 afii57453-2 glyph1046 glyph1047 afii57543-3 afii57454-3 afii57451-3 glyph1051 glyph1052 afii57471-3 afii57458-3 afii57457-3 afii57494-3 afii57459-3 afii57455-3 afii57452-3 glyph1060 glyph1061 glyph1062 afii62884-3 afii62881-3 afii62886-3 afii62883-3 afii62885-3 afii62882-3 afii57504-3 afii57456-3 afii57453-3 glyph1072 glyph1073 afii57543-4 afii57454-4 afii57451-4 glyph1077 glyph1078 afii57471-4 afii57458-4 afii57457-4 afii57494-4 afii57459-4 afii57455-4 afii57452-4 glyph1086 glyph1087 glyph1088 afii62884-4 afii62881-4 afii62886-4 afii62883-4 afii62885-4 afii62882-4 afii57504-4 afii57456-4 afii57453-4 glyph1098 glyph1099 glyph1100 glyph1101 glyph1102 glyph1103 glyph1104 glyph1105 glyph1106 glyph1107 glyph1108 glyph1109 glyph1110 glyph1111 glyph1112 glyph1113 glyph1114 glyph1115 glyph1116 glyph1117 glyph1118 glyph1119 glyph1120 glyph1121 glyph1122 glyph1123 glyph1124 glyph1125 glyph1126 afii57440-2 afii57440-3 afii57440-4OhornohornUhornuhornf006f007f009combininghookabovef010f013f011f01cf015combiningtildeaccentf02cdongsignonethird twothirdsf008f00ff012f014f016f017f018f019f01af01bf01ef01ff020f021f022combininggraveaccentcombiningacuteaccentf01dcombiningdotbelowf023f029f02af02bf024f025f026f027f028f02df02ef02ff030f031 Adotbelow adotbelow Ahookabove ahookaboveAcircumflexacuteacircumflexacuteAcircumflexgraveacircumflexgraveAcircumflexhookaboveacircumflexhookaboveAcircumflextildeacircumflextildeAcircumflexdotbelowacircumflexdotbelow Abreveacute abreveacute Abrevegrave abrevegraveAbrevehookaboveabrevehookabove Abrevetilde abrevetildeAbrevedotbelowabrevedotbelow Edotbelow edotbelow Ehookabove ehookaboveEtildeetildeEcircumflexacuteecircumflexacuteEcircumflexgraveecircumflexgraveEcircumflexhookaboveecircumflexhookaboveEcircumflextildeecircumflextildeEcircumflexdotbelowecircumflexdotbelow Ihookabove ihookabove Idotbelow idotbelow Odotbelow odotbelow Ohookabove ohookaboveOcircumflexacuteocircumflexacuteOcircumflexgraveocircumflexgraveOcircumflexhookaboveocircumflexhookaboveOcircumflextildeocircumflextildeOcircumflexdotbelowocircumflexdotbelow Ohornacute ohornacute Ohorngrave ohorngraveOhornhookaboveohornhookabove Ohorntilde ohorntilde Ohorndotbelow ohorndotbelow Udotbelow udotbelow Uhookabove uhookabove Uhornacute uhornacute Uhorngrave uhorngraveUhornhookaboveuhornhookabove Uhorntilde uhorntilde Uhorndotbelow uhorndotbelow Ydotbelow ydotbelow Yhookabove yhookaboveYtildeytildeuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni0492uni0493uni0496uni0497uni049auni049buni049cuni049duni04a2uni04a3uni04aeuni04afuni04b0uni04b1uni04b2uni04b3uni04b8uni04b9uni04bauni04bbuni018funi0259uni04e8uni04e9\ N$7<  h$$7h$9h$:$D:A:E-:H:I:K:L`:M:N:Q`:W:Z:l:m:n:w-;:;A;H;K;L;M;N;O;Q;T;W;Y;e;m<:<><@<A<E<H<K<Lw<M<N<O<Q<T<Y<^<f<l<m<o<q<y===w=w==:w=>w=A=E=F=H=K=Y=Z=\=^w=_w=b=e=f=g=hw=j=mw=u=v=x=yw>M>N>Q>aD>h>m-?A?k@A@H@K@L@M-@T-@Z@_@h@mAEAHAKALAMANAQAYDADHDKDMDNENE[EmFNFQFZF_FhFkFmFqFwH:H>H@HEHMHOHQHYH^HeHoJ}J}JJJJ:wJ>wJ@JAJEJFJHJKJLJMJNJOJYJZJ^`J_JhJwJyK:K>KAKEKFKHKLKMKOKQKTKWKZK`-KqLLLLLL:L>L@LALELHLNLYLZL\L_LbLdLeLfLh`LiLjLkLmLoLsLuLvLxLyMMMMMMMM:wM>MAMEMHMNMWMYM[M\M]M^`M_wM`MaMbMcMdMewMfMgMhwMiMjMkwMoMpMrMsMxMywN:N>NENLNMNQNYNeOAOHOKONOWOhOmPHPZ-SZSm-TwTYV`V:V>V@VAVEVFVHVKVL3VOVQ`VWVYW>W@WAWEWOWYW^W`WeWfWyX:X>X@XEXHXKXLXOXQX^XeXfZaZlZmZq[Z[^[_[`[a[e[f[k[m[n[o[q[t[w[y\Z\[\^\_\`\a\e\f\h\k\l\m\n\q\t\y]]]Z]^]_]a]e]h]k]y^t^w_[_^_`_a_e_l_m_o_q`[`m`q`t-a[a^a_aaaeahakamanaqatdZ-d[-d_dadedhdkdldmdweheqf[fafmh^h`hahehlhmhohqj^jajejljmjojqjyk`khkqkwlllZl^l_l`Dlelhlklmm3m3mmZm[m^m_m`memfmhmjmkmnmwmyn[n^nenlnmnqnyoZo[o_oaohokolonoqp_paphpks_shsmvl`vqww^w_wawewhwlwowyx^x`xexfxlxoxq33``.13 "&+M"9&@M Arial ?ARLR00@   "'6GK|}  LNgi $Xarabinitmedi fina&liga, >>!)/3'-15>"*04'-15`- (,.26-'+-15 X 6  & : &,GHIJKarab ghi0 *H |0x10 *H 0` +7R0P0, +7<<<Obsolete>>>0 0 *H :.[nxL~0@0Ǐ7ے(<g0  *H 0a10UInternet10U VeriSign, Inc.1301U *VeriSign Commercial Software Publishers CA0 960409000000Z 040107235959Z0a10UInternet10U VeriSign, Inc.1301U *VeriSign Commercial Software Publishers CA00  *H 0ieRT(bTUDEJ;~Ȁ k)vsb<ulMԘisbN1 }GQod5}gwQ>wCʣA="HH0  *H ujdxç2ur&`0LH4RJQS-{1eAA/czszAЎ:84Duqā85J>2!8\8dT_݈)Oqd1<<00M,3{TT0  *H 010U VeriSign Trust Network10U VeriSign, Inc.1,0*U #VeriSign Time Stamping Service Root1402U +NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc.0 991116000000Z 040106235959Z010U VeriSign, Inc.10U VeriSign Trust Network1F0DU =www.verisign.com/repository/RPA Incorp. by Ref.,LIAB.LTD(c)981.0,U%VeriSign Time Stamping Service CA SW10"0  *H 0 Ԙgm*2,/O_rϩEA/@˒-Mb/3@հmՆO_I޷ Ne E# );7FдX#`Rv p#aܲpb Hrɇ7 S,)H2&,4PNJJ0b+GqS EQ22w0򹅒RנI93才`YVB%00U% 0 +0OU H0F0D `HE0503+'https://www.verisign.com/repository/RPA0U00 U0  *H |C!{XyKl?!^5˓QC-,qǵ1%$sLGu ;맕O`fcHd(r_XiU(42P[0<U 5Digital ID Class 3 - Microsoft Software Validation v21 0 UUS10U Washington10URedmond10UMicrosoft Corporation10U Microsoft Corporation00  *H 0I&8bURDF3aѲg@ 6@YQmQt+n]n: ;Q]NZ0V0 U00 U0U0{Ch8n; c0a10UInternet10U VeriSign, Inc.1301U *VeriSign Commercial Software Publishers CAǏ7ے(<g0!U000  +70 U 0@06 +7 #0)'https://www.verisign.com/repository/CPSThis certificate incorporates by reference, and its use is strictly subject to, the VeriSign Certification Practice Statement (CPS) version 1.0, available in the VeriSign repository at: https://www.verisign.com; by E-mail at CPS-requests@verisign.com; or by mail at VeriSign, Inc., 2593 Coast Ave., Mountain View, CA 94043 USA Copyright (c)1996 VeriSign, Inc. All Rights Reserved. CERTAIN WARRANTIES DISCLAIMED AND LIABILITY LIMITED. WARNING: THE USE OF THIS CERTIFICATE IS STRICTLY SUBJECT TO THE VERISIGN CERTIFICATION PRACTICE STATEMENT. THE ISSUING AUTHORITY DISCLAIMS CERTAIN IMPLIED AND EXPRESS WARRANTIES, INCLUDING WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, AND WILL NOT BE LIABLE FOR CONSEQUENTIAL, PUNITIVE, AND CERTAIN OTHER DAMAGES. SEE THE CPS FOR DETAILS. Contents of the VeriSign registered nonverifiedSubjectAttributes extension value shall not be considered as accurate information validated by the IA. 64https://www.verisign.com/repository/verisignlogo.gif0U000  `HE0This certificate incorporates by reference, and its use is strictly subject to, the VeriSign Certification Practice Statement (CPS), available at: https://www.verisign.com/CPS; by E-mail at CPS-requests@verisign.com; or by mail at VeriSign, Inc., 2593 Coast Ave., Mountain View, CA 94043 USA Tel. +1 (415) 961-8830 Copyright (c) 1996 VeriSign, Inc. All Rights Reserved. CERTAIN WARRANTIES DISCLAIMED and LIABILITY LIMITED. `HE `HE0,0*(https://www.verisign.com/repository/CPS 0 +700  *H A s#piafH)1ئnǨ{ x3eXKZnffD]dXՍN6?+2ojQm<LOJuϼB!w B]1,0(0u0a10UInternet10U VeriSign, Inc.1301U *VeriSign Commercial Software Publishers CAumRKe\0 *H 0 *H  1  +70 +7 10  +70 *H  15ܷ `Q0^ +7 1P0N&$Arial Regular Font$"http://www.microsoft.com/truetype/0  *H ';'&5p޷2x$E9ǖ9cEp:?SQ/D-Ke]fj=Yrȍ\l4P a36C,ݍtY=}9{␭">:bdjM0I *H  1:060010U VeriSign Trust Network10U VeriSign, Inc.1,0*U #VeriSign Time Stamping Service Root1402U +NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc.,3{TT0 *H Y0 *H  1  *H 0 *H  1 000511184855Z0 *H  1e~Im;ߜ2!0  *H 3 ?މ|ogif*pԧDh !<&*Nn 'ciŹ|\ ״Wyсς25n-)|vʡ,ÅaJ-scXs4^ZZLa0㾧vS[_P%Z"Q= (B).begin(); --A) namespace tvg { template struct Array { T* data = nullptr; uint32_t count = 0; uint32_t reserved = 0; Array() = default; Array(int32_t size) { reserve(size); } Array(const Array& rhs) { reset(); *this = rhs; } void push(T element) { if (count + 1 > reserved) { reserved = count + (count + 2) / 2; data = tvg::realloc(data, sizeof(T) * reserved); } data[count++] = element; } void push(const Array& rhs) { if (rhs.count == 0) return; grow(rhs.count); memcpy(data + count, rhs.data, rhs.count * sizeof(T)); count += rhs.count; } bool reserve(uint32_t size) { if (size > reserved) { reserved = size; data = tvg::realloc(data, sizeof(T) * reserved); } return true; } bool grow(uint32_t size) { return reserve(count + size); } const T& operator[](size_t idx) const { return data[idx]; } T& operator[](size_t idx) { return data[idx]; } void operator=(const Array& rhs) { reserve(rhs.count); if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * rhs.count); count = rhs.count; } void move(Array& to) { to.reset(); to.data = data; to.count = count; to.reserved = reserved; data = nullptr; count = reserved = 0; } const T* begin() const { return data; } T* begin() { return data; } T* end() { return data + count; } const T* end() const { return data + count; } const T& last() const { return data[count - 1]; } const T& first() const { return data[0]; } T& last() { return data[count - 1]; } T& next() { if (full()) grow(count + 1); return data[count++]; } T& first() { return data[0]; } void pop() { if (count > 0) --count; } void reset() { tvg::free(data); data = nullptr; count = reserved = 0; } void clear() { count = 0; } bool empty() const { return count == 0; } bool full() { return count == reserved; } ~Array() { tvg::free(data); } }; } #endif //_TVG_ARRAY_H_ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-scanner.h000664 001750 001750 00000027327 15164251010 043456 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JS_SCANNER_H #define JS_SCANNER_H /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_scanner Scanner * @{ */ /** * Allowed types for scanner_info_t structures. */ typedef enum { SCANNER_TYPE_END, /**< mark the last info block */ SCANNER_TYPE_END_ARGUMENTS, /**< mark the end of function arguments * (only present if a function script is parsed) */ SCANNER_TYPE_FUNCTION, /**< declarations in a function */ SCANNER_TYPE_BLOCK, /**< declarations in a code block (usually enclosed in {}) */ SCANNER_TYPE_WHILE, /**< while statement */ SCANNER_TYPE_FOR, /**< for statement */ SCANNER_TYPE_FOR_IN, /**< for-in statement */ SCANNER_TYPE_FOR_OF, /**< for-of statement */ SCANNER_TYPE_SWITCH, /**< switch statement */ SCANNER_TYPE_CASE, /**< case statement */ SCANNER_TYPE_INITIALIZER, /**< destructuring binding or assignment pattern with initializer */ SCANNER_TYPE_LITERAL_FLAGS, /**< object or array literal with non-zero flags (stored in u8_arg) */ SCANNER_TYPE_CLASS_CONSTRUCTOR, /**< class constructor */ SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END, /**< class field initializer end */ SCANNER_TYPE_CLASS_STATIC_BLOCK_END, /**< class static block end */ SCANNER_TYPE_LET_EXPRESSION, /**< let expression */ SCANNER_TYPE_ERR_REDECLARED, /**< syntax error: a variable is redeclared */ SCANNER_TYPE_ERR_ASYNC_FUNCTION, /**< an invalid async function follows */ SCANNER_TYPE_EXPORT_MODULE_SPECIFIER, /**< export with module specifier */ } scanner_info_type_t; /** * Source code location which can be used to change the position of parsing. */ typedef struct { const uint8_t *source_p; /**< next source byte */ parser_line_counter_t line; /**< token start line */ parser_line_counter_t column; /**< token start column */ } scanner_location_t; /** * Source code range with its start and end position. */ typedef struct { const uint8_t *source_end_p; /**< end position */ scanner_location_t start_location; /**< start location */ } scanner_range_t; /** * Scanner info blocks which provides information for the parser. */ typedef struct scanner_info_t { struct scanner_info_t *next_p; /**< next info structure */ const uint8_t *source_p; /**< triggering position of this scanner info */ uint8_t type; /**< type of the scanner info */ uint8_t u8_arg; /**< custom 8-bit value */ uint16_t u16_arg; /**< custom 16-bit value */ } scanner_info_t; /** * Scanner info for class private field */ typedef struct scanner_class_private_member_t { lexer_lit_location_t loc; /**< loc */ uint8_t u8_arg; /**< custom 8-bit value */ struct scanner_class_private_member_t *prev_p; /**< prev private field */ } scanner_class_private_member_t; /** * Scanner info extended with class private fields. */ typedef struct { scanner_info_t info; /**< header */ scanner_class_private_member_t *members; /**< first private field */ } scanner_class_info_t; /** * Scanner info extended with a location. */ typedef struct { scanner_info_t info; /**< header */ scanner_location_t location; /**< location */ } scanner_location_info_t; /** * Scanner info for "for" statements. */ typedef struct { scanner_info_t info; /**< header */ scanner_location_t expression_location; /**< location of expression start */ scanner_location_t end_location; /**< location of expression end */ } scanner_for_info_t; /** * Case statement list for scanner_switch_info_t structure. */ typedef struct scanner_case_info_t { struct scanner_case_info_t *next_p; /**< next case statement info */ scanner_location_t location; /**< location of case statement */ } scanner_case_info_t; /** * Scanner info for "switch" statements. */ typedef struct { scanner_info_t info; /**< header */ scanner_case_info_t *case_p; /**< list of switch cases */ } scanner_switch_info_t; /* * Description of compressed streams. * * The stream is a sequence of commands which encoded as bytes. The first byte * contains the type of the command (see scanner_function_compressed_stream_types_t). * * The variable declaration commands has two arguments: * - The first represents the length of the declared identifier * - The second contains the relative distance from the end of the previous declaration * Usually the distance is between 1 and 255, and represented as a single byte * Distances between -256 and 65535 are encoded as two bytes * Larger distances are encoded as pointers */ /** * Constants for compressed streams. */ typedef enum { SCANNER_STREAM_UINT16_DIFF = (1 << 7), /**< relative distance is between -256 and 65535 */ SCANNER_STREAM_HAS_ESCAPE = (1 << 6), /**< binding has escape */ SCANNER_STREAM_NO_REG = (1 << 5), /**< binding cannot be stored in register */ SCANNER_STREAM_EARLY_CREATE = (1 << 4), /**< binding must be created with ECMA_VALUE_UNINITIALIZED */ SCANNER_STREAM_LOCAL_ARGUMENTS = SCANNER_STREAM_EARLY_CREATE, /**< arguments is redeclared * as let/const binding later */ /* Update SCANNER_STREAM_TYPE_MASK macro if more bits are added. */ } scanner_compressed_stream_flags_t; /** * Types for compressed streams. */ typedef enum { SCANNER_STREAM_TYPE_END, /**< end of scanner data */ SCANNER_STREAM_TYPE_HOLE, /**< no name is assigned to this argument */ SCANNER_STREAM_TYPE_ARGUMENTS, /**< arguments object should be created */ SCANNER_STREAM_TYPE_ARGUMENTS_FUNC, /**< arguments object should be created which * is later initialized with a function */ SCANNER_STREAM_TYPE_VAR, /**< var declaration */ SCANNER_STREAM_TYPE_LET, /**< let declaration */ SCANNER_STREAM_TYPE_CONST, /**< const declaration */ SCANNER_STREAM_TYPE_LOCAL, /**< local declaration (e.g. catch block) */ #if JERRY_MODULE_SYSTEM SCANNER_STREAM_TYPE_IMPORT, /**< module import */ #endif /* JERRY_MODULE_SYSTEM */ /* The next four types must be in this order (see SCANNER_STREAM_TYPE_IS_ARG). */ SCANNER_STREAM_TYPE_ARG, /**< argument declaration */ SCANNER_STREAM_TYPE_ARG_VAR, /**< argument declaration which is later copied * into a variable declared by var statement */ SCANNER_STREAM_TYPE_DESTRUCTURED_ARG, /**< destructuring argument declaration */ SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR, /**< destructuring argument declaration which is later * copied into a variable declared by var statement */ /* Function types should be at the end. See the SCANNER_STREAM_TYPE_IS_FUNCTION macro. */ SCANNER_STREAM_TYPE_ARG_FUNC, /**< argument declaration which * is later initialized with a function */ SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC, /**< destructuring argument declaration which * is later initialized with a function */ SCANNER_STREAM_TYPE_FUNC, /**< function declaration */ } scanner_compressed_stream_types_t; /** * Mask for decoding the type from the compressed stream. */ #define SCANNER_STREAM_TYPE_MASK 0xf /** * Checks whether the decoded type represents a function declaration. */ #define SCANNER_STREAM_TYPE_IS_FUNCTION(type) ((type) >= SCANNER_STREAM_TYPE_ARG_FUNC) /** * Checks whether the decoded type represents a function argument. */ #define SCANNER_STREAM_TYPE_IS_ARG(type) \ ((type) >= SCANNER_STREAM_TYPE_ARG && (type) <= SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR) /** * Checks whether the decoded type represents both a function argument and a function declaration. */ #define SCANNER_STREAM_TYPE_IS_ARG_FUNC(type) \ ((type) == SCANNER_STREAM_TYPE_ARG_FUNC || (type) == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC) /** * Checks whether the decoded type represents an arguments declaration */ #define SCANNER_STREAM_TYPE_IS_ARGUMENTS(type) \ ((type) == SCANNER_STREAM_TYPE_ARGUMENTS || (type) == SCANNER_STREAM_TYPE_ARGUMENTS_FUNC) /** * Constants for u8_arg flags in scanner_function_info_t. */ typedef enum { SCANNER_FUNCTION_ARGUMENTS_NEEDED = (1 << 0), /**< arguments object needs to be created */ SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT = (1 << 1), /**< function has complex (ES2015+) argument definition */ SCANNER_FUNCTION_LEXICAL_ENV_NEEDED = (1 << 2), /**< lexical environment is needed for the function body */ SCANNER_FUNCTION_STATEMENT = (1 << 3), /**< function is function statement (not arrow expression) * this flag must be combined with the type of function (e.g. async) */ SCANNER_FUNCTION_ASYNC = (1 << 4), /**< function is async function */ SCANNER_FUNCTION_IS_STRICT = (1 << 5), /**< function is strict */ } scanner_function_flags_t; /** * Constants for u8_arg flags in scanner_class_info_t. */ typedef enum { SCANNER_CONSTRUCTOR_IMPLICIT = 0, /**< implicit constructor */ SCANNER_CONSTRUCTOR_EXPLICIT = (1 << 0), /**< explicit constructor */ SCANNER_SUCCESSFUL_CLASS_SCAN = (1 << 1), /**< class scan was successful */ SCANNER_PRIVATE_FIELD_ACTIVE = (1 << 2), /**< private field is active */ } scanner_constuctor_flags_t; /** * Constants for u8_arg flags in scanner_class_private_member_t. */ typedef enum { SCANNER_PRIVATE_FIELD_PROPERTY = (1 << 0), /**< private field initializer */ SCANNER_PRIVATE_FIELD_METHOD = (1 << 1), /**< private field method */ SCANNER_PRIVATE_FIELD_STATIC = (1 << 2), /**< static private property */ SCANNER_PRIVATE_FIELD_GETTER = (1 << 3), /**< private field getter */ SCANNER_PRIVATE_FIELD_SETTER = (1 << 4), /**< private field setter */ SCANNER_PRIVATE_FIELD_SEEN = (1 << 5), /**< private field has already been seen */ SCANNER_PRIVATE_FIELD_IGNORED = SCANNER_PRIVATE_FIELD_METHOD | SCANNER_PRIVATE_FIELD_STATIC, SCANNER_PRIVATE_FIELD_GETTER_SETTER = (SCANNER_PRIVATE_FIELD_GETTER | SCANNER_PRIVATE_FIELD_SETTER), SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER = (SCANNER_PRIVATE_FIELD_PROPERTY | SCANNER_PRIVATE_FIELD_GETTER_SETTER), } scanner_private_field_flags_t; /** * Object or array literal constants for u8_arg flags in scanner_info_t. */ typedef enum { /* These flags affects both array and object literals */ SCANNER_LITERAL_DESTRUCTURING_FOR = (1 << 0), /**< for loop with destructuring pattern */ SCANNER_LITERAL_NO_DESTRUCTURING = (1 << 1), /**< this literal cannot be a destructuring pattern */ /* These flags affects only object literals */ SCANNER_LITERAL_OBJECT_HAS_SUPER = (1 << 2), /**< super keyword is used in the object literal */ SCANNER_LITERAL_OBJECT_HAS_REST = (1 << 3), /**< the object literal has a member prefixed with three dots */ } scanner_literal_flags_t; /** * Option bits for scanner_create_variables function. */ typedef enum { SCANNER_CREATE_VARS_NO_OPTS = 0, /**< no options */ SCANNER_CREATE_VARS_IS_SCRIPT = (1 << 0), /**< create variables for script or direct eval */ SCANNER_CREATE_VARS_IS_MODULE = (1 << 1), /**< create variables for module */ SCANNER_CREATE_VARS_IS_FUNCTION_ARGS = (1 << 2), /**< create variables for function arguments */ SCANNER_CREATE_VARS_IS_FUNCTION_BODY = (1 << 3), /**< create variables for function body */ } scanner_create_variables_flags_t; /** * @} * @} * @} */ #endif /* !JS_SCANNER_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlProgram.h000664 001750 001750 00000004335 15164251010 036253 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_PROGRAM_H_ #define _TVG_GL_PROGRAM_H_ #include "tvgGlShader.h" class GlProgram { public: GlProgram(const char* vertSrc, const char* fragSrc); ~GlProgram(); void load(); static void unload(); int32_t getAttributeLocation(const char* name); int32_t getUniformLocation(const char* name); int32_t getUniformBlockIndex(const char* name); uint32_t getProgramId(); void setUniform1Value(int32_t location, int count, const int* values); void setUniform2Value(int32_t location, int count, const int* values); void setUniform3Value(int32_t location, int count, const int* values); void setUniform4Value(int32_t location, int count, const int* values); void setUniform1Value(int32_t location, int count, const float* values); void setUniform2Value(int32_t location, int count, const float* values); void setUniform3Value(int32_t location, int count, const float* values); void setUniform4Value(int32_t location, int count, const float* values); private: uint32_t mProgramObj; static uint32_t mCurrentProgram; }; #endif /* _TVG_GL_PROGRAM_H_ */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array.inc.h000664 001750 001750 00000003332 15164251010 047730 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Array description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ARRAY /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.4.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_ARRAY_PROTOTYPE, ECMA_PROPERTY_FIXED) /* Number properties: * (property name, object pointer getter) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_ARRAY_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_IS_ARRAY_UL, ECMA_ARRAY_ROUTINE_IS_ARRAY, 1, 1) ROUTINE (LIT_MAGIC_STRING_FROM, ECMA_ARRAY_ROUTINE_FROM, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_OF, ECMA_ARRAY_ROUTINE_OF, NON_FIXED, 0) /* ECMA-262 v6, 22.1.2.5 */ ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_SPECIES, ECMA_ARRAY_ROUTINE_SPECIES_GET, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* !(JERRY_BUILTIN_ARRAY) */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-iterator-object.h000664 001750 001750 00000006153 15164251010 046565 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_ITERATOR_OBJECT_H #define ECMA_ITERATOR_OBJECT_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaiteratorobject ECMA iterator object related routines * @{ */ /** * Generator resume execution flags. */ typedef enum { ECMA_ITERATOR_NEXT, /**< generator should continue its execution */ ECMA_ITERATOR_THROW, /**< generator should perform a throw operation */ ECMA_ITERATOR_RETURN, /**< generator should perform a return operation */ } ecma_iterator_command_type_t; /** * Maximum value of [[%Iterator%NextIndex]] until it can be stored * in an ecma pseudo array object structure element. */ #define ECMA_ITERATOR_INDEX_LIMIT UINT16_MAX ecma_value_t ecma_op_create_iterator_object (ecma_value_t iterated_value, ecma_object_t *prototype_obj_p, ecma_object_class_type_t iterator_type, ecma_iterator_kind_t kind); ecma_value_t ecma_create_iter_result_object (ecma_value_t value, ecma_value_t done); ecma_value_t ecma_create_array_from_iter_element (ecma_value_t value, ecma_value_t index_value); ecma_value_t ecma_op_get_iterator (ecma_value_t value, ecma_value_t method, ecma_value_t *next_method_p); ecma_value_t ecma_op_iterator_complete (ecma_value_t iter_result); ecma_value_t ecma_op_iterator_value (ecma_value_t iter_result); ecma_value_t ecma_op_iterator_next (ecma_value_t iterator, ecma_value_t next_method, ecma_value_t value); ecma_value_t ecma_op_iterator_close (ecma_value_t iterator); ecma_value_t ecma_op_iterator_step (ecma_value_t iterator, ecma_value_t next_method); ecma_value_t ecma_op_iterator_do (ecma_iterator_command_type_t command, ecma_value_t iterator, ecma_value_t next_method, ecma_value_t value, bool *done_p); ecma_value_t ecma_op_create_async_from_sync_iterator (ecma_value_t sync_iterator, ecma_value_t sync_next_method, ecma_value_t *async_next_method_p); ecma_value_t ecma_async_from_sync_iterator_unwrap_cb (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); /** * @} * @} */ #endif /* !ECMA_ITERATOR_OBJECT_H */ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/vm-stack.h000664 001750 001750 00000010710 15164251010 041636 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 VM_STACK_H #define VM_STACK_H #include "ecma-globals.h" #include "vm-defines.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup stack VM stack * @{ */ /** * Create context on the vm stack. */ #define VM_CREATE_CONTEXT(type, end_offset) ((ecma_value_t) ((type) | ((end_offset) << 7))) /** * Create context on the vm stack with environment. */ #define VM_CREATE_CONTEXT_WITH_ENV(type, end_offset) (VM_CREATE_CONTEXT ((type), (end_offset)) | VM_CONTEXT_HAS_LEX_ENV) /** * Get type of a vm context. */ #define VM_GET_CONTEXT_TYPE(value) ((vm_stack_context_type_t) ((value) &0x1f)) /** * Get the end position of a vm context. */ #define VM_GET_CONTEXT_END(value) ((value) >> 7) /** * This flag is set if the context has a lexical environment. */ #define VM_CONTEXT_HAS_LEX_ENV 0x20 /** * This flag is set if the iterator close operation should be invoked during a for-of context break. */ #define VM_CONTEXT_CLOSE_ITERATOR 0x40 /** * Context types for the vm stack. */ typedef enum { /* Update VM_CONTEXT_IS_FINALLY macro if the following three values are changed. */ VM_CONTEXT_FINALLY_JUMP, /**< finally context with a jump */ VM_CONTEXT_FINALLY_THROW, /**< finally context with a throw */ VM_CONTEXT_FINALLY_RETURN, /**< finally context with a return */ VM_CONTEXT_TRY, /**< try context */ VM_CONTEXT_CATCH, /**< catch context */ VM_CONTEXT_BLOCK, /**< block context */ VM_CONTEXT_WITH, /**< with context */ VM_CONTEXT_FOR_IN, /**< for-in context */ VM_CONTEXT_FOR_OF, /**< for-of context */ VM_CONTEXT_FOR_AWAIT_OF, /**< for-await-of context */ /* contexts with variable length */ VM_CONTEXT_ITERATOR, /**< iterator context */ VM_CONTEXT_OBJ_INIT, /**< object-initializer context */ VM_CONTEXT_OBJ_INIT_REST, /**< object-initializer-rest context */ } vm_stack_context_type_t; /** * Return types for vm_stack_find_finally. */ typedef enum { VM_CONTEXT_FOUND_FINALLY, /**< found finally */ VM_CONTEXT_FOUND_ERROR, /**< found an error */ VM_CONTEXT_FOUND_AWAIT, /**< found an await operation */ VM_CONTEXT_FOUND_EXPECTED, /**< found the type specified in finally_type */ } vm_stack_found_type; /** * Checks whether the context has variable context size * * Layout: * - [context descriptor] * - [JS values belong to the context] * - [previous JS values stored by the VM stack] */ #define VM_CONTEXT_IS_VARIABLE_LENGTH(context_type) ((context_type) >= VM_CONTEXT_ITERATOR) /** * Checks whether the context type is a finally type. */ #define VM_CONTEXT_IS_FINALLY(context_type) ((context_type) <= VM_CONTEXT_FINALLY_RETURN) /** * Shift needs to be applied to get the next item of the offset array. */ #define VM_CONTEXT_OFFSET_SHIFT 4 /** * Checks whether an offset is available. */ #define VM_CONTEXT_HAS_NEXT_OFFSET(offsets) ((offsets) >= (1 << VM_CONTEXT_OFFSET_SHIFT)) /** * Get the next offset from the offset array. */ #define VM_CONTEXT_GET_NEXT_OFFSET(offsets) (-((int32_t) ((offsets) & ((1 << VM_CONTEXT_OFFSET_SHIFT) - 1)))) ecma_value_t *vm_stack_context_abort_variable_length (vm_frame_ctx_t *frame_ctx_p, ecma_value_t *vm_stack_top_p, uint32_t context_stack_allocation); ecma_value_t *vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, ecma_value_t *vm_stack_top_p); vm_stack_found_type vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, ecma_value_t *stack_top_p, vm_stack_context_type_t finally_type, uint32_t search_limit); uint32_t vm_get_context_value_offsets (ecma_value_t *context_item_p); void vm_ref_lex_env_chain (ecma_object_t *lex_env_p, uint16_t context_depth, ecma_value_t *context_end_p, bool do_ref); /** * @} * @} */ #endif /* !VM_STACK_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/build_windows.yml000664 001750 001750 00000003107 15164251010 036130 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0name: Windows on: pull_request: branches: - main push: branches: - main permissions: contents: read jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v4 - uses: ilammy/msvc-dev-cmd@v1 - name: Install Packages run: | pip install meson ninja - name: Build run: | meson setup build -Dlog=true -Dloaders=all -Dsavers=all -Dbindings=capi -Dtools=all where link ninja -C build install - uses: actions/upload-artifact@v4 with: name: result_windows path: build/src/thorvg* compact_test: runs-on: windows-latest steps: - uses: actions/checkout@v4 - uses: ilammy/msvc-dev-cmd@v1 - name: Install Packages run: | pip install meson ninja - name: Build run: | meson setup build -Dlog=true -Dloaders=all -Dsavers=all -Dstatic=true -Dthreads=false where link ninja -C build install - uses: actions/upload-artifact@v4 with: name: result_windows_static path: build/src/thorvg* unit_test: runs-on: windows-latest steps: - uses: actions/checkout@v4 - uses: ilammy/msvc-dev-cmd@v1 - name: Install Packages run: | pip install meson ninja - name: Build run: | meson setup build -Dloaders=all -Dsavers=all -Dbindings=capi -Dtests=true --errorlogs where link ninja -C build install test - uses: actions/upload-artifact@v4 with: name: UnitTestReport path: build/meson-logs/testlog.txt src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/error/en.h000664 001750 001750 00000031341 15164251010 036555 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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."); } } //! Maps error code of validation into error message. /*! \ingroup RAPIDJSON_ERRORS \param validateErrorCode Error code obtained from validator. \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* GetValidateError_En(ValidateErrorCode validateErrorCode) { switch (validateErrorCode) { case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred"); case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error."); case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'."); case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'."); case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'."); case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'."); case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'."); case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'."); case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'."); case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression."); case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'."); case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'."); case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true."); case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema."); case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'."); case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'."); case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'."); case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema."); case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema."); case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors."); case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values."); case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'."); case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors."); case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'."); case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors."); case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing."); case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading."); default: return RAPIDJSON_ERROR_STRING("Unknown error."); } } //! Maps error code of schema document compilation into error message. /*! \ingroup RAPIDJSON_ERRORS \param schemaErrorCode Error code obtained from compiling the schema document. \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* GetSchemaError_En(SchemaErrorCode schemaErrorCode) { switch (schemaErrorCode) { case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error."); case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document."); case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer."); case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string."); case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'."); case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document."); case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical."); case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider."); case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema."); case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'."); case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized."); case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported."); case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document."); case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'."); default: return RAPIDJSON_ERROR_STRING("Unknown error."); } } //! Maps error code of pointer parse into error message. /*! \ingroup RAPIDJSON_ERRORS \param pointerParseErrorCode Error code obtained from pointer parse. \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* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) { switch (pointerParseErrorCode) { case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'."); case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape."); case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment."); case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment."); default: return RAPIDJSON_ERROR_STRING("Unknown error."); } } RAPIDJSON_NAMESPACE_END #ifdef __clang__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_ERROR_EN_H_ lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-float32array.inc.h000664 001750 001750 00000001717 15164251010 053314 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Float32Array description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 4 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_FLOAT32_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_FLOAT32ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieLoader.h000664 001750 001750 00000007542 15164251010 036147 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_LOADER_H_ #define _TVG_LOTTIE_LOADER_H_ #include "tvgCommon.h" #include "tvgInlist.h" #include "tvgFrameModule.h" #include "tvgTaskScheduler.h" struct LottieComposition; struct LottieBuilder; struct LottieProperty; struct LottieSlot; struct LottieCustomSlot { INLIST_ITEM(LottieCustomSlot); struct Pair { LottieProperty* prop; // New overriding property LottieSlot* target; // Overriding target }; Array props; // This has a list of the overriding target(LottieSlot) with a property to apply. uint32_t code; // Slot source-code key LottieCustomSlot(uint32_t code) : code(code) {} ~LottieCustomSlot(); }; class LottieLoader : public FrameModule, public Task { public: const char* content = nullptr; //lottie file data uint32_t size = 0; //lottie data size float frameNo = 0.0f; //current frame number float frameCnt = 0.0f; float frameRate = 0.0f; LottieBuilder* builder; LottieComposition* comp = nullptr; Inlist slots; //user custom slot list uint32_t curSlot = 0; //current applied slotcode Key key; char* dirName = nullptr; //base resource directory bool copy = false; //"content" is owned by this loader bool build = true; //require building the lottie scene LottieLoader(); ~LottieLoader(); bool open(const char* path) override; bool open(const char* data, uint32_t size, const char* rpath, bool copy) override; bool resize(Paint* paint, float w, float h) override; bool read() override; Paint* paint() override; //Slot Methods uint32_t gen(const char* slot, bool byDefault = false); bool apply(uint32_t slotcode, bool byDefault = false); bool del(uint32_t slotcode, bool byDefault = false); //Frame Controls bool frame(float no) override; float totalFrame() override; float curFrame() override; float duration() override; void sync() override; //Marker Supports uint32_t markersCnt(); const char* markers(uint32_t index); bool segment(const char* marker, float& begin, float& end); Result segment(float begin, float end) override; float shorten(float frameNo); //Reduce the accuracy for performance bool tween(float from, float to, float progress); bool assign(const char* layer, uint32_t ix, const char* var, float val); bool quality(uint8_t value); void set(const AssetResolver* resolver) override; private: bool ready(); bool header(); void clear(); float startFrame(); void run(unsigned tid) override; void release(); bool prepare(); }; #endif //_TVG_LOTTIELOADER_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/000775 001750 001750 00000000000 15164251010 027215 5ustar00ddennedyddennedy000000 000000 thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-arguments-object.h000664 001750 001750 00000004031 15164251010 046732 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_ARGUMENTS_OBJECT_H #define ECMA_ARGUMENTS_OBJECT_H #include "ecma-globals.h" #include "ecma-helpers.h" #include "vm-defines.h" ecma_value_t ecma_op_create_arguments_object (vm_frame_ctx_shared_args_t *shared_p, ecma_object_t *lex_env_p); ecma_value_t ecma_op_arguments_object_define_own_property (ecma_object_t *object_p, ecma_string_t *property_name_p, const ecma_property_descriptor_t *property_desc_p); ecma_property_t *ecma_op_arguments_object_try_to_lazy_instantiate_property (ecma_object_t *object_p, ecma_string_t *property_name_p); void ecma_op_arguments_delete_built_in_property (ecma_object_t *object_p, ecma_string_t *property_name_p); void ecma_op_arguments_object_list_lazy_property_names (ecma_object_t *obj_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p, jerry_property_filter_t filter); ecma_string_t *ecma_op_arguments_object_get_formal_parameter (ecma_mapped_arguments_t *mapped_arguments_p, uint32_t index); #endif /* !ECMA_ARGUMENTS_OBJECT_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-globals.h000664 001750 001750 00000011357 15164251010 042500 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 LIT_GLOBALS_H #define LIT_GLOBALS_H #include "jrt.h" /** * ECMAScript standard defines terms "code unit" and "character" as 16-bit unsigned value * used to represent 16-bit unit of text, this is the same as code unit in UTF-16 (See ECMA-262 5.1 Chapter 6). * * The term "code point" or "Unicode character" is used to refer a single Unicode scalar value (may be longer * than 16 bits: 0x0 - 0x10FFFFF). One code point could be represented with one ore two 16-bit code units. * * According to the standard all strings and source text are assumed to be a sequence of code units. * Length of a string equals to number of code units in the string, which is not the same as number of Unicode * characters in a string. * * Internally JerryScript engine uses UTF-8 representation of strings to reduce memory overhead. Unicode character * occupies from one to four bytes in UTF-8 representation. * * Unicode scalar value | Bytes in UTF-8 | Bytes in UTF-16 * | (internal representation) | * ---------------------------------------------------------------------- * 0x0 - 0x7F | 1 byte | 2 bytes * 0x80 - 0x7FF | 2 bytes | 2 bytes * 0x800 - 0xFFFF | 3 bytes | 2 bytes * 0x10000 - 0x10FFFF | 4 bytes | 4 bytes * * Scalar values from 0xD800 to 0xDFFF are permanently reserved by Unicode standard to encode high and low * surrogates in UTF-16 (Code points 0x10000 - 0x10FFFF are encoded via pair of surrogates in UTF-16). * Despite that the official Unicode standard says that no UTF forms can encode these code points, we allow * them to be encoded inside strings. The reason for that is compatibility with ECMA standard. * * For example, assume a string which consists one Unicode character: 0x1D700 (Mathematical Italic Small Epsilon). * It has the following representation in UTF-16: 0xD835 0xDF00. * * ECMA standard allows extracting a substring from this string: * > var str = String.fromCharCode (0xD835, 0xDF00); // Create a string containing one character: 0x1D700 * > str.length; // 2 * > var str1 = str.substring (0, 1); * > str1.length; // 1 * > str1.charCodeAt (0); // 55349 (this equals to 0xD835) * * Internally original string would be represented in UTF-8 as the following byte sequence: 0xF0 0x9D 0x9C 0x80. * After substring extraction high surrogate 0xD835 should be encoded via UTF-8: 0xED 0xA0 0xB5. * * Pair of low and high surrogates encoded separately should never occur in internal string representation, * it should be encoded as any code point and occupy 4 bytes. So, when constructing a string from two surrogates, * it should be processed gracefully; * > var str1 = String.fromCharCode (0xD835); // 0xED 0xA0 0xB5 - internal representation * > var str2 = String.fromCharCode (0xDF00); // 0xED 0xBC 0x80 - internal representation * > var str = str1 + str2; // 0xF0 0x9D 0x9C 0x80 - internal representation, * // !!! not 0xED 0xA0 0xB5 0xED 0xBC 0x80 */ /** * Description of an ecma-character, which represents 16-bit code unit, * which is equal to UTF-16 character (see Chapter 6 from ECMA-262 5.1) */ typedef uint16_t ecma_char_t; /** * Max bytes needed to represent a code unit (utf-16 char) via utf-8 encoding */ #define LIT_UTF8_MAX_BYTES_IN_CODE_UNIT (3) /** * Max bytes needed to represent a code point (Unicode character) via utf-8 encoding */ #define LIT_UTF8_MAX_BYTES_IN_CODE_POINT (4) /** * Max bytes needed to represent a code unit (utf-16 char) via cesu-8 encoding */ #define LIT_CESU8_MAX_BYTES_IN_CODE_UNIT (3) /** * Max bytes needed to represent a code point (Unicode character) via cesu-8 encoding */ #define LIT_CESU8_MAX_BYTES_IN_CODE_POINT (6) /** * A byte of utf-8 string */ typedef uint8_t lit_utf8_byte_t; /** * Size of a utf-8 string in bytes */ typedef uint32_t lit_utf8_size_t; /** * Size of a magic string in bytes */ typedef uint8_t lit_magic_size_t; /** * Unicode code point */ typedef uint32_t lit_code_point_t; /** * ECMA string hash */ typedef uint32_t lit_string_hash_t; #endif /* !LIT_GLOBALS_H */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-atomics-object.h000664 001750 001750 00000003426 15164251010 046373 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_ATOMICS_OBJECT_H #define ECMA_ATOMICS_OBJECT_H #include "ecma-globals.h" #if JERRY_BUILTIN_ATOMICS /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaatomicsobject ECMA builtin Atomics helper functions * @{ */ /** * Atomics flags. */ typedef enum { ECMA_ATOMICS_ADD, /**< Atomics add operation */ ECMA_ATOMICS_SUBTRACT, /**< Atomics subtract operation */ ECMA_ATOMICS_AND, /**< Atomics and operation */ ECMA_ATOMICS_OR, /**< Atomics or operation */ ECMA_ATOMICS_XOR, /**< Atomics xor operation */ ECMA_ATOMICS_EXCHANGE, /**< Atomics exchange operation */ ECMA_ATOMICS_COMPARE_EXCHANGE /**< Atomics compare exchange operation */ } ecma_atomics_op_t; ecma_value_t ecma_validate_shared_integer_typedarray (ecma_value_t typedarray, bool waitable); ecma_value_t ecma_validate_atomic_access (ecma_value_t typedarray, ecma_value_t request_index); ecma_value_t ecma_atomic_read_modify_write (ecma_value_t typedarray, ecma_value_t index, ecma_value_t value, ecma_atomics_op_t op); ecma_value_t ecma_atomic_load (ecma_value_t typedarray, ecma_value_t index); /** * @} * @} */ #endif /* JERRY_BUILTIN_ATOMICS */ #endif /* ECMA_ATOMICS_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgLoader.cpp000664 001750 001750 00000424066 15164251010 035363 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgStr.h" #include "tvgMath.h" #include "tvgColor.h" #include "tvgLoader.h" #include "tvgXmlParser.h" #include "tvgSvgLoader.h" #include "tvgSvgSceneBuilder.h" #include "tvgSvgCssStyle.h" #include "tvgSvgUtil.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ /* * According to: https://www.w3.org/TR/SVG2/coords.html#Units * and: https://www.w3.org/TR/css-values-4/#absolute-lengths */ #define PX_PER_IN 96 //1 in = 96 px #define PX_PER_PC 16 //1 pc = 1/6 in -> PX_PER_IN/6 #define PX_PER_PT 1.333333f //1 pt = 1/72 in -> PX_PER_IN/72 #define PX_PER_MM 3.779528f //1 in = 25.4 mm -> PX_PER_IN/25.4 #define PX_PER_CM 37.79528f //1 in = 2.54 cm -> PX_PER_IN/2.54 typedef bool (*parseAttributes)(const char* buf, unsigned bufLength, xmlAttributeCb func, const void* data); typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func); typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength); static bool _parseStyleAttr(void* data, const char* key, const char* value); static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style); static char* _copyId(const char* str) { if (!str) return nullptr; if (strlen(str) == 0) return nullptr; return duplicate(str); } static bool _parseNumber(const char** content, const char** end, float* number) { auto _end = end ? *end : nullptr; *number = toFloat(*content, (char**)&_end); //If the start of string is not number if ((*content) == _end) { if (end) *end = _end; return false; } //Skip comma if any *content = svgUtilSkipWhiteSpaceAndComma(_end); if (end) *end = _end; return true; } static constexpr struct { AspectRatioAlign align; const char* tag; } alignTags[] = { { AspectRatioAlign::XMinYMin, "xMinYMin" }, { AspectRatioAlign::XMidYMin, "xMidYMin" }, { AspectRatioAlign::XMaxYMin, "xMaxYMin" }, { AspectRatioAlign::XMinYMid, "xMinYMid" }, { AspectRatioAlign::XMidYMid, "xMidYMid" }, { AspectRatioAlign::XMaxYMid, "xMaxYMid" }, { AspectRatioAlign::XMinYMax, "xMinYMax" }, { AspectRatioAlign::XMidYMax, "xMidYMax" }, { AspectRatioAlign::XMaxYMax, "xMaxYMax" }, }; static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) { if (STR_AS(*content, "none")) { *align = AspectRatioAlign::None; return; } for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) { if (!strncmp(*content, alignTags[i].tag, 8)) { *align = alignTags[i].align; *content += 8; *content = svgUtilSkipWhiteSpace(*content, nullptr); break; } } if (STR_AS(*content, "meet")) { *meetOrSlice = AspectRatioMeetOrSlice::Meet; } else if (STR_AS(*content, "slice")) { *meetOrSlice = AspectRatioMeetOrSlice::Slice; } } // According to https://www.w3.org/TR/SVG/coords.html#Units static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type) { float parsedValue = toFloat(str, nullptr); if (strstr(str, "cm")) parsedValue *= PX_PER_CM; else if (strstr(str, "mm")) parsedValue *= PX_PER_MM; else if (strstr(str, "pt")) parsedValue *= PX_PER_PT; else if (strstr(str, "pc")) parsedValue *= PX_PER_PC; else if (strstr(str, "in")) parsedValue *= PX_PER_IN; else if (strstr(str, "%")) { if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h; else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w; else if (type == SvgParserLengthType::Diagonal) parsedValue = (sqrtf(powf(svgParse->global.w, 2) + powf(svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f); else //if other than it's radius { float max = svgParse->global.w; if (max < svgParse->global.h) max = svgParse->global.h; parsedValue = (parsedValue / 100.0f) * max; } } //TODO: Implement 'em', 'ex' attributes return parsedValue; } static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool& isPercentage) { char* end = nullptr; auto parsedValue = toFloat(str, &end); isPercentage = false; if (strstr(str, "%")) { parsedValue = parsedValue / 100.0f; isPercentage = true; } else if (strstr(str, "cm")) parsedValue *= PX_PER_CM; else if (strstr(str, "mm")) parsedValue *= PX_PER_MM; else if (strstr(str, "pt")) parsedValue *= PX_PER_PT; else if (strstr(str, "pc")) parsedValue *= PX_PER_PC; else if (strstr(str, "in")) parsedValue *= PX_PER_IN; //TODO: Implement 'em', 'ex' attributes return parsedValue; } static float _toOffset(const char* str) { char* end = nullptr; auto strEnd = str + strlen(str); auto parsedValue = toFloat(str, &end); end = (char*)svgUtilSkipWhiteSpace(end, nullptr); auto ptr = strstr(str, "%"); if (ptr) { parsedValue = parsedValue / 100.0f; if (end != ptr || (end + 1) != strEnd) return 0; } else if (end != strEnd) return 0; return parsedValue; } static int _toOpacity(const char* str) { char* end = nullptr; auto opacity = toFloat(str, &end); if (end) { if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f); else if (*end == '\0') return lrint(opacity * 255); } return 255; } static SvgMaskType _toMaskType(const char* str) { if (STR_AS(str, "Alpha")) return SvgMaskType::Alpha; return SvgMaskType::Luminance; } //The default rendering order: fill, stroke, markers //If any is omitted, will be rendered in its default order after the specified ones. static bool _toPaintOrder(const char* str) { auto position = 1; auto strokePosition = 0; auto fillPosition = 0; while (*str != '\0') { str = svgUtilSkipWhiteSpace(str, nullptr); if (!strncmp(str, "fill", 4)) { fillPosition = position++; str += 4; } else if (!strncmp(str, "stroke", 6)) { strokePosition = position++; str += 6; } else if (!strncmp(str, "markers", 7)) { str += 7; } else { return _toPaintOrder("fill stroke"); } } if (fillPosition == 0) fillPosition = position++; if (strokePosition == 0) strokePosition = position++; return fillPosition < strokePosition; } #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \ static Type _to##Name1(const char* str) \ { \ unsigned int i; \ \ for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \ if (STR_AS(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \ } \ return Default; \ } /* parse the line cap used during stroking a path. * Value: butt | round | square | inherit * Initial: butt * https://www.w3.org/TR/SVG/painting.html */ static constexpr struct { StrokeCap lineCap; const char* tag; } lineCapTags[] = { { StrokeCap::Butt, "butt" }, { StrokeCap::Round, "round" }, { StrokeCap::Square, "square" } }; _PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt) /* parse the line join used during stroking a path. * Value: miter | round | bevel | inherit * Initial: miter * https://www.w3.org/TR/SVG/painting.html */ static constexpr struct { StrokeJoin lineJoin; const char* tag; } lineJoinTags[] = { { StrokeJoin::Miter, "miter" }, { StrokeJoin::Round, "round" }, { StrokeJoin::Bevel, "bevel" } }; _PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter) /* parse the fill rule used during filling a path. * Value: nonzero | evenodd | inherit * Initial: nonzero * https://www.w3.org/TR/SVG/painting.html */ static constexpr struct { FillRule fillRule; const char* tag; } fillRuleTags[] = { { FillRule::EvenOdd, "evenodd" } }; _PARSE_TAG(FillRule, fillRule, FillRule, fillRuleTags, FillRule::NonZero) /* parse the dash pattern used during stroking a path. * Value: none | | inherit * Initial: none * https://www.w3.org/TR/SVG/painting.html */ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* dash) { if (!strncmp(str, "none", 4)) return; char *end = nullptr; while (*str) { str = svgUtilSkipWhiteSpaceAndComma(str); auto parsedValue = toFloat(str, &end); if (str == end) break; if (parsedValue < 0.0f) { dash->array.reset(); return; } if (*end == '%') { ++end; //Refers to the diagonal length of the viewport. //https://www.w3.org/TR/SVG2/coords.html#Units parsedValue = (sqrtf(powf(loader->svgParse->global.w, 2) + powf(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f); } dash->array.push(parsedValue); str = end; } } static char* _idFromUrl(const char* url) { auto open = strchr(url, '('); auto close = strchr(url, ')'); if (!open || !close || open >= close) return nullptr; open = strchr(url, '#'); if (!open || open >= close) return nullptr; ++open; --close; //trim the rest of the spaces and the quote marks if any while (open < close && (*close == ' ' || *close == '\'' || *close == '\"')) --close; //quick verification for (auto id = open; id < close; id++) { if (*id == ' ' || *id == '\'') return nullptr; } return duplicate(open, (close - open + 1)); } static size_t _srcFromUrl(const char* url, char*& src) { src = (char*)strchr(url, '('); auto close = strchr(url, ')'); if (!src || !close || src >= close) return 0; src = strchr(src, '\''); if (!src || src >= close) return 0; ++src; close = strchr(src, '\''); if (!close || close == src) return 0; --close; while (src < close && *src == ' ') ++src; while (src < close && *close == ' ') --close; return close - src + 1; } static unsigned char _parseColor(const char* value, char** end) { auto r = toFloat(value, end); *end = (char*)svgUtilSkipWhiteSpace(*end, nullptr); if (**end == '%') { r = 255 * r / 100; (*end)++; } *end = (char*)svgUtilSkipWhiteSpace(*end, nullptr); if (r < 0 || r > 255) { *end = nullptr; return 0; } return lrint(r); } static constexpr struct { const char* name; unsigned int value; } colors[] = { { "aliceblue", 0xfff0f8ff }, { "antiquewhite", 0xfffaebd7 }, { "aqua", 0xff00ffff }, { "aquamarine", 0xff7fffd4 }, { "azure", 0xfff0ffff }, { "beige", 0xfff5f5dc }, { "bisque", 0xffffe4c4 }, { "black", 0xff000000 }, { "blanchedalmond", 0xffffebcd }, { "blue", 0xff0000ff }, { "blueviolet", 0xff8a2be2 }, { "brown", 0xffa52a2a }, { "burlywood", 0xffdeb887 }, { "cadetblue", 0xff5f9ea0 }, { "chartreuse", 0xff7fff00 }, { "chocolate", 0xffd2691e }, { "coral", 0xffff7f50 }, { "cornflowerblue", 0xff6495ed }, { "cornsilk", 0xfffff8dc }, { "crimson", 0xffdc143c }, { "cyan", 0xff00ffff }, { "darkblue", 0xff00008b }, { "darkcyan", 0xff008b8b }, { "darkgoldenrod", 0xffb8860b }, { "darkgray", 0xffa9a9a9 }, { "darkgrey", 0xffa9a9a9 }, { "darkgreen", 0xff006400 }, { "darkkhaki", 0xffbdb76b }, { "darkmagenta", 0xff8b008b }, { "darkolivegreen", 0xff556b2f }, { "darkorange", 0xffff8c00 }, { "darkorchid", 0xff9932cc }, { "darkred", 0xff8b0000 }, { "darksalmon", 0xffe9967a }, { "darkseagreen", 0xff8fbc8f }, { "darkslateblue", 0xff483d8b }, { "darkslategray", 0xff2f4f4f }, { "darkslategrey", 0xff2f4f4f }, { "darkturquoise", 0xff00ced1 }, { "darkviolet", 0xff9400d3 }, { "deeppink", 0xffff1493 }, { "deepskyblue", 0xff00bfff }, { "dimgray", 0xff696969 }, { "dimgrey", 0xff696969 }, { "dodgerblue", 0xff1e90ff }, { "firebrick", 0xffb22222 }, { "floralwhite", 0xfffffaf0 }, { "forestgreen", 0xff228b22 }, { "fuchsia", 0xffff00ff }, { "gainsboro", 0xffdcdcdc }, { "ghostwhite", 0xfff8f8ff }, { "gold", 0xffffd700 }, { "goldenrod", 0xffdaa520 }, { "gray", 0xff808080 }, { "grey", 0xff808080 }, { "green", 0xff008000 }, { "greenyellow", 0xffadff2f }, { "honeydew", 0xfff0fff0 }, { "hotpink", 0xffff69b4 }, { "indianred", 0xffcd5c5c }, { "indigo", 0xff4b0082 }, { "ivory", 0xfffffff0 }, { "khaki", 0xfff0e68c }, { "lavender", 0xffe6e6fa }, { "lavenderblush", 0xfffff0f5 }, { "lawngreen", 0xff7cfc00 }, { "lemonchiffon", 0xfffffacd }, { "lightblue", 0xffadd8e6 }, { "lightcoral", 0xfff08080 }, { "lightcyan", 0xffe0ffff }, { "lightgoldenrodyellow", 0xfffafad2 }, { "lightgray", 0xffd3d3d3 }, { "lightgrey", 0xffd3d3d3 }, { "lightgreen", 0xff90ee90 }, { "lightpink", 0xffffb6c1 }, { "lightsalmon", 0xffffa07a }, { "lightseagreen", 0xff20b2aa }, { "lightskyblue", 0xff87cefa }, { "lightslategray", 0xff778899 }, { "lightslategrey", 0xff778899 }, { "lightsteelblue", 0xffb0c4de }, { "lightyellow", 0xffffffe0 }, { "lime", 0xff00ff00 }, { "limegreen", 0xff32cd32 }, { "linen", 0xfffaf0e6 }, { "magenta", 0xffff00ff }, { "maroon", 0xff800000 }, { "mediumaquamarine", 0xff66cdaa }, { "mediumblue", 0xff0000cd }, { "mediumorchid", 0xffba55d3 }, { "mediumpurple", 0xff9370d8 }, { "mediumseagreen", 0xff3cb371 }, { "mediumslateblue", 0xff7b68ee }, { "mediumspringgreen", 0xff00fa9a }, { "mediumturquoise", 0xff48d1cc }, { "mediumvioletred", 0xffc71585 }, { "midnightblue", 0xff191970 }, { "mintcream", 0xfff5fffa }, { "mistyrose", 0xffffe4e1 }, { "moccasin", 0xffffe4b5 }, { "navajowhite", 0xffffdead }, { "navy", 0xff000080 }, { "oldlace", 0xfffdf5e6 }, { "olive", 0xff808000 }, { "olivedrab", 0xff6b8e23 }, { "orange", 0xffffa500 }, { "orangered", 0xffff4500 }, { "orchid", 0xffda70d6 }, { "palegoldenrod", 0xffeee8aa }, { "palegreen", 0xff98fb98 }, { "paleturquoise", 0xffafeeee }, { "palevioletred", 0xffd87093 }, { "papayawhip", 0xffffefd5 }, { "peachpuff", 0xffffdab9 }, { "peru", 0xffcd853f }, { "pink", 0xffffc0cb }, { "plum", 0xffdda0dd }, { "powderblue", 0xffb0e0e6 }, { "purple", 0xff800080 }, { "red", 0xffff0000 }, { "rosybrown", 0xffbc8f8f }, { "royalblue", 0xff4169e1 }, { "saddlebrown", 0xff8b4513 }, { "salmon", 0xfffa8072 }, { "sandybrown", 0xfff4a460 }, { "seagreen", 0xff2e8b57 }, { "seashell", 0xfffff5ee }, { "sienna", 0xffa0522d }, { "silver", 0xffc0c0c0 }, { "skyblue", 0xff87ceeb }, { "slateblue", 0xff6a5acd }, { "slategray", 0xff708090 }, { "slategrey", 0xff708090 }, { "snow", 0xfffffafa }, { "springgreen", 0xff00ff7f }, { "steelblue", 0xff4682b4 }, { "tan", 0xffd2b48c }, { "teal", 0xff008080 }, { "thistle", 0xffd8bfd8 }, { "tomato", 0xffff6347 }, { "turquoise", 0xff40e0d0 }, { "violet", 0xffee82ee }, { "wheat", 0xfff5deb3 }, { "white", 0xffffffff }, { "whitesmoke", 0xfff5f5f5 }, { "yellow", 0xffffff00 }, { "yellowgreen", 0xff9acd32 } }; static bool _toColor(const char* str, uint8_t& r, uint8_t&g, uint8_t& b, char** ref) { auto len = strlen(str); if (len == 4 && str[0] == '#') { //Case for "#456" should be interpreted as "#445566" if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) { char tmp[3] = { '\0', '\0', '\0' }; tmp[0] = str[1]; tmp[1] = str[1]; r = strtol(tmp, nullptr, 16); tmp[0] = str[2]; tmp[1] = str[2]; g = strtol(tmp, nullptr, 16); tmp[0] = str[3]; tmp[1] = str[3]; b = strtol(tmp, nullptr, 16); } return true; } else if (len == 7 && str[0] == '#') { if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) { char tmp[3] = { '\0', '\0', '\0' }; tmp[0] = str[1]; tmp[1] = str[2]; r = strtol(tmp, nullptr, 16); tmp[0] = str[3]; tmp[1] = str[4]; g = strtol(tmp, nullptr, 16); tmp[0] = str[5]; tmp[1] = str[6]; b = strtol(tmp, nullptr, 16); } return true; } else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') { char *red, *green, *blue; auto tr = _parseColor(str + 4, &red); if (red && *red == ',') { auto tg = _parseColor(red + 1, &green); if (green && *green == ',') { auto tb = _parseColor(green + 1, &blue); if (blue && blue[0] == ')' && blue[1] == '\0') { r = tr; g = tg; b = tb; } } } return true; } else if (ref && len >= 3 && !strncmp(str, "url", 3)) { tvg::free(*ref); *ref = _idFromUrl((const char*)(str + 3)); return true; } else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') { tvg::HSL hsl; const char* content = svgUtilSkipWhiteSpace(str + 4, nullptr); const char* hue = nullptr; if (_parseNumber(&content, &hue, &hsl.h) && hue) { const char* saturation = nullptr; hue = svgUtilSkipWhiteSpace(hue, nullptr); hue = (char*)svgUtilSkipWhiteSpaceAndComma(hue); hue = svgUtilSkipWhiteSpace(hue, nullptr); if (_parseNumber(&hue, &saturation, &hsl.s) && saturation && *saturation == '%') { const char* brightness = nullptr; hsl.s /= 100.0f; saturation = svgUtilSkipWhiteSpace(saturation + 1, nullptr); saturation = (char*)svgUtilSkipWhiteSpaceAndComma(saturation); saturation = svgUtilSkipWhiteSpace(saturation, nullptr); if (_parseNumber(&saturation, &brightness, &hsl.l) && brightness && *brightness == '%') { hsl.l /= 100.0f; brightness = svgUtilSkipWhiteSpace(brightness + 1, nullptr); if (brightness && brightness[0] == ')' && brightness[1] == '\0') { hsl2rgb(hsl.h, tvg::clamp(hsl.s, 0.0f, 1.0f), tvg::clamp(hsl.l, 0.0f, 1.0f), r, g, b); return true; } } } } } else { //Handle named color for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) { if (!strcasecmp(colors[i].name, str)) { r = ((uint8_t*)(&(colors[i].value)))[2]; g = ((uint8_t*)(&(colors[i].value)))[1]; b = ((uint8_t*)(&(colors[i].value)))[0]; return true; } } } return false; } static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len) { int count = 0; str = (char*)svgUtilSkipWhiteSpace(str, nullptr); while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) { char* end = nullptr; points[count++] = toFloat(str, &end); str = end; str = (char*)svgUtilSkipWhiteSpaceAndComma(str); //Eat the rest of space str = (char*)svgUtilSkipWhiteSpace(str, nullptr); } *ptCount = count; return str; } enum class MatrixState { Unknown, Matrix, Translate, Rotate, Scale, SkewX, SkewY }; #define MATRIX_DEF(Name, Value) \ { \ #Name, sizeof(#Name), Value \ } static constexpr struct { const char* tag; int sz; MatrixState state; } matrixTags[] = { MATRIX_DEF(matrix, MatrixState::Matrix), MATRIX_DEF(translate, MatrixState::Translate), MATRIX_DEF(rotate, MatrixState::Rotate), MATRIX_DEF(scale, MatrixState::Scale), MATRIX_DEF(skewX, MatrixState::SkewX), MATRIX_DEF(skewY, MatrixState::SkewY) }; /* parse transform attribute * https://www.w3.org/TR/SVG/coords.html#TransformAttribute */ static Matrix* _parseTransformationMatrix(const char* value) { const int POINT_CNT = 8; auto matrix = tvg::malloc(sizeof(Matrix)); tvg::identity(matrix); float points[POINT_CNT]; int ptCount = 0; auto str = (char*)value; auto end = str + strlen(str); while (str < end) { auto state = MatrixState::Unknown; if (isspace(*str) || (*str == ',')) { ++str; continue; } for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) { if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) { state = matrixTags[i].state; str += (matrixTags[i].sz - 1); break; } } if (state == MatrixState::Unknown) goto error; str = (char*)svgUtilSkipWhiteSpace(str, end); if (*str != '(') goto error; ++str; str = _parseNumbersArray(str, points, &ptCount, POINT_CNT); if (*str != ')') goto error; ++str; if (state == MatrixState::Matrix) { if (ptCount != 6) goto error; Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1}; *matrix *= tmp; } else if (state == MatrixState::Translate) { if (ptCount == 1) { Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1}; *matrix *= tmp; } else if (ptCount == 2) { Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1}; *matrix *= tmp; } else goto error; } else if (state == MatrixState::Rotate) { //Transform to signed. points[0] = fmodf(points[0], 360.0f); if (points[0] < 0) points[0] += 360.0f; auto c = cosf(deg2rad(points[0])); auto s = sinf(deg2rad(points[0])); if (ptCount == 1) { Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; *matrix *= tmp; } else if (ptCount == 3) { Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 }; *matrix *= tmp; tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; *matrix *= tmp; tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 }; *matrix *= tmp; } else { goto error; } } else if (state == MatrixState::Scale) { if (ptCount < 1 || ptCount > 2) goto error; auto sx = points[0]; auto sy = sx; if (ptCount == 2) sy = points[1]; Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 }; *matrix *= tmp; } else if (state == MatrixState::SkewX) { if (ptCount != 1) goto error; auto deg = tanf(deg2rad(points[0])); Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 }; *matrix *= tmp; } else if (state == MatrixState::SkewY) { if (ptCount != 1) goto error; auto deg = tanf(deg2rad(points[0])); Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 }; *matrix *= tmp; } } return matrix; error: tvg::free(matrix); return nullptr; } #define LENGTH_DEF(Name, Value) \ { \ #Name, sizeof(#Name), Value \ } static bool _attrParseSvgNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgDocNode* doc = &(node->node.doc); if (STR_AS(key, "width")) { doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) { doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent); } else { doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width); } } else if (STR_AS(key, "height")) { doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) { doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent); } else { doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height); } } else if (STR_AS(key, "viewBox")) { if (_parseNumber(&value, nullptr, &doc->vbox.x)) { if (_parseNumber(&value, nullptr, &doc->vbox.y)) { if (_parseNumber(&value, nullptr, &doc->vbox.w)) { if (_parseNumber(&value, nullptr, &doc->vbox.h)) { doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox); loader->svgParse->global.h = doc->vbox.h; } loader->svgParse->global.w = doc->vbox.w; } loader->svgParse->global.y = doc->vbox.y; } loader->svgParse->global.x = doc->vbox.x; } if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vbox.w < 0.0f || doc->vbox.h < 0.0f)) { doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox); TVGLOG("SVG", "Negative values of the width and/or height - the attribute invalidated."); } if (!(doc->viewFlag & SvgViewFlag::Viewbox)) { loader->svgParse->global.x = loader->svgParse->global.y = 0.0f; loader->svgParse->global.w = loader->svgParse->global.h = 1.0f; } } else if (STR_AS(key, "preserveAspectRatio")) { _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice); } else if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); #ifdef THORVG_LOG_ENABLED } else if ((STR_AS(key, "x") || STR_AS(key, "y")) && fabsf(toFloat(value, nullptr)) > FLOAT_EPSILON) { TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value); #endif } else { return _parseStyleAttr(loader, key, value, false); } return true; } //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint static void _handlePaintAttr(SvgPaint* paint, const char* value) { if (STR_AS(value, "none")) { //No paint property paint->none = true; return; } if (STR_AS(value, "currentColor")) { paint->curColor = true; paint->none = false; return; } if (_toColor(value, paint->color.r, paint->color.g, paint->color.b, &paint->url)) paint->none = false; } static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { auto style = node->style; if (_toColor(value, style->color.r, style->color.g, style->color.b, nullptr)) { style->curColorSet = true; } } static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { auto style = node->style; style->fill.flags = (style->fill.flags | SvgFillFlags::Paint); _handlePaintAttr(&style->fill.paint, value); } static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { auto style = node->style; style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint); _handlePaintAttr(&style->stroke.paint, value); } static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity); node->style->stroke.opacity = _toOpacity(value); } static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash); _parseDashArray(loader, value, &node->style->stroke.dash); } static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset); node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); } static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width); node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Diagonal); } static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap); node->style->stroke.cap = _toLineCap(value); } static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join); node->style->stroke.join = _toLineJoin(value); } static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { char* end = nullptr; const float miterlimit = toFloat(value, &end); // https://www.w3.org/TR/SVG2/painting.html#LineJoin // - A negative value for stroke-miterlimit must be treated as an illegal value. if (miterlimit < 0.0f) { TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.", node->style->stroke.miterlimit, miterlimit); return; } node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Miterlimit); node->style->stroke.miterlimit = miterlimit; } static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule); node->style->fill.fillRule = _toFillRule(value); } static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->flags = (node->style->flags | SvgStyleFlags::Opacity); node->style->opacity = _toOpacity(value); } static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity); node->style->fill.opacity = _toOpacity(value); } static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->transform = _parseTransformationMatrix(value); } static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { auto style = node->style; int len = strlen(value); if (len >= 3 && !strncmp(value, "url", 3)) { tvg::free(style->clipPath.url); style->clipPath.url = _idFromUrl((const char*)(value + 3)); } } static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { auto style = node->style; int len = strlen(value); if (len >= 3 && !strncmp(value, "url", 3)) { tvg::free(style->mask.url); style->mask.url = _idFromUrl((const char*)(value + 3)); } } static void _handleFilterAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { auto style = node->style; int len = strlen(value); if (len >= 3 && !strncmp(value, "url", 3)) { if (style->filter.url) tvg::free(style->filter.url); style->filter.url = _idFromUrl((const char*)(value + 3)); } } static void _handleMaskTypeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->node.mask.type = _toMaskType(value); } static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { //TODO : The display attribute can have various values as well as "none". // The default is "inline" which means visible and "none" means invisible. // Depending on the type of node, additional functionality may be required. // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display node->style->flags = (node->style->flags | SvgStyleFlags::Display); if (STR_AS(value, "none")) node->style->display = false; else node->style->display = true; } static bool _cssApplyClass(SvgNode* node, const char* classString, SvgNode* styleRoot); static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder); node->style->paintOrder = _toPaintOrder(value); } static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { auto cssClass = &node->style->cssClass; if (value) tvg::free(*cssClass); *cssClass = _copyId(value); if (!_cssApplyClass(node, *cssClass, loader->cssStyle)) { loader->nodesToStyle.push({node, *cssClass}); } } typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value); #define STYLE_DEF(Name, Name1, Flag) { #Name, sizeof(#Name), _handle##Name1##Attr, Flag } static constexpr struct { const char* tag; int sz; styleMethod tagHandler; SvgStyleFlags flag; } styleTags[] = { STYLE_DEF(color, Color, SvgStyleFlags::Color), STYLE_DEF(fill, Fill, SvgStyleFlags::Fill), STYLE_DEF(fill-rule, FillRule, SvgStyleFlags::FillRule), STYLE_DEF(fill-opacity, FillOpacity, SvgStyleFlags::FillOpacity), STYLE_DEF(opacity, Opacity, SvgStyleFlags::Opacity), STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke), STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth), STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin), STYLE_DEF(stroke-miterlimit, StrokeMiterlimit, SvgStyleFlags::StrokeMiterlimit), STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap), STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity), STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray), STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset), STYLE_DEF(transform, Transform, SvgStyleFlags::Transform), STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath), STYLE_DEF(mask, Mask, SvgStyleFlags::Mask), STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType), STYLE_DEF(display, Display, SvgStyleFlags::Display), STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder), STYLE_DEF(filter, Filter, SvgStyleFlags::Filter) }; static SvgXmlSpace _toXmlSpace(const char* str) { if (STR_AS(str, "default")) return SvgXmlSpace::Default; if (STR_AS(str, "preserve")) return SvgXmlSpace::Preserve; return SvgXmlSpace::None; } static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; int sz; if (!key || !value) return false; //Trim the white space key = svgUtilSkipWhiteSpace(key, nullptr); value = svgUtilSkipWhiteSpace(value, nullptr); if (!style && STR_AS(key, "xml:space")) { node->xmlSpace = _toXmlSpace(value); return true; } sz = strlen(key); for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) { if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) { bool importance = false; if (auto ptr = strstr(value, "!important")) { size_t size = ptr - value; while (size > 0 && isspace(value[size - 1])) { size--; } value = duplicate(value, size); importance = true; } if (style) { if (importance || !(node->style->flagsImportance & styleTags[i].flag)) { styleTags[i].tagHandler(loader, node, value); node->style->flags = (node->style->flags | styleTags[i].flag); } } else if (!(node->style->flags & styleTags[i].flag)) { styleTags[i].tagHandler(loader, node, value); } if (importance) { node->style->flagsImportance = (node->style->flags | styleTags[i].flag); tvg::free(const_cast(value)); } return true; } } return false; } static bool _parseStyleAttr(void* data, const char* key, const char* value) { return _parseStyleAttr(data, key, value, true); } /* parse g node * https://www.w3.org/TR/SVG/struct.html#Groups */ static bool _attrParseGNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "transform")) { node->transform = _parseTransformationMatrix(value); } else if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } /* parse clipPath node * https://www.w3.org/TR/SVG/struct.html#Groups */ static bool _attrParseClipPathNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgClipNode* clip = &(node->node.clip); if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "transform")) { node->transform = _parseTransformationMatrix(value); } else if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (STR_AS(key, "clipPathUnits")) { if (STR_AS(value, "objectBoundingBox")) clip->userSpace = false; } else { return _parseStyleAttr(loader, key, value, false); } return true; } static bool _attrParseMaskNode(void* data, const char* key, const char* value) { auto loader = (SvgLoaderData*)data; auto node = loader->svgParse->node; auto mask = &(node->node.mask); if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "transform")) { node->transform = _parseTransformationMatrix(value); } else if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (STR_AS(key, "maskContentUnits")) { if (STR_AS(value, "objectBoundingBox")) mask->userSpace = false; } else if (STR_AS(key, "mask-type")) { mask->type = _toMaskType(value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } static bool _attrParseCssStyleNode(void* data, const char* key, const char* value) { auto loader = (SvgLoaderData*)data; auto node = loader->svgParse->node; if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } static bool _attrParseSymbolNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgSymbolNode* symbol = &(node->node.symbol); if (STR_AS(key, "viewBox")) { if (!_parseNumber(&value, nullptr, &symbol->vx) || !_parseNumber(&value, nullptr, &symbol->vy)) return false; if (!_parseNumber(&value, nullptr, &symbol->vw) || !_parseNumber(&value, nullptr, &symbol->vh)) return false; symbol->hasViewBox = true; } else if (STR_AS(key, "width")) { symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); symbol->hasWidth = true; } else if (STR_AS(key, "height")) { symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); symbol->hasHeight = true; } else if (STR_AS(key, "preserveAspectRatio")) { _parseAspectRatio(&value, &symbol->align, &symbol->meetOrSlice); } else if (STR_AS(key, "overflow")) { if (STR_AS(value, "visible")) symbol->overflowVisible = true; } else { return _attrParseGNode(data, key, value); } return true; } static constexpr struct { const char* tag; SvgParserLengthType type; int sz; size_t offset; } boxTags[] = { {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(Box, x)}, {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(Box, y)}, {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(Box, w)}, {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(Box, h)} }; static bool _parseBox(const char* key, const char* value, Box* box, bool (&isPercentage)[4]) { auto array = (unsigned char*)box; int sz = strlen(key); for (unsigned int i = 0; i < sizeof(boxTags) / sizeof(boxTags[0]); i++) { if (boxTags[i].sz - 1 == sz && !strncmp(boxTags[i].tag, key, sz)) { *(float*)(array + boxTags[i].offset) = _gradientToFloat(nullptr, value, isPercentage[i]); return true; } } return false; } static void _recalcBox(const SvgLoaderData* loader, Box* box, bool (&isPercentage)[4]) { auto array = (unsigned char*)box; for (unsigned int i = 0; i < sizeof(boxTags) / sizeof(boxTags[0]); i++) { if (!isPercentage[i]) continue; if (boxTags[i].type == SvgParserLengthType::Horizontal) *(float*)(array + boxTags[i].offset) *= loader->svgParse->global.w; else *(float*)(array + boxTags[i].offset) *= loader->svgParse->global.h; } } static bool _attrParseFilterNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgFilterNode* filter = &node->node.filter; _parseBox(key, value, &filter->box, filter->isPercentage); if (STR_AS(key, "id")) { if (node->id && value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "primitiveUnits")) { if (STR_AS(value, "objectBoundingBox")) filter->primitiveUserSpace = false; } else if (STR_AS(key, "filterUnits")) { if (STR_AS(value, "userSpaceOnUse")) filter->filterUserSpace = true; } return true; } static void _parseGaussianBlurStdDeviation(const char** content, float* x, float* y) { auto str = *content; char* end = nullptr; float deviation[2] = {0, 0}; int n = 0; while (*str && n < 2) { str = svgUtilSkipWhiteSpaceAndComma(str); auto parsedValue = toFloat(str, &end); if (parsedValue < 0.0f) break; deviation[n++] = parsedValue; str = end; } *x = deviation[0]; *y = n == 1 ? deviation[0] : deviation[1]; } static bool _attrParseGaussianBlurNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgGaussianBlurNode* gaussianBlur = &node->node.gaussianBlur; if (_parseBox(key, value, &gaussianBlur->box, gaussianBlur->isPercentage)) gaussianBlur->hasBox = true; if (STR_AS(key, "id")) { if (node->id && value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "stdDeviation")) { _parseGaussianBlurStdDeviation(&value, &gaussianBlur->stdDevX, &gaussianBlur->stdDevY); } else if (STR_AS(key, "edgeMode")) { if (STR_AS(value, "wrap")) gaussianBlur->edgeModeWrap = true; } else { return _parseStyleAttr(loader, key, value, false); } return true; } static SvgNode* _createNode(SvgNode* parent, SvgNodeType type) { SvgNode* node = tvg::calloc(1, sizeof(SvgNode)); //Default fill property node->style = tvg::calloc(1, sizeof(SvgStyleProperty)); //Set the default values other than 0/false: https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint node->style->opacity = 255; node->style->fill.opacity = 255; node->style->fill.fillRule = FillRule::NonZero; node->style->stroke.paint.none = true; node->style->stroke.opacity = 255; node->style->stroke.width = 1; node->style->stroke.cap = StrokeCap::Butt; node->style->stroke.join = StrokeJoin::Miter; node->style->stroke.miterlimit = 4.0f; node->style->stroke.scale = 1.0; node->style->paintOrder = _toPaintOrder("fill stroke"); node->style->display = true; node->parent = parent; node->type = type; node->xmlSpace = SvgXmlSpace::None; if (parent) parent->child.push(node); return node; } static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength, TVG_UNUSED parseAttributes func) { if (loader->def && loader->doc->node.doc.defs) return loader->def; loader->def = loader->doc->node.doc.defs = _createNode(nullptr, SvgNodeType::Defs); return loader->def; } static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::G); func(buf, bufLength, _attrParseGNode, loader); return loader->svgParse->node; } static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Doc); auto doc = &(loader->svgParse->node->node.doc); loader->svgParse->global.w = 1.0f; loader->svgParse->global.h = 1.0f; doc->align = AspectRatioAlign::XMidYMid; doc->meetOrSlice = AspectRatioMeetOrSlice::Meet; doc->viewFlag = SvgViewFlag::None; func(buf, bufLength, _attrParseSvgNode, loader); if (!(doc->viewFlag & SvgViewFlag::Viewbox)) { if (doc->viewFlag & SvgViewFlag::Width) { loader->svgParse->global.w = doc->w; } if (doc->viewFlag & SvgViewFlag::Height) { loader->svgParse->global.h = doc->h; } } return loader->svgParse->node; } static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Mask); if (!loader->svgParse->node) return nullptr; loader->svgParse->node->node.mask.userSpace = true; loader->svgParse->node->node.mask.type = SvgMaskType::Luminance; func(buf, bufLength, _attrParseMaskNode, loader); return loader->svgParse->node; } static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath); if (!loader->svgParse->node) return nullptr; loader->svgParse->node->style->display = false; loader->svgParse->node->node.clip.userSpace = true; func(buf, bufLength, _attrParseClipPathNode, loader); return loader->svgParse->node; } static SvgNode* _createCssStyleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::CssStyle); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParseCssStyleNode, loader); return loader->svgParse->node; } static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol); if (!loader->svgParse->node) return nullptr; loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid; loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet; func(buf, bufLength, _attrParseSymbolNode, loader); return loader->svgParse->node; } static SvgNode* _createGaussianBlurNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::GaussianBlur); if (!loader->svgParse->node) return nullptr; loader->svgParse->node->style->display = false; loader->svgParse->node->node.gaussianBlur.box = {0.0f, 0.0f, 1.0f, 1.0f}; func(buf, bufLength, _attrParseGaussianBlurNode, loader); return loader->svgParse->node; } static SvgNode* _createFilterNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Filter); if (!loader->svgParse->node) return nullptr; SvgFilterNode& filter = loader->svgParse->node->node.filter; loader->svgParse->node->style->display = false; filter.box = {-0.1f, -0.1f, 1.2f, 1.2f}; filter.primitiveUserSpace = true; func(buf, bufLength, _attrParseFilterNode, loader); if (filter.filterUserSpace) _recalcBox(loader, &filter.box, filter.isPercentage); return loader->svgParse->node; } static bool _attrParsePathNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgPathNode* path = &(node->node.path); if (STR_AS(key, "d")) { tvg::free(path->path); //Temporary: need to copy path->path = _copyId(value); } else if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Path); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParsePathNode, loader); return loader->svgParse->node; } static constexpr struct { const char* tag; SvgParserLengthType type; int sz; size_t offset; } circleTags[] = { {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)}, {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)}, {"r", SvgParserLengthType::Diagonal, sizeof("r"), offsetof(SvgCircleNode, r)} }; /* parse the attributes for a circle element. * https://www.w3.org/TR/SVG/shapes.html#CircleElement */ static bool _attrParseCircleNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgCircleNode* circle = &(node->node.circle); unsigned char* array; int sz = strlen(key); array = (unsigned char*)circle; for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) { if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) { *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type); return true; } } if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Circle); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParseCircleNode, loader); return loader->svgParse->node; } static constexpr struct { const char* tag; SvgParserLengthType type; int sz; size_t offset; } ellipseTags[] = { {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)}, {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)}, {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)}, {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)} }; /* parse the attributes for an ellipse element. * https://www.w3.org/TR/SVG/shapes.html#EllipseElement */ static bool _attrParseEllipseNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgEllipseNode* ellipse = &(node->node.ellipse); unsigned char* array; int sz = strlen(key); array = (unsigned char*)ellipse; for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) { if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) { *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type); return true; } } if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParseEllipseNode, loader); return loader->svgParse->node; } static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon) { float num_x, num_y; while (_parseNumber(&str, nullptr, &num_x) && _parseNumber(&str, nullptr, &num_y)) { polygon->pts.push(num_x); polygon->pts.push(num_y); } return true; } /* parse the attributes for a polygon element. * https://www.w3.org/TR/SVG/shapes.html#PolylineElement */ static bool _attrParsePolygonNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgPolygonNode* polygon = nullptr; if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon); else polygon = &(node->node.polyline); if (STR_AS(key, "points")) { return _attrParsePolygonPoints(value, polygon); } else if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParsePolygonNode, loader); return loader->svgParse->node; } static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParsePolygonNode, loader); return loader->svgParse->node; } static constexpr struct { const char* tag; SvgParserLengthType type; int sz; size_t offset; } rectTags[] = { {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)}, {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)}, {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)}, {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)}, {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)}, {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)} }; /* parse the attributes for a rect element. * https://www.w3.org/TR/SVG/shapes.html#RectElement */ static bool _attrParseRectNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgRectNode* rect = &(node->node.rect); unsigned char* array; bool ret = true; int sz = strlen(key); array = (unsigned char*)rect; for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) { if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) { *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type); //Case if only rx or ry is declared if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true; if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true; if ((rect->rx >= FLOAT_EPSILON) && (rect->ry < FLOAT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx; if ((rect->ry >= FLOAT_EPSILON) && (rect->rx < FLOAT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry; return ret; } } if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (STR_AS(key, "style")) { ret = xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else { ret = _parseStyleAttr(loader, key, value, false); } return ret; } static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Rect); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParseRectNode, loader); return loader->svgParse->node; } static constexpr struct { const char* tag; SvgParserLengthType type; int sz; size_t offset; } lineTags[] = { {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)}, {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)}, {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)}, {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)} }; /* parse the attributes for a line element. * https://www.w3.org/TR/SVG/shapes.html#LineElement */ static bool _attrParseLineNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgLineNode* line = &(node->node.line); unsigned char* array; int sz = strlen(key); array = (unsigned char*)line; for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) { if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) { *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type); return true; } } if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Line); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParseLineNode, loader); return loader->svgParse->node; } static char* _idFromHref(const char* href) { href = svgUtilSkipWhiteSpace(href, nullptr); if ((*href) == '#') href++; return duplicate(href); } static constexpr struct { const char* tag; SvgParserLengthType type; int sz; size_t offset; } imageTags[] = { {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)}, {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)}, {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)}, {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)}, }; /* parse the attributes for a image element. * https://www.w3.org/TR/SVG/embedded.html#ImageElement */ static bool _attrParseImageNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgImageNode* image = &(node->node.image); unsigned char* array; int sz = strlen(key); array = (unsigned char*)image; for (unsigned int i = 0; i < sizeof(imageTags) / sizeof(imageTags[0]); i++) { if (imageTags[i].sz - 1 == sz && !strncmp(imageTags[i].tag, key, sz)) { *((float*)(array + imageTags[i].offset)) = _toFloat(loader->svgParse, value, imageTags[i].type); return true; } } if (STR_AS(key, "href") || STR_AS(key, "xlink:href")) { if (value) tvg::free(image->href); image->href = _idFromHref(value); } else if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else if (STR_AS(key, "transform")) { node->transform = _parseTransformationMatrix(value); } else { return _parseStyleAttr(loader, key, value); } return true; } static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Image); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParseImageNode, loader); return loader->svgParse->node; } static char* _unquote(const char* str) { auto len = str ? strlen(str) : 0; if (len >= 2 && str[0] == '\'' && str[len - 1] == '\'') return duplicate(str + 1, len - 2); return strdup(str); } static bool _attrParseFontFace(void* data, const char* key, const char* value) { if (!key || !value) return false; key = svgUtilSkipWhiteSpace(key, nullptr); value = svgUtilSkipWhiteSpace(value, nullptr); auto loader = (SvgLoaderData*)data; auto& font = loader->fonts.last(); if (STR_AS(key, "font-family")) { if (font.name) tvg::free(font.name); font.name = _unquote(value); } else if (STR_AS(key, "src")) { font.srcLen = _srcFromUrl(value, font.src); } return true; } static void _createFontFace(SvgLoaderData* loader, const char* buf, unsigned bufLength, parseAttributes func) { loader->fonts.push(FontFace()); func(buf, bufLength, _attrParseFontFace, loader); } static SvgNode* _getDefsNode(SvgNode* node) { if (!node) return nullptr; while (node->parent) { node = node->parent; } if (node->type == SvgNodeType::Doc) return node->node.doc.defs; if (node->type == SvgNodeType::Defs) return node; return nullptr; } static SvgNode* _findNodeById(SvgNode *node, const char* id) { if (!node) return nullptr; if (node->id && STR_AS(node->id, id)) return node; if (node->child.count > 0) { ARRAY_FOREACH(p, node->child) { if (auto result = _findNodeById(*p, id)) return result; } } return nullptr; } static SvgNode* _findParentById(SvgNode* node, char* id, SvgNode* doc) { SvgNode *parent = node->parent; while (parent != nullptr && parent != doc) { if (parent->id && STR_AS(parent->id, id)) { return parent; } parent = parent->parent; } return nullptr; } static bool _checkPostponed(SvgNode* node, SvgNode* cloneNode, int depth) { if (node == cloneNode) return true; if (depth == 512) { TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); return false; } ARRAY_FOREACH(p, node->child) { if (_checkPostponed(*p, cloneNode, depth + 1)) return true; } return false; } static constexpr struct { const char* tag; SvgParserLengthType type; int sz; size_t offset; } useTags[] = { {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgUseNode, x)}, {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgUseNode, y)}, {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgUseNode, w)}, {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)} }; static void _cloneNode(SvgNode* from, SvgNode* parent, int depth); static bool _attrParseUseNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode *defs, *nodeFrom, *node = loader->svgParse->node; char* id; SvgUseNode* use = &(node->node.use); int sz = strlen(key); unsigned char* array = (unsigned char*)use; for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) { if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) { *((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type); if (useTags[i].offset == offsetof(SvgUseNode, w)) use->isWidthSet = true; else if (useTags[i].offset == offsetof(SvgUseNode, h)) use->isHeightSet = true; return true; } } if (STR_AS(key, "href") || STR_AS(key, "xlink:href")) { id = _idFromHref(value); defs = _getDefsNode(node); nodeFrom = _findNodeById(defs, id); if (nodeFrom) { if (!_findParentById(node, id, loader->doc)) { //Check if none of nodeFrom's children are in the cloneNodes list auto postpone = false; INLIST_FOREACH(loader->cloneNodes, pair) { if (_checkPostponed(nodeFrom, pair->node, 1)) { postpone = true; loader->cloneNodes.back(new(tvg::malloc(sizeof(SvgNodeIdPair))) SvgNodeIdPair(node, id)); break; } } //None of the children of nodeFrom are on the cloneNodes list, so it can be cloned immediately if (!postpone) { _cloneNode(nodeFrom, node, 0); if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; tvg::free(id); } } else { TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", id); tvg::free(id); } } else { //some svg export software include element at the end of the file //if so the 'from' element won't be found now and we have to repeat finding //after the whole file is parsed loader->cloneNodes.back(new(tvg::malloc(sizeof(SvgNodeIdPair))) SvgNodeIdPair(node, id)); } } else { return _attrParseGNode(data, key, value); } return true; } static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Use); if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParseUseNode, loader); return loader->svgParse->node; } static constexpr struct { const char* tag; SvgParserLengthType type; int sz; size_t offset; } textTags[] = { {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgTextNode, x)}, {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgTextNode, y)}, {"font-size", SvgParserLengthType::Vertical, sizeof("font-size"), offsetof(SvgTextNode, fontSize)} }; static bool _attrParseTextNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; SvgTextNode* text = &(node->node.text); unsigned char* array; int sz = strlen(key); array = (unsigned char*)text; for (unsigned int i = 0; i < sizeof(textTags) / sizeof(textTags[0]); i++) { if (textTags[i].sz - 1 == sz && !strncmp(textTags[i].tag, key, sz)) { *((float*)(array + textTags[i].offset)) = _toFloat(loader->svgParse, value, textTags[i].type); return true; } } if (STR_AS(key, "font-family")) { if (value) { tvg::free(text->fontFamily); text->fontFamily = duplicate(value); } } else if (STR_AS(key, "style")) { return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (STR_AS(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (STR_AS(key, "mask")) { _handleMaskAttr(loader, node, value); } else if (STR_AS(key, "filter")) { _handleFilterAttr(loader, node, value); } else if (STR_AS(key, "id")) { if (value) tvg::free(node->id); node->id = _copyId(value); } else if (STR_AS(key, "class")) { _handleCssClassAttr(loader, node, value); } else { return _parseStyleAttr(loader, key, value, false); } return true; } static SvgNode* _createTextNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) { loader->svgParse->node = _createNode(parent, SvgNodeType::Text); if (!loader->svgParse->node) return nullptr; //TODO: support the def font and size as used in a system? loader->svgParse->node->node.text.fontSize = 10.0f; func(buf, bufLength, _attrParseTextNode, loader); return loader->svgParse->node; } static constexpr struct { const char* tag; int sz; FactoryMethod tagHandler; } graphicsTags[] = { {"use", sizeof("use"), _createUseNode}, {"circle", sizeof("circle"), _createCircleNode}, {"ellipse", sizeof("ellipse"), _createEllipseNode}, {"path", sizeof("path"), _createPathNode}, {"polygon", sizeof("polygon"), _createPolygonNode}, {"rect", sizeof("rect"), _createRectNode}, {"polyline", sizeof("polyline"), _createPolylineNode}, {"line", sizeof("line"), _createLineNode}, {"image", sizeof("image"), _createImageNode}, {"text", sizeof("text"), _createTextNode}, {"feGaussianBlur", sizeof("feGaussianBlur"), _createGaussianBlurNode} }; static constexpr struct { const char* tag; int sz; FactoryMethod tagHandler; } groupTags[] = { {"defs", sizeof("defs"), _createDefsNode}, {"g", sizeof("g"), _createGNode}, {"svg", sizeof("svg"), _createSvgNode}, {"mask", sizeof("mask"), _createMaskNode}, {"clipPath", sizeof("clipPath"), _createClipPathNode}, {"style", sizeof("style"), _createCssStyleNode}, {"symbol", sizeof("symbol"), _createSymbolNode}, {"filter", sizeof("filter"), _createFilterNode} }; #define FIND_FACTORY(Short_Name, Tags_Array) \ static FactoryMethod \ _find##Short_Name##Factory(const char* name) \ { \ unsigned int i; \ int sz = strlen(name); \ \ for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \ if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \ return Tags_Array[i].tagHandler; \ } \ } \ return nullptr; \ } FIND_FACTORY(Group, groupTags) FIND_FACTORY(Graphics, graphicsTags) FillSpread _parseSpreadValue(const char* value) { auto spread = FillSpread::Pad; if (STR_AS(value, "reflect")) { spread = FillSpread::Reflect; } else if (STR_AS(value, "repeat")) { spread = FillSpread::Repeat; } return spread; } static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { radial->cx = _gradientToFloat(loader->svgParse, value, radial->isCxPercentage); if (!loader->svgParse->gradient.parsedFx) { radial->fx = radial->cx; radial->isFxPercentage = radial->isCxPercentage; } } static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { radial->cy = _gradientToFloat(loader->svgParse, value, radial->isCyPercentage); if (!loader->svgParse->gradient.parsedFy) { radial->fy = radial->cy; radial->isFyPercentage = radial->isCyPercentage; } } static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { radial->fx = _gradientToFloat(loader->svgParse, value, radial->isFxPercentage); loader->svgParse->gradient.parsedFx = true; } static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { radial->fy = _gradientToFloat(loader->svgParse, value, radial->isFyPercentage); loader->svgParse->gradient.parsedFy = true; } static void _handleRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { radial->fr = _gradientToFloat(loader->svgParse, value, radial->isFrPercentage); } static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage); } static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w; } static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h; } static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w; } static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h; } static void _recalcRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html if (userSpace && !radial->isFrPercentage) radial->fr = radial->fr / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0)); } static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0)); } static void _recalcInheritedRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (!radial->isCxPercentage) { if (userSpace) radial->cx /= loader->svgParse->global.w; else radial->cx *= loader->svgParse->global.w; } } static void _recalcInheritedRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (!radial->isCyPercentage) { if (userSpace) radial->cy /= loader->svgParse->global.h; else radial->cy *= loader->svgParse->global.h; } } static void _recalcInheritedRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (!radial->isFxPercentage) { if (userSpace) radial->fx /= loader->svgParse->global.w; else radial->fx *= loader->svgParse->global.w; } } static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (!radial->isFyPercentage) { if (userSpace) radial->fy /= loader->svgParse->global.h; else radial->fy *= loader->svgParse->global.h; } } static void _recalcInheritedRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (!radial->isFrPercentage) { if (userSpace) radial->fr /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); else radial->fr *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); } } static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { if (!radial->isRPercentage) { if (userSpace) radial->r /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); else radial->r *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); } } static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->cx = from->radial->cx; to->radial->isCxPercentage = from->radial->isCxPercentage; to->flags = (to->flags | SvgGradientFlags::Cx); } static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->cy = from->radial->cy; to->radial->isCyPercentage = from->radial->isCyPercentage; to->flags = (to->flags | SvgGradientFlags::Cy); } static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->fx = from->radial->fx; to->radial->isFxPercentage = from->radial->isFxPercentage; to->flags = (to->flags | SvgGradientFlags::Fx); } static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->fy = from->radial->fy; to->radial->isFyPercentage = from->radial->isFyPercentage; to->flags = (to->flags | SvgGradientFlags::Fy); } static void _inheritRadialFrAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->fr = from->radial->fr; to->radial->isFrPercentage = from->radial->isFrPercentage; to->flags = (to->flags | SvgGradientFlags::Fr); } static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->r = from->radial->r; to->radial->isRPercentage = from->radial->isRPercentage; to->flags = (to->flags | SvgGradientFlags::R); } typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value); typedef void (*radialInheritMethod)(SvgStyleGradient* to, SvgStyleGradient* from); typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace); #define RADIAL_DEF(Name, Name1, Flag) \ { \ #Name, sizeof(#Name), _handleRadial##Name1##Attr, _inheritRadial##Name1##Attr, _recalcRadial##Name1##Attr, _recalcInheritedRadial##Name1##Attr, Flag \ } static constexpr struct { const char* tag; int sz; radialMethod tagHandler; radialInheritMethod tagInheritHandler; radialMethodRecalc tagRecalc; radialMethodRecalc tagInheritedRecalc; SvgGradientFlags flag; } radialTags[] = { RADIAL_DEF(cx, Cx, SvgGradientFlags::Cx), RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy), RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx), RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy), RADIAL_DEF(r, R, SvgGradientFlags::R), RADIAL_DEF(fr, Fr, SvgGradientFlags::Fr) }; static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgStyleGradient* grad = loader->svgParse->styleGrad; SvgRadialGradient* radial = grad->radial; int sz = strlen(key); for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) { radialTags[i].tagHandler(loader, radial, value); grad->flags = (grad->flags | radialTags[i].flag); return true; } } if (STR_AS(key, "id")) { if (value) tvg::free(grad->id); grad->id = _copyId(value); } else if (STR_AS(key, "spreadMethod")) { grad->spread = _parseSpreadValue(value); grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod); } else if (STR_AS(key, "href") || STR_AS(key, "xlink:href")) { if (value) tvg::free(grad->ref); grad->ref = _idFromHref(value); } else if (STR_AS(key, "gradientUnits")) { if (STR_AS(value, "userSpaceOnUse")) grad->userSpace = true; grad->flags = (grad->flags | SvgGradientFlags::GradientUnits); } else if (STR_AS(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { return false; } return true; } static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength) { auto grad = tvg::calloc(1, sizeof(SvgStyleGradient)); loader->svgParse->styleGrad = grad; grad->flags = SvgGradientFlags::None; grad->type = SvgGradientType::Radial; grad->radial = tvg::calloc(1, sizeof(SvgRadialGradient)); if (!grad->radial) { grad->clear(); tvg::free(grad); return nullptr; } /** * Default values of gradient transformed into global percentage */ grad->radial->cx = 0.5f; grad->radial->cy = 0.5f; grad->radial->fx = 0.5f; grad->radial->fy = 0.5f; grad->radial->r = 0.5f; grad->radial->isCxPercentage = true; grad->radial->isCyPercentage = true; grad->radial->isFxPercentage = true; grad->radial->isFyPercentage = true; grad->radial->isRPercentage = true; grad->radial->isFrPercentage = true; loader->svgParse->gradient.parsedFx = false; loader->svgParse->gradient.parsedFy = false; xmlParseAttributes(buf, bufLength, _attrParseRadialGradientNode, loader); for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace); } return loader->svgParse->styleGrad; } static SvgColor* _findLatestColor(const SvgLoaderData* loader) { auto parent = loader->stack.count > 0 ? loader->stack.last() : loader->doc; while (parent != nullptr) { if (parent->style->curColorSet) return &parent->style->color; parent = parent->parent; } return nullptr; } static bool _attrParseStopsStyle(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; auto stop = &loader->svgParse->gradStop; if (STR_AS(key, "stop-opacity")) { stop->a = _toOpacity(value); loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity); } else if (STR_AS(key, "stop-color")) { if (STR_AS(value, "currentColor")) { if (auto latestColor = _findLatestColor(loader)) { stop->r = latestColor->r; stop->g = latestColor->g; stop->b = latestColor->b; } } else if (_toColor(value, stop->r, stop->g, stop->b, nullptr)) { loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor); } } else { return false; } return true; } static bool _attrParseStops(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; auto stop = &loader->svgParse->gradStop; if (STR_AS(key, "offset")) { stop->offset = _toOffset(value); } else if (STR_AS(key, "stop-opacity")) { if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) { stop->a = _toOpacity(value); } } else if (STR_AS(key, "stop-color")) { if (STR_AS(value, "currentColor")) { if (auto latestColor = _findLatestColor(loader)) { stop->r = latestColor->r; stop->g = latestColor->g; stop->b = latestColor->b; } } else if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { _toColor(value, stop->r, stop->g, stop->b, nullptr); } } else if (STR_AS(key, "style")) { xmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data); } else { return false; } return true; } static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value) { linear->x1 = _gradientToFloat(loader->svgParse, value, linear->isX1Percentage); } static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value) { linear->y1 = _gradientToFloat(loader->svgParse, value, linear->isY1Percentage); } static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value) { linear->x2 = _gradientToFloat(loader->svgParse, value, linear->isX2Percentage); } static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value) { linear->y2 = _gradientToFloat(loader->svgParse, value, linear->isY2Percentage); } static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) { if (userSpace && !linear->isX1Percentage) linear->x1 = linear->x1 / loader->svgParse->global.w; } static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) { if (userSpace && !linear->isY1Percentage) linear->y1 = linear->y1 / loader->svgParse->global.h; } static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) { if (userSpace && !linear->isX2Percentage) linear->x2 = linear->x2 / loader->svgParse->global.w; } static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) { if (userSpace && !linear->isY2Percentage) linear->y2 = linear->y2 / loader->svgParse->global.h; } static void _recalcInheritedLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) { if (!linear->isX1Percentage) { if (userSpace) linear->x1 /= loader->svgParse->global.w; else linear->x1 *= loader->svgParse->global.w; } } static void _recalcInheritedLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) { if (!linear->isX2Percentage) { if (userSpace) linear->x2 /= loader->svgParse->global.w; else linear->x2 *= loader->svgParse->global.w; } } static void _recalcInheritedLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) { if (!linear->isY1Percentage) { if (userSpace) linear->y1 /= loader->svgParse->global.h; else linear->y1 *= loader->svgParse->global.h; } } static void _recalcInheritedLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) { if (!linear->isY2Percentage) { if (userSpace) linear->y2 /= loader->svgParse->global.h; else linear->y2 *= loader->svgParse->global.h; } } static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from) { to->linear->x1 = from->linear->x1; to->linear->isX1Percentage = from->linear->isX1Percentage; to->flags = (to->flags | SvgGradientFlags::X1); } static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from) { to->linear->x2 = from->linear->x2; to->linear->isX2Percentage = from->linear->isX2Percentage; to->flags = (to->flags | SvgGradientFlags::X2); } static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from) { to->linear->y1 = from->linear->y1; to->linear->isY1Percentage = from->linear->isY1Percentage; to->flags = (to->flags | SvgGradientFlags::Y1); } static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from) { to->linear->y2 = from->linear->y2; to->linear->isY2Percentage = from->linear->isY2Percentage; to->flags = (to->flags | SvgGradientFlags::Y2); } typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value); typedef void (*Linear_Inherit_Method)(SvgStyleGradient* to, SvgStyleGradient* from); typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace); #define LINEAR_DEF(Name, Name1, Flag) \ { \ #Name, sizeof(#Name), _handleLinear##Name1##Attr, _inheritLinear##Name1##Attr, _recalcLinear##Name1##Attr, _recalcInheritedLinear##Name1##Attr, Flag \ } static constexpr struct { const char* tag; int sz; Linear_Method tagHandler; Linear_Inherit_Method tagInheritHandler; Linear_Method_Recalc tagRecalc; Linear_Method_Recalc tagInheritedRecalc; SvgGradientFlags flag; } linear_tags[] = { LINEAR_DEF(x1, X1, SvgGradientFlags::X1), LINEAR_DEF(y1, Y1, SvgGradientFlags::Y1), LINEAR_DEF(x2, X2, SvgGradientFlags::X2), LINEAR_DEF(y2, Y2, SvgGradientFlags::Y2) }; static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgStyleGradient* grad = loader->svgParse->styleGrad; SvgLinearGradient* linear = grad->linear; int sz = strlen(key); for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) { linear_tags[i].tagHandler(loader, linear, value); grad->flags = (grad->flags | linear_tags[i].flag); return true; } } if (STR_AS(key, "id")) { if (value) tvg::free(grad->id); grad->id = _copyId(value); } else if (STR_AS(key, "spreadMethod")) { grad->spread = _parseSpreadValue(value); grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod); } else if (STR_AS(key, "href") || STR_AS(key, "xlink:href")) { if (value) tvg::free(grad->ref); grad->ref = _idFromHref(value); } else if (STR_AS(key, "gradientUnits")) { if (STR_AS(value, "userSpaceOnUse")) grad->userSpace = true; grad->flags = (grad->flags | SvgGradientFlags::GradientUnits); } else if (STR_AS(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { return false; } return true; } static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength) { auto grad = tvg::calloc(1, sizeof(SvgStyleGradient)); loader->svgParse->styleGrad = grad; grad->flags = SvgGradientFlags::None; grad->type = SvgGradientType::Linear; grad->linear = tvg::calloc(1, sizeof(SvgLinearGradient)); if (!grad->linear) { grad->clear(); tvg::free(grad); return nullptr; } /** * Default value of x2 is 100% - transformed to the global percentage */ grad->linear->x2 = 1.0f; grad->linear->isX2Percentage = true; xmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader); for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace); } return loader->svgParse->styleGrad; } #define GRADIENT_DEF(Name, Name1) \ { \ #Name, sizeof(#Name), _create##Name1 \ } /** * In the case when the gradients lengths are given as numbers (not percentages) * in the current user coordinate system, they are recalculated into percentages * related to the canvas width and height. */ static constexpr struct { const char* tag; int sz; GradientFactoryMethod tagHandler; } gradientTags[] = { GRADIENT_DEF(linearGradient, LinearGradient), GRADIENT_DEF(radialGradient, RadialGradient) }; static GradientFactoryMethod _findGradientFactory(const char* name) { int sz = strlen(name); for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) { if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) { return gradientTags[i].tagHandler; } } return nullptr; } static void _cloneGradStops(Array& dst, const Array& src) { ARRAY_FOREACH(p, src) { dst.push(*p); } } static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from) { if (!to || !from) return; if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) { to->spread = from->spread; to->flags = (to->flags | SvgGradientFlags::SpreadMethod); } bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits); if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) { to->userSpace = from->userSpace; to->flags = (to->flags | SvgGradientFlags::GradientUnits); } if (!to->transform && from->transform) { to->transform = tvg::malloc(sizeof(Matrix)); if (to->transform) *to->transform = *from->transform; } if (to->type == SvgGradientType::Linear) { for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { bool coordSet = to->flags & linear_tags[i].flag; if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) { linear_tags[i].tagInheritHandler(to, from); } //GradUnits not set directly, coord set if (!gradUnitSet && coordSet) { linear_tags[i].tagRecalc(loader, to->linear, to->userSpace); } //GradUnits set, coord not set directly if (to->userSpace == from->userSpace) continue; if (gradUnitSet && !coordSet) { linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace); } } } else if (to->type == SvgGradientType::Radial) { for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { bool coordSet = (to->flags & radialTags[i].flag); if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) { radialTags[i].tagInheritHandler(to, from); } //GradUnits not set directly, coord set if (!gradUnitSet && coordSet) { radialTags[i].tagRecalc(loader, to->radial, to->userSpace); //If fx and fy are not set, set cx and cy. if (STR_AS(radialTags[i].tag, "cx") && !(to->flags & SvgGradientFlags::Fx)) to->radial->fx = to->radial->cx; if (STR_AS(radialTags[i].tag, "cy") && !(to->flags & SvgGradientFlags::Fy)) to->radial->fy = to->radial->cy; } //GradUnits set, coord not set directly if (to->userSpace == from->userSpace) continue; if (gradUnitSet && !coordSet) { //If fx and fx are not set, do not call recalc. if (STR_AS(radialTags[i].tag, "fx") && !(to->flags & SvgGradientFlags::Fx)) continue; if (STR_AS(radialTags[i].tag, "fy") && !(to->flags & SvgGradientFlags::Fy)) continue; radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace); } } } if (to->stops.empty()) _cloneGradStops(to->stops, from->stops); } static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from) { if (!from) return nullptr; auto grad = tvg::calloc(1, sizeof(SvgStyleGradient)); if (!grad) return nullptr; grad->type = from->type; grad->id = from->id ? _copyId(from->id) : nullptr; grad->ref = from->ref ? _copyId(from->ref) : nullptr; grad->spread = from->spread; grad->userSpace = from->userSpace; grad->flags = from->flags; if (from->transform) { grad->transform = tvg::calloc(1, sizeof(Matrix)); if (grad->transform) *grad->transform = *from->transform; } if (grad->type == SvgGradientType::Linear) { grad->linear = tvg::calloc(1, sizeof(SvgLinearGradient)); if (!grad->linear) goto error_grad_alloc; memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient)); } else if (grad->type == SvgGradientType::Radial) { grad->radial = tvg::calloc(1, sizeof(SvgRadialGradient)); if (!grad->radial) goto error_grad_alloc; memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient)); } _cloneGradStops(grad->stops, from->stops); return grad; error_grad_alloc: if (grad) { grad->clear(); tvg::free(grad); } return nullptr; } static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent) { if (parent == nullptr) return; //Inherit the property of parent if not present in child. if (!child->curColorSet) { child->color = parent->color; child->curColorSet = parent->curColorSet; } if (!(child->flags & SvgStyleFlags::PaintOrder)) { child->paintOrder = parent->paintOrder; } //Fill if (!(child->fill.flags & SvgFillFlags::Paint)) { child->fill.paint.color = parent->fill.paint.color; child->fill.paint.none = parent->fill.paint.none; child->fill.paint.curColor = parent->fill.paint.curColor; if (parent->fill.paint.url) { tvg::free(child->fill.paint.url); child->fill.paint.url = _copyId(parent->fill.paint.url); } } if (!(child->fill.flags & SvgFillFlags::Opacity)) { child->fill.opacity = parent->fill.opacity; } if (!(child->fill.flags & SvgFillFlags::FillRule)) { child->fill.fillRule = parent->fill.fillRule; } //Stroke if (!(child->stroke.flags & SvgStrokeFlags::Paint)) { child->stroke.paint.color = parent->stroke.paint.color; child->stroke.paint.none = parent->stroke.paint.none; child->stroke.paint.curColor = parent->stroke.paint.curColor; if (parent->stroke.paint.url) { tvg::free(child->stroke.paint.url); child->stroke.paint.url = _copyId(parent->stroke.paint.url); } } if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) { child->stroke.opacity = parent->stroke.opacity; } if (!(child->stroke.flags & SvgStrokeFlags::Width)) { child->stroke.width = parent->stroke.width; } if (!(child->stroke.flags & SvgStrokeFlags::Dash)) { if (parent->stroke.dash.array.count > 0) { child->stroke.dash.array.clear(); child->stroke.dash.array.reserve(parent->stroke.dash.array.count); ARRAY_FOREACH(p, parent->stroke.dash.array) { child->stroke.dash.array.push(*p); } } } if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) { child->stroke.dash.offset = parent->stroke.dash.offset; } if (!(child->stroke.flags & SvgStrokeFlags::Cap)) { child->stroke.cap = parent->stroke.cap; } if (!(child->stroke.flags & SvgStrokeFlags::Join)) { child->stroke.join = parent->stroke.join; } if (!(child->stroke.flags & SvgStrokeFlags::Miterlimit)) { child->stroke.miterlimit = parent->stroke.miterlimit; } } static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) { if (from == nullptr) return; //Copy the properties of 'from' only if they were explicitly set (not the default ones). if (from->curColorSet) { to->color = from->color; to->curColorSet = true; } if (from->flags & SvgStyleFlags::Opacity) { to->opacity = from->opacity; } if (from->flags & SvgStyleFlags::PaintOrder) { to->paintOrder = from->paintOrder; } if (from->flags & SvgStyleFlags::Display) { to->display = from->display; } //Fill to->fill.flags = (to->fill.flags | from->fill.flags); if (from->fill.flags & SvgFillFlags::Paint) { to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; if (from->fill.paint.url) { tvg::free(to->fill.paint.url); to->fill.paint.url = _copyId(from->fill.paint.url); } } if (from->fill.flags & SvgFillFlags::Opacity) { to->fill.opacity = from->fill.opacity; } if (from->fill.flags & SvgFillFlags::FillRule) { to->fill.fillRule = from->fill.fillRule; } //Stroke to->stroke.flags = (to->stroke.flags | from->stroke.flags); if (from->stroke.flags & SvgStrokeFlags::Paint) { to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; if (from->stroke.paint.url) { tvg::free(to->stroke.paint.url); to->stroke.paint.url = _copyId(from->stroke.paint.url); } } if (from->stroke.flags & SvgStrokeFlags::Opacity) { to->stroke.opacity = from->stroke.opacity; } if (from->stroke.flags & SvgStrokeFlags::Width) { to->stroke.width = from->stroke.width; } if (from->stroke.flags & SvgStrokeFlags::Dash) { if (from->stroke.dash.array.count > 0) { to->stroke.dash.array.clear(); to->stroke.dash.array.reserve(from->stroke.dash.array.count); ARRAY_FOREACH(p, from->stroke.dash.array) { to->stroke.dash.array.push(*p); } } } if (from->stroke.flags & SvgStrokeFlags::DashOffset) { to->stroke.dash.offset = from->stroke.dash.offset; } if (from->stroke.flags & SvgStrokeFlags::Cap) { to->stroke.cap = from->stroke.cap; } if (from->stroke.flags & SvgStrokeFlags::Join) { to->stroke.join = from->stroke.join; } if (from->stroke.flags & SvgStrokeFlags::Miterlimit) { to->stroke.miterlimit = from->stroke.miterlimit; } } static void _copyAttr(SvgNode* to, const SvgNode* from) { //Copy matrix attribute if (from->transform) { to->transform = tvg::malloc(sizeof(Matrix)); if (to->transform) *to->transform = *from->transform; } //Copy style attribute _styleCopy(to->style, from->style); to->style->flags = (to->style->flags | from->style->flags); if (from->style->clipPath.url) { tvg::free(to->style->clipPath.url); to->style->clipPath.url = duplicate(from->style->clipPath.url); } if (from->style->mask.url) { tvg::free(to->style->mask.url); to->style->mask.url = duplicate(from->style->mask.url); } if (from->style->filter.url) { if (to->style->filter.url) tvg::free(to->style->filter.url); to->style->filter.url = duplicate(from->style->filter.url); } //Copy node attribute switch (from->type) { case SvgNodeType::Circle: { to->node.circle = from->node.circle; break; } case SvgNodeType::Ellipse: { to->node.ellipse = from->node.ellipse; break; } case SvgNodeType::Rect: { to->node.rect = from->node.rect; break; } case SvgNodeType::Line: { to->node.line = from->node.line; break; } case SvgNodeType::Path: { if (from->node.path.path) { tvg::free(to->node.path.path); to->node.path.path = duplicate(from->node.path.path); } break; } case SvgNodeType::Polygon: { if ((to->node.polygon.pts.count = from->node.polygon.pts.count)) { to->node.polygon.pts = from->node.polygon.pts; } break; } case SvgNodeType::Polyline: { if ((to->node.polyline.pts.count = from->node.polyline.pts.count)) { to->node.polyline.pts = from->node.polyline.pts; } break; } case SvgNodeType::Image: { to->node.image.x = from->node.image.x; to->node.image.y = from->node.image.y; to->node.image.w = from->node.image.w; to->node.image.h = from->node.image.h; if (from->node.image.href) { tvg::free(to->node.image.href); to->node.image.href = duplicate(from->node.image.href); } break; } case SvgNodeType::Use: { to->node.use.x = from->node.use.x; to->node.use.y = from->node.use.y; to->node.use.w = from->node.use.w; to->node.use.h = from->node.use.h; to->node.use.isWidthSet = from->node.use.isWidthSet; to->node.use.isHeightSet = from->node.use.isHeightSet; to->node.use.symbol = from->node.use.symbol; break; } case SvgNodeType::Text: { to->node.text.x = from->node.text.x; to->node.text.y = from->node.text.y; to->node.text.fontSize = from->node.text.fontSize; if (from->node.text.text) { tvg::free(to->node.text.text); to->node.text.text = duplicate(from->node.text.text); } if (from->node.text.fontFamily) { tvg::free(to->node.text.fontFamily); to->node.text.fontFamily = duplicate(from->node.text.fontFamily); } break; } default: { break; } } } static void _cloneNode(SvgNode* from, SvgNode* parent, int depth) { /* Exception handling: Prevent invalid SVG data input. The size is the arbitrary value, we need an experimental size. */ if (depth == 8192) { TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); return; } SvgNode* newNode; if (!from || !parent || from == parent) return; newNode = _createNode(parent, from->type); if (!newNode) return; _styleInherit(newNode->style, parent->style); _copyAttr(newNode, from); ARRAY_FOREACH(p, from->child) { _cloneNode(*p, newNode, depth + 1); } } static void _clonePostponedNodes(Inlist* cloneNodes, SvgNode* doc) { auto nodeIdPair = cloneNodes->front(); while (nodeIdPair) { if (!_findParentById(nodeIdPair->node, nodeIdPair->id, doc)) { //Check if none of nodeFrom's children are in the cloneNodes list auto postpone = false; auto nodeFrom = _findNodeById(_getDefsNode(nodeIdPair->node), nodeIdPair->id); if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair->id); if (nodeFrom) { INLIST_FOREACH((*cloneNodes), pair) { if (_checkPostponed(nodeFrom, pair->node, 1)) { postpone = true; cloneNodes->back(nodeIdPair); break; } } } //Since none of the child nodes of nodeFrom are present in the cloneNodes list, it can be cloned immediately if (!postpone) { _cloneNode(nodeFrom, nodeIdPair->node, 0); if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair->node->type == SvgNodeType::Use) { nodeIdPair->node->node.use.symbol = nodeFrom; } tvg::free(nodeIdPair->id); tvg::free(nodeIdPair); } } else { TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", nodeIdPair->id); tvg::free(nodeIdPair->id); tvg::free(nodeIdPair); } nodeIdPair = cloneNodes->front(); } } static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content, unsigned int length) { const char* itr = nullptr; int sz = length; char tagName[20] = ""; content = svgUtilSkipWhiteSpace(content, nullptr); itr = content; while ((itr != nullptr) && *itr != '>') itr++; if (itr) { sz = itr - content; while ((sz > 0) && (isspace(content[sz - 1]))) sz--; if ((unsigned int)sz >= sizeof(tagName)) sz = sizeof(tagName) - 1; strncpy(tagName, content, sz); tagName[sz] = '\0'; } else return; for (unsigned int i = 0; i < sizeof(groupTags) / sizeof(groupTags[0]); i++) { if (!strncmp(tagName, groupTags[i].tag, sz)) { loader->stack.pop(); break; } } for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) { if (!strncmp(tagName, gradientTags[i].tag, sz)) { loader->gradientStack.pop(); break; } } for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) { if (!strncmp(tagName, graphicsTags[i].tag, sz)) { loader->currentGraphicsNode = nullptr; if (!strncmp(tagName, "text", 4)) loader->openedTag = OpenedTagType::Other; loader->stack.pop(); break; } } loader->level--; } static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty) { const char* attrs = nullptr; int attrsLength = 0; int sz = length; char tagName[20] = ""; FactoryMethod method; GradientFactoryMethod gradientMethod; SvgNode *node = nullptr, *parent = nullptr; loader->level++; attrs = xmlFindAttributesTag(content, length); if (!attrs) { //Parse the empty tag attrs = content; while ((attrs != nullptr) && *attrs != '>') attrs++; if (empty) attrs--; } if (attrs) { //Find out the tag name starting from content till sz length sz = attrs - content; while ((sz > 0) && (isspace(content[sz - 1]))) sz--; if ((unsigned)sz >= sizeof(tagName)) return; strncpy(tagName, content, sz); tagName[sz] = '\0'; attrsLength = length - sz; } if ((method = _findGroupFactory(tagName))) { //Group if (empty) return; if (!loader->doc) { if (!STR_AS(tagName, "svg")) return; //Not a valid svg document node = method(loader, nullptr, attrs, attrsLength, xmlParseAttributes); loader->doc = node; } else { if (STR_AS(tagName, "svg")) return; //Already loaded (SvgNodeType::Doc) tag if (loader->stack.count > 0) parent = loader->stack.last(); else parent = loader->doc; if (STR_AS(tagName, "style")) { // TODO: For now only the first style node is saved. After the css id selector // is introduced this if condition shouldn't be necessary any more if (!loader->cssStyle) { node = method(loader, nullptr, attrs, attrsLength, xmlParseAttributes); loader->cssStyle = node; loader->doc->node.doc.style = node; loader->openedTag = OpenedTagType::Style; } } else { node = method(loader, parent, attrs, attrsLength, xmlParseAttributes); } } if (!node) return; if (node->type != SvgNodeType::Defs || !empty) { loader->stack.push(node); } } else if ((method = _findGraphicsFactory(tagName))) { if (loader->stack.count > 0) parent = loader->stack.last(); else parent = loader->doc; node = method(loader, parent, attrs, attrsLength, xmlParseAttributes); if (node && !empty) { if (STR_AS(tagName, "text")) loader->openedTag = OpenedTagType::Text; auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr); loader->stack.push(defs); loader->currentGraphicsNode = node; } } else if ((gradientMethod = _findGradientFactory(tagName))) { SvgStyleGradient* gradient; gradient = gradientMethod(loader, attrs, attrsLength); //Gradients do not allow nested declarations, so only the earliest declared Gradient is valid. if (loader->gradientStack.count == 0) { //FIXME: The current parsing structure does not distinguish end tags. // There is no way to know if the currently parsed gradient is in defs. // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs. // But finally, the loader has a gradient style list regardless of defs. // This is only to support this when multiple gradients are declared, even if no defs are declared. // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs if (loader->def && loader->doc->node.doc.defs) { loader->def->node.defs.gradients.push(gradient); } else { loader->gradients.push(gradient); } } if (!empty) loader->gradientStack.push(gradient); } else if (STR_AS(tagName, "stop")) { if (loader->gradientStack.count == 0) { TVGLOG("SVG", "Stop element is used outside of the Gradient element"); return; } /* default value for opacity */ loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255}; loader->svgParse->flags = SvgStopStyleFlags::StopDefault; xmlParseAttributes(attrs, attrsLength, _attrParseStops, loader); loader->gradientStack.last()->stops.push(loader->svgParse->gradStop); } else { if (!isIgnoreUnsupportedLogElements(tagName)) TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName); } } static void _svgLoaderParserText(SvgLoaderData* loader, const char* content, unsigned int length) { auto& text = loader->svgParse->node->node.text; text.text = append(text.text, content, length); } static char* _parseName(char* str, const char* delims, char** saveptr) { auto pch = strtok_r(str, delims, saveptr); while (pch) { while (*pch && isspace(*pch)) pch++; if (*pch == '\0') { pch = strtok_r(nullptr, delims, saveptr); continue; } auto end = pch + strlen(pch) - 1; while (end > pch && isspace(*end)) *end-- = '\0'; if (*pch == '\0') { pch = strtok_r(nullptr, delims, saveptr); continue; } break; } return pch; } static void _free(SvgStyleProperty* style) { if (!style) return; //style->clipPath.node/mask.node/filter.node has only the addresses of node. Therefore, node is released from _freeNode. tvg::free(style->clipPath.url); tvg::free(style->mask.url); tvg::free(style->filter.url); tvg::free(style->cssClass); if (style->fill.paint.gradient) { style->fill.paint.gradient->clear(); tvg::free(style->fill.paint.gradient); } if (style->stroke.paint.gradient) { style->stroke.paint.gradient->clear(); tvg::free(style->stroke.paint.gradient); } tvg::free(style->fill.paint.url); tvg::free(style->stroke.paint.url); style->stroke.dash.array.reset(); tvg::free(style); } static void _free(SvgNode* node) { if (!node) return; ARRAY_FOREACH(p, node->child) _free(*p); node->child.reset(); tvg::free(node->id); tvg::free(node->transform); _free(node->style); switch (node->type) { case SvgNodeType::Path: { tvg::free(node->node.path.path); break; } case SvgNodeType::Polygon: { tvg::free(node->node.polygon.pts.data); break; } case SvgNodeType::Polyline: { tvg::free(node->node.polyline.pts.data); break; } case SvgNodeType::Doc: { _free(node->node.doc.defs); _free(node->node.doc.style); break; } case SvgNodeType::Defs: { ARRAY_FOREACH(p, node->node.defs.gradients) { (*p)->clear(); tvg::free(*p); } node->node.defs.gradients.reset(); break; } case SvgNodeType::Image: { tvg::free(node->node.image.href); break; } case SvgNodeType::Text: { tvg::free(node->node.text.text); tvg::free(node->node.text.fontFamily); break; } default: { break; } } tvg::free(node); } static bool _cssApplyClass(SvgNode* node, const char* classString, SvgNode* styleRoot) { if (!classString || !styleRoot) return false; auto classes = duplicate(classString); bool allFound = true; auto tempNode = tvg::calloc(1, sizeof(SvgNode)); tempNode->style = tvg::calloc(1, sizeof(SvgStyleProperty)); tempNode->type = node->type; tempNode->style->opacity = 255; tempNode->style->fill.opacity = 255; tempNode->style->stroke.opacity = 255; char* tokPtr = nullptr; auto name = _parseName(classes, " ", &tokPtr); tvg::Array applyClasses; while (name) { auto isDuplicate = false; ARRAY_FOREACH(p, applyClasses) { if (STR_AS(*p, name)) { isDuplicate = true; break; } } if (isDuplicate) { name = _parseName(nullptr, " ", &tokPtr); continue; } applyClasses.push(name); auto found = false; //css styling: tag.name has higher priority than .name if (auto cssNode = cssFindStyleNode(styleRoot, name)) { cssCopyStyleAttr(tempNode, cssNode, true); found = true; } if (auto cssNode = cssFindStyleNode(styleRoot, name, node->type)) { cssCopyStyleAttr(tempNode, cssNode, true); found = true; } if (!found) allFound = false; name = _parseName(nullptr, " ", &tokPtr); } tvg::free(classes); //Apply the merged style to the node (without overwriting existing styles) cssCopyStyleAttr(node, tempNode); _free(tempNode); return allFound; } static void _cssApplyStyleToPostponeds(Array& postponeds, SvgNode* style) { ARRAY_FOREACH(p, postponeds) { auto nodeIdPair = *p; _cssApplyClass(nodeIdPair.node, nodeIdPair.id, style); } } static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length) { char* tag; char* name; const char* attrs = nullptr; unsigned int attrsLength = 0; FactoryMethod method; GradientFactoryMethod gradientMethod; SvgNode *node = nullptr; while (auto next = xmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) { if ((method = _findGroupFactory(tag))) { if ((node = method(loader, loader->cssStyle, attrs, attrsLength, xmlParseW3CAttribute))) node->id = _copyId(name); } else if ((method = _findGraphicsFactory(tag))) { if ((node = method(loader, loader->cssStyle, attrs, attrsLength, xmlParseW3CAttribute))) node->id = _copyId(name); } else if ((gradientMethod = _findGradientFactory(tag))) { TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } else if (STR_AS(tag, "stop")) { TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } else if (STR_AS(tag, "all")) { char* tokPtr = nullptr; auto id = _parseName(name, ",", &tokPtr); while (id) { if (*id == '.') id++; if (auto cssNode = cssFindStyleNode(loader->cssStyle, id)) { auto oldNode = loader->svgParse->node; loader->svgParse->node = cssNode; xmlParseW3CAttribute(attrs, attrsLength, _attrParseCssStyleNode, loader); loader->svgParse->node = oldNode; } else { if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, xmlParseW3CAttribute))) { node->id = _copyId(id); } } id = _parseName(nullptr, ",", &tokPtr); } } else if (STR_AS(tag, "@font-face")) { //css at-rule specifying font _createFontFace(loader, attrs, attrsLength, xmlParseW3CAttribute); } else if (!isIgnoreUnsupportedLogElements(tag)) { TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } length -= next - content; content = next; tvg::free(tag); tvg::free(name); } loader->openedTag = OpenedTagType::Other; } static bool _svgLoaderParser(void* data, XMLType type, const char* content, unsigned int length) { SvgLoaderData* loader = (SvgLoaderData*)data; switch (type) { case XMLType::Open: { _svgLoaderParserXmlOpen(loader, content, length, false); break; } case XMLType::OpenEmpty: { _svgLoaderParserXmlOpen(loader, content, length, true); break; } case XMLType::Close: { _svgLoaderParserXmlClose(loader, content, length); break; } case XMLType::Data: case XMLType::CData: { if (loader->openedTag == OpenedTagType::Style) _svgLoaderParserXmlCssStyle(loader, content, length); else if (loader->openedTag == OpenedTagType::Text) _svgLoaderParserText(loader, content, length); break; } case XMLType::DoctypeChild: { break; } case XMLType::Ignored: case XMLType::Comment: case XMLType::Doctype: { break; } default: { break; } } return true; } static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle) { _styleInherit(node->style, parentStyle); ARRAY_FOREACH(p, node->child) { _updateStyle(*p, node->style); } } static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array* gradients) { auto duplicate = [&](SvgLoaderData* loader, Array* gradients, const char* id) -> SvgStyleGradient* { SvgStyleGradient* result = nullptr; ARRAY_FOREACH(p, *gradients) { if ((*p)->id && STR_AS((*p)->id, id)) { result = _cloneGradient(*p); break; } } if (result && result->ref) { ARRAY_FOREACH(p, *gradients) { if ((*p)->id && STR_AS((*p)->id, result->ref)) { _inheritGradient(loader, result, *p); break; } } } return result; }; if (node->child.count > 0) { ARRAY_FOREACH(p, node->child) { _updateGradient(loader, *p, gradients); } } else { if (node->style->fill.paint.url) { auto newGrad = duplicate(loader, gradients, node->style->fill.paint.url); if (newGrad) { if (node->style->fill.paint.gradient) { node->style->fill.paint.gradient->clear(); tvg::free(node->style->fill.paint.gradient); } node->style->fill.paint.gradient = newGrad; } } if (node->style->stroke.paint.url) { auto newGrad = duplicate(loader, gradients, node->style->stroke.paint.url); if (newGrad) { if (node->style->stroke.paint.gradient) { node->style->stroke.paint.gradient->clear(); tvg::free(node->style->stroke.paint.gradient); } node->style->stroke.paint.gradient = newGrad; } } } } static void _updateComposite(SvgNode* node, SvgNode* root) { if (node->style->clipPath.url && !node->style->clipPath.node) { SvgNode* findResult = _findNodeById(root, node->style->clipPath.url); if (findResult) node->style->clipPath.node = findResult; } if (node->style->mask.url && !node->style->mask.node) { SvgNode* findResult = _findNodeById(root, node->style->mask.url); if (findResult) node->style->mask.node = findResult; } if (node->child.count > 0) { ARRAY_FOREACH(p, node->child) { _updateComposite(*p, root); } } } static void _updateFilter(SvgNode* node, SvgNode* root) { if (node->style->filter.url && !node->style->filter.node) { node->style->filter.node = _findNodeById(root, node->style->filter.url); } ARRAY_FOREACH(child, node->child) { _updateFilter(*child, root); } } static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length) { const char* attrs = nullptr; int sz = length; char tagName[20] = ""; FactoryMethod method; SvgNode *node = nullptr; int attrsLength = 0; loader->level++; attrs = xmlFindAttributesTag(content, length); if (!attrs) { //Parse the empty tag attrs = content; while ((attrs != nullptr) && *attrs != '>') attrs++; } if (attrs) { sz = attrs - content; while ((sz > 0) && (isspace((unsigned char)content[sz - 1]))) sz--; if ((unsigned)sz >= sizeof(tagName)) return false; strncpy(tagName, content, sz); tagName[sz] = '\0'; attrsLength = length - sz; } if ((method = _findGroupFactory(tagName))) { if (!loader->doc) { if (!STR_AS(tagName, "svg")) return true; //Not a valid svg document node = method(loader, nullptr, attrs, attrsLength, xmlParseAttributes); loader->doc = node; loader->stack.push(node); return false; } } return true; } static bool _svgLoaderParserForValidCheck(void* data, XMLType type, const char* content, unsigned int length) { switch (type) { case XMLType::Open: case XMLType::OpenEmpty: { //If 'res' is false, it means tag is found. return _svgLoaderParserForValidCheckXmlOpen(static_cast(data), content, length); } default: return true; } } void SvgLoader::clear(bool all) { //flush out the intermediate data tvg::free(loaderData.svgParse); loaderData.svgParse = nullptr; ARRAY_FOREACH(p, loaderData.gradients) { (*p)->clear(); tvg::free(*p); } loaderData.gradients.reset(); loaderData.gradientStack.reset(); _free(loaderData.doc); loaderData.doc = nullptr; loaderData.stack.reset(); if (!all) return; ARRAY_FOREACH(p, loaderData.images) tvg::free(*p); loaderData.images.reset(); ARRAY_FOREACH(p, loaderData.fonts) { Text::unload(p->name); tvg::free(p->decoded); tvg::free(p->name); } loaderData.fonts.reset(); if (copy) tvg::free((char*)content); if (root) { root->unref(); root = nullptr; } size = 0; content = nullptr; copy = false; } void SvgLoader::run(unsigned tid) { //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vbox.w) <= FLOAT_EPSILON || fabsf(vbox.h) <= FLOAT_EPSILON)) { TVGLOG("SVG", "The width and/or height set to 0 - rendering disabled."); root = Scene::gen(); } else { if (xmlParse(content, size, true, _svgLoaderParser, &(loaderData))) { if (loaderData.doc) { auto defs = loaderData.doc->node.doc.defs; if (loaderData.nodesToStyle.count > 0) _cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle); if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle); if (!loaderData.cloneNodes.empty()) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc); _updateComposite(loaderData.doc, loaderData.doc); if (defs) _updateComposite(loaderData.doc, defs); _updateFilter(loaderData.doc, loaderData.doc); if (defs) _updateFilter(loaderData.doc, defs); _updateStyle(loaderData.doc, nullptr); if (defs) _updateStyle(defs, nullptr); if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients); root = svgSceneBuild(loaderData, vbox, w, h, align, meetOrSlice, svgPath, viewFlag); //In case no viewbox and width/height data is provided the completion of loading //has to be forced, in order to establish this data based on the whole picture. if (!(viewFlag & SvgViewFlag::Viewbox)) { //Override viewbox & size again after svg loading. vbox = loaderData.doc->node.doc.vbox; w = loaderData.doc->node.doc.w; h = loaderData.doc->node.doc.h; } } } } root->ref(); clear(false); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ SvgLoader::SvgLoader() : ImageLoader(FileType::Svg) { } SvgLoader::~SvgLoader() { done(); clear(); } bool SvgLoader::header() { //For valid check, only tag is parsed first. //If the tag is found, the loaded file is valid and stores viewbox information. //After that, the remaining content data is parsed in order with async. loaderData.svgParse = tvg::malloc(sizeof(SvgParser)); loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault; viewFlag = SvgViewFlag::None; xmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData)); if (!loaderData.doc || loaderData.doc->type != SvgNodeType::Doc) { TVGLOG("SVG", "No SVG File. There is no "); return false; } viewFlag = loaderData.doc->node.doc.viewFlag; align = loaderData.doc->node.doc.align; meetOrSlice = loaderData.doc->node.doc.meetOrSlice; if (viewFlag & SvgViewFlag::Viewbox) { vbox = loaderData.doc->node.doc.vbox; if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w; else { w = loaderData.doc->node.doc.vbox.w; if (viewFlag & SvgViewFlag::WidthInPercent) { w *= loaderData.doc->node.doc.w; viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent); } viewFlag = (viewFlag | SvgViewFlag::Width); } if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h; else { h = loaderData.doc->node.doc.vbox.h; if (viewFlag & SvgViewFlag::HeightInPercent) { h *= loaderData.doc->node.doc.h; viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent); } viewFlag = (viewFlag | SvgViewFlag::Height); } //In case no viewbox and width/height data is provided the completion of loading //has to be forced, in order to establish this data based on the whole picture. } else { //Before loading, set default viewbox & size if they are empty vbox.x = vbox.y = 0.0f; if (viewFlag & SvgViewFlag::Width) { vbox.w = w = loaderData.doc->node.doc.w; } else { vbox.w = 1.0f; if (viewFlag & SvgViewFlag::WidthInPercent) { w = loaderData.doc->node.doc.w; } else w = 1.0f; } if (viewFlag & SvgViewFlag::Height) { vbox.h = h = loaderData.doc->node.doc.h; } else { vbox.h = 1.0f; if (viewFlag & SvgViewFlag::HeightInPercent) { h = loaderData.doc->node.doc.h; } else h = 1.0f; } run(0); } return true; } bool SvgLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy) { if (copy) { content = tvg::malloc(size + 1); if (!content) return false; memcpy((char*)content, data, size); content[size] = '\0'; } else content = (char*)data; this->size = size; this->copy = copy; return header(); } bool SvgLoader::open(const char* path) { #ifdef THORVG_FILE_IO_SUPPORT if ((content = LoadModule::open(path, size, true))) { copy = true; return header(); } #endif return false; } bool SvgLoader::resize(Paint* paint, float w, float h) { if (!paint) return false; auto sx = w / this->w; auto sy = h / this->h; Matrix m = {sx, 0, 0, 0, sy, 0, 0, 0, 1}; paint->transform(m); return true; } bool SvgLoader::read() { if (!content || size == 0) return false; if (!LoadModule::read() || root) return true; TaskScheduler::request(this); return true; } bool SvgLoader::close() { if (!LoadModule::close()) return false; this->done(); clear(); return true; } Paint* SvgLoader::paint() { this->done(); if (root) { //Primary usage: sharing the svg if (root->refCnt() == 1) return root; return root->duplicate(); } return nullptr; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlGeometry.cpp���000664 �001750 �001750 �00000030643 15164251010 036773� 0����������������������������������������������������������������������������������������������������ustar�00ddennedy������������������������ddennedy������������������������000000 �000000 �mlt-7.38.0�������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlCommon.h" #include "tvgGlGpuBuffer.h" #include "tvgGlRenderTask.h" #include "tvgGlTessellator.h" static RenderRegion _transformBounds(const RenderRegion& bounds, const Matrix& matrix) { if (bounds.invalid()) return bounds; auto lt = Point{float(bounds.min.x), float(bounds.min.y)} * matrix; auto lb = Point{float(bounds.min.x), float(bounds.max.y)} * matrix; auto rt = Point{float(bounds.max.x), float(bounds.min.y)} * matrix; auto rb = Point{float(bounds.max.x), float(bounds.max.y)} * matrix; auto left = min(min(lt.x, lb.x), min(rt.x, rb.x)); auto top = min(min(lt.y, lb.y), min(rt.y, rb.y)); auto right = max(max(lt.x, lb.x), max(rt.x, rb.x)); auto bottom = max(max(lt.y, lb.y), max(rt.y, rb.y)); return RenderRegion{{int32_t(floor(left)), int32_t(floor(top))}, {int32_t(ceil(right)), int32_t(ceil(bottom))}}; } bool GlIntersector::isPointInTriangle(const Point& p, const Point& a, const Point& b, const Point& c) { auto d1 = tvg::cross(p - a, p - b); auto d2 = tvg::cross(p - b, p - c); auto d3 = tvg::cross(p - c, p - a); auto has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0); auto has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0); return !(has_neg && has_pos); } bool GlIntersector::isPointInImage(const Point& p, const GlGeometryBuffer& mesh, const Matrix& tr) { for (uint32_t i = 0; i < mesh.index.count; i += 3) { auto p0 = Point{mesh.vertex[mesh.index[i+0]*4+0], mesh.vertex[mesh.index[i+0]*4+1]} * tr; auto p1 = Point{mesh.vertex[mesh.index[i+1]*4+0], mesh.vertex[mesh.index[i+1]*4+1]} * tr; auto p2 = Point{mesh.vertex[mesh.index[i+2]*4+0], mesh.vertex[mesh.index[i+2]*4+1]} * tr; if (isPointInTriangle(p, p0, p1, p2)) return true; } return false; } // triangle list bool GlIntersector::isPointInTris(const Point& p, const GlGeometryBuffer& mesh, const Matrix& tr) { for (uint32_t i = 0; i < mesh.index.count; i += 3) { auto p0 = Point{mesh.vertex[mesh.index[i+0]*2+0], mesh.vertex[mesh.index[i+0]*2+1]} * tr; auto p1 = Point{mesh.vertex[mesh.index[i+1]*2+0], mesh.vertex[mesh.index[i+1]*2+1]} * tr; auto p2 = Point{mesh.vertex[mesh.index[i+2]*2+0], mesh.vertex[mesh.index[i+2]*2+1]} * tr; if (isPointInTriangle(p, p0, p1, p2)) return true; } return false; } // even-odd triangle list bool GlIntersector::isPointInMesh(const Point& p, const GlGeometryBuffer& mesh, const Matrix& tr) { uint32_t crossings = 0; for (uint32_t i = 0; i < mesh.index.count; i += 3) { Point triangle[3] = { Point{mesh.vertex[mesh.index[i+0]*2+0], mesh.vertex[mesh.index[i+0]*2+1]} * tr, Point{mesh.vertex[mesh.index[i+1]*2+0], mesh.vertex[mesh.index[i+1]*2+1]} * tr, Point{mesh.vertex[mesh.index[i+2]*2+0], mesh.vertex[mesh.index[i+2]*2+1]} * tr }; for (uint32_t j = 0; j < 3; j++) { auto p1 = triangle[j]; auto p2 = triangle[(j + 1) % 3]; if (p1.y == p2.y) continue; if (p1.y > p2.y) std::swap(p1, p2); if ((p.y > p1.y) && (p.y <= p2.y)) { auto intersectionX = (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x; if (intersectionX > p.x) crossings++; } } } return (crossings % 2) == 1; } bool GlIntersector::intersectClips(const Point& pt, const tvg::Array& clips) { for (uint32_t i = 0; i < clips.count; i++) { auto clip = (GlShape*)clips[i]; if (!isPointInMesh(pt, clip->geometry.fill, clip->geometry.fillWorld ? tvg::identity() : clip->geometry.matrix)) return false; } return true; } bool GlIntersector::intersectShape(const RenderRegion region, const GlShape* shape) { if (!shape || ((shape->geometry.fill.index.count == 0) && (shape->geometry.stroke.index.count == 0))) return false; auto sizeX = region.sw(); auto sizeY = region.sh(); for (int32_t y = 0; y <= sizeY; y++) { for (int32_t x = 0; x <= sizeX; x++) { Point pt{(float)x + region.min.x, (float)y + region.min.y}; if (y % 2 == 1) pt.y = (float)sizeY - y - sizeY % 2 + region.min.y; if (intersectClips(pt, shape->clips)) { if (shape->validFill && isPointInMesh(pt, shape->geometry.fill, shape->geometry.fillWorld ? tvg::identity() : shape->geometry.matrix)) return true; if (shape->validStroke && isPointInTris(pt, shape->geometry.stroke, tvg::identity())) return true; } } } return false; } bool GlIntersector::intersectImage(const RenderRegion region, const GlShape* image) { if (image) { auto sizeX = region.sw(); auto sizeY = region.sh(); for (int32_t y = 0; y <= sizeY; y++) { for (int32_t x = 0; x <= sizeX; x++) { Point pt{(float) x + region.min.x, (float) y + region.min.y}; if (y % 2 == 1) pt.y = (float) sizeY - y - sizeY % 2 + region.min.y; if (intersectClips(pt, image->clips) && isPointInImage(pt, image->geometry.fill, image->geometry.fillWorld ? tvg::identity() : image->geometry.matrix)) return true; } } } return false; } void GlGeometry::prepare(const RenderShape& rshape) { if (rshape.trimpath()) { RenderPath trimmedPath; if (rshape.stroke->trim.trim(rshape.path, trimmedPath)) { trimmedPath.optimizeGL(optPath, matrix); } else { optPath.clear(); } } else { rshape.path.optimizeGL(optPath, matrix); } } bool GlGeometry::tesselateShape(const RenderShape& rshape, float* opacityMultiplier) { fill.clear(); fillBounds = {}; fillWorld = true; convex = false; // When the CTM scales a filled path so small that its device-space // World: [========] // normal-sized filled path // After CTM: [.] // thinner than 1 px in device space // Handling: two points // collapse to a 2-point handle for stability if (optPath.pts.count == 2 && tvg::zero(rshape.strokeWidth())) { if (tesselateLine(optPath)) { // The time spent is similar to substituting buffers in tessellation, so we just move the buffers to keep the code simple. stroke.index.move(fill.index); stroke.vertex.move(fill.vertex); fillBounds = strokeBounds; strokeBounds = {}; strokeRenderWidth = 0.0f; if (opacityMultiplier) *opacityMultiplier = MIN_GL_STROKE_ALPHA; fillRule = rshape.rule; return true; } return false; } // Handle normal shapes with more than 2 points BWTessellator bwTess{&fill}; bwTess.tessellate(optPath); fillRule = rshape.rule; fillBounds = bwTess.bounds(); convex = bwTess.convex; if (opacityMultiplier) *opacityMultiplier = 1.0f; return true; } bool GlGeometry::tesselateLine(const RenderPath& path) { stroke.clear(); strokeBounds = {}; strokeRenderWidth = MIN_GL_STROKE_WIDTH; if (path.pts.count != 2) return false; Stroker stroker(&stroke, MIN_GL_STROKE_WIDTH, StrokeCap::Butt, StrokeJoin::Bevel); stroker.run(path); strokeBounds = stroker.bounds(); return true; } bool GlGeometry::tesselateStroke(const RenderShape& rshape) { stroke.clear(); strokeBounds = {}; strokeRenderWidth = 0.0f; auto strokeWidth = 0.0f; if (isinf(matrix.e11)) { strokeWidth = rshape.strokeWidth() * scaling(matrix); if (strokeWidth <= MIN_GL_STROKE_WIDTH) strokeWidth = MIN_GL_STROKE_WIDTH; strokeWidth = strokeWidth / matrix.e11; } else { strokeWidth = rshape.strokeWidth(); } //run stroking only if it's valid auto strokeWidthWorld = strokeWidth * scaling(matrix); if (!std::isfinite(strokeWidthWorld)) strokeWidthWorld = strokeWidth; if (!tvg::zero(strokeWidthWorld)) { Stroker stroker(&stroke, strokeWidthWorld, rshape.strokeCap(), rshape.strokeJoin(), rshape.strokeMiterlimit()); RenderPath dashedPathWorld; if (rshape.strokeDash(dashedPathWorld, &matrix)) stroker.run(dashedPathWorld); else stroker.run(optPath); strokeBounds = stroker.bounds(); strokeRenderWidth = strokeWidthWorld; return true; } return false; } void GlGeometry::tesselateImage(const RenderSurface* image) { fill.clear(); fillBounds = {}; fillWorld = true; strokeRenderWidth = 0.0f; fill.vertex.reserve(5 * 4); fill.index.reserve(6); auto leftTop = Point{0.f, 0.f} * matrix; auto leftBottom = Point{0.f, float(image->h)} * matrix; auto rightTop = Point{float(image->w), 0.f} * matrix; auto rightBottom = Point{float(image->w), float(image->h)} * matrix; auto appendVertex = [&](const Point& pt, float u, float v) { fill.vertex.push(pt.x); fill.vertex.push(pt.y); fill.vertex.push(u); fill.vertex.push(v); }; appendVertex(leftTop, 0.f, 1.f); appendVertex(leftBottom, 0.f, 0.f); appendVertex(rightTop, 1.f, 1.f); appendVertex(rightBottom, 1.f, 0.f); fill.index.push(0); fill.index.push(1); fill.index.push(2); fill.index.push(2); fill.index.push(1); fill.index.push(3); fillBounds = _transformBounds(RenderRegion{{0, 0}, {int32_t(image->w), int32_t(image->h)}}, matrix); } bool GlGeometry::draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdateFlag flag) { if (flag == RenderUpdateFlag::None) return false; auto buffer = ((flag & RenderUpdateFlag::Stroke) || (flag & RenderUpdateFlag::GradientStroke)) ? &stroke : &fill; if (buffer->index.empty()) return false; auto vertexOffset = gpuBuffer->push(buffer->vertex.data, buffer->vertex.count * sizeof(float)); auto indexOffset = gpuBuffer->pushIndex(buffer->index.data, buffer->index.count * sizeof(uint32_t)); if (flag & RenderUpdateFlag::Image) { // image has two attribute: [pos, uv] task->addVertexLayout(GlVertexLayout{0, 2, 4 * sizeof(float), vertexOffset}); task->addVertexLayout(GlVertexLayout{1, 2, 4 * sizeof(float), vertexOffset + 2 * sizeof(float)}); } else { task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), vertexOffset}); } task->setDrawRange(indexOffset, buffer->index.count); return true; } GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag) { if (flag & RenderUpdateFlag::Stroke) return GlStencilMode::Stroke; if (flag & RenderUpdateFlag::GradientStroke) return GlStencilMode::Stroke; if (flag & RenderUpdateFlag::Image) return GlStencilMode::None; if (convex) return GlStencilMode::None; if (fillRule == FillRule::NonZero) return GlStencilMode::FillNonZero; if (fillRule == FillRule::EvenOdd) return GlStencilMode::FillEvenOdd; return GlStencilMode::None; } RenderRegion GlGeometry::getBounds() const { auto bounds = RenderRegion{}; auto hasBounds = false; if (!fill.index.empty()) { auto fill = fillWorld ? fillBounds : _transformBounds(fillBounds, matrix); if (fill.valid()) { bounds = fill; hasBounds = true; } } if (!stroke.index.empty()) { auto stroke = strokeBounds; if (stroke.valid()) { if (hasBounds) bounds.add(stroke); else { bounds = stroke; hasBounds = true; } } } if (hasBounds) return bounds; return {}; } ���������������������������������������������������������������������������������������������modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/api/�000775 �001750 �001750 �00000000000 15164251010 040070� 5����������������������������������������������������������������������������������������������������ustar�00ddennedy������������������������ddennedy������������������������000000 �000000 �mlt-7.38.0/src���������������������������������������������������������������������������������������������������������������������������������������������������������mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/vp8.cpp����000664 �001750 �001750 �00000047601 15164251010 034356� 0����������������������������������������������������������������������������������������������������ustar�00ddennedy������������������������ddennedy������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // main entry for the decoder // // Author: Skal (pascal.massimino@gmail.com) #include "tvgCommon.h" #include "./alphai.h" #include "./vp8i.h" #include "./vp8li.h" #include "./webpi.h" #include "../utils/bit_reader_inl.h" #include "../utils/utils.h" //------------------------------------------------------------------------------ int WebPGetDecoderVersion(void) { return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION; } //------------------------------------------------------------------------------ // VP8Decoder static void SetOk(VP8Decoder* const dec) { dec->status_ = VP8_STATUS_OK; dec->error_msg_ = "OK"; } int VP8InitIoInternal(VP8Io* const io, int version) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { return 0; // mismatch error } if (io != NULL) { memset(io, 0, sizeof(*io)); } return 1; } VP8Decoder* VP8New(void) { VP8Decoder* const dec = tvg::calloc(1ULL, sizeof(*dec)); if (dec != NULL) { SetOk(dec); dec->ready_ = 0; dec->num_parts_ = 1; } return dec; } VP8StatusCode VP8Status(VP8Decoder* const dec) { if (!dec) return VP8_STATUS_INVALID_PARAM; return dec->status_; } const char* VP8StatusMessage(VP8Decoder* const dec) { if (dec == NULL) return "no object"; if (!dec->error_msg_) return "OK"; return dec->error_msg_; } void VP8Delete(VP8Decoder* const dec) { if (dec != NULL) { VP8Clear(dec); tvg::free(dec); } } int VP8SetError(VP8Decoder* const dec, VP8StatusCode error, const char* const msg) { // The oldest error reported takes precedence over the new one. if (dec->status_ == VP8_STATUS_OK) { dec->status_ = error; dec->error_msg_ = msg; dec->ready_ = 0; } return 0; } //------------------------------------------------------------------------------ int VP8CheckSignature(const uint8_t* const data, size_t data_size) { return (data_size >= 3 && data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a); } int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size, int* const width, int* const height) { if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) { return 0; // not enough data } // check signature if (!VP8CheckSignature(data + 3, data_size - 3)) { return 0; // Wrong signature. } else { const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16); const int key_frame = !(bits & 1); const int w = ((data[7] << 8) | data[6]) & 0x3fff; const int h = ((data[9] << 8) | data[8]) & 0x3fff; if (!key_frame) { // Not a keyframe. return 0; } if (((bits >> 1) & 7) > 3) { return 0; // unknown profile } if (!((bits >> 4) & 1)) { return 0; // first frame is invisible! } if (((bits >> 5)) >= chunk_size) { // partition_length return 0; // inconsistent size information. } if (w == 0 || h == 0) { return 0; // We don't support both width and height to be zero. } if (width) { *width = w; } if (height) { *height = h; } return 1; } } //------------------------------------------------------------------------------ // Header parsing static void ResetSegmentHeader(VP8SegmentHeader* const hdr) { assert(hdr != NULL); hdr->use_segment_ = 0; hdr->update_map_ = 0; hdr->absolute_delta_ = 1; memset(hdr->quantizer_, 0, sizeof(hdr->quantizer_)); memset(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_)); } // Paragraph 9.3 static int ParseSegmentHeader(VP8BitReader* br, VP8SegmentHeader* hdr, VP8Proba* proba) { assert(br != NULL); assert(hdr != NULL); hdr->use_segment_ = VP8Get(br); if (hdr->use_segment_) { hdr->update_map_ = VP8Get(br); if (VP8Get(br)) { // update data int s; hdr->absolute_delta_ = VP8Get(br); for (s = 0; s < NUM_MB_SEGMENTS; ++s) { hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0; } for (s = 0; s < NUM_MB_SEGMENTS; ++s) { hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0; } } if (hdr->update_map_) { int s; for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) { proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u; } } } else { hdr->update_map_ = 0; } return !br->eof_; } // Paragraph 9.5 // This function returns VP8_STATUS_SUSPENDED if we don't have all the // necessary data in 'buf'. // This case is not necessarily an error (for incremental decoding). // Still, no bitreader is ever initialized to make it possible to read // unavailable memory. // If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA // is returned, and this is an unrecoverable error. // If the partitions were positioned ok, VP8_STATUS_OK is returned. static VP8StatusCode ParsePartitions(VP8Decoder* const dec, const uint8_t* buf, size_t size) { VP8BitReader* const br = &dec->br_; const uint8_t* sz = buf; const uint8_t* buf_end = buf + size; const uint8_t* part_start; int last_part; int p; dec->num_parts_ = 1 << VP8GetValue(br, 2); last_part = dec->num_parts_ - 1; part_start = buf + last_part * 3; if (buf_end < part_start) { // we can't even read the sizes with sz[]! That's a failure. return VP8_STATUS_NOT_ENOUGH_DATA; } for (p = 0; p < last_part; ++p) { const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16); const uint8_t* part_end = part_start + psize; if (part_end > buf_end) part_end = buf_end; VP8InitBitReader(dec->parts_ + p, part_start, part_end); part_start = part_end; sz += 3; } VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end); return (part_start < buf_end) ? VP8_STATUS_OK : VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data } // Paragraph 9.4 static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) { VP8FilterHeader* const hdr = &dec->filter_hdr_; hdr->simple_ = VP8Get(br); hdr->level_ = VP8GetValue(br, 6); hdr->sharpness_ = VP8GetValue(br, 3); hdr->use_lf_delta_ = VP8Get(br); if (hdr->use_lf_delta_) { if (VP8Get(br)) { // update lf-delta? int i; for (i = 0; i < NUM_REF_LF_DELTAS; ++i) { if (VP8Get(br)) { hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6); } } for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) { if (VP8Get(br)) { hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6); } } } } dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2; return !br->eof_; } // Topmost call int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { const uint8_t* buf; size_t buf_size; VP8FrameHeader* frm_hdr; VP8PictureHeader* pic_hdr; VP8BitReader* br; VP8StatusCode status; if (dec == NULL) { return 0; } SetOk(dec); if (io == NULL) { return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, "null VP8Io passed to VP8GetHeaders()"); } buf = io->data; buf_size = io->data_size; if (buf_size < 4) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "Truncated header."); } // Paragraph 9.1 { const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16); frm_hdr = &dec->frm_hdr_; frm_hdr->key_frame_ = !(bits & 1); frm_hdr->profile_ = (bits >> 1) & 7; frm_hdr->show_ = (bits >> 4) & 1; frm_hdr->partition_length_ = (bits >> 5); if (frm_hdr->profile_ > 3) return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "Incorrect keyframe parameters."); if (!frm_hdr->show_) return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, "Frame not displayable."); buf += 3; buf_size -= 3; } pic_hdr = &dec->pic_hdr_; if (frm_hdr->key_frame_) { // Paragraph 9.2 if (buf_size < 7) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "cannot parse picture header"); } if (!VP8CheckSignature(buf, buf_size)) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "Bad code word"); } pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff; pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2 pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff; pic_hdr->yscale_ = buf[6] >> 6; buf += 7; buf_size -= 7; dec->mb_w_ = (pic_hdr->width_ + 15) >> 4; dec->mb_h_ = (pic_hdr->height_ + 15) >> 4; // Setup default output area (can be later modified during io->setup()) io->width = pic_hdr->width_; io->height = pic_hdr->height_; io->use_scaling = 0; io->use_cropping = 0; io->crop_top = 0; io->crop_left = 0; io->crop_right = io->width; io->crop_bottom = io->height; io->mb_w = io->width; // sanity check io->mb_h = io->height; // ditto VP8ResetProba(&dec->proba_); ResetSegmentHeader(&dec->segment_hdr_); } // Check if we have all the partition #0 available, and initialize dec->br_ // to read this partition (and this partition only). if (frm_hdr->partition_length_ > buf_size) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "bad partition length"); } br = &dec->br_; VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_); buf += frm_hdr->partition_length_; buf_size -= frm_hdr->partition_length_; if (frm_hdr->key_frame_) { pic_hdr->colorspace_ = VP8Get(br); pic_hdr->clamp_type_ = VP8Get(br); } if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "cannot parse segment header"); } // Filter specs if (!ParseFilterHeader(br, dec)) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "cannot parse filter header"); } status = ParsePartitions(dec, buf, buf_size); if (status != VP8_STATUS_OK) { return VP8SetError(dec, status, "cannot parse partitions"); } // quantizer change VP8ParseQuant(dec); // Frame buffer marking if (!frm_hdr->key_frame_) { return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, "Not a key frame."); } VP8Get(br); // ignore the value of update_proba_ VP8ParseProba(br, dec); // sanitized state dec->ready_ = 1; return 1; } //------------------------------------------------------------------------------ // Residual decoding (Paragraph 13.2 / 13.3) static const uint8_t kCat3[] = { 173, 148, 140, 0 }; static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 }; static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 }; static const uint8_t kCat6[] = { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 }; static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 }; static const uint8_t kZigzag[16] = { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 }; // See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2 static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) { int v; if (!VP8GetBit(br, p[3])) { if (!VP8GetBit(br, p[4])) { v = 2; } else { v = 3 + VP8GetBit(br, p[5]); } } else { if (!VP8GetBit(br, p[6])) { if (!VP8GetBit(br, p[7])) { v = 5 + VP8GetBit(br, 159); } else { v = 7 + 2 * VP8GetBit(br, 165); v += VP8GetBit(br, 145); } } else { const uint8_t* tab; const int bit1 = VP8GetBit(br, p[8]); const int bit0 = VP8GetBit(br, p[9 + bit1]); const int cat = 2 * bit1 + bit0; v = 0; for (tab = kCat3456[cat]; *tab; ++tab) { v += v + VP8GetBit(br, *tab); } v += 3 + (8 << cat); } } return v; } // Returns the position of the last non-zero coeff plus one static int GetCoeffs(VP8BitReader* const br, const VP8BandProbas* const prob[], int ctx, const quant_t dq, int n, int16_t* out) { const uint8_t* p = prob[n]->probas_[ctx]; for (; n < 16; ++n) { if (!VP8GetBit(br, p[0])) { return n; // previous coeff was last non-zero coeff } while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs p = prob[++n]->probas_[0]; if (n == 16) return 16; } { // non zero coeff const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; int v; if (!VP8GetBit(br, p[2])) { v = 1; p = p_ctx[1]; } else { v = GetLargeValue(br, p); p = p_ctx[2]; } out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0]; } } return 16; } static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) { nz_coeffs <<= 2; nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz; return nz_coeffs; } static void TransformWHT(const int16_t* in, int16_t* out) { int tmp[16]; int i; for (i = 0; i < 4; ++i) { const int a0 = in[0 + i] + in[12 + i]; const int a1 = in[4 + i] + in[ 8 + i]; const int a2 = in[4 + i] - in[ 8 + i]; const int a3 = in[0 + i] - in[12 + i]; tmp[0 + i] = a0 + a1; tmp[8 + i] = a0 - a1; tmp[4 + i] = a3 + a2; tmp[12 + i] = a3 - a2; } for (i = 0; i < 4; ++i) { const int dc = tmp[0 + i * 4] + 3; // w/ rounder const int a0 = dc + tmp[3 + i * 4]; const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4]; const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4]; const int a3 = dc - tmp[3 + i * 4]; out[ 0] = (a0 + a1) >> 3; out[16] = (a3 + a2) >> 3; out[32] = (a0 - a1) >> 3; out[48] = (a3 - a2) >> 3; out += 64; } } static int ParseResiduals(VP8Decoder* const dec, VP8MB* const mb, VP8BitReader* const token_br) { const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_; const VP8BandProbas* const * ac_proba; VP8MBData* const block = dec->mb_data_ + dec->mb_x_; const VP8QuantMatrix* const q = &dec->dqm_[block->segment_]; int16_t* dst = block->coeffs_; VP8MB* const left_mb = dec->mb_info_ - 1; uint8_t tnz, lnz; uint32_t non_zero_y = 0; uint32_t non_zero_uv = 0; int x, y, ch; uint32_t out_t_nz, out_l_nz; int first; memset(dst, 0, 384 * sizeof(*dst)); if (!block->is_i4x4_) { // parse DC int16_t dc[16] = { 0 }; const int ctx = mb->nz_dc_ + left_mb->nz_dc_; const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc); mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0); if (nz > 1) { // more than just the DC -> perform the full transform TransformWHT(dc, dst); } else { // only DC is non-zero -> inlined simplified transform int i; const int dc0 = (dc[0] + 3) >> 3; for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0; } first = 1; ac_proba = bands[0]; } else { first = 0; ac_proba = bands[3]; } tnz = mb->nz_ & 0x0f; lnz = left_mb->nz_ & 0x0f; for (y = 0; y < 4; ++y) { int l = lnz & 1; uint32_t nz_coeffs = 0; for (x = 0; x < 4; ++x) { const int ctx = l + (tnz & 1); const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst); l = (nz > first); tnz = (tnz >> 1) | (l << 7); nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0); dst += 16; } tnz >>= 4; lnz = (lnz >> 1) | (l << 7); non_zero_y = (non_zero_y << 8) | nz_coeffs; } out_t_nz = tnz; out_l_nz = lnz >> 4; for (ch = 0; ch < 4; ch += 2) { uint32_t nz_coeffs = 0; tnz = mb->nz_ >> (4 + ch); lnz = left_mb->nz_ >> (4 + ch); for (y = 0; y < 2; ++y) { int l = lnz & 1; for (x = 0; x < 2; ++x) { const int ctx = l + (tnz & 1); const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst); l = (nz > 0); tnz = (tnz >> 1) | (l << 3); nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0); dst += 16; } tnz >>= 2; lnz = (lnz >> 1) | (l << 5); } // Note: we don't really need the per-4x4 details for U/V blocks. non_zero_uv |= nz_coeffs << (4 * ch); out_t_nz |= (tnz << 4) << ch; out_l_nz |= (lnz & 0xf0) << ch; } mb->nz_ = out_t_nz; left_mb->nz_ = out_l_nz; block->non_zero_y_ = non_zero_y; block->non_zero_uv_ = non_zero_uv; // We look at the mode-code of each block and check if some blocks have less // than three non-zero coeffs (code < 2). This is to avoid dithering flat and // empty blocks. block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_; return !(non_zero_y | non_zero_uv); // will be used for further optimization } //------------------------------------------------------------------------------ // Main loop int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) { VP8MB* const left = dec->mb_info_ - 1; VP8MB* const mb = dec->mb_info_ + dec->mb_x_; VP8MBData* const block = dec->mb_data_ + dec->mb_x_; int skip = dec->use_skip_proba_ ? block->skip_ : 0; if (!skip) { skip = ParseResiduals(dec, mb, token_br); } else { left->nz_ = mb->nz_ = 0; if (!block->is_i4x4_) { left->nz_dc_ = mb->nz_dc_ = 0; } block->non_zero_y_ = 0; block->non_zero_uv_ = 0; block->dither_ = 0; } if (dec->filter_type_ > 0) { // store filter info VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_; *finfo = dec->fstrengths_[block->segment_][block->is_i4x4_]; finfo->f_inner_ |= !skip; } return !token_br->eof_; } void VP8InitScanline(VP8Decoder* const dec) { VP8MB* const left = dec->mb_info_ - 1; left->nz_ = 0; left->nz_dc_ = 0; memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_)); dec->mb_x_ = 0; } static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) { // Parse bitstream for this row. VP8BitReader* const token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)]; if (!VP8ParseIntraModeRow(&dec->br_, dec)) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "Premature end-of-partition0 encountered."); } for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { if (!VP8DecodeMB(dec, token_br)) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "Premature end-of-file encountered."); } } VP8InitScanline(dec); // Prepare for next scanline // Reconstruct, filter and emit the row. if (!VP8ProcessRow(dec, io)) { return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted."); } } return 1; } // Main entry point int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { int ok = 0; if (dec == NULL) { return 0; } if (io == NULL) { return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, "NULL VP8Io parameter in VP8Decode()."); } if (!dec->ready_) { if (!VP8GetHeaders(dec, io)) { return 0; } } assert(dec->ready_); // Finish setting up the decoding parameter. Will call io->setup(). ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK); if (ok) { // good to go. // Will allocate memory and prepare everything. if (ok) ok = VP8InitFrame(dec, io); // Main decoding loop if (ok) ok = ParseFrame(dec, io); // Exit. ok &= VP8ExitCritical(dec, io); } if (!ok) { VP8Clear(dec); return 0; } dec->ready_ = 0; return ok; } void VP8Clear(VP8Decoder* const dec) { if (dec == NULL) { return; } ALPHDelete(dec->alph_dec_); dec->alph_dec_ = NULL; tvg::free(dec->mem_); dec->mem_ = NULL; dec->mem_size_ = 0; memset(&dec->br_, 0, sizeof(dec->br_)); dec->ready_ = 0; } //------------------------------------------------------------------------------ �������������������������������������������������������������������������������������������������������������������������������glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-strings.h�������000664 �001750 �001750 �00000015404 15164251010 042543� 0����������������������������������������������������������������������������������������������������ustar�00ddennedy������������������������ddennedy������������������������000000 �000000 �mlt-7.38.0/src/modules/glaxnimate��������������������������������������������������������������������������������������������������������������������������������������/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 LIT_STRINGS_H #define LIT_STRINGS_H #include "jrt.h" #include "lit-globals.h" /** * Null character (used in few cases as utf-8 string end marker) */ #define LIT_BYTE_NULL (0) /** * For the formal definition of Unicode transformation formats (UTF) see Section 3.9, Unicode Encoding Forms in The * Unicode Standard (http://www.unicode.org/versions/Unicode3.0.0/ch03.pdf#G7404). */ #define LIT_UNICODE_CODE_POINT_NULL (0x0) #define LIT_UNICODE_CODE_POINT_MAX (0x10FFFF) #define LIT_UTF16_CODE_UNIT_MAX (0xFFFF) #define LIT_UTF16_FIRST_SURROGATE_CODE_POINT (0x10000) #define LIT_UTF16_LOW_SURROGATE_MARKER (0xDC00) #define LIT_UTF16_HIGH_SURROGATE_MARKER (0xD800) #define LIT_UTF16_HIGH_SURROGATE_MIN (0xD800) #define LIT_UTF16_HIGH_SURROGATE_MAX (0xDBFF) #define LIT_UTF16_LOW_SURROGATE_MIN (0xDC00) #define LIT_UTF16_LOW_SURROGATE_MAX (0xDFFF) #define LIT_UTF16_BITS_IN_SURROGATE (10) #define LIT_UTF16_LAST_10_BITS_MASK (0x3FF) #define LIT_UTF8_1_BYTE_MARKER (0x00) #define LIT_UTF8_2_BYTE_MARKER (0xC0) #define LIT_UTF8_3_BYTE_MARKER (0xE0) #define LIT_UTF8_4_BYTE_MARKER (0xF0) #define LIT_UTF8_EXTRA_BYTE_MARKER (0x80) #define LIT_UTF8_1_BYTE_MASK (0x80) #define LIT_UTF8_2_BYTE_MASK (0xE0) #define LIT_UTF8_3_BYTE_MASK (0xF0) #define LIT_UTF8_4_BYTE_MASK (0xF8) #define LIT_UTF8_EXTRA_BYTE_MASK (0xC0) #define LIT_UTF8_LAST_7_BITS_MASK (0x7F) #define LIT_UTF8_LAST_6_BITS_MASK (0x3F) #define LIT_UTF8_LAST_5_BITS_MASK (0x1F) #define LIT_UTF8_LAST_4_BITS_MASK (0x0F) #define LIT_UTF8_LAST_3_BITS_MASK (0x07) #define LIT_UTF8_LAST_2_BITS_MASK (0x03) #define LIT_UTF8_LAST_1_BIT_MASK (0x01) #define LIT_UTF8_BITS_IN_EXTRA_BYTES (6) #define LIT_UTF8_1_BYTE_CODE_POINT_MAX (0x7F) #define LIT_UTF8_2_BYTE_CODE_POINT_MIN (0x80) #define LIT_UTF8_2_BYTE_CODE_POINT_MAX (0x7FF) #define LIT_UTF8_3_BYTE_CODE_POINT_MIN (0x800) #define LIT_UTF8_3_BYTE_CODE_POINT_MAX (LIT_UTF16_CODE_UNIT_MAX) #define LIT_UTF8_4_BYTE_CODE_POINT_MIN (0x10000) #define LIT_UTF8_4_BYTE_CODE_POINT_MAX (LIT_UNICODE_CODE_POINT_MAX) /** * Difference between byte count needed to represent code point greater than 0xFFFF * in common UTF-8 (4 bytes required) and CESU-8 (6 bytes required) */ #define LIT_UTF8_CESU8_SURROGATE_SIZE_DIF (2 * LIT_UTF8_MAX_BYTES_IN_CODE_UNIT - LIT_UTF8_MAX_BYTES_IN_CODE_POINT) /** * Byte values >= LIT_UTF8_FIRST_BYTE_MAX are not allowed in internal strings */ #define LIT_UTF8_FIRST_BYTE_MAX (0xF8) /* validation */ bool lit_is_valid_utf8_string (const lit_utf8_byte_t *utf8_buf_p, lit_utf8_size_t buf_size, bool strict); bool lit_is_valid_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, lit_utf8_size_t buf_size); /* checks */ bool lit_is_code_point_utf16_low_surrogate (lit_code_point_t code_point); bool lit_is_code_point_utf16_high_surrogate (lit_code_point_t code_point); /* size */ lit_utf8_size_t lit_zt_utf8_string_size (const lit_utf8_byte_t *utf8_str_p); lit_utf8_size_t lit_get_utf8_size_of_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, lit_utf8_size_t cesu8_buf_size); /* length */ lit_utf8_size_t lit_utf8_string_length (const lit_utf8_byte_t *utf8_buf_p, lit_utf8_size_t utf8_buf_size); lit_utf8_size_t lit_get_utf8_length_of_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, lit_utf8_size_t cesu8_buf_size); /* hash */ lit_string_hash_t lit_utf8_string_calc_hash (const lit_utf8_byte_t *utf8_buf_p, lit_utf8_size_t utf8_buf_size); lit_string_hash_t lit_utf8_string_hash_combine (lit_string_hash_t hash_basis, const lit_utf8_byte_t *utf8_buf_p, lit_utf8_size_t utf8_buf_size); /* code unit access */ ecma_char_t lit_utf8_string_code_unit_at (const lit_utf8_byte_t *utf8_buf_p, lit_utf8_size_t utf8_buf_size, lit_utf8_size_t code_unit_offset); lit_utf8_size_t lit_get_unicode_char_size_by_utf8_first_byte (const lit_utf8_byte_t first_byte); /* conversion */ lit_utf8_size_t lit_code_unit_to_utf8 (ecma_char_t code_unit, lit_utf8_byte_t *buf_p); lit_utf8_size_t lit_code_point_to_utf8 (lit_code_point_t code_point, lit_utf8_byte_t *buf); lit_utf8_size_t lit_code_point_to_cesu8 (lit_code_point_t code_point, lit_utf8_byte_t *buf); lit_utf8_size_t lit_convert_cesu8_string_to_utf8_string (const lit_utf8_byte_t *cesu8_string_p, lit_utf8_size_t cesu8_size, lit_utf8_byte_t *utf8_string_p, lit_utf8_size_t utf8_size); lit_code_point_t lit_convert_surrogate_pair_to_code_point (ecma_char_t high_surrogate, ecma_char_t low_surrogate); bool lit_compare_utf8_strings_relational (const lit_utf8_byte_t *string1_p, lit_utf8_size_t string1_size, const lit_utf8_byte_t *string2_p, lit_utf8_size_t string2_size); uint8_t lit_utf16_encode_code_point (lit_code_point_t cp, ecma_char_t *cu_p); /* read code point from buffer */ lit_utf8_size_t lit_read_code_point_from_utf8 (const lit_utf8_byte_t *buf_p, lit_utf8_size_t buf_size, lit_code_point_t *code_point); lit_utf8_size_t lit_read_code_unit_from_cesu8 (const lit_utf8_byte_t *buf_p, ecma_char_t *code_unit); lit_utf8_size_t lit_read_code_point_from_cesu8 (const lit_utf8_byte_t *buf_p, const lit_utf8_byte_t *buf_end_p, lit_code_point_t *code_point); lit_utf8_size_t lit_read_prev_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, ecma_char_t *code_point); ecma_char_t lit_cesu8_read_next (const lit_utf8_byte_t **buf_p); ecma_char_t lit_cesu8_read_prev (const lit_utf8_byte_t **buf_p); ecma_char_t lit_cesu8_peek_next (const lit_utf8_byte_t *buf_p); ecma_char_t lit_cesu8_peek_prev (const lit_utf8_byte_t *buf_p); void lit_utf8_incr (const lit_utf8_byte_t **buf_p); void lit_utf8_decr (const lit_utf8_byte_t **buf_p); #endif /* !LIT_STRINGS_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgInitializer.cpp�000664 �001750 �001750 �00000007545 15164251010 035335� 0����������������������������������������������������������������������������������������������������ustar�00ddennedy������������������������ddennedy������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgCommon.h" #include "tvgTaskScheduler.h" #include "tvgLoader.h" #ifdef THORVG_SW_RASTER_SUPPORT #include "tvgSwRenderer.h" #endif #ifdef THORVG_GL_RASTER_SUPPORT #include "tvgGlRenderer.h" #endif #ifdef THORVG_WG_RASTER_SUPPORT #include "tvgWgRenderer.h" #endif /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ namespace tvg { int engineInit = 0; } static uint16_t _version = 0; static bool _buildVersionInfo(uint32_t* major, uint32_t* minor, uint32_t* micro) { auto VER = THORVG_VERSION_STRING; auto p = VER; const char* x; if (!(x = strchr(p, '.'))) return false; uint32_t majorVal = atoi(p); p = x + 1; if (!(x = strchr(p, '.'))) return false; uint32_t minorVal = atoi(p); p = x + 1; uint32_t microVal = atoi(p); char sum[7]; snprintf(sum, sizeof(sum), "%d%02d%02d", majorVal, minorVal, microVal); _version = atoi(sum); if (major) *major = majorVal; if (minor) *minor = minorVal; if (micro) *micro = microVal; return true; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ Result Initializer::init(uint32_t threads) noexcept { if (engineInit++ > 0) return Result::Success; if (!_buildVersionInfo(nullptr, nullptr, nullptr)) return Result::Unknown; if (!LoaderMgr::init()) return Result::Unknown; TaskScheduler::init(threads); return Result::Success; } Result Initializer::term() noexcept { if (engineInit == 0) return Result::InsufficientCondition; if (--engineInit > 0) return Result::Success; #ifdef THORVG_SW_RASTER_SUPPORT if (!SwRenderer::term()) return Result::InsufficientCondition; #endif #ifdef THORVG_GL_RASTER_SUPPORT if (!GlRenderer::term()) return Result::InsufficientCondition; #endif #ifdef THORVG_WG_RASTER_SUPPORT if (!WgRenderer::term()) return Result::InsufficientCondition; #endif TaskScheduler::term(); if (!LoaderMgr::term()) return Result::Unknown; return Result::Success; } const char* Initializer::version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept { if ((!major && ! minor && !micro) || _buildVersionInfo(major, minor, micro)) return THORVG_VERSION_STRING; return nullptr; } uint16_t THORVG_VERSION_NUMBER() { return _version; } void* operator new(std::size_t size) { return tvg::malloc(size); } void operator delete(void* ptr) noexcept { tvg::free(ptr); }�����������������������������������������������������������������������������������������������������������������������������������������������������������thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-error.cpp000664 �001750 �001750 �00000004137 15164251010 051172� 0����������������������������������������������������������������������������������������������������ustar�00ddennedy������������������������ddennedy������������������������000000 �000000 �mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg�����������������������������������������������������������������������������������������������������������/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltinhelpers ECMA builtin helper operations * @{ */ /** * Handle calling [[Call]] of a built-in error object * * @return ecma value */ ecma_value_t ecma_builtin_helper_error_dispatch_call (jerry_error_t error_type, /**< native error type */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); if (arguments_list_len != 0 && !ecma_is_value_undefined (arguments_list_p[0])) { ecma_string_t *message_string_p = ecma_op_to_string (arguments_list_p[0]); if (JERRY_UNLIKELY (message_string_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *new_error_object_p = ecma_new_standard_error (error_type, message_string_p); ecma_deref_ecma_string (message_string_p); return ecma_make_object_value (new_error_object_p); } ecma_object_t *new_error_object_p = ecma_new_standard_error (error_type, NULL); return ecma_make_object_value (new_error_object_p); } /* ecma_builtin_helper_error_dispatch_call */ /** * @} * @} */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-bigint-prototype.cpp����000664 �001750 �001750 �00000012121 15164251010 051710� 0����������������������������������������������������������������������������������������������������ustar�00ddennedy������������������������ddennedy������������������������000000 �000000 �mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg����������������������������������������������������������������������������������������������������/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-bigint.h" #include "ecma-exceptions.h" #if JERRY_BUILTIN_BIGINT #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BIGINT_PROTOTYPE_ROUTINE_START = 0, ECMA_BIGINT_PROTOTYPE_VALUE_OF, ECMA_BIGINT_PROTOTYPE_TO_STRING, ECMA_BIGINT_PROTOTYPE_TO_LOCALE_STRING, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-bigint-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID bigint_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup bigint ECMA BigInt object built-in * @{ */ /** * The BigInt.prototype object's 'valueOf' routine * * See also: * ECMA-262 v11, 20.2.3.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_bigint_prototype_object_value_of (ecma_value_t this_arg) /**< this argument */ { if (ecma_is_value_bigint (this_arg)) { return ecma_copy_value (this_arg); } if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_BIGINT)) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; JERRY_ASSERT (ecma_is_value_bigint (ext_object_p->u.cls.u3.value)); return ecma_copy_value (ext_object_p->u.cls.u3.value); } } return ecma_raise_type_error (ECMA_ERR_BIGINT_VALUE_EXPECTED); } /* ecma_builtin_bigint_prototype_object_value_of */ /** * The BigInt.prototype object's 'toString' routine * * See also: * ECMA-262 v11, 20.2.3.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_bigint_prototype_object_to_string (ecma_value_t this_arg, /**< this argument */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { uint32_t radix = 10; if (arguments_list_len > 0 && !ecma_is_value_undefined (arguments_list_p[0])) { ecma_number_t arg_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (arguments_list_p[0], &arg_num))) { return ECMA_VALUE_ERROR; } if (arg_num < 2 || arg_num > 36) { return ecma_raise_range_error (ECMA_ERR_RADIX_IS_OUT_OF_RANGE); } radix = (uint32_t) arg_num; } ecma_string_t *string_p = ecma_bigint_to_string (this_arg, radix); if (string_p == NULL) { return ECMA_VALUE_ERROR; } return ecma_make_string_value (string_p); } /* ecma_builtin_bigint_prototype_object_to_string */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_bigint_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { ecma_value_t this_value = ecma_builtin_bigint_prototype_object_value_of (this_arg); ecma_value_t ret_val; if (ECMA_IS_VALUE_ERROR (this_value)) { return this_value; } switch (builtin_routine_id) { case ECMA_BIGINT_PROTOTYPE_VALUE_OF: { ret_val = this_value; break; } case ECMA_BIGINT_PROTOTYPE_TO_STRING: { ret_val = ecma_builtin_bigint_prototype_object_to_string (this_value, arguments_list_p, arguments_number); ecma_free_value (this_value); break; } case ECMA_BIGINT_PROTOTYPE_TO_LOCALE_STRING: { ret_val = ecma_builtin_bigint_prototype_object_to_string (this_value, 0, 0); ecma_free_value (this_value); break; } default: { JERRY_UNREACHABLE (); } } return ret_val; } /* ecma_builtin_bigint_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_BIGINT */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/document.h����000664 �001750 �001750 �00000405203 15164251010 036642� 0����������������������������������������������������������������������������������������������������ustar�00ddennedy������������������������ddennedy������������������������000000 �000000 �mlt-7.38.0�������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // // 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 // placement new #include #ifdef __cpp_lib_three_way_comparison #include #endif RAPIDJSON_DIAG_PUSH #ifdef __clang__ RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(c++98-compat) #elif defined(_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 __GNUC__ RAPIDJSON_DIAG_OFF(effc++) #endif // __GNUC__ #ifdef GetObject // see https://github.com/Tencent/rapidjson/issues/1448 // a former included windows.h might have defined a macro called GetObject, which affects // GetObject defined here. This ensures the macro does not get applied #pragma push_macro("GetObject") #define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED #undef GetObject #endif #ifndef RAPIDJSON_NOMEMBERITERATORCLASS #include // std::random_access_iterator_tag #endif #if RAPIDJSON_USE_MEMBERSMAP #include // std::multimap #endif RAPIDJSON_NAMESPACE_BEGIN // Forward declaration. template class GenericValue; template class GenericDocument; /*! \def RAPIDJSON_DEFAULT_ALLOCATOR \ingroup RAPIDJSON_CONFIG \brief Allows to choose default allocator. User can define this to use CrtAllocator or MemoryPoolAllocator. */ #ifndef RAPIDJSON_DEFAULT_ALLOCATOR #define RAPIDJSON_DEFAULT_ALLOCATOR ::RAPIDJSON_NAMESPACE::MemoryPoolAllocator<::RAPIDJSON_NAMESPACE::CrtAllocator> #endif /*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR \ingroup RAPIDJSON_CONFIG \brief Allows to choose default stack allocator for Document. User can define this to use CrtAllocator or MemoryPoolAllocator. */ #ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR #define RAPIDJSON_DEFAULT_STACK_ALLOCATOR ::RAPIDJSON_NAMESPACE::CrtAllocator #endif /*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY \ingroup RAPIDJSON_CONFIG \brief User defined kDefaultObjectCapacity value. User can define this as any natural number. */ #ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY // number of objects that rapidjson::Value allocates memory for by default #define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 #endif /*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY \ingroup RAPIDJSON_CONFIG \brief User defined kDefaultArrayCapacity value. User can define this as any natural number. */ #ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY // number of array elements that rapidjson::Value allocates memory for by default #define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 #endif //! 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 class GenericMember { public: GenericValue name; //!< name of member (must be a string) GenericValue value; //!< value of member. #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT : name(std::move(rhs.name)), value(std::move(rhs.value)) { } //! Move assignment in C++11 GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { return *this = static_cast(rhs); } #endif //! Assignment with move semantics. /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. */ GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { if (RAPIDJSON_LIKELY(this != &rhs)) { name = rhs.name; value = rhs.value; } return *this; } // swap() for std::sort() and other potential use in STL. friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { a.name.Swap(b.name); a.value.Swap(b.value); } private: //! Copy constructor is not permitted. GenericMember(const GenericMember& rhs); }; /////////////////////////////////////////////////////////////////////////////// // 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++ header. \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator */ template class GenericMemberIterator { friend class GenericValue; template friend class GenericMemberIterator; typedef GenericMember PlainType; typedef typename internal::MaybeAddConst::Type ValueType; public: //! Iterator type itself typedef GenericMemberIterator Iterator; //! Constant iterator type typedef GenericMemberIterator ConstIterator; //! Non-constant iterator type typedef GenericMemberIterator NonConstIterator; /** \name std::iterator_traits support */ //@{ typedef ValueType value_type; typedef ValueType * pointer; typedef ValueType & reference; typedef std::ptrdiff_t difference_type; typedef std::random_access_iterator_tag iterator_category; //@} //! Pointer to (const) GenericMember typedef pointer Pointer; //! Reference to (const) GenericMember typedef reference Reference; //! Signed integer type (e.g. \c ptrdiff_t) typedef 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 //@{ template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } #ifdef __cpp_lib_three_way_comparison template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } #endif //@} //! @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 class GenericMemberIterator; //! non-const GenericMemberIterator template class GenericMemberIterator { public: //! use plain pointer as iterator type typedef GenericMember* Iterator; }; //! const GenericMemberIterator template class GenericMemberIterator { public: //! use plain const pointer as iterator type typedef const GenericMember* 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 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 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 GenericStringRef(CharType (&str)[N]) /* = delete */; //! Copy assignment operator not permitted - immutable type GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; template const CharType GenericStringRef::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 inline GenericStringRef StringRef(const CharType* str) { return GenericStringRef(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 inline GenericStringRef StringRef(const CharType* str, size_t length) { return GenericStringRef(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 inline GenericStringRef StringRef(const std::basic_string& str) { return GenericStringRef(str.data(), SizeType(str.size())); } #endif /////////////////////////////////////////////////////////////////////////////// // GenericValue type traits namespace internal { template struct IsGenericValueImpl : FalseType {}; // select candidates according to nested encoding and allocator types template struct IsGenericValueImpl::Type, typename Void::Type> : IsBaseOf, T>::Type {}; // helper to match arbitrary GenericValue instantiations, including derived classes template struct IsGenericValue : IsGenericValueImpl::Type {}; } // namespace internal /////////////////////////////////////////////////////////////////////////////// // TypeHelper namespace internal { template struct TypeHelper {}; template struct TypeHelper { 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 struct TypeHelper { 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 struct TypeHelper { 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); } }; #ifdef _MSC_VER RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsInt(); } static long Get(const ValueType& v) { return v.GetInt(); } static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } }; RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsUint(); } static unsigned long Get(const ValueType& v) { return v.GetUint(); } static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } }; #endif template struct TypeHelper { 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 struct TypeHelper { 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 struct TypeHelper { 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 struct TypeHelper { 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 struct TypeHelper { 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 struct TypeHelper > { typedef std::basic_string 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 struct TypeHelper { 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 struct TypeHelper { typedef typename ValueType::ConstArray ArrayType; static bool Is(const ValueType& v) { return v.IsArray(); } static ArrayType Get(const ValueType& v) { return v.GetArray(); } }; template struct TypeHelper { 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 struct TypeHelper { 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 class GenericArray; template 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 class GenericValue { public: //! Name-value pair in an object. typedef GenericMember 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 StringRefType; //!< Reference to a constant string typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. typedef typename GenericMemberIterator::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 ValueType; //!< Value type of itself. typedef GenericArray Array; typedef GenericArray ConstArray; typedef GenericObject Object; typedef GenericObject 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 GenericValue(GenericDocument&& rhs); //! Move assignment from a GenericDocument is not permitted. template GenericValue& operator=(GenericDocument&& 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[] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; RAPIDJSON_NOEXCEPT_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 GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { switch (rhs.GetType()) { case kObjectType: DoCopyMembers(rhs, allocator, copyConstStrings); break; case kArrayType: { SizeType count = rhs.data_.a.size; GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); const GenericValue* 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(&rhs.data_); } else SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); break; default: data_.f.flags = rhs.data_.f.flags; data_ = *reinterpret_cast(&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 explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 #else explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT #endif : data_() { // safe-guard against failing SFINAE RAPIDJSON_STATIC_ASSERT((internal::IsSame::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(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) data_.f.flags |= kUintFlag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) data_.f.flags |= kIntFlag; } else if (i64 >= static_cast(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(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& 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 source 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 source 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() { // With RAPIDJSON_USE_MEMBERSMAP, the maps need to be destroyed to release // their Allocator if it's refcounted (e.g. MemoryPoolAllocator). if (Allocator::kNeedFree || (RAPIDJSON_USE_MEMBERSMAP+0 && internal::IsRefCounted::Value)) { switch(data_.f.flags) { case kArrayFlag: { GenericValue* e = GetElementsPointer(); for (GenericValue* v = e; v != e + data_.a.size; ++v) v->~GenericValue(); if (Allocator::kNeedFree) { // Shortcut by Allocator's trait Allocator::Free(e); } } break; case kObjectFlag: DoFreeMembers(); break; case kCopyStringFlag: if (Allocator::kNeedFree) { // Shortcut by Allocator's trait Allocator::Free(const_cast(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 { if (RAPIDJSON_LIKELY(this != &rhs)) { // Can't destroy "this" before assigning "rhs", otherwise "rhs" // could be used after free if it's an sub-Value of "this", // hence the temporary dance. GenericValue temp; temp.RawAssign(rhs); this->~GenericValue(); RawAssign(temp); } 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 RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (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 GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { RAPIDJSON_ASSERT(static_cast(this) != static_cast(&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 Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). */ template bool operator==(const GenericValue& rhs) const { typedef GenericValue 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& 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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } #ifndef __cpp_impl_three_way_comparison //! Not-equal-to operator /*! \return !(*this == rhs) */ template bool operator!=(const GenericValue& 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 RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } //! Equal-to operator with arbitrary types (symmetric version) /*! \return (rhs == lhs) */ template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } //! Not-Equal-to operator with arbitrary types (symmetric version) /*! \return !(rhs == lhs) */ template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } //@} #endif //!@name Type //@{ Type GetType() const { return static_cast(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(u); return (d >= 0.0) && (d < static_cast((std::numeric_limits::max)())) && (u == static_cast(d)); } if (IsInt64()) { int64_t i = GetInt64(); volatile double d = static_cast(i); return (d >= static_cast((std::numeric_limits::min)())) && (d < static_cast((std::numeric_limits::max)())) && (i == static_cast(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(-(std::numeric_limits::max)()) || a > static_cast((std::numeric_limits::max)())) return false; double b = static_cast(static_cast(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 RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { GenericValue n(StringRef(name)); return (*this)[n]; } template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*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 GenericValue& operator[](const GenericValue& name) { MemberIterator member = FindMember(name); if (member != MemberEnd()) return member->value; else { RAPIDJSON_ASSERT(false); // see above note #if RAPIDJSON_HAS_CXX11 // Use thread-local storage to prevent races between threads. // Use static buffer and placement-new to prevent destruction, with // alignas() to ensure proper alignment. alignas(GenericValue) thread_local static char buffer[sizeof(GenericValue)]; return *new (buffer) GenericValue(); #elif defined(_MSC_VER) && _MSC_VER < 1900 // There's no way to solve both thread locality and proper alignment // simultaneously. __declspec(thread) static char buffer[sizeof(GenericValue)]; return *new (buffer) GenericValue(); #elif defined(__GNUC__) || defined(__clang__) // This will generate -Wexit-time-destructors in clang, but that's // better than having under-alignment. __thread static GenericValue buffer; return buffer; #else // Don't know what compiler this is, so don't know how to ensure // thread-locality. static GenericValue buffer; return buffer; #endif } } template const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } #if RAPIDJSON_HAS_STDSTRING //! Get a value from an object associated with name (string object). GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } const GenericValue& operator[](const std::basic_string& 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()); DoReserveMembers(newCapacity, allocator); 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& 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 bool HasMember(const GenericValue& 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(*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 MemberIterator FindMember(const GenericValue& name) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); return DoFindMember(name); } template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*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& name) { return FindMember(GenericValue(StringRef(name))); } ConstMemberIterator FindMember(const std::basic_string& 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()); DoAddMember(name, value, allocator); 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& 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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (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()); DoClearMembers(); } //! 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& name) { return RemoveMember(GenericValue(StringRef(name))); } #endif template bool RemoveMember(const GenericValue& 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()); return DoRemoveMember(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()); return DoEraseMembers(first, last); } //! 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& name) { return EraseMember(GenericValue(StringRef(name))); } #endif template bool EraseMember(const GenericValue& name) { MemberIterator m = FindMember(name); if (m != MemberEnd()) { EraseMember(m); return true; } else return false; } Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } Object GetObj() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } ConstObject GetObj() 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(*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(*this).Begin(); } //! Constant \em past-the-end element iterator /*! \pre IsArray() == true */ ConstValueIterator End() const { return const_cast(*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(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(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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (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(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); data_.a.size -= static_cast(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 conversion 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(data_.n.i64); // int64_t -> double (may lose precision) RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(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 conversion is lossless. */ float GetFloat() const { return static_cast(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(f)); return *this; } //@} //!@name String //@{ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return DataString(data_); } //! 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 DataStringLength(data_); } //! 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& 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 */ template bool Is() const { return internal::TypeHelper::Is(*this); } template T Get() const { return internal::TypeHelper::Get(*this); } template T Get() { return internal::TypeHelper::Get(*this); } template ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } template ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::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 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 (ConstValueIterator 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 friend class GenericValue; template 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, // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), kObjectFlag = kObjectType, kArrayFlag = kArrayType, kTypeMask = 0x07 }; static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; 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(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(MaxSize - len); } inline SizeType GetLength() const { return static_cast(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 static RAPIDJSON_FORCEINLINE const Ch* DataString(const Data& data) { return (data.f.flags & kInlineStrFlag) ? data.ss.str : RAPIDJSON_GETPOINTER(Ch, data.s.str); } static RAPIDJSON_FORCEINLINE SizeType DataStringLength(const Data& data) { return (data.f.flags & kInlineStrFlag) ? data.ss.GetLength() : data.s.length; } 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); } #if RAPIDJSON_USE_MEMBERSMAP struct MapTraits { struct Less { bool operator()(const Data& s1, const Data& s2) const { SizeType n1 = DataStringLength(s1), n2 = DataStringLength(s2); int cmp = std::memcmp(DataString(s1), DataString(s2), sizeof(Ch) * (n1 < n2 ? n1 : n2)); return cmp < 0 || (cmp == 0 && n1 < n2); } }; typedef std::pair Pair; typedef std::multimap > Map; typedef typename Map::iterator Iterator; }; typedef typename MapTraits::Map Map; typedef typename MapTraits::Less MapLess; typedef typename MapTraits::Pair MapPair; typedef typename MapTraits::Iterator MapIterator; // // Layout of the members' map/array, re(al)located according to the needed capacity: // // {Map*}<>{capacity}<>{Member[capacity]}<>{MapIterator[capacity]} // // (where <> stands for the RAPIDJSON_ALIGN-ment, if needed) // static RAPIDJSON_FORCEINLINE size_t GetMapLayoutSize(SizeType capacity) { return RAPIDJSON_ALIGN(sizeof(Map*)) + RAPIDJSON_ALIGN(sizeof(SizeType)) + RAPIDJSON_ALIGN(capacity * sizeof(Member)) + capacity * sizeof(MapIterator); } static RAPIDJSON_FORCEINLINE SizeType &GetMapCapacity(Map* &map) { return *reinterpret_cast(reinterpret_cast(&map) + RAPIDJSON_ALIGN(sizeof(Map*))); } static RAPIDJSON_FORCEINLINE Member* GetMapMembers(Map* &map) { return reinterpret_cast(reinterpret_cast(&map) + RAPIDJSON_ALIGN(sizeof(Map*)) + RAPIDJSON_ALIGN(sizeof(SizeType))); } static RAPIDJSON_FORCEINLINE MapIterator* GetMapIterators(Map* &map) { return reinterpret_cast(reinterpret_cast(&map) + RAPIDJSON_ALIGN(sizeof(Map*)) + RAPIDJSON_ALIGN(sizeof(SizeType)) + RAPIDJSON_ALIGN(GetMapCapacity(map) * sizeof(Member))); } static RAPIDJSON_FORCEINLINE Map* &GetMap(Member* members) { RAPIDJSON_ASSERT(members != 0); return *reinterpret_cast(reinterpret_cast(members) - RAPIDJSON_ALIGN(sizeof(SizeType)) - RAPIDJSON_ALIGN(sizeof(Map*))); } // Some compilers' debug mechanisms want all iterators to be destroyed, for their accounting.. RAPIDJSON_FORCEINLINE MapIterator DropMapIterator(MapIterator& rhs) { #if RAPIDJSON_HAS_CXX11 MapIterator ret = std::move(rhs); #else MapIterator ret = rhs; #endif rhs.~MapIterator(); return ret; } Map* &DoReallocMap(Map** oldMap, SizeType newCapacity, Allocator& allocator) { Map **newMap = static_cast(allocator.Malloc(GetMapLayoutSize(newCapacity))); GetMapCapacity(*newMap) = newCapacity; if (!oldMap) { *newMap = new (allocator.Malloc(sizeof(Map))) Map(MapLess(), allocator); } else { *newMap = *oldMap; size_t count = (*oldMap)->size(); std::memcpy(static_cast(GetMapMembers(*newMap)), static_cast(GetMapMembers(*oldMap)), count * sizeof(Member)); MapIterator *oldIt = GetMapIterators(*oldMap), *newIt = GetMapIterators(*newMap); while (count--) { new (&newIt[count]) MapIterator(DropMapIterator(oldIt[count])); } Allocator::Free(oldMap); } return *newMap; } RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { return GetMapMembers(DoReallocMap(0, capacity, allocator)); } void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { ObjectData& o = data_.o; if (newCapacity > o.capacity) { Member* oldMembers = GetMembersPointer(); Map **oldMap = oldMembers ? &GetMap(oldMembers) : 0, *&newMap = DoReallocMap(oldMap, newCapacity, allocator); RAPIDJSON_SETPOINTER(Member, o.members, GetMapMembers(newMap)); o.capacity = newCapacity; } } template MemberIterator DoFindMember(const GenericValue& name) { if (Member* members = GetMembersPointer()) { Map* &map = GetMap(members); MapIterator mit = map->find(reinterpret_cast(name.data_)); if (mit != map->end()) { return MemberIterator(&members[mit->second]); } } return MemberEnd(); } void DoClearMembers() { if (Member* members = GetMembersPointer()) { Map* &map = GetMap(members); MapIterator* mit = GetMapIterators(map); for (SizeType i = 0; i < data_.o.size; i++) { map->erase(DropMapIterator(mit[i])); members[i].~Member(); } data_.o.size = 0; } } void DoFreeMembers() { if (Member* members = GetMembersPointer()) { GetMap(members)->~Map(); for (SizeType i = 0; i < data_.o.size; i++) { members[i].~Member(); } if (Allocator::kNeedFree) { // Shortcut by Allocator's trait Map** map = &GetMap(members); Allocator::Free(*map); Allocator::Free(map); } } } #else // !RAPIDJSON_USE_MEMBERSMAP RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { return Malloc(allocator, capacity); } void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { ObjectData& o = data_.o; if (newCapacity > o.capacity) { Member* newMembers = Realloc(allocator, GetMembersPointer(), o.capacity, newCapacity); RAPIDJSON_SETPOINTER(Member, o.members, newMembers); o.capacity = newCapacity; } } template MemberIterator DoFindMember(const GenericValue& name) { MemberIterator member = MemberBegin(); for ( ; member != MemberEnd(); ++member) if (name.StringEqual(member->name)) break; return member; } void DoClearMembers() { for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) m->~Member(); data_.o.size = 0; } void DoFreeMembers() { for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) m->~Member(); Allocator::Free(GetMembersPointer()); } #endif // !RAPIDJSON_USE_MEMBERSMAP void DoAddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { ObjectData& o = data_.o; if (o.size >= o.capacity) DoReserveMembers(o.capacity ? (o.capacity + (o.capacity + 1) / 2) : kDefaultObjectCapacity, allocator); Member* members = GetMembersPointer(); Member* m = members + o.size; m->name.RawAssign(name); m->value.RawAssign(value); #if RAPIDJSON_USE_MEMBERSMAP Map* &map = GetMap(members); MapIterator* mit = GetMapIterators(map); new (&mit[o.size]) MapIterator(map->insert(MapPair(m->name.data_, o.size))); #endif ++o.size; } MemberIterator DoRemoveMember(MemberIterator m) { ObjectData& o = data_.o; Member* members = GetMembersPointer(); #if RAPIDJSON_USE_MEMBERSMAP Map* &map = GetMap(members); MapIterator* mit = GetMapIterators(map); SizeType mpos = static_cast(&*m - members); map->erase(DropMapIterator(mit[mpos])); #endif MemberIterator last(members + (o.size - 1)); if (o.size > 1 && m != last) { #if RAPIDJSON_USE_MEMBERSMAP new (&mit[mpos]) MapIterator(DropMapIterator(mit[&*last - members])); mit[mpos]->second = mpos; #endif *m = *last; // Move the last one to this place } else { m->~Member(); // Only one left, just destroy } --o.size; return m; } MemberIterator DoEraseMembers(ConstMemberIterator first, ConstMemberIterator last) { ObjectData& o = data_.o; MemberIterator beg = MemberBegin(), pos = beg + (first - beg), end = MemberEnd(); #if RAPIDJSON_USE_MEMBERSMAP Map* &map = GetMap(GetMembersPointer()); MapIterator* mit = GetMapIterators(map); #endif for (MemberIterator itr = pos; itr != last; ++itr) { #if RAPIDJSON_USE_MEMBERSMAP map->erase(DropMapIterator(mit[itr - beg])); #endif itr->~Member(); } #if RAPIDJSON_USE_MEMBERSMAP if (first != last) { // Move remaining members/iterators MemberIterator next = pos + (last - first); for (MemberIterator itr = pos; next != end; ++itr, ++next) { std::memcpy(static_cast(&*itr), &*next, sizeof(Member)); SizeType mpos = static_cast(itr - beg); new (&mit[mpos]) MapIterator(DropMapIterator(mit[next - beg])); mit[mpos]->second = mpos; } } #else std::memmove(static_cast(&*pos), &*last, static_cast(end - last) * sizeof(Member)); #endif o.size -= static_cast(last - first); return pos; } template void DoCopyMembers(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings) { RAPIDJSON_ASSERT(rhs.GetType() == kObjectType); data_.f.flags = kObjectFlag; SizeType count = rhs.data_.o.size; Member* lm = DoAllocMembers(count, allocator); const typename GenericValue::Member* rm = rhs.GetMembersPointer(); #if RAPIDJSON_USE_MEMBERSMAP Map* &map = GetMap(lm); MapIterator* mit = GetMapIterators(map); #endif 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); #if RAPIDJSON_USE_MEMBERSMAP new (&mit[i]) MapIterator(map->insert(MapPair(lm[i].name.data_, i))); #endif } data_.o.size = data_.o.capacity = count; SetMembersPointer(lm); } // 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(allocator.Malloc(count * sizeof(GenericValue))); SetElementsPointer(e); std::memcpy(static_cast(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 = DoAllocMembers(count, allocator); SetMembersPointer(m); std::memcpy(static_cast(m), members, count * sizeof(Member)); #if RAPIDJSON_USE_MEMBERSMAP Map* &map = GetMap(m); MapIterator* mit = GetMapIterators(map); for (SizeType i = 0; i < count; i++) { new (&mit[i]) MapIterator(map->insert(MapPair(m[i].name.data_, i))); } #endif } 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(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 bool StringEqual(const GenericValue& 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 > Value; /////////////////////////////////////////////////////////////////////////////// // 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 class GenericDocument : public GenericValue { public: typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. typedef GenericValue ValueType; //!< Value type of the document. typedef Allocator AllocatorType; //!< Allocator type from template parameter. typedef StackAllocator StackAllocatorType; //!< StackAllocator 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(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(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() { // Clear the ::ValueType before ownAllocator is destroyed, ~ValueType() // runs last and may access its elements or members which would be freed // with an allocator like MemoryPoolAllocator (CrtAllocator does not // free its data when destroyed, but MemoryPoolAllocator does). if (ownAllocator_) { ValueType::SetNull(); } 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(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 bool f(Handler) prototype. \param g Generator functor which sends SAX events to the parameter. \return The document itself for fluent API. */ template 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(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 GenericDocument& ParseStream(InputStream& is) { GenericReader reader( stack_.HasAllocator() ? &stack_.GetAllocator() : 0); ClearStackOnExit scope(*this); parseResult_ = reader.template Parse(is, *this); if (parseResult_) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object ValueType::operator=(*stack_.template Pop(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 GenericDocument& ParseStream(InputStream& is) { return ParseStream(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 GenericDocument& ParseStream(InputStream& is) { return ParseStream(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 GenericDocument& ParseInsitu(Ch* str) { GenericInsituStringStream s(str); return ParseStream(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(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 GenericDocument& Parse(const typename SourceEncoding::Ch* str) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); GenericStringStream s(str); return ParseStream(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 GenericDocument& Parse(const Ch* str) { return Parse(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(str); } template GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); EncodedInputStream is(ms); ParseStream(is); return *this; } template GenericDocument& Parse(const Ch* str, size_t length) { return Parse(str, length); } GenericDocument& Parse(const Ch* str, size_t length) { return Parse(str, length); } #if RAPIDJSON_HAS_STDSTRING template GenericDocument& Parse(const std::basic_string& str) { // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) return Parse(str.c_str()); } template GenericDocument& Parse(const std::basic_string& str) { return Parse(str.c_str()); } GenericDocument& Parse(const std::basic_string& str) { return Parse(str); } #endif // RAPIDJSON_HAS_STDSTRING //!@} //!@name Handling parse errors //!@{ //! Whether a parse error has occurred 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 friend class GenericReader; // for parsing template friend class GenericValue; // for deep copying public: // Implementation of Handler bool Null() { new (stack_.template Push()) ValueType(); return true; } bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } bool RawNumber(const Ch* str, SizeType length, bool copy) { if (copy) new (stack_.template Push()) ValueType(str, length, GetAllocator()); else new (stack_.template Push()) ValueType(str, length); return true; } bool String(const Ch* str, SizeType length, bool copy) { if (copy) new (stack_.template Push()) ValueType(str, length, GetAllocator()); else new (stack_.template Push()) ValueType(str, length); return true; } bool StartObject() { new (stack_.template Push()) 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(memberCount); stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); return true; } bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } bool EndArray(SizeType elementCount) { ValueType* elements = stack_.template Pop(elementCount); stack_.template Top()->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(1))->~ValueType(); else stack_.Clear(); stack_.ShrinkToFit(); } void Destroy() { RAPIDJSON_DELETE(ownAllocator_); } static const size_t kDefaultStackCapacity = 1024; Allocator* allocator_; Allocator* ownAllocator_; internal::Stack stack_; ParseResult parseResult_; }; //! GenericDocument with UTF8 encoding typedef GenericDocument > Document; //! 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 class GenericArray { public: typedef GenericArray ConstArray; typedef GenericArray Array; typedef ValueT PlainType; typedef typename internal::MaybeAddConst::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 friend class GenericValue; GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } ~GenericArray() {} operator ValueType&() const { return value_; } 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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (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 class GenericObject { public: typedef GenericObject ConstObject; typedef GenericObject Object; typedef ValueT PlainType; typedef typename internal::MaybeAddConst::Type ValueType; typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator typedef GenericMemberIterator ConstMemberIterator; typedef typename ValueType::AllocatorType AllocatorType; typedef typename ValueType::StringRefType StringRefType; typedef typename ValueType::EncodingType EncodingType; typedef typename ValueType::Ch Ch; template friend class GenericValue; GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } ~GenericObject() {} operator ValueType&() const { return value_; } SizeType MemberCount() const { return value_.MemberCount(); } SizeType MemberCapacity() const { return value_.MemberCapacity(); } bool ObjectEmpty() const { return value_.ObjectEmpty(); } template ValueType& operator[](T* name) const { return value_[name]; } template ValueType& operator[](const GenericValue& name) const { return value_[name]; } #if RAPIDJSON_HAS_STDSTRING ValueType& operator[](const std::basic_string& 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& name) const { return value_.HasMember(name); } #endif template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } #if RAPIDJSON_HAS_STDSTRING MemberIterator FindMember(const std::basic_string& 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& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (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& name) const { return value_.RemoveMember(name); } #endif template bool RemoveMember(const GenericValue& 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& name) const { return EraseMember(ValueType(StringRef(name))); } #endif template bool EraseMember(const GenericValue& 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 #ifdef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED #pragma pop_macro("GetObject") #undef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED #endif #endif // RAPIDJSON_DOCUMENT_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/svg2png/svg2png.cpp000664 001750 001750 00000032006 15164251010 034036 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif #else #include #include #include #include #endif #include "lodepng.h" #define WIDTH_8K 7680 #define HEIGHT_8K 4320 #define SIZE_8K 33177600 //WIDTH_8K x HEIGHT_8K using namespace std; struct PngBuilder { void build(const string& fileName, const uint32_t width, const uint32_t height, uint32_t* buffer) { //Used ARGB8888 so have to move pixels now vector image; image.resize(width * height * 4); for (unsigned y = 0; y < height; y++) { for (unsigned x = 0; x < width; x++) { uint32_t n = buffer[y * width + x]; image[4 * width * y + 4 * x + 0] = (n >> 16) & 0xff; image[4 * width * y + 4 * x + 1] = (n >> 8) & 0xff; image[4 * width * y + 4 * x + 2] = n & 0xff; image[4 * width * y + 4 * x + 3] = (n >> 24) & 0xff; } } unsigned error = lodepng::encode(fileName, image, width, height); //if there's an error, display it if (error) cout << "encoder error " << error << ": " << lodepng_error_text(error) << endl; } }; struct Renderer { public: int render(const char* path, int w, int h, const string& dst, uint32_t bgColor) { //Canvas if (!canvas) createCanvas(); if (!canvas) { cout << "Error: Canvas failure" << endl; return 1; } //Picture auto picture = tvg::Picture::gen(); tvg::Result result = picture->load(path); if (result == tvg::Result::Unknown) { cout << "Error: Couldn't load image " << path << endl; return 1; } else if (result == tvg::Result::InvalidArguments) { cout << "Error: Couldn't load image(Invalid path or invalid SVG image) : " << path << endl; return 1; } else if (result == tvg::Result::NonSupport) { cout << "Error: Couldn't load image(Not supported extension) : " << path << endl; return 1; } if (w == 0 || h == 0) { float fw, fh; picture->size(&fw, &fh); w = static_cast(fw); h = static_cast(fh); if (fw > w) w++; if (fh > h) h++; if (w * h > SIZE_8K) { float scale = fw / fh; if (scale > 1) { w = WIDTH_8K; h = static_cast(w / scale); } else { h = HEIGHT_8K; w = static_cast(h * scale); } cout << "Warning: The SVG width and/or height values exceed the 8k resolution. " "To avoid the heap overflow, the conversion to the PNG file made in " << w << " x " << h << " resolution." << endl; picture->size(static_cast(w), static_cast(h)); } } else { picture->size(static_cast(w), static_cast(h)); } //Buffer createBuffer(w, h); if (!buffer) { cout << "Error: Buffer failure" << endl; return 1; } if (canvas->target(buffer, w, w, h, tvg::ColorSpace::ARGB8888S) != tvg::Result::Success) { cout << "Error: Canvas target failure" << endl; return 1; } //Background color if needed if (bgColor != 0xffffffff) { uint8_t r = (uint8_t)((bgColor & 0xff0000) >> 16); uint8_t g = (uint8_t)((bgColor & 0x00ff00) >> 8); uint8_t b = (uint8_t)((bgColor & 0x0000ff)); auto shape = tvg::Shape::gen(); shape->appendRect(0, 0, static_cast(w), static_cast(h), 0, 0); shape->fill(r, g, b); if (canvas->add(shape) != tvg::Result::Success) return 1; } //Drawing canvas->add(picture); canvas->draw(true); canvas->sync(); //Build Png PngBuilder builder; builder.build(dst, w, h, buffer); cout << "Generated PNG file: " << dst << endl; return 0; } void terminate() { tvg::Initializer::term(); free(buffer); } private: void createCanvas() { //Threads Count auto threads = thread::hardware_concurrency(); if (threads > 0) --threads; //Initialize ThorVG Engine if (tvg::Initializer::init(threads) != tvg::Result::Success) { cout << "Error: Engine is not supported" << endl; } //Create a Canvas canvas = unique_ptr(tvg::SwCanvas::gen()); } void createBuffer(int w, int h) { uint32_t size = w * h; //Reuse old buffer if size is enough if (buffer && bufferSize >= size) return; //Alloc or realloc buffer buffer = (uint32_t*) realloc(buffer, sizeof(uint32_t) * size); bufferSize = size; } private: unique_ptr canvas = nullptr; uint32_t* buffer = nullptr; uint32_t bufferSize = 0; }; struct App { public: int setup(int argc, char** argv) { vector paths; for (int i = 1; i < argc; i++) { const char* p = argv[i]; if (*p == '-') { //flags const char* p_arg = (i + 1 < argc) ? argv[++i] : nullptr; if (p[1] == 'r') { //image resolution if (!p_arg) { cout << "Error: Missing resolution attribute. Expected eg. -r 200x200." << endl; return 1; } const char* x = strchr(p_arg, 'x'); if (x) { width = atoi(p_arg); height = atoi(x + 1); } if (!x || width <= 0 || height <= 0) { cout << "Error: Resolution (" << p_arg << ") is corrupted. Expected eg. -r 200x200." << endl; return 1; } } else if (p[1] == 'b') { //image background color if (!p_arg) { cout << "Error: Missing background color attribute. Expected eg. -b fa7410." << endl; return 1; } bgColor = (uint32_t) strtol(p_arg, NULL, 16); } else { cout << "Warning: Unknown flag (" << p << ")." << endl; } } else { //arguments paths.push_back(p); } } int ret = 0; if (paths.empty()) { //no attributes - print help return help(); } else { for (auto path : paths) { auto real_path = realFile(path); if (real_path) { if (isDirectory(real_path)) { //load from directory cout << "Trying load from directory \"" << real_path << "\"." << endl; if ((ret = handleDirectory(real_path))) break; } else if (svgFile(path)) { //load single file if ((ret = renderFile(real_path))) break; } else { //not a directory and not .svg file cout << "Error: File \"" << path << "\" is not a proper svg file." << endl; } } else { cout << "Error: Invalid file or path name: \"" << path << "\"" << endl; } } } //Terminate renderer renderer.terminate(); return ret; } private: Renderer renderer; uint32_t bgColor = 0xffffffff; uint32_t width = 0; uint32_t height = 0; char full[PATH_MAX]; private: int help() { cout << "Usage:\n tvg-svg2png [SVG file] or [SVG folder] [-r resolution] [-b bgColor]\n\nFlags:\n -r set the output image resolution.\n -b set the output image background color.\n\nExamples:\n $ tvg-svg2png input.svg\n $ tvg-svg2png input.svg -r 200x200\n $ tvg-svg2png input.svg -r 200x200 -b ff00ff\n $ tvg-svg2png input1.svg input2.svg -r 200x200 -b ff00ff\n $ tvg-svg2png . -r 200x200\n\nNote:\n In the case, where the width and height in the SVG file determine the size of the image in resolution higher than 8k (7680 x 4320), limiting the resolution to this value is enforced.\n\n"; return 1; } bool svgFile(const char* path) { size_t length = strlen(path); return length > 4 && (strcmp(&path[length - 4], ".svg") == 0); } const char* realFile(const char* path) { //real path #ifdef _WIN32 path = _fullpath(full, path, PATH_MAX); #else path = realpath(path, full); #endif return path; } bool isDirectory(const char* path) { #ifdef _WIN32 DWORD attr = GetFileAttributes(path); if (attr == INVALID_FILE_ATTRIBUTES) return false; return attr & FILE_ATTRIBUTE_DIRECTORY; #else struct stat buf; if (stat(path, &buf) != 0) { return false; } return S_ISDIR(buf.st_mode); #endif } int renderFile(const char* path) { if (!path) return 1; //destination png file const char* dot = strrchr(path, '.'); if (!dot) return 1; string dst(path, dot - path); dst += ".png"; return renderer.render(path, width, height, dst, bgColor); } int handleDirectory(const string& path) { //open directory #ifdef _WIN32 WIN32_FIND_DATA fd; HANDLE h = FindFirstFileEx((path + "\\*").c_str(), FindExInfoBasic, &fd, FindExSearchNameMatch, NULL, 0); if (h == INVALID_HANDLE_VALUE) { cout << "Couldn't open directory \"" << path.c_str() << "\"." << endl; return 1; } int ret = 0; do { if (*fd.cFileName == '.' || *fd.cFileName == '$') continue; if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { string subpath = string(path); subpath += '\\'; subpath += fd.cFileName; ret = handleDirectory(subpath); if (ret) break; } else { if (!svgFile(fd.cFileName)) continue; string fullpath = string(path); fullpath += '\\'; fullpath += fd.cFileName; ret = renderFile(fullpath.c_str()); if (ret) break; } } while (FindNextFile(h, &fd)); FindClose(h); #else DIR* dir = opendir(path.c_str()); if (!dir) { cout << "Couldn't open directory \"" << path.c_str() << "\"." << endl; return 1; } //list directory int ret = 0; struct dirent* entry; while ((entry = readdir(dir)) != NULL) { if (*entry->d_name == '.' || *entry->d_name == '$') continue; if (entry->d_type == DT_DIR) { string subpath = string(path); subpath += '/'; subpath += entry->d_name; ret = handleDirectory(subpath); if (ret) break; } else { if (!svgFile(entry->d_name)) continue; string fullpath = string(path); fullpath += '/'; fullpath += entry->d_name; ret = renderFile(fullpath.c_str()); if (ret) break; } } closedir(dir); #endif return ret; } }; int main(int argc, char** argv) { App app; return app.setup(argc, argv); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test12.lot000664 001750 001750 00000006270 15164251010 034055 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":30,"ip":0,"op":60,"w":400,"h":400,"layers":[{"ty":4,"ef":[{"ty":21,"nm":"Fill","v":[{"ty":2,"nm":"Fill Mask","v":{"a":0,"k":[0]}},{"ty":0,"nm":"All Masks","v":{"a":0,"k":0}},{"ty":2,"nm":"Color","v":{"a":0,"k":[1,0,0,1]}},{"ty":0,"nm":"Opacity","v":{"a":0,"k":100}}]},{"ty":20,"nm":"Stroke","v":[{"ty":2,"nm":"Color","v":{"a":0,"k":[0,1,0,1]}},{"ty":0,"nm":"Stroke Width","v":{"a":0,"k":10}}]},{"ty":20,"nm":"Tint","v":[{"ty":2,"nm":"Map Black To","v":{"a":0,"k":[1,0,0,1]}},{"ty":2,"nm":"Map White To","v":{"a":0,"k":[0,0,1,1]}},{"ty":0,"nm":"Amount to Tint","v":{"a":0,"k":50}}]},{"ty":23,"nm":"Tritone","v":[{"ty":2,"nm":"Highlights","v":{"a":0,"k":[1,1,1,1]}},{"ty":2,"nm":"Midtones","v":{"a":0,"k":[0.5,0.5,0.5,1]}},{"ty":2,"nm":"Shadows","v":{"a":0,"k":[0,0,0,1]}}]}],"shapes":[{"ty":"rc","s":{"a":0,"k":[200,200]}},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"w":{"a":0,"k":5}}],"ip":0,"op":60,"ind":1,"nm":"Multiple Effects Layer"},{"ty":4,"nm":"Drop Shadow Effect","ip":0,"op":60,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":191.25}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":135}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":10}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":20}}]}],"shapes":[{"ty":"rc","nm":"Rectangle","p":{"a":0,"k":[200,200]},"s":{"a":0,"k":[150,150]},"r":{"a":0,"k":10}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[1,0.5,0,1]},"o":{"a":0,"k":100}}],"ind":2},{"ty":4,"nm":"Gaussian Blur Effect","ip":0,"op":60,"ef":[{"ty":29,"nm":"Gaussian Blur","np":6,"mn":"ADBE Gaussian Blur 2","ix":1,"en":1,"ef":[{"ty":0,"nm":"Blurriness","mn":"ADBE Gaussian Blur 2-0001","ix":1,"v":{"a":1,"k":[{"t":0,"s":[0],"e":[20]},{"t":30,"s":[20],"e":[0]},{"t":60,"s":[0]}]}},{"ty":7,"nm":"Blur Dimensions","mn":"ADBE Gaussian Blur 2-0002","ix":2,"v":{"a":0,"k":1}},{"ty":7,"nm":"Repeat Edge Pixels","mn":"ADBE Gaussian Blur 2-0003","ix":3,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"el","nm":"Ellipse","p":{"a":0,"k":[200,200]},"s":{"a":0,"k":[120,120]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0,0.8,1,1]},"o":{"a":0,"k":100}}],"ind":3},{"ty":4,"nm":"Custom Effect with Various Properties","ip":0,"op":60,"ef":[{"ty":5,"nm":"Custom Effect","np":10,"mn":"Custom","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider Control","mn":"Slider","ix":1,"v":{"a":0,"k":50}},{"ty":1,"nm":"Angle Control","mn":"Angle","ix":2,"v":{"a":0,"k":45}},{"ty":2,"nm":"Color Control","mn":"Color","ix":3,"v":{"a":0,"k":[1,0,0,1]}},{"ty":3,"nm":"Point Control","mn":"Point","ix":4,"v":{"a":0,"k":[100,100]}},{"ty":4,"nm":"Checkbox Control","mn":"Checkbox","ix":5,"v":{"a":0,"k":1}},{"ty":7,"nm":"Dropdown Control","mn":"Dropdown","ix":6,"v":{"a":0,"k":2}},{"ty":10,"nm":"Effect Layer","mn":"EffectLayer","ix":7,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"sr","nm":"Star","p":{"a":0,"k":[200,200]},"or":{"a":0,"k":80},"ir":{"a":0,"k":40},"pt":{"a":0,"k":5},"sy":1},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[1,1,0,1]},"o":{"a":0,"k":100}}],"ind":4}],"nm":"Layer Effects Tests","assets":[]}mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/raw/tvgRawLoader.cpp000664 001750 001750 00000004215 15164251010 035335 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgLoader.h" #include "tvgRawLoader.h" RawLoader::RawLoader() : ImageLoader(FileType::Raw) { } RawLoader::~RawLoader() { if (copy) tvg::free(surface.buf32); } bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy) { if (!LoadModule::read()) return true; if (!data || w == 0 || h == 0) return false; this->w = (float)w; this->h = (float)h; this->copy = copy; if (copy) { surface.buf32 = tvg::malloc(sizeof(uint32_t) * w * h); if (!surface.buf32) return false; memcpy((void*)surface.buf32, data, sizeof(uint32_t) * w * h); } else surface.buf32 = const_cast(data); //setup the surface surface.stride = w; surface.w = w; surface.h = h; surface.cs = cs; surface.channelSize = sizeof(uint32_t); surface.premultiplied = (cs == ColorSpace::ABGR8888 || cs == ColorSpace::ARGB8888) ? true : false; return true; } bool RawLoader::read() { LoadModule::read(); return true; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/000775 001750 001750 00000000000 15164251010 032612 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/argb.cpp000664 001750 001750 00000004203 15164251010 034576 0ustar00ddennedyddennedy000000 000000 // Copyright 2014 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // ARGB making functions. // // Author: Djordje Pesut (djordje.pesut@imgtec.com) #include "./dsp.h" static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) { return (((uint32_t)a << 24) | (r << 16) | (g << 8) | b); } static void PackARGB(const uint8_t* a, const uint8_t* r, const uint8_t* g, const uint8_t* b, int len, uint32_t* out) { int i; for (i = 0; i < len; ++i) { out[i] = MakeARGB32(a[4 * i], r[4 * i], g[4 * i], b[4 * i]); } } static void PackRGB(const uint8_t* r, const uint8_t* g, const uint8_t* b, int len, int step, uint32_t* out) { int i, offset = 0; for (i = 0; i < len; ++i) { out[i] = MakeARGB32(0xff, r[offset], g[offset], b[offset]); offset += step; } } void (*VP8PackARGB)(const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*, int, uint32_t*); void (*VP8PackRGB)(const uint8_t*, const uint8_t*, const uint8_t*, int, int, uint32_t*); extern void VP8EncDspARGBInitMIPSdspR2(void); extern void VP8EncDspARGBInitSSE2(void); static volatile VP8CPUInfo argb_last_cpuinfo_used = (VP8CPUInfo)&argb_last_cpuinfo_used; WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspARGBInit(void) { if (argb_last_cpuinfo_used == VP8GetCPUInfo) return; VP8PackARGB = PackARGB; VP8PackRGB = PackRGB; // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) if (VP8GetCPUInfo(kSSE2)) { VP8EncDspARGBInitSSE2(); } #endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { VP8EncDspARGBInitMIPSdspR2(); } #endif } argb_last_cpuinfo_used = VP8GetCPUInfo; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test6.lot000664 001750 001750 00000021302 15164251010 033771 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":30,"ip":0,"op":90,"w":400,"h":400,"nm":"Shape Modifiers Tests","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Trim Path Animation","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"tm","s":{"a":1,"k":[{"t":0,"s":[0],"e":[100]},{"t":60,"s":[100]}]},"e":{"a":0,"k":100},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths"},{"ty":"st","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":5},"lc":2,"lj":1,"ml":4,"nm":"Stroke"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Repeated Circles","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[150,150,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"fl","c":{"a":0,"k":[1,0.5,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"},{"ty":"rp","c":{"a":0,"k":8},"o":{"a":0,"k":0},"m":1,"tr":{"ty":"tr","p":{"a":0,"k":[40,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":45},"so":{"a":0,"k":100},"eo":{"a":0,"k":100}},"nm":"Repeater"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Rounded Rectangle","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,80]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"t":0,"s":[5],"e":[30]},{"t":30,"s":[30],"e":[5]},{"t":60,"s":[5]}]},"nm":"Rectangle"},{"ty":"rd","r":{"a":0,"k":15},"nm":"Round Corners"},{"ty":"fl","c":{"a":0,"k":[0.4,0.6,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":3},{"ddd":0,"ind":4,"ty":4,"nm":"Overlay Circle","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[200,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[70,70]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"fl","c":{"a":0,"k":[1,0.8,0.2,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":5},{"ddd":0,"ind":5,"ty":4,"nm":"Star with OffsetPath","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[150,150,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":1,"pt":{"a":0,"k":5},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":60},"os":{"a":0,"k":0},"ir":{"a":0,"k":30},"is":{"a":0,"k":0},"nm":"Star"},{"ty":"op","a":{"a":1,"k":[{"t":0,"s":[0],"e":[10]},{"t":30,"s":[10],"e":[-10]},{"t":60,"s":[-10]}]},"lj":2,"ml":4,"nm":"OffsetPath 1"},{"ty":"st","c":{"a":0,"k":[1,0.5,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":3},"lc":2,"lj":2,"ml":4,"nm":"Stroke"},{"ty":"fl","c":{"a":0,"k":[1,1,0,1]},"o":{"a":0,"k":70},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Rectangle with Multiple Offsets","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[80,80,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[50,50]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":5},"nm":"Rectangle"},{"ty":"op","a":{"a":0,"k":[5]},"lj":1,"ml":10,"nm":"OffsetPath Miter"},{"ty":"st","c":{"a":0,"k":[0,0.8,0.2,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Star with RoundedCorner","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"p":{"a":0,"k":[120,120,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":1,"pt":{"a":0,"k":5},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":1,"k":[{"t":0,"s":[50],"e":[70]},{"t":45,"s":[70],"e":[50]},{"t":90,"s":[50]}]},"os":{"a":1,"k":[{"t":0,"s":[0],"e":[20]},{"t":45,"s":[20],"e":[0]},{"t":90,"s":[0]}]},"ir":{"a":0,"k":[25]},"is":{"a":0,"k":[0]},"nm":"Star with Roundness"},{"ty":"rd","r":{"a":1,"k":[{"t":0,"s":[5],"e":[25]},{"t":45,"s":[25],"e":[5]},{"t":90,"s":[5]}]},"nm":"RoundedCorner Animated"},{"ty":"fl","c":{"a":0,"k":[1,0.6,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"},{"ty":"st","c":{"a":0,"k":[0.2,0.2,0.8,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":3},"lc":2,"lj":2,"ml":4,"nm":"Stroke"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Polygon with RoundedCorner","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[280,120,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":2,"pt":{"a":1,"k":[{"t":0,"s":[3],"e":[8]},{"t":45,"s":[8],"e":[3]},{"t":90,"s":[3]}]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[60]},"os":{"a":0,"k":[0]},"nm":"Polygon"},{"ty":"rd","r":{"a":0,"k":[15]},"nm":"RoundedCorner"},{"ty":"fl","c":{"a":0,"k":[0,1,0.5,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Star with OffsetPath","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[120,280,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":1,"pt":{"a":0,"k":6},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[50]},"os":{"a":0,"k":[10]},"ir":{"a":0,"k":[20]},"is":{"a":0,"k":[5]},"nm":"Star"},{"ty":"op","a":{"a":1,"k":[{"t":0,"s":[0],"e":[8]},{"t":45,"s":[8],"e":[-8]},{"t":90,"s":[-8]}]},"lj":2,"ml":4,"nm":"OffsetPath Animated"},{"ty":"fl","c":{"a":0,"k":[1,0.2,0.6,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Polygon with OffsetPath and RoundedCorner","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[280,280,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":2,"pt":{"a":0,"k":7},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[55]},"os":{"a":0,"k":[0]},"nm":"Heptagon"},{"ty":"rd","r":{"a":1,"k":[{"t":0,"s":[0],"e":[20]},{"t":90,"s":[20]}]},"nm":"RoundedCorner"},{"ty":"op","a":{"a":0,"k":[5]},"lj":1,"ml":10,"nm":"OffsetPath"},{"ty":"fl","c":{"a":0,"k":[0.4,0.2,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Rectangle with Animated RoundedCorner","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,80,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,60]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":[10]},"nm":"Rectangle"},{"ty":"rd","r":{"a":1,"k":[{"t":0,"s":[2],"e":[30]},{"t":30,"s":[30],"e":[2]},{"t":60,"s":[2]}]},"nm":"RoundedCorner Extra"},{"ty":"fl","c":{"a":0,"k":[1,0.3,0.3,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Ellipse with OffsetPath","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[220,80,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[50,50]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"op","a":{"a":1,"k":[{"t":0,"s":[-10],"e":[10]},{"t":30,"s":[10],"e":[-10]},{"t":60,"s":[-10]}]},"lj":1,"ml":4,"nm":"OffsetPath"},{"ty":"st","c":{"a":0,"k":[0,0.7,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"ml":4,"nm":"Stroke"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Path with OffsetPath","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[150,200,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[20,-10],[0,0],[-20,-10]],"o":[[-20,10],[0,0],[20,10],[0,0]],"v":[[-50,0],[0,-30],[50,0],[0,30]],"c":true}},"nm":"Custom Path"},{"ty":"op","a":{"a":1,"k":[{"t":0,"s":[0],"e":[15]},{"t":30,"s":[15],"e":[-15]},{"t":60,"s":[-15]}]},"lj":2,"ml":4,"nm":"OffsetPath Round"},{"ty":"fl","c":{"a":0,"k":[1,1,0,1]},"o":{"a":0,"k":80},"r":1,"nm":"Fill"},{"ty":"st","c":{"a":0,"k":[1,0,0.5,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":2},"lc":2,"lj":2,"ml":4,"nm":"Stroke"}],"ip":0,"op":60,"st":0,"bm":0},{"ty":4,"nm":"Round Corner Test","ip":0,"op":60,"shapes":[{"ty":"rc","nm":"Rectangle","p":{"a":0,"k":[200,200]},"s":{"a":0,"k":[150,150]},"r":{"a":0,"k":30}},{"ty":"rd","nm":"Round Corners","r":{"a":0,"k":20}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0,0,1,1]},"o":{"a":0,"k":100}}],"ind":14},{"ty":4,"nm":"Round Corner with Path","ip":0,"op":60,"shapes":[{"ty":"sh","nm":"Custom Path","ks":{"a":0,"k":{"c":true,"v":[[100,100],[300,100],[300,300],[100,300]],"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"rd","nm":"Round Corners","r":{"a":0,"k":40}},{"ty":"st","nm":"Stroke","c":{"a":0,"k":[1,0,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8}}],"ind":15}],"markers":[]}src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_webp/tvgWebpLoader.h000664 001750 001750 00000003261 15164251010 037135 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WEBP_LOADER_H_ #define _TVG_WEBP_LOADER_H_ #include "tvgLoader.h" #include "tvgTaskScheduler.h" class WebpLoader : public ImageLoader, public Task { public: WebpLoader(); ~WebpLoader(); bool open(const char* path) override; bool open(const char* data, uint32_t size, const char* rpath, bool copy) override; bool read() override; RenderSurface* bitmap() override; private: void run(unsigned tid) override; unsigned char* data = nullptr; uint32_t size = 0; bool freeData = false; }; #endif //_TVG_WEBP_LOADER_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/raw/meson.build000664 001750 001750 00000000266 15164251010 034374 0ustar00ddennedyddennedy000000 000000 source_file = [ 'tvgRawLoader.h', 'tvgRawLoader.cpp', ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.cpp000664 001750 001750 00000012374 15164251010 055073 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-gc.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-regexp-string-iterator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID regexp_string_iterator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup %regexpstringiteratorprototype% ECMA %ArrayIteratorPrototype% object built-in * @{ */ /** * The %RegExpStringIteratorPrototype% object's 'next' routine * * See also: * ECMA-262 v11, 21.2.7.1.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object, if success * error - otherwise */ #ifdef JERRY_BUILTIN_REGEXP static ecma_value_t ecma_builtin_regexp_string_iterator_prototype_object_next (ecma_value_t this_val) /**< this argument */ { /* 2. */ if (!ecma_is_value_object (this_val)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (this_val); /* 3. */ if (!ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_ITERATOR); } ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) obj_p; /* 4. */ if (ecma_is_value_empty (regexp_string_iterator_obj->iterated_string)) { return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } /* 5. */ ecma_value_t regexp = regexp_string_iterator_obj->iterating_regexp; /* 6. */ ecma_value_t matcher_str_value = regexp_string_iterator_obj->iterated_string; ecma_string_t *matcher_str_p = ecma_get_string_from_value (matcher_str_value); /* 9. */ ecma_value_t match = ecma_op_regexp_exec (regexp, matcher_str_p); if (ECMA_IS_VALUE_ERROR (match)) { return match; } /* 10. */ if (ecma_is_value_null (match)) { ecma_free_value (regexp_string_iterator_obj->iterated_string); regexp_string_iterator_obj->iterated_string = ECMA_VALUE_EMPTY; return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } ecma_object_t *match_result_array_p = ecma_get_object_from_value (match); ecma_value_t result = ECMA_VALUE_ERROR; /* 11. */ if (regexp_string_iterator_obj->header.u.cls.u1.regexp_string_iterator_flags & RE_FLAG_GLOBAL) { ecma_value_t matched_str_value = ecma_op_object_get_by_index (match_result_array_p, 0); if (ECMA_IS_VALUE_ERROR (matched_str_value)) { goto free_variables; } ecma_string_t *matched_str_p = ecma_op_to_string (matched_str_value); ecma_free_value (matched_str_value); if (JERRY_UNLIKELY (matched_str_p == NULL)) { goto free_variables; } if (ecma_string_is_empty (matched_str_p)) { ecma_object_t *regexp_obj_p = ecma_get_object_from_value (regexp); ecma_value_t get_last_index = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_LASTINDEX_UL); if (ECMA_IS_VALUE_ERROR (get_last_index)) { goto free_variables; } ecma_length_t this_index; ecma_value_t to_len = ecma_op_to_length (get_last_index, &this_index); ecma_free_value (get_last_index); if (ECMA_IS_VALUE_ERROR (to_len)) { goto free_variables; } uint8_t flags = regexp_string_iterator_obj->header.u.cls.u1.regexp_string_iterator_flags; ecma_length_t next_index = ecma_op_advance_string_index (matcher_str_p, this_index, (flags & RE_FLAG_UNICODE) != 0); ecma_value_t next_index_value = ecma_make_length_value (next_index); ecma_value_t set = ecma_op_object_put (regexp_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), next_index_value, true); ecma_free_value (next_index_value); if (ECMA_IS_VALUE_ERROR (set)) { goto free_variables; } } else { ecma_deref_ecma_string (matched_str_p); } } else { ecma_free_value (regexp_string_iterator_obj->iterated_string); regexp_string_iterator_obj->iterated_string = ECMA_VALUE_EMPTY; } result = ecma_create_iter_result_object (match, ECMA_VALUE_FALSE); free_variables: ecma_deref_object (match_result_array_p); return result; } /* ecma_builtin_regexp_string_iterator_prototype_object_next */ #endif /** * @} * @} * @} */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-bigint64array.cpp000664 001750 001750 00000004511 15164251010 053246 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_BUILTIN_BIGINT #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-bigint64array.inc.h" #define BUILTIN_UNDERSCORED_ID bigint64array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup bigint64array ECMA BigInt64Array object built-in * @{ */ /** * Handle calling [[Call]] of BigInt64Array * * @return ecma value */ ecma_value_t ecma_builtin_bigint64array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_BIGINT64_ARRAY_REQUIRES_NEW); } /* ecma_builtin_bigint64array_dispatch_call */ /** * Handle calling [[Construct]] of BigInt64Array * * @return ecma value */ ecma_value_t ecma_builtin_bigint64array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_BIGINT64_ARRAY); } /* ecma_builtin_bigint64array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_BIGINT */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers.cpp000664 001750 001750 00000152342 15164251010 044216 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-helpers.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtins.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-lcache.h" #include "ecma-property-hashmap.h" #include "byte-code.h" #include "jcontext.h" #include "jrt-bit-fields.h" #include "re-compiler.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ JERRY_STATIC_ASSERT (((int)ECMA_OBJECT_TYPE_MASK >= ((int)ECMA_OBJECT_TYPE__MAX) - 1), ecma_object_types_must_be_lower_than_the_container_mask); JERRY_STATIC_ASSERT (((int)ECMA_OBJECT_TYPE_MASK >= (int)ECMA_LEXICAL_ENVIRONMENT_TYPE__MAX), ecma_lexical_environment_types_must_be_lower_than_the_container_mask); JERRY_STATIC_ASSERT (((int)ECMA_OBJECT_FLAG_EXTENSIBLE == ((int)ECMA_OBJECT_TYPE_MASK) + 1), ecma_extensible_flag_must_follow_the_object_type); JERRY_STATIC_ASSERT ((ECMA_OBJECT_REF_ONE == (ECMA_OBJECT_FLAG_EXTENSIBLE << 1)), ecma_object_ref_one_must_follow_the_extensible_flag); JERRY_STATIC_ASSERT (((ECMA_OBJECT_MAX_REF + ECMA_OBJECT_REF_ONE) == ECMA_OBJECT_REF_MASK), ecma_object_max_ref_does_not_fill_the_remaining_bits); JERRY_STATIC_ASSERT (((ECMA_OBJECT_REF_MASK & (ECMA_OBJECT_TYPE_MASK | ECMA_OBJECT_FLAG_EXTENSIBLE)) == 0), ecma_object_ref_mask_overlaps_with_object_type_or_extensible); JERRY_STATIC_ASSERT ((ECMA_PROPERTY_FLAGS_MASK == ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE), ecma_property_flags_mask_must_use_the_configurable_enumerable_writable_flags); /* These checks are needed by ecma_get_object_base_type. */ JERRY_STATIC_ASSERT (((int) ECMA_OBJECT_TYPE_BUILT_IN_GENERAL == ((int) ECMA_OBJECT_TYPE_GENERAL | 0x1)) && (((int) ECMA_OBJECT_TYPE_GENERAL & 0x1) == 0), ecma_object_type_built_in_general_has_unexpected_value); JERRY_STATIC_ASSERT (((int) ECMA_OBJECT_TYPE_BUILT_IN_CLASS == ((int) ECMA_OBJECT_TYPE_CLASS | 0x1)) && (((int) ECMA_OBJECT_TYPE_CLASS & 0x1) == 0), ecma_object_type_built_in_class_has_unexpected_value); JERRY_STATIC_ASSERT (((int) ECMA_OBJECT_TYPE_BUILT_IN_ARRAY == ((int) ECMA_OBJECT_TYPE_ARRAY | 0x1)) && (((int) ECMA_OBJECT_TYPE_ARRAY & 0x1) == 0), ecma_object_type_built_in_array_has_unexpected_value); /** * Create an object with specified prototype object * (or NULL prototype if there is not prototype for the object) * and value of 'Extensible' attribute. * * Reference counter's value will be set to one. * * @return pointer to the object's descriptor */ ecma_object_t * ecma_create_object (ecma_object_t *prototype_object_p, /**< pointer to prototype of the object (or NULL) */ size_t ext_object_size, /**< size of extended objects */ ecma_object_type_t type) /**< object type */ { ecma_object_t *new_object_p; if (ext_object_size > 0) { new_object_p = (ecma_object_t *) ecma_alloc_extended_object (ext_object_size); } else { new_object_p = ecma_alloc_object (); } new_object_p->type_flags_refs = (ecma_object_descriptor_t) (type | ECMA_OBJECT_FLAG_EXTENSIBLE); ecma_init_gc_info (new_object_p); new_object_p->u1.property_list_cp = JMEM_CP_NULL; ECMA_SET_POINTER (new_object_p->u2.prototype_cp, prototype_object_p); return new_object_p; } /* ecma_create_object */ /** * Create a declarative lexical environment with specified outer lexical environment * (or NULL if the environment is not nested). * * See also: ECMA-262 v5, 10.2.1.1 * * Reference counter's value will be set to one. * * @return pointer to the descriptor of lexical environment */ ecma_object_t * ecma_create_decl_lex_env (ecma_object_t *outer_lexical_environment_p) /**< outer lexical environment */ { ecma_object_t *new_lexical_environment_p = ecma_alloc_object (); new_lexical_environment_p->type_flags_refs = ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE; ecma_init_gc_info (new_lexical_environment_p); new_lexical_environment_p->u1.property_list_cp = JMEM_CP_NULL; ECMA_SET_POINTER (new_lexical_environment_p->u2.outer_reference_cp, outer_lexical_environment_p); return new_lexical_environment_p; } /* ecma_create_decl_lex_env */ /** * Create a object lexical environment with specified outer lexical environment * (or NULL if the environment is not nested), and binding object. * * See also: ECMA-262 v5, 10.2.1.2 * * Reference counter's value will be set to one. * * @return pointer to the descriptor of lexical environment */ ecma_object_t * ecma_create_object_lex_env (ecma_object_t *outer_lexical_environment_p, /**< outer lexical environment */ ecma_object_t *binding_obj_p) /**< binding object */ { JERRY_ASSERT (binding_obj_p != NULL && !ecma_is_lexical_environment (binding_obj_p)); ecma_object_t *new_lexical_environment_p = ecma_alloc_object (); new_lexical_environment_p->type_flags_refs = ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND; ecma_init_gc_info (new_lexical_environment_p); ECMA_SET_NON_NULL_POINTER (new_lexical_environment_p->u1.bound_object_cp, binding_obj_p); ECMA_SET_POINTER (new_lexical_environment_p->u2.outer_reference_cp, outer_lexical_environment_p); return new_lexical_environment_p; } /* ecma_create_object_lex_env */ /** * Create a lexical environment with a specified size. * * @return pointer to the descriptor of the lexical environment */ ecma_object_t * ecma_create_lex_env_class (ecma_object_t *outer_lexical_environment_p, /**< outer lexical environment */ size_t lexical_env_size) /**< size of the lexical environment */ { ecma_object_t *new_lexical_environment_p; ecma_object_descriptor_t type_flags_refs = ECMA_LEXICAL_ENVIRONMENT_CLASS; if (lexical_env_size > 0) { new_lexical_environment_p = (ecma_object_t *) ecma_alloc_extended_object (lexical_env_size); type_flags_refs |= ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA; } else { new_lexical_environment_p = ecma_alloc_object (); } new_lexical_environment_p->type_flags_refs = type_flags_refs; ecma_init_gc_info (new_lexical_environment_p); new_lexical_environment_p->u1.property_list_cp = JMEM_CP_NULL; ECMA_SET_POINTER (new_lexical_environment_p->u2.outer_reference_cp, outer_lexical_environment_p); return new_lexical_environment_p; } /* ecma_create_lex_env_class */ /** * Check if the object is lexical environment. * * @return true - if object is a lexical environment * false - otherwise */ bool JERRY_ATTR_PURE ecma_is_lexical_environment (const ecma_object_t *object_p) /**< object or lexical environment */ { JERRY_ASSERT (object_p != NULL); return (object_p->type_flags_refs & ECMA_OBJECT_TYPE_MASK) >= ECMA_LEXICAL_ENVIRONMENT_TYPE_START; } /* ecma_is_lexical_environment */ /** * Set value of [[Extensible]] object's internal property. */ void ecma_op_ordinary_object_set_extensible (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (!ecma_is_lexical_environment (object_p)); object_p->type_flags_refs |= ECMA_OBJECT_FLAG_EXTENSIBLE; } /* ecma_op_ordinary_object_set_extensible */ /** * Get the internal type of an object. * * @return type of the object (ecma_object_type_t) */ ecma_object_type_t JERRY_ATTR_PURE ecma_get_object_type (const ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (!ecma_is_lexical_environment (object_p)); return (ecma_object_type_t) (object_p->type_flags_refs & ECMA_OBJECT_TYPE_MASK); } /* ecma_get_object_type */ /** * Get the internal base type of an object. * * @return base type of the object (ecma_object_base_type_t) */ ecma_object_base_type_t JERRY_ATTR_PURE ecma_get_object_base_type (const ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (!ecma_is_lexical_environment (object_p)); return (ecma_object_base_type_t) (object_p->type_flags_refs & (ECMA_OBJECT_TYPE_MASK - 0x1)); } /* ecma_get_object_base_type */ /** * Get value of an object if the class matches * * @return value of the object if the class matches * ECMA_VALUE_NOT_FOUND otherwise */ bool ecma_object_class_is (ecma_object_t *object_p, /**< object */ ecma_object_class_type_t class_id) /**< class id */ { if (ecma_get_object_base_type (object_p) == ECMA_OBJECT_BASE_TYPE_CLASS) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (ext_object_p->u.cls.type == (uint8_t) class_id) { return true; } } return false; } /* ecma_object_class_is */ /** * Get type of lexical environment. * * @return type of the lexical environment (ecma_lexical_environment_type_t) */ ecma_lexical_environment_type_t JERRY_ATTR_PURE ecma_get_lex_env_type (const ecma_object_t *object_p) /**< lexical environment */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (ecma_is_lexical_environment (object_p)); return (ecma_lexical_environment_type_t) (object_p->type_flags_refs & ECMA_OBJECT_TYPE_MASK); } /* ecma_get_lex_env_type */ /** * Get lexical environment's bound object. * * @return pointer to ecma object */ ecma_object_t *JERRY_ATTR_PURE ecma_get_lex_env_binding_object (const ecma_object_t *object_p) /**< object-bound lexical environment */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (ecma_is_lexical_environment (object_p)); JERRY_ASSERT (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND || (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && !ECMA_LEX_ENV_CLASS_IS_MODULE (object_p))); return ECMA_GET_NON_NULL_POINTER (ecma_object_t, object_p->u1.bound_object_cp); } /* ecma_get_lex_env_binding_object */ /** * Create a new lexical environment with the same property list as the passed lexical environment * * @return pointer to the newly created lexical environment */ ecma_object_t * ecma_clone_decl_lexical_environment (ecma_object_t *lex_env_p, /**< declarative lexical environment */ bool copy_values) /**< copy property values as well */ { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); ecma_object_t *outer_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); ecma_object_t *new_lex_env_p = ecma_create_decl_lex_env (outer_lex_env_p); jmem_cpointer_t prop_iter_cp = lex_env_p->u1.property_list_cp; ecma_property_header_t *prop_iter_p; JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL); #if JERRY_PROPERTY_HASHMAP prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prop_iter_cp = prop_iter_p->next_property_cp; } JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL); #endif /* JERRY_PROPERTY_HASHMAP */ do { prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if (prop_iter_p->types[i] != ECMA_PROPERTY_TYPE_DELETED) { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW_DATA (prop_iter_p->types[i])); uint8_t prop_attributes = (uint8_t) (prop_iter_p->types[i] & ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_string_t *name_p = ecma_string_from_property_name (prop_iter_p->types[i], prop_pair_p->names_cp[i]); ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (new_lex_env_p, name_p, prop_attributes, NULL); ecma_deref_ecma_string (name_p); JERRY_ASSERT (property_value_p->value == ECMA_VALUE_UNDEFINED); if (copy_values) { property_value_p->value = ecma_copy_value_if_not_object (prop_pair_p->values[i].value); } else { property_value_p->value = ECMA_VALUE_UNINITIALIZED; } } } prop_iter_cp = prop_iter_p->next_property_cp; } while (prop_iter_cp != JMEM_CP_NULL); ecma_deref_object (lex_env_p); return new_lex_env_p; } /* ecma_clone_decl_lexical_environment */ /** * Create a property in an object and link it into * the object's properties' linked-list (at start of the list). * * @return pointer to the newly created property value */ static ecma_property_value_t * ecma_create_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *name_p, /**< property name */ uint8_t type_and_flags, /**< type and flags, see ecma_property_info_t */ ecma_property_value_t value, /**< property value */ ecma_property_t **out_prop_p) /**< [out] the property is also returned * if this field is non-NULL */ { JERRY_ASSERT (ECMA_PROPERTY_PAIR_ITEM_COUNT == 2); JERRY_ASSERT (name_p != NULL); JERRY_ASSERT (object_p != NULL); jmem_cpointer_t *property_list_head_p = &object_p->u1.property_list_cp; if (*property_list_head_p != ECMA_NULL_POINTER) { /* If the first entry is free (deleted), it is reused. */ ecma_property_header_t *first_property_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, *property_list_head_p); #if JERRY_PROPERTY_HASHMAP bool has_hashmap = false; if (first_property_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { property_list_head_p = &first_property_p->next_property_cp; first_property_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, *property_list_head_p); has_hashmap = true; } #endif /* JERRY_PROPERTY_HASHMAP */ JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (first_property_p)); if (first_property_p->types[0] == ECMA_PROPERTY_TYPE_DELETED) { ecma_property_pair_t *first_property_pair_p = (ecma_property_pair_t *) first_property_p; ecma_property_t name_type; first_property_pair_p->names_cp[0] = ecma_string_to_property_name (name_p, &name_type); first_property_p->types[0] = (ecma_property_t) (type_and_flags | name_type); ecma_property_t *property_p = first_property_p->types + 0; JERRY_ASSERT (ECMA_PROPERTY_VALUE_PTR (property_p) == first_property_pair_p->values + 0); if (out_prop_p != NULL) { *out_prop_p = property_p; } first_property_pair_p->values[0] = value; #if JERRY_PROPERTY_HASHMAP /* The property must be fully initialized before ecma_property_hashmap_insert * is called, because the insert operation may reallocate the hashmap, and * that triggers garbage collection which scans all properties of all objects. * A not fully initialized but queued property may cause a crash. */ if (has_hashmap) { ecma_property_hashmap_insert (object_p, name_p, first_property_pair_p, 0); } #endif /* JERRY_PROPERTY_HASHMAP */ return first_property_pair_p->values + 0; } } /* Otherwise we create a new property pair and use its second value. */ ecma_property_pair_t *first_property_pair_p = ecma_alloc_property_pair (); /* Need to query property_list_head_p again and recheck the existence * of property hashmap, because ecma_alloc_property_pair may delete them. */ property_list_head_p = &object_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP bool has_hashmap = false; if (*property_list_head_p != ECMA_NULL_POINTER) { ecma_property_header_t *first_property_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, *property_list_head_p); if (first_property_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { property_list_head_p = &first_property_p->next_property_cp; has_hashmap = true; } } #endif /* JERRY_PROPERTY_HASHMAP */ /* Just copy the previous value (no need to decompress, compress). */ first_property_pair_p->header.next_property_cp = *property_list_head_p; first_property_pair_p->header.types[0] = ECMA_PROPERTY_TYPE_DELETED; first_property_pair_p->names_cp[0] = LIT_INTERNAL_MAGIC_STRING_DELETED; ecma_property_t name_type; first_property_pair_p->names_cp[1] = ecma_string_to_property_name (name_p, &name_type); first_property_pair_p->header.types[1] = (ecma_property_t) (type_and_flags | name_type); ECMA_SET_NON_NULL_POINTER (*property_list_head_p, &first_property_pair_p->header); ecma_property_t *property_p = first_property_pair_p->header.types + 1; JERRY_ASSERT (ECMA_PROPERTY_VALUE_PTR (property_p) == first_property_pair_p->values + 1); if (out_prop_p != NULL) { *out_prop_p = property_p; } first_property_pair_p->values[1] = value; #if JERRY_PROPERTY_HASHMAP /* See the comment before the other ecma_property_hashmap_insert above. */ if (has_hashmap) { ecma_property_hashmap_insert (object_p, name_p, first_property_pair_p, 1); } #endif /* JERRY_PROPERTY_HASHMAP */ return first_property_pair_p->values + 1; } /* ecma_create_property */ /** * Create named data property with given name, attributes and undefined value * in the specified object. * * @return pointer to the newly created property value */ ecma_property_value_t * ecma_create_named_data_property (ecma_object_t *object_p, /**< object */ ecma_string_t *name_p, /**< property name */ uint8_t prop_attributes, /**< property attributes (See: ecma_property_flags_t) */ ecma_property_t **out_prop_p) /**< [out] the property is also returned * if this field is non-NULL */ { JERRY_ASSERT (object_p != NULL && name_p != NULL); JERRY_ASSERT (ecma_is_lexical_environment (object_p) || !ecma_op_object_is_fast_array (object_p)); JERRY_ASSERT (ecma_find_named_property (object_p, name_p) == NULL); JERRY_ASSERT ((prop_attributes & ~ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_ENUMERABLE_WRITABLE) == 0); uint8_t type_and_flags = ECMA_PROPERTY_FLAG_DATA | prop_attributes; ecma_property_value_t value; value.value = ECMA_VALUE_UNDEFINED; return ecma_create_property (object_p, name_p, type_and_flags, value, out_prop_p); } /* ecma_create_named_data_property */ /** * Create named accessor property with given name, attributes, getter and setter. * * @return pointer to the newly created property value */ ecma_property_value_t * ecma_create_named_accessor_property (ecma_object_t *object_p, /**< object */ ecma_string_t *name_p, /**< property name */ ecma_object_t *get_p, /**< getter */ ecma_object_t *set_p, /**< setter */ uint8_t prop_attributes, /**< property attributes */ ecma_property_t **out_prop_p) /**< [out] the property is also returned * if this field is non-NULL */ { JERRY_ASSERT (object_p != NULL && name_p != NULL); JERRY_ASSERT (ecma_is_lexical_environment (object_p) || !ecma_op_object_is_fast_array (object_p)); JERRY_ASSERT (ecma_find_named_property (object_p, name_p) == NULL); JERRY_ASSERT ((prop_attributes & ~ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_ENUMERABLE) == 0); uint8_t type_and_flags = prop_attributes; ecma_property_value_t value; ECMA_SET_POINTER (value.getter_setter_pair.getter_cp, get_p); ECMA_SET_POINTER (value.getter_setter_pair.setter_cp, set_p); return ecma_create_property (object_p, name_p, type_and_flags, value, out_prop_p); } /* ecma_create_named_accessor_property */ #if JERRY_MODULE_SYSTEM /** * Create property reference */ void ecma_create_named_reference_property (ecma_object_t *object_p, /**< object */ ecma_string_t *name_p, /**< property name */ ecma_value_t reference) /**< property reference */ { JERRY_ASSERT (object_p != NULL && name_p != NULL); JERRY_ASSERT (ecma_find_named_property (object_p, name_p) == NULL); JERRY_ASSERT ((ecma_is_lexical_environment (object_p) && ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)) || ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_MODULE_NAMESPACE)); uint8_t type_and_flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE; ecma_property_value_t value; value.value = reference; ecma_create_property (object_p, name_p, type_and_flags, value, NULL); } /* ecma_create_named_reference_property */ #endif /* JERRY_MODULE_SYSTEM */ /** * Find named data property or named accessor property in a specified object. * * @return pointer to the property, if it is found, * NULL - otherwise. */ ecma_property_t * ecma_find_named_property (ecma_object_t *obj_p, /**< object to find property in */ ecma_string_t *name_p) /**< property's name */ { JERRY_ASSERT (obj_p != NULL); JERRY_ASSERT (name_p != NULL); JERRY_ASSERT (ecma_is_lexical_environment (obj_p) || !ecma_op_object_is_fast_array (obj_p)); #if JERRY_LCACHE ecma_property_t *property_p = ecma_lcache_lookup (obj_p, name_p); if (property_p != NULL) { return property_p; } #else /* !JERRY_LCACHE */ ecma_property_t *property_p = NULL; #endif /* JERRY_LCACHE */ jmem_cpointer_t prop_iter_cp = obj_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { jmem_cpointer_t property_real_name_cp; property_p = ecma_property_hashmap_find ((ecma_property_hashmap_t *) prop_iter_p, name_p, &property_real_name_cp); #if JERRY_LCACHE if (property_p != NULL && !ecma_is_property_lcached (property_p)) { ecma_lcache_insert (obj_p, property_real_name_cp, property_p); } #endif /* JERRY_LCACHE */ return property_p; } } #endif /* JERRY_PROPERTY_HASHMAP */ #if JERRY_PROPERTY_HASHMAP uint32_t steps = 0; #endif /* JERRY_PROPERTY_HASHMAP */ jmem_cpointer_t property_name_cp = ECMA_NULL_POINTER; if (ECMA_IS_DIRECT_STRING (name_p)) { ecma_property_t prop_name_type = (ecma_property_t) ECMA_GET_DIRECT_STRING_TYPE (name_p); property_name_cp = (jmem_cpointer_t) ECMA_GET_DIRECT_STRING_VALUE (name_p); JERRY_ASSERT (prop_name_type > 0); while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; if (prop_pair_p->names_cp[0] == property_name_cp && ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[0]) == prop_name_type) { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[0])); property_p = prop_iter_p->types + 0; break; } if (prop_pair_p->names_cp[1] == property_name_cp && ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[1]) == prop_name_type) { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[1])); property_p = prop_iter_p->types + 1; break; } #if JERRY_PROPERTY_HASHMAP steps++; #endif /* JERRY_PROPERTY_HASHMAP */ prop_iter_cp = prop_iter_p->next_property_cp; } } else { while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; if (ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[0]) == ECMA_DIRECT_STRING_PTR) { property_name_cp = prop_pair_p->names_cp[0]; ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, property_name_cp); if (ecma_compare_ecma_non_direct_strings (name_p, prop_name_p)) { property_p = prop_iter_p->types + 0; break; } } if (ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[1]) == ECMA_DIRECT_STRING_PTR) { property_name_cp = prop_pair_p->names_cp[1]; ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, property_name_cp); if (ecma_compare_ecma_non_direct_strings (name_p, prop_name_p)) { property_p = prop_iter_p->types + 1; break; } } #if JERRY_PROPERTY_HASHMAP steps++; #endif /* JERRY_PROPERTY_HASHMAP */ prop_iter_cp = prop_iter_p->next_property_cp; } } #if JERRY_PROPERTY_HASHMAP if (steps >= (ECMA_PROPERTY_HASHMAP_MINIMUM_SIZE / 2)) { ecma_property_hashmap_create (obj_p); } #endif /* JERRY_PROPERTY_HASHMAP */ #if JERRY_LCACHE if (property_p != NULL && !ecma_is_property_lcached (property_p)) { ecma_lcache_insert (obj_p, property_name_cp, property_p); } #endif /* JERRY_LCACHE */ return property_p; } /* ecma_find_named_property */ /** * Get named data property or named access property in specified object. * * Warning: * the property must exist * * @return pointer to the property, if it is found, * NULL - otherwise. */ ecma_property_value_t * ecma_get_named_data_property (ecma_object_t *obj_p, /**< object to find property in */ ecma_string_t *name_p) /**< property's name */ { JERRY_ASSERT (obj_p != NULL); JERRY_ASSERT (name_p != NULL); JERRY_ASSERT (ecma_is_lexical_environment (obj_p) || !ecma_op_object_is_fast_array (obj_p)); ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p); JERRY_ASSERT (property_p != NULL && ECMA_PROPERTY_IS_RAW_DATA (*property_p)); return ECMA_PROPERTY_VALUE_PTR (property_p); } /* ecma_get_named_data_property */ /** * Delete the object's property referenced by its value pointer. * * Note: specified property must be owned by specified object. */ void ecma_delete_property (ecma_object_t *object_p, /**< object */ ecma_property_value_t *prop_value_p) /**< property value reference */ { jmem_cpointer_t cur_prop_cp = object_p->u1.property_list_cp; ecma_property_header_t *prev_prop_p = NULL; #if JERRY_PROPERTY_HASHMAP ecma_property_hashmap_delete_status hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_NO_HASHMAP; if (cur_prop_cp != JMEM_CP_NULL) { ecma_property_header_t *cur_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, cur_prop_cp); if (cur_prop_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prev_prop_p = cur_prop_p; cur_prop_cp = cur_prop_p->next_property_cp; hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP; } } #endif /* JERRY_PROPERTY_HASHMAP */ while (cur_prop_cp != JMEM_CP_NULL) { ecma_property_header_t *cur_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, cur_prop_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (cur_prop_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) cur_prop_p; for (uint32_t i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if ((prop_pair_p->values + i) == prop_value_p) { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (cur_prop_p->types[i])); #if JERRY_PROPERTY_HASHMAP if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP) { hashmap_status = ecma_property_hashmap_delete (object_p, prop_pair_p->names_cp[i], cur_prop_p->types + i); } #endif /* JERRY_PROPERTY_HASHMAP */ ecma_gc_free_property (object_p, prop_pair_p, i); cur_prop_p->types[i] = ECMA_PROPERTY_TYPE_DELETED; prop_pair_p->names_cp[i] = LIT_INTERNAL_MAGIC_STRING_DELETED; JERRY_ASSERT (ECMA_PROPERTY_PAIR_ITEM_COUNT == 2); if (cur_prop_p->types[1 - i] != ECMA_PROPERTY_TYPE_DELETED) { #if JERRY_PROPERTY_HASHMAP /* The other property is still valid. */ if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP) { ecma_property_hashmap_free (object_p); ecma_property_hashmap_create (object_p); } #endif /* JERRY_PROPERTY_HASHMAP */ return; } JERRY_ASSERT (cur_prop_p->types[i] == ECMA_PROPERTY_TYPE_DELETED); if (prev_prop_p == NULL) { object_p->u1.property_list_cp = cur_prop_p->next_property_cp; } else { prev_prop_p->next_property_cp = cur_prop_p->next_property_cp; } ecma_dealloc_property_pair ((ecma_property_pair_t *) cur_prop_p); #if JERRY_PROPERTY_HASHMAP if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP) { ecma_property_hashmap_free (object_p); ecma_property_hashmap_create (object_p); } #endif /* JERRY_PROPERTY_HASHMAP */ return; } } prev_prop_p = cur_prop_p; cur_prop_cp = cur_prop_p->next_property_cp; } } /* ecma_delete_property */ /** * Check whether the object contains a property */ static void ecma_assert_object_contains_the_property (const ecma_object_t *object_p, /**< ecma-object */ const ecma_property_value_t *prop_value_p, /**< property value */ bool is_data) /**< property should be data property */ { #ifndef JERRY_NDEBUG jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp; JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL); ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prop_iter_cp = prop_iter_p->next_property_cp; } while (prop_iter_cp != JMEM_CP_NULL) { prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if ((prop_pair_p->values + i) == prop_value_p) { JERRY_ASSERT (is_data == ((prop_pair_p->header.types[i] & ECMA_PROPERTY_FLAG_DATA) != 0)); return; } } prop_iter_cp = prop_iter_p->next_property_cp; } #else /* JERRY_NDEBUG */ JERRY_UNUSED (object_p); JERRY_UNUSED (prop_value_p); JERRY_UNUSED (is_data); #endif /* !JERRY_NDEBUG */ } /* ecma_assert_object_contains_the_property */ /** * Assign value to named data property * * Note: * value previously stored in the property is freed * * @return void */ void ecma_named_data_property_assign_value (ecma_object_t *obj_p, /**< object */ ecma_property_value_t *prop_value_p, /**< property value reference */ ecma_value_t value) /**< value to assign */ { ecma_assert_object_contains_the_property (obj_p, prop_value_p, true); ecma_value_assign_value (&prop_value_p->value, value); } /* ecma_named_data_property_assign_value */ /** * Get named accessor property getter-setter-pair * * @return pointer to object's getter-setter pair */ ecma_getter_setter_pointers_t * ecma_get_named_accessor_property (const ecma_property_value_t *prop_value_p) /**< property value reference */ { return (ecma_getter_setter_pointers_t *) &prop_value_p->getter_setter_pair; } /* ecma_get_named_accessor_property */ /** * Set getter of named accessor property */ void ecma_set_named_accessor_property_getter (ecma_object_t *object_p, /**< the property's container */ ecma_property_value_t *prop_value_p, /**< property value reference */ ecma_object_t *getter_p) /**< getter object */ { ecma_assert_object_contains_the_property (object_p, prop_value_p, false); ECMA_SET_POINTER (prop_value_p->getter_setter_pair.getter_cp, getter_p); } /* ecma_set_named_accessor_property_getter */ /** * Set setter of named accessor property */ void ecma_set_named_accessor_property_setter (ecma_object_t *object_p, /**< the property's container */ ecma_property_value_t *prop_value_p, /**< property value reference */ ecma_object_t *setter_p) /**< setter object */ { ecma_assert_object_contains_the_property (object_p, prop_value_p, false); ECMA_SET_POINTER (prop_value_p->getter_setter_pair.setter_cp, setter_p); } /* ecma_set_named_accessor_property_setter */ #if JERRY_MODULE_SYSTEM /** * Construct a reference to a given property * * @return property reference */ ecma_value_t ecma_property_to_reference (ecma_property_t *property_p) /**< data or reference property */ { ecma_property_value_t *referenced_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (!(*property_p & ECMA_PROPERTY_FLAG_DATA)) { return referenced_value_p->value; } jmem_cpointer_tag_t offset = (jmem_cpointer_tag_t) (((uintptr_t) property_p) & 0x1); if (offset == 0) { ++referenced_value_p; } JERRY_ASSERT ((((uintptr_t) referenced_value_p) & (((uintptr_t) 1 << JMEM_ALIGNMENT_LOG) - 1)) == 0); ecma_value_t result; ECMA_SET_NON_NULL_POINTER_TAG (result, referenced_value_p, offset); return result; } /* ecma_property_to_reference */ /** * Gets the referenced property value * * @return pointer to the value */ ecma_property_value_t * ecma_get_property_value_from_named_reference (ecma_property_value_t *reference_p) /**< data property reference */ { ecma_value_t value = reference_p->value; reference_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_property_value_t, value); if (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (value)) { --reference_p; } return reference_p; } /* ecma_get_property_value_from_named_reference */ #endif /* JERRY_MODULE_SYSTEM */ /** * Get property's 'Writable' attribute value * * @return true - property is writable, * false - otherwise */ bool ecma_is_property_writable (ecma_property_t property) /**< property */ { JERRY_ASSERT (property & ECMA_PROPERTY_FLAG_DATA); return (property & ECMA_PROPERTY_FLAG_WRITABLE) != 0; } /* ecma_is_property_writable */ /** * Set property's 'Writable' attribute value */ void ecma_set_property_writable_attr (ecma_property_t *property_p, /**< [in,out] property */ bool is_writable) /**< new value for writable flag */ { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW_DATA (*property_p)); if (is_writable) { *property_p = (uint8_t) (*property_p | ECMA_PROPERTY_FLAG_WRITABLE); } else { *property_p = (uint8_t) (*property_p & ~ECMA_PROPERTY_FLAG_WRITABLE); } } /* ecma_set_property_writable_attr */ /** * Get property's 'Enumerable' attribute value * * @return true - property is enumerable, * false - otherwise */ bool ecma_is_property_enumerable (ecma_property_t property) /**< property */ { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (property)); return (property & ECMA_PROPERTY_FLAG_ENUMERABLE) != 0; } /* ecma_is_property_enumerable */ /** * Set property's 'Enumerable' attribute value */ void ecma_set_property_enumerable_attr (ecma_property_t *property_p, /**< [in,out] property */ bool is_enumerable) /**< new value for enumerable flag */ { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); if (is_enumerable) { *property_p = (uint8_t) (*property_p | ECMA_PROPERTY_FLAG_ENUMERABLE); } else { *property_p = (uint8_t) (*property_p & ~ECMA_PROPERTY_FLAG_ENUMERABLE); } } /* ecma_set_property_enumerable_attr */ /** * Get property's 'Configurable' attribute value * * @return true - property is configurable, * false - otherwise */ bool ecma_is_property_configurable (ecma_property_t property) /**< property */ { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (property)); return (property & ECMA_PROPERTY_FLAG_CONFIGURABLE) != 0; } /* ecma_is_property_configurable */ /** * Set property's 'Configurable' attribute value */ void ecma_set_property_configurable_attr (ecma_property_t *property_p, /**< [in,out] property */ bool is_configurable) /**< new value for configurable flag */ { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); if (is_configurable) { *property_p = (uint8_t) (*property_p | ECMA_PROPERTY_FLAG_CONFIGURABLE); } else { *property_p = (uint8_t) (*property_p & ~ECMA_PROPERTY_FLAG_CONFIGURABLE); } } /* ecma_set_property_configurable_attr */ #if JERRY_LCACHE /** * Check whether the property is registered in LCache * * @return true / false */ bool ecma_is_property_lcached (ecma_property_t *property_p) /**< property */ { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*property_p)); return (*property_p & ECMA_PROPERTY_FLAG_LCACHED) != 0; } /* ecma_is_property_lcached */ /** * Set value of flag indicating whether the property is registered in LCache */ void ecma_set_property_lcached (ecma_property_t *property_p, /**< property */ bool is_lcached) /**< new value for lcached flag */ { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*property_p)); if (is_lcached) { *property_p = (uint8_t) (*property_p | ECMA_PROPERTY_FLAG_LCACHED); } else { *property_p = (uint8_t) (*property_p & ~ECMA_PROPERTY_FLAG_LCACHED); } } /* ecma_set_property_lcached */ #endif /* JERRY_LCACHE */ /** * Construct empty property descriptor, i.e.: * property descriptor with all is_defined flags set to false and the rest - to default value. * * @return empty property descriptor */ ecma_property_descriptor_t ecma_make_empty_property_descriptor (void) { ecma_property_descriptor_t prop_desc; prop_desc.flags = 0; prop_desc.value = ECMA_VALUE_UNDEFINED; prop_desc.get_p = NULL; prop_desc.set_p = NULL; return prop_desc; } /* ecma_make_empty_property_descriptor */ /** * Free values contained in the property descriptor * and make it empty property descriptor */ void ecma_free_property_descriptor (ecma_property_descriptor_t *prop_desc_p) /**< property descriptor */ { if (prop_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) { ecma_free_value (prop_desc_p->value); } if ((prop_desc_p->flags & JERRY_PROP_IS_GET_DEFINED) && prop_desc_p->get_p != NULL) { ecma_deref_object (prop_desc_p->get_p); } if ((prop_desc_p->flags & JERRY_PROP_IS_SET_DEFINED) && prop_desc_p->set_p != NULL) { ecma_deref_object (prop_desc_p->set_p); } *prop_desc_p = ecma_make_empty_property_descriptor (); } /* ecma_free_property_descriptor */ /** * Increase ref count of an extended primitive value. */ void ecma_ref_extended_primitive (ecma_extended_primitive_t *primitive_p) /**< extended primitive value */ { if (JERRY_LIKELY (primitive_p->refs_and_type < ECMA_EXTENDED_PRIMITIVE_MAX_REF)) { primitive_p->refs_and_type += ECMA_EXTENDED_PRIMITIVE_REF_ONE; } else { jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT); } } /* ecma_ref_extended_primitive */ /** * Decrease ref count of an error reference. */ void ecma_deref_exception (ecma_extended_primitive_t *error_ref_p) /**< error reference */ { JERRY_ASSERT (error_ref_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE); error_ref_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE; if (error_ref_p->refs_and_type < ECMA_EXTENDED_PRIMITIVE_REF_ONE) { ecma_free_value (error_ref_p->u.value); jmem_pools_free (error_ref_p, sizeof (ecma_extended_primitive_t)); } } /* ecma_deref_exception */ #if JERRY_BUILTIN_BIGINT /** * Decrease ref count of a bigint value. */ void ecma_deref_bigint (ecma_extended_primitive_t *bigint_p) /**< bigint value */ { JERRY_ASSERT (bigint_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE); bigint_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE; if (bigint_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE) { return; } uint32_t size = ECMA_BIGINT_GET_SIZE (bigint_p); JERRY_ASSERT (size > 0); size_t mem_size = ECMA_BIGINT_GET_BYTE_SIZE (size) + sizeof (ecma_extended_primitive_t); jmem_heap_free_block (bigint_p, mem_size); } /* ecma_deref_bigint */ #endif /* JERRY_BUILTIN_BIGINT */ /** * Create an error reference from a given value. * * Note: * Reference of the value is taken. * * @return error reference value */ ecma_value_t ecma_create_exception (ecma_value_t value, /**< referenced value */ uint32_t options) /**< ECMA_ERROR_API_* options */ { ecma_extended_primitive_t *error_ref_p; error_ref_p = (ecma_extended_primitive_t *) jmem_pools_alloc (sizeof (ecma_extended_primitive_t)); error_ref_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | options; error_ref_p->u.value = value; return ecma_make_extended_primitive_value (error_ref_p, ECMA_TYPE_ERROR); } /* ecma_create_exception */ /** * Create an error reference from the currently thrown error value. * * @return error reference value */ ecma_value_t ecma_create_exception_from_context (void) { uint32_t options = 0; uint32_t status_flags = JERRY_CONTEXT (status_flags); if (status_flags & ECMA_STATUS_ABORT) { options |= ECMA_ERROR_API_FLAG_ABORT; } #if JERRY_VM_THROW if (status_flags & ECMA_STATUS_ERROR_THROWN) { options |= ECMA_ERROR_API_FLAG_THROW_CAPTURED; } #endif /* JERRY_VM_THROW */ return ecma_create_exception (jcontext_take_exception (), options); } /* ecma_create_exception_from_context */ /** * Create an exception from a given object. * * Note: * Reference of the object is taken. * * @return exception value */ ecma_value_t ecma_create_exception_from_object (ecma_object_t *object_p) /**< referenced object */ { return ecma_create_exception (ecma_make_object_value (object_p), 0); } /* ecma_create_exception_from_object */ /** * Raise a new exception from the argument exception value. * * Note: the argument exceptions reference count is decreased */ void ecma_throw_exception (ecma_value_t value) /**< error reference */ { JERRY_ASSERT (!jcontext_has_pending_exception () && !jcontext_has_pending_abort ()); ecma_extended_primitive_t *error_ref_p = ecma_get_extended_primitive_from_value (value); JERRY_ASSERT (error_ref_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE); ecma_value_t referenced_value = error_ref_p->u.value; uint32_t status_flags = JERRY_CONTEXT (status_flags); status_flags |= (ECMA_STATUS_EXCEPTION #if JERRY_VM_THROW | ECMA_STATUS_ERROR_THROWN #endif /* JERRY_VM_THROW */ | ECMA_STATUS_ABORT); if (!(error_ref_p->refs_and_type & ECMA_ERROR_API_FLAG_ABORT)) { status_flags &= ~(uint32_t) ECMA_STATUS_ABORT; } #if JERRY_VM_THROW if (!(error_ref_p->refs_and_type & ECMA_ERROR_API_FLAG_THROW_CAPTURED)) { status_flags &= ~(uint32_t) ECMA_STATUS_ERROR_THROWN; } #endif /* JERRY_VM_THROW */ JERRY_CONTEXT (status_flags) = status_flags; if (error_ref_p->refs_and_type >= 2 * ECMA_EXTENDED_PRIMITIVE_REF_ONE) { error_ref_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE; referenced_value = ecma_copy_value (referenced_value); } else { jmem_pools_free (error_ref_p, sizeof (ecma_extended_primitive_t)); } JERRY_CONTEXT (error_value) = referenced_value; } /* ecma_throw_exception */ /** * Decrease the reference counter of a script value. */ void ecma_script_deref (ecma_value_t script_value) /**< script value */ { cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); script_p->refs_and_type -= CBC_SCRIPT_REF_ONE; if (script_p->refs_and_type >= CBC_SCRIPT_REF_ONE) { return; } size_t script_size = sizeof (cbc_script_t); uint32_t type = script_p->refs_and_type; if (type & CBC_SCRIPT_HAS_USER_VALUE) { script_size += sizeof (ecma_value_t); if (!(type & CBC_SCRIPT_USER_VALUE_IS_OBJECT)) { ecma_value_t user_value = CBC_SCRIPT_GET_USER_VALUE (script_p); JERRY_ASSERT (!ecma_is_value_object (user_value)); ecma_free_value (user_value); } } #if JERRY_SOURCE_NAME ecma_deref_ecma_string (ecma_get_string_from_value (script_p->source_name)); #endif /* JERRY_SOURCE_NAME */ #if JERRY_MODULE_SYSTEM if (type & CBC_SCRIPT_HAS_IMPORT_META) { JERRY_ASSERT (!(type & CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS)); JERRY_ASSERT (ecma_is_value_object (CBC_SCRIPT_GET_IMPORT_META (script_p, type))); script_size += sizeof (ecma_value_t); } #endif /* JERRY_MODULE_SYSTEM */ jmem_heap_free_block (script_p, script_size); } /* ecma_script_deref */ /** * Increase reference counter of Compact * Byte Code or regexp byte code. */ void ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */ { /* Abort program if maximum reference number is reached. */ if (bytecode_p->refs >= UINT16_MAX) { jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT); } bytecode_p->refs++; } /* ecma_bytecode_ref */ /** * Decrease reference counter of Compact * Byte Code or regexp byte code. */ void ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */ { JERRY_ASSERT (bytecode_p->refs > 0); JERRY_ASSERT (!CBC_IS_FUNCTION (bytecode_p->status_flags) || !(bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)); bytecode_p->refs--; if (bytecode_p->refs > 0) { /* Non-zero reference counter. */ return; } if (CBC_IS_FUNCTION (bytecode_p->status_flags)) { ecma_value_t *literal_start_p = NULL; uint32_t literal_end; uint32_t const_literal_end; if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_p; literal_end = args_p->literal_end; const_literal_end = args_p->const_literal_end; literal_start_p = (ecma_value_t *) ((uint8_t *) bytecode_p + sizeof (cbc_uint16_arguments_t)); literal_start_p -= args_p->register_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_p; literal_end = args_p->literal_end; const_literal_end = args_p->const_literal_end; literal_start_p = (ecma_value_t *) ((uint8_t *) bytecode_p + sizeof (cbc_uint8_arguments_t)); literal_start_p -= args_p->register_end; } for (uint32_t i = const_literal_end; i < literal_end; i++) { ecma_compiled_code_t *bytecode_literal_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, literal_start_p[i]); /* Self references are ignored. */ if (bytecode_literal_p != bytecode_p) { ecma_bytecode_deref (bytecode_literal_p); } } ecma_script_deref (((cbc_uint8_arguments_t *) bytecode_p)->script_value); if (bytecode_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS) { ecma_collection_t *collection_p = ecma_compiled_code_get_tagged_template_collection (bytecode_p); /* Since the objects in the tagged template collection are not strong referenced anymore by the compiled code we can treat them as 'new' objects. */ JERRY_CONTEXT (ecma_gc_new_objects) += collection_p->item_count * 2; ecma_collection_free_template_literal (collection_p); } } else { #if JERRY_BUILTIN_REGEXP re_compiled_code_t *re_bytecode_p = (re_compiled_code_t *) bytecode_p; ecma_deref_ecma_string (ecma_get_string_from_value (re_bytecode_p->source)); #endif /* JERRY_BUILTIN_REGEXP */ } jmem_heap_free_block (bytecode_p, ((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG); } /* ecma_bytecode_deref */ /** * Gets the script data assigned to a script / module / function * * @return script data - if available, JMEM_CP_NULL - otherwise */ ecma_value_t ecma_script_get_from_value (ecma_value_t value) /**< compiled code */ { if (!ecma_is_value_object (value)) { return JMEM_CP_NULL; } ecma_object_t *object_p = ecma_get_object_from_value (value); const ecma_compiled_code_t *bytecode_p = NULL; while (true) { switch (ecma_get_object_type (object_p)) { case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (ext_object_p->u.cls.type == ECMA_OBJECT_CLASS_SCRIPT) { bytecode_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, ext_object_p->u.cls.u3.value); break; } #if JERRY_MODULE_SYSTEM if (ext_object_p->u.cls.type == ECMA_OBJECT_CLASS_MODULE) { ecma_module_t *module_p = (ecma_module_t *) object_p; if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE)) { bytecode_p = module_p->u.compiled_code_p; break; } } #endif /* JERRY_MODULE_SYSTEM */ return JMEM_CP_NULL; } case ECMA_OBJECT_TYPE_FUNCTION: { bytecode_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) object_p); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; object_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_object_p->u.bound_function.target_function); continue; } case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION: { return ((ecma_extended_object_t *) object_p)->u.constructor_function.script_value; } default: { return JMEM_CP_NULL; } } JERRY_ASSERT (bytecode_p != NULL); return ((cbc_uint8_arguments_t *) bytecode_p)->script_value; } } /* ecma_script_get_from_value */ /** * Resolve the position of the arguments list start of the compiled code * * @return start position of the arguments list start of the compiled code */ ecma_value_t * ecma_compiled_code_resolve_arguments_start (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */ { JERRY_ASSERT (bytecode_header_p != NULL); uint8_t *byte_p = (uint8_t *) bytecode_header_p; byte_p += ((size_t) bytecode_header_p->size) << JMEM_ALIGNMENT_LOG; if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED)) { return ((ecma_value_t *) byte_p); } if (JERRY_LIKELY (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS))) { return ((ecma_value_t *) byte_p) - ((cbc_uint8_arguments_t *) bytecode_header_p)->argument_end; } return ((ecma_value_t *) byte_p) - ((cbc_uint16_arguments_t *) bytecode_header_p)->argument_end; } /* ecma_compiled_code_resolve_arguments_start */ /** * Resolve the position of the function name of the compiled code * * @return position of the function name of the compiled code */ ecma_value_t * ecma_compiled_code_resolve_function_name (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */ { JERRY_ASSERT (bytecode_header_p != NULL); ecma_value_t *base_p = ecma_compiled_code_resolve_arguments_start (bytecode_header_p); if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR) { base_p--; } return base_p; } /* ecma_compiled_code_resolve_function_name */ /** * Get the tagged template collection of the compiled code * * @return pointer to the tagged template collection */ ecma_collection_t * ecma_compiled_code_get_tagged_template_collection (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */ { JERRY_ASSERT (bytecode_header_p != NULL); JERRY_ASSERT (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS); ecma_value_t *base_p = ecma_compiled_code_resolve_function_name (bytecode_header_p); return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, base_p[-1]); } /* ecma_compiled_code_get_tagged_template_collection */ /** * Get the source name of a compiled code. * * @return source name value */ ecma_value_t ecma_get_source_name (const ecma_compiled_code_t *bytecode_p) /**< compiled code */ { #if JERRY_SOURCE_NAME #if JERRY_SNAPSHOT_EXEC if (JERRY_UNLIKELY (bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_SOURCE_NAME_ANON); } #endif /* JERRY_SNAPSHOT_EXEC */ ecma_value_t script_value = ((cbc_uint8_arguments_t *) bytecode_p)->script_value; return ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value)->source_name; #else /* !JERRY_SOURCE_NAME */ JERRY_UNUSED (bytecode_p); return ecma_make_magic_string_value (LIT_MAGIC_STRING_SOURCE_NAME_ANON); #endif /* !JERRY_SOURCE_NAME */ } /* ecma_get_source_name */ #if (JERRY_STACK_LIMIT != 0) /** * Check the current stack usage by calculating the difference from the initial stack base. * * @return current stack usage in bytes */ uintptr_t JERRY_ATTR_NOINLINE ecma_get_current_stack_usage (void) { volatile int __sp; return (uintptr_t) (JERRY_CONTEXT (stack_base) - (uintptr_t) &__sp); } /* ecma_get_current_stack_usage */ #endif /* (JERRY_STACK_LIMIT != 0) */ /** * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-bigint.cpp000664 001750 001750 00000140774 15164251010 045307 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-bigint.h" #include "ecma-big-uint.h" #include "ecma-exceptions.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "lit-char-helpers.h" #if JERRY_BUILTIN_BIGINT /** * Raise a not enough memory error * * @return ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_bigint_raise_memory_error (void) { return ecma_raise_range_error (ECMA_ERR_ALLOCATE_BIGINT_VALUE); } /* ecma_bigint_raise_memory_error */ /** * Create a single digit long BigInt value * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_bigint_create_from_digit (ecma_bigint_digit_t digit, /* single digit */ bool sign) /* set ECMA_BIGINT_SIGN if true */ { JERRY_ASSERT (digit != 0); ecma_extended_primitive_t *result_value_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); if (JERRY_UNLIKELY (result_value_p == NULL)) { return ecma_bigint_raise_memory_error (); } if (sign) { result_value_p->u.bigint_sign_and_size |= ECMA_BIGINT_SIGN; } *ECMA_BIGINT_GET_DIGITS (result_value_p, 0) = digit; return ecma_make_extended_primitive_value (result_value_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_create_from_digit */ /** * Parse a string and create a BigInt value * * @return ecma BigInt value or a special value allowed by the option flags * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_parse_string (const lit_utf8_byte_t *string_p, /**< string representation of the BigInt */ lit_utf8_size_t size, /**< string size */ uint32_t options) /**< ecma_bigint_parse_string_options_t option bits */ { ecma_bigint_digit_t radix = 10; uint32_t sign = (options & ECMA_BIGINT_PARSE_SET_NEGATIVE) ? ECMA_BIGINT_SIGN : 0; bool allow_underscore = options & ECMA_BIGINT_PARSE_ALLOW_UNDERSCORE; const lit_utf8_byte_t *string_end_p = string_p + size; string_p = ecma_string_trim_front (string_p, string_p + size); size = (lit_utf8_size_t) (string_end_p - string_p); if (size >= 3 && string_p[0] == LIT_CHAR_0) { radix = lit_char_to_radix (string_p[1]); if (radix != 10) { string_p += 2; size -= 2; } } else if (size >= 2) { if (string_p[0] == LIT_CHAR_PLUS) { size--; string_p++; } else if (string_p[0] == LIT_CHAR_MINUS) { sign = ECMA_BIGINT_SIGN; size--; string_p++; } } else if (size == 0) { return ECMA_BIGINT_ZERO; } while (string_p < string_end_p && (*string_p == LIT_CHAR_0 || (*string_p == LIT_CHAR_UNDERSCORE && allow_underscore))) { string_p++; } ecma_extended_primitive_t *result_p = NULL; if (string_p == string_end_p) { return ECMA_BIGINT_ZERO; } do { ecma_bigint_digit_t digit = radix; if (*string_p >= LIT_CHAR_0 && *string_p <= LIT_CHAR_9) { digit = (ecma_bigint_digit_t) (*string_p - LIT_CHAR_0); } else if (*string_p == LIT_CHAR_UNDERSCORE && allow_underscore) { continue; } else { lit_utf8_byte_t character = (lit_utf8_byte_t) LEXER_TO_ASCII_LOWERCASE (*string_p); if (character >= LIT_CHAR_LOWERCASE_A && character <= LIT_CHAR_LOWERCASE_F) { digit = (ecma_bigint_digit_t) (character - (LIT_CHAR_LOWERCASE_A - 10)); } else if (ecma_string_trim_front (string_p, string_end_p) == string_end_p) { string_p = string_end_p; break; } } if (digit >= radix) { if (result_p != NULL) { ecma_deref_bigint (result_p); } if (options & ECMA_BIGINT_PARSE_DISALLOW_SYNTAX_ERROR) { return ECMA_VALUE_FALSE; } return ecma_raise_syntax_error (ECMA_ERR_STRING_CANNOT_BE_CONVERTED_TO_BIGINT_VALUE); } result_p = ecma_big_uint_mul_digit (result_p, radix, digit); if (JERRY_UNLIKELY (result_p == NULL)) { break; } } while (++string_p < string_end_p); if (JERRY_UNLIKELY (result_p == NULL)) { if (options & ECMA_BIGINT_PARSE_DISALLOW_MEMORY_ERROR) { return ECMA_VALUE_NULL; } return ecma_bigint_raise_memory_error (); } result_p->u.bigint_sign_and_size |= sign; return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_parse_string */ /** * Parse a string value and create a BigInt value * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_parse_string_value (ecma_value_t string, /**< ecma string */ uint32_t options) /**< ecma_bigint_parse_string_options_t option bits */ { JERRY_ASSERT (ecma_is_value_string (string)); ECMA_STRING_TO_UTF8_STRING (ecma_get_string_from_value (string), string_buffer_p, string_buffer_size); ecma_value_t result = ecma_bigint_parse_string (string_buffer_p, string_buffer_size, options); ECMA_FINALIZE_UTF8_STRING (string_buffer_p, string_buffer_size); return result; } /* ecma_bigint_parse_string_value */ /** * Create a string representation for a BigInt value * * @return ecma string or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_string_t * ecma_bigint_to_string (ecma_value_t value, /**< BigInt value */ ecma_bigint_digit_t radix) /**< conversion radix */ { JERRY_ASSERT (ecma_is_value_bigint (value)); if (value == ECMA_BIGINT_ZERO) { return ecma_new_ecma_string_from_code_unit (LIT_CHAR_0); } uint32_t char_start_p, char_size_p; ecma_extended_primitive_t *bigint_p = ecma_get_extended_primitive_from_value (value); lit_utf8_byte_t *string_buffer_p = ecma_big_uint_to_string (bigint_p, radix, &char_start_p, &char_size_p); if (JERRY_UNLIKELY (string_buffer_p == NULL)) { ecma_raise_range_error (ECMA_ERR_ALLOCATE_BIGINT_STRING); return NULL; } JERRY_ASSERT (char_start_p > 0); if (bigint_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) { string_buffer_p[--char_start_p] = LIT_CHAR_MINUS; } ecma_string_t *string_p; string_p = ecma_new_ecma_string_from_ascii (string_buffer_p + char_start_p, char_size_p - char_start_p); jmem_heap_free_block (string_buffer_p, char_size_p); return string_p; } /* ecma_bigint_to_string */ /** * Get the size of zero digits from the result of ecma_bigint_number_to_digits */ #define ECMA_BIGINT_NUMBER_TO_DIGITS_GET_ZERO_SIZE(value) (((value) &0xffff) * (uint32_t) sizeof (ecma_bigint_digit_t)) /** * Get the number of digits from the result of ecma_bigint_number_to_digits */ #define ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS(value) ((value) >> 20) /** * Get the size of digits from the result of ecma_bigint_number_to_digits */ #define ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS_SIZE(value) \ (ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS (value) * (uint32_t) sizeof (ecma_bigint_digit_t)) /** * Set number of digits in the result of ecma_bigint_number_to_digits */ #define ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS(value) ((uint32_t) (value) << 20) /** * This flag is set when the number passed to ecma_bigint_number_to_digits has fraction part */ #define ECMA_BIGINT_NUMBER_TO_DIGITS_HAS_FRACTION 0x10000 /** * Convert a number to maximum of 3 digits and left shift * * @return packed value, ECMA_BIGINT_NUMBER_TO_DIGITS* macros can be used to decode it */ static uint32_t ecma_bigint_number_to_digits (ecma_number_t number, /**< ecma number */ ecma_bigint_digit_t *digits_p) /**< [out] BigInt digits */ { if (ecma_number_is_zero (number)) { return ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS (0); } ecma_binary_num_t binary = ecma_number_to_binary (number); uint32_t biased_exp = ecma_number_biased_exp (binary); uint64_t fraction = ecma_number_fraction (binary); if (biased_exp < ((1 << (ECMA_NUMBER_BIASED_EXP_WIDTH - 1)) - 1)) { /* Number is less than 1. */ return ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS (0) | ECMA_BIGINT_NUMBER_TO_DIGITS_HAS_FRACTION; } biased_exp -= ((1 << (ECMA_NUMBER_BIASED_EXP_WIDTH - 1)) - 1); fraction |= ((uint64_t) 1) << ECMA_NUMBER_FRACTION_WIDTH; if (biased_exp <= ECMA_NUMBER_FRACTION_WIDTH) { uint32_t has_fraction = 0; if (biased_exp < ECMA_NUMBER_FRACTION_WIDTH && (fraction << (biased_exp + ((8 * sizeof (uint64_t)) - ECMA_NUMBER_FRACTION_WIDTH))) != 0) { has_fraction |= ECMA_BIGINT_NUMBER_TO_DIGITS_HAS_FRACTION; } fraction >>= ECMA_NUMBER_FRACTION_WIDTH - biased_exp; digits_p[0] = (ecma_bigint_digit_t) fraction; #if JERRY_NUMBER_TYPE_FLOAT64 digits_p[1] = (ecma_bigint_digit_t) (fraction >> (8 * sizeof (ecma_bigint_digit_t))); return ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS (digits_p[1] == 0 ? 1 : 2) | has_fraction; #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ return ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS (1) | has_fraction; #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ } digits_p[0] = (ecma_bigint_digit_t) fraction; #if JERRY_NUMBER_TYPE_FLOAT64 digits_p[1] = (ecma_bigint_digit_t) (fraction >> (8 * sizeof (ecma_bigint_digit_t))); #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ biased_exp -= ECMA_NUMBER_FRACTION_WIDTH; uint32_t shift_left = biased_exp & ((8 * sizeof (ecma_bigint_digit_t)) - 1); biased_exp = biased_exp >> ECMA_BIGINT_DIGIT_SHIFT; if (shift_left == 0) { #if JERRY_NUMBER_TYPE_FLOAT64 return biased_exp | ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS (2); #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ return biased_exp | ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS (1); #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ } uint32_t shift_right = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_left; #if JERRY_NUMBER_TYPE_FLOAT64 digits_p[2] = digits_p[1] >> shift_right; digits_p[1] = (digits_p[1] << shift_left) | (digits_p[0] >> shift_right); digits_p[0] <<= shift_left; return biased_exp | ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS (digits_p[2] == 0 ? 2 : 3); #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ digits_p[1] = digits_p[0] >> shift_right; digits_p[0] <<= shift_left; return biased_exp | ECMA_BIGINT_NUMBER_TO_DIGITS_SET_DIGITS (digits_p[1] == 0 ? 1 : 2); #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ } /* ecma_bigint_number_to_digits */ /** * Convert an ecma number to BigInt value * * See also: * ECMA-262 v11, 20.2.1.1.1 * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_bigint_number_to_bigint (ecma_number_t number) /**< ecma number */ { if (ecma_number_is_nan (number) || ecma_number_is_infinity (number)) { return ecma_raise_range_error (ECMA_ERR_INFINITY_OR_NAN_CANNOT_BE_CONVERTED_TO_BIGINT); } ecma_bigint_digit_t digits[3]; uint32_t result = ecma_bigint_number_to_digits (number, digits); JERRY_ASSERT (ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS (result) == 0 || digits[ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS (result) - 1] > 0); if (result & ECMA_BIGINT_NUMBER_TO_DIGITS_HAS_FRACTION) { return ecma_raise_range_error (ECMA_ERR_ONLY_INTEGER_NUMBERS_CAN_BE_CONVERTED_TO_BIGINT); } uint32_t digits_size = ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS_SIZE (result); if (digits_size == 0) { return ECMA_BIGINT_ZERO; } uint32_t zero_size = ECMA_BIGINT_NUMBER_TO_DIGITS_GET_ZERO_SIZE (result); ecma_extended_primitive_t *result_p = ecma_bigint_create (digits_size + zero_size); if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } uint8_t *data_p = (uint8_t *) ECMA_BIGINT_GET_DIGITS (result_p, 0); memset (data_p, 0, zero_size); memcpy (data_p + zero_size, digits, digits_size); if (number < 0) { result_p->u.bigint_sign_and_size |= ECMA_BIGINT_SIGN; } return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_number_to_bigint */ /** * Convert a value to BigInt value * * See also: * ECMA-262 v11, 7.1.13 * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_to_bigint (ecma_value_t value, /**< any value */ bool allow_numbers) /**< converting ecma numbers is allowed */ { bool free_value = false; if (ecma_is_value_object (value)) { value = ecma_op_object_default_value (ecma_get_object_from_value (value), ECMA_PREFERRED_TYPE_NUMBER); free_value = true; if (ECMA_IS_VALUE_ERROR (value)) { return value; } } ecma_value_t result; if (ecma_is_value_string (value)) { result = ecma_bigint_parse_string_value (value, ECMA_BIGINT_PARSE_NO_OPTIONS); } else if (ecma_is_value_bigint (value)) { result = value; if (!free_value && value != ECMA_BIGINT_ZERO) { ecma_ref_extended_primitive (ecma_get_extended_primitive_from_value (value)); } else { free_value = false; } } else if (allow_numbers && ecma_is_value_number (value)) { result = ecma_bigint_number_to_bigint (ecma_get_number_from_value (value)); } else if (ecma_is_value_false (value)) { result = ECMA_BIGINT_ZERO; } else if (ecma_is_value_true (value)) { result = ecma_bigint_create_from_digit (1, false); } else { result = ecma_raise_type_error (ECMA_ERR_VALUE_CANNOT_BE_CONVERTED_TO_BIGINT); } if (free_value) { ecma_free_value (value); } return result; } /* ecma_bigint_to_bigint */ /** * Convert a BigInt value to number value * * @return ecma number value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_to_number (ecma_value_t value) /**< BigInt value */ { JERRY_ASSERT (ecma_is_value_bigint (value)); if (value == ECMA_BIGINT_ZERO) { return ecma_make_integer_value (0); } ecma_extended_primitive_t *value_p = ecma_get_extended_primitive_from_value (value); uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); ecma_bigint_digit_t *digits_p = ECMA_BIGINT_GET_DIGITS (value_p, size); if (size == sizeof (ecma_bigint_digit_t)) { if (!(value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { if (digits_p[-1] <= ECMA_INTEGER_NUMBER_MAX) { return ecma_make_integer_value ((ecma_integer_value_t) digits_p[-1]); } } else if (digits_p[-1] <= -ECMA_INTEGER_NUMBER_MIN) { return ecma_make_integer_value (-(ecma_integer_value_t) digits_p[-1]); } } uint64_t fraction = 0; ecma_bigint_digit_t shift_left; if (digits_p[-1] == 1) { JERRY_ASSERT (size > sizeof (ecma_bigint_digit_t)); fraction = ((uint64_t) digits_p[-2]) << (8 * sizeof (ecma_bigint_digit_t)); shift_left = (uint32_t) (8 * sizeof (ecma_bigint_digit_t)); if (size >= 3 * sizeof (ecma_bigint_digit_t)) { fraction |= (uint64_t) digits_p[-3]; } } else { shift_left = ecma_big_uint_count_leading_zero (digits_p[-1]) + 1; fraction = ((uint64_t) digits_p[-1]) << (8 * sizeof (ecma_bigint_digit_t) + shift_left); if (size >= 2 * sizeof (ecma_bigint_digit_t)) { fraction |= ((uint64_t) digits_p[-2]) << shift_left; } if (size >= 3 * sizeof (ecma_bigint_digit_t)) { fraction |= ((uint64_t) digits_p[-3]) >> (8 * sizeof (ecma_bigint_digit_t) - shift_left); } } uint32_t biased_exp = (uint32_t) (((1 << (ECMA_NUMBER_BIASED_EXP_WIDTH - 1)) - 1) + (size * 8 - shift_left)); /* Rounding result. */ const uint64_t rounding_bit = (((uint64_t) 1) << (8 * sizeof (uint64_t) - ECMA_NUMBER_FRACTION_WIDTH - 1)); bool round_up = false; if (fraction & rounding_bit) { round_up = true; /* IEEE_754 roundTiesToEven mode: when rounding_bit is set, and all the remaining bits * are zero, the number needs to be rounded down the bit before rounding_bit is zero. */ if ((fraction & ((rounding_bit << 2) - 1)) == rounding_bit) { round_up = false; if ((size >= (3 * sizeof (ecma_bigint_digit_t))) && (shift_left == (8 * sizeof (ecma_bigint_digit_t)) || (digits_p[-3] & ((((ecma_bigint_digit_t) 1) << shift_left) - 1)) == 0)) { ecma_bigint_digit_t *digits_start_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); digits_p -= 3; while (digits_p > digits_start_p) { if (digits_p[-1] != 0) { round_up = true; break; } digits_p--; } } } } if (round_up) { fraction += rounding_bit; fraction >>= (8 * sizeof (uint64_t) - ECMA_NUMBER_FRACTION_WIDTH); if (fraction == 0) { biased_exp++; } } else { fraction >>= (8 * sizeof (uint64_t) - ECMA_NUMBER_FRACTION_WIDTH); } bool sign = (value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN); ecma_number_t result; if (biased_exp < (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1) { result = ecma_number_create (sign, biased_exp, fraction); } else { result = ecma_number_make_infinity (sign); } return ecma_make_number_value (result); } /* ecma_bigint_to_number */ /** * Returns with a BigInt if the value is BigInt, * or the value is object, and its default value is BigInt * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_get_bigint (ecma_value_t value, /**< any value */ bool *free_result_p) /**< [out] result should be freed */ { *free_result_p = false; if (ecma_is_value_bigint (value)) { return value; } if (ecma_is_value_object (value)) { ecma_object_t *object_p = ecma_get_object_from_value (value); ecma_value_t default_value = ecma_op_object_default_value (object_p, ECMA_PREFERRED_TYPE_NUMBER); if (ECMA_IS_VALUE_ERROR (default_value)) { return default_value; } if (ecma_is_value_bigint (default_value)) { *free_result_p = (default_value != ECMA_BIGINT_ZERO); return default_value; } ecma_free_value (default_value); } return ecma_raise_type_error (ECMA_ERR_CONVERT_BIGINT_TO_NUMBER); } /* ecma_bigint_get_bigint */ /** * Create BigInt value from uint64 digits * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_create_from_digits (const uint64_t *digits_p, /**< BigInt digits */ uint32_t size, /**< number of BigInt digits */ bool sign) /**< sign bit, true if the result should be negative */ { const uint64_t *digits_end_p = digits_p + size; while (digits_end_p > digits_p && digits_end_p[-1] == 0) { digits_end_p--; } if (digits_p == digits_end_p) { return ECMA_BIGINT_ZERO; } size = (uint32_t) (digits_end_p - digits_p); if (size < ECMA_BIGINT_MAX_SIZE) { size *= (uint32_t) sizeof (uint64_t); } if ((digits_end_p[-1] >> (8 * sizeof (ecma_bigint_digit_t))) == 0) { size -= (uint32_t) sizeof (ecma_bigint_digit_t); } ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size); if (JERRY_UNLIKELY (result_value_p == NULL)) { return ecma_bigint_raise_memory_error (); } if (sign) { result_value_p->u.bigint_sign_and_size |= ECMA_BIGINT_SIGN; } ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); while (digits_p < digits_end_p) { uint64_t digit = *digits_p++; result_p[0] = (ecma_bigint_digit_t) digit; result_p[1] = (ecma_bigint_digit_t) (digit >> (8 * sizeof (ecma_bigint_digit_t))); result_p += 2; } return ecma_make_extended_primitive_value (result_value_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_create_from_digits */ /** * Get the number of uint64 digits of a BigInt value * * @return number of uint64 digits */ uint32_t ecma_bigint_get_size_in_digits (ecma_value_t value) /**< BigInt value */ { JERRY_ASSERT (ecma_is_value_bigint (value)); if (value == ECMA_BIGINT_ZERO) { return 0; } ecma_extended_primitive_t *value_p = ecma_get_extended_primitive_from_value (value); uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); return (size + (uint32_t) sizeof (ecma_bigint_digit_t)) / sizeof (uint64_t); } /* ecma_bigint_get_size_in_digits */ /** * Get the uint64 digits of a BigInt value */ void ecma_bigint_get_digits_and_sign (ecma_value_t value, /**< BigInt value */ uint64_t *digits_p, /**< [out] buffer for digits */ uint32_t size, /**< buffer size in digits */ bool *sign_p) /**< [out] sign of BigInt */ { JERRY_ASSERT (ecma_is_value_bigint (value)); if (value == ECMA_BIGINT_ZERO) { if (sign_p != NULL) { *sign_p = false; } memset (digits_p, 0, size * sizeof (uint64_t)); return; } ecma_extended_primitive_t *value_p = ecma_get_extended_primitive_from_value (value); if (sign_p != NULL) { *sign_p = (value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) != 0; } uint32_t bigint_size = ECMA_BIGINT_GET_SIZE (value_p); uint32_t copy_size = bigint_size / sizeof (uint64_t); if (copy_size > size) { copy_size = size; } const uint64_t *digits_end_p = digits_p + copy_size; ecma_bigint_digit_t *source_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); while (digits_p < digits_end_p) { *digits_p++ = source_p[0] | (((uint64_t) source_p[1]) << (8 * sizeof (ecma_bigint_digit_t))); source_p += 2; } size -= copy_size; if (size == 0) { return; } if (ECMA_BIGINT_SIZE_IS_ODD (bigint_size)) { *digits_p++ = source_p[0]; size--; } if (size > 0) { memset (digits_p, 0, size * sizeof (uint64_t)); } } /* ecma_bigint_get_digits_and_sign */ /** * Compare two BigInt values * * @return true if they are the same, false otherwise */ bool ecma_bigint_is_equal_to_bigint (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value) /**< right BigInt value */ { JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); if (left_value == ECMA_BIGINT_ZERO) { return right_value == ECMA_BIGINT_ZERO; } else if (right_value == ECMA_BIGINT_ZERO) { return false; } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); if (left_p->u.bigint_sign_and_size != right_p->u.bigint_sign_and_size) { return false; } uint32_t size = ECMA_BIGINT_GET_SIZE (left_p); return memcmp (ECMA_BIGINT_GET_DIGITS (left_p, 0), ECMA_BIGINT_GET_DIGITS (right_p, 0), size) == 0; } /* ecma_bigint_is_equal_to_bigint */ /** * Compare a BigInt value and a number * * @return true if they are the same, false otherwise */ bool ecma_bigint_is_equal_to_number (ecma_value_t left_value, /**< left BigInt value */ ecma_number_t right_value) /**< right number value */ { JERRY_ASSERT (ecma_is_value_bigint (left_value)); if (ecma_number_is_nan (right_value) || ecma_number_is_infinity (right_value)) { return false; } if (left_value == ECMA_BIGINT_ZERO) { return right_value == 0; } ecma_extended_primitive_t *left_value_p = ecma_get_extended_primitive_from_value (left_value); /* Sign must be the same. */ if (left_value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) { if (right_value > 0) { return false; } } else if (right_value < 0) { return false; } ecma_bigint_digit_t digits[3]; uint32_t result = ecma_bigint_number_to_digits (right_value, digits); JERRY_ASSERT (ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS (result) == 0 || digits[ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS (result) - 1] > 0); if (result & ECMA_BIGINT_NUMBER_TO_DIGITS_HAS_FRACTION) { return false; } uint32_t digits_size = ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS_SIZE (result); uint32_t zero_size = ECMA_BIGINT_NUMBER_TO_DIGITS_GET_ZERO_SIZE (result); if (ECMA_BIGINT_GET_SIZE (left_value_p) != digits_size + zero_size) { return false; } ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t *left_end_p = (ecma_bigint_digit_t *) (((uint8_t *) left_p) + zero_size); /* Check value bits first. */ if (memcmp (left_end_p, digits, digits_size) != 0) { return false; } while (left_p < left_end_p) { if (*left_p++ != 0) { return false; } } return true; } /* ecma_bigint_is_equal_to_number */ /** * Convert 0 to 1, and 1 to -1. Useful for getting sign. */ #define ECMA_BIGINT_TO_SIGN(value) (1 - (((int) (value)) << 1)) /** * Convert 0 to -1, and 1 to 1. Useful for getting negated sign. */ #define ECMA_BIGINT_TO_NEGATED_SIGN(value) (-1 + (((int) (value)) << 1)) /** * Compare two BigInt values * * return -1, if left value < right value, 0 if they are equal, 1 otherwise */ int ecma_bigint_compare_to_bigint (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value) /**< right BigInt value */ { JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); if (left_value == ECMA_BIGINT_ZERO) { if (right_value == ECMA_BIGINT_ZERO) { return 0; } ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); return ECMA_BIGINT_TO_NEGATED_SIGN (right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN); } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); uint32_t left_sign = left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; if (right_value == ECMA_BIGINT_ZERO) { return ECMA_BIGINT_TO_SIGN (left_sign); } ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); uint32_t right_sign = right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; if ((left_sign ^ right_sign) != 0) { return ECMA_BIGINT_TO_SIGN (left_sign); } if (left_sign == 0) { return ecma_big_uint_compare (left_p, right_p); } return -ecma_big_uint_compare (left_p, right_p); } /* ecma_bigint_compare_to_bigint */ /** * Compare a BigInt value and a number * * return -1, if left value < right value, 0 if they are equal, 1 otherwise */ int ecma_bigint_compare_to_number (ecma_value_t left_value, /**< left BigInt value */ ecma_number_t right_value) /**< right number value */ { JERRY_ASSERT (ecma_is_value_bigint (left_value)); JERRY_ASSERT (!ecma_number_is_nan (right_value)); int right_invert_sign = ECMA_BIGINT_TO_SIGN (right_value > 0); if (left_value == ECMA_BIGINT_ZERO) { if (right_value == 0) { return 0; } return right_invert_sign; } ecma_extended_primitive_t *left_value_p = ecma_get_extended_primitive_from_value (left_value); int left_sign = ECMA_BIGINT_TO_SIGN (left_value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN); if (right_value == 0 || left_sign == right_invert_sign) { /* Second condition: a positive BigInt is always greater than any negative number, and the opposite is true. */ return left_sign; } if (ecma_number_is_infinity (right_value)) { /* Infinity is always bigger than any BigInt number. */ return right_invert_sign; } ecma_bigint_digit_t digits[3]; uint32_t result = ecma_bigint_number_to_digits (right_value, digits); JERRY_ASSERT (ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS (result) == 0 || digits[ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS (result) - 1] > 0); uint32_t digits_size = ECMA_BIGINT_NUMBER_TO_DIGITS_GET_DIGITS_SIZE (result); if (digits_size == 0) { JERRY_ASSERT (result & ECMA_BIGINT_NUMBER_TO_DIGITS_HAS_FRACTION); /* The number is between [-1 .. 1] exclusive. */ return left_sign; } uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); uint32_t right_size = digits_size + ECMA_BIGINT_NUMBER_TO_DIGITS_GET_ZERO_SIZE (result); if (left_size != right_size) { return left_size > right_size ? left_sign : -left_sign; } ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, right_size); ecma_bigint_digit_t *left_end_p = (ecma_bigint_digit_t *) (((uint8_t *) left_p) - digits_size); ecma_bigint_digit_t *digits_p = (ecma_bigint_digit_t *) (((uint8_t *) digits) + digits_size); do { ecma_bigint_digit_t left = *(--left_p); ecma_bigint_digit_t right = *(--digits_p); if (left != right) { return left > right ? left_sign : -left_sign; } } while (left_p > left_end_p); left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); while (left_p > left_end_p) { if (*(--left_p) != 0) { return left_sign; } } return (result & ECMA_BIGINT_NUMBER_TO_DIGITS_HAS_FRACTION) ? -left_sign : 0; } /* ecma_bigint_compare_to_number */ #undef ECMA_BIGINT_TO_SIGN /** * Negate a non-zero BigInt value * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_negate (ecma_extended_primitive_t *value_p) /**< BigInt value */ { uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); ecma_extended_primitive_t *result_p = ecma_bigint_create (size); if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } memcpy (result_p + 1, value_p + 1, size); result_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT; result_p->u.bigint_sign_and_size = value_p->u.bigint_sign_and_size ^ ECMA_BIGINT_SIGN; return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_negate */ /** * Invert all bits of a BigInt value * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_unary (ecma_value_t value, /**< BigInt value */ ecma_bigint_unary_operation_type type) /**< type of unary operation */ { JERRY_ASSERT (ecma_is_value_bigint (value)); if (value == ECMA_BIGINT_ZERO) { return ecma_bigint_create_from_digit (1, type != ECMA_BIGINT_UNARY_INCREASE); } ecma_extended_primitive_t *value_p = ecma_get_extended_primitive_from_value (value); uint32_t sign = (type != ECMA_BIGINT_UNARY_DECREASE) ? ECMA_BIGINT_SIGN : 0; if ((value_p->u.bigint_sign_and_size == (uint32_t) (sizeof (ecma_bigint_digit_t) | sign)) && *ECMA_BIGINT_GET_DIGITS (value_p, 0) == 1) { return ECMA_BIGINT_ZERO; } ecma_extended_primitive_t *result_p; if ((value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) == (sign ^ ECMA_BIGINT_SIGN)) { result_p = ecma_big_uint_increase (value_p); if (type != ECMA_BIGINT_UNARY_INCREASE && result_p != NULL) { result_p->u.bigint_sign_and_size |= ECMA_BIGINT_SIGN; } } else { result_p = ecma_big_uint_decrease (value_p); if (type == ECMA_BIGINT_UNARY_INCREASE && result_p != NULL) { result_p->u.bigint_sign_and_size |= ECMA_BIGINT_SIGN; } } if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_unary */ /** * Add/subtract right BigInt value to/from left BigInt value * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_add_sub (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value, /**< right BigInt value */ bool is_add) /**< true if add operation should be performed */ { JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); if (right_value == ECMA_BIGINT_ZERO) { return ecma_copy_value (left_value); } ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); if (left_value == ECMA_BIGINT_ZERO) { if (!is_add) { return ecma_bigint_negate (right_p); } ecma_ref_extended_primitive (right_p); return right_value; } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); uint32_t sign = is_add ? 0 : ECMA_BIGINT_SIGN; if (((left_p->u.bigint_sign_and_size ^ right_p->u.bigint_sign_and_size) & ECMA_BIGINT_SIGN) == sign) { ecma_extended_primitive_t *result_p = ecma_big_uint_add (left_p, right_p); if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } result_p->u.bigint_sign_and_size |= left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } int compare_result = ecma_big_uint_compare (left_p, right_p); ecma_extended_primitive_t *result_p; if (compare_result == 0) { return ECMA_BIGINT_ZERO; } if (compare_result > 0) { sign = left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; result_p = ecma_big_uint_sub (left_p, right_p); } else { sign = right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; if (!is_add) { sign ^= ECMA_BIGINT_SIGN; } result_p = ecma_big_uint_sub (right_p, left_p); } if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } result_p->u.bigint_sign_and_size |= sign; return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_add_sub */ /** * Multiply two BigInt values * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_mul (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value) /**< right BigInt value */ { JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); if (left_value == ECMA_BIGINT_ZERO || right_value == ECMA_BIGINT_ZERO) { return ECMA_BIGINT_ZERO; } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_p); uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_p); if (left_size == sizeof (ecma_bigint_digit_t) && ECMA_BIGINT_GET_LAST_DIGIT (left_p, sizeof (ecma_bigint_digit_t)) == 1) { if (left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) { return ecma_bigint_negate (right_p); } ecma_ref_extended_primitive (right_p); return right_value; } if (right_size == sizeof (ecma_bigint_digit_t) && ECMA_BIGINT_GET_LAST_DIGIT (right_p, sizeof (ecma_bigint_digit_t)) == 1) { if (right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) { return ecma_bigint_negate (left_p); } ecma_ref_extended_primitive (left_p); return left_value; } ecma_extended_primitive_t *result_p = ecma_big_uint_mul (left_p, right_p); if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } uint32_t sign = (left_p->u.bigint_sign_and_size ^ right_p->u.bigint_sign_and_size) & ECMA_BIGINT_SIGN; result_p->u.bigint_sign_and_size |= sign; return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_mul */ /** * Divide two BigInt values * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_div_mod (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value, /**< right BigInt value */ bool is_mod) /**< true if return with remainder */ { JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); if (right_value == ECMA_BIGINT_ZERO) { return ecma_raise_range_error (ECMA_ERR_BIGINT_ZERO_DIVISION); } if (left_value == ECMA_BIGINT_ZERO) { return left_value; } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); int compare_result = ecma_big_uint_compare (left_p, right_p); ecma_extended_primitive_t *result_p; if (compare_result < 0) { if (!is_mod) { return ECMA_BIGINT_ZERO; } ecma_ref_extended_primitive (left_p); return left_value; } else if (compare_result == 0) { if (is_mod) { return ECMA_BIGINT_ZERO; } result_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); if (result_p != NULL) { *ECMA_BIGINT_GET_DIGITS (result_p, 0) = 1; } } else { result_p = ecma_big_uint_div_mod (left_p, right_p, is_mod); if (result_p == ECMA_BIGINT_POINTER_TO_ZERO) { return ECMA_BIGINT_ZERO; } } if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } if (is_mod) { result_p->u.bigint_sign_and_size |= left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; } else { uint32_t sign = (left_p->u.bigint_sign_and_size ^ right_p->u.bigint_sign_and_size) & ECMA_BIGINT_SIGN; result_p->u.bigint_sign_and_size |= sign; } return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_div_mod */ /** * Shift left BigInt value to left or right * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_shift (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value, /**< right BigInt value */ bool is_left) /**< true if left shift operation should be performed */ { JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); if (left_value == ECMA_BIGINT_ZERO) { return ECMA_BIGINT_ZERO; } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); if (right_value == ECMA_BIGINT_ZERO) { ecma_ref_extended_primitive (left_p); return left_value; } ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); if (right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) { is_left = !is_left; } if (ECMA_BIGINT_GET_SIZE (right_p) > sizeof (ecma_bigint_digit_t)) { if (is_left) { return ecma_bigint_raise_memory_error (); } else if (left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) { /* Shifting a negative value with a very big number to the right should be -1. */ return ecma_bigint_create_from_digit (1, true); } return ECMA_BIGINT_ZERO; } ecma_extended_primitive_t *result_p; ecma_bigint_digit_t shift = ECMA_BIGINT_GET_LAST_DIGIT (right_p, sizeof (ecma_bigint_digit_t)); uint32_t left_sign = left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; if (is_left) { result_p = ecma_big_uint_shift_left (left_p, shift); } else { /* -x >> y == ~(x - 1) >> y == ~((x - 1) >> y) == -(((x - 1) >> y) + 1) * When a non-zero bit is shifted out: (x - 1) >> y == x >> y, so the formula is -((x >> y) + 1) * When only zero bits are shifted out: (((x - 1) >> y) + 1) == x >> y so the formula is: -(x >> y) */ result_p = ecma_big_uint_shift_right (left_p, shift, left_sign != 0); if (result_p == ECMA_BIGINT_POINTER_TO_ZERO) { return ECMA_BIGINT_ZERO; } } if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } result_p->u.bigint_sign_and_size |= left_sign; return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_shift */ /** * Compute the left value raised to the power of right value * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_pow (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value) /**< right BigInt value */ { JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); if (right_value == ECMA_BIGINT_ZERO) { return ecma_bigint_create_from_digit (1, false); } ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); if (right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) { return ecma_raise_range_error (ECMA_ERR_NEGATIVE_EXPONENT_IS_NOT_ALLOWED_FOR_BIGINTS); } if (left_value == ECMA_BIGINT_ZERO) { return ECMA_BIGINT_ZERO; } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); ecma_bigint_digit_t base = 0; if (ECMA_BIGINT_GET_SIZE (left_p) == sizeof (ecma_bigint_digit_t)) { base = *ECMA_BIGINT_GET_DIGITS (left_p, 0); JERRY_ASSERT (base != 0); if (base == 1) { if (!(left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) || ECMA_BIGINT_NUMBER_IS_ODD (*ECMA_BIGINT_GET_DIGITS (right_p, 0))) { ecma_ref_extended_primitive (left_p); return left_value; } return ecma_bigint_create_from_digit (1, false); } } if (JERRY_UNLIKELY (ECMA_BIGINT_GET_SIZE (right_p) > sizeof (ecma_bigint_digit_t))) { return ecma_bigint_raise_memory_error (); } ecma_bigint_digit_t power = *ECMA_BIGINT_GET_DIGITS (right_p, 0); if (power == 1) { ecma_ref_extended_primitive (left_p); return left_value; } ecma_extended_primitive_t *result_p; if (base == 2) { result_p = ecma_big_uint_shift_left (left_p, power - 1); } else { result_p = ecma_big_uint_pow (left_p, power); } if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } if ((left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) && ECMA_BIGINT_NUMBER_IS_ODD (power)) { result_p->u.bigint_sign_and_size |= ECMA_BIGINT_SIGN; } return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_pow */ /** * Convert the result to an ecma value * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_bigint_bitwise_op (uint32_t operation_and_options, /**< bitwise operation type and options */ ecma_extended_primitive_t *left_value_p, /**< left BigInt value */ ecma_extended_primitive_t *right_value_p) /**< right BigInt value */ { ecma_extended_primitive_t *result_p; result_p = ecma_big_uint_bitwise_op (operation_and_options, left_value_p, right_value_p); if (JERRY_UNLIKELY (result_p == NULL)) { return ecma_bigint_raise_memory_error (); } if (result_p == ECMA_BIGINT_POINTER_TO_ZERO) { return ECMA_BIGINT_ZERO; } if (operation_and_options & ECMA_BIG_UINT_BITWISE_INCREASE_RESULT) { result_p->u.bigint_sign_and_size |= ECMA_BIGINT_SIGN; } return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } /* ecma_bigint_bitwise_op */ /** * Perform bitwise 'and' operations on two BigUInt numbers * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_and (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value) /**< right BigInt value */ { if (left_value == ECMA_BIGINT_ZERO || right_value == ECMA_BIGINT_ZERO) { return ECMA_BIGINT_ZERO; } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); if (!(left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { if (!(right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { return ecma_bigint_bitwise_op (ECMA_BIG_UINT_BITWISE_AND, left_p, right_p); } /* x & (-y) == x & ~(y-1) == x &~ (y-1) */ uint32_t operation_and_options = ECMA_BIG_UINT_BITWISE_AND_NOT | ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT; return ecma_bigint_bitwise_op (operation_and_options, left_p, right_p); } if (!(right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { /* (-x) & y == ~(x-1) & y == y &~ (x-1) */ uint32_t operation_and_options = ECMA_BIG_UINT_BITWISE_AND_NOT | ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT; return ecma_bigint_bitwise_op (operation_and_options, right_p, left_p); } /* (-x) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) */ uint32_t operation_and_options = (ECMA_BIG_UINT_BITWISE_OR | ECMA_BIG_UINT_BITWISE_DECREASE_BOTH | ECMA_BIG_UINT_BITWISE_INCREASE_RESULT); return ecma_bigint_bitwise_op (operation_and_options, left_p, right_p); } /* ecma_bigint_and */ /** * Perform bitwise 'or' operations on two BigUInt numbers * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_or (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value) /**< right BigInt value */ { if (left_value == ECMA_BIGINT_ZERO) { return ecma_copy_value (right_value); } if (right_value == ECMA_BIGINT_ZERO) { return ecma_copy_value (left_value); } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); if (!(left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { if (!(right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { return ecma_bigint_bitwise_op (ECMA_BIG_UINT_BITWISE_OR, left_p, right_p); } /* x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1) */ uint32_t operation_and_options = (ECMA_BIG_UINT_BITWISE_AND_NOT | ECMA_BIG_UINT_BITWISE_DECREASE_LEFT | ECMA_BIG_UINT_BITWISE_INCREASE_RESULT); return ecma_bigint_bitwise_op (operation_and_options, right_p, left_p); } if (!(right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { /* (-x) | y == ~(x-1) | y == ~((x-1) &~ y) == -(((x-1) &~ y) + 1) */ uint32_t operation_and_options = (ECMA_BIG_UINT_BITWISE_AND_NOT | ECMA_BIG_UINT_BITWISE_DECREASE_LEFT | ECMA_BIG_UINT_BITWISE_INCREASE_RESULT); return ecma_bigint_bitwise_op (operation_and_options, left_p, right_p); } /* (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1)) = -(((x-1) & (y-1)) + 1) */ uint32_t operation_and_options = (ECMA_BIG_UINT_BITWISE_AND | ECMA_BIG_UINT_BITWISE_DECREASE_BOTH | ECMA_BIG_UINT_BITWISE_INCREASE_RESULT); return ecma_bigint_bitwise_op (operation_and_options, left_p, right_p); } /* ecma_bigint_or */ /** * Perform bitwise 'xor' operations on two BigUInt numbers * * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_bigint_xor (ecma_value_t left_value, /**< left BigInt value */ ecma_value_t right_value) /**< right BigInt value */ { if (left_value == ECMA_BIGINT_ZERO) { return ecma_copy_value (right_value); } if (right_value == ECMA_BIGINT_ZERO) { return ecma_copy_value (left_value); } ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); if (!(left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { if (!(right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { return ecma_bigint_bitwise_op (ECMA_BIG_UINT_BITWISE_XOR, left_p, right_p); } /* x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1) */ uint32_t operation_and_options = (ECMA_BIG_UINT_BITWISE_XOR | ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT | ECMA_BIG_UINT_BITWISE_INCREASE_RESULT); return ecma_bigint_bitwise_op (operation_and_options, left_p, right_p); } if (!(right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)) { /* (-x) | y == ~(x-1) ^ y == ~((x-1) ^ y) == -(((x-1) ^ y) + 1) */ uint32_t operation_and_options = (ECMA_BIG_UINT_BITWISE_XOR | ECMA_BIG_UINT_BITWISE_DECREASE_LEFT | ECMA_BIG_UINT_BITWISE_INCREASE_RESULT); return ecma_bigint_bitwise_op (operation_and_options, left_p, right_p); } /* (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1) */ uint32_t operation_and_options = ECMA_BIG_UINT_BITWISE_XOR | ECMA_BIG_UINT_BITWISE_DECREASE_BOTH; return ecma_bigint_bitwise_op (operation_and_options, left_p, right_p); } /* ecma_bigint_xor */ #endif /* JERRY_BUILTIN_BIGINT */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-typedarray-object.cpp000664 001750 001750 00000200476 15164251010 047457 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-typedarray-object.h" #include #include "ecma-arraybuffer-object.h" #include "ecma-big-uint.h" #include "ecma-bigint.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "ecma-shared-arraybuffer-object.h" #include "jcontext.h" #if JERRY_BUILTIN_TYPEDARRAY /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmatypedarrayobject ECMA TypedArray object related routines * @{ */ /** * Read and copy a number from a given buffer to a value. **/ #define ECMA_TYPEDARRAY_GET_ELEMENT(src_p, num, type) \ do \ { \ if (JERRY_LIKELY ((((uintptr_t) (src_p)) & (sizeof (type) - 1)) == 0)) \ { \ num = *(type *) ((void *) src_p); \ } \ else \ { \ memcpy (&num, (void *) src_p, sizeof (type)); \ } \ } while (0) /** * Copy a number from a value to the given buffer **/ #define ECMA_TYPEDARRAY_SET_ELEMENT(src_p, num, type) \ do \ { \ if (JERRY_LIKELY ((((uintptr_t) (src_p)) & (sizeof (type) - 1)) == 0)) \ { \ *(type *) ((void *) src_p) = num; \ } \ else \ { \ memcpy ((void *) src_p, &num, sizeof (type)); \ } \ } while (0) /** * Read an int8_t value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_int8_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { int8_t num = (int8_t) *src; return ecma_make_integer_value (num); } /* ecma_typedarray_get_int8_element */ /** * Read an uint8_t value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_uint8_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { uint8_t num = (uint8_t) *src; return ecma_make_integer_value (num); } /* ecma_typedarray_get_uint8_element */ /** * Read an int16_t value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_int16_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { int16_t num; ECMA_TYPEDARRAY_GET_ELEMENT (src, num, int16_t); return ecma_make_integer_value (num); } /* ecma_typedarray_get_int16_element */ /** * Read an uint16_t value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_uint16_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { uint16_t num; ECMA_TYPEDARRAY_GET_ELEMENT (src, num, uint16_t); return ecma_make_integer_value (num); } /* ecma_typedarray_get_uint16_element */ /** * Read an int32_t value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_int32_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { int32_t num; ECMA_TYPEDARRAY_GET_ELEMENT (src, num, int32_t); return ecma_make_number_value (num); } /* ecma_typedarray_get_int32_element */ /** * Read an uint32_t value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_uint32_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { uint32_t num; ECMA_TYPEDARRAY_GET_ELEMENT (src, num, uint32_t); return ecma_make_number_value (num); } /* ecma_typedarray_get_uint32_element */ /** * Read a float value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_float_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { float num; ECMA_TYPEDARRAY_GET_ELEMENT (src, num, float); return ecma_make_number_value ((ecma_value_t)num); } /* ecma_typedarray_get_float_element */ /** * Read a double value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_double_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { double num; ECMA_TYPEDARRAY_GET_ELEMENT (src, num, double); return ecma_make_number_value (num); } /* ecma_typedarray_get_double_element */ #if JERRY_BUILTIN_BIGINT /** * Read a bigint64 value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_bigint64_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { uint64_t num; ECMA_TYPEDARRAY_GET_ELEMENT (src, num, uint64_t); bool sign = (num >> 63) != 0; if (sign) { num = (uint64_t) (-(int64_t) num); } return ecma_bigint_create_from_digits (&num, 1, sign); } /* ecma_typedarray_get_bigint64_element */ /** * Read a biguint64 value from the given arraybuffer */ static ecma_value_t ecma_typedarray_get_biguint64_element (lit_utf8_byte_t *src) /**< the location in the internal arraybuffer */ { uint64_t num; ECMA_TYPEDARRAY_GET_ELEMENT (src, num, uint64_t); return ecma_bigint_create_from_digits (&num, 1, false); } /* ecma_typedarray_get_biguint64_element */ #endif /* JERRY_BUILTIN_BIGINT */ /** * Normalize the given ecma_number_t to an uint32_t value */ static uint32_t ecma_typedarray_setter_number_to_uint32 (ecma_number_t value) /**< the number value to normalize */ { uint32_t uint32_value = 0; if (!ecma_number_is_nan (value) && !ecma_number_is_infinity (value)) { bool is_negative = false; if (value < 0) { is_negative = true; value = -value; } if (value > ((ecma_number_t) 0xffffffff)) { value = (ecma_number_t) (fmod (value, (ecma_number_t) 0x100000000)); } uint32_value = (uint32_t) value; if (is_negative) { uint32_value = (uint32_t) (-(int32_t) uint32_value); } } return uint32_value; } /* ecma_typedarray_setter_number_to_uint32 */ /** * Write an int8_t value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_int8_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } int8_t num = (int8_t) ecma_typedarray_setter_number_to_uint32 (result_num); *dst_p = (lit_utf8_byte_t) num; return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_int8_element */ /** * Write an uint8_t value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_uint8_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } uint8_t num = (uint8_t) ecma_typedarray_setter_number_to_uint32 (result_num); *dst_p = (lit_utf8_byte_t) num; return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_uint8_element */ /** * Write an uint8_t clamped value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_uint8_clamped_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } uint8_t clamped; if (result_num > 255) { clamped = 255; } else if (result_num <= 0) { clamped = 0; } else { clamped = (uint8_t) result_num; if (clamped + 0.5 < result_num || (clamped + 0.5 == result_num && (clamped % 2) == 1)) { clamped++; } } *dst_p = (lit_utf8_byte_t) clamped; return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_uint8_clamped_element */ /** * Write an int16_t value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_int16_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } int16_t num = (int16_t) ecma_typedarray_setter_number_to_uint32 (result_num); ECMA_TYPEDARRAY_SET_ELEMENT (dst_p, num, int16_t); return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_int16_element */ /** * Write an uint8_t value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_uint16_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } uint16_t num = (uint16_t) ecma_typedarray_setter_number_to_uint32 (result_num); ECMA_TYPEDARRAY_SET_ELEMENT (dst_p, num, uint16_t); return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_uint16_element */ /** * Write an int32_t value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_int32_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } int32_t num = (int32_t) ecma_typedarray_setter_number_to_uint32 (result_num); ECMA_TYPEDARRAY_SET_ELEMENT (dst_p, num, int32_t); return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_int32_element */ /** * Write an uint32_t value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_uint32_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } uint32_t num = (uint32_t) ecma_typedarray_setter_number_to_uint32 (result_num); ECMA_TYPEDARRAY_SET_ELEMENT (dst_p, num, uint32_t); return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_uint32_element */ /** * Write a float value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_float_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } float num = (float) result_num; ECMA_TYPEDARRAY_SET_ELEMENT (dst_p, num, float); return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_float_element */ #if JERRY_NUMBER_TYPE_FLOAT64 /** * Write a double value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToNumber operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_double_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the number value to set */ { ecma_number_t result_num; ecma_value_t to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } double num = (double) result_num; ECMA_TYPEDARRAY_SET_ELEMENT (dst_p, num, double); return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_double_element */ #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT /** * Write a bigint64/biguint64 value into the given arraybuffer * * @return ECMA_VALUE_ERROR - if the ToBigInt operation fails * ECMA_VALUE_TRUE - otherwise */ static ecma_value_t ecma_typedarray_set_bigint_element (lit_utf8_byte_t *dst_p, /**< the location in the internal arraybuffer */ ecma_value_t value) /**< the bigint value to set */ { ecma_value_t bigint = ecma_bigint_to_bigint (value, false); if (ECMA_IS_VALUE_ERROR (bigint)) { return bigint; } uint64_t num; bool sign; ecma_bigint_get_digits_and_sign (bigint, &num, 1, &sign); if (sign) { num = (uint64_t) (-(int64_t) num); } ECMA_TYPEDARRAY_SET_ELEMENT (dst_p, num, uint64_t); ecma_free_value (bigint); return ECMA_VALUE_TRUE; } /* ecma_typedarray_set_bigint_element */ #endif /* JERRY_BUILTIN_BIGINT */ /** * Builtin id of the first %TypedArray% builtin routine intrinsic object */ #define ECMA_FIRST_TYPEDARRAY_BUILTIN_ROUTINE_ID ECMA_BUILTIN_ID_INT8ARRAY #if JERRY_BUILTIN_BIGINT /** * Builtin id of the last %TypedArray% builtin routine intrinsic object */ #define ECMA_LAST_TYPEDARRAY_BUILTIN_ROUTINE_ID ECMA_BUILTIN_ID_BIGUINT64ARRAY #elif !JERRY_BUILTIN_BIGINT && JERRY_NUMBER_TYPE_FLOAT64 /** * Builtin id of the last %TypedArray% builtin routine intrinsic object */ #define ECMA_LAST_TYPEDARRAY_BUILTIN_ROUTINE_ID ECMA_BUILTIN_ID_FLOAT64ARRAY #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Builtin id of the last %TypedArray% builtin routine intrinsic object */ #define ECMA_LAST_TYPEDARRAY_BUILTIN_ROUTINE_ID ECMA_BUILTIN_ID_FLOAT32ARRAY #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Builtin id of the first %TypedArray% builtin prototype intrinsic object */ #define ECMA_FIRST_TYPEDARRAY_BUILTIN_PROTOTYPE_ID ECMA_BUILTIN_ID_INT8ARRAY_PROTOTYPE /** * List of typedarray getters based on their builtin id */ static const ecma_typedarray_getter_fn_t ecma_typedarray_getters[] = { ecma_typedarray_get_int8_element, /**< Int8Array */ ecma_typedarray_get_uint8_element, /**< Uint8Array */ ecma_typedarray_get_uint8_element, /**< Uint8ClampedArray */ ecma_typedarray_get_int16_element, /**< Int16Array */ ecma_typedarray_get_uint16_element, /**< Int32Array */ ecma_typedarray_get_int32_element, /**< Uint32Array */ ecma_typedarray_get_uint32_element, /**< Uint32Array */ ecma_typedarray_get_float_element, /**< Float32Array */ #if JERRY_NUMBER_TYPE_FLOAT64 ecma_typedarray_get_double_element, /**< Float64Array */ #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT ecma_typedarray_get_bigint64_element, /**< BigInt64Array*/ ecma_typedarray_get_biguint64_element, /**< BigUint64Array */ #endif /* JERRY_BUILTIN_BIGINT */ }; /** * List of typedarray setters based on their builtin id */ static const ecma_typedarray_setter_fn_t ecma_typedarray_setters[] = { ecma_typedarray_set_int8_element, /**< Int8Array */ ecma_typedarray_set_uint8_element, /**< Uint8Array */ ecma_typedarray_set_uint8_clamped_element, /**< Uint8ClampedArray */ ecma_typedarray_set_int16_element, /**< Int16Array */ ecma_typedarray_set_uint16_element, /**< Int32Array */ ecma_typedarray_set_int32_element, /**< Uint32Array */ ecma_typedarray_set_uint32_element, /**< Uint32Array */ ecma_typedarray_set_float_element, /**< Float32Array */ #if JERRY_NUMBER_TYPE_FLOAT64 ecma_typedarray_set_double_element, /**< Float64Array */ #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT ecma_typedarray_set_bigint_element, /**< BigInt64Array */ ecma_typedarray_set_bigint_element, /**< BigUInt64Array */ #endif /* JERRY_BUILTIN_BIGINT */ }; /** * List of typedarray element shift sizes based on their builtin id */ static const uint8_t ecma_typedarray_element_shift_sizes[] = { 0, /**< Int8Array */ 0, /**< Uint8Array */ 0, /**< Uint8ClampedArray */ 1, /**< Int16Array */ 1, /**< Uint16Array */ 2, /**< Int32Array */ 2, /**< Uint32Array */ 2, /**< Float32Array */ #if JERRY_NUMBER_TYPE_FLOAT64 3, /**< Float64Array */ #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT 3, /**< BigInt64Array */ 3, /**< BigUInt64Array */ #endif /* JERRY_BUILTIN_BIGINT */ }; /** * List of typedarray class magic strings based on their builtin id */ static const uint16_t ecma_typedarray_magic_string_list[] = { (uint16_t) LIT_MAGIC_STRING_INT8_ARRAY_UL, /**< Int8Array */ (uint16_t) LIT_MAGIC_STRING_UINT8_ARRAY_UL, /**< Uint8Array */ (uint16_t) LIT_MAGIC_STRING_UINT8_CLAMPED_ARRAY_UL, /**< Uint8ClampedArray */ (uint16_t) LIT_MAGIC_STRING_INT16_ARRAY_UL, /**< Int16Array */ (uint16_t) LIT_MAGIC_STRING_UINT16_ARRAY_UL, /**< Uint16Array */ (uint16_t) LIT_MAGIC_STRING_INT32_ARRAY_UL, /**< Int32Array */ (uint16_t) LIT_MAGIC_STRING_UINT32_ARRAY_UL, /**< Uint32Array */ (uint16_t) LIT_MAGIC_STRING_FLOAT32_ARRAY_UL, /**< Float32Array */ #if JERRY_NUMBER_TYPE_FLOAT64 (uint16_t) LIT_MAGIC_STRING_FLOAT64_ARRAY_UL, /**< Float64Array */ #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT (uint16_t) LIT_MAGIC_STRING_BIGINT64_ARRAY_UL, /**< BigInt64Array */ (uint16_t) LIT_MAGIC_STRING_BIGUINT64_ARRAY_UL, /**< BigUInt64Array */ #endif /* JERRY_BUILTIN_BIGINT */ }; /** * Get the magic string id of a typedarray * * @return magic string */ lit_magic_string_id_t ecma_get_typedarray_magic_string_id (ecma_typedarray_type_t typedarray_id) { return (lit_magic_string_id_t) ecma_typedarray_magic_string_list[typedarray_id]; } /* ecma_get_typedarray_magic_string_id */ /** * Get typedarray's getter function callback * * @return ecma_typedarray_getter_fn_t: the getter function for the given builtin TypedArray id */ ecma_typedarray_getter_fn_t ecma_get_typedarray_getter_fn (ecma_typedarray_type_t typedarray_id) /**< typedarray id */ { return ecma_typedarray_getters[typedarray_id]; } /* ecma_get_typedarray_getter_fn */ /** * Get element from a TypedArray * * @return the value of the element */ ecma_value_t ecma_get_typedarray_element (ecma_typedarray_info_t *info_p, /**< typedarray info */ uint32_t index) /**< element index */ { if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info_p->array_buffer_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (info_p->array_buffer_p) || index >= info_p->length) { return ECMA_VALUE_UNDEFINED; } uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); return ecma_typedarray_getters[info_p->id](buffer_p + (index << info_p->shift)); } /* ecma_get_typedarray_element */ /** * Get typedarray's setter function callback * * @return ecma_typedarray_setter_fn_t: the setter function for the given builtin TypedArray id */ ecma_typedarray_setter_fn_t ecma_get_typedarray_setter_fn (ecma_typedarray_type_t typedarray_id) /**< typedarray id */ { return ecma_typedarray_setters[typedarray_id]; } /* ecma_get_typedarray_setter_fn */ /** * set typedarray's element value */ ecma_value_t ecma_set_typedarray_element (ecma_typedarray_info_t *info_p, /**< typedarray info */ ecma_value_t value, /**< value to be set */ uint32_t index) /**< element index */ { ecma_value_t to_num; ecma_number_t result_num; to_num = ecma_op_to_numeric (value, &result_num, ECMA_TO_NUMERIC_NO_OPTS); if (ECMA_IS_VALUE_ERROR (to_num)) { return to_num; } if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info_p->array_buffer_p)) { ecma_free_value (to_num); return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (info_p->array_buffer_p) || index >= info_p->length) { ecma_free_value (to_num); return ECMA_VALUE_FALSE; } uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); ecma_free_value (to_num); return ecma_typedarray_setters[info_p->id](buffer_p + (index << info_p->shift), value); } /* ecma_set_typedarray_element */ /** * Get the element shift size of a TypedArray type. * * @return uint8_t */ uint8_t ecma_typedarray_helper_get_shift_size (ecma_typedarray_type_t typedarray_id) { return ecma_typedarray_element_shift_sizes[typedarray_id]; } /* ecma_typedarray_helper_get_shift_size */ /** * Check if the builtin is a TypedArray type. * * @return bool: - true if based on the given id it is a TypedArray * - false if based on the given id it is not a TypedArray */ bool ecma_typedarray_helper_is_typedarray (ecma_builtin_id_t builtin_id) /**< the builtin id of a type **/ { return ((builtin_id >= ECMA_FIRST_TYPEDARRAY_BUILTIN_ROUTINE_ID) && (builtin_id <= ECMA_LAST_TYPEDARRAY_BUILTIN_ROUTINE_ID)); } /* ecma_typedarray_helper_is_typedarray */ /** * Get the prototype ID of a TypedArray type. * * @return ecma_builtin_id_t */ ecma_builtin_id_t ecma_typedarray_helper_get_prototype_id (ecma_typedarray_type_t typedarray_id) /**< the id of the typedarray **/ { return (ecma_builtin_id_t) (ECMA_FIRST_TYPEDARRAY_BUILTIN_PROTOTYPE_ID + typedarray_id); } /* ecma_typedarray_helper_get_prototype_id */ /** * Get the constructor ID of a TypedArray type. * * @return ecma_builtin_id_t */ ecma_builtin_id_t ecma_typedarray_helper_get_constructor_id (ecma_typedarray_type_t typedarray_id) /**< the id of the typedarray **/ { return (ecma_builtin_id_t) (ECMA_FIRST_TYPEDARRAY_BUILTIN_ROUTINE_ID + typedarray_id); } /* ecma_typedarray_helper_get_constructor_id */ /** * Get the built-in TypedArray type of the given object. * * @return ecma_typedarray_type_t */ ecma_typedarray_type_t ecma_get_typedarray_id (ecma_object_t *obj_p) /**< typedarray object **/ { JERRY_ASSERT (ecma_object_is_typedarray (obj_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; return (ecma_typedarray_type_t) ext_object_p->u.cls.u1.typedarray_type; } /* ecma_get_typedarray_id */ /** * Get the built-in TypedArray type of the given object. * * @return ecma_typedarray_type_t */ ecma_typedarray_type_t ecma_typedarray_helper_builtin_to_typedarray_id (ecma_builtin_id_t builtin_id) { JERRY_ASSERT (ecma_typedarray_helper_is_typedarray (builtin_id)); return (ecma_typedarray_type_t) (builtin_id - ECMA_FIRST_TYPEDARRAY_BUILTIN_ROUTINE_ID); } /* ecma_typedarray_helper_builtin_to_typedarray_id */ /** * Create a TypedArray object by given array_length * * See also: ES2015 22.2.1.2.1 * * @return ecma value of the new typedarray object * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_typedarray_create_object_with_length (uint32_t array_length, /**< length of the typedarray */ ecma_object_t *src_buffer_p, /**< source buffer */ ecma_object_t *proto_p, /**< prototype object */ uint8_t element_size_shift, /**< the size shift of the element length */ ecma_typedarray_type_t typedarray_id) /**< id of the typedarray */ { uint32_t byte_length = UINT32_MAX; if (array_length <= (UINT32_MAX >> element_size_shift)) { byte_length = array_length << element_size_shift; } if (byte_length > UINT32_MAX - sizeof (ecma_extended_object_t) - JMEM_ALIGNMENT + 1) { #if JERRY_ERROR_MESSAGES ecma_value_t array_length_value = ecma_make_number_value (array_length); ecma_value_t result = ecma_raise_standard_error_with_format (JERRY_ERROR_RANGE, "Invalid typed array length: %", array_length_value); ecma_free_value (array_length_value); return result; #else /* !JERRY_ERROR_MESSAGES */ return ecma_raise_range_error (ECMA_ERR_EMPTY); #endif /* JERRY_ERROR_MESSAGES */ } ecma_object_t *new_arraybuffer_p = NULL; if (src_buffer_p == NULL) { new_arraybuffer_p = ecma_arraybuffer_new_object (byte_length); } else { ecma_value_t ctor_proto = ecma_op_species_constructor (src_buffer_p, ECMA_BUILTIN_ID_ARRAYBUFFER); if (ECMA_IS_VALUE_ERROR (ctor_proto)) { return ctor_proto; } ecma_object_t *ctor_proto_p = ecma_get_object_from_value (ctor_proto); ecma_object_t *prototype_p = ecma_op_get_prototype_from_constructor (ctor_proto_p, ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE); ecma_deref_object (ctor_proto_p); if (JERRY_UNLIKELY (prototype_p == NULL)) { return ECMA_VALUE_ERROR; } new_arraybuffer_p = ecma_arraybuffer_new_object (byte_length); ECMA_SET_NON_NULL_POINTER (new_arraybuffer_p->u2.prototype_cp, prototype_p); ecma_deref_object (prototype_p); if (ecma_arraybuffer_is_detached (src_buffer_p)) { ecma_deref_object (new_arraybuffer_p); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } } ecma_object_t *object_p = ecma_create_object (proto_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_TYPEDARRAY; ext_object_p->u.cls.u1.typedarray_type = (uint8_t) typedarray_id; ext_object_p->u.cls.u2.typedarray_flags = 0; ext_object_p->u.cls.u3.arraybuffer = ecma_make_object_value (new_arraybuffer_p); ecma_deref_object (new_arraybuffer_p); return ecma_make_object_value (object_p); } /* ecma_typedarray_create_object_with_length */ /** * Create a TypedArray object by given another TypedArray object * * See also: ES2015 22.2.1.3 * * @return ecma value of the new typedarray object * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_typedarray_create_object_with_typedarray (ecma_object_t *typedarray_p, /**< a typedarray object */ ecma_object_t *proto_p, /**< prototype object */ uint8_t element_size_shift, /**< the size shift of the element length */ ecma_typedarray_type_t typedarray_id) /**< id of the typedarray */ { uint32_t array_length = ecma_typedarray_get_length (typedarray_p); ecma_object_t *src_arraybuffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (src_arraybuffer_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (src_arraybuffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } ecma_value_t new_typedarray = ecma_typedarray_create_object_with_length (array_length, src_arraybuffer_p, proto_p, element_size_shift, typedarray_id); if (ECMA_IS_VALUE_ERROR (new_typedarray)) { return new_typedarray; } ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); ecma_object_t *dst_arraybuffer_p = ecma_typedarray_get_arraybuffer (new_typedarray_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (dst_arraybuffer_p)) { ecma_deref_object (new_typedarray_p); return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (dst_arraybuffer_p)) { ecma_deref_object (new_typedarray_p); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } lit_utf8_byte_t *src_buf_p = ecma_arraybuffer_get_buffer (src_arraybuffer_p); lit_utf8_byte_t *dst_buf_p = ecma_arraybuffer_get_buffer (dst_arraybuffer_p); src_buf_p += ecma_typedarray_get_offset (typedarray_p); ecma_typedarray_type_t src_id = ecma_get_typedarray_id (typedarray_p); if (src_id == typedarray_id) { memcpy (dst_buf_p, src_buf_p, array_length << element_size_shift); } else { #if JERRY_BUILTIN_BIGINT if ((ECMA_TYPEDARRAY_IS_BIGINT_TYPE (src_id) ^ ECMA_TYPEDARRAY_IS_BIGINT_TYPE (typedarray_id)) == 1) { ecma_deref_object (new_typedarray_p); return ecma_raise_type_error (ECMA_ERR_INCOMPATIBLE_TYPEDARRAY_TYPES); } #endif /* JERRY_BUILTIN_BIGINT */ uint32_t src_element_size = 1u << ecma_typedarray_get_element_size_shift (typedarray_p); uint32_t dst_element_size = 1u << element_size_shift; ecma_typedarray_getter_fn_t src_typedarray_getter_cb = ecma_get_typedarray_getter_fn (src_id); ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (typedarray_id); for (uint32_t i = 0; i < array_length; i++) { /* Convert values from source to destination format. */ ecma_value_t tmp = src_typedarray_getter_cb (src_buf_p); ecma_value_t set_element = target_typedarray_setter_cb (dst_buf_p, tmp); ecma_free_value (tmp); if (ECMA_IS_VALUE_ERROR (set_element)) { ecma_deref_object (new_typedarray_p); return set_element; } src_buf_p += src_element_size; dst_buf_p += dst_element_size; } } return new_typedarray; } /* ecma_typedarray_create_object_with_typedarray */ /** * Helper method for ecma_op_typedarray_from * * @return ECMA_VALUE_TRUE - if setting the given value to the new typedarray was successful * ECMA_VALUE_ERROR - otherwise */ static ecma_value_t ecma_op_typedarray_from_helper (ecma_value_t this_val, /**< this_arg for the above from function */ ecma_value_t current_value, /**< given value to set */ uint32_t index, /**< current index */ ecma_object_t *func_object_p, /**< map function object */ uint8_t *buffer_p, /**< target buffer */ ecma_typedarray_setter_fn_t setter_cb) /**< setter callback function */ { ecma_value_t mapped_value = current_value; if (func_object_p != NULL) { /* 17.d 17.f */ ecma_value_t current_index = ecma_make_uint32_value (index); ecma_value_t call_args[] = { current_value, current_index }; ecma_value_t cb_value = ecma_op_function_call (func_object_p, this_val, call_args, 2); ecma_free_value (current_value); ecma_free_value (current_index); if (ECMA_IS_VALUE_ERROR (cb_value)) { return cb_value; } mapped_value = cb_value; } ecma_value_t set_element = setter_cb (buffer_p, mapped_value); ecma_free_value (mapped_value); if (ECMA_IS_VALUE_ERROR (set_element)) { return set_element; } return ECMA_VALUE_TRUE; } /* ecma_op_typedarray_from_helper */ /** * Create a TypedArray object by transforming from an array-like object or iterable object * * See also: ES11 22.2.4.4 * * @return ecma value of the new typedarray object * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_typedarray_create_object_with_object (ecma_value_t items_val, /**< the source array-like object */ ecma_object_t *proto_p, /**< prototype object */ uint8_t element_size_shift, /**< the size shift of the element length */ ecma_typedarray_type_t typedarray_id) /**< id of the typedarray */ { /* 5 */ ecma_value_t using_iterator = ecma_op_get_method_by_symbol_id (items_val, LIT_GLOBAL_SYMBOL_ITERATOR); ecma_value_t ret_value = ECMA_VALUE_EMPTY; ecma_object_t *new_typedarray_p; ecma_typedarray_info_t info; ecma_value_t *next_value_p; uint8_t *buffer_p; ecma_typedarray_setter_fn_t setter_cb; uint8_t *limit_p; if (ECMA_IS_VALUE_ERROR (using_iterator)) { return using_iterator; } /* 6 */ if (!ecma_is_value_undefined (using_iterator)) { /* 6.a */ ecma_value_t next_method; ecma_value_t iterator = ecma_op_get_iterator (items_val, using_iterator, &next_method); ecma_free_value (using_iterator); if (ECMA_IS_VALUE_ERROR (iterator)) { return iterator; } ecma_collection_t *values_p = ecma_new_collection (); while (true) { ecma_value_t next = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (next)) { ret_value = next; break; } if (next == ECMA_VALUE_FALSE) { break; } ecma_value_t next_value = ecma_op_iterator_value (next); ecma_free_value (next); if (ECMA_IS_VALUE_ERROR (next_value)) { ret_value = next_value; break; } ecma_collection_push_back (values_p, next_value); } ecma_free_value (iterator); ecma_free_value (next_method); if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_collection_free (values_p); return ret_value; } /* 8.c */ ecma_value_t new_typedarray = ecma_typedarray_create_object_with_length (values_p->item_count, NULL, proto_p, element_size_shift, typedarray_id); if (ECMA_IS_VALUE_ERROR (new_typedarray)) { ecma_collection_free (values_p); return new_typedarray; } new_typedarray_p = ecma_get_object_from_value (new_typedarray); info = ecma_typedarray_get_info (new_typedarray_p); next_value_p = values_p->buffer_p; ret_value = ECMA_VALUE_ERROR; if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { goto free_collection; } if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); goto free_collection; } buffer_p = ecma_typedarray_get_buffer (&info); setter_cb = ecma_get_typedarray_setter_fn (info.id); limit_p = buffer_p + (values_p->item_count << info.shift); ret_value = ecma_make_object_value (new_typedarray_p); /* 8.e */ while (buffer_p < limit_p) { ecma_value_t value = *next_value_p++; ecma_value_t set_value = setter_cb (buffer_p, value); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (set_value)) { ret_value = set_value; break; } buffer_p += info.element_size; } free_collection: if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_value_t *last_value_p = values_p->buffer_p + values_p->item_count; while (next_value_p < last_value_p) { ecma_free_value (*next_value_p++); } ecma_deref_object (new_typedarray_p); } ecma_collection_destroy (values_p); return ret_value; } /* 8 */ ecma_value_t arraylike_object_val = ecma_op_to_object (items_val); if (ECMA_IS_VALUE_ERROR (arraylike_object_val)) { return arraylike_object_val; } ecma_object_t *arraylike_object_p = ecma_get_object_from_value (arraylike_object_val); /* 9 */ ecma_length_t length_index; ecma_value_t len_value = ecma_op_object_get_length (arraylike_object_p, &length_index); if (ECMA_IS_VALUE_ERROR (len_value)) { ecma_deref_object (arraylike_object_p); return len_value; } if (length_index >= UINT32_MAX) { ecma_deref_object (arraylike_object_p); return ecma_raise_range_error (ECMA_ERR_INVALID_TYPEDARRAY_LENGTH); } uint32_t len = (uint32_t) length_index; /* 10 */ ecma_value_t new_typedarray = ecma_typedarray_create_object_with_length (len, NULL, proto_p, element_size_shift, typedarray_id); if (ECMA_IS_VALUE_ERROR (new_typedarray)) { ecma_deref_object (arraylike_object_p); return new_typedarray; } new_typedarray_p = ecma_get_object_from_value (new_typedarray); info = ecma_typedarray_get_info (new_typedarray_p); ret_value = ECMA_VALUE_ERROR; if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { goto free_object; } if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); goto free_object; } buffer_p = ecma_typedarray_get_buffer (&info); setter_cb = ecma_get_typedarray_setter_fn (info.id); ret_value = ecma_make_object_value (new_typedarray_p); /* 12 */ for (uint32_t index = 0; index < len; index++) { ecma_value_t value = ecma_op_object_find_by_index (arraylike_object_p, index); if (ECMA_IS_VALUE_ERROR (value)) { ret_value = value; break; } if (!ecma_is_value_found (value)) { value = ECMA_VALUE_UNDEFINED; } ecma_value_t set_value = setter_cb (buffer_p, value); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (set_value)) { ret_value = set_value; break; } buffer_p += info.element_size; } free_object: ecma_deref_object (arraylike_object_p); if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_deref_object (new_typedarray_p); } return ret_value; } /* ecma_typedarray_create_object_with_object */ /** * Create a TypedArray object by transforming from an array-like object or iterable object * * See also: ES11 22.2.2.1 * * @return ecma value of the new typedarray object * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_typedarray_from (ecma_value_t this_val, /**< this value */ ecma_value_t source_val, /**< source value */ ecma_value_t map_fn_val, /**< mapped function value */ ecma_value_t this_arg) /**< this argument */ { JERRY_UNUSED (this_arg); /* 3 */ JERRY_ASSERT (ecma_op_is_callable (map_fn_val) || ecma_is_value_undefined (map_fn_val)); /* 4 */ ecma_object_t *func_object_p = NULL; if (!ecma_is_value_undefined (map_fn_val)) { func_object_p = ecma_get_object_from_value (map_fn_val); } /* 5 */ ecma_value_t using_iterator = ecma_op_get_method_by_symbol_id (source_val, LIT_GLOBAL_SYMBOL_ITERATOR); if (ECMA_IS_VALUE_ERROR (using_iterator)) { return using_iterator; } /* 6 */ if (!ecma_is_value_undefined (using_iterator)) { /* 6.a */ ecma_value_t next_method; ecma_value_t iterator = ecma_op_get_iterator (source_val, using_iterator, &next_method); ecma_free_value (using_iterator); /* 6.b */ if (ECMA_IS_VALUE_ERROR (iterator)) { return iterator; } /* 6.c */ ecma_collection_t *values_p = ecma_new_collection (); ecma_value_t ret_value = ECMA_VALUE_EMPTY; /* 6.e */ while (true) { ecma_value_t next = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (next)) { ret_value = next; break; } if (next == ECMA_VALUE_FALSE) { break; } ecma_value_t next_value = ecma_op_iterator_value (next); ecma_free_value (next); if (ECMA_IS_VALUE_ERROR (next_value)) { ret_value = next_value; break; } ecma_collection_push_back (values_p, next_value); } ecma_free_value (iterator); ecma_free_value (next_method); if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_collection_free (values_p); return ret_value; } /* 6.c */ ecma_object_t *constructor_obj_p = ecma_get_object_from_value (this_val); ecma_value_t len_val = ecma_make_uint32_value (values_p->item_count); ecma_value_t new_typedarray = ecma_typedarray_create (constructor_obj_p, &len_val, 1); ecma_free_value (len_val); if (ECMA_IS_VALUE_ERROR (new_typedarray)) { ecma_collection_free (values_p); return new_typedarray; } ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); ecma_typedarray_info_t info = ecma_typedarray_get_info (new_typedarray_p); ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); ecma_value_t *next_value_p = values_p->buffer_p; uint8_t *buffer_p; ret_value = ECMA_VALUE_ERROR; if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { goto free_collection; } if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); goto free_collection; } buffer_p = ecma_typedarray_get_buffer (&info); ret_value = ecma_make_object_value (new_typedarray_p); /* 6.e */ for (uint32_t index = 0; index < values_p->item_count; index++) { ecma_value_t set_value = ecma_op_typedarray_from_helper (this_arg, *next_value_p++, index, func_object_p, buffer_p, setter_cb); if (ECMA_IS_VALUE_ERROR (set_value)) { ret_value = set_value; break; } buffer_p += info.element_size; } free_collection: if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_value_t *last_value_p = values_p->buffer_p + values_p->item_count; while (next_value_p < last_value_p) { ecma_free_value (*next_value_p++); } ecma_deref_object (new_typedarray_p); } ecma_collection_destroy (values_p); return ret_value; } /* 8 */ ecma_value_t arraylike_object_val = ecma_op_to_object (source_val); if (ECMA_IS_VALUE_ERROR (arraylike_object_val)) { return arraylike_object_val; } ecma_object_t *arraylike_object_p = ecma_get_object_from_value (arraylike_object_val); /* 9 */ ecma_length_t length_index; ecma_value_t len_value = ecma_op_object_get_length (arraylike_object_p, &length_index); if (ECMA_IS_VALUE_ERROR (len_value)) { ecma_deref_object (arraylike_object_p); return len_value; } if (length_index >= UINT32_MAX) { ecma_deref_object (arraylike_object_p); return ecma_raise_range_error (ECMA_ERR_INVALID_TYPEDARRAY_LENGTH); } uint32_t len = (uint32_t) length_index; /* 10 */ ecma_object_t *constructor_obj_p = ecma_get_object_from_value (this_val); ecma_value_t len_val = ecma_make_uint32_value (len); ecma_value_t new_typedarray = ecma_typedarray_create (constructor_obj_p, &len_val, 1); ecma_free_value (len_val); if (ECMA_IS_VALUE_ERROR (new_typedarray)) { ecma_deref_object (arraylike_object_p); return new_typedarray; } ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); ecma_typedarray_info_t info = ecma_typedarray_get_info (new_typedarray_p); ecma_value_t ret_value = ECMA_VALUE_ERROR; uint8_t *buffer_p; ecma_typedarray_setter_fn_t setter_cb; if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { goto free_object; } if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); goto free_object; } buffer_p = ecma_typedarray_get_buffer (&info); setter_cb = ecma_get_typedarray_setter_fn (info.id); ret_value = ecma_make_object_value (new_typedarray_p); /* 12 */ for (uint32_t index = 0; index < len; index++) { ecma_value_t value = ecma_op_object_find_by_index (arraylike_object_p, index); if (ECMA_IS_VALUE_ERROR (value)) { ret_value = value; break; } if (!ecma_is_value_found (value)) { value = ECMA_VALUE_UNDEFINED; } ecma_value_t set_value = ecma_op_typedarray_from_helper (this_arg, value, index, func_object_p, buffer_p, setter_cb); if (ECMA_IS_VALUE_ERROR (set_value)) { ret_value = set_value; break; } buffer_p += info.element_size; } free_object: ecma_deref_object (arraylike_object_p); if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_deref_object (new_typedarray_p); } return ret_value; } /* ecma_op_typedarray_from */ /** * Get the arraybuffer of the typedarray object * * @return the pointer to the internal arraybuffer */ ecma_object_t * ecma_typedarray_get_arraybuffer (ecma_object_t *typedarray_p) /**< the pointer to the typedarray object */ { JERRY_ASSERT (ecma_object_is_typedarray (typedarray_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) typedarray_p; return ecma_get_object_from_value (ext_object_p->u.cls.u3.arraybuffer); } /* ecma_typedarray_get_arraybuffer */ /** * Get the element size shift in the typedarray object * * @return the size shift of the element, size is 1 << shift */ uint8_t ecma_typedarray_get_element_size_shift (ecma_object_t *typedarray_p) /**< the pointer to the typedarray object */ { JERRY_ASSERT (ecma_object_is_typedarray (typedarray_p)); return ecma_typedarray_helper_get_shift_size (ecma_get_typedarray_id (typedarray_p)); } /* ecma_typedarray_get_element_size_shift */ /** * Get the array length of the typedarray object * * @return the array length */ uint32_t ecma_typedarray_get_length (ecma_object_t *typedarray_p) /**< the pointer to the typedarray object */ { JERRY_ASSERT (ecma_object_is_typedarray (typedarray_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) typedarray_p; if (!(ext_object_p->u.cls.u2.typedarray_flags & ECMA_TYPEDARRAY_IS_EXTENDED)) { ecma_object_t *arraybuffer_p = ecma_get_object_from_value (ext_object_p->u.cls.u3.arraybuffer); ecma_extended_object_t *arraybuffer_object_p = (ecma_extended_object_t *) arraybuffer_p; uint32_t buffer_length = arraybuffer_object_p->u.cls.u3.length; uint8_t shift = ecma_typedarray_get_element_size_shift (typedarray_p); return buffer_length >> shift; } ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); if (ecma_arraybuffer_is_detached (arraybuffer_p)) { return 0; } ecma_extended_typedarray_object_t *info_p = (ecma_extended_typedarray_object_t *) ext_object_p; return info_p->array_length; } /* ecma_typedarray_get_length */ /** * Get the offset of the internal arraybuffer * * @return the offset */ uint32_t ecma_typedarray_get_offset (ecma_object_t *typedarray_p) /**< the pointer to the typedarray object */ { JERRY_ASSERT (ecma_object_is_typedarray (typedarray_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) typedarray_p; if (!(ext_object_p->u.cls.u2.typedarray_flags & ECMA_TYPEDARRAY_IS_EXTENDED)) { return 0; } ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); if (ecma_arraybuffer_is_detached (arraybuffer_p)) { return 0; } ecma_extended_typedarray_object_t *info_p = (ecma_extended_typedarray_object_t *) ext_object_p; return info_p->byte_offset; } /* ecma_typedarray_get_offset */ /** * Utility function: return the pointer of the data buffer referenced by the typedarray info * * @return pointer to the data buffer if successful, * NULL otherwise */ uint8_t * ecma_typedarray_get_buffer (ecma_typedarray_info_t *info_p) /**< typedarray info */ { return ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; } /* ecma_typedarray_get_buffer */ /** * Create a new typedarray object. * * The struct of the typedarray object * ecma_object_t * extend_part * typedarray_info * * @return ecma value of the new typedarray object * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_typedarray (const ecma_value_t *arguments_list_p, /**< the arg list passed to typedarray construct */ uint32_t arguments_list_len, /**< the length of the arguments_list_p */ ecma_object_t *proto_p, /**< prototype object */ uint8_t element_size_shift, /**< the size shift of the element length */ ecma_typedarray_type_t typedarray_id) /**< id of the typedarray */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); if (arguments_list_len == 0) { /* 22.2.1.1 */ return ecma_typedarray_create_object_with_length (0, NULL, proto_p, element_size_shift, typedarray_id); } if (!ecma_is_value_object (arguments_list_p[0])) { ecma_number_t num = 0; if (!ecma_is_value_undefined (arguments_list_p[0]) && ECMA_IS_VALUE_ERROR (ecma_op_to_index (arguments_list_p[0], &num))) { return ECMA_VALUE_ERROR; } JERRY_ASSERT (num >= 0 && num <= ECMA_NUMBER_MAX_SAFE_INTEGER); if (num > UINT32_MAX) { #if JERRY_ERROR_MESSAGES return ecma_raise_standard_error_with_format (JERRY_ERROR_RANGE, "Invalid typed array length: %", arguments_list_p[0]); #else /* !JERRY_ERROR_MESSAGES */ return ecma_raise_range_error (ECMA_ERR_EMPTY); #endif /* JERRY_ERROR_MESSAGES */ } return ecma_typedarray_create_object_with_length ((uint32_t) num, NULL, proto_p, element_size_shift, typedarray_id); } ecma_object_t *obj_p = ecma_get_object_from_value (arguments_list_p[0]); if (ecma_object_is_typedarray (obj_p)) { /* 22.2.1.3 */ ecma_object_t *typedarray_p = obj_p; return ecma_typedarray_create_object_with_typedarray (typedarray_p, proto_p, element_size_shift, typedarray_id); } if (!ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) && !ecma_object_is_shared_arraybuffer (obj_p)) { /* 22.2.1.4 */ return ecma_typedarray_create_object_with_object (arguments_list_p[0], proto_p, element_size_shift, typedarray_id); } /* 22.2.1.5 */ ecma_object_t *arraybuffer_p = obj_p; ecma_value_t byte_offset_value = ((arguments_list_len > 1) ? arguments_list_p[1] : ECMA_VALUE_UNDEFINED); ecma_value_t length_value = ((arguments_list_len > 2) ? arguments_list_p[2] : ECMA_VALUE_UNDEFINED); ecma_number_t offset; if (ECMA_IS_VALUE_ERROR (ecma_op_to_index (byte_offset_value, &offset))) { return ECMA_VALUE_ERROR; } if (ecma_number_is_negative (offset) || fmod (offset, (1 << element_size_shift)) != 0) { /* ES2015 22.2.1.5: 9 - 10. */ if (ecma_number_is_zero (offset)) { offset = 0; } else { return ecma_raise_range_error (ECMA_ERR_INVALID_OFFSET); } } ecma_number_t new_length = 0; if (ECMA_IS_VALUE_ERROR (ecma_op_to_index (length_value, &new_length))) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (arraybuffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } if (offset > UINT32_MAX) { return ecma_raise_range_error (ECMA_ERR_INVALID_LENGTH); } uint32_t byte_offset = (uint32_t) offset; uint32_t buf_byte_length = ecma_arraybuffer_get_length (arraybuffer_p); uint32_t new_byte_length = 0; if (ecma_is_value_undefined (length_value)) { if ((buf_byte_length % (uint32_t) (1 << element_size_shift) != 0) || (buf_byte_length < byte_offset)) { return ecma_raise_range_error (ECMA_ERR_INVALID_LENGTH); } new_byte_length = (uint32_t) (buf_byte_length - byte_offset); } else { if (new_length > (UINT32_MAX >> element_size_shift)) { return ecma_raise_range_error (ECMA_ERR_MAXIMUM_TYPEDARRAY_SIZE_IS_REACHED); } new_byte_length = (uint32_t) new_length << element_size_shift; if (byte_offset > buf_byte_length || new_byte_length > (buf_byte_length - byte_offset)) { return ecma_raise_range_error (ECMA_ERR_INVALID_LENGTH); } } bool needs_ext_typedarray_obj = (byte_offset != 0 || new_byte_length != buf_byte_length); size_t object_size = (needs_ext_typedarray_obj ? sizeof (ecma_extended_typedarray_object_t) : sizeof (ecma_extended_object_t)); ecma_object_t *object_p = ecma_create_object (proto_p, object_size, ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_TYPEDARRAY; ext_object_p->u.cls.u1.typedarray_type = (uint8_t) typedarray_id; ext_object_p->u.cls.u2.typedarray_flags = 0; ext_object_p->u.cls.u3.arraybuffer = ecma_make_object_value (arraybuffer_p); if (needs_ext_typedarray_obj) { ext_object_p->u.cls.u2.typedarray_flags |= ECMA_TYPEDARRAY_IS_EXTENDED; ecma_extended_typedarray_object_t *typedarray_info_p = (ecma_extended_typedarray_object_t *) object_p; typedarray_info_p->array_length = new_byte_length >> element_size_shift; typedarray_info_p->byte_offset = byte_offset; } return ecma_make_object_value (object_p); } /* ecma_op_create_typedarray */ /** * Helper function for typedArray.prototype object's {'keys', 'values', 'entries', '@@iterator'} * routines common parts. * * See also: * ECMA-262 v6, 22.2.3.15 * ECMA-262 v6, 22.2.3.29 * ECMA-262 v6, 22.2.3.6 * ECMA-262 v6, 22.1.3.30 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object, if success * error - otherwise */ ecma_value_t ecma_typedarray_iterators_helper (ecma_value_t this_arg, /**< this argument */ ecma_iterator_kind_t kind) /**< iterator kind */ { JERRY_ASSERT (ecma_is_typedarray (this_arg)); ecma_object_t *typedarray_p = ecma_get_object_from_value (this_arg); ecma_typedarray_info_t info = ecma_typedarray_get_info (typedarray_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAY_ITERATOR_PROTOTYPE); return ecma_op_create_iterator_object (this_arg, prototype_obj_p, ECMA_OBJECT_CLASS_ARRAY_ITERATOR, kind); } /* ecma_typedarray_iterators_helper */ /** * Check if the object is typedarray * * @return true - if object is a TypedArray object * false - otherwise */ bool ecma_object_is_typedarray (ecma_object_t *obj_p) /**< the target object need to be checked */ { JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); return ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_TYPEDARRAY); } /* ecma_object_is_typedarray */ /** * Check if the value is typedarray * * @return true - if value is a TypedArray object * false - otherwise */ bool ecma_is_typedarray (ecma_value_t value) /**< the target need to be checked */ { if (!ecma_is_value_object (value)) { return false; } return ecma_object_is_typedarray (ecma_get_object_from_value (value)); } /* ecma_is_typedarray */ /** * Checks whether the property name is a valid element index * * @return true, if valid * false, otherwise */ bool ecma_typedarray_is_element_index (ecma_string_t *property_name_p) /**< property name */ { ecma_number_t num = ecma_string_to_number (property_name_p); if (num == 0) { return true; } ecma_string_t *num_to_str = ecma_new_ecma_string_from_number (num); bool is_same = ecma_compare_ecma_strings (property_name_p, num_to_str); ecma_deref_ecma_string (num_to_str); return is_same; } /* ecma_typedarray_is_element_index */ /** * List names of a TypedArray object's integer indexed properties */ void ecma_op_typedarray_list_lazy_property_names (ecma_object_t *obj_p, /**< a TypedArray object */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< property name filter options */ { JERRY_ASSERT (ecma_object_is_typedarray (obj_p)); if (filter & JERRY_PROPERTY_FILTER_EXCLUDE_INTEGER_INDICES) { return; } uint32_t array_length = ecma_typedarray_get_length (obj_p); for (uint32_t i = 0; i < array_length; i++) { ecma_string_t *name_p = ecma_new_ecma_string_from_uint32 (i); ecma_collection_push_back (prop_names_p, ecma_make_string_value (name_p)); } prop_counter_p->array_index_named_props += array_length; } /* ecma_op_typedarray_list_lazy_property_names */ /** * [[DefineOwnProperty]] operation for TypedArray objects * * See also: ES2015 9.4.5.3 * * @return ECMA_VALUE_TRUE - if the property is successfully defined * ECMA_VALUE_FALSE - if is JERRY_PROP_SHOULD_THROW is not set * raised TypeError - otherwise */ ecma_value_t ecma_op_typedarray_define_own_property (ecma_object_t *obj_p, /**< TypedArray object */ ecma_string_t *property_name_p, /**< property name */ const ecma_property_descriptor_t *property_desc_p) /**< property descriptor */ { JERRY_ASSERT (ecma_object_is_typedarray (obj_p)); if (JERRY_UNLIKELY (ecma_prop_name_is_symbol (property_name_p))) { return ecma_op_general_object_define_own_property (obj_p, property_name_p, property_desc_p); } uint32_t index = ecma_string_get_array_index (property_name_p); if (index == ECMA_STRING_NOT_ARRAY_INDEX && !ecma_typedarray_is_element_index (property_name_p)) { return ecma_op_general_object_define_own_property (obj_p, property_name_p, property_desc_p); } if ((property_desc_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) || ((property_desc_p->flags & (JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE)) == (JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE)) || ((property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE_DEFINED) && !(property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE)) || ((property_desc_p->flags & JERRY_PROP_IS_WRITABLE_DEFINED) && !(property_desc_p->flags & JERRY_PROP_IS_WRITABLE))) { return ecma_raise_property_redefinition (property_name_p, property_desc_p->flags); } ecma_typedarray_info_t info = ecma_typedarray_get_info (obj_p); if (index >= info.length || ecma_arraybuffer_is_detached (info.array_buffer_p)) { return ECMA_VALUE_FALSE; } if (property_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) { ecma_value_t set_element = ecma_set_typedarray_element (&info, property_desc_p->value, index); if (ECMA_IS_VALUE_ERROR (set_element)) { return set_element; } } return ECMA_VALUE_TRUE; } /* ecma_op_typedarray_define_own_property */ /** * Specify the creation of a new TypedArray * object using a constructor function. * * See also: ES11 22.2.4.6 * * Used by: * - ecma_typedarray_species_create * * @return ecma_value_t function object from created from constructor_p argument */ ecma_value_t ecma_typedarray_create (ecma_object_t *constructor_p, /**< constructor function */ ecma_value_t *arguments_list_p, /**< argument list */ uint32_t arguments_list_len) /**< length of argument list */ { ecma_value_t ret_val = ecma_op_function_construct (constructor_p, constructor_p, arguments_list_p, arguments_list_len); if (ECMA_IS_VALUE_ERROR (ret_val)) { return ret_val; } if (!ecma_is_typedarray (ret_val)) { ecma_free_value (ret_val); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTED_OBJECT_IS_NOT_TYPEDARRAY); } ecma_object_t *typedarray_p = ecma_get_object_from_value (ret_val); ecma_typedarray_info_t info = ecma_typedarray_get_info (typedarray_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { ecma_deref_object (typedarray_p); return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { ecma_deref_object (typedarray_p); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } if ((arguments_list_len == 1) && (ecma_is_value_number (arguments_list_p[0]))) { ecma_number_t num = ecma_get_number_from_value (arguments_list_p[0]); if (info.length < num) { ecma_free_value (ret_val); return ecma_raise_type_error (ECMA_ERR_TYPEDARRAY_SMALLER_THAN_FILTER_CALL_RESULT); } } return ret_val; } /* ecma_typedarray_create */ /* Specify the creation of a new TypedArray object * using a constructor function that is derived from this_arg. * * See also: ES11 22.2.4.7 * * @return ecma value of the new typedarray object, constructed by default or species constructor */ ecma_value_t ecma_typedarray_species_create (ecma_value_t this_arg, /**< this argument */ ecma_value_t *arguments_list_p, /**< the arg list passed to typedarray construct */ uint32_t arguments_list_len) /**< length of the arg list */ { ecma_object_t *typedarray_p = ecma_get_object_from_value (this_arg); ecma_typedarray_info_t info = ecma_typedarray_get_info (typedarray_p); JERRY_ASSERT (ecma_is_typedarray (this_arg)); ecma_builtin_id_t default_constructor = ecma_typedarray_helper_get_constructor_id (info.id); ecma_value_t constructor = ecma_op_species_constructor (typedarray_p, default_constructor); if (ECMA_IS_VALUE_ERROR (constructor)) { return constructor; } ecma_object_t *constructor_proto_p = ecma_get_object_from_value (constructor); ecma_value_t result = ecma_typedarray_create (constructor_proto_p, arguments_list_p, arguments_list_len); ecma_deref_object (constructor_proto_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } #if JERRY_BUILTIN_BIGINT ecma_object_t *result_p = ecma_get_object_from_value (result); ecma_typedarray_info_t result_info = ecma_typedarray_get_info (result_p); /* * Check result_info.id to to be either bigint type if info.id is one * or be neither of them is info.id is none of them as well. */ if (ECMA_TYPEDARRAY_IS_BIGINT_TYPE (info.id) ^ ECMA_TYPEDARRAY_IS_BIGINT_TYPE (result_info.id)) { ecma_free_value (result); return ecma_raise_type_error (ECMA_ERR_CONTENTTYPE_RETURNED_TYPEDARRAY_NOT_MATCH_SOURCE); } #endif /* JERRY_BUILTIN_BIGINT */ return result; } /* ecma_typedarray_species_create */ /** * Create a typedarray object based on the "type" and arraylength * The "type" is same with arg1 * * @return ecma_value_t */ ecma_value_t ecma_op_create_typedarray_with_type_and_length (ecma_typedarray_type_t typedarray_id, /** TypedArray id */ uint32_t array_length) /**< length of the typedarray */ { // TODO: assert validate typedarray_id ecma_object_t *proto_p = ecma_builtin_get (ecma_typedarray_helper_get_prototype_id (typedarray_id)); uint8_t element_size_shift = ecma_typedarray_helper_get_shift_size (typedarray_id); ecma_value_t new_obj = ecma_typedarray_create_object_with_length (array_length, NULL, proto_p, element_size_shift, typedarray_id); return new_obj; } /* ecma_op_create_typedarray_with_type_and_length */ /** * Method for getting the additional typedArray information. */ ecma_typedarray_info_t ecma_typedarray_get_info (ecma_object_t *typedarray_p) { ecma_typedarray_info_t info; info.id = ecma_get_typedarray_id (typedarray_p); info.length = ecma_typedarray_get_length (typedarray_p); info.shift = ecma_typedarray_get_element_size_shift (typedarray_p); info.element_size = (uint8_t) (1 << info.shift); info.offset = ecma_typedarray_get_offset (typedarray_p); info.array_buffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); return info; } /* ecma_typedarray_get_info */ /** * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgRender.cpp000664 001750 001750 00000106324 15164251010 034264 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgRender.h" /************************************************************************/ /* RenderMethod Class Implementation */ /************************************************************************/ uint32_t RenderMethod::ref() { ScopedLock lock(key); return (++refCnt); } uint32_t RenderMethod::unref() { ScopedLock lock(key); return (--refCnt); } RenderRegion RenderMethod::viewport() { return vport; } bool RenderMethod::viewport(const RenderRegion& vp) { vport = vp; return true; } /************************************************************************/ /* RenderPath Class Implementation */ /************************************************************************/ bool RenderPath::bounds(const Matrix* m, BBox& box) { if (cmds.empty() || cmds.first() == PathCommand::CubicTo) return false; auto pt = pts.begin(); auto cmd = cmds.begin(); auto assign = [&](const Point& pt, BBox& box) -> void { if (pt.x < box.min.x) box.min.x = pt.x; if (pt.y < box.min.y) box.min.y = pt.y; if (pt.x > box.max.x) box.max.x = pt.x; if (pt.y > box.max.y) box.max.y = pt.y; }; while (cmd < cmds.end()) { switch (*cmd) { case PathCommand::MoveTo: { //skip the invalid assignments if (cmd + 1 < cmds.end()) { auto next = *(cmd + 1); if (next == PathCommand::LineTo || next == PathCommand::CubicTo) { assign(*pt * m, box); } } ++pt; break; } case PathCommand::LineTo: { assign(*pt * m, box); ++pt; break; } case PathCommand::CubicTo: { Bezier::bounds(box, pt[-1] * m, pt[0] * m, pt[1] * m, pt[2] * m); pt += 3; break; } default: break; } ++cmd; } return true; } void RenderPath::optimizeGL(RenderPath& out, const Matrix& matrix) const { #if defined(THORVG_GL_RASTER_SUPPORT) static constexpr auto PX_TOLERANCE = 0.25f; if (empty()) return; out.cmds.clear(); out.pts.clear(); out.cmds.reserve(cmds.count); out.pts.reserve(pts.count); auto cmds = this->cmds.data; auto cmdCnt = this->cmds.count; auto pts = this->pts.data; Point lastOutT, prevOutT; // The suffix "T" indicates that the point is transformed. uint32_t prevIdx = 0; uint32_t prevPrevIdx = 0; auto hasPrevPrev = false; //vecLen is guaranteed to be non-zero since closed points are already merged auto point2Line = [](const Point& point, const Point& start, const Point& vec, float vecLen, float& maxDist, float& minT, float& maxT) { Point offset = point - start; auto dist = fabsf(tvg::cross(vec, offset)) / vecLen; if (dist > maxDist) maxDist = dist; auto t = tvg::dot(offset, vec) / (vecLen * vecLen); if (t < minT) minT = t; if (t > maxT) maxT = t; }; auto validateCubic = [&point2Line](const Point& start, const Point& ctrl1, const Point& ctrl2, const Point& end, float& maxDist, float& minT, float& maxT, float& vecLen) { auto vec = end - start; vecLen = sqrtf(vec.x * vec.x + vec.y * vec.y); maxDist = 0.0f; minT = FLT_MAX; maxT = FLT_MIN; point2Line(ctrl1, start, vec, vecLen, maxDist, minT, maxT); point2Line(ctrl2, start, vec, vecLen, maxDist, minT, maxT); }; auto point2LineSimple = [](const Point& point, const Point& start, const Point& end, float& dist, float& t, float& vecLen) { auto vec = end - start; auto vecLenSq = vec.x * vec.x + vec.y * vec.y; vecLen = sqrtf(vecLenSq); Point offset = point - start; dist = fabsf(tvg::cross(vec, offset)) / vecLen; t = tvg::dot(offset, vec) / vecLenSq; }; auto addLineCmd = [&](const Point& ptT) { out.cmds.push(PathCommand::LineTo); out.pts.push(ptT); prevOutT = lastOutT; lastOutT = ptT; prevPrevIdx = prevIdx; prevIdx = out.pts.count - 1; hasPrevPrev = true; }; auto processLineCollinear = [&](const Point& startT, const Point& ptT) { if (!hasPrevPrev || out.pts.count <= 1) { addLineCmd(ptT); return; } float dist, t, vecLen; point2LineSimple(ptT, prevOutT, startT, dist, t, vecLen); if (dist > PX_TOLERANCE) { addLineCmd(ptT); return; } auto tEps = PX_TOLERANCE / vecLen; if (t <= -tEps) { out.pts[prevPrevIdx] = ptT; lastOutT = ptT; } else if (t >= 1.0f - tEps) { out.pts[prevIdx] = ptT; lastOutT = ptT; } }; auto processCubicTo = [&](const Point* cubicPts, const Point& startT) { auto ctrl1T = cubicPts[0] * matrix; auto ctrl2T = cubicPts[1] * matrix; auto endT = cubicPts[2] * matrix; if (tvg::closed(startT, endT, PX_TOLERANCE)) return; float maxDist, minT, maxT, vecLen; validateCubic(startT, ctrl1T, ctrl2T, endT, maxDist, minT, maxT, vecLen); auto flat = (maxDist <= PX_TOLERANCE); auto tEps = PX_TOLERANCE / vecLen; auto inSpan = (minT >= -tEps) && (maxT <= 1.0f + tEps); if (flat && inSpan) { processLineCollinear(startT, endT); } else { out.cmds.push(PathCommand::CubicTo); out.pts.push(ctrl1T); out.pts.push(ctrl2T); out.pts.push(endT); prevOutT = lastOutT; lastOutT = endT; prevPrevIdx = prevIdx; prevIdx = out.pts.count - 1; hasPrevPrev = true; } }; for (uint32_t i = 0; i < cmdCnt; i++) { switch (cmds[i]) { case PathCommand::MoveTo: { auto ptT = (*pts) * matrix; out.cmds.push(PathCommand::MoveTo); out.pts.push(ptT); lastOutT = ptT; prevIdx = out.pts.count - 1; hasPrevPrev = false; pts++; break; } case PathCommand::LineTo: { auto startT = lastOutT; auto ptT = (*pts) * matrix; if (tvg::closed(startT, ptT, PX_TOLERANCE)) { pts++; break; } processLineCollinear(startT, ptT); pts++; break; } case PathCommand::CubicTo: { processCubicTo(pts, lastOutT); pts += 3; break; } case PathCommand::Close: { out.cmds.push(PathCommand::Close); hasPrevPrev = false; break; } default: break; } } #else TVGLOG("RENDERER", "RenderPath transformed optimization is disabled"); #endif } void RenderPath::optimizeWG(RenderPath& out, const Matrix& matrix) const { #if defined(THORVG_WG_RASTER_SUPPORT) static constexpr auto PX_TOLERANCE = 0.25f; if (empty()) return; out.cmds.clear(); out.pts.clear(); out.cmds.reserve(cmds.count); out.pts.reserve(pts.count); auto cmds = this->cmds.data; auto cmdCnt = this->cmds.count; auto pts = this->pts.data; Point lastOutT, prevOutT; // The suffix "T" indicates that the point is transformed. uint32_t prevIdx = 0; uint32_t prevPrevIdx = 0; auto hasPrevPrev = false; //vecLen is guaranteed to be non-zero since closed points are already merged auto point2Line = [](const Point& point, const Point& start, const Point& vec, float vecLen, float& maxDist, float& minT, float& maxT) { Point offset = point - start; auto dist = fabsf(tvg::cross(vec, offset)) / vecLen; if (dist > maxDist) maxDist = dist; auto t = tvg::dot(offset, vec) / (vecLen * vecLen); if (t < minT) minT = t; if (t > maxT) maxT = t; }; auto validateCubic = [&point2Line](const Point& start, const Point& ctrl1, const Point& ctrl2, const Point& end, float& maxDist, float& minT, float& maxT, float& vecLen) { auto vec = end - start; vecLen = sqrtf(vec.x * vec.x + vec.y * vec.y); maxDist = 0.0f; minT = FLT_MAX; maxT = FLT_MIN; point2Line(ctrl1, start, vec, vecLen, maxDist, minT, maxT); point2Line(ctrl2, start, vec, vecLen, maxDist, minT, maxT); }; auto point2LineSimple = [](const Point& point, const Point& start, const Point& end, float& dist, float& t, float& vecLen) { auto vec = end - start; auto vecLenSq = vec.x * vec.x + vec.y * vec.y; vecLen = sqrtf(vecLenSq); Point offset = point - start; dist = fabsf(tvg::cross(vec, offset)) / vecLen; t = tvg::dot(offset, vec) / vecLenSq; }; auto addLineCmd = [&](const Point& pt, const Point& ptT) { out.cmds.push(PathCommand::LineTo); out.pts.push(pt); prevOutT = lastOutT; lastOutT = ptT; prevPrevIdx = prevIdx; prevIdx = out.pts.count - 1; hasPrevPrev = true; }; auto processLineCollinear = [&](const Point& startT, const Point& pt, const Point& ptT) { if (!hasPrevPrev || out.pts.count <= 1) { addLineCmd(pt, ptT); return; } float dist, t, vecLen; point2LineSimple(ptT, prevOutT, startT, dist, t, vecLen); if (dist > PX_TOLERANCE) { addLineCmd(pt, ptT); return; } auto tEps = PX_TOLERANCE / vecLen; if (t <= -tEps) { out.pts[prevPrevIdx] = pt; lastOutT = ptT; } else if (t >= 1.0f - tEps) { out.pts[prevIdx] = pt; lastOutT = ptT; } }; auto processCubicTo = [&](const Point* cubicPts, const Point& startT) { auto endT = cubicPts[2] * matrix; if (tvg::closed(startT, endT, PX_TOLERANCE)) return; float maxDist, minT, maxT, vecLen; validateCubic(startT, cubicPts[0] * matrix, cubicPts[1] * matrix, endT, maxDist, minT, maxT, vecLen); auto flat = (maxDist <= PX_TOLERANCE); auto tEps = PX_TOLERANCE / vecLen; auto inSpan = (minT >= -tEps) && (maxT <= 1.0f + tEps); if (flat && inSpan) { processLineCollinear(startT, cubicPts[2], endT); } else { out.cmds.push(PathCommand::CubicTo); out.pts.push(cubicPts[0]); out.pts.push(cubicPts[1]); out.pts.push(cubicPts[2]); prevOutT = lastOutT; lastOutT = endT; prevPrevIdx = prevIdx; prevIdx = out.pts.count - 1; hasPrevPrev = true; } }; for (uint32_t i = 0; i < cmdCnt; i++) { switch (cmds[i]) { case PathCommand::MoveTo: { out.cmds.push(PathCommand::MoveTo); out.pts.push(*pts); lastOutT = *pts * matrix; prevIdx = out.pts.count - 1; hasPrevPrev = false; pts++; break; } case PathCommand::LineTo: { auto startT = lastOutT; auto ptT = (*pts) * matrix; if (tvg::closed(startT, ptT, PX_TOLERANCE)) { pts++; break; } processLineCollinear(startT, *pts, ptT); pts++; break; } case PathCommand::CubicTo: { processCubicTo(pts, lastOutT); pts += 3; break; } case PathCommand::Close: { out.cmds.push(PathCommand::Close); hasPrevPrev = false; break; } default: break; } } #else TVGLOG("RENDERER", "RenderPath Optimization is disabled"); #endif } /************************************************************************/ /* RenderRegion Class Implementation */ /************************************************************************/ void RenderRegion::intersect(const RenderRegion& rhs) { if (min.x < rhs.min.x) min.x = rhs.min.x; if (min.y < rhs.min.y) min.y = rhs.min.y; if (max.x > rhs.max.x) max.x = rhs.max.x; if (max.y > rhs.max.y) max.y = rhs.max.y; // Not intersected: collapse to zero-area region if (max.x < min.x) max.x = min.x; if (max.y < min.y) max.y = min.y; } #ifdef THORVG_PARTIAL_RENDER_SUPPORT #include void RenderDirtyRegion::init(uint32_t w, uint32_t h) { auto cnt = int(sqrt(PARTITIONING)); auto px = int32_t(w / cnt); auto py = int32_t(h / cnt); auto lx = int32_t(w % cnt); auto ly = int32_t(h % cnt); //space partitioning for (int y = 0; y < cnt; ++y) { for (int x = 0; x < cnt; ++x) { auto& partition = partitions[y * cnt + x]; partition.list[0].reserve(64); auto& region = partition.region; region.min = {x * px, y * py}; region.max = {region.min.x + px, region.min.y + py}; //leftovers if (x == cnt -1) region.max.x += lx; if (y == cnt -1) region.max.y += ly; } } } bool RenderDirtyRegion::add(const RenderRegion& bbox) { for (int idx = 0; idx < PARTITIONING; ++idx) { auto& partition = partitions[idx]; if (bbox.max.y <= partition.region.min.y) break; if (bbox.intersected(partition.region)) { ScopedLock lock(key); partition.list[partition.current].push(RenderRegion::intersect(bbox, partition.region)); } } return true; } bool RenderDirtyRegion::add(const RenderRegion& prv, const RenderRegion& cur) { if (prv == cur) return add(prv); for (int idx = 0; idx < PARTITIONING; ++idx) { auto& partition = partitions[idx]; if (prv.intersected(partition.region)) { ScopedLock lock(key); partition.list[partition.current].push(RenderRegion::intersect(prv, partition.region)); } if (cur.intersected(partition.region)) { ScopedLock lock(key); partition.list[partition.current].push(RenderRegion::intersect(cur, partition.region)); } } return true; } void RenderDirtyRegion::clear() { for (int idx = 0; idx < PARTITIONING; ++idx) { partitions[idx].list[0].clear(); partitions[idx].list[1].clear(); } } void RenderDirtyRegion::subdivide(Array& targets, uint32_t idx, RenderRegion& lhs, RenderRegion& rhs) { RenderRegion temp[3]; uint32_t cnt = 0; //subtract top if (rhs.min.y < lhs.min.y) { temp[cnt++] = {{rhs.min.x, rhs.min.y}, {rhs.max.x, lhs.min.y}}; rhs.min.y = lhs.min.y; } //subtract bottom if (rhs.max.y > lhs.max.y) { temp[cnt++] = {{rhs.min.x, lhs.max.y}, {rhs.max.x, rhs.max.y}}; rhs.max.y = lhs.max.y; } //subtract right if (rhs.max.x > lhs.max.x) { temp[cnt++] = {{lhs.max.x, rhs.min.y}, {rhs.max.x, rhs.max.y}}; } //Please reserve memory enough with targets.reserve() if (targets.count + cnt - 1 >= targets.reserved) { TVGERR("RENDERER", "reserved(%d), required(%d)", targets.reserved, targets.count + cnt - 1); return; } /* Shift data. Considered using a list to avoid memory shifting, but ultimately, the array outperformed the list due to better cache locality. */ auto src = &targets[idx + 1]; auto dst = &targets[idx + cnt]; // <-- shift right by (cnt - 1) auto nmove = targets.count - idx - 1; // number of tail elements memmove(dst, src, sizeof(RenderRegion) * nmove); memcpy(&targets[idx], temp, sizeof(RenderRegion) * cnt); targets.count += (cnt - 1); //sorting by x coord again, only for the updated region while (dst < targets.end() && dst->min.x < rhs.max.x) ++dst; stable_sort(&targets[idx], dst, [](const RenderRegion& a, const RenderRegion& b) -> bool { return a.min.x < b.min.x; }); } void RenderDirtyRegion::commit() { if (disabled) return; for (int idx = 0; idx < PARTITIONING; ++idx) { auto current = partitions[idx].current; auto& targets = partitions[idx].list[current]; if (targets.empty()) continue; current = !current; //swapping buffers auto& output = partitions[idx].list[current]; targets.reserve(targets.count * 10); //one intersection can be divided up to 3 output.reserve(targets.count); partitions[idx].current = current; //sorting by x coord. guarantee the stable performance: O(NlogN) stable_sort(targets.begin(), targets.end(), [](const RenderRegion& a, const RenderRegion& b) -> bool { return a.min.x < b.min.x; }); //Optimized using sweep-line algorithm: O(NlogN) for (uint32_t i = 0; i < targets.count; ++i) { auto& lhs = targets[i]; if (lhs.invalid()) continue; auto merged = false; for (uint32_t j = i + 1; j < targets.count; ++j) { auto& rhs = targets[j]; if (rhs.invalid()) continue; if (lhs.max.x < rhs.min.x) break; //line sweeping //fully overlapped. drop lhs if (rhs.contained(lhs)) { merged = true; break; } //fully overlapped. replace the lhs with rhs if (lhs.contained(rhs)) { rhs = {}; continue; } //just merge & expand on x axis if (lhs.min.y == rhs.min.y && lhs.max.y == rhs.max.y) { if (lhs.max.x >= rhs.min.x) { lhs.max.x = rhs.max.x; rhs = {}; j = i; //lhs dirty region has been damaged, try again. continue; } } //just merge & expand on y axis if (lhs.min.x == rhs.min.x && lhs.max.x == rhs.max.x) { if (lhs.min.y <= rhs.max.y && rhs.min.y <= lhs.max.y) { rhs.min.y = std::min(lhs.min.y, rhs.min.y); rhs.max.y = std::max(lhs.max.y, rhs.max.y); merged = true; break; } } //subdivide regions if (lhs.intersected(rhs)) { subdivide(targets, j, lhs, rhs); --j; //rhs dirty region has been damaged, try again. } } if (!merged) output.push(lhs); //this region is complete isolated lhs = {}; } } } #endif /************************************************************************/ /* RenderTrimPath Class Implementation */ /************************************************************************/ #define EPSILON 1e-4f static void _trimAt(const PathCommand* cmds, const Point* pts, Point& moveTo, float at1, float at2, bool start, RenderPath& out) { switch (*cmds) { case PathCommand::LineTo: { Line tmp, left, right; Line{*(pts - 1), *pts}.split(at1, left, tmp); tmp.split(at2, left, right); if (start) { out.pts.push(left.pt1); moveTo = left.pt1; out.cmds.push(PathCommand::MoveTo); } out.pts.push(left.pt2); out.cmds.push(PathCommand::LineTo); break; } case PathCommand::CubicTo: { Bezier tmp, left, right; Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.split(at1, left, tmp); tmp.split(at2, left, right); if (start) { moveTo = left.start; out.pts.push(left.start); out.cmds.push(PathCommand::MoveTo); } out.pts.push(left.ctrl1); out.pts.push(left.ctrl2); out.pts.push(left.end); out.cmds.push(PathCommand::CubicTo); break; } case PathCommand::Close: { Line tmp, left, right; Line{*(pts - 1), moveTo}.split(at1, left, tmp); tmp.split(at2, left, right); if (start) { moveTo = left.pt1; out.pts.push(left.pt1); out.cmds.push(PathCommand::MoveTo); } out.pts.push(left.pt2); out.cmds.push(PathCommand::LineTo); break; } default: break; } } static void _add(const PathCommand* cmds, const Point* pts, const Point& moveTo, bool& start, RenderPath& out) { switch (*cmds) { case PathCommand::MoveTo: { out.cmds.push(PathCommand::MoveTo); out.pts.push(*pts); start = false; break; } case PathCommand::LineTo: { if (start) { out.cmds.push(PathCommand::MoveTo); out.pts.push(*(pts - 1)); } out.cmds.push(PathCommand::LineTo); out.pts.push(*pts); start = false; break; } case PathCommand::CubicTo: { if (start) { out.cmds.push(PathCommand::MoveTo); out.pts.push(*(pts - 1)); } out.cmds.push(PathCommand::CubicTo); out.pts.push(*pts); out.pts.push(*(pts + 1)); out.pts.push(*(pts + 2)); start = false; break; } case PathCommand::Close: { if (start) { out.cmds.push(PathCommand::MoveTo); out.pts.push(*(pts - 1)); } out.cmds.push(PathCommand::LineTo); out.pts.push(moveTo); start = true; break; } } } static void _trimPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, TVG_UNUSED uint32_t inPtsCnt, float trimStart, float trimEnd, RenderPath& out, bool connect = false) { auto cmds = const_cast(inCmds); auto pts = const_cast(inPts); auto moveToTrimmed = *pts; auto moveTo = *pts; auto len = 0.0f; auto _length = [&]() -> float { switch (*cmds) { case PathCommand::MoveTo: return 0.0f; case PathCommand::LineTo: return tvg::length(*(pts - 1), *pts); case PathCommand::CubicTo: return Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length(); case PathCommand::Close: return tvg::length(*(pts - 1), moveTo); } return 0.0f; }; auto _shift = [&]() -> void { switch (*cmds) { case PathCommand::MoveTo: moveTo = *pts; moveToTrimmed = *pts; ++pts; break; case PathCommand::LineTo: ++pts; break; case PathCommand::CubicTo: pts += 3; break; case PathCommand::Close: break; } ++cmds; }; auto start = !connect; for (uint32_t i = 0; i < inCmdsCnt; ++i) { auto dLen = _length(); //very short segments are skipped since due to the finite precision of Bezier curve subdivision and length calculation (1e-2), //trimming may produce very short segments that would effectively have zero length with higher computational accuracy. if (len <= trimStart) { //cut the segment at the beginning and at the end if (len + dLen > trimEnd) { _trimAt(cmds, pts, moveToTrimmed, trimStart - len, trimEnd - trimStart, start, out); start = false; //cut the segment at the beginning } else if (len + dLen > trimStart + EPSILON) { _trimAt(cmds, pts, moveToTrimmed, trimStart - len, len + dLen - trimStart, start, out); start = false; } } else if (len <= trimEnd - EPSILON) { //cut the segment at the end if (len + dLen > trimEnd) { _trimAt(cmds, pts, moveTo, 0.0f, trimEnd - len, start, out); start = true; //add the whole segment } else if (len + dLen > trimStart + EPSILON) _add(cmds, pts, moveTo, start, out); } len += dLen; _shift(); } } static void _trim(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, float begin, float end, bool connect, RenderPath& out) { auto totalLength = tvg::length(inCmds, inCmdsCnt, inPts, inPtsCnt); auto trimStart = begin * totalLength; auto trimEnd = end * totalLength; if (begin >= end) { _trimPath(inCmds, inCmdsCnt, inPts, inPtsCnt, trimStart, totalLength, out); _trimPath(inCmds, inCmdsCnt, inPts, inPtsCnt, 0.0f, trimEnd, out, connect); } else { _trimPath(inCmds, inCmdsCnt, inPts, inPtsCnt, trimStart, trimEnd, out); } } static void _get(float& begin, float& end) { auto loop = true; if (begin > 1.0f && end > 1.0f) loop = false; if (begin < 0.0f && end < 0.0f) loop = false; if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false; if (begin > 1.0f) begin -= 1.0f; if (begin < 0.0f) begin += 1.0f; if (end > 1.0f) end -= 1.0f; if (end < 0.0f) end += 1.0f; if ((loop && begin < end) || (!loop && begin > end)) std::swap(begin, end); } bool RenderTrimPath::trim(const RenderPath& in, RenderPath& out) const { if (in.pts.count < 2 || tvg::zero(begin - end)) return false; float begin = this->begin, end = this->end; _get(begin, end); out.cmds.reserve(in.cmds.count * 2); out.pts.reserve(in.pts.count * 2); auto pts = in.pts.data; auto cmds = in.cmds.data; if (simultaneous) { auto startCmds = cmds; auto startPts = pts; uint32_t i = 0; while (i < in.cmds.count) { switch (in.cmds[i]) { case PathCommand::MoveTo: { if (startCmds != cmds) _trim(startCmds, cmds - startCmds, startPts, pts - startPts, begin, end, *(cmds - 1) == PathCommand::Close, out); startPts = pts; startCmds = cmds; ++pts; ++cmds; break; } case PathCommand::LineTo: { ++pts; ++cmds; break; } case PathCommand::CubicTo: { pts += 3; ++cmds; break; } case PathCommand::Close: { ++cmds; if (startCmds != cmds) _trim(startCmds, cmds - startCmds, startPts, pts - startPts, begin, end, *(cmds - 1) == PathCommand::Close, out); startPts = pts; startCmds = cmds; break; } } i++; } if (startCmds != cmds) _trim(startCmds, cmds - startCmds, startPts, pts - startPts, begin, end, *(cmds - 1) == PathCommand::Close, out); } else { _trim(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, begin, end, false, out); } return out.pts.count >= 2; } /************************************************************************/ /* StrokeDashPath Class Implementation */ /************************************************************************/ //TODO: use this common function from sw engine #if defined(THORVG_GL_RASTER_SUPPORT) || defined(THORVG_WG_RASTER_SUPPORT) struct StrokeDashPath { public: StrokeDashPath(RenderStroke::Dash dash) : dash(dash) {} bool gen(const RenderPath& in, RenderPath& out, bool drawPoint, const Matrix* transform = nullptr); private: void lineTo(RenderPath& out, const Point& pt, bool drawPoint); void cubicTo(RenderPath& out, const Point& pt1, const Point& pt2, const Point& pt3, bool drawPoint); void point(RenderPath& out, const Point& p); Point map(const Point& pt) const { return applyTransform ? pt * (*transform) : pt; } template void segment(Segment seg, float len, RenderPath& out, bool allowDot, LengthFn lengthFn, SplitFn splitFn, DrawFn drawFn, PointFn getStartPt, const Point& endPos); RenderStroke::Dash dash; float curLen = 0.0f; int32_t curIdx = 0; Point curPos{}; bool opGap = false; bool move = true; const Matrix* transform = nullptr; bool applyTransform = false; }; template void StrokeDashPath::segment(Segment seg, float len, RenderPath& out, bool allowDot, LengthFn lengthFn, SplitFn splitFn, DrawFn drawFn, PointFn getStartPt, const Point& end) { #define MIN_CURR_LEN_THRESHOLD 0.1f if (tvg::zero(len)) { out.moveTo(map(curPos)); } else if (len <= curLen) { curLen -= len; if (!opGap) { if (move) { out.moveTo(map(curPos)); move = false; } drawFn(seg); } } else { Segment left, right; while (len - curLen > DASH_PATTERN_THRESHOLD) { if (curLen > 0.0f) { splitFn(seg, curLen, left, right); len -= curLen; if (!opGap) { if (move || dash.pattern[curIdx] - curLen < FLOAT_EPSILON) { out.moveTo(map(getStartPt(left))); move = false; } drawFn(left); } } else { if (allowDot && !opGap) point(out, getStartPt(seg)); right = seg; } curIdx = (curIdx + 1) % dash.count; curLen = dash.pattern[curIdx]; opGap = !opGap; seg = right; curPos = getStartPt(seg); move = true; } curLen -= len; if (!opGap) { if (move) { out.moveTo(map(getStartPt(seg))); move = false; } drawFn(seg); } if (curLen < MIN_CURR_LEN_THRESHOLD) { curIdx = (curIdx + 1) % dash.count; curLen = dash.pattern[curIdx]; opGap = !opGap; } } curPos = end; } //allowDot: zero length segment with non-butt cap still should be rendered as a point - only the caps are visible bool StrokeDashPath::gen(const RenderPath& in, RenderPath& out, bool allowDot, const Matrix* transform) { this->transform = transform; this->applyTransform = (transform && !tvg::identity(transform)); int32_t idx = 0; auto offset = dash.offset; auto gap = false; if (!tvg::zero(dash.offset)) { auto length = (dash.count % 2) ? dash.length * 2 : dash.length; offset = fmodf(offset, length); if (offset < 0) offset += length; for (uint32_t i = 0; i < dash.count * (dash.count % 2 + 1); ++i, ++idx) { auto curPattern = dash.pattern[i % dash.count]; if (offset < curPattern) break; offset -= curPattern; gap = !gap; } idx = idx % dash.count; } auto pts = in.pts.data; Point start{}; ARRAY_FOREACH(cmd, in.cmds) { switch (*cmd) { case PathCommand::Close: { lineTo(out, start, allowDot); break; } case PathCommand::MoveTo: { // reset the dash state curIdx = idx; curLen = dash.pattern[idx] - offset; opGap = gap; move = true; start = curPos = *pts; pts++; break; } case PathCommand::LineTo: { lineTo(out, *pts, allowDot); pts++; break; } case PathCommand::CubicTo: { cubicTo(out, pts[0], pts[1], pts[2], allowDot); pts += 3; break; } default: break; } } return true; } void StrokeDashPath::point(RenderPath& out, const Point& p) { if (move || dash.pattern[curIdx] < FLOAT_EPSILON) { out.moveTo(map(p)); move = false; } out.lineTo(map(p)); } void StrokeDashPath::lineTo(RenderPath& out, const Point& to, bool allowDot) { Line line = {curPos, to}; auto len = length(to - curPos); segment(line, len, out, allowDot, [](const Line& l) { return length(l.pt2 - l.pt1); }, [](const Line& l, float len, Line& left, Line& right) { l.split(len, left, right); }, [&](const Line& l) { out.lineTo(map(l.pt2)); }, [](const Line& l) { return l.pt1; }, to ); } void StrokeDashPath::cubicTo(RenderPath& out, const Point& cnt1, const Point& cnt2, const Point& end, bool allowDot) { Bezier curve = {curPos, cnt1, cnt2, end}; auto len = curve.length(); segment(curve, len, out, allowDot, [](const Bezier& b) { return b.length(); }, [](const Bezier& b, float len, Bezier& left, Bezier& right) { b.split(len, left, right); }, [&](const Bezier& b) { out.cubicTo(map(b.ctrl1), map(b.ctrl2), map(b.end)); }, [](const Bezier& b) { return b.start; }, end ); } bool RenderShape::strokeDash(RenderPath& out, const Matrix* transform) const { if (!stroke || stroke->dash.count == 0 || stroke->dash.length < DASH_PATTERN_THRESHOLD) return false; out.cmds.reserve(20 * path.cmds.count); out.pts.reserve(20 * path.pts.count); StrokeDashPath dash(stroke->dash); auto allowDot = stroke->cap != StrokeCap::Butt; if (trimpath()) { RenderPath tpath; if (stroke->trim.trim(path, tpath)) return dash.gen(tpath, out, allowDot, transform); else return false; } return dash.gen(path, out, allowDot, transform); } #else bool RenderShape::strokeDash(RenderPath& out, const Matrix* transform) const { return false; } #endif mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/000775 001750 001750 00000000000 15164251010 030174 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/savers/gif/tvgGifSaver.h000664 001750 001750 00000003446 15164251010 034463 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GIFSAVER_H_ #define _TVG_GIFSAVER_H_ #include "tvgSaveModule.h" #include "tvgTaskScheduler.h" namespace tvg { class GifSaver : public SaveModule, public Task { private: uint32_t* buffer = nullptr; Animation* animation = nullptr; Paint* bg = nullptr; char *path = nullptr; float vsize[2] = {0.0f, 0.0f}; float fps = 0.0f; void run(unsigned tid) override; public: ~GifSaver(); bool save(Paint* paint, Paint* bg, const char* filename, uint32_t quality) override; bool save(Animation* animation, Paint* bg, const char* filename, uint32_t quality, uint32_t fps) override; bool close() override; }; } #endif //_TVG_GIFSAVER_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgCompositor.cpp000664 001750 001750 00000144127 15164251010 037367 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgCompositor.h" #include "tvgWgShaderTypes.h" #include void WgCompositor::initialize(WgContext& context, uint32_t width, uint32_t height) { // pipelines (external handle, do not release) pipelines.initialize(context); stageBufferGeometry.initialize(context); // initialize opacity pool initPools(context); // allocate global view matrix handles WgShaderTypeMat4x4f viewMat(width, height); context.allocateBufferUniform(bufferViewMat, &viewMat, sizeof(viewMat)); bindGroupViewMat = context.layouts.createBindGroupBuffer1Un(bufferViewMat); // create render targets handles resize(context, width, height); // composition and blend geometries meshDataBlit.blitBox(); // force stage buffers initialization flush(context); } void WgCompositor::initPools(WgContext& context) { for (uint32_t i = 0; i < 256; i++) { float opacity = i / 255.0f; context.allocateBufferUniform(bufferOpacities[i], &opacity, sizeof(float)); bindGroupOpacities[i] = context.layouts.createBindGroupBuffer1Un(bufferOpacities[i]); } } void WgCompositor::release(WgContext& context) { // release render targets habdles resize(context, 0, 0); // release opacity pool releasePools(context); // release global view matrix handles context.layouts.releaseBindGroup(bindGroupViewMat); context.releaseBuffer(bufferViewMat); // release stage buffer stageBufferPaint.release(context); stageBufferGeometry.release(context); // release pipelines pipelines.release(context); } void WgCompositor::releasePools(WgContext& context) { // release opacity pool for (uint32_t i = 0; i < 256; i++) { context.layouts.releaseBindGroup(bindGroupOpacities[i]); context.releaseBuffer(bufferOpacities[i]); } } void WgCompositor::resize(WgContext& context, uint32_t width, uint32_t height) { // release existig handles if ((this->width != width) || (this->height != height)) { context.layouts.releaseBindGroup(bindGroupStorageTemp); // release intermediate render target targetTemp1.release(context); targetTemp0.release(context); // release global stencil buffer handles context.releaseTextureView(texViewDepthStencilMS); context.releaseTexture(texDepthStencilMS); context.releaseTextureView(texViewDepthStencil); context.releaseTexture(texDepthStencil); // store render target dimensions this->height = height; this->width = width; } // create render targets handles if ((width != 0) && (height != 0)) { // store render target dimensions this->width = width; this->height = height; // reallocate global view matrix handles WgShaderTypeMat4x4f viewMat(width, height); context.allocateBufferUniform(bufferViewMat, &viewMat, sizeof(viewMat)); // allocate global stencil buffer handles texDepthStencil = context.createTexAttachement(width, height, WGPUTextureFormat_Depth24PlusStencil8, 1); texViewDepthStencil = context.createTextureView(texDepthStencil); texDepthStencilMS = context.createTexAttachement(width, height, WGPUTextureFormat_Depth24PlusStencil8, 4); texViewDepthStencilMS = context.createTextureView(texDepthStencilMS); // initialize intermediate render targets targetTemp0.initialize(context, width, height); targetTemp1.initialize(context, width, height); bindGroupStorageTemp = context.layouts.createBindGroupStrorage2RO(targetTemp0.texView, targetTemp1.texView); } } RenderRegion WgCompositor::shrinkRenderRegion(const RenderRegion& rect) { return { {std::max(0, std::min((int32_t)width, rect.min.x)), std::max(0, std::min((int32_t)height, rect.min.y))}, {std::max(0, std::min((int32_t)width, rect.max.x)), std::max(0, std::min((int32_t)height, rect.max.y))} }; } void WgCompositor::copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src) { const RenderRegion region = {{0, 0}, {(int32_t)src->width, (int32_t)src->height}}; copyTexture(dst, src, region); } void WgCompositor::copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src, const RenderRegion& region) { assert(dst); assert(src); assert(commandEncoder); const WGPUTexelCopyTextureInfo texSrc { .texture = src->texture, .origin = { .x = (uint32_t)region.min.x, .y = (uint32_t)region.min.y } }; const WGPUTexelCopyTextureInfo texDst { .texture = dst->texture, .origin = { .x = (uint32_t)region.min.x, .y = (uint32_t)region.min.y } }; const WGPUExtent3D copySize { .width = region.w(), .height = region.h(), .depthOrArrayLayers = 1 }; wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &texSrc, &texDst, ©Size); } void WgCompositor::beginRenderPassMS(WGPUCommandEncoder commandEncoder, WgRenderTarget* target, bool clear, WGPUColor clearColor) { assert(target); assert(commandEncoder); // do not start same render bass if (target == currentTarget) return; // we must to end render pass first endRenderPass(); this->currentTarget = target; // start new render pass this->commandEncoder = commandEncoder; const WGPURenderPassDepthStencilAttachment depthStencilAttachment{ .view = texViewDepthStencilMS, .depthLoadOp = WGPULoadOp_Clear, .depthStoreOp = WGPUStoreOp_Discard, .depthClearValue = 1.0f, .stencilLoadOp = WGPULoadOp_Clear, .stencilStoreOp = WGPUStoreOp_Discard, .stencilClearValue = 0 }; const WGPURenderPassColorAttachment colorAttachment{ .view = target->texViewMS, .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, .resolveTarget = target->texView, .loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store, .clearValue = clearColor }; WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment }; renderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc); assert(renderPassEncoder); } void WgCompositor::beginRenderPass(WGPUCommandEncoder encoder, WgRenderTarget* target) { assert(!renderPassEncoder); currentTarget = target; const WGPURenderPassDepthStencilAttachment depthStencilAttachment{ .view = texViewDepthStencil, .depthLoadOp = WGPULoadOp_Load, .depthStoreOp = WGPUStoreOp_Discard, .stencilLoadOp = WGPULoadOp_Load, .stencilStoreOp = WGPUStoreOp_Discard }; const WGPURenderPassColorAttachment colorAttachment { .view = target->texView, .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, .loadOp = WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store, }; const WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment }; renderPassEncoder = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc); } void WgCompositor::endRenderPass() { if (currentTarget) { assert(renderPassEncoder); wgpuRenderPassEncoderEnd(renderPassEncoder); wgpuRenderPassEncoderRelease(renderPassEncoder); renderPassEncoder = nullptr; currentTarget = nullptr; } } void WgCompositor::reset(WgContext& context) { stageBufferGeometry.clear(); stageBufferPaint.clear(); } void WgCompositor::flush(WgContext& context) { stageBufferGeometry.append(&meshDataBlit); stageBufferGeometry.flush(context); stageBufferPaint.flush(context); context.submit(); } void WgCompositor::requestShape(WgRenderDataShape* renderData) { stageBufferGeometry.append(renderData); renderData->renderSettingsShape.bindGroupInd = stageBufferPaint.append(renderData->renderSettingsShape.settings); renderData->renderSettingsStroke.bindGroupInd = stageBufferPaint.append(renderData->renderSettingsStroke.settings); ARRAY_FOREACH(p, renderData->clips) requestShape((WgRenderDataShape* )(*p)); } void WgCompositor::requestImage(WgRenderDataPicture* renderData) { stageBufferGeometry.append(renderData); renderData->renderSettings.bindGroupInd = stageBufferPaint.append(renderData->renderSettings.settings); ARRAY_FOREACH(p, renderData->clips) requestShape((WgRenderDataShape* )(*p)); } void WgCompositor::renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod) { assert(renderData); assert(renderPassEncoder); // apply clip path if necessary if (!renderData->clips.empty()) { renderClipPath(context, renderData); if (renderData->strokeFirst) { clipStrokes(context, renderData); clipShape(context, renderData); } else { clipShape(context, renderData); clipStrokes(context, renderData); } clearClipPath(context, renderData); // use custom blending } else if (blendMethod != BlendMethod::Normal) { if (renderData->strokeFirst) { blendStrokes(context, renderData, blendMethod); blendShape(context, renderData, blendMethod); } else { blendShape(context, renderData, blendMethod); blendStrokes(context, renderData, blendMethod); } // use direct hardware blending } else { if (renderData->strokeFirst) { drawStrokes(context, renderData); drawShape(context, renderData); } else { drawShape(context, renderData); drawStrokes(context, renderData); } } } void WgCompositor::renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod) { assert(renderData); assert(renderPassEncoder); // apply clip path if necessary if (renderData->clips.count != 0) { renderClipPath(context, renderData); clipImage(context, renderData); clearClipPath(context, renderData); // use custom blending } else if (blendMethod != BlendMethod::Normal) blendImage(context, renderData, blendMethod); // use direct hardware blending else drawImage(context, renderData); } void WgCompositor::renderScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose) { assert(scene); assert(compose); assert(renderPassEncoder); // use custom blending if (compose->blend != BlendMethod::Normal) blendScene(context, scene, compose); // use direct hardware blending else drawScene(context, scene, compose); } void WgCompositor::composeScene(WgContext& context, WgRenderTarget* src, WgRenderTarget* mask, WgCompose* cmp) { assert(cmp); assert(src); assert(mask); assert(renderPassEncoder); RenderRegion rect = shrinkRenderRegion(cmp->aabb); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x(), rect.y(), rect.w(), rect.h()); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, mask->bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene_compose[(uint32_t)cmp->method]); drawMeshImage(context, &meshDataBlit); } void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderTarget* src, WGPUTextureView dstView) { assert(!renderPassEncoder); const WGPURenderPassDepthStencilAttachment depthStencilAttachment{ .view = texViewDepthStencil, .depthLoadOp = WGPULoadOp_Load, .depthStoreOp = WGPUStoreOp_Discard, .stencilLoadOp = WGPULoadOp_Load, .stencilStoreOp = WGPUStoreOp_Discard }; const WGPURenderPassColorAttachment colorAttachment { .view = dstView, .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, .loadOp = WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store, }; const WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment }; renderPassEncoder = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.blit); drawMeshImage(context, &meshDataBlit); wgpuRenderPassEncoderEnd(renderPassEncoder); wgpuRenderPassEncoderRelease(renderPassEncoder); renderPassEncoder = nullptr; } void WgCompositor::drawMesh(WgContext& context, WgMeshData* meshData) { assert(meshData); assert(renderPassEncoder); uint64_t icount = meshData->ibuffer.count; uint64_t vsize = meshData->vbuffer.count * sizeof(Point); uint64_t isize = icount * sizeof(uint32_t); wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBufferGeometry.vbuffer_gpu, meshData->voffset, vsize); wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, stageBufferGeometry.ibuffer_gpu, WGPUIndexFormat_Uint32, meshData->ioffset, isize); wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0); }; void WgCompositor::drawMeshImage(WgContext& context, WgMeshData* meshData) { assert(meshData); assert(renderPassEncoder); uint64_t icount = meshData->ibuffer.count; uint64_t vsize = meshData->vbuffer.count * sizeof(Point); uint64_t isize = icount * sizeof(uint32_t); wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBufferGeometry.vbuffer_gpu, meshData->voffset, vsize); wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, stageBufferGeometry.vbuffer_gpu, meshData->toffset, vsize); wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, stageBufferGeometry.ibuffer_gpu, WGPUIndexFormat_Uint32, meshData->ioffset, isize); wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0); }; void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData) { assert(renderData); assert(renderPassEncoder); if (renderData->renderSettingsShape.skip || renderData->meshShape.vbuffer.count == 0 || renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettingsShape; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // setup stencil rules if (!renderData->convex) { WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd; wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline); // draw to stencil (first pass) drawMesh(context, &renderData->meshShape); } // setup fill rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); if (settings.fillType == WgRenderSettingsType::Solid) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, renderData->convex ? pipelines.solid_conv : pipelines.solid); } else if (settings.fillType == WgRenderSettingsType::Linear) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, renderData->convex ? pipelines.linear_conv : pipelines.linear); } else if (settings.fillType == WgRenderSettingsType::Radial) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, renderData->convex ? pipelines.radial_conv : pipelines.radial); } // draw to color (second pass) drawMesh(context, renderData->convex ? &renderData->meshShape : &renderData->meshBBox); } void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod) { assert(renderData); assert(renderPassEncoder); if (renderData->renderSettingsShape.skip || renderData->meshShape.vbuffer.count == 0 || renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettingsShape; // copy current render target data to dst target WgRenderTarget *target = currentTarget; endRenderPass(); copyTexture(&targetTemp0, target); beginRenderPassMS(commandEncoder, target, false); // render shape with blend settings wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // setup stencil rules WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd; wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline); // draw to stencil (first pass) drawMesh(context, &renderData->meshShape); // setup fill rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexture, 0, nullptr); uint32_t blendMethodInd = (uint32_t)blendMethod; if (settings.fillType == WgRenderSettingsType::Solid) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid_blend[blendMethodInd]); } else if (settings.fillType == WgRenderSettingsType::Linear) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear_blend[blendMethodInd]); } else if (settings.fillType == WgRenderSettingsType::Radial) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]); } // draw to color (second pass) drawMesh(context, &renderData->meshBBox); } void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData) { assert(renderData); assert(renderPassEncoder); if (renderData->renderSettingsShape.skip || renderData->meshShape.vbuffer.count == 0 || renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettingsShape; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // setup stencil rules WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd; wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline); // draw to stencil (first pass) drawMesh(context, &renderData->meshShape); // merge depth and stencil buffer wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil); drawMesh(context, &renderData->meshBBox); // setup fill rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); if (settings.fillType == WgRenderSettingsType::Solid) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid); } else if (settings.fillType == WgRenderSettingsType::Linear) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear); } else if (settings.fillType == WgRenderSettingsType::Radial) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial); } // draw to color (second pass) drawMesh(context, &renderData->meshBBox); } void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData) { assert(renderData); assert(renderPassEncoder); if (renderData->renderSettingsStroke.skip || renderData->meshStrokes.vbuffer.count == 0 || renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettingsStroke; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // draw strokes to stencil (first pass) // setup stencil rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); // draw to stencil (first pass) drawMesh(context, &renderData->meshStrokes); // setup fill rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); if (settings.fillType == WgRenderSettingsType::Solid) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid); } else if (settings.fillType == WgRenderSettingsType::Linear) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear); } else if (settings.fillType == WgRenderSettingsType::Radial) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial); } // draw to color (second pass) drawMesh(context, &renderData->meshStrokesBBox); } void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod) { assert(renderData); assert(renderPassEncoder); if (renderData->renderSettingsStroke.skip || renderData->meshStrokes.vbuffer.count == 0 || renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettingsStroke; // copy current render target data to dst target WgRenderTarget *target = currentTarget; endRenderPass(); copyTexture(&targetTemp0, target); beginRenderPassMS(commandEncoder, target, false); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // draw strokes to stencil (first pass) // setup stencil rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); // draw to stencil (first pass) drawMesh(context, &renderData->meshStrokes); // setup fill rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexture, 0, nullptr); uint32_t blendMethodInd = (uint32_t)blendMethod; if (settings.fillType == WgRenderSettingsType::Solid) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid_blend[blendMethodInd]); } else if (settings.fillType == WgRenderSettingsType::Linear) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear_blend[blendMethodInd]); } else if (settings.fillType == WgRenderSettingsType::Radial) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]); } // draw to color (second pass) drawMesh(context, &renderData->meshStrokesBBox); }; void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData) { assert(renderData); assert(renderPassEncoder); if (renderData->renderSettingsStroke.skip || renderData->meshStrokes.vbuffer.count == 0 || renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettingsStroke; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // draw strokes to stencil (first pass) // setup stencil rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); // draw to stencil (first pass) drawMesh(context, &renderData->meshStrokes); // merge depth and stencil buffer wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil); drawMesh(context, &renderData->meshBBox); // setup fill rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); if (settings.fillType == WgRenderSettingsType::Solid) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid); } else if (settings.fillType == WgRenderSettingsType::Linear) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear); } else if (settings.fillType == WgRenderSettingsType::Radial) { wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial); } // draw to color (second pass) drawMesh(context, &renderData->meshStrokesBBox); } void WgCompositor::drawImage(WgContext& context, WgRenderDataPicture* renderData) { assert(renderData); assert(renderPassEncoder); if (renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettings; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // draw stencil wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); drawMeshImage(context, &renderData->meshData); // draw image wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->imageData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image); drawMeshImage(context, &renderData->meshData); } void WgCompositor::blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod) { assert(renderData); assert(renderPassEncoder); if (renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettings; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // copy current render target data to dst target WgRenderTarget *target = currentTarget; endRenderPass(); copyTexture(&targetTemp0, target); beginRenderPassMS(commandEncoder, target, false); // setup stencil rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); drawMeshImage(context, &renderData->meshData); // blend image uint32_t blendMethodInd = (uint32_t)blendMethod; wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->imageData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image_blend[blendMethodInd]); drawMeshImage(context, &renderData->meshData); }; void WgCompositor::clipImage(WgContext& context, WgRenderDataPicture* renderData) { assert(renderData); assert(renderPassEncoder); if (renderData->viewport.invalid()) return; WgRenderSettings& settings = renderData->renderSettings; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); // setup stencil rules wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); drawMeshImage(context, &renderData->meshData); // merge depth and stencil buffer wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil); drawMeshImage(context, &renderData->meshData); // draw image wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->imageData.bindGroup, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image); drawMeshImage(context, &renderData->meshData); } void WgCompositor::drawScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose) { assert(scene); assert(compose); assert(currentTarget); // draw scene RenderRegion rect = shrinkRenderRegion(compose->aabb); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x(), rect.y(), rect.w(), rect.h()); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, bindGroupOpacities[compose->opacity], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene); drawMeshImage(context, &meshDataBlit); } void WgCompositor::blendScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose) { assert(scene); assert(compose); assert(currentTarget); // copy current render target data to dst target WgRenderTarget *target = currentTarget; endRenderPass(); copyTexture(&targetTemp0, target); beginRenderPassMS(commandEncoder, target, false); // blend scene uint32_t blendMethodInd = (uint32_t)compose->blend; RenderRegion rect = shrinkRenderRegion(compose->aabb); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x(), rect.y(), rect.w(), rect.h()); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[compose->opacity], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene_blend[blendMethodInd]); drawMeshImage(context, &meshDataBlit); } void WgCompositor::markupClipPath(WgContext& context, WgRenderDataShape* renderData) { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); // markup stencil if (renderData->meshStrokes.vbuffer.count > 0) { WgRenderSettings& settings = renderData->renderSettingsStroke; wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); drawMesh(context, &renderData->meshStrokes); } else if (renderData->meshShape.vbuffer.count > 0) { WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd; WgRenderSettings& settings = renderData->renderSettingsShape; wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline); drawMesh(context, &renderData->meshShape); } } void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* paint) { assert(paint); assert(renderPassEncoder); assert(paint->clips.count > 0); // reset scissor recr to full screen wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, 0, 0, width, height); // get render data WgRenderDataShape* renderData0 = (WgRenderDataShape*)paint->clips[0]; WgRenderSettings& settings0 = renderData0->renderSettingsShape; // markup stencil markupClipPath(context, renderData0); // copy stencil to depth wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings0.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth); drawMesh(context, &renderData0->meshBBox); // merge clip paths with AND logic for (auto p = paint->clips.begin() + 1; p < paint->clips.end(); ++p) { // get render data WgRenderDataShape* renderData = (WgRenderDataShape*)(*p); WgRenderSettings& settings = renderData->renderSettingsShape; // markup stencil markupClipPath(context, renderData); // copy stencil to depth (clear stencil) wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth_interm); drawMesh(context, &renderData->meshBBox); // copy depth to stencil wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 1); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_depth_to_stencil); drawMesh(context, &renderData->meshBBox); // clear depth current (keep stencil) wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth); drawMesh(context, &renderData->meshBBox); // clear depth original (keep stencil) wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings0.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth); drawMesh(context, &renderData0->meshBBox); // copy stencil to depth (clear stencil) wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth); drawMesh(context, &renderData->meshBBox); } } void WgCompositor::clearClipPath(WgContext& context, WgRenderDataPaint* paint) { assert(paint); assert(renderPassEncoder); assert(paint->clips.count > 0); // reset scissor recr to full screen wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, 0, 0, width, height); // get render data ARRAY_FOREACH(p, paint->clips) { WgRenderDataShape* renderData = (WgRenderDataShape*)(*p); WgRenderSettings& settings = renderData->renderSettingsShape; // set transformations wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth); drawMesh(context, &renderData->meshBBox); } } bool WgCompositor::gaussianBlur(WgContext& context, WgRenderTarget* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose) { assert(dst); assert(params); assert(params->rd); assert(!renderPassEncoder); auto renderDataParams = (WgRenderDataEffectParams*)params->rd; auto aabb = shrinkRenderRegion(compose->aabb); copyTexture(&targetTemp0, dst); if (params->direction == 0) { // both beginRenderPass(commandEncoder, &targetTemp0); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, dst->bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.gaussian_horz); drawMeshImage(context, &meshDataBlit); } endRenderPass(); beginRenderPass(commandEncoder, dst); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.gaussian_vert); drawMeshImage(context, &meshDataBlit); } endRenderPass(); } else if (params->direction == 1) { // horizontal beginRenderPass(commandEncoder, dst); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.gaussian_horz); drawMeshImage(context, &meshDataBlit); } endRenderPass(); } else if (params->direction == 2) { // vertical beginRenderPass(commandEncoder, dst); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.gaussian_vert); drawMeshImage(context, &meshDataBlit); } endRenderPass(); } return true; } bool WgCompositor::dropShadow(WgContext& context, WgRenderTarget* dst, const RenderEffectDropShadow* params, const WgCompose* compose) { assert(dst); assert(params); assert(params->rd); assert(!renderPassEncoder); auto renderDataParams = (WgRenderDataEffectParams*)params->rd; auto aabb = shrinkRenderRegion(compose->aabb); copyTexture(&targetTemp0, dst); copyTexture(&targetTemp1, dst); if (!tvg::zero(params->sigma)) { // horizontal beginRenderPass(commandEncoder, &targetTemp0); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, dst->bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.gaussian_horz); drawMeshImage(context, &meshDataBlit); } endRenderPass(); // vertical beginRenderPass(commandEncoder, &targetTemp1); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.gaussian_vert); drawMeshImage(context, &meshDataBlit); } endRenderPass(); } // drop shadow copyTexture(&targetTemp0, dst, aabb); beginRenderPass(commandEncoder, dst); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, targetTemp1.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.dropshadow); drawMeshImage(context, &meshDataBlit); } endRenderPass(); return true; } bool WgCompositor::fillEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectFill* params, const WgCompose* compose) { assert(dst); assert(params); assert(params->rd); assert(!renderPassEncoder); auto renderDataParams = (WgRenderDataEffectParams*)params->rd; auto aabb = shrinkRenderRegion(compose->aabb); copyTexture(&targetTemp0, dst, aabb); beginRenderPass(commandEncoder, dst); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.fill_effect); drawMeshImage(context, &meshDataBlit); } endRenderPass(); return true; } bool WgCompositor::tintEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTint* params, const WgCompose* compose) { assert(dst); assert(params); assert(params->rd); assert(!renderPassEncoder); auto renderDataParams = (WgRenderDataEffectParams*)params->rd; auto aabb = shrinkRenderRegion(compose->aabb); copyTexture(&targetTemp0, dst, aabb); beginRenderPass(commandEncoder, dst); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.tint_effect); drawMeshImage(context, &meshDataBlit); } endRenderPass(); return true; } bool WgCompositor::tritoneEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTritone* params, const WgCompose* compose) { assert(dst); assert(params); assert(params->rd); assert(!renderPassEncoder); auto renderDataParams = (WgRenderDataEffectParams*)params->rd; auto aabb = shrinkRenderRegion(compose->aabb); copyTexture(&targetTemp0, dst, aabb); beginRenderPass(commandEncoder, dst); { wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, aabb.x(), aabb.y(), aabb.w(), aabb.h()); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, targetTemp0.bindGroupTexture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderDataParams->bindGroupParams, 0, nullptr); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.tritone_effect); drawMeshImage(context, &meshDataBlit); } endRenderPass(); return true; } src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/build_android.yml000664 001750 001750 00000003743 15164251010 036064 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0name: Android on: pull_request: branches: - main push: branches: - main permissions: contents: read jobs: build_x86_64: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: nttld/setup-ndk@v1.3.1 id: setup-ndk with: submodules: true ndk-version: r21e local-cache: true - name: Install Packages run: | sudo apt-get update sudo apt-get install meson ninja-build libgles-dev - name: Build env: NDK: ${{ steps.setup-ndk.outputs.ndk-path }} API: 21 run: | sed -e "s|NDK|$NDK|g" -e "s|HOST_TAG|linux-x86_64|g" -e "s|API|$API|g" ./cross/android_x86_64.txt > /tmp/android_cross.txt meson setup build -Dlog=true -Dengines="sw, gl" -Dloaders=all -Dsavers=all -Dbindings=capi -Dstatic=true -Dthreads=false -Dextra="lottie_exp" --cross-file /tmp/android_cross.txt sudo ninja -C build install - uses: actions/upload-artifact@v4 with: name: result_x86_64 path: build/src/libthorvg* build_aarch64: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: nttld/setup-ndk@v1.3.1 id: setup-ndk with: submodules: true ndk-version: r21e local-cache: true - name: Install Packages run: | sudo apt-get update sudo apt-get install meson ninja-build libgles-dev - name: Build env: NDK: ${{ steps.setup-ndk.outputs.ndk-path }} API: 21 run: | sed -e "s|NDK|$NDK|g" -e "s|HOST_TAG|linux-x86_64|g" -e "s|API|$API|g" ./cross/android_aarch64.txt > /tmp/android_cross.txt meson setup build -Dlog=true -Dengines="sw, gl" -Dloaders=all -Dsavers=all -Dbindings=capi -Dstatic=true -Dthreads=false -Dextra="lottie_exp" --cross-file /tmp/android_cross.txt sudo ninja -C build install - uses: actions/upload-artifact@v4 with: name: result_aarch64 path: build/src/libthorvg* glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/include/000775 001750 001750 00000000000 15164251010 040742 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modulesmlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/ttf/meson.build000664 001750 001750 00000000341 15164251010 034372 0ustar00ddennedyddennedy000000 000000 source_file = [ 'tvgTtfLoader.h', 'tvgTtfReader.h', 'tvgTtfLoader.cpp', 'tvgTtfReader.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-typeerror.cpp000664 001750 001750 00000005325 15164251010 050434 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-typeerror.inc.h" #define BUILTIN_UNDERSCORED_ID type_error #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup typeerror ECMA TypeError object built-in * @{ */ /** * Handle calling [[Call]] of built-in TypeError object * * @return ecma value */ ecma_value_t ecma_builtin_type_error_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_helper_error_dispatch_call (JERRY_ERROR_TYPE, arguments_list_p, arguments_list_len); } /* ecma_builtin_type_error_dispatch_call */ /** * Handle calling [[Construct]] of built-in TypeError object * * @return ecma value */ ecma_value_t ecma_builtin_type_error_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_TYPE_ERROR_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_type_error_dispatch_call (arguments_list_p, arguments_list_len); if (!ECMA_IS_VALUE_ERROR (result)) { ecma_object_t *object_p = ecma_get_object_from_value (result); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, proto_p); } ecma_deref_object (proto_p); return result; } /* ecma_builtin_type_error_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ERRORS */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp000664 001750 001750 00000063076 15164251010 036533 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgSwCommon.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #define SW_STROKE_TAG_POINT 1 #define SW_STROKE_TAG_CUBIC 2 #define SW_STROKE_TAG_BEGIN 4 #define SW_STROKE_TAG_END 8 static inline int64_t SIDE_TO_ROTATE(const int32_t s) { return (SW_ANGLE_PI2 - static_cast(s) * SW_ANGLE_PI); } static inline void SCALE(const SwStroke& stroke, SwPoint& pt) { pt.x = static_cast(pt.x * stroke.sx); pt.y = static_cast(pt.y * stroke.sy); } static void _growBorder(SwStrokeBorder* border, uint32_t newPts) { if (border->pts.count + newPts <= border->pts.reserved) return; border->pts.grow(newPts * 20); border->tags = tvg::realloc(border->tags, border->pts.reserved); //align the pts / tags memory size } static void _borderClose(SwStrokeBorder* border, bool reverse) { auto start = border->start; auto count = border->pts.count; //Don't record empty paths! if (count <= start + 1U) { border->pts.count = start; } else { /* Copy the last point to the start of this sub-path, since it contains the adjusted starting coordinates */ border->pts.count = --count; border->pts[start] = border->pts[count]; if (reverse) { //reverse the points auto pt1 = border->pts.data + start + 1; auto pt2 = border->pts.data + count - 1; while (pt1 < pt2) { std::swap(*pt1, *pt2); ++pt1; --pt2; } //reverse the tags auto tag1 = border->tags + start + 1; auto tag2 = border->tags + count - 1; while (tag1 < tag2) { std::swap(*tag1, *tag2); ++tag1; --tag2; } } border->tags[start] |= SW_STROKE_TAG_BEGIN; border->tags[count - 1] |= SW_STROKE_TAG_END; } border->start = -1; border->movable = false; } static void _borderCubicTo(SwStrokeBorder* border, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) { _growBorder(border, 3); auto tag = border->tags + border->pts.count; border->pts.push(ctrl1); border->pts.push(ctrl2); border->pts.push(to); tag[0] = SW_STROKE_TAG_CUBIC; tag[1] = SW_STROKE_TAG_CUBIC; tag[2] = SW_STROKE_TAG_POINT; border->movable = false; } static void _borderArcTo(SwStrokeBorder* border, const SwPoint& center, int64_t radius, int64_t angleStart, int64_t angleDiff, SwStroke& stroke) { constexpr int64_t ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2; SwPoint a = {static_cast(radius), 0}; mathRotate(a, angleStart); SCALE(stroke, a); a += center; auto total = angleDiff; auto angle = angleStart; auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2; while (total != 0) { auto step = total; if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE; else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE; auto next = angle + step; auto theta = step; if (theta < 0) theta = -theta; theta >>= 1; //compute end point SwPoint b = {static_cast(radius), 0}; mathRotate(b, next); SCALE(stroke, b); b += center; //compute first and second control points auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3); SwPoint a2 = {static_cast(length), 0}; mathRotate(a2, angle + rotate); SCALE(stroke, a2); a2 += a; SwPoint b2 = {static_cast(length), 0}; mathRotate(b2, next - rotate); SCALE(stroke, b2); b2 += b; //add cubic arc _borderCubicTo(border, a2, b2, b); //process the rest of the arc? a = b; total -= step; angle = next; } } static void _borderLineTo(SwStrokeBorder* border, const SwPoint& to, bool movable) { if (border->movable) { //move last point border->pts.last() = to; } else { //don't add zero-length line_to if (!border->pts.empty() && (border->pts.last() - to).tiny()) return; _growBorder(border, 1); border->tags[border->pts.count] = SW_STROKE_TAG_POINT; border->pts.push(to); } border->movable = movable; } static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to) { //close current open path if any? if (border->start >= 0) _borderClose(border, false); border->start = border->pts.count; border->movable = false; _borderLineTo(border, to, false); } static void _arcTo(SwStroke& stroke, int32_t side) { auto border = stroke.borders[side]; auto rotate = SIDE_TO_ROTATE(side); auto total = mathDiff(stroke.angleIn, stroke.angleOut); if (total == SW_ANGLE_PI) total = -rotate * 2; _borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke); border->movable = false; } static void _outside(SwStroke& stroke, int32_t side, int64_t lineLength) { auto border = stroke.borders[side]; if (stroke.join == StrokeJoin::Round) { _arcTo(stroke, side); } else { //this is a mitered (pointed) or beveled (truncated) corner auto rotate = SIDE_TO_ROTATE(side); auto bevel = stroke.join == StrokeJoin::Bevel; int64_t phi = 0; int64_t thcos = 0; if (!bevel) { auto theta = mathDiff(stroke.angleIn, stroke.angleOut); if (theta == SW_ANGLE_PI) { theta = rotate; phi = stroke.angleIn; } else { theta /= 2; phi = stroke.angleIn + theta + rotate; } thcos = mathCos(theta); auto sigma = mathMultiply(stroke.miterlimit, thcos); //is miter limit exceeded? if (sigma < 0x10000L) bevel = true; } //this is a bevel (broken angle) if (bevel) { SwPoint delta = {static_cast(stroke.width), 0}; mathRotate(delta, stroke.angleOut + rotate); SCALE(stroke, delta); delta += stroke.center; border->movable = false; _borderLineTo(border, delta, false); //this is a miter (intersection) } else { auto length = mathDivide(stroke.width, thcos); SwPoint delta = {static_cast(length), 0}; mathRotate(delta, phi); SCALE(stroke, delta); delta += stroke.center; _borderLineTo(border, delta, false); /* Now add and end point Only needed if not lineto (lineLength is zero for curves) */ if (lineLength == 0) { delta = {static_cast(stroke.width), 0}; mathRotate(delta, stroke.angleOut + rotate); SCALE(stroke, delta); delta += stroke.center; _borderLineTo(border, delta, false); } } } } static void _inside(SwStroke& stroke, int32_t side, int64_t lineLength) { auto border = stroke.borders[side]; auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2; SwPoint delta; bool intersect = false; /* Only intersect borders if between two line_to's and both lines are long enough (line length is zero for curves). */ if (border->movable && lineLength > 0) { //compute minimum required length of lines int64_t minLength = abs(mathMultiply(stroke.width, mathTan(theta))); if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true; } auto rotate = SIDE_TO_ROTATE(side); if (!intersect) { delta = {static_cast(stroke.width), 0}; mathRotate(delta, stroke.angleOut + rotate); SCALE(stroke, delta); delta += stroke.center; border->movable = false; } else { //compute median angle auto phi = stroke.angleIn + theta; auto thcos = mathCos(theta); delta = {static_cast(mathDivide(stroke.width, thcos)), 0}; mathRotate(delta, phi + rotate); SCALE(stroke, delta); delta += stroke.center; } _borderLineTo(border, delta, false); } void _processCorner(SwStroke& stroke, int64_t lineLength) { auto turn = mathDiff(stroke.angleIn, stroke.angleOut); //no specific corner processing is required if the turn is 0 if (turn == 0) return; //when we turn to the right, the inside side is 0 int32_t inside = 0; //otherwise, the inside is 1 if (turn < 0) inside = 1; //process the inside _inside(stroke, inside, lineLength); //process the outside _outside(stroke, 1 - inside, lineLength); } void _firstSubPath(SwStroke& stroke, int64_t startAngle, int64_t lineLength) { SwPoint delta = {static_cast(stroke.width), 0}; mathRotate(delta, startAngle + SW_ANGLE_PI2); SCALE(stroke, delta); auto pt = stroke.center + delta; _borderMoveTo(stroke.borders[0], pt); pt = stroke.center - delta; _borderMoveTo(stroke.borders[1], pt); /* Save angle, position and line length for last join lineLength is zero for curves */ stroke.subPathAngle = startAngle; stroke.firstPt = false; stroke.subPathLineLength = lineLength; } static void _lineTo(SwStroke& stroke, const SwPoint& to) { auto delta = to - stroke.center; //a zero-length lineto is a no-op if (delta.zero()) { //round and square caps are expected to be drawn as a dot even for zero-length lines if (stroke.firstPt && stroke.cap != StrokeCap::Butt) _firstSubPath(stroke, 0, 0); return; } /* The lineLength is used to determine the intersection of strokes outlines. The scale needs to be reverted since the stroke width has not been scaled. An alternative option is to scale the width of the stroke properly by calculating the mixture of the sx/sy rating on the stroke direction. */ delta.x = static_cast(delta.x / stroke.sx); delta.y = static_cast(delta.y / stroke.sy); auto lineLength = mathLength(delta); auto angle = mathAtan(delta); delta = {static_cast(stroke.width), 0}; mathRotate(delta, angle + SW_ANGLE_PI2); SCALE(stroke, delta); //process corner if necessary if (stroke.firstPt) { /* This is the first segment of a subpath. We need to add a point to each border at their respective starting point locations. */ _firstSubPath(stroke, angle, lineLength); } else { //process the current corner stroke.angleOut = angle; _processCorner(stroke, lineLength); } //now add a line segment to both the inside and outside paths for (int side = 0; side < 2; ++side) { //the ends of lineto borders are movable _borderLineTo(stroke.borders[side], to + delta, true); delta.x = -delta.x; delta.y = -delta.y; } stroke.angleIn = angle; stroke.center = to; stroke.lineLength = lineLength; } static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) { SwPoint bezStack[37]; //TODO: static? auto limit = bezStack + 32; auto arc = bezStack; auto firstArc = true; arc[0] = to; arc[1] = ctrl2; arc[2] = ctrl1; arc[3] = stroke.center; while (arc >= bezStack) { int64_t angleIn, angleOut, angleMid; //initialize with current direction angleIn = angleOut = angleMid = stroke.angleIn; auto valid = mathCubicAngle(arc, angleIn, angleMid, angleOut); //valid size if (valid > 0 && arc < limit) { if (stroke.firstPt) stroke.angleIn = angleIn; mathSplitCubic(arc); arc += 3; continue; } //ignoreable size if (valid < 0 && arc == bezStack) { stroke.center = to; //round and square caps are expected to be drawn as a dot even for zero-length lines if (stroke.firstPt && stroke.cap != StrokeCap::Butt) _firstSubPath(stroke, 0, 0); return; } //small size if (firstArc) { firstArc = false; //process corner if necessary if (stroke.firstPt) { _firstSubPath(stroke, angleIn, 0); } else { stroke.angleOut = angleIn; _processCorner(stroke, 0); } } else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8) / 4) { //if the deviation from one arc to the next is too great add a round corner stroke.center = arc[3]; stroke.angleOut = angleIn; stroke.join = StrokeJoin::Round; _processCorner(stroke, 0); //reinstate line join style stroke.join = stroke.joinSaved; } //the arc's angle is small enough; we can add it directly to each border auto theta1 = mathDiff(angleIn, angleMid) / 2; auto theta2 = mathDiff(angleMid, angleOut) / 2; auto phi1 = mathMean(angleIn, angleMid); auto phi2 = mathMean(angleMid, angleOut); auto length1 = mathDivide(stroke.width, mathCos(theta1)); auto length2 = mathDivide(stroke.width, mathCos(theta2)); int64_t alpha0 = 0; //compute direction of original arc if (stroke.handleWideStrokes) { alpha0 = mathAtan(arc[0] - arc[3]); } for (int side = 0; side < 2; ++side) { auto border = stroke.borders[side]; auto rotate = SIDE_TO_ROTATE(side); //compute control points SwPoint _ctrl1 = {static_cast(length1), 0}; mathRotate(_ctrl1, phi1 + rotate); SCALE(stroke, _ctrl1); _ctrl1 += arc[2]; SwPoint _ctrl2 = {static_cast(length2), 0}; mathRotate(_ctrl2, phi2 + rotate); SCALE(stroke, _ctrl2); _ctrl2 += arc[1]; //compute end point SwPoint end = {static_cast(stroke.width), 0}; mathRotate(end, angleOut + rotate); SCALE(stroke, end); end += arc[0]; if (stroke.handleWideStrokes) { /* determine whether the border radius is greater than the radius of curvature of the original arc */ auto start = border->pts.last(); auto alpha1 = mathAtan(end - start); //is the direction of the border arc opposite to that of the original arc? if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) { //use the sine rule to find the intersection point auto beta = mathAtan(arc[3] - start); auto gamma = mathAtan(arc[0] - end); auto bvec = end - start; auto blen = mathLength(bvec); auto sinA = abs(mathSin(alpha1 - gamma)); auto sinB = abs(mathSin(beta - gamma)); auto alen = mathMulDiv(blen, sinA, sinB); SwPoint delta = {static_cast(alen), 0}; mathRotate(delta, beta); delta += start; //circumnavigate the negative sector backwards border->movable = false; _borderLineTo(border, delta, false); _borderLineTo(border, end, false); _borderCubicTo(border, _ctrl2, _ctrl1, start); //and then move to the endpoint _borderLineTo(border, end, false); continue; } } _borderCubicTo(border, _ctrl1, _ctrl2, end); } arc -= 3; stroke.angleIn = angleOut; } stroke.center = to; } static void _addCap(SwStroke& stroke, int64_t angle, int32_t side) { if (stroke.cap == StrokeCap::Square) { auto rotate = SIDE_TO_ROTATE(side); auto border = stroke.borders[side]; SwPoint delta = {static_cast(stroke.width), 0}; mathRotate(delta, angle); SCALE(stroke, delta); SwPoint delta2 = {static_cast(stroke.width), 0}; mathRotate(delta2, angle + rotate); SCALE(stroke, delta2); delta += stroke.center + delta2; _borderLineTo(border, delta, false); delta = {static_cast(stroke.width), 0}; mathRotate(delta, angle); SCALE(stroke, delta); delta2 = {static_cast(stroke.width), 0}; mathRotate(delta2, angle - rotate); SCALE(stroke, delta2); delta += delta2 + stroke.center; _borderLineTo(border, delta, false); } else if (stroke.cap == StrokeCap::Round) { stroke.angleIn = angle; stroke.angleOut = angle + SW_ANGLE_PI; _arcTo(stroke, side); } else { //Butt auto rotate = SIDE_TO_ROTATE(side); auto border = stroke.borders[side]; SwPoint delta = {static_cast(stroke.width), 0}; mathRotate(delta, angle + rotate); SCALE(stroke, delta); delta += stroke.center; _borderLineTo(border, delta, false); delta = {static_cast(stroke.width), 0}; mathRotate(delta, angle - rotate); SCALE(stroke, delta); delta += stroke.center; _borderLineTo(border, delta, false); } } static void _addReverseLeft(SwStroke& stroke, bool opened) { auto right = stroke.borders[0]; auto left = stroke.borders[1]; auto newPts = left->pts.count - left->start; if (newPts <= 0) return; _growBorder(right, newPts); auto dstTag = right->tags + right->pts.count; auto srcPt = left->pts.end() - 1; auto srcTag = left->tags + left->pts.count - 1; while (srcPt >= left->pts.data + left->start) { right->pts.push(*srcPt); *dstTag = *srcTag; if (opened) { dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END); } else { //switch begin/end tags if necessary auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END); if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END) dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END); } --srcPt; --srcTag; ++dstTag; } left->pts.count = left->start; right->movable = false; left->movable = false; } static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed) { /* We cannot process the first point because there is not enough information regarding its corner/cap. Later, it will be processed in the _endSubPath() */ stroke.firstPt = true; stroke.center = to; stroke.closedSubPath = closed; /* 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. */ if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt)) stroke.handleWideStrokes = true; else stroke.handleWideStrokes = false; stroke.ptStartSubPath = to; stroke.angleIn = 0; } static void _endSubPath(SwStroke& stroke) { if (stroke.closedSubPath) { //close the path if needed if (stroke.center != stroke.ptStartSubPath) _lineTo(stroke, stroke.ptStartSubPath); //process the corner stroke.angleOut = stroke.subPathAngle; auto turn = mathDiff(stroke.angleIn, stroke.angleOut); //No specific corner processing is required if the turn is 0 if (turn != 0) { //when we turn to the right, the inside is 0 int32_t inside = 0; //otherwise, the inside is 1 if (turn < 0) inside = 1; _inside(stroke, inside, stroke.subPathLineLength); //inside _outside(stroke, 1 - inside, stroke.subPathLineLength); //outside } _borderClose(stroke.borders[0], false); _borderClose(stroke.borders[1], true); } else { auto right = stroke.borders[0]; /* 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 */ _addCap(stroke, stroke.angleIn, 0); //add reversed points from 'left' to 'right' _addReverseLeft(stroke, true); //now add the final cap stroke.center = stroke.ptStartSubPath; _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0); /* now end the right subpath accordingly. The left one is rewind and doesn't need further processing */ _borderClose(right, false); } } static void _exportBorderOutline(const SwStroke& stroke, SwOutline* outline, uint32_t side) { auto border = stroke.borders[side]; if (border->pts.empty()) return; auto src = border->tags; auto idx = outline->pts.count; ARRAY_FOREACH(pts, border->pts) { if (*src & SW_STROKE_TAG_POINT) outline->types.push(SW_CURVE_TYPE_POINT); else if (*src & SW_STROKE_TAG_CUBIC) outline->types.push(SW_CURVE_TYPE_CUBIC); if (*src & SW_STROKE_TAG_END) outline->cntrs.push(idx); ++src; ++idx; } outline->pts.push(border->pts); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ void strokeFree(SwStroke* stroke) { if (!stroke) return; fillFree(stroke->fill); stroke->fill = nullptr; tvg::free(stroke); } void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid) { stroke->sx = sqrtf(powf(transform.e11, 2.0f) + powf(transform.e21, 2.0f)); stroke->sy = sqrtf(powf(transform.e12, 2.0f) + powf(transform.e22, 2.0f)); stroke->width = HALF_STROKE(rshape->strokeWidth()); stroke->cap = rshape->strokeCap(); stroke->miterlimit = static_cast(rshape->strokeMiterlimit() * 65536.0f); //Save line join: it can be temporarily changed when stroking curves... stroke->joinSaved = stroke->join = rshape->strokeJoin(); stroke->borders[0] = mpoolReqStrokeLBorder(mpool, tid); stroke->borders[1] = mpoolReqStrokeRBorder(mpool, tid); } bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline, SwMpool* mpool, unsigned tid) { uint32_t first = 0; uint32_t i = 0; ARRAY_FOREACH(p, outline.cntrs) { auto last = *p; //index of last point in contour auto limit = outline.pts.data + last; ++i; //Skip empty points if (last <= first) { first = last + 1; continue; } auto start = outline.pts[first]; auto pt = outline.pts.data + first; auto types = outline.types.data + first; auto type = types[0]; //A contour cannot start with a cubic control point if (type == SW_CURVE_TYPE_CUBIC) return false; ++types; auto closed = outline.closed.data ? outline.closed.data[i - 1]: false; _beginSubPath(*stroke, start, closed); while (pt < limit) { //emit a single line_to if (types[0] == SW_CURVE_TYPE_POINT) { ++pt; ++types; _lineTo(*stroke, *pt); //types cubic } else { pt += 3; types += 3; if (pt <= limit) _cubicTo(*stroke, pt[-2], pt[-1], pt[0]); else if (pt - 1 == limit) _cubicTo(*stroke, pt[-2], pt[-1], start); else goto close; } } close: if (!stroke->firstPt) _endSubPath(*stroke); first = last + 1; } return true; } SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid) { auto reserve = stroke->borders[0]->pts.count + stroke->borders[1]->pts.count; auto outline = mpoolReqOutline(mpool, tid); outline->pts.reserve(reserve); outline->types.reserve(reserve); outline->fillRule = FillRule::NonZero; _exportBorderOutline(*stroke, outline, 0); //left _exportBorderOutline(*stroke, outline, 1); //right return outline; } thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array.cpp000664 001750 001750 00000034656 15164251010 047530 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #if JERRY_BUILTIN_ARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_ARRAY_ROUTINE_START = 0, ECMA_ARRAY_ROUTINE_IS_ARRAY, ECMA_ARRAY_ROUTINE_FROM, ECMA_ARRAY_ROUTINE_OF, ECMA_ARRAY_ROUTINE_SPECIES_GET }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-array.inc.h" #define BUILTIN_UNDERSCORED_ID array #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup array ECMA Array object built-in * @{ */ /** * The Array object's 'from' routine * * See also: * ECMA-262 v6, 22.1.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_object_from (ecma_value_t this_arg, /**< 'this' argument */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { /* 1. */ ecma_value_t constructor = this_arg; ecma_value_t call_this_arg = ECMA_VALUE_UNDEFINED; ecma_value_t items = arguments_list_p[0]; ecma_value_t mapfn = (arguments_list_len > 1) ? arguments_list_p[1] : ECMA_VALUE_UNDEFINED; ecma_length_t k = 0; ecma_value_t set_status; /* 2. */ ecma_object_t *mapfn_obj_p = NULL; /* 3. */ if (!ecma_is_value_undefined (mapfn)) { /* 3.a */ if (!ecma_op_is_callable (mapfn)) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } /* 3.b */ if (arguments_list_len > 2) { call_this_arg = arguments_list_p[2]; } /* 3.c */ mapfn_obj_p = ecma_get_object_from_value (mapfn); } /* 4. */ ecma_value_t using_iterator = ecma_op_get_method_by_symbol_id (items, LIT_GLOBAL_SYMBOL_ITERATOR); /* 5. */ if (ECMA_IS_VALUE_ERROR (using_iterator)) { return using_iterator; } ecma_value_t ret_value = ECMA_VALUE_ERROR; /* 6. */ if (!ecma_is_value_undefined (using_iterator)) { ecma_object_t *array_obj_p; /* 6.a */ if (ecma_is_constructor (constructor)) { ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor); ecma_value_t array = ecma_op_function_construct (constructor_obj_p, constructor_obj_p, NULL, 0); if (ecma_is_value_undefined (array) || ecma_is_value_null (array)) { ecma_free_value (using_iterator); return ecma_raise_type_error (ECMA_ERR_CANNOT_CONVERT_TO_OBJECT); } /* 6.c */ if (ECMA_IS_VALUE_ERROR (array)) { ecma_free_value (using_iterator); return array; } array_obj_p = ecma_get_object_from_value (array); } else { /* 6.b */ array_obj_p = ecma_op_new_array_object (0); } /* 6.d */ ecma_value_t next_method; ecma_value_t iterator = ecma_op_get_iterator (items, using_iterator, &next_method); ecma_free_value (using_iterator); /* 6.e */ if (ECMA_IS_VALUE_ERROR (iterator)) { ecma_deref_object (array_obj_p); return iterator; } /* 6.f */ uint32_t k = 0; /* 6.g */ while (true) { /* 6.g.ii */ ecma_value_t next = ecma_op_iterator_step (iterator, next_method); /* 6.g.iii */ if (ECMA_IS_VALUE_ERROR (next)) { goto iterator_cleanup; } /* 6.g.iii */ if (ecma_is_value_false (next)) { /* 6.g.iv.1 */ ecma_value_t len_value = ecma_make_uint32_value (k); ecma_value_t set_status = ecma_op_object_put (array_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), len_value, true); ecma_free_value (len_value); /* 6.g.iv.2 */ if (ECMA_IS_VALUE_ERROR (set_status)) { goto iterator_cleanup; } ecma_free_value (iterator); ecma_free_value (next_method); /* 6.g.iv.3 */ return ecma_make_object_value (array_obj_p); } /* 6.g.v */ ecma_value_t next_value = ecma_op_iterator_value (next); ecma_free_value (next); /* 6.g.vi */ if (ECMA_IS_VALUE_ERROR (next_value)) { goto iterator_cleanup; } ecma_value_t mapped_value; /* 6.g.vii */ if (mapfn_obj_p != NULL) { /* 6.g.vii.1 */ ecma_value_t args_p[2] = { next_value, ecma_make_uint32_value (k) }; /* 6.g.vii.3 */ mapped_value = ecma_op_function_call (mapfn_obj_p, call_this_arg, args_p, 2); ecma_free_value (args_p[1]); ecma_free_value (next_value); /* 6.g.vii.2 */ if (ECMA_IS_VALUE_ERROR (mapped_value)) { ecma_op_iterator_close (iterator); goto iterator_cleanup; } } else { /* 6.g.viii */ mapped_value = next_value; } /* 6.g.ix */ const uint32_t flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW; ecma_value_t set_status = ecma_builtin_helper_def_prop_by_index (array_obj_p, k, mapped_value, flags); ecma_free_value (mapped_value); /* 6.g.x */ if (ECMA_IS_VALUE_ERROR (set_status)) { ecma_op_iterator_close (iterator); goto iterator_cleanup; } /* 6.g.xi */ k++; } iterator_cleanup: ecma_free_value (iterator); ecma_free_value (next_method); ecma_deref_object (array_obj_p); return ret_value; } /* 8. */ ecma_value_t array_like = ecma_op_to_object (items); /* 9. */ if (ECMA_IS_VALUE_ERROR (array_like)) { return array_like; } ecma_object_t *array_like_obj_p = ecma_get_object_from_value (array_like); /* 10. */ ecma_length_t len; ecma_value_t len_value = ecma_op_object_get_length (array_like_obj_p, &len); /* 11. */ if (ECMA_IS_VALUE_ERROR (len_value)) { goto cleanup; } /* 12. */ ecma_object_t *array_obj_p; /* 12.a */ if (ecma_is_constructor (constructor)) { ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor); len_value = ecma_make_length_value (len); ecma_value_t array = ecma_op_function_construct (constructor_obj_p, constructor_obj_p, &len_value, 1); ecma_free_value (len_value); if (ecma_is_value_undefined (array) || ecma_is_value_null (array)) { ecma_raise_type_error (ECMA_ERR_CANNOT_CONVERT_TO_OBJECT); goto cleanup; } /* 14. */ if (ECMA_IS_VALUE_ERROR (array)) { goto cleanup; } array_obj_p = ecma_get_object_from_value (array); } else { /* 13.a */ array_obj_p = ecma_op_new_array_object_from_length (len); if (JERRY_UNLIKELY (array_obj_p == NULL)) { goto cleanup; } } /* 15. */ k = 0; /* 16. */ while (k < len) { /* 16.b */ ecma_value_t k_value = ecma_op_object_get_by_index (array_like_obj_p, k); /* 16.c */ if (ECMA_IS_VALUE_ERROR (k_value)) { goto construct_cleanup; } ecma_value_t mapped_value; /* 16.d */ if (mapfn_obj_p != NULL) { /* 16.d.i */ ecma_value_t args_p[2] = { k_value, ecma_make_length_value (k) }; mapped_value = ecma_op_function_call (mapfn_obj_p, call_this_arg, args_p, 2); ecma_free_value (args_p[1]); ecma_free_value (k_value); /* 16.d.ii */ if (ECMA_IS_VALUE_ERROR (mapped_value)) { goto construct_cleanup; } } else { /* 16.e */ mapped_value = k_value; } /* 16.f */ const uint32_t flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW; ecma_value_t set_status = ecma_builtin_helper_def_prop_by_index (array_obj_p, k, mapped_value, flags); ecma_free_value (mapped_value); /* 16.g */ if (ECMA_IS_VALUE_ERROR (set_status)) { goto construct_cleanup; } /* 16.h */ k++; } /* 17. */ len_value = ecma_make_length_value (k); set_status = ecma_op_object_put (array_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), len_value, true); ecma_free_value (len_value); /* 18. */ if (ECMA_IS_VALUE_ERROR (set_status)) { goto construct_cleanup; } /* 19. */ ecma_deref_object (array_like_obj_p); return ecma_make_object_value (array_obj_p); construct_cleanup: ecma_deref_object (array_obj_p); cleanup: ecma_deref_object (array_like_obj_p); return ret_value; } /* ecma_builtin_array_object_from */ /** * The Array object's 'of' routine * * See also: * ECMA-262 v6, 22.1.2.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_object_of (ecma_value_t this_arg, /**< 'this' argument */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { if (!ecma_is_constructor (this_arg)) { return ecma_op_new_array_object_from_buffer (arguments_list_p, arguments_list_len); } ecma_value_t len = ecma_make_uint32_value (arguments_list_len); ecma_value_t ret_val = ecma_op_function_construct (ecma_get_object_from_value (this_arg), ecma_get_object_from_value (this_arg), &len, 1); if (ECMA_IS_VALUE_ERROR (ret_val)) { ecma_free_value (len); return ret_val; } uint32_t k = 0; ecma_object_t *obj_p = ecma_get_object_from_value (ret_val); const uint32_t prop_status_flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW; while (k < arguments_list_len) { ecma_value_t define_status = ecma_builtin_helper_def_prop_by_index (obj_p, k, arguments_list_p[k], prop_status_flags); if (ECMA_IS_VALUE_ERROR (define_status)) { ecma_free_value (len); ecma_deref_object (obj_p); return define_status; } k++; } ret_val = ecma_op_object_put (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), len, true); ecma_free_value (len); if (ECMA_IS_VALUE_ERROR (ret_val)) { ecma_deref_object (obj_p); return ret_val; } return ecma_make_object_value (obj_p); } /* ecma_builtin_array_object_of */ /** * Handle calling [[Call]] of built-in Array object * * @return ECMA_VALUE_ERROR - if the array construction fails * constructed array object - otherwise */ ecma_value_t ecma_builtin_array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); if (arguments_list_len != 1 || !ecma_is_value_number (arguments_list_p[0])) { return ecma_op_new_array_object_from_buffer (arguments_list_p, arguments_list_len); } ecma_number_t num = ecma_get_number_from_value (arguments_list_p[0]); uint32_t num_uint32 = ecma_number_to_uint32 (num); if (num != ((ecma_number_t) num_uint32)) { return ecma_raise_range_error (ECMA_ERR_INVALID_ARRAY_LENGTH); } return ecma_make_object_value (ecma_op_new_array_object (num_uint32)); } /* ecma_builtin_array_dispatch_call */ /** * Handle calling [[Construct]] of built-in Array object * * @return ECMA_VALUE_ERROR - if the array construction fails * constructed array object - otherwise */ ecma_value_t ecma_builtin_array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_ARRAY_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_array_dispatch_call (arguments_list_p, arguments_list_len); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (proto_p); return result; } ecma_object_t *object_p = ecma_get_object_from_value (result); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, proto_p); ecma_deref_object (proto_p); return result; } /* ecma_builtin_array_dispatch_construct */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_array_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); switch (builtin_routine_id) { case ECMA_ARRAY_ROUTINE_IS_ARRAY: { JERRY_UNUSED (this_arg); return ecma_is_value_array (arguments_list_p[0]); } case ECMA_ARRAY_ROUTINE_FROM: { return ecma_builtin_array_object_from (this_arg, arguments_list_p, arguments_number); } case ECMA_ARRAY_ROUTINE_OF: { return ecma_builtin_array_object_of (this_arg, arguments_list_p, arguments_number); } case ECMA_ARRAY_ROUTINE_SPECIES_GET: { return ecma_copy_value (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_array_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/color_cache.h000664 001750 001750 00000004565 15164251010 036077 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Color Cache for WebP Lossless // // Authors: Jyrki Alakuijala (jyrki@google.com) // Urvang Joshi (urvang@google.com) #ifndef WEBP_UTILS_COLOR_CACHE_H_ #define WEBP_UTILS_COLOR_CACHE_H_ #include "../webp/types.h" #ifdef __cplusplus extern "C" { #endif // Main color cache struct. typedef struct { uint32_t *colors_; // color entries int hash_shift_; // Hash shift: 32 - hash_bits_. int hash_bits_; } VP8LColorCache; static const uint32_t kHashMul = 0x1e35a7bd; static WEBP_INLINE uint32_t VP8LColorCacheLookup( const VP8LColorCache* const cc, uint32_t key) { assert(key <= (~0U >> cc->hash_shift_)); return cc->colors_[key]; } static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc, uint32_t argb) { const uint32_t key = (kHashMul * argb) >> cc->hash_shift_; cc->colors_[key] = argb; } static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc, uint32_t argb) { return (kHashMul * argb) >> cc->hash_shift_; } static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc, uint32_t argb) { const uint32_t key = (kHashMul * argb) >> cc->hash_shift_; return cc->colors_[key] == argb; } //------------------------------------------------------------------------------ // Initializes the color cache with 'hash_bits' bits for the keys. // Returns false in case of memory error. int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits); void VP8LColorCacheCopy(const VP8LColorCache* const src, VP8LColorCache* const dst); // Delete the memory associated to color cache. void VP8LColorCacheClear(VP8LColorCache* const color_cache); //------------------------------------------------------------------------------ #ifdef __cplusplus } #endif #endif // WEBP_UTILS_COLOR_CACHE_H_ lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint8clampedarray.inc.h000664 001750 001750 00000001737 15164251010 054441 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Uint8ClampedArray description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 1 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_UINT8_CLAMPED_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_UINT8CLAMPEDARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jmem/000775 001750 001750 00000000000 15164251010 040247 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/srcthorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp000664 001750 001750 00000077346 15164251010 050057 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jmem.h" #include "lit-char-helpers.h" #include "lit-magic-strings.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltinhelpers ECMA builtin helper operations * @{ */ /** * Helper function for Object.prototype.toString routine when * the @@toStringTag property is present * * See also: * ECMA-262 v6, 19.1.3.6 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_helper_object_to_string_tag_helper (ecma_value_t tag_value) /**< string tag */ { JERRY_ASSERT (ecma_is_value_string (tag_value)); ecma_string_t *tag_str_p = ecma_get_string_from_value (tag_value); lit_utf8_size_t tag_str_size = ecma_string_get_size (tag_str_p); ecma_string_t *ret_string_p; /* Building string "[object #@@toStringTag#]" The string size will be size("[object ") + size(#@@toStringTag#) + size ("]"). */ const lit_utf8_size_t buffer_size = 9 + tag_str_size; JMEM_DEFINE_LOCAL_ARRAY (str_buffer, buffer_size, lit_utf8_byte_t); lit_utf8_byte_t *buffer_ptr = str_buffer; const lit_magic_string_id_t magic_string_ids[] = { LIT_MAGIC_STRING_LEFT_SQUARE_CHAR, LIT_MAGIC_STRING_OBJECT, LIT_MAGIC_STRING_SPACE_CHAR, }; /* Copy to buffer the "[object " string */ for (uint32_t i = 0; i < sizeof (magic_string_ids) / sizeof (lit_magic_string_id_t); ++i) { buffer_ptr = lit_copy_magic_string_to_buffer (magic_string_ids[i], buffer_ptr, (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr)); JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size); } /* Copy to buffer the #@@toStringTag# string */ ecma_string_to_cesu8_bytes (tag_str_p, buffer_ptr, tag_str_size); buffer_ptr += tag_str_size; JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size); /* Copy to buffer the "]" string */ buffer_ptr = lit_copy_magic_string_to_buffer (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR, buffer_ptr, (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr)); JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size); ret_string_p = ecma_new_ecma_string_from_utf8 (str_buffer, (lit_utf8_size_t) (buffer_ptr - str_buffer)); JMEM_FINALIZE_LOCAL_ARRAY (str_buffer); ecma_deref_ecma_string (tag_str_p); return ecma_make_string_value (ret_string_p); } /* ecma_builtin_helper_object_to_string_tag_helper */ /** * Common implementation of the Object.prototype.toString routine * * See also: * ECMA-262 v5, 15.2.4.2 * * Used by: * - The Object.prototype.toString routine. * - The Array.prototype.toString routine as fallback. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_helper_object_to_string (const ecma_value_t this_arg) /**< this argument */ { lit_magic_string_id_t builtin_tag; if (ecma_is_value_undefined (this_arg)) { builtin_tag = LIT_MAGIC_STRING_UNDEFINED_UL; } else if (ecma_is_value_null (this_arg)) { builtin_tag = LIT_MAGIC_STRING_NULL_UL; } else { ecma_value_t obj_this = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (obj_this)) { return obj_this; } JERRY_ASSERT (ecma_is_value_object (obj_this)); ecma_object_t *obj_p = ecma_get_object_from_value (obj_this); builtin_tag = ecma_object_get_class_name (obj_p); ecma_value_t is_array = ecma_is_value_array (obj_this); if (ECMA_IS_VALUE_ERROR (is_array)) { ecma_deref_object (obj_p); return is_array; } if (ecma_is_value_true (is_array)) { builtin_tag = LIT_MAGIC_STRING_ARRAY_UL; } ecma_value_t tag = ecma_op_object_get_by_symbol_id (obj_p, LIT_GLOBAL_SYMBOL_TO_STRING_TAG); if (ECMA_IS_VALUE_ERROR (tag)) { ecma_deref_object (obj_p); return tag; } if (ecma_is_value_string (tag)) { ecma_deref_object (obj_p); return ecma_builtin_helper_object_to_string_tag_helper (tag); } else if (builtin_tag != LIT_MAGIC_STRING_ARGUMENTS_UL && builtin_tag != LIT_MAGIC_STRING_FUNCTION_UL && builtin_tag != LIT_MAGIC_STRING_ERROR_UL && builtin_tag != LIT_MAGIC_STRING_BOOLEAN_UL && builtin_tag != LIT_MAGIC_STRING_NUMBER_UL && builtin_tag != LIT_MAGIC_STRING_STRING_UL && builtin_tag != LIT_MAGIC_STRING_DATE_UL && builtin_tag != LIT_MAGIC_STRING_REGEXP_UL && builtin_tag != LIT_MAGIC_STRING_ARRAY_UL) { builtin_tag = LIT_MAGIC_STRING_OBJECT_UL; } ecma_free_value (tag); ecma_deref_object (obj_p); } ecma_stringbuilder_t builder = ecma_stringbuilder_create (); ecma_stringbuilder_append_magic (&builder, LIT_MAGIC_STRING_OBJECT_TO_STRING_UL); ecma_stringbuilder_append_magic (&builder, builtin_tag); ecma_stringbuilder_append_byte (&builder, LIT_CHAR_RIGHT_SQUARE); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_helper_object_to_string */ /** * The Array.prototype's 'toLocaleString' single element operation routine * * See also: * ECMA-262 v5, 15.4.4.3 steps 6-8 and 10.b-d * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_string_t * ecma_builtin_helper_get_to_locale_string_at_index (ecma_object_t *obj_p, /**< this object */ ecma_length_t index) /**< array index */ { ecma_value_t index_value = ecma_op_object_get_by_index (obj_p, index); if (ECMA_IS_VALUE_ERROR (index_value)) { return NULL; } if (ecma_is_value_undefined (index_value) || ecma_is_value_null (index_value)) { return ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } ecma_value_t call_value = ecma_op_invoke_by_magic_id (index_value, LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, NULL, 0); ecma_free_value (index_value); if (ECMA_IS_VALUE_ERROR (call_value)) { return NULL; } ecma_string_t *ret_string_p = ecma_op_to_string (call_value); ecma_free_value (call_value); return ret_string_p; } /* ecma_builtin_helper_get_to_locale_string_at_index */ /** * Helper function to normalizing an array index * * See also: * ECMA-262 v5, 15.4.4.10 steps 5, 6, 7 part 2, 8 * ECMA-262 v5, 15.4.4.12 steps 5, 6 * * ECMA-262 v6, 22.1.3.6 steps 5 - 7, 8 part 2, 9, 10 * ECMA-262 v6, 22.1.3.3 steps 5 - 10, 11 part 2, 12, 13 * * Used by: * - The Array.prototype.slice routine. * - The Array.prototype.splice routine. * - The Array.prototype.fill routine. * - The Array.prototype.copyWithin routine. * * @return ECMA_VALUE_EMPTY if successful * conversion error otherwise */ ecma_value_t ecma_builtin_helper_array_index_normalize (ecma_value_t arg, /**< index */ ecma_length_t length, /**< array's length */ ecma_length_t *number_p) /**< [out] ecma_length_t */ { ecma_number_t to_int; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (arg, &to_int))) { return ECMA_VALUE_ERROR; } *number_p = ((to_int < 0) ? (ecma_length_t) JERRY_MAX (((ecma_number_t) length + to_int), 0) : (ecma_length_t) JERRY_MIN (to_int, (ecma_number_t) length)); return ECMA_VALUE_EMPTY; } /* ecma_builtin_helper_array_index_normalize */ /** * Helper function to normalizing an uint32 index * * See also: * ECMA-262 v5, 15.5.4.13 steps 4 - 7 * ECMA-262 v6, 22.2.3.5 steps 5 - 10, 11 part 2, 12, 13 * ECMA-262 v6, 22.2.3.23 steps 5 - 10 * ECMA-262 v6, 24.1.4.3 steps 6 - 8, 9 part 2, 10, 11 * ECMA-262 v6, 22.2.3.26 steps 7 - 9, 10 part 2, 11, 12 * ECMA-262 v6, 22.2.3.8 steps 5 - 7, 8 part 2, 9, 10 * * Used by: * - The String.prototype.slice routine. * - The Array.prototype.copyWithin routine. * - The TypedArray.prototype.copyWithin routine. * - The TypedArray.prototype.slice routine. * - The ArrayBuffer.prototype.slice routine. * - The TypedArray.prototype.subarray routine. * - The TypedArray.prototype.fill routine. * * @return ECMA_VALUE_EMPTY if successful * conversion error otherwise */ ecma_value_t ecma_builtin_helper_uint32_index_normalize (ecma_value_t arg, /**< index */ uint32_t length, /**< array's length */ uint32_t *number_p) /**< [out] uint32_t number */ { ecma_number_t to_int; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (arg, &to_int))) { return ECMA_VALUE_ERROR; } *number_p = ((to_int < 0) ? (uint32_t) JERRY_MAX ((ecma_number_t) length + to_int, 0) : (uint32_t) JERRY_MIN (to_int, (ecma_number_t) length)); return ECMA_VALUE_EMPTY; } /* ecma_builtin_helper_uint32_index_normalize */ /** * Helper function for concatenating an ecma_value_t to an Array. * * See also: * ECMA-262 v5, 15.4.4.4 steps 5.b - 5.c * * Used by: * - The Array.prototype.concat routine. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_helper_array_concat_value (ecma_object_t *array_obj_p, /**< array */ ecma_length_t *length_p, /**< [in,out] array's length */ ecma_value_t value) /**< value to concat */ { /* 5.b */ ecma_value_t is_spreadable = ecma_op_is_concat_spreadable (value); if (ECMA_IS_VALUE_ERROR (is_spreadable)) { return is_spreadable; } bool spread_object = is_spreadable == ECMA_VALUE_TRUE; /* ES11: 22.1.3.1.5.c.iv.3.b */ const uint32_t prop_flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW; if (spread_object) { ecma_object_t *obj_p = ecma_get_object_from_value (value); ecma_length_t arg_len; ecma_value_t error = ecma_op_object_get_length (obj_p, &arg_len); if (ECMA_IS_VALUE_ERROR (error)) { return error; } /* 4 . */ if ((ecma_number_t) (*length_p + arg_len) > ECMA_NUMBER_MAX_SAFE_INTEGER) { return ecma_raise_type_error (ECMA_ERR_INVALID_ARRAY_LENGTH); } /* 5.b.iii */ for (ecma_length_t array_index = 0; array_index < arg_len; array_index++) { /* 5.b.iii.2 */ ecma_value_t get_value = ecma_op_object_find_by_index (obj_p, array_index); if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } if (!ecma_is_value_found (get_value)) { continue; } /* 5.b.iii.3.b */ /* This will always be a simple value since 'is_throw' is false, so no need to free. */ ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (array_obj_p, *length_p + array_index, get_value, prop_flags); ecma_free_value (get_value); if (ECMA_IS_VALUE_ERROR (put_comp)) { return put_comp; } } *length_p += arg_len; return ECMA_VALUE_EMPTY; } /* 5.c.i */ /* This will always be a simple value since 'is_throw' is false, so no need to free. */ ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (array_obj_p, (*length_p)++, value, prop_flags); if (ECMA_IS_VALUE_ERROR (put_comp)) { return put_comp; } return ECMA_VALUE_EMPTY; } /* ecma_builtin_helper_array_concat_value */ /** * Helper function to normalizing a string index * * This function clamps the given index to the [0, length] range. * If the index is negative, 0 value is used. * If the index is greater than the length of the string, the normalized index will be the length of the string. * NaN is mapped to zero or length depending on the nan_to_zero parameter. * * See also: * ECMA-262 v5, 15.5.4.15 * * Used by: * - The String.prototype.substring routine. * - The ecma_builtin_helper_string_prototype_object_index_of helper routine. * * @return lit_utf8_size_t - the normalized value of the index */ lit_utf8_size_t ecma_builtin_helper_string_index_normalize (ecma_number_t index, /**< index */ lit_utf8_size_t length, /**< string's length */ bool nan_to_zero) /**< whether NaN is mapped to zero (t) or length (f) */ { uint32_t norm_index = 0; if (ecma_number_is_nan (index)) { if (!nan_to_zero) { norm_index = length; } } else if (!ecma_number_is_negative (index)) { if (ecma_number_is_infinity (index)) { norm_index = length; } else { norm_index = ecma_number_to_uint32 (index); if (norm_index > length) { norm_index = length; } } } return norm_index; } /* ecma_builtin_helper_string_index_normalize */ /** * Helper function for finding lastindex of a search string * * This function clamps the given index to the [0, length] range. * If the index is negative, 0 value is used. * If the index is greater than the length of the string, the normalized index will be the length of the string. * * See also: * ECMA-262 v6, 21.1.3.9 * * Used by: * - The ecma_builtin_helper_string_prototype_object_index_of helper routine. * - The ecma_builtin_string_prototype_object_replace_match helper routine. * * @return uint32_t - whether there is a match for the search string */ static uint32_t ecma_builtin_helper_string_find_last_index (ecma_string_t *original_str_p, /**< original string */ ecma_string_t *search_str_p, /**< search string */ uint32_t position) /**< start_position */ { if (ecma_string_is_empty (search_str_p)) { return position; } uint32_t original_length = ecma_string_get_length (original_str_p); ECMA_STRING_TO_UTF8_STRING (search_str_p, search_str_utf8_p, search_str_size); ECMA_STRING_TO_UTF8_STRING (original_str_p, original_str_utf8_p, original_str_size); uint32_t ret_value = UINT32_MAX; if (original_str_size >= search_str_size) { const lit_utf8_byte_t *end_p = original_str_utf8_p + original_str_size; const lit_utf8_byte_t *current_p = end_p; for (ecma_number_t i = original_length; i > position; i--) { lit_utf8_decr (¤t_p); } while (current_p + search_str_size > end_p) { lit_utf8_decr (¤t_p); position--; } while (true) { if (memcmp (current_p, search_str_utf8_p, search_str_size) == 0) { ret_value = position; break; } if (position == 0) { break; } lit_utf8_decr (¤t_p); position--; } } ECMA_FINALIZE_UTF8_STRING (original_str_utf8_p, original_str_size); ECMA_FINALIZE_UTF8_STRING (search_str_utf8_p, search_str_size); return ret_value; } /* ecma_builtin_helper_string_find_last_index */ /** * Helper function for string indexOf, lastIndexOf, startsWith, includes, endsWith functions * * See also: * ECMA-262 v5, 15.5.4.7 * ECMA-262 v5, 15.5.4.8 * ECMA-262 v6, 21.1.3.6 * ECMA-262 v6, 21.1.3.7 * ECMA-262 v6, 21.1.3.18 * * Used by: * - The String.prototype.indexOf routine. * - The String.prototype.lastIndexOf routine. * - The String.prototype.startsWith routine. * - The String.prototype.includes routine. * - The String.prototype.endsWith routine. * * @return ecma_value_t - Returns index (last index) or a * boolean value */ ecma_value_t ecma_builtin_helper_string_prototype_object_index_of (ecma_string_t *original_str_p, /**< this argument */ ecma_value_t arg1, /**< routine's first argument */ ecma_value_t arg2, /**< routine's second argument */ ecma_string_index_of_mode_t mode) /**< routine's mode */ { /* 5 (indexOf) -- 6 (lastIndexOf) */ const lit_utf8_size_t original_len = ecma_string_get_length (original_str_p); /* 4, 6 (startsWith, includes, endsWith) */ if (mode >= ECMA_STRING_STARTS_WITH) { ecma_value_t regexp = ecma_op_is_regexp (arg1); if (ECMA_IS_VALUE_ERROR (regexp)) { return regexp; } if (regexp == ECMA_VALUE_TRUE) { JERRY_ASSERT (ECMA_STRING_LAST_INDEX_OF < mode && mode <= ECMA_STRING_ENDS_WITH); return ecma_raise_type_error (ECMA_ERR_SEARCH_STRING_CANNOT_BE_OF_TYPE_REGEXP); } } /* 7, 8 */ ecma_string_t *search_str_p = ecma_op_to_string (arg1); if (JERRY_UNLIKELY (search_str_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t ret_value; /* 4 (indexOf, lastIndexOf), 9 (startsWith, includes), 10 (endsWith) */ ecma_number_t pos_num; if (mode > ECMA_STRING_LAST_INDEX_OF) { ret_value = ecma_op_to_integer (arg2, &pos_num); } else { ret_value = ecma_op_to_number (arg2, &pos_num); } /* 10 (startsWith, includes), 11 (endsWith) */ if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_deref_ecma_string (search_str_p); return ret_value; } bool use_first_index = mode != ECMA_STRING_LAST_INDEX_OF; /* 4b, 6 (indexOf) - 4b, 5, 7 (lastIndexOf) */ lit_utf8_size_t start = ecma_builtin_helper_string_index_normalize (pos_num, original_len, use_first_index); ecma_number_t ret_num = ECMA_NUMBER_MINUS_ONE; ret_value = ECMA_VALUE_FALSE; switch (mode) { case ECMA_STRING_STARTS_WITH: { if (start > original_len) { break; } /* 15, 16 (startsWith) */ uint32_t index = ecma_builtin_helper_string_find_index (original_str_p, search_str_p, start); ret_value = ecma_make_boolean_value (index == start); break; } case ECMA_STRING_INCLUDES: { if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, start) != UINT32_MAX) { ret_value = ECMA_VALUE_TRUE; } break; } case ECMA_STRING_ENDS_WITH: { if (start == 0) { start = original_len; } lit_utf8_size_t search_str_len = ecma_string_get_length (search_str_p); if (search_str_len == 0) { ret_value = ECMA_VALUE_TRUE; break; } int32_t start_ends_with = (int32_t) (start - search_str_len); if (start_ends_with < 0) { break; } uint32_t index = ecma_builtin_helper_string_find_index (original_str_p, search_str_p, (uint32_t) start_ends_with); ret_value = ecma_make_boolean_value (index == (uint32_t) start_ends_with); break; } case ECMA_STRING_INDEX_OF: { /* 8 (indexOf) -- 9 (lastIndexOf) */ ecma_value_t find_index = ecma_builtin_helper_string_find_index (original_str_p, search_str_p, start); if (find_index != UINT32_MAX) { ret_num = ((ecma_number_t) find_index); } ret_value = ecma_make_number_value (ret_num); break; } case ECMA_STRING_LAST_INDEX_OF: { uint32_t index = ecma_builtin_helper_string_find_last_index (original_str_p, search_str_p, start); if (index != UINT32_MAX) { ret_num = ((ecma_number_t) index); } ret_value = ecma_make_number_value (ret_num); break; } default: { JERRY_UNREACHABLE (); } } ecma_deref_ecma_string (search_str_p); return ret_value; } /* ecma_builtin_helper_string_prototype_object_index_of */ /** * Helper function for finding index of a search string * * This function clamps the given index to the [0, length] range. * If the index is negative, 0 value is used. * If the index is greater than the length of the string, the normalized index will be the length of the string. * * See also: * ECMA-262 v6, 21.1.3.8 * * Used by: * - The ecma_builtin_helper_string_prototype_object_index_of helper routine. * - The ecma_builtin_string_prototype_object_replace_match helper routine. * * @return uint32_t - whether there is a match for the search string */ uint32_t ecma_builtin_helper_string_find_index (ecma_string_t *original_str_p, /**< index */ ecma_string_t *search_str_p, /**< string's length */ uint32_t start_pos) /**< start position */ { uint32_t match_found = UINT32_MAX; if (ecma_string_is_empty (search_str_p)) { return start_pos; } ECMA_STRING_TO_UTF8_STRING (search_str_p, search_str_utf8_p, search_str_size); ECMA_STRING_TO_UTF8_STRING (original_str_p, original_str_utf8_p, original_str_size); const lit_utf8_byte_t *str_current_p = original_str_utf8_p; for (ecma_number_t i = 0; i < start_pos; i++) { lit_utf8_incr (&str_current_p); } const lit_utf8_byte_t *original_end_p = original_str_utf8_p + original_str_size; while (!((size_t) (original_end_p - str_current_p) < search_str_size)) { if (memcmp (str_current_p, search_str_utf8_p, search_str_size) == 0) { match_found = start_pos; break; } lit_utf8_incr (&str_current_p); start_pos++; } ECMA_FINALIZE_UTF8_STRING (original_str_utf8_p, original_str_size); ECMA_FINALIZE_UTF8_STRING (search_str_utf8_p, search_str_size); return match_found; } /* ecma_builtin_helper_string_find_index */ /** * Helper function for using [[DefineOwnProperty]] specialized for indexed property names * * Note: this method falls back to the general ecma_builtin_helper_def_prop * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_helper_def_prop_by_index (ecma_object_t *obj_p, /**< object */ ecma_length_t index, /**< property index */ ecma_value_t value, /**< value */ uint32_t opts) /**< any combination of ecma_property_flag_t bits */ { if (JERRY_LIKELY (index <= ECMA_DIRECT_STRING_MAX_IMM)) { return ecma_builtin_helper_def_prop (obj_p, ECMA_CREATE_DIRECT_UINT32_STRING (index), value, opts); } ecma_string_t *index_str_p = ecma_new_ecma_string_from_length (index); ecma_value_t ret_value = ecma_builtin_helper_def_prop (obj_p, index_str_p, value, opts); ecma_deref_ecma_string (index_str_p); return ret_value; } /* ecma_builtin_helper_def_prop_by_index */ /** * Helper function for at() functions. * * See also: * ECMA-262 Stage 3 Draft Relative Indexing Method proposal 3. 4. 5. 6. * * Used by: * - The Array.prototype.at routine. * - The String.prototype.at routine. * - The TypedArray.prototype.at routine. * * @return ECMA_VALUE_ERROR - on conversion error * ECMA_VALUE_UNDEFINED - if the requested index is not exist * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t ecma_builtin_helper_calculate_index (ecma_value_t index, /**< relative index argument */ ecma_length_t length, /**< object's length */ ecma_length_t *out_index) /**< calculated index */ { JERRY_ASSERT (out_index != NULL); ecma_number_t relative_index; ecma_value_t conversion_result = ecma_op_to_integer (index, &relative_index); /* 4. */ if (ECMA_IS_VALUE_ERROR (conversion_result)) { return ECMA_VALUE_ERROR; } /* 5. 6. */ ecma_number_t k; if (relative_index >= 0) { k = relative_index; } else { k = ((ecma_number_t) length + relative_index); } /* 7. */ if (k < 0 || k >= ((ecma_number_t) length)) { return ECMA_VALUE_UNDEFINED; } *out_index = (ecma_length_t) k; return ECMA_VALUE_EMPTY; } /* ecma_builtin_helper_calculate_index */ /** * Helper function for using [[DefineOwnProperty]]. * * See also: * ECMA-262 v5, 8.12.9 * ECMA-262 v5, 15.4.5.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_helper_def_prop (ecma_object_t *obj_p, /**< object */ ecma_string_t *name_p, /**< name string */ ecma_value_t value, /**< value */ uint32_t opts) /**< any combination of ecma_property_descriptor_status_flags_t bits */ { ecma_property_descriptor_t prop_desc; prop_desc.flags = (uint16_t) (ECMA_NAME_DATA_PROPERTY_DESCRIPTOR_BITS | opts); prop_desc.value = value; return ecma_op_object_define_own_property (obj_p, name_p, &prop_desc); } /* ecma_builtin_helper_def_prop */ /** * GetSubstitution abstract operation * * See: * ECMA-262 v6.0 21.1.3.14.1 */ void ecma_builtin_replace_substitute (ecma_replace_context_t *ctx_p) /**< replace context */ { JERRY_ASSERT (ctx_p->string_p != NULL); JERRY_ASSERT (ctx_p->matched_p == NULL || (ctx_p->matched_p >= ctx_p->string_p && ctx_p->matched_p <= ctx_p->string_p + ctx_p->string_size)); lit_utf8_size_t replace_size; uint8_t replace_flags = ECMA_STRING_FLAG_IS_ASCII; const lit_utf8_byte_t *replace_buf_p = ecma_string_get_chars (ctx_p->replace_str_p, &replace_size, NULL, NULL, &replace_flags); const lit_utf8_byte_t *const replace_end_p = replace_buf_p + replace_size; const lit_utf8_byte_t *curr_p = replace_buf_p; const lit_utf8_byte_t *last_inserted_end_p = replace_buf_p; while (curr_p < replace_end_p) { if (*curr_p++ == LIT_CHAR_DOLLAR_SIGN) { ecma_stringbuilder_append_raw (&(ctx_p->builder), last_inserted_end_p, (lit_utf8_size_t) (curr_p - last_inserted_end_p - 1)); if (curr_p >= replace_end_p) { last_inserted_end_p = curr_p - 1; break; } const lit_utf8_byte_t c = *curr_p++; switch (c) { case LIT_CHAR_DOLLAR_SIGN: { ecma_stringbuilder_append_byte (&(ctx_p->builder), LIT_CHAR_DOLLAR_SIGN); break; } case LIT_CHAR_AMPERSAND: { if (JERRY_UNLIKELY (ctx_p->matched_p == NULL)) { JERRY_ASSERT (ctx_p->capture_count == 0); JERRY_ASSERT (ctx_p->u.collection_p != NULL); JERRY_ASSERT (ctx_p->u.collection_p->item_count > 0); const ecma_value_t match_value = ctx_p->u.collection_p->buffer_p[0]; JERRY_ASSERT (ecma_is_value_string (match_value)); ecma_stringbuilder_append (&(ctx_p->builder), ecma_get_string_from_value (match_value)); break; } JERRY_ASSERT (ctx_p->matched_p != NULL); ecma_stringbuilder_append_raw (&(ctx_p->builder), ctx_p->matched_p, ctx_p->matched_size); break; } case LIT_CHAR_GRAVE_ACCENT: { ecma_stringbuilder_append_raw (&(ctx_p->builder), ctx_p->string_p, ctx_p->match_byte_pos); break; } case LIT_CHAR_SINGLE_QUOTE: { if (JERRY_UNLIKELY (ctx_p->matched_p == NULL)) { JERRY_ASSERT (ctx_p->capture_count == 0); JERRY_ASSERT (ctx_p->u.collection_p != NULL); JERRY_ASSERT (ctx_p->u.collection_p->item_count > 0); const ecma_value_t match_value = ctx_p->u.collection_p->buffer_p[0]; JERRY_ASSERT (ecma_is_value_string (match_value)); const ecma_string_t *const matched_p = ecma_get_string_from_value (match_value); const lit_utf8_size_t match_size = ecma_string_get_size (matched_p); const lit_utf8_byte_t *const begin_p = ctx_p->string_p + ctx_p->match_byte_pos + match_size; ecma_stringbuilder_append_raw (&(ctx_p->builder), begin_p, (lit_utf8_size_t) (ctx_p->string_p + ctx_p->string_size - begin_p)); break; } JERRY_ASSERT (ctx_p->matched_p != NULL); ecma_stringbuilder_append_raw (&(ctx_p->builder), ctx_p->matched_p + ctx_p->matched_size, ctx_p->string_size - ctx_p->match_byte_pos - ctx_p->matched_size); break; } default: { const lit_utf8_byte_t *const number_begin_p = curr_p - 1; if (lit_char_is_decimal_digit (c)) { uint32_t capture_count = ctx_p->capture_count; if (capture_count == 0 && ctx_p->u.collection_p != NULL) { capture_count = ctx_p->u.collection_p->item_count; } uint8_t idx = (uint8_t) (c - LIT_CHAR_0); if (curr_p < replace_end_p && lit_char_is_decimal_digit (*(curr_p))) { uint8_t two_digit_index = (uint8_t) (idx * 10 + (uint8_t) (*(curr_p) -LIT_CHAR_0)); if (two_digit_index < capture_count) { idx = two_digit_index; curr_p++; } } if (idx > 0 && idx < capture_count) { if (ctx_p->capture_count > 0) { #if JERRY_BUILTIN_REGEXP JERRY_ASSERT (ctx_p->u.captures_p != NULL); const ecma_regexp_capture_t *const capture_p = ctx_p->u.captures_p + idx; if (ECMA_RE_IS_CAPTURE_DEFINED (capture_p)) { ecma_stringbuilder_append_raw (&(ctx_p->builder), capture_p->begin_p, (lit_utf8_size_t) (capture_p->end_p - capture_p->begin_p)); } break; #endif /* JERRY_BUILTIN_REGEXP */ } else if (ctx_p->u.collection_p != NULL) { const ecma_value_t capture_value = ctx_p->u.collection_p->buffer_p[idx]; if (!ecma_is_value_undefined (capture_value)) { ecma_stringbuilder_append (&(ctx_p->builder), ecma_get_string_from_value (capture_value)); } break; } } } ecma_stringbuilder_append_byte (&(ctx_p->builder), LIT_CHAR_DOLLAR_SIGN); curr_p = number_begin_p; break; } } last_inserted_end_p = curr_p; } } ecma_stringbuilder_append_raw (&(ctx_p->builder), last_inserted_end_p, (lit_utf8_size_t) (replace_end_p - last_inserted_end_p)); if (replace_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) replace_buf_p, replace_size); } } /* ecma_builtin_replace_substitute */ /** * Helper function to determine if method is the builtin exec method * * @return true, if function is the builtin exec method * false, otherwise */ bool ecma_builtin_is_regexp_exec (ecma_extended_object_t *obj_p) /**< function object */ { return (ecma_get_object_type (&obj_p->object) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION && obj_p->u.built_in.routine_id == ECMA_REGEXP_PROTOTYPE_ROUTINE_EXEC); } /* ecma_builtin_is_regexp_exec */ /** * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/memcheck_asan.sh000775 001750 001750 00000002646 15164251010 035660 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0#!/bin/bash if [[ -z "$GITHUB_TOKEN" ]]; then echo "The GITHUB_TOKEN is required." exit 1 fi if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then echo "Run Address Sanitizer" echo "meson -Db_sanitize=\"address,undefined\" -Dloaders=\"all\" -Dsavers=\"all\" -Dtests=\"true\" -Dbindings=\"capi\" . build" pwd cd ${GITHUB_WORKSPACE}/build/test ./tvgUnitTests > memcheck_asan.txt 2>&1 PAYLOAD_MEMCHECK=`cat memcheck_asan.txt` COMMENTS_URL=$(cat $GITHUB_EVENT_PATH | jq -r .pull_request.comments_url) echo $COMMENTS_URL echo "MEMCHECK errors:" echo $PAYLOAD_MEMCHECK if [[ $PAYLOAD_MEMCHECK == *"runtime error:"* || $PAYLOAD_MEMCHECK == *"ERROR: AddressSanitizer:"* ]]; then OUTPUT+=$'\n**MEMCHECK(ASAN) RESULT**:\n' OUTPUT+=$'\n`meson -Db_sanitize="address,undefined" -Dloaders="all" -Dsavers="all" -Dtests="true" -Dbindings="capi" . build`\n' OUTPUT+=$'\n```\n' OUTPUT+="$PAYLOAD_MEMCHECK" OUTPUT+=$'\n```\n' ( echo '
ASAN output' echo echo "$OUTPUT" echo echo '
' ) >> "$GITHUB_STEP_SUMMARY" PAYLOAD=$(echo '{}' | jq --arg body "$OUTPUT" '.body = $body') curl -s -S -H "Authorization: token $GITHUB_TOKEN" --header "Content-Type: application/vnd.github.VERSION.text+json" --data "$PAYLOAD" "$COMMENTS_URL" fi fi glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-module.h000664 001750 001750 00000011471 15164251010 043503 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_MODULE_H #define ECMA_MODULE_H #include "ecma-globals.h" #include "common.h" #if JERRY_MODULE_SYSTEM #define ECMA_MODULE_MAX_PATH 255u /** * Module status flags. */ typedef enum { ECMA_MODULE_IS_NATIVE = (1 << 0), /**< native module */ ECMA_MODULE_HAS_NAMESPACE = (1 << 1), /**< namespace object has been initialized */ } ecma_module_flags_t; /** * Imported or exported names, such as "a as b" * Note: See https://www.ecma-international.org/ecma-262/6.0/#table-39 * and https://www.ecma-international.org/ecma-262/6.0/#table-41 */ typedef struct ecma_module_names { struct ecma_module_names *next_p; /**< next linked list node */ ecma_string_t *imex_name_p; /**< Import/export name of the item */ ecma_string_t *local_name_p; /**< Local name of the item */ } ecma_module_names_t; /** * Module structure storing an instance of a module * * Note: * The imports_p list follows the order of import-from/export-from statements in the source * code of a module, even if a given module specifier is only used by export-from statements. */ typedef struct ecma_module { /* Note: state is stored in header.u.class_prop.extra_info */ ecma_extended_object_t header; /**< header part */ /* TODO(dbatyai): These could be compressed pointers */ ecma_object_t *scope_p; /**< lexical environment of the module */ ecma_object_t *namespace_object_p; /**< namespace object of the module */ struct ecma_module_node *imports_p; /**< import requests of the module */ ecma_module_names_t *local_exports_p; /**< local exports of the module */ struct ecma_module_node *indirect_exports_p; /**< indirect exports of the module */ struct ecma_module_node *star_exports_p; /**< star exports of the module */ /* Code used for evaluating a module */ union { ecma_compiled_code_t *compiled_code_p; /**< compiled code for the module */ jerry_native_module_evaluate_cb_t callback; /**< callback for evaluating native modules */ } u; } ecma_module_t; /** * Module node to store imports / exports. * * Note: * Only one module node is created for each module specifier: the names are * concatenated if the same specifier is used multiple times in the source code. * However, multiple nodes are created for modules with multiple alias * (for example ./a.mjs and ././a.mjs can refer to the same module). */ typedef struct ecma_module_node { struct ecma_module_node *next_p; /**< next linked list node */ ecma_module_names_t *module_names_p; /**< names of the requested import/export node */ union { ecma_value_t path_or_module; /**< imports: module specifier (if string) or module reference (if object) */ ecma_value_t *module_object_p; /**< non-imports: reference to a path_or_module field in the imports */ } u; } ecma_module_node_t; /** * A list of module records that can be used to identify circular imports during resolution */ typedef struct ecma_module_resolve_set { struct ecma_module_resolve_set *next_p; /**< next in linked list */ ecma_module_t *module_p; /**< module */ ecma_string_t *name_p; /**< identifier name */ } ecma_module_resolve_set_t; /** * A list that is used like a stack to drive the resolution process, instead of recursion. */ typedef struct ecma_module_resolve_stack { struct ecma_module_resolve_stack *next_p; /**< next in linked list */ ecma_module_t *module_p; /**< module request */ ecma_string_t *export_name_p; /**< export identifier name */ bool resolving; /**< flag storing whether the current frame started resolving */ } ecma_module_resolve_stack_t; ecma_value_t ecma_module_initialize (ecma_module_t *module_p); ecma_module_t *ecma_module_get_resolved_module (ecma_value_t module_val); ecma_value_t ecma_module_link (ecma_module_t *module_p, jerry_module_resolve_cb_t callback_p, void *user_p); ecma_value_t ecma_module_evaluate (ecma_module_t *module_p); ecma_value_t ecma_module_import (ecma_value_t specifier, ecma_value_t user_value); ecma_module_t *ecma_module_create (void); void ecma_module_cleanup_context (void); void ecma_module_release_module_names (ecma_module_names_t *module_name_p); void ecma_module_release_module (ecma_module_t *module_p); #endif /* JERRY_MODULE_SYSTEM */ #endif /* !ECMA_MODULE_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakref.cpp000664 001750 001750 00000005633 15164251010 050027 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "jcontext.h" #if JERRY_BUILTIN_WEAKREF #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-weakref.inc.h" #define BUILTIN_UNDERSCORED_ID weakref #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup weakref ECMA WeakRef object built-in * @{ */ /** * Handle calling [[Call]] of built-in WeakRef object * * @return ecma value */ ecma_value_t ecma_builtin_weakref_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_WEAKREF_REQUIRES_NEW); } /* ecma_builtin_weakref_dispatch_call */ /** * Handle calling [[Construct]] of built-in WeakRef object * * @return ecma value */ ecma_value_t ecma_builtin_weakref_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { if (arguments_list_len == 0 || !ecma_is_value_object (arguments_list_p[0])) { return ecma_raise_type_error (ECMA_ERR_WEAKREF_TARGET_MUST_BE_AN_OBJECT); } JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p) != NULL); ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_WEAKREF_PROTOTYPE); if (JERRY_UNLIKELY (proto_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *object_p = ecma_create_object (proto_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_deref_object (proto_p); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; ext_obj_p->u.cls.type = ECMA_OBJECT_CLASS_WEAKREF; ext_obj_p->u.cls.u3.target = arguments_list_p[0]; ecma_op_object_set_weak (ecma_get_object_from_value (arguments_list_p[0]), object_p); return ecma_make_object_value (object_p); } /* ecma_builtin_weakref_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_WEAKREF */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-literal-storage.h000664 001750 001750 00000005035 15164251010 045313 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_LIT_STORAGE_H #define ECMA_LIT_STORAGE_H #include "ecma-globals.h" #include "jmem.h" #include "lit-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmalitstorage Literal storage * @{ */ #if JERRY_SNAPSHOT_SAVE /** * Snapshot literal - offset map */ typedef struct { ecma_value_t literal_id; /**< literal id */ ecma_value_t literal_offset; /**< literal offset */ } lit_mem_to_snapshot_id_map_entry_t; #endif /* JERRY_SNAPSHOT_SAVE */ void ecma_finalize_lit_storage (void); ecma_value_t ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, lit_utf8_size_t size, bool is_ascii); ecma_value_t ecma_find_or_create_literal_number (ecma_number_t number_arg); #if JERRY_BUILTIN_BIGINT ecma_value_t ecma_find_or_create_literal_bigint (ecma_value_t bigint); #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_SNAPSHOT_SAVE void ecma_save_literals_append_value (ecma_value_t value, ecma_collection_t *lit_pool_p); void ecma_save_literals_add_compiled_code (const ecma_compiled_code_t *compiled_code_p, ecma_collection_t *lit_pool_p); bool ecma_save_literals_for_snapshot (ecma_collection_t *lit_pool_p, uint32_t *buffer_p, size_t buffer_size, size_t *in_out_buffer_offset_p, lit_mem_to_snapshot_id_map_entry_t **out_map_p, uint32_t *out_map_len_p); #endif /* JERRY_SNAPSHOT_SAVE */ #if JERRY_SNAPSHOT_EXEC || JERRY_SNAPSHOT_SAVE ecma_value_t ecma_snapshot_get_literal (const uint8_t *literal_base_p, ecma_value_t literal_value); ecma_value_t *ecma_snapshot_resolve_serializable_values (const ecma_compiled_code_t *compiled_code_p, uint8_t *byte_code_end_p); #endif /* JERRY_SNAPSHOT_EXEC || JERRY_SNAPSHOT_SAVE */ /** * @} * @} */ #endif /* !ECMA_LIT_STORAGE_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieParserHandler.h000664 001750 001750 00000012331 15164251010 037463 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_PARSER_HANDLER_H_ #define _TVG_LOTTIE_PARSER_HANDLER_H_ #include "rapidjson/document.h" #include "tvgCommon.h" using namespace rapidjson; #define PARSE_FLAGS (kParseDefaultFlags | kParseInsituFlag) struct LookaheadParserHandler { enum LookaheadParsingState { kInit = 0, kError, kHasNull, kHasBool, kHasNumber, kHasString, kHasKey, kEnteringObject, kExitingObject, kEnteringArray, kExitingArray }; Value val; LookaheadParsingState state = kInit; Reader reader; InsituStringStream iss; LookaheadParserHandler(const char *str) : iss((char*)str) { reader.IterativeParseInit(); } bool Null() { state = kHasNull; val.SetNull(); return true; } bool Bool(bool b) { state = kHasBool; val.SetBool(b); return true; } bool Int(int i) { state = kHasNumber; val.SetInt(i); return true; } bool Uint(unsigned u) { state = kHasNumber; val.SetUint(u); return true; } bool Int64(int64_t i) { state = kHasNumber; val.SetInt64(i); return true; } bool Uint64(int64_t u) { state = kHasNumber; val.SetUint64(u); return true; } bool Double(double d) { state = kHasNumber; val.SetDouble(d); return true; } bool RawNumber(const char *, SizeType, TVG_UNUSED bool) { return false; } bool String(const char *str, SizeType length, TVG_UNUSED bool) { state = kHasString; val.SetString(str, length); return true; } bool StartObject() { state = kEnteringObject; return true; } bool Key(const char *str, SizeType length, TVG_UNUSED bool) { state = kHasKey; val.SetString(str, length); return true; } bool EndObject(SizeType) { state = kExitingObject; return true; } bool StartArray() { state = kEnteringArray; return true; } bool EndArray(SizeType) { state = kExitingArray; return true; } void Error() { TVGERR("LOTTIE", "Invalid JSON: unexpected or misaligned data fields."); state = kError; reader.IterativeParseNext(iss, *this); //something wrong but try advancement. } bool Invalid() { return state == kError; } bool enterObject(); bool enterArray(); bool nextArrayValue(); int getInt(); float getFloat(); const char* getString(); char* getStringCopy(); bool getBool(); void getNull(); bool parseNext(); const char* nextObjectKey(); void skip(); void skipOut(int depth); int peekType(); bool isPrimitive(); char* getPos(); }; #endif //_TVG_LOTTIE_PARSER_HANDLER_H_lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-string-iterator-prototype.cpp000664 001750 001750 00000014172 15164251010 053601 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-iterator-object.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BUILTIN_STRING_ITERATOR_PROTOTYPE_ROUTINE_START = 0, ECMA_BUILTIN_STRING_ITERATOR_PROTOTYPE_OBJECT_NEXT, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-string-iterator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID string_iterator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup %stringiteratorprototype% ECMA %ArrayIteratorPrototype% object built-in * @{ */ /** * The %StringIteratorPrototype% object's 'next' routine * * See also: * ECMA-262 v6, 22.1.5.2.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object, if success * error - otherwise */ static ecma_value_t ecma_builtin_string_iterator_prototype_object_next (ecma_value_t this_val) /**< this argument */ { /* 1 - 2. */ if (!ecma_is_value_object (this_val)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (this_val); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; /* 3. */ if (!ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_STRING_ITERATOR)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_ITERATOR); } ecma_value_t iterated_value = ext_obj_p->u.cls.u3.iterated_value; /* 4 - 5 */ if (ecma_is_value_empty (iterated_value)) { return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } JERRY_ASSERT (ecma_is_value_string (iterated_value)); ecma_string_t *string_p = ecma_get_string_from_value (iterated_value); /* 6. */ lit_utf8_size_t position = ext_obj_p->u.cls.u2.iterator_index; if (JERRY_UNLIKELY (position == ECMA_ITERATOR_INDEX_LIMIT)) { /* After the ECMA_ITERATOR_INDEX_LIMIT limit is reached the [[%Iterator%NextIndex]] property is stored as an internal property */ ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX); ecma_value_t position_value = ecma_op_object_get (obj_p, prop_name_p); position = (lit_utf8_size_t) (ecma_get_number_from_value (position_value)); ecma_free_value (position_value); } /* 7. */ lit_utf8_size_t len = ecma_string_get_length (string_p); /* 8. */ if (position >= len) { ecma_deref_ecma_string (string_p); ext_obj_p->u.cls.u3.iterated_value = ECMA_VALUE_EMPTY; return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } /* 9. */ ecma_char_t first = ecma_string_get_char_at_pos (string_p, position); ecma_string_t *result_str_p; lit_utf8_size_t result_size = 1; /* 10. */ if (first < LIT_UTF16_HIGH_SURROGATE_MIN || first > LIT_UTF16_HIGH_SURROGATE_MAX || (position + 1 == len)) { result_str_p = ecma_new_ecma_string_from_code_unit (first); } /* 11. */ else { /* 11.a */ ecma_char_t second = ecma_string_get_char_at_pos (string_p, position + 1); /* 11.b */ if (second < LIT_UTF16_LOW_SURROGATE_MIN || second > LIT_UTF16_LOW_SURROGATE_MAX) { result_str_p = ecma_new_ecma_string_from_code_unit (first); } /* 11.c */ else { result_str_p = ecma_new_ecma_string_from_code_units (first, second); result_size = 2; } } /* 13. */ if (position + result_size < ECMA_ITERATOR_INDEX_LIMIT) { ext_obj_p->u.cls.u2.iterator_index = (uint16_t) (position + result_size); } else { ext_obj_p->u.cls.u2.iterator_index = ECMA_ITERATOR_INDEX_LIMIT; ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX); ecma_op_object_put (obj_p, prop_name_p, ecma_make_length_value (position + result_size), true); } /* 14. */ ecma_value_t result = ecma_create_iter_result_object (ecma_make_string_value (result_str_p), ECMA_VALUE_FALSE); ecma_deref_ecma_string (result_str_p); return result; } /* ecma_builtin_string_iterator_prototype_object_next */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_string_iterator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide * routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< * list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_list_p, arguments_number); switch (builtin_routine_id) { case ECMA_BUILTIN_STRING_ITERATOR_PROTOTYPE_OBJECT_NEXT: { return ecma_builtin_string_iterator_prototype_object_next (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_string_iterator_prototype_dispatch_routine */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgAnimation.h000664 001750 001750 00000002667 15164251010 034436 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_ANIMATION_H_ #define _TVG_ANIMATION_H_ #include "tvgCommon.h" #include "tvgPicture.h" struct Animation::Impl { Picture* picture = nullptr; Impl() { picture = Picture::gen(); picture->ref(); } ~Impl() { picture->unref(); } }; #endif //_TVG_ANIMATION_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/decode_vp8.h000664 001750 001750 00000014653 15164251010 035250 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Low-level API for VP8 decoder // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_WEBP_DECODE_VP8_H_ #define WEBP_WEBP_DECODE_VP8_H_ #include "../webp/decode.h" #ifdef __cplusplus extern "C" { #endif //------------------------------------------------------------------------------ // Lower-level API // // These functions provide fine-grained control of the decoding process. // The call flow should resemble: // // VP8Io io; // VP8InitIo(&io); // io.data = data; // io.data_size = size; // /* customize io's functions (setup()/put()/teardown()) if needed. */ // // VP8Decoder* dec = VP8New(); // bool ok = VP8Decode(dec); // if (!ok) printf("Error: %s\n", VP8StatusMessage(dec)); // VP8Delete(dec); // return ok; // Input / Output typedef struct VP8Io VP8Io; typedef int (*VP8IoPutHook)(const VP8Io* io); typedef int (*VP8IoSetupHook)(VP8Io* io); typedef void (*VP8IoTeardownHook)(const VP8Io* io); struct VP8Io { // set by VP8GetHeaders() int width, height; // picture dimensions, in pixels (invariable). // These are the original, uncropped dimensions. // The actual area passed to put() is stored // in mb_w / mb_h fields. // set before calling put() int mb_y; // position of the current rows (in pixels) int mb_w; // number of columns in the sample int mb_h; // number of rows in the sample const uint8_t* y, *u, *v; // rows to copy (in yuv420 format) int y_stride; // row stride for luma int uv_stride; // row stride for chroma void* opaque; // user data // called when fresh samples are available. Currently, samples are in // YUV420 format, and can be up to width x 24 in size (depending on the // in-loop filtering level, e.g.). Should return false in case of error // or abort request. The actual size of the area to update is mb_w x mb_h // in size, taking cropping into account. VP8IoPutHook put; // called just before starting to decode the blocks. // Must return false in case of setup error, true otherwise. If false is // returned, teardown() will NOT be called. But if the setup succeeded // and true is returned, then teardown() will always be called afterward. VP8IoSetupHook setup; // Called just after block decoding is finished (or when an error occurred // during put()). Is NOT called if setup() failed. VP8IoTeardownHook teardown; // this is a recommendation for the user-side yuv->rgb converter. This flag // is set when calling setup() hook and can be overwritten by it. It then // can be taken into consideration during the put() method. int fancy_upsampling; // Input buffer. size_t data_size; const uint8_t* data; // If true, in-loop filtering will not be performed even if present in the // bitstream. Switching off filtering may speed up decoding at the expense // of more visible blocking. Note that output will also be non-compliant // with the VP8 specifications. int bypass_filtering; // Cropping parameters. int use_cropping; int crop_left, crop_right, crop_top, crop_bottom; // Scaling parameters. int use_scaling; int scaled_width, scaled_height; // If non NULL, pointer to the alpha data (if present) corresponding to the // start of the current row (That is: it is pre-offset by mb_y and takes // cropping into account). const uint8_t* a; }; // Internal, version-checked, entry point int VP8InitIoInternal(VP8Io* const, int); // Main decoding object. This is an opaque structure. typedef struct VP8Decoder VP8Decoder; // Create a new decoder object. VP8Decoder* VP8New(void); // Must be called to make sure 'io' is initialized properly. // Returns false in case of version mismatch. Upon such failure, no other // decoding function should be called (VP8Decode, VP8GetHeaders, ...) static WEBP_INLINE int VP8InitIo(VP8Io* const io) { return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION); } // Decode the VP8 frame header. Returns true if ok. // Note: 'io->data' must be pointing to the start of the VP8 frame header. int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); // Decode a picture. Will call VP8GetHeaders() if it wasn't done already. // Returns false in case of error. int VP8Decode(VP8Decoder* const dec, VP8Io* const io); // Return current status of the decoder: VP8StatusCode VP8Status(VP8Decoder* const dec); // return readable string corresponding to the last status. const char* VP8StatusMessage(VP8Decoder* const dec); // Resets the decoder in its initial state, reclaiming memory. // Not a mandatory call between calls to VP8Decode(). void VP8Clear(VP8Decoder* const dec); // Destroy the decoder object. void VP8Delete(VP8Decoder* const dec); //------------------------------------------------------------------------------ // Miscellaneous VP8/VP8L bitstream probing functions. // Returns true if the next 3 bytes in data contain the VP8 signature. WEBP_EXTERN(int) VP8CheckSignature(const uint8_t* const data, size_t data_size); // Validates the VP8 data-header and retrieves basic header information viz // width and height. Returns 0 in case of formatting error. *width/*height // can be passed NULL. WEBP_EXTERN(int) VP8GetInfo( const uint8_t* data, size_t data_size, // data available so far size_t chunk_size, // total data size expected in the chunk int* const width, int* const height); // Returns true if the next byte(s) in data is a VP8L signature. WEBP_EXTERN(int) VP8LCheckSignature(const uint8_t* const data, size_t size); // Validates the VP8L data-header and retrieves basic header information viz // width, height and alpha. Returns 0 in case of formatting error. // width/height/has_alpha can be passed NULL. WEBP_EXTERN(int) VP8LGetInfo( const uint8_t* data, size_t data_size, // data available so far int* const width, int* const height, int* const has_alpha); #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_WEBP_DECODE_VP8_H_ */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/CONTRIBUTORS.md000664 001750 001750 00000004352 15164251010 031500 0ustar00ddennedyddennedy000000 000000 ## Project Lead - Hermet Park ## Core Contributors - Pranay Samanta - Junsu Choi - Mira Grudzinska - Ruiwen Tang - Sergii Liebodkin - Jinny You - Jay WenJie ## Contributors - Prudhvi Raj Vasireddi - Mateusz Palkowski - Subhransu Mohanty - Michal Szczecinski - Shinwoo Kim - Piotr Kalota - Vincent Torri - Pankaj Kumar - Patryk Kaczmarek - Michal Maciola - Peter Vullings - K. S. Ernest (iFire) Lee - Rémi Verschelde - Martin Liska - Vincenzo Pupillo - EunSik Jeong - Rafał Mikrut - Martin Capitanio - YouJin Lee - Nattu Adnan - Gabor Kiss-Vamosi - Lorcán Mc Donagh - Lucas Niu - Francisco Ramírez - Abdelrahman Ashraf - Neo Xu - Thaddeus Crews - Josh Soref - Elliott Sales de Andrade - Łukasz Pomietło - Kelly Loh - Dragoș Tiselice - Marcin Baszczewski - Fabian Blatz - Jakub Marcowski - Benjamin Halko - Benson Muite - kkocdko - SoonGeon Noh - Giseong Ji - KunYoung Park - Wang SiMiao 王思淼 - Jackson Hu - Andy French - Andreas Deininger - Jonathan Liu loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-map-iterator-prototype.cpp000664 001750 001750 00000006210 15164251010 053042 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-container-object.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BUILTIN_MAP_ITERATOR_PROTOTYPE_ROUTINE_START = 0, ECMA_BUILTIN_MAP_ITERATOR_PROTOTYPE_ROUTINE_OBJECT_NEXT, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-map-iterator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID map_iterator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup %mapiteratorprototype% ECMA %MapIteratorPrototype% object built-in * @{ */ /** * The %MapIteratorPrototype% object's 'next' routine * * See also: * ECMA-262 v6, 23.1.5.2.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object, if success * error - otherwise */ static ecma_value_t ecma_builtin_map_iterator_prototype_object_next (ecma_value_t this_val) /**< this argument */ { return ecma_op_container_iterator_next (this_val, ECMA_OBJECT_CLASS_MAP_ITERATOR); } /* ecma_builtin_map_iterator_prototype_object_next */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_map_iterator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide * routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_list_p, arguments_number); switch (builtin_routine_id) { case ECMA_BUILTIN_MAP_ITERATOR_PROTOTYPE_ROUTINE_OBJECT_NEXT: { return ecma_builtin_map_iterator_prototype_object_next (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_map_iterator_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/meson.build000664 001750 001750 00000005475 15164251010 032161 0ustar00ddennedyddennedy000000 000000 compiler_flags = [] override_options = [] lib_type = get_option('default_library') if lib_type == 'shared' compiler_flags += ['-DTVG_EXPORT', '-DTVG_BUILD'] else compiler_flags += ['-DTVG_STATIC'] endif cc = meson.get_compiler('cpp') if cc.get_id() == 'clang-cl' if simd_type == 'avx' compiler_flags += ['/clang:-mavx'] endif if simd_type == 'neon-arm' compiler_flags += ['/clang:-mfpu=neon'] endif compiler_flags += ['/clang:-Wno-unknown-pragmas'] if logging == false compiler_flags += ['/clang:-Wdouble-promotion'] endif if get_option('b_sanitize') == 'none' override_options += ['cpp_eh=none','cpp_rtti=false'] compiler_flags += ['/clang:-fno-math-errno', '/clang:-Woverloaded-virtual', '/clang:-fno-stack-protector', '/clang:-fno-unwind-tables' , '/clang:-fno-asynchronous-unwind-tables'] endif elif (cc.get_id() == 'msvc') compiler_flags += ['-DNOMINMAX'] else if simd_type == 'avx' compiler_flags += ['-mavx'] endif if simd_type == 'neon-arm' compiler_flags += ['-mfpu=neon'] endif compiler_flags += ['-Wno-unknown-pragmas'] if logging == false compiler_flags += ['-Wdouble-promotion'] endif if get_option('b_sanitize') == 'none' compiler_flags += ['-fno-exceptions', '-fno-rtti', '-fno-stack-protector', '-fno-math-errno', '-fno-unwind-tables', '-fno-asynchronous-unwind-tables', '-Woverloaded-virtual'] endif endif subdir('common') subdir('renderer') subdir('loaders') subdir('savers') thorvg_lib_dep = [common_dep, utils_dep, loader_dep, saver_dep] if get_option('threads') and host_machine.system() != 'windows' and host_machine.system() != 'android' thread_dep = meson.get_compiler('cpp').find_library('pthread') thorvg_lib_dep += [thread_dep] endif subdir('bindings') thorvg_lib = library( 'thorvg-' + vmaj, include_directories : headers, version : meson.project_version(), soversion : host_machine.system() != 'windows' ? vmaj : '', dependencies : thorvg_lib_dep, install : true, cpp_args : compiler_flags, gnu_symbol_visibility : 'hidden', override_options : override_options ) thorvg_dep = declare_dependency( compile_args: (lib_type == 'static') ? ['-DTVG_STATIC'] : [], include_directories: thorvg_inc, link_with: thorvg_lib, ) meson.override_dependency('thorvg-' + vmaj, thorvg_dep) pkg_mod = import('pkgconfig') pkg_mod.generate( libraries : thorvg_lib, version : meson.project_version(), name : 'libthorvg', filebase : 'thorvg-' + vmaj, subdirs : 'thorvg-' + vmaj, description : 'A Thor library for rendering vector graphics' ) glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jrt/meson.build000664 001750 001750 00000000411 15164251010 042254 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'jrt-bit-fields.h', 'jrt-libc-includes.h', 'jrt-types.h', 'jrt.h', 'jrt-fatals.cpp', 'jrt-logging.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgShaderTypes.cpp000664 001750 001750 00000023362 15164251010 037461 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgShaderTypes.h" #include #include "tvgMath.h" #include "tvgFill.h" //************************************************************************ // WgShaderTypeMat4x4f //************************************************************************ WgShaderTypeMat4x4f::WgShaderTypeMat4x4f() { identity(); } WgShaderTypeMat4x4f::WgShaderTypeMat4x4f(const Matrix& transform) { update(transform); } void WgShaderTypeMat4x4f::identity() { mat[0] = 1.0f; mat[1] = 0.0f; mat[2] = 0.0f; mat[3] = 0.0f; mat[4] = 0.0f; mat[5] = 1.0f; mat[6] = 0.0f; mat[7] = 0.0f; mat[8] = 0.0f; mat[9] = 0.0f; mat[10] = 1.0f; mat[11] = 0.0f; mat[12] = 0.0f; mat[13] = 0.0f; mat[14] = 0.0f; mat[15] = 1.0f; } WgShaderTypeMat4x4f::WgShaderTypeMat4x4f(size_t w, size_t h) { update(w, h); } void WgShaderTypeMat4x4f::update(const Matrix& transform) { mat[0] = transform.e11; mat[1] = transform.e21; mat[2] = 0.0f; mat[3] = transform.e31; mat[4] = transform.e12; mat[5] = transform.e22; mat[6] = 0.0f; mat[7] = transform.e32; mat[8] = 0.0f; mat[9] = 0.0f; mat[10] = 1.0f; mat[11] = 0.0f; mat[12] = transform.e13; mat[13] = transform.e23; mat[14] = 0.0f; mat[15] = transform.e33; } void WgShaderTypeMat4x4f::update(size_t w, size_t h) { mat[0] = +2.0f / w; mat[1] = +0.0f; mat[2] = +0.0f; mat[3] = +0.0f; mat[4] = +0.0f; mat[5] = -2.0f / h; mat[6] = +0.0f; mat[7] = +0.0f; mat[8] = +0.0f; mat[9] = +0.0f; mat[10] = -1.0f; mat[11] = +0.0f; mat[12] = -1.0f; mat[13] = +1.0f; mat[14] = +0.0f; mat[15] = +1.0f; } //************************************************************************ // WgShaderTypeVec4f //************************************************************************ WgShaderTypeVec4f::WgShaderTypeVec4f(const ColorSpace colorSpace, uint8_t o) { update(colorSpace, o); } WgShaderTypeVec4f::WgShaderTypeVec4f(const RenderColor& c) { update(c); } void WgShaderTypeVec4f::update(const ColorSpace colorSpace, uint8_t o) { vec[0] = (uint32_t)colorSpace; vec[3] = o / 255.0f; } void WgShaderTypeVec4f::update(const RenderColor& c) { vec[0] = c.r / 255.0f; // red vec[1] = c.g / 255.0f; // green vec[2] = c.b / 255.0f; // blue vec[3] = c.a / 255.0f; // alpha } void WgShaderTypeVec4f::update(const RenderRegion& r) { vec[0] = r.min.x; vec[1] = r.min.y; vec[2] = r.max.x - 1; vec[3] = r.max.y - 1; } //************************************************************************ // WgShaderTypeGradSettings //************************************************************************ void WgShaderTypeGradSettings::update(const Fill* fill) { assert(fill); // update transform matrix Matrix invTransform; if (inverse(&fill->transform(), &invTransform)) transform.update(invTransform); else transform.identity(); // update gradient base points if (fill->type() == Type::LinearGradient) ((LinearGradient*)fill)->linear(&coords.vec[0], &coords.vec[1], &coords.vec[2], &coords.vec[3]); else if (fill->type() == Type::RadialGradient) { ((RadialGradient*)fill)->radial(&coords.vec[0], &coords.vec[1], &coords.vec[2], &focal.vec[0], &focal.vec[1], &focal.vec[2]); CONST_RADIAL(fill)->correct(focal.vec[0], focal.vec[1], focal.vec[2]); } } //************************************************************************ // WgShaderTypeGradientData //************************************************************************ void WgShaderTypeGradientData::update(const Fill* fill) { if (!fill) return; const Fill::ColorStop* stops = nullptr; auto stopCnt = fill->colorStops(&stops); if (stopCnt == 0) return; static Array sstops(stopCnt); sstops.clear(); sstops.push(stops[0]); // filter by increasing offset for (uint32_t i = 1; i < stopCnt; i++) if (sstops.last().offset < stops[i].offset) sstops.push(stops[i]); else if (sstops.last().offset == stops[i].offset) sstops.last() = stops[i]; // head uint32_t range_s = 0; uint32_t range_e = uint32_t(sstops[0].offset * (WG_TEXTURE_GRADIENT_SIZE-1)); for (uint32_t ti = range_s; (ti < range_e) && (ti < WG_TEXTURE_GRADIENT_SIZE); ti++) { data[ti * 4 + 0] = sstops[0].r; data[ti * 4 + 1] = sstops[0].g; data[ti * 4 + 2] = sstops[0].b; data[ti * 4 + 3] = sstops[0].a; } // body for (uint32_t di = 1; di < sstops.count; di++) { range_s = uint32_t(sstops[di-1].offset * (WG_TEXTURE_GRADIENT_SIZE-1)); range_e = uint32_t(sstops[di-0].offset * (WG_TEXTURE_GRADIENT_SIZE-1)); float delta = 1.0f/(range_e - range_s); for (uint32_t ti = range_s; (ti < range_e) && (ti < WG_TEXTURE_GRADIENT_SIZE); ti++) { float t = (ti - range_s) * delta; data[ti * 4 + 0] = tvg::lerp(sstops[di-1].r, sstops[di].r, t); data[ti * 4 + 1] = tvg::lerp(sstops[di-1].g, sstops[di].g, t); data[ti * 4 + 2] = tvg::lerp(sstops[di-1].b, sstops[di].b, t); data[ti * 4 + 3] = tvg::lerp(sstops[di-1].a, sstops[di].a, t); } } // tail const tvg::Fill::ColorStop& colorStopLast = sstops.last(); range_s = uint32_t(colorStopLast.offset * (WG_TEXTURE_GRADIENT_SIZE-1)); range_e = WG_TEXTURE_GRADIENT_SIZE; for (uint32_t ti = range_s; ti < range_e; ti++) { data[ti * 4 + 0] = colorStopLast.r; data[ti * 4 + 1] = colorStopLast.g; data[ti * 4 + 2] = colorStopLast.b; data[ti * 4 + 3] = colorStopLast.a; } } //************************************************************************ // WgShaderTypeEffectParams //************************************************************************ bool WgShaderTypeEffectParams::update(RenderEffectGaussianBlur* gaussian, const Matrix& transform) { assert(gaussian); params[0] = gaussian->sigma; params[1] = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); params[2] = 2 * gaussian->sigma * params[1]; extend = params[2] * 2; // kernel gaussian->valid = (extend > 0); return gaussian->valid; } bool WgShaderTypeEffectParams::update(RenderEffectDropShadow* dropShadow, const Matrix& transform) { assert(dropShadow); const auto scale = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); const auto kernel = 2 * dropShadow->sigma * scale; const auto radian = tvg::deg2rad(90.0f - dropShadow->angle) - tvg::radian(transform); const Point offset = {dropShadow->distance * cosf(radian) * scale, -dropShadow->distance * sinf(radian) * scale}; params[0] = dropShadow->sigma; params[1] = scale; params[2] = kernel; params[3] = 0.0f; params[7] = dropShadow->color[3] / 255.0f; // alpha //Color is premultiplied to avoid multiplication in the fragment shader: params[4] = dropShadow->color[0] / 255.0f * params[7]; // red params[5] = dropShadow->color[1] / 255.0f * params[7]; // green params[6] = dropShadow->color[2] / 255.0f * params[7]; // blue params[8] = offset.x; params[9] = offset.y; extend = 2 * std::max(dropShadow->sigma * scale + std::abs(offset.x), dropShadow->sigma * scale + std::abs(offset.y)); dropShadow->valid = (extend >= 0); return dropShadow->valid; } bool WgShaderTypeEffectParams::update(RenderEffectFill* fill) { params[0] = fill->color[0] / 255.0f; params[1] = fill->color[1] / 255.0f; params[2] = fill->color[2] / 255.0f; params[3] = fill->color[3] / 255.0f; fill->valid = true; return true; } bool WgShaderTypeEffectParams::update(RenderEffectTint* tint) { params[0] = tint->black[0] / 255.0f; params[1] = tint->black[1] / 255.0f; params[2] = tint->black[2] / 255.0f; params[3] = 0.0f; params[4] = tint->white[0] / 255.0f; params[5] = tint->white[1] / 255.0f; params[6] = tint->white[2] / 255.0f; params[7] = 0.0f; params[8] = tint->intensity / 255.0f; tint->valid = (tint->intensity > 0); return tint->valid; } bool WgShaderTypeEffectParams::update(RenderEffectTritone* tritone) { params[0] = tritone->shadow[0] / 255.0f; params[1] = tritone->shadow[1] / 255.0f; params[2] = tritone->shadow[2] / 255.0f; params[3] = 0.0f; params[4] = tritone->midtone[0] / 255.0f; params[5] = tritone->midtone[1] / 255.0f; params[6] = tritone->midtone[2] / 255.0f; params[7] = 0.0f; params[8] = tritone->highlight[0] / 255.0f; params[9] = tritone->highlight[1] / 255.0f; params[10] = tritone->highlight[2] / 255.0f; params[11] = tritone->blender / 255.0f; tritone->valid = tritone->blender < 255; return true; } modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/dtoa.h000664 001750 001750 00000020431 15164251010 037563 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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 uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL, 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, 10000000000000000000ULL }; const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); const DiyFp wp_w = Mp - W; uint32_t p1 = static_cast(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('0' + static_cast(d)); kappa--; uint64_t tmp = (static_cast(p1) << -one.e) + p2; if (tmp <= delta) { *K += kappa; GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f); return; } } // kappa = 0 for (;;) { p2 *= 10; delta *= 10; char d = static_cast(p2 >> -one.e); if (d || *len) buffer[(*len)++] = static_cast('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 < 20 ? 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('0' + static_cast(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('0' + static_cast(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(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(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(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_ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jmem/jmem-poolman.cpp000664 001750 001750 00000006141 15164251010 043350 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /** * Memory pool manager implementation */ #include "jcontext.h" #include "jmem.h" #include "jrt-libc-includes.h" #define JMEM_ALLOCATOR_INTERNAL #include "jmem-allocator-internal.h" /** \addtogroup mem Memory allocation * @{ * * \addtogroup poolman Memory pool manager * @{ */ /** * Finalize pool manager */ void jmem_pools_finalize (void) { jmem_pools_collect_empty (); JERRY_ASSERT (JERRY_CONTEXT (jmem_free_8_byte_chunk_p) == NULL); } /* jmem_pools_finalize */ /** * Allocate a chunk of specified size * * @return pointer to allocated chunk, if allocation was successful, * or NULL - if not enough memory. */ void *JERRY_ATTR_HOT jmem_pools_alloc (size_t size) /**< size of the chunk */ { JERRY_ASSERT (size <= 8); if (JERRY_CONTEXT (jmem_free_8_byte_chunk_p) != NULL) { const jmem_pools_chunk_t *const chunk_p = JERRY_CONTEXT (jmem_free_8_byte_chunk_p); JMEM_VALGRIND_DEFINED_SPACE (chunk_p, sizeof (jmem_pools_chunk_t)); JERRY_CONTEXT (jmem_free_8_byte_chunk_p) = chunk_p->next_p; JMEM_VALGRIND_UNDEFINED_SPACE (chunk_p, sizeof (jmem_pools_chunk_t)); JMEM_HEAP_STAT_ALLOC (8); return (void *) chunk_p; } else { void *chunk_p = jmem_heap_alloc_block_internal (8); JMEM_HEAP_STAT_ALLOC (8); return chunk_p; } } /* jmem_pools_alloc */ /** * Free the chunk * * @return void */ void JERRY_ATTR_HOT jmem_pools_free (void *chunk_p, /**< pointer to the chunk */ size_t size) /**< size of the chunk */ { JERRY_ASSERT (chunk_p != NULL); JMEM_HEAP_STAT_FREE (size); jmem_pools_chunk_t *const chunk_to_free_p = (jmem_pools_chunk_t *) chunk_p; JMEM_VALGRIND_DEFINED_SPACE (chunk_to_free_p, size); JERRY_ASSERT (size <= 8); chunk_to_free_p->next_p = JERRY_CONTEXT (jmem_free_8_byte_chunk_p); JERRY_CONTEXT (jmem_free_8_byte_chunk_p) = chunk_to_free_p; JMEM_VALGRIND_NOACCESS_SPACE (chunk_to_free_p, size); } /* jmem_pools_free */ /** * Collect empty pool chunks */ void jmem_pools_collect_empty (void) { jmem_pools_chunk_t *chunk_p = JERRY_CONTEXT (jmem_free_8_byte_chunk_p); JERRY_CONTEXT (jmem_free_8_byte_chunk_p) = NULL; while (chunk_p) { JMEM_VALGRIND_DEFINED_SPACE (chunk_p, sizeof (jmem_pools_chunk_t)); jmem_pools_chunk_t *const next_p = chunk_p->next_p; JMEM_VALGRIND_NOACCESS_SPACE (chunk_p, sizeof (jmem_pools_chunk_t)); jmem_heap_free_block_internal (chunk_p, 8); chunk_p = next_p; } } /* jmem_pools_collect_empty */ /** * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-boolean-object.h000664 001750 001750 00000001677 15164251010 046361 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BOOLEAN_OBJECT_H #define ECMA_BOOLEAN_OBJECT_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabooleanobject ECMA Boolean object related routines * @{ */ ecma_value_t ecma_op_create_boolean_object (ecma_value_t arg); /** * @} * @} */ #endif /* !ECMA_BOOLEAN_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/meson.build000664 001750 001750 00000000664 15164251010 034404 0ustar00ddennedyddennedy000000 000000 source_file = [ 'tvgSvgCssStyle.h', 'tvgSvgLoader.h', 'tvgSvgLoaderCommon.h', 'tvgSvgPath.h', 'tvgSvgSceneBuilder.h', 'tvgSvgUtil.h', 'tvgXmlParser.h', 'tvgSvgCssStyle.cpp', 'tvgSvgLoader.cpp', 'tvgSvgPath.cpp', 'tvgSvgSceneBuilder.cpp', 'tvgSvgUtil.cpp', 'tvgXmlParser.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-regexp.cpp000664 001750 001750 00000013700 15164251010 047667 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-regexp-object.h" #include "jcontext.h" #if JERRY_BUILTIN_REGEXP #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-regexp.inc.h" #define BUILTIN_UNDERSCORED_ID regexp #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup regexp ECMA RegExp object built-in * @{ */ static ecma_value_t ecma_builtin_regexp_dispatch_helper (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_value_t pattern_value = ECMA_VALUE_UNDEFINED; ecma_value_t flags_value = ECMA_VALUE_UNDEFINED; bool create_regexp_from_bc = false; bool free_arguments = false; ecma_object_t *new_target_p = JERRY_CONTEXT (current_new_target_p); if (arguments_list_len > 0) { /* pattern string or RegExp object */ pattern_value = arguments_list_p[0]; if (arguments_list_len > 1) { flags_value = arguments_list_p[1]; } } ecma_value_t regexp_value = ecma_op_is_regexp (pattern_value); if (ECMA_IS_VALUE_ERROR (regexp_value)) { return regexp_value; } bool pattern_is_regexp = regexp_value == ECMA_VALUE_TRUE; re_compiled_code_t *bc_p = NULL; if (new_target_p == NULL) { new_target_p = ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP); if (pattern_is_regexp && ecma_is_value_undefined (flags_value)) { ecma_object_t *pattern_obj_p = ecma_get_object_from_value (pattern_value); ecma_value_t pattern_constructor = ecma_op_object_get_by_magic_id (pattern_obj_p, LIT_MAGIC_STRING_CONSTRUCTOR); if (ECMA_IS_VALUE_ERROR (pattern_constructor)) { return pattern_constructor; } bool is_same = ecma_op_same_value (ecma_make_object_value (new_target_p), pattern_constructor); ecma_free_value (pattern_constructor); if (is_same) { return ecma_copy_value (pattern_value); } } } if (ecma_object_is_regexp_object (pattern_value)) { ecma_extended_object_t *pattern_obj_p = (ecma_extended_object_t *) ecma_get_object_from_value (pattern_value); bc_p = ECMA_GET_INTERNAL_VALUE_POINTER (re_compiled_code_t, pattern_obj_p->u.cls.u3.value); create_regexp_from_bc = ecma_is_value_undefined (flags_value); if (!create_regexp_from_bc) { pattern_value = bc_p->source; } } else if (pattern_is_regexp) { ecma_object_t *pattern_obj_p = ecma_get_object_from_value (pattern_value); pattern_value = ecma_op_object_get_by_magic_id (pattern_obj_p, LIT_MAGIC_STRING_SOURCE); if (ECMA_IS_VALUE_ERROR (pattern_value)) { return pattern_value; } if (ecma_is_value_undefined (flags_value)) { flags_value = ecma_op_object_get_by_magic_id (pattern_obj_p, LIT_MAGIC_STRING_FLAGS); if (ECMA_IS_VALUE_ERROR (flags_value)) { ecma_free_value (pattern_value); return flags_value; } } else { flags_value = ecma_copy_value (flags_value); } free_arguments = true; } ecma_value_t ret_value = ECMA_VALUE_ERROR; ecma_object_t *new_target_obj_p = ecma_op_regexp_alloc (new_target_p); if (JERRY_LIKELY (new_target_obj_p != NULL)) { if (create_regexp_from_bc) { ret_value = ecma_op_create_regexp_from_bytecode (new_target_obj_p, bc_p); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (ret_value)); } else { ret_value = ecma_op_create_regexp_from_pattern (new_target_obj_p, pattern_value, flags_value); if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_deref_object (new_target_obj_p); } } } if (free_arguments) { ecma_free_value (pattern_value); ecma_free_value (flags_value); } return ret_value; } /* ecma_builtin_regexp_dispatch_helper */ /** * Handle calling [[Call]] of built-in RegExp object * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_regexp_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_regexp_dispatch_helper (arguments_list_p, arguments_list_len); } /* ecma_builtin_regexp_dispatch_call */ /** * Handle calling [[Construct]] of built-in RegExp object * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_regexp_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_regexp_dispatch_helper (arguments_list_p, arguments_list_len); } /* ecma_builtin_regexp_dispatch_construct */ /** * 21.2.4.2 get RegExp [ @@species ] accessor * * @return ecma_value * returned value must be freed with ecma_free_value */ ecma_value_t ecma_builtin_regexp_species_get (ecma_value_t this_value) /**< This Value */ { return ecma_copy_value (this_value); } /* ecma_builtin_regexp_species_get */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/allocators.h000664 001750 001750 00000054104 15164251010 037167 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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" #include "internal/meta.h" #include #include #if RAPIDJSON_HAS_CXX11 #include #endif 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 */ /*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY \ingroup RAPIDJSON_CONFIG \brief User-defined kDefaultChunkCapacity definition. User can define this as any \c size that is a power of 2. */ #ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY #define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) #endif /////////////////////////////////////////////////////////////////////////////// // 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 RAPIDJSON_MALLOC(size); else return NULL; // standardize to returning NULL. } void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; if (newSize == 0) { RAPIDJSON_FREE(originalPtr); return NULL; } return RAPIDJSON_REALLOC(originalPtr, newSize); } static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); } bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { return true; } bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { return false; } }; /////////////////////////////////////////////////////////////////////////////// // 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 class MemoryPoolAllocator { //! 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. }; struct SharedData { ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation. BaseAllocator* ownBaseAllocator; //!< base allocator created by this object. size_t refcount; bool ownBuffer; }; static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData)); static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader)); static inline ChunkHeader *GetChunkHead(SharedData *shared) { return reinterpret_cast(reinterpret_cast(shared) + SIZEOF_SHARED_DATA); } static inline uint8_t *GetChunkBuffer(SharedData *shared) { return reinterpret_cast(shared->chunkHead) + SIZEOF_CHUNK_HEADER; } static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. public: static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy //! Constructor with chunkSize. /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. \param baseAllocator The allocator for allocating memory chunks. */ explicit MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : chunk_capacity_(chunkSize), baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()), shared_(static_cast(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0)) { RAPIDJSON_ASSERT(baseAllocator_ != 0); RAPIDJSON_ASSERT(shared_ != 0); if (baseAllocator) { shared_->ownBaseAllocator = 0; } else { shared_->ownBaseAllocator = baseAllocator_; } shared_->chunkHead = GetChunkHead(shared_); shared_->chunkHead->capacity = 0; shared_->chunkHead->size = 0; shared_->chunkHead->next = 0; shared_->ownBuffer = true; shared_->refcount = 1; } //! 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) : chunk_capacity_(chunkSize), baseAllocator_(baseAllocator), shared_(static_cast(AlignBuffer(buffer, size))) { RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER); shared_->chunkHead = GetChunkHead(shared_); shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER; shared_->chunkHead->size = 0; shared_->chunkHead->next = 0; shared_->ownBaseAllocator = 0; shared_->ownBuffer = false; shared_->refcount = 1; } MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT : chunk_capacity_(rhs.chunk_capacity_), baseAllocator_(rhs.baseAllocator_), shared_(rhs.shared_) { RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); ++shared_->refcount; } MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT { RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); ++rhs.shared_->refcount; this->~MemoryPoolAllocator(); baseAllocator_ = rhs.baseAllocator_; chunk_capacity_ = rhs.chunk_capacity_; shared_ = rhs.shared_; return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT : chunk_capacity_(rhs.chunk_capacity_), baseAllocator_(rhs.baseAllocator_), shared_(rhs.shared_) { RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); rhs.shared_ = 0; } MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT { RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); this->~MemoryPoolAllocator(); baseAllocator_ = rhs.baseAllocator_; chunk_capacity_ = rhs.chunk_capacity_; shared_ = rhs.shared_; rhs.shared_ = 0; return *this; } #endif //! Destructor. /*! This deallocates all memory chunks, excluding the user-supplied buffer. */ ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT { if (!shared_) { // do nothing if moved return; } if (shared_->refcount > 1) { --shared_->refcount; return; } Clear(); BaseAllocator *a = shared_->ownBaseAllocator; if (shared_->ownBuffer) { baseAllocator_->Free(shared_); } RAPIDJSON_DELETE(a); } //! Deallocates all memory chunks, excluding the first/user one. void Clear() RAPIDJSON_NOEXCEPT { RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); for (;;) { ChunkHeader* c = shared_->chunkHead; if (!c->next) { break; } shared_->chunkHead = c->next; baseAllocator_->Free(c); } shared_->chunkHead->size = 0; } //! Computes the total capacity of allocated memory chunks. /*! \return total capacity in bytes. */ size_t Capacity() const RAPIDJSON_NOEXCEPT { RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); size_t capacity = 0; for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) capacity += c->capacity; return capacity; } //! Computes the memory blocks allocated. /*! \return total used bytes. */ size_t Size() const RAPIDJSON_NOEXCEPT { RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); size_t size = 0; for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) size += c->size; return size; } //! Whether the allocator is shared. /*! \return true or false. */ bool Shared() const RAPIDJSON_NOEXCEPT { RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); return shared_->refcount > 1; } //! Allocates a memory block. (concept Allocator) void* Malloc(size_t size) { RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); if (!size) return NULL; size = RAPIDJSON_ALIGN(size); if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity)) if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) return NULL; void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size; shared_->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); RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); 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 == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) { size_t increment = static_cast(newSize - originalSize); if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) { shared_->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) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing //! Compare (equality) with another MemoryPoolAllocator bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); return shared_ == rhs.shared_; } //! Compare (inequality) with another MemoryPoolAllocator bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { return !operator==(rhs); } private: //! Creates a new chunk. /*! \param capacity Capacity of the chunk in bytes. \return true if success. */ bool AddChunk(size_t capacity) { if (!baseAllocator_) shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); if (ChunkHeader* chunk = static_cast(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) { chunk->capacity = capacity; chunk->size = 0; chunk->next = shared_->chunkHead; shared_->chunkHead = chunk; return true; } else return false; } static inline void* AlignBuffer(void* buf, size_t &size) { RAPIDJSON_NOEXCEPT_ASSERT(buf != 0); const uintptr_t mask = sizeof(void*) - 1; const uintptr_t ubuf = reinterpret_cast(buf); if (RAPIDJSON_UNLIKELY(ubuf & mask)) { const uintptr_t abuf = (ubuf + mask) & ~mask; RAPIDJSON_ASSERT(size >= abuf - ubuf); buf = reinterpret_cast(abuf); size -= abuf - ubuf; } return buf; } size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. SharedData *shared_; //!< The shared data of the allocator }; namespace internal { template struct IsRefCounted : public FalseType { }; template struct IsRefCounted::Type> : public TrueType { }; } template inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n) { RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits::max)() / sizeof(T) && new_n <= (std::numeric_limits::max)() / sizeof(T)); return static_cast(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T))); } template inline T *Malloc(A& a, size_t n = 1) { return Realloc(a, NULL, 0, n); } template inline void Free(A& a, T *p, size_t n = 1) { static_cast(Realloc(a, p, n, 0)); } #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited #endif template class StdAllocator : public std::allocator { typedef std::allocator allocator_type; #if RAPIDJSON_HAS_CXX11 typedef std::allocator_traits traits_type; #else typedef allocator_type traits_type; #endif public: typedef BaseAllocator BaseAllocatorType; StdAllocator() RAPIDJSON_NOEXCEPT : allocator_type(), baseAllocator_() { } StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : allocator_type(rhs), baseAllocator_(rhs.baseAllocator_) { } template StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : allocator_type(rhs), baseAllocator_(rhs.baseAllocator_) { } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT : allocator_type(std::move(rhs)), baseAllocator_(std::move(rhs.baseAllocator_)) { } #endif #if RAPIDJSON_HAS_CXX11 using propagate_on_container_move_assignment = std::true_type; using propagate_on_container_swap = std::true_type; #endif /* implicit */ StdAllocator(const BaseAllocator& allocator) RAPIDJSON_NOEXCEPT : allocator_type(), baseAllocator_(allocator) { } ~StdAllocator() RAPIDJSON_NOEXCEPT { } template struct rebind { typedef StdAllocator other; }; typedef typename traits_type::size_type size_type; typedef typename traits_type::difference_type difference_type; typedef typename traits_type::value_type value_type; typedef typename traits_type::pointer pointer; typedef typename traits_type::const_pointer const_pointer; #if RAPIDJSON_HAS_CXX11 typedef typename std::add_lvalue_reference::type &reference; typedef typename std::add_lvalue_reference::type>::type &const_reference; pointer address(reference r) const RAPIDJSON_NOEXCEPT { return std::addressof(r); } const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT { return std::addressof(r); } size_type max_size() const RAPIDJSON_NOEXCEPT { return traits_type::max_size(*this); } template void construct(pointer p, Args&&... args) { traits_type::construct(*this, p, std::forward(args)...); } void destroy(pointer p) { traits_type::destroy(*this, p); } #else // !RAPIDJSON_HAS_CXX11 typedef typename allocator_type::reference reference; typedef typename allocator_type::const_reference const_reference; pointer address(reference r) const RAPIDJSON_NOEXCEPT { return allocator_type::address(r); } const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT { return allocator_type::address(r); } size_type max_size() const RAPIDJSON_NOEXCEPT { return allocator_type::max_size(); } void construct(pointer p, const_reference r) { allocator_type::construct(p, r); } void destroy(pointer p) { allocator_type::destroy(p); } #endif // !RAPIDJSON_HAS_CXX11 template U* allocate(size_type n = 1, const void* = 0) { return RAPIDJSON_NAMESPACE::Malloc(baseAllocator_, n); } template void deallocate(U* p, size_type n = 1) { RAPIDJSON_NAMESPACE::Free(baseAllocator_, p, n); } pointer allocate(size_type n = 1, const void* = 0) { return allocate(n); } void deallocate(pointer p, size_type n = 1) { deallocate(p, n); } #if RAPIDJSON_HAS_CXX11 using is_always_equal = std::is_empty; #endif template bool operator==(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT { return baseAllocator_ == rhs.baseAllocator_; } template bool operator!=(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT { return !operator==(rhs); } //! rapidjson Allocator concept static const bool kNeedFree = BaseAllocator::kNeedFree; static const bool kRefCounted = internal::IsRefCounted::Value; void* Malloc(size_t size) { return baseAllocator_.Malloc(size); } void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { return baseAllocator_.Realloc(originalPtr, originalSize, newSize); } static void Free(void *ptr) RAPIDJSON_NOEXCEPT { BaseAllocator::Free(ptr); } private: template friend class StdAllocator; // access to StdAllocator.* BaseAllocator baseAllocator_; }; #if !RAPIDJSON_HAS_CXX17 // std::allocator deprecated in C++17 template class StdAllocator : public std::allocator { typedef std::allocator allocator_type; public: typedef BaseAllocator BaseAllocatorType; StdAllocator() RAPIDJSON_NOEXCEPT : allocator_type(), baseAllocator_() { } StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : allocator_type(rhs), baseAllocator_(rhs.baseAllocator_) { } template StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : allocator_type(rhs), baseAllocator_(rhs.baseAllocator_) { } /* implicit */ StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : allocator_type(), baseAllocator_(baseAllocator) { } ~StdAllocator() RAPIDJSON_NOEXCEPT { } template struct rebind { typedef StdAllocator other; }; typedef typename allocator_type::value_type value_type; private: template friend class StdAllocator; // access to StdAllocator.* BaseAllocator baseAllocator_; }; #endif #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_ENCODINGS_H_ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-set-prototype.cpp000664 001750 001750 00000004326 15164251010 051237 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-container-object.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH #define BUILTIN_INC_HEADER_NAME "ecma-builtin-set-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID set_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup set ECMA Set object built-in * @{ */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_set_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); return ecma_builtin_container_dispatch_routine (builtin_routine_id, this_arg, arguments_list_p, LIT_MAGIC_STRING_SET_UL); } /* ecma_builtin_set_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieRenderPooler.h000664 001750 001750 00000003501 15164251010 037330 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_RENDER_POOLER_H_ #define _TVG_LOTTIE_RENDER_POOLER_H_ #include "tvgCommon.h" #include "tvgArray.h" #include "tvgPaint.h" template struct LottieRenderPooler { Array pooler; ~LottieRenderPooler() { ARRAY_FOREACH(p, pooler) { (*p)->unref(); } } T* pooling(bool copy = false) { //return available one. ARRAY_FOREACH(p, pooler) { if ((*p)->refCnt() == 1) return *p; } //no empty, generate a new one. auto p = copy ? static_cast(pooler[0]->duplicate()) : T::gen(); p->ref(); pooler.push(p); return p; } }; #endif //_TVG_LOTTIE_RENDER_POOLER_H_src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlShaderSrc.cpp000664 001750 001750 00000173040 15164251010 037055 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlShaderSrc.h" #define TVG_COMPOSE_SHADER(shader) #shader const char* COLOR_VERT_SHADER = TVG_COMPOSE_SHADER( uniform float uDepth; \n uniform mat3 uViewMatrix; \n layout(location = 0) in vec2 aLocation; \n layout(std140) uniform SolidInfo { \n vec4 solidColor; \n } uSolidInfo; \n \n void main() \n { \n vec3 pos = uViewMatrix * vec3(aLocation, 1.0); \n gl_Position = vec4(pos.xy, uDepth, 1.0); \n } \n ); const char* COLOR_FRAG_SHADER = TVG_COMPOSE_SHADER( layout(std140) uniform SolidInfo { \n vec4 solidColor; \n } uSolidInfo; \n out vec4 FragColor; \n \n void main() \n { \n vec4 uColor = uSolidInfo.solidColor; \n FragColor = vec4(uColor.rgb * uColor.a, uColor.a); \n } \n ); const char* GRADIENT_VERT_SHADER = TVG_COMPOSE_SHADER( uniform float uDepth; \n uniform mat3 uViewMatrix; \n layout(location = 0) in vec2 aLocation; \n out vec2 vPos; \n layout(std140) uniform TransformInfo { \n mat3 invTransform; \n } uTransformInfo; \n \n void main() \n { \n vec3 glPos = uViewMatrix * vec3(aLocation, 1.0); \n gl_Position = vec4(glPos.xy, uDepth, 1.0); \n vec3 pos = uTransformInfo.invTransform * vec3(aLocation, 1.0); \n vPos = pos.xy; \n } \n ); //See: GlRenderer::initShaders() const char* STR_GRADIENT_FRAG_COMMON_VARIABLES = TVG_COMPOSE_SHADER( const int MAX_STOP_COUNT = 16; \n in vec2 vPos; \n ); //See: GlRenderer::initShaders() const char* STR_GRADIENT_FRAG_COMMON_FUNCTIONS = TVG_COMPOSE_SHADER( float gradientStep(float edge0, float edge1, float x) \n { \n // linear \n x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); \n return x; \n } \n \n float gradientStop(int index) \n { \n if (index >= MAX_STOP_COUNT) index = MAX_STOP_COUNT - 1; \n int i = index / 4; \n int j = index % 4; \n return uGradientInfo.stopPoints[i][j]; \n } \n \n float gradientWrap(float d) \n { \n int spread = int(uGradientInfo.nStops[2]); \n if (spread == 0) return clamp(d, 0.0, 1.0); \n \n if (spread == 1) { /* Reflect */ \n float n = mod(d, 2.0); \n if (n > 1.0) { \n n = 2.0 - n; \n } \n return n; \n } \n if (spread == 2) { /* Repeat */ \n float n = mod(d, 1.0); \n if (n < 0.0) { \n n += 1.0 + n; \n } \n return n; \n } \n } \n \n vec4 gradient(float t, float d, float l) \n { \n float dist = d * 2.0 / l; \n vec4 col = vec4(0.0); \n int i = 0; \n int count = int(uGradientInfo.nStops[0]); \n if (t <= gradientStop(0)) { \n col = uGradientInfo.stopColors[0]; \n } else if (t >= gradientStop(count - 1)) { \n col = uGradientInfo.stopColors[count - 1]; \n if (int(uGradientInfo.nStops[2]) == 2 && (1.0 - t) < dist) { \n float dd = (1.0 - t) / dist; \n float alpha = dd; \n col *= alpha; \n col += uGradientInfo.stopColors[0] * (1. - alpha); \n } \n } else { \n for (i = 0; i < count - 1; ++i) { \n float stopi = gradientStop(i); \n float stopi1 = gradientStop(i + 1); \n if (t >= stopi && t <= stopi1) { \n col = (uGradientInfo.stopColors[i] * (1. - gradientStep(stopi, stopi1, t))); \n col += (uGradientInfo.stopColors[i + 1] * gradientStep(stopi, stopi1, t)); \n if (int(uGradientInfo.nStops[2]) == 2 && abs(d) > dist) { \n if (i == 0 && (t - stopi) < dist) { \n float dd = (t - stopi) / dist; \n float alpha = dd; \n col *= alpha; \n vec4 nc = uGradientInfo.stopColors[0] * (1.0 - (t - stopi)); \n nc += uGradientInfo.stopColors[count - 1] * (t - stopi); \n col += nc * (1.0 - alpha); \n } else if (i == count - 2 && (1.0 - t) < dist) { \n float dd = (1.0 - t) / dist; \n float alpha = dd; \n col *= alpha; \n col += (uGradientInfo.stopColors[0]) * (1.0 - alpha); \n } \n } \n break; \n } \n } \n } \n return col; \n } \n \n vec3 ScreenSpaceDither(vec2 vScreenPos) \n { \n vec3 vDither = vec3(dot(vec2(171.0, 231.0), vScreenPos.xy)); \n vDither.rgb = fract(vDither.rgb / vec3(103.0, 71.0, 97.0)); \n return vDither.rgb / 255.0; \n } \n ); //See: GlRenderer::initShaders() const char* STR_LINEAR_GRADIENT_VARIABLES = TVG_COMPOSE_SHADER( layout(std140) uniform GradientInfo { \n vec4 nStops; \n vec2 gradStartPos; \n vec2 gradEndPos; \n vec4 stopPoints[MAX_STOP_COUNT / 4]; \n vec4 stopColors[MAX_STOP_COUNT]; \n } uGradientInfo; \n ); //See: GlRenderer::initShaders() const char* STR_LINEAR_GRADIENT_MAIN = TVG_COMPOSE_SHADER( out vec4 FragColor; \n void main() \n { \n FragColor = linearGradientColor(vPos); \n } \n ); //See: GlRenderer::initShaders() const char* STR_LINEAR_GRADIENT_FUNCTIONS = TVG_COMPOSE_SHADER( vec4 linearGradientColor(vec2 pos) \n { \n vec2 st = uGradientInfo.gradStartPos; \n vec2 ed = uGradientInfo.gradEndPos; \n vec2 ba = ed - st; \n float d = dot(pos - st, ba) / dot(ba, ba); \n float t = gradientWrap(d); \n vec4 color = gradient(t, d, length(pos - st)); \n return vec4(color.rgb * color.a, color.a); \n } \n ); //See: GlRenderer::initShaders() const char* STR_RADIAL_GRADIENT_VARIABLES = TVG_COMPOSE_SHADER( layout(std140) uniform GradientInfo { \n vec4 nStops; \n vec4 centerPos; \n vec2 radius; \n vec4 stopPoints[MAX_STOP_COUNT / 4]; \n vec4 stopColors[MAX_STOP_COUNT]; \n } uGradientInfo ; \n ); //See: GlRenderer::initShaders() const char* STR_RADIAL_GRADIENT_MAIN = TVG_COMPOSE_SHADER( out vec4 FragColor; \n \n void main() \n { \n FragColor = radialGradientColor(vPos); \n } \n ); // TODO: Precompute radial_matrix, f, r1n, inv_r1, d_radius_sign, is_focal_on_circle, is_well_behaved, is_swapped in CPU as a uniform //See: GlRenderer::initShaders() const char* STR_RADIAL_GRADIENT_FUNCTIONS = TVG_COMPOSE_SHADER( mat3 radial_matrix(vec2 p0, vec2 p1) \n { \n mat3 a = mat3(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); \n mat3 b = mat3(p1.y - p0.y, p0.x - p1.x, 0.0, p1.x - p0.x, p1.y - p0.y, 0.0, p0.x, p0.y, 1.0); \n return a * inverse(b); \n } \n \n vec2 compute_radial_t(vec2 c0, float r0, vec2 c1, float r1, vec2 pos) \n { \n const float scalar_nearly_zero = 2.44140625e-4; \n float d_center = distance(c0, c1); \n float d_radius = r1 - r0; \n bool radial = d_center < scalar_nearly_zero; \n bool strip = abs(d_radius) < scalar_nearly_zero; \n \n if (radial) { \n if (strip) return vec2(0.0, -1.0); \n \n float scale = 1.0 / d_radius; \n float scale_sign = sign(d_radius); \n float bias = r0 / d_radius; \n vec2 pt = (pos - c0) * scale; \n float t = length(pt) * scale_sign - bias; \n return vec2(t, 1.0); \n } else if (strip) { \n mat3 transform = radial_matrix(c0, c1); \n float r = r0 / d_center; \n float r_2 = r * r; \n vec2 pt = (transform * vec3(pos.xy, 1.0)).xy; \n float t = r_2 - pt.y * pt.y; \n \n if (t < 0.0) return vec2(0.0, -1.0); \n \n t = pt.x + sqrt(t); \n return vec2(t, 1.0); \n } else { \n float f = r0 / (r0 - r1); \n bool is_swapped = abs(f - 1.0) < scalar_nearly_zero; \n vec2 c0p = is_swapped ? c1 : c0; \n vec2 c1p = is_swapped ? c0 : c1; \n float fp = is_swapped ? 0.0 : f; \n vec2 cf = c0p * (1.0 - fp) + c1p * fp; \n mat3 transform = radial_matrix(cf, c1p); \n \n float scale_x = abs(1.0 - fp); \n float scale_y = scale_x; \n float r1n = abs(r1 - r0) / d_center; \n bool is_focal_on_circle = abs(r1n - 1.0) < scalar_nearly_zero; \n if (is_focal_on_circle) { \n scale_x *= 0.5; \n scale_y *= 0.5; \n } else { \n float denom = r1n * r1n - 1.0; \n scale_x *= r1n / denom; \n scale_y /= sqrt(abs(denom)); \n } \n transform = mat3(scale_x, 0.0, 0.0, 0.0, scale_y, 0.0, 0.0, 0.0, 1.0) * transform; \n \n vec2 pt = (transform * vec3(pos.xy, 1.0)).xy; \n \n float inv_r1 = 1.0 / r1n; \n float d_radius_sign = sign(1.0 - fp); \n \n float x_t = -1.0; \n if (is_focal_on_circle) x_t = dot(pt, pt) / pt.x; \n else if (r1n > 1.0) x_t = length(pt) - pt.x * inv_r1; \n else { \n float discriminant = pt.x * pt.x - pt.y * pt.y; \n float root = sqrt(max(discriminant, 0.0)); \n float s = (is_swapped == (d_radius_sign > 0.0)) ? 1.0 : -1.0; \n x_t = s * root - pt.x * inv_r1; \n if (discriminant < 0.0 || x_t < 0.0) return vec2(is_swapped ? 0.0 : 1.0, 1.0); \n } \n float t = fp + d_radius_sign * x_t; \n if (is_swapped) t = 1.0 - t; \n return vec2(t, 1.0); \n } \n } \n \n vec4 radialGradientColor(vec2 pos) \n { \n vec2 res = compute_radial_t(uGradientInfo.centerPos.xy, \n uGradientInfo.radius.x, \n uGradientInfo.centerPos.zw, \n uGradientInfo.radius.y, \n pos); \n if (res.y < 0.0) return vec4(0.0, 0.0, 0.0, 0.0); \n \n float t = gradientWrap(res.x); \n vec4 color = gradient(t, res.x, length(pos - uGradientInfo.centerPos.xy)); \n return vec4(color.rgb * color.a, color.a); \n } \n ); const char* IMAGE_VERT_SHADER = TVG_COMPOSE_SHADER( uniform float uDepth; \n uniform mat3 uViewMatrix; \n layout (location = 0) in vec2 aLocation; \n layout (location = 1) in vec2 aUV; \n out vec2 vUV; \n \n void main() \n { \n vUV = aUV; \n vec3 pos = uViewMatrix * vec3(aLocation, 1.0); \n gl_Position = vec4(pos.xy, uDepth, 1.0); \n } \n ); const char* IMAGE_FRAG_SHADER = TVG_COMPOSE_SHADER( layout(std140) uniform ColorInfo { \n int format; \n int flipY; \n int opacity; \n int dummy; \n } uColorInfo; \n uniform sampler2D uTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec2 uv = vUV; \n if (uColorInfo.flipY == 1) { uv.y = 1.0 - uv.y; } \n vec4 color = texture(uTexture, uv); \n vec4 result; \n if (uColorInfo.format == 0) { /* FMT_ABGR8888 */ \n result = color; \n } else if (uColorInfo.format == 1) { /* FMT_ARGB8888 */ \n result = color.bgra; \n } else if (uColorInfo.format == 2) { /* FMT_ABGR8888S */ \n result = vec4(color.rgb * color.a, color.a); \n } else if (uColorInfo.format == 3) { /* FMT_ARGB8888S */ \n result = vec4(color.bgr * color.a, color.a); \n } \n FragColor = result * float(uColorInfo.opacity) / 255.0; \n } \n ); const char* MASK_VERT_SHADER = TVG_COMPOSE_SHADER( uniform float uDepth; \n layout(location = 0) in vec2 aLocation; \n layout(location = 1) in vec2 aUV; \n out vec2 vUV; \n \n void main() \n { \n vUV = aUV; \n gl_Position = vec4(aLocation, uDepth, 1.0); \n } \n ); const char* MASK_ALPHA_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n FragColor = srcColor * maskColor.a; \n } \n ); const char* MASK_INV_ALPHA_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n FragColor = srcColor *(1.0 - maskColor.a); \n } \n ); const char* MASK_LUMA_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n \n if (maskColor.a > 0.000001) { \n maskColor = vec4(maskColor.rgb / maskColor.a, maskColor.a); \n } \n \n FragColor = srcColor * dot(maskColor.rgb, vec3(0.2125, 0.7154, 0.0721)) * maskColor.a; \n } \n ); const char* MASK_INV_LUMA_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n float luma = dot(maskColor.rgb, vec3(0.2125, 0.7154, 0.0721)); \n FragColor = srcColor * (1.0 - luma); \n } \n ); const char* MASK_ADD_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n vec4 color = srcColor + maskColor * (1.0 - srcColor.a); \n FragColor = min(color, vec4(1.0, 1.0, 1.0, 1.0)) ; \n } \n ); const char* MASK_SUB_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n float a = srcColor.a - maskColor.a; \n \n if (a < 0.0 || srcColor.a == 0.0) { \n FragColor = vec4(0.0, 0.0, 0.0, 0.0); \n } else { \n vec3 srcRgb = srcColor.rgb / srcColor.a; \n FragColor = vec4(srcRgb * a, a); \n } \n } \n ); const char* MASK_INTERSECT_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n FragColor = maskColor * srcColor.a; \n } \n ); const char* MASK_DIFF_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n float da = srcColor.a - maskColor.a; \n if (da == 0.0) { \n FragColor = vec4(0.0, 0.0, 0.0, 0.0); \n } else if (da > 0.0) { \n FragColor = srcColor * da; \n } else { \n FragColor = maskColor * (-da); \n } \n } \n ); const char* MASK_DARKEN_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n if (srcColor.a > 0.0) srcColor.rgb /= srcColor.a; \n float alpha = min(srcColor.a, maskColor.a); \n FragColor = vec4(srcColor.rgb * alpha, alpha); \n } \n ); const char* MASK_LIGHTEN_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n uniform sampler2D uMaskTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n vec4 srcColor = texture(uSrcTexture, vUV); \n vec4 maskColor = texture(uMaskTexture, vUV); \n if (srcColor.a > 0.0) srcColor.rgb /= srcColor.a; \n float alpha = max(srcColor.a, maskColor.a); \n FragColor = vec4(srcColor.rgb * alpha, alpha); \n } \n ); const char* STENCIL_VERT_SHADER = TVG_COMPOSE_SHADER( uniform float uDepth; \n uniform mat3 uViewMatrix; \n layout(location = 0) in vec2 aLocation; \n \n void main() \n { \n vec3 pos = uViewMatrix * vec3(aLocation, 1.0); \n gl_Position = vec4(pos.xy, uDepth, 1.0); \n }); const char* STENCIL_FRAG_SHADER = TVG_COMPOSE_SHADER( out vec4 FragColor; \n \n void main() \n { \n FragColor = vec4(0.0); \n } \n ); const char* BLIT_VERT_SHADER = TVG_COMPOSE_SHADER( layout(location = 0) in vec2 aLocation; \n layout(location = 1) in vec2 aUV; \n out vec2 vUV; \n \n void main() \n { \n vUV = aUV; \n gl_Position = vec4(aLocation, 0.0, 1.0); \n } ); const char* BLIT_FRAG_SHADER = TVG_COMPOSE_SHADER( uniform sampler2D uSrcTexture; \n in vec2 vUV; \n out vec4 FragColor; \n \n void main() \n { \n FragColor = texture(uSrcTexture, vUV); \n } ); const char* BLEND_SHAPE_SOLID_FRAG_HEADER = R"( layout(std140) uniform SolidInfo { vec4 solidColor; } uSolidInfo; layout(std140) uniform BlendRegion { vec4 region; } uBlendRegion; uniform sampler2D uDstTexture; out vec4 FragColor; vec3 One = vec3(1.0, 1.0, 1.0); struct FragData { vec3 Sc; float Sa; float So; vec3 Dc; float Da; }; FragData d; void getFragData() { vec2 uv = (gl_FragCoord.xy - uBlendRegion.region.xy) / uBlendRegion.region.zw; vec4 colorSrc = uSolidInfo.solidColor; vec4 colorDst = texture(uDstTexture, uv); d.Sc = colorSrc.rgb; d.Sa = colorSrc.a; d.So = 1.0; d.Dc = colorDst.rgb; d.Da = colorDst.a; } vec4 postProcess(vec4 R) { return mix(vec4(d.Dc, d.Da), R, d.Sa * d.So); } )"; const char* BLEND_SHAPE_LINEAR_FRAG_HEADER = R"( layout(std140) uniform BlendRegion { vec4 region; } uBlendRegion; uniform sampler2D uDstTexture; out vec4 FragColor; vec3 One = vec3(1.0, 1.0, 1.0); struct FragData { vec3 Sc; float Sa; float So; vec3 Dc; float Da; }; FragData d; void getFragData() { vec4 colorSrc = linearGradientColor(vPos); vec2 uv = (gl_FragCoord.xy - uBlendRegion.region.xy) / uBlendRegion.region.zw; vec4 colorDst = texture(uDstTexture, uv); d.Sc = colorSrc.rgb; d.Sa = colorSrc.a; d.So = 1.0; d.Dc = colorDst.rgb; d.Da = colorDst.a; if (d.Sa > 0.0) { d.Sc = d.Sc / d.Sa; } } vec4 postProcess(vec4 R) { return mix(vec4(d.Dc, d.Da), R, d.Sa * d.So); } )"; const char* BLEND_SHAPE_RADIAL_FRAG_HEADER = R"( layout(std140) uniform BlendRegion { vec4 region; } uBlendRegion; uniform sampler2D uDstTexture; out vec4 FragColor; vec3 One = vec3(1.0, 1.0, 1.0); struct FragData { vec3 Sc; float Sa; float So; vec3 Dc; float Da; }; FragData d; void getFragData() { vec4 colorSrc = radialGradientColor(vPos); vec2 uv = (gl_FragCoord.xy - uBlendRegion.region.xy) / uBlendRegion.region.zw; vec4 colorDst = texture(uDstTexture, uv); d.Sc = colorSrc.rgb; d.Sa = colorSrc.a; d.So = 1.0; d.Dc = colorDst.rgb; d.Da = colorDst.a; if (d.Sa > 0.0) { d.Sc = d.Sc / d.Sa; } } vec4 postProcess(vec4 R) { return mix(vec4(d.Dc, d.Da), R, d.Sa * d.So); } )"; const char* BLEND_IMAGE_FRAG_HEADER = R"( layout(std140) uniform BlendRegion { vec4 region; } uBlendRegion; uniform sampler2D uSrcTexture; uniform sampler2D uDstTexture; in vec2 vUV; out vec4 FragColor; vec3 One = vec3(1.0, 1.0, 1.0); struct FragData { vec3 Sc; float Sa; float So; vec3 Dc; float Da; }; FragData d; void getFragData() { // get source data vec4 colorSrc = texture(uSrcTexture, vUV); vec2 uvDst = (gl_FragCoord.xy - uBlendRegion.region.xy) / uBlendRegion.region.zw; vec4 colorDst = texture(uDstTexture, uvDst); // fill fragment data d.Sc = colorSrc.rgb; d.Sa = colorSrc.a; d.So = 1.0; d.Dc = colorDst.rgb; d.Da = colorDst.a; if (d.Sa > 0.0) { d.Sc = d.Sc / d.Sa; } } vec4 postProcess(vec4 R) { return mix(vec4(d.Dc, d.Da), R, d.Sa * d.So); } )"; const char* BLEND_SCENE_FRAG_HEADER = R"( layout(std140) uniform ColorInfo { int format; int flipY; int opacity; int dummy; } uColorInfo; layout(std140) uniform BlendRegion { vec4 region; } uBlendRegion; uniform sampler2D uSrcTexture; uniform sampler2D uDstTexture; in vec2 vUV; out vec4 FragColor; vec3 One = vec3(1.0, 1.0, 1.0); struct FragData { vec3 Sc; float Sa; float So; vec3 Dc; float Da; }; FragData d; void getFragData() { // get source data vec4 colorSrc = texture(uSrcTexture, vUV); vec2 uvDst = (gl_FragCoord.xy - uBlendRegion.region.xy) / uBlendRegion.region.zw; vec4 colorDst = texture(uDstTexture, uvDst); // fill fragment data d.Sc = colorSrc.rgb; d.Sa = colorSrc.a; d.So = float(uColorInfo.opacity) / 255.0; d.Dc = colorDst.rgb; d.Da = colorDst.a; if (d.Sa > 0.0) {d.Sc = d.Sc / d.Sa; } } vec4 postProcess(vec4 R) { return mix(vec4(d.Dc, d.Da), R, d.Sa * d.So); } )"; const char* BLEND_FRAG_HSL = R"( // RGB to HSL conversion vec3 rgbToHsl(vec3 color) { float minVal = min(color.r, min(color.g, color.b)); float maxVal = max(color.r, max(color.g, color.b)); float delta = maxVal - minVal; float h = 0.0; if (delta > 0.0) { if (maxVal == color.r) { h = (color.g - color.b) / delta - trunc(h / 6.0) * 6.0; } else if (maxVal == color.g) { h = (color.b - color.r) / delta + 2.0; } else { h = (color.r - color.g) / delta + 4.0; } h = h * 60.0; if (h < 0.0) { h += 360.0; } } float l = (maxVal + minVal) * 0.5; float s = delta > 0.0 ? delta / (1.0 - abs(2.0 * l - 1.0)) : 0.0; return vec3(h, s, l); } // HSL to RGB conversion vec3 hslToRgb(vec3 color) { float h = color.x; float s = color.y; float l = color.z; float C = (1.0 - abs(2.0 * l - 1.0)) * s; float h_prime = h / 60.0; float X = C * (1.0 - abs(h_prime - 2.0 * trunc(h_prime / 2.0) - 1.0)); float m = l - C / 2.0; vec3 rgb = vec3(0.0); if (h_prime >= 0.0 && h_prime < 1.0) { rgb = vec3(C, X, 0.0); } else if (h_prime >= 1.0 && h_prime < 2.0) { rgb = vec3(X, C, 0.0); } else if (h_prime >= 2.0 && h_prime < 3.0) { rgb = vec3(0.0, C, X); } else if (h_prime >= 3.0 && h_prime < 4.0) { rgb = vec3(0.0, X, C); } else if (h_prime >= 4.0 && h_prime < 5.0) { rgb = vec3(X, 0.0, C); } else { rgb = vec3(C, 0.0, X); } return rgb + vec3(m); } )"; const char* NORMAL_BLEND_FRAG = R"( void main() { FragColor = texture(uSrcTexture, vUV); } )"; const char* MULTIPLY_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { Rc = d.Sc * min(One, d.Dc / d.Da); Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* SCREEN_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc + d.Dc - d.Sc * d.Dc; FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* OVERLAY_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); Rc.r = Dc.r < 0.5 ? min(1.0, 2.0 * d.Sc.r * Dc.r) : 1.0 - min(1.0, 2.0 * (1.0 - d.Sc.r) * (1.0 - Dc.r)); Rc.g = Dc.g < 0.5 ? min(1.0, 2.0 * d.Sc.g * Dc.g) : 1.0 - min(1.0, 2.0 * (1.0 - d.Sc.g) * (1.0 - Dc.g)); Rc.b = Dc.b < 0.5 ? min(1.0, 2.0 * d.Sc.b * Dc.b) : 1.0 - min(1.0, 2.0 * (1.0 - d.Sc.b) * (1.0 - Dc.b)); Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* DARKEN_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { Rc = min(d.Sc, min(One, d.Dc / d.Da)); Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* LIGHTEN_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = max(d.Sc, d.Dc); FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* COLOR_DODGE_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); Rc.r = Dc.r > 0.0 ? d.Sc.r < 1.0 ? min(1.0, Dc.r / (1.0 - d.Sc.r)) : 1.0 : 0.0; Rc.g = Dc.g > 0.0 ? d.Sc.g < 1.0 ? min(1.0, Dc.g / (1.0 - d.Sc.g)) : 1.0 : 0.0; Rc.b = Dc.b > 0.0 ? d.Sc.b < 1.0 ? min(1.0, Dc.b / (1.0 - d.Sc.b)) : 1.0 : 0.0; Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* COLOR_BURN_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); Rc.r = d.Sc.r > 0.0 ? 1.0 - min(1.0, (1.0 - Dc.r) / d.Sc.r) : Dc.r < 1.0 ? 0.0 : 1.0; Rc.g = d.Sc.g > 0.0 ? 1.0 - min(1.0, (1.0 - Dc.g) / d.Sc.g) : Dc.g < 1.0 ? 0.0 : 1.0; Rc.b = d.Sc.b > 0.0 ? 1.0 - min(1.0, (1.0 - Dc.b) / d.Sc.b) : Dc.b < 1.0 ? 0.0 : 1.0; Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* HARD_LIGHT_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); Rc.r = d.Sc.r < 0.5 ? min(1.0, 2.0 * d.Sc.r * Dc.r) : 1.0 - min(1.0, 2.0 * (1.0 - d.Sc.r) * (1.0 - Dc.r)); Rc.g = d.Sc.g < 0.5 ? min(1.0, 2.0 * d.Sc.g * Dc.g) : 1.0 - min(1.0, 2.0 * (1.0 - d.Sc.g) * (1.0 - Dc.g)); Rc.b = d.Sc.b < 0.5 ? min(1.0, 2.0 * d.Sc.b * Dc.b) : 1.0 - min(1.0, 2.0 * (1.0 - d.Sc.b) * (1.0 - Dc.b)); Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* SOFT_LIGHT_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); Rc = min(One, (One - 2.0 * d.Sc) * Dc * Dc + 2.0 * d.Sc * Dc); Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* DIFFERENCE_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = abs(d.Dc - d.Sc); FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* EXCLUSION_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc + d.Dc - 2.0 * d.Sc * d.Dc; FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* HUE_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); vec3 Sc = d.Sc; vec3 Shsl = rgbToHsl(Sc); vec3 Dhsl = rgbToHsl(Dc); Rc = hslToRgb(vec3(Shsl.r, Dhsl.g, Dhsl.b)); // sh, ds, dl Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* SATURATION_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); vec3 Sc = d.Sc; vec3 Shsl = rgbToHsl(Sc); vec3 Dhsl = rgbToHsl(Dc); Rc = hslToRgb(vec3(Dhsl.r, Shsl.g, Dhsl.b)); // dh, ss, dl Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* COLOR_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); vec3 Sc = d.Sc; vec3 Shsl = rgbToHsl(Sc); vec3 Dhsl = rgbToHsl(Dc); Rc = hslToRgb(vec3(Shsl.r, Shsl.g, Dhsl.b)); // sh, ss, dl Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* LUMINOSITY_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = d.Sc; if (d.Da > 0.0) { vec3 Dc = min(One, d.Dc / d.Da); vec3 Sc = d.Sc; vec3 Shsl = rgbToHsl(Sc); vec3 Dhsl = rgbToHsl(Dc); Rc = hslToRgb(vec3(Dhsl.r, Dhsl.g, Shsl.b)); // dh, ds, sl Rc = mix(d.Sc, Rc, d.Da); } FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* ADD_BLEND_FRAG = R"( void main() { getFragData(); vec3 Rc = min(One, d.Sc + d.Dc); FragColor = postProcess(vec4(Rc, 1.0)); } )"; const char* EFFECT_VERTEX = R"( layout(location = 0) in vec2 aLocation; out vec2 vUV; void main() { vUV = aLocation * 0.5 + 0.5; gl_Position = vec4(aLocation, 0.0, 1.0); } )"; const char* GAUSSIAN_VERTICAL = R"( uniform sampler2D uSrcTexture; layout(std140) uniform Gaussian { float sigma; float scale; float extend; float dummy0; } uGaussian; layout(std140) uniform Viewport { vec4 vp; } uViewport; in vec2 vUV; out vec4 FragColor; float gaussian(float x, float sigma) { float exponent = -x * x / (2.0 * sigma * sigma); return exp(exponent) / (sqrt(2.0 * 3.141592) * sigma); } void main() { vec2 texelSize = 1.0 / vec2(textureSize(uSrcTexture, 0)); vec4 colorSum = vec4(0.0); float sigma = uGaussian.sigma * uGaussian.scale; float weightSum = 0.0; int radius = int(uGaussian.extend); for (int y = -radius; y <= radius; ++y) { vec2 offset = vec2(0.0, float(y) * texelSize.y); vec2 coord = vUV + offset; float pixCoord = uViewport.vp.y - coord.y / texelSize.y; float weight = pixCoord < uViewport.vp.w ? gaussian(float(y), sigma) : 0.0; colorSum += texture(uSrcTexture, coord) * weight; weightSum += weight; } FragColor = colorSum / weightSum; } )"; const char* GAUSSIAN_HORIZONTAL = R"( uniform sampler2D uSrcTexture; layout(std140) uniform Gaussian { float sigma; float scale; float extend; float dummy0; } uGaussian; layout(std140) uniform Viewport { vec4 vp; } uViewport; in vec2 vUV; out vec4 FragColor; float gaussian(float x, float sigma) { float exponent = -x * x / (2.0 * sigma * sigma); return exp(exponent) / (sqrt(2.0 * 3.141592) * sigma); } void main() { vec2 texelSize = 1.0 / vec2(textureSize(uSrcTexture, 0)); vec4 colorSum = vec4(0.0); float sigma = uGaussian.sigma * uGaussian.scale; float weightSum = 0.0; int radius = int(uGaussian.extend); for (int y = -radius; y <= radius; ++y) { vec2 offset = vec2(float(y) * texelSize.x, 0.0); vec2 coord = vUV + offset; float pixCoord = uViewport.vp.x + coord.x / texelSize.x; float weight = pixCoord < uViewport.vp.z ? gaussian(float(y), sigma) : 0.0; colorSum += texture(uSrcTexture, coord) * weight; weightSum += weight; } FragColor = colorSum / weightSum; } )"; const char* EFFECT_DROPSHADOW = R"( uniform sampler2D uSrcTexture; uniform sampler2D uBlrTexture; layout(std140) uniform DropShadow { float sigma; float scale; float extend; float dummy0; vec4 color; vec2 offset; } uDropShadow; in vec2 vUV; out vec4 FragColor; void main() { vec2 texelSize = 1.0 / vec2(textureSize(uSrcTexture, 0)); vec2 offset = uDropShadow.offset * texelSize; vec4 orig = texture(uSrcTexture, vUV); vec4 blur = texture(uBlrTexture, vUV + offset); vec4 shad = uDropShadow.color * blur.a; FragColor = orig + shad * (1.0 - orig.a); } )"; const char* EFFECT_FILL = R"( uniform sampler2D uSrcTexture; layout(std140) uniform Params { vec4 params[3]; } uParams; in vec2 vUV; out vec4 FragColor; void main() { vec4 orig = texture(uSrcTexture, vUV); vec4 fill = uParams.params[0]; FragColor = fill * orig.a * fill.a; } )"; const char* EFFECT_TINT = R"( uniform sampler2D uSrcTexture; layout(std140) uniform Params { vec4 params[3]; } uParams; in vec2 vUV; out vec4 FragColor; void main() { vec4 orig = texture(uSrcTexture, vUV); float luma = dot(orig.rgb, vec3(0.2126, 0.7152, 0.0722)); FragColor = vec4(mix(orig.rgb, mix(uParams.params[0].rgb, uParams.params[1].rgb, luma), uParams.params[2].r) * orig.a, orig.a); } )"; const char* EFFECT_TRITONE = R"( uniform sampler2D uSrcTexture; layout(std140) uniform Params { vec4 params[3]; } uParams; in vec2 vUV; out vec4 FragColor; void main() { vec4 orig = texture(uSrcTexture, vUV); float luma = dot(orig.rgb, vec3(0.2126, 0.7152, 0.0722)); bool isBright = luma >= 0.5f; float t = isBright ? (luma - 0.5f) * 2.0f : luma * 2.0f; vec3 from = isBright ? uParams.params[1].rgb : uParams.params[0].rgb; vec3 to = isBright ? uParams.params[2].rgb : uParams.params[1].rgb; vec4 tmp = vec4(mix(from, to, t), 1.0f); if (uParams.params[2].a > 0.0f) tmp = mix(tmp, orig, uParams.params[2].a); FragColor = tmp * orig.a; } )"; #undef TVG_COMPOSE_SHADER src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/encodings.h000664 001750 001750 00000071112 15164251010 036773 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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" #if defined(_MSC_VER) && !defined(__clang__) 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 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 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 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 static CharType TakeBOM(InputByteStream& is); //! Take a character from input byte stream. template static Ch Take(InputByteStream& is); //! Put BOM to output byte stream. template static void PutBOM(OutputByteStream& os); //! Put a character to output byte stream. template 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 struct UTF8 { typedef CharType Ch; enum { supportUnicode = 1 }; template static void Encode(OutputStream& os, unsigned codepoint) { if (codepoint <= 0x7F) os.Put(static_cast(codepoint & 0xFF)); else if (codepoint <= 0x7FF) { os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); } else if (codepoint <= 0xFFFF) { os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); os.Put(static_cast(0x80 | (codepoint & 0x3F))); } else { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); os.Put(static_cast(0x80 | (codepoint & 0x3F))); } } template static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { if (codepoint <= 0x7F) PutUnsafe(os, static_cast(codepoint & 0xFF)); else if (codepoint <= 0x7FF) { PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); } else if (codepoint <= 0xFFFF) { PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); } else { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); } } template static bool Decode(InputStream& is, unsigned* codepoint) { #define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) #define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) #define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) typename InputStream::Ch c = is.Take(); if (!(c & 0x80)) { *codepoint = static_cast(c); return true; } unsigned char type = GetRange(static_cast(c)); if (type >= 32) { *codepoint = 0; } else { *codepoint = (0xFFu >> type) & static_cast(c); } bool result = true; switch (type) { case 2: RAPIDJSON_TAIL(); return result; case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } #undef RAPIDJSON_COPY #undef RAPIDJSON_TRANS #undef RAPIDJSON_TAIL } template static bool Validate(InputStream& is, OutputStream& os) { #define RAPIDJSON_COPY() os.Put(c = is.Take()) #define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) #define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) Ch c; RAPIDJSON_COPY(); if (!(c & 0x80)) return true; bool result = true; switch (GetRange(static_cast(c))) { case 2: RAPIDJSON_TAIL(); return result; case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } #undef RAPIDJSON_COPY #undef RAPIDJSON_TRANS #undef RAPIDJSON_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 static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); typename InputByteStream::Ch c = Take(is); if (static_cast(c) != 0xEFu) return c; c = is.Take(); if (static_cast(c) != 0xBBu) return c; c = is.Take(); if (static_cast(c) != 0xBFu) return c; c = is.Take(); return c; } template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); return static_cast(is.Take()); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0xEFu)); os.Put(static_cast(0xBBu)); os.Put(static_cast(0xBFu)); } template static void Put(OutputByteStream& os, Ch c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(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 struct UTF16 { typedef CharType Ch; RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); enum { supportUnicode = 1 }; template 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(codepoint)); } else { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; os.Put(static_cast((v >> 10) | 0xD800)); os.Put(static_cast((v & 0x3FF) | 0xDC00)); } } template 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(codepoint)); } else { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; PutUnsafe(os, static_cast((v >> 10) | 0xD800)); PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); } } template 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(c); return true; } else if (c <= 0xDBFF) { *codepoint = (static_cast(c) & 0x3FF) << 10; c = is.Take(); *codepoint |= (static_cast(c) & 0x3FF); *codepoint += 0x10000; return c >= 0xDC00 && c <= 0xDFFF; } return false; } template 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(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 struct UTF16LE : UTF16 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return static_cast(c) == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(is.Take()); c |= static_cast(static_cast(is.Take())) << 8; return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0xFFu)); os.Put(static_cast(0xFEu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(static_cast(c) & 0xFFu)); os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); } }; //! UTF-16 big endian encoding. template struct UTF16BE : UTF16 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return static_cast(c) == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(static_cast(is.Take())) << 8; c |= static_cast(static_cast(is.Take())); return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0xFEu)); os.Put(static_cast(0xFFu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); os.Put(static_cast(static_cast(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 struct UTF32 { typedef CharType Ch; RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); enum { supportUnicode = 1 }; template static void Encode(OutputStream& os, unsigned codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); os.Put(codepoint); } template static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); PutUnsafe(os, codepoint); } template 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 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 encoding. template struct UTF32LE : UTF32 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return static_cast(c) == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(is.Take()); c |= static_cast(static_cast(is.Take())) << 8; c |= static_cast(static_cast(is.Take())) << 16; c |= static_cast(static_cast(is.Take())) << 24; return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0xFFu)); os.Put(static_cast(0xFEu)); os.Put(static_cast(0x00u)); os.Put(static_cast(0x00u)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(c & 0xFFu)); os.Put(static_cast((c >> 8) & 0xFFu)); os.Put(static_cast((c >> 16) & 0xFFu)); os.Put(static_cast((c >> 24) & 0xFFu)); } }; //! UTF-32 big endian encoding. template struct UTF32BE : UTF32 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return static_cast(c) == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(static_cast(is.Take())) << 24; c |= static_cast(static_cast(is.Take())) << 16; c |= static_cast(static_cast(is.Take())) << 8; c |= static_cast(static_cast(is.Take())); return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0x00u)); os.Put(static_cast(0x00u)); os.Put(static_cast(0xFEu)); os.Put(static_cast(0xFFu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast((c >> 24) & 0xFFu)); os.Put(static_cast((c >> 16) & 0xFFu)); os.Put(static_cast((c >> 8) & 0xFFu)); os.Put(static_cast(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 struct ASCII { typedef CharType Ch; enum { supportUnicode = 0 }; template static void Encode(OutputStream& os, unsigned codepoint) { RAPIDJSON_ASSERT(codepoint <= 0x7F); os.Put(static_cast(codepoint & 0xFF)); } template static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { RAPIDJSON_ASSERT(codepoint <= 0x7F); PutUnsafe(os, static_cast(codepoint & 0xFF)); } template static bool Decode(InputStream& is, unsigned* codepoint) { uint8_t c = static_cast(is.Take()); *codepoint = c; return c <= 0X7F; } template static bool Validate(InputStream& is, OutputStream& os) { uint8_t c = static_cast(is.Take()); os.Put(static_cast(c)); return c <= 0x7F; } template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); uint8_t c = static_cast(Take(is)); return static_cast(c); } template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); return static_cast(is.Take()); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); (void)os; } template static void Put(OutputByteStream& os, Ch c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(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 AutoUTFInputStream and AutoUTFOutputStream, which provides GetType(). */ template struct AutoUTF { typedef CharType Ch; enum { supportUnicode = 1 }; #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template 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 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 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 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 struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template 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 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 static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; // Forward declaration. template inline void PutUnsafe(Stream& stream, typename Stream::Ch c); //! Specialization of Transcoder with same source and target encoding. template struct Transcoder { template 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 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 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) && !defined(__clang__)) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_ENCODINGS_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/stringbuffer.h000664 001750 001750 00000007604 15164251010 037527 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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 // 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 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() = c; } void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } void Flush() {} void Clear() { stack_.Clear(); } void ShrinkToFit() { // Push and pop a null terminator. This is safe. *stack_.template Push() = '\0'; stack_.ShrinkToFit(); stack_.template Pop(1); } void Reserve(size_t count) { stack_.template Reserve(count); } Ch* Push(size_t count) { return stack_.template Push(count); } Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } void Pop(size_t count) { stack_.template Pop(count); } const Ch* GetString() const { // Push and pop a null terminator. This is safe. *stack_.template Push() = '\0'; stack_.template Pop(1); return stack_.template Bottom(); } //! 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 stack_; private: // Prohibit copy constructor & assignment operator. GenericStringBuffer(const GenericStringBuffer&); GenericStringBuffer& operator=(const GenericStringBuffer&); }; //! String buffer with UTF8 encoding typedef GenericStringBuffer > StringBuffer; template inline void PutReserve(GenericStringBuffer& stream, size_t count) { stream.Reserve(count); } template inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { stream.PutUnsafe(c); } //! Implement specialized version of PutN() with memset() for better performance. template<> inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { std::memset(stream.stack_.Push(n), c, n * sizeof(c)); } RAPIDJSON_NAMESPACE_END #if defined(__clang__) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_STRINGBUFFER_H_ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-promise-object.h000664 001750 001750 00000013440 15164251010 046407 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_PROMISE_OBJECT_H #define ECMA_PROMISE_OBJECT_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaarraybufferobject ECMA ArrayBuffer object related routines * @{ */ /** * The PromiseState of promise object. */ typedef enum { ECMA_PROMISE_IS_PENDING = (1 << 0), /**< pending state */ ECMA_PROMISE_IS_FULFILLED = (1 << 1), /**< fulfilled state */ ECMA_PROMISE_ALREADY_RESOLVED = (1 << 2), /**< already resolved */ } ecma_promise_flags_t; /** * Description of a promise resolving function. */ typedef struct { ecma_extended_object_t header; /**< extended object part */ ecma_value_t promise; /**< [[Promise]] internal slot */ } ecma_promise_resolver_t; /** * Description of the promise object. * It need more space than normal object to store builtin properties. */ typedef struct { ecma_extended_object_t header; /**< extended object part */ ecma_collection_t *reactions; /**< list of promise reactions */ } ecma_promise_object_t; /** * Description of the finally function object */ typedef struct { ecma_extended_object_t header; /**< extended object part */ ecma_value_t constructor; /**< [[Constructor]] internal slot */ ecma_value_t on_finally; /**< [[OnFinally]] internal slot */ } ecma_promise_finally_function_t; /** * Description of the thunk function object */ typedef struct { ecma_extended_object_t header; /**< extended object part */ ecma_value_t value; /**< value thunk */ } ecma_promise_value_thunk_t; /* The Promise reaction is a compressed structure, where each item can * be a sequence of up to three ecma object values as seen below: * * [ Capability ][ Optional fulfilled callback ][ Optional rejected callback ] * [ Async function callback ] * * The first member is an object, which lower bits specify the type of the reaction: * bit 2 is not set: callback reactions * The first two objects specify the resolve/reject functions of the promise * returned by the `then` operation which can be used to chain event handlers. * * bit 0: has a fulfilled callback * bit 1: has a rejected callback * * bit 2 is set: async function callback */ bool ecma_is_promise (ecma_object_t *obj_p); ecma_value_t ecma_op_create_promise_object (ecma_value_t executor, ecma_value_t parent, ecma_object_t *new_target_p); uint8_t ecma_promise_get_flags (ecma_object_t *promise_p); ecma_value_t ecma_promise_get_result (ecma_object_t *promise_p); void ecma_reject_promise (ecma_value_t promise, ecma_value_t reason); void ecma_fulfill_promise (ecma_value_t promise, ecma_value_t value); ecma_value_t ecma_reject_promise_with_checks (ecma_value_t promise, ecma_value_t reason); ecma_value_t ecma_fulfill_promise_with_checks (ecma_value_t promise, ecma_value_t value); ecma_object_t *ecma_promise_new_capability (ecma_value_t constructor, ecma_value_t parent); ecma_value_t ecma_promise_reject_or_resolve (ecma_value_t this_arg, ecma_value_t value, bool is_resolve); ecma_value_t ecma_promise_then (ecma_value_t promise, ecma_value_t on_fulfilled, ecma_value_t on_rejected); ecma_value_t ecma_value_thunk_helper_cb (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); ecma_value_t ecma_value_thunk_thrower_cb (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); ecma_value_t ecma_promise_then_finally_cb (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); ecma_value_t ecma_promise_catch_finally_cb (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); ecma_value_t ecma_promise_reject_handler (ecma_object_t *function_obj_p, const ecma_value_t argv[], const uint32_t args_count); ecma_value_t ecma_promise_resolve_handler (ecma_object_t *function_obj_p, const ecma_value_t argv[], const uint32_t args_count); ecma_value_t ecma_promise_all_or_all_settled_handler_cb (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); ecma_value_t ecma_op_get_capabilities_executor_cb (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); ecma_value_t ecma_promise_finally (ecma_value_t promise, ecma_value_t on_finally); void ecma_promise_async_then (ecma_value_t promise, ecma_value_t executable_object); ecma_value_t ecma_promise_async_await (ecma_extended_object_t *async_generator_object_p, ecma_value_t value); ecma_value_t ecma_promise_run_executor (ecma_object_t *promise_p, ecma_value_t executor, ecma_value_t this_value); ecma_value_t ecma_op_if_abrupt_reject_promise (ecma_value_t *value_p, ecma_object_t *capability_obj_p); uint32_t ecma_promise_remaining_inc_or_dec (ecma_value_t remaining, bool is_inc); ecma_value_t ecma_promise_perform_then (ecma_value_t promise, ecma_value_t on_fulfilled, ecma_value_t on_rejected, ecma_object_t *result_capability_obj_p); /** * @} * @} */ #endif /* !ECMA_PROMISE_OBJECT_H */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-regexp-object.cpp000664 001750 001750 00000263570 15164251010 046571 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-regexp-object.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt-libc-includes.h" #include "lit-char-helpers.h" #include "re-compiler.h" #if JERRY_BUILTIN_REGEXP #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaregexpobject ECMA RegExp object related routines * @{ */ /** * Index of the global capturing group */ #define RE_GLOBAL_CAPTURE 0 /** * Parse RegExp flags (global, ignoreCase, multiline) * * See also: ECMA-262 v5, 15.10.4.1 * * @return empty ecma value - if parsed successfully * error ecma value - otherwise * * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_regexp_parse_flags (ecma_string_t *flags_str_p, /**< Input string with flags */ uint16_t *flags_p) /**< [out] parsed flag bits */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; uint16_t result_flags = RE_FLAG_EMPTY; ECMA_STRING_TO_UTF8_STRING (flags_str_p, flags_start_p, flags_start_size); const lit_utf8_byte_t *flags_str_curr_p = flags_start_p; const lit_utf8_byte_t *flags_str_end_p = flags_start_p + flags_start_size; while (flags_str_curr_p < flags_str_end_p) { ecma_regexp_flags_t flag; switch (*flags_str_curr_p++) { case 'g': { flag = RE_FLAG_GLOBAL; break; } case 'i': { flag = RE_FLAG_IGNORE_CASE; break; } case 'm': { flag = RE_FLAG_MULTILINE; break; } case 'y': { flag = RE_FLAG_STICKY; break; } case 'u': { flag = RE_FLAG_UNICODE; break; } case 's': { flag = RE_FLAG_DOTALL; break; } default: { flag = RE_FLAG_EMPTY; break; } } if (flag == RE_FLAG_EMPTY || (result_flags & flag) != 0) { ret_value = ecma_raise_syntax_error (ECMA_ERR_INVALID_REGEXP_FLAGS); break; } result_flags = (uint16_t) (result_flags | flag); } ECMA_FINALIZE_UTF8_STRING (flags_start_p, flags_start_size); *flags_p = result_flags; return ret_value; } /* ecma_regexp_parse_flags */ /** * RegExpAlloc method * * See also: ECMA-262 v5, 15.10.4.1 * ECMA-262 v6, 21.2.3.2.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return ecma_object_t */ ecma_object_t * ecma_op_regexp_alloc (ecma_object_t *ctr_obj_p) /**< constructor object pointer */ { if (ctr_obj_p == NULL) { ctr_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP); } ecma_object_t *proto_obj_p = ecma_op_get_prototype_from_constructor (ctr_obj_p, ECMA_BUILTIN_ID_REGEXP_PROTOTYPE); if (JERRY_UNLIKELY (proto_obj_p == NULL)) { return proto_obj_p; } ecma_object_t *new_object_p = ecma_create_object (proto_obj_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_deref_object (proto_obj_p); ecma_extended_object_t *regexp_obj_p = (ecma_extended_object_t *) new_object_p; /* Class id will be initialized after the bytecode is compiled. */ regexp_obj_p->u.cls.type = ECMA_OBJECT_CLASS__MAX; ecma_value_t status = ecma_builtin_helper_def_prop (new_object_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), ecma_make_uint32_value (0), ECMA_PROPERTY_FLAG_WRITABLE | JERRY_PROP_SHOULD_THROW); JERRY_ASSERT (ecma_is_value_true (status)); return new_object_p; } /* ecma_op_regexp_alloc */ /** * Helper method for initializing an already existing RegExp object. */ static inline void ecma_op_regexp_initialize (ecma_object_t *regexp_obj_p, /**< RegExp object */ const re_compiled_code_t *bc_p) /**< bytecode */ { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) regexp_obj_p; ext_obj_p->u.cls.type = ECMA_OBJECT_CLASS_REGEXP; ECMA_SET_INTERNAL_VALUE_POINTER (ext_obj_p->u.cls.u3.value, bc_p); } /* ecma_op_regexp_initialize */ /** * Method for creating a RegExp object from pattern. * * Note: * Allocation have to happen before invoking this function using ecma_op_regexp_alloc. * * @return ecma_value_t */ ecma_value_t ecma_op_create_regexp_from_pattern (ecma_object_t *regexp_obj_p, /**< RegExp object */ ecma_value_t pattern_value, /**< pattern */ ecma_value_t flags_value) /**< flags */ { ecma_string_t *pattern_str_p = ecma_regexp_read_pattern_str_helper (pattern_value); uint16_t flags = 0; if (JERRY_UNLIKELY (pattern_str_p == NULL)) { return ECMA_VALUE_ERROR; } if (!ecma_is_value_undefined (flags_value)) { ecma_string_t *flags_str_p = ecma_op_to_string (flags_value); if (JERRY_UNLIKELY (flags_str_p == NULL)) { ecma_deref_ecma_string (pattern_str_p); return ECMA_VALUE_ERROR; } ecma_value_t parse_flags_value = ecma_regexp_parse_flags (flags_str_p, &flags); ecma_deref_ecma_string (flags_str_p); if (ECMA_IS_VALUE_ERROR (parse_flags_value)) { ecma_deref_ecma_string (pattern_str_p); return parse_flags_value; } JERRY_ASSERT (ecma_is_value_empty (parse_flags_value)); } re_compiled_code_t *bc_p = re_compile_bytecode (pattern_str_p, flags); if (JERRY_UNLIKELY (bc_p == NULL)) { ecma_deref_ecma_string (pattern_str_p); return ECMA_VALUE_ERROR; } ecma_op_regexp_initialize (regexp_obj_p, bc_p); ecma_deref_ecma_string (pattern_str_p); return ecma_make_object_value (regexp_obj_p); } /* ecma_op_create_regexp_from_pattern */ /** * Method for creating a RegExp object from bytecode. * * Note: * Allocation have to happen before invoking this function using ecma_op_regexp_alloc. * * @return ecma_value_t */ ecma_value_t ecma_op_create_regexp_from_bytecode (ecma_object_t *regexp_obj_p, /**< RegExp object */ re_compiled_code_t *bc_p) /**< bytecode */ { ecma_bytecode_ref ((ecma_compiled_code_t *) bc_p); ecma_op_regexp_initialize (regexp_obj_p, bc_p); return ecma_make_object_value (regexp_obj_p); } /* ecma_op_create_regexp_from_bytecode */ /** * Method for creating a RegExp object from pattern with already parsed flags. * * Note: * Allocation have to happen before invoking this function using ecma_op_regexp_alloc. * * @return ecma_value_t */ ecma_value_t ecma_op_create_regexp_with_flags (ecma_object_t *regexp_obj_p, /**< RegExp object */ ecma_value_t pattern_value, /**< pattern */ uint16_t flags) /**< flags */ { ecma_string_t *pattern_str_p = ecma_regexp_read_pattern_str_helper (pattern_value); if (JERRY_UNLIKELY (pattern_str_p == NULL)) { return ECMA_VALUE_ERROR; } re_compiled_code_t *bc_p = re_compile_bytecode (pattern_str_p, flags); ecma_deref_ecma_string (pattern_str_p); if (JERRY_UNLIKELY (bc_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_op_regexp_initialize (regexp_obj_p, bc_p); return ecma_make_object_value (regexp_obj_p); } /* ecma_op_create_regexp_with_flags */ /** * Canonicalize a character * * @return ecma_char_t canonicalized character */ lit_code_point_t ecma_regexp_canonicalize_char (lit_code_point_t ch, /**< character */ bool unicode) /**< unicode */ { if (unicode) { /* In unicode mode the mappings contained in the CaseFolding.txt file should be used to canonicalize the character. * These mappings generally correspond to the lowercase variant of the character, however there are some * differences. In some cases the uppercase variant is used, in others the lowercase of the uppercase character is * used, and there are also cases where the character has no case folding mapping even though it has upper/lower * variants. Since lowercasing is the most common this is used as the default behaviour, and characters with * differing behaviours are encoded in lookup tables. */ if (lit_char_fold_to_upper (ch)) { ch = lit_char_to_upper_case (ch, NULL); JERRY_ASSERT (ch != LIT_MULTIPLE_CU); } if (lit_char_fold_to_lower (ch)) { ch = lit_char_to_lower_case (ch, NULL); JERRY_ASSERT (ch != LIT_MULTIPLE_CU); } return ch; } JERRY_UNUSED (unicode); lit_code_point_t cu = lit_char_to_upper_case (ch, NULL); if (ch <= LIT_UTF8_1_BYTE_CODE_POINT_MAX || (cu > LIT_UTF8_1_BYTE_CODE_POINT_MAX && cu != LIT_MULTIPLE_CU)) { return cu; } return ch; } /* ecma_regexp_canonicalize_char */ /** * RegExp Canonicalize abstract operation * * See also: ECMA-262 v5, 15.10.2.8 * * @return ecma_char_t canonicalized character */ static inline lit_code_point_t ecma_regexp_canonicalize (lit_code_point_t ch, /**< character */ uint16_t flags) /**< flags */ { if (flags & RE_FLAG_IGNORE_CASE) { return ecma_regexp_canonicalize_char (ch, flags & RE_FLAG_UNICODE); } return ch; } /* ecma_regexp_canonicalize */ /** * Check if a code point is matched by a class escape. * * @return true, if code point matches escape * false, otherwise */ static bool ecma_regexp_check_class_escape (lit_code_point_t cp, /**< char */ ecma_class_escape_t escape) /**< escape */ { switch (escape) { case RE_ESCAPE_DIGIT: { return (cp >= LIT_CHAR_0 && cp <= LIT_CHAR_9); } case RE_ESCAPE_NOT_DIGIT: { return (cp < LIT_CHAR_0 || cp > LIT_CHAR_9); } case RE_ESCAPE_WORD_CHAR: { return lit_char_is_word_char (cp); } case RE_ESCAPE_NOT_WORD_CHAR: { return !lit_char_is_word_char (cp); } case RE_ESCAPE_WHITESPACE: { return lit_char_is_white_space ((ecma_char_t) cp); } case RE_ESCAPE_NOT_WHITESPACE: { return !lit_char_is_white_space ((ecma_char_t) cp); } default: { JERRY_UNREACHABLE (); } } } /* ecma_regexp_check_class_escape */ /** * Helper function to get current code point or code unit depending on execution mode, * and advance the string pointer. * * @return lit_code_point_t current code point */ static lit_code_point_t ecma_regexp_advance (ecma_regexp_ctx_t *re_ctx_p, /**< regexp context */ const lit_utf8_byte_t **str_p) /**< reference to string pointer */ { JERRY_ASSERT (str_p != NULL); lit_code_point_t cp = lit_cesu8_read_next (str_p); if (JERRY_UNLIKELY (re_ctx_p->flags & RE_FLAG_UNICODE) && lit_is_code_point_utf16_high_surrogate ((ecma_char_t) cp) && *str_p < re_ctx_p->input_end_p) { const ecma_char_t next_ch = lit_cesu8_peek_next (*str_p); if (lit_is_code_point_utf16_low_surrogate (next_ch)) { cp = lit_convert_surrogate_pair_to_code_point ((ecma_char_t) cp, next_ch); *str_p += LIT_UTF8_MAX_BYTES_IN_CODE_UNIT; } } return ecma_regexp_canonicalize (cp, re_ctx_p->flags); } /* ecma_regexp_advance */ /** * Helper function to get current full unicode code point and advance the string pointer. * * @return lit_code_point_t current code point */ lit_code_point_t ecma_regexp_unicode_advance (const lit_utf8_byte_t **str_p, /**< reference to string pointer */ const lit_utf8_byte_t *end_p) /**< string end pointer */ { JERRY_ASSERT (str_p != NULL); const lit_utf8_byte_t *current_p = *str_p; lit_code_point_t ch = lit_cesu8_read_next (¤t_p); if (lit_is_code_point_utf16_high_surrogate ((ecma_char_t) ch) && current_p < end_p) { const ecma_char_t next_ch = lit_cesu8_peek_next (current_p); if (lit_is_code_point_utf16_low_surrogate (next_ch)) { ch = lit_convert_surrogate_pair_to_code_point ((ecma_char_t) ch, next_ch); current_p += LIT_UTF8_MAX_BYTES_IN_CODE_UNIT; } } *str_p = current_p; return ch; } /* ecma_regexp_unicode_advance */ /** * Helper function to revert the string pointer to the previous code point. * * @return pointer to previous code point */ static JERRY_ATTR_NOINLINE const lit_utf8_byte_t * ecma_regexp_step_back (ecma_regexp_ctx_t *re_ctx_p, /**< regexp context */ const lit_utf8_byte_t *str_p) /**< reference to string pointer */ { JERRY_ASSERT (str_p != NULL); lit_code_point_t ch = lit_cesu8_read_prev (&str_p); if (JERRY_UNLIKELY (re_ctx_p->flags & RE_FLAG_UNICODE) && lit_is_code_point_utf16_low_surrogate (ch) && lit_is_code_point_utf16_high_surrogate (lit_cesu8_peek_prev (str_p))) { str_p -= LIT_UTF8_MAX_BYTES_IN_CODE_UNIT; } return str_p; } /* ecma_regexp_step_back */ /** * Check if the current position is on a word boundary. * * @return true, if on a word boundary * false - otherwise */ static bool ecma_regexp_is_word_boundary (ecma_regexp_ctx_t *re_ctx_p, /**< regexp context */ const lit_utf8_byte_t *str_p) /**< string pointer */ { lit_code_point_t left_cp; lit_code_point_t right_cp; if (JERRY_UNLIKELY (str_p <= re_ctx_p->input_start_p)) { left_cp = LIT_INVALID_CP; } else if (JERRY_UNLIKELY ((re_ctx_p->flags & (RE_FLAG_UNICODE | RE_FLAG_IGNORE_CASE)) == (RE_FLAG_UNICODE | RE_FLAG_IGNORE_CASE))) { const lit_utf8_byte_t *prev_p = ecma_regexp_step_back (re_ctx_p, str_p); left_cp = ecma_regexp_advance (re_ctx_p, &prev_p); JERRY_ASSERT (prev_p == str_p); } else { left_cp = str_p[-1]; } if (JERRY_UNLIKELY (str_p >= re_ctx_p->input_end_p)) { right_cp = LIT_INVALID_CP; } else if (JERRY_UNLIKELY ((re_ctx_p->flags & (RE_FLAG_UNICODE | RE_FLAG_IGNORE_CASE)) == (RE_FLAG_UNICODE | RE_FLAG_IGNORE_CASE))) { right_cp = ecma_regexp_advance (re_ctx_p, &str_p); } else { right_cp = str_p[0]; } return lit_char_is_word_char (left_cp) != lit_char_is_word_char (right_cp); } /* ecma_regexp_is_word_boundary */ /** * Recursive function for executing RegExp bytecode. * * See also: * ECMA-262 v5, 15.10.2.1 * * @return pointer to the end of the currently matched substring * NULL, if pattern did not match */ static const lit_utf8_byte_t * ecma_regexp_run (ecma_regexp_ctx_t *re_ctx_p, /**< RegExp matcher context */ const uint8_t *bc_p, /**< pointer to the current RegExp bytecode */ const lit_utf8_byte_t *str_curr_p) /**< input string pointer */ { #if (JERRY_STACK_LIMIT != 0) if (JERRY_UNLIKELY (ecma_get_current_stack_usage () > CONFIG_MEM_STACK_LIMIT)) { return ECMA_RE_OUT_OF_STACK; } #endif /* JERRY_STACK_LIMIT != 0 */ const lit_utf8_byte_t *str_start_p = str_curr_p; const uint8_t *next_alternative_p = NULL; while (true) { const re_opcode_t op = re_get_opcode (&bc_p); switch (op) { case RE_OP_EOF: { re_ctx_p->captures_p[RE_GLOBAL_CAPTURE].end_p = str_curr_p; /* FALLTHRU */ } case RE_OP_ASSERT_END: case RE_OP_ITERATOR_END: { return str_curr_p; } case RE_OP_ALTERNATIVE_START: { const uint32_t offset = re_get_value (&bc_p); next_alternative_p = bc_p + offset; continue; } case RE_OP_ALTERNATIVE_NEXT: { while (true) { const uint32_t offset = re_get_value (&bc_p); bc_p += offset; if (*bc_p != RE_OP_ALTERNATIVE_NEXT) { break; } bc_p++; } continue; } case RE_OP_NO_ALTERNATIVE: { return NULL; } case RE_OP_CAPTURING_GROUP_START: { const uint32_t group_idx = re_get_value (&bc_p); ecma_regexp_capture_t *const group_p = re_ctx_p->captures_p + group_idx; group_p->subcapture_count = re_get_value (&bc_p); const lit_utf8_byte_t *const saved_begin_p = group_p->begin_p; const lit_utf8_byte_t *const saved_end_p = group_p->end_p; const uint32_t saved_iterator = group_p->iterator; const uint32_t qmin = re_get_value (&bc_p); group_p->end_p = NULL; /* If zero iterations are allowed, then execute the end opcode which will handle further iterations, * otherwise run the 1st iteration immediately by executing group bytecode. */ if (qmin == 0) { group_p->iterator = 0; group_p->begin_p = NULL; const uint32_t end_offset = re_get_value (&bc_p); group_p->bc_p = bc_p; bc_p += end_offset; } else { group_p->iterator = 1; group_p->begin_p = str_curr_p; group_p->bc_p = bc_p; } const lit_utf8_byte_t *matched_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); group_p->iterator = saved_iterator; if (matched_p == NULL) { group_p->begin_p = saved_begin_p; group_p->end_p = saved_end_p; goto fail; } return matched_p; } case RE_OP_NON_CAPTURING_GROUP_START: { const uint32_t group_idx = re_get_value (&bc_p); ecma_regexp_non_capture_t *const group_p = re_ctx_p->non_captures_p + group_idx; group_p->subcapture_start = re_get_value (&bc_p); group_p->subcapture_count = re_get_value (&bc_p); const lit_utf8_byte_t *const saved_begin_p = group_p->begin_p; const uint32_t saved_iterator = group_p->iterator; const uint32_t qmin = re_get_value (&bc_p); /* If zero iterations are allowed, then execute the end opcode which will handle further iterations, * otherwise run the 1st iteration immediately by executing group bytecode. */ if (qmin == 0) { group_p->iterator = 0; group_p->begin_p = NULL; const uint32_t end_offset = re_get_value (&bc_p); group_p->bc_p = bc_p; bc_p += end_offset; } else { group_p->iterator = 1; group_p->begin_p = str_curr_p; group_p->bc_p = bc_p; } const lit_utf8_byte_t *matched_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); group_p->iterator = saved_iterator; if (matched_p == NULL) { group_p->begin_p = saved_begin_p; goto fail; } return matched_p; } case RE_OP_GREEDY_CAPTURING_GROUP_END: { const uint32_t group_idx = re_get_value (&bc_p); ecma_regexp_capture_t *const group_p = re_ctx_p->captures_p + group_idx; const uint32_t qmin = re_get_value (&bc_p); if (group_p->iterator < qmin) { /* No need to save begin_p since we don't have to backtrack beyond the minimum iteration count, but we have * to clear nested capturing groups. */ group_p->begin_p = str_curr_p; for (uint32_t i = 1; i < group_p->subcapture_count; ++i) { group_p[i].begin_p = NULL; } group_p->iterator++; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, group_p->bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } group_p->iterator--; goto fail; } /* Empty matches are not allowed after reaching the minimum number of iterations. */ if (JERRY_UNLIKELY (group_p->begin_p >= str_curr_p) && (group_p->iterator > qmin)) { goto fail; } const uint32_t qmax = re_get_value (&bc_p) - RE_QMAX_OFFSET; if (JERRY_UNLIKELY (group_p->iterator >= qmax)) { /* Reached maximum number of iterations, try to match tail bytecode. */ group_p->end_p = str_curr_p; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } goto fail; } { /* Save and clear all nested capturing groups, and try to iterate. */ JERRY_VLA (const lit_utf8_byte_t *, saved_captures_p, group_p->subcapture_count); for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { saved_captures_p[i] = group_p[i].begin_p; group_p[i].begin_p = NULL; } group_p->iterator++; group_p->begin_p = str_curr_p; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, group_p->bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } /* Failed to iterate again, backtrack to current match, and try to run tail bytecode. */ for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { group_p[i].begin_p = saved_captures_p[i]; } group_p->iterator--; group_p->end_p = str_curr_p; } const lit_utf8_byte_t *const tail_match_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (tail_match_p != NULL) { return tail_match_p; } goto fail; } case RE_OP_GREEDY_NON_CAPTURING_GROUP_END: { const uint32_t group_idx = re_get_value (&bc_p); ecma_regexp_non_capture_t *const group_p = re_ctx_p->non_captures_p + group_idx; const uint32_t qmin = re_get_value (&bc_p); if (group_p->iterator < qmin) { /* No need to save begin_p but we have to clear nested capturing groups. */ group_p->begin_p = str_curr_p; ecma_regexp_capture_t *const capture_p = re_ctx_p->captures_p + group_p->subcapture_start; for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { capture_p[i].begin_p = NULL; } group_p->iterator++; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, group_p->bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } group_p->iterator--; goto fail; } /* Empty matches are not allowed after reaching the minimum number of iterations. */ if (JERRY_UNLIKELY (group_p->begin_p >= str_curr_p) && (group_p->iterator > qmin)) { goto fail; } const uint32_t qmax = re_get_value (&bc_p) - RE_QMAX_OFFSET; if (JERRY_UNLIKELY (group_p->iterator >= qmax)) { /* Reached maximum number of iterations, try to match tail bytecode. */ const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } goto fail; } { /* Save and clear all nested capturing groups, and try to iterate. */ JERRY_VLA (const lit_utf8_byte_t *, saved_captures_p, group_p->subcapture_count); for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { ecma_regexp_capture_t *const capture_p = re_ctx_p->captures_p + group_p->subcapture_start + i; saved_captures_p[i] = capture_p->begin_p; capture_p->begin_p = NULL; } group_p->iterator++; const lit_utf8_byte_t *const saved_begin_p = group_p->begin_p; group_p->begin_p = str_curr_p; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, group_p->bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } /* Failed to iterate again, backtrack to current match, and try to run tail bytecode. */ for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { ecma_regexp_capture_t *const capture_p = re_ctx_p->captures_p + group_p->subcapture_start + i; capture_p->begin_p = saved_captures_p[i]; } group_p->iterator--; group_p->begin_p = saved_begin_p; } const lit_utf8_byte_t *const tail_match_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (tail_match_p != NULL) { return tail_match_p; } goto fail; } case RE_OP_LAZY_CAPTURING_GROUP_END: { const uint32_t group_idx = re_get_value (&bc_p); ecma_regexp_capture_t *const group_p = re_ctx_p->captures_p + group_idx; const uint32_t qmin = re_get_value (&bc_p); if (group_p->iterator < qmin) { /* No need to save begin_p but we have to clear nested capturing groups. */ group_p->begin_p = str_curr_p; for (uint32_t i = 1; i < group_p->subcapture_count; ++i) { group_p[i].begin_p = NULL; } group_p->iterator++; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, group_p->bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } group_p->iterator--; goto fail; } /* Empty matches are not allowed after reaching the minimum number of iterations. */ if (JERRY_UNLIKELY (group_p->begin_p >= str_curr_p) && (group_p->iterator > qmin)) { goto fail; } const uint32_t qmax = re_get_value (&bc_p) - RE_QMAX_OFFSET; group_p->end_p = str_curr_p; /* Try to match tail bytecode. */ const lit_utf8_byte_t *const tail_match_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (tail_match_p != NULL) { return tail_match_p; } if (JERRY_UNLIKELY (group_p->iterator >= qmax)) { /* Reached maximum number of iterations and tail bytecode did not match. */ goto fail; } { /* Save and clear all nested capturing groups, and try to iterate. */ JERRY_VLA (const lit_utf8_byte_t *, saved_captures_p, group_p->subcapture_count); for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { saved_captures_p[i] = group_p[i].begin_p; group_p[i].begin_p = NULL; } group_p->iterator++; group_p->begin_p = str_curr_p; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, group_p->bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } /* Backtrack to current match. */ for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { group_p[i].begin_p = saved_captures_p[i]; } group_p->iterator--; } goto fail; } case RE_OP_LAZY_NON_CAPTURING_GROUP_END: { const uint32_t group_idx = re_get_value (&bc_p); ecma_regexp_non_capture_t *const group_p = re_ctx_p->non_captures_p + group_idx; const uint32_t qmin = re_get_value (&bc_p); if (group_p->iterator < qmin) { /* Clear nested captures. */ ecma_regexp_capture_t *const capture_p = re_ctx_p->captures_p + group_p->subcapture_start; for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { capture_p[i].begin_p = NULL; } group_p->iterator++; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, group_p->bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } group_p->iterator--; goto fail; } /* Empty matches are not allowed after reaching the minimum number of iterations. */ if (JERRY_UNLIKELY (group_p->begin_p >= str_curr_p) && (group_p->iterator > qmin)) { goto fail; } const uint32_t qmax = re_get_value (&bc_p) - RE_QMAX_OFFSET; /* Try to match tail bytecode. */ const lit_utf8_byte_t *const tail_match_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (tail_match_p != NULL) { return tail_match_p; } if (JERRY_UNLIKELY (group_p->iterator >= qmax)) { /* Reached maximum number of iterations and tail bytecode did not match. */ goto fail; } { /* Save and clear all nested capturing groups, and try to iterate. */ JERRY_VLA (const lit_utf8_byte_t *, saved_captures_p, group_p->subcapture_count); for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { ecma_regexp_capture_t *const capture_p = re_ctx_p->captures_p + group_p->subcapture_start + i; saved_captures_p[i] = capture_p->begin_p; capture_p->begin_p = NULL; } group_p->iterator++; const lit_utf8_byte_t *const saved_begin_p = group_p->begin_p; group_p->begin_p = str_curr_p; const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, group_p->bc_p, str_curr_p); if (matched_p != NULL) { return matched_p; } /* Backtrack to current match. */ for (uint32_t i = 0; i < group_p->subcapture_count; ++i) { ecma_regexp_capture_t *const capture_p = re_ctx_p->captures_p + group_p->subcapture_start + i; capture_p->begin_p = saved_captures_p[i]; } group_p->iterator--; group_p->begin_p = saved_begin_p; } goto fail; } case RE_OP_GREEDY_ITERATOR: { const uint32_t qmin = re_get_value (&bc_p); const uint32_t qmax = re_get_value (&bc_p) - RE_QMAX_OFFSET; const uint32_t end_offset = re_get_value (&bc_p); uint32_t iterator = 0; while (iterator < qmin) { str_curr_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (str_curr_p == NULL) { goto fail; } if (ECMA_RE_STACK_LIMIT_REACHED (str_curr_p)) { return str_curr_p; } iterator++; } while (iterator < qmax) { const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (matched_p == NULL) { break; } if (ECMA_RE_STACK_LIMIT_REACHED (str_curr_p)) { return str_curr_p; } str_curr_p = matched_p; iterator++; } const uint8_t *const tail_bc_p = bc_p + end_offset; while (true) { const lit_utf8_byte_t *const tail_match_p = ecma_regexp_run (re_ctx_p, tail_bc_p, str_curr_p); if (tail_match_p != NULL) { return tail_match_p; } if (JERRY_UNLIKELY (iterator <= qmin)) { goto fail; } iterator--; JERRY_ASSERT (str_curr_p > re_ctx_p->input_start_p); str_curr_p = ecma_regexp_step_back (re_ctx_p, str_curr_p); } JERRY_UNREACHABLE (); } case RE_OP_LAZY_ITERATOR: { const uint32_t qmin = re_get_value (&bc_p); const uint32_t qmax = re_get_value (&bc_p) - RE_QMAX_OFFSET; const uint32_t end_offset = re_get_value (&bc_p); uint32_t iterator = 0; while (iterator < qmin) { str_curr_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (str_curr_p == NULL) { goto fail; } if (ECMA_RE_STACK_LIMIT_REACHED (str_curr_p)) { return str_curr_p; } iterator++; } const uint8_t *const tail_bc_p = bc_p + end_offset; while (true) { const lit_utf8_byte_t *const tail_match_p = ecma_regexp_run (re_ctx_p, tail_bc_p, str_curr_p); if (tail_match_p != NULL) { return tail_match_p; } if (JERRY_UNLIKELY (iterator >= qmax)) { goto fail; } const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (matched_p == NULL) { goto fail; } if (ECMA_RE_STACK_LIMIT_REACHED (matched_p)) { return matched_p; } iterator++; str_curr_p = matched_p; } JERRY_UNREACHABLE (); } case RE_OP_BACKREFERENCE: { const uint32_t backref_idx = re_get_value (&bc_p); JERRY_ASSERT (backref_idx >= 1 && backref_idx < re_ctx_p->captures_count); const ecma_regexp_capture_t *capture_p = re_ctx_p->captures_p + backref_idx; if (!ECMA_RE_IS_CAPTURE_DEFINED (capture_p) || capture_p->end_p <= capture_p->begin_p) { /* Undefined or zero length captures always match. */ continue; } const lit_utf8_size_t capture_size = (lit_utf8_size_t) (capture_p->end_p - capture_p->begin_p); if (str_curr_p + capture_size > re_ctx_p->input_end_p || memcmp (str_curr_p, capture_p->begin_p, capture_size)) { goto fail; } str_curr_p += capture_size; continue; } case RE_OP_ASSERT_LINE_START: { if (str_curr_p <= re_ctx_p->input_start_p) { continue; } if (!(re_ctx_p->flags & RE_FLAG_MULTILINE) || !lit_char_is_line_terminator (lit_cesu8_peek_prev (str_curr_p))) { goto fail; } continue; } case RE_OP_ASSERT_LINE_END: { if (str_curr_p >= re_ctx_p->input_end_p) { continue; } if (!(re_ctx_p->flags & RE_FLAG_MULTILINE) || !lit_char_is_line_terminator (lit_cesu8_peek_next (str_curr_p))) { goto fail; } continue; } case RE_OP_ASSERT_WORD_BOUNDARY: { if (!ecma_regexp_is_word_boundary (re_ctx_p, str_curr_p)) { goto fail; } continue; } case RE_OP_ASSERT_NOT_WORD_BOUNDARY: { if (ecma_regexp_is_word_boundary (re_ctx_p, str_curr_p)) { goto fail; } continue; } case RE_OP_ASSERT_LOOKAHEAD_POS: { const uint8_t qmin = re_get_byte (&bc_p); const uint32_t capture_start = re_get_value (&bc_p); const uint32_t capture_count = re_get_value (&bc_p); const uint32_t end_offset = re_get_value (&bc_p); /* If qmin is zero, the assertion implicitly matches. */ if (qmin == 0) { bc_p += end_offset; continue; } /* Capture end pointers might get clobbered and need to be restored after a tail match fail. */ JERRY_VLA (const lit_utf8_byte_t *, saved_captures_p, capture_count); for (uint32_t i = 0; i < capture_count; ++i) { ecma_regexp_capture_t *const capture_p = re_ctx_p->captures_p + capture_start + i; saved_captures_p[i] = capture_p->end_p; } /* The first iteration will decide whether the assertion matches depending on whether * the iteration matched or not. */ const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (ECMA_RE_STACK_LIMIT_REACHED (matched_p)) { return matched_p; } if (matched_p == NULL) { goto fail; } const lit_utf8_byte_t *tail_match_p = ecma_regexp_run (re_ctx_p, bc_p + end_offset, str_curr_p); if (tail_match_p == NULL) { for (uint32_t i = 0; i < capture_count; ++i) { ecma_regexp_capture_t *const capture_p = re_ctx_p->captures_p + capture_start + i; capture_p->begin_p = NULL; capture_p->end_p = saved_captures_p[i]; } goto fail; } return tail_match_p; } case RE_OP_ASSERT_LOOKAHEAD_NEG: { const uint8_t qmin = re_get_byte (&bc_p); uint32_t capture_idx = re_get_value (&bc_p); const uint32_t capture_count = re_get_value (&bc_p); const uint32_t end_offset = re_get_value (&bc_p); /* If qmin is zero, the assertion implicitly matches. */ if (qmin > 0) { /* The first iteration will decide whether the assertion matches depending on whether * the iteration matched or not. */ const lit_utf8_byte_t *const matched_p = ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); if (ECMA_RE_STACK_LIMIT_REACHED (matched_p)) { return matched_p; } if (matched_p != NULL) { /* Nested capturing groups inside a negative lookahead can never capture, so we clear their results. */ const uint32_t capture_end = capture_idx + capture_count; while (capture_idx < capture_end) { re_ctx_p->captures_p[capture_idx++].begin_p = NULL; } goto fail; } } bc_p += end_offset; continue; } case RE_OP_CLASS_ESCAPE: { if (str_curr_p >= re_ctx_p->input_end_p) { goto fail; } const lit_code_point_t cp = ecma_regexp_advance (re_ctx_p, &str_curr_p); const ecma_class_escape_t escape = (ecma_class_escape_t) re_get_byte (&bc_p); if (!ecma_regexp_check_class_escape (cp, escape)) { goto fail; } continue; } case RE_OP_CHAR_CLASS: { if (str_curr_p >= re_ctx_p->input_end_p) { goto fail; } uint8_t flags = re_get_byte (&bc_p); uint32_t char_count = (flags & RE_CLASS_HAS_CHARS) ? re_get_value (&bc_p) : 0; uint32_t range_count = (flags & RE_CLASS_HAS_RANGES) ? re_get_value (&bc_p) : 0; const lit_code_point_t cp = ecma_regexp_advance (re_ctx_p, &str_curr_p); uint8_t escape_count = flags & RE_CLASS_ESCAPE_COUNT_MASK; while (escape_count > 0) { escape_count--; const ecma_class_escape_t escape = (ecma_class_escape_t) re_get_byte (&bc_p); if (ecma_regexp_check_class_escape (cp, escape)) { goto class_found; } } while (char_count > 0) { char_count--; const lit_code_point_t curr = re_get_char (&bc_p, re_ctx_p->flags & RE_FLAG_UNICODE); if (cp == curr) { goto class_found; } } while (range_count > 0) { range_count--; const lit_code_point_t begin = re_get_char (&bc_p, re_ctx_p->flags & RE_FLAG_UNICODE); if (cp < begin) { bc_p += re_ctx_p->char_size; continue; } const lit_code_point_t end = re_get_char (&bc_p, re_ctx_p->flags & RE_FLAG_UNICODE); if (cp <= end) { goto class_found; } } /* Not found */ if (flags & RE_CLASS_INVERT) { continue; } goto fail; class_found: if (flags & RE_CLASS_INVERT) { goto fail; } const uint32_t chars_size = char_count * re_ctx_p->char_size; const uint32_t ranges_size = range_count * re_ctx_p->char_size * 2; bc_p = bc_p + escape_count + chars_size + ranges_size; continue; } case RE_OP_UNICODE_PERIOD: { if (str_curr_p >= re_ctx_p->input_end_p) { goto fail; } const lit_code_point_t cp = ecma_regexp_unicode_advance (&str_curr_p, re_ctx_p->input_end_p); if (!(re_ctx_p->flags & RE_FLAG_DOTALL) && JERRY_UNLIKELY (cp <= LIT_UTF16_CODE_UNIT_MAX && lit_char_is_line_terminator ((ecma_char_t) cp))) { goto fail; } continue; } case RE_OP_PERIOD: { if (str_curr_p >= re_ctx_p->input_end_p) { goto fail; } const ecma_char_t ch = lit_cesu8_read_next (&str_curr_p); if (!(re_ctx_p->flags & RE_FLAG_DOTALL) && lit_char_is_line_terminator (ch)) { goto fail; } continue; } case RE_OP_CHAR: { if (str_curr_p >= re_ctx_p->input_end_p) { goto fail; } const lit_code_point_t ch1 = re_get_char (&bc_p, re_ctx_p->flags & RE_FLAG_UNICODE); const lit_code_point_t ch2 = ecma_regexp_advance (re_ctx_p, &str_curr_p); if (ch1 != ch2) { goto fail; } continue; } default: { JERRY_ASSERT (op == RE_OP_BYTE); if (str_curr_p >= re_ctx_p->input_end_p || *bc_p++ != *str_curr_p++) { goto fail; } continue; } } JERRY_UNREACHABLE (); fail: bc_p = next_alternative_p; if (bc_p == NULL || *bc_p++ != RE_OP_ALTERNATIVE_NEXT) { /* None of the alternatives matched. */ return NULL; } /* Get the end of the new alternative and continue execution. */ str_curr_p = str_start_p; const uint32_t offset = re_get_value (&bc_p); next_alternative_p = bc_p + offset; } } /* ecma_regexp_run */ /** * Match a RegExp at a specific position in the input string. * * @return pointer to the end of the matched sub-string * NULL, if pattern did not match */ static const lit_utf8_byte_t * ecma_regexp_match (ecma_regexp_ctx_t *re_ctx_p, /**< RegExp matcher context */ const uint8_t *bc_p, /**< pointer to the current RegExp bytecode */ const lit_utf8_byte_t *str_curr_p) /**< input string pointer */ { re_ctx_p->captures_p[RE_GLOBAL_CAPTURE].begin_p = str_curr_p; for (uint32_t i = 1; i < re_ctx_p->captures_count; ++i) { re_ctx_p->captures_p[i].begin_p = NULL; } return ecma_regexp_run (re_ctx_p, bc_p, str_curr_p); } /* ecma_regexp_match */ /* * Helper function to get the result of a capture * * @return string value, if capture is defined * undefined, otherwise */ ecma_value_t ecma_regexp_get_capture_value (const ecma_regexp_capture_t *const capture_p) /**< capture */ { if (ECMA_RE_IS_CAPTURE_DEFINED (capture_p)) { JERRY_ASSERT (capture_p->end_p >= capture_p->begin_p); const lit_utf8_size_t capture_size = (lit_utf8_size_t) (capture_p->end_p - capture_p->begin_p); ecma_string_t *const capture_str_p = ecma_new_ecma_string_from_utf8 (capture_p->begin_p, capture_size); return ecma_make_string_value (capture_str_p); } return ECMA_VALUE_UNDEFINED; } /* ecma_regexp_get_capture_value */ /** * Helper function to create a result array from the captures in a regexp context * * @return ecma value containing the created array object */ static ecma_value_t ecma_regexp_create_result_object (ecma_regexp_ctx_t *re_ctx_p, /**< regexp context */ ecma_string_t *input_string_p, /**< input ecma string */ uint32_t index) /**< match index */ { ecma_object_t *result_p = ecma_op_new_array_object (0); for (uint32_t i = 0; i < re_ctx_p->captures_count; i++) { ecma_value_t capture_value = ecma_regexp_get_capture_value (re_ctx_p->captures_p + i); ecma_builtin_helper_def_prop_by_index (result_p, i, capture_value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_free_value (capture_value); } ecma_builtin_helper_def_prop (result_p, ecma_get_magic_string (LIT_MAGIC_STRING_INDEX), ecma_make_uint32_value (index), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_builtin_helper_def_prop (result_p, ecma_get_magic_string (LIT_MAGIC_STRING_INPUT), ecma_make_string_value (input_string_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); return ecma_make_object_value (result_p); } /* ecma_regexp_create_result_object */ /** * Helper function to initialize a regexp match context */ static void ecma_regexp_initialize_context (ecma_regexp_ctx_t *ctx_p, /**< regexp context */ const re_compiled_code_t *bc_p, /**< regexp bytecode */ const lit_utf8_byte_t *input_start_p, /**< pointer to input string */ const lit_utf8_byte_t *input_end_p) /**< pointer to end of input string */ { JERRY_ASSERT (ctx_p != NULL); JERRY_ASSERT (bc_p != NULL); JERRY_ASSERT (input_start_p != NULL); JERRY_ASSERT (input_end_p >= input_start_p); ctx_p->flags = bc_p->header.status_flags; ctx_p->char_size = (ctx_p->flags & RE_FLAG_UNICODE) ? sizeof (lit_code_point_t) : sizeof (ecma_char_t); ctx_p->input_start_p = input_start_p; ctx_p->input_end_p = input_end_p; ctx_p->captures_count = bc_p->captures_count; ctx_p->non_captures_count = bc_p->non_captures_count; ctx_p->captures_p = (ecma_regexp_capture_t *) jmem_heap_alloc_block (ctx_p->captures_count * sizeof (ecma_regexp_capture_t)); if (ctx_p->non_captures_count > 0) { ctx_p->non_captures_p = (ecma_regexp_non_capture_t *) jmem_heap_alloc_block (ctx_p->non_captures_count * sizeof (ecma_regexp_non_capture_t)); } } /* ecma_regexp_initialize_context */ /** * Helper function to clean up a regexp context */ static void ecma_regexp_cleanup_context (ecma_regexp_ctx_t *ctx_p) /**< regexp context */ { JERRY_ASSERT (ctx_p != NULL); jmem_heap_free_block (ctx_p->captures_p, ctx_p->captures_count * sizeof (ecma_regexp_capture_t)); if (ctx_p->non_captures_count > 0) { jmem_heap_free_block (ctx_p->non_captures_p, ctx_p->non_captures_count * sizeof (ecma_regexp_non_capture_t)); } } /* ecma_regexp_cleanup_context */ /** * RegExp helper function to start the recursive matching algorithm * and create the result Array object * * See also: * ECMA-262 v5, 15.10.6.2 * ECMA-262 v11, 21.2.5.2.2 * * @return array object - if matched * null - otherwise * * May raise error. * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_regexp_exec_helper (ecma_object_t *regexp_object_p, /**< RegExp object */ ecma_string_t *input_string_p) /**< input string */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; lit_utf8_byte_t *matched_p; uint8_t *bc_start_p; /* 1. */ JERRY_ASSERT (ecma_object_is_regexp_object (ecma_make_object_value (regexp_object_p))); /* 9. */ ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) regexp_object_p; re_compiled_code_t *bc_p = ECMA_GET_INTERNAL_VALUE_POINTER (re_compiled_code_t, ext_object_p->u.cls.u3.value); /* 3. */ lit_utf8_size_t input_size; lit_utf8_size_t input_length; uint8_t input_flags = ECMA_STRING_FLAG_IS_ASCII; const lit_utf8_byte_t *input_buffer_p = ecma_string_get_chars (input_string_p, &input_size, &input_length, NULL, &input_flags); const lit_utf8_byte_t *input_curr_p = input_buffer_p; const lit_utf8_byte_t *input_end_p = input_buffer_p + input_size; ecma_regexp_ctx_t re_ctx; ecma_regexp_initialize_context (&re_ctx, bc_p, input_buffer_p, input_end_p); /* 4. */ ecma_length_t index = 0; ecma_value_t lastindex_value = ecma_op_object_get_by_magic_id (regexp_object_p, LIT_MAGIC_STRING_LASTINDEX_UL); ret_value = ecma_op_to_length (lastindex_value, &index); ecma_free_value (lastindex_value); if (ECMA_IS_VALUE_ERROR (ret_value)) { goto cleanup_context; } if (re_ctx.flags & (RE_FLAG_GLOBAL | RE_FLAG_STICKY)) { /* 12.a */ if (index > input_length) { goto fail_put_lastindex; } if (index > 0) { if (input_flags & ECMA_STRING_FLAG_IS_ASCII) { input_curr_p += index; } else { for (uint32_t i = 0; i < index; i++) { lit_utf8_incr (&input_curr_p); } } } } /* 8. */ else { index = 0; } /* 9. */ bc_start_p = (uint8_t *) (bc_p + 1); /* 11. */ matched_p = NULL; /* 12. */ JERRY_ASSERT (index <= input_length); while (true) { matched_p = (lit_utf8_byte_t *) ecma_regexp_match (&re_ctx, bc_start_p, input_curr_p); if (matched_p != NULL) { goto match_found; } /* 12.c.i */ if (re_ctx.flags & RE_FLAG_STICKY) { goto fail_put_lastindex; } /* 12.a */ if (input_curr_p >= input_end_p) { if (re_ctx.flags & RE_FLAG_GLOBAL) { goto fail_put_lastindex; } goto match_failed; } JERRY_ASSERT (input_curr_p < input_end_p); /* 12.c.ii */ index++; if (re_ctx.flags & RE_FLAG_UNICODE) { const lit_code_point_t cp = ecma_regexp_unicode_advance (&input_curr_p, input_end_p); if (cp > LIT_UTF16_CODE_UNIT_MAX) { index++; } continue; } lit_utf8_incr (&input_curr_p); } JERRY_UNREACHABLE (); fail_put_lastindex: /* We should only get here if the regexp is global or sticky */ JERRY_ASSERT ((re_ctx.flags & (RE_FLAG_GLOBAL | RE_FLAG_STICKY)) != 0); ret_value = ecma_op_object_put (regexp_object_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), ecma_make_integer_value (0), true); if (ECMA_IS_VALUE_ERROR (ret_value)) { goto cleanup_context; } JERRY_ASSERT (ecma_is_value_boolean (ret_value)); match_failed: /* 12.a.ii */ ret_value = ECMA_VALUE_NULL; goto cleanup_context; match_found: JERRY_ASSERT (matched_p != NULL); if (ECMA_RE_STACK_LIMIT_REACHED (matched_p)) { ret_value = ecma_raise_range_error (ECMA_ERR_STACK_LIMIT_EXCEEDED); goto cleanup_context; } JERRY_ASSERT (index <= input_length); /* 15. */ if (re_ctx.flags & (RE_FLAG_GLOBAL | RE_FLAG_STICKY)) { /* 13-14. */ lit_utf8_size_t match_length; const lit_utf8_byte_t *match_begin_p = re_ctx.captures_p[0].begin_p; const lit_utf8_byte_t *match_end_p = re_ctx.captures_p[0].end_p; if (input_flags & ECMA_STRING_FLAG_IS_ASCII) { match_length = (lit_utf8_size_t) (match_end_p - match_begin_p); } else { match_length = lit_utf8_string_length (match_begin_p, (lit_utf8_size_t) (match_end_p - match_begin_p)); } ret_value = ecma_op_object_put (regexp_object_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), ecma_make_uint32_value ((uint32_t) index + match_length), true); if (ECMA_IS_VALUE_ERROR (ret_value)) { goto cleanup_context; } JERRY_ASSERT (ecma_is_value_boolean (ret_value)); } /* 16-27. */ ret_value = ecma_regexp_create_result_object (&re_ctx, input_string_p, (uint32_t) index); cleanup_context: ecma_regexp_cleanup_context (&re_ctx); if (input_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) input_buffer_p, input_size); } return ret_value; } /* ecma_regexp_exec_helper */ /** * Helper function for converting a RegExp pattern parameter to string. * * See also: * RegExp.compile * RegExp dispatch call * * @return empty value if success, error value otherwise * Returned value must be freed with ecma_free_value. */ ecma_string_t * ecma_regexp_read_pattern_str_helper (ecma_value_t pattern_arg) /**< the RegExp pattern */ { if (!ecma_is_value_undefined (pattern_arg)) { ecma_string_t *pattern_string_p = ecma_op_to_string (pattern_arg); if (JERRY_UNLIKELY (pattern_string_p == NULL) || !ecma_string_is_empty (pattern_string_p)) { return pattern_string_p; } } return ecma_get_magic_string (LIT_MAGIC_STRING_EMPTY_NON_CAPTURE_GROUP); } /* ecma_regexp_read_pattern_str_helper */ /** * Helper function for RegExp based string searches * * See also: * ECMA-262 v6, 21.2.5.9 * * @return index of the match */ ecma_value_t ecma_regexp_search_helper (ecma_value_t regexp_arg, /**< regexp argument */ ecma_value_t string_arg) /**< string argument */ { /* 2. */ if (!ecma_is_value_object (regexp_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_value_t result = ECMA_VALUE_ERROR; ecma_value_t current_last_index; ecma_value_t match; bool same_value; /* 3. */ ecma_string_t *const string_p = ecma_op_to_string (string_arg); if (string_p == NULL) { return result; } ecma_object_t *const regexp_object_p = ecma_get_object_from_value (regexp_arg); /* 4. */ ecma_string_t *const last_index_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL); const ecma_value_t prev_last_index = ecma_op_object_get (regexp_object_p, last_index_str_p); if (ECMA_IS_VALUE_ERROR (prev_last_index)) { goto cleanup_string; } /* 5. */ if (prev_last_index != ecma_make_uint32_value (0)) { const ecma_value_t status = ecma_op_object_put (regexp_object_p, last_index_str_p, ecma_make_uint32_value (0), true); if (ECMA_IS_VALUE_ERROR (status)) { goto cleanup_prev_last_index; } JERRY_ASSERT (ecma_is_value_boolean (status)); } /* 6. */ match = ecma_op_regexp_exec (regexp_arg, string_p); if (ECMA_IS_VALUE_ERROR (match)) { goto cleanup_prev_last_index; } /* 7. */ current_last_index = ecma_op_object_get (regexp_object_p, last_index_str_p); if (ECMA_IS_VALUE_ERROR (current_last_index)) { ecma_free_value (match); goto cleanup_prev_last_index; } same_value = ecma_op_same_value (prev_last_index, current_last_index); ecma_free_value (current_last_index); /* 8. */ if (!same_value) { result = ecma_op_object_put (regexp_object_p, last_index_str_p, prev_last_index, true); if (ECMA_IS_VALUE_ERROR (result)) { ecma_free_value (match); goto cleanup_prev_last_index; } JERRY_ASSERT (ecma_is_value_boolean (result)); } /* 9-10. */ if (ecma_is_value_null (match)) { result = ecma_make_int32_value (-1); } else { ecma_object_t *const match_p = ecma_get_object_from_value (match); result = ecma_op_object_get_by_magic_id (match_p, LIT_MAGIC_STRING_INDEX); ecma_deref_object (match_p); } cleanup_prev_last_index: ecma_free_value (prev_last_index); cleanup_string: ecma_deref_ecma_string (string_p); return result; } /* ecma_regexp_search_helper */ /** * Helper function for RegExp based string split operation * * See also: * ECMA-262 v6, 21.2.5.11 * * @return array of split and captured strings */ ecma_value_t ecma_regexp_split_helper (ecma_value_t this_arg, /**< this value */ ecma_value_t string_arg, /**< string value */ ecma_value_t limit_arg) /**< limit value */ { /* 2. */ if (!ecma_is_value_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_value_t result = ECMA_VALUE_ERROR; ecma_length_t current_index; ecma_length_t previous_index; ecma_object_t *constructor_obj_p; ecma_value_t flags; ecma_string_t *flags_str_p; lit_utf8_size_t flags_size; uint8_t flags_str_flags; lit_utf8_byte_t *flags_buffer_p; lit_utf8_byte_t *flags_end_p; ecma_value_t arguments[2]; ecma_value_t splitter; ecma_object_t *splitter_obj_p; ecma_object_t *array_p; ecma_value_t array; ecma_string_t *end_str_p; ecma_string_t *lastindex_str_p; lit_utf8_size_t string_length; uint32_t array_length; uint32_t limit; bool unicode; bool sticky; /* 3-4. */ ecma_string_t *const string_p = ecma_op_to_string (string_arg); if (string_p == NULL) { return result; } /* 5-6. */ ecma_object_t *const regexp_obj_p = ecma_get_object_from_value (this_arg); ecma_value_t constructor = ecma_op_species_constructor (regexp_obj_p, ECMA_BUILTIN_ID_REGEXP); if (ECMA_IS_VALUE_ERROR (constructor)) { goto cleanup_string; } constructor_obj_p = ecma_get_object_from_value (constructor); /* 7-8. */ flags = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_FLAGS); if (ECMA_IS_VALUE_ERROR (flags)) { ecma_deref_object (constructor_obj_p); goto cleanup_string; } flags_str_p = ecma_op_to_string (flags); ecma_free_value (flags); if (JERRY_UNLIKELY (flags_str_p == NULL)) { ecma_deref_object (constructor_obj_p); goto cleanup_string; } flags_str_flags = ECMA_STRING_FLAG_IS_ASCII; flags_buffer_p = (lit_utf8_byte_t *) ecma_string_get_chars (flags_str_p, &flags_size, NULL, NULL, &flags_str_flags); unicode = false; sticky = false; /* 9-11. */ flags_end_p = flags_buffer_p + flags_size; for (const lit_utf8_byte_t *current_p = flags_buffer_p; current_p < flags_end_p; ++current_p) { switch (*current_p) { case LIT_CHAR_LOWERCASE_U: { unicode = true; break; } case LIT_CHAR_LOWERCASE_Y: { sticky = true; break; } } } if (flags_str_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) flags_buffer_p, flags_size); } /* 12. */ if (!sticky) { ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (flags_str_p); ecma_stringbuilder_append_byte (&builder, LIT_CHAR_LOWERCASE_Y); ecma_deref_ecma_string (flags_str_p); flags_str_p = ecma_stringbuilder_finalize (&builder); } /* 13-14. */ arguments[0] = this_arg; arguments[1] = ecma_make_string_value (flags_str_p); splitter = ecma_op_function_construct (constructor_obj_p, constructor_obj_p, arguments, 2); ecma_deref_ecma_string (flags_str_p); ecma_deref_object (constructor_obj_p); if (ECMA_IS_VALUE_ERROR (splitter)) { goto cleanup_string; } splitter_obj_p = ecma_get_object_from_value (splitter); /* 17. */ limit = UINT32_MAX - 1; if (!ecma_is_value_undefined (limit_arg)) { /* ECMA-262 v11, 21.2.5.13 13 */ ecma_number_t num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (limit_arg, &num))) { goto cleanup_splitter; } limit = ecma_number_to_uint32 (num); } /* 15. */ array_p = ecma_op_new_array_object (0); array = ecma_make_object_value (array_p); /* 21. */ if (limit == 0) { result = array; goto cleanup_splitter; } string_length = ecma_string_get_length (string_p); array_length = 0; /* 22. */ if (string_length == 0) { const ecma_value_t match = ecma_op_regexp_exec (splitter, string_p); if (ECMA_IS_VALUE_ERROR (match)) { goto cleanup_array; } if (ecma_is_value_null (match)) { result = ecma_builtin_helper_def_prop_by_index (array_p, array_length, ecma_make_string_value (string_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); JERRY_ASSERT (ecma_is_value_true (result)); } ecma_free_value (match); result = array; goto cleanup_splitter; } /* 23. */ current_index = 0; previous_index = 0; lastindex_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL); /* 24. */ while (current_index < string_length) { /* 24.a-b. */ ecma_value_t index_value = ecma_make_length_value (current_index); result = ecma_op_object_put (splitter_obj_p, lastindex_str_p, index_value, true); ecma_free_value (index_value); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_array; } JERRY_ASSERT (ecma_is_value_true (result)); /* 24.c-d. */ result = ecma_op_regexp_exec (splitter, string_p); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_array; } /* 24.e. */ if (ecma_is_value_null (result)) { current_index = ecma_op_advance_string_index (string_p, current_index, unicode); continue; } ecma_object_t *const match_array_p = ecma_get_object_from_value (result); /* 24.f.i. */ result = ecma_op_object_get (splitter_obj_p, lastindex_str_p); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (match_array_p); goto cleanup_array; } ecma_length_t end_index; const ecma_value_t length_value = ecma_op_to_length (result, &end_index); ecma_free_value (result); if (ECMA_IS_VALUE_ERROR (length_value)) { result = ECMA_VALUE_ERROR; ecma_deref_object (match_array_p); goto cleanup_array; } /* ECMA-262 v11, 21.2.5.11 19.d.ii */ if (end_index > string_length) { end_index = string_length; } /* 24.f.iii. */ if (previous_index == end_index) { ecma_deref_object (match_array_p); current_index = ecma_op_advance_string_index (string_p, current_index, unicode); continue; } /* 24.f.iv.1-4. */ JERRY_ASSERT (previous_index <= string_length && current_index <= string_length); ecma_string_t *const split_str_p = ecma_string_substr (string_p, (lit_utf8_size_t) previous_index, (lit_utf8_size_t) current_index); result = ecma_builtin_helper_def_prop_by_index (array_p, array_length++, ecma_make_string_value (split_str_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); JERRY_ASSERT (ecma_is_value_true (result)); ecma_deref_ecma_string (split_str_p); /* 24.f.iv.5. */ if (array_length == limit) { ecma_deref_object (match_array_p); result = array; goto cleanup_splitter; } /* 24.f.iv.6. */ previous_index = end_index; /* 24.f.iv.7-8. */ ecma_length_t match_length; result = ecma_op_object_get_length (match_array_p, &match_length); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (match_array_p); goto cleanup_array; } /* 24.f.iv.9. */ match_length = (match_length > 0) ? match_length - 1 : match_length; ecma_length_t match_index = 1; while (match_index <= match_length) { /* 24.f.iv.11.a-b. */ result = ecma_op_object_get_by_index (match_array_p, match_index++); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (match_array_p); goto cleanup_array; } const ecma_value_t capture = result; /* 24.f.iv.11.c. */ result = ecma_builtin_helper_def_prop_by_index (array_p, array_length++, capture, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); JERRY_ASSERT (ecma_is_value_true (result)); ecma_free_value (capture); if (array_length == limit) { ecma_deref_object (match_array_p); result = array; goto cleanup_splitter; } } /* 24.f.iv.12. */ JERRY_ASSERT (end_index <= UINT32_MAX); current_index = (uint32_t) end_index; ecma_deref_object (match_array_p); } JERRY_ASSERT (previous_index <= string_length); end_str_p = ecma_string_substr (string_p, (lit_utf8_size_t) previous_index, string_length); result = ecma_builtin_helper_def_prop_by_index (array_p, array_length++, ecma_make_string_value (end_str_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); JERRY_ASSERT (ecma_is_value_true (result)); ecma_deref_ecma_string (end_str_p); result = array; goto cleanup_splitter; cleanup_array: ecma_deref_object (array_p); cleanup_splitter: ecma_deref_object (splitter_obj_p); cleanup_string: ecma_deref_ecma_string (string_p); return result; } /* ecma_regexp_split_helper */ /** * Fast path for RegExp based replace operation * * This method assumes the following: * - The RegExp object is a built-in RegExp * - The 'exec' method of the RegExp object is the built-in 'exec' method * - The 'lastIndex' property is writable * * The standard would normally require us to first execute the regexp and collect the results, * and after that iterate over the collected results and replace them. * The assumptions above guarantee that during the matching phase there will be no exceptions thrown, * which means we can do the match/replace in a single loop, without collecting the results. * * @return string value if successful * thrown value otherwise */ static ecma_value_t ecma_regexp_replace_helper_fast (ecma_replace_context_t *ctx_p, /**u.cls.u3.value); ecma_bytecode_ref ((ecma_compiled_code_t *) bc_p); JERRY_ASSERT (bc_p != NULL); uint8_t string_flags = ECMA_STRING_FLAG_IS_ASCII; lit_utf8_size_t string_length; ctx_p->string_p = ecma_string_get_chars (string_p, &(ctx_p->string_size), &string_length, NULL, &string_flags); const lit_utf8_byte_t *const string_end_p = ctx_p->string_p + ctx_p->string_size; const uint8_t *const bc_start_p = (const uint8_t *) (bc_p + 1); const lit_utf8_byte_t *matched_p = NULL; const lit_utf8_byte_t *current_p = ctx_p->string_p; const lit_utf8_byte_t *last_append_p = current_p; ecma_length_t index; lit_utf8_size_t trailing_size; ecma_regexp_ctx_t re_ctx; ecma_regexp_initialize_context (&re_ctx, bc_p, ctx_p->string_p, string_end_p); /* lastIndex must be accessed to remain consistent with the standard, even though we may not need the value. */ ecma_value_t lastindex_value = ecma_op_object_get_by_magic_id ((ecma_object_t *) re_obj_p, LIT_MAGIC_STRING_LASTINDEX_UL); ecma_value_t result = ecma_op_to_length (lastindex_value, &index); ecma_free_value (lastindex_value); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_context; } /* Only non-global sticky matches use the lastIndex value, otherwise the starting index is 0. */ if (JERRY_UNLIKELY ((ctx_p->flags & RE_FLAG_GLOBAL) == 0 && (re_ctx.flags & RE_FLAG_STICKY) != 0)) { if (index > string_length) { result = ecma_op_object_put ((ecma_object_t *) re_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), ecma_make_uint32_value (0), true); if (!ECMA_IS_VALUE_ERROR (result)) { JERRY_ASSERT (ecma_is_value_true (result)); ecma_ref_ecma_string (string_p); result = ecma_make_string_value (string_p); } goto cleanup_context; } if (string_flags & ECMA_STRING_FLAG_IS_ASCII) { current_p += index; } else { ecma_length_t counter = index; while (counter--) { lit_utf8_incr (¤t_p); } } } else { index = 0; } ctx_p->builder = ecma_stringbuilder_create (); ctx_p->capture_count = re_ctx.captures_count; ctx_p->u.captures_p = re_ctx.captures_p; while (true) { matched_p = ecma_regexp_match (&re_ctx, bc_start_p, current_p); if (matched_p != NULL) { if (ECMA_RE_STACK_LIMIT_REACHED (matched_p)) { result = ecma_raise_range_error (ECMA_ERR_STACK_LIMIT_EXCEEDED); goto cleanup_builder; } const lit_utf8_size_t remaining_size = (lit_utf8_size_t) (current_p - last_append_p); ecma_stringbuilder_append_raw (&(ctx_p->builder), last_append_p, remaining_size); if (ctx_p->replace_str_p != NULL) { ctx_p->matched_p = current_p; const ecma_regexp_capture_t *const global_capture_p = re_ctx.captures_p; ctx_p->matched_size = (lit_utf8_size_t) (global_capture_p->end_p - global_capture_p->begin_p); ctx_p->match_byte_pos = (lit_utf8_size_t) (current_p - re_ctx.input_start_p); ecma_builtin_replace_substitute (ctx_p); } else { ecma_collection_t *arguments_p = ecma_new_collection (); for (uint32_t i = 0; i < re_ctx.captures_count; i++) { ecma_value_t capture = ecma_regexp_get_capture_value (re_ctx.captures_p + i); ecma_collection_push_back (arguments_p, capture); } ecma_collection_push_back (arguments_p, ecma_make_length_value (index)); ecma_ref_ecma_string (string_p); ecma_collection_push_back (arguments_p, ecma_make_string_value (string_p)); ecma_object_t *function_p = ecma_get_object_from_value (replace_arg); result = ecma_op_function_call (function_p, ECMA_VALUE_UNDEFINED, arguments_p->buffer_p, arguments_p->item_count); ecma_collection_free (arguments_p); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_builder; } /* 16.m.v */ ecma_string_t *const replace_result_p = ecma_op_to_string (result); ecma_free_value (result); if (replace_result_p == NULL) { result = ECMA_VALUE_ERROR; goto cleanup_builder; } ecma_stringbuilder_append (&(ctx_p->builder), replace_result_p); ecma_deref_ecma_string (replace_result_p); } const ecma_regexp_capture_t *global_capture_p = re_ctx.captures_p; last_append_p = global_capture_p->end_p; const lit_utf8_size_t matched_size = (lit_utf8_size_t) (global_capture_p->end_p - global_capture_p->begin_p); const bool is_ascii = (string_flags & ECMA_STRING_FLAG_IS_ASCII) != 0; index += is_ascii ? matched_size : lit_utf8_string_length (current_p, matched_size); if (!(ctx_p->flags & RE_FLAG_GLOBAL)) { if (JERRY_UNLIKELY ((re_ctx.flags & RE_FLAG_STICKY) != 0)) { ecma_value_t index_value = ecma_make_length_value (index); result = ecma_op_object_put ((ecma_object_t *) re_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), index_value, true); ecma_free_value (index_value); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_builder; } } break; } if (matched_size > 0) { current_p = last_append_p; continue; } } else if (JERRY_UNLIKELY ((re_ctx.flags & RE_FLAG_STICKY) != 0)) { result = ecma_op_object_put ((ecma_object_t *) re_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), ecma_make_uint32_value (0), true); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_builder; } break; } if (current_p >= string_end_p) { break; } if ((ctx_p->flags & RE_FLAG_UNICODE) != 0) { index++; const lit_code_point_t cp = ecma_regexp_unicode_advance (¤t_p, string_end_p); if (cp > LIT_UTF16_CODE_UNIT_MAX) { index++; } continue; } index++; lit_utf8_incr (¤t_p); } trailing_size = (lit_utf8_size_t) (string_end_p - last_append_p); ecma_stringbuilder_append_raw (&(ctx_p->builder), last_append_p, trailing_size); result = ecma_make_string_value (ecma_stringbuilder_finalize (&(ctx_p->builder))); goto cleanup_context; cleanup_builder: ecma_stringbuilder_destroy (&(ctx_p->builder)); cleanup_context: ecma_regexp_cleanup_context (&re_ctx); ecma_bytecode_deref ((ecma_compiled_code_t *) bc_p); if (string_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) ctx_p->string_p, ctx_p->string_size); } return result; } /* ecma_regexp_replace_helper_fast */ /** * Helper function for RegExp based replacing * * See also: * String.prototype.replace * RegExp.prototype[@@replace] * * @return result string of the replacement, if successful * error value, otherwise */ ecma_value_t ecma_regexp_replace_helper (ecma_value_t this_arg, /**< this argument */ ecma_value_t string_arg, /**< source string */ ecma_value_t replace_arg) /**< replace string */ { /* 2. */ if (!ecma_is_value_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } lit_utf8_size_t index; uint8_t string_flags; ecma_object_t *this_obj_p = ecma_get_object_from_value (this_arg); ecma_replace_context_t replace_ctx; ecma_collection_t *results_p; lit_utf8_size_t string_length; lit_utf8_byte_t *source_position_p; lit_utf8_byte_t *string_end_p; replace_ctx.flags = RE_FLAG_EMPTY; /* 3. */ ecma_string_t *string_p = ecma_op_to_string (string_arg); if (string_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ECMA_VALUE_ERROR; /* 6. */ replace_ctx.replace_str_p = NULL; if (!ecma_op_is_callable (replace_arg)) { replace_ctx.replace_str_p = ecma_op_to_string (replace_arg); if (replace_ctx.replace_str_p == NULL) { goto cleanup_string; } } /* 8 */ result = ecma_op_object_get_by_magic_id (this_obj_p, LIT_MAGIC_STRING_GLOBAL); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_replace; } if (ecma_op_to_boolean (result)) { replace_ctx.flags |= RE_FLAG_GLOBAL; } ecma_free_value (result); string_length = ecma_string_get_length (string_p); /* 10. */ if (replace_ctx.flags & RE_FLAG_GLOBAL) { result = ecma_op_object_get_by_magic_id (this_obj_p, LIT_MAGIC_STRING_UNICODE); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_replace; } if (ecma_op_to_boolean (result)) { replace_ctx.flags |= RE_FLAG_UNICODE; } ecma_free_value (result); result = ecma_op_object_put (this_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), ecma_make_uint32_value (0), true); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_replace; } JERRY_ASSERT (ecma_is_value_boolean (result)); } result = ecma_op_object_get_by_magic_id (this_obj_p, LIT_MAGIC_STRING_EXEC); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_replace; } /* Check for fast path. */ if (ecma_op_is_callable (result)) { ecma_extended_object_t *function_p = (ecma_extended_object_t *) ecma_get_object_from_value (result); if (ecma_object_class_is (this_obj_p, ECMA_OBJECT_CLASS_REGEXP) && ecma_builtin_is_regexp_exec (function_p)) { ecma_deref_object ((ecma_object_t *) function_p); result = ecma_regexp_replace_helper_fast (&replace_ctx, (ecma_extended_object_t *) this_obj_p, string_p, replace_arg); goto cleanup_replace; } } results_p = ecma_new_collection (); while (true) { /* 13.a */ if (ecma_op_is_callable (result)) { ecma_object_t *const function_p = ecma_get_object_from_value (result); ecma_value_t arguments[] = { ecma_make_string_value (string_p) }; result = ecma_op_function_call (function_p, this_arg, arguments, 1); ecma_deref_object (function_p); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_results; } if (!ecma_is_value_object (result) && !ecma_is_value_null (result)) { ecma_free_value (result); result = ecma_raise_type_error (ECMA_ERR_RETURN_VALUE_OF_EXEC_MUST_BE_AN_OBJECT_OR_NULL); goto cleanup_results; } } else { ecma_free_value (result); if (!ecma_object_class_is (this_obj_p, ECMA_OBJECT_CLASS_REGEXP)) { result = ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_REG_EXP_OBJECT); goto cleanup_results; } result = ecma_regexp_exec_helper (this_obj_p, string_p); } /* 13.c */ if (ecma_is_value_null (result)) { break; } /* 13.d.i */ ecma_collection_push_back (results_p, result); if ((replace_ctx.flags & RE_FLAG_GLOBAL) == 0) { break; } /* 13.d.iii.1 */ result = ecma_op_object_get_by_index (ecma_get_object_from_value (result), 0); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_results; } ecma_string_t *match_str_p = ecma_op_to_string (result); ecma_free_value (result); if (match_str_p == NULL) { result = ECMA_VALUE_ERROR; goto cleanup_results; } const bool is_empty = ecma_string_is_empty (match_str_p); ecma_deref_ecma_string (match_str_p); /* 13.d.iii.3 */ if (is_empty) { result = ecma_op_object_get_by_magic_id (this_obj_p, LIT_MAGIC_STRING_LASTINDEX_UL); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_results; } ecma_value_t last_index = result; ecma_length_t index; result = ecma_op_to_length (last_index, &index); ecma_free_value (last_index); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_results; } index = ecma_op_advance_string_index (string_p, index, (replace_ctx.flags & RE_FLAG_UNICODE) != 0); last_index = ecma_make_length_value (index); /* 10.d.iii.3.c */ result = ecma_op_object_put (this_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), last_index, true); ecma_free_value (last_index); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_results; } JERRY_ASSERT (ecma_is_value_boolean (result)); } result = ecma_op_object_get_by_magic_id (this_obj_p, LIT_MAGIC_STRING_EXEC); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_results; } } string_flags = ECMA_STRING_FLAG_IS_ASCII; replace_ctx.string_p = ecma_string_get_chars (string_p, &(replace_ctx.string_size), NULL, NULL, &string_flags); /* 14. */ replace_ctx.builder = ecma_stringbuilder_create (); replace_ctx.matched_p = NULL; replace_ctx.capture_count = 0; index = 0; /* 15. */ source_position_p = (lit_utf8_byte_t*) replace_ctx.string_p; string_end_p = (lit_utf8_byte_t*) replace_ctx.string_p + replace_ctx.string_size; /* 16. */ for (ecma_value_t *current_p = results_p->buffer_p; current_p < results_p->buffer_p + results_p->item_count; current_p++) { /* 16.a */ ecma_object_t *current_object_p = ecma_get_object_from_value (*current_p); ecma_length_t capture_count; result = ecma_op_object_get_length (current_object_p, &capture_count); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_builder; } /* 16.c */ capture_count = (capture_count > 0) ? capture_count - 1 : capture_count; /* 16.d */ result = ecma_op_object_get_by_index (current_object_p, 0); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_builder; } ecma_string_t *matched_str_p = ecma_op_to_string (result); ecma_free_value (result); /* 16.e */ if (matched_str_p == NULL) { result = ECMA_VALUE_ERROR; goto cleanup_builder; } /* 16.g */ result = ecma_op_object_get_by_magic_id (current_object_p, LIT_MAGIC_STRING_INDEX); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_ecma_string (matched_str_p); goto cleanup_builder; } const ecma_value_t index_value = result; ecma_number_t position_num; result = ecma_op_to_integer (index_value, &position_num); ecma_free_value (index_value); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_ecma_string (matched_str_p); goto cleanup_builder; } /* 16.i */ lit_utf8_size_t position = JERRY_MIN ((lit_utf8_size_t) JERRY_MAX (position_num, 0.0f), string_length); /* 16.k */ ecma_collection_t *arguments_p = ecma_new_collection (); ecma_collection_push_back (arguments_p, ecma_make_string_value (matched_str_p)); /* 16.j, l */ ecma_length_t n = 1; while (n <= capture_count) { result = ecma_op_object_get_by_index (current_object_p, n); if (ECMA_IS_VALUE_ERROR (result)) { ecma_collection_free (arguments_p); goto cleanup_builder; } /* 16.l.iii */ if (!ecma_is_value_undefined (result)) { ecma_string_t *capture_str_p = ecma_op_to_string (result); ecma_free_value (result); if (capture_str_p == NULL) { ecma_collection_free (arguments_p); result = ECMA_VALUE_ERROR; goto cleanup_builder; } result = ecma_make_string_value (capture_str_p); } /* 16.l.iv */ ecma_collection_push_back (arguments_p, result); n++; } const bool should_replace = (position >= index); /* 16.p */ if (should_replace) { const lit_utf8_byte_t *match_position_p; const lit_utf8_size_t matched_str_size = ecma_string_get_size (matched_str_p); const lit_utf8_size_t matched_str_length = ecma_string_get_length (matched_str_p); if (string_flags & ECMA_STRING_FLAG_IS_ASCII) { match_position_p = replace_ctx.string_p + position; } else { match_position_p = source_position_p; lit_utf8_size_t distance = position - index; while (distance--) { lit_utf8_incr (&match_position_p); } } ecma_stringbuilder_append_raw (&replace_ctx.builder, source_position_p, (lit_utf8_size_t) (match_position_p - source_position_p)); replace_ctx.match_byte_pos = (lit_utf8_size_t) (match_position_p - replace_ctx.string_p); if ((string_flags & ECMA_STRING_FLAG_IS_ASCII) && matched_str_size == matched_str_length) { source_position_p = (lit_utf8_byte_t*) JERRY_MIN (match_position_p + matched_str_size, string_end_p); } else { lit_utf8_size_t code_unit_count = matched_str_length; while (code_unit_count-- > 0 && JERRY_LIKELY (match_position_p < string_end_p)) { lit_utf8_incr (&match_position_p); } source_position_p = (lit_utf8_byte_t*) match_position_p; } index = JERRY_MIN (position + matched_str_length, string_length); } /* 16.m */ if (replace_ctx.replace_str_p == NULL) { /* 16.m.i-ii. * arguments_p already contains <> */ /* 16.m.iii */ ecma_collection_push_back (arguments_p, ecma_make_uint32_value (position)); ecma_ref_ecma_string (string_p); ecma_collection_push_back (arguments_p, ecma_make_string_value (string_p)); result = ecma_op_function_call (ecma_get_object_from_value (replace_arg), ECMA_VALUE_UNDEFINED, arguments_p->buffer_p, arguments_p->item_count); ecma_collection_free (arguments_p); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_builder; } /* 16.m.v */ ecma_string_t *const replace_result_p = ecma_op_to_string (result); ecma_free_value (result); if (replace_result_p == NULL) { result = ECMA_VALUE_ERROR; goto cleanup_builder; } /* 16.m/p */ if (should_replace) { ecma_stringbuilder_append (&replace_ctx.builder, replace_result_p); } ecma_deref_ecma_string (replace_result_p); } else { /* 16.n/p */ if (should_replace) { replace_ctx.u.collection_p = arguments_p; ecma_builtin_replace_substitute (&replace_ctx); } ecma_collection_free (arguments_p); } } /* 18. */ JERRY_ASSERT (index <= string_length); ecma_stringbuilder_append_raw (&(replace_ctx.builder), source_position_p, (lit_utf8_size_t) (string_end_p - source_position_p)); result = ecma_make_string_value (ecma_stringbuilder_finalize (&replace_ctx.builder)); goto cleanup_chars; cleanup_builder: ecma_stringbuilder_destroy (&replace_ctx.builder); cleanup_chars: if (string_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) replace_ctx.string_p, replace_ctx.string_size); } cleanup_results: ecma_collection_free (results_p); cleanup_replace: if (replace_ctx.replace_str_p != NULL) { ecma_deref_ecma_string (replace_ctx.replace_str_p); } cleanup_string: ecma_deref_ecma_string (string_p); return result; } /* ecma_regexp_replace_helper */ /** * Helper function for RegExp based matching * * See also: * String.prototype.match * RegExp.prototype[@@match] * * @return ecma_value_t */ ecma_value_t ecma_regexp_match_helper (ecma_value_t this_arg, /**< this argument */ ecma_value_t string_arg) /**< source string */ { if (!ecma_is_value_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_string_t *str_p = ecma_op_to_string (string_arg); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *obj_p = ecma_get_object_from_value (this_arg); ecma_value_t global_value = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_GLOBAL); if (ECMA_IS_VALUE_ERROR (global_value)) { ecma_deref_ecma_string (str_p); return global_value; } bool global = ecma_op_to_boolean (global_value); ecma_free_value (global_value); if (!global) { ecma_value_t result = ecma_op_regexp_exec (this_arg, str_p); ecma_deref_ecma_string (str_p); return result; } ecma_value_t full_unicode_value = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_UNICODE); if (ECMA_IS_VALUE_ERROR (full_unicode_value)) { ecma_deref_ecma_string (str_p); return full_unicode_value; } bool full_unicode = ecma_op_to_boolean (full_unicode_value); ecma_free_value (full_unicode_value); ecma_value_t set_status = ecma_op_object_put (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), ecma_make_uint32_value (0), true); if (ECMA_IS_VALUE_ERROR (set_status)) { ecma_deref_ecma_string (str_p); return set_status; } ecma_value_t ret_value = ECMA_VALUE_ERROR; ecma_object_t *result_array_p = ecma_op_new_array_object (0); uint32_t n = 0; while (true) { ecma_value_t result_value = ecma_op_regexp_exec (this_arg, str_p); if (ECMA_IS_VALUE_ERROR (result_value)) { goto result_cleanup; } if (ecma_is_value_null (result_value)) { if (n == 0) { ret_value = ECMA_VALUE_NULL; goto result_cleanup; } ecma_deref_ecma_string (str_p); return ecma_make_object_value (result_array_p); } ecma_object_t *result_value_p = ecma_get_object_from_value (result_value); ecma_value_t match_value = ecma_op_object_get_by_index (result_value_p, 0); ecma_deref_object (result_value_p); if (ECMA_IS_VALUE_ERROR (match_value)) { goto result_cleanup; } ecma_string_t *match_str_p = ecma_op_to_string (match_value); ecma_free_value (match_value); if (JERRY_UNLIKELY (match_str_p == NULL)) { goto result_cleanup; } ecma_value_t new_prop = ecma_builtin_helper_def_prop_by_index (result_array_p, n, ecma_make_string_value (match_str_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (new_prop)); const bool is_match_empty = ecma_string_is_empty (match_str_p); ecma_deref_ecma_string (match_str_p); if (is_match_empty) { ecma_value_t last_index = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_LASTINDEX_UL); if (ECMA_IS_VALUE_ERROR (last_index)) { goto result_cleanup; } ecma_length_t index; ecma_value_t length_value = ecma_op_to_length (last_index, &index); ecma_free_value (last_index); if (ECMA_IS_VALUE_ERROR (length_value)) { goto result_cleanup; } index = ecma_op_advance_string_index (str_p, index, full_unicode); last_index = ecma_make_length_value (index); ecma_value_t next_set_status = ecma_op_object_put (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), last_index, true); ecma_free_value (last_index); if (ECMA_IS_VALUE_ERROR (next_set_status)) { goto result_cleanup; } } n++; } result_cleanup: ecma_deref_ecma_string (str_p); ecma_deref_object (result_array_p); return ret_value; } /* ecma_regexp_match_helper */ /** * RegExpExec operation * * See also: * ECMA-262 v6.0, 21.2.5.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_regexp_exec (ecma_value_t this_arg, /**< this argument */ ecma_string_t *str_p) /**< input string */ { ecma_object_t *arg_obj_p = ecma_get_object_from_value (this_arg); ecma_value_t exec = ecma_op_object_get_by_magic_id (arg_obj_p, LIT_MAGIC_STRING_EXEC); if (ECMA_IS_VALUE_ERROR (exec)) { return exec; } if (ecma_op_is_callable (exec)) { ecma_object_t *function_p = ecma_get_object_from_value (exec); ecma_value_t arguments[] = { ecma_make_string_value (str_p) }; ecma_value_t result = ecma_op_function_call (function_p, this_arg, arguments, 1); ecma_deref_object (function_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } if (!ecma_is_value_object (result) && !ecma_is_value_null (result)) { ecma_free_value (result); return ecma_raise_type_error (ECMA_ERR_RETURN_VALUE_OF_EXEC_MUST_BE_AN_OBJECT_OR_NULL); } return result; } else { ecma_free_value (exec); } if (!ecma_object_is_regexp_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_REG_EXP); } return ecma_regexp_exec_helper (arg_obj_p, str_p); } /* ecma_op_regexp_exec */ /** * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwRasterC.h000664 001750 001750 00000015426 15164251010 036270 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ template static void inline cRasterTranslucentPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity) { //TODO: 64bits faster? if (opacity == 255) { for (uint32_t x = 0; x < len; ++x, ++dst, ++src) { *dst = *src + ALPHA_BLEND(*dst, IA(*src)); } } else { for (uint32_t x = 0; x < len; ++x, ++dst, ++src) { auto tmp = ALPHA_BLEND(*src, opacity); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } } template static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity) { //TODO: 64bits faster? if (opacity == 255) { for (uint32_t x = 0; x < len; ++x, ++dst, ++src) { *dst = *src; } } else { cRasterTranslucentPixels(dst, src, len, opacity); } } template static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len) { dst += offset; //fix the misaligned memory auto alignOffset = (long long) dst % 8; if (alignOffset > 0) { if (sizeof(PIXEL_T) == 4) alignOffset /= 4; else if (sizeof(PIXEL_T) == 1) alignOffset = 8 - alignOffset; while (alignOffset > 0 && len > 0) { *dst++ = val; --len; --alignOffset; } } //64bits faster clear if ((sizeof(PIXEL_T) == 4)) { auto val64 = (uint64_t(val) << 32) | uint64_t(val); while (len > 1) { *reinterpret_cast(dst) = val64; len -= 2; dst += 2; } } else if (sizeof(PIXEL_T) == 1) { auto val32 = (uint32_t(val) << 24) | (uint32_t(val) << 16) | (uint32_t(val) << 8) | uint32_t(val); auto val64 = (uint64_t(val32) << 32) | val32; while (len > 7) { *reinterpret_cast(dst) = val64; len -= 8; dst += 8; } } //leftovers while (len--) *dst++ = val; } static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { const SwSpan* end; int32_t x, len; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, c.a); uint32_t src; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf32[span->y * surface->stride + x]; if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); else src = color; auto ialpha = IA(src); for (auto x = 0; x < len; ++x, ++dst) { *dst = src + ALPHA_BLEND(*dst, ialpha); } } //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { uint8_t src; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf8[span->y * surface->stride + x]; if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); else src = c.a; auto ialpha = ~c.a; for (auto x = 0; x < len; ++x, ++dst) { *dst = src + MULTIPLY(*dst, ialpha); } } } return true; } static bool inline cRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; auto ialpha = 255 - c.a; for (uint32_t y = 0; y < bbox.h(); ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) { *dst = color + ALPHA_BLEND(*dst, ialpha); } } //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x; auto ialpha = ~c.a; for (uint32_t y = 0; y < bbox.h(); ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) { *dst = c.a + MULTIPLY(*dst, ialpha); } } } return true; } static bool inline cRasterABGRtoARGB(RenderSurface* surface) { TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h); //64bits faster converting if (surface->w % 2 == 0) { auto buffer = reinterpret_cast(surface->buf32); for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride / 2) { auto dst = buffer; for (uint32_t x = 0; x < surface->w / 2; ++x, ++dst) { auto c = *dst; //flip Blue, Red channels *dst = (c & 0xff000000ff000000) + ((c & 0x00ff000000ff0000) >> 16) + (c & 0x0000ff000000ff00) + ((c & 0x000000ff000000ff) << 16); } } //default converting } else { auto buffer = surface->buf32; for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { auto dst = buffer; for (uint32_t x = 0; x < surface->w; ++x, ++dst) { auto c = *dst; //flip Blue, Red channels *dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16); } } } return true; } static bool inline cRasterARGBtoABGR(RenderSurface* surface) { //exactly same with ABGRtoARGB return cRasterABGRtoARGB(surface); }thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtins.cpp000664 001750 001750 00000152431 15164251010 046567 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-alloc.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt-bit-fields.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ */ JERRY_STATIC_ASSERT ((((int)ECMA_BUILTIN_ID_GLOBAL) == ((int)ECMA_BUILTIN_OBJECTS_COUNT)), ecma_builtin_id_global_must_be_the_last_builtin_id); /** * Checks whether the built-in is an ecma_extended_built_in_object_t */ #define ECMA_BUILTIN_IS_EXTENDED_BUILT_IN(object_type) \ ((object_type) == ECMA_OBJECT_TYPE_BUILT_IN_CLASS || (object_type) == ECMA_OBJECT_TYPE_BUILT_IN_ARRAY) /** * Helper definition for ecma_builtin_property_list_references. */ typedef const ecma_builtin_property_descriptor_t *ecma_builtin_property_list_reference_t; /** * Definition of built-in dispatch routine function pointer. */ typedef ecma_value_t (*ecma_builtin_dispatch_routine_t) (uint8_t builtin_routine_id, ecma_value_t this_arg, const ecma_value_t arguments_list[], uint32_t arguments_number); /** * Definition of built-in dispatch call function pointer. */ typedef ecma_value_t (*ecma_builtin_dispatch_call_t) (const ecma_value_t arguments_list[], uint32_t arguments_number); /** * Definition of a builtin descriptor which contains the builtin object's: * - prototype objects's id (13-bits) * - type (3-bits) * * Layout: * * |----------------------|---------------| * prototype_id(12) obj_type(4) */ typedef uint16_t ecma_builtin_descriptor_t; /** * Bitshift index for get the prototype object's id from a builtin descriptor */ #define ECMA_BUILTIN_PROTOTYPE_ID_SHIFT 4 /** * Bitmask for get the object's type from a builtin descriptor */ #define ECMA_BUILTIN_OBJECT_TYPE_MASK ((1 << ECMA_BUILTIN_PROTOTYPE_ID_SHIFT) - 1) /** * Create a builtin descriptor value */ #define ECMA_MAKE_BUILTIN_DESCRIPTOR(type, proto_id) (((proto_id) << ECMA_BUILTIN_PROTOTYPE_ID_SHIFT) | (type)) /** * List of the built-in descriptors. */ static const ecma_builtin_descriptor_t ecma_builtin_descriptors[] = { /** @cond doxygen_suppress */ #define BUILTIN(a, b, c, d, e) #define BUILTIN_ROUTINE(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ ECMA_MAKE_BUILTIN_DESCRIPTOR (object_type, object_prototype_builtin_id), #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE #define BUILTIN_ROUTINE(a, b, c, d, e) #define BUILTIN(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ ECMA_MAKE_BUILTIN_DESCRIPTOR (object_type, object_prototype_builtin_id), #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE /** @endcond */ }; #ifndef JERRY_NDEBUG /** @cond doxygen_suppress */ enum { ECMA_BUILTIN_EXTENSIBLE_CHECK = #define BUILTIN(a, b, c, d, e) #define BUILTIN_ROUTINE(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ (is_extensible != 0 || builtin_id == ECMA_BUILTIN_ID_TYPE_ERROR_THROWER) && #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE #define BUILTIN_ROUTINE(a, b, c, d, e) #define BUILTIN(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ (is_extensible != 0 || builtin_id == ECMA_BUILTIN_ID_TYPE_ERROR_THROWER) && #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE true }; /** @endcond */ /** * All the builtin object must be extensible except the ThrowTypeError object. */ JERRY_STATIC_ASSERT (ECMA_BUILTIN_EXTENSIBLE_CHECK == true, ecma_builtin_must_be_extensible_except_the_builtin_throw_type_error_object); #endif /* !JERRY_NDEBUG */ /** * List of the built-in routines. */ static const ecma_builtin_dispatch_routine_t ecma_builtin_routines[] = { /** @cond doxygen_suppress */ #define BUILTIN(a, b, c, d, e) #define BUILTIN_ROUTINE(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ ecma_builtin_##lowercase_name##_dispatch_routine, #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE #define BUILTIN_ROUTINE(a, b, c, d, e) #define BUILTIN(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ ecma_builtin_##lowercase_name##_dispatch_routine, #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE /** @endcond */ }; /** * List of the built-in call functions. */ static const ecma_builtin_dispatch_call_t ecma_builtin_call_functions[] = { /** @cond doxygen_suppress */ #define BUILTIN(a, b, c, d, e) #define BUILTIN_ROUTINE(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ ecma_builtin_##lowercase_name##_dispatch_call, #include "ecma-builtins.inc.h" #undef BUILTIN_ROUTINE #undef BUILTIN /** @endcond */ }; /** * List of the built-in construct functions. */ static const ecma_builtin_dispatch_call_t ecma_builtin_construct_functions[] = { /** @cond doxygen_suppress */ #define BUILTIN(a, b, c, d, e) #define BUILTIN_ROUTINE(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ ecma_builtin_##lowercase_name##_dispatch_construct, #include "ecma-builtins.inc.h" #undef BUILTIN_ROUTINE #undef BUILTIN /** @endcond */ }; /** * Property descriptor lists for all built-ins. */ static const ecma_builtin_property_list_reference_t ecma_builtin_property_list_references[] = { /** @cond doxygen_suppress */ #define BUILTIN(a, b, c, d, e) #define BUILTIN_ROUTINE(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ ecma_builtin_##lowercase_name##_property_descriptor_list, #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE #define BUILTIN_ROUTINE(a, b, c, d, e) #define BUILTIN(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ ecma_builtin_##lowercase_name##_property_descriptor_list, #include "ecma-builtins.inc.h" #undef BUILTIN_ROUTINE #undef BUILTIN /** @endcond */ }; /** * Get the number of properties of a built-in object. * * @return the number of properties */ static size_t ecma_builtin_get_property_count (ecma_builtin_id_t builtin_id) /**< built-in ID */ { JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); const ecma_builtin_property_descriptor_t *property_list_p = ecma_builtin_property_list_references[builtin_id]; const ecma_builtin_property_descriptor_t *curr_property_p = property_list_p; while (curr_property_p->magic_string_id != LIT_MAGIC_STRING__COUNT) { curr_property_p++; } return (size_t) (curr_property_p - property_list_p); } /* ecma_builtin_get_property_count */ /** * Check if passed object is a global built-in. * * @return true - if the object is a global built-in * false - otherwise */ bool ecma_builtin_is_global (ecma_object_t *object_p) /**< pointer to an object */ { return (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BUILT_IN_GENERAL && ((ecma_extended_object_t *) object_p)->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL); } /* ecma_builtin_is_global */ /** * Get reference to the global object * * Note: * Does not increase the reference counter. * * @return pointer to the global object */ ecma_object_t * ecma_builtin_get_global (void) { JERRY_ASSERT (JERRY_CONTEXT (global_object_p) != NULL); return (ecma_object_t *) JERRY_CONTEXT (global_object_p); } /* ecma_builtin_get_global */ /** * Checks whether the given function is a built-in routine * * @return true - if the function object is a built-in routine * false - otherwise */ bool ecma_builtin_function_is_routine (ecma_object_t *func_obj_p) /**< function object */ { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p; return (ext_func_obj_p->u.built_in.routine_id != 0); } /* ecma_builtin_function_is_routine */ #if JERRY_BUILTIN_REALMS /** * Get reference to the realm provided by another built-in object * * Note: * Does not increase the reference counter. * * @return pointer to the global object */ static ecma_global_object_t * ecma_builtin_get_realm (ecma_object_t *builtin_object_p) /**< built-in object */ { ecma_object_type_t object_type = ecma_get_object_type (builtin_object_p); ecma_value_t realm_value; JERRY_ASSERT (object_type == ECMA_OBJECT_TYPE_BUILT_IN_GENERAL || object_type == ECMA_OBJECT_TYPE_BUILT_IN_CLASS || object_type == ECMA_OBJECT_TYPE_BUILT_IN_ARRAY || object_type == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) { realm_value = ((ecma_extended_built_in_object_t *) builtin_object_p)->built_in.realm_value; } else { realm_value = ((ecma_extended_object_t *) builtin_object_p)->u.built_in.realm_value; } return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, realm_value); } /* ecma_builtin_get_realm */ #endif /* JERRY_BUILTIN_REALMS */ /** * Instantiate specified ECMA built-in object * * @return the newly instantiated built-in */ static ecma_object_t * ecma_instantiate_builtin (ecma_global_object_t *global_object_p, /**< global object */ ecma_builtin_id_t obj_builtin_id) /**< built-in id */ { jmem_cpointer_t *builtin_objects = global_object_p->builtin_objects; JERRY_ASSERT (obj_builtin_id < ECMA_BUILTIN_OBJECTS_COUNT); JERRY_ASSERT (builtin_objects[obj_builtin_id] == JMEM_CP_NULL); ecma_builtin_descriptor_t builtin_desc = ecma_builtin_descriptors[obj_builtin_id]; ecma_builtin_id_t object_prototype_builtin_id = (ecma_builtin_id_t) (builtin_desc >> ECMA_BUILTIN_PROTOTYPE_ID_SHIFT); ecma_object_t *prototype_obj_p; if (JERRY_UNLIKELY (object_prototype_builtin_id == ECMA_BUILTIN_ID__COUNT)) { prototype_obj_p = NULL; } else { if (builtin_objects[object_prototype_builtin_id] == JMEM_CP_NULL) { ecma_instantiate_builtin (global_object_p, object_prototype_builtin_id); } prototype_obj_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, builtin_objects[object_prototype_builtin_id]); JERRY_ASSERT (prototype_obj_p != NULL); } ecma_object_type_t obj_type = (ecma_object_type_t) (builtin_desc & ECMA_BUILTIN_OBJECT_TYPE_MASK); JERRY_ASSERT (obj_type == ECMA_OBJECT_TYPE_BUILT_IN_GENERAL || obj_type == ECMA_OBJECT_TYPE_BUILT_IN_CLASS || obj_type == ECMA_OBJECT_TYPE_BUILT_IN_ARRAY || obj_type == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); bool is_extended_built_in = ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (obj_type); size_t ext_object_size = (is_extended_built_in ? sizeof (ecma_extended_built_in_object_t) : sizeof (ecma_extended_object_t)); size_t property_count = ecma_builtin_get_property_count (obj_builtin_id); if (property_count > ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE) { /* Only 64 extra properties supported at the moment. * This can be extended to 256 later. */ JERRY_ASSERT (property_count <= (ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE + 64)); ext_object_size += sizeof (uint64_t); } ecma_object_t *obj_p = ecma_create_object (prototype_obj_p, ext_object_size, obj_type); if (JERRY_UNLIKELY (obj_builtin_id == ECMA_BUILTIN_ID_TYPE_ERROR_THROWER)) { ecma_op_ordinary_object_prevent_extensions (obj_p); } else { ecma_op_ordinary_object_set_extensible (obj_p); } /* * [[Class]] property of built-in object is not stored explicitly. * * See also: ecma_object_get_class_name */ ecma_built_in_props_t *built_in_props_p; if (is_extended_built_in) { built_in_props_p = &((ecma_extended_built_in_object_t *) obj_p)->built_in; } else { built_in_props_p = &((ecma_extended_object_t *) obj_p)->u.built_in; } built_in_props_p->id = (uint8_t) obj_builtin_id; built_in_props_p->routine_id = 0; built_in_props_p->u.length_and_bitset_size = 0; built_in_props_p->u2.instantiated_bitset[0] = 0; #if JERRY_BUILTIN_REALMS ECMA_SET_INTERNAL_VALUE_POINTER (built_in_props_p->realm_value, global_object_p); #else /* !JERRY_BUILTIN_REALMS */ built_in_props_p->continue_instantiated_bitset[0] = 0; #endif /* JERRY_BUILTIN_REALMS */ if (property_count > ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE) { built_in_props_p->u.length_and_bitset_size = 1 << ECMA_BUILT_IN_BITSET_SHIFT; uint32_t *instantiated_bitset_p = (uint32_t *) (built_in_props_p + 1); instantiated_bitset_p[0] = 0; instantiated_bitset_p[1] = 0; } /** Initializing [[PrimitiveValue]] properties of built-in prototype objects */ switch (obj_builtin_id) { #if JERRY_BUILTIN_ARRAY case ECMA_BUILTIN_ID_ARRAY_PROTOTYPE: { JERRY_ASSERT (obj_type == ECMA_OBJECT_TYPE_BUILT_IN_ARRAY); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; ext_object_p->u.array.length = 0; ext_object_p->u.array.length_prop_and_hole_count = ECMA_PROPERTY_FLAG_WRITABLE | ECMA_PROPERTY_VIRTUAL; break; } #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_STRING case ECMA_BUILTIN_ID_STRING_PROTOTYPE: { JERRY_ASSERT (obj_type == ECMA_OBJECT_TYPE_BUILT_IN_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_STRING; ext_object_p->u.cls.u3.value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); break; } #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_NUMBER case ECMA_BUILTIN_ID_NUMBER_PROTOTYPE: { JERRY_ASSERT (obj_type == ECMA_OBJECT_TYPE_BUILT_IN_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_NUMBER; ext_object_p->u.cls.u3.value = ecma_make_integer_value (0); break; } #endif /* JERRY_BUILTIN_NUMBER */ #if JERRY_BUILTIN_BOOLEAN case ECMA_BUILTIN_ID_BOOLEAN_PROTOTYPE: { JERRY_ASSERT (obj_type == ECMA_OBJECT_TYPE_BUILT_IN_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_BOOLEAN; ext_object_p->u.cls.u3.value = ECMA_VALUE_FALSE; break; } #endif /* JERRY_BUILTIN_BOOLEAN */ default: { JERRY_ASSERT (obj_type != ECMA_OBJECT_TYPE_BUILT_IN_CLASS); break; } } ECMA_SET_NON_NULL_POINTER (builtin_objects[obj_builtin_id], obj_p); ecma_deref_object (obj_p); return obj_p; } /* ecma_instantiate_builtin */ /** * Create a global object * * @return a new global object */ ecma_global_object_t * ecma_builtin_create_global_object (void) { ecma_builtin_descriptor_t builtin_desc = ecma_builtin_descriptors[ECMA_BUILTIN_ID_GLOBAL]; ecma_builtin_id_t prototype_builtin_id = (ecma_builtin_id_t) (builtin_desc >> ECMA_BUILTIN_PROTOTYPE_ID_SHIFT); ecma_object_type_t obj_type = (ecma_object_type_t) (builtin_desc & ECMA_BUILTIN_OBJECT_TYPE_MASK); ecma_builtin_get_property_count (ECMA_BUILTIN_ID_GLOBAL); ecma_object_t *object_p = ecma_create_object (NULL, sizeof (ecma_global_object_t), obj_type); ecma_op_ordinary_object_set_extensible (object_p); ecma_global_object_t *global_object_p = (ecma_global_object_t *) object_p; global_object_p->extended_object.u.built_in.id = (uint8_t) ECMA_BUILTIN_ID_GLOBAL; global_object_p->extended_object.u.built_in.routine_id = 0; /* Bitset size is ignored by the gc. */ global_object_p->extended_object.u.built_in.u.length_and_bitset_size = 0; global_object_p->extended_object.u.built_in.u2.instantiated_bitset[0] = 0; global_object_p->extra_instantiated_bitset[0] = 0; #if JERRY_BUILTIN_REALMS ECMA_SET_INTERNAL_VALUE_POINTER (global_object_p->extended_object.u.built_in.realm_value, global_object_p); global_object_p->extra_realms_bitset = 0; global_object_p->this_binding = ecma_make_object_value (object_p); #else /* !JERRY_BUILTIN_REALMS */ global_object_p->extended_object.u.built_in.continue_instantiated_bitset[0] = 0; #endif /* JERRY_BUILTIN_REALMS */ memset (global_object_p->builtin_objects, 0, (sizeof (jmem_cpointer_t) * ECMA_BUILTIN_OBJECTS_COUNT)); /* Temporary self reference for GC mark. */ ECMA_SET_NON_NULL_POINTER (global_object_p->global_env_cp, object_p); global_object_p->global_scope_cp = global_object_p->global_env_cp; ecma_object_t *global_lex_env_p = ecma_create_object_lex_env (NULL, object_p); ECMA_SET_NON_NULL_POINTER (global_object_p->global_env_cp, global_lex_env_p); global_object_p->global_scope_cp = global_object_p->global_env_cp; ecma_deref_object (global_lex_env_p); ecma_object_t *prototype_object_p; prototype_object_p = ecma_instantiate_builtin (global_object_p, prototype_builtin_id); JERRY_ASSERT (prototype_object_p != NULL); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, prototype_object_p); return global_object_p; } /* ecma_builtin_create_global_object */ /** * Get reference to specified built-in object * * Note: * Does not increase the reference counter. * * @return pointer to the object's instance */ ecma_object_t * ecma_builtin_get (ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ { JERRY_ASSERT (builtin_id < ECMA_BUILTIN_OBJECTS_COUNT); ecma_global_object_t *global_object_p = (ecma_global_object_t *) ecma_builtin_get_global (); jmem_cpointer_t *builtin_p = global_object_p->builtin_objects + builtin_id; if (JERRY_UNLIKELY (*builtin_p == JMEM_CP_NULL)) { return ecma_instantiate_builtin (global_object_p, builtin_id); } return ECMA_GET_NON_NULL_POINTER (ecma_object_t, *builtin_p); } /* ecma_builtin_get */ #if JERRY_BUILTIN_REALMS /** * Get reference to specified built-in object using the realm provided by another built-in object * * Note: * Does not increase the reference counter. * * @return pointer to the object's instance */ ecma_object_t * ecma_builtin_get_from_realm (ecma_global_object_t *global_object_p, /**< global object */ ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ { JERRY_ASSERT (builtin_id < ECMA_BUILTIN_OBJECTS_COUNT); jmem_cpointer_t *builtin_p = global_object_p->builtin_objects + builtin_id; if (JERRY_UNLIKELY (*builtin_p == JMEM_CP_NULL)) { return ecma_instantiate_builtin (global_object_p, builtin_id); } return ECMA_GET_NON_NULL_POINTER (ecma_object_t, *builtin_p); } /* ecma_builtin_get_from_realm */ #endif /* JERRY_BUILTIN_REALMS */ /** * Get reference to specified built-in object using the realm provided by another built-in object * * Note: * Does not increase the reference counter. * * @return pointer to the object's instance */ static inline ecma_object_t * ecma_builtin_get_from_builtin (ecma_object_t *builtin_object_p, /**< built-in object */ ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ { JERRY_ASSERT (builtin_id < ECMA_BUILTIN_OBJECTS_COUNT); #if JERRY_BUILTIN_REALMS return ecma_builtin_get_from_realm (ecma_builtin_get_realm (builtin_object_p), builtin_id); #else /* !JERRY_BUILTIN_REALMS */ JERRY_UNUSED (builtin_object_p); return ecma_builtin_get (builtin_id); #endif /* JERRY_BUILTIN_REALMS */ } /* ecma_builtin_get_from_builtin */ /** * Construct a Function object for specified built-in routine * * See also: ECMA-262 v5, 15 * * @return pointer to constructed Function object */ static ecma_object_t * ecma_builtin_make_function_object_for_routine (ecma_object_t *builtin_object_p, /**< builtin object */ uint8_t routine_id, /**< builtin-wide identifier of the built-in * object's routine property */ uint32_t routine_index, /**< property descriptor index of routine */ uint8_t flags) /**< see also: ecma_builtin_routine_flags */ { ecma_object_t *prototype_obj_p = ecma_builtin_get_from_builtin (builtin_object_p, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); size_t ext_object_size = sizeof (ecma_extended_object_t); ecma_object_t *func_obj_p = ecma_create_object (prototype_obj_p, ext_object_size, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); JERRY_ASSERT (routine_id > 0); JERRY_ASSERT (routine_index <= UINT8_MAX); ecma_built_in_props_t *built_in_props_p; if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (ecma_get_object_type (builtin_object_p))) { built_in_props_p = &((ecma_extended_built_in_object_t *) builtin_object_p)->built_in; } else { built_in_props_p = &((ecma_extended_object_t *) builtin_object_p)->u.built_in; } ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p; ext_func_obj_p->u.built_in.id = built_in_props_p->id; ext_func_obj_p->u.built_in.routine_id = routine_id; ext_func_obj_p->u.built_in.u.routine_index = (uint8_t) routine_index; ext_func_obj_p->u.built_in.u2.routine_flags = flags; #if JERRY_BUILTIN_REALMS ext_func_obj_p->u.built_in.realm_value = built_in_props_p->realm_value; #endif /* JERRY_BUILTIN_REALMS */ return func_obj_p; } /* ecma_builtin_make_function_object_for_routine */ /** * Construct a Function object for specified built-in accessor getter * * @return pointer to constructed accessor getter Function object */ static ecma_object_t * ecma_builtin_make_function_object_for_getter_accessor (ecma_object_t *builtin_object_p, /**< builtin object */ uint8_t routine_id, /**< builtin-wide id of the built-in * object's routine property */ uint32_t routine_index) /**< property descriptor index * of routine */ { return ecma_builtin_make_function_object_for_routine (builtin_object_p, routine_id, routine_index, ECMA_BUILTIN_ROUTINE_GETTER); } /* ecma_builtin_make_function_object_for_getter_accessor */ /** * Construct a Function object for specified built-in accessor setter * * @return pointer to constructed accessor getter Function object */ static ecma_object_t * ecma_builtin_make_function_object_for_setter_accessor (ecma_object_t *builtin_object_p, /**< builtin object */ uint8_t routine_id, /**< builtin-wide id of the built-in * object's routine property */ uint32_t routine_index) /**< property descriptor index * of routine */ { return ecma_builtin_make_function_object_for_routine (builtin_object_p, routine_id, routine_index, ECMA_BUILTIN_ROUTINE_SETTER); } /* ecma_builtin_make_function_object_for_setter_accessor */ /** * Create specification defined properties for built-in native handlers. * * @return pointer property, if one was instantiated, * NULL - otherwise. */ static ecma_property_t * ecma_builtin_native_handler_try_to_instantiate_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property's name */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; ecma_property_t *prop_p = NULL; if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_NAME)) { if ((ext_obj_p->u.built_in.u2.routine_flags & ECMA_NATIVE_HANDLER_FLAGS_NAME_INITIALIZED) == 0) { ecma_property_value_t *value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE, &prop_p); value_p->value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } } else if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH)) { if ((ext_obj_p->u.built_in.u2.routine_flags & ECMA_NATIVE_HANDLER_FLAGS_LENGTH_INITIALIZED) == 0) { ecma_property_value_t *value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE, &prop_p); const uint8_t length = ecma_builtin_handler_get_length ((ecma_native_handler_id_t) ext_obj_p->u.built_in.routine_id); value_p->value = ecma_make_integer_value (length); } } return prop_p; } /* ecma_builtin_native_handler_try_to_instantiate_property */ /** * Lazy instantiation of builtin routine property of builtin object * * If the property is not instantiated yet, instantiate the property and * return pointer to the instantiated property. * * @return pointer property, if one was instantiated, * NULL - otherwise. */ ecma_property_t * ecma_builtin_routine_try_to_instantiate_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property name */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION && ecma_builtin_function_is_routine (object_p)); ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; if (JERRY_UNLIKELY (ext_func_p->u.built_in.id == ECMA_BUILTIN_ID_HANDLER)) { return ecma_builtin_native_handler_try_to_instantiate_property (object_p, property_name_p); } if (ecma_string_is_length (property_name_p)) { /* * Lazy instantiation of 'length' property */ ecma_property_t *len_prop_p; uint8_t *bitset_p = &ext_func_p->u.built_in.u2.routine_flags; if (*bitset_p & ECMA_BUILTIN_ROUTINE_LENGTH_INITIALIZED) { /* length property was already instantiated */ return NULL; } /* We mark that the property was lazily instantiated, * as it is configurable and so can be deleted (ECMA-262 v6, 19.2.4.1) */ ecma_property_value_t *len_prop_value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE, &len_prop_p); uint8_t length = 0; if (ext_func_p->u.built_in.u2.routine_flags & ECMA_BUILTIN_ROUTINE_SETTER) { length = 1; } else if (!(ext_func_p->u.built_in.u2.routine_flags & ECMA_BUILTIN_ROUTINE_GETTER)) { uint8_t routine_index = ext_func_p->u.built_in.u.routine_index; const ecma_builtin_property_descriptor_t *property_list_p; property_list_p = ecma_builtin_property_list_references[ext_func_p->u.built_in.id]; JERRY_ASSERT (property_list_p[routine_index].type == ECMA_BUILTIN_PROPERTY_ROUTINE); length = ECMA_GET_ROUTINE_LENGTH (property_list_p[routine_index].value); } len_prop_value_p->value = ecma_make_integer_value (length); return len_prop_p; } /* * Lazy instantiation of 'name' property */ if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_NAME)) { uint8_t *bitset_p = &ext_func_p->u.built_in.u2.routine_flags; if (*bitset_p & ECMA_BUILTIN_ROUTINE_NAME_INITIALIZED) { /* name property was already instantiated */ return NULL; } /* We mark that the property was lazily instantiated */ ecma_property_t *name_prop_p; ecma_property_value_t *name_prop_value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE, &name_prop_p); uint8_t routine_index = ext_func_p->u.built_in.u.routine_index; const ecma_builtin_property_descriptor_t *property_list_p; property_list_p = ecma_builtin_property_list_references[ext_func_p->u.built_in.id]; JERRY_ASSERT (property_list_p[routine_index].type == ECMA_BUILTIN_PROPERTY_ROUTINE || property_list_p[routine_index].type == ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_WRITE || property_list_p[routine_index].type == ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_ONLY); lit_magic_string_id_t name_id = (lit_magic_string_id_t) property_list_p[routine_index].magic_string_id; ecma_string_t *name_p; if (JERRY_UNLIKELY (name_id > LIT_NON_INTERNAL_MAGIC_STRING__COUNT)) { /* Note: Whenever new intrinsic routine is being added this mapping should be updated as well! */ if (JERRY_UNLIKELY (name_id == LIT_INTERNAL_MAGIC_STRING_ARRAY_PROTOTYPE_VALUES) || JERRY_UNLIKELY (name_id == LIT_INTERNAL_MAGIC_STRING_TYPEDARRAY_PROTOTYPE_VALUES) || JERRY_UNLIKELY (name_id == LIT_INTERNAL_MAGIC_STRING_SET_PROTOTYPE_VALUES)) { name_p = ecma_get_magic_string (LIT_MAGIC_STRING_VALUES); } else if (JERRY_UNLIKELY (name_id == LIT_INTERNAL_MAGIC_STRING_MAP_PROTOTYPE_ENTRIES)) { name_p = ecma_get_magic_string (LIT_MAGIC_STRING_ENTRIES); } else { JERRY_ASSERT (LIT_IS_GLOBAL_SYMBOL (name_id)); name_p = ecma_op_get_global_symbol (name_id); } } else { name_p = ecma_get_magic_string (name_id); } const char *prefix_p = NULL; lit_utf8_size_t prefix_size = 0; if (*bitset_p & (ECMA_BUILTIN_ROUTINE_GETTER | ECMA_BUILTIN_ROUTINE_SETTER)) { prefix_size = 4; prefix_p = (*bitset_p & ECMA_BUILTIN_ROUTINE_GETTER) ? "get " : "set "; } name_prop_value_p->value = ecma_op_function_form_name (name_p, (char*)prefix_p, prefix_size); if (JERRY_UNLIKELY (name_id > LIT_NON_INTERNAL_MAGIC_STRING__COUNT)) { ecma_deref_ecma_string (name_p); } return name_prop_p; } return NULL; } /* ecma_builtin_routine_try_to_instantiate_property */ /** * If the property's name is one of built-in properties of the object * that is not instantiated yet, instantiate the property and * return pointer to the instantiated property. * * @return pointer property, if one was instantiated, * NULL - otherwise. */ ecma_property_t * ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property's name */ { lit_magic_string_id_t magic_string_id = ecma_get_string_magic (property_name_p); if (JERRY_UNLIKELY (ecma_prop_name_is_symbol (property_name_p)) && property_name_p->u.hash & ECMA_SYMBOL_FLAG_GLOBAL) { magic_string_id = (lit_magic_string_id_t) ((int) property_name_p->u.hash >> (int) ECMA_SYMBOL_FLAGS_SHIFT); } if (magic_string_id == LIT_MAGIC_STRING__COUNT) { return NULL; } ecma_built_in_props_t *built_in_props_p; ecma_object_type_t object_type = ecma_get_object_type (object_p); JERRY_ASSERT (object_type == ECMA_OBJECT_TYPE_BUILT_IN_GENERAL || object_type == ECMA_OBJECT_TYPE_BUILT_IN_CLASS || object_type == ECMA_OBJECT_TYPE_BUILT_IN_ARRAY || (object_type == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION && !ecma_builtin_function_is_routine (object_p))); if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) { built_in_props_p = &((ecma_extended_built_in_object_t *) object_p)->built_in; } else { built_in_props_p = &((ecma_extended_object_t *) object_p)->u.built_in; } ecma_builtin_id_t builtin_id = (ecma_builtin_id_t) built_in_props_p->id; JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); const ecma_builtin_property_descriptor_t *property_list_p = ecma_builtin_property_list_references[builtin_id]; const ecma_builtin_property_descriptor_t *curr_property_p = property_list_p; while (curr_property_p->magic_string_id != magic_string_id) { if (curr_property_p->magic_string_id == LIT_MAGIC_STRING__COUNT) { return NULL; } curr_property_p++; } uint32_t index = (uint32_t) (curr_property_p - property_list_p); uint8_t *bitset_p = built_in_props_p->u2.instantiated_bitset + (index >> 3); #if JERRY_BUILTIN_REALMS if (index >= 8 * sizeof (uint8_t)) { bitset_p += sizeof (ecma_value_t); } #endif /* JERRY_BUILTIN_REALMS */ uint8_t bit_for_index = (uint8_t) (1u << (index & 0x7)); if (*bitset_p & bit_for_index) { /* This property was instantiated before. */ return NULL; } ecma_value_t value = ECMA_VALUE_EMPTY; bool is_accessor = false; ecma_object_t *getter_p = NULL; ecma_object_t *setter_p = NULL; switch (curr_property_p->type) { case ECMA_BUILTIN_PROPERTY_SIMPLE: { value = curr_property_p->value; #if JERRY_BUILTIN_GLOBAL_THIS if (value == ECMA_VALUE_GLOBAL_THIS) { /* Only the global object has globalThis property. */ JERRY_ASSERT (ecma_builtin_is_global (object_p)); ecma_ref_object (object_p); value = ecma_make_object_value (object_p); } #endif /* JERRY_BUILTIN_GLOBAL_THIS */ break; } case ECMA_BUILTIN_PROPERTY_NUMBER: { ecma_number_t num = 0.0; if (curr_property_p->value < ECMA_BUILTIN_NUMBER_MAX) { num = curr_property_p->value; } else if (curr_property_p->value < ECMA_BUILTIN_NUMBER_NAN) { static const ecma_number_t builtin_number_list[] = { ECMA_NUMBER_MAX_VALUE, ECMA_NUMBER_MIN_VALUE, ECMA_NUMBER_EPSILON, ECMA_NUMBER_MAX_SAFE_INTEGER, ECMA_NUMBER_MIN_SAFE_INTEGER, ECMA_NUMBER_E, ECMA_NUMBER_PI, ECMA_NUMBER_LN10, ECMA_NUMBER_LN2, ECMA_NUMBER_LOG2E, ECMA_NUMBER_LOG10E, ECMA_NUMBER_SQRT2, ECMA_NUMBER_SQRT_1_2, }; num = builtin_number_list[curr_property_p->value - ECMA_BUILTIN_NUMBER_MAX]; } else { switch (curr_property_p->value) { case ECMA_BUILTIN_NUMBER_POSITIVE_INFINITY: { num = ecma_number_make_infinity (false); break; } case ECMA_BUILTIN_NUMBER_NEGATIVE_INFINITY: { num = ecma_number_make_infinity (true); break; } default: { JERRY_ASSERT (curr_property_p->value == ECMA_BUILTIN_NUMBER_NAN); num = ecma_number_make_nan (); break; } } } value = ecma_make_number_value (num); break; } case ECMA_BUILTIN_PROPERTY_STRING: { value = ecma_make_magic_string_value ((lit_magic_string_id_t) curr_property_p->value); break; } case ECMA_BUILTIN_PROPERTY_SYMBOL: { lit_magic_string_id_t symbol_id = (lit_magic_string_id_t) curr_property_p->value; value = ecma_make_symbol_value (ecma_op_get_global_symbol (symbol_id)); break; } case ECMA_BUILTIN_PROPERTY_INTRINSIC_PROPERTY: { ecma_object_t *intrinsic_object_p = ecma_builtin_get_from_builtin (object_p, ECMA_BUILTIN_ID_INTRINSIC_OBJECT); value = ecma_op_object_get_by_magic_id (intrinsic_object_p, (lit_magic_string_id_t) curr_property_p->value); break; } case ECMA_BUILTIN_PROPERTY_ACCESSOR_BUILTIN_FUNCTION: { is_accessor = true; uint16_t getter_id = ECMA_ACCESSOR_READ_WRITE_GET_GETTER_ID (curr_property_p->value); uint16_t setter_id = ECMA_ACCESSOR_READ_WRITE_GET_SETTER_ID (curr_property_p->value); getter_p = ecma_builtin_get_from_builtin (object_p, (ecma_builtin_id_t) getter_id); setter_p = ecma_builtin_get_from_builtin (object_p, (ecma_builtin_id_t) setter_id); ecma_ref_object (getter_p); ecma_ref_object (setter_p); break; } case ECMA_BUILTIN_PROPERTY_OBJECT: { ecma_object_t *builtin_object_p; builtin_object_p = ecma_builtin_get_from_builtin (object_p, (ecma_builtin_id_t) curr_property_p->value); ecma_ref_object (builtin_object_p); value = ecma_make_object_value (builtin_object_p); break; } case ECMA_BUILTIN_PROPERTY_ROUTINE: { ecma_object_t *func_obj_p; func_obj_p = ecma_builtin_make_function_object_for_routine (object_p, ECMA_GET_ROUTINE_ID (curr_property_p->value), index, ECMA_BUILTIN_ROUTINE_NO_OPTS); value = ecma_make_object_value (func_obj_p); break; } case ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_WRITE: { is_accessor = true; uint8_t getter_id = ECMA_ACCESSOR_READ_WRITE_GET_GETTER_ID (curr_property_p->value); uint8_t setter_id = ECMA_ACCESSOR_READ_WRITE_GET_SETTER_ID (curr_property_p->value); getter_p = ecma_builtin_make_function_object_for_getter_accessor (object_p, getter_id, index); setter_p = ecma_builtin_make_function_object_for_setter_accessor (object_p, setter_id, index); break; } default: { JERRY_ASSERT (curr_property_p->type == ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_ONLY); is_accessor = true; uint8_t getter_id = (uint8_t) curr_property_p->value; getter_p = ecma_builtin_make_function_object_for_getter_accessor (object_p, getter_id, index); break; } } ecma_property_t *prop_p; JERRY_ASSERT (curr_property_p->attributes & ECMA_PROPERTY_FLAG_BUILT_IN); if (is_accessor) { ecma_create_named_accessor_property (object_p, property_name_p, getter_p, setter_p, curr_property_p->attributes, &prop_p); if (setter_p) { ecma_deref_object (setter_p); } if (getter_p) { ecma_deref_object (getter_p); } } else { ecma_property_value_t *prop_value_p = ecma_create_named_data_property (object_p, property_name_p, curr_property_p->attributes, &prop_p); prop_value_p->value = value; /* Reference count of objects must be decreased. */ ecma_deref_if_object (value); } return prop_p; } /* ecma_builtin_try_to_instantiate_property */ /** * Delete configurable properties of native handlers. */ static void ecma_builtin_native_handler_delete_built_in_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property name */ { ecma_extended_object_t *extended_obj_p = (ecma_extended_object_t *) object_p; if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH)) { JERRY_ASSERT (!(extended_obj_p->u.built_in.u2.routine_flags & ECMA_NATIVE_HANDLER_FLAGS_LENGTH_INITIALIZED)); extended_obj_p->u.built_in.u2.routine_flags |= ECMA_NATIVE_HANDLER_FLAGS_LENGTH_INITIALIZED; return; } JERRY_ASSERT (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_NAME)); JERRY_ASSERT (!(extended_obj_p->u.built_in.u2.routine_flags & ECMA_NATIVE_HANDLER_FLAGS_NAME_INITIALIZED)); extended_obj_p->u.built_in.u2.routine_flags |= ECMA_NATIVE_HANDLER_FLAGS_NAME_INITIALIZED; } /* ecma_builtin_native_handler_delete_built_in_property */ /** * Delete configurable properties of built-in routines. */ void ecma_builtin_routine_delete_built_in_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property name */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION && ecma_builtin_function_is_routine (object_p)); ecma_extended_object_t *extended_obj_p = (ecma_extended_object_t *) object_p; if (JERRY_UNLIKELY (extended_obj_p->u.built_in.id == ECMA_BUILTIN_ID_HANDLER)) { ecma_builtin_native_handler_delete_built_in_property (object_p, property_name_p); return; } uint8_t *bitset_p = &extended_obj_p->u.built_in.u2.routine_flags; if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH)) { JERRY_ASSERT (!(*bitset_p & ECMA_BUILTIN_ROUTINE_LENGTH_INITIALIZED)); *bitset_p |= ECMA_BUILTIN_ROUTINE_LENGTH_INITIALIZED; return; } JERRY_ASSERT (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_NAME)); JERRY_ASSERT (!(*bitset_p & ECMA_BUILTIN_ROUTINE_NAME_INITIALIZED)); *bitset_p |= ECMA_BUILTIN_ROUTINE_NAME_INITIALIZED; } /* ecma_builtin_routine_delete_built_in_property */ /** * Delete configurable properties of built-ins. */ void ecma_builtin_delete_built_in_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property name */ { lit_magic_string_id_t magic_string_id = ecma_get_string_magic (property_name_p); if (JERRY_UNLIKELY (ecma_prop_name_is_symbol (property_name_p))) { if (property_name_p->u.hash & ECMA_SYMBOL_FLAG_GLOBAL) { magic_string_id = (lit_magic_string_id_t ) ((int) property_name_p->u.hash >> ECMA_SYMBOL_FLAGS_SHIFT); } } ecma_built_in_props_t *built_in_props_p; ecma_object_type_t object_type = ecma_get_object_type (object_p); JERRY_ASSERT (object_type == ECMA_OBJECT_TYPE_BUILT_IN_GENERAL || object_type == ECMA_OBJECT_TYPE_BUILT_IN_CLASS || object_type == ECMA_OBJECT_TYPE_BUILT_IN_ARRAY || (object_type == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION && !ecma_builtin_function_is_routine (object_p))); if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) { built_in_props_p = &((ecma_extended_built_in_object_t *) object_p)->built_in; } else { built_in_props_p = &((ecma_extended_object_t *) object_p)->u.built_in; } ecma_builtin_id_t builtin_id = (ecma_builtin_id_t) built_in_props_p->id; JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); const ecma_builtin_property_descriptor_t *property_list_p = ecma_builtin_property_list_references[builtin_id]; const ecma_builtin_property_descriptor_t *curr_property_p = property_list_p; while (curr_property_p->magic_string_id != magic_string_id) { JERRY_ASSERT (curr_property_p->magic_string_id != LIT_MAGIC_STRING__COUNT); curr_property_p++; } uint32_t index = (uint32_t) (curr_property_p - property_list_p); uint8_t *bitset_p = built_in_props_p->u2.instantiated_bitset + (index >> 3); #if JERRY_BUILTIN_REALMS if (index >= 8 * sizeof (uint8_t)) { bitset_p += sizeof (ecma_value_t); } #endif /* JERRY_BUILTIN_REALMS */ uint8_t bit_for_index = (uint8_t) (1u << (index & 0x7)); JERRY_ASSERT (!(*bitset_p & bit_for_index)); *bitset_p |= bit_for_index; } /* ecma_builtin_delete_built_in_property */ /** * List names of an Built-in native handler object's lazy instantiated properties, * adding them to corresponding string collections */ static void ecma_builtin_native_handler_list_lazy_property_names (ecma_object_t *object_p, /**< function object */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p) /**< prop counter */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; if ((ext_obj_p->u.built_in.u2.routine_flags & ECMA_NATIVE_HANDLER_FLAGS_NAME_INITIALIZED) == 0) { ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_NAME)); prop_counter_p->string_named_props++; } if ((ext_obj_p->u.built_in.u2.routine_flags & ECMA_NATIVE_HANDLER_FLAGS_LENGTH_INITIALIZED) == 0) { ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); prop_counter_p->string_named_props++; } } /* ecma_builtin_native_handler_list_lazy_property_names */ /** * List names of a built-in function's lazy instantiated properties * * See also: * ecma_builtin_routine_try_to_instantiate_property */ void ecma_builtin_routine_list_lazy_property_names (ecma_object_t *object_p, /**< a built-in object */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< name filters */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); JERRY_ASSERT (ecma_builtin_function_is_routine (object_p)); if (filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS) { return; } ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; if (JERRY_UNLIKELY (ext_func_p->u.built_in.id == ECMA_BUILTIN_ID_HANDLER)) { ecma_builtin_native_handler_list_lazy_property_names (object_p, prop_names_p, prop_counter_p); return; } if (!(ext_func_p->u.built_in.u2.routine_flags & ECMA_BUILTIN_ROUTINE_LENGTH_INITIALIZED)) { /* Uninitialized 'length' property is non-enumerable (ECMA-262 v6, 19.2.4.1) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); prop_counter_p->string_named_props++; } if (!(ext_func_p->u.built_in.u2.routine_flags & ECMA_BUILTIN_ROUTINE_NAME_INITIALIZED)) { /* Uninitialized 'name' property is non-enumerable (ECMA-262 v6, 19.2.4.2) */ ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_NAME)); prop_counter_p->string_named_props++; } } /* ecma_builtin_routine_list_lazy_property_names */ /** * List names of a built-in object's lazy instantiated properties * * See also: * ecma_builtin_try_to_instantiate_property */ void ecma_builtin_list_lazy_property_names (ecma_object_t *object_p, /**< a built-in object */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< name filters */ { JERRY_ASSERT (ecma_get_object_type (object_p) != ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION || !ecma_builtin_function_is_routine (object_p)); ecma_built_in_props_t *built_in_props_p; ecma_object_type_t object_type = ecma_get_object_type (object_p); if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) { built_in_props_p = &((ecma_extended_built_in_object_t *) object_p)->built_in; } else { built_in_props_p = &((ecma_extended_object_t *) object_p)->u.built_in; } ecma_builtin_id_t builtin_id = (ecma_builtin_id_t) built_in_props_p->id; JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); #if JERRY_BUILTIN_REALMS uint8_t *bitset_p = built_in_props_p->u2.instantiated_bitset + 1 + sizeof (ecma_value_t); #else /* !JERRY_BUILTIN_REALMS */ uint8_t *bitset_p = built_in_props_p->u2.instantiated_bitset + 1; #endif /* JERRY_BUILTIN_REALMS */ uint8_t *symbol_bitset_p = bitset_p; bool has_symbol = true; if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS)) { const ecma_builtin_property_descriptor_t *curr_property_p = ecma_builtin_property_list_references[builtin_id]; uint8_t bitset = built_in_props_p->u2.instantiated_bitset[0]; uint32_t index = 0; has_symbol = false; while (curr_property_p->magic_string_id != LIT_MAGIC_STRING__COUNT) { if (index == 8) { bitset = *bitset_p++; index = 0; } uint32_t bit_for_index = (uint32_t) 1u << index; if (!(bitset & bit_for_index)) { if (JERRY_LIKELY (curr_property_p->magic_string_id < LIT_NON_INTERNAL_MAGIC_STRING__COUNT)) { ecma_value_t name = ecma_make_magic_string_value ((lit_magic_string_id_t) curr_property_p->magic_string_id); ecma_collection_push_back (prop_names_p, name); prop_counter_p->string_named_props++; } else { JERRY_ASSERT (LIT_IS_GLOBAL_SYMBOL (curr_property_p->magic_string_id)); has_symbol = true; } } curr_property_p++; index++; } } if (has_symbol && !(filter & JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS)) { const ecma_builtin_property_descriptor_t *curr_property_p = ecma_builtin_property_list_references[builtin_id]; uint8_t bitset = built_in_props_p->u2.instantiated_bitset[0]; uint32_t index = 0; while (curr_property_p->magic_string_id != LIT_MAGIC_STRING__COUNT) { if (index == 8) { bitset = *symbol_bitset_p++; index = 0; } uint32_t bit_for_index = (uint32_t) 1u << index; if (curr_property_p->magic_string_id > LIT_NON_INTERNAL_MAGIC_STRING__COUNT && !(bitset & bit_for_index)) { ecma_string_t *name_p = ecma_op_get_global_symbol ((lit_magic_string_id_t ) curr_property_p->magic_string_id); ecma_collection_push_back (prop_names_p, ecma_make_symbol_value (name_p)); prop_counter_p->symbol_named_props++; } curr_property_p++; index++; } } } /* ecma_builtin_list_lazy_property_names */ /** * Dispatcher of built-in routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_dispatch_routine (ecma_extended_object_t *func_obj_p, /**< builtin object */ ecma_value_t this_arg_value, /**< 'this' argument value */ const ecma_value_t *arguments_list_p, /**< list of arguments passed to routine */ uint32_t arguments_list_len) /**< length of arguments' list */ { JERRY_ASSERT (ecma_builtin_function_is_routine ((ecma_object_t *) func_obj_p)); ecma_value_t padded_arguments_list_p[3] = { ECMA_VALUE_UNDEFINED, ECMA_VALUE_UNDEFINED, ECMA_VALUE_UNDEFINED }; if (arguments_list_len <= 2) { switch (arguments_list_len) { case 2: { padded_arguments_list_p[1] = arguments_list_p[1]; /* FALLTHRU */ } case 1: { padded_arguments_list_p[0] = arguments_list_p[0]; break; } default: { JERRY_ASSERT (arguments_list_len == 0); } } arguments_list_p = padded_arguments_list_p; } return ecma_builtin_routines[func_obj_p->u.built_in.id](func_obj_p->u.built_in.routine_id, this_arg_value, arguments_list_p, arguments_list_len); } /* ecma_builtin_dispatch_routine */ /** * Handle calling [[Call]] of built-in object * * @return ecma value */ ecma_value_t ecma_builtin_dispatch_call (ecma_object_t *obj_p, /**< built-in object */ ecma_value_t this_arg_value, /**< 'this' argument value */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< arguments list length */ { JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (ecma_builtin_function_is_routine (obj_p)) { if (JERRY_UNLIKELY (ext_obj_p->u.built_in.id == ECMA_BUILTIN_ID_HANDLER)) { ecma_builtin_handler_t handler = ecma_builtin_handler_get ((ecma_native_handler_id_t ) ext_obj_p->u.built_in.routine_id); return handler (obj_p, arguments_list_p, arguments_list_len); } return ecma_builtin_dispatch_routine (ext_obj_p, this_arg_value, arguments_list_p, arguments_list_len); } ecma_builtin_id_t builtin_object_id = (ecma_builtin_id_t) ext_obj_p->u.built_in.id; JERRY_ASSERT (builtin_object_id < sizeof (ecma_builtin_call_functions) / sizeof (ecma_builtin_dispatch_call_t)); return ecma_builtin_call_functions[builtin_object_id](arguments_list_p, arguments_list_len); } /* ecma_builtin_dispatch_call */ /** * Handle calling [[Construct]] of built-in object * * @return ecma value */ ecma_value_t ecma_builtin_dispatch_construct (ecma_object_t *obj_p, /**< built-in object */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< arguments list length */ { JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION); if (ecma_builtin_function_is_routine (obj_p)) { return ecma_raise_type_error (ECMA_ERR_BUILTIN_ROUTINES_HAVE_NO_CONSTRUCTOR); } ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; ecma_builtin_id_t builtin_object_id = (ecma_builtin_id_t) ext_obj_p->u.built_in.id; JERRY_ASSERT (builtin_object_id < sizeof (ecma_builtin_construct_functions) / sizeof (ecma_builtin_dispatch_call_t)); return ecma_builtin_construct_functions[builtin_object_id](arguments_list_p, arguments_list_len); } /* ecma_builtin_dispatch_construct */ /** * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgCommon.cpp000664 001750 001750 00000022343 15164251010 036454 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgWgCommon.h" #include "tvgArray.h" void WgContext::initialize(WGPUInstance instance, WGPUDevice device) { assert(instance); assert(device); // store global instance and surface this->instance = instance; this->device = device; this->preferredFormat = WGPUTextureFormat_BGRA8Unorm; // get current queue queue = wgpuDeviceGetQueue(device); assert(queue); // create shared webgpu assets samplerNearestRepeat = createSampler(WGPUFilterMode_Nearest, WGPUMipmapFilterMode_Nearest, WGPUAddressMode_Repeat); samplerLinearRepeat = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_Repeat, 4); samplerLinearMirror = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_MirrorRepeat, 4); samplerLinearClamp = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_ClampToEdge, 4); assert(samplerNearestRepeat); assert(samplerLinearRepeat); assert(samplerLinearMirror); assert(samplerLinearClamp); // initialize bind group layouts layouts.initialize(device); } void WgContext::release() { layouts.release(); releaseSampler(samplerLinearClamp); releaseSampler(samplerLinearMirror); releaseSampler(samplerLinearRepeat); releaseSampler(samplerNearestRepeat); releaseQueue(queue); } WGPUSampler WgContext::createSampler(WGPUFilterMode filter, WGPUMipmapFilterMode mipmapFilter, WGPUAddressMode addrMode, uint16_t anisotropy) { const WGPUSamplerDescriptor samplerDesc { .addressModeU = addrMode, .addressModeV = addrMode, .addressModeW = addrMode, .magFilter = filter, .minFilter = filter, .mipmapFilter = mipmapFilter, .lodMinClamp = 0.0f, .lodMaxClamp = 32.0f, .maxAnisotropy = anisotropy }; return wgpuDeviceCreateSampler(device, &samplerDesc); } bool WgContext::allocateTexture(WGPUTexture& texture, uint32_t width, uint32_t height, WGPUTextureFormat format, void* data) { if ((texture) && (wgpuTextureGetWidth(texture) == width) && (wgpuTextureGetHeight(texture) == height)) { // update texture data const WGPUTexelCopyTextureInfo copyTextureInfo{ .texture = texture }; const WGPUTexelCopyBufferLayout copyBufferLayout{ .bytesPerRow = 4 * width, .rowsPerImage = height }; const WGPUExtent3D writeSize{ .width = width, .height = height, .depthOrArrayLayers = 1 }; wgpuQueueWriteTexture(queue, ©TextureInfo, data, 4 * width * height, ©BufferLayout, &writeSize); } else { releaseTexture(texture); texture = createTexture(width, height, format); // update texture data const WGPUTexelCopyTextureInfo copyTextureInfo{ .texture = texture }; const WGPUTexelCopyBufferLayout copyBufferLayout{ .bytesPerRow = 4 * width, .rowsPerImage = height }; const WGPUExtent3D writeSize{ .width = width, .height = height, .depthOrArrayLayers = 1 }; wgpuQueueWriteTexture(queue, ©TextureInfo, data, 4 * width * height, ©BufferLayout, &writeSize); return true; } return false; } WGPUTexture WgContext::createTexture(uint32_t width, uint32_t height, WGPUTextureFormat format) { const WGPUTextureDescriptor textureDesc { .usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding, .dimension = WGPUTextureDimension_2D, .size = { width, height, 1 }, .format = format, .mipLevelCount = 1, .sampleCount = 1 }; return wgpuDeviceCreateTexture(device, &textureDesc); } WGPUTexture WgContext::createTexStorage(uint32_t width, uint32_t height, WGPUTextureFormat format) { const WGPUTextureDescriptor textureDesc { .usage = WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_StorageBinding | WGPUTextureUsage_RenderAttachment, .dimension = WGPUTextureDimension_2D, .size = { width, height, 1 }, .format = format, .mipLevelCount = 1, .sampleCount = 1 }; return wgpuDeviceCreateTexture(device, &textureDesc); } WGPUTexture WgContext::createTexAttachement(uint32_t width, uint32_t height, WGPUTextureFormat format, uint32_t sc) { const WGPUTextureDescriptor textureDesc { .usage = WGPUTextureUsage_RenderAttachment, .dimension = WGPUTextureDimension_2D, .size = { width, height, 1 }, .format = format, .mipLevelCount = 1, .sampleCount = sc }; return wgpuDeviceCreateTexture(device, &textureDesc); } WGPUTextureView WgContext::createTextureView(WGPUTexture texture) { const WGPUTextureViewDescriptor textureViewDesc { .format = wgpuTextureGetFormat(texture), .dimension = WGPUTextureViewDimension_2D, .baseMipLevel = 0, .mipLevelCount = 1, .baseArrayLayer = 0, .arrayLayerCount = 1, .aspect = WGPUTextureAspect_All }; return wgpuTextureCreateView(texture, &textureViewDesc); } void WgContext::releaseTextureView(WGPUTextureView& textureView) { if (textureView) { wgpuTextureViewRelease(textureView); textureView = nullptr; } } void WgContext::releaseTexture(WGPUTexture& texture) { if (texture) { wgpuTextureDestroy(texture); wgpuTextureRelease(texture); texture = nullptr; } } void WgContext::releaseSampler(WGPUSampler& sampler) { if (sampler) { wgpuSamplerRelease(sampler); sampler = nullptr; } } bool WgContext::allocateBufferUniform(WGPUBuffer& buffer, const void* data, uint64_t size) { if ((buffer) && (wgpuBufferGetSize(buffer) >= size)) wgpuQueueWriteBuffer(queue, buffer, 0, data, size); else { releaseBuffer(buffer); const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, .size = size }; buffer = wgpuDeviceCreateBuffer(device, &bufferDesc); wgpuQueueWriteBuffer(queue, buffer, 0, data, size); return true; } return false; } bool WgContext::allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint64_t size) { if ((buffer) && (wgpuBufferGetSize(buffer) >= size)) wgpuQueueWriteBuffer(queue, buffer, 0, data, size); else { releaseBuffer(buffer); const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, .size = size }; buffer = wgpuDeviceCreateBuffer(device, &bufferDesc); wgpuQueueWriteBuffer(queue, buffer, 0, data, size); return true; } return false; } bool WgContext::allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, uint64_t size) { if ((buffer) && (wgpuBufferGetSize(buffer) >= size)) wgpuQueueWriteBuffer(queue, buffer, 0, data, size); else { releaseBuffer(buffer); const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, .size = size }; buffer = wgpuDeviceCreateBuffer(device, &bufferDesc); wgpuQueueWriteBuffer(queue, buffer, 0, data, size); return true; } return false; } void WgContext::releaseBuffer(WGPUBuffer& buffer) { if (buffer) { wgpuBufferDestroy(buffer); wgpuBufferRelease(buffer); buffer = nullptr; } } void WgContext::releaseQueue(WGPUQueue& queue) { if (queue) { wgpuQueueRelease(queue); queue = nullptr; } } WGPUCommandEncoder WgContext::createCommandEncoder() { WGPUCommandEncoderDescriptor commandEncoderDesc{}; return wgpuDeviceCreateCommandEncoder(device, &commandEncoderDesc); } void WgContext::submitCommandEncoder(WGPUCommandEncoder commandEncoder) { const WGPUCommandBufferDescriptor commandBufferDesc{}; WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc); wgpuQueueSubmit(queue, 1, &commandsBuffer); wgpuCommandBufferRelease(commandsBuffer); } void WgContext::releaseCommandEncoder(WGPUCommandEncoder& commandEncoder) { if (commandEncoder) { wgpuCommandEncoderRelease(commandEncoder); commandEncoder = nullptr; } } void WgContext::submit() { wgpuQueueSubmit(queue, 0, nullptr); } bool WgContext::invalid() { return !instance || !device; } jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint16array-prototype.inc.h000664 001750 001750 00000001620 15164251010 055224 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Uint16Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 2 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_UINT16ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-function-prototype.cpp000664 001750 001750 00000002132 15164251010 053375 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-globals.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-function-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID async_function_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup asyncfunctionprototype ECMA AsyncFunction.prototype object built-in * @{ */ /** * @} * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieBuilder.cpp000664 001750 001750 00000166603 15164251010 036666 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgCommon.h" #include "tvgMath.h" #include "tvgScene.h" #include "tvgLoadModule.h" #include "tvgLottieModel.h" #include "tvgLottieBuilder.h" #include "tvgLottieExpressions.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static bool _buildComposition(LottieComposition* comp, LottieLayer* parent); static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx); static void _rotate(LottieTransform* transform, float frameNo, Matrix& m, float angle, Tween& tween, LottieExpressions* exps) { //rotation xyz if (transform->rotationEx) { auto radianX = deg2rad(transform->rotationEx->x(frameNo, tween, exps)); auto radianY = deg2rad(transform->rotationEx->y(frameNo, tween, exps)); auto radianZ = deg2rad(transform->rotation(frameNo, tween, exps)) + angle; auto cx = cosf(radianX), sx = sinf(radianX); auto cy = cosf(radianY), sy = sinf(radianY);; auto cz = cosf(radianZ), sz = sinf(radianZ);; m.e11 = cy * cz; m.e12 = -cy * sz; m.e21 = sx * sy * cz + cx * sz; m.e22 = -sx * sy * sz + cx * cz; //rotation z } else { auto degree = transform->rotation(frameNo, tween, exps) + angle; if (degree == 0.0f) return; auto radian = deg2rad(degree); m.e11 = cosf(radian); m.e12 = -sinf(radian); m.e21 = sinf(radian); m.e22 = cosf(radian); } } static void _skew(Matrix* m, float angleDeg, float axisDeg) { auto angle = -deg2rad(angleDeg); auto tanVal = tanf(angle); axisDeg = fmod(axisDeg, 180.0f); if (fabsf(axisDeg) < 0.01f || fabsf(axisDeg - 180.0f) < 0.01f || fabsf(axisDeg + 180.0f) < 0.01f) { float cosVal = cosf(deg2rad(axisDeg)); auto B = cosVal * cosVal * tanVal; m->e12 += B * m->e11; m->e22 += B * m->e21; return; } else if (fabsf(axisDeg - 90.0f) < 0.01f || fabsf(axisDeg + 90.0f) < 0.01f) { float sinVal = -sinf(deg2rad(axisDeg)); auto C = sinVal * sinVal * tanVal; m->e11 -= C * m->e12; m->e21 -= C * m->e22; return; } auto axis = -deg2rad(axisDeg); auto cosVal = cosf(axis); auto sinVal = sinf(axis); auto A = sinVal * cosVal * tanVal; auto B = cosVal * cosVal * tanVal; auto C = sinVal * sinVal * tanVal; auto e11 = m->e11; auto e21 = m->e21; m->e11 = (1.0f - A) * e11 - C * m->e12; m->e12 = B * e11 + (1.0f + A) * m->e12; m->e21 = (1.0f - A) * e21 - C * m->e22; m->e22 = B * e21 + (1.0f + A) * m->e22; } static bool _update(LottieTransform* transform, float frameNo, Matrix& matrix, uint8_t& opacity, bool autoOrient, Tween& tween, LottieExpressions* exps) { tvg::identity(&matrix); if (!transform) { opacity = 255; return false; } if (transform->coords) translate(&matrix, {transform->coords->x(frameNo, tween, exps), transform->coords->y(frameNo, tween, exps)}); else translate(&matrix, transform->position(frameNo, tween, exps)); auto angle = (autoOrient) ? transform->position.angle(frameNo, tween) : 0.0f; _rotate(transform, frameNo, matrix, angle, tween, exps); auto skewAngle = transform->skewAngle(frameNo, tween, exps); if (skewAngle != 0.0f) { // For angles where tangent explodes, the shape degenerates into an infinitely thin line. // This is handled by zeroing out the matrix due to finite numerical precision. skewAngle = fmod(skewAngle, 180.0f); if (fabsf(skewAngle - 90.0f) < 0.01f || fabsf(skewAngle + 90.0f) < 0.01f) return false; _skew(&matrix, skewAngle, transform->skewAxis(frameNo, exps)); } auto scale = transform->scale(frameNo, tween, exps); scaleR(&matrix, scale * 0.01f); //Lottie specific anchor transform. translateR(&matrix, -transform->anchor(frameNo, tween, exps)); //invisible just in case. if (scale.x == 0.0f || scale.y == 0.0f) opacity = 0; else opacity = transform->opacity(frameNo, tween, exps); return true; } void LottieBuilder::updateTransform(LottieLayer* layer, float frameNo) { if (!layer || (!tweening() && tvg::equal(layer->cache.frameNo, frameNo))) return; auto transform = layer->transform; auto parent = layer->parent; if (parent) updateTransform(parent, frameNo); auto& matrix = layer->cache.matrix; _update(transform, frameNo, matrix, layer->cache.opacity, layer->autoOrient, tween, exps); if (parent) layer->cache.matrix = parent->cache.matrix * matrix; layer->cache.frameNo = frameNo; } void LottieBuilder::updateTransform(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto transform = static_cast(*child); if (!transform) return; Matrix m; uint8_t opacity; if (parent->mergeable()) { if (ctx->transform) { _update(transform, frameNo, m, opacity, false, tween, exps); *ctx->transform *= m; } else { ctx->transform = new Matrix; _update(transform, frameNo, *ctx->transform, opacity, false, tween, exps); } return; } ctx->merging = nullptr; if (!_update(transform, frameNo, m, opacity, false, tween, exps)) return; ctx->propagator->transform(ctx->propagator->transform() * m); ctx->propagator->opacity(MULTIPLY(opacity, PAINT(ctx->propagator)->opacity)); //FIXME: preserve the stroke width. too workaround, need a better design. if (to(ctx->propagator)->rs.strokeWidth() > 0.0f) { auto denominator = sqrtf(m.e11 * m.e11 + m.e12 * m.e12); if (denominator > 1.0f) ctx->propagator->strokeWidth(ctx->propagator->strokeWidth() / denominator); } } void LottieBuilder::updateGroup(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& pcontexts, RenderContext* ctx) { auto group = static_cast(*child); if (!group->visible) return; //Prepare render data if (group->blendMethod == parent->blendMethod) { group->scene = parent->scene; } else { group->scene = tvg::Scene::gen(); group->scene->blend(group->blendMethod); parent->scene->add(group->scene); } group->reqFragment |= ctx->reqFragment; //generate a merging shape to consolidate partial shapes into a single entity if (group->mergeable()) _draw(group, nullptr, ctx); Inlist contexts; auto propagator = group->mergeable() ? ctx->propagator : static_cast(PAINT(ctx->propagator)->duplicate(group->pooling())); contexts.back(new RenderContext(*ctx, propagator, group->mergeable())); updateChildren(group, frameNo, contexts); } static void _update(LottieStroke* stroke, float frameNo, RenderContext* ctx, Tween& tween, LottieExpressions* exps) { ctx->propagator->strokeWidth(stroke->width(frameNo, tween, exps)); ctx->propagator->strokeCap(stroke->cap); ctx->propagator->strokeJoin(stroke->join); ctx->propagator->strokeMiterlimit(stroke->miterLimit); if (stroke->dashattr) { auto dashes = tvg::malloc(stroke->dashattr->size * sizeof(float)); for (uint8_t i = 0; i < stroke->dashattr->size; ++i) dashes[i] = stroke->dashattr->values[i](frameNo, tween, exps); ctx->propagator->strokeDash(dashes, stroke->dashattr->size, stroke->dashattr->offset(frameNo, tween, exps)); tvg::free(dashes); } else { ctx->propagator->strokeDash(nullptr, 0); } } bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx, RenderFragment fragment) { if (ctx->fragment) return true; if (!ctx->reqFragment) return false; contexts.back(new RenderContext(*ctx, (Shape*)(PAINT(ctx->propagator)->duplicate(parent->pooling())))); contexts.tail->begin = child - 1; ctx->fragment = fragment; return false; } bool LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { auto stroke = static_cast(*child); if (fragmented(parent, child, contexts, ctx, RenderFragment::ByStroke)) return false; auto opacity = stroke->opacity(frameNo, tween, exps); if (opacity == 0) return false; ctx->merging = nullptr; auto color = stroke->color(frameNo, tween, exps); ctx->propagator->strokeFill(color.r, color.g, color.b, opacity); _update(static_cast(stroke), frameNo, ctx, tween, exps); return false; } bool LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { auto stroke = static_cast(*child); if (fragmented(parent, child, contexts, ctx, RenderFragment::ByStroke)) return false; auto opacity = stroke->opacity(frameNo, tween, exps); if (opacity == 0 && !stroke->opaque) return false; ctx->merging = nullptr; if (auto val = stroke->fill(frameNo, opacity, tween, exps)) ctx->propagator->strokeFill(val); _update(static_cast(stroke), frameNo, ctx, tween, exps); return false; } bool LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { auto fill = static_cast(*child); auto opacity = fill->opacity(frameNo, tween, exps); //interrupted by fully opaque, stop the current rendering if (ctx->fragment == RenderFragment::ByFill && opacity == 255) return true; if (opacity == 0) return false; if (fragmented(parent, child, contexts, ctx, RenderFragment::ByFill)) return false; ctx->merging = nullptr; auto color = fill->color(frameNo, tween, exps); ctx->propagator->fill(color.r, color.g, color.b, opacity); ctx->propagator->fillRule(fill->rule); if (ctx->propagator->strokeWidth() > 0) ctx->propagator->order(true); return false; } bool LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { auto fill = static_cast(*child); auto opacity = fill->opacity(frameNo, tween, exps); //interrupted by fully opaque, stop the current rendering if (ctx->fragment == RenderFragment::ByFill && fill->opaque && opacity == 255) return true; if (fragmented(parent, child, contexts, ctx, RenderFragment::ByFill)) return false; ctx->merging = nullptr; if (auto val = fill->fill(frameNo, opacity, tween, exps)) ctx->propagator->fill(val); ctx->propagator->fillRule(fill->rule); if (ctx->propagator->strokeWidth() > 0) ctx->propagator->order(true); return false; } static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx) { if (ctx->merging) return false; if (shape) { ctx->merging = shape->pooling(); PAINT(ctx->propagator)->duplicate(ctx->merging); } else { ctx->merging = static_cast(ctx->propagator->duplicate()); } parent->scene->add(ctx->merging); return true; } static void _repeat(LottieGroup* parent, Shape* path, LottieRenderPooler* pooler, RenderContext* ctx) { path->ref(); //prevent pooler returns the same path. Array propagators; propagators.push(ctx->propagator); Array shapes; ARRAY_REVERSE_FOREACH(repeater, ctx->repeaters) { shapes.reserve(repeater->cnt); for (int i = 0; i < repeater->cnt; ++i) { auto multiplier = repeater->offset + static_cast(i); ARRAY_FOREACH(p, propagators) { auto shape = pooler->pooling(); shape->ref(); //prevent pooler returns the same shape PAINT((*p))->duplicate(shape); to(shape)->rs.path = to(path)->rs.path; auto opacity = tvg::lerp(repeater->startOpacity, repeater->endOpacity, static_cast(i + 1) / repeater->cnt); shape->opacity(MULTIPLY(shape->opacity(), opacity)); auto m = tvg::identity(); translate(&m, repeater->position * multiplier + repeater->anchor); scale(&m, {powf(repeater->scale.x * 0.01f, multiplier), powf(repeater->scale.y * 0.01f, multiplier)}); rotate(&m, repeater->rotation * multiplier); translateR(&m, -repeater->anchor); Matrix inv; inverse(&repeater->transform, &inv); shape->transform((repeater->transform * m) * (inv * shape->transform())); shapes.push(shape); } } propagators.clear(); propagators.reserve(shapes.count); //push repeat shapes in order. if (repeater->inorder) { ARRAY_FOREACH(p, shapes) { parent->scene->add(*p); (*p)->unref(); propagators.push(*p); } } else if (!shapes.empty()) { ARRAY_REVERSE_FOREACH(p, shapes) { parent->scene->add(*p); (*p)->unref(); propagators.push(*p); } } shapes.clear(); } path->unref(); } void LottieBuilder::appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx) { auto temp = (ctx->offset) ? Shape::gen() : shape; auto cnt = to(temp)->rs.path.pts.count; temp->appendRect(pos.x, pos.y, size.x, size.y, r, r, clockwise); if (ctx->transform) { for (auto i = cnt; i < to(temp)->rs.path.pts.count; ++i) { to(temp)->rs.path.pts[i] *= *ctx->transform; } } if (ctx->offset) { ctx->offset->modifyRect(to(temp)->rs.path, to(shape)->rs.path); Paint::rel(temp); } } void LottieBuilder::updateRect(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto rect = static_cast(*child); auto size = rect->size(frameNo, tween, exps); auto pos = rect->position(frameNo, tween, exps) - size * 0.5f; auto r = rect->radius(frameNo, tween, exps); if (r == 0.0f) { if (ctx->roundness) ctx->roundness->modifyRect(size, r); } else { r = std::min({r, size.x * 0.5f, size.y * 0.5f}); } if (ctx->repeaters.empty()) { _draw(parent, rect, ctx); appendRect(ctx->merging, pos, size, r, rect->clockwise, ctx); } else { auto shape = rect->pooling(); shape->reset(); appendRect(shape, pos, size, r, rect->clockwise, ctx); _repeat(parent, shape, rect, ctx); } } static void _appendCircle(Shape* shape, Point& center, Point& radius, bool clockwise, RenderContext* ctx) { if (ctx->offset) ctx->offset->modifyEllipse(radius); auto cnt = to(shape)->rs.path.pts.count; shape->appendCircle(center.x, center.y, radius.x, radius.y, clockwise); if (ctx->transform) { for (auto i = cnt; i < to(shape)->rs.path.pts.count; ++i) { to(shape)->rs.path.pts[i] *= *ctx->transform; } } } void LottieBuilder::updateEllipse(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto ellipse = static_cast(*child); auto pos = ellipse->position(frameNo, tween, exps); auto size = ellipse->size(frameNo, tween, exps) * 0.5f; if (ctx->repeaters.empty()) { _draw(parent, ellipse, ctx); _appendCircle(ctx->merging, pos, size, ellipse->clockwise, ctx); } else { auto shape = ellipse->pooling(); shape->reset(); _appendCircle(shape, pos, size, ellipse->clockwise, ctx); _repeat(parent, shape, ellipse, ctx); } } void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto path = static_cast(*child); if (ctx->repeaters.empty()) { _draw(parent, path, ctx); if (path->pathset(frameNo, to(ctx->merging)->rs.path, ctx->transform, tween, exps, ctx->modifier)) { PAINT(ctx->merging)->mark(RenderUpdateFlag::Path); } } else { auto shape = path->pooling(); shape->reset(); path->pathset(frameNo, to(shape)->rs.path, ctx->transform, tween, exps, ctx->modifier); _repeat(parent, shape, path, ctx); } } static void _close(Array& pts, const Point& p, bool round) { if (round && tvg::zero(pts.last() - pts[pts.count - 2])) pts[pts.count - 2] = p; pts.last() = p; } void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* transform, Shape* merging, RenderContext* ctx, Tween& tween, LottieExpressions* exps) { static constexpr auto POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f; auto ptsCnt = star->ptsCnt(frameNo, tween, exps); auto innerRadius = star->innerRadius(frameNo, tween, exps); auto outerRadius = star->outerRadius(frameNo, tween, exps); auto innerRoundness = star->innerRoundness(frameNo, tween, exps) * 0.01f; auto outerRoundness = star->outerRoundness(frameNo, tween, exps) * 0.01f; auto angle = deg2rad(-90.0f); auto partialPointRadius = 0.0f; auto anglePerPoint = (2.0f * MATH_PI / ptsCnt); auto halfAnglePerPoint = anglePerPoint * 0.5f; auto partialPointAmount = ptsCnt - floorf(ptsCnt); auto longSegment = false; auto numPoints = size_t(ceilf(ptsCnt) * 2); auto direction = star->clockwise ? 1.0f : -1.0f; auto hasRoundness = false; bool roundedCorner = ctx->roundness && (tvg::zero(innerRoundness) || tvg::zero(outerRoundness)); Shape* shape; if (roundedCorner || ctx->offset) { shape = star->pooling(); shape->reset(); } else { shape = merging; } float x, y; if (!tvg::zero(partialPointAmount)) { angle += halfAnglePerPoint * (1.0f - partialPointAmount) * direction; } if (!tvg::zero(partialPointAmount)) { partialPointRadius = innerRadius + partialPointAmount * (outerRadius - innerRadius); x = partialPointRadius * cosf(angle); y = partialPointRadius * sinf(angle); angle += anglePerPoint * partialPointAmount * 0.5f * direction; } else { x = outerRadius * cosf(angle); y = outerRadius * sinf(angle); angle += halfAnglePerPoint * direction; } if (tvg::zero(innerRoundness) && tvg::zero(outerRoundness)) { to(shape)->rs.path.pts.reserve(numPoints + 2); to(shape)->rs.path.cmds.reserve(numPoints + 3); } else { to(shape)->rs.path.pts.reserve(numPoints * 3 + 2); to(shape)->rs.path.cmds.reserve(numPoints + 3); hasRoundness = true; } auto in = Point{x, y} * transform; shape->moveTo(in.x, in.y); for (size_t i = 0; i < numPoints; i++) { auto radius = longSegment ? outerRadius : innerRadius; auto dTheta = halfAnglePerPoint; if (!tvg::zero(partialPointRadius) && i == numPoints - 2) { dTheta = anglePerPoint * partialPointAmount * 0.5f; } if (!tvg::zero(partialPointRadius) && i == numPoints - 1) { radius = partialPointRadius; } auto previousX = x; auto previousY = y; x = radius * cosf(angle); y = radius * sinf(angle); if (hasRoundness) { auto cp1Theta = (tvg::atan2(previousY, previousX) - MATH_PI2 * direction); auto cp1Dx = cosf(cp1Theta); auto cp1Dy = sinf(cp1Theta); auto cp2Theta = (tvg::atan2(y, x) - MATH_PI2 * direction); auto cp2Dx = cosf(cp2Theta); auto cp2Dy = sinf(cp2Theta); auto cp1Roundness = longSegment ? innerRoundness : outerRoundness; auto cp2Roundness = longSegment ? outerRoundness : innerRoundness; auto cp1Radius = longSegment ? innerRadius : outerRadius; auto cp2Radius = longSegment ? outerRadius : innerRadius; auto cp1x = cp1Radius * cp1Roundness * POLYSTAR_MAGIC_NUMBER * cp1Dx / ptsCnt; auto cp1y = cp1Radius * cp1Roundness * POLYSTAR_MAGIC_NUMBER * cp1Dy / ptsCnt; auto cp2x = cp2Radius * cp2Roundness * POLYSTAR_MAGIC_NUMBER * cp2Dx / ptsCnt; auto cp2y = cp2Radius * cp2Roundness * POLYSTAR_MAGIC_NUMBER * cp2Dy / ptsCnt; if (!tvg::zero(partialPointAmount) && ((i == 0) || (i == numPoints - 1))) { cp1x *= partialPointAmount; cp1y *= partialPointAmount; cp2x *= partialPointAmount; cp2y *= partialPointAmount; } auto in2 = Point{previousX - cp1x, previousY - cp1y} * transform; auto in3 = Point{x + cp2x, y + cp2y} * transform; auto in4 = Point{x, y} * transform; shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y); } else { auto in = Point{x, y} * transform; shape->lineTo(in.x, in.y); } angle += dTheta * direction; longSegment = !longSegment; } //ensure proper shape closure - important for modifiers that behave differently for degenerate (linear) vs curved cubics _close(to(shape)->rs.path.pts, in, hasRoundness); shape->close(); if (ctx->modifier) ctx->modifier->modifyPolystar(to(shape)->rs.path, to(merging)->rs.path, outerRoundness, hasRoundness); } void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, float frameNo, Matrix* transform, Shape* merging, RenderContext* ctx, Tween& tween, LottieExpressions* exps) { static constexpr auto POLYGON_MAGIC_NUMBER = 0.25f; auto ptsCnt = size_t(floor(star->ptsCnt(frameNo, tween, exps))); auto radius = star->outerRadius(frameNo, tween, exps); auto outerRoundness = star->outerRoundness(frameNo, tween, exps) * 0.01f; auto angle = -MATH_PI2; auto anglePerPoint = 2.0f * MATH_PI / float(ptsCnt); auto direction = star->clockwise ? 1.0f : -1.0f; auto hasRoundness = !tvg::zero(outerRoundness); bool roundedCorner = ctx->roundness && !hasRoundness; auto x = radius * cosf(angle); auto y = radius * sinf(angle); angle += anglePerPoint * direction; Shape* shape; if (roundedCorner || ctx->offset) { shape = star->pooling(); shape->reset(); } else { shape = merging; if (hasRoundness) { to(shape)->rs.path.pts.reserve(ptsCnt * 3 + 2); to(shape)->rs.path.cmds.reserve(ptsCnt + 3); } else { to(shape)->rs.path.pts.reserve(ptsCnt + 2); to(shape)->rs.path.cmds.reserve(ptsCnt + 3); } } auto in = Point{x, y} * transform; shape->moveTo(in.x, in.y); auto coeff = anglePerPoint * radius * outerRoundness * POLYGON_MAGIC_NUMBER; for (size_t i = 0; i < ptsCnt; i++) { auto previousX = x; auto previousY = y; x = (radius * cosf(angle)); y = (radius * sinf(angle)); if (hasRoundness) { auto cp1Theta = tvg::atan2(previousY, previousX) - MATH_PI2 * direction; auto cp1x = coeff * cosf(cp1Theta); auto cp1y = coeff * sinf(cp1Theta); auto cp2Theta = tvg::atan2(y, x) - MATH_PI2 * direction; auto cp2x = coeff * cosf(cp2Theta); auto cp2y = coeff * sinf(cp2Theta); auto in2 = Point{previousX - cp1x, previousY - cp1y} * transform; auto in3 = Point{x + cp2x, y + cp2y} * transform; auto in4 = Point{x, y} * transform; shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y); } else { Point in = {x, y}; if (transform) in *= *transform; shape->lineTo(in.x, in.y); } angle += anglePerPoint * direction; } //ensure proper shape closure - important for modifiers that behave differently for degenerate (linear) vs curved cubics _close(to(shape)->rs.path.pts, in, hasRoundness); shape->close(); if (ctx->modifier) ctx->modifier->modifyPolystar(to(shape)->rs.path, to(merging)->rs.path, 0.0f, false); } void LottieBuilder::updatePolystar(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto star = static_cast(*child); //Optimize: Can we skip the individual coords transform? auto matrix = tvg::identity(); translate(&matrix, star->position(frameNo, tween, exps)); rotate(&matrix, star->rotation(frameNo, tween, exps)); if (ctx->transform) matrix = *ctx->transform * matrix; auto identity = tvg::identity((const Matrix*)&matrix); if (ctx->repeaters.empty()) { _draw(parent, star, ctx); if (star->type == LottiePolyStar::Star) updateStar(star, frameNo, (identity ? nullptr : &matrix), ctx->merging, ctx, tween, exps); else updatePolygon(parent, star, frameNo, (identity ? nullptr : &matrix), ctx->merging, ctx, tween, exps); PAINT(ctx->merging)->mark(RenderUpdateFlag::Path); } else { auto shape = star->pooling(); shape->reset(); if (star->type == LottiePolyStar::Star) updateStar(star, frameNo, (identity ? nullptr : &matrix), shape, ctx, tween, exps); else updatePolygon(parent, star, frameNo, (identity ? nullptr : &matrix), shape, ctx, tween, exps); _repeat(parent, shape, star, ctx); } } void LottieBuilder::updateRoundedCorner(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto roundedCorner = static_cast(*child); auto r = roundedCorner->radius(frameNo, tween, exps); if (r < LottieRoundnessModifier::ROUNDNESS_EPSILON) return; if (!ctx->roundness) ctx->roundness = new LottieRoundnessModifier(&buffer, r); else if (ctx->roundness->r < r) ctx->roundness->r = r; ctx->update(ctx->roundness); } void LottieBuilder::updateOffsetPath(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto offset = static_cast(*child); if (!ctx->offset) ctx->offset = new LottieOffsetModifier(offset->offset(frameNo, tween, exps), offset->miterLimit(frameNo, tween, exps), offset->join); ctx->update(ctx->offset); } void LottieBuilder::updateRepeater(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto repeater = static_cast(*child); RenderRepeater r; r.cnt = static_cast(repeater->copies(frameNo, tween, exps)); r.transform = ctx->propagator->transform(); r.offset = repeater->offset(frameNo, tween, exps); r.position = repeater->position(frameNo, tween, exps); r.anchor = repeater->anchor(frameNo, tween, exps); r.scale = repeater->scale(frameNo, tween, exps); r.rotation = repeater->rotation(frameNo, tween, exps); r.startOpacity = repeater->startOpacity(frameNo, tween, exps); r.endOpacity = repeater->endOpacity(frameNo, tween, exps); r.inorder = repeater->inorder; ctx->repeaters.push(r); ctx->merging = nullptr; } void LottieBuilder::updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto trimpath = static_cast(*child); float begin, end; trimpath->segment(frameNo, begin, end, tween, exps); if (to(ctx->propagator)->rs.stroke) { auto length = fabsf(begin - end); auto tmp = begin; begin = (length * to(ctx->propagator)->rs.stroke->trim.begin) + tmp; end = (length * to(ctx->propagator)->rs.stroke->trim.end) + tmp; } ctx->propagator->trimpath(begin, end, trimpath->type == LottieTrimpath::Type::Simultaneous); ctx->merging = nullptr; } void LottieBuilder::updateChildren(LottieGroup* parent, float frameNo, Inlist& contexts) { contexts.head->begin = parent->children.end() - 1; while (!contexts.empty()) { auto ctx = contexts.front(); ctx->reqFragment = parent->reqFragment; auto stop = false; for (auto child = ctx->begin; child >= parent->children.data; --child) { //Here switch-case statements are more performant than virtual methods. switch ((*child)->type) { case LottieObject::Group: { updateGroup(parent, child, frameNo, contexts, ctx); break; } case LottieObject::Transform: { updateTransform(parent, child, frameNo, contexts, ctx); break; } case LottieObject::SolidFill: { stop = updateSolidFill(parent, child, frameNo, contexts, ctx); break; } case LottieObject::SolidStroke: { stop = updateSolidStroke(parent, child, frameNo, contexts, ctx); break; } case LottieObject::GradientFill: { stop = updateGradientFill(parent, child, frameNo, contexts, ctx); break; } case LottieObject::GradientStroke: { stop = updateGradientStroke(parent, child, frameNo, contexts, ctx); break; } case LottieObject::Rect: { updateRect(parent, child, frameNo, contexts, ctx); break; } case LottieObject::Ellipse: { updateEllipse(parent, child, frameNo, contexts, ctx); break; } case LottieObject::Path: { updatePath(parent, child, frameNo, contexts, ctx); break; } case LottieObject::Polystar: { updatePolystar(parent, child, frameNo, contexts, ctx); break; } case LottieObject::Trimpath: { updateTrimpath(parent, child, frameNo, contexts, ctx); break; } case LottieObject::Repeater: { updateRepeater(parent, child, frameNo, contexts, ctx); break; } case LottieObject::RoundedCorner: { updateRoundedCorner(parent, child, frameNo, contexts, ctx); break; } case LottieObject::OffsetPath: { updateOffsetPath(parent, child, frameNo, contexts, ctx); break; } default: break; } //stop processing for those invisible contents if (stop || ctx->propagator->opacity() == 0) break; } delete(ctx); } } void LottieBuilder::updatePrecomp(LottieComposition* comp, LottieLayer* precomp, float frameNo) { if (precomp->children.empty()) return; frameNo = precomp->remap(comp, frameNo, exps); ARRAY_REVERSE_FOREACH(c, precomp->children) { auto child = static_cast(*c); if (!child->matteSrc) updateLayer(comp, precomp->scene, child, frameNo); } //clip the layer viewport auto clipper = precomp->statical.pooling(true); clipper->transform(precomp->cache.matrix); precomp->scene->clip(clipper); } void LottieBuilder::updatePrecomp(LottieComposition* comp, LottieLayer* precomp, float frameNo, Tween& tween) { //record & recover the tweening frame number before remapping auto record = tween.frameNo; tween.frameNo = precomp->remap(comp, record, exps); updatePrecomp(comp, precomp, frameNo); tween.frameNo = record; } void LottieBuilder::updateSolid(LottieLayer* layer) { auto solidFill = layer->statical.pooling(true); solidFill->opacity(layer->cache.opacity); layer->scene->add(solidFill); } void LottieBuilder::updateImage(LottieGroup* layer) { if (layer->children.empty()) return; auto image = static_cast(layer->children.first()); auto picture = image->bitmap.picture; //resolve an image asset if need if (resolver && !image->resolved) { resolver->func(picture, image->bitmap.path, resolver->data); picture->size(image->bitmap.width, image->bitmap.height); image->resolved = true; } //LottieImage can be shared among other layers layer->scene->add(picture->refCnt() == 1 ? picture : picture->duplicate()); } void LottieBuilder::updateURLFont(LottieLayer* layer, float frameNo, LottieText* text, const TextDocument& doc) { //text load //TODO: cache the text instance, don't need to reload every frame. auto paint = Text::gen(); if (paint->font(doc.name) != Result::Success) { char* src; bool free = false; if (text->font && text->font->path) src = text->font->path; else { auto len = (strlen(doc.name) + 6); src = tvg::malloc(sizeof(char) * len); snprintf(src, len, "name:%s", doc.name); free = true; } if (!resolver || !resolver->func(paint, src, resolver->data)) { paint->font(nullptr); //fallback to any available font } if (free) tvg::free(src); } //text build auto len = strlen(doc.text); auto buf = tvg::malloc(len + 1); // preprocessing text for modern systems: handle carriage return ('\r') and end-of-text ('\3') // as line feed ('\n') only when they appear independently. auto p = buf; auto feed = false; for (size_t i = 0; i < len; ++i) { //replace the carriage return and end of text with line feed. if (doc.text[i] == '\r' || doc.text[i] == '\3') { if (!feed) *p = '\n'; else continue; } else *p = doc.text[i]; feed = (*p == '\n') ? true : false; ++p; } *p = '\0'; auto color = doc.color; paint->fill(color.r, color.g, color.b); paint->size(doc.size * 75.0f); //1 pt = 1/72; 1 in = 96 px; -> 72/96 = 0.75 paint->text(buf); paint->layout(doc.bbox.size.x, doc.bbox.size.y); paint->translate(doc.bbox.pos.x, doc.bbox.pos.y); //align the text to the base line TextMetrics metrics; paint->metrics(metrics); paint->align(doc.justify, metrics.ascent / (metrics.ascent - metrics.descent)); layer->scene->add(paint); //outline auto strkColor = doc.stroke.color; if (doc.stroke.width > 0.0f) paint->outline(doc.stroke.width, strkColor.r, strkColor.g, strkColor.b); tvg::free(buf); //text range if (text->ranges.empty()) return; //FIXME: it only considers a single text-range. ARRAY_FOREACH(p, text->ranges) { auto range = *p; auto f = range->factor(frameNo, float(len), 0.0f); if (tvg::zero(f)) continue; //fill & opacity range->color(frameNo, color, strkColor, f, tween, exps); paint->fill(color.r, color.g, color.b); paint->opacity(range->style.opacity(frameNo, tween, exps)); //stroke if (range->style.flags.strokeWidth) { paint->outline(f * range->style.strokeWidth(frameNo, tween, exps), strkColor.r, strkColor.g, strkColor.b); } } } Shape* LottieBuilder::textShape(LottieText* text, float frameNo, const TextDocument& doc, LottieGlyph* glyph, const RenderText& ctx) { auto& transform = ctx.lineScene->transform(); auto shape = text->pooling(); shape->reset(); ARRAY_FOREACH(p, glyph->children) { auto group = static_cast(*p); ARRAY_FOREACH(p, group->children) { if (static_cast(*p)->pathset(frameNo, to(shape)->rs.path, nullptr, tween, exps)) { PAINT(shape)->mark(RenderUpdateFlag::Path); } } } shape->fill(doc.color.r, doc.color.g, doc.color.b); shape->translate(ctx.cursor.x - transform.e13, ctx.cursor.y - transform.e23); shape->opacity(255); if (doc.stroke.width > 0.0f) { shape->strokeJoin(StrokeJoin::Round); shape->strokeWidth(doc.stroke.width / ctx.scale); shape->strokeFill(doc.stroke.color.r, doc.stroke.color.g, doc.stroke.color.b); shape->order(doc.stroke.below); } return shape; } bool LottieBuilder::updateTextRange(LottieText* text, float frameNo, Shape* shape, const TextDocument& doc, RenderText& ctx) { if (text->ranges.empty()) return false; auto& transform = ctx.lineScene->transform(); Point scaling = {1.0f, 1.0f}; Point translation = {}; auto rotation = 0.0f; auto color = doc.color; auto strokeColor = doc.stroke.color; uint8_t opacity = 255, fillOpacity = 255, strokeOpacity = 255; auto needGroup = false; ARRAY_FOREACH(p, text->ranges) { auto range = *p; auto basedIdx = ctx.idx; if (range->based == LottieTextRange::Based::CharsExcludingSpaces) basedIdx = ctx.idx - ctx.space; else if (range->based == LottieTextRange::Based::Words) basedIdx = ctx.line + ctx.space; else if (range->based == LottieTextRange::Based::Lines) basedIdx = ctx.line; auto f = range->factor(frameNo, float(ctx.nChars), (float)basedIdx); if (tvg::zero(f)) continue; needGroup = true; //transform translation = translation + f * range->style.position(frameNo, tween, exps); scaling *= (f * (range->style.scale(frameNo, tween, exps) * 0.01f - Point{1.0f, 1.0f}) + Point{1.0f, 1.0f}); rotation += f * range->style.rotation(frameNo, tween, exps); //fill & opacity opacity = (uint8_t)(opacity - f * (opacity - range->style.opacity(frameNo, tween, exps))); shape->opacity(opacity); range->color(frameNo, color, strokeColor, f, tween, exps); fillOpacity = (uint8_t)(fillOpacity - f * (fillOpacity - range->style.fillOpacity(frameNo, tween, exps))); shape->fill(color.r, color.g, color.b, fillOpacity); //stroke if (range->style.flags.strokeWidth) shape->strokeWidth(f * range->style.strokeWidth(frameNo, tween, exps) / ctx.scale); if (shape->strokeWidth() > 0.0f) { strokeOpacity = (uint8_t)(strokeOpacity - f * (strokeOpacity - range->style.strokeOpacity(frameNo, tween, exps))); shape->strokeFill(strokeColor.r, strokeColor.g, strokeColor.b, strokeOpacity); shape->order(doc.stroke.below); } ctx.cursor.x += f * range->style.letterSpace(frameNo, tween, exps); auto spacing = f * range->style.lineSpace(frameNo, tween, exps); if (spacing > ctx.lineSpace) ctx.lineSpace = spacing; } // Apply line group transformation just once if (ctx.lineScene->paints().empty() && needGroup) { tvg::identity(&transform); translate(&transform, ctx.cursor); // center pivoting auto align = text->alignOp.anchor(frameNo, tween, exps); transform.e13 += align.x; transform.e23 += align.y; rotate(&transform, rotation); //center pivoting auto pivot = align * -1; transform.e13 += (pivot.x * transform.e11 + pivot.x * transform.e12); transform.e23 += (pivot.y * transform.e21 + pivot.y * transform.e22); ctx.lineScene->transform(transform); } auto& matrix = shape->transform(); tvg::identity(&matrix); translate(&matrix, (translation / ctx.scale + ctx.cursor) - Point{transform.e13, transform.e23}); tvg::scale(&matrix, scaling * ctx.capScale); shape->transform(matrix); if (needGroup) ctx.lineScene->add(shape); return needGroup; } static void _commit(LottieGlyph* glyph, Shape* shape, const RenderText& ctx) { auto& matrix = shape->transform(); if (ctx.follow) { tvg::identity(&matrix); auto angle = 0.0f; auto width = glyph->width * 0.5f; auto pos = ctx.follow->position(ctx.cursor.x + width + ctx.firstMargin, angle); matrix.e11 = matrix.e22 = ctx.capScale; matrix.e13 = pos.x - width * matrix.e11; matrix.e23 = pos.y - width * matrix.e21; } else { matrix.e11 = matrix.e22 = ctx.capScale; matrix.e13 = ctx.cursor.x; matrix.e23 = ctx.cursor.y; } shape->transform(matrix); ctx.textScene->add(shape); } void LottieBuilder::updateLocalFont(LottieLayer* layer, float frameNo, LottieText* text, const TextDocument& doc) { RenderText ctx(text, doc); ctx.follow = (text->follow && ((uint32_t)text->follow->maskIdx < layer->masks.count)) ? text->follow : nullptr; ctx.firstMargin = ctx.follow ? ctx.follow->prepare(layer->masks[ctx.follow->maskIdx], frameNo, ctx.scale, tween, exps) : 0.0f; auto lineWrapped = false; //text string while (true) { //new line of the cursor position if (lineWrapped || *ctx.p == 13 || *ctx.p == 3 || *ctx.p == '\0') { //text layout position auto ascent = text->font->ascent * ctx.scale; if (ascent > doc.bbox.size.y) ascent = doc.bbox.size.y; //horizontal alignment Point layout = {doc.bbox.pos.x, doc.bbox.pos.y + ascent - doc.shift}; layout.x += doc.justify * (doc.bbox.size.x - ctx.cursor.x * ctx.scale); //new text group, single scene based on text-grouping ctx.textScene->add(ctx.lineScene); ctx.textScene->translate(layout.x, layout.y); ctx.textScene->scale(ctx.scale); layer->scene->add(ctx.textScene); ctx.lineScene = Scene::gen(); ctx.lineScene->translate(ctx.cursor.x, ctx.cursor.y); if (*ctx.p == '\0') { ctx.textScene = nullptr; break; } if (!lineWrapped) ++ctx.p; ctx.totalLineSpace += ctx.lineSpace; ctx.lineSpace = 0.0f; lineWrapped = false; //new text group, single scene for each line ctx.textScene = Scene::gen(); ctx.cursor = {0.0f, (++ctx.line * doc.height + ctx.totalLineSpace) / ctx.scale}; continue; } if (*ctx.p == ' ') { ++ctx.space; //new text group, single scene for each word if (text->alignOp.group == LottieText::AlignOption::Group::Word) { ctx.textScene->add(ctx.lineScene); ctx.lineScene = Scene::gen(); ctx.lineScene->translate(ctx.cursor.x, ctx.cursor.y); } } /* all lowercase letters are converted to uppercase in the "t" text field, making the "ca" value irrelevant, thus AllCaps is nothing to do. So only convert lowercase letters to uppercase (for 'SmallCaps' an extra scaling factor applied) */ ctx.capScale = 1.0f; auto code = ctx.p; char capCode; if ((unsigned char)(ctx.p[0]) < 0x80 && doc.caps) { if (*ctx.p >= 'a' && *ctx.p <= 'z') { capCode = *ctx.p + 'A' - 'a'; code = &capCode; if (doc.caps == 2) ctx.capScale = 0.7f; } } // text building auto found = false; ARRAY_FOREACH(g, text->font->chars) { auto glyph = *g; //draw matched glyphs if (!strncmp(glyph->code, code, glyph->len)) { //new text group, single scene for each characters if (text->alignOp.group == LottieText::AlignOption::Group::Chars || text->alignOp.group == LottieText::AlignOption::Group::All) { ctx.textScene->add(ctx.lineScene); ctx.lineScene = Scene::gen(); ctx.lineScene->translate(ctx.cursor.x, ctx.cursor.y); } auto shape = textShape(text, frameNo, doc, glyph, ctx); if (!updateTextRange(text, frameNo, shape, doc, ctx)) _commit(glyph, shape, ctx); if (doc.bbox.size.x > 0.0f && ctx.cursor.x * ctx.scale >= doc.bbox.size.x) lineWrapped = true; else ctx.cursor.x += (glyph->width + doc.tracking) * ctx.capScale; ctx.p += glyph->len; ctx.idx += glyph->len; found = true; break; } } if (!found) { ++ctx.p; ++ctx.idx; } } } void LottieBuilder::updateText(LottieLayer* layer, float frameNo) { if (layer->children.empty()) return; auto text = static_cast(layer->children.first()); auto& doc = text->doc(frameNo, exps); if (!doc.text) return; if (text->font && text->font->origin == LottieFont::Origin::Local && !text->font->chars.empty()) { updateLocalFont(layer, frameNo, text, doc); } else { updateURLFont(layer, frameNo, text, doc); } } void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo) { if (layer->masks.count == 0) return; //Introduce an intermediate scene for embracing matte + masking or precomp clipping + masking replaced by clipping if (layer->matteTarget || layer->type == LottieLayer::Precomp) { auto scene = Scene::gen(); scene->add(layer->scene); layer->scene = scene; } Shape* pShape = nullptr; MaskMethod pMethod; uint8_t pOpacity; ARRAY_FOREACH(p, layer->masks) { auto mask = *p; if (mask->method == MaskMethod::None) continue; auto method = mask->method; auto opacity = mask->opacity(frameNo); auto expand = mask->expand(frameNo); //the first mask if (!pShape) { pShape = layer->pooling(); to(pShape)->reset(); auto compMethod = (method == MaskMethod::Subtract || method == MaskMethod::InvAlpha) ? MaskMethod::InvAlpha : MaskMethod::Alpha; //Cheaper. Replace the masking with a clipper if (layer->effects.empty() && layer->masks.count == 1 && compMethod == MaskMethod::Alpha) { layer->scene->opacity(MULTIPLY(layer->scene->opacity(), opacity)); layer->scene->clip(pShape); } else { layer->scene->mask(pShape, compMethod); } //Chain mask composition } else if (pMethod != method || pOpacity != opacity || (method != MaskMethod::Subtract && method != MaskMethod::Difference)) { auto shape = layer->pooling(); to(shape)->reset(); pShape->mask(shape, method); pShape = shape; } pShape->fill(255, 255, 255, opacity); pShape->transform(layer->cache.matrix); //Default Masking if (expand == 0.0f) { mask->pathset(frameNo, to(pShape)->rs.path, nullptr, tween, exps); //Masking with Expansion (Offset) } else { //TODO: Once path direction support is implemented, ensure that the direction is ignored here auto offset = LottieOffsetModifier(expand); mask->pathset(frameNo, to(pShape)->rs.path, nullptr, tween, exps, &offset); } pOpacity = opacity; pMethod = method; } } bool LottieBuilder::updateMatte(LottieComposition* comp, float frameNo, Scene* scene, LottieLayer* layer) { auto target = layer->matteTarget; if (!target || target->type == LottieLayer::Null) return true; updateLayer(comp, scene, target, frameNo); if (target->scene) { layer->scene->mask(target->scene, layer->matteType); } else if (layer->matteType == MaskMethod::Alpha || layer->matteType == MaskMethod::Luma) { //matte target is not exist. alpha blending definitely bring an invisible result Paint::rel(layer->scene); layer->scene = nullptr; return false; } return true; } void LottieBuilder::updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo) { if (layer->masks.count == 0) return; auto shape = layer->pooling(); shape->reset(); //FIXME: all mask if (effect->allMask(frameNo)) { ARRAY_FOREACH(p, layer->masks) { (*p)->pathset(frameNo, to(shape)->rs.path, nullptr, tween, exps); } //A specific mask } else { auto idx = static_cast(effect->mask(frameNo) - 1); if (idx < 0 || idx >= layer->masks.count) return; layer->masks[idx]->pathset(frameNo, to(shape)->rs.path, nullptr, tween, exps); } shape->transform(layer->cache.matrix); shape->trimpath(effect->begin(frameNo) * 0.01f, effect->end(frameNo) * 0.01f); shape->strokeFill(255, 255, 255, (int)(effect->opacity(frameNo) * 255.0f)); shape->strokeJoin(StrokeJoin::Round); shape->strokeCap(StrokeCap::Round); auto size = effect->size(frameNo) * 2.0f; shape->strokeWidth(size); //fill the color to the layer shapes if any auto color = effect->color(frameNo); if (color.r != 255 || color.g != 255 || color.b != 255) { auto accessor = tvg::Accessor::gen(); auto stroke = (layer->type == LottieLayer::Type::Shape) ? true : false; auto f = [color, size, stroke](const tvg::Paint* paint, void* data) -> bool { if (paint->type() == tvg::Type::Shape) { auto shape = (tvg::Shape*) paint; //expand shape to fill the stroke region if (stroke) { shape->strokeWidth(size); shape->strokeFill(color.r, color.g, color.b, 255); } shape->fill(color.r, color.g, color.b, 255); } return true; }; accessor->set(layer->scene, f, nullptr); delete(accessor); } layer->scene->mask(shape, MaskMethod::Alpha); } void LottieBuilder::updateEffect(LottieLayer* layer, float frameNo, uint8_t quality) { constexpr float BLUR_TO_SIGMA = 0.3f; if (layer->effects.count == 0) return; ARRAY_FOREACH(p, layer->effects) { if (!(*p)->enable) continue; switch ((*p)->type) { case LottieEffect::Tint: { auto effect = static_cast(*p); auto black = effect->black(frameNo); auto white = effect->white(frameNo); layer->scene->add(SceneEffect::Tint, black.r, black.g, black.b, white.r, white.g, white.b, (double)effect->intensity(frameNo)); break; } case LottieEffect::Fill: { auto effect = static_cast(*p); auto color = effect->color(frameNo); layer->scene->add(SceneEffect::Fill, color.r, color.g, color.b, (int)(255.0f * effect->opacity(frameNo))); break; } case LottieEffect::Stroke: { auto effect = static_cast(*p); updateStrokeEffect(layer, effect, frameNo); break; } case LottieEffect::Tritone: { auto effect = static_cast(*p); auto dark = effect->dark(frameNo); auto midtone = effect->midtone(frameNo); auto bright = effect->bright(frameNo); layer->scene->add(SceneEffect::Tritone, dark.r, dark.g, dark.b, midtone.r, midtone.g, midtone.b, bright.r, bright.g, bright.b, (int)effect->blend(frameNo)); break; } case LottieEffect::DropShadow: { auto effect = static_cast(*p); auto color = effect->color(frameNo); //seems the opacity range in dropshadow is 0 ~ 256 layer->scene->add(SceneEffect::DropShadow, color.r, color.g, color.b, std::min(255, (int)effect->opacity(frameNo)), (double)effect->angle(frameNo), double(effect->distance(frameNo)), (double)(effect->blurness(frameNo) * BLUR_TO_SIGMA), quality); break; } case LottieEffect::GaussianBlur: { auto effect = static_cast(*p); layer->scene->add(SceneEffect::GaussianBlur, (double)(effect->blurness(frameNo) * BLUR_TO_SIGMA), effect->direction(frameNo) - 1, effect->wrap(frameNo), quality); break; } default: break; } } } void LottieBuilder::updateLayer(LottieComposition* comp, Scene* scene, LottieLayer* layer, float frameNo) { layer->scene = nullptr; //visibility if (frameNo < layer->inFrame || frameNo >= layer->outFrame) return; updateTransform(layer, frameNo); //full transparent scene. no need to perform if (layer->type != LottieLayer::Null && layer->cache.opacity == 0) return; //Prepare render data layer->scene = Scene::gen(); layer->scene->id = layer->id; //ignore opacity when Null layer? if (layer->type != LottieLayer::Null) layer->scene->opacity(layer->cache.opacity); layer->scene->transform(layer->cache.matrix); if (!layer->matteSrc && !updateMatte(comp, frameNo, scene, layer)) return; switch (layer->type) { case LottieLayer::Precomp: { if (!tweening()) updatePrecomp(comp, layer, frameNo); else updatePrecomp(comp, layer, frameNo, tween); break; } case LottieLayer::Solid: { updateSolid(layer); break; } case LottieLayer::Image: { updateImage(layer); break; } case LottieLayer::Text: { updateText(layer, frameNo); break; } default: { if (!layer->children.empty()) { Inlist contexts; contexts.back(new RenderContext(layer->pooling())); updateChildren(layer, frameNo, contexts); contexts.free(); } break; } } updateMasks(layer, frameNo); layer->scene->blend(layer->blendMethod); updateEffect(layer, frameNo, comp->quality); if (!layer->matteSrc) scene->add(layer->scene); } static void _buildReference(LottieComposition* comp, LottieLayer* layer) { ARRAY_FOREACH(p, comp->assets) { if (layer->rid != (*p)->id) continue; if (layer->type == LottieLayer::Precomp) { auto assetLayer = static_cast(*p); if (_buildComposition(comp, assetLayer)) { layer->children = assetLayer->children; layer->reqFragment = assetLayer->reqFragment; } } else if (layer->type == LottieLayer::Image) { layer->children.push(*p); } break; } } static void _buildHierarchy(LottieGroup* parent, LottieLayer* child) { if (child->pix == -1) return; if (child->matteTarget && child->pix == child->matteTarget->ix) { child->parent = child->matteTarget; return; } ARRAY_FOREACH(p, parent->children) { auto parent = static_cast(*p); if (child == parent) continue; if (child->pix == parent->ix) { child->parent = parent; break; } if (parent->matteTarget && parent->matteTarget->ix == child->pix) { child->parent = parent->matteTarget; break; } } } static void _attachFont(LottieComposition* comp, LottieLayer* parent) { //TODO: Consider to migrate this attachment to the frame update time. ARRAY_FOREACH(p, parent->children) { auto text = static_cast(*p); auto& doc = text->doc(0); if (!doc.name) continue; auto len = strlen(doc.name); for (uint32_t i = 0; i < comp->fonts.count; ++i) { auto font = comp->fonts[i]; auto len2 = strlen(font->name); if (len == len2 && !strcmp(font->name, doc.name)) { text->font = font; break; } } } } static bool _buildComposition(LottieComposition* comp, LottieLayer* parent) { if (parent->children.count == 0) return false; if (parent->buildDone) return true; parent->buildDone = true; ARRAY_FOREACH(p, parent->children) { auto child = static_cast(*p); //attach the precomp layer. if (child->rid) _buildReference(comp, child); if (child->matteType != MaskMethod::None) { //no index of the matte layer is provided: the layer above is used as the matte source if (child->mix == -1) { if (p > parent->children.begin()) { child->matteTarget = static_cast(*(p - 1)); } //matte layer is specified by an index. } else child->matteTarget = parent->layerByIdx(child->mix); } if (child->matteTarget) { child->matteTarget->matteSrc = true; //parenting _buildHierarchy(parent, child->matteTarget); //precomp referencing if (child->matteTarget->rid) _buildReference(comp, child->matteTarget); } _buildHierarchy(parent, child); //attach the necessary font data if (child->type == LottieLayer::Text) _attachFont(comp, child); } return true; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool LottieBuilder::update(LottieComposition* comp, float frameNo) { if (comp->root->children.empty()) return false; comp->clamp(frameNo); if (tweening()) { comp->clamp(tween.frameNo); //tweening is not necessary. if (equal(frameNo, tween.frameNo)) offTween(); } if (exps && comp->expressions) exps->update(comp->timeAtFrame(frameNo)); //update children layers ARRAY_REVERSE_FOREACH(child, comp->root->children) { auto layer = static_cast(*child); if (!layer->matteSrc) updateLayer(comp, comp->root->scene, layer, frameNo); } return true; } void LottieBuilder::build(LottieComposition* comp) { if (!comp) return; comp->root->scene = Scene::gen(); _buildComposition(comp, comp->root); //viewport clip auto clip = Shape::gen(); clip->appendRect(0, 0, comp->w, comp->h); comp->root->scene->clip(clip); //turn off partial rendering for children to(comp->root->scene)->size({comp->w, comp->h}); }src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/random.cpp000664 001750 001750 00000003446 15164251010 035446 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2013 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Pseudo-random utilities // // Author: Skal (pascal.massimino@gmail.com) #include #include "./random.h" //------------------------------------------------------------------------------ // 31b-range values static const uint32_t kRandomTable[VP8_RANDOM_TABLE_SIZE] = { 0x0de15230, 0x03b31886, 0x775faccb, 0x1c88626a, 0x68385c55, 0x14b3b828, 0x4a85fef8, 0x49ddb84b, 0x64fcf397, 0x5c550289, 0x4a290000, 0x0d7ec1da, 0x5940b7ab, 0x5492577d, 0x4e19ca72, 0x38d38c69, 0x0c01ee65, 0x32a1755f, 0x5437f652, 0x5abb2c32, 0x0faa57b1, 0x73f533e7, 0x685feeda, 0x7563cce2, 0x6e990e83, 0x4730a7ed, 0x4fc0d9c6, 0x496b153c, 0x4f1403fa, 0x541afb0c, 0x73990b32, 0x26d7cb1c, 0x6fcc3706, 0x2cbb77d8, 0x75762f2a, 0x6425ccdd, 0x24b35461, 0x0a7d8715, 0x220414a8, 0x141ebf67, 0x56b41583, 0x73e502e3, 0x44cab16f, 0x28264d42, 0x73baaefb, 0x0a50ebed, 0x1d6ab6fb, 0x0d3ad40b, 0x35db3b68, 0x2b081e83, 0x77ce6b95, 0x5181e5f0, 0x78853bbc, 0x009f9494, 0x27e5ed3c }; void VP8InitRandom(VP8Random* const rg, float dithering) { memcpy(rg->tab_, kRandomTable, sizeof(rg->tab_)); rg->index1_ = 0; rg->index2_ = 31; rg->amp_ = (dithering < 0.0f) ? 0 : (dithering > 1.0f) ? (1 << VP8_RANDOM_DITHER_FIX) : (uint32_t)((1 << VP8_RANDOM_DITHER_FIX) * dithering); } //------------------------------------------------------------------------------ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgRenderTask.h000664 001750 001750 00000005733 15164251010 036737 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_RENDER_TASK_H_ #define _TVG_WG_RENDER_TASK_H_ #include "tvgWgCompositor.h" // base class for any renderable objects struct WgRenderTask { virtual ~WgRenderTask() {} virtual void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) = 0; }; // task for single shape rendering struct WgPaintTask: public WgRenderTask { // shape render properties WgRenderDataPaint* renderData{}; BlendMethod blendMethod{}; WgPaintTask(WgRenderDataPaint* renderData, BlendMethod blendMethod) : renderData(renderData), blendMethod(blendMethod) {} // apply shape execution, including custom blending and clipping void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override; }; // task for scene rendering with blending, composition and effect struct WgSceneTask: public WgRenderTask { public: // parent scene (nullptr for root scene) WgSceneTask* parent{}; // children can be shapes or scenes tasks Array children; // scene blend/compose targets WgRenderTarget* renderTarget{}; WgRenderTarget* renderTargetMsk{}; WgRenderTarget* renderTargetDst{}; // scene blend/compose properties WgCompose* compose{}; // scene effect properties const RenderEffect* effect{}; WgSceneTask(WgRenderTarget* renderTarget, WgCompose* compose, WgSceneTask* parent) : parent(parent), renderTarget(renderTarget), compose(compose) {} // run all, including all shapes drawing, blending, composition and effect void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override; private: void runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder); void runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder); }; #endif // _TVG_WG_RENDER_TASK_H_ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-generator.cpp000664 001750 001750 00000002041 15164251010 050357 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-globals.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-generator.inc.h" #define BUILTIN_UNDERSCORED_ID generator #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup generator ECMA Generator object built-in * @{ */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/alpha.cpp000664 001750 001750 00000013016 15164251010 034717 0ustar00ddennedyddennedy000000 000000 // Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Alpha-plane decompression. // // Author: Skal (pascal.massimino@gmail.com) #include "tvgCommon.h" #include "./alphai.h" #include "./vp8i.h" #include "./vp8li.h" #include "../dsp/dsp.h" #include "../utils/quant_levels_dec.h" #include "../utils/utils.h" #include "../webp/format_constants.h" //------------------------------------------------------------------------------ // ALPHDecoder object. ALPHDecoder* ALPHNew(void) { ALPHDecoder* const dec = tvg::calloc(1ULL, sizeof(*dec)); return dec; } void ALPHDelete(ALPHDecoder* const dec) { if (dec != NULL) { VP8LDelete(dec->vp8l_dec_); dec->vp8l_dec_ = NULL; tvg::free(dec); } } //------------------------------------------------------------------------------ // Decoding. // Initialize alpha decoding by parsing the alpha header and decoding the image // header for alpha data stored using lossless compression. // Returns false in case of error in alpha header (data too short, invalid // compression method or filter, error in lossless header data etc). static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, size_t data_size, int width, int height, uint8_t* output) { int ok = 0; const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN; const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN; int rsrv; assert(width > 0 && height > 0); assert(data != NULL && output != NULL); dec->width_ = width; dec->height_ = height; if (data_size <= ALPHA_HEADER_LEN) { return 0; } dec->method_ = (data[0] >> 0) & 0x03; dec->filter_ = static_cast((data[0] >> 2) & 0x03); dec->pre_processing_ = (data[0] >> 4) & 0x03; rsrv = (data[0] >> 6) & 0x03; if (dec->method_ < ALPHA_NO_COMPRESSION || dec->method_ > ALPHA_LOSSLESS_COMPRESSION || dec->filter_ >= WEBP_FILTER_LAST || dec->pre_processing_ > ALPHA_PREPROCESSED_LEVELS || rsrv != 0) { return 0; } if (dec->method_ == ALPHA_NO_COMPRESSION) { const size_t alpha_decoded_size = dec->width_ * dec->height_; ok = (alpha_data_size >= alpha_decoded_size); } else { assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION); ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size, output); } VP8FiltersInit(); return ok; } // Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha // starting from row number 'row'. It assumes that rows up to (row - 1) have // already been decoded. // Returns false in case of bitstream error. static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) { ALPHDecoder* const alph_dec = dec->alph_dec_; const int width = alph_dec->width_; const int height = alph_dec->height_; WebPUnfilterFunc unfilter_func = WebPUnfilters[alph_dec->filter_]; uint8_t* const output = dec->alpha_plane_; if (alph_dec->method_ == ALPHA_NO_COMPRESSION) { const size_t offset = row * width; const size_t num_pixels = num_rows * width; assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN + offset + num_pixels); memcpy(dec->alpha_plane_ + offset, dec->alpha_data_ + ALPHA_HEADER_LEN + offset, num_pixels); } else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION assert(alph_dec->vp8l_dec_ != NULL); if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) { return 0; } } if (unfilter_func != NULL) { unfilter_func(width, height, width, row, num_rows, output); } if (row + num_rows == dec->pic_hdr_.height_) { dec->is_alpha_decoded_ = 1; } return 1; } //------------------------------------------------------------------------------ // Main entry point. const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, int row, int num_rows) { const int width = dec->pic_hdr_.width_; const int height = dec->pic_hdr_.height_; if (row < 0 || num_rows <= 0 || row + num_rows > height) { return NULL; // sanity check. } if (row == 0) { // Initialize decoding. assert(dec->alpha_plane_ != NULL); dec->alph_dec_ = ALPHNew(); if (dec->alph_dec_ == NULL) return NULL; if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_, width, height, dec->alpha_plane_)) { ALPHDelete(dec->alph_dec_); dec->alph_dec_ = NULL; return NULL; } // if we allowed use of alpha dithering, check whether it's needed at all if (dec->alph_dec_->pre_processing_ != ALPHA_PREPROCESSED_LEVELS) { dec->alpha_dithering_ = 0; // disable dithering } else { num_rows = height; // decode everything in one pass } } if (!dec->is_alpha_decoded_) { int ok = 0; assert(dec->alph_dec_ != NULL); ok = ALPHDecode(dec, row, num_rows); if (ok && dec->alpha_dithering_ > 0) { ok = WebPDequantizeLevels(dec->alpha_plane_, width, height, dec->alpha_dithering_); } if (!ok || dec->is_alpha_decoded_) { ALPHDelete(dec->alph_dec_); dec->alph_dec_ = NULL; } if (!ok) return NULL; // Error. } // Return a pointer to the current decoded row. return dec->alpha_plane_ + row * width; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testInitializer.cpp000664 001750 001750 00000004463 15164251010 034072 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "config.h" #include "catch.hpp" #include using namespace tvg; TEST_CASE("Basic initialization", "[tvgInitializer]") { REQUIRE(Initializer::init() == Result::Success); REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Multiple initialization", "[tvgInitializer]") { REQUIRE(Initializer::init() == Result::Success); REQUIRE(Initializer::init() == Result::Success); REQUIRE(Initializer::term() == Result::Success); REQUIRE(Initializer::init() == Result::Success); REQUIRE(Initializer::term() == Result::Success); REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Version", "[tvgInitializer]") { REQUIRE(strcmp(Initializer::version(nullptr, nullptr, nullptr), THORVG_VERSION_STRING) == 0); uint32_t major, minor, micro; REQUIRE(strcmp(Initializer::version(&major, &minor, µ), THORVG_VERSION_STRING) == 0); char curVersion[10]; snprintf(curVersion, sizeof(curVersion), "%d.%d.%d", major, minor, micro); REQUIRE(strcmp(curVersion, THORVG_VERSION_STRING) == 0); } TEST_CASE("Negative termination", "[tvgInitializer]") { REQUIRE(Initializer::term() == Result::InsufficientCondition); }mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/jpg/tvgJpgLoader.cpp000664 001750 001750 00000006656 15164251010 035326 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgJpgLoader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ void JpgLoader::clear() { jpgdDelete(decoder); if (freeData) tvg::free(data); decoder = nullptr; data = nullptr; freeData = false; } void JpgLoader::run(unsigned tid) { surface.cs = ImageLoader::cs; surface.buf8 = jpgdDecompress(decoder, surface.cs); surface.stride = static_cast(w); surface.w = static_cast(w); surface.h = static_cast(h); surface.channelSize = sizeof(uint32_t); surface.premultiplied = true; clear(); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ JpgLoader::JpgLoader() : ImageLoader(FileType::Jpg) { } JpgLoader::~JpgLoader() { done(); clear(); tvg::free(surface.buf8); } bool JpgLoader::open(const char* path) { #ifdef THORVG_FILE_IO_SUPPORT int width, height; if (!(decoder = jpgdHeader(path, &width, &height))) return false; w = static_cast(width); h = static_cast(height); return true; #else return false; #endif } bool JpgLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy) { if (copy) { this->data = tvg::malloc(size); if (!this->data) return false; memcpy((char *)this->data, data, size); freeData = true; } else { this->data = (char *) data; freeData = false; } int width, height; decoder = jpgdHeader(this->data, size, &width, &height); if (!decoder) return false; w = static_cast(width); h = static_cast(height); return true; } bool JpgLoader::read() { if (!LoadModule::read()) return true; if (!decoder || w == 0 || h == 0) return false; TaskScheduler::request(this); return true; } bool JpgLoader::close() { if (!LoadModule::close()) return false; this->done(); return true; } RenderSurface* JpgLoader::bitmap() { this->done(); return ImageLoader::bitmap(); }mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/ttf/000775 001750 001750 00000000000 15164251010 032232 5ustar00ddennedyddennedy000000 000000 thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-async-generator-object.h000664 001750 001750 00000006521 15164251010 050034 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_ASYNC_GENERATOR_OBJECT_H #define ECMA_ASYNC_GENERATOR_OBJECT_H #include "ecma-globals.h" #include "vm-defines.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaasyncgeneratorobject ECMA AsyncGenerator object related routines * @{ */ /** * AsyncGenerator command types. */ typedef enum { ECMA_ASYNC_GENERATOR_DO_NEXT, /**< async generator next operation */ ECMA_ASYNC_GENERATOR_DO_THROW, /**< async generator throw operation */ ECMA_ASYNC_GENERATOR_DO_RETURN, /**< async generator return operation */ } ecma_async_generator_operation_type_t; /** * Get the state of an async yield iterator. */ #define ECMA_AWAIT_GET_STATE(async_generator_object_p) \ ((async_generator_object_p)->extended_object.u.cls.u2.executable_obj_flags >> ECMA_AWAIT_STATE_SHIFT) /** * Set the state of an async yield iterator. */ #define ECMA_AWAIT_SET_STATE(async_generator_object_p, to) \ do \ { \ uint16_t extra_info = (async_generator_object_p)->extended_object.u.cls.u2.executable_obj_flags; \ extra_info &= ((1 << ECMA_AWAIT_STATE_SHIFT) - 1); \ extra_info |= (ECMA_AWAIT_##to) << ECMA_AWAIT_STATE_SHIFT; \ (async_generator_object_p)->extended_object.u.cls.u2.executable_obj_flags = extra_info; \ } while (false) /** * Mask for clearing all ASYNC_AWAIT status bits */ #define ECMA_AWAIT_CLEAR_MASK (((1 << ECMA_AWAIT_STATE_SHIFT) - 1) - ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) /** * Helper macro for ECMA_AWAIT_CHANGE_STATE. */ #define ECMA_AWAIT_CS_HELPER(from, to) (((ECMA_AWAIT_##from) ^ (ECMA_AWAIT_##to)) << ECMA_AWAIT_STATE_SHIFT) /** * Change the state of an async yield iterator. */ #define ECMA_AWAIT_CHANGE_STATE(async_generator_object_p, from, to) \ ((async_generator_object_p)->extended_object.u.cls.u2.executable_obj_flags ^= ECMA_AWAIT_CS_HELPER (from, to)) ecma_value_t ecma_async_generator_enqueue (vm_executable_object_t *async_generator_object_p, ecma_async_generator_operation_type_t operation, ecma_value_t value); ecma_value_t ecma_async_generator_run (vm_executable_object_t *async_generator_object_p); void ecma_async_generator_finalize (vm_executable_object_t *async_generator_object_p, ecma_value_t value); ecma_value_t ecma_await_continue (vm_executable_object_t *async_generator_object_p, ecma_value_t value); /** * @} * @} */ #endif /* !ECMA_ASYNC_GENERATOR_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgLoader.cpp000664 001750 001750 00000030515 15164251010 034251 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgInlist.h" #include "tvgStr.h" #include "tvgLoader.h" #include "tvgLock.h" #ifdef THORVG_SVG_LOADER_SUPPORT #include "tvgSvgLoader.h" #endif #ifdef THORVG_PNG_LOADER_SUPPORT #include "tvgPngLoader.h" #endif #ifdef THORVG_JPG_LOADER_SUPPORT #include "tvgJpgLoader.h" #endif #ifdef THORVG_WEBP_LOADER_SUPPORT #include "tvgWebpLoader.h" #endif #ifdef THORVG_TTF_LOADER_SUPPORT #include "tvgTtfLoader.h" #endif #ifdef THORVG_LOTTIE_LOADER_SUPPORT #include "tvgLottieLoader.h" #endif #include "tvgRawLoader.h" uintptr_t HASH_KEY(const char* data) { return reinterpret_cast(data); } /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ //TODO: remove it. atomic ImageLoader::cs{ColorSpace::ARGB8888}; static Key _key; static Inlist _activeLoaders; static tvg::LoadModule* _find(FileType type) { switch(type) { case FileType::Png: { #ifdef THORVG_PNG_LOADER_SUPPORT return new PngLoader; #endif break; } case FileType::Jpg: { #ifdef THORVG_JPG_LOADER_SUPPORT return new JpgLoader; #endif break; } case FileType::Webp: { #ifdef THORVG_WEBP_LOADER_SUPPORT return new WebpLoader; #endif break; } case FileType::Svg: { #ifdef THORVG_SVG_LOADER_SUPPORT return new SvgLoader; #endif break; } case FileType::Ttf: { #ifdef THORVG_TTF_LOADER_SUPPORT return new TtfLoader; #endif break; } case FileType::Lot: { #ifdef THORVG_LOTTIE_LOADER_SUPPORT return new LottieLoader; #endif break; } case FileType::Raw: { return new RawLoader; break; } default: { break; } } #ifdef THORVG_LOG_ENABLED const char *format; switch(type) { case FileType::Svg: { format = "SVG"; break; } case FileType::Ttf: { format = "TTF"; break; } case FileType::Lot: { format = "LOT"; break; } case FileType::Raw: { format = "RAW"; break; } case FileType::Png: { format = "PNG"; break; } case FileType::Jpg: { format = "JPG"; break; } case FileType::Webp: { format = "WEBP"; break; } default: { format = "???"; break; } } TVGLOG("RENDERER", "%s format is not supported", format); #endif return nullptr; } #ifdef THORVG_FILE_IO_SUPPORT static tvg::LoadModule* _findByPath(const char* filename) { auto ext = fileext(filename); if (!ext) return nullptr; if (!strcmp(ext, "svg")) return _find(FileType::Svg); if (!strcmp(ext, "lot") || !strcmp(ext, "json")) return _find(FileType::Lot); if (!strcmp(ext, "png")) return _find(FileType::Png); if (!strcmp(ext, "jpg")) return _find(FileType::Jpg); if (!strcmp(ext, "webp")) return _find(FileType::Webp); if (!strcmp(ext, "ttf") || !strcmp(ext, "ttc")) return _find(FileType::Ttf); if (!strcmp(ext, "otf") || !strcmp(ext, "otc")) return _find(FileType::Ttf); return nullptr; } #endif static FileType _convert(const char* mimeType) { if (!mimeType) return FileType::Unknown; auto type = FileType::Unknown; if (!strcmp(mimeType, "svg") || !strcmp(mimeType, "svg+xml")) type = FileType::Svg; else if (!strcmp(mimeType, "ttf") || !strcmp(mimeType, "otf")) type = FileType::Ttf; else if (!strcmp(mimeType, "lot") || !strcmp(mimeType, "lottie+json")) type = FileType::Lot; else if (!strcmp(mimeType, "raw")) type = FileType::Raw; else if (!strcmp(mimeType, "png")) type = FileType::Png; else if (!strcmp(mimeType, "jpg") || !strcmp(mimeType, "jpeg")) type = FileType::Jpg; else if (!strcmp(mimeType, "webp")) type = FileType::Webp; else TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType); return type; } static tvg::LoadModule* _findByType(const char* mimeType) { return _find(_convert(mimeType)); } static tvg::LoadModule* _findFromCache(const char* filename) { ScopedLock lock(_key); INLIST_FOREACH(_activeLoaders, loader) { if (loader->cached && loader->hashpath && !strcmp(loader->hashpath, filename)) { ++loader->sharing; return loader; } } return nullptr; } static tvg::LoadModule* _findFromCache(const char* data, uint32_t size, const char* mimeType) { auto type = _convert(mimeType); if (type == FileType::Unknown) return nullptr; auto key = HASH_KEY(data); ScopedLock lock(_key); INLIST_FOREACH(_activeLoaders, loader) { if (loader->type == type && loader->hashkey == key) { ++loader->sharing; return loader; } } return nullptr; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool LoaderMgr::init() { return true; } bool LoaderMgr::term() { //clean up the remained font loaders which is globally used. INLIST_SAFE_FOREACH(_activeLoaders, loader) { if (loader->type != FileType::Ttf) continue; auto ret = loader->close(); _activeLoaders.remove(loader); if (ret) delete(loader); } return true; } bool LoaderMgr::retrieve(LoadModule* loader) { if (!loader) return false; if (loader->close()) { if (loader->cached) { _activeLoaders.remove(loader); } delete(loader); } return true; } tvg::LoadModule* LoaderMgr::loader(const char* filename, bool* invalid) { #ifdef THORVG_FILE_IO_SUPPORT *invalid = false; //TODO: make lottie sharable. auto allowCache = true; auto ext = fileext(filename); if (ext && (!strcmp(ext, "json") || !strcmp(ext, "lot"))) allowCache = false; if (allowCache) { if (auto loader = _findFromCache(filename)) return loader; } if (auto loader = _findByPath(filename)) { if (loader->open(filename)) { if (allowCache) { loader->cache(duplicate(filename)); { ScopedLock lock(_key); _activeLoaders.back(loader); } } return loader; } delete(loader); } //Unknown MimeType. Try with the candidates in the order for (int i = 0; i < static_cast(FileType::Raw); i++) { if (auto loader = _find(static_cast(i))) { if (loader->open(filename)) { if (allowCache) { loader->cache(duplicate(filename)); { ScopedLock lock(_key); _activeLoaders.back(loader); } } return loader; } delete(loader); } } *invalid = true; #endif return nullptr; } bool LoaderMgr::retrieve(const char* filename) { return retrieve(_findFromCache(filename)); } tvg::LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy) { //Note that users could use the same data pointer with the different content. //Thus caching is only valid for shareable. auto allowCache = !copy; //TODO: make lottie shareable. if (allowCache) { auto type = _convert(mimeType); if (type == FileType::Lot) allowCache = false; } if (allowCache) { if (auto loader = _findFromCache(data, size, mimeType)) return loader; } //Try with the given MimeType if (mimeType) { if (auto loader = _findByType(mimeType)) { if (loader->open(data, size, rpath, copy)) { if (allowCache) { loader->cache(HASH_KEY(data)); ScopedLock lock(_key); _activeLoaders.back(loader); } return loader; } else { TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported.", mimeType); delete(loader); } } } //Unknown MimeType. Try with the candidates in the order for (int i = 0; i < static_cast(FileType::Raw); i++) { auto loader = _find(static_cast(i)); if (loader) { if (loader->open(data, size, rpath, copy)) { if (allowCache) { loader->cache(HASH_KEY(data)); ScopedLock lock(_key); _activeLoaders.back(loader); } return loader; } delete(loader); } } return nullptr; } tvg::LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, ColorSpace cs, bool copy) { //Note that users could use the same data pointer with the different content. //Thus caching is only valid for shareable. if (!copy) { //TODO: should we check premultiplied?? if (auto loader = _findFromCache((const char*)(data), w * h, "raw")) return loader; } //function is dedicated for raw images only auto loader = new RawLoader; if (loader->open(data, w, h, cs, copy)) { if (!copy) { loader->cache(HASH_KEY((const char*)data)); ScopedLock lock(_key); _activeLoaders.back(loader); } return loader; } delete(loader); return nullptr; } //loads fonts from memory - loader is cached (regardless of copy value) in order to access it while setting font tvg::LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size, TVG_UNUSED const char* mimeType, bool copy) { #ifdef THORVG_TTF_LOADER_SUPPORT //TODO: add check for mimetype ? if (auto loader = font(name)) return loader; //function is dedicated for ttf loader (the only supported font loader) auto loader = new TtfLoader; if (loader->open(data, size, "", copy)) { loader->name = duplicate(name); loader->cached = true; //force it. ScopedLock lock(_key); _activeLoaders.back(loader); return loader; } TVGLOG("LOADER", "The font data \"%s\" could not be loaded.", name); delete(loader); #endif return nullptr; } tvg::LoadModule* LoaderMgr::font(const char* name) { ScopedLock lock(_key); INLIST_FOREACH(_activeLoaders, loader) { if (loader->type != FileType::Ttf) continue; if (loader->cached && tvg::equal(name, static_cast(loader)->name)) { ++loader->sharing; return loader; } } return nullptr; } tvg::LoadModule* LoaderMgr::anyfont() { ScopedLock lock(_key); INLIST_FOREACH(_activeLoaders, loader) { if (loader->cached && loader->type == FileType::Ttf) { ++loader->sharing; return loader; } } return nullptr; } src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h000664 001750 001750 00000014753 15164251010 037007 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifdef THORVG_NEON_VECTOR_SUPPORT #include //TODO : need to support windows ARM #if defined(__ARM_64BIT_STATE) || defined(_M_ARM64) #define TVG_AARCH64 1 #else #define TVG_AARCH64 0 #endif static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a) { uint16x8_t t = vmull_u8(c, a); return vshrn_n_u16(t, 8); } static void neonRasterGrayscale8(uint8_t* dst, uint8_t val, uint32_t offset, int32_t len) { dst += offset; int32_t i = 0; const uint8x16_t valVec = vdupq_n_u8(val); #if TVG_AARCH64 uint8x16x4_t valQuad = {valVec, valVec, valVec, valVec}; for (; i <= len - 16 * 4; i += 16 * 4) { vst1q_u8_x4(dst + i, valQuad); } #else for (; i <= len - 16; i += 16) { vst1q_u8(dst + i, valVec); } #endif for (; i < len; i++) { dst[i] = val; } } static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) { dst += offset; uint32x4_t vectorVal = vdupq_n_u32(val); #if TVG_AARCH64 uint32_t iterations = len / 16; uint32_t neonFilled = iterations * 16; uint32x4x4_t valQuad = {vectorVal, vectorVal, vectorVal, vectorVal}; for (uint32_t i = 0; i < iterations; ++i) { vst4q_u32(dst, valQuad); dst += 16; } #else uint32_t iterations = len / 4; uint32_t neonFilled = iterations * 4; for (uint32_t i = 0; i < iterations; ++i) { vst1q_u32(dst, vectorVal); dst += 4; } #endif int32_t leftovers = len - neonFilled; while (leftovers--) *dst++ = val; } static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { const SwSpan* end; int32_t x, len; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, c.a); uint32_t src; uint8x8_t *vDst = nullptr; int32_t align; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); else src = color; auto dst = &surface->buf32[span->y * surface->stride + x]; auto ialpha = IA(src); if ((((uintptr_t) dst) & 0x7) != 0) { //fill not aligned byte *dst = src + ALPHA_BLEND(*dst, ialpha); vDst = (uint8x8_t*)(dst + 1); align = 1; } else { vDst = (uint8x8_t*) dst; align = 0; } uint8x8_t vSrc = (uint8x8_t) vdup_n_u32(src); uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha); for (int32_t x = 0; x < (len - align) / 2; ++x) vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha)); auto leftovers = (len - align) % 2; if (leftovers > 0) dst[len - 1] = src + ALPHA_BLEND(dst[len - 1], ialpha); } //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize); uint8_t src; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf8[span->y * surface->stride + x]; if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); else src = c.a; auto ialpha = ~c.a; for (auto x = 0; x < len; ++x, ++dst) { *dst = src + MULTIPLY(*dst, ialpha); } } } return true; } static bool neonRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { auto h = bbox.h(); auto w = bbox.w(); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; auto ialpha = 255 - c.a; auto vColor = vdup_n_u32(color); auto vIalpha = vdup_n_u8((uint8_t) ialpha); uint8x8_t* vDst = nullptr; uint32_t align; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; if ((((uintptr_t) dst) & 0x7) != 0) { //fill not aligned byte *dst = color + ALPHA_BLEND(*dst, ialpha); vDst = (uint8x8_t*) (dst + 1); align = 1; } else { vDst = (uint8x8_t*) dst; align = 0; } for (uint32_t x = 0; x < (w - align) / 2; ++x) vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha)); auto leftovers = (w - align) % 2; if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha); } //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize); auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x; auto ialpha = ~c.a; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < w; ++x, ++dst) { *dst = c.a + MULTIPLY(*dst, ialpha); } } } return true; } #endif lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int8array-prototype.inc.h000664 001750 001750 00000001614 15164251010 054763 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Int8Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 1 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_INT8ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-conversion.h000664 001750 001750 00000005513 15164251010 045654 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_CONVERSION_H #define ECMA_CONVERSION_H #include "ecma-builtins.h" #include "ecma-globals.h" #include "ecma-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaconversion ECMA conversion routines * @{ */ /** * Second argument of 'ToPrimitive' operation that is a hint, * specifying the preferred type of conversion result. */ typedef enum { ECMA_PREFERRED_TYPE_NO = 0, /**< no preferred type is specified */ ECMA_PREFERRED_TYPE_NUMBER, /**< Number */ ECMA_PREFERRED_TYPE_STRING /**< String */ } ecma_preferred_type_hint_t; /** * Option bits for ecma_op_to_numeric. */ typedef enum { ECMA_TO_NUMERIC_NO_OPTS = 0, /**< no options (same as toNumber operation) */ ECMA_TO_NUMERIC_ALLOW_BIGINT = (1 << 0), /**< allow BigInt values (ignored if BigInts are disabled) */ } ecma_to_numeric_options_t; bool ecma_op_require_object_coercible (ecma_value_t value); bool ecma_op_same_value (ecma_value_t x, ecma_value_t y); #if JERRY_BUILTIN_CONTAINER bool ecma_op_same_value_zero (ecma_value_t x, ecma_value_t y, bool strict_equality); #endif /* JERRY_BUILTIN_CONTAINER */ ecma_value_t ecma_op_to_primitive (ecma_value_t value, ecma_preferred_type_hint_t preferred_type); bool ecma_op_to_boolean (ecma_value_t value); ecma_value_t ecma_op_to_number (ecma_value_t value, ecma_number_t *number_p); ecma_value_t ecma_op_to_numeric (ecma_value_t value, ecma_number_t *number_p, ecma_to_numeric_options_t options); ecma_string_t *ecma_op_to_string (ecma_value_t value); ecma_string_t *ecma_op_to_property_key (ecma_value_t value); ecma_value_t ecma_op_to_object (ecma_value_t value); bool ecma_op_is_integer (ecma_number_t value); ecma_value_t ecma_op_to_integer (ecma_value_t value, ecma_number_t *number_p); ecma_value_t ecma_op_to_length (ecma_value_t value, ecma_length_t *length); ecma_value_t ecma_op_to_index (ecma_value_t value, ecma_number_t *index); ecma_collection_t *ecma_op_create_list_from_array_like (ecma_value_t arr, bool prop_names_only); ecma_object_t *ecma_op_from_property_descriptor (const ecma_property_descriptor_t *src_prop_desc_p); ecma_value_t ecma_op_to_property_descriptor (ecma_value_t obj_value, ecma_property_descriptor_t *out_prop_desc_p); /** * @} * @} */ #endif /* !ECMA_CONVERSION_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function.inc.h000664 001750 001750 00000002307 15164251010 050440 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Function built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.3.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, ECMA_PROPERTY_FIXED) /* Number properties: * (property name, object pointer getter) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_FUNCTION_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.cpp000664 001750 001750 00000052457 15164251010 051371 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #if JERRY_BUILTIN_DATE #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * Checks whether the function uses UTC time zone. */ #define BUILTIN_DATE_FUNCTION_IS_UTC(builtin_routine_id) \ (((builtin_routine_id) -ECMA_DATE_PROTOTYPE_GET_FULL_YEAR) & 0x1) /** * List of built-in routine identifiers. */ enum { ECMA_DATE_PROTOTYPE_ROUTINE_START = 0, ECMA_DATE_PROTOTYPE_GET_FULL_YEAR, /* ECMA-262 v5 15.9.5.10 */ ECMA_DATE_PROTOTYPE_GET_UTC_FULL_YEAR, /* ECMA-262 v5 15.9.5.11 */ #if JERRY_BUILTIN_ANNEXB ECMA_DATE_PROTOTYPE_GET_YEAR, /* ECMA-262 v5, AnnexB.B.2.4 */ ECMA_DATE_PROTOTYPE_GET_UTC_YEAR, /* has no UTC variant */ #endif /* JERRY_BUILTIN_ANNEXB */ ECMA_DATE_PROTOTYPE_GET_MONTH, /* ECMA-262 v5 15.9.5.12 */ ECMA_DATE_PROTOTYPE_GET_UTC_MONTH, /* ECMA-262 v5 15.9.5.13 */ ECMA_DATE_PROTOTYPE_GET_DATE, /* ECMA-262 v5 15.9.5.14 */ ECMA_DATE_PROTOTYPE_GET_UTC_DATE, /* ECMA-262 v5 15.9.5.15 */ ECMA_DATE_PROTOTYPE_GET_DAY, /* ECMA-262 v5 15.9.5.16 */ ECMA_DATE_PROTOTYPE_GET_UTC_DAY, /* ECMA-262 v5 15.9.5.17 */ ECMA_DATE_PROTOTYPE_GET_HOURS, /* ECMA-262 v5 15.9.5.18 */ ECMA_DATE_PROTOTYPE_GET_UTC_HOURS, /* ECMA-262 v5 15.9.5.19 */ ECMA_DATE_PROTOTYPE_GET_MINUTES, /* ECMA-262 v5 15.9.5.20 */ ECMA_DATE_PROTOTYPE_GET_UTC_MINUTES, /* ECMA-262 v5 15.9.5.21 */ ECMA_DATE_PROTOTYPE_GET_SECONDS, /* ECMA-262 v5 15.9.5.22 */ ECMA_DATE_PROTOTYPE_GET_UTC_SECONDS, /* ECMA-262 v5 15.9.5.23 */ ECMA_DATE_PROTOTYPE_GET_MILLISECONDS, /* ECMA-262 v5 15.9.5.24 */ ECMA_DATE_PROTOTYPE_GET_UTC_MILLISECONDS, /* ECMA-262 v5 15.9.5.25 */ ECMA_DATE_PROTOTYPE_GET_TIMEZONE_OFFSET, /* has no local time zone variant */ ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET, /* ECMA-262 v5 15.9.5.26 */ ECMA_DATE_PROTOTYPE_SET_FULL_YEAR, /* ECMA-262 v5, 15.9.5.40 */ ECMA_DATE_PROTOTYPE_SET_UTC_FULL_YEAR, /* ECMA-262 v5, 15.9.5.41 */ #if JERRY_BUILTIN_ANNEXB ECMA_DATE_PROTOTYPE_SET_YEAR, /* ECMA-262 v5, ECMA-262 v5, AnnexB.B.2.5 */ ECMA_DATE_PROTOTYPE_SET_UTC_YEAR, /* has no UTC variant */ #endif /* JERRY_BUILTIN_ANNEXB */ ECMA_DATE_PROTOTYPE_SET_MONTH, /* ECMA-262 v5, 15.9.5.38 */ ECMA_DATE_PROTOTYPE_SET_UTC_MONTH, /* ECMA-262 v5, 15.9.5.39 */ ECMA_DATE_PROTOTYPE_SET_DATE, /* ECMA-262 v5, 15.9.5.36 */ ECMA_DATE_PROTOTYPE_SET_UTC_DATE, /* ECMA-262 v5, 15.9.5.37 */ ECMA_DATE_PROTOTYPE_SET_HOURS, /* ECMA-262 v5, 15.9.5.34 */ ECMA_DATE_PROTOTYPE_SET_UTC_HOURS, /* ECMA-262 v5, 15.9.5.35 */ ECMA_DATE_PROTOTYPE_SET_MINUTES, /* ECMA-262 v5, 15.9.5.32 */ ECMA_DATE_PROTOTYPE_SET_UTC_MINUTES, /* ECMA-262 v5, 15.9.5.33 */ ECMA_DATE_PROTOTYPE_SET_SECONDS, /* ECMA-262 v5, 15.9.5.30 */ ECMA_DATE_PROTOTYPE_SET_UTC_SECONDS, /* ECMA-262 v5, 15.9.5.31 */ ECMA_DATE_PROTOTYPE_SET_MILLISECONDS, /* ECMA-262 v5, 15.9.5.28 */ ECMA_DATE_PROTOTYPE_SET_UTC_MILLISECONDS, /* ECMA-262 v5, 15.9.5.29 */ ECMA_DATE_PROTOTYPE_TO_STRING, /* ECMA-262 v5, 15.9.5.2 */ ECMA_DATE_PROTOTYPE_TO_DATE_STRING, /* ECMA-262 v5, 15.9.5.3 */ ECMA_DATE_PROTOTYPE_TO_TIME_STRING, /* ECMA-262 v5, 15.9.5.4 */ ECMA_DATE_PROTOTYPE_TO_ISO_STRING, /* ECMA-262 v5, 15.9.5.43 */ ECMA_DATE_PROTOTYPE_GET_TIME, /* ECMA-262 v5, 15.9.5.9 */ ECMA_DATE_PROTOTYPE_SET_TIME, /* ECMA-262 v5, 15.9.5.27 */ ECMA_DATE_PROTOTYPE_TO_JSON, /* ECMA-262 v5, 15.9.5.44 */ ECMA_DATE_PROTOTYPE_TO_PRIMITIVE, /* ECMA-262 v6 20.3.4.45 */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-date-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID date_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup dateprototype ECMA Date.prototype object built-in * @{ */ /** * The Date.prototype object's 'toJSON' routine * * See also: * ECMA-262 v5, 15.9.5.44 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_date_prototype_to_json (ecma_value_t this_arg) /**< this argument */ { /* 1. */ ecma_value_t obj = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (obj)) { return obj; } /* 2. */ ecma_value_t tv = ecma_op_to_primitive (obj, ECMA_PREFERRED_TYPE_NUMBER); if (ECMA_IS_VALUE_ERROR (tv)) { ecma_free_value (obj); return tv; } /* 3. */ if (ecma_is_value_number (tv)) { ecma_number_t num_value = ecma_get_number_from_value (tv); ecma_free_value (tv); if (ecma_number_is_nan (num_value) || ecma_number_is_infinity (num_value)) { ecma_free_value (obj); return ECMA_VALUE_NULL; } } else { ecma_free_value (tv); } ecma_object_t *value_obj_p = ecma_get_object_from_value (obj); /* 4. */ ecma_value_t ret_value = ecma_op_invoke_by_magic_id (obj, LIT_MAGIC_STRING_TO_ISO_STRING_UL, NULL, 0); ecma_deref_object (value_obj_p); return ret_value; } /* ecma_builtin_date_prototype_to_json */ /** * The Date.prototype object's toPrimitive routine * * See also: * ECMA-262 v6, 20.3.4.45 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_date_prototype_to_primitive (ecma_value_t this_arg, /**< this argument */ ecma_value_t hint_arg) /**< {"default", "number", "string"} */ { if (ecma_is_value_object (this_arg) && ecma_is_value_string (hint_arg)) { ecma_string_t *hint_str_p = ecma_get_string_from_value (hint_arg); ecma_preferred_type_hint_t hint = ECMA_PREFERRED_TYPE_NO; if (hint_str_p == ecma_get_magic_string (LIT_MAGIC_STRING_STRING) || hint_str_p == ecma_get_magic_string (LIT_MAGIC_STRING_DEFAULT)) { hint = ECMA_PREFERRED_TYPE_STRING; } else if (hint_str_p == ecma_get_magic_string (LIT_MAGIC_STRING_NUMBER)) { hint = ECMA_PREFERRED_TYPE_NUMBER; } if (hint != ECMA_PREFERRED_TYPE_NO) { return ecma_op_general_object_ordinary_value (ecma_get_object_from_value (this_arg), hint); } } return ecma_raise_type_error (ECMA_ERR_INVALID_ARGUMENT_TYPE_IN_TOPRIMITIVE); } /* ecma_builtin_date_prototype_to_primitive */ /** * Dispatch get date functions * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_date_prototype_dispatch_get (uint16_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_number_t date_value) /**< date converted to number */ { if (ecma_number_is_nan (date_value)) { return ecma_make_nan_value (); } int32_t result; switch (builtin_routine_id) { case ECMA_DATE_PROTOTYPE_GET_FULL_YEAR: case ECMA_DATE_PROTOTYPE_GET_UTC_FULL_YEAR: { result = ecma_date_year_from_time (date_value); break; } #if JERRY_BUILTIN_ANNEXB case ECMA_DATE_PROTOTYPE_GET_YEAR: { result = (ecma_date_year_from_time (date_value) - 1900); break; } #endif /* JERRY_BUILTIN_ANNEXB */ case ECMA_DATE_PROTOTYPE_GET_MONTH: case ECMA_DATE_PROTOTYPE_GET_UTC_MONTH: { result = ecma_date_month_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_DATE: case ECMA_DATE_PROTOTYPE_GET_UTC_DATE: { result = ecma_date_date_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_DAY: case ECMA_DATE_PROTOTYPE_GET_UTC_DAY: { result = ecma_date_week_day (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_HOURS: case ECMA_DATE_PROTOTYPE_GET_UTC_HOURS: { result = ecma_date_hour_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_MINUTES: case ECMA_DATE_PROTOTYPE_GET_UTC_MINUTES: { result = ecma_date_min_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_SECONDS: case ECMA_DATE_PROTOTYPE_GET_UTC_SECONDS: { result = ecma_date_sec_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_MILLISECONDS: case ECMA_DATE_PROTOTYPE_GET_UTC_MILLISECONDS: { result = ecma_date_ms_from_time (date_value); break; } default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET); result = -ecma_date_local_time_zone_adjustment (date_value) / ECMA_DATE_MS_PER_MINUTE; break; } } return ecma_make_int32_value (result); } /* ecma_builtin_date_prototype_dispatch_get */ #if JERRY_BUILTIN_ANNEXB /** * Returns true, if the built-in id sets a year. */ #define ECMA_DATE_PROTOTYPE_IS_SET_YEAR_ROUTINE(builtin_routine_id) \ ((builtin_routine_id) == ECMA_DATE_PROTOTYPE_SET_FULL_YEAR \ || (builtin_routine_id) == ECMA_DATE_PROTOTYPE_SET_UTC_FULL_YEAR \ || (builtin_routine_id) == ECMA_DATE_PROTOTYPE_SET_YEAR) #else /* !JERRY_BUILTIN_ANNEXB */ /** * Returns true, if the built-in id sets a year. */ #define ECMA_DATE_PROTOTYPE_IS_SET_YEAR_ROUTINE(builtin_routine_id) \ ((builtin_routine_id) == ECMA_DATE_PROTOTYPE_SET_FULL_YEAR \ || (builtin_routine_id) == ECMA_DATE_PROTOTYPE_SET_UTC_FULL_YEAR) #endif /* JERRY_BUILTIN_ANNEXB */ /** * Dispatch set date functions * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_date_prototype_dispatch_set (uint16_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_object_t *object_p, /**< date object */ const ecma_value_t arguments_list[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { ecma_number_t converted_number[4]; uint32_t conversions = 0; /* If the first argument is not specified, it is always converted to NaN. */ converted_number[0] = ecma_number_make_nan (); switch (builtin_routine_id) { #if JERRY_BUILTIN_ANNEXB case ECMA_DATE_PROTOTYPE_SET_YEAR: #endif /* JERRY_BUILTIN_ANNEXB */ case ECMA_DATE_PROTOTYPE_SET_DATE: case ECMA_DATE_PROTOTYPE_SET_UTC_DATE: case ECMA_DATE_PROTOTYPE_SET_UTC_MILLISECONDS: case ECMA_DATE_PROTOTYPE_SET_MILLISECONDS: { conversions = 1; break; } case ECMA_DATE_PROTOTYPE_SET_MONTH: case ECMA_DATE_PROTOTYPE_SET_UTC_MONTH: case ECMA_DATE_PROTOTYPE_SET_UTC_SECONDS: case ECMA_DATE_PROTOTYPE_SET_SECONDS: { conversions = 2; break; } case ECMA_DATE_PROTOTYPE_SET_FULL_YEAR: case ECMA_DATE_PROTOTYPE_SET_UTC_FULL_YEAR: case ECMA_DATE_PROTOTYPE_SET_MINUTES: case ECMA_DATE_PROTOTYPE_SET_UTC_MINUTES: { conversions = 3; break; } default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_HOURS || builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_UTC_HOURS); conversions = 4; break; } } if (conversions > arguments_number) { conversions = arguments_number; } for (uint32_t i = 0; i < conversions; i++) { ecma_value_t value = ecma_op_to_number (arguments_list[i], &converted_number[i]); if (ECMA_IS_VALUE_ERROR (value)) { return value; } } ecma_date_object_t *date_object_p = (ecma_date_object_t *) object_p; ecma_number_t *date_value_p = &date_object_p->date_value; ecma_number_t date_value = *date_value_p; if (!BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id)) { ecma_number_t local_tza; if (date_object_p->header.u.cls.u1.date_flags & ECMA_DATE_TZA_SET) { local_tza = date_object_p->header.u.cls.u3.tza; JERRY_ASSERT (local_tza == ecma_date_local_time_zone_adjustment (date_value)); } else { local_tza = ecma_date_local_time_zone_adjustment (date_value); } date_value += local_tza; } ecma_number_t day_part; ecma_number_t time_part; if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_SET_UTC_DATE) { if (ecma_number_is_nan (date_value)) { if (!ECMA_DATE_PROTOTYPE_IS_SET_YEAR_ROUTINE (builtin_routine_id)) { return ecma_make_number_value (date_value); } date_value = ECMA_NUMBER_ZERO; } time_part = ecma_date_time_in_day_from_time (date_value); ecma_number_t year = ecma_date_year_from_time (date_value); ecma_number_t month = ecma_date_month_from_time (date_value); ecma_number_t day = ecma_date_date_from_time (date_value); switch (builtin_routine_id) { case ECMA_DATE_PROTOTYPE_SET_FULL_YEAR: case ECMA_DATE_PROTOTYPE_SET_UTC_FULL_YEAR: { year = converted_number[0]; if (conversions >= 2) { month = converted_number[1]; } if (conversions >= 3) { day = converted_number[2]; } break; } #if JERRY_BUILTIN_ANNEXB case ECMA_DATE_PROTOTYPE_SET_YEAR: { if (ecma_number_is_nan (converted_number[0])) { *date_value_p = converted_number[0]; date_object_p->header.u.cls.u1.date_flags &= (uint8_t) ~ECMA_DATE_TZA_SET; return ecma_make_number_value (converted_number[0]); } year = ecma_number_trunc (converted_number[0]); if (year >= 0 && year <= 99) { year += 1900; } break; } #endif /* JERRY_BUILTIN_ANNEXB */ case ECMA_DATE_PROTOTYPE_SET_MONTH: case ECMA_DATE_PROTOTYPE_SET_UTC_MONTH: { month = converted_number[0]; if (conversions >= 2) { day = converted_number[1]; } break; } default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_DATE || builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_UTC_DATE); day = converted_number[0]; break; } } day_part = ecma_date_make_day (year, month, day); #if JERRY_BUILTIN_ANNEXB if (builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_YEAR) { if (ecma_number_is_nan (converted_number[0])) { day_part = 0; time_part = converted_number[0]; } } #endif /* JERRY_BUILTIN_ANNEXB */ } else { if (ecma_number_is_nan (date_value)) { return ecma_make_number_value (date_value); } day_part = ecma_date_day_from_time (date_value) * (ecma_number_t) ECMA_DATE_MS_PER_DAY; ecma_number_t hour = ecma_date_hour_from_time (date_value); ecma_number_t min = ecma_date_min_from_time (date_value); ecma_number_t sec = ecma_date_sec_from_time (date_value); ecma_number_t ms = ecma_date_ms_from_time (date_value); switch (builtin_routine_id) { case ECMA_DATE_PROTOTYPE_SET_HOURS: case ECMA_DATE_PROTOTYPE_SET_UTC_HOURS: { hour = converted_number[0]; if (conversions >= 2) { min = converted_number[1]; } if (conversions >= 3) { sec = converted_number[2]; } if (conversions >= 4) { ms = converted_number[3]; } break; } case ECMA_DATE_PROTOTYPE_SET_MINUTES: case ECMA_DATE_PROTOTYPE_SET_UTC_MINUTES: { min = converted_number[0]; if (conversions >= 2) { sec = converted_number[1]; } if (conversions >= 3) { ms = converted_number[2]; } break; } case ECMA_DATE_PROTOTYPE_SET_UTC_SECONDS: case ECMA_DATE_PROTOTYPE_SET_SECONDS: { sec = converted_number[0]; if (conversions >= 2) { ms = converted_number[1]; } break; } default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_UTC_MILLISECONDS || builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_MILLISECONDS); ms = converted_number[0]; break; } } time_part = ecma_date_make_time (hour, min, sec, ms); } bool is_utc = BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id); ecma_number_t full_date = ecma_date_make_date (day_part, time_part); if (!is_utc) { full_date = ecma_date_utc (full_date); } full_date = ecma_date_time_clip (full_date); *date_value_p = full_date; date_object_p->header.u.cls.u1.date_flags &= (uint8_t) ~ECMA_DATE_TZA_SET; return ecma_make_number_value (full_date); } /* ecma_builtin_date_prototype_dispatch_set */ #undef ECMA_DATE_PROTOTYPE_IS_SET_YEAR_ROUTINE /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_date_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { if (JERRY_UNLIKELY (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_JSON)) { return ecma_builtin_date_prototype_to_json (this_arg); } if (JERRY_UNLIKELY (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_PRIMITIVE)) { return ecma_builtin_date_prototype_to_primitive (this_arg, arguments_list[0]); } if (!ecma_is_value_object (this_arg) || !ecma_object_class_is (ecma_get_object_from_value (this_arg), ECMA_OBJECT_CLASS_DATE)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_DATE_OBJECT); } ecma_object_t *this_obj_p = ecma_get_object_from_value (this_arg); ecma_date_object_t *date_object_p = (ecma_date_object_t *) this_obj_p; ecma_number_t *date_value_p = &date_object_p->date_value; ecma_number_t date_value = *date_value_p; if (builtin_routine_id == ECMA_DATE_PROTOTYPE_GET_TIME) { return ecma_make_number_value (date_value); } if (builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_TIME) { ecma_number_t time_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (arguments_list[0], &time_num))) { return ECMA_VALUE_ERROR; } *date_value_p = ecma_date_time_clip (time_num); date_object_p->header.u.cls.u1.date_flags &= (uint8_t) ~ECMA_DATE_TZA_SET; return ecma_make_number_value (*date_value_p); } if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_SET_UTC_MILLISECONDS) { if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET) { if (!BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id)) { ecma_number_t local_tza; if (date_object_p->header.u.cls.u1.date_flags & ECMA_DATE_TZA_SET) { local_tza = date_object_p->header.u.cls.u3.tza; JERRY_ASSERT (local_tza == ecma_date_local_time_zone_adjustment (date_value)); } else { local_tza = ecma_date_local_time_zone_adjustment (date_value); JERRY_ASSERT (local_tza <= INT32_MAX && local_tza >= INT32_MIN); date_object_p->header.u.cls.u3.tza = (int32_t) local_tza; date_object_p->header.u.cls.u1.date_flags |= ECMA_DATE_TZA_SET; } date_value += local_tza; } return ecma_builtin_date_prototype_dispatch_get (builtin_routine_id, date_value); } return ecma_builtin_date_prototype_dispatch_set (builtin_routine_id, this_obj_p, arguments_list, arguments_number); } if (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_ISO_STRING) { if (ecma_number_is_nan (date_value)) { return ecma_raise_range_error (ECMA_ERR_DATE_MUST_BE_A_FINITE_NUMBER); } return ecma_date_value_to_iso_string (date_value); } if (ecma_number_is_nan (date_value)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_INVALID_DATE_UL); } switch (builtin_routine_id) { case ECMA_DATE_PROTOTYPE_TO_STRING: { return ecma_date_value_to_string (date_value); } case ECMA_DATE_PROTOTYPE_TO_DATE_STRING: { return ecma_date_value_to_date_string (date_value); } default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_TIME_STRING); return ecma_date_value_to_time_string (date_value); } } } /* ecma_builtin_date_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_DATE */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-reflect.inc.h000664 001750 001750 00000003742 15164251010 050243 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_REFLECT /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_APPLY, ECMA_REFLECT_OBJECT_APPLY, 3, 3) ROUTINE (LIT_MAGIC_STRING_DEFINE_PROPERTY_UL, ECMA_REFLECT_OBJECT_DEFINE_PROPERTY, 3, 3) ROUTINE (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL, ECMA_REFLECT_OBJECT_GET_OWN_PROPERTY_DESCRIPTOR, 2, 2) ROUTINE (LIT_MAGIC_STRING_GET, ECMA_REFLECT_OBJECT_GET, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_SET, ECMA_REFLECT_OBJECT_SET, NON_FIXED, 3) ROUTINE (LIT_MAGIC_STRING_HAS, ECMA_REFLECT_OBJECT_HAS, 2, 2) ROUTINE (LIT_MAGIC_STRING_DELETE_PROPERTY_UL, ECMA_REFLECT_OBJECT_DELETE_PROPERTY, 2, 2) ROUTINE (LIT_MAGIC_STRING_OWN_KEYS_UL, ECMA_REFLECT_OBJECT_OWN_KEYS, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_CONSTRUCT, ECMA_REFLECT_OBJECT_CONSTRUCT, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_GET_PROTOTYPE_OF_UL, ECMA_REFLECT_OBJECT_GET_PROTOTYPE_OF, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_EXTENSIBLE, ECMA_REFLECT_OBJECT_IS_EXTENSIBLE, 1, 1) ROUTINE (LIT_MAGIC_STRING_PREVENT_EXTENSIONS_UL, ECMA_REFLECT_OBJECT_PREVENT_EXTENSIONS, 1, 1) ROUTINE (LIT_MAGIC_STRING_SET_PROTOTYPE_OF_UL, ECMA_REFLECT_OBJECT_SET_PROTOTYPE_OF, 2, 2) #endif /* JERRY_BUILTIN_REFLECT */ #include "ecma-builtin-helpers-macro-undefs.inc.h" glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser.h000664 001750 001750 00000002041 15164251010 043303 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JS_PARSER_H #define JS_PARSER_H #include "ecma-globals.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_parser Parser * @{ */ /* Note: source must be a valid UTF-8 string */ ecma_compiled_code_t *parser_parse_script (void *source_p, uint32_t parse_opts, const jerry_parse_options_t *options_p); /** * @} * @} * @} */ #endif /* !JS_PARSER_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.h000664 001750 001750 00000002511 15164251010 050502 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_SHARED_ARRAYBUFFER_OBJECT_H #define ECMA_SHARED_ARRAYBUFFER_OBJECT_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmasharedarraybufferobject ECMA SharedArrayBuffer object related routines * @{ */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER ecma_value_t ecma_op_create_shared_arraybuffer_object (const ecma_value_t *, uint32_t); /** * Helper functions for SharedArrayBuffer. */ ecma_object_t *ecma_shared_arraybuffer_new_object (uint32_t length); #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ bool ecma_is_shared_arraybuffer (ecma_value_t val); bool ecma_object_is_shared_arraybuffer (ecma_object_t *val); /** * @} * @} */ #endif /* !ECMA_SHARED_ARRAYBUFFER_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/random.h000664 001750 001750 00000004075 15164251010 035171 0ustar00ddennedyddennedy000000 000000 // Copyright 2013 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Pseudo-random utilities // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_UTILS_RANDOM_H_ #define WEBP_UTILS_RANDOM_H_ #include #include "../webp/types.h" #ifdef __cplusplus extern "C" { #endif #define VP8_RANDOM_DITHER_FIX 8 // fixed-point precision for dithering #define VP8_RANDOM_TABLE_SIZE 55 typedef struct { int index1_, index2_; uint32_t tab_[VP8_RANDOM_TABLE_SIZE]; int amp_; } VP8Random; // Initializes random generator with an amplitude 'dithering' in range [0..1]. void VP8InitRandom(VP8Random* const rg, float dithering); // Returns a centered pseudo-random number with 'num_bits' amplitude. // (uses D.Knuth's Difference-based random generator). // 'amp' is in VP8_RANDOM_DITHER_FIX fixed-point precision. static WEBP_INLINE int VP8RandomBits2(VP8Random* const rg, int num_bits, int amp) { int diff; assert(num_bits + VP8_RANDOM_DITHER_FIX <= 31); diff = rg->tab_[rg->index1_] - rg->tab_[rg->index2_]; if (diff < 0) diff += (1u << 31); rg->tab_[rg->index1_] = diff; if (++rg->index1_ == VP8_RANDOM_TABLE_SIZE) rg->index1_ = 0; if (++rg->index2_ == VP8_RANDOM_TABLE_SIZE) rg->index2_ = 0; // sign-extend, 0-center diff = (int)((uint32_t)diff << 1) >> (32 - num_bits); diff = (diff * amp) >> VP8_RANDOM_DITHER_FIX; // restrict range diff += 1 << (num_bits - 1); // shift back to 0.5-center return diff; } static WEBP_INLINE int VP8RandomBits(VP8Random* const rg, int num_bits) { return VP8RandomBits2(rg, num_bits, rg->amp_); } #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_UTILS_RANDOM_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp000664 001750 001750 00000024732 15164251010 035643 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2022 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgStr.h" #include "tvgSvgCssStyle.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static inline bool _isImportanceApplicable(const SvgStyleFlags& toFlagsImportance, SvgStyleFlags fromFlagsImportance, SvgStyleFlags flag) { return !(toFlagsImportance & flag) && (fromFlagsImportance & flag); } static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from, bool overwrite) { if (!from) return; //Copy the properties of 'from' only if they were explicitly set (not the default ones). if ((from->curColorSet && (overwrite || !(to->flags & SvgStyleFlags::Color))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Color)) { to->color = from->color; to->curColorSet = true; to->flags |= SvgStyleFlags::Color; if (from->flagsImportance & SvgStyleFlags::Color) to->flagsImportance |= SvgStyleFlags::Color; } if (((from->flags & SvgStyleFlags::PaintOrder) && (overwrite || !(to->flags & SvgStyleFlags::PaintOrder))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::PaintOrder)) { to->paintOrder = from->paintOrder; to->flags |= SvgStyleFlags::PaintOrder; if (from->flagsImportance & SvgStyleFlags::PaintOrder) to->flagsImportance |= SvgStyleFlags::PaintOrder; } if (((from->flags & SvgStyleFlags::Display) && (overwrite || !(to->flags & SvgStyleFlags::Display))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Display)) { to->display = from->display; to->flags |= SvgStyleFlags::Display; if (from->flagsImportance & SvgStyleFlags::Display) to->flagsImportance |= SvgStyleFlags::Display; } //Fill if (((from->fill.flags & SvgFillFlags::Paint) && (overwrite || !(to->flags & SvgStyleFlags::Fill))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) { to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; if (from->fill.paint.url) { if (to->fill.paint.url) tvg::free(to->fill.paint.url); to->fill.paint.url = duplicate(from->fill.paint.url); } to->fill.flags |= SvgFillFlags::Paint; to->flags |= SvgStyleFlags::Fill; if (from->flagsImportance & SvgStyleFlags::Fill) to->flagsImportance |= SvgStyleFlags::Fill; } if (((from->fill.flags & SvgFillFlags::Opacity) && (overwrite || !(to->flags & SvgStyleFlags::FillOpacity))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillOpacity)) { to->fill.opacity = from->fill.opacity; to->fill.flags |= SvgFillFlags::Opacity; to->flags |= SvgStyleFlags::FillOpacity; if (from->flagsImportance & SvgStyleFlags::FillOpacity) to->flagsImportance |= SvgStyleFlags::FillOpacity; } if (((from->fill.flags & SvgFillFlags::FillRule) && (overwrite || !(to->flags & SvgStyleFlags::FillRule))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillRule)) { to->fill.fillRule = from->fill.fillRule; to->fill.flags |= SvgFillFlags::FillRule; to->flags |= SvgStyleFlags::FillRule; if (from->flagsImportance & SvgStyleFlags::FillRule) to->flagsImportance |= SvgStyleFlags::FillRule; } //Stroke if (((from->stroke.flags & SvgStrokeFlags::Paint) && (overwrite || !(to->flags & SvgStyleFlags::Stroke))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Stroke)) { to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; if (from->stroke.paint.url) { if (to->stroke.paint.url) tvg::free(to->stroke.paint.url); to->stroke.paint.url = duplicate(from->stroke.paint.url); } to->stroke.flags |= SvgStrokeFlags::Paint; to->flags |= SvgStyleFlags::Stroke; if (from->flagsImportance & SvgStyleFlags::Stroke) to->flagsImportance |= SvgStyleFlags::Stroke; } if (((from->stroke.flags & SvgStrokeFlags::Opacity) && (overwrite || !(to->flags & SvgStyleFlags::StrokeOpacity))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeOpacity)) { to->stroke.opacity = from->stroke.opacity; to->stroke.flags |= SvgStrokeFlags::Opacity; to->flags |= SvgStyleFlags::StrokeOpacity; if (from->flagsImportance & SvgStyleFlags::StrokeOpacity) to->flagsImportance |= SvgStyleFlags::StrokeOpacity; } if (((from->stroke.flags & SvgStrokeFlags::Width) && (overwrite || !(to->flags & SvgStyleFlags::StrokeWidth))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeWidth)) { to->stroke.width = from->stroke.width; to->stroke.flags |= SvgStrokeFlags::Width; to->flags |= SvgStyleFlags::StrokeWidth; if (from->flagsImportance & SvgStyleFlags::StrokeWidth) to->flagsImportance |= SvgStyleFlags::StrokeWidth; } if (((from->stroke.flags & SvgStrokeFlags::Dash) && (overwrite || !(to->flags & SvgStyleFlags::StrokeDashArray))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeDashArray)) { if (!from->stroke.dash.array.empty()) { to->stroke.dash.array.clear(); to->stroke.dash.array.reserve(from->stroke.dash.array.count); ARRAY_FOREACH(p, from->stroke.dash.array) to->stroke.dash.array.push(*p); to->stroke.flags |= SvgStrokeFlags::Dash; to->flags |= SvgStyleFlags::StrokeDashArray; if (from->flagsImportance & SvgStyleFlags::StrokeDashArray) to->flagsImportance |= SvgStyleFlags::StrokeDashArray; } } if (((from->stroke.flags & SvgStrokeFlags::Cap) && (overwrite || !(to->flags & SvgStyleFlags::StrokeLineCap))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineCap)) { to->stroke.cap = from->stroke.cap; to->stroke.flags |= SvgStrokeFlags::Cap; to->flags |= SvgStyleFlags::StrokeLineCap; if (from->flagsImportance & SvgStyleFlags::StrokeLineCap) to->flagsImportance |= SvgStyleFlags::StrokeLineCap; } if (((from->stroke.flags & SvgStrokeFlags::Join) && (overwrite || !(to->flags & SvgStyleFlags::StrokeLineJoin))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineJoin)) { to->stroke.join = from->stroke.join; to->stroke.flags |= SvgStrokeFlags::Join; to->flags |= SvgStyleFlags::StrokeLineJoin; if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) to->flagsImportance |= SvgStyleFlags::StrokeLineJoin; } //Opacity if (((from->flags & SvgStyleFlags::Opacity) && (overwrite || !(to->flags & SvgStyleFlags::Opacity))) || _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Opacity)) { to->opacity = from->opacity; to->flags |= SvgStyleFlags::Opacity; if (from->flagsImportance & SvgStyleFlags::Opacity) to->flagsImportance |= SvgStyleFlags::Opacity; } } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ void cssCopyStyleAttr(SvgNode* to, const SvgNode* from, bool overwrite) { //Copy matrix attribute if (from->transform && (overwrite || !(to->style->flags & SvgStyleFlags::Transform))) { if (!to->transform) to->transform = tvg::malloc(sizeof(Matrix)); *to->transform = *from->transform; to->style->flags |= SvgStyleFlags::Transform; } //Copy style attribute _copyStyle(to->style, from->style, overwrite); if (from->style->clipPath.url) { if (to->style->clipPath.url) tvg::free(to->style->clipPath.url); to->style->clipPath.url = duplicate(from->style->clipPath.url); } if (from->style->mask.url) { if (to->style->mask.url) tvg::free(to->style->mask.url); to->style->mask.url = duplicate(from->style->mask.url); } } SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type) { if (!style) return nullptr; ARRAY_FOREACH(p, style->child) { if ((*p)->type == type) { if ((!title && !(*p)->id) || (title && (*p)->id && STR_AS((*p)->id, title))) return *p; } } return nullptr; } SvgNode* cssFindStyleNode(const SvgNode* style, const char* title) { if (!style || !title) return nullptr; ARRAY_FOREACH(p, style->child) { if ((*p)->type == SvgNodeType::CssStyle) { if ((*p)->id && STR_AS((*p)->id, title)) return *p; } } return nullptr; } void cssUpdateStyle(SvgNode* doc, SvgNode* style) { ARRAY_FOREACH(p, doc->child) { if (auto cssNode = cssFindStyleNode(style, nullptr, (*p)->type)) { cssCopyStyleAttr(*p, cssNode); } cssUpdateStyle(*p, style); } }thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-dataview.cpp000664 001750 001750 00000004067 15164251010 050207 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-dataview-object.h" #include "ecma-exceptions.h" #if JERRY_BUILTIN_DATAVIEW #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-dataview.inc.h" #define BUILTIN_UNDERSCORED_ID dataview #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup dataview ECMA DataView object built-in * @{ */ /** * Handle calling [[Call]] of built-in DataView object * * @return ecma value */ ecma_value_t ecma_builtin_dataview_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_DATAVIEW_REQUIRES_NEW); } /* ecma_builtin_dataview_dispatch_call */ /** * Handle calling [[Construct]] of built-in DataView object * * @return ecma value */ ecma_value_t ecma_builtin_dataview_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_op_dataview_create (arguments_list_p, arguments_list_len); } /* ecma_builtin_dataview_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_DATAVIEW */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/prettywriter.h000664 001750 001750 00000024426 15164251010 037614 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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 output 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 TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class PrettyWriter : public Writer { public: typedef Writer 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), formatOptions_(kFormatDefault) {} #if RAPIDJSON_HAS_CXX11_RVALUE_REFS PrettyWriter(PrettyWriter&& rhs) : Base(std::forward(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::EndValue(Base::WriteNull()); } bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } bool RawNumber(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteString(str, length)); } bool String(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); return Base::EndValue(Base::WriteString(str, length)); } #if RAPIDJSON_HAS_STDSTRING bool String(const std::basic_string& str) { return String(str.data(), SizeType(str.size())); } #endif bool StartObject() { PrettyPrefix(kObjectType); new (Base::level_stack_.template Push()) 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& 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()->inArray); // currently inside an Array, not Object RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { Base::os_->Put('\n'); WriteIndent(); } bool ret = Base::EndValue(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(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()->inArray); bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { Base::os_->Put('\n'); WriteIndent(); } bool ret = Base::EndValue(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::EndValue(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(); 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(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_ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-type-error-thrower.cpp000664 001750 001750 00000004667 15164251010 052211 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-type-error-thrower.inc.h" #define BUILTIN_UNDERSCORED_ID type_error_thrower #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup type_error_thrower ECMA [[ThrowTypeError]] object built-in * @{ */ /** * Handle calling [[Call]] of built-in [[ThrowTypeError]] object * * See also: * ECMA-262 v5, 13.2.3 * * @return ecma value */ ecma_value_t ecma_builtin_type_error_thrower_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CANNOT_ACCESS_CALLER_CALLEE_ARGUMENTS); } /* ecma_builtin_type_error_thrower_dispatch_call */ /** * Handle calling [[Construct]] of built-in [[ThrowTypeError]] object * * See also: * ECMA-262 v5, 13.2.3 * * @return ecma value */ ecma_value_t ecma_builtin_type_error_thrower_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_builtin_type_error_thrower_dispatch_call (arguments_list_p, arguments_list_len); } /* ecma_builtin_type_error_thrower_dispatch_construct */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_webp/000775 001750 001750 00000000000 15164251010 034274 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/bindings/capi/thorvg_capi.h000664 001750 001750 00000402716 15164251010 035205 0ustar00ddennedyddennedy000000 000000 #ifndef __THORVG_CAPI_H__ #define __THORVG_CAPI_H__ #include #include #ifdef TVG_API #undef TVG_API #endif #define TVG_VERSION_MAJOR 1 // for compile-time checks #define TVG_VERSION_MINOR 0 // for compile-time checks #define TVG_VERSION_MICRO 1 // for compile-time checks #ifndef TVG_STATIC #ifdef _WIN32 #if TVG_BUILD #define TVG_API __declspec(dllexport) #else #define TVG_API __declspec(dllimport) #endif #elif (defined(__SUNPRO_C) || defined(__SUNPRO_CC)) #define TVG_API __global #else #if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__INTEL_COMPILER) #define TVG_API __attribute__ ((visibility("default"))) #else #define TVG_API #endif #endif #else #define TVG_API #endif #ifdef TVG_DEPRECATED #undef TVG_DEPRECATED #endif #ifdef _WIN32 #define TVG_DEPRECATED __declspec(deprecated) #elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) #define TVG_DEPRECATED __attribute__ ((__deprecated__)) #else #define TVG_DEPRECATED #endif #ifdef __cplusplus extern "C" { #endif /** * @defgroup ThorVG_CAPI ThorVG_CAPI * @brief ThorVG C language binding APIs. * * \{ */ /** * @brief A structure responsible for managing and drawing graphical elements. * * It sets up the target buffer, which can be drawn on the screen. It stores the Tvg_Paint objects (Shape, Scene, Picture). */ typedef struct _Tvg_Canvas* Tvg_Canvas; /** * @brief A structure representing a graphical element. * * @warning The TvgPaint objects cannot be shared between Canvases. */ typedef struct _Tvg_Paint* Tvg_Paint; /** * @brief A structure representing a gradient fill of a Tvg_Paint object. */ typedef struct _Tvg_Gradient* Tvg_Gradient; /** * @brief A structure representing an object that enables to save a Tvg_Paint object into a file. */ typedef struct _Tvg_Saver* Tvg_Saver; /** * @brief A structure representing an animation controller object. */ typedef struct _Tvg_Animation* Tvg_Animation; /** * @brief A structure representing an object that enables iterating through a scene's descendents. */ typedef struct _Tvg_Accessor* Tvg_Accessor; /** * @brief Enumeration specifying the result from the APIs. * * All ThorVG APIs could potentially return one of the values in the list. * Please note that some APIs may additionally specify the reasons that trigger their return values. * */ typedef enum { TVG_RESULT_SUCCESS = 0, ///< The value returned in case of a correct request execution. TVG_RESULT_INVALID_ARGUMENT, ///< The value returned in the event of a problem with the arguments given to the API - e.g. empty paths or null pointers. TVG_RESULT_INSUFFICIENT_CONDITION, ///< The value returned in case the request cannot be processed - e.g. asking for properties of an object, which does not exist. TVG_RESULT_FAILED_ALLOCATION, ///< The value returned in case of unsuccessful memory allocation. TVG_RESULT_MEMORY_CORRUPTION, ///< The value returned in the event of bad memory handling - e.g. failing in pointer releasing or casting TVG_RESULT_NOT_SUPPORTED, ///< The value returned in case of choosing unsupported engine features(options). TVG_RESULT_UNKNOWN = 255 ///< The value returned in all other cases. } Tvg_Result; /** * @brief Enumeration specifying the methods of combining the 8-bit color channels into 32-bit color. */ typedef enum { TVG_COLORSPACE_ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. TVG_COLORSPACE_ARGB8888, ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. TVG_COLORSPACE_ABGR8888S, ///< The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. (since 0.13) TVG_COLORSPACE_ARGB8888S, ///< The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. (since 0.13) TVG_COLORSPACE_UNKNOWN = 255, ///< Unknown channel data. This is reserved for an initial ColorSpace value. (since 1.0) } Tvg_Colorspace; /** * @brief Enumeration to specify rendering engine behavior. * * @note The availability or behavior of @c TVG_ENGINE_OPTION_SMART_RENDER may vary depending on platform or backend support. * It attempts to optimize rendering performance by updating only the regions of the canvas that have * changed between frames (partial redraw). This can be highly effective in scenarios where most of the * canvas remains static and only small portions are updated—such as simple animations or GUI interactions. * However, in complex scenes where a large portion of the canvas changes frequently (e.g., full-screen animations * or heavy object movements), the overhead of tracking changes and managing update regions may outweigh the benefits, * resulting in decreased performance compared to the default rendering mode. Thus, it is recommended to benchmark * both modes in your specific use case to determine the optimal setting. * * @since 1.0 */ typedef enum { TVG_ENGINE_OPTION_NONE = 0, /**< No engine options are enabled. This may be used to explicitly disable all optional behaviors. */ TVG_ENGINE_OPTION_DEFAULT = 1 << 0, /**< Uses the default rendering mode. */ TVG_ENGINE_OPTION_SMART_RENDER = 1 << 1 /**< Enables automatic partial (smart) rendering optimizations. */ } Tvg_Engine_Option; /** * @brief Enumeration indicating the method used in the masking of two objects - the target and the source. * * @ingroup ThorVGCapi_Paint */ typedef enum { TVG_MASK_METHOD_NONE = 0, ///< No Masking is applied. TVG_MASK_METHOD_ALPHA, ///< Alpha Masking using the masking target's pixels as an alpha value. TVG_MASK_METHOD_INVERSE_ALPHA, ///< Alpha Masking using the complement to the masking target's pixels as an alpha value. TVG_MASK_METHOD_LUMA, ///< Alpha Masking using the grayscale (0.2126R + 0.7152G + 0.0722*B) of the masking target's pixels. @since 0.9 TVG_MASK_METHOD_INVERSE_LUMA, ///< Alpha Masking using the grayscale (0.2126R + 0.7152G + 0.0722*B) of the complement to the masking target's pixels. @since 0.11 TVG_MASK_METHOD_ADD, ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) @since 1.0 TVG_MASK_METHOD_SUBTRACT, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) @since 1.0 TVG_MASK_METHOD_INTERSECT, ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) @since 1.0 TVG_MASK_METHOD_DIFFERENCE, ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) @since 1.0 TVG_MASK_METHOD_LIGHTEN, ///< Where multiple masks intersect, the highest transparency value is used. @since 1.0 TVG_MASK_METHOD_DARKEN ///< Where multiple masks intersect, the lowest transparency value is used. @since 1.0 } Tvg_Mask_Method; /** * @brief Enumeration indicates the method used for blending paint. Please refer to the respective formulas for each method. * * @ingroup ThorVGCapi_Paint * * @since 0.15 */ typedef enum { TVG_BLEND_METHOD_NORMAL = 0, ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D TVG_BLEND_METHOD_MULTIPLY, ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D) TVG_BLEND_METHOD_SCREEN, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D) TVG_BLEND_METHOD_OVERLAY, ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) TVG_BLEND_METHOD_DARKEN, ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D) TVG_BLEND_METHOD_LIGHTEN, ///< Only has the opposite action of Darken Only. max(S, D) TVG_BLEND_METHOD_COLORDODGE, ///< Divides the bottom layer by the inverted top layer. D / (255 - S) TVG_BLEND_METHOD_COLORBURN, ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S TVG_BLEND_METHOD_HARDLIGHT, ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) TVG_BLEND_METHOD_SOFTLIGHT, ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D) TVG_BLEND_METHOD_DIFFERENCE, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S) TVG_BLEND_METHOD_EXCLUSION, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d) TVG_BLEND_METHOD_HUE, ///< Combine with HSL(Sh + Ds + Dl) then convert it to RGB. TVG_BLEND_METHOD_SATURATION, ///< Combine with HSL(Dh + Ss + Dl) then convert it to RGB. TVG_BLEND_METHOD_COLOR, ///< Combine with HSL(Sh + Ss + Dl) then convert it to RGB. TVG_BLEND_METHOD_LUMINOSITY, ///< Combine with HSL(Dh + Ds + Sl) then convert it to RGB. TVG_BLEND_METHOD_ADD, ///< Simply adds pixel values of one layer with the other. (S + D) TVG_BLEND_METHOD_COMPOSITION = 255 ///< Used for intermediate composition. @since 1.0 } Tvg_Blend_Method; /** * @brief Enumeration indicating the ThorVG object type value. * * ThorVG's drawing objects can return object type values, allowing you to identify the specific type of each object. * * @ingroup ThorVGCapi_Paint * * @see tvg_paint_get_type() * @see tvg_gradient_get_type() * * @since 1.0 */ typedef enum { TVG_TYPE_UNDEF = 0, ///< Undefined type. TVG_TYPE_SHAPE, ///< A shape type paint. TVG_TYPE_SCENE, ///< A scene type paint. TVG_TYPE_PICTURE, ///< A picture type paint. TVG_TYPE_TEXT, ///< A text type paint. TVG_TYPE_LINEAR_GRAD = 10, ///< A linear gradient type. TVG_TYPE_RADIAL_GRAD ///< A radial gradient type. } Tvg_Type; /** * @addtogroup ThorVGCapi_Shape * \{ */ /** * @brief Enumeration specifying the values of the path commands accepted by ThorVG. */ typedef uint8_t Tvg_Path_Command; enum { TVG_PATH_COMMAND_CLOSE = 0, ///< Ends the current sub-path and connects it with its initial point - corresponds to Z command in the svg path commands. TVG_PATH_COMMAND_MOVE_TO, ///< Sets a new initial point of the sub-path and a new current point - corresponds to M command in the svg path commands. TVG_PATH_COMMAND_LINE_TO, ///< Draws a line from the current point to the given point and sets a new value of the current point - corresponds to L command in the svg path commands. TVG_PATH_COMMAND_CUBIC_TO ///< Draws a cubic Bezier curve from the current point to the given point using two given control points and sets a new value of the current point - corresponds to C command in the svg path commands. }; /** * @brief Enumeration determining the ending type of a stroke in the open sub-paths. */ typedef enum { TVG_STROKE_CAP_BUTT = 0, ///< The stroke ends exactly at each of the two endpoints of a sub-path. For zero length sub-paths no stroke is rendered. TVG_STROKE_CAP_ROUND, ///< The stroke is extended in both endpoints of a sub-path by a half circle, with a radius equal to the half of a stroke width. For zero length sub-paths a full circle is rendered. TVG_STROKE_CAP_SQUARE ///< The stroke is extended in both endpoints of a sub-path by a rectangle, with the width equal to the stroke width and the length equal to the half of the stroke width. For zero length sub-paths the square is rendered with the size of the stroke width. } Tvg_Stroke_Cap; /** * @brief Enumeration specifying how to fill the area outside the gradient bounds. */ typedef enum { TVG_STROKE_JOIN_MITER = 0, ///< The outer corner of the joined path segments is spiked. The spike is created by extension beyond the join point of the outer edges of the stroke until they intersect. In case the extension goes beyond the limit, the join style is converted to the Bevel style. TVG_STROKE_JOIN_ROUND, ///< The outer corner of the joined path segments is rounded. The circular region is centered at the join point. TVG_STROKE_JOIN_BEVEL ///< The outer corner of the joined path segments is bevelled at the join point. The triangular region of the corner is enclosed by a straight line between the outer corners of each stroke. } Tvg_Stroke_Join; /** * @brief Enumeration specifying how to fill the area outside the gradient bounds. */ typedef enum { TVG_STROKE_FILL_PAD = 0, ///< The remaining area is filled with the closest stop color. TVG_STROKE_FILL_REFLECT, ///< The gradient pattern is reflected outside the gradient area until the expected region is filled. TVG_STROKE_FILL_REPEAT ///< The gradient pattern is repeated continuously beyond the gradient area until the expected region is filled. } Tvg_Stroke_Fill; /** * @brief Enumeration specifying the algorithm used to establish which parts of the shape are treated as the inside of the shape. */ typedef enum { TVG_FILL_RULE_NON_ZERO = 0, ///< A line from the point to a location outside the shape is drawn. The intersections of the line with the path segment of the shape are counted. Starting from zero, if the path segment of the shape crosses the line clockwise, one is added, otherwise one is subtracted. If the resulting sum is non zero, the point is inside the shape. TVG_FILL_RULE_EVEN_ODD ///< A line from the point to a location outside the shape is drawn and its intersections with the path segments of the shape are counted. If the number of intersections is an odd number, the point is inside the shape. } Tvg_Fill_Rule; /** \} */ // end addtogroup ThorVGCapi_Shape /** * @addtogroup ThorVGCapi_Gradient * \{ */ /** * @brief A data structure storing the information about the color and its relative position inside the gradient bounds. */ typedef struct { float offset; /**< The relative position of the color. */ uint8_t r; /**< The red color channel value in the range [0 ~ 255]. */ uint8_t g; /**< The green color channel value in the range [0 ~ 255]. */ uint8_t b; /**< The blue color channel value in the range [0 ~ 255]. */ uint8_t a; /**< The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. */ } Tvg_Color_Stop; /** \} */ // end addtogroup ThorVGCapi_Gradient /** * @addtogroup ThorVGCapi_Text * \{ */ /** * @brief A data structure storing the information about the color and its relative position inside the gradient bounds. */ typedef enum { TVG_TEXT_WRAP_NONE = 0, ///< Do not wrap text. Text is rendered on a single line and may overflow the bounding area. TVG_TEXT_WRAP_CHARACTER, ///< Wrap at the character level. If a word cannot fit, it is broken into individual characters to fit the line. TVG_TEXT_WRAP_WORD, ///< Wrap at the word level. Words that do not fit are moved to the next line. TVG_TEXT_WRAP_SMART, ///< Smart choose wrapping method: word wrap first, falling back to character wrap if a word does not fit. TVG_TEXT_WRAP_ELLIPSIS, ///< Truncate overflowing text and append an ellipsis ("...") at the end. Typically used for single-line labels. TVG_TEXT_WRAP_HYPHENATION ///< Reserved. No Support. } Tvg_Text_Wrap; /** \} */ // end addtogroup ThorVGCapi_Text /** * @brief A data structure representing a point in two-dimensional space. */ typedef struct { float x, y; } Tvg_Point; /** * @brief A data structure representing a three-dimensional matrix. * * The elements e11, e12, e21 and e22 represent the rotation matrix, including the scaling factor. * The elements e13 and e23 determine the translation of the object along the x and y-axis, respectively. * The elements e31 and e32 are set to 0, e33 is set to 1. */ typedef struct { float e11, e12, e13; float e21, e22, e23; float e31, e32, e33; } Tvg_Matrix; /** * @brief Describes the font metrics of a text object. * * Provides the basic vertical layout metrics used for text rendering, * such as ascent, descent, and line spacing (linegap). * * @see tvg_text_get_metrics() * @note Experimental API */ typedef struct { float ascent; ///< Distance from the baseline to the top of the highest glyph (usually positive). float descent; ///< Distance from the baseline to the bottom of the lowest glyph (usually negative, as in TTF). float linegap; ///< Additional spacing recommended between lines (leading). float advance; ///< The total vertical advance between lines of text: ascent - descent + linegap (i.e., ascent + |descent| + linegap when descent is negative). } Tvg_Text_Metrics; /** * @brief Callback function type for resolving external assets. * * This callback is invoked when a Picture requires an external asset * (such as an image or font resource). Implementations should load the asset * into the given @p paint object. * * @param[in] paint The target paint object where the resolved asset will be loaded. * @param[in] src The source path, identifier, or URI of the asset to be resolved. * @param[in] data User-provided custom data passed to the callback for context. * * @return @c true if the asset was successfully resolved and loaded into @p paint, otherwise @c false. * * @see tvg_picture_set_asset_resolver() * * @since Experimental API */ typedef bool (*Tvg_Picture_Asset_Resolver)(Tvg_Paint paint, const char* src, void* data); /** * @defgroup ThorVGCapi_Initializer Initializer * @brief A module enabling initialization and termination of the TVG engines. * * \{ */ /************************************************************************/ /* Engine API */ /************************************************************************/ /** * @brief Initializes the ThorVG engine. * * ThorVG requires an active runtime environment to operate. * Internally, it utilizes a task scheduler to efficiently parallelize rendering operations. * You can specify the number of worker threads using the @p threads parameter. * During initialization, ThorVG will spawn the specified number of threads. * * @param[in] threads The number of worker threads to create. A value of zero indicates that only the main thread will be used. * * @note The initializer uses internal reference counting to track multiple calls. * The number of threads is fixed on the first call to tvg_engine_init() and cannot be changed in subsequent calls. * @see tvg_engine_term() */ TVG_API Tvg_Result tvg_engine_init(unsigned threads); /** * @brief Terminates the ThorVG engine. * * Cleans up resources and stops any internal threads initialized by tvg_engine_init(). * * @retval TVG_RESULT_INSUFFICIENT_CONDITION Returned if there is nothing to terminate (e.g., tvg_engine_init() was not called). * * @note The initializer maintains a reference count for safe repeated use. Only the final call to tvg_engine_term() will fully shut down the engine. * @see tvg_engine_init() */ TVG_API Tvg_Result tvg_engine_term(void); /** * @brief Retrieves the version of the TVG engine. * * @param[out] major A major version number. * @param[out] minor A minor version number. * @param[out] micro A micro version number. * @param[out] version The version of the engine in the format major.minor.micro, or a @p nullptr in case of an internal error. * * @retval TVG_RESULT_SUCCESS. * * @since 0.15 */ TVG_API Tvg_Result tvg_engine_version(uint32_t* major, uint32_t* minor, uint32_t* micro, const char** version); /** \} */ // end defgroup ThorVGCapi_Initializer /** * @defgroup ThorVGCapi_Canvas Canvas * @brief A module for managing and drawing graphical elements. * * A canvas is an entity responsible for drawing the target. It sets up the drawing engine and the buffer, which can be drawn on the screen. It also manages given Paint objects. * * @note A Canvas behavior depends on the raster engine though the final content of the buffer is expected to be identical. * @warning The Paint objects belonging to one Canvas can't be shared among multiple Canvases. * \{ */ /** * @defgroup ThorVGCapi_SwCanvas SwCanvas * @ingroup ThorVGCapi_Canvas * * @brief A module for rendering the graphical elements using the software engine. * * \{ */ /************************************************************************/ /* SwCanvas API */ /************************************************************************/ /** * @brief Creates a new Software Canvas object with optional rendering engine settings. * * This method generates a software canvas instance that can be used for drawing vector graphics. * It accepts an optional parameter @p op to choose between different rendering engine behaviors. * * @param[in] op The rendering engine option. * * @return A new Tvg_Canvas object. * * @see enum Tvg_Engine_Option */ TVG_API Tvg_Canvas tvg_swcanvas_create(Tvg_Engine_Option op); /** * @brief Sets the buffer used in the rasterization process and defines the used colorspace. * * For optimisation reasons TVG does not allocate memory for the output buffer on its own. * The buffer of a desirable size should be allocated and owned by the caller. * * @param[in] canvas The Tvg_Canvas object managing the @p buffer. * @param[in] buffer A pointer to the allocated memory block of the size @p stride x @p h. * @param[in] stride The stride of the raster image - in most cases same value as @p w. * @param[in] w The width of the raster image. * @param[in] h The height of the raster image. * @param[in] cs The colorspace value defining the way the 32-bits colors should be read/written. * * @retval TVG_RESULT_INVALID_ARGUMENTS An invalid canvas or buffer pointer passed or one of the @p stride, @p w or @p h being zero. * @retval TVG_RESULT_INSUFFICIENT_CONDITION if the canvas is performing rendering. Please ensure the canvas is synced. * @retval TVG_RESULT_NOT_SUPPORTED The software engine is not supported. * * @warning Do not access @p buffer during tvg_canvas_draw() - tvg_canvas_sync(). It should not be accessed while the engine is writing on it. * * @see Tvg_Colorspace */ TVG_API Tvg_Result tvg_swcanvas_set_target(Tvg_Canvas canvas, uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Tvg_Colorspace cs); /** \} */ // end defgroup ThorVGCapi_SwCanvas /** * @defgroup ThorVGCapi_GlCanvas SwCanvas * @ingroup ThorVGCapi_Canvas * * @brief A module for rendering the graphical elements using the opengl engine. * * \{ */ /************************************************************************/ /* GlCanvas API */ /************************************************************************/ /** * @brief Creates a new OpenGL/ES Canvas object with optional rendering engine settings. * * This method generates a OpenGL/ES canvas instance that can be used for drawing vector graphics. * It accepts an optional parameter @p op to choose between different rendering engine behaviors. * * @param[in] op The rendering engine option. * * @return A new Tvg_Canvas object. * * @note Currently, it does not support @c TVG_ENGINE_OPTION_SMART_RENDER. The request will be ignored. * * @see enum Tvg_Engine_Option * * @since 1.0 */ TVG_API Tvg_Canvas tvg_glcanvas_create(Tvg_Engine_Option op); /** * @brief Sets the drawing target for rasterization. * * This function specifies the drawing target where the rasterization will occur. It can target * a specific framebuffer object (FBO) or the main surface. * * @param[in] display The platform-specific display handle (EGLDisplay for EGL). Set @c nullptr for other systems. * @param[in] surface The platform-specific surface handle (EGLSurface for EGL, HDC for WGL). Set @c nullptr for other systems. * @param[in] context The OpenGL context to be used for rendering on this canvas. * @param[in] id The GL target ID, usually indicating the FBO ID. A value of @c 0 specifies the main surface. * @param[in] w The width (in pixels) of the raster image. * @param[in] h The height (in pixels) of the raster image. * @param[in] cs Specifies how the pixel values should be interpreted. Currently, it only allows @c TVG_COLORSPACE_ABGR8888S as @c GL_RGBA8. * * @note If @p display and @p surface are not provided, the ThorVG GL engine assumes that * the appropriate OpenGL context is already current and will not attempt to bind a new one. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION If the canvas is currently rendering. * Ensure that @ref tvg_canvas_sync() has been called before setting a new target. * @retval TVG_RESULT_NOT_SUPPORTED In case the gl engine is not supported. * * @see tvg_canvas_sync() * * @since 1.0 */ TVG_API Tvg_Result tvg_glcanvas_set_target(Tvg_Canvas canvas, void* display, void* surface, void* context, int32_t id, uint32_t w, uint32_t h, Tvg_Colorspace cs); /** \} */ // end defgroup ThorVGCapi_GlCanvas /** * @defgroup ThorVGCapi_WgCanvas WgCanvas * @ingroup ThorVGCapi_Canvas * * @brief A module for rendering the graphical elements using the webgpu engine. * * \{ */ /************************************************************************/ /* WgCanvas API */ /************************************************************************/ /** * @brief Creates a new WebGPU Canvas object with optional rendering engine settings. * * This method generates a WebGPU canvas instance that can be used for drawing vector graphics. * It accepts an optional parameter @p op to choose between different rendering engine behaviors. * * @param[in] op The rendering engine option. * * @return A new Tvg_Canvas object. * * @note Currently, it does not support @c TVG_ENGINE_OPTION_SMART_RENDER. The request will be ignored. * * @see enum Tvg_Engine_Option * * @since 1.0 */ TVG_API Tvg_Canvas tvg_wgcanvas_create(Tvg_Engine_Option op); /** * @brief Sets the drawing target for the rasterization. * * @param[in] device WGPUDevice, a desired handle for the wgpu device. If it is @c nullptr, ThorVG will assign an appropriate device internally. * @param[in] instance WGPUInstance, context for all other wgpu objects. * @param[in] target Either WGPUSurface or WGPUTexture, serving as handles to a presentable surface or texture. * @param[in] w The width of the target. * @param[in] h The height of the target. * @param[in] cs Specifies how the pixel values should be interpreted. Currently, it only allows @c TVG_COLORSPACE_ABGR8888S as @c WGPUTextureFormat_RGBA8Unorm. * @param[in] type @c 0: surface, @c 1: texture are used as pesentable target. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION if the canvas is performing rendering. Please ensure the canvas is synced. * @retval TVG_RESULT_NOT_SUPPORTED In case the wg engine is not supported. * * @since 1.0 */ TVG_API Tvg_Result tvg_wgcanvas_set_target(Tvg_Canvas canvas, void* device, void* instance, void* target, uint32_t w, uint32_t h, Tvg_Colorspace cs, int type); /** \} */ // end defgroup ThorVGCapi_WgCanvas /************************************************************************/ /* Common Canvas API */ /************************************************************************/ /** * @brief Clears the canvas internal data, releases all paints stored by the canvas and destroys the canvas object itself. * * @param[in] canvas The Tvg_Canvas object to be destroyed. * */ TVG_API Tvg_Result tvg_canvas_destroy(Tvg_Canvas canvas); /** * @brief Adds a paint object to the canvas for rendering. * * Adds the specified paint into the canvas root scene. Only paints added to * the canvas are considered rendering targets. The canvas retains the paint * object until it is explicitly removed via tvg_canvas_remove(). * * @param[in] canvas A handle to the canvas that will manage the paint object. * @param[in] paint A handle to the paint object to be rendered. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION If the canvas is not in a valid state to accept new paints. * * @note Ownership of the @p paint object is transferred to the canvas upon * successful addition. To retain ownership, call @ref tvg_paint_ref() * before adding it to the canvas. * @note The rendering order of paint objects follows the order in which they are * added to the canvas. If layering is required, ensure paints are added in * the desired order. * * @see tvg_canvas_insert() * @see tvg_canvas_remove() * * @since 1.0 */ TVG_API Tvg_Result tvg_canvas_add(Tvg_Canvas canvas, Tvg_Paint paint); /** * @brief Inserts a paint object into the canvas root scene. * * Inserts a paint object into the root scene of the specified canvas. If the * @p at parameter is provided, the paint object is inserted immediately before * the specified paint in the root scene. If @p at is @c nullptr, the paint object * is appended to the end of the root scene. * * @param[in] canvas A handle to the canvas that will manage the paint object. * This parameter must be a valid canvas handle. * @param[in] target A handle to the paint object to be inserted into the root scene. * This parameter must not be @c nullptr. * @param[in] at A handle to an existing paint object in the root scene before * which @p target will be inserted. If @c nullptr, @p target is * appended to the end of the root scene. * * @note Ownership of the @p target object is transferred to the canvas upon * successful addition. To retain ownership, call @ref tvg_paint_ref() * before adding it to the canvas. * @note The rendering order of paint objects follows their order in the root * scene. If layering is required, ensure paints are inserted in the * desired order. * * @see tvg_canvas_add() * @see tvg_canvas_remove() * * @since 1.0 */ TVG_API Tvg_Result tvg_canvas_insert(Tvg_Canvas canvas, Tvg_Paint target, Tvg_Paint at); /** * @brief Removes a paint object from the root scene. * * This function removes a specified paint object from the root scene. If no paint * object is specified (i.e., the default @c nullptr is used), the function * performs to clear all paints from the scene. * * @param[in] canvas A Tvg_Canvas object to remove the @p paint. * @param[in] paint A pointer to the Paint object to be removed from the root scene. * If @c nullptr, remove all the paints from the root scene. * * @see tvg_canvas_add() * @see tvg_canvas_insert() * @since 1.0 */ TVG_API Tvg_Result tvg_canvas_remove(Tvg_Canvas canvas, Tvg_Paint paint); /** * @brief Requests the canvas to update modified paint objects in preparation for rendering. * * This function triggers an internal update for all paint instances that have been modified * since the last update. It ensures that the canvas state is ready for accurate rendering. * * @param[in] canvas The Tvg_Canvas object to be updated. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. * @retval TVG_RESULT_INSUFFICIENT_CONDITION The canvas is not properly prepared. * This may occur if the canvas target has not been set or if the update is called during drawing. * Call tvg_canvas_sync() before trying. * * @note Only paint objects that have been changed will be processed. * @note If the canvas is configured with multiple threads, the update may be performed asynchronously. * * @see tvg_canvas_sync() */ TVG_API Tvg_Result tvg_canvas_update(Tvg_Canvas canvas); /** * @brief Requests the canvas to render the Paint objects. * * @param[in] canvas The Tvg_Canvas object containing elements to be drawn. * @param[in] clear If @c true, clears the target buffer to zero before drawing. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. * @retval TVG_RESULT_INSUFFICIENT_CONDITION The canvas is not properly prepared. * This may occur if the canvas target has not been set or if the update is called during drawing. * without calling tvg_canvas_sync() in between. * * @note Clearing the buffer is unnecessary if the canvas will be fully covered * with opaque content. Skipping the clear can improve performance. * @note Drawing may be performed asynchronously if the thread count is greater than zero. * To ensure the drawing process is complete, call sync() afterwards. * @note If the canvas has not been updated prior to tvg_canvas_draw(), it may implicitly perform tvg_canvas_update() * * @see tvg_canvas_sync() * @see tvg_canvas_update() */ TVG_API Tvg_Result tvg_canvas_draw(Tvg_Canvas canvas, bool clear); /** * @brief Guarantees that drawing task is finished. * * @param[in] canvas The Tvg_Canvas object containing elements which were drawn. * * The Canvas rendering can be performed asynchronously. To make sure that rendering is finished, * the tvg_canvas_sync() must be called after the tvg_canvas_draw() regardless of threading. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. * * @see tvg_canvas_draw() */ TVG_API Tvg_Result tvg_canvas_sync(Tvg_Canvas canvas); /** * @brief Sets the drawing region of the canvas. * * This function defines a rectangular area of the canvas to be used for drawing operations. * The specified viewport clips rendering output to the boundaries of that rectangle. * * Please note that changing the viewport is only allowed at the beginning of the rendering sequence—that is, after calling tvg_canvas_sync(). * * @param[in] canvas The Tvg_Canvas object containing elements which were drawn. * @param[in] x The x-coordinate of the upper-left corner of the rectangle. * @param[in] y The y-coordinate of the upper-left corner of the rectangle. * @param[in] w The width of the rectangle. * @param[in] h The height of the rectangle. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. * @retval TVG_RESULT_INSUFFICIENT_CONDITION If the canvas is not in a synced state. * * @see tvg_canvas_sync() * @see tvg_swcanvas_set_target() * @see tvg_glcanvas_set_target() * @see tvg_wgcanvas_set_target() * * @warning Changing the viewport is not allowed after calling tvg_canvas_add(), * tvg_canvas_remove(), tvg_canvas_update(), or tvg_canvas_draw(). * * @note When the target is reset, the viewport will also be reset to match the target size. * @since 0.15 */ TVG_API Tvg_Result tvg_canvas_set_viewport(Tvg_Canvas canvas, int32_t x, int32_t y, int32_t w, int32_t h); /** \} */ // end defgroup ThorVGCapi_Canvas /** * @defgroup ThorVGCapi_Paint Paint * @brief A module for managing graphical elements. It enables duplication, transformation and composition. * * \{ */ /************************************************************************/ /* Paint API */ /************************************************************************/ /** * @brief Safely releases a Tv_Paint object. * * This is the counterpart to the `new()` API, and releases the given Paint object safely, * handling @c nullptr and managing ownership properly. * * @param[in] paint A Tvg_Paint object to release. */ TVG_API Tvg_Result tvg_paint_rel(Tvg_Paint paint); /** * @brief Increment the reference count for the Tvg_Paint object. * * This method increases the reference count of Tvg_Paint object, allowing shared ownership and control over its lifetime. * * @param[in] paint The Tvg_Paint object to increase the reference count. * * @return The updated reference count after the increment by 1. * * @warning Please ensure that each call to tvg_paint_ref() is paired with a corresponding call to tvg_paint_unref() to prevent a dangling instance. * * @see tvg_paint_unref() * @see tvg_paint_get_ref() * * @since 1.0 */ TVG_API uint16_t tvg_paint_ref(Tvg_Paint paint); /** * @brief Decrement the reference count for the Tvg_Paint object. * * This method decreases the reference count of the Tvg_Paint object by 1. * If the reference count reaches zero and the @p free flag is set to true, the instance is automatically deleted. * * @param[in] paint The Tvg_Paint object to decrease the reference count. * @param[in] free Flag indicating whether to delete the Paint instance when the reference count reaches zero. * * @return The updated reference count after the decrement. * * @see tvg_paint_ref() * @see tvg_paint_get_ref() * * @since 1.0 */ TVG_API uint16_t tvg_paint_unref(Tvg_Paint paint, bool free); /** * @brief Retrieve the current reference count of the Tvg_Paint object. * * This method provides the current reference count, allowing the user to check the shared ownership state of the Tvg_Paint object. * * @param[in] paint The Tvg_Paint object to return the reference count. * * @return The current reference count of the Tvg_Paint object. * * @see tvg_paint_ref() * @see tvg_paint_unref() * * @since 1.0 */ TVG_API uint16_t tvg_paint_get_ref(const Tvg_Paint paint); /** * @brief Sets the visibility of the Paint object. * * This is useful for selectively excluding paint objects during rendering. * * @param[in] paint The Tvg_Paint object to set the visibility status. * @param[in] on A boolean flag indicating visibility. The default is @c true. * @c true, the object will be rendered by the engine. * @c false, the object will be excluded from the drawing process. * * @note An invisible object is not considered inactive—it may still participate * in internal update processing if its properties are updated, but it will not * be taken into account for the final drawing output. To completely deactivate * a paint object, remove it from the canvas. * * @see tvg_paint_get_visible() * @see tvg_canvas_remove() * * @since 1.0 */ TVG_API Tvg_Result tvg_paint_set_visible(Tvg_Paint paint, bool visible); /** * @brief Gets the current visibility status of the Paint object. * * @param[in] paint The Tvg_Paint object to return the visibility status. * * @return true if the object is visible and will be rendered. * false if the object is hidden and will not be rendered. * * @see tvg_paint_set_visible() * * @since 1.0 */ TVG_API bool tvg_paint_get_visible(const Tvg_Paint paint); /** * @brief Scales the given Tvg_Paint object by the given factor. * * @param[in] paint The Tvg_Paint object to be scaled. * @param[in] factor The value of the scaling factor. The default value is 1. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * @retval TVG_RESULT_INSUFFICIENT_CONDITION in case a custom transform is applied. * * @see tvg_paint_set_transform() */ TVG_API Tvg_Result tvg_paint_scale(Tvg_Paint paint, float factor); /** * @brief Rotates the given Tvg_Paint by the given angle. * * The angle in measured clockwise from the horizontal axis. * The rotational axis passes through the point on the object with zero coordinates. * * @param[in] paint The Tvg_Paint object to be rotated. * @param[in] degree The value of the rotation angle in degrees. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * @retval TVG_RESULT_INSUFFICIENT_CONDITION in case a custom transform is applied. * * @see tvg_paint_set_transform() */ TVG_API Tvg_Result tvg_paint_rotate(Tvg_Paint paint, float degree); /** * @brief Moves the given Tvg_Paint in a two-dimensional space. * * The origin of the coordinate system is in the upper-left corner of the canvas. * The horizontal and vertical axes point to the right and down, respectively. * * @param[in] paint The Tvg_Paint object to be shifted. * @param[in] x The value of the horizontal shift. * @param[in] y The value of the vertical shift. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * @retval TVG_RESULT_INSUFFICIENT_CONDITION in case a custom transform is applied. * * @see tvg_paint_set_transform() */ TVG_API Tvg_Result tvg_paint_translate(Tvg_Paint paint, float x, float y); /** * @brief Transforms the given Tvg_Paint using the augmented transformation matrix. * * The augmented matrix of the transformation is expected to be given. * * @param[in] paint The Tvg_Paint object to be transformed. * @param[in] m The 3x3 augmented matrix. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. */ TVG_API Tvg_Result tvg_paint_set_transform(Tvg_Paint paint, const Tvg_Matrix* m); /** * @brief Gets the matrix of the affine transformation of the given Tvg_Paint object. * * In case no transformation was applied, the identity matrix is returned. * * @param[in] paint The Tvg_Paint object of which to get the transformation matrix. * @param[out] m The 3x3 augmented matrix. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. */ TVG_API Tvg_Result tvg_paint_get_transform(Tvg_Paint paint, Tvg_Matrix* m); /** * @brief Sets the opacity of the given Tvg_Paint. * * @param[in] paint The Tvg_Paint object of which the opacity value is to be set. * @param[in] opacity The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note Setting the opacity with this API may require multiple renderings using a composition. It is recommended to avoid changing the opacity if possible. */ TVG_API Tvg_Result tvg_paint_set_opacity(Tvg_Paint paint, uint8_t opacity); /** * @brief Gets the opacity of the given Tvg_Paint. * * @param[in] paint The Tvg_Paint object of which to get the opacity value. * @param[out] opacity The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * * @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. */ TVG_API Tvg_Result tvg_paint_get_opacity(const Tvg_Paint paint, uint8_t* opacity); /** * @brief Duplicates the given Tvg_Paint object. * * Creates a new object and sets its all properties as in the original object. * * @param[in] paint The Tvg_Paint object to be copied. * * @return A copied Tvg_Paint object if succeed, @c nullptr otherwise. */ TVG_API Tvg_Paint tvg_paint_duplicate(Tvg_Paint paint); /** * @brief Checks whether a given region intersects the filled area of the paint. * * This function determines whether the specified rectangular region—defined by (`x`, `y`, `w`, `h`)— * intersects the geometric fill region of the paint object. * * This is useful for hit-testing purposes, such as detecting whether a user interaction (e.g., touch or click) * occurs within a painted region. * * The paint must be updated in a Canvas beforehand—typically after the Canvas has been * drawn and synchronized. * * @param[in] paint A Tvg_Paint pointer to the shape object to be tested. * @param[in] x The x-coordinate of the top-left corner of the test region. * @param[in] y The y-coordinate of the top-left corner of the test region. * @param[in] w The width of the region to test. Must be greater than 0; defaults to 1. * @param[in] h The height of the region to test. Must be greater than 0; defaults to 1. * * @return @c true if any part of the region intersects the filled area; otherwise, @c false. * * @note To test a single point, set the region size to w = 1, h = 1. * @note For efficiency, an AABB (axis-aligned bounding box) test is performed internally before precise hit detection. * @note This test does not take into account the results of blending or masking. * @note This test does take into account the the hidden paints as well. @see tvg_paint_set_visible(). * @since 1.0 */ TVG_API bool tvg_paint_intersects(Tvg_Paint paint, int32_t x, int32_t y, int32_t w, int32_t h); /** * @brief Retrieves the axis-aligned bounding box (AABB) of the paint object in canvas space. * * Returns the bounding box of the paint as an axis-aligned bounding box (AABB), with all relevant transformations applied. * The returned values @p x, @p y, @p w, @p h, may have invalid if the operation fails. Thus, please check the retval. * * This bounding box can be used to determine the actual rendered area of the object on the canvas, * for purposes such as hit-testing, culling, or layout calculations. * * @param[in] paint The Tvg_Paint object of which to get the bounds. * @param[out] x The x-coordinate of the upper-left corner of the bounding box. * @param[out] y The y-coordinate of the upper-left corner of the bounding box. * @param[out] w The width of the bounding box. * @param[out] h The height of the bounding box. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid @p paint. * @retval TVG_RESULT_INSUFFICIENT_CONDITION If it failed to compute the bounding box (mostly due to invalid path information). * * @see tvg_paint_get_obb() * @see tvg_canvas_update() */ TVG_API Tvg_Result tvg_paint_get_aabb(Tvg_Paint paint, float* x, float* y, float* w, float* h); /** * @brief Retrieves the object-oriented bounding box (OBB) of the paint object in canvas space. * * This function returns the bounding box of the paint, as an oriented bounding box (OBB) after transformations are applied. * The returned values @p pt4 may have invalid if the operation fails. Thus, please check the retval. * * This bounding box can be used to obtain the transformed bounding region in canvas space * by taking the geometry's axis-aligned bounding box (AABB) in the object's local coordinate space * and applying the object's transformations. * * @param[in] paint The Tvg_Paint object of which to get the bounds. * @param[out] pt4 An array of four points representing the bounding box. The array size must be 4. * * @retval TVG_RESULT_INVALID_ARGUMENT @p paint or @p pt4 is invalid. * @retval TVG_RESULT_INSUFFICIENT_CONDITION If it failed to compute the bounding box (mostly due to invalid path information). * * @see tvg_paint_get_aabb() * @see tvg_canvas_update() * * @since 1.0 */ TVG_API Tvg_Result tvg_paint_get_obb(Tvg_Paint paint, Tvg_Point* pt4); /** * @brief Sets the masking target object and the masking method. * * @param[in] paint The source object of the masking. * @param[in] target The target object of the masking. * @param[in] method The method used to mask the source object with the target. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION if the target has already belonged to another paint. * */ TVG_API Tvg_Result tvg_paint_set_mask_method(Tvg_Paint paint, Tvg_Paint target, Tvg_Mask_Method method); /** * @brief Gets the masking target object and the masking method. * * @param[in] paint The source object of the masking. * @param[out] target The target object of the masking. * @param[out] method The method used to mask the source object with the target. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. */ TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint paint, const Tvg_Paint target, Tvg_Mask_Method* method); /** * @brief Clip the drawing region of the paint object. * * This function restricts the drawing area of the paint object to the specified shape's paths. * * @param[in] paint The target object of the clipping. * @param[in] clipper The shape object as the clipper. * * @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. * @retval TVG_RESULT_INSUFFICIENT_CONDITION if the target has already belonged to another paint. * @retval TVG_RESULT_NOT_SUPPORTED If the @p clipper type is not Shape. * * @see tvg_paint_get_clip() * * @since 1.0 */ TVG_API Tvg_Result tvg_paint_set_clip(Tvg_Paint paint, Tvg_Paint clipper); /** * @brief Get the clipper shape of the paint object. * * This function returns the clipper that has been previously set to this paint object. * * @return The shape object used as the clipper, or @c nullptr if no clipper is set. * * @see tvg_paint_set_clip() * * @since 1.0 */ TVG_API Tvg_Paint tvg_paint_get_clip(const Tvg_Paint paint); /** * @brief Retrieves the parent paint object. * * This function returns a pointer to the parent object if the current paint * belongs to one. Otherwise, it returns @c nullptr. * * @param[in] paint The Tvg_Paint object of which to get the scene. * * @return A pointer to the parent object if available, otherwise @c nullptr. * * @see tvg_scene_add() * @see tvg_canvas_add() * * @since 1.0 */ TVG_API Tvg_Paint tvg_paint_get_parent(const Tvg_Paint paint); /** * @brief Gets the unique value of the paint instance indicating the instance type. * * @param[in] paint The Tvg_Paint object of which to get the type value. * @param[out] type The unique type of the paint instance type. * * @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. * * @since 1.0 */ TVG_API Tvg_Result tvg_paint_get_type(const Tvg_Paint paint, Tvg_Type* type); /** * @brief Sets the blending method for the paint object. * * The blending feature allows you to combine colors to create visually appealing effects, including transparency, lighting, shading, and color mixing, among others. * its process involves the combination of colors or images from the source paint object with the destination (the lower layer image) using blending operations. * The blending operation is determined by the chosen @p BlendMethod, which specifies how the colors or images are combined. * * @param[in] paint The Tvg_Paint object of which to set the blend method. * @param[in] method The blending method to be set. * * @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. * * @since 0.15 */ TVG_API Tvg_Result tvg_paint_set_blend_method(Tvg_Paint paint, Tvg_Blend_Method method); /** \} */ // end defgroup ThorVGCapi_Paint /** * @defgroup ThorVGCapi_Shape Shape * * @brief A module for managing two-dimensional figures and their properties. * * A shape has three major properties: shape outline, stroking, filling. The outline in the shape is retained as the path. * Path can be composed by accumulating primitive commands such as tvg_shape_move_to(), tvg_shape_line_to(), tvg_shape_cubic_to() or complete shape interfaces such as tvg_shape_append_rect(), tvg_shape_append_circle(), etc. * Path can consists of sub-paths. One sub-path is determined by a close command. * * The stroke of a shape is an optional property in case the shape needs to be represented with/without the outline borders. * It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context. * * \{ */ /************************************************************************/ /* Shape API */ /************************************************************************/ /** * @brief Creates a new Shape object. * * This function allocates and returns a new Shape instance. * To properly destroy the Shape object, use @ref tvg_paint_rel(). * * @return A pointer to the newly created Shape object. * * @see tvg_paint_rel() */ TVG_API Tvg_Paint tvg_shape_new(void); /** * @brief Resets the shape path properties. * * The color, the fill and the stroke properties are retained. * * @param[in] paint A Tvg_Paint pointer to the shape object. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note The memory, where the path data is stored, is not deallocated at this stage for caching effect. */ TVG_API Tvg_Result tvg_shape_reset(Tvg_Paint paint); /** * @brief Sets the initial point of the sub-path. * * The value of the current point is set to the given point. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] x The horizontal coordinate of the initial point of the sub-path. * @param[in] y The vertical coordinate of the initial point of the sub-path. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. */ TVG_API Tvg_Result tvg_shape_move_to(Tvg_Paint paint, float x, float y); /** * @brief Adds a new point to the sub-path, which results in drawing a line from the current point to the given end-point. * * The value of the current point is set to the given end-point. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] x The horizontal coordinate of the end-point of the line. * @param[in] y The vertical coordinate of the end-point of the line. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note In case this is the first command in the path, it corresponds to the tvg_shape_move_to() call. */ TVG_API Tvg_Result tvg_shape_line_to(Tvg_Paint paint, float x, float y); /** * @brief Adds new points to the sub-path, which results in drawing a cubic Bezier curve. * * The Bezier curve starts at the current point and ends at the given end-point (@p x, @p y). Two control points (@p cx1, @p cy1) and (@p cx2, @p cy2) are used to determine the shape of the curve. * The value of the current point is set to the given end-point. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] cx1 The horizontal coordinate of the 1st control point. * @param[in] cy1 The vertical coordinate of the 1st control point. * @param[in] cx2 The horizontal coordinate of the 2nd control point. * @param[in] cy2 The vertical coordinate of the 2nd control point. * @param[in] x The horizontal coordinate of the endpoint of the curve. * @param[in] y The vertical coordinate of the endpoint of the curve. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note In case this is the first command in the path, no data from the path are rendered. */ TVG_API Tvg_Result tvg_shape_cubic_to(Tvg_Paint paint, float cx1, float cy1, float cx2, float cy2, float x, float y); /** * @brief Closes the current sub-path by drawing a line from the current point to the initial point of the sub-path. * * The value of the current point is set to the initial point of the closed sub-path. * * @param[in] paint A Tvg_Paint pointer to the shape object. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note In case the sub-path does not contain any points, this function has no effect. */ TVG_API Tvg_Result tvg_shape_close(Tvg_Paint paint); /** * @brief Appends a rectangle to the path. * * The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments. * The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners. * * The position of the rectangle is specified by the coordinates of its upper-left corner - @p x and @p y arguments. * * The rectangle is treated as a new sub-path - it is not connected with the previous sub-path. * * The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater * than @p w/2 the current point is set to (@p x + @p w/2, @p y) * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] x The horizontal coordinate of the upper-left corner of the rectangle. * @param[in] y The vertical coordinate of the upper-left corner of the rectangle. * @param[in] w The width of the rectangle. * @param[in] h The height of the rectangle. * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle. * @param[in] ry The y-axis radius of the ellipse defining the rounded corners of the rectangle. * @param[in] cw Specifies the path direction: @c true for clockwise, @c false for counterclockwise. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse. */ TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint paint, float x, float y, float w, float h, float rx, float ry, bool cw); /** * @brief Appends an ellipse to the path. * * The position of the ellipse is specified by the coordinates of its center - @p cx and @p cy arguments. * * The ellipse is treated as a new sub-path - it is not connected with the previous sub-path. * * The value of the current point is set to (@p cx, @p cy - @p ry). * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] cx The horizontal coordinate of the center of the ellipse. * @param[in] cy The vertical coordinate of the center of the ellipse. * @param[in] rx The x-axis radius of the ellipse. * @param[in] ry The y-axis radius of the ellipse. * @param[in] cw Specifies the path direction: @c true for clockwise, @c false for counterclockwise. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. */ TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint paint, float cx, float cy, float rx, float ry, bool cw); /** * @brief Appends a given sub-path to the path. * * The current point value is set to the last point from the sub-path. * For each command from the @p cmds array, an appropriate number of points in @p pts array should be specified. * If the number of points in the @p pts array is different than the number required by the @p cmds array, the shape with this sub-path will not be displayed on the screen. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] cmds The array of the commands in the sub-path. * @param[in] cmdCnt The length of the @p cmds array. * @param[in] pts The array of the two-dimensional points. * @param[in] ptsCnt The length of the @p pts array. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument or @p cmdCnt or @p ptsCnt equal to zero. */ TVG_API Tvg_Result tvg_shape_append_path(Tvg_Paint paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt); /** * @brief Retrieves the current path data of the shape. * * This function provides access to the shape's path data, including the commands * and points that define the path. * * @param[out] cmds Pointer to the array of commands representing the path. * Can be @c nullptr if this information is not needed. * @param[out] cmdsCnt Pointer to the variable that receives the number of commands in the @p cmds array. * Can be @c nullptr if this information is not needed. * @param[out] pts Pointer to the array of two-dimensional points that define the path. * Can be @c nullptr if this information is not needed. * @param[out] ptsCnt Pointer to the variable that receives the number of points in the @p pts array. * Can be @c nullptr if this information is not needed. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note If any of the arguments are @c nullptr, that value will be ignored. */ TVG_API Tvg_Result tvg_shape_get_path(const Tvg_Paint paint, const Tvg_Path_Command** cmds, uint32_t* cmdsCnt, const Tvg_Point** pts, uint32_t* ptsCnt); /** * @brief Sets the stroke width for the path. * * This function defines the thickness of the stroke applied to all figures * in the path object. A stroke is the outline drawn along the edges of the * path's geometry. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] width The width of the stroke in pixels. Must be positive value. (The default is 0) * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note A value of @p width 0 disables the stroke. * * @see tvg_shape_set_stroke_color() */ TVG_API Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint paint, float width); /** * @brief Gets the shape's stroke width. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] width The stroke width. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. */ TVG_API Tvg_Result tvg_shape_get_stroke_width(const Tvg_Paint paint, float* width); /** * @brief Sets the shape's stroke color. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note If the stroke width is 0 (default), the stroke will not be visible regardless of the color. * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * * @see tvg_shape_set_stroke_width() * @see tvg_shape_set_stroke_gradient() */ TVG_API Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a); /** * @brief Gets the shape's stroke color. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] r The red color channel value in the range [0 ~ 255]. The default value is 0. * @param[out] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[out] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * @retval TVG_RESULT_INSUFFICIENT_CONDITION No stroke was set. */ TVG_API Tvg_Result tvg_shape_get_stroke_color(const Tvg_Paint paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a); /** * @brief Sets the gradient fill of the stroke for all of the figures from the path. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] grad The gradient fill. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * @retval TVG_RESULT_MEMORY_CORRUPTION An invalid Tvg_Gradient pointer or an error with accessing it. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * * @see tvg_shape_set_stroke_color() */ TVG_API Tvg_Result tvg_shape_set_stroke_gradient(Tvg_Paint paint, Tvg_Gradient grad); /** * @brief Gets the gradient fill of the shape's stroke. * * The function does not allocate any memory. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] grad The gradient fill. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. */ TVG_API Tvg_Result tvg_shape_get_stroke_gradient(const Tvg_Paint paint, Tvg_Gradient* grad); /** * @brief Sets the shape's stroke dash pattern. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] dashPattern An array of alternating dash and gap lengths. * @param[in] cnt The size of the @p dashPattern array. * @param[in] offset The shift of the starting point within the repeating dash pattern, from which the pattern begins to be applied. * * @retval TVG_RESULT_INVALID_ARGUMENT In case @p dashPattern is @c nullptr and @p cnt > 0 or @p dashPattern is not @c nullptr and @p cnt is zero. * * @note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt. * @note Values of @p dashPattern less than zero are treated as zero. * @note If all values in the @p dashPattern are equal to or less than 0, the dash is ignored. * @note If the @p dashPattern contains an odd number of elements, the sequence is repeated in the same * order to form an even-length pattern, preserving the alternation of dashes and gaps. * @since 1.0 */ TVG_API Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint paint, const float* dashPattern, uint32_t cnt, float offset); /** * @brief Gets the dash pattern of the stroke. * * The function does not allocate any memory. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] dashPattern The array of consecutive pair values of the dash length and the gap length. * @param[out] cnt The size of the @p dashPattern array. * @param[out] offset The shift of the starting point within the repeating dash pattern. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. * @since 1.0 */ TVG_API Tvg_Result tvg_shape_get_stroke_dash(const Tvg_Paint paint, const float** dashPattern, uint32_t* cnt, float* offset); /** * @brief Sets the cap style used for stroking the path. * * The cap style specifies the shape to be used at the end of the open stroked sub-paths. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] cap The cap style value. The default value is @c TVG_STROKE_CAP_SQUARE. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. */ TVG_API Tvg_Result tvg_shape_set_stroke_cap(Tvg_Paint paint, Tvg_Stroke_Cap cap); /** * @brief Gets the stroke cap style used for stroking the path. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] cap The cap style value. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. */ TVG_API Tvg_Result tvg_shape_get_stroke_cap(const Tvg_Paint paint, Tvg_Stroke_Cap* cap); /** * @brief Sets the join style for stroked path segments. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] join The join style value. The default value is @c TVG_STROKE_JOIN_BEVEL. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. */ TVG_API Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint paint, Tvg_Stroke_Join join); /** * @brief The function gets the stroke join method * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] join The join style value. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. */ TVG_API Tvg_Result tvg_shape_get_stroke_join(const Tvg_Paint paint, Tvg_Stroke_Join* join); /** * @brief Sets the stroke miterlimit. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join when the @c TVG_STROKE_JOIN_MITER join style is set. The default value is 4. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer or Unsupported @p miterlimit values (less than zero). * * @since 0.11 */ TVG_API Tvg_Result tvg_shape_set_stroke_miterlimit(Tvg_Paint paint, float miterlimit); /** * @brief The function gets the stroke miterlimit. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] miterlimit The stroke miterlimit. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. * * @since 0.11 */ TVG_API Tvg_Result tvg_shape_get_stroke_miterlimit(const Tvg_Paint paint, float* miterlimit); /** * @brief Sets the trim of the shape along the defined path segment, allowing control over which part of the shape is visible. * * If the values of the arguments @p begin and @p end exceed the 0-1 range, they are wrapped around in a manner similar to angle wrapping, effectively treating the range as circular. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] begin Specifies the start of the segment to display along the path. * @param[in] end Specifies the end of the segment to display along the path. * @param[in] simultaneous Determines how to trim multiple paths within a single shape. If set to @c true (default), trimming is applied simultaneously to all paths; * Otherwise, all paths are treated as a single entity with a combined length equal to the sum of their individual lengths and are trimmed as such. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @since 1.0 */ TVG_API Tvg_Result tvg_shape_set_trimpath(Tvg_Paint paint, float begin, float end, bool simultaneous); /** * @brief Sets the shape's solid color. * * The parts of the shape defined as inner are colored. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * @see tvg_shape_set_fill_rule() */ TVG_API Tvg_Result tvg_shape_set_fill_color(Tvg_Paint paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a); /** * @brief Gets the shape's solid color. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] r The red color channel value in the range [0 ~ 255]. The default value is 0. * @param[out] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[out] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. */ TVG_API Tvg_Result tvg_shape_get_fill_color(const Tvg_Paint paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a); /** * @brief Sets the fill rule for the shape. * * Specifies how the interior of the shape is determined when its path intersects itself. * The default fill rule is @c TVG_FILL_RULE_NON_ZERO. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] rule The fill rule to apply to the shape. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. */ TVG_API Tvg_Result tvg_shape_set_fill_rule(Tvg_Paint paint, Tvg_Fill_Rule rule); /** * @brief Retrieves the current fill rule used by the shape. * * This function returns the fill rule, which determines how the interior * regions of the shape are calculated when it overlaps itself. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] rule The current Tvg_Fill_Rule value of the shape. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. */ TVG_API Tvg_Result tvg_shape_get_fill_rule(const Tvg_Paint paint, Tvg_Fill_Rule* rule); /** * @brief Sets the rendering order of the stroke and the fill. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option). * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @since 0.10 */ TVG_API Tvg_Result tvg_shape_set_paint_order(Tvg_Paint paint, bool strokeFirst); /** * @brief Sets the gradient fill for all of the figures from the path. * * The parts of the shape defined as inner are filled. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[in] grad The gradient fill. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * @retval TVG_RESULT_MEMORY_CORRUPTION An invalid Tvg_Gradient pointer. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * @see tvg_shape_set_fill_rule() */ TVG_API Tvg_Result tvg_shape_set_gradient(Tvg_Paint paint, Tvg_Gradient grad); /** * @brief Gets the gradient fill of the shape. * * The function does not allocate any data. * * @param[in] paint A Tvg_Paint pointer to the shape object. * @param[out] grad The gradient fill. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. */ TVG_API Tvg_Result tvg_shape_get_gradient(const Tvg_Paint paint, Tvg_Gradient* grad); /** \} */ // end defgroup ThorVGCapi_Shape /** * @defgroup ThorVGCapi_Gradient Gradient * @brief A module managing the gradient fill of objects. * * The module enables to set and to get the gradient colors and their arrangement inside the gradient bounds, * to specify the gradient bounds and the gradient behavior in case the area defined by the gradient bounds * is smaller than the area to be filled. * * \{ */ /************************************************************************/ /* Gradient API */ /************************************************************************/ /** * @brief Creates a new linear gradient object. * * @return A new linear gradient object. */ TVG_API Tvg_Gradient tvg_linear_gradient_new(void); /** * @brief Creates a new radial gradient object. * * @return A new radial gradient object. */ TVG_API Tvg_Gradient tvg_radial_gradient_new(void); /** * @brief Sets the linear gradient bounds. * * The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing * the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking * (@p x1, @p y1) and (@p x2, @p y2). * * @param[in] grad The Tvg_Gradient object of which bounds are to be set. * @param[in] x1 The horizontal coordinate of the first point used to determine the gradient bounds. * @param[in] y1 The vertical coordinate of the first point used to determine the gradient bounds. * @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds. * @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. * * @note In case the first and the second points are equal, an object is filled with a single color using the last color specified in the tvg_gradient_set_color_stops(). * @see tvg_gradient_set_color_stops() */ TVG_API Tvg_Result tvg_linear_gradient_set(Tvg_Gradient grad, float x1, float y1, float x2, float y2); /** * @brief Gets the linear gradient bounds. * * The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing * the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking * (@p x1, @p y1) and (@p x2, @p y2). * * @param[in] grad The Tvg_Gradient object of which to get the bounds. * @param[out] x1 The horizontal coordinate of the first point used to determine the gradient bounds. * @param[out] y1 The vertical coordinate of the first point used to determine the gradient bounds. * @param[out] x2 The horizontal coordinate of the second point used to determine the gradient bounds. * @param[out] y2 The vertical coordinate of the second point used to determine the gradient bounds. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. */ TVG_API Tvg_Result tvg_linear_gradient_get(Tvg_Gradient grad, float* x1, float* y1, float* x2, float* y2); /** * @brief Sets the radial gradient attributes. * * The radial gradient is defined by the end circle with a center (@p cx, @p cy) and a radius @p r and * the start circle with a center/focal point (@p fx, @p fy) and a radius @p fr. * The gradient will be rendered such that the gradient stop at an offset of 100% aligns with the edge of the end circle * and the stop at an offset of 0% aligns with the edge of the start circle. * * @param[in] grad The Tvg_Gradient object of which bounds are to be set. * @param[in] cx The horizontal coordinate of the center of the end circle. * @param[in] cy The vertical coordinate of the center of the end circle. * @param[in] r The radius of the end circle. * @param[in] fx The horizontal coordinate of the center of the start circle. * @param[in] fy The vertical coordinate of the center of the start circle. * @param[in] fr The radius of the start circle. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer or the radius @p r or @p fr value is negative. * * @note In case the radius @p r is zero, an object is filled with a single color using the last color specified in the specified in the tvg_gradient_set_color_stops(). * @note In case the focal point (@p fx and @p fy) lies outside the end circle, it is projected onto the edge of the end circle. * @note If the start circle doesn't fully fit inside the end circle (after possible repositioning), the @p fr is reduced accordingly. * @note By manipulating the position and size of the focal point, a wide range of visual effects can be achieved, such as directing * the gradient focus towards a specific edge or enhancing the depth and complexity of shading patterns. * If a focal effect is not desired, simply align the focal point (@p fx and @p fy) with the center of the end circle (@p cx and @p cy) * and set the radius (@p fr) to zero. This will result in a uniform gradient without any focal variations. * * @see tvg_gradient_set_color_stops() */ TVG_API Tvg_Result tvg_radial_gradient_set(Tvg_Gradient grad, float cx, float cy, float r, float fx, float fy, float fr); /** * @brief The function gets radial gradient attributes. * * @param[in] grad The Tvg_Gradient object of which to get the gradient attributes. * @param[out] cx The horizontal coordinate of the center of the end circle. * @param[out] cy The vertical coordinate of the center of the end circle. * @param[out] r The radius of the end circle. * @param[out] fx The horizontal coordinate of the center of the start circle. * @param[out] fy The vertical coordinate of the center of the start circle. * @param[out] fr The radius of the start circle. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. * * @see tvg_radial_gradient_set() */ TVG_API Tvg_Result tvg_radial_gradient_get(Tvg_Gradient grad, float* cx, float* cy, float* r, float* fx, float* fy, float* fr); /** * @brief Sets the parameters of the colors of the gradient and their position. * * @param[in] grad The Tvg_Gradient object of which the color information is to be set. * @param[in] color_stop An array of Tvg_Color_Stop data structure. * @param[in] cnt The size of the @p color_stop array equal to the colors number used in the gradient. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. */ TVG_API Tvg_Result tvg_gradient_set_color_stops(Tvg_Gradient grad, const Tvg_Color_Stop* color_stop, uint32_t cnt); /** * @brief Gets the parameters of the colors of the gradient, their position and number * * The function does not allocate any memory. * * @param[in] grad The Tvg_Gradient object of which to get the color information. * @param[out] color_stop An array of Tvg_Color_Stop data structure. * @param[out] cnt The size of the @p color_stop array equal to the colors number used in the gradient. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. */ TVG_API Tvg_Result tvg_gradient_get_color_stops(const Tvg_Gradient grad, const Tvg_Color_Stop** color_stop, uint32_t* cnt); /** * @brief Sets the Tvg_Stroke_Fill value, which specifies how to fill the area outside the gradient bounds. * * @param[in] grad The Tvg_Gradient object. * @param[in] spread The FillSpread value. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. */ TVG_API Tvg_Result tvg_gradient_set_spread(Tvg_Gradient grad, const Tvg_Stroke_Fill spread); /** * @brief Gets the FillSpread value of the gradient object. * * @param[in] grad The Tvg_Gradient object. * @param[out] spread The FillSpread value. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. */ TVG_API Tvg_Result tvg_gradient_get_spread(const Tvg_Gradient grad, Tvg_Stroke_Fill* spread); /** * @brief Sets the matrix of the affine transformation for the gradient object. * * The augmented matrix of the transformation is expected to be given. * * @param[in] grad The Tvg_Gradient object to be transformed. * @param[in] m The 3x3 augmented matrix. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. */ TVG_API Tvg_Result tvg_gradient_set_transform(Tvg_Gradient grad, const Tvg_Matrix* m); /** * @brief Gets the matrix of the affine transformation of the gradient object. * * In case no transformation was applied, the identity matrix is set. * * @param[in] grad The Tvg_Gradient object of which to get the transformation matrix. * @param[out] m The 3x3 augmented matrix. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. */ TVG_API Tvg_Result tvg_gradient_get_transform(const Tvg_Gradient grad, Tvg_Matrix* m); /** * @brief Gets the unique value of the gradient instance indicating the instance type. * * @param[in] grad The Tvg_Gradient object of which to get the type value. * @param[out] type The unique type of the gradient instance type. * * @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. * * @since 1.0 */ TVG_API Tvg_Result tvg_gradient_get_type(const Tvg_Gradient grad, Tvg_Type* type); /** * @brief Duplicates the given Tvg_Gradient object. * * Creates a new object and sets its all properties as in the original object. * * @param[in] grad The Tvg_Gradient object to be copied. * * @return A copied Tvg_Gradient object if succeed, @c nullptr otherwise. */ TVG_API Tvg_Gradient tvg_gradient_duplicate(Tvg_Gradient grad); /** * @brief Deletes the given gradient object. * * @param[in] grad The gradient object to be deleted. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. */ TVG_API Tvg_Result tvg_gradient_del(Tvg_Gradient grad); /** \} */ // end defgroup ThorVGCapi_Gradient /** * @defgroup ThorVGCapi_Picture Picture * * @brief A module enabling to create and to load an image in one of the supported formats: svg, png, jpg, lottie and raw. * * * \{ */ /************************************************************************/ /* Picture API */ /************************************************************************/ /** * @brief Creates a new Picture object. * * This function allocates and returns a new Picture instance. * To properly destroy the Picture object, use @ref tvg_paint_rel(). * * @return A pointer to the newly created Picture object. * * @see tvg_paint_rel() */ TVG_API Tvg_Paint tvg_picture_new(void); /** * @brief Loads a picture data directly from a file. * * ThorVG efficiently caches the loaded data using the specified @p path as a key. * This means that loading the same file again will not result in duplicate operations; * instead, ThorVG will reuse the previously loaded picture data. * * @param[in] picture A Tvg_Paint pointer to the picture object. * @param[in] path The absolute path to the image file. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer or an empty @p path. * @retval TVG_RESULT_NOT_SUPPORTED A file with an unknown extension. */ TVG_API Tvg_Result tvg_picture_load(Tvg_Paint picture, const char* path); /** * @brief Loads raw image data in a specific format from a memory block of the given size. * * ThorVG efficiently caches the loaded data, using the provided @p data address as a key * when @p copy is set to @c false. This allows ThorVG to avoid redundant operations * by reusing the previously loaded picture data for the same sharable @p data, * rather than duplicating the load process. * * @param[in] picture A Tvg_Paint pointer to the picture object. * @param[in] data A pointer to the memory block where the raw image data is stored. * @param[in] w The width of the image in pixels. * @param[in] h The height of the image in pixels. * @param[in] cs Specifies how the 32-bit color values should be interpreted (read/write). * @param[in] copy If @c true, the data is copied into the engine's local buffer. If @c false, the data is not copied. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer or no data are provided or the @p w or @p h value is zero or less. * * @since 0.9 */ TVG_API Tvg_Result tvg_picture_load_raw(Tvg_Paint picture, const uint32_t *data, uint32_t w, uint32_t h, Tvg_Colorspace cs, bool copy); /** * @brief Loads a picture data from a memory block of a given size. * * ThorVG efficiently caches the loaded data using the specified @p data address as a key * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations * for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data. * * @param[in] picture A Tvg_Paint pointer to the picture object. * @param[in] data A pointer to a memory location where the content of the picture file is stored. A null-terminated string is expected for non-binary data if @p copy is @c false * @param[in] size The size in bytes of the memory occupied by the @p data. * @param[in] mimetype Mimetype or extension of data such as "jpg", "jpeg", "svg", "svg+xml", "lot", "lottie+json", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one. * @param[in] rpath A resource directory path, if the @p data needs to access any external resources. * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. * * @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument or the @p size is zero or less. * @retval TVG_RESULT_NOT_SUPPORTED A file with an unknown extension. * * @warning: It's the user responsibility to release the @p data memory if the @p copy is @c true. */ TVG_API Tvg_Result tvg_picture_load_data(Tvg_Paint picture, const char *data, uint32_t size, const char *mimetype, const char* rpath, bool copy); /** * @brief Sets the asset resolver callback for handling external resources (e.g., images and fonts). * * This callback is invoked when an external asset reference (such as an image source or file path) * is encountered in a Picture object. It allows the user to provide a custom mechanism for loading * or substituting assets, such as loading from an external source or a virtual filesystem. * * @param[in] resolver A user-defined function that handles the resolution of asset paths. * The function should return @c true if the asset was successfully resolved by the user, or @c false if it was not. * @param[in] data A pointer to user-defined data that will be passed to the callback each time it is invoked. * This can be used to maintain context or access external resources. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the @p picture argument. * @retval TVG_RESULT_INSUFFICIENT_CONDITION If the @p picture is already loaded. * * @note This function must be called before @ref tvg_picture_load() * Setting the resolver after loading will have no effect on asset resolution for that asset. * @note If @c false is returned by @p resolver, ThorVG will attempt to resolve the resource using its internal resolution mechanism as a fallback. * @note To unset the resolver, pass @c nullptr as the @p resolver parameter. * @note Experimental API * * @see Tvg_Picture_Asset_Resolver */ TVG_API Tvg_Result tvg_picture_set_asset_resolver(Tvg_Paint picture, Tvg_Picture_Asset_Resolver resolver, void* data); /** * @brief Resizes the picture content to the given width and height. * * The picture content is resized while keeping the default size aspect ratio. * The scaling factor is established for each of dimensions and the smaller value is applied to both of them. * * @param[in] picture A Tvg_Paint pointer to the picture object. * @param[in] w A new width of the image in pixels. * @param[in] h A new height of the image in pixels. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. */ TVG_API Tvg_Result tvg_picture_set_size(Tvg_Paint picture, float w, float h); /** * @brief Gets the size of the loaded picture. * * @param[in] picture A Tvg_Paint pointer to the picture object. * @param[out] w A width of the image in pixels. * @param[out] h A height of the image in pixels. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. */ TVG_API Tvg_Result tvg_picture_get_size(const Tvg_Paint picture, float* w, float* h); /** * @brief Sets the normalized origin point of the Picture object. * * This method defines the origin point of the Picture using normalized coordinates. * Unlike a typical pivot point used only for transformations, this origin affects both * the transformation behavior and the actual rendering position of the Picture. * * The specified origin becomes the reference point for positioning the Picture on the canvas. * For example, setting the origin to (0.5f, 0.5f) moves the visual center of the picture * to the position specified by Paint::translate(). * * The coordinates are given in a normalized range relative to the picture's bounds: * - (0.0f, 0.0f): top-left corner * - (0.5f, 0.5f): center * - (1.0f, 1.0f): bottom-right corner * * @param[in] picture A Tvg_Paint pointer to the picture object. * @param[in] x The normalized x-coordinate of the origin point (range: 0.0f to 1.0f). * @param[in] y The normalized y-coordinate of the origin point (range: 0.0f to 1.0f). * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @note This origin directly affects how the Picture is placed on the canvas when using * transformations such as translate(), rotate(), or scale(). * * @see tvg_paint_translate() * @see tvg_paint_rotate() * @see tvg_paint_scale() * @see tvg_paint_set_transform() * @see tvg_picture_get_origin() * * @since 1.0 */ TVG_API Tvg_Result tvg_picture_set_origin(Tvg_Paint picture, float x, float y); /** * @brief Gets the normalized origin point of the Picture object. * * This method retrieves the current origin point of the Picture, expressed * in normalized coordinates relative to the picture’s bounds. * * @param[in] picture A Tvg_Paint pointer to the picture object. * @param[out] x The normalized x-coordinate of the origin (range: 0.0f to 1.0f). * @param[out] y The normalized y-coordinate of the origin (range: 0.0f to 1.0f). * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * * @see tvg_picture_set_origin() * @since 1.0 */ TVG_API Tvg_Result tvg_picture_get_origin(const Tvg_Paint picture, float* x, float* y); /** * @brief Retrieve a paint object from the Picture scene by its Unique ID. * * This function searches for a paint object within the Picture scene that matches the provided @p id. * * @param[in] picture A Tvg_Paint pointer to the picture object. * @param[in] id The Unique ID of the paint object. * * @return A pointer to the paint object that matches the given identifier, or @c nullptr if no matching paint object is found. * * @see tvg_accessor_generate_id() * @since 1.0 */ TVG_API const Tvg_Paint tvg_picture_get_paint(Tvg_Paint picture, uint32_t id); /** \} */ // end defgroup ThorVGCapi_Picture /** * @defgroup ThorVGCapi_Scene Scene * @brief A module managing the multiple paints as one group paint. * * As a group, scene can be transformed, translucent, composited with other target paints, * its children will be affected by the scene world. * * \{ */ /************************************************************************/ /* Scene API */ /************************************************************************/ /** * @brief Creates a new Scene object. * * This function allocates and returns a new Scene instance. * To properly destroy the Scene object, use @ref tvg_paint_rel(). * * @return A pointer to the newly created Scene object. * * @see tvg_paint_rel() */ TVG_API Tvg_Paint tvg_scene_new(void); /** * @brief Adds a paint object to the scene. * * Appends the specified paint object to the given scene. Only paint objects * added to the scene are considered rendering targets. * * @param[in] scene A handle to the scene object. * This parameter must not be @c nullptr. * @param[in] paint A handle to the paint object to be added to the scene. * This parameter must not be @c nullptr. * * @note Ownership of the @p paint object is transferred to the canvas upon * successful addition. To retain ownership, call @ref tvg_paint_ref() * before adding it to the scene. * @note The rendering order of paint objects follows their order in the root * scene. If layering is required, ensure the paints are added in the * desired order. * * @see tvg_scene_insert() * @see tvg_scene_remove() * * @since 1.0 */ TVG_API Tvg_Result tvg_scene_add(Tvg_Paint scene, Tvg_Paint paint); /** * @brief Inserts a paint object into the scene. * * Inserts the specified paint object into the scene immediately before the * given paint object @p at. The @p at parameter must reference an existing * paint object already added to the scene. * * @param[in] scene A handle to the scene object. * This parameter must not be @c nullptr. * @param[in] target A handle to the paint object to be inserted into the scene. * This parameter must not be @c nullptr. * @param[in] at A handle to an existing paint object in the scene before * which @p target will be inserted. * This parameter must not be @c nullptr. * * @note Ownership of the @p target object is transferred to the scene upon * successful addition. To retain ownership, call @ref tvg_paint_ref() * before adding it to the scene. * @note The rendering order of paint objects follows their order in the root * scene. If layering is required, ensure the paints are added in the * desired order. * * @see tvg_scene_add() * @see tvg_scene_remove() * * @since 1.0 */ TVG_API Tvg_Result tvg_scene_insert(Tvg_Paint scene, Tvg_Paint target, Tvg_Paint at); /** * @brief Removes a paint object from the scene. * * This function removes a specified paint object from the scene. If no paint * object is specified (i.e., the default @c nullptr is used), the function * performs to clear all paints from the scene. * * @param[in] scene A Tvg_Paint pointer to the scene object. * @param[in] paint A pointer to the Paint object to be removed from the scene. * If @c nullptr, remove all the paints from the scene. * * @see tvg_scene_add() * @since 1.0 */ TVG_API Tvg_Result tvg_scene_remove(Tvg_Paint scene, Tvg_Paint paint); /** * @brief Clears all previously applied scene effects. * * This function clears all effects that have been applied to the scene, * restoring it to its original state without any post-processing. * * @param[in] scene A pointer to the Tvg_Paint scene object. * * @since 1.0 */ TVG_API Tvg_Result tvg_scene_clear_effects(Tvg_Paint scene); /** * @brief Adds a Gaussian blur effect to the scene. * * This function adds a Gaussian blur filter to the scene as a post-processing effect. * The blur can be applied in different directions with configurable border handling and quality settings. * * @param[in] scene A pointer to the Tvg_Paint scene object. * @param[in] sigma The blur radius (sigma) value. Must be greater than 0. * @param[in] direction Blur direction: 0 = both directions, 1 = horizontal only, 2 = vertical only. * @param[in] border Border handling method: 0 = duplicate, 1 = wrap. * @param[in] quality Blur quality level [0 - 100]. * * @since 1.0 */ TVG_API Tvg_Result tvg_scene_add_effect_gaussian_blur(Tvg_Paint scene, double sigma, int direction, int border, int quality); /** * @brief Adds a drop shadow effect to the scene. * * This function adds a drop shadow with a Gaussian blur to the scene. The shadow * can be customized using color, opacity, angle, distance, blur radius (sigma), * and quality parameters. * * @param[in] scene A pointer to the Tvg_Paint scene object. * @param[in] r Red channel value of the shadow color [0 - 255]. * @param[in] g Green channel value of the shadow color [0 - 255]. * @param[in] b Blue channel value of the shadow color [0 - 255]. * @param[in] a Alpha (opacity) channel value of the shadow [0 - 255]. * @param[in] angle Shadow direction in degrees [0 - 360]. * @param[in] distance Distance of the shadow from the original object. * @param[in] sigma Gaussian blur sigma value for the shadow. Must be > 0. * @param[in] quality Blur quality level [0 - 100]. * * @since 1.0 */ TVG_API Tvg_Result tvg_scene_add_effect_drop_shadow(Tvg_Paint scene, int r, int g, int b, int a, double angle, double distance, double sigma, int quality); /** * @brief Adds a fill color effect to the scene. * * This function overrides the scene's content colors with the specified fill color. * * @param[in] scene A pointer to the Tvg_Paint scene object. * @param[in] r Red color channel value [0 - 255]. * @param[in] g Green color channel value [0 - 255]. * @param[in] b Blue color channel value [0 - 255]. * @param[in] a Alpha (opacity) channel value [0 - 255]. * * @since 1.0 */ TVG_API Tvg_Result tvg_scene_add_effect_fill(Tvg_Paint scene, int r, int g, int b, int a); /** * @brief Adds a tint effect to the scene. * * This function tints the current scene using specified black and white color values, * modulated by a given intensity. * * @param[in] scene A pointer to the Tvg_Paint scene object. * @param[in] black_r Red component of the black color [0 - 255]. * @param[in] black_g Green component of the black color [0 - 255]. * @param[in] black_b Blue component of the black color [0 - 255]. * @param[in] white_r Red component of the white color [0 - 255]. * @param[in] white_g Green component of the white color [0 - 255]. * @param[in] white_b Blue component of the white color [0 - 255]. * @param[in] intensity Tint intensity value [0 - 100]. * * @since 1.0 */ TVG_API Tvg_Result tvg_scene_add_effect_tint(Tvg_Paint scene, int black_r, int black_g, int black_b, int white_r, int white_g, int white_b, double intensity); /** * @brief Adds a tritone color effect to the scene. * * This function adds a tritone color effect to the given scene using three sets of RGB values * representing shadow, midtone, and highlight colors. * * @param[in] scene A pointer to the Tvg_Paint scene object. * @param[in] shadow_r Red component of the shadow color [0 - 255]. * @param[in] shadow_g Green component of the shadow color [0 - 255]. * @param[in] shadow_b Blue component of the shadow color [0 - 255]. * @param[in] midtone_r Red component of the midtone color [0 - 255]. * @param[in] midtone_g Green component of the midtone color [0 - 255]. * @param[in] midtone_b Blue component of the midtone color [0 - 255]. * @param[in] highlight_r Red component of the highlight color [0 - 255]. * @param[in] highlight_g Green component of the highlight color [0 - 255]. * @param[in] highlight_b Blue component of the highlight color [0 - 255]. * @param[in] blend A blending factor that determines the mix between the original color and the tritone colors [0 - 255]. * * @since 1.0 */ TVG_API Tvg_Result tvg_scene_add_effect_tritone(Tvg_Paint scene, int shadow_r, int shadow_g, int shadow_b, int midtone_r, int midtone_g, int midtone_b, int highlight_r, int highlight_g, int highlight_b, int blend); /** \} */ // end defgroup ThorVGCapi_Scene /** * @defgroup ThorVGCapi_Text Text * @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text. * * @since 0.15 * * \{ */ /************************************************************************/ /* Text API */ /************************************************************************/ /** * @brief Creates a new Text object. * * This function allocates and returns a new Text instance. * To properly destroy the Text object, use @ref tvg_paint_rel(). * * @return A pointer to the newly created Text object. * * @see tvg_paint_rel() * * @since 0.15 */ TVG_API Tvg_Paint tvg_text_new(void); /** * @brief Sets the font family for the text. * * This function specifies the name of the font to be used when rendering text. * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] name The name of the font. This should match a font available through the canvas backend. * If set to @c nullptr, ThorVG will attempt to select a fallback font available on the engine. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the @p paint argument. * @retval TVG_RESULT_INSUFFICIENT_CONDITION The specified @p name cannot be found. * * @note This function only sets the font family name. Use @ref size() to define the font size. * @note If the @p name is not specified, ThorVG will select an available fallback font. * * @see tvg_text_set_size() * @see tvg_font_load() * * @since 1.0 */ TVG_API Tvg_Result tvg_text_set_font(Tvg_Paint text, const char* name); /** * @brief Sets the font size for the text. * * This function sets the font size used during text rendering. * The size is specified in point units, and supports floating-point precision * for smooth scaling and animation effects. * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] size The font size in points. Must be greater than 0.0. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the @p paint argument. * @retval TVG_RESULT_INVALID_ARGUMENT if the @p size is less than or equal to 0. * * @note Use this function in combination with @ref font() to fully define text appearance. * @note Fractional sizes (e.g., 12.5) are supported for sub-pixel rendering and animations. * * @see tvg_text_set_font() * * @since 1.0 */ TVG_API Tvg_Result tvg_text_set_size(Tvg_Paint text, float size); /** * @brief Assigns the given unicode text to be rendered. * * This function sets the unicode text that will be displayed by the rendering system. * The text is set according to the specified UTF encoding method, which defaults to UTF-8. * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] utf8 The multi-byte text encoded with utf8 string to be rendered. * * @since 1.0 */ TVG_API Tvg_Result tvg_text_set_text(Tvg_Paint text, const char* utf8); /** * @brief Sets text alignment or anchor per axis. * * If layout width/height is set on an axis, align within the layout box. * Otherwise, treat it as an anchor within the text bounds which point of * the text box is pinned to the paint position. * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] x Horizontal alignment/anchor in [0..1]: 0=left/start, 0.5=center, 1=right/end. (Default is 0) * @param[in] y Vertical alignment/anchor in [0..1]: 0=top, 0.5=middle, 1=bottom. (Default is 0) * * @since 1.0 * * @see tvg_text_layout() */ TVG_API Tvg_Result tvg_text_align(Tvg_Paint text, float x, float y); /** * @brief Sets the virtual layout box (constraints) for the text. * * If width/height is set on an axis, that axis is constrained by a virtual layout box and * the text may wrap/align inside it. If width/height == 0, the axis is * unconstrained and @ref tvg_text_align() acts as an anchor on that axis. * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] w Layout width in user space. Use 0 for no horizontal constraint. (Default is 0) * @param[in] h Layout height in user space. Use 0 for no vertical constraint. (Default is 0) * * @note This defines constraints only; alignment/anchoring is controlled by @ref align(). * @since 1.0 * * @see tvg_text_align() * @see tvg_text_spacing() */ TVG_API Tvg_Result tvg_text_layout(Tvg_Paint text, float w, float h); /** * @brief Sets the text wrapping mode for this text object. * * This method controls how the text is laid out when it exceeds the available space. * The wrapping mode determines whether text is truncated, wrapped by character or word, * or adjusted automatically. An ellipsis mode is also available for truncation with "...". * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] mode The wrapping strategy to apply. Default is @c TVG_TEXT_WRAP_NONE. * * @see Tvg_Text_Wrap * @since 1.0 */ TVG_API Tvg_Result tvg_text_wrap_mode(Tvg_Paint text, Tvg_Text_Wrap mode); /** * @brief Set the spacing scale factors for text layout. * * This function adjusts the letter spacing (horizontal space between glyphs) and * line spacing (vertical space between lines of text) using scale factors. * * Both values are relative to the font's default metrics: * - The letter spacing is applied as a scale factor to the glyph's advance width. * - The line spacing is applied as a scale factor to the glyph's advance height. * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] letter The scale factor for letter spacing. * Values > 1.0 increase spacing, values < 1.0 decrease it. * Must be greater than or equal to 0.0. (default: 1.0) * * @param[in] line The scale factor for line spacing. * Values > 1.0 increase line spacing, values < 1.0 decrease it. * Must be greater than or equal to 0.0. (default: 1.0) * * @since 1.0 */ TVG_API Tvg_Result tvg_text_spacing(Tvg_Paint text, float letter, float line); /** * @brief Apply an italic (slant) transformation to the text. * * This function applies a shear transformation to simulate an italic (oblique) style * for the current text object. The shear factor determines the degree of slant * applied along the X-axis. * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] shear The shear factor to apply. A value of 0.0 applies no slant, while values around 0.5 result in a strong slant. * Must be in the range [0.0, 0.5]. Recommended value is 0.18. * * @note The @p shear factor will be clamped to the valid range if it exceeds the limits. * @note This does not require the font itself to be italic. * It visually simulates the effect by applying a transformation matrix. * * @warning Excessive slanting may cause visual distortion depending on the font and size. * * @see tvg_text_set_font() * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the @p paint argument. * * @since 1.0 */ TVG_API Tvg_Result tvg_text_set_italic(Tvg_Paint text, float shear); /** * @brief Sets an outline (stroke) around the text object. * * This function adds an outline to the text with the specified width and RGB color. * The outline enhances the visibility of the text by rendering a stroke around its glyphs. * * @param[in] text A Tvg_Paint pointer to the text object. * @param width The width of the outline. Must be positive value. (The default is 0) * @param r Red component of the outline color (0–255). * @param g Green component of the outline color (0–255). * @param b Blue component of the outline color (0–255). * * @note To disable the outline, set @p width to 0. * @see tvg_text_set_fill_color() to set the main text fill color. * * @since 1.0 */ TVG_API Tvg_Result tvg_text_set_outline(Tvg_Paint text, float width, uint8_t r, uint8_t g, uint8_t b); /** * @brief Sets the text solid color. * * @param[in] paint A Tvg_Paint pointer to the text object. * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the @p paint argument. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * * @see tvg_text_set_font() * @see tvg_text_set_outline() * * @since 0.15 */ TVG_API Tvg_Result tvg_text_set_color(Tvg_Paint text, uint8_t r, uint8_t g, uint8_t b); /** * @brief Sets the gradient fill for the text. * * @param[in] text A Tvg_Paint pointer to the text object. * @param[in] grad The linear or radial gradient fill * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the @p paint argument. * @retval TVG_RESULT_MEMORY_CORRUPTION An invalid Tvg_Gradient pointer. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * @see tvg_text_set_font() * * @since 0.15 */ TVG_API Tvg_Result tvg_text_set_gradient(Tvg_Paint text, Tvg_Gradient gradient); /** * @brief Retrieves the layout metrics of the text object. * * Fills the provided `Tvg_Text_Metrics` structure with the font layout values of this text object, * such as ascent, descent, linegap, and line advance. * * The returned values reflect the font size applied to the text object, * but do not include any transformations (e.g., scale, rotation, or translation). * * @param[in] text A Tvg_Paint pointer to the text object. * @param[out] metrics A reference to a `Tvg_Text_Metrics` structure to be filled with the resulting values. * * @return TVG_RESULT_INSUFFICIENT_CONDITION if no font or size has been set yet. * * @see Tvg_Text_Metrics * @note Experimental API */ TVG_API Tvg_Result tvg_text_get_metrics(const Tvg_Paint text, Tvg_Text_Metrics* metrics); /** * @brief Loads a scalable font data from a file. * * ThorVG efficiently caches the loaded data using the specified @p path as a key. * This means that loading the same file again will not result in duplicate operations; * instead, ThorVG will reuse the previously loaded font data. * * @param[in] path The path to the font file. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid @p path passed as an argument. * @retval TVG_RESULT_NOT_SUPPORTED When trying to load a file with an unknown extension. * * @see tvg_font_unload() * * @since 0.15 */ TVG_API Tvg_Result tvg_font_load(const char* path); /** * @brief Loads a scalable font data from a memory block of a given size. * * ThorVG efficiently caches the loaded font data using the specified @p name as a key. * This means that loading the same fonts again will not result in duplicate operations. * Instead, ThorVG will reuse the previously loaded font data. * * @param[in] name The name under which the font will be stored and accessible (e.x. in a @p tvg_text_set_font API). * @param[in] data A pointer to a memory location where the content of the font data is stored. * @param[in] size The size in bytes of the memory occupied by the @p data. * @param[in] mimetype Mimetype or extension of font data. In case a @c nullptr or an empty "" value is provided the loader will be determined automatically. * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not (default). * * @retval TVG_RESULT_INVALID_ARGUMENT If no name is provided or if @p size is zero while @p data points to a valid memory location. * @retval TVG_RESULT_NOT_SUPPORTED When trying to load a file with an unknown extension. * @retval TVG_RESULT_INSUFFICIENT_CONDITION When trying to unload the font data that has not been previously loaded. * * @warning: It's the user responsibility to release the @p data memory. * * @note To unload the font data loaded using this API, pass the proper @p name and @c nullptr as @p data. * * @since 0.15 */ TVG_API Tvg_Result tvg_font_load_data(const char* name, const char* data, uint32_t size, const char *mimetype, bool copy); /** * @brief Unloads the specified scalable font data that was previously loaded. * * This function is used to release resources associated with a font file that has been loaded into memory. * * @param[in] path The path to the loaded font file. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION The loader is not initialized. * * @note If the font data is currently in use, it will not be immediately unloaded. * @see tvg_font_load() * * @since 0.15 */ TVG_API Tvg_Result tvg_font_unload(const char* path); /** \} */ // end defgroup ThorVGCapi_Text /** * @defgroup ThorVGCapi_Saver Saver * @brief A module for exporting a paint object into a specified file. * * The module enables to save the composed scene and/or image from a paint object. * Once it's successfully exported to a file, it can be recreated using the Picture module. * * \{ */ /************************************************************************/ /* Saver API */ /************************************************************************/ /** * @brief Creates a new Tvg_Saver object. * * @return A new Tvg_Saver object. */ TVG_API Tvg_Saver tvg_saver_new(void); /** * @brief Exports the given @p paint data to the given @p path * * If the saver module supports any compression mechanism, it will optimize the data size. * This might affect the encoding/decoding time in some cases. You can turn off the compression * if you wish to optimize for speed. * * @param[in] saver The Tvg_Saver object connected with the saving task. * @param[in] paint The paint to be saved with all its associated properties. * @param[in] path A path to the file, in which the paint data is to be saved. * @param[in] quality The encoded quality level. @c 0 is the minimum, @c 100 is the maximum value(recommended). * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. * @retval TVG_RESULT_INSUFFICIENT_CONDITION Currently saving other resources. * @retval TVG_RESULT_NOT_SUPPORTED Trying to save a file with an unknown extension or in an unsupported format. * @retval TVG_RESULT_UNKNOWN An empty paint is to be saved. * * @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call tvg_saver_sync() afterwards. * @see tvg_saver_sync() */ TVG_API Tvg_Result tvg_saver_save_paint(Tvg_Saver saver, Tvg_Paint paint, const char* path, uint32_t quality); /** * @brief Exports the given @p animation data to the given @p path * * If the saver module supports any compression mechanism, it will optimize the data size. * This might affect the encoding/decoding time in some cases. You can turn off the compression * if you wish to optimize for speed. * * @param[in] saver The Tvg_Saver object connected with the saving task. * @param[in] animation The animation to be saved with all its associated properties. * @param[in] path A path to the file, in which the animation data is to be saved. * @param[in] quality The encoded quality level. @c 0 is the minimum, @c 100 is the maximum value(recommended). * @param[in] fps The frames per second for the animation. If @c 0, the default fps is used. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. * @retval TVG_RESULT_INSUFFICIENT_CONDITION Currently saving other resources or animation has no frames. * @retval TVG_RESULT_NOT_SUPPORTED Trying to save a file with an unknown extension or in an unsupported format. * @retval TVG_RESULT_UNKNOWN Unknown if attempting to save an empty paint. * * @note A higher frames per second (FPS) would result in a larger file size. It is recommended to use the default value. * @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call tvg_saver_sync() afterwards. * * @see tvg_saver_sync() * * @since 1.0 */ TVG_API Tvg_Result tvg_saver_save_animation(Tvg_Saver saver, Tvg_Animation animation, const char* path, uint32_t quality, uint32_t fps); /** * @brief Guarantees that the saving task is finished. * * The behavior of the Saver module works on a sync/async basis, depending on the threading setting of the Initializer. * Thus, if you wish to have a benefit of it, you must call tvg_saver_sync() after the tvg_saver_save_paint() in the proper delayed time. * Otherwise, you can call tvg_saver_sync() immediately. * * @param[in] saver The Tvg_Saver object connected with the saving task. * * @retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. * @retval TVG_RESULT_INSUFFICIENT_CONDITION No saving task is running. * * @note The asynchronous tasking is dependent on the Saver module implementation. * @see tvg_saver_save_paint() */ TVG_API Tvg_Result tvg_saver_sync(Tvg_Saver saver); /** * @brief Deletes the given Tvg_Saver object. * * @param[in] saver The Tvg_Saver object to be deleted. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Saver pointer. */ TVG_API Tvg_Result tvg_saver_del(Tvg_Saver saver); /** \} */ // end defgroup ThorVGCapi_Saver /** * @defgroup ThorVGCapi_Animation Animation * @brief A module for manipulation of animatable images. * * The module supports the display and control of animation frames. * * \{ */ /************************************************************************/ /* Animation API */ /************************************************************************/ /** * @brief Creates a new Animation object. * * @return Tvg_Animation A new Tvg_Animation object. * * @since 0.13 */ TVG_API Tvg_Animation tvg_animation_new(void); /** * @brief Specifies the current frame in the animation. * * @param[in] animation A Tvg_Animation pointer to the animation object. * @param[in] no The index of the animation frame to be displayed. The index should be less than the tvg_animation_get_total_frame(). * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer. * @retval TVG_RESULT_INSUFFICIENT_CONDITION if the given @p no is the same as the current frame value. * @retval TVG_RESULT_NOT_SUPPORTED The picture data does not support animations. * * @note For efficiency, ThorVG ignores updates to the new frame value if the difference from the current frame value * is less than 0.001. In such cases, it returns @c Result::InsufficientCondition. * Values less than 0.001 may be disregarded and may not be accurately retained by the Animation. * @see tvg_animation_get_total_frame() * * @since 0.13 */ TVG_API Tvg_Result tvg_animation_set_frame(Tvg_Animation animation, float no); /** * @brief Retrieves a picture instance associated with this animation instance. * * This function provides access to the picture instance that can be used to load animation formats, such as lot. * After setting up the picture, it can be added to the designated canvas, enabling control over animation frames * with this Animation instance. * * @param[in] animation A Tvg_Animation pointer to the animation object. * * @return A picture instance that is tied to this animation. * * @warning The picture instance is owned by Animation. It should not be deleted manually. * * @since 0.13 */ TVG_API Tvg_Paint tvg_animation_get_picture(Tvg_Animation animation); /** * @brief Retrieves the current frame number of the animation. * * @param[in] animation A Tvg_Animation pointer to the animation object. * @param[in] no The current frame number of the animation, between 0 and totalFrame() - 1. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer or @p no * * @see tvg_animation_get_total_frame() * @see tvg_animation_set_frame() * * @since 0.13 */ TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation animation, float* no); /** * @brief Retrieves the total number of frames in the animation. * * @param[in] animation A Tvg_Animation pointer to the animation object. * @param[in] cnt The total number of frames in the animation. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer or @p cnt. * * @note Frame numbering starts from 0. * @note If the Picture is not properly configured, this function will return 0. * * @since 0.13 */ TVG_API Tvg_Result tvg_animation_get_total_frame(Tvg_Animation animation, float* cnt); /** * @brief Retrieves the duration of the animation in seconds. * * @param[in] animation A Tvg_Animation pointer to the animation object. * @param[in] duration The duration of the animation in seconds. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer or @p duration. * * @note If the Picture is not properly configured, this function will return 0. * * @since 0.13 */ TVG_API Tvg_Result tvg_animation_get_duration(Tvg_Animation animation, float* duration); /** * @brief Specifies the playback segment of the animation. * * The set segment is designated as the play area of the animation. * This is useful for playing a specific segment within the entire animation. * After setting, the number of animation frames and the playback time are calculated * by mapping the playback segment as the entire range. * * @param[in] animation The Tvg_Animation pointer to the animation object. * @param[in] begin segment begin frame. * @param[in] end segment end frame. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded. * @retval TVG_RESULT_INVALID_ARGUMENT If the @p begin is higher than @p end. * * @note Animation allows a range from 0.0 to the total frame. @p end should not be higher than @p begin. * @note If a marker has been specified, its range will be disregarded. * * @see tvg_lottie_animation_set_marker() * @see tvg_animation_get_total_frame() * * @since 1.0 */ TVG_API Tvg_Result tvg_animation_set_segment(Tvg_Animation animation, float begin, float end); /** * @brief Gets the current segment range information. * * @param[in] animation The Tvg_Animation pointer to the animation object. * @param[out] begin segment begin frame. * @param[out] end segment end frame. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded. * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer. * * @since 1.0 */ TVG_API Tvg_Result tvg_animation_get_segment(Tvg_Animation animation, float* begin, float* end); /** * @brief Deletes the given Tvg_Animation object. * * @param[in] animation The Tvg_Animation object to be deleted. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer. * * @since 0.13 */ TVG_API Tvg_Result tvg_animation_del(Tvg_Animation animation); /** \} */ // end defgroup ThorVGCapi_Animation /** * @defgroup ThorVGCapi_Accesssor Accessor * @brief A module for manipulation of the scene tree * * This module helps to control the scene tree. * \{ */ /************************************************************************/ /* Accessor API */ /************************************************************************/ /** * @brief Creates a new accessor object. * * @return A new accessor object. * * @since 1.0 */ TVG_API Tvg_Accessor tvg_accessor_new(void); /** * @brief Deletes the given accessor object. * * @param[in] accessor The accessor object to be deleted. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Accessor pointer. * * @since 1.0 */ TVG_API Tvg_Result tvg_accessor_del(Tvg_Accessor accessor); /** * @brief Sets the paint of the accessor then iterates through its descendents. * * Iterates through all descendents of the scene passed through the paint argument * while calling func on each and passing the data pointer to this function. When * func returns false iteration stops and the function returns. * * @param[in] accessor A Tvg_Accessor pointer to the accessor object. * @param[in] paint A Tvg_Paint pointer to the scene object. * @param[in] func A function pointer to the function that will be execute for each child. * @param[in] data A void pointer to data that will be passed to the func. * * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Accessor, Tvg_Paint, or function pointer. * * @since 1.0 */ TVG_API Tvg_Result tvg_accessor_set(Tvg_Accessor accessor, Tvg_Paint paint, bool (*func)(Tvg_Paint paint, void* data), void* data); /** * @brief Generate a unique ID (hash key) from a given name. * * This function computes a unique identifier value based on the provided string. * You can use this to assign a unique ID to the Paint object. * * @param[in] name The input string to generate the unique identifier from. * * @return The generated unique identifier value. * * @since 1.0 */ TVG_API uint32_t tvg_accessor_generate_id(const char* name); /** \} */ // end defgroup ThorVGCapi_Accessor /** * @defgroup ThorVGCapi_LottieAnimation LottieAnimation * @brief A module for manipulation of lottie extension features. * * The module enables control of advanced Lottie features. * \{ */ /************************************************************************/ /* LottieAnimation Extension API */ /************************************************************************/ /** * @brief Creates a new LottieAnimation object. * * @return Tvg_Animation A new Tvg_LottieAnimation object. * * @since 0.15 */ TVG_API Tvg_Animation tvg_lottie_animation_new(void); /** * @brief Generates a new slot from the given slot data. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[in] slot The Lottie slot data in JSON format. * * @return The generated slot ID when successful, 0 otherwise. * * @since 1.0 */ TVG_API uint32_t tvg_lottie_animation_gen_slot(Tvg_Animation animation, const char* slot); /** * @brief Applies a previously generated slot to the animation. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[in] id The ID of the slot to apply, or 0 to reset all slots. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded. * @retval TVG_RESULT_INVALID_ARGUMENT When the given @p id is invalid * @retval TVG_RESULT_NOT_SUPPORTED The Lottie Animation is not supported. * * @since 1.0 */ TVG_API Tvg_Result tvg_lottie_animation_apply_slot(Tvg_Animation animation, uint32_t id); /** * @brief Deletes a previously generated slot. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[in] id The ID of the slot to delete. * * @return Tvg_Result enumeration. * @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded or the slot ID is invalid. * @retval TVG_RESULT_NOT_SUPPORTED The Lottie Animation is not supported. * * @note This function should be paired with gen. * @see tvg_lottie_animation_gen_slot() * @since 1.0 */ TVG_API Tvg_Result tvg_lottie_animation_del_slot(Tvg_Animation animation, uint32_t id); /** * @brief Specifies a segment by marker. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[in] marker The name of the segment marker. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded. * @retval TVG_RESULT_INVALID_ARGUMENT When the given @p marker is invalid. * @retval TVG_RESULT_NOT_SUPPORTED The Lottie Animation is not supported. * * @since 1.0 */ TVG_API Tvg_Result tvg_lottie_animation_set_marker(Tvg_Animation animation, const char* marker); /** * @brief Gets the marker count of the animation. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[out] cnt The count value of the markers. * * @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. * * @since 1.0 */ TVG_API Tvg_Result tvg_lottie_animation_get_markers_cnt(Tvg_Animation animation, uint32_t* cnt); /** * @brief Gets the marker name by a given index. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[in] idx The index of the animation marker, starts from 0. * @param[out] name The name of marker when succeed. * * @retval TVG_RESULT_INVALID_ARGUMENT In case @c nullptr is passed as the argument or @c idx is out of range. * * @since 1.0 */ TVG_API Tvg_Result tvg_lottie_animation_get_marker(Tvg_Animation animation, uint32_t idx, const char** name); /** * @brief Interpolates between two frames over a specified duration. * * This method performs tweening, a process of generating intermediate frame * between @p from and @p to based on the given @p progress. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[in] from The start frame number of the interpolation. * @param[in] to The end frame number of the interpolation. * @param[in] progress The current progress of the interpolation (range: 0.0 to 1.0). * * @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded. * * @since 1.0 */ TVG_API Tvg_Result tvg_lottie_animation_tween(Tvg_Animation animation, float from, float to, float progress); /** * @brief Updates the value of an expression variable for a specific layer. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[in] layer The name of the layer containing the variable to be updated. * @param[in] ix The property index of the variable within the layer. * @param[in] var The name of the variable to be updated. * @param[in] val The new value to assign to the variable. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION If the animation is not loaded. * @retval TVG_RESULT_INVALID_ARGUMENT When the given parameter is invalid. * @retval TVG_RESULT_NOT_SUPPORTED When neither the layer nor the property is found in the current animation. * * @note Experimental API */ TVG_API Tvg_Result tvg_lottie_animation_assign(Tvg_Animation animation, const char* layer, uint32_t ix, const char* var, float val); /** * @brief Sets the quality level for Lottie effects. * * This function controls the rendering quality of effects like blur, shadows, etc. * Lower values prioritize performance while higher values prioritize quality. * * @param[in] animation The Tvg_Animation pointer to the Lottie animation object. * @param[in] value The quality level (0-100). 0 represents lowest quality/best performance, * 100 represents highest quality/lowest performance, default is 50. * * @retval TVG_RESULT_INSUFFICIENT_CONDITION If the animation is not loaded. * @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer. * * @note This option is used as a hint; its behavior heavily depends on the render backend. * * @since 1.0 */ TVG_API Tvg_Result tvg_lottie_animation_set_quality(Tvg_Animation animation, uint8_t value); /** \} */ // end addtogroup ThorVGCapi_LottieAnimation /** \} */ // end defgroup ThorVGCapi #ifdef __cplusplus } #endif #endif //_THORVG_CAPI_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/meson.build000664 001750 001750 00000001466 15164251010 032345 0ustar00ddennedyddennedy000000 000000 test_compiler_flags = compiler_flags if lib_type == 'static' test_compiler_flags += ['-DTVG_STATIC'] endif test_dep = [] if host_machine.system() == 'darwin' test_dep += declare_dependency(link_args: ['-framework', 'Cocoa', '-framework', 'IOKit']) endif test_file = [ 'testAccessor.cpp', 'testAnimation.cpp', 'testFill.cpp', 'testInitializer.cpp', 'testLottie.cpp', 'testMain.cpp', 'testPaint.cpp', 'testPicture.cpp', 'testSavers.cpp', 'testScene.cpp', 'testShape.cpp', 'testSwCanvas.cpp', 'testSwEngine.cpp', 'testText.cpp' ] tests = executable('tvgUnitTests', test_file, include_directories : headers, link_with : thorvg_lib, cpp_args : test_compiler_flags, dependencies : test_dep) test('Unit Tests', tests, args : ['--success']) lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-prototype.cpp000664 001750 001750 00000013207 15164251010 053543 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-async-generator-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-promise-object.h" #include "jcontext.h" #include "opcodes.h" #include "vm-defines.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_START = 0, ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_NEXT, ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_THROW, ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_RETURN }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-generator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID async_generator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup asyncgeneratorprototype ECMA AsyncGenerator.prototype object built-in * @{ */ /** * Convert routine type to operation type. */ #define ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION(type) \ ((ecma_async_generator_operation_type_t) ((type) -ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_NEXT)) JERRY_STATIC_ASSERT ((ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION (ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_NEXT) == ECMA_ASYNC_GENERATOR_DO_NEXT), convert_ecma_async_generator_routine_next_to_ecma_async_generator_do_next_failed); JERRY_STATIC_ASSERT ((ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION (ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_THROW) == ECMA_ASYNC_GENERATOR_DO_THROW), convert_ecma_async_generator_routine_throw_to_ecma_async_generator_do_throw_failed); JERRY_STATIC_ASSERT ((ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION (ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_RETURN) == ECMA_ASYNC_GENERATOR_DO_RETURN), convert_ecma_async_generator_routine_return_to_ecma_async_generator_do_return_failed); /** * Dispatcher of the Generator built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_async_generator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to * routine */ uint32_t arguments_number) /**< length of arguments' * list */ { JERRY_UNUSED (arguments_number); vm_executable_object_t *executable_object_p = NULL; if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ASYNC_GENERATOR)) { executable_object_p = (vm_executable_object_t *) object_p; } } if (executable_object_p == NULL) { const char *msg_p = ecma_get_error_msg (ECMA_ERR_ARGUMENT_THIS_NOT_ASYNC_GENERATOR); lit_utf8_size_t msg_size = ecma_get_error_size (ECMA_ERR_ARGUMENT_THIS_NOT_ASYNC_GENERATOR); ecma_string_t *error_msg_p = ecma_new_ecma_string_from_ascii ((const lit_utf8_byte_t *) msg_p, msg_size); ecma_object_t *type_error_obj_p = ecma_new_standard_error (JERRY_ERROR_TYPE, error_msg_p); ecma_deref_ecma_string (error_msg_p); ecma_value_t promise = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_VALUE_UNDEFINED, NULL); ecma_reject_promise (promise, ecma_make_object_value (type_error_obj_p)); ecma_deref_object (type_error_obj_p); return promise; } if (executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_COMPLETED) { ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); if (JERRY_UNLIKELY (builtin_routine_id == ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_THROW)) { return ecma_promise_reject_or_resolve (promise, arguments_list_p[0], false); } ecma_value_t iter_result = ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); ecma_value_t result = ecma_promise_reject_or_resolve (promise, iter_result, true); ecma_free_value (iter_result); return result; } return ecma_async_generator_enqueue (executable_object_p, ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION (builtin_routine_id), arguments_list_p[0]); } /* ecma_builtin_async_generator_prototype_dispatch_routine */ /** * @} * @} * @} */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-boolean.cpp000664 001750 001750 00000005026 15164251010 050016 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-boolean-object.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jrt.h" #if JERRY_BUILTIN_BOOLEAN #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-boolean.inc.h" #define BUILTIN_UNDERSCORED_ID boolean #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup boolean ECMA Boolean object built-in * @{ */ /** * Handle calling [[Call]] of built-in Boolean object * * @return ecma value */ ecma_value_t ecma_builtin_boolean_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_value_t arg_value; if (arguments_list_len == 0) { arg_value = ECMA_VALUE_UNDEFINED; } else { arg_value = arguments_list_p[0]; } return ecma_make_boolean_value (ecma_op_to_boolean (arg_value)); } /* ecma_builtin_boolean_dispatch_call */ /** * Handle calling [[Construct]] of built-in Boolean object * * @return ecma value */ ecma_value_t ecma_builtin_boolean_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); if (arguments_list_len == 0) { return ecma_op_create_boolean_object (ECMA_VALUE_FALSE); } else { return ecma_op_create_boolean_object (arguments_list_p[0]); } } /* ecma_builtin_boolean_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_BOOLEAN */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/lossless.cpp000664 001750 001750 00000055530 15164251010 035464 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Image transforms and color space conversion methods for lossless decoder. // // Authors: Vikas Arora (vikaas.arora@gmail.com) // Jyrki Alakuijala (jyrki@google.com) // Urvang Joshi (urvang@google.com) #include "./dsp.h" #include #include #include "../dec/vp8li.h" #include "../utils/endian_inl.h" #include "./lossless.h" #include "./yuv.h" #define MAX_DIFF_COST (1e30f) //------------------------------------------------------------------------------ // Image transforms. // In-place sum of each component with mod 256. static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) { const uint32_t alpha_and_green = (*a & 0xff00ff00u) + (b & 0xff00ff00u); const uint32_t red_and_blue = (*a & 0x00ff00ffu) + (b & 0x00ff00ffu); *a = (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); } static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) { return (((a0 ^ a1) & 0xfefefefeL) >> 1) + (a0 & a1); } static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) { return Average2(Average2(a0, a2), a1); } static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3) { return Average2(Average2(a0, a1), Average2(a2, a3)); } static WEBP_INLINE uint32_t Clip255(uint32_t a) { if (a < 256) { return a; } // return 0, when a is a negative integer. // return 255, when a is positive. return ~a >> 24; } static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) { return Clip255(a + b - c); } static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1, uint32_t c2) { const int a = AddSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24); const int r = AddSubtractComponentFull((c0 >> 16) & 0xff, (c1 >> 16) & 0xff, (c2 >> 16) & 0xff); const int g = AddSubtractComponentFull((c0 >> 8) & 0xff, (c1 >> 8) & 0xff, (c2 >> 8) & 0xff); const int b = AddSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff); return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; } static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) { return Clip255(a + (a - b) / 2); } static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1, uint32_t c2) { const uint32_t ave = Average2(c0, c1); const int a = AddSubtractComponentHalf(ave >> 24, c2 >> 24); const int r = AddSubtractComponentHalf((ave >> 16) & 0xff, (c2 >> 16) & 0xff); const int g = AddSubtractComponentHalf((ave >> 8) & 0xff, (c2 >> 8) & 0xff); const int b = AddSubtractComponentHalf((ave >> 0) & 0xff, (c2 >> 0) & 0xff); return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; } // gcc-4.9 on ARM generates incorrect code in Select() when Sub3() is inlined. #if defined(__arm__) && LOCAL_GCC_VERSION == 0x409 # define LOCAL_INLINE __attribute__ ((noinline)) #else # define LOCAL_INLINE WEBP_INLINE #endif static LOCAL_INLINE int Sub3(int a, int b, int c) { const int pb = b - c; const int pa = a - c; return abs(pb) - abs(pa); } #undef LOCAL_INLINE static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) { const int pa_minus_pb = Sub3((a >> 24) , (b >> 24) , (c >> 24) ) + Sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) + Sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) + Sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff); return (pa_minus_pb <= 0) ? a : b; } //------------------------------------------------------------------------------ // Predictors static uint32_t Predictor0(uint32_t left, const uint32_t* const top) { (void)top; (void)left; return ARGB_BLACK; } static uint32_t Predictor1(uint32_t left, const uint32_t* const top) { (void)top; return left; } static uint32_t Predictor2(uint32_t left, const uint32_t* const top) { (void)left; return top[0]; } static uint32_t Predictor3(uint32_t left, const uint32_t* const top) { (void)left; return top[1]; } static uint32_t Predictor4(uint32_t left, const uint32_t* const top) { (void)left; return top[-1]; } static uint32_t Predictor5(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average3(left, top[0], top[1]); return pred; } static uint32_t Predictor6(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(left, top[-1]); return pred; } static uint32_t Predictor7(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(left, top[0]); return pred; } static uint32_t Predictor8(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(top[-1], top[0]); (void)left; return pred; } static uint32_t Predictor9(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(top[0], top[1]); (void)left; return pred; } static uint32_t Predictor10(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average4(left, top[-1], top[0], top[1]); return pred; } static uint32_t Predictor11(uint32_t left, const uint32_t* const top) { const uint32_t pred = Select(top[0], left, top[-1]); return pred; } static uint32_t Predictor12(uint32_t left, const uint32_t* const top) { const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]); return pred; } static uint32_t Predictor13(uint32_t left, const uint32_t* const top) { const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]); return pred; } //------------------------------------------------------------------------------ // Inverse prediction. static void PredictorInverseTransform(const VP8LTransform* const transform, int y_start, int y_end, uint32_t* data) { const int width = transform->xsize_; if (y_start == 0) { // First Row follows the L (mode=1) mode. int x; const uint32_t pred0 = Predictor0(data[-1], NULL); AddPixelsEq(data, pred0); for (x = 1; x < width; ++x) { const uint32_t pred1 = Predictor1(data[x - 1], NULL); AddPixelsEq(data + x, pred1); } data += width; ++y_start; } { int y = y_start; const int tile_width = 1 << transform->bits_; const int mask = tile_width - 1; const int safe_width = width & ~mask; const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_); const uint32_t* pred_mode_base = transform->data_ + (y >> transform->bits_) * tiles_per_row; while (y < y_end) { const uint32_t pred2 = Predictor2(data[-1], data - width); const uint32_t* pred_mode_src = pred_mode_base; VP8LPredictorFunc pred_func; int x = 1; int t = 1; // First pixel follows the T (mode=2) mode. AddPixelsEq(data, pred2); // .. the rest: while (x < safe_width) { pred_func = VP8LPredictors[((*pred_mode_src++) >> 8) & 0xf]; for (; t < tile_width; ++t, ++x) { const uint32_t pred = pred_func(data[x - 1], data + x - width); AddPixelsEq(data + x, pred); } t = 0; } if (x < width) { pred_func = VP8LPredictors[((*pred_mode_src++) >> 8) & 0xf]; for (; x < width; ++x) { const uint32_t pred = pred_func(data[x - 1], data + x - width); AddPixelsEq(data + x, pred); } } data += width; ++y; if ((y & mask) == 0) { // Use the same mask, since tiles are squares. pred_mode_base += tiles_per_row; } } } } // Add green to blue and red channels (i.e. perform the inverse transform of // 'subtract green'). void VP8LAddGreenToBlueAndRed_C(uint32_t* data, int num_pixels) { int i; for (i = 0; i < num_pixels; ++i) { const uint32_t argb = data[i]; const uint32_t green = ((argb >> 8) & 0xff); uint32_t red_blue = (argb & 0x00ff00ffu); red_blue += (green << 16) | green; red_blue &= 0x00ff00ffu; data[i] = (argb & 0xff00ff00u) | red_blue; } } static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred, int8_t color) { return (uint32_t)((int)(color_pred) * color) >> 5; } static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, VP8LMultipliers* const m) { m->green_to_red_ = (color_code >> 0) & 0xff; m->green_to_blue_ = (color_code >> 8) & 0xff; m->red_to_blue_ = (color_code >> 16) & 0xff; } void VP8LTransformColorInverse_C(const VP8LMultipliers* const m, uint32_t* data, int num_pixels) { int i; for (i = 0; i < num_pixels; ++i) { const uint32_t argb = data[i]; const uint32_t green = argb >> 8; const uint32_t red = argb >> 16; uint32_t new_red = red; uint32_t new_blue = argb; new_red += ColorTransformDelta(m->green_to_red_, green); new_red &= 0xff; new_blue += ColorTransformDelta(m->green_to_blue_, green); new_blue += ColorTransformDelta(m->red_to_blue_, new_red); new_blue &= 0xff; data[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); } } // Color space inverse transform. static void ColorSpaceInverseTransform(const VP8LTransform* const transform, int y_start, int y_end, uint32_t* data) { const int width = transform->xsize_; const int tile_width = 1 << transform->bits_; const int mask = tile_width - 1; const int safe_width = width & ~mask; const int remaining_width = width - safe_width; const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_); int y = y_start; const uint32_t* pred_row = transform->data_ + (y >> transform->bits_) * tiles_per_row; while (y < y_end) { const uint32_t* pred = pred_row; VP8LMultipliers m = { 0, 0, 0 }; const uint32_t* const data_safe_end = data + safe_width; const uint32_t* const data_end = data + width; while (data < data_safe_end) { ColorCodeToMultipliers(*pred++, &m); VP8LTransformColorInverse(&m, data, tile_width); data += tile_width; } if (data < data_end) { // Left-overs using C-version. ColorCodeToMultipliers(*pred++, &m); VP8LTransformColorInverse(&m, data, remaining_width); data += remaining_width; } ++y; if ((y & mask) == 0) pred_row += tiles_per_row; } } // Separate out pixels packed together using pixel-bundling. // We define two methods for ARGB data (uint32_t) and alpha-only data (uint8_t). #define COLOR_INDEX_INVERSE(FUNC_NAME, F_NAME, STATIC_DECL, TYPE, BIT_SUFFIX, \ GET_INDEX, GET_VALUE) \ static void F_NAME(const TYPE* src, const uint32_t* const color_map, \ TYPE* dst, int y_start, int y_end, int width) { \ int y; \ for (y = y_start; y < y_end; ++y) { \ int x; \ for (x = 0; x < width; ++x) { \ *dst++ = GET_VALUE(color_map[GET_INDEX(*src++)]); \ } \ } \ } \ STATIC_DECL void FUNC_NAME(const VP8LTransform* const transform, \ int y_start, int y_end, const TYPE* src, \ TYPE* dst) { \ int y; \ const int bits_per_pixel = 8 >> transform->bits_; \ const int width = transform->xsize_; \ const uint32_t* const color_map = transform->data_; \ if (bits_per_pixel < 8) { \ const int pixels_per_byte = 1 << transform->bits_; \ const int count_mask = pixels_per_byte - 1; \ const uint32_t bit_mask = (1 << bits_per_pixel) - 1; \ for (y = y_start; y < y_end; ++y) { \ uint32_t packed_pixels = 0; \ int x; \ for (x = 0; x < width; ++x) { \ /* We need to load fresh 'packed_pixels' once every */ \ /* 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte */ \ /* is a power of 2, so can just use a mask for that, instead of */ \ /* decrementing a counter. */ \ if ((x & count_mask) == 0) packed_pixels = GET_INDEX(*src++); \ *dst++ = GET_VALUE(color_map[packed_pixels & bit_mask]); \ packed_pixels >>= bits_per_pixel; \ } \ } \ } else { \ VP8LMapColor##BIT_SUFFIX(src, color_map, dst, y_start, y_end, width); \ } \ } COLOR_INDEX_INVERSE(ColorIndexInverseTransform, MapARGB, static, uint32_t, 32b, VP8GetARGBIndex, VP8GetARGBValue) COLOR_INDEX_INVERSE(VP8LColorIndexInverseTransformAlpha, MapAlpha, , uint8_t, 8b, VP8GetAlphaIndex, VP8GetAlphaValue) #undef COLOR_INDEX_INVERSE void VP8LInverseTransform(const VP8LTransform* const transform, int row_start, int row_end, const uint32_t* const in, uint32_t* const out) { const int width = transform->xsize_; assert(row_start < row_end); assert(row_end <= transform->ysize_); switch (transform->type_) { case SUBTRACT_GREEN: VP8LAddGreenToBlueAndRed(out, (row_end - row_start) * width); break; case PREDICTOR_TRANSFORM: PredictorInverseTransform(transform, row_start, row_end, out); if (row_end != transform->ysize_) { // The last predicted row in this iteration will be the top-pred row // for the first row in next iteration. memcpy(out - width, out + (row_end - row_start - 1) * width, width * sizeof(*out)); } break; case CROSS_COLOR_TRANSFORM: ColorSpaceInverseTransform(transform, row_start, row_end, out); break; case COLOR_INDEXING_TRANSFORM: if (in == out && transform->bits_ > 0) { // Move packed pixels to the end of unpacked region, so that unpacking // can occur seamlessly. // Also, note that this is the only transform that applies on // the effective width of VP8LSubSampleSize(xsize_, bits_). All other // transforms work on effective width of xsize_. const int out_stride = (row_end - row_start) * width; const int in_stride = (row_end - row_start) * VP8LSubSampleSize(transform->xsize_, transform->bits_); uint32_t* const src = out + out_stride - in_stride; memmove(src, out, in_stride * sizeof(*src)); ColorIndexInverseTransform(transform, row_start, row_end, src, out); } else { ColorIndexInverseTransform(transform, row_start, row_end, in, out); } break; } } //------------------------------------------------------------------------------ // Color space conversion. static int is_big_endian(void) { static const union { uint16_t w; uint8_t b[2]; } tmp = { 1 }; return (tmp.b[0] != 1); } void VP8LConvertBGRAToRGB_C(const uint32_t* src, int num_pixels, uint8_t* dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; *dst++ = (argb >> 16) & 0xff; *dst++ = (argb >> 8) & 0xff; *dst++ = (argb >> 0) & 0xff; } } void VP8LConvertBGRAToRGBA_C(const uint32_t* src, int num_pixels, uint8_t* dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; *dst++ = (argb >> 16) & 0xff; *dst++ = (argb >> 8) & 0xff; *dst++ = (argb >> 0) & 0xff; *dst++ = (argb >> 24) & 0xff; } } void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src, int num_pixels, uint8_t* dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; const uint8_t rg = ((argb >> 16) & 0xf0) | ((argb >> 12) & 0xf); const uint8_t ba = ((argb >> 0) & 0xf0) | ((argb >> 28) & 0xf); #ifdef WEBP_SWAP_16BIT_CSP *dst++ = ba; *dst++ = rg; #else *dst++ = rg; *dst++ = ba; #endif } } void VP8LConvertBGRAToRGB565_C(const uint32_t* src, int num_pixels, uint8_t* dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; const uint8_t rg = ((argb >> 16) & 0xf8) | ((argb >> 13) & 0x7); const uint8_t gb = ((argb >> 5) & 0xe0) | ((argb >> 3) & 0x1f); #ifdef WEBP_SWAP_16BIT_CSP *dst++ = gb; *dst++ = rg; #else *dst++ = rg; *dst++ = gb; #endif } } void VP8LConvertBGRAToBGR_C(const uint32_t* src, int num_pixels, uint8_t* dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; *dst++ = (argb >> 0) & 0xff; *dst++ = (argb >> 8) & 0xff; *dst++ = (argb >> 16) & 0xff; } } static void CopyOrSwap(const uint32_t* src, int num_pixels, uint8_t* dst, int swap_on_big_endian) { if (is_big_endian() == swap_on_big_endian) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; #if !defined(WORDS_BIGENDIAN) #if !defined(WEBP_REFERENCE_IMPLEMENTATION) *(uint32_t*)dst = BSwap32(argb); #else // WEBP_REFERENCE_IMPLEMENTATION dst[0] = (argb >> 24) & 0xff; dst[1] = (argb >> 16) & 0xff; dst[2] = (argb >> 8) & 0xff; dst[3] = (argb >> 0) & 0xff; #endif #else // WORDS_BIGENDIAN dst[0] = (argb >> 0) & 0xff; dst[1] = (argb >> 8) & 0xff; dst[2] = (argb >> 16) & 0xff; dst[3] = (argb >> 24) & 0xff; #endif dst += sizeof(argb); } } else { memcpy(dst, src, num_pixels * sizeof(*src)); } } void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, WEBP_CSP_MODE out_colorspace, uint8_t* const rgba) { switch (out_colorspace) { case MODE_RGB: VP8LConvertBGRAToRGB(in_data, num_pixels, rgba); break; case MODE_RGBA: VP8LConvertBGRAToRGBA(in_data, num_pixels, rgba); break; case MODE_rgbA: VP8LConvertBGRAToRGBA(in_data, num_pixels, rgba); WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0); break; case MODE_BGR: VP8LConvertBGRAToBGR(in_data, num_pixels, rgba); break; case MODE_BGRA: CopyOrSwap(in_data, num_pixels, rgba, 1); break; case MODE_bgrA: CopyOrSwap(in_data, num_pixels, rgba, 1); WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0); break; case MODE_ARGB: CopyOrSwap(in_data, num_pixels, rgba, 0); break; case MODE_Argb: CopyOrSwap(in_data, num_pixels, rgba, 0); WebPApplyAlphaMultiply(rgba, 1, num_pixels, 1, 0); break; case MODE_RGBA_4444: VP8LConvertBGRAToRGBA4444(in_data, num_pixels, rgba); break; case MODE_rgbA_4444: VP8LConvertBGRAToRGBA4444(in_data, num_pixels, rgba); WebPApplyAlphaMultiply4444(rgba, num_pixels, 1, 0); break; case MODE_RGB_565: VP8LConvertBGRAToRGB565(in_data, num_pixels, rgba); break; default: assert(0); // Code flow should not reach here. } } //------------------------------------------------------------------------------ VP8LProcessBlueAndRedFunc VP8LAddGreenToBlueAndRed; VP8LPredictorFunc VP8LPredictors[16]; VP8LTransformColorFunc VP8LTransformColorInverse; VP8LConvertFunc VP8LConvertBGRAToRGB; VP8LConvertFunc VP8LConvertBGRAToRGBA; VP8LConvertFunc VP8LConvertBGRAToRGBA4444; VP8LConvertFunc VP8LConvertBGRAToRGB565; VP8LConvertFunc VP8LConvertBGRAToBGR; VP8LMapARGBFunc VP8LMapColor32b; VP8LMapAlphaFunc VP8LMapColor8b; extern void VP8LDspInitSSE2(void); extern void VP8LDspInitNEON(void); extern void VP8LDspInitMIPSdspR2(void); static volatile VP8CPUInfo lossless_last_cpuinfo_used = (VP8CPUInfo)&lossless_last_cpuinfo_used; WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInit(void) { if (lossless_last_cpuinfo_used == VP8GetCPUInfo) return; VP8LPredictors[0] = Predictor0; VP8LPredictors[1] = Predictor1; VP8LPredictors[2] = Predictor2; VP8LPredictors[3] = Predictor3; VP8LPredictors[4] = Predictor4; VP8LPredictors[5] = Predictor5; VP8LPredictors[6] = Predictor6; VP8LPredictors[7] = Predictor7; VP8LPredictors[8] = Predictor8; VP8LPredictors[9] = Predictor9; VP8LPredictors[10] = Predictor10; VP8LPredictors[11] = Predictor11; VP8LPredictors[12] = Predictor12; VP8LPredictors[13] = Predictor13; VP8LPredictors[14] = Predictor0; // <- padding security sentinels VP8LPredictors[15] = Predictor0; VP8LAddGreenToBlueAndRed = VP8LAddGreenToBlueAndRed_C; VP8LTransformColorInverse = VP8LTransformColorInverse_C; VP8LConvertBGRAToRGB = VP8LConvertBGRAToRGB_C; VP8LConvertBGRAToRGBA = VP8LConvertBGRAToRGBA_C; VP8LConvertBGRAToRGBA4444 = VP8LConvertBGRAToRGBA4444_C; VP8LConvertBGRAToRGB565 = VP8LConvertBGRAToRGB565_C; VP8LConvertBGRAToBGR = VP8LConvertBGRAToBGR_C; VP8LMapColor32b = MapARGB; VP8LMapColor8b = MapAlpha; // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) if (VP8GetCPUInfo(kSSE2)) { VP8LDspInitSSE2(); } #endif #if defined(WEBP_USE_NEON) if (VP8GetCPUInfo(kNEON)) { VP8LDspInitNEON(); } #endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { VP8LDspInitMIPSdspR2(); } #endif } lossless_last_cpuinfo_used = VP8GetCPUInfo; } //------------------------------------------------------------------------------ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test3.svg000664 001750 001750 00000017261 15164251010 034000 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/utils.h000664 001750 001750 00000004266 15164251010 035053 0ustar00ddennedyddennedy000000 000000 // Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Misc. common utility functions // // Authors: Skal (pascal.massimino@gmail.com) // Urvang (urvang@google.com) #ifndef WEBP_UTILS_UTILS_H_ #define WEBP_UTILS_UTILS_H_ #include #include "../webp/types.h" #ifdef __cplusplus extern "C" { #endif //------------------------------------------------------------------------------ // Returns (int)floor(log2(n)). n must be > 0. // use GNU builtins where available. #if defined(__GNUC__) && \ ((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4) static WEBP_INLINE int BitsLog2Floor(uint32_t n) { return 31 ^ __builtin_clz(n); } #elif defined(_MSC_VER) && _MSC_VER > 1310 && \ (defined(_M_X64) || defined(_M_IX86)) #include #pragma intrinsic(_BitScanReverse) static WEBP_INLINE int BitsLog2Floor(uint32_t n) { uint32_t first_set_bit; _BitScanReverse((unsigned long*)&first_set_bit, n); return first_set_bit; } #else static WEBP_INLINE int BitsLog2Floor(uint32_t n) { int log = 0; uint32_t value = n; int i; for (i = 4; i >= 0; --i) { const int shift = (1 << i); const uint32_t x = value >> shift; if (x != 0) { value = x; log += shift; } } return log; } #endif //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Alignment #include // memcpy() is the safe way of moving potentially unaligned 32b memory. static WEBP_INLINE uint32_t WebPMemToUint32(const uint8_t* const ptr) { uint32_t A; memcpy(&A, ptr, sizeof(A)); return A; } //------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_UTILS_UTILS_H_ */ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/000775 001750 001750 00000000000 15164251010 041227 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modulessrc/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/build_macos.yml000664 001750 001750 00000004466 15164251010 035551 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0name: macOS on: pull_request: branches: - main push: branches: - main permissions: contents: read jobs: build: runs-on: macos-latest steps: - uses: actions/checkout@v4 with: submodules: true - name: Unbreak Python in GitHub Actions run: | find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete sudo rm -rf /Library/Frameworks/Python.framework/ brew install --force python3 && brew unlink python3 && brew link --overwrite python3 - name: Install Packages run: | export HOMEBREW_NO_INSTALL_FROM_API=1 brew update brew install meson sdl2 - name: Build run: | meson setup build -Dlog=true -Dloaders=all -Dsavers=all -Dbindings=capi -Dtools=all -Dextra="lottie_exp" ninja -C build install compact_test: runs-on: macos-latest steps: - uses: actions/checkout@v4 with: submodules: true - name: Unbreak Python in GitHub Actions run: | find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete sudo rm -rf /Library/Frameworks/Python.framework/ brew install --force python3 && brew unlink python3 && brew link --overwrite python3 - name: Install Packages run: | brew update brew install meson - name: Build run: | meson setup build -Dlog=true -Dloaders=all -Dsavers=all -Dstatic=true -Dthreads=false -Dextra="lottie_exp" ninja -C build install unit_test: runs-on: macos-latest steps: - uses: actions/checkout@v4 with: submodules: true - name: Unbreak Python in GitHub Actions run: | find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete sudo rm -rf /Library/Frameworks/Python.framework/ brew install --force python3 && brew unlink python3 && brew link --overwrite python3 - name: Install Packages run: | brew update brew install meson - name: Build run: | meson setup build -Dloaders=all -Dsavers=all -Dbindings=capi -Dtests=true -Dextra="lottie_exp" --errorlogs ninja -C build install test - uses: actions/upload-artifact@v4 with: name: UnitTestReport path: build/meson-logs/testlog.txt src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlRenderer.cpp000664 001750 001750 00000143157 15164251010 036753 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgFill.h" #include "tvgGlCommon.h" #include "tvgGlRenderer.h" #include "tvgGlGpuBuffer.h" #include "tvgGlRenderTask.h" #include "tvgGlProgram.h" #include "tvgGlShaderSrc.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #define NOISE_LEVEL 0.5f static atomic rendererCnt{-1}; static void _solidUniforms(GlShape& sdata, const RenderColor& c, RenderUpdateFlag flag, float* solidInfo); void GlRenderer::clearDisposes() { if (mDisposed.textures.count > 0) { glDeleteTextures(mDisposed.textures.count, mDisposed.textures.data); mDisposed.textures.clear(); } ARRAY_FOREACH(p, mRenderPassStack) delete(*p); mRenderPassStack.clear(); } void GlRenderer::flush() { clearDisposes(); mRootTarget.reset(); ARRAY_FOREACH(p, mComposePool) delete(*p); mComposePool.clear(); ARRAY_FOREACH(p, mBlendPool) delete(*p); mBlendPool.clear(); ARRAY_FOREACH(p, mComposeStack) delete(*p); mComposeStack.clear(); } bool GlRenderer::currentContext() { #if defined(__EMSCRIPTEN__) const auto targetContext = reinterpret_cast(mContext); if (emscripten_webgl_get_current_context() == targetContext) return true; return emscripten_webgl_make_context_current(targetContext) == 0; #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(THORVG_GL_TARGET_GL) if (tvgWglGetCurrentContext() == static_cast(mContext)) return true; return (bool) tvgWglMakeCurrent((HDC)mSurface, static_cast(mContext)); #elif defined(THORVG_GL_TARGET_GLES) if (tvgEglGetCurrentContext() == static_cast(mContext)) return true; if (mDisplay && mSurface) return (bool) tvgEglMakeCurrent((EGLDisplay)mDisplay, (EGLSurface)mSurface, (EGLSurface)mSurface, (EGLContext)mContext); #endif TVGLOG("GL_ENGINE", "Maybe missing currentContext()?"); return true; } GlRenderer::GlRenderer() : mEffect(GlEffect(&mGpuBuffer)) { ++rendererCnt; } GlRenderer::~GlRenderer() { --rendererCnt; flush(); ARRAY_FOREACH(p, mPrograms) delete(*p); } void GlRenderer::initShaders() { mPrograms.reserve((int)RT_None); #if 1 //for optimization #define LINEAR_TOTAL_LENGTH 2831 #define RADIAL_TOTAL_LENGTH 5315 #define BLEND_TOTAL_LENGTH 5500 #else #define COMMON_TOTAL_LENGTH strlen(STR_GRADIENT_FRAG_COMMON_VARIABLES) + strlen(STR_GRADIENT_FRAG_COMMON_FUNCTIONS) + 1 #define LINEAR_TOTAL_LENGTH strlen(STR_LINEAR_GRADIENT_VARIABLES) + strlen(STR_LINEAR_GRADIENT_FUNCTIONS) + strlen(STR_LINEAR_GRADIENT_MAIN) + COMMON_TOTAL_LENGTH #define RADIAL_TOTAL_LENGTH strlen(STR_RADIAL_GRADIENT_VARIABLES) + strlen(STR_RADIAL_GRADIENT_FUNCTIONS) + strlen(STR_RADIAL_GRADIENT_MAIN) + COMMON_TOTAL_LENGTH #define BLEND_TOTAL_LENGTH strlen(BLEND_SCENE_FRAG_HEADER) + strlen(BLEND_FRAG_HSL) + strlen(COLOR_BURN_BLEND_FRAG) + 1 #endif char linearGradientFragShader[LINEAR_TOTAL_LENGTH]; snprintf(linearGradientFragShader, LINEAR_TOTAL_LENGTH, "%s%s%s%s%s", STR_GRADIENT_FRAG_COMMON_VARIABLES, STR_LINEAR_GRADIENT_VARIABLES, STR_GRADIENT_FRAG_COMMON_FUNCTIONS, STR_LINEAR_GRADIENT_FUNCTIONS, STR_LINEAR_GRADIENT_MAIN ); char radialGradientFragShader[RADIAL_TOTAL_LENGTH]; snprintf(radialGradientFragShader, RADIAL_TOTAL_LENGTH, "%s%s%s%s%s", STR_GRADIENT_FRAG_COMMON_VARIABLES, STR_RADIAL_GRADIENT_VARIABLES, STR_GRADIENT_FRAG_COMMON_FUNCTIONS, STR_RADIAL_GRADIENT_FUNCTIONS, STR_RADIAL_GRADIENT_MAIN ); mPrograms.push(new GlProgram(COLOR_VERT_SHADER, COLOR_FRAG_SHADER)); mPrograms.push(new GlProgram(GRADIENT_VERT_SHADER, linearGradientFragShader)); mPrograms.push(new GlProgram(GRADIENT_VERT_SHADER, radialGradientFragShader)); mPrograms.push(new GlProgram(IMAGE_VERT_SHADER, IMAGE_FRAG_SHADER)); // compose Renderer mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_ALPHA_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_INV_ALPHA_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_LUMA_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_INV_LUMA_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_ADD_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_SUB_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_INTERSECT_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_DIFF_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_LIGHTEN_FRAG_SHADER)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, MASK_DARKEN_FRAG_SHADER)); // stencil Renderer mPrograms.push(new GlProgram(STENCIL_VERT_SHADER, STENCIL_FRAG_SHADER)); // blit Renderer mPrograms.push(new GlProgram(BLIT_VERT_SHADER, BLIT_FRAG_SHADER)); // blend programs: image (17) + scene (17) + shape solid (17) + shape linear (17) + shape radial (17) for (uint32_t i = 0; i < 85; ++i) mPrograms.push(nullptr); } void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdateFlag flag, int32_t depth) { auto blendShape = (mBlendMethod != BlendMethod::Normal); auto vp = currentPass()->getViewport(); auto bbox = blendShape ? sdata.geometry.getBounds() : sdata.geometry.viewport; bbox.intersect(vp); if (bbox.invalid()) return; auto x = bbox.sx() - vp.sx(); auto y = bbox.sy() - vp.sy(); auto w = bbox.sw(); auto h = bbox.sh(); auto yGl = vp.sh() - y - h; RenderRegion viewRegion = {{x, yGl}, {x + w, yGl + h}}; GlRenderTask* task = nullptr; GlRenderTarget* dstCopyFbo = nullptr; if (blendShape) { if (mBlendPool.empty()) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); #if defined(THORVG_GL_TARGET_GL) dstCopyFbo = mBlendPool[0]->getRenderTarget(viewRegion); #else // TODO: create partial buffer when MSAA is disabled dstCopyFbo = mBlendPool[0]->getRenderTarget(currentPass()->getViewport()); #endif auto program = getBlendProgram(mBlendMethod, BlendSource::Solid); task = new GlDirectBlendTask(program, currentPass()->getFbo(), dstCopyFbo, viewRegion); } else { task = new GlRenderTask(mPrograms[RT_Color]); } task->setViewMatrix(currentPass()->getViewMatrix()); task->setDrawDepth(depth); if (!sdata.geometry.draw(task, &mGpuBuffer, flag)) { delete task; return; } task->setViewport(viewRegion); GlRenderTask* stencilTask = nullptr; auto stencilMode = sdata.geometry.getStencilMode(flag); if (stencilMode != GlStencilMode::None) { stencilTask = new GlRenderTask(mPrograms[RT_Stencil], task); stencilTask->setDrawDepth(depth); } float solidInfo[4]; _solidUniforms(sdata, c, flag, solidInfo); auto solidOffset = mGpuBuffer.push(solidInfo, sizeof(solidInfo), true); task->addBindResource(GlBindingResource{ 0, task->getProgram()->getUniformBlockIndex("SolidInfo"), mGpuBuffer.getBufferId(), solidOffset, sizeof(solidInfo), }); if (blendShape && dstCopyFbo) { #if defined(THORVG_GL_TARGET_GL) float region[] = {float(viewRegion.sx()), float(viewRegion.sy()), float(dstCopyFbo->width), float(dstCopyFbo->height)}; #else // TODO: create partial buffer when MSAA is disabled float region[] = {0.0f, 0.0f, float(dstCopyFbo->width), float(dstCopyFbo->height)}; #endif task->addBindResource(GlBindingResource{ 2, task->getProgram()->getUniformBlockIndex("BlendRegion"), mGpuBuffer.getBufferId(), mGpuBuffer.push(region, 4 * sizeof(float), true), 4 * sizeof(float), }); task->addBindResource(GlBindingResource{0, dstCopyFbo->colorTex, task->getProgram()->getUniformLocation("uDstTexture")}); } if (stencilTask) currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode)); else currentPass()->addRenderTask(task); } void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFlag flag, int32_t depth) { auto blendShape = (mBlendMethod != BlendMethod::Normal); auto vp = currentPass()->getViewport(); auto bbox = blendShape ? sdata.geometry.getBounds() : sdata.geometry.viewport; bbox.intersect(vp); if (bbox.invalid()) return; const Fill::ColorStop* stops = nullptr; auto stopCnt = min(fill->colorStops(&stops), static_cast(MAX_GRADIENT_STOPS)); if (stopCnt < 2) return; GlRenderTask* task = nullptr; GlRenderTarget* dstCopyFbo = nullptr; auto radial = fill->type() == Type::RadialGradient; auto x = bbox.sx() - vp.sx(); auto y = bbox.sy() - vp.sy(); auto w = bbox.sw(); auto h = bbox.sh(); auto yGl = vp.sh() - y - h; RenderRegion viewRegion = {{x, yGl}, {x + w, yGl + h}}; if (blendShape) { if (mBlendPool.empty()) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); #if defined(THORVG_GL_TARGET_GL) dstCopyFbo = mBlendPool[0]->getRenderTarget(viewRegion); #else // TODO: create partial buffer when MSAA is disabled dstCopyFbo = mBlendPool[0]->getRenderTarget(currentPass()->getViewport()); #endif auto program = getBlendProgram(mBlendMethod, radial ? BlendSource::RadialGradient : BlendSource::LinearGradient); task = new GlDirectBlendTask(program, currentPass()->getFbo(), dstCopyFbo, viewRegion); } else if (fill->type() == Type::LinearGradient) task = new GlRenderTask(mPrograms[RT_LinGradient]); else if (fill->type() == Type::RadialGradient) task = new GlRenderTask(mPrograms[RT_RadGradient]); else return; task->setViewMatrix(currentPass()->getViewMatrix()); task->setDrawDepth(depth); if (!sdata.geometry.draw(task, &mGpuBuffer, flag)) { delete task; return; } task->setViewport(viewRegion); GlRenderTask* stencilTask = nullptr; GlStencilMode stencilMode = sdata.geometry.getStencilMode(flag); if (stencilMode != GlStencilMode::None) { stencilTask = new GlRenderTask(mPrograms[RT_Stencil], task); stencilTask->setDrawDepth(depth); } // transform buffer (inverse fill-space transform) float invMat3[GL_MAT3_STD140_SIZE]; Matrix inv; inverse(&fill->transform(), &inv); Matrix invShape; inverse(&sdata.geometry.matrix, &invShape); inv = inv * invShape; getMatrix3Std140(inv, invMat3); float transformInfo[GL_MAT3_STD140_SIZE]; memcpy(transformInfo, invMat3, GL_MAT3_STD140_BYTES); auto transformOffset = mGpuBuffer.push(transformInfo, sizeof(transformInfo), true); task->addBindResource(GlBindingResource{ 0, task->getProgram()->getUniformBlockIndex("TransformInfo"), mGpuBuffer.getBufferId(), transformOffset, sizeof(transformInfo), }); auto alpha = sdata.opacity / 255.f; if (flag & RenderUpdateFlag::GradientStroke) { auto strokeWidth = sdata.geometry.strokeRenderWidth; if (strokeWidth < MIN_GL_STROKE_WIDTH) { alpha = strokeWidth / MIN_GL_STROKE_WIDTH; } } // gradient block GlBindingResource gradientBinding{}; auto loc = task->getProgram()->getUniformBlockIndex("GradientInfo"); if (fill->type() == Type::LinearGradient) { auto linearFill = static_cast(fill); GlLinearGradientBlock gradientBlock; gradientBlock.nStops[1] = NOISE_LEVEL; gradientBlock.nStops[2] = static_cast(fill->spread()) * 1.f; uint32_t nStops = 0; for (uint32_t i = 0; i < stopCnt; ++i) { if (i > 0 && gradientBlock.stopPoints[nStops - 1] > stops[i].offset) continue; gradientBlock.stopPoints[i] = stops[i].offset; gradientBlock.stopColors[i * 4 + 0] = stops[i].r / 255.f; gradientBlock.stopColors[i * 4 + 1] = stops[i].g / 255.f; gradientBlock.stopColors[i * 4 + 2] = stops[i].b / 255.f; gradientBlock.stopColors[i * 4 + 3] = stops[i].a / 255.f * alpha; nStops++; } gradientBlock.nStops[0] = nStops * 1.f; float x1, x2, y1, y2; linearFill->linear(&x1, &y1, &x2, &y2); gradientBlock.startPos[0] = x1; gradientBlock.startPos[1] = y1; gradientBlock.stopPos[0] = x2; gradientBlock.stopPos[1] = y2; gradientBinding = GlBindingResource{ 2, loc, mGpuBuffer.getBufferId(), mGpuBuffer.push(&gradientBlock, sizeof(GlLinearGradientBlock), true), sizeof(GlLinearGradientBlock), }; } else { auto radialFill = static_cast(fill); GlRadialGradientBlock gradientBlock; gradientBlock.nStops[1] = NOISE_LEVEL; gradientBlock.nStops[2] = static_cast(fill->spread()) * 1.f; uint32_t nStops = 0; for (uint32_t i = 0; i < stopCnt; ++i) { if (i > 0 && gradientBlock.stopPoints[nStops - 1] > stops[i].offset) continue; gradientBlock.stopPoints[i] = stops[i].offset; gradientBlock.stopColors[i * 4 + 0] = stops[i].r / 255.f; gradientBlock.stopColors[i * 4 + 1] = stops[i].g / 255.f; gradientBlock.stopColors[i * 4 + 2] = stops[i].b / 255.f; gradientBlock.stopColors[i * 4 + 3] = stops[i].a / 255.f * alpha; nStops++; } gradientBlock.nStops[0] = nStops * 1.f; float x, y, r, fx, fy, fr; radialFill->radial(&x, &y, &r, &fx, &fy, &fr); CONST_RADIAL(radialFill)->correct(fx, fy, fr); gradientBlock.centerPos[0] = fx; gradientBlock.centerPos[1] = fy; gradientBlock.centerPos[2] = x; gradientBlock.centerPos[3] = y; gradientBlock.radius[0] = fr; gradientBlock.radius[1] = r; gradientBinding = GlBindingResource{ 2, loc, mGpuBuffer.getBufferId(), mGpuBuffer.push(&gradientBlock, sizeof(GlRadialGradientBlock), true), sizeof(GlRadialGradientBlock), }; } task->addBindResource(gradientBinding); if (blendShape && dstCopyFbo) { #if defined(THORVG_GL_TARGET_GL) float region[] = {float(viewRegion.sx()), float(viewRegion.sy()), float(dstCopyFbo->width), float(dstCopyFbo->height)}; #else // TODO: create partial buffer when MSAA is disabled float region[] = {0.0f, 0.0f, float(dstCopyFbo->width), float(dstCopyFbo->height)}; #endif task->addBindResource(GlBindingResource{ 3, task->getProgram()->getUniformBlockIndex("BlendRegion"), mGpuBuffer.getBufferId(), mGpuBuffer.push(region, 4 * sizeof(float), true), 4 * sizeof(float), }); task->addBindResource(GlBindingResource{0, dstCopyFbo->colorTex, task->getProgram()->getUniformLocation("uDstTexture")}); } if (stencilTask) { currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode)); } else { currentPass()->addRenderTask(task); } } void GlRenderer::drawClip(Array& clips) { Array identityVertex(4 * 2); float left = -1.f; float top = 1.f; float right = 1.f; float bottom = -1.f; identityVertex.push(left); identityVertex.push(top); identityVertex.push(left); identityVertex.push(bottom); identityVertex.push(right); identityVertex.push(top); identityVertex.push(right); identityVertex.push(bottom); Array identityIndex(6); identityIndex.push(0); identityIndex.push(1); identityIndex.push(2); identityIndex.push(2); identityIndex.push(1); identityIndex.push(3); auto identityVertexOffset = mGpuBuffer.push(identityVertex.data, 8 * sizeof(float)); auto identityIndexOffset = mGpuBuffer.pushIndex(identityIndex.data, 6 * sizeof(uint32_t)); Array clipDepths(clips.count); clipDepths.count = clips.count; for (int32_t i = clips.count - 1; i >= 0; i--) { clipDepths[i] = currentPass()->nextDrawDepth(); } const auto& vp = currentPass()->getViewport(); const auto& viewMatrix = currentPass()->getViewMatrix(); for (uint32_t i = 0; i < clips.count; ++i) { auto sdata = static_cast(clips[i]); auto clipTask = new GlRenderTask(mPrograms[RT_Stencil]); clipTask->setDrawDepth(clipDepths[i]); clipTask->setViewMatrix(viewMatrix); auto flag = (sdata->geometry.stroke.vertex.count > 0) ? RenderUpdateFlag::Stroke : RenderUpdateFlag::Path; sdata->geometry.draw(clipTask, &mGpuBuffer, flag); auto bbox = sdata->geometry.viewport; bbox.intersect(vp); auto x = bbox.sx() - vp.sx(); auto y = vp.sh() - (bbox.sy() - vp.sy()) - bbox.sh(); clipTask->setViewport({{x, y}, {x + bbox.sw(), y + bbox.sh()}}); auto maskTask = new GlRenderTask(mPrograms[RT_Stencil]); maskTask->setDrawDepth(clipDepths[i]); maskTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), identityVertexOffset}); maskTask->setDrawRange(identityIndexOffset, 6); maskTask->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); currentPass()->addRenderTask(new GlClipTask(clipTask, maskTask)); } } GlRenderPass* GlRenderer::currentPass() { if (mRenderPassStack.empty()) return nullptr; return mRenderPassStack.last(); } bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bounds) { if (vp.invalid()) return false; bounds.intersect(vp); if (bounds.invalid()) return false; if (mBlendMethod == BlendMethod::Normal) return false; if (mBlendPool.empty()) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); auto blendFbo = mBlendPool[0]->getRenderTarget(bounds); mRenderPassStack.push(new GlRenderPass(blendFbo)); return true; } void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask) { auto blendPass = mRenderPassStack.last(); mRenderPassStack.pop(); blendPass->setDrawDepth(currentPass()->nextDrawDepth()); auto composeTask = blendPass->endRenderPass(nullptr, currentPass()->getFboId()); const auto& vp = blendPass->getViewport(); if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); #if defined(THORVG_GL_TARGET_GL) auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp); #else // TODO: create partial buffer when MSAA is disabled auto dstCopyFbo = mBlendPool[1]->getRenderTarget(currentPass()->getViewport()); #endif auto x = vp.sx(); auto y = currentPass()->getViewport().sh() - vp.sy() - vp.sh(); stencilTask->setViewport({{x, y}, {x + vp.sw(), y + vp.sh()}}); stencilTask->setDrawDepth(currentPass()->nextDrawDepth()); stencilTask->setViewMatrix(currentPass()->getViewMatrix()); auto program = getBlendProgram(mBlendMethod, BlendSource::Image); auto task = new GlComplexBlendTask(program, currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask); prepareCmpTask(task, vp, blendPass->getFboWidth(), blendPass->getFboHeight()); task->setDrawDepth(currentPass()->nextDrawDepth()); #if defined(THORVG_GL_TARGET_GL) const auto& taskVp = task->getViewport(); float region[] = {float(taskVp.sx()), float(taskVp.sy()), float(dstCopyFbo->width), float(dstCopyFbo->height)}; #else // TODO: create partial buffer when MSAA is disabled float region[] = {0.0f, 0.0f, float(dstCopyFbo->width), float(dstCopyFbo->height)}; #endif task->addBindResource(GlBindingResource{ 0, task->getProgram()->getUniformBlockIndex("BlendRegion"), mGpuBuffer.getBufferId(), mGpuBuffer.push(region, 4 * sizeof(float), true), 4 * sizeof(float), }); // src and dst texture task->addBindResource(GlBindingResource{1, blendPass->getFbo()->colorTex, task->getProgram()->getUniformLocation("uSrcTexture")}); task->addBindResource(GlBindingResource{2, dstCopyFbo->colorTex, task->getProgram()->getUniformLocation("uDstTexture")}); currentPass()->addRenderTask(task); delete(blendPass); } GlProgram* GlRenderer::getBlendProgram(BlendMethod method, BlendSource source) { // custom blend shaders static const char* shaderFunc[17] { NORMAL_BLEND_FRAG, MULTIPLY_BLEND_FRAG, SCREEN_BLEND_FRAG, OVERLAY_BLEND_FRAG, DARKEN_BLEND_FRAG, LIGHTEN_BLEND_FRAG, COLOR_DODGE_BLEND_FRAG, COLOR_BURN_BLEND_FRAG, HARD_LIGHT_BLEND_FRAG, SOFT_LIGHT_BLEND_FRAG, DIFFERENCE_BLEND_FRAG, EXCLUSION_BLEND_FRAG, HUE_BLEND_FRAG, SATURATION_BLEND_FRAG, COLOR_BLEND_FRAG, LUMINOSITY_BLEND_FRAG, ADD_BLEND_FRAG }; uint32_t methodInd = (uint32_t)method; uint32_t shaderInd = methodInd; switch (source) { case BlendSource::Scene: shaderInd += (uint32_t)RT_Blend_Scene_Normal; break; case BlendSource::Image: shaderInd += (uint32_t)RT_Blend_Image_Normal; break; case BlendSource::Solid: shaderInd += (uint32_t)RT_ShapeBlend_Solid_Normal; break; case BlendSource::LinearGradient: shaderInd += (uint32_t)RT_ShapeBlend_Linear_Normal; break; case BlendSource::RadialGradient: shaderInd += (uint32_t)RT_ShapeBlend_Radial_Normal; break; } if (mPrograms[shaderInd]) return mPrograms[shaderInd]; const char* helpers = ""; if ((method == BlendMethod::Hue) || (method == BlendMethod::Saturation) || (method == BlendMethod::Color) || (method == BlendMethod::Luminosity)) helpers = BLEND_FRAG_HSL; const char* vertShader; char fragShader[BLEND_TOTAL_LENGTH]; if (source == BlendSource::Scene || source == BlendSource::Image) { vertShader = BLIT_VERT_SHADER; const char* header = (source == BlendSource::Scene) ? BLEND_SCENE_FRAG_HEADER : BLEND_IMAGE_FRAG_HEADER; snprintf(fragShader, BLEND_TOTAL_LENGTH, "%s%s%s", header, helpers, shaderFunc[methodInd]); mPrograms[shaderInd] = new GlProgram(vertShader, fragShader); return mPrograms[shaderInd]; } vertShader = (source == BlendSource::Solid) ? COLOR_VERT_SHADER : GRADIENT_VERT_SHADER; switch (source) { case BlendSource::Solid: snprintf(fragShader, BLEND_TOTAL_LENGTH, "%s%s%s", BLEND_SHAPE_SOLID_FRAG_HEADER, helpers, shaderFunc[methodInd]); break; case BlendSource::LinearGradient: snprintf(fragShader, BLEND_TOTAL_LENGTH, "%s%s%s%s%s%s%s", STR_GRADIENT_FRAG_COMMON_VARIABLES, STR_LINEAR_GRADIENT_VARIABLES, STR_GRADIENT_FRAG_COMMON_FUNCTIONS, STR_LINEAR_GRADIENT_FUNCTIONS, BLEND_SHAPE_LINEAR_FRAG_HEADER, helpers, shaderFunc[methodInd]); break; case BlendSource::RadialGradient: snprintf(fragShader, BLEND_TOTAL_LENGTH, "%s%s%s%s%s%s%s", STR_GRADIENT_FRAG_COMMON_VARIABLES, STR_RADIAL_GRADIENT_VARIABLES, STR_GRADIENT_FRAG_COMMON_FUNCTIONS, STR_RADIAL_GRADIENT_FUNCTIONS, BLEND_SHAPE_RADIAL_FRAG_HEADER, helpers, shaderFunc[methodInd]); break; default: TVGERR("RENDERER", "Unsupported blend source! = %d", (int)source); break; } mPrograms[shaderInd] = new GlProgram(vertShader, fragShader); return mPrograms[shaderInd]; } void GlRenderer::prepareBlitTask(GlBlitTask* task) { prepareCmpTask(task, {{0, 0}, {int32_t(surface.w), int32_t(surface.h)}}, surface.w, surface.h); task->addBindResource(GlBindingResource{0, task->getColorTexture(), task->getProgram()->getUniformLocation("uSrcTexture")}); } void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight) { const auto& passVp = currentPass()->getViewport(); auto taskVp = vp; taskVp.intersect(passVp); auto x = taskVp.sx() - passVp.sx(); auto y = taskVp.sy() - passVp.sy(); auto w = taskVp.sw(); auto h = taskVp.sh(); float rw = static_cast(passVp.w()); float rh = static_cast(passVp.h()); float l = static_cast(x); float t = static_cast(rh - y); float r = static_cast(x + w); float b = static_cast(rh - y - h); // map vp ltrp to -1:1 float left = (l / rw) * 2.f - 1.f; float top = (t / rh) * 2.f - 1.f; float right = (r / rw) * 2.f - 1.f; float bottom = (b / rh) * 2.f - 1.f; float uw = static_cast(w) / static_cast(cmpWidth); float uh = static_cast(h) / static_cast(cmpHeight); float vertices[4*4] { left, top, 0.f, uh, // left top point left, bottom, 0.f, 0.f, // left bottom point right, top, uw, uh, // right top point right, bottom, uw, 0.f // right bottom point }; uint32_t indices[6]{0, 1, 2, 2, 1, 3}; uint32_t vertexOffset = mGpuBuffer.push(vertices, sizeof(vertices)); uint32_t indexOffset = mGpuBuffer.pushIndex(indices, sizeof(indices)); task->addVertexLayout(GlVertexLayout{0, 2, 4 * sizeof(float), vertexOffset}); task->addVertexLayout(GlVertexLayout{1, 2, 4 * sizeof(float), vertexOffset + 2 * sizeof(float)}); task->setDrawRange(indexOffset, 6); y = (passVp.sh() - y - h); task->setViewport({{x, y}, {x + w, y + h}}); } void GlRenderer::endRenderPass(RenderCompositor* cmp) { auto glCmp = static_cast(cmp); // setup masking and blending render pass configurations if ((glCmp->flags & (tvg::Blending | tvg::Masking)) == (tvg::Blending | tvg::Masking)) { // rearrange render tree auto selfPass = mRenderPassStack.last(); mRenderPassStack.pop(); auto prevPass = mRenderPassStack.last(); mRenderPassStack.pop(); auto maskPass = mRenderPassStack.last(); mRenderPassStack.pop(); mRenderPassStack.push(prevPass); mRenderPassStack.push(maskPass); mRenderPassStack.push(selfPass); // setup composition properties auto prevCompose = mComposeStack.last(); auto opacity = glCmp->opacity; auto blendMethod = glCmp->blendMethod; // self scene task must be masked but not blended glCmp->method = prevCompose->method; glCmp->opacity = 255; glCmp->blendMethod = BlendMethod::Normal; // prev scene task must be blended but not masked prevCompose->method = MaskMethod::None; prevCompose->opacity = opacity; prevCompose->blendMethod = blendMethod; }; if (cmp->method != MaskMethod::None) { auto selfPass = mRenderPassStack.last(); mRenderPassStack.pop(); // mask is pushed first auto maskPass = mRenderPassStack.last(); mRenderPassStack.pop(); GlProgram* program = nullptr; switch(cmp->method) { case MaskMethod::Alpha: program = mPrograms[RT_MaskAlpha]; break; case MaskMethod::InvAlpha: program = mPrograms[RT_MaskAlphaInv]; break; case MaskMethod::Luma: program = mPrograms[RT_MaskLuma]; break; case MaskMethod::InvLuma: program = mPrograms[RT_MaskLumaInv]; break; case MaskMethod::Add: program = mPrograms[RT_MaskAdd]; break; case MaskMethod::Subtract: program = mPrograms[RT_MaskSub]; break; case MaskMethod::Intersect: program = mPrograms[RT_MaskIntersect]; break; case MaskMethod::Difference: program = mPrograms[RT_MaskDifference]; break; case MaskMethod::Lighten: program = mPrograms[RT_MaskLighten]; break; case MaskMethod::Darken: program = mPrograms[RT_MaskDarken]; break; default: break; } if (program && !selfPass->isEmpty() && !maskPass->isEmpty()) { auto prev_task = maskPass->endRenderPass(nullptr, currentPass()->getFboId()); prev_task->setDrawDepth(currentPass()->nextDrawDepth()); prev_task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h()); prev_task->setViewport(glCmp->bbox); auto compose_task = selfPass->endRenderPass(program, currentPass()->getFboId()); compose_task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h()); compose_task->setPrevTask(prev_task); prepareCmpTask(compose_task, glCmp->bbox, selfPass->getFboWidth(), selfPass->getFboHeight()); compose_task->addBindResource(GlBindingResource{0, selfPass->getTextureId(), program->getUniformLocation("uSrcTexture")}); compose_task->addBindResource(GlBindingResource{1, maskPass->getTextureId(), program->getUniformLocation("uMaskTexture")}); compose_task->setDrawDepth(currentPass()->nextDrawDepth()); compose_task->setParentSize(currentPass()->getViewport().w(), currentPass()->getViewport().h()); currentPass()->addRenderTask(compose_task); } delete(selfPass); delete(maskPass); } else if (glCmp->blendMethod != BlendMethod::Normal) { auto renderPass = mRenderPassStack.last(); mRenderPassStack.pop(); if (!renderPass->isEmpty()) { if (mBlendPool.count < 1) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); #if defined(THORVG_GL_TARGET_GL) auto dstCopyFbo = mBlendPool[1]->getRenderTarget(renderPass->getViewport()); #else // TODO: create partial buffer when MSAA is disabled auto dstCopyFbo = mBlendPool[1]->getRenderTarget(currentPass()->getViewport()); #endif // image info uint32_t info[4] = {(uint32_t)ColorSpace::ABGR8888, 0, cmp->opacity, 0}; auto program = getBlendProgram(glCmp->blendMethod, BlendSource::Scene); auto task = renderPass->endRenderPass(program, currentPass()->getFboId()); task->setSrcTarget(currentPass()->getFbo()); task->setDstCopy(dstCopyFbo); task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h()); prepareCmpTask(task, glCmp->bbox, renderPass->getFboWidth(), renderPass->getFboHeight()); task->setDrawDepth(currentPass()->nextDrawDepth()); #if defined(THORVG_GL_TARGET_GL) const auto& taskVp = task->getViewport(); float region[] = {float(taskVp.sx()), float(taskVp.sy()), float(dstCopyFbo->width), float(dstCopyFbo->height)}; #else // TODO: create partial buffer when MSAA is disabled float region[] = {0.0f, 0.0f, float(dstCopyFbo->width), float(dstCopyFbo->height)}; #endif task->addBindResource(GlBindingResource{ 1, task->getProgram()->getUniformBlockIndex("BlendRegion"), mGpuBuffer.getBufferId(), mGpuBuffer.push(region, 4 * sizeof(float), true), 4 * sizeof(float), }); // info task->addBindResource(GlBindingResource{0, task->getProgram()->getUniformBlockIndex("ColorInfo"), mGpuBuffer.getBufferId(), mGpuBuffer.push(info, sizeof(info), true), sizeof(info)}); // textures task->addBindResource(GlBindingResource{0, renderPass->getTextureId(), task->getProgram()->getUniformLocation("uSrcTexture")}); task->addBindResource(GlBindingResource{1, dstCopyFbo->colorTex, task->getProgram()->getUniformLocation("uDstTexture")}); task->setParentSize(currentPass()->getViewport().w(), currentPass()->getViewport().h()); currentPass()->addRenderTask(std::move(task)); } delete(renderPass); } else { auto renderPass = mRenderPassStack.last(); mRenderPassStack.pop(); if (!renderPass->isEmpty()) { auto task = renderPass->endRenderPass(mPrograms[RT_Image], currentPass()->getFboId()); task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h()); prepareCmpTask(task, glCmp->bbox, renderPass->getFboWidth(), renderPass->getFboHeight()); task->setDrawDepth(currentPass()->nextDrawDepth()); task->setViewMatrix(tvg::identity()); // image info uint32_t info[4] = {(uint32_t)ColorSpace::ABGR8888, 0, cmp->opacity, 0}; task->addBindResource(GlBindingResource{ 1, task->getProgram()->getUniformBlockIndex("ColorInfo"), mGpuBuffer.getBufferId(), mGpuBuffer.push(info, 4 * sizeof(uint32_t), true), 4 * sizeof(uint32_t), }); // texture id task->addBindResource(GlBindingResource{0, renderPass->getTextureId(), task->getProgram()->getUniformLocation("uTexture")}); task->setParentSize(currentPass()->getViewport().w(), currentPass()->getViewport().h()); currentPass()->addRenderTask(std::move(task)); } delete(renderPass); } } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool GlRenderer::clear() { if (mRootTarget.invalid()) return false; mClearBuffer = true; return true; } bool GlRenderer::target(void* display, void* surface, void* context, int32_t id, uint32_t w, uint32_t h, ColorSpace cs) { //assume the context zero is invalid if (!context || w == 0 || h == 0) return false; if (mContext) currentContext(); flush(); this->surface.stride = w; this->surface.w = w; this->surface.h = h; this->surface.cs = cs; mDisplay = display; mSurface = surface; mContext = context; mTargetFboId = static_cast(id); auto ret = currentContext(); mRootTarget.viewport = {{0, 0}, {int32_t(this->surface.w), int32_t(this->surface.h)}}; mRootTarget.init(this->surface.w, this->surface.h, mTargetFboId); return ret; } bool GlRenderer::sync() { //nothing to be done. if (mRenderPassStack.empty()) return true; currentContext(); // Blend function for straight alpha GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); GL_CHECK(glEnable(GL_BLEND)); GL_CHECK(glEnable(GL_SCISSOR_TEST)); GL_CHECK(glCullFace(GL_FRONT_AND_BACK)); GL_CHECK(glFrontFace(GL_CCW)); GL_CHECK(glEnable(GL_DEPTH_TEST)); GL_CHECK(glDepthFunc(GL_GREATER)); auto task = mRenderPassStack.first()->endRenderPass(mPrograms[RT_Blit], mTargetFboId); prepareBlitTask(task); task->mClearBuffer = mClearBuffer; task->setTargetViewport({{0, 0}, {int32_t(surface.w), int32_t(surface.h)}}); if (mGpuBuffer.flushToGPU()) { mGpuBuffer.bind(); task->run(); } mGpuBuffer.unbind(); GL_CHECK(glDisable(GL_SCISSOR_TEST)); clearDisposes(); // Reset clear buffer flag to default (false) after use. mClearBuffer = false; delete task; return true; } bool GlRenderer::bounds(RenderData data, Point* pt4, const Matrix& m) { if (data) { auto sdata = static_cast(data); if (sdata->validStroke) { tvg::BBox bbox; bbox.init(); auto& vertexes = sdata->geometry.stroke.vertex; if (m == sdata->geometry.matrix) { // Common AABB path: stroke vertices are already in world space. for (uint32_t i = 0; i < vertexes.count / 2; i++) { Point vert = {vertexes[i * 2 + 0], vertexes[i * 2 + 1]}; bbox = { min(bbox.min, vert), max(bbox.max, vert)}; } } else { // GL stroke vertices are generated in world space. // Normalize to local space first, then remap to the caller-requested space. // - OBB path passes m = identity -> result becomes local (caller applies model later). const auto inverseModel = sdata->geometry.inverseMatrix(); for (uint32_t i = 0; i < vertexes.count / 2; i++) { Point vert = {vertexes[i * 2 + 0], vertexes[i * 2 + 1]}; vert *= (*inverseModel) * m; bbox = { min(bbox.min, vert), max(bbox.max, vert)}; } } pt4[0] = bbox.min; pt4[1] = {bbox.max.x, bbox.min.y}; pt4[2] = bbox.max; pt4[3] = {bbox.min.x, bbox.max.y}; return true; } } return false; } RenderRegion GlRenderer::region(RenderData data) { if (!data) return {}; auto shape = reinterpret_cast(data); return shape->geometry.getBounds(); } bool GlRenderer::preRender() { if (mRootTarget.invalid()) return false; currentContext(); if (mPrograms.empty()) initShaders(); mRenderPassStack.push(new GlRenderPass(&mRootTarget)); return true; } bool GlRenderer::postRender() { return true; } RenderCompositor* GlRenderer::target(const RenderRegion& region, TVG_UNUSED ColorSpace cs, TVG_UNUSED CompositionFlag flags) { auto vp = region; if (currentPass()->isEmpty()) return nullptr; vp.intersect(currentPass()->getViewport()); mComposeStack.push(new GlCompositor(vp, flags)); return mComposeStack.last(); } bool GlRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) { if (!cmp) return false; auto glCmp = static_cast(cmp); glCmp->method = method; glCmp->opacity = opacity; glCmp->blendMethod = mBlendMethod; uint32_t index = mRenderPassStack.count - 1; if (index >= mComposePool.count) mComposePool.push( new GlRenderTargetPool(surface.w, surface.h)); if (glCmp->bbox.valid()) mRenderPassStack.push(new GlRenderPass(mComposePool[index]->getRenderTarget(glCmp->bbox))); else mRenderPassStack.push(new GlRenderPass(nullptr)); return true; } bool GlRenderer::endComposite(RenderCompositor* cmp) { if (mComposeStack.empty()) return false; if (mComposeStack.last() != cmp) return false; // end current render pass; auto curCmp = mComposeStack.last(); mComposeStack.pop(); assert(cmp == curCmp); endRenderPass(curCmp); delete(curCmp); return true; } void GlRenderer::prepare(RenderEffect* effect, const Matrix& transform) { // we must be sure, that we have intermediate FBOs if (mBlendPool.count < 1) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); mEffect.update(effect, transform); } bool GlRenderer::region(RenderEffect* effect) { return mEffect.region(effect); } bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* effect, TVG_UNUSED bool direct) { return mEffect.render(const_cast(effect), currentPass(), mBlendPool); } void GlRenderer::dispose(RenderEffect* effect) { tvg::free(effect->rd); effect->rd = nullptr; } ColorSpace GlRenderer::colorSpace() { return surface.cs; } const RenderSurface* GlRenderer::mainSurface() { return &surface; } bool GlRenderer::blend(BlendMethod method) { if (method == mBlendMethod) return true; mBlendMethod = (method == BlendMethod::Composition ? BlendMethod::Normal : method); return true; } bool GlRenderer::renderImage(void* data) { auto sdata = static_cast(data); if (!sdata) return false; if (currentPass()->isEmpty() || !sdata->validFill) return true; auto vp = currentPass()->getViewport(); auto bbox = sdata->geometry.viewport; bbox.intersect(vp); if (bbox.invalid()) return true; auto x = bbox.sx() - vp.sx(); auto y = bbox.sy() - vp.sy(); auto drawDepth = currentPass()->nextDrawDepth(); if (!sdata->clips.empty()) drawClip(sdata->clips); auto task = new GlRenderTask(mPrograms[RT_Image]); task->setDrawDepth(drawDepth); if (!sdata->geometry.draw(task, &mGpuBuffer, RenderUpdateFlag::Image)) { delete task; return true; } bool complexBlend = beginComplexBlending(bbox, sdata->geometry.getBounds()); if (complexBlend) vp = currentPass()->getViewport(); task->setViewMatrix(currentPass()->getViewMatrix()); // image info uint32_t info[4] = {(uint32_t)sdata->texColorSpace, sdata->texFlipY, sdata->opacity, 0}; task->addBindResource(GlBindingResource{ 1, task->getProgram()->getUniformBlockIndex("ColorInfo"), mGpuBuffer.getBufferId(), mGpuBuffer.push(info, 4 * sizeof(uint32_t), true), 4 * sizeof(uint32_t), }); // texture id task->addBindResource(GlBindingResource{0, sdata->texId, task->getProgram()->getUniformLocation("uTexture")}); y = vp.sh() - y - bbox.sh(); auto x2 = x + bbox.sw(); auto y2 = y + bbox.sh(); task->setViewport({{x, y}, {x2, y2}}); currentPass()->addRenderTask(task); if (complexBlend) { auto task = new GlRenderTask(mPrograms[RT_Stencil]); sdata->geometry.draw(task, &mGpuBuffer, RenderUpdateFlag::Image); endBlendingCompose(task); } return true; } bool GlRenderer::renderShape(RenderData data) { auto sdata = static_cast(data); if (currentPass()->isEmpty() || (!sdata->validFill && !sdata->validStroke)) return true; auto bbox = sdata->geometry.viewport; bbox.intersect(currentPass()->getViewport()); if (bbox.invalid()) return true; int32_t drawDepth1 = 0, drawDepth2 = 0; if (sdata->validFill) drawDepth1 = currentPass()->nextDrawDepth(); if (sdata->validStroke) drawDepth2 = currentPass()->nextDrawDepth(); if (!sdata->clips.empty()) drawClip(sdata->clips); auto processFill = [&]() { if (sdata->validFill) { if (const auto& gradient = sdata->rshape->fill) { drawPrimitive(*sdata, gradient, RenderUpdateFlag::Gradient, drawDepth1); } else if (sdata->rshape->color.a > 0) { drawPrimitive(*sdata, sdata->rshape->color, RenderUpdateFlag::Color, drawDepth1); } } }; auto processStroke = [&]() { if (sdata->validStroke) { if (const auto& gradient = sdata->rshape->strokeFill()) { drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke, drawDepth2); } else if (sdata->rshape->stroke->color.a > 0) { drawPrimitive(*sdata, sdata->rshape->stroke->color, RenderUpdateFlag::Stroke, drawDepth2); } } }; if (sdata->rshape->strokeFirst()) { processStroke(); processFill(); } else { processFill(); processStroke(); } return true; } void GlRenderer::dispose(RenderData data) { auto sdata = static_cast(data); //dispose the non thread-safety resources on clearDisposes() call if (sdata->texId) { ScopedLock lock(mDisposed.key); mDisposed.textures.push(sdata->texId); } delete sdata; } static GLuint _genTexture(RenderSurface* image) { GLuint tex = 0; GL_CHECK(glGenTextures(1, &tex)); GL_CHECK(glBindTexture(GL_TEXTURE_2D, tex)); GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->data)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0)); return tex; } RenderData GlRenderer::prepare(RenderSurface* image, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) { //TODO: redefine GlImage. auto sdata = static_cast(data); if (!sdata) sdata = new GlShape; sdata->validFill = false; if (opacity == 0 || flags == RenderUpdateFlag::None) return data; sdata->viewWd = static_cast(surface.w); sdata->viewHt = static_cast(surface.h); if (sdata->texId == 0) { sdata->texId = _genTexture(image); sdata->texColorSpace = image->cs; sdata->texFlipY = 1; sdata->geometry = GlGeometry(); } sdata->opacity = opacity; sdata->geometry.setMatrix(transform); sdata->geometry.viewport = vport; sdata->geometry.tesselateImage(image); sdata->validFill = true; if (flags & RenderUpdateFlag::Clip) { sdata->clips.clear(); sdata->clips.push(clips); } return sdata; } RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) { auto sdata = static_cast(data); if (!sdata) { sdata = new GlShape; sdata->rshape = &rshape; flags = RenderUpdateFlag::All; } if ((opacity == 0 && !clipper) || flags == RenderUpdateFlag::None) return sdata; sdata->viewWd = static_cast(surface.w); sdata->viewHt = static_cast(surface.h); sdata->opacity = opacity; if (flags & RenderUpdateFlag::Path) sdata->geometry = GlGeometry(); sdata->geometry.setMatrix(transform); sdata->geometry.viewport = vport; if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) sdata->geometry.prepare(rshape); //TODO: Please precisely update tessellation not to update only if the color is changed. if (flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Path)) { sdata->validFill = false; float opacityMultiplier = 1.0f; if (sdata->geometry.tesselateShape(*(sdata->rshape), &opacityMultiplier)) { sdata->opacity *= opacityMultiplier; sdata->validFill = true; } } //TODO: Please precisely update tessellation not to update only if the color is changed. if (flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke | RenderUpdateFlag::Transform | RenderUpdateFlag::Path)) { sdata->validStroke = false; if (sdata->geometry.tesselateStroke(*(sdata->rshape))) sdata->validStroke = true; } if (flags & RenderUpdateFlag::Clip) { sdata->clips.clear(); sdata->clips.push(clips); } return sdata; } bool GlRenderer::preUpdate() { if (mRootTarget.invalid()) return false; currentContext(); return true; } bool GlRenderer::postUpdate() { return true; } void GlRenderer::damage(TVG_UNUSED RenderData rd, TVG_UNUSED const RenderRegion& region) { //TODO } bool GlRenderer::partial(bool disable) { //TODO return false; } bool GlRenderer::intersectsShape(RenderData data, TVG_UNUSED const RenderRegion& region) { if (!data) return false; auto shape = (GlShape*)data; const auto& bbox = shape->geometry.getBounds(); if (region.intersected(bbox)) { if (region.contained(bbox)) return true; GlIntersector intersector; return intersector.intersectShape(RenderRegion::intersect(region, bbox), shape); } return false; } bool GlRenderer::intersectsImage(RenderData data, TVG_UNUSED const RenderRegion& region) { if (!data) return false; auto shape = (GlShape*)data; const auto& bbox = shape->geometry.getBounds(); if (region.intersected(bbox)) { if (region.contained(bbox)) return true; GlIntersector intersector; if (intersector.intersectImage(RenderRegion::intersect(region, bbox), shape)) return true; } return false; } bool GlRenderer::term() { if (rendererCnt > 0) return false; glTerm(); rendererCnt = -1; return true; } GlRenderer* GlRenderer::gen(TVG_UNUSED uint32_t threads) { //initialize engine if (rendererCnt == -1) { if (!glInit()) { TVGERR("GL_ENGINE", "Failed GL initialization!"); return nullptr; } rendererCnt = 0; } return new GlRenderer; } static void _solidUniforms(GlShape& sdata, const RenderColor& c, RenderUpdateFlag flag, float* solidInfo) { auto a = MULTIPLY(c.a, sdata.opacity); if (flag & RenderUpdateFlag::Stroke) { auto strokeWidth = sdata.geometry.strokeRenderWidth; if (strokeWidth < MIN_GL_STROKE_WIDTH) { auto alpha = strokeWidth / MIN_GL_STROKE_WIDTH; a = MULTIPLY(a, static_cast(alpha * 255)); } } solidInfo[0] = c.r / 255.f; solidInfo[1] = c.g / 255.f; solidInfo[2] = c.b / 255.f; solidInfo[3] = a / 255.f; } thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-number-object.h000664 001750 001750 00000001671 15164251010 046224 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_NUMBER_OBJECT_H #define ECMA_NUMBER_OBJECT_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmanumberobject ECMA Number object related routines * @{ */ ecma_value_t ecma_op_create_number_object (ecma_value_t arg); /** * @} * @} */ #endif /* !ECMA_NUMBER_OBJECT_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieParser.h000664 001750 001750 00000013426 15164251010 036173 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_PARSER_H_ #define _TVG_LOTTIE_PARSER_H_ #include "tvgCommon.h" #include "tvgLottieParserHandler.h" #include "tvgLottieProperty.h" struct LottieParser : LookaheadParserHandler { public: LottieParser(const char *str, const char* dirName, bool expressions) : LookaheadParserHandler(str) { this->dirName = dirName; this->expressions = expressions; } bool parse(); const char* sid(bool first = false); LottieProperty* parse(LottieSlot* slot); LottieComposition* comp = nullptr; const char* dirName = nullptr; //base resource directory char* slots = nullptr; bool expressions = false; //support expressions? private: RGB32 getColor(const char *str); FillRule getFillRule(); MaskMethod getMaskMethod(bool inversed); LottieInterpolator* getInterpolator(const char* key, Point& in, Point& out); LottieEffect* getEffect(int type); void getExpression(char* code, LottieComposition* comp, LottieLayer* layer, LottieObject* object, LottieProperty* property); void getInterpolatorPoint(Point& pt); void getPathSet(LottiePath* obj, LottiePathSet& path); void getLayerSize(float& val); bool getValue(TextDocument& doc); bool getValue(PathSet& path); bool getValue(Array& pts); bool getValue(ColorStop& color); bool getValue(float& val); bool getValue(uint8_t& val); bool getValue(int8_t& val); bool getValue(RGB32& color); bool getValue(Point& pt); template bool parseTangent(const char *key, LottieVectorFrame& value); template bool parseTangent(const char *key, LottieScalarFrame& value); template void parseKeyFrame(T& prop); template void parsePropertyInternal(T& prop); template void parseProperty(T& prop, LottieObject* obj = nullptr); template void parseSlotProperty(T& prop); LottieObject* parseObject(const char* type); LottieObject* parseAsset(); void parseImage(LottieImage* image, const char* data, const char* subPath, bool embedded, float width, float height); LottieLayer* parseLayer(LottieLayer* precomp); LottieObject* parseGroup(); LottieRect* parseRect(); LottieEllipse* parseEllipse(); LottieSolidFill* parseSolidFill(); LottieTransform* parseTransform(bool ddd = false); LottieSolidStroke* parseSolidStroke(); LottieGradientStroke* parseGradientStroke(); LottiePath* parsePath(); LottiePolyStar* parsePolyStar(); LottieRoundedCorner* parseRoundedCorner(); LottieGradientFill* parseGradientFill(); LottieLayer* parseLayers(LottieLayer* root); LottieMask* parseMask(); LottieTrimpath* parseTrimpath(); LottieRepeater* parseRepeater(); LottieOffsetPath* parseOffsetPath(); LottieFont* parseFont(); void parseFontData(LottieFont* font, const char* data); LottieMarker* parseMarker(); bool parseEffect(LottieEffect* effect, void(LottieParser::*func)(LottieEffect*, int)); void parseCustom(LottieEffect* effect, int idx); void parseStroke(LottieEffect* effect, int idx); void parseTritone(LottieEffect* effect, int idx); void parseTint(LottieEffect* effect, int idx); void parseFill(LottieEffect* effect, int idx); void parseGaussianBlur(LottieEffect* effect, int idx); void parseDropShadow(LottieEffect* effect, int idx); bool parseDirection(LottieShape* shape, const char* key); bool parseCommon(LottieObject* obj, const char* key); bool parseCommon(LottieObject* obj, LottieProperty& prop, const char* key); void parseObject(Array& parent); void parseShapes(Array& parent); void parseText(Array& parent); void parseMasks(LottieLayer* layer); void parseEffects(LottieLayer* layer); void parseTimeRemap(LottieLayer* layer); void parseStrokeDash(LottieStroke* stroke); void parseGradient(LottieGradient* gradient, const char* key); void parseColorStop(LottieGradient* gradient); void parseTextRange(LottieText* text); void parseTextAlignmentOption(LottieText* text); void parseTextFollowPath(LottieText* text); void parseAssets(); void parseFonts(); void parseChars(Array& glyphs); void parseMarkers(); bool parseEffect(LottieEffect* effect); void postProcess(Array& glyphs); char* captureType(); void captureSlots(const char* key); void registerSlot(LottieObject* obj, const char* sid, LottieProperty& prop); //Current parsing context struct Context { LottieLayer* layer = nullptr; LottieObject* parent = nullptr; } context; }; #endif //_TVG_LOTTIE_PARSER_H_ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-typeerror-prototype.cpp000664 001750 001750 00000002265 15164251010 052477 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-typeerror-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID type_error_prototype #include "ecma-builtin-internal-routines-template.inc.h" #endif /* JERRY_BUILTIN_ERRORS */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/include/jerryscript-port.h000664 001750 001750 00000023245 15164251010 044463 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JERRYSCRIPT_PORT_H #define JERRYSCRIPT_PORT_H #include "jerryscript-types.h" JERRY_C_API_BEGIN /** * @defgroup jerry-port JerryScript Port API * @{ */ /** * @defgroup jerry-port-process Process management API * * It is questionable whether a library should be able to terminate an * application. However, as of now, we only have the concept of completion * code around jerry_parse and jerry_run. Most of the other API functions * have no way of signaling an error. So, we keep the termination approach * with this port function. * * @{ */ /** * Error codes that can be passed by the engine when calling jerry_port_fatal */ typedef enum { JERRY_FATAL_OUT_OF_MEMORY = 10, /**< Out of memory */ JERRY_FATAL_REF_COUNT_LIMIT = 12, /**< Reference count limit reached */ JERRY_FATAL_DISABLED_BYTE_CODE = 13, /**< Executed disabled instruction */ JERRY_FATAL_UNTERMINATED_GC_LOOPS = 14, /**< Garbage collection loop limit reached */ JERRY_FATAL_FAILED_ASSERTION = 120 /**< Assertion failed */ } jerry_fatal_code_t; /** * Signal the port that the process experienced a fatal failure from which it cannot * recover. * * A libc-based port may implement this with exit() or abort(), or both. * * @param code: the cause of the error. * @return This function is expected to not return. */ void JERRY_ATTR_NORETURN jerry_port_fatal (jerry_fatal_code_t code); /** * Make the process sleep for a given time. * * This port function can be called by jerry-core when JERRY_DEBUGGER is enabled. * Otherwise this function is not used. * * @param sleep_time: milliseconds to sleep. */ void jerry_port_sleep (uint32_t sleep_time); /** * jerry-port-process @} */ /** * @defgroup jerry-port-context External Context API * @{ */ /** * Allocate a new context for the engine. * * This port function is called by jerry_init when JERRY_EXTERNAL_CONTEXT is enabled. Otherwise this function is not * used. * * The engine will pass the size required for the context structure. An implementation must make sure to * allocate at least this amount. * * Excess allocated space will be used as the engine heap when JerryScript is configured to use it's internal allocator, * this can be used to control the internal heap size. * * NOTE: The allocated memory must be pointer-aligned, otherwise the behavior is undefined. * * @param context_size: the size of the internal context structure * * @return total size of the allocated buffer */ size_t jerry_port_context_alloc (size_t context_size); /** * Get the currently active context of the engine. * * This port function is called by jerry-core when JERRY_EXTERNAL_CONTEXT is enabled. * Otherwise this function is not used. * * @return the pointer to the currently used engine context. */ struct jerry_context_t *jerry_port_context_get (void); /** * Free the currently used context. * * This port function is called by jerry_cleanup when JERRY_EXTERNAL_CONTEXT is enabled. * Otherwise this function is not used. */ void jerry_port_context_free (void); /** * jerry-port-context @} */ /** * @defgroup jerry-port-io I/O API * @{ */ /** * Display or log a debug/error message. * * The message is passed as a zero-terminated string. Messages may be logged in parts, which * will result in multiple calls to this functions. The implementation should consider * this before appending or prepending strings to the argument. * * This function is called with messages coming from the jerry engine as * the result of some abnormal operation or describing its internal operations * (e.g., data structure dumps or tracing info). * * The implementation can decide whether error and debug messages are logged to * the console, or saved to a database or to a file. */ void jerry_port_log (const char *message_p); /** * Print a single character to standard output. * * This port function is never called from jerry-core directly, it is only used by jerry-ext components to print * information. * * @param byte: the byte to print. */ void jerry_port_print_byte (jerry_char_t byte); /** * Print a buffer to standard output * * This port function is never called from jerry-core directly, it is only used by jerry-ext components to print * information. * * @param buffer_p: input buffer * @param buffer_size: data size */ void jerry_port_print_buffer (const jerry_char_t *buffer_p, jerry_size_t buffer_size); /** * Read a line from standard input. * * The implementation should allocate storage necessary for the string. The result string should include the ending line * terminator character(s) and should be zero terminated. * * An implementation may return NULL to signal that the end of input is reached, or an error occurred. * * When a non-NULL value is returned, the caller will pass the returned value to `jerry_port_line_free` when the line is * no longer needed. This can be used to finalize dynamically allocated buffers if necessary. * * This port function is never called from jerry-core directly, it is only used by some jerry-ext components that * require user input. * * @param out_size_p: size of the input string in bytes, excluding terminating zero byte * * @return pointer to the buffer storing the string, * or NULL if end of input */ jerry_char_t *jerry_port_line_read (jerry_size_t *out_size_p); /** * Free a line buffer allocated by jerry_port_line_read * * @param buffer_p: buffer returned by jerry_port_line_read */ void jerry_port_line_free (jerry_char_t *buffer_p); /** * jerry-port-io @} */ /** * @defgroup jerry-port-fd Filesystem API * @{ */ /** * Canonicalize a file path. * * If possible, the implementation should resolve symbolic links and other directory references found in the input path, * and create a fully canonicalized file path as the result. * * The function may return with NULL in case an error is encountered, in which case the calling operation will not * proceed. * * The implementation should allocate storage for the result path as necessary. Non-NULL return values will be passed * to `jerry_port_path_free` when the result is no longer needed by the caller, which can be used to finalize * dynamically allocated buffers. * * NOTE: The implementation must not return directly with the input, as the input buffer is released after the call. * * @param path_p: zero-terminated string containing the input path * @param path_size: size of the input path string in bytes, excluding terminating zero * * @return buffer with the normalized path if the operation is successful, * NULL otherwise */ jerry_char_t *jerry_port_path_normalize (const jerry_char_t *path_p, jerry_size_t path_size); /** * Free a path buffer returned by jerry_port_path_normalize. * * @param path_p: the path buffer to free */ void jerry_port_path_free (jerry_char_t *path_p); /** * Get the offset of the basename component in the input path. * * The implementation should return the offset of the first character after the last path separator found in the path. * This is used by the caller to split the path into a directory name and a file name. * * @param path_p: input zero-terminated path string * * @return offset of the basename component in the input path */ jerry_size_t jerry_port_path_base (const jerry_char_t *path_p); /** * Open a source file and read the content into a buffer. * * When the source file is no longer needed by the caller, the returned pointer will be passed to * `jerry_port_source_free`, which can be used to finalize the buffer. * * @param file_name_p: Path that points to the source file in the filesystem. * @param out_size_p: The opened file's size in bytes. * * @return pointer to the buffer which contains the content of the file. */ jerry_char_t *jerry_port_source_read (const char *file_name_p, jerry_size_t *out_size_p); /** * Free a source file buffer. * * @param buffer_p: buffer returned by jerry_port_source_read */ void jerry_port_source_free (jerry_char_t *buffer_p); /** * jerry-port-fs @} */ /** * @defgroup jerry-port-date Date API * @{ */ /** * Get local time zone adjustment in milliseconds for the given input time. * * The argument is a time value representing milliseconds since unix epoch. * * Ideally, this function should satisfy the stipulations applied to LocalTZA * in section 21.4.1.7 of the ECMAScript version 12.0, as if called with isUTC true. * * This port function can be called by jerry-core when JERRY_BUILTIN_DATE is enabled. * Otherwise this function is not used. * * @param unix_ms: time value in milliseconds since unix epoch * * @return local time offset in milliseconds applied to UTC for the given time value */ int32_t jerry_port_local_tza (double unix_ms); /** * Get the current system time in UTC. * * This port function is called by jerry-core when JERRY_BUILTIN_DATE is enabled. * It can also be used in the implementing application to initialize the random number generator. * * @return milliseconds since Unix epoch */ double jerry_port_current_time (void); /** * jerry-port-date @} */ /** * jerry-port @} */ JERRY_C_API_END #endif /* !JERRYSCRIPT_PORT_H */ /* vim: set fdm=marker fmr=@{,@}: */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/raw/000775 001750 001750 00000000000 15164251010 032226 5ustar00ddennedyddennedy000000 000000 src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h000664 001750 001750 00000021075 15164251010 036641 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifdef THORVG_AVX_VECTOR_SUPPORT #include #define N_32BITS_IN_128REG 4 #define N_32BITS_IN_256REG 8 static inline __m128i ALPHA_BLEND(__m128i c, __m128i a) { //1. set the masks for the A/G and R/B channels auto AG = _mm_set1_epi32(0xff00ff00); auto RB = _mm_set1_epi32(0x00ff00ff); //2. mask the alpha vector - originally quartet [a, a, a, a] auto aAG = _mm_and_si128(a, AG); auto aRB = _mm_and_si128(a, RB); //3. calculate the alpha blending of the 2nd and 4th channel //- mask the color vector //- multiply it by the masked alpha vector //- add the correction to compensate bit shifting used instead of dividing by 255 //- shift bits - corresponding to division by 256 auto even = _mm_and_si128(c, RB); even = _mm_mullo_epi16(even, aRB); even =_mm_add_epi16(even, RB); even = _mm_srli_epi16(even, 8); //4. calculate the alpha blending of the 1st and 3rd channel: //- mask the color vector //- multiply it by the corresponding masked alpha vector and store the high bits of the result //- add the correction to compensate division by 256 instead of by 255 (next step) //- remove the low 8 bits to mimic the division by 256 auto odd = _mm_and_si128(c, AG); odd = _mm_mulhi_epu16(odd, aAG); odd = _mm_add_epi16(odd, RB); odd = _mm_and_si128(odd, AG); //5. the final result return _mm_or_si128(odd, even); } static void avxRasterGrayscale8(uint8_t* dst, uint8_t val, uint32_t offset, int32_t len) { dst += offset; __m256i vecVal = _mm256_set1_epi8(val); int32_t i = 0; for (; i <= len - 32; i += 32) { _mm256_storeu_si256((__m256i*)(dst + i), vecVal); } for (; i < len; ++i) { dst[i] = val; } } static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) { //1. calculate how many iterations we need to cover the length uint32_t iterations = len / N_32BITS_IN_256REG; uint32_t avxFilled = iterations * N_32BITS_IN_256REG; //2. set the beginning of the array dst += offset; //3. fill the octets for (uint32_t i = 0; i < iterations; ++i, dst += N_32BITS_IN_256REG) { _mm256_storeu_si256((__m256i*)dst, _mm256_set1_epi32(val)); } //4. fill leftovers (in the first step we have to set the pointer to the place where the avx job is done) int32_t leftovers = len - avxFilled; while (leftovers--) *dst++ = val; } static bool avxRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c) { auto h = bbox.h(); auto w = bbox.w(); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; uint32_t ialpha = 255 - c.a; auto avxColor = _mm_set1_epi32(color); auto avxIalpha = _mm_set1_epi8(ialpha); for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) auto notAligned = ((uintptr_t)dst & 0xf) / 4; if (notAligned) { notAligned = (N_32BITS_IN_128REG - notAligned > w ? w : N_32BITS_IN_128REG - notAligned); for (uint32_t x = 0; x < notAligned; ++x, ++dst) { *dst = color + ALPHA_BLEND(*dst, ialpha); } } //2. fill the aligned memory - N_32BITS_IN_128REG pixels processed at once uint32_t iterations = (w - notAligned) / N_32BITS_IN_128REG; uint32_t avxFilled = iterations * N_32BITS_IN_128REG; auto avxDst = (__m128i*)dst; for (uint32_t x = 0; x < iterations; ++x, ++avxDst) { *avxDst = _mm_add_epi32(avxColor, ALPHA_BLEND(*avxDst, avxIalpha)); } //3. fill the remaining pixels int32_t leftovers = w - notAligned - avxFilled; dst += avxFilled; while (leftovers--) { *dst = color + ALPHA_BLEND(*dst, ialpha); dst++; } } //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize); auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x; auto ialpha = ~c.a; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < w; ++x, ++dst) { *dst = c.a + MULTIPLY(*dst, ialpha); } } } return true; } static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c) { const SwSpan* end; int32_t x, len; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(c.r, c.g, c.b, c.a); uint32_t src; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); else src = color; auto dst = &surface->buf32[span->y * surface->stride + x]; auto ialpha = IA(src); //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) int32_t notAligned = ((uintptr_t)dst & 0xf) / 4; if (notAligned) { notAligned = (N_32BITS_IN_128REG - notAligned > len ? len : N_32BITS_IN_128REG - notAligned); for (auto x = 0; x < notAligned; ++x, ++dst) { *dst = src + ALPHA_BLEND(*dst, ialpha); } } //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once //In order to avoid unnecessary avx variables declarations a check is made whether there are any iterations at all int32_t iterations = (len - notAligned) / N_32BITS_IN_128REG; int32_t avxFilled = 0; if (iterations > 0) { auto avxSrc = _mm_set1_epi32(src); auto avxIalpha = _mm_set1_epi8(ialpha); avxFilled = iterations * N_32BITS_IN_128REG; auto avxDst = (__m128i*)dst; for (auto x = 0; x < iterations; ++x, ++avxDst) { *avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha)); } } //3. fill the remaining pixels auto leftovers = len - notAligned - avxFilled; dst += avxFilled; while (leftovers--) { *dst = src + ALPHA_BLEND(*dst, ialpha); dst++; } } //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize); uint8_t src; for (auto span = rle->fetch(bbox, &end); span < end; ++span) { if (!span->fetch(bbox, x, len)) continue; auto dst = &surface->buf8[span->y * surface->stride + x]; if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); else src = c.a; auto ialpha = ~c.a; for (auto x = 0; x < len; ++x, ++dst) { *dst = src + MULTIPLY(*dst, ialpha); } } } return true; } #endif mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgLock.h000664 001750 001750 00000003552 15164251010 033063 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOCK_H_ #define _TVG_LOCK_H_ #ifdef THORVG_THREAD_SUPPORT #include #include "tvgTaskScheduler.h" namespace tvg { struct Key { std::mutex mtx; }; struct ScopedLock { Key* key = nullptr; ScopedLock(Key& k) { if (TaskScheduler::threads() > 0) { k.mtx.lock(); key = &k; } } ~ScopedLock() { if (TaskScheduler::threads() > 0) { key->mtx.unlock(); } } }; } #else //THORVG_THREAD_SUPPORT namespace tvg { struct Key {}; struct ScopedLock { ScopedLock(Key& key) {} }; } #endif //THORVG_THREAD_SUPPORT #endif //_TVG_LOCK_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgLoader.h000664 001750 001750 00000004076 15164251010 035023 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SVG_LOADER_H_ #define _TVG_SVG_LOADER_H_ #include "tvgTaskScheduler.h" #include "tvgSvgLoaderCommon.h" class SvgLoader : public ImageLoader, public Task { public: string svgPath = ""; char* content = nullptr; uint32_t size = 0; SvgLoaderData loaderData; Scene* root = nullptr; bool copy = false; SvgLoader(); ~SvgLoader(); bool open(const char* path) override; bool open(const char* data, uint32_t size, const char* rpath, bool copy) override; bool resize(Paint* paint, float w, float h) override; bool read() override; bool close() override; Paint* paint() override; private: SvgViewFlag viewFlag = SvgViewFlag::None; AspectRatioAlign align = AspectRatioAlign::XMidYMid; AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; Box vbox{}; bool header(); void clear(bool all = true); void run(unsigned tid) override; }; #endif //_TVG_SVG_LOADER_H_ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jrt/jrt-bit-fields.h000664 001750 001750 00000003120 15164251010 043102 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JRT_BIT_FIELDS_H #define JRT_BIT_FIELDS_H /** * Extract a bit-field. * * @param type type of container * @param container container to extract bit-field from * @param lsb least significant bit of the value to be extracted * @param width width of the bit-field to be extracted * @return bit-field's value */ #define JRT_EXTRACT_BIT_FIELD(type, container, lsb, width) (((container) >> lsb) & ((((type) 1) << (width)) - 1)) /** * Set a bit-field. * * @param type type of container * @param container container to insert bit-field to * @param new_bit_field_value value of bit-field to insert * @param lsb least significant bit of the value to be inserted * @param width width of the bit-field to be inserted * @return bit-field's value */ #define JRT_SET_BIT_FIELD_VALUE(type, container, new_bit_field_value, lsb, width) \ (((container) & ~(((((type) 1) << (width)) - 1) << (lsb))) | (((type) new_bit_field_value) << (lsb))) #endif /* !JRT_BIT_FIELDS_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgXmlParser.h000664 001750 001750 00000005340 15164251010 035045 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_XML_PARSER_H_ #define _TVG_XML_PARSER_H_ #include "tvgSvgLoaderCommon.h" #define NUMBER_OF_XML_ENTITIES 9 const char* const xmlEntity[] = {" ", """, " ", "'", "&", "<", ">", "#", "'"}; const int xmlEntityLength[] = {5, 6, 6, 6, 5, 4, 4, 6, 6}; enum class XMLType { Open = 0, //!< \ OpenEmpty, //!< \ Close, //!< \ Data, //!< tag text data CData, //!< \ Error, //!< error contents Processing, //!< \ \ Doctype, //!< \ Ignored, //!< whatever is ignored by parser, like whitespace DoctypeChild //!< \ #include "./dsp.h" // Tables can be faster on some platform but incur some extra binary size (~2k). // #define USE_TABLES_FOR_ALPHA_MULT // ----------------------------------------------------------------------------- #define MFIX 24 // 24bit fixed-point arithmetic #define HALF ((1u << MFIX) >> 1) #define KINV_255 ((1u << MFIX) / 255u) static uint32_t Mult(uint8_t x, uint32_t mult) { const uint32_t v = (x * mult + HALF) >> MFIX; assert(v <= 255); // <- 24bit precision is enough to ensure that. return v; } #ifdef USE_TABLES_FOR_ALPHA_MULT static const uint32_t kMultTables[2][256] = { { // (255u << MFIX) / alpha 0x00000000, 0xff000000, 0x7f800000, 0x55000000, 0x3fc00000, 0x33000000, 0x2a800000, 0x246db6db, 0x1fe00000, 0x1c555555, 0x19800000, 0x172e8ba2, 0x15400000, 0x139d89d8, 0x1236db6d, 0x11000000, 0x0ff00000, 0x0f000000, 0x0e2aaaaa, 0x0d6bca1a, 0x0cc00000, 0x0c249249, 0x0b9745d1, 0x0b1642c8, 0x0aa00000, 0x0a333333, 0x09cec4ec, 0x0971c71c, 0x091b6db6, 0x08cb08d3, 0x08800000, 0x0839ce73, 0x07f80000, 0x07ba2e8b, 0x07800000, 0x07492492, 0x07155555, 0x06e45306, 0x06b5e50d, 0x0689d89d, 0x06600000, 0x063831f3, 0x06124924, 0x05ee23b8, 0x05cba2e8, 0x05aaaaaa, 0x058b2164, 0x056cefa8, 0x05500000, 0x05343eb1, 0x05199999, 0x05000000, 0x04e76276, 0x04cfb2b7, 0x04b8e38e, 0x04a2e8ba, 0x048db6db, 0x0479435e, 0x04658469, 0x045270d0, 0x04400000, 0x042e29f7, 0x041ce739, 0x040c30c3, 0x03fc0000, 0x03ec4ec4, 0x03dd1745, 0x03ce540f, 0x03c00000, 0x03b21642, 0x03a49249, 0x03976fc6, 0x038aaaaa, 0x037e3f1f, 0x03722983, 0x03666666, 0x035af286, 0x034fcace, 0x0344ec4e, 0x033a5440, 0x03300000, 0x0325ed09, 0x031c18f9, 0x0312818a, 0x03092492, 0x03000000, 0x02f711dc, 0x02ee5846, 0x02e5d174, 0x02dd7baf, 0x02d55555, 0x02cd5cd5, 0x02c590b2, 0x02bdef7b, 0x02b677d4, 0x02af286b, 0x02a80000, 0x02a0fd5c, 0x029a1f58, 0x029364d9, 0x028ccccc, 0x0286562d, 0x02800000, 0x0279c952, 0x0273b13b, 0x026db6db, 0x0267d95b, 0x026217ec, 0x025c71c7, 0x0256e62a, 0x0251745d, 0x024c1bac, 0x0246db6d, 0x0241b2f9, 0x023ca1af, 0x0237a6f4, 0x0232c234, 0x022df2df, 0x02293868, 0x02249249, 0x02200000, 0x021b810e, 0x021714fb, 0x0212bb51, 0x020e739c, 0x020a3d70, 0x02061861, 0x02020408, 0x01fe0000, 0x01fa0be8, 0x01f62762, 0x01f25213, 0x01ee8ba2, 0x01ead3ba, 0x01e72a07, 0x01e38e38, 0x01e00000, 0x01dc7f10, 0x01d90b21, 0x01d5a3e9, 0x01d24924, 0x01cefa8d, 0x01cbb7e3, 0x01c880e5, 0x01c55555, 0x01c234f7, 0x01bf1f8f, 0x01bc14e5, 0x01b914c1, 0x01b61eed, 0x01b33333, 0x01b05160, 0x01ad7943, 0x01aaaaaa, 0x01a7e567, 0x01a5294a, 0x01a27627, 0x019fcbd2, 0x019d2a20, 0x019a90e7, 0x01980000, 0x01957741, 0x0192f684, 0x01907da4, 0x018e0c7c, 0x018ba2e8, 0x018940c5, 0x0186e5f0, 0x01849249, 0x018245ae, 0x01800000, 0x017dc11f, 0x017b88ee, 0x0179574e, 0x01772c23, 0x01750750, 0x0172e8ba, 0x0170d045, 0x016ebdd7, 0x016cb157, 0x016aaaaa, 0x0168a9b9, 0x0166ae6a, 0x0164b8a7, 0x0162c859, 0x0160dd67, 0x015ef7bd, 0x015d1745, 0x015b3bea, 0x01596596, 0x01579435, 0x0155c7b4, 0x01540000, 0x01523d03, 0x01507eae, 0x014ec4ec, 0x014d0fac, 0x014b5edc, 0x0149b26c, 0x01480a4a, 0x01466666, 0x0144c6af, 0x01432b16, 0x0141938b, 0x01400000, 0x013e7063, 0x013ce4a9, 0x013b5cc0, 0x0139d89d, 0x01385830, 0x0136db6d, 0x01356246, 0x0133ecad, 0x01327a97, 0x01310bf6, 0x012fa0be, 0x012e38e3, 0x012cd459, 0x012b7315, 0x012a150a, 0x0128ba2e, 0x01276276, 0x01260dd6, 0x0124bc44, 0x01236db6, 0x01222222, 0x0120d97c, 0x011f93bc, 0x011e50d7, 0x011d10c4, 0x011bd37a, 0x011a98ef, 0x0119611a, 0x01182bf2, 0x0116f96f, 0x0115c988, 0x01149c34, 0x0113716a, 0x01124924, 0x01112358, 0x01100000, 0x010edf12, 0x010dc087, 0x010ca458, 0x010b8a7d, 0x010a72f0, 0x01095da8, 0x01084a9f, 0x010739ce, 0x01062b2e, 0x01051eb8, 0x01041465, 0x01030c30, 0x01020612, 0x01010204, 0x01000000 }, { // alpha * KINV_255 0x00000000, 0x00010101, 0x00020202, 0x00030303, 0x00040404, 0x00050505, 0x00060606, 0x00070707, 0x00080808, 0x00090909, 0x000a0a0a, 0x000b0b0b, 0x000c0c0c, 0x000d0d0d, 0x000e0e0e, 0x000f0f0f, 0x00101010, 0x00111111, 0x00121212, 0x00131313, 0x00141414, 0x00151515, 0x00161616, 0x00171717, 0x00181818, 0x00191919, 0x001a1a1a, 0x001b1b1b, 0x001c1c1c, 0x001d1d1d, 0x001e1e1e, 0x001f1f1f, 0x00202020, 0x00212121, 0x00222222, 0x00232323, 0x00242424, 0x00252525, 0x00262626, 0x00272727, 0x00282828, 0x00292929, 0x002a2a2a, 0x002b2b2b, 0x002c2c2c, 0x002d2d2d, 0x002e2e2e, 0x002f2f2f, 0x00303030, 0x00313131, 0x00323232, 0x00333333, 0x00343434, 0x00353535, 0x00363636, 0x00373737, 0x00383838, 0x00393939, 0x003a3a3a, 0x003b3b3b, 0x003c3c3c, 0x003d3d3d, 0x003e3e3e, 0x003f3f3f, 0x00404040, 0x00414141, 0x00424242, 0x00434343, 0x00444444, 0x00454545, 0x00464646, 0x00474747, 0x00484848, 0x00494949, 0x004a4a4a, 0x004b4b4b, 0x004c4c4c, 0x004d4d4d, 0x004e4e4e, 0x004f4f4f, 0x00505050, 0x00515151, 0x00525252, 0x00535353, 0x00545454, 0x00555555, 0x00565656, 0x00575757, 0x00585858, 0x00595959, 0x005a5a5a, 0x005b5b5b, 0x005c5c5c, 0x005d5d5d, 0x005e5e5e, 0x005f5f5f, 0x00606060, 0x00616161, 0x00626262, 0x00636363, 0x00646464, 0x00656565, 0x00666666, 0x00676767, 0x00686868, 0x00696969, 0x006a6a6a, 0x006b6b6b, 0x006c6c6c, 0x006d6d6d, 0x006e6e6e, 0x006f6f6f, 0x00707070, 0x00717171, 0x00727272, 0x00737373, 0x00747474, 0x00757575, 0x00767676, 0x00777777, 0x00787878, 0x00797979, 0x007a7a7a, 0x007b7b7b, 0x007c7c7c, 0x007d7d7d, 0x007e7e7e, 0x007f7f7f, 0x00808080, 0x00818181, 0x00828282, 0x00838383, 0x00848484, 0x00858585, 0x00868686, 0x00878787, 0x00888888, 0x00898989, 0x008a8a8a, 0x008b8b8b, 0x008c8c8c, 0x008d8d8d, 0x008e8e8e, 0x008f8f8f, 0x00909090, 0x00919191, 0x00929292, 0x00939393, 0x00949494, 0x00959595, 0x00969696, 0x00979797, 0x00989898, 0x00999999, 0x009a9a9a, 0x009b9b9b, 0x009c9c9c, 0x009d9d9d, 0x009e9e9e, 0x009f9f9f, 0x00a0a0a0, 0x00a1a1a1, 0x00a2a2a2, 0x00a3a3a3, 0x00a4a4a4, 0x00a5a5a5, 0x00a6a6a6, 0x00a7a7a7, 0x00a8a8a8, 0x00a9a9a9, 0x00aaaaaa, 0x00ababab, 0x00acacac, 0x00adadad, 0x00aeaeae, 0x00afafaf, 0x00b0b0b0, 0x00b1b1b1, 0x00b2b2b2, 0x00b3b3b3, 0x00b4b4b4, 0x00b5b5b5, 0x00b6b6b6, 0x00b7b7b7, 0x00b8b8b8, 0x00b9b9b9, 0x00bababa, 0x00bbbbbb, 0x00bcbcbc, 0x00bdbdbd, 0x00bebebe, 0x00bfbfbf, 0x00c0c0c0, 0x00c1c1c1, 0x00c2c2c2, 0x00c3c3c3, 0x00c4c4c4, 0x00c5c5c5, 0x00c6c6c6, 0x00c7c7c7, 0x00c8c8c8, 0x00c9c9c9, 0x00cacaca, 0x00cbcbcb, 0x00cccccc, 0x00cdcdcd, 0x00cecece, 0x00cfcfcf, 0x00d0d0d0, 0x00d1d1d1, 0x00d2d2d2, 0x00d3d3d3, 0x00d4d4d4, 0x00d5d5d5, 0x00d6d6d6, 0x00d7d7d7, 0x00d8d8d8, 0x00d9d9d9, 0x00dadada, 0x00dbdbdb, 0x00dcdcdc, 0x00dddddd, 0x00dedede, 0x00dfdfdf, 0x00e0e0e0, 0x00e1e1e1, 0x00e2e2e2, 0x00e3e3e3, 0x00e4e4e4, 0x00e5e5e5, 0x00e6e6e6, 0x00e7e7e7, 0x00e8e8e8, 0x00e9e9e9, 0x00eaeaea, 0x00ebebeb, 0x00ececec, 0x00ededed, 0x00eeeeee, 0x00efefef, 0x00f0f0f0, 0x00f1f1f1, 0x00f2f2f2, 0x00f3f3f3, 0x00f4f4f4, 0x00f5f5f5, 0x00f6f6f6, 0x00f7f7f7, 0x00f8f8f8, 0x00f9f9f9, 0x00fafafa, 0x00fbfbfb, 0x00fcfcfc, 0x00fdfdfd, 0x00fefefe, 0x00ffffff } }; static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) { return kMultTables[!inverse][a]; } #else static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) { return inverse ? (255u << MFIX) / a : a * KINV_255; } #endif // USE_TABLES_FOR_ALPHA_MULT void WebPMultARGBRowC(uint32_t* const ptr, int width, int inverse) { int x; for (x = 0; x < width; ++x) { const uint32_t argb = ptr[x]; if (argb < 0xff000000u) { // alpha < 255 if (argb <= 0x00ffffffu) { // alpha == 0 ptr[x] = 0; } else { const uint32_t alpha = (argb >> 24) & 0xff; const uint32_t scale = GetScale(alpha, inverse); uint32_t out = argb & 0xff000000u; out |= Mult(argb >> 0, scale) << 0; out |= Mult(argb >> 8, scale) << 8; out |= Mult(argb >> 16, scale) << 16; ptr[x] = out; } } } } void WebPMultRowC(uint8_t* const ptr, const uint8_t* const alpha, int width, int inverse) { int x; for (x = 0; x < width; ++x) { const uint32_t a = alpha[x]; if (a != 255) { if (a == 0) { ptr[x] = 0; } else { const uint32_t scale = GetScale(a, inverse); ptr[x] = Mult(ptr[x], scale); } } } } #undef KINV_255 #undef HALF #undef MFIX void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse); void (*WebPMultRow)(uint8_t* const ptr, const uint8_t* const alpha, int width, int inverse); //------------------------------------------------------------------------------ // Generic per-plane calls void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows, int inverse) { int n; for (n = 0; n < num_rows; ++n) { WebPMultARGBRow((uint32_t*)ptr, width, inverse); ptr += stride; } } void WebPMultRows(uint8_t* ptr, int stride, const uint8_t* alpha, int alpha_stride, int width, int num_rows, int inverse) { int n; for (n = 0; n < num_rows; ++n) { WebPMultRow(ptr, alpha, width, inverse); ptr += stride; alpha += alpha_stride; } } //------------------------------------------------------------------------------ // Premultiplied modes // non dithered-modes // (x * a * 32897) >> 23 is bit-wise equivalent to (int)(x * a / 255.) // for all 8bit x or a. For bit-wise equivalence to (int)(x * a / 255. + .5), // one can use instead: (x * a * 65793 + (1 << 23)) >> 24 #if 1 // (int)(x * a / 255.) #define MULTIPLIER(a) ((a) * 32897U) #define PREMULTIPLY(x, m) (((x) * (m)) >> 23) #else // (int)(x * a / 255. + .5) #define MULTIPLIER(a) ((a) * 65793U) #define PREMULTIPLY(x, m) (((x) * (m) + (1U << 23)) >> 24) #endif static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first, int w, int h, int stride) { while (h-- > 0) { uint8_t* const rgb = rgba + (alpha_first ? 1 : 0); const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3); int i; for (i = 0; i < w; ++i) { const uint32_t a = alpha[4 * i]; if (a != 0xff) { const uint32_t mult = MULTIPLIER(a); rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult); rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult); rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult); } } rgba += stride; } } #undef MULTIPLIER #undef PREMULTIPLY // rgbA4444 #define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15 static WEBP_INLINE uint8_t dither_hi(uint8_t x) { return (x & 0xf0) | (x >> 4); } static WEBP_INLINE uint8_t dither_lo(uint8_t x) { return (x & 0x0f) | (x << 4); } static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) { return (x * m) >> 16; } static WEBP_INLINE void ApplyAlphaMultiply4444(uint8_t* rgba4444, int w, int h, int stride, int rg_byte_pos /* 0 or 1 */) { while (h-- > 0) { int i; for (i = 0; i < w; ++i) { const uint32_t rg = rgba4444[2 * i + rg_byte_pos]; const uint32_t ba = rgba4444[2 * i + (rg_byte_pos ^ 1)]; const uint8_t a = ba & 0x0f; const uint32_t mult = MULTIPLIER(a); const uint8_t r = multiply(dither_hi(rg), mult); const uint8_t g = multiply(dither_lo(rg), mult); const uint8_t b = multiply(dither_hi(ba), mult); rgba4444[2 * i + rg_byte_pos] = (r & 0xf0) | ((g >> 4) & 0x0f); rgba4444[2 * i + (rg_byte_pos ^ 1)] = (b & 0xf0) | a; } rgba4444 += stride; } } #undef MULTIPLIER static void ApplyAlphaMultiply_16b(uint8_t* rgba4444, int w, int h, int stride) { #ifdef WEBP_SWAP_16BIT_CSP ApplyAlphaMultiply4444(rgba4444, w, h, stride, 1); #else ApplyAlphaMultiply4444(rgba4444, w, h, stride, 0); #endif } static int DispatchAlpha(const uint8_t* alpha, int alpha_stride, int width, int height, uint8_t* dst, int dst_stride) { uint32_t alpha_mask = 0xff; int i, j; for (j = 0; j < height; ++j) { for (i = 0; i < width; ++i) { const uint32_t alpha_value = alpha[i]; dst[4 * i] = alpha_value; alpha_mask &= alpha_value; } alpha += alpha_stride; dst += dst_stride; } return (alpha_mask != 0xff); } static void DispatchAlphaToGreen(const uint8_t* alpha, int alpha_stride, int width, int height, uint32_t* dst, int dst_stride) { int i, j; for (j = 0; j < height; ++j) { for (i = 0; i < width; ++i) { dst[i] = alpha[i] << 8; // leave A/R/B channels zero'd. } alpha += alpha_stride; dst += dst_stride; } } static int ExtractAlpha(const uint8_t* argb, int argb_stride, int width, int height, uint8_t* alpha, int alpha_stride) { uint8_t alpha_mask = 0xff; int i, j; for (j = 0; j < height; ++j) { for (i = 0; i < width; ++i) { const uint8_t alpha_value = argb[4 * i]; alpha[i] = alpha_value; alpha_mask &= alpha_value; } argb += argb_stride; alpha += alpha_stride; } return (alpha_mask == 0xff); } void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int); void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int); int (*WebPDispatchAlpha)(const uint8_t*, int, int, int, uint8_t*, int); void (*WebPDispatchAlphaToGreen)(const uint8_t*, int, int, int, uint32_t*, int); int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int); //------------------------------------------------------------------------------ // Init function extern void WebPInitAlphaProcessingMIPSdspR2(void); extern void WebPInitAlphaProcessingSSE2(void); static volatile VP8CPUInfo alpha_processing_last_cpuinfo_used = (VP8CPUInfo)&alpha_processing_last_cpuinfo_used; WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessing(void) { if (alpha_processing_last_cpuinfo_used == VP8GetCPUInfo) return; WebPMultARGBRow = WebPMultARGBRowC; WebPMultRow = WebPMultRowC; WebPApplyAlphaMultiply = ApplyAlphaMultiply; WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply_16b; WebPDispatchAlpha = DispatchAlpha; WebPDispatchAlphaToGreen = DispatchAlphaToGreen; WebPExtractAlpha = ExtractAlpha; // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) if (VP8GetCPUInfo(kSSE2)) { WebPInitAlphaProcessingSSE2(); } #endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { WebPInitAlphaProcessingMIPSdspR2(); } #endif } alpha_processing_last_cpuinfo_used = VP8GetCPUInfo; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/ttf/tvgTtfReader.cpp000664 001750 001750 00000050372 15164251010 035346 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgTtfReader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static inline uint32_t _u32(uint8_t* data, uint32_t offset) { auto base = data + offset; return (base[0] << 24 | base[1] << 16 | base[2] << 8 | base[3]); } static inline uint16_t _u16(uint8_t* data, uint32_t offset) { auto base = data + offset; return (base[0] << 8 | base[1]); } static inline int16_t _i16(uint8_t* data, uint32_t offset) { return (int16_t) _u16(data, offset); } static inline uint8_t _u8(uint8_t* data, uint32_t offset) { return *(data + offset); } static inline int8_t _i8(uint8_t* data, uint32_t offset) { return (int8_t) _u8(data, offset); } static int _cmpu32(const void *a, const void *b) { return memcmp(a, b, 4); } bool TtfReader::validate(uint32_t offset, uint32_t margin) const { if ((offset > size) || (size - offset < margin)) { TVGERR("TTF", "Invalidate data"); return false; } return true; } uint32_t TtfReader::table(const char* tag) { auto tableCnt = _u16(data, 4); if (!validate(12, (uint32_t) tableCnt * 16)) return 0; auto match = bsearch(tag, data + 12, tableCnt, 16, _cmpu32); if (!match) { TVGLOG("TTF", "No searching table = %s", tag); return 0; } return _u32(data, (uint32_t) ((uint8_t*) match - data + 8)); } uint32_t TtfReader::cmap_12_13(uint32_t table, uint32_t codepoint, int fmt) const { //A minimal header is 16 bytes auto len = _u32(data, table + 4); if (len < 16) return -1; if (!validate(table, len)) return -1; auto entryCnt = _u32(data, table + 12); for (uint32_t i = 0; i < entryCnt; ++i) { auto firstCode = _u32(data, table + (i * 12) + 16); auto lastCode = _u32(data, table + (i * 12) + 16 + 4); if (codepoint < firstCode || codepoint > lastCode) continue; auto glyphOffset = _u32(data, table + (i * 12) + 16 + 8); if (fmt == 12) return (codepoint - firstCode) + glyphOffset; else return glyphOffset; } return -1; } uint32_t TtfReader::cmap_4(uint32_t table, uint32_t codepoint) const { //cmap format 4 only supports the Unicode BMP. if (codepoint > 0xffff) return -1; auto shortCode = static_cast(codepoint); if (!validate(table, 8)) return -1; auto segmentCnt = _u16(data, table); if ((segmentCnt & 1) || segmentCnt == 0) return -1; //find starting positions of the relevant arrays. auto endCodes = table + 8; auto startCodes = endCodes + segmentCnt + 2; auto idDeltas = startCodes + segmentCnt; auto idRangeOffsets = idDeltas + segmentCnt; if (!validate(idRangeOffsets, segmentCnt)) return -1; //find the segment that contains shortCode by binary searching over the highest codes in the segments. //binary search, but returns the next highest element if key could not be found. uint8_t* segment = nullptr; auto n = segmentCnt / 2; if (n > 0) { uint8_t key[2] = {(uint8_t)(codepoint >> 8), (uint8_t)codepoint}; auto bytes = data + endCodes; auto low = 0; auto high = n - 1; while (low != high) { auto mid = low + (high - low) / 2; auto sample = bytes + mid * 2; if (memcmp(key, sample, 2) > 0) { low = mid + 1; } else { high = mid; } } segment = (bytes + low * 2); } auto segmentIdx = static_cast(segment - (data + endCodes)); //look up segment info from the arrays & short circuit if the spec requires. auto startCode = _u16(data, startCodes + segmentIdx); if (startCode > shortCode) return 0; auto delta = _u16(data, idDeltas + segmentIdx); auto idRangeOffset = _u16(data, idRangeOffsets + segmentIdx); //intentional integer under- and overflow. if (idRangeOffset == 0) return (shortCode + delta) & 0xffff; //calculate offset into glyph array and determine ultimate value. auto offset = idRangeOffsets + segmentIdx + idRangeOffset + 2U * (unsigned int)(shortCode - startCode); if (!validate(offset, 2)) return -1; auto id = _u16(data, offset); //intentional integer under- and overflow. if (id > 0) return (id + delta) & 0xffff; return 0; } uint32_t TtfReader::cmap_6(uint32_t table, uint32_t codepoint) const { //cmap format 6 only supports the Unicode BMP. if (codepoint > 0xFFFF) return 0; auto firstCode = _u16(data, table); auto entryCnt = _u16(data, table + 2); if (!validate(table, 4 + 2 * entryCnt)) return -1; if (codepoint < firstCode) return -1; codepoint -= firstCode; if (codepoint >= entryCnt) return -1; return _u16(data, table + 4 + 2 * codepoint); } //Returns the offset into the font that the glyph's outline is stored at uint32_t TtfReader::outlineOffset(uint32_t glyph) { uint32_t cur, next; auto loca = this->loca.load(); if (loca == 0) this->loca = loca = table("loca"); auto glyf = this->glyf.load(); if (glyf == 0) this->glyf = glyf = table("glyf"); if (metrics.locaFormat == 0) { auto base = loca + 2 * glyph; if (!validate(base, 4)) { TVGERR("TTF", "invalid outline offset"); return 0; } cur = 2U * (uint32_t) _u16(data, base); next = 2U * (uint32_t) _u16(data, base + 2); } else { auto base = loca + 4 * glyph; if (!validate(base, 8)) return 0; cur = _u32(data, base); next = _u32(data, base + 4); } if (cur == next) return 0; return glyf + cur; } bool TtfReader::points(uint32_t outline, uint8_t* flags, Point* pts, uint32_t ptsCnt, const Point& offset) { #define X_CHANGE_IS_SMALL 0x02 #define X_CHANGE_IS_POSITIVE 0x10 #define X_CHANGE_IS_ZERO 0x10 #define Y_CHANGE_IS_SMALL 0x04 #define Y_CHANGE_IS_ZERO 0x20 #define Y_CHANGE_IS_POSITIVE 0x20 long accum = 0L; for (uint32_t i = 0; i < ptsCnt; ++i) { if (flags[i] & X_CHANGE_IS_SMALL) { if (!validate(outline, 1)) { return false; } auto value = (long) _u8(data, outline++); auto bit = (uint8_t)!!(flags[i] & X_CHANGE_IS_POSITIVE); accum -= (value ^ -bit) + bit; } else if (!(flags[i] & X_CHANGE_IS_ZERO)) { if (!validate(outline, 2)) return false; accum += _i16(data, outline); outline += 2; } pts[i].x = offset.x + (float) accum; } accum = 0L; for (uint32_t i = 0; i < ptsCnt; ++i) { if (flags[i] & Y_CHANGE_IS_SMALL) { if (!validate(outline, 1)) return false; auto value = (long) _u8(data, outline++); auto bit = (uint8_t)!!(flags[i] & Y_CHANGE_IS_POSITIVE); accum -= (value ^ -bit) + bit; } else if (!(flags[i] & Y_CHANGE_IS_ZERO)) { if (!validate(outline, 2)) return false; accum += _i16(data, outline); outline += 2; } pts[i].y = offset.y - (float) accum; } return true; } bool TtfReader::flags(uint32_t *outline, uint8_t* flags, uint32_t flagsCnt) { #define REPEAT_FLAG 0x08 auto offset = *outline; uint8_t value = 0; uint8_t repeat = 0; for (uint32_t i = 0; i < flagsCnt; ++i) { if (repeat) { --repeat; } else { if (!validate(offset, 1)) return false; value = _u8(data, offset++); if (value & REPEAT_FLAG) { if (!validate(offset, 1)) return false; repeat = _u8(data, offset++); } } flags[i] = value; } *outline = offset; return true; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool TtfReader::header() { if (!validate(0, 12)) return false; //verify ttf(scalable font) auto type = _u32(data, 0); if (type != 0x00010000 && type != 0x74727565) return false; //header auto head = table("head"); if (!validate(head, 54)) return false; metrics.unitsPerEm = _u16(data, head + 18); metrics.locaFormat = _u16(data, head + 50); //horizontal metrics auto hhea = table("hhea"); if (!validate(hhea, 36)) return false; metrics.hhea.ascent = _i16(data, hhea + 4); metrics.hhea.descent = _i16(data, hhea + 6); metrics.hhea.linegap = _i16(data, hhea + 8); metrics.hhea.advance = metrics.hhea.ascent - metrics.hhea.descent + metrics.hhea.linegap; metrics.numHmtx = _u16(data, hhea + 34); //kerning auto kern = this->kern = table("kern"); if (kern) { if (!validate(kern, 4)) return false; if (_u16(data, kern) != 0) return false; } return true; } uint32_t TtfReader::glyph(uint32_t codepoint) { auto cmap = this->cmap.load(); if (cmap == 0) { this->cmap = cmap = table("cmap"); if (!validate(cmap, 4)) return -1; } auto entryCnt = _u16(data, cmap + 2); if (!validate(cmap, 4 + entryCnt * 8)) return -1; //full repertory (non-BMP map). for (auto idx = 0; idx < entryCnt; ++idx) { auto entry = cmap + 4 + idx * 8; auto type = _u16(data, entry) * 0100 + _u16(data, entry + 2); //unicode map if (type == 0004 || type == 0312) { auto table = cmap + _u32(data, entry + 4); if (!validate(table, 8)) return -1; //dispatch based on cmap format. auto format = _u16(data, table); switch (format) { case 12: return cmap_12_13(table, codepoint, 12); default: return -1; } } } //Try looking for a BMP map. for (auto idx = 0; idx < entryCnt; ++idx) { auto entry = cmap + 4 + idx * 8; auto type = _u16(data, entry) * 0100 + _u16(data, entry + 2); //Unicode BMP if (type == 0003 || type == 0301) { auto table = cmap + _u32(data, entry + 4); if (!validate(table, 6)) return -1; //Dispatch based on cmap format. switch (_u16(data, table)) { case 4: return cmap_4(table + 6, codepoint); case 6: return cmap_6(table + 6, codepoint); default: return -1; } } } return -1; } uint32_t TtfReader::glyph(uint32_t codepoint, TtfGlyphMetrics* tgm) { tgm->idx = glyph(codepoint); if (tgm->idx == INVALID_GLYPH) return 0; return glyphMetrics(*tgm); } uint32_t TtfReader::glyphMetrics(TtfGlyph& glyph) { //horizontal metrics auto hmtx = this->hmtx.load(); if (hmtx == 0) this->hmtx = hmtx = table("hmtx"); //glyph is inside long metrics segment. if (glyph.idx < metrics.numHmtx) { auto offset = hmtx + 4 * glyph.idx; if (!validate(offset, 4)) return 0; glyph.advance = _u16(data, offset); glyph.lsb = _i16(data, offset + 2); /* glyph is inside short metrics segment. */ } else { auto boundary = hmtx + 4U * (uint32_t) metrics.numHmtx; if (boundary < 4) return 0; auto offset = boundary - 4; if (!validate(offset, 4)) return 0; glyph.advance = _u16(data, offset); offset = boundary + 2 * (glyph.idx - metrics.numHmtx); if (!validate(offset, 2)) return 0; glyph.lsb = _i16(data, offset); } auto glyphOffset = outlineOffset(glyph.idx); // glyph without outline if (glyphOffset == 0) { glyph.y = glyph.w = glyph.h = 0.0f; return 0; } if (!validate(glyphOffset, 10)) return 0; //read the bounding box from the font file verbatim. float bbox[4]; bbox[0] = static_cast(_i16(data, glyphOffset + 2)); bbox[1] = static_cast(_i16(data, glyphOffset + 4)); bbox[2] = static_cast(_i16(data, glyphOffset + 6)); bbox[3] = static_cast(_i16(data, glyphOffset + 8)); glyph.w = bbox[2] - bbox[0] + 1; glyph.h = bbox[3] - bbox[1] + 1; glyph.y = bbox[3]; return glyphOffset; } bool TtfReader::convert(RenderPath& path, TtfGlyph& glyph, uint32_t glyphOffset, const Point& offset, uint16_t depth) { #define ON_CURVE 0x01 if (glyphOffset == 0) return true; auto outlineCnt = _i16(data, glyphOffset); if (outlineCnt == 0) return false; if (outlineCnt < 0) { uint16_t maxComponentDepth = 1U; auto maxp = this->maxp.load(); if (maxp == 0) this->maxp = maxp = table("maxp"); if (validate(maxp, 32) && _u32(data, maxp) >= 0x00010000U) { // >= version 1.0 maxComponentDepth = _u16(data, maxp + 30); } if (depth > maxComponentDepth) return false; return convertComposite(path, glyph, glyphOffset, offset, depth + 1); } auto cntrsCnt = (uint32_t) outlineCnt; auto outline = glyphOffset + 10; if (!validate(outline, cntrsCnt * 2 + 2)) return false; auto ret = false; auto ptsCnt = _u16(data, outline + (cntrsCnt - 1) * 2) + 1; auto endPts = tvg::malloc(cntrsCnt * sizeof(size_t)); //the index of the contour points. for (uint32_t i = 0; i < cntrsCnt; ++i) { endPts[i] = (uint32_t) _u16(data, outline); outline += 2; } outline += 2U + _u16(data, outline); auto flags = tvg::malloc(ptsCnt * sizeof(uint8_t)); if (this->flags(&outline, flags, ptsCnt)) { auto pts = tvg::malloc(ptsCnt * sizeof(Point)); if (this->points(outline, flags, pts, ptsCnt, offset)) { //generate tvg paths path.cmds.reserve(ptsCnt); path.pts.reserve(ptsCnt); uint32_t begin = 0; for (uint32_t i = 0; i < cntrsCnt; ++i) { //contour must start with move to auto offCurve = !(flags[begin] & ON_CURVE); auto ptsBegin = offCurve ? (pts[begin] + pts[endPts[i]]) * 0.5f : pts[begin]; path.moveTo(ptsBegin); auto cnt = endPts[i] - begin + 1; for (uint32_t x = 1; x < cnt; ++x) { if (flags[begin + x] & ON_CURVE) { if (offCurve) { path.cubicTo(path.pts.last() + (2.0f/3.0f) * (pts[begin + x - 1] - path.pts.last()), pts[begin + x] + (2.0f/3.0f) * (pts[begin + x - 1] - pts[begin + x]), pts[begin + x]); offCurve = false; } else { path.lineTo(pts[begin + x]); } } else { if (offCurve) { auto end = (pts[begin + x] + pts[begin + x - 1]) * 0.5f; path.cubicTo(path.pts.last() + (2.0f/3.0f) * (pts[begin + x - 1] - path.pts.last()), end + (2.0f/3.0f) * (pts[begin + x - 1] - end), end); } else { offCurve = true; } } } if (offCurve) { path.cubicTo(path.pts.last() + (2.0f/3.0f) * (pts[begin + cnt - 1] - path.pts.last()), ptsBegin + (2.0f/3.0f) * (pts[begin + cnt - 1] - ptsBegin), ptsBegin); } //contour must end with close path.close(); begin = endPts[i] + 1; } ret = true; } tvg::free(pts); } tvg::free(flags); tvg::free(endPts); return ret; } bool TtfReader::convertComposite(RenderPath& path, TtfGlyph& glyph, uint32_t glyphOffset, const Point& offset, uint16_t depth) { #define ARG_1_AND_2_ARE_WORDS 0x0001 #define ARGS_ARE_XY_VALUES 0x0002 #define WE_HAVE_A_SCALE 0x0008 #define MORE_COMPONENTS 0x0020 #define WE_HAVE_AN_X_AND_Y_SCALE 0x0040 #define WE_HAVE_A_TWO_BY_TWO 0x0080 TtfGlyph compGlyph; Point compOffset; uint16_t flags; auto pointer = glyphOffset + 10; do { if (!validate(pointer, 4)) return false; flags = _u16(data, pointer); compGlyph.idx = _u16(data, pointer + 2U); if (compGlyph.idx == INVALID_GLYPH) continue; pointer += 4U; if (flags & ARG_1_AND_2_ARE_WORDS) { if (!validate(pointer, 4)) return false; // TODO: align to parent point compOffset = (flags & ARGS_ARE_XY_VALUES) ? Point{float(_i16(data, pointer)), -float(_i16(data, pointer + 2U))} : Point{0.0f, 0.0f}; pointer += 4U; } else { if (!validate(pointer, 2)) return false; // TODO: align to parent point compOffset = (flags & ARGS_ARE_XY_VALUES) ? Point{float(_i8(data, pointer)), -float(_i8(data, pointer + 1U))} : Point{0.0f, 0.0f}; pointer += 2U; } if (flags & WE_HAVE_A_SCALE) { if (!validate(pointer, 2)) return false; // TODO transformation // F2DOT14 scale; /* Format 2.14 */ pointer += 2U; } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { if (!validate(pointer, 4)) return false; // TODO transformation // F2DOT14 xscale; /* Format 2.14 */ // F2DOT14 yscale; /* Format 2.14 */ pointer += 4U; } else if (flags & WE_HAVE_A_TWO_BY_TWO) { if (!validate(pointer, 8)) return false; // TODO transformation // F2DOT14 xscale; /* Format 2.14 */ // F2DOT14 scale01; /* Format 2.14 */ // F2DOT14 scale10; /* Format 2.14 */ // F2DOT14 yscale; /* Format 2.14 */ pointer += 8U; } if (!convert(path, compGlyph, glyphMetrics(compGlyph), offset + compOffset, depth)) return false; } while (flags & MORE_COMPONENTS); return true; } bool TtfReader::kerning(uint32_t lglyph, uint32_t rglyph, Point& out) { #define HORIZONTAL_KERNING 0x01 #define MINIMUM_KERNING 0x02 #define CROSS_STREAM_KERNING 0x04 if (!kern) return false; auto kern = this->kern.load(); //kern tables auto tableCnt = _u16(data, kern + 2); kern += 4; while (tableCnt > 0) { //read subtable header. if (!validate(kern, 6)) return false; auto length = _u16(data, kern + 2); auto format = _u8(data, kern + 4); auto flags = _u8(data, kern + 5); kern += 6; if (format == 0 && (flags & HORIZONTAL_KERNING) && !(flags & MINIMUM_KERNING)) { //read format 0 header. if (!validate(kern, 8)) return false; auto pairCnt = _u16(data, kern); kern += 8; //look up character code pair via binary search. uint8_t key[4]; key[0] = (lglyph >> 8) & 0xff; key[1] = lglyph & 0xff; key[2] = (rglyph >> 8) & 0xff; key[3] = rglyph & 0xff; auto match = bsearch(key, data + kern, pairCnt, 6, _cmpu32); if (match) { auto value = _i16(data, (uint32_t)((uint8_t *) match - data + 4)); if (flags & CROSS_STREAM_KERNING) out.y += value; else out.x += value; } } kern += length; --tableCnt; } return true; } modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlRenderTarget.cpp000664 001750 001750 00000011400 15164251010 037554 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlRenderTarget.h" GlRenderTarget::GlRenderTarget() {} GlRenderTarget::~GlRenderTarget() { reset(); } void GlRenderTarget::init(uint32_t width, uint32_t height, GLint resolveId) { if (width == 0 || height == 0) return; this->width = width; this->height = height; //TODO: fbo is used. maybe we can consider the direct rendering with resolveId as well. GL_CHECK(glGenFramebuffers(1, &fbo)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, fbo)); GL_CHECK(glGenRenderbuffers(1, &colorBuffer)); GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer)); GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, width, height)); GL_CHECK(glGenRenderbuffers(1, &depthStencilBuffer)); GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer)); GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height)); GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0)); GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer)); GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer)); // resolve target GL_CHECK(glGenTextures(1, &colorTex)); GL_CHECK(glBindTexture(GL_TEXTURE_2D, colorTex)); GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0)); GL_CHECK(glGenFramebuffers(1, &resolvedFbo)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, resolvedFbo)); GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, resolveId)); } void GlRenderTarget::reset() { if (fbo == 0) return; GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); GL_CHECK(glDeleteFramebuffers(1, &fbo)); GL_CHECK(glDeleteRenderbuffers(1, &colorBuffer)); GL_CHECK(glDeleteRenderbuffers(1, &depthStencilBuffer)); GL_CHECK(glDeleteFramebuffers(1, &resolvedFbo)); GL_CHECK(glDeleteTextures(1, &colorTex)); fbo = colorBuffer = depthStencilBuffer = resolvedFbo = colorTex = 0; } GlRenderTargetPool::GlRenderTargetPool(uint32_t maxWidth, uint32_t maxHeight): maxWidth(maxWidth), maxHeight(maxHeight), pool() {} GlRenderTargetPool::~GlRenderTargetPool() { for (uint32_t i = 0; i < pool.count; i++) { delete pool[i]; } } uint32_t alignPow2(uint32_t value) { uint32_t ret = 1; while (ret < value) { ret <<= 1; } return ret; } GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLuint resolveId) { auto width = vp.w(); auto height = vp.h(); // pow2 align width and height if (width >= maxWidth) width = maxWidth; else width = alignPow2(width); if (width >= maxWidth) width = maxWidth; if (height >= maxHeight) height = maxHeight; else height = alignPow2(height); if (height >= maxHeight) height = maxHeight; for (uint32_t i = 0; i < pool.count; i++) { auto rt = pool[i]; if (rt->width == width && rt->height == height) { rt->viewport = vp; return rt; } } auto rt = new GlRenderTarget(); rt->init(width, height, resolveId); rt->viewport = vp; pool.push(rt); return rt; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.git000664 001750 001750 00000000153 15164251007 030006 0ustar00ddennedyddennedy000000 000000 gitdir: ../../../../../../../.git/modules/src/modules/glaxnimate/glaxnimate/modules/external/thorvg/thorvg external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-errors.cpp000664 001750 001750 00000003771 15164251010 044071 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-errors.h" #if JERRY_ERROR_MESSAGES /** * Struct to store ecma error message with its size. */ typedef struct { char *text; /* Text of ecma error message. */ uint8_t size; /* Size of ecma error message. */ } ecma_error_message_t; /* Error message texts with size. */ static ecma_error_message_t ecma_error_messages[] JERRY_ATTR_CONST_DATA = { { "", 0 }, /* ECMA_ERR_EMPTY */ /** @cond doxygen_suppress */ #define ECMA_ERROR_DEF(id, string) { string, sizeof (string) - 1 }, #include "ecma-error-messages.inc.h" #undef ECMA_ERROR_DEF /** @endcond */ }; #endif /* JERRY_ERROR_MESSAGES */ /** * Get specified ecma error as zero-terminated string * * @return pointer to zero-terminated ecma error */ const char * ecma_get_error_msg (ecma_error_msg_t id) /**< ecma error id */ { JERRY_ASSERT (id != ECMA_IS_VALID_CONSTRUCTOR); #if JERRY_ERROR_MESSAGES return ecma_error_messages[id].text; #else /* !JERRY_ERROR_MESSAGES */ return NULL; #endif /* JERRY_ERROR_MESSAGES */ } /* ecma_get_error_msg */ /** * Get size of specified ecma error * * @return size in bytes */ lit_utf8_size_t ecma_get_error_size (ecma_error_msg_t id) /**< ecma error id */ { JERRY_ASSERT (id != ECMA_IS_VALID_CONSTRUCTOR); #if JERRY_ERROR_MESSAGES return ecma_error_messages[id].size; #else /* !JERRY_ERROR_MESSAGES */ return 0; #endif /* JERRY_ERROR_MESSAGES */ } /* ecma_get_error_size */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/000775 001750 001750 00000000000 15164251010 033554 5ustar00ddennedyddennedy000000 000000 lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-template.inc.h000664 001750 001750 00000003301 15164251010 054767 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ #if JERRY_BUILTIN_TYPEDARRAY #ifndef TYPEDARRAY_BYTES_PER_ELEMENT #error "Please define TYPEDARRAY_BYTES_PER_ELEMENT" #endif /* !TYPEDARRAY_BYTES_PER_ELEMENT */ #ifndef TYPEDARRAY_MAGIC_STRING_ID #error "Please define TYPEDARRAY_MAGIC_STRING_ID" #endif /* !TYPEDARRAY_MAGIC_STRING_ID */ #ifndef TYPEDARRAY_BUILTIN_ID #error "Please define TYPEDARRAY_BUILTIN_ID" #endif /* !TYPEDARRAY_BUILTIN_ID */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* ES2015 22.2.5 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 3, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ES2015 22.2.5.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_BYTES_PER_ELEMENT_U, TYPEDARRAY_BYTES_PER_ELEMENT, ECMA_PROPERTY_FIXED) /* ES2015 22.2.5 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, TYPEDARRAY_MAGIC_STRING_ID, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ES2015 22.2.5.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, TYPEDARRAY_BUILTIN_ID, ECMA_PROPERTY_FIXED) #include "ecma-builtin-helpers-macro-undefs.inc.h" #undef TYPEDARRAY_BUILTIN_ID #undef TYPEDARRAY_MAGIC_STRING_ID #undef TYPEDARRAY_BYTES_PER_ELEMENT #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgTaskScheduler.h000664 001750 001750 00000005365 15164251010 035256 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_TASK_SCHEDULER_H_ #define _TVG_TASK_SCHEDULER_H_ #include "tvgCommon.h" #include "tvgInlist.h" #ifdef THORVG_THREAD_SUPPORT #include #include #include #include #endif namespace tvg { #ifdef THORVG_THREAD_SUPPORT using ThreadID = std::thread::id; struct Task { private: mutex mtx; condition_variable cv; bool ready = true; bool pending = false; public: INLIST_ITEM(Task); virtual ~Task() = default; void done() { if (!pending) return; unique_lock lock(mtx); while (!ready) cv.wait(lock); pending = false; } protected: virtual void run(unsigned tid) = 0; private: void operator()(unsigned tid) { run(tid); lock_guard lock(mtx); ready = true; cv.notify_one(); } void prepare() { ready = false; pending = true; } friend struct TaskSchedulerImpl; }; #else //THORVG_THREAD_SUPPORT using ThreadID = uint8_t; struct Task { public: INLIST_ITEM(Task); virtual ~Task() = default; void done() {} protected: virtual void run(unsigned tid) = 0; private: friend struct TaskSchedulerImpl; }; #endif //THORVG_THREAD_SUPPORT struct TaskScheduler { static uint32_t threads(); static void init(uint32_t threads); static void term(); static void request(Task* task); static bool onthread(); //figure out whether on worker thread or not static ThreadID tid(); }; } //namespace #endif //_TVG_TASK_SCHEDULER_H_ lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-helpers.cpp000664 001750 001750 00000005025 15164251010 054406 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-typedarray-helpers.h" #if JERRY_BUILTIN_TYPEDARRAY #include "ecma-builtins.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-objects.h" #include "ecma-typedarray-object.h" #include "jcontext.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * Common implementation of the [[Construct]] call of TypedArrays. * * @return ecma value of the new TypedArray object * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_typedarray_helper_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len, /**< number of arguments */ ecma_typedarray_type_t typedarray_id) /**< id of the typedarray */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_builtin_id_t proto_id = ecma_typedarray_helper_get_prototype_id (typedarray_id); ecma_object_t *prototype_obj_p = NULL; ecma_object_t *current_new_target_p = JERRY_CONTEXT (current_new_target_p); if (current_new_target_p != NULL) { prototype_obj_p = ecma_op_get_prototype_from_constructor (current_new_target_p, proto_id); if (prototype_obj_p == NULL) { return ECMA_VALUE_ERROR; } } else { prototype_obj_p = ecma_builtin_get (proto_id); } ecma_value_t val = ecma_op_create_typedarray (arguments_list_p, arguments_list_len, prototype_obj_p, ecma_typedarray_helper_get_shift_size (typedarray_id), typedarray_id); if (current_new_target_p != NULL) { ecma_deref_object (prototype_obj_p); } return val; } /* ecma_typedarray_helper_dispatch_construct */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakref.inc.h000664 001750 001750 00000002401 15164251010 050232 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * WeakRef built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_WEAKREF /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_WEAKREF_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_WEAKREF_PROTOTYPE, ECMA_PROPERTY_FIXED) #endif /* JERRY_BUILTIN_WEAKREF */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/memcheck_valgrind.sh000775 001750 001750 00000003022 15164251010 036531 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0#!/bin/bash if [[ -z "$GITHUB_TOKEN" ]]; then echo "The GITHUB_TOKEN is required." exit 1 fi if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then echo "Run Valgrind" echo "valgrind --leak-check=yes ./tvgUnitTests" cd ./build/test valgrind --leak-check=yes ./tvgUnitTests > memcheck_valgrind.txt 2>&1 PAYLOAD_MEMCHECK=`cat memcheck_valgrind.txt` COMMENTS_URL=$(cat $GITHUB_EVENT_PATH | jq -r .pull_request.comments_url) echo $COMMENTS_URL echo "MEMCHECK errors:" echo $PAYLOAD_MEMCHECK DEFINITELY_LOST_NUMBER=$(echo "$PAYLOAD_MEMCHECK" | grep -oP 'definitely lost:\s*\K[0-9]+(?=\s*bytes in)') ERROR_NUMBER=$(echo "$PAYLOAD_MEMCHECK" | grep -oP 'ERROR SUMMARY:\s*\K[0-9]+(?=\s*errors)') if [[ $ERROR_NUMBER != 0 || $DEFINITELY_LOST_NUMBER != 0 || $PAYLOAD_MEMCHECK == *"Invalid read "* || $PAYLOAD_MEMCHECK == *"Invalid write "* ]]; then OUTPUT+=$'\n**MEMCHECK(VALGRIND) RESULT**:\n' OUTPUT+=$'\n`valgrind --leak-check=yes ./tvgUnitTests`\n' OUTPUT+=$'\n```\n' OUTPUT+="$PAYLOAD_MEMCHECK" OUTPUT+=$'\n```\n' ( echo '
Valgrind output' echo echo "$OUTPUT" echo echo '
' ) >> "$GITHUB_STEP_SUMMARY" PAYLOAD=$(echo '{}' | jq --arg body "$OUTPUT" '.body = $body') curl -s -S -H "Authorization: token $GITHUB_TOKEN" --header "Content-Type: application/vnd.github.VERSION.text+json" --data "$PAYLOAD" "$COMMENTS_URL" fi fi mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/000775 001750 001750 00000000000 15164251010 033570 5ustar00ddennedyddennedy000000 000000 external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-objects.cpp000664 001750 001750 00000324146 15164251010 045461 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-objects.h" #include "ecma-arguments-object.h" #include "ecma-array-object.h" #include "ecma-bigint.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lcache.h" #include "ecma-lex-env.h" #include "ecma-objects-general.h" #include "ecma-proxy-object.h" #include "ecma-string-object.h" #include "jcontext.h" #if JERRY_BUILTIN_TYPEDARRAY #include "ecma-arraybuffer-object.h" #include "ecma-typedarray-object.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaobjectsinternalops ECMA objects' operations * @{ */ /** * Hash bitmap size for ecma objects */ #define ECMA_OBJECT_HASH_BITMAP_SIZE 256 /** * Assert that specified object type value is valid * * @param type object's implementation-defined type */ #ifndef JERRY_NDEBUG #define JERRY_ASSERT_OBJECT_TYPE_IS_VALID(type) JERRY_ASSERT (type < ECMA_OBJECT_TYPE__MAX); #else /* JERRY_NDEBUG */ #define JERRY_ASSERT_OBJECT_TYPE_IS_VALID(type) #endif /* !JERRY_NDEBUG */ /** * [[GetOwnProperty]] ecma object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * * @return pointer to a property - if it exists, * NULL (i.e. ecma-undefined) - otherwise. */ ecma_property_t ecma_op_object_get_own_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ ecma_property_ref_t *property_ref_p, /**< property reference */ uint32_t options) /**< option bits */ { JERRY_ASSERT (object_p != NULL && !ecma_is_lexical_environment (object_p)); #if JERRY_BUILTIN_PROXY JERRY_ASSERT (!ECMA_OBJECT_IS_PROXY (object_p)); #endif /* JERRY_BUILTIN_PROXY */ JERRY_ASSERT (property_name_p != NULL); JERRY_ASSERT (options == ECMA_PROPERTY_GET_NO_OPTIONS || property_ref_p != NULL); ecma_object_base_type_t base_type = ecma_get_object_base_type (object_p); switch (base_type) { case ECMA_OBJECT_BASE_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_STRING: { if (ecma_string_is_length (property_name_p)) { if (options & ECMA_PROPERTY_GET_VALUE) { ecma_value_t prim_value_p = ext_object_p->u.cls.u3.value; ecma_string_t *prim_value_str_p = ecma_get_string_from_value (prim_value_p); lit_utf8_size_t length = ecma_string_get_length (prim_value_str_p); property_ref_p->virtual_value = ecma_make_uint32_value (length); } return ECMA_PROPERTY_VIRTUAL; } uint32_t index = ecma_string_get_array_index (property_name_p); if (index != ECMA_STRING_NOT_ARRAY_INDEX) { ecma_value_t prim_value_p = ext_object_p->u.cls.u3.value; ecma_string_t *prim_value_str_p = ecma_get_string_from_value (prim_value_p); if (index < ecma_string_get_length (prim_value_str_p)) { if (options & ECMA_PROPERTY_GET_VALUE) { ecma_char_t char_at_idx = ecma_string_get_char_at_pos (prim_value_str_p, index); ecma_string_t *char_str_p = ecma_new_ecma_string_from_code_unit (char_at_idx); property_ref_p->virtual_value = ecma_make_string_value (char_str_p); } return ECMA_PROPERTY_FLAG_ENUMERABLE | ECMA_PROPERTY_VIRTUAL; } } break; } #if JERRY_BUILTIN_TYPEDARRAY /* ES2015 9.4.5.1 */ case ECMA_OBJECT_CLASS_TYPEDARRAY: { if (ecma_prop_name_is_symbol (property_name_p)) { break; } uint32_t index = ecma_string_get_array_index (property_name_p); if (index == ECMA_STRING_NOT_ARRAY_INDEX) { JERRY_ASSERT (index == UINT32_MAX); if (!ecma_typedarray_is_element_index (property_name_p)) { break; } } ecma_typedarray_info_t info = ecma_typedarray_get_info (object_p); ecma_value_t value = ecma_get_typedarray_element (&info, index); if (ECMA_IS_VALUE_ERROR (value)) { return ECMA_PROPERTY_TYPE_NOT_FOUND_AND_THROW; } if (JERRY_UNLIKELY (ecma_is_value_undefined (value))) { return ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP; } if (options & ECMA_PROPERTY_GET_VALUE) { property_ref_p->virtual_value = value; } else { ecma_fast_free_value (value); } return ECMA_PROPERTY_ENUMERABLE_WRITABLE | ECMA_PROPERTY_VIRTUAL; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE_NAMESPACE: { if (JERRY_UNLIKELY (ecma_prop_name_is_symbol (property_name_p))) { if (!ecma_op_compare_string_to_global_symbol (property_name_p, LIT_GLOBAL_SYMBOL_TO_STRING_TAG)) { return ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP; } /* ECMA-262 v11, 26.3.1 */ if (options & ECMA_PROPERTY_GET_VALUE) { property_ref_p->virtual_value = ecma_make_magic_string_value (LIT_MAGIC_STRING_MODULE_UL); } return ECMA_PROPERTY_VIRTUAL; } ecma_property_t *property_p = ecma_find_named_property (object_p, property_name_p); if (property_p == NULL) { return ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP; } JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); if (*property_p & ECMA_PROPERTY_FLAG_DATA) { if (options & ECMA_PROPERTY_GET_EXT_REFERENCE) { ((ecma_extended_property_ref_t *) property_ref_p)->property_p = property_p; } if (property_ref_p != NULL) { property_ref_p->value_p = ECMA_PROPERTY_VALUE_PTR (property_p); } return *property_p; } if (options & ECMA_PROPERTY_GET_VALUE) { ecma_property_value_t *prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); prop_value_p = ecma_get_property_value_from_named_reference (prop_value_p); property_ref_p->virtual_value = ecma_fast_copy_value (prop_value_p->value); } return ECMA_PROPERTY_ENUMERABLE_WRITABLE | ECMA_PROPERTY_VIRTUAL; } #endif /* JERRY_MODULE_SYSTEM */ } break; } case ECMA_OBJECT_BASE_TYPE_ARRAY: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (ecma_string_is_length (property_name_p)) { if (options & ECMA_PROPERTY_GET_VALUE) { property_ref_p->virtual_value = ecma_make_uint32_value (ext_object_p->u.array.length); } uint32_t length_prop = ext_object_p->u.array.length_prop_and_hole_count; return length_prop & (ECMA_PROPERTY_FLAG_WRITABLE | ECMA_PROPERTY_VIRTUAL); } if (ecma_op_array_is_fast_array (ext_object_p)) { uint32_t index = ecma_string_get_array_index (property_name_p); if (index != ECMA_STRING_NOT_ARRAY_INDEX) { if (JERRY_LIKELY (index < ext_object_p->u.array.length)) { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); if (ecma_is_value_array_hole (values_p[index])) { return ECMA_PROPERTY_TYPE_NOT_FOUND; } if (options & ECMA_PROPERTY_GET_VALUE) { property_ref_p->virtual_value = ecma_fast_copy_value (values_p[index]); } return ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | ECMA_PROPERTY_VIRTUAL; } } return ECMA_PROPERTY_TYPE_NOT_FOUND; } break; } default: { break; } } ecma_property_t *property_p = ecma_find_named_property (object_p, property_name_p); ecma_object_type_t type = ecma_get_object_type (object_p); if (property_p == NULL) { switch (type) { case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { if (ecma_builtin_function_is_routine (object_p)) { property_p = ecma_builtin_routine_try_to_instantiate_property (object_p, property_name_p); break; } /* FALLTHRU */ } case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { property_p = ecma_builtin_try_to_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_CLASS: { if (((ecma_extended_object_t *) object_p)->u.cls.type == ECMA_OBJECT_CLASS_ARGUMENTS) { property_p = ecma_op_arguments_object_try_to_lazy_instantiate_property (object_p, property_name_p); } break; } case ECMA_OBJECT_TYPE_FUNCTION: { /* Get prototype physical property. */ property_p = ecma_op_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { property_p = ecma_op_external_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { property_p = ecma_op_bound_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } default: { break; } } if (property_p == NULL) { return ECMA_PROPERTY_TYPE_NOT_FOUND; } } else if (type == ECMA_OBJECT_TYPE_CLASS && ((ecma_extended_object_t *) object_p)->u.cls.type == ECMA_OBJECT_CLASS_ARGUMENTS && (((ecma_extended_object_t *) object_p)->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED)) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; uint32_t index = ecma_string_get_array_index (property_name_p); if (index < ext_object_p->u.cls.u2.formal_params_number) { ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) ext_object_p; ecma_value_t *argv_p = (ecma_value_t *) (mapped_arguments_p + 1); if (!ecma_is_value_empty (argv_p[index]) && argv_p[index] != ECMA_VALUE_ARGUMENT_NO_TRACK) { #if JERRY_LCACHE /* Mapped arguments initialized properties MUST not be lcached */ if (ecma_is_property_lcached (property_p)) { jmem_cpointer_t prop_name_cp; if (JERRY_UNLIKELY (ECMA_IS_DIRECT_STRING (property_name_p))) { prop_name_cp = (jmem_cpointer_t) ECMA_GET_DIRECT_STRING_VALUE (property_name_p); } else { ECMA_SET_NON_NULL_POINTER (prop_name_cp, property_name_p); } ecma_lcache_invalidate (object_p, prop_name_cp, property_p); } #endif /* JERRY_LCACHE */ ecma_string_t *name_p = ecma_op_arguments_object_get_formal_parameter (mapped_arguments_p, index); ecma_object_t *lex_env_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, mapped_arguments_p->lex_env); ecma_value_t binding_value = ecma_op_get_binding_value (lex_env_p, name_p, true); ecma_named_data_property_assign_value (object_p, ECMA_PROPERTY_VALUE_PTR (property_p), binding_value); ecma_free_value (binding_value); } } } if (options & ECMA_PROPERTY_GET_EXT_REFERENCE) { ((ecma_extended_property_ref_t *) property_ref_p)->property_p = property_p; } if (property_ref_p != NULL) { property_ref_p->value_p = ECMA_PROPERTY_VALUE_PTR (property_p); } return *property_p; } /* ecma_op_object_get_own_property */ /** * Generic [[HasProperty]] operation * * See also: * ECMAScript v6, 9.1.7.1 * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE_FALSE} - whether the property is found */ ecma_value_t ecma_op_object_has_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p) /**< property name */ { while (true) { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (object_p)) { return ecma_proxy_object_has (object_p, property_name_p); } #endif /* JERRY_BUILTIN_PROXY */ /* 2 - 3. */ ecma_property_t property = ecma_op_object_get_own_property (object_p, property_name_p, NULL, ECMA_PROPERTY_GET_NO_OPTIONS); if (property != ECMA_PROPERTY_TYPE_NOT_FOUND) { #if JERRY_BUILTIN_TYPEDARRAY if (JERRY_UNLIKELY (property == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_THROW)) { return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP || ECMA_PROPERTY_IS_FOUND (property)); return ecma_make_boolean_value (property != ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP); } jmem_cpointer_t proto_cp = ecma_op_ordinary_object_get_prototype_of (object_p); /* 7. */ if (proto_cp == JMEM_CP_NULL) { return ECMA_VALUE_FALSE; } object_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp); } } /* ecma_op_object_has_property */ /** * Search the value corresponding to a property name * * Note: search includes prototypes * * @return ecma value if property is found * ECMA_VALUE_NOT_FOUND if property is not found * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_find_own (ecma_value_t base_value, /**< base value */ ecma_object_t *object_p, /**< target object */ ecma_string_t *property_name_p) /**< property name */ { JERRY_ASSERT (object_p != NULL && !ecma_is_lexical_environment (object_p)); JERRY_ASSERT (property_name_p != NULL); JERRY_ASSERT (!ECMA_OBJECT_IS_PROXY (object_p)); ecma_object_base_type_t base_type = ecma_get_object_base_type (object_p); switch (base_type) { case ECMA_OBJECT_BASE_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_STRING: { if (ecma_string_is_length (property_name_p)) { ecma_value_t prim_value_p = ext_object_p->u.cls.u3.value; ecma_string_t *prim_value_str_p = ecma_get_string_from_value (prim_value_p); lit_utf8_size_t length = ecma_string_get_length (prim_value_str_p); return ecma_make_uint32_value (length); } uint32_t index = ecma_string_get_array_index (property_name_p); if (index != ECMA_STRING_NOT_ARRAY_INDEX) { ecma_value_t prim_value_p = ext_object_p->u.cls.u3.value; ecma_string_t *prim_value_str_p = ecma_get_string_from_value (prim_value_p); if (index < ecma_string_get_length (prim_value_str_p)) { ecma_char_t char_at_idx = ecma_string_get_char_at_pos (prim_value_str_p, index); return ecma_make_string_value (ecma_new_ecma_string_from_code_unit (char_at_idx)); } } break; } case ECMA_OBJECT_CLASS_ARGUMENTS: { if (!(ext_object_p->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED)) { break; } uint32_t index = ecma_string_get_array_index (property_name_p); if (index < ext_object_p->u.cls.u2.formal_params_number) { ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) ext_object_p; ecma_value_t *argv_p = (ecma_value_t *) (mapped_arguments_p + 1); if (!ecma_is_value_empty (argv_p[index]) && argv_p[index] != ECMA_VALUE_ARGUMENT_NO_TRACK) { ecma_string_t *name_p = ecma_op_arguments_object_get_formal_parameter (mapped_arguments_p, index); ecma_object_t *lex_env_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, mapped_arguments_p->lex_env); return ecma_op_get_binding_value (lex_env_p, name_p, true); } } break; } #if JERRY_BUILTIN_TYPEDARRAY /* ES2015 9.4.5.4 */ case ECMA_OBJECT_CLASS_TYPEDARRAY: { if (ecma_prop_name_is_symbol (property_name_p)) { break; } uint32_t index = ecma_string_get_array_index (property_name_p); if (index == ECMA_STRING_NOT_ARRAY_INDEX) { JERRY_ASSERT (index == UINT32_MAX); if (!ecma_typedarray_is_element_index (property_name_p)) { break; } } ecma_typedarray_info_t info = ecma_typedarray_get_info (object_p); return ecma_get_typedarray_element (&info, index); } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE_NAMESPACE: { if (JERRY_UNLIKELY (ecma_prop_name_is_symbol (property_name_p))) { /* ECMA-262 v11, 26.3.1 */ if (ecma_op_compare_string_to_global_symbol (property_name_p, LIT_GLOBAL_SYMBOL_TO_STRING_TAG)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_MODULE_UL); } return ECMA_VALUE_NOT_FOUND; } ecma_property_t *property_p = ecma_find_named_property (object_p, property_name_p); if (property_p == NULL) { return ECMA_VALUE_NOT_FOUND; } JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); ecma_property_value_t *prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (!(*property_p & ECMA_PROPERTY_FLAG_DATA)) { prop_value_p = ecma_get_property_value_from_named_reference (prop_value_p); if (JERRY_UNLIKELY (prop_value_p->value == ECMA_VALUE_UNINITIALIZED)) { return ecma_raise_reference_error (ECMA_ERR_LET_CONST_NOT_INITIALIZED); } } return ecma_fast_copy_value (prop_value_p->value); } #endif /* JERRY_MODULE_SYSTEM */ } break; } case ECMA_OBJECT_BASE_TYPE_ARRAY: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (ecma_string_is_length (property_name_p)) { return ecma_make_uint32_value (ext_object_p->u.array.length); } if (JERRY_LIKELY (ecma_op_array_is_fast_array (ext_object_p))) { uint32_t index = ecma_string_get_array_index (property_name_p); if (JERRY_LIKELY (index != ECMA_STRING_NOT_ARRAY_INDEX)) { if (JERRY_LIKELY (index < ext_object_p->u.array.length)) { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); return (ecma_is_value_array_hole (values_p[index]) ? ECMA_VALUE_NOT_FOUND : ecma_fast_copy_value (values_p[index])); } } return ECMA_VALUE_NOT_FOUND; } break; } default: { break; } } ecma_property_t *property_p = ecma_find_named_property (object_p, property_name_p); if (property_p == NULL) { switch (ecma_get_object_type (object_p)) { case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { if (ecma_builtin_function_is_routine (object_p)) { property_p = ecma_builtin_routine_try_to_instantiate_property (object_p, property_name_p); break; } /* FALLTHRU */ } case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { property_p = ecma_builtin_try_to_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_CLASS: { if (((ecma_extended_object_t *) object_p)->u.cls.type == ECMA_OBJECT_CLASS_ARGUMENTS) { property_p = ecma_op_arguments_object_try_to_lazy_instantiate_property (object_p, property_name_p); } break; } case ECMA_OBJECT_TYPE_FUNCTION: { /* Get prototype physical property. */ property_p = ecma_op_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { property_p = ecma_op_external_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { property_p = ecma_op_bound_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } default: { break; } } if (property_p == NULL) { return ECMA_VALUE_NOT_FOUND; } } JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); ecma_property_value_t *prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (*property_p & ECMA_PROPERTY_FLAG_DATA) { return ecma_fast_copy_value (prop_value_p->value); } ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (prop_value_p); if (get_set_pair_p->getter_cp == JMEM_CP_NULL) { return ECMA_VALUE_UNDEFINED; } ecma_object_t *getter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp); return ecma_op_function_call (getter_p, base_value, NULL, 0); } /* ecma_op_object_find_own */ /** * Search the value corresponding to a property index * * Note: this method falls back to the general ecma_op_object_find * * @return ecma value if property is found * ECMA_VALUE_NOT_FOUND if property is not found * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_find_by_index (ecma_object_t *object_p, /**< the object */ ecma_length_t index) /**< property index */ { if (JERRY_LIKELY (index <= ECMA_DIRECT_STRING_MAX_IMM)) { return ecma_op_object_find (object_p, ECMA_CREATE_DIRECT_UINT32_STRING (index)); } ecma_string_t *index_str_p = ecma_new_ecma_string_from_length (index); ecma_value_t ret_value = ecma_op_object_find (object_p, index_str_p); ecma_deref_ecma_string (index_str_p); return ret_value; } /* ecma_op_object_find_by_index */ /** * Search the value corresponding to a property name * * Note: search includes prototypes * * @return ecma value if property is found * ECMA_VALUE_NOT_FOUND if property is not found * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_find (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p) /**< property name */ { ecma_value_t base_value = ecma_make_object_value (object_p); while (true) { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (object_p)) { return ecma_proxy_object_find (object_p, property_name_p); } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t value = ecma_op_object_find_own (base_value, object_p, property_name_p); if (ecma_is_value_found (value)) { return value; } if (object_p->u2.prototype_cp == JMEM_CP_NULL) { break; } object_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, object_p->u2.prototype_cp); } return ECMA_VALUE_NOT_FOUND; } /* ecma_op_object_find */ /** * [[Get]] operation of ecma object * * This function returns the value of a named property, or undefined * if the property is not found in the prototype chain. If the property * is an accessor, it calls the "get" callback function and returns * with its result (including error throws). * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_get (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p) /**< property name */ { return ecma_op_object_get_with_receiver (object_p, property_name_p, ecma_make_object_value (object_p)); } /* ecma_op_object_get */ /** * [[Get]] operation of ecma object with the specified receiver * * This function returns the value of a named property, or undefined * if the property is not found in the prototype chain. If the property * is an accessor, it calls the "get" callback function and returns * with its result (including error throws). * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_get_with_receiver (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ ecma_value_t receiver) /**< receiver to invoke getter function */ { while (true) { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (object_p)) { return ecma_proxy_object_get (object_p, property_name_p, receiver); } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t value = ecma_op_object_find_own (receiver, object_p, property_name_p); if (ecma_is_value_found (value)) { return value; } jmem_cpointer_t proto_cp = ecma_op_ordinary_object_get_prototype_of (object_p); if (proto_cp == JMEM_CP_NULL) { break; } object_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp); } return ECMA_VALUE_UNDEFINED; } /* ecma_op_object_get_with_receiver */ /** * [[Get]] operation of ecma object specified for property index * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_get_by_index (ecma_object_t *object_p, /**< the object */ ecma_length_t index) /**< property index */ { if (JERRY_LIKELY (index <= ECMA_DIRECT_STRING_MAX_IMM)) { return ecma_op_object_get (object_p, ECMA_CREATE_DIRECT_UINT32_STRING (index)); } ecma_string_t *index_str_p = ecma_new_ecma_string_from_length (index); ecma_value_t ret_value = ecma_op_object_get (object_p, index_str_p); ecma_deref_ecma_string (index_str_p); return ret_value; } /* ecma_op_object_get_by_index */ /** * Perform ToLength(O.[[Get]]("length")) operation * * The property is converted to uint32 during the operation * * @return ECMA_VALUE_ERROR - if there was any error during the operation * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t ecma_op_object_get_length (ecma_object_t *object_p, /**< the object */ ecma_length_t *length_p) /**< [out] length value converted to uint32 */ { if (JERRY_LIKELY (ecma_get_object_base_type (object_p) == ECMA_OBJECT_BASE_TYPE_ARRAY)) { *length_p = (ecma_length_t) ecma_array_get_length (object_p); return ECMA_VALUE_EMPTY; } ecma_value_t len_value = ecma_op_object_get_by_magic_id (object_p, LIT_MAGIC_STRING_LENGTH); ecma_value_t len_number = ecma_op_to_length (len_value, length_p); ecma_free_value (len_value); JERRY_ASSERT (ECMA_IS_VALUE_ERROR (len_number) || ecma_is_value_empty (len_number)); return len_number; } /* ecma_op_object_get_length */ /** * [[Get]] operation of ecma object where the property name is a magic string * * This function returns the value of a named property, or undefined * if the property is not found in the prototype chain. If the property * is an accessor, it calls the "get" callback function and returns * with its result (including error throws). * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_get_by_magic_id (ecma_object_t *object_p, /**< the object */ lit_magic_string_id_t property_id) /**< property magic string id */ { return ecma_op_object_get (object_p, ecma_get_magic_string (property_id)); } /* ecma_op_object_get_by_magic_id */ /** * Descriptor string for each global symbol */ static const uint16_t ecma_global_symbol_descriptions[] = { LIT_MAGIC_STRING_ASYNC_ITERATOR, LIT_MAGIC_STRING_HAS_INSTANCE, LIT_MAGIC_STRING_IS_CONCAT_SPREADABLE, LIT_MAGIC_STRING_ITERATOR, LIT_MAGIC_STRING_MATCH, LIT_MAGIC_STRING_REPLACE, LIT_MAGIC_STRING_SEARCH, LIT_MAGIC_STRING_SPECIES, LIT_MAGIC_STRING_SPLIT, LIT_MAGIC_STRING_TO_PRIMITIVE, LIT_MAGIC_STRING_TO_STRING_TAG, LIT_MAGIC_STRING_UNSCOPABLES, LIT_MAGIC_STRING_MATCH_ALL, }; JERRY_STATIC_ASSERT ((sizeof (ecma_global_symbol_descriptions) / sizeof (uint16_t) == ECMA_BUILTIN_GLOBAL_SYMBOL_COUNT), ecma_global_symbol_descriptions_must_have_global_symbol_count_elements); /** * [[Get]] a well-known symbol by the given property id * * @return pointer to the requested well-known symbol */ ecma_string_t * ecma_op_get_global_symbol (lit_magic_string_id_t property_id) /**< property symbol id */ { JERRY_ASSERT (LIT_IS_GLOBAL_SYMBOL (property_id)); uint32_t symbol_index = (uint32_t) property_id - (uint32_t) LIT_GLOBAL_SYMBOL__FIRST; jmem_cpointer_t symbol_cp = JERRY_CONTEXT (global_symbols_cp)[symbol_index]; if (symbol_cp != JMEM_CP_NULL) { ecma_string_t *symbol_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, symbol_cp); ecma_ref_ecma_string (symbol_p); return symbol_p; } ecma_string_t *symbol_dot_p = ecma_get_magic_string (LIT_MAGIC_STRING_SYMBOL_DOT_UL); uint16_t description = ecma_global_symbol_descriptions[symbol_index]; ecma_string_t *name_p = ecma_get_magic_string ((lit_magic_string_id_t) description); ecma_string_t *descriptor_p = ecma_concat_ecma_strings (symbol_dot_p, name_p); ecma_string_t *symbol_p = ecma_new_symbol_from_descriptor_string (ecma_make_string_value (descriptor_p)); symbol_p->u.hash = (uint16_t) ((property_id << ECMA_SYMBOL_FLAGS_SHIFT) | ECMA_SYMBOL_FLAG_GLOBAL); ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (global_symbols_cp)[symbol_index], symbol_p); ecma_ref_ecma_string (symbol_p); return symbol_p; } /* ecma_op_get_global_symbol */ /** * Checks whether the string equals to the global symbol. * * @return true - if the string equals to the global symbol * false - otherwise */ bool ecma_op_compare_string_to_global_symbol (ecma_string_t *string_p, /**< string to compare */ lit_magic_string_id_t property_id) /**< property symbol id */ { JERRY_ASSERT (LIT_IS_GLOBAL_SYMBOL (property_id)); uint32_t symbol_index = (uint32_t) property_id - (uint32_t) LIT_GLOBAL_SYMBOL__FIRST; jmem_cpointer_t symbol_cp = JERRY_CONTEXT (global_symbols_cp)[symbol_index]; return (symbol_cp != JMEM_CP_NULL && string_p == ECMA_GET_NON_NULL_POINTER (ecma_string_t, symbol_cp)); } /* ecma_op_compare_string_to_global_symbol */ /** * [[Get]] operation of ecma object where the property is a well-known symbol * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_get_by_symbol_id (ecma_object_t *object_p, /**< the object */ lit_magic_string_id_t property_id) /**< property symbol id */ { ecma_string_t *symbol_p = ecma_op_get_global_symbol (property_id); ecma_value_t ret_value = ecma_op_object_get (object_p, symbol_p); ecma_deref_ecma_string (symbol_p); return ret_value; } /* ecma_op_object_get_by_symbol_id */ /** * GetMethod operation * * See also: ECMA-262 v6, 7.3.9 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator function object - if success * raised error - otherwise */ static ecma_value_t ecma_op_get_method (ecma_value_t value, /**< ecma value */ ecma_string_t *prop_name_p) /**< property name */ { /* 2. */ ecma_value_t obj_value = ecma_op_to_object (value); if (ECMA_IS_VALUE_ERROR (obj_value)) { return obj_value; } ecma_object_t *obj_p = ecma_get_object_from_value (obj_value); ecma_value_t func; func = ecma_op_object_get (obj_p, prop_name_p); ecma_deref_object (obj_p); /* 3. */ if (ECMA_IS_VALUE_ERROR (func)) { return func; } /* 4. */ if (ecma_is_value_undefined (func) || ecma_is_value_null (func)) { return ECMA_VALUE_UNDEFINED; } /* 5. */ if (!ecma_op_is_callable (func)) { ecma_free_value (func); return ecma_raise_type_error (ECMA_ERR_ITERATOR_IS_NOT_CALLABLE); } /* 6. */ return func; } /* ecma_op_get_method */ /** * GetMethod operation when the property is a well-known symbol * * See also: ECMA-262 v6, 7.3.9 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator function object - if success * raised error - otherwise */ ecma_value_t ecma_op_get_method_by_symbol_id (ecma_value_t value, /**< ecma value */ lit_magic_string_id_t symbol_id) /**< property symbol id */ { ecma_string_t *prop_name_p = ecma_op_get_global_symbol (symbol_id); ecma_value_t ret_value = ecma_op_get_method (value, prop_name_p); ecma_deref_ecma_string (prop_name_p); return ret_value; } /* ecma_op_get_method_by_symbol_id */ /** * GetMethod operation when the property is a magic string * * See also: ECMA-262 v6, 7.3.9 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator function object - if success * raised error - otherwise */ ecma_value_t ecma_op_get_method_by_magic_id (ecma_value_t value, /**< ecma value */ lit_magic_string_id_t magic_id) /**< property magic id */ { return ecma_op_get_method (value, ecma_get_magic_string (magic_id)); } /* ecma_op_get_method_by_magic_id */ /** * [[Put]] ecma general object's operation specialized for property index * * Note: This function falls back to the general ecma_op_object_put * * @return ecma value * The returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_object_put_by_index (ecma_object_t *object_p, /**< the object */ ecma_length_t index, /**< property index */ ecma_value_t value, /**< ecma value */ bool is_throw) /**< flag that controls failure handling */ { if (JERRY_LIKELY (index <= ECMA_DIRECT_STRING_MAX_IMM)) { return ecma_op_object_put (object_p, ECMA_CREATE_DIRECT_UINT32_STRING (index), value, is_throw); } ecma_string_t *index_str_p = ecma_new_ecma_string_from_length (index); ecma_value_t ret_value = ecma_op_object_put (object_p, index_str_p, value, is_throw); ecma_deref_ecma_string (index_str_p); return ret_value; } /* ecma_op_object_put_by_index */ /** * [[Put]] ecma general object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * ECMA-262 v5, 8.12.5 * Also incorporates [[CanPut]] ECMA-262 v5, 8.12.4 * * @return ecma value * The returned value must be freed with ecma_free_value. * * Returns with ECMA_VALUE_TRUE if the operation is * successful. Otherwise it returns with an error object * or ECMA_VALUE_FALSE. * * Note: even if is_throw is false, the setter can throw an * error, and this function returns with that error. */ ecma_value_t ecma_op_object_put (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ ecma_value_t value, /**< ecma value */ bool is_throw) /**< flag that controls failure handling */ { return ecma_op_object_put_with_receiver (object_p, property_name_p, value, ecma_make_object_value (object_p), is_throw); } /* ecma_op_object_put */ /** * [[Set]] ( P, V, Receiver) operation part for ordinary objects * * See also: ECMAScript v6, 9.19.9 * * @return ecma value * The returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_op_object_put_apply_receiver (ecma_value_t receiver, /**< receiver */ ecma_string_t *property_name_p, /**< property name */ ecma_value_t value, /**< value to set */ bool is_throw) /**< flag that controls failure handling */ { /* 5.b */ if (!ecma_is_value_object (receiver)) { return ECMA_REJECT (is_throw, ECMA_ERR_RECEIVER_MUST_BE_AN_OBJECT); } ecma_object_t *receiver_obj_p = ecma_get_object_from_value (receiver); ecma_property_descriptor_t prop_desc; /* 5.c */ ecma_value_t status = ecma_op_object_get_own_property_descriptor (receiver_obj_p, property_name_p, &prop_desc); /* 5.d */ if (ECMA_IS_VALUE_ERROR (status)) { return status; } /* 5.e */ if (ecma_is_value_true (status)) { ecma_value_t result; /* 5.e.i - 5.e.ii */ if (prop_desc.flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED) || !(prop_desc.flags & JERRY_PROP_IS_WRITABLE)) { result = ecma_raise_property_redefinition (property_name_p, prop_desc.flags); } else { /* 5.e.iii */ JERRY_ASSERT (prop_desc.flags & JERRY_PROP_IS_VALUE_DEFINED); ecma_free_value (prop_desc.value); prop_desc.value = ecma_copy_value (value); /* 5.e.iv */ result = ecma_op_object_define_own_property (receiver_obj_p, property_name_p, &prop_desc); if (JERRY_UNLIKELY (ecma_is_value_false (result))) { result = ECMA_REJECT (is_throw, ECMA_ERR_PROXY_TRAP_RETURNED_FALSISH); } } ecma_free_property_descriptor (&prop_desc); return result; } #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (receiver_obj_p)) { ecma_property_descriptor_t desc; /* Based on: ES6 9.1.9 [[Set]] 4.d.i. / ES11 9.1.9.2 OrdinarySetWithOwnDescriptor 2.c.i. */ desc.flags = (JERRY_PROP_IS_CONFIGURABLE | JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE | JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_WRITABLE | JERRY_PROP_IS_WRITABLE_DEFINED | JERRY_PROP_IS_VALUE_DEFINED); desc.value = value; ecma_value_t ret_value = ecma_proxy_object_define_own_property (receiver_obj_p, property_name_p, &desc); if (JERRY_UNLIKELY (ecma_is_value_false (ret_value))) { ret_value = ECMA_REJECT (is_throw, ECMA_ERR_PROXY_TRAP_RETURNED_FALSISH); } return ret_value; } #endif /* JERRY_BUILTIN_PROXY */ if (JERRY_UNLIKELY (ecma_op_object_is_fast_array (receiver_obj_p))) { ecma_fast_array_convert_to_normal (receiver_obj_p); } /* 5.f.i */ ecma_property_value_t *new_prop_value_p; new_prop_value_p = ecma_create_named_data_property (receiver_obj_p, property_name_p, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); JERRY_ASSERT (ecma_is_value_undefined (new_prop_value_p->value)); new_prop_value_p->value = ecma_copy_value_if_not_object (value); return ECMA_VALUE_TRUE; } /* ecma_op_object_put_apply_receiver */ /** * [[Put]] ecma general object's operation with given receiver * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * ECMA-262 v5, 8.12.5 * ECMA-262 v6, 9.1.9 * Also incorporates [[CanPut]] ECMA-262 v5, 8.12.4 * * @return ecma value * The returned value must be freed with ecma_free_value. * * Returns with ECMA_VALUE_TRUE if the operation is * successful. Otherwise it returns with an error object * or ECMA_VALUE_FALSE. * * Note: even if is_throw is false, the setter can throw an * error, and this function returns with that error. */ ecma_value_t ecma_op_object_put_with_receiver (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ ecma_value_t value, /**< ecma value */ ecma_value_t receiver, /**< receiver */ bool is_throw) /**< flag that controls failure handling */ { JERRY_ASSERT (object_p != NULL && !ecma_is_lexical_environment (object_p)); JERRY_ASSERT (property_name_p != NULL); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (object_p)) { return ecma_proxy_object_set (object_p, property_name_p, value, receiver, is_throw); } #endif /* JERRY_BUILTIN_PROXY */ ecma_object_base_type_t base_type = ecma_get_object_base_type (object_p); switch (base_type) { case ECMA_OBJECT_BASE_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_ARGUMENTS: { if (!(ext_object_p->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED)) { break; } uint32_t index = ecma_string_get_array_index (property_name_p); if (index < ext_object_p->u.cls.u2.formal_params_number) { ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) ext_object_p; ecma_value_t *argv_p = (ecma_value_t *) (mapped_arguments_p + 1); if (!ecma_is_value_empty (argv_p[index]) && argv_p[index] != ECMA_VALUE_ARGUMENT_NO_TRACK) { ecma_string_t *name_p = ecma_op_arguments_object_get_formal_parameter (mapped_arguments_p, index); ecma_object_t *lex_env_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, mapped_arguments_p->lex_env); ecma_op_set_mutable_binding (lex_env_p, name_p, value, true); return ECMA_VALUE_TRUE; } } break; } #if JERRY_BUILTIN_TYPEDARRAY /* ES2015 9.4.5.5 */ case ECMA_OBJECT_CLASS_TYPEDARRAY: { if (ecma_prop_name_is_symbol (property_name_p)) { break; } uint32_t index = ecma_string_get_array_index (property_name_p); if (index == ECMA_STRING_NOT_ARRAY_INDEX) { JERRY_ASSERT (index == UINT32_MAX); if (!ecma_typedarray_is_element_index (property_name_p)) { break; } } ecma_typedarray_info_t info = ecma_typedarray_get_info (object_p); return ecma_set_typedarray_element (&info, value, index); } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE_NAMESPACE: { return ecma_raise_readonly_assignment (property_name_p, is_throw); } #endif /* JERRY_MODULE_SYSTEM */ } break; } case ECMA_OBJECT_BASE_TYPE_ARRAY: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (ecma_string_is_length (property_name_p)) { if (ecma_is_property_writable ((ecma_property_t) ext_object_p->u.array.length_prop_and_hole_count)) { return ecma_op_array_object_set_length (object_p, value, 0); } return ecma_raise_readonly_assignment (property_name_p, is_throw); } if (JERRY_LIKELY (ecma_op_array_is_fast_array (ext_object_p))) { uint32_t index = ecma_string_get_array_index (property_name_p); if (JERRY_UNLIKELY (index == ECMA_STRING_NOT_ARRAY_INDEX)) { ecma_fast_array_convert_to_normal (object_p); } else if (ecma_fast_array_set_property (object_p, index, value)) { return ECMA_VALUE_TRUE; } } JERRY_ASSERT (!ecma_op_object_is_fast_array (object_p)); break; } default: { break; } } ecma_property_t *property_p = ecma_find_named_property (object_p, property_name_p); if (property_p == NULL) { switch (ecma_get_object_type (object_p)) { case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { if (ecma_builtin_function_is_routine (object_p)) { property_p = ecma_builtin_routine_try_to_instantiate_property (object_p, property_name_p); break; } /* FALLTHRU */ } case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { property_p = ecma_builtin_try_to_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_STRING: { uint32_t index = ecma_string_get_array_index (property_name_p); if (index != ECMA_STRING_NOT_ARRAY_INDEX) { ecma_value_t prim_value_p = ext_object_p->u.cls.u3.value; ecma_string_t *prim_value_str_p = ecma_get_string_from_value (prim_value_p); if (index < ecma_string_get_length (prim_value_str_p)) { return ecma_raise_readonly_assignment (property_name_p, is_throw); } } break; } case ECMA_OBJECT_CLASS_ARGUMENTS: { property_p = ecma_op_arguments_object_try_to_lazy_instantiate_property (object_p, property_name_p); break; } } break; } case ECMA_OBJECT_TYPE_FUNCTION: { if (ecma_string_is_length (property_name_p)) { /* Uninitialized 'length' property is non-writable (ECMA-262 v6, 19.2.4.1) */ if (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (((ecma_extended_object_t *) object_p)->u.function.scope_cp)) { return ecma_raise_readonly_assignment (property_name_p, is_throw); } } /* Get prototype physical property. */ property_p = ecma_op_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { property_p = ecma_op_external_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { property_p = ecma_op_bound_function_try_to_lazy_instantiate_property (object_p, property_name_p); break; } default: { break; } } } jmem_cpointer_t setter_cp = JMEM_CP_NULL; if (property_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); if (*property_p & ECMA_PROPERTY_FLAG_DATA) { if (ecma_is_property_writable (*property_p)) { if (ecma_make_object_value (object_p) != receiver) { return ecma_op_object_put_apply_receiver (receiver, property_name_p, value, is_throw); } /* There is no need for special casing arrays here because changing the * value of an existing property never changes the length of an array. */ ecma_named_data_property_assign_value (object_p, ECMA_PROPERTY_VALUE_PTR (property_p), value); return ECMA_VALUE_TRUE; } } else { ecma_getter_setter_pointers_t *get_set_pair_p; get_set_pair_p = ecma_get_named_accessor_property (ECMA_PROPERTY_VALUE_PTR (property_p)); setter_cp = get_set_pair_p->setter_cp; } } else { bool create_new_property = true; jmem_cpointer_t obj_cp; ECMA_SET_NON_NULL_POINTER (obj_cp, object_p); ecma_object_t *proto_p = object_p; while (true) { obj_cp = ecma_op_ordinary_object_get_prototype_of (proto_p); if (obj_cp == JMEM_CP_NULL) { break; } ecma_property_ref_t property_ref = { NULL }; proto_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_cp); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (proto_p)) { return ecma_op_object_put_with_receiver (proto_p, property_name_p, value, receiver, is_throw); } #endif /* JERRY_BUILTIN_PROXY */ ecma_property_t inherited_property = ecma_op_object_get_own_property (proto_p, property_name_p, &property_ref, ECMA_PROPERTY_GET_NO_OPTIONS); if (ECMA_PROPERTY_IS_FOUND (inherited_property)) { JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (inherited_property)); if (!(inherited_property & ECMA_PROPERTY_FLAG_DATA)) { setter_cp = ecma_get_named_accessor_property (property_ref.value_p)->setter_cp; create_new_property = false; break; } create_new_property = ecma_is_property_writable (inherited_property); break; } JERRY_ASSERT (inherited_property == ECMA_PROPERTY_TYPE_NOT_FOUND || inherited_property == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP); } #if JERRY_BUILTIN_PROXY if (create_new_property && ecma_is_value_object (receiver) && ECMA_OBJECT_IS_PROXY (ecma_get_object_from_value (receiver))) { return ecma_op_object_put_apply_receiver (receiver, property_name_p, value, is_throw); } #endif /* JERRY_BUILTIN_PROXY */ if (create_new_property && ecma_op_ordinary_object_is_extensible (object_p)) { const ecma_object_base_type_t obj_base_type = ecma_get_object_base_type (object_p); if (obj_base_type == ECMA_OBJECT_BASE_TYPE_CLASS) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (ext_object_p->u.cls.type == ECMA_OBJECT_CLASS_ARGUMENTS && ext_object_p->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { const uint32_t flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW; return ecma_builtin_helper_def_prop (object_p, property_name_p, value, flags); } } uint32_t index = ecma_string_get_array_index (property_name_p); if (obj_base_type == ECMA_OBJECT_BASE_TYPE_ARRAY && index != ECMA_STRING_NOT_ARRAY_INDEX) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (index < UINT32_MAX && index >= ext_object_p->u.array.length) { if (!ecma_is_property_writable ((ecma_property_t) ext_object_p->u.array.length_prop_and_hole_count)) { return ecma_raise_readonly_assignment (property_name_p, is_throw); } ext_object_p->u.array.length = index + 1; } } return ecma_op_object_put_apply_receiver (receiver, property_name_p, value, is_throw); ecma_property_value_t *new_prop_value_p; new_prop_value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); JERRY_ASSERT (ecma_is_value_undefined (new_prop_value_p->value)); new_prop_value_p->value = ecma_copy_value_if_not_object (value); return ECMA_VALUE_TRUE; } } if (setter_cp == JMEM_CP_NULL) { return ecma_raise_readonly_assignment (property_name_p, is_throw); } ecma_value_t ret_value = ecma_op_function_call (ECMA_GET_NON_NULL_POINTER (ecma_object_t, setter_cp), receiver, &value, 1); if (!ECMA_IS_VALUE_ERROR (ret_value)) { ecma_fast_free_value (ret_value); ret_value = ECMA_VALUE_TRUE; } return ret_value; } /* ecma_op_object_put_with_receiver */ /** * [[Delete]] ecma object's operation specialized for property index * * Note: * This method falls back to the general ecma_op_object_delete * * @return true - if deleted successfully * false - or type error otherwise (based in 'is_throw') */ ecma_value_t ecma_op_object_delete_by_index (ecma_object_t *obj_p, /**< the object */ ecma_length_t index, /**< property index */ bool is_throw) /**< flag that controls failure handling */ { if (JERRY_LIKELY (index <= ECMA_DIRECT_STRING_MAX_IMM)) { return ecma_op_object_delete (obj_p, ECMA_CREATE_DIRECT_UINT32_STRING (index), is_throw); ; } ecma_string_t *index_str_p = ecma_new_ecma_string_from_length (index); ecma_value_t ret_value = ecma_op_object_delete (obj_p, index_str_p, is_throw); ecma_deref_ecma_string (index_str_p); return ret_value; } /* ecma_op_object_delete_by_index */ /** * [[Delete]] ecma object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * * Note: * returned value must be freed with ecma_free_value * * @return true - if deleted successfully * false - or type error otherwise (based in 'is_throw') */ ecma_value_t ecma_op_object_delete (ecma_object_t *obj_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ bool is_strict) /**< flag that controls failure handling */ { JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); JERRY_ASSERT (property_name_p != NULL); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { return ecma_proxy_object_delete_property (obj_p, property_name_p, is_strict); } #endif /* JERRY_BUILTIN_PROXY */ JERRY_ASSERT_OBJECT_TYPE_IS_VALID (ecma_get_object_type (obj_p)); return ecma_op_general_object_delete (obj_p, property_name_p, is_strict); } /* ecma_op_object_delete */ /** * [[DefaultValue]] ecma object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_default_value (ecma_object_t *obj_p, /**< the object */ ecma_preferred_type_hint_t hint) /**< hint on preferred result type */ { JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); JERRY_ASSERT_OBJECT_TYPE_IS_VALID (ecma_get_object_type (obj_p)); /* * typedef ecma_property_t * (*default_value_ptr_t) (ecma_object_t *, ecma_string_t *); * static const default_value_ptr_t default_value [ECMA_OBJECT_TYPE__COUNT] = * { * [ECMA_OBJECT_TYPE_GENERAL] = &ecma_op_general_object_default_value, * [ECMA_OBJECT_TYPE_CLASS] = &ecma_op_general_object_default_value, * [ECMA_OBJECT_TYPE_FUNCTION] = &ecma_op_general_object_default_value, * [ECMA_OBJECT_TYPE_NATIVE_FUNCTION] = &ecma_op_general_object_default_value, * [ECMA_OBJECT_TYPE_ARRAY] = &ecma_op_general_object_default_value, * [ECMA_OBJECT_TYPE_BOUND_FUNCTION] = &ecma_op_general_object_default_value, * [ECMA_OBJECT_TYPE_PSEUDO_ARRAY] = &ecma_op_general_object_default_value * }; * * return default_value[type] (obj_p, property_name_p); */ return ecma_op_general_object_default_value (obj_p, hint); } /* ecma_op_object_default_value */ /** * [[DefineOwnProperty]] ecma object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_define_own_property (ecma_object_t *obj_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ const ecma_property_descriptor_t *property_desc_p) /**< property * descriptor */ { JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); JERRY_ASSERT (property_name_p != NULL); const ecma_object_type_t type = ecma_get_object_type (obj_p); switch (type) { case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_ARGUMENTS: { return ecma_op_arguments_object_define_own_property (obj_p, property_name_p, property_desc_p); } #if JERRY_BUILTIN_TYPEDARRAY /* ES2015 9.4.5.1 */ case ECMA_OBJECT_CLASS_TYPEDARRAY: { return ecma_op_typedarray_define_own_property (obj_p, property_name_p, property_desc_p); } #endif /* JERRY_BUILTIN_TYPEDARRAY */ } break; } case ECMA_OBJECT_TYPE_ARRAY: case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { return ecma_op_array_object_define_own_property (obj_p, property_name_p, property_desc_p); } #if JERRY_BUILTIN_PROXY case ECMA_OBJECT_TYPE_PROXY: { return ecma_proxy_object_define_own_property (obj_p, property_name_p, property_desc_p); } #endif /* JERRY_BUILTIN_PROXY */ default: { break; } } return ecma_op_general_object_define_own_property (obj_p, property_name_p, property_desc_p); } /* ecma_op_object_define_own_property */ /** * Get property descriptor from specified property * * depending on the property type the following fields are set: * - for named data properties: { [Value], [Writable], [Enumerable], [Configurable] }; * - for named accessor properties: { [Get] - if defined, * [Set] - if defined, * [Enumerable], [Configurable] * }. * * The output property descriptor will always be initialized to an empty descriptor. * * @return ECMA_VALUE_ERROR - if the Proxy.[[GetOwnProperty]] operation raises error * ECMA_VALUE_{TRUE, FALSE} - if property found or not */ ecma_value_t ecma_op_object_get_own_property_descriptor (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ ecma_property_descriptor_t *prop_desc_p) /**< property descriptor */ { *prop_desc_p = ecma_make_empty_property_descriptor (); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (object_p)) { return ecma_proxy_object_get_own_property_descriptor (object_p, property_name_p, prop_desc_p); } #endif /* JERRY_BUILTIN_PROXY */ ecma_property_ref_t property_ref; property_ref.virtual_value = ECMA_VALUE_EMPTY; ecma_property_t property = ecma_op_object_get_own_property (object_p, property_name_p, &property_ref, ECMA_PROPERTY_GET_VALUE); if (!ECMA_PROPERTY_IS_FOUND (property)) { #if JERRY_BUILTIN_TYPEDARRAY if (JERRY_UNLIKELY (property == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_THROW)) { return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_NOT_FOUND || property == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP); return ECMA_VALUE_FALSE; } uint32_t flags = ecma_is_property_enumerable (property) ? JERRY_PROP_IS_ENUMERABLE : JERRY_PROP_NO_OPTS; flags |= ecma_is_property_configurable (property) ? JERRY_PROP_IS_CONFIGURABLE : JERRY_PROP_NO_OPTS; prop_desc_p->flags = (uint16_t) (JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE_DEFINED | flags); if (property & ECMA_PROPERTY_FLAG_DATA) { if (!ECMA_PROPERTY_IS_VIRTUAL (property)) { prop_desc_p->value = ecma_copy_value (property_ref.value_p->value); } else { #if JERRY_MODULE_SYSTEM if (JERRY_UNLIKELY (property_ref.virtual_value == ECMA_VALUE_UNINITIALIZED)) { return ecma_raise_reference_error (ECMA_ERR_LET_CONST_NOT_INITIALIZED); } #endif /* JERRY_MODULE_SYSTEM */ prop_desc_p->value = property_ref.virtual_value; } prop_desc_p->flags |= (uint16_t) (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE_DEFINED | (ecma_is_property_writable (property) ? JERRY_PROP_IS_WRITABLE : JERRY_PROP_NO_OPTS)); } else { ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (property_ref.value_p); prop_desc_p->flags |= (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED); if (get_set_pair_p->getter_cp == JMEM_CP_NULL) { prop_desc_p->get_p = NULL; } else { prop_desc_p->get_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp); ecma_ref_object (prop_desc_p->get_p); } if (get_set_pair_p->setter_cp == JMEM_CP_NULL) { prop_desc_p->set_p = NULL; } else { prop_desc_p->set_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp); ecma_ref_object (prop_desc_p->set_p); } } return ECMA_VALUE_TRUE; } /* ecma_op_object_get_own_property_descriptor */ #if JERRY_BUILTIN_PROXY /** * Get property descriptor from a target value for a specified property. * * For more details see ecma_op_object_get_own_property_descriptor * * @return ECMA_VALUE_ERROR - if the Proxy.[[GetOwnProperty]] operation raises error * ECMA_VALUE_{TRUE, FALSE} - if property found or not */ ecma_value_t ecma_op_get_own_property_descriptor (ecma_value_t target, /**< target value */ ecma_string_t *property_name_p, /**< property name */ ecma_property_descriptor_t *prop_desc_p) /**< property descriptor */ { if (!ecma_is_value_object (target)) { return ECMA_VALUE_FALSE; } return ecma_op_object_get_own_property_descriptor (ecma_get_object_from_value (target), property_name_p, prop_desc_p); } /* ecma_op_get_own_property_descriptor */ #endif /* JERRY_BUILTIN_PROXY */ /** * [[HasInstance]] ecma object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 9 * * @return ecma value containing a boolean value or an error * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_object_has_instance (ecma_object_t *obj_p, /**< the object */ ecma_value_t value) /**< argument 'V' */ { JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); JERRY_ASSERT_OBJECT_TYPE_IS_VALID (ecma_get_object_type (obj_p)); if (ecma_op_object_is_callable (obj_p)) { return ecma_op_function_has_instance (obj_p, value); } return ecma_raise_type_error (ECMA_ERR_EXPECTED_A_FUNCTION_OBJECT); } /* ecma_op_object_has_instance */ /** * General [[GetPrototypeOf]] abstract operation * * Note: returned valid object must be freed. * * @return ecma_object_t * - prototype of the input object. * ECMA_OBJECT_POINTER_ERROR - error reported during Proxy resolve. * NULL - the input object does not have a prototype. */ ecma_object_t * ecma_op_object_get_prototype_of (ecma_object_t *obj_p) /**< input object */ { JERRY_ASSERT (obj_p != NULL); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { ecma_value_t proto = ecma_proxy_object_get_prototype_of (obj_p); if (ECMA_IS_VALUE_ERROR (proto)) { return ECMA_OBJECT_POINTER_ERROR; } if (ecma_is_value_null (proto)) { return NULL; } JERRY_ASSERT (ecma_is_value_object (proto)); return ecma_get_object_from_value (proto); } else #endif /* JERRY_BUILTIN_PROXY */ { jmem_cpointer_t proto_cp = ecma_op_ordinary_object_get_prototype_of (obj_p); if (proto_cp == JMEM_CP_NULL) { return NULL; } ecma_object_t *proto_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp); ecma_ref_object (proto_p); return proto_p; } } /* ecma_op_object_get_prototype_of */ /** * Object's isPrototypeOf operation * * See also: * ECMA-262 v5, 15.2.4.6; 3 * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_TRUE - if the target object is prototype of the base object * ECMA_VALUE_FALSE - if the target object is not prototype of the base object */ ecma_value_t ecma_op_object_is_prototype_of (ecma_object_t *base_p, /**< base object */ ecma_object_t *target_p) /**< target object */ { ecma_ref_object (target_p); do { ecma_object_t *proto_p = ecma_op_object_get_prototype_of (target_p); ecma_deref_object (target_p); if (proto_p == NULL) { return ECMA_VALUE_FALSE; } else if (proto_p == ECMA_OBJECT_POINTER_ERROR) { return ECMA_VALUE_ERROR; } else if (proto_p == base_p) { ecma_deref_object (proto_p); return ECMA_VALUE_TRUE; } /* Advance up on prototype chain. */ target_p = proto_p; } while (true); } /* ecma_op_object_is_prototype_of */ /** * Object's EnumerableOwnPropertyNames operation * * See also: * ECMA-262 v11, 7.3.23 * * @return NULL - if operation fails * collection of property names / values / name-value pairs - otherwise */ ecma_collection_t * ecma_op_object_get_enumerable_property_names (ecma_object_t *obj_p, /**< routine's first argument */ ecma_enumerable_property_names_options_t option) /**< listing option */ { /* 2. */ ecma_collection_t *prop_names_p = ecma_op_object_own_property_keys (obj_p, JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS); #if JERRY_BUILTIN_PROXY if (JERRY_UNLIKELY (prop_names_p == NULL)) { return prop_names_p; } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t *names_buffer_p = prop_names_p->buffer_p; /* 3. */ ecma_collection_t *properties_p = ecma_new_collection (); /* 4. */ for (uint32_t i = 0; i < prop_names_p->item_count; i++) { /* 4.a */ if (ecma_is_value_string (names_buffer_p[i])) { ecma_string_t *key_p = ecma_get_string_from_value (names_buffer_p[i]); /* 4.a.i */ ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_p, key_p, &prop_desc); if (ECMA_IS_VALUE_ERROR (status)) { ecma_collection_free (prop_names_p); ecma_collection_free (properties_p); return NULL; } const bool is_enumerable = (prop_desc.flags & JERRY_PROP_IS_ENUMERABLE) != 0; ecma_free_property_descriptor (&prop_desc); /* 4.a.ii */ if (is_enumerable) { /* 4.a.ii.1 */ if (option == ECMA_ENUMERABLE_PROPERTY_KEYS) { ecma_collection_push_back (properties_p, ecma_copy_value (names_buffer_p[i])); } else { /* 4.a.ii.2.a */ ecma_value_t value = ecma_op_object_get (obj_p, key_p); if (ECMA_IS_VALUE_ERROR (value)) { ecma_collection_free (prop_names_p); ecma_collection_free (properties_p); return NULL; } /* 4.a.ii.2.b */ if (option == ECMA_ENUMERABLE_PROPERTY_VALUES) { ecma_collection_push_back (properties_p, value); } else { /* 4.a.ii.2.c.i */ JERRY_ASSERT (option == ECMA_ENUMERABLE_PROPERTY_ENTRIES); /* 4.a.ii.2.c.ii */ ecma_object_t *entry_p = ecma_op_new_array_object (2); ecma_builtin_helper_def_prop_by_index (entry_p, 0, names_buffer_p[i], ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_builtin_helper_def_prop_by_index (entry_p, 1, value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_free_value (value); /* 4.a.ii.2.c.iii */ ecma_collection_push_back (properties_p, ecma_make_object_value (entry_p)); } } } } } ecma_collection_free (prop_names_p); return properties_p; } /* ecma_op_object_get_enumerable_property_names */ /** * Helper method for getting lazy instantiated properties for [[OwnPropertyKeys]] */ static void ecma_object_list_lazy_property_names (ecma_object_t *obj_p, /**< object */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< property name filter options */ { switch (ecma_get_object_type (obj_p)) { case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { if (ecma_builtin_function_is_routine (obj_p)) { ecma_builtin_routine_list_lazy_property_names (obj_p, prop_names_p, prop_counter_p, filter); break; } /* FALLTHRU */ } case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { ecma_builtin_list_lazy_property_names (obj_p, prop_names_p, prop_counter_p, filter); break; } case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_STRING: { ecma_op_string_list_lazy_property_names (obj_p, prop_names_p, prop_counter_p, filter); break; } case ECMA_OBJECT_CLASS_ARGUMENTS: { ecma_op_arguments_object_list_lazy_property_names (obj_p, prop_names_p, prop_counter_p, filter); break; } #if JERRY_BUILTIN_TYPEDARRAY /* ES2015 9.4.5.1 */ case ECMA_OBJECT_CLASS_TYPEDARRAY: { ecma_op_typedarray_list_lazy_property_names (obj_p, prop_names_p, prop_counter_p, filter); break; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ } break; } case ECMA_OBJECT_TYPE_FUNCTION: { ecma_op_function_list_lazy_property_names (obj_p, prop_names_p, prop_counter_p, filter); break; } case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { ecma_op_external_function_list_lazy_property_names (obj_p, prop_names_p, prop_counter_p, filter); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { ecma_op_bound_function_list_lazy_property_names (obj_p, prop_names_p, prop_counter_p, filter); break; } case ECMA_OBJECT_TYPE_ARRAY: { if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS)) { ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); prop_counter_p->string_named_props++; } break; } default: { JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_GENERAL || ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION); break; } } } /* ecma_object_list_lazy_property_names */ /** * Helper routine for heapsort algorithm. */ static void ecma_op_object_heap_sort_shift_down (ecma_value_t *buffer_p, /**< array of items */ uint32_t item_count, /**< number of items */ uint32_t item_index) /**< index of updated item */ { while (true) { uint32_t highest_index = item_index; uint32_t current_index = (item_index << 1) + 1; if (current_index >= item_count) { return; } uint32_t value = ecma_string_get_array_index (ecma_get_string_from_value (buffer_p[highest_index])); uint32_t left_value = ecma_string_get_array_index (ecma_get_string_from_value (buffer_p[current_index])); if (value < left_value) { highest_index = current_index; value = left_value; } current_index++; if (current_index < item_count && value < ecma_string_get_array_index (ecma_get_string_from_value (buffer_p[current_index]))) { highest_index = current_index; } if (highest_index == item_index) { return; } ecma_value_t tmp = buffer_p[highest_index]; buffer_p[highest_index] = buffer_p[item_index]; buffer_p[item_index] = tmp; item_index = highest_index; } } /* ecma_op_object_heap_sort_shift_down */ /** * Object's [[OwnPropertyKeys]] internal method * * Order of names in the collection: * - integer indices in ascending order * - other indices in creation order (for built-ins: the order of the properties are listed in specification). * * Note: * Implementation of the routine assumes that new properties are appended to beginning of corresponding object's * property list, and the list is not reordered (in other words, properties are stored in order that is reversed * to the properties' addition order). * * @return NULL - if the Proxy.[[OwnPropertyKeys]] operation raises error * collection of property names - otherwise */ ecma_collection_t * ecma_op_object_own_property_keys (ecma_object_t *obj_p, /**< object */ jerry_property_filter_t filter) /**< name filters */ { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { return ecma_proxy_object_own_property_keys (obj_p); } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_op_object_is_fast_array (obj_p)) { return ecma_fast_array_object_own_property_keys (obj_p, filter); } ecma_collection_t *prop_names_p = ecma_new_collection (); ecma_property_counter_t prop_counter = { 0, 0, 0 }; ecma_object_list_lazy_property_names (obj_p, prop_names_p, &prop_counter, filter); jmem_cpointer_t prop_iter_cp = obj_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prop_iter_cp = prop_iter_p->next_property_cp; } } #endif /* JERRY_PROPERTY_HASHMAP */ jmem_cpointer_t counter_prop_iter_cp = prop_iter_cp; uint32_t array_index_named_props = 0; uint32_t string_named_props = 0; uint32_t symbol_named_props = 0; while (counter_prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, counter_prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { ecma_property_t *property_p = prop_iter_p->types + i; if (!ECMA_PROPERTY_IS_RAW (*property_p) || (*property_p & ECMA_PROPERTY_FLAG_BUILT_IN)) { continue; } ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_MAGIC && prop_pair_p->names_cp[i] >= LIT_NON_INTERNAL_MAGIC_STRING__COUNT && prop_pair_p->names_cp[i] < LIT_MAGIC_STRING__COUNT) { continue; } ecma_string_t *name_p = ecma_string_from_property_name (*property_p, prop_pair_p->names_cp[i]); if (ecma_string_get_array_index (name_p) != ECMA_STRING_NOT_ARRAY_INDEX) { array_index_named_props++; } else if (ecma_prop_name_is_symbol (name_p)) { if (!(name_p->u.hash & ECMA_SYMBOL_FLAG_PRIVATE_KEY)) { symbol_named_props++; } } else { string_named_props++; } ecma_deref_ecma_string (name_p); } counter_prop_iter_cp = prop_iter_p->next_property_cp; } if (filter & JERRY_PROPERTY_FILTER_EXCLUDE_INTEGER_INDICES) { JERRY_ASSERT (prop_counter.array_index_named_props == 0); array_index_named_props = 0; } if (filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS) { JERRY_ASSERT (prop_counter.string_named_props == 0); string_named_props = 0; } if (filter & JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS) { JERRY_ASSERT (prop_counter.symbol_named_props == 0); symbol_named_props = 0; } uint32_t total = array_index_named_props + string_named_props + symbol_named_props; if (total == 0) { return prop_names_p; } ecma_collection_reserve (prop_names_p, total); prop_names_p->item_count += total; ecma_value_t *buffer_p = prop_names_p->buffer_p; ecma_value_t *array_index_current_p = buffer_p + array_index_named_props + prop_counter.array_index_named_props; ecma_value_t *string_current_p = array_index_current_p + string_named_props + prop_counter.string_named_props; ecma_value_t *symbol_current_p = string_current_p + symbol_named_props + prop_counter.symbol_named_props; if (prop_counter.symbol_named_props > 0 && (array_index_named_props + string_named_props) > 0) { memmove ((void *) string_current_p, (void *) (buffer_p + prop_counter.array_index_named_props + prop_counter.string_named_props), prop_counter.symbol_named_props * sizeof (ecma_value_t)); } if (prop_counter.string_named_props > 0 && array_index_named_props > 0) { memmove ((void *) array_index_current_p, (void *) (buffer_p + prop_counter.array_index_named_props), prop_counter.string_named_props * sizeof (ecma_value_t)); } while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { ecma_property_t *property_p = prop_iter_p->types + i; if (!ECMA_PROPERTY_IS_RAW (*property_p) || (*property_p & ECMA_PROPERTY_FLAG_BUILT_IN)) { continue; } ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_MAGIC && prop_pair_p->names_cp[i] >= LIT_NON_INTERNAL_MAGIC_STRING__COUNT && prop_pair_p->names_cp[i] < LIT_MAGIC_STRING__COUNT) { continue; } ecma_string_t *name_p = ecma_string_from_property_name (*property_p, prop_pair_p->names_cp[i]); if (ecma_string_get_array_index (name_p) != ECMA_STRING_NOT_ARRAY_INDEX) { if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_INTEGER_INDICES)) { *(--array_index_current_p) = ecma_make_string_value (name_p); continue; } } else if (ecma_prop_name_is_symbol (name_p)) { if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS) && !(name_p->u.hash & ECMA_SYMBOL_FLAG_PRIVATE_KEY)) { *(--symbol_current_p) = ecma_make_symbol_value (name_p); continue; } } else { if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS)) { *(--string_current_p) = ecma_make_string_value (name_p); continue; } } ecma_deref_ecma_string (name_p); } prop_iter_cp = prop_iter_p->next_property_cp; } if (array_index_named_props > 1 || (array_index_named_props == 1 && prop_counter.array_index_named_props > 0)) { uint32_t prev_value = 0; ecma_value_t *array_index_p = buffer_p + prop_counter.array_index_named_props; ecma_value_t *array_index_end_p = array_index_p + array_index_named_props; if (prop_counter.array_index_named_props > 0) { prev_value = ecma_string_get_array_index (ecma_get_string_from_value (array_index_p[-1])); } do { uint32_t value = ecma_string_get_array_index (ecma_get_string_from_value (*array_index_p++)); if (value < prev_value) { uint32_t array_props = prop_counter.array_index_named_props + array_index_named_props; uint32_t i = (array_props >> 1) - 1; do { ecma_op_object_heap_sort_shift_down (buffer_p, array_props, i); } while (i-- > 0); i = array_props - 1; do { ecma_value_t tmp = buffer_p[i]; buffer_p[i] = buffer_p[0]; buffer_p[0] = tmp; ecma_op_object_heap_sort_shift_down (buffer_p, i, 0); } while (--i > 0); break; } prev_value = value; } while (array_index_p < array_index_end_p); } return prop_names_p; } /* ecma_op_object_own_property_keys */ /** * EnumerateObjectProperties abstract method * * See also: * ECMA-262 v11, 13.7.5.15 * * @return NULL - if the Proxy.[[OwnPropertyKeys]] operation raises error * collection of enumerable property names - otherwise */ ecma_collection_t * ecma_op_object_enumerate (ecma_object_t *obj_p) /**< object */ { ecma_collection_t *visited_names_p = ecma_new_collection (); ecma_collection_t *return_names_p = ecma_new_collection (); ecma_ref_object (obj_p); while (true) { ecma_collection_t *keys = ecma_op_object_own_property_keys (obj_p, JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS); if (JERRY_UNLIKELY (keys == NULL)) { ecma_collection_free (return_names_p); ecma_collection_free (visited_names_p); ecma_deref_object (obj_p); return keys; } for (uint32_t i = 0; i < keys->item_count; i++) { ecma_value_t prop_name = keys->buffer_p[i]; ecma_string_t *name_p = ecma_get_prop_name_from_value (prop_name); if (ecma_prop_name_is_symbol (name_p)) { continue; } ecma_property_descriptor_t prop_desc; ecma_value_t get_desc = ecma_op_object_get_own_property_descriptor (obj_p, name_p, &prop_desc); if (ECMA_IS_VALUE_ERROR (get_desc)) { ecma_collection_free (keys); ecma_collection_free (return_names_p); ecma_collection_free (visited_names_p); ecma_deref_object (obj_p); return NULL; } if (ecma_is_value_true (get_desc)) { bool is_enumerable = (prop_desc.flags & JERRY_PROP_IS_ENUMERABLE) != 0; ecma_free_property_descriptor (&prop_desc); if (ecma_collection_has_string_value (visited_names_p, name_p) || ecma_collection_has_string_value (return_names_p, name_p)) { continue; } ecma_ref_ecma_string (name_p); if (is_enumerable) { ecma_collection_push_back (return_names_p, prop_name); } else { ecma_collection_push_back (visited_names_p, prop_name); } } } ecma_collection_free (keys); /* Query the prototype. */ ecma_object_t *proto_p = ecma_op_object_get_prototype_of (obj_p); ecma_deref_object (obj_p); if (proto_p == NULL) { break; } else if (JERRY_UNLIKELY (proto_p == ECMA_OBJECT_POINTER_ERROR)) { ecma_collection_free (return_names_p); ecma_collection_free (visited_names_p); return NULL; } /* Advance up on prototype chain. */ obj_p = proto_p; } ecma_collection_free (visited_names_p); return return_names_p; } /* ecma_op_object_enumerate */ #ifndef JERRY_NDEBUG /** * Check if passed object is the instance of specified built-in. * * @return true - if the object is instance of the specified built-in * false - otherwise */ static bool ecma_builtin_is (ecma_object_t *object_p, /**< pointer to an object */ ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ { JERRY_ASSERT (object_p != NULL && !ecma_is_lexical_environment (object_p)); JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); ecma_object_type_t type = ecma_get_object_type (object_p); switch (type) { case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { ecma_extended_object_t *built_in_object_p = (ecma_extended_object_t *) object_p; return (built_in_object_p->u.built_in.id == builtin_id && built_in_object_p->u.built_in.routine_id == 0); } case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { ecma_extended_built_in_object_t *extended_built_in_object_p = (ecma_extended_built_in_object_t *) object_p; return (extended_built_in_object_p->built_in.id == builtin_id && extended_built_in_object_p->built_in.routine_id == 0); } default: { return false; } } } /* ecma_builtin_is */ #endif /* !JERRY_NDEBUG */ /** * Used by ecma_object_get_class_name to get the magic string id of class objects */ static const uint16_t ecma_class_object_magic_string_id[] = { /* These objects require custom property resolving. */ LIT_MAGIC_STRING_STRING_UL, /**< magic string id of ECMA_OBJECT_CLASS_STRING */ LIT_MAGIC_STRING_ARGUMENTS_UL, /**< magic string id of ECMA_OBJECT_CLASS_ARGUMENTS */ #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING__EMPTY, /**< ECMA_OBJECT_CLASS_TYPEDARRAY needs special resolver */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM LIT_MAGIC_STRING_MODULE_UL, /**< magic string id of ECMA_OBJECT_CLASS_MODULE_NAMESPACE */ #endif /* JERRY_MODULE_SYSTEM */ /* These objects are marked by Garbage Collector. */ LIT_MAGIC_STRING_GENERATOR_UL, /**< magic string id of ECMA_OBJECT_CLASS_GENERATOR */ LIT_MAGIC_STRING_ASYNC_GENERATOR_UL, /**< magic string id of ECMA_OBJECT_CLASS_ASYNC_GENERATOR */ LIT_MAGIC_STRING_ARRAY_ITERATOR_UL, /**< magic string id of ECMA_OBJECT_CLASS_ARRAY_ITERATOR */ LIT_MAGIC_STRING_SET_ITERATOR_UL, /**< magic string id of ECMA_OBJECT_CLASS_SET_ITERATOR */ LIT_MAGIC_STRING_MAP_ITERATOR_UL, /**< magic string id of ECMA_OBJECT_CLASS_MAP_ITERATOR */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL, /**< magic string id of ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR */ #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_MODULE_SYSTEM LIT_MAGIC_STRING_MODULE_UL, /**< magic string id of ECMA_OBJECT_CLASS_MODULE */ #endif /* JERRY_MODULE_SYSTEM */ LIT_MAGIC_STRING_PROMISE_UL, /**< magic string id of ECMA_OBJECT_CLASS_PROMISE */ LIT_MAGIC_STRING_OBJECT_UL, /**< magic string id of ECMA_OBJECT_CLASS_PROMISE_CAPABILITY */ LIT_MAGIC_STRING_OBJECT_UL, /**< magic string id of ECMA_OBJECT_CLASS_ASYNC_FROM_SYNC_ITERATOR */ #if JERRY_BUILTIN_DATAVIEW LIT_MAGIC_STRING_DATAVIEW_UL, /**< magic string id of ECMA_OBJECT_CLASS_DATAVIEW */ #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_CONTAINER LIT_MAGIC_STRING__EMPTY, /**< magic string id of ECMA_OBJECT_CLASS_CONTAINER needs special resolver */ #endif /* JERRY_BUILTIN_CONTAINER */ /* Normal objects. */ LIT_MAGIC_STRING_BOOLEAN_UL, /**< magic string id of ECMA_OBJECT_CLASS_BOOLEAN */ LIT_MAGIC_STRING_NUMBER_UL, /**< magic string id of ECMA_OBJECT_CLASS_NUMBER */ LIT_MAGIC_STRING_ERROR_UL, /**< magic string id of ECMA_OBJECT_CLASS_ERROR */ LIT_MAGIC_STRING_OBJECT_UL, /**< magic string id of ECMA_OBJECT_CLASS_INTERNAL_OBJECT */ #if JERRY_PARSER LIT_MAGIC_STRING_SCRIPT_UL, /**< magic string id of ECMA_OBJECT_CLASS_SCRIPT */ #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_DATE LIT_MAGIC_STRING_DATE_UL, /**< magic string id of ECMA_OBJECT_CLASS_DATE */ #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_REGEXP_UL, /**< magic string id of ECMA_OBJECT_CLASS_REGEXP */ #endif /* JERRY_BUILTIN_REGEXP */ LIT_MAGIC_STRING_SYMBOL_UL, /**< magic string id of ECMA_OBJECT_CLASS_SYMBOL */ LIT_MAGIC_STRING_STRING_ITERATOR_UL, /**< magic string id of ECMA_OBJECT_CLASS_STRING_ITERATOR */ #if JERRY_BUILTIN_TYPEDARRAY LIT_MAGIC_STRING_ARRAY_BUFFER_UL, /**< magic string id of ECMA_OBJECT_CLASS_ARRAY_BUFFER */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER LIT_MAGIC_STRING_SHARED_ARRAY_BUFFER_UL, /**< magic string id of ECMA_OBJECT_CLASS_SHAREDARRAY_BUFFER */ #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ #if JERRY_BUILTIN_BIGINT LIT_MAGIC_STRING_BIGINT_UL, /**< magic string id of ECMA_OBJECT_CLASS_BIGINT */ #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_WEAKREF LIT_MAGIC_STRING_WEAKREF_UL, /**< magic string id of ECMA_OBJECT_CLASS_WEAKREF */ #endif /* JERRY_BUILTIN_WEAKREF */ }; JERRY_STATIC_ASSERT ((sizeof (ecma_class_object_magic_string_id) == ECMA_OBJECT_CLASS__MAX * sizeof (uint16_t)), ecma_class_object_magic_string_id_must_have_object_class_max_elements); /** * Get [[Class]] string of specified object * * @return class name magic string */ lit_magic_string_id_t ecma_object_get_class_name (ecma_object_t *obj_p) /**< object */ { ecma_object_type_t type = ecma_get_object_type (obj_p); switch (type) { case ECMA_OBJECT_TYPE_ARRAY: case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { return LIT_MAGIC_STRING_ARRAY_UL; } case ECMA_OBJECT_TYPE_CLASS: case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; switch (ext_object_p->u.cls.type) { #if JERRY_BUILTIN_TYPEDARRAY case ECMA_OBJECT_CLASS_TYPEDARRAY: { return ecma_get_typedarray_magic_string_id ((ecma_typedarray_type_t) ext_object_p->u.cls.u1.typedarray_type); } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_CONTAINER case ECMA_OBJECT_CLASS_CONTAINER: { return (lit_magic_string_id_t) ext_object_p->u.cls.u2.container_id; } #endif /* JERRY_BUILTIN_CONTAINER */ default: { break; } } JERRY_ASSERT (ext_object_p->u.cls.type < ECMA_OBJECT_CLASS__MAX); JERRY_ASSERT (ecma_class_object_magic_string_id[ext_object_p->u.cls.type] != LIT_MAGIC_STRING__EMPTY); return (lit_magic_string_id_t) ecma_class_object_magic_string_id[ext_object_p->u.cls.type]; } case ECMA_OBJECT_TYPE_FUNCTION: case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: case ECMA_OBJECT_TYPE_BOUND_FUNCTION: case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION: { return LIT_MAGIC_STRING_FUNCTION_UL; } #if JERRY_BUILTIN_PROXY case ECMA_OBJECT_TYPE_PROXY: { ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; if (!ecma_is_value_null (proxy_obj_p->target) && ecma_is_value_object (proxy_obj_p->target)) { ecma_object_t *target_obj_p = ecma_get_object_from_value (proxy_obj_p->target); return ecma_object_get_class_name (target_obj_p); } return LIT_MAGIC_STRING_OBJECT_UL; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; switch (ext_obj_p->u.built_in.id) { #if JERRY_BUILTIN_MATH case ECMA_BUILTIN_ID_MATH: { return LIT_MAGIC_STRING_MATH_UL; } #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_REFLECT case ECMA_BUILTIN_ID_REFLECT: { return LIT_MAGIC_STRING_REFLECT_UL; } #endif /* JERRY_BUILTIN_REFLECT */ case ECMA_BUILTIN_ID_GENERATOR: { return LIT_MAGIC_STRING_GENERATOR_UL; } case ECMA_BUILTIN_ID_ASYNC_GENERATOR: { return LIT_MAGIC_STRING_ASYNC_GENERATOR_UL; } #if JERRY_BUILTIN_ATOMICS case ECMA_BUILTIN_ID_ATOMICS: { return LIT_MAGIC_STRING_ATOMICS_U; } #endif /* JERRY_BUILTIN_ATOMICS */ default: { break; } } return LIT_MAGIC_STRING_OBJECT_UL; } default: { JERRY_ASSERT (type == ECMA_OBJECT_TYPE_GENERAL || type == ECMA_OBJECT_TYPE_PROXY); return LIT_MAGIC_STRING_OBJECT_UL; } } } /* ecma_object_get_class_name */ #if JERRY_BUILTIN_REGEXP /** * Checks if the given argument has [[RegExpMatcher]] internal slot * * @return true - if the given argument is a regexp * false - otherwise */ bool ecma_object_is_regexp_object (ecma_value_t arg) /**< argument */ { return (ecma_is_value_object (arg) && ecma_object_class_is (ecma_get_object_from_value (arg), ECMA_OBJECT_CLASS_REGEXP)); } /* ecma_object_is_regexp_object */ #endif /* JERRY_BUILTIN_REGEXP */ /** * Object's IsConcatSpreadable operation, used for Array.prototype.concat * It checks the argument's [Symbol.isConcatSpreadable] property value * * See also: * ECMA-262 v6, 22.1.3.1.1; * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_TRUE - if the argument is concatSpreadable * ECMA_VALUE_FALSE - otherwise */ ecma_value_t ecma_op_is_concat_spreadable (ecma_value_t arg) /**< argument */ { if (!ecma_is_value_object (arg)) { return ECMA_VALUE_FALSE; } ecma_value_t spreadable = ecma_op_object_get_by_symbol_id (ecma_get_object_from_value (arg), LIT_GLOBAL_SYMBOL_IS_CONCAT_SPREADABLE); if (ECMA_IS_VALUE_ERROR (spreadable)) { return spreadable; } if (!ecma_is_value_undefined (spreadable)) { const bool to_bool = ecma_op_to_boolean (spreadable); ecma_free_value (spreadable); return ecma_make_boolean_value (to_bool); } return ecma_is_value_array (arg); } /* ecma_op_is_concat_spreadable */ /** * IsRegExp operation * * See also: * ECMA-262 v6, 22.1.3.1.1; * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_TRUE - if the argument is regexp * ECMA_VALUE_FALSE - otherwise */ ecma_value_t ecma_op_is_regexp (ecma_value_t arg) /**< argument */ { #if JERRY_BUILTIN_REGEXP if (!ecma_is_value_object (arg)) { return ECMA_VALUE_FALSE; } ecma_value_t is_regexp = ecma_op_object_get_by_symbol_id (ecma_get_object_from_value (arg), LIT_GLOBAL_SYMBOL_MATCH); if (ECMA_IS_VALUE_ERROR (is_regexp)) { return is_regexp; } if (!ecma_is_value_undefined (is_regexp)) { const bool to_bool = ecma_op_to_boolean (is_regexp); ecma_free_value (is_regexp); return ecma_make_boolean_value (to_bool); } return ecma_make_boolean_value (ecma_object_is_regexp_object (arg)); #else return ECMA_VALUE_FALSE; #endif } /* ecma_op_is_regexp */ /** * SpeciesConstructor operation * See also: * ECMA-262 v6, 7.3.20; * * @return ecma_value * returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_species_constructor (ecma_object_t *this_value, /**< This Value */ ecma_builtin_id_t default_constructor_id) /**< Builtin ID of default constructor */ { ecma_object_t *default_constructor_p = ecma_builtin_get (default_constructor_id); ecma_value_t constructor = ecma_op_object_get_by_magic_id (this_value, LIT_MAGIC_STRING_CONSTRUCTOR); if (ECMA_IS_VALUE_ERROR (constructor)) { return constructor; } if (ecma_is_value_undefined (constructor)) { ecma_ref_object (default_constructor_p); return ecma_make_object_value (default_constructor_p); } if (!ecma_is_value_object (constructor)) { ecma_free_value (constructor); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_NOT_AN_OBJECT); } ecma_object_t *ctor_object_p = ecma_get_object_from_value (constructor); ecma_value_t species = ecma_op_object_get_by_symbol_id (ctor_object_p, LIT_GLOBAL_SYMBOL_SPECIES); ecma_deref_object (ctor_object_p); if (ECMA_IS_VALUE_ERROR (species)) { return species; } if (ecma_is_value_undefined (species) || ecma_is_value_null (species)) { ecma_ref_object (default_constructor_p); return ecma_make_object_value (default_constructor_p); } if (!ecma_is_constructor (species)) { ecma_free_value (species); return ecma_raise_type_error (ECMA_ERR_SPECIES_MUST_BE_A_CONSTRUCTOR); } return species; } /* ecma_op_species_constructor */ /** * 7.3.18 Abstract operation Invoke when property name is a magic string * * @return ecma_value result of the invoked function or raised error * note: returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_invoke_by_symbol_id (ecma_value_t object, /**< Object value */ lit_magic_string_id_t symbol_id, /**< Symbol ID */ ecma_value_t *args_p, /**< Argument list */ uint32_t args_len) /**< Argument list length */ { ecma_string_t *symbol_p = ecma_op_get_global_symbol (symbol_id); ecma_value_t ret_value = ecma_op_invoke (object, symbol_p, args_p, args_len); ecma_deref_ecma_string (symbol_p); return ret_value; } /* ecma_op_invoke_by_symbol_id */ /** * 7.3.18 Abstract operation Invoke when property name is a magic string * * @return ecma_value result of the invoked function or raised error * note: returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_invoke_by_magic_id (ecma_value_t object, /**< Object value */ lit_magic_string_id_t magic_string_id, /**< Magic string ID */ ecma_value_t *args_p, /**< Argument list */ uint32_t args_len) /**< Argument list length */ { return ecma_op_invoke (object, ecma_get_magic_string (magic_string_id), args_p, args_len); } /* ecma_op_invoke_by_magic_id */ /** * 7.3.18 Abstract operation Invoke * * @return ecma_value result of the invoked function or raised error * note: returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_invoke (ecma_value_t object, /**< Object value */ ecma_string_t *property_name_p, /**< Property name */ ecma_value_t *args_p, /**< Argument list */ uint32_t args_len) /**< Argument list length */ { /* 3. */ ecma_value_t object_value = ecma_op_to_object (object); if (ECMA_IS_VALUE_ERROR (object_value)) { return object_value; } ecma_object_t *object_p = ecma_get_object_from_value (object_value); ecma_value_t func = ecma_op_object_get_with_receiver (object_p, property_name_p, object); if (ECMA_IS_VALUE_ERROR (func)) { ecma_deref_object (object_p); return func; } /* 4. */ ecma_value_t call_result = ecma_op_function_validated_call (func, object, args_p, args_len); ecma_free_value (func); ecma_deref_object (object_p); return call_result; } /* ecma_op_invoke */ /** * Ordinary object [[GetPrototypeOf]] operation * * See also: * ECMAScript v6, 9.1.1 * * @return the value of the [[Prototype]] internal slot of the given object. */ jmem_cpointer_t ecma_op_ordinary_object_get_prototype_of (ecma_object_t *obj_p) /**< object */ { JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); JERRY_ASSERT (!ECMA_OBJECT_IS_PROXY (obj_p)); return obj_p->u2.prototype_cp; } /* ecma_op_ordinary_object_get_prototype_of */ /** * Ordinary object [[SetPrototypeOf]] operation * * See also: * ECMAScript v6, 9.1.2 * * @return ECMA_VALUE_FALSE - if the operation fails * ECMA_VALUE_TRUE - otherwise */ ecma_value_t ecma_op_ordinary_object_set_prototype_of (ecma_object_t *obj_p, /**< base object */ ecma_value_t proto) /**< prototype object */ { JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); JERRY_ASSERT (!ECMA_OBJECT_IS_PROXY (obj_p)); /* 1. */ JERRY_ASSERT (ecma_is_value_object (proto) || ecma_is_value_null (proto)); /* 3. */ ecma_object_t *current_proto_p = ECMA_GET_POINTER (ecma_object_t, ecma_op_ordinary_object_get_prototype_of (obj_p)); ecma_object_t *new_proto_p = ecma_is_value_null (proto) ? NULL : ecma_get_object_from_value (proto); /* 4. */ if (new_proto_p == current_proto_p) { return ECMA_VALUE_TRUE; } /* 2 - 5. */ if (!ecma_op_ordinary_object_is_extensible (obj_p)) { return ECMA_VALUE_FALSE; } /** * When the prototype of a fast array changes, it is required to convert the * array to a "normal" array. This ensures that all [[Get]]/[[Set]]/etc. * calls works as expected. */ if (ecma_op_object_is_fast_array (obj_p)) { ecma_fast_array_convert_to_normal (obj_p); } /* 6. */ ecma_object_t *iter_p = new_proto_p; /* 7 - 8. */ while (true) { /* 8.a */ if (iter_p == NULL) { break; } /* 8.b */ if (obj_p == iter_p) { return ECMA_VALUE_FALSE; } /* 8.c.i */ #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (iter_p)) { /** * Prevent setting 'Object.prototype.__proto__' * to avoid circular referencing in the prototype chain. */ if (obj_p == ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE)) { return ECMA_VALUE_FALSE; } break; } #endif /* JERRY_BUILTIN_PROXY */ /* 8.c.ii */ iter_p = ECMA_GET_POINTER (ecma_object_t, ecma_op_ordinary_object_get_prototype_of (iter_p)); } /* 9. */ ECMA_SET_POINTER (obj_p->u2.prototype_cp, new_proto_p); /* 10. */ return ECMA_VALUE_TRUE; } /* ecma_op_ordinary_object_set_prototype_of */ /** * [[IsExtensible]] operation for Ordinary object. * * See also: * ECMAScript v6, 9.1.2 * * @return true - if object is extensible * false - otherwise */ bool JERRY_ATTR_PURE ecma_op_ordinary_object_is_extensible (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (!ECMA_OBJECT_IS_PROXY (object_p)); bool is_extensible = (object_p->type_flags_refs & ECMA_OBJECT_FLAG_EXTENSIBLE) != 0; JERRY_ASSERT (!ecma_op_object_is_fast_array (object_p) || is_extensible); return is_extensible; } /* ecma_op_ordinary_object_is_extensible */ /** * Set value of [[Extensible]] object's internal property. * * @return void */ void JERRY_ATTR_NOINLINE ecma_op_ordinary_object_prevent_extensions (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (!ECMA_OBJECT_IS_PROXY (object_p)); if (JERRY_UNLIKELY (ecma_op_object_is_fast_array (object_p))) { ecma_fast_array_convert_to_normal (object_p); } object_p->type_flags_refs &= (ecma_object_descriptor_t) ~ECMA_OBJECT_FLAG_EXTENSIBLE; } /* ecma_op_ordinary_object_prevent_extensions */ /** * Checks whether an object (excluding prototypes) has a named property * * @return true - if property is found * false - otherwise */ ecma_value_t ecma_op_ordinary_object_has_own_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p) /**< property name */ { JERRY_ASSERT (!ECMA_OBJECT_IS_PROXY (object_p)); ecma_property_t property = ecma_op_object_get_own_property (object_p, property_name_p, NULL, ECMA_PROPERTY_GET_NO_OPTIONS); #if JERRY_BUILTIN_TYPEDARRAY if (JERRY_UNLIKELY (property == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_THROW)) { return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ JERRY_ASSERT (ECMA_PROPERTY_IS_FOUND (property) || property == ECMA_PROPERTY_TYPE_NOT_FOUND || property == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP); return ecma_make_boolean_value (ECMA_PROPERTY_IS_FOUND (property)); } /* ecma_op_ordinary_object_has_own_property */ /** * Checks whether an object (excluding prototypes) has a named property. Handles proxy objects too. * * @return true - if property is found * false - otherwise */ ecma_value_t ecma_op_object_has_own_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p) /**< property name */ { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (object_p)) { ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_proxy_object_get_own_property_descriptor (object_p, property_name_p, &prop_desc); if (ecma_is_value_true (status)) { ecma_free_property_descriptor (&prop_desc); } return status; } #endif /* JERRY_BUILTIN_PROXY */ return ecma_op_ordinary_object_has_own_property (object_p, property_name_p); } /* ecma_op_object_has_own_property */ #if JERRY_BUILTIN_WEAKREF || JERRY_BUILTIN_CONTAINER /** * Set a weak reference from a container or WeakRefObject to a key object */ void ecma_op_object_set_weak (ecma_object_t *object_p, /**< key object */ ecma_object_t *target_p) /**< target object */ { if (JERRY_UNLIKELY (ecma_op_object_is_fast_array (object_p))) { ecma_fast_array_convert_to_normal (object_p); } ecma_string_t *weak_refs_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_WEAK_REFS); ecma_property_t *property_p = ecma_find_named_property (object_p, weak_refs_string_p); ecma_collection_t *refs_p; if (property_p == NULL) { refs_p = ecma_new_collection (); ecma_property_value_t *value_p; ECMA_CREATE_INTERNAL_PROPERTY (object_p, weak_refs_string_p, property_p, value_p); ECMA_SET_INTERNAL_VALUE_POINTER (value_p->value, refs_p); } else { refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, (ECMA_PROPERTY_VALUE_PTR (property_p)->value)); } const ecma_value_t target_value = ecma_make_object_value ((ecma_object_t *) target_p); for (uint32_t i = 0; i < refs_p->item_count; i++) { if (ecma_is_value_empty (refs_p->buffer_p[i])) { refs_p->buffer_p[i] = target_value; return; } } ecma_collection_push_back (refs_p, target_value); } /* ecma_op_object_set_weak */ /** * Helper function to remove a weak reference to an object. * * @return ecma value * Returned value must be freed with ecma_free_value. */ void ecma_op_object_unref_weak (ecma_object_t *object_p, /**< this argument */ ecma_value_t ref_holder) /**< key argument */ { ecma_string_t *weak_refs_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_WEAK_REFS); ecma_property_t *property_p = ecma_find_named_property (object_p, weak_refs_string_p); JERRY_ASSERT (property_p != NULL); ecma_collection_t *refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, ECMA_PROPERTY_VALUE_PTR (property_p)->value); ecma_value_t *buffer_p = refs_p->buffer_p; while (true) { if (*buffer_p == ref_holder) { *buffer_p = ECMA_VALUE_EMPTY; return; } JERRY_ASSERT (buffer_p < refs_p->buffer_p + refs_p->item_count); buffer_p++; } } /* ecma_op_object_unref_weak */ #endif /* JERRY_BUILTIN_WEAKREF || JERRY_BUILTIN_CONTAINER */ /** * Raise property redefinition error * * @return ECMA_VALUE_FALSE - if JERRY_PROP_SHOULD_THROW is not set * raised TypeError - otherwise */ ecma_value_t ecma_raise_property_redefinition (ecma_string_t *property_name_p, /**< property name */ uint16_t flags) /**< property descriptor flags */ { JERRY_UNUSED (property_name_p); return ECMA_REJECT_WITH_FORMAT (flags & JERRY_PROP_SHOULD_THROW, "Cannot redefine property: %", ecma_make_prop_name_value (property_name_p)); } /* ecma_raise_property_redefinition */ /** * Raise readonly assignment error * * @return ECMA_VALUE_FALSE - if is_throw is true * raised TypeError - otherwise */ ecma_value_t ecma_raise_readonly_assignment (ecma_string_t *property_name_p, /**< property name */ bool is_throw) /**< is throw flag */ { JERRY_UNUSED (property_name_p); return ECMA_REJECT_WITH_FORMAT (is_throw, "Cannot assign to read only property '%'", ecma_make_prop_name_value (property_name_p)); } /* ecma_raise_readonly_assignment */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/000775 001750 001750 00000000000 15164251010 033532 5ustar00ddennedyddennedy000000 000000 thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-arraybuffer.inc.h000664 001750 001750 00000003223 15164251010 051121 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * ArrayBuffer built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_TYPEDARRAY /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_ARRAY_BUFFER_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ /* ES2015 24.1.3.1 */ ROUTINE (LIT_MAGIC_STRING_IS_VIEW_UL, ecma_builtin_arraybuffer_object_is_view, 1, 1) /* ES2015 24.1.3.3 */ ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_SPECIES, ecma_builtin_arraybuffer_species_get, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_TYPEDARRAY */ #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-objects.h000664 001750 001750 00000020224 15164251010 045114 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_OBJECTS_H #define ECMA_OBJECTS_H #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaobjectsinternalops ECMA objects' operations * @{ */ #if JERRY_ERROR_MESSAGES /** * Reject with TypeError depending on 'is_throw' with the given format */ #define ECMA_REJECT_WITH_FORMAT(is_throw, msg, ...) \ ((is_throw) ? ecma_raise_standard_error_with_format (JERRY_ERROR_TYPE, (msg), __VA_ARGS__) : ECMA_VALUE_FALSE) /** * Reject with TypeError depending on 'is_throw' with the given message */ #define ECMA_REJECT(is_throw, msg) ((is_throw) ? ecma_raise_type_error (msg) : ECMA_VALUE_FALSE) #else /* !JERRY_ERROR_MESSAGES */ /** * Reject with TypeError depending on is_throw flags wit the given format */ #define ECMA_REJECT_WITH_FORMAT(is_throw, msg, ...) ECMA_REJECT ((is_throw), (msg)) /** * Reject with TypeError depending on is_throw flags wit the given message */ #define ECMA_REJECT(is_throw, msg) ((is_throw) ? ecma_raise_type_error (ECMA_ERR_EMPTY) : ECMA_VALUE_FALSE) #endif /* JERRY_ERROR_MESSAGES */ ecma_value_t ecma_raise_property_redefinition (ecma_string_t *property_name_p, uint16_t flags); ecma_value_t ecma_raise_readonly_assignment (ecma_string_t *property_name_p, bool is_throw); ecma_property_t ecma_op_object_get_own_property (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_property_ref_t *property_ref_p, uint32_t options); ecma_value_t ecma_op_ordinary_object_has_own_property (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_value_t ecma_op_object_has_own_property (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_value_t ecma_op_object_has_property (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_value_t ecma_op_object_find_own (ecma_value_t base_value, ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_value_t ecma_op_object_find (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_value_t ecma_op_object_find_by_index (ecma_object_t *object_p, ecma_length_t index); ecma_value_t ecma_op_object_get (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_value_t ecma_op_object_get_with_receiver (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_value_t receiver); ecma_value_t ecma_op_object_get_length (ecma_object_t *object_p, ecma_length_t *length_p); ecma_value_t ecma_op_object_get_by_index (ecma_object_t *object_p, ecma_length_t index); ecma_value_t ecma_op_object_get_by_magic_id (ecma_object_t *object_p, lit_magic_string_id_t property_id); ecma_string_t *ecma_op_get_global_symbol (lit_magic_string_id_t property_id); bool ecma_op_compare_string_to_global_symbol (ecma_string_t *string_p, lit_magic_string_id_t property_id); ecma_value_t ecma_op_object_get_by_symbol_id (ecma_object_t *object_p, lit_magic_string_id_t property_id); ecma_value_t ecma_op_get_method_by_symbol_id (ecma_value_t value, lit_magic_string_id_t symbol_id); ecma_value_t ecma_op_get_method_by_magic_id (ecma_value_t value, lit_magic_string_id_t magic_id); ecma_value_t ecma_op_object_put_with_receiver (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_value_t value, ecma_value_t receiver, bool is_throw); ecma_value_t ecma_op_object_put (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_value_t value, bool is_throw); ecma_value_t ecma_op_object_put_with_receiver (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_value_t value, ecma_value_t receiver, bool is_throw); ecma_value_t ecma_op_object_put_by_index (ecma_object_t *object_p, ecma_length_t index, ecma_value_t value, bool is_throw); ecma_value_t ecma_op_object_delete (ecma_object_t *obj_p, ecma_string_t *property_name_p, bool is_throw); ecma_value_t ecma_op_object_delete_by_index (ecma_object_t *obj_p, ecma_length_t index, bool is_throw); ecma_value_t ecma_op_object_default_value (ecma_object_t *obj_p, ecma_preferred_type_hint_t hint); ecma_value_t ecma_op_object_define_own_property (ecma_object_t *obj_p, ecma_string_t *property_name_p, const ecma_property_descriptor_t *property_desc_p); ecma_value_t ecma_op_object_get_own_property_descriptor (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_property_descriptor_t *prop_desc_p); ecma_value_t ecma_op_object_has_instance (ecma_object_t *obj_p, ecma_value_t value); ecma_object_t *ecma_op_object_get_prototype_of (ecma_object_t *obj_p); ecma_value_t ecma_op_object_is_prototype_of (ecma_object_t *base_p, ecma_object_t *target_p); ecma_collection_t *ecma_op_object_get_enumerable_property_names (ecma_object_t *obj_p, ecma_enumerable_property_names_options_t option); ecma_collection_t *ecma_op_object_own_property_keys (ecma_object_t *obj_p, jerry_property_filter_t filter); ecma_collection_t *ecma_op_object_enumerate (ecma_object_t *obj_p); lit_magic_string_id_t ecma_object_get_class_name (ecma_object_t *obj_p); #if JERRY_BUILTIN_REGEXP bool ecma_object_is_regexp_object (ecma_value_t arg); #endif /* JERRY_BUILTIN_REGEXP */ ecma_value_t ecma_op_is_concat_spreadable (ecma_value_t arg); ecma_value_t ecma_op_is_regexp (ecma_value_t arg); ecma_value_t ecma_op_species_constructor (ecma_object_t *this_value, ecma_builtin_id_t default_constructor_id); ecma_value_t ecma_op_invoke_by_symbol_id (ecma_value_t object, lit_magic_string_id_t magic_string_id, ecma_value_t *args_p, uint32_t args_len); #if JERRY_BUILTIN_WEAKREF || JERRY_BUILTIN_CONTAINER void ecma_op_object_set_weak (ecma_object_t *object_p, ecma_object_t *target_p); void ecma_op_object_unref_weak (ecma_object_t *object_p, ecma_value_t ref_holder); #endif /* JERRY_BUILTIN_WEAKREF || JERRY_BUILTIN_CONTAINER */ ecma_value_t ecma_op_invoke (ecma_value_t object, ecma_string_t *property_name_p, ecma_value_t *args_p, uint32_t args_len); ecma_value_t ecma_op_invoke_by_magic_id (ecma_value_t object, lit_magic_string_id_t magic_string_id, ecma_value_t *args_p, uint32_t args_len); jmem_cpointer_t ecma_op_ordinary_object_get_prototype_of (ecma_object_t *obj_p); ecma_value_t ecma_op_ordinary_object_set_prototype_of (ecma_object_t *base_p, ecma_value_t proto); bool JERRY_ATTR_PURE ecma_op_ordinary_object_is_extensible (ecma_object_t *object_p); void ecma_op_ordinary_object_prevent_extensions (ecma_object_t *object_p); #if JERRY_BUILTIN_PROXY ecma_value_t ecma_op_get_own_property_descriptor (ecma_value_t target, ecma_string_t *property_name_p, ecma_property_descriptor_t *prop_desc_p); #endif /* JERRY_BUILTIN_PROXY */ /** * @} * @} */ #endif /* !ECMA_OBJECTS_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/meson.build000664 001750 001750 00000000550 15164251010 035267 0ustar00ddennedyddennedy000000 000000 source_file = [ 'alphai.h', 'common.h', 'decode_vp8.h', 'vp8i.h', 'vp8li.h', 'webpi.h', 'alpha.cpp', 'buffer.cpp', 'frame.cpp', 'io.cpp', 'quant.cpp', 'tree.cpp', 'vp8.cpp', 'vp8l.cpp', 'webp.cpp' ] webp_deb += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwCommon.h000664 001750 001750 00000057562 15164251010 036164 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SW_COMMON_H_ #define _TVG_SW_COMMON_H_ #include #include "tvgCommon.h" #include "tvgMath.h" #include "tvgColor.h" #include "tvgRender.h" #define SW_CURVE_TYPE_POINT 0 #define SW_CURVE_TYPE_CUBIC 1 #define SW_ANGLE_PI (180L << 16) #define SW_ANGLE_2PI (SW_ANGLE_PI << 1) #define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1) #define SW_COLOR_TABLE 1024 static inline float TO_FLOAT(int32_t val) { return static_cast(val) / 64.0f; } struct SwPoint { int32_t x, y; SwPoint& operator-=(const SwPoint& rhs) { x -= rhs.x; y -= rhs.y; return *this; } SwPoint& operator+=(const SwPoint& rhs) { x += rhs.x; y += rhs.y; return *this; } SwPoint operator+(const SwPoint& rhs) const { return {x + rhs.x, y + rhs.y}; } SwPoint operator-(const SwPoint& rhs) const { return {x - rhs.x, y - rhs.y}; } bool operator==(const SwPoint& rhs) const { return (x == rhs.x && y == rhs.y); } bool operator!=(const SwPoint& rhs) const { return (x != rhs.x || y != rhs.y); } bool zero() const { if (x == 0 && y == 0) return true; else return false; } bool tiny() const { //2 is epsilon... if (abs(x) < 2 && abs(y) < 2) return true; else return false; } Point toPoint() const { return {TO_FLOAT(x), TO_FLOAT(y)}; } }; struct SwSize { int32_t w, h; }; struct SwOutline { Array pts; //the outline's points Array cntrs; //the contour end points Array types; //curve type Array closed; //opened or closed path? FillRule fillRule; }; struct SwSpan { uint16_t x, y; uint16_t len; uint8_t coverage; bool fetch(const RenderRegion& bbox, int32_t& x, int32_t& len) const { x = std::max((int32_t)this->x, bbox.min.x); len = std::min((int32_t)(this->x + this->len), bbox.max.x) - x; return (len > 0) ? true : false; } }; struct SwRle { Array spans; const SwSpan* fetch(const RenderRegion& bbox, const SwSpan** end) const { return fetch(bbox.min.y, bbox.max.y - 1, end); } const SwSpan* fetch(int32_t min, uint32_t max, const SwSpan** end) const { const SwSpan* begin; if (min <= spans.first().y) { begin = spans.begin(); } else { auto comp = [](const SwSpan& span, int y) { return span.y < y; }; begin = lower_bound(spans.begin(), spans.end(), min, comp); } if (end) { if (max >= spans.last().y) { *end = spans.end(); } else { auto comp = [](int y, const SwSpan& span) { return y < span.y; }; *end = upper_bound(spans.begin(), spans.end(), max, comp); } } return begin; } bool invalid() const { return spans.empty(); } bool valid() const { return !invalid(); } uint32_t size() const { return spans.count; } SwSpan* data() const { return spans.data; } }; using Area = long; struct SwCell { int32_t x; int32_t cover; Area area; SwCell *next; }; struct SwFill { struct SwLinear { float dx, dy; float offset; }; struct SwRadial { float a11, a12, a13; float a21, a22, a23; float fx, fy, fr; float dx, dy, dr; float invA, a; }; union { SwLinear linear; SwRadial radial; }; uint32_t ctable[SW_COLOR_TABLE]; FillSpread spread; bool solid = false; //solid color fill with the last color from colorStops bool translucent; }; struct SwStrokeBorder { Array pts; uint8_t* tags = nullptr; int32_t start = 0; //index of current sub-path start point bool movable = false; //true: for ends of lineto borders ~SwStrokeBorder() { tvg::free(tags); } }; struct SwStroke { int64_t angleIn; int64_t angleOut; SwPoint center; int64_t lineLength; int64_t subPathAngle; SwPoint ptStartSubPath; int64_t subPathLineLength; int64_t width; int64_t miterlimit; SwFill* fill = nullptr; SwStrokeBorder* borders[2]; float sx, sy; StrokeCap cap; StrokeJoin join; StrokeJoin joinSaved; bool firstPt; bool closedSubPath; bool handleWideStrokes; }; struct SwDashStroke { SwOutline* outline = nullptr; float curLen = 0; int32_t curIdx = 0; Point ptStart = {0, 0}; Point ptCur = {0, 0}; float* pattern = nullptr; uint32_t cnt = 0; bool curOpGap = false; bool move = true; }; struct SwShape { SwOutline* outline = nullptr; SwStroke* stroke = nullptr; SwFill* fill = nullptr; SwRle* rle = nullptr; SwRle* strokeRle = nullptr; RenderRegion bbox; //Keep it boundary without stroke region. Using for optimal filling. bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips? }; struct SwImage { SwOutline* outline = nullptr; SwRle* rle = nullptr; union { pixel_t* data; //system based data pointer uint32_t* buf32; //for explicit 32bits channels uint8_t* buf8; //for explicit 8bits grayscale }; uint32_t w, h, stride; int32_t ox = 0; //offset x int32_t oy = 0; //offset y float scale; uint8_t channelSize; bool direct = false; //draw image directly (with offset) bool scaled = false; //draw scaled image }; typedef uint8_t(*SwMask)(uint8_t s, uint8_t d, uint8_t a); //src, dst, alpha typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d); //src, dst typedef uint32_t(*SwBlenderA)(uint32_t s, uint32_t d, uint8_t a); //src, dst, alpha typedef uint32_t(*SwJoin)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); //color channel join typedef uint8_t(*SwAlpha)(uint8_t*); //blending alpha struct SwCompositor; struct SwSurface : RenderSurface { SwJoin join; SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5 SwBlender blender = nullptr; //blender (optional) SwCompositor* compositor = nullptr; //compositor (optional) BlendMethod blendMethod = BlendMethod::Normal; SwAlpha alpha(MaskMethod method) { auto idx = (int)(method) - 1; //-1 for None return alphas[idx > 3 ? 0 : idx]; //CompositeMethod has only four Matting methods. } SwSurface() { } SwSurface(const SwSurface* rhs) : RenderSurface(rhs) { join = rhs->join; memcpy(alphas, rhs->alphas, sizeof(alphas)); blender = rhs->blender; compositor = rhs->compositor; blendMethod = rhs->blendMethod; } }; struct SwCompositor : RenderCompositor { SwSurface* recoverSfc; //Recover surface when composition is started SwCompositor* recoverCmp; //Recover compositor when composition is done SwImage image; RenderRegion bbox; bool valid; }; struct SwCellPool { #define DEFAULT_POOL_SIZE 16368 uint32_t size; SwCell* buffer; SwCellPool() : size(DEFAULT_POOL_SIZE), buffer(tvg::malloc(DEFAULT_POOL_SIZE)) {} ~SwCellPool() { tvg::free(buffer); } }; struct SwMpool { SwOutline* outline; SwStrokeBorder* leftBorder; SwStrokeBorder* rightBorder; SwCellPool* cellPool; unsigned allocSize; }; static inline int32_t TO_SWCOORD(float val) { return int32_t(val * 64.0f); } static inline uint32_t JOIN(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) { return (c0 << 24 | c1 << 16 | c2 << 8 | c3); } static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a) { ++a; return (((((c >> 8) & 0x00ff00ff) * a) & 0xff00ff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff)); } static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a) { return (((((((s >> 8) & 0xff00ff) - ((d >> 8) & 0xff00ff)) * a) + (d & 0xff00ff00)) & 0xff00ff00) + ((((((s & 0xff00ff) - (d & 0xff00ff)) * a) >> 8) + (d & 0xff00ff)) & 0xff00ff)); } static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a) { return (((s) * (a) + 0xff) >> 8) + (((d) * ~(a) + 0xff) >> 8); } static inline int32_t HALF_STROKE(float width) { return TO_SWCOORD(width * 0.5f); } static inline uint8_t A(uint32_t c) { return ((c) >> 24); } static inline uint8_t IA(uint32_t c) { return (~(c) >> 24); } static inline uint8_t C1(uint32_t c) { return ((c) >> 16); } static inline uint8_t C2(uint32_t c) { return ((c) >> 8); } static inline uint8_t C3(uint32_t c) { return (c); } static inline uint32_t PREMULTIPLY(uint32_t c, uint8_t a) { return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); } static inline RenderColor BLEND_UPRE(uint32_t c) { RenderColor o = {C1(c), C2(c), C3(c), A(c)}; if (o.a > 0 && o.a < 255) { o.r = std::min(o.r * 255u / o.a, 255u); o.g = std::min(o.g * 255u / o.a, 255u); o.b = std::min(o.b * 255u / o.a, 255u); } return o; } static inline uint32_t BLEND_PRE(uint32_t c1, uint32_t c2, uint8_t a) { if (a == 255) return c1; return ALPHA_BLEND(c1, a) + ALPHA_BLEND(c2, 255 - a); } static inline uint32_t opBlendInterp(uint32_t s, uint32_t d, uint8_t a) { return INTERPOLATE(s, d, a); } static inline uint32_t opBlendNormal(uint32_t s, uint32_t d, uint8_t a) { auto t = ALPHA_BLEND(s, a); return t + ALPHA_BLEND(d, IA(t)); } static inline uint32_t opBlendPreNormal(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) { return s + ALPHA_BLEND(d, IA(s)); } static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNUSED uint8_t a) { return s; } static inline uint32_t opBlendDifference(uint32_t s, uint32_t d) { auto f = [](uint8_t s, uint8_t d) { return (s > d) ? (s - d) : (d - s); }; return JOIN(255, f(C1(s), C1(d)), f(C2(s), C2(d)), f(C3(s), C3(d))); } static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d) { auto f = [](uint8_t s, uint8_t d) { return tvg::clamp(s + d - 2 * MULTIPLY(s, d), 0, 255); }; return JOIN(255, f(C1(s), C1(d)), f(C2(s), C2(d)), f(C3(s), C3(d))); } static inline uint32_t opBlendAdd(uint32_t s, uint32_t d) { auto f = [](uint8_t s, uint8_t d) { return std::min(s + d, 255); }; return JOIN(255, f(C1(s), C1(d)), f(C2(s), C2(d)), f(C3(s), C3(d))); } static inline uint32_t opBlendScreen(uint32_t s, uint32_t d) { auto f = [](uint8_t s, uint8_t d) { return s + d - MULTIPLY(s, d); }; return JOIN(255, f(C1(s), C1(d)), f(C2(s), C2(d)), f(C3(s), C3(d))); } static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); auto f = [](uint8_t s, uint8_t d) { return MULTIPLY(s, d); }; return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a); } static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); auto f = [](uint8_t s, uint8_t d) { return (d < 128) ? std::min(255, 2 * MULTIPLY(s, d)) : (255 - std::min(255, 2 * MULTIPLY(255 - s, 255 - d))); }; return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a); } static inline uint32_t opBlendDarken(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); auto f = [](uint8_t s, uint8_t d) { return std::min(s, d); }; return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a); } static inline uint32_t opBlendLighten(uint32_t s, uint32_t d) { auto f = [](uint8_t s, uint8_t d) { return std::max(s, d); }; return JOIN(255, f(C1(s), C1(d)), f(C2(s), C2(d)), f(C3(s), C3(d))); } static inline uint32_t opBlendColorDodge(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); auto f = [](uint8_t s, uint8_t d) { return d == 0 ? 0 : (s == 255 ? 255 : std::min(d * 255 / (255 - s), 255)); }; return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a); } static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); auto f = [](uint8_t s, uint8_t d) { return d == 255 ? 255 : (s == 0 ? 0 : 255 - std::min((255 - d) * 255 / s, 255)); }; return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a); } static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); auto f = [](uint8_t s, uint8_t d) { return (s < 128) ? std::min(255, 2 * MULTIPLY(s, d)) : (255 - std::min(255, 2 * MULTIPLY(255 - s, 255 - d))); }; return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a); } static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); auto f = [](uint8_t s, uint8_t d) { return MULTIPLY(255 - std::min(255, 2 * s), MULTIPLY(d, d)) + std::min(255, 2 * MULTIPLY(s, d)); }; return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a); } void rasterRGB2HSL(uint8_t r, uint8_t g, uint8_t b, float* h, float* s, float* l); static inline uint32_t opBlendHue(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); float sh, ds, dl; rasterRGB2HSL(C1(s), C2(s), C3(s), &sh, 0, 0); rasterRGB2HSL(o.r, o.g, o.b, 0, &ds, &dl); uint8_t r, g, b; hsl2rgb(sh, ds, dl, r, g, b); return BLEND_PRE(JOIN(255, r, g, b), s, o.a); } static inline uint32_t opBlendSaturation(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); float dh, ss, dl; rasterRGB2HSL(C1(s), C2(s), C3(s), 0, &ss, 0); rasterRGB2HSL(o.r, o.g, o.b, &dh, 0, &dl); uint8_t r, g, b; hsl2rgb(dh, ss, dl, r, g, b); return BLEND_PRE(JOIN(255, r, g, b), s, o.a); } static inline uint32_t opBlendColor(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); float sh, ss, dl; rasterRGB2HSL(C1(s), C2(s), C3(s), &sh, &ss, 0); rasterRGB2HSL(o.r, o.g, o.b, 0, 0, &dl); uint8_t r, g, b; hsl2rgb(sh, ss, dl, r, g, b); return BLEND_PRE(JOIN(255, r, g, b), s, o.a); } static inline uint32_t opBlendLuminosity(uint32_t s, uint32_t d) { auto o = BLEND_UPRE(d); float dh, ds, sl; rasterRGB2HSL(C1(s), C2(s), C3(s), 0, 0, &sl); rasterRGB2HSL(o.r, o.g, o.b, &dh, &ds, 0); uint8_t r, g, b; hsl2rgb(dh, ds, sl, r, g, b); return BLEND_PRE(JOIN(255, r, g, b), s, o.a); } int64_t mathMultiply(int64_t a, int64_t b); int64_t mathDivide(int64_t a, int64_t b); int64_t mathMulDiv(int64_t a, int64_t b, int64_t c); void mathRotate(SwPoint& pt, int64_t angle); int64_t mathTan(int64_t angle); int64_t mathAtan(const SwPoint& pt); int64_t mathCos(int64_t angle); int64_t mathSin(int64_t angle); void mathSplitCubic(SwPoint* base); void mathSplitLine(SwPoint* base); int64_t mathDiff(int64_t angle1, int64_t angle2); int64_t mathLength(const SwPoint& pt); int mathCubicAngle(const SwPoint* base, int64_t& angleIn, int64_t& angleMid, int64_t& angleOut); int64_t mathMean(int64_t angle1, int64_t angle2); SwPoint mathTransform(const Point* to, const Matrix& transform); bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack); void shapeReset(SwShape& shape); bool shapePrepare(SwShape& shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid, bool hasComposite); bool shapeGenRle(SwShape& shape, const RenderRegion& bbox, SwMpool* mpool, unsigned tid, bool antiAlias); void shapeDelOutline(SwShape& shape, SwMpool* mpool, uint32_t tid); void shapeResetStroke(SwShape& shape, const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid); bool shapeGenStrokeRle(SwShape& shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid); void shapeFree(SwShape& shape); void shapeDelStroke(SwShape& shape); bool shapeGenFillColors(SwShape& shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable); bool shapeGenStrokeFillColors(SwShape& shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable); void shapeResetFill(SwShape& shape); void shapeResetStrokeFill(SwShape& shape); bool shapeStrokeBBox(SwShape& shape, const RenderShape* rshape, Point* pt4, const Matrix& m, SwMpool* mpool); void shapeDelFill(SwShape& shape); void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix& transform, SwMpool* mpool, unsigned tid); bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline, SwMpool* mpool, unsigned tid); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); void strokeFree(SwStroke* stroke); bool imagePrepare(SwImage& image, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid); bool imageGenRle(SwImage& image, const RenderRegion& bbox, SwMpool* mpool, unsigned tid, bool antiAlias); void imageDelOutline(SwImage& image, SwMpool* mpool, uint32_t tid); void imageReset(SwImage& image); void imageFree(SwImage& image); bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable); const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata); void fillReset(SwFill* fill); void fillFree(SwFill* fill); //OPTIMIZE_ME: Skip the function pointer access void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t opacity); //composite masking ver. void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t opacity); //direct masking ver. void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, uint8_t a); //blending ver. void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver. void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver. void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a); //composite masking ver. void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) ; //direct masking ver. void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, uint8_t a); //blending ver. void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver. void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver. SwRle* rleRender(SwRle* rle, const SwOutline* outline, const RenderRegion& bbox, SwMpool* mpool, unsigned tid, bool antiAlias); SwRle* rleRender(const RenderRegion* bbox); void rleFree(SwRle* rle); void rleReset(SwRle* rle); void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2); bool rleClip(SwRle* rle, const SwRle* clip); bool rleClip(SwRle* rle, const RenderRegion* clip); bool rleIntersect(const SwRle* rle, const RenderRegion& region); SwMpool* mpoolInit(uint32_t threads); void mpoolTerm(SwMpool* mpool); SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx); SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx); SwStrokeBorder* mpoolReqStrokeLBorder(SwMpool* mpool, unsigned idx); SwStrokeBorder* mpoolReqStrokeRBorder(SwMpool* mpool, unsigned idx); SwCellPool* mpoolReqCellPool(SwMpool* mpool, unsigned idx); bool rasterCompositor(SwSurface* surface); bool rasterShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c); bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity); bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity); bool rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity); bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity); bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity); bool rasterStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c); bool rasterGradientShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity); void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity); void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len); void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const RenderRegion& bbox, bool flipped); void rasterUnpremultiply(RenderSurface* surface); void rasterPremultiply(RenderSurface* surface); bool rasterConvertCS(RenderSurface* surface, ColorSpace to); uint32_t rasterUnpremultiply(uint32_t data); bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params); bool effectGaussianBlurRegion(RenderEffectGaussianBlur* effect); void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform); bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, bool direct); bool effectDropShadowRegion(RenderEffectDropShadow* effect); void effectDropShadowUpdate(RenderEffectDropShadow* effect, const Matrix& transform); void effectFillUpdate(RenderEffectFill* effect); bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct); void effectTintUpdate(RenderEffectTint* effect); bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct); void effectTritoneUpdate(RenderEffectTritone* effect); bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct); #endif /* _TVG_SW_COMMON_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/regression/settings_crash_leak.toml000664 001750 001750 00000004246 15164251010 037214 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0[general] folder_with_files_to_check = "FilesToTest" problematic_files_path = "ProblematicSVG" # Where to store problematic files, in which conversion failed(e.g. due program crash) output_folder = "BrokenSVG" # Place where to save files(Input and output that show differences) ignored_files_path = "IgnoredSVG" # Place where to save ignored files timeout = 120 limit_threads = 0 # 0 will use all available threads px_size_of_generated_file = 400 ignore_conversion_step = false # Ignore step with conversion files from svg to png, just compare files ignore_similarity_checking_step = false # Useful to finding problems with generating files ignore_thorvg_not_supported_items = true # Thorvg not supports files with text, filters max_difference = 5 # Bigger similarity will show only broken files that are completely different, looks that 0-100 is quite reasonable range limit_files = 0 # Limit checked files, useful if you are just wanting to check how app works, 0 will remove limit of checked files remove_files_from_output_folder_at_start = true # Useful if you run app with different settings and you don't want to remove files one by one debug_show_always_output = false # Allows to find broken files return_error_when_finding_invalid_files = false # When finding invalid files(broken or problematic) app will close with status 1 remove_problematic_files_after_copying = false # Remove from output folder problematic svg files remove_broken_files_after_copying = false # Remove from output folder broken svg files remove_ignored_files_after_copying = false # Removes not supported folders after copying remove_generated_png_files_at_end = false # Remove all png from output folder at end lottie_path = "" lottie_broken_files_path = "" lottie_test = false thorvg_path = "./build/src/tools/svg2png/svg2png" thorvg_broken_files_path = "BrokenFILES" thorvg_test = true [first_tool] name = "thorvg_pr" path = "./build/src/tools/svg2png/svg2png" png_name_ending = "_thorvg_pr.png" arguments = "{FILE} -r {SIZE}x{SIZE} -b ffffff" [other_tool] name = "thorvg_develop" path = "./thorvg_develop/build/src/tools/svg2png/svg2png" png_name_ending = "_thorvg_develop.png" arguments = "{FILE} -r {SIZE}x{SIZE} -b ffffff" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/re-bytecode.cpp000664 001750 001750 00000044646 15164251010 045031 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "re-bytecode.h" #include "ecma-globals.h" #include "ecma-regexp-object.h" #include "lit-strings.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup parser Parser * @{ * * \addtogroup regexparser Regular expression * @{ * * \addtogroup regexparser_bytecode Bytecode * @{ */ void re_initialize_regexp_bytecode (re_compiler_ctx_t *re_ctx_p) /**< RegExp bytecode context */ { const size_t initial_size = sizeof (re_compiled_code_t); re_ctx_p->bytecode_start_p = (uint8_t *) jmem_heap_alloc_block (initial_size); re_ctx_p->bytecode_size = initial_size; } /* re_initialize_regexp_bytecode */ uint32_t re_bytecode_size (re_compiler_ctx_t *re_ctx_p) /**< RegExp bytecode context */ { return (uint32_t) re_ctx_p->bytecode_size; } /* re_bytecode_size */ /** * Append a new bytecode to the and of the bytecode container */ static uint8_t * re_bytecode_reserve (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const size_t size) /**< size */ { const size_t old_size = re_ctx_p->bytecode_size; const size_t new_size = old_size + size; re_ctx_p->bytecode_start_p = reinterpret_cast(jmem_heap_realloc_block (re_ctx_p->bytecode_start_p, old_size, new_size)); re_ctx_p->bytecode_size = new_size; return re_ctx_p->bytecode_start_p + old_size; } /* re_bytecode_reserve */ /** * Insert a new bytecode to the bytecode container */ static uint8_t * re_bytecode_insert (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const size_t offset, /**< distance from the start of the container */ const size_t size) /**< size */ { const size_t tail_size = re_ctx_p->bytecode_size - offset; re_bytecode_reserve (re_ctx_p, size); uint8_t *dest_p = re_ctx_p->bytecode_start_p + offset; memmove (dest_p + size, dest_p, tail_size); return dest_p; } /* re_bytecode_insert */ /** * Append a byte */ void re_append_byte (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const uint8_t byte) /**< byte value */ { uint8_t *dest_p = re_bytecode_reserve (re_ctx_p, sizeof (uint8_t)); *dest_p = byte; } /* re_append_byte */ /** * Insert a byte value */ void re_insert_byte (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const uint32_t offset, /**< distance from the start of the container */ const uint8_t byte) /**< byte value */ { uint8_t *dest_p = re_bytecode_insert (re_ctx_p, offset, sizeof (uint8_t)); *dest_p = byte; } /* re_insert_byte */ /** * Get a single byte and increase bytecode position. */ uint8_t re_get_byte (const uint8_t **bc_p) /**< pointer to bytecode start */ { return *((*bc_p)++); } /* re_get_byte */ /** * Append a RegExp opcode */ void re_append_opcode (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const re_opcode_t opcode) /**< input opcode */ { re_append_byte (re_ctx_p, (uint8_t) opcode); } /* re_append_opcode */ /** * Insert a RegExp opcode */ void re_insert_opcode (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const uint32_t offset, /**< distance from the start of the container */ const re_opcode_t opcode) /**< input opcode */ { re_insert_byte (re_ctx_p, offset, (uint8_t) opcode); } /* re_insert_opcode */ /** * Get a RegExp opcode and increase the bytecode position * * @return current RegExp opcode */ re_opcode_t re_get_opcode (const uint8_t **bc_p) /**< pointer to bytecode start */ { return (re_opcode_t) re_get_byte (bc_p); } /* re_get_opcode */ /** * Encode 2 byte unsigned integer into the bytecode */ static void re_encode_u16 (uint8_t *dest_p, /**< destination */ const uint16_t value) /**< value */ { *dest_p++ = (uint8_t) ((value >> 8) & 0xFF); *dest_p = (uint8_t) (value & 0xFF); } /* re_encode_u16 */ /** * Encode 4 byte unsigned integer into the bytecode */ static void re_encode_u32 (uint8_t *dest_p, /**< destination */ const uint32_t value) /**< value */ { *dest_p++ = (uint8_t) ((value >> 24) & 0xFF); *dest_p++ = (uint8_t) ((value >> 16) & 0xFF); *dest_p++ = (uint8_t) ((value >> 8) & 0xFF); *dest_p = (uint8_t) (value & 0xFF); } /* re_encode_u32 */ /** * Decode 2 byte unsigned integer from bytecode * * @return uint16_t value */ static uint16_t re_decode_u16 (const uint8_t *src_p) /**< source */ { uint16_t value = (uint16_t) (((uint16_t) *src_p++) << 8); value = (uint16_t) (value + *src_p++); return value; } /* re_decode_u16 */ /** * Decode 4 byte unsigned integer from bytecode * * @return uint32_t value */ static uint32_t JERRY_ATTR_NOINLINE re_decode_u32 (const uint8_t *src_p) /**< source */ { uint32_t value = (uint32_t) (((uint32_t) *src_p++) << 24); value += (uint32_t) (((uint32_t) *src_p++) << 16); value += (uint32_t) (((uint32_t) *src_p++) << 8); value += (uint32_t) (*src_p++); return value; } /* re_decode_u32 */ /** * Get the encoded size of an uint32_t value. * * @return encoded value size */ static inline size_t re_get_encoded_value_size (uint32_t value) /**< value */ { if (JERRY_LIKELY (value <= RE_VALUE_1BYTE_MAX)) { return 1; } return 5; } /* re_get_encoded_value_size */ /* * Encode a value to the specified position in the bytecode. */ static void re_encode_value (uint8_t *dest_p, /**< position in bytecode */ const uint32_t value) /**< value */ { if (JERRY_LIKELY (value <= RE_VALUE_1BYTE_MAX)) { *dest_p = (uint8_t) value; return; } *dest_p++ = (uint8_t) (RE_VALUE_4BYTE_MARKER); re_encode_u32 (dest_p, value); } /* re_encode_value */ /** * Append a value to the end of the bytecode. */ void re_append_value (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const uint32_t value) /**< value */ { const size_t size = re_get_encoded_value_size (value); uint8_t *dest_p = re_bytecode_reserve (re_ctx_p, size); re_encode_value (dest_p, value); } /* re_append_value */ /** * Insert a value into the bytecode at a specific offset. */ void re_insert_value (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const uint32_t offset, /**< bytecode offset */ const uint32_t value) /**< value */ { const size_t size = re_get_encoded_value_size (value); uint8_t *dest_p = re_bytecode_insert (re_ctx_p, offset, size); re_encode_value (dest_p, value); } /* re_insert_value */ /** * Read an encoded value from the bytecode. * * @return decoded value */ uint32_t re_get_value (const uint8_t **bc_p) /** refence to bytecode pointer */ { uint32_t value = *(*bc_p)++; if (JERRY_LIKELY (value <= RE_VALUE_1BYTE_MAX)) { return value; } value = re_decode_u32 (*bc_p); *bc_p += sizeof (uint32_t); return value; } /* re_get_value */ /** * Append a character to the RegExp bytecode */ void re_append_char (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const lit_code_point_t cp) /**< code point */ { const size_t size = (re_ctx_p->flags & RE_FLAG_UNICODE) ? sizeof (lit_code_point_t) : sizeof (ecma_char_t); uint8_t *dest_p = re_bytecode_reserve (re_ctx_p, size); if (re_ctx_p->flags & RE_FLAG_UNICODE) { re_encode_u32 (dest_p, cp); return; } JERRY_ASSERT (cp <= LIT_UTF16_CODE_UNIT_MAX); re_encode_u16 (dest_p, (ecma_char_t) cp); } /* re_append_char */ /** * Append a character to the RegExp bytecode */ void re_insert_char (re_compiler_ctx_t *re_ctx_p, /**< RegExp bytecode context */ const uint32_t offset, /**< bytecode offset */ const lit_code_point_t cp) /**< code point*/ { const size_t size = (re_ctx_p->flags & RE_FLAG_UNICODE) ? sizeof (lit_code_point_t) : sizeof (ecma_char_t); uint8_t *dest_p = re_bytecode_insert (re_ctx_p, offset, size); if (re_ctx_p->flags & RE_FLAG_UNICODE) { re_encode_u32 (dest_p, cp); return; } JERRY_ASSERT (cp <= LIT_UTF16_CODE_UNIT_MAX); re_encode_u16 (dest_p, (ecma_char_t) cp); } /* re_insert_char */ /** * Decode a character from the bytecode. * * @return decoded character */ lit_code_point_t re_get_char (const uint8_t **bc_p, /**< reference to bytecode pointer */ bool unicode) /**< full unicode mode */ { lit_code_point_t cp; if (unicode) { cp = re_decode_u32 (*bc_p); *bc_p += sizeof (lit_code_point_t); } else { cp = re_decode_u16 (*bc_p); *bc_p += sizeof (ecma_char_t); } return cp; } /* re_get_char */ #if JERRY_REGEXP_DUMP_BYTE_CODE static uint32_t re_get_bytecode_offset (const uint8_t *start_p, /**< bytecode start pointer */ const uint8_t *current_p) /**< current bytecode pointer */ { return (uint32_t) ((uintptr_t) current_p - (uintptr_t) start_p); } /* re_get_bytecode_offset */ /** * RegExp bytecode dumper */ void re_dump_bytecode (re_compiler_ctx_t *re_ctx_p) /**< RegExp bytecode context */ { static const char escape_chars[] = { 'd', 'D', 'w', 'W', 's', 'S' }; re_compiled_code_t *compiled_code_p = (re_compiled_code_t *) re_ctx_p->bytecode_start_p; JERRY_DEBUG_MSG ("Flags: 0x%x ", compiled_code_p->header.status_flags); JERRY_DEBUG_MSG ("Capturing groups: %d ", compiled_code_p->captures_count); JERRY_DEBUG_MSG ("Non-capturing groups: %d\n", compiled_code_p->non_captures_count); const uint8_t *bytecode_start_p = (const uint8_t *) (compiled_code_p + 1); const uint8_t *bytecode_p = bytecode_start_p; while (true) { JERRY_DEBUG_MSG ("[%3u] ", (uint32_t) ((uintptr_t) bytecode_p - (uintptr_t) bytecode_start_p)); re_opcode_t op = *bytecode_p++; switch (op) { case RE_OP_ALTERNATIVE_START: { JERRY_DEBUG_MSG ("ALTERNATIVE_START "); const uint32_t offset = re_get_value (&bytecode_p) + re_get_bytecode_offset (bytecode_start_p, bytecode_p); JERRY_DEBUG_MSG ("tail offset: [%3u]\n", offset); break; } case RE_OP_ALTERNATIVE_NEXT: { JERRY_DEBUG_MSG ("ALTERNATIVE_NEXT "); const uint32_t offset = re_get_value (&bytecode_p) + re_get_bytecode_offset (bytecode_start_p, bytecode_p); JERRY_DEBUG_MSG ("tail offset: [%3u]\n", offset); break; } case RE_OP_NO_ALTERNATIVE: { JERRY_DEBUG_MSG ("NO_ALTERNATIVES\n"); break; } case RE_OP_CAPTURING_GROUP_START: { JERRY_DEBUG_MSG ("CAPTURING_GROUP_START "); JERRY_DEBUG_MSG ("idx: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("capture count: %u, ", re_get_value (&bytecode_p)); const uint32_t qmin = re_get_value (&bytecode_p); JERRY_DEBUG_MSG ("qmin: %u", qmin); if (qmin == 0) { const uint32_t offset = re_get_value (&bytecode_p) + re_get_bytecode_offset (bytecode_start_p, bytecode_p); JERRY_DEBUG_MSG (", tail offset: [%3u]\n", offset); } else { JERRY_DEBUG_MSG ("\n"); } break; } case RE_OP_NON_CAPTURING_GROUP_START: { JERRY_DEBUG_MSG ("NON_CAPTURING_GROUP_START "); JERRY_DEBUG_MSG ("idx: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("capture start: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("capture count: %u, ", re_get_value (&bytecode_p)); const uint32_t qmin = re_get_value (&bytecode_p); JERRY_DEBUG_MSG ("qmin: %u", qmin); if (qmin == 0) { const uint32_t offset = re_get_value (&bytecode_p) + re_get_bytecode_offset (bytecode_start_p, bytecode_p); JERRY_DEBUG_MSG (", tail offset: [%3u]\n", offset); } else { JERRY_DEBUG_MSG ("\n"); } break; } case RE_OP_GREEDY_CAPTURING_GROUP_END: { JERRY_DEBUG_MSG ("GREEDY_CAPTURING_GROUP_END "); JERRY_DEBUG_MSG ("idx: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmin: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmax: %u\n", re_get_value (&bytecode_p) - RE_QMAX_OFFSET); break; } case RE_OP_LAZY_CAPTURING_GROUP_END: { JERRY_DEBUG_MSG ("LAZY_CAPTURING_GROUP_END "); JERRY_DEBUG_MSG ("idx: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmin: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmax: %u\n", re_get_value (&bytecode_p) - RE_QMAX_OFFSET); break; } case RE_OP_GREEDY_NON_CAPTURING_GROUP_END: { JERRY_DEBUG_MSG ("GREEDY_NON_CAPTURING_GROUP_END "); JERRY_DEBUG_MSG ("idx: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmin: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmax: %u\n", re_get_value (&bytecode_p) - RE_QMAX_OFFSET); break; } case RE_OP_LAZY_NON_CAPTURING_GROUP_END: { JERRY_DEBUG_MSG ("LAZY_NON_CAPTURING_GROUP_END "); JERRY_DEBUG_MSG ("idx: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmin: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmax: %u\n", re_get_value (&bytecode_p) - RE_QMAX_OFFSET); break; } case RE_OP_GREEDY_ITERATOR: { JERRY_DEBUG_MSG ("GREEDY_ITERATOR "); JERRY_DEBUG_MSG ("qmin: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmax: %u, ", re_get_value (&bytecode_p) - RE_QMAX_OFFSET); const uint32_t offset = re_get_value (&bytecode_p) + re_get_bytecode_offset (bytecode_start_p, bytecode_p); JERRY_DEBUG_MSG ("tail offset: [%3u]\n", offset); break; } case RE_OP_LAZY_ITERATOR: { JERRY_DEBUG_MSG ("LAZY_ITERATOR "); JERRY_DEBUG_MSG ("qmin: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("qmax: %u, ", re_get_value (&bytecode_p) - RE_QMAX_OFFSET); const uint32_t offset = re_get_value (&bytecode_p) + re_get_bytecode_offset (bytecode_start_p, bytecode_p); JERRY_DEBUG_MSG ("tail offset: [%3u]\n", offset); break; } case RE_OP_ITERATOR_END: { JERRY_DEBUG_MSG ("ITERATOR_END\n"); break; } case RE_OP_BACKREFERENCE: { JERRY_DEBUG_MSG ("BACKREFERENCE "); JERRY_DEBUG_MSG ("idx: %d\n", re_get_value (&bytecode_p)); break; } case RE_OP_ASSERT_LINE_START: { JERRY_DEBUG_MSG ("ASSERT_LINE_START\n"); break; } case RE_OP_ASSERT_LINE_END: { JERRY_DEBUG_MSG ("ASSERT_LINE_END\n"); break; } case RE_OP_ASSERT_LOOKAHEAD_POS: { JERRY_DEBUG_MSG ("ASSERT_LOOKAHEAD_POS "); JERRY_DEBUG_MSG ("qmin: %u, ", *bytecode_p++); JERRY_DEBUG_MSG ("capture start: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("capture count: %u, ", re_get_value (&bytecode_p)); const uint32_t offset = re_get_value (&bytecode_p) + re_get_bytecode_offset (bytecode_start_p, bytecode_p); JERRY_DEBUG_MSG ("tail offset: [%3u]\n", offset); break; } case RE_OP_ASSERT_LOOKAHEAD_NEG: { JERRY_DEBUG_MSG ("ASSERT_LOOKAHEAD_NEG "); JERRY_DEBUG_MSG ("qmin: %u, ", *bytecode_p++); JERRY_DEBUG_MSG ("capture start: %u, ", re_get_value (&bytecode_p)); JERRY_DEBUG_MSG ("capture count: %u, ", re_get_value (&bytecode_p)); const uint32_t offset = re_get_value (&bytecode_p) + re_get_bytecode_offset (bytecode_start_p, bytecode_p); JERRY_DEBUG_MSG ("tail offset: [%3u]\n", offset); break; } case RE_OP_ASSERT_END: { JERRY_DEBUG_MSG ("ASSERT_END\n"); break; } case RE_OP_ASSERT_WORD_BOUNDARY: { JERRY_DEBUG_MSG ("ASSERT_WORD_BOUNDARY\n"); break; } case RE_OP_ASSERT_NOT_WORD_BOUNDARY: { JERRY_DEBUG_MSG ("ASSERT_NOT_WORD_BOUNDARY\n"); break; } case RE_OP_CLASS_ESCAPE: { ecma_class_escape_t escape = (ecma_class_escape_t) *bytecode_p++; JERRY_DEBUG_MSG ("CLASS_ESCAPE \\%c\n", escape_chars[escape]); break; } case RE_OP_CHAR_CLASS: { JERRY_DEBUG_MSG ("CHAR_CLASS "); uint8_t flags = *bytecode_p++; uint32_t char_count = (flags & RE_CLASS_HAS_CHARS) ? re_get_value (&bytecode_p) : 0; uint32_t range_count = (flags & RE_CLASS_HAS_RANGES) ? re_get_value (&bytecode_p) : 0; if (flags & RE_CLASS_INVERT) { JERRY_DEBUG_MSG ("inverted "); } JERRY_DEBUG_MSG ("escapes: "); uint8_t escape_count = flags & RE_CLASS_ESCAPE_COUNT_MASK; while (escape_count--) { JERRY_DEBUG_MSG ("\\%c, ", escape_chars[*bytecode_p++]); } JERRY_DEBUG_MSG ("chars: "); while (char_count--) { JERRY_DEBUG_MSG ("\\u%04x, ", re_get_char (&bytecode_p, re_ctx_p->flags & RE_FLAG_UNICODE)); } JERRY_DEBUG_MSG ("ranges: "); while (range_count--) { const lit_code_point_t begin = re_get_char (&bytecode_p, re_ctx_p->flags & RE_FLAG_UNICODE); const lit_code_point_t end = re_get_char (&bytecode_p, re_ctx_p->flags & RE_FLAG_UNICODE); JERRY_DEBUG_MSG ("\\u%04x-\\u%04x, ", begin, end); } JERRY_DEBUG_MSG ("\n"); break; } case RE_OP_UNICODE_PERIOD: { JERRY_DEBUG_MSG ("UNICODE_PERIOD\n"); break; } case RE_OP_PERIOD: { JERRY_DEBUG_MSG ("PERIOD\n"); break; } case RE_OP_CHAR: { JERRY_DEBUG_MSG ("CHAR \\u%04x\n", re_get_char (&bytecode_p, re_ctx_p->flags & RE_FLAG_UNICODE)); break; } case RE_OP_BYTE: { const uint8_t ch = *bytecode_p++; JERRY_DEBUG_MSG ("BYTE \\u%04x '%c'\n", ch, (char) ch); break; } case RE_OP_EOF: { JERRY_DEBUG_MSG ("EOF\n"); return; } default: { JERRY_DEBUG_MSG ("UNKNOWN(%d)\n", (uint32_t) op); break; } } } } /* re_dump_bytecode */ #endif /* JERRY_REGEXP_DUMP_BYTE_CODE */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgAccessor.cpp000664 001750 001750 00000005545 15164251010 034612 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgIteratorAccessor.h" #include "tvgCompressor.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static bool accessChildren(Iterator* it, function func, void* data) { while (auto child = it->next()) { //Access the child if (!func(child, data)) return false; //Access the children of the child if (auto it2 = IteratorAccessor::iterator(child)) { if (!accessChildren(it2, func, data)) { delete(it2); return false; } delete(it2); } } return true; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ Result Accessor::set(Paint* paint, function func, void* data) noexcept { if (!paint || !func) return Result::InvalidArguments; //Use the Preorder Tree-Search paint->ref(); //Root if (!func(paint, data)) { paint->unref(false); return Result::Success; } //Children if (auto it = IteratorAccessor::iterator(paint)) { accessChildren(it, func, data); delete(it); } paint->unref(false); return Result::Success; } uint32_t Accessor::id(const char* name) noexcept { return djb2Encode(name); } Accessor::~Accessor() { } Accessor::Accessor() : pImpl(nullptr) { } Accessor* Accessor::gen() noexcept { return new Accessor; } src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp000664 001750 001750 00000060104 15164251010 037313 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgSwCommon.h" /************************************************************************/ /* Gaussian Blur Implementation */ /************************************************************************/ struct SwGaussianBlur { static constexpr int MAX_LEVEL = 3; int level; int kernel[MAX_LEVEL]; int extends; }; static inline int _gaussianEdgeWrap(int end, int idx) { auto r = idx % (end + 1); return (r < 0) ? (end + 1) + r : r; } static inline int _gaussianEdgeExtend(int end, int idx) { if (idx < 0) return 0; else if (idx > end) return end; return idx; } template static inline int _gaussianRemap(int end, int idx) { if (border == 1) return _gaussianEdgeWrap(end, idx); return _gaussianEdgeExtend(end, idx); } //TODO: SIMD OPTIMIZATION? template static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const RenderRegion& bbox, int32_t dimension, bool flipped) { if (flipped) { src += (bbox.min.x * stride + bbox.min.y) << 2; dst += (bbox.min.x * stride + bbox.min.y) << 2; } else { src += (bbox.min.y * stride + bbox.min.x) << 2; dst += (bbox.min.y * stride + bbox.min.x) << 2; } auto iarr = 1.0f / (dimension + dimension + 1); auto end = w - 1; #pragma omp parallel for for (int y = 0; y < h; ++y) { auto p = y * stride; auto i = p * 4; //current index auto l = -(dimension + 1); //left index auto r = dimension; //right index int acc[4] = {0, 0, 0, 0}; //sliding accumulator //initial accumulation for (int x = l; x < r; ++x) { auto id = (_gaussianRemap(end, x) + p) * 4; acc[0] += src[id++]; acc[1] += src[id++]; acc[2] += src[id++]; acc[3] += src[id]; } //perform filtering for (int x = 0; x < w; ++x, ++r, ++l) { auto rid = (_gaussianRemap(end, r) + p) * 4; auto lid = (_gaussianRemap(end, l) + p) * 4; acc[0] += src[rid++] - src[lid++]; acc[1] += src[rid++] - src[lid++]; acc[2] += src[rid++] - src[lid++]; acc[3] += src[rid] - src[lid]; //ignored rounding for the performance. It should be originally: acc[idx] * iarr + 0.5f dst[i++] = static_cast(acc[0] * iarr); dst[i++] = static_cast(acc[1] * iarr); dst[i++] = static_cast(acc[2] * iarr); dst[i++] = static_cast(acc[3] * iarr); } } } //Fast Almost-Gaussian Filtering Method by Peter Kovesi static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality) { const auto MAX_LEVEL = SwGaussianBlur::MAX_LEVEL; if (tvg::zero(sigma)) return 0; data->level = int(SwGaussianBlur::MAX_LEVEL * ((quality - 1) * 0.01f)) + 1; //compute box kernel sizes auto wl = (int) sqrt((12 * sigma / MAX_LEVEL) + 1); if (wl % 2 == 0) --wl; auto wu = wl + 2; auto mi = (12 * sigma - MAX_LEVEL * wl * wl - 4 * MAX_LEVEL * wl - 3 * MAX_LEVEL) / (-4 * wl - 4); auto m = int(mi + 0.5f); auto extends = 0; for (int i = 0; i < data->level; i++) { data->kernel[i] = ((i < m ? wl : wu) - 1) / 2; extends += data->kernel[i]; } return extends; } bool effectGaussianBlurRegion(RenderEffectGaussianBlur* params) { //region expansion for feathering auto& bbox = params->extend; auto extra = static_cast(params->rd)->extends; if (params->direction != 2) { bbox.min.x = -extra; bbox.max.x = extra; } if (params->direction != 1) { bbox.min.y = -extra; bbox.max.y = extra; } return true; } void effectGaussianBlurUpdate(RenderEffectGaussianBlur* params, const Matrix& transform) { if (!params->rd) params->rd = tvg::malloc(sizeof(SwGaussianBlur)); auto rd = static_cast(params->rd); //compute box kernel sizes auto scale = sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality); //invalid if (rd->extends == 0) { params->valid = false; return; } params->valid = true; } bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params) { auto& buffer = surface->compositor->image; auto data = static_cast(params->rd); auto& bbox = cmp->bbox; auto w = (bbox.max.x - bbox.min.x); auto h = (bbox.max.y - bbox.min.y); auto stride = cmp->image.stride; auto front = cmp->image.buf32; auto back = buffer.buf32; auto swapped = false; TVGLOG("SW_ENGINE", "GaussianFilter region(%d, %d, %d, %d) params(%f %d %d), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->sigma, params->direction, params->border, data->level); /* It is best to take advantage of the Gaussian blur’s separable property by dividing the process into two passes. horizontal and vertical. We can expect fewer calculations. */ //horizontal if (params->direction != 2) { for (int i = 0; i < data->level; ++i) { _gaussianFilter(reinterpret_cast(back), reinterpret_cast(front), stride, w, h, bbox, data->kernel[i], false); std::swap(front, back); swapped = !swapped; } } //vertical. x/y flipping and horionztal access is pretty compatible with the memory architecture. if (params->direction != 1) { rasterXYFlip(front, back, stride, w, h, bbox, false); std::swap(front, back); for (int i = 0; i < data->level; ++i) { _gaussianFilter(reinterpret_cast(back), reinterpret_cast(front), stride, h, w, bbox, data->kernel[i], true); std::swap(front, back); swapped = !swapped; } rasterXYFlip(front, back, stride, h, w, bbox, true); std::swap(front, back); } if (swapped) std::swap(cmp->image.buf8, buffer.buf8); return true; } /************************************************************************/ /* Drop Shadow Implementation */ /************************************************************************/ struct SwDropShadow : SwGaussianBlur { SwPoint offset; }; //TODO: SIMD OPTIMIZATION? static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const RenderRegion& bbox, int32_t dimension, uint32_t color, bool flipped) { if (flipped) { src += (bbox.min.x * stride + bbox.min.y); dst += (bbox.min.x * stride + bbox.min.y); } else { src += (bbox.min.y * stride + bbox.min.x); dst += (bbox.min.y * stride + bbox.min.x); } auto iarr = 1.0f / (dimension + dimension + 1); auto end = w - 1; #pragma omp parallel for for (int y = 0; y < h; ++y) { auto p = y * stride; auto i = p; //current index auto l = -(dimension + 1); //left index auto r = dimension; //right index int acc = 0; //sliding accumulator //initial accumulation for (int x = l; x < r; ++x) { auto id = _gaussianEdgeExtend(end, x) + p; acc += A(src[id]); } //perform filtering for (int x = 0; x < w; ++x, ++r, ++l) { auto rid = _gaussianEdgeExtend(end, r) + p; auto lid = _gaussianEdgeExtend(end, l) + p; acc += A(src[rid]) - A(src[lid]); //ignored rounding for the performance. It should be originally: acc * iarr dst[i++] = ALPHA_BLEND(color, static_cast(acc * iarr)); } } } static void _shift(uint32_t** dst, uint32_t** src, int dstride, int sstride, int wmax, int hmax, const RenderRegion& bbox, SwPoint offset, SwSize& size) { size.w = bbox.max.x - bbox.min.x; size.h = bbox.max.y - bbox.min.y; //shift if (offset.x < 0) { *src -= offset.x; size.w += offset.x; } else { *dst += offset.x; size.w -= offset.x; } if (offset.y < 0) { *src -= (offset.y * sstride); size.h += offset.y; } else { *dst += (offset.y * dstride); size.h -= offset.y; } } static void _dropShadowNoFilter(uint32_t* dst, uint32_t* src, int dstride, int sstride, int dw, int dh, const RenderRegion& bbox, const SwPoint& offset, uint32_t color, uint8_t opacity, bool direct) { src += (bbox.min.y * sstride + bbox.min.x); dst += (bbox.min.y * dstride + bbox.min.x); SwSize size; _shift(&dst, &src, dstride, sstride, dw, dh, bbox, offset, size); for (auto y = 0; y < size.h; ++y) { auto s2 = src; auto d2 = dst; for (int x = 0; x < size.w; ++x, ++d2, ++s2) { auto a = MULTIPLY(opacity, A(*s2)); if (!direct || a == 255) *d2 = ALPHA_BLEND(color, a); else *d2 = INTERPOLATE(color, *d2, a); } src += sstride; dst += dstride; } } static void _dropShadowNoFilter(SwImage* dimg, SwImage* simg, const RenderRegion& bbox, const SwPoint& offset, uint32_t color) { int dstride = dimg->stride; int sstride = simg->stride; //shadow image _dropShadowNoFilter(dimg->buf32, simg->buf32, dstride, sstride, dimg->w, dimg->h, bbox, offset, color, 255, false); //original image auto src = simg->buf32 + (bbox.min.y * sstride + bbox.min.x); auto dst = dimg->buf32 + (bbox.min.y * dstride + bbox.min.x); for (auto y = 0; y < (bbox.max.y - bbox.min.y); ++y) { auto s = src; auto d = dst; for (int x = 0; x < (bbox.max.x - bbox.min.x); ++x, ++d, ++s) { *d = *s + ALPHA_BLEND(*d, IA(*s)); } src += sstride; dst += dstride; } } static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, int dw, int dh, const RenderRegion& bbox, const SwPoint& offset, uint8_t opacity, bool direct) { src += (bbox.min.y * sstride + bbox.min.x); dst += (bbox.min.y * dstride + bbox.min.x); SwSize size; _shift(&dst, &src, dstride, sstride, dw, dh, bbox, offset, size); for (auto y = 0; y < size.h; ++y) { if (direct) rasterTranslucentPixel32(dst, src, size.w, opacity); else rasterPixel32(dst, src, size.w, opacity); src += sstride; dst += dstride; } } bool effectDropShadowRegion(RenderEffectDropShadow* params) { //region expansion for feathering auto& bbox = params->extend; auto& offset = static_cast(params->rd)->offset; auto extra = static_cast(params->rd)->extends; bbox.min = {-extra, -extra}; bbox.max = {extra, extra}; if (offset.x < 0) bbox.min.x += (int32_t) offset.x; else bbox.max.x += offset.x; if (offset.y < 0) bbox.min.y += (int32_t) offset.y; else bbox.max.y += offset.y; return true; } void effectDropShadowUpdate(RenderEffectDropShadow* params, const Matrix& transform) { if (!params->rd) params->rd = tvg::malloc(sizeof(SwDropShadow)); auto rd = static_cast(params->rd); //compute box kernel sizes auto scale = sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality); //invalid if (params->color[3] == 0) { params->valid = false; return; } //offset auto radian = tvg::deg2rad(90.0f - params->angle) - tvg::radian(transform); rd->offset = {(int32_t)((params->distance * scale) * cosf(radian)), (int32_t)(-1.0f * (params->distance * scale) * sinf(radian))}; params->valid = true; } //A quite same integration with effectGaussianBlur(). See it for detailed comments. //surface[0]: the original image, to overlay it into the filtered image. //surface[1]: temporary buffer for generating the filtered image. bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, bool direct) { //FIXME: if the body is partially visible due to clipping, the shadow also becomes partially visible. auto data = static_cast(params->rd); auto& bbox = cmp->bbox; auto w = (bbox.max.x - bbox.min.x); auto h = (bbox.max.y - bbox.min.y); //outside the screen if (abs(data->offset.x) >= w || abs(data->offset.y) >= h) return true; SwImage* buffer[] = {&surface[0]->compositor->image, &surface[1]->compositor->image}; auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255); auto stride = cmp->image.stride; auto front = cmp->image.buf32; auto back = buffer[1]->buf32; auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3]; TVGLOG("SW_ENGINE", "DropShadow region(%d, %d, %d, %d) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level); //no filter required if (data->extends == 0) { if (direct) { _dropShadowNoFilter(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, cmp->image.stride, cmp->recoverSfc->w, cmp->recoverSfc->h, bbox, data->offset, color, opacity, direct); } else { _dropShadowNoFilter(buffer[1], &cmp->image, bbox, data->offset, color); std::swap(cmp->image.buf32, buffer[1]->buf32); } return true; } //saving the original image in order to overlay it into the filtered image. _dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false); std::swap(front, buffer[0]->buf32); std::swap(front, back); //horizontal for (int i = 1; i < data->level; ++i) { _dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[i], color, false); std::swap(front, back); } //vertical rasterXYFlip(front, back, stride, w, h, bbox, false); std::swap(front, back); for (int i = 0; i < data->level; ++i) { _dropShadowFilter(back, front, stride, h, w, bbox, data->kernel[i], color, true); std::swap(front, back); } rasterXYFlip(front, back, stride, h, w, bbox, true); std::swap(cmp->image.buf32, back); //draw to the main surface directly if (direct) { _dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, cmp->image.stride, cmp->recoverSfc->w, cmp->recoverSfc->h, bbox, data->offset, opacity, direct); std::swap(cmp->image.buf32, buffer[0]->buf32); return true; } //draw to the intermediate surface rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h); _dropShadowShift(buffer[1]->buf32, cmp->image.buf32, buffer[1]->stride, cmp->image.stride, buffer[1]->w, buffer[1]->h, bbox, data->offset, opacity, direct); std::swap(cmp->image.buf32, buffer[1]->buf32); //compositing shadow and body auto s = buffer[0]->buf32 + (bbox.min.y * buffer[0]->stride + bbox.min.x); auto d = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); for (auto y = 0; y < h; ++y) { rasterTranslucentPixel32(d, s, w, 255); s += buffer[0]->stride; d += cmp->image.stride; } return true; } /************************************************************************/ /* Fill Implementation */ /************************************************************************/ void effectFillUpdate(RenderEffectFill* params) { params->valid = true; } bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct) { auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3]; auto& bbox = cmp->bbox; auto w = size_t(bbox.max.x - bbox.min.x); auto h = size_t(bbox.max.y - bbox.min.y); auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255); TVGLOG("SW_ENGINE", "Fill region(%d, %d, %d, %d), param(%d %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->color[0], params->color[1], params->color[2], params->color[3]); if (direct) { auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x); auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); for (size_t y = 0; y < h; ++y) { auto dst = dbuffer; auto src = sbuffer; for (size_t x = 0; x < w; ++x, ++dst, ++src) { auto a = MULTIPLY(opacity, A(*src)); auto tmp = ALPHA_BLEND(color, a); *dst = tmp + ALPHA_BLEND(*dst, 255 - a); } dbuffer += cmp->image.stride; sbuffer += cmp->recoverSfc->stride; } cmp->valid = true; //no need the subsequent composition } else { auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); for (size_t y = 0; y < h; ++y) { auto dst = dbuffer; for (size_t x = 0; x < w; ++x, ++dst) { *dst = ALPHA_BLEND(color, MULTIPLY(opacity, A(*dst))); } dbuffer += cmp->image.stride; } } return true; } /************************************************************************/ /* Tint Implementation */ /************************************************************************/ void effectTintUpdate(RenderEffectTint* params) { params->valid = (params->intensity > 0); } bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct) { auto& bbox = cmp->bbox; auto w = size_t(bbox.max.x - bbox.min.x); auto h = size_t(bbox.max.y - bbox.min.y); auto black = cmp->recoverSfc->join(params->black[0], params->black[1], params->black[2], 255); auto white = cmp->recoverSfc->join(params->white[0], params->white[1], params->white[2], 255); auto opacity = cmp->opacity; auto luma = cmp->recoverSfc->alphas[2]; //luma function TVGLOG("SW_ENGINE", "Tint region(%d, %d, %d, %d), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity); if (direct) { auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x); auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); for (size_t y = 0; y < h; ++y) { auto dst = dbuffer; auto src = sbuffer; for (size_t x = 0; x < w; ++x, ++dst, ++src) { auto val = INTERPOLATE(white, black, luma((uint8_t*)src)); if (params->intensity < 255) val = INTERPOLATE(val, *src, params->intensity); *dst = INTERPOLATE(val, *dst, MULTIPLY(opacity, A(*src))); } dbuffer += cmp->image.stride; sbuffer += cmp->recoverSfc->stride; } cmp->valid = true; //no need the subsequent composition } else { auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); for (size_t y = 0; y < h; ++y) { auto dst = dbuffer; for (size_t x = 0; x < w; ++x, ++dst) { auto val = INTERPOLATE(white, black, luma((uint8_t*)&dst)); if (params->intensity < 255) val = INTERPOLATE(val, *dst, params->intensity); *dst = ALPHA_BLEND(val, MULTIPLY(opacity, A(*dst))); } dbuffer += cmp->image.stride; } } return true; } /************************************************************************/ /* Tritone Implementation */ /************************************************************************/ static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l) { if (l < 128) { auto a = std::min(l * 2, 255); return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a); } else { auto a = 2 * std::max(0, l - 128); return ALPHA_BLEND(m, 255 - a) + ALPHA_BLEND(h, a); } } void effectTritoneUpdate(RenderEffectTritone* params) { params->valid = (params->blender < 255); } bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct) { auto& bbox = cmp->bbox; auto w = size_t(bbox.max.x - bbox.min.x); auto h = size_t(bbox.max.y - bbox.min.y); auto shadow = cmp->recoverSfc->join(params->shadow[0], params->shadow[1], params->shadow[2], 255); auto midtone = cmp->recoverSfc->join(params->midtone[0], params->midtone[1], params->midtone[2], 255); auto highlight = cmp->recoverSfc->join(params->highlight[0], params->highlight[1], params->highlight[2], 255); auto opacity = cmp->opacity; auto luma = cmp->recoverSfc->alphas[2]; //luma function TVGLOG("SW_ENGINE", "Tritone region(%d, %d, %d, %d), param(%d %d %d, %d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->shadow[0], params->shadow[1], params->shadow[2], params->midtone[0], params->midtone[1], params->midtone[2], params->highlight[0], params->highlight[1], params->highlight[2], params->blender); if (direct) { auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x); auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); for (size_t y = 0; y < h; ++y) { auto dst = dbuffer; auto src = sbuffer; if (params->blender == 0) { for (size_t x = 0; x < w; ++x, ++dst, ++src) { *dst = INTERPOLATE(_trintone(shadow, midtone, highlight, luma((uint8_t*)src)), *dst, MULTIPLY(opacity, A(*src))); } } else { for (size_t x = 0; x < w; ++x, ++dst, ++src) { *dst = INTERPOLATE(INTERPOLATE(*src, _trintone(shadow, midtone, highlight, luma((uint8_t*)src)), params->blender), *dst, MULTIPLY(opacity, A(*src))); } } dbuffer += cmp->image.stride; sbuffer += cmp->recoverSfc->stride; } cmp->valid = true; //no need the subsequent composition } else { auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); for (size_t y = 0; y < h; ++y) { auto dst = dbuffer; if (params->blender == 0) { for (size_t x = 0; x < w; ++x, ++dst) { *dst = ALPHA_BLEND(_trintone(shadow, midtone, highlight, luma((uint8_t*)dst)), MULTIPLY(A(*dst), opacity)); } } else { for (size_t x = 0; x < w; ++x, ++dst) { *dst = ALPHA_BLEND(INTERPOLATE(*dst, _trintone(shadow, midtone, highlight, luma((uint8_t*)dst)), params->blender), MULTIPLY(A(*dst), opacity)); } } dbuffer += cmp->image.stride; } } return true; }external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-eval.h000664 001750 001750 00000001717 15164251010 044420 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_EVAL_H #define ECMA_EVAL_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup eval eval * @{ */ ecma_value_t ecma_op_eval (ecma_value_t source_code, uint32_t parse_opts); ecma_value_t ecma_op_eval_chars_buffer (void *source_p, uint32_t parse_opts); /** * @} * @} */ #endif /* !ECMA_EVAL_H */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-syntaxerror-prototype.cpp000664 001750 001750 00000002271 15164251010 053041 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-syntaxerror-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID syntax_error_prototype #include "ecma-builtin-internal-routines-template.inc.h" #endif /* JERRY_BUILTIN_ERRORS */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-date.inc.h000664 001750 001750 00000002456 15164251010 047535 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Date built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_DATE /* ECMA-262 v5, 15.9.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_DATE_PROTOTYPE, ECMA_PROPERTY_FIXED) NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 7, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) ROUTINE (LIT_MAGIC_STRING_PARSE, ECMA_DATE_ROUTINE_PARSE, 1, 1) ROUTINE (LIT_MAGIC_STRING_UTC_U, ECMA_DATE_ROUTINE_UTC, NON_FIXED, 7) ROUTINE (LIT_MAGIC_STRING_NOW, ECMA_DATE_ROUTINE_NOW, 0, 0) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_DATE_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_DATE */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/png/meson.build000664 001750 001750 00000000336 15164251010 034365 0ustar00ddennedyddennedy000000 000000 source_file = [ 'tvgLodePng.h', 'tvgPngLoader.h', 'tvgLodePng.cpp', 'tvgPngLoader.cpp', ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/re-parser.h000664 001750 001750 00000002400 15164251010 044152 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 RE_PARSER_H #define RE_PARSER_H #include "re-compiler-context.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup parser Parser * @{ * * \addtogroup regexparser Regular expression * @{ * * \addtogroup regexparser_parser Parser * @{ */ /** * @} * * \addtogroup regexparser_parser Parser * @{ */ /** * Value used for infinite quantifier. */ #define RE_INFINITY UINT32_MAX /** * Maximum decimal value of an octal escape */ #define RE_MAX_OCTAL_VALUE 0xff ecma_value_t re_parse_alternative (re_compiler_ctx_t *re_ctx_p, bool expect_eof); /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ #endif /* !RE_PARSER_H */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/stack.h000664 001750 001750 00000015773 15164251010 037756 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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" #include #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 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_); // NOLINT (+clang-analyzer-unix.Malloc) 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 RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { // Expand the stack if needed if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) Expand(count); } template RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { Reserve(count); return PushUnsafe(count); } template RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { RAPIDJSON_ASSERT(stackTop_); RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; return ret; } template T* Pop(size_t count) { RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); stackTop_ -= count * sizeof(T); return reinterpret_cast(stackTop_); } template T* Top() { RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); return reinterpret_cast(stackTop_ - sizeof(T)); } template const T* Top() const { RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); return reinterpret_cast(stackTop_ - sizeof(T)); } template T* End() { return reinterpret_cast(stackTop_); } template const T* End() const { return reinterpret_cast(stackTop_); } template T* Bottom() { return reinterpret_cast(stack_); } template const T* Bottom() const { return reinterpret_cast(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(stackTop_ - stack_); } size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } private: template 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(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_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/000775 001750 001750 00000000000 15164251010 032372 5ustar00ddennedyddennedy000000 000000 thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-bigint-object.h000664 001750 001750 00000001766 15164251010 046215 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BIGINT_OBJECT_H #define ECMA_BIGINT_OBJECT_H #include "ecma-globals.h" #if JERRY_BUILTIN_BIGINT /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabigintobject ECMA BigInt object related routines * @{ */ ecma_value_t ecma_op_create_bigint_object (ecma_value_t arg); /** * @} * @} */ #endif /* JERRY_BUILTIN_BIGINT */ #endif /* !ECMA_BIGINT_OBJECT_H */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-dataview-prototype.cpp000664 001750 001750 00000020575 15164251010 052254 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-arraybuffer-object.h" #include "ecma-dataview-object.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #if JERRY_BUILTIN_DATAVIEW #if !JERRY_BUILTIN_TYPEDARRAY #error "DataView builtin requires ES2015 TypedArray builtin" #endif /* !JERRY_BUILTIN_TYPEDARRAY */ #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_DATAVIEW_PROTOTYPE_ROUTINE_START = 0, ECMA_DATAVIEW_PROTOTYPE_BUFFER_GETTER, ECMA_DATAVIEW_PROTOTYPE_BYTE_LENGTH_GETTER, ECMA_DATAVIEW_PROTOTYPE_BYTE_OFFSET_GETTER, ECMA_DATAVIEW_PROTOTYPE_GET_INT8, ECMA_DATAVIEW_PROTOTYPE_GET_UINT8, ECMA_DATAVIEW_PROTOTYPE_GET_UINT8_CLAMPED, /* unused value */ ECMA_DATAVIEW_PROTOTYPE_GET_INT16, ECMA_DATAVIEW_PROTOTYPE_GET_UINT16, ECMA_DATAVIEW_PROTOTYPE_GET_INT32, ECMA_DATAVIEW_PROTOTYPE_GET_UINT32, ECMA_DATAVIEW_PROTOTYPE_GET_FLOAT32, #if JERRY_NUMBER_TYPE_FLOAT64 ECMA_DATAVIEW_PROTOTYPE_GET_FLOAT64, #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT ECMA_DATAVIEW_PROTOTYPE_GET_BIGINT64, ECMA_DATAVIEW_PROTOTYPE_GET_BIGUINT64, #endif /* JERRY_BUILTIN_BIGINT */ ECMA_DATAVIEW_PROTOTYPE_SET_INT8, ECMA_DATAVIEW_PROTOTYPE_SET_UINT8, ECMA_DATAVIEW_PROTOTYPE_SET_UINT8_CLAMPED, /* unused value */ ECMA_DATAVIEW_PROTOTYPE_SET_INT16, ECMA_DATAVIEW_PROTOTYPE_SET_UINT16, ECMA_DATAVIEW_PROTOTYPE_SET_INT32, ECMA_DATAVIEW_PROTOTYPE_SET_UINT32, ECMA_DATAVIEW_PROTOTYPE_SET_FLOAT32, #if JERRY_NUMBER_TYPE_FLOAT64 ECMA_DATAVIEW_PROTOTYPE_SET_FLOAT64, #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT ECMA_DATAVIEW_PROTOTYPE_SET_BIGINT64, ECMA_DATAVIEW_PROTOTYPE_SET_BIGUINT64, #endif /* JERRY_BUILTIN_BIGINT */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-dataview-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID dataview_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup dataviewprototype ECMA DataView.prototype object built-in * @{ */ /** * The DataView.prototype object's {buffer, byteOffset, byteLength} getters * * See also: * ECMA-262 v6, 24.2.4.1 * ECMA-262 v6, 24.2.4.2 * ECMA-262 v6, 24.2.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_dataview_prototype_object_getters (ecma_value_t this_arg, /**< this argument */ uint16_t builtin_routine_id) /**< built-in wide routine identifier */ { ecma_dataview_object_t *obj_p = ecma_op_dataview_get_object (this_arg); if (JERRY_UNLIKELY (obj_p == NULL)) { return ECMA_VALUE_ERROR; } switch (builtin_routine_id) { case ECMA_DATAVIEW_PROTOTYPE_BUFFER_GETTER: { ecma_object_t *buffer_p = obj_p->buffer_p; ecma_ref_object (buffer_p); return ecma_make_object_value (buffer_p); } case ECMA_DATAVIEW_PROTOTYPE_BYTE_LENGTH_GETTER: { if (ecma_arraybuffer_is_detached (obj_p->buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } return ecma_make_uint32_value (obj_p->header.u.cls.u3.length); } default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATAVIEW_PROTOTYPE_BYTE_OFFSET_GETTER); if (ecma_arraybuffer_is_detached (obj_p->buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } return ecma_make_uint32_value (obj_p->byte_offset); } } } /* ecma_builtin_dataview_prototype_object_getters */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_dataview_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); switch (builtin_routine_id) { case ECMA_DATAVIEW_PROTOTYPE_BUFFER_GETTER: case ECMA_DATAVIEW_PROTOTYPE_BYTE_LENGTH_GETTER: case ECMA_DATAVIEW_PROTOTYPE_BYTE_OFFSET_GETTER: { return ecma_builtin_dataview_prototype_object_getters (this_arg, builtin_routine_id); } case ECMA_DATAVIEW_PROTOTYPE_GET_FLOAT32: #if JERRY_NUMBER_TYPE_FLOAT64 case ECMA_DATAVIEW_PROTOTYPE_GET_FLOAT64: #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ case ECMA_DATAVIEW_PROTOTYPE_GET_INT16: case ECMA_DATAVIEW_PROTOTYPE_GET_INT32: case ECMA_DATAVIEW_PROTOTYPE_GET_UINT16: case ECMA_DATAVIEW_PROTOTYPE_GET_UINT32: #if JERRY_BUILTIN_BIGINT case ECMA_DATAVIEW_PROTOTYPE_GET_BIGINT64: case ECMA_DATAVIEW_PROTOTYPE_GET_BIGUINT64: #endif /* JERRY_BUILTIN_BIGINT */ { ecma_typedarray_type_t id = (ecma_typedarray_type_t) (builtin_routine_id - ECMA_DATAVIEW_PROTOTYPE_GET_INT8); return ecma_op_dataview_get_set_view_value (this_arg, arguments_list_p[0], arguments_list_p[1], ECMA_VALUE_EMPTY, id); } case ECMA_DATAVIEW_PROTOTYPE_SET_FLOAT32: #if JERRY_NUMBER_TYPE_FLOAT64 case ECMA_DATAVIEW_PROTOTYPE_SET_FLOAT64: #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ case ECMA_DATAVIEW_PROTOTYPE_SET_INT16: case ECMA_DATAVIEW_PROTOTYPE_SET_INT32: case ECMA_DATAVIEW_PROTOTYPE_SET_UINT16: case ECMA_DATAVIEW_PROTOTYPE_SET_UINT32: #if JERRY_BUILTIN_BIGINT case ECMA_DATAVIEW_PROTOTYPE_SET_BIGINT64: case ECMA_DATAVIEW_PROTOTYPE_SET_BIGUINT64: #endif /* JERRY_BUILTIN_BIGINT */ { ecma_typedarray_type_t id = (ecma_typedarray_type_t) (builtin_routine_id - ECMA_DATAVIEW_PROTOTYPE_SET_INT8); return ecma_op_dataview_get_set_view_value (this_arg, arguments_list_p[0], arguments_list_p[2], arguments_list_p[1], id); } case ECMA_DATAVIEW_PROTOTYPE_GET_INT8: case ECMA_DATAVIEW_PROTOTYPE_GET_UINT8: { ecma_typedarray_type_t id = (ecma_typedarray_type_t) (builtin_routine_id - ECMA_DATAVIEW_PROTOTYPE_GET_INT8); return ecma_op_dataview_get_set_view_value (this_arg, arguments_list_p[0], ECMA_VALUE_FALSE, ECMA_VALUE_EMPTY, id); } default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATAVIEW_PROTOTYPE_SET_INT8 || builtin_routine_id == ECMA_DATAVIEW_PROTOTYPE_SET_UINT8); ecma_typedarray_type_t id = (ecma_typedarray_type_t) (builtin_routine_id - ECMA_DATAVIEW_PROTOTYPE_SET_INT8); return ecma_op_dataview_get_set_view_value (this_arg, arguments_list_p[0], ECMA_VALUE_FALSE, arguments_list_p[1], id); } } } /* ecma_builtin_dataview_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_DATAVIEW */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-evalerror-prototype.cpp000664 001750 001750 00000002265 15164251010 052445 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-evalerror-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID eval_error_prototype #include "ecma-builtin-internal-routines-template.inc.h" #endif /* JERRY_BUILTIN_ERRORS */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlRenderPass.h000664 001750 001750 00000005114 15164251010 036706 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_RENDER_PASS_H_ #define _TVG_GL_RENDER_PASS_H_ #include "tvgGlCommon.h" #include "tvgGlRenderTask.h" #include "tvgGlRenderTarget.h" class GlProgram; class GlRenderPass { public: GlRenderPass(GlRenderTarget* fbo); GlRenderPass(GlRenderPass&& other); ~GlRenderPass(); bool isEmpty() const { return mFbo == nullptr; } void addRenderTask(GlRenderTask* task); GLuint getFboId() { return mFbo->fbo; } GLuint getTextureId() { return mFbo->colorTex; } const RenderRegion& getViewport() const { return mFbo->viewport; } uint32_t getFboWidth() const { return mFbo->width; } uint32_t getFboHeight() const { return mFbo->height; } const Matrix& getViewMatrix() const { return mViewMatrix; } template T* endRenderPass(GlProgram* program, GLuint targetFbo) { int32_t maxDepth = mDrawDepth + 1; for (uint32_t i = 0; i < mTasks.count; i++) { mTasks[i]->normalizeDrawDepth(maxDepth); } auto task = new T(program, targetFbo, mFbo, std::move(mTasks)); task->setRenderSize(mFbo->viewport.w(), mFbo->viewport.h()); return task; } int nextDrawDepth() { return ++mDrawDepth; } void setDrawDepth(int32_t depth) { mDrawDepth = depth; } GlRenderTarget* getFbo() { return mFbo; } private: GlRenderTarget* mFbo; Array mTasks = {}; int32_t mDrawDepth = 0; Matrix mViewMatrix = {}; }; #endif // _TVG_GL_RENDER_PASS_H_src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/rawimage_200x300.raw000664 001750 001750 00000724600 15164251010 035443 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0kkl~l}l{kylxjtjrjsjtjzj|j|k}k}k{jzj{iyhugsfpfpenemfoepeqergthuiviwiwjzi|i{k}k}k~klkk~k~klk~k|kzjxkwisiqioipiqisiririqiphphnhkgghfhehehdhcibicjbjbi`j`i_h]h]i]h\h]h\gZhYh[h\h[gYgYfXgWfWfVeTeTfUeTeUeTeTeSdSeTfXiZhZi\h]h^g_hah_e^d]c]c]b\a\a\b`bcepetdueweze|e{dwcsapaoaoanamap`n_l^i^h^m`q`s_sa{b`{_t^r^r^u_u]s\r\p[kZjZjYgXbW`XcYiZkYkXjXjYp\{]~\}[y[vYoWgWgWgXiYkXjXiXjWjWjViWlXpWqXrWsYvXuVnVkVlUjSdkkl}l{lzmykvktktlrjukzl~ml|kwkwkxkyjvhtirirhrgpfofpepergrhuhviwiyiyj{j|j|j}k~lllmlllmm~l|kzlylykvjrjpjpjqjriqjsjrjsjsiqipikhjihificibjbjcjcjcibk`j`j`j^i^i]i[h[iYiYiYh[hZhZh[hZgXhXgXgWhXgWfVfVfVfUfUeVfWgXh\j]j^g_f_fahdidhbfbc_c_b`b`b`a`cffqgvgyg|ghgf}dzcubr`l_h_g_h_i`j_j^f^g^las_taza}`x^q^o^n^r^s]r\o\m[jZjZhYdYcYcXcYeYeXgYgYiYl\v\|\{\z\wZrYlXjXjYmYnYlXlXoYoXmWmXoXqXsXsXuYuXrVlUfUeUeTckkmmnnmzmwlvkulwn}onlzktktlvlxjujtirjtktishshqfrgqgrhuiwiyk|j}j|j~j~k~kllmlmlmlmm~l{lxlylwltkqlpkqkpkqjqjrksktkuktkqjmjlihjfjeieidiekeiajcjbjbjbi_i\j]j\j[iYiYj[i[j\i[h[hXhXiYhZhYhYgWgVfUfVfVfXgYg\i^jai`icicifihhdhdgfgeeceddedededheqgxg}hhhgf~f}eycs`j`i_h_h`j`l`l^f]c^g`n`q`s`s_n\j[i\h]k]o]n\k[hZhYfZdXbXaW`X^W_XaXdXeYhZj[q]y[x\x\w[s[pZlZnZqZqYoZrZsZrZqYqZsZtXsYsXsXrXoVgT`T`T_S^mlmmnnm|nzmzmym{o~onlykslsluluktktltjskvkuisirhrhqhsiuiwi{k}j~kk~kkklmmmmmlmmmm{mzmxlvlulsmqlolololpkpkrktlvkujtkskpjljkkijgifiekejdjdkejdjblbkak^k\j[jYjZj[j[j]i\iZiXiXiZjZiZiXgUgWgVgWgXhYh\h]h_iagckfkglgiffbgcfdfdeeefefegfjemgth{h}ggghf~e}dzcrajaiaj`i`j`n`k_h^e]f^i_m`p_l^h]g\e\e\h]j]kZeYbXaXaYaX`X`W]W\W]W_XaXdYfYj[r[t\v\w\w[r[m[k[p[rZqZp[v[wZtZsZtZtZsXrXpWoWoVkVdT^S\S[SZpomlmmmm|m|n}o~pon~mxltltmsmsmrlrlslskukvksiriqiririujxj{k}llmmmnmmnnmmmnnno~o|nynwnvmtlqnpmomolplplpltlumvlvkulrkpknklkkkjlilhkfkgkfkfjeldlbl`l_l]k[k[k\k]k^k]j[jYjYiZj[k[hXgWiVhVhXhYi[i]i^h`g`idjgkhlhkggcgdgegdggfhghfjelfofthyh|ihgg~ggezesakai`h`i_h_i_j_i^g^g`l`n_n_j]f\e\e\f\g\i\i[fYcYaZbYcYaXaX`X_XaXaXaYdXfZk[p\r[t]x\v[pZjZj\q\sZqZq[v[vZrZsZtZsYrYqXqYqXmWiVdT_R[SZRYpoonmnmooopppo|nwmvmrnrnqnqnqmsmtnumvmvksjqiqjsitjwkxkzl~mnnnnnnnonmnoonoo~ozoxoxnvmsnroqnpmonomonqmsmsmumumtmrlplplnlmlmkkljljkgkhlglfldmclal^l]l]l\l]l]l]k\k[jZkZj[j\jZhWiVhVhXiZi\i]i^i_i`hcidlhmikhjgkhkjkjikhigiglgogpgshxi}h~hh~g~f}f~f|eubnah`f`h_f_g_j^i^g^h`m_n`n^j\c\b]f]h\i\j]k\i\g[f[h[gZfZeZfYfZfZfXdXcXeZiZk\o\s\v]u[pZkZm\r\u[s[r\t[sZqZrZtYsZuZtZuYsXnWhVeT`S]SZRXpqqpoonnopqqqp{ozoxotosornpnqorosotnwowlslqjpksjtjukwlyl}nnnnonnopooooooooo}ozpzozovouornooooonnnononpnqnrmrmrmpmomomnmmmnllljljljlhlglfmemdmcm`l^m_l]l]l]l\l\k[k[k[k[jZjXjWjWjYj[jZj\j^i_i`jbjbjckgkgjfmimklkjlgifhhngpgqhrhuhzj}ih|f{g{g|f{etclah`e`e_f_i_i_h_g_g_i_k`m^h]d\`[c]k\k]k[l]l\g\i]n\m\m\k[l[m[kYgYfXdYeYfYiZl\o]w]v\r\o\o\r]u]u[r[r[r[p[rZs[t[vYrZqYpXlVgVeU`T\RZSYqqrrqqpppprrrqq}pyqwpsqsqrpqpqqsptqwqwotnqmqlsltlumwmym|nnonopoppppppppoqpqqq}pyqxotorpqopnnonononoonpnpnpnonnnononnnnmmmmmmmmlilimimhmgmgnencncmbm`m_m^m]l\l[kZl[kYkXkXjXk[k\k\j\j^k`jaiaj`k`ldkdmfoimjkjjjgigjhritishrhthvi{i~i}hzfzg{fyfublaiag`f`h`i_j^g^g]e^f_h_i^f]d\d\d]l]o^p]q]q\m\m^p^q]p]q]r]q\nZjZgZgZgZgZh[m]r^w^x^u]s]s^t_w^u]s]q\r\r]u\v\v[sYmXiXiYiWgWeVaT\SWRUrrrsrsrpqqqstsr~q{qxpurvruqtpsqtruqvrwqvprpspunvmvnwmyn{n~ppprqqqqqqqppppppqrrr}r|qwptqtprpopnpmpnonpnoonnnmnnnmnnononnnnnnnmmnmnlnlmkmkmjminimfmfnemcm`m_m^m\l\l\m[lYlXlZk]k]k]k_k_kaj`k`j`jalcmeohqknikiiihigljvjxjxirhqhsiyj}i~h|h|h|gzfxcobkaibkaj`lal_k^i^g_h^h_h^i]i\h\j]m]q^u^u]v]u\s]s^s]s^u^v]s\o[kZjYjZkZk[l\p^v_y_z_z^w_v_v`v^v^s^q\n[n[r[r[qZlXfWaVaWaWbV`U^T[SVSUsssstttssrrsttsr|r{rxrxswrwrvrururvruqvruruqvqwpxoyozo{o}opqrsrrrrrqqppppqqqrssrzpvrwrtqqqoroplpmpmpmpnoonnonomomonooonnonnnmnmmlnnnnnonnnlnlokpiogncn`m_m^m]l\m\lZl[l[l\l^l^k_kakbkak`kaj`ldnfohnhlhkhkklmjokvkzkxjqiohrivi{h{i}i}g{gyhwcpbkbkbkbmbnbpap`o`l_l_l`m`o_p_o]o]q^u^w^w^x^x]y]y^v]v_y_x\r[nZkZiZhZk[n[p]r^w_{_{_z_x_w^t`t^q]m]m\j[j\n[m[kYfWaU\UZVZU[VZTYTWSWSUssttuvuuussrsstts~r|t}t}t|tzswtwswsysztytxtyszszs{q{q{q~qqrrssstsssrrrrqrrrrttss}t{swqrqpqprormsmrkrkqlplpnpnqppppppppqpppoooonooopoqorpqooolojpiofododncnan_n`m_m_m_m_m_m_lalblambkalblblcnfnglgmhnhmimlmplulxlwjogmgqhtiuj{j~i}gyfvfrdmbibicnbpcqbsctbuasarar`r_s_u`u_u^u^x_y^y^z]y^z^z_z^z_{^x\r\o[jZfZfZh\n]r]u^x_{_{_{`|_x_r\k\i[g[h[g[i[k[jYgWcV_U[VYUWTVTWTXTWTXUZruuvvwwvuutssstttttuvvu|uztzt}u~t|uztztzu|t{t}s}r~rsrssuuuttttttssstssstutt|sxsurrsqsqrnrmrkrirjrkskqmpnqoqpppqrqsptpsqrptquququotqrpoompkpkpjoioinfnfoendndmam`n_m`mambmcldldkdkdlcmdnfohqkoimimmmnksnymvjnhmgphshui{ii~hzfsdmcibfbfdkdpcrdudxdwbvavau`v`v`w`x`x`x_y^z_z_{]z^y^y_z_{^z^y^u]p[jZdZd[h\l]s]v^y_y`z`z^x^s\k[eZe[eZd[e[f[fZeXdXcXaV]VYUWSVUWTXTYTYU\uuuvvwvvvvutsssuuuuvvwwu}v~xvu}v}v|u}wwv~u~uuvvwwvwxwvvvuuuttttutuuuutzsustttttsrsnsksjtjsitjskrjrjrkqmqoqpqpqqqrqrqtqvqwrvqvqtpqpnpnpopopopnomojphognenbn`nandnfnfmelelfldmdmcndmfpiphmimmllmrowmsjnhlhmhohuj{jjhzfpekcfcdcfcidndsdvewdxdwav`wbxayawaxax`y_y`z_{_{_z_y_x_x_y_{`|_z^u\nZf[f\h]o]u_x_y`y_y_w_s]k[d[dZc[cZa[aYaY^Y`YbYbXbV^V[UYUWUXTZU[U\U]vvuuuwvuvvvuttstutvxxxzww~xx~w|v}v~xyx~x|w}wwxxxzyyyyxxvvvuuuuuvuvuuvvuzuyuvtttstptmsjukujuktjsftfsfrgrjslrlrnrororrrsrururvquqrqqppprrtqwpvptopnloknhofoeoenfohohnfmfmemdmcmcmbmeoepgpjoknlnqotkqknimhjgkgqjzj~j}ixgqekcecdcgdkeneqdrdscucubtbubyaxbxawaw`x`x`y_|`{_y_z`z`{_{`~```}^u]k\h]k^s_z`|`|a|`z_v_r\k[e[d[dZbZaZ_Z]Y\WZX^YcXbW^W\W\VZVYV\V^V^U_vwvvvvwwwuvuutttuvvxwyzyx~x}x}w|x|xxxx~y|y}y~zyyzz{zzyyywwwwvvwwwxwvwwwvu}vzuyuvurvpumukvkuktjthtftfththtititktltltmsorpsrsursqqqqqrqtrzs|s|s{rwqtpppnplpjninioimhmhnhngnendmcmdndneogohpkplnlkmkojojnhkgkgnhriviuiufnejdecdbgdhdlencmcnbpbpbqarbtauav_u`v`v`xaz`|`{_z`{`}`~``aaa`{]o]j^mawa}aaa~a{_u_q^n\i\f\f[cZaZ`[_Y[XXX[Y]X^W]W\V[V[V\V^V`W`V`wxyxwvwyxxwwwvvvuvxyyyyzyx|x|w|x}yxyyz}z{z|y{y}yyy{{{{yzzyyyyxxxyzxwvxxwxwv|vzuvusvowmvkvkujuiuiujujvjujujukuiuhtiuktlsosrrqrprqrtsys}tt~s|rzqxqtprpqopolnjojoioioinhognfmflflfmenfoiqkqknhkhkllplpikgkgkgmgngngmfkehdeddcfdidickcjbkbmblbkalalao`r_r`s`t`waza{`{`za}aabaabba^q]m^p`wa}ccb|`s`r_q_q^o^m]i[c\a[`[_YZXYXZX\X]W]W]W\V\W_WaWdWeVcvxxxxwwyyzxyxyxxwyyz|{{{{y~y}x~xyx~y~zz}z{yzyzz|zz{z|}}|{{{{{{zyyzzyxxyyyxxww}vzwwvuvqwnwlvlvkvlvlvmvmwmwlvlvivhuhvkulupvqtososrsstvu{u}t}t}t}s{syryqvqupronomokokojojninhnhmimhmhoipjojngmgkglimmlpilhkglgkfjgjghfifgeeeeegdiejckcjclbkaj`iai`j`laoar`s`uawaya{a|a|b~abbbbbba_u_p_taza}bb~aw_m`p_r`s`v`t_n\e\d[c[_Y]Y\X\X]X]W_X]X^X_XaXdYgWgWguvxxxxxxyz{yyyzy{{|}}~~}zyzzyz~z|z|z}z|y{z{z|y{||}|~}}||||||||{zzyzz{zzzxww}x|xzwvwrwovnvmwnwowowowownvlwkvivjuluoururuststsuttuvzu}t~t}u~u~ttts|rzpvprpponpnolomnlnmnlnlonooooolnililimjllkoingmhlhlgkgifgghffddeffiekdldldlclclblalalbmaoaras`ratavaya{a|a{a}aa~a}bbbba`z`xazb}a}b}`x`s`p`r`rawayay_q]k\h\dZa[_Z_Z^Y`Y`Y`X`X_X`XbZfXiXjXhvuwxyyyyz{{{{{|||}~~~}{||{zzy~z}{}{~z|z}{~|{||}~}~}}~~}~~}~}||{{zz{zzyzyy~xzwvwtxtysxrwrxryrxqwowmvmvmvmwnvrwtvvvvvvuutsuuuyu|u~uvuwvvutsrzrwquptqspqoqpqoqpsospupuppnmmlmkmklnkoiojqjohlgkfighghefeffhfkfleldldmcmcococobobrbsatas`qar`tauaxay`waxay`w_wbbbaaa~a~b~b}b~azau`s`q`s`yc~ba{_t_p^n^k\g\f[d[b[cZaYaYbYbYbYbYeYhXjXjvvxyzzy{{{{z{z||}~}}}}}|{|{{~{}{~{{|}}}~~~~}~~~~}}||||||z{{{xx{xxyvxtxsyryrysxrxoxpxqxpxryswuwvwwxxwxwwusvrwtvxv}v~vwwxvwtttr~r{r|r}r|rzrzrzq{q{qzqzpvpqopnqopmplqlpkqjphmhlglgjgjfieifkflfmgofnendodqdqcrdtcubvbuaraq`q`r`r`t`u_s`t_r_r_tb|ca~a}bbbb~b}b~bzbxavauaycdcb}ayawav_r_p^n\h[e[d[dZdZdZdZcZcYdYgYjYlvwxyyzzz{{z{zz||~~~~~|||}|}|~}}~|~~}~~~}||}|}}}||zzx{yyxwxuzuzvyuxtytxtwtxuxvywywxxxyyzwzwwvuwuvtvvwzv{v}wxxxwvutsssttsssssssr{qvqvpvototmqlrkrlriqhrishqhpgnfnfmflfngqfrfqdpdqcqcscucvcxcwbtar`q`q`r`q`s_s_s^p^o`q`uayaza}bccbccb~b|a{b{cedcb~b}c|bz`x`w_s]m]j[i[i[h[hYdYdZdYdZfYiYkywwxyyyyzz{{{z{||~~}~~~}}}~|}|}}}~}~~}}~}~~~}{zy~y|y{y{zzyzyzyzyzz{z|z|z|yzyyy{z|y|wxwwwvwwxxwyx|xxxyxwwvutttttssttttsr|qxrxqypxovntmtmtltktjujuhuiuiugrgpfogqgtgvgtfsfrercscvdxd{cycvbtas`q`o`p`q`s`t`s_q`q`s`v`za~cccbcdddddeeedcccb}a{ay`x`u]q]o]n\m\jZfZdZe[gZh[k[l{zxyyyyyzz{|}{z}||~~~}}}}}~}}}~|{{zzzyz{z{{|{z{{z{{y|x{xywywxxzxyyyzyxvvu{t{s}tuuustttstqzqxqypxpwntounultltktjtisivjwjyhwhtgsgrhuhvgugtftereretdwdze{dxcvbtas`ras`s`vbyc|aw`rasawb{ddedceeefeeedeeddcdb{ax`y`y_w^s]q^p\m[h[g\h\g[g\i[j|}{yyyzzz{z{|||}}|~~~~~}~~|{z{zz|||{||||{|{|{zzyxzxzxzyzxz{zyxu}txswt{t~tuuuut~s}s}r|qxqyr{pvornpnrmrmslulukrjqjujxiwjwhthrgqhthufsgqfqeqdqeseveyezezdwcubtatavbxb{c~cbyaubwczdddddcddeffeeeedeedc~ax`vaway_x^r]o]p^m\k[i\i\f[e\hZe}~}zzz{{{||||}}}}|~~~}}|||||}}||}}}|{|{z{{zy~z~y~yz{{zyzxu{vzuytyu}uuuuts{rxryqxqxq{r|pvnqopnpnrmsmrnsmpjrkviuisithtgogngqgtfpfofoeocncpeugyfzexdvbsbtaubyc{c}cca{bxbzdedeeedddefefeeffdedc{auat`t`t`t^o^m^p]p]n]l\i[f[g[eYc{~}|||}}~~|}}}|}}~~~}||}|}}|}~}~|{||}|zzzyzz{|zxw}w{wzv|w|v|u~vvvtt|rwqvqvqvqypxqxotnrornrnrnsmsmqnqmvlvithrhriqgpfogqgqgoemendndndpetfvevdvevcrcscucyd}d~ccc~c|d|defffffefeedd}dfffecb}av`r`o_o^m^l]k^l]n]p]n]j\gZeYbYaY`|~}}~~}~~}|}}}}~~}}}}}|~~~~}||}}}{{zzz{|{yx}w{xzw{wvv~uvvut}tzrvrvrwrwqwqwouotororososnrnqnrmsmtkriqiohohphphrgrgqfofoeododoereteududvdvcrcrdtcwe|edddedeffgfefeedddb}ceffdb~ayat_n_m_l_k^j]j]k]k^n\j[eZdZcYbY`Y^}~}}}}}~~~~~~}~}}~~}|{|{{||{y{wxwvwwx|xxwwvvvu|tysxsxryqyqypxovouospspuotornqnpnrmqjoimjnjohqishshtgtgrfqerdresftftdrdtcubtbscrbscudzdeeffeffgffgged}d~db}c}ddefcb{aw`q_j_i_k_l_l^l^l^n^p\i[d[e[fZdXaY`}~~~~}}~~~~~~}||{|||{yyxuwtwvx|xxwwwvvu~t|t{s|s{s{q}p|pyoypyqwpvpunsnrmqmrmrjoinkpjqjsiuivhwhwhuftftfteufveudrcsctctbrarbtbwczeeffeffffffefe~dydzc{d|czd|eecbzaxatar_m_k_l^m_l_l^o_p^n\j[e\f[gZdXaX_~}~}~~~~|}}||z|xwwuxwyzxyxwxvvvutttsrrq~p}qzqwpuotounvnumumtlsltkukvjvjwixhygxhwgwfvevewfwetdsesdsbrararbvbxd}deefeffffee|e|d{cwcsctcubvcxd{d~eczbwawavas_q_o^n_o_m^n_q^s^q\l\hZgZgZeYaW_~~~~}~}}|{z{xyxxy{y~yxxww~wwvuuttssrq~pzpwovntovnxmwmxnznymxm{lzkzkyjyixiwhxgwfwfyfyexetdrdrdrbrarasaub{ceegeefddeee}dydxcrbpbqbpapcscwdzdycubvbwbwau`t`r`r_p_o_p_t_u^t]o\k[j[iZfYaY`}~~~~~~}|{zyy~zyyyww}xwvvutstsrqp}o{nxnvowozo{o~oo|nzn{m}l{kzkyizi{hyhxgyg|g~f}dzdwdwcubsctataub{dcegfdeeeffe|ewdscnbmaoamaobocrcrcqbocubwbxbz`v`u`u_u`t`w`x`w_v]t\o\lZiZdYcXb~~~}{{zzzyyxwwxxwvvuttsrqppo|nynyo{pqqrq~m{n}l}kzkyizj}i}h|f{ghhffd|czcycwbtbwc}ddfffeedeff~ezducobkalbnbobnboboamamblbqcxc{b|bzazazaza{a}a|`{_y^v\q\n[j[g[fYe}|||{zzyxxxxvwvvuuttsrqooo~o~pprssro|m~m~l{jxizi}ih~ghhiihfee}cybwbxb}degiffedff~evdtcpbkbjblbmbnbnbnamambmbmbocwc}dcdcccbba}`z^w^u\q\l[e[d[f}~~|{{zyxyyxxwwvuuuttrqonnppprttso|n|m~m~jzh{h~jgghijjiiefd}c{c}deghhffeef|eudqdqcpcnblblbmbmcobobobpbpanbocwc}decddccba`|_z^x^u\mZeZc[f~~~~|||{zyxyxxvvuutttrrppooooqssssp{n|m|k|j}i~iihhijijjkjhfedeghhhgfeeeydseqdqdqcpcpcocococpcqbpbobobpbrcwdeedddbccaaa}`|^w\r\j[f[g~}||||zzzzywuttt~ss~sqpppoopqrqqrp|n|m}l}kijijjjkllklkjhgegijjihffgeyeuesesftetetdsdsdqdsctcqcncobocsdxd~eeeedccba~a}`|`|^x^r]n\k\l}|}|{zzyyxwvuts~strqrqqpppqpopqpommllllkkklmlkkkkjhfijjiihhfhf~fzfwgwfwfwfweveuftewevdtcpbqbrbtcvczeedccdc|bzazax`z_x`v^s^o^o^r~}}}{{{yyxxwvtttssrssqqppoonnopoonnnmmmlmkkkkkllkjiijjiihghhhgg|g{gzgyfyezfzeyfxevdscqcsbsctcvczeedccc{avavawawaw`w_w_u_t_s`v~}}|{zxxxwvuttsssssrrqponmnmnnoonnonmnlkjjjllllljjjiiiiiiihhhhi~f}g}f}g|fzfxevdrcqcqbrcucxc|dd~b~b|bzbwbuaubwawax`z`yaxax`v`v}}||{xxxwvuutssrsrrrqqpnonnnnooppppomlkljjjklllljjjjjikkjiiiiihgggf|eyevdudscqcqcuczd}d~b{aza{bxbvbvawbwbyb|b|b|a{b{bxau~~}}{yxxwvuutttsssssrrqqqppoppppqqrqnlkkkjjijkllkkjkjklkkkkjjjhhiihg~ezewducscscvbyc}b}a|a|a{bybyayb{b{cddd}c~d~c|b|~~}}}zyyzywvvvwvvuuttsrsssrrrstrssrqqomlkkjkjkkmnmmnmlllllklkjjiiijiif}ezcvcwdwcybyaybza|b~b~c|b|b}cddeddd~dcc~~~|{zyzzxwwwxxvwwvtutttusttuvvsrqopnmllkjkjklnmmnnnnmnmmmllllkijjiifd~d|d{c}c~b~b~a}bcccb~ddeeedec}c}de~~~}|{zzyyxxwwxyxxxwwvuvuuuuwwxvtsponmmmmllmmnmnnnnnnnonmmmmmmljjkjiigffeedccbccdceeegffedc{d|d~d~~}||{zyxxyyzzzzzxxxwxxwvvwxxxvutronlmmmmmnopnnnponmnnnmmmoomllkkjjighgfeeccddeefffefffee~e}e|ee~}||{zyzyyzzz|{zyxyyxyyywwxzyvvtrqononnnnopppoooonnnonnnoonlklkkjjiiiggeeeddfeffgefffffeefee–••”~~~||{z{zzz|}}|z{{zyy{{zyyyyywuutsrqppopqqrqqqppponmnnpppollkklkjjkjigfefefgghhhgggfgggggfe}e~›˜˜ØėÖĖĖÕՖ•–}}|}{{|{}}}{{{{{yy{|z{zzz{xvuuurqrqqqrrrqqqqronmnooqqomljklmjjklkjgeffggijjkihhhhihgggfee}œœœÛÚÙÙÙĘŘŘŗŖŕ֖–Ö—×~~}||}||}~~~}|{{{zyz{{{{||{ywvuutssrrrrrrrrrrrponopqpqollkklmlmlkkhgghhijklkkjiiiijihhhggežĝÝĝĞÞÞŝĜŜśƛěŚřŚřƙƘŘĘĘ××ė֗—}~~}}~~~~~~}||{{z{{|{{z{{|}zywvvusssrrqqrrrrrrpooqqoponml}llkllmkljiiikkkkkkkllkjkkkjjjihggžÞŞŞŞŞƟǞŝƝǝǝƜǛƚƚƚƚǙƚƚŚƙŘĘėĖÖ×~~~}~~~|{|{|||{{{|}}|ywwvuurrsrqrrrrqrqrqppqpppmm~m~llllllllkjjjllkllkllmmkklljjjihggÝĞĞŞŞƞƟƟǞǞƞȞɞȜȜȜƜȜȜȜǜƜǚřƘĘĘė×Ęė–~~}~~}}}}}~|{{{||||{ywwutsrsqqrrrpqsssrrqqppomm~mlmlllkmmmmjllllllmmmnnllmlljjkjiiŸğŞŞŞƟƞƟǟǟȟɟɝʝʜʝȞȞɞȝȝȝȜȚȚƚƚƚƚŚƙřė×~~~~}}}||}|}}{zzxvtssqqrrrrqqsttsrqqpoonooonmmmlmnnlllllllnnnnonmmommmmkkjj àĠĠƟƟƟƟǞǟǠɠ˟˞̞̝̞̞˝ʟʞɞɞɝʜɜɜɛȜȜǜƚșƙƘŘs5"1tX{~~~~~~~~~|}}|{{yxvuttrssrrrtssttssrqpooooopommmonnmmmmmoopqoonnnpnnmmmlllláġŠƠƠǟǟǠǟȟɟʟ˞˞͟͞Μ̟͝͞˟ʟʞʜʜʜʜɝȜȜȜȚȚǙƙřØB-By~}}}}||yxxwvuustssuuuuttsrrrppopppppponnnommnonopqqpoppqppoonnmmml¢âġơơǡǠǠǠȡȠȠɟʞ˝̞͞ΞϞϟ̞̟͞˟˞̞˝ʝʝɝɝɜɛɛɛǛƚƚ٘I5L~~~}|}}|{zyxxxvuuuuttvwvwvtsssqppppqqqqpppoonoopopqrqrrrrrqppppnooonãŢǢǢǢǡȡȡȠɡʠʟʟ̞̞ΞϞϟϟϞΞ̟͞˞˞̝˞ʞ˞ʞʝ˜ʛȜțȜǛǚꘗK4L~~~~}|||{z{zxyywvvuutwwwwvuttussqqqqqqqqpqpppppqqrrsrssssrqpqqrqpppo¢âĢǢȢǢǢȡȡɡɡʡˠ̠͟͞ΟϟРПϟϟϟΟΟΝ̞͞˞˞ʞ˝ʝɝɝɞɜȜǛƛřØK5K~|}||}||{zyxxxwvvvwwwwvvvvussrrrqqqqrrqqppqqrrsssssssrrsrrrrrqqpãâ¡ šƣǣȤƢȢȡɢˢ̡͡ϡϡРРРСϡСРРϠПϟ̠͟͠˟ʞʟ˞˝ɝɝɝȜǛƛٙM5J~~}}}~{|z{yyxwwxxxxwvvwwwuuututtsttttrrrrsssstssssstsssrrrrrrrqƤƤǢơġŸŸŸßšǤǢȣȢȡɢ̢̢͡ϡТТңҢѢҢѡѡѡСРРϠΠ΢̠̟͟˟˞ʞʞȝɝɝǛƛÚL7K~~~~}}|{zzyyyyxxxyxwvwwwuvvvuuuvvvvtsstttsssssrsttutttsrsssrrrǦɥˤʣʣȡơĠġšơǢȢɢɣʣ̢͢͢΢ТТңҤҤҢҢѢѢѢѡССϠΠ̟̟͟͠͠˟˞˞ɞɞʝțŚ™-)ye~~}~}||zz{zzyyxxzyxvwwwxvvvuuwwvwvuuvuuuttsstttuutttttsssssrrǧɦ˦ʥˣ̣ʤȣǢǢǡǡǠǠɡˢ̣ͣΣΣϣѣѣҤҥԥԤӤҤӣңҢѢѢϢΠϡΠ͠͠˟˟̠ʟɞɞɞȜśšš™"eS~~}}}}{{{{{z{{zz{|zxxyyxxxwwwwxwxwvvvwwvvttuuvvvvvvtuttstutssȩʩ̧̦̤̤ͦͤʤɣȢǡȡɡɡʡˣͣΤϤϤФѤҤԥԦԦԥեԥԤӣӣңѢТϡϡϡ͠Ρ̡͠ˠ˞˞˞ʝȜǜŚšěœ›&%uh~~}||||{|{|z{|}~~}}|{{{zzz{yxxyywxyywwxxwwvwwvwwwxwvvvuttuuuttȪɩ˩̧ΧЦΥͤͤʤɣɢʢʡɡɢˣ̣ΤϥϥХѥҥӦӦէԦԦզեԦեԤӤӤѣТϢϢ̡̟͢͢͟͡˞ʝɞȝǝĜÜÛÝşŸ%$dW~~~~~}~}}{|}~}||{|{||{{zzyyxzzzyxxwwxxxyyywxwwwvvvwvvuuuȩȪʩ̩ΨЧШЧЦ̥̤ͦʢʢˣʢ̣ͤΤЦѧЦѦҥӦӧզէէէզեեեԥԥԥӤҤңУϢΡΡ͠Ο̟̞ʞɞȞȞƝěŝĝƞƠġ '%h\~}}~}~~}}|~~}~~~~~}}{yzzzzzyyyyxxyyzzyyyyxxxxxwwvwvuɪʫʫ˩ͩΨѨѧѧѧϦͦͥˤ̣ˣ̣ͣΤЦѦҦҦҦԧԨէէ֧֦֦֦֨զզ֥զզԥҤӥңУѢТΡϠΠ̟ʟɞɞȞǜǝƞěÛƞǠǡšáp"A2L~||}}}}}}}~~~}{|zzz{zzzzzzzz{{yyzzyzyyyxwwwwxwǪɪ˫˫̫ͪϪЩѩѨѧΦΧ̦̤̤̣ͦͤΥЦѥӥӦԨԨըը֨֨ק֦֦֥צ֦զզեզեԦԥԤӣңТϢΡ̟˟ʞɞʞ˟ʟƜÙǞȟɡǡơšà u^-!2~}}}}}~~~}{|{{|{{z{{{|{z{{zzyyz{yyyzyyzzzƬǫȬɬ˫ͫΫΫϪϩϧШШϧΦ̥ͥΥͥϥϤХѥӦԨ֩թ֨֨ררקצاب֦զԦզ֧էԦզԥԥӤҥФУϢ̡͠ˠʟ̠̠͠ʟ̡͡ˢʢǡǢġ¡ZGj:*> M@ay~~~~}|{{|||{{{{||{{zzz{{{{{{|{|{{z{ūǫǬȬʬͫϫЫΫϪЪШҨѨϨϧϧϦΦХϥХХѦӧ֩֩֩רש֩ابקبר֧ק֦էզ֦էզեԥԥԥҤҤѤϡ̞͟͠ТТѢУΣΣ̣ʣɣƣĢaOu)-"$#B6Xwd}~}~~}}{|||{z{z|{{{{{{|||}||{{zƫǫƬƫɬˬ̫ΫΫϫЪѫѪҩѩЩϨЧϨϧЦЦЦѧҧӨ֩֩֩שתתשש֩שة֧֧֧֩֨էզզզզզզԥӥӤѢΡϠТҢңѣУϣΤ̤ʣǣţJ;`  zh~~}||||||{{z||{{{|}}||{{{{ūƬŬŭȬʬʬˬ˫ͫΫϫѫѪҪѩϩϨϩΨШϧЧѨҨӨ֪֩֩ששתتةתةש֩֩ש֧֧֨֨զ֦էէէզԦԦӥӤңѣѣңѤѣФΤ̣̤ɣĥ¤?6b    dU~~}~}}}}{{yz{|{z|{|{{z{||{ǬŬŬƬƬȬɬɬˬ˫ͫϫЫѫѪҫЪЪѪѩϩЩѨѧҨթթ֪֩ת֩תתתتשתתשש֨֨ר֧֨֨ק֧է֨զզեզԦԦӥӤҤҤФϤΤ̤ʤƥ¤*+ MAf~~~~}}|{yz{{{zz{zyy{{|{{ǭǭƭǮƬǭǬȬʬˬ˫̬ͫϫЫЫѫЪѪЪЩЩѩҩҨԩժժ֪֫֫תתתתتתתתثתשששרש֧֧֧֧֧֧֨֨֨֨էԧӦӥХϥΥˤɥƥ¤xm(%!##$" ?/I~~~~~|{{|||{{{{zyzz{zzzȮɯȭƮƮǭƭǬȭɬɬɬɬˬϫϫϬЬѪЪЫϪЪѩҩөԩժԫժժ֫תת֪֫תתת׫תةתשةשר֩֩ը֨֨֨էר֧֨֨էӦҦϥ̥ͥʦƥåma'"%!$ % %##/"2}~~~{||}}|{zzyyyyyyxxxǯǯǯƮƭǭǭǭȭǭȬǬȬɬɬ̬ͬάάϫЪЫЪҫҪԪӪԫիժ֫׫׫֫׫׫֪֫׫ثثتتتשתتת֪֩֩֨֨ררשרררէҦЦΦ̦ɦǦpd   &%{~}{{|}|||zyyxwwvvttsǰƯƯįŮƯŮƮƭŮƭƭƭƭƭȭɭʭˬͬЫЫЫЫѫѫӫӫӫԬի֫׬׬׬ج׬׬׫ث׫ثثثتתث֪֪֫թԨը֩֩שةששըըҧϧͨʧƧ¦wi    %%~}}{z{|{zzwxywwtrrrqoưǯƯŰįĮŮŮĭîĮĮĭĮŭĭĭƭȮˬͬάάϫϫϫѬѫЫӬԬի֫׬֭׭ج׬جج׬׬ح֫׫֪իժ֩ԪթԩԨըժת֩שթԩԨѧϨͨʨǧçr$  (*~|}|z{{{zywvuutusrqpnmȱDZǰưůůİİįð¯įî®®îíĭŭȭɭ˭̬̭ͬͬάϫϬѬӫԪլ֭֭֬ج׭֭֭֬֬׬ثתի֫ժժԪԩԨԨԩԩժ֪תש֩ӨӨѨΨʨǨħiX0$<r~||{zzyyxxwttsqqqpnnmkʲȱDzƲŲűıññ¯¯¯°®®®íĭǭǬɬ˭ʭʭʬ̬ͬϬЬӬԬԬ֭֭֭֭֬֬֬֬֫֬֫իժԪԪԩҨөөөԩժժ֪֪ԩөөϩΩɨƨè| ,1~}{yxxxwwvuurrqooonllkjʲɲƲŲƲIJò¯îŭŭǭȭǬǬȬʭ̬ΫϬҭӭԭխ֭լ׭֭׭֭֬֬ԫլԫԪӪӫҩҩҪҩӪөӪժժԩөҪϪ̪ȩè#"2'>|||yywutuutssrqpoonnnkkii̳ʳȲȳDzDzŲò®­ĮĭƭŬƭȬʭˬͬѬѭҭӮԭ֭֭֭֭֭֬֬լլիԫԫӫӫѪѪҩӪԪԪժԪԪӪѪϫʪƪ©(#   TGm~}{zyxvvutttssqqppooonmljji˴̳˴ʳȳdzƲIJó®ííĭĭǭȭʬͭЭҮӭӯӮծԭԭԭխխլխ֭լԫԫҫЫѪѪҫԫիԬիիҫѪΫ˫Ǫ©$#!  ]Q|~~|ywxvutsrsqqppooonppnmkkkdzɳʹ˴˴ȳȲųijó®­íĬƭȭͭϮѯҮѯүӮҮѭҭӮҭԬӭӬӬӫѫЬϫЫѫҬӬլլԭӬЬ̫ɫŪ%!*/&"$$SIo~|zwwvuusrqpppppoonooponmllŴƴdzʴʴɳȴdzĴô´­¬íĭȭˮήήϯϯЮЮҮѭҮҭҭӭҭҬҬѬЬϬЬѭѭӬԮծԭҮѭέʫƫª** .#8ZNv~}{{yywvuutssqppoopppooonnnnmlĵôĵƵȴȵȴɴȴŴĴ´¬­ĭȭʮˮ̮ͮͯϯЮЮЮѭѮҭҭѭѮѭέέͭϮѭҮҮӭӭҭѭάʫƬë') "@6U}||{zyxwxwvvttsrrqqqppqpppnooonllͶöĵŴǴƵȶǵƵŴõ­íîƮȮɮ̯ͯͯίϮϯЯүѮѮѭЭέͭ˭̭ϮϯҮҮӮҮѮϭ̬ʬƬ«, 1#"#90K~~}|{zzzyxyxxwvtsssrrrqqoppqpoonnmmkĶööĶƶƵŶŶƷƷŷĶ­­®®ĮƯɰ˰˰ͰΰϰаЯѯѯЯЮή̮ˮ̮ͯЯѯүүЮϭ̬ͭɬƫ«%% ""7-H~}|||||{{z{{zyywvutssssrrqqpoonnmmlkjǸƸĸŸŸƷͶ·¸ïůǰɯʰ̱αΰΰΰϰϯίίίʮ˯̮ίϯϯϯήͮˮʮǬī«{ $'+1$(}~}|}{|{{{zzyyyxxwvvussssqrqooommlkjiiȸǹǹĸŹƸŸĸ¸İŰȰ˱̱̰̰̰̰ΰΰͰͯʯȮɯ̯Ͱ̯̯ͯʯȮǮĬ¬~u8,H(,"!*!5"#~~~}}}|{{{{{zzyyzzzyxwwutsrrrrqpponmlkjjiʸɹʹɹȹǸƹĹĺıƱʱ˱˱ʰɰʯʰʰ˯ʯɯȯȮɯʯ˯ɮȮȮ٬wp%. "&."6~~}|}zzyzyyyz{||{{zxvvtttsrrqqqqpoommlkkͺͺ̹̹˺ʺȺƺĻúº¹ıŲȲɱȱ˱ʰʰɰɯɮȯɯɰɰɰɯɮǮƭí­oi#& %,&/~}}}|{zyyz{{|{zzxwvvvttsssqpponnnmlmmλͻͻ̻˺ʺȻƻƼĻ»»IJDZɱ˱ʰʰɰɯǯǯȯȯɰɯȯǭĭ¬gaA:`.$;%""&=6[%+~~~||{{{zzz{||{|zzyxwwwvuttrrqppponmmlnξͽ̽ʼʼʻȻǼȼȼżļ³Ųɲ˲̱ͱͱ˰ɱȰȯǰȰȱɰǰůï_YD=i#%(17-N|~|zzz{zyz{}}}|{{{zzywwvutttssrqpomllk̾̾˿̿˼ʼʼɼ˼ʼɽǻļ»»ºIJȲ̳ϲггϳͳ̳ʲʱȰȱȲȱǰįï®êª[Rz+ 2 (382Wtv~{{z{zzyz{|}~~}{{zxwvvuutssrpnllljʽʾ;ξ;ͽʽ˽̼̼˽ɽǽżļ¼³ijƳɳͳгѴѴѴϳ̲˲ʱȱDZűƱŰï¯UKn/%<(-!* 4.%?nl~}||{{{{||}~~~~|zzxwvvvuvtsqqollj~gʾ˾̾ͿοͿ̿˾̾ͽ̽˾ɾƽĽ¼óŴǴȳ˳γгѴѴдϴʹ̳ʲDzƲűŰİïMCaQHx"$"$)) 6he~~}}|}}~}||{{zyxxxwwwvtronl~k|izgɾʾ˿˾̿̿̿̿˿̿̿Ϳ˾Ⱦƾ¾óŲȳɳ̴ʹϳгҴѵѴѳϴ̳ʳƲƱƱűŰŰïF;VD>c  , 4>3Xda~}~~~}|}||{zzzzyxxwtrrn|j{iyhvdƾȿʿʿʿ˾ʾʿ̿οͿͿͿȿſ¾´Ƴɳ̴ʹϳϴѴӴӴѴѴдͳ̳ȱűŰƱǰȰűï?5J(-, 2" *!2HAj[U~~~}{{zywvtpo~o|m{ixfueteĿſǿȿʾɾʾʾ̿˿ϿѿҿѿοǴʵ̶εдϴдҴӴҵҴѴϴʹʲDZƱŰDZȱDZűİ@5MZT-"4%""!=4S6+FYPx}|zxur|p|nxlykvhuescqcocĿľſžƾǾɾɾʿ̿˿ѿֿտѿĴȵͶͷжжѵѵгѴҴӵҵѵг˳ɲƲűıİƱưݯ>3KC;e!4)EZPz~|zx~u}r{pzmxkvhvgtdrcqcnambĿ¿ÿľƿǿȿɿʿοѿҿӿǵ̶ͶϷзѶѵҶҴҳѳдѴѴδʳDZŰŰððİİű°>5M3)B " &)@8WSHj~{yy}u|s{pynxlvjuhuftdrdpcocmcƿƿƿƿͿѿ¶Ƕ˷θѸҸӸոշնֵҳϲϲϴʹ̳ʳȳƲƲƱƱƱűñ°5+>;1N2(@$"3';\RM?[}{}w|v{sxoymxkvivhuftfscsdrdrdpd¿Ŀſɿŷʷ͸ѹҸԹֹ׸׷׶ֵԴѴβδεδ̴ʴɳȲDZƲƲűİðI<[kg/$9'(%$KAb8,CUEb|y|v{uxsvqunslsjthrfsftesesdtercrdķɷͷиҹԸո׸طֶնԶѵͳɱɱʳɳɳDzDzdzƲƲŲűñ®S}{{zvxsvqsqropnonnipgqgqfqerfqeqfqepdŶɶ˷ηҷӹոֶ׷ַշӶдͳȱůűdzDzʳʴȳȴȳƲƲı±°İŰİï¯FQªªëìĬŬƫȫɫʭʬʫȫǨ}{zxv~t{rwrvpsopnononnmmkjmiminingnhmhnhkfkfѿѿøƷɷʷ̷϶ѶԷַշҶѶдδ̴ʳƱįDZɲɲdzDzdzʳ˳ɴɳɳȲdzŲıİưȱɱɱDZ۰¯SGgmc2$6'$&%OA[?0AcPiŭȭȮɮʮʮ˯̮ͯͯήήϭά˫ȩŨ}{xwvuut|syrvquptornpmpmpmolnlmlmkmkllkllklkkjlĸɷɷ˶ζзҶҶӷҵѶдʹʴɴȳưůDZǴDzŲDzʳʴʳ˴˴ʳȲƱIJƱƱȱȱDZűݰF=XZOw, +% "$ A1>dPdȭˮ˯˯˯ͰͰͯΰбаѱҰҰаήʪƨ~{ywuutt~t|syrvquosnrnqmqmqmqlqlpmomoknknjninininƷʸ̷ηзҷҷԷҷҶϵ͵ʳȴȳȳİįDZȳdzDzDzDzdzʴʴɲDZűűƲƲűűűðİB5NJ?[- +  -(bM\U@Nʯˮ̯̰̰̰̰̰αϱбѱҰұұӱѱ̭ɪħ|zxvvtst~s|ryqvpunsmrlqlrmrlpkplpmpkojojojpiphphpƸʸ̷ηиӸӷԷӷѶжε˶ɵȵȴű®®ïİıï°IJƲȳdzűıŲƲDzƳƱƱİï9-@:,>3$1$bN^t\jͱͲͱ̰˱˱ɰʰ˱̱̱ͱαϲѲѱҰҰϮʪǨŦĤģ}zxvutsrq~p|pyoxnvmululslsmqlpkqjqkqjqkrkrkrisisirŸɸ̹θиѹӸԸӸԸѶ϶̶ɶǵƴŲıî¯İñ±ñı±IJƲƳȲȲƱİð}[Jdwe9)7'#$?,9OrlA6N'#5(8!rg|o\Lg|ywvusrtsrqonoprtwx{}~~|yzzz||||Ϳ˼ʺʺɹǸŷµ´²4#/XIcA1B&%)$:,< /#0PC`6'7}zvtsrrrqqqqqstuuvy|̽ɺǺźŹĹķµ*$A4@`Sm5*8(#+)'%N?W;-=s{vsqqqqqqsvyz|}}}̿ɽɼȾŻúù¹y*(UF^H9N, +'$$ X&"C8MA5E|wbOd++uxsu?4H'%* * l`hWn*+\^HCf7-B. , , )("L?U>0@PDYF8Ms!>9WFEkEBc* -#MD_% $ YOmB4I6+6qdx~=9XF?dFBc))6-:QD[M@TOCT^Qgjk-#5mrmo=3H'#SKcpdxvxcSc~}}}]Xz<1GXMi?3F/#-[Rl3&1M?Q{{iUe~}}|}~~~~SJcH>P|^Rjk^z5&3'#0$0.*H,'/ ),&*$,%*#2#.&!$+#^QdĶķķŷöö³ĵȺɹɻȻɻǻǻƹǺȻɻȻɼɼɼɼ˼ʽʼʼʼɻɻɺǸǸȸƸƷǶƵŵijó²|{{{{{{{{{|{|}}}~fa,'&"%$- &7)2. +1!+5$/.'5%1$"$&J=JµµöķƹǻɼȼǺŸķʽʾʿʿʿʿɿʽɾʿʾ˿ʽʽʽʽʽ˽˽ʾ˽˽ʼʼɼɼȻǹǷƶƶŵóó²{{{{{|}}|}}}}~~~XQl/!'%$- ( $ ,%"*"!;-7¶öķŷŸŸŹǺǻǻŻźķ÷źɽɽȼȼƹķǼɿʿʿʾʿʿʾʾʽɽʼɽȽȻǺƹƸöô´³{{||}}~~~~|rE8G-%*#'% *$ $"&!'%&%+"teq·øŸĹĹĺźǼǼǾȽȽȽȼƼǽȽǻƻƻǽȿɿȾǼ·øʾʿɾȾȽȽȽȾɾǽȼȼǼȾʿɿɾʾɽȽȼǽȽȼǻƺŹĸöö{||}~~~~{NCR&)##(&)$.&$'$;8K;.9=-8G8DJ28:.4- (%"&."&,&|4'/' /"(&")"A6CC8?- &$0$)*#( bZgi`sPES!4(0"+!%+&!ZQ][Sa\Sd]TiYRi[Uh`ViC6C!!8*30%+5(->2:VHZA5@MBP8,9=1=>2=.!*4%-cYr%%/#+@6@:.74%0y{?3B4&1aYo=3@.#*3(/2$0E;Hme4&0()3'-=1<0",rh~}~}zz|}|~~ts+ )5*6OF]1%--!(-(0".WO`x6'1&-!%<074*0=0;K>J}~|zzzz{~}{z{{{{||{|{}~RI^:/9:/:PGX. (7+4'!,)mg~7)1'8-5PFQNEQ0%,4%/}{yzz{zz{z{|{yxxwwvvuvvvxyyz{{|}<1?bYn-!(;.8("3(0/!)*%{?/=##:,4@4?90;.!)}x}|{{|{{||{yxwwwxwvuuuvuvtttuuuuuuvvvwvuvwwxxy}wrA4D\TjG=L/ *>12TVNh3&/H;M+ (4'26)38)7B8Iog;*5) 6*35&/8,5E9E6)5~~|z{yxxwvvvuuuututtuttuuuuuvvvvwwwwxwxyz|~~~|}}{yyy{|z{{{{{z{y{zzzzzyzzyz||}~{~7*;.+4(54)53(57,94'20"-IAX}6%/(>4CQGZKBS/$+("pj}|}{zzyyxwvvvuuuuuvvvvvvwwwvwvxxz{{zyzz|~}|zzzzxwwwwvwwvxwwxxxxxxywwwxxxyzz||}}~}~}~~ikQJlA7O9.><4G0#00&38,:*'OHd9)3$+ %@3AG:I8.:'#NEY|{{yxxxxxwxyyyyxxxxxyyyzzz{{}~~~~~}}}|{zyzyyyxvuvvtuvuvwuvvvxxxxyyyyy{{|{|||}~~~PLphm,"-5):4'8B>]A;R8)6.$/[X{I/>E9HH@U2$-~~~}}{{{zzyzzzz{zzz{{{{|~~~~~}~}{{xwwvuvvvvvuvwxxxxyyy{{}~}~G1A0%2y|pn6&/*%6,9A6HF=QD:M>1AL@U~}}}}|}}~~}~~}}||||||||tuvxyz{{{{{zzzyyzzzzz{|}~~}NC^4%27,<9->5(65H4)68.?0$..#,?4FxxD6F&;0?QJeWOnMD],%7,8f_~~~}~~~~}}||{{{zzustrruuuuuusrrrsrsttuutuutsrrssrsrrrtvvxxz{{{|}|~}}~~~~~~~w|xzw{_\7)6H?V1$-0"+1#,+%,&6)4+)3(7F;QH=TH=UG=RE;OC7JD8LF;OF9MB4H>1EC6KC5F*%& ,$1"*0"*)".!*C8Ie`fbhe~~~~}~}~||}{|zzzzzzzpqpoqrsrrrrqrsstuuwxwyyyzzyxxwuusstssuuuwxyxxwwvvxyxwxy{{zyxwwxy{}||~JLn$#(%+ ),+#+%. &/$3!(% &!3#.F5KRCaPA[PAWF8MULmPB[ND^G4BL7HM9MN7H@/=?4C#2*8-#!'')!%%!81B~~~}}}||{{{{zyyxxxxopssutttuttutsttrvvuuututtsstuuuuwwyyyzzz{||}~}~~~}~|~}}|{|zz{{|}~~~~}~~}~~ZbBDf"% +&& 6&/7&++ /%#"3#+M9JS=OP;MN;MI5GS;OR=SO6HN8KS,;:'54%.,%*"&-'*$' '!/ )*$.!'7+:<4H~~~}}|}|}|{{{yyyyxyxwvvvvpoqprsurqsrqrqqqrqqrsstststtststtuvuuvuwxyy{|{|{||~}~~~~~}}||v>8S5'55'23%03%/1#.1%/1#-2".1#.2$.0!,0",- '.!)- (,&. )- '/!). ',(- (-)-(. )-'0!+,'-!(+'- (*'0!*. &0"*."+, )p~~~}|{{{|{{zzzyyxwvwvvvuttsopqqqqqrqqprpqqpqqrqsrpsrqqrqpqoqoppqppqqqqrtuvvwyyz{||}}~}}~}}~~}~}}}{z|}}|~|}}z=;X  ! !  !!!!$ # 3.Dsxy{{||~~~}|||z{zzzzyyxwxvvvuuvutttsllmnopppqqqpqrqrsssrssrrsqrrsrssrqrqqrrqqqpopoppqrrtwxwwvwvvvwwvwwyyxxxwxxyzz{{|{OT-"1) ,,".('+ +*(-!,))+!,* *,!,* +) ** (*(*((''&&&*)'%+ +)',"+((-!-('."-)(-",)'.$.)%/%0,!,2'3F?[}}~}}||{{zzzzyyyxwxwwvuuuuuttsssopoqpppprrsstssttsttsuttvwuvwvvvvvvuvwwxxwwvtqpnnpopqsrrtrsrssrstuuvuuuuuuvuvuvuvMV3'4/$0/%1/$./#//#.-",.".+!,)(*))')&(&'$%"%"$"&#$"&"& (#%"'&&#(%&"/#.,)."-+(1#,-!+0#.0$.C;Q~~~}|}||{{zzzzyzyyxwwwwvuvuutttttoooonoppqqqrrrrrqstvvvvwwwvvvwwwxwvuuvwwwyxyxywsoopqsssqprrrtuuuuvutssrrrrrrssssoEDi1&3,!)(".!)("- +-!'9,8)',''$, (&!'"( &)#$*&)"("+!'( -#+,&*%*%-!)8+60%-+$- '+$+%,',&3&1vz~~~}{||{|{{zzzyyyxxxxwwwwvvuuutttooonnnnnonnnpoppqprqqrqrrqqqqrqrrqqrqpqrsstuvwxwwwwxxxwvvuwwyxyzz{zzzyyxxwxxxxwxcu4'6* +7)6-$.9-;5+:2&1-"+;.<3(30(5:/>&:,55$+3$(A7KG@Y<.?=3E3%1D=U@5I/$.7+5:/?9*64+8;/=7)4.!)&4'12$18+72&3,%G=T~~~}||||{zz{zzzyyxxxxwwwwwwvvvuvuuttoonoononnnnnoooppopqppppppopooopooqqrrrstustuuuusttvvvwwwvwwwwxxyzyyyyyzzyzzzyz{D5N3#1>/C3%18)56+;8-;3%2/ (H2?7'.G6BHE^E;SH=U2)5>2A?5H>1D@7G=3D=.<@2B7)2@2@B3D5)48+58*7A4E9,:8*5:.:;/=>/><.>.<@1@A5E:-7=/>4&1?1?6(1B3BC2A7(2;/:9+7;,8<,8=,9<,9A5H{uelN\yk~~~~~}|||||||||||||{{zzyyyyxxxxxxxxxwwwwwvwvuuuvuuvvvwwllnnnnmllmlmmllllmmlmmlmnmmnnnppsx{urmonmnnmnnnmnooonopoppqrssstusuutvvvwwwwwwq}A2H:,;:-87(3=/;5)3<-:9+75(3&"$%,%48-<3$/8)47*53'16)43&03%18(66(17)55'33&0;.<7)33%.PNjA9MF>R#/#+;,8:,89+8:,9<-88,6=.:<.:dazjjNWnR_u`v~~~~}}}|{{||{|{{{{{z{zzzzzxxyyyyyxwxxwwwwwvv~vwv~uvuuvvvwwwxjq_mklllllmmnmllmnmmllmllkkkknnmmnmmy|}~}unnnnoomnnonmnmmmmmmmmnoopopqqqrsssttttttY\8+7/!+- (5'2-!*."+-*B3D<.<'#=2G?/>>3I:*67(27)67(53$-0#-1$/2'13&/5)68+8>/=;-;5&/8*6C0=SIbb[}<-7E5D(#!$ -&"!)%I>T}nnQ\rU`x`w~~~~}}|{|{{{{|{{{|{{{zzzzzzyyxxxxxyxyxwxwwwvwvuvvuvvvvvvw|ocJ[`FShhgghhhhiiihhhiighiiiiiiiijjjjjjjnw~xwsykkkllkkkllllllllkjkjjkkkjkklmmlmlnmnnoi<4M($$#!%''!/"& ?1@('2%0:-;E9QE9Q;/@2#-(!&. %=2?SPq[Y}[Z}VTvNJgC=S=5E9-;3%/6*4:,9=,=WKe, '=.;,&!$(!*%f[zzfnQ[qS]tXfwe{nxiyiueuevdta}sb}wfvfudr`z|s~}||{|{|{{{{z{zymwlym{p{}s~txyyyxxxxxyxzzwxxwwwwxwvvt|otwwxvup}mgO`eKWeeeeffggfghhgfgggihihhiiiiijjlpkkmzu{q~r}p}pwkkkkkmlklllmmlmmmommmnnmjqknnl~mnmon~optOR}.$2)$2&5, )80I/'99,86)57+70",>.>1$24,A/%5!>,1U-0A,/@+,;04FMPs\_onsfqa|ufu`w}wl\uq`}lZwvpvmfVom]|p`{hXncOcdZyfZymdiYsi\~skbZ|_Lbh`lggXvYF`WEZ_K_cOceRij\woafQbkVhiTfo[oeObs\lq[ln[qr_~nWlmVfkRc_F[eSmgSkfRcgRbbO`;,83%.3%.:*4*'+3'(* '8,7<.;# B17mTbr[nqZnjO^iNZgLUeHQhKUjNZnTbnTbjO\tZiqWfZ>DY=ChPYqZhwarpWglQ]rXf`IS%9,7:,74'0'!+"2#'+$#2$*:*2N;HjQ_^DKlQ\pVcmQ[lQ[t[hjP]rWfrWcgKTfJRiMVoUahMXfOZgOYfOZbIT\ANZ@IZBKV?L\DPR;IL6DQ:BQ9BN8?\KXK7AU?KS=IT?N^M_L=FbJSY?CZ@HZAK`EQlSdnT_WBOK6DO:HN8DQ>LI5DQ;HaHSiO]H3=B,5>*2H8DG7CH7CXAKSM;7I/0?&(/$")#!)#")$%/+-:#"*%!(&%.''/K:F_K\TAT`MaP:KI7HC0=UGc>/AA/@M9LO)2@*1D-4?).D.7@.8;)4I6DN8FP;IP;KK7GNnZlfRbo[ms]niTdeQ_bLXaFQbHUeMZbKXaIWiN_dKXX@FT=EM6:]DKkR_cM[\TjeO^i[tWnTUm!6(3D5C2&,*!'."(, &%0!(;+9eM^_DM_FP_DM_EQ_ENdKUbHUaGS]CMZAKT=DG58`KU^FP]ER^LZ`P]ZFS_N`VBTS@QYFUYCO`M^WCQP6AG/6ZHVWDTQ=GU>IUCT6-8 &!!(73>,(2<1=M9FR;ERGO>NI8HD0?,2>)2N8@SL8BH4=J7AJ7BM9FS>KVAOS?OP;KR=KK5AQ=I:/<1*7+&2$!+&$)8/=WHXZIZZHYYGXYG[VCUWDRWCPTCS;AT;?S7K8EI:FI7DF2=D2>J:IO>LM9I>:K=:H?:H94B&",$&;.;[GXSMTEXeUldQdcIUjP]tXfmR`[BPP9EN9DO9CP:CO9CN7AK4=O:GL8CL7CO:EM5@N8AN9CPKR>JT?J=0<# A3A!'-!&, (+%O=I8)3=2?4.9/-://;.,71-8=6D60<($-'%-# ''"*,'-+&*,%+0).-)/&!& "$"!)'&...9/2>-5A,/;-.;'(1=5BJ9ETER,(0 % <18B7BKBQ4,;'","$9-9=2@J=MM;IJ:HH=ORJ`ODXD3;E5@7/;*)/"$, %3$3+++%&  #$.&0B4EE6FE9H$,:'0B'4E(2C!%.%#'0 $."%.*)33*5&%%%!#""!"#! "#$"!($#* %)$.6-P>5B(%**&.)&,(%*&#)$&"##')&01-6>085%,3%,4(1@2?D5CD4DC3DK9GP>ME:G44@.06K6(14$// (6(21#,+%'!9-85)3!0(+;279.5L8EO8B>06J6AVAOH8JI4A<1?H=OPDYYJ^UJ\;5B#(%4 /+('$#%   "$(/&4;1@%.&/#*4 &/ &/ '3#)7 &0"&/#(2'&/#!!!! ! !" %%&-$&- $&#),(2@7D*!+*!+<0<80<.*3*'-'$(" ## "$!#)#($""!%!&*$+C4>F4@>4B51?/,90*7:.;=0A;/=D4@?2;809,'-*'.(&2%$1%!, # !!#!"('/44@46B24D,.851@3'3/$*+%*&,!), '"#9.:#$ ?9C8294',8/:1%+, /$)50?74G<9M=5E:1;907K>KWEVR?NK7ES@NSBQR>LN;GM;IVEVQCT>6B:5B>9H?=K509.##,#(8+7!,"''"("*$(#+%($ ,'&#(+$*,$*'!'! # '"*$,$ +  ! ! #"& #53<('-! !# ""$!% !!"%$.,,:43C@:LI>NE?NB=K54B"$-"."1)&&%%$# "" $&)!&""" %$'$*$ ' ""  !##*"",%#,#!'($)$#$".$1*)3*&,$"&"" # % "'"$#!%%&#)85C65H+0@*,;)&3)'0*%+,&,*&--(0-'0-&-,&+% '"*&# $ #! /0900=-,8&%./*4.&00%* .!*"&,%3'/1%/ ,(.2/6-&**$'1-51191/70-521>3/<.'-/(,2*/3*.Q>IqWafLVP;C[GSbLZeRZW@JQ20<22>13>36B/1;1/:,!(9*4* %#'#,")$"""!%&&    # "! !"&$%1&'3+.?*0A#(7$&/!#,%)!.)'$$!  "#&#% ! $*  !!  ! !! )% &$$"("#$!-*-D%):"$! ! $!'!%'%.&-="(6 $2!%2$'5%",&$.'"*( ()$,)&0)%.*$.+&.%"* '##%!" "" ))3&'3 + )%!#$ *$-!'&';,7C4C$*$*+&0)$++'.--5,.6,.8,*7)&-(!$+"%.%*2'-@3=[K^P@JC6:LAHG=FK@JK@GKBGG?DB69^N`A6A1,7+)0+*1((1(+3$"#$ ;.93'0% #!    "# *!)"+ (7!,$3 $.&' +*'%(('$ "$&"$!#! "#$+%&/ $$" ,!( &#$4!%5 "/"%3"&5 .#"+  "#!$&*7"+%$/"", )%&0%!+% ('#,'#+&'1(*6''3'&1%%0 ) %#&  %#% ( *# &""'!&;-76(4,*2**5*)4)'1('0)(0$"($%$ &"*!#9,5H9JREW<17835>>C=>C@?G??G?>E>?D@@F6./TGZI=M.)1!!!$$+$"(&!%#!!+'!#)*' ("*'&!,",#*" !   !! $ 2!"3&-A#*=#"6! -! /%'9#'8$2#/!$2$/'' "&- "*""$$"")%#)&#)'%.&'1%&0"#- )!#- !+&$ !"$")&'!6'1)%#!%!$! ! # /')1&&D7ASFZE9C@<@>=C?KL@N1)1%#( $0',#0'!*( !#'&$(%(#+$!"(&.$ #$6 +"!"#!$!(!&7 (9!2$%:#$4"&6 &5$0#. #/""1-+!)!#($  ! "$ %%#+$&/%$$ $%%&""!$#"& $! %#3%.6)3""%# ( '4(1I;GRAP=/4*"!610>8:A;>A:=@9;A:>A=@@>@?:>@:>,#%G=FUFWI;I"$=3=%!#*!'!$,!$,$*"&"$! "!*"+%.$$ !".$7&;%8 )? # !#!"(#1!%5%&* -!!.+ "0##2%$7#"5$$4 ,!*$!!! $!""%&/%(4!* ' ("/ '8!)<"&9!%2 &  #%)$$(!!!! !"%""" &-!)% *%! .'0*!)6*6F8IK?P6.3&# /0/4893983:95;;:@B;@F49:177#76;K@RJ>T*#,?6F0(1  ! "!!  !%+ "& ""## %!!!" "&!"  ""!!"  %!( $#, &5$5$6"3 0!"&""%%&+&$+#")%&1 $.#-!' !'#$') -$&2#&4 "0!"/&&!$ % #("#!#'!"'!# !""##&"%)%!(1".@&8V#4S!2P!0K .G ,E#+C $/!"%!!# !"$!'!$(%) $@6D)"-!' *!)>4C,$+!8,5A4C>/B.&("-.-4897=;286389?9=?68;!236;.7A3C/'0%%C9JD8I-%/$#  RENXGQ"  #" !# + '"$0!%0   !")%,%-  !.#1!/ .*$"!! $#$("#%!#!#"%'"$' #'#%+"#* "&!"'#%,%(0$(0"&0$(/!&,$)/$&-!##'#(-!'-%* "&  ! #!!)#)@)>(>'>';$7&9%7"2(04*6+%,*6."(3'0* $$7)40#+1$)7,1<0;A2A8,3*!#/')302123334223757QBNS@PA:D246545512404(!#4-19+4%0$+:-7?2>=2@-$0!,%-D8DeTehQ^iQ`kWgM?O;0=&,%.$' #1", )?*@( ##!  " %!+$      &-)(# "!!$ " #!"#!! "&!" # !$#"& $(#&!#'""'&*2#&-!&!% #-!+B*C$7 /* - 1"14-6?4@-"-5E/"&5)0)$<+5A0;<,5<+2<.3;.8C2?F6AM;HO>KTDRVFUWFURARXFTWDTWETUEUWFVXFWM;GVFXVGUJ:G?1;7)1C2=M;KF5B2'/' # '+"'$)# %!@4@6,670;5+69.<1'28,93)2*.$$!.( ++D(?) $" &"!(# ("!- /(#!  "! "!$!# " ! !!!$!% %!%! % #!%8$6#4 0&."0.* 4)/'<8>X7(4:+8=0<<.8;*6G4A9)/4#'8-/G]WF:@J8EO>LQ@NPAOOAON?NK?Guo=E3BM=LM>MN?LN?MPANC1F>,5;-79*2;.XjThdPd^O]=/<@/8=+2=,4@-5;)0>-6D4>B3>5'2A2=K9FC3.7:(2dOcfPbQ>L)N>KB2B4'.F6F/6X)Fx-G{/Ev32L/!&&&$(+$/+$1&)'!* ")&*"-5)0 %,.#"$$& -!#' #'$*&) %&$$" !!+!& (")1%."        "&.$)0',)!% !*!$* -#&1*1&.#* %"(&/'1#$,# %) $!.$,5,2/&+7-2:,59)0A04C2:<,0@06*, %#1#)'#* '0$*XCE+',10:-'08)4@0;B3@?3B+(:-J*G!5( .<)3M5G9>^RH_T;DH8@G4@N=HdPb^J[^K[?-6C19F3=H5=M9DK7BL9CE9J@9KR=GjPYoT\pT^bHRM8CM:AK:CK8BK7AI8C7'.8'+F49I5>YDS\GXZFSO;FI27N9D<*0D/6$$7+B,G$<52J4'3 "('&$'%,#)#*-7$*$'!&%'+"## %1%)5&!%'%." $"(1!$#"! " -'>!* #*)%.0+6##-#   !&1%2%#,  !)!#/$%4 , (        <,05'/! "  "H59K39E/3H5;@(/H;F19I.5E$  %&1%+<:Of6FZA('T-+8$&6),S7;K7?7'+VFOPSg2$6) ") % #*)'(*&&1#'9*3."+.$,+!&) #1$)H48& $!%!("*&)%+(/"+- *0"++ &7)/9'.=,927RCA\ZDU[GUZGUH7A.!&4%.O=JP>JSAMXDRL9CR?IR+3-#/#,%!'!*$.!'(")$,#( %% % %!'")  )#'."%2(-4*520>,1A%8P$9Q#4F*2@#+2&+'.5(.81.64*03%)#5!!&"!%*")"% !!##(       W=HN6F6-7%!!#!* $$"#%]Id;)=$#$"")!-9,8$  %"0$4G+/:D3?OM7>M9CG5FPNJ?NLBQJESIQZLKZK?NI:*5-"*6,4@:B@4?9,4+ %& .$,)#+'& 2$+( "*$/#))#'0#+8,6C2>L:E\HXU@OZFUU@ORJ4>K5?M6?U=IL5:M7=M6>M7@O;KB/:N9DS>J7&/SAR`M_^K^Q?MN % ")   =6C:3> '$%3A"'0+2(+@ +@ ,!2I$!&9':(;,@0D"!(&-!!#    #" '#&%+%&*180:7+3F5@I9EG7BJ;EF=F?:BJ?MF7DXDPS@NH8EH8DH8GE8F7,31&.8.8:0;<0;C3??/93&.=/7F9AB5?=/61&,"!,")+ %.!')#:,7.#*(#7+55)11#*3'.=0L5>N7@Q:CT>HS>Iyd{zf{VALN8AG5EG2;P9DT?L, %eRd_L_^K\L;JF7F5)12&1+ '%+#&- $/"',%(!#)'#( +$5'27*54,4    !!""&!0+7".?$/ &",   $! &"& #!%!% !!$#  "#   icyWL\$,$+#*!%!&"4!2'; +B,B,B/C!0E*;#3"2!."-?'6#,!    ('+ &! %!(/)10$*3%,4&-4'-1%+2'-9,54&.L8BE3<8(/F5BL;IE5B3&.9+4=/:B3?F6CN/9E6CJ:GQ?M_L]UBS[GYXDSVDQT@LW@XJ7NR:DQ9CT[BLTQ;FWAO$aN`aM`VCSJ9IM=O?18C4>?1::-24%,*"2")9*04&,0#()"*#' ' %+ +!,%' &      -!(1(2!$."*"  ""#-)../(%'!M9AXBLZBQ:+4>.7>,5C48H7>A/2D29D49I7==+-J6=1+3    84@i`oaU`/(-&  $$F?QYH[.0D"3$4%3'4(7&4+"1$2!+")"'   $,*.>71E5*8'#%.*3A5?;-8<-87)2;,62$)7(04&-;,4>/6@0:F5AM;IE3?8)35(1=.9A1=F4AM:GK:E:,4=.76(-&-%2$.9)1-!'.!)7)4.$1C4A?1=I1>>.7UBSRAOT@PZGYWDTU@NWAPT?KR>PI8QH3T>JVBO)bObaN`YFWN=KG7E2%.2$.7(15&/;+74',<,6E4?C2=?/75'-7)05&//#,+!)0%-2%//$.,#+      "4*2'+;"'3 ("$ !&819+)48,42%.B2:gMXfLXWBNW@KVALVBKZEQT?FXEP@.5N>B*$51H7*<;.>+ , #%,#(!&$061?!$5)*"/'->!%2*&'#"     '%0'$.!#+#'1%)2G;DQ?IQ>HI8ATANE4=6&0?.6:*5F6BB2<9)08*0<+3?.7@/7>.6>.5?.63&+<-37(/<,4M=HcP_eP`dO_aK\XES?0=B1?>1?M>J0$+9*4K;G^J^YDVP>MbOaVCRU?MYDQRGO=Q?.9H4=TAMVBO3$.cOb`L__L]YFWUCS[IYaM]bN_^K[S@N3$,C3;E4>C2>H6@C1;C3=@0:;+59)48*57(25&/1$($#      !4(/!&0*8#, "   & &+*9)"(##" %!&$$+7/7"$#$ 106Q=JT*2H6@G6?QJ7=G59*0I8@K9BM8AVAM`LY`LZaLZYEQZEOO;DJ9DH:H;/;:-6H7DI6B_L_YEW[HWWDRWCQ[GUVAON>S9((G16A.HQ:C_EPnS[U>IC.97%*$&2>+3F6NB/8K6@S?KQ?PE4HaL^`L]\I[WDVQ?PcN_dM^gQaaL[S?J1!&B06H3;H4;J6=M8AM8AO:BQ;DP292*1!1'+7*/M;D@5@M@KC;CC7AK=EQ?IO@L?0<61>?7A >-67%*P:BO:@:(,:)-0#$8'+*E18M9AR>HI16L7@!#"D8F{f|N5?aJTu^j8.6.2:!-2#"$%4!!&//6)"& %$$!! '         N^IW^JV`IVZBLsTfX@N_FO^GS`JT]FO`IU_HT[ER^JVWCOF6=_JXS?.;RANXFVSBRV@N^L^XEUVCPWCP_JZWCOH=fo]|R<_&dJ;pA,EeDMV6FiSiP=^%EoXkI?e63[D3DG2:R=HK8AN?e;;aL[^K]_K^YEXP=O_L[bM\cO^bLZJ9C_JV,0!%2$'5%*8(,:(,=*0@-3A.5A06?/4H6=?,;D2BB59 5).ZDRJ6@7,2K:EZDO^GT--9C;GM>IZDPI8@A09I8AT@LT?KS>GE5<7)1,"(5(.3$*;)2K;I6&/?/3F5;! (!?-2J48H38G19A,2E19."3!')=+1N7BT1=?.7>,4A.6;+2:*1B07S?H4%+J5;8(.:*0A/7C3;=-5>.6;*24&+3'&8*/7)-9+1A1:O=KL:DMLTAPaO]ZER[FTO?LC=Q==Z%#?WNf!/l50k! QUF\?:P03K;2E?3>F5>K7@K6@WBNG5?T@RH5GaNa^K]^K]XFWP>NM;IPO4%aVAT#$##-",7+81"+,#& $(')", $$$1#(1$*1$*0"&0$,#(#,2$(/!&,#( ' ''$) #-!#oT\hMR[DKK5;M9=G490()8+.D:C %""e# 4#,E.6F04`FM;.<2/BD;L{f}~fyXAP9%0L7Ahv~drlP[3"+.(/B=D'*-!!!$  #&9:M1.:97B*(@18/.7F;@0-7(&,'"%5+0.&(  8       ,)6L>J7)1<1>H;F0#*8*04(-- $/"'- &7*0?2:F9BE8BD6?3(.&"5+3N@OE8CTFSF8B@0;;+3E7CE5=C2;&LKT>K<(2K7KJ8BA8PF3-8;,5E4?E4>R@L2$,( ' /#/3)57+68,67+50&.)'#.$.',%-.$+2'-)#& #1%+,!(.#+-#*%!(&7,88+6/#+* $# !$ '!"$"A059(-?.2[EMnMPfl^FH2%(,!%#'(!!>6C7/6 ."K7=O;@K7;=-1<,2,!&10>62A4.;4-5'&43)63,9?.3+$/8*0*"*'$(! ##% !(1-9YFUoWibVjU]~V`"!.('7##(!%#- !+!   #!$!%!7-5,#)-"(""$"6*40"(+%,$, &)#(#)%(&.!*8*3-(- ', %*%&!("5&.1$-0");+3:*2*". %8)1<,5D4?TBQ[IY[HY[HZ[GXQ?O[GWcPa`M^fRfeSf^K\cRc\J]eSeU6C[HaUBQ]J\WDScP_ZFTaM]XDQ_KXV@NZDTV@PWCSD2:P=K^IZVDTL;HL5FL.66&,C2=A0:D3?079*17(05',9*19*1L;FTCOXEU[HYbNb`K]dQcgVjjXliVkgSgeRffRecQbaO`dPaR?K_JYYDTYDT[GV\FW^IX^JZ_K[`K]aL\cN^aN_aM^ZFVS@OXEWZEVO/<=.:;*6:*59*5:+49+49*4;-7<.:<-;6(4C1@E3AH7GI7GA1TCQ[IZWDUXCTO=JL9F#"& !"!&6.=""')"(9-;8,;UMcID]1/E"     :*3-)9; ,&>BTW=OiB>O1,82!(3#*2#(- #) +!* 2"(/!&*"- %+#+!$,#,$&-!%%#0#(1#$#(*4#'2!19)69(1>,5A0NP;JWAR[FV\GW]HY]IZ`L^_K]_L\_K[_K\`L]\IXR?LZFU\ET[ESVCPR>KR?KQ=JS?KV@NYCRVBN3%*TCOS@KSBNS@MR@K`M[`N\aN[bM^`L]^QEYGMP=II8CH6BE3?C1;A/8C1=B1/=E1CA/?C1CE2FE2FE0?D2AE3@G5ANFQ;DR=GH6@L:IG4EXEYR?RUBUUATUAVUAWTAUQ>SOLS@NTARYGYWDW_La]K__N`aNbeSieSi`Ob_M`YGX[HXR@O?.:P=J?.JOKU?LZESR=JG6@RANR@NS@NM9EL:FO=LOJT?MT@MUAOWCQXDUXEWG6E-%5( ,   %#("'/".SP@RN@RO@TQBXP?RR@TSAUC3@I8DNKUCQQ?MH7BUCRVCSXFVYHZ]J]]K^]M`_Ma[HZ[GX[IZ\JZ]J\^K\_K^\J[^K^[HZXEZ\I``J`_J]]IZ]IY^HX\HV]HW[GV[GVXETXDPWBOUAMM:ET?MR>JPC1:E3F6?O@N=.4P?LO=JWDQYDSZFT[GTR=F]GUZGRXDPYDQ]HU^IW_KY_KX_JW[FRXCPWANWBNT@LR=IR=IQ>HM8AL8AVDPZFTXFSZFTQ=GVCPI9AN=GXETXESN:CT@NS?MTAOUBPWDRZGW\GYQ>L2,=!,!+" - ." -''8+8<-:1'3J?QD>MAL2&.7+2@1:T?KZETXERYEQR=EVAKXCMZDQ[FS[GTZGTYFTZFSYFR[GTZEQYFQXERVCOYDPXDPUBOT@LQ>IG41,6-)32-85-8$#$!"! O?MRAPUDV\K]fTglZpm\sm\rjYoWCTiYnkXngTigTjkYoiXon[sdShcOen[so\so\rr_us`vs`xs`ysbxuczvf~tf~vgyhzjwhwhtd|J2>7,9^Odtd~tc~verc~tdsc~ra|q_xo[un\tr`yq`yn_viWnkZpn]tp_wr_yn\thUlkZpjXpl\tn]vpa{rc~m\uhVkVFUk[rk]ul]vq_z[IZZGWm^zocl_yk\uhYpfVmdRgcPdC4>F5AB1=K:K\Ha\I_^J^]I[]K[]IZ[GXWFSXESYDSXETZHWYGWZEUPKS>KQ=IR=JS=IO;FQOhVmiWmhVleTidRfbQdeSgeRheSjgTkgUlhVkhVlhWldRgcRf`Qc_Oc^Pc]Pe\Pc[O_WLZWL[VN]WN^RLYOIUDNF6CMBSKCSEIBEB?H=9AIAKIBMKDNLCOMDNOEQRESTERRDQSDPTDPQALSBQZIX]JZ^JZ`J[_JY\IXZGVWETREV:/92*08/9qgqbgcpE29UDPWDP[GS\GS]GU]IU]IV\IV[ET]IV[HT]IV\FT^HUXCP]GVG?A<10+--$9/-2*&.)$*#+(#2%*.#->3A(!,0%+ODQYN`QER]N^QGTLDWTGSA6AB>Q55B88DHCQE>N;4B3.9($,$#       IH`?CTAEU@BP::G65?%#)#! 4*3[H[^K_^L^_NbQ@PB1=YGXK:HG6C_L[cPacQcfRe^J[G6BcPbeRcR?NbM]aM^aM`^L\[HVL;H8+52'09.;?3BM>NYGXaL`bM`_M]^M__N`eSfZM_UJYPGTD>H;8?46:258.37+24'00&//%./#-.!-, +*'%'$'$ )&% % $ $# &"&#&$!((##&&%$&%''%&#"""$&$'#&$&$$$#####$$#$#! !   !"!$"&!%"&#)&+) .-"//"01'35)48,6:07;28=47>AAJF@JVMZ\N]bSgJ>N3+42(04)1B7A?18M9=C26A29R@L^JZ_J\bN^YFTUAO`L\gSeF3?cO_dSafQbgTebO]N>J_M\:95$"(("&&&)* @:8D565.-! !# +"!,$#1*1H@NRKY[M_A5>J*.1/5;:>GHCPUL^MANOIYC>N72?/+6!!)$#%)* +-'*, /.-,**(*75?/.4     !" 3(1JNhSfeRgfQfgShUBSC2@^J^hTjI8Hye|mZpbPdXJ^DAN?;C.$.3'36-9A4DNAR>CL2PF6FN@O:26))#,-$.*#UHMZLXL?O:2@($/&&'!0!#G@LL?G1%%"!!%/'UMiSMiSNgMK_:>H"#)(*..1HFS\Wj^Xng`waZpQI]F>O=7E0,6(#- %!"'+&17 +!&:,6H9FE5A^J\`K^\I\^I]_J`ePfbOccOecNeaMdbNdcLd`Ka`Ia_IacMgfOieNhaMdP@SSEW@9E98D.5>%16&24.07*%,.&.<4@MDTSL\-8>*8;,9=+9=+8<,7<-4:07<6;C#! ""###$' '$%' !/,,:95@A@GK@EJNTZX]fchrkn{uv}~jbmH;:G:0;<1;<.:;1>@1>?1?A0>?0=ATOc&#,1+4/,4%)&.3-2:03<-3>*0;1/;PCTcRjkXslYtbPi^Jc^Lc^Kc_Ia]H`\H_[G^[F]ZF\\H]^I`_Ja]I^XF\WG[J?K410&( $(A=>E:BFEECN--2*/2*79+9;,<=);>*>@,=@+<>*<=);< 34..001/0/!33"11#//"/0"./%-110<>6C^NfiUplXtiVqdQlgRmfQlfPjdPhbNf_Jb\H^^I`_I`_H__J`bNdbLb^K^VGXNBP>9>&'!"!()"$'-$+&%.+.81:H:CSMX8;C4;FLLcVRpYVv>9K?;QKAVOJeEC]<;K*.6**$#))(.3OHVf\s]Rgh^seYqN?N_Vn_WnRJ_OJ\_JZdOb]J\[H[ZFZZF\`Kb`KcdMefPgfPfeOffOggOifOhdMfcLebKebMfbNgcNieRi\K_ZNb84T?5TA7L:1K:8jXl@6> &%fVlm_yk`{RK[,(.+(+(,*,)/+2/3142324454646677 88767453 65!87 67!56320,17HH?G=05=.6UHTXGX]K\]Oc%#'% ()/FBPABJ".-#13;ESLLfMMhG@SSKhXUsXTrXTtWRrLLe5=J58>)-.!*+39AON\^ShH;HbZqbYpVMb`WnYFZXDX]I^cMdcMeePffPggQghPgfOggOgeNgbLfaLfbLfdOheOhiUnZL`JFVDGS3>E#5946799;#:>8:78897868798:78776754210/,,+**((&'$'#%!%"&#($%"%"%#QI^OBR_Pc_Oa^R_eYZM?9F91H91K;3J:3J;3L;4L;4L:2J:3J:5E50J83I70VFA_KFaKHVA8UA6XB7\H9YF9XHHpb|(#'vh]Re&(*! *'*&+'+',',','-'0,1/2041645355667776757576&<<%:<7775543/!20CBMdRio\{q_}iVqcNh`KdbLf`Kc_I`^I`]G^\F^\E^\F^\F]]G^^H_cMcePedNc]H\dPfcObtaywdxsauk[pfUi4-4 34<89A0793;>2>D445L=5O>6O?8O?6P@8O?7RB=UC>SA>^JJfMMbIGXB=\G@_IA^H>_GAdRYq`w'#& tc{TFU%%(&&#*$*&+'+%,%-&+%*%+'-*1.4131313253444454548655655443412.1.-47YL`l[wo^zp\yhSo^IaaKecMf`Jb_Jb^Ia\G`]F_\F^[E][F^\F^[G\_H_aIa_Ia]G]VBXaOcp^qlXjeRcYHXE8E*!% "$'),88@=6<+23%477?LMMhZVxXUuWTsXTsWSrVRqSQqLLjFE\5-5106#"*)'59jSjiSimUjlSjiQigOgeKedMfiTohUpl[vaTkbYq6>I47547767!6:687:8:696878786655340.0-..-*)''%(%&"'#(#(%'$%"%!&"'#(#&"$!$'#)$''( 0',kYihTd\JW>14}^QJQB8UF=SDS\LaiWjiVhfSeF2;K=J3.5   5/9CAL/8<,,(49>DTQRmVRpWSqXRrWSrVQqRMl:/?FD`@?W36D*-mTmjTlfPhdMecNegSkcPg]Qg\Xo9;D1:B!5801234657664433445644434332201-/,+''$$ #&"%"#"$ )$($(#&"%!&"&!'#%"$ %!'!&#'' %4)/eVhVFSF8@E;?~znrI9-H:.J>5G;4?1(F7.J;3J;3H93F85>0.D42VDE[INVCJXCKXBBdLNgNPeMMUBCO>@P>Is^r mZoVDT **%!%!*%+&-(.(,'+&*$,&+&+&*&+(,','.*1/31424255555432224422323210/..0<^JI`KH[GB_IE]FA^F?]IA[GA^KSq\m s^sO@N'01*&,'*$-(,')$,%+#*#+$,&+$+&+&*$*%*&+'-*1/32323333222301/1(:=22445533-,&16QEYdSmn[wm\xn\xjUpeNjN;MiQmeNjbLdaKd`Kd`Jc_Jb_Ia^I`_I`_Ga]F^\F^\F^\F^ZCZXCZRAT]MbhUhbL]^JXXDUK:I4-6" '    !%1.3VL[YL\H=KNCTUI[QG\A6CVPnRMlQMlSMlaTlMGX4=C241032456636#58344433210.0-.,-+,),*+)+)*(*()((&'%($'"%% (#%!"#""!$%' '")"&!&!&"&  1,3dQ`lWgRBM@52]NRugNWeKSkR]cYfRGG]UZI@>_U]QEDI=6OAh`nLAAZNRQCF@32L<:L;6TEApdjvn[j  lXi\IX.15)$+'+&*&*&+$+$*")"*#+$)")!*#)#+&+&+&+'+'-).-00121304031324464535342211.-*).4:PG\cPkm\xcQj N;QbLhdMfeNhdMh`Kc^Ib`Jc_Ja_Jb^H`\F^\F][F]\E][D\ZC\ZD\XC[WBYS>TQ?SWDX^J]eQebM_S?L=-4O@M<2<&!(  )$)E7EA/8H;KOBUVH]]Pf!7844333333100.,)+**()&'$'"$!!  "$!%"# $ "!B><!$&!&!"""##$"# !H>G cQ`oXi_L[C5?9+-xkRDD~QEII=[Kb2&0gVqlZtp]z^L`E0=^IcbLf_Ia`Jc^Ia]I`ZC[QRBBPA>TE?bNMfPRk}dSa%"$p\mVIW:4;("(#($*$*$*%*#)")")$*%+$*$*%+&,'+'-(+&)%(#)%)$*&+'*)*(+)-..,///0-.012100.-,**)()"#*/791>\LcgTniVq4%. _NfE7F7*6cMf\G^UBW)0'$_I^_J`^G_]G^]H_^H_^H_]F^^F]]F]]G^\G^[E[U?TS?SXDXiTjt`ur\qp[pmYnfRf[M_NBRF9GH:J0!'  !$!$"# "#""!#$!##""!"!""!"$ #*,,244!"     !"#% [Rg   ("(gUdmYkVCOYP_A5+yrtggNTfMT_ELWKM|UC@`PSVEBZKOXKNs\ctVYqPOyxfw[M[  gTiaQd?6?&!'"'!)$+'-(*#*")!("*%*%*&*%*$*$*%+&)%(#(#("(#(#(#'#'$+(+)+),)-.-///--*+$#"!#" )/31C00A()8C8NTBWcRlI;N!dRo"! K9L5'2MENDKULS{w|wxu`S`TDBRDHcV]UFCQA'#)$+&+(+'*#(!(!(#(")$)%+%*#*$)$(#*$+&(#'"'"($)$($*&*'*')&*')')****++,-/-0+-,,++,1$/7%-7*,<30=WI`L=O9+;lXw6*9K=P;.A  B2BbKebKcdNfcMebLebJbbKcaKcbKcaKbbJaaIaaIa_H__H__H^`H^P;OQ@ScRglWnqZpoXljTgbK_=*59+85+5 !""   !! "# $#"!!!!   #$+*  0&,p]n`LY_KX_JYO>I:0+`WhxtjatXKUbXkQCFPA?SC>WFB[IEVD>rep|j~kWjXJW r]nkWi:.8,)*%+&*$*$*%(!'!'"(#)#*$*#&' (")#*$(#&"'#(#)#(#($)%+&*&(%*'*(*()()(,+/016@HU!45--++((&'&+%.*+582>@3E_Me2'33(5^Mg/!1 # 0#-YD[bLf`Jb`Ja`JbaKdaJbbJbaJa`JaaIaaI`aH``G^_F^_G^^G^_G_]G_[F]Q>SQ>TSAWbPfiVk_J];)1TBQTAPG7C5*5    !! !! #"!  !!K?L kZjo\lbO^;-73+35--yu~wVFKqhO?<^NPWIGUFFVFGTFG^QVyn<.9nYkH>F  dRbr]p9-4,*' ("(!)$*#'"( )")$("' &(!*")"(!'!'"'"'"'")%)#(#)$*&*%(#($(#&"%"'$)'%% "&(('+*-,+,+,*,(*$&!#;8EN?S;/=B4D`Oi6(5<,7?+I*)5&0[G`aKcaKd`Jb_I`_Ia_Ia`JabJb`I`_G__G__I_`I``H``G__H_`I`_H_]H^^H_[E]WBZK8NJ:NZMbVFXS?P[GW]HYYAS!      " *!%n^s*"(!"& "^M[`KZ=12I?MzPFRYMWshzWGGYGFP@VI5KLFlXh`P\lWdH8AD:CL@BWJQ|upo|pYIGYIF^OPydomzp}bkR@8pYi1),  L6FUC[]JcdRjfRlfOkhRmo[s^I_]F_cLefOgfPheOgeNfdMedMedMfdNfcKcdKcbLdbJbaH`aH``H^`H`_G]_F^^F]]F\\E\^F^]F^ZDZQ9U<6O:6L94r}|rzoYi& +.9;.6wcu:,2(-0$& %'!( ' ' (!'"%*$+$ $ ("(#'"'!*$)$($)$(!(#'"'#!.-]MQBAB%%!'!&"%"(%&#%"&$&#%"$!%$$"&"(&+*+,*+(.$)))1XKeZHabNjjUriUqiTor\wgQjaJbaKdfPihQkhRlfPiePhfPieOhfNgeLfeLfeLedKccJcbJbbJacJbbIaaH``H`_F^^F^^F]`G__E^^C\               !!   bSe^M]B29>.2>/5M>F\JW>382(#h_rriulsi^Tlvxx\MWeYgK>:WHKI;5M>8O?9R@:rdkjfrh}[QeKCP DH`R=Ho[l:,4)+/%!% '#'!(!(")%("& (#(#" ' ' '!'!("'")%(#&"'$($&#,)+1/#.+&!(#(#'"&"'$&#*+-/*+''''&%&&('()*,+,+,)+'*$*"'+ODYdRpeSohTphTogQmjTonYrhRk]HbfOjhRmkTnjTmhRkfPihQkiQkhPigOifNhdNfcLddKdbJccIccJbbKb`Ia_G_^F^`H`aGa`F`             J:GiTdgQ_^HUbNYVDQ>06.(#6-'~`VsSH`|wYOhtm[Vlzof`~ne{eZhM?Z,4K%>%8T(8S#)6jS`s[j6+2+,/("& '#'$'!(#)$(#%!($,&'"&! !%!%("&!& (!(#&"'#)%%!&!&"&!&#&!%!'!%"%"&")(NJZTBQQ?OVEWVGWXHVVGTVHVXIWUFUSFWSEWTEXWHXZIZcTmtyhSojSogRmeQmlXsnXs_IbdNgfPjhRkkTnkTnhRlkSmkTmiQkhOifNifPifNifMfcKdcJdcJdaKcbJdaHa`H`bHabHa() )) ')!*+")+"*+!)*&&!**#,-"+-#,+   ! !+*-3(//&-iVggRcgRaN=H8*2C:IRObGBLda~c[v_WnXPeXL^e^{[Rc{tOERupwrj_pPB@PBBSSVK==K>9XGHXHJn`qoaroatEBR!+A.;[&0M 5 _L[v`rJG"84Az~qpie}B7C1).9)1-"(:,34*25+16.7:./UK]SOmd^{[NUG96QFGSPVF98O@Ar^op\osawiWkPAR++G .L)F(B1;6HRBQ]K[`M__K\]L\WGWXKYZM\ZM\\L[^N]]M]_Pb`Rc`Ra^P`]O_M@P:1=I@O]Qc`Rd^QcbTfdUh`SfaSdaSecVgeYjfYkhYmi\nfYmi[nk[ni]pl_rl_rg[oj\oi[npbv}jxe|xd~ze}h{gp_weWnVJ[]Nal[qxe}~i{guazjZsfYvzuXE^bNhdQmdPleQmkVqgSmYD]_IccLgeNiiRljRlhPjhPjiRljQlgOhfNhgOigOihNigOhfLedJdcIdcJddJe(#/)#/("/&"/+$2,#2PEXzis_tr\p\J\L?O%(+       !fUiuc{RBR<.76)-<-2N;EcQ`E7>.&%0-1UKd=0*9)0=)2. &2&,"B12*^Tg#%b`^U_J<6I<7K=9I:6gS_q[mq^r`RaI;H )".!+H/M*G)C2+<)/0G0)93+81)5/%2,#/+$1+&3-'5.)5.(5+%1,(5*&5+$2,#0*"/)".-&3.'4-%5/'5/(6.'5.'50)72+:/*80(61)70)70*81)80(52*83);5,;5-<7/?5,>7.@8/@:0@90A:0B:/AWG]kk}js`xOARE8Fzjyd~kj~hr`yk]w_Qo|u\I`aLgfOjeOkeOkgSop\v`JcYE\^H`_IbcLegQifOheMgdNgeNgdNhcLeeMffMffLfdKfeKecJdbHabIbI;HKA?3/B23:,,E550$!=,-.%#B234&&@/18)"ZRfMF]  |mnid{M?:OA=XHJiTas^qq]qcQdK>NF8F6+4B4=%)A .1%?@3D+3M@@WC7F>4AHKK=KJ=KG9FD6C5+5B8DB7CD7DB6B@5BA5CA4C=2A?4B@5DK=IG:GB6D?3??2?E8FC7FC8GB7FF:IH;IK?NPCSPBROBSL?OM@QL@P>4?ODSKAQMASOBSQDVZJ]n[rr_wsawubyvb{s`yvb{q]tr^swbywbzs_wgVnhZqeXqrq\HabLgeOkfQleQlgRnn[ufRkYD[V@X[E\]G_bKddMgbKd_IbaJdaJdaHbaHbaHbbHbaIbaIc`G`_F_XHUYGV=/9B1:?.8bShq\ut_wjUikVjQDSNBQ82;      !"   /'.o[nmYjeQ^nWd\JV6+-?9Ekj:0,~xeuK@O(3',+!2&*), #+,##7*)B219'(B.+VNd!~72AyvbnmYfs^qt`slXkWEWD5GL?OJ;G4&-9*2-&'%)=_M_@4E)/H7=W)#-'!))#,)#*+$++$+4*2I;IG:HD9FUFWM?M8,6' '%&' )' )&('!*,#-Y@KZBJJ;IL;IK;II:H2(2.&-2'1;-;B3BB4?I8CI8EJ9GJ9GM>LMAPB6BWGUSCQL=KN>LP@LJ9FI:GO=LQ>LRAPYGV^L]aO`N=KKL  hWhdRblYhYHS6)-HAKvroe~^?E^CHI4<=0)=*2;,72$+.!':**0#"D2-H31=++KBVOFZqmeb1(7j]usYht_qq]qgUgSBU?2B?2CQBQSDL;/3V@JYBLC;G%&+,/2?05F=N@=Q~fdXRm@9JG?V|~syiJ8C TDS\K[VEXD7HB6HL?RVGXQAPD6=WFQYHU9,8A7F+ &##0B7E!1175H<8HQG[{i|z|k~k{h~kzgtbyvcyubyo]tkYpp_vud|vd|xe|vdyvczwe}vc{r`vvdzve{wf|ve{xf}yhvf}wf~wg~wg~wf~yfygxe~yhziygyhwffYnKDQA('S9M>6GD:IA7GA9KB7IA6G@4D?4B6-:xf~mYkO>LO>LR?LR@MP=KP=KK;FN>JQ@LM=JO=JO=JN=JP>KNS\M]eUcaTa^O_]K\fWm@;2BF9GJ=Jo^s`ShSHYYK\eXhgZlgYkfXj_RdXK]WK^TI^>=Q+,>''9('8$$3$$262CUK^XL\WK]`UgcWlgYndXmbWj`TfYL_we}s_rRCPTEROAPM@NSETK?JI=GI=HOANL@LNBPMANL?NL?MJ>KL@MJ?KIJRERB3;UDTVCTTBPTAPXEVWFXZGYZGZZFYWEXWDWMCYHFdh_}zms{leTqhVthSplWtgTqfRomZwua|iUm[F^[F]_HabJdbKfbLgcLfbLfcLfeNg\JYC7C^L^t`vp\qYHWK:F\K[\L\MAP@7G<3A ?3AUE\cRip_ytbo]v  paxn^rlYmfQbUAMJ9D4'/=1zthMME8E:/=6-;2+7D8GK=KRBOR@MG5AK:EkXnXJ^5*30$,D>JICPG@OHBQHCQLGVKETEBRA@W02D./C--A)*=++=3/@IBSUHWKCSICSIBTIBSJBQOGTI@KfZmm`xcQa""'% .22!     !  " ##$#%    !$F9EP>MUCRUDSWDUXEWXEWWEXXEXVEWSCUJ>SCB]EA\]TouirqhYtQ?[aMjiUsgTpdQn_LhkVqq]vcNf^G`bKebLfbLecLfcLfcMfeOhhTifSek\nu`raO`aN_]IZZFUS?MWFUYHZSCR5,8.&4/&32(57-:C7G#'/)8qd~p|y{mM:=F65L=?;,-H79<.0<.-B039*)G88,F23@+)T76G7A?)(E:JGNE6C<.;8*72'40'51)66,:7-<91>?5BF;IG:E&$UEQO?KJ8CI8BG5>`M^gUi\Nc>1=;0:8:I6O>R//@+,<));((9+);1/A98J:;M>CUAFZCEY>CTCU9>Otipc|lXk"#!!"!"""$#!"& % % &)#%#"&!$$%%#""!     &),E8DO>MR@PTARWDVWDWWCVVCWSATP>P?1B81D@=VD?W]Ql|mx|r`Li\FdgQogSp\HeSB]dRnr^{pYt^HabLedMfeLfhOigPjfOiRBRUCTTAQUBPXES^JZbNaB1>cP`XFUP@LH7A&N?L@3?5*6<2>7-:5/<,&2)$1 $(.$/7*9F9L}rvbQfB5FRIT!!!&5%$E0/7"!Q65GOI=8G;=NDI_AG]GJ`]Zs]XnPLbJHZ00PF7G5*6:2DF@WKD[YQjna|rzx^LhXGccOmhTqcPlVDbaNjq\xt^zcMgbJceLedLefNggOiSAQXET_L[bN_iThq\p\I\[IZgSeiUhdRfE2<+=/:B4?+!)?1;Me\x{mKAT=4FD9KdU]?11#$ # %((3"!A+-J0.D//G>O;.4G=NJ3A@4BC6DF8F@3>JNQ?PQ@QTASSARTBSR@RL2>J=IA5AE@PGEX32BzmH7CK:GH7BE3==-7J;I_[pb\spf}|nl`t[PcTJ\NEWOEXODUPFWWK\]OaiXkuextg~|swkqbx^M^"")"*,#..$.2&02'04'/4(33(41&13(45*76*77+86,:6-;6-99-:8,:7+97,:7,87-89.96+6J=KqoocYk(%&"% %$$ $#"#! "$"6.6J;IN6JLE^PIcPE_aTmpazovoWE_ZFbiTplWsePkaKhfQmq\yr]wjUm`G`dLeqa|ud~r_wL?QH9HN=KP?NUBQUBQP@MJ9HH8IRDV\L`cSfYK\VEYhWjeRgjXlOAP-"-8/@5F>2C9.?wSDR?.):*+"1$ '"(. %;*),B++6)2E,-<.9D:M<2D^Mazf|zhJ:HOAO,$,#%%&("*H;FbQaiWigSdq]n}gzn\n1.9!!+ "+&!+ !)2.793;;3=<3=6/9!&3/=zs2+9I:H9-:C3A?0=6*5:/;6-:3,8:3@7/>90@;4C<4D>5D;4B<3B;2B92A92?70=;3B7/=6,:;2@>4B@4CA6EA5DB7H>5E?7HC9H<1=@4@C7DF;ILBOOCQJ@MJ?LJ?KMAMQGSRGTRHVTJWWJZTHVSHWSHWVK\TIYTGXRFVPDSPESI>J<4?ve|qrt??J$ % %$$""#"""##"!F9HL=KL=LN=MO=NR@QSAQWEVUCTM=K@2?2'2=5GLE\ME^OF^SIbcVpueprjXs`JfhRmmWsiSoaMjePllXus_{s^zdOh`TjTDVZGYTBRI8FD3AC4?E4AE5BC5BH9IG9IK=OOARP@PH9IN3BA4Czgzu`pm|i{]K[PAMP>DJ6?@17A28<,0@056&-<).>.27')G49D14L8:SBMW>CTANQAQQBRlZnqzsRAP:.85+3!  ?3=UCOTANXCOXFPN>HK=F !'%-43=74@PJ[SPc-.:!*!)&(1A=Licw~yz+*6OAQ5*7=1?>2?8,84,8 )'#!+'! -$"1$"/"!-!!-"!/*''))&($#0%%1&'3$$2%%1%&2$$3#$2#"-)#**$--)2/,430751:2.60,4/)05.6:3=71;84=:6@<6A?9C?8A@8D?9EC8A4-5607m^tmqs|l!*.#&$ "$% $ $""!!  &*-I:HI9IJ:JL;JM;KO=NQ?OP?NO=MI9F;-:0&1@8KNF_UMgTKdTKd_Skm_w}lqta}ZHdcOjlVrkVrgRneNkhRopZwu_{hXpdRjl]wl]vj[un_xk]xn_|m_|m`{m`{VI]o_{jYqhXohYpgYrj\ti[riYqqf|t{qg|{qc~{x{~}?4=o\t]PdUK]UHYTGXMAOOBO^L^aPabQbaPa^N]RDSXJ[rf{ykyip`vdSezh~dUgMDSLBS_Sfrdxwg}wf}wf~sbv<3?QBP5)6:.=5+8;/<8-:   !%'* **++*"*#'0(+5*.:,2>.6@3:C3;E3:E5:F7K8>K5;E6I8=I8=J5MN9.7+$+4+42)09/9E8DF8DG8EH8FE7CC5BA6A1*290:I:GL=KN@MN?MF9GK:KI9HE5DC5A7-51&.:/5D4:VBIWBI`HQ[EK`IPfNVZDJWBFA27S>FL:C9-2p_rtsoyiyl^Sd>6AXK\n`sl^ueYnbWmi^wl`xj]vaVmOH[VM`;1;LEUVN`YN_PHUPFWNEYYRh[UlZTjGBQ-,3RBQ=0>E8G?2A?3@) (                     #!)$)2).8-3?.7B/9E19G2;H2;H5=K7?M6>L6>L7AN8CQ8AQ8@O9BP;CQ:CQ8CP7AO8BP8CQ7CS2>M'2=WTfg[rtcyygqqBCP$!$%## " ! ! !"  !5,7I9JL;JKLN=IP?LMMO>NQ@QS@RVCUVDVWCVXFYZGYXEWWDUVCTXFWXFXWEWYFYYGYZHYWFXZHYZGY[GZWEVZGYZHY[H[[J]]J\]I\\I[]K\]J\]K]]K^^L^`M_^K^_K_hTenWifyvtyetetgvsZipWgy^n{`oz`psZkjSefO_aL\aK\aL\_KY^JX`L[`KZ_JX]HW[GVZFTYESXESVCRSAOP?LL:GC6A 322YMT#"  &H@QF5B9*6:+6:+6?.:J8DF4OJ:HL;IJ9GI7GD4D7+8.%1IATPI_WMh\SlVKeJ?XgZuqa}|jstvceOkkUpoXtLNO>MO>MTARSARVDUXEVYFWZFXWCUYFYYFXXDVXEWYFYXFYZGYZHYYGX]J]]I[ZGXWDWZGY\I[\IZ[HZ[HZ\J[]J\]J\\J\ZHZTCTRBSSCUYFW\JZ\I[^J\_K\^K[[IZ[HZaM]|csx_qoVhbJ\oWieP`\JY_JZ[EVdN_jSdfPafPbbM^^JZ]IZjTfpYk^JY]IW`K[]IX^JY\HV[GTYFTWDRTAOT@NO=IK9F0-0         5(23'05(2<,88*45(3Y?IW@G        ! DDN{wyZM[qbswq:ARslaTjr_vwe~qt57>"% $& %# # # ##"!#"$#';/MSARR@QRAQVDUXFXWEVYFWYEWVDVWDW[I[ZFYZFYZFXZG[\H][I]XFXYFYYGYYFYWDVZGZZIZ\H[[GY[GZ[I[\I\]J\UCUM=MA4E@3BA3CF7GO>NYGY\I\\H[[HZ[IZ\J[]J\]J[[GYZGXXEV\HZ]I[[HY[HX]HY]HY\HY_L]_L\^J[\HY[HXZGW_J\`K\[HUYGUZFU[HV[FVWCSWCRVBPUBPR?LQ?LO1>oTaYCN    38B{|}q}|urkXf]KYoAFVQWik`wsax{hprpe{%$""! !"# ! #""##!.(0A3DF7GF7FE4EG6GI8HI7GI8GH9GG7F@1>4(26-;NF\NH^ME[UJd_Tm_Rl_Smk]wtd}{jrr~jM;IP>NTAST@SS@RUBU[H[]J\\H[\I\[H\ZGZ[G[[I\YF[[H\\I]^J_YGZXFXWEXVDWVDWZGZYGZ\I\ZG[ZGZYH[ZJ[ZH[ZI\TEWG:J@2BC5EP@R]K^]J\[HYZHYZGY[HZ[HZ[IZ[IZ[HYYFXWETXEU]GZ]HZ]I[[IZ]HY\GX]J[]I[_L]]J[]J[ZGXXEWWFUZHWXETVCQWDRYES]I\fTjeTg]M_YGWQ>KP=LL:HJ>I         "#B5BB5CA4DC5DB5C@3Cx`sWGT  !#"#!######      % '%%%.7}{nbp|q~aTexevpqvbt}kzwetdUepl{+5Fb^tj]s~joqqPMZ!! "!"% & "  !!  !8-9A2BH8HI9HJ9JH8GF6EE3CF4DF6DC4B=/<3'46-=LE\KCYOG__VobXr_So\Pki[vtdygmsP>NTBRVCUWDTWDWZFYZFY\H\\I\]J]]I][G\\H\\I\[H][H]^K^]J][J\XEXUEWUEVWFYXFYYGYYFZ[H]\I^[I[ZI[[I[\J]RATN?OWFWZHZ]K]\I\ZHZZGXXFWZHZZGXZHZZGXZGZZGYYFVWDT\IXZGXZFX[HY_L]cN_cO`aM^_J\bN_]J[^J[ZHYZHWYGWYGVYFUWDST@OUAQcSgh[rl^uj[s[K]R@OO=KL1@{g~UHW#)%,%.&.'/'0(1'/(0)/)/&,'.'.%-%-&.(/'0!''0-7}z_Ycyvo{%-6.1<65?96A:7@64=./868?MQ>MQAPQ@MQ@NP?LM=JLNOAQP@R|hVIY,-7/3?03?03?05A25C5:H49H59I3:I/8D.5B.6C+4A+3?'3<&1;%0;#09#/9$19$/:$1=ggyZKUz`_mILZLNaoe|wf}olppFCN"""""  ""! ! &%*:/SFXZLaZKaTEYL>PE8JRF[WI_[MdVJ_XJaJSK=SQDYRDXVG\\Nb_RgZNcVJ`VJ_[Mb[Mc`RjbSkjZsfVn\MdfVnl\tj[reUl_Pg\Pf\Of^PhbSkgWodUl_PeWG[TFYSFYJ>OI>LGGB?H@=F>;E76=35<;7@I>LM=MQAPMNM>OO?PWEWWEXYHZZJZXHYREV]OdeWlcTkfYpk]vn^viZqfWoiZq9.::/2@TDVZGZTCRUFVRAQO?OJ2A7,95+9=3BB7FA6DF8IB3C@1?>1?K=ML?RVH\TG]TGZbRhgVm^McZI^\KacSieTkbPfaPe^Na`Od\LaQCTE9ID8IPBUNARG1=^PedTi^ObYH\UDUUDUeRfn[ru`xvbyxczuaxq]rn[piWldTir`yvc}fVm]MajZmhWl^La`Nbrawq_uwd{ubz}hxf{rawiXlhYkp_wp_wm]s[K^YH\VH[ZJ^cRfZJ][H[^L^SBRP?MiXldTgK=LPAQI:IWEVWDVO>N8,8=2@XGZYGZ]L^TFURDSVHYVH[RDVSEVRCTZJ\PBRM=MMOD6FL=NUEX^Ma\L^QASM=NUEWWFYXGZbOcaPd\K]aPacQecQedSeaN`]K\G8JJ;LTCVZG[\I\]J]`L`bNb\H[G9JJNL>OH:JC5D6+8A5CTDWVGZQCURCVM?QI:KF9IN@QPDWJ?P9/=;0?C8IH=MJ=N>3B>2A3(4?4BH3BA6FA5FNBSSFYWI_bShdTj`OfcQhcPgcQhaRh]Mc[K`]M`\K^aPe\Ma\K_SCVH;LE:IE8HL?PPCSG:J7+8C7EL>MTFWE8FTCTSDUKOQBTUEWNAQI=MD7GQAT[K`cSfK=MNARVFX]L`UGYNAPK=LLQWFYTDVTDV]M^[J\VEXXGYYGYTCUaQd]L_UFVH:H@2?P@QVDVM=NLMN?QH;J3(27*5NAQTEWN>O[J\VFZQATM@QQDWNARJ=NK>PI4DH;NOBVOCVG:KD7GA6DF;JMAQD:IH:KZJ_^Nc_NccQfbPddRgeSi]L_[K^eSj[K^IG;KREWO@Q^MbgVliWlm\tZLaXI^_OeZJ_L=PTFYD9J?2AB6FA6DH;MK?QD8FF8IC8IE;JE9HE7GIMPCTOARQCSQBTOBSHOK=NQBRM>NNARL?NI;KKPPARN>NG9IH9JK:MO?QJ>QOCVF:J9,:@3C\K_`NdUFYWEWQ@TI:MKOM@QH;KM?QF:JM@RPCWGO[NaVI\QDVSEXOASRCTTDXUEY]MaYI[[K]XIZO@RN?PF9HC5DE7FE9IF9JI=NA6GF:KTGYP@Q\K__OdVEW\MbHPD7GC8HHOPCTOAQF9I@5EF;KD8GHNOASQCTPCUPBUN?QJNE8F=1?B6ESDTXI[/"-B6F_OeTDVP?OSBSSBSQ?QQARRASTCTN>PSCUSDV\Ma_Na_L`^K`]K^dQdaOc\K^XHZWEXTCTWEWP>OUDV]M`\K_ZH[UFWTDUTBTQARL=NRASUDUUEWQ@SQ@RP?QG7IH:LJ;L>3D<1BC4DRCV^MadQfcQfcPf\K`UEYPBUOATM?QJ?OC7EN?RTFYRBUWG[TEXQDVSDXL=QL=PTDVYJ]WI^XH]WH]UFYVFYL=OO@SQBSK=NK=MI;LF:JF9I:.OKNXGZWFYSDTJOP=NK:KP@RSBTYGZRASO@RJQSDWUCXYI^WH\OARUFZ\KbPBUG:IC7FF9ID9IC7GK>MK>OK?QK?QREXWH\]M`\La]Mb^OdXJ\^NbbShYI^YI^\L`UEVTEXXI\YJ\]N`^ObZJ^YK^XJ^SEY^OdTFYUHZXK]XJZVGZ`RfXJ]WJ[\M`UHZZK^`QffUkeTjaPfgWkiXnjXngWnfVmbRijZoeTibQedRieTkbRe]N_ZK]M?OB3B^NaYH[YI[\J]ZJ^bRfcQg]K`[I\QBSJ=NUGZZK]_OacSemYokXm\K^RBSM>OVGZM>N?0=@0>RCURCUI=LSFVYJ^^NaYIZSBV>1OWH[O?PM>MN>MM;JSAQF6B<0:E8HD8HNF8HJOUDVTDVSDURDTRAU_M`aOcbOd]L`]Ka^K`WFZYK^TDVTDXSDYRDWWG]RCVPBUPATTEZVG\WF\[J`^NdYI^VFZQ@VN?RUE[SCXSCWTHZSEXK=OF7HE7GD8GB7GF;K=3BL>OUFYPBSM?QPCWJ?SSFZ[L_UFXTEXQBTVGZUFYXI[RBTZJ][J`XH\TEY`Oc`M``NbbPc^Mc[K_TEYH;KTEXUFXUFW_Nb\LaYH]\K`VGZUGZPCURCWWH\VEW`Na[J]cRfbNdeSheTjhXnjYohUjlZofTjjXocRg_NbVDXSCVRBTSEVOBRXGY[K]jWiZI[\K`YI]UEY_Na`Pb[L]QCU[K^XI[WGYWEW]K`aNbTBSQ@QOBSVGZUFWRDUXHZYI\]L^XFXN@PQCSeTieShaNbVEX[J^VFYPBRIOP@RN@QJ=MK?OL@QPASOARQCUYI^dShaNcXFYZHZ]K^[I\F7GO@QaQdcQf`NdYI]YJ]UFXQBTVEW\K^^NaaPd[J][K\XFWXGXWFYXGYVHXSCSZJ\\K_WFZ]MaWEWP?QQAQQ@QO@QTEVUFWTDXTCWUCVN>NVFYN?PWFXSAUMPM@QO@SVFZSDWVGYPBTTEWRDUSFXRFXUFXXFX]K_]K_fUkfTj_Pe\MacSi\L_[K^aQf`NdYH\WH\YI^XJ\WI^ZK`bRgfTiaPc]L__Na]K__Lb\K^`NbaNbcQdXGXdRggTjeSibPd_OeaQf`NcbQg`OdbPd`Nb]J\VDVYH\]Mb\La_Na]L_[J]RCTN?PVGYYH\UDV\J`XFYL;LP@QSEV^Pc]Oa\L_YJ[XGY^K]UCTI;IK=LUGXQCSZI[ZHZ_L_VDVUDVZI[XFXTETL;JM?OTDVO?PN>NF8GINIOJ:KLPI9IRBSSBTN>NP?RLPM?RTEZSEXM@SPBUG:KOATJ;NF:KRDWYI]\LaZJ^WFZXG]WG[WHZPBSN@RXH]cQg]Mb_Oc\MaXJ]RDVTG[L@QXJ[SCUWH[VEZdQf]M_cRhcQf]Ma_MbgVjaPc_LaUDV_Od`OeXH]^MccQgeTieThZJ\UFXSATSATQ@SRCT]L`gTifTjaPfbPgdRgdSheTicQfaOd\K_\K_^Ma`Oc_Na[L]XGY`Oc]Lb`Nc]K_^L`[I_UEYWH[TEXWGZSEW[J]]K`ZI^aPefTjfVkbPdYHYWGYWGYbNagTgXFZVFW]Ma[K^VEW\K]aObbOc^K_aOcP>P;-8L>LN>NH:HJ;JRDUPCTQDVXGZWGYRDVXI\ZI[VGXXHYZH[[H[_L`_LaeSg`MabNdaObJ:KF7H[J_^La_Mb]Mb]McTEY[K^]L_\K][J]VFWQASO>OH9HL>NP@QUFWWF[UFZ[K`]K`\K`ZJ^UEWRBUOASL>NO?ORBTTDWO?QJPN?QN?QJPF9ILOJ;LUFYUCWUDVVEWL1?@2?LN;.:J;LYG[\K^ZJ_XH\VGY\L`]L`UDUTCVWGZVGYXHZQCUSEWP@RYH\VEYVEYZJ^WF[UEXSCVWGYYI]SCTSEWUGXN?PL=OM>QP@SSCUP?RO>OL;LH8IC4GJ;KM>PG7IJ9IF6FF7GKPTDVPASO@RUEXZI]PARP?QQARN>OQBTQASJ:HK=NQ?RR@RL:KSDVMPTEWM@O1>A2BK=NSCUK:KN>PSCXWF\[J^UCV^K^ZG[SARJ:IN?MWGUSBSVGYSDWSBTM>MSCTPATMN\J]ZHZ\K^P@PQ@RRBQOAQQBSN>OR@ReRdYHYXFXZI[^K]^L_\J\`Ob[I\RAQN>M[JZUEVP?OVDUUBSTCUVEXRAS[H[]J]]J^SCSQBRTDUVFWVEWXGX\J]^K^WEVUCUZHZWFY_Ma\I^YFX\HZbNc^K^XFWRARUDUTBRYGYYGYUEUTBSVEVYHZ[K\YGYRASR@PQ@QZJ\TDUMPRCVQAUWFZZI]WEXWEZWG\N?SSBVTEYK=OL?PRDVL>NJ;KJ9KWFXZH\VFXXI[\LaVEXN>ON>OXHZVEWSBSP@QQBSSCUTCUUCUZH\>/H9GI:HUFWSCTN>OQBSF8GG9IB4D@3@A3@L>LO?LO>LSBRN?ORBSUCVTDWSDWI:LG8FO?OM=NE6FG7FSASO@QL1>XGYTCUUDVUEWSBTP@PJ=LJ=MI:JQ@Q`M`YGYJ9HZG[SAS^M`SBSYGZSCTUFWO@NK=IF6ETDUYFYUCTRARSBSTCUYH[\K\WFWXGYXGYSCTQBRTDUWGYUEVO@PXGYZHZVEVQ@P^K^bOafQfdPd_L_ZIZR@RP?QVEVSBRUDTZH[K9IF7DL;KSARWGXZGYWEWUBTZF[VEXP?PLOR@QO?OM>NQAR^L]\J\YHXZHZ]J][H[\K\]J]]J\]J^_L`TASE4BC5CN=PMPRCTO?QRBSWGYYI[ZHZWEV[HZVEWRBRUCVYGZTCSSARL=MLNN?QKOM>MJ;JJ;JVEXWFYUEYUEWUDWSBSXEXVEYJ:HRBSN>OG7GJ:IF6EF6E?0?B2AKD7FH;K@2AJ;LQASVEX]M_O?OTCTTDTLMH8ITDUK>LH:IH9JN?OH8HSDVWGZUFWRBTL=LE8FN@QO@PWFXWFW[I[PAQUEVUCV`OcYH[YG[UEWO?OJ9GM=LM>NLNLPSBSVGXZHZ\J\]K\dQd[HXVCTXCVWEVXFW_L`^J^]J]ZG[]J[G5F?.>PASN=ORBVWFYUDWTDVSCTRASK:KN?OWGYYHZYGZWEWYGZUDV[J\VEUWEVXEWSAPQ@QSCSVCVVBUVDVTDVP@PK;KRCTL;KWEXSARL;MM=NH8IF5FF6Hloaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-iterator-prototype.cpp000664 001750 001750 00000006244 15164251010 053411 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-iterator-object.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BUILTIN_ASYNC_ITERATOR_PROTOTYPE_ROUTINE_START = 0, ECMA_BUILTIN_ASYNC_ITERATOR_PROTOTYPE_OBJECT_ASYNC_ITERATOR, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-iterator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID async_iterator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup %asynciteratorprototype% ECMA %AsyncIteratorPrototype% object built-in * @{ */ /** * The %AsyncIteratorPrototype% object's '@@asyncIterator' routine * * See also: * ECMA-262 v10, 25.1.3.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return the given this value */ static ecma_value_t ecma_builtin_async_iterator_prototype_object_async_iterator (ecma_value_t this_val) /**< this argument */ { /* 1. */ return ecma_copy_value (this_val); } /* ecma_builtin_async_iterator_prototype_object_async_iterator */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_async_iterator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide * routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< * list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_list_p, arguments_number); switch (builtin_routine_id) { case ECMA_BUILTIN_ASYNC_ITERATOR_PROTOTYPE_OBJECT_ASYNC_ITERATOR: { return ecma_builtin_async_iterator_prototype_object_async_iterator (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_async_iterator_prototype_dispatch_routine */ /** * @} * @} * @} */ lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-helpers.h000664 001750 001750 00000002334 15164251010 054053 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_TYPEDARRAY_HELPERS_H #define ECMA_TYPEDARRAY_HELPERS_H #include "ecma-globals.h" #if JERRY_BUILTIN_TYPEDARRAY /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltinhelpers ECMA builtin helper operations * @{ */ ecma_value_t ecma_typedarray_helper_dispatch_construct (const ecma_value_t *arguments_list_p, uint32_t arguments_list_len, ecma_typedarray_type_t typedarray_id); /** * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ #endif /* !ECMA_TYPEDARRAY_HELPERS_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-globals.h000664 001750 001750 00000222377 15164251010 043652 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_GLOBALS_H #define ECMA_GLOBALS_H #include "jerry-config.h" #include "ecma-errors.h" #include "jmem.h" #include "jrt.h" #include "lit-magic-strings.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmatypes ECMA types * @{ * * \addtogroup compressedpointer Compressed pointer * @{ */ /** * The NULL value for compressed pointers */ #define ECMA_NULL_POINTER JMEM_CP_NULL #if defined(JMEM_CAN_STORE_POINTER_VALUE_DIRECTLY) /** * JMEM_ALIGNMENT_LOG aligned pointers can be stored directly in ecma_value_t */ #define ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY #endif /* JMEM_CAN_STORE_POINTER_VALUE_DIRECTLY */ /** * @} */ /** * JerryScript status flags. */ typedef enum { ECMA_STATUS_API_ENABLED = (1u << 0), /**< api available */ ECMA_STATUS_DIRECT_EVAL = (1u << 1), /**< eval is called directly */ #if JERRY_PROPERTY_HASHMAP ECMA_STATUS_HIGH_PRESSURE_GC = (1u << 2), /**< last gc was under high pressure */ #endif /* JERRY_PROPERTY_HASHMAP */ ECMA_STATUS_EXCEPTION = (1u << 3), /**< last exception is a normal exception */ ECMA_STATUS_ABORT = (1u << 4), /**< last exception is an abort */ ECMA_STATUS_ERROR_UPDATE = (1u << 5), /**< the error_object_created_callback_p is called */ #if JERRY_VM_THROW ECMA_STATUS_ERROR_THROWN = (1u << 6), /**< the vm_throw_callback_p is called */ #endif /* JERRY_VM_THROW */ } ecma_status_flag_t; /** * Type of ecma value */ typedef enum { ECMA_TYPE_DIRECT = 0, /**< directly encoded value, a 28 bit signed integer or a simple value */ ECMA_TYPE_STRING = 1, /**< pointer to description of a string */ ECMA_TYPE_FLOAT = 2, /**< pointer to a 64 or 32 bit floating point number */ ECMA_TYPE_OBJECT = 3, /**< pointer to description of an object */ ECMA_TYPE_SYMBOL = 4, /**< pointer to description of a symbol */ ECMA_TYPE_DIRECT_STRING = 5, /**< directly encoded string values */ ECMA_TYPE_BIGINT = 6, /**< pointer to a bigint primitive */ ECMA_TYPE_ERROR = 7, /**< pointer to description of an error reference (only supported by C API) */ ECMA_TYPE_SNAPSHOT_OFFSET = ECMA_TYPE_ERROR, /**< offset to a snapshot number/string */ ECMA_TYPE___MAX = ECMA_TYPE_ERROR /** highest value for ecma types */ } ecma_type_t; /** * Option flags for parser_parse_script and internal flags for global_status_flags in parser context. */ typedef enum { ECMA_PARSE_NO_OPTS = 0, /**< no options passed */ ECMA_PARSE_STRICT_MODE = (1u << 0), /**< enable strict mode, must be same as PARSER_IS_STRICT */ ECMA_PARSE_MODULE = (1u << 1), /**< module is parsed */ ECMA_PARSE_EVAL = (1u << 2), /**< eval is called */ ECMA_PARSE_DIRECT_EVAL = (1u << 3), /**< eval is called directly (ECMA-262 v5, 15.1.2.1.1) */ ECMA_PARSE_CLASS_CONSTRUCTOR = (1u << 4), /**< a class constructor is being parsed */ /* These five status flags must be in this order. The first four are also parser status flags. * See PARSER_SAVE_STATUS_FLAGS / PARSER_RESTORE_STATUS_FLAGS. */ ECMA_PARSE_ALLOW_SUPER = (1u << 5), /**< allow super property access */ ECMA_PARSE_ALLOW_SUPER_CALL = (1u << 6), /**< allow super constructor call */ ECMA_PARSE_FUNCTION_IS_PARSING_ARGS = (1u << 7), /**< set when parsing function arguments */ ECMA_PARSE_INSIDE_CLASS_FIELD = (1u << 8), /**< a class field is being parsed */ ECMA_PARSE_ALLOW_NEW_TARGET = (1u << 9), /**< allow new.target access */ ECMA_PARSE_FUNCTION_CONTEXT = (1u << 10), /**< function context is present (ECMA_PARSE_DIRECT_EVAL must be set) */ ECMA_PARSE_HAS_SOURCE_VALUE = (1u << 11), /**< source_p points to a value list * and the first value is the source code */ ECMA_PARSE_HAS_ARGUMENT_LIST_VALUE = (1u << 12), /**< source_p points to a value list * and the second value is the argument list */ ECMA_PARSE_GENERATOR_FUNCTION = (1u << 13), /**< generator function is parsed */ ECMA_PARSE_ASYNC_FUNCTION = (1u << 14), /**< async function is parsed */ /* These flags are internally used by the parser. */ ECMA_PARSE_INTERNAL_FREE_SOURCE = (1u << 15), /**< free source_p data */ ECMA_PARSE_INTERNAL_FREE_ARG_LIST = (1u << 16), /**< free arg_list_p data */ ECMA_PARSE_INTERNAL_PRE_SCANNING = (1u << 17), /**< the parser is in pre-scanning mode */ #if JERRY_MODULE_SYSTEM ECMA_PARSE_INTERNAL_HAS_IMPORT_META = (1u << 18), /**< module has import.meta expression */ #endif /* JERRY_MODULE_SYSTEM */ #ifndef JERRY_NDEBUG /** * This flag represents an error in for in/of statements, which cannot be set * if the parsing is completed successfully. */ ECMA_PARSE_INTERNAL_FOR_IN_OFF_CONTEXT_ERROR = (1u << 30), #endif /* !JERRY_NDEBUG */ } ecma_parse_opts_t; /** * Description of an ecma value * * Bit-field structure: type (3) | value (29) */ typedef uint32_t ecma_value_t; /** * Type for directly encoded integer numbers in JerryScript. */ typedef int32_t ecma_integer_value_t; /** * Mask for ecma types in ecma_value_t */ #define ECMA_VALUE_TYPE_MASK 0x7u /** * Shift for value part in ecma_value_t */ #define ECMA_VALUE_SHIFT 3 /** * Mask for directly encoded values */ #define ECMA_DIRECT_TYPE_MASK ((1u << ECMA_VALUE_SHIFT) | ECMA_VALUE_TYPE_MASK) /** * Ecma integer value type */ #define ECMA_DIRECT_TYPE_INTEGER_VALUE ((0u << ECMA_VALUE_SHIFT) | ECMA_TYPE_DIRECT) /** * Ecma simple value type */ #define ECMA_DIRECT_TYPE_SIMPLE_VALUE ((1u << ECMA_VALUE_SHIFT) | ECMA_TYPE_DIRECT) /** * Shift for directly encoded values in ecma_value_t */ #define ECMA_DIRECT_SHIFT 4 /** * ECMA make simple value */ #define ECMA_MAKE_VALUE(value) ((((ecma_value_t) (value)) << ECMA_DIRECT_SHIFT) | ECMA_DIRECT_TYPE_SIMPLE_VALUE) /** * Simple ecma values */ enum { /** * Empty value is implementation defined value, used for representing: * - empty (uninitialized) values * - immutable binding values * - special register or stack values for vm */ ECMA_VALUE_EMPTY = ECMA_MAKE_VALUE (0), /**< uninitialized value */ ECMA_VALUE_ERROR = ECMA_MAKE_VALUE (1), /**< an error is currently thrown */ ECMA_VALUE_FALSE = ECMA_MAKE_VALUE (2), /**< boolean false */ ECMA_VALUE_TRUE = ECMA_MAKE_VALUE (3), /**< boolean true */ ECMA_VALUE_UNDEFINED = ECMA_MAKE_VALUE (4), /**< undefined value */ ECMA_VALUE_NULL = ECMA_MAKE_VALUE (5), /**< null value */ ECMA_VALUE_UNINITIALIZED = ECMA_MAKE_VALUE (6), /**< a special value for uninitialized let/const declarations */ ECMA_VALUE_NOT_FOUND = ECMA_MAKE_VALUE (7), /**< a special value returned by * ecma_op_object_find */ /* Values for controlling the VM */ ECMA_VALUE_ARRAY_HOLE = ECMA_MAKE_VALUE (8), /**< array hole, used for * initialization of an array literal */ ECMA_VALUE_REGISTER_REF = ECMA_MAKE_VALUE (9), /**< register reference, * a special "base" value for vm */ ECMA_VALUE_RELEASE_LEX_ENV = ECMA_MAKE_VALUE (10), /**< if this error remains on the stack when an exception occurs the top lexical environment of the VM frame should be popped */ ECMA_VALUE_SPREAD_ELEMENT = ECMA_MAKE_VALUE (11), /**< a special value for spread elements in array initialization * or function call argument list */ /* Other values */ ECMA_VALUE_ARGUMENT_NO_TRACK = ECMA_MAKE_VALUE (12), /**< represents not-tracked arguments formal parameter */ ECMA_VALUE_SYNC_ITERATOR = ECMA_MAKE_VALUE (13), /**< option for ecma_op_get_iterator: sync iterator is requested */ ECMA_VALUE_ASYNC_ITERATOR = ECMA_MAKE_VALUE (14), /**< option for ecma_op_get_iterator: async iterator is requested */ #if JERRY_BUILTIN_GLOBAL_THIS ECMA_VALUE_GLOBAL_THIS = ECMA_MAKE_VALUE (15), /**< globalThis built-in */ #endif /* JERRY_BUILTIN_GLOBAL_THIS */ }; #if !JERRY_NUMBER_TYPE_FLOAT64 /** * Maximum integer number for an ecma value */ #define ECMA_INTEGER_NUMBER_MAX 0x7fffff /** * Maximum integer number for an ecma value (shifted left with ECMA_DIRECT_SHIFT) */ #define ECMA_INTEGER_NUMBER_MAX_SHIFTED 0x7fffff0 #else /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Maximum integer number for an ecma value */ #define ECMA_INTEGER_NUMBER_MAX 0x7ffffff /** * Maximum integer number for an ecma value (shifted left with ECMA_DIRECT_SHIFT) */ #define ECMA_INTEGER_NUMBER_MAX_SHIFTED 0x7ffffff0 #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ #if !JERRY_NUMBER_TYPE_FLOAT64 /** * Minimum integer number for an ecma value */ #define ECMA_INTEGER_NUMBER_MIN -0x7fffff /** * Minimum integer number for an ecma value (shifted left with ECMA_DIRECT_SHIFT) */ #define ECMA_INTEGER_NUMBER_MIN_SHIFTED -0x7fffff0 #else /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Minimum integer number for an ecma value */ #define ECMA_INTEGER_NUMBER_MIN -0x8000000 /** * Minimum integer number for an ecma value (shifted left with ECMA_DIRECT_SHIFT) */ #define ECMA_INTEGER_NUMBER_MIN_SHIFTED (-0x7fffffff - 1) /* -0x80000000 */ #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ #if ECMA_DIRECT_SHIFT != 4 #error "Please update ECMA_INTEGER_NUMBER_MIN/MAX_SHIFTED according to the new value of ECMA_DIRECT_SHIFT." #endif /* ECMA_DIRECT_SHIFT != 4 */ /** * Checks whether the integer number is in the integer number range. */ #define ECMA_IS_INTEGER_NUMBER(num) (ECMA_INTEGER_NUMBER_MIN <= (num) && (num) <= ECMA_INTEGER_NUMBER_MAX) /** * Maximum integer number, which if squared, still fits in ecma_integer_value_t */ #if !JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_INTEGER_MULTIPLY_MAX 0xb50 #else /* JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_INTEGER_MULTIPLY_MAX 0x2d41 #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Checks whether the error flag is set. */ #define ECMA_IS_VALUE_ERROR(value) (JERRY_UNLIKELY ((value) == ECMA_VALUE_ERROR)) /** * Callback which tells whether the ECMAScript execution should be stopped. */ typedef ecma_value_t (*ecma_vm_exec_stop_callback_t) (void *user_p); /** * Forward definition of jerry_call_info_t. */ struct jerry_call_info_t; /** * Type of an external function handler. */ typedef ecma_value_t (*ecma_native_handler_t) (const struct jerry_call_info_t *call_info_p, const ecma_value_t args_p[], const uint32_t args_count); /** * Representation of native pointer data. */ typedef struct { void *native_p; /**< points to the data of the object */ jerry_object_native_info_t *native_info_p; /**< native info */ } ecma_native_pointer_t; /** * Representation of native pointer data chain. */ typedef struct ecma_native_pointer_chain_t { ecma_native_pointer_t data; /**< pointer data */ struct ecma_native_pointer_chain_t *next_p; /**< next in the list */ } ecma_native_pointer_chain_t; /** * Representation for class constructor environment record. */ typedef struct { ecma_value_t this_binding; /**< this binding */ ecma_value_t function_object; /**< function object */ } ecma_environment_record_t; /** * Property list: * The property list of an object is a chain list of various items. * The type of each item is stored in the first byte of the item. * * The most common item is the property pair, which contains two * ecmascript properties. It is also important, that after the * first property pair, only property pair items are allowed. * * Example for other items is property name hash map, or array of items. */ /** * Property name listing options. */ typedef enum { ECMA_LIST_NO_OPTS = (0), /**< no options are provided */ ECMA_LIST_ARRAY_INDICES = (1 << 0), /**< exclude properties with names * that are not indices */ ECMA_LIST_ENUMERABLE = (1 << 1), /**< exclude non-enumerable properties */ ECMA_LIST_PROTOTYPE = (1 << 2), /**< list properties from prototype chain */ ECMA_LIST_SYMBOLS = (1 << 3), /**< list symbol properties */ ECMA_LIST_SYMBOLS_ONLY = (1 << 4), /**< list symbol properties only */ ECMA_LIST_CONVERT_FAST_ARRAYS = (1 << 5), /**< after listing the properties convert * the fast access mode array back to normal array */ } ecma_list_properties_options_t; /** * Enumerable property name listing options. */ typedef enum { ECMA_ENUMERABLE_PROPERTY_KEYS, /**< List only property names */ ECMA_ENUMERABLE_PROPERTY_VALUES, /**< List only property values */ ECMA_ENUMERABLE_PROPERTY_ENTRIES, /**< List both property names and values */ ECMA_ENUMERABLE_PROPERTY__COUNT /**< Number of enumerable property listing types */ } ecma_enumerable_property_names_options_t; /** * List enumerable properties and include the prototype chain. */ #define ECMA_LIST_ENUMERABLE_PROTOTYPE (ECMA_LIST_ENUMERABLE | ECMA_LIST_PROTOTYPE) /** * Property flag list. Several flags are alias */ typedef enum { ECMA_PROPERTY_FLAG_CONFIGURABLE = (1u << 0), /**< property is configurable */ ECMA_PROPERTY_FLAG_ENUMERABLE = (1u << 1), /**< property is enumerable */ ECMA_PROPERTY_FLAG_WRITABLE = (1u << 2), /**< property is writable */ ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL = (1u << 2), /**< only one external pointer is assigned to this object */ ECMA_PROPERTY_FLAG_DELETED = (1u << 3), /**< property is deleted */ ECMA_PROPERTY_FLAG_BUILT_IN = (1u << 3), /**< property is defined by the ECMAScript standard */ ECMA_FAST_ARRAY_FLAG = (1u << 3), /**< array is fast array */ ECMA_PROPERTY_FLAG_LCACHED = (1u << 4), /**< property is lcached */ ECMA_ARRAY_TEMPLATE_LITERAL = (1u << 4), /**< array is a template literal constructed by the parser */ ECMA_PROPERTY_FLAG_DATA = (1u << 5), /**< property contains data */ /* The last two bits contains an ECMA_DIRECT_STRING value. */ } ecma_property_flags_t; /** * Property flags configurable, enumerable, writable. */ #define ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE \ (ECMA_PROPERTY_FLAG_CONFIGURABLE | ECMA_PROPERTY_FLAG_ENUMERABLE | ECMA_PROPERTY_FLAG_WRITABLE) /** * Property flags configurable, enumerable. */ #define ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE (ECMA_PROPERTY_FLAG_CONFIGURABLE | ECMA_PROPERTY_FLAG_ENUMERABLE) /** * Property flags configurable, enumerable. */ #define ECMA_PROPERTY_CONFIGURABLE_WRITABLE (ECMA_PROPERTY_FLAG_CONFIGURABLE | ECMA_PROPERTY_FLAG_WRITABLE) /** * Property flag built-in. */ #define ECMA_PROPERTY_BUILT_IN_FIXED (ECMA_PROPERTY_FLAG_BUILT_IN) /** * Property flags enumerable, writable. */ #define ECMA_PROPERTY_ENUMERABLE_WRITABLE (ECMA_PROPERTY_FLAG_ENUMERABLE | ECMA_PROPERTY_FLAG_WRITABLE) /** * Property flags built-in, configurable. */ #define ECMA_PROPERTY_BUILT_IN_CONFIGURABLE (ECMA_PROPERTY_FLAG_BUILT_IN | ECMA_PROPERTY_FLAG_CONFIGURABLE) /** * Property flags built-in, configurable, enumerable. */ #define ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_ENUMERABLE \ (ECMA_PROPERTY_FLAG_BUILT_IN | ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE) /** * Property flags built-in, configurable, writable. */ #define ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_WRITABLE (ECMA_PROPERTY_FLAG_BUILT_IN | ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /** * Property flags built-in, writable. */ #define ECMA_PROPERTY_BUILT_IN_WRITABLE (ECMA_PROPERTY_FLAG_BUILT_IN | ECMA_PROPERTY_FLAG_WRITABLE) /** * Property flags built-in, configurable, enumerable, writable. */ #define ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_ENUMERABLE_WRITABLE \ (ECMA_PROPERTY_FLAG_BUILT_IN | ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) /** * No attributes can be changed for this property. */ #define ECMA_PROPERTY_FIXED 0 /** * Default flag of length property. */ #define ECMA_PROPERTY_FLAG_DEFAULT_LENGTH ECMA_PROPERTY_FLAG_CONFIGURABLE /** * Shift for property name part. */ #define ECMA_PROPERTY_NAME_TYPE_SHIFT 6 /** * Type of hash-map property. */ #define ECMA_PROPERTY_TYPE_HASHMAP (ECMA_DIRECT_STRING_SPECIAL << ECMA_PROPERTY_NAME_TYPE_SHIFT) /** * Type of deleted property. */ #define ECMA_PROPERTY_TYPE_DELETED \ (ECMA_PROPERTY_FLAG_DELETED | (ECMA_DIRECT_STRING_SPECIAL << ECMA_PROPERTY_NAME_TYPE_SHIFT)) /** * Type of property not found. */ #define ECMA_PROPERTY_TYPE_NOT_FOUND ECMA_PROPERTY_TYPE_HASHMAP /** * Type of property not found and no more searching in the proto chain. */ #define ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP ECMA_PROPERTY_TYPE_DELETED /** * Type of property not found and an exception is thrown. */ #define ECMA_PROPERTY_TYPE_NOT_FOUND_AND_THROW \ (ECMA_PROPERTY_FLAG_LCACHED | (ECMA_DIRECT_STRING_SPECIAL << ECMA_PROPERTY_NAME_TYPE_SHIFT)) /** * Checks whether a property is not found. */ #define ECMA_PROPERTY_IS_FOUND(property) \ (((property) & (ECMA_PROPERTY_FLAG_DATA | (ECMA_DIRECT_STRING_SPECIAL << ECMA_PROPERTY_NAME_TYPE_SHIFT))) \ != (ECMA_DIRECT_STRING_SPECIAL << ECMA_PROPERTY_NAME_TYPE_SHIFT)) /** * Abstract property representation. * * A property is a type_and_flags byte and an ecma_value_t value pair. * This pair is represented by a single pointer in JerryScript. Although * a packed struct would only consume sizeof(ecma_value_t)+1 memory * bytes, accessing such structure is inefficient from the CPU viewpoint * because the value is not naturally aligned. To improve performance, * two type bytes and values are packed together. The memory layout is * the following: * * [type 1, type 2, unused byte 1, unused byte 2][value 1][value 2] * * The unused two bytes are used to store a compressed pointer for the * next property pair. * * The advantage of this layout is that the value reference can be computed * from the property address. However, property pointers cannot be compressed * anymore. */ typedef uint8_t ecma_property_t; /**< ecma_property_types_t (3 bit) and ecma_property_flags_t */ /** * Number of items in a property pair. */ #define ECMA_PROPERTY_PAIR_ITEM_COUNT 2 /** * Property header for all items in a property list. */ typedef struct { ecma_property_t types[ECMA_PROPERTY_PAIR_ITEM_COUNT]; /**< two property type slot. The first represent * the type of this property (e.g. property pair) */ jmem_cpointer_t next_property_cp; /**< next cpointer */ } ecma_property_header_t; /** * Pair of pointers - to property's getter and setter */ typedef struct { jmem_cpointer_t getter_cp; /**< compressed pointer to getter object */ jmem_cpointer_t setter_cp; /**< compressed pointer to setter object */ } ecma_getter_setter_pointers_t; /** * Property data. */ typedef union { ecma_value_t value; /**< value of a property */ ecma_getter_setter_pointers_t getter_setter_pair; /**< getter setter pair */ } ecma_property_value_t; /** * Property pair. */ typedef struct { ecma_property_header_t header; /**< header of the property */ ecma_property_value_t values[ECMA_PROPERTY_PAIR_ITEM_COUNT]; /**< property value slots */ jmem_cpointer_t names_cp[ECMA_PROPERTY_PAIR_ITEM_COUNT]; /**< property name slots */ } ecma_property_pair_t; /** * Get property name type. */ #define ECMA_PROPERTY_GET_NAME_TYPE(property) ((property) >> ECMA_PROPERTY_NAME_TYPE_SHIFT) /** * Returns true if the property pointer is a property pair. */ #define ECMA_PROPERTY_IS_PROPERTY_PAIR(property_header_p) ((property_header_p)->types[0] != ECMA_PROPERTY_TYPE_HASHMAP) /** * Property value of all internal properties */ #define ECMA_PROPERTY_INTERNAL (ECMA_PROPERTY_FLAG_DATA | (ECMA_DIRECT_STRING_SPECIAL << ECMA_PROPERTY_NAME_TYPE_SHIFT)) /** * Checks whether a property is internal property */ #define ECMA_PROPERTY_IS_INTERNAL(property) ((property) >= ECMA_PROPERTY_INTERNAL) /** * Checks whether a property is raw data or accessor property */ #define ECMA_PROPERTY_IS_RAW(property) ((property) < (ECMA_DIRECT_STRING_SPECIAL << ECMA_PROPERTY_NAME_TYPE_SHIFT)) /** * Checks whether a property is raw data property (should only be used in assertions) */ #define ECMA_PROPERTY_IS_RAW_DATA(property) \ (((property) &ECMA_PROPERTY_FLAG_DATA) && (property) < ECMA_PROPERTY_INTERNAL) /** * Create internal property. */ #define ECMA_CREATE_INTERNAL_PROPERTY(object_p, name_p, property_p, property_value_p) \ do \ { \ (property_value_p) = ecma_create_named_data_property ((object_p), (name_p), 0, &(property_p)); \ JERRY_ASSERT (*(property_p) == ECMA_PROPERTY_INTERNAL); \ } while (0) /** * Property type of all virtual properties */ #define ECMA_PROPERTY_VIRTUAL ECMA_PROPERTY_INTERNAL /** * Checks whether a property is virtual property */ #define ECMA_PROPERTY_IS_VIRTUAL(property) ECMA_PROPERTY_IS_INTERNAL (property) /** * Returns true if the property is named property. */ #define ECMA_PROPERTY_IS_NAMED_PROPERTY(property) \ ((property) < ECMA_PROPERTY_TYPE_HASHMAP || (property) >= ECMA_PROPERTY_INTERNAL) /** * Add the offset part to a property for computing its property data pointer. */ #define ECMA_PROPERTY_VALUE_ADD_OFFSET(property_p) \ ((uintptr_t) ((((uint8_t *) (property_p)) + (sizeof (ecma_property_value_t) * 2 - 1)))) /** * Align the property for computing its property data pointer. */ #define ECMA_PROPERTY_VALUE_DATA_PTR(property_p) \ (ECMA_PROPERTY_VALUE_ADD_OFFSET (property_p) & ~(sizeof (ecma_property_value_t) - 1)) /** * Compute the property data pointer of a property. * The property must be part of a property pair. */ #define ECMA_PROPERTY_VALUE_PTR(property_p) ((ecma_property_value_t *) ECMA_PROPERTY_VALUE_DATA_PTR (property_p)) /** * Property reference. It contains the value pointer * for real, and the value itself for virtual properties. */ typedef union { ecma_property_value_t *value_p; /**< property value pointer for real properties */ ecma_value_t virtual_value; /**< property value for virtual properties */ } ecma_property_ref_t; /** * Extended property reference, which also contains the * property descriptor pointer for real properties. */ typedef struct { ecma_property_ref_t property_ref; /**< property reference */ ecma_property_t *property_p; /**< property descriptor pointer for real properties */ } ecma_extended_property_ref_t; /** * Option flags for ecma_op_object_get_property. */ typedef enum { ECMA_PROPERTY_GET_NO_OPTIONS = 0, /**< no option flags for ecma_op_object_get_property */ ECMA_PROPERTY_GET_VALUE = 1u << 0, /**< fill virtual_value field for virtual properties */ ECMA_PROPERTY_GET_EXT_REFERENCE = 1u << 1, /**< get extended reference to the property */ } ecma_property_get_option_bits_t; /** * Internal object types. */ typedef enum { ECMA_OBJECT_TYPE_GENERAL = 0, /**< all objects that are not belongs to the sub-types below */ ECMA_OBJECT_TYPE_BUILT_IN_GENERAL = 1, /**< built-in general object */ ECMA_OBJECT_TYPE_CLASS = 2, /**< Objects with class property */ ECMA_OBJECT_TYPE_BUILT_IN_CLASS = 3, /**< built-in object with class property */ ECMA_OBJECT_TYPE_ARRAY = 4, /**< Array object (15.4) */ ECMA_OBJECT_TYPE_BUILT_IN_ARRAY = 5, /**< Built-in array object */ ECMA_OBJECT_TYPE_PROXY = 6, /**< Proxy object ECMAScript v6 26.2 */ /* Note: these 4 types must be in this order. See IsCallable operation. */ ECMA_OBJECT_TYPE_FUNCTION = 7, /**< Function objects (15.3), created through 13.2 routine */ ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION = 8, /**< Native built-in function object */ ECMA_OBJECT_TYPE_BOUND_FUNCTION = 9, /**< Function objects (15.3), created through 15.3.4.5 routine */ ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION = 10, /**< implicit class constructor function */ ECMA_OBJECT_TYPE_NATIVE_FUNCTION = 11, /**< Native function object */ ECMA_OBJECT_TYPE__MAX /**< maximum value */ } ecma_object_type_t; /** * Base object types without built-in flag. * * Note: * only these types can be checked with ecma_get_object_base_type. */ typedef enum { ECMA_OBJECT_BASE_TYPE_GENERAL = ECMA_OBJECT_TYPE_GENERAL, /**< generic objects */ ECMA_OBJECT_BASE_TYPE_CLASS = ECMA_OBJECT_TYPE_CLASS, /**< Objects with class property */ ECMA_OBJECT_BASE_TYPE_ARRAY = ECMA_OBJECT_TYPE_ARRAY, /**< Array object (15.4) */ } ecma_object_base_type_t; /** * Types of objects with class property. * * Note: * when this type is changed, both ecma_class_object_magic_string_id * and jerry_class_object_type must be updated as well */ typedef enum { /* These objects require custom property resolving. */ ECMA_OBJECT_CLASS_STRING, /**< String Object (ECMAScript v5.1, 4.3.18) */ ECMA_OBJECT_CLASS_ARGUMENTS, /**< Arguments object (10.6) */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_OBJECT_CLASS_TYPEDARRAY, /**< TypedArray which does NOT need extra space to store length and offset */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM ECMA_OBJECT_CLASS_MODULE_NAMESPACE, /**< Module Namespace (ECMAScript v11, 9.4.6) */ #endif /* JERRY_MODULE_SYSTEM */ /* These objects are marked by Garbage Collector. */ ECMA_OBJECT_CLASS_GENERATOR, /**< Generator object (ECMAScript v6, 25.2) */ ECMA_OBJECT_CLASS_ASYNC_GENERATOR, /**< Async generator object (ECMAScript v11, 25.3) */ ECMA_OBJECT_CLASS_ARRAY_ITERATOR, /**< Array iterator object (ECMAScript v6, 22.1.5.1) */ ECMA_OBJECT_CLASS_SET_ITERATOR, /**< Set iterator object (ECMAScript v6, 23.2.5.1) */ ECMA_OBJECT_CLASS_MAP_ITERATOR, /**< Map iterator object (ECMAScript v6, 23.1.5.1) */ #if JERRY_BUILTIN_REGEXP ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR, /** RegExp string iterator object (ECMAScript v11, 21.2.7) */ #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_MODULE_SYSTEM ECMA_OBJECT_CLASS_MODULE, /**< Module (ECMAScript v6, 15.2) */ #endif /* JERRY_MODULE_SYSTEM */ ECMA_OBJECT_CLASS_PROMISE, /**< Promise (ECMAScript v6, 25.4) */ ECMA_OBJECT_CLASS_PROMISE_CAPABILITY, /**< Promise capability (ECMAScript v6, 25.4.1.1) */ ECMA_OBJECT_CLASS_ASYNC_FROM_SYNC_ITERATOR, /**< AsyncFromSyncIterator (ECMAScript v11, 25.1.4) */ #if JERRY_BUILTIN_DATAVIEW ECMA_OBJECT_CLASS_DATAVIEW, /**< DataView (ECMAScript v6, 24.2) */ #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_CONTAINER ECMA_OBJECT_CLASS_CONTAINER, /**< Container (Map, WeakMap, Set, WeakSet) */ #endif /* JERRY_BUILTIN_CONTAINER */ /* Normal objects. */ ECMA_OBJECT_CLASS_BOOLEAN, /**< Boolean Object (ECMAScript v5.1, 4.3.15) */ ECMA_OBJECT_CLASS_NUMBER, /**< Number Object (ECMAScript v5.1, 4.3.21) */ ECMA_OBJECT_CLASS_ERROR, /**< Error Object (ECMAScript v5.1, 15.11) */ ECMA_OBJECT_CLASS_INTERNAL_OBJECT, /**< object for internal properties */ #if JERRY_PARSER ECMA_OBJECT_CLASS_SCRIPT, /**< Compiled ECMAScript byte code */ #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_DATE ECMA_OBJECT_CLASS_DATE, /**< Date Object (ECMAScript v5.1, 15.9) */ #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_REGEXP ECMA_OBJECT_CLASS_REGEXP, /**< RegExp Object (ECMAScript v5.1, 15.10) */ #endif /* JERRY_BUILTIN_REGEXP */ ECMA_OBJECT_CLASS_SYMBOL, /**< Symbol object (ECMAScript v6, 4.3.27) */ ECMA_OBJECT_CLASS_STRING_ITERATOR, /**< String iterator object (ECMAScript v6, 22.1.5.1) */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_OBJECT_CLASS_ARRAY_BUFFER, /**< Array Buffer (ECMAScript v6, 24.1) */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER, /**< Shared Array Buffer (ECMAScript v11 24.2) */ #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ #if JERRY_BUILTIN_BIGINT ECMA_OBJECT_CLASS_BIGINT, /**< Bigint (ECMAScript v11, 4.3.27) */ #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_WEAKREF ECMA_OBJECT_CLASS_WEAKREF, /**< WeakRef (Not standardized yet) */ #endif /* JERRY_BUILTIN_WEAKREF */ ECMA_OBJECT_CLASS__MAX /**< maximum value */ } ecma_object_class_type_t; /** * Types of lexical environments. */ typedef enum { /* Types between 0 - 12 are ecma_object_type_t. */ ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE = 13, /**< declarative lexical environment */ ECMA_LEXICAL_ENVIRONMENT_CLASS = 14, /**< lexical environment with class */ ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND = 15, /**< object-bound lexical environment */ ECMA_LEXICAL_ENVIRONMENT_TYPE_START = ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE, /**< first lexical * environment type */ ECMA_LEXICAL_ENVIRONMENT_TYPE__MAX = ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND /**< maximum value */ } ecma_lexical_environment_type_t; /** * Types of array iterators. */ typedef enum { ECMA_ITERATOR_KEYS, /**< keys iterator */ ECMA_ITERATOR_VALUES, /**< values iterator */ ECMA_ITERATOR_ENTRIES, /**< entries iterator */ ECMA_ITERATOR__COUNT, /**< number of iterator kinds */ } ecma_iterator_kind_t; /** * Offset for JERRY_CONTEXT (status_flags) top 8 bits. */ #define ECMA_LOCAL_PARSE_OPTS_OFFSET ((sizeof (uint32_t) - sizeof (uint8_t)) * JERRY_BITSINBYTE) /** * Set JERRY_CONTEXT (status_flags) top 8 bits to the specified 'opts'. */ #define ECMA_SET_LOCAL_PARSE_OPTS(opts) \ do \ { \ JERRY_CONTEXT (status_flags) |= ((uint32_t) opts << ECMA_LOCAL_PARSE_OPTS_OFFSET) | ECMA_STATUS_DIRECT_EVAL; \ } while (0) /** * Get JERRY_CONTEXT (status_flags) top 8 bits. */ #define ECMA_GET_LOCAL_PARSE_OPTS() \ (JERRY_CONTEXT (status_flags) >> (ECMA_LOCAL_PARSE_OPTS_OFFSET - JERRY_LOG2 (ECMA_PARSE_ALLOW_SUPER))) /** * Clear JERRY_CONTEXT (status_flags) top 8 bits. */ #define ECMA_CLEAR_LOCAL_PARSE_OPTS() \ do \ { \ JERRY_CONTEXT (status_flags) &= ((1 << ECMA_LOCAL_PARSE_OPTS_OFFSET) - 1); \ } while (0) /** * Ecma object type mask for getting the object type. */ #define ECMA_OBJECT_TYPE_MASK 0x00fu /** * Extensible object. */ #define ECMA_OBJECT_FLAG_EXTENSIBLE 0x10 /** * Declarative lexical environments created for non-closure code blocks */ #define ECMA_OBJECT_FLAG_BLOCK ECMA_OBJECT_FLAG_EXTENSIBLE /** * Lexical environments with class has extra data */ #define ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA ECMA_OBJECT_FLAG_EXTENSIBLE /** * Bitshift index for an ecma-object reference count field */ #define ECMA_OBJECT_REF_SHIFT 5 /** * Value for increasing or decreasing the object reference counter. */ #define ECMA_OBJECT_REF_ONE (1u << ECMA_OBJECT_REF_SHIFT) /** * Type of the descriptor field of an object */ typedef uint16_t ecma_object_descriptor_t; /** * Bitmask for an ecma-object reference count field */ #define ECMA_OBJECT_REF_MASK ((ecma_object_descriptor_t) (~0u << ECMA_OBJECT_REF_SHIFT)) /** * Represents non-visited white object */ #define ECMA_OBJECT_NON_VISITED ECMA_OBJECT_REF_MASK /** * Maximum value of the object reference counter (1022 / 67108862). */ #define ECMA_OBJECT_MAX_REF (ECMA_OBJECT_NON_VISITED - ECMA_OBJECT_REF_ONE) /** * Description of ECMA-object or lexical environment * (depending on is_lexical_environment). */ typedef struct { /** type : 4 bit : ecma_object_type_t or ecma_lexical_environment_type_t depending on ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV flags : 2 bit : ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV, ECMA_OBJECT_FLAG_EXTENSIBLE or ECMA_OBJECT_FLAG_BLOCK refs : 10 / 26 bit (max 1022 / 67108862) */ ecma_object_descriptor_t type_flags_refs; /** next in the object chain maintained by the garbage collector */ jmem_cpointer_t gc_next_cp; /** compressed pointer to property list or bound object */ union { jmem_cpointer_t property_list_cp; /**< compressed pointer to object's * or declarative lexical environments's property list */ jmem_cpointer_t bound_object_cp; /**< compressed pointer to lexical environments's the bound object */ jmem_cpointer_t home_object_cp; /**< compressed pointer to lexical environments's the home object */ } u1; /** object prototype or outer reference */ union { jmem_cpointer_t prototype_cp; /**< compressed pointer to the object's prototype */ jmem_cpointer_t outer_reference_cp; /**< compressed pointer to the lexical environments's outer reference */ } u2; } ecma_object_t; /** * Description of built-in properties of an object. */ typedef struct { uint8_t id; /**< built-in id */ uint8_t routine_id; /**< routine id for built-in functions */ /** built-in specific field */ union { uint8_t length_and_bitset_size; /**< length and bit set size for generic built-ins */ uint8_t routine_index; /**< property descriptor index for built-in routines */ } u; /** extra built-in info */ union { uint8_t instantiated_bitset[1]; /**< instantiated property bit set for generic built-ins */ uint8_t routine_flags; /**< flags for built-in routines */ } u2; #if JERRY_BUILTIN_REALMS ecma_value_t realm_value; /**< realm value */ #else /* !JERRY_BUILTIN_REALMS */ uint32_t continue_instantiated_bitset[1]; /**< bit set for instantiated properties */ #endif /* JERRY_BUILTIN_REALMS */ } ecma_built_in_props_t; /** * Type of a built-in function handler. */ typedef ecma_value_t (*ecma_builtin_handler_t) (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); #if JERRY_BUILTIN_REALMS /** * Number of bits available in the instantiated bitset without allocation */ #define ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE (8) #else /* !JERRY_BUILTIN_REALMS */ /** * Number of bits available in the instantiated bitset without allocation */ #define ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE (8 + 32) #endif /* JERRY_BUILTIN_REALMS */ /** * Builtin routine function object status flags */ typedef enum { ECMA_BUILTIN_ROUTINE_NO_OPTS = 0, /**< No options are provided */ ECMA_BUILTIN_ROUTINE_LENGTH_INITIALIZED = (1u << 0), /**< 'length' property has been initialized */ ECMA_BUILTIN_ROUTINE_NAME_INITIALIZED = (1u << 1), /**< 'name' property has been initialized */ ECMA_BUILTIN_ROUTINE_GETTER = (1u << 2), /**< this routine is getter */ ECMA_BUILTIN_ROUTINE_SETTER = (1u << 3), /**< this routine is setter */ } ecma_builtin_routine_flags_t; /** * Start position of bit set size in length_and_bitset_size field. */ #define ECMA_BUILT_IN_BITSET_SHIFT 5 /** * Description of extended ECMA-object. * * The extended object is an object with extra fields. */ typedef struct { ecma_object_t object; /**< object header */ /** * Description of extra fields. These extra fields depend on the object type. */ union { ecma_built_in_props_t built_in; /**< built-in object part */ /** * Description of objects with class. * * Note: * class is a reserved word in c++, so cls is used instead */ struct { uint8_t type; /**< class type of the object */ /** * Description of 8 bit extra fields. These extra fields depend on the type. */ union { uint8_t arguments_flags; /**< arguments object flags */ uint8_t error_type; /**< jerry_error_t type of native error objects */ #if JERRY_BUILTIN_DATE uint8_t date_flags; /**< flags for date objects */ #endif /* JERRY_BUILTIN_DATE */ #if JERRY_MODULE_SYSTEM uint8_t module_state; /**< Module state */ #endif /* JERRY_MODULE_SYSTEM */ uint8_t iterator_kind; /**< type of iterator */ uint8_t regexp_string_iterator_flags; /**< flags for RegExp string iterator */ uint8_t promise_flags; /**< Promise object flags */ #if JERRY_BUILTIN_CONTAINER uint8_t container_flags; /**< container object flags */ #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_TYPEDARRAY uint8_t array_buffer_flags; /**< ArrayBuffer flags */ uint8_t typedarray_type; /**< type of typed array */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ } u1; /** * Description of 16 bit extra fields. These extra fields depend on the type. */ union { uint16_t formal_params_number; /**< for arguments: formal parameters number */ #if JERRY_MODULE_SYSTEM uint16_t module_flags; /**< Module flags */ #endif /* JERRY_MODULE_SYSTEM */ uint16_t iterator_index; /**< for %Iterator%: [[%Iterator%NextIndex]] property */ uint16_t executable_obj_flags; /**< executable object flags */ #if JERRY_BUILTIN_CONTAINER uint16_t container_id; /**< magic string id of a container */ #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_TYPEDARRAY uint16_t typedarray_flags; /**< typed array object flags */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ } u2; /** * Description of 32 bit / value. These extra fields depend on the type. */ union { ecma_value_t value; /**< value of the object (e.g. boolean, number, string, etc.) */ ecma_value_t target; /**< [[ProxyTarget]] or [[WeakRefTarget]] internal property */ #if JERRY_BUILTIN_TYPEDARRAY ecma_value_t arraybuffer; /**< for typedarray: ArrayBuffer reference */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ ecma_value_t head; /**< points to the async generator task queue head item */ ecma_value_t iterated_value; /**< for %Iterator%: [[IteratedObject]] property */ ecma_value_t promise; /**< PromiseCapability[[Promise]] internal slot */ ecma_value_t sync_iterator; /**< IteratorRecord [[Iterator]] internal slot for AsyncFromSyncIterator */ ecma_value_t spread_value; /**< for spread object: spread element */ int32_t tza; /**< TimeZone adjustment for date objects */ uint32_t length; /**< length related property (e.g. length of ArrayBuffer) */ uint32_t arguments_number; /**< for arguments: arguments number */ #if JERRY_MODULE_SYSTEM uint32_t dfs_ancestor_index; /**< module dfs ancestor index (ES2020 15.2.1.16) */ #endif /* JERRY_MODULE_SYSTEM */ } u3; } cls; /** * Description of function objects. */ struct { jmem_cpointer_tag_t scope_cp; /**< function scope */ ecma_value_t bytecode_cp; /**< function byte code */ } function; /** * Description of array objects. */ struct { uint32_t length; /**< length property value */ uint32_t length_prop_and_hole_count; /**< length property attributes and number of array holes in * a fast access mode array multiplied ECMA_FAST_ACCESS_HOLE_ONE */ } array; /** * Description of bound function object. */ struct { jmem_cpointer_tag_t target_function; /**< target function */ ecma_value_t args_len_or_this; /**< length of arguments or this value */ } bound_function; /** * Description of implicit class constructor function. */ struct { ecma_value_t script_value; /**< script value */ uint8_t flags; /**< constructor flags */ } constructor_function; } u; } ecma_extended_object_t; /** * Description of built-in extended ECMA-object. */ typedef struct { ecma_extended_object_t extended_object; /**< extended object part */ ecma_built_in_props_t built_in; /**< built-in object part */ } ecma_extended_built_in_object_t; /** * Type of lexical environment with class */ typedef enum { ECMA_LEX_ENV_CLASS_TYPE_MODULE, /**< module object reference */ ECMA_LEX_ENV_CLASS_TYPE_CLASS_ENV, /**< class constructor object reference */ } ecma_lexical_environment_class_type_t; /** * Description of lexical environment with class */ typedef struct { ecma_object_t lexical_env; /**< lexical environment header */ uint32_t type; /**< element of ecma_lexical_environment_class_type_t */ ecma_object_t *object_p; /**< object reference */ } ecma_lexical_environment_class_t; /** * Check whether the given lexical class environment is a module */ #define ECMA_LEX_ENV_CLASS_IS_MODULE(lex_env_p) \ (((lex_env_p)->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) \ && ((ecma_lexical_environment_class_t *) (lex_env_p))->type == ECMA_LEX_ENV_CLASS_TYPE_MODULE) /** * Description of native functions */ typedef struct { ecma_extended_object_t extended_object; /**< extended object part */ #if JERRY_BUILTIN_REALMS ecma_value_t realm_value; /**< realm value */ #endif /* JERRY_BUILTIN_REALMS */ ecma_native_handler_t native_handler_cb; /**< external function */ } ecma_native_function_t; /** * Alignment for the fast access mode array length. * The real length is aligned up for allocating the underlying buffer. */ #define ECMA_FAST_ARRAY_ALIGNMENT (8) /** * Align the length of the fast mode array to get the allocated size of the underlying buffer */ #define ECMA_FAST_ARRAY_ALIGN_LENGTH(length) \ (uint32_t) ((((length)) + ECMA_FAST_ARRAY_ALIGNMENT - 1) / ECMA_FAST_ARRAY_ALIGNMENT * ECMA_FAST_ARRAY_ALIGNMENT) /** * Compiled byte code data. */ typedef struct { uint16_t size; /**< real size >> JMEM_ALIGNMENT_LOG */ uint16_t refs; /**< reference counter for the byte code */ uint16_t status_flags; /**< various status flags: * CBC_IS_FUNCTION check tells whether the byte code * is function or regular expression. * If function, the other flags must be CBC_CODE_FLAGS... * If regexp, the other flags must be RE_FLAG... */ } ecma_compiled_code_t; /** * Description of bound function objects. */ typedef struct { ecma_extended_object_t header; /**< extended object header */ ecma_value_t target_length; /**< length of target function */ } ecma_bound_function_t; #if JERRY_SNAPSHOT_EXEC /** * Description of static function objects. */ typedef struct { ecma_extended_object_t header; /**< header part */ const ecma_compiled_code_t *bytecode_p; /**< real byte code pointer */ } ecma_static_function_t; #endif /* JERRY_SNAPSHOT_EXEC */ /** * Description of arrow function objects. */ typedef struct { ecma_extended_object_t header; /**< extended object header */ ecma_value_t this_binding; /**< value of 'this' binding */ ecma_value_t new_target; /**< value of new.target */ } ecma_arrow_function_t; #if JERRY_SNAPSHOT_EXEC /** * Description of static arrow function objects. */ typedef struct { ecma_arrow_function_t header; const ecma_compiled_code_t *bytecode_p; } ecma_static_arrow_function_t; #endif /* JERRY_SNAPSHOT_EXEC */ #if JERRY_BUILTIN_CONTAINER /** * Flags for container objects */ typedef enum { ECMA_CONTAINER_FLAGS_EMPTY = (0), /** empty flags */ ECMA_CONTAINER_FLAGS_WEAK = (1 << 0) /** container object is weak */ } ecma_container_flags_t; /** * Description of map collection. */ typedef struct { ecma_value_t key; /**< key value */ ecma_value_t value; /**< value of the key */ } ecma_container_pair_t; /** * Size of a single element (in ecma_value_t unit). */ #define ECMA_CONTAINER_VALUE_SIZE 1 /** * Size of a key - value pair (in ecma_value_t unit). */ #define ECMA_CONTAINER_PAIR_SIZE 2 /** * Size of the internal buffer. */ #define ECMA_CONTAINER_GET_SIZE(container_p) (container_p->buffer_p[0]) /** * Remove the size field of the internal buffer. */ #define ECMA_CONTAINER_SET_SIZE(container_p, size) (container_p->buffer_p[0] = (ecma_value_t) (size)) /** * Number of entries of the internal buffer. */ #define ECMA_CONTAINER_ENTRY_COUNT(collection_p) (collection_p->item_count - 1) /** * Pointer to the first entry of the internal buffer. */ #define ECMA_CONTAINER_START(collection_p) (collection_p->buffer_p + 1) #endif /* JERRY_BUILTIN_CONTAINER */ /** * Description of ECMA property descriptor * * See also: ECMA-262 v5, 8.10. * * Note: * If a component of descriptor is undefined then corresponding * field should contain it's default value. */ typedef struct { uint16_t flags; /**< any combination of jerry_property_descriptor_flags_t bits */ ecma_value_t value; /**< [[Value]] */ ecma_object_t *get_p; /**< [[Get]] */ ecma_object_t *set_p; /**< [[Set]] */ } ecma_property_descriptor_t; /** * Bitfield which represents a namedata property options in an ecma_property_descriptor_t * Attributes: * - is_get_defined, is_set_defined : false * - is_configurable, is_writable, is_enumerable : undefined (false) * - is_throw : undefined (false) * - is_value_defined : true * - is_configurable_defined, is_writable_defined, is_enumerable_defined : true */ #define ECMA_NAME_DATA_PROPERTY_DESCRIPTOR_BITS \ ((uint16_t) (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE_DEFINED \ | JERRY_PROP_IS_WRITABLE_DEFINED)) /** * Bitmask to get a the physical property flags from an ecma_property_descriptor */ #define ECMA_PROPERTY_FLAGS_MASK \ ((uint16_t) (JERRY_PROP_IS_CONFIGURABLE | JERRY_PROP_IS_ENUMERABLE | JERRY_PROP_IS_WRITABLE)) /** * Description of an ecma-number */ #if JERRY_NUMBER_TYPE_FLOAT64 typedef double ecma_number_t; #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ typedef float ecma_number_t; #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Convert double to an ecma-number. */ #define DOUBLE_TO_ECMA_NUMBER_T(value) ((ecma_number_t) (value)) /** * Value '0' of ecma_number_t */ #define ECMA_NUMBER_ZERO ((ecma_number_t) 0.0f) /** * Value '1' of ecma_number_t */ #define ECMA_NUMBER_ONE ((ecma_number_t) 1.0f) /** * Value '2' of ecma_number_t */ #define ECMA_NUMBER_TWO ((ecma_number_t) 2.0f) /** * Value '0.5' of ecma_number_t */ #define ECMA_NUMBER_HALF ((ecma_number_t) 0.5f) /** * Value '-1' of ecma_number_t */ #define ECMA_NUMBER_MINUS_ONE ((ecma_number_t) -1.0f) /** * Maximum number of characters in string representation of ecma-number */ #define ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER 64 /** * Maximum number of characters in string representation of ecma-uint32 */ #define ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32 10 /** * String is not a valid array index. */ #define ECMA_STRING_NOT_ARRAY_INDEX UINT32_MAX /** * Ecma-collection: a growable list of ecma-values. */ typedef struct { uint32_t item_count; /**< number of items in the collection */ uint32_t capacity; /**< number of items can be stored in the underlying buffer */ ecma_value_t *buffer_p; /**< underlying data buffer */ } ecma_collection_t; /** * Initial capacity of an ecma-collection */ #define ECMA_COLLECTION_INITIAL_CAPACITY 4 /** * Ecma-collection grow factor when the collection underlying buffer need to be reallocated */ #define ECMA_COLLECTION_GROW_FACTOR (ECMA_COLLECTION_INITIAL_CAPACITY * 2) /** * Compute the total allocated size of the collection based on it's capacity */ #define ECMA_COLLECTION_ALLOCATED_SIZE(capacity) (uint32_t) (capacity * sizeof (ecma_value_t)) /** * Initial allocated size of an ecma-collection */ #define ECMA_COLLECTION_INITIAL_SIZE ECMA_COLLECTION_ALLOCATED_SIZE (ECMA_COLLECTION_INITIAL_CAPACITY) /** * Size shift of a compact collection */ #define ECMA_COMPACT_COLLECTION_SIZE_SHIFT 3 /** * Get the size of the compact collection */ #define ECMA_COMPACT_COLLECTION_GET_SIZE(compact_collection_p) \ ((compact_collection_p)[0] >> ECMA_COMPACT_COLLECTION_SIZE_SHIFT) /** * Direct string types (2 bit). */ typedef enum { ECMA_DIRECT_STRING_PTR = 0, /**< string is a string pointer, only used by property names */ ECMA_DIRECT_STRING_MAGIC = 1, /**< string is a magic string */ ECMA_DIRECT_STRING_UINT = 2, /**< string is an unsigned int */ ECMA_DIRECT_STRING_SPECIAL = 3, /**< string is special */ } ecma_direct_string_type_t; /** * Maximum value of the immediate part of a direct magic string. * Must be compatible with the immediate property name. */ #define ECMA_DIRECT_STRING_MAX_IMM 0x0000ffff /** * Shift for direct string value part in ecma_value_t. */ #define ECMA_DIRECT_STRING_SHIFT (ECMA_VALUE_SHIFT + 2) /** * Full mask for direct strings. */ #define ECMA_DIRECT_STRING_MASK ((uintptr_t) (ECMA_DIRECT_TYPE_MASK | (0x3u << ECMA_VALUE_SHIFT))) /** * Create an ecma direct string. */ #define ECMA_CREATE_DIRECT_STRING(type, value) \ ((uintptr_t) (ECMA_TYPE_DIRECT_STRING | ((type) << ECMA_VALUE_SHIFT) | (value) << ECMA_DIRECT_STRING_SHIFT)) /** * Create an ecma direct string from the given number. * * Note: the given number must be less or equal than ECMA_DIRECT_STRING_MAX_IMM */ #define ECMA_CREATE_DIRECT_UINT32_STRING(uint32_number) \ ((ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_UINT, (uintptr_t) uint32_number)) /** * Checks whether the string is direct. */ #define ECMA_IS_DIRECT_STRING(string_p) ((((uintptr_t) (string_p)) & 0x1) != 0) /** * Checks whether the string is direct. */ #define ECMA_IS_DIRECT_STRING_WITH_TYPE(string_p, type) \ ((((uintptr_t) (string_p)) & ECMA_DIRECT_STRING_MASK) == ECMA_CREATE_DIRECT_STRING (type, 0)) /** * Returns the type of a direct string. */ #define ECMA_GET_DIRECT_STRING_TYPE(string_p) ((((uintptr_t) (string_p)) >> ECMA_VALUE_SHIFT) & 0x3) /** * Shift applied to type conversions. */ #define ECMA_STRING_TYPE_CONVERSION_SHIFT (ECMA_PROPERTY_NAME_TYPE_SHIFT - ECMA_VALUE_SHIFT) /** * Converts direct string type to property name type. */ #define ECMA_DIRECT_STRING_TYPE_TO_PROP_NAME_TYPE(string_p) \ ((((uintptr_t) (string_p)) & (0x3 << ECMA_VALUE_SHIFT)) << ECMA_STRING_TYPE_CONVERSION_SHIFT) /** * Returns the value of a direct string. */ #define ECMA_GET_DIRECT_STRING_VALUE(string_p) (((uintptr_t) (string_p)) >> ECMA_DIRECT_STRING_SHIFT) /** * Maximum number of bytes that a long-utf8-string is able to store */ #define ECMA_STRING_SIZE_LIMIT UINT32_MAX typedef enum { ECMA_STRING_CONTAINER_HEAP_UTF8_STRING, /**< actual data is on the heap as an utf-8 (cesu8) string * maximum size is 2^16. */ ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING, /**< the string is a long string or provided externally * and only its attributes are stored. */ ECMA_STRING_CONTAINER_UINT32_IN_DESC, /**< string representation of an uint32 number */ ECMA_STRING_CONTAINER_HEAP_ASCII_STRING, /**< actual data is on the heap as an ASCII string * maximum size is 2^16. */ ECMA_STRING_CONTAINER_MAGIC_STRING_EX, /**< the ecma-string is equal to one of external magic strings */ ECMA_STRING_CONTAINER_SYMBOL, /**< the ecma-string is a symbol */ ECMA_STRING_CONTAINER__MAX = ECMA_STRING_CONTAINER_SYMBOL /**< maximum value */ } ecma_string_container_t; /** * Mask for getting the container of a string. */ #define ECMA_STRING_CONTAINER_MASK 0x7u /** * Value for increasing or decreasing the reference counter. */ #define ECMA_STRING_REF_ONE (1u << 4) /** * Maximum value of the reference counter (4294967280). */ #define ECMA_STRING_MAX_REF (0xFFFFFFF0) /** * Flag that identifies that the string is static which means it is stored in JERRY_CONTEXT (string_list_cp) */ #define ECMA_STATIC_STRING_FLAG (1 << 3) /** * Set an ecma-string as static string */ #define ECMA_SET_STRING_AS_STATIC(string_p) (string_p)->refs_and_container |= ECMA_STATIC_STRING_FLAG /** * Checks whether the ecma-string is static string */ #define ECMA_STRING_IS_STATIC(string_p) ((string_p)->refs_and_container & ECMA_STATIC_STRING_FLAG) /** * Returns with the container type of a string. */ #define ECMA_STRING_GET_CONTAINER(string_desc_p) \ ((ecma_string_container_t) ((string_desc_p)->refs_and_container & ECMA_STRING_CONTAINER_MASK)) /** * Checks whether the reference counter is 1 of a string. */ #define ECMA_STRING_IS_REF_EQUALS_TO_ONE(string_desc_p) (((string_desc_p)->refs_and_container >> 4) == 1) /** * Checks whether the reference counter is 1 of an extended primitive. */ #define ECMA_EXTENDED_PRIMITIVE_IS_REF_EQUALS_TO_ONE(extended_primitive_p) \ (((extended_primitive_p)->refs_and_type >> 3) == 1) /** * ECMA string-value descriptor */ typedef struct { /** Reference counter for the string */ uint32_t refs_and_container; /** * Actual data or identifier of it's place in container (depending on 'container' field) */ union { lit_string_hash_t hash; /**< hash of the ASCII/UTF8 string */ uint32_t magic_string_ex_id; /**< identifier of an external magic string (lit_magic_string_ex_id_t) */ uint32_t uint32_number; /**< uint32-represented number placed locally in the descriptor */ } u; } ecma_string_t; /** * ECMA UTF8 string-value descriptor */ typedef struct { ecma_string_t header; /**< string header */ uint16_t size; /**< size of this utf-8 string in bytes */ uint16_t length; /**< length of this utf-8 string in characters */ } ecma_short_string_t; /** * Long or external CESU8 string-value descriptor */ typedef struct { ecma_string_t header; /**< string header */ const lit_utf8_byte_t *string_p; /**< string data */ lit_utf8_size_t size; /**< size of this external string in bytes */ lit_utf8_size_t length; /**< length of this external string in characters */ } ecma_long_string_t; /** * External UTF8 string-value descriptor */ typedef struct { ecma_long_string_t header; /**< long string header */ void *user_p; /**< user pointer passed to the callback when the string is freed */ } ecma_external_string_t; /** * Header size of an ecma ASCII string */ #define ECMA_ASCII_STRING_HEADER_SIZE ((lit_utf8_size_t) (sizeof (ecma_string_t) + sizeof (uint8_t))) /** * Get the size of an ecma ASCII string */ #define ECMA_ASCII_STRING_GET_SIZE(string_p) \ ((lit_utf8_size_t) * ((lit_utf8_byte_t *) (string_p) + sizeof (ecma_string_t)) + 1) /** * Set the size of an ecma ASCII string */ #define ECMA_ASCII_STRING_SET_SIZE(string_p, size) \ (*((lit_utf8_byte_t *) (string_p) + sizeof (ecma_string_t)) = (uint8_t) ((size) -1)) /** * Get the start position of the string buffer of an ecma ASCII string */ #define ECMA_ASCII_STRING_GET_BUFFER(string_p) ((lit_utf8_byte_t *) (string_p) + ECMA_ASCII_STRING_HEADER_SIZE) /** * Get the start position of the string buffer of an ecma UTF8 string */ #define ECMA_SHORT_STRING_GET_BUFFER(string_p) ((lit_utf8_byte_t *) (string_p) + sizeof (ecma_short_string_t)) /** * Get the start position of the string buffer of an ecma long CESU8 string */ #define ECMA_LONG_STRING_BUFFER_START(string_p) ((lit_utf8_byte_t *) (string_p) + sizeof (ecma_long_string_t)) /** * ECMA extended string-value descriptor */ typedef struct { ecma_string_t header; /**< string header */ union { ecma_value_t symbol_descriptor; /**< symbol descriptor string-value */ ecma_value_t value; /**< original key value corresponds to the map key string */ } u; } ecma_extended_string_t; /** * Required number of ecma values in a compact collection to represent PrivateElement */ #define ECMA_PRIVATE_ELEMENT_LIST_SIZE 3 /** * String builder header */ typedef struct { lit_utf8_size_t current_size; /**< size of the data in the buffer */ } ecma_stringbuilder_header_t; /** * Get pointer to the beginning of the stored string in the string builder */ #define ECMA_STRINGBUILDER_STRING_PTR(header_p) \ ((lit_utf8_byte_t *) (((lit_utf8_byte_t *) header_p) + ECMA_ASCII_STRING_HEADER_SIZE)) /** * Get the size of the stored string in the string builder */ #define ECMA_STRINGBUILDER_STRING_SIZE(header_p) \ ((lit_utf8_size_t) (header_p->current_size - ECMA_ASCII_STRING_HEADER_SIZE)) /** * String builder handle */ typedef struct { ecma_stringbuilder_header_t *header_p; /**< pointer to header */ } ecma_stringbuilder_t; #ifndef JERRY_BUILTIN_BIGINT /** * BigInt type. */ #define ECMA_EXTENDED_PRIMITIVE_BIGINT 0 #endif /* !defined (JERRY_BUILTIN_BIGINT) */ /** * Flags for exception values. */ typedef enum { ECMA_ERROR_API_FLAG_NONE = 0, ECMA_ERROR_API_FLAG_ABORT = (1u << 0), /**< abort flag */ ECMA_ERROR_API_FLAG_THROW_CAPTURED = (1u << 1), /**< throw captured flag */ } ecma_error_api_flags_t; /** * Representation of a thrown value on API level. */ typedef struct { uint32_t refs_and_type; /**< reference counter and type */ union { ecma_value_t value; /**< referenced value */ uint32_t bigint_sign_and_size; /**< BigInt properties */ } u; } ecma_extended_primitive_t; /** * Value for increasing or decreasing the reference counter. */ #define ECMA_EXTENDED_PRIMITIVE_REF_ONE (1u << 3) /** * Maximum value of the reference counter. */ #define ECMA_EXTENDED_PRIMITIVE_MAX_REF (UINT32_MAX - (ECMA_EXTENDED_PRIMITIVE_REF_ONE - 1)) #if JERRY_PROPERTY_HASHMAP /** * The lowest state of the ecma_prop_hashmap_alloc_state counter. * If ecma_prop_hashmap_alloc_state other than this value, it is * disabled. */ #define ECMA_PROP_HASHMAP_ALLOC_ON 0 /** * The highest state of the ecma_prop_hashmap_alloc_state counter. */ #define ECMA_PROP_HASHMAP_ALLOC_MAX 4 #endif /* JERRY_PROPERTY_HASHMAP */ /** * Number of values in a literal storage item */ #define ECMA_LIT_STORAGE_VALUE_COUNT 3 /** * Literal storage item */ typedef struct { jmem_cpointer_t next_cp; /**< cpointer ot next item */ jmem_cpointer_t values[ECMA_LIT_STORAGE_VALUE_COUNT]; /**< list of values */ } ecma_lit_storage_item_t; #if JERRY_LCACHE /** * Container of an LCache entry identifier */ typedef uint32_t ecma_lcache_hash_entry_id_t; /** * Entry of LCache hash table */ typedef struct { /** Pointer to a property of the object */ ecma_property_t *prop_p; /** Entry identifier in LCache */ ecma_lcache_hash_entry_id_t id; } ecma_lcache_hash_entry_t; /** * Number of rows in LCache's hash table */ #define ECMA_LCACHE_HASH_ROWS_COUNT 128 /** * Number of entries in a row of LCache's hash table */ #define ECMA_LCACHE_HASH_ROW_LENGTH 2 #endif /* JERRY_LCACHE */ #if JERRY_BUILTIN_TYPEDARRAY /** * Function callback descriptor of a %TypedArray% object getter */ typedef ecma_value_t (*ecma_typedarray_getter_fn_t) (lit_utf8_byte_t *src); /** * Function callback descriptor of a %TypedArray% object setter */ typedef ecma_value_t (*ecma_typedarray_setter_fn_t) (lit_utf8_byte_t *src, ecma_value_t value); /** * Builtin id for the different types of TypedArray's */ typedef enum { ECMA_INT8_ARRAY, /**< Int8Array */ ECMA_UINT8_ARRAY, /**< Uint8Array */ ECMA_UINT8_CLAMPED_ARRAY, /**< Uint8ClampedArray */ ECMA_INT16_ARRAY, /**< Int16Array */ ECMA_UINT16_ARRAY, /**< Uint16Array */ ECMA_INT32_ARRAY, /**< Int32Array */ ECMA_UINT32_ARRAY, /**< Uint32Array */ ECMA_FLOAT32_ARRAY, /**< Float32Array */ ECMA_FLOAT64_ARRAY, /**< Float64Array */ /* ECMA_TYPEDARRAY_IS_BIGINT_TYPE macro should be updated when new types are added */ ECMA_BIGINT64_ARRAY, /**< BigInt64Array */ ECMA_BIGUINT64_ARRAY, /**< BigUInt64Array */ } ecma_typedarray_type_t; /** * TypedArray flags. */ typedef enum { ECMA_TYPEDARRAY_IS_EXTENDED = (1u << 0), /* an ecma_extended_typedarray_object_t is allocated for the TypedArray */ } ecma_typedarray_flag_t; /** * Array buffer flags. */ typedef enum { ECMA_ARRAYBUFFER_HAS_POINTER = (1u << 0), /* ArrayBuffer has a buffer pointer. */ ECMA_ARRAYBUFFER_ALLOCATED = (1u << 1), /* ArrayBuffer memory is allocated */ ECMA_ARRAYBUFFER_DETACHED = (1u << 2), /* ArrayBuffer has been detached */ } ecma_arraybuffer_flag_t; /** * Structure for array buffers with a backing store pointer. */ typedef struct { ecma_extended_object_t extended_object; /**< extended object part */ void *buffer_p; /**< pointer to the backing store of the array buffer object */ void *arraybuffer_user_p; /**< user pointer passed to the free callback */ } ecma_arraybuffer_pointer_t; /** * Some internal properties of TypedArray object. * It is only used when the offset is not 0, and * the array-length is not buffer-length / element_size. */ typedef struct { ecma_extended_object_t extended_object; /**< extended object part */ uint32_t byte_offset; /**< the byteoffset of the above arraybuffer */ uint32_t array_length; /**< the array length */ } ecma_extended_typedarray_object_t; /** * General structure for query %TypedArray% object's properties. **/ typedef struct { ecma_object_t *array_buffer_p; /**< pointer to the typedArray's [[ViewedArrayBuffer]] internal slot */ ecma_typedarray_type_t id; /**< [[TypedArrayName]] internal slot */ uint32_t length; /**< [[ByteLength]] internal slot */ uint32_t offset; /**< [[ByteOffset]] internal slot. */ uint8_t shift; /**< the element size shift in the typedArray */ uint8_t element_size; /**< element size based on [[TypedArrayName]] in Table 49 */ } ecma_typedarray_info_t; #if JERRY_BUILTIN_BIGINT /** * Checks whether a given typedarray is BigInt type or not. **/ #define ECMA_TYPEDARRAY_IS_BIGINT_TYPE(id) ((id) >= ECMA_BIGINT64_ARRAY) #endif /* JERRY_BUILTIN_BIGINT */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ /** * Executable (e.g. generator, async) object flags. */ typedef enum { ECMA_EXECUTABLE_OBJECT_COMPLETED = (1u << 0), /**< executable object is completed and cannot be resumed */ ECMA_EXECUTABLE_OBJECT_RUNNING = (1u << 1), /**< executable object is currently running */ /* Generator specific flags. */ ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD = (1u << 2), /**< the executable object performs * an await or a yield* operation */ ECMA_ASYNC_GENERATOR_CALLED = (1u << 3), /**< the async generator was executed before */ /* This must be the last generator specific flag. */ ECMA_AWAIT_STATE_SHIFT = 4, /**< shift for await states */ } ecma_executable_object_flags_t; /** * Async function states after an await is completed. */ typedef enum { ECMA_AWAIT_YIELD_NEXT, /**< wait for an iterator result object */ ECMA_AWAIT_YIELD_NEXT_RETURN, /**< wait for an iterator result object after a return operation */ ECMA_AWAIT_YIELD_RETURN, /**< wait for the argument passed to return operation */ ECMA_AWAIT_YIELD_NEXT_VALUE, /**< wait for the value property of an iterator result object */ ECMA_AWAIT_YIELD_OPERATION, /**< wait for the generator operation (next/throw/return) */ ECMA_AWAIT_YIELD_CLOSE, /**< wait for the result of iterator close operation */ /* After adding new ECMA_AWAIT_YIELD items, the ECMA_AWAIT_YIELD_END should be updated. */ ECMA_AWAIT_FOR_CLOSE, /**< wait for a close iterator result object of for-await-of statement */ ECMA_AWAIT_FOR_NEXT, /**< wait for an iterator result object of for-await-of statement */ } ecma_await_states_t; /** * Checks whether the executable object is waiting for resuming. */ #define ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED(executable_object_p) \ (!((executable_object_p)->extended_object.u.cls.u2.executable_obj_flags \ & (ECMA_EXECUTABLE_OBJECT_COMPLETED | ECMA_EXECUTABLE_OBJECT_RUNNING))) /** * Last item of yield* related await states. */ #define ECMA_AWAIT_YIELD_END ECMA_AWAIT_YIELD_CLOSE /** * Helper macro for ECMA_EXECUTABLE_OBJECT_RESUME_EXEC. */ #define ECMA_EXECUTABLE_OBJECT_RESUME_EXEC_MASK ((uint16_t) ~ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) /** * Resume execution of the byte code. */ #define ECMA_EXECUTABLE_OBJECT_RESUME_EXEC(executable_object_p) \ ((executable_object_p)->extended_object.u.cls.u2.executable_obj_flags &= ECMA_EXECUTABLE_OBJECT_RESUME_EXEC_MASK) /** * Enqueued task of an AsyncGenerator. * * An execution of a task has three steps: * 1) Perform a next/throw/return operation * 2) Resume the execution of the AsyncGenerator * 3) Fulfill or reject a promise if the AsyncGenerator yielded a value * (these Promises are created by the AsyncGenerator itself) */ typedef struct { ecma_value_t next; /**< points to the next task which will be performed after this task is completed */ ecma_value_t promise; /**< promise which will be fulfilled or rejected after this task is completed */ ecma_value_t operation_value; /**< value argument of the operation */ uint8_t operation_type; /**< type of operation (see ecma_async_generator_operation_type_t) */ } ecma_async_generator_task_t; /** * Definition of PromiseCapability Records */ typedef struct { ecma_extended_object_t header; /**< object header, and [[Promise]] internal slot */ ecma_value_t resolve; /**< [[Resolve]] internal slot */ ecma_value_t reject; /**< [[Reject]] internal slot */ } ecma_promise_capability_t; /** * Definition of GetCapabilitiesExecutor Functions */ typedef struct { ecma_extended_object_t header; /**< object header */ ecma_value_t capability; /**< [[Capability]] internal slot */ } ecma_promise_capability_executor_t; /** * Definition of Promise.all Resolve Element Functions */ typedef struct { ecma_extended_object_t header; /**< object header */ ecma_value_t remaining_elements; /**< [[Remaining elements]] internal slot */ ecma_value_t capability; /**< [[Capabilities]] internal slot */ ecma_value_t values; /**< [[Values]] or [[Errors]] internal slot */ uint32_t index; /**< [[Index]] and [[AlreadyCalled]] internal slot * 0 - if the element has been resolved * real index + 1 in the [[Values]] list - otherwise */ } ecma_promise_all_executor_t; /** * Promise prototype methods helper. */ typedef enum { ECMA_PROMISE_ALL_RESOLVE, /**< promise.all resolve */ ECMA_PROMISE_ALLSETTLED_RESOLVE, /**< promise.allSettled resolve */ ECMA_PROMISE_ALLSETTLED_REJECT, /**< promise.allSettled reject */ ECMA_PROMISE_ANY_REJECT, /**< promise.any reject */ } ecma_promise_executor_type_t; #if JERRY_BUILTIN_DATAVIEW /** * Description of DataView objects. */ typedef struct { ecma_extended_object_t header; /**< header part */ ecma_object_t *buffer_p; /**< [[ViewedArrayBuffer]] internal slot */ uint32_t byte_offset; /**< [[ByteOffset]] internal slot */ } ecma_dataview_object_t; #endif /* JERRY_BUILTIN_DATAVIEW */ typedef enum { ECMA_SYMBOL_FLAG_NONE = 0, /**< no options */ ECMA_SYMBOL_FLAG_GLOBAL = (1 << 0), /**< symbol is a well known symbol, See also: 6.1.5.1 */ ECMA_SYMBOL_FLAG_PRIVATE_KEY = (1 << 1), /**< symbol is a private field */ ECMA_SYMBOL_FLAG_PRIVATE_INSTANCE_METHOD = (1 << 2), /**< symbol is a private method or accessor */ } ecma_symbol_flags_t; /** * Bitshift index for the symbol hash property */ #define ECMA_SYMBOL_FLAGS_SHIFT 3 /** * Bitmask for symbol hash flags */ #define ECMA_SYMBOL_FLAGS_MASK ((1 << ECMA_SYMBOL_FLAGS_SHIFT) - 1) #if (JERRY_STACK_LIMIT != 0) /** * Check the current stack usage. If the limit is reached a RangeError is raised. * The macro argument specifies the return value which is usually ECMA_VALUE_ERROR or NULL. */ #define ECMA_CHECK_STACK_USAGE_RETURN(RETURN_VALUE) \ do \ { \ if (ecma_get_current_stack_usage () > CONFIG_MEM_STACK_LIMIT) \ { \ ecma_raise_maximum_callstack_error (); \ return RETURN_VALUE; \ } \ } while (0) /** * Specialized version of ECMA_CHECK_STACK_USAGE_RETURN which returns ECMA_VALUE_ERROR. * This version should be used in most cases. */ #define ECMA_CHECK_STACK_USAGE() ECMA_CHECK_STACK_USAGE_RETURN (ECMA_VALUE_ERROR) #else /* JERRY_STACK_LIMIT == 0) */ /** * If the stack limit is unlimited, this check is an empty macro. */ #define ECMA_CHECK_STACK_USAGE_RETURN(RETURN_VALUE) /** * If the stack limit is unlimited, this check is an empty macro. */ #define ECMA_CHECK_STACK_USAGE() #endif /* (JERRY_STACK_LIMIT != 0) */ /** * Invalid object pointer which represents abrupt completion */ #define ECMA_OBJECT_POINTER_ERROR ((ecma_object_t *) 0x01) /** * Invalid property pointer which represents abrupt completion */ #define ECMA_PROPERTY_POINTER_ERROR ((ecma_property_t *) 0x01) #if JERRY_BUILTIN_PROXY /** * Proxy object flags. */ typedef enum { ECMA_PROXY_SKIP_RESULT_VALIDATION = (1u << 0), /**< skip result validation for [[GetPrototypeOf]], * [[SetPrototypeOf]], [[IsExtensible]], * [[PreventExtensions]], [[GetOwnProperty]], * [[DefineOwnProperty]], [[HasProperty]], [[Get]], * [[Set]], [[Delete]] and [[OwnPropertyKeys]] */ ECMA_PROXY_IS_CALLABLE = (1u << 1), /**< proxy is callable */ ECMA_PROXY_IS_CONSTRUCTABLE = (1u << 2), /**< proxy is constructable */ } ecma_proxy_flag_types_t; /** * Description of Proxy objects. * * A Proxy object's property list is used to store extra information: * * The "header.u2.prototype_cp" 1st tag bit stores the IsCallable information. * * The "header.u2.prototype_cp" 2nd tag bit stores the IsConstructor information. */ typedef struct { ecma_object_t header; /**< header part */ ecma_value_t target; /**< [[ProxyTarget]] internal slot */ ecma_value_t handler; /**< [[ProxyHandler]] internal slot */ } ecma_proxy_object_t; /** * Description of Proxy objects. */ typedef struct { ecma_extended_object_t header; /**< header part */ ecma_value_t proxy; /**< [[RevocableProxy]] internal slot */ } ecma_revocable_proxy_object_t; #endif /* JERRY_BUILTIN_PROXY */ /** * Type to represent the maximum property index * * For ES6+ the maximum valid property index is 2**53 - 1 */ typedef uint64_t ecma_length_t; #if JERRY_BUILTIN_BIGINT /** * BigUInt data is a sequence of uint32_t numbers. */ typedef uint32_t ecma_bigint_digit_t; /** * Special BigInt value representing zero. */ #define ECMA_BIGINT_ZERO ((ecma_value_t) ECMA_TYPE_BIGINT) /** * Special BigInt value representing zero when the result is pointer. */ #define ECMA_BIGINT_POINTER_TO_ZERO ((ecma_extended_primitive_t *) 0x1) /** * Return the size of a BigInt value in ecma_bigint_data_t units. */ #define ECMA_BIGINT_GET_SIZE(value_p) \ ((value_p)->u.bigint_sign_and_size & ~(uint32_t) (sizeof (ecma_bigint_digit_t) - 1)) /** * Size of memory needs to be allocated for the digits of a BigInt. * The value is rounded up for two digits. */ #define ECMA_BIGINT_GET_BYTE_SIZE(size) \ (size_t) (((size) + sizeof (ecma_bigint_digit_t)) & ~(2 * sizeof (ecma_bigint_digit_t) - 1)) #endif /* JERRY_BUILTIN_BIGINT */ /** * Struct for counting the different types properties in objects */ typedef struct { uint32_t array_index_named_props; /**< number of array index named properties */ uint32_t string_named_props; /**< number of string named properties */ uint32_t symbol_named_props; /**< number of symbol named properties */ } ecma_property_counter_t; /** * Arguments object related status flags */ typedef enum { ECMA_ARGUMENTS_OBJECT_NO_FLAGS = 0, /* unmapped arguments object */ ECMA_ARGUMENTS_OBJECT_MAPPED = (1 << 0), /* mapped arguments object */ ECMA_ARGUMENTS_OBJECT_STATIC_BYTECODE = (1 << 1), /* static mapped arguments object */ ECMA_ARGUMENTS_OBJECT_CALLEE_INITIALIZED = (1 << 2), /* 'callee' property has been lazy initialized */ ECMA_ARGUMENTS_OBJECT_LENGTH_INITIALIZED = (1 << 3), /* 'length' property has been lazy initialized */ ECMA_ARGUMENTS_OBJECT_ITERATOR_INITIALIZED = (1 << 4), /* 'Symbol.iterator' property has been lazy initialized */ } ecma_arguments_object_flags_t; /** * Definition of unmapped arguments object */ typedef struct { ecma_extended_object_t header; /**< object header */ ecma_value_t callee; /**< 'callee' property */ } ecma_unmapped_arguments_t; /** * Definition of mapped arguments object */ typedef struct { ecma_unmapped_arguments_t unmapped; /**< unmapped arguments object header */ ecma_value_t lex_env; /**< environment reference */ union { ecma_value_t byte_code; /**< callee's compiled code */ #if JERRY_SNAPSHOT_EXEC ecma_compiled_code_t *byte_code_p; /**< real byte code pointer */ #endif /* JERRY_SNAPSHOT_EXEC */ } u; } ecma_mapped_arguments_t; /** * Date object descriptor flags */ typedef enum { ECMA_DATE_TZA_NONE = 0, /**< no time-zone adjustment is set */ ECMA_DATE_TZA_SET = (1 << 0), /**< time-zone adjustment is set */ } ecma_date_object_flags_t; /** * Definition of date object */ typedef struct { ecma_extended_object_t header; /**< object header */ ecma_number_t date_value; /**< [[DateValue]] internal property */ } ecma_date_object_t; /** * Implicit class constructor flags */ typedef enum { ECMA_CONSTRUCTOR_FUNCTION_HAS_HERITAGE = (1 << 0), /**< heritage object is present */ } ecma_constructor_function_flags_t; /** * Description of AsyncFromSyncIterator objects. */ typedef struct { ecma_extended_object_t header; /**< header part */ ecma_value_t sync_next_method; /**< IteratorRecord [[NextMethod]] internal slot */ } ecma_async_from_sync_iterator_object_t; /** * Private method kind */ typedef enum { ECMA_PRIVATE_FIELD = 0, /**< private field */ ECMA_PRIVATE_METHOD, /**< private method */ ECMA_PRIVATE_GETTER, /**< private setter */ ECMA_PRIVATE_SETTER, /**< private getter */ } ecma_private_property_kind_t; /** * Private static property flag */ #define ECMA_PRIVATE_PROPERTY_STATIC_FLAG (1 << 2) /* * Get private property kind from a descriptor */ #define ECMA_PRIVATE_PROPERTY_KIND(prop) ((ecma_private_property_kind_t) ((int) (prop) & (ECMA_PRIVATE_PROPERTY_STATIC_FLAG - 1))) /** * @} * @} */ #endif /* !ECMA_GLOBALS_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/yuv.cpp000664 001750 001750 00000013635 15164251010 034517 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // YUV->RGB conversion functions // // Author: Skal (pascal.massimino@gmail.com) #include "./yuv.h" #if defined(WEBP_YUV_USE_TABLE) static int done = 0; static WEBP_INLINE uint8_t clip(int v, int max_value) { return v < 0 ? 0 : v > max_value ? max_value : v; } int16_t VP8kVToR[256], VP8kUToB[256]; int32_t VP8kVToG[256], VP8kUToG[256]; uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN]; uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN]; WEBP_TSAN_IGNORE_FUNCTION void VP8YUVInit(void) { int i; if (done) { return; } #ifndef USE_YUVj for (i = 0; i < 256; ++i) { VP8kVToR[i] = (89858 * (i - 128) + YUV_HALF) >> YUV_FIX; VP8kUToG[i] = -22014 * (i - 128) + YUV_HALF; VP8kVToG[i] = -45773 * (i - 128); VP8kUToB[i] = (113618 * (i - 128) + YUV_HALF) >> YUV_FIX; } for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) { const int k = ((i - 16) * 76283 + YUV_HALF) >> YUV_FIX; VP8kClip[i - YUV_RANGE_MIN] = clip(k, 255); VP8kClip4Bits[i - YUV_RANGE_MIN] = clip((k + 8) >> 4, 15); } #else for (i = 0; i < 256; ++i) { VP8kVToR[i] = (91881 * (i - 128) + YUV_HALF) >> YUV_FIX; VP8kUToG[i] = -22554 * (i - 128) + YUV_HALF; VP8kVToG[i] = -46802 * (i - 128); VP8kUToB[i] = (116130 * (i - 128) + YUV_HALF) >> YUV_FIX; } for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) { const int k = i; VP8kClip[i - YUV_RANGE_MIN] = clip(k, 255); VP8kClip4Bits[i - YUV_RANGE_MIN] = clip((k + 8) >> 4, 15); } #endif done = 1; } #else WEBP_TSAN_IGNORE_FUNCTION void VP8YUVInit(void) {} #endif // WEBP_YUV_USE_TABLE //----------------------------------------------------------------------------- // Plain-C version #define ROW_FUNC(FUNC_NAME, FUNC, XSTEP) \ static void FUNC_NAME(const uint8_t* y, \ const uint8_t* u, const uint8_t* v, \ uint8_t* dst, int len) { \ const uint8_t* const end = dst + (len & ~1) * XSTEP; \ while (dst != end) { \ FUNC(y[0], u[0], v[0], dst); \ FUNC(y[1], u[0], v[0], dst + XSTEP); \ y += 2; \ ++u; \ ++v; \ dst += 2 * XSTEP; \ } \ if (len & 1) { \ FUNC(y[0], u[0], v[0], dst); \ } \ } \ // All variants implemented. ROW_FUNC(YuvToRgbRow, VP8YuvToRgb, 3) ROW_FUNC(YuvToBgrRow, VP8YuvToBgr, 3) ROW_FUNC(YuvToRgbaRow, VP8YuvToRgba, 4) ROW_FUNC(YuvToBgraRow, VP8YuvToBgra, 4) ROW_FUNC(YuvToArgbRow, VP8YuvToArgb, 4) ROW_FUNC(YuvToRgba4444Row, VP8YuvToRgba4444, 2) ROW_FUNC(YuvToRgb565Row, VP8YuvToRgb565, 2) #undef ROW_FUNC // Main call for processing a plane with a WebPSamplerRowFunc function: void WebPSamplerProcessPlane(const uint8_t* y, int y_stride, const uint8_t* u, const uint8_t* v, int uv_stride, uint8_t* dst, int dst_stride, int width, int height, WebPSamplerRowFunc func) { int j; for (j = 0; j < height; ++j) { func(y, u, v, dst, width); y += y_stride; if (j & 1) { u += uv_stride; v += uv_stride; } dst += dst_stride; } } //----------------------------------------------------------------------------- // Main call WebPSamplerRowFunc WebPSamplers[MODE_LAST]; extern void WebPInitSamplersSSE2(void); extern void WebPInitSamplersMIPS32(void); extern void WebPInitSamplersMIPSdspR2(void); static volatile VP8CPUInfo yuv_last_cpuinfo_used = (VP8CPUInfo)&yuv_last_cpuinfo_used; WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplers(void) { if (yuv_last_cpuinfo_used == VP8GetCPUInfo) return; WebPSamplers[MODE_RGB] = YuvToRgbRow; WebPSamplers[MODE_RGBA] = YuvToRgbaRow; WebPSamplers[MODE_BGR] = YuvToBgrRow; WebPSamplers[MODE_BGRA] = YuvToBgraRow; WebPSamplers[MODE_ARGB] = YuvToArgbRow; WebPSamplers[MODE_RGBA_4444] = YuvToRgba4444Row; WebPSamplers[MODE_RGB_565] = YuvToRgb565Row; WebPSamplers[MODE_rgbA] = YuvToRgbaRow; WebPSamplers[MODE_bgrA] = YuvToBgraRow; WebPSamplers[MODE_Argb] = YuvToArgbRow; WebPSamplers[MODE_rgbA_4444] = YuvToRgba4444Row; // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) if (VP8GetCPUInfo(kSSE2)) { WebPInitSamplersSSE2(); } #endif // WEBP_USE_SSE2 #if defined(WEBP_USE_MIPS32) if (VP8GetCPUInfo(kMIPS32)) { WebPInitSamplersMIPS32(); } #endif // WEBP_USE_MIPS32 #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { WebPInitSamplersMIPSdspR2(); } #endif // WEBP_USE_MIPS_DSP_R2 } yuv_last_cpuinfo_used = VP8GetCPUInfo; } //----------------------------------------------------------------------------- src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieInterpolator.h000664 001750 001750 00000003310 15164251010 037410 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_INTERPOLATOR_H_ #define _TVG_LOTTIE_INTERPOLATOR_H_ #define SPLINE_TABLE_SIZE 11 struct LottieInterpolator { char* key; Point outTangent, inTangent; float progress(float t); void set(const char* key, Point& inTangent, Point& outTangent); private: static constexpr float SAMPLE_STEP_SIZE = 1.0f / float(SPLINE_TABLE_SIZE - 1); float samples[SPLINE_TABLE_SIZE]; float getTForX(float aX); float binarySubdivide(float aX, float aA, float aB); float NewtonRaphsonIterate(float aX, float aGuessT); }; #endif //_TVG_LOTTIE_INTERPOLATOR_H_src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieProperty.h000664 001750 001750 00000073551 15164251010 036570 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_PROPERTY_H_ #define _TVG_LOTTIE_PROPERTY_H_ #include #include "tvgMath.h" #include "tvgStr.h" #include "tvgLottieData.h" #include "tvgLottieInterpolator.h" #include "tvgLottieExpressions.h" #include "tvgLottieModifier.h" struct LottieFont; struct LottieLayer; struct LottieObject; struct LottieProperty; //default keyframe updates condition (no tweening) #define DEFAULT_COND (!tween.active || !frames || (frames->count == 1)) template struct LottieScalarFrame { T value; //keyframe value float no; //frame number LottieInterpolator* interpolator; bool hold = false; //do not interpolate. T interpolate(LottieScalarFrame* next, float frameNo) { auto t = (frameNo - no) / (next->no - no); if (interpolator) t = interpolator->progress(t); if (hold) { if (t < 1.0f) return value; else return next->value; } return tvg::lerp(value, next->value, t); } float angle(LottieScalarFrame* next, float frameNo) { return 0.0f; } void prepare(TVG_UNUSED LottieScalarFrame* next) { } }; template struct LottieVectorFrame { T value; //keyframe value float no; //frame number LottieInterpolator* interpolator; T outTangent, inTangent; float length; bool hasTangent = false; bool hold = false; T interpolate(LottieVectorFrame* next, float frameNo) { auto t = (frameNo - no) / (next->no - no); if (interpolator) t = interpolator->progress(t); if (hold) { if (t < 1.0f) return value; else return next->value; } if (hasTangent) { Bezier bz = {value, value + outTangent, next->value + inTangent, next->value}; return bz.at(bz.atApprox(t * length, length)); } else { return tvg::lerp(value, next->value, t); } } float angle(LottieVectorFrame* next, float frameNo) { if (!hasTangent) { Point dp = next->value - value; return rad2deg(tvg::atan2(dp.y, dp.x)); } auto t = (frameNo - no) / (next->no - no); if (interpolator) t = interpolator->progress(t); Bezier bz = {value, value + outTangent, next->value + inTangent, next->value}; t = bz.atApprox(t * length, length); return bz.angle(t >= 1.0f ? 0.99f : (t <= 0.0f ? 0.01f : t)); } void prepare(LottieVectorFrame* next) { Bezier bz = {value, value + outTangent, next->value + inTangent, next->value}; length = bz.lengthApprox(); } }; struct LottieExpression { //writable expressions variable name and value. struct Writable { char* var; float val; }; char* code; LottieComposition* comp; LottieLayer* layer; LottieObject* object; LottieProperty* property; Array writables; bool disabled = false; LottieExpression() {} LottieExpression(const LottieExpression* rhs) { code = strdup(rhs->code); comp = rhs->comp; layer = rhs->layer; object = rhs->object; property = rhs->property; disabled = rhs->disabled; } ~LottieExpression() { ARRAY_FOREACH(p, writables) { tvg::free(p->var); } tvg::free(code); } bool assign(const char* var, float val) { //overwrite the existing value ARRAY_FOREACH(p, writables) { if (tvg::equal(var, p->var)) { p->val = val; return true; } } writables.push({tvg::duplicate(var), val}); return true; } }; //Property would have an either keyframes or single value. struct LottieProperty { enum class Type : uint8_t {Invalid = 0, Integer, Float, Scalar, Vector, PathSet, Color, Opacity, ColorStop, TextDoc, Image}; enum class Loop : uint8_t {None = 0, InCycle = 1, InPingPong, InOffset, InContinue, OutCycle, OutPingPong, OutOffset, OutContinue}; LottieExpression* exp = nullptr; Type type; uint8_t ix; //property index unsigned long sid; //property sid for slot LottieProperty(Type type = Type::Invalid) : type(type) {} virtual ~LottieProperty() {} virtual uint32_t frameCnt() = 0; virtual uint32_t nearest(float frameNo) = 0; virtual float frameNo(int32_t key) = 0; virtual float loop(float frameNo, uint32_t key, Loop mode, float inout) = 0; bool copy(LottieProperty* rhs, bool shallow) { type = rhs->type; ix = rhs->ix; sid = rhs->sid; if (!rhs->exp) return false; if (shallow) { exp = rhs->exp; rhs->exp = nullptr; } else { exp = new LottieExpression(rhs->exp); } exp->property = this; return true; } }; static void _copy(PathSet* pathset, Array& out, Matrix* transform) { if (transform) { for (int i = 0; i < pathset->ptsCnt; ++i) { out.push(pathset->pts[i] * *transform); } } else { Array in; in.data = pathset->pts; in.count = pathset->ptsCnt; out.push(in); in.data = nullptr; } } static void _copy(PathSet* pathset, Array& out) { Array in; in.data = pathset->cmds; in.count = pathset->cmdsCnt; out.push(in); in.data = nullptr; } template uint32_t _bsearch(T* frames, float frameNo) { int32_t low = 0; int32_t high = int32_t(frames->count) - 1; while (low <= high) { auto mid = low + (high - low) / 2; auto frame = frames->data + mid; if (frameNo < frame->no) high = mid - 1; else low = mid + 1; } if (high < low) low = high; if (low < 0) low = 0; return low; } template uint32_t _nearest(T* frames, float frameNo) { if (frames) { auto key = _bsearch(frames, frameNo); if (key == frames->count - 1) return key; return (fabsf(frames->data[key].no - frameNo) < fabsf(frames->data[key + 1].no - frameNo)) ? key : (key + 1); } return 0; } template float _frameNo(T* frames, int32_t key) { if (!frames) return 0.0f; if (key < 0) key = 0; if (key >= (int32_t) frames->count) key = (int32_t)(frames->count - 1); return (*frames)[key].no; } //TODO: good abstract "frames" interface to remove the template method. template float _loop(T* frames, float frameNo, uint32_t key, LottieProperty::Loop mode, float inout) { if (!frames) return frameNo; if (mode == LottieProperty::Loop::None) return frameNo; frameNo -= frames->first().no; switch (mode) { case LottieProperty::Loop::InCycle: { return fmodf(frameNo, inout - frames->first().no) + (*frames)[key].no; } case LottieProperty::Loop::InPingPong: { auto range = inout - (*frames)[key].no; auto forward = (static_cast(frameNo / range) % 2) == 0 ? true : false; frameNo = fmodf(frameNo, range); return (forward ? frameNo : (range - frameNo)) + (*frames)[key].no; } case LottieProperty::Loop::OutCycle: { return fmodf(frameNo, (*frames)[frames->count - 1 - key].no - frames->first().no) + frames->first().no; } case LottieProperty::Loop::OutPingPong: { auto range = (*frames)[frames->count - 1 - key].no - frames->first().no; auto forward = (static_cast(frameNo / range) % 2) == 0 ? true : false; frameNo = fmodf(frameNo, range); return (forward ? frameNo : (range - frameNo)) + frames->first().no; } default: break; } return frameNo; } template struct LottieGenericProperty : LottieProperty { using MyProperty = LottieGenericProperty; //Property has an either keyframes or single value. Array* frames = nullptr; Value value; LottieGenericProperty(Value v) : LottieProperty(PType), value(v) {} LottieGenericProperty() : LottieProperty(PType) {} LottieGenericProperty(const MyProperty& rhs) { copy(const_cast(rhs)); } ~LottieGenericProperty() { release(); } void release() { delete(frames); frames = nullptr; if (exp) { delete(exp); exp = nullptr; } } uint32_t nearest(float frameNo) override { return _nearest(frames, frameNo); } uint32_t frameCnt() override { return frames ? frames->count : 1; } float frameNo(int32_t key) override { return _frameNo(frames, key); } float loop(float frameNo, uint32_t key, Loop mode, float inout) override { return _loop(frames, frameNo, key, mode, inout); } Frame& newFrame() { if (!frames) frames = new Array; if (frames->count + 1 >= frames->reserved) { auto old = frames->reserved; frames->grow(frames->count + 2); memset((void*)(frames->data + old), 0x00, sizeof(Frame) * (frames->reserved - old)); } ++frames->count; return frames->last(); } Frame& nextFrame() { return (*frames)[frames->count]; } Value operator()(float frameNo, LottieExpressions* exps = nullptr) { //overriding with expressions if (exps && exp) { Value out{}; if (exps->result(frameNo, out, exp)) return out; } if (!frames) return value; if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value; if (frameNo >= frames->last().no) return frames->last().value; auto frame = frames->data + _bsearch(frames, frameNo); if (tvg::equal(frame->no, frameNo)) return frame->value; return frame->interpolate(frame + 1, frameNo); } Value operator()(float frameNo, Tween& tween, LottieExpressions* exps) { if (DEFAULT_COND) return operator()(frameNo, exps); return tvg::lerp(operator()(frameNo, exps), operator()(tween.frameNo, exps), tween.progress); } void copy(MyProperty& rhs, bool shallow = true) { if (LottieProperty::copy(&rhs, shallow)) return; if (rhs.frames) { if (shallow) { frames = rhs.frames; rhs.frames = nullptr; } else { frames = new Array; *frames = *rhs.frames; } } else { frames = nullptr; value = rhs.value; } } float angle(float frameNo) { if (!frames || frames->count == 1) return 0; if (frameNo <= frames->first().no) return frames->first().angle(frames->data + 1, frames->first().no); if (frameNo >= frames->last().no) { auto frame = frames->data + frames->count - 2; return frame->angle(frame + 1, frames->last().no); } auto frame = frames->data + _bsearch(frames, frameNo); return frame->angle(frame + 1, frameNo); } float angle(float frameNo, Tween& tween) { if (DEFAULT_COND) return angle(frameNo); return tvg::lerp(angle(frameNo), angle(tween.frameNo), tween.progress); } void prepare() { if (Scalar) return; if (!frames || frames->count < 2) return; for (auto frame = frames->begin() + 1; frame < frames->end(); ++frame) { (frame - 1)->prepare(frame); } } }; struct LottiePathSet : LottieProperty { Array>* frames = nullptr; PathSet value; LottiePathSet() : LottieProperty(LottieProperty::Type::PathSet) {} ~LottiePathSet() { release(); } void release() { if (exp) { delete(exp); exp = nullptr; } tvg::free(value.cmds); tvg::free(value.pts); if (!frames) return; ARRAY_FOREACH(p, *frames) { tvg::free((*p).value.cmds); tvg::free((*p).value.pts); } tvg::free(frames->data); tvg::free(frames); } uint32_t nearest(float frameNo) override { return _nearest(frames, frameNo); } uint32_t frameCnt() override { return frames ? frames->count : 1; } float frameNo(int32_t key) override { return _frameNo(frames, key); } float loop(float frameNo, uint32_t key, Loop mode, float inout) override { return _loop(frames, frameNo, key, mode, inout); } LottieScalarFrame& newFrame() { if (!frames) { frames = tvg::calloc>>(1, sizeof(Array>)); } if (frames->count + 1 >= frames->reserved) { auto old = frames->reserved; frames->grow(frames->count + 2); memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame) * (frames->reserved - old)); } ++frames->count; return frames->last(); } LottieScalarFrame& nextFrame() { return (*frames)[frames->count]; } //return false means requiring the interpolation bool dispatch(float frameNo, PathSet*& path, LottieScalarFrame*& frame, float& t) { if (!frames) path = &value; else if (frames->count == 1 || frameNo <= frames->first().no) path = &frames->first().value; else if (frameNo >= frames->last().no) path = &frames->last().value; else { frame = frames->data + _bsearch(frames, frameNo); if (tvg::equal(frame->no, frameNo)) path = &frame->value; else if (frame->value.ptsCnt != (frame + 1)->value.ptsCnt) { path = &frame->value; } else { t = (frameNo - frame->no) / ((frame + 1)->no - frame->no); if (frame->interpolator) t = frame->interpolator->progress(t); if (frame->hold) path = &(frame + ((t < 1.0f) ? 0 : 1))->value; else return false; } } return true; } bool modifiedPath(float frameNo, RenderPath& out, Matrix* transform, LottieModifier* modifier) { PathSet* path; LottieScalarFrame* frame; RenderPath temp; float t; if (dispatch(frameNo, path, frame, t)) { if (modifier) return modifier->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, transform, out); _copy(path, out.cmds); _copy(path, out.pts, transform); return true; } //interpolate 2 frames auto s = frame->value.pts; auto e = (frame + 1)->value.pts; auto interpPts = tvg::malloc(frame->value.ptsCnt * sizeof(Point)); auto p = interpPts; for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e, ++p) { *p = tvg::lerp(*s, *e, t); if (transform) *p *= *transform; } if (modifier) modifier->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, nullptr, out); tvg::free(interpPts); return true; } bool defaultPath(float frameNo, RenderPath& out, Matrix* transform) { PathSet* path; LottieScalarFrame* frame; float t; if (dispatch(frameNo, path, frame, t)) { _copy(path, out.cmds); _copy(path, out.pts, transform); return true; } //interpolate 2 frames auto s = frame->value.pts; auto e = (frame + 1)->value.pts; for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e) { auto pt = tvg::lerp(*s, *e, t); if (transform) pt *= *transform; out.pts.push(pt); } _copy(&frame->value, out.cmds); return true; } bool tweening(float frameNo, RenderPath& out, Matrix* transform, LottieModifier* modifier, Tween& tween, LottieExpressions* exps) { RenderPath to; //used as temp as well. auto pivot = out.pts.count; if (!operator()(frameNo, out, transform, exps)) return false; if (!operator()(tween.frameNo, to, transform, exps)) return false; auto from = out.pts.data + pivot; if (to.pts.count != out.pts.count - pivot) TVGLOG("LOTTIE", "Tweening has different numbers of points in consecutive frames."); for (uint32_t i = 0; i < std::min(to.pts.count, (out.pts.count - pivot)); ++i) { from[i] = tvg::lerp(from[i], to.pts[i], tween.progress); } if (!modifier) return true; //Apply modifiers to.clear(); return modifier->modifyPath(to.cmds.data, to.cmds.count, to.pts.data, to.pts.count, transform, out); } bool operator()(float frameNo, RenderPath& out, Matrix* transform, LottieExpressions* exps, LottieModifier* modifier = nullptr) { //overriding with expressions if (exps && exp) { if (exps->result(frameNo, out, transform, modifier, exp)) return true; } if (modifier) return modifiedPath(frameNo, out, transform, modifier); else return defaultPath(frameNo, out, transform); } bool operator()(float frameNo, RenderPath& out, Matrix* transform, Tween& tween, LottieExpressions* exps, LottieModifier* modifier = nullptr) { if (DEFAULT_COND) return operator()(frameNo, out, transform, exps, modifier); return tweening(frameNo, out, transform, modifier, tween, exps); } }; struct LottieColorStop : LottieProperty { Array>* frames = nullptr; ColorStop value; uint16_t count = 0; //colorstop count bool populated = false; LottieColorStop() : LottieProperty(LottieProperty::Type::ColorStop) {} LottieColorStop(const LottieColorStop& rhs) { copy(const_cast(rhs)); } ~LottieColorStop() { release(); } void release() { delete(exp); exp = nullptr; tvg::free(value.data); value.data = nullptr; delete(value.input); value.input = nullptr; if (!frames) return; ARRAY_FOREACH(p, *frames) { tvg::free((*p).value.data); delete((*p).value.input); } tvg::free(frames->data); tvg::free(frames); frames = nullptr; } uint32_t nearest(float frameNo) override { return _nearest(frames, frameNo); } uint32_t frameCnt() override { return frames ? frames->count : 1; } float frameNo(int32_t key) override { return _frameNo(frames, key); } float loop(float frameNo, uint32_t key, Loop mode, float inout) override { return _loop(frames, frameNo, key, mode, inout); } LottieScalarFrame& newFrame() { if (!frames) { frames = tvg::calloc>>(1, sizeof(Array>)); } if (frames->count + 1 >= frames->reserved) { auto old = frames->reserved; frames->grow(frames->count + 2); memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame) * (frames->reserved - old)); } ++frames->count; return frames->last(); } LottieScalarFrame& nextFrame() { return (*frames)[frames->count]; } Result tweening(float frameNo, Fill* fill, Tween& tween, LottieExpressions* exps) { auto frame = frames->data + _bsearch(frames, frameNo); if (tvg::equal(frame->no, frameNo)) return fill->colorStops(frame->value.data, count); //from operator()(frameNo, fill, exps); //to auto dup = fill->duplicate(); operator()(tween.frameNo, dup, exps); //interpolate const Fill::ColorStop* from; auto fromCnt = fill->colorStops(&from); const Fill::ColorStop* to; auto toCnt = fill->colorStops(&to); if (fromCnt != toCnt) TVGLOG("LOTTIE", "Tweening has different numbers of color data in consecutive frames."); for (uint32_t i = 0; i < std::min(fromCnt, toCnt); ++i) { const_cast(from)->offset = tvg::lerp(from->offset, to->offset, tween.progress); const_cast(from)->r = tvg::lerp(from->r, to->r, tween.progress); const_cast(from)->g = tvg::lerp(from->g, to->g, tween.progress); const_cast(from)->b = tvg::lerp(from->b, to->b, tween.progress); const_cast(from)->a = tvg::lerp(from->a, to->a, tween.progress); } return Result::Success; } Result operator()(float frameNo, Fill* fill, LottieExpressions* exps = nullptr) { //overriding with expressions if (exps && exp) { if (exps->result(frameNo, fill, exp)) return Result::Success; } if (!frames) return fill->colorStops(value.data, count); if (frames->count == 1 || frameNo <= frames->first().no) { return fill->colorStops(frames->first().value.data, count); } if (frameNo >= frames->last().no) return fill->colorStops(frames->last().value.data, count); auto frame = frames->data + _bsearch(frames, frameNo); if (tvg::equal(frame->no, frameNo)) return fill->colorStops(frame->value.data, count); //interpolate auto t = (frameNo - frame->no) / ((frame + 1)->no - frame->no); if (frame->interpolator) t = frame->interpolator->progress(t); if (frame->hold) { if (t < 1.0f) fill->colorStops(frame->value.data, count); else fill->colorStops((frame + 1)->value.data, count); } auto s = frame->value.data; auto e = (frame + 1)->value.data; Array result; for (auto i = 0; i < count; ++i, ++s, ++e) { auto offset = tvg::lerp(s->offset, e->offset, t); auto r = tvg::lerp(s->r, e->r, t); auto g = tvg::lerp(s->g, e->g, t); auto b = tvg::lerp(s->b, e->b, t); auto a = tvg::lerp(s->a, e->a, t); result.push({offset, r, g, b, a}); } return fill->colorStops(result.data, count); } Result operator()(float frameNo, Fill* fill, Tween& tween, LottieExpressions* exps) { if (DEFAULT_COND) return operator()(frameNo, fill, exps); return tweening(frameNo, fill, tween, exps); } void copy(LottieColorStop& rhs, bool shallow = true) { if (LottieProperty::copy(&rhs, shallow)) return; //the rhs colorstop is supposed be populated already. if (rhs.frames) { if (shallow) { frames = rhs.frames; rhs.frames = nullptr; } else { frames = tvg::calloc>>(1, sizeof(Array>)); *frames = *rhs.frames; for (uint32_t i = 0; i < (*rhs.frames).count; ++i) { (*frames)[i].value.copy((*rhs.frames)[i].value, rhs.count); } } } else { frames = nullptr; if (shallow) { value = rhs.value; rhs.value = ColorStop(); } else { value.copy(rhs.value, rhs.count); } } populated = rhs.populated; count = rhs.count; } void prepare() {} }; struct LottieTextDoc : LottieProperty { Array>* frames = nullptr; TextDocument value; LottieTextDoc() : LottieProperty(LottieProperty::Type::TextDoc) {} LottieTextDoc(const LottieTextDoc& rhs) { copy(const_cast(rhs)); } ~LottieTextDoc() { release(); } void release() { if (exp) { delete(exp); exp = nullptr; } if (value.text) { tvg::free(value.text); value.text = nullptr; } if (value.name) { tvg::free(value.name); value.name = nullptr; } if (!frames) return; ARRAY_FOREACH(p, *frames) { tvg::free((*p).value.text); tvg::free((*p).value.name); } delete(frames); frames = nullptr; } uint32_t nearest(float frameNo) override { return _nearest(frames, frameNo); } uint32_t frameCnt() override { return frames ? frames->count : 1; } float frameNo(int32_t key) override { return _frameNo(frames, key); } float loop(float frameNo, uint32_t key, Loop mode, float inout) override { return _loop(frames, frameNo, key, mode, inout); } LottieScalarFrame& newFrame() { if (!frames) frames = new Array>; if (frames->count + 1 >= frames->reserved) { auto old = frames->reserved; frames->grow(frames->count + 2); memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame) * (frames->reserved - old)); } ++frames->count; return frames->last(); } LottieScalarFrame& nextFrame() { return (*frames)[frames->count]; } TextDocument& operator()(float frameNo) { if (!frames) return value; if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value; if (frameNo >= frames->last().no) return frames->last().value; auto frame = frames->data + _bsearch(frames, frameNo); return frame->value; } TextDocument& operator()(float frameNo, LottieExpressions* exps) { auto& out = operator()(frameNo); //overriding with expressions if (exps && exp) exps->result(frameNo, out, exp); return out; } void copy(LottieTextDoc& rhs, bool shallow = true) { if (LottieProperty::copy(&rhs, shallow)) return; if (rhs.frames) { if (shallow) { frames = rhs.frames; rhs.frames = nullptr; } else { frames = new Array>; *frames = *rhs.frames; for (uint32_t i = 0; i < (*rhs.frames).count; ++i) { (*frames)[i].value.copy((*rhs.frames)[i].value); } } } else { frames = nullptr; if (shallow) { value = rhs.value; rhs.value.text = nullptr; rhs.value.name = nullptr; } else { value.copy(rhs.value); } } } void prepare() {} }; struct LottieBitmap : LottieProperty { union { char* data = nullptr; char* path; }; Picture *picture = nullptr; char* mimeType = nullptr; uint32_t size = 0; float width = 0.0f; float height = 0.0f; LottieBitmap() : LottieProperty(LottieProperty::Type::Image) {} LottieBitmap(const LottieBitmap& rhs) { copy(const_cast(rhs)); } ~LottieBitmap() { release(); } void release() { if (picture) { picture->unref(); picture = nullptr; } tvg::free(data); tvg::free(mimeType); data = nullptr; mimeType = nullptr; } uint32_t frameCnt() override { return 0; } uint32_t nearest(float frameNo) override { return 0; } float frameNo(int32_t key) override { return 0; } float loop(float frameNo, TVG_UNUSED uint32_t key, TVG_UNUSED Loop mode, TVG_UNUSED float inout) override { return frameNo; } void copy(LottieBitmap& rhs, bool shallow = true) { if (LottieProperty::copy(&rhs, shallow)) return; release(); if (rhs.picture) { picture = rhs.picture; picture->ref(); } width = rhs.width; height = rhs.height; } }; using LottieFloat = LottieGenericProperty, float, LottieProperty::Type::Float>; using LottieInteger = LottieGenericProperty, int8_t, LottieProperty::Type::Integer>; using LottieScalar = LottieGenericProperty, Point, LottieProperty::Type::Scalar>; using LottieVector = LottieGenericProperty, Point, LottieProperty::Type::Vector, 0>; using LottieColor = LottieGenericProperty, RGB32, LottieProperty::Type::Color>; using LottieOpacity = LottieGenericProperty, uint8_t, LottieProperty::Type::Opacity>; #endif //_TVG_LOTTIE_PROPERTY_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test7.lot000664 001750 001750 00000236440 15164251010 034005 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":30,"ip":0,"op":90,"w":400,"h":400,"nm":"Gradient Tests","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Linear Gradient","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[75,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[120,120]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"Rectangle"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":5,"k":{"a":0,"k":[0,1,0,0,0.25,1,0.5,0,0.5,0.5,1,0,0.75,0,1,0.5,1,0,0,1]}},"s":{"a":0,"k":[-50,0]},"e":{"a":0,"k":[50,0]},"t":1,"nm":"Linear Gradient Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Radial Gradient","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[225,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0.2,0.2,0.5,1,0.6,0,1,1,1,0]}},"s":{"a":0,"k":[0,0]},"e":{"a":0,"k":[50,0]},"t":2,"nm":"Radial Gradient Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Gradient Stroke with Zero Opacity","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[150,80,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[60,60]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"gs","o":{"a":1,"k":[{"t":0,"s":[0],"e":[50]},{"t":20,"s":[50],"e":[100]},{"t":30,"s":[100]}]},"w":{"a":0,"k":8},"lc":2,"lj":2,"ml":4,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0,1,0,1,0,0,1,0,1,0.5,1,1,1]}},"s":{"a":0,"k":[-30,0]},"e":{"a":0,"k":[30,0]},"t":1,"nm":"Gradient Stroke"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Gradient Stroke Varying Opacity","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[150,180,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,60]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":10},"nm":"Rectangle"},{"ty":"gs","o":{"a":1,"k":[{"t":30,"s":[10],"e":[30]},{"t":45,"s":[30],"e":[60]},{"t":60,"s":[60],"e":[90]},{"t":75,"s":[90],"e":[100]},{"t":90,"s":[100]}]},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":4,"g":{"p":2,"k":{"a":0,"k":[0,1,0.5,0,1,0,1,0.5,0,1,1,0.8]}},"s":{"a":0,"k":[0,-30]},"e":{"a":0,"k":[0,30]},"t":2,"nm":"Radial Gradient Stroke"}],"ip":0,"op":90,"st":0,"bm":3},{"ddd":0,"ind":5,"ty":4,"nm":"Gradient with Many Color Stops","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[120,80]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":5,"k":{"a":0,"k":[0,1,0,0,0.25,1,1,0,0.5,0,1,0,0.75,0,0,1,1,1,0,1,0,1,0.25,1,0.5,1,0.75,1,1,1]}},"s":{"a":0,"k":[-60,0]},"e":{"a":0,"k":[60,0]},"t":1,"nm":"Gradient Fill 5 Stops"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Radial Gradient with Mismatched Stops","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[300,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":4,"k":{"a":1,"k":[{"t":0,"s":[0,0.2,0.4,0.8,0.33,0.4,0.6,0.9,0.67,0.8,0.2,0.6,1,1,0.5,0.3,0,1,0.33,0.9,0.67,0.7,1,0.5],"e":[0,1,0.5,0.3,0.33,0.8,0.2,0.6,0.67,0.4,0.6,0.9,1,0.2,0.4,0.8,0,0.5,0.33,0.7,0.67,0.9,1,1]},{"t":30,"s":[0,1,0.5,0.3,0.33,0.8,0.2,0.6,0.67,0.4,0.6,0.9,1,0.2,0.4,0.8,0,0.5,0.33,0.7,0.67,0.9,1,1]}]}},"s":{"a":0,"k":[0,0]},"e":{"a":0,"k":[50,0]},"t":2,"nm":"Radial Gradient Animated"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Linear Gradient with 3 Stops","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[200,220,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":1,"pt":{"a":0,"k":5},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":50},"os":{"a":0,"k":0},"ir":{"a":0,"k":25},"is":{"a":0,"k":0},"nm":"Star"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,1,1,1,1,0,0,1,0,1,0.5,0.8,1,1]}},"s":{"a":1,"k":[{"t":0,"s":[0,-50],"e":[-50,50]},{"t":30,"s":[-50,50],"e":[50,-50]},{"t":60,"s":[50,-50]}]},"e":{"a":1,"k":[{"t":0,"s":[0,50],"e":[50,-50]},{"t":30,"s":[50,-50],"e":[-50,50]},{"t":60,"s":[-50,50]}]},"t":1,"nm":"Animated Linear Gradient"}],"ip":0,"op":60,"st":0,"bm":0},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":8,"nm":"Gradient Layer 1 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.39,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.6,23.28],"t":45},{"s":[8.39,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[75.07,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[395.4,214.72],"t":45},{"s":[75.07,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[23.2,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20.000000298023224],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[20.000000298023224],"t":45},{"s":[20.000000298023224],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":9,"nm":"Gradient Layer 2 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":10,"nm":"Gradient Layer 3 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.39,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.6,23.28],"t":45},{"s":[8.39,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[75.07,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[395.4,214.72],"t":45},{"s":[75.07,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[23.2,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":11,"nm":"Gradient Layer 4 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":12,"nm":"Gradient Layer 5 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.28,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.46,23.28],"t":45},{"s":[8.28,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[58.4,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[372.35,214.72],"t":45},{"s":[58.4,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.57,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[22.91,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.57,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20.000000298023224],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[20.000000298023224],"t":45},{"s":[20.000000298023224],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":13,"nm":"Gradient Layer 6 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":14,"nm":"Gradient Layer 7 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.28,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.46,23.28],"t":45},{"s":[8.28,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[58.4,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[372.35,214.72],"t":45},{"s":[58.4,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.57,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[22.91,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.57,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":15,"nm":"Gradient Layer 8 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":16,"nm":"Gradient Layer 9 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.39,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.6,23.28],"t":45},{"s":[8.39,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[41.73,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[349.29,214.72],"t":45},{"s":[41.73,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[23.2,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20.000000298023224],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[20.000000298023224],"t":45},{"s":[20.000000298023224],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":17,"nm":"Gradient Layer 10 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":18,"nm":"Gradient Layer 11 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.39,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.6,23.28],"t":45},{"s":[8.39,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[41.73,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[349.29,214.72],"t":45},{"s":[41.73,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[23.2,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":19,"nm":"Gradient Layer 12 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":20,"nm":"Gradient Layer 13 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.28,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.46,23.28],"t":45},{"s":[8.28,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[25.06,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[326.24,214.72],"t":45},{"s":[25.06,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.57,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[22.91,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.57,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20.000000298023224],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[20.000000298023224],"t":45},{"s":[20.000000298023224],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":21,"nm":"Gradient Layer 14 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":22,"nm":"Gradient Layer 15 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.28,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.46,23.28],"t":45},{"s":[8.28,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[25.06,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[326.24,214.72],"t":45},{"s":[25.06,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.57,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[22.91,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.57,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":23,"nm":"Gradient Layer 16 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":24,"nm":"Gradient Layer 17 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.39,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.6,23.28],"t":45},{"s":[8.39,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.39,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[303.18,214.72],"t":45},{"s":[8.39,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[23.2,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20.000000298023224],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[20.000000298023224],"t":45},{"s":[20.000000298023224],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":25,"nm":"Gradient Layer 18 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":26,"nm":"Gradient Layer 19 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.39,16.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[11.6,23.28],"t":45},{"s":[8.39,16.83],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8.39,220.83],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[303.18,214.72],"t":45},{"s":[8.39,220.83],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[23.2,23.28],[0,46.56]]}],"t":45},{"s":[{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[0,0],[16.77,16.83],[0,33.66]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":10,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[2],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[2],"t":45},{"s":[2],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]}}],"ind":27,"nm":"Gradient Layer 20 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[132.5,60.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[195.99,65],"t":45},{"s":[132.5,60.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[200.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[210.88,182],"t":45},{"s":[200.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,45.01],[34,0],[0,-45.01],[-47.96,23.5]],"o":[[75.54,-10.5],[-31.92,-33.5],[-45.27,0],[-25.5,65],[0,0]],"v":[[88.07,106.5],[170.03,48.5],[83.11,0],[6.11,48.5],[88.07,106.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[60.5,121],[121,60.5],[60.5,0],[0,60.5],[60.5,121]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[30,87.5],[45.27,0],[-25,-44.5],[-45.27,0]],"o":[[8.96,-46],[68.5,-82],[-45.27,0],[0,45.01],[0,0]],"v":[[283.15,130],[370.11,60],[283.15,4],[220.61,48.5],[283.15,130]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,33.41],[33.41,0],[0,-33.41],[-33.41,0]],"o":[[33.41,0],[0,-33.41],[-33.41,0],[0,33.41],[0,0]],"v":[[204.5,121],[265,60.5],[204.5,0],[144,60.5],[204.5,121]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0,0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0,0],"t":45},{"s":[0,0,0],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":28,"nm":"Gradient Layer 21 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[240,87.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[240,87.5],"t":45},{"s":[240,87.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[443,171.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[147,171.5],"t":45},{"s":[443,171.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.47,8.4],[-1.28,8.28],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.47,-8.28],[0,-11.32],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-10.5],[0.58,-8.4],[0,0],[0,0],[0,0],[0,0],[0,0],[1.28,7.82],[0.58,8.17],[0,0],[0,0],[0,0]],"v":[[0,129.5],[0,7],[32.55,7],[77.18,79.63],[50.75,79.45],[95.9,7],[127.4,7],[127.4,129.5],[91,129.5],[91,100.28],[91.7,71.92],[94.5,46.9],[98.7,57.92],[69.13,103.78],[57.58,103.78],[28.53,57.92],[32.9,46.9],[35.53,71.05],[36.4,100.28],[36.4,129.5],[0,129.5]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.47,8.4],[-1.28,8.28],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.47,-8.28],[0,-11.32],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-10.5],[0.58,-8.4],[0,0],[0,0],[0,0],[0,0],[0,0],[1.28,7.82],[0.58,8.17],[0,0],[0,0],[0,0]],"v":[[0,129.5],[0,7],[32.55,7],[77.18,79.63],[50.75,79.45],[95.9,7],[127.4,7],[127.4,129.5],[91,129.5],[91,100.28],[91.7,71.92],[94.5,46.9],[98.7,57.92],[69.13,103.78],[57.58,103.78],[28.53,57.92],[32.9,46.9],[35.53,71.05],[36.4,100.28],[36.4,129.5],[0,129.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.47,8.4],[-1.28,8.28],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.47,-8.28],[0,-11.32],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-10.5],[0.58,-8.4],[0,0],[0,0],[0,0],[0,0],[0,0],[1.28,7.82],[0.58,8.17],[0,0],[0,0],[0,0]],"v":[[0,129.5],[0,7],[32.55,7],[77.18,79.63],[50.75,79.45],[95.9,7],[127.4,7],[127.4,129.5],[91,129.5],[91,100.28],[91.7,71.92],[94.5,46.9],[98.7,57.92],[69.13,103.78],[57.58,103.78],[28.53,57.92],[32.9,46.9],[35.53,71.05],[36.4,100.28],[36.4,129.5],[0,129.5]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[6.07,4.08],[3.5,7.23],[0,9.45],[-3.38,7.23],[-6.07,4.08],[-8.05,0],[-3.5,-1.17],[-2.68,-2.1],[-1.98,-2.8],[-1.4,-3.15],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[2.1,-2.92],[2.8,-2.1],[3.5,-1.28],[4.2,0]],"o":[[-7.82,0],[-6.07,-4.2],[-3.5,-7.35],[0,-9.57],[3.5,-7.35],[6.18,-4.2],[4.2,0],[3.5,1.17],[2.68,2.1],[1.98,2.68],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.17,3.38],[-1.98,2.92],[-2.8,2.1],[-3.5,1.17],[0,0]],"v":[[187.02,131.07],[166.2,124.95],[151.85,107.8],[146.6,82.6],[151.67,57.4],[166.02,40.25],[187.37,33.95],[198.92,35.7],[208.2,40.6],[215.2,47.95],[220.27,56.7],[213.1,56.52],[213.1,35.87],[247.92,35.87],[247.92,129.5],[212.22,129.5],[212.22,106.57],[220.1,107.28],[215.2,116.72],[208.02,124.25],[198.57,129.32],[187.02,131.07]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[6.07,4.08],[3.5,7.23],[0,9.45],[-3.38,7.23],[-6.07,4.08],[-8.05,0],[-3.5,-1.17],[-2.68,-2.1],[-1.98,-2.8],[-1.4,-3.15],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[2.1,-2.92],[2.8,-2.1],[3.5,-1.28],[4.2,0]],"o":[[-7.82,0],[-6.07,-4.2],[-3.5,-7.35],[0,-9.57],[3.5,-7.35],[6.18,-4.2],[4.2,0],[3.5,1.17],[2.68,2.1],[1.98,2.68],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.17,3.38],[-1.98,2.92],[-2.8,2.1],[-3.5,1.17],[0,0]],"v":[[187.02,131.07],[166.2,124.95],[151.85,107.8],[146.6,82.6],[151.67,57.4],[166.02,40.25],[187.37,33.95],[198.92,35.7],[208.2,40.6],[215.2,47.95],[220.27,56.7],[213.1,56.52],[213.1,35.87],[247.92,35.87],[247.92,129.5],[212.22,129.5],[212.22,106.57],[220.1,107.28],[215.2,116.72],[208.02,124.25],[198.57,129.32],[187.02,131.07]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[6.07,4.08],[3.5,7.23],[0,9.45],[-3.38,7.23],[-6.07,4.08],[-8.05,0],[-3.5,-1.17],[-2.68,-2.1],[-1.98,-2.8],[-1.4,-3.15],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[2.1,-2.92],[2.8,-2.1],[3.5,-1.28],[4.2,0]],"o":[[-7.82,0],[-6.07,-4.2],[-3.5,-7.35],[0,-9.57],[3.5,-7.35],[6.18,-4.2],[4.2,0],[3.5,1.17],[2.68,2.1],[1.98,2.68],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.17,3.38],[-1.98,2.92],[-2.8,2.1],[-3.5,1.17],[0,0]],"v":[[187.02,131.07],[166.2,124.95],[151.85,107.8],[146.6,82.6],[151.67,57.4],[166.02,40.25],[187.37,33.95],[198.92,35.7],[208.2,40.6],[215.2,47.95],[220.27,56.7],[213.1,56.52],[213.1,35.87],[247.92,35.87],[247.92,129.5],[212.22,129.5],[212.22,106.57],[220.1,107.28],[215.2,116.72],[208.02,124.25],[198.57,129.32],[187.02,131.07]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[-2.33,1.52],[-1.17,2.92],[0,4.2],[1.28,2.92],[2.45,1.52],[3.38,0],[2.33,-1.52],[1.28,-3.03],[0,-4.2],[-1.17,-2.92],[-2.33,-1.52],[-3.38,0]],"o":[[3.38,0],[2.45,-1.52],[1.28,-2.92],[0,-4.2],[-1.17,-3.03],[-2.33,-1.52],[-3.38,0],[-2.33,1.52],[-1.17,2.92],[0,4.2],[1.28,2.92],[2.33,1.52],[0,0]],"v":[[197.17,102.2],[205.75,99.92],[211.17,93.28],[213.1,82.6],[211.17,71.92],[205.75,65.1],[197.17,62.83],[188.6,65.1],[183.17,71.92],[181.42,82.6],[183.17,93.28],[188.6,99.92],[197.17,102.2]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[-2.33,1.52],[-1.17,2.92],[0,4.2],[1.28,2.92],[2.45,1.52],[3.38,0],[2.33,-1.52],[1.28,-3.03],[0,-4.2],[-1.17,-2.92],[-2.33,-1.52],[-3.38,0]],"o":[[3.38,0],[2.45,-1.52],[1.28,-2.92],[0,-4.2],[-1.17,-3.03],[-2.33,-1.52],[-3.38,0],[-2.33,1.52],[-1.17,2.92],[0,4.2],[1.28,2.92],[2.33,1.52],[0,0]],"v":[[197.17,102.2],[205.75,99.92],[211.17,93.28],[213.1,82.6],[211.17,71.92],[205.75,65.1],[197.17,62.83],[188.6,65.1],[183.17,71.92],[181.42,82.6],[183.17,93.28],[188.6,99.92],[197.17,102.2]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[-2.33,1.52],[-1.17,2.92],[0,4.2],[1.28,2.92],[2.45,1.52],[3.38,0],[2.33,-1.52],[1.28,-3.03],[0,-4.2],[-1.17,-2.92],[-2.33,-1.52],[-3.38,0]],"o":[[3.38,0],[2.45,-1.52],[1.28,-2.92],[0,-4.2],[-1.17,-3.03],[-2.33,-1.52],[-3.38,0],[-2.33,1.52],[-1.17,2.92],[0,4.2],[1.28,2.92],[2.33,1.52],[0,0]],"v":[[197.17,102.2],[205.75,99.92],[211.17,93.28],[213.1,82.6],[211.17,71.92],[205.75,65.1],[197.17,62.83],[188.6,65.1],[183.17,71.92],[181.42,82.6],[183.17,93.28],[188.6,99.92],[197.17,102.2]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[7.58,3.03],[4.2,4.67],[0,0],[-4.78,-1.63],[-3.27,0],[-0.82,0.12],[-0.47,0.35],[-0.23,0.47],[0,0.7],[1.87,0.82],[2.45,0.7],[3.85,0.93],[3.85,1.75],[2.68,2.33],[1.63,3.27],[0,4.08],[-2.22,3.73],[-3.85,2.57],[-4.78,1.4],[-5.02,0],[-4.9,-1.17],[-4.32,-2.1],[-3.62,-2.8],[0,0],[2.68,1.52],[2.92,0.93],[2.45,0],[0.93,-0.23],[0.58,-0.47],[0.35,-0.58],[0,-0.7],[-0.58,-0.82],[-1.28,-0.58],[-2.45,-0.82],[-4.9,-1.4],[-3.97,-1.98],[-2.45,-2.68],[-0.93,-2.33],[0,-2.68],[3.73,-5.02],[6.42,-2.92],[8.28,0]],"o":[[-10.15,0],[-7.47,-3.15],[0,0],[3.73,3.27],[4.9,1.52],[1.17,0],[0.93,-0.23],[0.58,-0.47],[0.35,-0.47],[0,-1.75],[-1.05,-0.47],[-2.33,-0.7],[-4.55,-1.17],[-3.73,-1.75],[-2.8,-2.57],[-1.52,-3.27],[0,-4.78],[2.22,-3.73],[3.85,-2.68],[4.78,-1.4],[5.72,0],[5.02,1.05],[4.43,1.98],[0,0],[-1.98,-1.63],[-2.57,-1.52],[-2.8,-1.05],[-1.28,0],[-0.82,0.12],[-0.58,0.35],[-0.23,0.47],[0,0.93],[0.58,0.82],[1.05,0.47],[2.57,0.82],[5.25,1.52],[4.08,1.98],[1.63,1.87],[0.93,2.33],[0,6.42],[-3.62,4.9],[-6.42,2.8],[0,0]],"v":[[304.4,131.25],[277.8,126.7],[260.3,114.97],[280.95,97.3],[293.72,104.65],[305.97,106.93],[308.95,106.75],[311.05,105.88],[312.27,104.47],[312.8,102.72],[310,98.88],[304.75,97.13],[295.47,94.67],[282.87,90.3],[273.25,84.18],[266.6,75.42],[264.32,64.4],[267.65,51.62],[276.75,42.17],[289.7,36.05],[304.4,33.95],[320.32,35.7],[334.32,40.42],[346.4,47.6],[328.37,67.72],[321.37,63],[313.15,59.32],[305.27,57.75],[301.95,58.1],[299.85,58.97],[298.45,60.38],[298.1,62.13],[298.97,64.75],[301.77,66.85],[307.02,68.78],[318.22,72.1],[332.05,77.35],[341.85,84.35],[345.7,90.65],[347.1,98.18],[341.5,115.33],[326.45,127.05],[304.4,131.25]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[7.58,3.03],[4.2,4.67],[0,0],[-4.78,-1.63],[-3.27,0],[-0.82,0.12],[-0.47,0.35],[-0.23,0.47],[0,0.7],[1.87,0.82],[2.45,0.7],[3.85,0.93],[3.85,1.75],[2.68,2.33],[1.63,3.27],[0,4.08],[-2.22,3.73],[-3.85,2.57],[-4.78,1.4],[-5.02,0],[-4.9,-1.17],[-4.32,-2.1],[-3.62,-2.8],[0,0],[2.68,1.52],[2.92,0.93],[2.45,0],[0.93,-0.23],[0.58,-0.47],[0.35,-0.58],[0,-0.7],[-0.58,-0.82],[-1.28,-0.58],[-2.45,-0.82],[-4.9,-1.4],[-3.97,-1.98],[-2.45,-2.68],[-0.93,-2.33],[0,-2.68],[3.73,-5.02],[6.42,-2.92],[8.28,0]],"o":[[-10.15,0],[-7.47,-3.15],[0,0],[3.73,3.27],[4.9,1.52],[1.17,0],[0.93,-0.23],[0.58,-0.47],[0.35,-0.47],[0,-1.75],[-1.05,-0.47],[-2.33,-0.7],[-4.55,-1.17],[-3.73,-1.75],[-2.8,-2.57],[-1.52,-3.27],[0,-4.78],[2.22,-3.73],[3.85,-2.68],[4.78,-1.4],[5.72,0],[5.02,1.05],[4.43,1.98],[0,0],[-1.98,-1.63],[-2.57,-1.52],[-2.8,-1.05],[-1.28,0],[-0.82,0.12],[-0.58,0.35],[-0.23,0.47],[0,0.93],[0.58,0.82],[1.05,0.47],[2.57,0.82],[5.25,1.52],[4.08,1.98],[1.63,1.87],[0.93,2.33],[0,6.42],[-3.62,4.9],[-6.42,2.8],[0,0]],"v":[[304.4,131.25],[277.8,126.7],[260.3,114.97],[280.95,97.3],[293.72,104.65],[305.97,106.93],[308.95,106.75],[311.05,105.88],[312.27,104.47],[312.8,102.72],[310,98.88],[304.75,97.13],[295.47,94.67],[282.87,90.3],[273.25,84.18],[266.6,75.42],[264.32,64.4],[267.65,51.62],[276.75,42.17],[289.7,36.05],[304.4,33.95],[320.32,35.7],[334.32,40.42],[346.4,47.6],[328.37,67.72],[321.37,63],[313.15,59.32],[305.27,57.75],[301.95,58.1],[299.85,58.97],[298.45,60.38],[298.1,62.13],[298.97,64.75],[301.77,66.85],[307.02,68.78],[318.22,72.1],[332.05,77.35],[341.85,84.35],[345.7,90.65],[347.1,98.18],[341.5,115.33],[326.45,127.05],[304.4,131.25]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[7.58,3.03],[4.2,4.67],[0,0],[-4.78,-1.63],[-3.27,0],[-0.82,0.12],[-0.47,0.35],[-0.23,0.47],[0,0.7],[1.87,0.82],[2.45,0.7],[3.85,0.93],[3.85,1.75],[2.68,2.33],[1.63,3.27],[0,4.08],[-2.22,3.73],[-3.85,2.57],[-4.78,1.4],[-5.02,0],[-4.9,-1.17],[-4.32,-2.1],[-3.62,-2.8],[0,0],[2.68,1.52],[2.92,0.93],[2.45,0],[0.93,-0.23],[0.58,-0.47],[0.35,-0.58],[0,-0.7],[-0.58,-0.82],[-1.28,-0.58],[-2.45,-0.82],[-4.9,-1.4],[-3.97,-1.98],[-2.45,-2.68],[-0.93,-2.33],[0,-2.68],[3.73,-5.02],[6.42,-2.92],[8.28,0]],"o":[[-10.15,0],[-7.47,-3.15],[0,0],[3.73,3.27],[4.9,1.52],[1.17,0],[0.93,-0.23],[0.58,-0.47],[0.35,-0.47],[0,-1.75],[-1.05,-0.47],[-2.33,-0.7],[-4.55,-1.17],[-3.73,-1.75],[-2.8,-2.57],[-1.52,-3.27],[0,-4.78],[2.22,-3.73],[3.85,-2.68],[4.78,-1.4],[5.72,0],[5.02,1.05],[4.43,1.98],[0,0],[-1.98,-1.63],[-2.57,-1.52],[-2.8,-1.05],[-1.28,0],[-0.82,0.12],[-0.58,0.35],[-0.23,0.47],[0,0.93],[0.58,0.82],[1.05,0.47],[2.57,0.82],[5.25,1.52],[4.08,1.98],[1.63,1.87],[0.93,2.33],[0,6.42],[-3.62,4.9],[-6.42,2.8],[0,0]],"v":[[304.4,131.25],[277.8,126.7],[260.3,114.97],[280.95,97.3],[293.72,104.65],[305.97,106.93],[308.95,106.75],[311.05,105.88],[312.27,104.47],[312.8,102.72],[310,98.88],[304.75,97.13],[295.47,94.67],[282.87,90.3],[273.25,84.18],[266.6,75.42],[264.32,64.4],[267.65,51.62],[276.75,42.17],[289.7,36.05],[304.4,33.95],[320.32,35.7],[334.32,40.42],[346.4,47.6],[328.37,67.72],[321.37,63],[313.15,59.32],[305.27,57.75],[301.95,58.1],[299.85,58.97],[298.45,60.38],[298.1,62.13],[298.97,64.75],[301.77,66.85],[307.02,68.78],[318.22,72.1],[332.05,77.35],[341.85,84.35],[345.7,90.65],[347.1,98.18],[341.5,115.33],[326.45,127.05],[304.4,131.25]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[395.89,103.25],[378.39,81.2],[421.62,35.87],[467.82,35.87],[395.89,103.25]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[395.89,103.25],[378.39,81.2],[421.62,35.87],[467.82,35.87],[395.89,103.25]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[395.89,103.25],[378.39,81.2],[421.62,35.87],[467.82,35.87],[395.89,103.25]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[361.07,129.5],[361.07,0],[396.59,0],[396.59,129.5],[361.07,129.5]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[361.07,129.5],[361.07,0],[396.59,0],[396.59,129.5],[361.07,129.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[361.07,129.5],[361.07,0],[396.59,0],[396.59,129.5],[361.07,129.5]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[424.77,129.5],[393.62,85.57],[418.47,67.72],[465.37,129.5],[424.77,129.5]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[424.77,129.5],[393.62,85.57],[418.47,67.72],[465.37,129.5],[424.77,129.5]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[424.77,129.5],[393.62,85.57],[418.47,67.72],[465.37,129.5],[424.77,129.5]]}],"t":66}]}},{"ty":"gf","e":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[226,152],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[226,152],"t":45},{"s":[226,152],"t":66}]},"g":{"p":2,"k":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0.9799999594688416,0,1,1,0,1,0.3999999761581421],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0,0.9799999594688416,0,1,1,1,0.6000000238418579,0],"t":45},{"s":[0,0.9799999594688416,0,1,1,0,1,0.3999999761581421],"t":66}]}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[226,-23],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[226,-23],"t":45},{"s":[226,-23],"t":66}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":29,"nm":"Gradient Layer 22 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15.5,16],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[12.5,13],"t":45},{"s":[15.5,16],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[264.5,215],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[254,225.5],"t":45},{"s":[264.5,215],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[-90],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[-0.8,-2.03],[-1.23,1.26],[-2.51,-2.58],[3.22,-3.31],[2.22,-0.81],[-1.38,-1.42],[2.51,-2.58],[3.22,3.31],[0.79,2.35],[1.41,-1.45],[2.51,2.58],[-2.89,2.97],[-2.07,0.83],[1.22,1.26],[-2.51,2.58],[-2.89,-2.97]],"o":[[1.15,1.18],[0.81,-2.18],[2.89,-2.97],[2.51,2.58],[-1.44,1.48],[2.41,0.83],[2.89,2.97],[-2.51,2.58],[-1.48,-1.52],[-0.79,2.23],[-3.22,3.31],[-2.51,-2.58],[1.2,-1.24],[-1.86,-0.81],[-3.22,-3.31],[2.51,-2.58],[0,0]],"v":[[12.39,2.23],[15.45,7.32],[18.66,1.83],[28.33,2.41],[28.88,12.33],[23.06,15.92],[29.21,19.52],[28.66,29.45],[19,30.02],[15.44,23.84],[12,29.68],[2.34,29.11],[1.79,19.18],[7.01,15.92],[2.17,12.73],[2.73,2.8],[12.39,2.23]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[-0.65,-1.65],[-0.99,1.02],[-2.03,-2.1],[2.6,-2.69],[1.79,-0.66],[-1.11,-1.15],[2.03,-2.1],[2.6,2.69],[0.64,1.91],[1.14,-1.18],[2.03,2.1],[-2.33,2.41],[-1.67,0.68],[0.99,1.02],[-2.03,2.1],[-2.33,-2.41]],"o":[[0.93,0.96],[0.66,-1.77],[2.33,-2.41],[2.03,2.1],[-1.16,1.2],[1.94,0.67],[2.33,2.41],[-2.03,2.1],[-1.19,-1.24],[-0.64,1.81],[-2.6,2.69],[-2.03,-2.1],[0.97,-1],[-1.5,-0.66],[-2.6,-2.69],[2.03,-2.1],[0,0]],"v":[[9.99,1.82],[12.46,5.95],[15.05,1.49],[22.84,1.95],[23.29,10.02],[18.6,12.93],[23.56,15.86],[23.11,23.93],[15.32,24.39],[12.45,19.37],[9.68,24.11],[1.89,23.65],[1.44,15.58],[5.65,12.94],[1.75,10.35],[2.2,2.28],[9.99,1.82]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[-0.8,-2.03],[-1.23,1.26],[-2.51,-2.58],[3.22,-3.31],[2.22,-0.81],[-1.38,-1.42],[2.51,-2.58],[3.22,3.31],[0.79,2.35],[1.41,-1.45],[2.51,2.58],[-2.89,2.97],[-2.07,0.83],[1.22,1.26],[-2.51,2.58],[-2.89,-2.97]],"o":[[1.15,1.18],[0.81,-2.18],[2.89,-2.97],[2.51,2.58],[-1.44,1.48],[2.41,0.83],[2.89,2.97],[-2.51,2.58],[-1.48,-1.52],[-0.79,2.23],[-3.22,3.31],[-2.51,-2.58],[1.2,-1.24],[-1.86,-0.81],[-3.22,-3.31],[2.51,-2.58],[0,0]],"v":[[12.39,2.23],[15.45,7.32],[18.66,1.83],[28.33,2.41],[28.88,12.33],[23.06,15.92],[29.21,19.52],[28.66,29.45],[19,30.02],[15.44,23.84],[12,29.68],[2.34,29.11],[1.79,19.18],[7.01,15.92],[2.17,12.73],[2.73,2.8],[12.39,2.23]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[2.51,2.41],[2.34,-2.58],[-2.51,-2.41],[-2.34,2.58]],"o":[[-2.48,2.44],[2.38,2.55],[2.48,-2.44],[-2.38,-2.55],[0,0]],"v":[[19.98,11.44],[11.09,11.51],[11.15,20.64],[20.05,20.58],[19.98,11.44]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[2.02,1.96],[1.89,-2.1],[-2.02,-1.96],[-1.89,2.1]],"o":[[-2,1.98],[1.92,2.07],[2,-1.98],[-1.92,-2.07],[0,0]],"v":[[16.12,9.3],[8.95,9.35],[9,16.77],[16.17,16.72],[16.12,9.3]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[2.51,2.41],[2.34,-2.58],[-2.51,-2.41],[-2.34,2.58]],"o":[[-2.48,2.44],[2.38,2.55],[2.48,-2.44],[-2.38,-2.55],[0,0]],"v":[[19.98,11.44],[11.09,11.51],[11.15,20.64],[20.05,20.58],[19.98,11.44]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[1,1,1],"t":45},{"s":[1,1,1],"t":66}]},"r":2,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":30,"nm":"Gradient Layer 23 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[28,28.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[26,27],"t":45},{"s":[28,28.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[140,87.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[121,90],"t":45},{"s":[140,87.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[-180],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[-1.45,-3.61],[-2.21,2.24],[-4.54,-4.6],[5.82,-5.89],[4,-1.45],[-2.5,-2.53],[4.54,-4.6],[5.82,5.89],[1.42,4.18],[2.55,-2.58],[4.54,4.6],[-5.22,5.28],[-3.75,1.48],[2.21,2.24],[-4.54,4.6],[-5.22,-5.28]],"o":[[2.08,2.11],[1.47,-3.88],[5.22,-5.28],[4.54,4.6],[-2.6,2.63],[4.35,1.48],[5.22,5.28],[-4.54,4.6],[-2.68,-2.71],[-1.44,3.97],[-5.82,5.89],[-4.54,-4.6],[2.17,-2.2],[-3.36,-1.44],[-5.82,-5.89],[4.54,-4.6],[0,0]],"v":[[22.38,3.98],[27.91,13.04],[33.72,3.27],[51.17,4.28],[52.17,21.97],[41.66,28.36],[52.77,34.77],[51.77,52.46],[34.32,53.48],[27.89,42.47],[21.68,52.87],[4.23,51.85],[3.23,34.17],[12.66,28.36],[3.93,22.68],[4.93,5],[22.38,3.98]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[-1.35,-3.42],[-2.06,2.13],[-4.22,-4.36],[5.4,-5.58],[3.72,-1.37],[-2.32,-2.4],[4.22,-4.36],[5.4,5.58],[1.32,3.96],[2.36,-2.44],[4.22,4.36],[-4.84,5.01],[-3.48,1.41],[2.05,2.12],[-4.22,4.36],[-4.84,-5.01]],"o":[[1.93,1.99],[1.36,-3.67],[4.84,-5.01],[4.22,4.36],[-2.41,2.5],[4.04,1.4],[4.84,5.01],[-4.22,4.36],[-2.48,-2.57],[-1.33,3.76],[-5.4,5.58],[-4.22,-4.36],[2.02,-2.09],[-3.12,-1.36],[-5.4,-5.58],[4.22,-4.36],[0,0]],"v":[[20.78,3.77],[25.92,12.35],[31.31,3.1],[47.51,4.06],[48.45,20.81],[38.68,26.86],[49,32.94],[48.07,49.7],[31.87,50.66],[25.9,40.24],[20.13,50.08],[3.93,49.12],[3,32.37],[11.76,26.87],[3.65,21.49],[4.58,4.73],[20.78,3.77]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[-1.45,-3.61],[-2.21,2.24],[-4.54,-4.6],[5.82,-5.89],[4,-1.45],[-2.5,-2.53],[4.54,-4.6],[5.82,5.89],[1.42,4.18],[2.55,-2.58],[4.54,4.6],[-5.22,5.28],[-3.75,1.48],[2.21,2.24],[-4.54,4.6],[-5.22,-5.28]],"o":[[2.08,2.11],[1.47,-3.88],[5.22,-5.28],[4.54,4.6],[-2.6,2.63],[4.35,1.48],[5.22,5.28],[-4.54,4.6],[-2.68,-2.71],[-1.44,3.97],[-5.82,5.89],[-4.54,-4.6],[2.17,-2.2],[-3.36,-1.44],[-5.82,-5.89],[4.54,-4.6],[0,0]],"v":[[22.38,3.98],[27.91,13.04],[33.72,3.27],[51.17,4.28],[52.17,21.97],[41.66,28.36],[52.77,34.77],[51.77,52.46],[34.32,53.48],[27.89,42.47],[21.68,52.87],[4.23,51.85],[3.23,34.17],[12.66,28.36],[3.93,22.68],[4.93,5],[22.38,3.98]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[4.53,4.29],[4.23,-4.6],[-4.53,-4.29],[-4.23,4.6]],"o":[[-4.48,4.35],[4.29,4.54],[4.48,-4.35],[-4.29,-4.53],[0,0]],"v":[[36.1,20.38],[20.04,20.49],[20.15,36.77],[36.21,36.66],[36.1,20.38]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[4.21,4.06],[3.93,-4.35],[-4.21,-4.06],[-3.93,4.35]],"o":[[-4.16,4.12],[3.99,4.3],[4.16,-4.12],[-3.99,-4.3],[0,0]],"v":[[33.52,19.31],[18.61,19.42],[18.71,34.83],[33.62,34.73],[33.52,19.31]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[4.53,4.29],[4.23,-4.6],[-4.53,-4.29],[-4.23,4.6]],"o":[[-4.48,4.35],[4.29,4.54],[4.48,-4.35],[-4.29,-4.53],[0,0]],"v":[[36.1,20.38],[20.04,20.49],[20.15,36.77],[36.21,36.66],[36.1,20.38]]}],"t":66}]}},{"ty":"fl","c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[1,1,1],"t":45},{"s":[1,1,1],"t":66}]},"r":2,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}}],"ind":31,"nm":"Gradient Layer 24 from test29"},{"ty":4,"sr":1,"st":0,"op":67,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[153.5,70.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[214,71],"t":45},{"s":[153.5,70.5],"t":66}]},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100,100],"t":45},{"s":[100,100],"t":66}]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[199.5,149.5],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[211,182],"t":45},{"s":[199.5,149.5],"t":66}]},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[0],"t":45},{"s":[0],"t":66}]},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]}},"shapes":[{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,38.94],[38.71,0],[0,-38.94],[-38.71,0]],"o":[[38.71,0],[0,-38.94],[-38.71,0],[0,38.94],[0,0]],"v":[[70.09,141],[140.18,70.5],[70.09,0],[0,70.5],[70.09,141]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[0,49.17],[37.12,0],[0,-49.17],[-52.37,25.67]],"o":[[82.48,-11.47],[-34.85,-36.59],[-49.42,0],[-27.84,71],[0,0]],"v":[[96.17,116.33],[185.66,52.98],[90.75,0],[6.68,52.98],[96.17,116.33]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,38.94],[38.71,0],[0,-38.94],[-38.71,0]],"o":[[38.71,0],[0,-38.94],[-38.71,0],[0,38.94],[0,0]],"v":[[70.09,141],[140.18,70.5],[70.09,0],[0,70.5],[70.09,141]]}],"t":66}]}},{"ty":"sh","d":1,"ks":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,38.94],[38.71,0],[0,-38.94],[-38.71,0]],"o":[[38.71,0],[0,-38.94],[-38.71,0],[0,38.94],[0,0]],"v":[[236.91,141],[307,70.5],[236.91,0],[166.82,70.5],[236.91,141]]}],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[{"c":true,"i":[[0,0],[32.76,95.58],[49.42,0],[-27.3,-48.61],[-49.42,0]],"o":[[9.78,-50.25],[74.79,-89.57],[-49.42,0],[0,49.17],[0,0]],"v":[[309.17,142],[404.12,65.54],[309.17,4.37],[240.88,52.98],[309.17,142]]}],"t":45},{"s":[{"c":true,"i":[[0,0],[0,38.94],[38.71,0],[0,-38.94],[-38.71,0]],"o":[[38.71,0],[0,-38.94],[-38.71,0],[0,38.94],[0,0]],"v":[[236.91,141],[307,70.5],[236.91,0],[166.82,70.5],[236.91,141]]}],"t":66}]}},{"ty":"st","lc":1,"lj":1,"ml":4,"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[100],"t":45},{"s":[100],"t":66}]},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[1],"t":45},{"s":[1],"t":66}]},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":0},{"o":{"x":0.645,"y":0.045},"i":{"x":0.355,"y":1},"s":[1,1,1],"t":45},{"s":[1,1,1],"t":66}]}}],"ind":32,"nm":"Gradient Layer 25 from test29"},{"ty":4,"shapes":[{"ty":"rc","s":{"a":0,"k":[300,200]},"p":{"a":0,"k":[200,200]}},{"ty":"gf","o":{"a":0,"k":100},"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0,1,0,1,0,0,1,0,1,0.25,0.8,0.5,0.6,0.75,0.4,1,0.2]}},"s":{"a":0,"k":[0,200]},"e":{"a":0,"k":[300,200]},"t":1}],"ip":0,"op":60,"ind":33,"nm":"Gradient Layer from test34"}],"meta":{"g":"LottieFiles Figma v37"},"markers":[]}external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/re-compiler.h000664 001750 001750 00000002130 15164251010 044470 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 RE_COMPILER_H #define RE_COMPILER_H #include "ecma-globals.h" #include "re-bytecode.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup parser Parser * @{ * * \addtogroup regexparser Regular expression * @{ * * \addtogroup regexparser_compiler Compiler * @{ */ re_compiled_code_t *re_compile_bytecode (ecma_string_t *pattern_str_p, uint16_t flags); void re_cache_gc (void); /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ #endif /* !RE_COMPILER_H */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers-conversion.cpp000664 001750 001750 00000055620 15164251010 046402 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "jrt-libc-includes.h" #include "lit-char-helpers.h" #include "lit-magic-strings.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ #if JERRY_NUMBER_TYPE_FLOAT64 /** * \addtogroup ecmahelpersbigintegers Helpers for operations intermediate 128-bit integers * @{ */ /** * 128-bit integer type */ typedef struct { uint64_t hi; /**< high 64 bits */ uint64_t lo; /**< low 64 bits */ } ecma_uint128_t; /** * Round high part of 128-bit integer to uint64_t * * @return rounded high to uint64_t */ static uint64_t ecma_round_high_to_uint64 (ecma_uint128_t *num_p) { uint64_t masked_lo = num_p->lo & ~(1ULL << 63u); uint64_t masked_hi = num_p->hi & 0x1; if ((num_p->lo >> 63u != 0) && (masked_lo > 0 || masked_hi != 0)) { return (num_p->hi + 1); } return num_p->hi; } /* ecma_round_high_to_uint64 */ /** * Left shift 128-bit integer by max 63 bits. */ static void ecma_uint128_shift_left (ecma_uint128_t *num_p, int32_t shift) { num_p->hi = (num_p->hi << shift) | (num_p->lo >> (64 - shift)); num_p->lo <<= shift; } /* ecma_uint128_shift_left */ /** * Right shift 128-bit integer by max 63 bits. */ static void ecma_uint128_shift_right (ecma_uint128_t *num_p, int32_t shift) { num_p->lo = (num_p->lo >> shift) | (num_p->hi << (64 - shift)); num_p->hi >>= shift; } /* ecma_uint128_shift_right */ /** * Add two 128-bit integer values and assign the result to the left one. */ static void ecma_uint128_add (ecma_uint128_t *left_p, ecma_uint128_t *right_p) { left_p->hi += right_p->hi; left_p->lo += right_p->lo; if (left_p->lo < right_p->lo) { left_p->hi++; } } /* ecma_uint128_add */ /** * Multiply 128-bit integer by 10 */ static void ecma_uint128_mul10 (ecma_uint128_t *num_p) { ecma_uint128_shift_left (num_p, 1u); ecma_uint128_t tmp = { num_p->hi, num_p->lo }; ecma_uint128_shift_left (&tmp, 2u); ecma_uint128_add (num_p, &tmp); } /* ecma_uint128_mul10 */ /** * Divide 128-bit integer by 10 * * N = N3 *2^96 + N2 *2^64 + N1 *2^32 + N0 *2^0 // 128-bit dividend * T = T3 *2^-32 + T2 *2^-64 + T1 *2^-96 + T0 *2^-128 // 128-bit divisor reciprocal, 1/10 * 2^-128 * * N * T = N3*T3 *2^64 + N2*T3 *2^32 + N1*T3 *2^0 + N0*T3 *2^-32 * + N3*T2 *2^32 + N2*T2 *2^0 + N1*T2 *2^-32 + N0*T2 *2^-64 * + N3*T1 *2^0 + N2*T1 *2^-32 + N1*T1 *2^-64 + N0*T1 *2^-96 * + N3*T0 *2^-32 + N2*T0 *2^-64 + N1*T0 *2^-96 + N0*T0 *2^-128 * * Q3=carry Q2=^+carry Q1=^+carry Q0=^+carry fraction=^... * * Q = Q3 *2^96 + Q2 *2^64 + Q1 *2^32 + Q0 *2^0 // 128-bit quotient */ static void ecma_uint128_div10 (ecma_uint128_t *num_p) { /* estimation of reciprocal of 10, 128 bits right of the binary point (T1 == T2) */ const uint64_t tenth_l = 0x9999999aul; const uint64_t tenth_m = 0x99999999ul; const uint64_t tenth_h = 0x19999999ul; const uint64_t l0 = ((uint32_t) num_p->lo) * tenth_l; const uint64_t l1 = (num_p->lo >> 32u) * tenth_l; const uint64_t l2 = ((uint32_t) num_p->hi) * tenth_l; const uint64_t l3 = (num_p->hi >> 32u) * tenth_l; const uint64_t m0 = ((uint32_t) num_p->lo) * tenth_m; const uint64_t m1 = (num_p->lo >> 32u) * tenth_m; const uint64_t m2 = ((uint32_t) num_p->hi) * tenth_m; const uint64_t m3 = (num_p->hi >> 32u) * tenth_m; const uint64_t h0 = ((uint32_t) num_p->lo) * tenth_h; const uint64_t h1 = (num_p->lo >> 32u) * tenth_h; const uint64_t h2 = ((uint32_t) num_p->hi) * tenth_h; const uint64_t h3 = (num_p->hi >> 32u) * tenth_h; uint64_t q0 = l0 >> 32u; q0 += (uint32_t) l1; q0 += (uint32_t) m0; q0 >>= 32u; q0 += l1 >> 32u; q0 += m0 >> 32u; q0 += (uint32_t) l2; q0 += (uint32_t) m1; q0 += (uint32_t) m0; q0 >>= 32u; q0 += l2 >> 32u; q0 += m1 >> 32u; q0 += m0 >> 32u; q0 += (uint32_t) l3; q0 += (uint32_t) m2; q0 += (uint32_t) m1; q0 += (uint32_t) h0; q0 >>= 32u; q0 += l3 >> 32u; q0 += m2 >> 32u; q0 += m1 >> 32u; q0 += h0 >> 32u; q0 += (uint32_t) m3; q0 += (uint32_t) m2; q0 += (uint32_t) h1; uint64_t q1 = q0 >> 32u; q1 += m3 >> 32u; q1 += m2 >> 32u; q1 += h1 >> 32u; q1 += (uint32_t) m3; q1 += (uint32_t) h2; uint64_t q32 = q1 >> 32u; q32 += m3 >> 32u; q32 += h2 >> 32u; q32 += h3; num_p->lo = (q1 << 32u) | ((uint32_t) q0); num_p->hi = q32; } /* ecma_uint128_div10 */ /** * Count leading zeros in a 64-bit integer. The behaviour is undefined for 0. * * @return number of leading zeros. */ inline static int JERRY_ATTR_CONST ecma_uint64_clz (uint64_t n) /**< integer to count leading zeros in */ { #if defined(__GNUC__) || defined(__clang__) return __builtin_clzll (n); #else /* !defined (__GNUC__) && !defined (__clang__) */ JERRY_ASSERT (n != 0); int cnt = 0; uint64_t one = 0x8000000000000000ull; while ((n & one) == 0) { cnt++; one >>= 1; } return cnt; #endif /* !defined (__GNUC__) && !defined (__clang__) */ } /* ecma_uint64_clz */ /** * Count leading zeros in the top 4 bits of a 64-bit integer. * * @return number of leading zeros in top 4 bits. */ inline static int JERRY_ATTR_CONST ecma_uint64_clz_top4 (uint64_t n) /**< integer to count leading zeros in */ { static const uint8_t ecma_uint4_clz[] = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; return ecma_uint4_clz[n >> 60]; } /* ecma_uint64_clz */ /** * Shift required to clear 4 bits of a 64-bit integer. * * @return 0-4 */ inline static int JERRY_ATTR_CONST ecma_uint64_normalize_shift (uint64_t n) /**< integer to count leading zeros in */ { static const uint8_t ecma_uint4_shift[] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 }; return ecma_uint4_shift[n >> 60]; } /* ecma_uint64_normalize_shift */ /** * @} */ /** * Number.MAX_VALUE exponent part when using 64 bit float representation. */ #define NUMBER_MAX_DECIMAL_EXPONENT 308 /** * Number.MIN_VALUE exponent part when using 64 bit float representation. */ #define NUMBER_MIN_DECIMAL_EXPONENT -324 #elif !JERRY_NUMBER_TYPE_FLOAT64 /** * Number.MAX_VALUE exponent part when using 32 bit float representation. */ #define NUMBER_MAX_DECIMAL_EXPONENT 38 /** * Number.MIN_VALUE exponent part when using 32 bit float representation. */ #define NUMBER_MIN_DECIMAL_EXPONENT -45 #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Value of epsilon */ #define EPSILON 0.0000001 /** * ECMA-defined conversion from string to number for different radixes (2, 8, 16). * * See also: * ECMA-262 v5 9.3.1 * ECMA-262 v6 7.1.3.1 * * @return NaN - if the conversion fails * converted number - otherwise */ ecma_number_t ecma_utf8_string_to_number_by_radix (const lit_utf8_byte_t *str_p, /**< utf-8 string */ const lit_utf8_size_t string_size, /**< end of utf-8 string */ uint32_t radix, /**< radix */ uint32_t options) /**< option flags */ { JERRY_ASSERT (radix == 2 || radix == 8 || radix == 16); JERRY_ASSERT (*str_p == LIT_CHAR_0); const lit_utf8_byte_t *end_p = str_p + string_size; /* Skip leading zero */ str_p++; if (radix != 8 || LEXER_TO_ASCII_LOWERCASE (*str_p) == LIT_CHAR_LOWERCASE_O) { /* Skip radix specifier */ str_p++; } ecma_number_t num = ECMA_NUMBER_ZERO; while (str_p < end_p) { lit_utf8_byte_t digit = *str_p++; if (digit == LIT_CHAR_UNDERSCORE && (options & ECMA_CONVERSION_ALLOW_UNDERSCORE)) { continue; } if (!lit_char_is_hex_digit (digit)) { return ecma_number_make_nan (); } uint32_t value = lit_char_hex_to_int (digit); if (value >= radix) { return ecma_number_make_nan (); } num = num * radix + value; } return num; } /* ecma_utf8_string_to_number_by_radix */ /** * ECMA-defined conversion of string to Number. * * See also: * ECMA-262 v5, 9.3.1 * * @return NaN - if the conversion fails * converted number - otherwise */ ecma_number_t ecma_utf8_string_to_number (const lit_utf8_byte_t *str_p, /**< utf-8 string */ lit_utf8_size_t str_size, /**< string size */ uint32_t options) /**< allowing underscore option bit */ { ecma_string_trim_helper (&str_p, &str_size); const lit_utf8_byte_t *end_p = str_p + str_size; if (str_size == 0) { return ECMA_NUMBER_ZERO; } bool sign = false; if (str_p + 2 < end_p && str_p[0] == LIT_CHAR_0) { uint8_t radix = lit_char_to_radix (str_p[1]); if (radix != 10) { return ecma_utf8_string_to_number_by_radix (str_p, str_size, radix, options); } } if (*str_p == LIT_CHAR_PLUS) { str_p++; } else if (*str_p == LIT_CHAR_MINUS) { sign = true; str_p++; } /* Check if string is equal to "Infinity". */ const lit_utf8_byte_t *infinity_str_p = lit_get_magic_string_utf8 (LIT_MAGIC_STRING_INFINITY_UL); const lit_utf8_size_t infinity_length = lit_get_magic_string_size (LIT_MAGIC_STRING_INFINITY_UL); if ((lit_utf8_size_t) (end_p - str_p) == infinity_length && memcmp (infinity_str_p, str_p, infinity_length) == 0) { return ecma_number_make_infinity (sign); } uint64_t significand = 0; uint32_t digit_count = 0; int32_t decimal_exponent = 0; bool has_significand = false; /* Parsing integer part */ while (str_p < end_p) { if (*str_p == LIT_CHAR_UNDERSCORE && (options & ECMA_CONVERSION_ALLOW_UNDERSCORE)) { str_p++; continue; } if (!lit_char_is_decimal_digit (*str_p)) { break; } has_significand = true; uint32_t digit_value = (uint32_t) (*str_p++ - LIT_CHAR_0); if (digit_count == 0 && digit_value == 0) { /* Leading zeros are omitted. */ continue; } if (digit_count >= ECMA_NUMBER_MAX_DIGITS) { decimal_exponent++; continue; } significand = significand * 10 + digit_value; digit_count++; } /* Parse fraction part */ if (str_p < end_p && *str_p == LIT_CHAR_DOT) { str_p++; while (str_p < end_p) { if (*str_p == LIT_CHAR_UNDERSCORE && (options & ECMA_CONVERSION_ALLOW_UNDERSCORE)) { str_p++; continue; } if (!lit_char_is_decimal_digit (*str_p)) { break; } has_significand = true; uint32_t digit_value = (uint32_t) (*str_p++ - LIT_CHAR_0); if (digit_count == 0 && digit_value == 0) { /* Leading zeros are omitted. */ decimal_exponent--; continue; } if (digit_count < ECMA_NUMBER_MAX_DIGITS) { significand = significand * 10 + digit_value; digit_count++; decimal_exponent--; } } } /* Parsing exponent */ if (str_p < end_p && LEXER_TO_ASCII_LOWERCASE (*str_p) == LIT_CHAR_LOWERCASE_E) { str_p++; int32_t exponent = 0; int32_t exponent_sign = 1; if (str_p >= end_p) { return ecma_number_make_nan (); } if (*str_p == LIT_CHAR_PLUS) { str_p++; } else if (*str_p == LIT_CHAR_MINUS) { exponent_sign = -1; str_p++; } if (str_p >= end_p || !lit_char_is_decimal_digit (*str_p)) { return ecma_number_make_nan (); } while (str_p < end_p) { if (*str_p == LIT_CHAR_UNDERSCORE && (options & ECMA_CONVERSION_ALLOW_UNDERSCORE)) { str_p++; continue; } if (!lit_char_is_decimal_digit (*str_p)) { break; } int32_t digit_value = (*str_p++ - LIT_CHAR_0); exponent = exponent * 10 + digit_value; if (exponent_sign * exponent > NUMBER_MAX_DECIMAL_EXPONENT) { return ecma_number_make_infinity (sign); } if (exponent_sign * exponent < NUMBER_MIN_DECIMAL_EXPONENT) { return sign ? -ECMA_NUMBER_ZERO : ECMA_NUMBER_ZERO; } } decimal_exponent += exponent_sign * exponent; } if (!has_significand || str_p < end_p) { return ecma_number_make_nan (); } if (significand == 0) { return sign ? -ECMA_NUMBER_ZERO : ECMA_NUMBER_ZERO; } #if JERRY_NUMBER_TYPE_FLOAT64 /* * 128-bit mantissa storage * * Normalized: |4 bits zero|124-bit mantissa with highest bit set to 1| */ ecma_uint128_t significand_uint128 = { significand, 0 }; /* Normalizing mantissa */ int shift = 4 - ecma_uint64_clz (significand_uint128.hi); if (shift < 0) { ecma_uint128_shift_left (&significand_uint128, -shift); } else { ecma_uint128_shift_right (&significand_uint128, shift); } int32_t binary_exponent = ECMA_NUMBER_FRACTION_WIDTH + shift; while (decimal_exponent > 0) { JERRY_ASSERT (ecma_uint64_clz (significand_uint128.hi) == 4); ecma_uint128_mul10 (&significand_uint128); decimal_exponent--; /* Re-normalizing mantissa */ shift = ecma_uint64_normalize_shift (significand_uint128.hi); JERRY_ASSERT (shift >= 0 && shift <= 4); ecma_uint128_shift_right (&significand_uint128, shift); binary_exponent += shift; } while (decimal_exponent < 0) { /* Denormalizing mantissa, moving highest 1 to bit 127 */ JERRY_ASSERT (ecma_uint64_clz (significand_uint128.hi) <= 4); shift = ecma_uint64_clz_top4 (significand_uint128.hi); JERRY_ASSERT (shift >= 0 && shift <= 4); ecma_uint128_shift_left (&significand_uint128, shift); binary_exponent -= shift; ecma_uint128_div10 (&significand_uint128); decimal_exponent++; } /* * Preparing mantissa for conversion to 52-bit representation, converting it to: * * |11 zero bits|1|116 mantissa bits| */ JERRY_ASSERT (ecma_uint64_clz (significand_uint128.hi) <= 4); shift = 11 - ecma_uint64_clz_top4 (significand_uint128.hi); ecma_uint128_shift_right (&significand_uint128, shift); binary_exponent += shift; JERRY_ASSERT (ecma_uint64_clz (significand_uint128.hi) == 11); binary_exponent += ECMA_NUMBER_EXPONENT_BIAS; /* Handle denormal numbers */ if (binary_exponent < 1) { ecma_uint128_shift_right (&significand_uint128, -binary_exponent + 1); binary_exponent = 0; } significand = ecma_round_high_to_uint64 (&significand_uint128); if (significand >= 1ull << (ECMA_NUMBER_FRACTION_WIDTH + 1)) { /* Rounding carried over to the most significant bit, re-normalize. * No need to shift mantissa right, as the low 52 bits will be 0 regardless. */ binary_exponent++; } if (binary_exponent >= ((1 << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1)) { return ecma_number_make_infinity (sign); } /* Mask low 52 bits. */ significand &= ((1ull << ECMA_NUMBER_FRACTION_WIDTH) - 1); JERRY_ASSERT (binary_exponent < (1 << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1); JERRY_ASSERT (significand < (1ull << ECMA_NUMBER_FRACTION_WIDTH)); return ecma_number_create (sign, (uint32_t) binary_exponent, significand); #elif !JERRY_NUMBER_TYPE_FLOAT64 /* Less precise conversion */ ecma_number_t num = (ecma_number_t) (uint32_t) fraction_uint64; ecma_number_t m = e_sign ? (ecma_number_t) 0.1 : (ecma_number_t) 10.0; while (e) { if (e % 2) { num *= m; } m *= m; e /= 2; } return num; #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ } /* ecma_utf8_string_to_number */ /** * ECMA-defined conversion of UInt32 to String (zero-terminated). * * See also: * ECMA-262 v5, 9.8.1 * * @return number of bytes copied to buffer */ lit_utf8_size_t ecma_uint32_to_utf8_string (uint32_t value, /**< value to convert */ lit_utf8_byte_t *out_buffer_p, /**< buffer for string */ lit_utf8_size_t buffer_size) /**< size of buffer */ { lit_utf8_byte_t *buf_p = out_buffer_p + buffer_size; do { JERRY_ASSERT (buf_p >= out_buffer_p); buf_p--; *buf_p = (lit_utf8_byte_t) ((value % 10) + LIT_CHAR_0); value /= 10; } while (value != 0); JERRY_ASSERT (buf_p >= out_buffer_p); lit_utf8_size_t bytes_copied = (lit_utf8_size_t) (out_buffer_p + buffer_size - buf_p); if (JERRY_LIKELY (buf_p != out_buffer_p)) { memmove (out_buffer_p, buf_p, bytes_copied); } return bytes_copied; } /* ecma_uint32_to_utf8_string */ /** * ECMA-defined conversion of Number value to UInt32 value * * See also: * ECMA-262 v5, 9.6 * * @return 32-bit unsigned integer - result of conversion. */ uint32_t ecma_number_to_uint32 (ecma_number_t num) /**< ecma-number */ { if (JERRY_UNLIKELY (ecma_number_is_zero (num) || !ecma_number_is_finite (num))) { return 0; } const bool sign = ecma_number_is_negative (num); const ecma_number_t abs_num = sign ? -num : num; /* 2 ^ 32 */ const uint64_t uint64_2_pow_32 = (1ull << 32); const ecma_number_t num_2_pow_32 = uint64_2_pow_32; ecma_number_t num_in_uint32_range; if (abs_num >= num_2_pow_32) { num_in_uint32_range = ecma_number_remainder (abs_num, num_2_pow_32); } else { num_in_uint32_range = abs_num; } /* Check that the floating point value can be represented with uint32_t. */ JERRY_ASSERT (num_in_uint32_range < uint64_2_pow_32); uint32_t uint32_num = (uint32_t) num_in_uint32_range; const uint32_t ret = sign ? (uint32_t)(-(int32_t)uint32_num) : uint32_num; #ifndef JERRY_NDEBUG if (sign && uint32_num != 0) { JERRY_ASSERT (ret == uint64_2_pow_32 - uint32_num); } else { JERRY_ASSERT (ret == uint32_num); } #endif /* !JERRY_NDEBUG */ return ret; } /* ecma_number_to_uint32 */ /** * ECMA-defined conversion of Number value to Int32 value * * See also: * ECMA-262 v5, 9.5 * * @return 32-bit signed integer - result of conversion. */ int32_t ecma_number_to_int32 (ecma_number_t num) /**< ecma-number */ { uint32_t uint32_num = ecma_number_to_uint32 (num); /* 2 ^ 32 */ const int64_t int64_2_pow_32 = (1ll << 32); /* 2 ^ 31 */ const uint32_t uint32_2_pow_31 = (1ull << 31); int32_t ret; if (uint32_num >= uint32_2_pow_31) { ret = (int32_t) (uint32_num - int64_2_pow_32); } else { ret = (int32_t) uint32_num; } #ifndef JERRY_NDEBUG int64_t int64_num = uint32_num; JERRY_ASSERT (int64_num >= 0); if (int64_num >= uint32_2_pow_31) { JERRY_ASSERT (ret == int64_num - int64_2_pow_32); } else { JERRY_ASSERT (ret == int64_num); } #endif /* !JERRY_NDEBUG */ return ret; } /* ecma_number_to_int32 */ /** * Perform conversion of ecma-number to decimal representation with decimal exponent. * * Note: * The calculated values correspond to s, n, k parameters in ECMA-262 v5, 9.8.1, item 5: * - parameter out_digits_p corresponds to s, the digits of the number; * - parameter out_decimal_exp_p corresponds to n, the decimal exponent; * - return value corresponds to k, the number of digits. * * @return the number of digits */ lit_utf8_size_t ecma_number_to_decimal (ecma_number_t num, /**< ecma-number */ lit_utf8_byte_t *out_digits_p, /**< [out] buffer to fill with digits */ int32_t *out_decimal_exp_p) /**< [out] decimal exponent */ { JERRY_ASSERT (!ecma_number_is_nan (num)); JERRY_ASSERT (!ecma_number_is_zero (num)); JERRY_ASSERT (!ecma_number_is_infinity (num)); JERRY_ASSERT (!ecma_number_is_negative (num)); return ecma_errol0_dtoa ((double) num, out_digits_p, out_decimal_exp_p); } /* ecma_number_to_decimal */ /** * Convert ecma-number to zero-terminated string * * See also: * ECMA-262 v5, 9.8.1 * * * @return size of utf-8 string */ lit_utf8_size_t ecma_number_to_utf8_string (ecma_number_t num, /**< ecma-number */ lit_utf8_byte_t *buffer_p, /**< buffer for utf-8 string */ lit_utf8_size_t buffer_size) /**< size of buffer */ { lit_utf8_byte_t *dst_p; if (ecma_number_is_nan (num)) { /* 1. */ dst_p = lit_copy_magic_string_to_buffer (LIT_MAGIC_STRING_NAN, buffer_p, buffer_size); return (lit_utf8_size_t) (dst_p - buffer_p); } if (ecma_number_is_zero (num)) { /* 2. */ *buffer_p = LIT_CHAR_0; JERRY_ASSERT (1 <= buffer_size); return 1; } dst_p = buffer_p; if (ecma_number_is_negative (num)) { /* 3. */ *dst_p++ = LIT_CHAR_MINUS; num = -num; } if (ecma_number_is_infinity (num)) { /* 4. */ dst_p = lit_copy_magic_string_to_buffer (LIT_MAGIC_STRING_INFINITY_UL, dst_p, (lit_utf8_size_t) (buffer_p + buffer_size - dst_p)); JERRY_ASSERT (dst_p <= buffer_p + buffer_size); return (lit_utf8_size_t) (dst_p - buffer_p); } /* 5. */ uint32_t num_uint32 = ecma_number_to_uint32 (num); if (((ecma_number_t) num_uint32) == num) { dst_p += ecma_uint32_to_utf8_string (num_uint32, dst_p, (lit_utf8_size_t) (buffer_p + buffer_size - dst_p)); JERRY_ASSERT (dst_p <= buffer_p + buffer_size); return (lit_utf8_size_t) (dst_p - buffer_p); } /* decimal exponent */ int32_t n; /* number of digits in mantissa */ int32_t k; k = (int32_t) ecma_number_to_decimal (num, dst_p, &n); if (k <= n && n <= 21) { /* 6. */ dst_p += k; memset (dst_p, LIT_CHAR_0, (size_t) (n - k)); dst_p += n - k; JERRY_ASSERT (dst_p <= buffer_p + buffer_size); return (lit_utf8_size_t) (dst_p - buffer_p); } if (0 < n && n <= 21) { /* 7. */ memmove (dst_p + n + 1, dst_p + n, (size_t) (k - n)); *(dst_p + n) = LIT_CHAR_DOT; dst_p += k + 1; JERRY_ASSERT (dst_p <= buffer_p + buffer_size); return (lit_utf8_size_t) (dst_p - buffer_p); } if (-6 < n && n <= 0) { /* 8. */ memmove (dst_p + 2 - n, dst_p, (size_t) k); memset (dst_p + 2, LIT_CHAR_0, (size_t) -n); *dst_p = LIT_CHAR_0; *(dst_p + 1) = LIT_CHAR_DOT; dst_p += k - n + 2; JERRY_ASSERT (dst_p <= buffer_p + buffer_size); return (lit_utf8_size_t) (dst_p - buffer_p); } if (k == 1) { /* 9. */ dst_p++; } else { /* 10. */ memmove (dst_p + 2, dst_p + 1, (size_t) (k - 1)); *(dst_p + 1) = LIT_CHAR_DOT; dst_p += k + 1; } /* 9., 10. */ *dst_p++ = LIT_CHAR_LOWERCASE_E; *dst_p++ = (n >= 1) ? LIT_CHAR_PLUS : LIT_CHAR_MINUS; uint32_t t = (uint32_t) (n >= 1 ? (n - 1) : -(n - 1)); dst_p += ecma_uint32_to_utf8_string (t, dst_p, (lit_utf8_size_t) (buffer_p + buffer_size - dst_p)); JERRY_ASSERT (dst_p <= buffer_p + buffer_size); return (lit_utf8_size_t) (dst_p - buffer_p); } /* ecma_number_to_utf8_string */ /** * @} * @} */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-handlers.cpp000664 001750 001750 00000003664 15164251010 050205 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-handlers.h" #include "ecma-globals.h" #include "ecma-iterator-object.h" #include "ecma-promise-object.h" static const ecma_builtin_handler_t ecma_native_handlers[] = { /** @cond doxygen_suppress */ #define ECMA_NATIVE_HANDLER(id, handler, length) handler, #include "ecma-builtin-handlers.inc.h" #undef ECMA_NATIVE_HANDLER /** @endcond */ }; static const uint8_t ecma_native_handler_lengths[] = { /** @cond doxygen_suppress */ #define ECMA_NATIVE_HANDLER(id, handler, length) length, #include "ecma-builtin-handlers.inc.h" #undef ECMA_NATIVE_HANDLER /** @endcond */ }; /** * Get the native handler of a built-in handler type. * * return Function pointer of the handler */ ecma_builtin_handler_t ecma_builtin_handler_get (ecma_native_handler_id_t id) /**< handler id */ { JERRY_ASSERT (id != ECMA_NATIVE_HANDLER_START && id < ECMA_NATIVE_HANDLER__COUNT); return ecma_native_handlers[id - 1]; } /* ecma_builtin_handler_get */ /** * Get the initial 'length' value of a built-in handler type. * * return 'length' value of the handler */ uint8_t ecma_builtin_handler_get_length (ecma_native_handler_id_t id) /**< handler id */ { JERRY_ASSERT (id != ECMA_NATIVE_HANDLER_START && id < ECMA_NATIVE_HANDLER__COUNT); return ecma_native_handler_lengths[id - 1]; } /* ecma_builtin_handler_get_length */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/parser-errors.cpp000664 001750 001750 00000004140 15164251010 044540 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "parser-errors.h" #if JERRY_ERROR_MESSAGES /** * Struct to store parser error message with its size. */ typedef struct { lit_utf8_byte_t *text; /* Text of parser error message. */ uint8_t size; /* Size of parser error message. */ } parser_error_message_t; /* Error message texts with size. */ static parser_error_message_t parser_error_messages[] JERRY_ATTR_CONST_DATA = { { (lit_utf8_byte_t *) "", 0 }, /* PARSER_ERR_EMPTY */ /** @cond doxygen_suppress */ #define PARSER_ERROR_DEF(id, utf8_string) { (lit_utf8_byte_t *) utf8_string, sizeof (utf8_string) - 1 }, #include "parser-error-messages.inc.h" #undef PARSER_ERROR_DEF /** @endcond */ }; #endif /* JERRY_ERROR_MESSAGES */ /** * Get specified parser error as zero-terminated string * * @return pointer to zero-terminated parser error */ const lit_utf8_byte_t * parser_get_error_utf8 (uint32_t id) /**< parser error id */ { JERRY_ASSERT (id < PARSER_ERR_OUT_OF_MEMORY); #if JERRY_ERROR_MESSAGES return parser_error_messages[id].text; #else /* !JERRY_ERROR_MESSAGES */ return NULL; #endif /* JERRY_ERROR_MESSAGES */ } /* parser_get_error_utf8 */ /** * Get size of specified parser error * * @return size in bytes */ lit_utf8_size_t parser_get_error_size (uint32_t id) /**< parser error id */ { JERRY_ASSERT (id < PARSER_ERR_OUT_OF_MEMORY); #if JERRY_ERROR_MESSAGES return parser_error_messages[id].size; #else /* !JERRY_ERROR_MESSAGES */ return 0; #endif /* JERRY_ERROR_MESSAGES */ } /* parser_get_error_size */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test.webp000664 001750 001750 00001523116 15164251010 034055 0ustar00ddennedyddennedy000000 000000 RIFFFWEBPVP8L9/PlI$k {VIff}>$IfnOA7$%D9m4n""W_\I|u]o,6[Ul9TjTM1_Tqu]WUY6݈yw ݻvwU_fۖJ#A[)t˽y0K <}/ӻ>B0 >>nq`|w؋B?|`~봷G0UY)uΗ@O?k}AA>N-[ΫNhQ6K8k74:#(am~yѭ|%áEң[ I~vUՙSt ꄂ"I$hgc 1q AAm7bx x E\.BѶm|HbxxG ;@_.%r3y x`]"6w8qerղ"w|<ܔ$Œ鸻q_r}7_$ 68|p$0$Z$;6sٸq阮>t j Tyto~[?? wFGb ϙBk nH+҈,gcx~ Ԛ Xr[[CbªL1$0Q8-LKvo>+Ԃ]PW%g` ^BA7H(((0y0ϴ~_2YHc $9z ڤt^J((H"B(0S:koؾiV3FΣ8Ź{ LAb\ F)~Xg?۸SGyZ{0/;E%E$H?!DB͠.iה fJLݷ~l׵>y) E-5 ̂`>aXn.i HT{a7잩$]Ve(6:48 y^yR #"HFLCwwΉQlW2Ӕ/xcHH3K8)iޜ{x kg+09nDZϵ޵bu"*F]Q&Oc'$?A"Xzv<|ݻ@ȴVܵj ;4qVAE5 Bv,}z{r.\WW`V88<}3ڤb\O#2Jl[;xg.f!QJze`9 MlevҌ/O |5l~T*(TR&nW_nPn*[$! M6E[Ju.TNrbJ'QCtO9*DE @Ypk95[,Ǵ p!TZ*U I8OeBAOvڰYZDPq]G7l!q^Ҥ.P,>Gq "40a$!V,v/^EO+IWeO8X *I/DA;h[ !O1ڽ}O ռZ'1<{!Ӌ'PQH1a,c`nY'jw}O8+`0̻01F`^o"} ceG=l+Lh 򍜡Lra:QSr0voV_>pɚ2*],8څvޏuFQ8sL>(v53V&NffM~EOLwova{qkE&ư01:zxY(Q9I):Kg|&Tkz ^,e7( [X Nf be- *he)$L|c>{{a\0 Tx\UEaժv8w5v:w3sB@Ľ3/ &Iu$Edҽܵ:CwEtVuUU{zH^{oKUCd;ߖLf0 _~@ILND!h3{OHHzlD`i tV KQ}/ha{cuDHFUcvLC;{OD1˨"IH$QLcTcbedZZI@ PU]0;g=!i-I%Im1{d]iKf\-0`6$jvﺟLOU,e۶+I$.xXw `=(mhW./۶I$YD=#?1 3=4 ߒ$Y$\̣{ =Un*"컶mնmɅ*ǜc> `f9!d˃Gg5Z D`G$I'= g~<3330We%DFIߒ$Y$_Dt{ ep`ÂI{7 <ضH$J1"Tz"n*ֶ)$֏fYfo{`f%f8d>eI]]f*"?m'wOI$I$ID"fQso wfBDoI,IlY"zf}C@O0gmI#2qiQ"L.am-t,5o@2#6d%="+jij=޷jiU3eEd @$I$@DbUs\g=9^"Llko#IKգgV$7UWgfIe#I$}ET~Ƹ;]Uw@$I$@D"5ӾT !?mI$E<"mf1:ƞ`MU ܃oI,Il/Q=۹LWe0{M$IXD-o-[7Wa"B I*T} Hd[ªj\[8LH˸fw'CUf"[$K$⋨yTXr_p7-I%Im1gWZ{O {ҶI$ID"Qsɼ˜YӲSZò0"@/۶ImG$b~R0gLw3UE [$K$bfQ5̞Z[/07Shmˑ$IzU5Ȫl--@ deLUe?1[@Ӷ$jF^Xiz=uƶ UX7ڦHl[Gd͗԰{rL?6Fe|j? {gio۶է$Ms=m{Ԧ㏟ )$I? (T03Ϭw<ӜInf*"?l۶yh۶c@Rr:߯1il$oڶ'dև=2d\ f^+1؃h1cehdUUff]SBub٪y\ͧOgwE ݰee'U4KyE{x[S@zUbO+'?Co'nӟk˟ug=] ؝\%h %NcY^$ZI u|-V'5];?k XҖ>̦47R✲dx3U0H(,z){]B[wae!$H]IU Zw2~Z:'f[K:XU\ $"nH/\+V|\E/䌵E+Jөx{%?U-3Wbqeα 3IkrŰ-2 hXsUFU{dL#C_Mp EY!f!gY{[gsɪKNrs❆-țnғi azx!0B nmVE/J^oΆFՠpN%= MyX27E~ljӲ¢/ϡ9of D1 Jx[_˿lccqZ-a +.^oa=ffhݕyH_ IS\@VQߖBB:q  ;n"(E]|`+ (n+% u]d@w"@Pw"ʆ)2 0W *K+ D EEmQY4[?` N!^ۯU\T@^qT; ܇eD@YU`@m@R|'[7[H\ :"2ܖ "a% ޓ$7jA` !P$n'  Z P9 . J 2 Hp^(oB[FQ (6@tQS(_jK/ m(;`I "4$qT@r$ $Ө0Ӡo'}I7y7$d}&?x\wN$"}}^ c`%*ukW E$o`"bMHϮEIJtw!.\n@%\@!{4 :(A4*C`(HAh2(4;ޒ\EE-mCbҼZsœ|`oE՝ke?MN ׊9`3pa',{R2 )RUϣȈx(-^c =X6٬_9Nb)3 Gmj+NZcoY奔Θ]st%Kd>{2`mʤʕ3giUp?3՜DLCP0NbFyA^f<:/L`B@84y`aҹ3&D}n[fƖlN]}ErTK^Y^dzRF3'VUWSdEuڊ ɵ »<@><,+\\l$<#-*IݍY+ʧ&D\0텹l ~BUd~ѫ/পs1&2YT =%✥ cNFZlWseIiQ -Qlc'!dHw{αI>ʣoќmtC+?5=*^jO,dZ"YuZ Xo"bcZQ'bStq~ʏ ,e-?P-ze!+}w`B?OkeՑ۾ʳӷx 3B&`+YǺsT]U;هXHe")R)gdCruSC(0&(&FLo+:I ]͚l+B=:G:wl4HN65튅lPsA%2j=zp800j5oj%z^3N0gaϒ}a\A6J^GjgIq? R9gAn͟E)KD\|&%}\` '%4_JZ?|uW䘸[;Y˴v,0[KAY!-ڇLmIo]L@h @̭K.TWcE*ɏY>EFxȖ$t?P!ta1`g* fD'**Q1^뼵j}‘jخO%V4ҎwN- sj1Jaݓs֠JR&{8`-#%PxpA)*Q/MBs[Dv#@,hS/#يmNi}xb.xJFZo¿-}`^L^. kkQ ZYwzVI#V3hIĠGf?A]s#t.lJu|W N~axȐ֭&ϩlNIދ2$Ue֦Uvo 򞊧Eܫ%G!BUWnW.~Waooq;&D6I]Y2;/D A*GhjpO5 jtk{D9}%OT932K췦0=v46oo+UѰEl:\bDgKxlg(;5j&Zso>P&~NOL#TTo>rn?ܼ[^c]%~+ ýs$lApV WH׵@sǚC-Df@ao@i7*‚ӼStTZ>P`7x1 Vl6PX0=\M SUK`%дc J?ZF~aW   Fmu?$M KMAU#E(q* yJ8MW+b QuZ>.)ɷ2iV^6ݦbR#g 8Ƃ#6cq$D' ,֧|wIg|R;N:Kyi٥J٣ϝGrę͡Y5N 568iw46@PJp8>b38j"~W}o4{et@TlŔŒLo6f5+_zyQLaEZJseJ;t@O@͜$ }i3oap/pP%|#ƒ,xLŘ_7OKȫEuk:{ b j^WD6RS F>e4˾m[K>f]tإGS̺܄v |IgRH[*'3~yYݙ5a2)|]et;äP\QSkM@ow\^~JR|3Oh*'a_Al?XC-?&:~8}\UU*j ""_nub])c_n!a?'a{6uəOlU<@*vuzXsʕ %1ϬU>:D"zos"fbMxZ6?L/V:QHP6zV+vPDޜ״O^˭}0^6㉓_qW5*xm;2D,킪|hʢhXýH1YYiׯ5ē#LC!Z6 sۋ|DZ61s䩆p{iVˮ>)Rgx0,n*YY"n{H__?lgMHKΜ/ORg>n,%l Z?$CNns-Y5Fjr٘񫨕Oۇ}j9z3žA:,)lZ)岖myMƄVtt's"Z]dE-2J6>>͓ʱilL{ʃ l+˝GvcVJgJ^T|@N`͘I4v3vՙs9huT;$gmKhBf $}ҡ_#4625il,_{jj"TD+"yqalQXH(}CWs]}zGeV`هw1øycnk\yYSAGٙWLOωC _}B-q۩ G3.2f 'КYd fƴ=U\tٍ ׽Z"kmv4kX&@dL?0m0;N"QQQac 0E| +ȫ/P(TiL@ g;x&qO \6vQ2jm[0g0h ]kBBT{sާJm 9ue5Սu764P[Q}{c*:+FsZ\v^_& {\>`$4 Әfrs(P0D>+R=#`;F\]AL/55kH$C^D•U /E)%CfWbChp=UVyp»w*CO<}0U:ޅ %gUogt. ^wvjEYN5l,Z.yίudotmf+d9 AڋR*Hs-&|vxV%Z\K}_%4f Oae~lX%~R>#Of<˹۫T!יUrCn kp!sDcxz_"Ψk!f@pڥ l=ְnoxLu+'} [y"%4]VN5g4' TE"9hJjXM788<۟Z#t<<mM*H ?+sXÁw͗ X;ꄥm"\}Mr/e\12iUܶsJrW"AYS`U``v'=Ȓ༇ 4+`]Wp3̢2ѩs V ;Z!6{I$4[-a۩lE.]^H¯#(Ir *yIu#Oi+o#}.:ㄭּ? |?8ַ U[ ވ@N$yfVb~66=JvKe/]sј?1p壻~$sPYAY{qάɒzΤHpLZOsjNܣ-*y&|VPX$YSPuz=E7*BeY18qLl(3,1Z@Z;HuJ$pGE?Ks>苞b痮d- $b¼Uە鋋@VdD?/AK3~t-J@ h^6bem$odeO>Dx,oXmmAtӊf7kI6zVb&ב_Qq)qδ8~5~8}dV@ y~}T?Н_7_8WHgh/)ҵPNp+ 8)bVTpjE^Llv=)'3|^uEnXϩIhkKfЂ"Г(~T= ų[CyiR!qIxMLsfΩGKJZi|& | SDeFEIv"(Y&vRB&>νcXr4*h!%gxOpoY=*o8 _,19NM@>X(*KD#g!-Iު:Se(:7*3"0-ɛ OW)C*t!S?`ۄ>jG@Q'OHW΅>h0svIojaVGآt'pd`iT/]n)E8R1 |TC1_g짵g?ixxyL.4) T9pϴqyo_pVHH,ҊK(h/m@[[sLv䡏SzS :C Ն^2d>r(ʃ[w!U'QLU!^9:)Ѽx]!#jg#LbC0X0N3ZA 9qI6ܜ"~>&FX?ҍ؞C F )<^fyښxAy֧pH0<#>TrEW\x3([vqCÁĉ8um˫/iyb >^M4S4ʢT ySa0e9fy-^?$ g,ʕ&{DqRůHmeWtY^ -3{BD TzG\̿ sO 7 YɝWy mg)BrHnHW.\eZVŽ,lNza%_%WEZR g9IG:>d'bCUY?_J{Tj@wLKGFl?}>=hKO({:W< KWs=^D!G_1\4ջpm}A4u8g(k6U[afm 3YBaG|m02CQxJ+nyM'[Wo_S$hM]+2#2" Km_|Q|[E{hI)vhcB <̄MhTKl{Ȭ-K &bb*YZOp+T)s@ILSf-wF\T|AS ;r&*)|PϠjynk֩:OJoKeGckjq8<] ߴ)Ӛoi,w?VU;E(.ݕ#wdv:*ho|osuXXϿ2~N~a_%ҽiםZ77`dҪ1~[(yvvogfl8BGJ n tU1lG;o_rGM3E)4`!+6RܳL#78f.Y~ܚ4ɦeo3rT^1(E4gR^4ˋ$z] %9l\> Qv,fMXUW %Ia*8: 4 5e}SdAG 뛩x~88Y2;eTJb/Vu(,rX-PvC:YGw qP(OsZ QKߜA5IsbMt3ק")_ЩÇQ>9W% n)nd{qI#NLdhHAAiO"g.}n3;Ŷ _lO%?N+PmxUPX0y73wLk<7˽2s1ؽ6,|4[!g_.aX4;uo f%?> {|6<͖YI#I4"m=gÂV`͔il1H* =р!%;;N=tm }wjamԭ6  D3I0]~y> u՟ aseE>F7OVIKW{JkTb?%jK,ĒAe񢤌 J7{Ss:̘{ `TK+/kPus$*ۮ)H#iP:И5zS+r3JfAU4E7Z9^Qä";; :,!jQ#P29pHƃl;柮3ëh,XJl05( )3wı`NQZˆAzQJ"s_w||M̴9#$_h%U*RjD]l!U' )2jj5~Tdǧ2G \\wSw8l"#Vi]FBeO{`L'ԑfbe>+nZY/v5wh mGctI/mb?֓ݘ-@ڀ2FQr5Y+v✷NElyҩMaևH?U_1sAbH(rǑxb=ns|#ݐSjٸFے3.#^Sx^ZwafU]>E4Sd0!1(?o!a7HD8est $I]U}$^tgٗqݚZueeiv x`N'& Km2_SVN UP(UU jh1yUټt(]>|cifbZҁT5hV;[,F= ˷i?9 9٬ݏ#N/BM0#ه&k%R2=ȉCs3W*$=\֩ _OI B>ƨj!PSBMBBjV,"HpM( mpbgv343m {o;N,p~sZ7\\-k:ќ6#3DoskW851+E< 8ZR S5SqJ3F;aljdw]%  @֚b WRv]4 牋Uʉlpxò],l#bἬiAbW}*J-M0ݤ^Y<~ bH6'Ub4ڒRrC (k*<WghUlf7OgsS('ʥl_%K(b%m@5!roV@4+*e~l5LWm/g^.og0{02 #DּAdWa.*[e4o9w8VlӢu !?j=FY]e+{Gx9 v?PE!%,5n:nP9 ӟK{Ifc8X'I}tR$D9`=_ ^7TH\ύplKb O0r~2ݠMW HٟieZƋlyBC!zXm3:|&0X?Q9DiF=tKBn1#!6!j% dH2b'Z>x2 z,V^(6Gk6^ul::7:atz)% qg.S<um<~".[&;İ^!J֫T9zxniUz4eQKq|eʆsDurrS> 4KE|]xR.zw#hJ㑱tس-q, K*lCCDžau]2.fЀ+aZwtcӂW ne<3ch~gp9 wTgNcɎZ<өg98~X0^QQ.1F]V6|rJLm2na][ ]bL:)@$*Gv[4mt;>d67a"[Φ=1Vp%S ^> z$O$ wVhZKo]V^fGZٵH$5ĵ. fDɴ?윊>Ꭼ^`ΙfneSVׯPN2s6vŻz!OU$ohWĚK,;lm TuXau68.B!aGr1Sob* Q(N- 5٣D8P-R|: ݔrZK]֛8H'>=m/q)]lm [誧^/tP 䣣Z.r"9>! gnT%Ňw pѸxy^~Iml<$U~*4 ɕs|W/eu,tQ"bu}` [@[T^Q5ojO±72@%QtGe{>ˇ@kdBƫB(LJnjU"wLJ3KݟESmO/8t Vy0?Mm}@]V8 9aPkҸq`',c:.]̛>vk36" C"&;#ג;<[4@ 1mdFVB ΘQh>~K[n'F`ǘuVux`ālk!()Xj^c4tAu I7\ڝW<̃Ed/g\˂qObe6's!olo;vӂ>bc$`PjĹڌv=amC<ꋷ ]J5{Ok8]Xh0 6 3=Ȟ~ϟ͢}ҬZq7=fglj0g=ƤqdDy+gךGsMW]OԺWP9{ϔgk_.JckMfi9R碗ްЉ$\hdu a{O>,H}n}UI| jU E/O9xb𘙯^\:gl﫸Pc4]Ќ<{Kz~4p 85e aVy#tÿ߷06N öŇ+C$,?mІBL:9&0erZ/זV ۮ3UAٴFǨ ɪo*pxU|Y[Hz>+ļ%t{CZFKj!W{ڠf/,Th]xe?ʳnPIPTԖ9MV rGW)f6vOsdAi͌y,7ZCDʇ5[r qn x"[Gm(n遶n/NEDDȎ&?`{ȁ254%;^ULުAf:h CPb{(;iV[xs9mK!LACD0h~9yozk۟֞cZiO[mX5eUĭyu[n9,j V^WNX;3K'&Ƞ(fA[]S^z1施ԃFʣ@Y sX؁Ea1(=4W6R8҈9JÍun,`DoŶsL+cI`yȠ\ n}<2NFAiw}/2E<`:1@ xzv lrSIPGSFO#BIBG}NߍW%5Y{>D_b`8M!myr;v9@* {i$1=nTW$'%Wt-:0@ $,?z??xEЃIRg?8Mt>_Zr1w=,AL fcdHgXBSFW-7l?w/w|gN?^.iAmA+2 +`3j?Q em^/D͆d( Ә c~tsS\ěWN: =}BZݫY@{+k]RB\:E $[o|e^bcP L!.dOc?cw{#|vHCk4`Lya,Z5mP(Gy2#-IHUg[&Đܩs F'爐zHOƘvr$w\4F+u2 [/ \%b;[C||/X6k^x%d u[jR{`u;fu2®[XTymS 7{sv(G? _i;8t;9བg1ȁ4fn >O-T*E5J6\&2p9ػjO4ꬂ; 9cJ nژ J YnTȹe zg=֦bg6GpW4C!H/Gf%u~|/ @ܢ),oj]oD}ދt_9څ9|{5@!a|uBKj㥝r,x$T-> Sɨ J+(Lz2Mɹ¥f+J &3cqq|7O Y,Y0_{|+堲vIpH쇱Ej'_[gD>CI>C :Y=}bGrh}εn+춸ԇ%J9f, o_aWp}y5}ʡ$"tr /<Q`}PԁbX5Q7A9 ɔ'Dɋa>gQ)XjXw(l|ભlh ZuƿB'ǘ'J6`u48bMENfdV[x7/zC,I2M3 I2R7dY vEC#pAi'_q2,Zn $(_"7?+;WD יGsb4o@7-C_:s+A7"jO w ,N&C͆H<8{9˒DCŠ@ڧ>ÌBC8Z&OQ1P >BιnjێݬY4*˦ƠOn{*T=NpƩY-1]Oith &B9UwOOrl!J 5n7%SU2h+0ayOc(S0 [NrKݓwVX859ڞ`M8Gcc̥7_ v6P*,Ǔ*&(kxaGWXx*KJɴM/X\7T8i5}MN.l܃Lg?2;Fij@S+ryT|N;Rg!ϵ9nsT%nMPNãv1{.F2x7˨^2\i{F"==u9w>oxCG h;QOaY]opY4ßdGևOU ܓHZʉnɯGRknqgQ#HZW;9Κu9?m'ZT5s+"Dx~ 7AM5KpWMLu'wj.RVI?V}JP1:WAtgDnŒ-WEߥña KBBk^{,t&VhMBl;Ik)V(yz"T- y7"stة̦9laT0\@r-am}$ߨ>GN[JmPYE1S3"f[3V(?0ȉFF?A!J6/Ac4,oZ.ZM18t*F%F7vadk)>֕a;s(Xϖ6qx"n9}ZʑeMJ'OYg ,8 ch02*olp I 9R_#o^z =r΄(eI+}zh§r3tzK^cxieiZ[XB"B5{*XJ/*YyӍ׳T,,Cmb>eHg  \Ywpl z ި6uPG\|Qo ϸ;MY={LG"= ?` uX-4$o,zS7kyqAb|86N8.6ȮϜԖ"  G&reL]|?C8#y[cL{m-D^#{y#hBZM8 Ǭx.n}'}W9ҵxP &}MSQ<f:Ñ,ҹCG.ir%xc1bETVM#ko5O.4Mv9˗){S!^|w E<!zx6Qb !P1O^)l VM.tJDZ$<5Džz;߃$2F')>x]xIvl4iFC->ckT J?:n`cك h:)"*ތ1fU5麗ч#P :^0ED~hi>miz-lT{*`*cUkCHUqhIk鿏I¿wŦaD'uPc'vr^3[8ȴV5)~i9*w`F@nj!`0Hg9E2 l["E׾S>%'pD-Ͷ7pT;Cu hS 4? "/0PJ›dgTSWyVqK~k :Nah8"LJ|0ճ8X%‡"i `d+nSG-9gKW0En+Q{$#TUkKWy"R,8ÑI8RΆDT8m B,׾y=yӅ=~?GSZl˓5W%#|D-jx$ V>fx $Ua*5?WȦ~|_Em&K $QƦ+tCd[x6}|.i]6 иvPOht/.o$Zxl. yuGaD! cb@O܃-v44"'l P&>'_6R@'Ieup〉m0O[`h?Gs0f*tB[D9P5&4Q`F%M.Dyyj: Gz+bD?A՗_'G`N YX&wFLITC~Q{ .S@Xq."6"y}ôhÕZ#&:QFZ:y.>xtT<&Wޱ;K:) W a(h)* 6J?P1S 3VKݑ)|VuJW\UMTQjmt^Qr*RHYh6U|^{LlAg <&`܎6,7W$XCX@Zy2n{=AȗHZ`9kBJBHhöK ^UJLX'lZ;xӳB#an&K2rh;l@7ڀr2~Z4/hy B3lIêtģSb|j'm8$a5VcEжC`% QjI@ƥw y47%ƛ5PfmLaRh:,$mj*v8\ 8ÝBW9")l(Dc-BȊBXPE^{d 2[Je ݢz{L7RI Qt8j"*dRF2zֳ-KHVji4aZta'AaRN6֜2CљUZХⅲRgeb_agOϺ"# Ch7V8Zu+ [B내wcdEu*Й!5&Ԋln<6ljVk kHy9DYuy؎$bPcGm`pzz&T9';ϯ#M@i{qmgq N#Nv&75}[܄3ג.2eZR^q;j*-PHsB6V RNxB&)=uš9N\J6[`%`^Ăb=̉p|tS>QE9)%pV}Q\~w-]L)Y1ǏCLbx;-F{gP)nA%n/MT]=}BdUC  *}E^` m>՗M+i>,Lp>[*H^Z|z~ B>c. d嶥p\3>o<3FP*kW0Oi=hr]f@F2eywydCX+u 'iNԔAm||7αJ9~' 1"xLy*<8{n3tJWl,cEU^scjد /itK :Ru:Cht_ĢJlaD2YwrA!ls*Ě^ZH/ Z& 78@ ?*]e]=MAQXUU&j"ZQdB)&r5e 6c"2p3Ϋ; y7 \qTD %t%I"H)$>,'%u,`οd<a9[NU57"AJb'NBn#QB1C$sݸ B]ja20¢rڮ~v;+1dw ]b:xyX7_"nF%3:hԼM }űXVSjhn_~E_ yGzLXK$*lK(]FIAE C~zЀ@M+fZdHwccDAE *NS_$#-Ɨ1KDztSlNl7#]+}C-=jI,s=4;/5*Nh[uZ<4/0ä>~fJYy.mшmb8ބ=0adXhf@i*-Y³AAÀ%Au4̲qcjXv{5Ge\6j6HǎrSÔ,4jnK mX~Z"-RuMb򬝕ymzi39u*i hf6rD!ݫ0 hy-GAc HkaW3|Bq4*ӤXvmO{=Uy&-JĤMWE3f,?8ܭ@HJqW_cK^"cK ^'ِ Ykξ*,$C&3AC?pKT`SH%ƣ˧m~u׬юCMÃƥevM%z]7qvEŶS%u??] (nƀiMk+֗qnPMkzXRsȋ܅_K xp#`Ϸr-N}Yp~9}ߨUdJRA ':S=a>R߶#gA=MwXSp]qNgck-Hrz}/s(LΧ agk)^N^^%^ >ح5_3<gVa:_.&QseAQO ܇|~VٰpO9!?Ew?u_`4=*)NxTprT~w%˿=ac9yg$ ,9nj`g&tm p/q].71K?[Ӳou[)J7e b5&B@I=ط]wkXˆс@ݐHV^^nF~aȊm[aX;*BbJyOLԀl7,U |g 1^KodU{ѽc3;: DFCћ}<@3C/m2 ȧ=}~33tqԸ8Y-he|"j[(R[\ә8^¡R~jSf6]ҷ957+6Ζ1)~ :fJrYaBY(Ea;0H- Y ] VM6IY+,nA%ѐk& .yBaGw*rU׏>ozz]&jqweGUQr8n9*DF>8VK8^$,bcoJ?X{vWy/aTrgrg_ > 4Vv*^y_%boGJBVvg$l{%Xe^k8HDz@x Iq.8?VG.(&/ǒkcNԟT|~QN:XTapxNO?VXL/o?bء5W?cT|rk/U|13~ x8U_/onƻ ~U%PWk[䖯]]1n>}:֗\?\k;H9-17>_yqx8 u]_wG/vޤuTgL=~4L%0]hT^Wqz9mREH(ЪbB3KjH7@IJbfCrEZg kؤ'7u9ۊ.)lD'/n5|rK#!fKxآm-AJV*Eu.RFu u&#k3/'s&cXa6Sq[4ڂQFiMD$?X)sj :d8|^zx!SOQ%!t0ekZwJ1ݕ-МK&B#$Vm !pc@:qԝ*"6e*6CQ:/&GV=Րvbk% 65h6٭ֱ&ꂌWPgEDT&|jA?ZrOAUN\:NPXY#K#޻!\T`" 7uh) %oU_qc%l&\#Lfm!FAjE+n̑ + 4ajm>a;OIVD Djz$ڏ9oR)z[GV ui7ڪ&*Lw9ۆtΦծswqHg FPH& 8qS c8yjyZ&{8T^6g[2ǟR3旞QE"8ލ]Es˗=6CV%d;vڮ=Xhl\*T1>#^\ _n~${NVH)xVR=#RSc9B #%yEثɏ- hg̳HN4/`KvcgXVnaemk{E0Ӈ;Ŝ2} nT>Ulw#$#Me&y|v!I5@u{eWW'*[kd. :+(SZbP:tnNF4gE8e { -+e!N[<(yTȴ& *r'fYFew$޶ Jb3v^{Qh\?]Q!Xi%F7x;*q:{r}f׃Ka1KLOJB KlkvꧡT֕&3h[]q*h%Rf 0{MsR\:ӣH8: P$^:ʓy4h@#z"y=ӀFrB?>\/|p"\Tprgg 8F= B5z>%̦EuI>Yƃmd]1,(݊e>oQFL|[$Ax#ta˳Ss_VjC7YV1UK돾1D1y(oڤPR* KK|(W,eE.r7lWpVe>QL:JfF0ܝUj[<" Cz6N@ju0'e~DX2=CU{);(Z|A }QD0* Ɂ8 hAaB9<8,iVVn,nUGFkm}6q TRq+xɃ{N(&t1'vM"eqwMi'k`2iðKHNt! i!aSe&*daZS"ۊ4D 3&μl5(Ö8eƴB6Xd-h3[Tg4X7\YDO sn L(ΘiGD,9*fO G$7w-ғ\0YV}WvSМHe:6׺ hh8jП\j9P$ϼ>0խ\rេ+ggugIGZqN[V9S*1MDB֦]R%}e4%(id@8rq.*\oJ=SzugT2od} +%%m#dzw v @kSvMj8[F|\=[E؂/7/ρͭ&kX:eG|Do1V9Wux,YZ lZ^(`@ uv0#UՃZ<Rbass WM5dm3MJԙ1Qep, `2ymP1A2j 9:=&hw6hWJy*5@ 7OhqEf[GGC4U,L"Bq C\m9b$ӪTx gT0Bh jppu9Wkl|\и)*yiLw3Qo¥ۄpFV,➓^q%,V211 YcguDC]ۅ9d`IL8j!!$?ՉԞ YV(Y݄}>~) `@9ke!PC2ҙr&^E"9@Q: ~eO]uNAb 0OFGmZzQ,VZ4A2Fe)CIU9_iN-F~*j$L{$C ( 9ey E./h|;v!A ]TGIrVd{c/[؞dc[UoאdF-0 \@2@^\]Јp¶Z$s޶Dv̷8oҜ뒞T)_ohb#iWPD7BH ѕ@ f#$R @ K؄]HƜRҭy#JmI j,=jZݿ4nLO_2} '$aOOSA G)¥mwh],H*7"^1O=Uqw%u-gB/ωX B0!Q,F wtQB- 7y!ψI0ۀݓ'3T~B5E x陹_\]d&^%.Nb3~3Ȇg iEoTh79XbI] K٨b\KmA#}eCQf,i+|ݤηT{QWr۵c&Gjiه3k ʚ˱CXPx D j6L|< m_s>j*f诀Pp<(ݓtt%oK=0 >nZZ92VTz8tBvas٘ Y[6t*ii}/Ыv9~(^]^X/KAf(On LpstN\4~ղ'@Ta\ieQ,1CL~Jui{g[ZK7>TdQj[8\2^%ׄi,B=ClvI E;~WΓ`>CQIu.FGB+lQDgS2U@(TGTb "P@:㐂& Yl`@` bgR VkGiK^́׋lS;?/SĚԝ^S4 :u>Jh}W$V\eGc?y<;OQSv;+&KJ1HFkq@H%Z9E\M%cp˾HmR}@ޅ)X0O=97H<޲ՂԦ#.R-5 @攑ZqЯoB)')(OJv]F&gQɞ׺ K#lxE؁[!Wks^BJ`,Y]|U-]hWlu:Uܗ%n3?';]T>?RUoeͅMغ :ݽZ^ٚ3ψvx[Ô;ȴk5fo~'K &3֏#%lIueP}aaɶZ$ARphQlb͕zJosfcC(e| TeoϽjtĈx7OM)4b6ޡr2J YQ$n"i! %XK,3(My=jt3cTu>P $ A҅!0%pD8E+#0 <4úlGq?gMsTG\bhmRMXO5 PF]Y߼fWgu9_N~(t5&oTPe Eq8DfpRDGNj}:+[̣2) "b:yd^7i @L۠Xm./w xsSVzcg"RK2;^aO3]! Uns(2a_b+5AM`qBcc-xh"$n`hqɁ$3$FqC9nq^i#.ydBzW(&1IJw M;H)d:n8M,7Lrʩ1d,L\zEg^:-`G4j)q)WH!lH54ɕ ahT LY(0 Q&Jla@6Vk .`II!QWܪ+X5OZB~5պ~B¸:Ȥ$`9~6kh=MpK"pTE,ȩ3FQ׮eq]NeʨrJ0lΦvG p9#A(+nJ _n>xAXC_5\*uV24"oov<?ʸ0P1:H078Y {N 9bTa XMޅMxB{+ 5%Az& `LuP4؀"HRHcGa&Cy0q;d>֘!&V!9R=Wwg-&!04mP# F`K+l;">)bIdG 7bf)GE7(۪{Eئz8{MD@Y&i3w+ݛg:$QT]}LL 1upR+~et6&{3c]T\pn[qs]pť[(5jVHKqm*V{diGjvO7#HAƲiTYYAC0oh"(0)1}GvGlruj)>r9Ul~+r ^5zuNvƶ{NWAE Mr*KV^A'WG!3!# nl EV(J2~OchT4qkB>5ZnՃ,!tЭ9Yπd,h^=i,гP7($QπHBZjuYFdWbFIrK¤oE!CuCN@#(1[0;GW $6}/^m"sCAϪ0+4ɻ١S:B{kJ@$1Bi\PCj9/5S:%C>?e;TO2e ;%1@Nz[oNYi<dqTt61,&6')%Ȅ4%:rT} O~nFԬ(("|kßP*=+b.b|Jr\$=YYjЧ!Wy|NhoG݃ݰ:ҕjzqc!12d,{ƏJ|`fC~hSiͱx1 _|U 0:ekU0oxIO|i'ܣ˅Z`|$MROr)w^]QK%wIziŬT;3̝)O4h6vZ ѱaJWt'AX6EaEiTJmG@rZ1zC$=2Q &;##Yxx_@Ѩ[(+=: SA6 O)`x+n"Ϲ4L[q)Tv ړl7o,IƱӊE5W9jԸ Nq\fhu7^Z?9^rDʖ)Y؄Z$c8N/)9jFMoqnG OZȤ;'634NލdЊx/".l^,]G'?UzӃSKu:FoPN4͎ _ $뻫Pղ1¤KLS͋uYn4Z^m[wT'_!_VL7t~Dl{y:J8~Dϫ>^NO_m5aiQqi+^ 52+.iϖ{9wW7s_:CN}W`@\ci7p`O'P܈vOuKF_-PNq9wo{S ~p>`qDI{GOឡV P\j H)w)B3$dXgEmd(=ڛޙ![9u0%2]g,?JO\q˔ʜ'o(Fޡ& 1'܀SQWJCuŜ Ҟ{q}]٢ 4QLRS(@$IRw#,,E!sjnk_>o2^hYqwgu%U^ ]haF*]Ekk}D Mi&Q}NQ d0;;hkz$ /<^͌89Owy5KqW% vW(%h jQdn^~0ij_j!IyڙL#  ¨{?3UO\o6_жPⰴWţwdb@>~R&#dE 3j'Jc̽cף&[hIOg#+-.reM!`j0%JR=[`q[Lqz՚ 9u%q0Y`/EUGjNu ~&(5OTD{v){ŢҊ cearί"}Mynw.iih<[;0 >-v-:#E~>j.b(H S%'wOTX0Hb;]U2fcB)<^PWQB!ڼEo\ 7X%&<ڿ\.Ef^`7Z^KagIWLy~*eWQ11"BVs@/4<{]AGK }^O`j<8 a]VN!SK"KSC[f2 ,"b a&lyIqyп{DzvHXKo6U;?k4)cm6_HcpspД 8HgHtXU*^ak e{&uJS){ ~l`q4 "=G'O|wK:P=GQ^> XTŽA5wT'zdH׀+hd4$'x ؖ9)C w@{@}7,mQxrC~!B-c&u󲤝n9?>w,*=?q? 03  3oyFIg7fOn Mh#+9]h( @V)HeʦGא.#B=g4&jyK%WC?V֩E|'F,U&B=YXc<7:dtA-G"vME!Gzˊwq?=yIK]DDpNY!FwKf}?S7]OekCIpH.JE[?c΂@UyLM4ԡEi*qYENX`]DLWiѽt!++0R H~%ZĞM2ߜ3va64;ij+xDi% 3}]\OT)CM})T.R0)EM#´VQԌÄeU0- iy%lڊ0$.h*a.ѥZTUؠ@DQM&fVRF6 ӢBM<<$DUZj_:է/H>6F~n;j0GdAJu+^'/;bU < L-n]v mm$ %kc"el CnjQQgkaYߣ_wkh\#G)]j@VUܽY?@x!R1g/eĵ:BmʪZ" %Df }6QLGB ]kCtsm;W XVj.9!Dֹd$i-sq&:suzRw@ȶp!9.O9B 0tmGVDQWɏ%O+}Y|fe$;W؀cmthZc2DSkd+Vy9 E0,M%6K9VӔNS_}n3nNb~Y-dvT{,ohT0q|c@)AhhگN)`PcKPgT_ 3-u3o}$-hC}\oiQx-2R[ e,- 4 %&Bq[g·OrCM5AmOIl="r5.N \ D|N \W. $R-[!.KBVdz{ǎffH|yt $Z12[XR 2BA>P{Q+Pkj7f]Ȯ)*JLX݂HJ%0USG+xX!ŘgzJ{bKx߷@p|D)ac .#?~xYk5miƻcTuic;Cs _J!M* "9`r{0RG+ G䲖6mG'Hl^g:x2-E` g퓡!kdkPOGFi?6̵ dʲlTfI8R#ݪK;v%q(,#qBW8hʭ-K*Nk!9lF:F-dӒ  b,Q)IMz˔Yg^!>q)+P[ڦ rs'K; Pf2_M_@!e6v`ٳ"MO`w]_u6+!LH+34DӸvsx_X&g^`"a~/#T(Ѷg4qD O>s]h@31&z38˽ x"-Ǜ. /^EаcoAe")z 36u-Qήo8HqVGyco453/B |.tmlEpR/T#^G)k5mR}0AOIrؑ<8$%WtZs Pۇ]3ۧ탑3zZ"I$0_нO,pdj!"k݁glBb#Y*5Ȼ˄';ߦsZRqvce5[2ݓ4(':Tb$d=a-,/ePCBcP= 7UܗѲ(ΐlTՍƆ،l;ZTwQDW\LP'cFكByCZ2/6L`=0;zfƽ$yIr[[^/ԯa^:?.٫W/βYޥ3KhZyX W6Qvлpz_'!`'emu1&14#;f> o=`1ŮwJa4LFRsQ1Gvߞۢ]ZF }@k`Ϧks%~FN6^ +0Ӷ0dI!A%e*9 @\Jw2ؒv"xx@H)'y .ݝsKнCsZ]au Ƅ(DE:,) lP#D)$i%3(Xm6>LJb9^wLt^YN}\pΜ'W|m]7xTX2KTYN"'k*3;&^{Gn|n;;N5wx|`Aƙk סJP7H@N("h.r Ȯ7 ZB]#Fh\ȋjϠʴbBj)y6[ՠ'kDB^$=hhog‰!RS.^ZHE KAiDr8 %!1NWtę/◱|{OԐ N3ɮ?nSBYY{? `EOЁ1VR Ϳ,ck.xL>6%u'}PK5`ᒁ"JF0G %t 3@/h~ڳֵBJ2 ag=6s6'"-*adZ h"h[#/R@ @XZ[xIcI^"+;춃yVz7.g6þ e%M և´AZGhA(eJ5.|6š5]5񦉙P'au!|z*q?ٲpmyW_eE*y.u"xPzRq9բi2b +taT;zQNjp"oekQgY1;<3#O?0E+4&OQR_ŸT:m9d!tp`mIU:3Ka]E@E9sY\k zݐWRC?0Sy;;=bxzL#H*`!1 CD&b6UdakѸŘr[`βi=p0;QBGt_/~j^EEޗ{ RFd4-`C`̭c]q^ٝ%Zl"V|Q_-r8z\ibJ٤D({6g~hDNUoڒ% <&Zh$e4:S:'Xm~*K lt9R{f&r>Iڝ'Rbm1-BzʧJL.8KۧҎj=nM{w$L@+$4 xФmW%MfӞ[[ cSZF$ }nԽN)5xs((TA G!v._>)%c*nPq2gqfOHv8@gJ71|ޓ(TO8v3@X_(&,il.Fg܉M6%JXzV%8~rpg]{9~\* iD%q@ǫ(VT|aJ"ALn5n@R噱J&ݙY漇̝&{=[ Q(4A5ǎe{:;H&냇 iZ\ȍaOj?)C"ک(-&^MäViEǦk9b<琐|>(zŚ$ZȾ sg 6"BJncتF9qMV@`15D, &,qbc,^DAËaZ`kN1GUHH,?j[EҚ v6,U@l >KȤm5#[5g#5ǻ^tAV񲧃E? ,7Y~NF*XPތsʪŐ,L.$j0i2lAH}1qIj5ы薖G/_3d+¡q(- l JQl_uiؤH3y->+OSr(d/yΐ&䫦Z^$6[hNCIڨWȱ {SEk!ғ]Oc&>wd!\() ވm m=Am.xVGb $J> +u\8ndL Vx36GylLJ0k["9|E%E(G~n;8#$Ř^ZĜ$-\ 8 zCU-u;Nir<Ƿ#q\ (CCq[4Ͱrs: pM|B=:JG"^r o n| (#)Ҷ fĝY}t{nF8cTPRAC򆅧ҟᴨ|xRPW+_lOJ͍NO }Ư( =9ABg&`&HI8XR_}r'0u cxuN<*"-{𱥄H6,r5kEHHDSD%͘$mWE`Vu*"VA |PZ _3<;ȗg(pcٱ<@|qvҾզaTxXT&Cc O~CwK1G̴ki(v(tʥԇHadvv 2V~Xr1yUH.3k ʛQ_un`=D*;%9ȫ),l^88HwmY&r!rGw ElVG`OHDdj(5e^.tUIc4X/hb7[~es&2,m(yO͊QE^[j PFaTT)DkE wo+鸅V2f[v~̽փqbP@P3#]6ҽ5S?W RRv=Cіe:ڧʾ&J GtX5e#y)CAe29KF<ڪ=35S 5%`Ә] LԲM&y^T.ԛ5.b(aʜrL#HQ`_1NwYbrcklO/[-Sݪ|Oba M:R \"GX3]NJbY1$[{ɮKo6N4( x=2EU58l͎^ +o=ۅQminhnM`(ayC;q¯uɫ,D+ K#!7a\mgaJe?RV @ 5Zn؅5H[[i)N!W<(3Zgۿ@/2ЎH 0{"Rʠxs% ԃeNcОvKC#9νD ?ѼP3a >7EƱ_KGdpM/BwWgcyV~A`ňQ7 r瞥bڝ} 6aP3/w+Tn"X/fGID %1h5͵D. iFȖ&~DFBU8lHhOXL(bJB`G#fx,Ugu6TGZdO[$*:ׂkS4kEkK",YxG$gy~*K{2yWBHe{UXAp ʢo-+*6uz;㰗)O҇"qo-89@`ba]Xe/WCy 6f%bwarq^x@ ;Y[h,8UrHs4v\eYO*0V|!a?mh9<@IųBW/WXNP??#/OK$wsL1$!UfλgrZ87nsllbZ4wCBq.`<,IG31ռ.AOT-%Yd`($;4hAο5Ԯph jA?sJKt= [K(lAh<+<:IT3ֶF[~f["MtOm} ͩ$Fh)1z`i J #) %^|&lT{Ͻ=4bN\:k~0$Z|1taS(-g0] %6;Wj!!єBHk0 k8e2Q6ߪj2=2HP4nKiMsP pz)܀G@Y?k5nCA6#))N 1p1.y}~'ʼn&Q%ר_*?$%Ҭ>wusN:4yRyp4g'H(7rrt;g)D_^JM`{Dt_K/nLm=7qr_9x<#VZEMl]$( 0m'Xofi~8I]="rF1,=KfY'Q@Uݥ1J#j!JD=qގjK83睌Sتkkq r;[0>HRڜ'yEs-_5:ĸ}ϗ=Gl2v)ՠd1VTc՘MNɜUЬȒ"KվF]n!Ș2MOHL).s`qm)>6ezat;DA l]  nP͟ĦVn=IMҍcvH |2!OKA8.xw22yRN}k8 9V?ʃPt3zF*<0&.pbeNWSMk}uBD:p&z_' p΅[SLY5JrJщ꣣Co~V=P)X#$>A..miPԡAeOq%fU:ZM{ܡ:\ئJ{t">MmC jx~|Cx>A\gI9#θ ɝzfuID$"44XL:OM  d_& &E+KOX@52nnd$dB3(?9|8XnO]%G%"Eevn~ȟ c+A:k 4sfXq>lLB%־]䯟e[FdчLoaṙ[Q:QP@ݪ=M#t#-8ͺ/VCc1.F -pK՘}6¡ЛN\#nL.Zh# I+ l=;aqYD@5y>d|t *Z}QkY(a]>G{/I2(PSA-ȴ. ?bLcc瘶VgWZ ^d.bn@ONSզԙ$ܢ Խwg#@YG%m}z‡"^uF><̀ɑ:8x\.[:Y2뜩d%ppF@:?8 =&e.\% rzJ.x4؛" ٗ"SLOKl g=F2Wk~?߿o\4I&x)4y z'iAa<`F- WUO `ngrIX1iG;3;`I9oJTޏُ5BPV5)^xka~?g"^LA A<[BS,#YʷۉryF|U~M/0X|AY7v5]blbxfVo#K@0my 1]j__oڶwM1|sgͮ>q.[㓛_c `96~e~0p{| :e -5tTEcRP\'eS->vn2%I4|W?_DwHG7֠\Y )-`ͦ\@/L3ĞjC9]&(|@Zrf(Pp>@Fٯ{[l0C>?@ O)fw+|V+y^{)(vC-7 'd.Z$7 udn,Bx-;K|L_yuVQOUR_VSK;o)C.#]s/ nב<^t\c"6橋S[wWeor[xSXˏOc_U oɔ*GkU%ydS- c"wdKP{%J >:nlsefBȬ.W3'F]tB21y;lƲ[c@V)[d+hGq]$$,8j0ST-@'$ zIf>@ vo~n㊆~3Yow4c~ /.f3Irj'?r(}WW|U[vsK2#H7t ^rӻ6@gY ⹞q(X:k.+Ӏ0 4"DeE@40 [~\Iv|?Q[rӑQOe01ZI- &[m &<d"#ʷ4B֎sh% U:"{lO%|yxVfyK2G8>ԅCQ}ʕkao/s Q! /ٶKxՒwnDE8@X]l88)KBݯt 5k,6ND _楷 f2·nlO)X-DX;t#'wi?Bm[ %x.H}cG6&Q|]U ߉^H ־;U~qf4LV\fUD$5+x7ngIQ `h^ºf}|YY*j`gJ{-L^zQpݨRpe=J@j$DO#§{:W C-gJ*՞ky`C#6wC>0>ҘŊ\Y+fnۂTAO&"!`ΒM+3*/hm2jefڊܛ Lm~L5PN Iy9NVR ş'EsRgBױ/MIZn5j]ޙ2xɑ}Mn =8VSJ RAM7~,HY CT |聣4Dէwf`dxVS1e1A>j5YMQFcڑxγr?ƥ5-$T4&G?y !t"R2MzHr̊Y:rG`e=VM\EBE[SuK84r[-B}QHܲHb9o0/X}Y>3W{?SoIOBLlkCCdj$nf}PQ~'u{IZ:\V3jIQԷIjww\\~'b>$~ %r:+O.YPvolNϳ§'Fb6꺛[_۩jѲD%aU /ǣ^K48C ..Jn:ָo&@m}hc?صoT"<ގi a?Orj1ƣ:驻2~D-U+6M;@^/Sv>U6*}mp?r|P$|;Bgz?D XP`gVhMRw?.k;I4EV(@&da0jJ^k⚴G!(%D ]cu2{%'0j/oҧm* Aם*[:dd#Xr i/ƅm [AXVG#PZݝK7 ؓ*1)S` H/6@{%vYz{O@#IH(!ֲR‚@.8#6+Z^J4mE ^$EÍuZG^+y7{ْmZ™PlEn&3٧q =pfHQQcM 1%<"7S0b4A*]Mj#8 tD  : PTVl<A $6*?1Hrq@csEf/+љWK>yZH8 }mӟNKz^ZhOeO#G~2xsf%a [A WvꝚzfS&>O]ACGv"Y${@#!%!d`yƧCzrk-وM83\bvY 0>]H_zx'N6g,KIm}UL<ݤoo(Cߖ`w>Eևf3f<&K|+[Lw}VŒhGO ,*Mx?[/yC#, +A! m5q{{ҳ$amYRx?&"q11ˁxYTGa%8dw3DhܢA$!L?/"ʑ:Jld- У eF5:SzJq'[/CёL)ʙd xJhGUu}:-XnEQpԩYlFQGV5:T;@A֚[~UƱj&]{&Q44*Va85^bDvVgP:FܝΊ)O/THfTI|Z,o9AVpu$%x8؁c\+g)CetH :;WB?ݴLAsޠB*[ J #M.V2*;?b^|@c& ~a`!Q)أ~+^|F^Q8'Ķq/<.reFrV9/acX]EXa/|{= /+Kȁ٫h: GmG\e߱U-g(u͆iK)$u,9Lݎ@8Zd[Dǯ0w]<{-tVM{MBqW ]oVo*h՝k M9Bh+T%J|ӦěYid0MV<佣g@ך8g 5\wvXޖ R{~,VchЯo1sm-miaTF/ ^jU'h&%~nw "k `-vhwQbr۬H"H4me.q&$2ct%=IKxΊaѹs~Plb%B\c5#@Q?JfW%V\L0vLr qRY)Ȳn?B6<.8 :͹G/`>= ;G{"}D:졂Ѯ gmR4oe->4~Xcc tJBLL VYĊ%LӢ'bű>A*e8 [gѕG en}a }t#C)㠻dvV?Ÿ;'JQ>tBtxחNQINf 㾷Vhjc5';^YZ̍j"KX~W;N3Vi/{W)rARSE&G_=>fzcy7OۗVd֊C ,B2JD.oC~ Re&;̗Z=[cf^ZdMjlόpyZz&d9f'_k΍?Ptn]OuAθy a߲i~+x:(3&ؑ*[l6R̤Xt281ϑ%{mI'P3T p-vJI\KI|=ҳ@;l퍞ZֳA78! ɥVgf1adMYiM|hˎoӣ\SQI2%%JN$RI"̛͎IԎ`H9 8}j\{(pl~7_I;3u+=/'Es:r=5BElރh&}4_OVcoO?-{y(.-v?|(5WP/h7.:CeFźrbQf.f~pIA)+*8Yߏ"p,1 kzJWL) Ƥz5KVuE81bd͗YXxz sU&&Aly޺rXۭ!M; ŠXw"J[;Q.eNOėX aJѷv̸~~B_ޚ^ 3'1*5I-v?oEQdUe@7ܳ9΢֤מs}}ouэ5Y^ ZY@ XcݔnFQ%%Vbd]N8 74*mp8zgH"??ubQ}(iZ^6Nh⍎ywq#e+^ 5Ƴ7qYWs } jx(5==zA}Go/"w re}9AJ.op~b ;QzyUT3eer9+nu<~YD .%ejh~?s[U `lBx><ӅBosbUO0Nl2.=(vXЧؖ.˸[ Q:RР sr2sVOFա⃏y,ުyk-Hnþt̨9L^j?ލLm/zg\ZePCcM =;l3ܘ2ZqӪwiU*˫,_Z,85@x>B],:j0֒.J}`>ڋcV0aS9$)$zn?ﻱe. pma5yjϩ4+%tJ ,.6b)& **@<˥7?[޴r4gh _>x.ٝۛ[l?ȶ)ɱVuKc2GUJpXl<֝ʚ*.U(RɠKd-A^y@işWhAV,$+^0&)R)yF!‰h-ܬP2cc *q t*l"[w&JuaOC -3,eрjPRxMz7o!I<BR(AP8p(vwl O]k15[`w<{aljcP\dE ~oL-G?l' ~#j,jAj}EsSqvtPfl%[#+r xBJ(nmT~&$3H$/)oi=PB;ʱdPx3]:Kgϑ?+ViuKC.>thVc(W-ཎ) YէV9ܖgaAK05l(re,[#*PTPlG׬@׷6g?xK8WLUWW$ ͽAQ$[k:  >V2 /~73mKW,9L;%_e?aEpCg1I8OwY*6"qƾ ~iPoisX"|~F*XNg,\^]p$󶆧cc/Z+L%ӯ|A_Z% `ORDauyD ؔ7uCCh!r`.<8'$AD^ù \x`Dd $< q#ס\˪'=|>h14_}GnK;"Oﱁ)dAk1Q j_J։*jC9sVR 'SZIQLxhPsռzӚѫ6CSٿ9D+U[7E$??r$HN0"it[a:6^F녱APO9f_HI A^%aI.P>-g[5BG#S]Xz]r0gūp@#՞$/I8K 9aSl?.Z25$x`ЋSᡶ[Û͛F?.M jax>cOj%b6ĹCMT\.@b5B~C`kmxg\ TzVl.2_@Ԣy>jFx(IO(O<QޚNLQ)Q4~]QԗH5* @5w}؉2yDaJB#y)$ΒV8Nsi붉W@"gVLRڠ|4Rg'f*.oX= @{B$ТtYn{=bDAfY3LwFV4Ïpz.CԞ}Tka {oL O$6|Y{ww e'YdRS3Mo#}6ia>㏔b:M"jA=칂Ȟ9SI9u7zs n9+Z$[J.%Xq/͐p*M\, 1] 2 VC 0q2 e"!c iŁi[UNr-406&掎 uy2Pdt-[EbL_[ieeK!VFW+$3(P(DqblXbCtDJBFQ(>A-tEfHFFi,_IU7RC2jQA8sB /&CF {yqs-pbkM"SMg4vY PE@=J!K8W v*\E0Yz]?5uo^dw@(fdՙ^ c-xfK L@Q]'` .u瞗rTQɗ}HοBLK`|5ry Z*\LkpIX*û(u^L䐉g\[bQl;,-]VR?IEDvQ14صssv7,fcOV$pqf& ȡ|ӽ#Qe{T&<C(O_m5-mcBN5چIrlmO?e\~KkqapPf/lK:&+y=s1Vk!$ʠbHD$ocΪ˝)[ 3)@|Kfנּ}(vĕw);tnbZܧAAE5[f>!MT(44i_;/o5E\+ra 2}Prc%I]us[zwD\7t#cF-jh ւ4f#lGƅQt?AWnA مy?yX1\KoSTQ8;,J6.8CH=B3;zSLd*\l` HB>L[ J.m5Ѭ|k۸?5Ky6|uDm7=ˆ|/RGP Gw͖QȜa)(Жո CiM@̷߮4[+JOm?ZC~.N¶CݼՁVkI]b\\h/!!sIW$=Ug4 9qou+k-rQc_,J=T3怈ظ(|hg.'b&yef[66F-}k:M RHOk?b||)feذ;ӂH.VաKjsFr1./ [P̩Jed/P b+0>Rn1 ""D#҉W޳]Lj0HB<~:o+gbs:C#א8X׳' ?fM.<|_N~;FX*m*B3q|Vv\.SÿyT ?wr!9q (H}kR>Xe|yzѦY>?9-v ̶ ɱ|lgp`}YN@\PpצhY Ʊ醈zCP0LQ qNF֟+/yF^;/ytqn.%nG; _L?kIqOkfCZ>|Xo}Kn*=vF <8e71IZF"_k#e-~zqtA}fM,HBX4˸Ѭɋ߮æBGzVTfqBjP N#6ĵW=YL%m> U`”EL A)YH4+.0\]dF\`Sy8!Q*@{}6Fau`k ڔTЕupksisO R\|zuw@exWt >$E{C=0+c!<6ST&zNJ,ZEY\d&'l4R{~-5GsTbV\M_Z9jܽNJ"ه252MK4e}1rQ#"We댲B/*l?2 ;&@ Q钚<?zz |>T1*|HA$,F;~`vR3!qz$G yBVp80W;&JYdj@ W}vo9ol:ݵiQ[E|2(K0|Y \YRH~#zw%ź*nZ^ݼk1wOp>1؉{fl-4.gYbLZV`/u9bVP1yIkȔ F\iZ:5+#gL{־ ^JP)k t*m8gC`HKsӨG km$/"8ty~& N>wZ":> \/d!STHda"}';r@=J4USl8̛^%"#T, /#+,r_C6ƕ @&ln]vtSzbGT_05Hn?d@-xg5i}2!Cy/%VsLu^- B!dy l5rɥKZr8g=:~DIk} R(k0l&1Cw%GEG8ߟAv92 awGI&iGt#BVl17ҧr(/3>~߿#w\Ê/-؊&JjBBbLIN , wn-tkYXn);r?XA` ?N襂OJXj>To<K.:dcp!ys&}+00C\t@N?v)؇x <ڧ7+RsiqA ˉ=cOd`$ c 3MG<48̻m#^s 鈋2yal*'x`CdF{4HCsodH12#' :$*u6-4Rf}ɥ}UtK9,?w1;Q)ttFuHǯGN5RXl8?p׼#By?~.+,JV @gmhT'A.om^%imlyB[V6)Q#kd33I^>$  Ð3?xr" eD8^qq٢ UӘdVpLcW$CO1!rE/,XG5{/{˟U=TݖbKΏ_w\@3Nr5^?6/ީBP7w<?]"E{w_n&ŌQou[ f]bvRr*˶8kRvhs+;=g{x6[2Iv~Umh͈}QJsE]OG܁<a{9,GFZ }zӱdЦVwy ^{ Hei騅9Ҏt}i;;@{m^?s^:~?NW+-/r6"Zv:n,[aȣK{=DnNj|^!6!UjxOv f{MhюƧQN-&옾s$?G1mkta4je Xv47C2$y6**zާs*y}ϔX6\wʩnYa!ElOTnCqU9 ;v|7 dĐ Xm|+Hao< :O#gk<χǮS%w"Fuh>-ҵ\ I}iT9KȯDwuZUC5ґcV*H± ڬNWz69srIwڞm u<3J]{0]- S^(˵\V#TK`#OW9*M W(W1ӭ]2DPa ]_yZ}TԄN2/LJSݴ g|Q_j!8d:b"PЊM;g"W%>IvQx4ZLs,/^ZCk&l4z[<)s0J_a]k:;_xY/WZ~ Vػ /CEM gEݢi=ȨJqu7##S\U >³f/ $;q2OL[J3^~Ssne$*Ӂf7iXdO؈0'U,3 Dr,y(z{x 2lp;kսƬi4"0[`#@>4 w=e9R6jl/#V67n%!nl+%lN-"96NEFDN>(eٺ$;cJ itD-I{wYoT,4Bp7x- 4Nc7vz`w"ĵݳDBQ1Xoȝ<#d!?|_ +*b^d@ 11um/gn:j* _) V3PDS(+!cUЍS6:p7[D"AzXp2;5(ZΑ$:5)0V-G4hDhݪ0/щ֌3z>+`)aaWr鿴"7ʼisn.^a K-{Z K,Ϫ5x"(lGb08坊qqcE!WkKٌ8i* nri(ۭ3ZIsPV;UG5O΀,5ZY' 9Wo{<`4~mҸK#Hգ${2bn'f&lʩ5t8LSW T'yWt2x]zop4 by"GHaDnr#Hq/W2Eu k?#-$af!}"R,>:Pgg`]ܵӷMi0^j^uB"|}8Mۅ5BHm_﫲S%u<0ȵxdD<AyNdzt^:Pm[Sv:71%*jbZCAUSѤa d@\kfy}:hg* yF )41GIJSTQxȁQzzy%8#=mW:ځ¤*vs@#dqq70zseǺšaHk=Co/I!ZZբzօ}Jy!8*޶^.gD4xȣ B)*@`6>ѭh*P\ kG37$"TvoMA4w٠QԆKR>z9ז$Y#-bdh8swww1Y^6<zi7쒬DJ\9"ܘF@O&;0}ܨF+ -sqq{Zlwk1de T~.m:qKəzw4cLyQG3&^LʴaJj}͊ylqcΤflL-'4ڇM#],/Y Wfo},1g{d m9?(UkmQjs W,!Gp3n :@VuC#ca!bߑ;-%ĺP%wF*Z[/Km5ΐ컥?y;&>oYanv," H y2qS4k=_eo3n CO-"=jxjz sR4Tr@wDybp&\h\i,`1J^Rdqxc }W[7%*s[FWpHļW MX!sOao@~bA%cŢ5vbbLPfdUEY()Q\[ u@|8ևɡ +%Ԕ{vZ ӊS\V*u dlK6H[6jUCA8qd[M8 LvGD.-QE޺ReExxIԍS+蒞c^jcFA{lT;phRf2W_!6e-MIfC9P&}%9>t>̪"II\yPg:h6N.6mdOȱX,OSN h[ ;IK]d Ld MBDY2c|f+]"t]|ꚍLHȀKx+Nb`Ί7L: K5;5ű>^?xovYwNuiq`qE-2Sif K{[< Spxƌ_Z,Im6Bf~<PdCch6=!#m?}ہ#|2IL{ˆluA [BصGgA}'l8lBek zzx|3ⶔִP DЮ3  7AG 6Jbr[D rȂ xPO =ͺL8dҔ뇡fK¬\?R6s 0g[f gY/CҜkdK}ln+6v\q_ū1cJvO0E"NS mݐIS9 G(v\lM[RM쁻G"lv@ͨx5#nؿC7r ;8@*D)+]Km"r1IQf3{guT ;~ ~"q>8sΕC14}yU%dNxBp&o8x/e'ֲ=-`$2X޹o p\7KI9Mg "_6rEMVC?]ޟr֬GO9"UگVo$ olڗLl]쬮o[B"EuG\#3YXf-KS" 1s 4nF"Cحl$!h/fFBFYBL+7W*Am@vĆb!!\ͤxdم޷+ql נ |z 'CХ'h]qX/V]cQzk cPPx5@5p6[P)R91s$BKxn Y.] & x 0qRߔCUY27~ߜ-$19;L*MWˢK_x<%(q۵'r1h H Ie|ܫ8v 췴$DO' ֪8|#>r84Zz G2TIJSm屈 mP"w (6!$< TƇg\%Fq,xE}{څ7|/Jt6mi<=쮃3]C7ef{?$Hh\dS9{1&!'GxK :Kəb=@rS T":QvƵc/<約9`mlP,},H/goGhswMIjhs@ o+UvQ! -(:Η}*1G!OC@37$9U,T쿊 O8г.4&StPhm6=@\hq5>R&[uvERp1ao<&g3~Dkp0 $`U@JHN/qS=|RpKs''Y)>\~(S*%R95 d7څBA5[C0 TakBj,8z! N;!nzFJ5ǨE~U|R!x~9(+uvG``̛$_|(޿xB*Laލ[UKɣ7GȀs ]`.Ǭ:[qEl<&E@ :MI=_n}W(!ƕsΓr;# ~aQ^R8S”)&pޱ]q[GFj{OilɓFoͷs M'aE*Rz˖V'_x+t趭g.)~a˞7GզضJ 1qkk[VvoݽɱI// \waY=iﻶȒԨxm :|U7~eXc:2xڞze͐p8uqH6="tޭ qhgBDZϙEnPld`vg_7gg 䥆Pk})* 4)ai>--h\yud+QU,e )dAqSWѠN=zSv9X[$׋"4| iY( nuܷS!F%)۹2H=&輋bJ dқFiZO|㋖)uoC dN:a1phYe;@q!GY/y~{OEt%1cO2];ߣns8N}AGQWKU@A5x9̉|f`J!dw:K͑tm#fm8m/ i<C\OA)+q6%2njnuwZUtp8 R_ g8*ۛw4>|b|jUL̰]h Č%tRS;k;U:Gy;"YSA J] /[-9ɥY-$Ӵ\G$O7qm[=_\\6pފA6rS`L29@eA eVt}I4MkGmQQ[}5Fiu֝U?Z_5.Eņ2 oTySֲe–%s)_R62~tjnK jQt^hyk! `̽Dm`=UԽ܎U xs`\(VA# D$R]se.>pxCS%B4' ,QIe,jMK~//#y0!>%'-{KgU4M7iFfJdZjWR^.LJSvv>@&+q$|";F6=!IwLQC=K09d@{P+}xsPHր987,P2prv)D~!>2Cj W&J~p$6W0P}4 U!D26R9MJU(p"O鱦2PqrG9CyKDLyRfgy"8[ x$ckz+bxS=hd@ KB`~:oZc'ĕte7N3c+@nP݋yrI\?Ci?Dkh+_]C]Օcm*M/)-d93u!Mqnt^.Q=ƳIrg|5Beʏ<[(.̎_YmqDM+@7# W*Eʄ]S+(So{idj?^0O}$ks!ȔV0>0Wƀ ԃAKW}=5PP 4`b*;t e@{[r: D SES8?,[F_HNlڍ%4iDxߠ#Il dѰڀ$te0 Zk0 sEhP%)&"+y?sy|MO3{'ڟڲ!RaQ0/0*w1dru/g HP /^_U 73j -Q$Y͛D*&>/vGCGZf#h`Pwu R&HFBWv )`rL!'zU&0`_#/@{E!ڭҰ6_vhpT>e 8r`O1'iDliZĊ̭Ո8G{$,:P͠x`%u򶍠{E%hXXIrng$QVU<8 p 'ۄS]lnp{?;)]onrSZ- 1 ?oJ1]'f|MBɶ#DDvcؒ -~6 a]=Ƈv߭&HuPYeϻ5i&i!)%;}vyjAS=XG By␃?-rh^@+a@T_VRG聗xeBE;J{!%M ŗ>&!c Leq̡!tk#-Yx)9uY#X?_>C5tg:OKnIJHǸybQ*T"-V TJ%`90 cFeRu,x&|L$j/qTٍX/TĪ 5L"+2eQLpр`3x'8jtTs0vc?L"*?]>|P."` e Y.E'XA+w PmwOږ,kX0 -CaצUdƒC#.kS=<CY6æ[ew|B|J6ޥ/{83BH*y1ŹIEK6|7ly~^,CLYN&Z 0B f? mҪ"jBlߖoQӧ_m5%$/:Ɇ2ɢQɋ}!+_^C\W!cEme/W$4ĭKT~nywnZ$7xv7/-Y6Tk9o (v}l.b(P4k?4w3V½ӣTrBxN3ǑJZ3kNtMgąZ}.'q'ĩ9=F=]!Q"զarBR1&R!S)p&E(hEsyؚ!Tv_pV![L&(c7:IfGt|FA6")\vy ;Rlc2 !>^(fRouzz>xe#,/-U{ҕ$;l? VouӾihrI}I1Lؿc鯕Wnjd ),THUP*F0DM,fns-Pӳ=B`H(jyLnO͆?FCkqWȗ9Xɔj&/M'@^8ʷ~)F.h YQ>N\Q:QDCfFXV]hj-}8SCZв|2ݷKy NT\ul/z  i@LR)TQg&OD (7& }xu~Ґ9~u2l5CGh$=@6gڊ22r<:ޏ:=J[w|]by¹QߺV$\Kf:%Q+L>o4ե\--˔J4]Oá9mS&lVG>gPhz-,!!;m &$>3nЗxX [@֥:90mok^.9rC%RAMsm:SwkxB09>r2}g3 m?W>-_ 7T/Dq)V,mИ68P`JW|__~𑬕b=;;}W:"ZHs&%"-c/oL{w CT'PySjl-k&﹗ Үrzɘ)$i[p$7br: C4P:Vaa/ā9(hon` I.K'd7zu}ò^nQi˥6<Ķ]Zh,5;oqӬE -@*<#W[lղ᛻cg< - +B$}Yr;1pUv+/83w=80 #]?w^ն10{/.Qp ۺJ73hY b9v}8̓Q^ʃ,?y֋l(]է"eTtƬ)9ZgFIuLra,jV7V 3ƠC۳+|zWge%/(GQFۚ |:PEtZ?1dP[Ψ7aCt-Q,])Nii[ZT8hF%t[@A$em!(AL~gf/DmwшR+G#+NjC`(>XZv4w jlq# N;Ɂ9ugT.3jLe 2E9PRB^[׉f|(I)v_\OB/_o~c-6O[r`?S?;\>ό^pdB5&HJS欒;(ͅ-Eϰ6L%G;@$֬< `1x7 B GM034iZ8QbGoW'u~}M'Lq .SzsWha!RD=hb sƠ&֩t_) ]a@B>]0mI9~&z#YDg(8DNȒY0{YF2;;Ѥ/w8xN"3ܞ ձ%9UaOV9I6w[xDy2qp@JR#d)>nYAZڅWlHgsZX΃HlXj?|AJ>kҞe y$<HJgmʗы_?bM5Z? Fw3pv֨YؤSɯ6 ^v)8|,ړܵ狲|>=X_c[b/Zq4ˏU:фXl~ϙdwdWS}O}Syl±acS~41mvj ֑CV1%eteO:k(iN`L/)//:mVHawC* j;ַ"ٯks4Ydj U}#IbY;I$Mft?seԹg?~-U.fxr7CF?Nټ# 6e<4uIGÖM]kcgx+]smz.}+aXOzXqFWh˧-7Ai5t<ѯ 2{_2 "/,yݑM0ߕ&1)KU( M"Q[瓠8?oZࣰ3RO*][`=mO!8(jb/-h7:f#ԝ:eԬđMB[k~&Rc%ਢ >]IMϮ;5pɷިB2_amTp5_</]'1UU^%B.DQK`7ѓc7 "8EO+W EKO(%%08"luJe2>"/UIAZ+s;V̹a|.J&x}|Bm ?bXyDKbJ>ݯ2C 5igt B41CX:ctt׋y=~~̻c* )lɤ̓Hq!!&)4Ț7h4Oqu O!qVws߲brRAǃZr4,ml/Բ/]j?@]ŷPtCq߫'1zBmY?cqHj\zI73^><{in¹Ch)bx&R~p /hilFn0qG'yt.F,K©}3}K2T]v׫FiԷBLPt'@AJaU}نՖ &-fTomw5!{(ۛH>OФܳ#5N0ZꚊDy;z.uk,qy8APOԼWi+d:ՔcH 8pF+S;8^ǜESV@V 1a5娱i*8^U'YèW΅pjvV JQ).ߤgȵl~?=z:O_8IjrsU8Xmt]jJI4谶P`mH.Rb R<g>!b;&uBlP,7"jRԀ}$ո fCu1;PݾO$iAxl _7N&jiz ̴Tv6Di &2"0 p4*=L3)-`08OP4i:lbĀR\j%~;d ]=‚*, riy|ɚ3L#8*}O*\lËq)o>/UO}Y_ʮT}0/Jx0r}zj1uwǓgi.4kIwawt2] R9IehT+!`D3z%@xKmmBn5fu7y(SJʞ ѥz[ ,j}&1.^3RKʅ1%F1Bm38ʧ_W5Cg ڳ7iI!QOz!+K_\:]4 &*r;x"ӝw*g*6%Yu煕v~͖>zL*A4tA1to1(%rUܑ  gӁ^(oG/޹@ V(*'٭Tmwz{H.>5_)&{rbGX'ըι".~s> /NW*L+f,f po3G_ݱ_Zqp?U˺g[UoűҽV$L }Zƌ7H>夕E_JN3GGلDք@e۷įܧV1=FN,z"z=}q^Be*bDy&x YP32S#`~d\r2G|y]H`:;ZSY#Er &g#TC%g씭4}e<Ǚ\EFJjIϷrb$] ϊzScSr"[&Bb 5$5C|b+إ5hjuE-9)!NrwM[% rcI[ xRgò f|XEB zdq8:lGZK~PzH5v9LuY7ºCCk/q^yEd([ FN*Jf @\Z"Mvq$ems-cv&~ݏnk Y0as=ݾ d͆ݹϪ!ۙ. B UK}(ӈĆ.Qb3YwBu6dM%%v94JS9[)]m./¶ F|E析s~-\E%|]JF<@m9دYU\nu=kwIxCvo3ei٬a GL/}mф3a}+E)BIXes?+n>ji Hz*ʢrPAP,WS?RtoSDű0zAo"I Y#@-0L)K˒DPM 58p zETn,;̈b48loý^ϾwW*roߪu'ʫ#kVmمp#)[DUiy^@k9 c]v(\ ;HFAq0e K+ AVnZ;XY; ֶզpê7@92Z2yEʫ!Ģ;׋R`X4="3sVfꆕ)Rz:#9>8KE.%#C`vQ᪑QM"ڌַ6[O1լXDTvӋ Eec'_xZ<%htt&lnfM߫W|[&EOBsG.68消/{]؟ v05Qk/ڶU e|ICr_4Grc!yG:FmkwgG>v zhl]~nG es:W>70/伈_Z.x}- 5QHN]$q7iDdoOǗ ͒ˆz9(L#vyr䦢B+bؑT o MAѥBA Hv2쐵ad!7M@!~Cw[B,nd#B@ESg78A? bvZ ŀ.{pԉ Ro-J,OɟӚl/`˴7jÖ LM,ޏ=MD4M78D'x?I\5x81ن)sRmn4[0N|)yX(`=v?ǹ"MFeW9hRE -`u"f÷ϟ| RZH5B'l,TeLccA]ZՍ;?KN]s eo XJ=\('B]oՂةFW(w̮.uzXs ǑhE-$|VKB H3 ;1}e4iw 9 詜K2G+LjCIy@-I=ȾƈZ{zC1&+?SYlrQ$:l7}3 Ʒ1W0YjiAx*: Tꐘ& 13"u{xA#zq_ҼoUj"ÝqF$h7?9ˣӭY;RF5+,n+ǝVpMåx>nA*e)|ϜICw9 W^BL*yzTWVY(m1!]ޟfZuwU6m(eDkD-,iv?0>]|]r-B ϐtd!2tC?|Ƭykӫ]~W~dJ9%p۟Z97rl.Rl^_R>]WqteDB̔8F3yn;!J?E% q}}7B mNg!T>7#gYO2Mb= k%X$ C3'vU,fvUNMqhJ+HBޛ;`gI[+@'f%~UZ.@`(udu ,|_8ZXzRjɸLЅvg;ٍQ:Fjs-*W]?#*[kD7 Md3VK !J|ޜ;2,Wz}j/9:Cxeip4}} 5ɨN= 85Iv?-9 u@pMB۩C" ᐮAMҮIfz6f'B~}Ǭ:җzJ)X! `0Z̘Q\YgZv=_0R>h)耧Վ*D( ]8+z iᘟ4GA2 i$O1R_u,%N$yCyL%C*q6wɓaNa%e3YC74qpC2˜\˶{_BYy]tYS'gQOjg@Nqz Ht b"CļF:%ƾ?'4]q"iAmAfP؃HyWHPn{!UޘNg:?bBK%p*3lŌ⪸~X pz/c{_KD!-Y"M: ܰu0⸊_3Y =r~( )N5_ġ*t)F4x9=*ʪPM?p]Un%պk#,AJZn`73t$6y] _INJHo3^kW{ɕi?ZE'iES$&TZCvzh4b;%]> C[gVr"_*QA"e2I2TpId9jjjV6k1d;& Mf 9$J|-h>ˮC  Jn]tg+\Oo7?V?L-ۥZO{BVM\(fS0@IMCM$6wM9<)R9'8(p/MIdRqA" %k>5[/cd?tҠqRX܀a*Q*W*SCcnK&'[NJ^F1U`p M~HHD?(W\DY잡v~%eF`ӂ\X6Μ\Q_g=֪jÌoM)cL3PrI>ƟhaQP~Vwyd%'Zx2*`U|^piXs?xH O%"{@wLf6wt]X-Vq]w÷?=7'{(B4ǐ6 K;XPhIn @ PQ<4rWW6ͯޮOoOV03̤z.${T4J(ݷfgrLv.8ut;H̙։sZP;$# 275RXT׬C#- r,`dW۪0 )aGJN:5xn"33+'q0;[bSszAʾ>?@KF@Xc[ksں/VT>Wc9FXk4P X WsZq`2#fE%˛idB>c "ao󘻌n-Y6m4dBs5%_~yh=TҮd>z;wFQZ'Oq1M͉ %dP)N󈚄س<%ŗJD7-bfx4{BY(" oU<=+7ވ"*}fPK5HY ϻЕ1~d6Ep69LO Fv3laMBVTQ#oֺ4[ep8.($ ~U>4+})!UgH@ II}oaMЀZɲ`Ш{C‚.P C3]U g*D8sGvR"p79sϟwS mɦYg7])DY@ G3\l;&* Vi~9vNg+cՖX a0t4yԮyRs4Ї[[*ڎA}[ {g-;cxh6H'GkbJG[ by?D` Kv2EH< D2k:6B%gtbJFz QCWk8:# _aBEs>8p$®VQu/k0D'jEBi7s0uܶ BC}S=]v?ͲBwN |^mQP|$~u6}73Z8 ܅OϴQ6+I6?[j_+:r|+W{wWfʹ!5^$Aj슗..>dFnXSE_e|<v.O@1>{Q?5zW')g  R S>k3]_0T r{| ,".qkc }Kq[ YU28e)S!]-"FHah̘4ӯҤO~ o 0nabNE\G-y}TrPZDHF@I9yx#^Hbͭ]Q+)MʊbOP"Fۅg6IsWwݧATE\\A9t˻#,zNӶ VktFceJؒHwڰtX~&_h=ŻyG~3BKGܲ%(ų^jY+<.[wu\gWln&Luh`B$θKNhuQą n@\Ot;([&Zmǫ٭3UMFs"o*-#-XZWʪ?o}oZBܙ2һ IچϻV΁dIy3T)klRBYЮ1sMZxEghJ\aqzRJVfZsCت.A> UJ^KG3T!'"h oZLQ%p2nص,ΰ{ĥ_E:Y񺟽J-6~/jϮ_uoYlyr]ڋ10/sΔג+ [1Z.Pݤ`k%/_9>~OYa`/~&C֨i_KTH@")YO?7S^ċOrLmҠ s_G:fe 8{\-Ev[- 3{r o,ٙ)똓 [ouIsd&HTBAE$VWb69-w%ɦ)s^NyUt_)w=c#6 goV%6Aπb9MB\*>/wyo<4-#}&)B$@(ǵr_#:oa_s|O^ǡR;7/U(}VՏ\uxe,}^qT#YIu?.1 E0WyGxfj*#H>&Q=UקUŭVj1L%˖U(4 pfG3x`dyB&.^.eurz踌\JոZיȟd1l sW(8$*_p%Aˀ[r<]ݫ.HI_WS;FЉ31%Jkm?DbzU]/pbt&}$>,=5l="كZ9 W5KqFZh6fT>DŽc.jl9a:I~k6h~TH 4ɒ:P 2R‹uji)qhi߫HPUd53ύ6{j^WwToәhfn`Wh()t2$(gwI{wy >Tn."SZyfɤ$ f&X2_C[<d{ܵWӶT5 C1+QԫM-Be@1PHm!"#0Ȟ4H+cƖ(qmWjFDgp(B* &Dnl2X#aN~ŋC2R(:b/A'rxN+YHJFtDewH6x^rvUś)N&vG^}RU uc^\'~ ?\۟{-~aS0ƣI8~+>_vu%sz>6:]@2(uEpM瑮8x4;VE]=$dL ky^ľ4ffm,$l皳`65E_62p*Ҫ KE$xrjs7Ёy0Wr kc7&NGsk4@zEx;s+NjS֯( |uwJvˁ=ȣm /RNl1ZY b*!HvǶ5]M1^SO#4q<ѳj ʡ*QTk4Eis<2:rEs'Ǟ5ueHyiy1Fn*B\|>)fI%%(j,ub9 4ZbFz:x$2DuW?gq_8ԉHZ*"4%GQ*-&*.˷0Hz˰k001QGJF03BР6`9{>֤0HI"9Q !R?4©S5D:8 PH6"#osp G%vȣljOTmNߵXϜVW%5s~eoŶBhuj'qH1CZs.(, C^N&voWnLz8י+bmݤDXeT< 9']fq^uu>vBM'Jx fZ \X1!ѝwzxcmXg- wGՁbşv(;)e]Tznz|%ai6,xcPdv0 7C*.e77ϾL?3>s\iƊ3@=K `/ZjKy9cHSvnޘmfYD(qM6JÈznT63( EQl/:Twl[na_{$l&Oɼ:?3 g?"?rnF]/U?.rZTS~^)831;D#un/s='5!_X,8 Ed v 'b!.o^[v=tF`#;s5]ԧ+Phz-w}tS3qa[>k%~%ypI]rp(1g - <{>п_ ZAB3u)/M?ѦcH3JLο5NM@}W8ѕt L_]-o4Y.fמQԐy(ZBc '՗^`ۤ8A gєHշ!NAB"\9;5#S{~auVM|dFLc S*wweM:37͒muI=x'R`=AL@^UO9ϧ>?ۉ:潕5M tHV#)b /QݜQ5NӰ5>3;R'TI??GIXz^࿯R;1wPwV=1)YU%r(KHœ w4#fG=('f¡ZYgm,3vkRy qu~Pr?nWI2g]FzGcj0TExEUwP <001%N|9}`cTmkbCy,c Ҩ,fJO^O\ĕdH2\d Ri)3a'`; Ŧb$ȡ"*X_)hhNI6jQ8vR(Tt4 kh6Nsc\TTg/ndj&z=5Jpm"贫\\yܞUTtə¨ s>/iCRsļ%dd}¡8X'K|".}=1ݨZg2D =KUtf0Dw0j́&yb:<,2&0ܟ܏bdFniWrq臑-xH2fْ;QU P:ɿ|${fW.FG$l1F6(%dGgՓkُG*߶P`|)B.{ߕ=25H" 6޹mRv:>fmr4tYOCO|E[KG΁t~oz=UjǞM/}8ʕ_ y֣6MkHRjeI /`I.Z2Fzqj.n{Ю[ m$3f쇙lNI-n5$*h_uMm2ʌ! X$O{и&> | 4R ֠B<){[ =ݙM?7yx`' Y&U #PYڧ>e_EOy& Gˈ0(, "Ѣo5f:}DxЩۥqTv 5 K."TXT( #@];(*6@Qsw~5RP,gAE e()?Jn\ KTD{/S L3լæX`D <3-E{5 ax'\Q< F '^Iy?nrԍ3P%xK'V3a'$j ɥhW#g\>~$X>ky:ܼD~ٹbw#>kio0`ķX/6T7Xc@~Qr(c"`5ȅSqUe$Zg2P^|h= kPo8vC%vQ0v8<-Hy>f/:HY-ӣ/0Ag.bx9I:cImynTdC71r0,>_eunyJXR[:zPQ٢Sl¾|=]`quy4sLARx$?o|*FRi5<_%3/*#52l\Vo1CSk(O~J{!Df~ƪh0\q4+ϤN&WL hg.X&kdT,BwJ5 [YSrp\Lz/TY\Zݱ^aICyv̙У3d ;K6;̞U]/w?ːEp=!H998C?탼߹wxpLm Y^1׸{ג`$ ,JM{sFƿ?tJ5@@ 5M02i3sغp IHtuW1ЈNKq>0p,;U ܨ]DZ$Q݇v%^@5 ׺V*ڎ 74hI3NQcXFq J:O;Om1\j"67<`b,֒džb?BxYŭro[ƒunw* -N>pB@zY<}o|D@.VJm0=֌WyzKZ{#<{Ř9GN?ӫ)qޱjă4D 5bgJ0=*)]ڪq k$CƧD}9xG1cս'FRJUP̼kѶƴ3G2sRUfn'&~lz}V89z(WE]%CUX~ѲWl+4»D#ac E1^MYև~jd}NI\8\{^tdb h x %e"&X'2v3.Db)WK\KrW+K5] ԣi#=U;va'( 'hR@GxJ2{16 >h77'oT27n֏:Z.2:AzQύ6Z "I񆸰M~C%ޒ"f-Cl^ >š )>;tӏex]JT%tK"gtס;rr-|яݓZĸw(]H0Д0bgKU?͠S7 " TGG~ >Ƙ]\t`4im9~ 󈖊0Wі[!f3GCǠ!sXA\@”VٙQn-op//B?0|Bc~'̩]q ЬlfhءSm[4}ʗ|aőŅ$u@a+Hp@W?+>K7HxW_Q*1hyd;G z@2HDHK##j# QC^D<Ѷ.thIU*ڳ0Wjd]ejOD}y> KPܒSjF2M'o^JnZҴ',)˱Mh)?IzicDM!{9"hq|URR c bfZ oU;6dv2xҌ0HZs&#]{q4_ELK!R^oN<^OX EͿ&,&LĀ4,hdo:I! b[!e**wyRcXj < uG I{THWs  #>4bej)"U"r$-{цl4 ٤c_8gq4mt {xQ8Ga٧5y D9auͲc%bh>e0R-1m9;&yZ J`hq opː^lzj*;W5O3I`Ų2Wb8N ޕr4 (}C  aH^`恿^J` E3zCYc'oGj{E mN4 xk9Ob#d0ӫr6]sd/nKOhv+9bZxzݜjWc֭VIkM1Q5 E!2Z% 4HD'j̾(B{fID"Ms;X)7!؇H:vmk>;'r7A{& j^. |=a y [uuIf^ %Yy~ѵ`㤕?5Z%7२R2huL{aS\1:$m(J#}{*AL6}0Կ" |evD˅A=e"۪|FY$Ce2yr- q]DYr,y "%kh1V2 _}9;m7e,ŒpO{Q.L&@K(U&6q3V¤c?96PؠK`2XBf=8J1QQ9]ۏ(k^'SAow o1fET%NH@U?6AK`')IV0=UGy~OO6FY%%ռ%5.}PMRc(p^.IUYL p0tg` !PAvNDpʯcwd聴ǢXt⑱\eH̕ԍ+HLLJhIUmۿ^(S~&~&-b8Q- 㔸-/?[jqTwzh%L(zPyJ ã~`X!FZ)!X}5H+gt1 Mg2U_в6ۏ[mVCُU 墠8R{L :bu?bjw\eR&WC=::ʐ=2*BM:HӮuN)ߎ|__o'@>jW!'x5M-R#4Y1S :wqyhS|]8Vyd,zBh]?K,{#}@ơj/L(8y3@2+.cWGmނ*$~㍞9sa)V,kN`Xp|_b>c$$)MfThȎO¥$ZXy{>h^O{A.*MH"@V0}L~hqfp{?Jwj㰧ӅW'dKTOrXB5}!.7+{buJ50SɻRi$S7ǝ*AcjYe`}1z哮aG](PkCU.;;!/O|L.CԈxNO3N{_ʅ2˙a^RW2}tBJ@uު9uTfYtY-Z{s0j)o\R0mλQkʠ;kiL-T̀Dp-y<0;}~nT(\/`<:/sDK*uuej5.4]It۱#]ѨS($(#'D#q=;2Ϻ2Z8 5/iLjwIb!JmZ4`Z[0C|)E3ԲhQUQ 2DT|[Ayҡr/VTjNCig}tv[f,ŤVt-ݦkEBp.jXpZ&@'H٩D#H+W,A=d bL<ᇼSJV"ӲnEO6cIܹf3)Vo$z>tӼ+i4q/,,"bX8|~is23GAK4a5öM7veu0Qpw-΢(5BLX2( RJ1(4)ܹP=`?DnΣV) Ͳo*gE0Q+_iIK RM(6bD o=~;s[߇^. £3aQvrV%+VmzQB>QR}z2\'c bxSu>H6Am(f01B P"xY^$`T M w\o=۩(k_ׂ3w QXϗ^rp|xLŝA:rp}Lz{?ȎFɶDrThP$V/ rt} ,û EpTQa"<>cN.K^7 tM<]n.võX=MؽBDzTt)g'ic 2_d䇣k \E/o`u} DHıS^}oS36G2мyI_R#Elay]VmXlN\kۤd3i~؇E7Ɛ`6Q!)<풓)!SJvӖHԿTIk*MWlSB:ʾ6c= ^@힟kwhܦaaCԡI~z^/1$'oba(x4ƈ26ND0;-غ ?ӀYL_`5>ClL-9gճb9yd*hx6_T~ ڧ<5 .(t[*J ڻQp )&s 2EW.L Y/b(A#n6 y7Rlr*X5U{~NyWx0ah&A4E@Iwt[ zsP͇'L ~x߲]i}-^V-!f*UNS=Tf a r D 2T<X,/uyf j )7LLAP!,)9rt31%e~pj2tqG(e9BkɆ<C\Hg>U1⻗oʯ^B~0c?D v~-ʄe# \Mد䒘9-' ׇg/%[0tkO"pq 'P/$VQ(y GD=$<ǘn.e,Fۡ%(*-YڴVsw>C=Xg('%%x.xQsG牍G2[ScA9ڍ2V1E0{34=B"!&1eLp_ogxba~cQⴴ 6v a!$0DW^.; 8f)g]= C yvjxrbf֎(2سq{mKXוur=(`d:LMMRέ7.tstc>9S1(]pzK }J)2_PsXl`\!q*q6-Xi; ľlQ\g~xw2*pL ɝ蹨%'6n\b5Uu٨jNYRt-C1if֮cp;=k!!:" 'rL NMt %~03)k.(.d>4&=:P7^(Go&Yd~͞p$-HS {P qzSFVa=_=\vKt.27aqLL`+ sW?6ƜVh4"EN@,Ɓr0s,:}ΣW6Ӽ\PsP #BL:uoɯdRbp~> tj  !L28}=Zgv|<ߐUV֤lkNen ߯m6v@BYzbA#f@H-sFXP9\D ILPwzM5M@'Z3uef@b=%LavF7z]$ ?NY|DPǮKXOOB]|;, qbE ȇ;%]GBŃ>lOL$8B8#NM0j8]{L·*'zk C$E "˙m$W;LwZg>oT{,}!k,7A$s6~D2bX b`H^2'v@]4Y5"LZB@)%e +E q(|0BkOʏ]\8N%f (Khyv9ڋjDYВ *ᆹuX0&H +Kހ=zJ\xchuޯ/;)'-'Unup7RCHEīK+^g<#z x'_òÜH[K;#%z9K③T~J2UrsbH%InifǸb` mCXawh2!ݾ3?]˄ Yt~Y"3{ab 0MYTvKT "SU6̶MҴKfݟмnbaȜ5CL`3R D6,^7++OScyc8bҚ@HXwsx8| j k>'K1a( Uo?}?x\^Е6#ޟڿ)Ot ^!O]0Sy>! ?iLtɂBrHB (Q(;a؁śZ&2T!YZ;RRqV c J V@#f>ש//[6ͦˌPHUfNp?\.g,-9M2&I͠f l~l &% dsabd(Lo[f_][bnށVg^,KXxԪM`*k/6VDzScmgn+G;:dѤ `ٖzf[t>bEr{^,G DaEbBhh8 #~E\>}-Gn= a|`ޒ ŇԗU7uvֽmGqʳF){$n}ˎGC'?U?ƻt?#݃52iZslKѺ[j@Qý-B,(V+h!-<ǩ }%_㯣i/B=pTm{Y-Zy1?'? ᘇ-_KpƥKo|EMʞ M>/ifp/O6qhmņՠ}C" &ꃇyYyzS 0!wmVc( A-Tb1B *S6?b'o?64H0jqIZ# 0@$`?҉ύ/gЧ<\0$ N'+}PK.]mzս').vM8`%urѳ`[N$e[| /+1jIDĒR22P ~P3 y F3l]@%,V+8eq Qc1)TP=(1_9I9yXXHu^u2KPN)C|T*p?eAV38xHoIH[=$4?!WG-tG*OԘQǎ3Bs MjULq`ωSzmxbs|%lG2ؘh^I)4hs}ؾp(hr,A® C0Je|Qcb r+wgJ 8{_`XxFw }$ 쏣@+o[שMvvW|Ю+cJAG5ҟb"eqlT6ԟ=8u9b`gW `;AV<*oYz^j0V3l*(z W$ ; -6Z_5??cD vAtY<7301Q2 zǜvᝤ #7MӲ7''VgZ5ۻҜpρ82JP6Q\}J kh$X.RM^ݿ{6j%0{?2N"m=!$$dL|!o) 2GCݸ(XDFr2oz!Ӗ!|H#T- hԉ0-!oƋI!~ìRU?5zW`bO?yl[t2u$H)qd!ӣ 9.񠛠~$zsֲdcp=0_KmVlԉ#4Ά/2Ŏ6!]lAYUS W7Dxrut+;DpUʆY)-G, KXXS&:~ Nxqʍpzʧ -n 恆?L"oVj•k<+Ԙ"mԕc D~cII?$$OKĹ,V~[C~KS?!aI ]`26y%}7ԤvCT'AV{^sx&?8Yjga/P6H!.7W8uOeVʕ"W>Txg/ fQMETšƇkEd^-6!Y\3zӇjM "ISqā}Xm*D8 j|R9Ow[A?}g ? RR8 cT  Tu*.DZ|_Ƒ1~5o8I(DirEFuR6p_W %3(3UFi1ƿ(*νa@h.X\`s܌shQrE<f˨WTF9,C$:7 Sp&5dض91^[ߪʈsoIE3gz4Ldlz:)]e@1r(3w/I$gmMNHSP4;3b%ݑb M2 krܴ+kOY2"$*$Wjuâ`^Vnb*=j/ת~MnkHT0KЂ$tPLYF@\Via'52"*3yܵ^ɫB *m싘8.EXv(x@!$GKՀ=BDB WA,&j\lE? @JܾA(X"lA@n#U3Y %bsL!zޟ$* ؔƺ7O/ )MfMc6C wDd;FIz *EM5c)"JXȊa)CA,-`sI| b0qÌl*m~zUqd7Ӵm(ft}@FG)Z;7.<祣W6K%P;{^`Ӂy*nt#HBV@,O%˲ :>u|Ľ\Ui1a`)!~UNɹ$_5ג`OW|~|TPB,hKG2Y \{.g*yfEhDD&G~@+ՏOժ9کz >ͷ_BTt9J:!ϋxdO!o/Z~E =Ŷ3Qa> ѓg:΃M8I^M% ܫXQ<Kڒf'mb&1,]%Q/{gi +?W֠jmd T{Q:2c b-Ve{qnЭ 8B4r7 5$N\ٟ޼i!Cg@f\d0\ *ɲK7Vt>b<(,$"dSW։aJ vId=#Β\(!7Q=? 41 r >(J-ڠ~ȃ2Y]tE%K ^z'{tiaP;kMX[EglƓ5&nj!B-NU@ @$ ;z@y;5Sx!0#n"l{56)Pri=K@?s-Mfk\S4Kc؊C?iͮ&Φ)%s `Yl߂k!Js/a ` \q3(6є/x',1g, uKv飤=vIIBl .l|gHWܽoVihP>g{>~dn:X ~؀udž®[M:!&0s`x;Rp'XB %[Q!P'<^!qAX7PIswj$83=PMOTEd͐S$2ASdˬ?He9sϬ6\P7̃7L%:>^< :Si݉t2d;ʣ(i!c[R@ːMl%LyR+ s0cGRY+`Ԅ9+*y VYzj@M jF ǁҊQ Kr zX$ԍgA@!B#Z.RlJi΋Q[o&D3uZ42U(Edn}'Pi+8p䝮4gHJ$bҸ|ɴda%"x8"Pm>ԝE}bSEC? 6 ?r" 60|WkJ4nHw$oRO@^<1F3Qvֹӌ05߹2xR%0angxeҜhzud)N0:.(,2 D>w.'A%m*{.Ay:2d՚z-n Xߊ=G({lK"3yܾ(nK@}$H!ůzs7}E,;GEbj|4_*T^~# tNchpb:]h/rLv{< 1cUα}l'$N\vG[A<7|C r8{Vygtc}=_5P,5`N]9gc \v_Ԥs5'Bgma.s ەS" ]yy󖃱\ȸd`~YL!\S\]H$ ,h'ZI tB};kHLjX̬@biQ 4g&aCo BG wࡾ,\si6WQ@ފw9dvJJ!u(Rz6K>:ՌEOHr5FTE0Riaa']ݾiyI6Bl`.DHy}R9`ߘnWq&nx8GT9{f /uz8ơIWVhaZ | '0ڟC/1z^%@> Mܘ/wFu@@ZC SO.P1MЯHvD8#7r$ˈ$l`=S#(xfN [Ƒ1[rEV4t[!s )sBf59q]!1{@ VFU#'Uj[̕9'ƠwgT%>a$1kOq^" vUI,[z^KV0*Gs*Lo_ͭ1 Y.w@l|s~Le79~)fߢ19C}^z t%)t&b-ێLV[pŀKK){ VP؟kYF<^8{G,~"jw/>>7;m"(%8O"ܛӔ55zj-e4 48_Q2t8a!iQJlQ }Dg䩜s4.c%ƥm˅IQqhE;79Gkdfqh#Ѱ2MT]w* Bͣq Д6Q AxI <7Y![QQ+v@eAWʥe+[S9lC9,6޺QT;1.tNgWA_(߹7\2Dj_a1)JY rks;N4 sʧjMl S׉=Ú)?^£.E*SS X\R6e|Uk.Mz%i$֚3$ w`gXs a/VdMENkYSӊާ<A' R&/b34{i|S*d-2{W%QՖ'1WupN#V//8L(v;!K|Rrw4;]SEȒjVhBP8ΐͯM ?3 ztߘzg>c$>`3PL y@EVh=p|F'j ;:" ?z'zqT/ -^WH(k}HXM"8rfi٦u&9M`Ż u Ho%0^c Wq\ۡɅ4bpwt4DeJufS~w2u*EV.GBgs< |TU5YŔݤJ1u*mDZAY>$/+֏ õO7V"'UHRr>D/{E\paIYww+ "S}o77;nAal {X4>dl=o! pB8|r&\bqcζf3>s1-@7vA4&^v[(5K> Npj.Ɯi4 ,4&Hs:  jKT4l@+Yٚ]!]YNUnvaWJ~e6%ZFYJ{- + Ya–o6@lJ̙6 >Ey:4CRʐx4( W>fQGWTGa#50';|^8 cSۭxPȭ7L}{{(Kʹ=V3wkb-$Lţgey%Zx垱m^9Jk<2"o>cSlAh7my%Jl9h,!jëM}Pq_:!{w蓰se H(9fR֜S58zo"i%]?aC77XӛtfsxFo!. V4HoTվF *3<`CpY` QI6.1l疼8ڕ98ZKi$H?>ۿ1뗇xhEg9!7^:>ǿ~ 9J-+qh5J(#MtJ)n1 `I'H@)EHR5R үv 8NIԳ-aTPECN֌5HbK7n" UGٵgoIلW_HM|=drG3ޝ묗[YۢT#O X2BP? ,i5L\H̛L z># !ai}0EذbSoNR$ KRw3F$r*46 OK$D9L/ kG!~tKZ/tOuLLo:"0r&Kd0*\g_cúSɌt- Ru` `#&2+)BA"lg0_&.ꖴ~i)7T[l$ɪu`>19et +]bqg_^ Xᦄ"^ex"2YO3:O ˔]^R[-`#4S2ck]2)h4}NHc ,xx {&FB8ݜ)/~Emݙn̂jGV$6 $ X4Y|{VD%hrkkhWsMqCBEؕNi>6^)ʹps)I}[lTD 1jwIeNd=fo4 +7Zs5ԋN["DPk}/^Q~}}oX]ևܕ'a{.&s:D*榣J9C$d& 'j]5v+'dP9Tt섗 ĝFDHOcu pĖe;eM"!Tӡ4̻%IÖ5B"" !JH=E$x)K eѡv!܏qb'+1p"8!}33<.M]Oe it- e!k/%K@ug%Bp(`p} GihnhLdc"3B3AjGGOI)"HPКB87c'6;Ͳ$ 2s:1S~^5[Oo6WVg E$QQn# oRk] \.Q(T[VDukT"ت*2M6KC*h.br$FsG” "&$s-ky#-7@{KوcĖ{'!(-zB-(BK!51E嵥4K/S>!Gp(lb .PQ**__Tn;#oٟR n@# 40H-4o3 3I0;S"v s:[´ ξ߷*kE4$H4[E1q)bLE)ɂ GRxʝޝB5 yHZܑ3X4cIy| `pJq2{t%#uwT dn.8 눻VG7 3>bU55e] ,puIMZ/&]ؙy~#yMݬ)egK9 {KV(f氰vLNƧ`Av4@N\K?m;/2o5q̓D!ePUH+7T,L$ tyZ|7# (u磎~^wbTQ7xRK\_\k[:]@C .Dš ?8țߵ6|9L[bOsxPfx;&9k5c,ڒ恤CtsM!DTME%Xaxa?A3S}9C!LXd-Gӵ: Yk-> J!-½:'M?oxm삈~a{rR)fY/i =~.yTA)y&0"x=ω7%W49SMNMQEf$;g^jfQKn !)M6a,T$Ϙ _'?G8D6 EwE1gك6>Wyx6(. kN9cࡢ)[+iQAX-S&  7QGh cz~ < +QٍAE:cy"+PxӍ'Rfs\/zJaBZ2g=g?f7.Ċ.;>쉑 L̈6MvJ?>` m6FAuĝ׌mGY Oq5ds)'P‰2|3ZYƅUl}/d@[B1o*sDy\:Of6 C%ܕ.`DVʪo cw3ю~ꜘ{*AB M[Ug;% bXS,bQ{'+y=rL\p7I\,whap'8&M]cŏctLP?Lg-bYB;D 襭/.Wf02l=\#CޙyGdm*/am=L]h+ai8 xęE'艊ƶWnVR*Ԡ2^0I.HLjOZj(YnqXk[a}'v/Zժ~6c7=AE<[-g2d U6Ϧ J^E-8l*z(=c`^8@E/+'ue|'y)/l ƐC\LjUZ^W=D8z4j&jFSBB} Y-wغfs=@:c0_ӂ&4b+D1`*U&F̆klٴ7MV Xd@$E"PG|EOvpBTu!s.vԝ]lHj몇A~>XѤ:暸o^߼lȖ}MfX ʏ. Q٠09"Y)vWImE% C%g$a2)67VkEkjqةc,' UdhfOS/#@xgylC}?N.ڭs=[29) i #!Zhlw$Bp0I t `D a'neࣙ(tv->8z.LLA8l·UwEޅk$vފB y bG$yOq8Z5֙9|]{&`[Ekd OMK[܅.;y7yx 샙hLlTo/04;Y#jts`;좆h;R 4y'&%%qB(j Mc&ٹ3sE(k]Ja^]Un{9T^( .~yxq JԬCϷ3L%=0c3t+FBB|j͒[TD;+9/D'Իt,kL/D)YT5{lHbvAUP~zMcx>l֔68]"B$mbzwCMd?!o4B lb^@F#Ԝ끾 /Rᣃ&4Hk)Vkʋ"HdR4}hCZӞ'0<9 IAé5j$ "$ӹ m B=6޷Ŝ ^ӺA &9 5էT) C1tY˦ϜT5/lfG*V-]Fud\Vrm9 Oqe!0bF0 Ҍ7&e$MauV! h`pVbkfD:0+w*^ o*cmZѭ{[mfDr&?F uL}spf+(q`w4UONk/ȘYMN ޑ)/c"mGaO4 JWލ!F"pϛ Xxs ;& VAeZK?aP;.Bmoz_/ EƔ MŋK^\Za,";rG`C[ݣ~ߏu;o_] 9є|<ҡebt0F*\Vhnvt"ubF/>O|M8SR B our iSvߺɁ\0<,t ,W-DqbIVc2:K޳Yr#\f V{.ydZk5ixAc/s1aIX7GϧzlZ3 dժ1JNaOHOٚVoOoEx3yEZD(|m?Oo#\D*ɊIldӠpRkǷ؃8("]zD -~x 62Bfے߱,3VWX[TS=>;'I i-OLV'XYFwAVLhV!^f,UEj9D]0TXƕ0N1׿m` w CZ(h+7\o*ojG5m%aJ;CHZ*[Py#*X`?N%6jKH3{-4aQ]);Gޡ>N}hP1hxn!_"gMtW\gla/}")RxI&ǻ#)vyE|y}xYqy~kިU`..+ڈrmk<+R-}WͧӬS =%R.s[ý$Lkntq %K`Zh0go5h8?JL/s҄ƾfX(aɱlr4lpfiT݃8Ķ LWfl(:!vzkU7Y BlIH׈mb[@֭ ei>Wsɬ4mп<~^G#|Ǻ㮇/i\-+ V/3aҋܙBYmK%'RIrEoF&U]> -c !; 5O{`hІ QT&Mуy˥ǛE~nJ7RZޙu4$״*8\&ay8ӎu0Z r2Fɗl7D9^֝kr < jHHhg*$#Rꔁ"wjqp%sHcIQl(b +77BHs(\8^fe>ʶKjx%Bu AzTL;i'p~ WG1<:>X&_vS{41zs#>5=)aP@=7i:JAQ5bCc7Tg&BR~da5$[ q4Ϭ{CꆳvN>G"[;aL^W3p_:4a?i|z8&D8c!Pڣ[ !i`cPL~=^H} Fw?3z#{eh;6,EnCӫnRAj'LlBqiU@yjyhѹUlsr)y#rT'c1?Ta xmMO x};*N'$Ѭ1pbCͨ]Z(%[{YmQxb)ع]zgYb,E#wʨ+{rez_k˵VF`UʭZ|OoIyUt#H B!)`s?'*- deRL4fCǽR1orSIJ|Eu*-7$k18A;jKC.h[lM9Q)U ;A[Vg!S/S"s"v$q2nF =XLS.}s+(6$b<"$$ƅt-r”N8vw'Gn[9nIpwWxM1m48 njf#{ ޡYUhbE;J d=@a/X-Mt~ hoLUG+#),izQz֖=`{t719.+(Vo?|r'N]z;!,#P0VD\',ٳ÷}<1맒- ^400:Kl6q)M R$RAoHC݇Nֲf :x2V0<~F'0 0+"=~چNL !}!:0MXy13;9o_o|Ch %4fhj;yxo?bY;20OJ1sݴ]Qu#2?rt9q/XCFGƢsO8~i1G.4*ٍޏj,GS{R8po,cS!P:u!DJ+ѢV3s5p\tI|1k897}- \NjCd=$[?чo?|θQ4xL\M) pX=NoT]%6 x^?jk';+'J-c#o$=Z%|uvr/isjjTR6 VPIki2àΥЧPa)PQ`P84%!b A'B -x$5u@a(+U#F, 3H``I#>GڡWb9MNΟ g),zV)#F`|D,X8!C9+=8+j$CT#O/)j IӽTGnGZy HH0Ql $D.YKS |qQן7>nL`, DI&BZ Kwca w cr^:jSX ; z+ؖ8=uK{mOXf'iXy3p|2gF#b$T0G 'S4Z}[>xJٌ湎fZzZ! P{ZU3x}׹xeN/r(,#&] "цis:>)ټyےN43]_m&M.Slԃo#ӯԖ:k7\_ue` jPhFO¶mhRl.s6Kkc8eiP|~:WAtE b|,=aE·{ή_M^j"te4d,,P%]Hk%jeI@- t"~vQv rp\px\)~|h ;G5pB@\Kk_•pvW fqxC˽r1ΰH#FǥŹqS8oW_2e-wnn !GWMUCEfO:s K^PdښTQ/n`4 cc̪: a*zBn5Yg;tSNDN,V+>I-8Z v6yR_Or>ɪljc'Ig.$qQw`{݉a?P@ξa-n^V|=hwG!Ko1^B\fVI2A Z>sISwq0Й9浘3 Ao \V|?Ѧٟ^ʋR>Mo_߃8}(ZwC4|Y6J-~vu^D]`'93 ]-qu]GHՖ(!Durȕi #aʉ'0f+{K82@uXfK}'U)98q;CV~紋gUh:Q$h5z :-38-{ 䗘p6gd~qݍ.=^n!n5R@^Q)t҂ ʇyy4]J.[O?~k<4GfR5isU?b Avb ס$!3 $(06)r#&2r׳XWӉI[($t}Vd>PBY6:94EY*gP:Onz+p`x__XdZ:O.S6)9l+L|,̻޺|Dkvݚ>$'i1???VڵprܓBUhTVj`=)9(ſ_k=JcjAD^gqAF>VJy!7v'LNE@Ku!ʬ JQC%k*<^ 4\s'=CYy\Q%[(yGÛb͋ĊXk}iX):؈08?$)NOΣ|C=Z*B|)q*" NI3U*60FP]@Jӑ1 fLGK00䈈!D/ow~z1gpd_;YtRr@cwF ~MS)grW.  'bb֝ *Wm+6u|#tA%oaW$b2[;Ynr Ul(RR<;84I}xa$BpuqJr-n)uE5*5Ow}%PÑ߳n[q`Q]iDdP1\"7/Sp0iqkfLH{+믏_-G>TRqw[;rw._;Hx3 TA6 Xˤ E3Z ި^ <aJ* ۻG-$~ڽŴn7?p,`nh`}t/>| [k|x^a+iN,Y@d'[>}}}~ _1ωs)ˣC:NS C}5Ê"9S# ƧRSg+RLYXtfk` Q͎j/4|BUo_^ h=t"PTM(O~ D0R:l@M.<] $hc(3xTyB&Ly-5x}}~S>pժ;tC 72ՆXrrhArp1ku}b8u(OY.xߪTJ [m2nwMr N/Ĉ9fet4 h8X t#I4&Dvv1qڶ"lƭ͖<^oY˰GEQ͓wpwxz…4$T[J*>odO4Ħv۶܁ðgo1 =L2ei -}̽j,SJ9#P:X=6 A4Ӣ^эa2*&ly y_cK$}Í$jE%KE8HG=4A+dup't8GXV?/HvY5 &:mfQ F7_~Q+SV/{O"6dY'x'2H˵-IZÜq$bAuj zO /<#~3k/?OoZ?29JkXܶ5+}N`.81Ja ,l4anZrHx9DSR))8F*Ak^5\H̳lsbh~>\'p?^ntR(:#7Uc}Oe{ya;wLs]BѧZ2`Kc7L+ݜI\q{έfw5q ZҭY9{ej$cՃdco.<͐J Yp-.@߂ Le]}nL6ZR;GjUq^8[ct*iN1kGCpE=A)+h@Ȳ 5 XfmЃĆn|,sRy!Lǔh /O64zޭmJ6ⓗUIHpGϹ5|K%PK癿g?>3(`)g 3GOp=0fgqx.bBlIG1y_o1FL:%/z($Lz(Ի ;\Vٷ+@[Rnh" 2|۟{n\bE95nR8Auh;_FQО-ۓxƍ>ӟ$iE]a#/0R㴇!f}bT.kPqTKY&͝D՜ڥaSQˡ0(|וɎʭHk#T"*++N\dCp`@AkGᴁp} K&DYn"7IK7Lݐ7L=<^}coӲnO!>~CǷc6{FZ⿦CAPdDq'd_䈶j3ΒZwªM'I#!1vIRB^!CP >H!Z JjD DÈ FYyJk7W~rоәOئ_H~;,D 8[RH_% ؽI8|[}w~.6 *'+Ʉ@9ҽ1)j\Z׆> [*jV$!e^Y@FCbKeirU#ƅ"=?)X 3Jh&$F>N +ZEC*ʱ*A36:V26F۾C2}Ǻ/?dW oJϥ}=>g̜ҤsH_xNaJ.zZjt㷠i٭4G,{ *:ta0hUa:j2 8TNJVhR Y&۽ 1.$]DQ4W.4TyT5S { |*7\\;{$ u(W朁/TɾqI,`e dD0zcK ̋CqANgdJY/y(Nj-nل.0ޓ[Mߔ3vy(Z&t^ۆ==l|>b#e is%G{mrdyPHX$cr#E 6[Pɦ]#ɡ;59TKg&<["Evibm;lv_Rm~`o@H~"~XϬ(m5<{"}Zk& "o/OO'Ņ%;L )6fx0&sDvJ5HڪIvQD%6.jUNt^}):_We]SIX]XUcFeA371Zrp#: rWj`vR ԡ%:TN\p!"ZAqs.-CڔVԳ葂H圬hoKqf\d0F_KxV'.R_NoJ-\'WQI0lPbztvh[DBư R&UZƲ)mF?w#[Am$`CdT\)> כXR.a%1)etjkiqtѸzl_* d7b4xVK8zcF)+NZBDujƍe>K@(Qם?tMv xc%yٓ-l/ݖT0&fQal=GuRcN8'$1H"3Sj ғf+ ꘀ1$~qۢ#6+w{=9<:?Nf=1>8T26af> kZn:2:INgCr'献gL@Z ],^cߠlEi5TOT q`K8ȳ"`XSZ~@Sr&+O+"޶4?ܤ%%i\ullIcY+ݳE}-XqC0h IB\pnjմ@K՛ߛp: WK6dK9ųϳwvDwF,ݣ՛?&ď%/0|1|䭡&=62 F#~c.qߓfay?Z 70_B: 3m&@$PHYoTo] UEqQ,C,jIW5pE($gB 7m,SP7Qn=1R,As!|:b# wOSBrݵ7M~Hq!aq4POvID"RbRu8>WmSxV"g4=dAy,J/V7%Q”SkGz3 {ت@G~[{vY%ay3,w倲,ymG c#%:/l#6euuw4rj8-SHrr۳Qލ" 3,aΔBLb;JE>pT6%,"ܐ WǦrޫȖuI{f9C%$*Xg0*ZuC+=BP8- "fV& ‚`@T^RuE&5trzCQn ! B&hȉ-6Zq=sQ\ d.~+?KyY{m)R N28cJG=/<Y?sx8K0Zgy׫+%sB׫`e :)I e^?C`At鐅1r0@f٧P]. z9"g(>bjYZUz b(gudEgcȱ|?>x eהܜ#UuæS-u0S(ؠQÈ@@5bI#Dh]Uj>+|U,^`®IE-ODD cUz`ZVV34>eq+=ү@00"a>f,ı,Pv{^dKkhP,-`4AHvI>8DRĔiF 4Ƴ-/w V#2,zӥ>hwT,֎c#zIol:jφMaV5?Gic++na}!U߭&7v{qzxxf fR:`W-f>2&dvlLfErLꮖa =epa,}Kc1a(C5(lTvz}QXD|B֬IvA!n|w^n.[ֺv!Щ+]=jzݐhiE<M'̌{Jcmλv\(GʯSᰡ b¤>=+Gf y0n;+@8eIĠ.x8@ѫShRaR j !^Os;8F$e;Id!7fn`L7Zm |Ef2KQxp\h>Kd)~CϐBVbӏaK=G]ZmdAEHavӮy! L813BjQr&c闛]P0$y"QڜQF=qP'n$v0!hMkW%W>pxV˭vT`(IWhH7AإS)k,҇H=#3vii2`CIi&\o:z RP@"{]VF.vv%-ŀ5"N5Dyg ~^Ʉ7:ubó1v va35(:t#>7JcTabOёJbӄ1IIsN@CJZ!]t5Gf\uU%~~۾+l(qz u@D5*_'Nf:em~%ͤa&Tg6!l'EZӲ8.)zL(Tӎjܞ҆p[S1 .]@gogSgTrr$_v ?+^jjzR6QzPZWޣuڷAS~~zI+iFvO0r#QMhXc*h#+sX]2,s*v"5q|E:51&ᚼwGjhPY4(`0V!|Ru*;غi |C&cXA"m D(qKA5h1:b<|weN)dJ0.`iʫ.&9y=d-Wȫj2! a3 8h#[E3Jq-έ— ]zP8u:H߻1k*(VC |-ͽlWH^xa4}ikǦ|>]b)E0Ձ#X2&cϤlNmuwi=r}/ij|G3՚fƄl>W aOYFryɳcw!@<||۾ouo>u/֏z|i yST"?CsʍzuڢPzx`QCl.i^3ʇ!|"2Q$00`H`BQma&ZQu?L.rzIc"޳ᬢi»L?OZ*CШͳۜ6u{B犔EcQ B`bKQml w>Lhcy.&A/MT7yE U\x_R|zAHȆ*H{<}}g@) &1YBEhp#<CF҅偅qm*߭%+ΕdMkbX𵴪_bv"r[!(;Xj#]9}-t)vs0tMV$2 MHd.T͂6S$^Ѝ=S1S-MMp.`+Y{Er\HJPc+?~,7d-A vݺѯ<1F7 %]lA" 'YajI Qy/$>!L2~fhbLl_]H[_t}w@k1|gW]dH"40rp?S4^2!߅"jhOu% x 42U)i4 `qHV]/G7 2284NΎ)IFz4x֛s)ǝ[E̜_ceb*-)&SSsׁ-0hD`:@>FU #z0r& @ErJs2E="GcR#dss_uggK T [ #! = GqAemը)NEpj*4@}_ݳh2*` ߑ+akw.pP\&$p-mj)J&@VZHfémX,YNA[{=4bTϩD54s:j ̮qCu,Eݞ/٥q7?N΃*VHsWJXIhRKp+oG|o׃7ַWRؽ'hf& PR@px^'{"^řD&FIA'15|T~O7EF)W |:i<|1y""^8A< 8S ,ᕙOu>v]Q#(JR*Fqkʤ8_ V:;L誃l98ѳQs2mKO4~x3ʞߏCT*|{7R]2 @F0|74 6{ѲS6K 4Bž D`h##ҧLZZ;P{*4JgA-tƝJx,<]~ޠZ'bvTӜeKfZ>{C(+6gb+@X4C.؛ uE+!kϾ'oo&CVB >+igf):oPk3. :wE<˺H"<݋^QfK[yƧ a>z 5)}^+zeڒpl,I$؛<%EG=x',͏:_.ƇY!rߵ10';t9UM>-(@r7=?>{^ytx(1H7x"[ Y}ˢw#?bƸx5LX_nS$i`dh.}vyMIΒb2sBmO;ȦI|ztꗒc!FMLu~C0G ?Ԇұ/"[ C㯣9[Ys@Ϳl+/$hc[]ſ/?ێ%r"ruKjƪS|x:+;D#0hPg|t4vs*!{tdzh&0 -W/}+]2CZ3DJ*8r D9ܗ#z$(G3)S8{7-]  +̂b fRR*R 1Ⱥ!h$Q2@}Br}5nCj7 50f}Rd[a^<+ @ez`gZ, ;7||8|Mʁ6fEшC2ғADP9_Vd.R zlN#ͥ._7[~X~xuc")@*5uMyOIa|<:M0*d]kwPHb-ESq!b|Tj@A&mӜ.l=:'//?ͨKjx4'K h׿]ݽ&y<x 2ic=3Ȣ[XI-,>}XE9s<[B~ `wi>遆s~?[[} ~AUi#bgFpOS.Mk3h}>o?3fT4X? n0mf~6j8_W?5}y?Z.^'&IRI W`r35}a`f#.v~PR=g.Xy*afNӉՙ DO,i(d-G!5 s[~$C3X2ڛ-pLÂ(H`wTe+l*\JVa3a0wɫ _.%$'_TCa3<D(&ؚ<ݧJ#vnCƸ2$ip8'lUDSPn/n N*e|3ΏJ{%5z.ᵆlulep=sf~<&5eZ$a! Cm͹dحVIeNwa)&Qt c&U%S}-|t %{ N 3G/'׏sg'T=B{(H sr+ǫoz49^jٮg:|PsMN|Ys sNwۏmڔ4gQpŀ" x>~vy?f>AY^(Pr/cFȭ6߲֐Y|JB v1G/I"BJH 6YS+KBeJw@$ÉVu0ڠ0` ll}|8h4s{B%B߃%G HbD48;P\|/b^HbDUXJeLI7z+Y iM+jrLZ; I5To+/ȪERN&'hlrTonUDY`80U4xgh͙$ \l$ i2?7j|W7"u4F^=lv n8x8[FFsAC-*쨰z௄N&'Sˋ^1Sگ(ũ')811]piIxۑ, _v:8PK9" Ӳ15̈B"I)hN\AXtqatT15:|_P(f~ɠS *"O?d8joGdƹX^80^𘊓Mbe{I***1idqmҿ4Іeq*֑[r 7 -YvQ<%-h[@oTq-ѰLWY#9 XV¿! RD9$#'lgyՇRR@ mg:-vZ#zՏ7롾]!{_#}"=T}<]un..R;em @-BČ(헿|t_9WGJ (ޢle1uu^l_L?NO{?˓.n<4x].&ޞ;nt|h/5,tN%%@y&z.<9D&3;bUR#Q 3pQ #9>6}EI'cS\Jl ;MaEA&(LGꟽş>~xŋ~~ztFf<=svhK>$Yq!(,xN+roR\dDP16#L'`cΑH`jtdrt緺$ ڀL"ۄTtg$ ;} ?ALE u$TFl`@\uNJL>6ןB_4h Ѡ Yo?"^: maT KoM giõ[ +V (OǼ@P uIۏh_qQ&0ewrrmvA|#_}_r B1:C`g*,󥉶? -Lh`?4Ҥd/l^(bLP o<@$#+T*G6?uYA}U'Z?v8mZ c*z*u{>`O55@躮s&| HW'η~)BW]Q8?ADv|~Zg䟔#G,I07>E!do̳ ie{yBvuGx$9roX_S\:v22@Qמ)ő.PݗΫ?>wX&o&/۸<;{<+<ïvӛ?0+of`BHǮԋ}p~'-LQ,:ճ?7zd69b D+gJ}Keqִ'51r$(%1.}:EDwM`Z: MBm9C1*Ο|Ϯd)[kHqЦEY&WRR)?=}H_nVFPRgAy2O1>U J[t>ǧ⹎K j\}MD^^WIA5H4|qY.!]VY Uth6S+^<'HhS_1g)ٱP^~1mvG`ҧΤd>ucm4.4Ea϶V4LD9TpˑN;EJcYìX -14 mMtߋ<laAp.TAױɃPԢ?Cepbo5matM2¶qSRxhaZVTb _~|7L✹ʹPgv_2]Tn{ܤ|JPQ>簾tti)y/ʑjY1}NٿObX$c%AaPM[0 Rѡ6_;?7|d?|)Ш&n0\K?O/Ǟ782$.=F>OcX0HK7'(\kc.UҵMhw6,l9 _EVbJJ[|gԍh*K1TAu*5,1EAIU'2 L;N1pSNiIg).=>3{m0eO8d0]R3J^ѢP`X̿]AHF xP(LWAZ4j<vs%F~s\z#(Yh @^9>Nש%Ę~4&>LיI\[j` Ҽ&Ь͖ɭZG{{w,f񀽀usC0avl f[cRs ,rJ ێS} LoG) [s!7a=XB^kbmE}VrFq6O'5y)!e+MLv.؈{9\~# wNXD0$7 qH'tHZl.iRn 8/XvPFv|Q)FEHtx%R/VG V6K^v `[NA,BIպ`vuX~k?3<$ 6/O"Am=\K kgH:G8K;p,UђC( e< 'X&쯡=vj6[*2Q̇}Fzo V,0'6:HV}^}S<ӁjDz({$ߧ?(W!zFa@H S 4![B( J~<,^doQ8f4leS/X6WY's7FVD]wAS;'o6ͬ\l5՝Kx75kgfeiT-U,r]νb )ݚ yg@&Q yP@L).*&+Te^^hnSsuQ)"1%r'źW7\YL 0??ڮ ٯZ}MHgg8ԼRhu{l.G6ŃϗAQ):J%i˷ q9*j)p}:ĸ +fp-P7]c3+c-bz{r<=qLh~j.G"x^zX$~,Uh߁v$pWUNt2Ce!ѹ7FP NO%t=%To[ݥ;',-z2o0^#ꇙ9 <( lP3mfXv=#,%H_?-n5~A<LJ:+^:~&閧f:`@6'Whj(\_^ǽŵ?XUVfx1 s,}p'nzЪhVOXr^t+͙Xk{φeB`R[n1rSilZ6(a [Rz9fg u>Θ{N65U녨fm*SI`<`Tf1z8 ]mAzF_dyK74cDH?\4MmDyhaJF L/9%,|i&"9a<+\B{@#Rb˪!U>]vWrCh_c[=jlge'd>FLilXMeDHtEbuْΠ> ̳rHthI,l, <WLaqq1H/,pa m y-V$3s|R&+ rXHā VUl}=;>kknF >nRKi]wu47ML ;cJjo?mAu9ђ׆F \qDTu'w &RZefc}g O,,y#]Q=[ژڇN,x9Xw/⥵U4v!\Q*$K&~4AsԱFgP8 хEkT <$40a//ǚӟ&?o[9G G<4= ?.֒60>و*qTG`"$ qz+6Y y\F}S4C5_Q]^ @JS-yChdIoO BXdUg?~ G&.rtpW@#SK&Fu\%3OM>]CpGjkYcsl"쑻&oý%X"3dX1ۖ؛iƽk`JѫUQM"s$C `aT[I^ #[R y+ a=0%/=0/ϲ>ݝ +=Mt]2GVKَҩe?O9X{ %6u 7C&X5`hp ![]..zxʇ/OO6ۮ.`w/dtgZ BD9@v ^N) 1ܐ;9a6=צN ˈ®07:r-_ژ`Htt;~hMg9[A&dUoH<.Z@EW~ޞ)jjOU4j9&Urr7QM/W>ͻ6jM@^ر WWZw}x5a$^}騖~D4Zn| J։4/5ٽwLC{MG՚TH! MdU.SWi_]}GL>#v;F<)PgzyV^њ].hR}$& CXUg U8% ΰ;|CȎ( RBf#PlBQw }N^45] "Qgt*}>?~|Cwk|@ʂcj4d91!IX X'%sE qX_wab'قR&R&幒H; nN- 2s7@艦F1:ҀL}!rliiI c<<6EIš4ƲmtWdrz0nvc7(.';(A= bIa"K@BBhbPą<3qgC&l2T՗{r98߆fW]5cؕ|W&O8 M3wX>`H<,[U&qg*`)<d1??$QѴkFr7|μz:,)5|K>.WOO X)e(c)7 5"sکdv d"F>۲Xa]0}%[3oN,h!h=hqDX'a?뿵jYzߤ|8?7W.5Z&oZK3V&O*MuR5?zi7j)Z_G%Vƹ'lF)kgq+Gg=b:¹爊kE"*{@QXS X\);Zn|~^P{6Bv瓗e8``EMZD 赧H5?7m9֒]ϩ!Bbg@#QDZ 8.@*>Ł,˞`Em%Q0۳j8f_c,!S2 >IOn?~87;)t_.*iʔ;Y?ݗ{=n0u v&fgmgG:W_?_H6gJIH*{U5FAfeVT'R1'-jmWz\aI 1:} xˬSMi(DF랊]rV8Yל2WShU [ #6'e|dHup񒦮 :yKv@ɚ 2yV[S.tNPQr1RP S^TvqrI K ÅJyއ|zj9,LzPٞ5Hdd/@o(Zï;7, }e]/ J( t{F5]QlǷ`t&O^=8961 |m j‘@ʜeum )DFMK;Kؘܑفs[n<~ōv_g~GS U#t3;@ZL_i,+_|_ PHW eblw *r;u,vtkc*qfM|.ctT|\PVY{4ie7UHr/ 7"h1CJH݁bmG %&!:\c"cCoNwY'2AI?OKLW.}7dݡ~溜08&el1_O1A$4!9LեXK>;`D)}$6<-T5Z`E.SVG}4%)A+Kj~C{WKᠻ&"92I1f23ntr7[4ECOֽwa&! PEۜx˅@'n5 p2p/UUiO3_0^Ľe&HރVSMk&0i~|A5k$ a+hF!Ķ]d'\_0F.Ӣpn\tx_IyTsB.664Ԩ|mkJNԁ$Sp[;ZFܔhrg,&u-Jt레ק/ؗ!(tN> %)*MiqW(;mdnQF;G357y̺0|kGߛK 8zZ&IUIRKA3!sސ}oƤI<ԓkIuq4OAĽgшD}PNkELs4"z<H_F@l3f3!Sg :5hZDQ1ez\I'3o(Jċ.kۈ~yl4OqI0Agά>k^r$c7mvYk1@\38t7[iV $;8yZQOoџ \/_ o=(,idevgsicl0BHzRrHolrzL\\ۃ1D4l?x&ߍGl)ۚmD4֗!3?]>mf 7% s~wĒ.(`&1ő Zi<ΒC@ڙtCC; - =HJJ$_ּ5"$R`YuUQݙh- +t-a$f)#'|,GG{8&iʅ qz ) {MLK\폸PK @C|v Cvi?k_{-YcZ獗i5, *B'HbZmą@;Lר0Pk5s|&I W\b~Ia,7 zVu|J\CDZ@'l.s`v#nՏU.,ZV6gC'e4rOYy¶|жTM1K͚-t@ʥVg'!b8 印_z! v}2[.+z"n+4ȶ=J51E+؁Wͤu\|'y\< ӧ^:79~g"f@7sJkyTٝn A 6neM( Fq*SrRM^mbV?vm/ C lEDQ`0B֘2x{ ^2{rVe@E%90bqگ*[ 6Au5JhNʀCdž0I `]iڑ kx[Rέ B]qzwWqEsW<a l 3Қ %DZϪMjx B5 T )\`;؊AʸE9 ֠?U-ԴR'+|&y=Ms7̿TCĿq27?/'n?-w O(q! DM*+}lGB:+9-pƁDx%a/iP4Z (XX*<%d~8f)SykÑs\4-x~=3bk;xihȥȕ >L ]q( >B츤s*v46jq,ɺfԈgvK7P`W QbLj }en:a 4zG_JXC_URօ$@K1U9rI-i? Iu _z@cqjuC򹸺$Z5dEd܀_ox,ˮZo ;d< (9yt) [%ؾ.~Ft%\V =:p7"_iQqX嗠`[+|.a CuS&r~K=kr{X#gB{< *kLt;.e\.^oUr &r<6:4tQ@˂5"fNW݋.rosF)EL) Abe+3?^3_ZO\I۞hn9kW_iK $ {UYZ:U]͗EX>; úMO4Cw8ĵ58Eu}h&l U ǁ"3 Y0UD|~;@lQyuK2w3T:!LA ߿k~+7>jm7Զ!%ųcw;-QZih kWYHxj9:ʲ_(H#K 5ՄH-T @Gv?0L>Uc82% ϶gZ͒E?0h6߼w@}pohHazJ)x}C}'@l( ::7afsҶ5̸[,1ƚ1eR'P P)ǟ DeH]r!8G%ӓˉY%ue=\B \+&~+Fn:z~ H&__q0,H+E gBFwG)bکz>fQթC־v QKiP2M pd0D紐9r7.7 ('[ VEn>!@ 1TRoM޶[2Вc |4b}1g#sF팙'G\s˰fCYyZ.Z4=Xq̀-[ߏEU>ganxrC+HrT3ܕqS9cYkg;k$P~j ~1P\FLR>ll0ɛ(CbiM3l8JzJ9Ʌuʃ>Fy 4Z2>9ߩ?Y1jj ָn&/.*Jge5%o}]k'tfx_/(O11˹FǞ~ 9U/Pv:_l5CQv 4<4*i29 +he=b[c"hJ^)f(dui|lx`ia.evOP=@+eqj2C?t{5O-)kp~{/}Y^WIjtm?`ؐ >bB;~b[摋 D br9ٻp[ m9:!'M.ZzB #͟v@{o*ʯgb?V2Zuv ;}jƃ{)36ėӑSt-#H_vmU"Ѹ=svxFB;fzYЁ^kc| vc<T=~ZŵvK>X'Ǻ4Ϊ3H[X+[jg45Bf]Bzt͒uwz2XB">`Št;'7T'?@k}k`AFM(M{6u_mlf.e\zHަ;ְhIm u1bm)h5tT"t*# K[+UC+GY!ZOճK.~m|iXX)]|i!OX rVƫps޹~s$hF~]ev|}A*751,/8l"ƘkjKyzl]^q]A`c~Pϟ)-[ Ri11d2I Ycc$^ =|s{Y%GyHo+v:$&:jGX'dpjiuOtYfߕ/iF,~Téq/]Et[O,i ZBv7sn9MB5 C8чpT٦bWr8jv"zKz@Q kMzzOTg{*3%bQiE~Az6ӧ-?X/c]3hZ0Xd%Sxy&]ʆ+CXN',q:>7(}y'߬2;EYƱD~r v=*CgMh,4o_N`Vԙ?:'E68Bf9pN#c7c0֖-(\=ZUa0ZuSbn~*1%܀Vp@&=8|ٯ&7a=HoU&,H; 0ߒal)2Yƣ2X=NzH_"GK Qb=F@SBqg躙"cٷuݥBwAEs"C_w$)6ܪ,C M#}kxfMsK5CkFCɎRQ|ux ,yZ_!F4uռBaqGxԤj3Vο2%PBؽddxόxe~TyS!j|g|>y'~rzܡ$P}Y)R f'л`Y(ke;\C4OK+@%qkmd="2k6_{v3k'UF\ĺat>y/QvHX&mwlB3"(d)[5J}mH͔Ld \)ݰ#`8P#$L&=>+jbFI3knȂ*ߏ{AZ>m>{?QsI=](7 !SB+w(p]T 17%tڄ|+}( y+R2qx+hh}RYt!NrC0>F?OO9-" [pn{҇w&c rE -9'uSn -E[K a˃⨺]wdonl@;{wX(w̩9YQV"ا6L %D5A>_J3@X74OE?dA4AXLnm)[hvq.si20mgkT^xZGlyNIij؈*W0Fzru.\ 1/@#jZy2'\s]4lz( vGRYbNOox#( uJ iJWN >X|^+h('WkۓܬмQ#scɤe'Ya?,~xq{,d {r/ܵwL -$9wjj)Aؔ RI!' e/lҧ1"!6ݱJBCݣo6@/&8/8*WIE\t!:TsBǦ(( )j)/~/|.Np;y0s{*o8}J7_{{]+m9BeRfX}JJI1 4A?d)`0ܔKBB*^m4bni°u`zLDMUޥ 3)ܥa9싍㜟n¥5E,H,g$0\t9Fv^I1#|q5Z,j5yX8?&]}dI:'ӝ3nݓn|hu}gįHUqy|/[,w'VM)C7U齺Q{p E]! ~-LIPK@5k0A&RjAn,9~up/y6rf5xnSPzy8|3.Bl }ngE$,UD"בyDaPe5B#ǯվ|RWs&KisxSPA#h5,+0a$N?Llho~r˪AڰJHfʕ 6׌w) #5sG[ DU^$ ,׷ 2}i],7PT051"1ެj#"N +&'p𧻬֝>C܊op;gGY1F4 a2]|p?Ç#] TPsVvJ6ḻ"8Bq΅('& ߨ5,a`g4UtxQ]P`k)W).H;P@w rԄoP}:cZuS6T(HqpReV&|>Mr ৕'y5Q/|pMt\?әDKܖBGQw}F< W=pP&GШܔ@!ePQQ?^Bw"QSщwYj(<9Z Q9EDwKeLVr01'U7$`l;1UM \I@snVd΂E &Y ڢl5ـ]\y:<Q T7->h ;3&!EFtjf=`SU6"ZtQ5Rw;w mޯ|2=YUo `R=h1I XhҭK(^P 1oQu׵Lmlj8Nڝ-?C\WHf ^1|#覽Pu;y&=}[xH_q6v- nxR?PMq=v0ס޽:ʉ ÑiȈAB8"T@R")$xFcc^+2)$-+R]zyFSfn_AzX]nn- Nx8}?~Ǚ=^L}sL&ZUzIV!+)mG~a7 nTHS#w2hip7ds@akG%XMRnTm&{ R o] #l?R[\`D,Ƅ!Cyx!J9LfO®9L=>^GrB逅 (cx+זlgۻ41~ ru#`o-zďdF\>,LKY^ = !11[Wc1L! 80Wj5Qah #ri09fU{+>rb&] l)G!Ϻ\FwI\ zn,3Ynq/k2?WE3oůjXA<݂V!AEh8r+wrYo~YH܌n+-zߗP?ٖY%>T1Pr ߩAfxa]ӴNq;$-skauZ֗1 U;12&O#< VShPmFG  ;ve4gUwg@CRrK=f[}}~wQvx #ȳ v0-/eS$V}8!̺.!lHANjVUG?m0 fbu32Z#o#&|I!_8{ng]ɪPZFXaY`0zZ`A<+; ˧wkh@xy1pVN֊{qTӝ'v(̎ tf!"ʎf:zf Hs|A42A^?~njšv14Z *{1׺gT>,xoUѯ<R[}BpYUqR;p7\.pEc.nU݁- o`HD`6Z%-I3;;xlzz%1B-'M<UH&Y=)6-E ɘjͰPA|||Fvb"oNc|\QO^_M 8}Π1!GԱn1W/IcUAPk!ehmaf.W`OREbݰK3rk\B Gj Cd6*q?d>t0 kRܜ7`PHZ=E=:VRS`.*1"BG_n?0f} !?~/\PD ҺѾLiYfS6}Tn/*f 4ԖBGך['|bd2UKu[4,qqF0F9h 6!ہR?囂]r=|wH7s=D]LH!⧮ )Ė]:2HG[ۓGSbhߕCqSZ!- t9Grζ951Ox'A9B]t-#GHFt!S?舎k%+T |J BȈ)74Ntpr#|=40dNZs(-h9CYȵz+ ҅gW'{A2QJFՉb^Mu xh& |kKi{hbjǺ׳{_{zr\g=˪H5y3- "ZM_ n9[:Ϋ8TǒaGR"U43h^XTGz绸=M~%r9YGZcd.j(@6qTa QmҖވq\񵖥zS/{_R {~u% XyÎ[^i>wRrzkoS9&U{p~&UN7x6+¥Gj¯SlgۓPyȟd$*@ !}TsP6kxX*yytڅChK-d '&&]V%!®"a]_}^XE<}iG#?rN1 r-=cSgQg&Q選WhvԱcr]nZd?a)j?zUX=$O(ۉqgQЊB#!J';ˈ!D  fVJL)ҘLXRu`Z: #հ]ϯz6Kz$1BLkL L%ofnk> SVہ*V$Xr[@8Vr=H ,蟙\ 4WW^(=y'jҦ'UK>wtEC$v1`[f8%dIlAB-:lH,XH0EjdH _ ScpJ`fxxgp>Qd ;A0:yG~Ȳ960EWFt 'ݠdx5PLRb&(ųz+S3%/~/1-5w|+RY1L$flCjz $a{t[#s7i)aI3[beGT"jy}oqH r26G ]Uz ̳Z}tF0U }Hth 搔A7 mϞa>}J̴|3|uF\ ToCqC0*-2Bp7/]{mt3CA@vNUJ#u\ wAL@Jq9ER=p9-Rxg^[6]bb`I,73cH*'lfG.YO&I+WJUr9 a-@{lL%c2]e1Z\t kAe" XhW&>ۼhJ_;cOn;RZP '0o%2O^>= pÔ qyy@' @b6-[ 4-fM%N70ᅒװ a2NSG n};}8:g2ڽzHzϪg ג17MM uBȫ9[beK=wqt3hT(u gm7U`/Z<^ask?ab͡]WόnJl_)"srZEkx1Uh p :;(.PI< 7" ꩅ]Y(z}a1!cᑘP r]|k@So莄++Z[Sdd-ڻr ,&3\>!="]LLRV(9E|[_n.v -%P?f][t S}Z&^9A-s E:ǎb'hm8wWcF$sh';)ZEr/I"}8QИQ4cʼyo/uv2E DO4>Ipfqp!J:E-aUl~]ƕmR~<.FP@T2i&O"P ;VJ=fs͡|LAVrs|Xp2a.Xۭ5K`ZXk].]AH&./0Uч,6ND Й3W%<]=6<ɪ:3F6b"yr~@h5<v0Jg*&a |GT\ўt(VY[gY= kz b =8C9eXFgs[6inI|ۆrD xχ(ŹSvHđ:&Zj1qZ)~^\zp}Qj#!qAC+5C3` [k Q;B'Y(EtP-vE󑞙} .ÀpZA3K SUIK߷I7!^ qcq2Q3TGy^oc%iP(%#>[?9'Y=4\*!C ~<Uo:v*t@m5Nu+oJTp++5_'>bձbăͰrmjGyWs:%ߞ|@2nR~@jiWcT: E1XC8QoѤeuJ} eqڗZr&.6{N;(!7Y2D{=)` ŸY}\7:Pﬢ:ߣ[zcg${ wQ%T@6Tăgn^ +ZLt}_%mKkl4aCIji't;Ҍ;/%[$Ê^|. cLR1"wՀַo\ 2]!Bs0%\$BG" '==rlﳏN^2]`.Eoj J~zWHQ+i=zsaHb61j1x_X IⅥpv˴V9vi3I+/:@bG9 0e[kjpKp &a VFKȡٽ17&bR,&6tԉ-l'`E;X0"Mp/0NmOFOjv9] 6}>L.\V44 9ЃvO4=R7eq ecNRTr|m@aFߕ6j0䘡dG֩xq?9}M2nX7Y.,U0KO襢?&2]"#Ts9爑s\y܋x98E,%IkIq6$7Sڦ{.C{zA~5l%+FX(To3WLhGg.[(48C6e|dBPZn`xȚޕ!IP::Cm U&hJ#ͨ`d2wiբ{zrRLLp*I&9fjHd%eRh Gq`ilin=z PYyJ1¾O^G:+zJ)3brU?ޟys6~ލ}]un[1?[s4q(۵Gfo_wS[+b *GEZCQ\Ǖ<"盟?jo?ߏZYw|@pb>ipbCubFvC J.D(QfvnBt2X3ON8bYU.<8FQT\ >2X~8|3ST JOcdY ˜ A F`;#3fj *5&K%D pzJ# A4ʠ $ y%حR˯;LtBEIY tn1Ҳ h>Hbȡ:nOFHFj ﯄a^&RפMq9Rinbs$z\~x$"kYKhXG؃FʧG(|\lK@)(+s(E5T]WZKǩO6)1 TM볧u["?)nR=r涸]аC>Va w%J$LBHP2wѷwɗ9=kɼm$ XBDأ[3i9t y:ߑ~۟]M15ivnQЍi>hv|]wge 4*]u5PCƨTJ AMQbPɆj۫8TOK b5K}M\6Ftw{n4@9YIZ>%;Fj[v60EK@ 4lwWT(GF4r}G` W 5ĂhjO=BCԬ `1ci<, ܛPEGpH+)~{x|h՚Yʆ KqI(?-͖bx3cH Ɋt5 G16 Wj8 )Faq !;/N5nTvК+ `vU8`E[MٴgM&&Jr0 ZrDY$X ]ӝ;ΜY/OХ܊d v+XHsOC[V#oKX~h{]寋?,u~,qI{@D[Oվ>QqY>,D "}P7˯dnmػq0R^fI^p}""?P;H[E`yvlR lubӡS#N͉4dWG/^xPCh =2g'17H=dODNT-P zS ,.JȢ4]8sj(FBpZaN_?ޯH1 RjjwF׀B%U)>ۀ]F 68OLF?U*Gtoۨ=*1lF .XQ8܌p팫#"jMx_UU  4͂0cbt;e犵ʨh @GZF ytI5O@:q6~=2kE ە0^x`A>Ȋi I Hޖ!fG|Dҳu*;֨d!OIQ {Zq 〪+ܦu#rB`;|՗CYR3Nvż V߾\d.3k3*Ou99ڶY.dBBI.TO֌:Kw}`K*y67KrZF$yRպo|,/_E$>԰:[`#1 )O iI`N+dVمiBbw!~l`\NBm:kn[p錏^#=02jc1Uf9ϖ^>X>W.}Őf>qjwmz8V{ф2(LskcUmNQafY4W [ L/_7L$$v<〈b"989({%1JB%kԼ>͐u ?za)tYb[Wc7'&7w 馄xaQXPɞPvVZ .]9a~qqPǓ|˿+gG^c;#?QLT|ɋŰ%mDYfp=U= y0yYvkS.bl_=P%CwUwm¢8?EV"J.1KY"Lv7Q(Q&yhj^s>#~.tfp@ x̷DUr8zݍz;WM{~W.lI05Ptֹ@_0+Uli; V En炓BkfӨ2=!HEjRuZ CBH xpMiO8 oQKŅ`H~u%/ґ2=_ҋoe|^j}諫"I^*qw:-Ȇ' N"^H? s?a0PQYG8;% 7 + Ȝn^vP]t\O.q6w"W ,ơ'}f." %$LO얼%dF=)/rƭY7́-Tj<' Ac#LTTGk6<,Đ:__ۯ]-^dvx4AdAQ"tT+Qv\_Tk *Q][^-_Q7l1`!9@n ҾX.zy)Sp?:oogoo}\_;2 w)y'E齆c(wmJ8U3[WmUpC^.s6ڽzWFc6 4CM^vǼhKn\}g[d$6, jmkق6:ㇾqn@?ԍ!UYZm ƇKUi }#Qu | NL&1Tc?7>e1WhZӋmr8$kAϺ >HXA`̵\ap84NU/ T&JZk:m1dBܗd1XmPMdt4p.&"x70C-5+ 6mXbs= JC}GkqKQ !-2+{g# 両b۫=2n3J=A5_Sx])@@qtJ$E`a'ۼ՚AX{mJnU p^'qӇY`L,Ug> ZuV=y ~kh׺;ڣ Y?ZEA*YL^&2wSdE$WDӂ!zAVg0.o6RJ&m[9#e~ڥ#ayjI U#aj\p4gJX^.1^W^vzy3>5t H%iL-jHQf@{}uZ ^'cpnMS /kImS)w!#9PH3BGDt}'hoS"1's}}8]xSq€܊g-)g@TQB\v҅UD9ǎVX1L9dW0`)a]N_}!*] dfjI/CQfQ fƬ8 rN1̣"wnHg;{AbIj7<ywO': V,`p$l8 ꮩ_kNW@^u-FzS'fc|aHRzkzB9YKߜ5瑴8_~{ BdO4dN WJL X+. rYa93pn9mZdl%5&\ÒCsڋ.Zh\gt}ftRqEpNi*^} zSϨ&y:x`NNp!ҖLXDVFh~ɏDYnʣ1#8Mwk`{4n!u>"6 Bzc?uۯ_ޓbxҴ,aHgbEYJr$令uʁf6jj}Eع09([W~kooъZ$+WaXZM3ET*5܇z<;;:bE6r8aݻDR5&6<tv +WeYTgNYl%SlpE^qWr ?HwWFП&b07< F$ SIQֈ¨tg1$QAaJR>$8${"0f#̖2Fnj.2Dn!am*W50F~$FwG5eRYiCڍ9VbpiM*9G_(fOJ( c$(fƥz/2p8=kW}Brcu"baA$@qۘd^P0gJ%:%im#SlD3{3=K9E jn^Ww.ӽ!{Ж0AN*kQSZ_]%yz`'=K򧵤uc&Qw2YN! `!71;|`}(͜_sKgB#!ԥdT ]רoұ=%)X(664*"urN.kRz\K J:8̛ҍU*yñWO);F $qh;&<{~|wÁwMݦ"C3WGqc119-<6Yf0K.-yZXѯ81ԒI$k_n*šo*ϦGv0d_տ~hLH@5zNWjM^k٣‡=+9 ܞld';Ȧ%06ѐ)wYd;>M'm `Ze2%<,D" G7_ߌ !@;!5jF(}C)}/!l[Vz` v0 79QOm).brVlmfO?iیߵs%ϤZ4c9CkD (Gpad6d&0z'Ĭό7G+kB%Blf ?Hv44BZ(cr o:)LI̢0iㆍlGQ+7hNͪWL/ s5. أ͎ckiz`(5e-t:ǘ]Ҍ9'E9-֫Dӵ91نh MEhh{] YGN< BNVu{TV VRDi9ʣx d{Ǘ}fqqeB"E*Z~xx!:,j9qU 40DvVKaZmisia7xF.uOA-AzRR޾HR߈*RPŘ)}Ij}@/*<)J֯Χg`X.Q)(t蓟_J%5&)dt3*<+se 9SLu6*0׸s99;QL5BWΔ~ z⨨cQK)ڝR HDUQU&t;m\8 " ĽN'$6-o 0NܺX% n&$O'2zmbH0M ×a"ʞT}lDgGLIjg"dpCmgH5i VcanH*|y+tTpT~E)Ƽ4yCT+(2B;Z4/M>IR/p#aFQʎN627ZDiy8ܟ=tB4*\+?}^ c4ԛS/f)Ȃ[+2 QUI.3@S0S ] ^]RY~fU2M҃rrAVf'4B'dVb=W^*n3|]lN\ >o駳g̔i( Xz=>0vPK!hq2|r: ,Gߩfp!9;}E=E9)S_m8n֞imn"nJBK ])zZ|!=u5i74A\MpFD2&Fmcvh4ҺTWR35M,0 3b";ۖͱN\C'(e΋ 6pO923@4cCk\$(l.P.]sϰ%Ts~It3g#TZ[$ʣVnL.IU[Q*Cܮ礮$[-r+RՠC'Yi*2ܷ Ճ:Fa@^Seԉ fy.ZOW)k*''yW_.\/ݦM:=#퀬O0xAC3j"\Ɯ.rA&v;Y aPͣ/e4Nk ӒO.YwӗǥD=pUn즼3<8 RnH1d-/<n3@s{5VxSf&6 4S{0Tuz@K##v5đIF %eayܨ(a)GGS]}4˒)]*.d-CoÈ㑜SFn]\wjF|8lI5z[_C)Lb`ʦ^⠪2C4͒U+ܽ\nGx]ĂNvB4Mol*'DT\<|G ݛ4 z 'LczYrV?RdZpq]J̉uvi_B=R89P5Wj$*3_916$h񣭘GQ"{I mHݪoR0HX m`'n2Ւ4XJ@mf+X :AS24\UaYӽ_,z FݡȂ]5N> zGDR})w[yڑ6wv(-|b!k2;9< 0CI8ƽ'$G %moDrr.Y~/b/<ۻV[a5p u*C / V^~97v@5a:JCù%̀f19pv|qg˾$& sӡXaê8,eq%[(Zy71aD /2SqMbF$J2y 쀘LBFTש?4ZO#^3i>})䶡^)˶Ie*w&:\ʾ&M׬/ERxT2Sy)" 9$W};;$wIoը%؍@܆p/Y;(%TܓD3QHhaްK0ȏx&,xz5, G)0LaL޵ÿ|loZ \0$m7)]',v6dq!L5k?L^dIldTdcd.`)7FNj[ӓ=/*ٰ3ȒOQYcCSx݆{}]~Oq(v^I{6ރofL/t8lOᅪWkYhCo/RZkvP us/TۍXgm(k <$sMNxh}lV= &97HlO0==j䴂'BLa䤚#5Fjz! ʫe-Og j w~)OΏ(1)_W%림B체D5H8RMV$.@^U0bP 18~pǩe)gbDe\4c-C Q4 TYQTpdqLa۷ؐ]nbx:!"hhSbk"(ʹ &XaX+e=OL*mQGZxmfUW`:,WVÿ߼ƫmt/עdFeQ0B`\ Hܧjh;y_?.h%u{"6Kg:qty} $|CBF= S#Œ|`׾Z8A?YHSLUZtAM6'UX[0HV?҃.('!g&C ,&RB_y#2ԡNJF>M=snK܊]:配\1`HO7C݉IApI9ź}UtT>ˡ "U[Ug7nBů"W֥KrCN x) pIK,H\h}gs^d,@׾R;ЩbXPK̦.QFw61,飡Ki}lu5%erq&_G$~M7ؗ"/KO+\aeV2}=Fv;BoPb^6"i ̤1{{Nz_R^ yuFsǢ+ \ R䤡"f>yDl{۟}eV(<+ ńYPgFoNԬ$@DGI1H ¥p\jx>-1z`%Ζа _ǧw؛PZÂծR2SAhntpR[T]O╡<2P;&S)}YJ.- fȕUz DRr}5 V T9FzwXH7Ga=u=oJnsa 9V6.ܑ;5cI%MC.,˥rh\!@oDDUavWStz>m*j3Iˁ%uB[/+?}U T! 'nk}ivT:yQ 6w5P5~ؔx4Z_B24pCinj8-am6c3Vu؋1~y [9冼rS+J'h+ľ%=f6}X4Enp2SʢLBR:=^gN^ý_wm0BXz߰Uv>rsmz#1Nu?rBAhKDԀ^^ŏ{)>#(I՗*wlNk%&m; ?i酎z9Tia\xd55@ -ﳱEHMd:]z!XGnHAJu`ra])B9sTK X$ &V2855ZѨAܧOAvLmїW$qp]2w^㶬9ɰXFа[leS/rx%à , 68& pç]# 2>țt(]Oo[=I)EVG;rͩ>3MiMet?s1mEpeg`2`&^]mG9 G{}% bŜ5y&YcEQY޾pϾR yJwh~ l*jRSHOS;Β;msͻnlFTwp~:{`0BC}ozodJ 8L -$z}{ 1n`!osP5nOm:7T1}F6ā*00̢WݫZgؔ0~[b$S9?B%#UuX;Gz @OjB7q`{_|fR:I*@j`˦lH!gd?޾yDLD͌d"rD VHLjswC9 (슻 rV?^FUeZabv%k^Tst|1Җ < MhэMG M:aaJRGd(^ɣaH g\߼5D .nwB\40K;p[/Sjy0Åi8^?WR ύG?g4 &v,7+ cWHN@|č+IIL†v0>iU3cwKv]\lGgn1&:{ 5z~V wv&B,K\[a HjLAe+kդuNӿKX$W B4!vXwxhmJ?0 %.ISo 1m"5(f|$:B뿬MJGr UsE!2{H -cY52@9GL@Q,4F +yIK7^dT0m~6{Kg$o.h@g4 ^|*Jvn² vFWkWɱqk* U؛{Viꧠb0#J.f+f-Z(_IF2}`v Ӟ PjNAUDvey!u$>Fxsiސ}<50ylkȮ ;(Jwtp ʹ]\vLzxe\TwOxo'bKNYs@ 4`VD%BNy!g ?ɃO9KGʣY*r0v=Чo% v~c_Ga k0mJBɀӺUPqK}Y|dLۜ[}NMAt9!r.ƒDzqSC\føt~>Uv( ))rywv=M(#^?7vS^ Ȼ¬6SSW'L+lҭPƬF6&Ψ\VSvbq$~5N y6>АPxrD% +CI#, <80^8rK8Z.56|ԎỹI݂ѣ[1R+MǠ1W)W}]m~ K2)J0y ZXM8jwOH8kM_VS`}OXz$m\-yUi41{aaaJo̠D9ٳ,卂u @Ը]c_*^DUKu4iے.`U޸T:z~W!zF@GŮd\F$^TG{;v/ǁa g]Sԧ)5 v(MZaJ'w'^VahM2ީg9fIvV 3TU+ˍWlЏ(&<bm0?.*~Ӹ2ZAR4;DXh T(](Ea6(wP+9EfXA*N;ܾ=6GVȧ3 F#c,n`MP$R%@c+{RO^K3ZWOPQB:.Ap+*T xlI,_Ə-zeGQP/!dů@N'`L vg$LIZ?c&E%K}Ooteqv0Pe' 'Zs@g@dyGlm^ 'l+15EdoȘq',n=Pnc"Zl#ydI802a4]ɭ[O TDΎ72NHm~ 1ez\T}NKNe>XRoM#4;?S;2N8I&j- wpAj+ľr~r6˲)cӰ2p+sF_mdJdowDJ9UyV(IO*d]WD/AHwг/51v r>ֺqqH(+e>QE@5Հ80&`x9]05~Wg7ܼ/u(FFdXm+ ɦLHy*9LXsH8@:=t~ޔܲ$gbDyC0s'ř5;J+I=wܭ;HB:T^=9!+\@Qwр6H!8k1: psu[}/"3^9Kbi^i .ׯ3QVzgAsD0)ٵАכJdZ$(e*jLtwTV@$>]*+Kb`;mg-ER$ipcE_Fwcsښn#|<R1w7E?*rXZx;:Q׿YP%є@s&*#F^$qFؚv%xډ\~N$uRW6!$1t<6;TU c;'*y"J<%$M:恬Gl<`5lw6hF[Y02 8ke< n;;@4o9y,^珟;L%TItgj\ T0:$먥p6 C0FrΨIm [F뗃N< XVNm0񵛁;R 2#7U͇!&$;a_M uU-dN $n*X{Q_83h @wK>S7!`1WF&(y׀iY ߞ yBfFL/oAN3s_12hw:~ b"LT5@s.vjb(wvGT7X :SQ5Fٝ?T#feo`pD2jaHCC;c?-\0}e(']aD10Ex3?_6m)4 JɂD l5wuFteڇǾV$AK* {k` NiɅOl-MuQTﰅMEY7z{)JiVg䢎& b0%fY;Z6\<d=cʓ NzR`j*T8e01N0tGA%fL $dНp/,>=ʰO%H '~Hs`dH3'd#qd+~Db2(꨺ƻ`]g>Bx#]*tR|ʼnSV^i\/V/8&jxHZ{hZzuhK@3@5^֯2ڦX&N( 4D>FH|6%X,Z&WSV)q=U82/QqKe2ǽ/vfZg9Ozze>)%=\KSłVk\Yk%S bvI1p^B`G7$lc kvEs>(y:zNU<4XO#LQC$,0p?uvHƣlDSIx t-,`p+Cv:$ѳow^. ЎS'SZx%] GD)w+_Χ?ʱM{ lv"Jf'iNM)YQ iJd{HcXwB._/SWKK΁8M@\;͸sG=ڹ%͂hc!cMX~a;O_2]ox[Z?ENS;v:]Ж\{b%4TD(Euyg:VHil?GCHyeLc 4SQ0V4Ggo.MX%'7`7OzF?7gZܧUHspo;ƪ^Ob8iBRi[sk4eZ'\ظq~Cᧂ2o0z4K[> k\xxDꪪO߄%L/Ml=6AtL@,O5{YYRo/> }5,lt?Q 7sS#)\t%KL5k9oOO)0ŷVÉvdʹH!):)޷`wuKЛ}N1qR(=Wd/uz2.:,Fٔ$i{qZTد$MF_0Ŏ:ġ9!=ꡏ ='I9,H8yD$8 tt{t5Hjkg'ɛfv8y%[ӑfg+37-s1=k1<@4vAD6MBcW{`÷L1^6)7ݼDyWNb!Dv=sp<}`1E#i0H1|5 3U+q]qṉ6.@%l禀\x!_ DN$/'].)Og*)~6g"Gjb%)8pmS!nq`$68XgzxK=5H(۫E:+sHĉpj=h$kҢܧ={ 4[mvNndǏeݰUBTSh:BJΌИ١ ĐOd5"-$klTCjsrx Lρ2Q>\iSX =cTdt**5G&52D| -rAda\q#Y( A&P"P~Pgҁ #+u@Yp, R9̲:@E`A:eS}ϖi XizZ6 ` #I*kJ.ђkÃgC=)r>ZYR/Ȝgī$-4&q]7 2gAab79"Y濙3< WGL#z*64j W d)~ TlK0ʉrIWn[fLt's= Uu-lph\DCvDm^}1hPREC#b\.d@hʫj4o#ߕf+f} 0EI=LZ7u [8׾2Fܳdf$ "{Q ÿOUG۫)L:Zݧ0;cp`JH!Ubu)EkAe8dXttk匿uam_-}yQ{ 2g9S`x" lkL nl| ggBؚ/9nxהiXۡN}4R]`h2.<(\QC nc<=Nk3&㤚O4ceg[ r%) Ȑnl $ L*ZZ8u'w#O*9(}}3Yj9ʤq͸}}G(`jX::8Jȿ/$i6=VpVJwCtFA卹C"+FnCEމg*l q;?a߆QvD;ND0HЋ]I&pnw5Cb%De]zZ#l4lR*j\8[cs.G7LS9zۀӿ](ep.VuDc.EG 4(m)l.8j ,*֮+D2[:g}i=- I^M3|!2OUk? l7B =_\V&% nx BTX:5Fܫ3XT1OPx(ymɅNu2+j,S?wQ.)w"y:4~p6S$dʑl=P=Zm$M]Gq⽭RѶJL'hUiP# c\V\NS& ya[xVF3!F?M=5O#t#9d͋JiEق妺:G$bQ6(6[`f_}/%ݰ6Ϳ㽵md#{f@Vwmq*ku}yoQ ^ 5n-tx֨&ױ>LT+!Wi6*;w=M05X]6Ww-͏,@^Ͳ$cah#P쩻)r9"#+[:S#~{wϟ'7Wzau԰VfVrzmG &0tU3N8\$*R;b,CCZ-K-̐ֈY * 0ZSw 1C0>l!: _%!gba LmPJfève3A**K!]5ʉ?S3Y%ff弹_nbBm?oY?V(TFB?v ӃᶊFzz O V~A)n/b1";kTNԨZ}S2^1Zwqܻ_A2< b/ҁzޯS29p`Ew8e%y-,"L7mi!rT(~Ж )2y~|3WRCp(3'T6اRȋ߁ua5CKS̵!A?1\o,oY,}`\yZ$VT[D{Oty6MaEy$I|$uy RE-O(Aku>'? X\år;)Oiw!'A{r{*ѼMYN>Q?msϨ{'npUOF; ?O{|DdT|jt`Jx%qk&.P•VxUS kI.B \ vy/"%վ#yH)e+zX`Ձi‰û%PM\ԩz;cƀ"6`i^qٮD9M#egE;!XANěetӌ3Vr=)sJA^E ' ; ,up!?ЗUAde&fB]S #Ɔ1-Sڊ g$C6a9 b{Nذ8f CNج`\[2_Nyi07> ^{c^NJ7]% g d~IRe2P:\l#3nu j &F IљARŘckٷhBs^Y]5-:+t߈u?m6%6Ew]-_ 0RDZ|z^t.eH]QY*.#gȒ,`{FZ-m0n.V7j2f1#d\4:ę iF4ѓ՘dO6ЯMZJNa |LA8w@EBR>[.;iyM7T]S>B~J Dw S =0$snALSfЖq*&/0߷n\RTiPZ*!xWpvl2üUsI4Ȧ&Kg Mgk}”>EL J`E}i̭8)@4&(,vCΠG-Qoܰ2Ob}n`BÃ^g,WxSJsQٚTvih9igӵB5zj=J9JO5?USKn9|xPhD|$ y^n"26PVkϱ k$p]qgEvmL|(`٥3΀@Q*X-<'s"?]A3csg_}&F*mDᵝw QO-N조%a`YTnD[7 7 LzY`r 5Hn{7W5t $_ ͐egaưG~J%s_e WP>`N(O_ũp2gҥ|<eqJUl%h\-2;kj`^Sy}/;E63>'z)ՂK[$[g3Op_q-gG=᫟f}fw?yO=?/ĻSDS=+Fl 'T&SHҎ4@a#)-@Fwc&GI=y=r~|u7R50DloQxh:<}K#,0),YNĻUggöG3zY; K@HH17ĭq^2C# * $1DmِafE TL8czT"vJajWx9AvD0A2bև*(F UCRB83yV4iS~OIfݵfR?ɹf/HPKKck ;_P^Bk@AV}̓=:O$0'ndZuU5rh^GrGpar\1.RTaUl?qGq -(Ƭ)f7,e$*mk=Y끓/ VU$w f_1h\áV? (](&54p(H3L❌z#R_hdYJ xsS!:FPNѳH}au4wpdVe9oRr4+K8V{0(nN t]יqF^>mRsr +CI#x('~ p푯eKXh|Puo\_[m 3g@˪3R44k/:*}spн\7 Qt)hz_!Ec!EK,z֎vpF//%W_:\H-ȽN o jcKχyi%rk%P=v+(֐8Ixzf}<pNj9(Ijj>-y{LvG"'CM4ŕRؘ[m&e4NdpYt)-ͯ@ޭz 3`;ng"m@:"Atf9*65˼}Ӵ;kAs-VEj%+ČCv`u,p پ?>^c&ҟ%9J%xrWh  $0x%b(;8K.:KÌ8,~JΤP--%K#42 Vc%pYr4.udWs)%?L)ם̫{͐ڊיU9dNI-bH&m8cPqHvȋjn5B6?R>[*)1kndOURrU|]yi-gq~L9 qq 2ҽ |d~+ B3Њ{8v-A~|34{[K?mt(HCz.k'[?8N'f$$"//ndmXlo!nJsOys+`87v5q}/t79{ RANxu^0~I y0NJ$Nn.@Zfy2:B9*Y¡uA͑,,NM ^W>vLK/HU_02 1Ѿ j.ΰgk/Zh$}C;!KF_0dbyj2]-y:'%>$cd.kf)Ra ~~kׇpbQ# ?̟~^N>fV@3sS 96t!4BDf=H"<krܷ"Y&Xt2Ӝ(!LJL@000}be;5L rFUiivFF(t-aK*չu55dT4>㫑&6c?h4) Y-Qu_S]`Z=۰'kq_F ,jHzoH;ly8 P|6owzv;ݏ<{?" |6L.V~>3Q6˻rWצpƑ80,/enI6`TTjT߱Ő 2ǂRK)&؂rb61emݽ<,2RwTj!,<̀|&sa]?)D) sW^U9Hى&'* Hޗu fK:m.}pn ReƤTTJnqW~Y\JQTw1fzPLhxC,QP1pM1+{ E٤h#wa\lrҊ WSҮG Y^ƳKKk~I)3Xbxvn[ZϿ㉺;$B~pjCiIn| x &Hz{s/[.(pK9=#{Tp+xvۂ]38xf=o\Yuy&]rq5zq0[%7lPmڼOKD#}Gk WHH~BŎe9NXzmB" }wl8 6#-t}ԊS&v,/;Ze yJ/vf{1$L91M?4d*~Xw̭WbCwgKj1;DӖs4 UZ[^DVJi ]t!R'm'WPH ;h#z:F]O0gCwdH{xΕ`F+mnCK2A.sKj {{eMeNf&N(jqh&$ʗpe;\j?=KR4tDN0 :P놛d#*)H+"#-K]v.F*Rd,$V)s$;$`[ 83tc`+ EFyvZp G.G?a9Q%{$1Dsm9{8w=|?u@ fMMHL1~ 6:NbF?lH6,Vg6sD)+T`qR".±rxdUZČeUHDVGUa,X`HElylDZlȟC$ d9  /[&dIIT`蛙x׌NO֮fO@EE|==ʼnڄ{=Lph0*mgi@Q*40^GKg4 0>9h@2`ZK#ap3Kjsu3ujDҚ>U,UIXYNiΓ kqeO9T[;to#. [06|cGBMȁP}FaOI T&h@`'FgfG.­ZPPfƶhS8$_ū"MՉo6_cώh uVf6!șc:OIeCDf g:kQ\[B.F* kz'lU`iv2{a'Wocm[%tǫzͶb ͜c vo%u?Gv#h/ Kz,"/'שР1ȺV3&##^xklQ`q&um2-r}]6O\KԘXku 3W-*+c ]P ɣ=^'{4:_&q4+D VEP6b+s@|=7Ò`C)r~,ܷ&QwPLJicB1s#'xQ-?/:3e1C d6Ɍ8#GG-R f^z6rv5R؍D:&Cfϵ02&D])lwM=hiY!+ 9xFU~-!BtVyM_όXM:Xt\{Ҧtm{]0@w? Y:<Œڱ=-Hi2.c)],aJD))WjHDO&'D])=uR! vG7#߈O'u;QX3Y l@Hb2 c4O{DD T^fXtϾҨۯ?O7*.xzyWr\FYkˑZ%犛Ku(/OOY?T9h 0]27h`EP qh0h m GUw:ustX'sе6 g˪/ ˔''Zϡ@B Q``1pɐmn{vfH3=Vg7Hg.ݸ_hZo YGd.τIi'yZ wX`"{Y@ 8c|I(2rGc"Rr1 =Ư5W\?V3ƎPsCd2Xr #Suϭ^hND%\^8sQSs.T6_oSvt?҂@#oma ޶Hu bu6OYݵfAtC$v˼ QwnV#_͛ԒC;͎Ѩ~_a>?4V%#h J۰r۾E!k7J4uͥ _ZB=Ri> 'wlG.\SCS LS1~=N_L<'3-!M-dSM35׎M&/073pTחဖ`ӯ (/y]ܦc;r}g94 jiop>W`,*P!Fvs~^oq[k;Q9N gf9;Y&zޙm1}l[5EYB@"pT LeN}Na 2{sN9E,}ujW8cxKv~9o?`;ֲw R\WzXiHgr褬Wp8r(v`UԅjZmi7O>̵|S/>c̫x5 ZD‚3O pҡO<_ ΁ ۵'Nښn!ke` AE׈*7fAmru)<B8ҿ%`fjFu9X.&c(o_e]M\Zc|j6`;lvQ+BW/( .XTVyצc܃S> vczifH _IH̘~gEwcf,D}QˤΖ0m*lP /]F/1J 2zfJcX}AGP5gOg# ~poxճei;#Y?FIԍ:z6ai~ѵiHUz?*"(!lȭ]ЫuG;=*z; lXs 37itWʁ1EzF++;V#silԡ mA em \25r^GWH #ex:RP( SPO k{}缝"Ր QtBLw)[j6\QZ9 n^Y=1l}Jhy1[[EBT5(@5[=%ǭJW;~7nI\ksc< P"doM948cL<^0+PW2I؛!&X:qbLK_MIÖk4T8|5? Lt),zhJcQ$YѸEQwӭ ySPo`!^trfǣϖWܴ]QuҎ9|{K8`KS08hK 7 "b,dZh=|ZnMz~t;3[]\]޾\9Fx#(f  {ƭt㳾zT^ݗ_?EP3 @ 4e}gM5QkO^BF?\r PZӟ\K~C?݌';zV%OM\8:_=sZnKZŢ3%7N3lZJsjh; mo;Yv&bXMB1,>ɤ՜qPNvũ߶>o޵]Fpٟ+̒A rڱ{w))NQ%.ZD5ݰEv8<:U|mÊȀ~0W BKhžWO muKWl2(*GGi%,!&-C.q<][QN_+Jd$$fFT*qV<+ LU 6N֋OK!W0ȭyh9#,={\`_yWw^xdjݕr>BZ']vWRHc C߽0ĪЩԦ 1c>(]a>M'+LqF ;<( ;kcADH ˴18fU{^Y:h `+{}s4O cѠ/$DAܝW%8eKBW}Is,GaV(PG $_ͿVOooѯ"dN<]cc{䷧;A7A7o|=;ЁX88ڈjÑ'D>q:9<-1rJLM@iVsZ+az(iewY?ܽHMY<CY .bFf{/IB LW?=sT-ҖU3nIptB=F$# -yG)Vlnw1Шۧ4,Gw֝?aӂ.=&ݛOQ}tn7O$wĠN)̅_.j֗oꉭvtU)k~IFpƓN(/N'Nʟ(ǻc錄=:"%Z}@j4xMٙ'2O0sjɏبt3SSme/^()v7wb>eL[F+#pt? 1W^[ jql%pUG\9/7x3}f+E8P QUGڷ)TÔP:g.!+z'V)wѮÊ-ڬso$5ׯҤZ<*?_3iɒG*iGL7pq#ZVӝC!LW'F=nZ zRs+̣IF wX-=^ r㤏Α)/K L8D^CԌQ(}sʹ Yɧ=>.;8F?} ?2t)(Yk@@O-Mr)ўR-mYJa!;̷с"W\ݺ *#$`TҴ@bBS块\TX&[25V4o_!@-]d[dZ/cáCCH@lLq&$nlr,k_5||3Ⱥ'Ѵx 怼+q!zE!':0\8v0%E3!jjx ׻oJZpj6c Ci8W5Je2.wglU=m?^ﺊ?-?9b:T ԰j#дixhKgez KzR3f{3ZA!>dȉ,}_wybt91]^؁4ľhX Viw< ={:@f:t=h7޽ʙ4^BMJU wȧ/ )h{a5ڛR7ܸ ceR[+YXӂ3y8YJY4`ႯW-#o{[*Qc>_ǝv!Pb.@3 6BB9#Bfx3֣diIڣS%$:;ԬM^_ Rר l&D;*GH{82PՄ{Jv}@-.7˧SWO?T6H@qw '' {Hs~8r{AL3aj 䚲D9hWv?kV8F8L*:"f`,Ԩ/s8FaŽ%]sי[y׾h3TcP|P 0 2 ):7؟s$OSX/2J>Hz-J\p:MQ/D<17u@(5 0YaO.&0j {ᵱ'A@̀^pt4<5hļG#:蓇guӧ_jyQBYCzŶusejpKO7L91!z! 4zn"&w:qh cxZ%sW m40f)_ևK2^bL?6='t$JO?pcT.>Gۦ`b]-õdYp )ΊGO.rИ E򼗽~|EA#FLa (aⷕQUDaWoKXX\cۏ_"![ X~_1_-mIUy^GMnZM,jοbհ17byX`vi mShq<2FKI飕ն>]%hg`Z X:1Df.>8 X%c1T(8;qD;E̒s SϘj@)N/e3s%@A:U]gH~nAcMh7#l.r)^SmzNL SUՍ:DG,Nw\$>VEO>#Q^FR@,wo0M.v-^2YRV)+R$_rBw$M|XTGI5e3n ȋ@8tˁ0"˸}>o-@TA&H:$,+#hY=a澵zf;G#0"c*PKJpU.y79=/ߌ,Yp,;$"o^_-nyC9=h.{[S)MJ`T+ÞMhr|GK'1/ Qs(5d[8EP2T`PqD=K;g0UɇL $6X1N"6DC\J${E.o\G~%'1<]0SK\1̧K P4;c@>q6W _lq801*v{Cߌ#+X+^,-P_]h@ G!A]9pڶ&C8?k]/32YOтrt_kPyGVr$ [؞\r:>m=GR"ńRjP@ЌV&dҘ廟af epL_&TC{Unow? ՑJ5bb^6a @EF/8! XT\l-"jd sKCtu}ۢ{G)}:8l,gb/d XQ9u81-Cdz,ũ>D8bQ`cT/ن3+F* *[SgpZAR2+@0Q=P߆ L2"?p^+ ȏ@ A9´XZ ) f^>N<1nC6Cd&D6-'Am5|)[\x+݈.Az_b$+>P ,%Fvϯ=݊|[4Eh; w 0;yUm݊;XC3t&:{,罎DھIJ}MO([G{i/'E:[y>Tj1pJ` m|ɋ8"k?/%x j#rDy }>_tT:ogꢽP٧MLBO$N"PRBQ]9M®q( a `Q+ţ!<%‹J? F^. ID&":(ui]'o灪S37h18gҍPn{䛿39f2:v15ͦv9A*lI_/r|/Nk,lKҖ _絺)@F|#%%]{.Ov&ycNȑOkJc JP0El^LR[H9lɽɄw?))/q~m]NIՀ'L{FmimL(O+k3"՞' c }/wLU]a c1Wz~eFv^I}Kooe0kH#$ o^biH[a<4 U -6<`W9.:zEiFvȘenRsa.7hÇ" #C؅1PQEF(/~E ]txazԐ2 iió2 ^c[ܗexv UbwbSXz󍂫 ʷ\gV|+.ؾc/%wNS)XtKe V>Jr'ka`5X")0=51)0fY((*03W[~ywvaڡ{cXWiCx@Z}\ސf|#o61i8=Ѵ 1V~:[CR93?4- 2.PUˎ_8TX{0r&teN _պޒwG#oET%^q!z451$Aň`ShA(n?o˓%B2& qW>05z5^AW|֍;_\9O*fٱƤ͊nFѮP{`iOZr m}]G3U D>e[ak\xǧj32jKXqj WŊ7=9к︄Jo?oAZqԽipJޑg#q+pIxW tVv؋jܟ,KIWjcur4 ~|-1V̠lL5hGb_.j61 ʳ%gyfէ aQ s)||fƨŞ)5B`i&ƶmg-g_v Pƙ +K Q]K#4,2*$J.3H&9m! 2?G1 2x:)1uoi~v% ~ ;7zD(jP!e/y+_}v 8Lgɏ-a<}j'ʴnFw9@ޢ֗l:C}>SxǺ6>R+0*cv/c_k&5RVg!d#, )d<[?=hǁ8<`h~|}4mevKWV%Etj&aQZޛĒ%sD'@N Xa<ELQ~UzNq&'3&Tkͧ9TXDd=a?Qs0j-BO?RhO \vE6{ER1O̦/E7ZoRĽqCDYqAB:w!Y6O; ?S}b~Un+b!i'J89Xc4}b.khթ@#"m[ѥlB!U,,CcjJF>=TV=Gg$V0;#`,1<90Bha>28\p -_C4W`@B>3TPpL$+xUq|w寞>^۾yH]['H}Q?(.jdElf-j:V0anuJ<ѶUa)C |QfΆ9CKTV&>e?[?|Q&V(1K.%ksnhdsȠ8'~BZQTXX75i "Np, @b[OOh_,%NK'39rQg:gEh#d?E={ajRa+ׇޱ#pnkX*B#xqs~=%SG nI! S9%lҖv-Ѩ~<#tQ]'uGSeh o߬Iz%2@=(I,;MJ4(1{T uXߴМ2 |gKZhsd?ңi =Ie_JUs8 I/)!)E_i4X&SM\ .dL4|PcU8Ol*.&2D?X>ӨfuW\@G>1WM`xp'ϭ F۠p6AlGlԼ'<2b.i @Ӫ$I4J86]{Eh4=>* qӾÉ}!LNᨌl>$!dnǫrdDl8G[|1.`-tCh8k9\;08 lAiP,9&6x*)ԀĦqs@N~KQƛ]RҹD$`@ @۲ety/R6-޸af _h^͵]̑zuxR͖.CM.h * 0R_[ HTтH qDjd.IT%wz֟6Dd8h9;4ǀ ҋ+yp-$5H-=UW䫢A$R3og1ƺ/{oƋW,Xٯ"7AM貋+f: /< " ڱT\H: ۝/WsHD3WaajCu=朻w_lwљ^ nlC(.i 6Hb~?cf̉2 hDDDFn#m$Mr^w2rJƿR9X氧l=[.&% D)ZwMiIl{B]aUq- ]-H<`kרh"^筯VD1QJ)`AۭP$8ӷN◓`{rΆRcJ Q|3z=L] 3-rV))ӏњ1>ϹQфP'xf:]&9Fd;U될 Uuf2̓ZŻAyCvrz h hk,c~hkhy;p^y]_"~X: ixGޣ'֒(la,$4ocع$)2NkmA)o6xW% (A'裔4t6ۯ㞥g^yL|ׯ%gmOɚHXtXPA%,G:t6_E])suYMEƣ=7_~zФN7ʕ\<#ˏ[ k:!Z̷O)tCy g#u@S P8 渻hU{Yix$Ǭ54ۦfϸFǣ-9"tHJY)]vu_ZeK1yR2ͻwSSWWvUn IԒSRVLbs). j)J]wH?f9NwiO޿D^KӉXwESAX(~Qf$/2EvXَS`*o>\7C~P'$%o㱃.9wgcD<Ϭ;6v@;dlܒ3ba"wJ$`2?۹oOgpM ʨva\ݧo7lџ%D" sNUwHqH^(@QyLx( 4  r}zm1 ̼6K3ba˻KsnVp/E^1+PdUN!*eS W@1ZhAVȼ㇀ӭ[;yzNϛowO'*⨜鵇x]7^<_hK ()HtDBs|T/>c"?Њ mixFC\{SHՅmž?Oxr)X"@rZ ,߭?MPvD_J(37Rc)WjdPD'gS7̞@=?\#eLa# ]&BVft:ɩSG~ -0$|>J iL\rFجvS(ag:0)/Dc6p~ޅh:޷sZ"yV7qM#Az)[$̘H4ZZu]GWۆ4FkZ A_c75V¦"Gգ͟Xrqj[gt@)1} gJPqq wBh*hٰxQh#Z 9f&; By'\O_2,1=F|P}ZM ZslOVqQ4" e)B; ԥUC̓ԨԱJR T(M3mi j<&9~|`a,xHi$nt&tSP =;d |rƫ!6M.H]:=8eqOoN~.J7ºA|̅ Ȅ>2T\:d8q;䬈\oe@\SSҸ%t8lɚCL`(zS4X`-@H%XTPHҍ`!Xj %s ${m,LQ_I`*?4tckb/I!x+E:sб/7T蠷vyݲd?y.o7u--Yh\yAvPn|DCЮZ#:,ǔF~J0B4N_#uHQ(oaR^|2)~WUxM"! '1n_+4$3E1(^x+Vw'T~;8t(vITF\A|4j_,S2fM!z?<4!pZrjZ E)8e4C`z*,o|hαۓDG0%MVuڿfoRA2H3֞i˜H|.FRa9|cimpr*goG2:==:)TFϟ`MQ;⷟%{}do_k8RhȐj{ۏ'(|^4G2R*Ky=E볫I;qs9RoQdϔaI74v~QWɴhAs U "iނA?k̻C <~zB]X;YZ֝ ZP?κW3G'x7 Z! A m3{|B/pfs^.t"k&Lfm @cThhBuhټoe^)+SIȽ,Hu}oݶd$hčvuy"k4ᡪ/qmO*"^bYj0Xv(F}2U Ndd`،7Ki4XqtXg8]_ǯ7OMחqj7շBIB.wǃH"E! \Ä2C%Bek,rHTBӄi,m HU1̱N ~) (TGl9A^dS'CϮ&qM}rR%9*kH8@35fs'wVoPR@}ntA 1w ̣:$NzX/xG.C EDžwJT>k/ En~h|p6$>WŕU~-:|3c|cn_k|=ÏqRRNx2] fF>|= #-wڌS캎'C Oy PX鯚`aK{=23 )dubj޴M7\aFlC55#ο}ı/jx(yM˶UC2#66 _Ƭ;/A29}%Fw]7JtJ-4MiMV5TX3vE,a%vSLpEu\3-{~VjXW "(J1IY1jylСv~1p:z`H9ұ*bsm3S &l74әx)Ho"[CAo׮5J> Os 'Fh:u/õFsvFL0G*gݬ+2/Ƶºn+}[ۿޠfhb>ܤΩTH_'hI  0GQ5:!b2f}"PD2G.|5uM#_+ ͚gf33b'N!x_ޣ߷~-[UNxJ^QJƝwh[Wk;w<<6bEQ!IUy[Vɇ}5UӧqEA[+4 &wk.n(@eRAͪ4ѩŸ 3oPRnK\#b8YZv\ZZ,֪(8I}{Wg 糾1@}8+p.'iʈ<;~%?ox4'[\tB՞7!>aKBeC>I>z2ʩj#o-}^\nꭑSzl$CuD95{ o'ƕZFmh̽IF7ޫ=l=Z{eyZy1?m^FCI]sFĥ1D@5ڻ,/B(55RMy˃2c%HA]b 5rdjq6wh?X?:F!|wێ^d Σp#L^ޗ#UJ;-M %ldxLc4,^x 7<"dcRsh70#60K+Y<~gmy{"`E8]bOID=yq=4}7: kN(iNL (IAx74)..}(+PUܼޣubw)KM6_5I~|R>s"=%> cR$zGH+-H5k 2[ "Hk LA2 h1 52idKOl2pc#lX5ͨ \^ $v a_oÈiܷu[z#$aTp($!d4L}YJ oEup1:6G {Gfj[pʘ ĒGZ"70U~S| e/&;L=)ӘtA(9Q+ $>fVY1Ao<.Ѣ|Pz҂mdxL%rubH+5"4]A ReHq,%6ETEޛlozQvhAG-p/6Z! $|HLl &tMBVS#CT\813~~n߰Z Cd;Folu{wyBs*j5H no·f8!g1Ǹ+薾+d@" :KrF] ICVN6;fvee@υKh1cޭ;__)L3ygZi9AzgO2?IQ ?\O; 2tc|&chkF]휼|I/7P/嫿o?ZBvgQ!=TwgysEVVZ)qM_ r3 @~7HH"Hgr*y~=lc@м*| SV&!jtZa#hb;[CL# $\nvƩvCz(oSFx* $M(qui3Cē]H);YBN]q8 w@g4EP\R\qHZ(kѐDK4vD4H:%Wn>7,$|?ߗ}՜PP0j{z=OtzP9րix 2EM\lnpPoߥVe=cL+yZhay<7:t,\Q;e d+\mWx= ,ҋ5lt(U$#`rb;DrK`aZ}ֵ# KV_ݡp\l̨eg8ʆf#دn[D)R_#p.[ tQ_^ B S{CE9S:/qagq#c\b-M]#%Fi0xȖHLLrHSc ; /_ gN]j"0Zb,ߥnq/ 5~)_Z&]O[oW;!ZGSf=^`!7kG^'&tA-4v 3+k)z{"ߚ2/Nng֨#yF+?E? ߴdy'Y̫ԉyUF&yՊ䲒т0ޓ9G}NNztUPCʵ#߱Ey5tkN8Ru#CĘ"GM@-R']{q)X_8#Oa&o)idF{u&Uy9p^xY*/-a&Q\dR1$u|ʩDfKWlI>q: ($esɓq\$̷C6붳?#Bg/xq}B o !&͠v!(5S<%ӁDŽ0GPϏ-:Ouykئwy}}?\އ0_a~N~ׯ6U//"DSx/Mm<`x{T=jHހ9"b Դht%Gu9'30Aְ% pXj2mpBO{:إdSuu 1a n{ gDeEvP(e;@!ɫ`E5߯*J]CMIRQ0KjojoRǿBz#.չ!$^,šuڽa_X&>.~ɕ'orj!Z.O0I/i>Qq{HtoҚ(ZWhq2 01$rLJ :65-ȫƭU~xο~~{veXN^8&/$?X? p- I[+;0o&mRkHA2N4-'C"Bq%+ѫi[ܩ-yWn$,ΖS Fj5DP8 sQQQy=T:=ۛbik eWuSΧ"f$G!c0aRJ* P(v֜I]MgMɎ)tͼaS%!:iDugIUr9 Eչ60cZ^+ȋ/; )BQ}}ﰱP{jqgT|hPdQ}!z,sKw< `[rQk](E.Ք<38=X Qw|G Pե˾lcuMtENxqFJx~k>]mݒF֚BgǗ zi%*hfuL:=[fe?ѵL= J1T@ 6>Y;Cp0-k>(,] M 8 Pۘ+;R0k39_Wf2-Dœa!ūk0B*@rhAPP` =ݯ k!s [2POn>o$pn>rz3Zh3܄ 8n3x9I7PoOȴE5tqGbYwXW-_ȯv# #r,Y!qg~!_89lSbC,бIo(og`X@0I*ᖍRW#K-g}9͚A@#+}`(@֔$mBa)AɩtǵB-Fˆp敾rt\R-(zC.Up5eɳݟɸ\׾;gBKꮵ 2 w}>OsG &k[^5m]Ol; LjCv&A~qi˅,{xBmֺn-dߪ偧 w T5Yp=s#|0k1m@.E/w8m'L@ <ІsM<蜘2CZ v:2%ѝ1GfsXp20c&YW#@ׅ9RdAP5a~n"#๱})NQ8C U)FYbO=K2o44TFnS[SZ)ir|t^k^R79i t8&tOM'un6ƹ/SۭV&k76#l1\N M!M-L`E fii AYX'&`<D9-Fwm<~<'Ė@D'd%r (s`teC=q#%>!gm&.CETZ*Hb{^&#QX>6?(r^%7TZLOI 9] 5Ais~.Y@j䭙SN!W Z.Nء>ow1uܒxaYEgWU9K$1ac*9p.FtlW,ϺX^f6MaDroRA~<|XY*5q2dGhrGq5@nKG5B.NޖJѯāvq3h8kM· Im*xo$ɎXZ@ӌ-!Oezm \\#f8f|-0ԂTz?3ǧ@|{W~If+a'e9ڨ#GƎQn;Z{dK}J3ڣ\3HQJBY(JeOcDl\/#];SS($ɒY5 V+E挪<wvM$^XY$6))DELBW;)n(,Ľ”}p@WApC[?tURb| ;K`)Afcs ua,_<S'8NXyKzsnÙr>_XKgr3ZLB DD8> D 0E԰%e 0l MmdP`eBI7Z݃VB.ޢ+clyn71ۡ-dqC(XF%[ͧ٤փ;s9` _ࠢ jrQ`B7ܞ}m~kB(g|]kΞ?70.R\)cHnMU`2N-wO@Z#ưץi.NSK3tG໏ fT џ&+1{ $cO,QTɰ}ofHs02^E$3 GuDvz6f:#ƹMz @ T CӇw($I@|@ ujFVA1t ^.Y B4OСݹa/i !GFKBƈS Ÿ}LV/#2XU䘣sGt] l\-<~Fیz~y=_zYncG4U^#v5IL(\w踜 g,>)I:,̶2X'~@c|eOr:h WQ&B}Dq8DQnt1G ~^apǩl,x[śN@uAҞeg^|/ %Il16;Ejȇ"]"\0 * 0y,1!b hŞiEtzKݲ;fH,Ƚcm[6I S$ 2<Ɨn;}:/ޟ7 y9Csf̉0 Z2gLMA xHVBji@D1PnObpp -ۭT?ϻ/ϘáWKNnڍ; q' qT @,dav{ ,x0BnZs-S ]EEN~#K uD NW2%aPf[Zgʄvr³bKWu#:h斮nЬZ pI8y¿g\< x-]TAt'`;i []0DGr?ezS&2Nsx|4N:om@-YW^o|4l)ܒq֢y4ҐwS2F.i, 'md bbјwD;c R _f\lOkA~ԢfxP(".u İZTUo:v!yd);'V-g~$.!\ (nH@ cRA`5sXθGb8] ADKBĀ{h](p=5Q μQ%Vt': p`#˪F'h׋m @T*΀FsWU#1H m0-Utл|;\ \׉srms6yVfΤ I+Rq9``F`qǰ,::Gbp'i?c__*p=V%$SI\2Iޏ?*|gM9 ~woUzotsۺ\ҥJl7D8a.$b=5L^0l!`;q,IӑNsi2vJnnm!naPKJFSpIHŚK+ ;pr>tT PDRtVnW**Ν a 9A@"As8oՙg5eH)YM4 JDl8k^Y;*9rfKԛ>1to6YúOo S_:9OXI)+GTYcdk @1‚2Xl18pADG^_˱NC+p*zG<7+Z$X.{Uv<y@ Yr 8j?U$\@Fҹp!d6z{pq1eS=QЮ}OfD Erʶ<3,3O u8oU৘a% LV4WwK$I1CC'zI7}b^\jS tbd+B:&s6~z^ű Vsl9r' 1[m+RG 7Q9/;K PE;͒Vޔ|Q^"i # SnnUdMnd6DmdKS Hrc |pIun(2W@EHveZ iYb8 [5vA *Ѹ<Ld5Q̬NN/zҿ5 /RuIbI%};8sErNgh*|>DpJM?,>F&wSg[֕w- UeBrmЬA82!FVQy[r8y:%3__uRh\Ą9s}+n̏0[W t'JBB2_fE0 Ӡ!B($ ClA | ՜{ZNy(6JZU&IRT-gÈ AM3,3njp;a\ *РYV'ds~hJ\H3iVt"3DT}ԬLFh nE'$W Eƻ3 .c}NNp[96gWIcÅyr7kv!%uİ+ sE!%pZ1m@KPqOj*y1!lbĐQ[EAOeҴMJꭸV 1Nqã X&&L}VQyy V9+t CpB֯~Yy.Ns|ٮA&$+BܮL%I 3YNg;l(l)Mё|UdM(@U>OEnzSE*F_g͈RaDy 5F( d:߹#șEQz2oSBH&НWX]Lۿq6?IGk?( hI,p{Es= w4kU c흖ޕz-ϰ"=yBչ0uM'?;y!!Hx$ xJҨXWH4e+"H`T6vPbQ c\O^..``Aԝ.*aECJ}N:r0˺dc+?ICtNNE"f(%lwcFjU.͡GRLlN::&0zfM Bɇ'oRӭÂu#%2Ao?zĝ˒X,״p{3}'*8dLk8;O"*—DSJu2^md"+t-#NL  tVI3¶}0|oBGZI p9:~ uq$Jud.Hce|S (Ke9pgB;iT Ȩo&B[̅c~?ZۣS E`/_)go6~"`ˉE`4.JevSkA.;,7 S!82XBM hBG,-D܆RC]}*q6ޟx``'.\'b( pP(b#Vn7՛I5tD`Hq-}--4(C(Tk":%b3.]jEB9(آV; uTT6 It&|Ap.̪ DPO›- ^!.?oyxHIPbv"8H''}R3Fc䟄 #ԟGxRMIJjڞNR04.~wqUꄸfMy"_Rp^'\v+Q-NMt5P$]k0fgFvlB 2r0 FibdBD#/4[6MVNP:d9SJjT‘;~q1ڨĂ_J1:x* ] }{xhFy40+q'ȳ|뀊iV] "[t#Dn͑=BAPlOǍtfD[x;ٰaV=0[ؤ8Ŭ YWxj L5E Ik1 |uM:8JRO$2َ͌ua0\PZ\9 SquzrQŀ PE0tK9[BNnUAP0[&R+[Hdz_={H1ْ!KѢL j"C>,mgZHU'\"Òژ@-g FL" &< " t@<0J5@xH): ^+q*"DJ:5沂vm"e7؈H_M\ 4I[35e$Wzn7mJpiɪa{A hS={4Q8T5"# dW=aSmإSO$.:d:`l, #Q >0ӈ= x}_[c+=ҫ h0^14&e(΃18!hYv)uk>-j< v[8 I~ Mf\S8ޟcዅ+/$o=m&0S*s]ZOp@%&N[ՒŎm38.+9[! V]f~o23% FiFEt#괖KI A-UZ eO >7+;{mhSăE n7my}V'Z.Dl5zڦ1bl. G%횷l:K?#~9<4~6V u ă !qKDYxLFlP~MN%~ٴo0}Q-.̦֨yI2ۺvq C +E'd};O/M.;˯OD tG&!B5ru HdTFnŇe߰K@7v6\d_ڐC9ג:q VzfJ+ԸfM NHrF)X;3灐K1!!H_p9v޶ozFHVF X[ca JK:(!ˠ&0v/ÖkPx,r).G7 H'5a-0菈\7W6B£e:DLC;ʕngQ 7mmpV_c}i@ W-E6/gl[{ڰF}񸘄C A ` nL3oUF+Bn8!APSݙ<`!ޛקϾ]<ƫ;L1Yx)L8dK   Fii)jR%EhXX$"$n,ym}PY{yE f qZrL.bM AWJ e[lqO[Nw/lK#o+ؠdaR aR2*mJJ`<[a5+m8onuⲴ[DNPJc`kn">$x~3`E.U7n<x`O~*Hn$m'\^ݺN(JŀJB/NdIѥ"mYyo-?kϠS\?zۅHpܦH)!k|@> wҺ,Meҧ?S[ OG #sd=G۔y]U]~^ױt K*c-^k-6]Rcܟ-^ 7ajhrJ1X 3j2 j +5~+d6dZ`kҕڑxX\YC^-&>%^>s ܅{dWFYt6nYPQrt<'~b1{†}i-TE[KVNz--wS#,_d=}֕r!!Laspe0$]Qe%9Ļy$f2 U-6x+E༉L3}#:eGi!cv^$rޥ6C{bcCFV^FՍD)(Gi1A fzS0>TkN-6/Z aڶ1̈́Yb,,+2%3cCjψf6[@A)>vV߂@P98#nzؽ{v,ڲŭ1O>IHOݭu{7x:F%>Ҙ1ڥUjW;iol&@OVIG,Ԅ]vRjn|  CTcq|塚QGẆ[kw8M8:P}W`[Ц g/udON#)ٗ`<ˬH\1wVvkU=8,t{יne^誴k})A ) ##|I>2I{䰺ǤUGj=W'?_5,ٹ6~)Y]+?MwGyvR Pg$,U9gpW{f^RxRewy.;+XjaHspŏ)p,~LU>1qZ΂t€ )Ilq<4A1$ dS; Bx6v/0r#'u|&'E7ؓuCK!AC OESBFG{ʙ/;6P fտ;t,bB F艞{'Ogiȼ(J/;L;P=g,jBQ+7.}0N)q=9I"ޘd<k߯UBkfG拚5 t}J(2{7S%y>J'D̴gJuc☵QocHK")'5mqI!d^WM)0+U_C?OZu@hmiǀ#,0]0g{^9~"_Ĩ! ۯK)fGnQD4Q#D CY͋ Fn+/Vӭ(Jֵ>T͘9O((\opl4 7biξjhh(:6ASzZb?Hi.z$ Op(`>22A\-ʍ]G$ӵ묶5Ev@-pqPg qg/ k-Ѻw瘵S(iNZ@[E S͜fO3PKKIͿ#j =H}/|Ij)i[Gw=ߕx%#X<=N5mifY# l(seٱw8tsh`<~@W 9Zb! j[1onٱG-Kgr<ܥOtմHB'7>V),+yx*B srW^73_AZ{~/Tc?9/k+@<9Lbī 1w؊+Ndhn󳨬§ SAOO{H!f :,,?M^??)Ϸ 2ĥh tgc 5}+B%Y+tH'7G>VE̟l7w eU12nEm.fhl?>wVU l+QR&qf5&1UБA^zr뀗mC&5ՔL Sʠڲ0]kW$Q@3o#_=ΎX&FwMP 6XVSdUknMRQ!$CbalQۛ ~亪mx<按0 gṋ[fQ ̗@IxH;nDy!ݐ d~/Y2Nr9f>bðvp/G7b򵟓[b<x*kÈHd! Hө۱Ul3c8qӃ5 rDUo>g/DsdAVtAhpfO͌qٱO Σ{B[: %rϕʵ*Ɏ.DrH.RvypO4ziHiU0oO-Q Do}_#zSws;xn^b7yl(:K׽9 I2aŎ,ɮAQ#9Pa_:b-ǗEKl#Lah5. ЛmXrN=wp#DdJPM R>][HPd?D86: dNS +$R2Sj%ebzRO@QID"7Q*cA;#( >!Bq%ZW-hd! v6 !zbt',GIKq\Csz6L|QRCs[@ ޸m[Kpzlv1V+\DMSfqΦL@ G匠?|Lvy{Za!Σ hVkC:y WuP?Vݏ,?ѼUi'MLr>}S?GN0Vg$OdA?tZW"OpԳ)ei8&RtB( Xp.78gz|q7)-r>ε?rOWBfi7s:ԣ SKf=`ʇ2koJ)O2iyO8M8&$zuªrQBϦnwMd]}'Z]z7;ؕ}w 6O]ep_0JVq(~([H ;v?6&| ´q{8G7Un-A<ȷ%9:SwثWq֜ޭ3,bSS Z+wb rD6Jy= xlb ƖMc˦4FSB`k6^(Ti"ֆ(џS)5ܓ?>离J&) (={OB=ۥRi7}—V6aFL]T)v!b17DwpCY$#)EJ޴u[Jv{Ix.8 "4DeOQ0/0׼$넹-b7~ۺ]`CAFB Ih1\? G1-S]X8) xeuQ*y Jr :S-{!`ކZ ҬjB 1c ]3`/W8s9 %љBeb&{Zs?3um9BQh[y7)Tݫ ꑶ^1V5>ܷvy%ZI-̈m/%>q[EJӼ d.#4:s]V4ۙ+7L S _W] uD4fMz9Ěg|nkI?4mCv(LUF8jܹq*c^K4JxqH`%A'fq'>a<Ű.v.Jz6+[3Lw;A<#m럃?mT+b|8EhJ"RI5s >=fɤZGI(A` k!`-q11B&U ufY`+h=?[!Ї\\3 m6̚"n:RBv"Pf8;{yv@ک= "x5z9%Qqiz|?cVR3STr9N}+Vh}[i-e`Ĥ}U6ELjGpRĴw[ 0J^EtFQn9fSr쎥LA1a|RKVV@dAz( IwMeVbQp G-$pN6ԝѠxˇH@]Y\yiAu{fg s*U fV~b FbifRwW1[L௩΍3T}cW(J_%UA t[=0>C1>P'12#it_@;Kp\ʢۦ4[˶c&øѵ6 ;_:Y#SJ娻o:iL\'[+fdձzѕs&]Cߖv Ska3Hj6goHBn~8ת{c# g٬F6뀗m>)k" "$uQ փYU[/Ov>%,NF0@3@Je\`[=cIqG]=U3@uv-p =8/&?f5HIQ<:Eb%!MME-ľY~b|w#4Fvq.V?Z*B *4I1񠥇>%_G-N)iuXV`3,%ramfm >?xc7 :T*؅_MgZp~֏*/̍A(d{G>]MI gԈУ  dqx>&CMD)+?2mW~{VmіTxaɆMnQz4T>؟$X^n+jX3B$٦\1SP6K'IMP#j$B|+gX4jFP=[hJ6㢤MI]e!mlJ3WJc  HE.;-S䂿$ פG/MT ߷^QY5C_oa ]_;YfRՕ6b\CbZۇ|vuԦ df%Ո&=GBiGV'1up˔[E6w;aw}O@FfV z7DZ)"lN Ztw:yq)c+Z]OR 8«mOf\'`#"ڔuE&Q*z9`D{~ .m%XnPvCfUUba# r@8}?g%.&y7dD(*UHUMU($)pTr! vC[f t[/O ,NRu PeV%߾cs[dhܶ6`X߰q^q^zmլ ,Wi.Xv;k]]Ev}hn'3?XR|O%3Z@ݥY+>n8O|I8M:gLNn%ٌtF,˴ríQ_2f.4)-;dky?=ET\~ūV i 6s4'pl$,hcɇ\_``'Aw7zI:[tg:}Op!=-ƭ2&ճzub>pGMvR-r2tW;J/#+#ִ6\ =@Y{P6F14K5sHzKZLVɂ*2HVab"i cZ/Af{| jr_V>kT=n//"j$]`X8gVH޲tӞ1v/zn>t9GҊ$\pXV c-6 ,M棆GIld qiC{t i}*V;y$3ݿ@Hi%õB?‐G".-!CK%BDr +doU<Ŋ{^="t8ƊKS7Ć+|/e+z\ {2B`Tpt{C@0/<$MPB,Z2_gpɚ,tBJıq!9"f:k$94@}dWCU&s"@P] 7d̵q+jTb)>`8As> 0m/pl:b53#J^CezIP <uMĭ=N*R:/M?5N)h^eICVU ijKFI~X~t1:)I[JQ7^uO 4#NFr-5晻"E@CnǑQJ h0{Ǽkc3 ;."Qyw 3EȼʒLm% Ye!dFsI?athsG/[nn Y\ ˘,]d YxZ!ҔK e{Ob7"YH` "S "i@pɐU Յy> j5,N0T(U,IQr9Vlօ T&\3H|ft j*`5Ub,YNڊj'2 21l#Q$)mC$œNT~`аhG׵$T=Kԣ21ϊjӕæK89W4' o]$'AL1<2D'|;z~GnSȫfjVް|.0>~daoZbwqf[, a^1-/՜ ͌===mg¡t73%1^@c^3>`&(FE#[$zvfmd]s?=s+SWzvEYق_-LnU >]7bVw3*(.\^nVBci}  Gq|9a<-;X<<rB-`" W\wW%Hɑe&3)3l?+rPyyMDDˍ$9a)KZFMR`wl񐍊xU>/y!hQ ~C"Ғ>FڡDrC.b`Ng !=tWl'z^j4k J8%f Q*ijaA+.4e>2\bЯ?]kg 4*9g֡}=t1rAKJ$%J}L0'gug>4|Hz@૕ڄ>9mNM0[%1U! +shh$GW.[wW(JJllPF ޡ +P#ԇkEr4 ꨗYV-a_Rl%a;q!fqbQSȜVE+k}_YAF@D{uO>`&_"-}-*؈o^jMrHk= 66]x2Yz5VNe{'$;デfY6$Bg5W'KT|`Vscc+@V`?)yP? +q&nTI\Mo%fUf &I ̀MFhяox?…]_K uFg>blwwrǕ!P^ wb`eyQH )*KzT(|fdTzY\Ð4XaiA]<xY)/TeaYxusk!=I/'<֜qdټn^W1Φ$$@~_#uW^ e&,Lou6^MComQl $:;1:4 MW#eۂ-JZ?_4(0̈k2^1Le!Qx޿T4T]*wbkh]eZ[Qٻ6){uA0%1| Q͟)CEM}SqhFpOZC:Pȶ0 .V8E)]V`75YlGlcXőMd;kX"\WR1&+/sp@"|uS i(P!;A$Q_>){K(mG-b(ǥ h8S:(ml87wdj&E yWJCu[v"gg)u<~/geVU6LyGCB7RH * ʈ"&KJUJ9C߳hΘctb% #HߋxE^Hǒo9"?})χ}Yi& aDM~^9Px} γ5qq¢3 (8R_“2 m0wLXaC ` ۺ9JV(9t{3-;~LwݲM`uf6LMhn)qC%i^Q)N|Ճsy_zaٓ|gp??YҨ*EI^r!}! XͲ= ] }ܧpmnwj}$u7Kp<-E~PVQ;jJ3 (ĪbFNi%uu7c]// +);0̈V>ϟ!2Tމ*A2ûc>/S,y/)19:*2~i)w]*/.!ko.z=,mRc}cD,AnZÆ_U/odJ8w7z}Ryb <{;m]VN pkDY:*&- xQ28 feZݴ7rhﷶ}Uź V拔H0n%#3FCL{N[kN;JqIx,FFz2~Nt%;A/ >s㶥c#CIFlWp 40\>]:Xn}u=*ѬN9 8A8ǝ"Me5J&0S&rw"0vdZe:uJ5qkcg7BtTx){SPj kdHb]@ѓ^c8 0'`1Fߠf Et[u1V&0>C4hV$n2'j>-$8n8ܓ[IQYt2Z~ 0FoJҼW 9Lӕ.|3蒸˫Hu"huM(; 0nK8BASzDszCpc_%yӶmIckidjypG@A)œ7\_:gwY\IA|⨨Zr+f~׶zn]${H)tJ%sn[$en2d|BHAOpw e%e%z9<@do{rz??}qdY|p3@Ъwó%[o1|B*'WX A2ƿYۤhiy^~ ÚajDp? }Du#paij ?"!a5K. @ZoW,MroV #b#zt光#LI5Jّ!s ];|i%e;s7\XBlŋi9@=Zaɟ:ۡ[[7/{=SCXGv! ̖->3ETкȪr>WW:j܏Y^00\xL֮@I:{icBL)O[o1 Fs nd1ExsQ4.[)Ktc9HxNR e/ zRaa=QdB3SB1G&EPx|e_ogIE>y|3E9;"djؼ/d16P\ 6!KSrnPn[b1̴h}Jqlof6ZK< `).s4KkQ;n ee"ĞJChw_e=iD4讘+G4[JRM"\p]Գ ܄Qd.֯]MJ}Pg45F7gɻ.f{]A?)8vQz5Q*kK9H$-M$oݰƚ~(U][H !rt? Y 䧿pi#jAݦRTq]]n8DYkq}*CX*M^B3qo}:,ڨ(^ 28W:׏'zVDQ(vPڸazC hTV1G.K?1^*L%Q&; }- 6:2-h!=~HC.X?jx$%Orl\Sm&Bsijf`0#"xQk:b lYsftELKh?|y?t4%Y> ɍnp0Io(-ؼ 2GۆDIlpeFr`Se`Z&Gy jj}3IoN;Q}64FybA’#^z\g&LągRμcCr푵asxݑ{VxAUL2ˢm !Q$ Irl@.Dm"|XA?Soӿ5ƵwٍMKӒ䎐)1P ЕaH_h-MjBB鐉<~s'iMIuxu"WogZda<I´ܼ~׿-jdP \ )gUi-_qb|Y H"2ޱ͍aʍY!H6Up䃴7p—JЄHd0*}PŧX8U΄f+' HׂTT߲AE7/1C$bGESpԴ6YU!fY  RJVk{-wF$pq&K`=φCw?ZZf9WDn)aA۴Mio4Cs1 0GqeW1fvvc8EA{|7ݾ:3a;RESqJD%xߌ0BĶLXvr j=t.~N{ /z5\AI^Qʫ,k@34_ݣiJw%y>^Y<'[w`o{ P-$5( 蒨O#zw4i~(ׄ'&2kYbB9`5v2"R \``*b5:KBON\43_k[Q܁oˮj[h9ёc eX "B J1`}uH~tmbm ÈP?}M/)?=29)K O=,{Qy%YMGvҷ´Fv;@5 11">O-&ASG/TsWj0J 4εvBS6ꎗ5m~*]"ˏ{Z,0ƨLZGjt(0w;-9Fj",Trcu3nғF(fzj*,U#kd|m k3+ A ^,dXLb}_X!,h™t-ZVۂؿކ k^Z7KU>lK}5|9B5+{y EMN2ha 9~B?Z<=g0ӝ.VbnjzڂT?h9hT!؋'阋S&p=XI& 97+`wM3MkffEXqx&*? 2%TVsYp̙|m IP5hT~S᥀)7A $hPœF^LL?(؍iq"8CC̜(V6Ht̕6>1p 1!|'2fU[" ɶǬv(]uֱRO<'?HIÆWH\`mcb`9EUc?Ĺu+l'P^xfڀ0X:z')R viK%|hzYOzp'RD;PD5>Gcxhoqճͨ0,eLF"Y]x#Dk-~x"WYvӓ?ܭo dp6-a||*5@[//Di$9t߀[q0u8$"6{˲fcЈU]Āqz~3kDFs\>X[SWQN8D.9 :"_Ӷ(?m]_e:AOi$ l+hC(j[L -ϓl5r bddSyHIg %&Fb|.4xԺ:hJ{|XzD5 ,Nߙt76y9]W/ਗ*nG2Go<\8î5(F=yv,UdiPl|e~ey |eB4BYbZx'9- 5@Ullum 3q9OđbWEUFyvowxNjLsmZBLhqGCy'H_L@gX,uX>^cH^Q.AV} c 6R xaMkgv<ݺքAHYW7b嚃/ATQFiԇ}n4 B'L.Q8iqF9aS:bɆaN B-ɐU$zܯ8>EsU{'K­C8S-~_=' !%]H&ի"?|W(Zj%+nqEհրS= ާtS%]ݔ[YV,bG XMۓ[y͓ik';aQPn/OL2~POSS@GU i6 \vwbSlJ/ }\+%.hW5:k %b: qlRdŔt"( vchYV>6UT(_"xRf'PG QMbA|MMHRŧH4~Lx43a Ed=! r*JfIXxZG'a4 Djkb̀B &fvoyLMN]zM&cu!Z) K<mv[7 {)s&K0 E0տ!~?;@5q0pqJp!kr:;l١¾nX2o_kL Q[U5S OHd mqթ O ^v_m!m%|/bFDTULu, 3T`-jܼnBtqv?}lIYY~^?~;?!=؍:W?|u]hvP4%bPMbn `4 @w58WRG<.A҈PLN네ۆF;n#7+/ы{}wʗz7^e!>R;yg.g׺Iߙm !DbiB3뽭ֹ%i t5%Jȥrj* p/$@?Ԛ@$ÕqVbHp|TIڕ;j]WpAJGt|:?}} uIckDсUP a\[T술} Q[qU*OGx4-5/#io=EirBpmNh#K0=묥Oel0ōkRf'q;\Zm[,= 긮tOB50vJ99߼W;GȡA۰%W:G(|| ,wy﫫@Gj,UwyAgKcCט1~ wpV>|'.S =`1 ْwٙ8I#hrOAN+N~JZ B@'l6ED C/vw`ybB}`h.݁ &s$ycP=52R :aZV\_7_I/D*\}%D#ճE $HhHLHA&(:QZiɷkT1 f[ESQ`K@GRPtIxĥM !/e|P"V h~RzE<᮴h2 T*ҬbtA"RpOsΤIqba^ n@ s-%TcY< hȣZ걫1dđZt {/z5~ s*+l!@O/Gϥ(IPUl$($nۢ׋&H#}!ᨔ1fY0Dug\%$.V-b,gMܧU˜Z ,_ak@n1ZiyO/nxTPt!GC:+k/@ADv/aFt0 D3 j#Uܸ+j:]pȰd`fbJ +i u_:2LD}h[S:gVϔV(^ x9SMѨZ* qA$/i' MMs@&yjm`ҩX0 42stZqy0 ӚůJĕIeƤ8ݦ @*_bϐP1[ECY>_-8ܪ,d7Ī:-lژDIx^[bg`fn`ɕbY!ϚߺG3H6T ׭.Cۏ: (*C? |kb.nַ]#H8&-[\<32& ! -1EJf'v`Q\=tNv=;EF%,C|;xX,~.BGk* :mMHE;'0=Ƶ|FЛѓmiQwEoek/]wU= וR-8)ǰZGw1XB*g5p{8}%2q|@^O:Gba+_"/F8l8@ Џu`+F,q|;~~/;灌h#t2bԻ{޳H.ߗca'QY@P;*j.77r zh}l PuHX )(Ѩ>C@y>Ԯ2es cZ9bU+^#b8X}TTZkA7k:Ds܀=ϙ!Xge2IQ3^مl~9͎ M~zWo5~ ArB 3Lt .jIAvHgr'#<ʝ3DZ!]J#GwT@'^2qz R,KWEgkV['ԏ5d{uyU=[\X[b6oRxd E ^ōzDN+O^";A'axO֔U~aqZj ?.x`24čU{뼉wJ9_t׊C PQP'eA]ک*ywR]&0/yMÓB"/BA)$(IW*2` Ĭe+vN1~ݴ:p{Sp8[}2A2?ȃ$7Ad>#f(Z,h=c@c8.omdt'H"nfP=z>pۏGk;h NM%DufAxx^7Kvrt$R'P:d~L} R=Q8gGd2OsOa151tvA4+|\yL/, 0՝,`;9SnTZCч;Pvp%čF,Y.8B[l[kXd(f2B/"卼ޙek(F%x7,ңcӏ!_Sn o:RLU M $4ZԈ6+v[å S"ओ%9zvi\+vKGp3pߑ/S(, 5!OlV [kil@L+[5>5JFL7qA "f)p։*]8eχ:t:щ!`L-p {Q zmhk`%y70I$- @Iqh=ɶ}ķ5Gse{2R;U7ҋW %_=QYF,Y]yJ$e2"yy!g҈* 461${QcGk #4_u֚[,Y ta/D%69e+y@ah<2u>C,2 ǾʱxWRr?jb+h%Dء'bCΌł'{,j{*Y9(ĹBs۠X9Ҙ&7([. jq,5X\!y]y2mJ9'Kq8N v2̷VeX@-ovFBSlv7c2$eG!n߁M_FwAnl/wzЋ&{( "BrhC}ryI}y~q&y*/Iݟ]fFP<#YsKX*8w-B:[H^~B˫.v;n⇘ JRKR1XO6;zW]Eb*Ac)lhr<\$b);?Ȳ\y_>uՇic/ TK ʕ0n#BnDؠ9hmV/A#U|JL5]=.=A`+D|7=`!L>:E;mμ-b3FolчxiF-% S#patnƭ[BJTܲh!ifQ6Rywޒѫa$Rr8)2ݽJ%ipc顟x197RRm$*\ 㷈C{0mvݓtQRδsiLRc/|F7d.Y|1vkz s!ќ#pf%k]nZLSә~[\thqTj7X_/v.a+uy\"w&#Bg #[Zޮw؅Նu,88fT CTX\zHm[GDP/Fp&DS@'-x抌ɯ-b.bWV|6.j&7rV2gxmj0H@PjM$#.-f l fRЁ|q j8k O \??}yRͱ=vh5v:P8!용S5y8}zosY/xn>2~~ç?}u_{|X/yշ` G)y#D~F{ՙ.+`Dhӆxv)B+HEEȧ\nJT / ʞzgHbBgT$wj'N"8W/%D6{}r'Wrylb|.u׊n[A{ ]Κ5hYޝȮVA4=%d,m1L+GzX;-:x20O8Ruy/K ȶ'rZ%M*hebQ4hB&a)ے4FKqx¦a EqrOϱ\`2(I{C׎Z4O#a$x!Om>Ui:XwHS牠@yud-ޢjr.&\)nNҵշO,F[ d,!D 2\]6 h.10Y뤶j -nJZ#7E} 3[Bv(ݝ蜔 #K7?\hGf*4s7d>M~[iS mhp#AXg_ MPtrFZWH!a tŜQHA:ӧ[t-_qCV.V`Gќ$jml 2yB@ Vtl+kck(yepWCۜt< srPY+Hqkk",S9v~k״hhߦ /=LԚ]<<*C@WfD?x,_Zʤ-gQIWc|z?z(*۔i9f:J=.]vz8ԆkU&-9*l~+3شpU62n=X:$\ca67Xk|o}YulS.fzErA}zcTiuJN42oEsHiݼ .WEl^cFʳ ֖CsUfMB _״OyXqeP`gDŽ^Y<9l4ӱ&{vCh,u\8< *kvD GuB\:0ZUn҅3*x;99K367g2מS֝Sko#X[ /ӭ):z Rǵ_` tȗCy2 1z#j[Ն_T4F`2dK o rm6we؄~ ^XbU%^)/dd?.C[tY쎑gC!]!x<~ /I:ä7= <ȭpUWL1kAXuE#BEnoEY$g"t|n.gk X"NyA$ fޒ8N}8uly-6v9834<Q*+|G>Y R Kmabi'&3lmv͑-#hWwh}n2=J hS0qu@ &A>v/9h=7^h| t CoWKY *TM݇t`N~vь i~R+}YZ:zMD{zo$<)haGWq;b&(C5SS]nl0u*F-sI[8sQW5߯.RlۜM?b̭b)t%07hua\׏K:}>҄bԵt`+tb-6!ж*^!`.towg~ol=+sJkXM)nlg{~i1 i2٘@&5q0N*].pGWHN<v7lw!iwRrEC]Wk%-ÖАM@VϷYI:+s HjhWnؙ-1拽s2 ml4׳Y<7>pܓoBЦζW)0sſfx8ɻ. O[MƿmjűjwxvolNB$~W5 J6rM ɌꚊFzE4ཱ$,3w+}[lI@AI}], ZK߈E8P#~!ٲ+Iom/ø7'T`6B֜:̃6h$!0]/=7!XCtg:08p$,XaqASDvGSYfNχ~+pG_q\ߵ!(I`bG>yv Kғ*-tceBAtaԸ5#PŚσf}8i`_ S^.'>O&ta]|\4xYV eDւV=l"t5+Ԇ80Me1"1[P>T[/ϧBZBCg@"]@O,f_ԁ<kJP~U;_o$|R]b ~q~E(,c*[qd/ #!B ;\4zՍGQ3c8D( y, c/KeԚ?ϘA $.)7m u~iw5oN .7hIAc|;{_-nx[Z< q!~D0^ik%!ty3HiH^c !%]rM}e(ͱ} FoNA-3dKZ#k<Ԙƌ/Y{Kb H,Ė lI/!W=<0&$<  z8('pG"JQ1!zwuo O=5Ɓ0;.< R  :*?aCg~c<1gYs͕o.7XinP$6+tؔoewk-[>ɒts[*6MX}Uj(qҜ jȧ+]_@G+A :Ak(뎤Fa%3j?RZKEX._Ȟ Zo50",:G~2s7sJԶoSH.!BhZq@<izX`y-t[rpR=:ٙʊc+fnѣwD{fLI7Rשx+W{.?\D?sZ:MY*Fa(&z}[xIM -f[X@׶\_o +/IEVF3D%mD@PjBlp-pgQ\VJ4RSJd/EupYO*дFrLs2!+4;jM=Ѐf@x͌9R&i84w;Y+2֤~>jVȝZxɍ4@Myw mD/= G~b!RVMB"}fuszHb$C ]iJpb8ql4tǚj_΋tYπD4m61&t$v8Z]9 p]AFy|Ų,-N" gn9) A9L6^>FV^!gBRdRLuϬ`ZjPxN[OàZ WIx8o0V6-ߏ?:W.Q 8,#dtZ9 Y؅z$7M_ +Q{(ּOH.Cp }l! :,㏴f]]/ +%pe>E\T?|,`W9G7LÿMNpGh٥P\ XDi@nLAt7Z?@&kYϥ:X^xQaȧG-kqG+TңUn_~[+qGBD6hct諑HgB$/ wSC֛AިƅȐ@삺v?_uNe=g5: GNMl.ZO+h]/ ]!N`hauWAY铄k&x cZ(n?b2FCذI⼔dEfmʙ!UMM%oNЕ*##By74jOr%~:Ⱦȑ:'0lCVzV"5{:G͔}-|; y(?鉠x aUAq:f'zn:gAt:DnFuqOg2Mbh]8ǟǡR6XyrsP j:{{l9~RX0 kv9%ַY$HD$O} Zw=Z΄,YBd6 ސ0 O\ҳigr*XK[.`yʽA8aH\;XjTr?QlkXǵ[rO]: 8@_ζ&DyjDh: !0bGT+fӊ| {6RDAveO@_t=u(u`(Y #)rP@l݈ t˸e:㵫u\?Wtft9A·l66B繌OaTt=0][3r݆M2V~wc8)^;mmy='5kπ}71C{F;Ff:߯ V#9,)kt0hIE;K^- Gsv6 74~f^ģu&%Xy>0W}!2bMåP*Q/hU->tHu_hMH)hl'fL:;EOp)bL8 M=@fL,a=Ͼ=V,M$U?hFBPp՘CQZwwqep,Ġ RL%#K\e ݀Ji]$]Dh9 ԇA)6ў؁~#mxpn4ҀȈC*nlƻ<7㋇"A巀lH^(i&noJhDspr'JzȕJJcoˏR;{^,UU7o5\;&Ym:6?^9*Y ^d0fgp孽wG2f%}Bym @֌JC+71.:wm%WbPzdSrǾa;Әd,Z#\.cC,@֦̿FWo؞+bVlD}6[)X!]xs BAи %aߝT1gʌmD M!Oֵ9l1j#%˦!PG$<\5sCxdg>-ӄQL#Mu$l 0/!Ğ@l\?JT}_5wc(ńQI*&1+>m!AD 4a/Rܤ،>D`v-nYL`*7rfqe:a%e!Ŕ $?K cr7fY^F.p1?%G~'c 5M.iXʧq)D舠f4`rZ+R%u=Wwkd4YSQwY dw~=gb:Ƞy^ еR0UShW0+bJ-\zs+NE >*[ۈeS{hx2̚Y{5-P>ueok_ T+CQ:rsS,%O>K$ 6\hE9 =m-8(T}rnG҆6Ϭ,V4=9u{L%GGY֚Ez0n!HO.D=哩]1ҸO"?EsAys }vFLq H1K@$clj\ٱ؈;˳g9s\™$QEE6G[>\EyfL2>q[!n@At';ļH{x׃,.3@id'hQk}4/X=@Q쾟}k4Cų4:wxU>+HejU5/\[7xU2'ضXNI^x<7wYHXR8.񚧸jcy!6VwK)jhQV_)Džty*ޢF߆?ٵ;<_՚߰3WrnE(0HL~_53Jծ1jz=O!|Wi-B>OY`kUuȣS%s6NA.Ջ@5Y9ŔR0$ ZAxFcj5ԟ Mxk/˯F/ LD?OoyBP7XdM֍AFnV:j՛~9ܛ׽|z^0!AMe_dqo[+[mq n"Ja]VƳɉkx߫KʢR*>7௼ 䙘R")bGnM4.('Ȯʲ"bO *=(~=2b晁3ee)&m@"**ukuLuh$Zbza,s--y#:8^ǯm.!Ʋ5 uZ0-Aev^Y2i ۦd6*2#bDےǕ6 y?k1^Ǧv߲66XD0Fr+oR\70%${!ل y5] SYZq3҇xGz~2Q7TC)$ Ȉ'N`r5zdV'Lknr4Yzap n(m4>6qsa%?Y?}ߥ:Irx4O#δ44d(HCf5;̈́v=7hYl ǧoܗN{Vp|cv/_~LpPJ tER !N2LYG<^IwB,i1\Fk{ZxIhO ^sgXEl^q٢t[o4-f pDL)l#TR?oNoe2zpY^?%T%4RT0xJ--kyP8C3e=7!h@p A<ʬfϪ 6^z#7V (ξ3o? P & *˳ܾVyN۬gxt`'8Y JR,9M8>Ke0-=,l%IOe mr 5Td3h5Q $9/h Lp! mQ_4H)#0./hNO8Iz>go ͘n mPY]M+|< &˘-+YkzVR;'K@u*!gr,CE}X DPVd\UMz6{VZ>!9Ϸ/eZG`]' 1 :_\dzsyeQW_HJ!LQ)@^'iǦM4)H{d v) Qi%)Y-{ߝ޼sH*&_7/%Z,,l2iKU L|򤉟CU OP瘒Y&ܮ' 8$A/VvZC@܅qޚ-J+8}wůWe~ ̞*Z82._>ltf[P <'VMbT-^9.u&27^RXֶ~_s ʎ) v$oɏH:]iLXŇ(ǂHU1 .BPۑȠ3#",6336q49 2a(M'!<╡*[u$4jJ;V겧bk0ь&{:JNJ@:>-T4b׿k|S-Hh* ̞Bh/{'R D73kͭD:sFs2"/CL;&a>gK@ȼ{wCUR;{~~5~Q!U.ū3Ё)ib唼Egɳ퍘9$Ejċ~tei*o}>;y:Wqw/ʤDE|-hiأY#;> m>x$d &ri2zJ4]xPf GQ&fF\X?p./be:;)"<2&=~m1} [RHӟ9EEˮ{['/un2yrzx RM iϺ k6"Q\L*aI8~ս7nnޚY?~?1'x#Z!ƋYlJBc9g]!'&4}~/"q(8W,bxT7g@B QJ )Μs404CVgj$jsaXkX%CMds/_sw;IW+_ҥnʰUMgo΢lIuI  9uv4Mp9Xl>/֪⬖`4n{nsd][v Ijan{:V'aԩDH@czϖhADu6͗$io:n}Tf |&n9N+{|U-pt |H÷=>cjzpHw<ׅS(x:y΄(՜VYĊG2A 6.FO hj@sl.U"TG]{nVձQ/OQixg8D,b Y޹qY C ,5]0I2+k̖dJL)1EˣB-IFSqZbR͹"3O~Vݑ)'Q3bca =jYZί5e@PFdpڻfFT"&i:!̡AՀe{̘@ܝű`4d!!ty>sNWȩA7נtKb㟒Dpמ E~ xm&Wo7KB.2c=[on˜Y^ mj K{Jg_$%7 ;9/S+O/{1l;!:Z/In?%=V 2#M18x3bv5Zɲ1^t٩kl!CWP#X_PL\SMFpv 'VT"x֓D_+ L`RN0_a2TGR0L ʫF5*-+yU,ܐg]Scf˷WdKN5fYJ JY E؀{BDH)#hcJZi%JޑKIcpjz{;F {iKqz87 {tʴRMo\trw *0 Y=0 `1>x/ɷs=[@&^c&˃x#$!%oοQbߍ'٨9tGיH:3>SrM0fMok:0Ǣ\>]xfW~$oeKR3k)K|׮I-wr )CU3G 81fyC*! "i\4eB>pt3yƼh)lL;U[:,兲zT+HHfi<B8;mFT=8H5vW#)LPng }b<8`Aie:UA&z#.$k'Czd9Z)#-H+&,xIą6x?`T%fthZ%#0Dm±:-ͭivr^v2.@Ė;.LC=}} ];6`|^pSg t,zw#R (B19 ?q!18@@&}W و[ -` KZKWkCB aI1vNvv&țnV٫#;֚1z֕Ƕu#͡ R/zqt'd74^ZF>]",`E.w3;,gtnzҲ_LnQif)݄p|Az ǂN[EP^\P&uP,\n87d%|2\VڝΪPB`ŴNJV260_ոqu5)nGG/v EsqxݿD՝?zʟǏc li~x #jTjJaܙpFGyy!}\ٺI78.Ey8aG+e U9| k5j8U/UmP.;=nl\u9ǿ2qb`&a/bk/w"u%U{4NfaԈ+9(90EG\CEQpD.|@MH4BHw>߃Mr9+EUm(.4a懒!Q:oo#@Q8e f qPE*Ðp%Ȫ}57Ew41.l)ŷ ֜ǔ>׫\3C#A2T_w[՗HqCXaM6 cxa伥}eztLmO;\:qqey+lƱկވC־ >CPEmD\`\89:giijpQ'C#N.TxX<)!oCmDR@1pR0L\a>W ȿߍ^;纱K$~0f2Nc<9GTϭq>+g(!qקR#}$]C?"7yOxeh;K}'ximeY39Eƻ GȪgVkL5KNp."\7Z ؀)j&`'\T%VXUd{1 AQ~HȄ RYM8LSU*L<"P_]w%NDGApK[[`qٓ0Eb/Wzl<6<}UnӁK}Uma\h[#T" Nz񷚄(4GPsٻghjO 3o+9I2?D-*rNTv4ezVJTe n{L+E w0Ӡ>!"_lo,Tw y"Cg6V(!a8r5^eFt@n9[m>f"c3Wg{oe6o#Y<8^v#ۇT;ŅP%?}f"٪I*-DRA RPUzB {]}(=mu vFO UW Ib('zz;M5acxL t2r< l0=pVAnKF_'H p:RA X#Lt@,ty nGe ܶkʀ@64OAfm pR Z֙WFf.pmmӇճ$H1C !8TvV 9qhnƻt}%P)-[_GZC퐧a{b풻}kIz ?1*=X7_x ibʟ =Ŗ@(j|!*6L:}|T}= h(6=JFUQv/G mq>:O99MkX8Մ5uy"&]n6nET \HO !Fr4nʏ0rֈ34۲;(}ć(FsryƔ9ѡYQSX+koP֮Uw-bJ۾`،97f=5r7bx\݂ Y~,C)nmPyJ+ϾB]-F˞H'rypbfYS"Vjq#@r; ',RJ|̥jac ) Ou7`]qT5 tJh/BK+;{jg ] ~;6}`Lk9ms͐*jUY7@rpqeڦx)1*ae{2li3<2qbTl7.Ft߄zjsXR{ 0MMnV|$t|3˜j] <|2,09L| vTU_dsԣrO{6;FmZ+^x il}yXek%?&@(MyTyцt,2o<Á 'fLAVR%7"ņy坛cQL?l1N[񗝿 o͔ZP2x)RR?>=sc&v"]CmyRl {٦~d)Xʋ+/zlVzn /:jֲS$ȣ0=쭪n<_vɇL@\#exGMy3DQ097)GiNj—ҩq薕4#DnyyU "#Myb,-FzE&::xth3_ȕ7 aIj|Dw6 Z2hE2cf]UD>] Myy8v&J2$֬Bqnsn3PC fTE᥌S^Q @  2g% "*C(PNY␂(1fSHnV-U7' @Qm]\U("ą ͑h#lH#-2˟h)itBcWܗƋŁ5qvD>z16KG^ v3]F3CJ-b#WqR utB|K~mYmꝐD[?ߴG(")S"1&ctƧVSn4W@=Xv7!ڵa2.ڪvѮ\p?6xZh|dRmԖQ4@:U۽J?Vn6P-)=^2}RJMLtk 1\91RV&'34!/h!9eć0ϨAi cH0睪X64F6}=a `fX?{8{Y{'C$еAØTc^ck@^v$^__fWkfTN:*x/q _gd{9?EE8Zc{}up%95T] ~=mGlk9Տp& jph"CxsYb.iݎt*1NЛ@KaW3l֐XfkB/FeO_l"chNȓKV@KAX] |3 )ahSWP.oEJxVSrw lefޘDIR䨷 ͆JcDRbcHUe>[kW2=*8^uSiH!4Be0Қ%)y(U%"i/0ASo=е f :B$L^΀g{7;]=4SǷ'?jHS.`9dDJzS}hж3Դ&qoeypW{ q81ƙ5XNeuX%1-SU]8xtx̅Y@&FG b^SMx4b<2]ψц4a4GXB^ 0yXb,paPP4!օI<~zW8ކ>M˧!±X汶}3ӫC#AƁO$GA2 TTy~(y?W6j+.P48JV;K+$.$zm@18Zҧ _[C"SK{N1O:8:5դ`+.f mkDJWv! +xbBRe`9Bb&k3t@yӚ`_Ř"S!qNKH 1shȔDTJ@O7n\W0̤¸2B_;pGnq;j\EoAxμ̊h]Xj֚;tndV[ J?W0Ǻ#l?ݢO+RD8O[Ȓ˫(eb)gRg z&*'#?̖M/.hi"ͬd7yy蠋0ၱRwW\O3_ځb,{VO 3„9]ܵrG)-B)L -v|R5!򸈷 üIjwo?dHz=\4iu$ SSiz'q`9(zLnH.ۛ4!ے>YB*Te& u{t G4dLA"$ubȯ֌p$/W^\#ҵ;( EU &pGPBC)y6HO%nnid7hnh -6EI51)ץm$ean"*KH4J|W-#lk3w"6Q0KrDAIuS^n<"Hk?rsKs.JmJ hu8 : 0#33ĵz_}~4p0tizp^iĄ!LȬm7B&CaSв`:֥kvվD:hleH*b5jeUce.qhX 3 &oa=eV=!$ANϿyYR^~@(*R C1(̓4̱{:͕,IC󐚄@i9P5#V.\h{Q$"$8BU]qe1wh: Zy8~%p۪v!s_#bF4Hf4YOsI߮Ǎ}| |깴mw^kBЎzxq5Y,zݯ%9G; xg2kSp%ݑQ9k5pOw/sӟ~TKVqsԩ{jbBTpS%$?켯UΕnX[9*_vIզy't/ 4В4sL*Te\*QILz}LSХ/?_>Nuqcu [ЙzY\Zgt:ت : %+1" ٭% !u%J$r=qV0N>N]Hos׬UJt" Q Qh L\wv^Uc 9;oMӿu;Lq(f]I oF; GTE%,OB6^"p`(Q<HuJN"-@X=u:"JP0q}5zq E\hd|l@5.1 e\=,@5 $m۝͊xHcՙk|չ̆}li)`D{G.28> ђ>yvz[%!=MB:Y3Es L#<` JɎP/Sݾ/ [(Y'S2²Ȣ!C( !@0v>fY[XؚIMj$ g5"fi4_bbamb6  )rQA.r캆֔yJ2^1$=Gs- tRhIS-PMi% bw~z̹|5TYKn&:mM=kwsCG wʮ,V_?e"4Fʠ0c hAf8+3ɑ T $)#@p7d;G;&j1|Ə rg.i4z eC24NVOai=FdoB1h-QfFwH}y,%3aH-QY[v\AU$V_lؙCݸ"yn TPKM_1QGqC[ˁe? \I{!״8L x|h.4DH8Zhi<8WgO8юKAa4MwzYGVG2' /YէACH]$1vp[(~jZ| ḦMz+1 $ cyg{Ui'u7PTF?*l\ OOL(X XuLX2Ơ(b2Ӟ?sĥwXX8t!2[Zy`̓1ٞou9&pm-(f~X<|A͌#ӀF#6޶8U5k D۶񃲈4* R# [z3a=^Ɠ¡e04G>d`>2>itqG@N4,Z@ 6ZEg̶9ɋз`GtWG#R^8Z՗Jg;ҭB6f7\W^M (M~ɱ$ߵc1asJ Qzrr#sJa0NC%?_/D sgٻ汹odf7a/{zM7NIJpW0~oaGP@~"<:|8'Ts;W̙<qfqPaBg=$/eJ _ٯ~8%`3yyG~-STb_Dպ.O鳫n:Ս~#T>T5\% Y f2e gR޴@w<|_Ȓtq '&F<8p(f{Ҁⵈ~#M?l ɆEZf& Y%NkC3FW;͸RÅd?.!,(\d"5cr7O A58Ϥ~q3-\,:a,Ւ4iaΔPPf]򝢣n` W)#D:jDJ)WmϨ.`USpv]Egdm^94q˷^hxx&bkf77Z"LE ވ$I+\|}gM.rMϮg2-*xriZIWj z"+qhpIX|*6f]!S) M?5ي㍪6Jx(Kʨ(WqxSxJxb(\G{wjz5=!eD˨pqk̀j(iS>Df78fAiE70Ɛ!ם̐dODXRgX f3-H%%e=z@F'z?# xP[)|`L PXOּTD~{>t)$a,Dl^lk5W?"Sh<&e}-g x_]|mog~m#>@rA!P'_=Ed⼓r}w*b91M["N6ԓc13X bG^=e;:y+*xLgݣH1Ac#mYC}gEN|w d$Q>iP kf+s*Zwn!Ջ/F (V ~`Y!v2 kaTl<&ʪC :a2&Y^=(FClb#@x#@z0\^],e fCOKgX5[֣b5s <^_VǗ3|V ൥.U=ʭJ!TB1uquaw´1+F&A$ÉqJ쾤>6^k?]gzVvxUTV0"4r1g ). _mH=YJ?4B S}eRJfpsY}Yv1AN \_l̀EO2=I|3)bz KYchgD \2$1<@~S . NvI{Q6/K vH S^d=LKg]6ڷ<~1)OyW0PP;ZįH-_/sL\tU:[%*jLmYʌ?`ޭ01g5_O0?/?}CI? bTfCz:aZh~DfJW/+>)csD|ǎg{}TgIut‚n:M{ zŢx {\EuS<JQ|[)]hN? XM˹gt.fWPى~a(q#\<=y^j"!  ӤHb4p#'m1RdD|`7V Q%q~-Ł:"$RvM^BX>E70,2}ե"B&K#L(RaGׇ8GLPA Sڋ˒8bWFGGH?nKioaa>9rNj٩ҥޭ㘻tB` s kⷻ~Jj煨IW,h|nI-ؼ R GS2bYG *0Vb?Й\OԒ") AHίUF|{OЩEO[箼*y{ w db:V Mbbܭ/M y @wv{ ^/ .^ `tz ߘ -9b!}cF?@n1[OFbSԲi1?msUW%&Ϊ q*Eڇ蛪f#:=h^lh\`@u/^߶/y_pϺ̜lx96OZO=x~;YS{;G)xݮZ_ 7/I3U%8tBe0@T/f+񌢧Ȱ R5BP[A^-Փ N >?ÿ́8PrUU+5 B5^D/'܇Wj'm)i̬MC0 ȯ#:2;cQv-) @v:F-ϖ6J\(K1Cҹ}wөZ%#aWkTJaMEAHB'6DyB17?e;z.xI 0C6hH$oXe@&KV% ~|ː[P#.ʴ|*muZ.SIĉ a{T9a.^Z}YU=i⃾!lQc@މ}e,s|Y|wZ,k@^ulD]}/׀hA,_2臽CcϭWaw`4Po9Vim۱=)N#'ItjOp#շ-C3 Vl"rB,cQ#$6&;x>-e$rXYF:(ʡ^/O\*jjWt;{+~ Ԁ)rG;cmdح;jQI]۷5Y0ZJ4~Ꟈ˰M,RXO0mF,_] ꭒmi ÷1X"P{͖_TcU T[B~%T)G$Bp6'd!~ yP(p_mv _}+k:z`H,C s o {V?r|ǎCYk^]frj_=I>nh+6E[Nު7{RHb7RUUeY-? .̈́>|2FA憝 hx,H,R0ݔ[ us>Wz{-`W(V*/3J֫Vyqa=*q B$S `xX(:IӷW!x 5͸PiX\JV@S?7=Si&D7O!rt5hQ-,S.'n{z7"ˠ'Žr]cmw:9:kH|!7ȁBVOᣄ@kT$MeK??:X ?{w=( ,mW̥~A jIV,TC* Q 10*T (oppD@[>JX@KG(mLN"9]!%i {/P*GEFZ^][ʗ>ڟHb}0oIu8q!Ѽp!0T}TٚkXluoћ[ JWeZh/#Y9U/[@WmHtWLKU7kOa{KNj7]#x?k7?q+x RAo;CO2 TJ!ab8 #2YlW|D 7#ho&\v 2qFC1xTq졆);)ˉMn]НҊxI}f7t[_!Օ5 0T҃ˠu.4PNΘ,I6ipST0d dʉ8P+<(܉C%"0-Air^I n,9*EL clGhhR[+ @}yʊ|0%HKٮ .H"6V#^9Gwu%3EhTyۇխ%\VF@`>39]9niMzHC1a(K*d0-p*xn"Sd4!Hfq8RVĖ|/iDŽRJa)ǂ/`+uߙ5S0ep YdZwOoKO%](ֈFiL!;;K;Q%yYh\WA5; kh^{m\):RyTDž5 yy6iE2wz5h|])1nPŭb څAZGٗt.2,tf"jTE\go3Oh~聝6l\Ok_SZ3?U13oBI XERNwe\@ ~G;(nJ8st^cjx$u1ף_+.Q35U|@79[%h>~bHĊjB%k_ b|4I^,!/+^Imi;Ljl;sj 2ROôw^CaO9:Ϭ9Lxq ' MC Bo 入uBܺÃChtE0֢u K'DC=CPr+S]J@P^tf(PUgvwT, 8jÞt n(*GX P}nHNo۠;?,)9ٓPX_']Kg|} X3S :sn`XZBu$: xy<3:>/À3NǘCTGT_WXl&6T6oԫVLDɛg M L:Kӡ*WLUAVZ ( %KH19Q z>PΊ Ppx6Eic3-Fz*[Bu373Cn ϒ-,p#j;-h)K?#+=MJ?#ZJDΤ'$lfAO~T_7ʪ8BTsjDHF):Bu2>B>͊cPp_7 5'0G.CaR ݰX_%CW uҍܙoEwߧQ^XV}wFY$iR;%6s6El l :]+X!=́9(Tk*%3=wZlEzAFG>^ ڰqK9.o -.6QZs) L0rU/ye$- ^2Ev})-),ں҅Ït3`v,tSQ;Ɲ(i*xY2"9zsɠmt=ӧ(WąCߜs|zcX )$!RĬ@(،+yKSԠsd<@n0bDYD  H&|/%v4AF"$bALqaS#<I@x,bv*m9T"\DHX[Z,u3d8f;*\%(%9lHmS;H@3Qhyf\@T=]9 u+(%z$f,~ifxcuL%e*r+#hI3% řf^o\Ԃ".(i!Vx=wh0C@hUEQ2押wPc \yīmmŲVB& Ӿbm}T! SÆ6)@BcX/_8/0,XɬE 9$߱y \5n.5Q:51U^JT]ŲmIbh=/ϛ['E~\dHJȎk@H=ܶo ˤVc@qb.f&ptGiՑ Kek|~$qM=[-9lmuyϳkPsْ6lz >u\N[HTkhw@LJ9/5?#g6`҅Lfy LyhwaQZQsZ'x9T}1`{эٲ.?~DC`jRS 6Q;K8)]WR R)\Ť,/rD8 OO]H%N-Lus#0{)lQqJYkCC>vt8΋_iwn3 `O< = Zq?MF"f@4zB1Ҏ!^>?8cZ; ƉsξP37{4MBZa\h={՛[54oCL5 Dß4kNj5Ou+@V6-=zEڼ72X[ a!0NZ4UHT,ɸϽFaR;.u2[f~pRU(G4vFqPJmLqUm0ީ`S:rQcg` HAwW"lp5ziHq1Ŭ)8(C쵕MV :uܦKtmA^#bOLҵl~Bs޲ϝ/u 8:3A{`Shqpp~K5sUqxm7&YEMSkvpQ΃{ S3XKG.t8Y5'<# oΊo])R]MX0o-rcse}y YˠhaqJ<7ҁtlz#\>6|4qAHi~; "AB@4 c>x?4G[=}//\&hBOGУl  x"3URS0Gq{ D|Ăy.@S$INR`yVA ) !g| yKTf@YSitsd~B)rhY2 ?_^BqKBx>`zl;N/_{N#'Äg̈́§JDCIy;uD`>TCBmsHG i1nVGX RSYB'#.eA vנ-ȩvt9OI.% VP&+KMl% Әڢ5Z]p(ߙ>yrxo?[z?MprD /5W ^>n_q?06ݛ /;\3je .983L]1ZnM Ww¥m#~A(%x\,G7""0~o=t֥螔*iJۧjǡ{NjYwq/׆YdwIFÏ ^Ң̵& drd޽գ/6zot>чbBR+E&%,4NyH!c @g;58H"&a a2j(qDH:RP> i4gb019 FbJ}i};0a4mj`(mNhHɿ ͎~6w&=ʌ#1p{Wgk8/lqɤ-mz4Uy7Ҩǹ6Ud|5X @j>#N~Nlg:L`E;R^~'Ak4Bm:eh|&KQ:;x__v6FcT[kAqҚ^X~rѡJ#qљ{l3ёI8XF hƔSGbI:9tHAg㘮϶ڈw xCCpG7k]7 +OTM+G𳂼޶MPdq>5|:E1ľwX_&vȊ+'\WN2Y7!/" r.Y̢؁AAhs+{Fv?9j!CK@ėcI oM#;٫1_f\A\ 6CSsּIpKBzIPLK+P5>*P<("Y:-NT 1$nwLAx0goO=++Vv.`@j1#DRj¸蟺xݾ3Wq 2^Rn'fSpw")(US1kKCY.#))xxe ch6}JX7#%^ " %HCB y4(i1f5E~ib/zG=# .@1;J0? {A_?RѭGjGKSE??+zCz 􉟮\nDDW&=̃uvc* p S)z5E"`k yҊY6jlw:SHҿ_/\xe93x8L@DzzOxJ:IakU8{X&/.(S ѱG ҆F%J e};+԰x~5hdD$K[DD0ɋ"Dw4ב(&2׬?[$HhqHIEWpnAC&js}IJ+<М ePa,Y{8:. 4tuմwM-wE!9\hc=&2}bd)al}R]½)w?`mg۽t_hǮ_+K DE RD2eUl^0=xSg3(}O97ShUfLFnmsW oU嫞0N.8YtRW wn:y UmTFfNNJn2@{“P@5br8!/C&}1yy@uޖ' YU{ԃq(2"/)˜B9@Pk0kmQ]fB"2{j}c*TaSjfY*7_66haRFߵTNaO4&Aɧ>ţ{ z¯@dyVN)J 9X !\lw h b&yu E4H@IgkZswnTߐ*ҌV6_hI42w6mDRؘN}Ӏy., H|Rj|aa $&vBy[z_o:GGHк]Ld:׸Ͼ [4Q\47n,G/FeZ`u[]qe޷:syENɒrQdp fW I5TZ,rhOmI%F/6]2'Bi+^g"6v?)\dKۯb$gzm evrHv$YZ&Has5Cac)N:ƀ; EO&ʮvC B*Vz8\ۃOw4l!?n?8hn3{X82+VWV FHv8N[ӍwX MRci0(O/*4e@'Z%Mi}^ImoV t䥁I˻Ej|dMfz*1<;C!܇c&3`~luK21N [  C13-9 vs?{1"̛,=_?sРXlmlNh6z:7c{_= |r .Ӛ Ss^KTTz:#*ZC䤨:rEn{sө/kT8$*$7jL ]'6YTASeY_tW0C=z-/;- T9$E Yyg.sq @a"by gkPup-oPt4trJiSOnLGbV下KSEdSMB1@6pz 4sU1vuҊv+mƑ1+CoM\z)?7@ǁW[n>qolmC$W|mgr`+Qi=xª9".#b&Lhh94|,;c%[* +%מ餟NA_!weipa[#^1>ŷWw 'l<|C/>0#TPXTΖ(b,[2~^\#-kG@} CVԤUZ/LmfJ_K0DOʽ˕`+,ny^}UPtQ+B# bc9o"<)|lHm)ΚQ{ \N$>V!87=Da>[̲{c+΁RJϏ TD@=Rl%z9.-{H[ѵ`ƃaPBt{i'3Qy~zٙhۆZtd I)g ͍+-ڏ~9L1JB yyQ˺@<p/I{X<},;4ti=j THimHtK3XAƍ LO?NȾEeJk az'5C_fK 2n9얪2=` _rɓK{[r-Dvs^N wo_?.wo'}12yȰ4vި,vD W*.f=]A҉al'Yj=V%wMPp-8eՍHD*,y5⟬_/\ctO =uma>}YZtcmx%lG ȭk)e!k܊i|]9-Ό,9[dN#p^ < B0')VwC@dK$5b}X:{ yHX [WO Vֵ5P6g!/\X%0ՆzAܤkř~htOW!VaS A\c{_-ic.6coӻb[<^ی~Gh0`$8,?hh3SNHD#ʮKdK ZȄK Ͼї"DaR<@* '$ˋ} ) w΀\P+WLQ 8V]FE8hLRrO>#pQ`z+&I{T'7JdA g=a)E՛vBSA@͹ŷͻ({@B{bxT&PBf7{>h:=t,sr,PFBsÿ2< e@Mcٖ.I}M&>%@_ &(=<) 9QOl_|#,gBpl ¬hN-LD/虡#oo&8A"EycBKQ3ܲ3&\y> J Pr>v­?>+25+C%ךf @m7;LoɴdWŵL hU/?]r }Ky Ug ~$ | >!2[b58oؘA`Ǿ]$IT}PMi' &Tlp6}oMMBzUsw>_6|ժa̭_V=f%F@N$=υ1@)c\dF n܆*zjG`4f s[P ^N8xя3W~_>w~x<44cQՕo?0kOq(F{W_fOz8;Ul0,N:2|DF+yNdgErc?U_u~ڏ} ӆЎk} PLUy1a9lu/. žUӏZsfjJ,tFBO[d-NY,³"||W%*=zIkwO̎x SUJbEa SbLw4 ҔUEïv3E,sn[CuIƨKhJ}9/ RH/~Š,ޚ@d˵.K;"s3]I,%Qvjbo s"I&/I T x 2ylNއ[5& iu<5ŽH ڃ=S5oDIWΘĺ&.Lؑ\6^[ȆE'8R ;(ggS[D)CP] 90l]sfnm5rq>H@A"3x{y~|L,b;|bHsth[X71A⫹iAk$:44bc}aȪZf>8a'8'IA`}>Iid0Č/6^ G60)\wZP2*E@m^ %\Kl1cf#h4AKxOwt̳puY3d_1αR#4v*s)4+> xufq7+=D3:'Kꏈ{ P>d6iqG!wΣV L-1Y*,C;xE M I{T IpL4ߝ}ڮ\;vv@pI N+g5Kdѐ`SsL L4sM*eSqe|,r6:tGH/-VXNjw 2FM%ǽw)ާDeY$4&_4ւ`?3x |5&BO7\BYd(.8x`-K[bUmuWJɶdG$ˬb]{tN1zwLzrIW%8P^ˈGeWku4f~ޏJk-hlwkpzaL뽤ii$ FNpBp!C@h{QC!}zVaZ}l T]~Hmpl ׄJ;ٺ$U.%|򥲒얃Ӱr3K.I/;;lGHw!0@M/tk4"" {4gLJ瓋6Ѕ85 d͐!N3 Y7/G\V{<&MX;Ah϶ OZs CrFԀ*>'iZ ~xx&*r_],e"³Z/^cz+4lZsPN.Y_n!j$2#_{u=*)07SZ;BBN 3Sf%үSGۃj&dIZd _ xm,pyTV |w;q2ǝeDLa`HDNBS8Ԯ( "ў  ZP mÜTb$@xvys w(tMN!.L6H/m# /crl{Q/ C*Z͝@;8SzHKNq.g+d7^ȋxL@KaC@*SgāH1r8}2$J(~ŤNmb4DI{S3D pY7W˳1si tĞ})S큜52I!PέDI>)[4"}*ޖIXlopW˗ ¬Zi5aƢ @%9Y%)Q xz_G4{ ئopm\6+2ݦNVBo(1llIc2t\ $"fj K]jHv>Fd:@Wyc/\49ljr6Jq:a2F5-燲SxY#vx=k-lW6ZuIS:K^[m՘p滺r?f2 v.Sf?p2OA 5oʲ+;♪[^z Wvlj˭VMr9Iݹ<8h-깇L$'JGDj3A}PSkE De2ޕf#tBr\dKj:Ѓ0%kW\wg!rB!#[lU J@n-( %JO|RY0dv ߰-rkmc@1\-piAi"2]2fda2%HjՃ}!?V 0<o -u<㛴Y/3/ZIvr9P͐F,ʡN .v\ۗ ղµ YraRKBX&oLp@F)ێϨc2I ɭZ 3~qm)UqlE<V !An{a OI9!B (u@eP'v g8~dZ} ToW0'@"+ rY X^+coBj -YF[/|4ʋvnpчi,rOOsX&B@vDTIŤEc  L th1W*nl^–fqѼ uΚױɫDZҬibOꔚz7e(H ©Slk6(yh!~c )۠r5j1GQcbgKSc6EYD Bu rIg?~>B֬l4 [﫺v_ii@y~| *ߔbe7$h}ۊ?:xebF/o|8%^̧y„a僧ANE뇿ʸ €`ɵnř)^$FWa3Ti1P'Wj鵌cXmڄ*Rz{6[Lad6LxY/ MP# S+L\nMg{VKl~E ;P,KP peI) ܑe63, mZuS;żQ[c@&KK0C'Cv@;O-vXō)4n̦{['·:R/u'gmqК+-^6x?zQu|e[HCs! Ky!I#8gC7]-R:Ws@܂ÑaqR#M_͵==lFvS zYb쾩' FH/q2 n$O-ařz8 ߼y+KW0G[F]fAǺ~N%\TaX"Q'b0#FE #On8K >__/"hiqt @33͈v :D6pvEgKnZ2ϐT8byPߘ-'nhr4Iosw/π+1͡j9QYb#_uI7hK+|s:r"ݵ1 ޥQЪ:7DgN2)'&%ʞc-8?"fef_R[DJ`lj襃r!~鿫ZUKy@ yH: jSelfʼnOxw0B_kd4 if?pV uvBp ihl5d̨ҧ:a4SA_S`'&;b*dg05;@[>ΔyhkXq(;fTj(Wv!Gq2zovMRW"NڷlZyW1LC/WAȬPDbF"y Fȝ"JKtws8#r v؎z|Rn' ?=+Y> %a2~PQټ̱M͓whի3-כNc fa?}?W{^S!?_vfrLJ?f*V >]~!SW \gZ #-ƗnFCqa}92P7ߜ|պy\uX v> lRtvMF)9:8.)>¦I3F(Ge[rg쀒؛ k䬆D^8.')N@&ƟsY8&Nso)&qO"7Yִ^9\nSphCfvɂ;s)5#N\Z/FQ548 M^UghnD3>2,1\X&J|nG=>P*VDNJMlRHD;0VNɤQ[d6t*^E,Nb;ҳu4ݟgWQtU,ea?H4N uI7m^Nkۭ>T56] F5a<7.|߭%gm)n|HP?rF}֗``m=DΤ,)jcd,v^%8"ã\ơ]G"#kG?׷{fs"D&Ckl˒Cլt,*Sj/i9FMi0<|`Ķot c?03Vl:g1 ,HOxs.%eɿRuѱimhOVv"q G%ZaJչ L*0cN[cn{ :&Ү1BrI]yUj$m!r~ޯu!U _SK/^;׎M+dEW־bggOv+X+KFOy.g; `&os JDA:6yFbTm#k>-Y "J&`j/Azu&H5h<%28m,.6׳%4B6$w}zYiV>;U?o3vve@cUyJ%.dQ)>`E.~59x,p;1j Q`zZdӓZ6~X[`<;qh+J*xD#QMx+16NCR4ݝ͹^.ؑ;dk@SdzP7,,`l1nN/W4~Rq6 (Ht?"5Tkޮ!b-(o:sWYgV̥>ǁ(Q $lCj)_en̝c /]3^$GB˟&׶Ep҂kxVM]ʇ K-9fS4"iT1kVNiv"DrNf+KpS ϶!{DJ\$Β+mt@C7NٝiN*tJ5e^uO' 0*(j@?P54&{] kh]I <'}w4,T I\ j6Ҁae$&)r` ȥt1KV*BwlJi'UCF<_ӰQnT-Rf+fd F Al5PW0:A껂\ yIx+hl@Qp;"PTM_^Ɏ1OՉ3Ѹ%4)`tO(p+hu1ÁKAQ :;;a(8a'/Sxn0ݡ}:[:93m~|Oz_ӿL\*ӊRаucrڴT*ˡL.#`k E{^>{K Emw&Qj[tlP}T;*F<4PF9Zf ҈CzYuG`"\mWA]KFS$x帏KdD^T[ aԷ$ tZ_.-Sl="X{%ht(^N,kG ;Y Ƹh˧\ԭZO!,$~JSe-M4d3<:WNSnSX"̛qŋA e]TMʘ #7K _VʖƷ)(b\g %}+]]{a$J7uyVa"dpŚ㻨,ZnW@Y)&lg5CXDQ^2Fr@SlDS|Xm"˃4ЂdD̎zR%)$;6rN\ }tCz&, "n-09B]t8_{'YUPhE;9!ea>2ab %a^q3;a:Lvώ*/[:DF2?[2tp@LmcGK{^z h{V=9WAؔ3i3 bMIcpf#m2 "e~ sG.*AVy P"@r 0jR-7@.}M}7DW8!Caïۏ3.;tEK4G|u #ZPMse"꙾j?E Ph-b7Ioc>%J-rd\ceR82sx\T%x!_қYҔ9vÒ9iYP1-$9C¦<{@n|1ChaMTK!JJƩl`@=ߚ DRt?!*,MEFf` `|>8fb|-X\Tu_mf ԠFjክ# KcwQܢw:490b04E櫔\e69u<΂J𡝏7j*\RzQSce*?k ͢7+P=Ū!|a c:méicģ7~GLtM0MςJȮ\$hʯD~N¨xd2ƂiXiy0"B)Z=IS^"I@2Oifg_]BJPHr&9@jtU] 7f7 ns v}b&:4>y&>kYN[VZxsj#vS#jp hÌ ଊwh?hfyWF'[2%[Ne+ϙMPӁ m#RVoP;.V܊D/ r+V@Vtu.8VʓM5CJV4.tMPEyȎJ)_ʢB$nzN~u9ߪquP,/%>x<:R`o:esl9AdE疫#}$4$X+7Odeo ZrWѿSmKrAE':&1E$-"+ 7ݬрi[d(P_6 U!OZl@3xFg tF9w(Ϋ{ q )>úo_z/{$< LoP 9b㗦9cȠ\yi( d >z7o_j2_ln޼0ոZgfKPzf 9?Xoؼ2XeRLs,ҫ̹x i,+bYK1Maϱ=XUL̍ |ΒA"P9ɼlOZ;ݞFPf>[o2ķtVw~j?_cK1V&'д((a-6ٗO:4LKftoIDO@?({LbK^Ԁk ufn븯*s\ImC;ͪѹ;W{tQil5 Q=< )C#֣zbw5f {ՋN5iO,ڵʔvvPrju& (OFqsr+65GJHm\+fB☒h//<:E0v ]l\)Vv]G>P:ʦj?a!CtuFyo]_rjFZ%vj>'A2u觵[5?:MSWN__a l1ZK ZqYeQsfTDF<_˶8Qb^i)tKp33(K}Fk*ۻBhV›䕁f&ݢ̨? ҤEG vgI?nZ Е:RA-x42&&guؙJHB3^ntaKNP=]zg*y)8`G_u[| F.vjUoG wuOs]y ۻߕ zjb! j8ð{4U$gbmd*1, ;=QK1qp3 ?̷us:#D߻KmRQmJ^&JZ$WJJ YՁ/I>X#j2|["湟wN`,.s+8;ҋ`ea#\V鹄NLJt2H*ᄙgv~|n+4d{$DZU{`_P)?SџsҰaW/nnjbחN,rbx]sdņ/d5_zQ͝'7`ƣIj4 2m$ -oMM߄G~^rfkh"Yקan 9 /V²} x|;lH ᭙ f|}^\ xB@0*s5cPCsWm2-p*9&^*-؊YCSgN=6xF!IBydPM_:Ů`7K uWXn`9W*V]JWRj!#|εwW86W =;:LJxBBB}k|-)`f$Xx܋ U}fBz`F? ӥq%HɁJhO[]41. N?ԋl|/9(`g͔tGybAΣ.>B"9n0?-=g^WC0&m(e/Q{a7 Q p+@P@+>*]a|p+Tq hK48!Xtj҂ (QjYPh UtxWBbuIjPkq/]Լ.@qt'Lgx,O,G pZؙ7h zp2Bʗ_,ľ m8e) o!y-D,P'A钔r~dwi+SO_mg 3UK؄2#V >;NOpgZIk˫\{lqaf\oXR A%lܔ94f ٟR7.i@ tYɓC-AAV3[ ~ғ'5͛n0ٛ>'v&)vo;#6*b3YШ%^; rc8M'h'4+8(O`B.ҁxL i H֕6ކ'9A/Ͼ zRNrdX#E+H:&j{0 J_Jϡ9_7ţS: 9 pZ-y?;)CσBX1}Ǝgf|31CT^Nd+i_j`C !kMA.e!6 q_׵) rcXz(sURrwDq d=s6.zuѺ2i["Μmz<8TۃY]޻ǣB;\0TMoM/%p' CLSwS.ihݾ7;g\? _YCHuPwB+^Z~;€ Cw 87{7S}\ CFbHo)pw9xqId)#$|#Eraef]z*Y'UvvSE4m&V{9e!P 8zKǒ.qhL|Fiڷ9(˻7+ |c;XY}=:ɑjiK|5jowkAd<8&y W foQF{Q &א pLX]Qk,D`߄hۢă>{R\7Vt%$-qU3݂~A`>O~d-J6I32h*:IF2qw2FaC# A *c'H&DIKK_+'8ݘV;=}Wl/qX=|V 8K`N(gLָ7ҹz۶mOh|<K+5_i%v(tQS=x}"[WU,5fR{nՃJgӏ׵w'r}A .j,DlEf-'j]H.CҌ6v^:M_)޻5z]r l]-g!GO=ezi85| yϷԝs<\.^#P83\lђT$ Tu˭HNً*EKX}m=egYkBZو\]$"@{F}r@cwi,fogf_ì&tNRaÆ,?RZkʼHiTS F^]~3gX5%Xsy-rKYbRN9n~)>I-0S&2_bfM|D4Hy]എWl P zJ5p?'1'")6eRa3ji>4-J}oyY611,Q4G CL=h!)U&s@ܚR6//)u34 ȣi=sEEk0'6ѱZl9X Qv}C d&8R͘+8w ㎻5&te>mdDW0Y{rt㷴Fc#}ߋP6p_YNHPZ8sH qd~ѢS.4 Eduq7kI-M[վǯ8ܘv<ZIc?/"d& {( FFڈ,;SG%֜ciĥEH>}0DX8qP=mǞn9,~vZVt~'$۹L 4=mU˖v9ZM)"s+LfX"PA!P2s5BL*CD ߥ>Љo!'_,0,KŬ>eԈ eYZLJ=N b50V`D^s!FZ2ӹ65Eyn)0;WMS~,qK/ԕ=pmKJ+J:!gLJwBaߞ{ '`*1cX9f!r:>E ,`4dn;[rw3y{$LbU_ȫ۫>I{!9>:9?f>ᯕq۲ٸ+P8~|(QC-# lׁopsZOv^7/߀]d."5KāO18t$j1xrHLHfNГjpS1;L.޻aQi w+&c+NX+ZYzX Xv&:AW2+ kk]ht@_^wޜ ^>{I&7p{|{s]R;R!edRR&k1Ωv;L=nD9~sRQ#2MKצd(/ػL"v-`AGX@8JuRg $U.$ o:V qBXucB ^b7C}6[]23WJcbzw9ߞۇW>BK¼0l}E/uR㶮P~}7?mX%vˁ+W'ljBnaH^]LPڑU7ǣ"TV>7HLM"䄦=y'HמN؎1Xeڱm1kFX )M^4GcظL:3 yo1jZ&"j~L8o<_{MT?ӹ\)ܨM9-Q ؕW?<,҈T*Rzpv!svu8%R+ KYM"#MJk0E$k75_~{LV=,_GS9Y-TO4g)1zjB),vSgjXtcn3|~23WG3۔F;/p, ^mR&;b㨧%ډR^˪BmAEҧSuUF$5ZԂiqYܒ`>KAIFd?şyq6VŸm8u6 مclgn"GfML%C:Й\$TSU ًêwK:KLeyEtĊ53$g,GՖtL7ysЗ3t2z5a4*Wߪ!,|RiYb`ed]l؊h7x*nu KasE6Kp{w !݉VQrE(SB\ZDj+Rj7fHmx4Mf?݇U~;|Sn[TZ ʌ53{*I鶺U hŪ*3>;[s=<⢪r<,ܨj[[xt#@|`tXC=<.vXK+wS Mю͂T:jĦDs:ۚü9=)rlfZnܲn z2]u]rz/w #vFFd_$S0vH (uT3]c2owp_{jA}8α% Iؙft)Xrq x( `^em Lhs %f7")Ǫ· ]" %Z򌟗GY?zyf[>!uhzJ37gohWFu1ޱ{@&NSZߌEkSiTTՑ?'!Je/RQ3tbz"w5_=q/>9[`1IMVv_B06riÀ)Dz)Pi9q~v]sȿdC\u7 8gxpu5 063=ETӉ$ 0HSY v-y WQ CQƜiewZv#Ov?ӿ։cq֏R !>R/ a ~ cIDcc:hk7$4Vo]/^ٸ&Kx҄+l: 5eecp&AF $RQDZWD qԫM,͟H nRӴwszm`@`0LQ9P)F%WyKf)` 1ڣZZb埙ܗ$A5DYl'ptZ QϑJ:XX*"ܝSYWl2൤q e Tbf:tOy޺JYD:q}.~>n}z^׫U@Hd.hޯJ ~jy@q0U6>j+A?@auǡmMZ}8q?|ACKg) '-;a 2Vr؎ZRD-mnS!VuucΌhXd%fvDaB~;үAĄb'\,Ք+qe9z}|߸tR/O̯mᆤv@ * Hf*c*%GMUQFcYʩ3=m"8Ͱp_$FU}̭gGO_\C^ȵr,|KQHj`"& "(V+BKPb+`YOvYr˛n?ɿ |"|Se?|ɗt=G*`Hc ȓ>CђE:Hs*2`Fa>jt@i *eA[FJl$`\##t#HeϪ"B%fwHJD @S#J<-Pn.R,n]wYvTz6ǒ 7%l95qp$~Ր.dF.G袾?诫JZ5+xnxQ9 jzűv&fmx{V\x<ahr΅*@_pKP3z}PyǾO!ñ0)"ΪZit4B`\xqk٧y'ɰYc]K& ֳoe|Ⱦ]_Mp!MR٦Bx/ltLoCc Wiu4<'U\>sNjhYk!WN2UuFSn/E 3&x|['YqO@xo 忰۹n,[X#C_~M,Q`\1}-+RP>B6u~UpJ Q:E#n*5٨YX1E'AtV'$`b)f1S#_-X %IBVi_ؤ|I#2{^ڿQPB:Fmhb{mevnSTp4@P ^3%I?Xd0fkRy'_BG8<[O/? bwx vR{ LQ޽\ x&NGLZyb6@12ˍ 7UֳMjtФyjI}(AxYOIn:ɄHEַml|x#+ۚ04AzђQ+Xp]']1O}Qs4{d[H` R-Vk ,Ŝ=9`!w1v =-A L{Ąvu LjK'O*׆ڄâړ [%(Yn䈙y|i1̇quolCbh:݊?]-ۅMlM!HE#7ek䒥չ0Y-6qg1tC(t8coN*}_O1 A-nqˮ̔ܖpO' H`&Wŏ`g;5Y=%Г4p*3-O T `ݱ83\Boc2HKL j$$Z~h_=eQyɭyMF;1C_\ꞌ XeSAε^xF$^ C=\@lNYx(=SaV/P>Zm£E$]\uLzyi)#g?CzR{Pz=^_5v=S, sBMBX0.+》'Q%w O]:@/bUm{8>)YmZ7$oԟok.fCV] c@8ӱ{vH.\:Ь7$</8Ґ94)6g+XF_>-6)<+ Y#>eTyG2%+TzwG f19 WDz8 }#F'F hCM'l;*JTG 칽Cj#ُ,#.S%XE_|R1k#.l]Xz?qEVduc3M"V9ƽQZ2xY%}vVUl ubTyRt07eh36Y3"r*lDw1UhT|"P|lo}ɈHZ<{`c𬠐RYMCLI-ȢnS($&IWcdz!辠?>9mvdeNN\pA7; bY\3RLblU:W}b^tV`W FDl6Zd;\1HM0/2+ҧH ' ifOx` o)<%T9>I'p37v0!b2:bo4%VIepAH)̾r0ġiĻtҌi礦Cw+'۳?{j!fM[u֞s871cuKmP>^O|:Naei(Up $C2 83Kx.r,BAYvXX0X|r2CoQBAt(]w/|%md""1k#a.Rt$&u;K.h̵Mimj6iޅ9ػMwٿVW_?y}=_psnKhmnPWj9<:A~U\]5F-pRIk%"siL9"_ "* Kz MZ˛_lV^ӵjM([+xG3>$ڧv|ͷ1<;Q3 F5[\/kO%FյŭDiB3@D%R^Č&5yeddMWBt/B>A[>(YsΠIr&)uwwNCZ UWVcٗ-&b5CcAX<3rH[iJNQoۦ4 Gfs"Ib* n$o%+͂tx}Uh?CF.I, Ih9ˣ4>5?MimGV)O>j۱OۢN~?azJ'8Ҫ9qE2iյw{տ.cin="+g9rcRh mkҘsy3Cw SG<3DV˭<ʌ]E䤖PKcMהS\pi?[nqTmW)|֓\D1A( *ZW/2@! g1_2Ӗ%S`51)b,04>Du0XdMg^c/-C)B,RP1C4zR7$52i͒dI{ĝXW}El esC<PisVfGk`HkН+83*#ydv(ꌨq6;U4lm؆a=M&^Gy?ן}qd0Ⱦmw/_0T-dbSv7B8rl!v=ʱrbI#u#=.d4e!cGgfek!rCO'TH Sە D̿&?mbFldY7;2yZ^ߴlǚ !Ѣ+i/qWnQi$:Q-~.T* +:N2Vx>InOn`m('֐QLQ̍BpNkoY` 9FQ.7EviЄҁPQ/_GeZ'l!̨eHC.Ž[sr}=nåJZTiI s%ppf{PkOޭ.zHJCs&d6'%k'Z T;ZY;~ُSu<m)C8]?vcI"^sHvQk p6yTs`r)k!jcXwl\Zb!*U/5ZJ'c{!sՕ:#[7N0Y!G֟~ijϜ>o"\ۿUIG@{25UIaUK;@3{LO3~, ASgkhjf6"1/<wZ?Ȳq0 rmy4ᲇi-{8 Rg"|y:։c;e >~4$E0Xo5VQ!N!)f* zpdG7WmjubrJ|Y%TCCP{9fY%5Nt#RB8sj|4?#Qvfr6'8$E9Zk$LauS%pPW&׋DQWRa ɫ&>C{5: mqPpb?6nWҞsK_AH ڦ6G3|])#c,XQ2]43EՕ-]&l?nZ앑SL6>o = &u[ ]8AYq|[aC|͈:-o 4q n||su4c m?[LS}1]$R8<(R`8G8miTT0=SJMfNi%u$\p.Dԩafx 0yp)&O=Tw7\ct  =P#qVH9/ qoEe }S.1Oۓx"s\{ GrhQGmsZD>_ wnog]fuQwB ZAe!\u)/“4 GW ӱ[iیN9pd0*dÙqu 8u)dg%jt%۱ r\{yLk /gmqR1Zܴ{MM@ZtS>*i=br~4XA$(n(0"?wH.ػܬE͊=%rzVHJ3BABΕDudWNnӤW l`TMJBĈ 2=&Fu(nQJnzcZWFJ~i>YfjZע!|c6~@tg@+Uxá>Ï@k֓GхH*OHX% S.زlz3"(܍X`}sK^37ڭ9$mr-򽝮ˊL,#`!gKiŦp;ˈ8%ciW:x6@)Kc>mPd :TBVrCveBA2- 6g{ĿyO? ~'oDnuQd^[远~5R/G.1;E-7a6Du̥[ ,yG~ږiQj#>x`M`7^+*nҲimiRqFTvĠ3&,uB:۲_uo7'q"rkPh 0!q!SE#%?oƜe|u)̐Z߭G*jeDpX{)ڿ]69@IW.v"g)L!^}o|KվOOKqtM ?[U7|G5B>l%Me_֫auC _?'aY绿 %#2&rq,}Z6.:OL?%`N;9\1dO9dy|!7D!9J no{*?2mprs=۬9OJޯ=Us^q_,ԋ4zN..)ݻ^|ܝSLJڽϮC$ *_aITm_"g0E% SձK]}2#sHjKkrR&\o(>(8gLCz-F{Ɋ.Ob.h1j؇pG?b!g-rwow7 AEuvլ!%DPHn ÕP$HvYzoz<S3%a`ju ݀-n$5ؚ,dpNK+OSuL(ɍʯd ;#~+Wzz+PyIL vޑ/vDO RV꿼m<}1{WzR;2t٣]].4>PGtNz75ma*Aޟ"zz{ }4y܄sg&TN m%+Nw"p`ĸQ/"I/|2tOy._lvζ_D)8XY-*uo{IOf }8Ҹ.{# H?+D8htr~CY~1d-}s72 eSe[KtT Lc@454$F{8EyK[@3pY6ihhG'꘤wBÏp_SL!jw| >gw08)嶢l1+6_{hw\F/`!7Q&~ܘ&!;D#}[!SATH.1;tM# nS9,pefvw#Y}|—=$`KT+mK}I.A{{{:q 4nީ6a\U$Z F(ui8D@eP3u l^)eh56{3νhDhvpTƣ'LÚLd+7WonQq_-* W}I)Rfd:sZ3x5%r2իMyH8ZB/E ]$!.\ (JKty5PݍD4`7XGeLڰub?6ŢLlS3jWHx + Y J.\xgm)bqEO:)˜Ws0$j\ 55bK% G=t]>XJ+\2=s[ 5 XuE g;S"r6Ik791WW@*`司F;·V7Z.:δw͞<~K9 6 8Aȷ!O}*+yPI7?=;yk{d@'jPz-+ yelƹuzq1=zZ̎m`catqSRLs7Ź!srRx'+[ݱlQ|Q'q+g12o`#^ڰNn-b.fS'\Ĭ/Gˍ>`ϏKBoP`v?07-) /(Z( (ژz`X9K{61?!{p/Oxvo"t Ra!jiۣ]_ FLm&EqҚ6v"%,Okb8YY'bW pl'Y @E@Yȓ0^p[G;cג('9_oo8<ߗ4t 9'E) EЁ&*}G ̠<8 88t9sn8:/ј0e/~Fݰ2Boa&Dfe勯ϛ_'&[Ls[-ӎU.XqW74ԍ S]-$67߅ ƙwLȡCR~so̘DWSŝDE:lUVuRwQBVaҺkr!q!Df5f >AƮ]<7/[~vU BI[* rÞ .H6:jz5C+KD^b^a*=sR|~ 8`(?3>kb/IAL") ԌN;{w#_ϓ+KP@/gЗߍ`hG#,GƐ]3C GTnֳlHWc8څK\@틼0Oxpw䘏)'"y/| /⠬BM=>,9{\Ƞbۙ} $븽q! (r%b~BH?~ O2l!:K$2G{Ҡ.X/?bm\?2Xw0{J!MȲԅ[&*`qz taXGx0kv_lPpçuy}_ä|yDn pEiB씽)0]GZhЄ Rx8}n پ_s춇Nf I#& [({6uTL*@Ž_)ηC i<&Ħ?w,9Д܈;,rGa'KQm0NF\(QIKs5J:c~hNoWQC=ڛ*1[2E0J 4 Fȸ{G¬d{"Qv3G9P D W϶0,[ѣc#VsM;ky$׸|D+,*"Hklv2!DA,!K]~Fncrphu9 (CtWv44X/^O郅YML{n%J&,RC )<-|05|75W*\mŖI6,򿀖./\GCmЃ$Εr=J09x("K:_ n!_ײ6ș=(RY o:R1̴ۊ3ʶn%ZId /s[ąLݫ&APq֤7ִ6Pv) )-L=b%A$t(zgji+C BC55,MޛGpR5HzCƥ_ܡVÔ2I!eYu<ޯI,;83tlzAEs+M(1eE - ޖ7eFE٘ fFKƎKGO4 t`f yk>'u>mch*mhn%@HrF%xTĉ}=YF BJi BPXNnDܡ\H/aZ >zXPŕ3+Ϲ[F/g\IKrC=7/+ ,k-gs E cUv̮BwN19M0z5fXt1oF( Y?{AEU)hjP7FFwuٱ"J[bk]p-w8H\ eJMi4$[k6Ss3Z5ܫjԓYRg)'m*y,J'&yD$åX1'BUd-6@'D?Ǫo3~~5<*,x .5Ey ;EP, ˫Ǚ︿Tˢ=^ ]f~XPT*`9[{!C{ojwg/qO'/J3/qY:X:UNR2qGn& & %b7DFKo,:鶳1Uh`&TNwBpAhk}q=zF/ U^]WcADЄj!&YUZ2U܄/ HNY+roA⋎}8x/Z<;5Ȱltߢny|i|lkڿ㯟}ērUڤ/>y8݉zĦ!FIx궯gQ1BO}xȦ`h ^õfǚWpLn-)_#Mrrư(]!Gs[ꡧm@/ "ȩ#=@ UHS;Ui Hjm4 =tWTp T$"?~>~\V.{ugr0 .In 7@P Gt%xW* ?Vz']2^rьfV" ɚ5Sr ɩ==1.*,OOW=C PL "&AGCBy&3ec x%Ă#U]mqv2?D_X"( zx8|ћRB,ߴˢ_o~}mts+#,b[{!TU-bNݘ ichNȫ&fn~T _7rNptK9=N*pfr޵+q?[nQh$lqS^ɲLKIv 4=sA5 g1LpԝC\G<$@آΝ{L_y8v{Bޭ[ 7{|~)r+` uZ3RIœ?1b$Z*:eve6Z.ni o:mr)tUgFHP.F A>S[K8)n@/?ėGW]x\?w_qL+!lw<h9X[-3LvB`Ί;^&y^YW'ݝ?r 0EMس7:x!aݴ3ac+mM9x3/Zzm5Ӝ8ɏWhѲ4ZCd9j;k da~y$^&T%y%K-k ? <`.٩e^/7d`U1UT(q[(RKP̜[PX ]*&tDry$@l}Q6IKA̮!JGy󬠁 C16@&i89+X㖘dfɑhR} GF6F\|sOJɀIlcx ^. 5۳ ,1<äIHLLCh^]P.I"Θ[.7úEVc ̢6\zE,)]کe\}Ԅ j%7>,m9cZɴ,j~/ϙDs>sR)H1YEl`ze^ס;:픦_(+؋jU&T'zX4Ouݎ}̽3puy/>@۳3Wֈ1'(!'dYqzPD{7ы33$R)ᩴʱ g4 4Gl:F {Bn؜/IDXXiI2- Ӆܝ/敖x)G$ė =Zu[ caϐSJM/*d2[F8Rý{o=O%8 X=gՄA"Va  e(/2SŠ*:˟cAiar3#//?~ret9(jſ3YXL-٢0k٬Fx?>䝭h~b;XP<A/ጙ4nOe_$a%UcϦߖG?E0?<([q\(e%8^Q/oBW+MPl082!7E668Ķsv1 ,.m>sloRf])tnՔ&96x{޴ʍ6]syE'VwSl&L ajK& [.' ˉ?{vvEfꐄT]&)UҲ?!4r0VEߖ/|LtQ'y'ӯAJz09,Hm#]b4o^ܫD,tjGF'{'Y9pj/julmrqRV<)gO] 6L,%ֆ.زï=[`A19N Q$ n3Ept< [&yH[uP.h  Yyڴ;w!qXC1E !s3J-Vod`p Sn1>嘰/!ǒi.ߓyaܺikO\ vÅ -Xpٳۉo }(}iuƽ-?hZ#:+E j-Y֒fWY1WLwʩIM\؅aϚm]2Go˭|MPkVH pfQSE0H%yW֜ >1K̪<M~5/auHq4IkXȦ2!vZ,i[a6QU}Η;&沘Lee(BEI˫u4?2\tCԨTT1S#1(eۋm:# q ]@YR v҇A =$fG]5ܫ2[e@Xbu ^c腼r8IdvV|褎d'rܪ$&dʰ98FE=0_K%˻b>qc+>lVkK,:DI jE-yvPRqJ qiIٷI:a r䏞pZG~CĬB|mF!zHIt@"ndra@*fNKVW2bqdԠZ];隲+e1|=Q֯iOɏw>}!9<*hd)+_u2>5K N2rصۄoOA3_yV=ѩ4ulOg ,ǽH 8´0`f~^'Q&o[4P!:9*,h$`e[҅-q-ኟ IsUl9UJ+cgN Q}Jj>H|tㄵĤD /dpQ_C?_ԭ%DI+P$DtK(bV-ҷi\Af8#9ŽH8q7[5bE8(0 *(,܇-7 Wlo"Rv Bn+w [T/:K ;SGYIq\ƕbNlxmttµ+Z|P S0r屒 +=363=롿coZep[ė;B3;U=)Y#(_Xb|ULGQh+ǵП}5"k{ 70MA( ȡQeNj} I.d*'XN2UcoEY5/!b:) 9LTQ- u_׈nMrsf .n?#%(vpϿ}͟}rvܖ3LeNB3v|׳/=@ ?3S׵Nyrx$ {^LE> RI‹$ LY>}Qk^Ps Hi\[+餶=^}:/n&d}$ЪNB"FAd?8,ҙ*h7|`(c3$X=N>_IݔkrWNYﭖ5ƍWS1sLxr@(&4M-Z6+Q7ͅA[~i3pُXT72&Hxd: SEOpdl͝/~Y^W~/wELk a!M'w:3z\󋯳Ϲ#{g^Y=ő^ :qoI'-jnbh$jDjs 旨Q25 19viHw-Q+TIwMȐ&li(Ţ{A- ^Db =ɧ5Mw!ʺR:[d_Ve̳XטoT2<4xpo:\ʂmUGx;U( F-pP?x= IQ+J9:K lI݇U A",.Eo&eH>y?{زmFU.H#m35P+1h y DYqֈDxZBHP`Ok4H c~F W1P$ |tJ`<_1PVR UV哃<V_i9#yQ\-~qu6BSdΨ,1]W)&4=#{(`O|]+3$;%{/y[ '\*F5)%F@uv` D|tE7FϹ}K mlt끶PDG?+beྻ.oS~^'ݿCՇӿ wY??Xw_mVgJ?cR^l>E1iG4-x|}]yh#X]r ~cDhx 94P#] Lb@nSs©~h?&:MxOBk)JU5'NHG9ɛQ^EZ m9h|(:H;MFqݸɎEfSFՌSwD(DDo+HvXȬ _UF\lʇH-] AzӅ㌰"T/[VC\ A- `7,i9F[\\ު98h'k] 1ٮBҼxlA2+ϻv/u%)Ŕ;]녫gL8xH`p?m9KB<(n$Nzxytޙ9D{%NF5Z`x.9mّ)ZQ3w*8cCӔ dPlb4?TF|o -7Cĺk[${ KڬV{eɝ젓e<ػVLR|35~,qo7|Us-Ι&C'(ΚbNTZ^;h)#O]%2m*DN>2$,8Z)+ lmdB, _MmS5ՖOqA=T"|ӕ6yj8y:ղ\7 |pVlX%@VS%Id[jya0W}V}h%58>C/H2^e_'$>.>>z5LgcӼzhS}I܋X3+ ئ+5>h';vMaga?h#?i| v6(:'Swӻ2}rAo쩴e"Y*b+]a>C AeYܰv(,6/xU' [큫w\)uuwXe^kYH<wjhn^ή^;Rz;;f4u㻀Ĥw.`T!ЕP76 Sm@``ofޘPw&!&FB_VxD]k7Y;9j^Z|ed-1IqgǽDG檮#={)u=מ1ۥ7 7/ճ0~'>%Lzn[/vKI#'ܽ4N6u8ZEpeN(~D0ۺ(a}4;ROMJMTטoVQ?# Xq/6*;3b戉\&g؇Lp!c$ y y H`շ1#Q$*V UXb,K_RS1?]zAڠGc$@T;.:.`4; Cf]*D$#HkwKZlVGɣKMSdt-Ni8I*Q@ڼHBl >LP\>&ԑ ;_kw edC}eru,;@v0ՕRru~j!C4u叽m}4v  r{eE?X"qM@HLD$3޾[7}`E k/+[m%"1Zu"TC$D^ xSx@ vn~@\a 2BrdG@tfhz|-F?%>etzt^=" t4\Z3v8!48]Ou6W1F= irtjB& z5q&3̔$֘Ss9gKC9:X);{ TǾ{kN $Xߔ gt,Sqr}DgoӅ ¹4q&[K[%ve JIU`XO&_zms*jhV6%G`,yУj*-9Z]7򸀚VױɗM{3n4 s9#g49L:TWGLhIJWe[ny\=4Pi"Cm`9"VQ58f;ja :X#Ms SU&.)&֯+5AGqhT,gj`OQvgS]@( /U|823qNhS;o\vNKU`0zRƝt<- S,f@rkJ Ņ[[m6L׎Tcxl9/ ׏,X8Xgx>$(P*"$LjHH^@jw::B2ޤWu/lpBGC:"VI-',ԌM\c92qEz`W4 kDKtM~䗭%(*()@m`>]ImnK(r=ZAa1 d!H8xK[1l`4 ޗwKX"ah!fmLh1>⩩-y>E2쁇v28jfoݮ xχ ޙֻS]: tJfoM:W?=NnhKP싄غ%Sےs ;LZ*DjG[)J W򵱩}:UJ`e@Pj jQ w uWV5bdjTҗĽE˱$ L<+¨DX5MF.fs 4s~_MFyǫ#3fU5<_ݽ*DMkDU,ͻY Ǹ+y^)N_V-sv td;|sׄi/Lc3ZeyJao)v >4+_e`+I BFyD=sIJ{T\HcE9?Fz^\\β"]6Aatb7'(ok0V6Qk~D.|2o'{8v.}dz)&nΥ;ǘ&[KMԢ7t>]fjmIdũ{LQN2L0& e% L*P(:<\>;DP| aHGF@l 郃}1SyQ-V[j-^ܭY_۟&S>OeQqn]ւ̚q0^$]AQf*H. dS4Swq̏tM%+JiaKR92x8Iz/>W}Mdž^LriZ 3/Xgsb0[_nغ5[ϴtsGbjAfE0k\KJ+7kҲw|\ţ9iѿ,$W_u2Ze-V 6uߓm/QAmP]2_M*iG&^5TU]bLmKmn&< 2M=OiHA .{8豺 {fKh$i&M]~?e﮿'x5'dzrUbv*c#[7g;CwSᣍ(:J b,ݕCT~R'Rdjr}Hj[jU*qG ®S{'W:;͉2@GKɗOU_x./Pzuo$C!EP @8)_.sզuz<1q+~ߢs^y]98>".QV‡_nKi8/^F#v%pV"&A9gwεHYU5zm*0eakgvY yNO_ʪǨobJhH'YrOj 3aX ʋܸ׏s۽Պ]uU%s y59A6/9#k?rӾ 4[zΚջ ST<6O bóoQymBk|uhzBM۩!AA8W;~^'C9YHf'n1+6E}uqݾ.sn5wS9sM6O?o"khq$ӂ2 "O.L}:kg&9kLv3'1mnC32KJ E?R]=î߻ l :UcؐZYU*WRel/p/3c媙 Rj" ˖"@eI?0_3rK$$'|Uڐv?N.rk I:&))s Bz`tP-Syq0=Wk4. بj%vn\L^i~Gڞf/~J/G'>4lGeFF8fw)E[B?{R϶ W+ X84e=DvF*۶h%e!`=v9&J$f-_O]bȭn5AJ'= Na}`(<Pf=QBWei@RJ]A!'\H63% `J$yhK NpQj݄%7A"% Zmܹ!\N /V=+jϧzЊH밵 oڟr]X/ד,"8,xi7# 11LΐVdD#dL1k T["H33]%&HzatSCc~E4<*P#mfLdud7dڸ;#_U#^+صj^d&aCfE6UW_1f[&^1pa@\8@H_C#4Ie~bOw(0:61P-꿯,eTᩢ(0O&磏|Rw˹NʕPg5 ņ^K #p`aw=n2aܨȡmxpn)bݎ_椯hk6\{Xl><_Ѷskz-ZjZndV\pl WRN7G5\Ksx3,}Hآ ~r8imϿJ6ίQ5P6^jV.Z&t '+%e6=7:%1VYTIfF.8= V/ׇ.ٺ lk8 )vL6-N O5ccXMC}z*m_x2+&m z&87b>c\7 e9D}i>HμxrR!T_|T簟t0B;2\O*Y4>M VIx0=Ey԰@-bX 1-Ex+ryYZó}RY9hbN2,T VX׫ <]^wL1d3md'j=8aڄU;e&F0#6ʔw<:zTCˎG@YOco?Iǔ Tdj*C81lI?Bc͹)'Iv!1ܕDEjr' 0Z'2cTwPkNBC$ϫidc2*ngD:#>%{SwU@;Թ$~K^(9zXe1r67~΁y|Pqs\SyRMa;B²umGlEJv(X̌\k@^t+8*yցr|]wpцD|UC] h֥Jot Z$Mm1yO`j ڹ-%*I(><pR۬hQ(@׫tEiM5=9 GiflVrt% ʬ\8*z:ڼ JSq-y髌7@B} C=)O~\oJW,>S-?@+c]8|p!`{X]Ž\} CG(\"[INѳ}@`{yޕ41G쐙c;d(5=mPLP0TE &QmajEߕpE D暫@3 kiV9QգCQ'M"S?2}Mr4~-tq?A sT9粝whl;ylw(o6fj 0m+CYG,iCN8|.ڽt(Ոca bPR"|ӸfIu1<:zp] /({EGyb^ =`KmLw<-5 pCh#}yf[RF\a./4.oBaK;c}/} DSF,𻼻?نg7g=@HC) ! kVT2M""Ss|gDUj!KKF;97}3/ڽUW_G ؓLT\kL(؄KɡolRfeB&kGDλ g2Ac>IץS4Kxjνgk|??ɜ_E5ϛcPHYj-y/:bw Y5}v/_ˁH̲rig p28Wy;ۏo>/٫āOGmp9]ұR`nx Q[kp"{Kg]3:>*`J28&W7sլOԲ{yԓv2=ɟK@Ej{p5A."^D.W!i[oJ"I>/=7dq'0,ʄ W`\!\8~3@>+W{7-'fuTfLv2&kr9. }5%Zߕ73=3L9MbU,7dZdG5QF {, :ތ'σvɰU }e|Bɨt c3h:>]> $Œ^{ ϼVHoS.$l+:DFd1jbR3w{ @5=\Ov0ZaOccaI][J:a=V5ݻ gl>[qw"Ԟ $WT`u qA@ 2]Aəu:]2v * i tUZpC4 DT5psAR-}b4Fec&4!6hQr8u:yT>l2vlOvaWIl8;'.5/|,y cftMF@ਙJRwBՆ!pt`mmH+N2K֥rcn%v{M5܃w&}`noWuR5hpbYDؤ|#WO_y|kjsd N{~L=C6zb!9$TOXC6!ݯG_f.e/4֋a3Kq?pck>?1cC^IlC)$Lm= Z}` ?)_\ޱ޳Fr2|D md#LyҊROkSmA-< bpľ^tܝ˲%9 Ŵ y`fAy?ίhaP$*W &M& ǏmZA|,)90OԷѿt".v~yH tDڀ5y=!aԂQ16Nܬm ҇++l2MGL)}_2/N6#1Wqpa qwt \鸀.إ*#kfdV7`$' !2$ iHH`EFI2r bL1IxC;R{TWFX\w4ڇ+;׋ttA "LJ~ΩAȺ+zz8M̖̓P sDBԜJ"Q}5hhsSg3+VbݾUxSђ$Jh2S}\#,0X ,1Lm![Æ$ۇ8:Aw˃c ;&#fW}R?'|eOl%ZPmL' &&k9bĖF@PMHYeAmO/i7"2| ;p+!?-hodI-HAJWbOɆGYnzuUI̽_:a2:Y4$O™4r|-mf#tK{5@_0-bkm48Yhm@&.\)Zi4vE@6pL:HhŇ,&#tbuB5…Hx/8ةRf҈VXrhxGQ^Cse,rb[)OY:3+,׋),IR:@b9~~[B*cZjލ8/鰦xFH[-(HTGO<}*\ؓsd6<||Fcdf (lHԡ3`MSw>g$6," 5mק6)%wn ڄ#E+Kk;w͛Q*z=,彅MQo]|ɢGD jX1(S|WYguYRDMm(rv UWBdOCbdpS1MSw%8mNrrs>uxCp:V;+>fmH _Savc2z#$M>%yN} U|^ZMKIvg?nZu:(;[.m~]ui_7~mW¶f*E(}R&H>PqǨE rec8t [A)f,Lc=rѓ4uv%n8};}}xk{1,Td_N$&CƭF*) kd&\"|u<DLg{iXy{ 6@`h#;ͬa'/sz$]MU`H #i,xҗsn9SU4 I/gd?$Rďـ,nL6Lib,{6I{+;_YMNOaϥYn^u[>K)PQMjHhuQ'Uut~)Q|C0 W!XsՒ/"1n;Oo?*:ugo t<4.;aYKhQSXmpĩR+oMlƥLKLXb6Z :3ȴ+*.l} p\,^B!4,WTvLBv|Ȥ/n4|z&UCftMyu͎}v8ԫ?/ 1zzSzgs":>8:\jӀH.n hRb*߭?&? 7+v{_C'.}w<؅c ٦٠4rإ ZL$&=RooWWpZ-D#l|:8UB-j lNO<<8DD\&KSt76髂1^ގ3!RyD/pdºw'CK,˧r6S6 /bOF >'Βbpgzkn]EU޲d;h3Q%[,XnVm᤭Ef( Xk**G@ vfw#)j<"V/ef]Hx:d6BeAo\\=jBd)lбquI( eT "DgHdۙk`;)8XPAgObŃLW$q cq;uFǭ]-$Rl+֪Q+2%2[L W\䀰]L $zv!ɢҽ/6 2'QSN)iZ0"Ϫdjwk),8T͌瞊MX~(ڌ=gQ:ƨH[WJuZ32~rXZK;BSpJWS6 w,$C ~R ;Quz!Nl8< (T #Fj"p:' vV,bSG5==տ\9e?gQơ+:T==}n9tG8?ݗSd .ʜmFeõ( A"Z86/͑وL5dzԃ)k"R6yw<|u4y䞣i.}uK"{ÉmmO (^.TD2BQC Y<ы}2S _roPf /Dֵ̋ͱ<$4]e!ί=o#a4,幝50bhSBp>O5aY D$ a53U;Sl\&U[i,d"ҋ3WU AbLSa/f4U J*zW^>geԚFѭc5R9 R`7_Sd4gZ_9|^V8?t%yl=2H熎"ߕwEWY& 2v%ai=3l+S1$e~S*`RYC9#@]:t&ovZu*:,p'ٌl@C=low.YϤe R.sf^6EWZ4 x׆VbXܒ B|B"W*'rDAtZp'4G]pt2#Ljvd⨓0>ªP2bAxfd+4FiZsU#)` MdV϶{e2ګMtoaTx.Íƫ蚪=2Wjad8s@e!@)̮ !d*W, ŀrEܕJS`ikuFpHG13BI;7?}o_q{O`؈1:-n }.e2 ,[Zm+Y Mn78nCQ9fjy"ݍ6n M2P@S(0JY@k ޅefβO^ 2@`"|rXYpɌZx ?̶KRgd$1F7U~˘יԛd5ߖzpZn>ٚPkg…74[*s NX%JkvԢh`ԥAzߵG35jG-*!8T{}Vr-y')'Wdj_v̗4r̕dp0ZZ'Vt ݼ]ף < )WYr1Ѥ?:?7>h T_@oeVd$2kή#"phXbXV4kϲݼmeNO\4m.ہ5Iile/?Np7ĭ^ [$q /u#zQELl ZkYi;3-c(j6p h8Vއ kυ+;e[B;1FX^o -w8&)d~&l#:WhZ9~!v  Y=yLʮ\p"R(yqC,m ]1@tw ` ߦeb@iLR@^zkWOelţ8QܥgpjPuي!q ތ8<9F%7JJb)=~ N7WqfCOdZ܏nZhz@EU4R>H1,*B,8?L}l5+o}#k/vcL(ilyaW"$ JuB,-K()"uY??[F70 8(ywODy.9 JWT֭Z BxچK-)GjO9Y|pNԳ۬ ;*{ۼchU&4؝jμLy.!eDѹS^=/Dr[%OKv\ k1U@D$d<7}t?D8{& & r:-.&xK2ׯs?_>a ؋#'֮'/28Ilk$;ƱҿD{! 4dq9w8E>׍A躈͡[a怏&*yF(vn0u=!-K/! qStDqfV0m[3_[S- ߐ2$TEe"̡@נvSnj3O3gJ FH0NsU;{liӯ*cŁp^(y6ڛPƻr9 (Lw"L?Cyq =)>|ʧ(vv;P !;Q5a708elPcSL2ܠr. 1^4%2[ã 'ss{hLpd> ɷ=s|qJjU%^, +ڭƫ_omwEb/Nq*AVh셰ma +`JĴ ">&ܡYEO0g +/uĴCa!=u<.f6T5j~aIrG[h2k/Ù|5.m ;Lx$G 113GQU%2ci2gM0=EKqggFd1u^G1/VXXay=l DlU [GS%7!@'^m9SPd1m7Lp`%P,P>2ukJD2s'f}b&7~t1^3Y]ێ%86غBSʁt@%vgTCI>rV0 ='<`Kk+ݑ( _pFMDi@ nEZZo5xVʩ'cxSw~0 U) 2L`L{ْؔpⲮ0x)gڭXWJ{!niEP|h06?n+NZ%$La3Cdl'_Gt]B !P&,^ӑ}jkA͡~[ sc!lh[LrB),)#z]@5J'f5e7L0a0Ib%ݓ\| $Ps,uV) DDB<'{6s: 9o'r]`ҁ4bQg ۰\юg懠1>BYUj@^ԩE!3m1 sK. @(\5TEHh6*~?Rh[ܧϹ$P,@R @dH_t˟G>Ezsن@@;aynj9t6%d,r.E$ar2& ~]㗘^e*eJV+SӖpġX$`QW0; / րr2d<}%?OXGVEO҉!8mvOAPPV(nm C6GB6[ڒ b^f眆ZݸE([^SsDOwq MTNcB(TUuL8U0Yݞ,2lі=B`Uj+v#ԃfG>̵ׇɷu}o+l1aj Kl-?x/CY 9Rti ~7ikfaq1|닶OF`b>Xȭv'o}BQ"XBF4: 2ET<:JliOԓ< ~UiЃS}Lh?C@44xwE 3,ޞG]Z#tLEE-@/nThqmK`$=!%__UmکJ ؂C!H !0$KMF[\X 8cJO۲JU: ȔX dޙ.F{|C@Z<-!52qWF ;),N]rث!\DxX\e43]Nd{}P>!4? VzWЋa2.tۅUk^䓆!f (^"1nbjݲ>b jòɉ]% b ,TqOP׉.neScDh4l )6CwL9'nu8G3([xTѲ|fڤ^oq%!2u:9rvg׏ø #Lы~Tq2 )Jg^a?<Eeʉp!I?R7Zm5tB\ug TzqF-Pe<>&*"^YVCyzd0/b %`%¢ FM j EHR8ʾNVV(Vi(<3~6)$TRZY(dXʒeoAp H;ڑu;ch %A[U܅asS m|!I cZ|j2޳p%@YM=WaV[@'z~l6%xl8CUPZP\4 9.6 ]NQMǛcz8h67yOroyͪYȭkcȍnbROjA.vKF];I{;I̡&"銪0R)][GskxGsz΅Da0N{Nke}zcˇˌ^TӖRI-W펌i +$ЊRan{agi0)^vݞ%@ ad q*XлwoʳuGl,Ԩ۰Vnq!ҍ2K Q,!}'0'd MbMfG^ 6Mܖɯl=(Tc_7P+:̫t;o&šHYީU)O$#ITěF*|5Z '˚NjU 4՛ H-#S6&h +Ow$O~'}=ioOކ/5>tH8|ȆG zѤaxuOM7iNX%i!݅ǠN@#3C9ʭ>+JPf 8 vߌ/D.ϐ"mB ur4y2C`;A?kf ]ĈMFK{NP;ň3hhrʯ Q/qUL&\OX. K3g&:4_ZXyCFr%Q N XZa(BU!0ҳ^/IV\ Awb'QP1+^Q6ώ]s:b_Ժaz1[&I kxBhYF2,ЊY6!xK"qZ@/'ke(G=& ^ڹdAʋkYP$)1F(:.iq o;m &h0!ᅍSGc"R?kE򆕶J(nVmS41aºShQI,Mڅ(#dd5ZMgzA(E.Wp}EC;`CIH2Fz4N偌P1vtVۧ?/gUc\~}ܦlN<R Kɘ)|kaڼz|]D5z7# zkkd}7DL. hG5={=K*d\r"4V/ h# 1HMnD!GB"ms8BJ>q:GNcPid VI\唾e˯Pڒe 3xb4cv @9;|| Ղ ԯfTb{CShdTS6JCO}y`v}ݒоfN"@꒞DN b2ĨR8$of꺫3IG⼴$'3O'H4=(NT_zmoӄEKM9XEwva)͜f&RD rTjӟdrqwko{dBhx8K>ޑa'X} /h[jg۫lRcLfp- t&M)!<,)'ɪČnU,)98Z(<~vyo -ܬ cu#b<651ڍ[pLpصyz*{>.A5jc j=B*鲩"gxdVo?jJH'=6,]>#\$ᮊN0D6{jh 2s^qzx5|ĩe ZBe`z_U x|+OP0j~s@#d`UvqOpOkjB{8g[$m:Q:B\pɅ694s9AjROv\z>ȉ|fUǺTF>Fc)SNMdR}ӗ?^^ &XB-y3 H0U9 -${_|v=EN%ըuf;|)~b RT=$'U DY̷ jrū䜪lplDp0 6P@i:`}ٛ@hW:f$#QIM0!)J &a30_ PA#șu"C}ړdDh%K wVpf EaI2Ǘ71K+h h@ŏ-JNa<"tOl>V"d[(5>Uc Q7zEzؘҗѴ`Hc_ĺy+v)!þf+m!z6V}s 6AGJӕ4>Q{/>!3H6gO 2л\/H6tܯe4ԫbje]D0,Ҥ+d +%#O(GP% <"wh~w.7%?Gh2$?pbA/a}*/4O$C9ˢWh(!VCPྤ*4tqx8g<$Z}ݘO9H=Xry{wP=wwzXak|dJTv~8\gW? |xQmM>M,4PW+fG1YM\12U5S74LՌ dJ a\(\;$}F۶J^;aB6(CZU0U$MsA(T3H+d+΋ {.crGCMTi9Mw/K9i+Ğ]i8v{&==KuCQ 6k\Hvǎ=# ̐,3G PaC3-?4uWD8'S?g/4ܻJ  \tX: &_|`ؔ]i;s+@$1rAta(δpRL5Q:wlcCn>v%aJىPZŻBQ'@SY se[S,3ݍt4JM2Er8bpudžDl߫Z dsMjv4C( S68JUׅ⯨I8@ et:{f/ ? ]G 1R#ER`2H|j<5nЮɚ!P2FXuhThUYSC>yTZpXغ7ޚG/q"[1ȍ[^`е Z,H'm{<Hx.Y2whkRPzw p@,0nRM:ЅLB!tI=_pNs•e:>$Zwjy$4 F3R fR#5PF +2 Q΀H]Ƀ'. -}%-QQjH])^^q%yej~x-TrC˲WZ=߾:$MUn dnp-ni1cDwgt\C9íǩ| SOK?4"(^\sl7YVy_ty~rC$Wjv P! 3+P)1/Xu WL ݌63gnZHnT74}V2KA^4^^ygZyI;D>cjp\9)W 2Y{riAUo΂sɳG,e,C$i:8gS/r?]Mj+VTp36ߨ)"o-Zd0317=qAgC{4e^$iV6gv &ReBZ#jIҧ[?M\zd4NO7@jD¥^X+eS (}, "B`m떬\kBO|i;$;R@LfMZ.`8V.Ϳe^08҈h˅XUHg> av&'#ـfփ(5#m"ae@}1ÚpJR0R&Ugm_B:1hP+Αoe:v8D/}̦Iyw%QW[\k hcI%>?fgB -_*)R8SN.q1U8t%-gψq5\I0FjFg2V+5KKbI"B{Y %R$)S;ڑ΀#8.꺰gYLwC0=2A#^kVٿ|n)l!" +0򹣭u2A7Iܡ=l!F` ,-㶵~DWwsx@8J3,+ ám e |M}%=o_?-g8jL|$XM{,bcȸ^WdARt`LdԄVaTM v4H&m!`󀩦(l[77qQHј`S|=5ßȉW剺sLka ݚlC(ösp *>\] @\6td-ְQG^|LK^!=U]XC> [֠r!P#U$vTX87VPGۆ&LNRK:۵U \k1\/ꂤ'$E5p4jйA+Чѫ|Ķ }-,qv /KY%8iI6uҰ@ D)Ѳyd#%HlqXyt^E}4$gx*bdNn"ؑwzyO;u U' K#Ob:1Rk&/]Vؾ`]O1FkYz%)!XHl)|g 3EU$6ܹ]= WR&]+QvFe$TQl6L@1tAt(k {.4[wwG>Ztbd'e# KQgt2بբQF!kZmQMAMsc@BxqWW1yVF$1qݿ:4Udi6Mkjk$ɒט?v"64_ybi*9c*dLA>  xwzk\)3-_r>Sy)U?u9G 9͚__8%8/;Kp7`_ 0#$2r ؘQDH'*ۤeKۊL^`V1s)C=~h^ëv_ g4طa"x DY-zҜ'=v*"^C(N+F\=ݧ|0sӀ]QvqFmFE ^'t<Pt:޴)mҹ]uv0#D7y-ٕNI$DE!b?O$F՛PsMf@:oHz`!K]ecqǩm&rN'ƨe)V /Qmi6=m>DH5ɝshZO} 㜼2djBtR0tq歖O"`7 06EO\h p*ӺuvJ -y[krXGxU6f̤YcmZK;VRĹ)ze6ԬHYi&2&/@Ǟ|*@YFf@n~oZadX} pl6Wڟ_nNmD(({>gy}8̨`tPr&{-6NsXb2`FYODIjrtYSJp^/!׌"L"7K.T9v˛޵jRT⎗}5ȢT#H>osw^R$46[|j S^kbG1,)#fkO3?*F;B/9y=*%7K?R+U.*K fJBqd.Z1Xmgs8OAk:/1I HboޅW CK*2-#^QJ㔙r"X,t UOkr5#գ<ǡ̱9TA^3'Շäϐ (4v3'؟01hQsrY/3ete#Zlj\` @(GO(׏I QJ"tQ$H{aYroV:ABz8jv[A?wW7mۏP=+fv_n^{ K~\>4uiks"|bSѿjXjK< +rM3zt",J1z!$vvTHW-i*}vy8V<ѽ }/6fQN'UyV,'|ҲLݜٶۻ)OϢzZ9 2ۻ2$fQbP-[3 4IOyGL VwО7ze B(Tl@ Q…?.c(=KXe^"SF?&`+Q!9>]>Y%0i̧M{Tpkͨmv8C|j}O̒gsR/3DQ=>wl)%EhCdF%e]V444<HBNN7]r'[Xy^I*a=b*xD8wL?-A?>_"re!Ȑ*jh&1 =GHda*FN~v%|0}y {382%d"gV!4sZaieR_ڄ<Ĵr l ?y -F )Yg\D[TiJX"%[E[?&8@a~@?Sʐ8uި#D#J!~NT,?1Hok{G(Ώ. )'s~aیJistlՕ)~-}Ǿ H3 E d#F؂J٥n,ɡ"eB#큂kj`JrHԘ>C Sqf6jKUjn/ɗz^<0h'<;Fp2n 3ː|y#nMqN '(RWcdcDdkgqEdn>Ucrh G9Y@{h˛ `s fv覵Yq3E +Ќn^'o"]]$D||<(e|Ų)Ą0?EJBdWJn=wڥ hZRe+ibMcpu57 z;x~@Z9w +te)}K/ʕiW}HV T&RC@#oYSW&I:[+N< mΆ^s^O]MXW{ j`F*jLhǶk;Η(f/VcbFэO毷7)w%v׻(4^^ d Kx<6rkһ&0"5K n?|R(hx2%^G []L<4YfVn'=&NoRQ%A. %% 'o4>]A4o/ Zh'8WKr CYo:qC#Y`Ƿ&} Zan.& a5uhYW, ~h渜ZS˳gywoCl,N/kT $hGR)094XZVUSŷ!@C!-~0v.llIA$qPcڪ*y5ꯒt족o CrEۂ|0NuW| oRE3ۗjr=C+iFgTk(}qu-ꉘy}LTPW )"AHwmmt mTk(;/<}Jۢ;3<篊D75c~mh8YH><ѠM +^2e?!-ľ#u!i/? ?~=9E6'8.HƝ^aO3ΡFd1H%+1 Z&c=Q~-$cϷhA1Js!QK85<9#sb;m$Z\9ڪ{@9ʇ05Dn0P@yeSs3Yޕ{RwnFٝq)CUi]- rc|X}K.&}/2C`-4Q(5g̴VI 3~.׋onۣ5 *H>"Z>|0p~ Z+v-7E۬gp&#UKR٩AMZJMIޥ|y^~im ]⺾_ӺMBw{ЁFȹ}49Y*isJ73zfP/cw?_QWem /ē%gYt%yAKJ#KJNQFۖll@,73;qW^뢺/Jè&Dȸi6<r$ګק\j;.%&pMפKD@tB~G&f9t> X^ is*+N.?gZHdQ>zK.m+p'?حN"/I+\԰Ϯr<*.CJ]`31sID'.L7!#=nl]fd/w6 KHH 5$yP``,k~jHq~3z^ 4s?0Ϲ )3G"sl"V̋G;& ?Vfl;p|C5FՐT}w 9!1 j')(b BkƄRqy05^T`(2Ě QzZ(qȋIZ1\mJ`\?I ѫDN!8`t =y%kLj:ih~$EW'>9Wٛ&()NW 6Ok 〈|)H q'У v _銳m>J2@t@^_ڒYFmoKVd$S1[1M8U-pQ T5a﬈J`FVzJ҅N8+Oa&&coSw5 _ =qqRLjyt@*l\F- NƗԲ/b^w5x%=b l+aJ-~XC$.T&Ar5TĨށAU>Bj*tkkkp^|M!tV+j|4 \:jZe4X` -4 2 Y&NX6!(O;&83T͑bSw gcutyP 萛rJZ 5Lh" edC'/s4 @uifу {/D *4q;G+p1n0?ȨYuBЛZ1y[BRqBy(ٿ8˭ןF PxV^^RJ#Tr-A2ԗ\Ve){$JI1Lû|:WIk؅vaqPj0Sţ@{4E{5H~>fMh_V?L%"@6YYۏ*+Ws8(83e! oL [yqBJȬ mA|M(G#$SO+'cՍ\a8NR,|R:ɦ·ztpShuF 06>d =a"'8?cmXVLcA}=a%L<-fKԥblw0E346'XV-!.{-Ql ojpe ;&V{},1w>5 V^&2HeD&m20 NmPrfKEMs 2ƪEL#,%WG{Pujo"U])-?i(aW6o_2GVPAAb\sZ }vD_ڀJ=io3GILǒAQCLUSя2@UT9bylf,uIMMlAh5cmᝓʽ#ΑyߝF' +r=֝.b[3?\y>\ pPly *;#Nn MCYǑFf+TnUձ7&` J.$|ܭKM@ͳvqTW~ dd 9I2桡~SˢAהpXү6cNn4u'WQzYE/O註ɮz>*k!*2j AŒ֔z&$ ,~Msc3uq͆dy<1g%KG3#s냍KkV>/W*Ѽс"T0۪q5Ie%2-$%Zܐ[zK j5R! k\K[q]uRD]@j ܸ%@d<6 KUNIs6yщSZ1a]w)& TɝzOUXchK&К lbۤj,@F>\Pu^?L'pv^Z7ќ#xA)ΕCF^3`0'TBBIMJpr`DἹm%/_gH.7m4q457[>w3|wڼ-}InFx5^͓[@4^?{#s0$%).3g|8M2nypM B0Hs^e9\YKbifeQ5ﱖ)+~O9Ϭwc1b`1C}jxǘ'W%8|7B\IuX&>ژ%e"a$*\1J 4}\coU)#O?cJ! ?Ӫ\QYZmm俯N(v{ZivO]gG>K5"_g8Ij1%6TBUr~eW\{4 b\y5ڷygNzac (JT!5!"] ;4U.mszbw[e{@Ej͞=+W[*)5 ,- U?v7Y3 WT8go bTr0CNLPӻRM{Fp{)ΫP^مy({4(8)uKD5=-sn[K19a/},9vzuvjIyؿ0$¡9r U5Mmڮsv?:qD.)y.VeDq>@&nD/d}=aSWH]mcgBK _o'v60Ac]/ѽ'٨1]LğO qq"iO+lװδ~-O0VT2kOwDF+ v@${K.x*ʻ Ib\u@zm!،/RWtI/X!$ 7rsCcn:I% ӓ1I0j0:1u5 fArcrT)Iz.cnƅ?!8}% V(Zqc&h'M#W*L/),YW?ܮ(y-6Fbjjb%K*({@pF܅?]C&yY./VAEW-ѭ=[ W|ycHϩ~px+9Nmx6xk䶓fmS,=Xͳ ) W[.'9Hr@(ٶ;D 0jlZ{|ڇpalw(4>( qN rNJT[K QyrR.. Yʮ8Z͊#̶dT` !AI8}]V" T7~>>U\q88,v4.H55ع28!|X1sSzZx7&ţ:~|ӖSCІ3FxMU +["ohoa:>r]5E7OEjT#yR4]Ə Ÿ)\.S4ddj c ȢXPv/륎^gu 2ʃ:^T 2lN GKCAJƆXe 1r0C.,j S;PR<]y'%UV\Ζ^.drk0ۤȔWȚ 3g߻__f6Ӧ׃1bW[A/]~v~X>"h }7GmIY='ޢodKNJPNWK<8+ĻdP/+gb' =<ׂ4$ Xd;5Q KV@O/rADȨoZ]/UtV׃۾~ /%ql/uX0sR+޺1*s6OͷEP\$" Jf:Hj @y?{eaͨw2Ҋgi% k9yd#Ԍz%!FBt^ ~4)t+lLoMYEzmhyMY_S Z?LU iD/D}.INy|jyERxADՅFOoB[~GϙUEռG{5专*.`bV;3.K !̉=71 nB' !)|D#ript;]}I+R-ge5/5BitgFU*\et ^/V'RE9a&q! RTksmA~;P(R֛rĚƆT*$/J;S15[tn㊯Hռ/n'k_ X;x鷿2\-sWOs/9^ I?_3|<#g:ҨABīQD2b-ٳ9@!͞ āN}CA=m&{ , $,tR\ EptsѴy(TU oц,uj/%*dNݹOzCf|&,mB!G DƖ{sAJS`{$SMKqDѱ'oA$2S=QAmA-;O!ݝ2jWQˏ"bEXlCl4NMiC\ȱoQ;&L4~5=͖OqݙǼ TX"i, vEc'y\|+ckT9zQ.E,&s34$s$sM赭ww݌n-~(I``q-4}< ƳRBF0!7WeӅGuf]AP {d" >sflIP"U#e@4>\8Wy;xūΪ /?`A:m; 3F0UQ=0zܸ'̝UP#\af6iHصT\0.aV0 eS5o` śٽGsŜlN^GWܶ)66*Go g3q,⑽ 'U:JKx؁I<+:PJe38uW$xr4#ҁ(-_8mxK&{+S>GVْ>GJ(G+u!@hV`8t%SL^[+ .K)][~ZXntb9!qNσzy T }NK4R 8tb(:>_l&Y jȺKǴZd?_4F (x\K[/!3+-[NL Y~xGq>h&XFԄ"F(}Jd{2+Sss /MnM!y>ǰ~BFeIS?#[Mc}\ҏ~AoihV1FZh'4U8яz|*&HzubyN9xE4!>ڿ=袳U-_TL9ǻؖN͇NRw̠^a*]=W>XGE{ Opbq {P_Jl=D&ff3M6+""~ysȥ#T84G3$+:bB <ؤCm nAdxޮcyr{ݹwx)5F~){e<6AڍNgk~H8?okO OT zy3.dS@g,P%ROPs%>aeY8R&Ck]/h/R1nwIUW)i pk euaݖhZYs*]8wSј H"ձ/5l%>NKApTt_wt0ޱu*q]\;ͬ%4_(̄Fc@L}Hl籮Im"X[C2]+h/e $Ud&ťy}6BF${DNE9/_}JYT0/c y.j%wzX/ pK)lH}'SL r!;'ʱlEُ~{SiLM9?_5qU@+x=8Oբ~)V.Mcqff jCR*ĺ P>OZkX {0eX&n4;+aXhC{zՋ.dMP=0X~MFŧ\WxېHY9m`r ek3ش= 7/0LZfjFEyR5Wh%&Z-J~a[LyOC]'5f].5V6%6r}=j95tK^{[{*h@B&b&/`A(݋'la2gne R0 4xj<:#듯.@!žpfqW(,N)G_'gq(G+[SRq}}$^Cv. PA--z6b1$U:=B"&uppGuӡz%m$p>RXG%G$SKqW]~ dMz+ib3C\MJrq/C3_:kBvQ[x=iJj'iYg[QH a!nreRE{hc -aKPH Dt<1ŕyXQ%ZSQ-&k]^⨮>#nrjBƞB~\/TW=8˃o/uk[DOBU{ɜ,3P6({e.]s>♈P񨜉U?:TZDPNh ɍeQQ@@CaSy.%ZVBQxU(3v / sK6E&ƒJO% N;`!fH6Mzn7Jw!]Cʮk=qC \9a[.xmU.=CJ]D8T-rpy@cKve Bhw){\L[%7NܫCu`fhwN&tБOCkMK(8㴋*%ٲA|wvc@@ 8\S '1#uգF4=pc-YeLsC4azDf2}o苅Q< ϑ5_w})<#ُ<#h #YEעrx}{Fg4(ƊaR&tGz%+&-+@o >8=2^`VbkY=KW1dz2K+17/g#@jeCi|ȅ~'40D K>K9n\D&b<χTu`}}av !CGŊ*hB̧TA>&۰zay.d-ﻪeX 9C*ԮlVlaQ ԉ,. kqc}wM_fS=993-\9خơE%h2nw¸Ƽ=-_%& 9c$+ T HhCUGT'Esө=,3s`,ђu!% 3b| ~4F:[>:ymtc:) lii̖@uYyqA}l@'bIu\Aoݦom~+[ps(3Jj0,.69Z"nbvI5+6۲́8![7hu}T,&a[uyH3D%Y'S=_htS9*"hBVN}wLKIF/.vsyH.*K0~+ƹ.ːc P <1a5[8\)e!N}v䚳e iܫ?? X9"v}UA ?\Tws+$iY#f06eilC;g2 j>_`}AsL؊@>ie&% teS#ufp|1.5Ywo&z ،g|S6St `MgF,Vo/99I"d~TV ezl M9ُ)\( P cocyNb -9;dA&=q =Vqt s4yBZ֤U`rLtD 1((D}yw3Nj-K"#c2Y((AϤκc*1% u{|[7P8P-/hV9ʐH(1(c%urhgH HXt7( ieOO>v/g Ҷf"%G؞ TʞTϣAW%WGzt:pvGx"S``VzKt5'pW+Omlrxi},O1PE4q# Jsv`# Q<&zKnj(M=zK<{C%5.8+J P,lÁw+.9ebd.lE{1 +Ž!XRii'u\JrA0 =kHv*8x4 43.>| N qhJRFO;kr)`=5+DױȚ!(PR߿O?v aƎ uV,w8%TMUBLjU&cb`b}頊=$/.;n__W}iuQW`!18[[ҍonnɫ?)&SfLɃn-!#78UlHY#d͚.U{`}Bl&CRF5r Y `Ʋ'j"V~lBFj"E{j#*)`gt/$kmgq6LjK>MwJ*CNI%AQU΀*j,Ou+>v7(Y` f$5?)RXgy-j@5P-,떻m%Uj~='Wd wO" HbPc0R9oIަglr8jj7|ώEFeeOd /z*:<=}&*?IǙb>K3V2%^ dyOJ'?|8CTYlW]_@$6HkHuK -4V^u 4> ,r1<Avw˿3]"CZ%FcL, PMD]WW$haoy_?7Kc!>A]XUrl+,^vf%}zwgT&gR@XΘyIgax 7 DWJ); 3e,4BKnUmZgA[>Ek֬԰Tý'f^YU@% AڤJ+;m)Y<+TҪy)f{72FFd@AI,ΞKӜP:Rͣ JV 6rpwB|m:-inǴ76Ocm}Jou´n;~FQ_-BimDpK%ŖLgO5^t)&JY\#}wQUfmGjNMZ-d3K7Ѳ.Lts> 5VbjmXЈ5F N/S8 `JiS-V(/HR/<͸/滢΄w4)ӵ*/wKN{CG"$X(FHr4}HO=φCptl/ EP`@ә( HJT<ڿ 'k2 A"x :MW'e;,4%gx۽CەZaѠVHvN~R\ӅVB/;SS"c3-Z5 U߇j2ѶMs4DhS6ЬF&Yr9RFwҵ)6`6f4ʀ"=A㋈@Ѝ"0!5^޶XrUxBm׀Fف l!|L ~c}ЄG\*L-iuIypDͷ˳iZQgaYJ԰~3<.sXfN]Yx)? aW5E'^(m5h+ ;ga_\P)@n.nu`C|a).Zh{3<{SΊ-Sk;+Bet{ƫ 0-j鵢RA`Ғd(V/[h><;<+*K0$  F^(T/2\YMެעGfT@?lp$łe  e_SslClQaw<+Wo;"][ƗǏxSlZ. +VN}r% hK)4 j%d<cd-~36NbD<)9U#l)?m4^f6n2,Ϋ>\ʜ~-ץv=i4mEjCx%]-qo?MyX*$-3Syf_ 4@Q#(ϩkCarm&E9Fn294U3= {TM\W)Vfd+wId.NCԔD@ӥ<؝Z**F xT(gtnI&Jvs:h %%EIP DApL= Wn6WG(A^{+sgCH@,.NR1Ȓ.)lB'{ߧs l\ %&|J jGLn^ NիYm$EVA^'"9Vٔ&s)=cj)/ӯa,9QX`R O/W̫pvaP>? ZNKvm{Jt^#cjaZD 똘F㷩8|]*s-r+nJHnZ}nxK笜[w&ٜpD뜒gp| ^&+0[]aohB<˷veh^b?֘'s7fͯ:`eY]Dzm.Êo` RfchCe̋-)_4g۪OPЙ%4wKblB)' K:+ް9_bJ E[Ӗ>?>>7r쑍*en-bX ЬZBTP,X\V]& ⷞ B/$#u-1Hu&$bឈynRb^e| ,쎆3)nPqWIW <=8DnR `liNow%:{٩IbS~u݌N~OkQq)[rw6Xa硕?0 F4Ë =!TC)9 jqy7|x?n#9vX RMDA4$e|R32XZ^]N#ьK`") $tN4.-њKOyxaBM2'E8zr|;wR<M.W`˔/0ȶ˸ܓv4Z0Pq޲'F,w{?bP:B3_UavnA 9ʠii#LjRe: 3".~Ta vz_MܖO_i~^dz}$}3hܵmPCcNo.Șm'U{\Z gj|CWDʓ' 'p4]pZ3au6aa! V=ZkeD{6qAtMu˿ rItj.U.5~o[w_aD9]G?c[~\JQk\1(3&+iFw čhUS!lte >oq0tGȇD6v>ѼW5oل%]wgsD [UE/#hV_ԒmNV6B&)aZWkY&H-Oק0I(v^}>d:Np !ZF[*EOL5x**%MTu&f";yGZjIB2]4ԘX{n>B 3$HU:2*FUA֙mfTQ0JL~;3%01uG&|9/ej)9T4yY,7Fx0]J \ *ZL$Y=1B/K;fL7hYiC#xB̎Wu^iJIYtibW_K=T&"O}9(e_kip. C GʯN[ u[9N$HHG"楲EA \( C9X}6؏nB I*G]tndx꣆Fi=˽rIs)>v  DH)>6 ƯOkMc|mɛ_r@pBRۛJjO8ZZ, 4 (#thL8&k QV#8'dU 1RC>6Ќ /~EBB#s['M j!ޖ,># DY9v:l~futTۍ_w> aJQ{Z@)7 qT5m2L@;,(4 vڝX 响x.vr RD*G(oyj JlKQuk#A@>[m [!vmz1ǍYwpIJq*7j+|^ &l+Bw[Vdd-f>l+1hς pe0 ZZX!3dvɽ+),cęU4'>  $$0f$'=^TIi̔E'вj~Ibi5 <ƾSlRI։$j$v[( ϚOC~\ aKUn{U-.J *hd=;Bޢ& "IT{Ɂ_Z|"N!ۆy2:)%C&(O*6'pr CN@az2jxJ4n$::ۓhSGdʳ Z9l"+vpd̎d<̊]4(͇)f-B3 z9y7w! y &TG@7IfR9ectHB-wD&,$`jҠ7!m_@SHt3 (Kj6$PV7s`NS_'q oOu1[7հ*<z=T0GƎ0n2)D(SzVLkHA9^ 7(yQA/v^?Es{ruuɈ)?ψZFC}ءң3wq4j6f=;DQ򯈅r^Cg bK>YZBu!DRW*Z?c6sZH?}=sڷ[ 9X5v}K43gRy:wI05.[SP qg T!TyؚcmT D$Yǡ r(r.:>x\ϳr# $E40"X|̆V[1 u@#cGN>LQZ?k;JF긮k`v u}}}̍NУ{~lQ\Z9W`P3- ҋL,Xu50{G.Gh)kL% 0l(fF2a+(6YFd am8V_,>:V{]L^&]"i"O~g!7Q>d+ث}Z#p7l5 9[XI[g ~d;&G㩴&QEKc""$4ʉ]#_Br `&s G7Xg̜ 22Gi20ϊ>~9sJ?WYcA6)V"DC8|I Ą1k%1s܁Oϧ~.Ɯ:qc̝~Jxok诳߶f $}fм&jأaC )k "6^O%F]1ЉYNN #w*dʐۉ&' L/ALdvC➺ZN \ Oٱv3o8ѓ O-M 8a@Q~ ,2([sc %~ XSVl 6/khSb6F3jo T(>P2W_KOԵnWD>OC aQx>7F]^VbT;zg-seJ0l7s=VްRu*dU3{f={^̻8~WTkKّ\B@'ZDKi&B6VVD&TN ަ>hxܼz{QEb-1lam5l4)KVcCɺ+{ KJYTϋ=ݕOZr0~舂^<4o;-u\z>#ɿ,l։`hhq"w WNh =;|M_4YK{bݵXK̐{M)YH] ыnsY\d-E:D˔;#E]8!P!]1KӌQU` IʬHV+^kCeAx6;s,X9=͡['鷛gJR3 Ebس6,% _̞5SXe!S6; xi?F٘\a E>d2T7>1YILKVeEW͑X N"׽p#yPy@T"i#nM4UQHd4r%˵;\m`jLF@$`;//RF02` s9E}ɛdB$\ n%LTR[ƿ.j{oY[`DJEFSEa`jJ|Ҕ(2~1D_nx8Fxϯ/);Mcc?R!bOklo5x1`|Կr-q^Ǿj#Qu(+engr^UJyzޒ64tc T.y`?|;_z\vg^Q4 R 17ܮh.n7(ϡOȆ[Y%.CxV}LQkz*TnpbKiO,cT}L^)q=I#SF ``QĹ_{#@k Gh)j)whk& q /je΁jix@E I:/Wtգ?Rӹ7Nӌt_o_ѷQvEx}``2ߵ=tXZt53}k٤d-WW97AaP;݂W#tŦ~Xl & 榣l^D6SܒGQR|K$oA6gHB1\"ʭKiկdPBd-;T ;y iJ֠e\~oFXH>-&ջP3t:a /,_n4Ԥ6;uf@_0U+)&}2_QC~ %{a]"iK{?9DR-d%X&M 521J;UM7j?5>˵FV4D`Lj꾗@uKNw3/uoSî6ArQV/kS5i>W2BnlP9WʄrX+@$N]^~ᖙos1 ƪdq@=筵:o<2?e8QߢIȤl -f]d.%y7]CtR݅<2GnAnqEkJ vZ!C/ʃtƻORklYm>R}@A$hHo8_{췟8J\pwSNtqAB-o.|Q=FJM-J-a8SL [QYg xx$k )!j:ƪ0 `/es06puA$>j,`K٤]3*c7St %YUOKh =SjBۺ[7@w ~:ḍ 0Ennd0'B*Ur.`.|fCս+gfz[>تM(higT a2*S'n !+$*t-TZ8[gnH_V'&5#Z#ݿl}h4&dUN mI2AH+L$D8i]t!,Ꙑz3h${I\~fF3Me f7tvGDS/5BEP\A D0( Pmv fVEςFۡi "Ҁ7'DjBtDJx7r䝕sk(FY3g];iK cI6@op:ATYA'ci*c(q&ǗA9g8_Nn[jC 5IDOPnEiߕ\_п;`S~coਢ[4|,M/6 XRЫkjXd*xy4 zL L'Q;YP|&C0 7gU)Ϡ=sݵꪗGPY[ږ[g/Z=;P %ꥀ4#[{EmMH,@%cÕ'C[r>M60/ [(EtuDGjڿ{]ϻ"u:0xi.MK^I~}A}7[S@[Z`. !N$ld9?e9E xІhy0%f:c0ʥmX>iKmrz<3]GWR6P1 j5QĤଅ")$ ƃ?YhТ 9V 2%/T/HVj͆*Cu$x350-Ry]W%t y_LXl )\# P.EL 4"?!$U!gkllxí̲lrXIiaɈEݿ>UTZIhae♬{ic)vraIP{I/5_6"߹06'h#x-9^y V1`Qy[%AbHaT8:&V$FG97/hs`<ZcoHoz&$)d$]cmBvJ&&fu(>E:_f]b`$HlXƒ7#M4^^:/ _ئ{ oiЀ<*#>k\t3/g9It9 эFH4И6<^"Ih7mZ+ƳTsRVfdSO>tI;ʢA#oߔMQs> FeΜ`x+/dH:R||?ylٳiB",O5V+VXD7-LRQ7nȮg NM |9uMff#ZU 3NZ,2o(OݺQ_܏ V,CDM*1Jf_B ~sbw#c%'^>|EJQ+1]uq%ߠEU2T$86I;YmnR!V , ܚjQlQM6 )Tʧt瓆B2j=DVA~4cwld6+y ` ~K#MYnr&Sp2K,ƦV+F*k2eN{ݣRV |0?W'l`tI>d:'848N[hm2Q]0lg;ߑS4 8%8&:M_,XPdi b\Xkfh`m,^&;a*\Ⱥ`!+𭇾UFL kRMpQOsc˖[K-f~P'bl=¸)jvήXE)~>y3's?4}{,Gn蚻hJMVUT'Ĉs(6.<ӿ߫rmya p ƾW{{nl7۩򕃴y38au|(aT;[lzY-2|C( Ka%_3$*Iw:udns6?N e)&uWT%uueh46:Z>u4Lwl`u=q\ϱBkljWC<Z}j 4&݊٭)S?4e$-@>@KҲPFyT|)aJg^)A xׇZƨmp "ݺ9e]h|~ =2L|kT,92S{!_vǎոi7J6wsjxđ0ޡGi*ƒ\j>#$llhh|Ok*@k3{E"lGv;j}N8A'3+kߪH`63s3`.o~s=o!fha ztr}Ԕ -2mp-BªIadE;U)_ӁW Z8tȧhb66*"5p*^+k;2n2){F6'6e!Z,!$\iQl|큊__0"~ѭnr>vW%YF[\[ +4ž44wD'=g-KIK2QaHZ7`N5^}`_P]k!C5L#—@/J:ҵ*4F+g gpQq_^}QÌf㱷c2~g <.&XrcѨl}( 'pb=yNI$"N}A4\>;9{k+rK\mݑ[+օ?>{ih]yRJOۧ,}8AM/h \[r]f HIVv ܗV U׃n L:q*&.ygMS˂%mCǀ] ʕKTLft wd >B:(*LI" h+وvn'U(7/f*bIP@$PexE0&tW~"Iڢeޔf&Jn+M.׽/45w+>Nګ4K(U軛N"Wkã!Ն^޷ö=9Cmp|U[VG9QV'ы?5s%%*,QXlãQU. ߂jAaImz Yv *限ZV3SPtE-67Ui'm0Gˍ%FMօ*Udc'Ow0?>ޫQIOf//[HF0MIjT&_}z G}|^0x,a|:fmch.",*'kzaq:~jpG'&Ǵqo l;*iЩ(`O'~_((J R3z?en_K pFp:pc=)b͚n.6R=%۱%sךi=+K.⓮|LP?Si<0COz̴ܬ%e<h:|JJ-bOMh(Pqo1%/12a~(2Fn,dVBm6r()+Zʗ )SuZNolM>FSr6W#;13`cTև1\/%ɹor5}0揉{0=ˇ>Yci?ȋ: sūLy(ƵcPx8HzMJ׊0N~r%P 2UFNӭCepZPm!2mPu"lkEpZ 0/H\,nifkچCQfwUPRkMU/=~d}2o{e&{ʬpei^f m}lC\~)nUqW(J NG|D)N1 2q4hb<弋w׫tӟzG }"4s OrdfܨTƴeX dBOLӋFQ_ md; Z8ܡed2CASO)bZ]~X}hRzM\^+sתpBsh- $θ3F?e-[:fH X,k ?uKm* efYM*jKM _YnfоίU+Oh3lJ*|.bldFY&Zm0bF+,.|PNt'(EeuW-~`&<RDܕ,|CP֥"s9ZT# wӊtDnG' ?JWflQ e@Um(J2utPy 8bXeqd( !q|f8Ƴ^m: ѨbhOKϧ-I ꃧc0O)2I+%>Nh@煫L+1Y Zz)yG؆fFB?'R : !O8"&݈(MaeNT Gݱ󆖕x?,r~pm#KǍ̐/d(D ] 69jbgPA%7fR5d8tuN\À8XJZ{^xDx2f ~z@q`oB sP&)c-H'cu΍%~ 7ipu;???׼~enl򁄠$7Y3f$p -r;V;An}!Nw"*Rqa&V2jjr~zP؁dj>RRE+Ŕ5sdJ$9dX9.W\麔C25,[1Ը;ޓǴj4 }-|$f^$TIeT2%<aidEΏd w)10*@4ڥG sdVwm )KmsڿqX`x]방)\^5_2:.h1' relk#ʂ^lF. vI\(ռ@J\ E}N]Ɯ=14.7!̡kHpe a B {6 o:rW܎-lG$;%]^:Bƙ‡JC.Q+a{/Ŭɇ_0QZv@Bdw JOYi/Ӑ&=;/ۭ'LiU|ą&913z\H|PĐ5D1$? Iܮ;^Z#ky ۯw1])r TT7j1lU=sBZO3%Ӡߟ}V&J6)) M "$hjӾ&'HY eOR) f/00X=7:Sr8cXkxe3)#V9d(a!;[ܳ-?uL'- ZRo H Yn̷J"n]No7FyhC&*]cs] yޛ_J.v`;SNB*4 kLޮ O]]؂3V{?m#LwK$Ԡ&Q1SCCU.ncusmpY6t_ι T \^z ?<j.&7rGL(q$+ ]xd@]Gp$7Adqu*A:>dSG̍z>0ihub0"u^EF Ot Ʋ0eqѥtpfB衒MXH1nC5tF79qs>G#yiȣq V1$$u| 9x+f F`Hna ]hZ̯s e325v/BqVu"a}'!-EZPI5NҶTRssN(MYmJYV`N~Crj߯2Nw-ئ_ER'|ϧnvN˰ie$5 aᖶV’.>d:*NȐJo}+aa-q3Nb~p7H*mC u$|S?KGb. ?? hW:2;_~L7ݸE[QP+JE!Tn k#4'[_Qڥ3udB]::Xb'/@ /| jnV߆nٞv5zΎSg2 j7Eќ]0=lI욳jʧGM_91%!1(lrpeQY8ykPEjxڋ\"ensc/W b zRΌ履tk_d"U XL_>_ 2Ѳ(0mPoqSO`3V ~(_WR^]](bc̶&tH+2H͹ 遖 ȍ抳#0ęy8Ero5;|7p=rdynaأCIGƬcWc^i':^\T?]HHCfKEp d cV ó8zu@/>[qr-hVm[ v]lfp~!Kpo:D(pS!Y(5Ő()O~[S|75U _*tpLۻdqXB_y12%IKKX&$)6أԞ rT|OmthP`!<‚c984̼n\jr{\|Y 8XAcY6b#Ur:Qɢ{֘z֢x)c}5[ED0*ȈC/{vߕQq(P ;<:(~1OItť@|䰾 \ySzH(|FLU+ }EF[ ̓Ǐ<Z{5g*_)Xm_)5Ͼ6QZ/kt!!Nq%Oӌ_mq_r45t|rWY\)xr"$zmZݓ|ߞPBy_"5j\"^?+]d=ⓙQ9%ʙj<^9@e>r!znEJj6Dqo̠TԺmS׊tNi@z@=YhviqmU׷ D͜DآKc@芋Y,u1NaM9lZY @PpmRݶu+3B}CGԴÉC\B/qePXZˡ˧x94Zh'+.ڀ\ X 5uN5I&+H'!evLHɛXM9b`S)!w1&Ga#(33ZfBJl_!.k$7VaOoAu0&V鱶✺ތT')k*g0(c s$7_/5A|f_yWχEaoͫD0(i1r\%7J'H?χC<VY[~lU.xB_zOwfW/ov/t`;DZV~8q' 1ok/kKy-<М[AJ z o& I U56QM1ϑY&:MԢ¿ zZh)KwDQ (3H`s('4}Ewowa !.jRȄZqRf]'i.f&X~j-VqW#L&IL6ĺnA5l:ڍTJWVd z[L=p#50w11M^KGnE+1Ӑ&./ +'|=0tRwOj_ܶ]F7X3g:HGox|΁YKE'c3)^w U? ׊]얟cI6v- Uӿі``ڋEA0\D0c%dp,OǑ/Oi*=1uK 攫mW >cȕg.-%nj_Bڽ6( 9YVq:i I (zG<ƣs+(. wQNCjS0|"C4`!H~ N"t0ζda t+^:=9Fq4*"Έ ,, #z hvs ږv}#e*&< 9tr /9a~IXu6/kѽBlkɅH99EHmIn Vx%o.>ymۗ!*|YN[IѸ o1A[<@XLK]v抓>l:ºg$s8٥U4؍ khѣ2 LoV9hn؂`.@p#\`` 39&+zzR0yLurc u8i\)$a`kl1([;;kf XpZU9BLd'9k> ;s<."GXs_6z\1L r3#0+jrMPJ-N]=n:\Ui \&ªԓóB)MePA컽=bIL>ZUJE wiC%E:k*4vbЩ*P{AֳYnk56ChݦYU^8YhoCk)@3vׅ*3G"B d*7}:C1~rZσCOG Szl5JCvYAv`);+"k2 ޴_ o" AFoxgB8?AQ?}j%Q_\,\ő)r N '7;:hC3l*09_fh[$5Vq;z9hKA̭*\_q46u]6}qƏ mRtZ$*(ZWOW'3J(*׳2R{l\[^u)Ŭphn_F%sp ` IMEW0zZ _&1 X[a?RsN*+կquw>PWXS#JGt,xFvQ!kL]2|F1Q3n55xc&$i(-S4 5p!oV+}iEJ@NTԨBtehBtƃ䳵=t$|~"˞_nFeh ^0-EGב;z_|# e:_ь\R{nz~ N38*1pVui-vr"G͜hӕݤcj})H V_3_8~3>5o{B,Igv5 ]Ғ+fI|UqCY]݆6#Ch ZŽ93.-giD7 >mwF} ٘_z{{Ųp FΘUҀkYQ@-beԪF`Y>g4ƛkpLcK|P#R@H~z_"_eD)gӮvM-@ƒYv/Lf TKE)o_z<;L uGj~,aД=oOTBoOGa|4D=}u]'paZQ*Id7 >ٲHj>zѓfYCbk#[+`فk OTrg+, ƈ#LD-w6,C^F=qE=OA$ݐh)_z4E q4iaڠC/ LCip*CxFMRon}gp?wͲfiltaA^.gףy^Ђ##C-ݨ'鴚1%sr:<^3Y(+Ŕ-Êur-wYjAH@oT*\2ǩpYnfN@d]||mA4rK4YZ"ӊKR_eڪ5*<#߄ V\,o'[(2D9̫؏7Ԕ>|UqϣO[$(B(r ,Q]1EQ0TUN3f3N0KS" 1T.v͂gL5c<+-Ǻ|_FS*Ç]08w@wGMF3Ug6sLOR?[ Le#!נ_Eְ;ՁLd}B|!{ON+$ϝ9T{0E[WS$2><"_\/T(S+o`vLR!-+zÅvfÔY OId~k+~VJ18$z4lL3-Y_a/V9cBkf00u__. XF>L9P4WOh$y+U#^Ǜ"ٶ&{4`5*Ũ3\}vbT?[NkĔPQ!zڑP_hD8?ԹJ OE57ZqK93rdSxZk k+ f}sFO/?+N#?mBC˽ a#]?CjZf9^G(퍱ҰV*ԴbCH=?XϿЭp#ln.mB'*{@H- (ȹuG%vzKʛ:Feۦmݴ>;ZFT1o-}HC{ GXݛ5z\7<~ C\ˆ<5.e$19lhZМN{@?"Ix>QSRaF ~ ^_qn,0X(%Py*nn^摁OhhK␨+HýEd"B@n`4"1G#$M:ΌJ'4B!o=DM8w5c,sHG,3 f lQSEJj?PU Q酀Gt|#{Qo@ Ln#n- ^[ ͨ@KMjM܆\:]XF[@)IHxCA\Z5}4y kh'1]^Гp'[~4d۽au}G;^T+YI#IH@=~0+&GV|WQN4A>cp k{"܆+;FRwqgTwU6_%ZA V0'a;%HwW ('!uw8:ka%R9DZ"YQ݀C  =(E*= 7^ 2o&o)o7n߀E6L!?*] o!T1dQF`9n!&H ¬@dpeGQjMLPˣܠe!*($A𴌄eqb2M$ s1N@)gthorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-promise-object.cpp000664 001750 001750 00000115455 15164251010 046753 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-promise-object.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-boolean-object.h" #include "ecma-builtin-handlers.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-jobqueue.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmapromiseobject ECMA Promise object related routines * @{ */ /** * Check if an object is promise. * * @return true - if the object is a promise. * false - otherwise. */ bool ecma_is_promise (ecma_object_t *obj_p) /**< points to object */ { return ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_PROMISE); } /* ecma_is_promise */ /** * Get the result of the promise. * * @return ecma value of the promise result. * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_promise_get_result (ecma_object_t *obj_p) /**< points to promise object */ { JERRY_ASSERT (ecma_is_promise (obj_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; return ecma_copy_value (ext_object_p->u.cls.u3.value); } /* ecma_promise_get_result */ /** * Set the PromiseResult of promise. * * @return void */ static inline void ecma_promise_set_result (ecma_object_t *obj_p, /**< points to promise object */ ecma_value_t result) /**< the result value */ { JERRY_ASSERT (ecma_is_promise (obj_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; JERRY_ASSERT (ext_object_p->u.cls.u3.value == ECMA_VALUE_UNDEFINED); ext_object_p->u.cls.u3.value = result; } /* ecma_promise_set_result */ /** * Get the PromiseState of promise. * * @return the state's enum value */ uint8_t ecma_promise_get_flags (ecma_object_t *obj_p) /**< points to promise object */ { JERRY_ASSERT (ecma_is_promise (obj_p)); return ((ecma_extended_object_t *) obj_p)->u.cls.u1.promise_flags; } /* ecma_promise_get_flags */ /** * Set the PromiseState of promise. * * @return void */ static inline void ecma_promise_set_state (ecma_object_t *obj_p, /**< points to promise object */ bool is_fulfilled) /**< new flags */ { JERRY_ASSERT (ecma_is_promise (obj_p)); JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING); uint8_t flags_to_invert = (is_fulfilled ? (ECMA_PROMISE_IS_PENDING | ECMA_PROMISE_IS_FULFILLED) : ECMA_PROMISE_IS_PENDING); ((ecma_extended_object_t *) obj_p)->u.cls.u1.promise_flags ^= flags_to_invert; } /* ecma_promise_set_state */ /** * Take a collection of Reactions and enqueue a new PromiseReactionJob for each Reaction. * * See also: ES2015 25.4.1.8 */ static void ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reactions */ ecma_value_t value, /**< value for resolve or reject */ bool is_reject) /**< true if promise is rejected, false otherwise */ { ecma_value_t *buffer_p = reactions->buffer_p; ecma_value_t *buffer_end_p = buffer_p + reactions->item_count; while (buffer_p < buffer_end_p) { ecma_value_t object_with_tag = *buffer_p++; ecma_object_t *object_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, object_with_tag); ecma_value_t object = ecma_make_object_value (object_p); if (JMEM_CP_GET_THIRD_BIT_FROM_POINTER_TAG (object_with_tag)) { ecma_enqueue_promise_async_reaction_job (object, value, is_reject); continue; } if (!is_reject) { ecma_value_t handler = ECMA_VALUE_TRUE; if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (object_with_tag)) { handler = *buffer_p++; } ecma_enqueue_promise_reaction_job (object, handler, value); } else if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (object_with_tag)) { buffer_p++; } if (is_reject) { ecma_value_t handler = ECMA_VALUE_FALSE; if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (object_with_tag)) { handler = *buffer_p++; } ecma_enqueue_promise_reaction_job (object, handler, value); } else if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (object_with_tag)) { buffer_p++; } } } /* ecma_promise_trigger_reactions */ /** * Checks whether a resolver is called before. * * @return true if it was called before, false otherwise */ static inline bool ecma_is_resolver_already_called (ecma_object_t *promise_obj_p) /**< promise */ { return (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED) != 0; } /* ecma_is_resolver_already_called */ /** * Reject a Promise with a reason. * * See also: ES2015 25.4.1.7 */ void ecma_reject_promise (ecma_value_t promise, /**< promise */ ecma_value_t reason) /**< reason for reject */ { ecma_object_t *obj_p = ecma_get_object_from_value (promise); JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING); ecma_promise_set_state (obj_p, false); ecma_promise_set_result (obj_p, ecma_copy_value_if_not_object (reason)); ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p; /* GC can be triggered by ecma_new_collection so freeing the collection first and creating a new one might cause a heap after use event. */ ecma_collection_t *reactions = promise_p->reactions; /* Fulfill reactions will never be triggered. */ ecma_promise_trigger_reactions (reactions, reason, true); promise_p->reactions = ecma_new_collection (); ecma_collection_destroy (reactions); } /* ecma_reject_promise */ /** * Fulfill a Promise with a value. * * See also: ES2015 25.4.1.4 */ void ecma_fulfill_promise (ecma_value_t promise, /**< promise */ ecma_value_t value) /**< fulfilled value */ { ecma_object_t *obj_p = ecma_get_object_from_value (promise); JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING); if (promise == value) { ecma_raise_type_error (ECMA_ERR_PROMISE_RESOLVE_ITSELF); ecma_value_t exception = jcontext_take_exception (); ecma_reject_promise (promise, exception); ecma_free_value (exception); return; } if (ecma_is_value_object (value)) { ecma_value_t then = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (value), LIT_MAGIC_STRING_THEN); if (ECMA_IS_VALUE_ERROR (then)) { then = jcontext_take_exception (); ecma_reject_promise (promise, then); ecma_free_value (then); return; } if (ecma_op_is_callable (then)) { ecma_enqueue_promise_resolve_thenable_job (promise, value, then); ecma_free_value (then); return; } ecma_free_value (then); } ecma_promise_set_state (obj_p, true); ecma_promise_set_result (obj_p, ecma_copy_value_if_not_object (value)); ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p; /* GC can be triggered by ecma_new_collection so freeing the collection first and creating a new one might cause a heap after use event. */ ecma_collection_t *reactions = promise_p->reactions; /* Reject reactions will never be triggered. */ ecma_promise_trigger_reactions (reactions, value, false); promise_p->reactions = ecma_new_collection (); ecma_collection_destroy (reactions); } /* ecma_fulfill_promise */ /** * Reject a Promise with a reason. Sanity checks are performed before the reject. * * See also: ES2015 25.4.1.3.1 * * @return ecma value of undefined. */ ecma_value_t ecma_reject_promise_with_checks (ecma_value_t promise, /**< promise */ ecma_value_t reason) /**< reason for reject */ { /* 1. */ ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise); JERRY_ASSERT (ecma_is_promise (promise_obj_p)); /* 3., 4. */ if (JERRY_UNLIKELY (ecma_is_resolver_already_called (promise_obj_p))) { return ECMA_VALUE_UNDEFINED; } /* 5. */ ((ecma_extended_object_t *) promise_obj_p)->u.cls.u1.promise_flags |= ECMA_PROMISE_ALREADY_RESOLVED; /* 6. */ ecma_reject_promise (promise, reason); return ECMA_VALUE_UNDEFINED; } /* ecma_reject_promise_with_checks */ /** * Fulfill a Promise with a value. Sanity checks are performed before the resolve. * * See also: ES2015 25.4.1.3.2 * * @return ecma value of undefined. */ ecma_value_t ecma_fulfill_promise_with_checks (ecma_value_t promise, /**< promise */ ecma_value_t value) /**< fulfilled value */ { /* 1. */ ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise); JERRY_ASSERT (ecma_is_promise (promise_obj_p)); /* 3., 4. */ if (JERRY_UNLIKELY (ecma_is_resolver_already_called (promise_obj_p))) { return ECMA_VALUE_UNDEFINED; } /* 5. */ ((ecma_extended_object_t *) promise_obj_p)->u.cls.u1.promise_flags |= ECMA_PROMISE_ALREADY_RESOLVED; ecma_fulfill_promise (promise, value); return ECMA_VALUE_UNDEFINED; } /* ecma_fulfill_promise_with_checks */ /** * Native handler for Promise Reject Function. * * @return ecma value of undefined. */ ecma_value_t ecma_promise_reject_handler (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { ecma_promise_resolver_t *function_p = (ecma_promise_resolver_t *) function_obj_p; ecma_value_t reject_value = (args_count == 0) ? ECMA_VALUE_UNDEFINED : args_p[0]; return ecma_reject_promise_with_checks (function_p->promise, reject_value); } /* ecma_promise_reject_handler */ /** * Native handler for Promise Resolve Function. * * @return ecma value of undefined. */ ecma_value_t ecma_promise_resolve_handler (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { ecma_promise_resolver_t *function_p = (ecma_promise_resolver_t *) function_obj_p; ecma_value_t fulfilled_value = (args_count == 0) ? ECMA_VALUE_UNDEFINED : args_p[0]; return ecma_fulfill_promise_with_checks (function_p->promise, fulfilled_value); } /* ecma_promise_resolve_handler */ /** * Helper function for PromiseCreateResolvingFunctions. * * See also: ES2015 25.4.1.3 2. - 7. * * @return pointer to the resolving function */ static ecma_object_t * ecma_promise_create_resolving_function (ecma_object_t *promise_p, /**< Promise Object */ ecma_native_handler_id_t id) /**< Callback handler */ { ecma_object_t *func_obj_p = ecma_op_create_native_handler (id, sizeof (ecma_promise_resolver_t)); ecma_promise_resolver_t *resolver_p = (ecma_promise_resolver_t *) func_obj_p; resolver_p->promise = ecma_make_object_value (promise_p); return func_obj_p; } /* ecma_promise_create_resolving_function */ /** * Helper function for running an executor. * * @return ecma value of the executor callable * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_promise_run_executor (ecma_object_t *promise_p, /**< Promise Object */ ecma_value_t executor, /**< executor function */ ecma_value_t this_value) /**< this value */ { ecma_object_t *resolve_func_p, *reject_func_p; resolve_func_p = ecma_promise_create_resolving_function (promise_p, ECMA_NATIVE_HANDLER_PROMISE_RESOLVE); reject_func_p = ecma_promise_create_resolving_function (promise_p, ECMA_NATIVE_HANDLER_PROMISE_REJECT); ecma_value_t argv[] = { ecma_make_object_value (resolve_func_p), ecma_make_object_value (reject_func_p) }; ecma_value_t result = ecma_op_function_call (ecma_get_object_from_value (executor), this_value, argv, 2); ecma_deref_object (resolve_func_p); ecma_deref_object (reject_func_p); return result; } /* ecma_promise_run_executor */ /** * Create a promise object. * * See also: ES2015 25.4.3.1 * * @return ecma value of the new promise object * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function or ECMA_VALUE_EMPTY */ ecma_value_t parent, /**< parent promise if available */ ecma_object_t *new_target_p) /**< new.target value */ { JERRY_UNUSED (parent); if (new_target_p == NULL) { new_target_p = ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE); } /* 3. */ ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (new_target_p, ECMA_BUILTIN_ID_PROMISE_PROTOTYPE); if (JERRY_UNLIKELY (proto_p == NULL)) { return ECMA_VALUE_ERROR; } /* Calling ecma_new_collection might trigger a GC call, so this * allocation is performed before the object is constructed. */ ecma_collection_t *reactions = ecma_new_collection (); ecma_object_t *object_p = ecma_create_object (proto_p, sizeof (ecma_promise_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_deref_object (proto_p); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_PROMISE; /* 5 */ ext_object_p->u.cls.u1.promise_flags = ECMA_PROMISE_IS_PENDING; ext_object_p->u.cls.u3.value = ECMA_VALUE_UNDEFINED; /* 6-8. */ ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) object_p; promise_object_p->reactions = reactions; /* 9. */ ecma_value_t completion = ECMA_VALUE_UNDEFINED; if (executor != ECMA_VALUE_EMPTY) { JERRY_ASSERT (ecma_op_is_callable (executor)); completion = ecma_promise_run_executor (object_p, executor, ECMA_VALUE_UNDEFINED); } ecma_value_t status = ECMA_VALUE_EMPTY; if (ECMA_IS_VALUE_ERROR (completion)) { /* 10.a. */ completion = jcontext_take_exception (); ecma_reject_promise_with_checks (ecma_make_object_value (object_p), completion); } ecma_free_value (completion); /* 10.b. */ if (ECMA_IS_VALUE_ERROR (status)) { ecma_deref_object (object_p); return status; } /* 11. */ ecma_free_value (status); return ecma_make_object_value (object_p); } /* ecma_op_create_promise_object */ /** * Helper function for increase or decrease the remaining count. * * @return the current remaining count after increase or decrease. */ uint32_t ecma_promise_remaining_inc_or_dec (ecma_value_t remaining, /**< the remaining count */ bool is_inc) /**< whether to increase the count */ { JERRY_ASSERT (ecma_is_value_object (remaining)); ecma_object_t *remaining_p = ecma_get_object_from_value (remaining); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) remaining_p; JERRY_ASSERT (ext_object_p->u.cls.type == ECMA_OBJECT_CLASS_NUMBER); JERRY_ASSERT (ecma_is_value_integer_number (ext_object_p->u.cls.u3.value)); uint32_t current = (uint32_t) ecma_get_integer_from_value (ext_object_p->u.cls.u3.value); if (is_inc) { current++; } else { current--; } ext_object_p->u.cls.u3.value = ecma_make_uint32_value (current); return current; } /* ecma_promise_remaining_inc_or_dec */ /** * Native handler for Promise.all and Promise.allSettled Resolve Element Function. * * See also: * ES2015 25.4.4.1.2 * * @return ecma value of undefined. */ ecma_value_t ecma_promise_all_or_all_settled_handler_cb (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { ecma_value_t arg = args_count > 0 ? args_p[0] : ECMA_VALUE_UNDEFINED; ecma_promise_all_executor_t *executor_p = (ecma_promise_all_executor_t *) function_obj_p; uint8_t promise_type = executor_p->header.u.built_in.u2.routine_flags; promise_type = (uint8_t) (promise_type >> ECMA_NATIVE_HANDLER_COMMON_FLAGS_SHIFT); /* 1 - 2. */ if (executor_p->index == 0) { return ECMA_VALUE_UNDEFINED; } if (promise_type == ECMA_PROMISE_ALL_RESOLVE || promise_type == ECMA_PROMISE_ANY_REJECT) { /* 8. */ ecma_op_object_put_by_index (ecma_get_object_from_value (executor_p->values), (uint32_t) (executor_p->index - 1), arg, false); } else { lit_magic_string_id_t status_property_val = LIT_MAGIC_STRING_REJECTED; lit_magic_string_id_t data_property_name = LIT_MAGIC_STRING_REASON; if (promise_type == ECMA_PROMISE_ALLSETTLED_RESOLVE) { status_property_val = LIT_MAGIC_STRING_FULFILLED; data_property_name = LIT_MAGIC_STRING_VALUE; } ecma_object_t *obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE), 0, ECMA_OBJECT_TYPE_GENERAL); ecma_property_value_t *prop_value_p; prop_value_p = ecma_create_named_data_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_STATUS), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); prop_value_p->value = ecma_make_magic_string_value (status_property_val); prop_value_p = ecma_create_named_data_property (obj_p, ecma_get_magic_string (data_property_name), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); prop_value_p->value = ecma_copy_value_if_not_object (arg); ecma_value_t obj_val = ecma_make_object_value (obj_p); /* 12. */ ecma_op_object_put_by_index (ecma_get_object_from_value (executor_p->values), (uint32_t) (executor_p->index - 1), obj_val, false); ecma_deref_object (obj_p); } /* 3. */ executor_p->index = 0; /* 9-10. */ ecma_value_t ret = ECMA_VALUE_UNDEFINED; if (ecma_promise_remaining_inc_or_dec (executor_p->remaining_elements, false) == 0) { ecma_value_t capability = executor_p->capability; ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) ecma_get_object_from_value (capability); if (promise_type == ECMA_PROMISE_ANY_REJECT) { ecma_value_t error_val = ecma_new_aggregate_error (executor_p->values, ECMA_VALUE_UNDEFINED); ret = ecma_op_function_call (ecma_get_object_from_value (capability_p->reject), ECMA_VALUE_UNDEFINED, &error_val, 1); ecma_free_value (error_val); } else { ret = ecma_op_function_call (ecma_get_object_from_value (capability_p->resolve), ECMA_VALUE_UNDEFINED, &executor_p->values, 1); } } return ret; } /* ecma_promise_all_or_all_settled_handler_cb */ /** * GetCapabilitiesExecutor Functions * * Checks and sets a promiseCapability's resolve and reject properties. * * See also: ES11 25.6.1.5.1 * * @return ECMA_VALUE_UNDEFINED or TypeError * returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_get_capabilities_executor_cb (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { /* 1. */ ecma_promise_capability_executor_t *executor_p = (ecma_promise_capability_executor_t *) function_obj_p; /* 2-3. */ ecma_object_t *capability_obj_p = ecma_get_object_from_value (executor_p->capability); JERRY_ASSERT (ecma_object_class_is (capability_obj_p, ECMA_OBJECT_CLASS_PROMISE_CAPABILITY)); ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) capability_obj_p; /* 4. */ if (!ecma_is_value_undefined (capability_p->resolve)) { return ecma_raise_type_error (ECMA_ERR_RESOLVE_MUST_BE_UNDEFINED); } /* 5. */ if (!ecma_is_value_undefined (capability_p->reject)) { return ecma_raise_type_error (ECMA_ERR_REJECT_MUST_BE_UNDEFINED); } /* 6. */ capability_p->resolve = (args_count > 0) ? args_p[0] : ECMA_VALUE_UNDEFINED; /* 7. */ capability_p->reject = (args_count > 1) ? args_p[1] : ECMA_VALUE_UNDEFINED; /* 8. */ return ECMA_VALUE_UNDEFINED; } /* ecma_op_get_capabilities_executor_cb */ /** * Create a new PromiseCapability. * * See also: ES11 25.6.1.5 * * @return NULL - if the operation raises error * new PromiseCapability object - otherwise */ ecma_object_t * ecma_promise_new_capability (ecma_value_t constructor, /**< constructor function */ ecma_value_t parent) /**< parent promise if available */ { /* 1. */ if (!ecma_is_constructor (constructor)) { ecma_raise_type_error (ECMA_ERR_INVALID_CAPABILITY); return NULL; } ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor); /* 3. */ ecma_object_t *capability_obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE), sizeof (ecma_promise_capability_t), ECMA_OBJECT_TYPE_CLASS); ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) capability_obj_p; capability_p->header.u.cls.type = ECMA_OBJECT_CLASS_PROMISE_CAPABILITY; capability_p->header.u.cls.u3.promise = ECMA_VALUE_UNDEFINED; capability_p->resolve = ECMA_VALUE_UNDEFINED; capability_p->reject = ECMA_VALUE_UNDEFINED; /* 4-5. */ ecma_object_t *executor_p = ecma_op_create_native_handler (ECMA_NATIVE_HANDLER_PROMISE_CAPABILITY_EXECUTOR, sizeof (ecma_promise_capability_executor_t)); /* 6. */ ecma_promise_capability_executor_t *executor_func_p = (ecma_promise_capability_executor_t *) executor_p; executor_func_p->capability = ecma_make_object_value (capability_obj_p); /* 7. */ ecma_value_t executor = ecma_make_object_value (executor_p); ecma_value_t promise; if (constructor_obj_p == ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)) { promise = ecma_op_create_promise_object (executor, parent, constructor_obj_p); } else { promise = ecma_op_function_construct (constructor_obj_p, constructor_obj_p, &executor, 1); } ecma_deref_object (executor_p); if (ECMA_IS_VALUE_ERROR (promise)) { ecma_deref_object (capability_obj_p); return NULL; } /* 8. */ if (!ecma_op_is_callable (capability_p->resolve)) { ecma_free_value (promise); ecma_deref_object (capability_obj_p); ecma_raise_type_error (ECMA_ERR_PARAMETER_RESOLVE_MUST_BE_CALLABLE); return NULL; } /* 9. */ if (!ecma_op_is_callable (capability_p->reject)) { ecma_free_value (promise); ecma_deref_object (capability_obj_p); ecma_raise_type_error (ECMA_ERR_PARAMETER_REJECT_MUST_BE_CALLABLE); return NULL; } /* 10. */ capability_p->header.u.cls.u3.promise = promise; ecma_free_value (promise); /* 11. */ return capability_obj_p; } /* ecma_promise_new_capability */ /** * The common function for 'reject' and 'resolve'. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_promise_reject_or_resolve (ecma_value_t this_arg, /**< "this" argument */ ecma_value_t value, /**< rejected or resolved value */ bool is_resolve) /**< the operation is resolve */ { if (!ecma_is_value_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } if (is_resolve && ecma_is_value_object (value) && ecma_is_promise (ecma_get_object_from_value (value))) { ecma_object_t *object_p = ecma_get_object_from_value (value); ecma_value_t constructor = ecma_op_object_get_by_magic_id (object_p, LIT_MAGIC_STRING_CONSTRUCTOR); if (ECMA_IS_VALUE_ERROR (constructor)) { return constructor; } /* The this_arg must be an object. */ bool is_same_value = (constructor == this_arg); ecma_free_value (constructor); if (is_same_value) { return ecma_copy_value (value); } } ecma_object_t *capability_obj_p = ecma_promise_new_capability (this_arg, ECMA_VALUE_UNDEFINED); if (JERRY_UNLIKELY (capability_obj_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) capability_obj_p; ecma_value_t func = is_resolve ? capability_p->resolve : capability_p->reject; ecma_value_t call_ret = ecma_op_function_call (ecma_get_object_from_value (func), ECMA_VALUE_UNDEFINED, &value, 1); if (ECMA_IS_VALUE_ERROR (call_ret)) { ecma_deref_object (capability_obj_p); return call_ret; } ecma_free_value (call_ret); ecma_value_t promise = ecma_copy_value (capability_p->header.u.cls.u3.promise); ecma_deref_object (capability_obj_p); return promise; } /* ecma_promise_reject_or_resolve */ /** * The common function for ecma_builtin_promise_prototype_then * and ecma_builtin_promise_prototype_catch. * * @return ecma value of a new promise object. * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_promise_then (ecma_value_t promise, /**< the promise which call 'then' */ ecma_value_t on_fulfilled, /**< on_fulfilled function */ ecma_value_t on_rejected) /**< on_rejected function */ { if (!ecma_is_value_object (promise)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *obj = ecma_get_object_from_value (promise); if (!ecma_is_promise (obj)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_PROMISE); } ecma_value_t species = ecma_op_species_constructor (obj, ECMA_BUILTIN_ID_PROMISE); if (ECMA_IS_VALUE_ERROR (species)) { return species; } ecma_object_t *result_capability_obj_p = ecma_promise_new_capability (species, promise); ecma_free_value (species); if (JERRY_UNLIKELY (result_capability_obj_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t ret = ecma_promise_perform_then (promise, on_fulfilled, on_rejected, result_capability_obj_p); ecma_deref_object (result_capability_obj_p); return ret; } /* ecma_promise_then */ /** * Definition of valueThunk function * * See also: * ES2020 25.6.5.3.1 step 8. * * @return ecma value */ ecma_value_t ecma_value_thunk_helper_cb (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { JERRY_UNUSED_2 (args_p, args_count); ecma_promise_value_thunk_t *value_thunk_obj_p = (ecma_promise_value_thunk_t *) function_obj_p; return ecma_copy_value (value_thunk_obj_p->value); } /* ecma_value_thunk_helper_cb */ /** * Definition of thrower function * * See also: * ES2020 25.6.5.3.2 step 8. * * @return ecma value */ ecma_value_t ecma_value_thunk_thrower_cb (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { JERRY_UNUSED_2 (args_p, args_count); ecma_promise_value_thunk_t *value_thunk_obj_p = (ecma_promise_value_thunk_t *) function_obj_p; jcontext_raise_exception (ecma_copy_value (value_thunk_obj_p->value)); return ECMA_VALUE_ERROR; } /* ecma_value_thunk_thrower_cb */ /** * Helper function for Then Finally and Catch Finally common parts * * See also: * ES2020 25.6.5.3.1 * ES2020 25.6.5.3.2 * * @return ecma value */ static ecma_value_t ecma_promise_then_catch_finally_helper (ecma_object_t *function_obj_p, /**< function object */ ecma_native_handler_id_t id, /**< handler id */ ecma_value_t arg) /**< callback function argument */ { /* 2. */ ecma_promise_finally_function_t *finally_func_obj = (ecma_promise_finally_function_t *) function_obj_p; /* 3. */ JERRY_ASSERT (ecma_op_is_callable (finally_func_obj->on_finally)); /* 4. */ ecma_value_t result = ecma_op_function_call (ecma_get_object_from_value (finally_func_obj->on_finally), ECMA_VALUE_UNDEFINED, NULL, 0); if (ECMA_IS_VALUE_ERROR (result)) { return result; } /* 6. */ JERRY_ASSERT (ecma_is_constructor (finally_func_obj->constructor)); /* 7. */ ecma_value_t promise = ecma_promise_reject_or_resolve (finally_func_obj->constructor, result, true); ecma_free_value (result); if (ECMA_IS_VALUE_ERROR (promise)) { return promise; } /* 8. */ ecma_object_t *value_thunk_func_p; value_thunk_func_p = ecma_op_create_native_handler (id, sizeof (ecma_promise_value_thunk_t)); ecma_promise_value_thunk_t *value_thunk_func_obj = (ecma_promise_value_thunk_t *) value_thunk_func_p; value_thunk_func_obj->value = ecma_copy_value_if_not_object (arg); /* 9. */ ecma_value_t value_thunk = ecma_make_object_value (value_thunk_func_p); ecma_value_t ret_value = ecma_op_invoke_by_magic_id (promise, LIT_MAGIC_STRING_THEN, &value_thunk, 1); ecma_free_value (promise); ecma_deref_object (value_thunk_func_p); return ret_value; } /* ecma_promise_then_catch_finally_helper */ /** * Definition of Then Finally Function * * See also: * ES2020 25.6.5.3.1 * * @return ecma value */ ecma_value_t ecma_promise_then_finally_cb (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { JERRY_UNUSED (args_count); JERRY_ASSERT (args_count > 0); return ecma_promise_then_catch_finally_helper (function_obj_p, ECMA_NATIVE_HANDLER_VALUE_THUNK, args_p[0]); } /* ecma_promise_then_finally_cb */ /** * Definition of Catch Finally Function * * See also: * ES2020 25.6.5.3.2 * * @return ecma value */ ecma_value_t ecma_promise_catch_finally_cb (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { JERRY_UNUSED (args_count); JERRY_ASSERT (args_count > 0); return ecma_promise_then_catch_finally_helper (function_obj_p, ECMA_NATIVE_HANDLER_VALUE_THROWER, args_p[0]); } /* ecma_promise_catch_finally_cb */ /** * The common function for ecma_builtin_promise_prototype_finally * * @return ecma value of a new promise object. * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_promise_finally (ecma_value_t promise, /**< the promise which call 'finally' */ ecma_value_t on_finally) /**< on_finally function */ { /* 2. */ if (!ecma_is_value_object (promise)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *obj = ecma_get_object_from_value (promise); /* 3. */ ecma_value_t species = ecma_op_species_constructor (obj, ECMA_BUILTIN_ID_PROMISE); if (ECMA_IS_VALUE_ERROR (species)) { return species; } /* 4. */ JERRY_ASSERT (ecma_is_constructor (species)); /* 5. */ if (!ecma_op_is_callable (on_finally)) { ecma_free_value (species); ecma_value_t invoke_args[2] = { on_finally, on_finally }; return ecma_op_invoke_by_magic_id (promise, LIT_MAGIC_STRING_THEN, invoke_args, 2); } /* 6.a-b */ ecma_object_t *then_finally_obj_p; then_finally_obj_p = ecma_op_create_native_handler (ECMA_NATIVE_HANDLER_PROMISE_THEN_FINALLY, sizeof (ecma_promise_finally_function_t)); /* 6.c-d */ ecma_promise_finally_function_t *then_finally_func_obj_p = (ecma_promise_finally_function_t *) then_finally_obj_p; then_finally_func_obj_p->constructor = species; then_finally_func_obj_p->on_finally = on_finally; /* 6.e-f */ ecma_object_t *catch_finally_obj_p; catch_finally_obj_p = ecma_op_create_native_handler (ECMA_NATIVE_HANDLER_PROMISE_CATCH_FINALLY, sizeof (ecma_promise_finally_function_t)); /* 6.g-h */ ecma_promise_finally_function_t *catch_finally_func_obj = (ecma_promise_finally_function_t *) catch_finally_obj_p; catch_finally_func_obj->constructor = species; catch_finally_func_obj->on_finally = on_finally; ecma_deref_object (ecma_get_object_from_value (species)); /* 7. */ ecma_value_t invoke_args[2] = { ecma_make_object_value (then_finally_obj_p), ecma_make_object_value (catch_finally_obj_p) }; ecma_value_t ret_value = ecma_op_invoke_by_magic_id (promise, LIT_MAGIC_STRING_THEN, invoke_args, 2); ecma_deref_object (then_finally_obj_p); ecma_deref_object (catch_finally_obj_p); return ret_value; } /* ecma_promise_finally */ /** * Resume the execution of an async function after the promise is resolved */ void ecma_promise_async_then (ecma_value_t promise, /**< promise object */ ecma_value_t executable_object) /**< executable object of the async function */ { ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise); uint16_t flags = ecma_promise_get_flags (promise_obj_p); if (flags & ECMA_PROMISE_IS_PENDING) { ecma_value_t executable_object_with_tag; ECMA_SET_NON_NULL_POINTER_TAG (executable_object_with_tag, ecma_get_object_from_value (executable_object), 0); ECMA_SET_THIRD_BIT_TO_POINTER_TAG (executable_object_with_tag); ecma_collection_push_back (((ecma_promise_object_t *) promise_obj_p)->reactions, executable_object_with_tag); return; } ecma_value_t value = ecma_promise_get_result (promise_obj_p); ecma_enqueue_promise_async_reaction_job (executable_object, value, !(flags & ECMA_PROMISE_IS_FULFILLED)); ecma_free_value (value); } /* ecma_promise_async_then */ /** * Resolves the value and resume the execution of an async function after the resolve is completed * * @return ECMA_VALUE_UNDEFINED if not error is occurred, an error otherwise */ ecma_value_t ecma_promise_async_await (ecma_extended_object_t *async_generator_object_p, /**< async generator function */ ecma_value_t value) /**< value to be resolved (takes the reference) */ { ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); ecma_value_t result = ecma_promise_reject_or_resolve (promise, value, true); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (result)) { return result; } ecma_promise_async_then (result, ecma_make_object_value ((ecma_object_t *) async_generator_object_p)); ecma_free_value (result); return ECMA_VALUE_UNDEFINED; } /* ecma_promise_async_await */ /** * Reject the promise if the value is error. * * See also: * ES2015 25.4.1.1.1 * * @return ecma value of the new promise. * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_if_abrupt_reject_promise (ecma_value_t *value_p, /**< [in - out] completion value */ ecma_object_t *capability_obj_p) /**< capability */ { JERRY_ASSERT (ecma_object_class_is (capability_obj_p, ECMA_OBJECT_CLASS_PROMISE_CAPABILITY)); if (!ECMA_IS_VALUE_ERROR (*value_p)) { return ECMA_VALUE_EMPTY; } ecma_value_t reason = jcontext_take_exception (); ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) capability_obj_p; ecma_value_t call_ret = ecma_op_function_call (ecma_get_object_from_value (capability_p->reject), ECMA_VALUE_UNDEFINED, &reason, 1); ecma_free_value (reason); if (ECMA_IS_VALUE_ERROR (call_ret)) { *value_p = call_ret; return call_ret; } ecma_free_value (call_ret); *value_p = ecma_copy_value (capability_p->header.u.cls.u3.promise); return ECMA_VALUE_EMPTY; } /* ecma_op_if_abrupt_reject_promise */ /** * It performs the "then" operation on promiFulfilled * and onRejected as its settlement actions. * * See also: 25.4.5.3.1 * * @return ecma value of the new promise object * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_promise_perform_then (ecma_value_t promise, /**< the promise which call 'then' */ ecma_value_t on_fulfilled, /**< on_fulfilled function */ ecma_value_t on_rejected, /**< on_rejected function */ ecma_object_t *result_capability_obj_p) /**< promise capability */ { JERRY_ASSERT (ecma_object_class_is (result_capability_obj_p, ECMA_OBJECT_CLASS_PROMISE_CAPABILITY)); ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) result_capability_obj_p; /* 3. boolean true indicates "identity" */ if (!ecma_op_is_callable (on_fulfilled)) { on_fulfilled = ECMA_VALUE_TRUE; } /* 4. boolean false indicates "thrower" */ if (!ecma_op_is_callable (on_rejected)) { on_rejected = ECMA_VALUE_FALSE; } ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise); ecma_promise_object_t *promise_p = (ecma_promise_object_t *) promise_obj_p; uint16_t flags = ecma_promise_get_flags (promise_obj_p); if (flags & ECMA_PROMISE_IS_PENDING) { /* 7. */ /* [ capability, (on_fulfilled), (on_rejected) ] */ ecma_value_t reaction_values[3]; ecma_value_t *reactions_p = reaction_values + 1; uint8_t tag = 0; if (on_fulfilled != ECMA_VALUE_TRUE) { tag |= JMEM_FIRST_TAG_BIT_MASK; *reactions_p++ = on_fulfilled; } if (on_rejected != ECMA_VALUE_FALSE) { tag |= JMEM_SECOND_TAG_BIT_MASK; *reactions_p++ = on_rejected; } ECMA_SET_NON_NULL_POINTER_TAG (reaction_values[0], result_capability_obj_p, tag); uint32_t value_count = (uint32_t) (reactions_p - reaction_values); ecma_collection_append (promise_p->reactions, reaction_values, value_count); } else if (flags & ECMA_PROMISE_IS_FULFILLED) { /* 8. */ ecma_value_t value = ecma_promise_get_result (promise_obj_p); ecma_enqueue_promise_reaction_job (ecma_make_object_value (result_capability_obj_p), on_fulfilled, value); ecma_free_value (value); } else { /* 9. */ ecma_value_t reason = ecma_promise_get_result (promise_obj_p); ecma_enqueue_promise_reaction_job (ecma_make_object_value (result_capability_obj_p), on_rejected, reason); ecma_free_value (reason); } /* 10. */ return ecma_copy_value (capability_p->header.u.cls.u3.promise); } /* ecma_promise_perform_then */ /** * @} * @} */ lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array-iterator-prototype.inc.h000664 001750 001750 00000002147 15164251010 053625 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %ArrayIteratorPrototype% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_ARRAY_ITERATOR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_NEXT, ECMA_ARRAY_ITERATOR_PROTOTYPE_OBJECT_NEXT, 0, 0) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/memorystream.h000664 001750 001750 00000005126 15164251010 037550 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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(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_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/meson.build000664 001750 001750 00000001637 15164251010 035633 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0source_file = [ 'tvgGl.h', 'tvgGlCommon.h', 'tvgGlEffect.h', 'tvgGlGpuBuffer.h', 'tvgGlProgram.h', 'tvgGlRenderer.h', 'tvgGlRenderPass.h', 'tvgGlRenderTarget.h', 'tvgGlRenderTask.h', 'tvgGlShader.h', 'tvgGlShaderSrc.h', 'tvgGl.cpp', 'tvgGlEffect.cpp', 'tvgGlGeometry.cpp', 'tvgGlGpuBuffer.cpp', 'tvgGlProgram.cpp', 'tvgGlRenderer.cpp', 'tvgGlRenderPass.cpp', 'tvgGlRenderTarget.cpp', 'tvgGlRenderTask.cpp', 'tvgGlShader.cpp', 'tvgGlShaderSrc.cpp', 'tvgGlTessellator.cpp', 'tvgGlTessellator.h', ] #force to use gles if cc.get_id() == 'emscripten' gl_variant = 'OpenGL ES' endif if gl_variant == 'OpenGL ES' compiler_flags += ['-DTHORVG_GL_TARGET_GLES=1'] else compiler_flags += ['-DTHORVG_GL_TARGET_GL=1'] endif engine_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file, )] loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint32array.inc.h000664 001750 001750 00000001714 15164251010 053163 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Uint32Array description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 4 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_UINT32_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_UINT32ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/opcodes-ecma-arithmetics.cpp000664 001750 001750 00000020453 15164251010 045322 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-bigint.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jrt-libc-includes.h" #include "opcodes.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup vm_opcodes Opcodes * @{ */ /** * Perform ECMA number arithmetic operation. * * The algorithm of the operation is following: * leftNum = ToNumber (leftValue); * rightNum = ToNumber (rightValue); * result = leftNum ArithmeticOp rightNum; * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t do_number_arithmetic (number_arithmetic_op op, /**< number arithmetic operation */ ecma_value_t left_value, /**< left value */ ecma_value_t right_value) /**< right value */ { ecma_number_t left_number; left_value = ecma_op_to_numeric (left_value, &left_number, ECMA_TO_NUMERIC_ALLOW_BIGINT); if (ECMA_IS_VALUE_ERROR (left_value)) { return left_value; } ecma_value_t ret_value = ECMA_VALUE_EMPTY; #if JERRY_BUILTIN_BIGINT if (JERRY_LIKELY (!ecma_is_value_bigint (left_value))) { #endif /* JERRY_BUILTIN_BIGINT */ ecma_number_t right_number; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (right_value, &right_number))) { return ECMA_VALUE_ERROR; } ecma_number_t result = ECMA_NUMBER_ZERO; switch (op) { case NUMBER_ARITHMETIC_SUBTRACTION: { result = left_number - right_number; break; } case NUMBER_ARITHMETIC_MULTIPLICATION: { result = left_number * right_number; break; } case NUMBER_ARITHMETIC_DIVISION: { result = left_number / right_number; break; } case NUMBER_ARITHMETIC_REMAINDER: { if (ecma_number_is_nan (left_number) || ecma_number_is_nan (right_number) || ecma_number_is_infinity (left_number) || ecma_number_is_zero (right_number)) { result = ecma_number_make_nan (); break; } if (ecma_number_is_infinity (right_number) || (ecma_number_is_zero (left_number) && !ecma_number_is_zero (right_number))) { result = left_number; break; } result = ecma_number_remainder (left_number, right_number); break; } case NUMBER_ARITHMETIC_EXPONENTIATION: { result = ecma_number_pow (left_number, right_number); break; } } ret_value = ecma_make_number_value (result); #if JERRY_BUILTIN_BIGINT } else { bool free_right_value; right_value = ecma_bigint_get_bigint (right_value, &free_right_value); if (ECMA_IS_VALUE_ERROR (right_value)) { ecma_free_value (left_value); return right_value; } switch (op) { case NUMBER_ARITHMETIC_SUBTRACTION: { ret_value = ecma_bigint_add_sub (left_value, right_value, false); break; } case NUMBER_ARITHMETIC_MULTIPLICATION: { ret_value = ecma_bigint_mul (left_value, right_value); break; } case NUMBER_ARITHMETIC_DIVISION: { ret_value = ecma_bigint_div_mod (left_value, right_value, false); break; } case NUMBER_ARITHMETIC_REMAINDER: { ret_value = ecma_bigint_div_mod (left_value, right_value, true); break; } case NUMBER_ARITHMETIC_EXPONENTIATION: { ret_value = ecma_bigint_pow (left_value, right_value); break; } } ecma_free_value (left_value); if (free_right_value) { ecma_free_value (right_value); } } #endif /* JERRY_BUILTIN_BIGINT */ return ret_value; } /* do_number_arithmetic */ /** * 'Addition' opcode handler. * * See also: ECMA-262 v5, 11.6.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t opfunc_addition (ecma_value_t left_value, /**< left value */ ecma_value_t right_value) /**< right value */ { bool free_left_value = false; bool free_right_value = false; if (ecma_is_value_object (left_value)) { ecma_object_t *obj_p = ecma_get_object_from_value (left_value); left_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NO); free_left_value = true; if (ECMA_IS_VALUE_ERROR (left_value)) { return left_value; } } if (ecma_is_value_object (right_value)) { ecma_object_t *obj_p = ecma_get_object_from_value (right_value); right_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NO); free_right_value = true; if (ECMA_IS_VALUE_ERROR (right_value)) { if (free_left_value) { ecma_free_value (left_value); } return right_value; } } ecma_value_t ret_value = ECMA_VALUE_EMPTY; if (ecma_is_value_string (left_value) || ecma_is_value_string (right_value)) { ecma_string_t *string1_p = ecma_op_to_string (left_value); if (JERRY_UNLIKELY (string1_p == NULL)) { if (free_left_value) { ecma_free_value (left_value); } if (free_right_value) { ecma_free_value (right_value); } return ECMA_VALUE_ERROR; } ecma_string_t *string2_p = ecma_op_to_string (right_value); if (JERRY_UNLIKELY (string2_p == NULL)) { if (free_right_value) { ecma_free_value (right_value); } if (free_left_value) { ecma_free_value (left_value); } ecma_deref_ecma_string (string1_p); return ECMA_VALUE_ERROR; } string1_p = ecma_concat_ecma_strings (string1_p, string2_p); ret_value = ecma_make_string_value (string1_p); ecma_deref_ecma_string (string2_p); } #if JERRY_BUILTIN_BIGINT else if (JERRY_UNLIKELY (ecma_is_value_bigint (left_value)) && JERRY_UNLIKELY (ecma_is_value_bigint (right_value))) { ret_value = ecma_bigint_add_sub (left_value, right_value, true); } #endif /* JERRY_BUILTIN_BIGINT */ else { ecma_number_t num_left; ecma_number_t num_right; if (!ECMA_IS_VALUE_ERROR (ecma_op_to_number (left_value, &num_left)) && !ECMA_IS_VALUE_ERROR (ecma_op_to_number (right_value, &num_right))) { ret_value = ecma_make_number_value (num_left + num_right); } else { ret_value = ECMA_VALUE_ERROR; } } if (free_left_value) { ecma_free_value (left_value); } if (free_right_value) { ecma_free_value (right_value); } return ret_value; } /* opfunc_addition */ /** * Unary operation opcode handler. * * See also: ECMA-262 v5, 11.4, 11.4.6, 11.4.7 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t opfunc_unary_operation (ecma_value_t left_value, /**< left value */ bool is_plus) /**< unary plus flag */ { ecma_number_t left_number; left_value = ecma_op_to_numeric (left_value, &left_number, ECMA_TO_NUMERIC_ALLOW_BIGINT); if (ECMA_IS_VALUE_ERROR (left_value)) { return left_value; } #if JERRY_BUILTIN_BIGINT if (JERRY_LIKELY (!ecma_is_value_bigint (left_value))) { return ecma_make_number_value (is_plus ? left_number : -left_number); } ecma_value_t ret_value; if (is_plus) { ret_value = ecma_raise_type_error (ECMA_ERR_UNARY_PLUS_IS_NOT_ALLOWED_FOR_BIGINTS); } else { ret_value = left_value; if (left_value != ECMA_BIGINT_ZERO) { ret_value = ecma_bigint_negate (ecma_get_extended_primitive_from_value (left_value)); } } ecma_free_value (left_value); return ret_value; #else /* !JERRY_BUILTIN_BIGINT */ return ecma_make_number_value (is_plus ? left_number : -left_number); #endif /* JERRY_BUILTIN_BIGINT */ } /* opfunc_unary_operation */ /** * @} * @} */ jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-bigint64array-prototype.inc.h000664 001750 001750 00000001717 15164251010 055533 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * BigInt64Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_BUILTIN_BIGINT #define TYPEDARRAY_BYTES_PER_ELEMENT 8 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_BIGINT64ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_BIGINT */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgPicture.cpp000664 001750 001750 00000006334 15164251010 034460 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgPaint.h" #include "tvgPicture.h" Picture::Picture() = default; Picture* Picture::gen() noexcept { return new PictureImpl; } Type Picture::type() const noexcept { return Type::Picture; } Result Picture::load(const char* filename) noexcept { #ifdef THORVG_FILE_IO_SUPPORT if (!filename) return Result::InvalidArguments; return to(this)->load(filename); #else TVGLOG("RENDERER", "FILE IO is disabled!"); return Result::NonSupport; #endif } Result Picture::load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy) noexcept { return to(this)->load(data, size, mimeType, rpath, copy); } Result Picture::load(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy) noexcept { return to(this)->load(data, w, h, cs, copy); } Result Picture::resolver(std::function func, void* data) noexcept { return to(this)->set(func, data); } Result Picture::size(float w, float h) noexcept { to(this)->size(w, h); return Result::Success; } Result Picture::size(float* w, float* h) const noexcept { return to(this)->size(w, h); } Result Picture::origin(float x, float y) noexcept { to(this)->origin = {x, y}; PAINT(this)->mark(RenderUpdateFlag::Transform); return Result::Success; } Result Picture::origin(float* x, float* y) const noexcept { if (x) *x = to(this)->origin.x; if (y) *y = to(this)->origin.y; return Result::Success; } const Paint* Picture::paint(uint32_t id) noexcept { struct Value { uint32_t id; const Paint* ret; } value = {id, nullptr}; auto cb = [](const tvg::Paint* paint, void* data) -> bool { auto p = static_cast(data); if (p->id == paint->id) { p->ret = paint; return false; } return true; }; auto accessor = tvg::Accessor::gen(); accessor->set(this, cb, &value); delete(accessor); return value.ret; }mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/regression.yml000664 001750 001750 00000014516 15164251010 035524 0ustar00ddennedyddennedy000000 000000 name: Regression # on: [push, pull_request] - enable when testing on: pull_request: branches: - main permissions: contents: read jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install things run: | sudo apt-get update sudo apt-get install -y meson wget2 librsvg2-dev librsvg2-bin ninja-build git gcc-multilib g++-multilib earlyoom - name: Compile Thorvg PR run: | meson . build -Dtools=svg2png,lottie2gif -Dsavers=gif -Db_sanitize=address,undefined sudo ninja -C build install - name: Compile Thorvg Develop run: | git clone https://github.com/thorvg/thorvg.git thorvg_develop cd thorvg_develop meson . build -Dtools=svg2png sudo ninja -C build install cd .. - name: Download SVG Regression finder and setup settings run: | wget -q https://github.com/qarmin/SVG-regression-finder/releases/download/0.4.0/svg_tester chmod +x ./svg_tester - name: Prepare valid files to test run: | wget -q https://github.com/qarmin/SVG-regression-finder/releases/download/0.3.0/ThorvgValidFiles.zip -O files.zip unzip -q files.zip rm files.zip wget -q https://github.com/qarmin/SVG-regression-finder/releases/download/0.3.0/ThorvgNotValidFiles.zip -O files.zip unzip -q files.zip rm files.zip mv ThorvgValidFiles FilesToTest find ThorvgNotValidFiles -type f -exec mv {} FilesToTest \; rmdir ThorvgNotValidFiles - name: Setup settings for crash/leak/timeout tests run: | mv test/regression/settings_crash_leak.toml settings.toml - name: Run regression finder tests run: | ./svg_tester 2>&1 | tee result_crashes.txt - name: Setup settings for comparison tests run: | mv test/regression/settings_comparison.toml settings.toml - name: Store Crashing/Leaking/Timing out Images uses: actions/upload-artifact@v4 with: name: crashing-leaking-timeouting-images path: BrokenFILES if-no-files-found: ignore - name: Clean Data run: | rm -rf BrokenSVG || true rm -rf FilesToTest || true rm -rf ProblematicSVG || true rm -rf IgnoredSVG || true rm -rf BrokenFILES || true # Test valid files - name: Prepare valid files to test run: | wget -q https://github.com/qarmin/SVG-regression-finder/releases/download/0.3.0/ThorvgValidFiles.zip -O files.zip unzip -q files.zip rm files.zip mv ThorvgValidFiles FilesToTest - name: Run regression finder tests run: | ./svg_tester 2>&1 | tee result_valid_files.txt - name: Store Broken Images for valid inputs uses: actions/upload-artifact@v4 with: name: differences-in-valid-files path: BrokenSVG if-no-files-found: ignore - name: Store Problematic Images for valid inputs uses: actions/upload-artifact@v4 with: name: problematic-images-in-valid-files path: ProblematicSVG if-no-files-found: ignore - name: Clean Data run: | rm -rf BrokenSVG || true rm -rf FilesToTest || true rm -rf ProblematicSVG || true rm -rf IgnoredSVG || true rm -rf BrokenFILES || true # Test files that may not work currently good with Thorvg, errors from this are not critical # it is possible that this will find also some improvements - name: Prepare not valid to test run: | wget -q https://github.com/qarmin/SVG-regression-finder/releases/download/0.3.0/ThorvgNotValidFiles.zip -O files.zip unzip -q files.zip rm files.zip mv ThorvgNotValidFiles FilesToTest - name: Run invalid files tests run: | ./svg_tester 2>&1 | tee result_not_valid_files.txt - name: Store Broken Images for not valid inputs uses: actions/upload-artifact@v4 with: name: differences-in-not-valid-files path: BrokenSVG if-no-files-found: ignore - name: Store Problematic Images for not valid inputs uses: actions/upload-artifact@v4 with: name: problematic-images-in-not-valid-files path: ProblematicSVG if-no-files-found: ignore - name: Clean Data run: | rm -rf BrokenSVG || true rm -rf FilesToTest || true rm -rf ProblematicSVG || true rm -rf IgnoredSVG || true rm -rf BrokenFILES || true - name: Test png reproducibility run: | wget -q https://github.com/thorvg/thorvg/files/11356766/AA_5.svg.zip -O files.zip unzip -q files.zip rm files.zip cp test/regression/check_same_image_size.py check_same_image_size.py # Forces to run tasks on different threads if possible, which should help find problem with data races taskset -c 0-15 python3 check_same_image_size.py AA_5.svg ./build/src/tools/svg2png/tvg-svg2png 100 500 2>&1 | tee result_image_size.txt - name: Check results run: | export PATH=$PATH:~/.local/bin/ python3 "${GITHUB_WORKSPACE}/.github/workflows/regression_check.py" if [[ -f "fail_ci.txt" ]]; then ( echo '```' cat "fail_ci.txt" echo '```' ) >> "$GITHUB_STEP_SUMMARY" fi - name: Find Comment uses: peter-evans/find-comment@v3 id: fc with: issue-number: ${{ github.event.pull_request.number }} comment-author: 'github-actions[bot]' body-includes: Regression report # TODO - not works # - name: Create or update comment # uses: peter-evans/create-or-update-comment@v4 # with: # comment-id: ${{ steps.fc.outputs.comment-id }} # issue-number: ${{ github.event.pull_request.number }} # body-path: 'comment.txt' # edit-mode: replace - name: Fail CI if regression found run: | if [[ -f "fail_ci.txt" ]]; then echo "Check Check results section for more details which tests failed" exit 1 fi mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGl.cpp000664 001750 001750 00000132147 15164251010 035340 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2025 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGl.h" #include "tvgCommon.h" #ifdef __EMSCRIPTEN__ bool glInit() { return true; } bool glTerm() { return true; } #else //__EMSCRIPTEN__ #if defined(_WIN32) && !defined(__CYGWIN__) #ifdef THORVG_GL_TARGET_GL typedef PROC(WINAPI *PFNGLGETPROCADDRESSPROC)(LPCSTR); static PFNGLGETPROCADDRESSPROC glGetProcAddress = NULL; #endif static HMODULE _libGL = NULL; #ifdef THORVG_GL_TARGET_GLES static HMODULE _libEGL = NULL; #endif static bool _glLoad() { #ifdef THORVG_GL_TARGET_GL _libGL = LoadLibraryW(L"opengl32.dll"); if (!_libGL) { TVGERR("GL_ENGINE", "Cannot find the gl library."); return false; } if (!glGetProcAddress) glGetProcAddress = (PFNGLGETPROCADDRESSPROC)GetProcAddress(_libGL, "wglGetProcAddress"); if (!glGetProcAddress) glGetProcAddress = (PFNGLGETPROCADDRESSPROC)GetProcAddress(_libGL, "wglGetProcAddressARB"); if (!glGetProcAddress) return false; #else if (!_libGL) _libGL = LoadLibraryW(L"GLESv2.dll"); if (!_libGL) _libGL = LoadLibraryW(L"libGLESv2.dll"); if (!_libGL) { TVGERR("GL_ENGINE", "Cannot find gles library."); return false; } #endif return true; } // load opengl proc address from dll or from wglGetProcAddress static PROC _getProcAddress(const char* procName) { auto procHandle = GetProcAddress(_libGL, procName); #ifdef THORVG_GL_TARGET_GL if (!procHandle) procHandle = glGetProcAddress(procName); #endif return procHandle; } #elif defined(__linux__) #include #ifdef THORVG_GL_TARGET_GL typedef void* (*PFNGLGETPROCADDRESSPROC)(const char*); static PFNGLGETPROCADDRESSPROC glGetProcAddress = nullptr; #endif static void* _libGL = nullptr; #ifdef THORVG_GL_TARGET_GLES static void* _libEGL = nullptr; #endif static bool _glLoad() { #ifdef THORVG_GL_TARGET_GL _libGL = dlopen("libGL.so", RTLD_LAZY); if (!_libGL) _libGL = dlopen("libGL.so.4", RTLD_LAZY); if (!_libGL) _libGL = dlopen("libGL.so.3", RTLD_LAZY); if (!_libGL) { TVGERR("GL_ENGINE", "Cannot find the gl library."); return false; } if (!glGetProcAddress) glGetProcAddress = (PFNGLGETPROCADDRESSPROC)dlsym(_libGL, "glXGetProcAddress"); if (!glGetProcAddress) glGetProcAddress = (PFNGLGETPROCADDRESSPROC)dlsym(_libGL, "glXGetProcAddressARB"); if (!glGetProcAddress) return false; #else if (!_libGL) _libGL = dlopen("libGLESv2.so", RTLD_LAZY); if (!_libGL) _libGL = dlopen("libGLESv2.so.2.0", RTLD_LAZY); // sometimes used in Mesa if (!_libGL) _libGL = dlopen("libGLESv2.so.2", RTLD_LAZY); if (!_libGL) { TVGERR("GL_ENGINE", "Cannot find the gles library."); return false; } #endif return true; } static void* _getProcAddress(const char* procName) { #ifdef THORVG_GL_TARGET_GL return glGetProcAddress(procName); #else return dlsym(_libGL, procName); #endif } #elif defined(__APPLE__) || defined(__MACH__) #include static void* _libGL = nullptr; #ifdef THORVG_GL_TARGET_GLES static void* _libEGL = nullptr; #endif static bool _glLoad() { if (!_libGL) _libGL = dlopen("/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY); if (!_libGL) _libGL = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY); if (_libGL) return true; TVGERR("GL_ENGINE", "Cannot find gl library."); return false; } static void* _getProcAddress(const char* procName) { return dlsym(_libGL, procName); } #endif #define GL_FUNCTION_FETCH(procName, procType) \ procName = (procType)_getProcAddress(#procName); \ if (!procName) { \ TVGERR("GL_ENGINE", "%s is not supported.", #procName); \ return false; \ } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ //GL_VERSION_1_0 PFNGLCULLFACEPROC glCullFace; PFNGLFRONTFACEPROC glFrontFace; PFNGLSCISSORPROC glScissor; PFNGLTEXPARAMETERIPROC glTexParameteri; PFNGLTEXIMAGE2DPROC glTexImage2D; PFNGLDRAWBUFFERPROC glDrawBuffer; PFNGLCLEARPROC glClear; PFNGLCLEARCOLORPROC glClearColor; PFNGLCLEARSTENCILPROC glClearStencil; PFNGLCLEARDEPTHPROC glClearDepth; PFNGLCLEARDEPTHFPROC glClearDepthf; // GLES PFNGLCOLORMASKPROC glColorMask; PFNGLDEPTHMASKPROC glDepthMask; PFNGLDISABLEPROC glDisable; PFNGLENABLEPROC glEnable; PFNGLBLENDFUNCPROC glBlendFunc; PFNGLSTENCILFUNCPROC glStencilFunc; PFNGLSTENCILOPPROC glStencilOp; PFNGLDEPTHFUNCPROC glDepthFunc; PFNGLGETERRORPROC glGetError; PFNGLGETINTEGERVPROC glGetIntegerv; PFNGLGETSTRINGPROC glGetString; PFNGLVIEWPORTPROC glViewport; //PFNGLHINTPROC glHint; //PFNGLLINEWIDTHPROC glLineWidth; //PFNGLPOINTSIZEPROC glPointSize; //PFNGLPOLYGONMODEPROC glPolygonMode; //PFNGLTEXPARAMETERFPROC glTexParameterf; //PFNGLTEXPARAMETERFVPROC glTexParameterfv; //PFNGLTEXPARAMETERIVPROC glTexParameteriv; //PFNGLTEXIMAGE1DPROC glTexImage1D; //PFNGLSTENCILMASKPROC glStencilMask; //PFNGLFINISHPROC glFinish; //PFNGLFLUSHPROC glFlush; //PFNGLLOGICOPPROC glLogicOp = nullptr //PFNGLPIXELSTOREFPROC glPixelStoref; //PFNGLPIXELSTOREIPROC glPixelStorei; //PFNGLREADBUFFERPROC glReadBuffer; //PFNGLREADPIXELSPROC glReadPixels; //PFNGLGETBOOLEANVPROC glGetBooleanv; //PFNGLGETDOUBLEVPROC glGetDoublev; //PFNGLGETFLOATVPROC glGetFloatv; //PFNGLGETTEXIMAGEPROC glGetTexImage; //PFNGLGETTEXPARAMETERFVPROC glGetTexParameterfv; //PFNGLGETTEXPARAMETERIVPROC glGetTexParameteriv; //PFNGLGETTEXLEVELPARAMETERFVPROC glGetTexLevelParameterfv; //PFNGLGETTEXLEVELPARAMETERIVPROC glGetTexLevelParameteriv; //PFNGLISENABLEDPROC glIsEnabled; //PFNGLDEPTHRANGEPROC glDepthRange; //GL_VERSION_1_1 PFNGLDRAWELEMENTSPROC glDrawElements; PFNGLBINDTEXTUREPROC glBindTexture; PFNGLDELETETEXTURESPROC glDeleteTextures; PFNGLGENTEXTURESPROC glGenTextures; //PFNGLDRAWARRAYSPROC glDrawArrays; //PFNGLGETPOINTERVPROC glGetPointerv; //PFNGLPOLYGONOFFSETPROC glPolygonOffset; //PFNGLCOPYTEXIMAGE1DPROC glCopyTexImage1D; //PFNGLCOPYTEXIMAGE2DPROC glCopyTexImage2D; //PFNGLCOPYTEXSUBIMAGE1DPROC glCopyTexSubImage1D; //PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D; //PFNGLTEXSUBIMAGE1DPROC glTexSubImage1D; //PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D; //PFNGLISTEXTUREPROC glIsTexture; //GL_VERSION_1_2 //PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; //PFNGLTEXIMAGE3DPROC glTexImage3D; //PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; //PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D; //GL_VERSION_1_3 PFNGLACTIVETEXTUREPROC glActiveTexture; //PFNGLSAMPLECOVERAGEPROC glSampleCoverage; //PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D; //PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D; //PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D; //PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D; //PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D; //PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D; //PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage; //GL_VERSION_1_4 PFNGLBLENDEQUATIONPROC glBlendEquation; //PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate; //PFNGLMULTIDRAWARRAYSPROC glMultiDrawArrays; //PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements; //PFNGLPOINTPARAMETERFPROC glPointParameterf; //PFNGLPOINTPARAMETERFVPROC glPointParameterfv; //PFNGLPOINTPARAMETERIPROC glPointParameteri; //PFNGLPOINTPARAMETERIVPROC glPointParameteriv; //PFNGLBLENDCOLORPROC glBlendColor; //GL_VERSION_1_5 PFNGLBINDBUFFERPROC glBindBuffer; PFNGLDELETEBUFFERSPROC glDeleteBuffers; PFNGLGENBUFFERSPROC glGenBuffers; PFNGLBUFFERDATAPROC glBufferData; //PFNGLGENQUERIESPROC glGenQueries; //PFNGLDELETEQUERIESPROC glDeleteQueries; //PFNGLISQUERYPROC glIsQuery; //PFNGLBEGINQUERYPROC glBeginQuery; //PFNGLENDQUERYPROC glEndQuery; //PFNGLGETQUERYIVPROC glGetQueryiv; //PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv; //PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv; //PFNGLISBUFFERPROC glIsBuffer; //PFNGLBUFFERSUBDATAPROC glBufferSubData; //PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData; //PFNGLMAPBUFFERPROC glMapBuffer; //PFNGLUNMAPBUFFERPROC glUnmapBuffer; //PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv; //PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv; //GL_VERSION_2_0 PFNGLDRAWBUFFERSPROC glDrawBuffers; PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate; PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate; PFNGLATTACHSHADERPROC glAttachShader; PFNGLCOMPILESHADERPROC glCompileShader; PFNGLCREATEPROGRAMPROC glCreateProgram; PFNGLCREATESHADERPROC glCreateShader; PFNGLDELETEPROGRAMPROC glDeleteProgram; PFNGLDELETESHADERPROC glDeleteShader; PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; PFNGLGETPROGRAMIVPROC glGetProgramiv; PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; PFNGLGETSHADERIVPROC glGetShaderiv; PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; PFNGLLINKPROGRAMPROC glLinkProgram; PFNGLSHADERSOURCEPROC glShaderSource; PFNGLUSEPROGRAMPROC glUseProgram; PFNGLUNIFORM1FPROC glUniform1f; PFNGLUNIFORM1FVPROC glUniform1fv; PFNGLUNIFORM2FVPROC glUniform2fv; PFNGLUNIFORM3FVPROC glUniform3fv; PFNGLUNIFORM4FVPROC glUniform4fv; PFNGLUNIFORM1IVPROC glUniform1iv; PFNGLUNIFORM2IVPROC glUniform2iv; PFNGLUNIFORM3IVPROC glUniform3iv; PFNGLUNIFORM4IVPROC glUniform4iv; //PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; //PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate; //PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate; //PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; //PFNGLDETACHSHADERPROC glDetachShader; //PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib; //PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform; //PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders; //PFNGLGETSHADERSOURCEPROC glGetShaderSource; //PFNGLGETUNIFORMFVPROC glGetUniformfv; //PFNGLGETUNIFORMIVPROC glGetUniformiv; //PFNGLGETVERTEXATTRIBDVPROC glGetVertexAttribdv; //PFNGLGETVERTEXATTRIBFVPROC glGetVertexAttribfv; //PFNGLGETVERTEXATTRIBIVPROC glGetVertexAttribiv; //PFNGLGETVERTEXATTRIBPOINTERVPROC glGetVertexAttribPointerv; //PFNGLISPROGRAMPROC glIsProgram; //PFNGLISSHADERPROC glIsShader; //PFNGLUNIFORM2FPROC glUniform2f; //PFNGLUNIFORM3FPROC glUniform3f; //PFNGLUNIFORM4FPROC glUniform4f; //PFNGLUNIFORM1IPROC glUniform1i; //PFNGLUNIFORM2IPROC glUniform2i; //PFNGLUNIFORM3IPROC glUniform3i; //PFNGLUNIFORM4IPROC glUniform4i; //PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv; PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv; //PFNGLVALIDATEPROGRAMPROC glValidateProgram; //PFNGLVERTEXATTRIB1DPROC glVertexAttrib1d; //PFNGLVERTEXATTRIB1DVPROC glVertexAttrib1dv; //PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f; //PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv; //PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s; //PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv; //PFNGLVERTEXATTRIB2DPROC glVertexAttrib2d; //PFNGLVERTEXATTRIB2DVPROC glVertexAttrib2dv; //PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f; //PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv; //PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s; //PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv; //PFNGLVERTEXATTRIB3DPROC glVertexAttrib3d; //PFNGLVERTEXATTRIB3DVPROC glVertexAttrib3dv; //PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f; //PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv; //PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s; //PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv; //PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv; //PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv; //PFNGLVERTEXATTRIB4NSVPROC glVertexAttrib4Nsv; //PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub; //PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv; //PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv; //PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv; //PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv; //PFNGLVERTEXATTRIB4DPROC glVertexAttrib4d; //PFNGLVERTEXATTRIB4DVPROC glVertexAttrib4dv; //PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f; //PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv; //PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv; //PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s; //PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv; //PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv; //PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv; //PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv; //GL_VERSION_2_1 //PFNGLUNIFORMMATRIX2X3FVPROC glUniformMatrix2x3fv; //PFNGLUNIFORMMATRIX3X2FVPROC glUniformMatrix3x2fv; //PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv; //PFNGLUNIFORMMATRIX4X2FVPROC glUniformMatrix4x2fv; //PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fv; //PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv; //GL_VERSION_3_0 PFNGLBINDBUFFERRANGEPROC glBindBufferRange; PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer; PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample; PFNGLBINDVERTEXARRAYPROC glBindVertexArray; PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; //PFNGLCOLORMASKIPROC glColorMaski; //PFNGLGETBOOLEANI_VPROC glGetBooleani_v; //PFNGLGETINTEGERI_VPROC glGetIntegeri_v; //PFNGLENABLEIPROC glEnablei; //PFNGLDISABLEIPROC glDisablei; //PFNGLISENABLEDIPROC glIsEnabledi; //PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback; //PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback; //PFNGLBINDBUFFERBASEPROC glBindBufferBase; //PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings; //PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glGetTransformFeedbackVarying; //PFNGLCLAMPCOLORPROC glClampColor; //PFNGLBEGINCONDITIONALRENDERPROC glBeginConditionalRender; //PFNGLENDCONDITIONALRENDERPROC glEndConditionalRender; //PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer; //PFNGLGETVERTEXATTRIBIIVPROC glGetVertexAttribIiv; //PFNGLGETVERTEXATTRIBIUIVPROC glGetVertexAttribIuiv; //PFNGLVERTEXATTRIBI1IPROC glVertexAttribI1i; //PFNGLVERTEXATTRIBI2IPROC glVertexAttribI2i; //PFNGLVERTEXATTRIBI3IPROC glVertexAttribI3i; //PFNGLVERTEXATTRIBI4IPROC glVertexAttribI4i; //PFNGLVERTEXATTRIBI1UIPROC glVertexAttribI1ui; //PFNGLVERTEXATTRIBI2UIPROC glVertexAttribI2ui; //PFNGLVERTEXATTRIBI3UIPROC glVertexAttribI3ui; //PFNGLVERTEXATTRIBI4UIPROC glVertexAttribI4ui; //PFNGLVERTEXATTRIBI1IVPROC glVertexAttribI1iv; //PFNGLVERTEXATTRIBI2IVPROC glVertexAttribI2iv; //PFNGLVERTEXATTRIBI3IVPROC glVertexAttribI3iv; //PFNGLVERTEXATTRIBI4IVPROC glVertexAttribI4iv; //PFNGLVERTEXATTRIBI1UIVPROC glVertexAttribI1uiv; //PFNGLVERTEXATTRIBI2UIVPROC glVertexAttribI2uiv; //PFNGLVERTEXATTRIBI3UIVPROC glVertexAttribI3uiv; //PFNGLVERTEXATTRIBI4UIVPROC glVertexAttribI4uiv; //PFNGLVERTEXATTRIBI4BVPROC glVertexAttribI4bv; //PFNGLVERTEXATTRIBI4SVPROC glVertexAttribI4sv; //PFNGLVERTEXATTRIBI4UBVPROC glVertexAttribI4ubv; //PFNGLVERTEXATTRIBI4USVPROC glVertexAttribI4usv; //PFNGLGETUNIFORMUIVPROC glGetUniformuiv; //PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation; //PFNGLGETFRAGDATALOCATIONPROC glGetFragDataLocation; //PFNGLUNIFORM1UIPROC glUniform1ui; //PFNGLUNIFORM2UIPROC glUniform2ui; //PFNGLUNIFORM3UIPROC glUniform3ui; //PFNGLUNIFORM4UIPROC glUniform4ui; //PFNGLUNIFORM1UIVPROC glUniform1uiv; //PFNGLUNIFORM2UIVPROC glUniform2uiv; //PFNGLUNIFORM3UIVPROC glUniform3uiv; //PFNGLUNIFORM4UIVPROC glUniform4uiv; //PFNGLTEXPARAMETERIIVPROC glTexParameterIiv; //PFNGLTEXPARAMETERIUIVPROC glTexParameterIuiv; //PFNGLGETTEXPARAMETERIIVPROC glGetTexParameterIiv; //PFNGLGETTEXPARAMETERIUIVPROC glGetTexParameterIuiv; //PFNGLCLEARBUFFERIVPROC glClearBufferiv; //PFNGLCLEARBUFFERUIVPROC glClearBufferuiv; //PFNGLCLEARBUFFERFVPROC glClearBufferfv; //PFNGLCLEARBUFFERFIPROC glClearBufferfi; //PFNGLGETSTRINGIPROC glGetStringi; //PFNGLISRENDERBUFFERPROC glIsRenderbuffer; //PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; //PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv; //PFNGLISFRAMEBUFFERPROC glIsFramebuffer; //PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; //PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D; //PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D; //PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv; //PFNGLGENERATEMIPMAPPROC glGenerateMipmap; //PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; //PFNGLMAPBUFFERRANGEPROC glMapBufferRange; //PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange; //PFNGLISVERTEXARRAYPROC glIsVertexArray; //GL_VERSION_3_1 PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex; PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding; //PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced; //PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced; //PFNGLTEXBUFFERPROC glTexBuffer; //PFNGLPRIMITIVERESTARTINDEXPROC glPrimitiveRestartIndex; //PFNGLCOPYBUFFERSUBDATAPROC glCopyBufferSubData; //PFNGLGETUNIFORMINDICESPROC glGetUniformIndices; //PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv; //PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName; //PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv; //PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName; #if defined(_WIN32) && !defined(__CYGWIN__) && defined(THORVG_GL_TARGET_GL) PFNWGLGETCURRENTCONTEXTPROC tvgWglGetCurrentContext; PFNWGLMAKECURRENTPROC tvgWglMakeCurrent; #endif #if defined(THORVG_GL_TARGET_GLES) PFNEGLGETCURRENTCONTEXTPROC tvgEglGetCurrentContext; PFNEGLMAKECURRENTPROC tvgEglMakeCurrent; #endif bool glInit() { if (!_glLoad()) return false; // GL_VERSION_1_0 GL_FUNCTION_FETCH(glCullFace, PFNGLCULLFACEPROC); GL_FUNCTION_FETCH(glFrontFace, PFNGLFRONTFACEPROC); // GL_FUNCTION_FETCH(glHint, PFNGLHINTPROC); // GL_FUNCTION_FETCH(glLineWidth, PFNGLLINEWIDTHPROC); // GL_FUNCTION_FETCH(glPointSize, PFNGLPOINTSIZEPROC); // GL_FUNCTION_FETCH(glPolygonMode, PFNGLPOLYGONMODEPROC); GL_FUNCTION_FETCH(glScissor, PFNGLSCISSORPROC); // GL_FUNCTION_FETCH(glTexParameterf, PFNGLTEXPARAMETERFPROC); // GL_FUNCTION_FETCH(glTexParameterfv, PFNGLTEXPARAMETERFVPROC); GL_FUNCTION_FETCH(glTexParameteri, PFNGLTEXPARAMETERIPROC); // GL_FUNCTION_FETCH(glTexParameteriv, PFNGLTEXPARAMETERIVPROC); // GL_FUNCTION_FETCH(glTexImage1D, PFNGLTEXIMAGE1DPROC); GL_FUNCTION_FETCH(glTexImage2D, PFNGLTEXIMAGE2DPROC); #if !defined(THORVG_GL_TARGET_GLES) GL_FUNCTION_FETCH(glDrawBuffer, PFNGLDRAWBUFFERPROC); #endif GL_FUNCTION_FETCH(glClear, PFNGLCLEARPROC); GL_FUNCTION_FETCH(glClearColor, PFNGLCLEARCOLORPROC); GL_FUNCTION_FETCH(glClearStencil, PFNGLCLEARSTENCILPROC); #if defined(THORVG_GL_TARGET_GLES) GL_FUNCTION_FETCH(glClearDepthf, PFNGLCLEARDEPTHFPROC); #else GL_FUNCTION_FETCH(glClearDepth, PFNGLCLEARDEPTHPROC); #endif // GL_FUNCTION_FETCH(glStencilMask, PFNGLSTENCILMASKPROC); GL_FUNCTION_FETCH(glColorMask, PFNGLCOLORMASKPROC); GL_FUNCTION_FETCH(glDepthMask, PFNGLDEPTHMASKPROC); GL_FUNCTION_FETCH(glDisable, PFNGLDISABLEPROC); GL_FUNCTION_FETCH(glEnable, PFNGLENABLEPROC); // GL_FUNCTION_FETCH(glFinish, PFNGLFINISHPROC); // GL_FUNCTION_FETCH(glFlush, PFNGLFLUSHPROC); GL_FUNCTION_FETCH(glBlendFunc, PFNGLBLENDFUNCPROC); // GL_FUNCTION_FETCH(glLogicOp, PFNGLLOGICOPPROC); GL_FUNCTION_FETCH(glStencilFunc, PFNGLSTENCILFUNCPROC); GL_FUNCTION_FETCH(glStencilOp, PFNGLSTENCILOPPROC); GL_FUNCTION_FETCH(glDepthFunc, PFNGLDEPTHFUNCPROC); // GL_FUNCTION_FETCH(glPixelStoref, PFNGLPIXELSTOREFPROC); // GL_FUNCTION_FETCH(glPixelStorei, PFNGLPIXELSTOREIPROC); // GL_FUNCTION_FETCH(glReadBuffer, PFNGLREADBUFFERPROC); // GL_FUNCTION_FETCH(glReadPixels, PFNGLREADPIXELSPROC); // GL_FUNCTION_FETCH(glGetBooleanv, PFNGLGETBOOLEANVPROC); // GL_FUNCTION_FETCH(glGetDoublev, PFNGLGETDOUBLEVPROC); GL_FUNCTION_FETCH(glGetError, PFNGLGETERRORPROC); // GL_FUNCTION_FETCH(glGetFloatv, PFNGLGETFLOATVPROC); GL_FUNCTION_FETCH(glGetIntegerv, PFNGLGETINTEGERVPROC); GL_FUNCTION_FETCH(glGetString, PFNGLGETSTRINGPROC); // GL_FUNCTION_FETCH(glGetTexImage, PFNGLGETTEXIMAGEPROC); // GL_FUNCTION_FETCH(glGetTexParameterfv, PFNGLGETTEXPARAMETERFVPROC); // GL_FUNCTION_FETCH(glGetTexParameteriv, PFNGLGETTEXPARAMETERIVPROC); // GL_FUNCTION_FETCH(glGetTexLevelParameterfv, PFNGLGETTEXLEVELPARAMETERFVPROC); // GL_FUNCTION_FETCH(glGetTexLevelParameteriv, PFNGLGETTEXLEVELPARAMETERIVPROC); // GL_FUNCTION_FETCH(glIsEnabled, PFNGLISENABLEDPROC); // GL_FUNCTION_FETCH(glDepthRange, PFNGLDEPTHRANGEPROC); GL_FUNCTION_FETCH(glViewport, PFNGLVIEWPORTPROC); // GL_VERSION_1_1 // GL_FUNCTION_FETCH(glDrawArrays, PFNGLDRAWARRAYSPROC); GL_FUNCTION_FETCH(glDrawElements, PFNGLDRAWELEMENTSPROC); // GL_FUNCTION_FETCH(glGetPointerv, PFNGLGETPOINTERVPROC); // GL_FUNCTION_FETCH(glPolygonOffset, PFNGLPOLYGONOFFSETPROC); // GL_FUNCTION_FETCH(glCopyTexImage1D, PFNGLCOPYTEXIMAGE1DPROC); // GL_FUNCTION_FETCH(glCopyTexImage2D, PFNGLCOPYTEXIMAGE2DPROC); // GL_FUNCTION_FETCH(glCopyTexSubImage1D, PFNGLCOPYTEXSUBIMAGE1DPROC); // GL_FUNCTION_FETCH(glCopyTexSubImage2D, PFNGLCOPYTEXSUBIMAGE2DPROC); // GL_FUNCTION_FETCH(glTexSubImage1D, PFNGLTEXSUBIMAGE1DPROC); // GL_FUNCTION_FETCH(glTexSubImage2D, PFNGLTEXSUBIMAGE2DPROC); GL_FUNCTION_FETCH(glBindTexture, PFNGLBINDTEXTUREPROC); GL_FUNCTION_FETCH(glDeleteTextures, PFNGLDELETETEXTURESPROC); GL_FUNCTION_FETCH(glGenTextures, PFNGLGENTEXTURESPROC); // GL_FUNCTION_FETCH(glIsTexture, PFNGLISTEXTUREPROC); // // GL_VERSION_1_2 // GL_FUNCTION_FETCH(glDrawRangeElements, PFNGLDRAWRANGEELEMENTSPROC); // GL_FUNCTION_FETCH(glTexImage3D, PFNGLTEXIMAGE3DPROC); // GL_FUNCTION_FETCH(glTexSubImage3D, PFNGLTEXSUBIMAGE3DPROC); // GL_FUNCTION_FETCH(glCopyTexSubImage3D, PFNGLCOPYTEXSUBIMAGE3DPROC); // // GL_VERSION_1_3 GL_FUNCTION_FETCH(glActiveTexture, PFNGLACTIVETEXTUREPROC); // GL_FUNCTION_FETCH(glSampleCoverage, PFNGLSAMPLECOVERAGEPROC); // GL_FUNCTION_FETCH(glCompressedTexImage3D, PFNGLCOMPRESSEDTEXIMAGE3DPROC); // GL_FUNCTION_FETCH(glCompressedTexImage2D, PFNGLCOMPRESSEDTEXIMAGE2DPROC); // GL_FUNCTION_FETCH(glCompressedTexImage1D, PFNGLCOMPRESSEDTEXIMAGE1DPROC); // GL_FUNCTION_FETCH(glCompressedTexSubImage3D, PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC); // GL_FUNCTION_FETCH(glCompressedTexSubImage2D, PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC); // GL_FUNCTION_FETCH(glCompressedTexSubImage1D, PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC); // GL_FUNCTION_FETCH(glGetCompressedTexImage, PFNGLGETCOMPRESSEDTEXIMAGEPROC); // // GL_VERSION_1_4 // GL_FUNCTION_FETCH(glBlendFuncSeparate, PFNGLBLENDFUNCSEPARATEPROC); // GL_FUNCTION_FETCH(glMultiDrawArrays, PFNGLMULTIDRAWARRAYSPROC); // GL_FUNCTION_FETCH(glMultiDrawElements, PFNGLMULTIDRAWELEMENTSPROC); // GL_FUNCTION_FETCH(glPointParameterf, PFNGLPOINTPARAMETERFPROC); // GL_FUNCTION_FETCH(glPointParameterfv, PFNGLPOINTPARAMETERFVPROC); // GL_FUNCTION_FETCH(glPointParameteri, PFNGLPOINTPARAMETERIPROC); // GL_FUNCTION_FETCH(glPointParameteriv, PFNGLPOINTPARAMETERIVPROC); // GL_FUNCTION_FETCH(glBlendColor, PFNGLBLENDCOLORPROC); GL_FUNCTION_FETCH(glBlendEquation, PFNGLBLENDEQUATIONPROC); // GL_VERSION_1_5 // GL_FUNCTION_FETCH(glGenQueries, PFNGLGENQUERIESPROC); // GL_FUNCTION_FETCH(glDeleteQueries, PFNGLDELETEQUERIESPROC); // GL_FUNCTION_FETCH(glIsQuery, PFNGLISQUERYPROC); // GL_FUNCTION_FETCH(glBeginQuery, PFNGLBEGINQUERYPROC); // GL_FUNCTION_FETCH(glEndQuery, PFNGLENDQUERYPROC); // GL_FUNCTION_FETCH(glGetQueryiv, PFNGLGETQUERYIVPROC); // GL_FUNCTION_FETCH(glGetQueryObjectiv, PFNGLGETQUERYOBJECTIVPROC); // GL_FUNCTION_FETCH(glGetQueryObjectuiv, PFNGLGETQUERYOBJECTUIVPROC); GL_FUNCTION_FETCH(glBindBuffer, PFNGLBINDBUFFERPROC); GL_FUNCTION_FETCH(glDeleteBuffers, PFNGLDELETEBUFFERSPROC); GL_FUNCTION_FETCH(glGenBuffers, PFNGLGENBUFFERSPROC); // GL_FUNCTION_FETCH(glIsBuffer, PFNGLISBUFFERPROC); GL_FUNCTION_FETCH(glBufferData, PFNGLBUFFERDATAPROC); // GL_FUNCTION_FETCH(glBufferSubData, PFNGLBUFFERSUBDATAPROC); // GL_FUNCTION_FETCH(glGetBufferSubData, PFNGLGETBUFFERSUBDATAPROC); // GL_FUNCTION_FETCH(glMapBuffer, PFNGLMAPBUFFERPROC); // GL_FUNCTION_FETCH(glUnmapBuffer, PFNGLUNMAPBUFFERPROC); // GL_FUNCTION_FETCH(glGetBufferParameteriv, PFNGLGETBUFFERPARAMETERIVPROC); // GL_FUNCTION_FETCH(glGetBufferPointerv, PFNGLGETBUFFERPOINTERVPROC); // GL_VERSION_2_0 // GL_FUNCTION_FETCH(glBlendEquationSeparate, PFNGLBLENDEQUATIONSEPARATEPROC); #if defined(THORVG_GL_TARGET_GLES) GL_FUNCTION_FETCH(glDrawBuffers, PFNGLDRAWBUFFERSPROC); #endif GL_FUNCTION_FETCH(glStencilOpSeparate, PFNGLSTENCILOPSEPARATEPROC); GL_FUNCTION_FETCH(glStencilFuncSeparate, PFNGLSTENCILFUNCSEPARATEPROC); // GL_FUNCTION_FETCH(glStencilMaskSeparate, PFNGLSTENCILMASKSEPARATEPROC); GL_FUNCTION_FETCH(glAttachShader, PFNGLATTACHSHADERPROC); // GL_FUNCTION_FETCH(glBindAttribLocation, PFNGLBINDATTRIBLOCATIONPROC); GL_FUNCTION_FETCH(glCompileShader, PFNGLCOMPILESHADERPROC); GL_FUNCTION_FETCH(glCreateProgram, PFNGLCREATEPROGRAMPROC); GL_FUNCTION_FETCH(glCreateShader, PFNGLCREATESHADERPROC); GL_FUNCTION_FETCH(glDeleteProgram, PFNGLDELETEPROGRAMPROC); GL_FUNCTION_FETCH(glDeleteShader, PFNGLDELETESHADERPROC); // GL_FUNCTION_FETCH(glDetachShader, PFNGLDETACHSHADERPROC); GL_FUNCTION_FETCH(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC); GL_FUNCTION_FETCH(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC); // GL_FUNCTION_FETCH(glGetActiveAttrib, PFNGLGETACTIVEATTRIBPROC); // GL_FUNCTION_FETCH(glGetActiveUniform, PFNGLGETACTIVEUNIFORMPROC); // GL_FUNCTION_FETCH(glGetAttachedShaders, PFNGLGETATTACHEDSHADERSPROC); GL_FUNCTION_FETCH(glGetAttribLocation, PFNGLGETATTRIBLOCATIONPROC); GL_FUNCTION_FETCH(glGetProgramiv, PFNGLGETPROGRAMIVPROC); GL_FUNCTION_FETCH(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC); GL_FUNCTION_FETCH(glGetShaderiv, PFNGLGETSHADERIVPROC); GL_FUNCTION_FETCH(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC); // GL_FUNCTION_FETCH(glGetShaderSource, PFNGLGETSHADERSOURCEPROC); GL_FUNCTION_FETCH(glGetUniformLocation, PFNGLGETUNIFORMLOCATIONPROC); // GL_FUNCTION_FETCH(glGetUniformfv, PFNGLGETUNIFORMFVPROC); // GL_FUNCTION_FETCH(glGetUniformiv, PFNGLGETUNIFORMIVPROC); // GL_FUNCTION_FETCH(glGetVertexAttribdv, PFNGLGETVERTEXATTRIBDVPROC); // GL_FUNCTION_FETCH(glGetVertexAttribfv, PFNGLGETVERTEXATTRIBFVPROC); // GL_FUNCTION_FETCH(glGetVertexAttribiv, PFNGLGETVERTEXATTRIBIVPROC); // GL_FUNCTION_FETCH(glGetVertexAttribPointerv, PFNGLGETVERTEXATTRIBPOINTERVPROC); // GL_FUNCTION_FETCH(glIsProgram, PFNGLISPROGRAMPROC); // GL_FUNCTION_FETCH(glIsShader, PFNGLISSHADERPROC); GL_FUNCTION_FETCH(glLinkProgram, PFNGLLINKPROGRAMPROC); GL_FUNCTION_FETCH(glShaderSource, PFNGLSHADERSOURCEPROC); GL_FUNCTION_FETCH(glUseProgram, PFNGLUSEPROGRAMPROC); GL_FUNCTION_FETCH(glUniform1f, PFNGLUNIFORM1FPROC); // GL_FUNCTION_FETCH(glUniform2f, PFNGLUNIFORM2FPROC); // GL_FUNCTION_FETCH(glUniform3f, PFNGLUNIFORM3FPROC); // GL_FUNCTION_FETCH(glUniform4f, PFNGLUNIFORM4FPROC); // GL_FUNCTION_FETCH(glUniform1i, PFNGLUNIFORM1IPROC); // GL_FUNCTION_FETCH(glUniform2i, PFNGLUNIFORM2IPROC); // GL_FUNCTION_FETCH(glUniform3i, PFNGLUNIFORM3IPROC); // GL_FUNCTION_FETCH(glUniform4i, PFNGLUNIFORM4IPROC); GL_FUNCTION_FETCH(glUniform1fv, PFNGLUNIFORM1FVPROC); GL_FUNCTION_FETCH(glUniform2fv, PFNGLUNIFORM2FVPROC); GL_FUNCTION_FETCH(glUniform3fv, PFNGLUNIFORM3FVPROC); GL_FUNCTION_FETCH(glUniform4fv, PFNGLUNIFORM4FVPROC); GL_FUNCTION_FETCH(glUniform1iv, PFNGLUNIFORM1IVPROC); GL_FUNCTION_FETCH(glUniform2iv, PFNGLUNIFORM2IVPROC); GL_FUNCTION_FETCH(glUniform3iv, PFNGLUNIFORM3IVPROC); GL_FUNCTION_FETCH(glUniform4iv, PFNGLUNIFORM4IVPROC); // GL_FUNCTION_FETCH(glUniformMatrix2fv, PFNGLUNIFORMMATRIX2FVPROC); GL_FUNCTION_FETCH(glUniformMatrix3fv, PFNGLUNIFORMMATRIX3FVPROC); // GL_FUNCTION_FETCH(glUniformMatrix4fv, PFNGLUNIFORMMATRIX4FVPROC); // GL_FUNCTION_FETCH(glValidateProgram, PFNGLVALIDATEPROGRAMPROC); // GL_FUNCTION_FETCH(glVertexAttrib1d, PFNGLVERTEXATTRIB1DPROC); // GL_FUNCTION_FETCH(glVertexAttrib1dv, PFNGLVERTEXATTRIB1DVPROC); // GL_FUNCTION_FETCH(glVertexAttrib1f, PFNGLVERTEXATTRIB1FPROC); // GL_FUNCTION_FETCH(glVertexAttrib1fv, PFNGLVERTEXATTRIB1FVPROC); // GL_FUNCTION_FETCH(glVertexAttrib1s, PFNGLVERTEXATTRIB1SPROC); // GL_FUNCTION_FETCH(glVertexAttrib1sv, PFNGLVERTEXATTRIB1SVPROC); // GL_FUNCTION_FETCH(glVertexAttrib2d, PFNGLVERTEXATTRIB2DPROC); // GL_FUNCTION_FETCH(glVertexAttrib2dv, PFNGLVERTEXATTRIB2DVPROC); // GL_FUNCTION_FETCH(glVertexAttrib2f, PFNGLVERTEXATTRIB2FPROC); // GL_FUNCTION_FETCH(glVertexAttrib2fv, PFNGLVERTEXATTRIB2FVPROC); // GL_FUNCTION_FETCH(glVertexAttrib2s, PFNGLVERTEXATTRIB2SPROC); // GL_FUNCTION_FETCH(glVertexAttrib2sv, PFNGLVERTEXATTRIB2SVPROC); // GL_FUNCTION_FETCH(glVertexAttrib3d, PFNGLVERTEXATTRIB3DPROC); // GL_FUNCTION_FETCH(glVertexAttrib3dv, PFNGLVERTEXATTRIB3DVPROC); // GL_FUNCTION_FETCH(glVertexAttrib3f, PFNGLVERTEXATTRIB3FPROC); // GL_FUNCTION_FETCH(glVertexAttrib3fv, PFNGLVERTEXATTRIB3FVPROC); // GL_FUNCTION_FETCH(glVertexAttrib3s, PFNGLVERTEXATTRIB3SPROC); // GL_FUNCTION_FETCH(glVertexAttrib3sv, PFNGLVERTEXATTRIB3SVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4Nbv, PFNGLVERTEXATTRIB4NBVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4Niv, PFNGLVERTEXATTRIB4NIVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4Nsv, PFNGLVERTEXATTRIB4NSVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4Nub, PFNGLVERTEXATTRIB4NUBPROC); // GL_FUNCTION_FETCH(glVertexAttrib4Nubv, PFNGLVERTEXATTRIB4NUBVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4Nuiv, PFNGLVERTEXATTRIB4NUIVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4Nusv, PFNGLVERTEXATTRIB4NUSVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4bv, PFNGLVERTEXATTRIB4BVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4d, PFNGLVERTEXATTRIB4DPROC); // GL_FUNCTION_FETCH(glVertexAttrib4dv, PFNGLVERTEXATTRIB4DVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4f, PFNGLVERTEXATTRIB4FPROC); // GL_FUNCTION_FETCH(glVertexAttrib4fv, PFNGLVERTEXATTRIB4FVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4iv, PFNGLVERTEXATTRIB4IVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4s, PFNGLVERTEXATTRIB4SPROC); // GL_FUNCTION_FETCH(glVertexAttrib4sv, PFNGLVERTEXATTRIB4SVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4ubv, PFNGLVERTEXATTRIB4UBVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4uiv, PFNGLVERTEXATTRIB4UIVPROC); // GL_FUNCTION_FETCH(glVertexAttrib4usv, PFNGLVERTEXATTRIB4USVPROC); GL_FUNCTION_FETCH(glVertexAttribPointer, PFNGLVERTEXATTRIBPOINTERPROC); // // GL_VERSION_2_1 // GL_FUNCTION_FETCH(glUniformMatrix2x3fv, PFNGLUNIFORMMATRIX2X3FVPROC); // GL_FUNCTION_FETCH(glUniformMatrix3x2fv, PFNGLUNIFORMMATRIX3X2FVPROC); // GL_FUNCTION_FETCH(glUniformMatrix2x4fv, PFNGLUNIFORMMATRIX2X4FVPROC); // GL_FUNCTION_FETCH(glUniformMatrix4x2fv, PFNGLUNIFORMMATRIX4X2FVPROC); // GL_FUNCTION_FETCH(glUniformMatrix3x4fv, PFNGLUNIFORMMATRIX3X4FVPROC); // GL_FUNCTION_FETCH(glUniformMatrix4x3fv, PFNGLUNIFORMMATRIX4X3FVPROC); // GL_VERSION_3_0 // GL_FUNCTION_FETCH(glColorMaski, PFNGLCOLORMASKIPROC); // GL_FUNCTION_FETCH(glGetBooleani_v, PFNGLGETBOOLEANI_VPROC); // GL_FUNCTION_FETCH(glGetIntegeri_v, PFNGLGETINTEGERI_VPROC); // GL_FUNCTION_FETCH(glEnablei, PFNGLENABLEIPROC); // GL_FUNCTION_FETCH(glDisablei, PFNGLDISABLEIPROC); // GL_FUNCTION_FETCH(glIsEnabledi, PFNGLISENABLEDIPROC); // GL_FUNCTION_FETCH(glBeginTransformFeedback, PFNGLBEGINTRANSFORMFEEDBACKPROC); // GL_FUNCTION_FETCH(glEndTransformFeedback, PFNGLENDTRANSFORMFEEDBACKPROC); GL_FUNCTION_FETCH(glBindBufferRange, PFNGLBINDBUFFERRANGEPROC); // GL_FUNCTION_FETCH(glBindBufferBase, PFNGLBINDBUFFERBASEPROC); // GL_FUNCTION_FETCH(glTransformFeedbackVaryings, PFNGLTRANSFORMFEEDBACKVARYINGSPROC); // GL_FUNCTION_FETCH(glGetTransformFeedbackVarying, PFNGLGETTRANSFORMFEEDBACKVARYINGPROC); // GL_FUNCTION_FETCH(glClampColor, PFNGLCLAMPCOLORPROC); // GL_FUNCTION_FETCH(glBeginConditionalRender, PFNGLBEGINCONDITIONALRENDERPROC); // GL_FUNCTION_FETCH(glEndConditionalRender, PFNGLENDCONDITIONALRENDERPROC); // GL_FUNCTION_FETCH(glVertexAttribIPointer, PFNGLVERTEXATTRIBIPOINTERPROC); // GL_FUNCTION_FETCH(glGetVertexAttribIiv, PFNGLGETVERTEXATTRIBIIVPROC); // GL_FUNCTION_FETCH(glGetVertexAttribIuiv, PFNGLGETVERTEXATTRIBIUIVPROC); // GL_FUNCTION_FETCH(glVertexAttribI1i, PFNGLVERTEXATTRIBI1IPROC); // GL_FUNCTION_FETCH(glVertexAttribI2i, PFNGLVERTEXATTRIBI2IPROC); // GL_FUNCTION_FETCH(glVertexAttribI3i, PFNGLVERTEXATTRIBI3IPROC); // GL_FUNCTION_FETCH(glVertexAttribI4i, PFNGLVERTEXATTRIBI4IPROC); // GL_FUNCTION_FETCH(glVertexAttribI1ui, PFNGLVERTEXATTRIBI1UIPROC); // GL_FUNCTION_FETCH(glVertexAttribI2ui, PFNGLVERTEXATTRIBI2UIPROC); // GL_FUNCTION_FETCH(glVertexAttribI3ui, PFNGLVERTEXATTRIBI3UIPROC); // GL_FUNCTION_FETCH(glVertexAttribI4ui, PFNGLVERTEXATTRIBI4UIPROC); // GL_FUNCTION_FETCH(glVertexAttribI1iv, PFNGLVERTEXATTRIBI1IVPROC); // GL_FUNCTION_FETCH(glVertexAttribI2iv, PFNGLVERTEXATTRIBI2IVPROC); // GL_FUNCTION_FETCH(glVertexAttribI3iv, PFNGLVERTEXATTRIBI3IVPROC); // GL_FUNCTION_FETCH(glVertexAttribI4iv, PFNGLVERTEXATTRIBI4IVPROC); // GL_FUNCTION_FETCH(glVertexAttribI1uiv, PFNGLVERTEXATTRIBI1UIVPROC); // GL_FUNCTION_FETCH(glVertexAttribI2uiv, PFNGLVERTEXATTRIBI2UIVPROC); // GL_FUNCTION_FETCH(glVertexAttribI3uiv, PFNGLVERTEXATTRIBI3UIVPROC); // GL_FUNCTION_FETCH(glVertexAttribI4uiv, PFNGLVERTEXATTRIBI4UIVPROC); // GL_FUNCTION_FETCH(glVertexAttribI4bv, PFNGLVERTEXATTRIBI4BVPROC); // GL_FUNCTION_FETCH(glVertexAttribI4sv, PFNGLVERTEXATTRIBI4SVPROC); // GL_FUNCTION_FETCH(glVertexAttribI4ubv, PFNGLVERTEXATTRIBI4UBVPROC); // GL_FUNCTION_FETCH(glVertexAttribI4usv, PFNGLVERTEXATTRIBI4USVPROC); // GL_FUNCTION_FETCH(glGetUniformuiv, PFNGLGETUNIFORMUIVPROC); // GL_FUNCTION_FETCH(glBindFragDataLocation, PFNGLBINDFRAGDATALOCATIONPROC); // GL_FUNCTION_FETCH(glGetFragDataLocation, PFNGLGETFRAGDATALOCATIONPROC); // GL_FUNCTION_FETCH(glUniform1ui, PFNGLUNIFORM1UIPROC); // GL_FUNCTION_FETCH(glUniform2ui, PFNGLUNIFORM2UIPROC); // GL_FUNCTION_FETCH(glUniform3ui, PFNGLUNIFORM3UIPROC); // GL_FUNCTION_FETCH(glUniform4ui, PFNGLUNIFORM4UIPROC); // GL_FUNCTION_FETCH(glUniform1uiv, PFNGLUNIFORM1UIVPROC); // GL_FUNCTION_FETCH(glUniform2uiv, PFNGLUNIFORM2UIVPROC); // GL_FUNCTION_FETCH(glUniform3uiv, PFNGLUNIFORM3UIVPROC); // GL_FUNCTION_FETCH(glUniform4uiv, PFNGLUNIFORM4UIVPROC); // GL_FUNCTION_FETCH(glTexParameterIiv, PFNGLTEXPARAMETERIIVPROC); // GL_FUNCTION_FETCH(glTexParameterIuiv, PFNGLTEXPARAMETERIUIVPROC); // GL_FUNCTION_FETCH(glGetTexParameterIiv, PFNGLGETTEXPARAMETERIIVPROC); // GL_FUNCTION_FETCH(glGetTexParameterIuiv, PFNGLGETTEXPARAMETERIUIVPROC); // GL_FUNCTION_FETCH(glClearBufferiv, PFNGLCLEARBUFFERIVPROC); // GL_FUNCTION_FETCH(glClearBufferuiv, PFNGLCLEARBUFFERUIVPROC); // GL_FUNCTION_FETCH(glClearBufferfv, PFNGLCLEARBUFFERFVPROC); // GL_FUNCTION_FETCH(glClearBufferfi, PFNGLCLEARBUFFERFIPROC); // GL_FUNCTION_FETCH(glGetStringi, PFNGLGETSTRINGIPROC); // GL_FUNCTION_FETCH(glIsRenderbuffer, PFNGLISRENDERBUFFERPROC); GL_FUNCTION_FETCH(glBindRenderbuffer, PFNGLBINDRENDERBUFFERPROC); GL_FUNCTION_FETCH(glDeleteRenderbuffers, PFNGLDELETERENDERBUFFERSPROC); GL_FUNCTION_FETCH(glGenRenderbuffers, PFNGLGENRENDERBUFFERSPROC); // GL_FUNCTION_FETCH(glRenderbufferStorage, PFNGLRENDERBUFFERSTORAGEPROC); // GL_FUNCTION_FETCH(glGetRenderbufferParameteriv, PFNGLGETRENDERBUFFERPARAMETERIVPROC); // GL_FUNCTION_FETCH(glIsFramebuffer, PFNGLISFRAMEBUFFERPROC); #if defined(THORVG_GL_TARGET_GLES) GL_FUNCTION_FETCH(glInvalidateFramebuffer, PFNGLINVALIDATEFRAMEBUFFERPROC); #endif GL_FUNCTION_FETCH(glBindFramebuffer, PFNGLBINDFRAMEBUFFERPROC); GL_FUNCTION_FETCH(glDeleteFramebuffers, PFNGLDELETEFRAMEBUFFERSPROC); GL_FUNCTION_FETCH(glGenFramebuffers, PFNGLGENFRAMEBUFFERSPROC); // GL_FUNCTION_FETCH(glCheckFramebufferStatus, PFNGLCHECKFRAMEBUFFERSTATUSPROC); // GL_FUNCTION_FETCH(glFramebufferTexture1D, PFNGLFRAMEBUFFERTEXTURE1DPROC); GL_FUNCTION_FETCH(glFramebufferTexture2D, PFNGLFRAMEBUFFERTEXTURE2DPROC); // GL_FUNCTION_FETCH(glFramebufferTexture3D, PFNGLFRAMEBUFFERTEXTURE3DPROC); GL_FUNCTION_FETCH(glFramebufferRenderbuffer, PFNGLFRAMEBUFFERRENDERBUFFERPROC); // GL_FUNCTION_FETCH(glGetFramebufferAttachmentParameteriv, PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC); // GL_FUNCTION_FETCH(glGenerateMipmap, PFNGLGENERATEMIPMAPPROC); GL_FUNCTION_FETCH(glBlitFramebuffer, PFNGLBLITFRAMEBUFFERPROC); GL_FUNCTION_FETCH(glRenderbufferStorageMultisample, PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC); // GL_FUNCTION_FETCH(glFramebufferTextureLayer, PFNGLFRAMEBUFFERTEXTURELAYERPROC); // GL_FUNCTION_FETCH(glMapBufferRange, PFNGLMAPBUFFERRANGEPROC); // GL_FUNCTION_FETCH(glFlushMappedBufferRange, PFNGLFLUSHMAPPEDBUFFERRANGEPROC); GL_FUNCTION_FETCH(glBindVertexArray, PFNGLBINDVERTEXARRAYPROC); GL_FUNCTION_FETCH(glDeleteVertexArrays, PFNGLDELETEVERTEXARRAYSPROC); GL_FUNCTION_FETCH(glGenVertexArrays, PFNGLGENVERTEXARRAYSPROC); // GL_FUNCTION_FETCH(glIsVertexArray, PFNGLISVERTEXARRAYPROC); // GL_VERSION_3_1 // GL_FUNCTION_FETCH(glDrawArraysInstanced, PFNGLDRAWARRAYSINSTANCEDPROC); // GL_FUNCTION_FETCH(glDrawElementsInstanced, PFNGLDRAWELEMENTSINSTANCEDPROC); // GL_FUNCTION_FETCH(glTexBuffer, PFNGLTEXBUFFERPROC); // GL_FUNCTION_FETCH(glPrimitiveRestartIndex, PFNGLPRIMITIVERESTARTINDEXPROC); // GL_FUNCTION_FETCH(glCopyBufferSubData, PFNGLCOPYBUFFERSUBDATAPROC); // GL_FUNCTION_FETCH(glGetUniformIndices, PFNGLGETUNIFORMINDICESPROC); // GL_FUNCTION_FETCH(glGetActiveUniformsiv, PFNGLGETACTIVEUNIFORMSIVPROC); // GL_FUNCTION_FETCH(glGetActiveUniformName, PFNGLGETACTIVEUNIFORMNAMEPROC); GL_FUNCTION_FETCH(glGetUniformBlockIndex, PFNGLGETUNIFORMBLOCKINDEXPROC); // GL_FUNCTION_FETCH(glGetActiveUniformBlockiv, PFNGLGETACTIVEUNIFORMBLOCKIVPROC); // GL_FUNCTION_FETCH(glGetActiveUniformBlockName, PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC); GL_FUNCTION_FETCH(glUniformBlockBinding, PFNGLUNIFORMBLOCKBINDINGPROC); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(THORVG_GL_TARGET_GL) tvgWglGetCurrentContext = (PFNWGLGETCURRENTCONTEXTPROC)GetProcAddress(_libGL, "wglGetCurrentContext"); tvgWglMakeCurrent = (PFNWGLMAKECURRENTPROC)GetProcAddress(_libGL, "wglMakeCurrent"); if (!tvgWglGetCurrentContext || !tvgWglMakeCurrent) { TVGERR("GL_ENGINE", "Failed to load WGL context management functions."); return false; } #endif #if defined(THORVG_GL_TARGET_GLES) #if defined(_WIN32) && !defined(__CYGWIN__) if (!_libEGL) _libEGL = LoadLibraryW(L"libEGL.dll"); if (!_libEGL) _libEGL = LoadLibraryW(L"EGL.dll"); if (!_libEGL) { TVGERR("GL_ENGINE", "Cannot find EGL library."); return false; } tvgEglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC)GetProcAddress(_libEGL, "eglGetCurrentContext"); tvgEglMakeCurrent = (PFNEGLMAKECURRENTPROC)GetProcAddress(_libEGL, "eglMakeCurrent"); #else if (!_libEGL) _libEGL = dlopen("libEGL.so.1", RTLD_LAZY); if (!_libEGL) _libEGL = dlopen("libEGL.so", RTLD_LAZY); if (!_libEGL) { TVGERR("GL_ENGINE", "Cannot find EGL library."); return false; } tvgEglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC)dlsym(_libEGL, "eglGetCurrentContext"); tvgEglMakeCurrent = (PFNEGLMAKECURRENTPROC)dlsym(_libEGL, "eglMakeCurrent"); #endif if (!tvgEglGetCurrentContext || !tvgEglMakeCurrent) { TVGERR("GL_ENGINE", "Failed to load EGL context management functions."); return false; } #endif //Confirm the version GLint vMajor, vMinor; glGetIntegerv(GL_MAJOR_VERSION, &vMajor); glGetIntegerv(GL_MINOR_VERSION, &vMinor); if (vMajor < TVG_REQUIRE_GL_MAJOR_VER || (vMajor == TVG_REQUIRE_GL_MAJOR_VER && vMinor < TVG_REQUIRE_GL_MINOR_VER)) { TVGERR("GL_ENGINE", "OpenGL/ES version is not satisfied. Current: v%d.%d, Required: v%d.%d", vMajor, vMinor, TVG_REQUIRE_GL_MAJOR_VER, TVG_REQUIRE_GL_MINOR_VER); return false; } TVGLOG("GL_ENGINE", "OpenGL/ES version = v%d.%d", vMajor, vMinor); return true; }; bool glTerm() { #if defined(_WIN32) && !defined(__CYGWIN__) if (_libGL) FreeLibrary(_libGL); #if defined(THORVG_GL_TARGET_GLES) if (_libEGL) FreeLibrary(_libEGL); #endif #else if (_libGL) dlclose(_libGL); #if defined(THORVG_GL_TARGET_GLES) if (_libEGL) dlclose(_libEGL); #endif #endif return true; } #endif // __EMSCRIPTEN__lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-float64array.inc.h000664 001750 001750 00000002024 15164251010 053311 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Float64Array description */ #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_NUMBER_TYPE_FLOAT64 #define TYPEDARRAY_BYTES_PER_ELEMENT 8 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_FLOAT64_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_FLOAT64ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-proxy.inc.h000664 001750 001750 00000002457 15164251010 050002 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Proxy object built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_PROXY /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 2, ECMA_PROPERTY_FLAG_CONFIGURABLE) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_PROXY_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_REVOCABLE, ECMA_BUILTIN_PROXY_OBJECT_REVOCABLE, 2, 2) #endif /* JERRY_BUILTIN_PROXY */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgStr.cpp000664 001750 001750 00000017105 15164251010 033275 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include #include "tvgCommon.h" #include "tvgMath.h" #include "tvgStr.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static inline bool _floatExact(float a, float b) { return memcmp(&a, &b, sizeof(float)) == 0; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ namespace tvg { /* * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160 * * src should be one of the following form : * * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits] * [whitespace] [sign] {INF | INFINITY} * [whitespace] [sign] NAN [sequence] * * No hexadecimal form supported * no sequence supported after NAN */ float toFloat(const char *str, char **end) { if (end) *end = (char *) (str); if (!str) return 0.0f; auto a = str; auto iter = str; auto val = 0.0f; unsigned long long integerPart = 0; int minus = 1; //ignore leading whitespaces while (isspace(*iter)) iter++; //signed or not if (*iter == '-') { minus = -1; iter++; } else if (*iter == '+') { iter++; } if (tolower(*iter) == 'i') { if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3; else goto error; if (tolower(*(iter)) == 'i') { if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5; else goto error; } if (end) *end = (char *) (iter); return (minus == -1) ? -INFINITY : INFINITY; } if (tolower(*iter) == 'n') { if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3; else goto error; if (end) *end = (char *) (iter); return (minus == -1) ? -NAN : NAN; } //Optional: integer part before dot if (isdigit(*iter)) { for (; isdigit(*iter); iter++) { integerPart = integerPart * 10ULL + (unsigned long long) (*iter - '0'); } a = iter; } else if (*iter != '.') { goto success; } val = static_cast(integerPart); //Optional: decimal part after dot if (*iter == '.') { unsigned long long decimalPart = 0; unsigned long long pow10 = 1; int count = 0; iter++; if (isdigit(*iter)) { for (; isdigit(*iter); iter++, count++) { if (count < 19) { decimalPart = decimalPart * 10ULL + +static_cast(*iter - '0'); pow10 *= 10ULL; } } } else if (isspace(*iter)) { //skip if there is a space after the dot. a = iter; goto success; } val += static_cast(decimalPart) / static_cast(pow10); a = iter; } //Optional: exponent if (*iter == 'e' || *iter == 'E') { ++iter; //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em if ((*iter == 'm') || (*iter == 'M')) { //TODO: We don't support font em unit now, but has to multiply val * font size later... a = iter + 1; goto success; } //signed or not int minus_e = 1; if (*iter == '-') { minus_e = -1; ++iter; } else if (*iter == '+') { iter++; } unsigned int exponentPart = 0; if (isdigit(*iter)) { while (*iter == '0') iter++; for (; isdigit(*iter); iter++) { exponentPart = exponentPart * 10U + static_cast(*iter - '0'); } } else if (!isdigit(*(a - 1))) { a = str; goto success; } else if (*iter == 0) { goto success; } //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast(exponentPart)) <= -308)) { if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast(exponentPart)) <= -38)) { //val *= 1.0e-308f; val *= 1.0e-38f; a = iter; goto success; } a = iter; auto scale = 1.0f; while (exponentPart >= 8U) { scale *= 1E8f; exponentPart -= 8U; } while (exponentPart > 0U) { scale *= 10.0f; exponentPart--; } val = (minus_e == -1) ? (val / scale) : (val * scale); } else if ((iter > str) && !isdigit(*(iter - 1))) { a = str; goto success; } success: if (end) *end = (char *)a; if (!std::isfinite(val)) return 0.0f; return minus * val; error: if (end) *end = (char *)str; return 0.0f; } char* duplicate(const char *str, size_t n) { auto len = strlen(str); if (len < n) n = len; auto ret = tvg::malloc(n + 1); ret[n] = '\0'; return (char*)memcpy(ret, str, n); } char* append(char* lhs, const char* rhs, size_t n) { if (!rhs) return lhs; if (!lhs) return duplicate(rhs, n); lhs = tvg::realloc(lhs, strlen(lhs) + n + 1); return strncat(lhs, rhs, n); } char* dirname(const char* path) { auto ptr = strrchr(path, '/'); #ifdef _WIN32 auto ptr2 = strrchr(ptr ? ptr : path, '\\'); if (ptr2) ptr = ptr2; #endif auto len = ptr ? size_t(ptr - path + 1) : SIZE_MAX; return duplicate(path, len); } char* filename(const char* path) { const char* ptr = strrchr(path, '/'); #ifdef _WIN32 auto ptr2 = strrchr(ptr ? ptr : path, '\\'); if (ptr2) ptr = ptr2; #endif if (ptr) ++ptr; else ptr = path; auto dot = fileext(ptr); auto len = (dot > ptr) ? (size_t)(dot - ptr - 1) : strlen(ptr); return duplicate(ptr, len); } const char* fileext(const char* path) { auto ext = path; while (ext) { auto p = strchr(ext, '.'); if (!p) break; ext = p + 1; } return ext; } char* concat(const char* a, const char* b) { auto len = strlen(a) + strlen(b) + 1; auto ret = tvg::malloc(len * sizeof(char)); strcpy(ret, a); strcat(ret, b); return ret; } } modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/encodedstream.h000664 001750 001750 00000024644 15164251010 037647 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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 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, MemoryStream> { public: typedef UTF8<>::Ch Ch; EncodedInputStream(MemoryStream& is) : is_(is) { if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); if (static_cast(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 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::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::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 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(is_->Peek4()); if (!c) return; unsigned bom = static_cast(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 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_ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-handlers.h000664 001750 001750 00000003114 15164251010 047640 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BUILTIN_HANDLERS_H #define ECMA_BUILTIN_HANDLERS_H #include "ecma-builtins.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-promise-object.h" #include "ecma-proxy-object.h" typedef enum { /** @cond doxygen_suppress */ ECMA_NATIVE_HANDLER_START = 0, #define ECMA_NATIVE_HANDLER(id, handler, length) id, #include "ecma-builtin-handlers.inc.h" #undef ECMA_NATIVE_HANDLER ECMA_NATIVE_HANDLER__COUNT /** @endcond */ } ecma_native_handler_id_t; typedef enum { ECMA_NATIVE_HANDLER_FLAGS_NONE = 0, ECMA_NATIVE_HANDLER_FLAGS_NAME_INITIALIZED = (1 << 0), ECMA_NATIVE_HANDLER_FLAGS_LENGTH_INITIALIZED = (1 << 1), } ecma_native_handler_flags_t; /** * Shift for Promise helper handler function. */ #define ECMA_NATIVE_HANDLER_COMMON_FLAGS_SHIFT 2 ecma_builtin_handler_t ecma_builtin_handler_get (ecma_native_handler_id_t id); uint8_t ecma_builtin_handler_get_length (ecma_native_handler_id_t id); #endif /* !ECMA_BUILTIN_HANDLERS_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgShape.cpp000664 001750 001750 00000013561 15164251010 034105 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgShape.h" Shape :: Shape() = default; Shape* Shape::gen() noexcept { return new ShapeImpl; } Type Shape::type() const noexcept { return Type::Shape; } Result Shape::reset() noexcept { to(this)->resetPath(); return Result::Success; } Result Shape::path(const PathCommand** cmds, uint32_t* cmdsCnt, const Point** pts, uint32_t* ptsCnt) const noexcept { if (cmds) *cmds = to(this)->rs.path.cmds.data; if (cmdsCnt) *cmdsCnt = to(this)->rs.path.cmds.count; if (pts) *pts = to(this)->rs.path.pts.data; if (ptsCnt) *ptsCnt = to(this)->rs.path.pts.count; return Result::Success; } Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept { return to(this)->appendPath(cmds, cmdCnt, pts, ptsCnt); } Result Shape::moveTo(float x, float y) noexcept { to(this)->rs.path.moveTo({x, y}); return Result::Success; } Result Shape::lineTo(float x, float y) noexcept { to(this)->rs.path.lineTo({x, y}); to(this)->impl.mark(RenderUpdateFlag::Path); return Result::Success; } Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept { to(this)->rs.path.cubicTo({cx1, cy1}, {cx2, cy2}, {x, y}); to(this)->impl.mark(RenderUpdateFlag::Path); return Result::Success; } Result Shape::close() noexcept { to(this)->rs.path.close(); to(this)->impl.mark(RenderUpdateFlag::Path); return Result::Success; } Result Shape::appendCircle(float cx, float cy, float rx, float ry, bool cw) noexcept { to(this)->appendCircle(cx, cy, rx, ry, cw); return Result::Success; } Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry, bool cw) noexcept { to(this)->appendRect(x, y, w, h, rx, ry, cw); return Result::Success; } Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept { to(this)->fill(r, g, b, a); return Result::Success; } Result Shape::fill(Fill* f) noexcept { return to(this)->fill(f); } Result Shape::fill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept { to(this)->rs.fillColor(r, g, b, a); return Result::Success; } const Fill* Shape::fill() const noexcept { return to(this)->rs.fill; } Result Shape::order(bool strokeFirst) noexcept { to(this)->strokeFirst(strokeFirst); return Result::Success; } Result Shape::strokeWidth(float width) noexcept { if (width < 0.0f) width = 0.0f; to(this)->strokeWidth(width); return Result::Success; } float Shape::strokeWidth() const noexcept { return to(this)->rs.strokeWidth(); } Result Shape::strokeFill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept { to(this)->strokeFill(r, g, b, a); return Result::Success; } Result Shape::strokeFill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept { if (!to(this)->rs.strokeFill(r, g, b, a)) return Result::InsufficientCondition; return Result::Success; } Result Shape::strokeFill(Fill* f) noexcept { return to(this)->strokeFill(f); } const Fill* Shape::strokeFill() const noexcept { return to(this)->rs.strokeFill(); } Result Shape::strokeDash(const float* dashPattern, uint32_t cnt, float offset) noexcept { return to(this)->strokeDash(dashPattern, cnt, offset); } uint32_t Shape::strokeDash(const float** dashPattern, float* offset) const noexcept { return to(this)->rs.strokeDash(dashPattern, offset); } Result Shape::strokeCap(StrokeCap cap) noexcept { to(this)->strokeCap(cap); return Result::Success; } Result Shape::strokeJoin(StrokeJoin join) noexcept { to(this)->strokeJoin(join); return Result::Success; } Result Shape::strokeMiterlimit(float miterlimit) noexcept { return to(this)->strokeMiterlimit(miterlimit); } StrokeCap Shape::strokeCap() const noexcept { return to(this)->rs.strokeCap(); } StrokeJoin Shape::strokeJoin() const noexcept { return to(this)->rs.strokeJoin(); } float Shape::strokeMiterlimit() const noexcept { return to(this)->rs.strokeMiterlimit(); } Result Shape::trimpath(float begin, float end, bool simultaneous) noexcept { to(this)->trimpath({begin, end, simultaneous}); return Result::Success; } Result Shape::fillRule(FillRule r) noexcept { to(this)->rs.rule = r; return Result::Success; } FillRule Shape::fillRule() const noexcept { return to(this)->rs.rule; }src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlShaderSrc.h000664 001750 001750 00000007116 15164251010 036522 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_SHADERSRC_H_ #define _TVG_GL_SHADERSRC_H_ extern const char* COLOR_VERT_SHADER; extern const char* COLOR_FRAG_SHADER; extern const char* GRADIENT_VERT_SHADER; extern const char* STR_GRADIENT_FRAG_COMMON_VARIABLES; extern const char* STR_GRADIENT_FRAG_COMMON_FUNCTIONS; extern const char* STR_LINEAR_GRADIENT_VARIABLES; extern const char* STR_LINEAR_GRADIENT_FUNCTIONS; extern const char* STR_LINEAR_GRADIENT_MAIN; extern const char* STR_RADIAL_GRADIENT_VARIABLES; extern const char* STR_RADIAL_GRADIENT_FUNCTIONS; extern const char* STR_RADIAL_GRADIENT_MAIN; extern const char* IMAGE_VERT_SHADER; extern const char* IMAGE_FRAG_SHADER; extern const char* MASK_VERT_SHADER; extern const char* MASK_ALPHA_FRAG_SHADER; extern const char* MASK_INV_ALPHA_FRAG_SHADER; extern const char* MASK_LUMA_FRAG_SHADER; extern const char* MASK_INV_LUMA_FRAG_SHADER; extern const char* MASK_ADD_FRAG_SHADER; extern const char* MASK_SUB_FRAG_SHADER; extern const char* MASK_INTERSECT_FRAG_SHADER; extern const char* MASK_DIFF_FRAG_SHADER; extern const char* MASK_DARKEN_FRAG_SHADER; extern const char* MASK_LIGHTEN_FRAG_SHADER; extern const char* STENCIL_VERT_SHADER; extern const char* STENCIL_FRAG_SHADER; extern const char* BLIT_VERT_SHADER; extern const char* BLIT_FRAG_SHADER; extern const char* BLEND_IMAGE_FRAG_HEADER; extern const char* BLEND_SCENE_FRAG_HEADER; extern const char* BLEND_SHAPE_SOLID_FRAG_HEADER; extern const char* BLEND_SHAPE_LINEAR_FRAG_HEADER; extern const char* BLEND_SHAPE_RADIAL_FRAG_HEADER; extern const char* BLEND_FRAG_HSL; extern const char* NORMAL_BLEND_FRAG; extern const char* MULTIPLY_BLEND_FRAG; extern const char* SCREEN_BLEND_FRAG; extern const char* OVERLAY_BLEND_FRAG; extern const char* DARKEN_BLEND_FRAG; extern const char* LIGHTEN_BLEND_FRAG; extern const char* COLOR_DODGE_BLEND_FRAG; extern const char* COLOR_BURN_BLEND_FRAG; extern const char* HARD_LIGHT_BLEND_FRAG; extern const char* SOFT_LIGHT_BLEND_FRAG; extern const char* DIFFERENCE_BLEND_FRAG; extern const char* EXCLUSION_BLEND_FRAG; extern const char* HUE_BLEND_FRAG; extern const char* SATURATION_BLEND_FRAG; extern const char* COLOR_BLEND_FRAG; extern const char* LUMINOSITY_BLEND_FRAG; extern const char* ADD_BLEND_FRAG; extern const char* EFFECT_VERTEX; extern const char* GAUSSIAN_VERTICAL; extern const char* GAUSSIAN_HORIZONTAL; extern const char* EFFECT_DROPSHADOW; extern const char* EFFECT_FILL; extern const char* EFFECT_TINT; extern const char* EFFECT_TRITONE; #endif /* _TVG_GL_SHADERSRC_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgTessellator.cpp000664 001750 001750 00000043760 15164251010 037533 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgTessellator.h" #include "tvgMath.h" WgStroker::WgStroker(WgMeshData* buffer, float width, StrokeCap cap, StrokeJoin join) : mBuffer(buffer), mWidth(width), mCap(cap), mJoin(join) { } void WgStroker::run(const RenderShape& rshape, const RenderPath& path, const Matrix& m) { mMiterLimit = rshape.strokeMiterlimit(); mCap = rshape.strokeCap(); mJoin = rshape.strokeJoin(); RenderPath dashed; if (rshape.strokeDash(dashed)) run(dashed, m); else run(path, m); } RenderRegion WgStroker::bounds() const { return {{int32_t(floor(mLeftTop.x)), int32_t(floor(mLeftTop.y))}, {int32_t(ceil(mRightBottom.x)), int32_t(ceil(mRightBottom.y))}}; } BBox WgStroker::getBBox() const { return {mLeftTop, mRightBottom}; } void WgStroker::run(const RenderPath& path, const Matrix& m) { mBuffer->vbuffer.reserve(path.pts.count * 4 + 16); mBuffer->ibuffer.reserve(path.pts.count * 3); mScale = tvg::scaling(m); auto validStrokeCap = false; auto pts = path.pts.data; ARRAY_FOREACH(cmd, path.cmds) { switch (*cmd) { case PathCommand::MoveTo: { if (validStrokeCap) { // check this, so we can skip if path only contains move instruction cap(); validStrokeCap = false; } mState.firstPt = *pts; mState.firstPtDir = {0.0f, 0.0f}; mState.prevPt = *pts; mState.prevPtDir = {0.0f, 0.0f}; pts++; validStrokeCap = false; } break; case PathCommand::LineTo: { validStrokeCap = true; lineTo(*pts); pts++; } break; case PathCommand::CubicTo: { validStrokeCap = true; cubicTo(pts[0], pts[1], pts[2], m); pts += 3; } break; case PathCommand::Close: { close(); validStrokeCap = false; } break; default: break; } } if (validStrokeCap) cap(); } void WgStroker::cap() { if (mCap == StrokeCap::Butt) return; if (mCap == StrokeCap::Square) { if (mState.firstPt == mState.prevPt) squarePoint(mState.firstPt); else { square(mState.firstPt, {-mState.firstPtDir.x, -mState.firstPtDir.y}); square(mState.prevPt, mState.prevPtDir); } } else if (mCap == StrokeCap::Round) { if (mState.firstPt == mState.prevPt) roundPoint(mState.firstPt); else { round(mState.firstPt, {-mState.firstPtDir.x, -mState.firstPtDir.y}); round(mState.prevPt, mState.prevPtDir); } } } void WgStroker::lineTo(const Point& curr) { auto dir = (curr - mState.prevPt); normalize(dir); if (dir.x == 0.f && dir.y == 0.f) return; //same point auto normal = Point{-dir.y, dir.x}; auto a = mState.prevPt + normal * radius(); auto b = mState.prevPt - normal * radius(); auto c = curr + normal * radius(); auto d = curr - normal * radius(); auto ia = mBuffer->vbuffer.count; mBuffer->vbuffer.push(a); auto ib = mBuffer->vbuffer.count; mBuffer->vbuffer.push(b); auto ic = mBuffer->vbuffer.count; mBuffer->vbuffer.push(c); auto id = mBuffer->vbuffer.count; mBuffer->vbuffer.push(d); /** * a --------- c * | | * | | * b-----------d */ mBuffer->ibuffer.push(ia); mBuffer->ibuffer.push(ib); mBuffer->ibuffer.push(ic); mBuffer->ibuffer.push(ib); mBuffer->ibuffer.push(id); mBuffer->ibuffer.push(ic); if (mState.prevPt == mState.firstPt) { // first point after moveTo mState.prevPt = curr; mState.prevPtDir = dir; mState.firstPtDir = dir; } else { join(dir); mState.prevPtDir = dir; mState.prevPt = curr; } if (ia == 0) { mRightBottom.x = mLeftTop.x = curr.x; mRightBottom.y = mLeftTop.y = curr.y; } mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x))); mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y))); mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x))); mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y))); } void WgStroker::cubicTo(const Point& cnt1, const Point& cnt2, const Point& end, const Matrix& m) { Bezier curve {mState.prevPt, cnt1, cnt2, end}; Bezier relCurve {curve.start, curve.ctrl1, curve.ctrl2, curve.end}; relCurve.start *= m; relCurve.ctrl1 *= m; relCurve.ctrl2 *= m; relCurve.end *= m; auto count = relCurve.segments(); auto step = 1.f / count; for (uint32_t i = 0; i <= count; i++) { lineTo(curve.at(step * i)); } } void WgStroker::close() { if (length(mState.prevPt - mState.firstPt) > 0.015625f) { lineTo(mState.firstPt); } // join firstPt with prevPt join(mState.firstPtDir); } void WgStroker::join(const Point& dir) { auto orient = orientation(mState.prevPt - mState.prevPtDir, mState.prevPt, mState.prevPt + dir); if (orient == Orientation::Linear) { if (mState.prevPtDir == dir) return; // check is same direction if (mJoin != StrokeJoin::Round) return; // opposite direction auto normal = Point{-dir.y, dir.x}; auto p1 = mState.prevPt + normal * radius(); auto p2 = mState.prevPt - normal * radius(); auto oc = mState.prevPt + dir * radius(); round(p1, oc, mState.prevPt); round(oc, p2, mState.prevPt); } else { auto normal = Point{-dir.y, dir.x}; auto prevNormal = Point{-mState.prevPtDir.y, mState.prevPtDir.x}; Point prevJoin, currJoin; if (orient == Orientation::CounterClockwise) { prevJoin = mState.prevPt + prevNormal * radius(); currJoin = mState.prevPt + normal * radius(); } else { prevJoin = mState.prevPt - prevNormal * radius(); currJoin = mState.prevPt - normal * radius(); } if (mJoin == StrokeJoin::Miter) miter(prevJoin, currJoin, mState.prevPt); else if (mJoin == StrokeJoin::Bevel) bevel(prevJoin, currJoin, mState.prevPt); else round(prevJoin, currJoin, mState.prevPt); } } void WgStroker::round(const Point &prev, const Point& curr, const Point& center) { auto orient = orientation(prev, center, curr); if (orient == Orientation::Linear) return; mLeftTop.x = std::min(mLeftTop.x, std::min(center.x, std::min(prev.x, curr.x))); mLeftTop.y = std::min(mLeftTop.y, std::min(center.y, std::min(prev.y, curr.y))); mRightBottom.x = std::max(mRightBottom.x, std::max(center.x, std::max(prev.x, curr.x))); mRightBottom.y = std::max(mRightBottom.y, std::max(center.y, std::max(prev.y, curr.y))); auto startAngle = tvg::atan2(prev.y - center.y, prev.x - center.x); auto endAngle = tvg::atan2(curr.y - center.y, curr.x - center.x); if (orient == Orientation::Clockwise) { if (endAngle > startAngle) endAngle -= 2 * MATH_PI; } else { if (endAngle < startAngle) endAngle += 2 * MATH_PI; } auto arcAngle = endAngle - startAngle; auto count = tvg::arcSegmentsCnt(arcAngle, radius() * mScale); auto c = mBuffer->vbuffer.count; mBuffer->vbuffer.push(center); auto pi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(prev); auto step = (endAngle - startAngle) / (count - 1); for (uint32_t i = 1; i < static_cast(count); i++) { auto angle = startAngle + step * i; Point out = {center.x + cos(angle) * radius(), center.y + sin(angle) * radius()}; auto oi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(out); mBuffer->ibuffer.push(c); mBuffer->ibuffer.push(pi); mBuffer->ibuffer.push(oi); pi = oi; mLeftTop.x = std::min(mLeftTop.x, out.x); mLeftTop.y = std::min(mLeftTop.y, out.y); mRightBottom.x = std::max(mRightBottom.x, out.x); mRightBottom.y = std::max(mRightBottom.y, out.y); } } void WgStroker::roundPoint(const Point &p) { auto count = tvg::arcSegmentsCnt(2.0f * MATH_PI, radius() * mScale); auto c = mBuffer->vbuffer.count; mBuffer->vbuffer.push(p); auto step = 2.0f * MATH_PI / (count - 1); for (uint32_t i = 1; i <= static_cast(count); i++) { float angle = i * step; Point dir = {cos(angle), sin(angle)}; Point out = p + dir * radius(); auto oi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(out); if (oi > 1) { mBuffer->ibuffer.push(c); mBuffer->ibuffer.push(oi); mBuffer->ibuffer.push(oi - 1); } } mLeftTop.x = std::min(mLeftTop.x, p.x - radius()); mLeftTop.y = std::min(mLeftTop.y, p.y - radius()); mRightBottom.x = std::max(mRightBottom.x, p.x + radius()); mRightBottom.y = std::max(mRightBottom.y, p.y + radius()); } void WgStroker::miter(const Point& prev, const Point& curr, const Point& center) { auto pp1 = prev - center; auto pp2 = curr - center; auto out = pp1 + pp2; auto k = 2.f * radius() * radius() / (out.x * out.x + out.y * out.y); auto pe = out * k; if (length(pe) >= mMiterLimit * radius()) { bevel(prev, curr, center); return; } auto join = center + pe; auto c = mBuffer->vbuffer.count; mBuffer->vbuffer.push(center); auto cp1 = mBuffer->vbuffer.count; mBuffer->vbuffer.push(prev); auto cp2 = mBuffer->vbuffer.count; mBuffer->vbuffer.push(curr); auto e = mBuffer->vbuffer.count; mBuffer->vbuffer.push(join); mBuffer->ibuffer.push(c); mBuffer->ibuffer.push(cp1); mBuffer->ibuffer.push(e); mBuffer->ibuffer.push(e); mBuffer->ibuffer.push(cp2); mBuffer->ibuffer.push(c); mLeftTop.x = std::min(mLeftTop.x, join.x); mLeftTop.y = std::min(mLeftTop.y, join.y); mRightBottom.x = std::max(mRightBottom.x, join.x); mRightBottom.y = std::max(mRightBottom.y, join.y); } void WgStroker::bevel(const Point& prev, const Point& curr, const Point& center) { auto a = mBuffer->vbuffer.count; mBuffer->vbuffer.push(prev); auto b = mBuffer->vbuffer.count; mBuffer->vbuffer.push(curr); auto c = mBuffer->vbuffer.count; mBuffer->vbuffer.push(center); mBuffer->ibuffer.push(a); mBuffer->ibuffer.push(b); mBuffer->ibuffer.push(c); } void WgStroker::square(const Point& p, const Point& outDir) { auto normal = Point{-outDir.y, outDir.x}; auto a = p + normal * radius(); auto b = p - normal * radius(); auto c = a + outDir * radius(); auto d = b + outDir * radius(); auto ai = mBuffer->vbuffer.count; mBuffer->vbuffer.push(a); auto bi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(b); auto ci = mBuffer->vbuffer.count; mBuffer->vbuffer.push(c); auto di = mBuffer->vbuffer.count; mBuffer->vbuffer.push(d); mBuffer->ibuffer.push(ai); mBuffer->ibuffer.push(bi); mBuffer->ibuffer.push(ci); mBuffer->ibuffer.push(ci); mBuffer->ibuffer.push(bi); mBuffer->ibuffer.push(di); mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x))); mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y))); mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x))); mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y))); } void WgStroker::squarePoint(const Point& p) { auto offsetX = Point{radius(), 0.0f}; auto offsetY = Point{0.0f, radius()}; auto a = p + offsetX + offsetY; auto b = p - offsetX + offsetY; auto c = p - offsetX - offsetY; auto d = p + offsetX - offsetY; auto ai = mBuffer->vbuffer.count; mBuffer->vbuffer.push(a); auto bi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(b); auto ci = mBuffer->vbuffer.count; mBuffer->vbuffer.push(c); auto di = mBuffer->vbuffer.count; mBuffer->vbuffer.push(d); mBuffer->ibuffer.push(ai); mBuffer->ibuffer.push(bi); mBuffer->ibuffer.push(ci); mBuffer->ibuffer.push(ci); mBuffer->ibuffer.push(di); mBuffer->ibuffer.push(ai); mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x))); mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y))); mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x))); mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y))); } void WgStroker::round(const Point& p, const Point& outDir) { auto normal = Point{-outDir.y, outDir.x}; auto a = p + normal * radius(); auto b = p - normal * radius(); auto c = p + outDir * radius(); round(a, c, p); round(c, b, p); } WgBWTessellator::WgBWTessellator(WgMeshData* buffer): mBuffer(buffer) { } void WgBWTessellator::tessellate(const RenderPath& path, const Matrix& matrix) { if (path.pts.count <= 2) return; auto cmds = path.cmds.data; auto cmdCnt = path.cmds.count; auto pts = path.pts.data; auto ptsCnt = path.pts.count; uint32_t firstIndex = 0; uint32_t prevIndex = 0; mBuffer->vbuffer.reserve(ptsCnt * 2); mBuffer->ibuffer.reserve((ptsCnt - 2) * 3); auto updateConvexity = [&](const Point& edge) { if (!convex) return; if (prevEdge.x == 0.0f && prevEdge.y == 0.0f) { prevEdge = edge; return; } auto c = cross(prevEdge, edge); if (zero(c)) { prevEdge = edge; return; } auto sign = (c > 0) ? 1 : -1; if (winding == 0) winding = sign; // The default winding is CCW, but it might be otherwise once we support unordered points.Expand commentComment on line R453ResolvedCode has comments. Press enter to view. else if (sign != winding) convex = false; prevEdge = edge; }; for (uint32_t i = 0; i < cmdCnt; i++) { switch(cmds[i]) { case PathCommand::MoveTo: { firstIndex = pushVertex(pts->x, pts->y); firstPt = prevPt = *pts; prevEdge = {}; prevIndex = 0; pts++; } break; case PathCommand::LineTo: { if (prevIndex == 0) { prevIndex = pushVertex(pts->x, pts->y); prevEdge = *pts - prevPt; prevPt = *pts++; } else { updateConvexity(*pts - prevPt); auto currIndex = pushVertex(pts->x, pts->y); pushTriangle(firstIndex, prevIndex, currIndex); prevIndex = currIndex; prevPt = *pts++; } } break; case PathCommand::CubicTo: { Bezier curve{pts[-1], pts[0], pts[1], pts[2]}; if (convex) { auto e1 = curve.ctrl1 - curve.start; auto e2 = curve.ctrl2 - curve.ctrl1; auto e3 = curve.end - curve.ctrl2; if (prevIndex != 0) updateConvexity(e1); else prevEdge = e1; updateConvexity(e2); updateConvexity(e3); } auto stepCount = (curve * matrix).segments(); if (stepCount <= 1) stepCount = 2; float step = 1.f / stepCount; for (uint32_t s = 1; s <= static_cast(stepCount); s++) { auto pt = curve.at(step * s); auto currIndex = pushVertex(pt.x, pt.y); if (prevIndex == 0) { prevIndex = currIndex; continue; } pushTriangle(firstIndex, prevIndex, currIndex); prevIndex = currIndex; } prevPt = curve.end; pts += 3; } break; case PathCommand::Close: { if (convex && prevIndex != 0) { updateConvexity(firstPt - prevPt); if (convex && winding != 0) { auto secondPt = mBuffer->vbuffer[firstIndex+1]; updateConvexity(secondPt - firstPt); } } } break; default: break; } } } RenderRegion WgBWTessellator::bounds() const { return {{int32_t(floor(bbox.min.x)), int32_t(floor(bbox.min.y))}, {int32_t(ceil(bbox.max.x)), int32_t(ceil(bbox.max.y))}}; } BBox WgBWTessellator::getBBox() const { return bbox; } uint32_t WgBWTessellator::pushVertex(float x, float y) { auto index = mBuffer->vbuffer.count; mBuffer->vbuffer.push({x, y}); if (index == 0) bbox.max = bbox.min = {x, y}; else bbox = {{std::min(bbox.min.x, x), std::min(bbox.min.y, y)}, {std::max(bbox.max.x, x), std::max(bbox.max.y, y)}}; return index; } void WgBWTessellator::pushTriangle(uint32_t a, uint32_t b, uint32_t c) { mBuffer->ibuffer.push(a); mBuffer->ibuffer.push(b); mBuffer->ibuffer.push(c); } src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgShaderSrc.cpp000664 001750 001750 00000105132 15164251010 037100 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgShaderSrc.h" #include //************************************************************************ // graphics shader source: stencil //************************************************************************ const char* cShaderSrc_Stencil = R"( struct VertexInput { @location(0) position: vec2f }; struct VertexOutput { @builtin(position) position: vec4f }; struct PaintSettings { transform: mat4x4f }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { return vec4f(0.0, 0.0, 0.0, 1.0); } )"; //************************************************************************ // graphics shader source: depth //************************************************************************ const char* cShaderSrc_Depth = R"( struct VertexInput { @location(0) position: vec2f }; struct VertexOutput { @builtin(position) position: vec4f }; struct PaintSettings { transform: mat4x4f }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @group(2) @binding(0) var uDepth : f32; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); out.position.z = uDepth; return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { return vec4f(1.0, 0.5, 0.0, 1.0); } )"; //************************************************************************ // graphics shader source: solid normal blend //************************************************************************ const char* cShaderSrc_Solid = R"( struct VertexInput { @location(0) position: vec2f }; struct VertexOutput { @builtin(position) position: vec4f }; struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { let Sc = uPaintSettings.color; let So = uPaintSettings.options.a; return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); } )"; //************************************************************************ // graphics shader source: linear normal blend //************************************************************************ const char* cShaderSrc_Linear = R"( struct VertexInput { @location(0) position: vec2f }; struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f }; struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f }; struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings }; // uniforms @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @group(2) @binding(0) var uSamplerGrad : sampler; @group(2) @binding(1) var uTextureGrad : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0); return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { let pos = in.vGradCoord.xy; let st = uPaintSettings.gradient.coords.xy; let ed = uPaintSettings.gradient.coords.zw; let ba = ed - st; let t = dot(pos - st, ba) / dot(ba, ba); let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5)); let So = uPaintSettings.options.a; return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); } )"; //************************************************************************ // graphics shader source: radial normal blend //************************************************************************ const char* cShaderSrc_Radial = R"( struct VertexInput { @location(0) position: vec2f }; struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f }; struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f }; struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @group(2) @binding(0) var uSamplerGrad : sampler; @group(2) @binding(1) var uTextureGrad : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0); return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { // original data let d0 = in.vGradCoord.xy - uPaintSettings.gradient.coords.xy; let d1 = uPaintSettings.gradient.coords.xy - uPaintSettings.gradient.focal.xy; let r0 = uPaintSettings.gradient.coords.z; let rd = uPaintSettings.gradient.focal.z - uPaintSettings.gradient.coords.z; let a = 1.0*dot(d1, d1) - 1.0*rd*rd; let b = 2.0*dot(d0, d1) - 2.0*r0*rd; let c = 1.0*dot(d0, d0) - 1.0*r0*r0; let d = b*b - 4*a*c; var t = 0.0; if (d >= 0) { t = min(1.0, (-b + sqrt(d))/(2*a)); } if ((c > 0) && (t >= 1.0)) { t = 0.0; } let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(1.0 - t, 0.5)); let So = uPaintSettings.options.a; return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); } )"; //************************************************************************ // graphics shader source: image normal blend //************************************************************************ const char* cShaderSrc_Image = R"( struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord: vec2f }; struct PaintSettings { transform: mat4x4f, options: vec4f }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @group(2) @binding(0) var uSampler : sampler; @group(2) @binding(1) var uTextureView : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); out.vTexCoord = in.texCoord; return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { var Sc: vec4f = textureSample(uTextureView, uSampler, in.vTexCoord.xy); let So: f32 = uPaintSettings.options.a; return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); }; )"; //************************************************************************ // graphics shader source: scene normal blend //************************************************************************ const char* cShaderSrc_Scene = R"( struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord: vec2f }; @group(0) @binding(0) var uSamplerSrc : sampler; @group(0) @binding(1) var uTextureSrc : texture_2d; @group(1) @binding(0) var So : f32; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = vec4f(in.position.xy, 0.0, 1.0); out.vTexCoord = in.texCoord; return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { let Sc = textureSample(uTextureSrc, uSamplerSrc, in.vTexCoord.xy); return vec4f(Sc.rgb * So, Sc.a * So); }; )"; //************************************************************************ // graphics shader source: custom shaders //************************************************************************ const char* cShaderSrc_Solid_Blend = R"( struct VertexInput { @location(0) position: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(1) vScrCoord: vec2f }; struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; // @group(2) - empty @group(3) @binding(0) var uSamplerDst : sampler; @group(3) @binding(1) var uTextureDst : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); out.position = pos; out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5); return out; } struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 }; fn getFragData(in: VertexOutput) -> FragData { // get source data let colorSrc = uPaintSettings.color; let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy); // fill fragment data var data: FragData; data.Sc = colorSrc.rgb; data.Sa = colorSrc.a; data.So = uPaintSettings.options.a; data.Dc = colorDst.rgb; data.Da = colorDst.a; data.Sc = data.Sa * data.So * data.Sc; data.Sa = data.Sa * data.So; return data; }; fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; }; )"; const char* cShaderSrc_Linear_Blend = R"( struct VertexInput { @location(0) position: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f }; struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f }; struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @group(2) @binding(0) var uSamplerGrad : sampler; @group(2) @binding(1) var uTextureGrad : texture_2d; @group(3) @binding(0) var uSamplerDst : sampler; @group(3) @binding(1) var uTextureDst : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); out.position = pos; out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0); out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5); return out; } struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 }; fn getFragData(in: VertexOutput) -> FragData { // get source data let pos = in.vGradCoord.xy; let st = uPaintSettings.gradient.coords.xy; let ed = uPaintSettings.gradient.coords.zw; let ba = ed - st; let t = dot(pos - st, ba) / dot(ba, ba); let colorSrc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5)); let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy); // fill fragment data var data: FragData; data.Sc = colorSrc.rgb; data.Sa = colorSrc.a; data.So = uPaintSettings.options.a; data.Dc = colorDst.rgb; data.Da = colorDst.a; data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So); data.Sa = mix(data.Da, 1.0, data.Sa * data.So); return data; }; fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; }; )"; const char* cShaderSrc_Radial_Blend = R"( struct VertexInput { @location(0) position: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f }; struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f }; struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @group(2) @binding(0) var uSamplerGrad : sampler; @group(2) @binding(1) var uTextureGrad : texture_2d; @group(3) @binding(0) var uSamplerDst : sampler; @group(3) @binding(1) var uTextureDst : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); out.position = pos; out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0); out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5); return out; } struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 }; fn getFragData(in: VertexOutput) -> FragData { let d0 = in.vGradCoord.xy - uPaintSettings.gradient.coords.xy; let d1 = uPaintSettings.gradient.coords.xy - uPaintSettings.gradient.focal.xy; let r0 = uPaintSettings.gradient.coords.z; let rd = uPaintSettings.gradient.focal.z - uPaintSettings.gradient.coords.z; let a = 1.0*dot(d1, d1) - 1.0*rd*rd; let b = 2.0*dot(d0, d1) - 2.0*r0*rd; let c = 1.0*dot(d0, d0) - 1.0*r0*r0; let d = b*b - 4*a*c; var t = 0.0; if (d >= 0) { t = min(1.0, (-b + sqrt(d))/(2*a)); } if ((c > 0) && (t >= 1.0)) { t = 0.0; } let colorSrc = textureSample(uTextureGrad, uSamplerGrad, vec2f(1.0 - t, 0.5)); let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy); // fill fragment data var data: FragData; data.Sc = colorSrc.rgb; data.Sa = colorSrc.a; data.So = uPaintSettings.options.a; data.Dc = colorDst.rgb; data.Da = colorDst.a; data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So); data.Sa = mix(data.Da, 1.0, data.Sa * data.So); return data; }; fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; }; )"; const char* cShaderSrc_Image_Blend = R"( struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord : vec2f, @location(1) vScrCoord: vec2f }; struct PaintSettings { transform: mat4x4f, options: vec4f }; @group(0) @binding(0) var uViewMat : mat4x4f; @group(1) @binding(0) var uPaintSettings : PaintSettings; @group(2) @binding(0) var uSamplerSrc : sampler; @group(2) @binding(1) var uTextureSrc : texture_2d; @group(3) @binding(0) var uSamplerDst : sampler; @group(3) @binding(1) var uTextureDst : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0); out.position = pos; out.vTexCoord = in.texCoord; out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5); return out; } struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 }; fn getFragData(in: VertexOutput) -> FragData { // get source data let colorSrc = textureSample(uTextureSrc, uSamplerSrc, in.vTexCoord.xy); let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy); // fill fragment data var data: FragData; data.Sc = colorSrc.rgb; data.Sa = colorSrc.a; data.So = uPaintSettings.options.a; data.Dc = colorDst.rgb; data.Da = colorDst.a; data.Sc = data.Sc * data.So; data.Sa = data.Sa * data.So; return data; }; fn postProcess(d: FragData, R: vec4f) -> vec4f { return mix(vec4(d.Dc, d.Da), R, d.Sa); }; )"; const char* cShaderSrc_Scene_Blend = R"( struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vScrCoord: vec2f }; @group(0) @binding(0) var uSamplerSrc : sampler; @group(0) @binding(1) var uTextureSrc : texture_2d; @group(1) @binding(0) var uSamplerDst : sampler; @group(1) @binding(1) var uTextureDst : texture_2d; @group(2) @binding(0) var So : f32; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = vec4f(in.position.xy, 0.0, 1.0); out.vScrCoord = in.texCoord; return out; } struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 }; fn getFragData(in: VertexOutput) -> FragData { // get source data let colorSrc = textureSample(uTextureSrc, uSamplerSrc, in.vScrCoord.xy); let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy); // fill fragment data var data: FragData; data.Sc = min(vec3f(1.0, 1.0, 1.0), colorSrc.rgb / select(colorSrc.a, 1.0, (colorSrc.a == 0.0) || (colorSrc.a == 1.0))); data.Sa = colorSrc.a; data.Dc = colorDst.rgb; data.Da = colorDst.a; return data; }; fn postProcess(d: FragData, R: vec4f) -> vec4f { return mix(vec4(d.Dc, d.Da), R, d.Sa * So); }; )"; const char* cShaderSrc_BlendFuncs = R"( const One = vec3f(1.0, 1.0, 1.0); // RGB to HSL conversion fn rgbToHsl(color: vec3f) -> vec3f { let minVal = min(color.r, min(color.g, color.b)); let maxVal = max(color.r, max(color.g, color.b)); let delta = maxVal - minVal; var h = 0.0; if (delta > 0.0) { if (maxVal == color.r) { h = (color.g - color.b) / delta - trunc(h / 6.0) * 6.0; } else if (maxVal == color.g) { h = (color.b - color.r) / delta + 2.0; } else { h = (color.r - color.g) / delta + 4.0; } h = h * 60.0; if (h < 0.0) { h += 360.0; } } let l = (maxVal + minVal) * 0.5; var s = select(0.0, delta / (1.0 - abs(2.0 * l - 1.0)), delta > 0.0); return vec3f(h, s, l); }; // HSL to RGB conversion fn hslToRgb(color: vec3f) -> vec3f { let h = color.x; let s = color.y; let l = color.z; let C = (1.0 - abs(2.0 * l - 1.0)) * s; let h_prime = h / 60.0; let X = C * (1.0 - abs(h_prime - 2.0 * trunc(h_prime / 2.0) - 1.0)); let m = l - C / 2.0; var rgb = vec3f(0.0); if (h_prime >= 0.0 && h_prime < 1.0) { rgb = vec3f(C, X, 0.0); } else if (h_prime >= 1.0 && h_prime < 2.0) { rgb = vec3f(X, C, 0.0); } else if (h_prime >= 2.0 && h_prime < 3.0) { rgb = vec3f(0.0, C, X); } else if (h_prime >= 3.0 && h_prime < 4.0) { rgb = vec3f(0.0, X, C); } else if (h_prime >= 4.0 && h_prime < 5.0) { rgb = vec3f(X, 0.0, C); } else { rgb = vec3f(C, 0.0, X); } return rgb + vec3f(m); }; @fragment fn fs_main_Normal(in: VertexOutput) -> @location(0) vec4f { // used as debug blend method return vec4f(1.0, 0.0, 0.0, 1.0); } @fragment fn fs_main_Multiply(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { Rc = d.Sc * min(One, d.Dc / d.Da); Rc = mix(d.Sc, Rc, d.Da); }; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Screen(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); let Rc = d.Sc + d.Dc - d.Sc * d.Dc; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Overlay(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); Rc.r = select(1.0 - min(1.0, 2 * (1 - d.Sc.r) * (1 - Dc.r)), min(1.0, 2 * d.Sc.r * Dc.r), (Dc.r < 0.5)); Rc.g = select(1.0 - min(1.0, 2 * (1 - d.Sc.g) * (1 - Dc.g)), min(1.0, 2 * d.Sc.g * Dc.g), (Dc.g < 0.5)); Rc.b = select(1.0 - min(1.0, 2 * (1 - d.Sc.b) * (1 - Dc.b)), min(1.0, 2 * d.Sc.b * Dc.b), (Dc.b < 0.5)); Rc = mix(d.Sc, Rc, d.Da); } return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Darken(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { Rc = min(d.Sc, min(One, d.Dc / d.Da)); Rc = mix(d.Sc, Rc, d.Da); }; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Lighten(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); let Rc = max(d.Sc, d.Dc); return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_ColorDodge(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); Rc.r = select(0.0, select(1.0, min(1.0, Dc.r / (1.0 - d.Sc.r)), d.Sc.r < 1.0), Dc.r > 0.0); Rc.g = select(0.0, select(1.0, min(1.0, Dc.g / (1.0 - d.Sc.g)), d.Sc.g < 1.0), Dc.g > 0.0); Rc.b = select(0.0, select(1.0, min(1.0, Dc.b / (1.0 - d.Sc.b)), d.Sc.b < 1.0), Dc.b > 0.0); Rc = mix(d.Sc, Rc, d.Da); } return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_ColorBurn(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); Rc.r = select(select(1.0, 0.0, Dc.r < 1), 1.0 - min(1.0, (1.0 - Dc.r) / d.Sc.r), d.Sc.r > 0); Rc.g = select(select(1.0, 0.0, Dc.g < 1), 1.0 - min(1.0, (1.0 - Dc.g) / d.Sc.g), d.Sc.g > 0); Rc.b = select(select(1.0, 0.0, Dc.b < 1), 1.0 - min(1.0, (1.0 - Dc.b) / d.Sc.b), d.Sc.b > 0); Rc = mix(d.Sc, Rc, d.Da); } return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_HardLight(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); Rc.r = select(1.0 - min(1.0, 2 * (1 - d.Sc.r) * (1 - Dc.r)), min(1.0, 2 * d.Sc.r * Dc.r), (d.Sc.r < 0.5)); Rc.g = select(1.0 - min(1.0, 2 * (1 - d.Sc.g) * (1 - Dc.g)), min(1.0, 2 * d.Sc.g * Dc.g), (d.Sc.g < 0.5)); Rc.b = select(1.0 - min(1.0, 2 * (1 - d.Sc.b) * (1 - Dc.b)), min(1.0, 2 * d.Sc.b * Dc.b), (d.Sc.b < 0.5)); Rc = mix(d.Sc, Rc, d.Da); } return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_SoftLight(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); Rc = min(One, (One - 2 * d.Sc) * Dc * Dc + 2.0 * d.Sc * Dc); Rc = mix(d.Sc, Rc, d.Da); }; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Difference(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); let Rc = abs(d.Dc - d.Sc); return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Exclusion(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); let Rc = d.Sc + d.Dc - 2 * d.Sc * d.Dc; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Hue(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); let Sc = d.Sc; let Shsl = rgbToHsl(Sc); let Dhsl = rgbToHsl(Dc); Rc = hslToRgb(vec3(Shsl.r, Dhsl.g, Dhsl.b)); // sh, ds, dl Rc = mix(d.Sc, Rc, d.Da); }; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Saturation(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); let Sc = d.Sc; let Shsl = rgbToHsl(Sc); let Dhsl = rgbToHsl(Dc); Rc = hslToRgb(vec3(Dhsl.r, Shsl.g, Dhsl.b)); // dh, ss, dl Rc = mix(d.Sc, Rc, d.Da); }; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Color(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); let Sc = d.Sc; let Shsl = rgbToHsl(Sc); let Dhsl = rgbToHsl(Dc); Rc = hslToRgb(vec3(Shsl.r, Shsl.g, Dhsl.b)); // sh, ss, dl Rc = mix(d.Sc, Rc, d.Da); }; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Luminosity(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); var Rc = d.Sc; if (d.Da > 0.0) { let Dc = min(One, d.Dc / d.Da); let Sc = d.Sc; let Shsl = rgbToHsl(Sc); let Dhsl = rgbToHsl(Dc); Rc = hslToRgb(vec3(Dhsl.r, Dhsl.g, Shsl.b)); // dh, ds, sl Rc = mix(d.Sc, Rc, d.Da); }; return postProcess(d, vec4f(Rc, 1.0)); } @fragment fn fs_main_Add(in: VertexOutput) -> @location(0) vec4f { let d: FragData = getFragData(in); let Rc = min(One, d.Sc + d.Dc); return postProcess(d, vec4f(Rc, 1.0)); } )"; //************************************************************************ // graphics shader source: scene compose //************************************************************************ const char* cShaderSrc_Scene_Compose = R"( struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord: vec2f }; @group(0) @binding(0) var uSamplerSrc : sampler; @group(0) @binding(1) var uTextureSrc : texture_2d; @group(1) @binding(0) var uSamplerMsk : sampler; @group(1) @binding(1) var uTextureMsk : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = vec4f(in.position.xy, 0.0, 1.0); out.texCoord = in.texCoord; return out; } @fragment fn fs_main_None(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); return vec4f(src); }; @fragment fn fs_main_ClipPath(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src * msk.a); }; @fragment fn fs_main_AlphaMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src * msk.a); }; @fragment fn fs_main_InvAlphaMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src * (1.0 - msk.a)); }; @fragment fn fs_main_LumaMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); let luma: f32 = dot(msk.rgb, vec3f(0.2125, 0.7154, 0.0721)); return vec4f(src * luma); }; @fragment fn fs_main_InvLumaMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); let luma: f32 = dot(msk.rgb, vec3f(0.2125, 0.7154, 0.0721)); return vec4f(src * (1.0 - luma)); }; @fragment fn fs_main_AddMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src.rgb, src.a + msk.a * (1.0 - src.a)); }; @fragment fn fs_main_SubtractMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src.rgb, src.a * (1.0 - msk.a)); }; @fragment fn fs_main_IntersectMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src.rgb, src.a * msk.a); }; @fragment fn fs_main_DifferenceMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src.rgb, src.a * (1.0 - msk.a) + msk.a * (1.0 - src.a)); }; @fragment fn fs_main_LightenMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src.rgb, max(src.a, msk.a)); }; @fragment fn fs_main_DarkenMask(in: VertexOutput) -> @location(0) vec4f { let src: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let msk: vec4f = textureSample(uTextureMsk, uSamplerMsk, in.texCoord.xy); return vec4f(src.rgb, min(src.a, msk.a)); }; )"; //************************************************************************ // graphics shader source: texture blit //************************************************************************ const char* cShaderSrc_Blit = R"( struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord: vec2f }; @group(0) @binding(0) var uSamplerSrc : sampler; @group(0) @binding(1) var uTextureSrc : texture_2d; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = vec4f(in.position.xy, 0.0, 1.0); out.texCoord = in.texCoord; return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { return textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); }; )"; //************************************************************************ // shader source: effects //************************************************************************ const char* cShaderSrc_Shadow = R"( struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord: vec2f }; @group(0) @binding(0) var uSamplerSrc : sampler; @group(0) @binding(1) var uTextureSrc : texture_2d; @group(1) @binding(0) var uSamplerSdw : sampler; @group(1) @binding(1) var uTextureSdw : texture_2d; @group(2) @binding(0) var settings: array; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = vec4f(in.position.xy, 0.0, 1.0); out.texCoord = in.texCoord; return out; } @fragment fn fs_main_shadow(in: VertexOutput) -> @location(0) vec4f { let texelSize: vec2f = 1.0 / vec2f(textureDimensions(uTextureSrc)); let offset: vec2f = settings[2].xy * texelSize; let orig: vec4f = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let blur: vec4f = textureSample(uTextureSdw, uSamplerSdw, in.texCoord.xy - offset); let shad: vec4f = settings[1] * blur.a; return orig + shad * (1.0 - orig.a); }; )"; const char* cShaderSrc_Effects = R"( struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord: vec2f }; @group(0) @binding(0) var uSamplerSrc : sampler; @group(0) @binding(1) var uTextureSrc : texture_2d; @group(1) @binding(0) var settings: array; @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = vec4f(in.position.xy, 0.0, 1.0); out.texCoord = in.texCoord; return out; } fn gaussian(x: f32, sigma: f32) -> f32 { let exponent: f32 = -x * x / (2.0 * sigma * sigma); return exp(exponent) * 0.39894f / sigma; } @fragment fn fs_main_vert(in: VertexOutput) -> @location(0) vec4f { let texelSize: vec2f = 1.0 / vec2f(textureDimensions(uTextureSrc)); var colorSum: vec4f = vec4(0.0); let sigma: f32 = settings[0].x * settings[0].y; var weightSum: f32 = 0.0; let radius: i32 = i32(settings[0].z); for (var y: i32 = -radius; y <= radius; y++) { let offset: vec2f = vec2f(0.0, f32(y) * texelSize.y); let coord = in.texCoord.xy + offset; let weight: f32 = select(0.0, gaussian(f32(y), sigma), saturate(coord.y) == coord.y); colorSum += textureSample(uTextureSrc, uSamplerSrc, coord) * weight; weightSum += weight; } return colorSum / weightSum; }; @fragment fn fs_main_horz(in: VertexOutput) -> @location(0) vec4f { let texelSize: vec2f = 1.0 / vec2f(textureDimensions(uTextureSrc)); var colorSum: vec4f = vec4(0.0); let sigma: f32 = settings[0].x * settings[0].y; var weightSum: f32 = 0.0; let radius: i32 = i32(settings[0].z); for (var y: i32 = -radius; y <= radius; y++) { let offset: vec2f = vec2f(f32(y) * texelSize.x, 0.0); let coord = in.texCoord.xy + offset; let weight: f32 = select(0.0, gaussian(f32(y), sigma), saturate(coord.x) == coord.x); colorSum += textureSample(uTextureSrc, uSamplerSrc, coord) * weight; weightSum += weight; } return colorSum / weightSum; }; @fragment fn fs_main_fill(in: VertexOutput) -> @location(0) vec4f { let orig = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let fill = settings[0]; return fill * orig.a * fill.a; }; @fragment fn fs_main_tint(in: VertexOutput) -> @location(0) vec4f { let orig = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let luma: f32 = dot(orig.rgb, vec3f(0.2126, 0.7152, 0.0722)); return vec4f(mix(orig.rgb, mix(settings[0].rgb, settings[1].rgb, luma), settings[2].r) * orig.a, orig.a); }; @fragment fn fs_main_tritone(in: VertexOutput) -> @location(0) vec4f { let orig = textureSample(uTextureSrc, uSamplerSrc, in.texCoord.xy); let luma: f32 = dot(orig.rgb, vec3f(0.2126, 0.7152, 0.0722)); let isBright: bool = luma >= 0.5f; let t = select(luma * 2.0f, (luma - 0.5) * 2.0f, isBright); let frm: vec3f = select(settings[0].rgb, settings[1].rgb, isBright); let to: vec3f = select(settings[1].rgb, settings[2].rgb, isBright); var tmp: vec4f = vec4f(mix(frm, to, t), 1.0f); if (settings[2].a > 0.0f) { tmp = mix(tmp, orig, settings[2].a); } return tmp * orig.a; }; )";loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-map-iterator-prototype.inc.h000664 001750 001750 00000002264 15164251010 053264 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %MapIteratorPrototype% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_MAP_ITERATOR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_NEXT, ECMA_BUILTIN_MAP_ITERATOR_PROTOTYPE_ROUTINE_OBJECT_NEXT, 0, 0) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgText.cpp000664 001750 001750 00000010160 15164251010 033761 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgText.h" Text::Text() = default; Result Text::text(const char* text) noexcept { return to(this)->text(text); } Result Text::font(const char* name) noexcept { return to(this)->font(name); } Result Text::size(float size) noexcept { return to(this)->size(size); } Result Text::load(const char* filename) noexcept { #ifdef THORVG_FILE_IO_SUPPORT bool invalid; //invalid path auto loader = LoaderMgr::loader(filename, &invalid); if (loader) { if (loader->sharing > 0) --loader->sharing; //font loading doesn't mean sharing. return Result::Success; } else { if (invalid) return Result::InvalidArguments; else return Result::NonSupport; } #else TVGLOG("RENDERER", "FILE IO is disabled!"); return Result::NonSupport; #endif } Result Text::load(const char* name, const char* data, uint32_t size, const char* mimeType, bool copy) noexcept { if (!name || (size == 0 && data)) return Result::InvalidArguments; //unload font if (!data) { if (LoaderMgr::retrieve(LoaderMgr::font(name))) return Result::Success; return Result::InsufficientCondition; } if (!LoaderMgr::loader(name, data, size, mimeType, copy)) return Result::NonSupport; return Result::Success; } Result Text::unload(const char* filename) noexcept { #ifdef THORVG_FILE_IO_SUPPORT if (LoaderMgr::retrieve(filename)) return Result::Success; return Result::InsufficientCondition; #else TVGLOG("RENDERER", "FILE IO is disabled!"); return Result::NonSupport; #endif } Result Text::align(float x, float y) noexcept { to(this)->fm.align = {x, y}; PAINT(this)->mark(RenderUpdateFlag::Transform); return Result::Success; } Result Text::layout(float w, float h) noexcept { to(this)->layout(w, h); return Result::Success; } Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept { return to(this)->shape->fill(r, g, b); } Result Text::outline(float width, uint8_t r, uint8_t g, uint8_t b) noexcept { to(this)->outlineWidth = width; to(this)->shape->strokeFill(r, g, b); PAINT(this)->mark(RenderUpdateFlag::Stroke); return Result::Success; } Result Text::fill(Fill* f) noexcept { return to(this)->shape->fill(f); } Result Text::italic(float shear) noexcept { if (shear < 0.0f) shear = 0.0f; else if (shear > 0.5f) shear = 0.5f; to(this)->italicShear = shear; to(this)->updated = true; return Result::Success; } Result Text::spacing(float letter, float line) noexcept { return to(this)->spacing(letter, line); } Result Text::wrap(TextWrap mode) noexcept { to(this)->wrapping(mode); return Result::Success; } Result Text::metrics(TextMetrics& metrics) const noexcept { return to(this)->metrics(metrics); } Text* Text::gen() noexcept { return new TextImpl; } Type Text::type() const noexcept { return Type::Text; } loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int16array.inc.h000664 001750 001750 00000001711 15164251010 052775 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Int16Array description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 2 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_INT16_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_INT16ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/bindings/capi/000775 001750 001750 00000000000 15164251010 032515 5ustar00ddennedyddennedy000000 000000 lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint32array-prototype.cpp000664 001750 001750 00000002226 15164251010 055010 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-uint32array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID uint32array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup uint32arrayprototype ECMA Uint32Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test2.lot000664 001750 001750 00001460267 15164251010 034007 0ustar00ddennedyddennedy000000 000000 { "v": "4.8.0", "meta": { "g": "LottieFiles AE 3.5.8", "a": "", "k": "", "d": "", "tc": "" }, "fr": 30, "ip": 0, "op": 90, "w": 1000, "h": 1000, "nm": "door_lock v2", "ddd": 0, "assets": [ { "id": "comp_0", "layers": [ { "ddd": 0, "ind": 1, "ty": 4, "nm": "Layer 14 Outlines", "parent": 2, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 34.051, 33.786, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 11.147, 11.102, 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": [ [ 5.6, 0.067 ], [ 0.041, 5.685 ], [ -5.729, 0.115 ], [ 0.212, -5.836 ] ], "o": [ [ -5.647, -0.067 ], [ -0.042, -5.704 ], [ 5.907, -0.118 ], [ -0.205, 5.631 ] ], "v": [ [ -0.238, 10.784 ], [ -10.855, 0.048 ], [ -0.318, -10.734 ], [ 10.685, 0.315 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.858823597431, 0.913725554943, 0.933333396912, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 11.147, 11.102 ], "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, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 45, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 2, "ty": 4, "nm": "Layer 16 Outlines", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 748.902, 421.903, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 33.971, 33.779, 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": [ [ -5.647, -0.067 ], [ -0.205, 5.631 ], [ 5.907, -0.118 ], [ -0.042, -5.704 ] ], "o": [ [ 5.6, 0.067 ], [ 0.212, -5.836 ], [ -5.729, 0.115 ], [ 0.041, 5.685 ] ], "v": [ [ -0.158, 10.792 ], [ 10.765, 0.323 ], [ -0.238, -10.726 ], [ -10.775, 0.056 ] ], "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": [ [ 18.438, -0.076 ], [ 0.207, 18.124 ], [ -18.435, 0.037 ], [ -0.218, -18.084 ] ], "o": [ [ -18.16, 0.076 ], [ -0.212, -18.489 ], [ 18.172, -0.036 ], [ 0.222, 18.428 ] ], "v": [ [ 0.09, 33.452 ], [ -33.509, 0.436 ], [ -0.134, -33.492 ], [ 33.499, -0.484 ] ], "c": true }, "ix": 2 }, "nm": "Path 2", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 4, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.086, 0.264, 0.221, 0.5, 0.227, 0.628, 0.497, 1, 0.368, 0.991, 0.773 ], "ix": 8 } }, "s": { "a": 0, "k": [ -32.412, -9.478 ], "ix": 4 }, "e": { "a": 0, "k": [ 5.257, 49.858 ], "ix": 5 }, "t": 2, "h": { "a": 0, "k": 0, "ix": 6 }, "a": { "a": 0, "k": 0, "ix": 7 }, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 33.971, 33.779 ], "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, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 45, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 3, "ty": 4, "nm": "Layer 15 Outlines", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 749.29, 361.18, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 59.949, 25.388, 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.761, -3.374 ], [ 3.439, -1.118 ], [ 3.606, 1.755 ], [ 9.463, 2.666 ], [ 14.915, -12.914 ], [ 3.381, 0.409 ], [ 1.162, 3.22 ], [ -2.295, 2.587 ], [ -28.413, -29.815 ] ], "o": [ [ -0.489, 4.126 ], [ -3.421, 1.112 ], [ -9.064, -4.412 ], [ -18.976, -5.347 ], [ -2.916, 2.525 ], [ -3.373, -0.407 ], [ -1.126, -3.117 ], [ 27.264, -30.747 ], [ 2.117, 2.222 ] ], "v": [ [ 58.199, 14.194 ], [ 50.823, 23.03 ], [ 38.874, 20.798 ], [ 12.165, 6.728 ], [ -38.851, 19.989 ], [ -49.598, 24.729 ], [ -58.572, 17.492 ], [ -56.177, 6.293 ], [ 54.021, 4.676 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 4, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.086, 0.264, 0.221, 0.5, 0.227, 0.628, 0.497, 1, 0.368, 0.991, 0.773 ], "ix": 8 } }, "s": { "a": 0, "k": [ -32.412, -9.478 ], "ix": 4 }, "e": { "a": 0, "k": [ 5.257, 49.858 ], "ix": 5 }, "t": 2, "h": { "a": 0, "k": 0, "ix": 6 }, "a": { "a": 0, "k": 0, "ix": 7 }, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 59.949, 25.388 ], "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, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 5, "op": 45, "st": 5, "bm": 0 }, { "ddd": 0, "ind": 4, "ty": 4, "nm": "Layer 17 Outlines", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 748.781, 324.32, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 92.236, 31.716, 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.017, -3.477 ], [ 3.012, -1.542 ], [ 3.41, 1.802 ], [ 1.885, 1.721 ], [ 38.278, -34.968 ], [ 0.868, -0.773 ], [ 4.826, 5.299 ], [ -5.89, 5.742 ], [ -21.793, 4.959 ], [ -32.604, -29.214 ] ], "o": [ [ 0.233, 4.273 ], [ -2.975, 1.523 ], [ -1.988, -1.051 ], [ -38.273, -34.931 ], [ -0.858, 0.784 ], [ -6.322, 5.632 ], [ -4.709, -5.17 ], [ 16.002, -15.601 ], [ 42.771, -9.734 ], [ 2.955, 2.647 ] ], "v": [ [ 89.736, 18.407 ], [ 83.938, 28.806 ], [ 73.559, 28.003 ], [ 67.71, 23.217 ], [ -67.606, 23.27 ], [ -70.145, 25.662 ], [ -87.277, 26.167 ], [ -85.688, 9.621 ], [ -29.066, -21.732 ], [ 84.092, 7.96 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 4, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.086, 0.264, 0.221, 0.5, 0.227, 0.628, 0.497, 1, 0.368, 0.991, 0.773 ], "ix": 8 } }, "s": { "a": 0, "k": [ -32.412, -9.478 ], "ix": 4 }, "e": { "a": 0, "k": [ 5.257, 49.858 ], "ix": 5 }, "t": 2, "h": { "a": 0, "k": 0, "ix": 6 }, "a": { "a": 0, "k": 0, "ix": 7 }, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 92.236, 31.716 ], "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, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 10, "op": 45, "st": 10, "bm": 0 }, { "ddd": 0, "ind": 5, "ty": 4, "nm": "Layer 18 Outlines", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 749.117, 289.409, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 124.326, 35.436, 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": [ [ -32.953, 0.244 ], [ -32.728, -31.835 ], [ 7.629, -3.944 ], [ 4.145, 4.028 ], [ 22.028, 6.493 ], [ 41.715, -36.318 ], [ 1.791, -1.48 ], [ 4.443, 4.653 ], [ -5.303, 4.356 ], [ -13.025, 7.862 ] ], "o": [ [ 41.717, 0.872 ], [ 6.766, 6.581 ], [ -5.783, 2.991 ], [ -16.454, -15.987 ], [ -53.105, -15.654 ], [ -1.754, 1.527 ], [ -5.258, 4.348 ], [ -4.443, -4.654 ], [ 11.817, -9.706 ], [ 24.984, -15.081 ] ], "v": [ [ 3.447, -35.186 ], [ 117.31, 13.145 ], [ 115.571, 32.194 ], [ 101.258, 28.609 ], [ 43.753, -5.803 ], [ -98.523, 25.624 ], [ -103.66, 30.348 ], [ -119.206, 29.804 ], [ -118.772, 14.377 ], [ -82.216, -13.329 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 4, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.086, 0.264, 0.221, 0.5, 0.227, 0.628, 0.497, 1, 0.368, 0.991, 0.773 ], "ix": 8 } }, "s": { "a": 0, "k": [ -32.412, -9.478 ], "ix": 4 }, "e": { "a": 0, "k": [ 5.257, 49.858 ], "ix": 5 }, "t": 2, "h": { "a": 0, "k": 0, "ix": 6 }, "a": { "a": 0, "k": 0, "ix": 7 }, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 124.325, 35.436 ], "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, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 15, "op": 45, "st": 15, "bm": 0 } ] } ], "layers": [ { "ddd": 0, "ind": 1, "ty": 4, "nm": "pad1", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 296.202, 240.008, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 20, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] }, { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 25, "s": [ { "i": [ [ -8.158, 0 ], [ 0, -8.158 ], [ 8.158, 0 ], [ 0, 8.158 ] ], "o": [ [ 8.158, 0 ], [ 0, 8.158 ], [ -8.158, 0 ], [ 0, -8.158 ] ], "v": [ [ -0.074, -14.595 ], [ 14.448, -0.074 ], [ -0.074, 14.448 ], [ -14.595, -0.074 ] ], "c": true } ] }, { "t": 30, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] } ], "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 1, "k": [ { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 20, "s": [ 2.191 ] }, { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 25, "s": [ 26.291 ] }, { "t": 30, "s": [ 2.191 ] } ], "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 20, "s": [ -6.784, -10.026 ], "to": [ 2.912, 1.444 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 25, "s": [ 10.687, -1.359 ], "to": [ 0, 0 ], "ti": [ 2.912, 1.444 ] }, { "t": 30, "s": [ -6.784, -10.026 ] } ], "ix": 4 }, "e": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 20, "s": [ -1.723, 9.732 ], "to": [ -2.587, -3.499 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 25, "s": [ -17.246, -11.259 ], "to": [ 0, 0 ], "ti": [ -2.587, -3.499 ] }, { "t": 30, "s": [ -1.723, 9.732 ] } ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.5, "y": 1 }, "o": { "x": 0, "y": 0 }, "t": 20, "s": [ -2.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "i": { "x": 1, "y": 1 }, "o": { "x": 0.5, "y": 0 }, "t": 25, "s": [ -31.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "t": 30, "s": [ -2.3, 26.799 ] } ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.96862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 2, "ty": 4, "nm": "pad2", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 394.475, 240.008, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2.191, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 0, "k": [ -6.784, -10.026 ], "ix": 4 }, "e": { "a": 0, "k": [ -1.723, 9.732 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 0, "k": [ -2.3, 26.799 ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.096862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 3, "ty": 4, "nm": "pad3", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 492.714, 240.008, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 40, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] }, { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 45, "s": [ { "i": [ [ -8.158, 0 ], [ 0, -8.158 ], [ 8.158, 0 ], [ 0, 8.158 ] ], "o": [ [ 8.158, 0 ], [ 0, 8.158 ], [ -8.158, 0 ], [ 0, -8.158 ] ], "v": [ [ -0.074, -14.595 ], [ 14.448, -0.074 ], [ -0.074, 14.448 ], [ -14.595, -0.074 ] ], "c": true } ] }, { "t": 50, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] } ], "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 1, "k": [ { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 40, "s": [ 2.191 ] }, { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 45, "s": [ 26.291 ] }, { "t": 50, "s": [ 2.191 ] } ], "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 40, "s": [ -6.784, -10.026 ], "to": [ 2.912, 1.444 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 45, "s": [ 10.687, -1.359 ], "to": [ 0, 0 ], "ti": [ 2.912, 1.444 ] }, { "t": 50, "s": [ -6.784, -10.026 ] } ], "ix": 4 }, "e": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 40, "s": [ -1.723, 9.732 ], "to": [ -2.587, -3.499 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 45, "s": [ -17.246, -11.259 ], "to": [ 0, 0 ], "ti": [ -2.587, -3.499 ] }, { "t": 50, "s": [ -1.723, 9.732 ] } ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.5, "y": 1 }, "o": { "x": 0, "y": 0 }, "t": 40, "s": [ -2.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "i": { "x": 1, "y": 1 }, "o": { "x": 0.5, "y": 0 }, "t": 45, "s": [ -31.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "t": 50, "s": [ -2.3, 26.799 ] } ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.96862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 4, "ty": 4, "nm": "pad4", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 296.202, 337.035, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2.191, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 0, "k": [ -6.784, -10.026 ], "ix": 4 }, "e": { "a": 0, "k": [ -1.723, 9.732 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 0, "k": [ -2.3, 26.799 ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.096862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 5, "ty": 4, "nm": "pad5", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 341.642, "ix": 10 }, "p": { "a": 0, "k": [ 394.475, 337.035, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 10, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] }, { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 15, "s": [ { "i": [ [ -8.158, 0 ], [ 0, -8.158 ], [ 8.158, 0 ], [ 0, 8.158 ] ], "o": [ [ 8.158, 0 ], [ 0, 8.158 ], [ -8.158, 0 ], [ 0, -8.158 ] ], "v": [ [ -0.074, -14.595 ], [ 14.448, -0.074 ], [ -0.074, 14.448 ], [ -14.595, -0.074 ] ], "c": true } ] }, { "t": 20, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] } ], "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 1, "k": [ { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 10, "s": [ 2.191 ] }, { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 15, "s": [ 26.291 ] }, { "t": 20, "s": [ 2.191 ] } ], "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 10, "s": [ -6.784, -10.026 ], "to": [ 2.912, 1.444 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 15, "s": [ 10.687, -1.359 ], "to": [ 0, 0 ], "ti": [ 2.912, 1.444 ] }, { "t": 20, "s": [ -6.784, -10.026 ] } ], "ix": 4 }, "e": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 10, "s": [ -1.723, 9.732 ], "to": [ -2.587, -3.499 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 15, "s": [ -17.246, -11.259 ], "to": [ 0, 0 ], "ti": [ -2.587, -3.499 ] }, { "t": 20, "s": [ -1.723, 9.732 ] } ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.5, "y": 1 }, "o": { "x": 0, "y": 0 }, "t": 10, "s": [ -2.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "i": { "x": 1, "y": 1 }, "o": { "x": 0.5, "y": 0 }, "t": 15, "s": [ -31.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "t": 20, "s": [ -2.3, 26.799 ] } ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.96862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 6, "ty": 4, "nm": "pad6", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 492.714, 337.035, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2.191, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 0, "k": [ -6.784, -10.026 ], "ix": 4 }, "e": { "a": 0, "k": [ -1.723, 9.732 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 0, "k": [ -2.3, 26.799 ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.096862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 7, "ty": 4, "nm": "pad7", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 296.202, 433.773, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 0, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] }, { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 5, "s": [ { "i": [ [ -8.158, 0 ], [ 0, -8.158 ], [ 8.158, 0 ], [ 0, 8.158 ] ], "o": [ [ 8.158, 0 ], [ 0, 8.158 ], [ -8.158, 0 ], [ 0, -8.158 ] ], "v": [ [ -0.074, -14.595 ], [ 14.448, -0.074 ], [ -0.074, 14.448 ], [ -14.595, -0.074 ] ], "c": true } ] }, { "t": 10, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] } ], "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 1, "k": [ { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 0, "s": [ 2.191 ] }, { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 5, "s": [ 26.291 ] }, { "t": 10, "s": [ 2.191 ] } ], "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 0, "s": [ -6.784, -10.026 ], "to": [ 2.912, 1.444 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 5, "s": [ 10.687, -1.359 ], "to": [ 0, 0 ], "ti": [ 2.912, 1.444 ] }, { "t": 10, "s": [ -6.784, -10.026 ] } ], "ix": 4 }, "e": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 0, "s": [ -1.723, 9.732 ], "to": [ -2.587, -3.499 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 5, "s": [ -17.246, -11.259 ], "to": [ 0, 0 ], "ti": [ -2.587, -3.499 ] }, { "t": 10, "s": [ -1.723, 9.732 ] } ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.5, "y": 1 }, "o": { "x": 0, "y": 0 }, "t": 0, "s": [ -2.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "i": { "x": 1, "y": 1 }, "o": { "x": 0.5, "y": 0 }, "t": 5, "s": [ -31.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "t": 10, "s": [ -2.3, 26.799 ] } ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.96862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 8, "ty": 4, "nm": "pad8", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 394.475, 433.773, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2.191, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 0, "k": [ -6.784, -10.026 ], "ix": 4 }, "e": { "a": 0, "k": [ -1.723, 9.732 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 0, "k": [ -2.3, 26.799 ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.096862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 9, "ty": 4, "nm": "pad9", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 492.714, 433.773, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 30, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] }, { "i": { "x": 0.12, "y": 0.58 }, "o": { "x": 0.88, "y": 0.42 }, "t": 35, "s": [ { "i": [ [ -8.158, 0 ], [ 0, -8.158 ], [ 8.158, 0 ], [ 0, 8.158 ] ], "o": [ [ 8.158, 0 ], [ 0, 8.158 ], [ -8.158, 0 ], [ 0, -8.158 ] ], "v": [ [ -0.074, -14.595 ], [ 14.448, -0.074 ], [ -0.074, 14.448 ], [ -14.595, -0.074 ] ], "c": true } ] }, { "t": 40, "s": [ { "i": [ [ -6.563, 0 ], [ 0, -6.563 ], [ 6.563, 0 ], [ 0, 6.563 ] ], "o": [ [ 6.563, 0 ], [ 0, 6.563 ], [ -6.563, 0 ], [ 0, -6.563 ] ], "v": [ [ 0, -11.683 ], [ 11.683, 0 ], [ 0, 11.683 ], [ -11.683, 0 ] ], "c": true } ] } ], "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 1, "k": [ { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 30, "s": [ 2.191 ] }, { "i": { "x": [ 0.12 ], "y": [ 0.929 ] }, "o": { "x": [ 0.88 ], "y": [ 0.071 ] }, "t": 35, "s": [ 26.291 ] }, { "t": 40, "s": [ 2.191 ] } ], "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.32, 0.5, 0.16, 1, 0 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 30, "s": [ -6.784, -10.026 ], "to": [ 2.912, 1.444 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 35, "s": [ 10.687, -1.359 ], "to": [ 0, 0 ], "ti": [ 2.912, 1.444 ] }, { "t": 40, "s": [ -6.784, -10.026 ] } ], "ix": 4 }, "e": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 30, "s": [ -1.723, 9.732 ], "to": [ -2.587, -3.499 ], "ti": [ 0, 0 ] }, { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 35, "s": [ -17.246, -11.259 ], "to": [ 0, 0 ], "ti": [ -2.587, -3.499 ] }, { "t": 40, "s": [ -1.723, 9.732 ] } ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.208, 0.996, 0.718, 1, 0.208, 0.996, 0.718, 0, 1, 0.5, 0.66, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ -0.018, -0.266 ], "ix": 5 }, "e": { "a": 0, "k": [ -0.928, 26.32 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.839215695858, 0.996078431606, 0.945098042488, 0.800000011921 ], "ix": 4 }, "o": { "a": 0, "k": 80, "ix": 5 }, "r": 1, "bm": 0, "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": "Inner Print", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 58.339, 58.339 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 2, "ix": 10 }, "g": { "p": 5, "k": { "a": 0, "k": [ 0, 0.141, 0.358, 0.3, 0.261, 0.226, 0.576, 0.483, 0.521, 0.312, 0.794, 0.666, 0.761, 0.235, 0.598, 0.501, 1, 0.158, 0.402, 0.337 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.5, "y": 1 }, "o": { "x": 0, "y": 0 }, "t": 30, "s": [ -2.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "i": { "x": 1, "y": 1 }, "o": { "x": 0.5, "y": 0 }, "t": 35, "s": [ -31.3, 26.799 ], "to": [ 0, 0 ], "ti": [ 0, 0 ] }, { "t": 40, "s": [ -2.3, 26.799 ] } ], "ix": 4 }, "e": { "a": 0, "k": [ 27.512, 23.572 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.5, 0.18, 0.486, 0.406, 1, 0, 0, 0, 0, 0.25, 0.5, 0.125, 1, 0 ], "ix": 9 } }, "s": { "a": 0, "k": [ -2.813, -47.219 ], "ix": 5 }, "e": { "a": 0, "k": [ 21.861, 13.823 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.380392163992, 0.96862745285, 0.811764717102, 0.159999996424 ], "ix": 4 }, "o": { "a": 0, "k": 16, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.133, -0.051 ], "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": 4, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 10, "ty": 4, "nm": "bar", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 1, "k": [ { "i": { "x": [ 0.1 ], "y": [ 1 ] }, "o": { "x": [ 0.9 ], "y": [ 0 ] }, "t": 60, "s": [ 0 ] }, { "i": { "x": [ 0.1 ], "y": [ 1 ] }, "o": { "x": [ 0.9 ], "y": [ 0 ] }, "t": 70, "s": [ 43 ] }, { "t": 80, "s": [ 0 ] } ], "ix": 10 }, "p": { "a": 0, "k": [ 385.481, 621.615, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 41.9, 82.869, 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": [ [ 22.626, 0 ], [ 0, 0 ], [ 0, 22.627 ], [ -22.626, 0 ], [ 0, 0 ], [ 0, -22.626 ] ], "o": [ [ 0, 0 ], [ -22.626, 0 ], [ 0, -22.626 ], [ 0, 0 ], [ 22.626, 0 ], [ 0, 22.627 ] ], "v": [ [ 213.224, 40.969 ], [ -213.225, 40.969 ], [ -254.192, 0 ], [ -213.225, -40.969 ], [ 213.224, -40.969 ], [ 254.193, 0 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 6.4, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.086, 0.264, 0.221, 0.5, 0.227, 0.628, 0.497, 1, 0.368, 0.991, 0.773 ], "ix": 8 } }, "s": { "a": 0, "k": [ 255.785, -65.331 ], "ix": 4 }, "e": { "a": 0, "k": [ -247.634, 64.163 ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.162, 0.777, 0.56, 1, 0.116, 0.558, 0.402 ], "ix": 9 } }, "s": { "a": 0, "k": [ 157.33, -12.543 ], "ix": 5 }, "e": { "a": 0, "k": [ -612.037, 233.426 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 296.092, 82.869 ], "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, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 11, "ty": 4, "nm": "Handle-Circle", "sr": 1, "ks": { "o": { "a": 0, "k": 32, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 327.241, 622.026, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -12.31, 0 ], [ 0, -12.31 ], [ 12.31, 0 ], [ 0, 12.31 ] ], "o": [ [ 12.31, 0 ], [ 0, 12.31 ], [ -12.31, 0 ], [ 0, -12.31 ] ], "v": [ [ 0, -21.912 ], [ 21.912, 0 ], [ 0, 21.912 ], [ -21.912, 0 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "rd", "nm": "Round Corners 1", "r": { "a": 0, "k": 8, "ix": 1 }, "ix": 2, "mn": "ADBE Vector Filter - RC", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.361, 0.973, 0.812, 0.284, 0.18, 0.486, 0.406, 0.567, 0, 0, 0, 0, 0.24, 0.284, 0.62, 0.567, 1 ], "ix": 9 } }, "s": { "a": 0, "k": [ 12.326, 43.735 ], "ix": 5 }, "e": { "a": 0, "k": [ 0, -27.48 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-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": "Ellipse 6436", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 12, "ty": 4, "nm": "Handle-Area", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 390.734, 622.024, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ty": "rc", "d": 1, "s": { "a": 0, "k": [ 222.305, 131.453 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "r": { "a": 0, "k": 120, "ix": 4 }, "nm": "Rectangle Path 1", "mn": "ADBE Vector Shape - Rect", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 5.478, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.38, 0.969, 0.812, 0.5, 0.19, 0.484, 0.406, 1, 0, 0, 0, 0, 0.5, 0.5, 0.25, 1, 0 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.833, "y": 0.833 }, "o": { "x": 0.167, "y": 0.167 }, "t": 60, "s": [ -11.953, 80.065 ], "to": [ -25.824, -9.311 ], "ti": [ 8.612, 22.746 ] }, { "i": { "x": 0.833, "y": 0.833 }, "o": { "x": 0.167, "y": 0.167 }, "t": 60, "s": [ -11.953, 80.065 ], "to": [ -25.824, -9.311 ], "ti": [ 8.612, 22.746 ] }, { "t": 89, "s": [ -63.623, -56.413 ] } ], "ix": 4 }, "e": { "a": 1, "k": [ { "i": { "x": 0.833, "y": 0.833 }, "o": { "x": 0.167, "y": 0.167 }, "t": 60, "s": [ -2.884, -74.535 ], "to": [ 18.507, 4.192 ], "ti": [ 2.212, -21.549 ] }, { "i": { "x": 0.833, "y": 0.833 }, "o": { "x": 0.167, "y": 0.167 }, "t": 60, "s": [ -2.884, -74.535 ], "to": [ 18.507, 4.192 ], "ti": [ 2.212, -21.549 ] }, { "t": 89, "s": [ -16.156, 54.76 ] } ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 0.380392163992, 0.96862745285, 0.811764717102, 0.159999996424 ], "ix": 3 }, "o": { "a": 0, "k": 16, "ix": 4 }, "w": { "a": 0, "k": 5.478, "ix": 5 }, "lc": 1, "lj": 1, "ml": 4, "bm": 0, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.208, 0.996, 0.718, 0.5, 0.104, 0.931, 0.71, 1, 0, 0.867, 0.702, 0, 0, 0.5, 0.16, 1, 0.32 ], "ix": 9 } }, "s": { "a": 0, "k": [ 93.381, -48.146 ], "ix": 5 }, "e": { "a": 0, "k": [ -204.584, 98.918 ], "ix": 6 }, "t": 1, "nm": "Gradient Fill 2", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -0.128, -0.682 ], "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": "Rectangle 19102", "np": 4, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": -59, "op": 126, "st": -59, "bm": 0 }, { "ddd": 0, "ind": 13, "ty": 4, "nm": "Inner", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 386.579, 467.246, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 115.9, 115.9, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ty": "rc", "d": 1, "s": { "a": 0, "k": [ 326.465, 570.551 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "r": { "a": 0, "k": 32, "ix": 4 }, "nm": "Rectangle Path 1", "mn": "ADBE Vector Shape - Rect", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 3.272, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.38, 0.969, 0.812, 0.5, 0.19, 0.484, 0.406, 1, 0, 0, 0, 0, 0.5, 0.5, 0.25, 1, 0 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.827, "y": 0.495 }, "o": { "x": 0.88, "y": 0.331 }, "t": 0, "s": [ 86.346, -243.003 ], "to": [ 36.639, 23.421 ], "ti": [ -15.048, 69.293 ] } ], "ix": 4 }, "e": { "a": 1, "k": [ { "i": { "x": 0.827, "y": 0.707 }, "o": { "x": 0.88, "y": 0.192 }, "t": 0, "s": [ 8.018, 187.979 ], "to": [ -38.331, -44.171 ], "ti": [ 13.568, -51.072 ] } ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "gf", "o": { "a": 0, "k": 100, "ix": 10 }, "r": 1, "bm": 0, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.05, 0.06, 0.057, 0.5, 0.025, 0.273, 0.225, 1, 0, 0.486, 0.393 ], "ix": 9 } }, "s": { "a": 0, "k": [ 125.195, -225.063 ], "ix": 5 }, "e": { "a": 0, "k": [ -821.18, 711.383 ], "ix": 6 }, "t": 2, "h": { "a": 0, "k": 7.772, "ix": 7 }, "a": { "a": 0, "k": 29.798, "ix": 8 }, "nm": "Gradient Fill 1", "mn": "ADBE Vector Graphic - G-Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 8.205, -0.413 ], "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": "Rectangle 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ty": "rc", "d": 1, "s": { "a": 0, "k": [ 326.465, 570.551 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "r": { "a": 0, "k": 32, "ix": 4 }, "nm": "Rectangle Path 1", "mn": "ADBE Vector Shape - Rect", "hd": false }, { "ty": "gs", "o": { "a": 0, "k": 100, "ix": 9 }, "w": { "a": 0, "k": 3.272, "ix": 10 }, "g": { "p": 3, "k": { "a": 0, "k": [ 0, 0.38, 0.969, 0.812, 0.5, 0.19, 0.639, 0.561, 1, 0, 0.31, 0.31 ], "ix": 8 } }, "s": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 0, "s": [ 15.048, -216.147 ], "to": [ 52.793, 52.374 ], "ti": [ 0, 0 ] } ], "ix": 4 }, "e": { "a": 1, "k": [ { "i": { "x": 0.12, "y": 0.86 }, "o": { "x": 0.88, "y": 0.14 }, "t": 0, "s": [ 8.021, 197.313 ], "to": [ -69.685, -22.712 ], "ti": [ 0, 0 ] } ], "ix": 5 }, "t": 1, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 13 }, "bm": 0, "nm": "Gradient Stroke 1", "mn": "ADBE Vector Graphic - G-Stroke", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0, 0, 0, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ -8.881, -0.413 ], "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": "Rectangle 2", "np": 3, "cix": 2, "bm": 0, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 90, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 14, "ty": 0, "nm": "wi-fi ", "refId": "comp_0", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 500, 500, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 500, 500, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "w": 1000, "h": 1000, "ip": 45, "op": 105, "st": 45, "bm": 0 }, { "ddd": 0, "ind": 15, "ty": 0, "nm": "wi-fi ", "refId": "comp_0", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 500, 500, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 500, 500, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "w": 1000, "h": 1000, "ip": 0, "op": 60, "st": 0, "bm": 0 } ], "markers": [] }src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/msinttypes/000775 001750 001750 00000000000 15164251010 037066 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieModel.cpp000664 001750 001750 00000054242 15164251010 036333 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgTaskScheduler.h" #include "tvgLottieModel.h" #include "tvgCompressor.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ Point LottieTextFollowPath::split(float dLen, float lenSearched, float& angle) { switch (*cmds) { case PathCommand::MoveTo: { angle = 0.0f; break; } case PathCommand::LineTo: { auto dp = *pts - *(pts - 1); angle = tvg::atan2(dp.y, dp.x); break; } case PathCommand::CubicTo: { auto bz = Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}; float t = bz.at(lenSearched - currentLen, dLen); angle = deg2rad(bz.angle(t)); return bz.at(t); } case PathCommand::Close: { auto dp = *start - *(pts - 1); angle = tvg::atan2(dp.y, dp.x); break; } } return {}; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ float LottieTextFollowPath::prepare(LottieMask* mask, float frameNo, float scale, Tween& tween, LottieExpressions* exps) { this->mask = mask; Matrix m{1.0f / scale, 0.0f, 0.0f, 0.0f, 1.0f / scale, 0.0f, 0.0f, 0.0f, 1.0f}; path.clear(); mask->pathset(frameNo, path, &m, tween, exps); pts = path.pts.data; cmds = path.cmds.data; cmdsCnt = path.cmds.count; totalLen = tvg::length(cmds, cmdsCnt, pts, path.pts.count); currentLen = 0.0f; start = pts; return firstMargin(frameNo, tween, exps) / scale; } Point LottieTextFollowPath::position(float lenSearched, float& angle) { //position before the start of the curve if (lenSearched <= 0.0f) { //shape is closed -> wrapping if (path.cmds.last() == PathCommand::Close) { while (lenSearched < 0.0f) lenSearched += totalLen; pts = path.pts.data; cmds = path.cmds.data; cmdsCnt = path.cmds.count; currentLen = 0.0f; //linear interpolation } else { if (cmds >= path.cmds.data + path.cmds.count - 1) return *start; switch (*(cmds + 1)) { case PathCommand::LineTo: { auto dp = *(pts + 1) - *pts; angle = tvg::atan2(dp.y, dp.x); return {pts->x + lenSearched * cos(angle), pts->y + lenSearched * sin(angle)}; } case PathCommand::CubicTo: { angle = deg2rad(Bezier{*pts, *(pts + 1), *(pts + 2), *(pts + 3)}.angle(0.0001f)); return {pts->x + lenSearched * cos(angle), pts->y + lenSearched * sin(angle)}; } default: angle = 0.0f; return *start; } } } auto shift = [&]() -> void { switch (*cmds) { case PathCommand::MoveTo: start = pts; ++pts; break; case PathCommand::LineTo: ++pts; break; case PathCommand::CubicTo: pts += 3; break; case PathCommand::Close: break; } ++cmds; --cmdsCnt; }; //position beyond the end of the curve if (lenSearched >= totalLen) { //shape is closed -> wrapping if (path.cmds.last() == PathCommand::Close) { while (lenSearched > totalLen) lenSearched -= totalLen; pts = path.pts.data; cmds = path.cmds.data; cmdsCnt = path.cmds.count; currentLen = 0.0f; //linear interpolation } else { while (cmdsCnt > 1) shift(); switch (*cmds) { case PathCommand::MoveTo: angle = 0.0f; return *pts; case PathCommand::LineTo: { auto len = lenSearched - totalLen; auto dp = *pts - *(pts - 1); angle = tvg::atan2(dp.y, dp.x); return {pts->x + len * cos(angle), pts->y + len * sin(angle)}; } case PathCommand::CubicTo: { auto len = lenSearched - totalLen; angle = deg2rad(Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.angle(0.999f)); return {(pts + 2)->x + len * cos(angle), (pts + 2)->y + len * sin(angle)}; } case PathCommand::Close: { auto len = lenSearched - totalLen; auto dp = *start - *(pts - 1); angle = tvg::atan2(dp.y, dp.x); return {(pts - 1)->x + len * cos(angle), (pts - 1)->y + len * sin(angle)}; } } } } //reset required if text partially crosses curve start if (lenSearched < currentLen) { pts = path.pts.data; cmds = path.cmds.data; cmdsCnt = path.cmds.count; currentLen = 0.0f; } auto length = [&]() -> float { switch (*cmds) { case PathCommand::MoveTo: return 0.0f; case PathCommand::LineTo: return tvg::length(*(pts - 1), *pts); case PathCommand::CubicTo: return Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length(); case PathCommand::Close: return tvg::length(*(pts - 1), *start); default: return 0.0f; } }; while (cmdsCnt > 0) { auto dLen = length(); if (currentLen + dLen < lenSearched) { shift(); currentLen += dLen; continue; } return split(dLen, lenSearched, angle); } return {}; } void LottieSlot::reset() { if (!overridden) return; ARRAY_FOREACH(pair, pairs) { pair->obj->override(pair->prop, true); delete(pair->prop); pair->prop = nullptr; } overridden = false; } void LottieSlot::apply(LottieProperty* prop, bool byDefault) { auto release = overridden || byDefault; //apply slot object to all targets ARRAY_FOREACH(pair, pairs) { auto backup = pair->obj->override(prop, release); if (!release) pair->prop = backup; } if (!byDefault) overridden = true; } float LottieTextRange::factor(float frameNo, float totalLen, float idx) { auto offset = this->offset(frameNo); auto start = this->start(frameNo) + offset; auto end = this->end(frameNo) + offset; if (random > 0) { auto range = end - start; auto len = (rangeUnit == Unit::Percent) ? 100.0f : totalLen; start = static_cast(random % int(len - range)); end = start + range; } auto divisor = (rangeUnit == Unit::Percent) ? (100.0f / totalLen) : 1.0f; start /= divisor; end /= divisor; auto f = 0.0f; switch (this->shape) { case Square: { auto smoothness = this->smoothness(frameNo); if (tvg::zero(smoothness)) f = idx >= nearbyintf(start) && idx < nearbyintf(end) ? 1.0f : 0.0f; else { if (idx >= std::floor(start)) { auto diff = idx - start; f = diff < 0.0f ? std::min(end, 1.0f) + diff : end - idx; } smoothness *= 0.01f; f = (f - (1.0f - smoothness) * 0.5f) / smoothness; } break; } case RampUp: { f = tvg::equal(start, end) ? (idx >= end ? 1.0f : 0.0f) : (0.5f + idx - start) / (end - start); break; } case RampDown: { f = tvg::equal(start, end) ? (idx >= end ? 0.0f : 1.0f) : 1.0f - (0.5f + idx - start) / (end - start); break; } case Triangle: { f = tvg::equal(start, end) ? 0.0f : 2.0f * (0.5f + idx - start) / (end - start); f = f < 1.0f ? f : 2.0f - f; break; } case Round: { idx = tvg::clamp(idx + (0.5f - start), 0.0f, end - start); auto range = 0.5f * (end - start); auto t = idx - range; f = tvg::equal(start, end) ? 0.0f : sqrtf(1.0f - t * t / (range * range)); break; } case Smooth: { idx = tvg::clamp(idx + (0.5f - start), 0.0f, end - start); f = tvg::equal(start, end) ? 0.0f : 0.5f * (1.0f + cosf(MATH_PI * (1.0f + 2.0f * idx / (end - start)))); break; } } f = tvg::clamp(f, 0.0f, 1.0f); //apply easing auto minEase = tvg::clamp(this->minEase(frameNo), -100.0f, 100.0f); auto maxEase = tvg::clamp(this->maxEase(frameNo), -100.0f, 100.0f); if (!tvg::zero(minEase) || !tvg::zero(maxEase)) { Point in{1.0f, 1.0f}; Point out{0.0f, 0.0f}; if (maxEase > 0.0f) in.x = 1.0f - maxEase * 0.01f; else in.y = 1.0f + maxEase * 0.01f; if (minEase > 0.0f) out.x = minEase * 0.01f; else out.y = -minEase * 0.01f; interpolator->set(nullptr, in, out); f = interpolator->progress(f); } f = tvg::clamp(f, 0.0f, 1.0f); return f * this->maxAmount(frameNo) * 0.01f; } void LottieFont::prepare() { if (!b64src) return; Text::load(name, b64src, size, "ttf", false); } void LottieImage::prepare() { LottieObject::type = LottieObject::Image; //Prepare the Picture image auto picture = Picture::gen(); auto result = (bitmap.size > 0) ? picture->load((const char*)bitmap.data, bitmap.size, bitmap.mimeType) : picture->load(bitmap.path); if (result == Result::Success) resolved = true; picture->size(bitmap.width, bitmap.height); bitmap.picture = picture; picture->ref(); } void LottieTrimpath::segment(float frameNo, float& start, float& end, Tween& tween, LottieExpressions* exps) { start = tvg::clamp(this->start(frameNo, tween, exps) * 0.01f, 0.0f, 1.0f); end = tvg::clamp(this->end(frameNo, tween, exps) * 0.01f, 0.0f, 1.0f); auto diff = fabs(start - end); if (tvg::zero(diff)) { start = 0.0f; end = 0.0f; return; } //Even if the start and end values do not cause trimming, an offset > 0 can still affect dashing starting point auto o = fmodf(this->offset(frameNo, tween, exps), 360.0f) / 360.0f; //0 ~ 1 if (tvg::zero(o) && diff >= 1.0f) { start = 0.0f; end = 1.0f; return; } if (start > end) std::swap(start, end); start += o; end += o; } uint32_t LottieGradient::populate(ColorStop& color, size_t count) { if (!color.input) return 0; uint32_t alphaCnt = (color.input->count - (count * 4)) / 2; Array output(count + alphaCnt); uint32_t cidx = 0; //color count uint32_t clast = count * 4; if (clast > color.input->count) clast = color.input->count; uint32_t aidx = clast; //alpha count Fill::ColorStop cs; //merge color stops. for (uint32_t i = 0; i < color.input->count; ++i) { if (cidx == clast || aidx == color.input->count) break; if ((*color.input)[cidx] == (*color.input)[aidx]) { cs.offset = (*color.input)[cidx]; cs.r = (uint8_t)nearbyint((*color.input)[cidx + 1] * 255.0f); cs.g = (uint8_t)nearbyint((*color.input)[cidx + 2] * 255.0f); cs.b = (uint8_t)nearbyint((*color.input)[cidx + 3] * 255.0f); cs.a = (uint8_t)nearbyint((*color.input)[aidx + 1] * 255.0f); cidx += 4; aidx += 2; } else if ((*color.input)[cidx] < (*color.input)[aidx]) { cs.offset = (*color.input)[cidx]; cs.r = (uint8_t)nearbyint((*color.input)[cidx + 1] * 255.0f); cs.g = (uint8_t)nearbyint((*color.input)[cidx + 2] * 255.0f); cs.b = (uint8_t)nearbyint((*color.input)[cidx + 3] * 255.0f); //generate alpha value if (output.count > 0) { auto p = ((*color.input)[cidx] - output.last().offset) / ((*color.input)[aidx] - output.last().offset); cs.a = tvg::lerp(output.last().a, (uint8_t)nearbyint((*color.input)[aidx + 1] * 255.0f), p); } else cs.a = (uint8_t)nearbyint((*color.input)[aidx + 1] * 255.0f); cidx += 4; } else { cs.offset = (*color.input)[aidx]; cs.a = (uint8_t)nearbyint((*color.input)[aidx + 1] * 255.0f); //generate color value if (output.count > 0) { auto p = ((*color.input)[aidx] - output.last().offset) / ((*color.input)[cidx] - output.last().offset); cs.r = tvg::lerp(output.last().r, (uint8_t)nearbyint((*color.input)[cidx + 1] * 255.0f), p); cs.g = tvg::lerp(output.last().g, (uint8_t)nearbyint((*color.input)[cidx + 2] * 255.0f), p); cs.b = tvg::lerp(output.last().b, (uint8_t)nearbyint((*color.input)[cidx + 3] * 255.0f), p); } else { cs.r = (uint8_t)nearbyint((*color.input)[cidx + 1] * 255.0f); cs.g = (uint8_t)nearbyint((*color.input)[cidx + 2] * 255.0f); cs.b = (uint8_t)nearbyint((*color.input)[cidx + 3] * 255.0f); } aidx += 2; } if (cs.a < 255) opaque = false; output.push(cs); } //color remains while (cidx + 3 < clast) { cs.offset = (*color.input)[cidx]; cs.r = (uint8_t)nearbyint((*color.input)[cidx + 1] * 255.0f); cs.g = (uint8_t)nearbyint((*color.input)[cidx + 2] * 255.0f); cs.b = (uint8_t)nearbyint((*color.input)[cidx + 3] * 255.0f); cs.a = (output.count > 0) ? output.last().a : 255; if (cs.a < 255) opaque = false; output.push(cs); cidx += 4; } //alpha remains while (aidx < color.input->count) { cs.offset = (*color.input)[aidx]; cs.a = (uint8_t)nearbyint((*color.input)[aidx + 1] * 255.0f); if (cs.a < 255) opaque = false; if (output.count > 0) { cs.r = output.last().r; cs.g = output.last().g; cs.b = output.last().b; } else cs.r = cs.g = cs.b = 255; output.push(cs); aidx += 2; } color.data = output.data; output.data = nullptr; color.input->reset(); delete(color.input); color.input = nullptr; return output.count; } Fill* LottieGradient::fill(float frameNo, uint8_t opacity, Tween& tween, LottieExpressions* exps) { if (opacity == 0) return nullptr; Fill* fill; auto s = start(frameNo, tween, exps); auto e = end(frameNo, tween, exps); //Linear Graident if (id == 1) { fill = LinearGradient::gen(); static_cast(fill)->linear(s.x, s.y, e.x, e.y); //Radial Gradient } else { fill = RadialGradient::gen(); auto w = fabsf(e.x - s.x); auto h = fabsf(e.y - s.y); auto r = (w > h) ? (w + 0.375f * h) : (h + 0.375f * w); auto progress = this->height(frameNo, tween, exps) * 0.01f; if (tvg::zero(progress)) { static_cast(fill)->radial(s.x, s.y, r, s.x, s.y, 0.0f); } else { auto startAngle = rad2deg(tvg::atan2(e.y - s.y, e.x - s.x)); auto angle = deg2rad((startAngle + this->angle(frameNo, tween, exps))); auto fx = s.x + cos(angle) * progress * r; auto fy = s.y + sin(angle) * progress * r; // Lottie doesn't have any focal radius concept static_cast(fill)->radial(s.x, s.y, r, fx, fy, 0.0f); } } colorStops(frameNo, fill, tween, exps); //multiply the current opacity with the fill if (opacity < 255) { const Fill::ColorStop* colorStops; auto cnt = fill->colorStops(&colorStops); for (uint32_t i = 0; i < cnt; ++i) { const_cast(&colorStops[i])->a = MULTIPLY(colorStops[i].a, opacity); } } return fill; } LottieGroup::LottieGroup() { reqFragment = false; buildDone = false; trimpath = false; visible = false; allowMerge = true; } LottieProperty* LottieGroup::property(uint16_t ix) { ARRAY_FOREACH(p, children) { auto child = static_cast(*p); if (auto property = child->property(ix)) return property; } return nullptr; } void LottieGroup::prepare(LottieObject::Type type) { LottieObject::type = type; if (children.count == 0) return; size_t strokeCnt = 0; size_t fillCnt = 0; ARRAY_REVERSE_FOREACH(c, children) { auto child = static_cast(*c); if (child->type == LottieObject::Type::Trimpath) trimpath = true; /* Figure out if this group is a simple path drawing. In that case, the rendering context can be sharable with the parent's. */ if (allowMerge && !child->mergeable()) allowMerge = false; //Figure out this group has visible contents switch (child->type) { case LottieObject::Group: { visible |= static_cast(child)->visible; break; } case LottieObject::Rect: case LottieObject::Ellipse: case LottieObject::Path: case LottieObject::Polystar: case LottieObject::Image: case LottieObject::Text: { visible = true; break; } default: break; } if (reqFragment) continue; /* Figure out if the rendering context should be fragmented. Multiple stroking or grouping with a stroking would occur this. This fragment resolves the overlapped stroke outlines. */ if (child->type == LottieObject::Group && !child->mergeable()) { if (strokeCnt > 0 || fillCnt > 0) reqFragment = true; } else if (child->type == LottieObject::SolidStroke || child->type == LottieObject::GradientStroke) { if (strokeCnt > 0) reqFragment = true; else ++strokeCnt; } else if (child->type == LottieObject::SolidFill || child->type == LottieObject::GradientFill) { if (fillCnt > 0) reqFragment = true; else ++fillCnt; } } //Reverse the drawing order if this group has a trimpath. if (!trimpath) return; for (uint32_t i = 0; i < children.count - 1; ) { auto child2 = children[i + 1]; if (!child2->mergeable() || child2->type == LottieObject::Transform) { i += 2; continue; } auto child = children[i]; if (!child->mergeable() || child->type == LottieObject::Transform) { i++; continue; } children[i] = child2; children[i + 1] = child; i++; } } LottieLayer::~LottieLayer() { //No need to free assets children because the Composition owns them. if (rid) children.clear(); ARRAY_FOREACH(p, masks) delete(*p); ARRAY_FOREACH(p, effects) delete(*p); delete(transform); tvg::free(name); } LottieProperty* LottieLayer::property(uint16_t ix) { if (transform) { if (auto property = transform->property(ix)) return property; } return LottieGroup::property(ix); } void LottieLayer::prepare(RGB32* color) { /* if layer is hidden, only useful data is its transform matrix. so force it to be a Null Layer and release all resource. */ if (hidden) { type = LottieLayer::Null; ARRAY_FOREACH(p, children) delete(*p); children.reset(); return; } //prepare the viewport clipper if (type == LottieLayer::Precomp) { auto clipper = Shape::gen(); clipper->appendRect(0.0f, 0.0f, w, h); clipper->ref(); statical.pooler.push(clipper); //prepare solid fill in advance if it is a layer type. } else if (color && type == LottieLayer::Solid) { auto solidFill = Shape::gen(); solidFill->appendRect(0, 0, static_cast(w), static_cast(h)); solidFill->fill(color->r, color->g, color->b); solidFill->ref(); statical.pooler.push(solidFill); } LottieGroup::prepare(LottieObject::Layer); } float LottieLayer::remap(LottieComposition* comp, float frameNo, LottieExpressions* exp) { if (timeRemap.frames || timeRemap.value >= 0.0f) { return comp->frameAtTime(timeRemap(frameNo, exp)); } return (frameNo - startFrame) / timeStretch; } bool LottieLayer::assign(const char* layer, uint32_t ix, const char* var, float val) { //find the target layer by name auto target = layerById(djb2Encode(layer)); if (!target) return false; //find the target property by ix auto property = target->property(ix); if (property && property->exp) return property->exp->assign(var, val); return false; } LottieComposition::~LottieComposition() { if (!initiated && root) Paint::rel(root->scene); delete(root); tvg::free(version); tvg::free(name); ARRAY_FOREACH(p, interpolators) { tvg::free((*p)->key); tvg::free(*p); } ARRAY_FOREACH(p, assets) delete(*p); ARRAY_FOREACH(p, fonts) delete(*p); ARRAY_FOREACH(p, slots) delete(*p); ARRAY_FOREACH(p, markers) delete(*p); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/png/tvgLodePng.h000664 001750 001750 00000020017 15164251010 034443 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* LodePNG version 20200306 Copyright (c) 2005-2020 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef _TVG_LODEPNG_H_ #define _TVG_LODEPNG_H_ #include /*The PNG color types (also used for raw image).*/ enum LodePNGColorType { LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/ LCT_RGB = 2, /*RGB: 8,16 bit*/ LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/ LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/ /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid byte value from 0 to 255 that could be present in an invalid PNG file header. Do not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use the valid color type names above, or numeric values like 1 or 7 when checking for particular disallowed color type byte values, or cast to integer to print it.*/ LCT_MAX_OCTET_VALUE = 255 }; /*Settings for zlib decompression*/ struct LodePNGDecompressSettings { /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */ unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/ /*use custom zlib decoder instead of built in one (default: null)*/ unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); /*use custom deflate decoder instead of built in one (default: null) if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/ unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); const void* custom_context; /*optional custom settings for custom functions*/ }; /* Color mode of an image. Contains all information required to decode the pixel bits to RGBA colors. This information is the same as used in the PNG file format, and is used both for PNG and raw image data in LodePNG. */ struct LodePNGColorMode { /*header (IHDR)*/ LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ /* palette (PLTE and tRNS) Dynamically allocated with the colors of the palette, including alpha. This field may not be allocated directly, use lodepng_color_mode_init first, then lodepng_palette_add per color to correctly initialize it (to ensure size of exactly 1024 bytes). The alpha channels must be set as well, set them to 255 for opaque images. When decoding, by default you can ignore this palette, since LodePNG already fills the palette colors in the pixels of the raw RGBA output. The palette is only supported for color type 3. */ unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/ size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/ /* transparent color key (tRNS) This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. For grayscale PNGs, r, g and b will all 3 be set to the same. When decoding, by default you can ignore this information, since LodePNG sets pixels with this key to transparent already in the raw RGBA output. The color key is only supported for color types 0 and 2. */ unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ unsigned key_r; /*red/grayscale component of color key*/ unsigned key_g; /*green component of color key*/ unsigned key_b; /*blue component of color key*/ }; /*Information about the PNG image, except pixels, width and height.*/ struct LodePNGInfo { /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ unsigned compression_method;/*compression method of the original file. Always 0.*/ unsigned filter_method; /*filter method of the original file*/ unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/ LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ }; /* Settings for the decoder. This contains settings for the PNG and the Zlib decoder, but not the Info settings from the Info structs. */ struct LodePNGDecoderSettings { LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */ unsigned ignore_crc; /*ignore CRC checksums*/ unsigned ignore_critical; /*ignore unknown critical chunks*/ unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters in string keys, etc... */ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ }; /*The settings, state and information for extended encoding and decoding.*/ struct LodePNGState { LodePNGDecoderSettings decoder; /*the decoding settings*/ LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ unsigned error; }; void lodepng_state_init(LodePNGState* state); void lodepng_state_cleanup(LodePNGState* state); unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize); unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize); #endif //_TVG_LODEPNG_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgCommon.h000664 001750 001750 00000006365 15164251010 036127 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_COMMON_H_ #define _TVG_WG_COMMON_H_ #include "tvgWgBindGroups.h" struct WgContext { // external webgpu handles WGPUInstance instance{}; WGPUAdapter adapter{}; WGPUDevice device{}; // common webgpu handles WGPUQueue queue{}; WGPUTextureFormat preferredFormat{}; // shared webgpu assets WGPUSampler samplerNearestRepeat{}; WGPUSampler samplerLinearRepeat{}; WGPUSampler samplerLinearMirror{}; WGPUSampler samplerLinearClamp{}; // bind groups layouts WgBindGroupLayouts layouts; void initialize(WGPUInstance instance, WGPUDevice device); void release(); // create common objects WGPUSampler createSampler(WGPUFilterMode filter, WGPUMipmapFilterMode mipmapFilter, WGPUAddressMode addrMode, uint16_t anisotropy = 1); WGPUTexture createTexture(uint32_t width, uint32_t height, WGPUTextureFormat format); WGPUTexture createTexStorage(uint32_t width, uint32_t height, WGPUTextureFormat format); WGPUTexture createTexAttachement(uint32_t width, uint32_t height, WGPUTextureFormat format, uint32_t sc); WGPUTextureView createTextureView(WGPUTexture texture); bool allocateTexture(WGPUTexture& texture, uint32_t width, uint32_t height, WGPUTextureFormat format, void* data); // release common objects void releaseTextureView(WGPUTextureView& textureView); void releaseTexture(WGPUTexture& texture); void releaseSampler(WGPUSampler& sampler); void releaseQueue(WGPUQueue& queue); // create buffer objects (return true, if buffer handle was changed) bool allocateBufferUniform(WGPUBuffer& buffer, const void* data, uint64_t size); bool allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint64_t size); bool allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, uint64_t size); // release buffer objects void releaseBuffer(WGPUBuffer& buffer); // command encoder WGPUCommandEncoder createCommandEncoder(); void submitCommandEncoder(WGPUCommandEncoder encoder); void releaseCommandEncoder(WGPUCommandEncoder& encoder); void submit(); bool invalid(); }; #endif // _TVG_WG_COMMON_H_ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint32array.cpp000664 001750 001750 00000004471 15164251010 052751 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-uint32array.inc.h" #define BUILTIN_UNDERSCORED_ID uint32array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup uint32array ECMA Uint32Array object built-in * @{ */ /** * Handle calling [[Call]] of Uint32Array * * @return ecma value */ ecma_value_t ecma_builtin_uint32array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_UINT32_ARRAY_REQUIRES_NEW); } /* ecma_builtin_uint32array_dispatch_call */ /** * Handle calling [[Construct]] of Uint32Array * * @return ecma value */ ecma_value_t ecma_builtin_uint32array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_UINT32_ARRAY); } /* ecma_builtin_uint32array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgMath.cpp000664 001750 001750 00000035577 15164251010 033433 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgArray.h" #define BEZIER_EPSILON 1e-2f /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static float _lineLengthApprox(const Point& pt1, const Point& pt2) { /* 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. */ Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; if (diff.x < 0) diff.x = -diff.x; if (diff.y < 0) diff.y = -diff.y; return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f); } static float _lineLength(const Point& pt1, const Point& pt2) { Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; return sqrtf(diff.x * diff.x + diff.y * diff.y); } template float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc) { Bezier left, right; auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end); auto chord = lineLengthFunc(cur.start, cur.end); if (fabsf(len - chord) > BEZIER_EPSILON) { cur.split(left, right); return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc); } return len; } template float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc) { auto biggest = 1.0f; auto smallest = 0.0f; auto t = 0.5f; //just in case to prevent an infinite loop if (at <= 0) return 0.0f; if (at >= length) return 1.0f; while (true) { auto right = bz; Bezier left; right.split(t, left); length = _bezLength(left, lineLengthFunc); if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < 1e-3f) { break; } if (length < at) { smallest = t; t = (t + biggest) * 0.5f; } else { biggest = t; t = (smallest + t) * 0.5f; } } return t; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ namespace tvg { uint8_t lerp(const uint8_t &start, const uint8_t &end, float t) { return static_cast(tvg::clamp(static_cast(start + (end - start) * t), 0, 255)); } float length(const PathCommand* cmds, uint32_t cmdsCnt, const Point* pts, uint32_t ptsCnt) { if (ptsCnt < 2) return 0.0f; auto start = pts; auto length = 0.0f; while (cmdsCnt-- > 0) { switch (*cmds) { case PathCommand::Close: { length += tvg::length(*(pts - 1), *start); break; } case PathCommand::MoveTo: { start = pts; ++pts; break; } case PathCommand::LineTo: { length += tvg::length(*(pts - 1), *pts); ++pts; break; } case PathCommand::CubicTo: { length += Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length(); pts += 3; break; } } ++cmds; } return length; } //https://en.wikipedia.org/wiki/Remez_algorithm float atan2(float y, float x) { if (y == 0.0f && x == 0.0f) return 0.0f; auto a = std::min(fabsf(x), fabsf(y)) / std::max(fabsf(x), fabsf(y)); auto s = a * a; auto r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a; if (fabsf(y) > fabsf(x)) r = 1.57079637f - r; if (x < 0) r = 3.14159274f - r; if (y < 0) return -r; return r; } bool inverse(const Matrix* m, Matrix* out) { auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) - m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) + m->e13 * (m->e21 * m->e32 - m->e22 * m->e31); auto invDet = 1.0f / det; if (std::isinf(invDet)) return false; out->e11 = (m->e22 * m->e33 - m->e32 * m->e23) * invDet; out->e12 = (m->e13 * m->e32 - m->e12 * m->e33) * invDet; out->e13 = (m->e12 * m->e23 - m->e13 * m->e22) * invDet; out->e21 = (m->e23 * m->e31 - m->e21 * m->e33) * invDet; out->e22 = (m->e11 * m->e33 - m->e13 * m->e31) * invDet; out->e23 = (m->e21 * m->e13 - m->e11 * m->e23) * invDet; out->e31 = (m->e21 * m->e32 - m->e31 * m->e22) * invDet; out->e32 = (m->e31 * m->e12 - m->e11 * m->e32) * invDet; out->e33 = (m->e11 * m->e22 - m->e21 * m->e12) * invDet; return true; } bool identity(const Matrix* m) { if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f || m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f || m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) { return false; } return true; } void rotate(Matrix* m, float degree) { if (degree == 0.0f) return; auto radian = degree / 180.0f * MATH_PI; auto cosVal = cosf(radian); auto sinVal = sinf(radian); m->e12 = m->e11 * -sinVal; m->e11 *= cosVal; m->e21 = m->e22 * sinVal; m->e22 *= cosVal; } Matrix operator*(const Matrix& lhs, const Matrix& rhs) { Matrix m; m.e11 = lhs.e11 * rhs.e11 + lhs.e12 * rhs.e21 + lhs.e13 * rhs.e31; m.e12 = lhs.e11 * rhs.e12 + lhs.e12 * rhs.e22 + lhs.e13 * rhs.e32; m.e13 = lhs.e11 * rhs.e13 + lhs.e12 * rhs.e23 + lhs.e13 * rhs.e33; m.e21 = lhs.e21 * rhs.e11 + lhs.e22 * rhs.e21 + lhs.e23 * rhs.e31; m.e22 = lhs.e21 * rhs.e12 + lhs.e22 * rhs.e22 + lhs.e23 * rhs.e32; m.e23 = lhs.e21 * rhs.e13 + lhs.e22 * rhs.e23 + lhs.e23 * rhs.e33; m.e31 = lhs.e31 * rhs.e11 + lhs.e32 * rhs.e21 + lhs.e33 * rhs.e31; m.e32 = lhs.e31 * rhs.e12 + lhs.e32 * rhs.e22 + lhs.e33 * rhs.e32; m.e33 = lhs.e31 * rhs.e13 + lhs.e32 * rhs.e23 + lhs.e33 * rhs.e33; return m; } bool operator==(const Matrix& lhs, const Matrix& rhs) { if (!tvg::equal(lhs.e11, rhs.e11) || !tvg::equal(lhs.e12, rhs.e12) || !tvg::equal(lhs.e13, rhs.e13) || !tvg::equal(lhs.e21, rhs.e21) || !tvg::equal(lhs.e22, rhs.e22) || !tvg::equal(lhs.e23, rhs.e23) || !tvg::equal(lhs.e31, rhs.e31) || !tvg::equal(lhs.e32, rhs.e32) || !tvg::equal(lhs.e33, rhs.e33)) { return false; } return true; } void operator*=(Point& pt, const Matrix& m) { auto tx = pt.x * m.e11 + pt.y * m.e12 + m.e13; auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23; pt.x = tx; pt.y = ty; } Point operator*(const Point& pt, const Matrix& m) { auto tx = pt.x * m.e11 + pt.y * m.e12 + m.e13; auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23; return {tx, ty}; } Point normal(const Point& p1, const Point& p2) { auto dir = p2 - p1; auto len = length(dir); if (tvg::zero(len)) return {}; auto unitDir = dir / len; return {-unitDir.y, unitDir.x}; } void normalize(Point& pt) { if (zero(pt)) return; //prevent zero division auto ilength = 1.0f / sqrtf((pt.x * pt.x) + (pt.y * pt.y)); pt.x *= ilength; pt.y *= ilength; } float Line::length() const { return _lineLength(pt1, pt2); } void Line::split(float at, Line& left, Line& right) const { auto len = length(); auto dx = ((pt2.x - pt1.x) / len) * at; auto dy = ((pt2.y - pt1.y) / len) * at; left.pt1 = pt1; left.pt2.x = left.pt1.x + dx; left.pt2.y = left.pt1.y + dy; right.pt1 = left.pt2; right.pt2 = pt2; } Bezier::Bezier(const Point& st, const Point& ed, float radius) { // Calculate the angle between the start and end points auto angle = tvg::atan2(ed.y - st.y, ed.x - st.x); // Calculate the control points of the cubic bezier curve auto c = radius * PATH_KAPPA; // c = radius * (4/3) * tan(pi/8) start = {st.x, st.y}; ctrl1 = {st.x + radius * cos(angle), st.y + radius * sin(angle)}; ctrl2 = {ed.x - c * cos(angle), ed.y - c * sin(angle)}; end = {ed.x, ed.y}; } void Bezier::split(Bezier& left, Bezier& right) const { auto c = (ctrl1.x + ctrl2.x) * 0.5f; left.ctrl1.x = (start.x + ctrl1.x) * 0.5f; right.ctrl2.x = (ctrl2.x + end.x) * 0.5f; left.start.x = start.x; right.end.x = end.x; left.ctrl2.x = (left.ctrl1.x + c) * 0.5f; right.ctrl1.x = (right.ctrl2.x + c) * 0.5f; left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f; c = (ctrl1.y + ctrl2.y) * 0.5f; left.ctrl1.y = (start.y + ctrl1.y) * 0.5f; right.ctrl2.y = (ctrl2.y + end.y) * 0.5f; left.start.y = start.y; right.end.y = end.y; left.ctrl2.y = (left.ctrl1.y + c) * 0.5f; right.ctrl1.y = (right.ctrl2.y + c) * 0.5f; left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f; } void Bezier::split(float at, Bezier& left, Bezier& right) const { right = *this; auto t = right.at(at, right.length()); right.split(t, left); } float Bezier::length() const { return _bezLength(*this, _lineLength); } float Bezier::lengthApprox() const { return _bezLength(*this, _lineLengthApprox); } void Bezier::split(float t, Bezier& left) { left.start = start; left.ctrl1.x = start.x + t * (ctrl1.x - start.x); left.ctrl1.y = start.y + t * (ctrl1.y - start.y); left.ctrl2.x = ctrl1.x + t * (ctrl2.x - ctrl1.x); //temporary holding spot left.ctrl2.y = ctrl1.y + t * (ctrl2.y - ctrl1.y); //temporary holding spot ctrl2.x = ctrl2.x + t * (end.x - ctrl2.x); ctrl2.y = ctrl2.y + t * (end.y - ctrl2.y); ctrl1.x = left.ctrl2.x + t * (ctrl2.x - left.ctrl2.x); ctrl1.y = left.ctrl2.y + t * (ctrl2.y - left.ctrl2.y); left.ctrl2.x = left.ctrl1.x + t * (left.ctrl2.x - left.ctrl1.x); left.ctrl2.y = left.ctrl1.y + t * (left.ctrl2.y - left.ctrl1.y); left.end.x = start.x = left.ctrl2.x + t * (ctrl1.x - left.ctrl2.x); left.end.y = start.y = left.ctrl2.y + t * (ctrl1.y - left.ctrl2.y); } float Bezier::at(float at, float length) const { return _bezAt(*this, at, length, _lineLength); } float Bezier::atApprox(float at, float length) const { return _bezAt(*this, at, length, _lineLengthApprox); } Point Bezier::at(float t) const { Point cur; auto it = 1.0f - t; auto ax = start.x * it + ctrl1.x * t; auto bx = ctrl1.x * it + ctrl2.x * t; auto cx = ctrl2.x * it + end.x * t; ax = ax * it + bx * t; bx = bx * it + cx * t; cur.x = ax * it + bx * t; float ay = start.y * it + ctrl1.y * t; float by = ctrl1.y * it + ctrl2.y * t; float cy = ctrl2.y * it + end.y * t; ay = ay * it + by * t; by = by * it + cy * t; cur.y = ay * it + by * t; return cur; } float Bezier::angle(float t) const { if (t < 0 || t > 1) return 0; //derivate // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * // t^2) * p2 + t^2 * p3) float mt = 1.0f - t; float d = t * t; float a = -mt * mt; float b = 1 - 4 * t + 3 * d; float c = 2 * t - 3 * d; Point pt ={a * start.x + b * ctrl1.x + c * ctrl2.x + d * end.x, a * start.y + b * ctrl1.y + c * ctrl2.y + d * end.y}; pt.x *= 3; pt.y *= 3; return rad2deg(tvg::atan2(pt.y, pt.x)); } void Bezier::bounds(BBox& box, const Point& start, const Point& ctrl1, const Point& ctrl2, const Point& end) { if (box.min.x > start.x) box.min.x = start.x; if (box.min.y > start.y) box.min.y = start.y; if (box.min.x > end.x) box.min.x = end.x; if (box.min.y > end.y) box.min.y = end.y; if (box.max.x < start.x) box.max.x = start.x; if (box.max.y < start.y) box.max.y = start.y; if (box.max.x < end.x) box.max.x = end.x; if (box.max.y < end.y) box.max.y = end.y; //find x/y-direction extrema (solving derivative of Bezier curve) auto findMinMax = [&](float start, float ctrl1, float ctrl2, float end, float& min, float& max) -> void { auto a = -1.0f * start + 3.0f * ctrl1 - 3.0f * ctrl2 + end; auto b = start - 2.0f * ctrl1 + ctrl2; auto c = -1.0f * start + ctrl1; auto h = b * b - a * c; if (h <= 0.0f) return; h = sqrtf(h); float t[2] = {(-b - h) / a, (-b + h) / a}; for (int i = 0; i < 2; ++i) { if (t[i] <= 0.0f || t[i] >= 1.0f) continue; auto s = 1.0f - t[i]; auto q = s * s * s * start + 3.0f * s * s * t[i] * ctrl1 + 3.0f * s * t[i] * t[i] * ctrl2 + t[i] * t[i] * t[i] * end; if (q < min) min = q; if (q > max) max = q; } }; findMinMax(start.x, ctrl1.x, ctrl2.x, end.x, box.min.x, box.max.x); findMinMax(start.y, ctrl1.y, ctrl2.y, end.y, box.min.y, box.max.y); } bool Bezier::flatten() const { float diff1_x = fabsf((ctrl1.x * 3.f) - (start.x * 2.f) - end.x); float diff1_y = fabsf((ctrl1.y * 3.f) - (start.y * 2.f) - end.y); float diff2_x = fabsf((ctrl2.x * 3.f) - (end.x * 2.f) - start.x); float diff2_y = fabsf((ctrl2.y * 3.f) - (end.y * 2.f) - start.y); if (diff1_x < diff2_x) diff1_x = diff2_x; if (diff1_y < diff2_y) diff1_y = diff2_y; return (diff1_x + diff1_y <= 0.5f); } uint32_t Bezier::segments() const { static constexpr uint32_t MaxSegments = 1u << 10; // 2^10 segments cap keeps runtime bounded uint32_t count = 0; Array stack; stack.push(*this); Bezier left, right; while (!stack.empty()) { auto current = stack.last(); stack.pop(); if (current.flatten()) { ++count; continue; } if (count + stack.count + 2 >= MaxSegments) { TVGERR("Common", "Bezier segments count exceeded the maximum limit."); return MaxSegments; } current.split(left, right); stack.push(left); stack.push(right); } return count; } Bezier Bezier::operator*(const Matrix& m) { return Bezier{start * m, ctrl1* m, ctrl2 * m, end * m}; } }mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgPicture.h000664 001750 001750 00000022762 15164251010 034130 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_PICTURE_H_ #define _TVG_PICTURE_H_ #include "tvgPaint.h" #include "tvgScene.h" #include "tvgLoader.h" namespace tvg { struct PictureIterator : Iterator { Paint* paint = nullptr; Paint* ptr = nullptr; PictureIterator(Paint* p) : paint(p) {} const Paint* next() override { if (!ptr) ptr = paint; else ptr = nullptr; return ptr; } uint32_t count() override { if (paint) return 1; else return 0; } void begin() override { ptr = nullptr; } }; struct PictureImpl : Picture { Paint::Impl impl; ImageLoader* loader = nullptr; Paint* vector = nullptr; //vector picture uses RenderSurface* bitmap = nullptr; //bitmap picture uses AssetResolver* resolver = nullptr; Point origin = {}; float w = 0, h = 0; bool resizing = false; PictureImpl() : impl(Paint::Impl(this)) { } ~PictureImpl() { LoaderMgr::retrieve(loader); tvg::free(resolver); if (vector) vector->unref(); } bool skip(RenderUpdateFlag flag) { if (flag == RenderUpdateFlag::None) return true; return false; } bool update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) { load(); auto pivot = Point{-origin.x * float(w), -origin.y * float(h)}; if (bitmap) { //Overriding Transformation by the desired image size auto sx = w / loader->w; auto sy = h / loader->h; auto scale = sx < sy ? sx : sy; auto m = transform * Matrix{scale, 0, pivot.x, 0, scale, pivot.y, 0, 0, 1}; impl.rd = renderer->prepare(bitmap, impl.rd, m, clips, opacity, flag); } else if (vector) { if (resizing) { loader->resize(vector, w, h); resizing = false; } needComposition(opacity); vector->blend(pImpl->blendMethod); //propagate blend method to nested vector scene translateR(const_cast(&transform), pivot); return vector->pImpl->update(renderer, transform, clips, opacity, flag, false); } return true; } void size(float w, float h) { this->w = w; this->h = h; resizing = true; } Result size(float* w, float* h) const { if (!loader) return Result::InsufficientCondition; if (w) *w = this->w; if (h) *h = this->h; return Result::Success; } bool intersects(const RenderRegion& region) { if (!impl.renderer) return false; load(); if (impl.rd) return impl.renderer->intersectsImage(impl.rd, region); else if (vector) return to(vector)->intersects(region); return false; } bool bounds(Point* pt4, const Matrix& m, TVG_UNUSED bool obb) { pt4[0] = Point{0.0f, 0.0f} * m; pt4[1] = Point{w, 0.0f} * m; pt4[2] = Point{w, h} * m; pt4[3] = Point{0.0f, h} * m; return true; } Result load(const char* filename) { if (vector || bitmap) return Result::InsufficientCondition; bool invalid; //Invalid Path auto loader = static_cast(LoaderMgr::loader(filename, &invalid)); if (!loader) { if (invalid) return Result::InvalidArguments; return Result::NonSupport; } return load(loader); } Result load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy) { if (!data || size <= 0) return Result::InvalidArguments; if (vector || bitmap) return Result::InsufficientCondition; auto loader = static_cast(LoaderMgr::loader(data, size, mimeType, rpath, copy)); if (!loader) return Result::NonSupport; return load(loader); } Result load(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy) { if (!data || w <= 0 || h <= 0 || cs == ColorSpace::Unknown) return Result::InvalidArguments; if (vector || bitmap) return Result::InsufficientCondition; auto loader = static_cast(LoaderMgr::loader(data, w, h, cs, copy)); if (!loader) return Result::FailedAllocation; return load(loader); } Result set(std::function resolver, void* data) { if (loader) return Result::InsufficientCondition; if (!resolver) { tvg::free(this->resolver); this->resolver = nullptr; return Result::Success; } if (!this->resolver) this->resolver = tvg::calloc(1, sizeof(AssetResolver)); *(this->resolver) = {resolver, data}; return Result::Success; } Paint* duplicate(Paint* ret) { if (ret) TVGERR("RENDERER", "TODO: duplicate()"); load(); auto picture = Picture::gen(); auto dup = to(picture); if (vector) { dup->vector = vector->duplicate(); PAINT(dup->vector)->parent = picture; } if (loader) { dup->loader = loader; ++dup->loader->sharing; PAINT(picture)->mark(RenderUpdateFlag::Image); } dup->bitmap = bitmap; dup->origin = origin; dup->w = w; dup->h = h; dup->resizing = resizing; return picture; } Iterator* iterator() { load(); return new PictureIterator(vector); } uint32_t* data(uint32_t* w, uint32_t* h) { //Try it, If not loaded yet. load(); if (loader) { if (w) *w = static_cast(loader->w); if (h) *h = static_cast(loader->h); } else { if (w) *w = 0; if (h) *h = 0; } if (bitmap) return bitmap->buf32; else return nullptr; } void load() { if (loader) { if (vector) { loader->sync(); } else if ((vector = loader->paint())) { vector->ref(); PAINT(vector)->parent = this; if (w != loader->w || h != loader->h) { if (!resizing) { w = loader->w; h = loader->h; } loader->resize(vector, w, h); resizing = false; } } else if (!bitmap) { bitmap = loader->bitmap(); } } } void needComposition(uint8_t opacity) { impl.cmpFlag = CompositionFlag::Invalid; //must clear after the rendering //In this case, paint(scene) would try composition itself. if (opacity < 255) return; //Composition test const Paint* target; PAINT(this)->mask(&target); if (!target || target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return; impl.mark(CompositionFlag::Opacity); } bool render(RenderMethod* renderer) { auto ret = true; if (bitmap) { renderer->blend(impl.blendMethod); return renderer->renderImage(impl.rd); } else if (vector) { RenderCompositor* cmp = nullptr; if (impl.cmpFlag) { cmp = renderer->target(bounds(), renderer->colorSpace(), impl.cmpFlag); renderer->beginComposite(cmp, MaskMethod::None, 255); } ret = vector->pImpl->render(renderer); if (cmp) renderer->endComposite(cmp); } return ret; } RenderRegion bounds() { if (vector) return vector->pImpl->bounds(); return impl.renderer->region(impl.rd); } Result load(ImageLoader* loader) { //Same resource has been loaded. if (this->loader == loader) { this->loader->sharing--; //make it sure the reference counting. return Result::Success; } else if (this->loader) { LoaderMgr::retrieve(this->loader); } this->loader = loader; loader->set(resolver); if (!loader->read()) return Result::Unknown; this->w = loader->w; this->h = loader->h; impl.mark(RenderUpdateFlag::All); return Result::Success; } }; } #endif //_TVG_PICTURE_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testSavers.cpp000664 001750 001750 00000004373 15164251010 033052 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; TEST_CASE("Saver Creation", "[tvgSavers]") { auto saver = unique_ptr(Saver::gen()); REQUIRE(saver); } #if defined(THORVG_GIF_SAVER_SUPPORT) && defined(THORVG_LOTTIE_LOADER_SUPPORT) TEST_CASE("Save a lottie into gif", "[tvgSavers]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = Animation::gen(); auto picture = animation->picture(); REQUIRE(picture->load(TEST_DIR"/test.lot") == Result::Success); REQUIRE(picture->size(100, 100) == Result::Success); auto bg = Shape::gen(); REQUIRE(bg->fill(255, 255, 255) == Result::Success); REQUIRE(bg->appendRect(0, 0, 100, 100) == Result::Success); auto saver = unique_ptr(Saver::gen()); REQUIRE(saver); REQUIRE(saver->background(bg) == Result::Success); REQUIRE(saver->save(animation, TEST_DIR"/test.gif") == Result::Success); REQUIRE(saver->sync() == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } #endifthorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-object.inc.h000664 001750 001750 00000006211 15164251010 050057 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Object built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.2.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_OBJECT_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_GET_PROTOTYPE_OF_UL, ECMA_OBJECT_ROUTINE_GET_PROTOTYPE_OF, 1, 1) ROUTINE (LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_NAMES, 1, 1) ROUTINE (LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS, 1, 1) ROUTINE (LIT_MAGIC_STRING_SEAL, ECMA_OBJECT_ROUTINE_SEAL, 1, 1) ROUTINE (LIT_MAGIC_STRING_FREEZE, ECMA_OBJECT_ROUTINE_FREEZE, 1, 1) ROUTINE (LIT_MAGIC_STRING_PREVENT_EXTENSIONS_UL, ECMA_OBJECT_ROUTINE_PREVENT_EXTENSIONS, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_SEALED_UL, ECMA_OBJECT_ROUTINE_IS_SEALED, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_FROZEN_UL, ECMA_OBJECT_ROUTINE_IS_FROZEN, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_EXTENSIBLE, ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE, 1, 1) ROUTINE (LIT_MAGIC_STRING_ENTRIES, ECMA_OBJECT_ROUTINE_ENTRIES, 1, 1) ROUTINE (LIT_MAGIC_STRING_VALUES, ECMA_OBJECT_ROUTINE_VALUES, 1, 1) ROUTINE (LIT_MAGIC_STRING_KEYS, ECMA_OBJECT_ROUTINE_KEYS, 1, 1) ROUTINE (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR, 2, 2) ROUTINE (LIT_MAGIC_STRING_HAS_OWN_UL, ECMA_OBJECT_ROUTINE_HAS_OWN, 2, 2) ROUTINE (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTORS, 1, 1) ROUTINE (LIT_MAGIC_STRING_OBJECT_FROM_ENTRIES, ECMA_OBJECT_ROUTINE_FROM_ENTRIES, 1, 1) ROUTINE (LIT_MAGIC_STRING_CREATE, ECMA_OBJECT_ROUTINE_CREATE, 2, 2) ROUTINE (LIT_MAGIC_STRING_DEFINE_PROPERTIES_UL, ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES, 2, 2) ROUTINE (LIT_MAGIC_STRING_DEFINE_PROPERTY_UL, ECMA_OBJECT_ROUTINE_DEFINE_PROPERTY, 3, 3) ROUTINE (LIT_MAGIC_STRING_SET_PROTOTYPE_OF_UL, ECMA_OBJECT_ROUTINE_SET_PROTOTYPE_OF, 2, 2) ROUTINE (LIT_MAGIC_STRING_ASSIGN, ECMA_OBJECT_ROUTINE_ASSIGN, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_IS, ECMA_OBJECT_ROUTINE_IS, 2, 2) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/bit_reader.cpp000664 001750 001750 00000015042 15164251010 036261 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Boolean decoder non-inlined methods // // Author: Skal (pascal.massimino@gmail.com) #ifdef HAVE_CONFIG_H #include "../webp/config.h" #endif #include "./bit_reader_inl.h" #include "./utils.h" //------------------------------------------------------------------------------ // VP8BitReader void VP8InitBitReader(VP8BitReader* const br, const uint8_t* const start, const uint8_t* const end) { assert(br != NULL); assert(start != NULL); assert(start <= end); br->range_ = 255 - 1; br->buf_ = start; br->buf_end_ = end; br->value_ = 0; br->bits_ = -8; // to load the very first 8bits br->eof_ = 0; VP8LoadNewBytes(br); } void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) { if (br->buf_ != NULL) { br->buf_ += offset; br->buf_end_ += offset; } } const uint8_t kVP8Log2Range[128] = { 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 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, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; // range = ((range - 1) << kVP8Log2Range[range]) + 1 const uint8_t kVP8NewRange[128] = { 127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239, 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, 247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 127 }; void VP8LoadFinalBytes(VP8BitReader* const br) { assert(br != NULL && br->buf_ != NULL); // Only read 8bits at a time if (br->buf_ < br->buf_end_) { br->bits_ += 8; br->value_ = (bit_t)(*br->buf_++) | (br->value_ << 8); } else if (!br->eof_) { br->value_ <<= 8; br->bits_ += 8; br->eof_ = 1; } else { br->bits_ = 0; // This is to avoid undefined behaviour with shifts. } } //------------------------------------------------------------------------------ // Higher-level calls uint32_t VP8GetValue(VP8BitReader* const br, int bits) { uint32_t v = 0; while (bits-- > 0) { v |= VP8GetBit(br, 0x80) << bits; } return v; } int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) { const int value = VP8GetValue(br, bits); return VP8Get(br) ? -value : value; } //------------------------------------------------------------------------------ // VP8LBitReader #define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits. #if (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \ defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64__) || defined(_M_X64)) #define VP8L_USE_FAST_LOAD #endif static const uint32_t kBitMask[VP8L_MAX_NUM_BIT_READ + 1] = { 0, 0x000001, 0x000003, 0x000007, 0x00000f, 0x00001f, 0x00003f, 0x00007f, 0x0000ff, 0x0001ff, 0x0003ff, 0x0007ff, 0x000fff, 0x001fff, 0x003fff, 0x007fff, 0x00ffff, 0x01ffff, 0x03ffff, 0x07ffff, 0x0fffff, 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff }; void VP8LInitBitReader(VP8LBitReader* const br, const uint8_t* const start, size_t length) { size_t i; vp8l_val_t value = 0; assert(br != NULL); assert(start != NULL); assert(length < 0xfffffff8u); // can't happen with a RIFF chunk. br->len_ = length; br->val_ = 0; br->bit_pos_ = 0; br->eos_ = 0; if (length > sizeof(br->val_)) { length = sizeof(br->val_); } for (i = 0; i < length; ++i) { value |= (vp8l_val_t)start[i] << (8 * i); } br->val_ = value; br->pos_ = length; br->buf_ = start; } void VP8LBitReaderSetBuffer(VP8LBitReader* const br, const uint8_t* const buf, size_t len) { assert(br != NULL); assert(buf != NULL); assert(len < 0xfffffff8u); // can't happen with a RIFF chunk. br->buf_ = buf; br->len_ = len; // pos_ > len_ should be considered a param error. br->eos_ = (br->pos_ > br->len_) || VP8LIsEndOfStream(br); } static void VP8LSetEndOfStream(VP8LBitReader* const br) { br->eos_ = 1; br->bit_pos_ = 0; // To avoid undefined behaviour with shifts. } // If not at EOS, reload up to VP8L_LBITS byte-by-byte static void ShiftBytes(VP8LBitReader* const br) { while (br->bit_pos_ >= 8 && br->pos_ < br->len_) { br->val_ >>= 8; br->val_ |= ((vp8l_val_t)br->buf_[br->pos_]) << (VP8L_LBITS - 8); ++br->pos_; br->bit_pos_ -= 8; } if (VP8LIsEndOfStream(br)) { VP8LSetEndOfStream(br); } } void VP8LDoFillBitWindow(VP8LBitReader* const br) { assert(br->bit_pos_ >= VP8L_WBITS); // TODO(jzern): given the fixed read size it may be possible to force // alignment in this block. #if defined(VP8L_USE_FAST_LOAD) if (br->pos_ + sizeof(br->val_) < br->len_) { br->val_ >>= VP8L_WBITS; br->bit_pos_ -= VP8L_WBITS; // The expression below needs a little-endian arch to work correctly. // This gives a large speedup for decoding speed. br->val_ |= (vp8l_val_t)HToLE32(WebPMemToUint32(br->buf_ + br->pos_)) << (VP8L_LBITS - VP8L_WBITS); br->pos_ += VP8L_LOG8_WBITS; return; } #endif ShiftBytes(br); // Slow path. } uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) { assert(n_bits >= 0); // Flag an error if end_of_stream or n_bits is more than allowed limit. if (!br->eos_ && n_bits <= VP8L_MAX_NUM_BIT_READ) { const uint32_t val = VP8LPrefetchBits(br) & kBitMask[n_bits]; const int new_bits = br->bit_pos_ + n_bits; br->bit_pos_ = new_bits; ShiftBytes(br); return val; } else { VP8LSetEndOfStream(br); return 0; } } //------------------------------------------------------------------------------ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-gc.h000664 001750 001750 00000003065 15164251010 042607 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_GC_H #define ECMA_GC_H #include "ecma-globals.h" #include "jmem.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmagc Garbage collector * @{ */ /** * Free option flags */ typedef enum { ECMA_GC_FREE_NO_OPTIONS = 0, /**< no options */ ECMA_GC_FREE_SECOND_PROPERTY = (1 << 0), /**< free second property of a property pair */ ECMA_GC_FREE_REFERENCES = (1 << 1), /**< free references */ } ecma_gc_free_options_t; void ecma_init_gc_info (ecma_object_t *object_p); void ecma_ref_object (ecma_object_t *object_p); void ecma_ref_object_inline (ecma_object_t *object_p); void ecma_deref_object (ecma_object_t *object_p); void ecma_gc_free_property (ecma_object_t *object_p, ecma_property_pair_t *prop_pair_p, uint32_t options); void ecma_gc_free_properties (ecma_object_t *object_p, uint32_t options); void ecma_gc_run (void); void ecma_free_unused_memory (jmem_pressure_t pressure); /** * @} * @} */ #endif /* !ECMA_GC_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-regexp.inc.h000664 001750 001750 00000002403 15164251010 050102 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * RegExp built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_REGEXP NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 2, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* ECMA-262 v5, 15.10.5.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_REGEXP_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_REGEXP_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 21.2.4.2 */ ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_SPECIES, ecma_builtin_regexp_species_get, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_REGEXP */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgScene.cpp000664 001750 001750 00000003527 15164251010 034103 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgScene.h" Scene::Scene() = default; Scene* Scene::gen() noexcept { return new SceneImpl; } Type Scene::type() const noexcept { return Type::Scene; } Result Scene::add(Paint* target, Paint* at) noexcept { return to(this)->insert(target, at); } Result Scene::remove(Paint* paint) noexcept { if (paint) return to(this)->remove(paint); else return to(this)->clearPaints(); } const list& Scene::paints() const noexcept { return to(this)->paints; } Result Scene::add(SceneEffect effect, ...) noexcept { va_list args; va_start(args, effect); auto ret = to(this)->add(effect, args); va_end(args); return ret; } thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-sort.cpp000664 001750 001750 00000012063 15164251010 051025 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" /** * Function used to merge two arrays for merge sort. * Arrays are stored as below: * First -> source_array_p [left_idx : right_idx - 1] * Second -> source_array_p [right_idx : end_idx - 1] * Output -> output_array_p * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_helper_array_merge_sort_bottom_up (ecma_value_t *source_array_p, /**< arrays to merge */ uint32_t left_idx, /**< first array begin */ uint32_t right_idx, /**< first array end */ uint32_t end_idx, /**< second array end */ ecma_value_t *output_array_p, /**< output array */ ecma_value_t compare_func, /**< compare function */ const ecma_builtin_helper_sort_compare_fn_t sort_cb, /**< sorting cb */ ecma_object_t *array_buffer_p) /* array_buffer_p */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; uint32_t i = left_idx, j = right_idx; for (uint32_t k = left_idx; k < end_idx; k++) { ecma_value_t compare_value = ecma_make_number_value (ECMA_NUMBER_ZERO); if (i < right_idx && j < end_idx) { compare_value = sort_cb (source_array_p[i], source_array_p[j], compare_func, array_buffer_p); if (ECMA_IS_VALUE_ERROR (compare_value)) { ret_value = ECMA_VALUE_ERROR; break; } } if (i < right_idx && ecma_get_number_from_value (compare_value) <= ECMA_NUMBER_ZERO) { output_array_p[k] = source_array_p[i]; i++; } else { output_array_p[k] = source_array_p[j]; j++; } ecma_free_value (compare_value); } return ret_value; } /* ecma_builtin_helper_array_merge_sort_bottom_up */ /** * Mergesort function * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_helper_array_merge_sort_helper (ecma_value_t *array_p, /**< array to sort */ uint32_t length, /**< length */ ecma_value_t compare_func, /**< compare function */ const ecma_builtin_helper_sort_compare_fn_t sort_cb, /**< sorting cb */ ecma_object_t *array_buffer_p) /**< arrayBuffer */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; JMEM_DEFINE_LOCAL_ARRAY (dest_array_p, length, ecma_value_t); ecma_value_t *temp_p; ecma_value_t *base_array_p = array_p; uint32_t r, e; for (uint32_t w = 1; w < length; w = 2 * w) { for (uint32_t i = 0; i < length; i = i + 2 * w) { // End of first array r = i + w; if (r > length) { r = length; } // End of second array e = i + 2 * w; if (e > length) { e = length; } // Merge two arrays ret_value = ecma_builtin_helper_array_merge_sort_bottom_up (array_p, i, r, e, dest_array_p, compare_func, sort_cb, array_buffer_p); if (ECMA_IS_VALUE_ERROR (ret_value)) { break; } } if (ECMA_IS_VALUE_ERROR (ret_value)) { break; } // Swap arrays temp_p = dest_array_p; dest_array_p = array_p; array_p = temp_p; } // Sorted array is in dest_array_p - there was uneven number of arrays swaps if (dest_array_p == base_array_p) { uint32_t index = 0; temp_p = dest_array_p; dest_array_p = array_p; array_p = temp_p; while (index < length) { array_p[index] = dest_array_p[index]; index++; } JERRY_ASSERT (index == length); } JMEM_FINALIZE_LOCAL_ARRAY (dest_array_p); return ret_value; } /* ecma_builtin_helper_array_merge_sort_helper */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlTessellator.cpp000664 001750 001750 00000042163 15164251010 037501 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlTessellator.h" namespace tvg { static uint32_t _pushVertex(Array& array, float x, float y) { array.push(x); array.push(y); return (array.count - 2) / 2; } Stroker::Stroker(GlGeometryBuffer* buffer, float width, StrokeCap cap, StrokeJoin join, float miterLimit) : mBuffer(buffer), mWidth(width), mMiterLimit(miterLimit), mCap(cap), mJoin(join) { } RenderRegion Stroker::bounds() const { return {{int32_t(floor(mLeftTop.x)), int32_t(floor(mLeftTop.y))}, {int32_t(ceil(mRightBottom.x)), int32_t(ceil(mRightBottom.y))}}; } void Stroker::run(const RenderPath& path) { mBuffer->vertex.reserve(path.pts.count * 4 + 16); mBuffer->index.reserve(path.pts.count * 3); auto validStrokeCap = false; auto pts = path.pts.data; ARRAY_FOREACH(cmd, path.cmds) { switch (*cmd) { case PathCommand::MoveTo: { if (validStrokeCap) { // check this, so we can skip if path only contains move instruction cap(); validStrokeCap = false; } mState.firstPt = *pts; mState.firstPtDir = {0.0f, 0.0f}; mState.prevPt = *pts; mState.prevPtDir = {0.0f, 0.0f}; pts++; validStrokeCap = false; } break; case PathCommand::LineTo: { validStrokeCap = true; lineTo(*pts); pts++; } break; case PathCommand::CubicTo: { validStrokeCap = true; cubicTo(pts[0], pts[1], pts[2]); pts += 3; } break; case PathCommand::Close: { close(); validStrokeCap = false; } break; default: break; } } if (validStrokeCap) cap(); } void Stroker::cap() { if (mCap == StrokeCap::Butt) return; if (mCap == StrokeCap::Square) { if (mState.firstPt == mState.prevPt) squarePoint(mState.firstPt); else { square(mState.firstPt, {-mState.firstPtDir.x, -mState.firstPtDir.y}); square(mState.prevPt, mState.prevPtDir); } } else if (mCap == StrokeCap::Round) { if (mState.firstPt == mState.prevPt) roundPoint(mState.firstPt); else { round(mState.firstPt, {-mState.firstPtDir.x, -mState.firstPtDir.y}); round(mState.prevPt, mState.prevPtDir); } } } void Stroker::lineTo(const Point& curr) { auto dir = (curr - mState.prevPt); normalize(dir); if (dir.x == 0.f && dir.y == 0.f) return; //same point auto normal = Point{-dir.y, dir.x}; auto a = mState.prevPt + normal * radius(); auto b = mState.prevPt - normal * radius(); auto c = curr + normal * radius(); auto d = curr - normal * radius(); auto ia = _pushVertex(mBuffer->vertex, a.x, a.y); auto ib = _pushVertex(mBuffer->vertex, b.x, b.y); auto ic = _pushVertex(mBuffer->vertex, c.x, c.y); auto id = _pushVertex(mBuffer->vertex, d.x, d.y); /** * a --------- c * | | * | | * b-----------d */ mBuffer->index.push(ia); mBuffer->index.push(ib); mBuffer->index.push(ic); mBuffer->index.push(ib); mBuffer->index.push(id); mBuffer->index.push(ic); if (mState.prevPt == mState.firstPt) { // first point after moveTo mState.prevPt = curr; mState.prevPtDir = dir; mState.firstPtDir = dir; } else { join(dir); mState.prevPtDir = dir; mState.prevPt = curr; } if (ia == 0) { mRightBottom.x = mLeftTop.x = curr.x; mRightBottom.y = mLeftTop.y = curr.y; } mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x))); mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y))); mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x))); mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y))); } void Stroker::cubicTo(const Point& cnt1, const Point& cnt2, const Point& end) { Bezier curve{ mState.prevPt, cnt1, cnt2, end }; auto count = curve.segments(); auto step = 1.f / count; for (uint32_t i = 0; i <= count; i++) { lineTo(curve.at(step * i)); } } void Stroker::close() { if (length(mState.prevPt - mState.firstPt) > 0.015625f) { lineTo(mState.firstPt); } // join firstPt with prevPt join(mState.firstPtDir); } void Stroker::join(const Point& dir) { auto orient = orientation(mState.prevPt - mState.prevPtDir, mState.prevPt, mState.prevPt + dir); if (orient == Orientation::Linear) { if (mState.prevPtDir == dir) return; // check is same direction if (mJoin != StrokeJoin::Round) return; // opposite direction auto normal = Point{-dir.y, dir.x}; auto p1 = mState.prevPt + normal * radius(); auto p2 = mState.prevPt - normal * radius(); auto oc = mState.prevPt + dir * radius(); round(p1, oc, mState.prevPt); round(oc, p2, mState.prevPt); } else { auto normal = Point{-dir.y, dir.x}; auto prevNormal = Point{-mState.prevPtDir.y, mState.prevPtDir.x}; Point prevJoin, currJoin; if (orient == Orientation::CounterClockwise) { prevJoin = mState.prevPt + prevNormal * radius(); currJoin = mState.prevPt + normal * radius(); } else { prevJoin = mState.prevPt - prevNormal * radius(); currJoin = mState.prevPt - normal * radius(); } if (mJoin == StrokeJoin::Miter) miter(prevJoin, currJoin, mState.prevPt); else if (mJoin == StrokeJoin::Bevel) bevel(prevJoin, currJoin, mState.prevPt); else round(prevJoin, currJoin, mState.prevPt); } } void Stroker::round(const Point &prev, const Point& curr, const Point& center) { auto orient = orientation(prev, center, curr); if (orient == Orientation::Linear) return; mLeftTop.x = std::min(mLeftTop.x, std::min(center.x, std::min(prev.x, curr.x))); mLeftTop.y = std::min(mLeftTop.y, std::min(center.y, std::min(prev.y, curr.y))); mRightBottom.x = std::max(mRightBottom.x, std::max(center.x, std::max(prev.x, curr.x))); mRightBottom.y = std::max(mRightBottom.y, std::max(center.y, std::max(prev.y, curr.y))); auto startAngle = tvg::atan2(prev.y - center.y, prev.x - center.x); auto endAngle = tvg::atan2(curr.y - center.y, curr.x - center.x); if (orient == Orientation::Clockwise) { if (endAngle > startAngle) endAngle -= 2 * MATH_PI; } else { if (endAngle < startAngle) endAngle += 2 * MATH_PI; } auto arcAngle = endAngle - startAngle; auto count = arcSegmentsCnt(arcAngle, radius()); auto c = _pushVertex(mBuffer->vertex, center.x, center.y); auto pi = _pushVertex(mBuffer->vertex, prev.x, prev.y); auto step = (endAngle - startAngle) / (count - 1); for (uint32_t i = 1; i < static_cast(count); i++) { auto angle = startAngle + step * i; Point out = {center.x + cos(angle) * radius(), center.y + sin(angle) * radius()}; auto oi = _pushVertex(mBuffer->vertex, out.x, out.y); mBuffer->index.push(c); mBuffer->index.push(pi); mBuffer->index.push(oi); pi = oi; mLeftTop.x = std::min(mLeftTop.x, out.x); mLeftTop.y = std::min(mLeftTop.y, out.y); mRightBottom.x = std::max(mRightBottom.x, out.x); mRightBottom.y = std::max(mRightBottom.y, out.y); } } void Stroker::roundPoint(const Point &p) { auto count = arcSegmentsCnt(2.0f * MATH_PI, radius()); auto c = _pushVertex(mBuffer->vertex, p.x, p.y); auto step = 2.0f * MATH_PI / (count - 1); for (uint32_t i = 1; i <= static_cast(count); i++) { float angle = i * step; Point dir = {cos(angle), sin(angle)}; Point out = p + dir * radius(); auto oi = _pushVertex(mBuffer->vertex, out.x, out.y); if (oi > 1) { mBuffer->index.push(c); mBuffer->index.push(oi); mBuffer->index.push(oi - 1); } } mLeftTop.x = std::min(mLeftTop.x, p.x - radius()); mLeftTop.y = std::min(mLeftTop.y, p.y - radius()); mRightBottom.x = std::max(mRightBottom.x, p.x + radius()); mRightBottom.y = std::max(mRightBottom.y, p.y + radius()); } void Stroker::miter(const Point& prev, const Point& curr, const Point& center) { auto pp1 = prev - center; auto pp2 = curr - center; auto out = pp1 + pp2; auto k = 2.f * radius() * radius() / (out.x * out.x + out.y * out.y); auto pe = out * k; if (length(pe) >= mMiterLimit * radius()) { bevel(prev, curr, center); return; } auto join = center + pe; auto c = _pushVertex(mBuffer->vertex, center.x, center.y); auto cp1 = _pushVertex(mBuffer->vertex, prev.x, prev.y); auto cp2 = _pushVertex(mBuffer->vertex, curr.x, curr.y); auto e = _pushVertex(mBuffer->vertex, join.x, join.y); mBuffer->index.push(c); mBuffer->index.push(cp1); mBuffer->index.push(e); mBuffer->index.push(e); mBuffer->index.push(cp2); mBuffer->index.push(c); mLeftTop.x = std::min(mLeftTop.x, join.x); mLeftTop.y = std::min(mLeftTop.y, join.y); mRightBottom.x = std::max(mRightBottom.x, join.x); mRightBottom.y = std::max(mRightBottom.y, join.y); } void Stroker::bevel(const Point& prev, const Point& curr, const Point& center) { auto a = _pushVertex(mBuffer->vertex, prev.x, prev.y); auto b = _pushVertex(mBuffer->vertex, curr.x, curr.y); auto c = _pushVertex(mBuffer->vertex, center.x, center.y); mBuffer->index.push(a); mBuffer->index.push(b); mBuffer->index.push(c); } void Stroker::square(const Point& p, const Point& outDir) { auto normal = Point{-outDir.y, outDir.x}; auto a = p + normal * radius(); auto b = p - normal * radius(); auto c = a + outDir * radius(); auto d = b + outDir * radius(); auto ai = _pushVertex(mBuffer->vertex, a.x, a.y); auto bi = _pushVertex(mBuffer->vertex, b.x, b.y); auto ci = _pushVertex(mBuffer->vertex, c.x, c.y); auto di = _pushVertex(mBuffer->vertex, d.x, d.y); mBuffer->index.push(ai); mBuffer->index.push(bi); mBuffer->index.push(ci); mBuffer->index.push(ci); mBuffer->index.push(bi); mBuffer->index.push(di); mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x))); mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y))); mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x))); mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y))); } void Stroker::squarePoint(const Point& p) { auto offsetX = Point{radius(), 0.0f}; auto offsetY = Point{0.0f, radius()}; auto a = p + offsetX + offsetY; auto b = p - offsetX + offsetY; auto c = p - offsetX - offsetY; auto d = p + offsetX - offsetY; auto ai = _pushVertex(mBuffer->vertex, a.x, a.y); auto bi = _pushVertex(mBuffer->vertex, b.x, b.y); auto ci = _pushVertex(mBuffer->vertex, c.x, c.y); auto di = _pushVertex(mBuffer->vertex, d.x, d.y); mBuffer->index.push(ai); mBuffer->index.push(bi); mBuffer->index.push(ci); mBuffer->index.push(ci); mBuffer->index.push(di); mBuffer->index.push(ai); mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x))); mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y))); mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x))); mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y))); } void Stroker::round(const Point& p, const Point& outDir) { auto normal = Point{-outDir.y, outDir.x}; auto a = p + normal * radius(); auto b = p - normal * radius(); auto c = p + outDir * radius(); round(a, c, p); round(c, b, p); } BWTessellator::BWTessellator(GlGeometryBuffer* buffer): mBuffer(buffer) { } void BWTessellator::tessellate(const RenderPath& path) { auto cmds = path.cmds.data; auto cmdCnt = path.cmds.count; auto pts = path.pts.data; auto ptsCnt = path.pts.count; if (ptsCnt <= 2) return; uint32_t firstIndex = 0; uint32_t prevIndex = 0; mBuffer->vertex.reserve(ptsCnt * 2); mBuffer->index.reserve((ptsCnt - 2) * 3); auto updateConvexity = [&](const Point& edge) { if (!convex) return; if (prevEdge.x == 0.0f && prevEdge.y == 0.0f) { prevEdge = edge; return; } auto c = cross(prevEdge, edge); if (zero(c)) { prevEdge = edge; return; } auto sign = (c > 0) ? 1 : -1; if (winding == 0) winding = sign; // The default winding is CCW, but it might be otherwise once we support unordered points. else if (sign != winding) convex = false; prevEdge = edge; }; for (uint32_t i = 0; i < cmdCnt; i++) { switch(cmds[i]) { case PathCommand::MoveTo: { firstIndex = pushVertex(pts->x, pts->y); firstPt = prevPt = *pts; prevEdge = {}; prevIndex = 0; pts++; } break; case PathCommand::LineTo: { if (prevIndex == 0) { prevIndex = pushVertex(pts->x, pts->y); prevEdge = *pts - prevPt; prevPt = *pts++; } else { updateConvexity(*pts - prevPt); auto currIndex = pushVertex(pts->x, pts->y); pushTriangle(firstIndex, prevIndex, currIndex); prevIndex = currIndex; prevPt = *pts++; } } break; case PathCommand::CubicTo: { Bezier curve{pts[-1], pts[0], pts[1], pts[2]}; if (convex) { auto e1 = curve.ctrl1 - curve.start; auto e2 = curve.ctrl2 - curve.ctrl1; auto e3 = curve.end - curve.ctrl2; if (prevIndex != 0) updateConvexity(e1); else prevEdge = e1; updateConvexity(e2); updateConvexity(e3); } auto stepCount = curve.segments(); if (stepCount <= 1) stepCount = 2; float step = 1.f / stepCount; for (uint32_t s = 1; s <= static_cast(stepCount); s++) { auto pt = curve.at(step * s); auto currIndex = pushVertex(pt.x, pt.y); if (prevIndex == 0) { prevIndex = currIndex; continue; } pushTriangle(firstIndex, prevIndex, currIndex); prevIndex = currIndex; } prevPt = curve.end; pts += 3; } break; case PathCommand::Close: { if (convex && prevIndex != 0) { updateConvexity(firstPt - prevPt); if (convex && winding != 0) { auto& v = mBuffer->vertex; auto secondPt = Point{v[firstIndex * 2 + 2], v[firstIndex * 2 + 3]}; updateConvexity(secondPt - firstPt); } } } break; default: break; } } } RenderRegion BWTessellator::bounds() const { return {{int32_t(floor(bbox.min.x)), int32_t(floor(bbox.min.y))}, {int32_t(ceil(bbox.max.x)), int32_t(ceil(bbox.max.y))}}; } uint32_t BWTessellator::pushVertex(float x, float y) { auto index = _pushVertex(mBuffer->vertex, x, y); if (index == 0) bbox.max = bbox.min = {x, y}; else bbox = {{std::min(bbox.min.x, x), std::min(bbox.min.y, y)}, {std::max(bbox.max.x, x), std::max(bbox.max.y, y)}}; return index; } void BWTessellator::pushTriangle(uint32_t a, uint32_t b, uint32_t c) { mBuffer->index.push(a); mBuffer->index.push(b); mBuffer->index.push(c); } } // namespace tvg glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-lexer.cpp000664 001750 001750 00000322020 15164251010 043463 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-bigint.h" #include "ecma-function-object.h" #include "ecma-helpers.h" #include "ecma-literal-storage.h" #include "jcontext.h" #include "js-parser-internal.h" #include "lit-char-helpers.h" #if JERRY_PARSER /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_lexer Lexer * @{ */ JERRY_STATIC_ASSERT (((int)LEXER_NUMBER_BINARY > (int)LEXER_NUMBER_OCTAL), lexer_number_binary_must_be_greater_than_lexer_number_octal); /** * Check whether the UTF-8 intermediate is an octet or not */ #define IS_UTF8_INTERMEDIATE_OCTET(byte) (((byte) &LIT_UTF8_EXTRA_BYTE_MASK) == LIT_UTF8_2_BYTE_CODE_POINT_MIN) /** * Align column to the next tab position. * * @return aligned position */ static parser_line_counter_t align_column_to_tab (parser_line_counter_t column) /**< current column */ { /* Tab aligns to zero column start position. */ return (parser_line_counter_t) (((column + (8u - 1u)) & ~ECMA_STRING_CONTAINER_MASK) + 1u); } /* align_column_to_tab */ /** * Parse hexadecimal character sequence * * @return character value or UINT32_MAX on error */ static lit_code_point_t lexer_hex_to_code_point (const uint8_t *source_p, /**< current source position */ parser_line_counter_t length) /**< source length */ { lit_code_point_t result = 0; do { uint32_t byte = *source_p++; result <<= 4; if (byte >= LIT_CHAR_0 && byte <= LIT_CHAR_9) { result += byte - LIT_CHAR_0; } else { byte = LEXER_TO_ASCII_LOWERCASE (byte); if (byte >= LIT_CHAR_LOWERCASE_A && byte <= LIT_CHAR_LOWERCASE_F) { result += byte - (LIT_CHAR_LOWERCASE_A - 10); } else { return UINT32_MAX; } } } while (--length > 0); return result; } /* lexer_hex_to_code_point */ /** * Parse hexadecimal character sequence enclosed in braces * * @return character value or UINT32_MAX on error */ static lit_code_point_t lexer_hex_in_braces_to_code_point (const uint8_t *source_p, /**< current source position */ const uint8_t *source_end_p, /**< source end */ uint32_t *length_p) /**< [out] length of the sequence */ { lit_code_point_t result = 0; /* Four is the size of \u{} sequence. */ uint32_t length = 4; JERRY_ASSERT (source_p[-1] == LIT_CHAR_LEFT_BRACE); JERRY_ASSERT (source_p < source_end_p); do { uint32_t byte = *source_p++; result <<= 4; if (byte >= LIT_CHAR_0 && byte <= LIT_CHAR_9) { result += byte - LIT_CHAR_0; } else { byte = LEXER_TO_ASCII_LOWERCASE (byte); if (byte >= LIT_CHAR_LOWERCASE_A && byte <= LIT_CHAR_LOWERCASE_F) { result += byte - (LIT_CHAR_LOWERCASE_A - 10); } else { return UINT32_MAX; } } if (result >= (LIT_UNICODE_CODE_POINT_MAX + 1) || source_p >= source_end_p) { return UINT32_MAX; } length++; } while (*source_p != LIT_CHAR_RIGHT_BRACE); *length_p = length; return result; } /* lexer_hex_in_braces_to_code_point */ /** * Parse hexadecimal character sequence * * @return character value */ static lit_code_point_t lexer_unchecked_hex_to_character (const uint8_t **source_p) /**< [in, out] current source position */ { lit_code_point_t result = 0; const uint8_t *char_p = *source_p; uint32_t length = (char_p[-1] == LIT_CHAR_LOWERCASE_U) ? 4 : 2; if (char_p[0] == LIT_CHAR_LEFT_BRACE) { length = 0; char_p++; } while (true) { uint32_t byte = *char_p++; result <<= 4; if (byte >= LIT_CHAR_0 && byte <= LIT_CHAR_9) { result += byte - LIT_CHAR_0; } else { JERRY_ASSERT ((byte >= LIT_CHAR_LOWERCASE_A && byte <= LIT_CHAR_LOWERCASE_F) || (byte >= LIT_CHAR_UPPERCASE_A && byte <= LIT_CHAR_UPPERCASE_F)); result += LEXER_TO_ASCII_LOWERCASE (byte) - (LIT_CHAR_LOWERCASE_A - 10); } JERRY_ASSERT (result <= LIT_UNICODE_CODE_POINT_MAX); if (length == 0) { if (*char_p != LIT_CHAR_RIGHT_BRACE) { continue; } *source_p = char_p + 1; return result; } if (--length == 0) { *source_p = char_p; return result; } } } /* lexer_unchecked_hex_to_character */ /** * Skip space mode */ typedef enum { LEXER_SKIP_SPACES, /**< skip spaces mode */ LEXER_SKIP_SINGLE_LINE_COMMENT, /**< parse single line comment */ LEXER_SKIP_MULTI_LINE_COMMENT, /**< parse multi line comment */ } skip_mode_t; /** * Skip spaces. */ static void lexer_skip_spaces (parser_context_t *context_p) /**< context */ { skip_mode_t mode = LEXER_SKIP_SPACES; const uint8_t *source_end_p = context_p->source_end_p; if (context_p->token.flags & LEXER_NO_SKIP_SPACES) { context_p->token.flags &= (uint8_t) ~LEXER_NO_SKIP_SPACES; return; } context_p->token.flags = 0; while (true) { if (context_p->source_p >= source_end_p) { if (mode == LEXER_SKIP_MULTI_LINE_COMMENT) { parser_raise_error (context_p, PARSER_ERR_UNTERMINATED_MULTILINE_COMMENT); } return; } switch (context_p->source_p[0]) { case LIT_CHAR_CR: { if (context_p->source_p + 1 < source_end_p && context_p->source_p[1] == LIT_CHAR_LF) { context_p->source_p++; } /* FALLTHRU */ } case LIT_CHAR_LF: { context_p->line++; context_p->column = 0; context_p->token.flags = LEXER_WAS_NEWLINE; if (mode == LEXER_SKIP_SINGLE_LINE_COMMENT) { mode = LEXER_SKIP_SPACES; } /* FALLTHRU */ } case LIT_CHAR_VTAB: case LIT_CHAR_FF: case LIT_CHAR_SP: { context_p->source_p++; context_p->column++; continue; } case LIT_CHAR_TAB: { context_p->column = align_column_to_tab (context_p->column); context_p->source_p++; continue; } case LIT_CHAR_SLASH: { if (mode == LEXER_SKIP_SPACES && context_p->source_p + 1 < source_end_p) { if (context_p->source_p[1] == LIT_CHAR_SLASH) { mode = LEXER_SKIP_SINGLE_LINE_COMMENT; } else if (context_p->source_p[1] == LIT_CHAR_ASTERISK) { mode = LEXER_SKIP_MULTI_LINE_COMMENT; context_p->token.line = context_p->line; context_p->token.column = context_p->column; } if (mode != LEXER_SKIP_SPACES) { context_p->source_p += 2; PARSER_PLUS_EQUAL_LC (context_p->column, 2); continue; } } break; } case LIT_CHAR_ASTERISK: { if (mode == LEXER_SKIP_MULTI_LINE_COMMENT && context_p->source_p + 1 < source_end_p && context_p->source_p[1] == LIT_CHAR_SLASH) { mode = LEXER_SKIP_SPACES; context_p->source_p += 2; PARSER_PLUS_EQUAL_LC (context_p->column, 2); continue; } break; } case 0xc2: { if (context_p->source_p + 1 < source_end_p && context_p->source_p[1] == 0xa0) { /* Codepoint \u00A0 */ context_p->source_p += 2; context_p->column++; continue; } break; } case LEXER_NEWLINE_LS_PS_BYTE_1: { JERRY_ASSERT (context_p->source_p + 2 < source_end_p); if (LEXER_NEWLINE_LS_PS_BYTE_23 (context_p->source_p)) { /* Codepoint \u2028 and \u2029 */ context_p->source_p += 3; context_p->line++; context_p->column = 1; context_p->token.flags = LEXER_WAS_NEWLINE; if (mode == LEXER_SKIP_SINGLE_LINE_COMMENT) { mode = LEXER_SKIP_SPACES; } continue; } break; } case 0xef: { if (context_p->source_p + 2 < source_end_p && context_p->source_p[1] == 0xbb && context_p->source_p[2] == 0xbf) { /* Codepoint \uFEFF */ context_p->source_p += 3; context_p->column++; continue; } break; } default: { break; } } if (mode == LEXER_SKIP_SPACES) { return; } context_p->source_p++; if (context_p->source_p < source_end_p && !IS_UTF8_INTERMEDIATE_OCTET (context_p->source_p[0])) { context_p->column++; } } } /* lexer_skip_spaces */ /** * Skip all the continuous empty statements. */ void lexer_skip_empty_statements (parser_context_t *context_p) /**< context */ { lexer_skip_spaces (context_p); while (context_p->source_p < context_p->source_end_p && *context_p->source_p == LIT_CHAR_SEMICOLON) { lexer_consume_next_character (context_p); lexer_skip_spaces (context_p); } context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); } /* lexer_skip_empty_statements */ /** * Checks whether the keyword has escape sequences. */ #define LEXER_CHECK_INVALID_KEYWORD(ident_start_p, buffer_p) \ (JERRY_UNLIKELY ((ident_start_p) == (buffer_p)) \ && !(context_p->global_status_flags & ECMA_PARSE_INTERNAL_PRE_SCANNING)) /** * Keyword data. */ typedef struct { const uint8_t *keyword_p; /**< keyword string */ lexer_token_type_t type; /**< keyword token type */ } keyword_string_t; /** * @{ * Keyword defines */ #define LEXER_KEYWORD(name, type) \ { \ (const uint8_t *) (name), (type) \ } #define LEXER_KEYWORD_LIST_LENGTH(name) (const uint8_t) (sizeof ((name)) / sizeof ((name)[0])) /** @} */ /** * Length of the shortest keyword. */ #define LEXER_KEYWORD_MIN_LENGTH 2 /** * Length of the longest keyword. */ #define LEXER_KEYWORD_MAX_LENGTH 10 /** * Keywords with 2 characters. */ static const keyword_string_t keywords_with_length_2[] = { LEXER_KEYWORD ("do", LEXER_KEYW_DO), LEXER_KEYWORD ("if", LEXER_KEYW_IF), LEXER_KEYWORD ("in", LEXER_KEYW_IN), }; /** * Keywords with 3 characters. */ static const keyword_string_t keywords_with_length_3[] = { LEXER_KEYWORD ("for", LEXER_KEYW_FOR), LEXER_KEYWORD ("let", LEXER_KEYW_LET), LEXER_KEYWORD ("new", LEXER_KEYW_NEW), LEXER_KEYWORD ("try", LEXER_KEYW_TRY), LEXER_KEYWORD ("var", LEXER_KEYW_VAR), }; /** * Keywords with 4 characters. */ static const keyword_string_t keywords_with_length_4[] = { LEXER_KEYWORD ("case", LEXER_KEYW_CASE), LEXER_KEYWORD ("else", LEXER_KEYW_ELSE), LEXER_KEYWORD ("enum", LEXER_KEYW_ENUM), LEXER_KEYWORD ("eval", LEXER_KEYW_EVAL), #if JERRY_MODULE_SYSTEM LEXER_KEYWORD ("meta", LEXER_KEYW_META), #endif /* JERRY_MODULE_SYSTEM */ LEXER_KEYWORD ("null", LEXER_LIT_NULL), LEXER_KEYWORD ("this", LEXER_KEYW_THIS), LEXER_KEYWORD ("true", LEXER_LIT_TRUE), LEXER_KEYWORD ("void", LEXER_KEYW_VOID), LEXER_KEYWORD ("with", LEXER_KEYW_WITH), }; /** * Keywords with 5 characters. */ static const keyword_string_t keywords_with_length_5[] = { LEXER_KEYWORD ("async", LEXER_KEYW_ASYNC), LEXER_KEYWORD ("await", LEXER_KEYW_AWAIT), LEXER_KEYWORD ("break", LEXER_KEYW_BREAK), LEXER_KEYWORD ("catch", LEXER_KEYW_CATCH), LEXER_KEYWORD ("class", LEXER_KEYW_CLASS), LEXER_KEYWORD ("const", LEXER_KEYW_CONST), LEXER_KEYWORD ("false", LEXER_LIT_FALSE), LEXER_KEYWORD ("super", LEXER_KEYW_SUPER), LEXER_KEYWORD ("throw", LEXER_KEYW_THROW), LEXER_KEYWORD ("while", LEXER_KEYW_WHILE), LEXER_KEYWORD ("yield", LEXER_KEYW_YIELD), }; /** * Keywords with 6 characters. */ static const keyword_string_t keywords_with_length_6[] = { LEXER_KEYWORD ("delete", LEXER_KEYW_DELETE), LEXER_KEYWORD ("export", LEXER_KEYW_EXPORT), LEXER_KEYWORD ("import", LEXER_KEYW_IMPORT), LEXER_KEYWORD ("public", LEXER_KEYW_PUBLIC), LEXER_KEYWORD ("return", LEXER_KEYW_RETURN), LEXER_KEYWORD ("static", LEXER_KEYW_STATIC), LEXER_KEYWORD ("switch", LEXER_KEYW_SWITCH), LEXER_KEYWORD ("typeof", LEXER_KEYW_TYPEOF), }; /** * Keywords with 7 characters. */ static const keyword_string_t keywords_with_length_7[] = { LEXER_KEYWORD ("default", LEXER_KEYW_DEFAULT), LEXER_KEYWORD ("extends", LEXER_KEYW_EXTENDS), LEXER_KEYWORD ("finally", LEXER_KEYW_FINALLY), LEXER_KEYWORD ("package", LEXER_KEYW_PACKAGE), LEXER_KEYWORD ("private", LEXER_KEYW_PRIVATE), }; /** * Keywords with 8 characters. */ static const keyword_string_t keywords_with_length_8[] = { LEXER_KEYWORD ("continue", LEXER_KEYW_CONTINUE), LEXER_KEYWORD ("debugger", LEXER_KEYW_DEBUGGER), LEXER_KEYWORD ("function", LEXER_KEYW_FUNCTION), }; /** * Keywords with 9 characters. */ static const keyword_string_t keywords_with_length_9[] = { LEXER_KEYWORD ("arguments", LEXER_KEYW_ARGUMENTS), LEXER_KEYWORD ("interface", LEXER_KEYW_INTERFACE), LEXER_KEYWORD ("protected", LEXER_KEYW_PROTECTED), }; /** * Keywords with 10 characters. */ static const keyword_string_t keywords_with_length_10[] = { LEXER_KEYWORD ("implements", LEXER_KEYW_IMPLEMENTS), LEXER_KEYWORD ("instanceof", LEXER_KEYW_INSTANCEOF), }; /** * List of the keyword groups. */ static const keyword_string_t *const keyword_strings_list[] = { keywords_with_length_2, keywords_with_length_3, keywords_with_length_4, keywords_with_length_5, keywords_with_length_6, keywords_with_length_7, keywords_with_length_8, keywords_with_length_9, keywords_with_length_10 }; JERRY_STATIC_ASSERT ((sizeof (keyword_strings_list) / sizeof (const keyword_string_t *) == (LEXER_KEYWORD_MAX_LENGTH - LEXER_KEYWORD_MIN_LENGTH) + 1), keyword_strings_list_size_must_equal_to_keyword_max_length_difference); /** * List of the keyword groups length. */ static const uint8_t keyword_lengths_list[] = { LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_2), LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_3), LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_4), LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_5), LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_6), LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_7), LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_8), LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_9), LEXER_KEYWORD_LIST_LENGTH (keywords_with_length_10) }; #undef LEXER_KEYWORD #undef LEXER_KEYWORD_LIST_LENGTH JERRY_STATIC_ASSERT (((int)LEXER_FIRST_NON_RESERVED_KEYWORD < (int)LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD), lexer_first_non_reserved_keyword_must_be_before_lexer_first_future_strict_reserved_word); /** * Parse identifier. * * @return true, if an identifier is parsed, false otherwise */ static bool lexer_parse_identifier (parser_context_t *context_p, /**< context */ lexer_parse_options_t options) /**< check keywords */ { /* Only very few identifiers contains \u escape sequences. */ const uint8_t *source_p = context_p->source_p; /* Note: newline or tab cannot be part of an identifier. */ parser_line_counter_t column = context_p->column; const uint8_t *source_end_p = context_p->source_end_p; size_t length = 0; lexer_lit_location_flags_t status_flags = LEXER_LIT_LOCATION_IS_ASCII; do { if (*source_p == LIT_CHAR_BACKSLASH) { /* After a backslash an identifier must start. */ lit_code_point_t code_point = UINT32_MAX; uint32_t escape_length = 6; if (options & (LEXER_PARSE_CHECK_START_AND_RETURN | LEXER_PARSE_CHECK_PART_AND_RETURN)) { return true; } status_flags = LEXER_LIT_LOCATION_HAS_ESCAPE; if (source_p + 5 <= source_end_p && source_p[1] == LIT_CHAR_LOWERCASE_U) { if (source_p[2] == LIT_CHAR_LEFT_BRACE) { code_point = lexer_hex_in_braces_to_code_point (source_p + 3, source_end_p, &escape_length); } else if (source_p + 6 <= source_end_p) { code_point = lexer_hex_to_code_point (source_p + 2, 4); } } if (code_point == UINT32_MAX) { context_p->source_p = source_p; context_p->token.column = column; parser_raise_error (context_p, PARSER_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE); } if (length == 0) { if (!lit_code_point_is_identifier_start (code_point)) { parser_raise_error (context_p, PARSER_ERR_INVALID_IDENTIFIER_START); } } else { if (!lit_code_point_is_identifier_part (code_point)) { parser_raise_error (context_p, PARSER_ERR_INVALID_IDENTIFIER_PART); } } length += lit_code_point_get_cesu8_length (code_point); source_p += escape_length; PARSER_PLUS_EQUAL_LC (column, escape_length); continue; } lit_code_point_t code_point = *source_p; lit_utf8_size_t utf8_length = 1, decoded_length = 1, char_count = 1; if (JERRY_UNLIKELY (code_point >= LIT_UTF8_2_BYTE_MARKER)) { status_flags = (lexer_lit_location_flags_t) ((uint32_t) status_flags & (uint32_t) ~LEXER_LIT_LOCATION_IS_ASCII); utf8_length = lit_read_code_point_from_utf8 (source_p, (lit_utf8_size_t) (source_end_p - source_p), &code_point); decoded_length = utf8_length; /* Only ES2015+ supports code points outside of the basic plane which can be part of an identifier. */ if ((code_point >= LIT_UTF16_HIGH_SURROGATE_MIN && code_point <= LIT_UTF16_HIGH_SURROGATE_MAX) && source_p + 3 < source_end_p) { lit_code_point_t low_surrogate; lit_read_code_point_from_utf8 (source_p + 3, (lit_utf8_size_t) (source_end_p - (source_p + 3)), &low_surrogate); if (low_surrogate >= LIT_UTF16_LOW_SURROGATE_MIN && low_surrogate <= LIT_UTF16_LOW_SURROGATE_MAX) { code_point = lit_convert_surrogate_pair_to_code_point ((ecma_char_t) code_point, (ecma_char_t) low_surrogate); utf8_length = 2 * 3; decoded_length = 2 * 3; char_count = 2; } } else if (source_p[0] >= LIT_UTF8_4_BYTE_MARKER) { decoded_length = 2 * 3; status_flags = LEXER_LIT_LOCATION_HAS_ESCAPE; } } if (length == 0) { if (JERRY_UNLIKELY (options & (LEXER_PARSE_CHECK_START_AND_RETURN | LEXER_PARSE_CHECK_PART_AND_RETURN))) { if (options & LEXER_PARSE_CHECK_START_AND_RETURN) { return lit_code_point_is_identifier_start (code_point); } else { return lit_code_point_is_identifier_part (code_point); } } if (!lit_code_point_is_identifier_start (code_point)) { return false; } } else if (!lit_code_point_is_identifier_part (code_point)) { break; } source_p += utf8_length; length += decoded_length; PARSER_PLUS_EQUAL_LC (column, char_count); } while (source_p < source_end_p); JERRY_ASSERT (length > 0); context_p->token.type = LEXER_LITERAL; context_p->token.lit_location.type = LEXER_IDENT_LITERAL; context_p->token.lit_location.status_flags = (uint8_t) status_flags; context_p->token.column = context_p->column; context_p->token.lit_location.char_p = context_p->source_p; context_p->token.lit_location.length = (prop_length_t) length; if (JERRY_UNLIKELY (length > PARSER_MAXIMUM_IDENT_LENGTH)) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_TOO_LONG); } /* Check keywords. */ if ((options & LEXER_PARSE_CHECK_KEYWORDS) && (length >= LEXER_KEYWORD_MIN_LENGTH && length <= LEXER_KEYWORD_MAX_LENGTH)) { const uint8_t *ident_start_p = context_p->source_p; uint8_t buffer_p[LEXER_KEYWORD_MAX_LENGTH]; if (JERRY_UNLIKELY (context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { lexer_convert_ident_to_cesu8 (buffer_p, ident_start_p, (prop_length_t) length); ident_start_p = buffer_p; } const keyword_string_t *keyword_list_p = keyword_strings_list[length - LEXER_KEYWORD_MIN_LENGTH]; int start = 0; int end = keyword_lengths_list[length - LEXER_KEYWORD_MIN_LENGTH]; int middle = end / 2; do { const keyword_string_t *keyword_p = keyword_list_p + middle; int compare_result = ident_start_p[0] - keyword_p->keyword_p[0]; if (compare_result == 0) { compare_result = memcmp (ident_start_p, keyword_p->keyword_p, length); if (compare_result == 0) { context_p->token.keyword_type = (uint8_t) keyword_p->type; if (JERRY_LIKELY (keyword_p->type < LEXER_FIRST_NON_RESERVED_KEYWORD)) { if (JERRY_UNLIKELY (keyword_p->type == LEXER_KEYW_AWAIT)) { if (!(context_p->status_flags & (PARSER_IS_ASYNC_FUNCTION | PARSER_IS_CLASS_STATIC_BLOCK)) && !(context_p->global_status_flags & ECMA_PARSE_MODULE)) { break; } if (context_p->status_flags & PARSER_DISALLOW_AWAIT_YIELD) { if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } parser_raise_error (context_p, PARSER_ERR_AWAIT_NOT_ALLOWED); } context_p->token.type = (uint8_t) LEXER_KEYW_AWAIT; break; } if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p)) { /* Escape sequences are not allowed in a keyword. */ parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } context_p->token.type = (uint8_t) keyword_p->type; break; } if (keyword_p->type == LEXER_KEYW_LET && (context_p->status_flags & PARSER_IS_STRICT)) { if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } context_p->token.type = (uint8_t) LEXER_KEYW_LET; break; } if (keyword_p->type == LEXER_KEYW_YIELD && (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)) { if (context_p->status_flags & PARSER_DISALLOW_AWAIT_YIELD) { if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } parser_raise_error (context_p, PARSER_ERR_YIELD_NOT_ALLOWED); } context_p->token.type = (uint8_t) LEXER_KEYW_YIELD; break; } if (keyword_p->type == LEXER_KEYW_ARGUMENTS && (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD)) { parser_raise_error (context_p, PARSER_ERR_ARGUMENTS_IN_CLASS_FIELD); } if (keyword_p->type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD && (context_p->status_flags & PARSER_IS_STRICT) && !(options & LEXER_PARSE_NO_STRICT_IDENT_ERROR)) { parser_raise_error (context_p, PARSER_ERR_STRICT_IDENT_NOT_ALLOWED); } break; } } if (compare_result > 0) { start = middle + 1; } else { JERRY_ASSERT (compare_result < 0); end = middle; } middle = (start + end) / 2; } while (start < end); } context_p->source_p = source_p; context_p->column = column; return true; } /* lexer_parse_identifier */ #undef LEXER_CHECK_INVALID_KEYWORD /** * Parse string. */ void lexer_parse_string (parser_context_t *context_p, /**< context */ lexer_string_options_t opts) /**< options */ { int32_t raw_length_adjust = 0; uint8_t str_end_character = context_p->source_p[0]; const uint8_t *source_p = context_p->source_p + 1; const uint8_t *string_start_p = source_p; const uint8_t *source_end_p = context_p->source_end_p; parser_line_counter_t line = context_p->line; parser_line_counter_t column = (parser_line_counter_t) (context_p->column + 1); parser_line_counter_t original_line = line; parser_line_counter_t original_column = column; size_t length = 0; lexer_lit_location_flags_t status_flags = LEXER_LIT_LOCATION_IS_ASCII; if (str_end_character == LIT_CHAR_RIGHT_BRACE) { str_end_character = LIT_CHAR_GRAVE_ACCENT; } while (true) { if (source_p >= source_end_p) { context_p->token.line = original_line; context_p->token.column = (parser_line_counter_t) (original_column - 1); parser_raise_error (context_p, PARSER_ERR_UNTERMINATED_STRING); } if (*source_p == str_end_character) { break; } if (*source_p == LIT_CHAR_BACKSLASH) { source_p++; column++; if (source_p >= source_end_p) { /* Will throw an unterminated string error. */ continue; } status_flags = LEXER_LIT_LOCATION_HAS_ESCAPE; /* Newline is ignored. */ if (*source_p == LIT_CHAR_CR) { source_p++; if (source_p < source_end_p && *source_p == LIT_CHAR_LF) { raw_length_adjust--; source_p++; } line++; column = 1; continue; } else if (*source_p == LIT_CHAR_LF) { source_p++; line++; column = 1; continue; } else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p)) { source_p += 3; line++; column = 1; continue; } if (opts & LEXER_STRING_RAW) { if ((*source_p == LIT_CHAR_GRAVE_ACCENT) || (*source_p == LIT_CHAR_BACKSLASH)) { source_p++; column++; length++; } continue; } if (*source_p == LIT_CHAR_0 && source_p + 1 < source_end_p && (*(source_p + 1) < LIT_CHAR_0 || *(source_p + 1) > LIT_CHAR_9)) { source_p++; column++; length++; continue; } /* Except \x, \u, and octal numbers, everything is * converted to a character which has the same byte length. */ if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_3) { if (str_end_character == LIT_CHAR_GRAVE_ACCENT) { parser_raise_error (context_p, PARSER_ERR_TEMPLATE_STR_OCTAL_ESCAPE); } if (context_p->status_flags & PARSER_IS_STRICT) { parser_raise_error (context_p, PARSER_ERR_OCTAL_ESCAPE_NOT_ALLOWED); } source_p++; column++; if (source_p < source_end_p && *source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7) { source_p++; column++; if (source_p < source_end_p && *source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7) { /* Numbers >= 0x200 (0x80) requires * two bytes for encoding in UTF-8. */ if (source_p[-2] >= LIT_CHAR_2) { length++; } source_p++; column++; } } length++; continue; } if (*source_p >= LIT_CHAR_4 && *source_p <= LIT_CHAR_7) { if (context_p->status_flags & PARSER_IS_STRICT) { parser_raise_error (context_p, PARSER_ERR_OCTAL_ESCAPE_NOT_ALLOWED); } source_p++; column++; if (source_p < source_end_p && *source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7) { source_p++; column++; } /* The maximum number is 0x4d so the UTF-8 * representation is always one byte. */ length++; continue; } if (*source_p == LIT_CHAR_LOWERCASE_X || *source_p == LIT_CHAR_LOWERCASE_U) { uint32_t escape_length = (*source_p == LIT_CHAR_LOWERCASE_X) ? 3 : 5; lit_code_point_t code_point = UINT32_MAX; if (source_p + 4 <= source_end_p && source_p[0] == LIT_CHAR_LOWERCASE_U && source_p[1] == LIT_CHAR_LEFT_BRACE) { code_point = lexer_hex_in_braces_to_code_point (source_p + 2, source_end_p, &escape_length); escape_length--; } else { if (source_p + escape_length <= source_end_p) { code_point = lexer_hex_to_code_point (source_p + 1, escape_length - 1); } } if (code_point == UINT32_MAX) { context_p->token.line = line; context_p->token.column = (parser_line_counter_t) (column - 1); parser_raise_error (context_p, PARSER_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE); } length += lit_code_point_get_cesu8_length (code_point); source_p += escape_length; PARSER_PLUS_EQUAL_LC (column, escape_length); continue; } } else if (str_end_character == LIT_CHAR_GRAVE_ACCENT && source_p[0] == LIT_CHAR_DOLLAR_SIGN && source_p + 1 < source_end_p && source_p[1] == LIT_CHAR_LEFT_BRACE) { raw_length_adjust--; source_p++; break; } if (*source_p >= LIT_UTF8_4_BYTE_MARKER) { /* Processing 4 byte unicode sequence (even if it is * after a backslash). Always converted to two 3 byte * long sequence. */ length += 2 * 3; status_flags = LEXER_LIT_LOCATION_HAS_ESCAPE; source_p += 4; raw_length_adjust += 2; column++; continue; } else if (*source_p == LIT_CHAR_TAB) { column = align_column_to_tab (column); /* Subtract -1 because column is increased below. */ column--; } else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p)) { source_p += 3; length += 3; line++; column = 1; continue; } else if (str_end_character == LIT_CHAR_GRAVE_ACCENT) { /* Newline (without backslash) is part of the string. Note: ECMAScript v6, 11.8.6.1 or are both normalized to */ if (*source_p == LIT_CHAR_CR) { status_flags = LEXER_LIT_LOCATION_HAS_ESCAPE; source_p++; length++; if (source_p < source_end_p && *source_p == LIT_CHAR_LF) { source_p++; raw_length_adjust--; } line++; column = 1; continue; } else if (*source_p == LIT_CHAR_LF) { source_p++; length++; line++; column = 1; continue; } } else if (*source_p == LIT_CHAR_CR || *source_p == LIT_CHAR_LF) { context_p->token.line = line; context_p->token.column = column; parser_raise_error (context_p, PARSER_ERR_NEWLINE_NOT_ALLOWED); } source_p++; column++; length++; while (source_p < source_end_p && IS_UTF8_INTERMEDIATE_OCTET (*source_p)) { source_p++; length++; } } if (opts & LEXER_STRING_RAW) { length = (size_t) ((source_p - string_start_p) + raw_length_adjust); } if (length > PARSER_MAXIMUM_STRING_LENGTH) { parser_raise_error (context_p, PARSER_ERR_STRING_TOO_LONG); } context_p->token.type = ((str_end_character != LIT_CHAR_GRAVE_ACCENT) ? LEXER_LITERAL : LEXER_TEMPLATE_LITERAL); /* Fill literal data. */ context_p->token.lit_location.char_p = string_start_p; context_p->token.lit_location.length = (prop_length_t) length; context_p->token.lit_location.type = LEXER_STRING_LITERAL; context_p->token.lit_location.status_flags = (uint8_t) status_flags; context_p->source_p = source_p + 1; context_p->line = line; context_p->column = (parser_line_counter_t) (column + 1); } /* lexer_parse_string */ /** * Check number */ static void lexer_check_numbers (parser_context_t *context_p, /**< context */ const uint8_t **source_p, /**< source_pointer */ const uint8_t *source_end_p, /**< end of the source */ const ecma_char_t digit_max, /**< maximum of the number range */ const bool is_legacy) /**< is legacy octal number */ { while (true) { while (*source_p < source_end_p && *source_p[0] >= LIT_CHAR_0 && *source_p[0] <= digit_max) { *source_p += 1; } if (*source_p != source_end_p && *source_p[0] == LIT_CHAR_UNDERSCORE) { *source_p += 1; if (is_legacy || *source_p == source_end_p || *source_p[0] == LIT_CHAR_UNDERSCORE || *source_p[0] > digit_max || *source_p[0] < LIT_CHAR_0) { parser_raise_error (context_p, PARSER_ERR_INVALID_UNDERSCORE_IN_NUMBER); } continue; } break; } } /* lexer_check_numbers */ /** * Parse number. */ static void lexer_parse_number (parser_context_t *context_p) /**< context */ { const uint8_t *source_p = context_p->source_p; const uint8_t *source_end_p = context_p->source_end_p; bool can_be_float = false; #if JERRY_BUILTIN_BIGINT bool can_be_bigint = true; #endif /* JERRY_BUILTIN_BIGINT */ size_t length; context_p->token.type = LEXER_LITERAL; context_p->token.extra_value = LEXER_NUMBER_DECIMAL; context_p->token.lit_location.char_p = source_p; context_p->token.lit_location.type = LEXER_NUMBER_LITERAL; context_p->token.lit_location.status_flags = LEXER_LIT_LOCATION_IS_ASCII; if (source_p[0] == LIT_CHAR_0 && source_p + 1 < source_end_p) { if (source_p[1] == LIT_CHAR_UNDERSCORE) { parser_raise_error (context_p, PARSER_ERR_INVALID_UNDERSCORE_IN_NUMBER); } if (LEXER_TO_ASCII_LOWERCASE (source_p[1]) == LIT_CHAR_LOWERCASE_X) { context_p->token.extra_value = LEXER_NUMBER_HEXADECIMAL; source_p += 2; if (source_p >= source_end_p || !lit_char_is_hex_digit (source_p[0])) { parser_raise_error (context_p, PARSER_ERR_INVALID_HEX_DIGIT); } do { source_p++; if (source_p < source_end_p && source_p[0] == LIT_CHAR_UNDERSCORE) { source_p++; if (source_p == source_end_p || !lit_char_is_hex_digit (source_p[0])) { parser_raise_error (context_p, PARSER_ERR_INVALID_UNDERSCORE_IN_NUMBER); } } } while (source_p < source_end_p && lit_char_is_hex_digit (source_p[0])); } else if (LEXER_TO_ASCII_LOWERCASE (source_p[1]) == LIT_CHAR_LOWERCASE_O) { context_p->token.extra_value = LEXER_NUMBER_OCTAL; source_p += 2; if (source_p >= source_end_p || !lit_char_is_octal_digit (source_p[0])) { parser_raise_error (context_p, PARSER_ERR_INVALID_OCTAL_DIGIT); } lexer_check_numbers (context_p, &source_p, source_end_p, LIT_CHAR_7, false); } else if (source_p[1] >= LIT_CHAR_0 && source_p[1] <= LIT_CHAR_9) { context_p->token.extra_value = LEXER_NUMBER_OCTAL; #if JERRY_BUILTIN_BIGINT can_be_bigint = false; #endif /* JERRY_BUILTIN_BIGINT */ if (context_p->status_flags & PARSER_IS_STRICT) { parser_raise_error (context_p, PARSER_ERR_OCTAL_NUMBER_NOT_ALLOWED); } lexer_check_numbers (context_p, &source_p, source_end_p, LIT_CHAR_7, true); if (source_p < source_end_p && source_p[0] >= LIT_CHAR_8 && source_p[0] <= LIT_CHAR_9) { lexer_check_numbers (context_p, &source_p, source_end_p, LIT_CHAR_9, true); context_p->token.extra_value = LEXER_NUMBER_DECIMAL; } } else if (LEXER_TO_ASCII_LOWERCASE (source_p[1]) == LIT_CHAR_LOWERCASE_B) { context_p->token.extra_value = LEXER_NUMBER_BINARY; source_p += 2; if (source_p >= source_end_p || !lit_char_is_binary_digit (source_p[0])) { parser_raise_error (context_p, PARSER_ERR_INVALID_BIN_DIGIT); } do { source_p++; if (source_p < source_end_p && source_p[0] == LIT_CHAR_UNDERSCORE) { source_p++; if (source_p == source_end_p || source_p[0] > LIT_CHAR_9 || source_p[0] < LIT_CHAR_0) { parser_raise_error (context_p, PARSER_ERR_INVALID_UNDERSCORE_IN_NUMBER); } } } while (source_p < source_end_p && lit_char_is_binary_digit (source_p[0])); } else { can_be_float = true; source_p++; } } else { lexer_check_numbers (context_p, &source_p, source_end_p, LIT_CHAR_9, false); can_be_float = true; } if (can_be_float) { if (source_p < source_end_p && source_p[0] == LIT_CHAR_DOT) { source_p++; #if JERRY_BUILTIN_BIGINT can_be_bigint = false; #endif /* JERRY_BUILTIN_BIGINT */ if (source_p < source_end_p && source_p[0] == LIT_CHAR_UNDERSCORE) { parser_raise_error (context_p, PARSER_ERR_INVALID_UNDERSCORE_IN_NUMBER); } lexer_check_numbers (context_p, &source_p, source_end_p, LIT_CHAR_9, false); } if (source_p < source_end_p && LEXER_TO_ASCII_LOWERCASE (source_p[0]) == LIT_CHAR_LOWERCASE_E) { source_p++; #if JERRY_BUILTIN_BIGINT can_be_bigint = false; #endif /* JERRY_BUILTIN_BIGINT */ if (source_p < source_end_p && (source_p[0] == LIT_CHAR_PLUS || source_p[0] == LIT_CHAR_MINUS)) { source_p++; } if (source_p >= source_end_p || source_p[0] < LIT_CHAR_0 || source_p[0] > LIT_CHAR_9) { parser_raise_error (context_p, PARSER_ERR_MISSING_EXPONENT); } lexer_check_numbers (context_p, &source_p, source_end_p, LIT_CHAR_9, false); } } #if JERRY_BUILTIN_BIGINT if (source_p < source_end_p && source_p[0] == LIT_CHAR_LOWERCASE_N) { if (!can_be_bigint) { parser_raise_error (context_p, PARSER_ERR_INVALID_BIGINT); } context_p->token.extra_value = LEXER_NUMBER_BIGINT; source_p++; } #endif /* JERRY_BUILTIN_BIGINT */ length = (size_t) (source_p - context_p->source_p); if (length > PARSER_MAXIMUM_STRING_LENGTH) { parser_raise_error (context_p, PARSER_ERR_NUMBER_TOO_LONG); } context_p->token.lit_location.length = (prop_length_t) length; PARSER_PLUS_EQUAL_LC (context_p->column, length); context_p->source_p = source_p; if (source_p < source_end_p && lexer_parse_identifier (context_p, LEXER_PARSE_CHECK_START_AND_RETURN)) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_AFTER_NUMBER); } } /* lexer_parse_number */ /** * One character long token (e.g. comma). * * @param char1 character * @param type1 type */ #define LEXER_TYPE_A_TOKEN(char1, type1) \ case (uint8_t) (char1): \ { \ context_p->token.type = (type1); \ length = 1; \ break; \ } /** * Token pair, where the first token is prefix of the second (e.g. % and %=). * * @param char1 first character * @param type1 type of the first character * @param char2 second character * @param type2 type of the second character */ #define LEXER_TYPE_B_TOKEN(char1, type1, char2, type2) \ case (uint8_t) (char1): \ { \ if (length >= 2 && context_p->source_p[1] == (uint8_t) (char2)) \ { \ context_p->token.type = (type2); \ length = 2; \ break; \ } \ \ context_p->token.type = (type1); \ length = 1; \ break; \ } /** * Three tokens, where the first is the prefix of the other two (e.g. &, &&, &=). * * @param char1 first character * @param type1 type of the first character * @param char2 second character * @param type2 type of the second character * @param char3 third character * @param type3 type of the third character */ #define LEXER_TYPE_C_TOKEN(char1, type1, char2, type2, char3, type3) \ case (uint8_t) (char1): \ { \ if (length >= 2) \ { \ if (context_p->source_p[1] == (uint8_t) (char2)) \ { \ context_p->token.type = (type2); \ length = 2; \ break; \ } \ \ if (context_p->source_p[1] == (uint8_t) (char3)) \ { \ context_p->token.type = (type3); \ length = 2; \ break; \ } \ } \ \ context_p->token.type = (type1); \ length = 1; \ break; \ } /** * Four tokens, where the first is the prefix of the other three * and the second is prefix of the fourth (e.g. &, &&, &=, &&= ). * * @param char1 first character * @param type1 type of the first character * @param char2 second character * @param type2 type of the second character * @param char3 third character * @param type3 type of the third character * @param char4 fourth character * @param type4 type of the fourth character */ #define LEXER_TYPE_D_TOKEN(char1, type1, char2, type2, char3, type3, char4, type4) \ case (uint8_t) (char1): \ { \ if (length >= 2) \ { \ if (context_p->source_p[1] == (uint8_t) (char2)) \ { \ context_p->token.type = (type2); \ length = 2; \ break; \ } \ \ if (context_p->source_p[1] == (uint8_t) (char3)) \ { \ if (length >= 3 && context_p->source_p[2] == (uint8_t) (char4)) \ { \ context_p->token.type = (type4); \ length = 3; \ break; \ } \ context_p->token.type = (type3); \ length = 2; \ break; \ } \ } \ \ context_p->token.type = (type1); \ length = 1; \ break; \ } /** * Get next token. */ void lexer_next_token (parser_context_t *context_p) /**< context */ { size_t length; lexer_skip_spaces (context_p); context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; length = (size_t) (context_p->source_end_p - context_p->source_p); if (length == 0) { context_p->token.type = LEXER_EOS; return; } if (lexer_parse_identifier (context_p, LEXER_PARSE_CHECK_KEYWORDS)) { return; } if (context_p->source_p[0] >= LIT_CHAR_0 && context_p->source_p[0] <= LIT_CHAR_9) { lexer_parse_number (context_p); return; } switch (context_p->source_p[0]) { LEXER_TYPE_A_TOKEN (LIT_CHAR_LEFT_BRACE, LEXER_LEFT_BRACE); LEXER_TYPE_A_TOKEN (LIT_CHAR_LEFT_PAREN, LEXER_LEFT_PAREN); LEXER_TYPE_A_TOKEN (LIT_CHAR_LEFT_SQUARE, LEXER_LEFT_SQUARE); LEXER_TYPE_A_TOKEN (LIT_CHAR_RIGHT_BRACE, LEXER_RIGHT_BRACE); LEXER_TYPE_A_TOKEN (LIT_CHAR_RIGHT_PAREN, LEXER_RIGHT_PAREN); LEXER_TYPE_A_TOKEN (LIT_CHAR_RIGHT_SQUARE, LEXER_RIGHT_SQUARE); LEXER_TYPE_A_TOKEN (LIT_CHAR_SEMICOLON, LEXER_SEMICOLON); LEXER_TYPE_A_TOKEN (LIT_CHAR_COMMA, LEXER_COMMA); LEXER_TYPE_A_TOKEN (LIT_CHAR_HASHMARK, LEXER_HASHMARK); case (uint8_t) LIT_CHAR_DOT: { if (length >= 2 && (context_p->source_p[1] >= LIT_CHAR_0 && context_p->source_p[1] <= LIT_CHAR_9)) { lexer_parse_number (context_p); return; } if (length >= 3 && context_p->source_p[1] == LIT_CHAR_DOT && context_p->source_p[2] == LIT_CHAR_DOT) { context_p->token.type = LEXER_THREE_DOTS; length = 3; break; } context_p->token.type = LEXER_DOT; length = 1; break; } case (uint8_t) LIT_CHAR_LESS_THAN: { if (length >= 2) { if (context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_LESS_EQUAL; length = 2; break; } if (context_p->source_p[1] == (uint8_t) LIT_CHAR_LESS_THAN) { if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_ASSIGN_LEFT_SHIFT; length = 3; break; } context_p->token.type = LEXER_LEFT_SHIFT; length = 2; break; } } context_p->token.type = LEXER_LESS; length = 1; break; } case (uint8_t) LIT_CHAR_GREATER_THAN: { if (length >= 2) { if (context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_GREATER_EQUAL; length = 2; break; } if (context_p->source_p[1] == (uint8_t) LIT_CHAR_GREATER_THAN) { if (length >= 3) { if (context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_ASSIGN_RIGHT_SHIFT; length = 3; break; } if (context_p->source_p[2] == (uint8_t) LIT_CHAR_GREATER_THAN) { if (length >= 4 && context_p->source_p[3] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_ASSIGN_UNS_RIGHT_SHIFT; length = 4; break; } context_p->token.type = LEXER_UNS_RIGHT_SHIFT; length = 3; break; } } context_p->token.type = LEXER_RIGHT_SHIFT; length = 2; break; } } context_p->token.type = LEXER_GREATER; length = 1; break; } case (uint8_t) LIT_CHAR_EQUALS: { if (length >= 2) { if (context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS) { if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_STRICT_EQUAL; length = 3; break; } context_p->token.type = LEXER_EQUAL; length = 2; break; } if (context_p->source_p[1] == (uint8_t) LIT_CHAR_GREATER_THAN) { context_p->token.type = LEXER_ARROW; length = 2; break; } } context_p->token.type = LEXER_ASSIGN; length = 1; break; } case (uint8_t) LIT_CHAR_EXCLAMATION: { if (length >= 2 && context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS) { if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_STRICT_NOT_EQUAL; length = 3; break; } context_p->token.type = LEXER_NOT_EQUAL; length = 2; break; } context_p->token.type = LEXER_LOGICAL_NOT; length = 1; break; } LEXER_TYPE_C_TOKEN (LIT_CHAR_PLUS, LEXER_ADD, LIT_CHAR_EQUALS, LEXER_ASSIGN_ADD, LIT_CHAR_PLUS, LEXER_INCREASE) LEXER_TYPE_C_TOKEN (LIT_CHAR_MINUS, LEXER_SUBTRACT, LIT_CHAR_EQUALS, LEXER_ASSIGN_SUBTRACT, LIT_CHAR_MINUS, LEXER_DECREASE) case (uint8_t) LIT_CHAR_ASTERISK: { if (length >= 2) { if (context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_ASSIGN_MULTIPLY; length = 2; break; } if (context_p->source_p[1] == (uint8_t) LIT_CHAR_ASTERISK) { if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_ASSIGN_EXPONENTIATION; length = 3; break; } context_p->token.type = LEXER_EXPONENTIATION; length = 2; break; } } context_p->token.type = LEXER_MULTIPLY; length = 1; break; } LEXER_TYPE_B_TOKEN (LIT_CHAR_SLASH, LEXER_DIVIDE, LIT_CHAR_EQUALS, LEXER_ASSIGN_DIVIDE) LEXER_TYPE_B_TOKEN (LIT_CHAR_PERCENT, LEXER_MODULO, LIT_CHAR_EQUALS, LEXER_ASSIGN_MODULO) LEXER_TYPE_D_TOKEN (LIT_CHAR_AMPERSAND, LEXER_BIT_AND, LIT_CHAR_EQUALS, LEXER_ASSIGN_BIT_AND, LIT_CHAR_AMPERSAND, LEXER_LOGICAL_AND, LIT_CHAR_EQUALS, LEXER_ASSIGN_LOGICAL_AND) LEXER_TYPE_D_TOKEN (LIT_CHAR_VLINE, LEXER_BIT_OR, LIT_CHAR_EQUALS, LEXER_ASSIGN_BIT_OR, LIT_CHAR_VLINE, LEXER_LOGICAL_OR, LIT_CHAR_EQUALS, LEXER_ASSIGN_LOGICAL_OR) LEXER_TYPE_B_TOKEN (LIT_CHAR_CIRCUMFLEX, LEXER_BIT_XOR, LIT_CHAR_EQUALS, LEXER_ASSIGN_BIT_XOR) LEXER_TYPE_A_TOKEN (LIT_CHAR_TILDE, LEXER_BIT_NOT); case (uint8_t) (LIT_CHAR_QUESTION): { if (length >= 2) { if (context_p->source_p[1] == (uint8_t) LIT_CHAR_QUESTION) { if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS) { context_p->token.type = LEXER_ASSIGN_NULLISH_COALESCING; length = 3; break; } context_p->token.type = LEXER_NULLISH_COALESCING; length = 2; break; } } context_p->token.type = LEXER_QUESTION_MARK; length = 1; break; } LEXER_TYPE_A_TOKEN (LIT_CHAR_COLON, LEXER_COLON); case LIT_CHAR_SINGLE_QUOTE: case LIT_CHAR_DOUBLE_QUOTE: case LIT_CHAR_GRAVE_ACCENT: { lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); return; } default: { parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); } } context_p->source_p += length; PARSER_PLUS_EQUAL_LC (context_p->column, length); } /* lexer_next_token */ #undef LEXER_TYPE_A_TOKEN #undef LEXER_TYPE_B_TOKEN #undef LEXER_TYPE_C_TOKEN #undef LEXER_TYPE_D_TOKEN /** * Checks whether the next token starts with the specified character. * * @return true - if the next is the specified character * false - otherwise */ bool lexer_check_next_character (parser_context_t *context_p, /**< context */ lit_utf8_byte_t character) /**< specified character */ { if (!(context_p->token.flags & LEXER_NO_SKIP_SPACES)) { lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); } return (context_p->source_p < context_p->source_end_p && context_p->source_p[0] == (uint8_t) character); } /* lexer_check_next_character */ /** * Checks whether the next token starts with either specified characters. * * @return true - if the next is the specified character * false - otherwise */ bool lexer_check_next_characters (parser_context_t *context_p, /**< context */ lit_utf8_byte_t character1, /**< first alternative character */ lit_utf8_byte_t character2) /**< second alternative character */ { if (!(context_p->token.flags & LEXER_NO_SKIP_SPACES)) { lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); } return (context_p->source_p < context_p->source_end_p && (context_p->source_p[0] == (uint8_t) character1 || context_p->source_p[0] == (uint8_t) character2)); } /* lexer_check_next_characters */ /** * Consumes the next character. The character cannot be a white space. * * @return consumed character */ uint8_t lexer_consume_next_character (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->source_p < context_p->source_end_p); context_p->token.flags &= (uint8_t) ~LEXER_NO_SKIP_SPACES; PARSER_PLUS_EQUAL_LC (context_p->column, 1); return *context_p->source_p++; } /* lexer_consume_next_character */ /** * Checks whether the next character can be the start of a post primary expression * * Note: * the result is not precise, but this imprecise result * has no side effects for negating number literals * * @return true if the next character can be the start of a post primary expression */ bool lexer_check_post_primary_exp (parser_context_t *context_p) /**< context */ { if (!(context_p->token.flags & LEXER_NO_SKIP_SPACES)) { lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); } if (context_p->source_p >= context_p->source_end_p) { return false; } switch (context_p->source_p[0]) { case LIT_CHAR_DOT: case LIT_CHAR_LEFT_PAREN: case LIT_CHAR_LEFT_SQUARE: case LIT_CHAR_GRAVE_ACCENT: { return true; } case LIT_CHAR_PLUS: case LIT_CHAR_MINUS: { return (!(context_p->token.flags & LEXER_WAS_NEWLINE) && context_p->source_p + 1 < context_p->source_end_p && context_p->source_p[1] == context_p->source_p[0]); } case LIT_CHAR_ASTERISK: { return (context_p->source_p + 1 < context_p->source_end_p && context_p->source_p[1] == (uint8_t) LIT_CHAR_ASTERISK); } } return false; } /* lexer_check_post_primary_exp */ /** * Checks whether the next token is a type used for detecting arrow functions. * * @return true if the next token is an arrow token */ bool lexer_check_arrow (parser_context_t *context_p) /**< context */ { if (!(context_p->token.flags & LEXER_NO_SKIP_SPACES)) { lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); } return (!(context_p->token.flags & LEXER_WAS_NEWLINE) && context_p->source_p + 2 <= context_p->source_end_p && context_p->source_p[0] == (uint8_t) LIT_CHAR_EQUALS && context_p->source_p[1] == (uint8_t) LIT_CHAR_GREATER_THAN); } /* lexer_check_arrow */ /** * Checks whether the next token is a comma or equal sign. * * @return true if the next token is a comma or equal sign */ bool lexer_check_arrow_param (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.flags & LEXER_NO_SKIP_SPACES); if (context_p->source_p >= context_p->source_end_p) { return false; } if (context_p->source_p[0] == LIT_CHAR_COMMA) { return true; } if (context_p->source_p[0] != LIT_CHAR_EQUALS) { return false; } return (context_p->source_p + 1 >= context_p->source_end_p || context_p->source_p[1] != LIT_CHAR_EQUALS); } /* lexer_check_arrow_param */ /** * Checks whether the yield expression has no argument. * * @return true if it has no argument */ bool lexer_check_yield_no_arg (parser_context_t *context_p) /**< context */ { if (context_p->token.flags & LEXER_WAS_NEWLINE) { return true; } switch (context_p->token.type) { case LEXER_RIGHT_BRACE: case LEXER_RIGHT_PAREN: case LEXER_RIGHT_SQUARE: case LEXER_COMMA: case LEXER_COLON: case LEXER_SEMICOLON: case LEXER_EOS: { return true; } default: { return false; } } } /* lexer_check_yield_no_arg */ /** * Checks whether the next token is a multiply and consumes it. * * @return true if the next token is a multiply */ bool lexer_consume_generator (parser_context_t *context_p) /**< context */ { if (!(context_p->token.flags & LEXER_NO_SKIP_SPACES)) { lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); } if (context_p->source_p >= context_p->source_end_p || context_p->source_p[0] != LIT_CHAR_ASTERISK || (context_p->source_p + 1 < context_p->source_end_p && (context_p->source_p[1] == LIT_CHAR_EQUALS || context_p->source_p[1] == LIT_CHAR_ASTERISK))) { return false; } lexer_consume_next_character (context_p); context_p->token.type = LEXER_MULTIPLY; return true; } /* lexer_consume_generator */ /** * Checks whether the next token is an equal sign and consumes it. * * @return true if the next token is an equal sign */ bool lexer_consume_assign (parser_context_t *context_p) /**< context */ { if (!(context_p->token.flags & LEXER_NO_SKIP_SPACES)) { lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); } if (context_p->source_p >= context_p->source_end_p || context_p->source_p[0] != LIT_CHAR_EQUALS || (context_p->source_p + 1 < context_p->source_end_p && (context_p->source_p[1] == LIT_CHAR_EQUALS || context_p->source_p[1] == LIT_CHAR_GREATER_THAN))) { return false; } lexer_consume_next_character (context_p); context_p->token.type = LEXER_ASSIGN; return true; } /* lexer_consume_assign */ /** * Update await / yield keywords after an arrow function with expression. */ void lexer_update_await_yield (parser_context_t *context_p, /**< context */ uint32_t status_flags) /**< parser status flags after restore */ { if (!(status_flags & PARSER_IS_STRICT)) { if (status_flags & PARSER_IS_GENERATOR_FUNCTION) { if (context_p->token.type == LEXER_LITERAL && context_p->token.keyword_type == LEXER_KEYW_YIELD) { context_p->token.type = LEXER_KEYW_YIELD; } } else { if (context_p->token.type == LEXER_KEYW_YIELD) { JERRY_ASSERT (context_p->token.keyword_type == LEXER_KEYW_YIELD); context_p->token.type = LEXER_LITERAL; } } } if (!(context_p->global_status_flags & ECMA_PARSE_MODULE)) { if (status_flags & PARSER_IS_ASYNC_FUNCTION) { if (context_p->token.type == LEXER_LITERAL && context_p->token.keyword_type == LEXER_KEYW_AWAIT) { context_p->token.type = LEXER_KEYW_AWAIT; } } else { if (context_p->token.type == LEXER_KEYW_AWAIT) { JERRY_ASSERT (context_p->token.keyword_type == LEXER_KEYW_AWAIT); context_p->token.type = LEXER_LITERAL; } } } } /* lexer_update_await_yield */ /** * Read next token without skipping whitespaces and checking keywords * * @return true if the next literal is private identifier, false otherwise */ bool lexer_scan_private_identifier (parser_context_t *context_p) /**< context */ { context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; return (context_p->source_p < context_p->source_end_p && lexer_parse_identifier (context_p, LEXER_PARSE_NO_OPTS)); } /* lexer_scan_private_identifier */ /** * Convert an ident with escapes to a utf8 string. */ void lexer_convert_ident_to_cesu8 (uint8_t *destination_p, /**< destination string */ const uint8_t *source_p, /**< source string */ prop_length_t length) /**< length of destination string */ { const uint8_t *destination_end_p = destination_p + length; JERRY_ASSERT (length <= PARSER_MAXIMUM_IDENT_LENGTH); do { if (*source_p == LIT_CHAR_BACKSLASH) { source_p += 2; destination_p += lit_code_point_to_cesu8_bytes (destination_p, lexer_unchecked_hex_to_character (&source_p)); continue; } if (*source_p >= LIT_UTF8_4_BYTE_MARKER) { lit_four_byte_utf8_char_to_cesu8 (destination_p, source_p); destination_p += 6; source_p += 4; continue; } *destination_p++ = *source_p++; } while (destination_p < destination_end_p); } /* lexer_convert_ident_to_cesu8 */ /** * Convert literal to character sequence */ const uint8_t * lexer_convert_literal_to_chars (parser_context_t *context_p, /**< context */ const lexer_lit_location_t *literal_p, /**< literal location */ uint8_t *local_byte_array_p, /**< local byte array to store chars */ lexer_string_options_t opts) /**< options */ { JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL); if (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { return literal_p->char_p; } uint8_t *destination_start_p; if (literal_p->length > LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE) { context_p->u.allocated_buffer_p = (uint8_t *) parser_malloc_local (context_p, literal_p->length); context_p->allocated_buffer_size = literal_p->length; destination_start_p = (uint8_t*) context_p->u.allocated_buffer_p; } else { destination_start_p = local_byte_array_p; } if (literal_p->type == LEXER_IDENT_LITERAL) { lexer_convert_ident_to_cesu8 (destination_start_p, literal_p->char_p, literal_p->length); return destination_start_p; } const uint8_t *source_p = literal_p->char_p; uint8_t *destination_p = destination_start_p; uint8_t str_end_character = source_p[-1]; if (str_end_character == LIT_CHAR_RIGHT_BRACE) { str_end_character = LIT_CHAR_GRAVE_ACCENT; } bool is_raw = (opts & LEXER_STRING_RAW) != 0; while (true) { if (*source_p == str_end_character) { break; } if (*source_p == LIT_CHAR_BACKSLASH && !is_raw) { uint8_t conv_character; source_p++; JERRY_ASSERT (source_p < context_p->source_end_p); /* Newline is ignored. */ if (*source_p == LIT_CHAR_CR) { source_p++; JERRY_ASSERT (source_p < context_p->source_end_p); if (*source_p == LIT_CHAR_LF) { source_p++; } continue; } else if (*source_p == LIT_CHAR_LF) { source_p++; continue; } else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p)) { source_p += 3; continue; } if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_3) { lit_code_point_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0); source_p++; JERRY_ASSERT (source_p < context_p->source_end_p); if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7) { octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0); source_p++; JERRY_ASSERT (source_p < context_p->source_end_p); if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7) { octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0); source_p++; JERRY_ASSERT (source_p < context_p->source_end_p); } } destination_p += lit_code_point_to_cesu8_bytes (destination_p, octal_number); continue; } if (*source_p >= LIT_CHAR_4 && *source_p <= LIT_CHAR_7) { uint32_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0); source_p++; JERRY_ASSERT (source_p < context_p->source_end_p); if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7) { octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0); source_p++; JERRY_ASSERT (source_p < context_p->source_end_p); } *destination_p++ = (uint8_t) octal_number; continue; } if (*source_p == LIT_CHAR_LOWERCASE_X || *source_p == LIT_CHAR_LOWERCASE_U) { source_p++; destination_p += lit_code_point_to_cesu8_bytes (destination_p, lexer_unchecked_hex_to_character (&source_p)); continue; } conv_character = *source_p; switch (*source_p) { case LIT_CHAR_LOWERCASE_B: { conv_character = 0x08; break; } case LIT_CHAR_LOWERCASE_T: { conv_character = 0x09; break; } case LIT_CHAR_LOWERCASE_N: { conv_character = 0x0a; break; } case LIT_CHAR_LOWERCASE_V: { conv_character = 0x0b; break; } case LIT_CHAR_LOWERCASE_F: { conv_character = 0x0c; break; } case LIT_CHAR_LOWERCASE_R: { conv_character = 0x0d; break; } } if (conv_character != *source_p) { *destination_p++ = conv_character; source_p++; continue; } } else if (str_end_character == LIT_CHAR_GRAVE_ACCENT) { if (source_p[0] == LIT_CHAR_DOLLAR_SIGN && source_p[1] == LIT_CHAR_LEFT_BRACE) { source_p++; JERRY_ASSERT (source_p < context_p->source_end_p); break; } if (*source_p == LIT_CHAR_CR) { *destination_p++ = LIT_CHAR_LF; source_p++; if (*source_p != str_end_character && *source_p == LIT_CHAR_LF) { source_p++; } continue; } if ((*source_p == LIT_CHAR_BACKSLASH) && is_raw) { JERRY_ASSERT (source_p + 1 < context_p->source_end_p); if ((*(source_p + 1) == LIT_CHAR_GRAVE_ACCENT) || (*(source_p + 1) == LIT_CHAR_BACKSLASH)) { *destination_p++ = *source_p++; *destination_p++ = *source_p++; continue; } } } if (*source_p >= LIT_UTF8_4_BYTE_MARKER) { /* Processing 4 byte unicode sequence (even if it is * after a backslash). Always converted to two 3 byte * long sequence. */ lit_four_byte_utf8_char_to_cesu8 (destination_p, source_p); destination_p += 6; source_p += 4; continue; } *destination_p++ = *source_p++; /* There is no need to check the source_end_p * since the string is terminated by a quotation mark. */ while (IS_UTF8_INTERMEDIATE_OCTET (*source_p)) { *destination_p++ = *source_p++; } } JERRY_ASSERT (destination_p == destination_start_p + literal_p->length); return destination_start_p; } /* lexer_convert_literal_to_chars */ /** * Construct an unused literal. * * @return a newly allocated literal */ lexer_literal_t * lexer_construct_unused_literal (parser_context_t *context_p) /**< context */ { lexer_literal_t *literal_p; if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) { parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->type = LEXER_UNUSED_LITERAL; literal_p->status_flags = 0; return literal_p; } /* lexer_construct_unused_literal */ /** * Construct a literal object from an identifier. */ void lexer_construct_literal_object (parser_context_t *context_p, /**< context */ const lexer_lit_location_t *lit_location_p, /**< literal location */ uint8_t literal_type) /**< final literal type */ { uint8_t local_byte_array[LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE]; const uint8_t *char_p = lexer_convert_literal_to_chars (context_p, lit_location_p, local_byte_array, LEXER_STRING_NO_OPTS); size_t length = lit_location_p->length; parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; uint32_t literal_index = 0; bool search_scope_stack = (literal_type == LEXER_IDENT_LITERAL); if (JERRY_UNLIKELY (literal_type == LEXER_NEW_IDENT_LITERAL)) { literal_type = LEXER_IDENT_LITERAL; } JERRY_ASSERT (literal_type == LEXER_IDENT_LITERAL || literal_type == LEXER_STRING_LITERAL); JERRY_ASSERT (literal_type != LEXER_IDENT_LITERAL || length <= PARSER_MAXIMUM_IDENT_LENGTH); JERRY_ASSERT (literal_type != LEXER_STRING_LITERAL || length <= PARSER_MAXIMUM_STRING_LENGTH); parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (literal_p->type == literal_type && literal_p->prop.length == length && memcmp (literal_p->u.char_p, char_p, length) == 0) { context_p->lit_object.literal_p = literal_p; context_p->lit_object.index = (uint16_t) literal_index; parser_free_allocated_buffer (context_p); if (search_scope_stack) { parser_scope_stack_t *scope_stack_start_p = context_p->scope_stack_p; parser_scope_stack_t *scope_stack_p = scope_stack_start_p + context_p->scope_stack_top; while (scope_stack_p > scope_stack_start_p) { scope_stack_p--; if (scope_stack_p->map_from == literal_index) { JERRY_ASSERT (scanner_decode_map_to (scope_stack_p) >= PARSER_REGISTER_START || (literal_p->status_flags & LEXER_FLAG_USED)); context_p->lit_object.index = scanner_decode_map_to (scope_stack_p); return; } } literal_p->status_flags |= LEXER_FLAG_USED; } return; } literal_index++; } JERRY_ASSERT (literal_index == context_p->literal_count); if (literal_index >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) { parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->prop.length = (prop_length_t) length; literal_p->type = literal_type; uint8_t status_flags = LEXER_FLAG_SOURCE_PTR; if (length > 0 && char_p == local_byte_array) { literal_p->u.char_p = (uint8_t *) jmem_heap_alloc_block (length); memcpy ((uint8_t *) literal_p->u.char_p, char_p, length); status_flags = 0; } else { literal_p->u.char_p = char_p; /* Buffer is taken over when a new literal is constructed. */ if (context_p->u.allocated_buffer_p != NULL) { JERRY_ASSERT (char_p == context_p->u.allocated_buffer_p); context_p->u.allocated_buffer_p = NULL; status_flags = 0; } } if (search_scope_stack) { status_flags |= LEXER_FLAG_USED; } if (lit_location_p->status_flags & LEXER_LIT_LOCATION_IS_ASCII) { literal_p->status_flags |= LEXER_FLAG_ASCII; } literal_p->status_flags = status_flags; context_p->lit_object.literal_p = literal_p; context_p->lit_object.index = (uint16_t) literal_index; context_p->literal_count++; JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL); } /* lexer_construct_literal_object */ /** * Construct a number object. * * @return true if number is small number */ bool lexer_construct_number_object (parser_context_t *context_p, /**< context */ bool is_expr, /**< expression is parsed */ bool is_negative_number) /**< sign is negative */ { parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; ecma_value_t lit_value; uint32_t literal_index = 0; prop_length_t length = context_p->token.lit_location.length; #if JERRY_BUILTIN_BIGINT if (JERRY_LIKELY (context_p->token.extra_value != LEXER_NUMBER_BIGINT)) { #endif /* JERRY_BUILTIN_BIGINT */ ecma_number_t num; uint32_t options = ECMA_CONVERSION_ALLOW_UNDERSCORE; if (context_p->token.extra_value == LEXER_NUMBER_OCTAL) { num = ecma_utf8_string_to_number_by_radix (context_p->token.lit_location.char_p, length, 8, options); } else { num = ecma_utf8_string_to_number (context_p->token.lit_location.char_p, length, options); } if (is_expr) { int32_t int_num = (int32_t) num; if (int_num == num && int_num <= CBC_PUSH_NUMBER_BYTE_RANGE_END && (int_num != 0 || !is_negative_number)) { context_p->lit_object.index = (uint16_t) int_num; return true; } } if (is_negative_number) { num = -num; } lit_value = ecma_find_or_create_literal_number (num); #if JERRY_BUILTIN_BIGINT } else { uint32_t options = (ECMA_BIGINT_PARSE_DISALLOW_SYNTAX_ERROR | ECMA_BIGINT_PARSE_DISALLOW_MEMORY_ERROR | ECMA_BIGINT_PARSE_ALLOW_UNDERSCORE); if (is_negative_number) { options |= ECMA_BIGINT_PARSE_SET_NEGATIVE; } JERRY_ASSERT (length >= 2); lit_value = ecma_bigint_parse_string (context_p->token.lit_location.char_p, (lit_utf8_size_t) (length - 1), options); JERRY_ASSERT (lit_value != ECMA_VALUE_FALSE && !ECMA_IS_VALUE_ERROR (lit_value)); if (lit_value == ECMA_VALUE_NULL) { parser_raise_error (context_p, PARSER_ERR_OUT_OF_MEMORY); } lit_value = ecma_find_or_create_literal_bigint (lit_value); } #endif /* JERRY_BUILTIN_BIGINT */ parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (literal_p->type == LEXER_NUMBER_LITERAL && literal_p->u.value == lit_value) { context_p->lit_object.literal_p = literal_p; context_p->lit_object.index = (uint16_t) literal_index; return false; } literal_index++; } JERRY_ASSERT (literal_index == context_p->literal_count); if (literal_index >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) { parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->u.value = lit_value; literal_p->prop.length = 0; /* Unused. */ literal_p->type = LEXER_NUMBER_LITERAL; literal_p->status_flags = 0; context_p->lit_object.literal_p = literal_p; context_p->lit_object.index = (uint16_t) literal_index; context_p->literal_count++; return false; } /* lexer_construct_number_object */ /** * Convert a push number opcode to push literal opcode */ void lexer_convert_push_number_to_push_literal (parser_context_t *context_p) /**< context */ { ecma_integer_value_t value; bool two_literals = context_p->last_cbc_opcode >= CBC_PUSH_LITERAL_PUSH_NUMBER_0; if (context_p->last_cbc_opcode == CBC_PUSH_NUMBER_0 || context_p->last_cbc_opcode == CBC_PUSH_LITERAL_PUSH_NUMBER_0) { value = 0; } else if (context_p->last_cbc_opcode == CBC_PUSH_NUMBER_POS_BYTE || context_p->last_cbc_opcode == CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE) { value = ((ecma_integer_value_t) context_p->last_cbc.value) + 1; } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_NUMBER_NEG_BYTE || context_p->last_cbc_opcode == CBC_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE); value = -((ecma_integer_value_t) context_p->last_cbc.value) - 1; } ecma_value_t lit_value = ecma_make_integer_value (value); parser_list_iterator_t literal_iterator; parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); context_p->last_cbc_opcode = two_literals ? CBC_PUSH_TWO_LITERALS : CBC_PUSH_LITERAL; uint32_t literal_index = 0; lexer_literal_t *literal_p; while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (literal_p->type == LEXER_NUMBER_LITERAL && literal_p->u.value == lit_value) { if (two_literals) { context_p->last_cbc.value = (uint16_t) literal_index; } else { context_p->last_cbc.literal_index = (uint16_t) literal_index; } return; } literal_index++; } JERRY_ASSERT (literal_index == context_p->literal_count); if (literal_index >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) { parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->u.value = lit_value; literal_p->prop.length = 0; /* Unused. */ literal_p->type = LEXER_NUMBER_LITERAL; literal_p->status_flags = 0; context_p->literal_count++; if (two_literals) { context_p->last_cbc.value = (uint16_t) literal_index; } else { context_p->last_cbc.literal_index = (uint16_t) literal_index; } } /* lexer_convert_push_number_to_push_literal */ /** * Construct a function literal object. * * @return function object literal index */ uint16_t lexer_construct_function_object (parser_context_t *context_p, /**< context */ uint32_t extra_status_flags) /**< extra status flags */ { #if (JERRY_STACK_LIMIT != 0) if (JERRY_UNLIKELY (ecma_get_current_stack_usage () > CONFIG_MEM_STACK_LIMIT)) { parser_raise_error (context_p, PARSER_ERR_STACK_OVERFLOW); } #endif /* JERRY_STACK_LIMIT != 0 */ ecma_compiled_code_t *compiled_code_p; lexer_literal_t *literal_p; uint16_t result_index; if (context_p->status_flags & PARSER_INSIDE_WITH) { extra_status_flags |= PARSER_INSIDE_WITH; } literal_p = lexer_construct_unused_literal (context_p); result_index = context_p->literal_count; context_p->literal_count++; parser_flush_cbc (context_p); if (JERRY_LIKELY (!(extra_status_flags & PARSER_IS_ARROW_FUNCTION))) { compiled_code_p = parser_parse_function (context_p, extra_status_flags); } else { compiled_code_p = parser_parse_arrow_function (context_p, extra_status_flags); } literal_p->u.bytecode_p = compiled_code_p; literal_p->type = LEXER_FUNCTION_LITERAL; return result_index; } /* lexer_construct_function_object */ /** * Construct a class static block function literal object. * * @return function object literal index */ uint16_t lexer_construct_class_static_block_function (parser_context_t *context_p) /**< context */ { ecma_compiled_code_t *compiled_code_p; lexer_literal_t *literal_p; uint16_t result_index; literal_p = lexer_construct_unused_literal (context_p); result_index = context_p->literal_count; context_p->literal_count++; parser_flush_cbc (context_p); compiled_code_p = parser_parse_class_static_block (context_p); literal_p->u.bytecode_p = compiled_code_p; literal_p->type = LEXER_FUNCTION_LITERAL; return result_index; } /* lexer_construct_class_static_block_function */ /** * Construct a regular expression object. * * Note: In ESNEXT the constructed literal's type can be LEXER_STRING_LITERAL which represents * invalid pattern. In this case the lit_object's index contains the thrown error message literal. * Otherwise a new literal is appended to the end of the literal pool. */ void lexer_construct_regexp_object (parser_context_t *context_p, /**< context */ bool parse_only) /**< parse only */ { #if JERRY_BUILTIN_REGEXP const uint8_t *source_p = context_p->source_p; const uint8_t *regex_start_p = context_p->source_p; const uint8_t *regex_end_p = regex_start_p; const uint8_t *source_end_p = context_p->source_end_p; parser_line_counter_t column = context_p->column; bool in_class = false; uint16_t current_flags; lit_utf8_size_t length; JERRY_ASSERT (context_p->token.type == LEXER_DIVIDE || context_p->token.type == LEXER_ASSIGN_DIVIDE); if (context_p->token.type == LEXER_ASSIGN_DIVIDE) { regex_start_p--; } while (true) { if (source_p >= source_end_p) { parser_raise_error (context_p, PARSER_ERR_UNTERMINATED_REGEXP); } if (!in_class && source_p[0] == LIT_CHAR_SLASH) { regex_end_p = source_p; source_p++; column++; break; } switch (source_p[0]) { case LIT_CHAR_CR: case LIT_CHAR_LF: case LEXER_NEWLINE_LS_PS_BYTE_1: { if (source_p[0] != LEXER_NEWLINE_LS_PS_BYTE_1 || LEXER_NEWLINE_LS_PS_BYTE_23 (source_p)) { parser_raise_error (context_p, PARSER_ERR_NEWLINE_NOT_ALLOWED); } break; } case LIT_CHAR_TAB: { column = align_column_to_tab (column); /* Subtract -1 because column is increased below. */ column--; break; } case LIT_CHAR_LEFT_SQUARE: { in_class = true; break; } case LIT_CHAR_RIGHT_SQUARE: { in_class = false; break; } case LIT_CHAR_BACKSLASH: { if (source_p + 1 >= source_end_p) { parser_raise_error (context_p, PARSER_ERR_UNTERMINATED_REGEXP); } if (source_p[1] >= 0x20 && source_p[1] <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { source_p++; column++; } } } source_p++; column++; while (source_p < source_end_p && IS_UTF8_INTERMEDIATE_OCTET (source_p[0])) { source_p++; } } current_flags = 0; while (source_p < source_end_p) { uint32_t flag = 0; if (source_p[0] == LIT_CHAR_LOWERCASE_G) { flag = RE_FLAG_GLOBAL; } else if (source_p[0] == LIT_CHAR_LOWERCASE_I) { flag = RE_FLAG_IGNORE_CASE; } else if (source_p[0] == LIT_CHAR_LOWERCASE_M) { flag = RE_FLAG_MULTILINE; } else if (source_p[0] == LIT_CHAR_LOWERCASE_U) { flag = RE_FLAG_UNICODE; } else if (source_p[0] == LIT_CHAR_LOWERCASE_Y) { flag = RE_FLAG_STICKY; } else if (source_p[0] == LIT_CHAR_LOWERCASE_S) { flag = RE_FLAG_DOTALL; } if (flag == 0) { break; } if (current_flags & flag) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_REGEXP_FLAG); } current_flags = (uint16_t) (current_flags | flag); source_p++; column++; } context_p->source_p = source_p; context_p->column = column; if (source_p < source_end_p && lexer_parse_identifier (context_p, LEXER_PARSE_CHECK_PART_AND_RETURN)) { parser_raise_error (context_p, PARSER_ERR_UNKNOWN_REGEXP_FLAG); } length = (lit_utf8_size_t) (regex_end_p - regex_start_p); if (length > PARSER_MAXIMUM_STRING_LENGTH) { parser_raise_error (context_p, PARSER_ERR_REGEXP_TOO_LONG); } context_p->column = column; context_p->source_p = source_p; if (parse_only) { return; } if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) { parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } /* Compile the RegExp literal and store the RegExp bytecode pointer */ ecma_string_t *pattern_str_p = NULL; if (lit_is_valid_cesu8_string (regex_start_p, length)) { pattern_str_p = ecma_new_ecma_string_from_utf8 (regex_start_p, length); } else { JERRY_ASSERT (lit_is_valid_utf8_string (regex_start_p, length, false)); pattern_str_p = ecma_new_ecma_string_from_utf8_converted_to_cesu8 (regex_start_p, length); } re_compiled_code_t *re_bytecode_p = re_compile_bytecode (pattern_str_p, current_flags); ecma_deref_ecma_string (pattern_str_p); if (JERRY_UNLIKELY (re_bytecode_p == NULL)) { parser_raise_error (context_p, PARSER_ERR_INVALID_REGEXP); } lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->u.bytecode_p = (ecma_compiled_code_t *) re_bytecode_p; literal_p->type = LEXER_REGEXP_LITERAL; literal_p->prop.length = (prop_length_t) length; literal_p->status_flags = 0; context_p->token.type = LEXER_LITERAL; context_p->token.lit_location.type = LEXER_REGEXP_LITERAL; context_p->lit_object.literal_p = literal_p; context_p->lit_object.index = context_p->literal_count++; #else /* !JERRY_BUILTIN_REGEXP */ JERRY_UNUSED (parse_only); parser_raise_error (context_p, PARSER_ERR_UNSUPPORTED_REGEXP); #endif /* JERRY_BUILTIN_REGEXP */ } /* lexer_construct_regexp_object */ /** * Next token must be an identifier. */ void lexer_expect_identifier (parser_context_t *context_p, /**< context */ uint8_t literal_type) /**< literal type */ { JERRY_ASSERT (literal_type == LEXER_STRING_LITERAL || literal_type == LEXER_IDENT_LITERAL || literal_type == LEXER_NEW_IDENT_LITERAL); lexer_skip_spaces (context_p); context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; if (context_p->source_p < context_p->source_end_p && lexer_parse_identifier ( context_p, (literal_type != LEXER_STRING_LITERAL ? LEXER_PARSE_CHECK_KEYWORDS : LEXER_PARSE_NO_OPTS))) { if (context_p->token.type == LEXER_LITERAL) { JERRY_ASSERT (context_p->token.lit_location.type == LEXER_IDENT_LITERAL); lexer_construct_literal_object (context_p, &context_p->token.lit_location, literal_type); if (literal_type != LEXER_STRING_LITERAL && (context_p->status_flags & PARSER_IS_STRICT)) { if (context_p->token.keyword_type == LEXER_KEYW_EVAL) { parser_raise_error (context_p, PARSER_ERR_EVAL_NOT_ALLOWED); } else if (context_p->token.keyword_type == LEXER_KEYW_ARGUMENTS) { parser_raise_error (context_p, PARSER_ERR_ARGUMENTS_NOT_ALLOWED); } } return; } } #if JERRY_MODULE_SYSTEM else if (context_p->status_flags & PARSER_MODULE_DEFAULT_CLASS_OR_FUNC) { /* When parsing default exports for modules, it is not required by functions or classes to have identifiers. * In this case we use a synthetic name for them. */ context_p->token.type = LEXER_LITERAL; context_p->token.lit_location = lexer_default_literal; lexer_construct_literal_object (context_p, &context_p->token.lit_location, literal_type); context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_DEFAULT_CLASS_OR_FUNC); return; } #endif /* JERRY_MODULE_SYSTEM */ if (context_p->token.type == LEXER_KEYW_YIELD) { parser_raise_error (context_p, PARSER_ERR_YIELD_NOT_ALLOWED); } if (context_p->token.type == LEXER_KEYW_AWAIT) { parser_raise_error (context_p, PARSER_ERR_AWAIT_NOT_ALLOWED); } parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } /* lexer_expect_identifier */ /** * Next token must be an identifier. */ void lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ uint32_t ident_opts) /**< lexer_obj_ident_opts_t option bits */ { lexer_skip_spaces (context_p); if (context_p->source_p >= context_p->source_end_p) { parser_raise_error (context_p, PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED); } context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; bool create_literal_object = false; JERRY_ASSERT ((ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER) || !(ident_opts & LEXER_OBJ_IDENT_CLASS_NO_STATIC)); if (lexer_parse_identifier (context_p, LEXER_PARSE_NO_OPTS)) { if (!(ident_opts & (LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJECT_PATTERN))) { lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); if (context_p->source_p < context_p->source_end_p && context_p->source_p[0] != LIT_CHAR_COMMA && context_p->source_p[0] != LIT_CHAR_RIGHT_BRACE && context_p->source_p[0] != LIT_CHAR_LEFT_PAREN && context_p->source_p[0] != LIT_CHAR_SEMICOLON && context_p->source_p[0] != LIT_CHAR_EQUALS && context_p->source_p[0] != LIT_CHAR_COLON) { if (lexer_compare_literal_to_string (context_p, "get", 3)) { context_p->token.type = LEXER_PROPERTY_GETTER; return; } if (lexer_compare_literal_to_string (context_p, "set", 3)) { context_p->token.type = LEXER_PROPERTY_SETTER; return; } if (lexer_compare_literal_to_string (context_p, "async", 5)) { context_p->token.type = LEXER_KEYW_ASYNC; return; } if (ident_opts & LEXER_OBJ_IDENT_CLASS_NO_STATIC) { if (lexer_compare_literal_to_string (context_p, "static", 6)) { context_p->token.type = LEXER_KEYW_STATIC; } return; } } } create_literal_object = true; } else if (ident_opts & LEXER_OBJ_IDENT_CLASS_PRIVATE) { parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); } else { switch (context_p->source_p[0]) { case LIT_CHAR_DOUBLE_QUOTE: case LIT_CHAR_SINGLE_QUOTE: { lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); create_literal_object = true; break; } case LIT_CHAR_LEFT_SQUARE: { lexer_consume_next_character (context_p); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED); } return; } case LIT_CHAR_ASTERISK: { if (ident_opts & (LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJECT_PATTERN)) { break; } context_p->token.type = LEXER_MULTIPLY; lexer_consume_next_character (context_p); return; } case LIT_CHAR_HASHMARK: { if (ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER) { context_p->token.type = LEXER_HASHMARK; return; } break; } case LIT_CHAR_LEFT_BRACE: { const uint32_t static_block_flags = (LEXER_OBJ_IDENT_CLASS_NO_STATIC | LEXER_OBJ_IDENT_CLASS_PRIVATE | LEXER_OBJ_IDENT_CLASS_IDENTIFIER); if ((ident_opts & static_block_flags) == LEXER_OBJ_IDENT_CLASS_IDENTIFIER) { context_p->token.type = LEXER_LEFT_BRACE; lexer_consume_next_character (context_p); return; } break; } case LIT_CHAR_RIGHT_BRACE: { if (ident_opts & LEXER_OBJ_IDENT_ONLY_IDENTIFIERS) { break; } context_p->token.type = LEXER_RIGHT_BRACE; lexer_consume_next_character (context_p); return; } case LIT_CHAR_DOT: { if (!(context_p->source_p + 1 >= context_p->source_end_p || lit_char_is_decimal_digit (context_p->source_p[1]))) { if ((ident_opts & ((uint32_t) ~(LEXER_OBJ_IDENT_OBJECT_PATTERN | LEXER_OBJ_IDENT_SET_FUNCTION_START))) || context_p->source_p + 2 >= context_p->source_end_p || context_p->source_p[1] != LIT_CHAR_DOT || context_p->source_p[2] != LIT_CHAR_DOT) { break; } context_p->token.type = LEXER_THREE_DOTS; context_p->token.flags &= (uint8_t) ~LEXER_NO_SKIP_SPACES; PARSER_PLUS_EQUAL_LC (context_p->column, 3); context_p->source_p += 3; return; } /* FALLTHRU */ } default: { const uint8_t *char_p = context_p->source_p; if (char_p[0] == LIT_CHAR_DOT) { char_p++; } if (char_p < context_p->source_end_p && char_p[0] >= LIT_CHAR_0 && char_p[0] <= LIT_CHAR_9) { lexer_parse_number (context_p); if (!(ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER)) { lexer_construct_number_object (context_p, false, false); } return; } break; } } } if (create_literal_object) { if (ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER) { return; } if (ident_opts & LEXER_OBJ_IDENT_CLASS_PRIVATE) { parser_resolve_private_identifier (context_p); return; } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); return; } parser_raise_error (context_p, PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED); } /* lexer_expect_object_literal_id */ /** * Read next token without checking keywords * * @return true if the next literal is identifier, false otherwise */ bool lexer_scan_identifier (parser_context_t *context_p, /**< context */ lexer_parse_options_t opts) /**< identifier parse options */ { lexer_skip_spaces (context_p); context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; if (context_p->source_p < context_p->source_end_p && lexer_parse_identifier (context_p, opts)) { return true; } context_p->token.flags |= LEXER_NO_SKIP_SPACES; lexer_next_token (context_p); return false; } /* lexer_scan_identifier */ /** * Check whether the identifier is a modifier in a property definition. */ void lexer_check_property_modifier (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (!(context_p->token.flags & LEXER_NO_SKIP_SPACES)); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); if (context_p->source_p >= context_p->source_end_p || context_p->source_p[0] == LIT_CHAR_COMMA || context_p->source_p[0] == LIT_CHAR_RIGHT_BRACE || context_p->source_p[0] == LIT_CHAR_LEFT_PAREN || context_p->source_p[0] == LIT_CHAR_EQUALS || context_p->source_p[0] == LIT_CHAR_COLON) { return; } if (lexer_compare_literal_to_string (context_p, "get", 3)) { context_p->token.type = LEXER_PROPERTY_GETTER; return; } if (lexer_compare_literal_to_string (context_p, "set", 3)) { context_p->token.type = LEXER_PROPERTY_SETTER; return; } if (lexer_compare_literal_to_string (context_p, "async", 5)) { context_p->token.type = LEXER_KEYW_ASYNC; return; } } /* lexer_check_property_modifier */ /** * Compares two identifiers. * * Note: * Escape sequences are allowed in the left identifier, but not in the right * * @return true if the two identifiers are the same */ static bool lexer_compare_identifier_to_chars (const uint8_t *left_p, /**< left identifier */ const uint8_t *right_p, /**< right identifier string */ size_t size) /**< byte size of the two identifiers */ { uint8_t utf8_buf[6]; do { if (*left_p == *right_p) { left_p++; right_p++; size--; continue; } size_t escape_size; if (*left_p == LIT_CHAR_BACKSLASH) { left_p += 2; lit_code_point_t code_point = lexer_unchecked_hex_to_character (&left_p); escape_size = lit_code_point_to_cesu8_bytes (utf8_buf, code_point); } else if (*left_p >= LIT_UTF8_4_BYTE_MARKER) { lit_four_byte_utf8_char_to_cesu8 (utf8_buf, left_p); escape_size = 3 * 2; left_p += 4; } else { return false; } size -= escape_size; uint8_t *utf8_p = utf8_buf; do { if (*right_p++ != *utf8_p++) { return false; } } while (--escape_size > 0); } while (size > 0); return true; } /* lexer_compare_identifier_to_chars */ /** * Compares an identifier to a string. * * Note: * Escape sequences are allowed in the left identifier, but not in the right * * @return true if the identifier equals to string */ bool lexer_compare_identifier_to_string (const lexer_lit_location_t *left_p, /**< left literal */ const uint8_t *right_p, /**< right identifier string */ size_t size) /**< byte size of the right identifier */ { if (left_p->length != size) { return false; } if (!(left_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { return memcmp (left_p->char_p, right_p, size) == 0; } return lexer_compare_identifier_to_chars (left_p->char_p, right_p, size); } /* lexer_compare_identifier_to_string */ /** * Compares two identifiers. * * Note: * Escape sequences are allowed in both identifiers * * @return true if the two identifiers are the same */ bool lexer_compare_identifiers (parser_context_t *context_p, /**< context */ const lexer_lit_location_t *left_p, /**< left literal */ const lexer_lit_location_t *right_p) /**< right literal */ { prop_length_t length = left_p->length; if (length != right_p->length) { return false; } if (!(left_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { return lexer_compare_identifier_to_chars (right_p->char_p, left_p->char_p, length); } if (!(right_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { return lexer_compare_identifier_to_chars (left_p->char_p, right_p->char_p, length); } if (length <= 64) { uint8_t buf_p[64]; lexer_convert_ident_to_cesu8 (buf_p, left_p->char_p, length); return lexer_compare_identifier_to_chars (right_p->char_p, buf_p, length); } uint8_t *dynamic_buf_p = (uint8_t*) parser_malloc (context_p, length); lexer_convert_ident_to_cesu8 (dynamic_buf_p, left_p->char_p, length); bool result = lexer_compare_identifier_to_chars (right_p->char_p, dynamic_buf_p, length); parser_free (dynamic_buf_p, length); return result; } /* lexer_compare_identifiers */ /** * Compares the current identifier in the context to the parameter identifier * * Note: * Escape sequences are allowed. * * @return true if the input identifiers are the same */ bool lexer_current_is_literal (parser_context_t *context_p, /**< context */ const lexer_lit_location_t *right_ident_p) /**< identifier */ { JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); lexer_lit_location_t *left_ident_p = &context_p->token.lit_location; JERRY_ASSERT (left_ident_p->length > 0 && right_ident_p->length > 0); if (left_ident_p->length != right_ident_p->length) { return false; } if (!((left_ident_p->status_flags | right_ident_p->status_flags) & LEXER_LIT_LOCATION_HAS_ESCAPE)) { return memcmp (left_ident_p->char_p, right_ident_p->char_p, left_ident_p->length) == 0; } return lexer_compare_identifiers (context_p, left_ident_p, right_ident_p); } /* lexer_current_is_literal */ /** * Compares the current string token to "use strict". * * Note: * Escape sequences are not allowed. * * @return true if "use strict" is found, false otherwise */ bool lexer_string_is_use_strict (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_STRING_LITERAL); return (context_p->token.lit_location.length == 10 && !(context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE) && memcmp (context_p->token.lit_location.char_p, "use strict", 10) == 0); } /* lexer_string_is_use_strict */ /** * Checks whether the string before the current token is a directive or a string literal. * * @return true if the string is a directive, false otherwise */ bool lexer_string_is_directive (parser_context_t *context_p) /**< context */ { return (context_p->token.type == LEXER_SEMICOLON || context_p->token.type == LEXER_RIGHT_BRACE || context_p->token.type == LEXER_EOS || ((context_p->token.flags & LEXER_WAS_NEWLINE) && !LEXER_IS_BINARY_OP_TOKEN (context_p->token.type) && context_p->token.type != LEXER_LEFT_PAREN && context_p->token.type != LEXER_LEFT_SQUARE && context_p->token.type != LEXER_DOT)); } /* lexer_string_is_directive */ /** * Compares the current token to an expected identifier. * * Note: * Escape sequences are not allowed. * * @return true if they are the same, false otherwise */ bool lexer_token_is_identifier (parser_context_t *context_p, /**< context */ const char *identifier_p, /**< identifier */ size_t identifier_length) /**< identifier length */ { /* Checking has_escape is unnecessary because memcmp will fail if escape sequences are present. */ return (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL && context_p->token.lit_location.length == identifier_length && memcmp (context_p->token.lit_location.char_p, identifier_p, identifier_length) == 0); } /* lexer_token_is_identifier */ /** * Compares the current identifier token to "let". * * Note: * Escape sequences are not allowed. * * @return true if "let" is found, false otherwise */ bool lexer_token_is_let (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_LITERAL); return (context_p->token.keyword_type == LEXER_KEYW_LET && !(context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)); } /* lexer_token_is_let */ /** * Compares the current identifier token to "async". * * Note: * Escape sequences are not allowed. * * @return true if "async" is found, false otherwise */ bool lexer_token_is_async (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_LITERAL || context_p->token.type == LEXER_TEMPLATE_LITERAL); return (context_p->token.keyword_type == LEXER_KEYW_ASYNC && !(context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)); } /* lexer_token_is_async */ /** * Compares the current identifier or string to an expected string. * * Note: * Escape sequences are not allowed. * * @return true if they are the same, false otherwise */ bool lexer_compare_literal_to_string (parser_context_t *context_p, /**< context */ const char *string_p, /**< string */ size_t string_length) /**< string length */ { JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && (context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_STRING_LITERAL)); /* Checking has_escape is unnecessary because memcmp will fail if escape sequences are present. */ return (context_p->token.lit_location.length == string_length && memcmp (context_p->token.lit_location.char_p, string_p, string_length) == 0); } /* lexer_compare_literal_to_string */ /** * Initialize line info to its default value */ void lexer_init_line_info (parser_context_t *context_p) /**< context */ { context_p->line = 1; context_p->column = 1; const jerry_parse_options_t *options_p = context_p->options_p; if (options_p != NULL && (options_p->options & JERRY_PARSE_HAS_START)) { if (options_p->start_line > 0) { context_p->line = options_p->start_line; } if (options_p->start_column > 0) { context_p->column = options_p->start_column; } } } /* lexer_init_line_info */ /** * Convert binary lvalue token to binary token * e.g. += -> + * ^= -> ^ * * @return binary token */ uint8_t lexer_convert_binary_lvalue_token_to_binary (uint8_t token) /**< binary lvalue token */ { JERRY_ASSERT (LEXER_IS_BINARY_LVALUE_OP_TOKEN (token)); JERRY_ASSERT (token != LEXER_ASSIGN); if (token <= LEXER_ASSIGN_EXPONENTIATION) { return (uint8_t) (LEXER_ADD + (token - LEXER_ASSIGN_ADD)); } if (token <= LEXER_ASSIGN_UNS_RIGHT_SHIFT) { return (uint8_t) (LEXER_LEFT_SHIFT + (token - LEXER_ASSIGN_LEFT_SHIFT)); } switch (token) { case LEXER_ASSIGN_BIT_AND: { return LEXER_BIT_AND; } case LEXER_ASSIGN_BIT_OR: { return LEXER_BIT_OR; } default: { JERRY_ASSERT (token == LEXER_ASSIGN_BIT_XOR); return LEXER_BIT_XOR; } } } /* lexer_convert_binary_lvalue_token_to_binary */ /** * @} * @} * @} */ #endif /* JERRY_PARSER */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakset.cpp000664 001750 001750 00000004327 15164251010 050045 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-container-object.h" #include "ecma-exceptions.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-weakset.inc.h" #define BUILTIN_UNDERSCORED_ID weakset #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup weakset ECMA WeakSet object built-in * @{ */ /** * Handle calling [[Call]] of built-in WeakSet object * * @return ecma value */ ecma_value_t ecma_builtin_weakset_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_WEAKSET_REQUIRES_NEW); } /* ecma_builtin_weakset_dispatch_call */ /** * Handle calling [[Construct]] of built-in WeakSet object * * @return ecma value */ ecma_value_t ecma_builtin_weakset_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_op_container_create (arguments_list_p, arguments_list_len, LIT_MAGIC_STRING_WEAKSET_UL, ECMA_BUILTIN_ID_WEAKSET_PROTOTYPE); } /* ecma_builtin_weakset_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jcontext/jcontext.cpp000664 001750 001750 00000007512 15164251010 043524 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "jcontext.h" /** \addtogroup context Context * @{ */ /** * Check the existence of the ECMA_STATUS_EXCEPTION flag. * * @return true - if the flag is set * false - otherwise */ bool jcontext_has_pending_exception (void) { return JERRY_CONTEXT (status_flags) & ECMA_STATUS_EXCEPTION; } /* jcontext_has_pending_exception */ /** * Check the existence of the ECMA_STATUS_ABORT flag. * * @return true - if the flag is set * false - otherwise */ bool jcontext_has_pending_abort (void) { return JERRY_CONTEXT (status_flags) & ECMA_STATUS_ABORT; } /* jcontext_has_pending_abort */ /** * Set the abort flag for the context. * * @return void */ void jcontext_set_abort_flag (bool is_abort) /**< true - if the abort flag should be set * false - if the abort flag should be removed */ { JERRY_ASSERT (jcontext_has_pending_exception ()); if (is_abort) { JERRY_CONTEXT (status_flags) |= ECMA_STATUS_ABORT; } else { JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_ABORT; } } /* jcontext_set_abort_flag */ /** * Set the exception flag for the context. * * @return void */ void jcontext_set_exception_flag (bool is_exception) /**< true - if the exception flag should be set * false - if the exception flag should be removed */ { if (is_exception) { JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; } else { JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION; } } /* jcontext_set_exception_flag */ /** * Raise exception from the given error value. * * @return void */ void jcontext_raise_exception (ecma_value_t error) /**< error to raise */ { JERRY_ASSERT (!jcontext_has_pending_exception ()); JERRY_ASSERT (!jcontext_has_pending_abort ()); JERRY_CONTEXT (error_value) = error; jcontext_set_exception_flag (true); } /* jcontext_raise_exception */ /** * Release the current exception/abort of the context. */ void jcontext_release_exception (void) { JERRY_ASSERT (jcontext_has_pending_exception ()); ecma_free_value (jcontext_take_exception ()); } /* jcontext_release_exception */ /** * Take the current exception/abort of context. * * @return current exception as an ecma-value */ ecma_value_t jcontext_take_exception (void) { JERRY_ASSERT (jcontext_has_pending_exception ()); JERRY_CONTEXT (status_flags) &= (uint32_t) ~(ECMA_STATUS_EXCEPTION #if JERRY_VM_THROW | ECMA_STATUS_ERROR_THROWN #endif /* JERRY_VM_THROW */ | ECMA_STATUS_ABORT); return JERRY_CONTEXT (error_value); } /* jcontext_take_exception */ #if !JERRY_EXTERNAL_CONTEXT /** * Global context. */ jerry_context_t jerry_global_context; #if !JERRY_SYSTEM_ALLOCATOR /** * Check size of heap is corresponding to configuration */ JERRY_STATIC_ASSERT ((sizeof (jmem_heap_t) <= JMEM_HEAP_SIZE), size_of_mem_heap_must_be_less_than_or_equal_to_JMEM_HEAP_SIZE); /** * Global heap. */ jmem_heap_t jerry_global_heap JERRY_ATTR_ALIGNED (JMEM_ALIGNMENT) JERRY_ATTR_GLOBAL_HEAP; #endif /* !JERRY_SYSTEM_ALLOCATOR */ #endif /* !JERRY_EXTERNAL_CONTEXT */ /** * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/regression_check.py000664 001750 001750 00000003772 15164251010 036433 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0from typing import Optional POSSIBLE_PROBLEM = "POSSIBLE_PROBLEM - " def check_file(file_name: str) -> Optional[str]: with open(file_name, "r") as file: print(f"Checking file {file_name}") for line in file: line = line.strip() if line.startswith(POSSIBLE_PROBLEM): return line[len(POSSIBLE_PROBLEM) :] return None if __name__ == "__main__": data_to_check = [ ( "result_valid_files.txt", True, "Found difference in converting images that properly converted in develop branch.", ), ( "result_not_valid_files.txt", False, "Found differences in converting images that were not properly converted in develop branch(this may be improvement).", ), ( "result_image_size.txt", True, "Generated png have different size in each run.", ), ( "result_crashes.txt", True, "Found crashes in crashes during when converting svg to png.", ), ] fail_ci = False comment = "" for file_name, fail_ci_when_triggered, new_comment in data_to_check: possible_problem = check_file(file_name) if possible_problem is not None: print(f">>>>> Found changes in {file_name} (look at CI steps from above)") print(f"Comment: {new_comment}") if fail_ci_when_triggered: fail_ci = True comment += new_comment + "\n" to_write = "Regression report:\n" if len(comment) > 0: to_write += comment to_write += "\nCheck CI for artifacts/logs for more details." else: to_write += "Not found any changes(both improvements and regressions) in this PR with test data." print("\n\n" + to_write + "\n\n") with open("comment.txt", "w") as file: file.write(to_write) if fail_ci: with open("fail_ci.txt", "w") as file: file.write("Fail") mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgFill.h000664 001750 001750 00000013246 15164251010 033400 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_FILL_H_ #define _TVG_FILL_H_ #include "tvgCommon.h" #include "tvgMath.h" #define LINEAR(A) static_cast(A) #define CONST_LINEAR(A) static_cast(A) #define RADIAL(A) static_cast(A) #define CONST_RADIAL(A) static_cast(A) struct Fill::Impl { ColorStop* colorStops = nullptr; Matrix transform = tvg::identity(); uint16_t cnt = 0; FillSpread spread = FillSpread::Pad; virtual ~Impl() { tvg::free(colorStops); } void copy(const Fill::Impl& dup) { cnt = dup.cnt; spread = dup.spread; colorStops = tvg::malloc(sizeof(ColorStop) * dup.cnt); if (dup.cnt > 0) memcpy(colorStops, dup.colorStops, sizeof(ColorStop) * dup.cnt); transform = dup.transform; } Result update(const ColorStop* colorStops, uint32_t cnt) { if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments; if (cnt == 0) { if (this->colorStops) { tvg::free(this->colorStops); this->colorStops = nullptr; this->cnt = 0; } return Result::Success; } if (cnt != this->cnt) { this->colorStops = tvg::realloc(this->colorStops, cnt * sizeof(ColorStop)); } this->cnt = cnt; memcpy(this->colorStops, colorStops, cnt * sizeof(ColorStop)); return Result::Success; } }; struct RadialGradientImpl : RadialGradient { Fill::Impl impl; Point center{}, focal{}; float r = 0.0f, fr = 0.0f; RadialGradientImpl() { Fill::pImpl = &impl; } Fill* duplicate() const { auto ret = RadialGradient::gen(); RADIAL(ret)->impl.copy(this->impl); RADIAL(ret)->center = center; RADIAL(ret)->r = r; RADIAL(ret)->focal = focal; RADIAL(ret)->fr = fr; return ret; } Result radial(float cx, float cy, float r, float fx, float fy, float fr) { if (r < 0 || fr < 0) return Result::InvalidArguments; this->center = {cx, cy}; this->r = r; this->focal = {fx, fy}; this->fr = fr; return Result::Success; } Result radial(float* cx, float* cy, float* r, float* fx, float* fy, float* fr) const { if (cx) *cx = center.x; if (cy) *cy = center.y; if (r) *r = this->r; if (fx) *fx = focal.x; if (fy) *fy = focal.y; if (fr) *fr = this->fr; return Result::Success; } //TODO: remove this logic once SVG 2.0 is adopted by sw and wg engines (gl already supports it); lottie-specific handling will then be delegated entirely to the loader //clamp focal point and shrink start circle if needed to avoid invalid gradient setup bool correct(float& fx, float& fy, float& fr) const { constexpr float PRECISION = 0.01f; if (r < PRECISION) return false; // too small, treated as solid fill auto dist = tvg::length(center, focal); // clamp focal point to inside end circle if outside if (this->r - dist < PRECISION) { auto diff = center - focal; if (dist < PRECISION) dist = diff.x = PRECISION; auto scale = this->r * (1.0f - PRECISION) / dist; diff *= scale; dist *= scale; // update effective dist after scaling fx = center.x - diff.x; fy = center.y - diff.y; } else { fx = focal.x; fy = focal.y; } // ensure start circle radius fr doesn't exceed the difference auto maxFr = (r - dist) * (1.0f - PRECISION); fr = (this->fr > maxFr) ? std::max(0.0f, maxFr) : this->fr; return true; } }; struct LinearGradientImpl : LinearGradient { Fill::Impl impl; Point p1{}, p2{}; LinearGradientImpl() { Fill::pImpl = &impl; } Fill* duplicate() const { auto ret = LinearGradient::gen(); LINEAR(ret)->impl.copy(this->impl); LINEAR(ret)->p1 = p1; LINEAR(ret)->p2 = p2; return ret; } Result linear(float x1, float y1, float x2, float y2) noexcept { p1 = {x1, y1}; p2 = {x2, y2}; return Result::Success; } Result linear(float* x1, float* y1, float* x2, float* y2) const noexcept { if (x1) *x1 = p1.x; if (x2) *x2 = p2.x; if (y1) *y1 = p1.y; if (y2) *y2 = p2.y; return Result::Success; } }; #endif //_TVG_FILL_H_ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieParserHandler.cpp000664 001750 001750 00000013741 15164251010 040024 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgStr.h" #include "tvgLottieParserHandler.h" /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool LookaheadParserHandler::enterArray() { if (state == kEnteringArray) { parseNext(); return true; } Error(); return false; } bool LookaheadParserHandler::nextArrayValue() { if (state == kExitingArray) { parseNext(); return false; } //SPECIAL CASE: same as nextObjectKey() if (state == kExitingObject) return false; if (state == kError || state == kHasKey) { Error(); return false; } return true; } int LookaheadParserHandler::getInt() { if (state == kHasNumber) { auto result = val.GetInt(); parseNext(); return result; } Error(); return 0; } float LookaheadParserHandler::getFloat() { if (state == kHasNumber) { auto result = val.GetFloat(); parseNext(); return result; } Error(); return 0; } const char* LookaheadParserHandler::getString() { if (state == kHasString) { auto result = val.GetString(); parseNext(); return result; } Error(); return nullptr; } char* LookaheadParserHandler::getStringCopy() { auto str = getString(); if (str) return duplicate(str); return nullptr; } bool LookaheadParserHandler::getBool() { if (state == kHasBool) { auto result = val.GetBool(); parseNext(); return result; } Error(); return false; } void LookaheadParserHandler::getNull() { if (state == kHasNull) { parseNext(); return; } Error(); } bool LookaheadParserHandler::parseNext() { if (reader.HasParseError() || !reader.IterativeParseNext(iss, *this)) { Error(); return false; } return true; } bool LookaheadParserHandler::enterObject() { if (state == kEnteringObject) { parseNext(); return true; } Error(); return false; } int LookaheadParserHandler::peekType() { if (state >= kHasNull && state <= kHasKey) return val.GetType(); if (state == kEnteringArray) return kArrayType; if (state == kEnteringObject) return kObjectType; return -1; } void LookaheadParserHandler::skipOut(int depth) { do { if (state == kEnteringArray || state == kEnteringObject) ++depth; else if (state == kExitingArray || state == kExitingObject) --depth; else if (state == kError) return; parseNext(); } while (depth > 0); } const char* LookaheadParserHandler::nextObjectKey() { if (state == kHasKey) { auto result = val.GetString(); parseNext(); return result; } /* SPECIAL CASE: The parser works with a predefined 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 (state == kExitingArray || state == kEnteringObject) return nullptr; if (state != kExitingObject) { Error(); return nullptr; } parseNext(); return nullptr; } void LookaheadParserHandler::skip() { if (peekType() == kArrayType) { enterArray(); skipOut(1); } else if (peekType() == kObjectType) { enterObject(); skipOut(1); } else { skipOut(0); } } char* LookaheadParserHandler::getPos() { return iss.src_; } bool LookaheadParserHandler::isPrimitive() { return state >= kHasNull && state <= kHasKey; }external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/include/jerryscript-compiler.h000664 001750 001750 00000012020 15164251010 045276 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JERRYSCRIPT_COMPILER_H #define JERRYSCRIPT_COMPILER_H #ifdef __cplusplus #define JERRY_C_API_BEGIN extern "C" { #define JERRY_C_API_END } #else /* !__cplusplus */ #define JERRY_C_API_BEGIN #define JERRY_C_API_END #endif /* __cplusplus */ JERRY_C_API_BEGIN /** \addtogroup jerry-compiler Jerry compiler compatibility components * @{ */ #ifdef __GNUC__ /* * Compiler-specific macros relevant for GCC. */ #define JERRY_ATTR_ALIGNED(ALIGNMENT) __attribute__ ((aligned (ALIGNMENT))) #define JERRY_ATTR_CONST __attribute__ ((const)) #define JERRY_ATTR_DEPRECATED __attribute__ ((deprecated)) #define JERRY_ATTR_FORMAT(...) __attribute__ ((format (__VA_ARGS__))) #define JERRY_ATTR_HOT __attribute__ ((hot)) #define JERRY_ATTR_NOINLINE __attribute__ ((noinline)) #define JERRY_ATTR_NORETURN __attribute__ ((noreturn)) #define JERRY_ATTR_PURE __attribute__ ((pure)) #define JERRY_ATTR_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) #define JERRY_ATTR_WEAK __attribute__ ((weak)) #define JERRY_WEAK_SYMBOL_SUPPORT #ifndef JERRY_LIKELY #define JERRY_LIKELY(x) __builtin_expect (!!(x), 1) #endif /* !JERRY_LIKELY */ #ifndef JERRY_UNLIKELY #define JERRY_UNLIKELY(x) __builtin_expect (!!(x), 0) #endif /* !JERRY_UNLIKELY */ #endif /* __GNUC__ */ #ifdef _MSC_VER /* * Compiler-specific macros relevant for Microsoft Visual C/C++ Compiler. */ #define JERRY_ATTR_DEPRECATED __declspec (deprecated) #define JERRY_ATTR_NOINLINE __declspec (noinline) #define JERRY_ATTR_NORETURN __declspec (noreturn) /* * Microsoft Visual C/C++ Compiler doesn't support for VLA, using _alloca * instead. */ void *__cdecl _alloca (size_t _Size); #define JERRY_VLA(type, name, size) type *name = (type *) (_alloca (sizeof (type) * (size))) #endif /* _MSC_VER */ /* * Default empty definitions for all compiler-specific macros. Define any of * these in a guarded block above (e.g., as for GCC) to fine tune compilation * for your own compiler. */ /** * Function attribute to align function to given number of bytes. */ #ifndef JERRY_ATTR_ALIGNED #define JERRY_ATTR_ALIGNED(ALIGNMENT) #endif /* !JERRY_ATTR_ALIGNED */ /** * Function attribute to declare that function has no effect except the return * value and it only depends on parameters. */ #ifndef JERRY_ATTR_CONST #define JERRY_ATTR_CONST #endif /* !JERRY_ATTR_CONST */ /** * Function attribute to trigger warning if deprecated function is called. */ #ifndef JERRY_ATTR_DEPRECATED #define JERRY_ATTR_DEPRECATED #endif /* !JERRY_ATTR_DEPRECATED */ /** * Function attribute to declare that function is variadic and takes a format * string and some arguments as parameters. */ #ifndef JERRY_ATTR_FORMAT #define JERRY_ATTR_FORMAT(...) #endif /* !JERRY_ATTR_FORMAT */ /** * Function attribute to predict that function is a hot spot, and therefore * should be optimized aggressively. */ #ifndef JERRY_ATTR_HOT #define JERRY_ATTR_HOT #endif /* !JERRY_ATTR_HOT */ /** * Function attribute not to inline function ever. */ #ifndef JERRY_ATTR_NOINLINE #define JERRY_ATTR_NOINLINE #endif /* !JERRY_ATTR_NOINLINE */ /** * Function attribute to declare that function never returns. */ #ifndef JERRY_ATTR_NORETURN #define JERRY_ATTR_NORETURN #endif /* !JERRY_ATTR_NORETURN */ /** * Function attribute to declare that function has no effect except the return * value and it only depends on parameters and global variables. */ #ifndef JERRY_ATTR_PURE #define JERRY_ATTR_PURE #endif /* !JERRY_ATTR_PURE */ /** * Function attribute to trigger warning if function's caller doesn't use (e.g., * check) the return value. */ #ifndef JERRY_ATTR_WARN_UNUSED_RESULT #define JERRY_ATTR_WARN_UNUSED_RESULT #endif /* !JERRY_ATTR_WARN_UNUSED_RESULT */ /** * Function attribute to declare a function a weak symbol */ #ifndef JERRY_ATTR_WEAK #define JERRY_ATTR_WEAK #endif /* !JERRY_ATTR_WEAK */ /** * Helper to predict that a condition is likely. */ #ifndef JERRY_LIKELY #define JERRY_LIKELY(x) (x) #endif /* !JERRY_LIKELY */ /** * Helper to predict that a condition is unlikely. */ #ifndef JERRY_UNLIKELY #define JERRY_UNLIKELY(x) (x) #endif /* !JERRY_UNLIKELY */ /** * Helper to declare (or mimic) a C99 variable-length array. */ #ifndef JERRY_VLA #define JERRY_VLA(type, name, size) type* name = (type*)alloca((size) * sizeof(type)) #endif /* !JERRY_VLA */ /** * @} */ JERRY_C_API_END #endif /* !JERRYSCRIPT_COMPILER_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jrt/jrt-logging.cpp000664 001750 001750 00000002045 15164251010 043046 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "jrt.h" static jerry_log_level_t jerry_log_level = JERRY_LOG_LEVEL_ERROR; /** * Get current log level * * @return log level */ jerry_log_level_t jerry_jrt_get_log_level (void) { return jerry_log_level; } /* jerry_jrt_get_log_level */ /** * Set log level * * @param level: new log level */ void jerry_jrt_set_log_level (jerry_log_level_t level) { jerry_log_level = level; } /* jerry_jrt_set_log_level */ lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype-unscopables.inc.h000664 001750 001750 00000003645 15164251010 054316 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Array.prototype[@@unscopables] built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" SIMPLE_VALUE (LIT_MAGIC_STRING_AT, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_COPY_WITHIN, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_ENTRIES, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_FILL, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_FIND, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_FIND_INDEX, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_FLAT, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_FLATMAP, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_INCLUDES, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_KEYS, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) SIMPLE_VALUE (LIT_MAGIC_STRING_VALUES, ECMA_VALUE_TRUE, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgUtil.cpp000664 001750 001750 00000006025 15164251010 035061 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgSvgUtil.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static uint8_t _hexCharToDec(const char c) { if (c >= 'a') return c - 'a' + 10; else if (c >= 'A') return c - 'A' + 10; else return c - '0'; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ const char* svgUtilSkipWhiteSpace(const char* itr, const char* itrEnd) { while ((itrEnd && itr < itrEnd) || (!itrEnd && *itr != '\0')) { if (!isspace((unsigned char)*itr)) break; itr++; } return itr; } const char* svgUtilUnskipWhiteSpace(const char* itr, const char* itrStart) { for (itr--; itr > itrStart; itr--) { if (!isspace((unsigned char)*itr)) break; } return itr + 1; } const char* svgUtilSkipWhiteSpaceAndComma(const char* content) { content = svgUtilSkipWhiteSpace(content, nullptr); if (*content == ',') return content + 1; return content; } size_t svgUtilURLDecode(const char *src, char** dst) { if (!src) return 0; auto length = strlen(src); if (length == 0) return 0; char* decoded = tvg::malloc(sizeof(char) * length + 1); char a, b; int idx =0; while (*src) { if (*src == '%' && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) { decoded[idx++] = (_hexCharToDec(a) << 4) + _hexCharToDec(b); src+=3; } else if (*src == '+') { decoded[idx++] = ' '; src++; } else { decoded[idx++] = *src++; } } decoded[idx] = '\0'; *dst = decoded; return idx + 1; } external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-internal.h000664 001750 001750 00000107074 15164251010 045131 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JS_PARSER_INTERNAL_H #define JS_PARSER_INTERNAL_H #include "ecma-module.h" #include "byte-code.h" #include "common.h" #include "js-lexer.h" #include "js-parser-limits.h" #include "js-parser.h" #include "js-scanner.h" #include "parser-errors.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_internals Internals * @{ */ /** * General parser flags. */ typedef enum { PARSER_IS_STRICT = (1u << 0), /**< strict mode code */ PARSER_IS_FUNCTION = (1u << 1), /**< function body is parsed */ PARSER_IS_CLOSURE = (1u << 2), /**< function body is encapsulated in {} block */ PARSER_IS_FUNC_EXPRESSION = (1u << 3), /**< a function expression is parsed */ PARSER_IS_PROPERTY_GETTER = (1u << 4), /**< a property getter function is parsed */ PARSER_IS_PROPERTY_SETTER = (1u << 5), /**< a property setter function is parsed */ PARSER_HAS_NON_STRICT_ARG = (1u << 6), /**< the function has arguments which * are not supported in strict mode */ PARSER_ARGUMENTS_NEEDED = (1u << 7), /**< arguments object must be created */ PARSER_LEXICAL_ENV_NEEDED = (1u << 8), /**< lexical environment object must be created */ PARSER_INSIDE_WITH = (1u << 9), /**< code block is inside a with statement */ PARSER_NO_END_LABEL = (1u << 10), /**< return instruction must be inserted * after the last byte code */ PARSER_DEBUGGER_BREAKPOINT_APPENDED = (1u << 11), /**< pending (unsent) breakpoint * info is available */ PARSER_LEXICAL_BLOCK_NEEDED = (1u << 12), /**< global script: needs a lexical environment for let and const * function: needs a lexical environment for arguments */ PARSER_IS_ARROW_FUNCTION = (1u << 13), /**< an arrow function is parsed */ PARSER_IS_GENERATOR_FUNCTION = (1u << 14), /**< a generator function is parsed */ PARSER_IS_ASYNC_FUNCTION = (1u << 15), /**< an async function is parsed */ PARSER_DISALLOW_AWAIT_YIELD = (1u << 16), /**< throw SyntaxError for await / yield keywords */ PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT = (1u << 17), /**< function has complex (ES2015+) argument definition */ PARSER_FUNCTION_HAS_REST_PARAM = (1u << 18), /**< function has rest parameter */ PARSER_CLASS_CONSTRUCTOR = (1u << 19), /**< a class constructor is parsed * Note: PARSER_ALLOW_SUPER must be present */ /* These five status flags must be in this order. See PARSER_SAVED_FLAGS_OFFSET. */ PARSER_ALLOW_SUPER = (1u << 20), /**< allow super property access */ PARSER_ALLOW_SUPER_CALL = (1u << 21), /**< allow super constructor call * Note: PARSER_CLASS_CONSTRUCTOR must be present */ PARSER_FUNCTION_IS_PARSING_ARGS = (1u << 22), /**< set when parsing function arguments */ PARSER_INSIDE_CLASS_FIELD = (1u << 23), /**< a class field is being parsed */ PARSER_ALLOW_NEW_TARGET = (1u << 24), /**< allow new.target parsing in the current context */ PARSER_IS_METHOD = (1u << 25), /**< method is parsed */ PARSER_IS_CLASS_STATIC_BLOCK = (1u << 26), /**< a class static block is parsed */ PARSER_PRIVATE_FUNCTION_NAME = PARSER_IS_FUNC_EXPRESSION, /**< represents private method for * parser_set_function_name*/ #if JERRY_MODULE_SYSTEM PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 27), /**< parsing a function or class default export */ PARSER_MODULE_STORE_IDENT = (1u << 28), /**< store identifier of the current export statement */ #endif /* JERRY_MODULE_SYSTEM */ PARSER_HAS_LATE_LIT_INIT = (1u << 30), /**< there are identifier or string literals which construction * is postponed after the local parser data is freed */ #ifndef JERRY_NDEBUG PARSER_SCANNING_SUCCESSFUL = PARSER_HAS_LATE_LIT_INIT, /**< scanning process was successful */ #endif /* !JERRY_NDEBUG */ } parser_general_flags_t; /** * Expression parsing flags. */ typedef enum { PARSE_EXPR = 0, /**< parse an expression without any special flags */ PARSE_EXPR_LEFT_HAND_SIDE = (1u << 0), /**< parse a left-hand-side expression */ PARSE_EXPR_NO_PUSH_RESULT = (1u << 1), /**< do not push the result of the expression onto the stack */ PARSE_EXPR_NO_COMMA = (1u << 2), /**< do not parse comma operator */ PARSE_EXPR_HAS_LITERAL = (1u << 3), /**< a primary literal is provided by a * CBC_PUSH_LITERAL instruction */ } parser_expression_flags_t; /** * Pattern parsing flags. */ typedef enum { PARSER_PATTERN_NO_OPTS = 0, /**< parse the expression after '=' */ PARSER_PATTERN_BINDING = (1u << 0), /**< parse BindingPattern */ PARSER_PATTERN_TARGET_ON_STACK = (1u << 1), /**< assignment target is the topmost element on the stack */ PARSER_PATTERN_TARGET_DEFAULT = (1u << 2), /**< perform default value comparison for assignment target */ PARSER_PATTERN_NESTED_PATTERN = (1u << 3), /**< parse pattern inside a pattern */ PARSER_PATTERN_LET = (1u << 4), /**< pattern is a let declaration */ PARSER_PATTERN_CONST = (1u << 5), /**< pattern is a const declaration */ PARSER_PATTERN_LOCAL = (1u << 6), /**< pattern is a local (catch parameter) declaration */ PARSER_PATTERN_REST_ELEMENT = (1u << 7), /**< parse rest array / object element */ PARSER_PATTERN_HAS_REST_ELEMENT = (1u << 8), /**< object literal rest element will be present */ PARSER_PATTERN_ARGUMENTS = (1u << 9), /**< parse arguments binding */ } parser_pattern_flags_t; /** * Check type for scanner_is_context_needed function. */ typedef enum { PARSER_CHECK_BLOCK_CONTEXT, /**< check block context */ PARSER_CHECK_GLOBAL_CONTEXT, /**< check global context */ PARSER_CHECK_FUNCTION_CONTEXT, /**< check function context */ } parser_check_context_type_t; /** * Class field bits. */ typedef enum { PARSER_CLASS_FIELD_END = (1u << 0), /**< last class field */ PARSER_CLASS_FIELD_NORMAL = (1u << 1), /**< normal (non-computed) class field */ PARSER_CLASS_FIELD_INITIALIZED = (1u << 2), /**< class field is initialized */ PARSER_CLASS_FIELD_STATIC = (1u << 3), /**< static class field */ PARSER_CLASS_FIELD_STATIC_BLOCK = (1u << 4), /**< static class field */ } parser_class_field_type_t; /** * Mask for strict mode code */ #define PARSER_STRICT_MODE_MASK 0x1 /** * Shorthand for function closure definition */ #define PARSER_FUNCTION_CLOSURE (PARSER_IS_FUNCTION | PARSER_IS_CLOSURE) #if PARSER_MAXIMUM_CODE_SIZE <= UINT16_MAX /** * Maximum number of bytes for branch target. */ #define PARSER_MAX_BRANCH_LENGTH 2 #else /* PARSER_MAXIMUM_CODE_SIZE > UINT16_MAX */ /** * Maximum number of bytes for branch target. */ #define PARSER_MAX_BRANCH_LENGTH 3 #endif /* PARSER_MAXIMUM_CODE_SIZE <= UINT16_MAX */ /** * Offset of PARSER_ALLOW_SUPER */ #define PARSER_SAVED_FLAGS_OFFSET JERRY_LOG2 (PARSER_ALLOW_SUPER) /** * Mask of saved flags */ #define PARSER_SAVED_FLAGS_MASK \ ((1 << (JERRY_LOG2 (PARSER_ALLOW_NEW_TARGET) - JERRY_LOG2 (PARSER_ALLOW_SUPER) + 1)) - 1) /** * Get class option bits from parser_general_flags_t */ #define PARSER_SAVE_STATUS_FLAGS(opts) ((uint16_t) (((opts) >> PARSER_SAVED_FLAGS_OFFSET) & PARSER_SAVED_FLAGS_MASK)) /** * Mask for get class option bits from ecma_parse_opts_t */ #define PARSER_RESTORE_STATUS_FLAGS_MASK (((ECMA_PARSE_ALLOW_NEW_TARGET << 1) - 1) - (ECMA_PARSE_ALLOW_SUPER - 1)) /** * Shift for get class option bits from ecma_parse_opts_t */ #define PARSER_RESTORE_STATUS_FLAGS_SHIFT (JERRY_LOG2 (PARSER_ALLOW_SUPER) - JERRY_LOG2 (ECMA_PARSE_ALLOW_SUPER)) /** * Get class option bits from ecma_parse_opts_t */ #define PARSER_RESTORE_STATUS_FLAGS(opts) \ (((opts) &PARSER_RESTORE_STATUS_FLAGS_MASK) << PARSER_RESTORE_STATUS_FLAGS_SHIFT) /** * All flags that affect exotic arguments object creation. */ #define PARSER_ARGUMENTS_RELATED_FLAGS \ (PARSER_ARGUMENTS_NEEDED | PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT | PARSER_IS_STRICT) /** * Get the corresponding eval flag for a ecma_parse_opts_t flag */ #define PARSER_GET_EVAL_FLAG(type) ((type) >> JERRY_LOG2 (ECMA_PARSE_ALLOW_SUPER)) /** * Check non-generator async functions */ #define PARSER_IS_NORMAL_ASYNC_FUNCTION(status_flags) \ (((status_flags) & (PARSER_IS_GENERATOR_FUNCTION | PARSER_IS_ASYNC_FUNCTION)) == PARSER_IS_ASYNC_FUNCTION) /* Checks whether unmapped arguments are needed. */ #define PARSER_NEEDS_MAPPED_ARGUMENTS(status_flags) \ (((status_flags) &PARSER_ARGUMENTS_RELATED_FLAGS) == PARSER_ARGUMENTS_NEEDED) /* The maximum of PARSER_CBC_STREAM_PAGE_SIZE is 127. */ #define PARSER_CBC_STREAM_PAGE_SIZE ((uint32_t) (64 - sizeof (void *))) /* Defines the size of the max page. */ #define PARSER_STACK_PAGE_SIZE ((uint32_t) (((sizeof (void *) > 4) ? 128 : 64) - sizeof (void *))) /* Avoid compiler warnings for += operations. */ #define PARSER_PLUS_EQUAL_U16(base, value) (base) = (uint16_t) ((base) + (value)) #define PARSER_MINUS_EQUAL_U16(base, value) (base) = (uint16_t) ((base) - (value)) #define PARSER_PLUS_EQUAL_LC(base, value) (base) = (parser_line_counter_t) ((base) + (value)) /** * Argument for a compact-byte code. */ typedef struct { uint16_t literal_index; /**< literal index argument */ uint16_t value; /**< other argument (second literal or byte). */ uint16_t third_literal_index; /**< literal index argument */ uint8_t literal_type; /**< last literal type */ uint8_t literal_keyword_type; /**< last literal keyword type */ } cbc_argument_t; /* Useful parser macros. */ #define PARSER_CBC_UNAVAILABLE CBC_EXT_OPCODE #define PARSER_TO_EXT_OPCODE(opcode) ((uint16_t) ((opcode) + 256)) #define PARSER_GET_EXT_OPCODE(opcode) ((opcode) -256) #define PARSER_IS_BASIC_OPCODE(opcode) ((opcode) < 256) #define PARSER_IS_PUSH_LITERAL(opcode) \ ((opcode) == CBC_PUSH_LITERAL || (opcode) == CBC_PUSH_TWO_LITERALS || (opcode) == CBC_PUSH_THREE_LITERALS) #define PARSER_IS_PUSH_NUMBER(opcode) \ ((opcode) >= CBC_PUSH_NUMBER_0 && (opcode) <= CBC_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE) #define PARSER_IS_MUTABLE_PUSH_LITERAL(opcode) ((opcode) >= CBC_PUSH_LITERAL && (opcode) <= CBC_PUSH_THIS_LITERAL) #define PARSER_IS_PUSH_LITERALS_WITH_THIS(opcode) ((opcode) >= CBC_PUSH_LITERAL && (opcode) <= CBC_PUSH_THREE_LITERALS) #define PARSER_IS_PUSH_PROP(opcode) ((opcode) >= CBC_PUSH_PROP && (opcode) <= CBC_PUSH_PROP_THIS_LITERAL) #define PARSER_IS_PUSH_PROP_LITERAL(opcode) \ ((opcode) >= CBC_PUSH_PROP_LITERAL && (opcode) <= CBC_PUSH_PROP_THIS_LITERAL) #define PARSER_PUSH_LITERAL_TO_PUSH_PROP_LITERAL(opcode) \ (uint16_t) ((opcode) + (CBC_PUSH_PROP_LITERAL - CBC_PUSH_LITERAL)) #define PARSER_PUSH_PROP_LITERAL_TO_PUSH_LITERAL(opcode) \ (uint16_t) ((opcode) - (CBC_PUSH_PROP_LITERAL - CBC_PUSH_LITERAL)) #define PARSER_PUSH_PROP_TO_PUSH_PROP_REFERENCE(opcode) \ (uint16_t) ((opcode) + (CBC_PUSH_PROP_REFERENCE - CBC_PUSH_PROP)) #define PARSER_PUSH_PROP_REFERENCE_TO_PUSH_PROP(opcode) \ (uint16_t) ((opcode) - (CBC_PUSH_PROP_REFERENCE - CBC_PUSH_PROP)) #define PARSER_GET_LITERAL(literal_index) \ ((lexer_literal_t *) parser_list_get (&context_p->literal_pool, (literal_index))) #define PARSER_TO_BINARY_OPERATION_WITH_RESULT(opcode) \ (PARSER_TO_EXT_OPCODE (opcode) - CBC_ASSIGN_ADD + CBC_EXT_ASSIGN_ADD_PUSH_RESULT) #define PARSER_TO_BINARY_OPERATION_WITH_BLOCK(opcode) \ ((uint16_t) (PARSER_TO_EXT_OPCODE (opcode) - CBC_ASSIGN_ADD + CBC_EXT_ASSIGN_ADD_BLOCK)) #define PARSER_GET_FLAGS(op) (PARSER_IS_BASIC_OPCODE (op) ? cbc_flags[(op)] : cbc_ext_flags[PARSER_GET_EXT_OPCODE (op)]) #define PARSER_OPCODE_IS_RETURN(op) \ ((op) == CBC_RETURN || (op) == CBC_RETURN_FUNCTION_END || (op) == CBC_RETURN_WITH_LITERAL) #define PARSER_ARGS_EQ(op, types) ((PARSER_GET_FLAGS (op) & CBC_ARG_TYPES) == (types)) /** * All data allocated by the parser is * stored in parser_data_pages in the memory. */ #if defined(_MSC_VER ) #pragma warning(push) #pragma warning(disable:4200) #endif typedef struct parser_mem_page_t { struct parser_mem_page_t *next_p; /**< next page */ uint8_t bytes[]; /**< memory bytes, C99 flexible array member */ } parser_mem_page_t; #if defined(_MSC_VER) #pragma warning(pop) #endif /** * Structure for managing parser memory. */ typedef struct { parser_mem_page_t *first_p; /**< first allocated page */ parser_mem_page_t *last_p; /**< last allocated page */ uint32_t last_position; /**< position of the last allocated byte */ } parser_mem_data_t; /** * Parser memory list. */ typedef struct { parser_mem_data_t data; /**< storage space */ uint32_t page_size; /**< size of each page */ uint32_t item_size; /**< size of each item */ uint32_t item_count; /**< number of items on each page */ } parser_list_t; /** * Iterator for parser memory list. */ typedef struct { parser_list_t *list_p; /**< parser list */ parser_mem_page_t *current_p; /**< currently processed page */ size_t current_position; /**< current position on the page */ } parser_list_iterator_t; /** * Parser memory stack. */ typedef struct { parser_mem_data_t data; /**< storage space */ parser_mem_page_t *free_page_p; /**< space for fast allocation */ } parser_stack_t; /** * Iterator for parser memory stack. */ typedef struct { parser_mem_page_t *current_p; /**< currently processed page */ size_t current_position; /**< current position on the page */ } parser_stack_iterator_t; /** * Branch type. */ typedef struct { parser_mem_page_t *page_p; /**< branch location page */ uint32_t offset; /**< branch location offset */ } parser_branch_t; /** * Branch chain type. */ typedef struct parser_branch_node_t { struct parser_branch_node_t *next_p; /**< next linked list node */ parser_branch_t branch; /**< branch */ } parser_branch_node_t; /** * Items of scope stack. */ typedef struct { uint16_t map_from; /**< original literal index */ uint16_t map_to; /**< encoded register or literal index and flags */ } parser_scope_stack_t; /** * This item represents a function literal in the scope stack. * * When map_from == PARSER_SCOPE_STACK_FUNC: * map_to represents the literal reserved for a function literal * Note: the name of the function is the previous value in the scope stack * Note: map_to is not encoded in this case */ #define PARSER_SCOPE_STACK_FUNC 0xffff /** * Mask for decoding the register index of map_to */ #define PARSER_SCOPE_STACK_REGISTER_MASK 0x3fff /** * Function statements with the name specified * in map_from should not be copied to global scope. */ #define PARSER_SCOPE_STACK_NO_FUNCTION_COPY 0x8000 /** * The scope stack item represents a const binding stored in register */ #define PARSER_SCOPE_STACK_IS_CONST_REG 0x4000 /** * The scope stack item represents a binding which has already created with ECMA_VALUE_UNINITIALIZED */ #define PARSER_SCOPE_STACK_IS_LOCAL_CREATED (PARSER_SCOPE_STACK_IS_CONST_REG) /** * Starting literal index for registers. */ #define PARSER_REGISTER_START 0x8000 /** * Invalid literal index */ #define PARSER_INVALID_LITERAL_INDEX UINT16_MAX /** * Lastly emitted opcode is not a function literal */ #define PARSER_NOT_FUNCTION_LITERAL PARSER_INVALID_LITERAL_INDEX /** * Lastly emitted opcode is not a named function literal */ #define PARSER_NAMED_FUNCTION (uint16_t) (PARSER_NOT_FUNCTION_LITERAL - 1) /** * Lastly emitted opcode is not an anonymous class literal */ #define PARSER_ANONYMOUS_CLASS (uint16_t) (PARSER_NAMED_FUNCTION - 1) /* Forward definitions for js-scanner-internal.h. */ struct scanner_context_t; typedef struct scanner_context_t scanner_context_t; /** * List of private field contexts */ typedef struct parser_private_context_t { scanner_class_private_member_t *members_p; /**< current private field context members */ struct parser_private_context_t *prev_p; /**< previous private field context */ uint8_t opts; /**< options */ } parser_private_context_t; /** * Those members of a context which needs * to be saved when a sub-function is parsed. */ typedef struct parser_saved_context_t { /* Parser members. */ uint32_t status_flags; /**< parsing options */ uint16_t stack_depth; /**< current stack depth */ uint16_t stack_limit; /**< maximum stack depth */ struct parser_saved_context_t *prev_context_p; /**< last saved context */ parser_stack_iterator_t last_statement; /**< last statement position */ /* Literal types */ uint16_t argument_count; /**< number of function arguments */ uint16_t argument_length; /**< length property of arguments */ uint16_t register_count; /**< number of registers */ uint16_t literal_count; /**< number of literals */ /* Memory storage members. */ parser_mem_data_t byte_code; /**< byte code buffer */ uint32_t byte_code_size; /**< byte code size for branches */ parser_mem_data_t literal_pool_data; /**< literal list */ parser_scope_stack_t *scope_stack_p; /**< scope stack */ uint16_t scope_stack_size; /**< size of scope stack */ uint16_t scope_stack_top; /**< preserved top of scope stack */ uint16_t scope_stack_reg_top; /**< preserved top register of scope stack */ uint16_t scope_stack_global_end; /**< end of global declarations of a function */ ecma_value_t tagged_template_literal_cp; /**< compressed pointer to the tagged template literal collection */ #ifndef JERRY_NDEBUG uint16_t context_stack_depth; /**< current context stack depth */ #endif /* !JERRY_NDEBUG */ } parser_saved_context_t; /** * Shared parser context. */ typedef struct { PARSER_TRY_CONTEXT (try_buffer); /**< try_buffer */ parser_error_msg_t error; /**< error code */ /** Union for rarely used members. */ union { void *allocated_buffer_p; /**< dynamically allocated buffer * which needs to be freed on error */ scanner_context_t *scanner_context_p; /**< scanner context for the pre-scanner */ } u; uint32_t allocated_buffer_size; /**< size of the dynamically allocated buffer */ /* Parser members. */ uint32_t status_flags; /**< status flags */ uint32_t global_status_flags; /**< global status flags */ uint16_t stack_depth; /**< current stack depth */ uint16_t stack_limit; /**< maximum stack depth */ const jerry_parse_options_t *options_p; /**< parse options */ parser_saved_context_t *last_context_p; /**< last saved context */ parser_stack_iterator_t last_statement; /**< last statement position */ cbc_script_t *script_p; /**< current script */ const uint8_t *source_start_p; /**< source start */ lit_utf8_size_t source_size; /**< source size */ const uint8_t *arguments_start_p; /**< function argument list start */ lit_utf8_size_t arguments_size; /**< function argument list size */ ecma_value_t script_value; /**< current script as value */ ecma_value_t argument_list; /**< current argument list as value */ ecma_value_t user_value; /**< current user value */ #if JERRY_MODULE_SYSTEM ecma_module_names_t *module_names_p; /**< import / export names that is being processed */ lexer_literal_t *module_identifier_lit_p; /**< the literal for the identifier of the current element */ #endif /* JERRY_MODULE_SYSTEM */ /* Lexer members. */ lexer_token_t token; /**< current token */ lexer_lit_object_t lit_object; /**< current literal object */ const uint8_t *source_p; /**< next source byte */ const uint8_t *source_end_p; /**< last source byte */ parser_line_counter_t line; /**< current line */ parser_line_counter_t column; /**< current column */ /* Scanner members. */ scanner_info_t *next_scanner_info_p; /**< next scanner info block */ scanner_info_t *active_scanner_info_p; /**< currently active scanner info block */ scanner_info_t *skipped_scanner_info_p; /**< next scanner info block */ scanner_info_t *skipped_scanner_info_end_p; /**< currently active scanner info block */ /* Compact byte code members. */ cbc_argument_t last_cbc; /**< argument of the last cbc */ uint16_t last_cbc_opcode; /**< opcode of the last cbc */ /* Literal types */ uint16_t argument_count; /**< number of function arguments */ uint16_t argument_length; /**< length property of arguments */ uint16_t register_count; /**< number of registers */ uint16_t literal_count; /**< number of literals */ /* Memory storage members. */ parser_mem_data_t byte_code; /**< byte code buffer */ uint32_t byte_code_size; /**< current byte code size for branches */ parser_list_t literal_pool; /**< literal list */ parser_mem_data_t stack; /**< storage space */ parser_scope_stack_t *scope_stack_p; /**< scope stack */ parser_mem_page_t *free_page_p; /**< space for fast allocation */ uint16_t scope_stack_size; /**< size of scope stack */ uint16_t scope_stack_top; /**< current top of scope stack */ uint16_t scope_stack_reg_top; /**< current top register of scope stack */ uint16_t scope_stack_global_end; /**< end of global declarations of a function */ ecma_value_t tagged_template_literal_cp; /**< compressed pointer to the tagged template literal collection */ parser_private_context_t *private_context_p; /**< private context */ uint8_t stack_top_uint8; /**< top byte stored on the stack */ #ifndef JERRY_NDEBUG /* Variables for debugging / logging. */ uint16_t context_stack_depth; /**< current context stack depth */ #endif /* !JERRY_NDEBUG */ #if JERRY_PARSER_DUMP_BYTE_CODE int is_show_opcodes; /**< show opcodes */ uint32_t total_byte_code_size; /**< total byte code size */ #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ } parser_context_t; /** * @} * @} * @} * * \addtogroup mem Memory allocation * @{ * * \addtogroup mem_parser Parser memory manager * @{ */ /* Memory management. * Note: throws an error if unsuccessful. */ void *parser_malloc (parser_context_t *context_p, size_t size); void parser_free (void *ptr, size_t size); void *parser_malloc_local (parser_context_t *context_p, size_t size); void parser_free_local (void *ptr, size_t size); void parser_free_allocated_buffer (parser_context_t *context_p); /* Parser byte stream. */ void parser_cbc_stream_init (parser_mem_data_t *data_p); void parser_cbc_stream_free (parser_mem_data_t *data_p); void parser_cbc_stream_alloc_page (parser_context_t *context_p, parser_mem_data_t *data_p); /* Parser list. Ensures pointer alignment. */ void parser_list_init (parser_list_t *list_p, uint32_t item_size, uint32_t item_count); void parser_list_free (parser_list_t *list_p); void parser_list_reset (parser_list_t *list_p); void *parser_list_append (parser_context_t *context_p, parser_list_t *list_p); void *parser_list_get (parser_list_t *list_p, size_t index); void parser_list_iterator_init (parser_list_t *list_p, parser_list_iterator_t *iterator_p); void *parser_list_iterator_next (parser_list_iterator_t *iterator_p); /* Parser stack. Optimized for pushing bytes. * Pop functions never throws error. */ void parser_stack_init (parser_context_t *context_p); void parser_stack_free (parser_context_t *context_p); void parser_stack_push_uint8 (parser_context_t *context_p, uint8_t uint8_value); void parser_stack_pop_uint8 (parser_context_t *context_p); void parser_stack_change_last_uint8 (parser_context_t *context_p, uint8_t new_value); uint8_t *parser_stack_get_prev_uint8 (parser_context_t *context_p); void parser_stack_push_uint16 (parser_context_t *context_p, uint16_t uint16_value); uint16_t parser_stack_pop_uint16 (parser_context_t *context_p); void parser_stack_push (parser_context_t *context_p, const void *data_p, uint32_t length); void parser_stack_pop (parser_context_t *context_p, void *data_p, uint32_t length); void parser_stack_iterator_init (parser_context_t *context_p, parser_stack_iterator_t *iterator); uint8_t parser_stack_iterator_read_uint8 (parser_stack_iterator_t *iterator); void parser_stack_iterator_skip (parser_stack_iterator_t *iterator, size_t length); void parser_stack_iterator_read (parser_stack_iterator_t *iterator, void *data_p, size_t length); void parser_stack_iterator_write (parser_stack_iterator_t *iterator, const void *data_p, size_t length); /** * @} * @} * * \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_utils Utility * @{ */ /* Compact byte code emitting functions. */ void parser_flush_cbc (parser_context_t *context_p); void parser_emit_cbc (parser_context_t *context_p, uint16_t opcode); void parser_emit_cbc_literal (parser_context_t *context_p, uint16_t opcode, uint16_t literal_index); void parser_emit_cbc_literal_value (parser_context_t *context_p, uint16_t opcode, uint16_t literal_index, uint16_t value); void parser_emit_cbc_literal_from_token (parser_context_t *context_p, uint16_t opcode); void parser_emit_cbc_call (parser_context_t *context_p, uint16_t opcode, size_t call_arguments); void parser_emit_cbc_push_number (parser_context_t *context_p, bool is_negative_number); void parser_emit_cbc_forward_branch (parser_context_t *context_p, uint16_t opcode, parser_branch_t *branch_p); parser_branch_node_t * parser_emit_cbc_forward_branch_item (parser_context_t *context_p, uint16_t opcode, parser_branch_node_t *next_p); void parser_emit_cbc_backward_branch (parser_context_t *context_p, uint16_t opcode, uint32_t offset); ecma_string_t *parser_new_ecma_string_from_literal (lexer_literal_t *literal_p); void parser_set_branch_to_current_position (parser_context_t *context_p, parser_branch_t *branch_p); void parser_set_breaks_to_current_position (parser_context_t *context_p, parser_branch_node_t *current_p); void parser_set_continues_to_current_position (parser_context_t *context_p, parser_branch_node_t *current_p); void parser_reverse_class_fields (parser_context_t *context_p, size_t fields_size); /* Convenience macros. */ #define parser_emit_cbc_ext(context_p, opcode) parser_emit_cbc ((context_p), PARSER_TO_EXT_OPCODE (opcode)) #define parser_emit_cbc_ext_literal(context_p, opcode, literal_index) \ parser_emit_cbc_literal ((context_p), PARSER_TO_EXT_OPCODE (opcode), (literal_index)) #define parser_emit_cbc_ext_literal_from_token(context_p, opcode) \ parser_emit_cbc_literal_from_token ((context_p), PARSER_TO_EXT_OPCODE (opcode)) #define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \ parser_emit_cbc_call ((context_p), PARSER_TO_EXT_OPCODE (opcode), (call_arguments)) #define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \ parser_emit_cbc_call ((context_p), PARSER_TO_EXT_OPCODE (opcode), (call_arguments)) #define parser_emit_cbc_ext_forward_branch(context_p, opcode, branch_p) \ parser_emit_cbc_forward_branch ((context_p), PARSER_TO_EXT_OPCODE (opcode), (branch_p)) #define parser_emit_cbc_ext_backward_branch(context_p, opcode, offset) \ parser_emit_cbc_backward_branch ((context_p), PARSER_TO_EXT_OPCODE (opcode), (offset)) /** * @} * * \addtogroup jsparser_lexer Lexer * @{ */ /* Lexer functions */ void lexer_next_token (parser_context_t *context_p); bool lexer_check_next_character (parser_context_t *context_p, lit_utf8_byte_t character); bool lexer_check_next_characters (parser_context_t *context_p, lit_utf8_byte_t character1, lit_utf8_byte_t character2); uint8_t lexer_consume_next_character (parser_context_t *context_p); bool lexer_check_post_primary_exp (parser_context_t *context_p); void lexer_skip_empty_statements (parser_context_t *context_p); bool lexer_check_arrow (parser_context_t *context_p); bool lexer_check_arrow_param (parser_context_t *context_p); bool lexer_check_yield_no_arg (parser_context_t *context_p); bool lexer_consume_generator (parser_context_t *context_p); bool lexer_consume_assign (parser_context_t *context_p); void lexer_update_await_yield (parser_context_t *context_p, uint32_t status_flags); bool lexer_scan_private_identifier (parser_context_t *context_p); void lexer_parse_string (parser_context_t *context_p, lexer_string_options_t opts); void lexer_expect_identifier (parser_context_t *context_p, uint8_t literal_type); bool lexer_scan_identifier (parser_context_t *context_p, lexer_parse_options_t opts); void lexer_check_property_modifier (parser_context_t *context_p); void lexer_convert_ident_to_cesu8 (uint8_t *destination_p, const uint8_t *source_p, prop_length_t length); const uint8_t *lexer_convert_literal_to_chars (parser_context_t *context_p, const lexer_lit_location_t *literal_p, uint8_t *local_byte_array_p, lexer_string_options_t opts); void lexer_expect_object_literal_id (parser_context_t *context_p, uint32_t ident_opts); lexer_literal_t *lexer_construct_unused_literal (parser_context_t *context_p); void lexer_construct_literal_object (parser_context_t *context_p, const lexer_lit_location_t *lit_location_p, uint8_t literal_type); bool lexer_construct_number_object (parser_context_t *context_p, bool is_expr, bool is_negative_number); void lexer_convert_push_number_to_push_literal (parser_context_t *context_p); uint16_t lexer_construct_function_object (parser_context_t *context_p, uint32_t extra_status_flags); uint16_t lexer_construct_class_static_block_function (parser_context_t *context_p); void lexer_construct_regexp_object (parser_context_t *context_p, bool parse_only); bool lexer_compare_identifier_to_string (const lexer_lit_location_t *left_p, const uint8_t *right_p, size_t size); bool lexer_compare_identifiers (parser_context_t *context_p, const lexer_lit_location_t *left_p, const lexer_lit_location_t *right_p); bool lexer_current_is_literal (parser_context_t *context_p, const lexer_lit_location_t *right_ident_p); bool lexer_string_is_use_strict (parser_context_t *context_p); bool lexer_string_is_directive (parser_context_t *context_p); bool lexer_token_is_identifier (parser_context_t *context_p, const char *identifier_p, size_t identifier_length); bool lexer_token_is_let (parser_context_t *context_p); bool lexer_token_is_async (parser_context_t *context_p); bool lexer_compare_literal_to_string (parser_context_t *context_p, const char *string_p, size_t string_length); void lexer_init_line_info (parser_context_t *context_p); uint8_t lexer_convert_binary_lvalue_token_to_binary (uint8_t token); /** * @} * * \addtogroup jsparser_expr Expression parser * @{ */ /* Parser functions. */ void parser_parse_block_expression (parser_context_t *context_p, int options); void parser_parse_expression_statement (parser_context_t *context_p, int options); void parser_parse_expression (parser_context_t *context_p, int options); void parser_resolve_private_identifier (parser_context_t *context_p); void parser_save_private_context (parser_context_t *context_p, parser_private_context_t *private_ctx_p, scanner_class_info_t *class_info_p); void parser_restore_private_context (parser_context_t *context_p, parser_private_context_t *private_ctx_p); void parser_parse_class (parser_context_t *context_p, bool is_statement); void parser_parse_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); void parser_parse_initializer_by_next_char (parser_context_t *context_p, parser_pattern_flags_t flags); /** * @} * * \addtogroup jsparser_scanner Scanner * @{ */ void scanner_release_next (parser_context_t *context_p, size_t size); void scanner_set_active (parser_context_t *context_p); void scanner_revert_active (parser_context_t *context_p); void scanner_release_active (parser_context_t *context_p, size_t size); void scanner_release_switch_cases (scanner_case_info_t *case_p); void scanner_release_private_fields (scanner_class_private_member_t *member_p); void scanner_seek (parser_context_t *context_p); void scanner_reverse_info_list (parser_context_t *context_p); void scanner_cleanup (parser_context_t *context_p); bool scanner_is_context_needed (parser_context_t *context_p, parser_check_context_type_t check_type); bool scanner_try_scan_new_target (parser_context_t *context_p); void scanner_check_variables (parser_context_t *context_p); void scanner_create_variables (parser_context_t *context_p, uint32_t option_flags); void scanner_get_location (scanner_location_t *location_p, parser_context_t *context_p); void scanner_set_location (parser_context_t *context_p, scanner_location_t *location_p); uint16_t scanner_decode_map_to (parser_scope_stack_t *stack_item_p); uint16_t scanner_save_literal (parser_context_t *context_p, uint16_t ident_index); bool scanner_literal_is_const_reg (parser_context_t *context_p, uint16_t literal_index); bool scanner_literal_is_created (parser_context_t *context_p, uint16_t literal_index); bool scanner_literal_exists (parser_context_t *context_p, uint16_t literal_index); void scanner_scan_all (parser_context_t *context_p); /** * @} * * \addtogroup jsparser_stmt Statement parser * @{ */ void parser_parse_statements (parser_context_t *context_p); void parser_free_jumps (parser_stack_iterator_t iterator); #if JERRY_MODULE_SYSTEM /** * @} * * \addtogroup jsparser_stmt Module statement parser * @{ */ extern const lexer_lit_location_t lexer_default_literal; void parser_module_check_request_place (parser_context_t *context_p); void parser_module_context_init (parser_context_t *context_p); void parser_module_append_names (parser_context_t *context_p, ecma_module_names_t **module_names_p); void parser_module_handle_module_specifier (parser_context_t *context_p, ecma_module_node_t **node_list_p); void parser_module_handle_requests (parser_context_t *context_p); void parser_module_parse_export_clause (parser_context_t *context_p); void parser_module_parse_import_clause (parser_context_t *context_p); void parser_module_set_default (parser_context_t *context_p); bool parser_module_check_duplicate_import (parser_context_t *context_p, ecma_string_t *local_name_p); bool parser_module_check_duplicate_export (parser_context_t *context_p, ecma_string_t *export_name_p); void parser_module_append_export_name (parser_context_t *context_p); void parser_module_add_names_to_node (parser_context_t *context_p, ecma_string_t *imex_name_p, ecma_string_t *local_name_p); #endif /* JERRY_MODULE_SYSTEM */ /** * @} * * \addtogroup jsparser_parser Parser * @{ */ ecma_compiled_code_t *parser_parse_function (parser_context_t *context_p, uint32_t status_flags); ecma_compiled_code_t *parser_parse_class_static_block (parser_context_t *context_p); ecma_compiled_code_t *parser_parse_arrow_function (parser_context_t *context_p, uint32_t status_flags); ecma_compiled_code_t *parser_parse_class_fields (parser_context_t *context_p); void parser_set_function_name (parser_context_t *context_p, uint16_t function_literal_index, uint16_t name_index, uint32_t status_flags); void parser_compiled_code_set_function_name (parser_context_t *context_p, ecma_compiled_code_t *bytecode_p, uint16_t name_index, uint32_t status_flags); uint16_t parser_check_anonymous_function_declaration (parser_context_t *context_p); /* Error management. */ void parser_raise_error (parser_context_t *context_p, parser_error_msg_t error); #if JERRY_PARSER_DUMP_BYTE_CODE void util_print_cbc (ecma_compiled_code_t *compiled_code_p); #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ /** * @} * @} * @} */ #endif /* !JS_PARSER_INTERNAL_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/config.h000664 001750 001750 00000001661 15165022620 027332 0ustar00ddennedyddennedy000000 000000 /* * SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia * * SPDX-License-Identifier: GPL-3.0-or-later */ #define THORVG_SW_RASTER_SUPPORT 1 #cmakedefine THORVG_THREAD_SUPPORT 1 #cmakedefine THORVG_GL_RASTER_SUPPORT 1 #cmakedefine THORVG_GL_TARGET_GL 1 #cmakedefine THORVG_GL_TARGET_GLES 1 // #define THORVG_WG_RASTER_SUPPORT 0 // #define THORVG_PARTIAL_RENDER_SUPPORT 0 // #define THORVG_SVG_LOADER_SUPPORT 0 // #define THORVG_PNG_LOADER_SUPPORT 0 // #define THORVG_JPG_LOADER_SUPPORT 0 // #define THORVG_LOTTIE_LOADER_SUPPORT 0 // #define THORVG_TTF_LOADER_SUPPORT 0 // #define THORVG_WEBP_LOADER_SUPPORT 0 // #define THORVG_GIF_SAVER_SUPPORT 0 // #define THORVG_AVX_VECTOR_SUPPORT 0 // #define THORVG_NEON_VECTOR_SUPPORT 0 // #define THORVG_CAPI_BINDING_SUPPORT 0 // #define THORVG_LOG_ENABLED 0 // #define THORVG_FILE_IO_SUPPORT 0 // #define THORVG_LOTTIE_EXPRESSIONS_SUPPORT 0 #define THORVG_VERSION_STRING "1.0.1" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-error-messages.inc.h000664 001750 001750 00000125414 15164251010 045727 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is automatically generated by the gen-strings.py script * from ecma-error-messages.ini. Do not edit! */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_INVALID_GROUP, "Invalid group") ECMA_ERROR_DEF (ECMA_ERR_INVALID_ESCAPE, "Invalid escape") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_ATOMICS || JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_INVALID_LENGTH, "Invalid length") #endif /* JERRY_BUILTIN_ATOMICS \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_INVALID_OFFSET, "Invalid offset") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_OBJECT_EXPECTED, "Object expected") #if JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_INVALID_ARGUMENT, "Invalid argument") #endif /* JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_REGEXP */ ECMA_ERROR_DEF (ECMA_ERR_INVALID_ENCODING, "Invalid encoding") #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_NOTHING_TO_REPEAT, "Nothing to repeat") #endif /* JERRY_BUILTIN_REGEXP */ ECMA_ERROR_DEF (ECMA_ERR_EXPECTED_AN_OBJECT, "Expected an object") ECMA_ERROR_DEF (ECMA_ERR_INVALID_CAPABILITY, "Invalid capability") #if JERRY_BUILTIN_STRING ECMA_ERROR_DEF (ECMA_ERR_INVALID_CODE_POINT, "Invalid code point") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_INVALID_QUANTIFIER, "Invalid quantifier") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_NO_SOURCE_ARGUMENT, "No source argument") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_UNTERMINATED_GROUP, "Unterminated group") #endif /* JERRY_BUILTIN_REGEXP */ ECMA_ERROR_DEF (ECMA_ERR_EXPECTED_A_FUNCTION, "Expected a function") ECMA_ERROR_DEF (ECMA_ERR_INVALID_UTF8_STRING, "Invalid UTF8 string") #if JERRY_BUILTIN_STRING ECMA_ERROR_DEF (ECMA_ERR_INVALID_COUNT_VALUE, "Invalid count value") #endif /* JERRY_BUILTIN_STRING */ #if !(JERRY_BUILTIN_REALMS) ECMA_ERROR_DEF (ECMA_ERR_REALMS_ARE_DISABLED, "Realms are disabled") #endif /* !(JERRY_BUILTIN_REALMS) */ ECMA_ERROR_DEF (ECMA_ERR_UNDEFINED_REFERENCE, "Undefined reference") ECMA_ERROR_DEF (ECMA_ERR_INVALID_ARRAY_LENGTH, "Invalid Array length") #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_INVALID_REGEXP_FLAGS, "Invalid RegExp flags") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_STACK_LIMIT_EXCEEDED, "Stack limit exceeded") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_WEAKREF ECMA_ERROR_DEF (ECMA_ERR_TARGET_IS_NOT_OBJECT, "Target is not Object") #endif /* JERRY_BUILTIN_WEAKREF */ #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_BIGINT_VALUE_EXPECTED, "BigInt value expected") #endif /* JERRY_BUILTIN_BIGINT */ ECMA_ERROR_DEF (ECMA_ERR_BINDING_CANNOT_SET, "Binding cannot be set") #if JERRY_BUILTIN_STRING ECMA_ERROR_DEF (ECMA_ERR_INVALID_STRING_, "Invalid string length") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_KEY_MUST_BE_AN_OBJECT, "Key must be an object") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_ARRAY ECMA_ERROR_DEF (ECMA_ERR_MISSING_ARRAY_ELEMENT, "Missing Array element") #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_WEAKREF ECMA_ERROR_DEF (ECMA_ERR_TARGET_IS_NOT_WEAKREF, "Target is not weakRef") #endif /* JERRY_BUILTIN_WEAKREF */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TARGET_NOT_EXTENSIBLE, "Target not extensible") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_ANNEXB ECMA_ERROR_DEF (ECMA_ERR_GETTER_IS_NOT_CALLABLE, "Getter is not callable") #endif /* JERRY_BUILTIN_ANNEXB */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_HANDLER_CANNOT_BE_NULL, "Handler cannot be null") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_INVALID_UTF8_CHARACTER, "Invalid UTF8 character") ECMA_ERROR_DEF (ECMA_ERR_INVALID_UTF8_CODEPOINT, "Invalid UTF8 codepoint") #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_INVALID_CONTAINER_TYPE, "Invalid container type") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_INVALID_RANGE_OF_INDEX, "Invalid range of index") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if !(JERRY_BUILTIN_PROXY) ECMA_ERROR_DEF (ECMA_ERR_PROXY_IS_NOT_SUPPORTED, "Proxy is not supported") #endif /* !(JERRY_BUILTIN_PROXY) */ #if !(JERRY_BUILTIN_REALMS) ECMA_ERROR_DEF (ECMA_ERR_REALM_IS_NOT_AVAILABLE, "Realm is not available") #endif /* !(JERRY_BUILTIN_REALMS) */ #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_BIGINT_ZERO_DIVISION, "BigInt division by zero") #endif /* JERRY_BUILTIN_BIGINT */ ECMA_ERROR_DEF (ECMA_ERR_EXPECTED_AN_ARRAYBUFFER, "Expected an ArrayBuffer") #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_INVALID_CHARACTER_CLASS, "Invalid character class") ECMA_ERROR_DEF (ECMA_ERR_INVALID_ESCAPE_SEQUENCE, "Invalid escape sequence") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_SNAPSHOT_EXEC ECMA_ERROR_DEF (ECMA_ERR_INVALID_SNAPSHOT_FORMAT, "Invalid snapshot format") #endif /* JERRY_SNAPSHOT_EXEC */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_LONE_QUANTIFIER_BRACKET, "Lone quantifier bracket") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_OBJECT_CANNOT_BE_FROZEN, "Object cannot be frozen") ECMA_ERROR_DEF (ECMA_ERR_OBJECT_CANNOT_BE_SEALED, "Object cannot be sealed") #endif /* JERRY_BUILTIN_PROXY */ #if !(JERRY_BUILTIN_REGEXP) ECMA_ERROR_DEF (ECMA_ERR_REGEXP_IS_NOT_SUPPORTED, "RegExp is not supported") #endif /* !(JERRY_BUILTIN_REGEXP) */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_UNMATCHED_CLOSE_BRACKET, "Unmatched close bracket") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_NOT_MODULE, "Argument is not a module") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_SET_PROTOTYPE, "Cannot set [[Prototype]]") #if JERRY_BUILTIN_ARRAY ECMA_ERROR_DEF (ECMA_ERR_INVALID_NEW_ARRAY_LENGTH, "Invalid new Array length") #endif /* JERRY_BUILTIN_ARRAY */ ECMA_ERROR_DEF (ECMA_ERR_ITERATOR_IS_NOT_CALLABLE, "Iterator is not callable") #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_MODULE_IS_IN_ERROR_STATE, "Module is in error state") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_REJECT_MUST_BE_UNDEFINED, "Reject must be undefined") #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_REQUEST_IS_NOT_AVAILABLE, "Request is not available") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT, "Argument is not an object") #if JERRY_BUILTIN_ATOMICS ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_NOT_SUPPORTED, "Argument is not supported") #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_STRING ECMA_ERROR_DEF (ECMA_ERR_INVALID_CODE_POINT_ERROR, "Error: Invalid code point") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_INVALID_TYPEDARRAY_LENGTH, "Invalid TypedArray length") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_INVALID_HEXADECIMAL_VALUE, "Invalid hexadecimal value") ECMA_ERROR_DEF (ECMA_ERR_ITERATOR_IS_NOT_AN_OBJECT, "Iterator is not an object") ECMA_ERROR_DEF (ECMA_ERR_RESOLVE_MUST_BE_UNDEFINED, "Resolve must be undefined") #if JERRY_SNAPSHOT_SAVE ECMA_ERROR_DEF (ECMA_ERR_SNAPSHOT_BUFFER_SMALL, "Snapshot buffer too small") #endif /* JERRY_SNAPSHOT_SAVE */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_UNEXPECTED_END_OF_PATTERN, "Unexpected end of pattern") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_SNAPSHOT_SAVE ECMA_ERROR_DEF (ECMA_ERR_SNAPSHOT_UNSUPPORTED_COMPILED_CODE, "Unsupported compiled code") #endif /* JERRY_SNAPSHOT_SAVE */ #if !(JERRY_BUILTIN_BIGINT) ECMA_ERROR_DEF (ECMA_ERR_BIGINT_NOT_SUPPORTED, "BigInt support is disabled") #endif /* !(JERRY_BUILTIN_BIGINT) */ #if JERRY_BUILTIN_DATAVIEW ECMA_ERROR_DEF (ECMA_ERR_EXPECTED_A_DATAVIEW_OBJECT, "Expected a DataView object") #endif /* JERRY_BUILTIN_DATAVIEW */ ECMA_ERROR_DEF (ECMA_ERR_EXPECTED_A_FUNCTION_OBJECT, "Expected a function object") #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_INVALID_ARRAYBUFFER_LENGTH, "Invalid ArrayBuffer length") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if !(JERRY_MODULE_SYSTEM) ECMA_ERROR_DEF (ECMA_ERR_MODULE_NOT_SUPPORTED, "Module support is disabled") #endif /* !(JERRY_MODULE_SYSTEM) */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_OBJECT_IS_NOT_A_TYPEDARRAY, "Object is not a TypedArray") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_RECEIVER_MUST_BE_AN_OBJECT, "Receiver must be an object") #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_BIGINT_SERIALIZED, "BigInt cannot be serialized") #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_CONTAINER_IS_NOT_AN_OBJECT, "Container is not an object.") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_INVALID_HEX_ESCAPE_SEQUENCE, "Invalid hex escape sequence") #endif /* JERRY_BUILTIN_REGEXP */ ECMA_ERROR_DEF (ECMA_ERR_INVALID_SPECIES_CONSTRUCTOR, "Invalid species constructor") ECMA_ERROR_DEF (ECMA_ERR_PROXY_TRAP_RETURNED_FALSISH, "Proxy trap returned falsish") #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_MIN_GREATER_THAN_MAX, "Quantifier error: min > max") #endif /* JERRY_BUILTIN_REGEXP */ ECMA_ERROR_DEF (ECMA_ERR_SYMBOL_IS_NOT_A_CONSTRUCTOR, "Symbol is not a constructor") #if JERRY_BUILTIN_REFLECT ECMA_ERROR_DEF (ECMA_ERR_TARGET_IS_NOT_A_CONSTRUCTOR, "Target is not a constructor") #endif /* JERRY_BUILTIN_REFLECT */ ECMA_ERROR_DEF (ECMA_ERR_ACCESSOR_WRITABLE, "Accessors cannot be writable") #if !(JERRY_BUILTIN_DATAVIEW) ECMA_ERROR_DEF (ECMA_ERR_DATA_VIEW_NOT_SUPPORTED, "DataView support is disabled") #endif /* !(JERRY_BUILTIN_DATAVIEW) */ #if JERRY_BUILTIN_DATE ECMA_ERROR_DEF (ECMA_ERR_DATE_MUST_BE_A_FINITE_NUMBER, "Date must be a finite number") #endif /* JERRY_BUILTIN_DATE */ ECMA_ERROR_DEF (ECMA_ERR_LOCAL_VARIABLE_IS_REDECLARED, "Local variable is redeclared") ECMA_ERROR_DEF (ECMA_ERR_UNSUPPORTED_BINARY_OPERATION, "Unsupported binary operation") #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_UNTERMINATED_CHARACTER_CLASS, "Unterminated character class") #endif /* JERRY_BUILTIN_REGEXP */ ECMA_ERROR_DEF (ECMA_ERR_ARRAYBUFFER_IS_DETACHED, "ArrayBuffer has been detached") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_NOT_AN_OBJECT, "Constructor must be an object") #if !(JERRY_BUILTIN_CONTAINER) ECMA_ERROR_DEF (ECMA_ERR_CONTAINER_NOT_SUPPORTED, "Container support is disabled") #endif /* !(JERRY_BUILTIN_CONTAINER) */ #if JERRY_BUILTIN_REALMS ECMA_ERROR_DEF (ECMA_ERR_FIRST_ARGUMENT_IS_NOT_A_REALM, "First argument is not a realm") #endif /* JERRY_BUILTIN_REALMS */ #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_INCOMPATIBLE_TYPEDARRAY_TYPES, "Incompatible TypedArray types") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_INCORRECT_TYPE_FOR_TYPEDARRAY, "Incorrect type for TypedArray") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_INVALID_OR_OUT_OF_RANGE_INDEX, "Invalid or out-of-range index") #if JERRY_SNAPSHOT_SAVE ECMA_ERROR_DEF (ECMA_ERR_MAXIMUM_SNAPSHOT_SIZE, "Maximum snapshot size reached") #endif /* JERRY_SNAPSHOT_SAVE */ #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_MODULE_CANNOT_BE_INSTANTIATED, "Module cannot be instantiated") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_SPECIES_MUST_BE_A_CONSTRUCTOR, "Species must be a constructor") ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_IS_NOT_A_PROXY, "Argument is not a Proxy object") #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_NOT_ARRAY_BUFFER, "Argument is not an ArrayBuffer") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_MAP_REQUIRES_NEW, "Constructor Map requires 'new'") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_SET_REQUIRES_NEW, "Constructor Set requires 'new'") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_MODULE_MUST_BE_IN_LINKED_STATE, "Module must be in linked state") ECMA_ERROR_DEF (ECMA_ERR_UNKNOWN_EXPORT, "Native module export not found") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_PASSED_ARGUMENT_IS_NOT_A_REALM, "Passed argument is not a realm") ECMA_ERROR_DEF (ECMA_ERR_PRIVATE_METHOD_IS_NOT_WRITABLE, "Private method is not writable") #if JERRY_BUILTIN_BIGINT || JERRY_BUILTIN_NUMBER ECMA_ERROR_DEF (ECMA_ERR_RADIX_IS_OUT_OF_RANGE, "Radix must be between 2 and 36") #endif /* JERRY_BUILTIN_BIGINT \ || JERRY_BUILTIN_NUMBER */ #if !(JERRY_SNAPSHOT_EXEC) ECMA_ERROR_DEF (ECMA_ERR_SNAPSHOT_EXEC_DISABLED, "Snapshot execution is disabled") #endif /* !(JERRY_SNAPSHOT_EXEC) */ #if !(JERRY_BUILTIN_TYPEDARRAY) ECMA_ERROR_DEF (ECMA_ERR_TYPED_ARRAY_NOT_SUPPORTED, "TypedArray support is disabled") #endif /* !(JERRY_BUILTIN_TYPEDARRAY) */ ECMA_ERROR_DEF (ECMA_ERR_UNICODE_SURROGATE_PAIR_MISSING, "Unicode surrogate pair missing") #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_INVALID_CONTROL_ESCAPE_SEQUENCE, "Invalid control escape sequence") ECMA_ERROR_DEF (ECMA_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE, "Invalid unicode escape sequence") #endif /* JERRY_BUILTIN_REGEXP */ ECMA_ERROR_DEF (ECMA_ERR_ITERATOR_NEXT_IS_NOT_CALLABLE, "Iterator 'next' is not callable") ECMA_ERROR_DEF (ECMA_ERR_ITERATOR_VALUE_IS_NOT_AN_OBJECT, "Iterator value is not an object") ECMA_ERROR_DEF (ECMA_ERR_RESOLVE_METHOD_MUST_BE_CALLABLE, "Resolve method must be callable") #if !(JERRY_SNAPSHOT_SAVE) ECMA_ERROR_DEF (ECMA_ERR_SNAPSHOT_SAVE_DISABLED, "Snapshot generation is disabled") #endif /* !(JERRY_SNAPSHOT_SAVE) */ #if !(JERRY_PARSER) ECMA_ERROR_DEF (ECMA_ERR_PARSER_NOT_SUPPORTED, "Source code parsing is disabled") #endif /* !(JERRY_PARSER) */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_MUST_RETURN_WITH_AN_OBJECT, "Trap must return with an object") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_UNSUPPORTED_CONTAINER_OPERATION, "Unsupported container operation") #endif /* JERRY_BUILTIN_CONTAINER */ ECMA_ERROR_DEF (ECMA_ERR_METHOD_RETURN_IS_NOT_CALLABLE, "method 'return' is not callable") ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_PROMISE, "Argument 'this' is not a Promise") ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT, "Argument 'this' is not an object") ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_SYMBOL, "Argument 'this' must be a Symbol") #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_CALLBACK_RESULT_NOT_MODULE, "Callback result must be a module") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_CLASS_CONSTRUCTOR_REQUIRES_NEW, "Class constructor requires 'new'") #if JERRY_BUILTIN_ARRAY ECMA_ERROR_DEF (ECMA_ERR_COMPARE_FUNC_NOT_CALLABLE, "Compare function is not callable") #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_PROXY_REQUIRES_NEW, "Constructor Proxy requires 'new'") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_EXPECTED_A_CONFIGURABLE_PROPERTY, "Expected a configurable property") ECMA_ERROR_DEF (ECMA_ERR_FIRST_PARAMETER_MUST_BE_CALLABLE, "First parameter must be callable") #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_FUNCTION_ADD_ORSET_IS_NOT_CALLABLE, "Function add/set is not callable") #endif /* JERRY_BUILTIN_CONTAINER */ ECMA_ERROR_DEF (ECMA_ERR_ITERATOR_RESULT_IS_NOT_AN_OBJECT, "Iterator result is not an object") #if (JERRY_STACK_LIMIT != 0) ECMA_ERROR_DEF (ECMA_ERR_MAXIMUM_CALL_STACK_SIZE_EXCEEDED, "Maximum call stack size exceeded") #endif /* (JERRY_STACK_LIMIT != 0) */ ECMA_ERROR_DEF (ECMA_ERR_MAXIMUM_STRING_LENGTH_IS_REACHED, "Maximum string length is reached") #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_MODULE_MUST_BE_IN_UNLINKED_STATE, "Module must be in unlinked state") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_SNAPSHOT_EXEC ECMA_ERROR_DEF (ECMA_ERR_STATIC_SNAPSHOTS_ARE_NOT_ENABLED, "Static snapshots are not enabled") #endif /* JERRY_SNAPSHOT_EXEC */ #if JERRY_BUILTIN_WEAKREF ECMA_ERROR_DEF (ECMA_ERR_WEAKREF_TARGET_MUST_BE_AN_OBJECT, "WeakRef target must be an object") #endif /* JERRY_BUILTIN_WEAKREF */ ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_FUNCTION, "Argument 'this' is not a function") #if JERRY_BUILTIN_ATOMICS ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_NOT_SHARED_ARRAY_BUFFER, "Argument is not SharedArrayBuffer") #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_ARRAY || JERRY_BUILTIN_CONTAINER || JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE, "Callback function is not callable") #endif /* JERRY_BUILTIN_ARRAY \ || JERRY_BUILTIN_CONTAINER \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_INITIAL_VALUE_CANNOT_BE_UNDEFINED, "Initial value cannot be undefined") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER ECMA_ERROR_DEF (ECMA_ERR_INVALID_SHARED_ARRAYBUFFER_LENGTH, "Invalid Shared ArrayBuffer length") #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ ECMA_ERROR_DEF (ECMA_ERR_INVALID_TYPE_FOR_CONSTRUCTOR_CALL, "Invalid type for constructor call") ECMA_ERROR_DEF (ECMA_ERR_ITERATOR_THROW_IS_NOT_AVAILABLE, "Iterator 'throw' is not available") #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_NAMESPACE_OBJECT_IS_NOT_AVAILABLE, "Namespace object is not available") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_PROXY_TARGET_IS_NOT_A_CONSTRUCTOR, "Proxy target is not a constructor") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_REALMS ECMA_ERROR_DEF (ECMA_ERR_SECOND_ARGUMENT_MUST_BE_AN_OBJECT, "Second argument must be an object") #endif /* JERRY_BUILTIN_REALMS */ #if JERRY_BUILTIN_DATAVIEW ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_BUFFER_NOT_OBJECT, "Argument 'buffer' is not an object") #endif /* JERRY_BUILTIN_DATAVIEW */ ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_ITERATOR, "Argument 'this' is not an iterator") ECMA_ERROR_DEF (ECMA_ERR_VALUE_MSG, "Argument cannot be marked as error") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_PROMISE_REQUIRES_NEW, "Constructor Promise requires 'new'") #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_WEAKMAP_REQUIRES_NEW, "Constructor WeakMap requires 'new'") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_WEAKSET_REQUIRES_NEW, "Constructor WeakSet requires 'new'") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_MAXIMUM_TYPEDARRAY_SIZE_IS_REACHED, "Maximum TypedArray size is reached") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_THE_GIVEN_ARGUMENT_IS_NOT_A_SYMBOL, "The given argument is not a Symbol") ECMA_ERROR_DEF (ECMA_ERR_PARAMETER_REJECT_MUST_BE_CALLABLE, "'reject' parameter must be callable") #if JERRY_BUILTIN_ATOMICS || JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_TYPED_ARRAY, "Argument 'this' is not a TypedArray") #endif /* JERRY_BUILTIN_ATOMICS \ || JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_SNAPSHOT_SAVE ECMA_ERROR_DEF (ECMA_ERR_CANNOT_ALLOCATE_MEMORY_LITERALS, "Cannot allocate memory for literals") #endif /* JERRY_SNAPSHOT_SAVE */ ECMA_ERROR_DEF (ECMA_ERR_INVOKE_NULLABLE_SUPER_METHOD, "Cannot invoke nullable super method") #if JERRY_BUILTIN_DATAVIEW ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_DATAVIEW_REQUIRES_NEW, "Constructor DataView requires 'new'") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_WEAKREF ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_WEAKREF_REQUIRES_NEW, "Constructor WeakRef requires 'new'.") #endif /* JERRY_BUILTIN_WEAKREF */ ECMA_ERROR_DEF (ECMA_ERR_SUPER_BINDING_MUST_BE_A_CONSTRUCTOR, "Super binding must be a constructor") #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_VALUE_CANNOT_BE_CONVERTED_TO_BIGINT, "Value cannot be converted to BigInt") #endif /* JERRY_BUILTIN_BIGINT */ ECMA_ERROR_DEF (ECMA_ERR_PARAMETER_RESOLVE_MUST_BE_CALLABLE, "'resolve' parameter must be callable") #if JERRY_BUILTIN_DATE ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_DATE_OBJECT, "Argument 'this' is not a Date object") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_CONSTRUCTOR, "Argument 'this' is not a constructor") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_BIGINT_FUNCTION_NOT_CONSTRUCTOR, "BigInt function is not a constructor") #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTED_OBJECT_IS_NOT_TYPEDARRAY, "Constructed object is not TypedArray") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_INT8_ARRAY_REQUIRES_NEW, "Constructor Int8Array requires 'new'") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_CONTAINER_IS_NOT_A_CONTAINER_OBJECT, "Container is not a container object.") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_DATE ECMA_ERROR_DEF (ECMA_ERR_INVALID_ARGUMENT_TYPE_IN_TOPRIMITIVE, "Invalid argument type in toPrimitive") #endif /* JERRY_BUILTIN_DATE */ #if JERRY_ERROR_MESSAGES ECMA_ERROR_DEF (ECMA_ERR_METHODS_INVOKE_WITH_NEW, "Methods cannot be invoked with 'new'") #endif /* JERRY_ERROR_MESSAGES */ #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_MODULE_EXPORTS_MUST_BE_STRING_VALUES, "Module exports must be string values") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_PROTOTYPE_IS_NEITHER_OBJECT_NOR_NULL, "Prototype is neither object nor null") #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_THE_MAPFN_ARGUMENT_IS_NOT_CALLABLE, "The 'mapfn' argument is not callable") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_THE_TWO_DESCRIPTORS_ARE_INCOMPATIBLE, "The two descriptors are incompatible") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_WRONG_ARGS_MSG, "This type of argument is not allowed") #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_CONTAINER_NEEDED, "Value is not a Container or Iterator") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_REG_EXP, "Argument 'this' is not a valid RegExp") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_ARRAY_BUFFER_DETACHED, "ArrayBuffer has already been detached") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_BUILTIN_ROUTINES_HAVE_NO_CONSTRUCTOR, "Built-in routines have no constructor") #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_INT16_ARRAY_REQUIRES_NEW, "Constructor Int16Array requires 'new'") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_INT32_ARRAY_REQUIRES_NEW, "Constructor Int32Array requires 'new'") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_UINT8_ARRAY_REQUIRES_NEW, "Constructor Uint8Array requires 'new'") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_SNAPSHOT_EXEC ECMA_ERROR_DEF (ECMA_ERR_FUNCTION_INDEX_IS_HIGHER_THAN_MAXIMUM, "Function index is higher than maximum") #endif /* JERRY_SNAPSHOT_EXEC */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_RANGE_OUT_OF_ORDER_IN_CHARACTER_CLASS, "Range out of order in character class") #endif /* JERRY_BUILTIN_REGEXP */ ECMA_ERROR_DEF (ECMA_ERR_RESULT_OF_DEFAULTVALUE_IS_INVALID, "Result of [[DefaultValue]] is invalid") ECMA_ERROR_DEF (ECMA_ERR_RIGHT_VALUE_OF_IN_MUST_BE_AN_OBJECT, "Right value of 'in' must be an object") #if !(JERRY_BUILTIN_SHAREDARRAYBUFFER) ECMA_ERROR_DEF (ECMA_ERR_SHARED_ARRAYBUFFER_NOT_SUPPORTED, "SharedArrayBuffer support is disabled") #endif /* !(JERRY_BUILTIN_SHAREDARRAYBUFFER) */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_RETURNED_NEITHER_OBJECT_NOR_NULL, "Trap returned neither object nor null") ECMA_ERROR_DEF (ECMA_ERR_TRAP_WITH_DUPLICATED_ENTRIES, "Trap returned with duplicated entries") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_UNARY_PLUS_IS_NOT_ALLOWED_FOR_BIGINTS, "Unary plus is not allowed for BigInts") #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_IS_NOT_AN_REGEXP, "Argument 'this' is not a RegExp object") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_ALLOCATE_ARRAY_BUFFER, "Cannot allocate memory for ArrayBuffer") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_CONSTANT_BINDINGS_CANNOT_BE_REASSIGNED, "Constant bindings cannot be reassigned") #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_ARRAYBUFFER_REQUIRES_NEW, "Constructor ArrayBuffer requires 'new'") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_UINT16_ARRAY_REQUIRES_NEW, "Constructor Uint16Array requires 'new'") ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_UINT32_ARRAY_REQUIRES_NEW, "Constructor Uint32Array requires 'new'") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_GENERATOR_IS_CURRENTLY_UNDER_EXECUTION, "Generator is currently under execution") ECMA_ERROR_DEF (ECMA_ERR_ITERATOR_RETURN_RESULT_IS_NOT_OBJECT, "Iterator 'return' result is not object") ECMA_ERROR_DEF (ECMA_ERR_SEARCH_STRING_CANNOT_BE_OF_TYPE_REGEXP, "Search string can't be of type: RegExp") ECMA_ERROR_DEF (ECMA_ERR_VALUE_RECEIVED_BY_YIELD_IS_NOT_OBJECT, "Value received by yield* is not object") #if JERRY_BUILTIN_BOOLEAN ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_BOOLEAN_OBJECT, "Argument 'this' is not a Boolean object") #endif /* JERRY_BUILTIN_BOOLEAN */ ECMA_ERROR_DEF (ECMA_ERR_CANNOT_DECLARE_SAME_PRIVATE_FIELD_TWICE, "Cannot declare same private field twice") #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_FLOAT32_ARRAY_REQUIRES_NEW, "Constructor Float32Array requires 'new'") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_TYPEDARRAY && JERRY_NUMBER_TYPE_FLOAT64 ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_FLOAT64_ARRAY_REQUIRES_NEW, "Constructor Float64Array requires 'new'") #endif /* JERRY_BUILTIN_TYPEDARRAY && JERRY_NUMBER_TYPE_FLOAT64 */ ECMA_ERROR_DEF (ECMA_ERR_FUNCTION_PROTOTYPE_NOT_A_CONSTRUCTOR, "Function.prototype is not a constructor") #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_IMPORTED_BINDING_SHADOWS_LOCAL_VARIABLE, "Imported binding shadows local variable") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_PROTOTYPE_FROM_REVOKED_PROXY_IS_INVALID, "Prototype from revoked Proxy is invalid") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_REGEXP && JERRY_BUILTIN_STRING ECMA_ERROR_DEF (ECMA_ERR_REGEXP_ARGUMENT_SHOULD_HAVE_GLOBAL_FLAG, "RegExp argument should have global flag") #endif /* JERRY_BUILTIN_REGEXP && JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_IS_NEITHER_AN_OBJECT_NOR_UNDEFINED, "Trap is neither an object nor undefined") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_PROMISE_RESOLVE_ITSELF, "A promise cannot be resolved with itself") #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_BIGINT64_ARRAY_REQUIRES_NEW, "Constructor BigInt64Array requires 'new'") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_MODULE_EXPORTS_MUST_BE_VALID_IDENTIFIERS, "Module exports must be valid identifiers") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_GENERATOR_OBJECT, "Argument 'this' is not a generator object") ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_CANNOT_CONVERT_TO_OBJECT, "Argument cannot be converted to an object") #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_ALLOCATE_BIGINT_VALUE, "Cannot allocate memory for a BigInt value") ECMA_ERROR_DEF (ECMA_ERR_CONVERT_BIGINT_TO_NUMBER, "Cannot convert a BigInt value to a number") #endif /* JERRY_BUILTIN_BIGINT */ ECMA_ERROR_DEF (ECMA_ERR_CONVERT_SYMBOL_TO_NUMBER, "Cannot convert a Symbol value to a number") ECMA_ERROR_DEF (ECMA_ERR_CONVERT_SYMBOL_TO_STRING, "Cannot convert a Symbol value to a string") #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_BIG_UINT64_ARRAY_REQUIRES_NEW, "Constructor BigUInt64Array requires 'new'") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_NUMBER ECMA_ERROR_DEF (ECMA_ERR_FRACTION_DIGITS_OUT_OF_RANGE, "Fraction digits must be between 0 and 100") #endif /* JERRY_BUILTIN_NUMBER */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_RETURN_VALUE_IS_NOT_AN_ARRAYBUFFER_OBJECT, "Return value is not an ArrayBuffer object") #endif /* JERRY_BUILTIN_TYPEDARRAY */ ECMA_ERROR_DEF (ECMA_ERR_SUPER_CONSTRUCTOR_MAY_ONLY_BE_CALLED_ONCE, "Super constructor may only be called once") ECMA_ERROR_DEF (ECMA_ERR_BINDING_NOT_EXIST_OR_UNINITIALIZED, "Binding does not exist or is uninitialised") #if JERRY_BUILTIN_ARRAY ECMA_ERROR_DEF (ECMA_ERR_CANNOT_CONVERT_TO_OBJECT, "Cannot convert undefined or null to object") #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_NUMBER ECMA_ERROR_DEF (ECMA_ERR_PRECISION_DIGITS_MUST_BE_BETWEEN_IN_RANGE, "Precision digits must be between 1 and 100") #endif /* JERRY_BUILTIN_NUMBER */ ECMA_ERROR_DEF (ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_GETTER, "Private field was defined without a getter") ECMA_ERROR_DEF (ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_SETTER, "Private field was defined without a setter") ECMA_ERROR_DEF (ECMA_ERR_PROPERTY_NAME_IS_NEITHER_SYMBOL_NOR_STRING, "Property name is neither Symbol nor string") #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_STRING_CANNOT_BE_CONVERTED_TO_BIGINT_VALUE, "String cannot be converted to BigInt value") #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_CONTAINER ECMA_ERROR_DEF (ECMA_ERR_INCORRECT_TYPE_CALL, "Operator called on incorrect container type") #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_ARRAY ECMA_ERROR_DEF (ECMA_ERR_REDUCE_OF_EMPTY_ARRAY_WITH_NO_INITIAL_VALUE, "Reduce of empty Array with no initial value") #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_REG_EXP_OBJECT, "Argument 'this' is not a valid RegExp object") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_ARRAY_BUFFER_OBJECT, "Argument 'this' is not an ArrayBuffer object") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_ERROR_MESSAGES ECMA_ERROR_DEF (ECMA_ERR_ARROW_FUNCTIONS_INVOKE_WITH_NEW, "Arrow functions cannot be invoked with 'new'") ECMA_ERROR_DEF (ECMA_ERR_ASYNC_FUNCTIONS_INVOKE_WITH_NEW, "Async functions cannot be invoked with 'new'") #endif /* JERRY_ERROR_MESSAGES */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_SET_EXTENSIBLE_PROPERTY, "Cannot set [[Extensible]] property of object") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_SHAREDARRAYBUFFER_REQUIRES_NEW, "Constructor SharedArrayBuffer requires 'new'") #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_UINT8_CLAMPED_ARRAY_REQUIRES_NEW, "Constructor Uint8ClampedArray requires 'new'") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_NEGATIVE_EXPONENT_IS_NOT_ALLOWED_FOR_BIGINTS, "Negative exponent is not allowed for BigInts") #endif /* JERRY_BUILTIN_BIGINT */ ECMA_ERROR_DEF (ECMA_ERR_PROMISE_ALL_REMAINING_ELEMENTS_LIMIT_REACHED, "Promise.all remaining elements limit reached") ECMA_ERROR_DEF (ECMA_ERR_VALUE_RECEIVED_BY_FOR_ASYNC_OF_IS_NOT_OBJECT, "Value received by for-async-of is not object") #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_INFINITY_OR_NAN_CANNOT_BE_CONVERTED_TO_BIGINT, "Infinity or NaN cannot be converted to BigInt") #endif /* JERRY_BUILTIN_BIGINT */ ECMA_ERROR_DEF (ECMA_ERR_OPERATOR_DELETE_RETURNED_FALSE_IN_STRICT_MODE, "Operator delete returned false in strict mode") ECMA_ERROR_DEF (ECMA_ERR_PROPERTY_PROTOTYPE_IS_NOT_AN_OBJECT, "Property 'prototype' is not an object or null") #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_PROXY_HANDLER_IS_NULL_FOR_ISARRAY_OPERATION, "Proxy handler is null for 'isArray' operation") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_SNAPSHOT_SAVE ECMA_ERROR_DEF (ECMA_ERR_REGULAR_EXPRESSION_NOT_SUPPORTED, "Regular expression literals are not supported") #endif /* JERRY_SNAPSHOT_SAVE */ ECMA_ERROR_DEF (ECMA_ERR_RIGHT_VALUE_OF_INSTANCEOF_MUST_BE_AN_OBJECT, "Right value of 'instanceof' must be an object") #if JERRY_SNAPSHOT_EXEC ECMA_ERROR_DEF (ECMA_ERR_STATIC_SNAPSHOTS_CANNOT_BE_COPIED_INTO_MEMORY, "Static snapshots cannot be copied into memory") #endif /* JERRY_SNAPSHOT_EXEC */ #if JERRY_SNAPSHOT_SAVE ECMA_ERROR_DEF (ECMA_ERR_TAGGED_TEMPLATE_LITERALS, "Unsupported feature: tagged template literals") ECMA_ERROR_DEF (ECMA_ERR_SNAPSHOT_FLAG_NOT_SUPPORTED, "Unsupported generate snapshot flags specified") #endif /* JERRY_SNAPSHOT_SAVE */ #if JERRY_SNAPSHOT_EXEC ECMA_ERROR_DEF (ECMA_ERR_UNSUPPORTED_SNAPSHOT_EXEC_FLAGS_ARE_SPECIFIED, "Unsupported snapshot exec flags are specified") #endif /* JERRY_SNAPSHOT_EXEC */ ECMA_ERROR_DEF (ECMA_ERR_VALUE_FOR_CLASS_HERITAGE_IS_NOT_A_CONSTRUCTOR, "Value for class heritage is not a constructor") ECMA_ERROR_DEF (ECMA_ERR_TOO_MANY_ARGUMENTS_DECLARED_FOR_FUNCTION_APPLY, "Too many arguments declared for Function.apply") #if JERRY_ERROR_MESSAGES ECMA_ERROR_DEF (ECMA_ERR_ACCESSOR_FUNCTIONS_INVOKE_WITH_NEW, "Accessor functions cannot be invoked with 'new'") #endif /* JERRY_ERROR_MESSAGES */ #if JERRY_MODULE_SYSTEM ECMA_ERROR_DEF (ECMA_ERR_LINK_TO_MODULE_IN_ERROR_STATE, "Cannot link to a module which is in error state") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_ONLY_INTEGER_NUMBERS_CAN_BE_CONVERTED_TO_BIGINT, "Only integer numbers can be converted to BigInt") #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_TYPEDARRAY_INTRINSIC_DIRECTLY_CALLED, "TypedArray intrinsic cannot be directly called") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_UNSIGNED_RIGHT_SHIFT_IS_NOT_ALLOWED_FOR_BIGINTS, "Unsigned right shift is not allowed for BigInts") #endif /* JERRY_BUILTIN_BIGINT */ ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_ASYNC_GENERATOR, "Argument 'this' is not an async generator object") ECMA_ERROR_DEF (ECMA_ERR_CLASS_EXTENDS_NOT_CONSTRUCTOR, "Class extends value is not a constructor or null") #if JERRY_ERROR_MESSAGES ECMA_ERROR_DEF (ECMA_ERR_GENERATOR_FUNCTIONS_INVOKE_WITH_NEW, "Generator functions cannot be invoked with 'new'") #endif /* JERRY_ERROR_MESSAGES */ #if JERRY_BUILTIN_REGEXP ECMA_ERROR_DEF (ECMA_ERR_RETURN_VALUE_OF_EXEC_MUST_BE_AN_OBJECT_OR_NULL, "Return value of 'exec' must be an object or null") #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_DATAVIEW ECMA_ERROR_DEF (ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER, "Start offset is outside the bounds of the buffer") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_SHARED_ARRAY_BUFFER, "Argument 'this' is not a SharedArrayBuffer object") #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ ECMA_ERROR_DEF (ECMA_ERR_CLASS_CONSTRUCTOR_NEW, "Class constructor cannot be invoked without 'new'") #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_INCORRECT_RETURN_PROXY_GET_TRAP, "Incorrect value is returned by a Proxy 'get' trap") ECMA_ERROR_DEF (ECMA_ERR_INCORRECT_RETURN_PROXY_SET_TRAP, "Incorrect value is returned by a Proxy 'set' trap") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_CLASS_IS_NON_CONFIGURABLE, "Prototype property of a class is non-configurable") #if JERRY_BUILTIN_ARRAY ECMA_ERROR_DEF (ECMA_ERR_PUSHING_TOO_HIGH_ELEMENT, "Pushing element over 2**53-1 length is disallowed") #endif /* JERRY_BUILTIN_ARRAY */ ECMA_ERROR_DEF (ECMA_ERR_THE_REQUESTED_PROPERTY_UPDATE_CANNOT_BE_PERFORMED, "The requested property update cannot be performed") #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_RESULT_NOT_INCLUDE_ALL_CONFIGURABLE_KEYS, "Trap result did not include all configurable keys") ECMA_ERROR_DEF (ECMA_ERR_TRAP_TRUISH_TARGET_NOT_EXTENSIBLE, "Trap returned truish for target is not extensible") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_NUMBER ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_NUMBER, "Argument 'this' is not a number or a Number object") #endif /* JERRY_BUILTIN_NUMBER */ #if JERRY_BUILTIN_STRING ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_STRING_OBJECT, "Argument 'this' is not a string or a String object") #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_SHARED_ARRAY_BUFFER_OBJECT, "Argument 'this' is not an SharedArrayBuffer object") #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ #if JERRY_ERROR_MESSAGES ECMA_ERROR_DEF (ECMA_ERR_ASYNC_ARROW_FUNCTIONS_INVOKE_WITH_NEW, "Async arrow functions cannot be invoked with 'new'") #endif /* JERRY_ERROR_MESSAGES */ #if JERRY_BUILTIN_ARRAY ECMA_ERROR_DEF (ECMA_ERR_UNSHIFT_TOO_HIGH, "Unshift elements over 2**53-1 length is disallowed") #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_RESULT_NOT_INCLUDE_ALL_NON_CONFIGURABLE_KEYS, "Trap result did not include all non-configurable keys") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_ERROR_MESSAGES ECMA_ERROR_DEF (ECMA_ERR_ASYNC_GENERATOR_FUNCTIONS_INVOKE_WITH_NEW, "Async generator functions cannot be invoked with 'new'") #endif /* JERRY_ERROR_MESSAGES */ #if JERRY_BUILTIN_REFLECT ECMA_ERROR_DEF (ECMA_ERR_REFLECT_EXPECTS_AN_OBJECT_AS_SECOND_ARGUMENT, "Reflect.construct expects an object as second argument") #endif /* JERRY_BUILTIN_REFLECT */ #if JERRY_ERROR_MESSAGES ECMA_ERROR_DEF (ECMA_ERR_SCRIPT_GLOBAL_FUNCTIONS_INVOKE_WITH_NEW, "Script (global) functions cannot be invoked with 'new'") #endif /* JERRY_ERROR_MESSAGES */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_CANNOT_CREATE_PROXY, "Cannot create Proxy with a non-object target or handler") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_DERIVED_CTOR_RETURN_NOR_OBJECT_OR_UNDEFINED, "Derived constructors may only return object or undefined") #if JERRY_SNAPSHOT_EXEC ECMA_ERROR_DEF (ECMA_ERR_INVALID_SNAPSHOT_VERSION_OR_FEATURES, "Invalid snapshot version or unsupported features present") #endif /* JERRY_SNAPSHOT_EXEC */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_TYPEDARRAY_SMALLER_THAN_FILTER_CALL_RESULT, "Constructed TypedArray is smaller than filter call result") ECMA_ERROR_DEF (ECMA_ERR_DERIVED_ARRAY_BUFFER_CTOR_BUFFER_TOO_SMALL, "Derived ArrayBuffer constructor created a too small buffer") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_RESULT_NOT_REFLECT_TARGET_EXTENSIBILITY, "Trap result does not reflect extensibility of Proxy target") ECMA_ERROR_DEF (ECMA_ERR_TRAP_EXTRA_KEYS_FOR_A_NON_EXTENSIBLE_TARGET, "Trap returned extra keys for a non-extensible Proxy target") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_ARRAY_BUFFER_RETURNED_THIS_FROM_CONSTRUCTOR, "ArrayBuffer subclass returned this from species constructor") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_DATAVIEW ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_BUFFER_NOT_ARRAY_OR_SHARED_BUFFER, "Argument 'buffer' is not an ArrayBuffer or SharedArrayBuffer") #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_RESULT_NOT_REFLECT_TARGET_INEXTENSIBILITY, "Trap result does not reflect inextensibility of Proxy target") #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_TYPEDARRAY_INTRINSIC_CALLED_BY_NEW_EXPRESSION, "TypedArray intrinsic cannot be called by a 'new' expression") #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONTENTTYPE_RETURNED_TYPEDARRAY_NOT_MATCH_SOURCE, "TypedArray returned by [[ContentType]] does not match source") #endif /* JERRY_BUILTIN_BIGINT && JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_BIGINT ECMA_ERROR_DEF (ECMA_ERR_ALLOCATE_BIGINT_STRING, "Cannot allocate memory for a string representation of a BigInt value") #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TARGET_NOT_EXTENSIBLE_DIFFERENT_PROTOTYPE_RETURNED, "Target object is non-extensible and trap returned different prototype") ECMA_ERROR_DEF (ECMA_ERR_TRAP_TRUISH_ADDING_PROPERTY_NON_EXTENSIBLE_TARGET, "Trap returned truish for adding property to the non-extensible target") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_CANNOT_READ_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT, "Cannot read private member to an object whose class did not declare it") #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_GIVEN_PROPERTY_IS_A_NON_CONFIGURABLE, "Given property is a non-configurable data property on the proxy target") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_CANNOT_WRITE_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT, "Cannot write private member to an object whose class did not declare it") #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_FALSISH_PROPERTY_TARGET_NOT_EXTENSIBLE, "Trap returned falsish for property but the proxy target is not extensible") ECMA_ERROR_DEF (ECMA_ERR_PROXY_PROPERTY_NOT_CONFIGURABLE_NOT_HAVE_GETTER, "Property of a Proxy is non-configurable and does not have a getter function") ECMA_ERROR_DEF (ECMA_ERR_TARGET_PROPERTY_CONFIGURE_ACCESSOR_WITHOUT_SETTER, "The property of a Proxy target is a non configurable accessor without a setter") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_LET_CONST_NOT_INITIALIZED, "Variables declared by let/const must be initialized before reading their value") #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_TRUISH_PROPERTY_NON_CONFIGURABLE, "Trap returned truish for property which is non-configurable in the proxy target") ECMA_ERROR_DEF (ECMA_ERR_TARGET_NOT_EXTENSIBLE_NOT_RETURNED_ITS_PROTOTYPE, "Proxy target is non-extensible, but the trap did not return its actual prototype") ECMA_ERROR_DEF (ECMA_ERR_TRAP_FALSISH_PROPERTY_NON_CONFIGURABLE, "Trap returned falsish for property which exists in the proxy target as non-configurable") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_CALL_SUPER_CONSTRUCTOR_DERIVED_CLASS_BEFORE_THIS, "Must call super constructor in derived class before accessing 'this' or returning from it") #if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_TRUISH_DEFINING_NON_EXISTENT_PROPERTY, "Trap returned truish for defining non-configurable property which is nonexistent in the target") ECMA_ERROR_DEF ( ECMA_ERR_TRAP_TRUISH_ADD_PROPERTY_INCOMPATIBLE_OTHER_PROP, "Trap returned truish for adding property that is incompatible with the existing property in the target") #endif /* JERRY_BUILTIN_PROXY */ ECMA_ERROR_DEF (ECMA_ERR_CANNOT_ACCESS_CALLER_CALLEE_ARGUMENTS, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the " "arguments objects for calls to them") mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgPaint.cpp000664 001750 001750 00000033102 15164251010 034111 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgPaint.h" #include "tvgShape.h" #include "tvgPicture.h" #include "tvgScene.h" #include "tvgText.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #define PAINT_METHOD(ret, METHOD) \ switch (paint->type()) { \ case Type::Shape: ret = to(paint)->METHOD; break; \ case Type::Scene: ret = to(paint)->METHOD; break; \ case Type::Picture: ret = to(paint)->METHOD; break; \ case Type::Text: ret = to(paint)->METHOD; break; \ default: ret = {}; \ } static bool _clipRect(RenderMethod* renderer, const Point* pts, const Matrix& m, RenderRegion& before) { Point c[4]; //corners for (int i = 0; i < 4; ++i) { c[i] = pts[i] * m; } //figure out if the clipper is a superset of the current viewport(before) region auto pointInConvexQuad = [](const Point& p, const Point* quad) { auto sign = [](const Point& p1, const Point& p2, const Point& p3) { return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y); }; auto b1 = sign(p, quad[0], quad[1]) < 0.0f; auto b2 = sign(p, quad[1], quad[2]) < 0.0f; auto b3 = sign(p, quad[2], quad[3]) < 0.0f; auto b4 = sign(p, quad[3], quad[0]) < 0.0f; return ((b1 == b2) && (b2 == b3) && (b3 == b4)); }; if (!pointInConvexQuad({float(before.min.x), float(before.min.y)}, c)) return false; if (!pointInConvexQuad({float(before.max.x), float(before.min.y)}, c)) return false; if (!pointInConvexQuad({float(before.max.x), float(before.max.y)}, c)) return false; if (!pointInConvexQuad({float(before.min.x), float(before.max.y)}, c)) return false; //same viewport return true; } static bool _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Matrix& pm, RenderRegion& before) { /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */ auto shape = static_cast(cmpTarget); //Trimming likely makes the shape non-rectangular if (to(shape)->rs.trimpath()) return false; //Rectangle Candidates? const Point* pts; uint32_t ptsCnt; shape->path(nullptr, nullptr, &pts, &ptsCnt); //No rectangle format if (ptsCnt != 4) return false; //No rotation and no skewing, still can try out clipping the rect region. auto tm = pm * cmpTarget->transform(); //Perpendicular Rectangle? if (rightAngle(tm) && !skewed(tm)) { auto pt1 = pts + 0; auto pt2 = pts + 1; auto pt3 = pts + 2; auto pt4 = pts + 3; if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) || (tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) { RenderRegion after; auto v1 = *pt1; auto v2 = *pt3; v1 *= tm; v2 *= tm; //sorting if (v1.x > v2.x) std::swap(v1.x, v2.x); if (v1.y > v2.y) std::swap(v1.y, v2.y); after.min.x = static_cast(nearbyint(v1.x)); after.min.y = static_cast(nearbyint(v1.y)); after.max.x = static_cast(nearbyint(v2.x)); after.max.y = static_cast(nearbyint(v2.y)); if (after.max.x < after.min.x) after.max.x = after.min.x; if (after.max.y < after.min.y) after.max.y = after.min.y; after.intersect(before); renderer->viewport(after); return true; } } return _clipRect(renderer, pts, tm, before); } RenderRegion Paint::Impl::bounds() { RenderRegion ret; PAINT_METHOD(ret, bounds()); return ret; } Iterator* Paint::Impl::iterator() { Iterator* ret; PAINT_METHOD(ret, iterator()); return ret; } Paint* Paint::Impl::duplicate(Paint* ret) { if (ret) ret->mask(nullptr, MaskMethod::None); PAINT_METHOD(ret, duplicate(ret)); if (maskData) ret->mask(maskData->target->duplicate(), maskData->method); if (clipper) ret->clip(static_cast(clipper->duplicate())); ret->pImpl->tr = tr; ret->pImpl->blendMethod = blendMethod; ret->pImpl->opacity = opacity; ret->pImpl->hidden = hidden; ret->pImpl->mark(RenderUpdateFlag::All); return ret; } bool Paint::Impl::render(RenderMethod* renderer) { if (hidden || opacity == 0) return true; RenderCompositor* cmp = nullptr; //OPTIMIZE: bounds(renderer) calls could dismiss the parallelization if (maskData && !(maskData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { RenderRegion region; PAINT_METHOD(region, bounds()); auto mData = maskData; while (mData) { if (MASK_REGION_MERGING(mData->method)) region.add(PAINT(mData->target)->bounds()); if (region.invalid()) return true; mData = PAINT(mData->target)->maskData; } cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking); if (renderer->beginComposite(cmp, MaskMethod::None, 255)) { maskData->target->pImpl->render(renderer); } } if (cmp) renderer->beginComposite(cmp, maskData->method, maskData->target->pImpl->opacity); bool ret; PAINT_METHOD(ret, render(renderer)); if (cmp) renderer->endComposite(cmp); return ret; } RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) { bool ret; PAINT_METHOD(ret, skip((flag | renderFlag))); if (ret) return rd; cmpFlag = CompositionFlag::Invalid; //must clear after the rendering if (this->renderer != renderer) { if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!"); renderer->ref(); this->renderer = renderer; } if (renderFlag & RenderUpdateFlag::Transform) tr.update(); /* 1. Composition Pre Processing */ RenderData trd = nullptr; //composite target render data RenderRegion viewport; auto compFastTrack = false; if (maskData) { auto target = maskData->target; auto method = maskData->method; PAINT(target)->ctxFlag &= ~ContextFlag::FastTrack; //reset /* If the transformation has no rotational factors and the Alpha(InvAlpha) Masking involves a simple rectangle, we can optimize by using the viewport instead of the regular Alphaing sequence for improved performance. */ if (target->type() == Type::Shape) { auto shape = static_cast(target); uint8_t a; shape->fill(nullptr, nullptr, nullptr, &a); //no gradient fill & no maskings of the masking target. if (!shape->fill() && !(PAINT(shape)->maskData)) { if ((method == MaskMethod::Alpha && a == 255 && PAINT(shape)->opacity == 255) || (method == MaskMethod::InvAlpha && (a == 0 || PAINT(shape)->opacity == 0))) { viewport = renderer->viewport(); if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport))) { PAINT(target)->ctxFlag |= ContextFlag::FastTrack; } } } } if (!compFastTrack) { trd = PAINT(target)->update(renderer, pm, clips, 255, flag, false); } } /* 2. Clipping */ if (this->clipper) { auto pclip = PAINT(this->clipper); pclip->ctxFlag &= ~ContextFlag::FastTrack; //reset viewport = renderer->viewport(); if (!pclip->clipper && to(this->clipper)->rs.strokeWidth() == 0.0f && _compFastTrack(renderer, this->clipper, pm, viewport)) { pclip->ctxFlag |= ContextFlag::FastTrack; compFastTrack = true; } else { mark(RenderUpdateFlag::Clip); trd = pclip->update(renderer, pm, clips, 255, flag, true); clips.push(trd); } } /* 3. Main Update */ opacity = MULTIPLY(opacity, this->opacity); PAINT_METHOD(ret, update(renderer, pm * tr.m, clips, opacity, (flag | renderFlag), clipper)); /* 4. Composition Post Processing */ if (compFastTrack) renderer->viewport(viewport); else if (this->clipper) clips.pop(); renderFlag = RenderUpdateFlag::None; return rd; } bool Paint::Impl::bounds(Point* pt4, const Matrix* pm, bool obb) { bool ret; PAINT_METHOD(ret, bounds(pt4, pm * transform(), obb)); return ret; } bool Paint::Impl::intersects(const RenderRegion& region) { if (renderer) { bool ret; PAINT_METHOD(ret, intersects(region)); return ret; } return false; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ Paint :: Paint() = default; Paint :: ~Paint() = default; void Paint::rel(Paint* paint) noexcept { if (paint && paint->refCnt() <= 0) delete(paint); } Result Paint::rotate(float degree) noexcept { if (pImpl->rotate(degree)) return Result::Success; return Result::InsufficientCondition; } Result Paint::scale(float factor) noexcept { if (pImpl->scale(factor)) return Result::Success; return Result::InsufficientCondition; } Result Paint::translate(float x, float y) noexcept { if (pImpl->translate(x, y)) return Result::Success; return Result::InsufficientCondition; } Result Paint::transform(const Matrix& m) noexcept { if (pImpl->transform(m)) return Result::Success; return Result::InsufficientCondition; } Matrix& Paint::transform() noexcept { return pImpl->transform(); } Result Paint::bounds(float* x, float* y, float* w, float* h) noexcept { Point pt4[4] = {}; const auto pm = pImpl->ptransform(); if (pImpl->bounds(pt4, &pm, false)) { BBox box = {{FLT_MAX, FLT_MAX}, {-FLT_MAX, -FLT_MAX}}; for (int i = 0; i < 4; ++i) { if (pt4[i].x < box.min.x) box.min.x = pt4[i].x; if (pt4[i].x > box.max.x) box.max.x = pt4[i].x; if (pt4[i].y < box.min.y) box.min.y = pt4[i].y; if (pt4[i].y > box.max.y) box.max.y = pt4[i].y; } if (x) *x = box.min.x; if (y) *y = box.min.y; if (w) *w = box.max.x - box.min.x; if (h) *h = box.max.y - box.min.y; return Result::Success; } return Result::InsufficientCondition; } Result Paint::bounds(Point* pt4) noexcept { if (!pt4) return Result::InvalidArguments; auto pm = pImpl->ptransform(); if (pImpl->bounds(pt4, &pm, true)) return Result::Success; return Result::InsufficientCondition; } bool Paint::intersects(int32_t x, int32_t y, int32_t w, int32_t h) noexcept { if (w <= 0 || h <= 0) return false; return pImpl->intersects({{x, y}, {x + w, y + h}}); } Paint* Paint::duplicate() const noexcept { return pImpl->duplicate(); } Result Paint::clip(Shape* clipper) noexcept { return pImpl->clip(clipper); } Shape* Paint::clip() const noexcept { return pImpl->clipper; } Result Paint::mask(Paint* target, MaskMethod method) noexcept { if (method > MaskMethod::Darken) return Result::InvalidArguments; return pImpl->mask(target, method); } MaskMethod Paint::mask(const Paint** target) const noexcept { return pImpl->mask(target); } Result Paint::opacity(uint8_t o) noexcept { if (pImpl->opacity != o) { pImpl->opacity = o; pImpl->mark(RenderUpdateFlag::Color); } return Result::Success; } uint8_t Paint::opacity() const noexcept { return pImpl->opacity; } Result Paint::blend(BlendMethod method) noexcept { if (method <= BlendMethod::Add || method == BlendMethod::Composition) { pImpl->blend(method); return Result::Success; } return Result::InvalidArguments; } uint16_t Paint::ref() noexcept { return pImpl->ref(); } uint16_t Paint::unref(bool free) noexcept { return pImpl->unrefx(free); } uint16_t Paint::refCnt() const noexcept { return pImpl->refCnt; } const Paint* Paint::parent() const noexcept { return pImpl->parent; } Result Paint::visible(bool on) noexcept { return pImpl->visible(!on); } bool Paint::visible() const noexcept { return !pImpl->hidden; }mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgRender.h000664 001750 001750 00000050641 15164251010 033731 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_RENDER_H_ #define _TVG_RENDER_H_ #include #include #include "tvgCommon.h" #include "tvgArray.h" #include "tvgLock.h" #include "tvgColor.h" #include "tvgMath.h" namespace tvg { using RenderData = void*; using RenderColor = tvg::RGBA; using pixel_t = uint32_t; #define DASH_PATTERN_THRESHOLD 0.001f //TODO: Separate Color & Opacity for more detailed conditional check enum RenderUpdateFlag : uint16_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, Clip = 256, All = 0xffff}; enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8}; //Composition Purpose static inline void operator|=(RenderUpdateFlag& a, const RenderUpdateFlag b) { a = RenderUpdateFlag(uint16_t(a) | uint16_t(b)); } static inline RenderUpdateFlag operator|(const RenderUpdateFlag a, const RenderUpdateFlag b) { return RenderUpdateFlag(uint16_t(a) | uint16_t(b)); } struct RenderSurface { union { pixel_t* data = nullptr; //system based data pointer uint32_t* buf32; //for explicit 32bits channels uint8_t* buf8; //for explicit 8bits grayscale }; Key key; //a reserved lock for the thread safety uint32_t stride = 0; uint32_t w = 0, h = 0; ColorSpace cs = ColorSpace::Unknown; uint8_t channelSize = 0; bool premultiplied = false; //Alpha-premultiplied RenderSurface() { } RenderSurface(const RenderSurface* rhs) { data = rhs->data; stride = rhs->stride; w = rhs->w; h = rhs->h; cs = rhs->cs; channelSize = rhs->channelSize; premultiplied = rhs->premultiplied; } }; struct RenderCompositor { MaskMethod method; uint8_t opacity; }; struct RenderRegion { struct { int32_t x, y; } min; struct { int32_t x, y; } max; static constexpr RenderRegion intersect(const RenderRegion& lhs, const RenderRegion& rhs) { RenderRegion ret = {{std::max(lhs.min.x, rhs.min.x), std::max(lhs.min.y, rhs.min.y)}, {std::min(lhs.max.x, rhs.max.x), std::min(lhs.max.y, rhs.max.y)}}; // Not intersected: collapse to zero-area region if (ret.min.x > ret.max.x) ret.max.x = ret.min.x; if (ret.min.y > ret.max.y) ret.max.y = ret.min.y; return ret; } static constexpr RenderRegion add(const RenderRegion& lhs, const RenderRegion& rhs) { return {{std::min(lhs.min.x, rhs.min.x), std::min(lhs.min.y, rhs.min.y)}, {std::max(lhs.max.x, rhs.max.x), std::max(lhs.max.y, rhs.max.y)}}; } void intersect(const RenderRegion& rhs); void add(const RenderRegion& rhs) { if (rhs.min.x < min.x) min.x = rhs.min.x; if (rhs.min.y < min.y) min.y = rhs.min.y; if (rhs.max.x > max.x) max.x = rhs.max.x; if (rhs.max.y > max.y) max.y = rhs.max.y; } bool contained(const RenderRegion& rhs) const { return (min.x <= rhs.min.x && max.x >= rhs.max.x && min.y <= rhs.min.y && max.y >= rhs.max.y); } bool intersected(const RenderRegion& rhs) const { return (rhs.min.x < max.x && rhs.max.x > min.x && rhs.min.y < max.y && rhs.max.y > min.y); } bool operator==(const RenderRegion& rhs) const { return (min.x == rhs.min.x && min.y == rhs.min.y && max.x == rhs.max.x && max.y == rhs.max.y); } void reset() { min.x = min.y = max.x = max.y = 0; } bool valid() const { return (max.x > min.x && max.y > min.y); } bool invalid() const { return !valid(); } int32_t sx() const { return min.x; } int32_t sy() const { return min.y; } int32_t sw() const { return max.x - min.x; } int32_t sh() const { return max.y - min.y; } uint32_t x() const { return (uint32_t) sx(); } uint32_t y() const { return (uint32_t) sy(); } uint32_t w() const { return (uint32_t) sw(); } uint32_t h() const { return (uint32_t) sh(); } }; #ifdef THORVG_PARTIAL_RENDER_SUPPORT struct RenderDirtyRegion { public: static constexpr const int PARTITIONING = 16; //must be N*N bool support = true; void init(uint32_t w, uint32_t h); void commit(); bool add(const RenderRegion& bbox); bool add(const RenderRegion& prv, const RenderRegion& cur); //collect the old and new dirty regions together void clear(); bool deactivate(bool on) { std::swap(on, disabled); return on; } bool deactivated() { return (!support || disabled); } const RenderRegion& partition(int idx) { return partitions[idx].region; } const Array& get(int idx) { return partitions[idx].list[partitions[idx].current]; } private: void subdivide(Array& targets, uint32_t idx, RenderRegion& lhs, RenderRegion& rhs); struct Partition { RenderRegion region; Array list[2]; //double buffer swapping uint8_t current = 0; //double buffer swapping list index. 0 or 1 }; Key key; Partition partitions[PARTITIONING]; bool disabled = false; }; #else struct RenderDirtyRegion { static constexpr const int PARTITIONING = 16; //must be N*N bool support = true; void init(uint32_t w, uint32_t h) {} void commit() {} bool add(TVG_UNUSED const RenderRegion& bbox) { return true; } bool add(TVG_UNUSED const RenderRegion& prv, TVG_UNUSED const RenderRegion& cur) { return true; } void clear() {} bool deactivate(TVG_UNUSED bool on) { return true; } bool deactivated() { return true; } const RenderRegion& partition(TVG_UNUSED int idx) { static RenderRegion tmp{}; return tmp; } const Array& get(TVG_UNUSED int idx) { static Array tmp; return tmp; } }; #endif struct RenderPath { Array cmds; Array pts; bool empty() const { return pts.empty(); } void clear() { pts.clear(); cmds.clear(); } void close() { //Don't close multiple times. if (cmds.count > 0 && cmds.last() == PathCommand::Close) return; cmds.push(PathCommand::Close); } void moveTo(const Point& pt) { pts.push(pt); cmds.push(PathCommand::MoveTo); } void lineTo(const Point& pt) { pts.push(pt); cmds.push(PathCommand::LineTo); } void cubicTo(const Point& cnt1, const Point& cnt2, const Point& end) { pts.push(cnt1); pts.push(cnt2); pts.push(end); cmds.push(PathCommand::CubicTo); } Point point(float progress) { if (progress <= 0.0f) return pts.first(); else if (progress >= 1.0f) return pts.last(); auto pleng = tvg::length(cmds.data, cmds.count, pts.data, pts.count) * progress; auto cleng = 0.0f; auto p = pts.data; auto c = cmds.data; Point curr{}, start{}, next{}; while (c < cmds.data + cmds.count) { switch (*c) { case PathCommand::MoveTo: { curr = start = *p++; break; } case PathCommand::LineTo: { next = *p; auto segLen = tvg::length(curr, next); if (cleng + segLen >= pleng) return lerp(curr, next, (pleng - cleng) / segLen); cleng += segLen; curr = *p++; break; } case PathCommand::CubicTo: { Bezier bz = {curr, *p, *(p + 1), *(p + 2)}; auto segLen = bz.length(); if (cleng + segLen >= pleng) return bz.at((pleng - cleng) / segLen); cleng += segLen; curr = *(p + 2); p += 3; break; } case PathCommand::Close: { auto segLen = tvg::length(curr, start); if (cleng + segLen >= pleng) return lerp(curr, start, (pleng - cleng) / segLen); cleng += segLen; curr = start; break; } } ++c; } return curr; } /* Optimize path in screen space with merging collinear lines, collapsing zero length lines, and removing unnecessary cubic beziers. */ void optimizeWG(RenderPath& out, const Matrix& matrix) const; void optimizeGL(RenderPath& out, const Matrix& matrix) const; bool bounds(const Matrix* m, BBox& box); }; struct RenderTrimPath { float begin = 0.0f; float end = 1.0f; bool simultaneous = true; bool valid() { if (begin != 0.0f || end != 1.0f) return true; return false; } bool trim(const RenderPath& in, RenderPath& out) const; }; struct RenderStroke { float width = 0.0f; RenderColor color{}; Fill *fill = nullptr; struct Dash { float* pattern = nullptr; uint32_t count = 0; float offset = 0.0f; float length = 0.0f; } dash; float miterlimit = 4.0f; RenderTrimPath trim; StrokeCap cap = StrokeCap::Square; StrokeJoin join = StrokeJoin::Bevel; bool first = false; void operator=(const RenderStroke& rhs) { width = rhs.width; color = rhs.color; delete(fill); if (rhs.fill) fill = rhs.fill->duplicate(); else fill = nullptr; tvg::free(dash.pattern); dash = rhs.dash; if (rhs.dash.count > 0) { dash.pattern = tvg::malloc(sizeof(float) * rhs.dash.count); memcpy(dash.pattern, rhs.dash.pattern, sizeof(float) * rhs.dash.count); } miterlimit = rhs.miterlimit; trim = rhs.trim; cap = rhs.cap; join = rhs.join; first = rhs.first; } ~RenderStroke() { tvg::free(dash.pattern); delete(fill); } }; struct RenderShape { RenderPath path; Fill *fill = nullptr; RenderColor color{}; RenderStroke *stroke = nullptr; FillRule rule = FillRule::NonZero; ~RenderShape() { delete(fill); delete(stroke); } void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const { if (r) *r = color.r; if (g) *g = color.g; if (b) *b = color.b; if (a) *a = color.a; } bool trimpath() const { return stroke ? stroke->trim.valid() : false; } bool strokeFirst() const { return (stroke && stroke->first) ? true : false; } float strokeWidth() const { return stroke ? stroke->width : 0.0f; } bool strokeFill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const { if (!stroke) return false; if (r) *r = stroke->color.r; if (g) *g = stroke->color.g; if (b) *b = stroke->color.b; if (a) *a = stroke->color.a; return true; } const Fill* strokeFill() const { return stroke ? stroke->fill : nullptr; } uint32_t strokeDash(const float** dashPattern, float* offset) const { if (!stroke) return 0; if (dashPattern) *dashPattern = stroke->dash.pattern; if (offset) *offset = stroke->dash.offset; return stroke->dash.count; } StrokeCap strokeCap() const { return stroke ? stroke->cap : StrokeCap::Square; } StrokeJoin strokeJoin() const { return stroke ? stroke->join : StrokeJoin::Bevel; } float strokeMiterlimit() const { return stroke ? stroke->miterlimit : 4.0f; } bool strokeDash(RenderPath& out, const Matrix* transform = nullptr) const; }; struct RenderEffect { RenderData rd = nullptr; RenderRegion extend{}; SceneEffect type; bool valid = false; virtual ~RenderEffect() {} }; struct RenderEffectGaussianBlur : RenderEffect { float sigma; uint8_t direction; //0: both, 1: horizontal, 2: vertical uint8_t border; //0: duplicate, 1: wrap uint8_t quality; //0 ~ 100 (optional) static RenderEffectGaussianBlur* gen(va_list& args) { auto inst = new RenderEffectGaussianBlur; inst->sigma = std::max((float) va_arg(args, double), 0.0f); inst->direction = tvg::clamp(va_arg(args, int), 0, 2); inst->border = std::min(va_arg(args, int), 1); inst->quality = std::min(va_arg(args, int), 100); inst->type = SceneEffect::GaussianBlur; return inst; } }; struct RenderEffectDropShadow : RenderEffect { uint8_t color[4]; //rgba float angle; float distance; float sigma; uint8_t quality; //0 ~ 100 (optional) static RenderEffectDropShadow* gen(va_list& args) { auto inst = new RenderEffectDropShadow; inst->color[0] = va_arg(args, int); inst->color[1] = va_arg(args, int); inst->color[2] = va_arg(args, int); inst->color[3] = va_arg(args, int); inst->angle = (float) va_arg(args, double); inst->distance = (float) va_arg(args, double); inst->sigma = std::max((float) va_arg(args, double), 0.0f); inst->quality = std::min(va_arg(args, int), 100); inst->type = SceneEffect::DropShadow; return inst; } }; struct RenderEffectFill : RenderEffect { uint8_t color[4]; //rgba static RenderEffectFill* gen(va_list& args) { auto inst = new RenderEffectFill; inst->color[0] = va_arg(args, int); inst->color[1] = va_arg(args, int); inst->color[2] = va_arg(args, int); inst->color[3] = va_arg(args, int); inst->type = SceneEffect::Fill; return inst; } }; struct RenderEffectTint : RenderEffect { uint8_t black[3]; //rgb uint8_t white[3]; //rgb uint8_t intensity; //0 - 255 static RenderEffectTint* gen(va_list& args) { auto inst = new RenderEffectTint; inst->black[0] = va_arg(args, int); inst->black[1] = va_arg(args, int); inst->black[2] = va_arg(args, int); inst->white[0] = va_arg(args, int); inst->white[1] = va_arg(args, int); inst->white[2] = va_arg(args, int); inst->intensity = (uint8_t)(static_cast(va_arg(args, double)) * 2.55f); inst->type = SceneEffect::Tint; return inst; } }; struct RenderEffectTritone : RenderEffect { uint8_t shadow[3]; //rgb uint8_t midtone[3]; //rgb uint8_t highlight[3]; //rgb uint8_t blender = 0; //0 ~ 255 static RenderEffectTritone* gen(va_list& args) { auto inst = new RenderEffectTritone; inst->shadow[0] = va_arg(args, int); inst->shadow[1] = va_arg(args, int); inst->shadow[2] = va_arg(args, int); inst->midtone[0] = va_arg(args, int); inst->midtone[1] = va_arg(args, int); inst->midtone[2] = va_arg(args, int); inst->highlight[0] = va_arg(args, int); inst->highlight[1] = va_arg(args, int); inst->highlight[2] = va_arg(args, int); inst->blender = va_arg(args, int); inst->type = SceneEffect::Tritone; return inst; } }; struct RenderMethod { private: uint32_t refCnt = 0; Key key; protected: RenderRegion vport; //viewport public: //common implementation uint32_t ref(); uint32_t unref(); RenderRegion viewport(); bool viewport(const RenderRegion& vp); //main features virtual ~RenderMethod() {} virtual bool preUpdate() = 0; virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; virtual RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; virtual bool postUpdate() = 0; virtual bool preRender() = 0; virtual bool renderShape(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0; virtual bool postRender() = 0; virtual void dispose(RenderData data) = 0; virtual RenderRegion region(RenderData data) = 0; virtual bool bounds(RenderData data, Point* pt4, const Matrix& m) = 0; virtual bool blend(BlendMethod method) = 0; virtual ColorSpace colorSpace() = 0; virtual const RenderSurface* mainSurface() = 0; virtual bool clear() = 0; virtual bool sync() = 0; virtual bool intersectsShape(RenderData data, const RenderRegion& region) = 0; virtual bool intersectsImage(RenderData data, const RenderRegion& region) = 0; //composition virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) = 0; virtual bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) = 0; virtual bool endComposite(RenderCompositor* cmp) = 0; //post effects virtual void prepare(RenderEffect* effect, const Matrix& transform) = 0; virtual bool region(RenderEffect* effect) = 0; virtual bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) = 0; virtual void dispose(RenderEffect* effect) = 0; //partial rendering virtual void damage(RenderData rd, const RenderRegion& region) = 0; virtual bool partial(bool disable) = 0; }; static inline bool MASK_REGION_MERGING(MaskMethod method) { switch(method) { case MaskMethod::Alpha: case MaskMethod::InvAlpha: case MaskMethod::Luma: case MaskMethod::InvLuma: case MaskMethod::Subtract: case MaskMethod::Intersect: return false; //these might expand the rendering region case MaskMethod::Add: case MaskMethod::Difference: case MaskMethod::Lighten: case MaskMethod::Darken: return true; default: TVGERR("RENDERER", "Unsupported Masking Method! = %d", (int)method); return false; } } static inline uint8_t CHANNEL_SIZE(ColorSpace cs) { switch(cs) { case ColorSpace::ABGR8888: case ColorSpace::ABGR8888S: case ColorSpace::ARGB8888: case ColorSpace::ARGB8888S: return sizeof(uint32_t); case ColorSpace::Grayscale8: return sizeof(uint8_t); case ColorSpace::Unknown: default: TVGERR("RENDERER", "Unsupported Channel Size! = %d", (int)cs); return 0; } } static inline ColorSpace MASK_TO_COLORSPACE(RenderMethod* renderer, MaskMethod method) { switch(method) { case MaskMethod::Alpha: case MaskMethod::InvAlpha: case MaskMethod::Add: case MaskMethod::Difference: case MaskMethod::Subtract: case MaskMethod::Intersect: case MaskMethod::Lighten: case MaskMethod::Darken: return ColorSpace::Grayscale8; //TODO: Optimize Luma/InvLuma colorspace to Grayscale8 case MaskMethod::Luma: case MaskMethod::InvLuma: return renderer->colorSpace(); default: TVGERR("RENDERER", "Unsupported Masking Size! = %d", (int)method); return ColorSpace::Unknown; } } static inline uint8_t MULTIPLY(uint8_t c, uint8_t a) { return (((c) * (a) + 0xff) >> 8); } } #endif //_TVG_RENDER_H_thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtins-internal.h000664 001750 001750 00000011530 15164251010 050040 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BUILTINS_INTERNAL_H #define ECMA_BUILTINS_INTERNAL_H #ifndef ECMA_BUILTINS_INTERNAL #error "!ECMA_BUILTINS_INTERNAL" #endif /* !ECMA_BUILTINS_INTERNAL */ #include "ecma-builtins.h" #include "ecma-globals.h" /** * Type of built-in properties. */ typedef enum { ECMA_BUILTIN_PROPERTY_SIMPLE, /**< simple value property */ ECMA_BUILTIN_PROPERTY_NUMBER, /**< number value property */ ECMA_BUILTIN_PROPERTY_STRING, /**< string value property */ ECMA_BUILTIN_PROPERTY_SYMBOL, /**< symbol value property */ ECMA_BUILTIN_PROPERTY_INTRINSIC_PROPERTY, /**< intrinsic routine property */ ECMA_BUILTIN_PROPERTY_ACCESSOR_BUILTIN_FUNCTION, /**< full accessor property with builtin function object getter/setter pair */ ECMA_BUILTIN_PROPERTY_OBJECT, /**< builtin object property */ ECMA_BUILTIN_PROPERTY_ROUTINE, /**< routine property */ ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_WRITE, /**< full accessor property */ ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_ONLY, /**< read-only accessor property */ ECMA_BUILTIN_PROPERTY_END, /**< last property */ } ecma_builtin_property_type_t; /** * Type of symbolic built-in number types (starting from 256). */ typedef enum { ECMA_BUILTIN_NUMBER_MAX = 256, /**< value of ECMA_NUMBER_MAX_VALUE */ ECMA_BUILTIN_NUMBER_MIN, /**< value of ECMA_NUMBER_MIN_VALUE */ ECMA_BUILTIN_NUMBER_EPSILON, /**< value of ECMA_NUMBER_EPSILON */ ECMA_BUILTIN_NUMBER_MAX_SAFE_INTEGER, /**< value of ECMA_NUMBER_MAX_SAFE_INTEGER */ ECMA_BUILTIN_NUMBER_MIN_SAFE_INTEGER, /**< value of ECMA_NUMBER_MIN_SAFE_INTEGER */ ECMA_BUILTIN_NUMBER_E, /**< value of ECMA_NUMBER_E */ ECMA_BUILTIN_NUMBER_PI, /**< value of ECMA_NUMBER_PI */ ECMA_BUILTIN_NUMBER_LN10, /**< value of ECMA_NUMBER_LN10 */ ECMA_BUILTIN_NUMBER_LN2, /**< value of ECMA_NUMBER_LN2 */ ECMA_BUILTIN_NUMBER_LOG2E, /**< value of ECMA_NUMBER_LOG2E */ ECMA_BUILTIN_NUMBER_LOG10E, /**< value of ECMA_NUMBER_LOG10E */ ECMA_BUILTIN_NUMBER_SQRT2, /**< value of ECMA_NUMBER_SQRT2 */ ECMA_BUILTIN_NUMBER_SQRT_1_2, /**< value of ECMA_NUMBER_SQRT_1_2 */ ECMA_BUILTIN_NUMBER_NAN, /**< result of ecma_number_make_nan () */ ECMA_BUILTIN_NUMBER_POSITIVE_INFINITY, /**< result of ecma_number_make_infinity (false) */ ECMA_BUILTIN_NUMBER_NEGATIVE_INFINITY, /**< result of ecma_number_make_infinity (true) */ } ecma_builtin_number_type_t; /** * Description of built-in properties. */ typedef struct { uint16_t magic_string_id; /**< name of the property */ uint8_t type; /**< type of the property */ uint8_t attributes; /**< attributes of the property */ uint16_t value; /**< value of the property */ } ecma_builtin_property_descriptor_t; #define BUILTIN_ROUTINE(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ extern const ecma_builtin_property_descriptor_t ecma_builtin_##lowercase_name##_property_descriptor_list[]; \ ecma_value_t ecma_builtin_##lowercase_name##_dispatch_call (const ecma_value_t *, uint32_t); \ ecma_value_t ecma_builtin_##lowercase_name##_dispatch_construct (const ecma_value_t *, uint32_t); \ ecma_value_t ecma_builtin_##lowercase_name##_dispatch_routine (uint8_t builtin_routine_id, \ ecma_value_t this_arg_value, \ const ecma_value_t[], \ uint32_t); #define BUILTIN(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) \ extern const ecma_builtin_property_descriptor_t ecma_builtin_##lowercase_name##_property_descriptor_list[]; \ ecma_value_t ecma_builtin_##lowercase_name##_dispatch_routine (uint8_t builtin_routine_id, \ ecma_value_t this_arg_value, \ const ecma_value_t[], \ uint32_t); #include "ecma-builtins.inc.h" #undef BUILTIN_ROUTINE #undef BUILTIN #endif /* !ECMA_BUILTINS_INTERNAL_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jmem/meson.build000664 001750 001750 00000000377 15164251010 042420 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'jmem-allocator-internal.h', 'jmem.h', 'jmem-allocator.cpp', 'jmem-heap.cpp', 'jmem-poolman.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/opcodes.h000664 001750 001750 00000015327 15164251010 041556 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 OPCODES_H #define OPCODES_H #include "ecma-globals.h" #include "vm-defines.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup vm_opcodes Opcodes * @{ */ /** * Number arithmetic operations. */ typedef enum { NUMBER_ARITHMETIC_SUBTRACTION, /**< subtraction */ NUMBER_ARITHMETIC_MULTIPLICATION, /**< multiplication */ NUMBER_ARITHMETIC_DIVISION, /**< division */ NUMBER_ARITHMETIC_REMAINDER, /**< remainder calculation */ NUMBER_ARITHMETIC_EXPONENTIATION, /**< exponentiation */ } number_arithmetic_op; /** * Number bitwise logic operations. */ typedef enum { NUMBER_BITWISE_LOGIC_AND, /**< bitwise AND calculation */ NUMBER_BITWISE_LOGIC_OR, /**< bitwise OR calculation */ NUMBER_BITWISE_LOGIC_XOR, /**< bitwise XOR calculation */ NUMBER_BITWISE_SHIFT_LEFT, /**< bitwise LEFT SHIFT calculation */ NUMBER_BITWISE_SHIFT_RIGHT, /**< bitwise RIGHT_SHIFT calculation */ NUMBER_BITWISE_SHIFT_URIGHT, /**< bitwise UNSIGNED RIGHT SHIFT calculation */ } number_bitwise_logic_op; /** * Types for opfunc_create_executable_object. */ typedef enum { VM_CREATE_EXECUTABLE_OBJECT_GENERATOR, /**< create a generator function */ VM_CREATE_EXECUTABLE_OBJECT_ASYNC, /**< create an async function */ } vm_create_executable_object_type_t; /** * The stack contains spread object during the upcoming APPEND_ARRAY operation */ #define OPFUNC_HAS_SPREAD_ELEMENT (1 << 8) ecma_value_t opfunc_equality (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t do_number_arithmetic (number_arithmetic_op op, ecma_value_t left_value, ecma_value_t right_value); ecma_value_t opfunc_unary_operation (ecma_value_t left_value, bool is_plus); ecma_value_t do_number_bitwise_logic (number_bitwise_logic_op op, ecma_value_t left_value, ecma_value_t right_value); ecma_value_t do_number_bitwise_not (ecma_value_t value); ecma_value_t opfunc_addition (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t opfunc_relation (ecma_value_t left_value, ecma_value_t right_value, bool left_first, bool is_invert); ecma_value_t opfunc_in (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t opfunc_instanceof (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t opfunc_typeof (ecma_value_t left_value); void opfunc_set_data_property (ecma_object_t *object_p, ecma_string_t *prop_name_p, ecma_value_t value); void opfunc_set_accessor (bool is_getter, ecma_value_t object, ecma_string_t *accessor_name_p, ecma_value_t accessor); ecma_value_t vm_op_delete_prop (ecma_value_t object, ecma_value_t property, bool is_strict); ecma_value_t vm_op_delete_var (ecma_value_t name_literal, ecma_object_t *lex_env_p); ecma_collection_t *opfunc_for_in (ecma_value_t left_value, ecma_value_t *result_obj_p); ecma_collection_t *opfunc_spread_arguments (ecma_value_t *stack_top_p, uint8_t argument_list_len); ecma_value_t opfunc_append_array (ecma_value_t *stack_top_p, uint16_t values_length); vm_executable_object_t *opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, vm_create_executable_object_type_t type); extern const uint8_t opfunc_resume_executable_object_with_throw[]; extern const uint8_t opfunc_resume_executable_object_with_return[]; ecma_value_t opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, ecma_value_t value); void opfunc_async_generator_yield (ecma_extended_object_t *async_generator_object_p, ecma_value_t value); ecma_value_t opfunc_async_create_and_await (vm_frame_ctx_t *frame_ctx_p, ecma_value_t value, uint16_t extra_flags); ecma_value_t opfunc_init_class_fields (ecma_object_t *class_object_p, ecma_value_t this_val); ecma_value_t opfunc_init_static_class_fields (ecma_value_t function_object, ecma_value_t this_val); ecma_value_t opfunc_add_computed_field (ecma_value_t class_object, ecma_value_t name); ecma_value_t opfunc_create_implicit_class_constructor (uint8_t opcode, const ecma_compiled_code_t *bytecode_p); void opfunc_set_home_object (ecma_object_t *func_p, ecma_object_t *parent_env_p); ecma_value_t opfunc_define_field (ecma_value_t base, ecma_value_t property, ecma_value_t value); ecma_string_t *opfunc_make_private_key (ecma_value_t descriptor); ecma_value_t opfunc_private_in (ecma_value_t base, ecma_value_t property); ecma_value_t opfunc_private_field_add (ecma_value_t base, ecma_value_t property, ecma_value_t value); ecma_value_t opfunc_private_set (ecma_value_t base, ecma_value_t property, ecma_value_t value); ecma_value_t opfunc_private_get (ecma_value_t base, ecma_value_t property); void opfunc_collect_private_properties (ecma_value_t constructor, ecma_value_t prop_name, ecma_value_t method, uint8_t opcode); void opfunc_push_class_environment (vm_frame_ctx_t *frame_ctx_p, ecma_value_t **vm_stack_top, ecma_value_t class_name); ecma_value_t opfunc_init_class (vm_frame_ctx_t *frame_context_p, ecma_value_t *stack_top_p); ecma_object_t *opfunc_bind_class_environment (ecma_object_t *lex_env_p, ecma_object_t *home_object_p, ecma_object_t *ctor_p, ecma_object_t *func_obj_p); void opfunc_pop_lexical_environment (vm_frame_ctx_t *frame_ctx_p); void opfunc_finalize_class (vm_frame_ctx_t *frame_ctx_p, ecma_value_t **vm_stack_top_p, ecma_value_t class_name); ecma_value_t opfunc_form_super_reference (ecma_value_t **vm_stack_top_p, vm_frame_ctx_t *frame_ctx_p, ecma_value_t prop_name, uint8_t opcode); ecma_value_t opfunc_assign_super_reference (ecma_value_t **vm_stack_top_p, vm_frame_ctx_t *frame_ctx_p, uint32_t opcode_data); ecma_value_t opfunc_copy_data_properties (ecma_value_t target_object, ecma_value_t source_object, ecma_value_t filter_array); ecma_value_t opfunc_lexical_scope_has_restricted_binding (vm_frame_ctx_t *vm_frame_ctx_p, ecma_string_t *name_p); /** * @} * @} */ #endif /* !OPCODES_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/color_cache.cpp000664 001750 001750 00000003004 15164251010 036415 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Color Cache for WebP Lossless // // Author: Jyrki Alakuijala (jyrki@google.com) #include #include "tvgCommon.h" #include "./color_cache.h" #include "../utils/utils.h" //------------------------------------------------------------------------------ // VP8LColorCache. int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) { const int hash_size = 1 << hash_bits; assert(cc != NULL); assert(hash_bits > 0); cc->colors_ = tvg::calloc((uint64_t)hash_size, sizeof(*cc->colors_)); if (cc->colors_ == NULL) return 0; cc->hash_shift_ = 32 - hash_bits; cc->hash_bits_ = hash_bits; return 1; } void VP8LColorCacheClear(VP8LColorCache* const cc) { if (cc != NULL) { tvg::free(cc->colors_); cc->colors_ = NULL; } } void VP8LColorCacheCopy(const VP8LColorCache* const src, VP8LColorCache* const dst) { assert(src != NULL); assert(dst != NULL); assert(src->hash_bits_ == dst->hash_bits_); memcpy(dst->colors_, src->colors_, ((size_t)1u << dst->hash_bits_) * sizeof(*dst->colors_)); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testShape.cpp000664 001750 001750 00000021172 15164251010 032643 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; TEST_CASE("Shape Creation", "[tvgShape]") { auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->type() == Type::Shape); Paint::rel(shape); } TEST_CASE("Appending Commands", "[tvgShape]") { auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->close() == Result::Success); REQUIRE(shape->moveTo(100, 100) == Result::Success); REQUIRE(shape->moveTo(99999999.0f, -99999999.0f) == Result::Success); REQUIRE(shape->moveTo(0, 0) == Result::Success); REQUIRE(shape->lineTo(120, 140) == Result::Success); REQUIRE(shape->lineTo(99999999.0f, -99999999.0f) == Result::Success); REQUIRE(shape->lineTo(0, 0) == Result::Success); REQUIRE(shape->cubicTo(0, 0, 0, 0, 0, 0) == Result::Success); REQUIRE(shape->cubicTo(0, 0, 99999999.0f, -99999999.0f, 0, 0) == Result::Success); REQUIRE(shape->cubicTo(0, 0, 99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f) == Result::Success); REQUIRE(shape->cubicTo(99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f) == Result::Success); REQUIRE(shape->close() == Result::Success); REQUIRE(shape->reset() == Result::Success); REQUIRE(shape->reset() == Result::Success); Paint::rel(shape); } TEST_CASE("Appending Shapes", "[tvgShape]") { auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->moveTo(100, 100) == Result::Success); REQUIRE(shape->lineTo(120, 140) == Result::Success); REQUIRE(shape->appendRect(0, 0, 0, 0, 0, 0) == Result::Success); REQUIRE(shape->appendRect(0, 0,99999999.0f, -99999999.0f, 0, 0, true) == Result::Success); REQUIRE(shape->appendRect(0, 0, 0, 0, -99999999.0f, 99999999.0f, false) == Result::Success); REQUIRE(shape->appendRect(99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, true) == Result::Success); REQUIRE(shape->appendRect(99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, false) == Result::Success); REQUIRE(shape->appendCircle(0, 0, 0, 0) == Result::Success); REQUIRE(shape->appendCircle(-99999999.0f, 99999999.0f, 0, 0, true) == Result::Success); REQUIRE(shape->appendCircle(-99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f, false) == Result::Success); Paint::rel(shape); } TEST_CASE("Appending Paths", "[tvgShape]") { auto shape = Shape::gen(); REQUIRE(shape); //Negative cases REQUIRE(shape->appendPath(nullptr, 0, nullptr, 0) == Result::InvalidArguments); REQUIRE(shape->appendPath(nullptr, 100, nullptr, 0) == Result::InvalidArguments); REQUIRE(shape->appendPath(nullptr, 0, nullptr, 100) == Result::InvalidArguments); PathCommand cmds[5] = { PathCommand::Close, PathCommand::MoveTo, PathCommand::LineTo, PathCommand::CubicTo, PathCommand::Close }; Point pts[5] = { {100, 100}, {200, 200}, {10, 10}, {20, 20}, {30, 30} }; REQUIRE(shape->appendPath(cmds, 0, pts, 5) == Result::InvalidArguments); REQUIRE(shape->appendPath(cmds, 5, pts, 0) == Result::InvalidArguments); REQUIRE(shape->appendPath(cmds, 5, pts, 5) == Result::Success); const PathCommand* cmds2; const Point* pts2; uint32_t cmds2Cnt, pts2Cnt; REQUIRE(shape->path(&cmds2, &cmds2Cnt, &pts2, &pts2Cnt) == Result::Success); REQUIRE(cmds2Cnt == 5); REQUIRE(pts2Cnt == 5); for (int i = 0; i < 5; ++i) { REQUIRE(cmds2[i] == cmds[i]); REQUIRE(pts[i].x == pts2[i].x); REQUIRE(pts[i].y == pts2[i].y); } shape->reset(); REQUIRE(shape->path(nullptr, &cmds2Cnt, nullptr, &pts2Cnt) == Result::Success); REQUIRE(cmds2Cnt == 0); REQUIRE(pts2Cnt == 0); Paint::rel(shape); } TEST_CASE("Stroking", "[tvgShape]") { auto shape = Shape::gen(); REQUIRE(shape); //Stroke Order Before Stroke Setting REQUIRE(shape->order(true) == Result::Success); REQUIRE(shape->order(false) == Result::Success); //Stroke Width REQUIRE(shape->strokeWidth(0) == Result::Success); REQUIRE(shape->strokeWidth() == 0); REQUIRE(shape->strokeWidth(300) == Result::Success); REQUIRE(shape->strokeWidth() == 300); //Stroke Color uint8_t r, g, b, a; REQUIRE(shape->strokeFill(0, 50, 100, 200) == Result::Success); REQUIRE(shape->strokeFill(nullptr, nullptr, &b, nullptr) == Result::Success); REQUIRE(b == 100); REQUIRE(shape->strokeFill(&r, &g, &b, &a) == Result::Success); REQUIRE(r == 0); REQUIRE(g == 50); REQUIRE(b == 100); REQUIRE(a == 200); REQUIRE(shape->strokeFill(nullptr, nullptr, nullptr, nullptr) == Result::Success); //Stroke Dash REQUIRE(shape->strokeDash(nullptr, 3) == Result::InvalidArguments); float dashPattern0[3] = {-10.0f, 1.5f, 2.22f}; REQUIRE(shape->strokeDash(dashPattern0, 0) == Result::InvalidArguments); REQUIRE(shape->strokeDash(dashPattern0, 3) == Result::Success); float dashPattern1[2] = {0.0f, 0.0f}; REQUIRE(shape->strokeDash(dashPattern1, 2) == Result::Success); float dashPattern2[1] = {10.0f}; REQUIRE(shape->strokeDash(dashPattern2, 1) == Result::Success); float dashPattern3[3] = {1.0f, 1.5f, 2.22f}; REQUIRE(shape->strokeDash(dashPattern3, 3) == Result::Success); REQUIRE(shape->strokeDash(dashPattern3, 3, 4.5) == Result::Success); const float* dashPattern4; float offset; REQUIRE(shape->strokeDash(nullptr) == 3); REQUIRE(shape->strokeDash(&dashPattern4) == 3); REQUIRE(shape->strokeDash(&dashPattern4, &offset) == 3); REQUIRE(dashPattern4[0] == 1.0f); REQUIRE(dashPattern4[1] == 1.5f); REQUIRE(dashPattern4[2] == 2.22f); REQUIRE(offset == 4.5f); REQUIRE(shape->strokeDash(nullptr, 0) == Result::Success); //Stroke Cap REQUIRE(shape->strokeCap() == StrokeCap::Square); REQUIRE(shape->strokeCap(StrokeCap::Round) == Result::Success); REQUIRE(shape->strokeCap(StrokeCap::Butt) == Result::Success); REQUIRE(shape->strokeCap() == StrokeCap::Butt); //Stroke Join REQUIRE(shape->strokeJoin() == StrokeJoin::Bevel); REQUIRE(shape->strokeJoin(StrokeJoin::Miter) == Result::Success); REQUIRE(shape->strokeJoin(StrokeJoin::Round) == Result::Success); REQUIRE(shape->strokeJoin() == StrokeJoin::Round); //Stroke Miterlimit REQUIRE(shape->strokeMiterlimit() == 4.0f); REQUIRE(shape->strokeMiterlimit(0.00001f) == Result::Success); REQUIRE(shape->strokeMiterlimit() == 0.00001f); REQUIRE(shape->strokeMiterlimit(1000.0f) == Result::Success); REQUIRE(shape->strokeMiterlimit() == 1000.0f); REQUIRE(shape->strokeMiterlimit(-0.001f) == Result::InvalidArguments); REQUIRE(shape->trimpath(0.3f, 0.88f, false) == Result::Success); //Stroke Order After Stroke Setting REQUIRE(shape->order(true) == Result::Success); REQUIRE(shape->order(false) == Result::Success); Paint::rel(shape); } TEST_CASE("Shape Filling", "[tvgShape]") { auto shape = Shape::gen(); REQUIRE(shape); //Fill Color uint8_t r, g, b, a; REQUIRE(shape->fill(255, 100, 50, 5) == Result::Success); REQUIRE(shape->fill(&r, nullptr, &b, nullptr) == Result::Success); REQUIRE(r == 255); REQUIRE(b == 50); REQUIRE(shape->fill(&r, &g, &b, &a) == Result::Success); REQUIRE(g == 100); REQUIRE(a == 5); //Fill Rule REQUIRE(shape->fillRule() == FillRule::NonZero); REQUIRE(shape->fillRule(FillRule::EvenOdd) == Result::Success); REQUIRE(shape->fillRule() == FillRule::EvenOdd); Paint::rel(shape); } loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint8array.cpp000664 001750 001750 00000004344 15164251010 052673 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-uint8array.inc.h" #define BUILTIN_UNDERSCORED_ID uint8array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup uint8array ECMA Uint8Array object built-in * @{ */ /** * Handle calling [[Call]] of Uint8Array * * @return ecma value */ ecma_value_t ecma_builtin_uint8array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_UINT8_ARRAY_REQUIRES_NEW); } /* ecma_builtin_uint8array_dispatch_call */ /** * Handle calling [[Construct]] of Uint8Array * * @return ecma value */ ecma_value_t ecma_builtin_uint8array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_UINT8_ARRAY); } /* ecma_builtin_uint8array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgLoadModule.h000664 001750 001750 00000011277 15164251010 034541 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOAD_MODULE_H_ #define _TVG_LOAD_MODULE_H_ #include #include "tvgCommon.h" #include "tvgRender.h" #include "tvgInlist.h" struct AssetResolver { std::function func; void* data; }; namespace tvg { struct LoadModule { INLIST_ITEM(LoadModule); //Use either hashkey(data) or hashpath(path) uintptr_t hashkey = 0; char* hashpath = nullptr; FileType type; //current loader file type atomic sharing{}; //reference count bool readied = false; //read done already. bool cached = false; //cached for sharing LoadModule(FileType type) : type(type) {} virtual ~LoadModule() { tvg::free(hashpath); } void cache(uintptr_t data) { hashkey = data; cached = true; } void cache(char* data) { hashpath = data; cached = true; } virtual bool open(const char* path) { return false; } virtual bool open(const char* data, uint32_t size, const char* rpath, bool copy) { return false; } virtual bool resize(Paint* paint, float w, float h) { return false; } virtual void sync() {}; //finish immediately if any async update jobs. virtual bool read() { if (readied) return false; readied = true; return true; } virtual bool close() { if (sharing == 0) return true; --sharing; return false; } char* open(const char* path, uint32_t& size, bool text = false) { #ifdef THORVG_FILE_IO_SUPPORT auto f = fopen(path, text ? "r" : "rb"); if (!f) return nullptr; fseek(f, 0, SEEK_END); size = ftell(f); if (size == 0) { fclose(f); return nullptr; } auto content = tvg::malloc(sizeof(char) * (text ? size + 1 : size)); fseek(f, 0, SEEK_SET); size = fread(content, sizeof(char), size, f); if (text) content[size] = '\0'; fclose(f); return content; #endif return nullptr; } }; struct ImageLoader : LoadModule { static atomic cs; //desired value float w = 0, h = 0; //default image size RenderSurface surface; ImageLoader(FileType type) : LoadModule(type) {} virtual bool animatable() { return false; } //true if this loader supports animation. virtual Paint* paint() { return nullptr; } virtual void set(const AssetResolver* resolver) {} virtual RenderSurface* bitmap() { if (surface.data) return &surface; return nullptr; } }; struct FontMetrics { Point size; //text width, height float scale; Point align{}, box{}, spacing{1.0f, 1.0f}; float fontSize = 0.0f; TextWrap wrap = TextWrap::None; void *engine = nullptr; //engine extension ~FontMetrics() { tvg::free(engine); } }; struct FontLoader : LoadModule { static constexpr const float DPI = 96.0f / 72.0f; //dpi base? char* name = nullptr; FontLoader(FileType type) : LoadModule(type) {} using LoadModule::read; virtual bool get(FontMetrics& fm, char* text, RenderPath& out) = 0; virtual void transform(Paint* paint, FontMetrics& fm, float italicShear) = 0; virtual void release(FontMetrics& fm) = 0; virtual void metrics(const FontMetrics& fm, TextMetrics& out) = 0; virtual void copy(const FontMetrics& in, FontMetrics& out) = 0; }; } #endif //_TVG_LOAD_MODULE_H_ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/include/jerryscript-types.h000664 001750 001750 00000105737 15164251010 044652 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JERRYSCRIPT_TYPES_H #define JERRYSCRIPT_TYPES_H #include #include #include #include "jerryscript-compiler.h" JERRY_C_API_BEGIN /** * @defgroup jerry-api-types JerryScript public API types * @{ */ /** * JerryScript init flags. */ typedef enum { JERRY_INIT_EMPTY = (0u), /**< empty flag set */ JERRY_INIT_SHOW_OPCODES = (1u << 0), /**< dump byte-code to log after parse */ JERRY_INIT_SHOW_REGEXP_OPCODES = (1u << 1), /**< dump regexp byte-code to log after compilation */ JERRY_INIT_MEM_STATS = (1u << 2), /**< dump memory statistics */ } jerry_init_flag_t; /** * Jerry log levels. The levels are in severity order * where the most serious levels come first. */ typedef enum { JERRY_LOG_LEVEL_ERROR = 0u, /**< the engine will terminate after the message is printed */ JERRY_LOG_LEVEL_WARNING = 1u, /**< a request is aborted, but the engine continues its operation */ JERRY_LOG_LEVEL_DEBUG = 2u, /**< debug messages from the engine, low volume */ JERRY_LOG_LEVEL_TRACE = 3u /**< detailed info about engine internals, potentially high volume */ } jerry_log_level_t; /** * JerryScript API Error object types. */ typedef enum { JERRY_ERROR_NONE = 0, /**< No Error */ JERRY_ERROR_COMMON, /**< Error */ JERRY_ERROR_EVAL, /**< EvalError */ JERRY_ERROR_RANGE, /**< RangeError */ JERRY_ERROR_REFERENCE, /**< ReferenceError */ JERRY_ERROR_SYNTAX, /**< SyntaxError */ JERRY_ERROR_TYPE, /**< TypeError */ JERRY_ERROR_URI, /**< URIError */ JERRY_ERROR_AGGREGATE /**< AggregateError */ } jerry_error_t; /** * JerryScript feature types. */ typedef enum { JERRY_FEATURE_CPOINTER_32_BIT, /**< 32 bit compressed pointers */ JERRY_FEATURE_ERROR_MESSAGES, /**< error messages */ JERRY_FEATURE_JS_PARSER, /**< js-parser */ JERRY_FEATURE_HEAP_STATS, /**< memory statistics */ JERRY_FEATURE_PARSER_DUMP, /**< parser byte-code dumps */ JERRY_FEATURE_REGEXP_DUMP, /**< regexp byte-code dumps */ JERRY_FEATURE_SNAPSHOT_SAVE, /**< saving snapshot files */ JERRY_FEATURE_SNAPSHOT_EXEC, /**< executing snapshot files */ JERRY_FEATURE_DEBUGGER, /**< debugging */ JERRY_FEATURE_VM_EXEC_STOP, /**< stopping ECMAScript execution */ JERRY_FEATURE_VM_THROW, /**< capturing ECMAScript throws */ JERRY_FEATURE_JSON, /**< JSON support */ JERRY_FEATURE_PROMISE, /**< promise support */ JERRY_FEATURE_TYPEDARRAY, /**< Typedarray support */ JERRY_FEATURE_DATE, /**< Date support */ JERRY_FEATURE_REGEXP, /**< Regexp support */ JERRY_FEATURE_LINE_INFO, /**< line info available */ JERRY_FEATURE_LOGGING, /**< logging */ JERRY_FEATURE_SYMBOL, /**< symbol support */ JERRY_FEATURE_DATAVIEW, /**< DataView support */ JERRY_FEATURE_PROXY, /**< Proxy support */ JERRY_FEATURE_MAP, /**< Map support */ JERRY_FEATURE_SET, /**< Set support */ JERRY_FEATURE_WEAKMAP, /**< WeakMap support */ JERRY_FEATURE_WEAKSET, /**< WeakSet support */ JERRY_FEATURE_BIGINT, /**< BigInt support */ JERRY_FEATURE_REALM, /**< realm support */ JERRY_FEATURE_GLOBAL_THIS, /**< GlobalThisValue support */ JERRY_FEATURE_PROMISE_CALLBACK, /**< Promise callback support */ JERRY_FEATURE_MODULE, /**< Module support */ JERRY_FEATURE_WEAKREF, /**< WeakRef support */ JERRY_FEATURE_FUNCTION_TO_STRING, /**< function toString support */ JERRY_FEATURE__COUNT /**< number of features. NOTE: must be at the end of the list */ } jerry_feature_t; /** * GC operational modes. */ typedef enum { JERRY_GC_PRESSURE_LOW, /**< free unused objects, but keep memory * allocated for performance improvements * such as property hash tables for large objects */ JERRY_GC_PRESSURE_HIGH /**< free as much memory as possible */ } jerry_gc_mode_t; /** * Jerry regexp flags. */ typedef enum { JERRY_REGEXP_FLAG_GLOBAL = (1u << 1), /**< Globally scan string */ JERRY_REGEXP_FLAG_IGNORE_CASE = (1u << 2), /**< Ignore case */ JERRY_REGEXP_FLAG_MULTILINE = (1u << 3), /**< Multiline string scan */ JERRY_REGEXP_FLAG_STICKY = (1u << 4), /**< ECMAScript v11, 21.2.5.14 */ JERRY_REGEXP_FLAG_UNICODE = (1u << 5), /**< ECMAScript v11, 21.2.5.17 */ JERRY_REGEXP_FLAG_DOTALL = (1u << 6) /**< ECMAScript v11, 21.2.5.3 */ } jerry_regexp_flags_t; /** * Character type of JerryScript. */ typedef uint8_t jerry_char_t; /** * Size type of JerryScript. */ typedef uint32_t jerry_size_t; /** * Length type of JerryScript. */ typedef uint32_t jerry_length_t; /** * Description of a JerryScript value. */ typedef uint32_t jerry_value_t; /** * Option bits for jerry_parse_options_t. */ typedef enum { JERRY_PARSE_NO_OPTS = 0, /**< no options passed */ JERRY_PARSE_STRICT_MODE = (1 << 0), /**< enable strict mode */ JERRY_PARSE_MODULE = (1 << 1), /**< parse source as an ECMAScript module */ JERRY_PARSE_HAS_ARGUMENT_LIST = (1 << 2), /**< argument_list field is valid, * this also means that function parsing will be done */ JERRY_PARSE_HAS_SOURCE_NAME = (1 << 3), /**< source_name field is valid */ JERRY_PARSE_HAS_START = (1 << 4), /**< start_line and start_column fields are valid */ JERRY_PARSE_HAS_USER_VALUE = (1 << 5), /**< user_value field is valid */ } jerry_parse_option_enable_feature_t; /** * Various configuration options for parsing functions such as jerry_parse or jerry_parse_function. */ typedef struct { uint32_t options; /**< combination of jerry_parse_option_enable_feature_t values */ jerry_value_t argument_list; /**< function argument list if JERRY_PARSE_HAS_ARGUMENT_LIST is set in options * Note: must be string value */ jerry_value_t source_name; /**< source name string (usually a file name) * if JERRY_PARSE_HAS_SOURCE_NAME is set in options * Note: must be string value */ uint32_t start_line; /**< start line of the source code if JERRY_PARSE_HAS_START is set in options */ uint32_t start_column; /**< start column of the source code if JERRY_PARSE_HAS_START is set in options */ jerry_value_t user_value; /**< user value assigned to all functions created by this script including eval * calls executed by the script if JERRY_PARSE_HAS_USER_VALUE is set in options */ } jerry_parse_options_t; /** * Description of ECMA property descriptor. */ typedef enum { JERRY_PROP_NO_OPTS = (0), /**< empty property descriptor */ JERRY_PROP_IS_CONFIGURABLE = (1 << 0), /**< [[Configurable]] */ JERRY_PROP_IS_ENUMERABLE = (1 << 1), /**< [[Enumerable]] */ JERRY_PROP_IS_WRITABLE = (1 << 2), /**< [[Writable]] */ JERRY_PROP_IS_CONFIGURABLE_DEFINED = (1 << 3), /**< is [[Configurable]] defined? */ JERRY_PROP_IS_ENUMERABLE_DEFINED = (1 << 4), /**< is [[Enumerable]] defined? */ JERRY_PROP_IS_WRITABLE_DEFINED = (1 << 5), /**< is [[Writable]] defined? */ JERRY_PROP_IS_VALUE_DEFINED = (1 << 6), /**< is [[Value]] defined? */ JERRY_PROP_IS_GET_DEFINED = (1 << 7), /**< is [[Get]] defined? */ JERRY_PROP_IS_SET_DEFINED = (1 << 8), /**< is [[Set]] defined? */ JERRY_PROP_SHOULD_THROW = (1 << 9), /**< should throw on error, instead of returning with false */ } jerry_property_descriptor_flags_t; /** * Description of ECMA property descriptor. */ typedef struct { uint16_t flags; /**< any combination of jerry_property_descriptor_flags_t bits */ jerry_value_t value; /**< [[Value]] */ jerry_value_t getter; /**< [[Get]] */ jerry_value_t setter; /**< [[Set]] */ } jerry_property_descriptor_t; /** * JerryScript object property filter options. */ typedef enum { JERRY_PROPERTY_FILTER_ALL = 0, /**< List all property keys independently * from key type or property value attributes * (equivalent to Reflect.ownKeys call) */ JERRY_PROPERTY_FILTER_TRAVERSE_PROTOTYPE_CHAIN = (1 << 0), /**< Include keys from the objects's * prototype chain as well */ JERRY_PROPERTY_FILTER_EXCLUDE_NON_CONFIGURABLE = (1 << 1), /**< Exclude property key if * the property is non-configurable */ JERRY_PROPERTY_FILTER_EXCLUDE_NON_ENUMERABLE = (1 << 2), /**< Exclude property key if * the property is non-enumerable */ JERRY_PROPERTY_FILTER_EXCLUDE_NON_WRITABLE = (1 << 3), /**< Exclude property key if * the property is non-writable */ JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS = (1 << 4), /**< Exclude property key if it is a string */ JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS = (1 << 5), /**< Exclude property key if it is a symbol */ JERRY_PROPERTY_FILTER_EXCLUDE_INTEGER_INDICES = (1 << 6), /**< Exclude property key if it is an integer index */ JERRY_PROPERTY_FILTER_INTEGER_INDICES_AS_NUMBER = (1 << 7), /**< By default integer index property keys are * converted to string. Enabling this flags keeps * integer index property keys as numbers. */ } jerry_property_filter_t; /** * String encoding. */ typedef enum { JERRY_ENCODING_CESU8, /**< cesu-8 encoding */ JERRY_ENCODING_UTF8, /**< utf-8 encoding */ } jerry_encoding_t; /** * Description of JerryScript heap memory stats. * It is for memory profiling. */ typedef struct { size_t version; /**< the version of the stats struct */ size_t size; /**< heap total size */ size_t allocated_bytes; /**< currently allocated bytes */ size_t peak_allocated_bytes; /**< peak allocated bytes */ size_t reserved[4]; /**< padding for future extensions */ } jerry_heap_stats_t; /** * Call related information passed to jerry_external_handler_t. */ typedef struct jerry_call_info_t { jerry_value_t function; /**< invoked function object */ jerry_value_t this_value; /**< this value passed to the function */ jerry_value_t new_target; /**< current new target value, undefined for non-constructor calls */ } jerry_call_info_t; /** * Type of an external function handler. */ typedef jerry_value_t (*jerry_external_handler_t) (const jerry_call_info_t *call_info_p, const jerry_value_t args_p[], const jerry_length_t args_count); /** * Native free callback of generic value types. */ typedef void (*jerry_value_free_cb_t) (void *native_p); /** * Forward definition of jerry_object_native_info_t. */ struct jerry_object_native_info_t; /** * Native free callback of an object. */ typedef void (*jerry_object_native_free_cb_t) (void *native_p, struct jerry_object_native_info_t *info_p); /** * Free callback for external strings. */ typedef void (*jerry_external_string_free_cb_t) (jerry_char_t *string_p, jerry_size_t string_size, void *user_p); /** * Decorator callback for Error objects. The decorator can create * or update any properties of the newly created Error object. */ typedef void (*jerry_error_object_created_cb_t) (const jerry_value_t error_object, void *user_p); /** * Callback which tells whether the ECMAScript execution should be stopped. * * As long as the function returns with undefined the execution continues. * When a non-undefined value is returned the execution stops and the value * is thrown by the engine as an exception. * * Note: if the function returns with a non-undefined value it * must return with the same value for future calls. */ typedef jerry_value_t (*jerry_halt_cb_t) (void *user_p); /** * Callback function which is called when an exception is thrown in an ECMAScript code. * The callback should not change the exception_value. The callback is not called again * until the value is caught. * * Note: the engine considers exceptions thrown by external functions as never caught. */ typedef void (*jerry_throw_cb_t) (const jerry_value_t exception_value, void *user_p); /** * Function type applied to each unit of encoding when iterating over a string. */ typedef void (*jerry_string_iterate_cb_t) (uint32_t value, void *user_p); /** * Function type applied for each data property of an object. */ typedef bool (*jerry_object_property_foreach_cb_t) (const jerry_value_t property_name, const jerry_value_t property_value, void *user_data_p); /** * Function type applied for each object in the engine. */ typedef bool (*jerry_foreach_live_object_cb_t) (const jerry_value_t object, void *user_data_p); /** * Function type applied for each matching object in the engine. */ typedef bool (*jerry_foreach_live_object_with_info_cb_t) (const jerry_value_t object, void *object_data_p, void *user_data_p); /** * User context item manager */ typedef struct { /** * Callback responsible for initializing a context item, or NULL to zero out the memory. This is called lazily, the * first time jerry_context_data () is called with this manager. * * @param [in] data The buffer that JerryScript allocated for the manager. The buffer is zeroed out. The size is * determined by the bytes_needed field. The buffer is kept alive until jerry_cleanup () is called. */ void (*init_cb) (void *data); /** * Callback responsible for deinitializing a context item, or NULL. This is called as part of jerry_cleanup (), * right *before* the VM has been cleaned up. This is a good place to release strong references to jerry_value_t's * that the manager may be holding. * Note: because the VM has not been fully cleaned up yet, jerry_object_native_info_t free_cb's can still get called * *after* all deinit_cb's have been run. See finalize_cb for a callback that is guaranteed to run *after* all * free_cb's have been run. * * @param [in] data The buffer that JerryScript allocated for the manager. */ void (*deinit_cb) (void *data); /** * Callback responsible for finalizing a context item, or NULL. This is called as part of jerry_cleanup (), * right *after* the VM has been cleaned up and destroyed and jerry_... APIs cannot be called anymore. At this point, * all values in the VM have been cleaned up. This is a good place to clean up native state that can only be cleaned * up at the very end when there are no more VM values around that may need to access that state. * * @param [in] data The buffer that JerryScript allocated for the manager. After returning from this callback, * the data pointer may no longer be used. */ void (*finalize_cb) (void *data); /** * Number of bytes to allocate for this manager. This is the size of the buffer that JerryScript will allocate on * behalf of the manager. The pointer to this buffer is passed into init_cb, deinit_cb and finalize_cb. It is also * returned from the jerry_context_data () API. */ size_t bytes_needed; } jerry_context_data_manager_t; /** * Function type for allocating buffer for JerryScript context. */ typedef void *(*jerry_context_alloc_cb_t) (size_t size, void *cb_data_p); /** * Type information of a native pointer. */ typedef struct jerry_object_native_info_t { jerry_object_native_free_cb_t free_cb; /**< the free callback of the native pointer */ uint16_t number_of_references; /**< the number of value references which are marked by the garbage collector */ uint16_t offset_of_references; /**< byte offset indicating the start offset of value * references in the user allocated buffer */ } jerry_object_native_info_t; /** * An opaque declaration of the JerryScript context structure. */ typedef struct jerry_context_t jerry_context_t; /** * Enum that contains the supported binary operation types */ typedef enum { JERRY_BIN_OP_EQUAL = 0u, /**< equal comparison (==) */ JERRY_BIN_OP_STRICT_EQUAL, /**< strict equal comparison (===) */ JERRY_BIN_OP_LESS, /**< less relation (<) */ JERRY_BIN_OP_LESS_EQUAL, /**< less or equal relation (<=) */ JERRY_BIN_OP_GREATER, /**< greater relation (>) */ JERRY_BIN_OP_GREATER_EQUAL, /**< greater or equal relation (>=)*/ JERRY_BIN_OP_INSTANCEOF, /**< instanceof operation */ JERRY_BIN_OP_ADD, /**< addition operator (+) */ JERRY_BIN_OP_SUB, /**< subtraction operator (-) */ JERRY_BIN_OP_MUL, /**< multiplication operator (*) */ JERRY_BIN_OP_DIV, /**< division operator (/) */ JERRY_BIN_OP_REM, /**< remainder operator (%) */ } jerry_binary_op_t; /** * Backtrace related types. */ /** * List of backtrace frame types returned by jerry_frame_type. */ typedef enum { JERRY_BACKTRACE_FRAME_JS, /**< indicates that the frame is created for a JavaScript function/method */ } jerry_frame_type_t; /** * Location info retrieved by jerry_frame_location. */ typedef struct { jerry_value_t source_name; /**< source name */ jerry_size_t line; /**< line index */ jerry_size_t column; /**< column index */ } jerry_frame_location_t; /* * Internal data structure for jerry_frame_t definition. */ struct jerry_frame_internal_t; /** * Backtrace frame data passed to the jerry_backtrace_cb_t handler. */ typedef struct jerry_frame_internal_t jerry_frame_t; /** * Callback function which is called by jerry_backtrace for each stack frame. */ typedef bool (*jerry_backtrace_cb_t) (jerry_frame_t *frame_p, void *user_p); /** * Detailed value type related types. */ /** * JerryScript API value type information. */ typedef enum { JERRY_TYPE_NONE = 0u, /**< no type information */ JERRY_TYPE_UNDEFINED, /**< undefined type */ JERRY_TYPE_NULL, /**< null type */ JERRY_TYPE_BOOLEAN, /**< boolean type */ JERRY_TYPE_NUMBER, /**< number type */ JERRY_TYPE_STRING, /**< string type */ JERRY_TYPE_OBJECT, /**< object type */ JERRY_TYPE_FUNCTION, /**< function type */ JERRY_TYPE_EXCEPTION, /**< exception/abort type */ JERRY_TYPE_SYMBOL, /**< symbol type */ JERRY_TYPE_BIGINT, /**< bigint type */ } jerry_type_t; /** * JerryScript object type information. */ typedef enum { JERRY_OBJECT_TYPE_NONE = 0u, /**< Non object type */ JERRY_OBJECT_TYPE_GENERIC, /**< Generic JavaScript object without any internal property */ JERRY_OBJECT_TYPE_MODULE_NAMESPACE, /**< Namespace object */ JERRY_OBJECT_TYPE_ARRAY, /**< Array object */ JERRY_OBJECT_TYPE_PROXY, /**< Proxy object */ JERRY_OBJECT_TYPE_SCRIPT, /**< Script object (see jerry_parse) */ JERRY_OBJECT_TYPE_MODULE, /**< Module object (see jerry_parse) */ JERRY_OBJECT_TYPE_PROMISE, /**< Promise object */ JERRY_OBJECT_TYPE_DATAVIEW, /**< Dataview object */ JERRY_OBJECT_TYPE_FUNCTION, /**< Function object (see jerry_function_type) */ JERRY_OBJECT_TYPE_TYPEDARRAY, /**< %TypedArray% object (see jerry_typedarray_type) */ JERRY_OBJECT_TYPE_ITERATOR, /**< Iterator object (see jerry_iterator_type) */ JERRY_OBJECT_TYPE_CONTAINER, /**< Container object (see jerry_container_get_type) */ JERRY_OBJECT_TYPE_ERROR, /**< Error object */ JERRY_OBJECT_TYPE_ARRAYBUFFER, /**< Array buffer object */ JERRY_OBJECT_TYPE_SHARED_ARRAY_BUFFER, /**< Shared Array Buffer object */ JERRY_OBJECT_TYPE_ARGUMENTS, /**< Arguments object */ JERRY_OBJECT_TYPE_BOOLEAN, /**< Boolean object */ JERRY_OBJECT_TYPE_DATE, /**< Date object */ JERRY_OBJECT_TYPE_NUMBER, /**< Number object */ JERRY_OBJECT_TYPE_REGEXP, /**< RegExp object */ JERRY_OBJECT_TYPE_STRING, /**< String object */ JERRY_OBJECT_TYPE_SYMBOL, /**< Symbol object */ JERRY_OBJECT_TYPE_GENERATOR, /**< Generator object */ JERRY_OBJECT_TYPE_BIGINT, /**< BigInt object */ JERRY_OBJECT_TYPE_WEAKREF, /**< WeakRef object */ } jerry_object_type_t; /** * JerryScript function object type information. */ typedef enum { JERRY_FUNCTION_TYPE_NONE = 0u, /**< Non function type */ JERRY_FUNCTION_TYPE_GENERIC, /**< Generic JavaScript function */ JERRY_FUNCTION_TYPE_ACCESSOR, /**< Accessor function */ JERRY_FUNCTION_TYPE_BOUND, /**< Bound function */ JERRY_FUNCTION_TYPE_ARROW, /**< Arrow function */ JERRY_FUNCTION_TYPE_GENERATOR, /**< Generator function */ } jerry_function_type_t; /** * JerryScript iterator object type information. */ typedef enum { JERRY_ITERATOR_TYPE_NONE = 0u, /**< Non iterator type */ JERRY_ITERATOR_TYPE_ARRAY, /**< Array iterator */ JERRY_ITERATOR_TYPE_STRING, /**< String iterator */ JERRY_ITERATOR_TYPE_MAP, /**< Map iterator */ JERRY_ITERATOR_TYPE_SET, /**< Set iterator */ } jerry_iterator_type_t; /** * Module related types. */ /** * An enum representing the current status of a module */ typedef enum { JERRY_MODULE_STATE_INVALID = 0, /**< return value for jerry_module_state when its argument is not a module */ JERRY_MODULE_STATE_UNLINKED = 1, /**< module is currently unlinked */ JERRY_MODULE_STATE_LINKING = 2, /**< module is currently being linked */ JERRY_MODULE_STATE_LINKED = 3, /**< module has been linked (its dependencies has been resolved) */ JERRY_MODULE_STATE_EVALUATING = 4, /**< module is currently being evaluated */ JERRY_MODULE_STATE_EVALUATED = 5, /**< module has been evaluated (its source code has been executed) */ JERRY_MODULE_STATE_ERROR = 6, /**< an error has been encountered before the evaluated state is reached */ } jerry_module_state_t; /** * Callback which is called by jerry_module_link to get the referenced module. */ typedef jerry_value_t (*jerry_module_resolve_cb_t) (const jerry_value_t specifier, const jerry_value_t referrer, void *user_p); /** * Callback which is called when an import is resolved dynamically to get the referenced module. */ typedef jerry_value_t (*jerry_module_import_cb_t) (const jerry_value_t specifier, const jerry_value_t user_value, void *user_p); /** * Callback which is called after the module enters into linked, evaluated or error state. */ typedef void (*jerry_module_state_changed_cb_t) (jerry_module_state_t new_state, const jerry_value_t module, const jerry_value_t value, void *user_p); /** * Callback which is called when an import.meta expression of a module is evaluated the first time. */ typedef void (*jerry_module_import_meta_cb_t) (const jerry_value_t module, const jerry_value_t meta_object, void *user_p); /** * Callback which is called by jerry_module_evaluate to evaluate the native module. */ typedef jerry_value_t (*jerry_native_module_evaluate_cb_t) (const jerry_value_t native_module); /** * Proxy related types. */ /** * JerryScript special Proxy object options. */ typedef enum { JERRY_PROXY_SKIP_RESULT_VALIDATION = (1u << 0), /**< skip result validation for [[GetPrototypeOf]], * [[SetPrototypeOf]], [[IsExtensible]], * [[PreventExtensions]], [[GetOwnProperty]], * [[DefineOwnProperty]], [[HasProperty]], [[Get]], * [[Set]], [[Delete]] and [[OwnPropertyKeys]] */ } jerry_proxy_custom_behavior_t; /** * Promise related types. */ /** * Enum values representing various Promise states. */ typedef enum { JERRY_PROMISE_STATE_NONE = 0u, /**< Invalid/Unknown state (possibly called on a non-promise object). */ JERRY_PROMISE_STATE_PENDING, /**< Promise is in "Pending" state. */ JERRY_PROMISE_STATE_FULFILLED, /**< Promise is in "Fulfilled" state. */ JERRY_PROMISE_STATE_REJECTED, /**< Promise is in "Rejected" state. */ } jerry_promise_state_t; /** * Event types for jerry_promise_event_cb_t callback function. * The description of the 'object' and 'value' arguments are provided for each type. */ typedef enum { JERRY_PROMISE_EVENT_CREATE = 0u, /**< a new Promise object is created * object: the new Promise object * value: parent Promise for `then` chains, undefined otherwise */ JERRY_PROMISE_EVENT_RESOLVE, /**< called when a Promise is about to be resolved * object: the Promise object * value: value for resolving */ JERRY_PROMISE_EVENT_REJECT, /**< called when a Promise is about to be rejected * object: the Promise object * value: value for rejecting */ JERRY_PROMISE_EVENT_RESOLVE_FULFILLED, /**< called when a resolve is called on a fulfilled Promise * object: the Promise object * value: value for resolving */ JERRY_PROMISE_EVENT_REJECT_FULFILLED, /**< called when a reject is called on a fulfilled Promise * object: the Promise object * value: value for rejecting */ JERRY_PROMISE_EVENT_REJECT_WITHOUT_HANDLER, /**< called when a Promise is rejected without a handler * object: the Promise object * value: value for rejecting */ JERRY_PROMISE_EVENT_CATCH_HANDLER_ADDED, /**< called when a catch handler is added to a rejected * Promise which did not have a catch handler before * object: the Promise object * value: undefined */ JERRY_PROMISE_EVENT_BEFORE_REACTION_JOB, /**< called before executing a Promise reaction job * object: the Promise object * value: undefined */ JERRY_PROMISE_EVENT_AFTER_REACTION_JOB, /**< called after a Promise reaction job is completed * object: the Promise object * value: undefined */ JERRY_PROMISE_EVENT_ASYNC_AWAIT, /**< called when an async function awaits the result of a Promise object * object: internal object representing the execution status * value: the Promise object */ JERRY_PROMISE_EVENT_ASYNC_BEFORE_RESOLVE, /**< called when an async function is continued with resolve * object: internal object representing the execution status * value: value for resolving */ JERRY_PROMISE_EVENT_ASYNC_BEFORE_REJECT, /**< called when an async function is continued with reject * object: internal object representing the execution status * value: value for rejecting */ JERRY_PROMISE_EVENT_ASYNC_AFTER_RESOLVE, /**< called when an async function resolve is completed * object: internal object representing the execution status * value: value for resolving */ JERRY_PROMISE_EVENT_ASYNC_AFTER_REJECT, /**< called when an async function reject is completed * object: internal object representing the execution status * value: value for rejecting */ } jerry_promise_event_type_t; /** * Filter types for jerry_promise_on_event callback function. * The callback is only called for those events which are enabled by the filters. */ typedef enum { JERRY_PROMISE_EVENT_FILTER_DISABLE = 0, /**< disable reporting of all events */ JERRY_PROMISE_EVENT_FILTER_CREATE = (1 << 0), /**< enables the following event: * JERRY_PROMISE_EVENT_CREATE */ JERRY_PROMISE_EVENT_FILTER_RESOLVE = (1 << 1), /**< enables the following event: * JERRY_PROMISE_EVENT_RESOLVE */ JERRY_PROMISE_EVENT_FILTER_REJECT = (1 << 2), /**< enables the following event: * JERRY_PROMISE_EVENT_REJECT */ JERRY_PROMISE_EVENT_FILTER_ERROR = (1 << 3), /**< enables the following events: * JERRY_PROMISE_EVENT_RESOLVE_FULFILLED * JERRY_PROMISE_EVENT_REJECT_FULFILLED * JERRY_PROMISE_EVENT_REJECT_WITHOUT_HANDLER * JERRY_PROMISE_EVENT_CATCH_HANDLER_ADDED */ JERRY_PROMISE_EVENT_FILTER_REACTION_JOB = (1 << 4), /**< enables the following events: * JERRY_PROMISE_EVENT_BEFORE_REACTION_JOB * JERRY_PROMISE_EVENT_AFTER_REACTION_JOB */ JERRY_PROMISE_EVENT_FILTER_ASYNC_MAIN = (1 << 5), /**< enables the following event: * JERRY_PROMISE_EVENT_ASYNC_AWAIT */ JERRY_PROMISE_EVENT_FILTER_ASYNC_REACTION_JOB = (1 << 6), /**< enables the following events: * JERRY_PROMISE_EVENT_ASYNC_BEFORE_RESOLVE * JERRY_PROMISE_EVENT_ASYNC_BEFORE_REJECT * JERRY_PROMISE_EVENT_ASYNC_AFTER_RESOLVE * JERRY_PROMISE_EVENT_ASYNC_AFTER_REJECT */ } jerry_promise_event_filter_t; /** * Notification callback for tracking Promise and async function operations. */ typedef void (*jerry_promise_event_cb_t) (jerry_promise_event_type_t event_type, const jerry_value_t object, const jerry_value_t value, void *user_p); /** * Symbol related types. */ /** * List of well-known symbols. */ typedef enum { JERRY_SYMBOL_ASYNC_ITERATOR, /**< @@asyncIterator well-known symbol */ JERRY_SYMBOL_HAS_INSTANCE, /**< @@hasInstance well-known symbol */ JERRY_SYMBOL_IS_CONCAT_SPREADABLE, /**< @@isConcatSpreadable well-known symbol */ JERRY_SYMBOL_ITERATOR, /**< @@iterator well-known symbol */ JERRY_SYMBOL_MATCH, /**< @@match well-known symbol */ JERRY_SYMBOL_REPLACE, /**< @@replace well-known symbol */ JERRY_SYMBOL_SEARCH, /**< @@search well-known symbol */ JERRY_SYMBOL_SPECIES, /**< @@species well-known symbol */ JERRY_SYMBOL_SPLIT, /**< @@split well-known symbol */ JERRY_SYMBOL_TO_PRIMITIVE, /**< @@toPrimitive well-known symbol */ JERRY_SYMBOL_TO_STRING_TAG, /**< @@toStringTag well-known symbol */ JERRY_SYMBOL_UNSCOPABLES, /**< @@unscopables well-known symbol */ JERRY_SYMBOL_MATCH_ALL, /**< @@matchAll well-known symbol */ } jerry_well_known_symbol_t; /** * TypedArray related types. */ /** * TypedArray types. */ typedef enum { JERRY_TYPEDARRAY_INVALID = 0, JERRY_TYPEDARRAY_UINT8, JERRY_TYPEDARRAY_UINT8CLAMPED, JERRY_TYPEDARRAY_INT8, JERRY_TYPEDARRAY_UINT16, JERRY_TYPEDARRAY_INT16, JERRY_TYPEDARRAY_UINT32, JERRY_TYPEDARRAY_INT32, JERRY_TYPEDARRAY_FLOAT32, JERRY_TYPEDARRAY_FLOAT64, JERRY_TYPEDARRAY_BIGINT64, JERRY_TYPEDARRAY_BIGUINT64, } jerry_typedarray_type_t; /** * Container types. */ typedef enum { JERRY_CONTAINER_TYPE_INVALID = 0, /**< Invalid container */ JERRY_CONTAINER_TYPE_MAP, /**< Map type */ JERRY_CONTAINER_TYPE_SET, /**< Set type */ JERRY_CONTAINER_TYPE_WEAKMAP, /**< WeakMap type */ JERRY_CONTAINER_TYPE_WEAKSET, /**< WeakSet type */ } jerry_container_type_t; /** * Container operations */ typedef enum { JERRY_CONTAINER_OP_ADD, /**< Set/WeakSet add operation */ JERRY_CONTAINER_OP_GET, /**< Map/WeakMap get operation */ JERRY_CONTAINER_OP_SET, /**< Map/WeakMap set operation */ JERRY_CONTAINER_OP_HAS, /**< Set/WeakSet/Map/WeakMap has operation */ JERRY_CONTAINER_OP_DELETE, /**< Set/WeakSet/Map/WeakMap delete operation */ JERRY_CONTAINER_OP_SIZE, /**< Set/WeakSet/Map/WeakMap size operation */ JERRY_CONTAINER_OP_CLEAR, /**< Set/Map clear operation */ } jerry_container_op_t; /** * Miscellaneous types. */ /** * Enabled fields of jerry_source_info_t. */ typedef enum { JERRY_SOURCE_INFO_HAS_SOURCE_CODE = (1 << 0), /**< source_code field is valid */ JERRY_SOURCE_INFO_HAS_FUNCTION_ARGUMENTS = (1 << 1), /**< function_arguments field is valid */ JERRY_SOURCE_INFO_HAS_SOURCE_RANGE = (1 << 2), /**< both source_range_start and source_range_length * fields are valid */ } jerry_source_info_enabled_fields_t; /** * Source related information of a script/module/function. */ typedef struct { uint32_t enabled_fields; /**< combination of jerry_source_info_enabled_fields_t values */ jerry_value_t source_code; /**< script source code or function body */ jerry_value_t function_arguments; /**< function arguments */ uint32_t source_range_start; /**< start position of the function in the source code */ uint32_t source_range_length; /**< source length of the function in the source code */ } jerry_source_info_t; /** * Array buffer types. */ /** * Type of an array buffer. */ typedef enum { JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER, /**< the object is an array buffer object */ JERRY_ARRAYBUFFER_TYPE_SHARED_ARRAYBUFFER, /**< the object is a shared array buffer object */ } jerry_arraybuffer_type_t; /** * Callback for allocating the backing store of array buffer or shared array buffer objects. */ typedef uint8_t *(*jerry_arraybuffer_allocate_cb_t) (jerry_arraybuffer_type_t buffer_type, uint32_t buffer_size, void **arraybuffer_user_p, void *user_p); /** * Callback for freeing the backing store of array buffer or shared array buffer objects. */ typedef void (*jerry_arraybuffer_free_cb_t) (jerry_arraybuffer_type_t buffer_type, uint8_t *buffer_p, uint32_t buffer_size, void *arraybuffer_user_p, void *user_p); /** * @} */ JERRY_C_API_END #endif /* !JERRYSCRIPT_TYPES_H */ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jrt/jrt.h000664 001750 001750 00000020011 15164251010 041060 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JRT_H #define JRT_H #include #include #include "jerryscript-core.h" #include "jerryscript-port.h" #include "jrt-types.h" #define JERRY_NDEBUG /* * Constants */ #define JERRY_BITSINBYTE 8 /* * Make sure unused parameters, variables, or expressions trigger no compiler warning. */ #define JERRY_UNUSED(x) ((void) (x)) #define JERRY_UNUSED_1(_1) JERRY_UNUSED (_1) #define JERRY_UNUSED_2(_1, _2) JERRY_UNUSED (_1), JERRY_UNUSED_1 (_2) #define JERRY_UNUSED_3(_1, _2, _3) JERRY_UNUSED (_1), JERRY_UNUSED_2 (_2, _3) #define JERRY_UNUSED_4(_1, _2, _3, _4) JERRY_UNUSED (_1), JERRY_UNUSED_3 (_2, _3, _4) #define JERRY_UNUSED_5(_1, _2, _3, _4, _5) JERRY_UNUSED (_1), JERRY_UNUSED_4 (_2, _3, _4, _5) #define JERRY_UNUSED_6(_1, _2, _3, _4, _5, _6) JERRY_UNUSED (_1), JERRY_UNUSED_5 (_2, _3, _4, _5, _6) #define JERRY_UNUSED_7(_1, _2, _3, _4, _5, _6, _7) JERRY_UNUSED (_1), JERRY_UNUSED_6 (_2, _3, _4, _5, _6, _7) #define JERRY_UNUSED_8(_1, _2, _3, _4, _5, _6, _7, _8) JERRY_UNUSED (_1), JERRY_UNUSED_7 (_2, _3, _4, _5, _6, _7, _8) #define JERRY_VA_ARGS_NUM_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define JERRY_VA_ARGS_NUM(...) JERRY_VA_ARGS_NUM_IMPL (__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define JERRY_UNUSED_ALL_IMPL_(nargs) JERRY_UNUSED_##nargs #define JERRY_UNUSED_ALL_IMPL(nargs) JERRY_UNUSED_ALL_IMPL_ (nargs) #define JERRY_UNUSED_ALL(...) JERRY_UNUSED_ALL_IMPL (JERRY_VA_ARGS_NUM (__VA_ARGS__)) (__VA_ARGS__) /* * Asserts * * Warning: * Don't use JERRY_STATIC_ASSERT in headers, because * __LINE__ may be the same for asserts in a header * and in an implementation file. */ #define JERRY_STATIC_ASSERT_GLUE_(a, b, c) a##b##_##c #define JERRY_STATIC_ASSERT_GLUE(a, b, c) JERRY_STATIC_ASSERT_GLUE_ (a, b, c) #define JERRY_STATIC_ASSERT(x, msg) \ enum \ { \ JERRY_STATIC_ASSERT_GLUE (static_assertion_failed_, __LINE__, msg) = (!!x) \ } #ifndef JERRY_NDEBUG void JERRY_ATTR_NORETURN jerry_assert_fail (const char *assertion, const char *file, const char *function, const uint32_t line); void JERRY_ATTR_NORETURN jerry_unreachable (const char *file, const char *function, const uint32_t line); #define JERRY_ASSERT(x) \ do \ { \ if (JERRY_UNLIKELY (!(x))) \ { \ jerry_assert_fail (#x, __FILE__, __func__, __LINE__); \ } \ } while (0) #define JERRY_UNREACHABLE() \ do \ { \ jerry_unreachable (__FILE__, __func__, __LINE__); \ } while (0) #else /* JERRY_NDEBUG */ #define JERRY_ASSERT(x) \ do \ { \ } while (0) #if defined(__GNUC__) || defined(__clang__) #define JERRY_UNREACHABLE() __builtin_unreachable () #elif defined(_MSC_VER) #define JERRY_UNREACHABLE() _assume (0) #else #ifndef JERRY_UNREACHABLE #define JERRY_UNREACHABLE() #endif #endif /* !JERRY_UNREACHABLE */ #endif /* !JERRY_NDEBUG */ /** * Exit on fatal error */ void JERRY_ATTR_NORETURN jerry_fatal (jerry_fatal_code_t code); jerry_log_level_t jerry_jrt_get_log_level (void); void jerry_jrt_set_log_level (jerry_log_level_t level); /* * Logging */ #define JERRY_ERROR_MSG(...) \ do \ { \ if (false) \ { \ JERRY_UNUSED_ALL (__VA_ARGS__); \ } \ } while (0) #define JERRY_WARNING_MSG(...) \ do \ { \ if (false) \ { \ JERRY_UNUSED_ALL (__VA_ARGS__); \ } \ } while (0) #define JERRY_DEBUG_MSG(...) \ do \ { \ if (false) \ { \ JERRY_UNUSED_ALL (__VA_ARGS__); \ } \ } while (0) #define JERRY_TRACE_MSG(...) \ do \ { \ if (false) \ { \ JERRY_UNUSED_ALL (__VA_ARGS__); \ } \ } while (0) /** * Size of struct member */ #define JERRY_SIZE_OF_STRUCT_MEMBER(struct_name, member_name) sizeof (((struct_name *) NULL)->member_name) /** * Aligns @a value to @a alignment. @a must be the power of 2. * * Returns minimum positive value, that divides @a alignment and is more than or equal to @a value */ #define JERRY_ALIGNUP(value, alignment) (((value) + ((alignment) -1)) & ~((alignment) -1)) /** * Align value down */ #define JERRY_ALIGNDOWN(value, alignment) ((value) & ~((alignment) -1)) /* * min, max */ #define JERRY_MIN(v1, v2) (((v1) < (v2)) ? (v1) : (v2)) #define JERRY_MAX(v1, v2) (((v1) < (v2)) ? (v2) : (v1)) /** * Calculate the index of the first non-zero bit of a 32 bit integer value */ #define JERRY__LOG2_1(n) (((n) >= 2) ? 1 : 0) #define JERRY__LOG2_2(n) (((n) >= 1 << 2) ? (2 + JERRY__LOG2_1 ((n) >> 2)) : JERRY__LOG2_1 (n)) #define JERRY__LOG2_4(n) (((n) >= 1 << 4) ? (4 + JERRY__LOG2_2 ((n) >> 4)) : JERRY__LOG2_2 (n)) #define JERRY__LOG2_8(n) (((n) >= 1 << 8) ? (8 + JERRY__LOG2_4 ((n) >> 8)) : JERRY__LOG2_4 (n)) #define JERRY_LOG2(n) (((n) >= 1 << 16) ? (16 + JERRY__LOG2_8 ((n) >> 16)) : JERRY__LOG2_8 (n)) /** * JERRY_BLOCK_TAIL_CALL_OPTIMIZATION * * Adapted from abseil ( https://github.com/abseil/ ) * * Instructs the compiler to avoid optimizing tail-call recursion. This macro is * useful when you wish to preserve the existing function order within a stack * trace for logging, debugging, or profiling purposes. * * Example: * * int f() { * int result = g(); * JERRY_BLOCK_TAIL_CALL_OPTIMIZATION(); * return result; * } * * This macro is intentionally here as jerryscript-compiler.h is a public header and * it does not make sense to expose this macro to the public. */ #if defined(__clang__) || defined(__GNUC__) /* Clang/GCC will not tail call given inline volatile assembly. */ #define JERRY_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__ ("") #else /* !defined(__clang__) && !defined(__GNUC__) */ /* On GCC 10.x this version also works. */ #define JERRY_BLOCK_TAIL_CALL_OPTIMIZATION() \ do \ { \ JERRY_CONTEXT (status_flags) |= ECMA_STATUS_API_ENABLED; \ } while (0) #endif /* defined(__clang__) || defined (__GNUC__) */ #endif /* !JRT_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieData.h000664 001750 001750 00000006053 15164251010 035606 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_COMMON_ #define _TVG_LOTTIE_COMMON_ #include #include "tvgCommon.h" #include "tvgArray.h" struct PathSet { Point* pts = nullptr; PathCommand* cmds = nullptr; uint16_t ptsCnt = 0; uint16_t cmdsCnt = 0; }; struct RGB32 { int32_t r, g, b; }; struct ColorStop { Fill::ColorStop* data = nullptr; Array* input = nullptr; void copy(const ColorStop& rhs, uint32_t cnt) { if (rhs.data) { data = tvg::malloc(sizeof(Fill::ColorStop) * cnt); memcpy(data, rhs.data, sizeof(Fill::ColorStop) * cnt); } if (rhs.input) TVGERR("LOTTIE", "Must be populated!"); } }; struct TextDocument { char* text = nullptr; float height; float shift; RGB32 color; struct { Point pos; Point size{}; } bbox; struct { RGB32 color; float width; bool below = false; } stroke; char* name = nullptr; float size; float tracking = 0.0f; float justify = 0.0f; //horizontal alignment uint8_t caps = 0; //0: Regular, 1: AllCaps, 2: SmallCaps void copy(const TextDocument& rhs) { text = duplicate(rhs.text); name = duplicate(rhs.name); } }; struct Tween { float frameNo = 0.0f; float progress = 0.0f; //greater than 0 and smaller than 1 bool active = false; }; static inline int32_t REMAP255(float val) { return (int32_t)nearbyintf(val * 255.0f); } static inline RGB32 operator-(const RGB32& lhs, const RGB32& rhs) { return {lhs.r - rhs.r, lhs.g - rhs.g, lhs.b - rhs.b}; } static inline RGB32 operator+(const RGB32& lhs, const RGB32& rhs) { return {lhs.r + rhs.r, lhs.g + rhs.g, lhs.b + rhs.b}; } static inline RGB32 operator*(const RGB32& lhs, float rhs) { return {(int32_t)nearbyintf(lhs.r * rhs), (int32_t)nearbyintf(lhs.g * rhs), (int32_t)nearbyintf(lhs.b * rhs)}; } #endif //_TVG_LOTTIE_COMMON_ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/opcodes-ecma-bitwise.cpp000664 001750 001750 00000013157 15164251010 044457 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-bigint.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "opcodes.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup vm_opcodes Opcodes * @{ */ /** * Perform ECMA number logic operation. * * The algorithm of the operation is following: * leftNum = ToNumber (leftValue); * rightNum = ToNumber (rightValue); * result = leftNum BitwiseLogicOp rightNum; * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t do_number_bitwise_logic (number_bitwise_logic_op op, /**< number bitwise logic operation */ ecma_value_t left_value, /**< left value */ ecma_value_t right_value) /**< right value */ { JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (left_value) && !ECMA_IS_VALUE_ERROR (right_value)); ecma_number_t left_number; left_value = ecma_op_to_numeric (left_value, &left_number, ECMA_TO_NUMERIC_ALLOW_BIGINT); if (ECMA_IS_VALUE_ERROR (left_value)) { return left_value; } ecma_value_t ret_value = ECMA_VALUE_EMPTY; #if JERRY_BUILTIN_BIGINT if (JERRY_LIKELY (!ecma_is_value_bigint (left_value))) { #endif /* JERRY_BUILTIN_BIGINT */ ecma_number_t right_number; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (right_value, &right_number))) { return ECMA_VALUE_ERROR; } ecma_number_t result = ECMA_NUMBER_ZERO; uint32_t right_uint32 = ecma_number_to_uint32 (right_number); switch (op) { case NUMBER_BITWISE_LOGIC_AND: { uint32_t left_uint32 = ecma_number_to_uint32 (left_number); result = (ecma_number_t) ((int32_t) (left_uint32 & right_uint32)); break; } case NUMBER_BITWISE_LOGIC_OR: { uint32_t left_uint32 = ecma_number_to_uint32 (left_number); result = (ecma_number_t) ((int32_t) (left_uint32 | right_uint32)); break; } case NUMBER_BITWISE_LOGIC_XOR: { uint32_t left_uint32 = ecma_number_to_uint32 (left_number); result = (ecma_number_t) ((int32_t) (left_uint32 ^ right_uint32)); break; } case NUMBER_BITWISE_SHIFT_LEFT: { result = (ecma_number_t) ((int32_t) ((uint32_t) ecma_number_to_int32 (left_number) << (right_uint32 & 0x1F))); break; } case NUMBER_BITWISE_SHIFT_RIGHT: { result = (ecma_number_t) (ecma_number_to_int32 (left_number) >> (right_uint32 & 0x1F)); break; } default: { JERRY_ASSERT (op == NUMBER_BITWISE_SHIFT_URIGHT); uint32_t left_uint32 = ecma_number_to_uint32 (left_number); result = (ecma_number_t) (left_uint32 >> (right_uint32 & 0x1F)); break; } } ret_value = ecma_make_number_value (result); #if JERRY_BUILTIN_BIGINT } else { bool free_right_value; right_value = ecma_bigint_get_bigint (right_value, &free_right_value); if (ECMA_IS_VALUE_ERROR (right_value)) { ecma_free_value (left_value); return right_value; } switch (op) { case NUMBER_BITWISE_LOGIC_AND: { ret_value = ecma_bigint_and (left_value, right_value); break; } case NUMBER_BITWISE_LOGIC_OR: { ret_value = ecma_bigint_or (left_value, right_value); break; } case NUMBER_BITWISE_LOGIC_XOR: { ret_value = ecma_bigint_xor (left_value, right_value); break; } case NUMBER_BITWISE_SHIFT_LEFT: { ret_value = ecma_bigint_shift (left_value, right_value, true); break; } case NUMBER_BITWISE_SHIFT_RIGHT: { ret_value = ecma_bigint_shift (left_value, right_value, false); break; } default: { JERRY_ASSERT (op == NUMBER_BITWISE_SHIFT_URIGHT); ret_value = ecma_raise_type_error (ECMA_ERR_UNSIGNED_RIGHT_SHIFT_IS_NOT_ALLOWED_FOR_BIGINTS); break; } } ecma_free_value (left_value); if (free_right_value) { ecma_free_value (right_value); } } #endif /* JERRY_BUILTIN_BIGINT */ return ret_value; } /* do_number_bitwise_logic */ /** * Perform ECMA number bitwise not operation. * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t do_number_bitwise_not (ecma_value_t value) /**< value */ { JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (value)); ecma_number_t number; value = ecma_op_to_numeric (value, &number, ECMA_TO_NUMERIC_ALLOW_BIGINT); if (ECMA_IS_VALUE_ERROR (value)) { return value; } #if JERRY_BUILTIN_BIGINT if (JERRY_LIKELY (!ecma_is_value_bigint (value))) { #endif /* JERRY_BUILTIN_BIGINT */ return ecma_make_number_value ((ecma_number_t) ((int32_t) ~ecma_number_to_uint32 (number))); #if JERRY_BUILTIN_BIGINT } ecma_value_t ret_value = ecma_bigint_unary (value, ECMA_BIGINT_UNARY_BITWISE_NOT); ecma_free_value (value); return ret_value; #endif /* JERRY_BUILTIN_BIGINT */ } /* do_number_bitwise_not */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/cross/android_x86_64.txt000664 001750 001750 00000001266 15164251010 033552 0ustar00ddennedyddennedy000000 000000 # Android developer (Use the NDK with other build systems) # https://developer.android.com/ndk/guides/other_build_systems [binaries] cpp = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/x86_64-linux-androidAPI-clang++' ar = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/llvm-ar' as = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/llvm-as' ranlib = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/llvm-ranlib' ld = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/ld' strip = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/llvm-strip' [properties] sys_root = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/sysroot' [host_machine] system = 'android' cpu_family = 'x86_64' cpu = 'x86_64' endian = 'little'mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgText.h000664 001750 001750 00000014600 15164251010 033431 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_TEXT_H #define _TVG_TEXT_H #include "tvgStr.h" #include "tvgMath.h" #include "tvgShape.h" #include "tvgFill.h" #include "tvgLoader.h" namespace tvg { struct TextImpl : Text { Paint::Impl impl; Shape* shape; //text shape FontLoader* loader = nullptr; FontMetrics fm; char* utf8 = nullptr; float outlineWidth = 0.0f; float italicShear = 0.0f; bool updated = false; TextImpl() : impl(Paint::Impl(this)), shape(Shape::gen()) { PAINT(shape)->parent = this; shape->strokeJoin(StrokeJoin::Round); } ~TextImpl() { tvg::free(utf8); if (loader) { loader->release(fm); LoaderMgr::retrieve(loader); } Paint::rel(shape); } Result text(const char* utf8) { tvg::free(this->utf8); if (utf8) this->utf8 = tvg::duplicate(utf8); else this->utf8 = nullptr; updated = true; impl.mark(RenderUpdateFlag::Path); return Result::Success; } Result font(const char* name) { auto loader = static_cast(name ? LoaderMgr::font(name) : LoaderMgr::anyfont()); if (!loader) return Result::InsufficientCondition; //Same resource has been loaded. if (this->loader == loader) { this->loader->sharing--; //make it sure the reference counting. return Result::Success; } else if (this->loader) { this->loader->release(fm); LoaderMgr::retrieve(this->loader); } this->loader = loader; updated = true; return Result::Success; } Result size(float fontSize) { if (fontSize > 0.0f) { if (fm.fontSize != fontSize) { fm.fontSize = fontSize; updated = true; } return Result::Success; } return Result::InvalidArguments; } RenderRegion bounds() { if (!load()) return {}; return to(shape)->bounds(); } bool render(RenderMethod* renderer) { if (!loader || !fm.engine) return true; renderer->blend(impl.blendMethod); return PAINT(shape)->render(renderer); } bool load() { if (!loader) return false; if (updated) { if (loader->get(fm, utf8, to(shape)->rs.path)) { loader->transform(shape, fm, italicShear); } updated = false; } return true; } Result metrics(TextMetrics& metrics) { if (!loader || fm.fontSize <= 0.0f) return Result::InsufficientCondition; loader->metrics(fm, metrics); return Result::Success; } bool skip(RenderUpdateFlag flag) { if (flag == RenderUpdateFlag::None) return true; return false; } void wrapping(TextWrap mode) { if (fm.wrap == mode) return; fm.wrap = mode; updated = true; impl.mark(RenderUpdateFlag::Path); } void layout(float w, float h) { fm.box = {w, h}; updated = true; } Result spacing(float letter, float line) { if (letter < 0.0f || line < 0.0f) return Result::InvalidArguments; fm.spacing = {letter, line}; updated = true; return Result::Success; } bool update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) { if (!load()) return true; auto scale = fm.scale; //transform the gradient coordinates based on the final scaled font. auto fill = to(shape)->rs.fill; if (fill && to(shape)->impl.marked(RenderUpdateFlag::Gradient)) { if (fill->type() == Type::LinearGradient) { LINEAR(fill)->p1 *= scale; LINEAR(fill)->p2 *= scale; } else { RADIAL(fill)->center *= scale; RADIAL(fill)->r *= scale; RADIAL(fill)->focal *= scale; RADIAL(fill)->fr *= scale; } } if (outlineWidth > 0.0f && impl.marked(RenderUpdateFlag::Stroke)) shape->strokeWidth(outlineWidth * scale); PAINT(shape)->update(renderer, transform, clips, opacity, flag, false); return true; } bool intersects(const RenderRegion& region) { if (!load()) return false; return to(shape)->intersects(region); } bool bounds(Point* pt4, const Matrix& m, bool obb) { if (!load()) return true; return PAINT(shape)->bounds(pt4, &const_cast(m), obb); } Paint* duplicate(Paint* ret) { if (ret) TVGERR("RENDERER", "TODO: duplicate()"); load(); auto text = Text::gen(); auto dup = to(text); to(shape)->duplicate(dup->shape); if (loader) { dup->loader = loader; ++dup->loader->sharing; loader->copy(fm, dup->fm); } dup->utf8 = tvg::duplicate(utf8); dup->italicShear = italicShear; dup->outlineWidth = outlineWidth; dup->updated = true; return text; } Iterator* iterator() { return nullptr; } }; } #endif //_TVG_TEXT_H src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwRle.cpp000664 001750 001750 00000074172 15164251010 036005 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * The FreeType Project LICENSE * ---------------------------- * 2006-Jan-27 * Copyright 1996-2002, 2006 by * David Turner, Robert Wilhelm, and Werner Lemberg * Introduction * ============ * The FreeType Project is distributed in several archive packages; * some of them may contain, in addition to the FreeType font engine, * various tools and contributions which rely on, or relate to, the * FreeType Project. * This license applies to all files found in such packages, and * which do not fall under their own explicit license. The license * affects thus the FreeType font engine, the test programs, * documentation and makefiles, at the very least. * This license was inspired by the BSD, Artistic, and IJG * (Independent JPEG Group) licenses, which all encourage inclusion * and use of free software in commercial and freeware products * alike. As a consequence, its main points are that: * o We don't promise that this software works. However, we will be * interested in any kind of bug reports. (`as is' distribution) * o You can use this software for whatever you want, in parts or * full form, without having to pay us. (`royalty-free' usage) * o You may not pretend that you wrote this software. If you use * it, or only parts of it, in a program, you must acknowledge * somewhere in your documentation that you have used the * FreeType code. (`credits') * We specifically permit and encourage the inclusion of this * software, with or without modifications, in commercial products. * We disclaim all warranties covering The FreeType Project and * assume no liability related to The FreeType Project. * Finally, many people asked us for a preferred form for a * credit/disclaimer to use in compliance with this license. We thus * encourage you to use the following text: * """ * Portions of this software are copyright � The FreeType * Project (www.freetype.org). All rights reserved. * """ * Please replace with the value from the FreeType version you * actually use. * Legal Terms * =========== * 0. Definitions * -------------- * Throughout this license, the terms `package', `FreeType Project', * and `FreeType archive' refer to the set of files originally * distributed by the authors (David Turner, Robert Wilhelm, and * Werner Lemberg) as the `FreeType Project', be they named as alpha, * beta or final release. * `You' refers to the licensee, or person using the project, where * `using' is a generic term including compiling the project's source * code as well as linking it to form a `program' or `executable'. * This program is referred to as `a program using the FreeType * engine'. * This license applies to all files distributed in the original * FreeType Project, including all source code, binaries and * documentation, unless otherwise stated in the file in its * original, unmodified form as distributed in the original archive. * If you are unsure whether or not a particular file is covered by * this license, you must contact us to verify this. * The FreeType Project is copyright (C) 1996-2000 by David Turner, * Robert Wilhelm, and Werner Lemberg. All rights reserved except as * specified below. * 1. No Warranty * -------------- * THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO * USE, OF THE FREETYPE PROJECT. * 2. Redistribution * ----------------- * This license grants a worldwide, royalty-free, perpetual and * irrevocable right and license to use, execute, perform, compile, * display, copy, create derivative works of, distribute and * sublicense the FreeType Project (in both source and object code * forms) and derivative works thereof for any purpose; and to * authorize others to exercise some or all of the rights granted * herein, subject to the following conditions: * o Redistribution of source code must retain this license file * (`FTL.TXT') unaltered; any additions, deletions or changes to * the original files must be clearly indicated in accompanying * documentation. The copyright notices of the unaltered, * original files must be preserved in all copies of source * files. * o Redistribution in binary form must provide a disclaimer that * states that the software is based in part of the work of the * FreeType Team, in the distribution documentation. We also * encourage you to put an URL to the FreeType web page in your * documentation, though this isn't mandatory. * These conditions apply to any software derived from or based on * the FreeType Project, not just the unmodified files. If you use * our work, you must acknowledge us. However, no fee need be paid * to us. * 3. Advertising * -------------- * Neither the FreeType authors and contributors nor you shall use * the name of the other for commercial, advertising, or promotional * purposes without specific prior written permission. * We suggest, but do not require, that you use one or more of the * following phrases to refer to this software in your documentation * or advertising materials: `FreeType Project', `FreeType Engine', * `FreeType library', or `FreeType Distribution'. * As you have not signed this license, you are not required to * accept it. However, as the FreeType Project is copyrighted * material, only this license, or another one contracted with the * authors, grants you the right to use, distribute, and modify it. * Therefore, by using, distributing, or modifying the FreeType * Project, you indicate that you understand and accept all the terms * of this license. * 4. Contacts * ----------- * There are two mailing lists related to FreeType: * o freetype@nongnu.org * Discusses general use and applications of FreeType, as well as * future and wanted additions to the library and distribution. * If you are looking for support, start in this list if you * haven't found anything to help you in the documentation. * o freetype-devel@nongnu.org * Discusses bugs, as well as engine internals, design issues, * specific licenses, porting, etc. * Our home page can be found at * http://www.freetype.org */ #include #include "tvgSwCommon.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ constexpr auto PIXEL_BITS = 8; //must be at least 6 bits! constexpr auto ONE_PIXEL = (1 << PIXEL_BITS); struct Band { int32_t min, max; }; struct RleWorker { SwRle* rle; SwPoint cellPos; SwPoint cellMin; SwPoint cellMax; int32_t cellXCnt; int32_t cellYCnt; Area area; int32_t cover; SwCell* cells; ptrdiff_t maxCells; ptrdiff_t cellsCnt; SwPoint pos; SwPoint bezStack[32 * 3 + 1]; SwPoint lineStack[32 + 1]; int levStack[32]; SwOutline* outline; int bandSize; int bandShoot; SwCell* buffer; uint32_t bufferSize; SwCell** yCells; int32_t yCnt; bool invalid; bool antiAlias; }; static inline SwPoint UPSCALE(const SwPoint& pt) { return {int32_t(((unsigned long) pt.x) << (PIXEL_BITS - 6)), int32_t(((unsigned long) pt.y) << (PIXEL_BITS - 6))}; } static inline int32_t TRUNC(const int32_t x) { return x >> PIXEL_BITS; } static inline SwPoint TRUNC(const SwPoint& pt) { return {TRUNC(pt.x), TRUNC(pt.y)}; } static inline SwPoint FRACT(const SwPoint& pt) { return {pt.x & (ONE_PIXEL - 1), pt.y & (ONE_PIXEL - 1)}; } // 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. static inline int32_t HYPOT(SwPoint pt) { if (pt.x < 0) pt.x = -pt.x; if (pt.y < 0) pt.y = -pt.y; return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3))); } // Used to prevent integer overflow when calculating the distance between points. // This function uses 64-bit arithmetic to safely compute the difference between coordinates. static inline uint32_t SAFE_HYPOT(SwPoint& pt1, SwPoint& pt2) { auto x = uint32_t(abs(int64_t(pt1.x) - int64_t(pt2.x))); auto y = uint32_t(abs(int64_t(pt1.y) - int64_t(pt2.y))); return (x > y) ? (x + (3 * y >> 3)) : (y + (3 * x >> 3)); } static void _horizLine(RleWorker& rw, int32_t x, int32_t y, int32_t area, int32_t aCount) { x += rw.cellMin.x; y += rw.cellMin.y; //Clip Y range if (y < rw.cellMin.y || y >= rw.cellMax.y) return; /* compute the coverage line's coverage, depending on the outline fill rule */ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ auto coverage = static_cast(area >> (PIXEL_BITS * 2 + 1 - 8)); //range 0 - 255 if (coverage < 0) coverage = -coverage; if (rw.outline->fillRule == FillRule::EvenOdd) { coverage &= 511; if (coverage > 255) coverage = 511 - coverage; } else { //normal non-zero winding rule if (coverage > 255) coverage = 255; } if (coverage == 0) return; //span has ushort coordinates. check limit overflow if (x >= SHRT_MAX || y >= SHRT_MAX) { TVGERR("SW_ENGINE", "XY-coordinate overflow!"); return; } auto rle = rw.rle; if (!rw.antiAlias) coverage = 255; //see whether we can add this span to the current list if (!rle->spans.empty()) { auto& span = rle->spans.last(); if ((span.coverage == coverage) && (span.y == y) && (span.x + span.len == x)) { //Clip x range int32_t xOver = 0; if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x); span.len += (aCount + xOver); return; } } //Clip x range int32_t xOver = 0; if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); if (x < rw.cellMin.x) { xOver -= (rw.cellMin.x - x); x = rw.cellMin.x; } //Nothing to draw if (aCount + xOver <= 0) return; //add a span to the current list rle->spans.next() = {(uint16_t)x, (uint16_t)y, uint16_t(aCount + xOver), (uint8_t)coverage}; } static void _sweep(RleWorker& rw) { if (rw.cellsCnt == 0) return; for (int y = 0; y < rw.yCnt; ++y) { auto cover = 0; auto x = 0; auto cell = rw.yCells[y]; while (cell) { if (cell->x > x && cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), cell->x - x); cover += cell->cover; auto area = cover * (ONE_PIXEL * 2) - cell->area; if (area != 0 && cell->x >= 0) _horizLine(rw, cell->x, y, area, 1); x = cell->x + 1; cell = cell->next; } if (cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), rw.cellXCnt - x); } } static SwCell* _findCell(RleWorker& rw) { auto x = rw.cellPos.x; if (x > rw.cellXCnt) x = rw.cellXCnt; auto pcell = &rw.yCells[rw.cellPos.y]; while(true) { auto cell = *pcell; if (!cell || cell->x > x) break; if (cell->x == x) return cell; pcell = &cell->next; } if (rw.cellsCnt >= rw.maxCells) return nullptr; auto cell = rw.cells + rw.cellsCnt++; cell->x = x; cell->area = 0; cell->cover = 0; cell->next = *pcell; *pcell = cell; return cell; } static bool _recordCell(RleWorker& rw) { if (rw.area | rw.cover) { auto cell = _findCell(rw); if (!cell) return false; cell->area += rw.area; cell->cover += rw.cover; } return true; } static bool _setCell(RleWorker& rw, SwPoint pos) { /* 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. */ pos -= rw.cellMin; //exceptions if (pos.x < 0) pos.x = -1; else if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; //Are we moving to a different cell? if (pos != rw.cellPos) { //Record the current one if it is valid if (!rw.invalid && !_recordCell(rw)) return false; rw.area = rw.cover = 0; rw.cellPos = pos; } rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt); return true; } static bool _startCell(RleWorker& rw, SwPoint pos) { if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x - 1; rw.area = 0; rw.cover = 0; rw.cellPos = pos - rw.cellMin; rw.invalid = false; return _setCell(rw, pos); } static bool _moveTo(RleWorker& rw, const SwPoint& to) { //record current cell, if any */ if (!rw.invalid && !_recordCell(rw)) return false; //start to a new position if (!_startCell(rw, TRUNC(to))) return false; rw.pos = to; return true; } static bool _lineTo(RleWorker& rw, const SwPoint& to) { auto e1 = TRUNC(rw.pos); auto e2 = TRUNC(to); //vertical clipping if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) { rw.pos = to; return true; } auto line = rw.lineStack; line[0] = to; line[1] = rw.pos; while (true) { if (SAFE_HYPOT(line[0], line[1]) > SHRT_MAX) { mathSplitLine(line); ++line; continue; } auto diff = line[0] - line[1]; e1 = TRUNC(line[1]); e2 = TRUNC(line[0]); auto f1 = FRACT(line[1]); SwPoint f2; //inside one cell if (e1 == e2) { ; //any horizontal line } else if (diff.y == 0) { e1.x = e2.x; if (!_setCell(rw, e1)) return false; } else if (diff.x == 0) { //vertical line up if (diff.y > 0) { do { f2.y = ONE_PIXEL; rw.cover += (f2.y - f1.y); rw.area += (f2.y - f1.y) * f1.x * 2; f1.y = 0; ++e1.y; if (!_setCell(rw, e1)) return false; } while(e1.y != e2.y); //vertical line down } else { do { f2.y = 0; rw.cover += (f2.y - f1.y); rw.area += (f2.y - f1.y) * f1.x * 2; f1.y = ONE_PIXEL; --e1.y; if (!_setCell(rw, e1)) return false; } while(e1.y != e2.y); } //any other line } else { #define SW_UDIV(a, b) (int32_t)((uint64_t(a) * uint64_t(b)) >> 32) Area prod = diff.x * f1.y - diff.y * f1.x; /* These macros speed up repetitive divisions by replacing them with multiplications and right shifts. */ auto dxr = (e1.x != e2.x) ? (int64_t)0xffffffff / diff.x : 0; auto dyr = (e1.y != e2.y) ? (int64_t)0xffffffff / diff.y : 0; auto px = diff.x * ONE_PIXEL; auto py = diff.y * ONE_PIXEL; /* 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 { //left if (prod <= 0 && prod - px > 0) { f2 = {0, SW_UDIV(-prod, -dxr)}; prod -= py; rw.cover += (f2.y - f1.y); rw.area += (f2.y - f1.y) * (f1.x + f2.x); f1 = {ONE_PIXEL, f2.y}; --e1.x; //up } else if (prod - px <= 0 && prod - px + py > 0) { prod -= px; f2 = {SW_UDIV(-prod, dyr), ONE_PIXEL}; rw.cover += (f2.y - f1.y); rw.area += (f2.y - f1.y) * (f1.x + f2.x); f1 = {f2.x, 0}; ++e1.y; //right } else if (prod - px + py <= 0 && prod + py >= 0) { prod += py; f2 = {ONE_PIXEL, SW_UDIV(prod, dxr)}; rw.cover += (f2.y - f1.y); rw.area += (f2.y - f1.y) * (f1.x + f2.x); f1 = {0, f2.y}; ++e1.x; //down } else { f2 = {SW_UDIV(prod, -dyr), 0}; prod += px; rw.cover += (f2.y - f1.y); rw.area += (f2.y - f1.y) * (f1.x + f2.x); f1 = {f2.x, ONE_PIXEL}; --e1.y; } if (!_setCell(rw, e1)) return false; } while(e1 != e2); } f2 = FRACT(line[0]); rw.cover += (f2.y - f1.y); rw.area += (f2.y - f1.y) * (f1.x + f2.x); rw.pos = line[0]; if (line-- == rw.lineStack) return true; } } static bool _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) { auto arc = rw.bezStack; arc[0] = to; arc[1] = ctrl2; arc[2] = ctrl1; arc[3] = rw.pos; //Short-cut the arc that crosses the current band auto min = arc[0].y; auto max = arc[0].y; int32_t y; for (auto i = 1; i < 4; ++i) { y = arc[i].y; if (y < min) min = y; if (y > max) max = y; } if (TRUNC(min) >= rw.cellMax.y || TRUNC(max) < rw.cellMin.y) goto draw; /* 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 */ while (true) { { //diff is the P0 - P3 chord vector auto diff = arc[3] - arc[0]; auto L = HYPOT(diff); //avoid possible arithmetic overflow below by splitting if (L > SHRT_MAX) goto split; //max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1) auto sLimit = L * (ONE_PIXEL / 6); auto diff1 = arc[1] - arc[0]; auto s = diff.y * diff1.x - diff.x * diff1.y; if (s < 0) s = -s; if (s > sLimit) goto split; //s is L * the perpendicular distance from P2 to the line P0 - P3 auto diff2 = arc[2] - arc[0]; s = diff.y * diff2.x - diff.x * diff2.y; if (s < 0) s = -s; if (s > sLimit) 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 (diff1.x * (diff1.x - diff.x) + diff1.y * (diff1.y - diff.y) > 0 || diff2.x * (diff2.x - diff.x) + diff2.y * (diff2.y - diff.y) > 0) goto split; //no reason to split goto draw; } split: mathSplitCubic(arc); arc += 3; continue; draw: if (!_lineTo(rw, arc[0])) return false; if (arc == rw.bezStack) return true; arc -= 3; } } static bool _decomposeOutline(RleWorker& rw) { auto outline = rw.outline; auto first = 0; //index of first point in contour ARRAY_FOREACH(p, outline->cntrs) { auto last = *p; auto limit = outline->pts.data + last; auto start = UPSCALE(outline->pts[first]); auto pt = outline->pts.data + first; auto types = outline->types.data + first; ++types; if (!_moveTo(rw, UPSCALE(outline->pts[first]))) return false; while (pt < limit) { //emit a single line_to if (types[0] == SW_CURVE_TYPE_POINT) { ++pt; ++types; if (!_lineTo(rw, UPSCALE(*pt))) return false; //types cubic } else { pt += 3; types += 3; if (pt <= limit) { if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]))) return false; } else if (pt - 1 == limit) { if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start)) return false; } else goto close; } } close: if (!_lineTo(rw, start)) return false; first = last + 1; } return true; } static bool _genRle(RleWorker& rw) { if (!_decomposeOutline(rw)) return false; if (!rw.invalid && !_recordCell(rw)) return false; return true; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const RenderRegion& bbox, SwMpool* mpool, unsigned tid, bool antiAlias) { if (!outline) return nullptr; RleWorker rw; auto cellPool = mpoolReqCellPool(mpool, tid); auto reqSize = uint32_t(std::max(bbox.w(), bbox.h()) * 0.75f) * sizeof(SwCell); //experimental decision // grow by 1.25x and align to multiple of sizeof(SwCell) if (reqSize > cellPool->size) { cellPool->size = ((reqSize + (reqSize >> 2)) / sizeof(SwCell)) * sizeof(SwCell); tvg::free(cellPool->buffer); cellPool->buffer = tvg::malloc(cellPool->size); } //Init Cells rw.buffer = cellPool->buffer; rw.bufferSize = cellPool->size; rw.yCells = reinterpret_cast(cellPool->buffer); rw.cells = nullptr; rw.maxCells = 0; rw.cellsCnt = 0; rw.area = 0; rw.cover = 0; rw.invalid = true; rw.cellMin = {bbox.min.x, bbox.min.y}; rw.cellMax = {bbox.max.x, bbox.max.y}; rw.cellXCnt = rw.cellMax.x - rw.cellMin.x; rw.cellYCnt = rw.cellMax.y - rw.cellMin.y; rw.outline = const_cast(outline); rw.bandSize = rw.bufferSize / (sizeof(SwCell) * 2); rw.bandShoot = 0; rw.antiAlias = antiAlias; if (!rle) rw.rle = new SwRle; else rw.rle = rle; rw.rle->spans.reserve(256); //Generate RLE constexpr auto BAND_SIZE = 40; Band bands[BAND_SIZE]; Band* band; /* set up vertical bands */ auto bandCnt = static_cast((rw.cellMax.y - rw.cellMin.y) / rw.bandSize); if (bandCnt == 0) bandCnt = 1; else if (bandCnt >= BAND_SIZE) bandCnt = (BAND_SIZE - 1); auto min = rw.cellMin.y; auto yMax = rw.cellMax.y; int32_t max; for (int n = 0; n < bandCnt; ++n, min = max) { max = min + rw.bandSize; if (n == bandCnt -1 || max > yMax) max = yMax; bands[0].min = min; bands[0].max = max; band = bands; while (band >= bands) { rw.yCells = reinterpret_cast(rw.buffer); rw.yCnt = band->max - band->min; int cellStart = sizeof(SwCell*) * (int)rw.yCnt; int cellMod = cellStart % sizeof(SwCell); if (cellMod > 0) cellStart += sizeof(SwCell) - cellMod; auto cellsMax = reinterpret_cast((char*)rw.buffer + rw.bufferSize); rw.cells = reinterpret_cast((char*)rw.buffer + cellStart); if (rw.cells >= cellsMax) goto reduce_bands; rw.maxCells = cellsMax - rw.cells; if (rw.maxCells < 2) goto reduce_bands; for (int y = 0; y < rw.yCnt; ++y) rw.yCells[y] = nullptr; rw.cellsCnt = 0; rw.invalid = true; rw.cellMin.y = band->min; rw.cellMax.y = band->max; rw.cellYCnt = band->max - band->min; if (_genRle(rw)) { _sweep(rw); --band; continue; } reduce_bands: /* render pool overflow: we will reduce the render band by half */ auto bottom = band->min; auto top = band->max; auto middle = bottom + ((top - bottom) >> 1); /* This is too complex for a single scanline; there must be some problems */ if (middle == bottom) { rleFree(rw.rle); return nullptr; } if (bottom - top >= rw.bandSize) ++rw.bandShoot; band[1].min = bottom; band[1].max = middle; band[0].min = middle; band[0].max = top; ++band; } } if (rw.bandShoot > 8 && rw.bandSize > 16) { rw.bandSize = (rw.bandSize >> 1); } return rw.rle; } SwRle* rleRender(const RenderRegion* bbox) { auto rle = tvg::calloc(sizeof(SwRle), 1); rle->spans.reserve(bbox->h()); rle->spans.count = bbox->h(); //cheaper without push() auto x = uint16_t(bbox->min.x); auto y = uint16_t(bbox->min.y); auto len = uint16_t(bbox->w()); ARRAY_FOREACH(p, rle->spans) { *p = {x, y++, len, 255}; } return rle; } void rleReset(SwRle* rle) { if (rle) rle->spans.clear(); } void rleFree(SwRle* rle) { delete(rle); } bool rleClip(SwRle* rle, const SwRle *clip) { if (rle->spans.empty() || clip->spans.empty()) return false; Array out; out.reserve(std::max(rle->spans.count, clip->spans.count)); const SwSpan *end; auto spans = rle->fetch(clip->spans.first().y, clip->spans.last().y, &end); if (spans >= end) { rle->spans.clear(); return false; } const SwSpan *cend; auto cspans = clip->fetch(spans->y, (end - 1)->y, &cend); while (spans < end && cspans < cend) { //align y-coordinates. if (cspans->y > spans->y) { ++spans; continue; } if (spans->y > cspans->y) { ++cspans; continue; } //try clipping with all clip spans which have a same y-coordinate. auto temp = cspans; while(temp < cend && temp->y == cspans->y) { //span must be left(x1) to right(x2) direction. Not intersected. if ((spans->x + spans->len) < spans->x || (temp->x + temp->len) < temp->x) { ++temp; continue; } //clip span region auto x = std::max(spans->x, temp->x); auto len = std::min((spans->x + spans->len), (temp->x + temp->len)) - x; if (len > 0) out.next() = {uint16_t(x), temp->y, uint16_t(len), (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8)}; ++temp; } ++spans; } out.move(rle->spans); return true; } //Need to confirm: dead code? bool rleClip(SwRle *rle, const RenderRegion* clip) { if (rle->spans.empty() || clip->invalid()) return false; auto& min = clip->min; auto& max = clip->max; Array out; out.reserve(rle->spans.count); auto data = out.data; const SwSpan* end; uint16_t x, len; for (auto p = rle->fetch(*clip, &end); p < end; ++p) { if (p->y >= max.y) break; if (p->y < min.y || p->x >= max.x || (p->x + p->len) <= min.x) continue; if (p->x < min.x) { x = min.x; len = std::min(uint16_t(p->len - (x - p->x)), uint16_t(max.x - x)); } else { x = p->x; len = std::min(p->len, uint16_t(max.x - x)); } if (len > 0) { *data = {x, p->y, len, p->coverage}; ++data; ++out.count; } } out.move(rle->spans); return true; } bool rleIntersect(const SwRle* rle, const RenderRegion& region) { if (!rle || rle->spans.empty()) return false; auto& min = region.min; auto& max = region.max; const SwSpan* end; for (auto p = rle->fetch(region, &end); p < end; ++p) { if (p->y >= max.y) break; if (p->y < min.y || p->x >= max.x || (p->x + p->len) <= min.x) continue; return true; } return false; }lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-shared-arraybuffer-prototype.inc.h000664 001750 001750 00000003217 15164251010 054433 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * SharedArrayBuffer.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_SHAREDARRAYBUFFER /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* Readonly accessor properties */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BYTE_LENGTH_UL, ecma_builtin_shared_arraybuffer_prototype_bytelength_getter, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v11, 24.2.4.4 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_SHARED_ARRAY_BUFFER_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_SLICE, ecma_builtin_shared_arraybuffer_prototype_object_slice, NON_FIXED, 2) #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-shared-arraybuffer.cpp000664 001750 001750 00000005661 15164251010 052160 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-dataview-object.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-shared-arraybuffer-object.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_SHAREDARRAYBUFFER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-shared-arraybuffer.inc.h" #define BUILTIN_UNDERSCORED_ID shared_arraybuffer #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup sharedarraybuffer ECMA SharedArrayBuffer object built-in * @{ */ /** * Handle calling [[Call]] of built-in SharedArrayBuffer object * * ES11 24.2.2 SharedArrayBuffer is not intended to be called as * a function and will throw an exception when called in * that manner. * * @return ecma value */ ecma_value_t ecma_builtin_shared_arraybuffer_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_SHAREDARRAYBUFFER_REQUIRES_NEW); } /* ecma_builtin_shared_arraybuffer_dispatch_call */ /** * Handle calling [[Construct]] of built-in SharedArrayBuffer object * * @return ecma value */ ecma_value_t ecma_builtin_shared_arraybuffer_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_op_create_shared_arraybuffer_object (arguments_list_p, arguments_list_len); } /* ecma_builtin_shared_arraybuffer_dispatch_construct */ /** * 24.2.3.2 get SharedArrayBuffer [ @@species ] accessor * * @return ecma_value * returned value must be freed with ecma_free_value */ ecma_value_t ecma_builtin_shared_arraybuffer_species_get (ecma_value_t this_value) /**< This Value */ { return ecma_copy_value (this_value); } /* ecma_builtin_shared_arraybuffer_species_get */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-set-prototype.inc.h000664 001750 001750 00000004474 15164251010 051460 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Set.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 23.2.3.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_SET, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.1.3.13 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_SET_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_CLEAR, ECMA_CONTAINER_ROUTINE_CLEAR, 0, 0) ROUTINE (LIT_MAGIC_STRING_ADD, ECMA_CONTAINER_ROUTINE_ADD, 1, 1) ROUTINE (LIT_MAGIC_STRING_DELETE, ECMA_CONTAINER_ROUTINE_DELETE, 1, 1) ROUTINE (LIT_MAGIC_STRING_FOR_EACH_UL, ECMA_CONTAINER_ROUTINE_FOREACH, 2, 1) ROUTINE (LIT_MAGIC_STRING_HAS, ECMA_CONTAINER_ROUTINE_HAS, 1, 1) ROUTINE (LIT_MAGIC_STRING_ENTRIES, ECMA_CONTAINER_ROUTINE_ENTRIES, 0, 0) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_VALUES, LIT_INTERNAL_MAGIC_STRING_SET_PROTOTYPE_VALUES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_GLOBAL_SYMBOL_ITERATOR, LIT_INTERNAL_MAGIC_STRING_SET_PROTOTYPE_VALUES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_KEYS, LIT_INTERNAL_MAGIC_STRING_SET_PROTOTYPE_VALUES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_SIZE, ECMA_CONTAINER_ROUTINE_SIZE_GETTER, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp000664 001750 001750 00000073576 15164251010 037040 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include "tvgSwCommon.h" #include "tvgTaskScheduler.h" #include "tvgSwRenderer.h" #ifdef THORVG_OPENMP_SUPPORT #include #endif /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static atomic rendererCnt{-1}; static SwMpool* globalMpool = nullptr; static uint32_t threadsCnt = 0; struct SwTask : Task { SwSurface* surface = nullptr; SwMpool* mpool = nullptr; RenderRegion clipBox; //clipping region applied to the task, may differ from curBox which is the actual rendering region RenderRegion curBox{}; //current rendering region RenderRegion prvBox{}; //previous rendering region Matrix transform; Array clips; RenderDirtyRegion* dirtyRegion; RenderUpdateFlag flags[2] = {RenderUpdateFlag::None, RenderUpdateFlag::None}; //cur&prv uint8_t opacity; bool pushed : 1; //Pushed into task list? bool disposed : 1; //Disposed task? bool nodirty : 1; //target for partial rendering? bool valid : 1; SwTask() : pushed(false), disposed(false) {} const RenderRegion& bounds() { done(); return curBox; } void invisible() { curBox.reset(); if (!nodirty) dirtyRegion->add(prvBox, curBox); } bool ready(bool condition) { //invisible if (condition) { if (flags[0] & RenderUpdateFlag::Color) invisible(); flags[1] = flags[0]; //backup return true; } flags[0] |= flags[1]; //applied the previous flags if it's skipped before flags[1] = RenderUpdateFlag::None; //reset return false; } virtual void dispose() = 0; virtual bool clip(SwRle* target) = 0; virtual ~SwTask() {} }; struct SwShapeTask : SwTask { SwShape shape; const RenderShape* rshape = nullptr; bool clipper = false; /* We assume that if the stroke width is greater than 2, the shape's outline beneath the stroke could be adequately covered by the stroke drawing. Therefore, antialiasing is disabled under this condition. Additionally, the stroke style should not be dashed. */ bool antialiasing(float strokeWidth) { return strokeWidth < 2.0f || rshape->stroke->dash.count > 0 || rshape->stroke->first || rshape->trimpath() || rshape->stroke->color.a < 255; } float validStrokeWidth(bool clipper) { if (!rshape->stroke || tvg::zero(rshape->stroke->width)) return 0.0f; if (!clipper && (!rshape->stroke->fill && (rshape->stroke->color.a == 0))) return 0.0f; if (tvg::zero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f; return (rshape->stroke->width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12)); } bool clip(SwRle* target) override { if (shape.strokeRle) return rleClip(target, shape.strokeRle); if (shape.fastTrack) return rleClip(target, &curBox); if (shape.rle) return rleClip(target, shape.rle); return false; } void run(unsigned tid) override { if (ready(opacity == 0 && !clipper)) return; auto strokeWidth = validStrokeWidth(clipper); auto updateShape = flags[0] & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform | RenderUpdateFlag::Clip); auto updateFill = (flags[0] & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient)); //Shape if (updateShape) { shapeReset(shape); if (rshape->fill || rshape->color.a > 0 || clipper) { if (shapePrepare(shape, rshape, transform, clipBox, curBox, mpool, tid, clips.count > 0 ? true : false)) { if (!shapeGenRle(shape, curBox, mpool, tid, antialiasing(strokeWidth))) goto err; } else { updateFill = false; curBox.reset(); } } } //Fill if (updateFill) { if (auto fill = rshape->fill) { auto ctable = (flags[0] & RenderUpdateFlag::Gradient) ? true : false; if (ctable) shapeResetFill(shape); if (!shapeGenFillColors(shape, fill, transform, surface, opacity, ctable)) goto err; } } //Stroke if (updateShape || flags[0] & RenderUpdateFlag::Stroke) { if (strokeWidth > 0.0f) { shapeResetStroke(shape, rshape, transform, mpool, tid); if (!shapeGenStrokeRle(shape, rshape, transform, clipBox, curBox, mpool, tid)) goto err; if (auto fill = rshape->strokeFill()) { auto ctable = (flags[0] & RenderUpdateFlag::GradientStroke) ? true : false; if (ctable) shapeResetStrokeFill(shape); if (!shapeGenStrokeFillColors(shape, fill, transform, surface, opacity, ctable)) goto err; } } else { shapeDelStroke(shape); } } //Clear current task memorypool here if the clippers would use the same memory pool shapeDelOutline(shape, mpool, tid); //Clip Path ARRAY_FOREACH(p, clips) { auto clipper = static_cast(*p); auto clipShapeRle = shape.rle ? clipper->clip(shape.rle) : true; auto clipStrokeRle = shape.strokeRle ? clipper->clip(shape.strokeRle) : true; if (!clipShapeRle || !clipStrokeRle) goto err; } valid = true; if (!nodirty) dirtyRegion->add(prvBox, curBox); return; err: shapeReset(shape); rleReset(shape.strokeRle); shapeDelOutline(shape, mpool, tid); invisible(); } void dispose() override { shapeFree(shape); } }; struct SwImageTask : SwTask { SwImage image; RenderSurface* source; //Image source bool clip(SwRle* target) override { TVGERR("SW_ENGINE", "Image is used as ClipPath?"); return true; } void run(unsigned tid) override { if (ready(opacity == 0)) return; //Convert colorspace if it's not aligned. rasterConvertCS(source, surface->cs); rasterPremultiply(source); image.data = source->data; image.w = source->w; image.h = source->h; image.stride = source->stride; image.channelSize = source->channelSize; auto updateImage = flags[0] & (RenderUpdateFlag::Image | RenderUpdateFlag::Clip | RenderUpdateFlag::Transform); auto updateColor = flags[0] & (RenderUpdateFlag::Color); //Invisible shape turned to visible by alpha. if ((updateImage || updateColor) && (opacity > 0)) { if (updateImage) imageReset(image); if (!image.data || image.w == 0 || image.h == 0) goto err; if (!imagePrepare(image, transform, clipBox, curBox, mpool, tid)) goto err; valid = true; if (clips.count > 0) { if (!imageGenRle(image, curBox, mpool, tid, false)) goto err; if (image.rle) { //Clear current task memorypool here if the clippers would use the same memory pool imageDelOutline(image, mpool, tid); ARRAY_FOREACH(p, clips) { auto clipper = static_cast(*p); if (!clipper->clip(image.rle)) goto err; } if (!nodirty) dirtyRegion->add(prvBox, curBox); return; } } else imageFree(image); } goto end; err: curBox.reset(); imageReset(image); end: imageDelOutline(image, mpool, tid); if (!nodirty) dirtyRegion->add(prvBox, curBox); } void dispose() override { imageFree(image); } }; /************************************************************************/ /* External Class Implementation */ /************************************************************************/ SwRenderer::~SwRenderer() { clearCompositors(); delete(surface); if (!sharedMpool) mpoolTerm(mpool); --rendererCnt; } bool SwRenderer::clear() { if (surface) { fulldraw = true; return rasterClear(surface, 0, 0, surface->w, surface->h); } return false; } bool SwRenderer::sync() { //clear if the rendering was not triggered. ARRAY_FOREACH(p, tasks) { if ((*p)->disposed) delete(*p); else { (*p)->done(); (*p)->pushed = false; } } tasks.clear(); return true; } bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs) { if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false; clearCompositors(); if (!surface) surface = new SwSurface; surface->data = data; surface->stride = stride; surface->w = w; surface->h = h; surface->cs = cs; surface->channelSize = CHANNEL_SIZE(cs); surface->premultiplied = true; dirtyRegion.init(w, h); fulldraw = true; //reset the screen return rasterCompositor(surface); } bool SwRenderer::preUpdate() { return surface != nullptr; } bool SwRenderer::postUpdate() { return true; } bool SwRenderer::preRender() { if (!surface) return false; if (fulldraw || dirtyRegion.deactivated()) return true; ARRAY_FOREACH(p, tasks) (*p)->done(); dirtyRegion.commit(); //clear buffer for partial regions for (int idx = 0; idx < RenderDirtyRegion::PARTITIONING; ++idx) { ARRAY_FOREACH(p, dirtyRegion.get(idx)) { rasterClear(surface, p->x(), p->y(), p->w(), p->h()); } } return true; } void SwRenderer::clearCompositors() { //Free Composite Caches ARRAY_FOREACH(p, compositors) { tvg::free((*p)->compositor->image.data); delete((*p)->compositor); delete(*p); } compositors.reset(); } bool SwRenderer::postRender() { //Unmultiply alpha if needed if (surface->cs == ColorSpace::ABGR8888S || surface->cs == ColorSpace::ARGB8888S) { rasterUnpremultiply(surface); } dirtyRegion.clear(); fulldraw = false; return true; } void SwRenderer::damage(RenderData rd, const RenderRegion& region) { SwTask* task = static_cast(rd); if (dirtyRegion.deactivated() || (task && task->opacity == 0)) return; dirtyRegion.add(region); } bool SwRenderer::partial(bool disable) { return dirtyRegion.deactivate(disable); } bool SwRenderer::renderImage(RenderData data) { auto task = static_cast(data); if (!task) return false; task->done(); if (task->valid) { auto raster = [&](SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity) { if (bbox.invalid() || bbox.x() >= surface->w || bbox.y() >= surface->h) return true; //RLE Image if (image.rle) { if (image.rle->invalid()) return true; if (image.direct) return rasterDirectRleImage(surface, image, bbox, opacity); else if (image.scaled) return rasterScaledRleImage(surface, image, transform, bbox, opacity); else { //create a intermediate buffer for rle clipping auto cmp = request(sizeof(pixel_t), false); cmp->compositor->method = MaskMethod::None; cmp->compositor->valid = true; cmp->compositor->image.rle = image.rle; rasterClear(cmp, bbox.x(), bbox.y(), bbox.w(), bbox.h()); rasterTexmapPolygon(cmp, image, transform, bbox, 255); return rasterDirectRleImage(surface, cmp->compositor->image, bbox, opacity); } //Whole Image } else { if (image.direct) return rasterDirectImage(surface, image, bbox, opacity); else if (image.scaled) return rasterScaledImage(surface, image, transform, bbox, opacity); else return rasterTexmapPolygon(surface, image, transform, bbox, opacity); } }; //full scene or partial rendering if (fulldraw || task->nodirty || task->pushed || dirtyRegion.deactivated()) { raster(surface, task->image, task->transform, task->curBox, task->opacity); } else if (task->curBox.valid()) { for (int idx = 0; idx < RenderDirtyRegion::PARTITIONING; ++idx) { if (!dirtyRegion.partition(idx).intersected(task->curBox)) continue; ARRAY_FOREACH(p, dirtyRegion.get(idx)) { if (task->curBox.max.x <= p->min.x) break; //dirtyRegion is sorted in x order if (task->curBox.intersected(*p)) { auto bbox = RenderRegion::intersect(task->curBox, *p); raster(surface, task->image, task->transform, bbox, task->opacity); } } } } } task->prvBox = task->curBox; return true; } bool SwRenderer::renderShape(RenderData data) { auto task = static_cast(data); if (!task) return false; task->done(); if (task->valid) { auto fill = [](SwShapeTask* task, SwSurface* surface, const RenderRegion& bbox) { if (auto fill = task->rshape->fill) { rasterGradientShape(surface, &task->shape, bbox, fill, task->opacity); } else { RenderColor c; task->rshape->fillColor(&c.r, &c.g, &c.b, &c.a); c.a = MULTIPLY(task->opacity, c.a); if (c.a > 0) rasterShape(surface, &task->shape, bbox, c); } }; auto stroke = [](SwShapeTask* task, SwSurface* surface, const RenderRegion& bbox) { if (auto strokeFill = task->rshape->strokeFill()) { rasterGradientStroke(surface, &task->shape, bbox, strokeFill, task->opacity); } else { RenderColor c; if (task->rshape->strokeFill(&c.r, &c.g, &c.b, &c.a)) { c.a = MULTIPLY(task->opacity, c.a); if (c.a > 0) rasterStroke(surface, &task->shape, bbox, c); } } }; //full scene or partial rendering if (fulldraw || task->nodirty || task->pushed || dirtyRegion.deactivated()) { if (task->rshape->strokeFirst()) { stroke(task, surface, task->curBox); fill(task, surface, task->shape.bbox); } else { fill(task, surface, task->shape.bbox); stroke(task, surface, task->curBox); } } else if (task->curBox.valid()) { for (int idx = 0; idx < RenderDirtyRegion::PARTITIONING; ++idx) { if (!dirtyRegion.partition(idx).intersected(task->curBox)) continue; ARRAY_FOREACH(p, dirtyRegion.get(idx)) { if (task->curBox.max.x <= p->min.x) break; //dirtyRegion is sorted in x order if (task->rshape->strokeFirst()) { if (task->rshape->stroke && task->curBox.intersected(*p)) stroke(task, surface, RenderRegion::intersect(task->curBox, *p)); if (task->shape.bbox.intersected(*p)) fill(task, surface, RenderRegion::intersect(task->shape.bbox, *p)); } else { if (task->shape.bbox.intersected(*p)) fill(task, surface, RenderRegion::intersect(task->shape.bbox, *p)); if (task->rshape->stroke && task->curBox.intersected(*p)) stroke(task, surface, RenderRegion::intersect(task->curBox, *p)); } } } } } task->prvBox = task->curBox; return true; } bool SwRenderer::blend(BlendMethod method) { if (surface->blendMethod == method) return true; surface->blendMethod = method; switch (method) { case BlendMethod::Multiply: surface->blender = opBlendMultiply; break; case BlendMethod::Screen: surface->blender = opBlendScreen; break; case BlendMethod::Overlay: surface->blender = opBlendOverlay; break; case BlendMethod::Darken: surface->blender = opBlendDarken; break; case BlendMethod::Lighten: surface->blender = opBlendLighten; break; case BlendMethod::ColorDodge: surface->blender = opBlendColorDodge; break; case BlendMethod::ColorBurn: surface->blender = opBlendColorBurn; break; case BlendMethod::HardLight: surface->blender = opBlendHardLight; break; case BlendMethod::SoftLight: surface->blender = opBlendSoftLight; break; case BlendMethod::Difference: surface->blender = opBlendDifference; break; case BlendMethod::Exclusion: surface->blender = opBlendExclusion; break; case BlendMethod::Hue: surface->blender = opBlendHue; break; case BlendMethod::Saturation: surface->blender = opBlendSaturation; break; case BlendMethod::Color: surface->blender = opBlendColor; break; case BlendMethod::Luminosity: surface->blender = opBlendLuminosity; break; case BlendMethod::Add: surface->blender = opBlendAdd; break; default: surface->blender = nullptr; break; } return true; } RenderRegion SwRenderer::region(RenderData data) { if (data) return static_cast(data)->bounds(); return {}; } bool SwRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) { if (!cmp) return false; auto p = static_cast(cmp); p->method = method; p->opacity = opacity; //Current Context? if (p->method != MaskMethod::None) { surface = p->recoverSfc; surface->compositor = p; } return true; } const RenderSurface* SwRenderer::mainSurface() { return surface; } SwSurface* SwRenderer::request(int channelSize, bool square) { SwSurface* cmp = nullptr; uint32_t w, h; if (square) { //Same Dimensional Size is demanded for the Post Processing Fast Flipping w = h = std::max(surface->w, surface->h); } else { w = surface->w; h = surface->h; } //Use cached data ARRAY_FOREACH(p, compositors) { auto cur = *p; if (cur->compositor->valid && cur->compositor->image.channelSize == channelSize) { if (w == cur->w && h == cur->h) { cmp = *p; break; } } } //New Composition if (!cmp) { //Inherits attributes from main surface cmp = new SwSurface(surface); cmp->compositor = new SwCompositor; cmp->compositor->image.data = tvg::malloc(channelSize * w * h); cmp->w = cmp->compositor->image.w = w; cmp->h = cmp->compositor->image.h = h; cmp->stride = cmp->compositor->image.stride = w; cmp->compositor->image.direct = true; cmp->compositor->valid = true; cmp->channelSize = cmp->compositor->image.channelSize = channelSize; compositors.push(cmp); } //Sync. This may have been modified by post-processing. cmp->data = cmp->compositor->image.data; return cmp; } RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) { auto bbox = RenderRegion::intersect(region, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}}); if (bbox.invalid()) return nullptr; auto cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing)); cmp->compositor->recoverSfc = surface; cmp->compositor->recoverCmp = surface->compositor; cmp->compositor->valid = false; cmp->compositor->bbox = bbox; /* TODO: Currently, only blending might work. Blending and composition must be handled together. */ rasterClear(cmp, bbox.x(), bbox.y(), bbox.w(), bbox.h()); //Switch render target surface = cmp; return cmp->compositor; } bool SwRenderer::endComposite(RenderCompositor* cmp) { if (!cmp) return false; auto p = static_cast(cmp); //Recover Context surface = p->recoverSfc; surface->compositor = p->recoverCmp; //only invalid (currently used) surface can be composited if (p->valid) return true; p->valid = true; //Default is alpha blending if (p->method == MaskMethod::None) { return rasterDirectImage(surface, p->image, p->bbox, p->opacity); } return true; } void SwRenderer::prepare(RenderEffect* effect, const Matrix& transform) { switch (effect->type) { case SceneEffect::GaussianBlur: effectGaussianBlurUpdate(static_cast(effect), transform); break; case SceneEffect::DropShadow: effectDropShadowUpdate(static_cast(effect), transform); break; case SceneEffect::Fill: effectFillUpdate(static_cast(effect)); break; case SceneEffect::Tint: effectTintUpdate(static_cast(effect)); break; case SceneEffect::Tritone: effectTritoneUpdate(static_cast(effect)); break; default: break; } } bool SwRenderer::bounds(RenderData data, Point* pt4, const Matrix& m) { if (!data) return false; auto task = static_cast(data); task->done(); return shapeStrokeBBox(task->shape, task->rshape, pt4, m, task->mpool); } bool SwRenderer::intersectsShape(RenderData data, const RenderRegion& region) { auto task = static_cast(data); task->done(); if (!task->valid || !task->bounds().intersected(region)) return false; if (rleIntersect(task->shape.strokeRle, region)) return true; return task->shape.rle ? rleIntersect(task->shape.rle, region): task->shape.fastTrack; } bool SwRenderer::intersectsImage(RenderData data, const RenderRegion& region) { auto task = static_cast(data); task->done(); if (!task->valid || !task->bounds().intersected(region)) return false; //aabb & obb transformed image intersection auto rad = tvg::radian(task->transform); if (rad > 0.0f && rad < MATH_PI) { Point aabb[4]; aabb[0] = {(float)region.min.x, (float)region.min.y}; aabb[1] = {(float)region.max.x, (float)region.min.y}; aabb[2] = {(float)region.max.x, (float)region.max.y}; aabb[3] = {(float)region.min.x, (float)region.max.y}; Point obb[4]; obb[0] = Point{0.0f, 0.0f} * task->transform; obb[1] = Point{(float)task->image.w, 0.0f} * task->transform; obb[2] = Point{(float)task->image.w, (float)task->image.h} * task->transform; obb[3] = Point{0.0f, (float)task->image.h} * task->transform; auto project = [](const Point* poly, const Point& axis, float& min, float& max) { min = max = dot(poly[0], axis); for (int i = 1; i < 4; ++i) { float projection = dot(poly[i], axis); if (projection < min) min = projection; if (projection > max) max = projection; } }; for (int i = 0; i < 4; ++i) { auto edge = (i < 2) ? (aabb[(i+1)%4] - aabb[i]) : (obb[(i-2+1)%4] - obb[i-2]); tvg::normalize(edge); float minA, maxA, minB, maxB; project(aabb, edge, minA, maxA); project(obb, edge, minB, maxB); if (maxA < minB || maxB < minA) return false; } } return task->image.rle ? rleIntersect(task->image.rle, region) : true; } bool SwRenderer::region(RenderEffect* effect) { switch (effect->type) { case SceneEffect::GaussianBlur: return effectGaussianBlurRegion(static_cast(effect)); case SceneEffect::DropShadow: return effectDropShadowRegion(static_cast(effect)); default: return false; } } bool SwRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) { auto p = static_cast(cmp); if (p->image.channelSize != sizeof(uint32_t)) { TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!"); return false; } //TODO: Support grayscale effects. if (p->recoverSfc->channelSize != sizeof(uint32_t)) direct = false; switch (effect->type) { case SceneEffect::GaussianBlur: { return effectGaussianBlur(p, request(surface->channelSize, true), static_cast(effect)); } case SceneEffect::DropShadow: { auto cmp1 = request(surface->channelSize, true); cmp1->compositor->valid = false; //prevent a conflict with cmp2 request. auto cmp2 = request(surface->channelSize, true); SwSurface* surfaces[] = {cmp1, cmp2}; auto ret = effectDropShadow(p, surfaces, static_cast(effect), direct); cmp1->compositor->valid = true; return ret; } case SceneEffect::Fill: { return effectFill(p, static_cast(effect), direct); } case SceneEffect::Tint: { return effectTint(p, static_cast(effect), direct); } case SceneEffect::Tritone: { return effectTritone(p, static_cast(effect), direct); } default: return false; } } void SwRenderer::dispose(RenderEffect* effect) { tvg::free(effect->rd); effect->rd = nullptr; } ColorSpace SwRenderer::colorSpace() { if (surface) return surface->cs; else return ColorSpace::Unknown; } void SwRenderer::dispose(RenderData data) { auto task = static_cast(data); task->done(); task->dispose(); if (task->pushed) task->disposed = true; else delete(task); } void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags) { if (task->disposed) return task; task->surface = surface; task->mpool = mpool; task->clipBox = RenderRegion::intersect(vport, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}}); task->transform = transform; task->clips = clips; task->dirtyRegion = &dirtyRegion; task->opacity = opacity; task->nodirty = dirtyRegion.deactivated(); task->flags[0] = flags; task->valid = false; if (!task->pushed) { task->pushed = true; tasks.push(task); } //TODO: Failed threading them. It would be better if it's possible. //See: https://github.com/thorvg/thorvg/issues/1409 //Guarantee composition targets get ready. ARRAY_FOREACH(p, clips) { static_cast(*p)->done(); } if (flags) TaskScheduler::request(task); return task; } RenderData SwRenderer::prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) { auto task = static_cast(data); if (task) task->done(); else { task = new SwImageTask; task->source = surface; } return prepareCommon(task, transform, clips, opacity, flags); } RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) { auto task = static_cast(data); if (task) task->done(); else { task = new SwShapeTask; task->rshape = &rshape; } task->clipper = clipper; return prepareCommon(task, transform, clips, opacity, flags); } bool SwRenderer::term() { if (rendererCnt > 0) return false; mpoolTerm(globalMpool); globalMpool = nullptr; rendererCnt = -1; return true; } SwRenderer::SwRenderer(uint32_t threads, EngineOption op) { //initialize engine if (rendererCnt == -1) { #ifdef THORVG_OPENMP_SUPPORT omp_set_num_threads(threads); #endif //Share the memory pool among the renderer globalMpool = mpoolInit(threads); threadsCnt = threads; rendererCnt = 0; } if (TaskScheduler::onthread()) { TVGLOG("SW_RENDERER", "Running on a non-dominant thread!, Renderer(%p)", this); mpool = mpoolInit(threadsCnt); sharedMpool = false; } else { mpool = globalMpool; sharedMpool = true; } if (op == EngineOption::None) dirtyRegion.support = false; ++rendererCnt; } glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/opcodes.cpp000664 001750 001750 00000221150 15164251010 042102 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "opcodes.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-lex-env.h" #include "ecma-objects.h" #include "ecma-promise-object.h" #include "ecma-proxy-object.h" #include "jcontext.h" #include "vm-defines.h" #include "vm-stack.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup vm_opcodes Opcodes * @{ */ /** * 'typeof' opcode handler. * * See also: ECMA-262 v5, 11.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t opfunc_typeof (ecma_value_t left_value) /**< left value */ { return ecma_make_magic_string_value (ecma_get_typeof_lit_id (left_value)); } /* opfunc_typeof */ /** * Update data property for object literals. */ void opfunc_set_data_property (ecma_object_t *object_p, /**< object */ ecma_string_t *prop_name_p, /**< data property name */ ecma_value_t value) /**< new value */ { JERRY_ASSERT (!ecma_op_object_is_fast_array (object_p)); ecma_property_t *property_p = ecma_find_named_property (object_p, prop_name_p); ecma_property_value_t *prop_value_p; if (property_p == NULL) { prop_value_p = ecma_create_named_data_property (object_p, prop_name_p, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); } else { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (!(*property_p & ECMA_PROPERTY_FLAG_DATA)) { *property_p |= ECMA_PROPERTY_FLAG_DATA | ECMA_PROPERTY_FLAG_WRITABLE; prop_value_p->value = ecma_copy_value_if_not_object (value); return; } } ecma_named_data_property_assign_value (object_p, prop_value_p, value); } /* opfunc_set_data_property */ /** * Update getter or setter for object literals. */ void opfunc_set_accessor (bool is_getter, /**< is getter accessor */ ecma_value_t object, /**< object value */ ecma_string_t *accessor_name_p, /**< accessor name */ ecma_value_t accessor) /**< accessor value */ { ecma_object_t *object_p = ecma_get_object_from_value (object); JERRY_ASSERT (!ecma_op_object_is_fast_array (object_p)); ecma_property_t *property_p = ecma_find_named_property (object_p, accessor_name_p); ecma_object_t *accessor_p = ecma_get_object_from_value (accessor); ecma_object_t *getter_func_p = NULL; ecma_object_t *setter_func_p = NULL; if (is_getter) { getter_func_p = accessor_p; } else { setter_func_p = accessor_p; } if (property_p == NULL) { ecma_create_named_accessor_property (object_p, accessor_name_p, getter_func_p, setter_func_p, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE, NULL); } else { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p)); ecma_property_value_t *prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (*property_p & ECMA_PROPERTY_FLAG_DATA) { ecma_free_value_if_not_object (prop_value_p->value); *property_p = (uint8_t) (*property_p & ~(ECMA_PROPERTY_FLAG_DATA | ECMA_PROPERTY_FLAG_WRITABLE)); ECMA_SET_POINTER (prop_value_p->getter_setter_pair.getter_cp, getter_func_p); ECMA_SET_POINTER (prop_value_p->getter_setter_pair.setter_cp, setter_func_p); return; } if (is_getter) { ecma_set_named_accessor_property_getter (object_p, prop_value_p, accessor_p); } else { ecma_set_named_accessor_property_setter (object_p, prop_value_p, accessor_p); } } } /* opfunc_set_accessor */ /** * Deletes an object property. * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t vm_op_delete_prop (ecma_value_t object, /**< base object */ ecma_value_t property, /**< property name */ bool is_strict) /**< strict mode */ { if (!ecma_op_require_object_coercible (object)) { return ECMA_VALUE_ERROR; } ecma_string_t *name_string_p = ecma_op_to_property_key (property); if (JERRY_UNLIKELY (name_string_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t obj_value = ecma_op_to_object (object); /* The ecma_op_require_object_coercible call already checked the op_to_object error cases. */ JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (obj_value)); JERRY_ASSERT (ecma_is_value_object (obj_value)); ecma_object_t *obj_p = ecma_get_object_from_value (obj_value); JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); ecma_value_t delete_op_ret = ecma_op_object_delete (obj_p, name_string_p, is_strict); JERRY_ASSERT (ecma_is_value_boolean (delete_op_ret) || ECMA_IS_VALUE_ERROR (delete_op_ret)); ecma_deref_object (obj_p); ecma_deref_ecma_string (name_string_p); if (is_strict && ecma_is_value_false (delete_op_ret)) { return ecma_raise_type_error (ECMA_ERR_OPERATOR_DELETE_RETURNED_FALSE_IN_STRICT_MODE); } return delete_op_ret; } /* vm_op_delete_prop */ /** * Deletes a variable. * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t vm_op_delete_var (ecma_value_t name_literal, /**< name literal */ ecma_object_t *lex_env_p) /**< lexical environment */ { ecma_value_t completion_value = ECMA_VALUE_EMPTY; ecma_string_t *var_name_str_p = ecma_get_string_from_value (name_literal); ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (lex_env_p, var_name_str_p); #if JERRY_BUILTIN_PROXY if (JERRY_UNLIKELY (ref_base_lex_env_p == ECMA_OBJECT_POINTER_ERROR)) { return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ if (ref_base_lex_env_p == NULL) { completion_value = ECMA_VALUE_TRUE; } else { JERRY_ASSERT (ecma_is_lexical_environment (ref_base_lex_env_p)); completion_value = ecma_op_delete_binding (ref_base_lex_env_p, var_name_str_p); } return completion_value; } /* vm_op_delete_var */ /** * 'for-in' opcode handler * * Note: from ES2015 (ES6) the for-in can trigger error when * the property names are not available (ex.: via Proxy ownKeys). * In these cases an error must be returned. * * This error is returned as the `result_obj_p` and the * function's return value is NULL. * * See also: * ECMA-262 v5, 12.6.4 * * @return - chain list of property names * - In case of error: NULL is returned and the `result_obj_p` * must be checked. */ ecma_collection_t * opfunc_for_in (ecma_value_t iterable_value, /**< ideally an iterable value */ ecma_value_t *result_obj_p) /**< expression object */ { /* 3. */ if (ecma_is_value_undefined (iterable_value) || ecma_is_value_null (iterable_value)) { return NULL; } /* 4. */ ecma_value_t obj_expr_value = ecma_op_to_object (iterable_value); /* ecma_op_to_object will only raise error on null/undefined values but those are handled above. */ JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (obj_expr_value)); ecma_object_t *obj_p = ecma_get_object_from_value (obj_expr_value); ecma_collection_t *prop_names_p = ecma_op_object_enumerate (obj_p); if (JERRY_UNLIKELY (prop_names_p == NULL)) { ecma_deref_object (obj_p); *result_obj_p = ECMA_VALUE_ERROR; return NULL; } if (prop_names_p->item_count != 0) { *result_obj_p = ecma_make_object_value (obj_p); return prop_names_p; } ecma_deref_object (obj_p); ecma_collection_destroy (prop_names_p); return NULL; } /* opfunc_for_in */ /** * 'VM_OC_APPEND_ARRAY' opcode handler specialized for spread objects * * @return ECMA_VALUE_ERROR - if the operation failed * ECMA_VALUE_EMPTY, otherwise */ static ecma_value_t JERRY_ATTR_NOINLINE opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top */ uint16_t values_length) /**< number of elements to set */ { JERRY_ASSERT (!(values_length & OPFUNC_HAS_SPREAD_ELEMENT)); ecma_object_t *array_obj_p = ecma_get_object_from_value (stack_top_p[-1]); JERRY_ASSERT (ecma_get_object_type (array_obj_p) == ECMA_OBJECT_TYPE_ARRAY); ecma_extended_object_t *ext_array_obj_p = (ecma_extended_object_t *) array_obj_p; uint32_t old_length = ext_array_obj_p->u.array.length; for (uint32_t i = 0, idx = old_length; i < values_length; i++, idx++) { if (ecma_is_value_array_hole (stack_top_p[i])) { continue; } if (stack_top_p[i] == ECMA_VALUE_SPREAD_ELEMENT) { i++; ecma_value_t ret_value = ECMA_VALUE_ERROR; ecma_value_t spread_value = stack_top_p[i]; ecma_value_t next_method; ecma_value_t iterator = ecma_op_get_iterator (spread_value, ECMA_VALUE_SYNC_ITERATOR, &next_method); if (!ECMA_IS_VALUE_ERROR (iterator)) { while (true) { ecma_value_t next_value = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (next_value)) { break; } if (ecma_is_value_false (next_value)) { idx--; ret_value = ECMA_VALUE_EMPTY; break; } ecma_value_t value = ecma_op_iterator_value (next_value); ecma_free_value (next_value); if (ECMA_IS_VALUE_ERROR (value)) { break; } ecma_builtin_helper_def_prop_by_index (array_obj_p, idx++, value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_free_value (value); } } ecma_free_value (iterator); ecma_free_value (next_method); ecma_free_value (spread_value); if (ECMA_IS_VALUE_ERROR (ret_value)) { for (uint32_t k = i + 1; k < values_length; k++) { ecma_free_value (stack_top_p[k]); } return ret_value; } } else { ecma_builtin_helper_def_prop_by_index (array_obj_p, idx, stack_top_p[i], ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_free_value (stack_top_p[i]); } } return ECMA_VALUE_EMPTY; } /* opfunc_append_to_spread_array */ /** * Spread function call/construct arguments into an ecma-collection * * @return NULL - if the operation failed * pointer to the ecma-collection with the spreaded arguments, otherwise */ JERRY_ATTR_NOINLINE ecma_collection_t * opfunc_spread_arguments (ecma_value_t *stack_top_p, /**< pointer to the current stack top */ uint8_t arguments_list_len) /**< number of arguments */ { ecma_collection_t *buff_p = ecma_new_collection (); for (uint32_t i = 0; i < arguments_list_len; i++) { ecma_value_t arg = *stack_top_p++; if (arg != ECMA_VALUE_SPREAD_ELEMENT) { ecma_collection_push_back (buff_p, arg); continue; } ecma_value_t ret_value = ECMA_VALUE_ERROR; ecma_value_t spread_value = *stack_top_p++; i++; ecma_value_t next_method; ecma_value_t iterator = ecma_op_get_iterator (spread_value, ECMA_VALUE_SYNC_ITERATOR, &next_method); if (!ECMA_IS_VALUE_ERROR (iterator)) { while (true) { ecma_value_t next_value = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (next_value)) { break; } if (ecma_is_value_false (next_value)) { ret_value = ECMA_VALUE_EMPTY; break; } ecma_value_t value = ecma_op_iterator_value (next_value); ecma_free_value (next_value); if (ECMA_IS_VALUE_ERROR (value)) { break; } ecma_collection_push_back (buff_p, value); } } ecma_free_value (iterator); ecma_free_value (next_method); ecma_free_value (spread_value); if (ECMA_IS_VALUE_ERROR (ret_value)) { for (uint32_t k = i + 1; k < arguments_list_len; k++) { ecma_free_value (*stack_top_p++); } ecma_collection_free (buff_p); buff_p = NULL; break; } } return buff_p; } /* opfunc_spread_arguments */ /** * 'VM_OC_APPEND_ARRAY' opcode handler, for setting array object properties * * @return ECMA_VALUE_ERROR - if the operation failed * ECMA_VALUE_EMPTY, otherwise */ ecma_value_t JERRY_ATTR_NOINLINE opfunc_append_array (ecma_value_t *stack_top_p, /**< current stack top */ uint16_t values_length) /**< number of elements to set * with potential OPFUNC_HAS_SPREAD_ELEMENT flag */ { if (values_length >= OPFUNC_HAS_SPREAD_ELEMENT) { return opfunc_append_to_spread_array (stack_top_p, (uint16_t) (values_length & ~OPFUNC_HAS_SPREAD_ELEMENT)); } ecma_object_t *array_obj_p = ecma_get_object_from_value (stack_top_p[-1]); JERRY_ASSERT (ecma_get_object_type (array_obj_p) == ECMA_OBJECT_TYPE_ARRAY); ecma_extended_object_t *ext_array_obj_p = (ecma_extended_object_t *) array_obj_p; uint32_t old_length = ext_array_obj_p->u.array.length; if (JERRY_LIKELY (ecma_op_array_is_fast_array (ext_array_obj_p))) { uint32_t filled_holes = 0; ecma_value_t *values_p = ecma_fast_array_extend (array_obj_p, old_length + values_length); for (uint32_t i = 0; i < values_length; i++) { values_p[old_length + i] = stack_top_p[i]; if (!ecma_is_value_array_hole (stack_top_p[i])) { filled_holes++; ecma_deref_if_object (stack_top_p[i]); } } ext_array_obj_p->u.array.length_prop_and_hole_count -= filled_holes * ECMA_FAST_ARRAY_HOLE_ONE; if (JERRY_UNLIKELY ((values_length - filled_holes) > ECMA_FAST_ARRAY_MAX_NEW_HOLES_COUNT)) { ecma_fast_array_convert_to_normal (array_obj_p); } } else { for (uint32_t i = 0; i < values_length; i++) { if (!ecma_is_value_array_hole (stack_top_p[i])) { ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (old_length + i); ecma_property_value_t *prop_value_p; prop_value_p = ecma_create_named_data_property (array_obj_p, index_str_p, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); ecma_deref_ecma_string (index_str_p); prop_value_p->value = stack_top_p[i]; ecma_deref_if_object (stack_top_p[i]); } } ext_array_obj_p->u.array.length = old_length + values_length; } return ECMA_VALUE_EMPTY; } /* opfunc_append_array */ /** * Create an executable object using the current frame context * * @return executable object */ vm_executable_object_t * opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ vm_create_executable_object_type_t type) /**< executable object type */ { const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->shared_p->bytecode_header_p; size_t size, register_end; ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_header_p); if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; register_end = (size_t) args_p->register_end; size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; register_end = (size_t) args_p->register_end; size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); } size_t total_size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t)); ecma_object_t *proto_p = NULL; /* Async function objects are not accessible, so their class_id is not relevant. */ uint8_t class_type = ECMA_OBJECT_CLASS_GENERATOR; if (type == VM_CREATE_EXECUTABLE_OBJECT_GENERATOR) { ecma_builtin_id_t default_proto_id = ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE; if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) == CBC_FUNCTION_ASYNC_GENERATOR) { default_proto_id = ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE; class_type = ECMA_OBJECT_CLASS_ASYNC_GENERATOR; } JERRY_ASSERT (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_NON_ARROW_FUNC); proto_p = ecma_op_get_prototype_from_constructor (frame_ctx_p->shared_p->function_object_p, default_proto_id); } ecma_object_t *object_p = ecma_create_object (proto_p, total_size, ECMA_OBJECT_TYPE_CLASS); vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; if (type == VM_CREATE_EXECUTABLE_OBJECT_GENERATOR) { ecma_deref_object (proto_p); } executable_object_p->extended_object.u.cls.type = class_type; executable_object_p->extended_object.u.cls.u2.executable_obj_flags = 0; ECMA_SET_INTERNAL_VALUE_ANY_POINTER (executable_object_p->extended_object.u.cls.u3.head, NULL); executable_object_p->iterator = ECMA_VALUE_UNDEFINED; JERRY_ASSERT (!(frame_ctx_p->status_flags & VM_FRAME_CTX_DIRECT_EVAL)); /* Copy shared data and frame context. */ vm_frame_ctx_shared_t *new_shared_p = &(executable_object_p->shared); *new_shared_p = *(frame_ctx_p->shared_p); new_shared_p->status_flags &= (uint32_t) ~VM_FRAME_CTX_SHARED_HAS_ARG_LIST; new_shared_p->status_flags |= VM_FRAME_CTX_SHARED_EXECUTABLE; vm_frame_ctx_t *new_frame_ctx_p = &(executable_object_p->frame_ctx); *new_frame_ctx_p = *frame_ctx_p; new_frame_ctx_p->shared_p = new_shared_p; /* The old register values are discarded. */ ecma_value_t *new_registers_p = VM_GET_REGISTERS (new_frame_ctx_p); memcpy (new_registers_p, VM_GET_REGISTERS (frame_ctx_p), size); size_t stack_top = (size_t) (frame_ctx_p->stack_top_p - VM_GET_REGISTERS (frame_ctx_p)); ecma_value_t *new_stack_top_p = new_registers_p + stack_top; new_frame_ctx_p->stack_top_p = new_stack_top_p; /* Initial state is "not running", so all object references are released. */ if (frame_ctx_p->context_depth > 0) { JERRY_ASSERT (type != VM_CREATE_EXECUTABLE_OBJECT_GENERATOR); ecma_value_t *register_end_p = new_registers_p + register_end; JERRY_ASSERT (register_end_p <= new_stack_top_p); while (new_registers_p < register_end_p) { ecma_deref_if_object (*new_registers_p++); } vm_ref_lex_env_chain (frame_ctx_p->lex_env_p, frame_ctx_p->context_depth, new_registers_p, false); new_registers_p += frame_ctx_p->context_depth; JERRY_ASSERT (new_registers_p <= new_stack_top_p); } while (new_registers_p < new_stack_top_p) { ecma_deref_if_object (*new_registers_p++); } JERRY_ASSERT (*VM_GET_EXECUTABLE_ITERATOR (new_frame_ctx_p) == ECMA_VALUE_UNDEFINED); new_frame_ctx_p->this_binding = ecma_copy_value_if_not_object (new_frame_ctx_p->this_binding); JERRY_CONTEXT (vm_top_context_p) = new_frame_ctx_p->prev_context_p; return executable_object_p; } /* opfunc_create_executable_object */ /** * Byte code which resumes an executable object with throw */ const uint8_t opfunc_resume_executable_object_with_throw[1] = { CBC_THROW }; /** * Byte code which resumes an executable object with return */ const uint8_t opfunc_resume_executable_object_with_return[2] = { CBC_EXT_OPCODE, CBC_EXT_RETURN }; /** * Resume the execution of an inactive executable object * * @return value provided by the execution */ ecma_value_t opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /**< executable object */ ecma_value_t value) /**< value pushed onto the stack (takes the reference) */ { const ecma_compiled_code_t *bytecode_header_p = executable_object_p->shared.bytecode_header_p; ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); ecma_value_t *register_end_p; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; register_end_p = register_p + args_p->register_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; register_end_p = register_p + args_p->register_end; } ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p; if (value != ECMA_VALUE_EMPTY) { *stack_top_p = value; executable_object_p->frame_ctx.stack_top_p = stack_top_p + 1; } if (executable_object_p->frame_ctx.context_depth > 0) { while (register_p < register_end_p) { ecma_ref_if_object (*register_p++); } vm_ref_lex_env_chain (executable_object_p->frame_ctx.lex_env_p, executable_object_p->frame_ctx.context_depth, register_p, true); register_p += executable_object_p->frame_ctx.context_depth; } while (register_p < stack_top_p) { ecma_ref_if_object (*register_p++); } ecma_ref_if_object (executable_object_p->iterator); JERRY_ASSERT (ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p)); executable_object_p->extended_object.u.cls.u2.executable_obj_flags |= ECMA_EXECUTABLE_OBJECT_RUNNING; executable_object_p->frame_ctx.prev_context_p = JERRY_CONTEXT (vm_top_context_p); JERRY_CONTEXT (vm_top_context_p) = &executable_object_p->frame_ctx; /* inside the generators the "new.target" is always "undefined" as it can't be invoked with "new" */ ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target_p); JERRY_CONTEXT (current_new_target_p) = NULL; #if JERRY_BUILTIN_REALMS ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); JERRY_CONTEXT (global_object_p) = ecma_op_function_get_realm (bytecode_header_p); #endif /* JERRY_BUILTIN_REALMS */ ecma_value_t result = vm_execute (&executable_object_p->frame_ctx); #if JERRY_BUILTIN_REALMS JERRY_CONTEXT (global_object_p) = saved_global_object_p; #endif /* JERRY_BUILTIN_REALMS */ JERRY_CONTEXT (current_new_target_p) = old_new_target; executable_object_p->extended_object.u.cls.u2.executable_obj_flags &= (uint8_t) ~ECMA_EXECUTABLE_OBJECT_RUNNING; if (executable_object_p->frame_ctx.call_operation != VM_EXEC_RETURN) { JERRY_ASSERT (executable_object_p->frame_ctx.call_operation == VM_NO_EXEC_OP); /* All resources are released. */ executable_object_p->extended_object.u.cls.u2.executable_obj_flags |= ECMA_EXECUTABLE_OBJECT_COMPLETED; return result; } JERRY_CONTEXT (vm_top_context_p) = executable_object_p->frame_ctx.prev_context_p; register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); stack_top_p = executable_object_p->frame_ctx.stack_top_p; if (executable_object_p->frame_ctx.context_depth > 0) { while (register_p < register_end_p) { ecma_deref_if_object (*register_p++); } vm_ref_lex_env_chain (executable_object_p->frame_ctx.lex_env_p, executable_object_p->frame_ctx.context_depth, register_p, false); register_p += executable_object_p->frame_ctx.context_depth; } while (register_p < stack_top_p) { ecma_deref_if_object (*register_p++); } ecma_deref_if_object (executable_object_p->iterator); return result; } /* opfunc_resume_executable_object */ /** * Fulfill the next promise of the async generator with the value */ void opfunc_async_generator_yield (ecma_extended_object_t *async_generator_object_p, /**< async generator object */ ecma_value_t value) /**< value (takes the reference) */ { ecma_async_generator_task_t *task_p; task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, async_generator_object_p->u.cls.u3.head); ecma_value_t iter_result = ecma_create_iter_result_object (value, ECMA_VALUE_FALSE); ecma_fulfill_promise (task_p->promise, iter_result); ecma_free_value (iter_result); ecma_free_value (value); ecma_value_t next = task_p->next; async_generator_object_p->u.cls.u3.head = next; JERRY_ASSERT (task_p->operation_value == ECMA_VALUE_UNDEFINED); jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); if (!ECMA_IS_INTERNAL_VALUE_NULL (next)) { ecma_value_t executable_object = ecma_make_object_value ((ecma_object_t *) async_generator_object_p); ecma_enqueue_promise_async_generator_job (executable_object); } } /* opfunc_async_generator_yield */ /** * Creates a new executable object and awaits for the value * * Note: * extra_flags can be used to set additional extra_info flags * * @return a new Promise object on success, error otherwise */ ecma_value_t opfunc_async_create_and_await (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t value, /**< awaited value (takes reference) */ uint16_t extra_flags) /**< extra flags */ { JERRY_ASSERT ( CBC_FUNCTION_GET_TYPE (frame_ctx_p->shared_p->bytecode_header_p->status_flags) == CBC_FUNCTION_ASYNC || (CBC_FUNCTION_GET_TYPE (frame_ctx_p->shared_p->bytecode_header_p->status_flags) == CBC_FUNCTION_ASYNC_ARROW)); ecma_object_t *promise_p = ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE); ecma_value_t result = ecma_promise_reject_or_resolve (ecma_make_object_value (promise_p), value, true); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (result)) { return result; } vm_executable_object_t *executable_object_p; executable_object_p = opfunc_create_executable_object (frame_ctx_p, VM_CREATE_EXECUTABLE_OBJECT_ASYNC); executable_object_p->extended_object.u.cls.u2.executable_obj_flags |= extra_flags; ecma_promise_async_then (result, ecma_make_object_value ((ecma_object_t *) executable_object_p)); ecma_deref_object ((ecma_object_t *) executable_object_p); ecma_free_value (result); result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_VALUE_UNDEFINED, promise_p); JERRY_ASSERT (ecma_is_value_object (result)); executable_object_p->iterator = result; return result; } /* opfunc_async_create_and_await */ /** * PrivateMethodOrAccessorAdd abstract operation. * * See also: ECMAScript v12, 7.3.29. * * @return ECMA_VALUE_ERROR - initialization fails * ECMA_VALUE_UNDEFINED - otherwise */ static ecma_value_t opfunc_private_method_or_accessor_add (ecma_object_t *class_object_p, /**< the function itself */ ecma_object_t *this_obj_p, /**< this object */ uint32_t static_flag) /**< static_flag */ { ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); ecma_property_t *prop_p = ecma_find_named_property (class_object_p, internal_string_p); if (prop_p == NULL) { return ECMA_VALUE_UNDEFINED; } ecma_value_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, ECMA_PROPERTY_VALUE_PTR (prop_p)->value); ecma_value_t *current_p = collection_p + 1; ecma_value_t *end_p = ecma_compact_collection_end (collection_p); while (current_p < end_p) { uint32_t prop_desc = *current_p++; ecma_private_property_kind_t kind = ECMA_PRIVATE_PROPERTY_KIND (prop_desc); if ((prop_desc & ECMA_PRIVATE_PROPERTY_STATIC_FLAG) != static_flag || kind == ECMA_PRIVATE_FIELD) { current_p += 2; continue; } ecma_string_t *prop_name_p = ecma_get_symbol_from_value (*current_p++); ecma_value_t method = *current_p++; JERRY_ASSERT (prop_name_p->u.hash & ECMA_SYMBOL_FLAG_PRIVATE_INSTANCE_METHOD); prop_p = ecma_find_named_property (this_obj_p, prop_name_p); ecma_object_t *method_p = ecma_get_object_from_value (method); if (kind == ECMA_PRIVATE_METHOD) { if (prop_p != NULL) { return ecma_raise_type_error (ECMA_ERR_CANNOT_DECLARE_SAME_PRIVATE_FIELD_TWICE); } ecma_property_value_t *prop_value_p = ecma_create_named_data_property (this_obj_p, prop_name_p, ECMA_PROPERTY_FIXED, NULL); prop_value_p->value = method; continue; } if (prop_p == NULL) { ecma_object_t *getter_p = (kind == ECMA_PRIVATE_GETTER) ? method_p : NULL; ecma_object_t *setter_p = (kind == ECMA_PRIVATE_SETTER) ? method_p : NULL; ecma_create_named_accessor_property (this_obj_p, prop_name_p, getter_p, setter_p, ECMA_PROPERTY_FIXED, NULL); continue; } ecma_property_value_t *accessor_objs_p = ECMA_PROPERTY_VALUE_PTR (prop_p); ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (accessor_objs_p); if (kind == ECMA_PRIVATE_GETTER) { ECMA_SET_POINTER (get_set_pair_p->getter_cp, method_p); } else { JERRY_ASSERT (kind == ECMA_PRIVATE_SETTER); ECMA_SET_POINTER (get_set_pair_p->setter_cp, method_p); } } return ECMA_VALUE_UNDEFINED; } /* opfunc_private_method_or_accessor_add */ /** * DefineField abstract operation. * * See also: ECMAScript v12, 7.3.32. * * @return ECMA_VALUE_ERROR - operation fails * ECMA_VALUE_{TRUE/FALSE} - otherwise */ ecma_value_t opfunc_define_field (ecma_value_t base, /**< base */ ecma_value_t property, /**< property */ ecma_value_t value) /**< value */ { ecma_string_t *property_key_p = ecma_op_to_property_key (property); JERRY_ASSERT (property_key_p != NULL); ecma_object_t *obj_p = ecma_get_object_from_value (base); ecma_property_descriptor_t desc = ecma_make_empty_property_descriptor (); desc.value = value; desc.flags = (JERRY_PROP_IS_WRITABLE | JERRY_PROP_IS_WRITABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE | JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE | JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_SHOULD_THROW); ecma_value_t result = ecma_op_object_define_own_property (obj_p, property_key_p, &desc); ecma_deref_ecma_string (property_key_p); return result; } /* opfunc_define_field */ /** * Initialize class fields. * * @return ECMA_VALUE_ERROR - initialization fails * ECMA_VALUE_UNDEFINED - otherwise */ ecma_value_t opfunc_init_class_fields (ecma_object_t *class_object_p, /**< the function itself */ ecma_value_t this_val) /**< this_arg of the function */ { JERRY_ASSERT (ecma_is_value_object (this_val)); ecma_object_t *this_obj_p = ecma_get_object_from_value (this_val); ecma_value_t result = opfunc_private_method_or_accessor_add (class_object_p, this_obj_p, 0); if (ECMA_IS_VALUE_ERROR (result)) { return result; } ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_INIT); ecma_property_t *property_p = ecma_find_named_property (class_object_p, name_p); if (property_p == NULL) { return ECMA_VALUE_UNDEFINED; } vm_frame_ctx_shared_class_fields_t shared_class_fields; shared_class_fields.header.status_flags = VM_FRAME_CTX_SHARED_HAS_CLASS_FIELDS; shared_class_fields.computed_class_fields_p = NULL; name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED); ecma_property_t *class_field_property_p = ecma_find_named_property (class_object_p, name_p); if (class_field_property_p != NULL) { ecma_value_t value = ECMA_PROPERTY_VALUE_PTR (class_field_property_p)->value; shared_class_fields.computed_class_fields_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, value); } ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); JERRY_ASSERT (ecma_op_is_callable (property_value_p->value)); ecma_extended_object_t *ext_function_p; ext_function_p = (ecma_extended_object_t *) ecma_get_object_from_value (property_value_p->value); shared_class_fields.header.bytecode_header_p = ecma_op_function_get_compiled_code (ext_function_p); shared_class_fields.header.function_object_p = &ext_function_p->object; ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_function_p->u.function.scope_cp); result = vm_run (&shared_class_fields.header, this_val, scope_p); JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result) || result == ECMA_VALUE_UNDEFINED); return result; } /* opfunc_init_class_fields */ /** * Initialize static class fields. * * @return ECMA_VALUE_ERROR - initialization fails * ECMA_VALUE_UNDEFINED - otherwise */ ecma_value_t opfunc_init_static_class_fields (ecma_value_t function_object, /**< the function itself */ ecma_value_t this_val) /**< this_arg of the function */ { JERRY_ASSERT (ecma_op_is_callable (function_object)); JERRY_ASSERT (ecma_is_value_object (this_val)); ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED); ecma_object_t *function_object_p = ecma_get_object_from_value (function_object); ecma_property_t *class_field_property_p = ecma_find_named_property (function_object_p, name_p); vm_frame_ctx_shared_class_fields_t shared_class_fields; shared_class_fields.header.function_object_p = function_object_p; shared_class_fields.header.status_flags = VM_FRAME_CTX_SHARED_HAS_CLASS_FIELDS; shared_class_fields.computed_class_fields_p = NULL; if (class_field_property_p != NULL) { ecma_value_t value = ECMA_PROPERTY_VALUE_PTR (class_field_property_p)->value; shared_class_fields.computed_class_fields_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, value); } ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) function_object_p; shared_class_fields.header.bytecode_header_p = ecma_op_function_get_compiled_code (ext_function_p); ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_function_p->u.function.scope_cp); ecma_value_t result = vm_run (&shared_class_fields.header, this_val, scope_p); JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result) || result == ECMA_VALUE_UNDEFINED); return result; } /* opfunc_init_static_class_fields */ /** * Add the name of a computed field to a name list * * @return ECMA_VALUE_ERROR - name is not a valid property name * ECMA_VALUE_UNDEFINED - otherwise */ ecma_value_t opfunc_add_computed_field (ecma_value_t class_object, /**< class object */ ecma_value_t name) /**< name of the property */ { ecma_string_t *prop_name_p = ecma_op_to_property_key (name); if (JERRY_UNLIKELY (prop_name_p == NULL)) { return ECMA_VALUE_ERROR; } if (ecma_prop_name_is_symbol (prop_name_p)) { name = ecma_make_symbol_value (prop_name_p); } else { name = ecma_make_string_value (prop_name_p); } ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED); ecma_object_t *class_object_p = ecma_get_object_from_value (class_object); ecma_property_t *property_p = ecma_find_named_property (class_object_p, name_p); ecma_value_t *compact_collection_p; ecma_property_value_t *property_value_p; if (property_p == NULL) { ECMA_CREATE_INTERNAL_PROPERTY (class_object_p, name_p, property_p, property_value_p); compact_collection_p = ecma_new_compact_collection (); } else { property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, property_value_p->value); } compact_collection_p = ecma_compact_collection_push_back (compact_collection_p, name); ECMA_SET_INTERNAL_VALUE_POINTER (property_value_p->value, compact_collection_p); return ECMA_VALUE_UNDEFINED; } /* opfunc_add_computed_field */ /** * Create implicit class constructor * * See also: ECMAScript v6, 14.5.14 * * @return - new external function ecma-object */ ecma_value_t opfunc_create_implicit_class_constructor (uint8_t opcode, /**< current cbc opcode */ const ecma_compiled_code_t *bytecode_p) /**< current byte code */ { /* 8. */ ecma_value_t script_value = ((cbc_uint8_arguments_t *) bytecode_p)->script_value; cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); if (JERRY_UNLIKELY (script_p->refs_and_type >= CBC_SCRIPT_REF_MAX)) { jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT); } ecma_object_t *function_object_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE), sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION); ecma_extended_object_t *constructor_object_p = (ecma_extended_object_t *) function_object_p; script_p->refs_and_type += CBC_SCRIPT_REF_ONE; constructor_object_p->u.constructor_function.script_value = script_value; constructor_object_p->u.constructor_function.flags = 0; /* 10.a.i */ if (opcode == CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR_HERITAGE) { constructor_object_p->u.constructor_function.flags |= ECMA_CONSTRUCTOR_FUNCTION_HAS_HERITAGE; } ecma_property_value_t *prop_value_p; prop_value_p = ecma_create_named_data_property (function_object_p, ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), ECMA_PROPERTY_FLAG_CONFIGURABLE, NULL); prop_value_p->value = ecma_make_uint32_value (0); return ecma_make_object_value (function_object_p); } /* opfunc_create_implicit_class_constructor */ /** * Set the [[HomeObject]] attribute of the given function object * * @return void */ void opfunc_set_home_object (ecma_object_t *func_p, /**< function object */ ecma_object_t *parent_env_p) /**< parent environment */ { JERRY_ASSERT (ecma_is_lexical_environment (parent_env_p)); if (ecma_get_object_type (func_p) == ECMA_OBJECT_TYPE_FUNCTION) { ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) func_p; ECMA_SET_NON_NULL_POINTER_TAG (ext_func_p->u.function.scope_cp, parent_env_p, JMEM_CP_GET_POINTER_TAG_BITS (ext_func_p->u.function.scope_cp)); } } /* opfunc_set_home_object */ /** * Make private key from descriptor * * @return pointer to private key */ ecma_string_t * opfunc_make_private_key (ecma_value_t descriptor) /**< descriptor */ { ecma_string_t *private_key_p = ecma_new_symbol_from_descriptor_string (descriptor); private_key_p->u.hash |= ECMA_SYMBOL_FLAG_PRIVATE_KEY; return (ecma_string_t *) private_key_p; } /* opfunc_make_private_key */ /** * Find a private property in the private elements internal property given the key * * @return pointer to the private property - if it is found, * NULL - otherwise */ static ecma_property_t * opfunc_find_private_key (ecma_object_t *class_object_p, /**< class environment */ ecma_object_t *obj_p, /**< object */ ecma_string_t *search_key_p, /**< key */ ecma_string_t **out_private_key_p) /**< [out] private key */ { if (ecma_op_object_is_fast_array (obj_p)) { return NULL; } ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); ecma_property_t *prop_p = ecma_find_named_property (class_object_p, internal_string_p); if (prop_p == NULL) { return NULL; } ecma_value_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, ECMA_PROPERTY_VALUE_PTR (prop_p)->value); ecma_value_t *current_p = collection_p + 1; ecma_value_t *end_p = ecma_compact_collection_end (collection_p); while (current_p < end_p) { current_p++; /* skip kind */ ecma_string_t *private_key_p = ecma_get_prop_name_from_value (*current_p++); current_p++; /* skip value */ JERRY_ASSERT (ecma_prop_name_is_symbol (private_key_p)); ecma_string_t *private_key_desc_p = ecma_get_string_from_value (((ecma_extended_string_t *) private_key_p)->u.symbol_descriptor); if (ecma_compare_ecma_strings (private_key_desc_p, search_key_p)) { prop_p = ecma_find_named_property (obj_p, private_key_p); if (out_private_key_p) { *out_private_key_p = private_key_p; } return prop_p; } } return NULL; } /* opfunc_find_private_key */ /** * PrivateElementFind abstract operation * * See also: ECMAScript v12, 7.3.27 * * @return - ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_EMPTY - otherwise */ static ecma_property_t * opfunc_find_private_element (ecma_object_t *obj_p, /**< object */ ecma_string_t *key_p, /**< key */ ecma_string_t **private_key_p, /**< [out] private key */ bool allow_heritage) /**< heritage flag */ { JERRY_ASSERT (private_key_p != NULL); JERRY_ASSERT (*private_key_p == NULL); ecma_object_t *lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p; while (true) { JERRY_ASSERT (lex_env_p != NULL); if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) != 0 && !ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { ecma_object_t *class_object_p = ((ecma_lexical_environment_class_t *) lex_env_p)->object_p; ecma_property_t *prop_p = opfunc_find_private_key (class_object_p, obj_p, key_p, private_key_p); if (prop_p || *private_key_p != NULL) { /* Found non shadowed property */ return prop_p; } if (!allow_heritage) { return NULL; } } if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) { break; } lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } return NULL; } /* opfunc_find_private_element */ /** * In expression runtime evaluation in case of private identifiers * * See also: ECMAScript v12, 13.10.1 * * @return - ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_TRUE - if the property was found in the base object * ECMA_VALUE_FALSE - otherwise */ ecma_value_t opfunc_private_in (ecma_value_t base, /**< base */ ecma_value_t property) /**< property */ { if (!ecma_is_value_object (base)) { return ecma_raise_type_error (ECMA_ERR_RIGHT_VALUE_OF_IN_MUST_BE_AN_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (base); ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (property); ecma_string_t *private_key_p = NULL; ecma_property_t *prop_p = opfunc_find_private_element (obj_p, prop_name_p, &private_key_p, false); return ecma_make_boolean_value (prop_p != NULL); } /* opfunc_private_in */ /** * PrivateFieldAdd abstract operation * * See also: ECMAScript v12, 7.3.28 * * @return - ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t opfunc_private_field_add (ecma_value_t base, /**< base object */ ecma_value_t property, /**< property name */ ecma_value_t value) /**< ecma value */ { ecma_object_t *obj_p = ecma_get_object_from_value (base); ecma_string_t *prop_name_p = ecma_get_string_from_value (property); ecma_string_t *private_key_p = NULL; ecma_property_t *prop_p = opfunc_find_private_element (obj_p, prop_name_p, &private_key_p, false); if (prop_p != NULL) { return ecma_raise_type_error (ECMA_ERR_CANNOT_DECLARE_SAME_PRIVATE_FIELD_TWICE); } ecma_property_value_t *value_p = ecma_create_named_data_property (obj_p, private_key_p, ECMA_PROPERTY_FLAG_WRITABLE, NULL); value_p->value = ecma_copy_value_if_not_object (value); return ECMA_VALUE_EMPTY; } /* opfunc_private_field_add */ /** * PrivateSet abstract operation * * See also: ECMAScript v12, 7.3.31 * * @return - ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t opfunc_private_set (ecma_value_t base, /**< this object */ ecma_value_t property, /**< property name */ ecma_value_t value) /**< ecma value */ { ecma_value_t base_obj = ecma_op_to_object (base); if (ECMA_IS_VALUE_ERROR (base_obj)) { return base_obj; } ecma_object_t *obj_p = ecma_get_object_from_value (base_obj); ecma_string_t *prop_name_p = ecma_get_string_from_value (property); ecma_string_t *private_key_p = NULL; ecma_property_t *prop_p = opfunc_find_private_element (obj_p, prop_name_p, &private_key_p, true); ecma_value_t result; if (prop_p == NULL) { result = ecma_raise_type_error (ECMA_ERR_CANNOT_WRITE_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT); } else if (*prop_p & ECMA_PROPERTY_FLAG_DATA) { JERRY_ASSERT (ecma_prop_name_is_symbol (private_key_p)); if (private_key_p->u.hash & ECMA_SYMBOL_FLAG_PRIVATE_INSTANCE_METHOD) { result = ecma_raise_type_error (ECMA_ERR_PRIVATE_METHOD_IS_NOT_WRITABLE); } else { ecma_value_assign_value (&ECMA_PROPERTY_VALUE_PTR (prop_p)->value, value); result = ecma_copy_value (value); } } else { ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (ECMA_PROPERTY_VALUE_PTR (prop_p)); if (get_set_pair_p->setter_cp == JMEM_CP_NULL) { result = ecma_raise_type_error (ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_SETTER); } else { ecma_object_t *setter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp); result = ecma_op_function_call (setter_p, base, &value, 1); } } ecma_deref_object (obj_p); return result; } /* opfunc_private_set */ /** * PrivateGet abstract operation * * See also: ECMAScript v12, 7.3.30 * * @return - ECMA_VALUE_ERROR - if the operation fails * private property value - otherwise */ ecma_value_t opfunc_private_get (ecma_value_t base, /**< this object */ ecma_value_t property) /**< property name */ { ecma_value_t base_obj = ecma_op_to_object (base); if (ECMA_IS_VALUE_ERROR (base_obj)) { return base_obj; } ecma_object_t *obj_p = ecma_get_object_from_value (base_obj); ecma_string_t *prop_name_p = ecma_get_string_from_value (property); ecma_string_t *private_key_p = NULL; ecma_property_t *prop_p = opfunc_find_private_element (obj_p, prop_name_p, &private_key_p, true); ecma_value_t result; if (prop_p == NULL) { result = ecma_raise_type_error (ECMA_ERR_CANNOT_READ_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT); } else if (*prop_p & ECMA_PROPERTY_FLAG_DATA) { result = ecma_copy_value (ECMA_PROPERTY_VALUE_PTR (prop_p)->value); } else { ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (ECMA_PROPERTY_VALUE_PTR (prop_p)); if (get_set_pair_p->getter_cp == JMEM_CP_NULL) { result = ecma_raise_type_error (ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_GETTER); } else { ecma_object_t *getter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp); result = ecma_op_function_call (getter_p, base, NULL, 0); } } ecma_deref_object (obj_p); return result; } /* opfunc_private_get */ /** * Find the private property in the object who's private key descriptor matches the given key * * @return pointer to private key */ static ecma_string_t * opfunc_create_private_key (ecma_value_t *collection_p, /**< collection of private properties */ ecma_value_t search_key, /**< key */ ecma_private_property_kind_t search_kind) /**< kind of the property */ { if (search_kind < ECMA_PRIVATE_GETTER) { return opfunc_make_private_key (search_key); } ecma_string_t *search_key_p = ecma_get_string_from_value (search_key); ecma_value_t *current_p = collection_p + 1; ecma_value_t *end_p = ecma_compact_collection_end (collection_p); while (current_p < end_p) { ecma_private_property_kind_t kind = ECMA_PRIVATE_PROPERTY_KIND (*current_p++); ecma_string_t *private_key_p = ecma_get_prop_name_from_value (*current_p++); current_p++; /* skip value */ if (kind < ECMA_PRIVATE_GETTER) { continue; } JERRY_ASSERT (ecma_prop_name_is_symbol (private_key_p)); ecma_string_t *private_key_desc_p = ecma_get_string_from_value (((ecma_extended_string_t *) private_key_p)->u.symbol_descriptor); if (ecma_compare_ecma_strings (private_key_desc_p, search_key_p)) { ecma_deref_ecma_string (search_key_p); ecma_ref_ecma_string (private_key_p); return private_key_p; } } return opfunc_make_private_key (search_key); } /* opfunc_create_private_key */ /** * Collect private members for PrivateMethodOrAccessorAdd and PrivateFieldAdd abstract operations */ void opfunc_collect_private_properties (ecma_value_t constructor, /**< constructor */ ecma_value_t prop_name, /**< property name */ ecma_value_t value, /**< value */ uint8_t opcode) /**< opcode */ { ecma_private_property_kind_t kind = ECMA_PRIVATE_FIELD; bool is_static = false; if (opcode >= CBC_EXT_COLLECT_PRIVATE_STATIC_FIELD) { opcode = (uint8_t) (opcode - PARSER_STATIC_PRIVATE_TO_PRIVATE_OFFSET); is_static = true; } if (opcode == CBC_EXT_COLLECT_PRIVATE_METHOD) { prop_name ^= value; value ^= prop_name; prop_name ^= value; kind = ECMA_PRIVATE_METHOD; } else if (opcode == CBC_EXT_COLLECT_PRIVATE_GETTER) { kind = ECMA_PRIVATE_GETTER; } else if (opcode == CBC_EXT_COLLECT_PRIVATE_SETTER) { kind = ECMA_PRIVATE_SETTER; } JERRY_ASSERT (ecma_is_value_object (constructor)); JERRY_ASSERT (ecma_is_value_string (prop_name)); JERRY_ASSERT (ecma_is_value_object (value) || ecma_is_value_undefined (value)); ecma_object_t *constructor_p = ecma_get_object_from_value (constructor); ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); ecma_property_t *prop_p = ecma_find_named_property (constructor_p, internal_string_p); ecma_value_t *collection_p; ecma_property_value_t *prop_value_p; if (prop_p == NULL) { collection_p = ecma_new_compact_collection (); ECMA_CREATE_INTERNAL_PROPERTY (constructor_p, internal_string_p, prop_p, prop_value_p); ECMA_SET_INTERNAL_VALUE_POINTER (prop_value_p->value, collection_p); } else { prop_value_p = ECMA_PROPERTY_VALUE_PTR (prop_p); collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, prop_value_p->value); } ecma_string_t *key_p = opfunc_create_private_key (collection_p, prop_name, kind); if (kind != ECMA_PRIVATE_FIELD) { key_p->u.hash |= ECMA_SYMBOL_FLAG_PRIVATE_INSTANCE_METHOD; } if (is_static) { kind = (ecma_private_property_kind_t) ((int) kind | (int) ECMA_PRIVATE_PROPERTY_STATIC_FLAG); } collection_p = ecma_compact_collection_push_back (collection_p, (ecma_value_t) kind); collection_p = ecma_compact_collection_push_back (collection_p, ecma_make_symbol_value (key_p)); collection_p = ecma_compact_collection_push_back (collection_p, value); #ifndef JERRY_NDEBUG ecma_value_t *end_p = ecma_compact_collection_end (collection_p); ecma_value_t *current_p = collection_p + 1; JERRY_ASSERT ((end_p - current_p) % ECMA_PRIVATE_ELEMENT_LIST_SIZE == 0); #endif /* !defined (JERRY_NDEBUG) */ ECMA_SET_INTERNAL_VALUE_POINTER (prop_value_p->value, collection_p); ecma_free_value (value); } /* opfunc_collect_private_properties */ /** * ClassDefinitionEvaluation environment initialization part * * See also: ECMAScript v6, 14.5.14 */ void opfunc_push_class_environment (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t **vm_stack_top, /**< VM stack top */ ecma_value_t class_name) /**< class name */ { JERRY_ASSERT (ecma_is_value_string (class_name)); ecma_object_t *class_env_p = ecma_create_decl_lex_env (frame_ctx_p->lex_env_p); /* 4.a */ ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (class_env_p, ecma_get_string_from_value (class_name), ECMA_PROPERTY_FLAG_ENUMERABLE, NULL); property_value_p->value = ECMA_VALUE_UNINITIALIZED; frame_ctx_p->lex_env_p = class_env_p; *(*vm_stack_top)++ = ECMA_VALUE_RELEASE_LEX_ENV; } /* opfunc_push_class_environment */ /** * ClassDefinitionEvaluation object initialization part * * See also: ECMAScript v6, 14.5.14 * * @return - ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t opfunc_init_class (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t *stack_top_p) /**< stack top */ { /* 5.b, 6.e.ii */ ecma_object_t *ctor_parent_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); ecma_object_t *proto_parent_p = NULL; bool free_proto_parent = false; ecma_value_t super_class = stack_top_p[-2]; ecma_object_t *ctor_p = ecma_get_object_from_value (stack_top_p[-1]); bool heritage_present = !ecma_is_value_array_hole (super_class); /* 5. ClassHeritage opt is not present */ if (!heritage_present) { /* 5.a */ proto_parent_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); } else if (!ecma_is_value_null (super_class)) { /* 6.f, 6.g.i */ if (!ecma_is_constructor (super_class)) { return ecma_raise_type_error (ECMA_ERR_CLASS_EXTENDS_NOT_CONSTRUCTOR); } ecma_object_t *parent_p = ecma_get_object_from_value (super_class); /* 6.g.ii */ ecma_value_t proto_parent = ecma_op_object_get_by_magic_id (parent_p, LIT_MAGIC_STRING_PROTOTYPE); /* 6.g.iii */ if (ECMA_IS_VALUE_ERROR (proto_parent)) { return proto_parent; } /* 6.g.iv */ if (ecma_is_value_object (proto_parent)) { proto_parent_p = ecma_get_object_from_value (proto_parent); free_proto_parent = true; } else if (ecma_is_value_null (proto_parent)) { proto_parent_p = NULL; } else { ecma_free_value (proto_parent); return ecma_raise_type_error (ECMA_ERR_PROPERTY_PROTOTYPE_IS_NOT_AN_OBJECT); } /* 6.g.v */ ctor_parent_p = parent_p; } /* 7. */ ecma_object_t *proto_p = ecma_create_object (proto_parent_p, 0, ECMA_OBJECT_TYPE_GENERAL); ecma_value_t proto = ecma_make_object_value (proto_p); ECMA_SET_POINTER (ctor_p->u2.prototype_cp, ctor_parent_p); if (free_proto_parent) { ecma_deref_object (proto_parent_p); } ecma_free_value (super_class); /* 16. */ ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (ctor_p, ecma_get_magic_string (LIT_MAGIC_STRING_PROTOTYPE), ECMA_PROPERTY_FIXED, NULL); property_value_p->value = proto; /* 18. */ property_value_p = ecma_create_named_data_property (proto_p, ecma_get_magic_string (LIT_MAGIC_STRING_CONSTRUCTOR), ECMA_PROPERTY_CONFIGURABLE_WRITABLE, NULL); property_value_p->value = ecma_make_object_value (ctor_p); if (ecma_get_object_type (ctor_p) == ECMA_OBJECT_TYPE_FUNCTION) { opfunc_bind_class_environment (frame_ctx_p->lex_env_p, proto_p, ctor_p, ctor_p); /* 15. set F’s [[ConstructorKind]] internal slot to "derived". */ if (heritage_present) { ECMA_SET_THIRD_BIT_TO_POINTER_TAG (((ecma_extended_object_t *) ctor_p)->u.function.scope_cp); } } stack_top_p[-2] = stack_top_p[-1]; stack_top_p[-1] = proto; return ECMA_VALUE_EMPTY; } /* opfunc_init_class */ /** * Creates a new class lexical environment and binds the bound object and the class's object * * @return newly created class lexical environment - if func_obj_p is not present * NULL - otherwise, also the environment is set as the func_obj_p's scope */ ecma_object_t * opfunc_bind_class_environment (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_object_t *home_object_p, /**< bound object */ ecma_object_t *ctor_p, /**< constructor object */ ecma_object_t *func_obj_p) /**< function object */ { ecma_object_t *proto_env_p = ecma_create_lex_env_class (lex_env_p, sizeof (ecma_lexical_environment_class_t)); ECMA_SET_NON_NULL_POINTER (proto_env_p->u1.bound_object_cp, home_object_p); ((ecma_lexical_environment_class_t *) proto_env_p)->object_p = ctor_p; ((ecma_lexical_environment_class_t *) proto_env_p)->type = ECMA_LEX_ENV_CLASS_TYPE_CLASS_ENV; if (func_obj_p) { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION); ECMA_SET_NON_NULL_POINTER_TAG (((ecma_extended_object_t *) func_obj_p)->u.function.scope_cp, proto_env_p, 0); ecma_deref_object (proto_env_p); return NULL; } return proto_env_p; } /* opfunc_bind_class_environment */ /** * Set [[Enumerable]] and [[HomeObject]] attributes for all class method */ static void opfunc_set_class_attributes (ecma_object_t *obj_p, /**< object */ ecma_object_t *parent_env_p) /**< parent environment */ { jmem_cpointer_t prop_iter_cp = obj_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prop_iter_cp = prop_iter_p->next_property_cp; } } #endif /* JERRY_PROPERTY_HASHMAP */ while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *property_pair_p = (ecma_property_pair_t *) prop_iter_p; for (uint32_t index = 0; index < ECMA_PROPERTY_PAIR_ITEM_COUNT; index++) { uint8_t property = property_pair_p->header.types[index]; if (!ECMA_PROPERTY_IS_RAW (property)) { JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_DELETED || (ECMA_PROPERTY_IS_INTERNAL (property) && LIT_INTERNAL_MAGIC_STRING_IGNORED (property_pair_p->names_cp[index]))); continue; } if (property & ECMA_PROPERTY_FLAG_DATA) { if (ecma_is_value_object (property_pair_p->values[index].value) && ecma_is_property_enumerable (property)) { property_pair_p->header.types[index] = (uint8_t) (property & ~ECMA_PROPERTY_FLAG_ENUMERABLE); opfunc_set_home_object (ecma_get_object_from_value (property_pair_p->values[index].value), parent_env_p); } continue; } property_pair_p->header.types[index] = (uint8_t) (property & ~ECMA_PROPERTY_FLAG_ENUMERABLE); ecma_property_value_t *accessor_objs_p = property_pair_p->values + index; ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (accessor_objs_p); if (get_set_pair_p->getter_cp != JMEM_CP_NULL) { opfunc_set_home_object (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp), parent_env_p); } if (get_set_pair_p->setter_cp != JMEM_CP_NULL) { opfunc_set_home_object (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp), parent_env_p); } } prop_iter_cp = prop_iter_p->next_property_cp; } } /* opfunc_set_class_attributes */ /** * Set [[HomeObject]] attributes for all class private elements */ static void opfunc_set_private_instance_method_attributes (ecma_object_t *class_object_p, /**< class constructor */ ecma_object_t *parent_env_p) /**< parent environment */ { ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); ecma_property_t *prop_p = ecma_find_named_property (class_object_p, internal_string_p); if (prop_p == NULL) { return; } ecma_value_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, ECMA_PROPERTY_VALUE_PTR (prop_p)->value); ecma_value_t *current_p = collection_p + 1; ecma_value_t *end_p = ecma_compact_collection_end (collection_p); while (current_p < end_p) { current_p += 2; /* skip kind, name */ ecma_value_t value = *current_p++; if (!ecma_is_value_undefined (value)) { opfunc_set_home_object (ecma_get_object_from_value (value), parent_env_p); } } } /* opfunc_set_private_instance_method_attributes */ /** * Pop the current lexical environment referenced by the frame context */ void opfunc_pop_lexical_environment (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { ecma_object_t *outer_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, frame_ctx_p->lex_env_p->u2.outer_reference_cp); ecma_deref_object (frame_ctx_p->lex_env_p); frame_ctx_p->lex_env_p = outer_env_p; } /* opfunc_pop_lexical_environment */ /** * ClassDefinitionEvaluation finalization part * * See also: ECMAScript v6, 14.5.14 */ void opfunc_finalize_class (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t **vm_stack_top_p, /**< current vm stack top */ ecma_value_t class_name) /**< class name */ { JERRY_ASSERT (ecma_is_value_undefined (class_name) || ecma_is_value_string (class_name)); ecma_value_t *stack_top_p = *vm_stack_top_p; ecma_object_t *ctor_p = ecma_get_object_from_value (stack_top_p[-2]); ecma_object_t *proto_p = ecma_get_object_from_value (stack_top_p[-1]); ecma_object_t *class_env_p = frame_ctx_p->lex_env_p; /* 23.a */ if (!ecma_is_value_undefined (class_name)) { ecma_op_initialize_binding (class_env_p, ecma_get_string_from_value (class_name), stack_top_p[-2]); } ecma_object_t *ctor_env_p = opfunc_bind_class_environment (class_env_p, ctor_p, ctor_p, NULL); ecma_object_t *proto_env_p = opfunc_bind_class_environment (class_env_p, proto_p, ctor_p, NULL); opfunc_set_class_attributes (ctor_p, ctor_env_p); opfunc_set_class_attributes (proto_p, proto_env_p); opfunc_set_private_instance_method_attributes (ctor_p, proto_env_p); ecma_deref_object (proto_env_p); ecma_deref_object (ctor_env_p); ecma_deref_object (proto_p); JERRY_ASSERT ((ecma_is_value_undefined (class_name) ? stack_top_p[-3] == ECMA_VALUE_UNDEFINED : stack_top_p[-3] == ECMA_VALUE_RELEASE_LEX_ENV)); /* only the current class remains on the stack */ if (stack_top_p[-3] == ECMA_VALUE_RELEASE_LEX_ENV) { opfunc_pop_lexical_environment (frame_ctx_p); } opfunc_private_method_or_accessor_add (ctor_p, ctor_p, ECMA_PRIVATE_PROPERTY_STATIC_FLAG); stack_top_p[-3] = stack_top_p[-2]; *vm_stack_top_p -= 2; } /* opfunc_finalize_class */ /** * MakeSuperPropertyReference operation * * See also: ECMAScript v6, 12.3.5.3 * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t opfunc_form_super_reference (ecma_value_t **vm_stack_top_p, /**< current vm stack top */ vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t prop_name, /**< property name to resolve */ uint8_t opcode) /**< current cbc opcode */ { ecma_environment_record_t *environment_record_p = ecma_op_get_environment_record (frame_ctx_p->lex_env_p); if (environment_record_p && !ecma_op_this_binding_is_initialized (environment_record_p)) { return ecma_raise_reference_error (ECMA_ERR_CALL_SUPER_CONSTRUCTOR_DERIVED_CLASS_BEFORE_THIS); } ecma_value_t parent = ecma_op_resolve_super_base (frame_ctx_p->lex_env_p); if (ECMA_IS_VALUE_ERROR (parent)) { return ecma_raise_type_error (ECMA_ERR_INVOKE_NULLABLE_SUPER_METHOD); } if (!ecma_op_require_object_coercible (parent)) { return ECMA_VALUE_ERROR; } ecma_value_t *stack_top_p = *vm_stack_top_p; if (opcode >= CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE) { JERRY_ASSERT (opcode == CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE || opcode == CBC_EXT_SUPER_PROP_LITERAL_ASSIGNMENT_REFERENCE); *stack_top_p++ = parent; *stack_top_p++ = ecma_copy_value (prop_name); *vm_stack_top_p = stack_top_p; return ECMA_VALUE_EMPTY; } ecma_object_t *parent_p = ecma_get_object_from_value (parent); ecma_string_t *prop_name_p = ecma_op_to_property_key (prop_name); if (prop_name_p == NULL) { ecma_deref_object (parent_p); return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_op_object_get_with_receiver (parent_p, prop_name_p, frame_ctx_p->this_binding); ecma_deref_ecma_string (prop_name_p); ecma_deref_object (parent_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } if (opcode == CBC_EXT_SUPER_PROP_LITERAL_REFERENCE || opcode == CBC_EXT_SUPER_PROP_REFERENCE) { *stack_top_p++ = ecma_copy_value (frame_ctx_p->this_binding); *stack_top_p++ = ECMA_VALUE_UNDEFINED; } *stack_top_p++ = result; *vm_stack_top_p = stack_top_p; return ECMA_VALUE_EMPTY; } /* opfunc_form_super_reference */ /** * Assignment operation for SuperRefence base * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t opfunc_assign_super_reference (ecma_value_t **vm_stack_top_p, /**< vm stack top */ vm_frame_ctx_t *frame_ctx_p, /**< frame context */ uint32_t opcode_data) /**< opcode data to store the result */ { ecma_value_t *stack_top_p = *vm_stack_top_p; ecma_value_t base_obj = ecma_op_to_object (stack_top_p[-3]); if (ECMA_IS_VALUE_ERROR (base_obj)) { return base_obj; } ecma_object_t *base_obj_p = ecma_get_object_from_value (base_obj); ecma_string_t *prop_name_p = ecma_op_to_property_key (stack_top_p[-2]); if (prop_name_p == NULL) { ecma_deref_object (base_obj_p); return ECMA_VALUE_ERROR; } bool is_strict = (frame_ctx_p->status_flags & VM_FRAME_CTX_IS_STRICT) != 0; ecma_value_t result = ecma_op_object_put_with_receiver (base_obj_p, prop_name_p, stack_top_p[-1], frame_ctx_p->this_binding, is_strict); ecma_deref_ecma_string (prop_name_p); ecma_deref_object (base_obj_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } for (int32_t i = 1; i <= 3; i++) { ecma_free_value (stack_top_p[-i]); } stack_top_p -= 3; if (opcode_data & VM_OC_PUT_STACK) { *stack_top_p++ = result; } else if (opcode_data & VM_OC_PUT_BLOCK) { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); VM_GET_REGISTERS (frame_ctx_p)[0] = result; } *vm_stack_top_p = stack_top_p; return result; } /* opfunc_assign_super_reference */ /** * Copy data properties of an object * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_EMPTY - otherwise */ ecma_value_t opfunc_copy_data_properties (ecma_value_t target_object, /**< target object */ ecma_value_t source_object, /**< source object */ ecma_value_t filter_array) /**< filter array */ { bool source_to_object = false; if (!ecma_is_value_object (source_object)) { source_object = ecma_op_to_object (source_object); if (ECMA_IS_VALUE_ERROR (source_object)) { return source_object; } source_to_object = true; } ecma_object_t *source_object_p = ecma_get_object_from_value (source_object); ecma_collection_t *names_p = ecma_op_object_own_property_keys (source_object_p, JERRY_PROPERTY_FILTER_ALL); #if JERRY_BUILTIN_PROXY if (names_p == NULL) { JERRY_ASSERT (!source_to_object); return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ ecma_object_t *target_object_p = ecma_get_object_from_value (target_object); ecma_value_t *buffer_p = names_p->buffer_p; ecma_value_t *buffer_end_p = buffer_p + names_p->item_count; ecma_value_t *filter_start_p = NULL; ecma_value_t *filter_end_p = NULL; ecma_value_t result = ECMA_VALUE_EMPTY; if (filter_array != ECMA_VALUE_UNDEFINED) { ecma_object_t *filter_array_p = ecma_get_object_from_value (filter_array); JERRY_ASSERT (ecma_get_object_type (filter_array_p) == ECMA_OBJECT_TYPE_ARRAY); JERRY_ASSERT (ecma_op_object_is_fast_array (filter_array_p)); if (filter_array_p->u1.property_list_cp != JMEM_CP_NULL) { filter_start_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, filter_array_p->u1.property_list_cp); filter_end_p = filter_start_p + ((ecma_extended_object_t *) filter_array_p)->u.array.length; } } while (buffer_p < buffer_end_p) { ecma_string_t *property_name_p = ecma_get_prop_name_from_value (*buffer_p++); if (filter_start_p != NULL) { ecma_value_t *filter_p = filter_start_p; do { if (ecma_compare_ecma_strings (property_name_p, ecma_get_prop_name_from_value (*filter_p))) { break; } } while (++filter_p < filter_end_p); if (filter_p != filter_end_p) { continue; } } ecma_property_descriptor_t descriptor; result = ecma_op_object_get_own_property_descriptor (source_object_p, property_name_p, &descriptor); if (ECMA_IS_VALUE_ERROR (result)) { break; } if (result == ECMA_VALUE_FALSE) { continue; } if (!(descriptor.flags & JERRY_PROP_IS_ENUMERABLE)) { ecma_free_property_descriptor (&descriptor); continue; } if ((descriptor.flags & JERRY_PROP_IS_VALUE_DEFINED) && !ECMA_OBJECT_IS_PROXY (source_object_p)) { result = descriptor.value; } else { ecma_free_property_descriptor (&descriptor); result = ecma_op_object_get (source_object_p, property_name_p); if (ECMA_IS_VALUE_ERROR (result)) { break; } } opfunc_set_data_property (target_object_p, property_name_p, result); ecma_free_value (result); result = ECMA_VALUE_EMPTY; } if (JERRY_UNLIKELY (source_to_object)) { ecma_deref_object (source_object_p); } ecma_collection_free (names_p); return result; } /* opfunc_copy_data_properties */ /** * Check whether the current lexical scope has restricted binding declaration with the given name * * Steps are include ES11: 8.1.1.4.14 HasRestrictedGlobalProperty abstract operation * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_TRUE - if it has restricted property binding * ECMA_VALUE_FALSE - otherwise */ ecma_value_t opfunc_lexical_scope_has_restricted_binding (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_string_t *name_p) /**< binding name */ { JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; ecma_property_t *binding_p = ecma_find_named_property (lex_env_p, name_p); if (binding_p != NULL) { return ECMA_VALUE_TRUE; } ecma_object_t *global_obj_p = ecma_get_object_from_value (frame_ctx_p->this_binding); #if JERRY_BUILTIN_REALMS ecma_object_t *const global_scope_p = ecma_get_global_scope ((ecma_object_t *) JERRY_CONTEXT (global_object_p)); #else /* !JERRY_BUILTIN_REALMS */ ecma_object_t *const global_scope_p = ecma_get_global_scope (global_obj_p); #endif /* JERRY_BUILTIN_REALMS */ if (global_scope_p != lex_env_p) { return ECMA_VALUE_FALSE; } #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (global_obj_p)) { ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_proxy_object_get_own_property_descriptor (global_obj_p, name_p, &prop_desc); if (ecma_is_value_true (status)) { status = ecma_make_boolean_value ((prop_desc.flags & JERRY_PROP_IS_CONFIGURABLE) == 0); ecma_free_property_descriptor (&prop_desc); } return status; } #endif /* JERRY_BUILTIN_PROXY */ ecma_property_t property = ecma_op_object_get_own_property (global_obj_p, name_p, NULL, ECMA_PROPERTY_GET_NO_OPTIONS); JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_NOT_FOUND || ECMA_PROPERTY_IS_FOUND (property)); return ecma_make_boolean_value (property != ECMA_PROPERTY_TYPE_NOT_FOUND && !ecma_is_property_configurable (property)); } /* opfunc_lexical_scope_has_restricted_binding */ /** * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/pointer.h000664 001750 001750 00000173244 15164251010 036513 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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 "uri.h" #include "internal/itoa.h" #include "error/error.h" // PointerParseErrorCode #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(switch-enum) #elif defined(_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 /////////////////////////////////////////////////////////////////////////////// // 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 > \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 class GenericPointer { public: typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value typedef typename ValueType::Ch Ch; //!< Character type from Value typedef GenericUri UriType; //! 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& 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(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } //! Copy constructor. GenericPointer(const GenericPointer& rhs, Allocator* allocator) : 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 ownAllocator 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; } //! Swap the content of this pointer with another. /*! \param other The pointer to swap with. \note Constant complexity. */ GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { internal::Swap(allocator_, other.allocator_); internal::Swap(ownAllocator_, other.ownAllocator_); internal::Swap(nameBuffer_, other.nameBuffer_); internal::Swap(tokens_, other.tokens_); internal::Swap(tokenCount_, other.tokenCount_); internal::Swap(parseErrorOffset_, other.parseErrorOffset_); internal::Swap(parseErrorCode_, other.parseErrorCode_); 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.pointer, b.pointer); // ... } \endcode \see Swap() */ friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } //@} //!@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 RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::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& name, Allocator* allocator = 0) const { return Append(name.c_str(), static_cast(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(end - buffer); buffer[length] = '\0'; if (sizeof(Ch) == 1) { Token token = { reinterpret_cast(buffer), length, index }; return Append(token, allocator); } else { Ch name[21]; for (size_t i = 0; i <= length; i++) name[i] = static_cast(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(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); } //! Less than operator. /*! \note Invalid pointers are always greater than valid ones. */ bool operator<(const GenericPointer& rhs) const { if (!IsValid()) return false; if (!rhs.IsValid()) return true; if (tokenCount_ != rhs.tokenCount_) return tokenCount_ < rhs.tokenCount_; for (size_t i = 0; i < tokenCount_; i++) { if (tokens_[i].index != rhs.tokens_[i].index) return tokens_[i].index < rhs.tokens_[i].index; if (tokens_[i].length != rhs.tokens_[i].length) return tokens_[i].length < rhs.tokens_[i].length; if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) return cmp < 0; } return false; } //@} //!@name Stringify //@{ //! Stringify the pointer into string representation. /*! \tparam OutputStream Type of output stream. \param os The output stream. */ template bool Stringify(OutputStream& os) const { return Stringify(os); } //! Stringify the pointer into URI fragment representation. /*! \tparam OutputStream Type of output stream. \param os The output stream. */ template bool StringifyUriFragment(OutputStream& os) const { return Stringify(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(GenericValue(GenericStringRef(t->name, t->length))); if (m == v->MemberEnd()) { v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); m = v->MemberEnd(); v = &(--m)->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 ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { return Create(document, document.GetAllocator(), alreadyExist); } //@} //!@name Compute URI //@{ //! Compute the in-scope URI for a subtree. // For use with JSON pointers into JSON schema documents. /*! \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \param rootUri Root URI \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. \param allocator Allocator for Uris \return Uri if it can be resolved. Otherwise null. \note There are only 3 situations when a URI cannot be resolved: 1. A value in the path is neither 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. */ UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { static const Ch kIdString[] = { 'i', 'd', '\0' }; static const ValueType kIdValue(kIdString, 2); UriType base = UriType(rootUri, allocator); RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { switch (v->GetType()) { case kObjectType: { // See if we have an id, and if so resolve with the current base typename ValueType::MemberIterator m = v->FindMember(kIdValue); if (m != v->MemberEnd() && (m->value).IsString()) { UriType here = UriType(m->value, allocator).Resolve(base, allocator); base = here; } m = v->FindMember(GenericValue(GenericStringRef(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(t - tokens_); return UriType(allocator); } return base; } UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { return GetUri(const_cast(root), rootUri, unresolvedTokenIndex, allocator); } //!@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 neither 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(GenericValue(GenericStringRef(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(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(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; ValueType& 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; ValueType& 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& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; ValueType& 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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (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 ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator()); } //! Query a value in a document with default null-terminated string. template ValueType& GetWithDefault(GenericDocument& 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 ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& 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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) GetWithDefault(GenericDocument& 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& 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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (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 ValueType& Set(GenericDocument& document, ValueType& value) const { return Create(document) = value; } //! Set a value in a document, with copy semantics. template ValueType& Set(GenericDocument& document, const ValueType& value) const { return Create(document).CopyFrom(value, document.GetAllocator()); } //! Set a null-terminated string in a document. template ValueType& Set(GenericDocument& 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 ValueType& Set(GenericDocument& document, const std::basic_string& 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 RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) Set(GenericDocument& 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 ValueType& Swap(GenericDocument& 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(GenericValue(GenericStringRef(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(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(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); nameBuffer_ = reinterpret_cast(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(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); Ch* name = nameBuffer_ = reinterpret_cast(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 os(name); Ch* begin = os.PutBegin(); if (!Transcoder, 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(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(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 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 source(&t->name[j]); PercentEncodeStream target(os); if (!Transcoder >().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(c << 4); Ch h = *src_; if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); else { valid_ = false; return 0; } src_++; } return c; } size_t Tell() const { return static_cast(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 class PercentEncodeStream { public: PercentEncodeStream(OutputStream& os) : os_(os) {} void Put(char c) { // UTF-8 must be byte unsigned char u = static_cast(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(hexDigits[u >> 4])); os_.Put(static_cast(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 Pointer; //!@name Helper functions for GenericPointer //@{ ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { return pointer.Create(root, a); } template typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Create(root, a); } // No allocator parameter template typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { return pointer.Create(document); } template typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { return GenericPointer(source, N - 1).Create(document); } ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { return pointer.Get(root, unresolvedTokenIndex); } template const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { return pointer.Get(root, unresolvedTokenIndex); } template typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); } template const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); } ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { return pointer.GetWithDefault(root, defaultValue, a); } template typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { return pointer.GetWithDefault(root, defaultValue, a); } #if RAPIDJSON_HAS_STDSTRING template typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { return pointer.GetWithDefault(root, defaultValue, a); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { return pointer.GetWithDefault(root, defaultValue, a); } template typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); } template typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); } #if RAPIDJSON_HAS_STDSTRING template typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); } // No allocator parameter template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { return pointer.GetWithDefault(document, defaultValue); } template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { return pointer.GetWithDefault(document, defaultValue); } #if RAPIDJSON_HAS_STDSTRING template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { return pointer.GetWithDefault(document, defaultValue); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { return pointer.GetWithDefault(document, defaultValue); } template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); } template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); } #if RAPIDJSON_HAS_STDSTRING template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); } ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } #if RAPIDJSON_HAS_STDSTRING template typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } #if RAPIDJSON_HAS_STDSTRING template typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } // No allocator parameter template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { return pointer.Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { return pointer.Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { return pointer.Set(document, value); } #if RAPIDJSON_HAS_STDSTRING template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { return pointer.Set(document, value); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { return pointer.Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { return GenericPointer(source, N - 1).Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { return GenericPointer(source, N - 1).Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { return GenericPointer(source, N - 1).Set(document, value); } #if RAPIDJSON_HAS_STDSTRING template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { return GenericPointer(source, N - 1).Set(document, value); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { return GenericPointer(source, N - 1).Set(document, value); } ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { return pointer.Swap(root, value, a); } template typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Swap(root, value, a); } template typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { return pointer.Swap(document, value); } template typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { return GenericPointer(source, N - 1).Swap(document, value); } ////////////////////////////////////////////////////////////////////////////// template bool EraseValueByPointer(T& root, const GenericPointer& pointer) { return pointer.Erase(root); } template bool EraseValueByPointer(T& root, const CharType(&source)[N]) { return GenericPointer(source, N - 1).Erase(root); } //@} RAPIDJSON_NAMESPACE_END #if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_POINTER_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/inc/thorvg.h000664 001750 001750 00000332124 15164251010 031455 0ustar00ddennedyddennedy000000 000000 #ifndef _THORVG_H_ #define _THORVG_H_ #include #include #include #include #define TVG_VERSION_MAJOR 1 // for compile-time checks #define TVG_VERSION_MINOR 0 // for compile-time checks #define TVG_VERSION_MICRO 1 // for compile-time checks #ifdef TVG_API #undef TVG_API #endif #ifndef TVG_STATIC #ifdef _WIN32 #if TVG_BUILD #define TVG_API __declspec(dllexport) #else #define TVG_API __declspec(dllimport) #endif #elif (defined(__SUNPRO_C) || defined(__SUNPRO_CC)) #define TVG_API __global #else #if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__INTEL_COMPILER) #define TVG_API __attribute__ ((visibility("default"))) #else #define TVG_API #endif #endif #else #define TVG_API #endif #ifdef TVG_DEPRECATED #undef TVG_DEPRECATED #endif #ifdef _WIN32 #define TVG_DEPRECATED __declspec(deprecated) #elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) #define TVG_DEPRECATED __attribute__ ((__deprecated__)) #else #define TVG_DEPRECATED #endif #define _TVG_DECLARE_PRIVATE(A) \ protected: \ A(const A&) = delete; \ const A& operator=(const A&) = delete; \ A() #define _TVG_DECLARE_PRIVATE_BASE(A) \ _TVG_DECLARE_PRIVATE(A); \ public: \ struct Impl; \ Impl* pImpl #define _TVG_DECLARE_PRIVATE_DERIVE(A) \ _TVG_DECLARE_PRIVATE(A); \ protected: \ ~A() {} #define _TVG_DISABLE_CTOR(A) \ A() = delete; \ ~A() = delete #define _TVG_DECLARE_ACCESSOR(A) \ friend A namespace tvg { struct RenderMethod; struct Animation; struct Shape; /** * @defgroup ThorVG ThorVG * @brief ThorVG classes and enumerations providing C++ APIs. */ /**@{*/ /** * @brief Enumeration specifying the result from the APIs. * * All ThorVG APIs could potentially return one of the values in the list. * Please note that some APIs may additionally specify the reasons that trigger their return values. * */ enum struct Result { Success = 0, ///< The value returned in case of a correct request execution. InvalidArguments, ///< The value returned in the event of a problem with the arguments given to the API - e.g. empty paths or null pointers. InsufficientCondition, ///< The value returned in case the request cannot be processed - e.g. asking for properties of an object, which does not exist. FailedAllocation, ///< The value returned in case of unsuccessful memory allocation. MemoryCorruption, ///< The value returned in the event of bad memory handling - e.g. failing in pointer releasing or casting NonSupport, ///< The value returned in case of choosing unsupported engine features(options). Unknown = 255 ///< The value returned in all other cases. }; /** * @brief Enumeration specifying the methods of combining the 8-bit color channels into 32-bit color. */ enum struct ColorSpace : uint8_t { ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. ARGB8888, ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. ABGR8888S, ///< The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. @since 0.12 ARGB8888S, ///< The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. @since 0.12 Grayscale8, ///< One single channel data. Unknown = 255 ///< Unknown channel data. This is reserved for an initial ColorSpace value. @since 1.0 }; /** * @brief Enumeration to specify rendering engine behavior. * * @note The availability or behavior of @c SmartRender may vary depending on platform or backend support. * It attempts to optimize rendering performance by updating only the regions of the canvas that have * changed between frames (partial redraw). This can be highly effective in scenarios where most of the * canvas remains static and only small portions are updated—such as simple animations or GUI interactions. * However, in complex scenes where a large portion of the canvas changes frequently (e.g., full-screen animations * or heavy object movements), the overhead of tracking changes and managing update regions may outweigh the benefits, * resulting in decreased performance compared to the default rendering mode. Thus, it is recommended to benchmark * both modes in your specific use case to determine the optimal setting. * * @since 1.0 */ enum struct EngineOption : uint8_t { None = 0, /**< No engine options are enabled. This may be used to explicitly disable all optional behaviors. */ Default = 1 << 0, /**< Uses the default rendering mode. */ SmartRender = 1 << 1 /**< Enables automatic partial (smart) rendering optimizations. */ }; /** * @brief Enumeration specifying the values of the path commands accepted by ThorVG. */ enum struct PathCommand : uint8_t { Close = 0, ///< Ends the current sub-path and connects it with its initial point. This command doesn't expect any points. MoveTo, ///< Sets a new initial point of the sub-path and a new current point. This command expects 1 point: the starting position. LineTo, ///< Draws a line from the current point to the given point and sets a new value of the current point. This command expects 1 point: the end-position of the line. CubicTo ///< Draws a cubic Bezier curve from the current point to the given point using two given control points and sets a new value of the current point. This command expects 3 points: the 1st control-point, the 2nd control-point, the end-point of the curve. }; /** * @brief Enumeration determining the ending type of a stroke in the open sub-paths. */ enum struct StrokeCap : uint8_t { Butt = 0, ///< The stroke ends exactly at each of the two end-points of a sub-path. For zero length sub-paths no stroke is rendered. Round, ///< The stroke is extended in both end-points of a sub-path by a half circle, with a radius equal to the half of a stroke width. For zero length sub-paths a full circle is rendered. Square ///< The stroke is extended in both end-points of a sub-path by a rectangle, with the width equal to the stroke width and the length equal to the half of the stroke width. For zero length sub-paths the square is rendered with the size of the stroke width. }; /** * @brief Enumeration determining the style used at the corners of joined stroked path segments. */ enum struct StrokeJoin : uint8_t { Miter = 0, ///< The outer corner of the joined path segments is spiked. The spike is created by extension beyond the join point of the outer edges of the stroke until they intersect. In case the extension goes beyond the limit, the join style is converted to the Bevel style. Round, ///< The outer corner of the joined path segments is rounded. The circular region is centered at the join point. Bevel ///< The outer corner of the joined path segments is bevelled at the join point. The triangular region of the corner is enclosed by a straight line between the outer corners of each stroke. }; /** * @brief Enumeration specifying how to fill the area outside the gradient bounds. */ enum struct FillSpread : uint8_t { Pad = 0, ///< The remaining area is filled with the closest stop color. Reflect, ///< The gradient pattern is reflected outside the gradient area until the expected region is filled. Repeat ///< The gradient pattern is repeated continuously beyond the gradient area until the expected region is filled. }; /** * @brief Enumeration specifying the algorithm used to establish which parts of the shape are treated as the inside of the shape. */ enum struct FillRule : uint8_t { NonZero = 0, ///< A line from the point to a location outside the shape is drawn. The intersections of the line with the path segment of the shape are counted. Starting from zero, if the path segment of the shape crosses the line clockwise, one is added, otherwise one is subtracted. If the resulting sum is non zero, the point is inside the shape. EvenOdd ///< A line from the point to a location outside the shape is drawn and its intersections with the path segments of the shape are counted. If the number of intersections is an odd number, the point is inside the shape. }; /** * @brief Enumeration indicating the method used in the mask of two objects - the target and the source. * * Notation: S(Source), T(Target), SA(Source Alpha), TA(Target Alpha) * * @see Paint::mask() */ enum struct MaskMethod : uint8_t { None = 0, ///< No Masking is applied. Alpha, ///< Alpha Masking using the masking target's pixels as an alpha value. InvAlpha, ///< Alpha Masking using the complement to the masking target's pixels as an alpha value. Luma, ///< Alpha Masking using the grayscale (0.2126R + 0.7152G + 0.0722*B) of the masking target's pixels. @since 0.9 InvLuma, ///< Alpha Masking using the grayscale (0.2126R + 0.7152G + 0.0722*B) of the complement to the masking target's pixels. @since 0.11 Add, ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) @since 1.0 Subtract, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) @since 1.0 Intersect, ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) @since 1.0 Difference, ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) @since 1.0 Lighten, ///< Where multiple masks intersect, the highest transparency value is used. @since 1.0 Darken ///< Where multiple masks intersect, the lowest transparency value is used. @since 1.0 }; /** * @brief Enumeration indicates the method used for blending paint. Please refer to the respective formulas for each method. * * Notation: S(source paint as the top layer), D(destination as the bottom layer), Sa(source paint alpha), Da(destination alpha) * * @see Paint::blend() * * @since 0.15 */ enum struct BlendMethod : uint8_t { Normal = 0, ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D Multiply, ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D) Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D) Overlay, ///< Combines Multiply and Screen blend modes. (2 * S * D) if (D < 128), otherwise 255 - 2 * (255 - S) * (255 - D) Darken, ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D) Lighten, ///< Only has the opposite action of Darken Only. max(S, D) ColorDodge, ///< Divides the bottom layer by the inverted top layer. D / (255 - S) ColorBurn, ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S HardLight, ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < 128), otherwise 255 - 2 * (255 - S) * (255 - D) SoftLight, ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (255 - 2 * S) * (D * D) + (2 * S * D) Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S) Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. S + D - (2 * S * D) Hue, ///< Combine with HSL(Sh + Ds + Dl) then convert it to RGB. @since 1.0 Saturation, ///< Combine with HSL(Dh + Ss + Dl) then convert it to RGB. @since 1.0 Color, ///< Combine with HSL(Sh + Ss + Dl) then convert it to RGB. @since 1.0 Luminosity, ///< Combine with HSL(Dh + Ds + Sl) then convert it to RGB. @since 1.0 Add, ///< Simply adds pixel values of one layer with the other. (S + D) Composition = 255 ///< For intermediate composition layers; suitable for use with Scene or Picture. @since 1.0 }; /** * @brief Enumeration that defines methods used for Scene Effects. * * This enum provides options to apply various post-processing effects to a scene. * Scene effects are typically applied to modify the final appearance of a rendered scene, such as blurring. * * @see Scene::add(SceneEffect effect, ...) * * @since 1.0 */ enum struct SceneEffect : uint8_t { Clear = 0, ///< Clear all previously applied scene effects, restoring the scene to its original state. GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(4) = {sigma(double)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]} DropShadow, ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(double)[0 - 360], distance(double), blur_sigma(double)[> 0], quality(int)[0 - 100]} Fill, ///< Override the scene content color with a given fill information. Param(4) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255]} Tint, ///< Tinting the current scene color with a given black, white color parameters. Param(7) = {black_R(int)[0 - 255], black_G(int)[0 - 255], black_B(int)[0 - 255], white_R(int)[0 - 255], white_G(int)[0 - 255], white_B(int)[0 - 255], intensity(double)[0 - 100]} Tritone ///< Apply a tritone color effect to the scene using three color parameters for shadows, midtones, and highlights. A blending factor determines the mix between the original color and the tritone colors. Param(9) = {Shadow_R(int)[0 - 255], Shadow_G(int)[0 - 255], Shadow_B(int)[0 - 255], Midtone_R(int)[0 - 255], Midtone_G(int)[0 - 255], Midtone_B(int)[0 - 255], Highlight_R(int)[0 - 255], Highlight_G(int)[0 - 255], Highlight_B(int)[0 - 255], Blend(int)[0 - 255]} }; /** * @brief Enumeration that defines methods used for wrapping text. * * This enum provides options to control how text is wrapped when it exceeds the available space. * Wrapping affects the layout and flow of text in the rendering area. * * @see Text::wrap(TextWrap mode) * * @since 1.0 */ enum struct TextWrap : uint8_t { None = 0, ///< Do not wrap text. Text is rendered on a single line and may overflow the bounding area. Character, ///< Wrap at the character level. If a word cannot fit, it is broken into individual characters to fit the line. Word, ///< Wrap at the word level. Words that do not fit are moved to the next line. Smart, ///< Smart choose wrapping method: word wrap first, falling back to character wrap if a word does not fit. Ellipsis ///< Truncate overflowing text and append an ellipsis ("...") at the end. Typically used for single-line labels. }; /** * @brief Enumeration specifying the ThorVG class type value. * * ThorVG's drawing objects can return class type values, allowing you to identify the specific class of each object. * * @see Paint::type() * @see Fill::type() * * @since 1.0 */ enum struct Type : uint8_t { Undefined = 0, ///< Unknown class Shape, ///< Shape class Scene, ///< Scene class Picture, ///< Picture class Text, ///< Text class LinearGradient = 10, ///< LinearGradient class RadialGradient ///< RadialGradient class }; /** * @brief A data structure representing a point in two-dimensional space. * * This structure defines a single point using Cartesian coordinates. * It is typically used for specifying positions or coordinates in 2D graphics. */ struct Point { float x; ///< The x-coordinate of the point. float y; ///< The y-coordinate of the point. }; /** * @brief A data structure representing a three-dimensional matrix. * * The elements e11, e12, e21 and e22 represent the rotation matrix, including the scaling factor. * The elements e13 and e23 determine the translation of the object along the x and y-axis, respectively. * The elements e31 and e32 are set to 0, e33 is set to 1. */ struct Matrix { float e11, e12, e13; float e21, e22, e23; float e31, e32, e33; }; /** * @brief Describes the font metrics of a text object. * * Provides the basic vertical layout metrics used for text rendering, * such as ascent, descent, and line spacing (linegap). * * @see Text::metrics() * @note Experimental API */ struct TextMetrics { float ascent; ///< Distance from the baseline to the top of the highest glyph (usually positive). float descent; ///< Distance from the baseline to the bottom of the lowest glyph (usually negative, as in TTF). float linegap; ///< Additional spacing recommended between lines (leading). float advance; ///< The total vertical advance between lines of text: ascent - descent + linegap (i.e., ascent + |descent| + linegap when descent is negative). }; /** * @class Paint * * @brief An abstract class for managing graphical elements. * * A graphical element in TVG is any object composed into a Canvas. * Paint represents such a graphical object and its behaviors such as duplication, transformation and composition. * TVG recommends the user to regard a paint as a set of volatile commands. They can prepare a Paint and then request a Canvas to run them. */ struct TVG_API Paint { /** * @brief Retrieves the parent paint object. * * This function returns a pointer to the parent object if the current paint * belongs to one. Otherwise, it returns @c nullptr. * * @return A pointer to the parent object if available, otherwise @c nullptr. * * @see Scene::add() * @see Canvas::add() * * @since 1.0 */ const Paint* parent() const noexcept; /** * @brief Sets the visibility of the Paint object. * * This is useful for selectively excluding paint objects during rendering. * * @param[in] on A boolean flag indicating visibility. The default is @c true. * @c true, the object will be rendered by the engine. * @c false, the object will be excluded from the drawing process. * * @note An invisible object is not considered inactive—it may still participate * in internal update processing if its properties are updated, but it will not * be taken into account for the final drawing output. To completely deactivate * a paint object, remove it from the canvas. * * @see Paint::visible() const * @see Result Canvas::remove(Paint* paint) * * @since 1.0 */ Result visible(bool on) noexcept; /** * @brief Sets the angle by which the object is rotated. * * The angle in measured clockwise from the horizontal axis. * The rotational axis passes through the point on the object with zero coordinates. * * @param[in] degree The value of the angle in degrees. * * @retval Result::InsufficientCondition in case a custom transform is applied. * @see Paint::transform() */ Result rotate(float degree) noexcept; /** * @brief Sets the scale value of the object. * * @param[in] factor The value of the scaling factor. The default value is 1. * * @retval Result::InsufficientCondition in case a custom transform is applied. * @see Paint::transform() */ Result scale(float factor) noexcept; /** * @brief Sets the values by which the object is moved in a two-dimensional space. * * The origin of the coordinate system is in the upper-left corner of the canvas. * The horizontal and vertical axes point to the right and down, respectively. * * @param[in] x The value of the horizontal shift. * @param[in] y The value of the vertical shift. * * @retval Result::InsufficientCondition in case a custom transform is applied. * @see Paint::transform() */ Result translate(float x, float y) noexcept; /** * @brief Sets the matrix of the affine transformation for the object. * * The augmented matrix of the transformation is expected to be given. * * @param[in] m The 3x3 augmented matrix. */ Result transform(const Matrix& m) noexcept; /** * @brief Gets the matrix of the affine transformation of the object. * * The values of the matrix can be set by the transform() API, as well by the translate(), * scale() and rotate(). In case no transformation was applied, the identity matrix is returned. * * @return The augmented transformation matrix. * * @since 0.4 */ Matrix& transform() noexcept; /** * @brief Sets the opacity of the object. * * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible. */ Result opacity(uint8_t o) noexcept; /** * @brief Sets the masking target object and the masking method. * * @param[in] target The paint of the target object. * @param[in] method The method used to mask the source object with the target. * * @retval Result::InsufficientCondition if the target has already belonged to another paint. * @retval Result::InvalidArguments @p method equals @c MaskMethod::None and @p target is not @c nullptr. */ Result mask(Paint* target, MaskMethod method) noexcept; /** * @brief Clip the drawing region of the paint object. * * This function restricts the drawing area of the paint object to the specified shape's paths. * * @param[in] clipper The shape object as the clipper. * * @retval Result::InsufficientCondition if the @p clipper has already belonged to another paint. * * @see Paint::clip() * * @since 1.0 */ Result clip(Shape* clipper) noexcept; /** * @brief Sets the blending method for the paint object. * * The blending feature allows you to combine colors to create visually appealing effects, including transparency, lighting, shading, and color mixing, among others. * its process involves the combination of colors or images from the source paint object with the destination (the lower layer image) using blending operations. * The blending operation is determined by the chosen @p BlendMethod, which specifies how the colors or images are combined. * * @param[in] method The blending method to be set. * * @since 1.0 */ Result blend(BlendMethod method) noexcept; /** * @brief Retrieves the object-oriented bounding box (OBB) of the paint object in canvas space. * * This function returns the bounding box of the paint, as an oriented bounding box (OBB) after transformations are applied. * The returned values @p pt4 may have invalid if the operation fails. Thus, please check the retval. * * This bounding box can be used to obtain the transformed bounding region in canvas space * by taking the geometry's axis-aligned bounding box (AABB) in the object's local coordinate space * and applying the object's transformations. * * @param[out] pt4 An array of four points representing the bounding box. The array size must be 4. * * @retval Result::InsufficientCondition If the paint has not been updated by the canvas. * * @see Paint::bounds(float* x, float* y, float* w, float* h) * @see Canvas::update() * * @since 1.0 */ Result bounds(Point* pt4) noexcept; /** * @brief Retrieves the axis-aligned bounding box (AABB) of the paint object in canvas space. * * Returns the bounding box of the paint as an axis-aligned bounding box (AABB), with all relevant transformations applied. * The returned values @p x, @p y, @p w, @p h, may have invalid if the operation fails. Thus, please check the retval. * * This bounding box can be used to determine the actual rendered area of the object on the canvas, * for purposes such as hit-testing, culling, or layout calculations. * * @param[out] x The x-coordinate of the upper-left corner of the bounding box. * @param[out] y The y-coordinate of the upper-left corner of the bounding box. * @param[out] w The width of the bounding box. * @param[out] h The height of the bounding box. * * @retval Result::InsufficientCondition If the paint has not been updated by the canvas. * * @see Paint::bounds(Point* pt4) * @see Canvas::update() */ Result bounds(float* x, float* y, float* w, float* h) noexcept; /** * @brief Checks whether a given region intersects the filled area of the paint. * * This function determines whether the specified rectangular region—defined by (`x`, `y`, `w`, `h`)— * intersects the geometric fill region of the paint object. * * This is useful for hit-testing purposes, such as detecting whether a user interaction (e.g., touch or click) * occurs within a painted region. * * The paint must be updated in a Canvas beforehand—typically after the Canvas has been * drawn and synchronized. * * @param[in] x The x-coordinate of the top-left corner of the test region. * @param[in] y The y-coordinate of the top-left corner of the test region. * @param[in] w The width of the region to test. Must be greater than 0; defaults to 1. * @param[in] h The height of the region to test. Must be greater than 0; defaults to 1. * * @return @c true if any part of the region intersects the filled area; otherwise, @c false. * * @note To test a single point, set the region size to w = 1, h = 1. * @note For efficiency, an AABB (axis-aligned bounding box) test is performed internally before precise hit detection. * @note This test does not take into account the results of blending or masking. * @note This test does take into account the the hidden paints as well. @see Paint::visible() * @since 1.0 */ bool intersects(int32_t x, int32_t y, int32_t w = 1, int32_t h = 1) noexcept; /** * @brief Duplicates the object. * * Creates a new object and sets its all properties as in the original object. * * @return The created object when succeed, @c nullptr otherwise. */ Paint* duplicate() const noexcept; /** * @brief Gets the opacity value of the object. * * @return The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. */ uint8_t opacity() const noexcept; /** * @brief Gets the masking target object and the masking method. * * @param[out] target The paint of the target object. * * @return The method used to mask the source object with the target. * * @since 0.5 */ MaskMethod mask(const Paint** target) const noexcept; /** * @brief Get the clipper shape of the paint object. * * This function returns the clipper that has been previously set to this paint object. * * @return The shape object used as the clipper, or @c nullptr if no clipper is set. * * @see Paint::clip(Shape* clipper) * * @since 1.0 */ Shape* clip() const noexcept; /** * @brief Gets the current visibility status of the Paint object. * * @return true if the object is visible and will be rendered. * false if the object is hidden and will not be rendered. * * @see Paint::visible(bool on) * * @since 1.0 */ bool visible() const noexcept; /** * @brief Increment the reference count for the Paint instance. * * This method increases the reference count of the Paint object, allowing shared ownership and control over its lifetime. * * @return The updated reference count after the increment by 1. * * @warning Please ensure that each call to ref() is paired with a corresponding call to unref() to prevent a dangling instance. * * @see Paint::unref() * @see Paint::refCnt() * * @since 1.0 */ uint16_t ref() noexcept; /** * @brief Decrement the reference count for the Paint instance. * * This method decreases the reference count of the Paint object by 1. * If the reference count reaches zero and the @p free flag is set to true, the Paint instance is automatically deleted. * * @param[in] free Flag indicating whether to delete the Paint instance when the reference count reaches zero. * * @return The updated reference count after the decrement. * * @see Paint::ref() * @see Paint::refCnt() * * @since 1.0 */ uint16_t unref(bool free = true) noexcept; /** * @brief Retrieve the current reference count of the Paint instance. * * This method provides the current reference count, allowing the user to check the shared ownership state of the Paint object. * * @return The current reference count of the Paint instance. * * @see Paint::ref() * @see Paint::unref() * * @since 1.0 */ uint16_t refCnt() const noexcept; /** * @brief Returns the ID value of this class. * * This method can be used to check the current concrete instance type. * * @return The class type ID of the Paint instance. * * @since 1.0 */ virtual Type type() const noexcept = 0; /** * @brief Unique ID of this instance. * * This is reserved to specify an paint instance in a scene. * * @since 1.0 */ uint32_t id = 0; /** * @brief Safely releases a Paint object. * * This is the counterpart to the `gen()` API, and releases the given Paint object safely, * handling @c nullptr and managing ownership properly. * * @param[in] paint A Paint object to release. * * @since 1.0 */ static void rel(Paint* paint) noexcept; protected: virtual ~Paint(); _TVG_DECLARE_PRIVATE_BASE(Paint); }; /** * @class Fill * * @brief An abstract class representing the gradient fill of the Shape object. * * It contains the information about the gradient colors and their arrangement * inside the gradient bounds. The gradients bounds are defined in the LinearGradient * or RadialGradient class, depending on the type of the gradient to be used. * It specifies the gradient behavior in case the area defined by the gradient bounds * is smaller than the area to be filled. */ struct TVG_API Fill { /** * @brief A data structure storing the information about the color and its relative position inside the gradient bounds. */ struct ColorStop { float offset; /**< The relative position of the color. */ uint8_t r; /**< The red color channel value in the range [0 ~ 255]. */ uint8_t g; /**< The green color channel value in the range [0 ~ 255]. */ uint8_t b; /**< The blue color channel value in the range [0 ~ 255]. */ uint8_t a; /**< The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. */ }; virtual ~Fill(); /** * @brief Sets the parameters of the colors of the gradient and their position. * * @param[in] colorStops An array of ColorStop data structure. * @param[in] cnt The count of the @p colorStops array equal to the colors number used in the gradient. */ Result colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept; /** * @brief Sets the FillSpread value, which specifies how to fill the area outside the gradient bounds. * * @param[in] s The FillSpread value. */ Result spread(FillSpread s) noexcept; /** * @brief Sets the matrix of the affine transformation for the gradient fill. * * The augmented matrix of the transformation is expected to be given. * * @param[in] m The 3x3 augmented matrix. */ Result transform(const Matrix& m) noexcept; /** * @brief Gets the parameters of the colors of the gradient, their position and number. * * @param[out] colorStops A pointer to the memory location, where the array of the gradient's ColorStop is stored. * * @return The number of colors used in the gradient. This value corresponds to the length of the @p colorStops array. */ uint32_t colorStops(const ColorStop** colorStops) const noexcept; /** * @brief Gets the FillSpread value of the fill. * * @return The FillSpread value of this Fill. */ FillSpread spread() const noexcept; /** * @brief Gets the matrix of the affine transformation of the gradient fill. * * In case no transformation was applied, the identity matrix is returned. * * @return The augmented transformation matrix. */ Matrix& transform() const noexcept; /** * @brief Creates a copy of the Fill object. * * Return a newly created Fill object with the properties copied from the original. * * @return A copied Fill object when succeed, @c nullptr otherwise. */ Fill* duplicate() const noexcept; /** * @brief Returns the ID value of this class. * * This method can be used to check the current concrete instance type. * * @return The class type ID of the Fill instance. * * @since 1.0 */ virtual Type type() const noexcept = 0; _TVG_DECLARE_PRIVATE_BASE(Fill); }; /** * @class Canvas * * @brief An abstract class for drawing graphical elements. * * A canvas is an entity responsible for drawing the target. It sets up the drawing engine and the buffer, which can be drawn on the screen. It also manages given Paint objects. * * @note A Canvas behavior depends on the raster engine though the final content of the buffer is expected to be identical. * @warning The Paint objects belonging to one Canvas can't be shared among multiple Canvases. */ struct TVG_API Canvas { virtual ~Canvas(); /** * @brief Returns the list of paints currently held by the Canvas. * * This function provides a list of paint nodes, allowing users to access scene-graph information. * * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync(). * @see Canvas::add() * @see Canvas::remove() * * @warning This is read-only. Do not modify the list. * @note 1.0 */ const std::list& paints() const noexcept; /** * @brief Adds a paint object to the canvas root scene. * * Appends a paint object to the root scene of the canvas. If the optional @p at * parameter is provided, the paint object is inserted immediately before the * specified paint in the root scene. If @p at is @c nullptr, the paint object * is appended to the end of the root scene. * * @param[in] target A pointer to the Paint object to be added to the root scene. * This parameter must not be @c nullptr. * @param[in] at A pointer to an existing Paint object in the root scene * before which @p target will be inserted. If @c nullptr, * @p target is appended to the end of the root scene. * The default value is @c nullptr. * * @note Ownership of the @p target object is transferred to the canvas upon * successful addition. To retain ownership, call @ref Paint::ref() * before adding it to the canvas. * @note The rendering order of paint objects follows their order in the root * scene. If layering is required, ensure the paints are added in the * desired order. * * @see Canvas::paints() * @see Canvas::remove() * @see Paint::ref() * * @since 1.0 */ Result add(Paint* target, Paint* at = nullptr) noexcept; /** * @brief Removes a paint object or all paint objects from the root scene. * * This function removes a specified paint object from the root scene. If no paint * object is specified (i.e., the default @c nullptr is used), the function * performs to clear all paints from the root scene. * * @param[in] paint A pointer to the Paint object to be removed from the root scene. * If @c nullptr, remove all the paints from the root scene. * * @see Canvas::add() * @see Canvas::paints() * * @since 1.0 */ Result remove(Paint* paint = nullptr) noexcept; /** * @brief Requests the canvas to update modified paint objects in preparation for rendering. * * This function triggers an internal update for all paint instances that have been modified * since the last update. It ensures that the canvas state is ready for accurate rendering. * * @retval Result::InsufficientCondition The canvas is not properly prepared. * This may occur if the canvas target has not been set or if the update is called during drawing. * Call Canvas::sync() before trying. * * @note Only paint objects that have been changed will be processed. * @note If the canvas is configured with multiple threads, the update may be performed asynchronously. * * @see Canvas::sync() */ Result update() noexcept; /** * @brief Requests the canvas to render the Paint objects. * * @param[in] clear If @c true, clears the target buffer to zero before drawing. * * @retval Result::InsufficientCondition The canvas is not properly prepared. * This may occur if Canvas::target() has not been set or if draw() is called multiple times * without calling Canvas::sync() in between. * * @note Clearing the buffer is unnecessary if the canvas will be fully covered * with opaque content. Skipping the clear can improve performance. * @note Drawing may be performed asynchronously if the thread count is greater than zero. * To ensure the drawing process is complete, call sync() afterwards. * @note If the canvas has not been updated prior to Canvas::draw(), it may implicitly perform Canvas::update(). * * @see Canvas::sync() * @see Canvas::update() */ Result draw(bool clear = false) noexcept; /** * @brief Sets the drawing region of the canvas. * * This function defines a rectangular area of the canvas to be used for drawing operations. * The specified viewport clips rendering output to the boundaries of that rectangle. * * Please note that changing the viewport is only allowed at the beginning of the rendering sequence—that is, after calling Canvas::sync(). * * @param[in] x The x-coordinate of the upper-left corner of the rectangle. * @param[in] y The y-coordinate of the upper-left corner of the rectangle. * @param[in] w The width of the rectangle. * @param[in] h The height of the rectangle. * * @retval Result::InsufficientCondition If the canvas is not in a synced state. * * @see Canvas::sync() * @see SwCanvas::target() * @see GlCanvas::target() * @see WgCanvas::target() * * @warning Changing the viewport is not allowed after calling Canvas::add(), * Canvas::remove(), Canvas::update(), or Canvas::draw(). * * @note When the target is reset, the viewport will also be reset to match the target size. * @since 0.15 */ Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept; /** * @brief Guarantees that drawing task is finished. * * The Canvas rendering can be performed asynchronously. To make sure that rendering is finished, * the sync() must be called after the draw() regardless of threading. * * @see Canvas::draw() */ Result sync() noexcept; _TVG_DECLARE_PRIVATE_BASE(Canvas); }; /** * @class LinearGradient * * @brief A class representing the linear gradient fill of the Shape object. * * Besides the APIs inherited from the Fill class, it enables setting and getting the linear gradient bounds. * The behavior outside the gradient bounds depends on the value specified in the spread API. * * @warning This class is not designed for inheritance. */ struct TVG_API LinearGradient : Fill { /** * @brief Sets the linear gradient bounds. * * The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing * the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking * (@p x1, @p y1) and (@p x2, @p y2). * * @param[in] x1 The horizontal coordinate of the first point used to determine the gradient bounds. * @param[in] y1 The vertical coordinate of the first point used to determine the gradient bounds. * @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds. * @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds. * * @note In case the first and the second points are equal, an object is filled with a single color using the last color specified in the colorStops(). * @see Fill::colorStops() */ Result linear(float x1, float y1, float x2, float y2) noexcept; /** * @brief Gets the linear gradient bounds. * * The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing * the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking * (@p x1, @p y1) and (@p x2, @p y2). * * @param[out] x1 The horizontal coordinate of the first point used to determine the gradient bounds. * @param[out] y1 The vertical coordinate of the first point used to determine the gradient bounds. * @param[out] x2 The horizontal coordinate of the second point used to determine the gradient bounds. * @param[out] y2 The vertical coordinate of the second point used to determine the gradient bounds. */ Result linear(float* x1, float* y1, float* x2, float* y2) const noexcept; /** * @brief Creates a new LinearGradient object. * * @return A new LinearGradient object. */ static LinearGradient* gen() noexcept; /** * @brief Returns the ID value of this class. * * This method can be used to check the current concrete instance type. * * @return The class type ID of the LinearGradient instance. * * @since 1.0 */ Type type() const noexcept override; _TVG_DECLARE_PRIVATE(LinearGradient); }; /** * @class RadialGradient * * @brief A class representing the radial gradient fill of the Shape object. * * @warning This class is not designed for inheritance. */ struct TVG_API RadialGradient : Fill { /** * @brief Sets the radial gradient attributes. * * The radial gradient is defined by the end circle with a center (@p cx, @p cy) and a radius @p r and * the start circle with a center/focal point (@p fx, @p fy) and a radius @p fr. * The gradient will be rendered such that the gradient stop at an offset of 100% aligns with the edge of the end circle * and the stop at an offset of 0% aligns with the edge of the start circle. * * @param[in] cx The horizontal coordinate of the center of the end circle. * @param[in] cy The vertical coordinate of the center of the end circle. * @param[in] r The radius of the end circle. * @param[in] fx The horizontal coordinate of the center of the start circle. * @param[in] fy The vertical coordinate of the center of the start circle. * @param[in] fr The radius of the start circle. * * @retval Result::InvalidArguments in case the radius @p r or @p fr value is negative. * * @note In case the radius @p r is zero, an object is filled with a single color using the last color specified in the colorStops(). * @note In case the focal point (@p fx and @p fy) lies outside the end circle, it is projected onto the edge of the end circle. * @note If the start circle doesn't fully fit inside the end circle (after possible repositioning), the @p fr is reduced accordingly. * @note By manipulating the position and size of the focal point, a wide range of visual effects can be achieved, such as directing * the gradient focus towards a specific edge or enhancing the depth and complexity of shading patterns. * If a focal effect is not desired, simply align the focal point (@p fx and @p fy) with the center of the end circle (@p cx and @p cy) * and set the radius (@p fr) to zero. This will result in a uniform gradient without any focal variations. */ Result radial(float cx, float cy, float r, float fx, float fy, float fr) noexcept; /** * @brief Gets the radial gradient attributes. * * @param[out] cx The horizontal coordinate of the center of the end circle. * @param[out] cy The vertical coordinate of the center of the end circle. * @param[out] r The radius of the end circle. * @param[out] fx The horizontal coordinate of the center of the start circle. * @param[out] fy The vertical coordinate of the center of the start circle. * @param[out] fr The radius of the start circle. * * @see RadialGradient::radial() */ Result radial(float* cx, float* cy, float* r, float* fx = nullptr, float* fy = nullptr, float* fr = nullptr) const noexcept; /** * @brief Creates a new RadialGradient object. * * @return A new RadialGradient object. */ static RadialGradient* gen() noexcept; /** * @brief Returns the ID value of this class. * * This method can be used to check the current concrete instance type. * * @return The class type ID of the LinearGradient instance. * * @since 1.0 */ Type type() const noexcept override; _TVG_DECLARE_PRIVATE(RadialGradient); }; /** * @class Shape * * @brief A class representing two-dimensional figures and their properties. * * A shape has three major properties: shape outline, stroking, filling. The outline in the Shape is retained as the path. * Path can be composed by accumulating primitive commands such as moveTo(), lineTo(), cubicTo(), or complete shape interfaces such as appendRect(), appendCircle(), etc. * Path can consists of sub-paths. One sub-path is determined by a close command. * * The stroke of Shape is an optional property in case the Shape needs to be represented with/without the outline borders. * It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context. * * @warning This class is not designed for inheritance. */ struct TVG_API Shape : Paint { /** * @brief Resets the shape path. * * The transformation matrix, color, fill, and stroke properties are retained. * * @note The memory where the path data is stored is not deallocated at this stage to allow for caching. */ Result reset() noexcept; /** * @brief Sets the initial point of the sub-path. * * The value of the current point is set to the given point. * * @param[in] x The horizontal coordinate of the initial point of the sub-path. * @param[in] y The vertical coordinate of the initial point of the sub-path. */ Result moveTo(float x, float y) noexcept; /** * @brief Adds a new point to the sub-path, which results in drawing a line from the current point to the given end-point. * * The value of the current point is set to the given end-point. * * @param[in] x The horizontal coordinate of the end-point of the line. * @param[in] y The vertical coordinate of the end-point of the line. * * @note In case this is the first command in the path, it corresponds to the moveTo() call. */ Result lineTo(float x, float y) noexcept; /** * @brief Adds new points to the sub-path, which results in drawing a cubic Bezier curve starting * at the current point and ending at the given end-point (@p x, @p y) using the control points (@p cx1, @p cy1) and (@p cx2, @p cy2). * * The value of the current point is set to the given end-point. * * @param[in] cx1 The horizontal coordinate of the 1st control point. * @param[in] cy1 The vertical coordinate of the 1st control point. * @param[in] cx2 The horizontal coordinate of the 2nd control point. * @param[in] cy2 The vertical coordinate of the 2nd control point. * @param[in] x The horizontal coordinate of the end-point of the curve. * @param[in] y The vertical coordinate of the end-point of the curve. * * @note In case this is the first command in the path, no data from the path are rendered. */ Result cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept; /** * @brief Closes the current sub-path by drawing a line from the current point to the initial point of the sub-path. * * The value of the current point is set to the initial point of the closed sub-path. * * @note In case the sub-path does not contain any points, this function has no effect. */ Result close() noexcept; /** * @brief Appends a rectangle to the path. * * The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments. * The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners. * * The position of the rectangle is specified by the coordinates of its upper-left corner - @p x and @p y arguments. * * The rectangle is treated as a new sub-path - it is not connected with the previous sub-path. * * The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater * than @p w/2 the current point is set to (@p x + @p w/2, @p y) * * @param[in] x The horizontal coordinate of the upper-left corner of the rectangle. * @param[in] y The vertical coordinate of the upper-left corner of the rectangle. * @param[in] w The width of the rectangle. * @param[in] h The height of the rectangle. * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle. * @param[in] ry The y-axis radius of the ellipse defining the rounded corners of the rectangle. * @param[in] cw Specifies the path direction: @c true for clockwise, @c false for counterclockwise. * * @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse. */ Result appendRect(float x, float y, float w, float h, float rx = 0, float ry = 0, bool cw = true) noexcept; /** * @brief Appends an ellipse to the path. * * The position of the ellipse is specified by the coordinates of its center - @p cx and @p cy arguments. * * The ellipse is treated as a new sub-path - it is not connected with the previous sub-path. * * The value of the current point is set to (@p cx, @p cy - @p ry). * * @param[in] cx The horizontal coordinate of the center of the ellipse. * @param[in] cy The vertical coordinate of the center of the ellipse. * @param[in] rx The x-axis radius of the ellipse. * @param[in] ry The y-axis radius of the ellipse. * @param[in] cw Specifies the path direction: @c true for clockwise, @c false for counterclockwise. * */ Result appendCircle(float cx, float cy, float rx, float ry, bool cw = true) noexcept; /** * @brief Appends a given sub-path to the path. * * The current point value is set to the last point from the sub-path. * For each command from the @p cmds array, an appropriate number of points in @p pts array should be specified. * If the number of points in the @p pts array is different than the number required by the @p cmds array, the shape with this sub-path will not be displayed on the screen. * * @param[in] cmds The array of the commands in the sub-path. * @param[in] cmdCnt The number of the sub-path's commands. * @param[in] pts The array of the two-dimensional points. * @param[in] ptsCnt The number of the points in the @p pts array. * * @note The interface is designed for optimal path setting if the caller has a completed path commands already. */ Result appendPath(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept; /** * @brief Sets the stroke width for the path. * * This function defines the thickness of the stroke applied to all figures * in the path object. A stroke is the outline drawn along the edges of the * path's geometry. * * @param[in] width The width of the stroke in pixels. Must be positive value. (The default is 0) * * @note A value of @p width 0 disables the stroke. * * @see strokeFill() */ Result strokeWidth(float width) noexcept; /** * @brief Sets the stroke color for the path. * * This function defines the RGBA color of the stroke applied to all figures * in the path object. The stroke color is used when rendering the outline * of the path geometry. * * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. * * @note If the stroke width is 0 (default), the stroke will not be visible regardless of the color. * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * * @see strokeWidth() * @see strokeFill(Fill* f) */ Result strokeFill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; /** * @brief Sets the gradient fill of the stroke for all of the figures from the path. * * @param[in] f The gradient fill. * * @retval Result::InvalidArgument In case a @c nullptr is passed as the argument. * * @note If the stroke width is 0 (default), the stroke will not be visible regardless of the color. * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * * @see strokeFill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) */ Result strokeFill(Fill* f) noexcept; /** * @brief Sets the dash pattern of the stroke. * * @param[in] dashPattern An array of alternating dash and gap lengths. * @param[in] cnt The length of the @p dashPattern array. * @param[in] offset The shift of the starting point within the repeating dash pattern, from which the pattern begins to be applied. * * @retval Result::InvalidArguments In case @p dashPattern is @c nullptr and @p cnt > 0 or @p dashPattern is not @c nullptr and @p cnt is zero. * * @note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt. * @note Values of @p dashPattern less than zero are treated as zero. * @note If all values in the @p dashPattern are equal to or less than 0, the dash is ignored. * @note If the @p dashPattern contains an odd number of elements, the sequence is repeated in the same * order to form an even-length pattern, preserving the alternation of dashes and gaps. * * @since 1.0 */ Result strokeDash(const float* dashPattern, uint32_t cnt, float offset = 0.0f) noexcept; /** * @brief Sets the cap style of the stroke in the open sub-paths. * * @param[in] cap The cap style value. The default value is @c StrokeCap::Square. * */ Result strokeCap(StrokeCap cap) noexcept; /** * @brief Sets the join style for stroked path segments. * * The join style is used for joining the two line segment while stroking the path. * * @param[in] join The join style value. The default value is @c StrokeJoin::Bevel. * */ Result strokeJoin(StrokeJoin join) noexcept; /** * @brief Sets the stroke miterlimit. * * @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4. * * @retval Result::InvalidArgument for @p miterlimit values less than zero. * * @since 0.11 */ Result strokeMiterlimit(float miterlimit) noexcept; /** * @brief Sets the trim of the shape along the defined path segment, allowing control over which part of the shape is visible. * * If the values of the arguments @p begin and @p end exceed the 0-1 range, they are wrapped around in a manner similar to angle wrapping, effectively treating the range as circular. * * @param[in] begin Specifies the start of the segment to display along the path. * @param[in] end Specifies the end of the segment to display along the path. * @param[in] simultaneous Determines how to trim multiple paths within a single shape. If set to @c true (default), trimming is applied simultaneously to all paths; * Otherwise, all paths are treated as a single entity with a combined length equal to the sum of their individual lengths and are trimmed as such. * * @since 1.0 */ Result trimpath(float begin, float end, bool simultaneous = true) noexcept; /** * @brief Sets the solid color for all of the figures from the path. * * The parts of the shape defined as inner are colored. * * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. */ Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; /** * @brief Sets the gradient fill for all of the figures from the path. * * The parts of the shape defined as inner are filled. * * @param[in] f The unique pointer to the gradient fill. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. */ Result fill(Fill* f) noexcept; /** * @brief Sets the fill rule for the shape. * * Specifies how the interior of the shape is determined when its path intersects itself. * The default fill rule is @c FillRule::NonZero. * * @param[in] r The fill rule to apply to the shape. */ Result fillRule(FillRule r) noexcept; /** * @brief Sets the rendering order of the stroke and the fill. * * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option). * * @since 0.10 */ Result order(bool strokeFirst) noexcept; /** * @brief Retrieves the current path data of the shape. * * This function provides access to the shape's path data, including the commands * and points that define the path. * * @param[out] cmds Pointer to the array of commands representing the path. * Can be @c nullptr if this information is not needed. * @param[out] cmdsCnt Pointer to the variable that receives the number of commands in the @p cmds array. * Can be @c nullptr if this information is not needed. * @param[out] pts Pointer to the array of two-dimensional points that define the path. * Can be @c nullptr if this information is not needed. * @param[out] ptsCnt Pointer to the variable that receives the number of points in the @p pts array. * Can be @c nullptr if this information is not needed. */ Result path(const PathCommand** cmds, uint32_t* cmdsCnt, const Point** pts, uint32_t* ptsCnt) const noexcept; /** * @brief Gets the pointer to the gradient fill of the shape. * * @return The pointer to the gradient fill of the stroke when succeed, @c nullptr in case no fill was set. */ const Fill* fill() const noexcept; /** * @brief Gets the solid color of the shape. * * @param[out] r The red color channel value in the range [0 ~ 255]. * @param[out] g The green color channel value in the range [0 ~ 255]. * @param[out] b The blue color channel value in the range [0 ~ 255]. * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * */ Result fill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; /** * @brief Retrieves the current fill rule used by the shape. * * This function returns the fill rule, which determines how the interior * regions of the shape are calculated when it overlaps itself. * * @see Shape::fillRule(FillRule r) * @return The current FillRule value of the shape. */ FillRule fillRule() const noexcept; /** * @brief Gets the stroke width. * * @return The stroke width value when succeed, zero if no stroke was set. */ float strokeWidth() const noexcept; /** * @brief Gets the color of the shape's stroke. * * @param[out] r The red color channel value in the range [0 ~ 255]. * @param[out] g The green color channel value in the range [0 ~ 255]. * @param[out] b The blue color channel value in the range [0 ~ 255]. * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * */ Result strokeFill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; /** * @brief Gets the pointer to the gradient fill of the stroke. * * @return The pointer to the gradient fill of the stroke when succeed, @c nullptr otherwise. */ const Fill* strokeFill() const noexcept; /** * @brief Gets the dash pattern of the stroke. * * @param[out] dashPattern The pointer to the memory, where the dash pattern array is stored. * @param[out] offset The shift of the starting point within the repeating dash pattern. * * @return The length of the @p dashPattern array. * * @since 1.0 */ uint32_t strokeDash(const float** dashPattern, float* offset = nullptr) const noexcept; /** * @brief Gets the cap style used for stroking the path. * * @return The cap style value of the stroke. */ StrokeCap strokeCap() const noexcept; /** * @brief Gets the join style value used for stroking the path. * * @return The join style value of the stroke. */ StrokeJoin strokeJoin() const noexcept; /** * @brief Gets the stroke miterlimit. * * @return The stroke miterlimit value when succeed, 4 if no stroke was set. * * @since 0.11 */ float strokeMiterlimit() const noexcept; /** * @brief Creates a new Shape object. * * This function allocates and returns a new Shape instance. * To properly destroy the Shape object, use @ref Paint::rel(). * * @return A pointer to the newly created Shape object. * * @see Paint::rel() */ static Shape* gen() noexcept; /** * @brief Returns the ID value of this class. * * This method can be used to check the current concrete instance type. * * @return The class type ID of the Shape instance. * * @since 1.0 */ Type type() const noexcept override; _TVG_DECLARE_PRIVATE_DERIVE(Shape); }; /** * @class Picture * * @brief A class representing an image read in one of the supported formats: raw, svg, png, jpg, lot and etc. * Besides the methods inherited from the Paint, it provides methods to load & draw images on the canvas. * * @note Supported formats are depended on the available TVG loaders. * @note See Animation class if the picture data is animatable. * * @warning This class is not designed for inheritance. */ struct TVG_API Picture : Paint { /** * @brief Loads a picture data directly from a file. * * ThorVG efficiently caches the loaded data using the specified @p path as a key. * This means that loading the same file again will not result in duplicate operations; * instead, ThorVG will reuse the previously loaded picture data. * * @param[in] filename A file name, including the path, for the picture file. * * @retval Result::InvalidArguments In case the @p path is invalid. * @retval Result::NonSupport When trying to load a file with an unknown extension. * * @note The Load behavior can be asynchronous if the assigned thread number is greater than zero. * @see Initializer::init() */ Result load(const char* filename) noexcept; /** * @brief Loads a picture data from a memory block of a given size. * * ThorVG efficiently caches the loaded data using the specified @p data address as a key * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations * for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data. * * @param[in] data A pointer to a memory location where the content of the picture file is stored. A null-terminated string is expected for non-binary data if @p copy is @c false. * @param[in] size The size in bytes of the memory occupied by the @p data. * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lot", "lottie+json", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one. * @param[in] rpath A resource directory path, if the @p data needs to access any external resources. * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. * * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less. * @retval Result::NonSupport When trying to load a file with an unknown extension. * * @warning It's the user responsibility to release the @p data memory. * * @note If you are unsure about the MIME type, you can provide an empty value like @c nullptr, and thorvg will attempt to figure it out. * @since 0.5 */ Result load(const char* data, uint32_t size, const char* mimeType, const char* rpath = nullptr, bool copy = false) noexcept; /** * @brief Resizes the picture content to the given width and height. * * The picture content is resized while keeping the default size aspect ratio. * The scaling factor is established for each of dimensions and the smaller value is applied to both of them. * * @param[in] w A new width of the image in pixels. * @param[in] h A new height of the image in pixels. * */ Result size(float w, float h) noexcept; /** * @brief Gets the size of the image. * * @param[out] w The width of the image in pixels. * @param[out] h The height of the image in pixels. * */ Result size(float* w, float* h) const noexcept; /** * @brief Sets the normalized origin point of the Picture object. * * This method defines the origin point of the Picture using normalized coordinates. * Unlike a typical pivot point used only for transformations, this origin affects both * the transformation behavior and the actual rendering position of the Picture. * * The specified origin becomes the reference point for positioning the Picture on the canvas. * For example, setting the origin to (0.5f, 0.5f) moves the visual center of the picture * to the position specified by Paint::translate(). * * The coordinates are given in a normalized range relative to the picture's bounds: * - (0.0f, 0.0f): top-left corner * - (0.5f, 0.5f): center * - (1.0f, 1.0f): bottom-right corner * * @param[in] x The normalized x-coordinate of the origin point (range: 0.0f to 1.0f). * @param[in] y The normalized y-coordinate of the origin point (range: 0.0f to 1.0f). * * @note This origin directly affects how the Picture is placed on the canvas when using * transformations such as translate(), rotate(), or scale(). * * @see Paint::translate() * @see Paint::rotate() * @see Paint::scale() * * @since 1.0 */ Result origin(float x, float y) noexcept; /** * @brief Gets the normalized origin point of the Picture object. * * This method retrieves the current origin point of the Picture, expressed * in normalized coordinates relative to the picture’s bounds. * * @param[out] x The normalized x-coordinate of the origin (range: 0.0f to 1.0f). * @param[out] y The normalized y-coordinate of the origin (range: 0.0f to 1.0f). * * @see origin() * @since 1.0 */ Result origin(float* x, float* y) const noexcept; /** * @brief Loads raw image data in a specific format from a memory block of the given size. * * ThorVG efficiently caches the loaded data, using the provided @p data address as a key * when @p copy is set to @c false. This allows ThorVG to avoid redundant operations * by reusing the previously loaded picture data for the same sharable @p data, * rather than duplicating the load process. * * @param[in] data A pointer to the memory block where the raw image data is stored. * @param[in] w The width of the image in pixels. * @param[in] h The height of the image in pixels. * @param[in] cs Specifies how the 32-bit color values should be interpreted. * @param[in] copy If @c true, the data is copied into the engine's local buffer. If @c false, the data is not copied. * * @since 0.9 */ Result load(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy = false) noexcept; /** * @brief Sets the asset resolver callback for handling external resources (e.g., images and fonts). * * This callback is invoked when an external asset reference (such as an image source or file path) * is encountered in a Picture object. It allows the user to provide a custom mechanism for loading * or substituting assets, such as loading from an external source or a virtual filesystem. * * @param[in] func A user-defined function that handles the resolution of asset paths. * @p resolver can return @c true if the asset was successfully resolved by the user, or @c false if it was not. * @param[in] data A pointer to user-defined data that will be passed to the callback each time it is invoked. * This can be used to maintain context or access external resources. * * @retval Result::InsufficientCondition If the picture is already loaded. * * @warning This function must be called before @ref Picture::load() * Setting the resolver after loading will have no effect on asset resolution for that asset. * @note @p src will be either a font path or a font name. In the case of a font name, @p src will begin with "name:", e.g., "name:FreeSans-Medium". * @note If @c false is returned by @p func, ThorVG will attempt to resolve the resource using its internal resolution mechanism as a fallback. * @note To unset the resolver, pass @c nullptr as the @p func parameter. * * @note Experimental API */ Result resolver(std::function func, void* data) noexcept; /** * @brief Retrieve a paint object from the Picture scene by its Unique ID. * * This function searches for a paint object within the Picture scene that matches the provided @p id. * * @param[in] id The Unique ID of the paint object. * * @return A pointer to the paint object that matches the given identifier, or @c nullptr if no matching paint object is found. * * @see Accessor::id() * * @since 1.0 */ const Paint* paint(uint32_t id) noexcept; /** * @brief Creates a new Picture object. * * This function allocates and returns a new Picture instance. * To properly destroy the Picture object, use @ref Paint::rel(). * * @return A pointer to the newly created Picture object. * * @see Paint::rel() */ static Picture* gen() noexcept; /** * @brief Returns the ID value of this class. * * This method can be used to check the current concrete instance type. * * @return The class type ID of the Picture instance. * * @since 1.0 */ Type type() const noexcept override; _TVG_DECLARE_ACCESSOR(Animation); _TVG_DECLARE_PRIVATE_DERIVE(Picture); }; /** * @class Scene * * @brief A class to composite children paints. * * As the traditional graphics rendering method, TVG also enables scene-graph mechanism. * This feature supports an array function for managing the multiple paints as one group paint. * * As a group, the scene can be transformed, made translucent and composited with other target paints, * its children will be affected by the scene world. * * @warning This class is not designed for inheritance. */ struct TVG_API Scene : Paint { /** * @brief Adds a paint object to the scene. * * Appends a paint object to the scene. If the optional @p at parameter is provided, * the paint object is inserted immediately before the specified paint in the scene. * If @p at is @c nullptr, the paint object is appended to the end of the scene. * * @param[in] target A pointer to the Paint object to be added to the scene. * This parameter must not be @c nullptr. * @param[in] at A pointer to an existing Paint object in the scene before * which @p target will be inserted. If @c nullptr, * @p target is appended to the end of the scene. * The default value is @c nullptr. * * @note Ownership of the @p target object is transferred to the scene upon * successful addition. To retain ownership, call @ref Paint::ref() * before adding it to the scene. * @note The rendering order of paint objects follows their order in the scene. * If layering is required, ensure the paints are added in the desired order. * * @see Scene::paints() * @see Scene::remove() * @see Paint::ref() * * @since 1.0 */ Result add(Paint* target, Paint* at = nullptr) noexcept; /** * @brief Returns the list of paints currently held by the Scene. * * This function provides a list of paint nodes, allowing users to access scene-graph information. * * @see Scene::add() * @see Scene::remove() * * @warning This is read-only. Do not modify the list. * @since 1.0 */ const std::list& paints() const noexcept; /** * @brief Removes a paint object or all paint objects from the scene. * * This function removes a specified paint object from the scene. If no paint * object is specified (i.e., the default @c nullptr is used), the function * performs to clear all paints from the scene. * * @param[in] paint A pointer to the Paint object to be removed from the scene. * If @c nullptr, remove all the paints from the scene. * * @see Scene::add() * @see Scene::paints() * * @since 1.0 */ Result remove(Paint* paint = nullptr) noexcept; /** * @brief Add a post-processing effect to the scene. * * Adds a post-processing effect to the scene's effect pipeline, which is applied * after the scene has been rendered. Effects are applied cumulatively and in the * order they are added. Calling this function multiple times will chain multiple * effects sequentially. * * Certain effects may be used to modify the pipeline behavior itself. For example, * @c SceneEffect::Clear removes all previously added effects. * * @param[in] effect The scene effect to add. Available effects are defined in the @ref SceneEffect enum. * @param[in] ... Additional variadic parameters required for certain effects (e.g., sigma and direction for GaussianBlur). * * @note The caller must provide the correct parameters for the selected effect. * Supplying incorrect or insufficient arguments results in undefined behavior. * * @since 1.0 */ Result add(SceneEffect effect, ...) noexcept; /** * @brief Creates a new Scene object. * * This function allocates and returns a new Scene instance. * To properly destroy the Scene object, use @ref Paint::rel(). * * @return A pointer to the newly created Scene object. * * @see Paint::rel() */ static Scene* gen() noexcept; /** * @brief Returns the ID value of this class. * * This method can be used to check the current concrete instance type. * * @return The class type ID of the Scene instance. * * @since 1.0 */ Type type() const noexcept override; _TVG_DECLARE_PRIVATE_DERIVE(Scene); }; /** * @class Text * * @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text. * * @warning This class is not designed for inheritance. * * @since 0.15 */ struct TVG_API Text : Paint { /** * @brief Sets the font family for the text. * * This function specifies the name of the font to be used when rendering text. * * @param[in] name The name of the font. This should match a font available through the canvas backend. * If set to @c nullptr, ThorVG will attempt to select a fallback font available on the engine. * * @retval Result::InsufficientCondition when the specified @p name cannot be found. * * @note This function only sets the font family name. Use @ref size() to define the font size. * @note If the @p name is not specified, ThorVG will select an available fallback font. * * @see Text::size() * @see Text::load() * * @since 1.0 */ Result font(const char* name) noexcept; /** * @brief Sets the font size for the text. * * This function sets the font size used during text rendering. * The size is specified in point units, and supports floating-point precision * for smooth scaling and animation effects. * * @param[in] size The font size in points. Must be greater than 0.0. * * @retval Result::InvalidArguments if the @p size is less than or equal to 0. * * @note Use this function in combination with @ref font() to fully define text appearance. * @note Fractional sizes (e.g., 12.5) are supported for sub-pixel rendering and animations. * * @see Text::font() * * @since 1.0 */ Result size(float size) noexcept; /** * @brief Assigns the given unicode text to be rendered. * * This function sets the unicode string that will be displayed by the rendering system. * The text is set according to the specified UTF encoding method, which defaults to UTF-8. * * @param[in] text The multi-byte text encoded with utf8 string to be rendered. * * @since 1.0 */ Result text(const char* text) noexcept; /** * @brief Sets text alignment or anchor per axis. * * If layout width/height is set on an axis, align within the layout box. * Otherwise, treat it as an anchor within the text bounds which point of * the text box is pinned to the paint position. * * @param[in] x Horizontal alignment/anchor in [0..1]: 0=left/start, 0.5=center, 1=right/end. (Default is 0) * @param[in] y Vertical alignment/anchor in [0..1]: 0=top, 0.5=middle, 1=bottom. (Default is 0) * * @since 1.0 * * @see layout() */ Result align(float x, float y) noexcept; /** * @brief Sets the virtual layout box (constraints) for the text. * * If width/height is set on an axis, that axis is constrained by a virtual layout box and * the text may wrap/align inside it. If width/height == 0, the axis is * unconstrained and @ref align() acts as an anchor on that axis. * * @param[in] w Layout width in user space. Use 0 for no horizontal constraint. (Default is 0) * @param[in] h Layout height in user space. Use 0 for no vertical constraint. (Default is 0) * * @note This defines constraints only; alignment/anchoring is controlled by @ref align(). * @since 1.0 * * @see align() * @see spacing() */ Result layout(float w, float h) noexcept; /** * @brief Sets the text wrapping mode for this text object. * * This method controls how the text is laid out when it exceeds the available space. * The wrapping mode determines whether text is truncated, wrapped by character or word, * or adjusted automatically. An ellipsis mode is also available for truncation with "...". * * @param[in] mode The wrapping strategy to apply. Default is @c TextWrap::None * * @see TextWrap * @since 1.0 */ Result wrap(TextWrap mode) noexcept; /** * @brief Apply an italic (slant) transformation to the text. * * This function applies a shear transformation to simulate an italic (oblique) style * for the current text object. The shear factor determines the degree of slant * applied along the X-axis. * * @param[in] shear The shear factor to apply. A value of 0.0 applies no slant, while values around 0.5 result in a strong slant. * Must be in the range [0.0, 0.5]. Default value is 0.18. * * @note The @p shear factor will be clamped to the valid range if it exceeds the limits. * @note This does not require the font itself to be italic. * It visually simulates the effect by applying a transformation matrix. * * @warning Excessive slanting may cause visual distortion depending on the font and size. * * @see Text::font() * * @since 1.0 */ Result italic(float shear = 0.18f) noexcept; /** * @brief Sets an outline (stroke) around the text object. * * This function adds an outline to the text with the specified width and RGB color. * The outline enhances the visibility of the text by rendering a stroke around its glyphs. * * @param width The width of the outline. Must be positive value. (The default is 0) * @param r Red component of the outline color (0–255). * @param g Green component of the outline color (0–255). * @param b Blue component of the outline color (0–255). * * @note To disable the outline, set @p width to 0. * @see Text::fill() to set the main text fill color. * * @since 1.0 */ Result outline(float width, uint8_t r, uint8_t g, uint8_t b) noexcept; /** * @brief Sets the text color. * * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * * @see Text::font() * @see Text::outline() * * @since 0.15 */ Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept; /** * @brief Sets the gradient fill for all of the figures from the text. * * The parts of the text defined as inner are filled. * * @param[in] f The unique pointer to the gradient fill. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * @see Text::font() * * @since 0.15 */ Result fill(Fill* f) noexcept; /** * @brief Set the spacing scale factors for text layout. * * This function adjusts the letter spacing (horizontal space between glyphs) and * line spacing (vertical space between lines of text) using scale factors. * * Both values are relative to the font's default metrics: * - The letter spacing is applied as a scale factor to the glyph's advance width. * - The line spacing is applied as a scale factor to the glyph's advance height. * * @param[in] letter The scale factor for letter spacing. * Values > 1.0 increase spacing, values < 1.0 decrease it. * Must be greater than or equal to 0.0. (default: 1.0) * * @param[in] line The scale factor for line spacing. * Values > 1.0 increase line spacing, values < 1.0 decrease it. * Must be greater than or equal to 0.0. (default: 1.0) * * @since 1.0 */ Result spacing(float letter, float line) noexcept; /** * @brief Retrieves the layout metrics of the text object. * * Fills the provided `TextMetrics` structure with the font layout values of this text object, * such as ascent, descent, linegap, and line advance. * * The returned values reflect the font size applied to the text object, * but do not include any transformations (e.g., scale, rotation, or translation). * * @param[out] metrics A reference to a `TextMetrics` structure to be filled with the resulting values. * * @return Result::InsufficientCondition if no font or size has been set yet. * * @see TextMetrics * @note Experimental API */ Result metrics(TextMetrics& metrics) const noexcept; /** * @brief Loads a scalable font data (ttf) from a file. * * ThorVG efficiently caches the loaded data using the specified @p path as a key. * This means that loading the same file again will not result in duplicate operations; * instead, ThorVG will reuse the previously loaded font data. * * @param[in] filename A file name, including the path, for the font file. * * @retval Result::InvalidArguments In case the @p path is invalid. * @retval Result::NonSupport When trying to load a file with an unknown extension. * * @see Text::unload(const char* filename) * * @since 0.15 */ static Result load(const char* filename) noexcept; /** * @brief Loads a scalable font data (ttf) from a memory block of a given size. * * ThorVG efficiently caches the loaded font data using the specified @p name as a key. * This means that loading the same fonts again will not result in duplicate operations. * Instead, ThorVG will reuse the previously loaded font data. * * @param[in] name The name under which the font will be stored and accessible (e.x. in a @p font() API). * @param[in] data A pointer to a memory location where the content of the font data is stored. * @param[in] size The size in bytes of the memory occupied by the @p data. * @param[in] mimeType Mimetype or extension of font data. In case an empty string is provided the loader will be determined automatically. * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not (default). * * @retval Result::InvalidArguments If no name is provided or if @p size is zero while @p data points to a valid memory location. * @retval Result::NonSupport When trying to load a file with an unsupported extension. * @retval Result::InsufficientCondition If attempting to unload the font data that has not been previously loaded. * * @warning It's the user responsibility to release the @p data memory. * * @note To unload the font data loaded using this API, pass the proper @p name and @c nullptr as @p data. * @note If you are unsure about the MIME type, you can provide an empty value like @c nullptr, and thorvg will attempt to figure it out. * @see Text::font(const char* name, float size, const char* style) * * @since 0.15 */ static Result load(const char* name, const char* data, uint32_t size, const char* mimeType = "ttf", bool copy = false) noexcept; /** * @brief Unloads the specified scalable font data (TTF) that was previously loaded. * * This function is used to release resources associated with a font file that has been loaded into memory. * * @param[in] filename The file name of the loaded font, including the path. * * @retval Result::InsufficientCondition Fails if the loader is not initialized. * * @note If the font data is currently in use, it will not be immediately unloaded. * @see Text::load(const char* filename) * * @since 0.15 */ static Result unload(const char* filename) noexcept; /** * @brief Creates a new Text object. * * This function allocates and returns a new Text instance. * To properly destroy the Text object, use @ref Paint::rel(). * * @return A pointer to the newly created Text object. * * @see Paint::rel() * * @since 0.15 */ static Text* gen() noexcept; /** * @brief Returns the ID value of this class. * * This method can be used to check the current concrete instance type. * * @return The class type ID of the Text instance. * * @since 1.0 */ Type type() const noexcept override; _TVG_DECLARE_PRIVATE_DERIVE(Text); }; /** * @class SwCanvas * * @brief A class for the rendering graphical elements with a software raster engine. */ struct TVG_API SwCanvas final : Canvas { ~SwCanvas() override; /** * @brief Sets the drawing target for the rasterization. * * The buffer of a desirable size should be allocated and owned by the caller. * * @param[in] buffer A pointer to a memory block of the size @p stride x @p h, where the raster data are stored. * @param[in] stride The stride of the raster image - greater than or equal to @p w. * @param[in] w The width of the raster image. * @param[in] h The height of the raster image. * @param[in] cs The value specifying the way the 32-bits colors should be read/written. * * @retval Result::InvalidArguments In case no valid pointer is provided or the width, or the height or the stride is zero. * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced. * @retval Result::NonSupport In case the software engine is not supported. * * @warning Do not access @p buffer during Canvas::add() - Canvas::sync(). It should not be accessed while the engine is writing on it. * * @see Canvas::viewport() * @see Canvas::sync() */ Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs) noexcept; /** * @brief Creates a new SwCanvas object with optional rendering engine settings. * * This method generates a software canvas instance that can be used for drawing vector graphics. * It accepts an optional parameter @p op to choose between different rendering engine behaviors. * * @param[in] op The rendering engine option. Default is @c EngineOption::Default. * * @return A new SwCanvas object. * * @see enum EngineOption */ static SwCanvas* gen(EngineOption op = EngineOption::Default) noexcept; _TVG_DECLARE_PRIVATE(SwCanvas); }; /** * @class GlCanvas * * @brief A class for the rendering graphic elements with a GL raster engine. * * @since 0.14 */ struct TVG_API GlCanvas final : Canvas { ~GlCanvas() override; /** * @brief Sets the drawing target for rasterization. * * This function specifies the drawing target where the rasterization will occur. It can target * a specific framebuffer object (FBO) or the main surface. * * @param[in] display The platform-specific display handle (EGLDisplay for EGL). Set @c nullptr for other systems. * @param[in] surface The platform-specific surface handle (EGLSurface for EGL, HDC for WGL). Set @c nullptr for other systems. * @param[in] context The OpenGL context to be used for rendering on this canvas. * @param[in] id The GL target ID, usually indicating the FBO ID. A value of @c 0 specifies the main surface. * @param[in] w The width (in pixels) of the raster image. * @param[in] h The height (in pixels) of the raster image. * @param[in] cs Specifies how the pixel values should be interpreted. Currently, it only allows @c ColorSpace::ABGR8888S as @c GL_RGBA8. * * @retval Result::InsufficientCondition If the canvas is currently rendering. * Ensure that @ref Canvas::sync() has been called before setting a new target. * @retval Result::NonSupport In case the gl engine is not supported. * * @note If @p display and @p surface are not provided, the ThorVG GL engine assumes that * the appropriate OpenGL context is already current and will not attempt to bind a new one. * * @see Canvas::sync() * * @since 1.0 */ Result target(void* display, void* surface, void* context, int32_t id, uint32_t w, uint32_t h, ColorSpace cs) noexcept; /** * @brief Creates a new OpenGL/ES Canvas object with optional rendering engine settings. * * This method generates a OpenGL canvas instance that can be used for drawing vector graphics. * It accepts an optional parameter @p op to choose between different rendering engine behaviors. * * @param[in] op The rendering engine option. Default is @c EngineOption::Default. * * @return A new GlCanvas object. * * @note Currently, it does not support @c EngineOption::SmartRender. The request will be ignored. * * @see enum EngineOption * * @since 1.0 */ static GlCanvas* gen(EngineOption op = EngineOption::Default) noexcept; _TVG_DECLARE_PRIVATE(GlCanvas); }; /** * @class WgCanvas * * @brief A class for the rendering graphic elements with a WebGPU raster engine. * * @warning Please do not use it. This class is not fully supported yet. * * @since 0.15 */ struct TVG_API WgCanvas final : Canvas { ~WgCanvas() override; /** * @brief Sets the drawing target for the rasterization. * * @param[in] device WGPUDevice, a desired handle for the wgpu device. If it is @c nullptr, ThorVG will assign an appropriate device internally. * @param[in] instance WGPUInstance, context for all other wgpu objects. * @param[in] target Either WGPUSurface or WGPUTexture, serving as handles to a presentable surface or texture. * @param[in] w The width of the target. * @param[in] h The height of the target. * @param[in] cs Specifies how the pixel values should be interpreted. Currently, it only allows @c ColorSpace::ABGR8888S as @c WGPUTextureFormat_RGBA8Unorm. * @param[in] type @c 0: surface, @c 1: texture are used as pesentable target. * * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced. * @retval Result::NonSupport In case the wg engine is not supported. * * @since 1.0 * * @see Canvas::viewport() * @see Canvas::sync() */ Result target(void* device, void* instance, void* target, uint32_t w, uint32_t h, ColorSpace cs, int type = 0) noexcept; /** * @brief Creates a new WebGPU Canvas object with optional rendering engine settings. * * This method generates a WebGPU canvas instance that can be used for drawing vector graphics. * It accepts an optional parameter @p op to choose between different rendering engine behaviors. * * @param[in] op The rendering engine option. Default is @c EngineOption::Default. * * @return A new WgCanvas object. * * @note Currently, it does not support @c EngineOption::SmartRender. The request will be ignored. * * @see enum EngineOption * * @since 1.0 */ static WgCanvas* gen(EngineOption op = EngineOption::Default) noexcept; _TVG_DECLARE_PRIVATE(WgCanvas); }; /** * @class Initializer * * @brief A class that enables initialization and termination of the TVG engines. */ struct TVG_API Initializer final { /** * @brief Initializes the ThorVG engine runtime. * * ThorVG requires an active runtime environment for rendering operations. * This function sets up an internal task scheduler and creates a specified number * of worker threads to enable parallel rendering. * * @param[in] threads The number of worker threads to launch. * A value of 0 indicates that only the main thread will be used. * * @return Result indicating success or failure of initialization. * * @note This function uses internal reference counting to allow multiple init() calls. * However, the number of threads is fixed during the first successful initialization * and cannot be changed in subsequent calls. * * @see Initializer::term() */ static Result init(uint32_t threads = 0) noexcept; /** * @brief Terminates the ThorVG engine. * * Cleans up resources and stops any internal threads initialized by init(). * * @retval Result::InsufficientCondition Returned if there is nothing to terminate (e.g., init() was not called). * * @note The initializer maintains a reference count for safe repeated use. * Only the final call to term() will fully shut down the engine. * @see Initializer::init() */ static Result term() noexcept; /** * @brief Retrieves the version of the TVG engine. * * @param[out] major A major version number. * @param[out] minor A minor version number. * @param[out] micro A micro version number. * * @return The version of the engine in the format major.minor.micro, or a @p nullptr in case of an internal error. * * @since 0.15 */ static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept; _TVG_DISABLE_CTOR(Initializer); }; /** * @class Animation * * @brief The Animation class enables manipulation of animatable images. * * This class supports the display and control of animation frames. * * @since 0.13 */ struct TVG_API Animation { virtual ~Animation(); /** * @brief Specifies the current frame in the animation. * * @param[in] no The index of the animation frame to be displayed. The index should be less than the totalFrame(). * * @retval Result::InsufficientCondition if the given @p no is the same as the current frame value. * @retval Result::NonSupport The current Picture data does not support animations. * * @note For efficiency, ThorVG ignores updates to the new frame value if the difference from the current frame value * is less than 0.001. In such cases, it returns @c Result::InsufficientCondition. * Values less than 0.001 may be disregarded and may not be accurately retained by the Animation. * * @see totalFrame() */ Result frame(float no) noexcept; /** * @brief Retrieves a picture instance associated with this animation instance. * * This function provides access to the picture instance that can be used to load animation formats, such as lot. * After setting up the picture, it can be added to the designated canvas, enabling control over animation frames * with this Animation instance. * * @return A picture instance that is tied to this animation. * * @warning The picture instance is owned by Animation. It should not be deleted manually. */ Picture* picture() const noexcept; /** * @brief Retrieves the current frame number of the animation. * * @return The current frame number of the animation, between 0 and totalFrame() - 1. * * @note If the Picture is not properly configured, this function will return 0. * * @see Animation::frame() * @see Animation::totalFrame() */ float curFrame() const noexcept; /** * @brief Retrieves the total number of frames in the animation. * * @return The total number of frames in the animation. * * @note Frame numbering starts from 0. * @note If the Picture is not properly configured, this function will return 0. */ float totalFrame() const noexcept; /** * @brief Retrieves the duration of the animation in seconds. * * @return The duration of the animation in seconds. * * @note If the Picture is not properly configured, this function will return 0. */ float duration() const noexcept; /** * @brief Specifies the playback segment of the animation. * * The set segment is designated as the play area of the animation. * This is useful for playing a specific segment within the entire animation. * After setting, the number of animation frames and the playback time are calculated * by mapping the playback segment as the entire range. * * @param[in] begin segment begin frame. * @param[in] end segment end frame. * * @retval Result::InsufficientCondition In case the animation is not loaded. * @retval Result::InvalidArguments If the @p begin is higher than @p end. * @retval Result::NonSupport When it's not animatable. * * @note Animation allows a range from 0.0 to the total frame. @p end should not be higher than @p begin. * @note If a marker has been specified, its range will be disregarded. * * @see LottieAnimation::segment(const char* marker) * @see Animation::totalFrame() * * @since 1.0 */ Result segment(float begin, float end) noexcept; /** * @brief Gets the current segment range information. * * @param[out] begin segment begin frame. * @param[out] end segment end frame. * * @retval Result::InsufficientCondition In case the animation is not loaded. * @retval Result::NonSupport When it's not animatable. * * @since 1.0 */ Result segment(float* begin, float* end = nullptr) noexcept; /** * @brief Creates a new Animation object. * * @return A new Animation object. */ static Animation* gen() noexcept; _TVG_DECLARE_PRIVATE_BASE(Animation); }; /** * @class Saver * * @brief A class for exporting a paint object into a specified file, from which to recover the paint data later. * * ThorVG provides a feature for exporting & importing paint data. The Saver role is to export the paint data to a file. * It's useful when you need to save the composed scene or image from a paint object and recreate it later. * * The file format is decided by the extension name(i.e. "*.tvg") while the supported formats depend on the TVG packaging environment. * If it doesn't support the file format, the save() method returns the @c Result::NonSupport result. * * Once you export a paint to the file successfully, you can recreate it using the Picture class. * * @see Picture::load() * * @since 0.5 */ struct TVG_API Saver final { ~Saver(); /** * @brief Sets the base background content for the saved image. * * @param[in] paint The paint to be drawn as the background image for the saving paint. * * @since 1.0 */ Result background(Paint* paint) noexcept; /** * @brief Exports the given @p paint data to the given @p path * * If the saver module supports any compression mechanism, it will optimize the data size. * This might affect the encoding/decoding time in some cases. You can turn off the compression * if you wish to optimize for speed. * * @param[in] paint The paint to be saved with all its associated properties. * @param[in] filename A file name, including the path, where the paint data will be saved. * @param[in] quality The encoded quality level. @c 0 is the minimum, @c 100 is the maximum value(recommended). * * @retval Result::InsufficientCondition If currently saving other resources. * @retval Result::NonSupport When trying to save a file with an unknown extension or in an unsupported format. * @retval Result::Unknown In case an empty paint is to be saved. * * @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call sync() afterwards. * @see Saver::sync() * * @since 0.5 */ Result save(Paint* paint, const char* filename, uint32_t quality = 100) noexcept; /** * @brief Export the provided animation data to the specified file path. * * This function exports the given animation data to the provided file path. You can also specify the desired frame rate in frames per second (FPS) by providing the fps parameter. * * @param[in] animation The animation to be saved, including all associated properties. * @param[in] filename A file name, including the path, where the animation will be saved. * @param[in] quality The encoded quality level. @c 0 is the minimum, @c 100 is the maximum value(recommended). * @param[in] fps The desired frames per second (FPS). For example, to encode data at 60 FPS, pass 60. Pass 0 to keep the original frame data. * * @retval Result::InsufficientCondition if there are ongoing resource-saving operations. * @retval Result::NonSupport if an attempt is made to save the file with an unknown extension or in an unsupported format. * @retval Result::Unknown if attempting to save an empty paint. * * @note A higher frames per second (FPS) would result in a larger file size. It is recommended to use the default value. * @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call sync() afterwards. * * @see Saver::sync() * * @since 1.0 */ Result save(Animation* animation, const char* filename, uint32_t quality = 100, uint32_t fps = 0) noexcept; /** * @brief Guarantees that the saving task is finished. * * The behavior of the Saver works on a sync/async basis, depending on the threading setting of the Initializer. * Thus, if you wish to have a benefit of it, you must call sync() after the save() in the proper delayed time. * Otherwise, you can call sync() immediately. * * @note The asynchronous tasking is dependent on the Saver module implementation. * @see Saver::save() * * @since 0.5 */ Result sync() noexcept; /** * @brief Creates a new Saver object. * * @return A new Saver object. * * @since 0.5 */ static Saver* gen() noexcept; _TVG_DECLARE_PRIVATE_BASE(Saver); }; /** * @class Accessor * * @brief The Accessor is a utility class to debug the Scene structure by traversing the scene-tree. * * The Accessor helps you search specific nodes to read the property information, figure out the structure of the scene tree and its size. * * @warning We strongly warn you not to change the paints of a scene unless you really know the design-structure. * * @since 0.10 */ struct TVG_API Accessor final { ~Accessor(); /** * @brief Set the access function for traversing the Picture scene tree nodes. * * @param[in] paint The paint node to traverse the internal scene-tree. * @param[in] func The callback function calling for every paint nodes of the Picture. * @param[in] data Data passed to the @p func as its argument. * * @note The bitmap based picture might not have the scene-tree. * * @since 1.0 */ Result set(Paint* paint, std::function func, void* data) noexcept; /** * @brief Generate a unique ID (hash key) from a given name. * * This function computes a unique identifier value based on the provided string. * You can use this to assign a unique ID to the Paint object. * * @param[in] name The input string to generate the unique identifier from. * * @return The generated unique identifier value. * * @see Paint::id * * @since 1.0 */ static uint32_t id(const char* name) noexcept; /** * @brief Creates a new Accessor object. * * @return A new Accessor object. */ static Accessor* gen() noexcept; _TVG_DECLARE_PRIVATE_BASE(Accessor); }; /** @}*/ } //namespace #endif //_THORVG_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/upsampling.cpp000664 001750 001750 00000030010 15164251010 035756 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // YUV to RGB upsampling functions. // // Author: somnath@google.com (Somnath Banerjee) #include "./dsp.h" #include "./yuv.h" #include //------------------------------------------------------------------------------ // Fancy upsampler #ifdef FANCY_UPSAMPLING // Fancy upsampling functions to convert YUV to RGB WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST]; // Given samples laid out in a square as: // [a b] // [c d] // we interpolate u/v as: // ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16 // ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16 // We process u and v together stashed into 32bit (16bit each). #define LOAD_UV(u, v) ((u) | ((v) << 16)) #define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \ const uint8_t* top_u, const uint8_t* top_v, \ const uint8_t* cur_u, const uint8_t* cur_v, \ uint8_t* top_dst, uint8_t* bottom_dst, int len) { \ int x; \ const int last_pixel_pair = (len - 1) >> 1; \ uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \ uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \ assert(top_y != NULL); \ { \ const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \ } \ if (bottom_y != NULL) { \ const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \ } \ for (x = 1; x <= last_pixel_pair; ++x) { \ const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \ const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \ /* precompute invariant values associated with first and second diagonals*/\ const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \ const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \ const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \ { \ const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \ const uint32_t uv1 = (diag_03 + t_uv) >> 1; \ FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \ top_dst + (2 * x - 1) * XSTEP); \ FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \ top_dst + (2 * x - 0) * XSTEP); \ } \ if (bottom_y != NULL) { \ const uint32_t uv0 = (diag_03 + l_uv) >> 1; \ const uint32_t uv1 = (diag_12 + uv) >> 1; \ FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \ bottom_dst + (2 * x - 1) * XSTEP); \ FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \ bottom_dst + (2 * x + 0) * XSTEP); \ } \ tl_uv = t_uv; \ l_uv = uv; \ } \ if (!(len & 1)) { \ { \ const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \ top_dst + (len - 1) * XSTEP); \ } \ if (bottom_y != NULL) { \ const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \ bottom_dst + (len - 1) * XSTEP); \ } \ } \ } // All variants implemented. UPSAMPLE_FUNC(UpsampleRgbLinePair, VP8YuvToRgb, 3) UPSAMPLE_FUNC(UpsampleBgrLinePair, VP8YuvToBgr, 3) UPSAMPLE_FUNC(UpsampleRgbaLinePair, VP8YuvToRgba, 4) UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4) UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4) UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2) UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2) #undef LOAD_UV #undef UPSAMPLE_FUNC #endif // FANCY_UPSAMPLING //------------------------------------------------------------------------------ #if !defined(FANCY_UPSAMPLING) #define DUAL_SAMPLE_FUNC(FUNC_NAME, FUNC) \ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bot_y, \ const uint8_t* top_u, const uint8_t* top_v, \ const uint8_t* bot_u, const uint8_t* bot_v, \ uint8_t* top_dst, uint8_t* bot_dst, int len) { \ const int half_len = len >> 1; \ int x; \ assert(top_dst != NULL); \ { \ for (x = 0; x < half_len; ++x) { \ FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x + 0); \ FUNC(top_y[2 * x + 1], top_u[x], top_v[x], top_dst + 8 * x + 4); \ } \ if (len & 1) FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x); \ } \ if (bot_dst != NULL) { \ for (x = 0; x < half_len; ++x) { \ FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x + 0); \ FUNC(bot_y[2 * x + 1], bot_u[x], bot_v[x], bot_dst + 8 * x + 4); \ } \ if (len & 1) FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x); \ } \ } DUAL_SAMPLE_FUNC(DualLineSamplerBGRA, VP8YuvToBgra) DUAL_SAMPLE_FUNC(DualLineSamplerARGB, VP8YuvToArgb) #undef DUAL_SAMPLE_FUNC #endif // !FANCY_UPSAMPLING WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last) { WebPInitUpsamplers(); VP8YUVInit(); #ifdef FANCY_UPSAMPLING return WebPUpsamplers[alpha_is_last ? MODE_BGRA : MODE_ARGB]; #else return (alpha_is_last ? DualLineSamplerBGRA : DualLineSamplerARGB); #endif } //------------------------------------------------------------------------------ // YUV444 converter #define YUV444_FUNC(FUNC_NAME, FUNC, XSTEP) \ static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ uint8_t* dst, int len) { \ int i; \ for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * XSTEP]); \ } YUV444_FUNC(Yuv444ToRgb, VP8YuvToRgb, 3) YUV444_FUNC(Yuv444ToBgr, VP8YuvToBgr, 3) YUV444_FUNC(Yuv444ToRgba, VP8YuvToRgba, 4) YUV444_FUNC(Yuv444ToBgra, VP8YuvToBgra, 4) YUV444_FUNC(Yuv444ToArgb, VP8YuvToArgb, 4) YUV444_FUNC(Yuv444ToRgba4444, VP8YuvToRgba4444, 2) YUV444_FUNC(Yuv444ToRgb565, VP8YuvToRgb565, 2) #undef YUV444_FUNC WebPYUV444Converter WebPYUV444Converters[MODE_LAST]; extern void WebPInitYUV444ConvertersMIPSdspR2(void); static volatile VP8CPUInfo upsampling_last_cpuinfo_used1 = (VP8CPUInfo)&upsampling_last_cpuinfo_used1; WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444Converters(void) { if (upsampling_last_cpuinfo_used1 == VP8GetCPUInfo) return; WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb; WebPYUV444Converters[MODE_RGBA] = Yuv444ToRgba; WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr; WebPYUV444Converters[MODE_BGRA] = Yuv444ToBgra; WebPYUV444Converters[MODE_ARGB] = Yuv444ToArgb; WebPYUV444Converters[MODE_RGBA_4444] = Yuv444ToRgba4444; WebPYUV444Converters[MODE_RGB_565] = Yuv444ToRgb565; WebPYUV444Converters[MODE_rgbA] = Yuv444ToRgba; WebPYUV444Converters[MODE_bgrA] = Yuv444ToBgra; WebPYUV444Converters[MODE_Argb] = Yuv444ToArgb; WebPYUV444Converters[MODE_rgbA_4444] = Yuv444ToRgba4444; if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { WebPInitYUV444ConvertersMIPSdspR2(); } #endif } upsampling_last_cpuinfo_used1 = VP8GetCPUInfo; } //------------------------------------------------------------------------------ // Main calls extern void WebPInitUpsamplersSSE2(void); extern void WebPInitUpsamplersNEON(void); extern void WebPInitUpsamplersMIPSdspR2(void); static volatile VP8CPUInfo upsampling_last_cpuinfo_used2 = (VP8CPUInfo)&upsampling_last_cpuinfo_used2; WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplers(void) { if (upsampling_last_cpuinfo_used2 == VP8GetCPUInfo) return; #ifdef FANCY_UPSAMPLING WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair; WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair; WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair; WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair; WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair; WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair; WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair; WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair; WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair; WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair; WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair; // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) if (VP8GetCPUInfo(kSSE2)) { WebPInitUpsamplersSSE2(); } #endif #if defined(WEBP_USE_NEON) if (VP8GetCPUInfo(kNEON)) { WebPInitUpsamplersNEON(); } #endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { WebPInitUpsamplersMIPSdspR2(); } #endif } #endif // FANCY_UPSAMPLING upsampling_last_cpuinfo_used2 = VP8GetCPUInfo; } //------------------------------------------------------------------------------ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/meson.build000664 001750 001750 00000000532 15164251010 033436 0ustar00ddennedyddennedy000000 000000 source_file = [ 'tvgArray.h', 'tvgCommon.h', 'tvgColor.h', 'tvgCompressor.h', 'tvgInlist.h', 'tvgLock.h', 'tvgMath.h', 'tvgStr.h', 'tvgColor.cpp', 'tvgCompressor.cpp', 'tvgMath.cpp', 'tvgStr.cpp' ] utils_dep = declare_dependency( include_directories : include_directories('.'), sources : source_file) loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.inc.h000664 001750 001750 00000002737 15164251010 052653 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Generator.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 25.3.1.5 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_GENERATOR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.2.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_GENERATOR, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_NEXT, ECMA_GENERATOR_PROTOTYPE_ROUTINE_NEXT, 1, 1) ROUTINE (LIT_MAGIC_STRING_RETURN, ECMA_GENERATOR_PROTOTYPE_ROUTINE_RETURN, 1, 1) ROUTINE (LIT_MAGIC_STRING_THROW, ECMA_GENERATOR_PROTOTYPE_ROUTINE_THROW, 1, 1) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/rawimage_250x375.raw000664 001750 001750 00001334330 15164251010 035462 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0kkl~l~l}l{kzlxkwjtjrjsjsjvjzj|j|k}k}k}k{jzjzjzixhugtgqfpfpenemfnfoepeqerfsgthuiviwiwixjzi|i{j|k}k}k~kllkk~k~kklk~k}k{kyjxkwjtiripioipiqirisiririqiqiphphnhlhigghfhehehehdhcibicjcjbjbi`j`j`i^h]h]i]i]h\h]h\g[hYhZh[h\h[hZgYgYfXgWgWfWfVeTeTfUfUeTeUeTeTeTeSdSeTfVgYiZhZi[i]h]h^g_hah`g_e^d]c]c]c]b\a\a\b^babeeneseudvexeze|e|ezdwctbqapaoaoanamanao`n_l^j^h^i^m`p`r_s`ua|ba}_w_s^r^r^t_u^t]s\r\q\m[kZjZjYhYdXaWaXcYgZjZkYkXjXjXmZs\{]}\}\{[x[uYpXjWgWgWgXiYkYkXjXiXjWjWjWjVjWlXoXqWqXrWsXuYvWsVnVkVkVkThSdkkk~l}l{lzlylxkvktksksksjvkzk}l~l}k{kykxkykyjyiwhuhshrhqgqfpfofofpepeqfrgshuhviwixixiyj{j|j|j|k}k~lllllllllml~l}k{kzkylxkvjsjqjpjpjqjririrjrjrjrjriripioilhihiigifidicibjbjcjcjcjcibj`j`j`i_i^i^i]i\i\h[hZiYiYiZh[h[hZhZhZgYgXgXgXgWfVgWgWfVfUfVfVfUfUfTeUeVfWgYh[i\j]i^g^g_f`gbhchbgaf`d_c^c^b_b_b_a_bacgeoftfwfyf{f~ggf~e{dxcubrap`m`j`i`j`k_k`k_j^h^g^i_m`q`s`va{a~`{_u^q^p^p^r^s^s]r\p\o\l[jZjZiYfYdYcXbXcYeYgYgXhYhYiYkZp\x\|\|\{\y[vZrYlXjXiXiYkYmYlXkXkXmXnXmWlWlXnXpXrXrXsXtYuYtWqVlUhUgUgUfTckklm~m}m}m|mzlwluluktkukxm|mnm}lykvkukwlxkxjuitisirjsishrgqgqgqfqfqgrhshuiwixjzj{j{j|j}j}j}k~klllmmmllmmmm}l|lzlylylwkukrkqkpkqjqjqjqjrjrjsksjsjsjrjpjmikijihifjdjcjcjcidjdjdibjajajajaj`i_i]i\i\i[iZiYiYiYi[i[i[i[h[h[hYhXhXhYhYhYhXgXgWgVfVfVfUfVfWfXgYg[i]j_j_i`hagagcheiehdhcgdfcebdacbcccccbcddhepgugyg|h~hhhgf}e|dycubp`k_i_h_h_h`j`k`k_i^e^f^i`o`r`t`w`x`u^p]m]l]k]m^p]q]p\m\k[jZiZhZfYdXbXbXbXaXbXbXdXeXfYhYjZn\t\z\z\y\x\v[sZpZmYkYlYoZoYoYnYoYqYqYpYoXoYqYrYsXsXsXtXtXrWoViUdTcUbTbTalklmmnnn}mzmxlwlvlwmzn~onm}lxktktlulwkvjujtjsjsjtktjtishrhrgrgqgrhtiviwiyj|k}j}j}j~j~kkkllmmlmmlmlmmm}lzlylylwlvlskrlplpkpkpkpkqjqkrksktkukukskqjojmjjjhjgjfifieiejekejbjcjcjcjcjbjaj_j]j]j\j[jZiYiZj[j[j\j\i[h[hYhXiXiYiZhZhYhXgVgVgVfVfVfWgXgYg[h]i_jaiaibjdjejfighegchdgegefdedeededfdfegekfqgwg{g~hhhhgf~f}e{dvbp`j`i`i_h`i`j`l`l_i^f]d^f_k`n`q`r`q_n^k\i[h\g\i]l]m]m\j[gZfYeYdYcYbXaX`W_X^W^W_XaXcXeYgYiZl[r\w\x\x\x\v[s[p[mZmZoZqZqZpZqZsZtZsZrZrYrZsZtYsXsYrXrXqXpWlVeT`T_T^S]S]mmmmmnnm~m|nzmzmzmzn|oonm|lwkslsltluluktktltksjtkvkujtirirhrhqhritivixizj|k~k~kkkkkllmmmmmmmlmmmm}m{mzmxmwlvltlsmqlplolololpkpkqkrktlvlukuktkskpjnjljkkijhjgifjfkejdjdkekejdjckblbk`k^k]k\jZjZjZj[j[j\j]i\iZiYiXiYiZjZiZiYhWgVgWgVgVgWgXhYh[h]h^h_iahbiekfkglgjghdfbgcfdfdfeefefefegeifkengshyh|h~gggggf~e}e{dvcpajaiai`i`i`j`m`l_i_g^e]f^i_l_n`o_l^i]g]f\e\e\g\i]j\j[fYcYbXaXaYaXaX`X_W^W]W]W^W`XaXdYeYhZm[r[t\u\w\w\v[r[n[l[l[p[rZqZpZr[v[vZuZsZsZtZtZsYrXqXpWpWoWmViVdT_S]S[S[SZponmlmmmm~m|m|n|n}oponm{mwltltmsmsmsmrlrlslsltkukvktjsiriqiriritivjxj{k|k~llmmmmmmmmnnmmmmnnnno}o{nynwnvnumtlqmpmomomolplplplqltlumvlvluktlrkpkokmklkkkjkilhlgkfkfkfkfkfjekdlclal`l_l]k\k[k[k\k\k]k^k]j[jZjYjYiZj[k[iZhXgWhVhVhWhXhYi[i\i]i^h`h`hciejgkhlhkgiegcgdgegdgfggfhghfifkemfofsgxh{h}ihggggge|ewdqakai`h`i`i_h_i_j_j_h^g^g_j`m`n_n_k^g]f\e\e\f\g\h\i\i[fZdYbYaZbYcYbXaXaX`X_X`XaXaXbYdXeYhZl[p\r[t\v]w\u[qZlZj[l\q\s[rZqZr[v[vZsZsZsZtZsYsYrYqXqYqXnWkWhVdT_S\S[SZRYpoonmmmmmnnoopppo~nzmwmumsmrnrnqnqmqmrmsmtmulvluktjrjqiqirisiujwkykzl}lmnnnnnnnnnonmmnoonooo}ozoxoxnwnumsnrnqnpnpmomomompmrmsmtmumulumtmrlplololmlllllkkjlilikhkgkgkglfldlcmblal_l]l\l\l\l]l]l]l]k\k[jZjZjZj[j\j[iYhWiVhVhWhYiZi\i]i^i^i_h`hbidjflhmilhjgifjgjhjhiihigigigkgmfogpgshwh{i}hhhg~g~f~f~f|exdrbmai`g`g`h_g_g_i_j^h^g^h_k`n_n_m^j]f\c\d]f]g\h\i\j]j\h[f[eZe[fZfZeZdYdYdYdYdYeYdXcXcXeYgZk[m\p\r\u\v\u[qZlZk[n\r\t[s[r[s\t[tZrZrZsZtYsZtZtZsYtYrXoXkWgVdT`S]S[SYRXpppponnnnooopqppp|oznxnwnsnsornqnqnqnrnsntnunvnwmukrkqjqjrjtjujvkwkyl{lnnnnnnnnnooonnooooooo~o|ozoyoynxnuntosoqnonononononpnqmqmrnsmsmsmrmqlpmomomnmmlmlllkljlikililhlglflemdmclam_l^l]l]l]l]l]l]l\l\k[kZk[k[j[j[jYiWiWiViWiYiZi[i\j]i^i_i`ibicjdkflhkhjgkgliljlkkkikgigiglgngpgqgrhuhyi|i~ih~g}f|f|f}f|fxdrbmai`f`f`f_f_h_i_i_h_g_h_j_l_m`m^i]e\b\b\e]i\j\j\k\k\j\h\g\i\k\j[j[i[hZiZiZiZhYfXeXdXdYfYhZj[m\p\t]v]u\q[n[m[o\r\u\t[s[r[s[r[qZqZrZsZtZuZtZsZsYrXoWjVgVdUaT^S[RZSYpqqqqppoooopqqqqq}p|pzoxpuotosoroqoqoqprpsotowownumslqkpkrktktkukwlxl{mnnnnnoonoppooooooooopo~p|p|p{pzowpvotornpopoononnnonononpnqnqmqmqmqmomomomomnmnmnlmlklklklklilhlgmgmfmemdmcmam`m`m_l^l^l]l]l\l\k[k[k[k[kZjYjXjWjWjXjZj[j[j\j]j^j_i`jajbjbjckfkfkflgmimjlkkkikgifigmhphqhrhrhthwizj}i~i}g{g{g{g{f{fwerclai`f`e`f_g_i_i_h_g_g^g_g_i_k_k^h]e\b\b\d]j]l]l]m\m]m\j\i\l]n]n\n\m\m\n\n[mZjYgYfYeYeYfYgYiZl\o]t]w]v]s\q\p\q]s]u]u\t\r\r[r[q\r[s[t[u[uZsYpYoYnXlWiVfVeUaT]S[RYSXqqrrrqqqppppqrrrrq~q|pyqwptpsqsqrprpqprqsptqvqwpvosnqmqmsltlulumwmxmzm}nnoooopoppppppppppppqpqqqq}pzqypvotorpqoqooononononoooonpnpnpnononnnononnnnnnmmmmmmmmmlljlimimimhmhmgmfnendncmcmbm`m_m^m^m]l\l[kZl[lZkYkXkXkXkZk\k\k\j\j]j_k`jajaj`j`kalckdlengoimjljjjijgigjhpititishrhshuhwi{i}i}h|gzgzg{fzfwesblajahag`g`h`i_j_i^h^g]f^f_g_h_i^g]e\e\d\f]k]n]p^q]q]q\o\n]o^p^q]q]q]r]r]q\o[lZiZgZgZgZgZhZi[m]q^u^x^x^v]t]s]t^u_v^v]t]r]q\q\q\s\u\u\u[sZoYkXhXhXhXgWfWdVaU]SYSVRUrrrrsrsrqpqqqrstsrr}q{qxpvqururuqtpsqsqtruqvrwqvpuprpsptounvmvnwmxmzn|nopppqqqqqqqqqqpppppppqqrrr}r|qypvptqtprpppopnpmpnononpnoononnnnnnnmnnnnononnnnnnnnnmmnlnlnlnkmkmkmjmiminhmfmfnendmbm`m_m^m]m\l\l\l[lZlYlXlYk[k]k]k]k^k_k`kaj`k`j`j`jalcmdnfoipjnilijiiihjglisjwjxjwishqhrhuizj}i~h}h|h|h|gzgyeucnbkaiajajaj`kal`k_j^i^g_h_h^h_h^i]i]h\h\j]m]p^s^t^u]u]t\s\s]s^s]s]t^u^u]s\p[m[jZjYjZjZkZk[l\o]t^w_y_z_y^w^v_v_v`v_v^t^r^q\o[n[p[r[r[qZnYjXeWbVbWbWbWbV`U^T\SYSVSUssssssttsrrrrrsttsr~r|rzqxrwrwsvrvqururururvrvrvquqtqtqupvpwoxoxnyozo|o}oppqrrrrrrrrqqqpppppqqqrrss~r|qxpvqvrtqrqpqoqnpmpmpmpmpnononnnnnonomomomonooonnnnnnnnmnmnlmlnmnmnmnmnlnknjnjoiognencn`m_m^m]m]l\m\m[lZlZlZl[l]l^l^k_k`kakbkak`k`jajalcmengohoimhkhkikkklinjtkxkykwjriphqhtiwi{i|h|i}i}h{gzgxfucoblbkbkbkblambnao`n_m_k_k_k_k`l_m_n^m^m]n]p]s^u^v^w^w^w]w]w]w^u]u^v_x^w]s\p[mZkZiZiZjZl[n[o]q^u_y_{_{_z_x_w_v_u`u_s^q]o]n\l[k[m[o[n[mZiXeWaV]U\V\V]V]V\U[TYSWSVSUssssttuutttsrrssttss~s}r{rzszszsysxswsvsvsvrwrwrxswswswrxryqyqzpzp{p|p~ppqrrssrsssrrrqqqqqqqqqrrstsr|rzsyswrsqqqpqoqnqmqmqmqlqlqmpmomononpnpopooopoopopooononnmnmnnnonpnpopononolokpiohoencnbnan`m_m^m^m]m]l]l]m]m^m^l_l`kakblalakakakakalcmengngmglhlhlilklmloltlwlykwjqinhphsiuixizi|i}i}gzgxgvfscnbkbjbkbmbobobqbrbrarap`o`o`o`p`q_r_r_r^r^s^v^w^x^x^y^y^y^z^y^x^x^y_z^w\s\o[lZjZhZgZi[l\o\q]s^v_y_{_{_{_z_y_v^s^p]n\k\j\j\i[i[k[l[kZiYfWbV^U[UZUYUXUXUXTXTWTWTXTWssttuuvvvuutssrsstttss~s~t~t~u~u|tztxtxsxszs{t{tztytytzszt{s{r{r|r|q~qqrrrsstttsssssssrrrrsrrrtttts~t|syrurrqqrprprnrmsmrkrkrkqkqlqmpnpoqppppppppqpqpqpqppopppoqpqprpspsprpponolojpiohofofoenencnbnanamamam`m_m_m_m_m`lblblbmblbkblclclcmenfngmhnininimjmmmolslwmxlvjphmgogrhtiuiyj}i~i}gzfvfseodkbibhckcncpcqcsctcubuatasasas`s_t_u`v`v_v_v^x^y_y^y^z]z]y^z^z_z_z^{^z^x]s\p\m[iZfZeZg[k\o]s]u^w_y_{_{_{_{_y_u^o\j[h[g[f[g[g[h[i[iZhYfXdWaV^U[VYUXTVTVTWTXTXTXTYU[stuuvvwwwvuuutssssttttttuvvv~u|uzt{t|u~u}t|u{uztzt{u|u|t}t}s}rrsssttuuuuutttttttsssttssstuutt}syswstrrsqsqsprnrmrkrjrjrjrkskrlqmqnqnqoppqqqrqspspsqrqrptquququpuptqspppoompkpkpkpjojoingngofoendndmbmam`n_m`mambmcmcldldldkdkdlcmdnenfohpjpinimjmmmnlqmvnxmujoimgngphshuiyi}ii~hzguepdlcibfbfchdldpcrdtdvdxdwbvavauau`v`v`w`w`x`x`x_y_y_z_z_{^z^y^y^y_z_z_z^z^y^v]r\n[iZeZe[g\j\o]s]v^x_y_y`z`z_x^u]p\j[fZe[e[dZd[d[e[eZeZdXdXcXbW`V]VZUXTWTVUWTXTYTYTZU\uuuuvvwwvvvvuutssstuuuuuvvwwvu}v}wwv~u}v}v|u|v}ww~v~u~u~uuuvvvwvvwwwvvvvuuuttttttutuuuuut{sxsustttttssspsnsksjsjtjsitjskrkrjrkrkqmqnqpqpqpqqqrqrqrqtquqvqvqvquqtprpppnpnpnpnpnpnpmomokoiphognencnan`nancnenfnfmeleleleldmdmcmdnengpiphnhmjmmlmmpnunvmsjoimhmhnhphtiyj}jj~hzgsendjcfcecechckdodrdudvewdxdwbvavawbxaxaxawaxax`y_y_y`z_{_{_z_y_y_x_x_y_z_{`{_z^v]q[kZf[f\g\k]q]u_x_y_y`y_y_x_u^p\j[e[dZd[c[cZb[aZbY`Y_YaYbYbXbWaV^V[UYUXUWUXTZU[U[U\U]vvuuuuvwvuvvvuuttsstuuuvwwxxxww~xxw~v}v}v}w~xxx~x}w}v~vvwwxxyxxyyxxwwvvvuuuuuuuvuuuuuvvu{txtwuvtttstrtotmsktjujtjuktjsisgsgsgrhrjrlrmrmrnrorprprrrtrurvrurvquqsqqqppppqqrqsqtptpropomokojnhnfodododnenfogognfmfmfmemdmcmcmcmcmeofpgphojnlnlnpnsntlqknimhlhkgmgrixj}j~j}iygsfndicecdcfcidkeneqdsdtducvcvbuaubwbyaxaxaxawaw`x`x`y`z_|`{_z_y_z`y`z_z_|`}`~`~`|^w]p\j\h\i]n^t_y`z`{`{`{`y_w_t^o\i[e[d[dZcZbZaZ`Z_Y]Y\X\X_YbYcXaW^W\V[VZVYVYU[V]V]U^U^vvvuuvvwvvvvvvuuttttuuuwxxxyyxx~x~x~x}w|w|w~xxxx~x}x|x~xxyyyyzzzzyyyyxwvvvvuuvvvvvwvvvvwvu|u{uxuwtvusuquotltkukukuktjthtgtftfsgshsisjsjslsmsmsnsosqrrrsrtruruqsqqqqqqprqurxryryrxqvpspponolojpiogogngohohnhngmgngnfndmdmcmcmcndneofphpjokolnnnplpkpkojnilhjglgohtixjziyiwgrfmeicecdcfchdkemeodpcpcqcrbsbsbsbubwawawaw`v`w`w`x`y`z`|`{_z_z`{`|`|`}`~``aa`|^u]m]j]k_q`w`|`}`~a}a|`z_v_s^o\k\g[e[e[dZbZaZ`Z^Z]Y[XYX\Y_Y`X_W]W\W\V[VZV[V]V^V_V_V_vwwwwvvvwxwvvvvvuuuuuvvwxxyyzyxx}x}x|w|x|x~xxxxy}y|y}y~zyyyzz{{zzzyyyxxxxwwwwwwxxxwvwwwwvvv|vzuyuvusvquoumvkvkukujtithtgtgthtiuitititjtktktktltmsnsosqstrsrrqqqqqsrurxs|s}s}s|szrwquprpppnpmolnjnioioinhnhnhnhngnfnemdmdmemenenfogoipkploklkklknkokoimhkgkgmgohrhshshrgoekehdecdcfcgdidkdmclclbnboboboapaqbrasat`t_t`u`u`waya{`|`{_z`{`|`~`a`aaaaa_y]o]l^m_raya}bbaa|ax_u_r_p^n]k]h\g\e[b[aZ`[_Z]YZXYXZX\X]X]W]W\V\V[V\V]V_VaWaWaVawxxyxwvwxyxxxwwwwvvvuvwxyyyyzzyx~x|x|w|x}y~xxyyz~z|z{z|y{y|yyyyz{{{{zzzzyyyyyxxxxyzyxwwxxxwxwv~v{vyuvutvqvowmvkvkukujuiuiujujvjvjujujukujuiuhtiukultmspsrsqrprprqrtsws{t}t~t~s}s{rzqxquqsprpqopomnkojojoioioioinhognfmfmflflfmfnfohpjqkpjoilhkikllolpjnhkgkgkglgmgngngmfkfiegdeddcfdhdidjckcjckblbmblbkakalalao`q`r_r`s`t`vaxaza{`{`{a|a~aabbaabbb`{^q]n^o_t`ya}ccbay`s`r_q_q_q^p^n^k\f[c\a[a[_Z]YZXZXZX\X]X]W]W]W\W\W]W_WaWcWeWeVdvwxxxxwwxyyyyxyxxxxxwxyyz{{{{{{yy}y}x~xyyx~y~zz~z|z{yzyzz{zzz{z{|}}|{{{{{{{{zyyyzzyxxxyyyyxxww~w|vywwvuvsvpwnwlvlvkvkvlvlvlvmvmwmwlvlvjvivhuhvjulumupuqtpsospsrsstutyu|u}t}t}t}s|szsyrxqwquqtprooomolokokojojojninhnhmhmhmhmhnioipjojohnglgkhljmmlojnilhkglgkgkfkgjgififhffeeeeefdhdiejckcjckclbkajajaiaj`k`laoaq`r`s`uavaxaza{a|a|a}babbbbbbbb`}_u_q_r`wa{a}bbb{au_o`p_q_r`t`u`s_o]i\e\d[c[`Z^Y\Y\X\X]X]X^W^X]X^X^X_XaXcXfXgWgWguvwxxxxxxxyzzzyyyyyyzz{{|}}}~}|{yyyyyyy}z}z}z}z|z{y{z{z|z~z{|{|}}}}}|||||||{{{{{zzyyyzzzzyyxww~w|x{xywwwswqwnvnvmvmwmwnwnwnwnwnwmvlvkwjviviukumuouqururtrtrtstttuuxv{u}t~t}t}u~t}t}s~s}s|rzqxpuprpppnomomolokolnknknknknknlomomomoknimhlhlimjlllnjnhmglhlhlgkgjgifhghfgefdeeffheiekdkdldkclclclbkakakakalanaparas`satavaxaza{a|a|a|a~aaa~bbbbbba`x`v`waza|a}b}a{`w`s`p`q`r`tawaxaw`s^m\h\f\d[aZ_Z^Z^Y^Y_Y_Y_X`X_X_X_X`XbYeYhXiXiXhuuvxxxyyxyyz{{zzz{{{{|}}}~~~}{z{{{zzzz}z|z}{}z}z|z|z}z~{{||}}}}~}}}}}}}}}}}}||{{{zz{zzzzyxxxxy}xzwwwtwrwqwqxpwpwpwqxqxpwpwowmvlvlvkvkvlvovqvsvtutuuutututtuuwu{u}u~t~uuuuuuutsr|qxquqsprpqpppoooonooooooopopororoqonnkmkljmjmklmknjoioioinhlglgjgighghfgefeeegfifkeldldldlclcmcmcnbnbmbnbpbqarasas`rasatavaxazazazaza{a|a{`za}bbbbaaa|a{a|b}b}b}a|ax`u`r`q`r`uaxb{a|az`u^p^m]k]h\e[c[b[a[`ZaZaY`YaYaXaXaXaYbYeYhXiXjXivuvxxyyyyzz{{{{{{{|||}~}|||||{zzzz~{}{}{}z}z~{~|||||}~~~~~}}~~~~~~~~~~~}|||{{{{{{{zzzzzy~x{wwwvxuytysxrxrxryryrxqwownwnwnwnwnwowqwswuvvwwvwvvuuusutuvvyu{u}uvvvwwvvuttsr|ryqwqwqvqvqtptptptptpupvpvpwpuproonmmmmmmmlnlokojpjqjoimhlgkfjgigifhegegfifkflflememdmdmcncpcpcpcpcqbsbtbtatar`qar`s`tauawax`v`vaw`w`v_v`zbbbaaaaa~b~b}b}b}azawau`s`s`uayb}cba|`w`t_r_p^n]k]i\h[e[c[c[cZbYbYbYcYbYbYcYdYgYiXjXkvvwxyzzzz{{{{{z{z{||}~}}}}}}}|{|{{{~{~{~{{||}}}~~~~~}~~~~~~~}}|||||||{{{{{zxx{xyywyuxtxsysysysysxrxpxpxqxqxqxrysxtwuwvwwxxxxwxwvutvsvswuvxv|v}v~vwwxwwwuttsr~r|r|r}r}r|r{r{r{r{q|q|q{q{qypvproqoqnqopmplqlqlqkqjpinhmhmgmgkgkgjfjejfkflflfngofoeneodpdqdqcrcsdtcubvbvbtaraq`q`r`r`s`t`u_t_t`s_r_r_s`wb|b~a~a}a~bbbbb~b~b~b{byaxavavayc~ddcb}azaxawav_s_q_p]m\h[f[e[e[eZdZdZdZcZcZcYdYfYhYjYlvwwxyyzzzz{{{z{zz{||~~~~~}|||}||}|~|}}|~~}~~~~}|||}|}}}}||{zzx|yyyxxvxuzuzuyuyuxtytxtxtxtxuxvyvywxwxxxyyzxzwywwvuwuwtvuvwwzv{v|vwxxxxwvuttssssttssssssssr}rzqvqupupuotosmrlqlrkrlrjqhqhrirhphpgngnfmfmflfmfogqfqfqepdpdqcqcrctcucvcxcwbubsar`q`q`r`r`r`s_s_s_r^p^p_q`tawaza{a|bbccbbccb~b|b{azb{c~dddcb~b}b|b{by`x`v_t^q]l]j\i[i[h[h[gZeYdYdZdYdZfYhYjYkxwwxxyyyyyzzz{{{z{||}~~}}}~~}}}}}}}}}}}~}}~~~}}}~}}~~~~}}|{zy}y{yzyyyyzyzyyxyxyxyxyyyyyzyzzzyzyyyyyzy{y{xzwxwwwvwvwvwxwyw{w}wxxxxxwwvutttttttsssttttssr{qxrwqxpxpwovntmsmslslsksjtjtiththtishrgqgofnfogqgsgugtfsererdrcsctcvdxdzcycwbubsar`q`p`p`p`q`s`s`s_r_q`q`r`u`w`za}bcccbbcdccccddeeddcccc~b|azay`x`u_r]o]n\m\l\k\jZgZeZdZeZfZgZiZkZlzyxxyyyyyyzzz{||{z{||}~~~}~}}|}}}}}~}~~~~~~}{{zzzy~y~y~y~z~z~z~z~z{{{zz~z}z}z}z~z~y|xzxywxwxwxwxxyx|xyyyyyxwwvvu~t~tttuttssttttts~r{qxqxqypxpwouounumtltltktktjtitiuivjwivhtgrgqgqgrgugvgugtfsfserdrdtdvdxdzdzdycvbubtar`q`q`q`r`t`uawaw`t`r`r`tawazb~cdddccdeeeeeeeeeeddcccc~b{ayay`x`w^t^r]p]o]n\l[hZf[f[g[g[g[i[j[k{{zyyyyyyyzzz{|}|{{}||~~~~}~}~}~}~~~~~}{{zzzzyz{{{{{||{{{{{z{{{y}y}x{xzwywyxzx~yyyyzzyxwvu|tztzs|ttuuutttttsss}qyqxqyqypwounsntntmtltltktktjsisiujwjxixiwhthsgrgshuhvgugtgsfserereseudwdyezezdxcvbubtasasatauawayc{c|bxatasaubxc|dddedcdeeeffeeeeeeedddcc~b{ax`x`y`y_w^t]r]p^p]m\j[h[h\h\g[f\h\i[i|}|{yyyzzzz{{{{||||}}|}~~~~~||{{{zz{|||{||||||{|{|{{zzyy}xzxzx{yzyyz{zzyxvu{txsxtzt}ttuuuuut~s}s}r|rzqxqyr{qypuornpnqnrmrmsltlulskrjrjujwiwiwivhthrgqgrhthugsfrgqfqeqdqdreteveyezezeydwcubtatauavbxbzc}c~c~bzavavbxc{ddddddcdddefffeeeeedeeddc}ax`v`vawax_w^s^p]o]p^n]l\j\i\i\f[f[g[gZe}~}|zzzz{{{|||||}}}}}|}~~}}|||||||}}|||}}}||{|{{z{{zyy~z~y~yzz{{zzyyxv|uzuyuytyu|uuuuuut~s{ryryryqxqxqzr{qyotnqopnpnqnrmsmsmsmrlqjskujvititithtgqgogogrgtfrfpfofoeodocodreufxfzeyexdvbtbtbtavbyczc|c~cc~b{bybyc{deddeeeddddeffefeeeffdeddc{avat`t`t`t`t_q^n^n^p]p]n]m]k\i[f[f[fZeYc|~~}||||||}}}}|}}}}|}}~~~~~}}|||}|}}||}~}}}|{|||||{zzzyyzz{|{yxxw|w{v{v{v{v{u}uvvvuts{rxqwqwqwqwqypyqypwotnroqnqnrnrnsmsmrnqmrlulvjuishrhrirhpgofogqgrgpfnenendndndodqeufwfwewdvducscsctbvcyd|d}c~ccb}c{c{d}deefffffeeeeeededefffedcb|awas`q`q_p_o_n^m^l^m]o]p]o]l]j\g[fZdZcYbYa|~~}|}}}}~~}}}}||}}}~~~}}|}}}}}}}~~~~~||||}}}{zzzzzz{{{zyx~w|w{wzw|v~v~v}u~uvvvtt}szrvrvqvrvqwqxqwpwouosororornrnrnrnrmrmrmsmultjsirhphphphpgpgqgqgqgpfnenendndndpereteueudvdvducscrcscvcyd|e~dccddddeefffffffeeeddd~c~cefffecb}ayau`q_o_n_m^l^k]j]k]l]m]n]m\j\g[eZdYbYaY`Y_|~~~~~~~}|}}}}}~~~}}}~}~}}~~}||}}}}|{{{zz{||{zx}w{wzxyw{w~wwvvvvvuu}t{syrwrwrwrxqxqxqwovouotororosotosnrnqnqnrmsmskqipioioiohphqhrhrgrgrfpfpfpepdpdpeqesetetdududvcucscrcsctcwd{e~eddeeeeeffgfffffeedddc~b~cdeffdc~a{aw`r_n_l_l_l_k^k^k]k]k]l^n]k[g[dZdZdZcYaY`Y_}~}}}}}}~~~~~~~~~}}~~~}||{|{{|||{z}xywwwvwwxzxxxwwwvvvu}t{tysxsxryryqyqypyowovouotptpupuotornqnpnqnrmqjoinjnjojpiqisishthtgtgsgrfrerdresetftftdrdsctcubtbscrbsbtcvdzd~eeeffeeffgfffggfed}d}dc~c}c}ddeeecb|ayau`q_l_j_j_k_l_l^l^l^m^n^o]k[g[d[e[fZeYbXaY`}~~~~~}}~~~~~~~~~}|||{||||{z|xwxuwuwvxzxxxwwwwvvu~u}t{t{s{s{s{r{q|p|pzoyoypxqwpvpuotnsnrmqmrmrmrjpinjokpjqjsitiuivhwhwhugtftftftetfufvetdrcsctctctbrarbsbubwczd~eeffeefffffffeffe}dzdzc{c|c{c{d}eeecb{ayavataq_m_k_k_l^m_l_l^m^o_p^n]k[g[e\f[gZeYcX`X_~~~}~}~~~}}||}|||z~yyxvwuxwxyx}xyxwwwvvvutt~t~t~s~srqq}p|p{qyqwpvpuototnuntmtmtmslrkrksktkujujviwixhxgxhwgvfvfueuevfwfvetdsdsdscsbsararatbvbxc|deeefeefffffee}e}e}d{dxcucvcwcwcxcyd{dedc{bxawavauar_p_n_n^n_n_m_m^o_q^r^p]m\i[g[gZgZeYcX`W_~~~~~}}}}||{z|yxxwxwyyy|yyxxxwwvwvuutttssrrqq|qzpwpvouotovnwnvmvmwmwmvmvlwlxkxkxkxjxiyhxhxhwhwgwfwfwexexevetdsdrdrcrbrararatbwbzc~deeffeffeeeee~e|e{dzcwcscrbrcrbsbtcvcyd{d|d{cxbwbwbwavat`s_q_p_p_o_n_o_q_s^t^s]o\k\i[hZhZfZcY`X`~~~~~~~}}||{z~y|xzy{y}yyxxxww~wwwvuutttssrrq~p{pxownvnuownxnxnynzn{nznymzm{l{kzkzkyjyiyixhxhxgwfxfzfzfzexevdtdsdscsbrbsasatawb|cdefgfeeeddeeee|dydwdtcpbobpboaoapcrcudwdwcvctbubwbwbwav`u`s`s`r_q_q_r_t_u_u^u]r\n\l[j[iZfYcYaYa~~~~~~~}||{zyyzzyyyxww~xwwvvuttstsrqqp}o|nynwnwoxozo{o}ooo}o{n{n|m}l{kzkyjyizi{hzhyhygzg|g~g~f}d{dydxdwcubtctbtatawb|dcdfgfeeeeefffe{ewdtcpcmbmaoananaobpcqcrcqcpbpctbvbxbybzaw`v`v`v_u`u`w`x`x`w_v^u]r\n\lZiZfZdYcXb~~~~}|{{zzzzyyxxwwxxwwvvutttsrrqppo}nznynyo{p}pqqqqo|m|n}l}k{kyjyizj|i}h|g{f|ghhgffe}dzcycxcwbububyc~ddefffeeedefff}ezdvcqcmblalbnbobnbnbobobnanbmbmbqcvcyc{b|bzazazazazaza|a|a{`z_y^w]s\p\m[j[h[fZeYe~|||{{zzzyxxxxxwwwvvuutttsrqqpoo~o|o}p~pqrrsrp}n|m~m~l|kyjyizi}i~i~h}g~hhhihgfed}d{cybwbwbyc~ddfghffeedeff|exducrcnbkbkblbmbnbnbnbnanamambmbmbocuczc}cc~c}c~b~b~b~bba~a|`z_x^u]s\p\l[g[e[eZf~}}}|{{zzyxxyxxwwwvvuuutttsrqoonopppqrstsq~o|m}m~m}k{jyi{i}iigghhijiihfee~d{czbzc|cdfghhfffeeff{evdsdrcpcmblbkblbmbmbnbnbnbnanbobnbnbocuczcdddddcccbba~`{_y^x^v]r\m[gZd[d[f~~~~|{{{zyyxyyxxwwwvuuuuttsrqoonopppqsttsqo|n|m}m}k|i{h|hiigghiijjijhgfed}c~ddeghhhgfeeef|evdrdqdqcpcocnbmbmbmbncncobpbobpbpboaobpcuczddeddddcccba`}`|_z^x^u\o[iZe[e[f~~~~~||||{zyyyyxxwvvuuttttsrqppoooooprssssq~o{n|m|l|k}j}iiiihhijjjjjkkihfeedeghhhhgffeeezduereqdqdqdqcpcpcpcococpcqcqbpbobobobpbrcvd{deeddddbccbaaa}`|_z^w]r\m\h[g[h~~}|||||zzzzzyxvutttt~ss~srqpppooopqrrqqrq~o|n|m}l}k~jijiijjjkkllklkkjhgfegiijiihgfffezevetesesftetetesdsdrdqdrdscscqcococobpcsdvd{deeeeedcccba~a}`|`|_{^w^s]o\l\k\l}||||{zzzyyyxwvutts~stsrqqqqppppqqpppqqpo~m~mlkkkkkkkkklmlkkkkkjhgfhjjjiihhgggf~fzfwfvfvfvfvfveveuetesetevdudscpcpbqbqbscvcyd~eeeddcdc~c|b{a{az`z`z_x_v^s^p^n]o]p~}}}|{{{zyyxxwvvttstssrrrrqqpppppoooppponnmmmmmlllllllkkkklkkihhijjiiihhgghgg~f{gygygygxfxfxexexfwfwewevdtcqcqbrbsbtcucxd|eedcccc~bzbxaxaxaxax`x_w_v_t^r^q_r_t~~}}}|{{zyyxxxwvutttssssssrqqppoonnnnooooonnnnmmmmmlkkkkklllkkjiijjiiihhhhhhhgg}g|h{gzfzfze{f{fzfyfwevdscqcrcrbsctcvcyd|eedcc~c}czbwavavawawawax`x_w`v`u_u`u`v~}}||{zxxxxwvuuttssssssrrrqpoonmnmnnnooonnoonmnlkkjjjklllllkjjjiiiiiiiiihhhhhig~g}g}f}g|f{fyfxevdscrcqcqbrcucwczc}dd~b~b}b{bzbwbvauavbwawaxay`z`yaxaxaw`v`v~}}|||{xxxxwvuuutsssrsrrrrqqpononnnnnooopppponmlkkkjjjkkllllkjjjjjjijkjjiiiiiihggggf}f{exevdudscrcqcrcucxc{d}d~b|a{a{azbxbwbvavawbwbyb{b|b|b|a{b{byawav~~}}|{yxxxwvuuutttssssssrrrqppppooooppppqqqqpnlkkkkjjjjkklllkkjjjjjkkkkjjjjjjihhhhhg~f{eyewducscrcscucxc{c}b}a|a{a{azbybxbxaybzbzc}ccc~c}c}c}c|bzbz~~~}}|zyyyyxwvuuuuuutttttsrrrrrrqqqqrrqqrrrrqpnmlkkkjjjjjklmmlllllkkllkkkkkjjjihiiiihgf{eydvcucucucwbxbzb{b{a|a}a}b|b{b{b{b|c~ccdddd~d~d~d~c~c~~}}}|zyyzzyxwvvvwwvvvvuttssssssrrsstttsssrqqpomllkkjkjjkkmnmmmnnmmlmllllllkkkjiiijiihfe|dycxcxdyczbzbza{b{a}b~b~c~c}b}b~cdddeeddd~d~ccd~~~}|{zyyzzxwwwwxxwwwwvuuuttttutttuuvvusrqpoonmlllkjkkkklmnmmnnnnnmnmmmmlllllkjijjiihfed}d|d|c~cbbb~a~bcccccdddeeeedec}c}dde~~~}}|{zzyyyxxwwwxyxxxxwwvvuvuuuuuvwwxwutsqoonmmmmmlllmmnmnnnnnnnnnonmmmmmmmmlkjjkjiihgffeeedcccbccddcdeeefffeedc|d|d}dd~~~}|||{zyyxxxyyyyzzyyxxxwwwwwvvvvwxxxwutsrpnmlmmmmmmmnoonnnnoonnnnnnmmmmnnnmllkkkjjihgggfeeedcccddeeeffeeffffee~d}e|e~ee~~}|||{zyyyyyyzzz{{zzyxxxxxxxxwwwwxyxwvutrqonmnnmmmnnoppoonooonnnnnnnmnnoonllllkkjjjihhggfeeddddeeeffffefffffeee~e~eee~}}||{{zzzyyzz{{||{zzyyyyxyzzyxxxyzywvutsrqpooonnooopqpppooooonnnonnoooonmlkklkkjjjjiihgfeeeeeefffgggffffffffffffee–•••”~~}||{{{{zzz{|}}|{z{{{zyyz{{zyyyyyyxvuutssrqpppopqqrrqqqqppponnmnnopppomllkkllkjjkkjihffefeffgghhhhggggfggggggfee~e~š˜˜—ØėÖÖĖĖÕՕ••–~}}|||{{{{|}}}|{{{{{zyyz{{z{zzzzzywvuuusrqrqqqqrrrrqqqqqqpnnmnoopqqonmkjkllljjkklkjhfeffgghijjjjihhhhhihggggffee~œ›šÚÙÙØÙĘėŗŗŗĖĕĕ–––––—–~~}|||||||}~~~}||{{{{zyz{{{{{{{{{yxvvuutsrsrrrrrrrrrrrrrrponnopppqqomllkklmmllllkkiggggghiikkkkkjiiiiijihhhhggfežÝܜܝÝĜěěĚĚĚĚęřřřŘŘƗŗė×××××ז~~~}}|}}}}}~~~~}||{{{zzz{{{{{{{|||zyxvvuutsssrrrrqrrrrrrrrpoooppppppomllllklllmlkkjihhhijjjkkkkkkkjjjjjjjiiiihggfÞĝĝĝĞĞĞĞŞŝŜŜƜƛƛśŚřŚŚřƙƙƙřęŘĘ×ėėÖ֗—~~~~}~~~~~~~~~}}|{{{{{{||{{z{{||}{zxwvvvutsssrrqqrrrrrrrrqpopqqppponmm~l~llllllmllkjiiijkkkkkkkkllllkkkkkjjjjihgggžÞŞŞŞŞŞƟƞƞƝƝǝǝǝǜǛƚƚƚƚƚǚǚƚƚŚƙŘŘĘėĖÖח~~~~~}~~~~||||{||||{{{{|}}|{ywwvvutsrssrqrrrrrqrrqrqqppqpppommm~mllllllllllkjjjkllklllklllmmlkkllkjjjiihggžÝĞĞŞŞŞƞƟƟƞǞǞƞǞȞȝȜȜȜǜǜȜȜȜǜǜƜǚƙŘŘĘĘėė×ĘėÖ~~}~~}}}}}}}}|{{{|||||{zywwvutsrrrqqrrrrqqrsssrrqqqpppommmmlmllllklmmmlkklllllllmmmmnnmllmllkjjkjiiiÞğŞŞŞŞƟƞƟǟǟǟǟȟɞɝɝɜɜȝȝȝɝȝȝȝǝȜǚǙǙřřřřřřřŘėח~~~~~~~}}}||||||}}|{zyxwutssrqqrrrrrqqrsttsrrqqppoonnnnnnmmmmlmmnnlllllllllmnnnnonmmmnmmlllkkjjj àğğşƞƟƟƟƞǟǟǠȠʟʞʝ˝˝˝˞ʞɞɞɞɝȞɝɝɛɛțțțǛǛǛƛƚƙƙŘʘxhs~~~~~}~}}}}}|}}|{{zyxvuttsrrrsrrrrrsstttsrrrqpoooooooonmmmmnnnnmmmllmmmnooononnnnonnmmmmlkkkk àĠĠŠƟƟƟƟǟǟǟȠɠʟ˞˞̞̞̞̝̝͞˞˞ʞʞɞɝʜʜɜɜɛɜȜȜǜǛȚǙǙƘŘØ˜gM8O{_~~~~~~~~~|}}}|{{zxxwvuttssssrrsttttttsssrrqpoooooopponnmnnnnmmmmnmnoppqpooooopponnmmmlllll áġŠƠƠƠǟǟǠǠǟȟɟʟʟ˞˞͟͞͝Ν̟̟͝͞˟ʟʞʝʜʜʜʜɝɝȜȜȜțȚȚǙƙřęØt\Fdu~~}}}}}||zyxxwvvuutttssuuuuuuttsrrrqppopppppppponnnnommnnonoppqqpppppqqppooonnmmmml¢âġơơơǡǠǠǠǠȠȠȠɟʟʞ˝̞͞ΞΞϞϟ̞̞̟͞˟˞˞˝˝ʝʝɝɝɝɜɛɛɛțǛƚƚŚÙ˜~dOr~~~}|}}}|{zyyxxxwvuuuutttuvvvvvutsssrqpppppqqqqqpppooonnoopoppqrqrrrrrrqqpppponooonn¢ĢŢƢǢǢǡǡȡȡȡȠɡɠʟʟʞ̞̞͞ΞϞϟϟϞΞ̟͞͞˞˞˞̝˞ʞʞʞʞʝʜʛɛȜțǛǛƚƚę˜fQu~~~~}||||{zzzyxxxxvvvuuutvwwwwvuttttsrrqqqqqqqqqqpqpppopppqpqrrsrrssssrrqpqqqqppppoo¢âŢǢǢǢǢǡȡȡȡɡɡʡʠˠ˟̞͞ΞϞϟϟϟϟϞΞΟ̞̝̞̞͟͟˞˞˞ʞʝ˝ʜɜȝɝɜȜǛǚŚęؗfPs~~~|||||{|{{zyyyxwwwvuuvwwwwwvuuuuutsrrqqqqqqqqqqqqppppqqqrrssssssssrrqrrrrrrqqqpp¢¡áġƢǢǢǢǢǡȡȡɡɡʡˡ̠͠ΟΟΟϟϟРРϠϠϠϟϟΟΞΞ̞̟͞˟˟ʞ˝ʞʝʝʝɞɝɜȜǛƛƚę˜~fPr}}}||}}||{zzyxxxxwvwwwwwwwvvvvvvuttssssrrrrrssrqqqqqrrrrssssssssssrssrrrrrrrqqpãââ¡  ġƢƣǣȣǢǢȡȡɢˢ̡̡ΡϡϡРРРРССССРРϠРПϟ̠͟͠͠˟ʞʟ˟˞˝ɝɝɝɝȜǛƛŚÙ™~fPq~~}}}~}||{zzyyxxwwxxxxxwvvvwwwvuuutuuttttttttsrrrrsssssstsssssstssssrrrrrrrrrqƤƤƣƢšġàŸŸŸßšƣǣǢȣȢȢȡɢˢ̢̡ΡϡТТѢҢҢѢѢѡѡѡѡСРРРϠΠΡ̠̟̟̟͡˟˞ʞʞɝȝɝɝȜƛŚÚ~dOo~~~~}}|{{zzyyyyyxxxxyxwvvwwwvuvvvuuuuuvvvvusssttttssssssrssttuttttsrrsssrrrrǥȥɤʣɢȢǡŠĠàĠšơǢǢȢɢɢɣʣ̢͢͢͢΢ТТѣңңңҢҢѢѢѢѢѡСССϠΠΠ̟̟̟͟͠͠˟˞ʞʞɞɞɝȜǛ̙wR@\m~~~~~}||{zzzzzzyyxxyyyxwvwwwwwvvvvuuvwvvwvuuuuuuutttssststtuuttttttssssssrrrǦȦʥ˥ʤʣʢɢǢƢơơơǡǡǡȡȢɢˣ̣ͣͣΣ΢ϢТѣңҤҤӤӣӣңңңҢѢѢѡСϡΡΠΠΠ̟͟͟˟˟˟˟ɞɞɞɝȜƛ̙p@2JkX~~}}}}}|{z{{{zzzzyyyzzyxwwxxxxwwwvvvwwwwwwvuvvvvvuuttttuuuuvuuutttttsstttsrrǨɧʧ˦˥ˤ̣̤ʤɣȣȢǢǡǡǠǠȡʡˢ̣ͣΣΣΣϣУѣѣҤӥԥԥԥӤӤӤӣӣҢҢѢТϢϡϡϡΠ̠̟̠͠͠ˠʟʞʞʞɞȜƛěÚڙšr=1KgW~~}}}}}{{{{{{z{{|{{{||{zyyzyyyyyxwwwxxxwxxwwvvwwwvvuuuvuvvvvwvvuuutttttuutssȩɩ˨̧̦̤̤ͦͥͤʤɣɣȢǡȡɡɡɡʡˣ̣ͣΤϤϤФѤѤҤԥԦԦԦԥեԥԥԤӣӣӣңѢТϡϡϡΡ͠Ρ̡̡͠ˠ˞˞˞˞ʝȜǜƛĚÚÛܜ›zA6Una~~}}|||||||{|{{||}~~~}}||{{{zzzz{zyxxyyxwxyyxwwxxwwwvwwwwwwwwxwvvvvuutuuuuuttȪɩʩ˨̧ΧϦϥΥͤͤˤɣɣɢʢʡɡɡɢˣ̣ͤΤϥϥХХѥҥӦӦԦէԦԦզեեԥեԤӤӤҤѣТТϢϢ̟͢͢͟͡͡͡˞˞ʝɞȝǝƜĜÜÛܜÝߟu=2OdW~~~~~}}}}}{||}~}|||{|{{||{{zzzyyyxzzzyyxxxwwxxxxyyyxwxwwwwvvvvvvvuuuuȩȪɩ˩̨ΨϧЧϧϦϥ̥ͥˤˣʢʢʢʢʢˣ̣ͤϥЦЦЦЦѥҥӦӦԧզէէէզզեեեԥԥԥӤҤңѣѣϣ΢ΡΡΡ̞͟͟͠˞ʞɞȞǞǝŝěĜĝÜĜşŠà v>3PdX~~}}~}~~~}}|}~~}}}}}~~~}}}||{yyzyzzzzyyyyxxxxyyzzyyxyxxxxwwwwwwvvvuuɪɪɪʪ̩ͩϨШѨѧѧЧΦ̥̤ͥˣˣˣˣ̣ͣΤϥЦѦѦѦҦҦӦԧԧէէէ֧զզ֦եզզեեեԥԥӤҤҤѣТϢϡϡΠΠ͟͟˟ʟɞɞȞǞƜƜƝŝ›ĝƟǠơġ¡|l8,DWKt~}|}}}}~}}}}}~~~~~~~}{{zzzzzzzzzzzyyyyzzzzyyzyyyyxyyxwwwwwwwvȪɪʫ˫˪ͪͩϩѨѨѨѧЧϦ̥̤̤ͦͦˣ̣̣ͣΥϦѦѦҦҦӦԧԨԨէէ֧֦֦֦֨֨զզ֦֥զզզԥӥӥӤҤѣңѢТϡϡΠ͠˟ʟʞɞȞȝȝȞǞŜšŝǞǠǡơšá¡nZ- 0@3My~||}}}}}}}}~~~||{{zz{{zzzzzzz{zz{{zyzzzyzzyyyxxxxxxxxxǪȪʫ˫˫̫ͪΪϪЩѩѨѨЧΦΧΦ̥̤̤̤ͦͤͤΥЦѥҥӥӦԨԨըըը֨֨רק֦֦֦צצ֦զզզեզեԦԦԥԤӣңѣТϢΡ̟͠˟ʞɞʞʟ˟ʟǝśƜȟɟɡȡǡơšà o_Km&%3';xh~}}}}}}~~~~}|||{{||{{z{{{{|{{z{{zzzyyz{zyyyzzyyzzzzƬǫȫɬɬ˫̫ΫΫΫϪϩϨϧШШϧΦΦ̥ͥͥͥΥϥϤХѥҥӦԨթթթ֨֨רררקצקاק֦զԦզզ֧էԦԦզԥԥӤҤѤФУϢ̡̠͡ˠʟ˟̠̠ˠʟ˟̡ˡˢʢȡǡƢġázdWDe;,@$%M@bwg|~~~~~}||{{||||{{{{{||{{{zzzz{{{{{{{{{{{{{zz{ūƫǬȬɬʬ̫ΫϫϫΫϪϩЩШѨѨШϧΦΦϦΦΥХϥХХѥҦӧթ֩֩֩ררשרااקببק֧֧֦֦էզզզզզեԥԥӥӥҤѣУϢΡ̟̟͟͠ϡϡϡϢϢ̣ͣ͢ʣɢȣƢĢ¡~iRA`0"4%"-!2L?bkZr~}}}}}}}|{{|||{{{{z{{{{{{{{{|||||||{{{zƫǫǬǬǬɬˬͫΫϫΫϫЪЪѩѩҩѩШϨϨϧϧϧϦЦХХЦѧҧԨթ֩֩֩ששששרררשררר֧֧է֧֧֧էզզզզեԥԥӥӥҤѢϡΠ͠ϡТѢҢѣУϣϣΣ̤ˣɣǣţâcT3&<#(M@bl~~~~~~}||||||{{{{{{{|{{{{{||}}|||{{{zƫƫƬƬƬȬʬˬ̫ͫͫΫϫЫЫѪҪҩѩЩϨϨϨϨϧЧЦЦЧѧҧӨը֩֩֩ששתתשששששةש֧֧֨֨֨֨էէզզզզզզզԥӥӤңТССТѢңңѣУϣΤ̤ͤʣȣƣãYM|(2   D8Y{j~~}}||||||{{zz{||{{{{|}}||{{{{{{ūƬŬŬƭǬɬʬʬˬ˫̫ͫΫϫѫѪѪҪѩϩϨϩϩϨШϧЧЧѨҨӨթ֪֩֩שששתتةתתةש֩֩֩ש֧֧֧֨֨է֦֧էէէզզԦԦӥӤӤңѣѣңҤѤѣФϤ̣̤ͤɣƤäQIy%0      9.On_~~}~~}}}}|{{yz{{|{z{{{|{{{z{|||{ǬƬŬŬƬƬȬɬɬɬˬ˫̫ΫϫЫѫѪѪѫЪЪЪѪЩϩЩШѨѧҨԩթթ֪֩תש֩תתתתتשתתששש֨֨֨ר֧֧֧֧֨֨է֨էզզեզԦԦԦӥӤҤҤҤФϤΤ̤ͤʤǥĥG>a  -#:`S~~~~~}}||{yz{{{{zz{{zzyz{{||{{ǭǭƭƭǭƬǬǬȬɬʬˬ˫̫ͬΫϫЫЫѫѫЪѪѪЪЩЩѩѩҨҨԩթժժ֪֪֫֫תתתתתتתתתתتתששששררר֧֧֧֦֧֧֨֨֨֨էէզԦӦӥҥХϥϥͤˤɥǥĤzp>4Q !!"!)+VHm~~~~~~}|{{{|||{{{{{zyyzz{{zzzȮȮȭǭƮƭǭǭǬȬɬʬʬʬˬˬ̫ΫЫЫЫЫѪѪЪЪЪЩѩҩҩөԩժժժիի֫תתתתתתתתתת׫תששששששר֧֧֧֧֨֨֨֨֨֨֨֨֨էէԦӦѥХΥͥ˥ʥǥĥmc9.F"$$$%$"&"L>^y~~~~~~}{|||}||{{{zzzyyzzzyyyyȮȯȮǮƮƮǭǭǭǬȭȭɬɬȬɬʬ̬ΫΫάϬЫЪЪЫЪЪЪѪҩөԩԪժԫժժ֫֫תת֪֫֫תתת׫׫תتتתשةששש֩֩֩֨֨֨֨֨֨ר֧֨֨֨էӦҦЦΥ̥ͥʦǦĥj^5+A     E9Uu~~|{||}}}|{zzyyyxxxxxwwwwǯǯǯǯƮƭǭǭǭǭȭǭǭȬǬȬȬɬʬ̬̬ͬάάϫЪЫЫѪҫҪӪӪӪԫիիի֫׫׫֫׫׫׫֫֫׫׫ثثتتتتשתتتת֪֩֩֩֨֨רררשררר֨էӦѦϦΦ̦ʦȦĥla2)?       A6Ru~}|{{|}||||zyyyxwwvvuttssǰƯƯƯůŮƮƮƮƮƭƮƭƭƭƭƭƭǭȭɭʭˬ̬άЫЫЫЫЫѫѫҫӫӫӫԬիի֫׬׬׬׬ج׬׬׫׫ث׫ثثثثتתת׫֪֪֫֩թԨը֩֩֩שةשש֩ըԨӧЧΧͨʧǧĦqd5+A        A7Uw~}}|{z{||{zzxxxywwvtssrrqpưǯƯƯŰįĮŮŮŮŭĮĮŮŮŭŭŭŭŭƭǭȭʭ̬άάϬϫϫЫЫѫҫҫѫӬԬիի֫׬׬׭׭ج׬׬جج׬׬جج׫׫ת֫֫ժ֩ժժթԩԨըթ֪תשש֩թԩԨҧЧΨͨʨȧŧ§xj:.F    C9X|~}}}{z{{{{zyxwvvvuuusrqqpnnǰǰǰưŰůįįůįĮïïïĮĮîîíííĭƭǭʭ˭̭ͬͬάάΫϬЬЫЫѬӬԫիի֭֭֬׭ج׬׬׬׭׭֬׬׬׫׫֪իիժժԪԪԩԨԨԨԩժ֪֪שש֩թԨҨѨϨͨʨȨŧç[Mu0#5   =3Oi^}|||{zzzzzyxwvuttsssrqponmlɱȱDZDZƱŰŰŰİİİð¯¯ï®îííĭŭǭȭɭʬˬ̭̬̬̬ͬΫάЬѬӫԫլխ֭֬֬׬׭֭֭֭֭֬֬׬׫ת֫իժժժԪԩөԨԨԩԩԩժ֪֪ת֩թӨӨҨШΨʨȨŨç{lF9X!!XLu~}|{zzyyyxxwwvtssrqppponmmlkʲɱDzDzƲŲűııññ°¯¯®®®®íĭƭǬȬɬʭʭʭʬʬ̬ͬάϬѬӬԬԬլ֭֭֭֭֭֬֬֬֬֬֫֬֫իիժԪԪԪөҨөөөөԩժժ֪֪ժԩөөѩϩͩɨǨĨ§~SGndW~}|{yxxxxwwvuuusrrqoooonllkkjʲɲȲƲŲƲŲIJò¯­îŭŭƭǭȭȬǬȬɬʭ̬ͬϫЬҭӭԭԭխ֭լ֭׭֭׭֭֬֬֬իլիԫԪӪӫӪҩҩҪҩөӪөӪժժժԩөҪѪΪ̪ȩŨ§UKv #$eX~}||{yywvuuuuutssrqqpoonnnnlkjii˳ʳȲDzdzDzƲŲIJ²îííŮŭƭƬƬǭȬʭˬ̬άѬҭҭӭԮխխ֭֭֭֭֭֭֬֬լլլԫԫԫӫӫҪѪѪҩҩӪԪԪժժԪԪӪҪЪΫʪǪĩ]S  4)@na~}{{zyxwvutttttssrqqppooonnmlkjii̴˳ʳɳɳȳDzƲŲò²îîĭĭĭŭƭȭɭʬ̬ϬЭѭҭӮӮԮխխխխխխ֭խլլլլիԫԫӫҫѪѪѪҪӫԪԫիԫժԪӪѪЪͫʪǪécY  ?4Qui~~}{zxxwvvutsssrrrqppooooooommkjjjʴ˳˴˴ʳɳȳDzƲIJó³®­íííŭƭǭɬ˭έЮҮҮӯҮӮԮԭӭӭԭԭԭԬխխխԬԫԫӫѫЫЪѪѫҫӫԫԬլիԬӫѫϫ̫ʫǪêe[%$!A6Tvl~}{ywwwvutsrrrqqpppoooonoppnmlkkkdzȳʳ̴˴˴ɳȳDzųijó³­íìĬƭȭ˭έϮѯѮѯѯүҮҮѭҭҭӮҭӬӭӭӬӬӫҫѫЬϫЫѫѬҬӬԬլլԭӬѬά˫ɫƪªd[ ')$ "">4Psj~}{zwwvvuutsrqppppppooonooooonmmllŴƴǴȳʴʴɳȳȴƳĴôô¬ìíĭǭʭ̮ήήϯϯЯЮЮѮѭѭҮҭҭӭӭҭҬҬҬѬЬϬЬЬѭѭӬԭծխԭҮѭϭͬɫƫêcY &,C8Vul~}|{zyywvvuutssrqpppooppppoooonnnnmmlĵĴĴƴǵȴȴȴȴȴǴŴĴô¬íĭǭʮˮ̮ͮͮήίϯЮЮЮѮѭѭҭҭҭѭѭѭЭϭέέϭЮѭҭӮӮԭӭҭѭЭͬʫƬī`V#"""7+Cmg~}}||{zyxxxwvvuttssrrqqqpppqpppponooonmllĶõõĵŵƴǴǵȵȵȵƴŴĴ´¬­íĭƮȮɮʮ̮ͯͯίϯϮϮЮЮѮҮѭѭѭѭЭϭ̭ͭͭέϮЮҮҮҮӭҭҭѭϬ̬ʬƬī]S{ #!! +1hd~||{{zzyxxxxxwvuttssrrrqqqpppppppooooonmllĶöööĵŵƴƵƶǶǶƶƵŵõ­íîîŮǮȮʯ̯̯̯ͯίϯϯЯѯѯѮѮѮЭϭέ̭˭̭ͮϯЯѮҮҮҮѮЮϭͬˬɬƬë[Pv  )-fd~}}||{{zzzyyyyyxxwvutsssrrrrqqppppppoonnnmmlkĶĶööĶƶƶŶŶŶŷŷŷķö­®®Įůǯɰʰ˰̰ͰΰϰаЯЯѯѯЯЮϮ̮ͮˮ̮̮ίЯѯѯүѯЮϭ̭ͭˬȬƫëVLo   '(db~}||||||{{{zz{zzyyxwvuttsssssrrqqppoonnnmmlkkjǸƸŸĸŸŸƷŶͶ¶···¸ïįƯȰɯʰ˰ͱαΰΰΰϰϰϯϯίίͯˮ˯˯ͮίϯϯϯϯή̮ͮˮɭǬūëQGk "#%)!a_~}~}|||{|{{{{zzzyyyxxxwvvutssssrqrqpooonmmlkkjiiȸǹƹƸĸŹƸŸķ÷·¯ïŰưȰʱ̱ͱͰͰͰͰͰΰΰΰͰͯʯɮɮ˯ͯͰί̯ͯͯ˯ʮȮǭŬì{SIo(.%(!'._]~~~~}}}||{{{{{{zzyyyzzyyxxwwvutsrrrrrqppponmmlkjjiiɸȹȹȹǹƹƸƸŹĹùðİƱɱ˱˱˱˰˰˰˰̰̰̰̯˯ʯȯȮɯ˯˯̯˯ʯʯɯǮŮĭ¬xOGk&,!!#) _\~~}}}}|{zzzzzzzzzz{{{{zyxwwvtttsrrrrqqqpponnllkkjj˹ʹʹʹʹɹȹǹƹŹĺºñűDZɱʱʱʱɰʰʯʰʰʯʯʯɯȯȯɯɯʯʯʮɮȮǮŮí¬zuF@a !!\Z~~}}}|{{zzzyyyz{{||{{zyxwvuuuttsrrrqqqqpoonmmllllͺͺ̺̹̹˺ʺɺǺƺĻúºº¹ñıŲDzȱȱɱ˱ʰʰɰɰɯɮȯȯɯɰɰɰɯɯȮǮƭĭ­­urC=^##&%."%VV~}}}||{zzyyz{{{|{zzyxwvvvvuttsssrqppoonnnmllmmλͻͻͻ̻˺ʺɺǻƻƼŻû»»²IJƱȱʱ˱ʰʰʰɰɯǯǯȯȯȯɰɯɯȮǭŭíqnLEl4,I*2$""&3,J-$=RS~~~|||{{{zzzz{|||{|{zzyxwwwwvutttrrqqpppoonmmlmnνͽͼ̼˼ʻʻɻȻǼǼǼżļü³ŲDzʲ˲˱̱̱˰ʰɰȰȯȯǰȰȰɰȰǯŮî®mjMFo6.P%(!+"9.#=SQ~~|{zzz{zzzz{|}}||{{{zzyyxwwvuttsssrrqqpoommlll;;̾˾˽ʼʼɻɼȼɼɼȼǼżûIJDzɲ̲ͲβϲβͲ˲ʲɱɱȰȰȱȱȱǰưįﮫjfD<_-$> #).$@QP~|~|{zzzzzyyz{|}}}}}}}}|{zzxxwvuuuutsssrqpnmlllk˾˾˾̿̾̽˼ʼʼʼ˼˼ʽɽǼżļû»»óƲɳ˳βгггϳγͳ̳˲ʱɰȱȱȱDZǰŰįﮪªhb;1J%)!!$,-%@MKwwx~|{{{{zzzz{{|}~~~}|{{zxwvvvuuutssrqpnlllkiʽʾ˾;ξ;ͽ̽ʽ˽̼̼̽˽ɽǽƼļü¼³ijųdzɳ̳ϳгѴѴѴгγ̲˲ʱɱȱƱűƱŰİï¯e_>4Q.#;%' %,)4GCjsr~}}||{{{{{||}~~~~~~|{zzxwwvvvvvvtsrqpnllk~i~gʾ˾˾̾ͿοͿ̿˾˾̾ͽ̽̽˾ɾǽŽý¼óĴƴǴɳ˳ͳϳгѴѴдϴδʹ̳ʲȲDzƲűŰİİïb\KAj<3V""!#'&.D>cpo~~~}}}|}~~~~}|||{{zyxxxxwwwwvtrqonlk}j|hzgɾʾ˾˿̾̿̿̿̿̿˿̾̾̾̿˾Ⱦƾľ½óIJƳȳɳ˴̴γϳѳҴѵѴѴдδ̳˳ȲƲƱƱűŰŰİï_YF>b91Q%*0%?HAhnl~}}~~~~}}|}||{{zyyyyyxxwvtsrpn}k|j{iygweȾɾʿ˿˿˿˿˾˿˿˿̿ͿͿ̿˿ʿȿſÿóųȳʳ˴ʹδϳгѴҴҴѴѴѴϴͳ˳ɲƱƱűƱƱưưݯ\V{7.F/&=$%%*8/NLEnkh~}~~~}}||{zzzyxwwusqqp}m|jzhxgvfuežǿȿɿɿʿʾʾʾʿ̿ͿοοϿͿ˿ȿſÿ´Ƴȳ˴̵δϳϴдѴӴӴҴѴѴдγͳʲȱƱŰƱƱǰȰƱİïZTy:1N5-H*."+"6;2RIBhhc~|{{zywvtrp~n}n|mzkyhwfuesdsdĿſƿǿȿɾɾɾʾʾ˿˿˿ϿпѿҿѿϿ´ƴɵ˶ͶεдддѴҴӴҵҵҴѴϴδ̳ɲDZƱŰƱDZDZDZƱİð[U{NFrE>e)+$!#"3*B7-IA7Vha}|{zxvs~q|p|nylxkxjvguetdrcpcocĿľſſžƾǾȾɾɾʿ˿˿˿пҿտԿѿĴǵ˶ͶηжжѵѵѴѳѴҴҴҵҵѵдͳʳȲƲűűıŰƱưŰð¯YSxA8^7/P!"+!7@6Via~}{zxv~s}q{ozmylwivhvgtescrcpbnbmbÿĿĿÿĿžƾǿȿɿʿʿ˿οпҿӿԿǵ˶ͶζϷзѶѵѵѵҴҳҳѴѴѴѴϴ̳ɳDZűŰİðİİİİݰXRv7,H-$<0'>C9Xg^~|{zx~v}t|r{pynxlwjviuguftdrdqcocnbmcÿĿſǿǿǿȿɿοпҿĶȶ˶ͷϷзѷҷҷӷӶӵԴӴѳггϴδ˳ʲȲDZƱııűŰűűı°UOq3(?0&=(. ')A8XNDid[}{y~x}w|tzqzoymxlwjviuhugtesdsdqdqdpdodſſſſƿ˿οŶɷ̷θѸҸӸԸոֶַַյӴѳгϳδʹʹ̳ʳɳdzDzDzƱƱƱűıð°WOrG?bB:[.#7%$! 4*?I?bPDjeY}|z~w|vztyrwowmwlvjuiuguftftesdsdsdrdrdqdĿſȿ÷Ƿʷ͸йѸӸԹֹ׸׷׷׶ֵԴҴгγδʹʹ̴˴ʴɳȲDzDZƲƲűűİð]Ty^WRLv+ 1&'&'?5OB7SB4MgZ|{~x|v{uytwrvpunsmsksishrfsfsfsesesdsesdrdrd·ƷʷͷϸѸӹԸոָ׷׷ֶնԶҵдͳʲʱʲʳɳɳȲDzDzdzƲƲŲűıñXNpTKvJAh'(#!.#4?2HH9PfWt~}}|zzvytwruqsqroqnpnokoipgqgqfqererfqeqfqeqepd÷Ƕʶ̷ηзӸԸոַ׶׷ַշԶѵϴ̲ɱDZƱƲdzDzȲɳɴȳdzdzDzƲŲı±°ðįï¯YOpH=^<2N"!%#H:SXGejYv©ĩĩĩç~}|{zx|vyvvtrrqqpppponnnmnkminhogpfpepfpepeofofoeodöǶʷ̷ηзѸӷշַַַշԷҶдδ˲ȱưŰƲDzɳʳʳɳɳɴȳȳDzƲıñð°°İŰƱƱƱİï¯SIf8,A4(;'* G:RXGdiWrªĪŪǫȫȫȪǪŨç}|{yxv}tzswsvqroqoooooonnmmmklimhnhngogofofnfngmflfleͿŷȷʷ̷ͷϷѶҶԷշշշӷҶѵϴʹ˳ɱưűƲȲɳʳɳɳȳȳɳɳɳDzƲƲűñ±±ðİưDZȱȲDZưŰï¯SHeF;XA6R.!2$#0%4D6KO?UiWpëīīŬŬƭƭǭȬɬʬ˭˭˭ˬ˫ɫȩĨ}{zyxwv~t{syrvquptoqnpnononomnlllkmjmjmjmimimimimilhkhkhѿѿ¸ŷǷɷʷ̷ζжҶԷշշӷҶѶдϴʹ˴ɳDzŰưDZȲɲȳdzDzdzɳʳʳɴɳɳȲȳdzŲıİŰǰȱɱɱȱƱŰð¯_TxaWSIn- .'$''A4IE7KK:Mp]vǭȭȮɮʮʮʮ˯̮ͯͯίήήϮϭά˫ɪƩç~|zxwvvuut|szsxrvquptornqnpmpmpmololnlmlmlmkmkllkllklkljkjl÷Ƹɷɷ˶Ͷ϶ѷҶҶӷӶҵѶеδ̴ʴɴȳDZưưDZdzdzƲƲDzɳʳʴʳʴ˴ʳɳDzƱűűƱDZȱȱȱƱűİð¯XOpVKqJ?_*(% #**8*7L;Kp\rʮˮ˯˯˯̯ͰͰͯΰϰааѰѰҰѯϯή˫ȩħ|zywvuutt~t|szrwrvquosnrnqnqmqmqmqlqlplpmomolnknknjnjnininin÷Ʒɷ˷ͷϷзҷҷӷӶҷѶеε̵ʴɴȳDzDzŰŰƱDzdzdzȲȲȳȲȳʴʴʳɲDZűűƱƲƲƱƱűűİð¯TIhJ?]A6N)&!%G6BXDSjUg˯ˮ̯̰̰̰̰̰̰ΰϱббѱҰҰұұѰЯͭɫƩç}{yxvvuttt~s|szrwqvpuosnrmqmqlrmrmqlplplpmplokojojojoioiohohoøǸʸ̷ͷϷзҸӷӷӷӷѶеε̴ʵɵȴȴƲŰİŰűűűƱűűűƳȳȳȲDzƱűűƲƲƲƲŲűűİï}OD_=1G9,?,)! L:GcN\r[k§̱̰̰̰̰̱˰˰˰̱ͱααϱббѲұұұаή̬ȩħå¥~{zxwvutssr~r|qzqxpwountmtlslrlrmrlpkpkpkplpkpjpjqjqjqiqirhqiqøƸɸ˷͸ϸѸҸӸԷӷҷҷѶ϶Ͷ˶ɵȵȴƳıïïïîïðï°±ıŲƲƲűııIJŲƲƳƳDZDZűİï{TGaM>WF7L0".% &H7CdO]}etĨββͱͱ̱˲ʱɱʰʱ˱˱˱̱ͱαϲвбѱҰѰϮ̬ʪȨǧƥŤã~|zxvutssrqp~o|ozoxnxmvmulultlslsmrlqlrlrkrkrkrkrkrkskrisisitis¹Ƹɸ˹͸ϸиѹҸӸӸӸӸѶжζ˶ɶǵƴųIJıﭯﱱ±ñññIJŲƲƳȲȲDZűİð¯zhXtq_`Ok3$0'#&6%0F4?[FR{erŪгвϳβͳ̲˲ɲɱʲʲʲʱʲʱ̱ͱβϱбѰѰѰϮϭ̪ͬ˨ʧȦƤ¢}zyxvusrqqpon~n|mzmylxlwlvlultmtmtltmtmtmtltmtltltlsktjtitiuiuhvŹɹʹ˹͹ιиҹӸӸҸӶѷϷͷʶǵƴųijIJñ°òIJijIJIJƲȳɳɲȲDzDzƱưƱzyhmhVn2#-($)#2#+D2GhR]ϳϲϲϲβββͲ̲˲ʲʱ̱̱̱˱ʰɯɯȯɯʮʮʮ̧̬ͬͫͪͩͩͩͩͨ˦ɥƣâ¡~}}|zywvurppoooppqq~r}r}r|r|r{q{pzpzpyoxnxnxmwmxlxlxlwlwkxkwkwjwjwiwiw˿˾˼˼ʼ˼̽ξξϿο̾˾Ⱦž¹ĸŷƷǷȷɷʶ͵϶жѶжϵε̴ȳŲò°İűDZȱȱDZǰưDZȱʲp@2;  )NQI>ZhaZRw+(&"$XMhob[LiTE_}}|~|||z|y}y}x~wwxyzz{~{|{}|{|y}y}y~x~x}y}z}{}|||{{{{|~}}~}~}~|~}~}~}~ƿǾǾȽɽ̽ξϿпѾҿѾϾνͼͼ̻̺˸ʷɶǵŴųųųĴôô³°ðï®|G9LOEdNEc?5K,+'$$ 2(6OD_`SwSGc}~|{zyxwvvuvvvvvvwwx~y}z|{{|{|||}{~zzzzzz{{{|||{{{{ʾȽǼƺŹƺȻʼ˻̺ͻϼнѾѿѿѿпппϾνͽͼ̻˹ɸȷǶŴijòòò²³|I;QSIlogaZ9-A)($! I?Wh\oa_Rr~|{yxwwuutttssrrssstuvxyz{{z{zyyzyyz{{{zzzο˾ɼǻƻƺǹɹʹ˹˹˹˹ʹɹɹʺʺʺ˺̻˻ʺʺɹȸǷƶŵóó³}RD\sixha=1F**,,#VMkwkwieWx~|zxwvutssssrqqpopqrsuwxz||||{zyyyyz{{{{{˾ɽɼʻ˻ʺɸȷǷƶĵĴĴõĴƵƶƶƶŶŶĵôôô³³}SE[|pqhRJk4);+)4'6(&JA[h]l`[Mjz~{ywvusrrssrqqpoopqstvwx{|~~}|||||}}}}}ο̽˻ʺʺɹǸƸ͵µ³³|C4ESE]OAW;/@)(+(5(6)'))?3HNA\C5Kwn|zwusrrrrqqqqqqstuvvwxz|~̾˼ɺǺƺŹĸĸķµs:,:C6EVI_K?S/#0)%,*# "8+=K1C2%2X, ,%!)&% :/>RF\^OgRCUG8HvA8Q;4OMGlSKnB9R$""+(#3)6L?S\Md_OdF7Eu~;2HE@_WSz_\NIi-#1&$4(6&%*)I=PfXssdUFZt|@9T`_tuig=2G, -;0C,!-5*7cWrzwm]wsv~HDdru{~^\9-?))-#.$!K@Wvj~rc|mowB>^cecdVT|CUG:O?3D_Rk_c) 1=8WDAeDAc83K&%()>5J/%4#;1CRGbG:P?3BUI^xWY.)>FDgLHoTRyEB`(("!*"."2)5K@TZMeXKaXL_ZM`s}PNrECchklmjl3(9/$2I@U2*7MCWqe~qqrqbvr~~~x{KEbVRtxvYTv9-=7,:SJb?5FE9Jk^w{~xh|r~}}~~}~~}vuI@W^Wu~ykaPCY5(62'3E2@PCUcTkbTjTEW@2@1#-)"%( ;,5M=HWFS^MY[KWQAJpboj`x5'3E8GYK]\M_ZL\F9E& %*!&7)1O?J]KYYITRBLJ9Bj[idZq?3AVI]h[r|oykYL\$&0!)(!+"UHUxi}fVgZIUTDNk\k²òijóòòòIJò³óó²²ò±±^TlK@Rncw}{nZM^3&/.!(7)4/"*K?Ln`tquex[LXXIUqbsĴƵƵƵƵƶƵŵŶŶƶŶŶŵŵŵĴĴĴijijóóòò²±XNf]To{udXoL?O>1?3&.7+62$.ZL^te{sdypatWIUZLZrcwz³ŴƶƶǶǷǷƷƷƷƷƷƷƸǸǷƷƷƷƶƶŶƶƶƶƶŵĵôôóóò²~~RIakexoh]xG;M8-;;1@/#,,"+.!*B4BTEWXJ[M@MMBMXLZfXj~póŵƷǷǷǸǸǸǸƹƹƹǹȹǺȹȹǹȹǹȹȹȺȺɺȺȹǸƷƷƷƶƶŵŵŴijóóòò±|||}}}}}}}~~~~~|xLC\UMhe^~H?S<1?3%0/!+.!++&,&+%2$.6(41%.+'-"(2&.E9FocyõööĶĶõõôôŷȹɹɺȻȻȻǻǻƺƺǺȻɻȻȻɼɼɼɼɼʼʼʼʼʻɻɻɺɺȹǸǸǸǷƷƶƶƵŵĴóò²|{{{{||||||||||}~~~~rpE4?4(3+%)!' (".(-&,$,$. %0#(4',NBL¿¾¾¼ľľý»ºŽſſĽ»WMZ4&03%05'15'13%.0",3&/8)3A5BE:J>1@6&1:+47*23&.5&04%.2#+2#*3$,5'/:,3C5?¼þĿþ½HF8BF8BC4?8)34%-4%.7'19)3:+4;.7xrþĿĿÿ¾¾¾¾¿};-8.'0!(-%*",&0#,+&-!'7*0=07:.55)0;/6A438D8=C7<>48@6:C74:LAIOEMODMNDONDNNDNMCMI>H8+4& )!1%+- &+$B8A}xzZP`D:E.$+3'/,!'+ %-"(*%+!'\Vfkfxjdxjdzjd~gcfcidf`yMCT0$- )#5(03(/4(.5'-nhyvpK>LE:FG@6>;09:,5\Td~}|{zzzzz|}|{zzzzzzzzzyzzz{|`[vD:I<1<@6BI?O4(13'/0$,(!;2>kf}^Yj++#9.6J?JLBN@6@/")NFT}{z{z{{zz{z{||{zyxxwwwwvvvvvvxyyyzzz{|}~TNdTK]KBR5)2;/:."*1&.2%.+#?7Dwsfav.%$+$;.6A5@>4?4*4C;G{w}||{{|{{{||{zyxwwwwwwvuuuuvvvvuuuuvvvwwwwxxxxxxwwxyyzz{||yNEZUL`RI[<1=4&08+5=2<:.8/ *C;J~{lh3#,#&.")=1;I>MG=K@7Be^q}|{{{{{zzyxwwvwwwvuuttttttssssssssssstttuuuuvvvwxxxyy{}icRH]PFZQH\G(&'#@5BK@OGPJ?RJ>R0$./"+0",5(37*7LDXytMBS/ ')!*&?4@B6C>1<@4?G=L~{}||{zzzyyyxwwvuuuutttttsttsstsssttuuuuvvvvwvwwvwxxxyz|}~}|{{{}}}}}|}}|||{|{{{||}||||}}~~~wvND\OF^?4E>1?@4C/$-3'25(36(49+:QJb~zZPg4$-,%5)3:-8;/:>2=@4?=2?tp~|{{zyyxxwvvvvuuuutuuttuuttuuuuuuvvvvvwwwwwxxxyyz{}~~}}}}{zyyz{|{z{{{{{{z{zz{zzzzzzzzzzzz|}}~~mn?5J7+;5)66*76+83(46*75)42$/7,:UOkc\x1!(, &;0>I>OKBSC9G3(01'0c]v~|||{zzyyyxwwvvvuuuuuuuvvvvvvvwwwwvwwwxxyzzzzzz{{}~}}{{{{zyxxxxxxwxxxxyxxyyxyyyyyyxxxxyxxyz{{|}}~~~~~~~~{eeJB`=3I9->9/?7-=1%22'46*71$/7-3A;3B>3Brp~}}||{zyzyyyxxyzzzzzyyyyzzzzz{||||}~~~~~}}||||||{{zyyxwvvvvvvvvuvvvvwwwwxyyyyyyzz{{{||||}~~~muUS{_a=8Q?7O<3K7-A?9T>7M:-=2%/A3B5'2QIaus~~~}}|{||{zz|||{{|||}}}~~~|}}}~}~~}~~~~~~~~~~~~~~}}|}}}~~~x|[Y}F>UOIgG>YVOrPIi<3G7,<;/>=0@5'4NLjx|~`\~7*6,&4)6=3CD:MG>RB8J=1AE8Jhd~~~}}}}||}}}~~}~~~~~~~}}}}|}}}}}wwxyzz{||||||||{|{{{{|{{{{|}}~~~~~kkH?W:/??6J>3GB8PB7O;/C5)8;/=?4F6*8TTvig=0@/!*0%18.>>4F>4E9.=5)67)5[Vv~~~~~}}}}~}~~~~~~~~~}}}}}}|||}rrrrstuvvwwwwvvvvuuvvvvvvwwxyyzzz{|||||}}}~~^\?3G;2E90BIC^@7M8,<7,<5+:6*68-<6)7ZZ~ljB5G. )2'4@7KJAZJBZC9M4(50"+OHbus~~~~~~~~~}~~~~~~~~}}}|||{{|rqppoprrrsstssrrqrrrrrrrstttuuuuuvvuvvwwxxxyyz{{|}|}~~~uyQLl@6JB;Q90BMHdC;R5*82'44)72&1.#,2&1SQqtxtxtwtwsvsurususutvtursqrooa]C6H- )1%1@7JJBZMD^F=S5)6.!)C9Lb\}|}~}~~~~~~~~~}|}|{{{{{zzztsssrrtuutttutsrrrrsssttuuuuuvvuutssttssssssstvvwxxyz{{{{|||}}}}}}}}~~~~~x~quqtnq^\C;R=2C?5H2%/2%02$./!),&- (2&0/",1%2>3GKA[LC]MC]KB[KBZKB[I?VI?VJ?VJ?UJ>TH;RF9OE9OE8M@2B.!*(%, '/"*1#-0#,+%+$4(4F=R]Xxb^b_po~~~~~}~}}}||||{|{zzzzzzzzqqqppqrssssssrrrsssttuvwwwxxxxyyxxwwwvutssstssuuuvwxyyyyxxxwxyyzyxyzz{{{zzyyxyz{|}}}}~dkFFf;7O;6M6/A0%3,,+*- '. &/%1 &.%'")$/!*<,WRGeOB[MAZJ'&((+(("%#%&#$ *"*XXz~~~}||||{{|{zzzzyyxxxxwwopqssuttuuvuttuuuuuutvwwvvvvvvvuuuuvvvvvwxwwxxxxyzz{||}}}}}~~~~~~~~~~~~~~~~~~~~~~}}~~|frPU??^'&% *%)#)".'.$+!*"("% %0!)=+7E3AH5DH4DE4CB1@C0?H3CH5GF1AE0@G3DI4HI4GH3EA.<6(41&3($&!)#*#&&$(!*#' *#-"+3)6XY}~~~~~}}}||{{{zzzzyyyyxwwwwwwooqrrtttuuuttsstuuutuuvvvvvvvvvuvvvwwvvvwwwwvvwwwyyz{|}}||||}}}~~~~~~}}}}y_kJJq@W]y|}~~~~~~~}}||{{{zzzzyyyyxxxxwwvvuuuuuutttsssmnnnopppppqrrrrrsrrstsssssttsstttttuttutttttsttuuuuttsrrppooopqqrtuuttuuttttuuttuvvwwwvvvvvvwxxxxxyyw^q@?_/#0-#/.$0,"--",-"--!,-",,!,+!,+!+**+ +* *)))()')'(&'%&%&$&%'&'%'$)&)&)&('((*(('*(+*,!+-!+,!+,!+-!*."+.#..#.0%/:0A[]}~~~}}}|||{{zzzzzyyyyyxwwwwwvuuuuuutttsssopooppppppqrrsssssssssttutuuuuvwvvvwwvvvwwvvuvwwwxxxxwwutsqonooopqrsrrsrrrrssssttuuuuttttttttttuuuutsZp?=Z1%1.#.-#-.#,.",-!,."--"+0$/.#.* **()')')&(%'$'#&!%!%!$ &#&"&!'!'!'!($(%(%'#($($-!+1$/-!*-!*,). ).!)- ).!+/"+5)6WW{{~~~}|}|||{{{zzzzyyzyyxxwwwwwvvvvuuttttttooooonooppqqqqqrrrrrqrstuuuuvvvvvuuuvvvvwvvuuuuvvvwxxxxxxxvsqpqqrsttsrqrssstuuvvvvvvutttsssssssttttsmSe91I/$/-"+* &."*-!*+ ).!+.")6)41&0)&, ))'-!**''"*%*")!+ &,#,+",-!+,!)+',!)/%/-!*-"*-"*-!)-!), (-"*3'26*41%-, ',%,&,',(. *- ).&LIfrw~~~}|{||{{|{{zzzzyyyyxxxxxwwwwwvvuuuuttttoooonnnnnnoonnopppppqqqrrrrrrssrrrrrrrrssrrrrrqqrstttuvvwxwvvvvvwwwwvuuuuvwxxxyyyzzyyyxxxwwwwwwwwwwuf}HMx0#0/$04'3/$.4)56+83(51%0/#-7*70$.'#1&11(56+90%/- '6(15%,2#(8,9@7KB:PXVy~~~~~}|||||{{zz{zzzzyyxxxxxwwwwwwwwvvvuuvuutttooonoonnnnnnnnnooooppppqqppppqqppppppppppppqqrqqrstttttuvvvvuuuvwwwwwwwvvwwxwxxyyzzyyyyyyyyyyyyyyyzuV\<3L5&57*:5'35(36*97,;6*84'37*70$/-!*.!*1&3@6K9.>8*4:*36&/A-8:)1>/9G;NGBZD:PD:P<2D7-;@5H?5H<0B<1@=3C<2B;.>:.?5*80%.3%/3$.1"+2$-5&05&15'25'13%/3$-E=Unp~~}}~~}}}}}||||{{{{{zzzyzyyyyxxxxwwwwvvvvvvvuuuuuvvuuttooonnnoonnnnnnnooooooooppoooooopoooonoopqqppppqqqrsssrssttttttstuuuuvvvvvvvvwwwwwxyyyyyyyyyyyyzzyzxiuG@_9)6:,<<,=:+8;,9;.=9-;8+7;-;>0@4'32%/5(25)6C7K;/>:,7<-87)4@/==-:>/:C6GE=RC6KE7LA3D<.;>0?@3D@3E?3C=2A<0@9+9<.>7*73&/;,8=-:8(36(28*58)48)49)49)58(3=0AVQsx{~|xowlz~~~}}}}}||||||||||{{{zzzzzzyyyxxxxyxxxxwwwwwvvvvuuuuuuuuvvvvvvmmnnmnonnmmnmmnnnnnmmnnnnnmmnnoonnnnnopqstsrqpoopopppppqqqqqrrrrrrrssssssstuuuuuvvvwxxwwwxxxxxxxxyp|SQw>3F;-;;-;;,9>/=<-:>0>;-:9+60@=,;?..;<,:<.;>2@<0=9,7;.<>5G?4E=1@:-::*6=-::+69,7:-9:,8;,8<,8<,8<,9=-;B7Jkj~~xufoVipWiyl~~~~}}}||||||||||||{||{{{{zzyyyyyyxxxxxxxxxxxwwwwwwwvwvuuuuvvuvvvvu}s}qllmnnnnmmllmmlmmlllllmmmlmmllmmmmmnnnopprvzyvtpnonmnnnnnnnnnnoooonoooopppqrrsssttstuutuvvvwwwwwwwscjC8P:,;8+78*58*5:,86)48*7:,99+85(3)%% ($,",1(78-=6(56'28)47*55(35(35'23&04&27(57(37)37)56(46(3:,:9,86'19-;JE]G@WG?TG@V<6L4)59+6:,87*57)57*69+68*47*4:+61$//"+2$.3&00#-/#,/!,9*9?1@7*7"#2(6;/B=/?=1E:,;8(36(36(56'44%02$.2%03'45*76*66+87,:9-:<.<<.<8*66'19+7C5EMAWVNjgcooG>T@/;9*7)%&"($- (+$&'!-!+D:Ojg}prZmoR\sWfye~}}~~~}}}|{|{{{{{|{{{{{{zzzzzzzzzzzyyxxxxxxyyxyxwwxwwwwvwvvvvvuuvvvvvvvv~rraeNbcK\jiiiiiiiijjkjjiiijkjiijjjjjjjjjjjkkkkkkkkouz~{y~}zsllllmmllllmmllmlllllkkkkkkllllllmmnnnnnonpoopppn\m?9U.".(%%!(%* +( ,&#'"6)5<.=/#/,!*2&1=1CC7NC6K=1D7)71"++&($+&. '4'0>6HJEaPLjQNlOLiKGcG@YA9M=3C9.;6(45'1:+6A4DG;PULj`Z}=3D;,77(4'!! "#! '!1%0WPpvp{|krYioR[sVbv^s{n~t}t}r}q{ozoznzmylxlzn{oznymxk{s{~}|||{|{|{{{{{{z{z~u{q{q|r~tyxvwxyyyyxxxxxxxyyzzxwxxwwwwwxwvvvu~r~suwwwwvus~or_}dKZcIUfffffgggggghhhhggghhghhhihhiiiiiiijjklljjlsz{}u~tr}txqlkkkkkllkkllllllllllkllkkllllkjklmmllmmlnnnnopd|LV1&5)&++(&+"0.'9*#2" , (6)58+80%02$/:+9TARc\ruuxvxtvrtopllgfa`ZWzMGc:0@0#+5&0@1AD7H8+68*49*50$,!#&(!( *"0!&E7K`Snuet]ppT_nPXoQZpS_qXjq[rq[sq[qqZpoYknXimWhmUfmTfjSemWko[pp\qoYnkUgtev~}|||}~~~~}}}|{{{xugm]xn^yo_zqa~tfpbk]|oazowzzyywwwxyxt{pxoyovw~t~tuvuvv}s|ruv~qykxfyf{j}n}m}orqyiwdxet_xmTegN]gggfgghhhhghiiihhhiiijjjkkjkkkkkjkklmoommnt~zr{o}p}o}o|swnlllmmnnmmnnnnoooooooppooonnom}kmlsnppmxmtowp~pqpzqxr|s|q{Y]<7R-".-!,3'51%14*=6-D-%5((6)5:,97*56)44&07(57*93(90'97/C?1>H4;R:A[@I]AMbDPjPdxk{{poa^PKi@7K6(45'1:,69*4;-89+6'"$&*$- '/!(-%6%,:(1I5?bIRlOVnPVmOUmNTlMSkLQgHMgHNiKPjKQiKPhJNfHLfGKfGKbCHcGNhNXlR^kP[dHNmXk|n{}}{ywu~t}s}r}r}r|pty{{vn[scKVdMYeMYfMZeLX^EOY?G^GUm\yzktvwxvuuuv}pvgn^{hXriYtujwkrcscxjzlxjwivhscravgykvdp[umUhnTdpWkr\us]us^vxeyfq\voVjqYopYmiRdbK]gffffgiiijkkllkkklmmml~l~m~m}n}n}o~ppoo~n}n|n{oyp}q}q}q~r~t|vvxoym{n{m|n|nxprpqqqqqqrrsstsstuututtssts|pronpnphn[sobswvu}ocoZrqZpugxyvqufvfxjxhkYrM>R4)80$13&36)85(66)<3(=+!02&3;.;<.;7)44&05'0. (4)6;2D>8NUOmcPfgLWfHPfHPeEOeFOjLXt\qr{u^Wv?4D8*57(37(2:+7;,95&2& #("- '/!(- &3$+:)2C0:Q;EgKTlNVmNVmNVmNVlNUiKQiKQkMSlMTjKRiKQhJPgINgHMcDJbFLhMWnSamS_fKQiOYs^w}nyzv|mtcn[rkVhhRdfPcgPbgN^dL\q]u}nuspnYnfLXgNZgMXgLWeJSbHNaGLaFM_GNjR`yaxj~nrq}nzkxi|mtajShcKY_FR^FTdQgcOd^ET^ESePgjVmgQf`I[`GXdJYbGUbIYdM`fNbcK]^GV]FRaGTeKYiPakQ`nUgpYopXmoUfnUhcM_VDTTCWXiSeScUeUfUf\l\k\jfrkwozqzpyoxpxpyqzoxjtmvisfqfqgqdngpnurzszszqtpopmohoarqswsutwuwvvwtwrwqyozn{nwiqduwu{v{vztwuxvzw{uxtuvyw{w{vxuutttruqvswvwxy{vusmtmumscp\so\ss_tq[nsb~yt|~yss`|s[pt[mxg}v{kxcxxd}zgwbxYGX>/;2%13'25&10#-0"0- 0%)""2&17)59+66)2-!)- %+#3&*OE]ng}vr_}hKWfHPgGOeELeFMhIRnR^v^qygyhyiut}r}t~ussq{lk[vJ9C8)03&.4(17)36'2# &+#+%+ $.!&G4Hp=CiFLuTYqsxvskrhulrqejSYHMyKQ|GLvDJrEKsEJrCHnFKrRW^bfjoprlrfreset`zypuopergpetnwqskmapcseqcl\uhWqjckenipimcnfsmpkibh]mgpmoij`cW~bVyeWwhYwj\|l_nerioblZsn\tn\tp[plVglUes\lr\mq]tteuhr`oZnnVhjRdiUnn`n[tmXkmXkmYmgSeH8F6(32%.5'17(2- *,/).%&(&5)4:,84'1)$#$4&+P=EgSesbxgoXniMZhKVgJSeGPgIRhKUlP[pUcpWhnUfqYkwayweiTkePdjVjq]ruavxdzwbxt^tpYkfO[WAJ:*10#,8+68*5/#+("( ,#/!&,$( 7'.@/7>.7Q=IdKXgLWgKUnR^pUanR]mQ[pU`rXdlQ\nR_oT_lPZgKSfJQgKSkP[mR_jOZfNXfNWfNWfOZhP_fOacJY`GR^DO\COaHU^GUXAPU>JV>GX@JW@J]GS]IUU>HZBMZCN[DP^HW`LZXDN_HQ_GN\BH]CK\CL`EOhLXmR`kR_bM\YDSV@MU>JT=IYETWBRXAO_FRgLYiN\U=HF09E/8E19I6AK9DJ8CM9CU>HT=HJ5@D2O?LSBOP=JN8EN9FL9FJ8GH8IH8J$)0!&, %, %- %-!'1&/=&.;$(2(*579PYXd[bRmbPg`SmZVxIKk26J'(2)*4&'0%&0')4)*6&&/''134F?A\JKk]VzgZxgVofUmjYsfRifZ}aTt]MbbRmZJc]RubXzaSo\K`^Lc^Lb\K^YFWVCTYI^[JaZKeXJcWFYXH_\OlYMhSDYQ@RTHcZPn\NiYF[T@SS?PT@QWCT\GY`K^^L_eSkeQfcL\dN^]IY]K[[I[[IZbN]dP`fSehUkiTkhRehRdgQccM`aL`cPeeQgfQdhRbcO_TBS<-91$-3%.7(27(2- *,.-.2$1;-::,84'2(%2&-RAMdO]lVfpZlqZllTdhP_fO[dLVdHReITgLXhN[hO]hO\iN]kQ_jQ]cIRX>DS9HJ7C9+82(40)5(%3$!-% +%(#&#&& *7.9;2>9.99)39(1?-7B0<=-97(3$)2!$, !( !)!"+!%/"(2$)4$(4#&0$&///9?4@H7DE7E83B..;()3%%+$!(#!'#!(##+%'1()5&%.%"*%#*%$,,(0E6ATANUAPT@RYEWQNXBQO8DXCNYDQS@NQ?NF6D@2?>3@?5BC6CL=LYGX^K]^K]_L^`M__L`_L__L]_K]`L^aK[cM\[HXC3C3&0+'2%.8)44&00",. */!,9*7C3B8*5(#-!)KHQ
F]ENdKW_IU\L\]L[aL[aUkX_~TTnFGU@LYGTZKXVGTQAMTEUP?PM=NQAQUDSVDQZIZYIYTAPP:FK5@SBOWFVTCRR?LU@MVCSI;I0*3!!'!!',*3,*3,)290;E5AM9CP:CPJO?NH8HC3AD2>G3?C1:B.8G2;L7@O:DS?LP=IK8CF2;C.5D3>G7CH8DG8CC6A7/;+&4#"2!$3$3 "0"!,# )"% # $"'! '% (.$+2%,D3>G6CA0<;+6!%/ #* ' &&!)!$,"&."(0#(2#%-*)381<<1=/(2)'/&&.$$+$"'# $# %#"'##)#$+%$-('0&%-%$*%$*+(0@4@F6?J5@L7EM:GO:FP:FJ6@A/8=,5@/9C0;K7CONOFU;BX?GZBJZBJZBK[BLW>GRJTALUANJ9D:-41'++"&(#*"%.%)2'-<09I=HI?K=7A0,55.;2+882>B;HG>LI?MMCTOEUNCRL@OI;JL?OMBSMARO@PSBRWFUI=I1,4#"'%$$"#)+*23-5B3:J8@LM>2A7-:?2>H6CG8DH8EH6BG4?F5AJ:IN>NN?NJ9ED/5C19F5=F6@>3<-+4%(3"%5 $5"2!/!.+($ ! ! "#%*#*2(1A5DD4CB0H=OSBPT?LO;HF6A>0:E6AP>KS@NP>KM;HP=MO>NN>LM6D4-9&"+ $ " "'!*9-9N>MXDTT>JT>IT@MTBQTANRIM:E9-7+ )9,83'2! &+$, '+'3%-I8DA0<=/;@5A<3?61>52>40<4/:815A4-7*%-'$+$!'%!()$*+&++%*.'-4,34-4.)0'"($ '"$# ''$-*'1.+430<54@55B36B21>31>1/;50M;2@.'4'!+.%/;.:>2@F9HJ;IK9GI9GHE5?>2=5.7,)0$%.!%2$3!0,++(&$ "% &.&0=0@C4ED5DD8F")6%,:%.=%/=$,: $. ' #+"&/!$.#%.((1/*50(2& &%%#""!"""!#$" %!$"$#%#!($")"!'#!',&190>D6EH8HH8GF6DH7EN;KH9G80:+(/,*2,*2+(0*'.($-%!)$&#')%04/<>4?D5?B1;:+46)1:-7D5BI9GI8GG7FH7HK:JN=KM=KE9F95B43A33@32?21=0/<+*7%#. $  %&7,8F8FL:IM8DP?LREWPEZH?TD:LE:M?3B6(25%02#,2%.2%.2$,0#*+%0$-7+64(3&"$#3*0@6?C7BP?OVBRS?KM:CU?J^FT\FTS@QO>QP@RQARQAQSBRWEUVDTRAPM9GI3@H3?G1I4?L6DTARUDUSCSQBQQCSTFWVGYTDWK=O90<$#/$-5*40%,%&*#/")/#*-'8)33'/4*35,7,'/(%.'%.(&/*'0-)30,8.+7'$-!#!#!$"(%!,&#.'$-)$,*$*)$*&"(#% !"! '$#,%%.#%.!$,!#*"#*!"(%#)0)0=/6A4<5,3! $ '#(-'.3,581=0)4(",#&$%*"+-%/4,8=2>B5AB6CA6EH>PNCWQDXSDUPDTB9G/*4! ' !+$2#2.*)(%$$# !"$',#/3(79.=>4C")5#+8$.<%/=$,9!'1$-!&1"'3!%/"&/%(2+)3+%.%##""!" !!  !!"# %##*$$,""(" &)$-1*6>3A>2?8,89,9B4BF7F?4B3-7*&,(%+&$(%#'%"&%"&&!'$$"##&'#+-(0;/8@/9=-79-85-87.;9/<<0>?1@@1BC3BH7EH9FA6A83=/.7,+4,+4*)5&'3#".!(" !   "%/)3;2??4AC;HD?PA>R99L85G81B5+82$,/!*-(0#,0$,, '& &!4)34)3* &",&,9299177,2?0;B1;>,36(-<-5D5BC7H@7KC;OG=OH=LGMSEUWFXTCSN;JK6DL9EL9EL8DJ7BJ6BM:HVDWVFYOBSH>LH>NKASMDUI@Q?6D3*2'$$!1&/4(2$")"-"(-!')$*%*!),%--%/&"+"("'$ )'"++%-+&/)%.#&! %!)",$!,&",&"+$ '!# ! "##  "! #&#'4-32+1($( !!!!#%)#+'!*$&!" ! ! "$',&/2-860>82AA9JI?QNCUPEWMDU@:I.,7 )!,#1 .*('&%$$$  "#$(+#/1(5$,#+%- %.#+#+$. %1 %0#, #,!%.#&/$#+##!!!!    !! $##)$%,"#*"!(&#+*&/6/92*4((+"+6+75.90+5+(/(%+&#'# #!!"!$ #'"&& $$"#"%!&)$*:0:B5B?4C93A30>/,9-)60*56,78-;7-::/:=0::/85-6/)/*&,(%-&$/$".# +"&!!!#!# "'%-11<34@23A.0=,,72.;2)61%.-"')#+&*&*%)"&($3(2.$-"! 1,4:4=5.42(-4,54,50'-.%)/'-2.:52B63D84D92?80:7.5>3;O?L^IW\FTS>JO;HVCPXETYERUAMP85F>9J@:J=9G:7D32?))4 !+!-!/+(&%%$#"  !"!%%( %!!!!#%$!#%!( ")# &!!"!  ""!(""+"!+$"+#!('$)'!'$#&'-%3+(5*'0($*$!%""!  # $!&!#% #$#%!''%-2/<63D22C+/>*,;)(5)&1)&/+%,,&,+&-,'/.(1-'0-&.,&,)#*$ (") ' $ " # #!!!,-6//;--:*)4'&/,(2-&0.%,+ %%,''!$)!/!)4'02&/)%&!'/+20,3-&++%)/+21/70080.60-50/:1/;1,6/(./(-1)/3*/?2;VCNiQ[eMVXBKR?HYFR^JWaNW[HQS?HP=GRALTBRI13>.09.-6%!&%!0$,6(2-"(&!% ($) &%# ""! !$%%"!  "  ! ! !!$ *$%0&'3),;*/?(-=%)7%'1#$. )'* -*(&%%#!!!$#& !$# % "'  !!! !   !!' (&!' &#!(# &#&%"/)*>'*<$$0#$! ! !# % #&"''%/&*8&*9%(7$'5#'4%'5%$0&#-'$-("*(!))#+)&.)&0*%.*%.+%.($,#!) '$!$"# !!"! !((1()4%%1"!,! )!'"&$#'")#, &(!%-':+6?0>0$-!&!&,'/+'/*%,+'--+3-.6-.7,-7,+6+(2*%,*#',#'.%*2'.9-5H:GWGWUDOM=DJ=CK@GK?INALOCKNBIKAGH>CF:?VGUPCR<3>1-7,*3+*2**3)+4)+2&%)$ "!*'8+52&/)$$#!!  "!& )!*#,!'4&4$1$2 $.!)&(**(&&(('%! "##%"$! $! !!! %##*"!) "#!)!) & '"!."$1!"/!"-#$3$(9#%5""."& "!##$"*&(4"&2#%1#$/ "- "-$%1%#-%!*&"*'"+'"*'%.((2((4(&1(&0'%0$#-! ( &!%!"  " """*"#- )%""'"&!& &.!(:,7:,8*&($++)2*(3*(2*'1*(1**2))1'&.'#+& &&#' ","&5)1?1>H;JL?NG:EA8=?:?@=BB?EB>GB?GB?FB?EB@EA>C<48PCRQCU@6D/*2&"&"""#$#)%%,$#'%!$ !+!''## !'%  "#"!# +!,* +!*$!#&)(&' +!+!*&  !"!" ! !   !("!.$'5%)7"$2!!/! -!," -##1#%5!%5 $1 #0!%2 %2 -* '  !%"$,"%0 ( ( )( (##,%#+%")&"*'$-'%.&&0%'2%&1$$/$$/#$. !+' $ #! !#&# %"%"$$- '7)21$-$ '#)&$+#!)#!("&!$"%# %"$""# '"*"$0%(>1:K=NNARH=I>7<;8<=C@>E@=E@=D@>DA>EA");"#6""3"!1"#3$'8"'7 %3#0#/ $1 #/+('" &!$+!'"  !"! &#!'%"'&#)&%-%&0$%.##,"!) ( !* )'%# ""$!&"!%/"*4&/)$ !%# ! #"'$1'-:/5>18B5>G;IE:G@8>@;??@>:><7:0(+>5@8;<59:255(%%415D@2C=1?4,2*$%+*)0335995;92973998<>>AF?BH9>C7=@7>?7=>7;=257&$#..0:4DKBLHAK<;A38:489689668214'##.+-6.48+44'20&.0&.9/;A6E?4C0'3&& !.&-A6@SDO_LXaMYXFRG9E7,7,$,!!' %  &'!,!$0#,  -$3".# "!("*!($     ",!.,+)%! ! !$"#&""% !# "!#!#&"#& "%!#'"#)!#(!% !%!"("$*#%+"%+!%,"%,"%+!%+#'-#&,!"'" "#$*#'-"', %*$(!%   & &7!*A)?'<%:%8$5#4$6$6 ,-%,6,75)4,!),+7!%"."(2'.-"(+$1$,9*55'/4&-7).9-4<0;A1?>0::-48,3;07?6=?9?@9@@8@>7?C;BOALTBQQ@OG=IA;CA:B@7>?5<@7@<2:;18/;A3??2>8.:-$.$"!!  1)2=2=OBP[KY\HU^IW^KZTDSD7E7,8"#&&-")!%,"/ )#%:*A':(!" !#"  $ (&  *-)'%! " # "!! # !" !  "!%"!"" #!$ $"& #'#& #&!"&""(%'/$&-!#)!& $ !) '9 *A(@$8!1-, . 1!1'+%,8/99-7-#..2C&$/ &1%*5(0/"*5%/>-7?.8;+4;+2<26=6:>1:C3?F6BJ9EMKRBPSDRTDSUFQWHOUDQUCRVCSUDSTDSUDTVEUP?MM=JSCTSDRL=JD5A>0:;-6B258P5.?5&29+8>0;=/:;,7@/;B0<:*16&+8*-?FDEOME9@H7CL;HO>KP?MO@NOAOO@OM?LZOFibAO@DK:JN>MN?MN?MO@NPAOK:FF5AK;JO?MJ:HG7EI8EI9EG6BF5AF6CA2=1#*+!+")%'**"15+99-;3'41%-8+22'/-$-' (%')!+(*"#"#$ &"!"'&,"&"$!"!"  $%,!/(  $ '$ '!$ # !"!  "))%! "$#  !&&/)2(2'/ $+#!'"  !  ',,/+!$*!+)$ * %@3;G7@K9CM;EN=EO=FQ>IJD6BJ:GH8DK@=SK7>14:,7=/;@/:O=L^J\]HYP=K<-6@22%,* (&(&)(!,)".&)$&"&!'$(!(-&,%,#*"   &# !!'#% "#"$%!# "  '"(!# !% &*!(&"      "! $&'("*!%( $" !""%# !&!'")#+&.&/&.&-!%-!"*!% !!!) %+#'( $*"&-$,-$-.$-2(23(2/$*0%+.&-$ %$% "$ !$8*,=/2/(.@6@E8BI9CM;FP>HQ?KR@MI,8:3FG>QQ@NI5=C2:@09B2FRAYgRecOb`N^P@M>.8A08A/8B08E1:F1;D1:E3MaK]aK\WDRG5?E3HNKF4>I7@K8CJ7AL9BN:EM9EN:FLJK7@I4*14'2%$5 ': '=$8*(<3+=/#.$#"#%$&#%!!# &!#)"($+)1$+#% ()* ,"#-&+)%,$(5#->%.>%)4')4&'/"%+!'/#%+$$*&$)'#''!$( ""% /'"6( &$")*%.*&/ !)""##"! ##,"-!*$"& + "/ -($         @.4;+30#+# ""&$-#-)&<,0E15D.3A-1B05>*0@09=8D/6E+2A%.  &$/ (7.=P6I^5AS>-3J*)D'(4$'8*-K38J5-1("&#' $) %+ &,!(+ '-!*/",/",-!+.",,!)1%+7(.8'/:*742H89SH>UU@PUBOUBONJS@MVCPP=IOHRR:@L5;H5>?2DI=WRBYR?ON.7G6AUBNVBGU@@L9<>/86(07*4G9ED4>B1:O=J?-52!'-'* (%#'#)%-!(.!)*&+ ',"('##&& #% & %!'")! *#'.$(0%*1)21,:00?,1B'5I$8P#7N"3E'1@&-8 (.&,$,3&-6*-6/,42*02&+$* 1#!%#$"  %)!' %"  !"&%      R:DM6C@0K6?L9FK9IK8HP
IC2;B1=P>N\HYXETP>JI9D?1;,%+ '.#*/"(0"(2#**!) '#%) +#/!)2$-1%/5(23&/, '(",%, %*$+"#)$$!&   #$6,55+51)5,.?(5L%5M$6M"7P7P3H-;!-8 )1!'!&(0+3+3#+4,,63*3%!*)7'0"&# $&#"  %  ' !"" %!       <+28'15-800;%&0 "!# $%+(2;1@/'5"# #,%/)2*5(3$-"    ##/+:!/>%,9"#-!!$%)+#()",!(9.9?3?@3@D6DG9GE8FD9FE:HENUAXL7JH3?H3=K4>M6?Q:DO8@L6M7?N9FI6CG4@O:FR=IB/9E4AVCT_K]\I\SAQN4@?3?E6CK;IKKJAMGDOFGQKFTJ?NN>MRAPP@PJ;KH9HG8GF8GE9G;0:3'02(16.881;8/:?2=@0;9*42&.9-5A6>C8A@3=:-4/$*&"(%,"(*%,&)#/"*3&.* %& -")2&.0$+- &-!'4'09,6B3>M;HQ>KZFUYFUVBPXDRT?MS>KU?QU?VN:PJ4AK5>M7@O8BQ;DT>IaL\gSdaM[R-6J:G^K]_L_^K]TBRM;JC4A1%-.$-)&$%( %()!( ''$&)!( )!-!(/#*2%.7(47)47-7*'.     +',6+48.782?24G#2G+;'3&0"," "!%#(!&.-)2#%,&-'.!% # "%! )(+& "!    "&PJ[OGW70; # )!+*5#+!($+"("($5*?*?&7-B+=#0#1'8&9&8'9*<.A%3 )"*#(        $ % %!'!'%(//+43+3;-6A1;C3>B3=C5>B6??6>=6>D9EB5AL:ES@KN;HE5AF6BI8FH9GC6C8,54(07+4;/:=1E5AE5@?/97)2=/8D6?D6??1:;-46*11&-/$,/$+."'0#),&5'1:,61%-0$,5*38,57)26(09,5?1=D4@MK:+3M=JcPb_L_\IZQ?OJ:ID5C9,58+54(0/$*-!&-%(,#1#(1$).!'+#&'( &%&) - &2$-4'02(0&#)   !&!-%--*7!*8$/ &&'!!#!$"!#"!" "$ *!5%*9)09(0-%'( '* "+!$*!)*!+!$-#'+ $,!$0'*%#)  3/9c\naWgE*@*@+@,@.A .A*;%5"2"1$2 *;(7$0!(!  "$%+#") ##!%"(*'/2)04(/5'/6(06(/4(.3'.4(/8+46(1A09I6@D2;<,4B2=I8FJ9GC3@7)27)2:-7>0;B3?E5BK:GM;HF5@;+5?0:C4=?088)07)/<-6>09;-64'/1$+4'/0#+:+5A1<:+6=0;=1<<0;<.9?0:F6CJ:GNJW?KW@K_FSY08@1;>09;-57*04%,- %1")7(/9*16'.2%+- &+$+$)"("' )!,#-%- '*!'!#        *!'/&/,(3!%/"*#    &&).'/- .*(($<,2O;CYBMYBOH5A?/8A09B08E5:H8>F5;D27E4;C38G6;A04=,/B171)0!   $>9G^Ve_TaI?H'!'"%"## B;MNASC:M'+>$6%6&5'6)8)8&5"/ ."1#0#.#,")$   ")&*6.->3,=/&3!'#$('/7/8=1::,7;,79+49+48*24&,8)16(/9*2>.6?/7?/9D3?J8EJ9FC2>9*46(1:+5>/:B2=E4AK8EL:FF6@;,5=.7:,32$),$1#*6(2;,58*12%,3%.7)42&2:-9B3?@2>F9FH;HC6C=/;B2=Q?NRAPR@OVCSZGYWDUVBQWAPVAOT@MR>ON;QJ6IJ5@P9DU>HU>IWAMVALS=HbJXT58N)U64W?HS>MJ6FG3?L8BT>JT@M;+3K;IbOb`M_ZGXQ@OK;ID4A7*37)3:+59*49*4:*54'-8)2@/9C2=A0:>.67)/5'.5'/3%-/#+,!)-"*0$,1$-/#--#+% $     !.&-/*6&)8!&2!*% $)&+3-6-(34)12&.:,4PIS?JT@IWBMU@IS?GS@JF4-6=-7C3?F6AC2==,59*29*2<,5?.8@/9A09B1;C2;?/77)/;,38)06'-;+3G8BUDQZGUYFTWDRUAPP>KA3?>/9,6>0:J:FYFXYEWT@PVCT^K\XETU@OWAOWAOS>LT?KO:FM7AP9BT>IZCO\DQWAMQ;ERHP=NF4CD1.8N=LbOb`L_]J\XFVSAQQ@ORAOUCQWDSVDSR@NJ8E8)2=-6C3D3>E4=A09?/8?/9<-68)36'15(26(25'13%/0$+)!%     !/%,-(2!(5(5#,% #&'%1%!)!!!$%#)-(.""!#(&) $'$'/+0A4>Q=H[BL]EQ]ER\ER\ES[DRWBNYCPS=IQ(%*(&-0*3'"*$!->6FUGYE4?S>JV@LC0: %%*%*$(!$*%%1$%2-&'(#1!'6#0($##! !     "%! !%!(#&-719E7?K8@I6>F4=M:EI7AE3B19@.6?.5A/7B08B19C3:D29B18C29?/68*0A18B29C1:I6@TAM`M[cO^dO^bM\^IWZERN;FG5AE5BD6DD6B9-6:,5C3>M;I\I[[GZWDTXEU\IYWDSWCQXCPUAPO;LJ6=J57J4>M6EU>H]EPeLW^FPO8BK5>N9B<$'4@*0J5?J9MD2AE1:M9CS@LS@NE3@R?ObM``L^^K\ZGXUCTWEU`L]cN^eO`cN_]IXP=I8(0=-4E3;G4=G4=I6?I6?I6?I6?H6?H4>G4=F4=D32:?4=>3=:/9.*4.*2'$("#%:/7H4?I3;SJ6@C/8B/8R>IXDQ^K\ZFVPIYDQ^JW_KX_KX]HUXDPYDNT@JM:EG6CE6DC5BD6AH8EL:HP>1E6,FWF9iSBgaL]_K]]J\YFXTASVCTbM]dM^eP`dO^]HVP=GE3;>-3?,3B/6C06D18G3;H4;I4L7?L8@L8@J7?I7=N:BM9CH6A=/8*!$  %7)14'-)#)!%<07R?ITALB7C+,7.,6:2M;DD7@F9CC8A>4<@4I8AM2*0 /$)>,3@-2L6=M8?E16=*.<*/2#&4$(0 $2"&D07L7@QHQE4BQ@NVETUDSSAOVAP]J\ZGWVCRWCQYES]HWZESP@YUGieTrR@a6*]<1gH9eE1O[?QW9L]EY`KcJ:Y3%JZF\ZIcB:];4TD4GG3=M8BPaC;QD~`L\^K]^K][GZTASTAR]JY_KZaL[aLZYETL;KXDQ@.6, 0"%2#'4$)7',9).;)/=*0>+2?,3>-3>-3>.3C29@.9?-<@1<;/3"#8*1PIM;DB2:>.6C3+;>,30!%/$,!-#<*1I3=P9CO7?V=D_GN;06"$#%8/9VGVuaunw]nR8BK7@|cqivdR\7-4&',)-2329++1!& $!$0'*8+.:..8.-850=A8CJ?HG?J40940:?9DHAKF@G-*/                !8-7A1;@.7@.7E2;C2;A09B08H5=T?IE3;D18I5;@.6A07D2:G5>G6>D3;C2;B1:>.59+/8+-?07>.5>/5B2:G6AO=JOIJ7CQ>SK:ZUC^`M_^K]^K]ZGYTBSP>MP>LR>KR>JT?MM:T?/[R>T:+4$&&' ,!)4'26)43$,0!'-$) '* - %,%, '/").#'%*"7(/7(/3&-3&,5&,7'-7'.0&.-&/5(.5'-5'-2%*+ $&, %0"(0"(0"'- %( *!(!&:+/aIPdJP]ELT>FJ5;L8[CJ@1=1,<61ATGXs_tzcvkTfR=JA,6S>Hzao~esqXdXAK3&..)0:6=438 %(   #!#(*-<45E10<53>2+682;51:5080+1)%),%)%$%!    ,%     ,'2?4@D6@:,4=/:B5?<.75'-;,2>/54&+4%*6',1#)7)/=/6B4=D6?B5>B3<9,3/#)-"'7,3E8DC6@G:DJ=GE6@E4@B1;D4>G8CG7@D4=3%-:,4O>K[IZ]K]\K]ZGXWETWDUXETVBQZFV^K[]IX[GUZESWBOR?LO=LM=N>/?D4FK=T@6S=5X<0KD4EJ9GG9FF9FG7DH6BI7@K8@K7@J6@N;FK9DA1;RAPVCRYFVYGXTBRYGXYFWTBRM;JG5BD2>B1;F4?G5GD3LP=M<-7)!&&+)0%04)57+66)34'0/$,)&%$* (()&)()-#).$)(##"%."(.")."*/#*-"()"(#.$-4)45(1/#*+ %' !!"!!#$$$#/"%N:@O:?J6;M9?XAHeHLwU[lOSK8:6),4*/$"#$%"2' >5-82)0& #1"'A/6G4:I5:E16C15F3:6)11-;63BD;KREVSDRC6D6*86*6A4?VCMTBMO=GE3;/$,*%*)*-#'*!!"% !%"!,;3@PAPWFURIYHH^IKcDEY-+771:/*3(#*##) "/%('$%   ("$#$# !,%0:/9;.6.!)/%.8,65)1/"(1$*."'+$*$*%+&1$,9+4;.7:.6:-69,32&-)%($2'/@3>>1D6@@2;=-76'.6)0<.7?/7?/89*3E5?UCQ\JZ]J[\I[[HZYFVVDSWDS[HW`L]aN_aM^`M^]IY\IX[HWYEU\HWQ9EN8JR?SP>KQATP>MS@LUBNTALXDPUAMUALUALR=HR=JPKL:GA1:Q?MZGW[GXTBRNLJ8E=,65&/1#*4&.;,4@08F5?@/88+58,;<.A3$6/!60$42&/2&-1%-/$+,"('"$+ ())&)(),!(-"'(#5+38.6#*$)$&"$"!#* (/$-/$++ '*$' #"#&'!($) %)!#( *!*!*!4&+5'-G:AXGMaEJlLO}Y\uSVU/4<-18).4&+2$*)%-'1-)4*&1'$.%!*!*$,(".)$/*&!$"#&##%#"#!%)30>KPg]`zaWmWNbOPlLSqCH_'%1�%'&!'!'",","'!!!%#  "$!&'#&%&$*!%("3(/3(/-"(, &% %$"$)$2&/4'/- %$'.!'0"(1#)4&-2%,2%,3&.4&05'19+4=/8;,56(17(16)03&-0$+.")/#)8*28*37)18)0;+39*12$*.!&2$*8)1;+3C3=N=IVDSZGW[HY\I[]I[[GXYFW^L]dQcdRddQdfRfeSfbOabPabPb_M_cQbZBPXAS[G[XDT[GXYFV]IY_K[\HW_K[\HW]IW^IXZET\GW[FVZFVVCQM:FQ>LYEVZFWTBQM;IK:GH8DH8CD3>7'/1"),$- %1$*5'06'.>,5?1??3H?0I/8&4-35(16)/6)07*16)03'+2&%5(.2%-."*-!)-!(+%(":/8?4=& '#)$'!##)%, (-"*.!).!'.!(."(."(-!'-!(."(0$*2&.3'03&00$,4&/8)29*37)38)48*4K>GXGNW<>^AEdDF^?AL375&+3$*7'.;,4?1;B4>@1.7;,5<.8/'-(%) !")",% '%$,!)0$--#-:2?92A.)9%#1$         ' # (!')"' /#*?-"*+ '($(!' '( +"- %.!'/"'/"'0#)0$*1$+1$*1$+0#-*&)#($!" %#&1 #4!(7"%4!)3"/5&..!&4(.6(02$,1$*/#(1$)1$*1#*2$,6(/8*38*35(/2$+7'/=-5;+3;+3B1=A0;B2?/8;,48)08)/=-5?/8J9ESANWETZGW\I[`M`aMaaN`cQceSfgUhgUhgSgeRedQedQdcPbaO_`M^`M]WDRXDR\GVXCSYDTZFU\GW]GX^IX^JZ_K[_K\`K\aL\bM]aM^`M^_K[YEUTAPWDTZEWWCSP=KK:EI8BF6?C2<@/:?/8?.7?.7>.89+5."(9+3@1?;-@. 5)4*2<,;I9EL.<=.;=-:<+7;+7:+7;+7;,7:,7:+7;,8<.:=.<<-;8*7?.Qk?>P;2@5(23"*4%,3%+0#(- %+#,#- #0"'1"(/"'.!&/"'/"'."'/#'/!&.!(*$,%-!()'/")1$(,"(-!/$4#(5#.5%49)58'1<+3?.8@0;B2>D4?E5AG6DH7FI8GH7EG6EE5DC3AB1>D4@E6BD3?D2?H7FF6EE6EG7EI8FJ:GK;HI9EM>KTESWGVZHZUCUH8GB5@PBQaQebShaSg^PcXIXRAMK9CB19C29N;FR@MYGV]K\^L_`M`aNcbOedRgdSgbPc]K]\IY^J[_J\^J\`L__L]]JZZGVVCQQ>JR@OT@QS>NR=MWAQZEU\GW\GX]HY]IZ_K\_K]_K\_L\_K[_K\`L]_K[ZGVTANWDR[EU[DSXCQUANQ>KP=IOKN=KNIK9DG6AF4@D2>B0;@/8@.8A0;A0;A/;A0=B2?D3@E3BD3AC2AC2BC2CB1BC1CD2DE2EE2EC1DC1DC1DC3ED3E@0?C1AD1AC1AE3DF3FE3FD0AB/=B1>E3?E3>G6@L;EP=IN9DQ=HS@LTANTAOUAPVAPUAQS?OK:H3)3)"*% &% &*$-+"/#&#"* ',"+'&+$./)7.*9($3!,*(%"!!&-(/+")#?/<;-98.//&"2+4G\yLh?F[O;Ho==f9>J7GL=OOARL?PG:IC4AB2,2C28F5
H5NRASUEWWFYWDXVDWWFX\HZ^J\ZJ\YJ\[K^^Ma`OdaPfbQf\K__NbeTieSicQh`NdUCVJ9IQ@R_Nd\K`VFXSCRP@MMP=IS@NVCRWEUYGY[I\[H\_La`Nc`Ob`Oa_M_aNbbOdbOc_M__L_\I[[HXXFUQ?MF4@M;HL9HG5CN:IU@QZEU\GX^HY^HY^IZ^IZ^IY]IX\HW[GV]IX^JY]HWWCOOKP=JPLR>NR?NQ=MPJ7=3&." 8*77*74'0*!4e&#zER|P`r;Bb38i77a;DR@QTEYUFYRCUO?OK:IJ8EM:EK6@K6@O;FS@JT?KS>JR=IO;IL:HM:KJ8IMOP>OQ>PQ>PQ>PP>OP>PQ?PQ>ONQWBUZEW\GX]HY^HY^HX]HX]HW\HW\GVZFUYETZFSZFSYDQT@KM:DR>JU@MS>KQ=IO;HN:FM9EM9EL9DK8DJ7CM;IQ>ONKQ=IQ=IR>JS?JT?KQJM;GMKT@NS?LM9DQ=IS?LS?MT@NUAOVCQXETZFWYEVN6D:3@82?5/=1+7#("$#"<4EJASD4@?+1M6:J4<:(73%56)76*7:.:KUAPWCRVBQTAPSAOS@MS?MS@MR@LR?LQ>LO=KN;JL:HM:FM;FJ8BH6@K9EO>KM;HL:FMJWBPYDSYESZFSVBMU@J[ERZFQYEQYDQ[FS]HU]IV]IW^JW^JV]IU[GSYEQXCPXCOWCOUAMT@LT?KS?KR?JP;EN:DP=GUBNZFSYFSYGTUBMS?JWDPQ@IQ@JVDQ^JXaLXU@JUBOWDRWCRXDSWDRWDRWDSYEUXDTO8G<7F<6E:2@*$.!'') (&%%#+ '0!'/!-%015'9E7EF8FN?OVFWZHZ[J[ZGXZFW[GYZH[YH[LJ[FSZGRYFRZGTgQ]uZe_HQZHU\JZ]IY]HWZFTVCQTANS@MR>KE3@4)9"*&#-#+) *%&4,71,6,(2,(2/*50)4""!!  N?NP@OSCTXGY]L^cRegUjhXmiYohXnfVkZHZ]L^gVkhUjfSgfShhVkjXnjYomZqgUjcPefShmZqn[qn[qo\qp^sq^tq^uq^vq_vr`vsaxtczsd{sd|ue~wfxgwgvfuf~n^uRCT;-83&.8,5>2><0>SEXiYqsc|sb}tc~rb}qb|rb}qa{q`zp^wo\umZsm[sp^vp^wn^vl\sjYpkZql[rm]to^vo]vm[shVmhVljXoiXok[sm\tn^xp`zo`zm\uiXm^M_\L]iYoj[rj\tm]wjYp\J\\IZgWom_{m`{k]vi[shXpfVmeTjcQfbOcQ@OG6BF5B?0;(#B2AUCX\I`\I_]J^]I\]J[]J[\IZ[HYYGVXFTYETYEUYFUYGWYHWYGWXDTR?NXDU\HY[GXZGWZGW[HWYFUYETZFV\HX]IY\IX[IY]JZZFUR?LSANZGU]IX^JY]IW\HW[HW[HW[GT[GURAO@4A.%..%+6+3ZO`rcvr]ghPW`ITS>IK8AS?JR=IRIT?JS>JS>KS>KS?KT?LT@MS?KS?KT?KT>JQ=HR=GS>HS>IQ>JQ?IQ@GI8>G5>S@LS@KSALNKO?LN?KJ;E:,6.&5!*"!         ZNd[Of\Og\Ph\Ph\Qg\Qg]Rh^Si]Re[O`RCRUGVZN`ZK_`OcjXmn\so]tn]tn]sdSg^M_bQdiWmiWmiWljXmlZnlYnmYolYplZpmZpn[qn]sn]to]to]uo^vo^wn^vo_vo^ukXnYHZ>1>,"*4*4B6C;/=J=N`Qfl[sm[sm[sjZqiYpiXnhWmiXniWniVniVnjWnkYpkYpkYpjYohWmgUjgUkfVjfUjeUjdTibShaShaSg`Re_Rc_Rd_Sf`Uh`Ui\RdYO_QGTNCPUJZUK\VL]XL^VI[RFUXHY`PbaSg^Qe[NbYL`YL_ZMaYL`THZRFXHPRDXPDVOBSREVTGXTGXSFWRFVQDSMAOK?KK?KK@LK@LJAKJALI@KE;FL@LPDPODQPEQQDQQEQREQSERUETVFTVFTVFSVFSWFSTCOR@NVDR[IX]JY^IY_IY^IY]IX[HWZGUYFTTDSHH8EM;DC3:K?JTHWVIYTERXHWTFSNCSNCSNAM@5@<5F52B--821<<7D<6D71?1,8+'0# '!!     UNgRMdRMdRMcOK_JFZGCTD@PA=L?:G=7B:08:0:92>:1>J=K_NagUkhVkgVkfVk\L^SBRUET_M`ZI[XGX^L]dRdfSefSggTgeRfaN`]K\eRfhUhdQdaNadReeRfeSgeSgdReaM_VDT@3?0%.4*4?4A=1@G9IWGYcPdeRgeRfcQdbPcaPbbPceSfbQe_Oc^Ob\N`XK]VIYTHXSHWRHVOETMDRKCPIBNIANHANGAOFANEALD?IA=EAGA>G=9E85@74?41;61<93>40:)(.,+252;95@75?42<95@;7C:7B97A97A85?42:107106117005014025/26./333765:65;77<86=97=:8><9@>:B@;B?;B?;BA;CB?P9GJEPNGSSIYPFWE;J4,62(03*181995<938A7:=48926?5IF8@aHOzY]S=>@<@GFVLI_DBT<;I:N;7E30<-+4'&. "(#"!##$%&&'#'(*)*)***, +/42:B3,50%/4)59/C:@E;AG=BH=AH59=+./#'%#&$!%#"%#"%$"%$!%$!%# %#%#$#$"$#$"#!#!$!#!# # #!$"$!$"$!$!# """"$!$!%"&"'#($(%)'+*-+-+,*+)--*47&26!13"23$45%46&47@?LHAQ4,71(13,4/07'47&57)59)69'47)26:8BWJ]gUlkXohTk`McYEZ^J`^JaWCXP?PYFY\H\^J^`K__J_\H\YDYYEY[GZYFYS@RL;LL=MK=K>5<40220.0.(;43OCHRFOMAMC8F70<-(1)!)(!-$+%(%-@;DFL<;G48A/5<,49/28,*1,%-2+4<5AH@OOHWCCN.9?+9<,9=+8<*7;)6:*59*38,4817=.4:&'#"" ""### #'!(!("($*'!-+'21-894?@:CE@GKAFKEJOPU\W[d^bldhsjlyqrvw{x}|okyULQC87B78@56B66B77A77A56@55?54=33;22:11921931821721531210//-+-,),,*,-*+,'++"+)*(*((&&"$"&"&"&!&"'"'#)%-+%23-79.8;-9:-99.9:-9;.;=.<=.<=.=>-<=-;>CESHDU,(2/*20,4(*0!*.&04+29-3;-3<+2;-1<:5CMBS\MbeSliVphVodRk`Mf]Jb\Ja^Jb_Ja^I`]I`]H_\G^[F]ZF\[G\\H]\H^\H^\H]YG[UDXSDVK?M?7=41/20-20-=9;C;AB8@@6<4*/,#*'%(#&%##!'-&((#%$$%(%"-('1//@;ILD]QIcSLfUOjUPlJE^-'3+(32/:32;03:26>:L99D67A37@,4;+6=)5<%4:$48%58$25"/1"./&/2,3939A7=E2LGGW;BM$01$$!!+("/..7:@FNRVabesnpuw{|}ys}x|bW\M=;J<:I:8J:7K<7I<6H:6I;7I:6H95G85G85F84F94F83F83E93D71D61B61@51@52>42<3182/721;7:A=CA>D536!,*-)' $&"&365;B79@,04,36-7:-9;-;<-<=+;=,=?->@.=@-=?,=>,<>2=C08@!,/#.1$/1!././#13%14'04'04%04%.3(.463?D;JSFZcRkiVqjVqhToeRlcPjdPjdOidNhcNgaMe`Ld^Jb]H_\H^^I__I`_I`_I`_J`_Lb_K`[I[SETK?LC:C85710./,*/,*..)-&%% #"*%&'&$-+,6/5@504;/4:.1904=67CICS[RecYp\Rg`Vj`VkWK_K>NRJ^SL`MFYE?QC?OF6CL;JO>MQ@OTBRXDVYFY[G[]I]]J^^I^_J`aKabMbcNddOffOgfOgeOfdNedNecMebMebMecNfcMfdNgbNeaNf`OfVI]MCTEAN:>G,8=&7:"58 5758 69#7:#7:"7:"7:!79564455554656 56"67#67"54"22!01 // --,,!,.#,.)-355@AAN=@KABN25=#*-/4< )&*GBNebsspsobVXJ:5I:5H:4I:3J:2J;2J;3J;4J:3J:3K:4J:4J:4K;4L;4L;3L;3K:1L:1L:1K:1J:1L;3L;4K;4I:5I;9M@DYLXMCN.(,""E>JQJYQK\MHZ@>J47?35<028%,."--"01"23#43$65#77#89$99$89$88$89#88 65323243444343 53 32!32!33!33!23#13&16*1698ANDV^MdhVplYukYuiVrePkdOidOhcNgbNfaLe`Kc^Ia]H_]H_^H__H__I``JaaLbcMcbNc`Ma]K]XHXOAOE;F>5<9256223*+/"&5.1>7J<4>)&+,'-9/5@;CA?I>?I;?I;?M@BUGJ`JKcGE\GE^IBXNF_QLhOLhLIbGE]CBY??S6:J+4<+2767=C@KLGWMHXYSdaXm\PePDT_VlbYq[SiVNcXQf^IZ`K^_K^]J]]I^]H^]I_`KabMdcMdeNffOgfPgfOfeOfeNgeNheNhdNgdNgdMfeNhcNg_Md\Lc[LcZMaUI[OFVJEU55Q@?eTeXK\0)/& ']Odk]ufZr[RhIDR319%%%"+'+(,),),).*0,2/303132435464646677787776746465!87"99"78 675430!3107;G@MZMbfVolZvn[xhTpePjcNgaLe`KdaKd`Jc_Ja^I`]H^\G]]G^^H__H`^H`aKbbMccNcbNbbMa^K^[I]VEVRCPQDQM=IQAO[K[bRbbPbbQcaQeK@O" $ !!%0/8;9C68?+24)251;B@P89E/2:)06/6=>AKKHVLDSF=JXQe^Wl[SgZRgaXo[G\[G[]H]`KacMddNeeOffPggPggPghPgfOgfOgfOgeNgdNhcNhdNidOibNg`MebPg\NcQHYGESBEQ9AI.:@%6;!697:8:9O49G-4@+2:,/5407EBMOJYQL\TOa\UkdNdeOefOffOfgPghQhiRiiRiiQhhPhgOgeNfeNheOidOjhTqgTpjWtgWrTL_DDQ;@K4=F.:A(8>&:=#9< 9:8:7:8:8989898968787867676757330/.-+)*()')&(&'%&#%"%"&#'#($($'#$!$ %!'#$! %" /)1)!&*$E7CYIX^N[cUb|yt}sfheVUPB:PA8PA8O?6M=5L=5N=6N>7N>6O>6O?7N>7O?:RA!8:7: 6968798:897979797877676645332121110/.-+**()''$'#'#($'%'$%"%!%!&"'#(#(#&"$!$&"($$!$$ 6,3WHU[JX[IWUEPUGQ{pvjm_QLRD:SD;SD;QA9O@7P@8M>5M=4L=4K;4I93G94J;9QAAYHL`NWeR[hRZhRXgSXgTZfTYgW\fTWcPObMQ`MWmZmWHX eUgiWjG4330301//-(14>;FWK`eUolZvn\xnZwjVrdOjbLgcMgdNhbLgaKf_Jd_Ic^Hb_Ib^H`]G_\G_]G^\G]^G^^G_^G_^G_^G^[E\YD[ZF\\J^aOcfTggTfcQbZIYD3=?2<7/7%"'   !$&(&).+154<:2OA7N@8J<3H90L<4K<3K<3J;3H93E62A31D54O>?YHM`NW`MXaLVaLScOTiTZiTZiV[cQU]KMZGLXEPlWjWGV aQbgTgI;J>;I<5I:3J;4J<6I;7E86B43C42L<:UCCXFIXEIYEIYCE[DBaIHbIHbIG\EBT@>Q>?UBKkVgVFT ^M_dQdI=I$,-'$'#)$+%,'-(-',',&+%*$+%,&+&+&*&+'+'+&,'-).,0.203142444444433222232322554322211001-5:E@MYKaeSnkXuo\xn[wkXshUpgQmfPldNhcMffNidMhbLfaLeaLe`Kd_Jb^Ia_Ib_Ia^Ha^I`_Ia`Ha_Ha]H`ZE\XCZYE[\H^]I^VDT]J\bOa`M^XETO>KB5@5+5)"*"()-./603:15;16<:ZHFbMMdNMdPOgVXm]am\`m[^m\^l]^k[^gU^nZiUEQ bQceSeFG/8?(69&69!474556 47"48 4634443322100.0-/-.,-+,*,*+)+)+)+(*()((&'%'$($'"& % &!'"%!#"#"""#$%' '!("("&!&!&"&!#)%**&+ %!UDQgSbcP^TDOJ=@UHKr~k~pZfoXdu`o|smYNTVMOYQTMCCWLQWLPNB@I=8M?;L=9PCA\NOjX]ycjzbhoy|yevUER eTfiWjK@L*14*'($)%*%+&+&+&,%,$+#*$*#+$+$+#,#,$+$*$)$*$*&*&+',)-+.-0022242324252424"57 6746343423121112,3:C?LWL`dVojZwn[zn[yhUpM=P>.=[F_cMhdNhbLfaKd`Jd_Ib_Ib_Ib^Ia]H`]G_]F^\E]\E][E][E][E][D\ZC[XBZVAYT@WS@UVDX^L``M``L^^J[\HWWDSP>KC3>2&.$" !!%*'-91;MBQ]PdfXmjZq\MbA3@VJ]dWndWq]RoYQn=O>9RA;YKIshoygtmZhM?I VFTdQbTES/14*&)$)$)$*&+&*$*#*#*")")"*")"(!)!+#+%+%+&+&*&*&+'-(-)-*.,.-..../0021213243534242301111100/..- /015>D>OVI^dSmhVqK.7ID88L>AVFJQA?UC>VD>VFAi]an_idP^eP_K"470100000/.-+,%/2;9DREXcQkM?S=1BcRnn[wkWrdOh]F^[C[aKebMfaKd`Ib^Ia]I`]I`]G_[F]ZE[\F\]F]]F]]F\\F\[E\ZE\[F]\F][E\ZDZXBYU@VQ=RP=QWFY[H\XDUXET`M]aN]_L\YGVO?ME7C7-6)"* 5'/A7BA6DF;LNBUPDX2211//..,,,*+)*(*()'(&(%($'$&#%"$!$!# # """!"# # # #$"(.-SDGG;>"!"""#$#"""##"! !" =3>K>L5+20&+0',E8BWGTZHTM>GWLZUJVj^s}zdZhrh{f]lLAG?10D56G86P@I TETaPbTEU538($&'"($*%*%)$)$)#*")!)!)"*#+$+#+&,',(,(+'+',(,(,(+'+'+',(,)-*-*.,.-0/11111101&59@GO/;A02/0./.--,,++**+33TQ>SXEX`M`cObeQbiSejTghSebN_YFVL=L?5A3+6'"* #%"-!(&!(##)(194A,**)('('('(&(&(%(%'$'$'#&#%"%"# """ "# """"#"""&$477WFLJI`MZ]HU]IVZHSQ@IK<@]P[vzy{~nf{ndt|v^UbH95N?>P@?QA?TE@\JGbMLnZaolYiK?I#^M[eTdPDQ83:)&'"(#($*$*$*%*$*#)")")#)$*%+$*$*%+&+&,'+',(,'+&)%)$)%)%*%*'+'+)*)+),+-..-/.//01"36141110//.-,++)))()'+/0:51=E:K]MefSmiVqWF\5&01$/XH^QAUB3CK:L`Jb\G^XDZG5F3 )5 '5%1S?R_I__I`^G_]G^]H_]H_^H_^H_]F^]F]]F]]F]\F][F\ZD[XCXWBVWCW[GZePemYnq\qoZnmXmjVjeRe^L_UHYL@PE9GD7E?2?/!(!#!#!$"%"%#%"$!$!$!$ $ # # $ $!#"""""""""""""%!#,*;57:57'-+""! ! !!   !!"#":5AD?M" #  F:DgTcjUe`LZYJWQEMK@Anfywxiucysaxtgwse[i~weZhUDBYIJVEDWHIWIJcQToW[rW[~en{ngWfF)''")#)$*&+'+&*$)")")"(")#)%*%*%*%*$*$)$)$*%*%)%(#(#'"(#($(#(#($(%)&*'*'*(*(+)+,,-,-,-,-+,)*&'&&()+/).:+0=(-:,-<93FE9LWI_VG]C5FNCSJ=HC5>C4=I9CUCP\JXM>DODLwqrk~~{cXhWHMVIP[NV[MQTEBRB=QA;UC>VD>[KJvkyucveScF:E #!YHWjVh_M^<3<*))$*%+&+'+&*$)"(!(!("(")#)$*%*$)")")#)#(#)$*%)$'#'"'"(#($)$($)%*&*'*')&*'*')())*)***+,-.0/2$4:#37..,,,/-2!,3#+4'*7.-:EQC6FE6IbOkJ;P*!+8-;J5=  _M\p[m`N^:09!,+*%*%*%*$*$*%)#(!'!'"(#)#)#*#)#' '(!(")#)#)#'#&"'#(#(#)#(#($)%*%+&*&(%)&*'*')')')'*),,./044>H0D8JYH^D6H2'3E8JVG^/$0,+*/$ 0"-OL=QZIb=/>9*5<+@7#>,+6'3Q?T^IbaKcaKc`Jb_Ia_Ia_Ia_Ia`JaaJbaJa`I`_H__G__H_`I_`H``H``G__H__H_`H`_H_]H^\G][E]YC[VAYP=TO>SUFZXI]SBTT@QXDUZFWYDUU?P           OCQXK[(!&>3;aP^dP_TCNE8ATLahcSK[{t{bVek`qeYeVFEYHHTDATEBRCAO@?QB?YKLtcWelYiPCN)%( PAMmYjiUe;18#+*("'!(#)#(#(!'!' ( ("(#'#'"'"(")#("'!("'' '"'"'"(#'"'!&!& '"(#*%*%*%($(#(#($(%*(*')&)'(&&&&''')(++,-,-,.+-+,+,*+'(&('+051<>6DD8HPAUZJaH8JK;OK:RC/J?+@H7HP=QWBY[F]]G_^H`^H`^H_^H__Ia`JbaJbaKbaJa`I`_H_^H_^H__H``H``H`_H__H^_H^_G^_G^^G^]F^\E]YC[V?XR=TO=SO>SP@TVEXYFY\H[^H[]FX        #  PCPdRcSALMO@9M>8N>9O@8UEAur^oM@J$"$  Q@KnXhhTc8.5#**'$'")$)%(#' &'( ("($($'#($(#(#" &!' (!("(#(#(#'!& && '")$)%)$(#'"% &"(%)'*)*(*(*')')((((')'*)+*+++-,-,-,.,-++('$#&).,5?6FN@UWF[RAVWE]VD]P=UPSR@UQ?TRAUWFZ]K_  !!    "#&&##$  D6?bO\jTceP]cOYaMWXGOH:@C67D75pi|j^ndV_bRX[KK\KKaOQcPRgQUhRV`LKWFB|u`pM?H$!#  MRVE\[Ia]Kc]Jb]IabLfbMg`Ld^Ia]G^_H`aKccMedNedMfcMecLecLdbLdbLdcLdbLdbKcaJaaJaaIaaIa`H``G_`H^_G^_G^^F]^F]^F]]E\\E\\E\[D[XAXR>SN<<044+)(%'#&"'#'$(%)&(%&#&#&$'%&$%#&$(&(')(+)+***(($%#%()2>6FN?UYG``MfcQieQkfPlgQmkVpjUmaLc_IaaJcdMfeOgfPheOheOgeNfdMedMedMfdNfdMedKcdKccLdbKcbIaaH`aH``H_`H``H__G^_F^^F^^F]]F\]E]]F^]F][D[WAWQGYIUOAK>25KBJqi~}wrj{uqhz}}wp~pg~[MU[OWPBBXHKQ@>M;4N<6N=7R@HR@ISBLRALLQAAK<8J;5M>8O@9RA;_PQs~mgpjumumjezXTkJF\>;N-1C)=",?7:MVETjTbdP^:.6++/(%%$&!'#'!'!("(#($'"&!(#)$("%  !% &' '!&!'!'!(#(#'"&"'$'#&"&")%*'*'(%&!&"'"'"&"&"&#'$,39<;F>8B=6A?7C?9D@:C@:B?9B?:B@;DA;E@:E>:E>:E>9E?9EA:FD:GH?O]Rhtfsu}mp^zgRnhRniSniUojVpiTnfPjcMgeOigQkiSliSljSmiRlhRliRljRljRkiQjhOifNheNgeNgeMgeLfdKdcJccJccJcaJc`Jb`Ha`G``H`aHaaGaaGa      !! !      "!! 7+5YGUiTdfQ`bLZ^JVVDOJ:D>2:;5;:33hemgmf|w^VpjciabZuldhb~mcldxtznez`T_OA@N?9N@:RB=UEDj]ivk~h_rKG[58O$/F#0J1;W+4M(@*B 0H!.D%(4XFSpYhgRa>3;0.3 ,*)%(%)'*(*&*%*%*%*%)&)&+',')%$ $&"'#(#)$(#(#(#)$($($($)%($&"'"'"'"&"&"&!%!&! &! %!%"%"&#<>IUM^YHYP?OTCUXHZZIZ[JYZJYZJYZJYWGVQCQVGXVGYWGZXHZXHYYHZbRipb}upnYuhRmgQleQlgSojWrkVqePidNgdNgeOhgQjiRmjSmiSmiRmjSmjSmjRliQkhPjgOjfOifOifNheMfdLedKedKecKdbKdcJdbIcaIaaHabHaaHa-/4-/4-/4./5.06/06/07/0601833;45=55<55<349,/1#        &&6298/7&$ '%TDRjVhhSdcO^SAMA3;@6EOJaWVnQNaonpp_Xr^Vn\TkUMaUK][Ri]Um]Uing\Si`Yp{w~{}wpaU`PBBPCCRNPOHJJ=;N@>ZIL\KPfWdoasn_qi[nOHY14G$0J+8V&0M&>.$&*")SBOkVgjUfM?J@9B45:.37.48/59/6:/69/57/57.37/5907;07;/79.68.47')-  "&.0,37.58-58-48/59/5:.59-48-48-48.59/59/59/5905906:/59/59049049/49/5:/5:.49-49.39.28JGVbXlgWkWHY[K^bPeeQffSgfSfeReaObWGWL=J[I\_M`aObbQcaOa]M`gXnj\uqdx~ljVpdOidPjdQleRnhUpkWqgQkcMfbLecLfeNifPkhQliRmiQmiQljRljRliQkhPkgOjgOifOifNifMhgNhfMgeMgeLfeLfeKecJdcIcbHbaHbSFWREVRFWSGXSHYSGZTH[UH\[NdfZqh[sfXmeVi`RbNBO+,/     =6@`SdTHW4+4 * &WGVn[niXhTDO9-32,6SSptwz~rs}}rp|z^Yv?7C<3>>1=7-7<09>3?=4@=4>CKANibmk_ZxxwvqWJPK=4A:2>F>LSJZXM]WL\XM]ZO_\O`YN_YN^YN]YN^[P_\Ra]Sc^Sd`Se`Uf_Te_TeaTfbUfaVgbXicXibWi_UgaUgaUffZlpavwe|r`vr_wt`zwb|ye}xd|r`wjZq_QfUIY\M_gVkp^uwczye|wc{uazo]vj[vpcyncQj`LfcPkdQmeQmfRniTohSncNh]Ga`JdbLfdMhfOjiQliRliQkhPjhQkiQliQkhOifOhfNhgOigOigNigNhfNgfLedJdcIdcIdcJdcJd=3B=3B=3B=4C=4C>4E?4EJ>QaSiue~saxq]sjWj]M^M@O-,2        4,5]N`o^t]N`B5A/$*-!%2%)A19YHVbQ`RDO=27.'(XZ~x~|{]VtWPhF;I/#'+#7&-' - %+#(!0%**!#9+,<.1LCSpkZZw/(6]^|~xwWKPJ=9K?*&7*(9:4H@6GC7FD7FC6DB5C?2@=2@>4B>6C?6D@7DA7DA7D@6DA8FA8GA8FA7EA6D?5C:0>4,94,991?@6FC8HC9HC9HD:IE:JD:JE;JE;JD;JE;JFNH>NG=MH=MI>OJ>PK@QKARLBSLBSJ@RLASLARNBTQEWUH[TFZSEY[KamZr~j~j{huc{gWmRDUPBRl\rxf{gj~j|gvb|n]wj\woa~y}mfTm_KecNieOkePkfQmgRnhTohTmaKd]H`^HaaJdcLeeNggPigPigOifOifOifOheNheNgdMfeNgfMggMgfMfeKfeKedJdcIcbIbcIc:/;;0<;0<:0=:0=;/<>2BUH]m]uwd}r^roZmcQcSDVH=L/-4         5+5^N_o\qfSdYFSQ>HN-1-"!2$&."#-""8*-/##;,-9(%?38d^x~PQkJMlqsy{|~\S_L>:K>8O@>UEGaNXnYiq\nmZmaQbRDSD7D0&03)2/,> )D&@$>&@.+A2*: !4+/G44J:3D:0>8/;<1><1>=2>=2?=3@=3@;1>:0<6,80(35.:6.;7.;8-:7-96,96,97-;8-<6-;6,;7-<:0><2?:0=8.<7-:7-:8.<:0>9/>:0>90>90?;2@<2@=3A@6DB7FB6GB7GA6FA6FA7G?5E;1@@7FB9HA8HB8IC8JD9JN@ScRgwc|yfyfwd|p^ueTiaQfr`xxd}wb{ze}{f~{fwc|p^wiYseWqsev|lfSm_JddNieOkePkfQlhTokWrhTmbNf]H`[E][E]^H`aKccMedMfdMgcLfbLebLebLfbKdcKdcJddJdcJdcJdcJdbJdaHbaG``G`N?LO@MKM51:""          &$L?KjWhmXihR`dNZcNX`LVWENI:@@8AD@L925ba~~z`YrD8<<0/<..:,-:,-<--2%$6''3&&3((>//:++<,-<*(D7=c^zb^}mjSTp KNn|mcw]MQZJM_NUhT`oZjp\okWj`NaSDUJ=LE7D8,5,1J99M;1>:02=;0;9/::/;:/<9.;7-:8.;9/=F7DO=IL;GE8EC6CB5AD6CE7E?3A=2?=2??3AB6DE8FG:HK=LM?NM?NM?OK=MK>ML?OJ>MA7CL@MOCRM@PL?PN@POBRRCTZI[dRghUkiWkjXmlZonZqmZqo\slXngThhVinZop[qo[qkXndSibShgZqqc|s|lhUp`KecMifPlfQmfQmhSokWriUodPi^Jb[F^ZD[[E]^H`aJcbLebKeaJc`JcaJdaJcaIbaHbaHbaHbaHa`HaaIbaHa`G``G_YIVYHVN>KC3>F4>E4?XI[iXnr^vs^vmWlkVj`N`RDTJ?M81;"$        !!$&"' <1;hUgmYkiUdhS`hS_ZHSE7>G@N]ZsjhRM[|||vxf~ll\tB8D,"$3'+."%/"&0#&*, "+,!!1&%8+*@0/>,,>*(F7>a\x}NG`[Zz__( (UWxot`nnZho[lp\omZleReXFXLLF8C=-6>-65*5 / ' ($7GMA6C;1<:0;?3@G8GK4>@6B  2)0bQagUehUefSaXGRC5FC5;PHK&%1)&172@QGZ\Qf]Sg]SgfYppaxqbyl^sj[po^up`xo`xgZoVJ]TGYRFXQEWPEWQFXTG[TH[YK]dSgn\siXofVlhWliWmfUkgWl`Qe\MaaQekYln[no\no\op\qq]qq^rtavvczsayl\qfWjo_rtbvtavuawt`uu_st]rq]rp]qo[oq]ps`svbxxe}zgxf}q`vm^sravq_sxf||j}k}kyg{m{oyl~q}lfSpaLhcNjeQlfSnhTohTpiVqlYsnZtlVp_JcZD][E^\F_]G``JcbKeaKd`JcbIccIdbIdbJdaIb`Ha_Ha`HaaHbbJcN>MO?NUES]KZ^K[N=KQBRXJ^bSio]tr^up\rmYnaNaA3A       ##'7/9UHX]OaC8D 0&-WHWcSb_N]RBM@398-1aZmundXhZHQK8=C42D33?.26)*C273&(5')9*,+>.//!!7''B//:(&D0-C11?/1?18XSlTRm%&YYyuvZVsSNi\Zy_\}{y}}td\wJ@RP?LaM\cQb_M`UEWL=OG:LIIKI7*01$)1$(5'+C2:XFRXIUL>H;/44*)?68wq{PEP@24<.-=./;,.6))=.02%&4''7)*0"!=-.8('A--G11?**K57H6:E59F6?RJ`ur}{ruAAWY[|rp\ToKCX0(4;2Di^{vhp`yeTgI9E/#,6+8L>NYJ[ZJ^I;N=3E@6HF;MJ?PH=MC8EC9DL?LL@N@6D=5D93B"0$ +$ +*$.3+7#"/!$40/B;7J?8IJAR]Odi\spc|qd~n`xgXndTicSgeUjxe}wczlYmdReeSffSfdQdaOb`NabQdcSfeTgfUhfUgdSeeSffTgeTgdRecRddSedTfeUgeUgdTfgUghWjhWkgVjhVkhWkgWjfVifUigUjgUjfTheTfeTfeUheShdTgcSg[M`H?N71<-*1409F>KXL]cTgfUigVjjYnm\rm[rl[rm\rm\sm\tl\tm\un]vo^vo]ulZrkZqlZqm[sn_xm^yjZuiXsjYtjWsePmfQniTpkUrkVskVsmXtlXriUndPh[G^YD\\F_`IcbJdcKdcKdcKecKecLecLfcKfcKebJcaIbdQddRfgVjhWkbPdTDTWEUYIXTGVMBSVI\bThl[rbRh;.;>3?LANH?J=5?1.4"%& "RAPdO`dN][FSR>HO;DM:CN;DS?HVCMH9@:035,+8/-OFK|>5=.""1#&/"%, ".!$, !)) + , 5&'>++F0/G//C--L79/=I=M_Qd`QgNBVE;LI?PMCTNEVNDUMDTLCSKBRI?PG>OH?QB!".$"0(%3)%2&#/"!,+*:<8KG?RH>OJ@PKAQLCTNCVOCVMBTLARK@PGKN>LO>KN>KN>KO>KM=IL=JL>JL=ILMN>KO>KLF?QTJ\VJ[UJ[ZOa]Sf`ThbUi`Th_Tg]Rd[Oa_Qeraxs`taO_M@KNAMK>KH=JL@ML@MF;ED:CD:CGJH>JGJJ?KK@LI>JGJH;FE9DH=HI=JJ?K@7A(#)?5@F:FJ>II=GC5?QAOUCSUBRTBQUBRWDUWFWXFXZGYZGZZFYXEXWDWTCWMCZKGd]Vsncyl~p{lo_|hWtgTrfQnjUrhUrgSphUqlYvp\xlWqdOg^Ia]G__HaaJcbKebKfbLgcLfbLfcLfdMfeNg\JZN@MSDTfTgr^to[p`N_SBOSBP[JZYIYPCRH=MC9I:0=!8-9N@S\KacRijYrk[vjYsVH\\Obn^sm\plYmhTf^KZUCQLKI8EH8CWFVhVk[MaE:H8-7>4?JBOMFTMETLETLEULFUMGVMFVJDTEBU?>T23F-/B--A++>))<**<1.?@:KNEUSGWNEULEVMFWMFXNFWNFVQHWNFSUJYfYmm_viXkNBM(),*+-$'*"%',.1/03 $%$$$$ $$"&&#''#'("'' && && && $%##"""""" "# "#!#$"%&$'(#('"''"''#(("(("'&$((#(("('#))#))$**'&  "#"&'%))%((+'+D8CN=LSAPUCRUCSWDUXEWXEWXEXXEXXEXWEWUDVQATI?UEB\HE`VOjh]xwjpouedTpZHdaMjgSphTqfSpeRocPlhSnlXrkVocMf`IbaJdbKebLfbLecLfcLfcMfdNgeOhdQd_M_bRcl[mr^qhUhaN_[HXYFUZGVWDSUDSSDTQBSH;H("0&1:0??3DE9JK>QOBVSEZI/17)*1""A-.>*)I/.L5:F39B-0E8FF;LE9JK>N[K]jWlvd|{jyjwjzmykufsc}sfl_xJ?PMBUaTl\NdVH\PBTK=MF8HC5D@3A?2A>2A=2@<3@=3@@5CB7C6,5 #!F9DPANN>JMM>@P?AQ>@P<>M;**<));+*MYFUYGUVETSCQJ:F-%;.9>2?5*72(44+74+78/<6.;)#/.(7JAVYOgbXqkb}qiul|stsdzsgzmdvE8>6)&9--8,-1$%6)*3&'0$$1%$3%'0##7**5''8''D//>)(M21N9@E6@D16F:IHO;1>9-:=0=9-85*65)64*65+89-;:.;;0<>2?B5CE9FG;GI;HI;G;/8$!=1;D6BC4@D4?D5>?19OAPcRfiUj_OaOAOC5@<4@:9H8NQ?OSARUBTVDUWDVWDVUBUSATQ@RL;MA3D8.>;5IAMaN^`M^[IYSBOD3=/%A2=F8D=0;5*5<14'10$//$0/&2/'21)45+86,86-8:1;<2=?3AA5BC6BC5A@1<1ONEXXOb_Wk\VkXTj]Zrcazeb{dczecydbxgdzohoep`w]M_?5@82:729839839938:59<6;=7<>9>>:?=:@>GC?HC@HB@HA?GA>G@>F?=D>=D>>E>=E>=E>=D==E<6IE>TKE\UMfaWqpb}~muwxffUq`NjaMjeQnfRoaNj\JgaNijVrq[wnXthRmfNheLedKedLffNgfNhXFW[IZ`M^cPacPafRelWkfRe\I[\J[dPbfReeReZIZC2<2")9+5@2=;.80%.;.8F6BF8E@4B9/>3)66+8=0=A3AA5DG=OcZu|o[PfD:L=3E[QcyYKR?23( "  "().8%%B,.G.-F/0F8CC:H>2;F;KL?Q^L]uawwzl\tH8H4)45+5;/;?3@A5BB5CC6CE7DA4@A4?H:FI:FJ:GK;GH9D@1;4(.*!%*#(2)2@5?XGS[IVTDPC5?) '1(0OBNTGUF3>J:IO=MP>OQ?PR@QSASSARTBSSASP?PL6IGAXMH`QIbZNghYrwfrumn\wbOjaMhfQmhSpdPm_LgaLhhSooZwq\ymWrgQjdMfdLfdLfdLelYpq^utaxq^ugUi]J]^L^^K^\I\^K]\J\[I[\I\UCTC4?;,7?0<>0<9,75)4/%/9-8;/=2'2:-8E5BK;IUFYqdvhbTiD:I6.=;1BYObh[i>00>00(%%  $)!1!. .8%'B.1@()D/5F8EC:J@5CG;L\L^o[qpteTEWC6DD6BD5@H7DK;GM=IL>IK;FJ:F@1<=.9H8E?2>5,5.'/*$,'!(!" !# &)&/=5@]M[aP_\KZRCPC6B=2=G;GH;HD;HGBRGDW<;L^Vnl_xN=JK9FI8CG5@D2>2>>2>?4@A5BB6DC6DC7DC7EB7FB7FB7DC7DC7EB6EB6EB7EB6DB6CB7CB7CC7DOCRsbym}lyicYk9;B&!%!% %$$###"""! !$!()-;1;I:HMOQ@QSARTASSARR@QQ?PN>MG7E<.;8-=A9NJC\OHaQHaXLeeXqrb|~lqpygaOi\HdcOkiTpiTpePkcNjePmkVrp[wp[ukVoeNgbJcdLeq`zuc}vc|l[qWH[O?OQ@PTBRUCTXEUWDTTBRQ@OMOWHY[K]\J^[K]J2IH=IE:E@5@:1;3+51*5TL_zq|sqj^WlNCVG8FA3@C3@C3?>/;9,7?2?DMMBRRGWQGXXPe]Vo`XqaYpbYocYncYm`ViYOcUK`SI\PDUF:I7,79.9<1=>3@A7CE;GG;HE:FC9EC9ED9EE:EH=II?KJ?LJ@NLBNNBPMAPKANKAOLAOMBRMBRLAPL@OK?NI>LI>LH=IC8DD:Fm]rnqq{lSM\$.0% % %$$$#"##""""#$!,).D7EKNP>OR@QSAQTBRUCTTBRN=LD5B:-:8.>A9MJCZNF^PF_UKc\QiiZtue~moygkYtcOkeOkiTojUphRndOkdPliUqmYup\woZukUodNgi[sgWnfUk`NbO?PH8GG7DH7DJ9FL:GL:HJ:FH8FG8GH9JN?QTEWXI[XHYRCSSAS[IZ]K\[JZ[JYN>KA3@I>PeZsu{ug^MdK1@>1A8,;MAPxvfxZKXI8>D22>+/3#$4&%2"!.1!!+/2""0! .<**9'&=*(D14D4J#!)! '$&.()20/;=:GA?M75@-,5,*2.*3-*4/,8B>MibwzbZm7/>6AB:ED;GA9D?7B?6A?6@B8CF=HG>IG>JG@KIAMKAOKANKAMKANKAOLCQMCRMCRMBQKAOJ@MI?LF=H?6@>6@dVi|iprsocyHDQ$!$%$####$# # """"!$#61:F9FKOR@QTBRSAQP>NJ:HA2?7+77.5A3-83-83,72*58.8G:EUDRXFTYFT[HUZHTTDON@KC7A;4>E=KLDSPHXPGWQHXYOa_VjOGX:6B0.93/;>9FNGWbYlvkvttcYj<4AK>M:.;9.<<0>;/<9-95,8&"-%&'$'+ , +*)**'%$%&&$%*!!-"#.#$0"#0"#/"#/"$0!#1!"0!".#",'$,)&/+)3-,6//822:43<33;11:1091/852;85?75?64>76@87B97B;9D=:E=:D=9D<9E>;G@NP?NO=ML;JF6D=/<6*690?DK8>K6J8>J8=J8=J7=J6=I5;F04?,-7YRdnaxwfmoryl\Tg#+.$%%$""#" ! !  !  #%#()-9/:E5EJ:JL:KL;KLMO=LM0=6*7<1BI@USKbXNgXMgUKcTKaZOfeWora{{jnzin\whSpiSokUqlVrmWtjTqiRpkTrnXw_Vl]SidZrj`yf\tka{nc~ocndodndsiwlmb|WL`cXni^vsi~svststhXM`L@PNEUHAQE=NMEWLDUKBSPEWUI[UI[VH[WI\VI[THZTH[RGYF=LF>MPFXXK^ZMa\Ob\OcZM`VJ^XJ_XJ^VH[SFYQEVLAPD9GH1?<0=.%.                       % #,%(1*-7.1;15@28D3;F4J7>K7>K9@M;AO:AN:@N:AN;CP;DR;CR;CRL/7CNM]bYnm_vud|zhoqqd|@AM)*$%$##" " ! ! !"! !"!*'-=1>I9IK;JKNMB4?D6BH8EJ:GLMQ?MRAOP?KQ?JXEPaLXiQ]tZfcnjvo{p}q~o|iv}cnw^hpXbkR]gOZaKV\HTgRalXjnZllXjhUhdRdbSd\M\RCOJ4DI>SQH_VLeWMfULeWOf]RjiZsue~~mtvq{fnYuiRokUrnWtnXtnXsG8DG8DH:GJ;IJ;HK;JL;KM;KN;KONQ>OQ?PQ?PQ@OP>NQ?NQ?OSAPUCSTBRTASTASTASSBSSCSRBRQAQRAPSBQTBRSAQSARUCSUDUVEWWFXWFWXFWXFWXEVVEUVETWEUXGWYHYZIZ[JZ[JZ[IZ[HZ^K\bM^gRcr[lzbr}csgum{rvzwrtuo~ixetes~cqy_mu[joWfhQaeN]aKY_JX^IW\HVZFSYFRYFRXFSWDQWDPWCPVCPUBOTANTAOS@MR@MQ?LN=IL;GH7CE5A:/8  !2/0<69"" )*5>7FE6F8*77*68+78+7<-9A1;>.6<,32%*        !  DBNiaq{q}suwyz{||||||}}}}}}~~~|wp~}v|~|~yZZjSSfc\qcVln\subz|intf~CAM)(#$$##$$$% % % $#"!"$'+8/;B5CH8IK:KL;LNLO?NN>MO>NQ@PR@RTASVCUWDVWDVXEXYFYYFXXEXXEWWDUWDUXFWXFXXFXXFXYGYYGYZHYYGYZHZ[HYZGYZFYYFXYGX[HZ[HZ[H[[I\\J\]J\]I\]J\]K\]J\\I[[I[ZI[[I[]J\^K]^K]^K^cOagRdnWiy`ri{j|k|rzzxkz|^n~ap{`oqYhkTcmTdqXht[ju\lsZkmVgiRdeO_bL\`K[aL]dO_dO_`L[_KY`KZ`KZ_JX^IX\HW[GVZFTYFSXESWDRUBQSANP?LM;HF7B4.4  %#857E>C#(( /-8@6D@0<9*68*4:+6;,7>.9D3>J6@N9@A16  !!! !"    %# @?Jogu|~sftrql|UVgZWlf\rjZqq^uwd}}l~ndYn::D$!#$#$$$$$% $ $#"!"$"&),6.8A3BF7GJ9JL;LO=OO=OL;KK:IL:IK9HI8GG6FB3B9-:2(5>5EIBUPI`VMgYQjWNgQG`UJcdXrm^yud}kpq|ir^zmWslVqmVrLMP>NO>NP?NSAQTBSTBTVDUXEVYFWYFWYEWXDVYFYYFXYEWXEWXEWYFYYFYYGZZGYZHYYGX[I[\I\\HZYFXXEXYFY[HZ\I[\IZ[HZ[HZ\I[\J[]J\\I[[IZYGYVDVRARQARQASUCUYGW\IZ\I[^J\_K\_K\_L\^L]^K]`L]kUey`pw^pqYjiPbfO`lUffQa_L[^JZ_IY^HYcM^hQbhQbePaeOabM^_K[]IY`L]iSelVgcN^]IW]IX_JZ]IX]IX\HW[GUZFTYESWDRUBPTANS@MP=JM;G?4=''(      ($*7*45)35)39+6;,89+6:,7S7GKCXRJaTMeULdUKcWMf]RldWqj]vpazwgmpnzfq\wmWsJ:IM=LP?OR@QR@QRAQTCSWEVWEWWEVYFWYFWXEWWDWXEWZHZZGZZFYZFXZFXZGZ[H\[H]ZH[XFXYFYYGYZGYYFXXEWYFYZHZ[H[\HZ[GY[GZ[H[[I[\I\\I[WEWQAQJ;LE7GD6ED6FH9IM=MTBSZGY\I[\H[\H[[IZ[IZ\J[\J[]J[^K\`K]_K\\IZ\HY^I[_K]]IZ[IY\HY]HY]HY]IZ`L]`M]`L]_K\]IZ\HY[HX\HY`K]bM^^JXZHUZGU[GV[HV[GWZFVXDTXDSWCRVCQTAOR?LQ?LO2>;0;:.:1>aJVeLXJ8A          +1:jam}~xyytpo\kcQ`vbswpk{HM]XYmg^tp`wwd|}jpqtg~NJX%##"""""# " "#"""#"!)%,9.7+66,9@8JMF[OH_NG]QH`XMg^Sl`SmaTnfYso`yvf|kopm|hL;IN=LR@PTASS@RS@RTBTXEX[HZ[HZ[HZ[HZZGZYGZYFY[G[[H[ZG[ZF[[G[[H\]I^\I]YGZXFXXEXWEXWEXXEXYFYYGZ[H[[H[[G[ZGZZH[ZI[ZI[[I\ZI[UEWL>ND6F@2CC5EK;MSCUXFXYGXZHY[HZ[HZ[HZ[HZ[HZ[IZ\I[\IZ[HYYFXXEUWEUZFX]HZ]HZ\IZ[IZ\HY]HY\HY]IZ]J[^K\^K\]J[]IZ[HYYFWYFWZGW[HXZGVXESWDRXESYFT[GX_L_bOc`N`\J[YGWUBPQ>KP=KM;IL=I818           ,)/A4A@4A?3A@3BA4B@3AC5DiScjScJ;F                  ""!$!*3h`k~~q}ryyiyzfxm~pk|u`qxdtm}m}zhyyXYhCHZa[qk_uud{koqof\o8;B# !" !!"$%#"!!!"! ""/)2>0?C4DG7GH8GH8HI8IH8GG6FF5EF5EG6EF6DC3A=/<7*77-<@8KKDZLD[NE]VLe_UnaVp_To_SndWrm_yue{imoqO=LQ?OTARUBTUBTUBUWCVYFY[H[]I\\I\]I\\I]\H\[G[[H\\I\[H\ZG\[H]]J^^J^\I]YHZXFXVEXVEWVEWWEXYGYYGZZG[[H\[H\[H\ZH[ZI[ZI[[I\ZI[TDVM>OKNQ@QVEW[I\]J][IZZHXYGXZGYZHY[HY[HZ[HYZHZ[HZZGXYFWWETYFV[GX\GY\GY\HZ]J[_K\`K\_K\_K\^J\_K]_L]]J[]J[[HYYGXYFWXGVYGVYFUXDSVCQVBQWCS^K^dTihYoiYoeUk^M`VDTQ>MO=KL;IA6@+),           +(/A4BB5CA4CB5DB5C@3BC5ElXlkXkH=I!" & (!)!(")")"*#*")#*#*$*#)!(!(!( '' '!(#*$+"* '#+)2f_jxlwxmy{r~XQ^PGU_R`gXfjYhcSb`R`_S_XMYTKW{tn{=CRRRfaZpqbz~kppqxiZRc**! !!"#$$#!  !!!   "%'2*4LMH91?C4CF6EF6FF6FI8HI8HG6FE4DD4DE5FD4DA2@:-;3(60)6A:KLEZQJbVMh]TnaVqbWr`Vp]Rm_TnbWoh[rn`wsczSCUTCTTCTTBSSAQRAQRARSBRUCTWFXWFXVDVVDWWEXWEWWDWWEXYG[ZH\XFZYFYZH[WFYTDVTCVTCVVFXWGYXGYYGYWFXWEYXFZZH][J_ZJ]ZJ[ZJ[ZI\ZH\XFYWFXWFXXGYZI\[I\ZH[YGYYGYYGZXGZXGYXFXWEWXEXZGYZGZXEXUDUTBSVCTVEUWEVXEVYGW[IY]K[bM^ePafPbdOadN`dN_bM^_L\^K\[HY[HY\JZ[HXXGUVESUCRTAQS@PS@PSBRSCRSCRTCRSBPP@NN>LM=JK)2?)2>&1=$0<"0: .8-5+3,4,5+3*2+3+4,3,3+3*1,4*4YYf}{q~tjusny`_jWXcAHSBHSOR\\\fjfpx{tFIWYWje^spbyrppp}lg[m239  !#"!!   !!!  !! %5,7>1?C5DF7FG6EH7HI9HH7FE5DD4DD4EE5FD4D@2@9-;1(44,8B:LNF^ULgZQk]Rl`UpaWq^Tn]RlYOiZOg^QgcSiTEXRCUL=LE6DE6DH9HH9II:JMNK=ML=MK=LK13@14@14A15B36C48F59H59H59H39H08E.6B.5A-5A,4@,3?*3>(2<(1<&0;%0:$09%09%09%0:$00?7+89/?F=STKeZPkYOiZPj^TnbWqbWp^SlZNgWKcWJaH:JF9HB6DA4BF7GKPN@RQDVOAUH;L?4B;0MGBPIBSIBRKCSLDUKCTE=L@:GB:ID77?66=46=46=77=87>66<55<228-04-/542:@8DG:IK;JM<1>>1@B4DI:KQATVFZVG[UGZSEYH;M=2A9-:>1?I;LTGZWI\YJ^YI^SDXJ=OL?RUH\WJ_OATOAUMATL@SK>QJLH?MH?MH>LKANMDRPFUSHXSGWRGVSIXSIXQGUQGVQHVPFUMCRI@NJ@NNAPQBRSDTVFVUDUSBRSCTUDVTCUSBTXFW[I[\J]\J\[JZ[J[ZIZXI[ZJ]^Na`OcaQfcTjeUkeTjdTheSifTkgWm9-99-:=0>C4DI:KRBUZJ_[K`UFYK>N:/<8-8A4BKML?OM?OSFVYL][M_YJ\VGXVHYUGXVGXYI\XI\\K`_OddTiiXneTicRhbRi^OeXI]XH\^Mb]MaYJ]XHZSDUM?OQCTZL^^ObVHXPBPJ1@C5FG9IOASWH\UFYH;L:/<=1>F:IOARRCVRCVXG[RBUO@TSCXTEXJ=NF9JB6F=1@8-;8.<=2AA6FB7FB6ED7GB4D@2A@2@B5CIOF:KG;LN@SOBTL@QJ>OM@SWH\]LbdQgmYou_wxc|vd}q`xl\siYpeUkhWnlZqr^us`u]MaVGZXG[WG[RDWPCTM@QPCTUHZUHZPBTHMTFVWHZPARN?PN@POAPPAQL=MRBTYI\_OcdShcQe\K^YI]YI^VH[TFYYJ^XH[PARJM]NcbRf_OcZK^WFYUDUUDV_M`hUjn[rs_vt`wvaxvaxt`vq]so\rlYofUjfVlo]uq_whWo_Od_ObfVieTi_Nb\J^cRfm\rn]rr_ur_vtaxxd{ucxp_tkZnfVigWjkZqm\sl\rfVk[K]WGZTFXVGZ[K^_NbYI\YGY[I[ZHYSBRRAP_N`dTg\L^M?NN@OJ<1@A6FE:KHOD7E?4B@5DB7GD8IL?PQDVSFZWI^_PebRhbQgcQhfSjgTkfTkdTkbRh^Od\La^Nb_NaaOccRg\MaZJ^WGZQATJ=NGMSCSQBSM>NTEVWHYVGXUFWSETRCTSEVSEWPBTM@QQBTTDVRCTM@OI=LF9IK=NUEY\La_PbRDTJ=MOARWHZ\L`YJ]SEVOAQM?NN>MSCS\J]dRfkWljVklXmq^subxvbxvcyr_ufTi]L`bOdbPd\K_XH[WGYVFXXI[SEWN>PN>OUEXZI]ZJ]YI\\L^aPc_Na\K^ZJ\[J\[J\YH[`OcbQe]M_UFWM>MG9GK=LTDUWEXQARO?OP?NO?MM=KL=LM>MVEVXHYN@OI;I@3@F8GTEU\L^XHZL=M>3?>3AM@Q\L``Nb`Ob^Ob[L_ZK]XH[YI\ZI\WGZUEWXH[VHZTFVUFWVGXWFXXGYXH[VFYQASRCUWH[]M`cRdbQd\K^TDVPASUEW_Nb_Ma^L`]L_ZJ\WGYVGZYJ]]Ma_Ma\J]XFYXFXVEWN>QKON;KP=NTATYFY[G[[GZE7GJOL>OH:J=0=:-9A4BJ=LO@QPARWGYWHZTDWQBTN@QNASM@QK>OK>OL@RK>PD9J>4C?4DF:LK?RNATK?QD8HB6E>3A@5CF;JJ?NF;JIOUFY^NcbRgeTjiXobRh\Mb\Lb_Oe]LaVF[VG[UG[L?RIOPBUPCVN@QK=NH;LE9JE:IE9HF9IH;KJ=MG;KC7FB6EF9HJ=MM?PF9HH;IOAQPCTN@QRCTSDUQCTOBRK>NK>NL?PL?OJ=MF:IINM?OL>NK>NK>MOARUEXYI]aPdTEWK>NL>OO?PRCTTFXTGXTGYVGYRCTTDU]M`fTigTjcPddQdjWjn[on[pp\qp]rhUi\J]UDVTCTTDU\L]`ObYI^XI^XK`RDWJNO=MK;JQAQSBRN>LF8GB5B<0PM;MQ@QSCTQASN>PN>ON?OLOK>OIPGOQBTWG[YI^[J__Nb_Nb_Ma`Ob_NbYI\UFXVFYVGZQBSIOF:JE:IA5EB7FK>OREWQASYI\aPecRgaPddTjXJ_PCWUFY\L_]M`RDWN@SL?QC7HA5DC7GE9IG;KK>PL?PG:JC7FC7HD9JD9IF:IH;KJ=NM@PK>PL?PL?PL?PK>OK=OE9IG:JNAQSFXVGZVH[UFYSDWSEXPCVREWOASJ=MK>NJ=ML>OL?PL?PL>NK=NN@QRDVVG[\K``OdWH[QCTQCSQBRRCTYJ]ZK`WI\UFXO@PPARYJ\_Nb]K_\J]^K^cPchTikWlmYop\qmZnbPcSBTSCUVGY`ObdRf]K`UDYWH]WI]TFXTDVSCUQARRCTRCTO@PN?ON>NSBS[IY^K\UEUP@ORBRWGXZJ[VGXVGXVHXXIY\K]]K]ZI[UDUSARTBSN>MTEUTDTM=LH:IH;IE9GD8FF9GF9HF9GM?NM?NB5C7+8K=OYI]^Oc`QdaQdaPb\J\XFXWFXWGYWGYWGXUEVRBSRBSRCTWHZ\L^`NabOdaOd`NbaOb`N`ZIZYIZZJ\XGZUDUVEVZI[XGYXGYZI[ZI\WFYVFYVGYTFYTEWTDVN>OK:LN>OSCTTDUSBTQASP@RO?PLPL>PL?PI=MH;LM?QK>OM@RQCVPCVK?QH;MI;NH:LG:LK=OO@RWI\YK_UH[TFYTFYRDVRCUSCURBUSCWXH[WHZUFXVGXTEVO@RL>OI;KC6ED6EF8GF9IF:KIMM>MM?NM>NL>NM@PL?NH;JD7FA5DH;KRDTXHZQBS>0=C6FVG[[K_WGYTDUQASP@QN>OLON?PM>ON>PRCTTEWYJ]\L`]K_]J^\I][I\^L_aNa^L_[I]YHZWEXUCVTCUVDVR@QQ@QUDVYI[ZI[YGZXFXVFWVEWVDVTCUP@QN>OR@RSBSSBSQASQ@SRASRATM=OJ;MJ2CC5FLRN?RTCVWGZWI\WH]WG\WH]VGZUFYSCVO?RQAUQBUN@QK=NJ=MIOOBROBRJ=MIOL>NRCSSDTXHZaPciWkmZnp[qnZpdRfSBTJNL=LQ@QVEWWFXRCTK=KH:HK=LQAQTDUUEWSCUQARSASVDWYG[ZH[VEVN>NE7E@2@?2@C6DH;IJ=LJ=LJ=MJ=MJ>NVGY[K^[J]XFXPAQIOP>NQAPTCSUCUVDVXFXZH[YHZWFXQASMPSDXWH\XH]VF[UDXWEWSBTRBUTDXVEYXH]WH\SEWQCUTEYXH^SDXL>OF:IE8HF9IE9ID8HF9IIPK?QM@SREXVG[ZJ^[K_[K`\Mb\MbYK^ZK^^Nb_PdYI^XI]ZJ_YI\UEWUFXVGZWH[YJ\\M_\M_ZJ^XJ]XJ]WI]UG[ZK`XI]SEWTGYWI[WIZUGXWH[\NbXJ]VI[XJ]YK]VH[ZK^]OcbRgdSicRhaPfdThgVkhWmhWmfUldUkbSidTjhXmdTibQebQfcRhcRhaPd]N`[L]WHZOAQJ;LXI[\L_YH[YI[[I\ZJ]]MaaQeaPe]La[J]VFXO@QL?PTEXXI[[L^`PbeTgkXmlXneSg[J\RBSM>ORCUQBTJ:JA2@B3AM>ORCUPBSM@PREUVH[[K_\L^XHYSBUG9GB4AKNO=MSAQP>NG8EB4AD7FD7G@4BA4CJ=MN@POBRQDTTFWXIZ\L__M`]K^\J]\J]PBSN@RUGY]M`^L`QARJ;KH:KK=OM@QM@RL?PM?PPBSVFX\K][K]ZI\ZH\ZH[VDVP?PP@PP@PLOUDUXGYYI[VFXUDVTBSRARSBRSCSO?PP?PUDVYI\XHZUFXO@QI9JH8HO=NLQJNO>ON>NLOM?OXHZ`Ob^L`ZI]WG[UFYVGZSEWQBTRCTVFXZJ]^M`_Na]L_[J][J[XFWUDUUDUTDVSCTQCRRBRVFWZJ\[J^XGZZI\YGZTCTQARQBRP@PO?PRBTVFXVGXTEXRBUQ@SP?QO?OQASO@RN?PTCUTBUQ@RP@QSCTVGYXG\]K_^L`^La`Mb]Ka]Ka\J`ZI]XH[ZL`YJ]XH\WG]UF\UF[YI_XI^TFZQCWPATQBVTFZWH]XH^ZI_[KaYJ_VG[TEYSCXO@TN?SO@TPAURCWTFZSFYPBUJ1?=1?<1?>3BC7HA6FE:JL?PN@RPBSN@ROATOBVMAUQDXVH[UFXPBTPBTPASTEXUEYUFXVGYRCUUFXWGZVG[UGZTFYYI\\J]]K^_Ma`NabPebQf_NcYJ^TFXYJ^ZK^XI[YI\_Nc_Nc\K`YH]YI]XH\WH[UGZTFZWH\\La^Ma`Na_Na]L_`Oc`NbaNcbPeaPedSheTifSheRfcQeeShhUlgUkcQf`Nb[J_ZJ^ZI]ZI][K_YJ\\K^]L_`NabOaZHZYH\[J_ZJ_YI]^M`_Na]M_XI[RDUTEWVGYXHZWFYWEX[I^\J^VDVP>OP@QQCTWI\ZK^YJ\XI[YI[YI[[J\\J[UDUM>NM?NVGY]M`\L_]L_ZH[[I\[I]VEWSCTRCTQARP@PPAOLNJ=MK>ML?OL?OK=ML>NQBTXH[^Ma_La[I\ZH[[J]]K__MaSBUH9JL=NZJ]aPebPf`Od\K_XH[VFYSCUSCUWFXZJ\[K]]L_\K^ZI[XHYTCTTDUVFXXGYXGYWHYVFWXHYZI\YH[WFYYI\XHZTDVQARP?PN>NM=OOARTFWWGYXG[VEYTCVQ@SM=MRBSSCURASSBTR@TN=OM=NP@RUEXZJ^ZI]XH[XG\[I^ZH]ZH^YH]XG\YI][L`[K`[K`ZI`WG^XH^[Kb]LcZJ`TF[RCWQCVTFZXJ_YJ_VF\VG\UH\TGZTEXSDYPBVL>QJPPBUQDXREXQCVL>PD6F@3C=1@<1?@4DD7HD8IH=NM@RN@RSEXSDXRDWSDXRDYRDXUGYRDUM?QN@RRCVXH\XH]WH[XI\UFYUFYTEWREVSFXQEVTFXVFXXGY[J][J^bQfeSicRg`Qf_OdaQf_Od^Ma_ObaPe`Od\J_XH[ZJ_[K`YJ^XJ^ZK`^NcbRgeTicQe^M`[J][J]ZI\ZH]ZH]ZI\^MabOcdQeaPc\K^bPfeSifSidShcQfaPeaPf`Od_Nc`Od`OcaOdaOc_M`\J\XFXZI\\L`]Mb^Mb^Ma^L`\K_YH[SDVQBTTEWWGZWGZVEX[I^ZH]UCVRATVFXYJ]_Pc`Qd^Na[K]YI[XHY\J\^K]YHYP@PN?OSEUVHYUFWXHYZI[]K^^K^ZHZXGY[J\YHZSBSM?ML=KM>NO@QP@QN>NO?PK=MJ=MNARTDVPARM?OM@OOAQO@PL>MM?NQBRVEWYGZ]K^\J^]L_^M`_LabPe[J^L=ND5FP@S]LaaOebPe_MaYI\YI\TDWTDWXHZYI[YI[ZI[YIZXGXTDUN>ON>NQBRUDVUEWVFXWGYXH[XH[XH[XH[XH[WGZVFXSCUP@QM=NKOP@QO>OLQSDXSCVQBURCUSCVSCVRAUSCWUFZWG\UFZUFZXG]YG]UDZWG]ZJ`]Lb\KaVG]SDYSDYUG\YK_ZJ_SCXQCWTG[VI\UFZRCWRDVNARK>OL>OM?QN@SQBVSEYRCVM@RN@SL>PK=ON@SKNQCTQCTPCUSEWXGZXGZUFXVHZYI\YI[WGXWGXXGYZGZ\I\_L``MacPddQeaNbaNc`NbVEWH9IH9KWFZ\K`^La^Ma\La\LaWG\WH[\K^\L^[J\ZI\XHZUEWRBTP@QLPKON?QPATN?RJOPASSCVSDWM>OK=NK=MN@QUFYVEXVEWVEWSBTO?QSCVRBTL=MIPTCVXGZXH\XH]YI^WF[WF[[J_\KaUFXUGXWHZXH[YI\WFY^La]La[J^]L`ZH[XFYZI]_Nb`Na[J\XGZXFYWFXUDWTCVYH\^La`OddQgdRgaPd\K_WGZVFY\K^\K^ZJ]XHZVFW\J\bOcbOd`Ma^K_]K_]K^^MaaOdcQfaOd]La[I]ZI[[K]_Na]L`[J^\L__N`[J\[J\]L`]Lb\J_\J^_Ma`Mb\J_VFYWHZXI[ZJ]YI]WG[ZI\\K^aOcbQe^L`]K_^L`\I]XFYWFXXGY[J]]K^_L`aNb_L`^K_]K^\K^[I[UDVUEWZI\]K^[I\XFYWEWTCVO@QH:IC5CB4BG8GN?OSDWUGYTEWSCVUDWYGZ]K_^Na^Nb^Ma]L^ZI[XGYWFXYFY\J]aNcdQffSgfSgdQf`Nc]K_VDVI:I@2@KQN?QO@RQASP@RO>PN=NK;KH8ID5GF7HK;LM=OI:LK;LJ:JH9II:KKPPASVEYVEYP@RP?RRASRBSPARO@RO@RN>OM>OO?RRATSATO=OQASTEXSBUVDXQATP@QQARSBSSBSQASRBURCUM?NB5CD6DH9IJ:KK;JI;JG9HH9GL=LRCTRBTSBTO>OI9HF7EE7FF7GG9IL>OQASLPRBUUEZWF\ZI]WEYYG[]J^[H]TCUP@PP@PTDTWGWUDUXHZWH[UEXVDWQARSBTTDWTDWTCTTDUVFXZJ[WHYKON>NQ@PSCUXHZUFWQARQ@QVEW\J]\J][J\ZI\ZI\\I\YGYUCUVDV[J]_Ma_L`aNabOb`M`]J^ZH\XEYO>O?1?C4CMRN>QQATRCVI:LJ;LL=NK=MI;KJ;LL;MQASVEWUEWSDVVFXYI]YI\SCUP?QO?PQASSCUSCURBSN>NM>NO@QRASSASQ?QTCURASG7GJ9JK;LLLRBRPAQQARO>PL1>A4AM=MYHZWFXXGYVEWRBSRASQAQN?OM?OM>OLOK>ON@QOARL>OKNQBSVEWWEXUDVSCUN>PK;LLPKNWEWUCVYH[\K^VEWXFYUEWSDURBRM>LL=JI:HM=MSBTVCVUCUUCUTDUUDVVEXYHZ[I[YHYWFXYGZXGYUDUSCTSCTTDUUEVRBSO?OPAQWFWYGYWFWSCSUCT]J\aM`dPddPdbNb^K^ZIZVDUSASTCTWFWVEVXFWZHZWEWN=MI:HLOR@QRARQARRBSVEV\K\^L^\J[YHXYGY[HZZGZZHZ\J\]J^]J]]J]]J^\I\UBTH7FC3BH9JN>PN=OSCUWFZSCVP@QRCSSCTQ@RP@QO@QO@QSCTVFXXHZYHZYGYXFXYFXYGYVEWTCTUDVXFXVEVSBRR@QO?OO>OSASYGZ]K^XGZSCTO?PO@QSCTP@QTCVUDVTCUTCWSBVN>PK;MLPM?PJMKNSCSJ:JRCTZJ\ZI[VCURAQPAQQ@RP>PL;KG6EA2@A4BF8HG:JB4DG8IN>PRBTWFY\K]TDTQAQTDTSCSN>MTDTXGYYHZXFYRAQJ:HE6ED5DE6DKOQBRK=LH:IH9IKMG:HJML=MN=MQ>OUCUXGYYH[YI\ZI[YHZZGZXFXXFXZH[WFXUCUUDVVEWUDURARK;JG9HL=MTCTWEVVEVTCTUCTZGY^J^bNbbOb`M`\J\YGYXEWWEWXFXZHZ\I\`L^_L^ZHZTDTN>MQ?OR@PSARWEWYGYZHZ[HZ[GYYFX^K]TCTM=MM=NP>PSBSUEVXGYZHZ\J\]K\bOb`M^YFWWCUXCVXEVWFWZHZ_K_^J^]J]\I\[H[ZGXJ8IA0@H8IO@RO>PRBUVEXVEXTDVTDVSCTRBSO>OLNQBSN=MTBTUCUQ?QM 0 ? arguments_list_p[0] : ECMA_VALUE_UNDEFINED, arguments_list_len > 1 ? arguments_list_p[1] : ECMA_VALUE_UNDEFINED, 0); if (JERRY_UNLIKELY (proxy_p == NULL)) { return ECMA_VALUE_ERROR; } return ecma_make_object_value (proxy_p); } /* ecma_builtin_proxy_dispatch_construct */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_proxy_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (this_arg, arguments_number); switch (builtin_routine_id) { case ECMA_BUILTIN_PROXY_OBJECT_REVOCABLE: { return ecma_builtin_proxy_object_revocable (arguments_list_p[0], arguments_list_p[1]); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_proxy_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_PROXY */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/strtod.h000664 001750 001750 00000021525 15164251010 040160 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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" #include #include 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 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(dS_Exp5)) <<= static_cast(dS_Exp2); BigInteger bS(bInt); bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); BigInteger hS(1); hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(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 template inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) { uint64_t significand = 0; int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 for (; i < dLen; i++) { if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5'))) break; significand = significand * 10u + static_cast(decimals[i] - Ch('0')); } if (i < dLen && decimals[i] >= Ch('5')) // Rounding significand++; int remaining = dLen - 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; dExp += remaining; int actualExp; DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); if (actualExp != dExp) { static const DiyFp kPow10[] = { DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 }; int adjustment = dExp - actualExp; RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); v = v * kPow10[adjustment - 1]; if (dLen + adjustment > 19) // 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(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(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); } template inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) { RAPIDJSON_ASSERT(dLen >= 0); const BigInteger dInt(decimals, static_cast(dLen)); 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(); } template inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) { RAPIDJSON_ASSERT(d >= 0.0); RAPIDJSON_ASSERT(length >= 1); double result = 0.0; if (StrtodFast(d, p, &result)) return result; RAPIDJSON_ASSERT(length <= INT_MAX); int dLen = static_cast(length); RAPIDJSON_ASSERT(length >= decimalPosition); RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); int dExpAdjust = static_cast(length - decimalPosition); RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); int dExp = exp - dExpAdjust; // Make sure length+dExp does not overflow RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); // Trim leading zeros while (dLen > 0 && *decimals == '0') { dLen--; decimals++; } // Trim trailing zeros while (dLen > 0 && decimals[dLen - 1] == '0') { dLen--; dExp++; } if (dLen == 0) { // Buffer only contains zeros. return 0.0; } // Trim right-most digits const int kMaxDecimalDigit = 767 + 1; if (dLen > kMaxDecimalDigit) { dExp += dLen - kMaxDecimalDigit; dLen = kMaxDecimalDigit; } // If too small, underflow to zero. // Any x <= 10^-324 is interpreted as zero. if (dLen + dExp <= -324) return 0.0; // If too large, overflow to infinity. // Any x >= 10^309 is interpreted as +infinity. if (dLen + dExp > 309) return std::numeric_limits::infinity(); if (StrtodDiyFp(decimals, dLen, dExp, &result)) return result; // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison return StrtodBigInteger(result, decimals, dLen, dExp); } } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_STRTOD_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/meson_options.txt000664 001750 001750 00000003216 15164251010 032654 0ustar00ddennedyddennedy000000 000000 option('engines', type: 'array', choices: ['sw', 'gl', 'wg', 'all'], value: ['sw'], description: 'Enable Rasterizer Engine in thorvg') option('partial', type: 'boolean', value: true, description: 'Enable Partial Rendering in thorvg') option('loaders', type: 'array', choices: ['', 'svg', 'png', 'jpg', 'lottie', 'ttf', 'webp', 'all'], value: ['svg', 'lottie', 'ttf'], description: 'Enable File Loaders in thorvg') option('savers', type: 'array', choices: ['', 'gif', 'all'], value: [''], description: 'Enable File Savers in thorvg') option('threads', type: 'boolean', value: true, description: 'Enable the multi-threading task scheduler in thorvg') option('simd', type: 'boolean', value: false, description: 'Enable CPU Vectorization(SIMD) in thorvg') option('bindings', type: 'array', choices: ['', 'capi'], value: [''], description: 'Enable API bindings') option('tools', type: 'array', choices: ['', 'svg2png', 'lottie2gif', 'all'], value: [''], description: 'Enable building thorvg tools') option('tests', type: 'boolean', value: false, description: 'Enable building Unit Tests') option('log', type: 'boolean', value: false, description: 'Enable log message') option('static', type: 'boolean', value: false, description: 'Force to use static linking modules in thorvg') option('file', type: 'boolean', value: true, description: 'Enable File IO calls in thorvg') option('extra', type: 'array', choices: ['', 'opengl_es', 'lottie_exp', 'openmp'], value: ['lottie_exp', 'openmp'], description: 'Enable support for extra options') glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/vm.cpp000664 001750 001750 00000525174 15164251010 041105 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "vm.h" #include "ecma-alloc.h" #include "ecma-arguments-object.h" #include "ecma-array-object.h" #include "ecma-bigint.h" #include "ecma-builtin-object.h" #include "ecma-builtins.h" #include "ecma-comparison.h" #include "ecma-conversion.h" #include "ecma-errors.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-lcache.h" #include "ecma-lex-env.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "ecma-promise-object.h" #include "ecma-regexp-object.h" #include "common.h" #include "jcontext.h" #include "opcodes.h" #include "vm-stack.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup vm_executor Executor * @{ */ JERRY_STATIC_ASSERT ((sizeof (vm_frame_ctx_t) % sizeof (ecma_value_t)) == 0, sizeof_vm_frame_ctx_must_be_sizeof_ecma_value_t_aligned); /** * Get the value of object[property]. * * @return ecma value */ static ecma_value_t vm_op_get_value (ecma_value_t object, /**< base object */ ecma_value_t property) /**< property name */ { if (ecma_is_value_object (object)) { ecma_object_t *object_p = ecma_get_object_from_value (object); ecma_string_t *property_name_p = NULL; if (ecma_is_value_integer_number (property)) { ecma_integer_value_t int_value = ecma_get_integer_from_value (property); if (int_value >= 0 && int_value <= ECMA_DIRECT_STRING_MAX_IMM) { if (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (JERRY_LIKELY (ecma_op_array_is_fast_array (ext_object_p) && (uint32_t) int_value < ext_object_p->u.array.length)) { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); if (JERRY_LIKELY (!ecma_is_value_array_hole (values_p[int_value]))) { return ecma_fast_copy_value (values_p[int_value]); } } } property_name_p = (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_UINT, (uintptr_t) int_value); } } else if (ecma_is_value_string (property)) { property_name_p = ecma_get_string_from_value (property); } if (ecma_is_value_symbol (property)) { property_name_p = ecma_get_symbol_from_value (property); } if (property_name_p != NULL) { #if JERRY_LCACHE ecma_property_t *property_p = ecma_lcache_lookup (object_p, property_name_p); if (property_p != NULL && (*property_p & ECMA_PROPERTY_FLAG_DATA)) { JERRY_ASSERT (!ECMA_PROPERTY_IS_INTERNAL (*property_p)); return ecma_fast_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value); } #endif /* JERRY_LCACHE */ /* There is no need to free the name. */ return ecma_op_object_get (object_p, property_name_p); } } if (JERRY_UNLIKELY (ecma_is_value_undefined (object) || ecma_is_value_null (object))) { #if JERRY_ERROR_MESSAGES ecma_value_t error_value = ecma_raise_standard_error_with_format (JERRY_ERROR_TYPE, "Cannot read property '%' of %", property, object); #else /* !JERRY_ERROR_MESSAGES */ ecma_value_t error_value = ecma_raise_type_error (ECMA_ERR_EMPTY); #endif /* JERRY_ERROR_MESSAGES */ return error_value; } ecma_string_t *property_name_p = ecma_op_to_property_key (property); if (property_name_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t get_value_result = ecma_op_get_value_object_base (object, property_name_p); ecma_deref_ecma_string (property_name_p); return get_value_result; } /* vm_op_get_value */ /** * Set the value of object[property]. * * Note: * this function frees its object and property arguments * * @return an ecma value which contains an error * if the property setting is unsuccessful */ static ecma_value_t vm_op_set_value (ecma_value_t base, /**< base object */ ecma_value_t property, /**< property name */ ecma_value_t value, /**< ecma value */ bool is_strict) /**< strict mode */ { ecma_value_t result = ECMA_VALUE_EMPTY; ecma_object_t *object_p; ecma_string_t *property_p; if (JERRY_UNLIKELY (!ecma_is_value_object (base))) { if (JERRY_UNLIKELY (ecma_is_value_null (base) || ecma_is_value_undefined (base))) { #if JERRY_ERROR_MESSAGES result = ecma_raise_standard_error_with_format (JERRY_ERROR_TYPE, "Cannot set property '%' of %", property, base); #else /* !JERRY_ERROR_MESSAGES */ result = ecma_raise_type_error (ECMA_ERR_EMPTY); #endif /* JERRY_ERROR_MESSAGES */ ecma_free_value (property); return result; } if (JERRY_UNLIKELY (!ecma_is_value_prop_name (property))) { property_p = ecma_op_to_string (property); ecma_fast_free_value (property); if (JERRY_UNLIKELY (property_p == NULL)) { ecma_free_value (base); return ECMA_VALUE_ERROR; } } else { property_p = ecma_get_prop_name_from_value (property); } ecma_value_t object = ecma_op_to_object (base); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (object)); object_p = ecma_get_object_from_value (object); ecma_op_ordinary_object_prevent_extensions (object_p); result = ecma_op_object_put_with_receiver (object_p, property_p, value, base, is_strict); ecma_free_value (base); } else { object_p = ecma_get_object_from_value (base); if (JERRY_UNLIKELY (!ecma_is_value_prop_name (property))) { property_p = ecma_op_to_string (property); ecma_fast_free_value (property); if (JERRY_UNLIKELY (property_p == NULL)) { ecma_deref_object (object_p); return ECMA_VALUE_ERROR; } } else { property_p = ecma_get_prop_name_from_value (property); } if (!ecma_is_lexical_environment (object_p)) { result = ecma_op_object_put_with_receiver (object_p, property_p, value, base, is_strict); } else { result = ecma_op_set_mutable_binding (object_p, property_p, value, is_strict); } } ecma_deref_object (object_p); ecma_deref_ecma_string (property_p); return result; } /* vm_op_set_value */ /** Compact bytecode define */ #define CBC_OPCODE(arg1, arg2, arg3, arg4) arg4, /** * Decode table for both opcodes and extended opcodes. */ static const uint16_t vm_decode_table[] JERRY_ATTR_CONST_DATA = { CBC_OPCODE_LIST CBC_EXT_OPCODE_LIST }; #undef CBC_OPCODE /** * Run global code * * Note: * returned value must be freed with ecma_free_value, when it is no longer needed. * * @return ecma value */ ecma_value_t vm_run_global (const ecma_compiled_code_t *bytecode_p, /**< pointer to bytecode to run */ ecma_object_t *function_object_p) /**< function object if available */ { #if JERRY_BUILTIN_REALMS ecma_object_t *global_obj_p = (ecma_object_t *) ecma_op_function_get_realm (bytecode_p); #else /* !JERRY_BUILTIN_REALMS */ ecma_object_t *global_obj_p = ecma_builtin_get_global (); #endif /* JERRY_BUILTIN_REALMS */ if (bytecode_p->status_flags & CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED) { ecma_create_global_lexical_block (global_obj_p); } ecma_object_t *const global_scope_p = ecma_get_global_scope (global_obj_p); vm_frame_ctx_shared_t shared; shared.bytecode_header_p = bytecode_p; shared.function_object_p = function_object_p; shared.status_flags = 0; #if JERRY_BUILTIN_REALMS ecma_value_t this_binding = ((ecma_global_object_t *) global_obj_p)->this_binding; ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); JERRY_CONTEXT (global_object_p) = (ecma_global_object_t *) global_obj_p; #else /* !JERRY_BUILTIN_REALMS */ ecma_value_t this_binding = ecma_make_object_value (global_obj_p); #endif /* JERRY_BUILTIN_REALMS */ ecma_value_t result = vm_run (&shared, this_binding, global_scope_p); #if JERRY_BUILTIN_REALMS JERRY_CONTEXT (global_object_p) = saved_global_object_p; #endif /* JERRY_BUILTIN_REALMS */ return result; } /* vm_run_global */ /** * Run specified eval-mode bytecode * * @return ecma value */ ecma_value_t vm_run_eval (ecma_compiled_code_t *bytecode_data_p, /**< byte-code data */ uint32_t parse_opts) /**< ecma_parse_opts_t option bits */ { ecma_value_t this_binding; ecma_object_t *lex_env_p; /* ECMA-262 v5, 10.4.2 */ if (parse_opts & ECMA_PARSE_DIRECT_EVAL) { this_binding = ecma_copy_value (JERRY_CONTEXT (vm_top_context_p)->this_binding); lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p; } else { #if JERRY_BUILTIN_REALMS ecma_object_t *global_obj_p = (ecma_object_t *) ecma_op_function_get_realm (bytecode_data_p); this_binding = ((ecma_global_object_t *) global_obj_p)->this_binding; ecma_ref_object (ecma_get_object_from_value (this_binding)); #else /* !JERRY_BUILTIN_REALMS */ ecma_object_t *global_obj_p = ecma_builtin_get_global (); ecma_ref_object (global_obj_p); this_binding = ecma_make_object_value (global_obj_p); #endif /* JERRY_BUILTIN_REALMS */ lex_env_p = ecma_get_global_scope (global_obj_p); } ecma_ref_object (lex_env_p); if ((bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0) { ecma_object_t *strict_lex_env_p = ecma_create_decl_lex_env (lex_env_p); ecma_deref_object (lex_env_p); lex_env_p = strict_lex_env_p; } if ((bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED) != 0) { ecma_object_t *lex_block_p = ecma_create_decl_lex_env (lex_env_p); lex_block_p->type_flags_refs |= ECMA_OBJECT_FLAG_BLOCK; ecma_deref_object (lex_env_p); lex_env_p = lex_block_p; } vm_frame_ctx_shared_t shared; shared.bytecode_header_p = bytecode_data_p; shared.function_object_p = NULL; shared.status_flags = (parse_opts & ECMA_PARSE_DIRECT_EVAL) ? VM_FRAME_CTX_SHARED_DIRECT_EVAL : 0; ecma_value_t completion_value = vm_run (&shared, this_binding, lex_env_p); ecma_deref_object (lex_env_p); ecma_free_value (this_binding); #if JERRY_SNAPSHOT_EXEC if (!(bytecode_data_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) { ecma_bytecode_deref (bytecode_data_p); } #else /* !JERRY_SNAPSHOT_EXEC */ ecma_bytecode_deref (bytecode_data_p); #endif /* JERRY_SNAPSHOT_EXEC */ return completion_value; } /* vm_run_eval */ #if JERRY_MODULE_SYSTEM /** * Run module code * * Note: * returned value must be freed with ecma_free_value, when it is no longer needed. * * @return ecma value */ ecma_value_t vm_run_module (ecma_module_t *module_p) /**< module to be executed */ { const ecma_value_t module_init_result = ecma_module_initialize (module_p); if (ECMA_IS_VALUE_ERROR (module_init_result)) { return module_init_result; } vm_frame_ctx_shared_t shared; shared.bytecode_header_p = module_p->u.compiled_code_p; shared.function_object_p = &module_p->header.object; shared.status_flags = 0; return vm_run (&shared, ECMA_VALUE_UNDEFINED, module_p->scope_p); } /* vm_run_module */ #endif /* JERRY_MODULE_SYSTEM */ /** * Construct object * * @return object value */ static ecma_value_t vm_construct_literal_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t lit_value) /**< literal */ { ecma_compiled_code_t *bytecode_p; #if JERRY_SNAPSHOT_EXEC if (JERRY_LIKELY (!(frame_ctx_p->shared_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION))) { #endif /* JERRY_SNAPSHOT_EXEC */ bytecode_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, lit_value); #if JERRY_SNAPSHOT_EXEC } else { uint8_t *byte_p = ((uint8_t *) frame_ctx_p->shared_p->bytecode_header_p) + lit_value; bytecode_p = (ecma_compiled_code_t *) byte_p; } #endif /* JERRY_SNAPSHOT_EXEC */ #if JERRY_BUILTIN_REGEXP if (JERRY_UNLIKELY (!CBC_IS_FUNCTION (bytecode_p->status_flags))) { ecma_object_t *regexp_obj_p = ecma_op_regexp_alloc (NULL); if (JERRY_UNLIKELY (regexp_obj_p == NULL)) { return ECMA_VALUE_ERROR; } return ecma_op_create_regexp_from_bytecode (regexp_obj_p, (re_compiled_code_t *) bytecode_p); } #else /* !JERRY_BUILTIN_REGEXP */ JERRY_ASSERT (CBC_IS_FUNCTION (bytecode_p->status_flags)); #endif /* JERRY_BUILTIN_REGEXP */ ecma_object_t *func_obj_p; if (JERRY_UNLIKELY (CBC_FUNCTION_IS_ARROW (bytecode_p->status_flags))) { func_obj_p = ecma_op_create_arrow_function_object (frame_ctx_p->lex_env_p, bytecode_p, frame_ctx_p->this_binding); } else { func_obj_p = ecma_op_create_any_function_object (frame_ctx_p->lex_env_p, bytecode_p); } return ecma_make_object_value (func_obj_p); } /* vm_construct_literal_object */ /** * Get implicit this value * * @return true - if the implicit 'this' value is updated, * false - otherwise */ static inline bool vm_get_implicit_this_value (ecma_value_t *this_value_p) /**< [in,out] this value */ { if (ecma_is_value_object (*this_value_p)) { ecma_object_t *this_obj_p = ecma_get_object_from_value (*this_value_p); if (ecma_is_lexical_environment (this_obj_p)) { ecma_value_t completion_value = ecma_op_implicit_this_value (this_obj_p); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (completion_value)); *this_value_p = completion_value; return true; } } return false; } /* vm_get_implicit_this_value */ /** * Special bytecode sequence for error handling while the vm_loop * is preserved for an execute operation */ static const uint8_t vm_error_byte_code_p[] = { CBC_EXT_OPCODE, CBC_EXT_ERROR }; /** * Get class function object * * @return the pointer to the object */ static ecma_object_t * vm_get_class_function (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { JERRY_ASSERT (frame_ctx_p != NULL); if (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_NON_ARROW_FUNC) { return frame_ctx_p->shared_p->function_object_p; } ecma_environment_record_t *environment_record_p = ecma_op_get_environment_record (frame_ctx_p->lex_env_p); JERRY_ASSERT (environment_record_p != NULL); return ecma_get_object_from_value (environment_record_p->function_object); } /* vm_get_class_function */ /** * 'super(...)' function call handler. */ static void vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { JERRY_ASSERT (frame_ctx_p->call_operation == VM_EXEC_SUPER_CALL); JERRY_ASSERT (frame_ctx_p->byte_code_p[0] == CBC_EXT_OPCODE); const uint8_t *byte_code_p = frame_ctx_p->byte_code_p + 3; uint8_t opcode = byte_code_p[-2]; uint32_t arguments_list_len; bool spread_arguments = opcode >= CBC_EXT_SPREAD_SUPER_CALL; ecma_collection_t *collection_p = NULL; ecma_value_t *arguments_p; if (spread_arguments) { ecma_value_t collection = *(--frame_ctx_p->stack_top_p); collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, collection); arguments_p = collection_p->buffer_p; arguments_list_len = collection_p->item_count; } else { arguments_list_len = byte_code_p[-1]; arguments_p = frame_ctx_p->stack_top_p; } ecma_value_t func_value = *(--frame_ctx_p->stack_top_p); ecma_value_t completion_value; ecma_environment_record_t *environment_record_p = ecma_op_get_environment_record (frame_ctx_p->lex_env_p); JERRY_ASSERT (environment_record_p); if (!ecma_is_constructor (func_value)) { completion_value = ecma_raise_type_error (ECMA_ERR_VALUE_FOR_CLASS_HERITAGE_IS_NOT_A_CONSTRUCTOR); } else { ecma_object_t *func_obj_p = ecma_get_object_from_value (func_value); completion_value = ecma_op_function_construct (func_obj_p, JERRY_CONTEXT (current_new_target_p), arguments_p, arguments_list_len); if (!ECMA_IS_VALUE_ERROR (completion_value) && ecma_op_this_binding_is_initialized (environment_record_p)) { ecma_free_value (completion_value); completion_value = ecma_raise_reference_error (ECMA_ERR_SUPER_CONSTRUCTOR_MAY_ONLY_BE_CALLED_ONCE); } } /* Free registers. */ for (uint32_t i = 0; i < arguments_list_len; i++) { ecma_fast_free_value (arguments_p[i]); } if (collection_p != NULL) { ecma_collection_destroy (collection_p); } if (ecma_is_value_object (completion_value)) { ecma_op_bind_this_value (environment_record_p, completion_value); frame_ctx_p->this_binding = completion_value; ecma_value_t fields_value = opfunc_init_class_fields (vm_get_class_function (frame_ctx_p), completion_value); if (ECMA_IS_VALUE_ERROR (fields_value)) { ecma_free_value (completion_value); completion_value = ECMA_VALUE_ERROR; } } ecma_free_value (func_value); if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (completion_value))) { frame_ctx_p->byte_code_p = (uint8_t *) vm_error_byte_code_p; } else { frame_ctx_p->byte_code_p = byte_code_p; uint32_t opcode_data = vm_decode_table[(CBC_END + 1) + opcode]; if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) { ecma_fast_free_value (completion_value); } else if (opcode_data & VM_OC_PUT_STACK) { *frame_ctx_p->stack_top_p++ = completion_value; } else { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); VM_GET_REGISTERS (frame_ctx_p)[0] = completion_value; } } } /* vm_super_call */ /** * Perform one of the following call/construct operation with spread argument list * - f(...args) * - o.f(...args) * - new O(...args) */ static void vm_spread_operation (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { JERRY_ASSERT (frame_ctx_p->byte_code_p[0] == CBC_EXT_OPCODE); uint8_t opcode = frame_ctx_p->byte_code_p[1]; ecma_value_t completion_value; ecma_value_t collection = *(--frame_ctx_p->stack_top_p); ecma_collection_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, collection); ecma_value_t func_value = *(--frame_ctx_p->stack_top_p); bool is_call_prop = opcode >= CBC_EXT_SPREAD_CALL_PROP; if (frame_ctx_p->byte_code_p[1] == CBC_EXT_SPREAD_NEW) { ecma_error_msg_t constructor_message_id = ecma_check_constructor (func_value); if (constructor_message_id != ECMA_IS_VALID_CONSTRUCTOR) { completion_value = ecma_raise_type_error (constructor_message_id); } else { ecma_object_t *constructor_obj_p = ecma_get_object_from_value (func_value); completion_value = ecma_op_function_construct (constructor_obj_p, constructor_obj_p, collection_p->buffer_p, collection_p->item_count); } } else { ecma_value_t this_value = is_call_prop ? frame_ctx_p->stack_top_p[-2] : ECMA_VALUE_UNDEFINED; if (!ecma_is_value_object (func_value) || !ecma_op_object_is_callable (ecma_get_object_from_value (func_value))) { completion_value = ecma_raise_type_error (ECMA_ERR_EXPECTED_A_FUNCTION); } else { ecma_object_t *func_obj_p = ecma_get_object_from_value (func_value); completion_value = ecma_op_function_call (func_obj_p, this_value, collection_p->buffer_p, collection_p->item_count); } if (is_call_prop) { ecma_free_value (*(--frame_ctx_p->stack_top_p)); ecma_free_value (*(--frame_ctx_p->stack_top_p)); } } ecma_collection_free (collection_p); ecma_free_value (func_value); if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (completion_value))) { frame_ctx_p->byte_code_p = (uint8_t *) vm_error_byte_code_p; } else { uint32_t opcode_data = vm_decode_table[(CBC_END + 1) + opcode]; if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) { ecma_fast_free_value (completion_value); } else if (opcode_data & VM_OC_PUT_STACK) { *frame_ctx_p->stack_top_p++ = completion_value; } else { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); VM_GET_REGISTERS (frame_ctx_p)[0] = completion_value; } /* EXT_OPCODE, SPREAD_OPCODE, BYTE_ARG */ frame_ctx_p->byte_code_p += 3; } } /* vm_spread_operation */ /** * 'Function call' opcode handler. * * See also: ECMA-262 v5, 11.2.3 */ static void opfunc_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { const uint8_t *byte_code_p = frame_ctx_p->byte_code_p + 1; uint8_t opcode = byte_code_p[-1]; uint32_t arguments_list_len; if (opcode >= CBC_CALL0) { arguments_list_len = (unsigned int) ((opcode - CBC_CALL0) / 6); } else { arguments_list_len = *byte_code_p++; } bool is_call_prop = ((opcode - CBC_CALL) % 6) >= 3; ecma_value_t *stack_top_p = frame_ctx_p->stack_top_p - arguments_list_len; ecma_value_t this_value = is_call_prop ? stack_top_p[-3] : ECMA_VALUE_UNDEFINED; ecma_value_t func_value = stack_top_p[-1]; ecma_value_t completion_value = ecma_op_function_validated_call (func_value, this_value, stack_top_p, arguments_list_len); JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_DIRECT_EVAL; /* Free registers. */ for (uint32_t i = 0; i < arguments_list_len; i++) { ecma_fast_free_value (stack_top_p[i]); } if (is_call_prop) { ecma_free_value (*(--stack_top_p)); ecma_free_value (*(--stack_top_p)); } if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (completion_value))) { frame_ctx_p->byte_code_p = (uint8_t *) vm_error_byte_code_p; } else { frame_ctx_p->byte_code_p = byte_code_p; ecma_free_value (*(--stack_top_p)); uint32_t opcode_data = vm_decode_table[opcode]; if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) { ecma_fast_free_value (completion_value); } else if (opcode_data & VM_OC_PUT_STACK) { *stack_top_p++ = completion_value; } else { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); VM_GET_REGISTERS (frame_ctx_p)[0] = completion_value; } } frame_ctx_p->stack_top_p = stack_top_p; } /* opfunc_call */ /** * 'Constructor call' opcode handler. * * See also: ECMA-262 v5, 11.2.2 */ static void opfunc_construct (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { const uint8_t *byte_code_p = frame_ctx_p->byte_code_p + 1; uint8_t opcode = byte_code_p[-1]; unsigned int arguments_list_len; if (opcode >= CBC_NEW0) { arguments_list_len = (unsigned int) (opcode - CBC_NEW0); } else { arguments_list_len = *byte_code_p++; } ecma_value_t *stack_top_p = frame_ctx_p->stack_top_p - arguments_list_len; ecma_value_t constructor_value = stack_top_p[-1]; ecma_value_t completion_value; ecma_error_msg_t constructor_message_id = ecma_check_constructor (constructor_value); if (constructor_message_id != ECMA_IS_VALID_CONSTRUCTOR) { completion_value = ecma_raise_type_error (constructor_message_id); } else { ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor_value); completion_value = ecma_op_function_construct (constructor_obj_p, constructor_obj_p, stack_top_p, arguments_list_len); } /* Free registers. */ for (uint32_t i = 0; i < arguments_list_len; i++) { ecma_fast_free_value (stack_top_p[i]); } if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (completion_value))) { frame_ctx_p->byte_code_p = (uint8_t *) vm_error_byte_code_p; } else { ecma_free_value (stack_top_p[-1]); frame_ctx_p->byte_code_p = byte_code_p; stack_top_p[-1] = completion_value; } frame_ctx_p->stack_top_p = stack_top_p; } /* opfunc_construct */ /** * Read literal index from the byte code stream into destination. * * @param destination destination */ #define READ_LITERAL_INDEX(destination) \ do \ { \ (destination) = *byte_code_p++; \ if ((destination) >= encoding_limit) \ { \ (destination) = (uint16_t) ((((destination) << 8) | *byte_code_p++) - encoding_delta); \ } \ } while (0) /** * Get literal value by literal index. * * @param literal_index literal index * @param target_value target value * * TODO: For performance reasons, we define this as a macro. * When we are able to construct a function with similar speed, * we can remove this macro. */ #define READ_LITERAL(literal_index, target_value) \ do \ { \ if ((literal_index) < ident_end) \ { \ if ((literal_index) < register_end) \ { \ /* Note: There should be no specialization for arguments. */ \ (target_value) = ecma_fast_copy_value (VM_GET_REGISTER (frame_ctx_p, literal_index)); \ } \ else \ { \ ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); \ \ result = ecma_op_resolve_reference_value (frame_ctx_p->lex_env_p, name_p); \ \ if (ECMA_IS_VALUE_ERROR (result)) \ { \ goto error; \ } \ (target_value) = result; \ } \ } \ else if (literal_index < const_literal_end) \ { \ (target_value) = ecma_fast_copy_value (literal_start_p[literal_index]); \ } \ else \ { \ /* Object construction. */ \ (target_value) = vm_construct_literal_object (frame_ctx_p, literal_start_p[literal_index]); \ } \ } while (0) /** * Store the original value for post increase/decrease operators * * @param value original value */ #define POST_INCREASE_DECREASE_PUT_RESULT(value) \ if (opcode_data & VM_OC_PUT_STACK) \ { \ if (opcode_flags & VM_OC_IDENT_INCR_DECR_OPERATOR_FLAG) \ { \ JERRY_ASSERT (opcode == CBC_POST_INCR_IDENT_PUSH_RESULT || opcode == CBC_POST_DECR_IDENT_PUSH_RESULT); \ *stack_top_p++ = (value); \ } \ else \ { \ /* The parser ensures there is enough space for the \ * extra value on the stack. See js-parser-expr.c. */ \ JERRY_ASSERT (opcode == CBC_POST_INCR_PUSH_RESULT || opcode == CBC_POST_DECR_PUSH_RESULT); \ stack_top_p++; \ stack_top_p[-1] = stack_top_p[-2]; \ stack_top_p[-2] = stack_top_p[-3]; \ stack_top_p[-3] = (value); \ } \ opcode_data &= (uint32_t) ~VM_OC_PUT_STACK; \ } \ else \ { \ JERRY_ASSERT (opcode_data &VM_OC_PUT_BLOCK); \ ecma_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); \ VM_GET_REGISTERS (frame_ctx_p)[0] = (value); \ opcode_data &= (uint32_t) ~VM_OC_PUT_BLOCK; \ } /** * Get the end of the existing topmost context */ #define VM_LAST_CONTEXT_END() (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth) /** * Run generic byte code. * * @return ecma value */ static ecma_value_t JERRY_ATTR_NOINLINE vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->shared_p->bytecode_header_p; const uint8_t *byte_code_p = frame_ctx_p->byte_code_p; ecma_value_t *literal_start_p = frame_ctx_p->literal_start_p; ecma_value_t *stack_top_p; uint16_t encoding_limit; uint16_t encoding_delta; uint16_t register_end; uint16_t ident_end; uint16_t const_literal_end; int32_t branch_offset = 0; uint8_t branch_offset_length = 0; ecma_value_t left_value; ecma_value_t right_value; ecma_value_t result = ECMA_VALUE_EMPTY; bool is_strict = ((bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0); /* Prepare for byte code execution. */ if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING)) { encoding_limit = CBC_SMALL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_SMALL_LITERAL_ENCODING_DELTA; } else { encoding_limit = CBC_FULL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_FULL_LITERAL_ENCODING_DELTA; } if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) (bytecode_header_p); register_end = args_p->register_end; ident_end = args_p->ident_end; const_literal_end = args_p->const_literal_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) (bytecode_header_p); register_end = args_p->register_end; ident_end = args_p->ident_end; const_literal_end = args_p->const_literal_end; } stack_top_p = frame_ctx_p->stack_top_p; /* Outer loop for exception handling. */ while (true) { /* Internal loop for byte code execution. */ while (true) { const uint8_t *byte_code_start_p = byte_code_p; uint8_t opcode = *byte_code_p++; uint32_t opcode_data = opcode; if (opcode == CBC_EXT_OPCODE) { opcode = *byte_code_p++; opcode_data = (uint32_t) ((CBC_END + 1) + opcode); } opcode_data = vm_decode_table[opcode_data]; left_value = ECMA_VALUE_UNDEFINED; right_value = ECMA_VALUE_UNDEFINED; uint32_t operands = VM_OC_GET_ARGS_INDEX (opcode_data); if (operands >= VM_OC_GET_LITERAL) { uint16_t literal_index; READ_LITERAL_INDEX (literal_index); READ_LITERAL (literal_index, left_value); if (operands != VM_OC_GET_LITERAL) { switch (operands) { case VM_OC_GET_LITERAL_LITERAL: { uint16_t second_literal_index; READ_LITERAL_INDEX (second_literal_index); READ_LITERAL (second_literal_index, right_value); break; } case VM_OC_GET_STACK_LITERAL: { JERRY_ASSERT (stack_top_p > VM_GET_REGISTERS (frame_ctx_p) + register_end); right_value = left_value; left_value = *(--stack_top_p); break; } default: { JERRY_ASSERT (operands == VM_OC_GET_THIS_LITERAL); right_value = left_value; left_value = ecma_copy_value (frame_ctx_p->this_binding); break; } } } } else if (operands >= VM_OC_GET_STACK) { JERRY_ASSERT (operands == VM_OC_GET_STACK || operands == VM_OC_GET_STACK_STACK); JERRY_ASSERT (stack_top_p > VM_GET_REGISTERS (frame_ctx_p) + register_end); left_value = *(--stack_top_p); if (operands == VM_OC_GET_STACK_STACK) { JERRY_ASSERT (stack_top_p > VM_GET_REGISTERS (frame_ctx_p) + register_end); right_value = left_value; left_value = *(--stack_top_p); } } else if (operands == VM_OC_GET_BRANCH) { branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (opcode); JERRY_ASSERT (branch_offset_length >= 1 && branch_offset_length <= 3); branch_offset = *(byte_code_p++); if (JERRY_UNLIKELY (branch_offset_length != 1)) { branch_offset <<= 8; branch_offset |= *(byte_code_p++); if (JERRY_UNLIKELY (branch_offset_length == 3)) { branch_offset <<= 8; branch_offset |= *(byte_code_p++); } } if (opcode_data & VM_OC_BACKWARD_BRANCH) { #if JERRY_VM_HALT if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL && --JERRY_CONTEXT (vm_exec_stop_counter) == 0) { result = JERRY_CONTEXT (vm_exec_stop_cb) (JERRY_CONTEXT (vm_exec_stop_user_p)); if (ecma_is_value_undefined (result)) { JERRY_CONTEXT (vm_exec_stop_counter) = JERRY_CONTEXT (vm_exec_stop_frequency); } else { JERRY_CONTEXT (vm_exec_stop_counter) = 1; if (ecma_is_value_exception (result)) { ecma_throw_exception (result); } else { jcontext_raise_exception (result); } JERRY_ASSERT (jcontext_has_pending_exception ()); jcontext_set_abort_flag (true); result = ECMA_VALUE_ERROR; goto error; } } #endif /* JERRY_VM_HALT */ branch_offset = -branch_offset; } } switch (VM_OC_GROUP_GET_INDEX (opcode_data)) { case VM_OC_POP: { JERRY_ASSERT (stack_top_p > VM_GET_REGISTERS (frame_ctx_p) + register_end); ecma_free_value (*(--stack_top_p)); continue; } case VM_OC_POP_BLOCK: { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); VM_GET_REGISTERS (frame_ctx_p)[0] = *(--stack_top_p); continue; } case VM_OC_PUSH: { *stack_top_p++ = left_value; continue; } case VM_OC_PUSH_TWO: { *stack_top_p++ = left_value; *stack_top_p++ = right_value; continue; } case VM_OC_PUSH_THREE: { uint16_t literal_index; *stack_top_p++ = left_value; left_value = ECMA_VALUE_UNDEFINED; READ_LITERAL_INDEX (literal_index); READ_LITERAL (literal_index, left_value); *stack_top_p++ = right_value; *stack_top_p++ = left_value; continue; } case VM_OC_PUSH_UNDEFINED: { *stack_top_p++ = ECMA_VALUE_UNDEFINED; continue; } case VM_OC_PUSH_TRUE: { *stack_top_p++ = ECMA_VALUE_TRUE; continue; } case VM_OC_PUSH_FALSE: { *stack_top_p++ = ECMA_VALUE_FALSE; continue; } case VM_OC_PUSH_NULL: { *stack_top_p++ = ECMA_VALUE_NULL; continue; } case VM_OC_PUSH_THIS: { *stack_top_p++ = ecma_copy_value (frame_ctx_p->this_binding); continue; } case VM_OC_PUSH_0: { *stack_top_p++ = ecma_make_integer_value (0); continue; } case VM_OC_PUSH_POS_BYTE: { ecma_integer_value_t number = *byte_code_p++; *stack_top_p++ = ecma_make_integer_value (number + 1); continue; } case VM_OC_PUSH_NEG_BYTE: { ecma_integer_value_t number = *byte_code_p++; *stack_top_p++ = ecma_make_integer_value (-(number + 1)); continue; } case VM_OC_PUSH_LIT_0: { stack_top_p[0] = left_value; stack_top_p[1] = ecma_make_integer_value (0); stack_top_p += 2; continue; } case VM_OC_PUSH_LIT_POS_BYTE: { ecma_integer_value_t number = *byte_code_p++; stack_top_p[0] = left_value; stack_top_p[1] = ecma_make_integer_value (number + 1); stack_top_p += 2; continue; } case VM_OC_PUSH_LIT_NEG_BYTE: { ecma_integer_value_t number = *byte_code_p++; stack_top_p[0] = left_value; stack_top_p[1] = ecma_make_integer_value (-(number + 1)); stack_top_p += 2; continue; } case VM_OC_PUSH_OBJECT: { ecma_object_t *obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE), 0, ECMA_OBJECT_TYPE_GENERAL); *stack_top_p++ = ecma_make_object_value (obj_p); continue; } case VM_OC_PUSH_NAMED_FUNC_EXPR: { ecma_object_t *func_p = ecma_get_object_from_value (left_value); JERRY_ASSERT (ecma_get_object_type (func_p) == ECMA_OBJECT_TYPE_FUNCTION); ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) func_p; JERRY_ASSERT (frame_ctx_p->lex_env_p == ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_func_p->u.function.scope_cp)); ecma_object_t *name_lex_env = ecma_create_decl_lex_env (frame_ctx_p->lex_env_p); ecma_op_create_immutable_binding (name_lex_env, ecma_get_string_from_value (right_value), left_value); ECMA_SET_NON_NULL_POINTER_TAG (ext_func_p->u.function.scope_cp, name_lex_env, 0); ecma_free_value (right_value); ecma_deref_object (name_lex_env); *stack_top_p++ = left_value; continue; } case VM_OC_CREATE_BINDING: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); JERRY_ASSERT (ecma_find_named_property (frame_ctx_p->lex_env_p, name_p) == NULL); uint8_t prop_attributes = ECMA_PROPERTY_FLAG_WRITABLE; if (opcode == CBC_CREATE_LET) { prop_attributes = ECMA_PROPERTY_ENUMERABLE_WRITABLE; } else if (opcode == CBC_CREATE_CONST) { prop_attributes = ECMA_PROPERTY_FLAG_ENUMERABLE; } ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (frame_ctx_p->lex_env_p, name_p, prop_attributes, NULL); if (opcode != CBC_CREATE_VAR) { property_value_p->value = ECMA_VALUE_UNINITIALIZED; } continue; } case VM_OC_VAR_EVAL: { uint32_t literal_index; ecma_value_t lit_value = ECMA_VALUE_UNDEFINED; if (opcode == CBC_CREATE_VAR_FUNC_EVAL) { uint32_t value_index; READ_LITERAL_INDEX (value_index); JERRY_ASSERT (value_index >= const_literal_end); lit_value = vm_construct_literal_object (frame_ctx_p, literal_start_p[value_index]); } READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (literal_index >= register_end); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) { #if !(defined JERRY_NDEBUG) if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); JERRY_ASSERT (property_p == NULL || !(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); } #endif /* !JERRY_NDEBUG */ JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } #if !(defined JERRY_NDEBUG) if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); JERRY_ASSERT (property_p == NULL || !(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); } #endif /* !JERRY_NDEBUG */ /* 'Variable declaration' */ result = ecma_op_has_binding (lex_env_p, name_p); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (result)) { goto error; } #endif /* JERRY_BUILTIN_PROXY */ ecma_property_t *prop_p = NULL; if (ecma_is_value_false (result)) { bool is_configurable = (frame_ctx_p->status_flags & VM_FRAME_CTX_DIRECT_EVAL) != 0; prop_p = ecma_op_create_mutable_binding (lex_env_p, name_p, is_configurable); if (JERRY_UNLIKELY (prop_p == ECMA_PROPERTY_POINTER_ERROR)) { result = ECMA_VALUE_ERROR; goto error; } } if (lit_value != ECMA_VALUE_UNDEFINED) { JERRY_ASSERT (ecma_is_value_object (lit_value)); if (prop_p != NULL) { JERRY_ASSERT (ecma_is_value_undefined (ECMA_PROPERTY_VALUE_PTR (prop_p)->value)); JERRY_ASSERT (ecma_is_property_writable (*prop_p)); ECMA_PROPERTY_VALUE_PTR (prop_p)->value = lit_value; ecma_free_object (lit_value); } else { result = ecma_op_put_value_lex_env_base (lex_env_p, name_p, is_strict, lit_value); ecma_free_object (lit_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } } } continue; } case VM_OC_EXT_VAR_EVAL: { uint32_t literal_index; ecma_value_t lit_value = ECMA_VALUE_UNDEFINED; JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE); if (opcode == CBC_EXT_CREATE_VAR_FUNC_EVAL) { uint32_t value_index; READ_LITERAL_INDEX (value_index); JERRY_ASSERT (value_index >= const_literal_end); lit_value = vm_construct_literal_object (frame_ctx_p, literal_start_p[value_index]); } READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (literal_index >= register_end); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; ecma_object_t *prev_lex_env_p = NULL; while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) { #if !(defined JERRY_NDEBUG) if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); JERRY_ASSERT (property_p == NULL || !(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); } #endif /* !JERRY_NDEBUG */ JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); prev_lex_env_p = lex_env_p; lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); JERRY_ASSERT (prev_lex_env_p != NULL && ecma_get_lex_env_type (prev_lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); ecma_property_t *property_p = ecma_find_named_property (prev_lex_env_p, name_p); ecma_property_value_t *property_value_p; if (property_p == NULL) { property_value_p = ecma_create_named_data_property (prev_lex_env_p, name_p, ECMA_PROPERTY_CONFIGURABLE_WRITABLE, NULL); if (lit_value == ECMA_VALUE_UNDEFINED) { continue; } } else { if (lit_value == ECMA_VALUE_UNDEFINED) { continue; } property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); ecma_free_value_if_not_object (property_value_p->value); } property_value_p->value = lit_value; ecma_deref_object (ecma_get_object_from_value (lit_value)); continue; } case VM_OC_CREATE_ARGUMENTS: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_ARG_LIST); result = ecma_op_create_arguments_object ((vm_frame_ctx_shared_args_t *) (frame_ctx_p->shared_p), frame_ctx_p->lex_env_p); if (literal_index < register_end) { JERRY_ASSERT (VM_GET_REGISTER (frame_ctx_p, literal_index) == ECMA_VALUE_UNDEFINED); VM_GET_REGISTER (frame_ctx_p, literal_index) = result; continue; } ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); JERRY_ASSERT (ecma_find_named_property (frame_ctx_p->lex_env_p, name_p) == NULL); uint8_t prop_attributes = ECMA_PROPERTY_FLAG_WRITABLE; ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (frame_ctx_p->lex_env_p, name_p, prop_attributes, NULL); property_value_p->value = result; ecma_deref_object (ecma_get_object_from_value (result)); continue; } #if JERRY_SNAPSHOT_EXEC case VM_OC_SET_BYTECODE_PTR: { memcpy (&byte_code_p, byte_code_p++, sizeof (uintptr_t)); frame_ctx_p->byte_code_start_p = byte_code_p; continue; } #endif /* JERRY_SNAPSHOT_EXEC */ case VM_OC_INIT_ARG_OR_FUNC: { uint32_t literal_index, value_index; ecma_value_t lit_value; bool release = false; READ_LITERAL_INDEX (value_index); if (value_index < register_end) { /* Take (not copy) the reference. */ lit_value = ecma_copy_value_if_not_object (VM_GET_REGISTER (frame_ctx_p, value_index)); } else { lit_value = vm_construct_literal_object (frame_ctx_p, literal_start_p[value_index]); release = true; } READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (value_index != literal_index); JERRY_ASSERT (value_index >= register_end || literal_index >= register_end); if (literal_index < register_end) { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, literal_index)); JERRY_ASSERT (release); VM_GET_REGISTER (frame_ctx_p, literal_index) = lit_value; continue; } ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); JERRY_ASSERT (ecma_find_named_property (frame_ctx_p->lex_env_p, name_p) == NULL); ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (frame_ctx_p->lex_env_p, name_p, ECMA_PROPERTY_FLAG_WRITABLE, NULL); JERRY_ASSERT (property_value_p->value == ECMA_VALUE_UNDEFINED); property_value_p->value = lit_value; if (release) { ecma_deref_object (ecma_get_object_from_value (lit_value)); } continue; } case VM_OC_CHECK_VAR: { JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (frame_ctx_p->shared_p->bytecode_header_p->status_flags) == CBC_FUNCTION_SCRIPT); uint32_t literal_index; READ_LITERAL_INDEX (literal_index); if ((frame_ctx_p->lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) == 0) { continue; } ecma_string_t *const literal_name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_property_t *const binding_p = ecma_find_named_property (frame_ctx_p->lex_env_p, literal_name_p); if (binding_p != NULL) { result = ecma_raise_syntax_error (ECMA_ERR_LOCAL_VARIABLE_IS_REDECLARED); goto error; } continue; } case VM_OC_CHECK_LET: { JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (frame_ctx_p->shared_p->bytecode_header_p->status_flags) == CBC_FUNCTION_SCRIPT); uint32_t literal_index; READ_LITERAL_INDEX (literal_index); ecma_string_t *literal_name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; if (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) { result = opfunc_lexical_scope_has_restricted_binding (frame_ctx_p, literal_name_p); if (!ecma_is_value_false (result)) { if (ecma_is_value_true (result)) { result = ecma_raise_syntax_error (ECMA_ERR_LOCAL_VARIABLE_IS_REDECLARED); } JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result)); goto error; } continue; } result = ecma_op_has_binding (lex_env_p, literal_name_p); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (result)) { goto error; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_true (result)) { result = ecma_raise_syntax_error (ECMA_ERR_LOCAL_VARIABLE_IS_REDECLARED); goto error; } continue; } case VM_OC_ASSIGN_LET_CONST: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (literal_index >= register_end); JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE || (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && ECMA_LEX_ENV_CLASS_IS_MODULE (frame_ctx_p->lex_env_p))); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_property_t *property_p = ecma_find_named_property (frame_ctx_p->lex_env_p, name_p); JERRY_ASSERT (property_p != NULL && ECMA_PROPERTY_IS_RAW_DATA (*property_p) && (*property_p & ECMA_PROPERTY_FLAG_DATA)); JERRY_ASSERT (ECMA_PROPERTY_VALUE_PTR (property_p)->value == ECMA_VALUE_UNINITIALIZED); ECMA_PROPERTY_VALUE_PTR (property_p)->value = left_value; if (ecma_is_value_object (left_value)) { ecma_deref_object (ecma_get_object_from_value (left_value)); } continue; } case VM_OC_INIT_BINDING: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (literal_index >= register_end); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); JERRY_ASSERT (ecma_find_named_property (frame_ctx_p->lex_env_p, name_p) == NULL); uint8_t prop_attributes = ECMA_PROPERTY_FLAG_WRITABLE; if (opcode == CBC_INIT_LET) { prop_attributes = ECMA_PROPERTY_ENUMERABLE_WRITABLE; } else if (opcode == CBC_INIT_CONST) { prop_attributes = ECMA_PROPERTY_FLAG_ENUMERABLE; } ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (frame_ctx_p->lex_env_p, name_p, prop_attributes, NULL); JERRY_ASSERT (property_value_p->value == ECMA_VALUE_UNDEFINED); ecma_value_t value = *(--stack_top_p); property_value_p->value = value; ecma_deref_if_object (value); continue; } case VM_OC_THROW_CONST_ERROR: { result = ecma_raise_type_error (ECMA_ERR_CONSTANT_BINDINGS_CANNOT_BE_REASSIGNED); goto error; } case VM_OC_COPY_TO_GLOBAL: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) { #ifndef JERRY_NDEBUG if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); JERRY_ASSERT (property_p == NULL || !(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); } #endif /* !JERRY_NDEBUG */ JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); ecma_property_value_t *prop_value_p; if (property_p == NULL) { prop_value_p = ecma_create_named_data_property (lex_env_p, name_p, ECMA_PROPERTY_FLAG_WRITABLE, NULL); } else { #ifndef JERRY_NDEBUG JERRY_ASSERT (!(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); #endif /* !JERRY_NDEBUG */ prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); } ecma_named_data_property_assign_value (lex_env_p, prop_value_p, left_value); } else { result = ecma_op_set_mutable_binding (lex_env_p, name_p, left_value, is_strict); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } } goto free_left_value; } case VM_OC_COPY_FROM_ARG: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (literal_index >= register_end); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; ecma_object_t *arg_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); JERRY_ASSERT ((lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) && ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); JERRY_ASSERT (arg_lex_env_p != NULL && !(arg_lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) && ecma_get_lex_env_type (arg_lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (lex_env_p, name_p, ECMA_PROPERTY_FLAG_WRITABLE, NULL); ecma_property_t *property_p = ecma_find_named_property (arg_lex_env_p, name_p); JERRY_ASSERT (property_p != NULL); ecma_property_value_t *arg_prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); property_value_p->value = ecma_copy_value_if_not_object (arg_prop_value_p->value); continue; } case VM_OC_CLONE_CONTEXT: { JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE); bool copy_values = (byte_code_start_p[1] == CBC_EXT_CLONE_FULL_CONTEXT); frame_ctx_p->lex_env_p = ecma_clone_decl_lexical_environment (frame_ctx_p->lex_env_p, copy_values); continue; } case VM_OC_SET__PROTO__: { result = ecma_builtin_object_object_set_proto (stack_top_p[-1], left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } goto free_left_value; } case VM_OC_CLASS_CALL_STATIC_BLOCK: { result = ecma_op_function_call (ecma_get_object_from_value (left_value), frame_ctx_p->this_binding, NULL, 0); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } goto free_left_value; } case VM_OC_PUSH_STATIC_FIELD_FUNC: { JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE && (byte_code_start_p[1] == CBC_EXT_PUSH_STATIC_FIELD_FUNC || byte_code_start_p[1] == CBC_EXT_PUSH_STATIC_COMPUTED_FIELD_FUNC)); bool push_computed = (byte_code_start_p[1] == CBC_EXT_PUSH_STATIC_COMPUTED_FIELD_FUNC); ecma_value_t value = stack_top_p[-1]; if (!push_computed) { stack_top_p++; } memmove (stack_top_p - 3, stack_top_p - 4, 3 * sizeof (ecma_value_t)); stack_top_p[-4] = left_value; ecma_object_t *class_object_p = ecma_get_object_from_value (stack_top_p[-2]); ecma_object_t *initializer_func_p = ecma_get_object_from_value (left_value); opfunc_bind_class_environment (frame_ctx_p->lex_env_p, class_object_p, class_object_p, initializer_func_p); if (!push_computed) { continue; } left_value = value; /* FALLTHRU */ } case VM_OC_ADD_COMPUTED_FIELD: { JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE && (byte_code_start_p[1] == CBC_EXT_PUSH_STATIC_COMPUTED_FIELD_FUNC || byte_code_start_p[1] == CBC_EXT_ADD_COMPUTED_FIELD || byte_code_start_p[1] == CBC_EXT_ADD_STATIC_COMPUTED_FIELD)); int index = (byte_code_start_p[1] == CBC_EXT_ADD_COMPUTED_FIELD) ? -2 : -4; result = opfunc_add_computed_field (stack_top_p[index], left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } goto free_left_value; } case VM_OC_COPY_DATA_PROPERTIES: { left_value = *(--stack_top_p); if (ecma_is_value_undefined (left_value) || ecma_is_value_null (left_value)) { continue; } result = opfunc_copy_data_properties (stack_top_p[-1], left_value, ECMA_VALUE_UNDEFINED); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } goto free_left_value; } case VM_OC_SET_COMPUTED_PROPERTY: { /* Swap values. */ left_value ^= right_value; right_value ^= left_value; left_value ^= right_value; /* FALLTHRU */ } case VM_OC_SET_PROPERTY: { JERRY_STATIC_ASSERT ((VM_OC_NON_STATIC_FLAG == VM_OC_BACKWARD_BRANCH), vm_oc_non_static_flag_must_be_equal_to_vm_oc_backward_branch); JERRY_ASSERT ((opcode_data >> VM_OC_NON_STATIC_SHIFT) <= 0x1); ecma_string_t *prop_name_p = ecma_op_to_property_key (right_value); if (JERRY_UNLIKELY (prop_name_p == NULL)) { result = ECMA_VALUE_ERROR; goto error; } if (JERRY_UNLIKELY (ecma_compare_ecma_string_to_magic_id (prop_name_p, LIT_MAGIC_STRING_PROTOTYPE)) && !(opcode_data & VM_OC_NON_STATIC_FLAG)) { result = ecma_raise_type_error (ECMA_ERR_CLASS_IS_NON_CONFIGURABLE); goto error; } const int index = (int) (opcode_data >> VM_OC_NON_STATIC_SHIFT) - 2; ecma_object_t *object_p = ecma_get_object_from_value (stack_top_p[index]); opfunc_set_data_property (object_p, prop_name_p, left_value); ecma_deref_ecma_string (prop_name_p); goto free_both_values; } case VM_OC_SET_GETTER: case VM_OC_SET_SETTER: { JERRY_ASSERT ((opcode_data >> VM_OC_NON_STATIC_SHIFT) <= 0x1); ecma_string_t *prop_name_p = ecma_op_to_property_key (left_value); if (JERRY_UNLIKELY (prop_name_p == NULL)) { result = ECMA_VALUE_ERROR; goto error; } if (JERRY_UNLIKELY (ecma_compare_ecma_string_to_magic_id (prop_name_p, LIT_MAGIC_STRING_PROTOTYPE)) && !(opcode_data & VM_OC_NON_STATIC_FLAG)) { result = ecma_raise_type_error (ECMA_ERR_CLASS_IS_NON_CONFIGURABLE); goto error; } const int index = (int) (opcode_data >> VM_OC_NON_STATIC_SHIFT) - 2; opfunc_set_accessor (VM_OC_GROUP_GET_INDEX (opcode_data) == VM_OC_SET_GETTER, stack_top_p[index], prop_name_p, right_value); ecma_deref_ecma_string (prop_name_p); goto free_both_values; } case VM_OC_PUSH_ARRAY: { /* Note: this operation cannot throw an exception */ *stack_top_p++ = ecma_make_object_value (ecma_op_new_array_object (0)); continue; } case VM_OC_LOCAL_EVAL: { ECMA_CLEAR_LOCAL_PARSE_OPTS (); uint8_t parse_opts = *byte_code_p++; ECMA_SET_LOCAL_PARSE_OPTS (parse_opts); continue; } case VM_OC_SUPER_CALL: { uint8_t arguments_list_len = *byte_code_p++; if (opcode >= CBC_EXT_SPREAD_SUPER_CALL) { stack_top_p -= arguments_list_len; ecma_collection_t *arguments_p = opfunc_spread_arguments (stack_top_p, arguments_list_len); if (JERRY_UNLIKELY (arguments_p == NULL)) { result = ECMA_VALUE_ERROR; goto error; } stack_top_p++; ECMA_SET_INTERNAL_VALUE_POINTER (stack_top_p[-1], arguments_p); } else { stack_top_p -= arguments_list_len; } frame_ctx_p->call_operation = VM_EXEC_SUPER_CALL; frame_ctx_p->byte_code_p = byte_code_start_p; frame_ctx_p->stack_top_p = stack_top_p; return ECMA_VALUE_UNDEFINED; } case VM_OC_PUSH_CLASS_ENVIRONMENT: { uint16_t literal_index; READ_LITERAL_INDEX (literal_index); opfunc_push_class_environment (frame_ctx_p, &stack_top_p, literal_start_p[literal_index]); continue; } case VM_OC_PUSH_IMPLICIT_CTOR: { *stack_top_p++ = opfunc_create_implicit_class_constructor (opcode, frame_ctx_p->shared_p->bytecode_header_p); continue; } case VM_OC_DEFINE_FIELD: { result = opfunc_define_field (frame_ctx_p->this_binding, right_value, left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } goto free_both_values; } case VM_OC_ASSIGN_PRIVATE: { result = opfunc_private_set (stack_top_p[-3], stack_top_p[-2], stack_top_p[-1]); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } ecma_free_value (stack_top_p[-3]); ecma_free_value (stack_top_p[-2]); ecma_free_value (stack_top_p[-1]); stack_top_p -= 3; if (opcode_data & VM_OC_PUT_STACK) { *stack_top_p++ = result; } else if (opcode_data & VM_OC_PUT_BLOCK) { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); VM_GET_REGISTERS (frame_ctx_p)[0] = result; } else { ecma_free_value (result); } goto free_both_values; } case VM_OC_PRIVATE_FIELD_ADD: { result = opfunc_private_field_add (frame_ctx_p->this_binding, right_value, left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } goto free_both_values; } case VM_OC_PRIVATE_PROP_GET: { result = opfunc_private_get (left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_PRIVATE_PROP_REFERENCE: { result = opfunc_private_get (stack_top_p[-1], left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = left_value; *stack_top_p++ = result; continue; } case VM_OC_PRIVATE_IN: { result = opfunc_private_in (left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_COLLECT_PRIVATE_PROPERTY: { opfunc_collect_private_properties (stack_top_p[-2], left_value, right_value, opcode); continue; } case VM_OC_INIT_CLASS: { result = opfunc_init_class (frame_ctx_p, stack_top_p); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } continue; } case VM_OC_FINALIZE_CLASS: { JERRY_ASSERT (opcode == CBC_EXT_FINALIZE_NAMED_CLASS || opcode == CBC_EXT_FINALIZE_ANONYMOUS_CLASS); if (opcode == CBC_EXT_FINALIZE_NAMED_CLASS) { uint16_t literal_index; READ_LITERAL_INDEX (literal_index); left_value = literal_start_p[literal_index]; } opfunc_finalize_class (frame_ctx_p, &stack_top_p, left_value); continue; } case VM_OC_SET_FIELD_INIT: { ecma_string_t *property_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_INIT); ecma_object_t *proto_object_p = ecma_get_object_from_value (stack_top_p[-1]); ecma_object_t *class_object_p = ecma_get_object_from_value (stack_top_p[-2]); ecma_object_t *initializer_func_p = ecma_get_object_from_value (left_value); opfunc_bind_class_environment (frame_ctx_p->lex_env_p, proto_object_p, class_object_p, initializer_func_p); ecma_property_value_t *property_value_p = ecma_create_named_data_property (class_object_p, property_name_p, ECMA_PROPERTY_FIXED, NULL); property_value_p->value = left_value; property_name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED); ecma_property_t *property_p = ecma_find_named_property (class_object_p, property_name_p); if (property_p != NULL) { property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); ecma_value_t *compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, property_value_p->value); compact_collection_p = ecma_compact_collection_shrink (compact_collection_p); ECMA_SET_INTERNAL_VALUE_POINTER (property_value_p->value, compact_collection_p); } goto free_left_value; } case VM_OC_RUN_FIELD_INIT: { JERRY_ASSERT (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_NON_ARROW_FUNC); result = opfunc_init_class_fields (frame_ctx_p->shared_p->function_object_p, frame_ctx_p->this_binding); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } continue; } case VM_OC_RUN_STATIC_FIELD_INIT: { left_value = stack_top_p[-2]; stack_top_p[-2] = stack_top_p[-1]; stack_top_p--; result = opfunc_init_static_class_fields (left_value, stack_top_p[-1]); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } goto free_left_value; } case VM_OC_SET_NEXT_COMPUTED_FIELD: { ecma_integer_value_t next_index = ecma_get_integer_from_value (stack_top_p[-2]) + 1; stack_top_p[-2] = ecma_make_integer_value (next_index); JERRY_ASSERT (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_CLASS_FIELDS); ecma_value_t *computed_class_fields_p = VM_GET_COMPUTED_CLASS_FIELDS (frame_ctx_p); JERRY_ASSERT ((ecma_value_t) next_index < ECMA_COMPACT_COLLECTION_GET_SIZE (computed_class_fields_p)); ecma_value_t prop_name = computed_class_fields_p[next_index]; if (opcode == CBC_EXT_SET_NEXT_COMPUTED_FIELD_ANONYMOUS_FUNC) { ecma_object_t *func_obj_p = ecma_get_object_from_value (stack_top_p[-1]); JERRY_ASSERT (ecma_find_named_property (func_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_NAME)) == NULL); ecma_property_value_t *value_p; value_p = ecma_create_named_data_property (func_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_NAME), ECMA_PROPERTY_FLAG_CONFIGURABLE, NULL); if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION) { ECMA_SET_SECOND_BIT_TO_POINTER_TAG (((ecma_extended_object_t *) func_obj_p)->u.function.scope_cp); } value_p->value = ecma_copy_value (prop_name); } result = opfunc_define_field (frame_ctx_p->this_binding, prop_name, stack_top_p[-1]); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } ecma_free_value (*(--stack_top_p)); continue; } case VM_OC_PUSH_SUPER_CONSTRUCTOR: { result = ecma_op_function_get_super_constructor (vm_get_class_function (frame_ctx_p)); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; continue; } case VM_OC_RESOLVE_LEXICAL_THIS: { result = ecma_op_get_this_binding (frame_ctx_p->lex_env_p); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; continue; } case VM_OC_OBJECT_LITERAL_HOME_ENV: { if (opcode == CBC_EXT_PUSH_OBJECT_SUPER_ENVIRONMENT) { ecma_value_t obj_value = stack_top_p[-1]; ecma_object_t *obj_env_p = ecma_create_lex_env_class (frame_ctx_p->lex_env_p, 0); ECMA_SET_NON_NULL_POINTER (obj_env_p->u1.bound_object_cp, ecma_get_object_from_value (obj_value)); stack_top_p[-1] = ecma_make_object_value (obj_env_p); *stack_top_p++ = obj_value; } else { JERRY_ASSERT (opcode == CBC_EXT_POP_OBJECT_SUPER_ENVIRONMENT); ecma_deref_object (ecma_get_object_from_value (stack_top_p[-2])); stack_top_p[-2] = stack_top_p[-1]; stack_top_p--; } continue; } case VM_OC_SET_HOME_OBJECT: { int offset = opcode == CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED ? -1 : 0; opfunc_set_home_object (ecma_get_object_from_value (stack_top_p[-1]), ecma_get_object_from_value (stack_top_p[-3 + offset])); continue; } case VM_OC_SUPER_REFERENCE: { result = opfunc_form_super_reference (&stack_top_p, frame_ctx_p, left_value, opcode); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } goto free_left_value; } case VM_OC_SET_FUNCTION_NAME: { const char *prefix_p = NULL; lit_utf8_size_t prefix_size = 0; if (opcode != CBC_EXT_SET_FUNCTION_NAME) { ecma_value_t prop_name_value; if (opcode == CBC_EXT_SET_CLASS_NAME) { uint16_t literal_index; READ_LITERAL_INDEX (literal_index); prop_name_value = literal_start_p[literal_index]; } else { prop_name_value = stack_top_p[-2]; } ecma_string_t *prop_name_p = ecma_op_to_property_key (prop_name_value); if (JERRY_UNLIKELY (prop_name_p == NULL)) { result = ECMA_VALUE_ERROR; goto error; } left_value = ecma_make_prop_name_value (prop_name_p); if (opcode != CBC_EXT_SET_CLASS_NAME) { ecma_ref_ecma_string (prop_name_p); ecma_free_value (stack_top_p[-2]); stack_top_p[-2] = left_value; } if (opcode == CBC_EXT_SET_COMPUTED_GETTER_NAME || opcode == CBC_EXT_SET_COMPUTED_SETTER_NAME) { prefix_p = (opcode == CBC_EXT_SET_COMPUTED_GETTER_NAME) ? "get " : "set "; prefix_size = 4; } } ecma_object_t *func_obj_p = ecma_get_object_from_value (stack_top_p[-1]); if (ecma_find_named_property (func_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_NAME)) != NULL) { ecma_free_value (left_value); continue; } ecma_property_value_t *value_p; value_p = ecma_create_named_data_property (func_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_NAME), ECMA_PROPERTY_FLAG_CONFIGURABLE, NULL); if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION) { ECMA_SET_SECOND_BIT_TO_POINTER_TAG (((ecma_extended_object_t *) func_obj_p)->u.function.scope_cp); } value_p->value = ecma_op_function_form_name (ecma_get_prop_name_from_value (left_value), (char*) prefix_p, prefix_size); ecma_free_value (left_value); continue; } case VM_OC_PUSH_SPREAD_ELEMENT: { *stack_top_p++ = ECMA_VALUE_SPREAD_ELEMENT; continue; } case VM_OC_PUSH_REST_OBJECT: { vm_frame_ctx_shared_t *shared_p = frame_ctx_p->shared_p; JERRY_ASSERT (shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_ARG_LIST); const ecma_value_t *arg_list_p = ((vm_frame_ctx_shared_args_t *) shared_p)->arg_list_p; uint32_t arg_list_len = ((vm_frame_ctx_shared_args_t *) shared_p)->arg_list_len; uint16_t argument_end; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { argument_end = ((cbc_uint16_arguments_t *) bytecode_header_p)->argument_end; } else { argument_end = ((cbc_uint8_arguments_t *) bytecode_header_p)->argument_end; } if (arg_list_len < argument_end) { arg_list_len = argument_end; } result = ecma_op_new_array_object_from_buffer (arg_list_p + argument_end, arg_list_len - argument_end); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (result)); *stack_top_p++ = result; continue; } case VM_OC_ITERATOR_CONTEXT_CREATE: { result = ecma_op_get_iterator (stack_top_p[-1], ECMA_VALUE_SYNC_ITERATOR, &left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } uint32_t context_size = (uint32_t) (stack_top_p + PARSER_ITERATOR_CONTEXT_STACK_ALLOCATION - VM_LAST_CONTEXT_END ()); stack_top_p += PARSER_ITERATOR_CONTEXT_STACK_ALLOCATION; VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, context_size); stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_ITERATOR, context_size) | VM_CONTEXT_CLOSE_ITERATOR; stack_top_p[-2] = result; stack_top_p[-3] = left_value; continue; } case VM_OC_ITERATOR_STEP: { ecma_value_t *last_context_end_p = VM_LAST_CONTEXT_END (); ecma_value_t iterator = last_context_end_p[-2]; ecma_value_t next_method = last_context_end_p[-3]; result = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (result)) { last_context_end_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; goto error; } ecma_value_t value = ECMA_VALUE_UNDEFINED; if (!ecma_is_value_false (result)) { value = ecma_op_iterator_value (result); ecma_free_value (result); if (ECMA_IS_VALUE_ERROR (value)) { last_context_end_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; result = value; goto error; } } else { last_context_end_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; } *stack_top_p++ = value; continue; } case VM_OC_ITERATOR_CONTEXT_END: { JERRY_ASSERT (VM_LAST_CONTEXT_END () == stack_top_p); if (stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR) { stack_top_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; result = ecma_op_iterator_close (stack_top_p[-2]); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } } stack_top_p = vm_stack_context_abort_variable_length (frame_ctx_p, stack_top_p, PARSER_ITERATOR_CONTEXT_STACK_ALLOCATION); continue; } case VM_OC_DEFAULT_INITIALIZER: { JERRY_ASSERT (stack_top_p > VM_GET_REGISTERS (frame_ctx_p) + register_end); if (stack_top_p[-1] != ECMA_VALUE_UNDEFINED) { byte_code_p = byte_code_start_p + branch_offset; continue; } stack_top_p--; continue; } case VM_OC_REST_INITIALIZER: { ecma_object_t *array_p = ecma_op_new_array_object (0); JERRY_ASSERT (ecma_op_object_is_fast_array (array_p)); ecma_value_t *last_context_end_p = VM_LAST_CONTEXT_END (); ecma_value_t iterator = last_context_end_p[-2]; ecma_value_t next_method = last_context_end_p[-3]; uint32_t index = 0; while (true) { result = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (result)) { last_context_end_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; ecma_deref_object (array_p); goto error; } if (ecma_is_value_false (result)) { last_context_end_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; break; } ecma_value_t value = ecma_op_iterator_value (result); ecma_free_value (result); if (ECMA_IS_VALUE_ERROR (value)) { ecma_deref_object (array_p); result = value; goto error; } ecma_fast_array_set_property (array_p, index++, value); ecma_free_value (value); } *stack_top_p++ = ecma_make_object_value (array_p); continue; } case VM_OC_OBJ_INIT_CONTEXT_CREATE: { left_value = stack_top_p[-1]; vm_stack_context_type_t context_type = VM_CONTEXT_OBJ_INIT; uint32_t context_stack_allocation = PARSER_OBJ_INIT_CONTEXT_STACK_ALLOCATION; if (opcode == CBC_EXT_OBJ_INIT_REST_CONTEXT_CREATE) { context_type = VM_CONTEXT_OBJ_INIT_REST; context_stack_allocation = PARSER_OBJ_INIT_REST_CONTEXT_STACK_ALLOCATION; } uint32_t context_size = (uint32_t) (stack_top_p + context_stack_allocation - VM_LAST_CONTEXT_END ()); stack_top_p += context_stack_allocation; VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, context_size); stack_top_p[-1] = VM_CREATE_CONTEXT (context_type, context_size); stack_top_p[-2] = left_value; if (context_type == VM_CONTEXT_OBJ_INIT_REST) { stack_top_p[-3] = ecma_make_object_value (ecma_op_new_array_object (0)); } continue; } case VM_OC_OBJ_INIT_CONTEXT_END: { JERRY_ASSERT (stack_top_p == VM_LAST_CONTEXT_END ()); uint32_t context_stack_allocation = PARSER_OBJ_INIT_CONTEXT_STACK_ALLOCATION; if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_OBJ_INIT_REST) { context_stack_allocation = PARSER_OBJ_INIT_REST_CONTEXT_STACK_ALLOCATION; } stack_top_p = vm_stack_context_abort_variable_length (frame_ctx_p, stack_top_p, context_stack_allocation); continue; } case VM_OC_OBJ_INIT_PUSH_REST: { ecma_value_t *last_context_end_p = VM_LAST_CONTEXT_END (); if (!ecma_op_require_object_coercible (last_context_end_p[-2])) { result = ECMA_VALUE_ERROR; goto error; } ecma_object_t *prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); ecma_object_t *result_object_p = ecma_create_object (prototype_p, 0, ECMA_OBJECT_TYPE_GENERAL); left_value = ecma_make_object_value (result_object_p); result = opfunc_copy_data_properties (left_value, last_context_end_p[-2], last_context_end_p[-3]); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } ecma_free_value (last_context_end_p[-3]); last_context_end_p[-3] = last_context_end_p[-2]; last_context_end_p[-2] = ECMA_VALUE_UNDEFINED; *stack_top_p++ = left_value; continue; } case VM_OC_INITIALIZER_PUSH_NAME: { if (JERRY_UNLIKELY (!ecma_is_value_prop_name (left_value))) { ecma_string_t *property_key = ecma_op_to_property_key (left_value); if (property_key == NULL) { result = ECMA_VALUE_ERROR; goto error; } ecma_free_value (left_value); left_value = ecma_make_string_value (property_key); } ecma_value_t *last_context_end_p = VM_LAST_CONTEXT_END (); ecma_object_t *array_obj_p = ecma_get_object_from_value (last_context_end_p[-3]); JERRY_ASSERT (ecma_get_object_type (array_obj_p) == ECMA_OBJECT_TYPE_ARRAY); ecma_extended_object_t *ext_array_obj_p = (ecma_extended_object_t *) array_obj_p; ecma_fast_array_set_property (array_obj_p, ext_array_obj_p->u.array.length, left_value); /* FALLTHRU */ } case VM_OC_INITIALIZER_PUSH_PROP: { ecma_value_t *last_context_end_p = VM_LAST_CONTEXT_END (); ecma_value_t base = last_context_end_p[-2]; if (opcode == CBC_EXT_INITIALIZER_PUSH_PROP) { left_value = *last_context_end_p++; while (last_context_end_p < stack_top_p) { last_context_end_p[-1] = *last_context_end_p; last_context_end_p++; } stack_top_p--; } result = vm_op_get_value (base, left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_left_value; } case VM_OC_SPREAD_ARGUMENTS: { uint8_t arguments_list_len = *byte_code_p++; stack_top_p -= arguments_list_len; ecma_collection_t *arguments_p = opfunc_spread_arguments (stack_top_p, arguments_list_len); if (JERRY_UNLIKELY (arguments_p == NULL)) { result = ECMA_VALUE_ERROR; goto error; } stack_top_p++; ECMA_SET_INTERNAL_VALUE_POINTER (stack_top_p[-1], arguments_p); frame_ctx_p->call_operation = VM_EXEC_SPREAD_OP; frame_ctx_p->byte_code_p = byte_code_start_p; frame_ctx_p->stack_top_p = stack_top_p; return ECMA_VALUE_UNDEFINED; } case VM_OC_CREATE_GENERATOR: { frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = stack_top_p; vm_executable_object_t *executable_object_p; executable_object_p = opfunc_create_executable_object (frame_ctx_p, VM_CREATE_EXECUTABLE_OBJECT_GENERATOR); return ecma_make_object_value ((ecma_object_t *) executable_object_p); } case VM_OC_YIELD: { frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = --stack_top_p; return *stack_top_p; } case VM_OC_ASYNC_YIELD: { ecma_extended_object_t *async_generator_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); opfunc_async_generator_yield (async_generator_object_p, stack_top_p[-1]); frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = --stack_top_p; return ECMA_VALUE_UNDEFINED; } case VM_OC_ASYNC_YIELD_ITERATOR: { ecma_extended_object_t *async_generator_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); JERRY_ASSERT ( !(async_generator_object_p->u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD)); /* Byte code is executed at the first time. */ left_value = stack_top_p[-1]; result = ecma_op_get_iterator (left_value, ECMA_VALUE_ASYNC_ITERATOR, stack_top_p - 1); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } ecma_free_value (left_value); left_value = result; result = ecma_op_iterator_next (left_value, stack_top_p[-1], ECMA_VALUE_UNDEFINED); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } result = ecma_promise_async_await (async_generator_object_p, result); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } async_generator_object_p->u.cls.u2.executable_obj_flags |= ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD; *VM_GET_EXECUTABLE_ITERATOR (frame_ctx_p) = left_value; frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = stack_top_p; return ECMA_VALUE_UNDEFINED; } case VM_OC_AWAIT: { if (JERRY_UNLIKELY (!(frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_EXECUTABLE))) { frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = --stack_top_p; result = opfunc_async_create_and_await (frame_ctx_p, *stack_top_p, 0); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } return result; } /* FALLTHRU */ } case VM_OC_GENERATOR_AWAIT: { ecma_extended_object_t *async_generator_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); result = ecma_promise_async_await (async_generator_object_p, *(--stack_top_p)); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = stack_top_p; return ECMA_VALUE_UNDEFINED; } case VM_OC_EXT_RETURN: { result = left_value; left_value = ECMA_VALUE_UNDEFINED; ecma_value_t *stack_bottom_p = VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth; while (stack_top_p > stack_bottom_p) { ecma_fast_free_value (*(--stack_top_p)); } goto error; } case VM_OC_ASYNC_EXIT: { JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); if (!(frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_EXECUTABLE)) { result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_VALUE_UNDEFINED, NULL); } else { result = *VM_GET_EXECUTABLE_ITERATOR (frame_ctx_p); *VM_GET_EXECUTABLE_ITERATOR (frame_ctx_p) = ECMA_VALUE_UNDEFINED; } vm_stack_context_type_t context_type = VM_GET_CONTEXT_TYPE (stack_top_p[-1]); if (context_type == VM_CONTEXT_TRY) { JERRY_ASSERT (frame_ctx_p->context_depth == PARSER_TRY_CONTEXT_STACK_ALLOCATION); left_value = ECMA_VALUE_UNDEFINED; } else { JERRY_ASSERT (frame_ctx_p->context_depth == PARSER_FINALLY_CONTEXT_STACK_ALLOCATION); left_value = stack_top_p[-2]; } if (context_type == VM_CONTEXT_FINALLY_THROW) { ecma_reject_promise (result, left_value); } else { JERRY_ASSERT (context_type == VM_CONTEXT_TRY || context_type == VM_CONTEXT_FINALLY_RETURN); ecma_fulfill_promise (result, left_value); } ecma_free_value (left_value); frame_ctx_p->context_depth = 0; frame_ctx_p->call_operation = VM_NO_EXEC_OP; return result; } case VM_OC_STRING_CONCAT: { ecma_string_t *left_str_p = ecma_op_to_string (left_value); if (JERRY_UNLIKELY (left_str_p == NULL)) { result = ECMA_VALUE_ERROR; goto error; } ecma_string_t *right_str_p = ecma_op_to_string (right_value); if (JERRY_UNLIKELY (right_str_p == NULL)) { ecma_deref_ecma_string (left_str_p); result = ECMA_VALUE_ERROR; goto error; } ecma_string_t *result_str_p = ecma_concat_ecma_strings (left_str_p, right_str_p); ecma_deref_ecma_string (right_str_p); *stack_top_p++ = ecma_make_string_value (result_str_p); goto free_both_values; } case VM_OC_GET_TEMPLATE_OBJECT: { uint8_t tagged_idx = *byte_code_p++; ecma_collection_t *collection_p = ecma_compiled_code_get_tagged_template_collection (bytecode_header_p); JERRY_ASSERT (tagged_idx < collection_p->item_count); *stack_top_p++ = ecma_copy_value (collection_p->buffer_p[tagged_idx]); continue; } case VM_OC_PUSH_NEW_TARGET: { ecma_object_t *new_target_object_p = JERRY_CONTEXT (current_new_target_p); if (new_target_object_p == NULL) { *stack_top_p++ = ECMA_VALUE_UNDEFINED; } else { ecma_ref_object (new_target_object_p); *stack_top_p++ = ecma_make_object_value (new_target_object_p); } continue; } case VM_OC_REQUIRE_OBJECT_COERCIBLE: { if (!ecma_op_require_object_coercible (stack_top_p[-1])) { result = ECMA_VALUE_ERROR; goto error; } continue; } case VM_OC_ASSIGN_SUPER: { result = opfunc_assign_super_reference (&stack_top_p, frame_ctx_p, opcode_data); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } continue; } case VM_OC_PUSH_ELISON: { *stack_top_p++ = ECMA_VALUE_ARRAY_HOLE; continue; } case VM_OC_APPEND_ARRAY: { uint16_t values_length = *byte_code_p++; stack_top_p -= values_length; if (*byte_code_start_p == CBC_EXT_OPCODE) { values_length = (uint16_t) (values_length | OPFUNC_HAS_SPREAD_ELEMENT); } result = opfunc_append_array (stack_top_p, values_length); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } continue; } case VM_OC_IDENT_REFERENCE: { uint16_t literal_index; READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (literal_index < ident_end); if (literal_index < register_end) { *stack_top_p++ = ECMA_VALUE_REGISTER_REF; *stack_top_p++ = ecma_make_integer_value (literal_index); *stack_top_p++ = ecma_fast_copy_value (VM_GET_REGISTER (frame_ctx_p, literal_index)); } else { ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_object_t *ref_base_lex_env_p; result = ecma_op_get_value_lex_env_base (frame_ctx_p->lex_env_p, &ref_base_lex_env_p, name_p); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } ecma_ref_object (ref_base_lex_env_p); ecma_ref_ecma_string (name_p); *stack_top_p++ = ecma_make_object_value (ref_base_lex_env_p); *stack_top_p++ = ecma_make_string_value (name_p); *stack_top_p++ = result; } continue; } case VM_OC_PROP_GET: { result = vm_op_get_value (left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_PROP_REFERENCE: { /* Forms with reference requires preserving the base and offset. */ if (opcode == CBC_PUSH_PROP_REFERENCE) { left_value = stack_top_p[-2]; right_value = stack_top_p[-1]; } else if (opcode == CBC_PUSH_PROP_LITERAL_REFERENCE) { *stack_top_p++ = left_value; right_value = left_value; left_value = stack_top_p[-2]; } else { JERRY_ASSERT (opcode == CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE || opcode == CBC_PUSH_PROP_THIS_LITERAL_REFERENCE); *stack_top_p++ = left_value; *stack_top_p++ = right_value; } /* FALLTHRU */ } case VM_OC_PROP_PRE_INCR: case VM_OC_PROP_PRE_DECR: case VM_OC_PROP_POST_INCR: case VM_OC_PROP_POST_DECR: { result = vm_op_get_value (left_value, right_value); if (opcode < CBC_PRE_INCR) { left_value = ECMA_VALUE_UNDEFINED; right_value = ECMA_VALUE_UNDEFINED; } if (ECMA_IS_VALUE_ERROR (result)) { goto error; } if (opcode < CBC_PRE_INCR) { break; } stack_top_p += 2; left_value = result; right_value = ECMA_VALUE_UNDEFINED; /* FALLTHRU */ } case VM_OC_PRE_INCR: case VM_OC_PRE_DECR: case VM_OC_POST_INCR: case VM_OC_POST_DECR: { uint32_t opcode_flags = VM_OC_GROUP_GET_INDEX (opcode_data) - VM_OC_PROP_PRE_INCR; ecma_number_t result_number; byte_code_p = byte_code_start_p + 1; if (ecma_is_value_integer_number (left_value)) { result = left_value; left_value = ECMA_VALUE_UNDEFINED; ecma_integer_value_t int_value = (ecma_integer_value_t) result; ecma_integer_value_t int_increase = 0; if (opcode_flags & VM_OC_DECREMENT_OPERATOR_FLAG) { if (int_value > ECMA_INTEGER_NUMBER_MIN_SHIFTED) { int_increase = -(1 << ECMA_DIRECT_SHIFT); } } else if (int_value < ECMA_INTEGER_NUMBER_MAX_SHIFTED) { int_increase = 1 << ECMA_DIRECT_SHIFT; } if (JERRY_LIKELY (int_increase != 0)) { /* Postfix operators require the unmodified number value. */ if (opcode_flags & VM_OC_POST_INCR_DECR_OPERATOR_FLAG) { POST_INCREASE_DECREASE_PUT_RESULT (result); } result = (ecma_value_t) (int_value + int_increase); break; } result_number = (ecma_number_t) ecma_get_integer_from_value (result); } else if (ecma_is_value_float_number (left_value)) { result = left_value; left_value = ECMA_VALUE_UNDEFINED; result_number = ecma_get_number_from_value (result); } else { result = ecma_op_to_numeric (left_value, &result_number, ECMA_TO_NUMERIC_ALLOW_BIGINT); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } ecma_free_value (left_value); left_value = ECMA_VALUE_UNDEFINED; #if JERRY_BUILTIN_BIGINT if (JERRY_UNLIKELY (ecma_is_value_bigint (result))) { ecma_bigint_unary_operation_type operation_type = ECMA_BIGINT_UNARY_INCREASE; if (opcode_flags & VM_OC_DECREMENT_OPERATOR_FLAG) { operation_type = ECMA_BIGINT_UNARY_DECREASE; } /* Postfix operators require the unmodified number value. */ if (opcode_flags & VM_OC_POST_INCR_DECR_OPERATOR_FLAG) { POST_INCREASE_DECREASE_PUT_RESULT (result); result = ecma_bigint_unary (result, operation_type); } else { ecma_value_t original_value = result; result = ecma_bigint_unary (original_value, operation_type); ecma_free_value (original_value); } if (ECMA_IS_VALUE_ERROR (result)) { goto error; } break; } #endif /* JERRY_BUILTIN_BIGINT */ result = ecma_make_number_value (result_number); } ecma_number_t increase = ECMA_NUMBER_ONE; if (opcode_flags & VM_OC_DECREMENT_OPERATOR_FLAG) { /* For decrement operators */ increase = ECMA_NUMBER_MINUS_ONE; } /* Postfix operators require the unmodified number value. */ if (opcode_flags & VM_OC_POST_INCR_DECR_OPERATOR_FLAG) { POST_INCREASE_DECREASE_PUT_RESULT (result); result = ecma_make_number_value (result_number + increase); break; } if (ecma_is_value_integer_number (result)) { result = ecma_make_number_value (result_number + increase); } else { result = ecma_update_float_number (result, result_number + increase); } break; } case VM_OC_ASSIGN: { result = left_value; left_value = ECMA_VALUE_UNDEFINED; break; } case VM_OC_MOV_IDENT: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (literal_index < register_end); JERRY_ASSERT (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))); ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, literal_index)); VM_GET_REGISTER (frame_ctx_p, literal_index) = left_value; continue; } case VM_OC_ASSIGN_PROP: { result = stack_top_p[-1]; stack_top_p[-1] = left_value; left_value = ECMA_VALUE_UNDEFINED; break; } case VM_OC_ASSIGN_PROP_THIS: { result = stack_top_p[-1]; stack_top_p[-1] = ecma_copy_value (frame_ctx_p->this_binding); *stack_top_p++ = left_value; left_value = ECMA_VALUE_UNDEFINED; break; } case VM_OC_RETURN_FUNCTION_END: { if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) == CBC_FUNCTION_SCRIPT) { result = VM_GET_REGISTER (frame_ctx_p, 0); VM_GET_REGISTERS (frame_ctx_p)[0] = ECMA_VALUE_UNDEFINED; } else { result = ECMA_VALUE_UNDEFINED; } goto error; } case VM_OC_RETURN: { JERRY_ASSERT (opcode == CBC_RETURN || opcode == CBC_RETURN_WITH_LITERAL); result = left_value; left_value = ECMA_VALUE_UNDEFINED; goto error; } case VM_OC_THROW: { jcontext_raise_exception (left_value); result = ECMA_VALUE_ERROR; left_value = ECMA_VALUE_UNDEFINED; goto error; } case VM_OC_THROW_REFERENCE_ERROR: { result = ecma_raise_reference_error (ECMA_ERR_UNDEFINED_REFERENCE); goto error; } case VM_OC_EVAL: { JERRY_CONTEXT (status_flags) |= ECMA_STATUS_DIRECT_EVAL; JERRY_ASSERT ((*byte_code_p >= CBC_CALL && *byte_code_p <= CBC_CALL2_PROP_BLOCK) || (*byte_code_p == CBC_EXT_OPCODE && byte_code_p[1] >= CBC_EXT_SPREAD_CALL && byte_code_p[1] <= CBC_EXT_SPREAD_CALL_PROP_BLOCK)); continue; } case VM_OC_CALL: { frame_ctx_p->call_operation = VM_EXEC_CALL; frame_ctx_p->byte_code_p = byte_code_start_p; frame_ctx_p->stack_top_p = stack_top_p; return ECMA_VALUE_UNDEFINED; } case VM_OC_NEW: { frame_ctx_p->call_operation = VM_EXEC_CONSTRUCT; frame_ctx_p->byte_code_p = byte_code_start_p; frame_ctx_p->stack_top_p = stack_top_p; return ECMA_VALUE_UNDEFINED; } case VM_OC_ERROR: { JERRY_ASSERT (frame_ctx_p->byte_code_p[1] == CBC_EXT_ERROR); result = ECMA_VALUE_ERROR; goto error; } case VM_OC_RESOLVE_BASE_FOR_CALL: { ecma_value_t this_value = stack_top_p[-3]; if (this_value == ECMA_VALUE_REGISTER_REF) { /* Lexical environment cannot be 'this' value. */ stack_top_p[-2] = ECMA_VALUE_UNDEFINED; stack_top_p[-3] = ECMA_VALUE_UNDEFINED; } else if (vm_get_implicit_this_value (&this_value)) { ecma_free_value (stack_top_p[-3]); stack_top_p[-3] = this_value; } continue; } case VM_OC_PROP_DELETE: { result = vm_op_delete_prop (left_value, right_value, is_strict); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } JERRY_ASSERT (ecma_is_value_boolean (result)); *stack_top_p++ = result; goto free_both_values; } case VM_OC_DELETE: { uint16_t literal_index; READ_LITERAL_INDEX (literal_index); if (literal_index < register_end) { *stack_top_p++ = ECMA_VALUE_FALSE; continue; } result = vm_op_delete_var (literal_start_p[literal_index], frame_ctx_p->lex_env_p); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } JERRY_ASSERT (ecma_is_value_boolean (result)); *stack_top_p++ = result; continue; } case VM_OC_JUMP: { byte_code_p = byte_code_start_p + branch_offset; continue; } case VM_OC_BRANCH_IF_STRICT_EQUAL: { ecma_value_t value = *(--stack_top_p); JERRY_ASSERT (stack_top_p > VM_GET_REGISTERS (frame_ctx_p) + register_end); if (ecma_op_strict_equality_compare (value, stack_top_p[-1])) { byte_code_p = byte_code_start_p + branch_offset; ecma_free_value (*--stack_top_p); } ecma_free_value (value); continue; } case VM_OC_BRANCH_IF_TRUE: case VM_OC_BRANCH_IF_FALSE: case VM_OC_BRANCH_IF_LOGICAL_TRUE: case VM_OC_BRANCH_IF_LOGICAL_FALSE: { uint32_t opcode_flags = VM_OC_GROUP_GET_INDEX (opcode_data) - VM_OC_BRANCH_IF_TRUE; ecma_value_t value = *(--stack_top_p); bool boolean_value = ecma_op_to_boolean (value); if (opcode_flags & VM_OC_BRANCH_IF_FALSE_FLAG) { boolean_value = !boolean_value; } if (boolean_value) { byte_code_p = byte_code_start_p + branch_offset; if (opcode_flags & VM_OC_LOGICAL_BRANCH_FLAG) { /* "Push" the value back to the stack. */ ++stack_top_p; continue; } } ecma_fast_free_value (value); continue; } case VM_OC_POP_REFERENCE: { ecma_free_value (stack_top_p[-2]); ecma_free_value (stack_top_p[-3]); stack_top_p[-3] = stack_top_p[-1]; stack_top_p -= 2; continue; } case VM_OC_BRANCH_IF_NULLISH: { left_value = stack_top_p[-1]; if (!ecma_is_value_null (left_value) && !ecma_is_value_undefined (left_value)) { byte_code_p = byte_code_start_p + branch_offset; continue; } --stack_top_p; continue; } case VM_OC_PLUS: case VM_OC_MINUS: { result = opfunc_unary_operation (left_value, VM_OC_GROUP_GET_INDEX (opcode_data) == VM_OC_PLUS); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_left_value; } case VM_OC_NOT: { *stack_top_p++ = ecma_make_boolean_value (!ecma_op_to_boolean (left_value)); JERRY_ASSERT (ecma_is_value_boolean (stack_top_p[-1])); goto free_left_value; } case VM_OC_BIT_NOT: { JERRY_STATIC_ASSERT ((ECMA_DIRECT_TYPE_MASK == ((1 << ECMA_DIRECT_SHIFT) - 1)), direct_type_mask_must_fill_all_bits_before_the_value_starts); if (ecma_is_value_integer_number (left_value)) { *stack_top_p++ = (~ECMA_DIRECT_TYPE_MASK) ^ left_value; goto free_left_value; } result = do_number_bitwise_not (left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_left_value; } case VM_OC_VOID: { *stack_top_p++ = ECMA_VALUE_UNDEFINED; goto free_left_value; } case VM_OC_TYPEOF_IDENT: { uint16_t literal_index; READ_LITERAL_INDEX (literal_index); JERRY_ASSERT (literal_index < ident_end); if (literal_index < register_end) { left_value = ecma_copy_value (VM_GET_REGISTER (frame_ctx_p, literal_index)); } else { ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_object_t *ref_base_lex_env_p; result = ecma_op_get_value_lex_env_base (frame_ctx_p->lex_env_p, &ref_base_lex_env_p, name_p); if (ref_base_lex_env_p == NULL) { jcontext_release_exception (); result = ECMA_VALUE_UNDEFINED; } else if (ECMA_IS_VALUE_ERROR (result)) { goto error; } left_value = result; } /* FALLTHRU */ } case VM_OC_TYPEOF: { result = opfunc_typeof (left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_left_value; } case VM_OC_ADD: { if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = ecma_get_integer_from_value (left_value); ecma_integer_value_t right_integer = ecma_get_integer_from_value (right_value); *stack_top_p++ = ecma_make_int32_value ((int32_t) (left_integer + right_integer)); continue; } if (ecma_is_value_float_number (left_value) && ecma_is_value_number (right_value)) { ecma_number_t new_value = (ecma_get_float_from_value (left_value) + ecma_get_number_from_value (right_value)); *stack_top_p++ = ecma_update_float_number (left_value, new_value); ecma_free_number (right_value); continue; } if (ecma_is_value_float_number (right_value) && ecma_is_value_integer_number (left_value)) { ecma_number_t new_value = ((ecma_number_t) ecma_get_integer_from_value (left_value) + ecma_get_float_from_value (right_value)); *stack_top_p++ = ecma_update_float_number (right_value, new_value); continue; } result = opfunc_addition (left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_SUB: { JERRY_STATIC_ASSERT (ECMA_INTEGER_NUMBER_MAX * 2 <= INT32_MAX && ECMA_INTEGER_NUMBER_MIN * 2 >= INT32_MIN, doubled_ecma_numbers_must_fit_into_int32_range); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (left_value) && !ECMA_IS_VALUE_ERROR (right_value)); if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = ecma_get_integer_from_value (left_value); ecma_integer_value_t right_integer = ecma_get_integer_from_value (right_value); *stack_top_p++ = ecma_make_int32_value ((int32_t) (left_integer - right_integer)); continue; } if (ecma_is_value_float_number (left_value) && ecma_is_value_number (right_value)) { ecma_number_t new_value = (ecma_get_float_from_value (left_value) - ecma_get_number_from_value (right_value)); *stack_top_p++ = ecma_update_float_number (left_value, new_value); ecma_free_number (right_value); continue; } if (ecma_is_value_float_number (right_value) && ecma_is_value_integer_number (left_value)) { ecma_number_t new_value = ((ecma_number_t) ecma_get_integer_from_value (left_value) - ecma_get_float_from_value (right_value)); *stack_top_p++ = ecma_update_float_number (right_value, new_value); continue; } result = do_number_arithmetic (NUMBER_ARITHMETIC_SUBTRACTION, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_MUL: { JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (left_value) && !ECMA_IS_VALUE_ERROR (right_value)); JERRY_STATIC_ASSERT (ECMA_INTEGER_MULTIPLY_MAX * ECMA_INTEGER_MULTIPLY_MAX <= ECMA_INTEGER_NUMBER_MAX && -(ECMA_INTEGER_MULTIPLY_MAX * ECMA_INTEGER_MULTIPLY_MAX) >= ECMA_INTEGER_NUMBER_MIN, square_of_integer_multiply_max_must_fit_into_integer_value_range); if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = ecma_get_integer_from_value (left_value); ecma_integer_value_t right_integer = ecma_get_integer_from_value (right_value); if (-ECMA_INTEGER_MULTIPLY_MAX <= left_integer && left_integer <= ECMA_INTEGER_MULTIPLY_MAX && -ECMA_INTEGER_MULTIPLY_MAX <= right_integer && right_integer <= ECMA_INTEGER_MULTIPLY_MAX && left_integer != 0 && right_integer != 0) { *stack_top_p++ = ecma_integer_multiply (left_integer, right_integer); continue; } ecma_number_t multiply = (ecma_number_t) left_integer * (ecma_number_t) right_integer; *stack_top_p++ = ecma_make_number_value (multiply); continue; } if (ecma_is_value_float_number (left_value) && ecma_is_value_number (right_value)) { ecma_number_t new_value = (ecma_get_float_from_value (left_value) * ecma_get_number_from_value (right_value)); *stack_top_p++ = ecma_update_float_number (left_value, new_value); ecma_free_number (right_value); continue; } if (ecma_is_value_float_number (right_value) && ecma_is_value_integer_number (left_value)) { ecma_number_t new_value = ((ecma_number_t) ecma_get_integer_from_value (left_value) * ecma_get_float_from_value (right_value)); *stack_top_p++ = ecma_update_float_number (right_value, new_value); continue; } result = do_number_arithmetic (NUMBER_ARITHMETIC_MULTIPLICATION, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_DIV: { JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (left_value) && !ECMA_IS_VALUE_ERROR (right_value)); result = do_number_arithmetic (NUMBER_ARITHMETIC_DIVISION, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_MOD: { JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (left_value) && !ECMA_IS_VALUE_ERROR (right_value)); if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = ecma_get_integer_from_value (left_value); ecma_integer_value_t right_integer = ecma_get_integer_from_value (right_value); if (right_integer != 0) { ecma_integer_value_t mod_result = left_integer % right_integer; if (mod_result != 0 || left_integer >= 0) { *stack_top_p++ = ecma_make_integer_value (mod_result); continue; } } } result = do_number_arithmetic (NUMBER_ARITHMETIC_REMAINDER, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_EXP: { result = do_number_arithmetic (NUMBER_ARITHMETIC_EXPONENTIATION, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_EQUAL: { result = opfunc_equality (left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_NOT_EQUAL: { result = opfunc_equality (left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = ecma_invert_boolean_value (result); goto free_both_values; } case VM_OC_STRICT_EQUAL: { bool is_equal = ecma_op_strict_equality_compare (left_value, right_value); result = ecma_make_boolean_value (is_equal); *stack_top_p++ = result; goto free_both_values; } case VM_OC_STRICT_NOT_EQUAL: { bool is_equal = ecma_op_strict_equality_compare (left_value, right_value); result = ecma_make_boolean_value (!is_equal); *stack_top_p++ = result; goto free_both_values; } case VM_OC_BIT_OR: { JERRY_STATIC_ASSERT ((ECMA_DIRECT_TYPE_MASK == ((1 << ECMA_DIRECT_SHIFT) - 1)), direct_type_mask_must_fill_all_bits_before_the_value_starts); if (ecma_are_values_integer_numbers (left_value, right_value)) { *stack_top_p++ = left_value | right_value; continue; } result = do_number_bitwise_logic (NUMBER_BITWISE_LOGIC_OR, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_BIT_XOR: { JERRY_STATIC_ASSERT ((ECMA_DIRECT_TYPE_MASK == ((1 << ECMA_DIRECT_SHIFT) - 1)), direct_type_mask_must_fill_all_bits_before_the_value_starts); if (ecma_are_values_integer_numbers (left_value, right_value)) { *stack_top_p++ = left_value ^ right_value; continue; } result = do_number_bitwise_logic (NUMBER_BITWISE_LOGIC_XOR, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_BIT_AND: { JERRY_STATIC_ASSERT ((ECMA_DIRECT_TYPE_MASK == ((1 << ECMA_DIRECT_SHIFT) - 1)), direct_type_mask_must_fill_all_bits_before_the_value_starts); if (ecma_are_values_integer_numbers (left_value, right_value)) { *stack_top_p++ = left_value & right_value; continue; } result = do_number_bitwise_logic (NUMBER_BITWISE_LOGIC_AND, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_LEFT_SHIFT: { JERRY_STATIC_ASSERT ((ECMA_DIRECT_TYPE_MASK == ((1 << ECMA_DIRECT_SHIFT) - 1)), direct_type_mask_must_fill_all_bits_before_the_value_starts); if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = ecma_get_integer_from_value (left_value); ecma_integer_value_t right_integer = ecma_get_integer_from_value (right_value); *stack_top_p++ = ecma_make_int32_value ((int32_t) ((uint32_t) left_integer << (right_integer & 0x1f))); continue; } result = do_number_bitwise_logic (NUMBER_BITWISE_SHIFT_LEFT, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_RIGHT_SHIFT: { JERRY_STATIC_ASSERT ((ECMA_DIRECT_TYPE_MASK == ((1 << ECMA_DIRECT_SHIFT) - 1)), direct_type_mask_must_fill_all_bits_before_the_value_starts); if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = ecma_get_integer_from_value (left_value); ecma_integer_value_t right_integer = ecma_get_integer_from_value (right_value); *stack_top_p++ = ecma_make_integer_value (left_integer >> (right_integer & 0x1f)); continue; } result = do_number_bitwise_logic (NUMBER_BITWISE_SHIFT_RIGHT, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_UNS_RIGHT_SHIFT: { JERRY_STATIC_ASSERT ((ECMA_DIRECT_TYPE_MASK == ((1 << ECMA_DIRECT_SHIFT) - 1)), direct_type_mask_must_fill_all_bits_before_the_value_starts); if (ecma_are_values_integer_numbers (left_value, right_value)) { uint32_t left_uint32 = (uint32_t) ecma_get_integer_from_value (left_value); ecma_integer_value_t right_integer = ecma_get_integer_from_value (right_value); *stack_top_p++ = ecma_make_uint32_value (left_uint32 >> (right_integer & 0x1f)); continue; } result = do_number_bitwise_logic (NUMBER_BITWISE_SHIFT_URIGHT, left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_LESS: { if (ecma_are_values_integer_numbers (left_value, right_value)) { bool is_less = (ecma_integer_value_t) left_value < (ecma_integer_value_t) right_value; #if !JERRY_VM_HALT /* This is a lookahead to the next opcode to improve performance. * If it is CBC_BRANCH_IF_TRUE_BACKWARD, execute it. */ if (*byte_code_p <= CBC_BRANCH_IF_TRUE_BACKWARD_3 && *byte_code_p >= CBC_BRANCH_IF_TRUE_BACKWARD) { byte_code_start_p = byte_code_p++; branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (*byte_code_start_p); JERRY_ASSERT (branch_offset_length >= 1 && branch_offset_length <= 3); if (is_less) { branch_offset = *(byte_code_p++); if (JERRY_UNLIKELY (branch_offset_length != 1)) { branch_offset <<= 8; branch_offset |= *(byte_code_p++); if (JERRY_UNLIKELY (branch_offset_length == 3)) { branch_offset <<= 8; branch_offset |= *(byte_code_p++); } } /* Note: The opcode is a backward branch. */ byte_code_p = byte_code_start_p - branch_offset; } else { byte_code_p += branch_offset_length; } continue; } #endif /* !JERRY_VM_HALT */ *stack_top_p++ = ecma_make_boolean_value (is_less); continue; } if (ecma_is_value_number (left_value) && ecma_is_value_number (right_value)) { ecma_number_t left_number = ecma_get_number_from_value (left_value); ecma_number_t right_number = ecma_get_number_from_value (right_value); *stack_top_p++ = ecma_make_boolean_value (left_number < right_number); goto free_both_values; } result = opfunc_relation (left_value, right_value, true, false); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_GREATER: { if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = (ecma_integer_value_t) left_value; ecma_integer_value_t right_integer = (ecma_integer_value_t) right_value; *stack_top_p++ = ecma_make_boolean_value (left_integer > right_integer); continue; } if (ecma_is_value_number (left_value) && ecma_is_value_number (right_value)) { ecma_number_t left_number = ecma_get_number_from_value (left_value); ecma_number_t right_number = ecma_get_number_from_value (right_value); *stack_top_p++ = ecma_make_boolean_value (left_number > right_number); goto free_both_values; } result = opfunc_relation (left_value, right_value, false, false); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_LESS_EQUAL: { if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = (ecma_integer_value_t) left_value; ecma_integer_value_t right_integer = (ecma_integer_value_t) right_value; *stack_top_p++ = ecma_make_boolean_value (left_integer <= right_integer); continue; } if (ecma_is_value_number (left_value) && ecma_is_value_number (right_value)) { ecma_number_t left_number = ecma_get_number_from_value (left_value); ecma_number_t right_number = ecma_get_number_from_value (right_value); *stack_top_p++ = ecma_make_boolean_value (left_number <= right_number); goto free_both_values; } result = opfunc_relation (left_value, right_value, false, true); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_GREATER_EQUAL: { if (ecma_are_values_integer_numbers (left_value, right_value)) { ecma_integer_value_t left_integer = (ecma_integer_value_t) left_value; ecma_integer_value_t right_integer = (ecma_integer_value_t) right_value; *stack_top_p++ = ecma_make_boolean_value (left_integer >= right_integer); continue; } if (ecma_is_value_number (left_value) && ecma_is_value_number (right_value)) { ecma_number_t left_number = ecma_get_number_from_value (left_value); ecma_number_t right_number = ecma_get_number_from_value (right_value); *stack_top_p++ = ecma_make_boolean_value (left_number >= right_number); goto free_both_values; } result = opfunc_relation (left_value, right_value, true, true); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_IN: { result = opfunc_in (left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_INSTANCEOF: { result = opfunc_instanceof (left_value, right_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; goto free_both_values; } case VM_OC_BLOCK_CREATE_CONTEXT: { ecma_value_t *stack_context_top_p; stack_context_top_p = VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth; JERRY_ASSERT (stack_context_top_p == stack_top_p || stack_context_top_p == stack_top_p - 1); if (byte_code_start_p[0] != CBC_EXT_OPCODE) { branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); if (stack_context_top_p != stack_top_p) { /* Preserve the value of switch statement. */ stack_context_top_p[1] = stack_context_top_p[0]; } stack_context_top_p[0] = VM_CREATE_CONTEXT_WITH_ENV (VM_CONTEXT_BLOCK, branch_offset); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_BLOCK_CONTEXT_STACK_ALLOCATION; } else { JERRY_ASSERT (byte_code_start_p[1] == CBC_EXT_TRY_CREATE_ENV); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_TRY || VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_CATCH || VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP || VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_FINALLY_THROW || VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN); JERRY_ASSERT (!(stack_context_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)); stack_context_top_p[-1] |= VM_CONTEXT_HAS_LEX_ENV; } frame_ctx_p->lex_env_p = ecma_create_decl_lex_env (frame_ctx_p->lex_env_p); frame_ctx_p->lex_env_p->type_flags_refs |= ECMA_OBJECT_FLAG_BLOCK; continue; } case VM_OC_WITH: { ecma_value_t value = *(--stack_top_p); ecma_object_t *object_p; ecma_object_t *with_env_p; branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); result = ecma_op_to_object (value); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } object_p = ecma_get_object_from_value (result); with_env_p = ecma_create_object_lex_env (frame_ctx_p->lex_env_p, object_p); ecma_deref_object (object_p); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_WITH_CONTEXT_STACK_ALLOCATION; stack_top_p[-1] = VM_CREATE_CONTEXT_WITH_ENV (VM_CONTEXT_WITH, branch_offset); with_env_p->type_flags_refs |= ECMA_OBJECT_FLAG_BLOCK; frame_ctx_p->lex_env_p = with_env_p; continue; } case VM_OC_FOR_IN_INIT: { ecma_value_t value = *(--stack_top_p); JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); ecma_value_t expr_obj_value = ECMA_VALUE_UNDEFINED; ecma_collection_t *prop_names_p = opfunc_for_in (value, &expr_obj_value); ecma_free_value (value); if (prop_names_p == NULL) { if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (expr_obj_value))) { result = expr_obj_value; goto error; } /* The collection is already released */ byte_code_p = byte_code_start_p + branch_offset; continue; } branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_IN, branch_offset); ECMA_SET_INTERNAL_VALUE_ANY_POINTER (stack_top_p[-2], prop_names_p); stack_top_p[-3] = 0; stack_top_p[-4] = expr_obj_value; if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT) { /* No need to duplicate the first context. */ byte_code_p += 2; } continue; } case VM_OC_FOR_IN_GET_NEXT: { ecma_value_t *context_top_p = VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth; ecma_collection_t *collection_p; collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, context_top_p[-2]); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (context_top_p[-1]) == VM_CONTEXT_FOR_IN); uint32_t index = context_top_p[-3]; ecma_value_t *buffer_p = collection_p->buffer_p; *stack_top_p++ = buffer_p[index]; context_top_p[-3]++; continue; } case VM_OC_FOR_IN_HAS_NEXT: { JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); ecma_collection_t *collection_p; collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, stack_top_p[-2]); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FOR_IN); ecma_value_t *buffer_p = collection_p->buffer_p; ecma_object_t *object_p = ecma_get_object_from_value (stack_top_p[-4]); uint32_t index = stack_top_p[-3]; while (index < collection_p->item_count) { ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (buffer_p[index]); result = ecma_op_object_has_property (object_p, prop_name_p); if (ECMA_IS_VALUE_ERROR (result)) { stack_top_p[-3] = index; goto error; } if (JERRY_LIKELY (ecma_is_value_true (result))) { byte_code_p = byte_code_start_p + branch_offset; break; } ecma_deref_ecma_string (prop_name_p); index++; } if (index == collection_p->item_count) { ecma_deref_object (object_p); ecma_collection_destroy (collection_p); VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); stack_top_p -= PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; } else { stack_top_p[-3] = index; } continue; } case VM_OC_FOR_OF_INIT: { ecma_value_t value = *(--stack_top_p); JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); ecma_value_t next_method; ecma_value_t iterator = ecma_op_get_iterator (value, ECMA_VALUE_SYNC_ITERATOR, &next_method); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (iterator)) { result = iterator; goto error; } result = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (result)) { ecma_free_value (iterator); ecma_free_value (next_method); goto error; } if (ecma_is_value_false (result)) { ecma_free_value (iterator); ecma_free_value (next_method); byte_code_p = byte_code_start_p + branch_offset; continue; } ecma_value_t next_value = ecma_op_iterator_value (result); ecma_free_value (result); if (ECMA_IS_VALUE_ERROR (next_value)) { result = next_value; ecma_free_value (iterator); ecma_free_value (next_method); goto error; } branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION; stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_OF, branch_offset) | VM_CONTEXT_CLOSE_ITERATOR; stack_top_p[-2] = next_value; stack_top_p[-3] = iterator; stack_top_p[-4] = next_method; if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT) { /* No need to duplicate the first context. */ byte_code_p += 2; } continue; } case VM_OC_FOR_OF_GET_NEXT: { ecma_value_t *context_top_p = VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth; JERRY_ASSERT (VM_GET_CONTEXT_TYPE (context_top_p[-1]) == VM_CONTEXT_FOR_OF || VM_GET_CONTEXT_TYPE (context_top_p[-1]) == VM_CONTEXT_FOR_AWAIT_OF); JERRY_ASSERT (context_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR); *stack_top_p++ = context_top_p[-2]; context_top_p[-2] = ECMA_VALUE_UNDEFINED; continue; } case VM_OC_FOR_OF_HAS_NEXT: { JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FOR_OF); JERRY_ASSERT (stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR); stack_top_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; result = ecma_op_iterator_step (stack_top_p[-3], stack_top_p[-4]); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } if (ecma_is_value_false (result)) { ecma_free_value (stack_top_p[-2]); ecma_free_value (stack_top_p[-3]); ecma_free_value (stack_top_p[-4]); VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION; continue; } ecma_value_t next_value = ecma_op_iterator_value (result); ecma_free_value (result); if (ECMA_IS_VALUE_ERROR (next_value)) { result = next_value; goto error; } JERRY_ASSERT (stack_top_p[-2] == ECMA_VALUE_UNDEFINED); stack_top_p[-1] |= VM_CONTEXT_CLOSE_ITERATOR; stack_top_p[-2] = next_value; byte_code_p = byte_code_start_p + branch_offset; continue; } case VM_OC_FOR_AWAIT_OF_INIT: { ecma_value_t value = *(--stack_top_p); JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); ecma_value_t next_method; result = ecma_op_get_iterator (value, ECMA_VALUE_ASYNC_ITERATOR, &next_method); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } ecma_value_t iterator = result; result = ecma_op_iterator_next (result, next_method, ECMA_VALUE_EMPTY); if (ECMA_IS_VALUE_ERROR (result)) { ecma_free_value (iterator); ecma_free_value (next_method); goto error; } branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION; stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_AWAIT_OF, branch_offset); stack_top_p[-2] = ECMA_VALUE_UNDEFINED; stack_top_p[-3] = iterator; stack_top_p[-4] = next_method; if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT) { /* No need to duplicate the first context. */ byte_code_p += 2; } frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = stack_top_p; uint16_t extra_flags = (ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD | (ECMA_AWAIT_FOR_NEXT << ECMA_AWAIT_STATE_SHIFT)); if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) == CBC_FUNCTION_ASYNC_GENERATOR || (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_EXECUTABLE)) { ecma_extended_object_t *executable_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); result = ecma_promise_async_await (executable_object_p, result); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } executable_object_p->u.cls.u2.executable_obj_flags |= extra_flags; return ECMA_VALUE_UNDEFINED; } result = opfunc_async_create_and_await (frame_ctx_p, result, extra_flags); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } return result; } case VM_OC_FOR_AWAIT_OF_HAS_NEXT: { JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FOR_AWAIT_OF); JERRY_ASSERT (stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR); stack_top_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; result = ecma_op_iterator_next (stack_top_p[-3], stack_top_p[-4], ECMA_VALUE_EMPTY); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } ecma_extended_object_t *executable_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); result = ecma_promise_async_await (executable_object_p, result); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } uint16_t extra_flags = (ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD | (ECMA_AWAIT_FOR_NEXT << ECMA_AWAIT_STATE_SHIFT)); executable_object_p->u.cls.u2.executable_obj_flags |= extra_flags; frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = byte_code_start_p + branch_offset; frame_ctx_p->stack_top_p = stack_top_p; return ECMA_VALUE_UNDEFINED; } case VM_OC_TRY: { /* Try opcode simply creates the try context. */ branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_TRY_CONTEXT_STACK_ALLOCATION; stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_TRY, branch_offset); continue; } case VM_OC_CATCH: { /* Catches are ignored and turned to jumps. */ JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY); byte_code_p = byte_code_start_p + branch_offset; continue; } case VM_OC_FINALLY: { branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY || VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH); if (stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) { ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); ecma_deref_object (lex_env_p); } VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION); stack_top_p += PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION; stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FINALLY_JUMP, branch_offset); stack_top_p[-2] = (ecma_value_t) branch_offset; continue; } case VM_OC_CONTEXT_END: { JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); JERRY_ASSERT (!(stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR)); ecma_value_t context_type = VM_GET_CONTEXT_TYPE (stack_top_p[-1]); if (!VM_CONTEXT_IS_FINALLY (context_type)) { stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); continue; } if (stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) { ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); ecma_deref_object (lex_env_p); } VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FINALLY_CONTEXT_STACK_ALLOCATION); stack_top_p -= PARSER_FINALLY_CONTEXT_STACK_ALLOCATION; if (context_type == VM_CONTEXT_FINALLY_RETURN) { result = *stack_top_p; goto error; } if (context_type == VM_CONTEXT_FINALLY_THROW) { jcontext_raise_exception (*stack_top_p); #if JERRY_VM_THROW JERRY_CONTEXT (status_flags) |= ECMA_STATUS_ERROR_THROWN; #endif /* JERRY_VM_THROW */ result = ECMA_VALUE_ERROR; goto error; } JERRY_ASSERT (context_type == VM_CONTEXT_FINALLY_JUMP); uint32_t jump_target = *stack_top_p; vm_stack_found_type type = vm_stack_find_finally (frame_ctx_p, stack_top_p, VM_CONTEXT_FINALLY_JUMP, jump_target); stack_top_p = frame_ctx_p->stack_top_p; switch (type) { case VM_CONTEXT_FOUND_FINALLY: { byte_code_p = frame_ctx_p->byte_code_p; JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); stack_top_p[-2] = jump_target; break; } case VM_CONTEXT_FOUND_ERROR: { JERRY_ASSERT (jcontext_has_pending_exception ()); result = ECMA_VALUE_ERROR; goto error; } case VM_CONTEXT_FOUND_AWAIT: { JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); stack_top_p[-2] = jump_target; return ECMA_VALUE_UNDEFINED; } default: { byte_code_p = frame_ctx_p->byte_code_start_p + jump_target; break; } } JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); continue; } case VM_OC_JUMP_AND_EXIT_CONTEXT: { JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); JERRY_ASSERT (!jcontext_has_pending_exception ()); branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); vm_stack_found_type type = vm_stack_find_finally (frame_ctx_p, stack_top_p, VM_CONTEXT_FINALLY_JUMP, (uint32_t) branch_offset); stack_top_p = frame_ctx_p->stack_top_p; switch (type) { case VM_CONTEXT_FOUND_FINALLY: { byte_code_p = frame_ctx_p->byte_code_p; JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); stack_top_p[-2] = (uint32_t) branch_offset; break; } case VM_CONTEXT_FOUND_ERROR: { JERRY_ASSERT (jcontext_has_pending_exception ()); result = ECMA_VALUE_ERROR; goto error; } case VM_CONTEXT_FOUND_AWAIT: { JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); stack_top_p[-2] = (uint32_t) branch_offset; return ECMA_VALUE_UNDEFINED; } default: { byte_code_p = frame_ctx_p->byte_code_start_p + branch_offset; break; } } JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); continue; } #if JERRY_MODULE_SYSTEM case VM_OC_MODULE_IMPORT: { left_value = *(--stack_top_p); ecma_value_t user_value = ECMA_VALUE_UNDEFINED; ecma_value_t script_value = ((cbc_uint8_arguments_t *) bytecode_header_p)->script_value; #if JERRY_SNAPSHOT_EXEC if (JERRY_UNLIKELY (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION))) { #endif /* JERRY_SNAPSHOT_EXEC */ cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); if (script_p->refs_and_type & CBC_SCRIPT_HAS_USER_VALUE) { user_value = CBC_SCRIPT_GET_USER_VALUE (script_p); } #if JERRY_SNAPSHOT_EXEC } #endif /* JERRY_SNAPSHOT_EXEC */ result = ecma_module_import (left_value, user_value); ecma_free_value (left_value); if (ECMA_IS_VALUE_ERROR (result)) { goto error; } *stack_top_p++ = result; continue; } case VM_OC_MODULE_IMPORT_META: { ecma_value_t script_value = ((cbc_uint8_arguments_t *) bytecode_header_p)->script_value; cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); JERRY_ASSERT (script_p->refs_and_type & CBC_SCRIPT_HAS_IMPORT_META); ecma_value_t import_meta = CBC_SCRIPT_GET_IMPORT_META (script_p, script_p->refs_and_type); ecma_object_t *import_meta_object_p = ecma_get_object_from_value (import_meta); if (ecma_get_object_type (import_meta_object_p) != ECMA_OBJECT_TYPE_GENERAL) { JERRY_ASSERT (ecma_object_class_is (import_meta_object_p, ECMA_OBJECT_CLASS_MODULE)); ecma_value_t module = import_meta; import_meta_object_p = ecma_create_object (NULL, 0, ECMA_OBJECT_TYPE_GENERAL); import_meta = ecma_make_object_value (import_meta_object_p); if (JERRY_CONTEXT (module_import_meta_callback_p) != NULL) { void *user_p = JERRY_CONTEXT (module_import_meta_callback_user_p); JERRY_CONTEXT (module_import_meta_callback_p) (module, import_meta, user_p); } CBC_SCRIPT_GET_IMPORT_META (script_p, script_p->refs_and_type) = import_meta; } else { ecma_ref_object (import_meta_object_p); } *stack_top_p++ = import_meta; continue; } #endif /* JERRY_MODULE_SYSTEM */ case VM_OC_NONE: default: { JERRY_ASSERT (VM_OC_GROUP_GET_INDEX (opcode_data) == VM_OC_NONE); jerry_fatal (JERRY_FATAL_DISABLED_BYTE_CODE); } } JERRY_ASSERT (VM_OC_HAS_PUT_RESULT (opcode_data)); if (opcode_data & VM_OC_PUT_IDENT) { uint16_t literal_index; READ_LITERAL_INDEX (literal_index); if (literal_index < register_end) { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, literal_index)); VM_GET_REGISTER (frame_ctx_p, literal_index) = result; if (opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK)) { result = ecma_fast_copy_value (result); } } else { ecma_string_t *var_name_str_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_value_t put_value_result = ecma_op_put_value_lex_env_base (frame_ctx_p->lex_env_p, var_name_str_p, is_strict, result); if (ECMA_IS_VALUE_ERROR (put_value_result)) { ecma_free_value (result); result = put_value_result; goto error; } if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) { ecma_fast_free_value (result); } } } else if (opcode_data & VM_OC_PUT_REFERENCE) { ecma_value_t property = *(--stack_top_p); ecma_value_t base = *(--stack_top_p); if (base == ECMA_VALUE_REGISTER_REF) { property = (ecma_value_t) ecma_get_integer_from_value (property); ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, property)); VM_GET_REGISTER (frame_ctx_p, property) = result; if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) { goto free_both_values; } result = ecma_fast_copy_value (result); } else { ecma_value_t set_value_result = vm_op_set_value (base, property, result, is_strict); if (ECMA_IS_VALUE_ERROR (set_value_result)) { ecma_free_value (result); result = set_value_result; goto error; } if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) { ecma_fast_free_value (result); goto free_both_values; } } } if (opcode_data & VM_OC_PUT_STACK) { *stack_top_p++ = result; } else if (opcode_data & VM_OC_PUT_BLOCK) { ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); VM_GET_REGISTERS (frame_ctx_p)[0] = result; } free_both_values: ecma_fast_free_value (right_value); free_left_value: ecma_fast_free_value (left_value); } error: ecma_fast_free_value (left_value); ecma_fast_free_value (right_value); if (ECMA_IS_VALUE_ERROR (result)) { JERRY_ASSERT (jcontext_has_pending_exception ()); ecma_value_t *stack_bottom_p = VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth; while (stack_top_p > stack_bottom_p) { ecma_value_t stack_item = *(--stack_top_p); if (stack_item == ECMA_VALUE_RELEASE_LEX_ENV) { opfunc_pop_lexical_environment (frame_ctx_p); continue; } ecma_fast_free_value (stack_item); } #if JERRY_VM_THROW if (!(JERRY_CONTEXT (status_flags) & ECMA_STATUS_ERROR_THROWN)) { JERRY_CONTEXT (status_flags) |= ECMA_STATUS_ERROR_THROWN; jerry_throw_cb_t vm_throw_callback_p = JERRY_CONTEXT (vm_throw_callback_p); if (vm_throw_callback_p != NULL) { vm_throw_callback_p (JERRY_CONTEXT (error_value), JERRY_CONTEXT (vm_throw_callback_user_p)); } } #endif /* JERRY_VM_THROW */ } JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); if (frame_ctx_p->context_depth == 0) { /* In most cases there is no context. */ frame_ctx_p->call_operation = VM_NO_EXEC_OP; return result; } if (!ECMA_IS_VALUE_ERROR (result)) { switch (vm_stack_find_finally (frame_ctx_p, stack_top_p, VM_CONTEXT_FINALLY_RETURN, 0)) { case VM_CONTEXT_FOUND_FINALLY: { stack_top_p = frame_ctx_p->stack_top_p; byte_code_p = frame_ctx_p->byte_code_p; JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN); JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); stack_top_p[-2] = result; continue; } case VM_CONTEXT_FOUND_ERROR: { JERRY_ASSERT (jcontext_has_pending_exception ()); ecma_free_value (result); stack_top_p = frame_ctx_p->stack_top_p; result = ECMA_VALUE_ERROR; break; } case VM_CONTEXT_FOUND_AWAIT: { stack_top_p = frame_ctx_p->stack_top_p; JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN); stack_top_p[-2] = result; return ECMA_VALUE_UNDEFINED; } default: { goto finish; } } } JERRY_ASSERT (jcontext_has_pending_exception ()); if (!jcontext_has_pending_abort ()) { switch (vm_stack_find_finally (frame_ctx_p, stack_top_p, VM_CONTEXT_FINALLY_THROW, 0)) { case VM_CONTEXT_FOUND_FINALLY: { stack_top_p = frame_ctx_p->stack_top_p; byte_code_p = frame_ctx_p->byte_code_p; JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); JERRY_ASSERT (!(stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)); result = jcontext_take_exception (); if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW) { stack_top_p[-2] = result; continue; } JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH); *stack_top_p++ = result; continue; } case VM_CONTEXT_FOUND_AWAIT: { JERRY_ASSERT (VM_GET_CONTEXT_TYPE (frame_ctx_p->stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW); return ECMA_VALUE_UNDEFINED; } default: { break; } } } else { do { JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); } while (frame_ctx_p->context_depth > 0); } finish: frame_ctx_p->call_operation = VM_NO_EXEC_OP; return result; } } /* vm_loop */ #if JERRY_MODULE_SYSTEM /** * Create and initialize module scope with all data properties * * @return ECMA_VALUE_EMPTY on success, * ECMA_VALUE_ERROR on failure */ ecma_value_t vm_init_module_scope (ecma_module_t *module_p) /**< module without scope */ { ecma_object_t *global_object_p; #if JERRY_BUILTIN_REALMS global_object_p = (ecma_object_t *) ecma_op_function_get_realm (module_p->u.compiled_code_p); #else /* !JERRY_BUILTIN_REALMS */ global_object_p = ecma_builtin_get_global (); #endif /* JERRY_BUILTIN_REALMS */ ecma_object_t *scope_p = ecma_create_lex_env_class (ecma_get_global_environment (global_object_p), sizeof (ecma_lexical_environment_class_t)); const ecma_compiled_code_t *compiled_code_p = module_p->u.compiled_code_p; ecma_value_t *literal_start_p; uint8_t *byte_code_p; uint16_t encoding_limit; uint16_t encoding_delta; ((ecma_lexical_environment_class_t *) scope_p)->object_p = (ecma_object_t *) module_p; ((ecma_lexical_environment_class_t *) scope_p)->type = ECMA_LEX_ENV_CLASS_TYPE_MODULE; module_p->scope_p = scope_p; ecma_deref_object (scope_p); if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p; literal_start_p = (ecma_value_t *) (args_p + 1); literal_start_p -= args_p->register_end; byte_code_p = (uint8_t *) (literal_start_p + args_p->literal_end); } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p; literal_start_p = (ecma_value_t *) (args_p + 1); literal_start_p -= args_p->register_end; byte_code_p = (uint8_t *) (literal_start_p + args_p->literal_end); } /* Prepare for byte code execution. */ if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING)) { encoding_limit = CBC_SMALL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_SMALL_LITERAL_ENCODING_DELTA; } else { encoding_limit = CBC_FULL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_FULL_LITERAL_ENCODING_DELTA; } JERRY_ASSERT (*byte_code_p >= CBC_JUMP_FORWARD && *byte_code_p <= CBC_JUMP_FORWARD_3); byte_code_p += 1 + CBC_BRANCH_OFFSET_LENGTH (*byte_code_p); while (true) { uint8_t opcode = *byte_code_p++; switch (opcode) { case CBC_CREATE_VAR: case CBC_CREATE_LET: case CBC_CREATE_CONST: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); JERRY_ASSERT (ecma_find_named_property (scope_p, name_p) == NULL); uint8_t prop_attributes = ECMA_PROPERTY_FLAG_WRITABLE; if (opcode == CBC_CREATE_LET) { prop_attributes = ECMA_PROPERTY_ENUMERABLE_WRITABLE; } else if (opcode == CBC_CREATE_CONST) { prop_attributes = ECMA_PROPERTY_FLAG_ENUMERABLE; } ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (scope_p, name_p, prop_attributes, NULL); if (opcode != CBC_CREATE_VAR) { property_value_p->value = ECMA_VALUE_UNINITIALIZED; } break; } case CBC_INIT_ARG_OR_FUNC: { uint32_t literal_index; READ_LITERAL_INDEX (literal_index); ecma_compiled_code_t *function_bytecode_p; #if JERRY_SNAPSHOT_EXEC if (JERRY_LIKELY (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION))) { #endif /* JERRY_SNAPSHOT_EXEC */ function_bytecode_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, literal_start_p[literal_index]); #if JERRY_SNAPSHOT_EXEC } else { uint8_t *byte_p = ((uint8_t *) compiled_code_p) + literal_start_p[literal_index]; function_bytecode_p = (ecma_compiled_code_t *) byte_p; } #endif /* JERRY_SNAPSHOT_EXEC */ JERRY_ASSERT (CBC_IS_FUNCTION (function_bytecode_p->status_flags)); ecma_object_t *function_obj_p; if (JERRY_UNLIKELY (CBC_FUNCTION_IS_ARROW (function_bytecode_p->status_flags))) { function_obj_p = ecma_op_create_arrow_function_object (scope_p, function_bytecode_p, ECMA_VALUE_UNDEFINED); } else { function_obj_p = ecma_op_create_any_function_object (scope_p, function_bytecode_p); } READ_LITERAL_INDEX (literal_index); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); JERRY_ASSERT (ecma_find_named_property (scope_p, name_p) == NULL); ecma_property_value_t *property_value_p; property_value_p = ecma_create_named_data_property (scope_p, name_p, ECMA_PROPERTY_FLAG_WRITABLE, NULL); JERRY_ASSERT (property_value_p->value == ECMA_VALUE_UNDEFINED); property_value_p->value = ecma_make_object_value (function_obj_p); ecma_deref_object (function_obj_p); break; } default: { JERRY_ASSERT (opcode == CBC_RETURN_FUNCTION_END); return ECMA_VALUE_EMPTY; } } } } /* vm_init_module_scope */ #endif /* JERRY_MODULE_SYSTEM */ #undef READ_LITERAL #undef READ_LITERAL_INDEX JERRY_STATIC_ASSERT (((int)VM_FRAME_CTX_SHARED_DIRECT_EVAL == (int)VM_FRAME_CTX_DIRECT_EVAL), vm_frame_ctx_shared_direct_eval_must_be_equal_to_frame_ctx_direct_eval); JERRY_STATIC_ASSERT (((int)CBC_CODE_FLAGS_STRICT_MODE == (int)VM_FRAME_CTX_IS_STRICT), cbc_code_flags_strict_mode_must_be_equal_to_vm_frame_ctx_is_strict); /** * Initialize code block execution * * @return ECMA_VALUE_ERROR - if the initialization fails * ECMA_VALUE_EMPTY - otherwise */ static void JERRY_ATTR_NOINLINE vm_init_exec (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { vm_frame_ctx_shared_t *shared_p = frame_ctx_p->shared_p; const ecma_compiled_code_t *bytecode_header_p = shared_p->bytecode_header_p; frame_ctx_p->prev_context_p = JERRY_CONTEXT (vm_top_context_p); frame_ctx_p->context_depth = 0; frame_ctx_p->status_flags = (uint8_t) ((shared_p->status_flags & VM_FRAME_CTX_DIRECT_EVAL) | (bytecode_header_p->status_flags & VM_FRAME_CTX_IS_STRICT)); uint16_t argument_end, register_end; ecma_value_t *literal_p; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; argument_end = args_p->argument_end; register_end = args_p->register_end; literal_p = (ecma_value_t *) (args_p + 1); literal_p -= register_end; frame_ctx_p->literal_start_p = literal_p; literal_p += args_p->literal_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; argument_end = args_p->argument_end; register_end = args_p->register_end; literal_p = (ecma_value_t *) (args_p + 1); literal_p -= register_end; frame_ctx_p->literal_start_p = literal_p; literal_p += args_p->literal_end; } frame_ctx_p->byte_code_p = (uint8_t *) literal_p; frame_ctx_p->byte_code_start_p = (uint8_t *) literal_p; frame_ctx_p->stack_top_p = VM_GET_REGISTERS (frame_ctx_p) + register_end; uint32_t arg_list_len = 0; if (argument_end > 0) { JERRY_ASSERT (shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_ARG_LIST); const ecma_value_t *arg_list_p = ((vm_frame_ctx_shared_args_t *) shared_p)->arg_list_p; arg_list_len = ((vm_frame_ctx_shared_args_t *) shared_p)->arg_list_len; if (arg_list_len > argument_end) { arg_list_len = argument_end; } for (uint32_t i = 0; i < arg_list_len; i++) { VM_GET_REGISTER (frame_ctx_p, i) = ecma_fast_copy_value (arg_list_p[i]); } } /* The arg_list_len contains the end of the copied arguments. * Fill everything else with undefined. */ if (register_end > arg_list_len) { ecma_value_t *stack_p = VM_GET_REGISTERS (frame_ctx_p) + arg_list_len; for (uint32_t i = arg_list_len; i < register_end; i++) { *stack_p++ = ECMA_VALUE_UNDEFINED; } } JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_DIRECT_EVAL; JERRY_CONTEXT (vm_top_context_p) = frame_ctx_p; } /* vm_init_exec */ /** * Resume execution of a code block. * * @return ecma value */ ecma_value_t JERRY_ATTR_NOINLINE vm_execute (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { while (true) { ecma_value_t completion_value = vm_loop (frame_ctx_p); switch (frame_ctx_p->call_operation) { case VM_EXEC_CALL: { opfunc_call (frame_ctx_p); break; } case VM_EXEC_SUPER_CALL: { vm_super_call (frame_ctx_p); break; } case VM_EXEC_SPREAD_OP: { vm_spread_operation (frame_ctx_p); break; } case VM_EXEC_RETURN: { return completion_value; } case VM_EXEC_CONSTRUCT: { opfunc_construct (frame_ctx_p); break; } default: { JERRY_ASSERT (frame_ctx_p->call_operation == VM_NO_EXEC_OP); const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->shared_p->bytecode_header_p; uint32_t register_end; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { register_end = ((cbc_uint16_arguments_t *) bytecode_header_p)->register_end; } else { register_end = ((cbc_uint8_arguments_t *) bytecode_header_p)->register_end; } /* Free arguments and registers */ ecma_value_t *registers_p = VM_GET_REGISTERS (frame_ctx_p); for (uint32_t i = 0; i < register_end; i++) { ecma_fast_free_value (registers_p[i]); } JERRY_CONTEXT (vm_top_context_p) = frame_ctx_p->prev_context_p; return completion_value; } } } } /* vm_execute */ /** * Run the code. * * @return ecma value */ ecma_value_t vm_run (vm_frame_ctx_shared_t *shared_p, /**< shared data */ ecma_value_t this_binding_value, /**< value of 'ThisBinding' */ ecma_object_t *lex_env_p) /**< lexical environment to use */ { const ecma_compiled_code_t *bytecode_header_p = shared_p->bytecode_header_p; vm_frame_ctx_t *frame_ctx_p; size_t frame_size; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; frame_size = (size_t) (args_p->register_end + args_p->stack_limit); } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; frame_size = (size_t) (args_p->register_end + args_p->stack_limit); } JERRY_VLA (ecma_value_t, stack, frame_size + (sizeof (vm_frame_ctx_t) / sizeof (ecma_value_t))); frame_ctx_p = (vm_frame_ctx_t *) stack; frame_ctx_p->shared_p = shared_p; frame_ctx_p->lex_env_p = lex_env_p; frame_ctx_p->this_binding = this_binding_value; vm_init_exec (frame_ctx_p); return vm_execute (frame_ctx_p); } /* vm_run */ /** * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-math.cpp000664 001750 001750 00000033666 15164251010 047343 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "jrt-libc-includes.h" #include "jrt.h" #if defined(_WIN32) #include #endif /* defined(_WIN32) */ #if JERRY_BUILTIN_MATH #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_MATH_OBJECT_ROUTINE_START = 0, ECMA_MATH_OBJECT_ABS, /* ECMA-262 v5, 15.8.2.1 */ ECMA_MATH_OBJECT_ACOS, /* ECMA-262 v5, 15.8.2.2 */ ECMA_MATH_OBJECT_ASIN, /* ECMA-262 v5, 15.8.2.3 */ ECMA_MATH_OBJECT_ATAN, /* ECMA-262 v5, 15.8.2.4 */ ECMA_MATH_OBJECT_CEIL, /* ECMA-262 v5, 15.8.2.6 */ ECMA_MATH_OBJECT_COS, /* ECMA-262 v5, 15.8.2.7 */ ECMA_MATH_OBJECT_EXP, /* ECMA-262 v5, 15.8.2.8 */ ECMA_MATH_OBJECT_FLOOR, /* ECMA-262 v5, 15.8.2.9 */ ECMA_MATH_OBJECT_LOG, /* ECMA-262 v5, 15.8.2.10 */ ECMA_MATH_OBJECT_ROUND, /* ECMA-262 v5, 15.8.2.15 */ ECMA_MATH_OBJECT_SIN, /* ECMA-262 v5, 15.8.2.16 */ ECMA_MATH_OBJECT_SQRT, /* ECMA-262 v5, 15.8.2.17 */ ECMA_MATH_OBJECT_TAN, /* ECMA-262 v5, 15.8.2.18 */ ECMA_MATH_OBJECT_ACOSH, /* ECMA-262 v6, 20.2.2.3 */ ECMA_MATH_OBJECT_ASINH, /* ECMA-262 v6, 20.2.2.5 */ ECMA_MATH_OBJECT_ATANH, /* ECMA-262 v6, 20.2.2.7 */ ECMA_MATH_OBJECT_CBRT, /* ECMA-262 v6, 20.2.2.9 */ ECMA_MATH_OBJECT_CLZ32, /* ECMA-262 v6, 20.2.2.11 */ ECMA_MATH_OBJECT_COSH, /* ECMA-262 v6, 20.2.2.13 */ ECMA_MATH_OBJECT_EXPM1, /* ECMA-262 v6, 20.2.2.15 */ ECMA_MATH_OBJECT_FROUND, /* ECMA-262 v6, 20.2.2.17 */ ECMA_MATH_OBJECT_LOG1P, /* ECMA-262 v6, 20.2.2.21 */ ECMA_MATH_OBJECT_LOG10, /* ECMA-262 v6, 20.2.2.22 */ ECMA_MATH_OBJECT_LOG2, /* ECMA-262 v6, 20.2.2.23 */ ECMA_MATH_OBJECT_SIGN, /* ECMA-262 v6, 20.2.2.29 */ ECMA_MATH_OBJECT_SINH, /* ECMA-262 v6, 20.2.2.31 */ ECMA_MATH_OBJECT_TANH, /* ECMA-262 v6, 20.2.2.34 */ ECMA_MATH_OBJECT_TRUNC, /* ECMA-262 v6, 20.2.2.35 */ ECMA_MATH_OBJECT_ATAN2, /* ECMA-262 v5, 15.8.2.5 */ /* first routine with 2 arguments */ ECMA_MATH_OBJECT_IMUL, /* ECMA-262 v6, 20.2.2.19 */ ECMA_MATH_OBJECT_POW, /* ECMA-262 v5, 15.8.2.13 */ /* last routine with 1 or 2 arguments*/ ECMA_MATH_OBJECT_MAX, /* ECMA-262 v5, 15.8.2.11 */ ECMA_MATH_OBJECT_MIN, /* ECMA-262 v5, 15.8.2.12 */ ECMA_MATH_OBJECT_HYPOT, /* ECMA-262 v6, 20.2.2.18 */ ECMA_MATH_OBJECT_RANDOM, /* ECMA-262 v5, 15.8.2.14 */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-math.inc.h" #define BUILTIN_UNDERSCORED_ID math #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup object ECMA Object object built-in * @{ */ /** * The Math object's 'max' 'min' routines. * * See also: * ECMA-262 v5, 15.8.2.11 * ECMA-262 v5, 15.8.2.12 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_math_object_max_min (bool is_max, /**< 'max' or 'min' operation */ const ecma_value_t *arg, /**< arguments list */ uint32_t args_number) /**< number of arguments */ { ecma_number_t result_num = ecma_number_make_infinity (is_max); while (args_number > 0) { ecma_number_t arg_num; ecma_value_t value = ecma_op_to_number (*arg, &arg_num); if (ECMA_IS_VALUE_ERROR (value)) { return value; } arg++; args_number--; if (ecma_number_is_nan (arg_num)) { result_num = arg_num; } if (ecma_number_is_zero (arg_num) && ecma_number_is_zero (result_num)) { bool is_negative = ecma_number_is_negative (arg_num); if (is_max ? !is_negative : is_negative) { result_num = arg_num; } } else { if (is_max ? (arg_num > result_num) : (arg_num < result_num)) { result_num = arg_num; } } } return ecma_make_number_value (result_num); } /* ecma_builtin_math_object_max_min */ /** * The Math object's 'hypot' routine * * See also: * ECMA-262 v6, 20.2.2.18 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_math_object_hypot (const ecma_value_t *arg, /**< arguments list */ uint32_t args_number) /**< number of arguments */ { if (args_number == 0) { return ecma_make_number_value (0.0); } ecma_number_t result_num = 0; bool inf = false; while (args_number > 0) { ecma_number_t arg_num; ecma_value_t value = ecma_op_to_number (*arg, &arg_num); if (ECMA_IS_VALUE_ERROR (value)) { return value; } arg++; args_number--; if (ecma_number_is_nan (arg_num) && !inf) { result_num = arg_num; continue; } if (ecma_number_is_infinity (arg_num)) { inf = true; result_num = ecma_number_make_infinity (false); continue; } result_num += arg_num * arg_num; } return ecma_make_number_value (sqrt (result_num)); } /* ecma_builtin_math_object_hypot */ /** * The Math object's 'trunc' routine * * See also: * ECMA-262 v6, 20.2.2.35 * * @return ecma number */ static ecma_number_t ecma_builtin_math_object_trunc (ecma_number_t arg) { if (ecma_number_is_nan (arg) || ecma_number_is_infinity (arg) || ecma_number_is_zero (arg)) { return arg; } if ((arg > 0) && (arg < 1)) { return (ecma_number_t) 0.0; } if ((arg < 0) && (arg > -1)) { return (ecma_number_t) -0.0; } return (ecma_number_t) arg - fmod (arg, 1); } /* ecma_builtin_math_object_trunc */ /** * The Math object's 'sign' routine * * See also: * ECMA-262 v6, 20.2.2.29 * * @return ecma number */ static ecma_number_t ecma_builtin_math_object_sign (ecma_number_t arg) { if (ecma_number_is_nan (arg) || ecma_number_is_zero (arg)) { return arg; } if (ecma_number_is_negative (arg)) { return (ecma_number_t) -1.0; } return (ecma_number_t) 1.0; } /* ecma_builtin_math_object_sign */ /** * The Math object's 'random' routine. * * See also: * ECMA-262 v5, 15.8.2.14 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_math_object_random (void) { const ecma_number_t rand_max = (ecma_number_t) RAND_MAX; const ecma_number_t rand_max_min_1 = (ecma_number_t) (RAND_MAX - 1); return ecma_make_number_value (((ecma_number_t) rand ()) / rand_max * rand_max_min_1 / rand_max); } /* ecma_builtin_math_object_random */ /** * Dispatcher for the built-in's routines. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_math_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (this_arg); if (builtin_routine_id <= ECMA_MATH_OBJECT_POW) { ecma_number_t x = ecma_number_make_nan (); ecma_number_t y = ecma_number_make_nan (); if (arguments_number >= 1) { ecma_value_t value = ecma_op_to_number (arguments_list[0], &x); if (ECMA_IS_VALUE_ERROR (value)) { return value; } } if (builtin_routine_id >= ECMA_MATH_OBJECT_ATAN2 && arguments_number >= 2) { if (ecma_is_value_number (arguments_list[1])) { y = ecma_get_number_from_value (arguments_list[1]); } else { ecma_value_t value = ecma_op_to_number (arguments_list[1], &y); if (ECMA_IS_VALUE_ERROR (value)) { return value; } } } switch (builtin_routine_id) { case ECMA_MATH_OBJECT_ABS: { x = DOUBLE_TO_ECMA_NUMBER_T (fabs (x)); break; } case ECMA_MATH_OBJECT_ACOS: { x = DOUBLE_TO_ECMA_NUMBER_T (acos (x)); break; } case ECMA_MATH_OBJECT_ASIN: { x = DOUBLE_TO_ECMA_NUMBER_T (asin (x)); break; } case ECMA_MATH_OBJECT_ATAN: { x = DOUBLE_TO_ECMA_NUMBER_T (atan (x)); break; } case ECMA_MATH_OBJECT_CEIL: { x = DOUBLE_TO_ECMA_NUMBER_T (ceil (x)); break; } case ECMA_MATH_OBJECT_COS: { x = DOUBLE_TO_ECMA_NUMBER_T (cos (x)); break; } case ECMA_MATH_OBJECT_EXP: { x = DOUBLE_TO_ECMA_NUMBER_T (exp (x)); break; } case ECMA_MATH_OBJECT_FLOOR: { x = DOUBLE_TO_ECMA_NUMBER_T (floor (x)); break; } case ECMA_MATH_OBJECT_LOG: { x = DOUBLE_TO_ECMA_NUMBER_T (log (x)); break; } case ECMA_MATH_OBJECT_TRUNC: { x = ecma_builtin_math_object_trunc (x); break; } case ECMA_MATH_OBJECT_SIGN: { x = ecma_builtin_math_object_sign (x); break; } case ECMA_MATH_OBJECT_ROUND: { if (ecma_number_is_nan (x) || ecma_number_is_zero (x) || ecma_number_is_infinity (x)) { break; } ecma_number_t fraction = fmod (x, ECMA_NUMBER_ONE); if (ecma_number_is_zero (fraction)) { break; } if (ecma_number_is_negative (x)) { if (x >= -ECMA_NUMBER_HALF) { x = -ECMA_NUMBER_ZERO; break; } if (fraction < -ECMA_NUMBER_HALF) { x -= ECMA_NUMBER_HALF; } } else if (fraction >= ECMA_NUMBER_HALF) { x += ECMA_NUMBER_HALF; } x = ecma_number_trunc (x); break; } case ECMA_MATH_OBJECT_SIN: { x = DOUBLE_TO_ECMA_NUMBER_T (sin (x)); break; } case ECMA_MATH_OBJECT_SQRT: { x = DOUBLE_TO_ECMA_NUMBER_T (sqrt (x)); break; } case ECMA_MATH_OBJECT_TAN: { x = DOUBLE_TO_ECMA_NUMBER_T (tan (x)); break; } case ECMA_MATH_OBJECT_ATAN2: { x = DOUBLE_TO_ECMA_NUMBER_T (atan2 (x, y)); break; } case ECMA_MATH_OBJECT_POW: { x = ecma_number_pow (x, y); break; } case ECMA_MATH_OBJECT_ACOSH: { x = DOUBLE_TO_ECMA_NUMBER_T (acosh (x)); break; } case ECMA_MATH_OBJECT_ASINH: { x = DOUBLE_TO_ECMA_NUMBER_T (asinh (x)); break; } case ECMA_MATH_OBJECT_ATANH: { x = DOUBLE_TO_ECMA_NUMBER_T (atanh (x)); break; } case ECMA_MATH_OBJECT_CBRT: { x = DOUBLE_TO_ECMA_NUMBER_T (cbrt (x)); break; } case ECMA_MATH_OBJECT_COSH: { x = DOUBLE_TO_ECMA_NUMBER_T (cosh (x)); break; } case ECMA_MATH_OBJECT_EXPM1: { x = DOUBLE_TO_ECMA_NUMBER_T (expm1 (x)); break; } case ECMA_MATH_OBJECT_LOG1P: { x = DOUBLE_TO_ECMA_NUMBER_T (log1p (x)); break; } case ECMA_MATH_OBJECT_LOG10: { x = DOUBLE_TO_ECMA_NUMBER_T (log10 (x)); break; } case ECMA_MATH_OBJECT_LOG2: { x = DOUBLE_TO_ECMA_NUMBER_T (log2 (x)); break; } case ECMA_MATH_OBJECT_SINH: { x = DOUBLE_TO_ECMA_NUMBER_T (sinh (x)); break; } case ECMA_MATH_OBJECT_TANH: { x = DOUBLE_TO_ECMA_NUMBER_T (tanh (x)); break; } case ECMA_MATH_OBJECT_CLZ32: { uint32_t n = ecma_number_to_uint32 (x); #if defined(__GNUC__) || defined(__clang__) x = n ? __builtin_clz (n) : 32; #elif defined(_WIN32) unsigned long ret; x = _BitScanReverse (&ret, n) ? 31 - ret : 32; #else /* !(defined(__GNUC__) || defined(__clang__) || defined(_WIN32)) */ x = 32; for (int i = 31; i >= 0; i--) { if (n >> i) { x = 31 - i; break; } } #endif /* defined (__GNUC__) || defined (__clang__) */ break; } case ECMA_MATH_OBJECT_FROUND: { break; } case ECMA_MATH_OBJECT_IMUL: { x = (int32_t) (ecma_number_to_uint32 (x) * ecma_number_to_uint32 (y)); break; } } return ecma_make_number_value (x); } /* if (builtin_routine_id <= ECMA_MATH_OBJECT_POW) */ if (builtin_routine_id <= ECMA_MATH_OBJECT_MIN) { return ecma_builtin_math_object_max_min (builtin_routine_id == ECMA_MATH_OBJECT_MAX, arguments_list, arguments_number); } if (builtin_routine_id == ECMA_MATH_OBJECT_HYPOT) { return ecma_builtin_math_object_hypot (arguments_list, arguments_number); } JERRY_ASSERT (builtin_routine_id == ECMA_MATH_OBJECT_RANDOM); return ecma_builtin_math_object_random (); } /* ecma_builtin_math_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_MATH */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/000775 001750 001750 00000000000 15164251010 035315 5ustar00ddennedyddennedy000000 000000 external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/include/jerryscript-core.h000664 001750 001750 00000006572 15164251010 044433 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JERRYSCRIPT_CORE_H #define JERRYSCRIPT_CORE_H #include "jerryscript-types.h" JERRY_C_API_BEGIN void jerry_init (jerry_init_flag_t flags); void jerry_cleanup (void); jerry_value_t jerry_current_realm (void); jerry_value_t jerry_set_realm (jerry_value_t realm); jerry_value_t jerry_eval (const jerry_char_t *source_p, size_t source_size, uint32_t flags); jerry_value_t jerry_run (const jerry_value_t script); bool jerry_value_is_undefined (const jerry_value_t value); bool jerry_value_is_number (const jerry_value_t value); bool jerry_value_is_object (const jerry_value_t value); bool jerry_value_is_string (const jerry_value_t value); bool jerry_value_is_exception (const jerry_value_t value); jerry_value_t jerry_value_to_object (const jerry_value_t value); jerry_value_t jerry_value_to_string (const jerry_value_t value); float jerry_value_as_number (const jerry_value_t value); int32_t jerry_value_as_int32 (const jerry_value_t value); uint32_t jerry_value_as_uint32 (const jerry_value_t value); jerry_value_t JERRY_ATTR_CONST jerry_undefined (void); jerry_value_t JERRY_ATTR_CONST jerry_null (void); jerry_value_t JERRY_ATTR_CONST jerry_boolean (bool value); jerry_value_t jerry_number (float value); jerry_value_t jerry_string (const jerry_char_t *buffer_p, jerry_size_t buffer_size, jerry_encoding_t encoding); jerry_value_t jerry_string_sz (const char *str_p); jerry_length_t jerry_string_length (const jerry_value_t value); jerry_size_t jerry_string_to_buffer (const jerry_value_t value, jerry_encoding_t encoding, jerry_char_t *buffer_p, jerry_size_t buffer_size); jerry_value_t jerry_object (void); jerry_value_t jerry_object_set (jerry_value_t object, const jerry_value_t key, const jerry_value_t value); jerry_value_t jerry_object_set_sz (jerry_value_t object, const char *key_p, const jerry_value_t value); jerry_value_t jerry_object_set_index (jerry_value_t object, uint32_t index, const jerry_value_t value); void jerry_object_set_native_ptr (jerry_value_t object, const jerry_object_native_info_t *native_info_p, void *native_pointer_p); jerry_value_t jerry_object_get (const jerry_value_t object, const jerry_value_t key); jerry_value_t jerry_object_get_sz (const jerry_value_t object, const char *key_p); jerry_value_t jerry_object_get_index (const jerry_value_t object, uint32_t index); void *jerry_object_get_native_ptr (const jerry_value_t object, const jerry_object_native_info_t *native_info_p); jerry_value_t jerry_function_external (jerry_external_handler_t handler); void jerry_value_free (jerry_value_t value); JERRY_C_API_END #endif /* !JERRYSCRIPT_CORE_H */ /* vim: set fdm=marker fmr=@{,@}: */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array-iterator-prototype.cpp000664 001750 001750 00000014211 15164251010 053403 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-arraybuffer-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-iterator-object.h" #include "ecma-typedarray-object.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_ARRAY_ITERATOR_PROTOTYPE_ROUTINE_START = 0, ECMA_ARRAY_ITERATOR_PROTOTYPE_OBJECT_NEXT, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-array-iterator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID array_iterator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup %arrayiteratorprototype% ECMA %ArrayIteratorPrototype% object built-in * @{ */ /** * The %ArrayIteratorPrototype% object's 'next' routine * * See also: * ECMA-262 v6, 22.1.5.2.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object, if success * error - otherwise */ static ecma_value_t ecma_builtin_array_iterator_prototype_object_next (ecma_value_t this_val) /**< this argument */ { /* 1 - 2. */ if (!ecma_is_value_object (this_val)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (this_val); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; /* 3. */ if (!ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_ARRAY_ITERATOR)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_ITERATOR); } ecma_value_t iterated_value = ext_obj_p->u.cls.u3.iterated_value; /* 4 - 5 */ if (ecma_is_value_empty (iterated_value)) { return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } ecma_object_t *array_object_p = ecma_get_object_from_value (iterated_value); /* 8. */ ecma_length_t length; if (ecma_object_is_typedarray (array_object_p)) { /* a. */ ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (array_object_p); if (ecma_arraybuffer_is_detached (arraybuffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } /* b. */ length = ecma_typedarray_get_length (array_object_p); } else { ecma_value_t len_value = ecma_op_object_get_length (array_object_p, &length); if (ECMA_IS_VALUE_ERROR (len_value)) { return len_value; } } ecma_length_t index = ext_obj_p->u.cls.u2.iterator_index; if (JERRY_UNLIKELY (index == ECMA_ITERATOR_INDEX_LIMIT)) { /* After the ECMA_ITERATOR_INDEX_LIMIT limit is reached the [[%Iterator%NextIndex]] property is stored as an internal property */ ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX); ecma_value_t index_value = ecma_op_object_get (obj_p, prop_name_p); if (!ecma_is_value_undefined (index_value)) { index = (ecma_length_t) (ecma_get_number_from_value (index_value) + 1); } ecma_op_object_put (obj_p, prop_name_p, ecma_make_length_value (index), true); ecma_free_value (index_value); } else { /* 11. */ ext_obj_p->u.cls.u2.iterator_index++; } if (index >= length) { ext_obj_p->u.cls.u3.iterated_value = ECMA_VALUE_EMPTY; return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } /* 7. */ uint8_t iterator_kind = ext_obj_p->u.cls.u1.iterator_kind; if (iterator_kind == ECMA_ITERATOR_KEYS) { /* 12. */ return ecma_create_iter_result_object (ecma_make_length_value (index), ECMA_VALUE_FALSE); } /* 14. */ ecma_value_t get_value = ecma_op_object_get_by_index (array_object_p, index); /* 15. */ if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } ecma_value_t result; /* 16. */ if (iterator_kind == ECMA_ITERATOR_VALUES) { result = ecma_create_iter_result_object (get_value, ECMA_VALUE_FALSE); } else { /* 17.a */ JERRY_ASSERT (iterator_kind == ECMA_ITERATOR_ENTRIES); /* 17.b */ ecma_value_t entry_array_value; entry_array_value = ecma_create_array_from_iter_element (get_value, ecma_make_length_value (index)); result = ecma_create_iter_result_object (entry_array_value, ECMA_VALUE_FALSE); ecma_free_value (entry_array_value); } ecma_free_value (get_value); return result; } /* ecma_builtin_array_iterator_prototype_object_next */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_array_iterator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_list_p, arguments_number); switch (builtin_routine_id) { case ECMA_ARRAY_ITERATOR_PROTOTYPE_OBJECT_NEXT: { return ecma_builtin_array_iterator_prototype_object_next (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_array_iterator_prototype_dispatch_routine */ /** * @} * @} * @} */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.inc.h000664 001750 001750 00000012073 15164251010 051574 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Date.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_DATE OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_DATE, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_DATE_PROTOTYPE_TO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_DATE_STRING_UL, ECMA_DATE_PROTOTYPE_TO_DATE_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_TIME_STRING_UL, ECMA_DATE_PROTOTYPE_TO_TIME_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, ECMA_DATE_PROTOTYPE_TO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_DATE_STRING_UL, ECMA_DATE_PROTOTYPE_TO_DATE_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_TIME_STRING_UL, ECMA_DATE_PROTOTYPE_TO_TIME_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_VALUE_OF_UL, ECMA_DATE_PROTOTYPE_GET_TIME, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_TIME_UL, ECMA_DATE_PROTOTYPE_GET_TIME, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_FULL_YEAR_UL, ECMA_DATE_PROTOTYPE_GET_FULL_YEAR, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_UTC_FULL_YEAR_UL, ECMA_DATE_PROTOTYPE_GET_UTC_FULL_YEAR, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_MONTH_UL, ECMA_DATE_PROTOTYPE_GET_MONTH, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_UTC_MONTH_UL, ECMA_DATE_PROTOTYPE_GET_UTC_MONTH, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_DATE_UL, ECMA_DATE_PROTOTYPE_GET_DATE, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_UTC_DATE_UL, ECMA_DATE_PROTOTYPE_GET_UTC_DATE, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_DAY_UL, ECMA_DATE_PROTOTYPE_GET_DAY, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_UTC_DAY_UL, ECMA_DATE_PROTOTYPE_GET_UTC_DAY, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_HOURS_UL, ECMA_DATE_PROTOTYPE_GET_HOURS, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_UTC_HOURS_UL, ECMA_DATE_PROTOTYPE_GET_UTC_HOURS, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_MINUTES_UL, ECMA_DATE_PROTOTYPE_GET_MINUTES, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_UTC_MINUTES_UL, ECMA_DATE_PROTOTYPE_GET_UTC_MINUTES, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_SECONDS_UL, ECMA_DATE_PROTOTYPE_GET_SECONDS, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_UTC_SECONDS_UL, ECMA_DATE_PROTOTYPE_GET_UTC_SECONDS, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_MILLISECONDS_UL, ECMA_DATE_PROTOTYPE_GET_MILLISECONDS, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_UTC_MILLISECONDS_UL, ECMA_DATE_PROTOTYPE_GET_UTC_MILLISECONDS, 0, 0) ROUTINE (LIT_MAGIC_STRING_GET_TIMEZONE_OFFSET_UL, ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET, 0, 0) ROUTINE (LIT_MAGIC_STRING_SET_TIME_UL, ECMA_DATE_PROTOTYPE_SET_TIME, 1, 1) ROUTINE (LIT_MAGIC_STRING_SET_MILLISECONDS_UL, ECMA_DATE_PROTOTYPE_SET_MILLISECONDS, 1, 1) ROUTINE (LIT_MAGIC_STRING_SET_UTC_MILLISECONDS_UL, ECMA_DATE_PROTOTYPE_SET_UTC_MILLISECONDS, 1, 1) ROUTINE (LIT_MAGIC_STRING_SET_SECONDS_UL, ECMA_DATE_PROTOTYPE_SET_SECONDS, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_UTC_SECONDS_UL, ECMA_DATE_PROTOTYPE_SET_UTC_SECONDS, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_MINUTES_UL, ECMA_DATE_PROTOTYPE_SET_MINUTES, NON_FIXED, 3) ROUTINE (LIT_MAGIC_STRING_SET_UTC_MINUTES_UL, ECMA_DATE_PROTOTYPE_SET_UTC_MINUTES, NON_FIXED, 3) ROUTINE (LIT_MAGIC_STRING_SET_HOURS_UL, ECMA_DATE_PROTOTYPE_SET_HOURS, NON_FIXED, 4) ROUTINE (LIT_MAGIC_STRING_SET_UTC_HOURS_UL, ECMA_DATE_PROTOTYPE_SET_UTC_HOURS, NON_FIXED, 4) ROUTINE (LIT_MAGIC_STRING_SET_DATE_UL, ECMA_DATE_PROTOTYPE_SET_DATE, 1, 1) ROUTINE (LIT_MAGIC_STRING_SET_UTC_DATE_UL, ECMA_DATE_PROTOTYPE_SET_UTC_DATE, 1, 1) ROUTINE (LIT_MAGIC_STRING_SET_MONTH_UL, ECMA_DATE_PROTOTYPE_SET_MONTH, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_UTC_MONTH_UL, ECMA_DATE_PROTOTYPE_SET_UTC_MONTH, 2, 2) ROUTINE (LIT_MAGIC_STRING_SET_FULL_YEAR_UL, ECMA_DATE_PROTOTYPE_SET_FULL_YEAR, NON_FIXED, 3) ROUTINE (LIT_MAGIC_STRING_SET_UTC_FULL_YEAR_UL, ECMA_DATE_PROTOTYPE_SET_UTC_FULL_YEAR, NON_FIXED, 3) ROUTINE (LIT_MAGIC_STRING_TO_ISO_STRING_UL, ECMA_DATE_PROTOTYPE_TO_ISO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_JSON_UL, ECMA_DATE_PROTOTYPE_TO_JSON, 1, 1) ROUTINE_CONFIGURABLE_ONLY (LIT_GLOBAL_SYMBOL_TO_PRIMITIVE, ECMA_DATE_PROTOTYPE_TO_PRIMITIVE, 1, 1) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TO_UTC_STRING_UL, LIT_MAGIC_STRING_TO_UTC_STRING_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #if JERRY_BUILTIN_ANNEXB INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TO_GMT_STRING_UL, LIT_MAGIC_STRING_TO_UTC_STRING_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) ROUTINE (LIT_MAGIC_STRING_GET_YEAR_UL, ECMA_DATE_PROTOTYPE_GET_YEAR, 0, 0) ROUTINE (LIT_MAGIC_STRING_SET_YEAR_UL, ECMA_DATE_PROTOTYPE_SET_YEAR, 1, 1) #endif /* JERRY_BUILTIN_ANNEXB */ #endif /* JERRY_BUILTIN_DATE */ #include "ecma-builtin-helpers-macro-undefs.inc.h" modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/swap.h000664 001750 001750 00000002566 15164251010 037617 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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++ 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 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_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwShape.cpp000664 001750 001750 00000046010 15164251010 036311 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgSwCommon.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static bool _outlineBegin(SwOutline& outline) { //Make a contour if lineTo/curveTo without calling close or moveTo beforehand. if (outline.pts.empty()) return false; outline.cntrs.push(outline.pts.count - 1); outline.closed.push(false); outline.pts.push(outline.pts[outline.cntrs.last()]); outline.types.push(SW_CURVE_TYPE_POINT); return false; } static bool _outlineEnd(SwOutline& outline) { if (outline.pts.empty()) return false; outline.cntrs.push(outline.pts.count - 1); outline.closed.push(false); return false; } static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix& transform, bool closed = false) { //make it a contour, if the last contour is not closed yet. if (!closed) _outlineEnd(outline); outline.pts.push(mathTransform(to, transform)); outline.types.push(SW_CURVE_TYPE_POINT); return false; } static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix& transform) { outline.pts.push(mathTransform(to, transform)); outline.types.push(SW_CURVE_TYPE_POINT); } static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform) { outline.pts.push(mathTransform(ctrl1, transform)); outline.types.push(SW_CURVE_TYPE_CUBIC); outline.pts.push(mathTransform(ctrl2, transform)); outline.types.push(SW_CURVE_TYPE_CUBIC); outline.pts.push(mathTransform(to, transform)); outline.types.push(SW_CURVE_TYPE_POINT); } static bool _outlineClose(SwOutline& outline) { uint32_t i; if (outline.cntrs.count > 0) i = outline.cntrs.last() + 1; else i = 0; //Make sure there is at least one point in the current path if (outline.pts.count == i) return false; //Close the path outline.pts.push(outline.pts[i]); outline.cntrs.push(outline.pts.count - 1); outline.types.push(SW_CURVE_TYPE_POINT); outline.closed.push(true); return true; } static void _drawPoint(SwDashStroke& dash, const Point* start, const Matrix& transform) { if (dash.move || dash.pattern[dash.curIdx] < FLOAT_EPSILON) { _outlineMoveTo(*dash.outline, start, transform); dash.move = false; } _outlineLineTo(*dash.outline, start, transform); } static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform, bool validPoint) { Line cur = {dash.ptCur, *to}; auto len = cur.length(); if (tvg::zero(len)) { _outlineMoveTo(*dash.outline, &dash.ptCur, transform); //draw the current line fully } else if (len <= dash.curLen) { dash.curLen -= len; if (!dash.curOpGap) { if (dash.move) { _outlineMoveTo(*dash.outline, &dash.ptCur, transform); dash.move = false; } _outlineLineTo(*dash.outline, to, transform); } //draw the current line partially } else { while (len - dash.curLen > DASH_PATTERN_THRESHOLD) { Line left, right; if (dash.curLen > 0) { len -= dash.curLen; cur.split(dash.curLen, left, right); if (!dash.curOpGap) { if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) { _outlineMoveTo(*dash.outline, &left.pt1, transform); dash.move = false; } _outlineLineTo(*dash.outline, &left.pt2, transform); } } else { if (validPoint && !dash.curOpGap) _drawPoint(dash, &cur.pt1, transform); right = cur; } dash.curIdx = (dash.curIdx + 1) % dash.cnt; dash.curLen = dash.pattern[dash.curIdx]; dash.curOpGap = !dash.curOpGap; cur = right; dash.ptCur = cur.pt1; dash.move = true; } //leftovers dash.curLen -= len; if (!dash.curOpGap) { if (dash.move) { _outlineMoveTo(*dash.outline, &cur.pt1, transform); dash.move = false; } _outlineLineTo(*dash.outline, &cur.pt2, transform); } if (dash.curLen < 1 && TO_SWCOORD(len) > 1) { //move to next dash dash.curIdx = (dash.curIdx + 1) % dash.cnt; dash.curLen = dash.pattern[dash.curIdx]; dash.curOpGap = !dash.curOpGap; } } dash.ptCur = *to; } static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform, bool validPoint) { Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to}; auto len = cur.length(); //draw the current line fully if (tvg::zero(len)) { _outlineMoveTo(*dash.outline, &dash.ptCur, transform); } else if (len <= dash.curLen) { dash.curLen -= len; if (!dash.curOpGap) { if (dash.move) { _outlineMoveTo(*dash.outline, &dash.ptCur, transform); dash.move = false; } _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform); } //draw the current line partially } else { while ((len - dash.curLen) > DASH_PATTERN_THRESHOLD) { Bezier left, right; if (dash.curLen > 0) { len -= dash.curLen; cur.split(dash.curLen, left, right); if (!dash.curOpGap) { if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) { _outlineMoveTo(*dash.outline, &left.start, transform); dash.move = false; } _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform); } } else { if (validPoint && !dash.curOpGap) _drawPoint(dash, &cur.start, transform); right = cur; } dash.curIdx = (dash.curIdx + 1) % dash.cnt; dash.curLen = dash.pattern[dash.curIdx]; dash.curOpGap = !dash.curOpGap; cur = right; dash.ptCur = right.start; dash.move = true; } //leftovers dash.curLen -= len; if (!dash.curOpGap) { if (dash.move) { _outlineMoveTo(*dash.outline, &cur.start, transform); dash.move = false; } _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform); } if (dash.curLen < 0.1f && TO_SWCOORD(len) > 1) { //move to next dash dash.curIdx = (dash.curIdx + 1) % dash.cnt; dash.curLen = dash.pattern[dash.curIdx]; dash.curOpGap = !dash.curOpGap; } } dash.ptCur = *to; } static void _dashClose(SwDashStroke& dash, const Matrix& transform, bool validPoint) { _dashLineTo(dash, &dash.ptStart, transform, validPoint); } static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts) { dash.curIdx = offIdx % dash.cnt; dash.curLen = dash.pattern[dash.curIdx] - offset; dash.curOpGap = offIdx % 2; dash.ptStart = dash.ptCur = *pts; dash.move = true; } static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid, bool trimmed) { PathCommand *cmds, *trimmedCmds = nullptr; Point *pts, *trimmedPts = nullptr; uint32_t cmdCnt, ptsCnt; if (trimmed) { RenderPath trimmedPath; if (!rshape->stroke->trim.trim(rshape->path, trimmedPath)) return nullptr; cmds = trimmedCmds = trimmedPath.cmds.data; cmdCnt = trimmedPath.cmds.count; pts = trimmedPts = trimmedPath.pts.data; ptsCnt = trimmedPath.pts.count; trimmedPath.cmds.data = nullptr; trimmedPath.pts.data = nullptr; } else { cmds = rshape->path.cmds.data; cmdCnt = rshape->path.cmds.count; pts = rshape->path.pts.data; ptsCnt = rshape->path.pts.count; } //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return nullptr; SwDashStroke dash; dash.pattern = rshape->stroke->dash.pattern; dash.cnt = rshape->stroke->dash.count; auto offset = rshape->stroke->dash.offset; //offset uint32_t offIdx = 0; if (!tvg::zero(offset)) { auto length = rshape->stroke->dash.length; bool isOdd = dash.cnt % 2; if (isOdd) length *= 2; offset = fmodf(offset, length); if (offset < 0) offset += length; for (size_t i = 0; i < dash.cnt * (1 + (size_t)isOdd); ++i, ++offIdx) { auto curPattern = dash.pattern[i % dash.cnt]; if (offset < curPattern) break; offset -= curPattern; } } dash.outline = mpoolReqOutline(mpool, tid); //must begin with moveTo if (cmds[0] == PathCommand::MoveTo) { _dashMoveTo(dash, offIdx, offset, pts); cmds++; pts++; } //zero length segment with non-butt cap still should be rendered as a point - only the caps are visible auto validPoint = rshape->stroke->cap != StrokeCap::Butt; while (--cmdCnt > 0) { switch (*cmds) { case PathCommand::Close: { _dashClose(dash, transform, validPoint); break; } case PathCommand::MoveTo: { _dashMoveTo(dash, offIdx, offset, pts); ++pts; break; } case PathCommand::LineTo: { _dashLineTo(dash, pts, transform, validPoint); ++pts; break; } case PathCommand::CubicTo: { _dashCubicTo(dash, pts, pts + 1, pts + 2, transform, validPoint); pts += 3; break; } } ++cmds; } _outlineEnd(*dash.outline); dash.outline->fillRule = rshape->rule; tvg::free(trimmedCmds); tvg::free(trimmedPts); return dash.outline; } static bool _axisAlignedRect(const SwOutline* outline) { //Fast Track: axis-aligned rectangle? if (outline->pts.count != 5) return false; if (outline->types[2] == SW_CURVE_TYPE_CUBIC) return false; auto pt1 = outline->pts.data + 0; auto pt2 = outline->pts.data + 1; auto pt3 = outline->pts.data + 2; auto pt4 = outline->pts.data + 3; auto a = SwPoint{pt1->x, pt3->y}; auto b = SwPoint{pt3->x, pt1->y}; if ((*pt2 == a && *pt4 == b) || (*pt2 == b && *pt4 == a)) return true; return false; } static SwOutline* _genOutline(SwShape& shape, const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid, bool hasComposite, bool trimmed = false) { PathCommand *cmds, *trimmedCmds = nullptr; Point *pts, *trimmedPts = nullptr; uint32_t cmdCnt, ptsCnt; if (trimmed) { RenderPath trimmedPath; if (!rshape->stroke->trim.trim(rshape->path, trimmedPath)) return nullptr; cmds = trimmedCmds = trimmedPath.cmds.data; cmdCnt = trimmedPath.cmds.count; pts = trimmedPts = trimmedPath.pts.data; ptsCnt = trimmedPath.pts.count; trimmedPath.cmds.data = nullptr; trimmedPath.pts.data = nullptr; } else { cmds = rshape->path.cmds.data; cmdCnt = rshape->path.cmds.count; pts = rshape->path.pts.data; ptsCnt = rshape->path.pts.count; } //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return nullptr; auto outline = mpoolReqOutline(mpool, tid); auto closed = false; //Generate Outlines while (cmdCnt-- > 0) { switch (*cmds) { case PathCommand::Close: { if (!closed) closed = _outlineClose(*outline); break; } case PathCommand::MoveTo: { closed = _outlineMoveTo(*outline, pts, transform, closed); ++pts; break; } case PathCommand::LineTo: { if (closed) closed = _outlineBegin(*outline); _outlineLineTo(*outline, pts, transform); ++pts; break; } case PathCommand::CubicTo: { if (closed) closed = _outlineBegin(*outline); _outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform); pts += 3; break; } } ++cmds; } if (!closed) _outlineEnd(*outline); outline->fillRule = rshape->rule; tvg::free(trimmedCmds); tvg::free(trimmedPts); shape.fastTrack = (!hasComposite && _axisAlignedRect(outline)); return outline; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool shapePrepare(SwShape& shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid, bool hasComposite) { if ((shape.outline = _genOutline(shape, rshape, transform, mpool, tid, hasComposite, rshape->trimpath()))) { if (mathUpdateOutlineBBox(shape.outline, clipBox, renderBox, shape.fastTrack)) { shape.bbox = renderBox; return true; } } return false; } bool shapeGenRle(SwShape& shape, const RenderRegion& bbox, SwMpool* mpool, unsigned tid, bool antiAlias) { //Case A: Fast Track Rectangle Drawing if (shape.fastTrack) return true; //Case B: Normal Shape RLE Drawing if ((shape.rle = rleRender(shape.rle, shape.outline, bbox, mpool, tid, antiAlias))) return true; return false; } void shapeDelOutline(SwShape& shape, SwMpool* mpool, uint32_t tid) { shape.outline = nullptr; } void shapeReset(SwShape& shape) { rleReset(shape.rle); shape.bbox.reset(); shape.fastTrack = false; } void shapeFree(SwShape& shape) { rleFree(shape.rle); shape.rle = nullptr; shapeDelFill(shape); if (shape.stroke) { rleFree(shape.strokeRle); shape.strokeRle = nullptr; strokeFree(shape.stroke); shape.stroke = nullptr; } } void shapeDelStroke(SwShape& shape) { if (!shape.stroke) return; rleFree(shape.strokeRle); shape.strokeRle = nullptr; strokeFree(shape.stroke); shape.stroke = nullptr; } void shapeResetStroke(SwShape& shape, const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid) { if (!shape.stroke) shape.stroke = tvg::calloc(1, sizeof(SwStroke)); auto stroke = shape.stroke; strokeReset(stroke, rshape, transform, mpool, tid); rleReset(shape.strokeRle); } bool shapeGenStrokeRle(SwShape& shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid) { SwOutline* shapeOutline = nullptr; //Dash style with/without trimming if (rshape->stroke->dash.length > DASH_PATTERN_THRESHOLD) { shapeOutline = _genDashOutline(rshape, transform, mpool, tid, rshape->trimpath()); //Trimming & Normal style } else { shapeOutline = shape.outline ? shape.outline : _genOutline(shape, rshape, transform, mpool, tid, false, rshape->trimpath()); } if (!shapeOutline) return false; if (!strokeParseOutline(shape.stroke, *shapeOutline, mpool, tid)) return false; auto strokeOutline = strokeExportOutline(shape.stroke, mpool, tid); auto ret = mathUpdateOutlineBBox(strokeOutline, clipBox, renderBox, false); if (ret) shape.strokeRle = rleRender(shape.strokeRle, strokeOutline, renderBox, mpool, tid, true); return ret; } bool shapeGenFillColors(SwShape& shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable) { return fillGenColorTable(shape.fill, fill, transform, surface, opacity, ctable); } bool shapeGenStrokeFillColors(SwShape& shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable) { return fillGenColorTable(shape.stroke->fill, fill, transform, surface, opacity, ctable); } void shapeResetFill(SwShape& shape) { if (!shape.fill) { shape.fill = tvg::calloc(1, sizeof(SwFill)); if (!shape.fill) return; } fillReset(shape.fill); } void shapeResetStrokeFill(SwShape& shape) { if (!shape.stroke->fill) { shape.stroke->fill = tvg::calloc(1, sizeof(SwFill)); if (!shape.stroke->fill) return; } fillReset(shape.stroke->fill); } void shapeDelFill(SwShape& shape) { if (!shape.fill) return; fillFree(shape.fill); shape.fill = nullptr; } bool shapeStrokeBBox(SwShape& shape, const RenderShape* rshape, Point* pt4, const Matrix& m, SwMpool* mpool) { auto outline = _genOutline(shape, rshape, m, mpool, 0, false, rshape->trimpath()); if (!outline) return false; if (rshape->strokeWidth() > 0.0f) { strokeReset(shape.stroke, rshape, m, mpool, 0); strokeParseOutline(shape.stroke, *outline, mpool, 0); auto func = [](SwStrokeBorder* border, SwPoint& min, SwPoint& max) { ARRAY_FOREACH(pts, border->pts) { if (pts->x < min.x) min.x = pts->x; if (pts->x > max.x) max.x = pts->x; if (pts->y < min.y) min.y = pts->y; if (pts->y > max.y) max.y = pts->y; } }; SwPoint min = {INT32_MAX, INT32_MAX}; SwPoint max = {-INT32_MAX, -INT32_MAX}; func(shape.stroke->borders[0], min, max); func(shape.stroke->borders[1], min, max); pt4[0] = min.toPoint(); pt4[1] = SwPoint{max.x, min.y}.toPoint(); pt4[2] = max.toPoint(); pt4[3] = SwPoint{min.x, max.y}.toPoint(); } shapeDelOutline(shape, mpool, 0); return true; }src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-iterator-prototype.cpp000664 001750 001750 00000006020 15164251010 052266 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-iterator-object.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BUILTIN_ITERATOR_PROTOTYPE_ROUTINE_START = 0, ECMA_BUILTIN_ITERATOR_PROTOTYPE_OBJECT_ITERATOR, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-iterator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID iterator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup %iteratorprototype% ECMA %IteratorPrototype% object built-in * @{ */ /** * The %IteratorPrototype% object's '@@iterator' routine * * See also: * ECMA-262 v6, 22.1.2.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return the given this value */ static ecma_value_t ecma_builtin_iterator_prototype_object_iterator (ecma_value_t this_val) /**< this argument */ { /* 1. */ return ecma_copy_value (this_val); } /* ecma_builtin_iterator_prototype_object_iterator */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_iterator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide * routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< * list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_list_p, arguments_number); switch (builtin_routine_id) { case ECMA_BUILTIN_ITERATOR_PROTOTYPE_OBJECT_ITERATOR: { return ecma_builtin_iterator_prototype_object_iterator (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_iterator_prototype_dispatch_routine */ /** * @} * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-bigint.h000664 001750 001750 00000007433 15164251010 044746 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BIG_INT_H #define ECMA_BIG_INT_H #include "ecma-globals.h" #if JERRY_BUILTIN_BIGINT /** * Sign bit of a BigInt value. The number is negative, if this bit is set. */ #define ECMA_BIGINT_SIGN 0x1 /** * Flags for ecma_bigint_parse_string. */ typedef enum { ECMA_BIGINT_PARSE_NO_OPTIONS = 0, /**< no options */ ECMA_BIGINT_PARSE_SET_NEGATIVE = (1 << 0), /**< return with a negative BigInt value */ ECMA_BIGINT_PARSE_DISALLOW_SYNTAX_ERROR = (1 << 1), /**< don't throw SyntaxError, * return with ECMA_VALUE_FALSE */ ECMA_BIGINT_PARSE_DISALLOW_MEMORY_ERROR = (1 << 2), /**< don't throw out-of-memory error, * return with ECMA_VALUE_NULL instead */ ECMA_BIGINT_PARSE_ALLOW_UNDERSCORE = (1 << 3), /** allow parse underscore characters */ } ecma_bigint_parse_string_options_t; /** * Types for unary operations */ typedef enum { ECMA_BIGINT_UNARY_BITWISE_NOT, /**< bitwise not operation */ ECMA_BIGINT_UNARY_INCREASE, /**< increase operation */ ECMA_BIGINT_UNARY_DECREASE, /**< decrease operation */ } ecma_bigint_unary_operation_type; ecma_value_t ecma_bigint_parse_string (const lit_utf8_byte_t *string_p, lit_utf8_size_t size, uint32_t options); ecma_value_t ecma_bigint_parse_string_value (ecma_value_t string, uint32_t options); ecma_string_t *ecma_bigint_to_string (ecma_value_t value, ecma_bigint_digit_t radix); ecma_value_t ecma_bigint_to_bigint (ecma_value_t value, bool allow_numbers); ecma_value_t ecma_bigint_to_number (ecma_value_t value); ecma_value_t ecma_bigint_get_bigint (ecma_value_t value, bool *free_result_p); ecma_value_t ecma_bigint_create_from_digits (const uint64_t *digits_p, uint32_t size, bool sign); uint32_t ecma_bigint_get_size_in_digits (ecma_value_t value); void ecma_bigint_get_digits_and_sign (ecma_value_t value, uint64_t *digits_p, uint32_t size, bool *sign_p); bool ecma_bigint_is_equal_to_bigint (ecma_value_t left_value, ecma_value_t right_value); bool ecma_bigint_is_equal_to_number (ecma_value_t left_value, ecma_number_t right_value); int ecma_bigint_compare_to_bigint (ecma_value_t left_value, ecma_value_t right_value); int ecma_bigint_compare_to_number (ecma_value_t left_value, ecma_number_t right_value); ecma_value_t ecma_bigint_negate (ecma_extended_primitive_t *value_p); ecma_value_t ecma_bigint_unary (ecma_value_t value, ecma_bigint_unary_operation_type type); ecma_value_t ecma_bigint_add_sub (ecma_value_t left_value, ecma_value_t right_value, bool is_add); ecma_value_t ecma_bigint_mul (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t ecma_bigint_div_mod (ecma_value_t left_value, ecma_value_t right_value, bool is_mod); ecma_value_t ecma_bigint_shift (ecma_value_t left_value, ecma_value_t right_value, bool is_left); ecma_value_t ecma_bigint_pow (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t ecma_bigint_and (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t ecma_bigint_or (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t ecma_bigint_xor (ecma_value_t left_value, ecma_value_t right_value); #endif /* JERRY_BUILTIN_BIGINT */ #endif /* ECMA_BIG_INT_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgPipelines.h000664 001750 001750 00000014437 15164251010 036626 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_PIPELINES_H_ #define _TVG_WG_PIPELINES_H_ #include "tvgWgCommon.h" class WgPipelines { private: // shaders helpers WGPUShaderModule shader_stencil{}; WGPUShaderModule shader_depth{}; // shaders normal blend WGPUShaderModule shader_solid{}; WGPUShaderModule shader_radial{}; WGPUShaderModule shader_linear{}; WGPUShaderModule shader_image{}; WGPUShaderModule shader_scene{}; // shaders custom blend WGPUShaderModule shader_solid_blend{}; WGPUShaderModule shader_radial_blend{}; WGPUShaderModule shader_linear_blend{}; WGPUShaderModule shader_image_blend{}; WGPUShaderModule shader_scene_blend{}; // shader scene compose WGPUShaderModule shader_scene_compose{}; // shader blit WGPUShaderModule shader_blit{}; // shader effects WGPUShaderModule shader_shadow; WGPUShaderModule shader_effects; // layouts helpers WGPUPipelineLayout layout_stencil{}; WGPUPipelineLayout layout_depth{}; // layouts normal blend WGPUPipelineLayout layout_solid{}; WGPUPipelineLayout layout_gradient{}; WGPUPipelineLayout layout_image{}; WGPUPipelineLayout layout_scene{}; // layouts custom blend WGPUPipelineLayout layout_solid_blend{}; WGPUPipelineLayout layout_gradient_blend{}; WGPUPipelineLayout layout_image_blend{}; WGPUPipelineLayout layout_scene_blend{}; // layouts scene compose WGPUPipelineLayout layout_scene_compose{}; // layouts blit WGPUPipelineLayout layout_blit{}; // layouts effects WGPUPipelineLayout layout_shadow{}; WGPUPipelineLayout layout_effects{}; public: // pipelines stencil markup WGPURenderPipeline nonzero{}; WGPURenderPipeline evenodd{}; WGPURenderPipeline direct{}; // pipelines clip path markup WGPURenderPipeline copy_stencil_to_depth{}; // depth 0.50, clear stencil WGPURenderPipeline copy_stencil_to_depth_interm{}; // depth 0.75, clear stencil WGPURenderPipeline copy_depth_to_stencil{}; // depth 0.50 and 0.75, update stencil WGPURenderPipeline merge_depth_stencil{}; // depth 0.75, update stencil WGPURenderPipeline clear_depth{}; // depth 1.00, clear ctencil // pipelines normal blend WGPURenderPipeline solid{}; WGPURenderPipeline radial{}; WGPURenderPipeline linear{}; WGPURenderPipeline solid_conv{}; // convex geometry (no stencil) WGPURenderPipeline radial_conv{}; // convex geometry (no stencil) WGPURenderPipeline linear_conv{}; // convex geometry (no stencil) WGPURenderPipeline image{}; WGPURenderPipeline scene{}; // pipelines custom blend WGPURenderPipeline solid_blend[18]{}; WGPURenderPipeline radial_blend[18]{}; WGPURenderPipeline linear_blend[18]{}; WGPURenderPipeline image_blend[18]{}; WGPURenderPipeline scene_blend[18]{}; // pipelines compose WGPURenderPipeline scene_compose[11]{}; // pipeline blit WGPURenderPipeline blit{}; // effects WGPURenderPipeline gaussian_vert{}; WGPURenderPipeline gaussian_horz{}; WGPURenderPipeline dropshadow{}; WGPURenderPipeline fill_effect{}; WGPURenderPipeline tint_effect{}; WGPURenderPipeline tritone_effect{}; private: void releaseGraphicHandles(WgContext& context); WGPUShaderModule createShaderModule(WGPUDevice device, const char* label, const char* code); WGPUPipelineLayout createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, const uint32_t bindGroupLayoutsCount); WGPURenderPipeline createRenderPipeline( WGPUDevice device, const char* pipelineLabel, const WGPUShaderModule shaderModule, const char* vsEntryPoint, const char* fsEntryPoint, const WGPUPipelineLayout pipelineLayout, const WGPUVertexBufferLayout *vertexBufferLayouts, const uint32_t vertexBufferLayoutsCount, const WGPUColorWriteMask writeMask, const WGPUTextureFormat colorTargetFormat, const WGPUBlendState blendState, const WGPUDepthStencilState depthStencilState, const WGPUMultisampleState multisampleState); WGPUComputePipeline createComputePipeline( WGPUDevice device, const char* pipelineLabel, const WGPUShaderModule shaderModule, const char* entryPoint, const WGPUPipelineLayout pipelineLayout); void releaseComputePipeline(WGPUComputePipeline& computePipeline); void releaseRenderPipeline(WGPURenderPipeline& renderPipeline); void releasePipelineLayout(WGPUPipelineLayout& pipelineLayout); void releaseShaderModule(WGPUShaderModule& shaderModule); WGPUDepthStencilState makeDepthStencilState( const WGPUCompareFunction depthCompare, WGPUOptionalBool depthWriteEnabled, const WGPUCompareFunction stencilFunctionFrnt, const WGPUStencilOperation stencilOperationFrnt); WGPUDepthStencilState makeDepthStencilState( const WGPUCompareFunction depthCompare, WGPUOptionalBool depthWriteEnabled, const WGPUCompareFunction stencilFunctionFrnt, const WGPUStencilOperation stencilOperationFrnt, const WGPUCompareFunction stencilFunctionBack, const WGPUStencilOperation stencilOperationBack); public: void initialize(WgContext& context); void release(WgContext& context); }; #endif // _TVG_WG_PIPELINES_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/rescaler.cpp000664 001750 001750 00000007603 15164251010 035413 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2014 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Rescaling functions #include "./dsp.h" #include "../utils/rescaler.h" //------------------------------------------------------------------------------ // Implementations of critical functions ImportRow / ExportRow #define ROUNDER (1 << (WEBP_RESCALER_RFIX - 1)) #define MULT_FIX(x, y) (((int64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) static void RescalerImportRowC(WebPRescaler* const wrk, const uint8_t* const src, int channel) { const int x_stride = wrk->num_channels; const int x_out_max = wrk->dst_width * wrk->num_channels; int x_in = channel; int x_out; int accum = 0; if (!wrk->x_expand) { int sum = 0; for (x_out = channel; x_out < x_out_max; x_out += x_stride) { accum += wrk->x_add; for (; accum > 0; accum -= wrk->x_sub) { sum += src[x_in]; x_in += x_stride; } { // Emit next horizontal pixel. const int32_t base = src[x_in]; const int32_t frac = base * (-accum); x_in += x_stride; wrk->frow[x_out] = (sum + base) * wrk->x_sub - frac; // fresh fractional start for next pixel sum = (int)MULT_FIX(frac, wrk->fx_scale); } } } else { // simple bilinear interpolation int left = src[channel], right = src[channel]; for (x_out = channel; x_out < x_out_max; x_out += x_stride) { if (accum < 0) { left = right; x_in += x_stride; right = src[x_in]; accum += wrk->x_add; } wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum; accum -= wrk->x_sub; } } // Accumulate the contribution of the new row. for (x_out = channel; x_out < x_out_max; x_out += x_stride) { wrk->irow[x_out] += wrk->frow[x_out]; } } void WebPRescalerExportRowC(WebPRescaler* const wrk, int x_out) { if (wrk->y_accum <= 0) { uint8_t* const dst = wrk->dst; int32_t* const irow = wrk->irow; const int32_t* const frow = wrk->frow; const int yscale = wrk->fy_scale * (-wrk->y_accum); const int x_out_max = wrk->dst_width * wrk->num_channels; for (; x_out < x_out_max; ++x_out) { const int frac = (int)MULT_FIX(frow[x_out], yscale); const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale); dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; irow[x_out] = frac; // new fractional start } wrk->y_accum += wrk->y_add; wrk->dst += wrk->dst_stride; } } #undef MULT_FIX #undef ROUNDER //------------------------------------------------------------------------------ void (*WebPRescalerImportRow)(struct WebPRescaler* const wrk, const uint8_t* const src, int channel); void (*WebPRescalerExportRow)(struct WebPRescaler* const wrk, int x_out); extern void WebPRescalerDspInitMIPS32(void); extern void WebPRescalerDspInitMIPSdspR2(void); static volatile VP8CPUInfo rescaler_last_cpuinfo_used = (VP8CPUInfo)&rescaler_last_cpuinfo_used; WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) { if (rescaler_last_cpuinfo_used == VP8GetCPUInfo) return; WebPRescalerImportRow = RescalerImportRowC; WebPRescalerExportRow = WebPRescalerExportRowC; if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_MIPS32) if (VP8GetCPUInfo(kMIPS32)) { WebPRescalerDspInitMIPS32(); } #endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { WebPRescalerDspInitMIPSdspR2(); } #endif } rescaler_last_cpuinfo_used = VP8GetCPUInfo; } external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-lex-env.h000664 001750 001750 00000006715 15164251010 045052 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_LEX_ENV_H #define ECMA_LEX_ENV_H #include "ecma-globals.h" #include "ecma-reference.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup lexicalenvironment Lexical environment * @{ * * \addtogroup globallexicalenvironment Global lexical environment * @{ */ void ecma_init_global_environment (void); void ecma_finalize_global_environment (void); ecma_object_t *ecma_get_global_environment (ecma_object_t *global_object_p); ecma_object_t *ecma_get_global_scope (ecma_object_t *global_object_p); void ecma_create_global_lexical_block (ecma_object_t *global_object_p); ecma_value_t ecma_op_raise_set_binding_error (ecma_property_t *property_p, bool is_strict); #if JERRY_MODULE_SYSTEM void ecma_module_add_lex_env (ecma_object_t *lex_env_p); void ecma_module_finalize_lex_envs (void); #endif /* JERRY_MODULE_SYSTEM */ /** * @} */ /* ECMA-262 v5, 8.7.1 and 8.7.2 */ ecma_value_t ecma_op_get_value_lex_env_base (ecma_object_t *lex_env_p, ecma_object_t **ref_base_lex_env_p, ecma_string_t *name_p); ecma_value_t ecma_op_get_value_object_base (ecma_value_t base_value, ecma_string_t *property_name_p); ecma_value_t ecma_op_put_value_lex_env_base (ecma_object_t *lex_env_p, ecma_string_t *var_name_string_p, bool is_strict, ecma_value_t value); /* ECMA-262 v5, Table 17. Abstract methods of Environment Records */ ecma_value_t ecma_op_has_binding (ecma_object_t *lex_env_p, ecma_string_t *name_p); ecma_property_t *ecma_op_create_mutable_binding (ecma_object_t *lex_env_p, ecma_string_t *name_p, bool is_deletable); ecma_value_t ecma_op_set_mutable_binding (ecma_object_t *lex_env_p, ecma_string_t *name_p, ecma_value_t value, bool is_strict); ecma_value_t ecma_op_get_binding_value (ecma_object_t *lex_env_p, ecma_string_t *name_p, bool is_strict); ecma_value_t ecma_op_delete_binding (ecma_object_t *lex_env_p, ecma_string_t *name_p); ecma_value_t ecma_op_implicit_this_value (ecma_object_t *lex_env_p); /* ECMA-262 v5, Table 18. Additional methods of Declarative Environment Records */ void ecma_op_create_immutable_binding (ecma_object_t *lex_env_p, ecma_string_t *name_p, ecma_value_t value); void ecma_op_initialize_binding (ecma_object_t *lex_env_p, ecma_string_t *name_p, ecma_value_t value); void ecma_op_create_environment_record (ecma_object_t *lex_env_p, ecma_value_t this_binding, ecma_object_t *func_obj_p); ecma_environment_record_t *ecma_op_get_environment_record (ecma_object_t *lex_env_p); bool ecma_op_this_binding_is_initialized (ecma_environment_record_t *environment_record_p); void ecma_op_bind_this_value (ecma_environment_record_t *environment_record_p, ecma_value_t this_binding); ecma_value_t ecma_op_get_this_binding (ecma_object_t *lex_env_p); /** * @} * @} */ #endif /* !ECMA_LEX_ENV_H */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-map.inc.h000664 001750 001750 00000002722 15164251010 047371 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Map built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER /* Number properties: * (property name, number value, writable, enumerable, configurable) */ /* ECMA-262 v6, 23.1.2 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 23.1 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_MAP_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 23.1.2.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_MAP_PROTOTYPE, ECMA_PROPERTY_FIXED) /* ECMA-262 v6, 23.1.2.2 */ ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_SPECIES, ecma_builtin_map_species_get, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/bit_reader_inl.h000664 001750 001750 00000011573 15164251010 036575 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2014 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Specific inlined methods for boolean decoder [VP8GetBit() ...] // This file should be included by the .c sources that actually need to call // these methods. // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_UTILS_BIT_READER_INL_H_ #define WEBP_UTILS_BIT_READER_INL_H_ #ifdef HAVE_CONFIG_H #include "../webp/config.h" #endif #include // memcpy #include "../dsp/dsp.h" #include "./bit_reader.h" #include "./endian_inl.h" #ifdef __cplusplus extern "C" { #endif //------------------------------------------------------------------------------ // Derived type lbit_t = natural type for memory I/O #if (BITS > 32) typedef uint64_t lbit_t; #elif (BITS > 16) typedef uint32_t lbit_t; #elif (BITS > 8) typedef uint16_t lbit_t; #else typedef uint8_t lbit_t; #endif extern const uint8_t kVP8Log2Range[128]; extern const uint8_t kVP8NewRange[128]; // special case for the tail byte-reading void VP8LoadFinalBytes(VP8BitReader* const br); //------------------------------------------------------------------------------ // Inlined critical functions // makes sure br->value_ has at least BITS bits worth of data static WEBP_INLINE void VP8LoadNewBytes(VP8BitReader* const br) { assert(br != NULL && br->buf_ != NULL); // Read 'BITS' bits at a time if possible. if (br->buf_ + sizeof(lbit_t) <= br->buf_end_) { // convert memory type to register type (with some zero'ing!) bit_t bits; #if defined(WEBP_USE_MIPS32) // This is needed because of un-aligned read. lbit_t in_bits; lbit_t* p_buf_ = (lbit_t*)br->buf_; __asm__ volatile( ".set push \n\t" ".set at \n\t" ".set macro \n\t" "ulw %[in_bits], 0(%[p_buf_]) \n\t" ".set pop \n\t" : [in_bits]"=r"(in_bits) : [p_buf_]"r"(p_buf_) : "memory", "at" ); #else lbit_t in_bits; memcpy(&in_bits, br->buf_, sizeof(in_bits)); #endif br->buf_ += BITS >> 3; #if !defined(WORDS_BIGENDIAN) #if (BITS > 32) bits = BSwap64(in_bits); bits >>= 64 - BITS; #elif (BITS >= 24) bits = BSwap32(in_bits); bits >>= (32 - BITS); #elif (BITS == 16) bits = BSwap16(in_bits); #else // BITS == 8 bits = (bit_t)in_bits; #endif // BITS > 32 #else // WORDS_BIGENDIAN bits = (bit_t)in_bits; if (BITS != 8 * sizeof(bit_t)) bits >>= (8 * sizeof(bit_t) - BITS); #endif br->value_ = bits | (br->value_ << BITS); br->bits_ += BITS; } else { VP8LoadFinalBytes(br); // no need to be inlined } } // Read a bit with proba 'prob'. Speed-critical function! static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) { // Don't move this declaration! It makes a big speed difference to store // 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't // alter br->range_ value. range_t range = br->range_; if (br->bits_ < 0) { VP8LoadNewBytes(br); } { const int pos = br->bits_; const range_t split = (range * prob) >> 8; const range_t value = (range_t)(br->value_ >> pos); #if defined(__arm__) || defined(_M_ARM) // ARM-specific const int bit = ((int)(split - value) >> 31) & 1; if (value > split) { range -= split + 1; br->value_ -= (bit_t)(split + 1) << pos; } else { range = split; } #else // faster version on x86 int bit; // Don't use 'const int bit = (value > split);", it's slower. if (value > split) { range -= split + 1; br->value_ -= (bit_t)(split + 1) << pos; bit = 1; } else { range = split; bit = 0; } #endif if (range <= (range_t)0x7e) { const int shift = kVP8Log2Range[range]; range = kVP8NewRange[range]; br->bits_ -= shift; } br->range_ = range; return bit; } } // simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here) static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) { if (br->bits_ < 0) { VP8LoadNewBytes(br); } { const int pos = br->bits_; const range_t split = br->range_ >> 1; const range_t value = (range_t)(br->value_ >> pos); const int32_t mask = (int32_t)(split - value) >> 31; // -1 or 0 br->bits_ -= 1; br->range_ += mask; br->range_ |= 1; br->value_ -= (bit_t)((split + 1) & mask) << pos; return (v ^ mask) - mask; } } #ifdef __cplusplus } // extern "C" #endif #endif // WEBP_UTILS_BIT_READER_INL_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/000775 001750 001750 00000000000 15164251010 036463 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgShaderTypes.h000664 001750 001750 00000010521 15164251010 037117 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_SHADER_TYPES_H_ #define _TVG_WG_SHADER_TYPES_H_ #include "tvgRender.h" /////////////////////////////////////////////////////////////////////////////// // shader types /////////////////////////////////////////////////////////////////////////////// // mat4x4f struct WgShaderTypeMat4x4f { float mat[16]{}; WgShaderTypeMat4x4f(); WgShaderTypeMat4x4f(const Matrix& transform); WgShaderTypeMat4x4f(size_t w, size_t h); void identity(); void update(const Matrix& transform); void update(size_t w, size_t h); }; // vec4f struct WgShaderTypeVec4f { float vec[4]{}; WgShaderTypeVec4f() {}; WgShaderTypeVec4f(const ColorSpace colorSpace, uint8_t o); WgShaderTypeVec4f(const RenderColor& c); void update(const ColorSpace colorSpace, uint8_t o); void update(const RenderColor& c); void update(const RenderRegion& r); }; // WGSL: struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f }; struct WgShaderTypeGradSettings { // gradient transform matrix WgShaderTypeMat4x4f transform; // linear: [0] - x1, [1] - y1, [2] - x2, [3] - y2 // radial: [0] - cx, [1] - cy, [2] - cr WgShaderTypeVec4f coords; // radial: [0] - fx, [1] - fy, [2] - fr WgShaderTypeVec4f focal; void update(const Fill* fill); }; // WGSL: struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings }; struct WgShaderTypePaintSettings { // paint transform matrix (must be at offset 0) WgShaderTypeMat4x4f transform; // [0] - color space, [3] - opacity WgShaderTypeVec4f options; // solid color WgShaderTypeVec4f color; // gradient settings (linear/radial) WgShaderTypeGradSettings gradient; // align to 256 bytes (see webgpu spec: minUniformBufferOffsetAlignment) uint8_t _padding[256 - sizeof(transform) - sizeof(options) - sizeof(color) - sizeof(gradient)]; }; // see webgpu spec: 3.6.2. Limits - minUniformBufferOffsetAlignment (256) static_assert(sizeof(WgShaderTypePaintSettings) == 256, "Uniform shader data type size must be aligned to 256 bytes"); // gradient color map #define WG_TEXTURE_GRADIENT_SIZE 512 struct WgShaderTypeGradientData { uint8_t data[WG_TEXTURE_GRADIENT_SIZE * 4]; void update(const Fill* fill); }; // gaussian params: sigma, scale, extend #define WG_GAUSSIAN_KERNEL_SIZE_MAX (128.0f) // gaussian blur, drop shadow, fill, tint, tritone struct WgShaderTypeEffectParams { // gaussian blur: [0]: sigma, [1]: scale, [2]: kernel size // drop shadow: [0]: sigma, [1]: scale, [2]: kernel size, [4..7]: color, [8, 9]: offset // fill: [0..3]: color // tint: [0..2]: black, [4..6]: white, [8]: intensity // tritone: [0..2]: shadow, [4..6]: midtone, [8..10]: highlight float params[4+4+4]{}; // settings: array; uint32_t extend{}; // gaussian blur extend Point offset{}; // drop shadow offset bool update(RenderEffectGaussianBlur* gaussian, const Matrix& transform); bool update(RenderEffectDropShadow* dropShadow, const Matrix& transform); bool update(RenderEffectFill* fill); bool update(RenderEffectTint* tint); bool update(RenderEffectTritone* tritone); }; #endif // _TVG_WG_SHADER_TYPES_H_ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/meson.build000664 001750 001750 00000003547 15164251010 044562 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimatesource_file = [ 'ecma-arguments-object.h', 'ecma-array-object.h', 'ecma-arraybuffer-object.h', 'ecma-async-generator-object.h', 'ecma-atomics-object.h', 'ecma-big-uint.h', 'ecma-bigint-object.h', 'ecma-bigint.h', 'ecma-boolean-object.h', 'ecma-comparison.h', 'ecma-container-object.h', 'ecma-conversion.h', 'ecma-dataview-object.h', 'ecma-eval.h', 'ecma-exceptions.h', 'ecma-function-object.h', 'ecma-iterator-object.h', 'ecma-jobqueue.h', 'ecma-lex-env.h', 'ecma-number-object.h', 'ecma-objects-general.h', 'ecma-objects.h', 'ecma-promise-object.h', 'ecma-proxy-object.h', 'ecma-reference.h', 'ecma-regexp-object.h', 'ecma-shared-arraybuffer-object.h', 'ecma-string-object.h', 'ecma-symbol-object.h', 'ecma-typedarray-object.h', 'ecma-arguments-object.cpp', 'ecma-array-object.cpp', 'ecma-arraybuffer-object.cpp', 'ecma-async-generator-object.cpp', 'ecma-atomics-object.cpp', 'ecma-big-uint.cpp', 'ecma-bigint-object.cpp', 'ecma-bigint.cpp', 'ecma-boolean-object.cpp', 'ecma-comparison.cpp', 'ecma-container-object.cpp', 'ecma-conversion.cpp', 'ecma-dataview-object.cpp', 'ecma-eval.cpp', 'ecma-exceptions.cpp', 'ecma-function-object.cpp', 'ecma-get-put-value.cpp', 'ecma-iterator-object.cpp', 'ecma-jobqueue.cpp', 'ecma-lex-env.cpp', 'ecma-number-object.cpp', 'ecma-objects-general.cpp', 'ecma-objects.cpp', 'ecma-promise-object.cpp', 'ecma-proxy-object.cpp', 'ecma-reference.cpp', 'ecma-regexp-object.cpp', 'ecma-shared-arraybuffer-object.cpp', 'ecma-string-object.cpp', 'ecma-symbol-object.cpp', 'ecma-typedarray-object.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/ttf/tvgTtfLoader.cpp000664 001750 001750 00000043715 15164251010 035355 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgStr.h" #include "tvgTtfLoader.h" #if defined(__linux__) #include #include #include #include #endif /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #define SPACE_GLYPH_IDX 1 #define LINE_FEED_GLYPH_IDX 10 #define DOT_GLYPH_IDX 46 #ifdef THORVG_FILE_IO_SUPPORT #if defined(_WIN32) && (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) static bool _map(TtfLoader* loader, const string& path) { auto& reader = loader->reader; auto file = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (file == INVALID_HANDLE_VALUE) return false; DWORD high; auto low = GetFileSize(file, &high); if (low == INVALID_FILE_SIZE) { CloseHandle(file); return false; } reader.size = (uint32_t)((size_t)high << (8 * sizeof(DWORD)) | low); loader->mapping = (uint8_t*)CreateFileMapping(file, NULL, PAGE_READONLY, high, low, NULL); CloseHandle(file); if (!loader->mapping) return false; loader->reader.data = (uint8_t*) MapViewOfFile(loader->mapping, FILE_MAP_READ, 0, 0, 0); if (!loader->reader.data) { CloseHandle(loader->mapping); loader->mapping = nullptr; return false; } return true; } static void _unmap(TtfLoader* loader) { auto& reader = loader->reader; if (reader.data) { UnmapViewOfFile(reader.data); reader.data = nullptr; } if (loader->mapping) { CloseHandle(loader->mapping); loader->mapping = nullptr; } } #elif defined(__linux__) static bool _map(TtfLoader* loader, const char* path) { auto& reader = loader->reader; auto fd = open(path, O_RDONLY); if (fd < 0) return false; struct stat info; if (fstat(fd, &info) < 0) { close(fd); return false; } reader.data = (uint8_t*)mmap(NULL, (size_t) info.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (reader.data == (uint8_t*)-1) return false; reader.size = (uint32_t) info.st_size; close(fd); return true; } static void _unmap(TtfLoader* loader) { auto& reader = loader->reader; if (reader.data == (uint8_t*) -1) return; munmap((void *) reader.data, reader.size); reader.data = (uint8_t*)-1; reader.size = 0; } #else static bool _map(TtfLoader* loader, const char* path) { auto& reader = loader->reader; auto f = fopen(path, "rb"); if (!f) return false; fseek(f, 0, SEEK_END); reader.size = ftell(f); if (reader.size == 0) { fclose(f); return false; } reader.data = tvg::malloc(reader.size); fseek(f, 0, SEEK_SET); auto ret = fread(reader.data, sizeof(char), reader.size, f); if (ret < reader.size) { fclose(f); return false; } fclose(f); return true; } static void _unmap(TtfLoader* loader) { auto& reader = loader->reader; tvg::free(reader.data); reader.data = nullptr; reader.size = 0; } #endif #endif //THORVG_FILE_IO_SUPPORT static size_t _codepoints(char** utf8) { auto p = *utf8; if (!(*p & 0x80U)) { (*utf8) += 1; return *p; } else if ((*p & 0xe0U) == 0xc0U) { (*utf8) += 2; return ((*p & 0x1fU) << 6) + (*(p + 1) & 0x3fU); } else if ((*p & 0xf0U) == 0xe0U) { (*utf8) += 3; return ((*p & 0x0fU) << 12) + ((*(p + 1) & 0x3fU) << 6) + (*(p + 2) & 0x3fU); } else if ((*p & 0xf8U) == 0xf0U) { (*utf8) += 4; return ((*p & 0x07U) << 18) + ((*(p + 1) & 0x3fU) << 12) + ((*(p + 2) & 0x3fU) << 6) + (*(p + 3) & 0x3fU); } //error case (*utf8) += 1; return 0; } static void _build(const RenderPath& in, const Point& cursor, const Point& kerning, RenderPath& out) { out.cmds.push(in.cmds); out.pts.grow(in.pts.count); ARRAY_FOREACH(p, in.pts) { out.pts.push(*p + cursor + kerning); } } static void _align(const Point& align, const Point& box, const Point& cursor, uint32_t begin, uint32_t end, RenderPath& out) { if (align.x > 0.0f || align.y > 0.0f) { auto shift = (box - cursor) * align; for (auto p = out.pts.data + begin; p < out.pts.data + end; p++) { *p += shift; } } } static void _alignX(float align, float box, float x, uint32_t begin, uint32_t end, RenderPath& out) { if (align > 0.0f) { auto shift = (box - x) * align; for (auto p = out.pts.data + begin; p < out.pts.data + end; p++) { p->x += shift; } } } static void _alignY(float align, float box, float y, uint32_t begin, uint32_t end, RenderPath& out) { if (align > 0.0f) { auto shift = (box - y) * align; for (auto p = out.pts.data + begin; p < out.pts.data + end; p++) { p->y += shift; } } } uint32_t TtfLoader::feedLine(FontMetrics& fm, float box, float x, uint32_t begin, uint32_t end, Point& cursor, uint32_t& loc, RenderPath& out) { _alignX(fm.align.x, box, x, begin, end, out); //align the given line cursor.x = 0.0f; cursor.y += reader.metrics.hhea.advance * fm.spacing.y; ++loc; return out.pts.count; } void TtfLoader::clear() { if (nomap) { if (freeData) tvg::free(reader.data); reader.data = nullptr; reader.size = 0; freeData = false; nomap = false; } else { #ifdef THORVG_FILE_IO_SUPPORT _unmap(this); #endif } tvg::free(name); name = nullptr; } TtfGlyphMetrics* TtfLoader::request(uint32_t code) { if (code == 0) return nullptr; auto it = glyphs.find(code); if (it == glyphs.end()) { auto it = glyphs.emplace(code, TtfGlyphMetrics{}).first; auto rtgm = &it->second; if (!reader.convert(rtgm->path, *rtgm, reader.glyph(code, rtgm), {0.0f, 0.0f}, 1U)) { TVGERR("TTF", "invalid glyph id, codepoint(0x%x)", code); glyphs.erase(it); return nullptr; } return rtgm; } return &it->second; } void TtfLoader::wrapNone(FontMetrics& fm, const Point& box, char* utf8, RenderPath& out) { TtfGlyphMetrics* ltgm = nullptr; //left side glyph between the two adjacent glyphs Point cursor = {}; uint32_t line = 0; //the begin pos of the last line among path uint32_t loc = 1; //line counter while (*utf8) { auto code = _codepoints(&utf8); if (code == LINE_FEED_GLYPH_IDX) { line = feedLine(fm, box.x, cursor.x, line, out.pts.count, cursor, loc, out); continue; } auto rtgm = request(code); //right side glyph between the two adjacent glyphs if (!rtgm) continue; Point kerning{}; if (ltgm) reader.kerning(ltgm->idx, rtgm->idx, kerning); _build(rtgm->path, cursor, kerning, out); cursor.x += (rtgm->advance + kerning.x) * fm.spacing.x; if (cursor.x > fm.size.x) fm.size.x = cursor.x; //text horizontal size //store the base glyph width for italic transform if (!ltgm && rtgm->w > 0.0f) static_cast(fm.engine)->baseWidth = rtgm->w; ltgm = rtgm; } fm.size.y = height(loc, fm.spacing.y); _alignY(fm.align.y, box.y, fm.size.y, 0, line, out); //before the last line _align(fm.align, box, {cursor.x, fm.size.y}, line, out.pts.count, out); //last line } void TtfLoader::wrapChar(FontMetrics& fm, const Point& box, char* utf8, RenderPath& out) { TtfGlyphMetrics* ltgm = nullptr; //left side glyph between the two adjacent glyphs uint32_t line = 0; //the begin pos of the last line among path uint32_t loc = 1; //line counter Point cursor = {}; while (*utf8) { auto code = _codepoints(&utf8); if (code == LINE_FEED_GLYPH_IDX) { line = feedLine(fm, box.x, cursor.x, line, out.pts.count, cursor, loc, out); continue; } auto rtgm = request(code); //right side glyph between the two adjacent glyphs if (!rtgm) continue; Point kerning{}; if (ltgm) reader.kerning(ltgm->idx, rtgm->idx, kerning); auto xadv = (rtgm->advance + kerning.x) * fm.spacing.x; //normal scenario if (xadv < box.x) { if (cursor.x + xadv > box.x) { line = feedLine(fm, box.x, cursor.x, line, out.pts.count, cursor, loc, out); } _build(rtgm->path, cursor, kerning, out); cursor.x += xadv; //not enough layout space, force pushing } else { _build(rtgm->path, cursor, kerning, out); line = feedLine(fm, box.x, cursor.x, line, out.pts.count, cursor, loc, out); } if (cursor.x > fm.size.x) fm.size.x = cursor.x; //text horizontal size //store the base glyph width for italic transform if (!ltgm && rtgm->w > 0.0f) static_cast(fm.engine)->baseWidth = rtgm->w; ltgm = rtgm; } fm.size.y = height(loc, fm.spacing.y); _alignY(fm.align.y, box.y, fm.size.y, 0, line, out); //before the last line _align(fm.align, box, {cursor.x, fm.size.y}, line, out.pts.count, out); //last line } void TtfLoader::wrapWord(FontMetrics& fm, const Point& box, char* utf8, RenderPath& out, bool smart) { TtfGlyphMetrics* ltgm = nullptr; //left side glyph between the two adjacent glyphs auto line = 0; //the begin pos of the last line among path auto word = 0; //the begin pos of the last word among path auto wadv = 0.0f; //word advance size auto hadv = reader.metrics.hhea.advance * fm.spacing.y; //line advance size uint32_t loc = 1; //line counter Point cursor = {}; while (*utf8) { auto code = _codepoints(&utf8); if (code == LINE_FEED_GLYPH_IDX) { line = feedLine(fm, box.x, cursor.x, line, out.pts.count, cursor, loc, out); continue; } auto rtgm = request(code); //right side glyph between the two adjacent glyphs if (!rtgm) continue; Point kerning{}; if (ltgm) reader.kerning(ltgm->idx, rtgm->idx, kerning); auto xadv = (rtgm->advance + kerning.x) * fm.spacing.x; //try line-wrap if (cursor.x + xadv > box.x) { //wrapping only if enough space for the current word if ((cursor.x - wadv) + xadv < box.x) { _alignX(fm.align.x, box.x, wadv, line, word, out); //align the current line //shift the wrapping word to the next line for (auto p = out.pts.begin() + word; p < out.pts.end(); p++) { p->x -= wadv; p->y += hadv; } cursor.x -= wadv; cursor.y += hadv; line = word; wadv = 0; ++loc; //not enough space, line wrap by character } else if (smart) { line = feedLine(fm, box.x, cursor.x, line, out.pts.count, cursor, loc, out); } } _build(rtgm->path, cursor, kerning, out); cursor.x += xadv; //capture the word start if (rtgm->idx == SPACE_GLYPH_IDX) { word = out.pts.count; wadv = cursor.x; } if (cursor.x > fm.size.x) fm.size.x = cursor.x; //text horizontal size //store the base glyph width for italic transform if (!ltgm && rtgm->w > 0.0f) static_cast(fm.engine)->baseWidth = rtgm->w; ltgm = rtgm; } fm.size.y = height(loc, fm.spacing.y); _alignY(fm.align.y, box.y, fm.size.y, 0, line, out); //before the last line _align(fm.align, box, {cursor.x, fm.size.y}, line, out.pts.count, out); //last line } void TtfLoader::wrapEllipsis(FontMetrics& fm, const Point& box, char* utf8, RenderPath& out) { TtfGlyphMetrics* ltgm = nullptr; //left side glyph between the two adjacent glyphs auto line = 0; //the begin pos of the last line among path Point cursor = {}; struct { uint32_t pts = 0; uint32_t cmds = 0; float xadv = 0.0f; } capture; //the last path for reverting the last character if the ... space is not enough uint32_t loc = 1; //line counter auto stop = false; while (*utf8) { auto code = _codepoints(&utf8); if (code == LINE_FEED_GLYPH_IDX) { line = feedLine(fm, box.x, cursor.x, line, out.pts.count, cursor, loc, out); continue; } auto rtgm = request(code); //right side glyph between the two adjacent glyphs if (!rtgm) continue; Point kerning{}; if (ltgm) reader.kerning(ltgm->idx, rtgm->idx, kerning); auto xadv = (rtgm->advance + kerning.x) * fm.spacing.x; //normal case if (cursor.x + xadv < box.x) { capture = {out.pts.count, out.cmds.count, xadv}; _build(rtgm->path, cursor, kerning, out); cursor.x += xadv; //ellipsis } else { rtgm = request(DOT_GLYPH_IDX); //right side glyph between the two adjacent glyphs if (!rtgm) { TVGERR("TTF", "Cannot support ... since no glyph data"); return; } kerning = {}; reader.kerning(rtgm->idx, rtgm->idx, kerning); //not enough space, revert one character back if (cursor.x + (rtgm->advance + kerning.x) * 3 > box.x) { out.pts.count = capture.pts; out.cmds.count = capture.cmds; cursor.x -= capture.xadv; } //append ... auto tmp = (rtgm->advance + kerning.x) * fm.spacing.x; for (int i = 0; i < 3; ++i) { _build(rtgm->path, cursor, kerning, out); cursor.x += tmp; } stop = true; } if (cursor.x > fm.size.x) fm.size.x = cursor.x; //text horizontal size //store the base glyph width for italic transform if (!ltgm && rtgm->w > 0.0f) static_cast(fm.engine)->baseWidth = rtgm->w; if (stop) break; //stop the process if the ellipsis is applied ltgm = rtgm; } fm.size.y = height(loc, fm.spacing.y); _alignY(fm.align.y, box.y, fm.size.y, 0, line, out); //before the last line _align(fm.align, box, {cursor.x, fm.size.y}, line, out.pts.count, out); //last line } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ void TtfLoader::transform(Paint* paint, FontMetrics& fm, float italicShear) { auto scale = 1.0f / fm.scale; Matrix m = {scale, -italicShear * scale, italicShear * static_cast(fm.engine)->baseWidth * scale, 0, scale, reader.metrics.hhea.ascent * scale, 0, 0, 1}; paint->transform(m); } TtfLoader::TtfLoader() : FontLoader(FileType::Ttf) { } TtfLoader::~TtfLoader() { clear(); } bool TtfLoader::open(const char* path) { #ifdef THORVG_FILE_IO_SUPPORT clear(); if (!_map(this, path)) return false; name = tvg::filename(path); return reader.header(); #else return false; #endif } bool TtfLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy) { reader.size = size; nomap = true; if (copy) { reader.data = tvg::malloc(size); if (!reader.data) return false; memcpy((char*)reader.data, data, reader.size); freeData = true; } else reader.data = (uint8_t*)data; return reader.header(); } bool TtfLoader::get(FontMetrics& fm, char* text, RenderPath& out) { out.clear(); if (!text || fm.fontSize == 0.0f) return false; fm.scale = reader.metrics.unitsPerEm / (fm.fontSize * FontLoader::DPI); fm.size = {}; if (!fm.engine) fm.engine = tvg::calloc(1, sizeof(TtfMetrics)); auto box = fm.box * fm.scale; if (fm.wrap == TextWrap::None || fm.box.x == 0.0f) wrapNone(fm, box, text, out); else if (fm.wrap == TextWrap::Character) wrapChar(fm, box, text, out); else if (fm.wrap == TextWrap::Word) wrapWord(fm, box, text, out, false); else if (fm.wrap == TextWrap::Smart) wrapWord(fm, box, text, out, true); else if (fm.wrap == TextWrap::Ellipsis) wrapEllipsis(fm, box, text, out); else return false; return true; } void TtfLoader::release(FontMetrics& fm) { tvg::free(fm.engine); fm.engine = nullptr; } void TtfLoader::copy(const FontMetrics& in, FontMetrics& out) { release(out); out = in; if (in.engine) out.engine = tvg::calloc(1, sizeof(TtfMetrics)); *static_cast(out.engine) = *static_cast(in.engine); } void TtfLoader::metrics(const FontMetrics& fm, TextMetrics& out) { auto scale = reader.metrics.unitsPerEm / (fm.fontSize * FontLoader::DPI); out.advance = reader.metrics.hhea.advance * scale; out.ascent = reader.metrics.hhea.ascent * scale; out.descent = reader.metrics.hhea.descent * scale; out.linegap = reader.metrics.hhea.linegap * scale; }mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testPaint.cpp000664 001750 001750 00000035564 15164251010 032670 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; TEST_CASE("Custom Transformation", "[tvgPaint]") { auto shape = Shape::gen(); REQUIRE(shape); //Verify default transform Matrix m1 = shape->transform(); REQUIRE(m1.e11 == Approx(1.0f).margin(0.000001)); REQUIRE(m1.e12 == Approx(0).margin(0.000001)); REQUIRE(m1.e13 == Approx(0).margin(0.000001)); REQUIRE(m1.e21 == Approx(0).margin(0.000001)); REQUIRE(m1.e22 == Approx(1.0f).margin(0.000001)); REQUIRE(m1.e23 == Approx(0).margin(0.000001)); REQUIRE(m1.e31 == Approx(0).margin(0.000001)); REQUIRE(m1.e32 == Approx(0).margin(0.000001)); REQUIRE(m1.e33 == Approx(1.0f).margin(0.000001)); //Custom transform auto m2 = Matrix{1.0f, 2.0f, 3.0f, 4.0f, 0.0f, -4.0f, -3.0f, -2.0f, -1.0f}; REQUIRE(shape->transform(m2) == Result::Success); auto m3 = shape->transform(); REQUIRE(m2.e11 == Approx(m3.e11).margin(0.000001)); REQUIRE(m2.e12 == Approx(m3.e12).margin(0.000001)); REQUIRE(m2.e13 == Approx(m3.e13).margin(0.000001)); REQUIRE(m2.e21 == Approx(m3.e21).margin(0.000001)); REQUIRE(m2.e22 == Approx(m3.e22).margin(0.000001)); REQUIRE(m2.e23 == Approx(m3.e23).margin(0.000001)); REQUIRE(m2.e31 == Approx(m3.e31).margin(0.000001)); REQUIRE(m2.e32 == Approx(m3.e32).margin(0.000001)); REQUIRE(m2.e33 == Approx(m3.e33).margin(0.000001)); //It's not allowed if the custom transform is applied. REQUIRE(shape->translate(155.0f, -155.0f) == Result::InsufficientCondition); REQUIRE(shape->scale(4.7f) == Result::InsufficientCondition); REQUIRE(shape->rotate(45.0f) == Result::InsufficientCondition); //Verify Transform is not modified auto m4 = shape->transform(); REQUIRE(m2.e11 == Approx(m4.e11).margin(0.000001)); REQUIRE(m2.e12 == Approx(m4.e12).margin(0.000001)); REQUIRE(m2.e13 == Approx(m4.e13).margin(0.000001)); REQUIRE(m2.e21 == Approx(m4.e21).margin(0.000001)); REQUIRE(m2.e22 == Approx(m4.e22).margin(0.000001)); REQUIRE(m2.e23 == Approx(m4.e23).margin(0.000001)); REQUIRE(m2.e31 == Approx(m4.e31).margin(0.000001)); REQUIRE(m2.e32 == Approx(m4.e32).margin(0.000001)); REQUIRE(m2.e33 == Approx(m4.e33).margin(0.000001)); Paint::rel(shape); } TEST_CASE("Basic Transformation", "[tvgPaint]") { auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->translate(155.0f, -155.0f) == Result::Success); REQUIRE(shape->rotate(45.0f) == Result::Success); REQUIRE(shape->scale(4.7f) == Result::Success); auto m = shape->transform(); REQUIRE(m.e11 == Approx(3.323402f).margin(0.000001)); REQUIRE(m.e12 == Approx(-3.323401f).margin(0.000001)); REQUIRE(m.e13 == Approx(155.0f).margin(0.000001)); REQUIRE(m.e21 == Approx(3.323401f).margin(0.000001)); REQUIRE(m.e22 == Approx(3.323402f).margin(0.000001)); REQUIRE(m.e23 == Approx(-155.0f).margin(0.000001)); REQUIRE(m.e31 == Approx(0).margin(0.000001)); REQUIRE(m.e32 == Approx(0).margin(0.000001)); REQUIRE(m.e33 == Approx(1).margin(0.000001)); Paint::rel(shape); } TEST_CASE("Opacity", "[tvgPaint]") { auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->opacity() == 255); REQUIRE(shape->opacity(155) == Result::Success); REQUIRE(shape->opacity() == 155); REQUIRE(shape->opacity(-1) == Result::Success); REQUIRE(shape->opacity() == 255); REQUIRE(shape->opacity(0) == Result::Success); REQUIRE(shape->opacity() == 0); Paint::rel(shape); } TEST_CASE("Visibility", "[tvgPaint]") { auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->visible() == true); REQUIRE(shape->visible(false) == Result::Success); REQUIRE(shape->visible() == false); REQUIRE(shape->visible(false) == Result::Success); REQUIRE(shape->visible() == false); REQUIRE(shape->visible(true) == Result::Success); REQUIRE(shape->visible() == true); Paint::rel(shape); } TEST_CASE("Bounding Box", "[tvgPaint]") { REQUIRE(Initializer::init() == Result::Success); { auto buffer = unique_ptr(new uint32_t[500*500]); auto canvas = unique_ptr(SwCanvas::gen()); canvas->target(buffer.get(), 500, 500, 500, ColorSpace::ARGB8888); auto shape = Shape::gen(); canvas->add(shape); //Negative float x = 0, y = 0, w = 0, h = 0; REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::InsufficientCondition); //Case 1 REQUIRE(shape->appendRect(0.0f, 10.0f, 20.0f, 100.0f, 50.0f, 50.0f) == Result::Success); REQUIRE(shape->translate(100.0f, 111.0f) == Result::Success); canvas->update(); //Positive REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::Success); REQUIRE(x == 100.0f); REQUIRE(y == 121.0f); REQUIRE(w == 20.0f); REQUIRE(h == 100.0f); Point pts[4]; REQUIRE(shape->bounds(pts) == Result::Success); REQUIRE(pts[0].x == 100.0f); REQUIRE(pts[3].x == 100.0f); REQUIRE(pts[0].y == 121.0f); REQUIRE(pts[1].y == 121.0f); REQUIRE(pts[1].x == 120.0f); REQUIRE(pts[2].x == 120.0f); REQUIRE(pts[2].y == 221.0f); REQUIRE(pts[3].y == 221.0f); REQUIRE(canvas->sync() == Result::Success); //Case 2 REQUIRE(shape->reset() == Result::Success); REQUIRE(shape->moveTo(0.0f, 10.0f) == Result::Success); REQUIRE(shape->lineTo(20.0f, 210.0f) == Result::Success); auto identity = Matrix{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}; REQUIRE(shape->transform(identity) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::Success); REQUIRE(x == 0.0f); REQUIRE(y == 10.0f); REQUIRE(w == 20.0f); REQUIRE(h == 200.0f); REQUIRE(shape->bounds(pts) == Result::Success); REQUIRE(pts[0].x == 0.0f); REQUIRE(pts[3].x == 0.0f); REQUIRE(pts[0].y == 10.0f); REQUIRE(pts[1].y == 10.0f); REQUIRE(pts[1].x == 20.0f); REQUIRE(pts[2].x == 20.0f); REQUIRE(pts[2].y == 210.0f); REQUIRE(pts[3].y == 210.0f); REQUIRE(canvas->sync() == Result::Success); //Case3 REQUIRE(shape->reset() == Result::Success); REQUIRE(shape->moveTo(10, 10) == Result::Success); REQUIRE(shape->lineTo(190, 10) == Result::Success); REQUIRE(shape->strokeWidth(12.0f) == Result::Success); REQUIRE(shape->strokeFill(255, 0, 0, 255) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::Success); REQUIRE(x == 4.0f); REQUIRE(y == 4.0f); REQUIRE(h == 12.0f); REQUIRE(w == 192.0f); REQUIRE(shape->bounds(pts) == Result::Success); REQUIRE(pts[0].x == 4.0f); REQUIRE(pts[3].x == 4.0f); REQUIRE(pts[0].y == 4.0f); REQUIRE(pts[1].y == 4.0f); REQUIRE(pts[1].x == 196.0f); REQUIRE(pts[2].x == 196.0f); REQUIRE(pts[2].y == 16.0f); REQUIRE(pts[3].y == 16.0f); #ifdef THORVG_TTF_LOADER_SUPPORT //Text REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); auto text = Text::gen(); REQUIRE(canvas->add(text) == Result::Success); REQUIRE(canvas->sync() == Result::Success); //Empty Size REQUIRE(text->bounds(&x, &y, &w, &h) == Result::Success); //Case 1 REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(32) == Result::Success); REQUIRE(text->text("TEST") == Result::Success); REQUIRE(text->translate(100.0f, 111.0f) == Result::Success); REQUIRE(text->bounds(&x, &y, &w, &h) == Result::Success); REQUIRE(x == Approx(101.0f).margin(0.000001)); REQUIRE(y == Approx(118.5625f).margin(0.000001)); REQUIRE(w == Approx(107.1875f).margin(0.000001)); REQUIRE(h == Approx(31.58334f).margin(0.001f)); REQUIRE(canvas->update() == Result::Success); REQUIRE(text->bounds(pts) == Result::Success); REQUIRE(pts[0].x == Approx(101.0f).margin(0.000001)); REQUIRE(pts[3].x == Approx(101.0f).margin(0.000001)); REQUIRE(pts[1].x == Approx(208.1875f).margin(0.000001)); REQUIRE(pts[2].x == Approx(208.1875f).margin(0.000001)); REQUIRE(pts[0].y == Approx(118.5625f).margin(0.000001)); REQUIRE(pts[1].y == Approx(118.5625f).margin(0.000001)); REQUIRE(pts[2].y == Approx(150.14584f).margin(0.000001)); REQUIRE(pts[3].y == Approx(150.14584f).margin(0.000001)); //Case 2 REQUIRE(text->text("BOUNDS") == Result::Success); identity = Matrix{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}; REQUIRE(text->transform(identity) == Result::Success); REQUIRE(text->bounds(&x, &y, &w, &h) == Result::Success); REQUIRE(x == Approx(3.125f).margin(0.000001)); REQUIRE(y == Approx(7.54167f).margin(0.000001)); REQUIRE(w == Approx(177.1875f).margin(0.000001)); REQUIRE(h == Approx(31.60417f).margin(0.000001)); REQUIRE(canvas->update() == Result::Success); REQUIRE(text->bounds(pts) == Result::Success); REQUIRE(pts[0].x == Approx(3.125f).margin(0.000001)); REQUIRE(pts[3].x == Approx(3.125f).margin(0.000001)); REQUIRE(pts[1].x == Approx(180.3125f).margin(0.000001)); REQUIRE(pts[2].x == Approx(180.3125f).margin(0.000001)); REQUIRE(pts[0].y == Approx(7.54167f).margin(0.000001)); REQUIRE(pts[1].y == Approx(7.54167f).margin(0.000001)); REQUIRE(pts[2].y == Approx(39.14584f).margin(0.000001)); REQUIRE(pts[3].y == Approx(39.14584f).margin(0.000001)); #endif } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Intersection", "[tvgPaint]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); uint32_t buffer[200 * 200]; canvas->target(buffer, 200, 200, 200, ColorSpace::ARGB8888); auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->appendRect(50, 50, 100, 100) == Result::Success); REQUIRE(shape->fill(255, 0, 0, 255) == Result::Success); REQUIRE(canvas->add(shape) == Result::Success); REQUIRE(canvas->draw() == Result::Success); // Case1. Fully contained REQUIRE(shape->intersects(0, 0, 200, 200) == true); // Case2. Partially overlapping REQUIRE(shape->intersects(25, 25, 50, 50) == true); REQUIRE(shape->intersects(125, 125, 50, 50) == true); // Case3. Edge-touching REQUIRE(shape->intersects(49, 49, 2, 2) == true); REQUIRE(shape->intersects(149, 149, 2, 2) == true); // Case4. Fully separated REQUIRE(shape->intersects(0, 0, 25, 25) == false); REQUIRE(shape->intersects(175, 175, 25, 25) == false); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Duplication", "[tvgPaint]") { vector paints; auto shape = Shape::gen(); REQUIRE(shape); paints.push_back(shape); #ifdef THORVG_TTF_LOADER_SUPPORT REQUIRE(Text::load(TEST_DIR"/Arial.ttf") == Result::Success); auto text = Text::gen(); REQUIRE(text); REQUIRE(text->font("Arial") == Result::Success); REQUIRE(text->size(32) == Result::Success); REQUIRE(text->text("Original Text") == Result::Success); REQUIRE(text->fill(255, 0, 0) == Result::Success); paints.push_back(text); #endif for (auto& paint : paints) { //Setup paint properties REQUIRE(paint->opacity(0) == Result::Success); REQUIRE(paint->translate(200.0f, 100.0f) == Result::Success); REQUIRE(paint->scale(2.2f) == Result::Success); REQUIRE(paint->rotate(90.0f) == Result::Success); auto comp = Shape::gen(); REQUIRE(comp); REQUIRE(paint->clip(comp) == Result::Success); //Duplication auto dup = paint->duplicate(); REQUIRE(dup); //Compare properties REQUIRE(dup->opacity() == 0); auto m = paint->transform(); REQUIRE(m.e11 == Approx(0.0f).margin(0.000001)); REQUIRE(m.e12 == Approx(-2.2f).margin(0.000001)); REQUIRE(m.e13 == Approx(200.0f).margin(0.000001)); REQUIRE(m.e21 == Approx(2.2f).margin(0.000001)); REQUIRE(m.e22 == Approx(0.0f).margin(0.000001)); REQUIRE(m.e23 == Approx(100.0f).margin(0.000001)); REQUIRE(m.e31 == Approx(0.0f).margin(0.000001)); REQUIRE(m.e32 == Approx(0.0f).margin(0.000001)); REQUIRE(m.e33 == Approx(1.0f).margin(0.000001)); Paint::rel(dup); } //release for (auto p : paints) { Paint::rel(p); } } TEST_CASE("Reference Count", "[tvgPaint]") { auto shape = Shape::gen(); REQUIRE(shape->refCnt() == 0); REQUIRE(shape->unref(false) == 0); REQUIRE(shape->ref() == 1); REQUIRE(shape->ref() == 2); REQUIRE(shape->ref() == 3); REQUIRE(shape->unref() == 2); REQUIRE(shape->unref() == 1); REQUIRE(shape->unref() == 0); Initializer::init(); { auto canvas = unique_ptr(SwCanvas::gen()); shape = Shape::gen(); REQUIRE(shape->ref() == 1); canvas->add(shape); REQUIRE(shape->refCnt() == 2); REQUIRE(shape->unref() == 1); shape = Shape::gen(); REQUIRE(shape->ref() == 1); auto scene = Scene::gen(); scene->add(shape); canvas->add(scene); REQUIRE(shape->refCnt() == 2); REQUIRE(shape->unref() == 1); shape = Shape::gen(); REQUIRE(shape->ref() == 1); scene = Scene::gen(); scene->add(shape); scene->remove(); canvas->add(scene); REQUIRE(shape->unref() == 0); } Initializer::term(); }external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-scanner-internal.h000664 001750 001750 00000044173 15164251010 045266 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JS_SCANNER_INTERNAL_H #define JS_SCANNER_INTERNAL_H /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_scanner Scanner * @{ */ /** * Scan mode types. */ typedef enum { SCAN_MODE_PRIMARY_EXPRESSION, /**< scanning primary expression */ SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW, /**< scanning primary expression after new */ SCAN_MODE_POST_PRIMARY_EXPRESSION, /**< scanning post primary expression */ SCAN_MODE_PRIMARY_EXPRESSION_END, /**< scanning primary expression end */ SCAN_MODE_STATEMENT, /**< scanning statement */ SCAN_MODE_STATEMENT_OR_TERMINATOR, /**< scanning statement or statement end */ SCAN_MODE_STATEMENT_END, /**< scanning statement end */ SCAN_MODE_VAR_STATEMENT, /**< scanning var statement */ SCAN_MODE_PROPERTY_NAME, /**< scanning property name */ SCAN_MODE_FUNCTION_ARGUMENTS, /**< scanning function arguments */ SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS, /**< continue scanning function arguments */ SCAN_MODE_BINDING, /**< array or object binding */ SCAN_MODE_CLASS_DECLARATION, /**< scanning class declaration */ SCAN_MODE_CLASS_BODY, /**< scanning class body */ SCAN_MODE_CLASS_BODY_NO_SCAN, /**< scanning class body without calling lexer_scan_identifier */ } scan_modes_t; /** * Scan stack mode types. */ typedef enum { SCAN_STACK_SCRIPT, /**< script */ SCAN_STACK_SCRIPT_FUNCTION, /**< script is a function body */ SCAN_STACK_BLOCK_STATEMENT, /**< block statement group */ SCAN_STACK_FUNCTION_STATEMENT, /**< function statement */ SCAN_STACK_FUNCTION_EXPRESSION, /**< function expression */ SCAN_STACK_FUNCTION_PROPERTY, /**< function expression in an object literal */ SCAN_STACK_FUNCTION_ARROW, /**< arrow function expression */ SCAN_STACK_SWITCH_BLOCK, /**< block part of "switch" statement */ SCAN_STACK_IF_STATEMENT, /**< statement part of "if" statements */ SCAN_STACK_WITH_STATEMENT, /**< statement part of "with" statements */ SCAN_STACK_WITH_EXPRESSION, /**< expression part of "with" statements */ SCAN_STACK_DO_STATEMENT, /**< statement part of "do" statements */ SCAN_STACK_DO_EXPRESSION, /**< expression part of "do" statements */ SCAN_STACK_WHILE_EXPRESSION, /**< expression part of "while" iterator */ SCAN_STACK_PAREN_EXPRESSION, /**< expression in brackets */ SCAN_STACK_STATEMENT_WITH_EXPR, /**< statement which starts with expression enclosed in brackets */ SCAN_STACK_BINDING_INIT, /**< post processing after a single initializer */ SCAN_STACK_BINDING_LIST_INIT, /**< post processing after an initializer list */ SCAN_STACK_LET, /**< let statement */ SCAN_STACK_CONST, /**< const statement */ /* The SCANNER_IS_FOR_START macro needs to be updated when the following constants are reordered. */ SCAN_STACK_VAR, /**< var statement */ SCAN_STACK_FOR_VAR_START, /**< start of "for" iterator with var statement */ SCAN_STACK_FOR_LET_START, /**< start of "for" iterator with let statement */ SCAN_STACK_FOR_CONST_START, /**< start of "for" iterator with const statement */ SCAN_STACK_FOR_START, /**< start of "for" iterator */ SCAN_STACK_FOR_CONDITION, /**< condition part of "for" iterator */ SCAN_STACK_FOR_EXPRESSION, /**< expression part of "for" iterator */ SCAN_STACK_SWITCH_EXPRESSION, /**< expression part of "switch" statement */ SCAN_STACK_CASE_STATEMENT, /**< case statement inside a switch statement */ SCAN_STACK_COLON_EXPRESSION, /**< expression between a question mark and colon */ SCAN_STACK_TRY_STATEMENT, /**< try statement */ SCAN_STACK_CATCH_STATEMENT, /**< catch statement */ SCAN_STACK_ARRAY_LITERAL, /**< array literal or destructuring assignment or binding */ SCAN_STACK_OBJECT_LITERAL, /**< object literal group */ SCAN_STACK_PROPERTY_ACCESSOR, /**< property accessor in square brackets */ /* These four must be in this order. */ SCAN_STACK_COMPUTED_PROPERTY, /**< computed property name */ SCAN_STACK_COMPUTED_GENERATOR, /**< computed generator function */ SCAN_STACK_COMPUTED_ASYNC, /**< computed async function */ SCAN_STACK_COMPUTED_ASYNC_GENERATOR, /**< computed async function */ SCAN_STACK_TEMPLATE_STRING, /**< template string */ SCAN_STACK_TAGGED_TEMPLATE_LITERAL, /**< tagged template literal */ SCAN_STACK_PRIVATE_BLOCK_EARLY, /**< private block for single statements (force early declarations) */ SCAN_STACK_PRIVATE_BLOCK, /**< private block for single statements */ SCAN_STACK_ARROW_ARGUMENTS, /**< might be arguments of an arrow function */ SCAN_STACK_ARROW_EXPRESSION, /**< expression body of an arrow function */ SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR, /**< explicit class constructor */ SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR, /**< implicit class constructor */ SCAN_STACK_CLASS_STATEMENT, /**< class statement */ SCAN_STACK_CLASS_EXPRESSION, /**< class expression */ SCAN_STACK_CLASS_EXTENDS, /**< class extends expression */ SCAN_STACK_CLASS_FIELD_INITIALIZER, /**< class field initializer */ SCAN_STACK_FUNCTION_PARAMETERS, /**< function parameter initializer */ SCAN_STACK_FOR_START_PATTERN, /**< possible assignment pattern for "for" iterator */ SCAN_STACK_USE_ASYNC, /**< an "async" identifier is used */ SCAN_STACK_CLASS_STATIC_BLOCK, /**< class static block */ #if JERRY_MODULE_SYSTEM SCAN_STACK_EXPORT_DEFAULT, /**< scan primary expression after export default */ #endif /* JERRY_MODULE_SYSTEM */ } scan_stack_modes_t; /** * Scanner context flag types. */ typedef enum { SCANNER_CONTEXT_NO_FLAGS = 0, /**< no flags are set */ SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION = (1 << 0), /**< throw async function error */ } scanner_context_flags_t; /** * Checks whether the stack top is a for statement start. */ #define SCANNER_IS_FOR_START(stack_top) ((stack_top) >= SCAN_STACK_FOR_VAR_START && (stack_top) <= SCAN_STACK_FOR_START) /** * Generic descriptor which stores only the start position. */ typedef struct { const uint8_t *source_p; /**< start source byte */ } scanner_source_start_t; /** * Descriptor for storing a binding literal on stack. */ typedef struct { lexer_lit_location_t *literal_p; /**< binding literal */ } scanner_binding_literal_t; /** * Flags for type member of lexer_lit_location_t structure in the literal pool. */ typedef enum { SCANNER_LITERAL_IS_ARG = (1 << 0), /**< literal is argument */ SCANNER_LITERAL_IS_VAR = (1 << 1), /**< literal is var */ /** literal is a destructured argument binding of a possible arrow function */ SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG = SCANNER_LITERAL_IS_VAR, SCANNER_LITERAL_IS_FUNC = (1 << 2), /**< literal is function */ SCANNER_LITERAL_NO_REG = (1 << 3), /**< literal cannot be stored in a register */ SCANNER_LITERAL_IS_LET = (1 << 4), /**< literal is let */ /** literal is a function declared in this block (prevents declaring let/const with the same name) */ SCANNER_LITERAL_IS_FUNC_DECLARATION = SCANNER_LITERAL_IS_LET, SCANNER_LITERAL_IS_CONST = (1 << 5), /**< literal is const */ /** literal is a destructured argument binding */ SCANNER_LITERAL_IS_DESTRUCTURED_ARG = SCANNER_LITERAL_IS_CONST, SCANNER_LITERAL_IS_USED = (1 << 6), /**< literal is used */ SCANNER_LITERAL_EARLY_CREATE = (1 << 7), /**< binding should be created early with ECMA_VALUE_UNINITIALIZED */ } scanner_literal_type_flags_t; /* * Known combinations: * * SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_FUNC_DECLARATION : * function declared in this block * SCANNER_LITERAL_IS_LOCAL : * module import on global scope, catch block variable otherwise * SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC : * a function argument which is reassigned to a function later * SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_DESTRUCTURED_ARG : * destructured binding argument * SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_DESTRUCTURED_ARG | SCANNER_LITERAL_IS_FUNC : * destructured binding argument which is reassigned to a function later */ /** * Literal is a local declaration (let, const, catch variable, etc.) */ #define SCANNER_LITERAL_IS_LOCAL (SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_IS_CONST) /** * Literal is a local function declaration */ #define SCANNER_LITERAL_IS_LOCAL_FUNC (SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_FUNC_DECLARATION) /** * For statement descriptor. */ typedef struct { /** shared fields of for statements */ union { const uint8_t *source_p; /**< start source byte */ scanner_for_info_t *for_info_p; /**< for info */ } u; } scanner_for_statement_t; /** * Switch statement descriptor. */ typedef struct { scanner_case_info_t **last_case_p; /**< last case info */ } scanner_switch_statement_t; /** * Types of scanner destructuring bindings. */ typedef enum { /* Update SCANNER_NEEDS_BINDING_LIST after changing these values. */ SCANNER_BINDING_NONE, /**< not a destructuring binding expression */ SCANNER_BINDING_VAR, /**< destructuring var binding */ SCANNER_BINDING_LET, /**< destructuring let binding */ SCANNER_BINDING_CATCH, /**< destructuring catch binding */ SCANNER_BINDING_CONST, /**< destructuring const binding */ SCANNER_BINDING_ARG, /**< destructuring arg binding */ SCANNER_BINDING_ARROW_ARG, /**< possible destructuring arg binding of an arrow function */ } scanner_binding_type_t; /** * Check whether a binding list is needed for the binding pattern. */ #define SCANNER_NEEDS_BINDING_LIST(type) ((type) >= SCANNER_BINDING_LET) /** * Scanner binding items for destructuring binding patterns. */ typedef struct scanner_binding_item_t { struct scanner_binding_item_t *next_p; /**< next binding in the list */ lexer_lit_location_t *literal_p; /**< binding literal */ } scanner_binding_item_t; /** * Scanner binding lists for destructuring binding patterns. */ typedef struct scanner_binding_list_t { struct scanner_binding_list_t *prev_p; /**< prev list */ scanner_binding_item_t *items_p; /**< list of bindings */ bool is_nested; /**< is nested binding declaration */ } scanner_binding_list_t; /** * Flags for scanner_literal_pool_t structure. */ typedef enum { SCANNER_LITERAL_POOL_FUNCTION = (1 << 0), /**< literal pool represents a function */ SCANNER_LITERAL_POOL_CLASS_NAME = (1 << 1), /**< literal pool which contains a class name */ SCANNER_LITERAL_POOL_CLASS_FIELD = (1 << 2), /**< literal pool is created for a class field initializer */ SCANNER_LITERAL_POOL_IS_STRICT = (1 << 3), /**< literal pool represents a strict mode code block */ SCANNER_LITERAL_POOL_CAN_EVAL = (1 << 4), /**< prepare for executing eval in this block */ SCANNER_LITERAL_POOL_NO_ARGUMENTS = (1 << 5), /**< arguments object must not be constructed, * or arguments cannot be stored in registers if * SCANNER_LITERAL_POOL_ARGUMENTS_IN_ARGS is set */ SCANNER_LITERAL_POOL_ARGUMENTS_IN_ARGS = (1 << 6), /**< arguments is referenced in function args */ SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT = (1 << 7), /**< function has complex (ES2015+) argument definition */ SCANNER_LITERAL_POOL_IN_WITH = (1 << 8), /**< literal pool is in a with statement */ SCANNER_LITERAL_POOL_ARROW = (1 << 9), /**< arrow function */ SCANNER_LITERAL_POOL_GENERATOR = (1 << 10), /**< generator function */ SCANNER_LITERAL_POOL_ASYNC = (1 << 11), /**< async function */ SCANNER_LITERAL_POOL_FUNCTION_STATEMENT = (1 << 12), /**< function statement */ SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE = (1 << 13), /**< function body contains super reference */ #if JERRY_MODULE_SYSTEM SCANNER_LITERAL_POOL_IN_EXPORT = (1 << 14), /**< the declared variables are exported by the module system */ #endif /* JERRY_MODULE_SYSTEM */ } scanner_literal_pool_flags_t; /** * Define a function where no arguments are allowed. */ #define SCANNER_LITERAL_POOL_ARROW_FLAGS \ (SCANNER_LITERAL_POOL_FUNCTION | SCANNER_LITERAL_POOL_NO_ARGUMENTS | SCANNER_LITERAL_POOL_ARROW) /** * This flag represents that the bracketed expression might be an async arrow function. * The SCANNER_LITERAL_POOL_ARROW flag is reused for this purpose. */ #define SCANNER_LITERAL_POOL_MAY_ASYNC_ARROW SCANNER_LITERAL_POOL_ARROW /** * Getting the generator and async properties of literal pool status flags. */ #define SCANNER_FROM_LITERAL_POOL_TO_COMPUTED(status_flags) \ ((uint8_t) ((((status_flags) >> 10) & 0x3) + SCAN_STACK_COMPUTED_PROPERTY)) /** * Setting the generator and async properties of literal pool status flags. */ #define SCANNER_FROM_COMPUTED_TO_LITERAL_POOL(mode) (((mode) -SCAN_STACK_COMPUTED_PROPERTY) << 10) /** * Literal pool which may contains function argument identifiers */ #define SCANNER_LITERAL_POOL_MAY_HAVE_ARGUMENTS(status_flags) \ (!((status_flags) & (SCANNER_LITERAL_POOL_CLASS_NAME | SCANNER_LITERAL_POOL_CLASS_FIELD))) /** * Local literal pool. */ typedef struct scanner_literal_pool_t { struct scanner_literal_pool_t *prev_p; /**< previous literal pool */ const uint8_t *source_p; /**< source position where the final data needs to be inserted */ parser_list_t literal_pool; /**< list of literal */ uint16_t status_flags; /**< combination of scanner_literal_pool_flags_t flags */ uint16_t no_declarations; /**< size of scope stack required during parsing */ } scanner_literal_pool_t; /** * Scanner context. */ struct scanner_context_t { uint32_t context_status_flags; /**< original status flags of the context */ uint8_t mode; /**< scanner mode */ uint8_t binding_type; /**< current destructuring binding type */ uint16_t status_flags; /**< scanner status flags */ scanner_binding_list_t *active_binding_list_p; /**< currently active binding list */ scanner_literal_pool_t *active_literal_pool_p; /**< currently active literal pool */ scanner_switch_statement_t active_switch_statement; /**< currently active switch statement */ scanner_info_t *end_arguments_p; /**< position of end arguments */ const uint8_t *async_source_p; /**< source position for async functions */ }; /* Scanner utils. */ void scanner_raise_error (parser_context_t *context_p); void scanner_raise_redeclaration_error (parser_context_t *context_p); void *scanner_malloc (parser_context_t *context_p, size_t size); void scanner_free (void *ptr, size_t size); size_t scanner_get_stream_size (scanner_info_t *info_p, size_t size); scanner_info_t *scanner_insert_info (parser_context_t *context_p, const uint8_t *source_p, size_t size); scanner_info_t *scanner_insert_info_before (parser_context_t *context_p, const uint8_t *source_p, scanner_info_t *start_info_p, size_t size); scanner_literal_pool_t * scanner_push_literal_pool (parser_context_t *context_p, scanner_context_t *scanner_context_p, uint16_t status_flags); void scanner_pop_literal_pool (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_filter_arguments (parser_context_t *context_p, scanner_context_t *scanner_context_p); lexer_lit_location_t *scanner_add_custom_literal (parser_context_t *context_p, scanner_literal_pool_t *literal_pool_p, const lexer_lit_location_t *literal_location_p); lexer_lit_location_t *scanner_add_literal (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_add_reference (parser_context_t *context_p, scanner_context_t *scanner_context_p); lexer_lit_location_t *scanner_append_argument (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_add_private_identifier (parser_context_t *context_p, scanner_private_field_flags_t opts); void scanner_detect_invalid_var (parser_context_t *context_p, scanner_context_t *scanner_context_p, lexer_lit_location_t *var_literal_p); void scanner_detect_invalid_let (parser_context_t *context_p, lexer_lit_location_t *let_literal_p); void scanner_detect_eval_call (parser_context_t *context_p, scanner_context_t *scanner_context_p); lexer_lit_location_t * scanner_push_class_declaration (parser_context_t *context_p, scanner_context_t *scanner_context_p, uint8_t stack_mode); void scanner_push_class_field_initializer (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_push_destructuring_pattern (parser_context_t *context_p, scanner_context_t *scanner_context_p, uint8_t binding_type, bool is_nested); void scanner_pop_binding_list (scanner_context_t *scanner_context_p); void scanner_append_hole (parser_context_t *context_p, scanner_context_t *scanner_context_p); /* Scanner operations. */ void scanner_add_async_literal (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_check_arrow (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_scan_simple_arrow (parser_context_t *context_p, scanner_context_t *scanner_context_p, const uint8_t *source_p); void scanner_check_arrow_arg (parser_context_t *context_p, scanner_context_t *scanner_context_p); bool scanner_check_async_function (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_check_function_after_if (parser_context_t *context_p, scanner_context_t *scanner_context_p); #if JERRY_MODULE_SYSTEM void scanner_check_import_meta (parser_context_t *context_p); #endif /* JERRY_MODULE_SYSTEM */ void scanner_scan_bracket (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_check_directives (parser_context_t *context_p, scanner_context_t *scanner_context_p); /** * @} * @} * @} */ #endif /* !JS_SCANNER_INTERNAL_H */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.cpp000664 001750 001750 00000021325 15164251010 052430 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "jcontext.h" #include "opcodes.h" #include "vm-defines.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_GENERATOR_PROTOTYPE_ROUTINE_START = 0, ECMA_GENERATOR_PROTOTYPE_ROUTINE_NEXT, ECMA_GENERATOR_PROTOTYPE_ROUTINE_THROW, ECMA_GENERATOR_PROTOTYPE_ROUTINE_RETURN }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-generator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID generator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup generatorprototype ECMA Generator.prototype object built-in * @{ */ /** * Convert routine type to operation type. */ #define ECMA_GENERATOR_ROUTINE_TO_OPERATION(type) \ ((ecma_iterator_command_type_t) ((type) -ECMA_GENERATOR_PROTOTYPE_ROUTINE_NEXT)) JERRY_STATIC_ASSERT ((ECMA_GENERATOR_ROUTINE_TO_OPERATION (ECMA_GENERATOR_PROTOTYPE_ROUTINE_NEXT) == ECMA_ITERATOR_NEXT), convert_ecma_generator_routine_next_to_ecma_iterator_next_failed); JERRY_STATIC_ASSERT ((ECMA_GENERATOR_ROUTINE_TO_OPERATION (ECMA_GENERATOR_PROTOTYPE_ROUTINE_THROW) == ECMA_ITERATOR_THROW), convert_ecma_generator_routine_throw_to_ecma_iterator_throw_failed); JERRY_STATIC_ASSERT ((ECMA_GENERATOR_ROUTINE_TO_OPERATION (ECMA_GENERATOR_PROTOTYPE_ROUTINE_RETURN) == ECMA_ITERATOR_RETURN), convert_ecma_generator_routine_return_to_ecma_iterator_return_failed); /** * Helper function for next / return / throw * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_generator_prototype_object_do (vm_executable_object_t *generator_object_p, /**< generator object */ ecma_value_t arg, /**< argument */ ecma_iterator_command_type_t resume_mode) /**< resume mode */ { arg = ecma_copy_value (arg); while (true) { if (generator_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) { if (generator_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_RUNNING) { return ecma_raise_type_error (ECMA_ERR_GENERATOR_IS_CURRENTLY_UNDER_EXECUTION); } ecma_value_t iterator = generator_object_p->iterator; ecma_value_t next_method = generator_object_p->frame_ctx.stack_top_p[-1]; bool done = false; generator_object_p->extended_object.u.cls.u2.executable_obj_flags |= ECMA_EXECUTABLE_OBJECT_RUNNING; ecma_value_t result = ecma_op_iterator_do (resume_mode, iterator, next_method, arg, &done); ecma_free_value (arg); generator_object_p->extended_object.u.cls.u2.executable_obj_flags &= (uint8_t) ~ECMA_EXECUTABLE_OBJECT_RUNNING; if (ECMA_IS_VALUE_ERROR (result)) { arg = result; } else if (done) { arg = ecma_op_iterator_value (result); ecma_free_value (result); if (resume_mode == ECMA_ITERATOR_THROW) { resume_mode = ECMA_ITERATOR_NEXT; } } else { return result; } ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (generator_object_p); generator_object_p->iterator = ECMA_VALUE_UNDEFINED; JERRY_ASSERT (generator_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED || ecma_is_value_object (generator_object_p->frame_ctx.stack_top_p[-1])); generator_object_p->frame_ctx.stack_top_p--; if (ECMA_IS_VALUE_ERROR (arg)) { arg = jcontext_take_exception (); resume_mode = ECMA_ITERATOR_THROW; } } if (resume_mode == ECMA_ITERATOR_RETURN) { generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; } else if (resume_mode == ECMA_ITERATOR_THROW) { generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; } ecma_value_t value = opfunc_resume_executable_object (generator_object_p, arg); if (ECMA_IS_VALUE_ERROR (value)) { return value; } bool done; done = (generator_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_COMPLETED); if (!done) { const uint8_t *byte_code_p = generator_object_p->frame_ctx.byte_code_p; JERRY_ASSERT (byte_code_p[-2] == CBC_EXT_OPCODE && (byte_code_p[-1] == CBC_EXT_YIELD || byte_code_p[-1] == CBC_EXT_YIELD_ITERATOR)); if (byte_code_p[-1] == CBC_EXT_YIELD_ITERATOR) { ecma_value_t iterator = ecma_op_get_iterator (value, ECMA_VALUE_SYNC_ITERATOR, generator_object_p->frame_ctx.stack_top_p); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (iterator)) { resume_mode = ECMA_ITERATOR_THROW; arg = jcontext_take_exception (); continue; } ecma_deref_object (ecma_get_object_from_value (iterator)); generator_object_p->extended_object.u.cls.u2.executable_obj_flags |= ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD; generator_object_p->iterator = iterator; if (generator_object_p->frame_ctx.stack_top_p[0] != ECMA_VALUE_UNDEFINED) { ecma_deref_object (ecma_get_object_from_value (generator_object_p->frame_ctx.stack_top_p[0])); } generator_object_p->frame_ctx.stack_top_p++; arg = ECMA_VALUE_UNDEFINED; continue; } } ecma_value_t result = ecma_create_iter_result_object (value, ecma_make_boolean_value (done)); ecma_fast_free_value (value); return result; } } /* ecma_builtin_generator_prototype_object_do */ /** * Dispatcher of the Generator built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_generator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); vm_executable_object_t *executable_object_p = NULL; if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_GENERATOR)) { executable_object_p = (vm_executable_object_t *) object_p; } } if (executable_object_p == NULL) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_GENERATOR_OBJECT); } if (executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_RUNNING) { return ecma_raise_type_error (ECMA_ERR_GENERATOR_IS_CURRENTLY_UNDER_EXECUTION); } if (executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_COMPLETED) { if (builtin_routine_id != ECMA_GENERATOR_PROTOTYPE_ROUTINE_THROW) { return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } jcontext_raise_exception (ecma_copy_value (arguments_list_p[0])); return ECMA_VALUE_ERROR; } return ecma_builtin_generator_prototype_object_do (executable_object_p, arguments_list_p[0], ECMA_GENERATOR_ROUTINE_TO_OPERATION (builtin_routine_id)); } /* ecma_builtin_generator_prototype_dispatch_routine */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/svg2png/000775 001750 001750 00000000000 15164251010 031743 5ustar00ddennedyddennedy000000 000000 lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-aggregateerror-prototype.inc.h000664 001750 001750 00000002162 15164251010 053655 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * AggregateError.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.7.9 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_AGGREGATE_ERROR_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.10 */ STRING_VALUE (LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-symbol-object.cpp000664 001750 001750 00000011473 15164251010 046575 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-symbol-object.h" #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "lit-char-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmasymbolobject ECMA Symbol object related routines * @{ */ /** * Symbol creation operation. * * See also: ECMA-262 v6, 6.1.5.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_symbol (const ecma_value_t *arguments_list_p, /**< list of arguments */ uint32_t arguments_list_len) /**< length of the arguments' list */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_value_t string_desc; /* 1-3. */ if (arguments_list_len == 0 || ecma_is_value_undefined (arguments_list_p[0])) { string_desc = ECMA_VALUE_UNDEFINED; } else { ecma_string_t *str_p = ecma_op_to_string (arguments_list_p[0]); /* 4. */ if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } string_desc = ecma_make_string_value (str_p); } /* 5. */ return ecma_make_symbol_value (ecma_new_symbol_from_descriptor_string (string_desc)); } /* ecma_op_create_symbol */ /** * Symbol object creation operation. * * See also: ECMA-262 v6, 19.4.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_symbol_object (const ecma_value_t value) /**< symbol value */ { JERRY_ASSERT (ecma_is_value_symbol (value)); ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_SYMBOL_PROTOTYPE); ecma_object_t *object_p = ecma_create_object (prototype_obj_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_SYMBOL; ext_object_p->u.cls.u3.value = ecma_copy_value (value); return ecma_make_object_value (object_p); } /* ecma_op_create_symbol_object */ /** * Get the symbol descriptor ecma-string from an ecma-symbol * * @return pointer to ecma-string descriptor */ ecma_value_t ecma_get_symbol_description (ecma_string_t *symbol_p) /**< ecma-symbol */ { JERRY_ASSERT (symbol_p != NULL); JERRY_ASSERT (ecma_prop_name_is_symbol (symbol_p)); return ((ecma_extended_string_t *) symbol_p)->u.symbol_descriptor; } /* ecma_get_symbol_description */ /** * Get the descriptive string of the Symbol. * * See also: ECMA-262 v6, 19.4.3.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_get_symbol_descriptive_string (ecma_value_t symbol_value) /**< symbol to stringify */ { /* 1. */ JERRY_ASSERT (ecma_is_value_symbol (symbol_value)); /* 2 - 3. */ ecma_string_t *symbol_p = ecma_get_symbol_from_value (symbol_value); ecma_value_t string_desc = ecma_get_symbol_description (symbol_p); ecma_stringbuilder_t builder = ecma_stringbuilder_create_raw ((lit_utf8_byte_t *) ("Symbol("), 7); if (!ecma_is_value_undefined (string_desc)) { ecma_string_t *string_desc_p = ecma_get_string_from_value (string_desc); ecma_stringbuilder_append (&builder, string_desc_p); } ecma_stringbuilder_append_byte (&builder, LIT_CHAR_RIGHT_PAREN); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_get_symbol_descriptive_string */ /** * thisSymbolValue abstract operation * * See also: * ECMA-262 v11, 19.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_symbol_this_value (ecma_value_t this_arg) /**< this argument value */ { /* 1. */ if (ecma_is_value_symbol (this_arg)) { return this_arg; } /* 2. */ if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_SYMBOL)) { return ((ecma_extended_object_t *) object_p)->u.cls.u3.value; } } /* 3. */ return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_SYMBOL); } /* ecma_symbol_this_value */ /** * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-unicode-conversions-sup.inc.h000664 001750 001750 00000002732 15164251010 046423 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is automatically generated by the gen-unicode.py script * from UnicodeData.txt and SpecialCasing.txt files. Do not edit! */ /* Contains start points of character case ranges (these are bidirectional conversions). */ static const uint32_t lit_unicode_character_case_ranges_sup[] JERRY_ATTR_CONST_DATA = { 0x010400, 0x010428, 0x0104b0, 0x0104d8, 0x010c80, 0x010cc0, 0x0118a0, 0x0118c0, 0x016e40, 0x016e60, 0x01e900, 0x01e922 }; /* Interval lengths of start points in `character_case_ranges` table. */ static const uint16_t lit_unicode_character_case_range_lengths_sup[] JERRY_ATTR_CONST_DATA = { 0x000028, 0x000024, 0x000033, 0x000020, 0x000020, 0x000022 }; mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/savers/gif/tvgGifEncoder.cpp000664 001750 001750 00000050364 15164251010 035316 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ // // gif.h // by Charlie Tangora // Public domain. // Email me : ctangora -at- gmail -dot- com // // This file offers a simple, very limited way to create animated GIFs directly in code. // // Those looking for particular cleverness are likely to be disappointed; it's pretty // much a straight-ahead implementation of the GIF format with optional Floyd-Steinberg // dithering. (It does at least use delta encoding - only the changed portions of each // frame are saved.) // // So resulting files are often quite large. The hope is that it will be handy nonetheless // as a quick and easily-integrated way for programs to spit out animations. // // Only RGBA8 is currently supported as an input format. (The alpha is ignored.) // // USAGE: // Create a GifWriter struct. Pass it to GifBegin() to initialize and write the header. // Pass subsequent frames to GifWriteFrame(). // Finally, call GifEnd() to close the file handle and free memory. // #include "tvgMath.h" #include "tvgGifEncoder.h" #define TRANSPARENT_IDX 0 #define TRANSPARENT_THRESHOLD 127 #define BIT_DEPTH 8 // Simple structure to write out the LZW-compressed portion of the image // one bit at a time typedef struct { uint8_t bitIndex; // how many bits in the partial byte written so far uint8_t byte; // current partial byte uint32_t chunkIndex; uint8_t chunk[256]; // bytes are written in here until we have 256 of them, then written to the file } GifBitStatus; // The LZW dictionary is a 256-ary tree constructed as the file is encoded, // this is one node typedef struct { uint16_t m_next[256]; } GifLzwNode; /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ // walks the k-d tree to pick the palette entry for a desired color. // Takes as in/out parameters the current best color and its error - // only changes them if it finds a better color in its subtree. // this is the major hotspot in the code at the moment. static void _getClosestPaletteColor( GifPalette* pPal, int r, int g, int b, int* bestInd, int* bestDiff, int treeRoot ) { // base case, reached the bottom of the tree if (treeRoot > (1 << BIT_DEPTH) - 1) { int ind = treeRoot-(1 << BIT_DEPTH); if(ind == TRANSPARENT_IDX) return; // check whether this color is better than the current winner int r_err = r - ((int32_t)pPal->r[ind]); int g_err = g - ((int32_t)pPal->g[ind]); int b_err = b - ((int32_t)pPal->b[ind]); int diff = abs(r_err)+ abs(g_err) + abs(b_err); if(diff < *bestDiff) { *bestInd = ind; *bestDiff = diff; } return; } // take the appropriate color (r, g, or b) for this node of the k-d tree int comps[3] = {r, g, b}; int splitComp = comps[pPal->treeSplitElt[treeRoot]]; int splitPos = pPal->treeSplit[treeRoot]; if (splitPos > splitComp) { // check the left subtree _getClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); if (*bestDiff > splitPos - splitComp) { // cannot prove there's not a better value in the right subtree, check that too _getClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); } } else { _getClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); if (*bestDiff > splitComp - splitPos) { _getClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); } } } static void _swapPixels(uint8_t* image, int pixA, int pixB) { uint8_t rA = image[pixA*4]; uint8_t gA = image[pixA*4+1]; uint8_t bA = image[pixA*4+2]; uint8_t aA = image[pixA*4+3]; uint8_t rB = image[pixB*4]; uint8_t gB = image[pixB*4+1]; uint8_t bB = image[pixB*4+2]; uint8_t aB = image[pixA*4+3]; image[pixA*4] = rB; image[pixA*4+1] = gB; image[pixA*4+2] = bB; image[pixA*4+3] = aB; image[pixB*4] = rA; image[pixB*4+1] = gA; image[pixB*4+2] = bA; image[pixB*4+3] = aA; } // just the partition operation from quicksort static int _partition(uint8_t* image, const int left, const int right, const int elt, int pivotIndex) { const int pivotValue = image[(pivotIndex)*4+elt]; _swapPixels(image, pivotIndex, right-1); int storeIndex = left; bool split = 0; for (int ii = left; ii < right - 1; ++ii) { int arrayVal = image[ii*4+elt]; if(arrayVal < pivotValue) { _swapPixels(image, ii, storeIndex); ++storeIndex; } else if(arrayVal == pivotValue) { if (split) { _swapPixels(image, ii, storeIndex); ++storeIndex; } split = !split; } } _swapPixels(image, storeIndex, right-1); return storeIndex; } // Perform an incomplete sort, finding all elements above and below the desired median static void _partitionByMedian(uint8_t* image, int left, int right, int com, int neededCenter) { if (left < right-1) { int pivotIndex = left + (right-left)/2; pivotIndex = _partition(image, left, right, com, pivotIndex); // Only "sort" the section of the array that contains the median if(pivotIndex > neededCenter) _partitionByMedian(image, left, pivotIndex, com, neededCenter); if(pivotIndex < neededCenter) _partitionByMedian(image, pivotIndex+1, right, com, neededCenter); } } // Builds a palette by creating a balanced k-d tree of all pixels in the image static void _splitPalette(uint8_t* image, int numPixels, int firstElt, int lastElt, int splitElt, int splitDist, int treeNode, GifPalette* pal) { if(lastElt <= firstElt || numPixels == 0) return; // base case, bottom of the tree if (lastElt == firstElt + 1) { // otherwise, take the average of all colors in this subcube uint64_t r=0, g=0, b=0; for(int ii=0; iir[firstElt] = (uint8_t)r; pal->g[firstElt] = (uint8_t)g; pal->b[firstElt] = (uint8_t)b; return; } // Find the axis with the largest range int minR = 255, maxR = 0; int minG = 255, maxG = 0; int minB = 255, maxB = 0; for (int ii=0; ii maxR) maxR = r; if(r < minR) minR = r; if(g > maxG) maxG = g; if(g < minG) minG = g; if(b > maxB) maxB = b; if(b < minB) minB = b; } int rRange = maxR - minR; int gRange = maxG - minG; int bRange = maxB - minB; // and split along that axis. (incidentally, this means this isn't a "proper" k-d tree but I don't know what else to call it) int splitCom = 1; if (bRange > gRange) splitCom = 2; if (rRange > bRange && rRange > gRange) splitCom = 0; int subPixelsA = numPixels * (splitElt - firstElt) / (lastElt - firstElt); int subPixelsB = numPixels-subPixelsA; _partitionByMedian(image, 0, numPixels, splitCom, subPixelsA); pal->treeSplitElt[treeNode] = (uint8_t)splitCom; pal->treeSplit[treeNode] = image[subPixelsA*4+splitCom]; _splitPalette(image, subPixelsA, firstElt, splitElt, splitElt-splitDist, splitDist/2, treeNode*2, pal); _splitPalette(image+subPixelsA*4, subPixelsB, splitElt, lastElt, splitElt+splitDist, splitDist/2, treeNode*2+1, pal); } // Finds all pixels that have changed from the previous image and // moves them to the from of the buffer. // This allows us to build a palette optimized for the colors of the // changed pixels only. static int _pickChangedPixels(const uint8_t* lastFrame, uint8_t* frame, int numPixels, bool transparent) { int numChanged = 0; uint8_t* writeIter = frame; for (int ii=0; ii < numPixels; ++ii) { if (frame[3] >= TRANSPARENT_THRESHOLD) { if (transparent || (lastFrame[0] != frame[0] || lastFrame[1] != frame[1] || lastFrame[2] != frame[2])) { writeIter[0] = frame[0]; writeIter[1] = frame[1]; writeIter[2] = frame[2]; ++numChanged; writeIter += 4; } } lastFrame += 4; frame += 4; } return numChanged; } // Creates a palette by placing all the image pixels in a k-d tree and then averaging the blocks at the bottom. // This is known as the "modified median split" technique static void _makePalette(GifWriter* writer, const uint8_t* lastFrame, const uint8_t* nextFrame, uint32_t width, uint32_t height, int bitDepth, bool transparent) { auto& pal = writer->pal; size_t imageSize = (size_t)(width * height * 4 * sizeof(uint8_t)); memcpy(writer->tmpImage, nextFrame, imageSize); int numPixels = (int)(width * height); if (lastFrame) numPixels = _pickChangedPixels(lastFrame, writer->tmpImage, numPixels, transparent); const int lastElt = 1 << bitDepth; const int splitElt = lastElt/2; const int splitDist = splitElt/2; _splitPalette(writer->tmpImage, numPixels, 1, lastElt, splitElt, splitDist, 1, &pal); // add the bottom node for the transparency index pal.treeSplit[1 << (bitDepth-1)] = 0; pal.treeSplitElt[1 << (bitDepth-1)] = 0; pal.r[0] = pal.g[0] = pal.b[0] = 0; } void _palettizePixel(const uint8_t* nextFrame, uint8_t* outFrame, GifPalette* pPal) { int32_t bestDiff = 1000000; int32_t bestInd = 1; _getClosestPaletteColor(pPal, nextFrame[0], nextFrame[1], nextFrame[2], &bestInd, &bestDiff, 1); // Write the resulting color to the output buffer outFrame[0] = pPal->r[bestInd]; outFrame[1] = pPal->g[bestInd]; outFrame[2] = pPal->b[bestInd]; outFrame[3] = (uint8_t)bestInd; } // Picks palette colors for the image using simple threshholding, no dithering static void _thresholdImage(GifWriter* writer, const uint8_t* lastFrame, const uint8_t* nextFrame, uint32_t width, uint32_t height, bool transparent) { auto outFrame = writer->oldImage; uint32_t numPixels = width*height; if (transparent) { for (uint32_t ii = 0; ii < numPixels; ++ii) { if (nextFrame[3] < TRANSPARENT_THRESHOLD) { outFrame[0] = 0; outFrame[1] = 0; outFrame[2] = 0; outFrame[3] = TRANSPARENT_IDX; } else { _palettizePixel(nextFrame, outFrame, &writer->pal); } if (lastFrame) lastFrame += 4; outFrame += 4; nextFrame += 4; } } else { for (uint32_t ii = 0; ii < numPixels; ++ii) { // if a previous color is available, and it matches the current color, // set the pixel to transparent if(lastFrame && lastFrame[0] == nextFrame[0] && lastFrame[1] == nextFrame[1] && lastFrame[2] == nextFrame[2]) { outFrame[0] = lastFrame[0]; outFrame[1] = lastFrame[1]; outFrame[2] = lastFrame[2]; outFrame[3] = TRANSPARENT_IDX; } else { _palettizePixel(nextFrame, outFrame, &writer->pal); } if (lastFrame) lastFrame += 4; outFrame += 4; nextFrame += 4; } } } // insert a single bit static void _writeBit(GifBitStatus* stat, uint32_t bit) { bit = bit & 1; bit = bit << stat->bitIndex; stat->byte |= bit; ++stat->bitIndex; if (stat->bitIndex > 7) { // move the newly-finished byte to the chunk buffer stat->chunk[stat->chunkIndex++] = stat->byte; // and start a new byte stat->bitIndex = 0; stat->byte = 0; } } // write all bytes so far to the file static void _writeChunk(FILE* f, GifBitStatus* stat) { fputc((int)stat->chunkIndex, f); fwrite(stat->chunk, 1, stat->chunkIndex, f); stat->bitIndex = 0; stat->byte = 0; stat->chunkIndex = 0; } static void _writeCode(FILE* f, GifBitStatus* stat, uint32_t code, uint32_t length) { for (uint32_t ii = 0; ii < length; ++ii) { _writeBit(stat, code); code = code >> 1; if (stat->chunkIndex == 255) _writeChunk(f, stat); } } // write a 256-color (8-bit) image palette to the file static void _writePalette(const GifPalette* pPal, FILE* f) { fputc(0, f); // first color: transparency fputc(0, f); fputc(0, f); for (int ii = 1; ii < (1 << BIT_DEPTH); ++ii) { uint32_t r = pPal->r[ii]; uint32_t g = pPal->g[ii]; uint32_t b = pPal->b[ii]; fputc((int)r, f); fputc((int)g, f); fputc((int)b, f); } } // write the image header, LZW-compress and write out the image static void _writeLzwImage(GifWriter* writer, uint32_t width, uint32_t height, uint32_t delay, bool transparent) { auto f = writer->f; auto image = writer->oldImage; // graphics control extension fputc(0x21, f); fputc(0xf9, f); fputc(0x04, f); fputc((transparent ? 0x09 : 0x05), f); //clear prev frame or not. fputc(delay & 0xff, f); fputc((delay >> 8) & 0xff, f); fputc(TRANSPARENT_IDX, f); // transparent color index fputc(0, f); fputc(0x2c, f); // image descriptor block // corner of image (left, top) in canvas space fputc(0, f); fputc(0, f); fputc(0, f); fputc(0, f); fputc(width & 0xff, f); // width and height of image fputc((width >> 8) & 0xff, f); fputc(height & 0xff, f); fputc((height >> 8) & 0xff, f); //fputc(0, f); // no local color table, no transparency //fputc(0x80, f); // no local color table, but transparency fputc(0x80 + BIT_DEPTH - 1, f); // local color table present, 2 ^ bitDepth entries _writePalette(&writer->pal, f); const int minCodeSize = BIT_DEPTH; const uint32_t clearCode = 1 << BIT_DEPTH; fputc(minCodeSize, f); // min code size 8 bits GifLzwNode* codetree = tvg::malloc(sizeof(GifLzwNode)*4096); memset(codetree, 0, sizeof(GifLzwNode)*4096); int32_t curCode = -1; uint32_t codeSize = (uint32_t)minCodeSize + 1; uint32_t maxCode = clearCode+1; GifBitStatus stat; stat.byte = 0; stat.bitIndex = 0; stat.chunkIndex = 0; _writeCode(f, &stat, clearCode, codeSize); // start with a fresh LZW dictionary for (uint32_t yy = 0; yy < height; ++yy) { for (uint32_t xx=0; xx= (1ul << codeSize)) { // dictionary entry count has broken a size barrier, // we need more bits for codes codeSize++; } if (maxCode == 4095) { // the dictionary is full, clear it out and begin anew _writeCode(f, &stat, clearCode, codeSize); // clear tree memset(codetree, 0, sizeof(GifLzwNode)*4096); codeSize = (uint32_t)(minCodeSize + 1); maxCode = clearCode+1; } curCode = nextValue; } } } // compression footer _writeCode(f, &stat, (uint32_t)curCode, codeSize); _writeCode(f, &stat, clearCode, codeSize); _writeCode(f, &stat, clearCode + 1, (uint32_t)minCodeSize + 1); // write out the last partial chunk while (stat.bitIndex) _writeBit(&stat, 0); if (stat.chunkIndex) _writeChunk(f, &stat); fputc(0, f); // image block terminator tvg::free(codetree); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool gifBegin(GifWriter* writer, const char* filename, uint32_t width, uint32_t height, uint32_t delay) { #if defined(_MSC_VER) && (_MSC_VER >= 1400) writer->f = 0; fopen_s(&writer->f, filename, "wb"); #else writer->f = fopen(filename, "wb"); #endif if (!writer->f) return false; writer->firstFrame = true; // allocate writer->oldImage = tvg::malloc(width*height*4); writer->tmpImage = tvg::malloc(width*height*4); fputs("GIF89a", writer->f); // screen descriptor fputc(width & 0xff, writer->f); fputc((width >> 8) & 0xff, writer->f); fputc(height & 0xff, writer->f); fputc((height >> 8) & 0xff, writer->f); fputc(0xf0, writer->f); // there is an unsorted global color table of 2 entries fputc(0, writer->f); // background color fputc(0, writer->f); // pixels are square (we need to specify this because it's 1989) // now the "global" palette (really just a dummy palette) // color 0: black fputc(0, writer->f); fputc(0, writer->f); fputc(0, writer->f); // color 1: also black fputc(0, writer->f); fputc(0, writer->f); fputc(0, writer->f); if(delay != 0) { // animation header fputc(0x21, writer->f); // extension fputc(0xff, writer->f); // application specific fputc(11, writer->f); // length 11 fputs("NETSCAPE2.0", writer->f); // yes, really fputc(3, writer->f); // 3 bytes of NETSCAPE2.0 data fputc(1, writer->f); // JUST BECAUSE fputc(0, writer->f); // loop infinitely (byte 0) fputc(0, writer->f); // loop infinitely (byte 1) fputc(0, writer->f); // block terminator } return true; } bool gifWriteFrame(GifWriter* writer, const uint8_t* image, uint32_t width, uint32_t height, uint32_t delay, bool transparent) { if (!writer->f) return false; const uint8_t* oldImage = writer->firstFrame? NULL : writer->oldImage; writer->firstFrame = false; _makePalette(writer, oldImage, image, width, height, 8, transparent); _thresholdImage(writer, oldImage, image, width, height, transparent); _writeLzwImage(writer, width, height, delay, transparent); return true; } bool gifEnd(GifWriter* writer) { if (!writer->f) return false; fputc(0x3b, writer->f); // end of file fclose(writer->f); tvg::free(writer->oldImage); tvg::free(writer->tmpImage); writer->f = NULL; writer->oldImage = NULL; return true; } external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/000775 001750 001750 00000000000 15164251010 045505 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimatemlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test.jpg000664 001750 001750 00001436544 15164251010 033710 0ustar00ddennedyddennedy000000 000000 JFIFHHCC   ^K@&Ij/IK~ε*Tr_s!H%_(mB[؛ xH[H am\cȀO`A-]gDaGMM^ X46.Q곶UlT<տ+_vmbqJOERQc,ŏ<X!xp=SJCߺ )$ P@.C)ss茶:BU揻8'璈lK9?&w(I';W ]Bwgז|%DQ, -=-BL_i96wsƒ@z Է[5_2(BnUx3"ݒ璎b&rϻ{c;a#%,@us!,g+ˡD TCT2YIQir^}˔ %(7W9ڟx_~ruH.Y{oZvnNXky@Ɩ^)}g,<7}_}ϊ;'E dm4uփwRֹS-E '~G\J$PHkkdep4z2 Wv]!R(Q/OدwwNPOyq-ël`sĪ ,[q;sg/ p҆@"e,{ĭ@#+ln}-ԮEE w};\HE'-}ۭOǒ>-]sSUS}Rs_w[b{Z_HWusRHĖ.8*A@ʹG_/g0.O(:ئ)*+X|uOuL2`/вaxEpQj&)>*QrhUc.W;urT:Fd2Wfj`mjrh .S@V%V@oMB  Oj^&>Ty:S5Ob~( ۹lB토MqS= ԡ }H mC.mMc &,-rgZ0L1 K!1CvL[i ؁Oĥ(s bʡ~LM.i(ù.z?4m#{B{CY1 #VYܡ\ h(.¡P23^=ЬZHn2O& -kj48D&t jIfFԳs~:o\Bʎ 9#v|2^g<>߲L򭨄9s.}:_խ \YQ".5EgQ}:Rn}KD6U1!U-JgTWp;?s(߰5 <"=)z=U꾌qj q1gEGP,+U\Y*NKrxSCD#YleVF 虇M\/bCjŒabFueT(٬pbc@NjV~9S깔oh:`o>T67e:ʫwT9Q1-nf^>8ۯ)y~CplnNfkPQvnjעeީ(s&s"ѕl'z XY!8׃ _|nQnFIlKrq^Q1,OocDp$$ :7sz=5( q& ,Q,.D2^|ry:}H6[5\xH[6Owy&cA'̪q!3u(s]GcDDN!1|"]/]t`竻^sէ;dP|9|T/8I}9'X6(P<`tㄹb% JyӲOS٤qAjm*<`+>ͪs'B`J<1Wc.-S\v+{D{>_C?S8y<,N/A60,WM:78*$GS?$UzUw.$:J/o6:T\-Y̺&~UXZ J9EkFeW!ؓMkkٺڥ8ve䳦5yUglVWXNnL; | l_F9S~[wB͈@%o`Uʬe0tn⢀~gcb,~)̻e@O7(O"^xke/ĚsZ {)CkQ= 6I]{M@Hec*]',ϑȋL7cU$|F5^3⒋9 WSm"f7cItw^?P#z^;(# oߜۑ5C PDsY3HuMmR7tIC/T{w3X:g4+9a*7X#^]P cQy2[eZSC~E҉f01n,^p׊|$_[5PbfBBU޺$~K C_Ӥ;<8LbY{/mcj3+͊Ǡ;z8Rg1\fdJ:;R7|/>Szj:vi)ƀ9b E lΖ1'6_|bS16wp~jOaY)6zcqD'Rk}Yjf=\SAx"c'&G5μy?]_C:(#oW#²[ q;5j$c1ȭOޘ=zOjtD7RI⺊+l %oBca~]=zMGP˄t;o.)QK_ϹhV+5;cOIxoyP%'J*I7uIT]Θ=q]Yĝ(pʸf}ǿW .5z%řIٹ3m:ð{p:dnՍѹ{Z-V :Jwz*yCWu/DzU_\[Aژ(0f@A`O:"Dg aT8xӐd&sd상D4yVU73ِIOYlN#n^]Ym.= 8͜IfKg^Ø˴RoQ;adsL+Q٧>V[w8I*Ny.ka;{k6j<{ם Xff6F%1jMNks`32PpGmpj׃U$` EF&=φZl'o~a:uSRGhdBG\%\atkC(˘T9W4kR|l)R<!뉾 >Yں۱tkOiqi4W'~_GX%F"5yF| Ҩv )٬ ,6)89n[PZ؃xsc]Js<BXzԦD_cb{萚aKq fh4ҨQKD xO(i!OėҺer`CL\QʹaC^3"Uӏ3I`, UD]ycK_;l?d{ >ײFC0:Y:Gz M_CA/ 3%Br;:勡̹[{?Z2*C,{́8Ӄ $ei`5O$)Aǜ{M_6VԂfl*̥gM'񽭭B5|c.*mZ}#6<hyuԳ`4Q HK|HLjSi}'*K5-fma#b藧ݨ2uf7p|-'X<ᷴtg#b}tjhra!&};ʞV% ϑ2uMwO&:Dj.JotS,*|HsޭQW:+p&qLNrDsFii4p)5Io|7 j:πqQOs&W:ml6rD!M*8zgMt+^V rA 1ZlI1YKk#|ĂwlZ6bZF8%$4GJ߬S NT?OWeÿ%4wJiJ9dڦ^^^4?靊ȓ!-11puĹwC jQV$&6Ͻ̼S4)p ,]roLuoc"ۺ EuuF<ԢzSN'#YR E򟽗F~d>ۦD2e++T^tTgh5Ta@9Y d l[)S9p%ldm$VUO/T3uJUj fD;"FB-WUhc-ydJR1S&Bm[ES4. AI05h/ 1GoXE?M6vG-Ӿk5SFJʩ]H1%F8$sVyƝs%+pz+ JÄ[; "o0e0Z;݌%?|3ӎFYru^ϩU~oaһYmX־ı9FEjl$,a919s&ŝo2t֮VN_N[i(9;/7x~l]8dt6UO1))hjNR#5ʳ]<+ aaj- =!3)綕d$?RFͻlo>*ĐM^}GVDB3 ʸj&qe9a+Ĩuz+ʿ5hs%c<f ڇS}%fPeI.[;OON],< pM d*#6q/ZZ9OQ–贴9)i] ШNC:>;A嬱]ĩA\ r`̮*[n*?_= ¡((듻 )wW֛rU_ vs#sZ)fI]Է CNS Lݟ:75L6SR/I(5K.CrQm\MM[֢h"3F@[$aZh/`a(_HNh^HAm.)ιe6.97{1)z%Ԫa*e>7/^bkș5aiV9$c jG">qƽ̸W 64Оg9慍xXm^Gri3v`N;lBE SyAvc>IߓIvE>dԜ x)4 Kʙ$],2(E]fy@ gUV'n!*vU %o-3@tUyOqctO,Yܵ۷VUs-[g9~ Yz30@B&8)GΫ͒_o`k-al.f2_G8Fk(VvTn;'RS: ,:2\smlV )j4|T̅1Dq&Cyzo o`Kzr/&:fs- S9鷛NKNBePo\p!^5ym j؜WXf0eܽtQAkEY_'r V eHGKG[~%`_,hmè Y|84ŤE>fR5 -#䶦Xu֯O/% }ͭn83hp{c-| IsT-ݰd}W^ݣ ;}5) /g;QUNd-b`Q-'BMΨV{_iìWj^gкk8Aiy鉸)*g~tfH_c٨ԯ%GT3omi㧺n6\zkhHC.N[Dn\ϒ[H)[I$YcϦ6n\+WL;Bc}VQDY04v#R"乧sd9ϥ{Nypm_:W4-iy.Lzz&8*c#|l$(e:ּlqWu}4QIfDK\fw7!&^K䭖?0jckz쓅$9J{YnG;ټ涗BY0RXE!%tW6X?zE ["\LP]@& ֵt+$dB(țzF gSI,a%LU?zL:k=}V2~Vs0#[kQD̄FZ2IMnCrv$)Vn|m.Fq4JIHq.RnhuJUKDŽWB[v ޭa]=eOZȮPRě;ַZ#?VM;}Uqhp.T焳F9sz"돣wF Jo}aϦ~7swVZ4,wUj[=ϤjeF}%ooG$162J~bw;I CIu 9(CI{͟Fj(?;ۍav;8cS/y5tX;"Ydz|חuvv"3}ruo 4S-z-W6=oνJ$ldjMO^|Xj,}d&1}XEIֶM…dJi\}RGvjiGuL'KvTJxĎ8++P}[%,'b#74Mi" [kۭԿ2l`byP\p2Z7F<bDRٓKOY^0\(rKs:y儤"п1|ueCKQS-*#7,`&tgqY,N\;opeO[-ͭՆ9~0#藚X@7YC;E_V:GVG]=$GksTZ:JE>#9ÖZe'ĔvuQG*Wү(jqa] G4ꭳA|]lLA%1s5R2osWzq#U 6.ҿpFkYdfXvvGDZx"P.޵7::S75iUuQ}I}$MCg[F><%N #l+gnW"޵-{KDJ Lt(ў` %T|8pRNAru7)7`?<ޓpA/D's߼ºi9-ab56S+8zs;V{P(V^MX퍧j!`r9RĀЗ$U~vr3ծ(fDnqe*ҐiS0dſDX4G.zOU(#'t\ PsXX3`6U44rhIۈ|@,6gq.RD#rLpN+Ҫz㎨::% ւB,2 uVNAa[cf'F(urUSy{tCQI|P,TծV#Jbc^n@&eᝦw'&/0}Z|^_Ik>ùFPAUoaCZ{ӖEo''MV0/iDZ(2x?0\Ғ TN6\t.V!⩊R;)Yv.]eq(΋%L~"S gv&EܤS`td0sERhdko /Q+vUGBRD k(uMoLrfXSYr`]% |mKf}bϣϝ ZR0hTW 8NiNWKip{r-C;" t%G%+([[/)ѽ] f`%rsR,/F˨WIYIMjJܔoVBpLsܡl3] bT[PߍjRDoΐTIgAdIYVi-u^u4鬻X-tg%8nJ%eʎ]˃W5}\vUX#ʓTV!}a*鯐_՛='%}׋=H]pBN|劖bVd)v%k3yId^xʧJQ5}{\(ȧ9Zj=MXl^Yh]Fq⓶q 9r ߸$Ab/y%CUvsk SĨn}-ޟ2,Ev<^b]!@J]܉?r+8 .*Ab"{-gO(^ߔpу.tt؜5ؙ4L*zqdkm_7%zcEE"#w*J`5]/Hr yֹ;X.}\xsHM (ފOWK} Bm^Z]$gmӯ^8685 F/sf\^ou6>Ǹ<;۩VVQV 8eϭIW Thlnp^:Sm+20T;_%a,%\sjWQS:O瓺]erc-Hض"Fhr8$fբ-dzvK y{ۭVZn䫲R268#R?f),nµPPR8_/~׺pP&8.9\`GtbrRXNjGTng8E`rkKq INs^N8,yrYˆc9f{\d*fE10!"12#A$3%4B&DRJp҈_+__9 :i 4-6KjZZ~I"RQ}W7-R~O/*B4_TU'cQ 4_&]q%}&5zM4*WbhT-]3\~k]VaVR8^xސVWZPZO>ҿ]ХqW]%jѥY5e{ť$[*RҪoJ$\̋gf櫗iChdKQ]j)B.!TO3iUA-'muHIU-.tI˟dUP Q*WGNu I'U/ƋTT/u<˜)7t*#%mDkiMBDG,Q(Mr>5q4ި-Ӄ\즫]tMvU\Tih*t*[Z9"ou"/'ӟCnB[*KRj..XnA2./ iǝ|( P7\)'t諭?/Kꚠ^QTWTT]*r't[T$54JCڂWTKF <93aγy"r5U8}KB=RiSuk z>k _1 ]) 76)~1XME .䰚lEZ&m+N SQ Kh%s؊&$i]0ٶI{tEϫj( <2<=Q}R% jEr3s+e^7< v!j?RRE鑟s ͈7_P7&li: -#G:jBǥFnMYd|א1BΣO/nO?Ιt]Gr\J,hp!T;/SFh2JvC È)j"ojK'IM*kbE< ?=5~ƔX^*Ձ5c*9:JJ:4n~.Ζ+i۰ZZz#(ߍG=X=-^ /jV2A9d{=i |kh<5= {^C)l;8: d A0qL׍M6x $+ed C0-ܓp\pCǬ] mUiRqZ@*l5H_~"U1[r)AAx'ߐIM=:"I#$W+HޛeR,eO']*4؇&st)Q޷'` ̚g1,+{0ܣrЛ;)q`LZi+3i#obܝ]Xg=w4 #M2쓿! J@uJFHtS[w"}J\k^7`J`Eڼ FnDbwrbab(6ܙxPjpY7ݶۓ.|z ڙhmd6| 'yoMMD<>Q_ޙ>/1Rias? htJ1T4|Vu>8N9'Gr m7HLA۟\3gFt9VV ڬXEgeUj,%\;`6 ϱ` %s(yfos*>gzMH9ic;SDV4^.b"Ӟc[nqm-ۅq/|kJ"Ӕ'F׉h)+iDLo|S"/"{CґUQP#3Jv9woݑ͏6rψSYrmkLVԏX mIoWr}ѝ[%<}g|pPFR'7 T.7 WhvnR gT2TB"m[k=jvvHJF/UiN6W ڒkwel22= HCJIN:Xv/*pmw"tA0g/xݧ&(B0ۂ/[Uoonr;RfJ1vh=x9lL>5&Icf$dvQfoT%JvYAd\ ,*Tk&1$WϓưN˾%ae.w*.VJZzEy)b2~5s}"S2Ck#^*ocH1ߠX(ˎ> (HNA# ѸO9~N[UA4%:-#*MƱEnWeizqٙj|nMLd9.8Ku3c1ym TImX"fD$8bx2QF4| }1\F[ZJ*ѻӎ9,kT+tC(STZt~|׻:N"9> '+;Ze>LE2!Y=G6ը֧X+@|Rc)m*2Wơ̺=?(Ѹ[J{[|2iCeO.-zcj%1Z$X`71NWՙUǿMS1in[T ]V %]QѽBRW;-#Q[i\{>K{an?j_t~ť;G (\%xSDr1i.,Dn )H0G;aن|aow?!QBNZŖ)4ecy,pxRv,򵺃ږ1t/$"-8"i꿺%Q,%7Qu_k4T[-v>!Rx[v,v81%ռ~ylVȦgQlRbj-3:ʷ;K[l<3x3T! Zɐө) ELQ Y2v:x7"7tm)VzqTiWX,ض}ҎYUv*S _Km]΋ɺ`O¹^gJdMtk69ra[@)K˟jc\lo)aձ㘵`ڽqͼcˉ0i݋/[s(2-5-6-.G8_MٽEX7c0x>ja >T.JtxlZoDqJqQY/ОmG747[s'iy°?)ni=mZJ-1ߵZm r#?<!kr}y=Iv&~m(;v֛Zki䎖TR㎢ ٜ_[lBǾy7ͤ_n|aZҋ&@%X -/\>I-)QxDgwQ#e\DǠ*]WU~|q6[-*RmUS'cf"%IZBnDWާ<!r)؏ݍ:sؠkqTEM-/ܕ@yɌָZ)W+e(6\_<9P}l]Ww"QY27=22.tH&ۃEP&ȇf];6kmOQ)1@чj 4_hאnPI$U~H-*RK.4tt.U44zڐY$"B^,j+$uQ. Ī;x:{4}6|,x6^a̱ϋfusdu嵕bwvR-Q[:M_%Hne= ousdrN>7m2.7RTޜSi^u IyQ|E&JELsy} ? }/8jn𳎟tmB|nՒFsy<(R\fgrunzpf]r2uݚ@0:`ec).,Ҷ24"9?[i otnqT@-6̆KAmy|Hb+M&ojl ?w"I%w{$ݱϊz0X3R;M[. :N2l7t. Krwlɋ6nS&?{s:4T}Yr,Ih&*jԦ3&r:Ǧ٪7ϯГtb:6`I"x%1[Y!G˜zc/mʓ6 U^[$2j,\^)'. 4clɡJ#a.N mOb[L^p 㖴zH4 KFCe֫"6*mwL @ >XW3g#K2.5WY }xPepw*l?Oz|9n69^$kH.UZ%bYqp26K|\y:P=Q NEH?1nMvqr/8I9M,"v=" Pii ML6]-=bX|]~%.ְ&>n/ZF|fHNQt띘+Ȃ29[{X>$_ĝޥxx0H/7ږ?+]W&TD&.AMg L@bLo9V9U@7 /1윥Oҋ!`k|%2p:qdƒ[$b۾8o 4kgV]t#2KF*vIJl)] -zk1[Н[ AVվv3*ME.ԋ1y>Ģ #O! 't, 6,Bizv E-"𯘼Qh>l &\iiUeTi0rXzugZK iO!ᎅS*`H o*Q률%.ĩ\QZ*ԓxd`upt\h aCHq%Mvd )ѪCW*#ft;rhE`| ſHl `" dyvD4=tHmXle_r#Y2 k$%pQHV4{e.y%X(1@FU4;J t eE%#:alAnjdN&ʋv/qJ,mGBt{t`m\|0=l̥q67!GXGGc"Yc"~L E:m/ڷq);u0.QbF=" X `Khk*1  arUGv⢓qOT: 5H9 cH }6cΟXQ4#SwЫIjoUOGk6QcRvS"'JP;ęA>ÂJ7j$piK0t$tq1?]v8"`S}FoO$|GcxF$L21۝xI+x-pPL5's϶SǢ$uRZjٵ4`F3% m1:ǂ2pg1WPz\j  Ÿc` *ZFq U"2FG8pJ7Q@ܜFw@zգnc#1ru`Fna[θʎѷ6 Llior#&HuĞ ^'ySvǎqS SvrG^=6UrGal~-s?3{mv*.x?]~ jq1 &3u׬*}c=ᥪ=xfbGPuqév_Q7}RM* t6v].y?߄#9%@M#ӜhA {"Aaa{Ť3_p0qUBŁ:ggH7:"_|1?@[k 2FI8i 6TqRNVG+VE ƭW$[rوwTX]6}XHb H< 81A۠ ;y=+imA4 eb88w+Y3: n`5h,AޤA ̉Bg0A0NZ8 _x|]ˏ04R@cݿ <땉L{7Ƈ0udˠNt#ɑ"u&uL4h SNVcA8wSLArI\t!( ݬl]y$bI[f1j IgLmdtoH#.*Dl9CHQ  upLzvse~]:ZN`sđGVGU\m ѝfFNJgYdUd`< S0f7`v2OnFo@ 셏o<E7 /qm4nX '*:GK 1/Lw!s*mpL̞ LxN"@B#B, drm&ea7hrN3iEQxH'Pw螜ۯ?` 70ԁk `*M E kӤ$]gdy&qzzLzf%`O,YD1=D䒀$/$O͠ JON'G`2( H*HlTuP  DdOio"鴵 wWL8 OP 7iX1bm=@b`K poBng a2DpN`2D6kN5CNu:팆j\ i0bӬ,ˑ TlK" $X9ЛEi[%2O\[t@&4:Wf 烄1 m`Ơ6Ui,Hƀ ?'2bcbF0L"5tp+UX=W@ grF%L}\i=<| VFPB~\AF[N% wp.212Bc&2Winz[H^':e'T ڛkt  s1jp!֘8Y$)+Еxd\5[$60 0)hX<&~X*)'+1"L/:~1YX1:ԡ@2G؉c٠LL8GIɂЈAJ֝_jlZ.:t'Y$9c{djNsǙcP pMAʠTમA>"1-γ11l %7,M"0Awq&`=SaM$;Btn摂[A  Z1 DS}]!X:9IR*C Z H+ 0E_0T 1HYBa6E_+  Lz$L9 ۍP1 wL\ $dA&1u\s[V^#-d*ًNv *kxIS፻IzT $q nb2Oy:V Bu:R!fغLC1G $2,3tݚV-:I3`. >I t[wr6"dNqZ5Pi Nߧ<Aۂ ֙cx ݁8>{s&' 5d@Ձ `/-a=bvo~-#H6ݬfLtC+deq>-ɽn\N[ v}7yj+-}㑮A9YʴWV &,5܌FJ =dhde}@@$ $ٴi)q?hbb ^ܐ >#Z\h.{[ERx8I=OS"Z]Rn4vj.GZj'ǎ*hͨ֓3%b HYkFn,#R '_;-hQ##38r&>M$9xI٥L`n"qDL$AD1f`[u ǹ"e@cؙD x% 6'Qr45J=B5lQ>:b1,Ԍ+Y6]S P*j~`xwj 0ZF1zi: ~+(/yHV\ULklt*zHJ !L:ׇH;b Έ)fRH̰b >RK'HpA@(ᶴFuFO͙. oSȘc?*b140`ڃc=JNAI&N2tyiu-'+`ÞٓhQ xK,ź !!J|#M$&tL cAR TYݮ$$$i$jn Aq<\E=$gio,SLRp6>34 gSnq0 DhPg8$ ;8ƅea|-ɘ㔡x dANvprHj2-u}n> #0` A[0ؐxNv q#FY##I)FcVnYΤ#6 D#RJ_2Н9h1Գ 7=ҭ%/ ƽ B>dIS06{ ), kr Nu[6{sڑG0dؘP|p‹Β'6ưA?QLa$#OI)t D3Wi,2D4$Or.T.e. 0kܑ m3f7BOt#`loVW\FNYӢp ,aq`~5d)/)c,]ݚ35zSS qzNq+5j5! JÓXi%^-0ަ8=KeL [yRhZj&jU ET~c6BWӾAwTgL H,5:XMK)ki?2Ԩ X]@5 51#^tR?¢9t]j e6 l-HZTU9̻hL|_<ȯE sAT$(Kӏ~r|å.lJX^}e Ukia4,ԭCL;8`Fr$uDqjS |> F"m>$Ѻ 1ƾ17A9YZU` 'zTe:  k6TSUeZa LD{ <3#Fϛ@Giu<,%Xk`d#f4̜4(P["0!\ڙi `LjfH }xeUk0탯>#iWŵ&gσ#8+>?eG94~ZZ E0d5B AߙU40 t8"&gFFLFMߔ H?Fdd i3p#R}fAƲ2 nU 7iF8QǸD ,q pDbb#mfRlBV=B{Lsq$]ԤisW϶bNcSu3ڣjN ́2U/?5fz#L Ok$ Bo d=v,{DD8U-eđ%"nc 8#bXu&I+=&@1$aguH>ȩCZ.Ui%q@BrUq覢TW`N"ڣ@i +ޛY8" :ܡfc0DUt7|n'!<I<paj0n&Ȼ>GqT!=Jdat7dX1mp`qhVG.f,m M*qY m-+Q 37{3PTPU ȵH93ծO{G O1Ldzac8Ac Dc$H LbL*NF.HɴtqQz/Ԍd2ʵ](ѓRP&]'suhy,rnSFj&$() kSbhLjiKP/lmh`htv&p< CLc;0DǼ b{:T|,2Z#q2X.q#8̈$遯Rc 2$7I!GHơvNqs,f-. Uu;)]胓#x/\IN9 A;F&0NӅGC8`qHrF#CL: 2"A⭾i q1$y `ZNAi;mDLDçZT%";0\w1 ivN@0mfIem* c8:`%fVY ,:98Zu$X:ܢ28:m Y0úDb`Dw,қpF(k1 E o7 ?+ӥWjՒ^j;9 'X^ЉH[,Le1 g<8A(?6f6Ѡw$ i=ZЊ)Ӆ޲جP7'%"Vn,ҧ<-(1/J-h CRM8!SY4éaU`hӎ8D5#*!RYOLZKA,J`ec|G =&Ma[=0kL**+IJH1eL'/QĤ[H6IIoTH'i4@ Vc~*-@2&2[,.,}Rah@ VTN`:o<LQ?˜!?+,xwn LHٸ3Phв2 JhF1 S\PV/P: %o͝Fg9 /򔹪5/\lk57e2*Gi XEWyZ3 xL 5rI,Ē`Dma%fKb 66 I(`%M;g7Bt\j#NI7Agr 7uNxSND Ձ:i 0"s624&buNdebA'UP mpd51:~ZZ !Ǧ%Sq#89AftЕ' [eNcQvyȓNI'=;_ s" Մqg|{₵C4i*rtC:570@$1)Lˁ1 ;'vIyRҦs?oOj0eBb3<*Jۡ$NӒwpJ(!|p'XqyP֭6-@ Oň !n9V A3<_V&ί:fcC< )SEӢP@ *7PCGNHlug3gN3Sզ'!0g#$`4Bbx9G X&c괶%b cY8y ߪ=Fg1$ar\hy~XʕU޲EAu!m.&3$O!@a'*^l\Pz퐠iLIB@aa$>ڡW9X;P$p3ҥ0:x,=JM_L3`I<m7NC6!)3Abp =\>٘o!{Z0R'A,mr  {i5ޙ\M1,1T[bg +" M\2 za*d3jX`)Su9ME;j $m#Jֆ(3 B^RaML*:_sHR:5a N`J2]PpWOV5U:,ʲA!D3ԅ-Q1w@0`- qi73]Uv deњ/HYA^@oS#s{됖e{^*bv`J}d&n_~Fq>FXïXuxN,8lo|+@ 9Nay͆e W:300 >Cmؖp i:# /\# V bXh[:ۃ8 n Ȍ@3r3TbHu*e)SQA I$fc,I*3UԌ9~^I 3 &1[i167A'N=Ui QLfػB ;Zۨ;B>A3'QYK@'hr `3:⢫k+#) )<: x# ":Aq%PTӂ:EzmP MpXP1宕$z "-l@bKkQpFLxG0G @s2aF0eF%Xzywcr(^70y$-g1.,F1M\iva'Pi4 qd:rG*;v0`ᜋ\Ҥ b`JԦZfծ P:G* %z1i \/ihe&npeNEڂRvii-P-f$S4ʓkj̕cE%n 6ޕ5T=m󱛘SM%!hb0/,̘R7-CZvޅ^M[Ӣ: 9BWZIfdљ4HY:D&L:Р%]@NB&IFr9 I IkI܂ =;I(9#rFAF"gLF6T5V*S1-i@p!Q/UZAc.X  fGLh&n?ۡ0k>xGXaҹhdgAN1X.ZnΤG{n?f?i+R)|/UCVDU H)V1 #[nYU+t{:bbLM^Ib:{u`ΐx8HFVL39]`$3×`AgCt8q=Y'N.㴃Y|pM[+Z”$E,[q탈HM" S s˜pAa i?ȹ+}4)39rf cBc9mQl*-$c@&v33_ЦТցrN.1KS SäY29JjJSPX-jziGC\ o*vVHf`"f(`AbR:FZzX#*v-P1Pp@!n*m \"- mZ BK.-$xeF URZQPMKA$Ck v-/xRkNEN8):QG$檻7s(Hpp֔5@:rۤ1J;0 }My6*W{!b9%n^naTBm'5j e[c *ȭ;3Rch*aAAd1P/x =CUZu/`~Tc'PUr-#hD [K-V?wɨT|zd$[P1kb1.07ϒd0Fr|c"z+ȈɞVwޛI%U1z~S0_Dl$k8%eLLl~15m{n"uLa^?d~9GЭ]5 Q\.`׺Idn<&A"$I17u0=88#I#!0@.VCP\~ n%E|jEY(8̟?+F$sԞ,#\~`f[RL 8o>RH@$;| tV/QQ@Sm~9~zor3c0Q =jU*ԦLTFe* (yR8O'˭epbԸcn0 ψ2ċ[̘ 83 {@됣Rc.]KʼnAݹ-t @ GqL2dN39"tձ px$簬9>'1q<@n`#jmOQLd Ed88`&t0LIgSi䉖ȴ$o>2l3ӜGu8Ucsu4N$t㩤ı8l,^tqlltU2@>1z=+uX dIr`mnAq$[p& O)е MSAY08& r*alhѠg"82p g;|GhL.Ǿ`)VĒ0t!&ڑ%AĀF9U#TIԙƿ]I>]ZSZnhӇ?1O00O3jWJŠ"z/NlSu$>s -9bu2 D_5Z ?ļ\tZrgIWr (@. Ҧ [A8q7.EE Ѿa iG*-'֭ܽ$Qic)x*JUԆ^ "[n=NHLOҦU2NnJhd~!MFP9 > ۩觘-|8jJ 5Q:JwiT\ .E"qE3J=MOtf}B,W 8,@ h=Sj%XT%JSҕ#?9*!b2i-ٲ}UI-0 ӦdBzRu63Ses1 TV :12į@*8652!`B o4^CfʊI GVGcAZ)sNJ9 #|7Ha!L0JybTid|܇A1@GL zcCx.;d3Lؐc81ҍ~$'ߎHlD8FLxa7k?`z,..GA[I'tNAH7F/k|s f (ml>\dcI+N$Hӫ"Lcߘ1A }"VfD `yG0B1$ɑ%,^iڏ'qK ZU{I91WN,51͕ )L7W#%q7>/nfUzeDP!Qjj-F>`\VA \F{qW&N`0&48.sFd c;6 AbLz餎-̌hQpgU)Txw`H?6qv34qiMm$&q<*؃$L-I߫f1=4Xƾw4noğh8c|L baoDO NOhic03 TFr. P. #|DVW RF .~SuDPRp~is#lti1#*`0Fݳ"sr20@#Y.\.Pێ 99zj*Hצd91 &zU&b @I'C_9|28=-MF IӺ ـq'`bzI72 ZwDI ua!FTt @)Byn]\@Un)x1ZIФ:I!xa;+A 굁 p E5!󤪩@F`*9PC-!Q]6T՘eE5NOԨ\qS e?4Q.-(,QFNKYN*p߉^*)It^A)I0HZ*TJJ"-AuC9+ԩͰ lBFCZ̆P]*yNf"!Vٓ n/ ԍJrVBYI"i#?ԥm )N\?QW zœ i#FXU=$9XzUMZ$VOI=2GA7zlA(=bJ1Ui J&m!2r%]E6&堯Z@Y /KZbfҔlQrK3EZHzd*CS۰Gx\? + H2γ>$k7uQk#NM;:oV.i IHI|yWU]Z&2eˋY`0򶸒;m4z#]L&F;ORtF]Ƚy|7a['VFP ?0qT3pM[XvVAnj':8;;[1ݼ̩49&FA oq M;:JJ=,I5"#[n`f:0N!$"V$Z? "{N5t)P.$u$s.s0%kmS&fqξo ?̙ lxS-0ĞqX)k:1mF3s`fA&, @g]mF@=Ā 3'ѕ]DuFam>2t*t;L4 6`̜t&`pO L@3-4 yc)=>\hyiɚw.P Zz +X`IͦӥDd!`·t1c<=F,=#*, 5Fonb.JkإͰ @ƙ`qԟ+f4у]OW%HFH#$1LIQ#Ipi=EA㘨 olw - * :k#&ZV$H=OpԘ'I`rlh,_Y!^` +*_6 V`y+BPJT$7F!51\0U*H,!]jpd^U@.yw%V;9%Pe:=+bI*^cKS[-U+n"ٕ:ףO4ڑyn3L"Ǭ[: JkTQ7 )Ң\ef>'cm:IJȦ2 f*zSF*REfZF7*!S$1UuJKʒVS4`MNVN6kr5 k V+T`nds/M*m"H&%3 \f`s:HP1-$;w8J 7-@\wT{ΜT KcFwhK;UyzrD)XBG*9mUZ47@ IM|k˚2RVeJq 66Cu<=ts^h?V@jUf}JjzҩOj Ŵ鄦!zXv5"<|n~=G]uo˯isRyzee MZ;g&ulR:>@##]:lf5f'-ӝ  R8:9]pt3GZLV1 oA x8x71(Bv8.&FLg<юY=0#' DFqlLONyqdjUc"d "Ft3:F =xRH W rhN=*1DYMjIevx5=Yte\*b5ǎoDudNdLV3jȉ1Ѯ$Q4q@~!:C#DBl B1wqïLt:Jܛt +5 ̗hR4Rţ 04m&'̗Eh10kH x124$ pqFnH9#\Z!eD =YщL=Zgppd d-Jjs\zZs UZŦ\;HzHY8,DUKTL=p!ncJG$aʜ' TW DWh&' Q>А+bnMU{f4P ļޥA'n0\{13&T Xt\AH[`ffV(^{´q,C/.8jAQ:#3D"99.A`!#!2]mf㤁g RLe$,dc7Ť 2:1s"d7\$Ͼ 1t=h3I..#Zy=Φb1IR^ŎL.0A ǧ,e)RH SDF5&LA:s̲>a`ݐ g$F#,<oDI1'kc%p!1Bw}t'H Щ4ź@f!0SLsf Ao*֐-e(Ө"%NwNl5@dI:ΡBt9cgnTAo82"e H+:ӜrmD4N`Y!pJ(4x}K[RZLڴӧhPP8gHU* d3 'lM'-eᘪztwPNrAI? fQ]/MjS`zmke~,, GHVi\I_{#v=3L өNթæ{:0)4o\*q*Ĩ.Y Lv(QS)MV4`=2Es QB<U FǶ7mȺT uE pSuzn^d KBAZ7^GaJXp m #Ht";:`-t*?IAceS fpAsHna/M{iNE3nss Q g.1\@~N>jȊC[0 7jmcQy%Em։"$DA0qI,[>N3JŠP(!4R (F,Wee]D}%5R}"2nD*YzLzsHAz',a}#oH1Mi6b)OLyQJzke"E5JUus8;!O+&DKS1X¶RGvCA ^ɩcz `4zؠ+UpT\pAў Z>-iN8F4`UlV4d1Vj"ĚM5Qj*ԨI!J(%* 6҈ۂA ~i$ JOw09ꬥ̓Pk+ֹ[Ñ4 rI%Bdx6)†Rm!\-:E4\kb^FLu\Gq_*{Jf/0UzgV<=QSH7*1j̪*Y@ $! Yrek$E]fAE,wC67C2>E`$lPy* uF UZi*- R=Ь:,3lc㮫7ԨTE5vsJiUKf%r)$!.ۢ<:׬neNN7bH|ށb:}OէyIm`2 xgc}cD?x;xpw8XFv]F19\rO3Z{=:Ix 51ByMiOiKUV!W5Nf*)%}Φ1NQPiSi*s ELVߤtt!JꑈuZf8P!B[FA 7,i8 Ft9 z>Si$Hf.ݰK`88БY|@UlLfD &~3ŸN#3~ڪA cl0rtp'}]n>L1'08i7m3i Zg@ vbz#5v:{FR>7pȀ䨼 qA =Wi編@Gv|cb4ݛ9:b:D0o#=C"4oզ"x00tؐHαq99#q(2 0)BbBm%0'u]`ehub3$&;c'h^jm&U|wBm1APqŽ77 \[  μ|p>'B\ѹN: ADdLUVjoS9^Qd3s2PaLhŴ"ETq3'ʑQT1\F"ھ*0ME娯@i]7 +M >W**]j2 @f m, l*U4!OmzTT/!j>.FSl 4F!)=w!ʛ`=%e#KEŦXXmp6ʁʙ*$(Fœ(bBY⣊O$6YdLmOkebTȺzMаe>)Sl"4q(JUXuNF_E*Vyqq@f;1RBKXxWpJ LP<M X@7>ej I2T Π.C)NMo!&f+z.KYtS嚫=wJ֐ EhX=,fjfYRSv!eAuVW j)PjL@TQ.1 oR -`PIr4B)9TZn(2-Xi]!1LVb8VJ)1Q=3RBJslRF:z/7zm`_W eJkXڡ  {l['Խ"չɫQZt $1UX*$A/rܖ UJy"K:ƃ ܒL3bq T˱?C9N߶3Ao7:Fձuq1:~7Xx> ȚIjɑJDьSձ|=ǧRԵoeK-4P)걺2+ \:)PGUٜ[0 s_H(^R E re.WVKF'm4B.NJb0@1'm4BL`ic31xptL#x$q!`Z(aB iz p2{#0` n1t&& `2iv"xtPc%f'` |:r}:'0`4 ))鬐5g\H49*:)5dRLXѸR,P' r.QapN7&e;Yd BGI ;kÉ#:N]\{ m h39']]tլO1r5dqJ$SqPSLKie80c!JM/Ĥmf.W 32|ˏAmJ^Ѵs?tMU%eLfItS hV]GAPY( !`΀)j3oRkV?C.Ua8%Pp۫h$LGFcQ/E0tEAʏN %ej?qSO*9U1.*FO^1iIGoS MQAd ʲwdHUoJ~j7NecЫRӮzP Ӧ &uTVA\֦TVQI*QH8C7{|B5 抻*b.%B=Qrt)9 2N 'FS .8UsQkKD$J :{y{*HO`2|2r7e1VnZ\ʰT(hbr?xhezy:UXp"LII/32v9:-:h#@% (SH*fN^~k*Wy˞>7:аSLt,N9R)L%D?s]uOAUy!ƵHĢ+vf\nJ `֘:3;H:qX5r"FzƱoѹ F7D}-p 2 I0 шzuyLN2}Lk#Vs;c$ '\+q%l20-ΚDsiSFt_|Ѿ;h|o A H"#@c/L6}Kl ig#bwbͰ? #$ 3uLNs:?k:.Ld/Gik$k ϸۊ$z4PJg[ f u+| 8 ;X߷TH'Ʉc;iD ԭs_p{thX&'dw8 6!c `cL/p{uB :BqS̠VD cG=WyDVM9zk]\zHI1ΨMU5s<75fZX(*jkP[K ֺɳ'0tŢ,U.7%M'^*ǬavZMoY fդDʝ@4EEb8D/{U *0N{V\ԴctD$ΤΪ"G9YӡI\LֹzDػ< ?g4\-pE-L68rU 6s ނRF"ZVR7Hɛؑ3ԋ ݓd5ni#蚣Q9|[t_;4K*嗥 IU]R*T X2+yxcv+spW47"ʪ E R'N7]*n3j(-7݌I9T?/*WQ\gs-W򊨳MUQ6Ud4 SS(S!d%$C-1.;.9>"D̃3Զ| N-#d1rev<A1}\}(RzE:LQNb.gN9_RZ&f_&%jUm;@9~VPQO"*Eaʒdf$8c$[or <5Zk!T- lfO9,jU`Q.u'#q%J C0s"Aρ :93' I㘮X҂;H2Jg3zހ뉓%FhH啑pGQwvU "= A'lp& .h5Mόx;mkFٵ$Ftch26]'`4Ϲm8hOf '1xc"zWƺH: 8ASF3846 Τtuw?6[;I6q$x{6Lā!Gc\ⰾ-d#1 -9ΤɃ;m:㴰9 OFoؙΡ|i""t?PCHHbmIӫmqQ; 0\Q3*nQf2ɈZq8$1wl E:y%I 187r8VtcA+q ;q8@9ձr:'?w'5˚Uj<5%yk>YX*(TJmIofaRV7Ts_6Ҧ_80u2}_O&6}z3-m%hYE^iN(#jxz*[i5 ԧop0*@ƶ‹QS@\ӬTf?)e,AǤ>#'(17** KmR)GbhVRt@ha<9ֲZ)~7Er-)-._s472H2Do%j4=+]&pIbnn Ic܂DA@"Z8ߊתFKW'}ˤ!I92Р-0^ĭ~}&ckQq**1ͭ͗zPb9=5`lt֬ Ҥ Y3u@sӺszG[=,c W둌|"(BS[*-luV`dzAhR|S,!" (9Q6ThnxR "dJ &t}23%.56gPu=&frtT"q$,sIr8B6˴0eƤfߎ[,昪"Tw7MVr i zhhVƙԂ2:38* j59e9'4;Yeu$k@P -H |KgkB1,buXuvP}J^R#Yq@ת*]0>BhKn]Fv;kZ&L q5<3tn@γqLchA-HB*c&'cL+=ǷC~vN9cy#ff><C ĎIrt 7!zb0~X[HA6Df@rfF pNd0@L [)HA:#4ǒӑc'iY l!uGH'@( HD|kOt ,bHҾf&-숋i# DccvYI?"q:lX3=n(eLI9 F4 bq'PGtX' 2B$0@1f A1 p;aGݘ"fpy8!s3;<,1Ȁ3 }O s [LTc>':9ܛ_k# )TfBaRҬxqɵz«5(WQ U-D bG#`#ւ0*9":RI& B%j4CyzzQWtAe CW5{ A/RVCNl5E\_#IQq0Aw -A"FѪ,BL`J\ 5`mC)R]qB=K+rUf[ U A7j`*>Yط҃ arw6F8VyD#yT`c r=#{1_Wx^3xQSV&FHݏh#@#lT qchd)b`2 *~@=3p&@6G.1`:$ sԟ H7:[5Y/{1ݟmզv& h`L'ql$cPc#C1np&LL6N}6 x_}( I|c믷H"A7x#'̒&q6<f$b|D?+q#i2'}xm\d\{{`a$ddpVA=X2>b,u-Ď9$s8'+Qr@6fzci C|0 bu0Iy>A$C q xidH)@pT1:'"$@lH;담;U@J+јu&L{(U7|$9>4$oH̑7뎞%fI+ 2$)#$EA tHkGE#iҩ^Gzw NZ,gu5U/uD'%E4=-dH b] PZhP/ V*e%U@aXSHUGUE*TIU)!#q{6PP*t $u^ %E>'&/ETQ6Ã-JO1|=O)բOkH Bڬ-̄e9F%I[tU&tuFdo /Je9Pf贴`ipa`[_t]D0dF!J͇%MMp>'jU6D`"yOn`Ɏ  ̹s9ftۆwcJl΃T嚽zZ4KKSӂ62n^jL#3P)De0ywRm:c)a`c:7#sO6TA TksxhJ)FhB9ctF4|,JbޣCi>d;{w4P~-M:AҦ?mY,@ 1hOP0<*:΃Mc3L~Sq >2 #νDDj,$ ɐHGt 8UcNpDwK13S,7  s-pO<>s@?"'!?|Ci\`nxg:7$=I, ƺBRH'K2gZcρ Ji$u``\ yBp jFg@8M$4, 0}ǎtcn` 9]? 3oB=@cHt$ 7} ,!jHlN*,mK``ܠS<"/cQ-!X8E$mlHlǒI#ȑi2ݢ j4] 3.$Hm θ&L_p`gU;jpw:hO OA2f#*iq?m)8!*P˫VV̵o1ܲ|?C%9_7ʪ`AݛUQ:mFw 0)aaWF[V-wt/TiN4 2 X17*UVMK29QHpuloz|F>eZm&O0 c RJ=HUxzm7H݌Ltt`ǧ3g:F"$L2I1K #'pb.d&=̤ɃÞ/E[g0#]9#1s!RO:H? o:⊽zM)hZ6raZ1fGZҦ棞 'kX|ECP/E54NjdtPQXdw2=OQ?-ĀbbsӨ1c*J48m I`ȸqHVbBӤ0Q&nLH$"q k2MF3 $`\52|ZAt+8e $$qa7~vȃ8<1v3rrDTmDH39NY@M0KZ`qzI GLj`>ģ*atpƬÀ^:zbX*9ʆ|+AQ bN[=' Q]A֝FdSQ՚=,L^ Āb%E(Sj TQUܽ U^iӄ -*u\72OH $KmZh3jҝz`{~ե_)ڔy٤Yb@[k]+d@:bDL s1%50 l%Lԛ95ޭi fJ / acI92CqT̲Ƴ]hIUuI[V )Ӥmprf$FIIAa3V5h @'%2bDCl\0BG6;{l3321& 0*Sf^70wLQ9580 GA้JOqx*s5 *na0#`\`r(+2*je= aIh8`5Pj=w6ri=7Jjs>3p\ƣAn^r4}!U7U`0W`~]8z"ػ d.t$G|>=:BoTkI[7L&ZYrpK Kk v@L?`k#xiɌcQ1܀tl:lA>9 /4]4|ĝ53?dyOXۃ"[>?k9qRCLiI#-;{U(i΋ ~Q;c o2WO*BFĦs6kQ^C15KSdc.=DisIJDo0ͩrf3Ď_q'A28P LqSUcd, `:ZL{73JhbIwT"db` iϴ݂fdFHN[zcxȓ FkT8yjxŢgxqz`t~H31t#\o@s @Z?N؁18X1gpZ=9}4 y`n y._un lrgO>f9R$LZM( 'B~eߎxfm%`3H=Ͱ:c3']~bcIƸȃ0ǗyXX Zs 6 jI~Q0 ?Cx) in$N#c`5X JȶN6;T1V,uL $1i D['\VttlΤsŷ*K6#&31!.s+{̑ Dh8jT|fg'BNfظcXP ĨO%H`A!HYS ^Lɞkii"^1LS GjtkT6Zd1&p;*oru鏚OӃO,f $(@`2&. ( bFq$sbrD)SY!n&ErAm`@1Ek0'eޣv(Z =&bF _8RbM wmt8*YeT @A r08YzBHWE0J5RZ7hN:viRE\6H<ѥD=J:=:KuA=Hy!ąԛxv'y~gB9IܷP 6ۇ#؟Nр4$lȜfN:˅N5DA43}y $gMGRKo*HAeH8귻6}26 F8A V"E:M$\3:ͅzlRNYjb'P"mNx$Aͫ%gP.ld2H iDqLF>]u0L.{݌Hw9⑆&ˤ ńh3H(鈉Sкaz-O>NM[&m883>L$D+nH NHm4klNg+vuvc\}8+=QcV5GI @;9,#L]+$>AxTW$qǂ3 R{''3:x1G_#H1r}Q#s$όƧ txۻ{5K0Fii3\.g]6` lgƋK|-Udg7q  r<92q d3N>NJ"9:}pEݥm:; ʉ+'QtL[:jxHmZؓ77M[r4 $wۻ'K lIszU~V,I,Q] K7ӜqyF(zcVW(>[I>'6Js£M5z3 ezt"SY86kEvF[&~Ykѩ#11zg2& ߘOjTP)3a颎ޕ"```k/73̸N ǘ>Lg2zS7qЏ?r9~R5jj餀@jkzƒ#Ȫ\ȅ0PFq.Ci11\jn!;k~Z0]Hqn#x6U2= C" c0q"c؜C& G: 9FZ1*AX3p~Ypc6?燦:tcyԁ5Ӈ`u;Fj],]?xs9T\Q"B[L L9Pu thE-l.-k0"lɴOD6VH-| C& 2!VGmƍo çp$htTd^x m8ZY7@d'Znh*g鹎#|i=38۩mAbu wA$M|Y92$벐[텧2Arp3NHȎ1'ۄ"='3#'uُ:Aƣ &Qʦ7AvT"}bTj&R`N~TXwXٜIʤ'187& M2`LsA{vB:u,gs1œI$@=*-T!L n$ ~^^ =0q; Gp qͮ ɑ$qT: $BcqZX=LF'Κ˲zF mfj <TcQ3+l ,O-0 ZZ~fA1<*AU,.+$#@En?ϲv [Ƥ\ R#0%J@-\G@gOZb c`&rbLsIV1 trLt}yԒ1RKjPzMj,S6zxrtyC4kMUj2ӶBבDECji %d Kz:JU@Ki ZOTewL„E$j%~mى)7sCZH56Qp9:%mp-ay ]H/L(E5`q&ƐID@FKl뮳:M|5hm7(EXh%VĂ8V]]Sz/MiAsW Cy6yZc3ӒbWRPzkO+V)XED(>4& !@Uke`$TKjGSeAR E | ʫ>@Ie @ ^玓%nS|(|cP4ptKP'r9*>c=D12ª4p؂ezCJYrҬc>F4X 07=6Ftؐp-#km[ 3rg1~=s:\T;*)P @ N}9^`sUO̵R Bk A DJ Ԙ-WVh˖W6BYVZ(68~-^:KMl 2hqH,rzI[Ngz9v{Vr*$%L(TFk%n^~ ' z}=h?-֭K?1 K$gW . \cHs8s/P%fMd*rF%EtIsC & =^V*)4\`-Uj[ VLhSj|L C :?s Įw)вMㆻ+Tzzɑ @X ~M+~ D&}#7US%f@Ayz1OQlP`92[.2G0h$I ZyꖳT{!ɸ%R QK|թMY+ܸ?{z)cAdfY ۑלM mf-#'+t#gf0 "t0ON#t21#^jBhN?ǿjmKsuA>H(q{ P8D uXӣNǨL5IxKISZQ10$tmbquTweY=L-Zv$#: VmKh}.ȴU3: MgzP> *KL)mvƸ#E &s;6cM go66G7LA.1r]mq& L!PӤ7Y"YCN"A :>wLudOӧ'Ib}iA1:m06E2LJQn)st0f4XզqkgLN"u0!"3LH@t 4 73s.7vxtC9 18A'2ĀMڈ':`H l"dkTQk- 'sd0`|;*zMjtdp`DM'$s狰)S8;T5ł8 ]N@3_Ɠ" #C%" 9̍fIA݈?A3q-3q̂n L qsDn C\|3iIbFfF$`dx$Li5ۧ\ND."c'~ٱXΟ}32æ3̉ |F:/cQoog;3iۣ9i c_bnl!gPA#B=ݟ$Κh#6Ng "~ɸDOLAlVĘeǺ@\s {VW $3L  ٢İ.ɂ@c1=8stәi] gPe3iiOc0c;2g4 c9H:"Oɖ%1hex,V;v28~MRIS,mE;L% DIAAcW%WR Աf`JUrLqOyf#j~GHСY (d?O%Dp.ՑL$x0 BsjU jUWu[S iH^$,Ax~Q$bYHtj1URp `)tDq2TҬ``Lq=OPm2UF aڒ#朌8uE|O:̟uCRZruABQKL/N$f q9DYfg]UX"DN 41PFvdd I7im` p^>.1 ԏQROO"arL8m0NXllnc @9$712.7$ɉ&6ጃi7U':$ 2ds$0M+96aF ʍ`]n%T5ȸ3@8&ᓘbcM:{<+rdM4 ِu>:H<8&=^>J#IbF#Ot?7=0C p3':pO:H3σK] 9\6]æƂ 9A2g>fat b#cPx*lX`tc zxh{A2udv@cx>7jel5Q,J^iK+P«BrZjzfX-; 3Z: 4[N3T31V3k^IZi+Lؔ}5:1BHq &_ xf!%zqA"p>gW.* sqX@X6K)껚jIW\g ӆ :ƿdk~Z.Rծi%BAimƂgF9\W*J>D2ժ daŸeU)ԆwSRSb XIUOH:$:ch:?r{ώPB÷Ly&q&~ %C> J>~#S͒YjO],eT^ "c_R0)Rf 3EN1Or irY`=OQvkiq0qLL3ٔ3H+&q,߰U ˮtМ6ĵ_2*$NqU<7Wi=扸y \'6'U 3X!ҧqT65dc)N{V4Q8ݙP*#jYmm'8rąj Nm&|KL{| =l3lB5#w>ΛeN-0@☣U* ZV'j=V=;@VKISob:gp"ThW M I˼#t&X֝A:MąNJ$S* mߖ_xFqf8 l=RJ&A03Ԓ.:xɌoh:qAK; 3H1v$?mץ'VEMƣ0&eن\ >Du9Q:fNfOH&Xw wx]q2q s, }c3cqqMTר3q3s@ O-MᜦxӤHOc3rQ&s1wM 5u3xoIV<~lDnOv qHPLth10A86䞒{ݭ3fsyYy~XԪ{Dn]ȑ1c<"IGzOI؊3_^+G-5х-Qc7Q'[rx^n2WjԦfiroffbf|DPZwuud>n& =[i9K292Ge tOݖHNVYK Ug7Xш$.`R82N NI P" x}n65X19aMD7efu&A i׊Ad:.o$7iҷ*5hҩW w֠olf*[䒀ZiEPZ- sLϜ +bǦF \fm9ĻA#I9JM[R!{ T.|Z~hz ZP3MuQ?T(ҡL!{EOjd^?d |"*3Z p\:8Shm'C9+.%OMzuhݠ8ʃND~+ʽH0FG{&s %dt?_Q gg ]tUBh0P1۝s#"ptifj,ZэM#S &SQT:+#n۪cҼcAC~\qTȋ\Fdmq.BY鈍|1 C,H0،B4@HQ9S,=SvH]Q& Z"tMdmNԨZSOԇ[ ̀7 CF%B ǽX稓8pXI29t`'Xu" UC'"GRtzT7IӅ@|>a@m¹'C0N}¼Fl$pG\s,NxF&fV8$%iqHC&HR2.1h-d PL `2ڃ1T`k`bDιO%1i\hG?hk(?>}Q2N1@ԑ#X:xc%bcu"43^e$ 1pNd OiLm?W@p vΤqBT@99=۱m-9㖢3n 1&4nC1ܿ ?8e5*ԬJ|t?k(3U@~[0aTjN5W|J%?ZGnG Ȗ㓩cdx1>G3{ROnrH_8-fc2AA ̟qF'='!Kൾvϼ?P:ţSS2HK r7;T5/D$\mi :鬴V 0e@␹ ә_1 n|qU!֐̠@[ TKGS:ݔ-V ; >'ܜ0u$rd :$lQ6Й,C9 ̹`\>o37u|Dg,ڃ8~sW< >pd6SzgFhG<;9ܑ?>:0uL <\ă| x;v񠘑#Y #Lyscǂc}rr&@QRS6Mqnu8Hc.,.$䝢܍ɝاcPd:^* BA hkTsQs,*R=Z3 z]b9c'2yjMҼ")-'a`p @9ޯUjZGPc+$OUQJV(/Q TM"1Yٚ3WQ) :Z.r.P[+[0(i;uC9f?UWJ %֋|A`[MLdc3ʕ?-+-ܵyG`"NÊg+!e&t0x Li%3<!V(̫Q.j",Z@0fL\xѬ{"FjuY |^nG0 {JjȔU:D̽*׭F+Z^P=2V8ojeqөR$tM݀LNT΀Nh/,.48EL5*TD5zڷ}Î^iڌZ#R^}WN @iӗY:`B(:F @Q="X+` pq8L"6 fhMQ: w\`qIeա.Z94# A*d Ê83$ ܽ)V_27Jil*"z7prB$8h;vL.F$ b&sd`1c I0 ZV:zZJ9^[MGo~/FŠ?YٮO'a+T9?t30K^\@q0C# tlNfrw"F@3w<ǩHn\䶇NōlN9u,H/n(#\̨S-1ʃoH. QTIj Eh#sQ([uZs*4XʪQ W% LCZEɺxۀ!\ܞ m.:"@3vxJlƀہt^"3q$d>1\ `#@MNq$b2"@ prJ O勶D|,Hf.#aO ` zY06{j:udXխqmoG8cOR.cH$+U9-3:1珆S_yo?sZIExvr㤘ϩ ğ !v#""uGMQj0ڳ;rt͹b )!XH>'zvX;I By0-u4`w"#rAu\ȺqKՙ#U -"drsuf$#84ZdaHŽ]q27ν'y""deI;G̣XxC33 fbHL(lΘ :3N AS*m:NH$ŮMEcQb{ƺOT.@3+1.;kzf<(zi?p{}7&x\\(918 t ظw$F@-A$j ̌1VIn%F LGOFr S?ͪ<ۇϏtfD01:0ţ$xԝdȁGoDڠH2U8s<5] ok.T87MO1$@ $t3@NΛGlX FVpl0nlJKNEV/X)Hy3JkpRpRXr/b ٛa+;f V=FOs/Ix:o'WD@Za:w~Kd*QPf&L\`K5M/-]ߑ1yH$@cpsYN_gvSUl 0I75PfXɒ 6 `d5U-Mz]IJ,VָS(&4U^jv5 t\дRtfХ%iiJ4&Dq^(\ʎʨ!b:ԎHڷqz1VUEJ" j"eedׂ;^Cʀ`=@ L5ۣ2+hMdH LOZNaǤΥSR.'!II 9UT"1"82`F>Dܥh=]Fa[<08=PUa Zg189|wy9:LGZ3%Ja4 +N9qq3'"|,:,ul* DlL@# % `Fs6c3nth"Gx&檒B:yþ?Mv8۝ᤈTrG?m< vF@ ӈ,L70T40pFFY":0O*ʤ E0g@ͱ[6g-f7]HƄu#}ɛ`zV kqȔ늉a$.ZGN9\`΍%ob4QtRXVmHHlNȂbkbWPg[A9fE:^L$Dc7s^ZگR4Q '͠AM5ܟQ tpIV@zjA4!|O/)ZT桨̪`\̀ …Q<#C CXETj&L\xM_U9jnK lZLd9FG FԢQNIB^:SQYUMnbҨ OT##,\ S |JbHf!Umܩ%H$`qB/F(b]`{)$\ȓ6n_59^^bJeBAi T[LK5|HW~Cs)WqLuQ/h2k!48.ߖ"*LۖӫR5ݒHn훺aw]oK+  Hǎ?gj ?p_JLI=CupA :zIjdtL@hXՈ*8Hp'ӦTIM*F4[߀F:;u-tF-0#x*p'11RH5P`c<~J0h0vUūhvf&.1w8xLL[qR-Y  yѬ 9NqiU zՉ~P@׶Db@9Q58@9:*d]%[=@aJ@IO2`=-0&Zm@C0f?QǃE]mlث5:DJfڀy0+#=;cӞ,q9ƾɶ @dZ7xGs'|b` iAr51°0sl[qh~C"2A>s ߎmWQ2R4*@$zΎR[VJhrŀ#\\Kf5Eוk`K?' 3hEj4z&BM;Q# ̺*0g=HP{us8M IrV,JaI7] njLa+.S9VrtiR3F0=*w.ӀcG 5iP=+-nvƚ7߆S~Nk^ޙf`LޱNȉ9j [7 YQͤI5K+WN)fE BV б9&8yrR40L(Me@ŧUڍ:|(<2ƴ㖤1Qa) =VTRTw*, UA 5#.Ne%7BEՁ /qۙe{Un#a3r @iȦI lni4Lc3T VV-r6"s3sC0JCLQz@\$HRVMs"htR#WPn!`i"`[0x<ᑩ 1k`Os#RsDob4h3D[ qpIlZ(mQ&15HcЬM:Y* #R$hROIqT3gPn Z&nXwF"'8v@sѹH@nH] Lg0ْsv$fA͑DݦRDojwH1 g]ܰm-5&pH$ꖟp`mzBg=LLy񧱏̞9oOxCYv}qV~/οm8x?~%]b:Aф4Hi0|uff zD I=EJ:ǼţL̞&4:`ad2u2|ݯ Vsq9gyH Ii|-D/mF:VmD`ZKSJ% $ۭJsuQLY q=֩ O{s[p6pq+4 hk'jDhg #l Ҥ4@D3t1ff`kpZQ١qX 5)K N&]TF/H, Lۉ3A_!zti=GT4D #ezRku YNvq'rǕLk2BZL,B4sTEj ߎs8URD#bfF湊Lt& s=+\7v3,֑=G?4(P<R dNcPxR@:ddMcP[M)`I$tFEP2{\HǕr4A[0Y'JmN~`5qEL+_"fx<3L `orq`jD~C#82mӀH Z>eqdDRaČ' j"zn3#i61tOrD @#H̞b:LN3F^сK x7{8fg_ۆ9 ?߁LmޜI>ov7*[ qq}xd 1 }N#MXP6S'L&(+ &툋e׶f蓠1n-73; И?~,jugiyfZuRЦK8q$-n$7o֨y:DO*k^[TgCb[ #~B=Hw 7c1X^nT%z\f0*g HEIQVnep qZkj }fDFŖr%TEc!3n= njbjtE q@6:۶Gh?"%ʓTt)AR elS F g_ T3<~\ȅr0*+V9RQK߻Ror@?ܨ}VdQp / 0$N3dүP,mz` hx|iHnx6flҐشmFI4'n]t0#|+-KAXE:xXiG,:e@%OVc3f9 ҦV@1$qPrg`%FTԯS#2qLb(C7@mAahbGZQ\%Q᧸9燣Jޅ 2H,$=YoRf)0U(g 8_^V,*Pӆ[bqh 3|UEzŊ-P`c(̞=bȧ Mڑ \z^TH*U2U1ˁ-IIy䟘f>uf)31οq%=* Eur{[Yf8#@jtufT:d\D  + \L:-9ͺp4 D2eVh&K#|t&8=I`X-Ù N('R,'m{gWpqp|3"&&pF&qd/ 2@H1>m8Й'%}o&` No[AP{aq#فj 2i#A#ʁѸ104 +9̟Lnj.'vj޿O8ϋ9~n'ȏO~ dcο21v8RVY`uWmq%kI iʈRsi9ȝdqIDFGy㕦=/, f0(a'ɝ k18ԑ:OOqh3@\@x$rT+V*%%nemH0| {T1v`t"q.yU>7P[|cJ͕"B'+*|G?@jtiM2$u >9D=gD)5S<0)F0 g%O}j'1y!MJSN6Af`9GZa\̚d ETDnaJJj]c! 1ΛÚ=Ufy'ckE:Kg3rYذ'jB>RS VyW] P\ZxF#HENJרtPԸDTzeUh[7ĹPGQיt J48m,(v/U"€T&"'-IF4 -3y,Ռh<Nn*2 %ngHi)ZJ}| A~^j2Doq]0 q"$G nEڃc BH2ĘuB틼iF2sI$W¸],v 8Tsus^IY$ft zs qrn:) |Vc!PKAFOU&us4_B %X,X@ i `֫NՆ* u-`]X s=^f4fM iPaw=PI;EU">7LDgsĉqA*j9jn12N3ť$\?qKVq .cY'1`$q2ԁ|@`EtL0H/sTZli8؄Hf l'$ lc H:LL'$k8`cFj`gh2v{Fv&8F , s=DL{j#|A$F>W6krՈ?7&>?y<>qxT, NuwQfNuAP1cTdHZt9\=;H'B|ȁxiQQMOHҳk,Wt۬Y?[b$};L~`a]rdͪWGO6&'"glwq8jƙ]1 ]08Ds[wR!u3fP-WUTV4©+{iv1Ay,ٴq|&6Y3\zLDNJU%ݳwE~99զgN"Frܝy煬a~@V-lfL8敍J<™p}*ȆdP6OxԞfibַBс8QizV2nU3QVrZjTqGu VF3ӞLH"xrs*WG ORGw 0ԒT5j-DRE$WI.Knj {((!eC;mYnn)No. @k¤kD:ƺƚ`I ??9#Y"7d}4@gy96EXՅO0`6ĴwH1G?n%CD2,_lhNH5\gMfZDit_3æT:ݭs=g:GΞzWV[x+[pF}p79~x^-:gmƼFuZ1Q>~H'Ph8?O9"G (1"m$+9y- 0HV$tS6O2] vtZԅ:?wA; OLq+58:V\*r0Hy`q'M4n*n'APLq9ɜ*zo0 L*IhJzj r'@3\oDz bI܈iuc9"OS,$#8#uzJsN! e8@_U^v0ja=Scrٙn 1fƍʙ;a'gvE†yƒA;BhiޠDZuR ڳ2Õ,Ha~nq &cٮaٙDɞ`t(0; &$4㑧mhȞtF>H%v8[ t&sư̑n124Af;X 븒6+ *s\I7 Ww/Lyy~bcrvrѢVchi,"E+t̗4L`gN'] |"HN2cRSrpqQOޓ]W-sREb5=N"rW+ބ2,OMO|ϿQLՊtd{kM}Wȍvt\o4iW~^FRJVH CCa]MW^khnojԊR?}NP++GV6ch7V2 0yN?%3`51p$A^O`IѰCiJXw L:@D(QHi^uH2q Tf܀@fNΥDbOZai`.=Fs4TWPeL-p2Bf* u1>B]m1 nAdɃ03+,W^DVWlWGJo_2#iP, @pтT3;يIp*kԌl@ x5jTjr0'%ugV3"7D(N@,m,(:aH"k= 1GlƒߊBܮD\4 ZؐG. |"|pb9{F;@xqFrcY~*x4L]A&_Hbw;<|D(1#gc F'%%cPNNV\ΞgWA4DJ*HARc#y #t(_+άXI59Ǒ*! :pdD:Wr:00 y>9%dn8Nڒ3v3 kiʜς &wƝ\Z["qLg O'Aygf!GR7PM*>8֨~!DPBW,yޝ*b4Sתs0pvQLփRYwzv &X Irg]9'8~ W;wHγA0#"z0n"u``5'K4I3ው`HX֓ r0b ':珅<‹T -|EDV~|0;lfG5 QUaҦJ"|`EɢӢ 4:hpHqsG->] ҫ^UI{,Z*?Sg- aybhlj-m:V2P9zU?w2R۩ѩ:-'MD ?p޴0e !>Ds-uzY"~LM%B9-2``MsH3{Yd@$&Dtb#FE؂5 Hn@xčf>VHq3^#Xf1l89*a[dmU2JB4-AFqJ-ER T T8J:oS&=vtZD2n]7f(5J`nR1px Ҡ$iqHFd;FQ\?3um=Qն(q-:CbYӧ0ȭNT|iбl#$A-$i8&~Xpͬ7 Mی XȺ - ~hAbVD0'BpI8bu: 3g8Αݴ3"BI>N0LG%1L,Ѵ!nXVUE#SI9$@=Xqθ FD8Rƾ8PHr'|РhH== G 2/JY 5#ma@djIgd 4$nNAbf&xQt$ f|@l|kr kI<|4TXh}nl-jn:ώE٘?j U7:IQ6r߯#fB7ߔ7S D.̈рx_LgBFfdH A;5 aRDufSpV\}^b  VWІiUgu]%XN:x?Tʎ6i8EU$Ld}N4Fj#S=mQjimh^zzUzԁ+[-2r&6^WR6C^z=1֬UC9^bN.xsin鞜c30@bN  i1 8\=G˒ h'~Lzw`@F3ώ9UCȶbW`sq3#=S `bI,~_ Ud.pPΦO2* ?6LE|$.*D@ 7@ I8pi[6'fpL1f .1|CwI)` V$b4&8P,R 1a"n<}'{Ч:!D3 ER0=0 0@(n0ZI 7B4C@,ppk#u9q1bIs6sAL4N>c?H|3 (tsccC7aHɹNQ6`I㞈lmq#m ιDA3 Mvu#99*)z1F-2/'̛`)Sjg9qF;aN僽G+RչH&RgM@BiFD @HeTezt-\9pL|*'{dI$1gSq:ڤ.9"|+RYr>dU f1b&a5 ߰mӅRQD-@KHGUrvq "sa#|ᅔNR) ElA$iD3B=7ğH OգT1)n"KY;9c%W嫕 sbGTG1u$!/d@Z,iSēvLwU*d˼5'#LTM 4,F1(&J AYWH:|GI:0,cr 5P'Ms8Y)P (sAsoR r 74|e:uA.3VBgx8zh_HzBXZjnGR!Xک,t6`_Jԝe׃tILn(# ^*GB1MF[0z ĜϹ2($^`gi8Ds`9H1:1qR $IA m3GiO 7hdψQMrFB48{I:H#h f@b|gc3:|59tP/* $MJu R~ijopjqy(i2UjMA%MꥊD\YΠ p z]@#׏15{P}`Ewp@':TOP0`&m".1#5:Pm8g?6bqiAgN031ο{dhg9YշOY2valo<7{̗;DL?90N,sF8?&3?oߏR9gGt2y$AP!Rws `q35"bzs)87c9]s3C F 9̱_`q¹`6$"&|Lo#dXɒ2ݪ> cקS1K7>P 1 cio9 _@3}t:q${N&O41Xps'b'S|g'|_S[R4Z%DO%:.@5,TT.:uml)/(2LHEcoo$k3yAjJӺtf w|>s5VPN-LƱLDN&FDFr1 F  ^ A+q$*3`/!&V+/L2gC)eHA18>%ZƋѱ[^VILLT![ZE8Se*TMsL_VֳXb ' t8;5j\gS<0?Q( Ԫ6;×Gj?d 4Ģ u)NԑAn";b};ۡ|]q<|>"8[P"mg? $pL!b̞Gд'@bd.*[;I9'264NF'CaHgf6X0 .2?gRq*;`EdOԏpӝ00tY} Ɵ~*'y5X6oNNz74%`4&qpGYrFN5ۤT-Z+%xR gY`bΆp[٤2t #F 6sՋnӉV7s-f"H '>ewQ}s[FUpa'"pk46c064\IcN9dq^NhӦ`I&F'uCp0o\.&"|@2p ׻бWv"fpv S"gAVI&n¯Y=Gnbc&qtPpp`` 7ƿL>&Y99|?8ʊ0A8HuD`6β[6pI c:b.|rT3O3;4PN'=P$Ds<|c]4SW[ Žv3%pň@$68~Q݁tƦN&-5PQɃ (i&DOLqir҉ 0HܐdFA=#3E>eSqΖ2],d=}HS=cV ۅ ˏb"eVI$1'yzoHTMɺ1svb阸y2L0s,OSx#1pu8f:AHh*&CH\-?U&U$S |Bݬd@Z0`|@04&nbK1[w/Hc+c'd]C "T2:G AEX-r؜*fA~1+8 ^3A:MP dhkkųY8]#'B57 &"s j-Uf>βcrL’Oc?q5ըcKC6wmʃJc51#N||?0X^'V;ƀt:ZlI3 8KբD51@ 1'Yj.X D2v@~ku X0m5&xe&t*0='0DfhFĝOʲӷ: arn3i t sq$qlURm9?Nwj Lx$8ݲub"N!:pʧhTͽQN?ٌ3 V.Y>I*CtwT~ !{{c f:@`"s;-$nA=B`#Hi888k#r1q[p᪰600w?_5P? gTG@39d}p~dlqT֫McϾ7dEE lU5Jm)pݛ03p!x$OHd 0LⱕlI!aaG37q\=9PǴ v`F[I 1+t2ߍA1 :3.9B--@""<`@G?c@듿%`_Yb0m`Eh3IRh{`c8.(9?,c##8`;K.$@/'^=OEH#O= Aǝ(m(1*$yհcWn 榾x`TJےWzp2qcq4,]p}>O`[p81<ɨ\y0 9l^rKfj`{ qRTL0@we,B;cÓzF99WҧU[Nḯmc UZ%4ft|J2jIle !nA7)"Vd|k3*VL*S20EE/u8U>![MN[@H-iݪZUj2laI3BYJeQu!YH(T\C89b̠aZ7k#~¹q*| z͚ x5H0D3 :MA72Huu~k B34bƱ0"*~`&~YbgicC#OV 0q,!NN֡ g :Lr>m«V[Pt#>'vdy2|aDn c; .G3:$EuiP*eqf,>&1E0dO}OPoi`TL0#=G1i?jxЏ΃pv⪐O8_ H0>~m-y鉯H0'8>* .TSHǸ6I̲vkvǤ1[!rzT9$ȀGfwrvd@sFr-N[i\N1LBY m[Zy^AK& ad@}^b$qOmG5ngllfn?f_S[;L} ko O#9@iJlpL$EӁ$nGP2I@E+k{6 0t% |۷?1<1$q/F}|$X˒R-sa8f= A3݂zu4!ucܜ fgJb0qZD:̑?.LF\EMw s'@~3vxބ1DnW|h"Nx"OEg8{fGHTg\@,De#Q0HE0|qS@x2 @Lΐ#\'&X2 Hİ,@%7A0Xw)N3k$FPbh !GlR[G18ux^5gy@փS׳EPʪD]Hq:H98y ^>Zmę$m <x駮D9Vy_ jVOqV jI51RA}99 ˒<* AA0NE"K0FR?U70pI# ie-o#H0d3vhIޙHvĈm J߰3qP`x=&$cBGr`f w/iA540;`O r \|H NC1^h:&=8Iŭk3D,6 $hY8-0fw j$M [Z',AmMY&OR0U?aH0?+i-DD鑤8xfAYqհkQb\k0m` abF\NU+906X\pb"taΚ}Ï. n3O|'ͩգAX}EV؛I/A3eN4~POz\&s0>l]KѶA_Oc@ LQ& 3HpNm:DsN(IؒuV30YHXFp'Gn>5Y pm/7q;MM^e}}>~rQ5"H-ɑZu#n9ʌY[hH@kl L~DLVmNr2t͢3pc#r=^Db w.3HybBo0 @I'#I^`r"YsNY ΀6U1L(8_qLy)^Xus튒3〢]$Gu5ّH#[I$8<3"a1*k3{Hgߙ{q<|9U x:1~+Uks᧠uE2z@3stqT'`g6%:UVQYc^-e^!N"d[K x=S Ԟ9Et! uIz<&;'@}u9O^S7L1.UTcg0s5QY9PA@ppw$Bm'l8jZ0LFqW-hר#N:X FtuOӎd u izI f$( D"O6ZmK`t"5mpLbc@{m" 23gΧe[qI5d}bDqVhKI%D1+P3LM' >nmlǘ3l='+e7`kL74A3R#m@ihn'D iDdL}`'dέ=ٳ{Ltߐ$ νa:82-]A8i=1b o2uI#pdOPp"1=v 5*C OP8'_(+E.q|)VyZTQ㱪FbCgPu+QQH֪Ƴ;zt}9 `nV*)&h+*0 n2398)CCwH@q0UH@&tݦ0u2D`K-QI=3]FtO 2' ,`ABç.T:eܤF`h<*ԫjTcded-{8/ 4W-;pwmb@7; i H%Ob#OHǞѝ@$nArs@a*`DN"cAHlaLjw fLk1: Q bI>cH;SWfP X.m"17]m&|%|+I@KF[ oks  0tDWƛ0&cOf<=6?qH뉃pPƻ-~ ODzznԏzdNY|gh@qMӒvYOQEJH1 I7VPH:s  lďIHgKVӝsDTZU:ǫPm:΃SOYڻA*i@<~b#s F|?7TZ`\c̽z=_ufpdAbr'hų1t$ ˃՝}H$}+hETAf# 'xk8P)dM$ ; 13$\}CO 'L];qFWn#qFnNxvhČ$߈?qIq?lpN&ubs8@B(RF!$40ZȁSH7FӒi4>E&:O hu֑MGBNOdUF& 㝨 aH'dtH5nslK>N.YqvNV7'3廓߆)e`n:Λa؍<#!:ccm\b2fb Iȩ =HnAѩt}x 6-g2D Xb.fG. }1ICHc=i)%m͂Dbp& ?29~aV3.+"7| %ɚUPP@vxwgζG˝g3amH yjnKĬQmŎ|;s`-Xĉ&0u*s|)HՑU,-qf= \9?9OdTs&dpY62s/.C?VI]\jN`{ KLTʜb N!N8?m~Ӈ fYF Y:ARbG\ә!rdVD '@!gɓ;BߢF ؘ:kLN `c:N6A2.<1-#XI= nNj >rt_qq܃&42Gq;$QxmlrI$郜1[Z#93,uXNI܄:8c$:Ak:!VmTN> X@l >=Κ[@b@tI>69e C:|;HgM?Oֲ LNNV XRV89>V>p|αfg 䍾rc}b /w<|AMNi|k(kzk"xU[]jbdV"AA-'\H`*&dna"ΐM6i: RR.#oLZ:'I$2bxºIUY3Hjn O RdfT7*ڋ+Q ęQ)dZuHP[TYX*էktB/ OUJ2ף\  tx|st[ւ2 tfҲb3q9Oـ|Q!-SO&o--4BC7 I9-d]*$~_~;Ē*@v+{ ĀT <$1Ȑe9-@+6j?17g$ q#b~Q;`!bI2D q*`f"L&۳$ q71"&8<) {f&ZpfTpwT m kg#OYdTybbI~e+ȕ5M7IlecnIN?R|G0^;lr$FfGDx1紐:@Hӧƞ=kO_GӏV&ܐO]##0/POL1`t17 .AfLnAZ $A`q4DH 9 pAtʽ|a @s9[vA!Clȓ8ejUoeGS ("{5I7vc+ 1 (`0x4 ㇪Ya!QlYI`=3 Ano[vL&183 9"Hg'PtmjFцts`d{`LF> pFA 8@Q=u @=&$hL 7Tm'AqE|)*!jv&|{|sT1qmdt:gTAcs1Ĩ7X։2'B L24$ypKi.A^$H=98R⧨D,64vw"&nΆALW wX^9ZHsjȆmǃjȑ V(#鑉52nauf*'C,jV> $ >d0 J9nA?LZ.Fs SyJ)F؂O?^]rNh!`=% MQzf.^@`T&g"]@sGFY(Դ`?/)tP :Ox":@jEm'3TJhѠ*ff L23BrZFH0gQ1~sH&.3@N8YӟͲ8F<=2IJ'pfD.Nvc縐snN9(A erщ|̌ZOɤu Ii[A"NZ&@ 4ᇨ<'BN66q$fL2ڮkcYel E@--ԉL3 d[I#DDD?!dSj@/q@Ndx* 7"r8`>L`a3 kLh> $`VH0w Ng'o|iO/;@̋yyJTƓ>$zpNT $eK08Vs8#@8 lyܜ3ҠIcݹ*)ı- FFI$g%l zLŃaFs9aqZ(9IĮJ DƃJB2Lv /iRms<2i''AGp#e92vJHФԢOl[pAm1mIKIACHY'g Ʊ3(W 6eZ#*s)A1tg2A:g6118C׬$Av}N \ M<2toOc~* H *A:󙜓w*?sy{BȭLc.wvMY :fLhw9F=Ͳ"2=8?I3 zՁRQf_|2q]pc0٩;9'qF;k-j9atzbnΑ"D ɜK}tƼ:(cUi heZl)[ui9' NUCOh1鳅`Φ ̞9aw*P>ɒaNO*a%Mj q'C34u=#$@S$0F&|\NN1`t!:bpF3$݁+ Vl! =gVCA9+RF*5Ȑj6TCnTNqr "bqXapI3⚆eel F1H4adQđ2&;V$I# [t)0֨ȝ3' LH"g2ځ$Gq#-l[x8;kq >43+R̽LL {*TR31_- k  5'9'i dQlfD2ro1vIuǘ16֑N9z^99g tX'ǜW 3s<2kimź _TϥV*~̠J, *d"N:b3l-3IhgEm@tIpqV\쓳nP;ΐn Nupp2o "n04&$NdO1f 1=1@#mӓtIDn `ںw)'bx,i e#H>GFkz=0'?8$bᜍ@d@L`\<hR *Al#Ť>\ 1HqJ!"1AQ2Baq#R3br$CS4cs%DU?Ut=) :}2@秪/*9 B Q <0xs;~Usw@ _O4H0~4>e zrbBfv 9$PHնA[ 'nF~D)pH躙7\Ќg:-8#m'c$"鶤}ʡ$!I"cF]f 1w1֖A5sIq78n#ongonA#hk_8sk>alx<zB ί|tH HP6 ~V+Fٵ: AzCp (*IoFx.C?~pvqvmXʀY&:&r5zdN0k8ܒն :4b@FnWb A‚c陘SxPOTߘv"k4kXMV0V|c)fϽQG-?Y|^¯6Lu11z]XjAT~z "?RO9u^snQ:UWZO \g6 Zg'g_BЙt"ɦWms9|7S{O??|NmTx2Sp w9h IB)Q-ziבQľ#]ϧj>Xq-A9)ô0 9;,e阖aiatd11I [l>0NNMֵ1 :vkNčb9i MBagD #m8 eNq#E7e*miGPZr#kNURnY.i$ΫQ&v؟":l/w~bD`Lb2wċd0F&# 30~;ox]FReB1 NLk'$7n1m;]bd xW"NY330@H'I0硓4'?eB o$HFAN@wN!0bg9R~lh'i`LY 7;K#>w؈`3I$bzPܔ3{` l@:oa` -uٙ%"4F&nԻ|Ds'gx @VLـd:i p-񉓋`/ugav>[vf|<m7S|J̉#V dq% VF:m:U"oy2O԰YotQ'g;}BBI%@q;s%M,A@>':HTI;a6s9uҵwh0mߖq`"ۉ8/i0@Y? `:;!yuЧ@C*I\)ag2Dr JaHmj?:X$b' b7-D A۪F[}J해+R 5 zk0p-2a3RLr"=űؙԔk iV儍8&?+SFdN*xbݮ ѳ~w縥G7eI.SѰ$d+ ioI n2\ZItOf8!g[2qvLIeL0NE%'* AUZ&Fq',@q_kw+m0~,Lsh-IOON(~]rzltc s;F v`&LT*TT,ӑo?7Ham#,BmˣQ' mSzFuI%nz20X78$IfJRint #d;kdC)PcMš Cg Cl]D{`~"- @߭Xdlɐ ˙>d9N:-s"#2pAr[?/ӓP@cy~&N:p1itȐD~u6ʠf,L{f.`n1f`fCd|LL=xM/$31ha!af@FLg1&w$'$(3..K\E<y1'j1$5CHE?cwȃl&oakry', 8#`T[:/$D=qU; $9l>:t68W hUC4;<4q Xw:LGXLI$?sdyz #@cGQrY[jgd6`%#hNX9i ~i}|QDrX["BYDęŎ;y`%H; #I?F =,r A&LwpK _ J|FKo>W@X~|Ș'idj+`+`K*dGd0` N:6t`ͷsun$oj&s0NODKX fW Ǵ'8v` 0* )I-[#,@ 5`n| \e~OHB1DHU[Tl1zv\$$#V*p}H0ylz|n$J;ZX5BΠdAгr@Φ K)>Sv M[>J2gI nk 4jOOTTp $AB 郓@Nuӆ K11C|1ذa0m,${A'1Đq0%s!rOc;sn9}Dz! FGZd6HʃD a"`J&38$TcU@DVêmR7͠F0L@F$rCD HeA<™`gbYHn nXa-fpF#`#K80D8+\24C2A%٦ p<]J $40g`1`_cܸDbD1UHHgqR|'[̴ ;~'136`d f?` #$Pq#cdm>rV؎66[9e g$NId1"F }Gq -:[Ye;q $$ZP9RKef3dȆ70< H y'19,2I'9ec:}8--i6"I-+geDldv,3&@HϹz\`J`VRGg9MM@MMB579eKF54`AXtۘJDv2Aajxh='0gxA/sMhwNڤ׬  *!ꚭ$i"ҧM}:TUiJj%:J*ilDC lH< L "Kjó&:SRI BeAiik2ź w .)J;;QkSUZ*p -I ȵi7QT`y18$OUUpj#HOuUFcy&2ym e &< A񩈑b 'VHĸcjv NFASSj7FTJRFޕ: ` P`; xma `}Hz4;Ҥ[,YU,ҚUQ$Z^jLDCFwܲF$I9ꢥTj5J3J:TJ@jUQZʶiX@1#GէJԪ(G N::FC9]kد> ZtѢ5~i6".V욝~ޭjMMǣV#) n=v?1(U*DBWr"5+guA% ,2@buzZpn[$%A=>t A\Dqm:v=| -ZāN`kk^VЭ,Xc8MMP(i* X.˽Y6e8=wc<3M| ʸl rNz(Yb[Ie;D]N dס $nP&țZ4Obr2:\}3g0&nf 䞖Ɦ k@,X@")IP9*Etvf~H~WP7QiFT۪O &2 gPK[?`?,̂$OYd@@1<}:1v]f2'S`gA _=]bcm2\}/.V1-fC{]R⃑ KeMTb_DI:l I6yNك x71vkH`\4kP0d JI̒I ?&]G|ȶ&;A;F0F}Ac6.􁋀Ȟ#`Í+zt%g4}3I UHX  8D3k #x;I2wcnG3ɓ~6&-"a 27J8 j9=TS->#&qiIpF'QI.=?# pO:xR\NBͤ;A Aw+`nRӴ 8>s9)Yl&w%N N`l1]D [^Nf:v ̶g6 @e*"̶zr^fAlqľHa`1.Y2*O`*n蓂$ 0nԉp;% t} ؎[iɃ@w_l-;(z'l1 '04f.`d1*@&@;Mq.#8Y.8]n*b3 ~&{zM36v2@Q=W]I_p%'1 ~"kv߻zbѨ̀&su`)| ݿ]QaNlI 61-5VУ3 #pvDaLL]qO_ d%@Ȉ co [i#>`DUd&L}@'23n;ع2I ۣLxqxOzm) 3dkau3-&-W[h3$d]Nz6Te* @Ƙ">';FbO0$ltZ AͰ6N TUЊ:\aŤ%iBR$aޡ3^`+6coW*˝V3QTpMP}BQn:sFS.TA`\ֶ&Bf l/;6 ;7)a'H{'&og W7):!jԻ42SYTFX`nl\duQr^&.MjjT-@܀Fҝ-*n3OCrrYбS &ǤI)_N;uGxBjG+%C"۹lJlQɦ,NW>]J'^X"5bV1@e UHՑmRSԇY 8ەG`EDWg9rLZ0 ,Q`< /V߹$\@\GQ2nѝL0yDdgI L<>Cd77k[ #y;bn8Wn.bMjldu'n80Ls9yDr<w $m=Vצ&Z;r(A$iuH0>T[8̙nJlO7DI bX0'*LĖm*"-&6ݸ1ҕ$ld G${T:`O 8{qdf@FU>Sn=T` ~ld@sEJ޲RaP:1DAU]-19BwGvOrWd2 SE φh3v?r,GЎOv#` 0rf{"1xj#nҭZٺPACjbI $eȺ6ԫ 6$ pWh3 uL~Z Ǔ$ Hԙ@@2I?X'?K9 >s 'aՃ"2Tme0c @+϶<:.̞rnq<k ~S\}\yA$VI#2w}$GDp-Y|-m>:$$n 0\p>r#<z5;ǡEL`S4R'$szTSK[H yʬX($, h$4 ҹ"~i;.GNu[z|.H C`6ц`(7ʒ`  |Ip˺_bm2 j{iF-̮Ρ7TT!Yw B) T- !ݑA Ui2 !f&EDSs$X,ZD 9X  )Su%?0cL!KL0zt uie?JPä)E @#q;l+#X+1$4pՙUYQ+Az,Өɺ@B )Oot™ &&MZlL{򳼐w<(?~yddOzPas$|d#H^eLW$g4GEɖ@X,>q7 m9oۙ<  $ h5@鈌mƒ @ZDE `B0u+<CM8'Yώ2g{$({DD}<?ߢVf1Lq;m#|lT 퍈#n D 'lS  uH'7u~֬4ےugcM"9ClA=O Ci$)*u|\Kv$G&yQ^6s,7b_Vsc c#KTM[7 rK ?G \ əsUW݌䑤C ȓ<#"%[m 1*TCLﲌm}ߣ݅?~8lF .E;gn ?Ig1406W`NcyV0L}:40<V: x0p5}y, 3H̝Jo8^Т瀲71Du H5Uk^S6MW}"d9*LL*qe~~Q4 ʴZ$ jEW2Lvnt9P j7ԢLeZրOK8۔HaҢ$( uMa.雉 G$ a-QE3))hUD7AW T.D30pqRB Zvt tz7VfcI7#M[b׀3BmbH?=UZ(k\:,ToMekMK9?Ht hA7> B h Ԡq %L* /En':Dܭ0 ޣ_O4ЅܓO%rf`\Foq] JQH.`AZ=l2WY`} fMۂ-cH@#"< ıG 39  āTq6OתT cb&uLݴTA^4hY.đ' #*"fG?3T7fA#یAў}mQ-q`+$diA&Fĭc`93n36Ɵ Q 䖓]$ աb{I6D'9Vc{:N`Gfq2MqX@@=&Eb7 '+і+_`>VAl`d0֬b\+ E&X'{OdDJgj`mUb,r"G1"Oņڦz!7Z8Nmb? zIW.$YU'&>GU o0yDAȈYH@mLiolj#"&vK2's;{ĉڏ۴1@H%D o"L`TuUX% G2/fd گmGTI X_?mo1#3u#@0DxzvcS~fSc>% Ù$tƘ8VĞ{Q[>gNhXljC>Bm8e 1~^vi2b:b|h'~3F232v/|L6lL9M nf񍳎-NvF90dl3"0⏚N v9[aVaA_rĤ:lD'JZXlY̲Rj H/D;\ݠfF*ggh>~e?Hde>?VDur7ix,*ceN̐#l{LI&,n# 'I0PC+2XbC! ,[u2S.=;+0~jͦX$8[nz2 `S#0@M 8Ү˒(`MOUR k8ZtF ^54ipoXaq@ W%HiTF+2@u^'4S>hAfIuǤI7]}7Nn\@"(ٗc2e0N-=! xiX$UO a] Nݹzj}Π=j+JXA9?m1OAg"pL=w"*H\d=pw1 2 ?-&>e1>7t \)>H_ EgUA;I *]OdFz8 <]el=:d.7"ϽHǏzibWum$ ̙qف!I M0NA >edp#mm;> 3ki! 'pdjc:$$eF$ ց*B_j~ܧI^r֫0 yŤt"#rߤ}N|D?*"um|;yn[$"BJ ;g4 ],}N9c` '["9bH/=Ã8_jfuy&-017"яF3c 9雉 iȜs&G8*"H9Df,z$ExH ³t~_eP=ZLId1$!T L:nKgpꊱZv J*ZDi:NmJ(J̨֢.zJTf` X:&ƛAV&[Uqӝ$@ } 썤'p!CKp8¸A#A 2E!0p l:ŠR#2Λ= .p \ C#%YKQsg۸JUVje'7o ѧA><:;glFv`81<.j̐(DeaD) s>~U1 \U#p 0oX}Æ1B;}3|1;#Iq2n v f$-Ff{RF#PH2"'y2:QI#=nr'13-cm|}88Bs#4{=uq8ifNsc$ӓ?^:7$ZIDdLBFZHᝊ&&Ҫ oX捱LH 1cF8 EVq"O'?[~rcX1BI*o.%B$H2c3,!Fk#'qKij` m Zmb.R>791;1@'Dq~ӐfAa@~Hlb@$6o&"bv`FL?0I@GC% b3Kl8\A0n3stGbY%n"y+ĐV`(i$f d`*`rg@©Ski FFzev1@w"XlV9?Hق*P,m(*4H[`(vf9( Q`@  qȑePpz6U j2YPAt9C,ժćEbSh }[ N*Bxi9@.- tuLanQ@P ,4Vz8os i.i]"ԕ41+ԫ~@MДJ[R_U3T/ۗZމ) y7\[M7\_hcPIZԝs/BEĐHj,JTܤ-Kh3k]Zh xdQ3M6r& Ŧʵjekvzu d*=&^O)[T,uB=Sd7^CZ&iQfUShI`踲e@AFO(i{&\( ӓ;̉YkŢj@8WlOZnO55`Ϋ:D.I[HJ s'`Uh:f#Hi`AW1f&y;nܻd@%pW =@|˺i/i-c @&%Igj;j,Mg18 *-EVb.G>S'~X|FM,]3)(g2>G#H$꩸d`GglNrfӹS\dɁH&~fUQـ zw@ingeFݤ2|O"?]?NL݈]+ 'A2Nzq$77LL0" X3e$Ǔ#=; Buē[7jsܹ'h%K#@b #ZttP LD($6ߦ6R-'dvx fl>,f6$ b42EVeO'aԐIT]^@P (Vi!`Ȃ =dV_{7a5 2s=AR\46~\\n(j>GP zB! b4jw1UB $QF5TJiPԘsE֖p`(U@5)52kTVUXGOHEV(k- N]*gNJ][ISV Q3S%,S.i,e9KK$)WZ]9 I[HjPp ASͤLFK`4ׯ&N.q Ɖ4,+@d*9i?yIp969rFT 1i!uG6,ߖ幩JF05);LLl~ IF ,8*wfѺd7S)F)ՀMzV6U]I(.1M2g;]Vs _!\\\j2n[Ҙ?7W?>I3Rcqp?9 .qD1LiG<KN#t`D)k*Ul*Lm7 Gi=7sOB>Y(ll4|T*]T%s*Ao^xQMuJ)tj*43j[2aw%{>ڍ˷ZH\H7\yOUtNC?~"q>bv9yi8\@~s܈;{WȈd`x;qUon 3< ;-q$7_?L`oR2 44vA0fgVb0IӸ縺0ۧbMrH7Y† "c1h\g9$o(:zsms<hl3?LIIͱ2IhD_O*2c*0s2\&%g܆+ wMI>3uJg{LF\ pƑJ1x3^@ed#`c POю@?h|r$56DqI-Iik8ւ7:L1ۥUkcYyU/MYGI'ͮQTM9TlIYP/-P𐵤Zk՜ oJ[^aaif*6HMJƵ棅*:`|1O] VF,,Yf1Z{W"wYVZaYN"XmXZA>-dԆ *HSSp9 zo֧RڥҲ#İxayT'ԤjF>Tv 0L P9Bd|iLT)L%)Tq+-0 ӵTU +3.E\#\C.(hbT+`UjpYZOtHZQAmC=‚]UtSpJi19BoMZA!T$lZ'$-6߂IJ0;SR%ΒX\ C617`?!8"N0F ABq#uLӂ {RM;6! KS!A2w,D1F[svNIT6A$uII8'20ZI 3|=‘Ɇ6Nj9?١.FYe\6L1#4̀RרݯU38̙!n 'hT40h LQܘ6N `fcE=@ r#}~Lp{Ӿ&*C&&Y6ڮ$|H#}#Ө9$lzm#;d } s2wpOc8GEbLcTLP vC[*-x_oC(\RU-ZWYH ?RczX ?t)T`vO3" Z`3TRU4M!A1-%F`3sejQ. ^ zn7a^0.o/T(=>$!*0$6HT5Kiۯ,s.V;j$E2^Y UĚ޵\+jH7~'VGu.)]Zm *,L-ȮMԘ#{m @IT, [n=;QOna0sk+=8v)4&nq# :7U,=DHS{\$"#zTiIan!Kd4ED zT@"yw̷̥U:aէ iTUYB2喚"@KũQ*$?637eų rB60V8Ē׬pJ{XEan%蹍&oC&%NAIذ ָQ$ 2dNGJw"[蠋~07EͷDԐ" 8 LENlfAp|J?N=a?Ɏ gm܌c a%MHSr/_Eۆ޶.Xg$Ej# z&.mjvmdڔr@VVt_ kd /ǝ0]w1HBZ)|Sm42=R(_aV-@A0cPURI7CH`cFz#Ȟy1~N#'qNf:6vG_muo!N3$0Fqte[s0D{ÑiJljaNuq"&'<)P5 cp8u0a|{sZ`di0pGUXQ@3ͧi92"VXi%L2$7mqi9؀dP*6&5l) 08S8I+ pCZvEͲhݹ0ζ\_˨FHoo9`v9&7}9HX("#Tpƨ8O_bʼn1[}Eee94!q&I-tmhq1կp0`nḋ&0E 3qŠ}工a~ā sO2$1o ӧ&dc| ,LG6#$3<qI7A69YO' H:G];_o$9u5QZZ&R:!DWR7_ `iY:ad0I}EF2HŪ@\a㥦Ѥ{L)3hg|arf;ZИ %jIYӈ#yoL[ )׶e~SlY!AMI}"uGz B^C-58YHMH)knxj03Nlz5o4宼rb_ܫLs=aȦzalTqL JA&TYf1j u'BSRZk5hEWKeu|;ӤK񓹝VW$]mBOq @UY˜KQЮg!|HJ"&Z+jX4[("T;7UA|̒q8` A"$4_MgN&Čm 1B hP R4ٮjȼT2O$~Xclgn:&6>hWQONѾ"Si 2~,&1`;m&c&]TMLQ_dtZim3Ɯ²= uAV,}42\2k1Q"ZJ>ă"NRvknHgw^ygm鞩Ȥ@/tS-5 Y=+[OG$Q$`0L HJJ\\0ssh8OY!`k)W[<\#JH&YEnL5jmU&E*(B`^ , BwΔVJUa 22e !+oCQp`u4פ8=BUpT(ԫSR+ D -XC!TZ 6=6Ui)գRV%.ēu:5:#UDk2+k/J&IhmEF8$jj&)WעS:uzıV} :YWM.ӹOyzShWUb٪"f믴 (ޓ UpQv&ml BՀ-U+nus2`{B#Cj,s|zj̬~ek 5C.YpQ{@HM vQ ˽F٦U$\fH\#ՠ~i}7XŬ2 Db- rBq"Z{84WRɊ{Ù uin>(41ve`X3(.$hXl%{t@Z58_*7Qe0OK6LBX88Gܰ#wHGҖw7ߢHNst A#LD$y9_+OR@0~q?WuAs^]%Y#m[:e(RHEQ'I ~A89oc1# ,C O92?Xv٘pueI8w~滱&ш31ΥM$թn[1.& AQ夐bK[[80H ;fA 4y`F~`(&3ymXF2ɀ oB `T/D36Z̝hg~X2Z6k)I @ { G Jm*{N[d/ 0'%ˆs1g߷/BD24$Ԡe`NUJ(U`KRR(jСX&$bZ vdjط3+iK*AZV` hmT%#*R]u\uᏵO=@gquM]["ujFLyRF'w`TJc-.쫓bwTJV*H Lt*E8MHf"'$ʵmۨNFlBaOIEijAϪ%lA͗l U@jxl#y3{rXG0T[Ӂgӹr~`D9$?6m}+{rB]fcnTS729 )n@h2 TjEz{ ~nf;SKnS8Ծ;OidCg~`@^M: c<;AXY꡸im#QSYϴ]#}詛(܌Aۤge@1?q%@cql] AVs6`H.HR Ӱ0n'ZA NxdD:IۏIӾvA$l7=f1qfjŽ̶L 2H&g 60v|#2Oq8@Iqh$F?6zEW݆Aӛ@0m g>g9{X[q ̃͞(.t0h70$ԶFXjͫ__ǧ ' 'e휈+#c7)ƜOnv w@wFO$#<'$@<}A1';߬czb6&'3dw"-߻6 .hc]PI]Ĝwɵ}gq;$ _14HUQވ,rƑ&Ej^pTo[Qzfr)x4UK¯9TX!Afsc$=T;\5Bi"D&uU4ؠ)Z1.Ui 7ߊHǼ[n$7'j >n_^$ι:QH$NxT %pbԟG4n~`Z pIMʷ*3kJXIz^I%ѝS,! V0MBn%ҐAHDfөX\A"LJMzEZO:H9r[df .ӻ֍g-bV1Vtvoښb,#hi%Lb"PKz0)"õBn5-,(Ha$(H(ְV*};vnHhee$C鈪 R݄ uq`ө' YZaimTЪSw3 [Ia,JT0Hxë F XoQyM##Ӥ\P*Ieu4A +Vztp4&+DAq%LZuZjWP=n*T(Ͳ]g7U >WI6$ʐ`…ӤDi $m b zS?~":9p` oc"c9;nIF@ ~3"UK~L%̺L){lcTZ #MY :A`#bw:Z@3J5bJNSM^lBa=TAI\ Bm!*a1;j'q?O{>E߬}F$㦺bI7>H=U2"B0d2A^Ҿ#y $F9furZl[zr Z:)[ە/a4˹eG}*f9AtTF:Zd.`r'o%$p#z$BFn3#qǯU n!7Bd w|G_ b)H`W>$u!|\s8#TL@1x܉ߜH-782ϕ8lqglh<[]|H/-WS 2D^7^m)IY5F5_n!פ__Mؤ4;W-A!:J)t]ojѾuܳ*51@=ezkY=/PXW- !Q!LVfb*!U}Nc4ޙ* e.NSզkd$ lPc *;z~DkiDqq,ĻnH$ S*@@^rnE.k.XJvpRY7O ;=5tM 3pfeE6"6  j'MBjҾЦo-V j)-kMV{-ǷٗP 0͖[jk- 6RHfKjo>'k?"ƏeBX@d/t}jڠ): Z6*-wpiZ*R"XJ4-DHb(_=?yS BR* 3kj Trrw)uPڠQ}1P1!Aup_#Y&Pm[ )A6U,=B)nݫ`ԟTEޝDn):ziAP,[p`I(Twt{K*ޫGRwQH3`K=j4.o[@j[*aE.%FeNaWUHlЄIMBrjH- [#^ o BqIͰ'GV.~amNz6`#avw8q3>s9ia=T#봕iVwv i\nJrMD@p@jSZiʹ$嚗gЗwjIX^LЪ@`Jڠ.9m=(vԥUE,YN)'sS8JoBREMF`"i]C36 gq;go}ME$g81ːb6"'xs`C+Onɀg-B8bL]L11mApN2 sKf!t z LHؘ&m8ŧxr22``7?8FE3^r}i#3G', Am$j\cA `AHiXzJNXG30Nu3Ē j #3[JH &0,@$# )[~y%I{uٺP6eeFn9 1- y 9`;]|.`j:T0c$z@=6c'zI'1V.2dc{usq61cP:ycy:Dm>]ЛQ, njHw!hFw% ڲVneR$ L]٦+QNGr]M*;*R ]RB)*&FkG~ȟ`Q{KĊA&fJZNٖ0 )T(Ĭ/O#.ནQ iS{:aUmĀQC3nBrfM2-FtBvrT $nIU.״TXxP5 u@-k[=vit$QPLQibwQ@T%~m1 䁧ZʵʥʃZ3i.ִB33~m2LKLkơm\Y-h~Zʌq%@S$ɵWEN(.@ IӥiB f 2 _[V~ttDN6oI ;ahiQQGv~AC+ nz~K|+ߺS CM 8{jT-|X1*6qSZ(EAQQ@\o_vp>¿v-vvxzn֕7Uvr߹<nmS,@@ QՒm71B1*E2AHvD1HN> #iΣiI8BF#I'Fxz,bBpscH1 (:Nq#qqr#gV T}jpas4;EHŊ 쌛rd+1v4):eEL,.b'u~ hF-18=Wrb$  3½ m5 (M/V0کvJ}l9o'3Vbrby&$5joppTeLF[8 J$*0V-vw%dcҜB* *t'rTW`H1AAL;by1P7a}Fy< lg3ԁiBhGM<{f=r-$i*6bZqɈ[ga`=W ݶXj+b~6weΕWU)HQw,Ѹ$ ޚ#Ϲ`zͰ€uI/8mR,rr~ &N= q#4!*MBxG31Pnfӏc;D 1> &Lo#a0!Fo2O3h2y}@G?aek?ǥY áA 9ӤLI~!E5pjκzu붻tmNҒSIp{]G,j;U\^R%Pv]RH~;Jly,MJ8OvYQZEݳVڼ݅oƤᐮ4"=WEBכ XB24!3v7e[Ln(*I%x$(FrҚرm߈ p%zyg&t2*9"1v-131qaY & oq6?mOZI̅U j n@k~UZ00 b-"éU`w3j;+(g5 HP p6BJ'̙5Jt*. f?* e~ e/E= K'ɿ]D#qţB5*57:dHF E+U?PX ijLJ1Mիw,)NKH 'q_~5Rav*U\A"A`kaZ2ҩ R쐽dRKպ5Te%ٰ-LKha0~m1bs nA ">3N-b<7JKAlh `q 6:5-1fWq Lv$@f=I KKo ㉐0(eIjv!QFd(>q GuTwΰtȦ6nb-P]z]+D|YirI2OU)j%bw$c}BCuW隤IIr/OOUɵ6Z BȨN K1P?gfzSpCQZRf+?L%?w pQۃ71$t|gl8Żat lO3]?M ԐV-s@$~wgH'K2I c6m6t`g3Ak"LE6 u74$8p #!D h%2e: 8,.+F 1oX 1a@^#7]"g10v(m"mv-)&ݛlJ1%}qiū3"DIl&wǜD]897mAƜ8?]!]PfX2;}$$$,+-4b̓GV@dn2I*L8Yf`IGH.uKU8NDσ->獇';Ϟi '#XN; ]8ѹ'=Č<{M'$G>vȃ 98bvvj{8ۺ^߿z壷riY(""!n.oVTveBH{ME^ڽJtvQamp{w 0p5~/ 0hЌZĀU:K(g EmbfP2 O`aCL33  OR=cQ0b]h-8T5XD cL7N8I!Ci+_)Qƛ7A*f- %rm75YS\YD&blm7=cWR:U 8 #nӧT@B|q۽>FX-4]Ar R-]g`:Em>n A15{j.٩{ͪЬNdĒ枴ZRJ5RGgBqek}n:5(/QE75:r\OQ=wZKFh)z$0MAN-nK^ၿ &ZǩaB̬";~,EU HYiY60,y7H7 Ǔp$)` b"g`Fw&t`(Gʔ d"]ف<ȁ0$d$Ѫ/vdn@doӳ;?sE,̐D@GHbP^[tԽWRzqQr-ažSnI NpD6/ݓ40l&VwU85ٝny7nf|cUE/@v`A&gTPɦV#4&ڏcfrAdHy\&@yd?Xj.H39|Bv}$s8RF"1]8٠}+\Z!kTأyBf ݦ8; #t^"BӷɂLiwLMW9;'䪟r8Iy7`TlL5{q=4;&`;㫙T$`a= (eǍ#j0Ҫ:i-Y`ǴFk\m")Tdy9cX0:$;er QPc'mNX}9;9>vQL`H#%9'}zSilQ,CpA+|S퉯lUsMnDŽi#4B qKU Z!vVVh=ZGay zjkF꾷=ۚ!J٘zDX+){)K D+S(mp*C+CR4\ J>Yd>oρwtoMT+F݂nKBcVVjL DJ{׷ipR 2'1")4Ao~U&&B̌@~}l@z#Qd*럞mD-\AfYon_P/h0 "LI`&K .H 1q/A4.L;N@8VyRd(+`O bx>%#q-mϵ)PKNlS.b|` 2QGbʼn!rCI6!bHhu\vT{U^Y&MbAe0sH=kZT}eR zC5ZE)uw&TH0G$3!  T.Hh)Vn[si*ցi yYdd H+H ydrKBìL6zu9b2g7&Y<6N~I# “89!Ãq#hmN:^J YkSYe?X2 rj~բHp0 -Q$jBp'pwsNҫ[ 9|?\ТHDIh4Kb1LtfDrBc' +O" JnXAi* K Q~=ʲ@m6ot@$RLK 긥[V\RUYEJXMrTk4=Jg-GDjtUXKx &9A #fIc3*}LŪN$_$LDr$@P1v0Pc1p[eЇ$.H qKmmiΣ_.Knfwf6V݆yg C~krnWnܓȷK :W~+WPlHDu4j__MBdէ$]AAתƊŪ QQlɅ#dzjk+2qGcvݕۭ7;`eĂgWb0Z snHn38prc昸Ln Ȏ+3 gǛp#$uIuѸ$mI$7 rvHy'oݻ~0 x1$}n坄iك#lA 3S5Nog@fڤFA A﹐Y}p>  $>TFH lAP f낵F$,G̠-lBgrr\$M=d#i&M0l,# @H["L@ |$و]00@!A$d0#T)q c?qA9 -8$t ~ NqBDH v@U\5n,Z#1J!$mf oxNZZ!݃.nZ89I#^#g38շNpfAQ3[t&Ig ;Ns*N#I؁?^o'3wgQN$:}]1r @'&8WRB;2wUnޏp!jRrPgAnکI$&du*.&*CF !}E1V&r1s!v,-*Әb ͞.J~,QM8faE7)ʐ (H̴l,\,2H HY'8؃OA¸Oq׎D~,&Č"H060spo0A~pH-J0 ǜnVIu2֛I$. >R )eF`@ߐ~m"Ϻ>#TR CtIvɺI2arK_yj:bf]Fdڒ/|3Bޥ*Eӹifv hlTYK곿X7$¨KnM(H:= lb -$ N66e:zNXfX憜*H]lX#y/%f7Nmل*\' v|F}pms4>^@;Y{"T;n8> `]G0A">URoL*. }c#;#'?#4oZDZa%6*n$T8R@ShRRS[chqA폧n'zqn3&3s06imco}۞xx`j_L\q͜ q8ZFd-@m銤Vb[Θ8 0GlpɀAYr @W6?/p Z`I:`& OFOD`\ OR >q$$;'eb9^!#XCbW2bў11`#nt -@8 =Qf[T0Aݏ& { ߖ?Qp,#>qLw sO:6ϴpNa7^n WǸd٭㑝q2sy@ 1#e@(=m*3=eXUdqՓwC;}0DmwF"9sY?f;oq;H$@3D#c8p3~{7Zf,5% EF|Zܫ=[SªIYJӤ-ZK`X,ŁmQFz >(-؏YJi ܮ3db BJa p[@A›Dꓢ)5#)12Dڸ.F!pk(Zv%U95QT:2լ- %?EfAQBOIh,\S@Ix:L?j`o* `F ږ Ub>OXbxG3Glt"~,p; \Lpi>ٹhQ&5 |5Jm agn+Ki֨Ҷ9f툭B9S{|  `nKN1#>mO81o0@i_`b}?XACã@#ztˑ& H"L툞&>|jݺFQ-s!F굴D󩩖'{`1FY;M"6 D u#Pc0)$h9t];  "qLb VMH [b07bWa٘ X`Zċ@~&L# 3-$č*< If"|(2Oa,4I,S+؍W [2F$F~``Gd\I9"":Y*" ~\Dv]oLV$\*Od7 D^5\S *Ē%Фe S]cLB#qx8=Zfep;Cy#sEN\c o_vg^vg?a?@z#Plb sli$sqӏ cn2 ]? B?y3lȐlߘΐa'znt&,`"$3㪵"0Ǭ*=E*K=h;u?5*"M =G=摕ʰgWY:T+uo>`IXSzoVX-Ԉ*`zEIO¾ Eԭ^N[Yg0vL+!ŪDM\ 5&$2BMM 8b=3DGc[Ӏa mbDi\2ivW= C|"DzPOu>&qt夵G[4b"GUEITdz4;ԫ,Jn#1r0~oƻuvwbQ-RRT'U-M]sZ vNBm@DX4I-kIh!&MC:-3]U* ؟hW:īh(jp$SLﲅU3, &A\ s6ۙI.`gHAHb99|Ew,/YY\bry _QթWK--!s:4|($Z dNe@;d`0#ad Q#V$Q$̜Z8m7Xf 2T`Ӓsߑ&GHULb9:΋G]P,0Xmפ 3 `W=*F$ڰ$~kF6K` p`l ;G%c <I7{A-nQ9LA1gKdvuB&[3 MV:BI j`/$+cliBI§3v h'P&<~3ajzN gmuDOlmM1ƐAr`l'~dL`Lv.1= +`Ft2r3YW91 VrH[&;QNb5TBZ%VLy8u.?*ZM^뾡H@C0j zYI`ƻ~v~*;z,;rhfe p cХ|4?ƭLԪ Olubic|>Ļ܇wn- G2~TTAaBV l+`l(Nz%ϦN)M5:,V3׫X120Ā Zb@1 瞄"11  u8]!*G綜̃҈Z!`4M S6_WԺ4Oӧ:-_ngliҧkwUH^˫%?U+bU T`Lz7{! $ `@334D3`m# ,4FZ␲O.$($K5$LvOqUTJShCT=CM*-0 *n@#嘑 ;frzzn8bBn֭:@FA]!u 2Wrb%@R`.ӂ ( p%[fWMxX$\fc #AnѰo I;N܃#3oҊ%,u;cVlLe"v a<UD포yxn=L~NG`! ۀH#5)ZlMQ)p7fDX&[6#ـd< q'I LIb:^ѹA~3"u@g;o ӱI(RHۂ&F1fm,! )LHA[}P-CIk$FP*[ ƑDž"1&:=Vd7~RA!* oK KQq&AE-MSz 1D'A"MIdO"u&DѦ\. nUwGu{#9vE[b 0$NX0Y 2ĈH`1!CW~_띱ŌNb9剃Rm" ̅IhX?+jKFѹD əE‘ZfT S`+q2p4 ;/wDvzK3&A gDp|*1al\CA{-8& N:L'+,={z=-^KRMh# ,i\ >~-k^ډUe4EJ Toh&~n~?JFgRo,ud\2|+ۭ?H"ZPt]hN?;?2EN w%^#[j$wPQT[n"3\3Ewȉ0_w,<&Aac>c3?X'=Am2Vę]b hWVE75Zd$Ym7RR(i th1gS%C{ap#(It=( gFIsQbTݓmErHXg `|Ib5% N$4E*YSD;32ͅ>/00L @Ƞ³mQJ(TMmY6ܴ1A`C-kp^&db3f)ΓܫkSLnfT-k}JLG 9t3BWD2ft2by,"V+SlM4I1uc:dI[m$ |c8;iSBd9P47֝?حaX$F;~,BNFM@T̜;aTB|1 >| |pO`A݌Ȳa ,z P9&7c w t OL6 DmgFK6iLψ t͂O">Ӷ'QL0I$q1&mj) 02DhEц 3ħ䄴|&>PG7 =ŭ ʵͺn…`Z @7r59z2!tx IL @& MMWH&6,M21y J|K@ޖSm$1gHcMZ^~RN\pcs>7 6dK AKۜɟ?Xd([o 6ʤb`i 3uƓ?B7giǦڕ3+lɖƑBݜylLUfʅn )X:g`eĶ$>sB+MĜ{pld $A4'H$"]JGlND+~|g?gla?Y9\84 9?s9#D`i\a`M7pXYv UԚbhĪ$[@ u:ښ2QҧG) Lb)([L,.Z\ f w:?o]fUi"M-tZ7*db@F~ 4蛩J[VQ` s70-r];nϳ[Ql[:jl LEj}B͡BNηzdSlmK} PƩ> "ՓAcDRmaTg|gXϹY-M"Ynn)&t B^A@pLl.`"'|A-&Y읰giL*V _T_AFDT5N҄aA3ʫ H {ޞ>#\YF-,Q"A .@z": k֩U{ 4@ksSiA'DdG}LZ@Ƀ*M;#bO֬uztmgɦaZhJU_uU_o%䊎,丸bûTNjx~&x )N)Je֚`+`c{TEi]+jd ӱR1$|SjRBL,\YADO^ VWzܹ&srqzP@( dh ȻيSE @H"D+1_{Y۰G[@B2FOG;e5=QlK`ǸVt o[ ` 5T”*1%_"n0Ɛ2Bkc}^NK0?|@7fG*DFq&d#voʤ@$&.nFlcnvY&~pXqz33՝H VFw~?Bޭ@@e0$@XԨ ,@ji-7af ŧo Y,wL)Đ ̒"gL7|j -<Ŧ5c10|4^Xe1*ln,Q uެ*|`]۟I]h_8np O7Ui^qq: #D+ҩ D5AݢN XAmTD D+AHJ9_;Hv6#Q@?'WI@I篎kHbvp1*i|IR ۬e6q ghQX=2̦T&䜷EiQA d9>zF>grpq ̙13$G^I3?WDGz"ٷD>8볣SzoJ}2l5>[LA]sCsxMAl%JoLڪ\20%'CԥOA*-~蠑U]lV*ȁu[]sW:F)J{EsT nݟl;R H.i}*@0wqVǦQY.2aS[Lu'%b(jVoy7\c'_n чgU";e"0D@0x|11F&l ;7zl22U{3x}؟GnwKBoZ}ك/!/8-yanHR9UHL 8z2,ͅ7%&E*-JuY{ji= $$.On@:ԭڿQ1- aNw>~:wsgբƥ20p8zT_ԩܥ4iԸ {{c%3$iȇAt#~|a߶]j:ZH# . ěURˍ;aZ(6t t Tʜ+X01އ&o8lNDrZD[bCgXª h\NVoJܬ`brf:f6319 TpD o$̜dn7:;vo=|tj6<@ SJ"I], l Q$h@u$ B$3t5$Qʃى[VRm ;x ;ir m챴8r $?6 w AX1.g ~hw=vv,wc.a cqL+So=.߲ ) JEʵ.f83mw$*'xz4fh;9j܎:U*M:YNQХHv{B\9Z`?av>ӵTTJKefcQɃ}irSSTVaiQsWuT0cHVi\ib\KJILd q8J#GwA"Nqƙ۠S'z-6Dn2!jizihzu;zv~jwJ7TK sb!j g"Dnjj"?ڈs=ڼNG;?ppV8\x}b[h\54;I7%iaO^*"o22D5j/j;;4f_QͫQs!Uޚ-1Q U`3@3m4-<,MD .NX6Ub,@]vRBOKk9 T3فéG Hi qKzjh?_C{鰓*XD *S7hm^ gWu/rڣ_QDnQdouһz V?B':j!hJ $kO1tj'||ƥ9*AEXD\ f=v2M61@4#l q9HЦjI'q ơ9c |Rܝr@ NP@!ܸa$n1H D &|b8H䖉ovPTCU[h[$۸kvN:u e Qn=8Bݶ+b %zuM}L8;*1:~v;>B?Kdi7 8x qA9n#> :4UHlj| \ɹ{\'4[IΓ@>5VAa ,B1ÒZeP`F[8&&P"U,Z7s"&~vvVɝ3Vg(Ca99<Tg0aos "G?I"0&Ez2Ua@k&DR(6f ֻS pD1] ?2$f?m鰙kc2>$߁iOJ;SU)U!WԦYQ*QP {ᓳR-OZܵdPڮzvt1Ai4e J{KfhP,Udcm dJQh] v2BGˮf wR֭Lc/;X' C)ZFS[-bL6CIA $)ïvF1q; V%0MsiŨIwfeTH@T%D+ED E`REj@5bCnzd!YUr [wB?Q~ǀ9cڕ RH&ss??@vT.ߴ#ԄM:$A}Z.HH cm>Z{uB5z҇r;T PѠ( +M\Եz?@z%VT_j,*HZ51$6D #&2p8` |@>Z Lj(K- U$i'*x)OdFb0<~XRqZ@%ə`0 I VV0>GW~Ȃ>[ӖJDc#gZ nE1y&s[ D>yc0O$g IdNpLAY}8}FpӉcZjSڹ.; T®>;bc#IrEh԰1OTewpߕ%L(;u( UH6kp IQ1?1$m^EA'o'I;>oؕ1I/sT{閖YY._];e9.-QZŎ)tҭ@LE"@m̓Gm5Afd $$T`a"ִFF52|B z6ŬMN$e' [Mp+~Cΐz}#CMgbh`x.Wvk2GqI7+NI)A'i'i lΧ GPm\!F?Ct)9> F# 3}$GaOd>0; k]-Wi2PaX -`@b"n 2 S>Š$l "LD(  aܚeL$YuI@,`lBk8AF[ffB W2 {T:L)i @c|ZDH1N R5 ߧW)v;xVL]6$0, )2g9dTf Ȃыťm:[ۘO ۍBo8G,A5u, eS"fw3K)U/>n3oNz!&*Qsj$^c%B\zU"^b7fMMn L\tUs*mtdp$H#Q)>[iMP9;$zES$N ;lnuܫQ|FֺM[ qۥ}ŕ@-զ]k,5D;pu ܉] %P."#&~:?_L=Qvb,=:0D5wD" K[e9(.\O~|!KV)# +Tځ4lT~8Rr&n2Jc< G'V@ ȉ_6A*?"0A:DH%emi4K ,:Ch9L&덱)t| =m74!ZJm6VBJx1m"&3Ɠrmc2$`sbz ;2-2N0qye3w$lg;`DAso?҃ˍ]Fp21U  $q$r| Y'si;\;gPr8;GÁoYUg ?H@棺tj}:]sF؃]ڡ gR:(@\H$IT12m=|k7L0`H@17]=wZ6uA.X6Sw堁v4R6Ÿ1p 4 Kȋ}N3fAGLO`W7ZȈS3tts&##\ c5CI)3Hns&`. dqL)cOB=RO ĔEZ훳@@mC=2pmϩ1.nbNOĘibD3Ae0"EhJmJRI388F`A4aq&ܝ|cl o&c}̘Kgy |G@IA1fd|@'n&;PKCL MmĂf[I1wLk` LDK)aEMFv !0vqʏq-[MIa1tY0msMEv,%Ap SNڵAO Lxw(ic:Wޅ.Oof zڭw:"}2H0 <:r#ڙKm@F2V;fD RE5h0fei1A /n&1 e4h$H ZI>u9'Ӧ lF )>Ah8 3indL=#qp0 f 3i$(0y n>P\G~݊~\RtFɨjYI TRNzDUcGAZZlNc=wZ~G)]V5A+SH-K ~ҩسSi9MQA2]?3 7S&vPAE DQR)TVI{hb_C;J+v=s^hShZJNC%FE钟mؚTP%A:Q!@'-;g'L= N1[y1~ 3O`eb$L @1Q~q׈Q !|)-F:gbW-1v{L1&X 0vGO@01 GF44C0=V,#pQ F=q -jB$T@ &{AfG} fӁV9ջ}m*sS2,7^܂c3<?/23 &$t70u^Ĩ s>5u޴-T9;cg$̃,F窪VNRۜڥ&D|-e QV`m$ɓ7bGlP[jwF?26COt t ]8[pZg&g̝ψ0Ӄ"`gr`oOL , () AHQ#1UJ@< Ⱥ6xbch`s. '`T N`2N*&nSѤI]`b$f[>M&sw?n'1>ޠ3WMA+k&Rkƨ @ 3!d@ᖕ0b32McBā*wI >P3"cP Ԃicsb|t*I3 18"~:Ld}up垿jF i 3T,!2OZ<0:` ͰFɀIX.ԠI6F- rCMph&Nݢ(3e nr04ڥn e~^n 8N}>۵3 Ozl71A >+TP*Yc pHR ZeңJfmɹՏX V(N ,kE*[%@/`): ĸ/m61VP\ۉU7?)zb 6H*`yȪRu M\LLc>:XТ|tɜy*MQ$Caۓ=|S6rj=jƝN5*)JujJUXnוuwزEJި9[۸ՠ4M4VN~Š%Ztr*"%BҦ#πI5ߧjySOշ Gkw{J=毨e#vj2Kk!g;z=A*~&V\21;R'CswdT$>wfIA`@< ʈo'X ¨6$3lLeG'@3Bn{P!zy38CO c1Vպ &#9qcRdBDNR=\,;hx|L`ISI`e`1d6~=~O4aQ?]>KdU x%OnV@ z{yRx '~n8:Wlt,Z"ť 6*"N>~5\,T8;3 m.98D築R òN#R3d~UfI)D˱MTm/FL NISQۨ2ۤ0!mΨ 1.:U s4$7`Zv%D$?_hlI'" -hHe`T|Lu n2-PavXm#'iT44T9^V= ssddRm0*+D@" !1ls46V]%`7%URš7@]O2 XB-5J8do99ۢ[<|DO9pH&7ۥ^ ŭ9p<9Fqpx0svʔǸb9$N0\ i.&%r&$FVqH @D]H)h*b`ȃe؂{Jbslmtǂy NDn㈉7$uٚߵƣ"> &ahR[iXibEˤ22D8mzJIFUj$X3I$f5]_tҨ38 ٓn:,eJ|M͢*=G/Ͷ:WZ>\P@ d0n*v Td™+=;?0g29hWo~L8>='dԤctQR: ̦#N|VkY wu.!VR@8\ XHA*26`(% SJZtҍH |CV:iav{Z{Oj] _KŁiR%qڄfJtiTB@i 0LqɛIDF,j!qG0+""g鑷]^0 9>-X`)s2HfAͰ$)` fP!Cf~)ہszƙ$x1yPH9H>G,8m9 u*&g8oH.JA8 X9nI @"tI$)9$bUC 0ѪDA{@$B!I[ gMNRScNS@#L.۟tg vȀI*>L[8l8slLw4(ɩ,br}0$`R)SI-o,O׹`G@R8:D#84R0Ͽe;b擙LJp,-ZwIvX`pۖ%U~%Lc 3tσ :\\ d7ʭ 4BZSqrP.]vld bZBXğh $nXjXdz`'`?{`s]NU MPhIeZj"BT` fT}6&܃7%ޥ*a'/jSuIQ ջvTW?"ܒs)d;#O=хX$br2GyZzjeS+\&@( OIJj߉ V&![ZZV郛;JC{ԨvʹRVj+A4nϳ R!waDhД)ZH Υq6$&AӸ8걵f51j8d[iW܃o UsN6"s+&ޫ-XLAͰz \3E[i@ItP )-?67Ah@H$H0M&1M֑< V9Dnp.88Ȁs){VZ#H]q?v'ţM b~i3&`F&? J\}V*f%ă!i/"1_."$gNfԱuD,DgَSJ ӦgK#I0#}0mNcKsѰ0m31Kc8m-1AKb`itDpxV>X4[C?5RHZ]p_3N ݥH1 "- /eU[a}}2]arڸ#-6j ,uU3ei14FTO*6pm9o~*}#P-"anJȟkf։YzT=*tԖ@i rZw#J=vz]XWwsզ!$l` ށ#Zإp^i,w"BĠZs[jڨBa-KdSQtY(@!K}` ,B4?z45 K{[ wEgGoIM"[w @ELq6Iߧ2 S >y#"U˜~[Hgv 72M}`(6:I:@9 NNLc 6t6i1H$=U:QhpM7=`w}0".wV Mjl dL60HY,]0D4 &D)Z_- kOGL^斗Hˆ`x'GAmhmA[2|K- ^r+\$4&mB7.=IN $EO̫.L{:j۳YxQ -3 #f $ %'SԪ( p V't~* 53Z2$m76lwT/kҢ5q$6&i9wo'EZ3 fMV=0@Fu^IRL))W]E ~63?ؘ0^oueK[D+b4pn/~F6 p@ۭ+%HP [c>a*`zJ+X0a6 Jkjf#:@ȁ I`U0wOŷH(a-ҟDͷw9H@'mb6 "[gf1`6} OprP&>xo㯊40V2=@b 1݆$[meVa&B`tLZs['Hb덎oKW|dۮd`.Bn8[Q`m\:mJ>0rN@AP{M!֑*|3Ns_ ,T fP82B\L\(DqۈM%9s7[ĩ֦#47 XHC 1rEՐr;ng{wG`magHtTV*a4I [@RNnp"]Ӯ C_EM'%9;vRj[UhJ0b9BR 3EEE ZT^I:zj*LYbTH1wH̱4*|{5kd(Ŧns{SF+Tq6?#꺫 #pEV DaꌵL3lr8;fR$E!H]E 4i+iqsU@ 'thZ֭xS$FŻ$LvrJrzU.[tf>;5FHAѱA2 x80d KVu6A _ dZ " L uI ;  }\ :PAF02 ӛF `Jtv N=f3IIAcg`8z 4gF>Pd#p e>)[dBjd4Z3l{AÒ9NAc@']U QozXh䑾T`H wē;BѴHo};GLIzqGp*lm2C tVC#*`v9r`lmf!̎`+AaQ%^ ȃ! $m<)V60s"mCʩ!ceigK@kFA ;n`[J"D 0I8IU ‚A8B\ip"]#y!yFvԒN>Fb#"]bb>ۻ꿂Wɟw" )U?19x$I`qiZH~iL Ncr/T<L?^ :4{f>dV,R1$|y` ,Q;?O]Ҷe]GP&~fB=?!H}$"I9VP04HRj"w42Oo߾H1Np M@L[TI2AAsP*{,Uĸ2E5Ϛ rOVqOU5[2P>$ " 落t{rjT W+)VWZA̟Ĭԟ?Qj4'DZɌ]*`;Bנ QWPh'[K /^ +ſ5:DEB(ϺʷsTw~>jGW H{KʭJP*Y*N*[uj3{.YP*UjK}hME**R0igoK"0W1sewf7su]6p c)+js3tqcbȄ07jv0Μݼ(6,70 ' Drmj Lǁ#p`:L-N,r&#qDo\i$ 1  n@v\W1ՐDaѪ˜ s6Μ4݂I`O$Ȣv,D lt˸Q N 'Z8CfI; N. j4 Zr1d*˗`Ѱ7}ô~ɰT֩ Jfh\ ֗kt@1:yߧ&w"r| 'k6JL0|@}&޴ۍʉŅfw5D$35;t喌ź|1nĘi/kXbM);411SCjY%;QҨP#J]J4V 32ŢK`P~c&q,S+H ;߆&"@^ yi$-HT#mCxe*}If2qlH'+!s2@iRNXhل MA4ҋ;;I}-u_&S0%bw,6U4-$<=ۜ}pfD/?vْ5N3b6؃Ta ?U3ͤlֽWr1Fc8MtO) cJ?SnՖ*ȔlA܃H̦ 7mJu=ZzcAx*JʹѽIq2F7C}?g=Vas0UV_Gl&F2qZN͎ag=VdL`0ɆmREag06j1kC %0Opn( [Ccɂ 78QH|lvc8"5-gB_]S:@d Uẅ @̀fFqb5 H++8ɴ2Ld>4O#%Nd ɨAt<‡Nc! ݢ 䱲%5Y #1_ ?uU+Uj%NI}#:?'gw qlIb`C 3 d,l ]ęWuf6 LjXf43'uؘ2 Lс"O91jfu;F.7D鈙9|MI=(cIrdI6tʊӛW;i(nHZk;<"8؟rFB6H>fO#3&>U:;45Q,DgT m32ZG}*"ܣV6!=q `0մ," Ϧnم+"GR(\&zE($ 1O,gd.A‚tNO;Ӝ=1}1O@OH7 ?My#<9 K 6)}'?} om/\GOX(b㟬f1Ln\؛NLAd-U*l]&c R=ծbs(vp%nQRķ,Z ?CiU6=q`3d+MiºTI;y!H%6ќ^?M=  cE@693=joԼn A/H*@`H,Xt-TzCW&x2*,Iޞ!NNGH0Ěv1}eQZŮxXNM,(0[iܹJFjX3lU2SE XIp=O܅3)Sj4=9y3-l#Lܳ%Se]`&<.Z4%Pc1  &ʍ)mv"6}ʉ2-߉v9b;IĎH3~Yq4j\>d7ҨANň'@$w Ce!q"k)T˃9O}G^Sٜ(ݝdfbގf󏛜d7?\ bGf$j#ۺ du/NRa `MӍh EoJnOI>@c xapw'12 dTI>N?'3Yɓ22q'Q9ǷlG$GK8b싵4DA/$hH˲T0n*1 N ZLl<Ϗc?y=|o-m<ɏ* _bDGߌ[Toh؂'呦-j ́#%T O!x,y&eJjK_cga>3 @+?cGe5BѠ^Ī,0hUȨOHߵ PSӣ'ҥBZ \*`SZ:"r6H" }<101̅ 1h#>b-3O] I2c ۞Ht\.U#*r##/Je!T6$y'*s<t89j|Vlnf* {Z11v8@$47uV JTQk` AV9M =zsuJ3 .lL]U A G&H8Q-MPڒ AGFBiQ1 !`ۯ`J; HV3_IӃLi{~Q*bu/ <cftd#mGqzo>0ANua1z 3zLڠL .1,G=ŖA`pA#f `C ӓf ǁ~6T^ P*L̑n@UH3]6Eާ1%*=}"L}eb1#枕"uaOci' 8n%z4v1<7D U@cr$@So=Sj|L(hx g$c ; $`=&8Nb2 wרn"AZ1r $m0vL?a&U3 |\ N:T4~@n''Y5_ >#UUX(0]Hi J#xG_ gU JkY^S@,-TOOv tF- [#`6HJQCG0f 0DIclu kT*qș=g{b#U0a fo'lSNEaLhC sQ*dnX"{Vj mc-Z_ !i9Xh3Xé(ns[OP]QLT SArQvMGhiG ֣QDgȘcH k9"g`bv^Tեr6"J#[ש`6F&A8`1XZ2m * ^lu n ڥU%L ZLYk-ƇTPyU-(3䣋7zHnڕ>Ʋ֯+E3uZfAUa6j U[R1SZ zԤ.MYգz8` #Kp2ꎬV4pt$$(Q?tX)zI9ߦ!9(m'm=}bG3$GVD;H"H_:J3g?dc8ni&BbdۍP#ljGi  D02.Υ ĪJT{~](0]K":|`πң^H06f_o,P;nY`B"d͑#p#={m+ NK[3Q 0AAx^A'r7=Q+v}jN0b"?)CBZr?!*,=@9}['g-EIe&H Ƣ.'ӕԃ{X] ?yzc$gV7 ϐySQ΀.CopS{Uc˒|L+6}1*#mf$Bzz%^њrpuo[wut-Ԡ_|@@1n4fn7o-LU|6b0o#0L,D5zuL\-|[!@Ǝd !OTf7 nLt0݁1"w j[ W2dq1$O)|=jQؕjMͤ$?C6hϑjD0@qתfiZ5}D $c>؇DRiPvqnĐtGLHoƔ~M$Fw;~6+#ߍ; dbJE'ۤ0SFtBzmM*0C8ȋo3_SR$ʱm.+YWοekn#"#Dg*UUiTS4-HJKFZV@]5oIG@LxnQ ݤӈDQog#̟H;@?@G~1$1'vU$C]'~z o#u"S ٢%fGz &~pv:]\\L&vyl`F`JE6戉CC%swEh8 .Lav۫1 cTOwL /رĒt4O`-/9>ۿA?m=z]jѪHte2 $ZK`G_Z{~)|Sk-mf*:ZY^3bDttdg$ѠXdxP #91]'ₚG8uյ^:ZYd2:˕“%b&`9;LDea22I ' h2L+o@1D..L\ m!?TʈhF$ d_=|KTT\HE sc]uO^L(S ( {wh^1IxՌqu8'{0L`nGKHRXJ+ dPv!$LIo' ̖'F& g;D@sAIp۬2Gl/ϏE3<|[ĞUd 5FFȺD HԙA:c~0GL ţ2A`yw?u#K(UlQ#s:E?uT╥u.Ajopi;ꦺ , )Р X}zUZjDc U|]]2m\2 JFA 'N $[i*` j #_&a{O sDJvN7 -aiծLOg~10:-hɷ"wK]20_Xɓ#'#z" hnƣ*nCgnDgq11猟:cJ*1,r7#L"$_" Rf&`G^F幁13秬i"NQxam91 ̮hc.?3p`Aќ6#I$@emFb G$D͢6=gsI'n:XU` ۉG0{3ZEG1*Bgy"JH`* d =`0c.3-g#0]G 7 5ԦhweCp2s;/O.֨Zk` vDKYsTjRtHb9|va-&XO(\}=R69L@iD;6=êMuq)MoF t6TgqjN$Rp+bFxaT$4ӛ5\*@ĶʹUVS>ɑ7 q ۂb$ /]+Nw83vDNTuIZ0.pN #;GނרGUO@z@KH*iwgS4KL֥I*ǩVFf xE̻sji,D¨cd_qU A10ՎDS#'8nQA+l[DG0 'Ԍ\IY6)ԁzA`N~H#uBJq!1m"81_j~TI}337&'lO_+A9\O' *GǣP|`g8 2ڦ 02r0MpA#fueZDһet ge1sG'eYH32$w /P 9 qŭ@%cXI>A"@lQE I7VN#sؕtA-m wCp~CSQܘGד9%y |H}$m߯(Tk "ǦWͤ_v U jH`9Y.\eT$fټv_0ROML;ٱM[ҪY700far3Yw4T,}z"tHܴ *=6<3siEnr9 NFLM% D'qe`P $b\ʖ|=3oe; ?NdJy;o18af0r$ Xtٌ`{Fqg 6WUTUVBaBjA3SccBT 8Ei:i6#ʦbAO-#ypsČ&Li6G$3 llcJ31R?P@$|3[>Of1EqIrT-1yNXlB> HO&g&'3zm721Ir04ȎO271c 7Ne+vJS 7r 9&Dd`kx<*=]TmrvJzQrqw)TYF#U2xWGP*Q6mtVӤux C|E[Ȟ4#q , |8DANd@bm*ے1ADn^mv #11"`l%v-6w3 ?#6tqX]b dfjw N&Ѽ@ b!b7bA˰iIgX" #9!dt JiK3ӏaoh;N+XU`1a1P&ZZ`9ik3T9`$2ɁR,F dy$]V ߈₲'s'C^8ȶ1GqFw+TNcp[; LeOLsA,±,H1c\$̌3#],zv "#Q<fg7_lomNDR=!Fd#L+Tas[" G5fVWVI{A7N$ ̄l 3 ~AmxNS 2|b~xpM&ۀMBSD^a)oh76?Vq` PIt1qϷOI6 Νa3$h[u56R|# x DE*US-7A (T;:nցӨ$\"B*z,[*ژnOuҥE)w^*5VPd(,=M( *zvǵ,SUg 9kT {pBEcjSZ\< r(b>%{}1WurUm&թ餜isrHt sZtI$ -O"9?L+w!ygϹv SYɀdcm'F3L) 7TZ#$!~XlX1 AnZ2F 2f䤉I 17D =dH?aOPrNxAY6Ql\}"+6}IcZaip813s(>2@!`F1|'="2Audq=SIr3D۹K˶"6"Ӥw |2-ls~, dQkF' GBL#;( ̬p[ dXPVps]Ocfw $dKxx r'V6[;PK3HN ^ ȉEkM3`R-vIe6{h \m7b$H54O-Ȓs PNX\[v"؍"<{eЅ$bL>Cb܈;$DuEPEsbv+[rvO"|qV{X"к<[##韴}93aL>dcD˒;,`̞cL A07|VA; 92|щs!*@?2hP (VV IgF@z _#;H fg9_D j)7 mfJcTS$T&g}050?u蠤Su60rO)K؜#'[McIoı6Ѝ{@I8'>5`s;NǑ1 Tvz`M ӕD(2w$^ `& h @XTP) /8dڣ=#,2c/p45CZ>i*O:\?y_LHܱ"BI#T`wn)Q*0ȫe+^;#B z6'd&QN릢G#Qq,yݹNܭJfZS,`GĪB@-~kQ*T!#*K)ؽM4P]PQX YCIQl1-LK_زBK(0f1@Fĉ}VTψ;3ȘH+|qmnN?L#5y6DQ<-H4pA%jLn3!8!N '8>?. @G FߔmΡ3ڋLΏz-"L>'$nz;Ay;|A`t,关uMn$NhUH4E~Kt#90F`bA=Ry>b 8'3c=*<" t7#n>Ì:Xii#9'#؞/svn`Kc?wPis ߊrIL\7 ms9Y,Aa@3c"90ą Bƭ[:-R dQWӛ0c%&sB13n.){uk@w[`36ۖ_GA)8=|n } R\o7&`WaWLHCDr(S@\{`DpYDo,A$7]YFD5o ttշw&[ѷo1Et`Oscf\! q ];fxA x$f0`}?b0qHdc_V'q0* 1w!@n[) 0`d0}$N̪c"d} cp ca}1XT&%v&#y4"8Rt4A:sܑ čKzFm}ZL" 8)lqAk^V*6*z=<oôKC5*4 Nxʵ]@VZޛK.|Zi.0. T%#k0%+X:Mn֪5X0#kDdV0@~:ێ.rP=))\P8= ` I>OԨ"r8GрOb ˎm,f2L{gv%hϘoĪ]˜TvmFT\#i'N U9IӀ|܌-H;w6.Lkl^"kuFL4wTsd匌`@UshɃ@Y:Т62s#|N16h~@N D7:bY8'aA@w@ OJ.N>8t1?Diռc$ѭT"tS 2'OU| w\fO9$OV1c,LiĐ>d0& i;3!#BrCDڅZ db`Yx1i6 t~[0Ӫ8$}f=HXz¸a15.mUB,ʵ5䲉=MYb#Q1.f r}yiSw1zMe% bnyΩ=P>wIV|p6Hqz.0<<>/ (y35*dru TTsT !Gr ?'`Φ'Bo!2DQЉ ` [ A"'lt' n20Iyd@8w"u}qN+ԕ<$NЪ.ZF HE"@?"z flQe`ԘM KVjtR)"l'KTUTA2gUz{j6RZVH-7}T{2*iږL`7ܛG2WQ6-` k ~̈+V "H mps-h:RH#a889:38\q[j;5~ }97DFfcyliga8V17>DtX!ZܴlaYyn 1R&悙38k{`2 wZ4 o=8wQf L2 \2F0TLzOg,.-H EζW.Fz K} ζh` JJg\oh#Iɷ]`O "YLG3"N4Nrq mqwly=d9#V PoNCLil$;*CgӼZ ȉFV:B!TA ".i | *ӾAsi$o1ѡ T8 pXAפn 3DσӅB0$ꙻVuɓ#IEn8&[ *, K-58%j0tťIqؐG{EBjf9A Axs$6Mӊt#p&ddkt}n4&>l$;g!@(U S 7j0yFNbgH#"~ԁ%-1 #9lKx*J]'93qԖ7j0 sN%;r M2}c0Aw;}S㯌{-3ĀNc#?\?OڹjkgK }$cׯw5\XR*D̘!'GTi .*"ۢ*,bLMB$HX?b`IufeNjyqȖ@1r\`fO3GTԌnH$L ~ܨ+lA-;Wh760cbLܲc~Q:]B3VdH8ɶgiq9= L;`/[!AK 2U|FX #"o!ĀM"2DY9 pm$b׀Tq2&4eƠ˧u.WD &6I:18ՉΐA %sbr1!}~R-Gn`[#yx#$ }Aǻl9,5RyヷX0x=|-jeC+*g6O/&5Gt;yo;-*p3-5&lL~n h,!'l;LLt~ӫ#27Oq\M~yո9">'VFcaJju9FϒE 7ZJCu0KCN b=^Sx:,IsFDGuWկF* dHE b1"h0BďɁݹRFpHFw(Q ,Lh'`T,č02wu'MmXP"7s|+,c0XBrkȓC2-$Lk gt6t`긂p۰[}ΐщohDg{-Dm!FvBPm8:0" ref2A&A;.j%FA {LS8짋3ih$Np6i9qM1`ޒ䛒K>j ?!;0VMucR6_s#Z*_EEo.P&jhgvoU%7+8X--Q G<z-<[03f7f˾bLȐ#5#DIv ~y'h8"~#ޤ!Ñn3 ns{v\Լ^Zk0)-@72?g?|^:"QE8 ƶdpz۸n *bmP``ctq$f*1bLO2r%IkDm 8 `?^7i&&gw;a GHH8і8qn=2n ك1Cz1>۸> _n3gHU3]n@8rT\A83 o=` m큾1G]\صQ 0H>#$0y=3/@m6̃4-I1NGFsxf̃8ebLp&@l& C A:TDlBR [~ڽRȘ5 QeF'=$V@ F7tᘪdgKU! ܞӵ_v2MSB( mfTuFV/2ZOjMHuC4+w vri~zo6] =t,@Qjd"c3uL똒i'$Mn.E o+& %Jh\չuYiԟ+Z1\}7GCVhei7pHf%,q3B*v bZh6-I$$,L5^<6 Sf8j `o7}@Ry H&A"0|E  ,?0ӉΘ1\l&ZdfȌI2|q;=&$S%;tW3 I@7Ǹ7d :1H WI@m. $EG[2©q9'Foh`UDF̐}܂NAH &1:Xi0 D';"|O]3~#Ԍm3VqRL㎝!$g}3GStD*3Ilb8!Ddx`v0M uwV) 2.5>gNH!HVܵ6ȏ?(3~Y*wOlbʐHrAPu`A .*RUDM!W`t oޤb]5\pNTI$NI0IϺqHgMf`( ^t D '=1 j\`A"3;?7&HąhB`1?.$|A$q$Lq򈘐($3wg~XL9,DF[=:Epx d0[TF;oy܏S9'o'cG:$<'~mhq6؆1@rw0 ےOqe8:-.?e~jҝ*("n2ژZDt'pN #|"$A;A9 P)avMnG f|@:`]"vBrӘyּc;&sdnZ>Šu`wo b=/* % IihTNtAr NmGF\nF',smwT7Z^  nnI8CX9@u:6q:?Bؙu}ӰBMq3DjޱGV-1#|0`J#,b'&Q‰aso?0&IG;DǐLg2b:*,P?3 ;ώ{z@:._N'3?zrHX}ӟD F`H2 a7_"$9lN7&73VcXLH#Fv20Nt˒Z`vU YeZX>c'DD:ăӤ ,%#vʺbÜSf`q:Nwn+WoML]F]0&ߒvq1 0i{C#nt?ί3?nNv;ӏ0N_۪`(nΔi-Cr0̂W`[nc#Qc%z߄Wl$`C;T%8)kejhs";Q l#jYZw6A1PYvjtfRʉ wTCj ì4fHMflзr$p=zit0nMdH$uU*YAӨ'KkB]vq209 <{ff@?[R|N"1<D2maA'"ca,.b5t@؝DWxEt/dG'cH-1RN Z l3Iߪ$ȷ'Z/8#ydʈXx s9l dO1 2x̬F%tst1f" zb g\vX i"F1+p;r."v"ZdjM6=dD6.&۷%S0d9\+DdһDT[|H͓!Uzֆ3>16A1{f1<'eVLoS:`+ğI̬D }VQH2 ?@dc8CQpU'~tmɹ;`uS@F=H  OK\݃m6_:ݱaHA?Aq}~q 3?@7vX }y%a<;oE @O< I&w` xhęiZO+N3sjՓg"H0`@ȠV`?G0_#?#򄢮sCKEDŽٺ)'~^w׸1#Gr_a ;Αޤ(a Qr|>Aಣټe}`֑0?;A_Ju w+1?X>^O9&8zBO7#h^}&|6"dYy3G8% ġ_# R )hsBQ~U7/F0'ol"*~뻔Y[񆱻{`[ǟDz^mQX֔ǟO/u jֻy~lUֵof#sB̏Q/jy @$=8{@5kHs.r\7_}5Zs^R~_Mna.'CNn>Z?> mhX~@qf mzz98n;i^~^o3ͅt$Ф(;) &j<,ꮿߏ8m4յ q:fXW4_N W2K;ٚ:G0SS/`vS:s&*$Tt<Κ7ˣtu=s)Vǭ~źNw wʤ>[N/{ymrcg_Zf*)(Aam35cmw~UYރa_h.oKh=~?SZ{GZu[8;ӓyGV@o )7 4j![ ڠ/g8SҔ[.Q!x_-CsN] ǟ5qk7oF5x-gٵY x{y5}=鸷\aGɷ{pcM۠Q! jUݴOR悇~/?W>oj|~tz:>m :cו=QwNAK9?&f 3(tq-u9_mXg^~+JPh9>V).IcF&$f_N4JҮ˗-vxhd{P;X>HWzkXMJ p~pz}[ܹx~F֤xfoP}>Qky}c(c})w[[W99z`6{:z]֣E<[7;:3Gu8yhqoZ^OX?^q(zU ^QSކygbZuu(׍頒U0վ\![_뫽]t` b]_wpБ8VR8i(Κ%leaV~U@DEAbAcx%yJՉތڊ>oknݯ;Ŝ F~-M!X9fYKJٴ)c` Z1bI!X۔/S7/Pը`KPS@ُdUd/} Z^C} _H5zqO(a>_X] 55uoOha0vr"ib{:SzL38'{֞Ͻސߏ,Np:ZiFеofҶBj4{+nx| ƕ8@QC%CsHhm·$T>U{)JVn^3`e+r֮c2*Y߾Rz[N{ZƎ 3}ij!ޮ:_}G^_6{F翮zxn=xVʱOZyR>S{PWM^_]MƼ.ې"{a z-8Co8ν3@qȊ1͚қOM}xr_sF<5xՀ!7F#ÙӴTe zT&_hxTP$QtJ;f` ` 6qW |ʣ!9~ J6M0ihPR.Ԙ.eJ1ϓ<JFD|J r;g$dv)֡Q2襤B LeĚϒ>نR^c)5ڥ7_(Z5pyZ+sB8ƶfQ-`]Ɯ?Zc]o Cqy\Q` uְł^2ԐYM +֧QRTs9kՃW_Zrr(CHbۙ}G ̢? JC5M*TyO :^A뜃"JG=w@#Ϯ.!kKXPhoJj@P4>tֆ渮j5T7NZocs77k wGFnNzc0}HB^%MO: -  L`WD Q(;m($B*M(2gYlIGR@`W|RBv!!WqP?Pb)+5Djh _™yiiԱ< TltRJ@?v蕻<ØU)9Z㿅'D/BuI_n' c%NVWV$ʙeg4 ,`iAu5Ҕ+Z_W8^oxӍn7~Z׌*߿_saKן!]iόy[O^83H9=#˓uE/qR k#RjcNym1E_'̖Q4s #*Tsz'y2eV$ݸ/2bViP-\x}9nhǞTiBWGygLeEYB7_w-d. 7~^C*Q&͢[:_R z?HY }*R_-gACJ{ 9}+ʟ[6TѴ,:/ otl ~a7Q[s/vfc7,nL߶­>^ȕ*ȩr{Jx)3f+̥ܾlUjՅ2E4d4!=_mh%Y,OU6LXa6*MrA IuQm2l'L .7$ 38H_B3?rL-.Jb! 2SǺG&3x.A-Ji4@5s;xdgxah21eJi?+/ bVW'ݴ Yˍ <7V.~ ojÃq_oJkc>of~ɾ2}: ?/4jyE\u[W>E_>IVU!OJX]@;6JnTt\Rpr "[RzW?H >CgAA?1[4Iޢ(xH Cz6|ɥ6oFlMltZm53s* ;$V$TKYXr󎎻龑Aj[z>mAT)*8 w PYXԔ9=hH^9=M܆Ԏ7ׇ; R}#kui⛪tr,y:-9NPJr)Gp̜*> %4$ %!6;4}‚M }kVm87T@ msiȚ_JJR96XJhDV( )YM4~;j-2cjb:'~UY,Vh8فɼݻ٭)g_(kßūoDB\8ϓB"r%t̂UCdm^.ͯ t'An_uJ:T_V !+/_̵Or˽WfGe Y ;W2)/]K)%Q7 .볣3kӦh>},i_,Xκz@E\QLSՂ)/ñ6z /mzhMz7zvҚҵ63Q@E۩A t(ִ2|sSqoqm_kim<4.W.R3(zo ? Uxʕ[s6D e'=ԟ$ dVbKmOy%(*2AK6EM{ Ô)KHf9ҝw"_vPSQu$mmL'Ƽz\JIP̰NxdCdJ 9ґWL.8Nf.וɣu͚ԠIQ3v@%%fg.yOEF[UBz)AWi`e$qt݇Ĥ#^-tR]$RK|?MˮCZ^۷Ŀ7ο'O_?+ X _ѩ]m^b&cp>lԽClx^P2RÏ[@I7|eZڇXG5o7ӍNt +}[X.C.mK4ƞ- 1MsT3p#kMKPt1߾!~H J~ KPkx]7},);˦: {k(jߌA\p冏= mmfJl`exke;֭vFblRXe7iِ ;ѿ,@ oLf -S+?R2kᖟzdJr.TSoI;̕eTSfS*Rd9f Wagȗ2YqcGR T%I\pಎX(Զ=eyѡ݀3!t^1 +FHjPJ?Bޑ,x/@ϔИӥK yI4 '6*LlFUOVn+ϯC_O[sڑM_^\t rf ԇx-54:88OkC{~ǐ3tR,}`ʄ똎-OLي͘%G1P!)+](C3e&3d!7e*}%aK0]~[nŨh H[ BQm'1Mո;UZFܾa{S ڐ`z ۫[ZBN,қU~T:0z׫@mKYHH>{\oʪllƦߝa`6p{|Jyjw54H9c-mxsj7=~fS`-G?T{27ZRB~.\M)O֐& VtT CƍƆTFjž%eKMTvꢖR7fϷLVjxZnY⮻aD/.Z_JAPHܣ%J6 :R$r[6xg@̤SX$z%kQJXŒ@]Y)ЪUAp }&}H(ÚʔaǺ-vtf]N(ord(3IXSacK%-,J~%0!m-nNo{ zC6:LDHڦs: 4L^+- PTU/V[t56ыYu˪{^]nS7o=;Gж87ORmU.5)?U\e2@B@Ȕxw&6B7cˇW~&Or R(=jByY$lT3[R5MxZu xAbϦjBK::XZ;ə\8mi=I6ъŐk~v_V f](Ie)VT%JlQfpĹi`OzhBMV: bkV{׈ V;;M^a+zxM](5qGpoq]Vwz«ouKߪx9jysۇjkӀ>( nX_v@Nb7YxH#B2e Vi7)MZW2 yyb Qgn'ANA7/"МԵ}dՅKaj38l(9A4I?顶KJ2z" ~`v|tj]!{>=3U:<mj J f[Ps 9zٳa9D妆Y1OdX?&jiSx+d.xU8yx3Nfe2&fcSh4vJW'/:*0{XFR^jiJUuG 9br¦ v}h@Mu~0QhVNmґ9L6D̘ɖ Z[t}Hs~-GD/m))4|[] K_8j_}dK\0>P[~|E`Zs8JNB'{|%8L9J}'GlbEaJgJr9\Yۈ}N6mUj561Rf@4z!Ώhw)7u}ma)2-_mƀB&{۫,Ӑ)|W\! nMZը/{RQXͭ7G7 5Zvo(5״?xQ֖=[ hlεqNVxsb޾a[XvsOsQerЋ%W.Ei9qW;?eKDhuTO"S)4`($,,>(KYFk\}6(=+8ޏS;,- Ԭ$1[\U&h0Bxqa%IAKJD@p0!!i1/Y04 ff̔$fW30C20H,H,'WKh7TҔV'`w8!'-B(JFK89EC E~)wȽZxdID;2שm*q0M.K[اDEUUzіys ?\Egmyp+rj-NjFt]znGaEZư ;[Ҁga S9KGs4)%`4!hS`;y6߭zogupy4Lݘ4p NI$F9]9j\Jd0NYa*/+ J)W̎Uu?^K_w0aɼr}cݢkqiYLTY%ӵfk HjPX0RPn\9~Rv G(C.155xJ^$C% ˕aW}y1s%eZUQ%7]y}9ouBhO|M_^;w?A w8 kS\cֿW&3Ti:ˆ-_ ӃWCZKV5nτTʠs0Q MN[S |RR/ aUYjZRC[\̒>ɬɪ V1 !@-*u̦u>IHJ0ȧT贏xpARWBq #%e/KNіo+MsJoT~ZXeϢUjz;}=aefP.<5ۑ_Z;qJ Oz;J5ImT7)!ا Tսw  ̻~LUaNZΝ9ZJ%Y@H HlК2Gf;(lS΢ML遀D'gJP3WBCa5-'Du-ZJ."}!f?T]\= $MO!6Yt@55;Oiv+^+jQӓ- 8Jf.Bw S*J)J{)^*k_|>WyVY2q N1ρ>zզj16S҇za)̒5 ʎUsLjfv[[pԫeRW[Cg{R"sZh4#^\A߮ߤW{?1|Ꭵ-~!Ahq^ꔅRaz¨dy|Csf %LYj{* t&۠ߚ8sYzV7=@괎ːț>Bg1oT2R<(h_Du*6{?(wJAw!?/.]4hsN)WQ#\cc<9o,pܗ];m-$HX^ .RAf_"jɆIª ug_e“+&-;xyDu/J~+Zn#ԝ+HM')k-`|A'l&J&a$"wu$]rZ%(4([QKY6^jԤdO'^==8pGH'jq|eVH4kZ?\i[@j~P9 5ysyk|E5_;0J s'6P3Swיݗ<7sROnX_a_<^0Xa3DmjJvlkIԥZl%('e)>vG>$;ل>5Qҗoj)@Z\"m[}C&I?{7$P! cXr{BL=S)N0 + 婐x~MBZjLDGÒ6A*MpOԺhRXq$ro èW%d\Йs ί\`?wKVnB򿆍,'jZGIP DIjoM.__ׯ>;ޛ"J@SA)&cxL#`ȟO]*˟EK6i$Xj36QLi2:ק)x-v*RPc0S-BPN±N [OxҞBujgqRR8)bq Q':i2l1wwİǻW2\x",Xk%Shf*ZSɉye&d+QQWvR [3ׯXoѸ G][5M6j]dFRW0 SUEkCXW]_!U q$L'I K! Rҋd_w4ΥڄXG!+͸RJS.`PusXpHh ĺĤZl9Km0|),$s)pJ| `5 rGvM_Y6&𤡐3+2UNHJh9ᥡXP#16JFd'&\% ܖA5e ;+@+)6kB/} h2p&6BA)VU NqIt1VH/N}:FdɔC춋R#Xy^r57},( SQOʟuxVëʯx\JiySIUѭ~pu}i=đ𗴡h:e8|eW9Vz$'cKGc.+ IĪ2:f poT[JSTmeT ]6a(j3.e5T|Cu>v_ZGLzSUt|..ǧ6|[HROk{>)=y5 g{^zau"}"cPcz[;«.xŴ>e4fv8&:79tUk΃ڡ{D0vU#㜯v3]T](&x(Q` L_&bْeMN~IwDܾlTg* h{cz]/H%OJ΢uhT٪}#?t=t2wApsQ-ŕh{2@k{7_y%,Bm%^`Rp ֺ 㝗hF7dYKJ7M 3{$Q."'`R# <@fva_天4)9"©]3~W+ 8f) ) !ëBx2Ԕ) "bB\6Wmlx8L7J*.V,XD&y2NL7B"J Q2r;5Ĥ W.x};HY#d(m8gz&+p!RrTfɶjiè䌪)KwQ9~j\3gDܥNVcH iHQWIlYHlw6a\|{;*6(b^AULLJX#~IJP.RYsRᒇZ8i3&)2T ;C[`BJw1}R'XXGw ʗ4SIRL:br&bEox(PUçE̾ddk@+g6QdA$ e+;E% DBjh垟7^IoHbtmu:8M92JRa%w¤y*[Wȑɖ Uu414Vg`lʒ_,D6u7A64*W5u{sS[pdHR{Ÿ] Mkc }hfe_@(%x'{r WER?åEM?n]?Ĭf 6 e (/.HRS̛1 ޕӎbD,XyYW%I UKf! 82idJBhpfQ*X' *^ *~Uswh_/ c{rRM3JpY UD&옝/Y2p|b+8*BLp>dT3+0ZV2ǛeZR'MayrҒ^~wNc&ڴNhrSa\g^L؏OX;Q@e8/[c^OgHIC4;q0V3+QU) Bv4 ROTO\"QVldˬz$D&7%ΚJ@%2tJe5Vѥ*JRcv^1;`q`M BYK I*@{;쒠-~|R6VkXHr嘒 MRn!ޯWM|UqHLɠf4Vk}W^0~^)b9E=|`WշuPU6HP|8@GʺQiN4㦧U`{DֻL*(}9L ? ٘ʧq~RxseMujGj֜xMzi5ˆ;+YxíkUwR4UX1qCb("^@jH* Kll%Ja)}r줊M '!*a/ +OR}[ LX(S5P+X kS.Z*GIX?C_Ak5KVxn`IZr}+aFΖ%ZD Y2dDmUXvTT8bb%-.KYfglx!X̲+h-f"-#>e s 2eɛ9x9 FƙDNRa0Lads5m,*pHũ(4(9e'*; [dfK@Vr׍.ϖ-K$e[K LVU.JMJ6.p5D"feL¬Pһ; %sKJ~"v <=6yxB@FP0;KJĕ CSI .xIo9]Z4+p2q]n+NP5gcgIHOd%#j.]c'>*d&H\5U3(H;6v'2b/6m vR‘,LʣBfĝ8[ #fN4ʕ-ֹ-D׆f=/ZuNiֽV8Fp@jQsoXO@tOZVXŜWn3z5&XѭO7ůKBmaX`EJX/]^-]_uZfv^1]ԡo9eҀ3iS1BXJ1 *\؉ZnRw a~JƸi*$lBӊxRYNl镝le#TQHL &).өWh}A3M3/`<\8f"Lʵ3r0LV3au.g6h{FT sN*fx٦]3}1X$X?y-KȰ? R:X v$ȪXljTlC RD9=ēq:[TԮaPUd!7/1ݹZF%rʠ^ BZ/r#%3!JHD"jwlYv)`.jgvd.Яu"RUIhI2[e;”vnYv9ˇK͙Te eژ]970Zx'Ӈgb*SԀ$eQX*݁-8ĩ}R3x Rf_<+sVg?KV#㗯(ZmSS~|!\>p4{cm3_,PiK `r|5̴e8GmbԬwf{Fĉ(2H# ViAFbY@?&KֹhA0f$9#,7B&R3ZBSR9@Wgʛ+013I5ÂvIchV=F}܀ MA֠r?c-T z֜z(DfR(G}bPa?_O:^\|)oΖ.MyPR][ѯWtѮ~Z^<-JjG!|Y~y},-kGC֕חה:\"n ЉSRU6di)YJQl$ýbqYsqԙLBRsLf.B'/,+)ʔ*mFT8<^_*fe Y˥vZKLfXPY5qW7^`(Ǐ :79~P-1Q'< f0ѡk 3_ DwXꕘ'kpU%bIԕz\JsP+0Iȶa~#3)z JHc2Xj<6t4,tӈsZGpM~@:S^o4bRX-xP _cP++KASJUCի\useI_1x4ND$KR%f+'R_"I (5i;%l)e9RomеOL))i36Bd:AXXW6կJP7yf`֌8h!,2Pfn{GyoiוE4:@+^qq!^=X+Ax ~h>B.*Zз|@t0i(~M۸~nc0{K˟-#?FvLDJLc)+٠mηQ?Խ RR]@M9.n^*{,ma78ܲd_b! b %U;n\8 ,D(ǭS3+ii *Q[iRH% lF˂s1GɊBE3RT5 1S; T:@ HbF f?\Lf]ej[2SQؽ>jqȘۥJJK1 45Ҧ;X8@8+:7ޮ4"]fްT 섔vL)Xa|BZLai%8\^sgQ++LT(*@maF6KT K#_ 6A.Oj~pf=@aooOxd[S >/hRd4RAI/^:bLN"Le~W%Obse(1]Ҕ̘ T[k>Λ䖠,z_ 3e7oN6b(Yp]xPsLYu:5jMhK>˲Vc\`>_Ύq*#.ȇS Q_ 0{n7&h~c=G]nXx/WC>=oG/ ?ԸנLjw\ٞkVӚͩM=Z [Q[Q lGʚWH͍+%0Wn[JIhTAd fODHB%aDhP椊aMn-8 Җ=.Dh󨌒6f :,m͚rZ{l8>Z&Jʹ;ΟG.w&wc0-fZAv͟Ô0r&&J"2%֡܁Rj=D"T+F jhM4r8ijRֳ!5qާ?yFս6xK0*lB9s(u g5bj9!Di+DPUԅe|J'JNhK)sVM,̔((9\%05$TI[7Ä`e=8$ً@-AjA`% |BvHj Ekg` 1UMhw[I?Z}kqRKgU3?3F(h>a˟.`qϜ[וPC(\Rժl.ٳUfORTnkEfSޫv" kW!g*-/Gu6\+VT'ևe>@O0)MwlN*mɃ-(lK ATT;dXBIJ^89CO7XR}iUF(k omQ|ǗZWmc$UYO/ceu;]dU ܿGj f jAv* w:GyIRdLB[*,A$ڥ/BI#C BA0) BU . []B'T{2OstKQ"{wL)R5Xӯ(SS wVhn-wۍHsJ7Ɨ7=71릏Ӯh7} 57Ew R?Ok`?S:~q߽Dά ʨ.T)Nêh&J5 ?ʴ*.rRفyY3q]dPTeW7q,Q/IZKk)χNYuD,K}ꡎd܆t볐5TP ;lD8\&**Q{B)|_a|A\>!hZ@!"ZB#?2|#B J@O"e%JIv Fl lcͭI!FdW#;a>ї*R7ӝ}[hߝ/Cg\L}w~k__Hug[yw!xWJ~DuK-Ƒ.Z En78ji~OS>pj:ݯO= h۟nnkMtx5{|ƄjnY$."pxNj?"7W7tp O?F6n0RD,Mu841(nҖx.Gwxl2ei'#NDyD ּcp+\*n#1%!i(LԆ9,[J&LaEKAS(x]-3Bcld}BՇ36#2s3JHIF7sғ(F;k>6Y2Xĥ(;$褭'AZV%J3:SYexDK~2Bm<6r r})hLəsQ:U$TR||ۣ ?+]+͋rܱOw0(BːJO*L) R&@}(M.!J|<=af_h>Lr}6L]5npsT`Fr}Oi%̘|2MJA'՝GL`pr S|;,̤-HFf%A%5,vJ6IxJ 蚅Q]9H2N[jD[~HޛxI:RUSO  )Lt|%]B%ib*[_*R$jA1Kd.)$;NcϽ*PSc䆰eIR_ۍ8b&ə5U6$**LEE(L3@(ruʠȀ >Mh %*dڣq;,;HJU$zeJ= nf6_?ku__h<ה~q|μ:Ѡuj|EZPimi_\‹|_!~sϐ9K[VkGn^ѯ,:p9nO }T lp{rY7;@ iOjcDž1%DAԊ\ - ~0*V^Hxegv_f`i˕`1ʒ;\wKC:v;*\3fQEDS]"\4)kS=Dx2 SJ>}%Jf͙.` XRf,/l)ISSb{ZPI,̜* ؇9 iP0Y)R%!.Ȩԙf1R{K*R&10JW*V"Vp͖'O3&&Zւ3xJUU5#^N#6gh*ʊrD2,JKmV%Yb1s>ʄ.\IJO70_L.̬$]*sQ<X~wxy#˲e@`(\m1 % Mxi /ۘK1RHCPaج\s.|:S*@K!)˕;) ^PYL5"/(M0+lJ_i-`^%1W0hEK1\$a{X}_w|4@2 mf!T4B(b۵v=bW^ \òuX q; r2A3z*XRf⻴ aH܂]-չ#GɏJ(|h|Sl`D.i!)%)ՔuWD]9+CdĐYބ jm˸f 56H0Zջr}!ҬSQ}U^Tj4"HF ễ5Etb{G"ӏNcO/9~5_?޽k^njjjF}7iZB.k>_XvMП.u[ysf}C=5S{To߮;V?Џnn76 pPK!*\ىҜ`W_56ؾ)8yy}TRkhqLlvn)](3YDi@L#{HL^!Sd̛ZeE3  #7$L|"p*Jܭכ4ŔTM!d32LP fJ`[+dLWh9NڕRV6Ic1"`\1 )̫4K<'JPLTF_H$ԣPRpݛ8IDT}gBQ0[k:Ka[iqufKxKaȚWV;:6'JAN&gFO brLQ^}u %.]gj$B*Ve% ]'G7$ROL|0(fO>09cړ2y$-Vk\p&u HKDJWOM) MXrF!@dc)&WhǂcAO*u\/מM|V7z)*>FӤX `8kw7토g ۭԽ.57-{~w#_8VWu:F{ǿ1Z{>gߺ4kc,o6U }r$șRЍjw 74T*S3wiPse&) 989Rdb$s3xt@TG Sl OoJ{Gg#] FsJR# haSړ>F36tTDwIVDeBCB0󰝥&FJ.:[iK9VH /t WpAQ̔,T^~5 658ݥ)) aS"3#0oMNi&f dHQ)_¼Wj'1%JKh*% |jκ;218w&q3Ft#K@֌6>\e%9-\=KX-D)cw[8P ]:Y'0 i$d?{3 kwHmB2KJ{*#31:r=ejQl<>Y, S*r Ű9$K&c{TG„fٕ)?@5[! rHH/OB"fb=#,#.HbLً[eS=&K2ĥ,8|}+>.rl#2fJV%#etBfwHH51#7љT/V'%6֙D%4R%-Rԩk7Xo%?w%e{ݜO8.\ %JL2Yٳs\lB%~*HK[[;Ĥ/hCBl4Qe?l:IJ~zRWe_ɓ4~i&(Z2b"J3e^e1՞rTܲ0Č>ui)@dNbpQ&VRS7)H3 /}}[|Nt#&bdffwǗKu585c*7kx)qs׽! X-zԹBv~ ξ[ʟ^o_Xc|N4p3Tpw] W+--%Er+Rl=HZRHPW;$(͚͗DL")IVRXK£"JSSb MYyρHBL"YZeҡLAVUPc;;&eaʚrE/:[Tv(h*?ñ^<04H2eH/aS̶R%}*j;^v\ْeb846R) Z9Ft1Q!D$7qxRhԛh*~qpfXb,JBvˍA[R Br_b%IeYAq;q4 I/K甩jR&vj/SX~/%6L`S"aO1`zPo}<SbtӪKkX_nXqbtw?/Xת.+mLKyk\{ө |ks"zw:Vfr*s{?/mKx寯y{S^~|:k e\| [\ &X93X[>;3™8t Hne#2Ls"CD:Ĭ'dvaBF7,ʖIʇE[s@!hzKL)W,ٖwĥXL<3==.Dt,5n'#V>jj6.$JNS50))'X{Vt9#:K̯;rLs:Ή&^ZJ@@Bܦq2ʦʛ2RRm(n{Y4Ol@sU)]ғGre5}ibI=EESI+X?Y)Ԯ!2$}J3Xban<Ɍ4pVF5{A)+iԟ]$>eixIvmmpuF?=09S1XH3ҦەxE.GdaI`9I;=!;eK 2M<+ "_x}[0'sr*bssM- aRx_C]lm^/Bo(Jt"Aڡ# лp_B yeĤl&^"5f%)f+slzbx앛ɛٻ4)cx$Y̓F#$@ᄒ W0R*hRp@ś^;)Ad\̇vr ZZهӏ릴9W utQ#*][tS:`UO]th { OW((~i^)}պ,ۡ!uu>ZD>Aw*Գ~iHKYUjb:;F' `ū!QlمN~Åb+#/Ґ~J^iR{gę1S1xdJG]*m R@c?$`b4@͖:7W(`Ha:lwAD43JT|j׻A$TTUiS&4*@Yi}Ib61j3U%whQ)N9)*NnbΟ"F$(F!2a;AL)W= sӳpX.7,MR{DPDA7yXs.I*]DeuһPIhv了vуF\vt;\?vb|I) 7fO=̙3e?'59TJEJPVP+MN! ]2a]_xع?O]a ssFVM#ZoQP(rDx5>OMNq?N:t[??f%iO!jwaKܜX_/;~u;/`C͠E*VNSQ*ګJ it)3 ̉ cZNhE?8 6|Pb Sa~r9( y-*b\xsm'HQ^2FvIA͆_yXMGοQCSG飿X}k@ zۓ8+R9] RZQַ:?*MuhKW*&ӮPip=}7O_bixثk}w4vt$(v97?4]k eQp] N> 31߾+x3r)Vf#2M^V_h-ơ|J771 Ͳ?2ԟ+9s*&LWdRt,ꔂIm㰧9f-Uo2?1A 9fezzn7ZգG@-M@Ĵ|wӸ_K7k )ǔiˌ?~?-cǟˉ^unvFc_cZV@_hoR<kΕ}hIk}ZfP  WKi?Zǯ>(10QB.LLu(%ORX )j >P&rjPEQ1>9ka@𼁛2+CfA %-w#8f XD|S$II+nT;FɌDs1s2"lıD(30rwP{cv&{Ee9q8die 9I nav d@L-%.ڙCp#Tnm`Ě4&lH4Lʚ]Zt)M*B'L&p ֹnN օΕyRYYgTTcq8@%b0&NSLJ!)Jd )#vj':zՉ"rUaC QS6Хdvv9}*R\ɁYQRĽbl2SpV!rfJR##03^-/h˱V[f g' DræIDgβj:0n3brϘ\e"t-R՞ĵMC}?ݥn[G.|k@sG4-yp"֣{@9NҊ}S_;$T\S١#7UxT+h3#1ā40He>~=k? ɢw*b%g;L(m)Қ=|N,fY?)K6c/&nf%ʝ0kycTIK)9k*LF\ŗp)F2$DQkq立qKZ*Z*CWO a]n(ӐshvJ6ЍX U_Z C :ăfL?yT_7Vnϯ_-!n\T/ '6`$ڔw0dJ;D;,/Mtyki)噌K2TWPnz 'ړeRSUkz[Z-^ߴyw&ښ\k֐۾J[֌7h/h7 5f#FnmהuАzJxI5kޜ//M̍-u]wzX;gG 6)u+[whWDT0YrUYew96gx?̀%A]Ԡh MO(R`&$vOepP+JU2ar_MX@D i#Bҙ.RsjY/ a&Z=Ҳh l`mL+ 9)F1 y8j3L X`oQ]8YS&}jn&*HP3W\%k)5Ajt۝%+#\6bT:&-D*Q**$˦Z”Klr˰_e M>ՅRRrWĩ;,۟84QmUZ-Z[yߟxjs|>WtoncҐ1R'gv-]Zq1ZBB/S5hVreA?k9*zdũs $&8B%J/ːSiZ٩,FۥiHQslJ%wrӵ&p͟&V ZIj53NLV*R-KI, Z11_ͫ1]5HӕuXkկFM}蔟毑%BEusw-Wwvm/_ݠߧNm O[ܺ p~9ФnD̜,OD^MZ51X9 "IYHαg2<%3'rr*70ݑ##a|&.FDq; 6Le-2儭BnFE* LؚȘ_ Ϙ-1)"a)#5Oes3JiF32b& -%(+Й(8|n0)#'vrJįفnX\ b&a3ҐUjEMwAD]TAyF3:v&T/ NibZZ6KpGiZoY T̚rOm+#&Y7ē[#@14ccH??u%[[YBIU)l&jLM3dР4d6WDyJLNdp5Df>G3 6tbPІx&7mη۝#9@lt(oVonQZpN{ m9v~DQ c)ug5 qCO&D=h{.Zd's?tk!nk38{ڜ`USPbHX.YT6F* eja-ZW)'0U⽣p> ^ ?$nk|~PMI^ԋlݪ3K9O+=Z{ni 뻯s&bӗ>^tf'¨)I9?QBjjC@L*b%!]Ny-H͕ 9.YBYK3-Vyr)Anm>4"nɒ kaykHp'b~͊d),CQ|r6f6?xCsPÒ$v lķ̉Jr,h|D<מb0eBS[xJA# YxLv/ <%O3śp@%_u-FJfr0 P9a'͚F@Mg⦃ Q 9+K qL??t8Ir0 KXxG{KϭRBi=DbmRU' |Wޘ)A+%fU%4mIm9YM_(ݤ1Mj|™cFHrvdWZxHYfa*qRw5֮>ƚ}#73RǗ~MCCW8lI.АShӁtErԅ8`<@W3'2 oAvd-YDhvQиRڱtQ(Nf Ri_KʐK? qŢ~7R+@;Գo~78癈R( 7'D5; ]t29rjd\%ԭ2 J*6R@%Y,\5EVJ8=8 *&*&LPؗN&Yy\ 8F8%{~~TϪwӆꇜpp.i; }(oX8o]h@>k*tkjwP(=-v~|~YT??N>=~P[M(R1)sjo4x9s Qf[ucB{6b/g}JDAv J#_#/ %91 @K}⎉N|)9H ٳfB݈Pf-^_£Cb!30ݬ iv"l;Nd@!RCoVPCO{O*<҃3.C9g ,6h%b*L9U6^.P͜ʍA\Z@ڧ/hHeP9)UwSZ'LIQY(@<34cф1SW9s$' ,Oү3 9 S.!3;$x\0Xb( o4a۽vm=],AZ27R7ixTi;$Ȇ0e%HsqrM%L6i$mm!WZ1^XIbи|nI~6#k[J8 |# \c:Ù(JYU%-R#VU}!j K)Дϙu}2ŸL)}CZ*WgKI+5%kSڇg~Ýv< FS-ڽWQq@c=(R*څ7>M)]Q"NI R P¼@{+ڇĺRK)Nf%DY$&cL(mHQwyZ^"-i^nwzH H~J)~\oH^O O7Z@}n=ӒaϏWmjwkLW5Gr"S;Mxn$yt`uBח-kJU[x!yujFa;% F&rh$ޱzTMY* ^F!j^7չe)BR P% Ϙu}ݳqxX+Xu2Bdm9`\ix2J&vWpsm$uQ H/Ju8XГ9^Zúi2G^ Q3ʘ[fJ(o*!@>ܲ[R^)qJ sM*7SNC~Q]SQɺSK[)odsgoJyda }]z^yhR}9FR8dчæ%Y{٪M6eLLfRՙѸ#>e+j:PJ3cq,ԣTqG$r&joE'1S;[I$aJH`\SVښQTՔLHj aG=Vv Xص.nNbJkN uNcQcNb.梪XIv\]QiT5.@nbVEU4pV%uC_;Mh2t~UHg; $Qq w7\Lb$a6^"N42hΤvMј]jI:L:1 mwɘTr˜ Oұ&t,rG(3So.Z 3|D_sGx)XT^C*wlF*MB}WҼ?&?sG|s{릋򵛮{ok@g;+V4ԟf VyYU=]H>?\ukSwo7 ga&\Q% G fj-Fδ׮09~ǔ?/m?HWׂw-Fƛ[GxF%'()!XNRKKJRDaD 2Gr,ҠUhxJgIɛIQēpͺ$ B`%"RP-4JM+}LwMB3.dGj[h1cYfaB-3\eD=&WI! BLvagJ*ҤRR69ݝ;:2K :JlМ +?ڒ,%*VY3Hu) ydzHrf)B<2ZD˴|+8,O_FYRf-h̀BSZ[(4T/ez~t)z[q;$56xV\6G"(4  zRT_ Y} S4r/}YH&M\4? $xs@"֍Zåi" 7\_x63k{jz}˞-RMRf (GF-Ls8Zs7QGtf(-A7;3(Nf`/7O _$~G.|fʰN}dU=2Jʴiv 3LžB7+TGWx [1$@thĨ$< >뽠]Sز­ARt=yvR{x.NnZJHHJJӧP *!ȲC֑4* ŸAɈZ)Q/˄$T(֣CrCx!檃2|g}ڶ ŦҾQF, RAח+6bҢrS5fɔҐɦ[_(Uڠ8tj:3)3dLTP;$kel&P?&jJSU\ΛxYdLfH"5Uщs''H x.^ )˒oD vb77>v+ξ͜Ydfݙ3DTLye02Vߘ1c.԰Đ@u5nvVdfr2djhYur=^IC39CGKhP S1ۧE ,^j7*I4 @!*fmh4֚v4D7^V￉jztbi=ta :TE&b|vN0%#Q5GVۡs'`Ӂ/eK ]!m-7)7gYR@u!F̬fKR[̛?ˡ6K(aGd˷3JȠ.b,w%Jb>Dk9(GvnzBRQIͲQ8f`PP;qy~|U{7;?T0}. 9=tiӳ3&|JA (JkČZmm7ySAT&w5N%lË^M[u&Zh( nW[{Bo )A lAvK"Sί%Ҭ4u%%S궉*.x_+>Ԕ T8pvs%aN$4gCHJ6i,ҥ6Pe7gLԥ#)l`2$,/{ L3b ¶JL̗|j3|Q2^jMëIJ&OJ*Ra1 +?S(rLSSjhpRdbT(l*ŶLN2S)-71GܔΗ~N;& h{=) rLȕԔkQ(ڽb$.jB$JBc0ɪW5/1r3滸v&Uδ \eQ{n}aOhlN<3WMr(M]:%YePll;:5,2Fj J($E(/[E)5ujE}kbF9)iǭ+^G+׉'|y#פ[Ua,hERgDK LHROx?TwS2wg2ER ⶴ#Zq=) J$(="cs$wPI~1` [?=qrž>!yߋRZfѵu>a/,ozԷnn_н9w6kW4?•kjVڔ(Irs*j_*o㽣 ~(/gg90` 2]‚҄JG[}{F~B4~f;MKXZGǏɸ<(槿gtaOE >R_(K~u0?iڤg*5;K q+s˕ã95>dH)F?frT1MCcf)$PMm\DVkë2.6$aӏI2Z+"|BV^$%y2xVAH^cD{x.pJļ2Q%JkJ-]qa<_wVVS)+|ʭC6gV_C}u)!¸ee%Yeʚre|Xkf "1ԌJ ʔD#BI-B{ bY,(V! OeB?f+XrU}8ENl<38ԥ jTjGhrOc7X3eV cf[c嶯LӤP7@*X}HCrͺ(jDVHVWJ[V 9TȂ(͔ns{y[@{˺I'MkeKbGja}-!y>:uӅ(2eʶ fYzܹ|;5 (+X@j->ޔn믔z@xWf oKMV:2dphR.+sHK דpZAx +/mV~ ?hVc#z_{ !KJV!  _ϗ0˘K%n7!ӕ11TLFV~lS+ްPuh\b N|^hHBsE`gt[ߩ;Z\o'}#w[A}}I19&|9% R|ӿ|f7n`13VeΒ+s,hY7ňI8\:DB@QW+RPAI4Vs0B$ t-Mr j(5l!hl9Ni ҝ)mxDKGe-]b_m|"r3WyΤV(k0SWbW1#ZB|PʹGi.qjYcEtjU'J2HbS4fA2iyeswh\a\ݧXސjd!A7xHRSD[HӔT-Dp1iMʵI8\) l!t x{~*R$>ywH!@FSd qUl`JXu D !JT^[?=D!*VT(RɈ@(kVw`%.M]K>r=:w_|u0ֱw& UPR+Wc!/fYbGM,3LÞ FUg l+Քl%=H4 ԇL r*jPOQJvԥz{56 ϲ7kw{h ݩ+K84Ƞ]CRlLM-9 ,0XԼIKߧ_X,۷o(Ȋ_ʼ2lfᰝV^*Ze'(J^tcæ45+o;-KJh& hhƀ*t]B$VJT\S?sTҥk.էarIl)˫9F8!(#2;VWjIp5w'Q#*Ù=/`k#AWg@,LTriyLew$PmwV}^>o }{ÿn+ ^,ÝX h".b*}StɊ,]|rT!J U^oGh}ӧ Vʦ7~E ?*յfkQ7?.͢Co| f?0]݉UIe?|h^s$Ly W1 J{JQ?̟Śbӷ=J^b[s/69{¨(!S )Q%3"N NDɩ*R1rY]3MBT4*b82mb1 %KIjX/Bii ?qS T)f!9ľy=%M*J1~j? R ("'<JHJ&,&>W;#-yr`Prei񝗆{=i1H&J'*t?g^e ŀCv?%bgJ eJ)AFfLb%&J"L]Z(ХO '  \52t$6v'3xؓrzCLe oo/V,#- PE9|5Ap¼Yh|OZ/G]_w?u_ֱ_>/huxɏ.]8]zE!-f/|4B^uyAIc7hxa%4ٜ=_XF'qa P!~RSHq&ItUs̿W!~/3~zS_۪nE+/gg#PwFCzHHaRs ^&INʔ߂ ߾. ^S сKR dfj"_Int`rtD.1(3^"Rvb>ڢKؚK~1jĤQxHFMkV%sA Pugg}^ϕfiMya^6Nb kRKo×k.d*\nv#$RCW2kq`bT#$(+h}Т}TU'v~wN @Pmn.*vR)JZ Ov?ʔGR#!%;8iRsVl@s>WfKysa8l10YRKK*FL/D(LDRx2kݭiI JUݯ1 1lb0=vqJ6Хc%.e$Vՙ9]'%ҊUrQ[2VL̝ sj(REVi(Ι&xZTf'2q؂"IoIinԠJr}m*F&-*~8bt̘Ŝ(ƞ* Pgrv͟V;!_˧~O:uÑiw{N??x y48~oن !O`^ ]Ą' = hy΍TsH?8n4o-SBuFvtQ۟>KBlׯX~&V w4XsovWu1p$XRV6]?}x$ _Za,6`S6WSxg0ȟ Aq#P.3\]}jd[?-Nnn#Х9Qm*ZP˚Txwf:(%IJz5#{lL\Y;%a?!&0slrn_֏6{fAٖx^‡ve+ ?4II3dM ҆*\K-zd %C2˪ɵ[ʯ/ jN*Q3SU &^)s%M9M w&/7)]d^*FzO/Nр7qd_$ȘVeE1}AXTS3`gY.T)̕œZ}qV? JJD埒dd(1IV#:v2i9VP_f/ ΝnS Gj |*&):B@+vc_aK_rRij6~}1+e#Fu.T$yƜ+aw-< O߄ U#wFW6^9]pq}Z^~q~йkf!\-ĢSi R ݐqF0nl=QU'S@)?ziqt4U0NWz{~>BġI+ 5%$zXw-MՃ9TCSZc\г2L.8WJoH5.4zZ27uJcNb:~* ^A D!AN/XS~aԤ+R5'gkX ҁtB5ț>kBrMVf4)DQ#deI^A%uT픜\n@3hjhF6y#6T ArjdLQMɓ=eC6e%Du;D+Ii`3Rym7-uLJ3q:^NDVR?в~IRS5c,ɒRǻ({Ĕ^6Y\zr(80'*paReJ%ey"lb>L+˟Ȗӝ]p ̜)ir >͊JwNegKDWOⴙ\=HJIr],U\:(v 0 @KE(xy*4ڠҁkÝj ١2^P| oD׏^)%FZZZf1 &fûc{ c? | u}xs.|utVoH <׭yPuξz=-_vO4%rJfSs.ml~.w =@EU6צ/H1t4s|) tiiġĵA9/ECp?:4A "hϚY1 P/*4wR0 S6`ev:[/IϣJiGHάl"vRsNRheҕ4dʆT%#lb5{ Gg]$ ;[NҵfD٨+m+9K-4󉠽;Ԇfɔ$l|?a_G'U3H-G4ʴ"k̉E"l]5 ; r*e9HTR2IZQw7{&P!Tthtwݠ'{N"T(FTNwFY*rHAoֆg%%ޞ> vMZjFarfQͷ@/ᶔW\f}[C}Դ+4%qI𢹔LeRG!)Wc=o}X~ _-?hop8MC 6nx {~ 0c^axn}Xb^%+L5w8kWrM+AV1ą,3SgS_ pT}yqQqNjʌ}sx+T, /.Inbds,?T&(R]7b=p1twkWnhtbpe 诔b0RH(:k c]ֶ ڼwwʹZw"I\lA4[&aMyr$ߋŸix囹JZPբ /W)򱡩jɉ͛nqe9ҰRdV]k'^ґaι8.X-C|Ń9ީ'V)}_^<7BeU}jFPo#26hi79GQ m˥Є~b]yt~W$ْ*f )KB3dBxBՔ7 > Le&_ц1hpδFC6$=`ٟX(Wa%"a#ZҔB[*;FV)jQM} QɜF$$we^U|5azq  h)B'%_[D'0x' r-h'3+wy8e!1M^ؘϛmYAO+1*1e(p%by 3*k: ]=څN`C vt%R^Nh13v%J2e_l:B`GdܘohH$♗J&l*阾 e(Xp寳0^yL#2*N\Хx0lLPʫe} 0P'> Y,^ 9J=@TjƐ PӍj|4aM#]J q"c<_XO>= ݀~Щ(CV%kȏ鉸VWSTprTwaJ^UQvo 3(x)0Vqr*}tNKH{GLz-3 0#)rB5*HN;fU^|8q1 ^XҢc5ih&b5 yA%~xsB89iFZQgQlH21xe&\CNiyN}+R*+rKݩh]$MR0s˔|˒:@ $cN|}kv\ŷ )Ckj\3R%JT| Vl_w]3+@JZ:;2YYz^0pM}῞ݮԂJt۵`èns6NP_>@zq#ɺ>tZ@~]Vq$})ϯHZ[=xǤc2R8 Z )K_j4}ueͦQ_ uvQ^Vk{=7,UznzYD!O0l FhݼF`N~:v~{IuTcNUSxqѼڻ:_kJ y7e3$k¯%!1AQaq?!.LA^M:}L=G^76A[8oo~ۻ#u~,xD!TʦmK YQJ5Cud뷞TwF* TʡG!SC@7qXG h9;+J'F8C%O*ѺVdQ/ϳ>e1|kӿ >ξJ]?tzYo7/ώ 9ڤO_ɯZuc1%(9Έ~{c~{ L]?\^7Yh+Q/r=珳AMBn6NH:Y5(05NzwYG\Q>^'5\2=*YO{ =o{5o]Fe,,s@Pmc9K*#/s9l a3;3yx&5}Տ8^X*Pc< Z]7@#F`z. jك,!&t`zb5$s `L=eWX|qkDƏN(+ݒĆd4xҊT ]S682:M*ùjv`ࡍހVַTǗ_| Sw BiA~ト/ﭞKu(^?;&q(Lc nZRN!څKZ^i'E/Bb8m6+ 쟲o~Dzӥ6N O\,Gk22^#^(迉{,> $O4G^Eh][:-5uP红ZsA #fycg.BCQ4}#IeKJ(@NHz"Y0+ nm#]FO[qN:6M;r p%V ~#+ ,?Okk- uΦL#RwY3~e?&uǍN0:J1ڏe8]c1>/y+ײtT޺Y hjk(:'bt uՓ^v0xC%><'[D'a>fL|1[$|?9G\"MNz^wnG8n; ~eSQl#1%:)FhCB/+5>f2NUGWb_L (FSj =aS]R{BFK +`Ӛ>Z4vЍWZߟJ56Aˡz?}''0KV6靈ArS,\q+|?Gi Yz XpI;NyYXuSAB ~=A~_=p;WAp-ÊIڳ:g;7c oYj5윆4 Z`qx$Lq9n*9I^_A^#Z%q pBj+(FVOH>_-Ac^³w>4}Ѿ]Isl4y)!&v<'v-:g7HjzhiZwoH0S@kHk*/)FG]]hqsԁk )X+?"#t|, Qe8hj^_;t9EGB#qX4Wtv4<6gX8+6"& mJ!Ǡ㚂 Bէou텗-Yv,Q bĤuH*NHp6aomUҁQF*v6Qg(Q5BѤJag wAB&'fIaxE_ϳɬ?Az1A,KP!O$cˡX鈞ۤ&u mz{H'@I؊ GvRr" >^ cAд5۝8ae'zå O*K2Up] /[>O!t=<;kg!:BNk>iS0ܣ/g%PS>LuADSa87 kM}B=p t}@ 9?P4**WԿ۪S! K!.@CK@.<{A ѳеIě:J!Act( &ֲ7իGF;np:$ӷs.7SnTk?Gڈ;Ls:<Һ Z1'P.+ZaR{]>.6 Thyʸ謢x*\R?I Ub]BtNGL2\v>ЈohIq ^;-Mݠ:W6T lm;9U(]Ƃcxzҟ*AjZd-#HG`|J U.G @3 SEɤp, y vٖxVN `eff 6=bh-!*??hj[22nzMxf( .9t8TO>Ӌr^ϵZjEzg:efCO8j)B;H(=j4HDB &Jm^u~` ;^Vr&! ~7I|4ďG70c{uez(P6, <*yo &m:-?EА_:'X|pV*ϥk9O|%ۅ|`,DOcSDr!vh_ O䜫CvTvќޙT[T/`a-`0G Œe7KNlcU4VUod@GI1zI8T2,3&!&)^\Bk7 bT@D,ɦ_*:c ay!uwLxs@&8>mm}+)=VN#*vb)& >g^c~#W0-/Hv>X/ĩ\S8=ϰ}CUrgwӴZl8*HAY_@إoyBwVG@WhA.@n>@%'e<@?TvԤ<=H oX;徘ڝӧTeꎸLdaS=vtrLr%C]@;& k^S W/B:qJ =!k `DRaPpi^*WJ-[3f@t:I[h 8uN ͬ1O!N:>Qpn @Q'BAzD~އa&Q/}܏;O/ESxE`t RS F 'AtaWO@`l19SQbf2:-qQh@;b.p  -A z"8h" ~43'-q*4 5GMC [/ !q'WXALtazN!~#Ӭȴ;/C菁qf!BMA1&5CxDBƞXUZ@}p66:vN (NBۈCLV}U<LN! Bthm`7@W>m4<%;>G0;t1@.!]RCñ^o]$;U /DPq{@dDTWC XDe|uԍ-mA:"LdPbE]uMDd05M@voTH@U{p@uvr<V@ XsUgȅ&!%=5!#拓=tHϙ]hG'CQnPXFs5C`h!I>޿bX?scF=F:6԰i*UUФ7Fvl;3W=XhW1'hMu-?$ACqV) ڝt0T Ԕ@H;'GUZ&2Tۇ^t.85:HNl>E'DO" ȷ(ŅmkuSOm/ ,Gdԩ$AS&{D)TUUr! v}0SCUMv4];dT_GBx3~~  œ2WDuv qOb.e!U9ң/D?CVFO:vr1igBu B4ZSc=L!FぁƏ4[AAì_Oi %gjm"7 $kB }NhUڰȘp!8j$<+"3 Z( :t @/NMzuqm&Jb)k("3+h'&))w% #)#KL) =hkC @-%:@TUӼ C HƎ=%x&7@p3 ~:~;vQM@q _D6k-Sm(t6zeϜ+8Сh["doQԑ^^ݣնt} ^sU&f‹}̣0:m{©! :6q/IDҺNA~GT']0AkrB=d3+'pLNF3*h@Z~~%=l]{8!޽z`QOMD|VpaZԎX2')ؐފaB&/d )_s D|DD-oq*`Ժt0N΁~`z" 4JL;MBUBO,_y(Ȉh ɲDc0Hv;hI/X8H; qi.krGlBfh5u4%REpyT.YPF H%n%҂0zNEj7cFL ^A0ST/T`2^ʇ贝9-#vM uS jt@0~^4 f lid> {I.ok[ā\, P#JqG> F8o#ȝK6?wv{'H޿es/ U,:A%"+ PxuL‡(H;x5F[=⦊K?V(^&i`F)@8X}0gojvLV  ~6HUox o{6G@GStDޘ<O$J"IpJͱMMրϪzF-Y'Aq-￁$ PcmGOLG>;KdjD;Ƒ 8-)_PQO1JjF t/ @$ `%KtDga %~]A5;;@XÖG30 j]'$Ao1Y>8@Dh:{̠Wb6D>+3[V#\ӸEꄄ1r8ӑ#[}BB]/-ab>@J pzdx.+|wE%7S$WamS} GvQ TB3S[Z;yQ2#s+ $oJNs\4YokdoRyި8%(h*Ju +oeLB)'/9(@zKE桔ez/3L!&61:c 9&YTF{eCos)xbF皹A#iL]5_x'h&z? \;'^;T×Iף/\.kx }üާvڻMI cYܝ X?@Î(DZI2FUԴ`[T0ۮj)׸~s +8@a^AKƬ b{UONV4vȊ"M XyޱEOtu$ |pxQ}^Hо9x&6E*cqɨgJHfxBJw u -R?F#֎Gj%;vEv4'+"&p;>,Oh*+i] PC;pW83dtH45z!P>{j4xŎWD+8g,`4hj[JiȗRAv/#fFT<bBP#\c?:`eJT-:y^>=uɽ`/+(s5G9ԡ7Ap@.zQ&o>$, %oӘ?qmޝFp舯契ƉK]|$>nJ%W'p:M2Ì ~Ls% r g[:7BL`#$(WL7a4ĵ+ܫG46]^w0BGZ65p-(`$ G핀Unj LL~N7} ~9m;O =i6;'7u? 7 k';QP+6'df+V 蝗cN"~$y;̨:T0oCR??z wU&hNQGS1M g-:N7m%v ZˊKtwAhZtB066(XvmGo` yEFR $L 2"E{4#q%3`3n2],r%mI?AYssdDPXi2/H5QCQJD8/K"N; K@ TC® FzNb"WC38\VQ$ aXW䈯J"=4}✖7)눮CmözD&_{ G|rx):({AE>콏F0ϟ:xKOxDz'Cg`6·rB?S]:~]g]C$}'SfSNH-vb"7N,ZGF& ,,:y\z;6Wn+}_H4F7_M9q?. BC>Jfʉ+h3;(D%Hа.FtXa[UǀM-U|݈g=νlq:.[Fao~N c>.\q^vYu:7TzIWѲڑP,%=rT&rjjyp{jI>Er KM ;CϘ!"t,t<à8$x`Qaœ@u¿C+v2WY:Uذ{`aUZ,ļLj([%藆"ƅ:Hk %Cy? @=_8!Tex3ͪZd J.)U 'HČt&!|A%;.q_#hd)GU@6"m<"I6&2mޚ֚"!pHN :docxފӧ1 ԅˡl()zB󬛿ޛtxٝl:i5r؜lt29'9ΟMz|˂xJ'j|,zz8ЛtT~R9Si?< ~%-χ?wad.ЁD*8[Q;'I8餄Xd?p/Z0>ag)I O7=OG=.hAƜ ߴB!R%)޶ TƗB) xoF1ǡkZ7] ש1¶G&r :.( 1]7$XyDl+ p vXRC{:Eqz Y+ K1Or38K6L.j{4)=%P1!N$4Av.m*&fg3$3Ez9k'X#_gA ͩi `P#V ' ^ELț/nq H Pv_ hjuX~~NŒSO4zp H{ =t]@^@/UF >2mo{1*`p7^N)7Ėu=a\iNնw S΀K4exS^$&qp;AN8D=:A,xH: QA7P/V$5iO;6Cy`t&$dB:"w#_yӅ8adD s(錜 SrK$p.0^ADLړsp.$c31aXLBÿd5T1Žy3Mq*sv S;Q CrA: ۼP넓 SK,cwd `HNIn- dtNWAB0a\csACt5SJ 8(SքvЗX B1dTzz 'ZE 5DwJi*D҉j mJ_!-QWrHQ lJZM*HheQ@҉r5fVl[TL΄`s UAxwgO֓:E]<{}!JPf`5=)P"& 353<5p?Tvz>_<~B"N(tvPѭ7\9hRK{R^cC`t%gUI~QҨL6:} !ߥwc̘침CPS4.rն"/aF? ҧ"C;Y!8<_v"H)?n^:/t;1BD(6*ºՓF"VOӪZG0i(^.+Lhqy(ۚp[P \&8 #Ngx'atT'+b~)"(Ox$xFU: D*`mkW`*^Ș~lEse$,9Q!@ 轆>[I +@+nEB|9$'w[Ǻ uWO `3\/\291hX=*9Vy )7d`IDe:@=dGPޑ$B6+zcDA:@ !qkSYh=hxh^I5!Us8[滓-zvw|y$UQ>@zQ Kiђ}5VQM,:P7a юgb`ϣ*QFή s@ӵS^4#`%q]=ht.xzfu8DSU람!oiw:pB/6o0E˰mD1eS!T¤OZ^AE ,9MBvZ!mlJ];YU;bh]3 .>x=1GTb.v)ƀäjh6}"d;8%wZXXYb2,^Qzc`u"݇n1 H PM>ǖHɼA^DQ 8"}GI{ 0x Wд+C@/e:BUy 8.%c/¨V+MU\3_3s /aN9B &ZeR93C=aZOȶyU퍗 u pI8Ra#+"𽓄MҶ6 ‡_mvS*9Â]ʕٜg@ɸjvm77.;נH{ %, RkQ߇χIkdvZ4Nw}/~l(ן:ǫҍaFZt%Js>$F-]}1k10jbh^H)$K0 jRj0{RQژw!RI8ZlMK-`f']k7RBѪv|o-m޺Kzt?K f$4 VBhq txu&Aѩw"殊-.l7F=<UZQ dSyK\l vdJ䠲ҨGOH"ҾSR*ZGu[d+k. VA Sf׆5Bi:䴯'BU 9 (UHOҝ:漘йc`؍HmL}F@Jq%q-,>Pvo'~m]m0\8N$([CH=k\A~8:= M#EV{C"r0QebCA4܆ [8Cl +a3#Ш#Qt5SttE9j0h`7y%r3߂:`/_Ga ĸ/ Fo\Sv'AA4SŦu~q\2?:\ "!&]?$EP@HD1a!dݕ,I=) 5OE28[V*Z+0:O C7W0(lGs+& ^u`bE`?7<|klL7;M]biw3Ã1=G~xES{;<Mav TTaVN7G2Or={OMnnO7K# ڭt4v9(k4hv$ÆB@vDMe0; jQi UJ*x # \p ;2-@ @AK R_?-`{ϴ@wa֪ <)Ni}2# ܊,(cz;&7ilg%$"P3CXa[†4ϒQNc^4Uh4;j Nsl!BK$ae8籈kc&;.~܇*LjT/o7GÏB^֌1俁8Boz>^Jv^>F14<: 0G$!W>ڐV Ԕ*d?_{~zw@ĮV@y=ow.!,]uq.XP;Kh`xP֥z] @>5S?2X0NZV x˥KacKV- S0~ 270eQp 'YVN-CSIAF1П3ҽ,0TP/E ђ?B@ F8K!-fWf`FѢ'$SU5%p>RoԌ8IwmJH(൉t/ˆo,җY4`Jܒι$: L9:<*]$=0:Y*9b$#[b8 { .:ȽXw$3KԐXXCMB} `l١`u0f i9-@Em-߉~jQ|X ^{А%GW_FL_ /L ĥu!GHdG0!oP%٠VtTtbg &?>7I5^0Q8ve+&5 M{r|OL~ډ~R^A;"JIEL`i }pJ]2J/)-RDHzJ)at t/-O !OT29˭/·IakB ~w&<=hpk0@BAAzVoF$ PR}FvRoD3%N88^()Dm>(XR[i|@e߉€IXR.k1P`5l_ ?;.\`_WlmP |^HdgqS^WPv=x#XNǯxPGwX=jGMBli1_hNZ"&[3t^ $jk+*gSgZeӤh5xώQ:c؀w'KNAcaKzI4Zk85;&+K!` QeaFZvR< eI(5jR hHhonJ#)ZHw@ bpaǷWqr ز!@ic$Qn_@ F$CXw]kNC:ťSGg9gkeGT|nIb`(t ;EFG-qujغ) GUgHyM [M4Q@u/ ߸K$_:L+dJm\DIVH@db̕b-[]{zE֪g8t}THF ~&Jh_-b<(pTN+,o=C< : xjB EQ4bbH&, ReX<I3=T1a/ D+J\ |n쥡Bpg?u<>Q)EgN4tώdpE??ڰZ>#t2g]^aڃgAIa)FzEV KQs:Ea yp`Okajᕮ*q@_^OU膴fZj $a t j#ف;:TLV:0t &p/3&Y5t^t 3C@'؅w@2)gF ~v% 80L5n- C@b {hc?F A^<8,Q4;Ge 6X̃v> i?}Y x|d3E7Kbʰ*8&[x Ie_P8CjmcM >Gc9%O6/}Ku;tnEQZ,RC@UMUnjUeC'P,S)aR;ֱ(uAbC2x %F#ò6궉iŀAفū֩N*|a{Ȏav/}-bptIpz8Cio'( zZm+{ oC/B9=1~WrA 4iFh yR\`'aEmƍúq#A lvs:'%,oq2~850 I^W4'ע0~pt?`,T%3D}}8}Γzv$vuKuc$QUq =.9m j<}i:\sR+|boIMۗC;P81c zɄ<1G?'yύ#L||Xh7U) aN TAא647  0A! O@G} ]?@ 9wRP u;Ѣ'p?Ho[~T2$87G=еVhm)rab`OI&i%㹦hW1Y >XG_bE0Cc!K~X]RSS4`"ᳫwN.%p#2 ExBbT=^ Z}v m؁{@5 (`ўZ咞- g/wШ~LӀ,R6MKKh$a_D;2༷@0݈~_<#y=~]s x|j9L<~f{DQ;d+k dsᅤ3B [ ]( Z FlY%z C7#޺FRiÍ;b5HD}2.?V'}I :?OѠa&'7׳B=Af c.OFL1#*QH#*UO/܁;IVr~ 2NOg2wd`;++Y ;zU>FEa( ,>à'N:jCFVŁ Ai]N*Q EA: vu{~BӢֺc'5NRh냫AB$gҙ^?NM JEĺ~:Jt"nE$,KF|$1t:v?T dX4m€*l34`NBF`Rb@}YvS GV)!_XBcfNk#cf]MMcdb-zǺ 's٪bQ<#C֔y6IҧϨ,9%p=TT|Ҧ Pѧ#ht iBg,- Q`Eb ՘-#Ah.x`4T_"3[I'9K^.aHbk@"T'M ɟKs*Q8'@w{0(DVʎ7bEЊ!XmwbGRMzM*l2=Yh+YLF :B f#SΜΔAk~ PG_׼s 6(u-+x݅$z윺RMX='l&"rVƝX1u{ʺ"IziJ9D7@^ aA#I;^d K3Jfj̏sCόWGg!HHAs[8&l( dcɕ Od0$khɠt@6q;xp+Iw`& rӧa+_ȰJE3 3t":A_pbuZLv_S:eאdy ;fqp(ܥ"RCHӝuYƙzn :;/;1| )دfaڃ=<(^`:UXǕ>JI2JX2%nj>Oq bMFTtwi`O6fd:-U fzpSi wfHȂ-vBװypou TEqH[ohlSz"ѱ2;P_xKC|vOm*X!5ӽTiE%Hy׿o, u}=np `_b~ށ =v|K2?WWrσ`cy=sg:Uk)XY:=| `3e셭+ q$^\cñ_$?K# ́'33]^Y*L`?*AJWxU hd~ DV)#` SW9*gӤ:H}VusdKBDzbNx*p! P@h/s=%5 0C"f+qO(ԃ|u/SWz Mi`37Jl5U-,3NBv#xz0}tP.: @ (pz"^ZQ i(bù~a0p԰i1>¹8+=-H:ohAe'Bc:W{t}7E˩TyLf׈#(n(g;k1=X]_)cO|Xn? 7??נj靽t0Η ϼ]iҺ^8ƕ}ZRއ\HNm(紦q_T1 ~dt-,@T098~ #\7tBkxz#EMډ8,%6yaЭ0 MZNb/xvj¥ET2ؼ<3.**. .'wBP|9.p7Nz\a#l&#3D":Dx4@4t[x& `}D "pmlAY q!Sdϡ6ԽorHh4`?@g /\.rrNtRvL^;Byd?1G~b%JZozcǠ2@σ0j1{|; y C& h݇*yfژG(I 5* nY WM@jU/A2(bP鱈@5=K5E)Ӎ$C6g%7o G{DыQrJ R\-ۖLU޻6}f> fqs:&K<0#) OG_ҽwY>M_36e'[N(48vGHmLwkۥjQK4$eB<9w.>3-5uF',~BgG_gEFr;v B)*)tȭuq^* !7g@tPXCF  W; &2@1B9겛Ҋ:ۏftί@FQQ*꘵b<ġ$D4ujkH_W$ 0pBhzI'5e:MW,.T_]nk:z pr 1Yt3~7IginY(vf ?l5@>"jM c29(aZq6aCz: AWqN3{AZ9jZ~źnh=vP&چ-DB%`A8 ;|e'Ќ{/ Ur?R I˴L|ȅ90Tcc?KtBA>t<=FE&), pnl~L,f485.v~lǢL?ZTbiuPA/g ŜA = ^%0ŝ=[6= uMbVO`|KbU3` FXL=m^3RRlJ_k#c+`@~ %2{ou>ޅy=E)>eZwqb=]🢹>Y3ǗNVMrUT?qkE+ s@`` jmKfi>w>`(@?:3ǟ{_f!?rFQjlGb4PE^05wBwV@޿_H4OEW tz[Ժt ?:`@R"7 g杨^s]{`= ptV'e͋}ąMt=QsASֈ !"(>> `V(?qa ̊1eF>9,YTUp0$ISGP0~wsNLs);sZS`@ PE`+"GW<5jPШC'f*tVaLu(t!k=m]G*aNk0$2W&kQ~j'tPc>_^=BA& S21[;㰂aUJ@-"XX_v,NP;"gln($,@Tyѵi"B/K ":[DL~ !h:OOww緀Ω~(G@ @p$^&j:&!+'ĩYLr!1ԥsUt{iAcz]1r8 KAbmwB`Z}Bip"B}n};gK\liJdL5^ f0~JZȱ0 5@bkDk t?.0CC`}҂ |E單!ZD Ch-A3gDZoՎ Id!O"Aٻo{@ uJƇ$/tiÌ#'oNZ+X{0A*0d *DV{g`?(`L&݁bEy@+=,f e*{vP.>MH@-HQR! g Xf>ԫZE:5r NVZh)aADJzWTCbu_!XpZ1"@:/ר  0}XcJaO#< #>tPƆ/zćh%5՞ q9/:]XW=G{c~a`֐06NÀ]mor;:w{ wڡV?'X}D#Qˏ;ӤPq ОSA6dbꏧ#q~:ڳZ GM!mDl Z8Lo^=~OOn" 1#Ƅ.F} ';) C.I%Q0K,Cj;r#GE1v5àpBcg'l1wPo`P3qy3\7wP- b"Ӿ6S^0R'& {QWIcD}fQů>e}V;Np‡e:ؗ9%PI; Зk; tþ7hNV˱(LV==̼@p@Tڴ9iT1y,ײ![؄bx‚MqJJMH)EUh/~C+u9[*條Jr=4LqJca!30d\Jj7a`F6d@NT"E P6yvAOS560)UnI'G^!:tiL647D4`j<,P @W,x U!(TF2jxFgX+"j>L WPA#NΠ:09HW*HGdw}[#˓,~&/j"Q?vGQD$"CIA8C:X[#E+'NpE3 [? +Κ\XKрUbǬJ^D5[c4Z;!И +ۆYBlEdx$'/?p>_&.}G>\?ͳ@p;[/Y3{L)9}]/C@Ue>.0Z#6 Ǝ /OGE#c+B(/;:zGLPAkDDЂ&T5jF cH6eS{ޘ8 WdDF8Md@:.!ƕg4v$XPbGh]4tqO`.@' mz1&BJv@@x@NA]ٲ |= iL~VPIBI &$KBF6fEC^ xU CJ`;ӣcW=Z@+}=zNI(BzyXg[[@0&G=9 ҅,FҰN8 ;#<\ΙKT4$թX ׸g$nK$bD"HGoNN--B50J$$s})p3ibim4( h4{ҺIB wkF~6Ή`.sC0IL B$,^ ("%gI&KJIS\sjW3£kqI`el',A`QSgC4NhhꋤuiYiY9: 7StQP5PO~3j#{##ݒ^ MJM0F+j~@ici)W 0}1󺃨8-&(!+.: lP4TYҎfN@å D$  +\ `Nf8\`MNzL*{ ƀ `aDwz68'{v׻x^g*\"ًf5g_8AJ!Yn \~{)A #F޷ ]^*8Z&WfpZ'euA92W8O޳_<|?J5nG~wûYꮃeںsb#B(:6os;L I* :8@p[PN2B J'\M]:ߨ}*^{&70M-z(RK`Q)SQ} 8(% '0D oDHٶGBfD5 晵ݘ@ou<mPʚr@%6ɯ W RR (6]X~kʆk ; b UВd+GxyZWaPCQ%&Þ(=DE`*E^(,r05bFفB3x+޵1_=5Ǵ~ï2TCZ*ug'Z-S謔C6_DM: W ''_kߜ`|%ly,:gw_;\tV? IoNØ&i"(vrtʧiE8 ~YE U-H^W RZLYRñ'|1n^=Ga'>qp `<%-Iu ;VM'Cx\XWL재j87 ȗ0~r',Rë#0 {ñhl c2A$H4# ,i0Ț>qاf H@D;3WC"c6OGm((+ nG pOE7IO񱹫_B~ańAo0 W>7@_{K T.5C,t]V/^tO@߇|> ӖRio\g?[d9 ooxqޙo֋R$i*Ǔ:t|B/(uMf9d:3c_3vP3T:KZkՉ !XbiD=D%gǪB;!xAÀQzvyunHF?Cti`eBtvX¾{߿xZ~Ww=L%jag\8ڮod{izl4=p cx~x664Z";B%;x``ЈX|ejN,%=FӒCh֞7WщLX!FM "3(wlcFR-LГ/+j;$4\AZeYT%x]='P1Ƃ%'DԱnG14h)`,p*eQz{0c.r3ąύY걼PߤNsƄ:<.d7B#V!? `_"Zp}6a @P\ul_[> H2)Ԇ!]:q *?")"d g !=z|wWF1 O7a] E+ DP"nHLB|D;T.Q) ^AAp)HA"{8# [Ǫ#;7`Zo1pR%Em+xLj?@^eJ_NIW[XJ^ftGQ4FV(HNλKڦ; Bz}I"4VE_]qj1qGL~Hg Ax߿1޽ǿ_0FoiӮX>">ڛmHoփaGn󠼄;#"gSGs' g~Lg Q;cHn {W3 H.Ď v!鮛# Y \y .C)0tD` :I5q'۫3Q7iD,\ѭ2YR7iG/ބ'cDO&g0aOpJCi<8#Y螬5 `{f y :M`)m_cv-8=ڀ;r^ZIU2 THHpkG%KZOOWGXERyI۠]'NVPMzѿ˔ w+7oF1hB,{+2WEߜgC"϶CY F-3{:WZa='qh5B%>bQ RtH+i * ޏGy[qg+̊ H*,'`߼XE"6GMQZd 7zBa.j])v@X!f=(a'Aw]^mA7(z0 18u] -})D]#G1ߙ&Ɓ?&Gyߴ\,.TOԼXqP"cl¢nZҲ9 MA? HLk$v5,(+^Ew6񔎯m Ou/o硭H7S1 Monظ;pj{ },ZPx^e]TZz0@V3$AŞ\|}]=/^!._?dvoWe/`=4ꍆvxaB%·$xᾅ4M+wbC}Qe/R4ml(ÄZOo sO"hHb<}_Dxpt7B@B "tXW >]@uS# c% 7$#2'*XjcZw& ,~Clv6LH$]7iWé.5lcα,i6ӂwgA1)~Nb-03V= +N㢜CK 40;z}Č"PIzkº*گ~5P@nkKs bb+eGG0#tvؙyC.;#>tܜgy&b[Ng.߼2 h@Qeav='ƋѷTTi$@adxCtQUW[z7lN^2wf`:>>Wt0Hl>Hj7D{36L0ԫhc gK4v[2H%q=C{.vgS$Mp]] ~:xaDEOQ "g (h)ZfU Ɨ K}'v`i`܄qE)a#f7cXI|13ЭN:M[5TTP#9iIV>*ahMh8K/U .j(v7N/ٜiCNh[ק aw&)O}JF4wq)z 㟾]U pQ ob)-<J[0bJ~_~8J xJȞ"wȀ|CF*9B gqPb׀ñ aλIZfjP"Y Qh뷈=j&"hv +ңR">%Pl$?x66BL#y2҆|}9nޫ2Z55@GIf[3`@'"ů" o:G5aa3,4Z{ ʒOzϏv*dOR8.Y Esay@$@B"TS H  GY9"cI)=@YwZ]q\/Ea@ |GWN1IڡRuѷichH>`9G5ׁ5% D;1厕}oɀEwHhKu?sYT:߈i;Yw7_+_G C_a-B{dވgk`_MjoiT]Qm =wC\V\<RSEIJqWL; '?unwY<y3C}n)^#{AA)dH͗ 1 L@=`,ɨ6ɂ,OWxOX1[reY6mAѴQ&=]QF'jNRU;=~:7IȘԪ/ #g;S oOONާNhO%gSXQzwEc)FDkR]x&BP|.P)ٕI&By;  /klob& ]F/;D֩%۱A#Z8ͪ+bf.HH3rh2B׮-m)?D"5|q˷+ڗVXcD( 15'`sDC$֑GNEAчuzv 8V|8[vk!8z qs "@*~o`q'QW޲7d&$iT CSo_Я"$ƈV;ג^kT'wr]<1xi&gG~/ϗ?do}{xA?w|]'hݧT{Guy>aξFhZTdÜ"a#qgg`+4^)@'CtLKG,v?G(ye42Q8Rb9m46j M} þ'NN]oӅzd %jJT$b-hfR ); ġ;63@蜷ӯq-l׷AV0;tu @HJOB>:S 8B[~8偟 }LT)E\J CU'C  I, AZځ a3n2Uc'"/dH쁔=WT:'K6 !".Jgl->`ږK:ӌ]ìd<^> kG c{Mg0䌐Up3Q)2jH{8^$Wc /{|84S#ؒs3R,"R %au1tvzzpkeل i5ץ?I1@{?Ԙ~tdǡ8shESPס5&:RÎOWON٭^>D=y__|b;}s=rʿô޺Ξe#QLi\`@VT:-7twOi"#:hVق9E1=½tS/*例O\|u.VBc EKU&M8'VyPdٝ%ilau3J2 z5usDB7!x y qD"PE-M (q`:=)0dE;oaՀΤF DHv w{O8g_j ,4 VU=T֊$\Ц,9?JqAp!? }FRQd  bVBuH DWMY'ӠWix(«($}4 bd2{c`P=ԏ%0Զb6 (I&FHvx6)п}J:AFHr)E;e Qxx^O|)«7xgwSַjt}-5Mj,evv]9qI=_윘>͏I vB=wR5NʺqT=A?2$+Iul,}toȿ ?"=1EgJm;#+!^iW(]qT#b᫤cG ~ p[ Z݆r=-&> DIxb|V( }7T ~(:pN^#6Ȇw DQ0BESGBp5+2v<6w `F O} knj%vnD2*hxZ9Pϖ7©k6:a@~?Jq{A ^Cկ?r$68NІ JG6L $})RxP;h1pX,;~pUE^dUun&Bo.$&Q2׹Җ r;н0 Yq!1䢱և q0 p;/1H]hA"v@PLtE` (HՐx R*_g3RX`1#K (RO*z/ !svStQaY)@XT z% b:YEdtԄOSxhX%[/KU_TnjcUgO^Bb%#cuy[#( ?,菉C3a э(z^㽾ףfol57³:h=c7C[2Qc1Ggh.$%i:{)Kz N~XHMgLOAG.6 Bk~CQ o"YUm,5:loB'"WAf {) )SʲBQ{>8lTTQ(>;hjD ;"z̿$PVıC Yljf_G'OQE>\ix̾#Xu,kGHAB)A~855H5 o~4(AJp!1< ĠU뎱w:]&8dX#tV$/Q( G79/D k<PН0}u+@|ҮQdF& d4 ƹd^ 0# h P6jEwL 5#hhƥ|] 7S(RR)\v^+4R϶Z2˕ڇ8ZRHc۽8q}C b:! 0i);4 DA'#{N$jY/QPq1g iQ&Sc(AbK`%tdp) ]cR+'(20x(_eFK ԫigtHb"AGHиSRG q3VN&ESiཏ8x)y=|^ E͑4f2&mG\:|?ݗ ѩ1B79Z$;bm/lqIQ d*Ec-jzĺbXXVnTM(0\ՙ$q[F!Z,G" "J4MG a2D#qly)A-G%#~p]e +1x* ۮHLHEfԦ7DO3o5`{㍍H2J{oaHõABh-^thBֶh%h` ]OC#ߌs A[]S%r?&zk\DMFu.e4( !d-(E]֟ b]mr k =!D{Sz@)@o7%:LtYBO^:4 # b[ :zݳf]gco VFLf耻=2Xm@@Y+ >6ql OhtKf+}]mҩ aU7<[C͐yQRjmW9QQx3PdT3Jz0akh"u^˞۱%'%.QHE@J*9J8$A (02pERR&B v"OcG`I{D!f;6P~ =¹[hajW X_; }" vi\:6En8Onv%{S9ґ|?(LN@vö!G֚ˉ. ?KӮ'd)d_ftGm.+nj+˰{`^׳J\d ,U} La3K'(OP_O߭>pEuw}oկ;7pMD:z*ENDKU$;Ro+"Zƌ-Ŭ8$HtF9<0'];`\GxcC򜧡/߭GDsqtfh,P׉Ҡ/FO&kB4{wm$GՠN4@HXK_(Jj!'Ampo NTK}_LdZ¡9f3M̩УTd|6}?lڞ=ډ$͟q!"b=9H.X C_]"UĪ9UՆ>y@|}gTvؼ`}z"}nhV_؝mMqSr$EfpN/sumUY7:&9Ϥ(gCUbQe[> [Kt3eyv#7óA<{ 30 -l] hT`|qj.ٯ(zIuf~ET NRN=F|b!Ҵz&o٫.6q$)vOo~ ,/x9+''mRZ6!}?jBC(Rikt]ݼn-1qOy%?R,t;(v}OV^~-o@VwzMHI2oy~a|CںbSki_,{j<|:dg+1U"UpNTi*;ZˬZrI^" <0 rF= Kx﻾$E'su>GfOTBOt`x   ]O^~'#Yd>1EOc ~5doŃ;U2-!ejÇ=v 9!X8ry7(0 ymoj&Bmx춍 a%fG Ÿ}5:~.-(~ExlkE_Āُf t@L eomὝ.hZ*&jڛ^4y-GlAo"Bʒw3sx鏾),5ܜ?l8K5ً%нYf9Pu"3R J$ၒEbog̒U(brG/>-^(P"HJ"t6ylpAAb$$,jȏ=}sHzO5'Rf";/t79a ?W YȜEEs1 $?Y&oUëkG\Gp?h'.T Ho 4в~ Y fBf`[lĒ+7_YaLܒ&<.̃cx\*kKSfmwЁ XM# {ae7୕ŗ77l3Bbks$õlQ0ei裚leQ-)`ɞIZ\fA%MK [e$_kUp #@'  [/)llۿUQS6:7 /h%~GpHchḃ[Ntcwq+G"ڝX_hn [@ ȂTSX,y&(Xv^[hƅe!1AQaq?M0FءAP.>QFb)PDE&߀i(+F?SY4Fk: p~(CL)Kga,CHhP8(KjTQaـd!!|K.,eG S=jW5JH"@!Z! LXP LE0LS4>6RAdiD\1"Eفbب4y|Ip@"'TYV/,JB!&j$WA-(*6H,@JeJa;@ P߆PB" j@H `b]NT=|FҥC$0!z#pQ4BP0C( 4@ sC@"8YGB!_KWp,DFBMN`J! )0 D*c [(( bH`Hb cTh cBP(Tz TB_@-XC0?" c0Ќ?QK0|>~tH|Qܢ+)4J#"7DBtij FSWx+YzAO!"6HL@$ʔAT!T7S!τ]4H)x`G( zS VCj!D*Br'E[i GQ0 0?-M(A[CM   FBCIL+)u)a,DIbCQe`-+b M70$7L, > ˀ1%ʘ]PC6~I*4bE54jhא G`ʄA%HίudG2g) Tq"h~,B.@$@4ATXA]8ma$F e^(T( z*,A9`*X/2R@xʚ"%1ReH~ ؁r D#B83ΨuBR AT [@)M S;(Mu4"`UH !TR0Q01( ZFaRXsSpH<\TlX6DIAR>8Ay(%O;DlyY!aD+ Ȧ .Â9K`Q 1A1+P:! 8TIPGB0JdV;+Py+@ H_;KH0$Ia᥵8([Jc.N`=l'i$ (=) y‹X. %FmZ*N5$3!րS([쎄\m*!V;$FmQBCD{ 2Hb?aI:A-xP&:PU  M. XLFą)a4z$c@C%}تZT(Z>A p@4!֊1a^ RLU 1T(q;[qt6 G6` >x¨zWǓtʡM56@fX.DF N*֗B@ c$B~hHPVwM0.BB*0(B!Pu,0aB,Y P1D:#^$B YGbh0A4( R)%h_/ B;h  ꈀpIP 4*DCO :i|>=^ 840AU9Kj0W]pߵhM&\}^A- t-k>[RB 18Ii˫O,0(D4&B puK Ł;_CZ0iFL@~R"]0k dLݏ]ҮHK@(B%+"E$᪄06-R|ҁ,>1T*jdJJ JpO钡v!kM% JBꇈ 6H0DVXaZeʅ[6BSjTIkUCК1b3 KȮdP`*B ``$]evGTtjQ% \gJ 6a>@ fFuT#yn&C1]d'Kh@ k~J]2iD,` b0 ?FDӔ&H]&0H%((j9#Bhp(FAJ8 ^$#2"@&$: IQ"($}$8 ݪ) yA(N"O*ѫ$PA] Y(1X42M)B1 *t"PT3#WХ[Uũ0!≚M! Aba6(C 2Ք:3OZ(4b|"q PX&QT *zMSJW`2QOW$JO℡LnSP<*@J^C,iL)" K \mCEA %HD"*h Gp  Z] @xTjq@GO)r6zvZb@y"1H GMh$KP5*aA*Y±0Xg3~8 !T!+UЄ]Ap !*B1X Ĥ ]4S1BP}A7JPT%J3!IP4**Cd@gEH#(%&U=@BٴDcLag;n$QZ(?]ZH(EDD 1'[Qy@9F**xӘ2)QT`]RΤ j%(v~d:4 q1^T(p+\OI0`HH$@ń&p,~NآzW$t<>`' A@^պ+z0੬ZA,FԠғj bpcߑ(`%If+a-"3 cm`| 1%ANJ6{!K D2J*X+  5D$Tl_-Ǣ ( +"C d@ Wkt^ 1 j`R }JU4hȢ4 EPo)&J$k_2 B}kdx`1(J lEkQijHLB&O^a 2DרD@!fAUSDȴ#K)LpA>$B.!4 %i`LIAЀ&eDl$pD JӍ!YII/T4G A [UB2=TR!ҠÕL%QC),mJ⤘,B>hK~PF/A\;[10S-HDqo< ft P}!p!B Zj*%cjF0oE@5 !LDdW V*^it(O `!,8F4( jd@ZČ|C=^>XN*IFPZ@^p#\&A`)L8Zi(߃Ƣq[Hd$-D8{AdFʒj|.@/@$(OAB)~ \zF*d)DB@Ѐk(0T*X~D[k-6Kq'5)ܰ(17ΜA~Pz:G[?f2m!2u>q064  bQBG |.@nVaBu$X2!@*EN %R צ TQUT(H$[lFż(J")hŰU܃BCւQVċ0Q,RIEuh%!0+ 6PPWueBdLC&q .!H  w(KJ/p{mj6PHTUe )g*9k16xd4A֢F0 -":Q@#]iimdҟNQG%T#89ZAѐD@5eB(( "X*?HJM;3*G@p +DPex@;`ñ-(!1MpՁ@b5^iH@As<$*b`P0 lT\9hFJ 8bkI u*H#pL֊@xS@0jVB۱D:H3KEƒPX :0RWר_^, \P` <ZU)aJq ) ZBծh#$%jꨰuƳh(LC@=YhrUF!}ĝYP 8POO$Itrt+w!J*8PyĂj=,me5c![AA PنB"80B)p#0Ъ1^* fXN(*hQj)X(6zh`.#P$!b}Hw,k!WtSZ&jTh$Lr TfKN1, RXT֣X2;JtB*I't & (\~LڷGM)7T`8Æ)k2ajrW"DC0kM6QvdAjif@2H {1}aYݹLj;U[ 0BYtpVV(-?1sTUF(td:)QZ !,"+2Tx=SK H,PA%%Cx$:P=РPINJL ~/15@'H\@1Z57 k|`X@yzF ۀ Z_ Q@ HdY:idVH (4@j)p >Ѝ Kqf!SciI (I[S@Q] G/P-hZf,R@׏(PQ&`  gBJ-1N .CMheXӦDcbE$eUH  #S ,ZTF|xHqaC(AhR #QTRh~!h"zcPIPdN^iH R8,ŎUT24ȑ j rQ@zF\ĈE-\7QR R8qQh@Q?ps%U @<ꣁQsS&NqqF8 @x@#<DĂӡe(@Ȓ VGE, "8ҁ*#-.tiˣ -eGoTIUaQ kARH9HFCҠz/( et ^kX ʁXY0"T@5@@--Sdxe "y #\tPN CtPX.ȀGv1RhKddS,1@P@B@7Va4"3 ] > ! @|Q PlA NiCP(|BpDdo V$N($F!35 WhBT))K A `V?Q E7HԠT0$@+DM/iQp "' C!kTdM4`#B_"2"X"d*QPu5 zgbZ˸$n , (hr"`7hT"h/q^̘g0T(#[E!5LƜG]A ȳQCZbH\ߢ"Z+ʣPFXBZ @ڼ2 URXmKPd$P I+H8L-*$Lٮ=hE"VΤlU"ѱ` a @ P%y( 95/ R0"Lɩ̈́ R@b%(ZHꅠP8M;-JPT qAK9G4JQTtHĚ)Ac#mNt$U,T2@"PBHPh! GTi@E$~9˂LT[%~E7Ƣ Aq8.k) M$홨QU(@.*aZaGBሳ(MBHK%#H1 hJ2"|@X-IPAÄ;6[.EY V.˷ N:?![r@ H4FJ9s!VUV4Ҩb`UdB) !\[y<6>- [̕H*mAPZ*`I&ES`OX+3* .kQC #-Dٛ)8(dHP1a^WA;H:]|}x("XFXj^,uLPyRZGQCB|@E3e*E}z8q`- 7Y PG҅)L !a*ru:bǔ 6Ljbb+b*rUZEH *H%u-eȨ]S iiM9ĴŤ6a]t!Z /`V@%J@4@E(ZvťJe-u4ED#`:HkN_| =60A{2ګ%PH8Ox1@E5N;DРZAF: ?`qNPX#Cph%I"Lq2TRT@DP I1bM@toJq(0c"Jh zHd9@Ek@Κ4' v99 a @=4ڌ$BMX U(GO"2̴geFzzٴ2W#X&ԈXT6m7R|'S贃kHQ:J({! XxMd̄@*YF\ WPBH!{fw*0P ǀGDa"bր:'lR=^ ,F(Pܠ)D x$`k1bWM 4Rj5Ia/@5@x?ĢPHxu6F@I PCP]h5 txAyjy@P=k@4Q1 *RTQU t\ڱ@=)} p@pA}5_"l~4=8!wHSf#KDnvBc($sXi0h a/DqPO px+Nµ ddB>~A +ZM!j%G " E^$D(u4 ^‰*@HPYE~ @h@U?2Q)0"RZBjl{b }(KQ-` B1$W*-1)Ԧ\ fpG˜rG Z*Hٓu) FB2J] 2 ԃBDmS QUVD pRQ.he Wpf!(c04xid46JNn jR͠PH8IT&( un#B@y@ ρ*kR2r!ʔ6$Zn. RkfEE"b*N(4 @[g@*"(TAo(NʵAJa4xV=B8._Hi0DYtJ YE Pbh];U3KA[Vr!0AhR(!.&iЊrpTF=640R,Im} HcW+x YĺECDRAQX(PH*VƎ`9 VL@< FRx1 p1yF5Y蹈*D-0O"D8?BBT$"l"J>P*Cԭ?Bl%減x,h '($BԉU@`\D(P &7 D(I-+r2 pD|P`#RS#cق<4 Qү6` C,[H IjB%~N3p bXmf R= R<జ0bus0@C *tLC(s0a7Z8 ܠnH$d 3S+/ b&¥( = H߁(FHЎdP; [}1DPc ``2QPYU8 VR@v&5$# }H*L 9L;":fBwA*S&ئh #ѠL# CGQ@(0i$_%XWQPve@ep/ KYu:$X& uh8z"GC0Q ` EFBԯ@ɯ4`SzR1P~ATIT+dF䁬81Hp̂@N *ڄC:A ,`eMh>*7]QE֞)DwPh8'80%5t%Tq+d )1ZѨT XRI+Жh :$؅QP>!jB ( m-,LB{IBA H'"5P-*$RQ@)HphD Ž`\sub#簜e("[h mXqaD >&H= k0@ <{M** D0AA\/e,9<`0M\r%@Dt hґQR$ly5a!DBFR'C D(#,Nư#ABM k5 d匣xB;7f? @'cEj1lhAyDh"I"É"PQBAR0DU`/{?D #2FJ oп2Eȭ/>JSУĪd+:I&#׀.oӴՆ07Bg@rPq$|bcBjD-W Ѝ Ɛ D!RBdY Ka"Z DJ`W6 EBUPe_ӱECZؔ!zǀF!EmN  F4EˑPh 1NU"ORP lPs=AB(#VPUF#:BEr% $ F4J'0P n" LV(H $F/Et B`J&|b.ށ@(a `@cW/B= IH3$(f3fI}tT0H!e@Z thOyd4ZPj5-g KB`U%4gFB)2:$I^f9 ŸI7#W@d\Y-%l1*sPk6V$Z8!XP%Q9WK 4X&OD 64  H4)aE"G(O z[~mZ A4p˞0$E xndDq@uqZѦ+f+yP Eq]3"` nBћ!#őojv]('xdu6H5 M A9U.(JX`浀+$#8vJMi,ara@rҚG p 3peTHf^TzD8o9"W:(9२F"ADG ,⊢`P(0@ F- !Pe@ART PNлY0ӄ|H|E*T.4Dt C`ZS*+zCE\P,)tbV!ErQڝ@&- }T-KD3(%@@Ĵ BTQOUz3|!ei4Z8_ҊP(HҠJrVnBڜ脨ED" #`F7 $$m%!Kc"B!V"Q\T>~`EVu1"D-OA(P, `\ˎ!B}C"hTPVA% !`5ђѐ>*2=Ap$8J+ BAVhG*#,Ny> *&.Bګ'U $&G @T,' 3,8^ZQ3PXa34yHe K QQXeQJ@pHmPb>ZHAaEk$ D+(܊Q`aUӌtl4 vX[w )oUUÆ瞂R ph,KtDI~2lqoC$g ܶՍ4R8['R`8p@Ҧ-(0+@n<YEaô"r- ; N DdVIƱZ7%Epx`EЖ)_Hٙ h 4"+BE!"%b0sA6aDYT2)ʑފ( (0.)qHUHuw8&Ys;R1G\h\h bEjD^)VUᯒK`~0m!_OQMA)DZg"%k}4UbA1ZH5-ĵF$1Ɓ])TUȴ!aT$ES JDIg СIM$jCN&&U#T(+#ENC:<*h@"_8/0%C!P""Y Z > $C$lI ed qL4~p2A\kh^+U? (ިw0 BiQEnzE,/jĪHqD-Hg & ;Y0&^ &gh7*zQpi E160eaPu :8b@s'l['8 h`8 b Cq FZSJa k0 )unD=TkX((G>H٦TI\]s &5Q, @1KJSQDmD 쾒i B(Eqh-/@ )eW Y!ʉ!C(\ k7Ip f@1E.^ab+I1 _S@Ŋr<4P5N`3VyQ(Sв3eD"@^&)`g (1j uC:g*J͂-G &$N`M{JIm@-H"@#M0 H }ʅ$<8iEUC|l c&YDY?*^j0i%BvE%~0H(Z1[)tطDȨ=G PR@Z{ y`$('@0!).e+])PGA)moI`NDhƴ$êB=Po @In'•V@5 ђ.AКL%UU~zH@= "I~V  u"]-Ԅ}FH[+ Qs "⣰AwXATҍz7f:8]4*(6VD (f" : N++!CuA\°Ֆqq LUU<PD)-)k1JRo _tNBq6:!no-R!P!'f2HZ &k@h Nt@@ EP⒀W$ "А*[ZCTX XK)f-jBb5V4PeBK9,lR Q4GVT5I]6Q{HJFbl0wA" E,%[V0h4@`xdCSu@\EJS #L_J(Р ?ƈ@#`rvY 8N7YE<:؈ZP!l4ś "h4Y(ȌPE҅KB>Hw5hU{VPpBMU D\T#c*Io5K@x8w|pA" ):G(]@4 S&@HT$v|GX Gs^?]!bLD(3+]T q3Fc|y%,, b@D5ʠ,˂N`BUP8e',P"3)F#⨅<* H'UbtZ+TGjh$)`V%i^jE *(%S!ҐD "<8Ja"(uB,C 5RȚ5 5ŋ @ s m2 a"wlF^4 z>tZ'0Q@5Q ¼>Ǖ,i օ7@ _tJanؘ MC;ZԂ|F!!tH\tH (imJ;(-1p"[MBn`?Y 0U2234RN=HnŅq0!} a,FHdQmBރ..SuF;gnD +uZ *@8 F2@YH U5^T(=%깡Rn $`L褉zZX&ĂcAXd QHbQ(> r_"bDag h6> Ñ5YQ`'zVx OY4z)01%-P +maeJ` (!AD%.-?{0)]JZ _!4"I ȵPLl0! C4h"7 d/Q(2"Z/ ׃P~U:S aDz V[4PqV63*)|tfCBD9U >bH,, D.P‰(Ό@0*\GҸ'I# <BXM~=C` a@ .0B g+/Bӂ(@A+Tڥ"*Bů؈!ʺ]3 6mE X…Gm&J G7I/0!RAQ`V/%&ESYZmPdf- S2!/BFZAU`yD(t0JʖBj%Ei2*N=9FϑsMq`vpxA4 U3&bObDQ$pHMs! afLVWl"H $I (E+1#Q`ܪKBD@q+( E,Sm!)B 6|!*Ys zH⊠Ð=}rT%pZx)2X AB5QT+@U< @ J (Uq 5:>)g )R3$0Pi#dRٛR 9 GT "j"S*xZq51O QxzD(R>1a*kCSL1|U'P[T "GIT"R tRLbzրtFjU!Z # J(ENDZF(A0`-&`.!,P`Ǣ!`MHp0Hb>h HX@ * J* z&7I_@ bjE!pՐÔP)i 4]T4E4sJcRL48hfܩ@"yV$HHEeV).DB)Z,XdhB*HR6)gBXS( Q-S%Ѕ(…: /CoxUr 5*wCLU"DS4vX2G),r8B4% lT&p$VFIX6qdP'l!E∃ bgs4ݰTIH 3*jSR:JS\qA"M4ErEJ 3Hi.#Em-/HrďH'!x@/ M8iDEeAG,6 Z"!a`*cT7 ae$DQ|"U%z 7R tQ`HRpMU.$0? JL-P!R!Ho(+M4ִ53t \M0=H2?cLg%$;."Ќ=@5%hP' mPP`{ (X*B£LdAsB4#aYDA p `^("C2aj \eJ, QLS& ܈Tu e:SQ̸¨(7"z=(ҳ:o<$&Lx*D)Ql3D*FKhIV*Z LXAJjV0Xd PQ|[c@t`X02X0]D0A% @4|^* #HLtW8XZG Htd"ТJDZ  HQS%JhK-d@3,QL,ZD DPa/K1YaŒ)(<5* 0(I SFAMj k1X{G7%1H҆@I\@W 8@XL x5b<h!X E ԒHO@(d<ĭ@FQ26E`C΀!1BJh8ITI1XB(&$ZE@[~f\z@ 4px.O M"@gD3UB4SiP0HCH&8!yo~ @UNIT@Y1ð DA -k\"0(Ih5\N$M fzAD34PA jшӈI *faJ젃@j!Xf&0E/ Ǣj7A3L\&BF ( \1i>yRANA4(~  V&c}>J@8M9P@PZ`@|EΨ*كw*@MhLf-T' H^-ED(aH(-5N+.(F#& @TUg H> @&Qa්ⴡ.EE@_L!)m`U4i?iT)HDdHJ8:a]e1C\D6 ea98І% i; %Iz^@B5}HDv(u  %U `YS 4#3 X)%[JHGEӜd ,[E@|JH . %pSTtaieRl?AM~6=AELpVžIj]Y-բT{CA:boKF:" M!!<,l){`xv;4oBA("* 8F9CZ@JV6%8dH#ԩhNZ*ءPʭ@@I !`$e ܤ=T.:T&_`(d5 XH*-4qCkQBD"yrlB;HZ H+ N N\B^Bt [a b -JFJ"@刄1Š&W] 4rtjJ>+x @A# ,c D} 0&D*T I@K bJ\"LR8,N D8{PID B48 %d JP0_&"p-;S eG 6*yxn:2Pj,y .UtP`g.!.$ʔ , UT$RU@SR' @V"mG (D v"0 (+tKj *D* 2QF( =!څD`S<Ѓhu& #)CҘ֡ZZldj BQ> EB!b+ ,^XIfc@%6"ZBYk,PN4 D$tUZ 6(&JnhD%%3Hҥ0 #'Bc Ag̊"uITl(0yymB0 <<]{@d!ە q JJp lv @]LA XVø@ǂ,*LWRX$ #尚09pe ɨ2IOl$Xsb<i)Ԣ)ah9zT!$֤q$` xf8T},1hASi Ȣ$V`-(0)hkZWka! R85+KJH1f!HSbe@%( F xV\=hm%Et"_<@@* &ʠ*`"(̡ b -`&\ʂEk0tpgB`T( f0 @3KȠu D <F8`@ŒB~R" elQGN(ϲuqA"Ŗީ KZAFuaX%Epїd$!'-'@`TI ) R@BA1#Q]X!F!b*+|c`E(#Kc!i$PUr#? ABh( @1΄TݘRD˙&b C_f)J1#ns$x WH=Aѡ(h(U*/lx d'IBa4 D'dIpK] p,ZlY<^8BF$6S1EYrNrR&$Fm.eyՐU3 F40Dڊk&֍D.IJH38Btup2*ZBV%PR4 I$ OQ#*ZIBI@(=YxlAH`I _ yf4b{b*x  `(M]DT pdps*/D#pkHL W(0l(.'zF@*FBMV7%`1pC'B\D,%i%/"UFD6 ˛-^'9R਋力LPPT &[ zzTHS[p <,NUtYy) hHgZ'EA]/K#MR6Pα*+FąjUX_ib **( PhAQ@hFbڀ7 `Zj!8j]Y56RF@'(;H* TDR(g1P3P0!*mNH@A$1R DP-Z)!'G3@N[(RHL̓ Zb}b(1B˄@PG2 Y`&AL 5.owJ 0")IzBG e8U ^2 ŕBX#Tj^b3'S},"d(P8QS(<4 x"@G JPLi C`P +Plh^"rLKU!)z h8 E.f?LpNJ4H.X54RDamk=х+E@$HA=Q 9HP1%P ?hfdWjҟX rb HY%H ?F YNm)qv?GEOЈPB\CaR8(!D5C68^[DwiE* DI#CZB̐K +A\0vJ!P1AUdYKAk"m!ʘ4)aLF*PD}8h)5 D -<[e1 ,d . JX9 {BSa|a*y  Xe)(H,KU2ȚP-z##ebzLS!l\B٩E5I `U{4@ZH2HZnliS$ơ4ʑ?ʖH0Q$Sa'"VS)'*@90@B pk p$a9W$l : bEաSTS Qab"KIׅrH 43LqSBvzC%HAueDZ1I1Ah݈5Ch#D&XQ3qV: OZXTޔ8N[fBW ",@p UE *@[H!J_1FsDYί@X8(&jU WVDB%T>iq(}HF@SpaP lj ) d:C* $V,) ,ht/҅i:R E AA&RkJG5 jB)U&҆D#C( DBքU$oETG #7<6 U2@ЖEeHhL bC`eu!%M{*oTH )O즇*`Ba@=(rwDB,0P@F Jj!\&eL#* YD,RB}ha}m;P\<KgF( +!T#LP#FRÌt0SU@f-&!EAeQ=qxƇaAUVIFA*㐀!BijRxH"0Mp5jV}πmء5I|QoՊQ]pJu~@)6BN>,08UAH0NiFP840+>GBWCp3@( y6%)@@tàI @[x `WAH[ݐ]no{ѐF@ /!mD+7 h6hOX$*FPzeD%c)tך9@)FTzm#Y4<%M&=mE2x# h27B 2J>FN>@2_Ԁ`$Z:ڑ0kQ*4 h# e( "\ƆQfѹpPo$ P l'֏l`&%8 Aңk4ˆf["r#BfE]X`kB`o(mR?%PdAlD AP |&LH,0]C'ad9.Щ@i/GECYkz+Σ+E2Y9m2!%@ wRU &I i%=\8IK<-[UBl$Tz<(u^E6Fx9"!Roᡈ8 4"k_jrAhgKM Pjo<%V0,6@R-_A_ ,lH Jh"J:P|T$j`| \gt@i%jd%կhEػtJ ǚ BR#(tWE-J#R)t5!ar )AH0b(jb pPXPG#*UJ7U7J(~AI-0 x r9E)a_T4#{1BKke( LB"JD@@> |d$ Ąh*1 f ,)pb-GѠ@/-2]<2A28"2J2 .|U+chSh=C:TBFTaǥb 2J}F/ >%h ,"H0._ \WSڪ">3urMU0FE Yq@SÐ /F#+$KB*D1B.ц|9` M K§2ABUpј?&aQ0zƈ'vSahZR!EB$QW(.%ڃk eʋrs0""R*f#D=. /@8PPZЈ5(X¢2+QQpJ 2hCzǧG 0*(RL@RKĦAF.-E2H qF4(@]jnxT 6, @ @C񀺈0n %Q05QHHd +PDgE E*+$V GC;75GD9Dqr.  S #Ƃj- '.FDʐ23J%V5H>fp!"F3V-P  Oʔ&2 5'G U3a4P5H!saH?]* ik*P Hq" 0"U:h2! 8( ( bNBzvQ`EWHU/qZD0[Ah(y&1m$%M|Fda"h*b r`8!i:9IT0) 8.+(M"GN#^%}%7d5ZX" U D"8KPV"q]3ªmTA+[hZR:h$x7"x Ř} *(lC8rBcY1 ! 40 XT@<1 Ed ,V8[Ap&?dâV, x.!@E+%b3"3UFR &7@ "/` w")]HY<)UHE4 AX@ ->ԯY蜝o3H1 RzJIJB劍L4B4FMgSb ". %FVF!@AZV(' +MUTH lC!` 0`:aBD*PheYNd6Ct`У*xA4[PZ48~ЫMO LʩJ P2hx׫WkrXJH[@|ihe*(DhAΆNQd}EihXW]X4՛P&^["FZJUȉEU=~ U}FqAV -QnX Ev$\S0{!)%R@:l2) eI 0"ZB%E]h(S@@BQf!ȬEyhF.FG r'w)km'" 㼤Ψ*4~1heKsE5` 0h TȜT~(ZA: ,&AJ1H&jS Nb4X SdeF "T}|Y.R@&F  K' *iMaB* 4r$?`ӫwVHz F`!_D  %j G$ (#l'SFtB4Qy@E 8V[!6B%A,p1H(h>4F>#e 0OJ'Dp L>SMK :PA@, !C+hVɰTh+@(L^W-=1b 5'5JIqA[d* 7J# eA`(h/!(x%kB-V CB^cQO 2Tx!h@ԃA=3@PwWqi %貀~hɶGDi 1pMI 4"0d 0jeOts1̝6ataF jD`VB@K !zT➲o0|-љ@U&()+1P^ם4Ԩ$FB0')VᲝ0y0"UC&莨1RPDɉ`"$=# =55Dw.<5#CPF/^|\0 M8bDe@˳+ RRAR bZ`ELi)IIW_N(0܃ +qa(piG~A#OBN%Cš1Ok8x/W] \G@*L%iF?CjA3S赡bfZc@D.  jUR UO!jzp'J64* :QFᨧd2j,'!%Lh$@@-BŔNeI[ tP0UPFqjHN.@B5v@IiҜlwbDN JEZkADŋ)3MkEق&: @Fl0 !<Z&ZXS0$>5&1+(A-Kœ63L!!3U|QP)@ U k/R{ 60AZи#5DBp x( A @8rVItG;* ۏ! XP 2P Jp"WS1Ȑ I$PäȜA] 5sTOJ@+ kPĜK!jКB$A@= E$ 4tJĚ+jݘ}ID  B4J.cDx@z UjC $҈#J aQV+!&J *D Q"6+`4:]xH%[& ga`> k E c J@ȅ \ Z0p"(C\zH*T(19$" +t$?*;T&kH@Y܄9 SG'XPK@E~E@!`"Z &[ Q1FTNG PЕ.$FTCm3򁎭vRN0e hp>ŃMxEx"BNaE0tVIJ,; @ (# -KG2EB)6GСBz`U,t n D qFZ'a t<*f $R@I5^elAX 7U = PzqA&RA#6kgQcqjBz ZYTQ@HjW&MF)&H%}|$ULDT W.f0 Ce t2"2ј+% PRCDQ!J4#Z&J%nM*`Ƈg Y#M`H J hZЄWJ PEU"  @T@HMLHjxPN!@ˉ U_"Ec@C[_CY%x *pEQA5G hZt I+|./ei`]p R@BTBk "K.Ql8X E!@ȁ.<)>p< Y|м#C؁G$ @|D:-VRĪRxZA$GZ ›Zb8{{P EVwNJaS& v`$K\``9b H:* "e8L"K!O R ȉ6dF*K "I hDr^ P@ {qv j$1|D;l$p/h!ZarҾ_G44%IIƳ3Bo aRn$4S̜,\$ +h֐M]|y *PpshB@ݮqʌH6 tPV| #dH7>&Oh!xF   x *8+*Ta *x=DT,5AĦIJRʄ RVQ" *aEcP] 7@DK@P(B(yVr*8B!0H!ID'[VTUP6,(4x*"N$b楄V/ƢQ*JD+i!D@%iU VtC,#Vp`|-e  jCuxJ 1u $ՀB2B=`v= U(nE)BТ~G!@iIQpX -"P2$( Zb#f_lHV!VDbbᾟAp"f%hfyJ'm4& qh S<V7^9BL+8㿴tpPE*@GC>V)pVD $b EI,Q)Zh*/ /u8PV"/F] Ԉ rA E x˓Bs=f=-r+~ b-.y*cXZF 8!|fbJȣ +++&?n5!Us5ŀ "u  YB!$PW#^3 02O;貤M!M(U h)VؚցDJ(DBFR$դ"Jb 1:*- 7`G)Tj FR:Dmz(l DD*=`QPQePR^;eHA>MSTBtT'5T4bdh![](R@ʅseB beﲺp>SbIӭL "8"HTD HIQy4]Bh r}p8`QT0,TA")(F#E@z(k2_lt4Ca%AۉŸd+p! "*< PޠLZB)BE$AJ,5.d5K\NxS\n *9D",hPeAYAAxZOEd2 XL4I4XpdP1"|"sT6 -g'7YU 66|u\C ) Ԏt TxsA֓F4 ;$`'# т_@ @" @  q*P *M$mV)b+n8XHQ)ti]ՐD(Mfצ<0P LH$Ds4&B"LE8 6(&R D {$$h5l pyQZxoM {)F"袄"8OHN&&Yl4U$Cz-  TF+F;T Ńa~pU< y(hzD[1ZIҳRlW6PDPS a C'@<@Ԡ0J&PZTGVr+h} > JI<%D+Jf9$7Aha] E4H$.M %!H%jv"+}y@dr[\vx$.&,]Z 0G)?V)69T" +غ_i`8Z8upJA09Cf0sl0Tbh e*r*GѤ7(6  TPD(~-UC0^;V Ti ye9t@uP":Z'@*E`9% B.`iz4D2((&eN@ B,`P(3@"$TT 0"@B$Qi"'3+TT3Ӄ)|\pQ6 #xʙ`" smV,d>9I8 hMLɛiPkL'"_ v Tm#!B9)2FMqM9#z @JzWѬ+V[OqdA2hb5;{=!h,]$"-HlP(vGK?( ``ZPa( jU+ Tp#fOF"PtDT,E;QX"E"A@xhb2P7(BuQBA@@cU[@TGiԂBDzD @%> E&r Պ3>"z)'u Llh#} qjuƂ fѣ"PJ%ШpjUPk9H&9Tl16 Ҕ|H"yPgP(E/e`i@$2MPRZAL_4B$iR` &/@B@;dCl,iI8] & fQUY}"m- ؐU(RB QHLQp"a?q@u6D  @IQ(VMD/zCu f(v*,-0!@FU^L4*ME0"L_D leDǹJ%\   NP102#bA0P aH!h&%I#H ؠ ౿\B,A2|k,0(@f%)QLwe|72H)SK  .\'j4r-"A"S&A`EF&ZHlGMd4=(ȇ%#u_~Q,(t3,0 @ j8IkCT,B砣0" P '`8m.@Vaa4#PE *0!RAe  -M "ݜ JYTЦ5n*lM2n[BN!U!fhm8W ^"@]2zOnUJ3q)*$)_ߐHZ b,!X dg@[=bԱP"cP> tS0 8, E HBP_E~JLA: VDR6HI`)@EAt/Tր UnY v b(5H !T; Y5MB]#S*!x(P@: 4"GY}{a xc&դBxΊ*zx+0P }TXz!F(EP  &q *TZ L* PB3OO Њ.&mYdZQ39Z +$$Ճbd1FPgP" KFT/ 0")I8dz +RuV1FN R%Ć7WI t$"@q pAiln'&ih%$DTHAJsa TP%}  #ZB )%mъFMD@({}NԘͭ."(8UEq(Tk fI,$U FGQVn#hRQ%b4Z(dvPEa+$a: vA -5hF )( e J ^o ^Њ*DFBa4X Xe0}H|8'wVY$?`,+TV 3M5 Re)W YKҒi1@>۹(0 Px`@`M#Ȭ6gmIl&_Pj#hvaPb= H1Q 6D pD=DTkl:zqjj @hD*z%}Bԉ*\ F1Ozj hXDC@hP*8xXh<_@4@A bQUP'Wf Q($m$$ҷPr]̨D4XCAS 9` -DbE -_QSP2ΑaS@b%st(e PsK$hf@AD^Z!Q6 H!HؓiP,A*4j0rh5?T Þ. PEXҨ KLi ihU"j hU!MlX P`&52TKA %V`᐀.CњʂJ UP'j D D,] 5(@K@`B"B@A?@3ja "e*h|2Zz8`ނT^[Q 1JFF1: 44,QHqt !"PQ4G"D E .Mbxt :hK'J Cgy0G@Qj2%T<*NCl=XsDaHf65b6BPIT0+@zAtN`iƔpxMAHJ5T ,w)J&`XqJa(^ =b`>ay,-S31@1Uky# &$ ?%V" F@^&Ίa(, BHc`D> T!ӃZ`z #9 @@&|0"1$!J4-KƤ F*l9. >p~ō3,10 8E dž[z() & ҍ*REd5|@ %b~%MP\0C-$alpdI*L~Q1 CHFJ6 B5JHBwȉ@1 IP¦(d!k6WJmA($Fr JC"Av @RGHl#ڞHTH`#"v|q14#"@ g]FFB{*@L |g*`j#-&͐*\(Eb42}^QPK$ [` "7 Z&UJ®RKH_Q0RtP!*L"7d *HBc,zTD?Ch 02HEPF 8U+!0P/ŤHx1TT" AN*,sѫ% <3F`GQ=DJ2¹Pa* @i~4$ ʢ%AhV5Ipp耒!7F"?.@kC>M$hV <N"E-Q"F@c DyYhRD\T  V$@`ɀ66#`1iBl"<!DTjT4bÄBKPu! RPES?@i uADBb)554 ‚ta "5 #S!z5xhV ]UJ UUh@IBj(/|e 4N"!T*Q pv@mQ@}2+DY"5KQzCI (@ \A$%N cUWQ 2#Uof$A&Jo-LEaąMTK ߈$c${ 4C+WP(6"T%*L!Bz))aH] K(ҭx #O@'*%AXP6TjNtDJĎPDR l4(D F) zV5o/5.tmBTLg|Vr+ /Q+a@a_ Ш~ 0 :_oD -q2-JР8AbaHDBɚU0`G.0XB@dG) 1hV& EU"T!cVzEAX-cƢ` Y#6 h+ @5$\ E&XB8 j00 Na#!R-}0 #[ڤb7sRt aM[t[P+A_MT)U,'dL(my<z3UD@"ꞁ[2! O`l_EG .PJ.fBqFB1A D=-[p!#B`pA*jP%yhqِz Yz Z1#}B@DykDTQFEW  K0s. C "nV' 3$St JMD@eHŕ ZiġO Kb DG:9  Js@LmybI/a;XAb P>S }:Bl3W8YkZ4B&2@* 2w>15q;6"Hf~j\TQe8n,&-@@ *i`XhMX*AholU!TMV?ZF4T\x#gDSh@QH#Qshkܩ{T"E n`E8F*SD \B`8dA>@HA6%I"Bl]`QÐAJh!a n8Z\A3GJ\b2 U*`AJ|<FA V$^|_(CBBHO@_vv_$L,TT`b{@; ‹gURqC3TEkL'S0 bPaJ;, !R3rV񌔱Ae[A4xX(AZ@bV.Mc+$ (jyd[ DۀxTpJ8m1Zc"@^3J(UԅQkA +M1@2pHDPds | r"X۩HABArȆB &L)Z00"!, jZ"C&Zĭ7@LC @J@ e @Jx tkR"b!] R)l\Anէ~S hEa9QB:ĠPh+K".D`k/I /#F\a 03ѷժ+ mRL"@Ud"20E!%0 Z'QUGxRV <15*&%%^!NiEAaHE |84’}^VN"(vU-2Y& TbZ\P11ԁ@Vgy@CTXW{!A4HF*#6hh̸*eZȠ*ӥ] ":|1Z` M Ra!RN -4<4L*jDN.NCZ=MX)D$T{. J"@)h ϭ0iRN(ZH%C@c X^ӛib {   VAP/st8@>MB4 4" &AhPd.`+؉PM'AX iU D^T8$VdP1KD$h4GR,*F=)( qYB%jԕ WB TF2 zA8 jbH/gbT[k xxrV-0E8(JQT@g1*| 8OGcܴ+g% 0#U:>*)H,D!SP}O/F̈́=@ ,U$bLEFVpkxH Jp> sQh– PI(1dMRh6RA%C41D%h)f8f[G`1h$ cJ Wdn2 E #'> BeRC!1T SCh6 0hv@dl#@x56`l4{/te60H`0DUI"|A-$)T2Df UfKA(QF:d/L@ !- ,c1Pc ugU8R_p }E%|u hTGCBt-BcG)#jB F8=E` E_Ai*-LJ QF1 J" G7)XPBQE!Vp`1–ARN@LPhgC(WDm?%b8q ``@4[0, \# ,BX$

`'2*JFOL &j\)P*%2k@&@sH AxihUE> %$P D  ? P 2J,u .41@("1cdFUҤM@N IUĒVT!*ӡ4Q%,\ X"A@!\(`l4X Tj"@ۂВʁ X]Ѝ!9"(u d ) X\jBKhJr5[/NFD*b!ɦ#F(41t TA!Jf g(#e'4$ԁm¾dS[ű *Qf MIX 2 Vi0*@'yDĠ}$]7O^CEPL02|$QA]& Of 6Fxk9$>#EJ€'.uQ7%STVZ:ƚADJ$*͠ }R2(+[ Q:u$*2XM@nz #"ť!"[a!eIa,H%Ҁm,S!kCFb$j6(*JIKbGhQSdiD! `d  0D,u@a(Շ脴oOFE4J^(R3J +J hk- x MfA*Kܨ ӷS H~, .Z q;0 VYp=Z @PV+'3AP(PV0oA0- HTm^DH B TB^ BEir%CQ=T1<D! M5*I$3Z(XLd@ȰUHB".+p("+$lO* T!p6D! ¹`!BClH B(1 ƀa r_4 Kb 0wNOeBrՑ" 8YЄG#o%P%н ^ eX @.N5a* & k09 tyi ohJ@oD$EK"2#O]Pr=R% Dx$T]]1@)EOIH4,[*cV =Z=?4 e@e P%B]yN([kC@ԓu|, 8lI9+"&KTN^ d@NPz"/EaR;J2GM&8@7Bp¨*)Q88KBrM#EOX$}*-:vXT (n4P [@G X̖@0IP ¡ n 9'тQH8.U\:՚ RR8㘊 Aa yلma0ќPy(A =}T)D^V, InHXL&Ei69RzYCnE1"-r#E2p C^XGRQVpi T^ 6F4{j'-(t@-AmbKUpiHhRPUT700Y PlZ$ izg *PUQHX !*И`(i`;:E!U`*ۀIy*@8EC$}!"`f+<\h0bdpN%:DR(Xc(4z L #g50!L@j! ` `҈G@}`/%`@R .)4JxrPs(Rr/>Dcbt*oI[OD) _h $*D50 6G3FT+h@ UЩ4`Ul2QP#- 2BBTSVVj+*( 6ЃADO)sj"-EPL;JXE,0,J^:|YEU"#ӡh)A RM F#˜J0 Yu),N*!Pp4ÊXLg`Ԡ8W'3*r`3!`Q/@8i2PX@@HEVf F ebv ™2H4 BR= gxBl h-hdb [a>jZBP+3""b@V"Q#u -KFS} r QbEM0| BV)iHKTt5/t  B0@F8dɵБE 2ђlȄZr)2`(ш"L"SD0~ RGRdd8@۬e pԹn xAэP R0YW8X##nD h C',O~G.B2@BfW~H4,H[/Jbi %5^-I*! UPlHA!\TZTYL A(m *ƢbG᠂DdAJ RIb]7D ߈Cm ERij"UG O5FWTBq*HQ "UFD J30R S6e(Sǀf`"#( <- XV] !QT2x @V h =-A3 $ƱPP5[T"B@@l) PH @:!BXjmd`ydRDx);@6J P0I"+G)pMez   IU'Q`(-*AhJBQ ՘V F[g(h"( `MP^L4Ѐw$ҳk @ٵt8u3"_ސ>%PT(@+<#]!T}!>3x0waT;>Db/JPk!  .Ql`iD.ЗF2u^  C`)d'P)T/wq䂞GJ L0&VRe 5I(b6tZ|@ST%3C4HJ ץ1_)V!['CZbF\ut,+Ja$4ZPRVC!e0 DHA,A%VJBCT͔(0 !T4$ +5!C#Gr{h1 ?S1:H&*6iLt 71Y6D+Jd,f']$ EÒ>hC(R#0meZDG;ؘ VP/#AB5F$L(@N$a eVJ"4"4Sb멬taICLjҊ8F..cl0]W+A0("e` zMD)**|1DO"F'$F`22*`#9IS" @Q> c<;. Z!uLW~B, fq}U& ~8J(uDkLdH*'PS%V1$*Na،-^$B`(ohA[bbPAEf2XED@XRP(p8X jee]WtIˣ3h pOI2VHpRdP:HC&Bҷ}@ \ @j>Z8턨f x4 5'ѣc`@H,BRaD$T$RHp[E J! r$X h'(ؖQBYLSZ ;,RV*%h!^Xb%}hyw[NAtW<*NhR!5*!q(mVX xnn' -l]G%!/!>% Ҁ*D(P\LA(.2#Ab·Z6VҠ .GuzDdEv,mI ,*#B+@HK4@IJ ո`4@Y(,UBKlS" E C+lDS H]x$(@B]/*ZTKjt HRBTE@=$Ux V_q#rj Bh^$ŧA<P!6  ۱ϠE= YQ0Xp0HSH"j'IUO@^.T"I0!8 Aa% S Y<IATT5 TE0 Z8^<"$2&QfF俰r zIm"O3V09HpQAFAL`D`Pz6tHR3`@+!ee]2zoudPp pHB5jDrTJ&TW2YjfM@:61 !jZU!@sYL'XZS/EY f7#v6,=C\aZtE ib DB (e\U,_(G\P@IB:6`T`Th:Є+JDb|&N)/JT%4b$&MJ*1$)*fG;(ZR \ B"5,B Eypi#n蠭^!U )8$0~->R]#R¸Oz h*NOAC,1V'5@81K-鲖*P XPB(2 BF*dGR$v CXzJ 1KvʌJ 4< JlTgC 5K D_Y(հA]U!Q@p>lėal C HZ jҡoP4I% (hA+"7H*q<,%tDZ!ô @MCI<)Xf AUXJ e!)TW ^  [$El"Zu0#@6/a ,Zk# ;F.|n"%B@ hCq:)Ū`5`\TP!7ā2r!MRP6@{ZNbb0pЈ`UKP L: I; WaD j(g 2Sz N3#TuȍUUý2 8KL5j7Zb4@3(M4R*&KPvo@i fݨXx8`D#O X,R%GQ8*5V|:m&FCMDOS:ٹG  CHTI Q*WI, PNܤc$S"UCE# k[B j: .=#ؕ@ˬCaV7xD `қ%!sH UժT yTֶ%G W<\Bdfy`22jeV%G("D@Zj,B( '`/X4sl)ޥ@6#$.Puݢq !GS TBPj0TҠrJ UpM8IĐ*M@&#S 5A8!)R40òU$̦a% ƧpAf1{b<<@V*xPM4lSI@FPbN A}c]0QPW(X{Kc02)UA j2EFLdu'00AbW6ŸG:ArB;GULمd$bT[xS0]DAqu}j  k΄dJ)@4XU D%6(ZMJZ1M:)DJx_@8@@21ЮA>C0)'(R G$PEi`Y*9'IkAЀWR p64C>EcVRŢOaN#DT)P:X2JKb# Z02)@FG' Q\!@L;Z sF5) h@,O!"#.C%**i'v4qr8½MDy:H/L,KYciP<-~%ҰEa3+`iDрQN t\#2BV*dP1:H%{pFIT)%m0:VdFA9Rr6Lj #UJ(X%x>$J(@􅐵`hQU3`҆@kmV2J-B Igi UX,%(Vm`^&I1Y%IQ[PL"$ 4!*;+BŴi B&)DDE}aDk5E@Ra!P 1=%8$/D=2l rPZB7$(HA^?[QL!EP,!k%z E~J$Ԑ@Ђb@!Hfm=j: S@M: p5eFW+jmZČC (D I[(4`SBҨ@V:4كJF+CAd!0 b$4PAZ~!z(\(a@7䀶TVi Fؿ `Vfb][{E_?EP$/d4D覩9k@(#sIE<ُ@Г2g4N,4BH;kb\N@0%2O:5:U$$0LN)l0`6W,uD)ATCJ:zvBl kJ~YW&m V@ $PL"<(ZA"f  ,bPtX ,"Q(`Z (\QF֮$j{Fi{S]> . 6 <LkX`@DYwO zu`qn@IZ_QDLuJBQbց$h+~g+M6%B@ A= $$= "*'M`vaI4oEW J-AD$kQHd!< H˜ľb@O\|!LWq @ :'VZ@jHA褉h ;d 2\&HP10 H_H ;@'e T"ꪬ5Ћ,n Nu2@N("ij($dtj4Z(ŒR@JP $84[EUG#p%@12,4rS*)+bAelDo&z%Fb4㍥ҡv@$[ФF $L hʨ+B<5Pn\L,p+$k=QࠄU11V6ID@H :䛊 g ̆IEfg .#/bYB*05k:v\jd /T@TD(. <b)@-x&2dGGyQ);ǹ2Ar!2eAt ,I@8 ^%u2P%mz=ɐDPAx.MOL΂$jx"6U^-@4"K1C|3#G0o9CAǧJa>]>}"GVxґݥKc>!–5TMh jnj-@C j6@v]V,ʪc%kt)#('8pJc31JΏtUˆPp b$H88 r* "e00$h&5z\*1&R@E& sD,\ U24Ǟp&@JȀV8'Z2}Uo: 0 RlB7 uB0ˁ(3$GhBCi;"#ڨV&1@0%Gv f dF 2A%7LA%#dĔ-)ϓ bK un&,GT0 dEu~CEm&j FxS0W TǢ!a"nRpE/%5EPtX20:M,(j1a݈ Qpa,r `ihcc$rJ2BZy CI1Ʃl#4IiP^88luh_KH!Du28$܍JNOATׯ,8Xl\ &5 $*cMn#G 7H"NC -a Tz2Rx>cl7=H #KOiĎFH&J 6HnH* "R4y$PhRCPq\ !PU"4_بm%;UP+pFSbpEA4xAF g@:H@P @]A)0 t\ m`1#A`P`Ʀ9JTw%$fdJ", , (X`$67]([(NQNT` ǍıRF QB߆!)F6pڐ ~n4iRSha;QV]FD_&: B Iam|(( "! 0vB&) AJE"ԡJ);&=-F.U@oAD\zWD j9WXT ʀCZ@+Od0@/g`Ch&#T'tUUcRPQVLT<@hp=RC~?@AG'%GnpAQW F(FSз* ( @ Gը(LIGrig>JrX+&i$k!ZӣPKvZuҢeZf$rpiRI4`+:>;X}\)Gh#07P8ҪX*1 .DΪ ̧mF,& aR`o**dTZO=Aq[ls2p$ .MZ VÛR)t*BxSA+C}_T}:[Wmt9 z1Ee&BkEk:Cv:h_$gT(, Poh5K$ĄB( s5RSS*Z!: ܐ*%&F"U9 CJ *R*Ba R)D͈A%0A"Rݱ P@,0=##kCIҽ3TqmZn KAL(CX R`*-"uEQ!2R2*a*~b 5 iE؇NɎ1<  "H,}VEGRh]lH[m'DP 59n`DLp9V3P8n vdI0,y ޵,Koa 6g%^CI"9q4%y$ HgqRA)1>jA$ĻD:<#X0 "ۅpFbH ֥'@`@NRY["h*CԚ4A>uAiᲡjBZ @ DDPtV@A n x$flAiyQDU-50RIpĵwX38$#iHqB ."m5IVA)4(Apab Ef)ӼFI'}*`U!SǝPlbc vptxBl.ECoGR.OM\66Ibz!ky(d/O@qdT0kh2SdF)ZJ x, A"E 6%Ԋэ7)֙9b |ъ₧AÈ*Q) A8AƤ8H@4PLB47"kb ȱHt!T I@@-Zauȉ*8fБ)LoP%NĀa.XGѐB*uE '2B;0F EHRp45I+@Uz, Ԃ>`[Ś5ګj GDD 0By⦕0b>aDc ciR$9XydJ_1x `mLmbb  %|tzJ 2 qY Āc{#XU><&8J(@* TUO C4nMEq*znE!uQByqYGEc +cJ= c9Vrʠ@laa !=By`0%Z&R$MP;PF%&"tu(Yd. SHAhBJp#'EW@D, /I!` 6T < 8cK%¡ ද8+Xz5Pz1ZiiQpkkaCIg R p6T XL%4s 2 K̭*6@C  )PJ1E*-8i=AbaUC"BKڋOЀ;HLA? *(% oY=yƽuM0 " )@4 8J !^YT+Oð5ז@K Q@DU$֋mbdeDjE`i x:#jHfk.*z? t# ALY" 4] T߂(@bgE9690^4 hE4  (%FPPd8U 0@ckPN+Oj~P *mQP@w a!Y `Tz9DOj@ BA6x 4׽b5y_)U[pU \y1$e(- !R* l@ Pk 4;4rg t裁w ɻt ăER@p`P򁷁("V)h0H& Sls4*H!J$@D8YD|XCDD6jI9 J Fț n4;"u l #4H -Bzqdb qEMUQJ68@Ŕ"Dw(p,(tD/ghFZ(:ӡ4CQE' ;.I)E^ #؁j0\;8@Ot2DUqj(pPʋ39B(DP2x0,R@ ݈v`p) oaۢz|T18D=<%)"7h$QpDdkjT7I"HdJ:vqdcL B*dKDDauj j8=*8) 4@tFg CTCa՝Ci-ՄT K'#[RaUt R{LO }V & XT2U- 5Å8CZ3 C$J4^ze8#C?iDDX%ej,R"$ٸLHꑥE܉IrQ 1 !DD d2E[RAJ@t(y HRL#Md$ dnQV( X FP-%p Ne0>i[Ɣ.Ŵh:*|4R @B. ($ $+W' d)+C0PĘBLxiLcHkwQ 4h LS` ZȚSH1Y]8`|K2YX m"+BPb PipUb0P"aD광5Y0 8v(RIGU#B"~O 5 Fv1F/ EWdʔ}6Ý:$j%WJhFv6 Bg!"/0xFa@9_(B(T%9 a"7 2EHZvA5@F"SR@b+bx R$ctz?RGb|x X8L!V j<"@N#h{%$HÅ D! >L\M~lC x@j]FEE Ad,.HVaM;x$ Tm6p::AE@2ܘtyǏQ.*"M( w,{HhA_ $* Ȑ0R`h>=< a B=F@* B@C'zz WOgM /qUA:I:uf|CΘVB L*nҺ]P" BH(2,lDU(P( b DBO%4('[DHr^$Hd$jEVd H"`F HT *)+J4 @h@.RQA Ԭk4(`@muhCh.B@B=r[S'xn .$i.tp=bjX@<ؿ`("R?d $ 8 A}SpECȥwCe* $E 4SrM,L~'YHE0 -$hUB mx4!?TmZiy C2[iŠ(DkZA8U@D"55*q:B )80j(Q@ -@م"hVbD"%iҰ"P$0~!@Za]]裁8F3O+6 &(aXQN(+V! rZuJPj B4ij崠^0< Scx%ԪŽd&pI,*TsɪGԠL+X%EAa+TWyBщU J[Ćet4"ara D@%h¨IG\!xCnJc!&QP0+_3"vj)d A.H0`*X(, @P#4.{3Qn`kqfVh+U(-@t_yJz!IQ,pF1jtUD}  M[8 X"iYȀKE1 8ʃHs6CKes'5U g9 @iE~YtOR*5HΑ S!0Sۦib4IN" YRXF2,q"E{@; T@MĶ B HLyV@%z0@(/QbP 4@"ѢV:P򆕅 4ST(o1 -AxVP\,DՓF,aDkDC e@i z HZZJnÀ,qGr$C% $xx ,( 7,*] 20UTz_ICeJvQH!"/e OMKXJ* V:V0҂TDdO>30_ILYD5D#(!q:+] 4P=\D"Fo@GՐXH-A *1NƤ̚dXb$@v% vPEmzNmB r P#r 'PQP8Q= -0&ԁ @OЅ#q b ,E@ ". }DaĬ-H6H,p)/`g3 B vۢ[|=-Ήp4=!VP)J6B"H@#7UeCS_jϴ}+!OPCҚ*Ҁ;R"C=D ,gN_/Ul:Үlj%VÜ@MT"e(Mva51'hz_Pd/2pa )FE !H2" :(XP$DIvY( i'XD%!WpcW$8Rn* S-Lm{&Nj}@0IHY(2*5Kq@ M T`@\ Q x!4(P ,@hɠtB#R) } Un!X"n7(EA0i0t<>`jl` p\ȐNjFY Tc"c3,9%B~bUD0Q8dŴBIǶj0`ik) s4%r4p`Zr{Ooz@BXBQ* 8FDeEYT! (cPTAuLqP -;)CL@ mAI&~GW1a} m^Db"&jAbd! H6' Df@`GC#hHztXD ;(1KpPUCe7Ѹ 8|q0NaD*dc@h$k(E dj"P eXxkB##b"3PiX} D9 P5k-%Lk2x= Y-hzN(^K9O -Ib)J0rÑѢhMm$x6(YGdBC6/hB{3`EAVg )0J=tGLBBP` 88Oh{HvqfBhĄʔU hE@WA,$&L_ytR)Nd5>GQ"t?/E5rG(@aJ~Vȭ∁T4"V뤁 XQ >X*' hcOZ*d40 B$XlKםj< "T$P`aL` ::Br_ȠvT@@D6zVkjGd d3̓ D!Q/ĨA,Ԧ~%0E(k ZD Y,Ic\|$[}:;gAV>t0C 0u䃆XT. D$&BL![ZXB(1e!#>*TtvBe l BR:J `Ia(,P #xD( eAKAVePuH'Q PU EScK܌^Ra.Se̕2*0, ^2Q<&$lLCh@ C\MX*mT E` b4**s5B̀ 3xDh.ЈPf T8 A(#HRAtEmp"_T$(H!ѣRK-eČTjs(K&{PN^P r0&T3 Bp>r3$`Xj`<* A-N(F"ug[mI0W$*i2Ď9 (™1dR(ЂbU"xZLSPT,B  ٣&:J(hذ"q] Ũ6axDEAE[@GL[ "GC ""F YѢ@) ZX%e WB E3hd`Q@t'E0CB KA`eFdKB¸CD6EibFp@0RjRBƄu`Rmʖ`cĦMGK @b [xQݐ;(9ajU u$uh + #iIYV} 5xJ VV0WEJBэ,@$ Åh ^(U",/@qm )&乪+0y-,tH,Cm;jEho.I֩|=5\.YVSrP`h T[ qPY<%= .½@JDAQ @)P ^e"U 9HPb E8|:!0Zժ䡢aw"+.Qr#a6D % gD 8 68! (ePM $TuUF#$T<  Ca < ~Y.KD)0SE$'3d'P 2djK! HVGB€/ h?H|n!PH8 TH \R%B@cbFc S5I" r Q`i A R %^d@lj7WH,%NJ dqKC}GHX:p$fykPq$!PZuGTIRyiKaFǗPpWV@aC XJE-L ihv?BB '4P@k _k,/} 'j 0TrX_`(P`aCBS B KV "BK% 0Qi,"H 9M{B9,*X)H,bTU'Z߉H%ti.sphbJEp"IƯH (!7/B@׌QTq t.AUp@Hc0sk C `B P\Joj`aZwkpB#Z(¯Q`Ҹbp"<%ǂް`ǖ IhԊ[`G∙YĄ W R"Ҋ dZ=W6V )0Q iG&6bF0tHPa8fi8 DV@AdNN!Zd4 ›OϘĨ@ Xۨ7 (( EN@lށ' ) D&4'"hY*ʉ TĄ.ҥp`A@eh, l!"i4" 0$ I A Y)A2(ȋtr s~H@11΢W-h0%/ iTUc!:!БIM9&I>jVI&5tH⼱d@T҂2I*U?RUE\F:["|) !.HՂGJ8 FER:r  4P|u4h BI䧯!Հ-_ = Nzxu@rCc!tv̍6=U&ӜRYhx" =fHؖ ֧ dt d  S&|Qh bEd%c% cð\T -H #UtX*AЯ-kq% p k6fBB1y9]TX&аӬ7#ASM\^ mT+DX(   : r2@bV_\Z-#Q `lAo0h˰RO&͠"TtTTj,d& )XH i(HC$J|9A !,y2atVĥkBA]ySv 1t!',D$PvpݔTڲiP7\@:$¢6lV8҈l(a$*jT$jK6f򢡘hB3vX"cl,(r@I 7=1U@B҆TZ)(Y:T qJE Qb)@ pIJY5B q,bDvGs  |A(/F nTJB"\m"MR!P.@+(<@L-V$E'3M_< 0d@$/KuKhr%ERy 礌%$"q̐) ,(ĵ9T Hu), M1jX4N*IrʙlEe`4~ ,0T ,LDa o@Z n7%BT{:!U`i@=1Ct@$dZU A@,1(OB m$9 YTP(UM`@V%! U @ZyChڽ Yr%5'H(" Ǒ)ul%'&t 2L(Օ_+p }@ [ FT:Ze0,`PK< *[OS_Ez ݕ&UªiZxL%}!H u-TDZ (Z4Cũ@(VA4lT5B´G|)qX" FR? ^s.UvEXFJC '$e*RBID0JXP]!ˣ0&҈tI;fDHF<@AHaCXH+R.\$3e)6%Im!@ఌNSV%D%MAd(9eVV2hFA'bMh% bHxbDVMz Zĩ(PObԠcP.]E`1A#BJ!)P @A%JhZV(!Qs@,֖)!aqGmJb_J8<0!I j) :b a * >W)`"a* "-@:;MF H $$Xb5Ppl=f)Z[_/AMkx 8MB*E'g8KNi(b[": i?bT"z CB, TRotBk8\`%@ԎQ `D"@(ād%imoB B9*iEnA<5DFKC4D1tD M@@DNq{*APMQ"B%{~-CӉ&-9X%(n ~e,FeWjBnrR @=@ (b~)*I%NxCYx %ؒne&&)5[PH:"U 6S8 @jLIR5āQt!7 Pd ( lj4dJŁ9Zx'D`Dap+*Pn )1de7F( "z%I$YA`w8M^ lJH Ab;_v0̱B+XJP3 tvd&B'R U ZDu#%J[]T^W:PDJfT`qAȸJKR+B!@GvCsJb)tm4 _pQWADD0@$+TE^P$`SRdZH$*+N*A%Td&}IPe$jUPmP }g,cDA* H"Y-*^D25UYzͅ+RҪDPDH'VW 40Nr P#Վ8R r`SJ |t`,  |v=nB`5+g`DU#AL(D+Dz:4&\-2 -, U 5#舐08!̇`0֠XFս@xJ<}P媱?[܁R_RpG @vRAۘ4HHS\-hPO£O bDDh V"$8 )?Hת@TRt&Jʢ|lc )> + 0IT,vc@.<ׄ(Ei!!a"41ك, ĭ@H ҅VGl2-sq/pN(B-Ad܄G!D ON!D&*=ȉ Dk =hP 2ᘤ A;DOՠU$C;"; R$ K8`JTd;4 ]eM b{;Kc#C?cXdB@H^2!u$J A_Z"-)!iRPfI @Z.aUpup(P)B`pL=1=ĠcJٓEҊ$AW4-Vp - i7*V= ) KIH:* BV'ةOUQG MGЍe't GN <j~",|, IX\e X(LkXQW@ hQLMc @q,ZݓOtB~bR׎H D&Xp*d b V8*.#` IfS[/e%MMk)jC3Ri&EaD! {Ei2^Wc+PPj> 1Yh CqATI%&<@h[~D8#  GQ@M J" h;UJC ә@*V[4>?E0!Jgu}ED(PCyR|Ջ /I" " M`5(5:򭠌kGP!mdf!!X N$rڔ؇UF@J VLhа2 )4rXX#APD`0 ERK N-(iE=6dBdQ  )uDTJ}ƊR,y`Sx;I:B1YNBQND2rfrBXeCiȰh!Rf%hw@YălpWi!`w.1$0:iK ]41+[ hJJHsSaJEHȱt4Z`J0(f[[:1R`>pQk- PT°ĮX6ʱ;ʁYہ0q8! Bs@ GN]4F0+{ C@TȠ%A69aFV1+? (~Mճ)J :vr6 6@AB6s~BD C&ĂC-h*Z'&Z@uE ,LU" 1 @ jZ * pv( BRP@B 1SaδjF%+F*+K7J!zB60t0@=b2O@镴QCmHH*C ."!Ph⢓ = AYh(SA('Y.W2'j Q7qStJ#I[UHWw(B v{0fHPҁ[z5%BV/STcDBgN0L9ՒKECA #$CncS@u:1`"J CDNxadJ4E\F[j\*R0)HbQi*p!%K2+lYM) Iq2$A{lc d-K2D I:X$C4R 䅘 AB1Զ$rRh$`'rrB= b^ B Eٝ c -S7u@jJa$,a.p{-^3"*>B :MU wI/)&hFQ@z@g Z]Xȳن&^BP}U^xi1.deJJw_08 lBS[ "C4PCB[cpzj?jA^T P@H]sM)h~W(bDR!#$E$)N"ސ7I,J`UW$vp + wHV[!/" P"bJbXbLS* p) :VBM0J P"Aҁb!"RRB>E8;VFBic^2L4@XXJ$E)SB(P : RPյrи[!`^E@V4\0++ ? A7 z- ##BM=r" BxXzb;SP)9}Suh'' (&Qj!lBQBM]2٢ƶ+CD.U Q`BN|FUܴE:. Gr±ЈޠV*/ ,aH) l-^"D c d@ ""JmD9&@ S#_ -i5PM& "U!-@ah<#hf D"6%+#C-HO*@BIWtʣEH{h ZJd-ݑKmz̃CEJ )aNAlSQLB+R@3)|ir b jThvLQP\T߲@T6:ZY$V`2V \ v@ȦRTq4*jZ@!@CTvXdCDlCJ~VR,@H) >Ò G5 nmK#kkյ(MʍPHpbzIb, PJdHB(-+Q20?̸jʖ]FOdb*E y"XMM9]70-\Wb{S9)"8m`ڊ#48C^!=!SԭXz_P-FRxE@t oӌE *Ii$R_FE,@YBKPD\PtXSE= /(P}bLeH A "4axQTZdzb+b"=%8f%UBɤPB5P"` ,6B.B!A"#iĶ!P@98Ple"~4WĄ|CAz<̓Uޙ"C!͸* H AN!L& /\B(*'O9tV"%iL70 qCX՛R,$_@d<"jEќD۠cAum!ˆCFv MUƸOK҆nI@6Ⓞ,1**If jPЦhR%,q$+,Hn-@kPsp @ք2~f#(M^Ja2 :P' QqD*5@QQT*BXwWh+`Q) X H0 |1MjUO i6 8g! 7ZC}@?*Bv%]Q, !Q&ᄿ$Z$&;H.ď (0C9%/o(LD7R"U 2TU i44CB$C E0" P!`E' jLNl rahHC# @P,fH!@F:qakB@P+fn4 @)*  fb Gрђ +bKJ#ZNt!&h QpT. VP*JjWSb%sG@:$;xd2CN99&h D>D𚄡QEt "}e XBT^$*!4DlxT,,$ʹbq!"g @ *h1gRZ!d&ǢUZ(yDw QR  iXB#)z&7عB/2L!6GduNxk9G~Rjmĉ Rӭ$*ilrM7t:&A] QĀ^H*3ۢ[dl QUhHLZB21`\!) YlŰP!d@bGa -am(EڠPԬ^ة`,4e zq`GΠ27'0XBݲaȖn jM$0bMF! !}g@d \ ڪVNBPo!P^UO@}eFIAHT @`o`)N=[ۺrŞ)`$E,*>$URP( $1bB:BaRJ¤Z0$:(]@c!E=ndj U8pLI4@(EH Q'b9d ,S&PMtB Vcb2 HЕ "^4$jX. >A+ )HE~YAJ$HA12qՍ"lZPH+Us0DwzR3IT ,G" Ed=0$ϊ5Xt- b +z 9tPZ4d@Yd`hpK%2hIs!}Q:b@04]: @BHpO5Y,RGh`H*h RSa 0"Q@BRE* b"aiZ QZ""hjҚ|: HN>O,H)3ӡ'Rl 2$S$6؜) d$BVijԣCA%Ī<-hP)mQZSBA@Uo裈ɲn[bIA@ =Pt+!E/ }S%)@PgxЮuF1MnJCEUGU(%bop@ ,J{ K܄%Nˀ2:@PV0HL7(hw`LVGL"`.@Z P*<` : QT!}jMe`pC,'HkBA0ʇP) ~ P8 d7 9v* Z0<^.@6 а_PP HРZϝP\ V&XRJ!0( Ebj |Pe ]A:LEbC$Q+)"hC`3Y.ba%=r )\CDЯ-(apA@z*i(4@T& ))` T&f* gl)،-`Krl3EcY/DCI6AapOOi*@"5 LGR <*EE@J8R3zEWe$cBMQQ ld!("MjISn"]IEw0r$D XAhĆ[óC@+Eq&`_aThP$;'!G!b膣@)  h(v>&$D@ O)(XJ"Ӛ@ ST4aTq(P09/A 6P$)P 48҈QC0RL@HIqP5d¯񰯊^PQJm:vJ%Q(TB[Ept%N"- a%@TT`XX@^heR2a5)I}TadD`@0jH%^IXth.xM0L~5"*J4 h 1)hlT%CZKF:. P,5À^$,-"Q8R{ ^'v.c#@>6 !h 2I@FM U "Ê&%'@*p7N. u=EBKPGڸ؄A&X :$BaxRHBLvVh"(OxBo'[" ap`ۓdo] ж΀t%$IJ*7$*C ā[@.!F GjR`[+F|' /4 X#eܩtA |c艴$1ǥ`AZ"02Hjk43]"HT6"zhcJe=(]Yu'("ltqcD2E!1AQaq?-ÈU ʤ2ԈߢAa Gj=? %hmPE@[aeST JSC{Q}` '-ܭPQ5t)ˡC!fuƋC"$|r]AT؁ a `H[] >HvE]"bԵPX sa4bBRV{@­P 46C(A,ƴs @^T440X @(B@$]U(VP9x_@Lt!nJ"`"dh)*T uI¢ e(PDԇPbh*Ya XcXAC&,)(r8$,B i Cf jz)42Ѳ Az"<ש~/( FtjD)q :Z4gcF%MzHkH>i tyM0E>aEFO(^A2 "EDT  Mm#1 `9Ӎ(,[6iDP\X6Nx[1 ZPzOQB+mbS&cB䅕iʓP zdll4@'KE 0TT$ ̵sY´ ˇHd@ ]->hTyfBCQQ)Kj!&J Q_W: YrMu9E9TF!o 10^=:,-(-4=)B9Ej4[хA|@&z<K]i$PB x&%#DHb[*M 胐 P ~  "PG^(C"Y+q6 Tx YN0p^FT*#J D![. (]xN RP $."  3&i B0w B @V% XZ0PA2)nV(Ұ'P`G1U[$8@OpVă)/!"?ZX~CSUUS2`zdkȡ@U(?LkZBn6$F4v2FoH 6 &``u&zHuh-P5Z<bwkUZ2X)ij*b¬*?Pw_ [(E(Ph2$ЂAS X`R=bP+[Vh2yF6[(F3_>d({@LF&B(֪QP"ђEP  s#”[-E-u]8!{Ԣ߱\eI.Z'4Lb 4]I-Vho "N*wW0H(q0i=H``#EBc@JE o/@&O" l@X"h=͠7p6& Zu *mJQUJbk G$ZTi-pQi@dTF3wB`@ SVN CgЃx0!Pʣ~W@0![ {^c[NU6G)AUf;>"@DUJqw@Kܶ`7$m c0ҌS=T~$v_TCJA;@F$hȫCU=41}k8`RVi-ck Y )Cky iPAsBQ*9GĝQ >OM2=8  gV$esIF>ptD1Z2!A&A!`A- }J8 A, ˱`PG+&:gY+IAҴp0eF$@ *g*$$vm mQ@[88Y11aD,Ddhe0N`fZt/ $nub)l;X$"UDRi4 m U ia#|$! XXqt+#>E,td RXB`ȪbLh)cDb-6RFQT%( Aff"B\""C#%WD.iȞ% TA@YiZ(@N\E&q*CBcruLж(R e ECaD4VEO`MWhBQ* B [[Oc(2(:T1 ;F/Š( P`$Fy3J -TG!%6LaPi16k/:ºb-zZbuJ|Bt/@#wZsD~ V DlдCT&BL&@L$Y@*^B@UZq#͜Z 7RQ)V ND\!$T2Az.@%ސQTVJ(0pp@Y6f:LSk%;M1&jdI@`i:*9QT)M-8 )#f>*;dePb֙5`| 6 0hG1'*ИE#&Q1Q0"U P-xpo !hP :8jJ)lZHC!lA5@fH_XYa%%h'x b ,@b|%>$:ʰ rpYEHFFEk:@`=&2 LvRZgV(j^ϢN [L4XXC "NTVkH0v#l^#H* "JiDoXB)~BS:&:24R`Lڰmz+L FL(FVU3 RՁ`EB@)Dz%T,T {TA)h.%T!'WǡUW&C@XK҄JA` @emB)>D|&pHx6p/N@(8VD[ԘRZcfKRB&U9Ӽ"UH'Ch)& Na!gi:t"|D&@$3*؂ :E9_EF<a[H7`gK AI+M=)jU a ' "Ԟ HሴHt2y) d jh H5dIqMhO!aySI@UH ӁДn5U,j %dҒpUqk6J(t:Y4i0 X+Y^`BVHd$4%dZzA){HQ_r ~5Uy! [k!pjBKvcMIqA&C?"J(QBH,AD_h2ؽΑzbIg,8D4] k#a ]0H̙D4_qK2QG_$N\@jJƗ!g(([4 Qс]#(,9Sl (jAkN{$diIՊ HUUAGTP&`l I da0 ו:@Aqw5Ps5J>*~- RRb79\i &3Z0jQis>|ZBAaN: @R](B*ҞQWC<D"aēѸ 'b$Y (&dv*۱ EJZðLXĐHA{HBUZ b<* *[hg@te$Yl/X a)v`@@"̥ (FA= rW,˥V8w޺PT3h*HV$iEzOZdP hf @yP4?)8DL&dǍ3HGz_*ΕkT Q=Z\(u q ((QpiP "7=8P`€(AavRT -]fhVaHeGPHΜ8#>iD$Q YgX e!P;a7ՙR.R B`$q*Ki0r}%J h]8Qtms`a҇i PQC%MϲP4I T) CD~2֠͞ZP` W4v%lj㱙e!; jDE%+P(<{*}tl3QhW|nF`.X* t.I:bYA BE'C=bA Aؘ{d0À1aD `@"e1,4ҹ^7 1̭~ BU =X*Rt2D}D)x5^A8X$F)K2.v!bK$ZF1v4gD~PQ {^XV,Az+ DfGS:m%n R 2CO2+/ISظ/10P()gO +;jIZjX: Ch.E2,ٹ&yW bZpAڡkB# [ Fw/P_(p #.+sÕ`M.0]t"quhrs򀄢gFT!(AYzɏ&hn2G!4AQ$ldfqBۅ ?A_ԉPWksHZe6|ƕG0KC(rB! 1"+@@4 J(f`UTq v⑆OyѨ)BNSP1e TIHH@-{5WD66"QD7JFŴ@0 (FǤEWrr9N 0.m@ $*v&Mj@BDIثoo%F!hhSRw H5lLQ!UW+脼i3p\ 7@͋MfՍPYh6^15<bSid"$#ŀ~@JL w 7Ʃq>b.S\MU-a(p6R-p2ip.h$PQ\ɋ@@$h98qP@3.e4XheBTVB "f@ᅢ\PL4DPS@@y[G/b~&s8I3fˬҔ*(RlZIE!0 l%drm z3U䚱Cjh0D2e`R6`zK=+h~NܧIUs}mE{0xgݺ0B VeiQ%0=!k-PH?C#4( KF_ Uf4h؏HfZ^pzH 2J З77*Jm-F*N/@blPzzmt]*^U/ӧACJ5dB=CCX kP}k!lJ糓5٢l* IT-%ܶa uܗ a4 ]B,$t @ztt4# XE %zDL6, ;tN@Bh ϬNszNU Rqf$0+ʁ{L" Fap5 (4Q+H`!OzXQZK^ODl1C.ҭ<X`ZI8L/Hy"aYiqC 붘eK`,If'T%[F1t$IXi3Pgf  wޛ@ `4D\ GyZ%0Bu0+3.X-1`koHȠM4$vn$4b{*\.< 4H"@caR_z7X%,ʙ0ۄ j>* @TԸށF#ǤP\ #OE(PFOGMł Hɘ81kAhQ=)Z$`H̐T ힳd5 SWBܹ j`|@]3i\+ԋT$ъ %(0 -f!L!ԆU1Rs!2`* E@5B5 1bA0 Yy4EQh@@jiC5y(:8F:(A;F( z^'tT S PI g kReTtH\xDՠ2% RR-,Z- EDV!4AX*IQ6@B+ BLK)JĴB 7MǼWxV_x@9~KЁ "Ă,0>>ɧSAkmP  U|kp`\1cթmoa+@AtG -UP!4Ur6Ɛ REGOY ydjh$Z', L`-\xТ B9Fr`A8Rl*D'hDӺpKD cv-Dm"qe ّ EË>!-xTx%><T 扶@"AE!K_,, Y!7P"%$<.lVsN{tjRU(,."xWXO\-%W`MOC䏠 ALen@~p@Є`@-$DH&H(|8*@!PD.*lBoH(A}qNd5@Z @UXXz 䩐4vi\CBHIȈͽamTkQ|HTtB-iR7B! Qh5I+} ^eɸub t}86b)Q*4۩5 @P Y\`( F†(ѕTLPiS(aJRPBC.*:.?ҟAx:JetpAFF0y hD4h@|+"8P4آ9$6GRD x6ȮJǐh 5 }zPHL!II( L0Pĕn#!@CH)tl!X';@U1}N}(hȆP<1p*땱j(E} J1bI3hX ,0˷j"YX>H D0 < A5T$&P`}YF Y. '9l"aHϙӢPB Z+%RM ԁ H!(K*ɚHP( <" PA"@Jn@MD1 ȡCc_PzP4wVf> v: "\q@+ᒜ+M &$i QsMxf ,P& x4[Q'JdDMYIW(cs4 }Y` KwzV&"rh` fs/1jK ^&H5š g̘ M)ĢOńg̵zs׊NЧ . kUdIh,QɌ`qvLP2k&o pJ/0V*( zy q Pj8DAUj`&#UH{"TfWG*#S%UFP +WJXP (VsTOL5.jd!e|(%@PA BAeT0R2 Arj$$0=SDL^Zp L0d Ҋ4*%PaxK1v%QΤ;IxfvR4"b04J$\!S7jF:G ՠHփbI<B!݈x*<4  [6OF[b0 !11TѮ|!u,n, vbDBr+A ! ڵEk!z kpBZVJήu, uH`*Axjd_imyWH8nD װkC0!% *GA("%Z %g *3`G (d\PIU8}v@^Phrrɬ5X0\xEaXߨ@AC.}TkQPU5Mpfs DV(GP\b*dzq6JU`Hb%X a E$4XVqGj(Z5H(k"zaTAcpc1@0DB膈XTMx^HS5UV&$A|hF 3 T+-sJL); X"R `}ä0V@qhS0+&Mi!gjeƆP):%:%E$1To :9[gzX"I7+@,`PȰD^Q²(ǀv bث.#-3`!LAtcV<|;GTS#,[^0`tz!s^-\cx9ӹzyBDDeyn_@Ѳlkj y$epLTaA5>W4V)׽h MjPA!ik@ᩛ!aR%jQ)T F "z99X (6,Vˀ1D4f}"W1 ֪H N};PuF ^l,rpra)krq5S%p7?UPUrUChR  $X.vQW3(iHq'RfCaG< TR'3 .FCOUz Df%A !#DT"N>ȵH%Z@P2"dSUQ< `&) AKcQ`l/Z *~ Dx޲Z# dBD AJj`*`'u%Ʒ6M;FP"gpp D[AJ0Z LSp#=@Bd95L8*B0yyA3Sϖ 1A^G VhEX NxFEϮiWPGd$P@OnD!jQ:)%6C0_V *Y)3 t/88D@MZDa0Zȟ3a wqcRJ"5 )^VHkeLvۀb0Z]ytDAj\@ʅ`d "&D( +'Jlf Q,[碍.0\7h`'sDo7#(Hzk Z }HDNWb0x$GQJQUN0U/2ĕED~+*Z1T !@-ir 't$ɤqh-")6iAt4GdSd 9Jj 3m@![AV-X%Ta*Rҫ)tR!@H[M`$ oT]C!2T.( T&;}e1]"OKM݀ *x5 Ђ}AQ]4At/!< ,01t `4|&\`r,dgtmYVRh@Q1MP>$8 uD&0;%QE7 l:«,@K{HldZ lY NZ`4Ҫj59 F-<%,UA}Luoks'-QHz 3$ *8T>7Wq`lTgvY4?)fqŠ?ڀ$j8XJ@ @dždzєIP$0#)3 ԓ0%FrRH%&a 6 E"@'&KN5aI?Y=n@0첹*|c riɧԊ+MJ]hT6*Lmc:zH>+bX A)HNy*-.P uk@`T~b\Apw/C>p}iB+% Ҿ Nz!Q )+&aRPD:P; G mQ^FpjtM!$4# "r =jBfR ܀"8Q2  (X9Z *((4!JW"BqI%A4 8ʂ}"C:AT3SdH93rdL@6# |%-6i9+WvFڔ&[i(:mjbECM$/fL_X@:hYh{r1ah^1EE =tY HPKH,1u*-L2t 97&p**/B#/h.h/C{`gv`/?lӣyښ; QLEMnȌ . H XP0XE . $"ҫr5ePjJ g"NFI3رɜekƃ AaYpH7 !ΥPh4"[MESsӕ8T:@}G B. TGv\<хhd(AP+VHjI$&^D];@"RU(g4CeH|$cp bp$ Mr@Z RA2 0T3D!ۥ iۂGxq>"'1ptz#٨Rs #@4lQ "T%]UtՄm%btܘgT*#l|A@a &- (P6kTA8*M !14es3%jEP5 @QaƬ2<^!Hbq9",&!@b`:$JU -% l!ؖ|D!`H*F fgDc+TH ̣gfs+j^~ @D0Ud($mfʒX*t4z9J4=cK yk[! 3!n:}1ř%ѐpPXF%BhtxH'2ȕY"f UcJI%+!3)i6Z[4 WE O PUVXV_#4XDw (a/Tȑ'1"XtjɅ7AT)i@a1r, F_T rPv.P#hѳ x`7MjHfx"'.:#7LSF/qm-ߤ489qKēn(}JU~fdP~Iܕ۾Wm.oo{K877dӇ# PF)&NuUhx4P— ǴG2+o(hS )LdVO9Z A%=bE$}*Sq,â"@EfQ"T!d`3d kdM3C Sֱ@"ֶ~Ke2ח )rbUz0w&䭕U #wi)X%^pX ,r8ӔD"&XD $>΀@H*Q HjQP)yb3U2Ax&4C ;A+;l7EHPh"E@!U\XUDBYtJPVZE"TlB艥h$EHJŀf!^ h 1ipN@`xlO,8! V3^JDhc ȗZW4 Ŭ ǦCRª4u`3Z2lQvУ}!@ ;]iSyF|Tr%Vƀ Z< C|kH;hmހ*gyC3їₐvqsBlA ℼ°Ä *byz=ꆃ"Wdr"P/8jJT oE!jO>"S A%#h7Y#Yi;J fuƄ:^ yǼ h4fwÒD:URM^V\;M#cȣ1ERN:F޺-+<EPx=pxI`VUL'KZ@pTif."KS!UT+(HE\ň@ $ f Z[%Xj)(  [.Aة')Q@:S8rP1( KADH+ e+u`lצ4APNd.:'D  -S [] \a1u9<(&HQ2A 'D0fg -kB&P+Eu6cT8 J0> RpP.F;ˡ=8P1Rd`BJuOIb-"4*`< ܁~VaQ,GFkGůDҽ<58 -Òu5RI:u (7-T8X|SiA:xG ŮU6P{ {s֤kJ mHE&h(؛ V2ݵ{mjⴿwYJ%M;,VvAީC:ThOTah@z WV0Vި&,hm+q\HǩHXRӂTD-.M@ F AJhapWƒTGɀb&N@H>6 24HILn:.I(2(bJ%' !r;3; F=T(Թlܡ@09  W`IIf>#ArMLAu_EB見\2P@"V 5 1c CJ)C:Q.`!q6t+}{fzW<{P`y ˠѹM8R!SwP$vJIn@}h4B>1]6$̆iu(TV<0L؍& [fE$A.@0 *|Pɥ?'p"ӳ#x,aɌlAdSacἝ^F]aM{цMX[I(D p wVdUdVrLbz`40$C*=[d 28\,(һ):96xTV8 Szբ"=7h/dkA1WTÐÉ0E[ 9 u$P+\H=u 2(smK;puh/骃!P8 EhU)u`WETR'-$ pF[ZpQB,!;2UTLxX`+RS}oD2nebc a'FBneDRdgTAؕm:uA  ":G$ %nғe><՘OT+3)UFHlNXQwk^B}NId*PSP+$4mur>7WW(0+*m/ Gu#cAD,Kf0*g0\7  o~ G,m$R|ѤHdUvUTJ+$*:!6T#VtT4gБĒLvX.@!190ZP1)ɖ(mUs2b #!;Ui/^$P9|.gaRj$vI@X툀N@ *۲P]I2tӨ\u@4" E«yMђ)B z xEtSĄuFphK;Y)7('&9hPQNdp+EX@T^Ԗ3Tl DT#ROZ%BX4h EyЀjRB)]Ô`IIP$ITt*+#jHT%IcKVԣH$b(BZYQh*bWd0@ Atȃc "?8\! AwH(倍ơSXMML, zɞ2\4%k{\XNJ`8Bj區M QDDÃ`WE RPM?uC"'+jx3T2F%1 E@TO> M`pz$)Q84*Di4C:js~  *TGH\q As2X u$D[̀0 q'"JKD+ ]9y62"/T;JQ7 XLNs|aA]ZLa)9qY0 BHP!RgdO)6,fK6NLML Ff81r@iXy򛪩DԲk^\4B "$@Wb!0`@ȷcԊ FS\AT'6e%\aE`̦=uFI 1F0>"̂ H)ZeB TAƜ; $0HTI.  iedJ_^jW-=l$~G((*"a$ 0޺@ A=I$`d?EY!iX,JE2XFQ 7((#qR)dD@X€*D%@SDnUM7r %x"qb)c(bR1T uGD [.ӌuz"cT` `p0ZSˁIO 1"B  50_Q' ,*538,(~OthWi`F ߴ@2 HMDE,qK)b \f;1g@u s$*t#12RBFPctDM6r23*e! i^knHnԁTbia)PHVc[q$z( t%U$v3DDPk EV%S5u ƺ$)[ͦRe1 c5\%a^3(MHL$N+&1Qm`iV#Jm`/`&I B *B@56+rF$XQ`a>$alԷ` zESD`VT&34tX D"FucωOqLCC^^c`5 8hV:(2 9Ocybt!6H:٦ 1ZK@!1iB`(A W)Zⶊ\ hKsq! R nRF7[ZuZe&O#x(XD@yY@iQSzzG Y`j( PC>+*0Z}PF7 i"J8!g @tc_L"vc!)_e)XatwtG,"Cq'~FDe|@xѭ[4<6$ B[#f`CpvYPk<l!HE.u 4YcQ̬p+@E/1X @/B_II8H, <)JA\z(BJ)jDKbܘ; A$LLv+2͈PQ61is(@*R sL"&9Bݹ]89Z PpH4Q3_@F,"sx&(++#U I,!] B!@*Z Ԑā KʃpfK%1o%Od3!8E _&LW AF‚^.  \Pp\ BcU-S$JCi6TԧÕ'M"2UAjEOUX]H>٩FR 4JHja BDhD"3KEJ}JP\ Dh gMAAPPTB `, Nh(C ٨k! 28@ȇ.DUC2"$Xdo=)тd8jXxx} vHPc2aE"| 33tPQFc1ҙTP "{a"ŅT0+lEk}(Xiɥ*@08m3]r}`QAMةr1 ( gH~%)R!:RF| @ZAQ@& [ DP_Cq@0Fp\H FSAz>R_vp('ӭWLAXnzf)1%Q fA ( HM i Aq0^Zl(P-Y|&RPP ӕb9R\"Jzi!%\dUP( FCA8Ut:a UUaz e֡ F) !$HT"|-XIDPTG+ FTFCVb)BF(CDVZbK,U6S@PāQ`&ͰT/C+Y&fj0~!0"rtGF/Ca1-X@$F@ )"z% @QQ!)BpaӍD[f]uYvMQn$84q^8!e/\2$f2;3TmNtp)\.K9 /<9PҤaA"9!!5ybT ZQ4a_Wl\bPNDEGUn!~VB áR ^%@$(#x(:3 GeCB&@X#YR E+!YjAvvUP0>M!!-%,\$@gF I)*-GdXpq IWcu]QHB'.#'H0$:9:d-a^,ܴ@_$LQ>}9@_*1 C N Tc'Yf [ ԓU 5ˁTr쐯J4B"!H@EL"5kXD 8@@\ B{K} Z*0LC5?;UkD^x-~QjREF<;$ZA* җAUְF HW3jNN@R!W DQhJ9Q$ 5eJ* r^Ͱ,$(S`U9E2(^M@0ap$Aq+BTA戬t 7q=ˀ #z9NV)Cmpn I5)bIp}EF@zAH'2("9RbXrͣ* $p@-0) O L@XD@*M")@KƁ*:UPxo& JOXwJ 5YrԦ>ԱR"s :NV k4ؕ#5%Q8RsUSR 4BEp662x0.J-Xh0u0†*p_g ٔfz"4=(:0m20)c}#-#Hph֙HXMV#$<"*N)3XE6xs (LMB/%+) ]PĈ+I, u/x]zhTE 9DXڃZrXq9D@S"U)C; AW\ai&#f du6_LC \`^7O )QsPX "8Y҇ Dxق@HR[",Z!ҀXr0hIJ$`%LР<K%ytɔԌN+D-!G+$)""ډKć3Ca/ҝab="I/Æ QB!N #ʿT'RbH $ `5-mbVZ4 WB@$a@)H٦P]A? Ҧ!h .k)hȡօd*C9&>P,h mcaA,HQpiyRV$@'Ĵ Fi#CD%GPaJ䊹&^l*D@p mPXD#U~СaTO@~1<ϣ;XTP֗ pN.aL(-2ѭyCS`HhI&D ,؞IY k2uJr`(/ RD*6` jipAQB^BcD. =Zd5ЧChXW`Yh̔CؠuZBP^N$F^BGxwdv% )C ́Liʼ9][PDtK"33HU}@䔠S6m ;hգJd-]Z%2x; 4y bnG|Cx)9(;98t9UwЙGu ?P IbKdCH-r2AD8#KF+ "& Y?&4~*'%cB7p 6ܮjV\Mv0rÐ̰' (L{PRAIbh$v7cn9⭳8pGX$UHQ͏ rAMVhx(p;A(X&7\E"&Y06\@kVQ CGYW]"%r1p~ #yf# KA P]XF^n~dp~2֐#HPPz"]Nf\.VIU1Y*ےN$ SS~Ez1 Gp =EƐxc0\ѶEnߋʯAUx)9g֡5U@-n$` hHd  KJɚnPˍ6y"5a;fZ ;IAKUJN°4G;@QѨ"!)J%1( %@k PIPRN50>?GPDj-Pb>jH?CI$ (jB*W ‡2`^Y6 }h[rHyJ #rRUP06?JÈb %LؓQh9Vc!("l.ph=iH u1 £${c/y #`ؐAJ\\n-1Z(DUQ}%@zyB4\"NV@HW\e6p'&-HcTAݢ&t7O|E}[ `mm(FȆ"Dl9bhit.?Y&@+fV"G9 )'C{&$PBԘrE 恶pX*Y8@2WP% HlzKAfE ]9t+Thu88 T- v (\-@*t !2*  6@&MTe8 `T4cpi& Pt\ Ƹ&5wA>#eX^0*pk GF/MLɬ0H $w^6С)Xu$C԰%0qS ŋ(0OPS b(SX(Վ%c h'K>hZK0A>XB\3E 9YtFOtBԗJDa9B0Kr5 NP 7Qq&3!CXkGЍ"ۈ Щ@ G Pu(ZP($Uq9Zғi97uXhI2N(5 c%}$Izj)xJn5\RS#E} (@+.[Q+UvH" $Iғuh=„,RjgEF+ Ҥ&^x$7! c+AO!4΂/Fw]$IRdj ʂaIܤ@IZm 8[E@GIoE`&*z`*m{bˆlcUrH%$ZH+57fB,  Q(kD[Ƌi(ZkVH<- 4!>;Q~MD"}R UA@ ʁF jOC[5e4 `{8 !f8@EK,VX@n ŧAph )譧^9 , c{A!`Z\<1B#'zRS!SRHh+)Z"bhB<! Z6[@s6 R:U2/! }%!bq8pS!qK y( dtJ=-$;3 %Β\; ]VQ\8j>3K&.l"/0zBh T@gN`H M頡c8x %}OC Aj ] hƄpm  W$KW:'qLk܅HF@HNF=weVeQ% M 8EѮ <ـmꬣEPn@.i$-9!4Cir`+!p v6Q%̅.EQpL#ۖ ?mKM ] F`'"iC)bv@$s`1`ZT Kk.E!h'IԒLܹ)n6uCr#4[ 4l< u0wj /]^  m C)D(ͷ`0>7hQ1Hj@avRB%hAb0_D#Qb+=Z)b hT)O?M\)!LRRJ"kG!N#Vp<>+ ATQJI#?PE A-GJMҍ Is%PE) }LjȄI"%vB=H4D4 KA-Ho%-ԭDҮ#"L%PJ A !CSU@˸k? k") P9 | 6=sc(#K`q8Rx`$Jw 'Y0jEIY&6V PTC0L@8BR*Sg1S`. -b_CUINN/by\C,JJA! $&+u=BJ' O(oF_}-Qm+R߉A)+AΈ @CRJaGQhi F 8P3f_PiOϝyAIbErpۖ dD&܎BK%%1=vY`k^%z"pb6rH0͊oZI 3Q ]8(SB PWYö\5zbOU8,BY)Q ,5jb4)+<L<`] -C}Z>68 )ڧL<<cZD͔6&$kU &-pdZ5L`WBSzR_t)`S""֍])xX'.Tn+MJXHH A 98(a|!4P@X(Xф"aBi(8@>67f%A>@ %@2vbCP hX$= fx)gٍ @D!6C GHlIRY}p>[0j:ӊzQu0F=`:l *˲@s @t"Pj h ^ױ) Ј>n0ʬiEYAD@C Oe07\:efkPDвȘLjLFeg5t@'1TPHyC6z"Mh E\ޏ/[z/j cpw pT^a,(ղ8#H(5&\~(jr$ GrIQTXA4ak 8*~[m%PU,Ʀ'BGFpɡ 7@jGBqvhN^@<@3 { $x/+Zjc@ioq@0, Pr&”t@jЯҚRS9W^-EP {F0W(Y4: &DjT'f3ƈ@"E@Y*&" a(Tlx5BPD`)Qb>'̒ \| R i)ɒ;x&ڈZ eB) 90 R$fYŒ'MzzI}o\m AP% C^a0chrPO*@LQ5H`$Um0G#l`*V!~C6abA#踪m9[?ߚY¡@xeb5;&$݈ =kA Ex7M kEO L=R>LGjPwĢpz6C򘰋E' Aӟ%2ldp.NHe\N(+$YJи4/_zpWF|)ţWb>%Ta$@^a %$*)yaG lLB2BNq*<{=aG  ]$4G$vP)ԢQjP!cP T @f PAZӚ<@EKVǤ@ z)X(V +Qi sVLj8tE @7]i0Q #| )6@cTQ U@i hTD GB-+ThU1"d+ Iy=(zX^܉5$ 1`*+m*` ٣Hbҁ̰,hIh8cx>Gϰ`pZ PBsȯ2`TITB 腕hөxLP5 ۊ^Dc=:!ҧ "a$RC%)r `)&M )TClA@Ԩ 0.ALйuA0>&zPbXhV8j#VdԪN@)%Bme_ T a+$%*uL6"9 2}g0#+(Vʡ dm8.7)$]5U)u. Lf: UuҀyĂJ,4|6%ygJ+TDs S&YǔPINsFܭ4Qƛ*/Dҗ]k1(r RD $+@(TN0V0ڢ2ZфA:$ 509\8̣0S  aD[P+5_(R%UA8(2m*_1cMM[Xbz9 vDtۣI@ұ@HV1!TJ2BiX ` 5` =Q_1'=aZVFN*" |-!TAlBJIʾ HI4^'pTgKI')ho8V-rU-Prc TX$-0 .!ɜ4p4"Pf0ME)  E%.ds*k ]' t:JLvG\0 ޲F i"g|ZyƳXaB[(h8` Z+;hTJ=PDJ@eiNG*ф d,$$mr(Ĥr'DRCFM:ȮKQ"AmO j>X(uSEb19xl(Y`䨚e*hNA(q'@R ^zK֧,Dl1cCDUcC# NJ/A5@Z*@h>?T|bdp:` 'FP@HӡEq50ҴcsEbF"7)L/Uk0!LV xJZЄˀꅛB%JFVx`'$jKiu*1*CrT]f1FH$ o@_N7$%,J8F1)JT6P #PHU@Y2zI"0)lgSU)Cf@\-W ( b\^@B ^HKA`& %@@a%alrhS%xV.uD h\ C: M4 B LW PF점|[J>̪EJM)4zdZ*D4ɑ `U` ުUi ;SgZ d NO{굄Kf+tLd-- SaЍ>ክ8q= J<8@PsC[4|Q$ R :2L-N>`CPv `w2+Ax] DLb.` s sЪ syni5! CT\eDʮrv i%4!QTzcqA"& !M J0j$bKWiTmJ{@]#@uo#Vs-Bz)w(,ۄ\\Y2(B-0 v#85Sw[VRE(Qi *(9;*Z8 ,Am(A(p!a X[ةQcH5+-R(U'Hê1LX#eEx}&tdW: K/,qy͑49n]^ ouak]SShAZXQ'9(etW I PVݜ[0ɉsʐ'"Yc(,CE\4Q&]jFElPYh)HOD>5(D1(b wXzJsƃC*#UC%<# 5a/RՐ%5 ݖ t@]P@Р, -"$yԒ3H.А=*NY! 8BzZ(^Y3H\ pu0`kcFʤ(( S֤ "05 7P ~mȂ>aF;W P*R2P` .BHk/bxT1DA-V٦Ȗ6T!$* }~!P@]iˏ顙l(bz'"$6Xe^ cdB#1 4Ívsrc/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/regression/settings_comparison.toml000664 001750 001750 00000004172 15164251010 037270 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0[general] folder_with_files_to_check = "FilesToTest" problematic_files_path = "ProblematicSVG" # Where to store problematic files, in which conversion failed(e.g. due program crash) output_folder = "BrokenSVG" # Place where to save files(Input and output that show differences) ignored_files_path = "IgnoredSVG" # Place where to save ignored files timeout = 120 limit_threads = 0 # 0 will use all available threads px_size_of_generated_file = 400 ignore_conversion_step = false # Ignore step with conversion files from svg to png, just compare files ignore_similarity_checking_step = false # Useful to finding problems with generating files ignore_thorvg_not_supported_items = true # Thorvg not supports files with text, filters max_difference = 5 # Bigger similarity will show only broken files that are completely different, looks that 0-100 is quite reasonable range limit_files = 0 # Limit checked files, useful if you are just wanting to check how app works, 0 will remove limit of checked files remove_files_from_output_folder_at_start = true # Useful if you run app with different settings and you don't want to remove files one by one debug_show_always_output = false # Allows to find broken files return_error_when_finding_invalid_files = false # When finding invalid files(broken or problematic) app will close with status 1 remove_problematic_files_after_copying = false # Remove from output folder problematic svg files remove_broken_files_after_copying = false # Remove from output folder broken svg files remove_ignored_files_after_copying = false # Removes not supported folders after copying remove_generated_png_files_at_end = false # Remove all png from output folder at end lottie_path = "" lottie_broken_files_path = "" lottie_test = false thorvg_path = "" thorvg_broken_files_path = "" thorvg_test = false [first_tool] name = "thorvg_pr" path = "./build/src/tools/svg2png/svg2png" png_name_ending = "_thorvg_pr.png" arguments = "{FILE} -r {SIZE}x{SIZE} -b ffffff" [other_tool] name = "thorvg_develop" path = "./thorvg_develop/build/src/tools/svg2png/svg2png" png_name_ending = "_thorvg_develop.png" arguments = "{FILE} -r {SIZE}x{SIZE} -b ffffff" jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-from-sync-iterator-prototype.inc.h000664 001750 001750 00000002335 15164251010 055536 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %AsyncFromSyncIteratorPrototype% built-in description (AsyncFunction.prototype) */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_NEXT, ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_NEXT, 1, 1) ROUTINE (LIT_MAGIC_STRING_RETURN, ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_RETURN, 1, 1) ROUTINE (LIT_MAGIC_STRING_THROW, ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_THROW, 1, 1) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.h000664 001750 001750 00000001630 15164251010 051731 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BUILTIN_FUNCTION_PROTOTYPE_H #define ECMA_BUILTIN_FUNCTION_PROTOTYPE_H #include "ecma-globals.h" ecma_value_t ecma_builtin_function_prototype_object_apply (ecma_object_t *func_obj_p, ecma_value_t arg1, ecma_value_t arg2); #endif /* !ECMA_BUILTIN_FUNCTION_PROTOTYPE_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-intrinsic.inc.h000664 001750 001750 00000003377 15164251010 050625 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Intrinsic built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_INTERNAL_MAGIC_STRING_ARRAY_PROTOTYPE_VALUES, ECMA_INTRINSIC_ARRAY_PROTOTYPE_VALUES, 0, 0) ROUTINE (LIT_INTERNAL_MAGIC_STRING_TYPEDARRAY_PROTOTYPE_VALUES, ECMA_INTRINSIC_TYPEDARRAY_PROTOTYPE_VALUES, 0, 0) ROUTINE (LIT_INTERNAL_MAGIC_STRING_SET_PROTOTYPE_VALUES, ECMA_INTRINSIC_SET_PROTOTYPE_VALUES, 0, 0) ROUTINE (LIT_INTERNAL_MAGIC_STRING_MAP_PROTOTYPE_ENTRIES, ECMA_INTRINSIC_MAP_PROTOTYPE_ENTRIES, 0, 0) ROUTINE (LIT_MAGIC_STRING_TRIM_START, ECMA_INTRINSIC_STRING_TRIM_START, 0, 0) ROUTINE (LIT_MAGIC_STRING_TRIM_END, ECMA_INTRINSIC_STRING_TRIM_END, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_INTRINSIC_ARRAY_TO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_UTC_STRING_UL, ECMA_INTRINSIC_DATE_TO_UTC_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_PARSE_FLOAT, ECMA_INTRINSIC_PARSE_FLOAT, 1, 1) ROUTINE (LIT_MAGIC_STRING_PARSE_INT, ECMA_INTRINSIC_PARSE_INT, 2, 2) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.inc.h000664 001750 001750 00000004334 15164251010 052126 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Object.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.2.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_OBJECT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) ACCESSOR_READ_WRITE (LIT_MAGIC_STRING__PROTO__, ECMA_OBJECT_PROTOTYPE_GET_PROTO, ECMA_OBJECT_PROTOTYPE_SET_PROTO, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_OBJECT_PROTOTYPE_TO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_VALUE_OF_UL, ECMA_OBJECT_PROTOTYPE_VALUE_OF, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, ECMA_OBJECT_PROTOTYPE_TO_LOCALE_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_HAS_OWN_PROPERTY_UL, ECMA_OBJECT_PROTOTYPE_HAS_OWN_PROPERTY, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_PROTOTYPE_OF_UL, ECMA_OBJECT_PROTOTYPE_IS_PROTOTYPE_OF, 1, 1) ROUTINE (LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL, ECMA_OBJECT_PROTOTYPE_PROPERTY_IS_ENUMERABLE, 1, 1) #if JERRY_BUILTIN_ANNEXB ROUTINE (LIT_MAGIC_STRING_DEFINE_GETTER, ECMA_OBJECT_PROTOTYPE_DEFINE_GETTER, 2, 2) ROUTINE (LIT_MAGIC_STRING_DEFINE_SETTER, ECMA_OBJECT_PROTOTYPE_DEFINE_SETTER, 2, 2) ROUTINE (LIT_MAGIC_STRING_LOOKUP_GETTER, ECMA_OBJECT_PROTOTYPE_LOOKUP_GETTER, 1, 1) ROUTINE (LIT_MAGIC_STRING_LOOKUP_SETTER, ECMA_OBJECT_PROTOTYPE_LOOKUP_SETTER, 1, 1) #endif /* JERRY_BUILTIN_ANNEXB*/ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-generator-function.inc.h000664 001750 001750 00000002176 15164251010 052430 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %GeneratorFunction% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* ECMA-262 v6, 25.2.2 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_GENERATOR_FUNCTION_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.2.2.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.2.2.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_GENERATOR, ECMA_PROPERTY_FIXED) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.cpp000664 001750 001750 00000131232 15164251010 051747 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtin-regexp.inc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jcontext.h" #include "jrt-libc-includes.h" #include "jrt.h" #include "lit-char-helpers.h" #include "lit-strings.h" #if JERRY_BUILTIN_REGEXP #include "ecma-regexp-object.h" #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_STRING #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_STRING_PROTOTYPE_ROUTINE_START = 0, /* Note: These 4 routines MUST be in this order */ ECMA_STRING_PROTOTYPE_TO_STRING, ECMA_STRING_PROTOTYPE_VALUE_OF, ECMA_STRING_PROTOTYPE_CHAR_AT, ECMA_STRING_PROTOTYPE_CHAR_CODE_AT, ECMA_STRING_PROTOTYPE_CONCAT, ECMA_STRING_PROTOTYPE_SLICE, ECMA_STRING_PROTOTYPE_AT, ECMA_STRING_PROTOTYPE_LOCALE_COMPARE, ECMA_STRING_PROTOTYPE_MATCH, ECMA_STRING_PROTOTYPE_REPLACE, ECMA_STRING_PROTOTYPE_SEARCH, ECMA_STRING_PROTOTYPE_SPLIT, ECMA_STRING_PROTOTYPE_SUBSTRING, ECMA_STRING_PROTOTYPE_TO_LOWER_CASE, ECMA_STRING_PROTOTYPE_TO_LOCAL_LOWER_CASE, ECMA_STRING_PROTOTYPE_TO_UPPER_CASE, ECMA_STRING_PROTOTYPE_TO_LOCAL_UPPER_CASE, ECMA_STRING_PROTOTYPE_TRIM, ECMA_STRING_PROTOTYPE_SUBSTR, ECMA_STRING_PROTOTYPE_REPEAT, ECMA_STRING_PROTOTYPE_CODE_POINT_AT, ECMA_STRING_PROTOTYPE_PAD_START, ECMA_STRING_PROTOTYPE_PAD_END, /* Note: These 5 routines MUST be in this order */ ECMA_STRING_PROTOTYPE_LAST_INDEX_OF, ECMA_STRING_PROTOTYPE_INDEX_OF, ECMA_STRING_PROTOTYPE_STARTS_WITH, ECMA_STRING_PROTOTYPE_INCLUDES, ECMA_STRING_PROTOTYPE_ENDS_WITH, ECMA_STRING_PROTOTYPE_ITERATOR, ECMA_STRING_PROTOTYPE_REPLACE_ALL, ECMA_STRING_PROTOTYPE_MATCH_ALL, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-string-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID string_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup stringprototype ECMA String.prototype object built-in * @{ */ /** * The String.prototype object's 'toString' and 'valueOf' routines * * See also: * ECMA-262 v5, 15.5.4.2 * ECMA-262 v5, 15.5.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_to_string (ecma_value_t this_arg) /**< this argument */ { if (ecma_is_value_string (this_arg)) { return ecma_copy_value (this_arg); } if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_STRING)) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; JERRY_ASSERT (ecma_is_value_string (ext_object_p->u.cls.u3.value)); return ecma_copy_value (ext_object_p->u.cls.u3.value); } } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_STRING_OBJECT); } /* ecma_builtin_string_prototype_object_to_string */ /** * Helper function for the String.prototype object's 'charAt' and charCodeAt' routine * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_char_at_helper (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg, /**< routine's argument */ bool charcode_mode) /**< routine mode */ { /* 3 */ ecma_number_t index_num; ecma_value_t to_num_result = ecma_op_to_integer (arg, &index_num); if (JERRY_UNLIKELY (!ecma_is_value_empty (to_num_result))) { return to_num_result; } /* 2 */ ecma_string_t *original_string_p = ecma_op_to_string (this_arg); if (JERRY_UNLIKELY (original_string_p == NULL)) { return ECMA_VALUE_ERROR; } /* 4 */ const lit_utf8_size_t len = ecma_string_get_length (original_string_p); /* 5 */ // When index_num is NaN, then the first two comparisons are false if (index_num < 0 || index_num >= len || (ecma_number_is_nan (index_num) && len == 0)) { ecma_deref_ecma_string (original_string_p); return (charcode_mode ? ecma_make_nan_value () : ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY)); } /* 6 */ /* * String length is currently uint32_t, but index_num may be bigger, * ToInteger performs floor, while ToUInt32 performs modulo 2^32, * hence after the check 0 <= index_num < len we assume to_uint32 can be used. * We assume to_uint32 (NaN) is 0. */ JERRY_ASSERT (ecma_number_is_nan (index_num) || ecma_number_to_uint32 (index_num) == ecma_number_trunc (index_num)); ecma_char_t new_ecma_char = ecma_string_get_char_at_pos (original_string_p, ecma_number_to_uint32 (index_num)); ecma_deref_ecma_string (original_string_p); return (charcode_mode ? ecma_make_uint32_value (new_ecma_char) : ecma_make_string_value (ecma_new_ecma_string_from_code_unit (new_ecma_char))); } /* ecma_builtin_string_prototype_char_at_helper */ /** * The String.prototype object's 'concat' routine * * See also: * ECMA-262 v5, 15.5.4.6 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_concat (ecma_string_t *this_string_p, /**< this argument */ const ecma_value_t *argument_list_p, /**< arguments list */ uint32_t arguments_number) /**< number of arguments */ { ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (this_string_p); /* 5 */ for (uint32_t arg_index = 0; arg_index < arguments_number; ++arg_index) { /* 5a, b */ ecma_string_t *get_arg_string_p = ecma_op_to_string (argument_list_p[arg_index]); if (JERRY_UNLIKELY (get_arg_string_p == NULL)) { ecma_stringbuilder_destroy (&builder); return ECMA_VALUE_ERROR; } ecma_stringbuilder_append (&builder, get_arg_string_p); ecma_deref_ecma_string (get_arg_string_p); } /* 6 */ return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_string_prototype_object_concat */ /** * The String.prototype object's 'localeCompare' routine * * See also: * ECMA-262 v5, 15.5.4.9 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_locale_compare (ecma_string_t *this_string_p, /**< this argument */ ecma_value_t arg) /**< routine's argument */ { /* 3. */ ecma_string_t *arg_string_p = ecma_op_to_string (arg); if (JERRY_UNLIKELY (arg_string_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_number_t result = ECMA_NUMBER_ZERO; if (ecma_compare_ecma_strings_relational (this_string_p, arg_string_p)) { result = ECMA_NUMBER_MINUS_ONE; } else if (!ecma_compare_ecma_strings (this_string_p, arg_string_p)) { result = ECMA_NUMBER_ONE; } else { result = ECMA_NUMBER_ZERO; } ecma_deref_ecma_string (arg_string_p); return ecma_make_number_value (result); } /* ecma_builtin_string_prototype_object_locale_compare */ #if JERRY_BUILTIN_REGEXP /** * The String.prototype object's 'match' routine * * See also: * ECMA-262 v5, 15.5.4.10 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_match (ecma_value_t this_argument, /**< this argument */ ecma_value_t regexp_arg) /**< routine's argument */ { /* 3. */ if (!(ecma_is_value_undefined (regexp_arg) || ecma_is_value_null (regexp_arg))) { /* 3.a */ ecma_value_t matcher = ecma_op_get_method_by_symbol_id (regexp_arg, LIT_GLOBAL_SYMBOL_MATCH); /* 3.b */ if (ECMA_IS_VALUE_ERROR (matcher)) { return matcher; } /* 3.c */ if (!ecma_is_value_undefined (matcher)) { /* 3.c.i */ ecma_object_t *matcher_method = ecma_get_object_from_value (matcher); ecma_value_t result = ecma_op_function_call (matcher_method, regexp_arg, &this_argument, 1); ecma_deref_object (matcher_method); return result; } } /* 4. */ ecma_string_t *this_str_p = ecma_op_to_string (this_argument); /* 5. */ if (JERRY_UNLIKELY (this_str_p == NULL)) { return ECMA_VALUE_ERROR; } /* 6. */ ecma_object_t *regexp_obj_p = ecma_op_regexp_alloc (NULL); if (JERRY_UNLIKELY (regexp_obj_p == NULL)) { ecma_deref_ecma_string (this_str_p); return ECMA_VALUE_ERROR; } ecma_value_t new_regexp = ecma_op_create_regexp_from_pattern (regexp_obj_p, regexp_arg, ECMA_VALUE_UNDEFINED); /* 7. */ if (ECMA_IS_VALUE_ERROR (new_regexp)) { ecma_deref_object (regexp_obj_p); ecma_deref_ecma_string (this_str_p); return new_regexp; } ecma_value_t this_str_value = ecma_make_string_value (this_str_p); /* 8. */ ecma_value_t ret_value = ecma_op_invoke_by_symbol_id (new_regexp, LIT_GLOBAL_SYMBOL_MATCH, &this_str_value, 1); ecma_deref_ecma_string (this_str_p); ecma_free_value (new_regexp); return ret_value; } /* ecma_builtin_string_prototype_object_match */ /** * The String.prototype object's 'matchAll' routine * * See also: * ECMA-262 v11, 21.1.3.12 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_match_all (ecma_value_t this_argument, /**< this argument */ ecma_value_t regexp_arg) /**< routine's argument */ { /* 2. */ if (!ecma_is_value_null (regexp_arg) && !ecma_is_value_undefined (regexp_arg)) { /* 2.a */ ecma_value_t is_regexp = ecma_op_is_regexp (regexp_arg); if (ECMA_IS_VALUE_ERROR (is_regexp)) { return is_regexp; } /* 2.b */ if (ecma_is_value_true (is_regexp)) { /* 2.b.i */ ecma_object_t *regexp_obj_p = ecma_get_object_from_value (regexp_arg); ecma_value_t get_flags = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_FLAGS); if (ECMA_IS_VALUE_ERROR (get_flags)) { return get_flags; } /* 2.b.ii */ if (!ecma_op_require_object_coercible (get_flags)) { ecma_free_value (get_flags); return ECMA_VALUE_ERROR; } /* 2.b.iii */ ecma_string_t *flags = ecma_op_to_string (get_flags); ecma_free_value (get_flags); if (JERRY_UNLIKELY (flags == NULL)) { return ECMA_VALUE_ERROR; } uint16_t parsed_flag; ecma_value_t flag_parse = ecma_regexp_parse_flags (flags, &parsed_flag); ecma_deref_ecma_string (flags); if (ECMA_IS_VALUE_ERROR (flag_parse)) { return flag_parse; } if (!(parsed_flag & RE_FLAG_GLOBAL)) { return ecma_raise_type_error (ECMA_ERR_REGEXP_ARGUMENT_SHOULD_HAVE_GLOBAL_FLAG); } } /* 2.c */ ecma_value_t matcher = ecma_op_get_method_by_symbol_id (regexp_arg, LIT_GLOBAL_SYMBOL_MATCH_ALL); if (ECMA_IS_VALUE_ERROR (matcher)) { return matcher; } /* 2.d */ if (!ecma_is_value_undefined (matcher)) { /* 2.d.i */ ecma_object_t *matcher_method = ecma_get_object_from_value (matcher); ecma_value_t result = ecma_op_function_call (matcher_method, regexp_arg, &this_argument, 1); ecma_deref_object (matcher_method); return result; } } /* 3. */ ecma_string_t *str_p = ecma_op_to_string (this_argument); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } /* 4. */ ecma_object_t *new_regexp_obj_p = ecma_op_regexp_alloc (NULL); if (JERRY_UNLIKELY (new_regexp_obj_p == NULL)) { ecma_deref_ecma_string (str_p); return ECMA_VALUE_ERROR; } ecma_value_t new_regexp = ecma_op_create_regexp_from_pattern (new_regexp_obj_p, regexp_arg, ECMA_VALUE_UNDEFINED); if (ECMA_IS_VALUE_ERROR (new_regexp)) { ecma_deref_ecma_string (str_p); ecma_deref_object (new_regexp_obj_p); return new_regexp; } /* 5. */ ecma_value_t string_arg = ecma_make_string_value (str_p); ecma_value_t ret_value = ecma_op_invoke_by_symbol_id (new_regexp, LIT_GLOBAL_SYMBOL_MATCH_ALL, &string_arg, 1); ecma_deref_ecma_string (str_p); ecma_free_value (new_regexp); return ret_value; } /* ecma_builtin_string_prototype_object_match_all */ /** * The String.prototype object's 'replace' and 'replaceAll' routine * * See also: * ECMA-262 v5, 15.5.4.11 (replace ES5) * ECMA-262 v6, 21.1.3.14 (replace ES6) * ECMA-262 v12, 21.1.3.18 (replaceAll) * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_replace_helper (ecma_value_t this_value, /**< this argument */ ecma_value_t search_value, /**< routine's first argument */ ecma_value_t replace_value, /**< routine's second argument */ bool replace_all) { ecma_string_t *result_string_p; lit_utf8_byte_t *search_buf_p; uint8_t search_flags; uint8_t input_flags; if (!(ecma_is_value_undefined (search_value) || ecma_is_value_null (search_value))) { if (replace_all) { ecma_value_t is_regexp = ecma_op_is_regexp (search_value); if (ECMA_IS_VALUE_ERROR (is_regexp)) { return is_regexp; } if (ecma_is_value_true (is_regexp)) { ecma_object_t *regexp_obj_p = ecma_get_object_from_value (search_value); ecma_value_t get_flags = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_FLAGS); if (ECMA_IS_VALUE_ERROR (get_flags)) { return get_flags; } if (!ecma_op_require_object_coercible (get_flags)) { ecma_free_value (get_flags); return ECMA_VALUE_ERROR; } ecma_string_t *flags = ecma_op_to_string (get_flags); ecma_free_value (get_flags); if (JERRY_UNLIKELY (flags == NULL)) { return ECMA_VALUE_ERROR; } bool have_global_flag = lit_find_char_in_string (flags, LIT_CHAR_LOWERCASE_G); ecma_deref_ecma_string (flags); if (!have_global_flag) { return ecma_raise_type_error (ECMA_ERR_REGEXP_ARGUMENT_SHOULD_HAVE_GLOBAL_FLAG); } } } ecma_object_t *obj_p = ecma_get_object_from_value (ecma_op_to_object (search_value)); ecma_value_t replace_symbol = ecma_op_object_get_by_symbol_id (obj_p, LIT_GLOBAL_SYMBOL_REPLACE); ecma_deref_object (obj_p); if (ECMA_IS_VALUE_ERROR (replace_symbol)) { return replace_symbol; } if (!ecma_is_value_undefined (replace_symbol) && !ecma_is_value_null (replace_symbol)) { ecma_value_t arguments[] = { this_value, replace_value }; ecma_value_t replace_result = ecma_op_function_validated_call (replace_symbol, search_value, arguments, 2); ecma_free_value (replace_symbol); return replace_result; } } ecma_string_t *input_str_p = ecma_op_to_string (this_value); if (input_str_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ECMA_VALUE_ERROR; ecma_string_t *search_str_p = ecma_op_to_string (search_value); if (search_str_p == NULL) { ecma_deref_ecma_string (input_str_p); return result; } ecma_replace_context_t replace_ctx; replace_ctx.capture_count = 0; replace_ctx.u.captures_p = NULL; replace_ctx.replace_str_p = NULL; if (!ecma_op_is_callable (replace_value)) { replace_ctx.replace_str_p = ecma_op_to_string (replace_value); if (replace_ctx.replace_str_p == NULL) { goto cleanup_search; } } input_flags = ECMA_STRING_FLAG_IS_ASCII; replace_ctx.string_p = ecma_string_get_chars (input_str_p, &(replace_ctx.string_size), NULL, NULL, &input_flags); lit_utf8_size_t search_size; lit_utf8_size_t search_length; search_flags = ECMA_STRING_FLAG_IS_ASCII; search_buf_p = (lit_utf8_byte_t *) ecma_string_get_chars (search_str_p, &search_size, &search_length, NULL, &search_flags); result_string_p = NULL; if (replace_ctx.string_size >= search_size) { replace_ctx.builder = ecma_stringbuilder_create (); replace_ctx.matched_size = search_size; const lit_utf8_byte_t *const input_end_p = replace_ctx.string_p + replace_ctx.string_size; const lit_utf8_byte_t *const loop_end_p = input_end_p - search_size; const lit_utf8_byte_t *last_match_end_p = replace_ctx.string_p; const lit_utf8_byte_t *curr_p = replace_ctx.string_p; lit_utf8_size_t pos = 0; while (curr_p <= loop_end_p) { if (!memcmp (curr_p, search_buf_p, search_size)) { const lit_utf8_size_t prefix_size = (lit_utf8_size_t) (curr_p - last_match_end_p); ecma_stringbuilder_append_raw (&replace_ctx.builder, last_match_end_p, prefix_size); last_match_end_p = curr_p + search_size; if (replace_ctx.replace_str_p == NULL) { ecma_object_t *function_p = ecma_get_object_from_value (replace_value); ecma_value_t args[] = { ecma_make_string_value (search_str_p), ecma_make_uint32_value (pos), ecma_make_string_value (input_str_p) }; result = ecma_op_function_call (function_p, ECMA_VALUE_UNDEFINED, args, 3); if (ECMA_IS_VALUE_ERROR (result)) { ecma_stringbuilder_destroy (&replace_ctx.builder); goto cleanup_replace; } ecma_string_t *const result_str_p = ecma_op_to_string (result); ecma_free_value (result); if (result_str_p == NULL) { ecma_stringbuilder_destroy (&replace_ctx.builder); result = ECMA_VALUE_ERROR; goto cleanup_replace; } ecma_stringbuilder_append (&replace_ctx.builder, result_str_p); ecma_deref_ecma_string (result_str_p); } else { replace_ctx.matched_p = curr_p; replace_ctx.match_byte_pos = (lit_utf8_size_t) (curr_p - replace_ctx.string_p); ecma_builtin_replace_substitute (&replace_ctx); } if (!replace_all || last_match_end_p == input_end_p) { break; } if (search_size != 0) { curr_p = last_match_end_p; pos += search_length; continue; } } pos++; lit_utf8_incr (&curr_p); } ecma_stringbuilder_append_raw (&replace_ctx.builder, last_match_end_p, (lit_utf8_size_t) (input_end_p - last_match_end_p)); result_string_p = ecma_stringbuilder_finalize (&replace_ctx.builder); } if (result_string_p == NULL) { ecma_ref_ecma_string (input_str_p); result_string_p = input_str_p; } result = ecma_make_string_value (result_string_p); cleanup_replace: if (input_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) replace_ctx.string_p, replace_ctx.string_size); } if (search_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) search_buf_p, search_size); } if (replace_ctx.replace_str_p != NULL) { ecma_deref_ecma_string (replace_ctx.replace_str_p); } cleanup_search: ecma_deref_ecma_string (search_str_p); ecma_deref_ecma_string (input_str_p); return result; } /* ecma_builtin_string_prototype_object_replace_helper */ /** * The String.prototype object's 'search' routine * * See also: * ECMA-262 v5, 15.5.4.12 * ECMA-262 v6, 21.1.3.15 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_search (ecma_value_t this_value, /**< this argument */ ecma_value_t regexp_value) /**< routine's argument */ { if (!(ecma_is_value_undefined (regexp_value) || ecma_is_value_null (regexp_value))) { ecma_object_t *obj_p = ecma_get_object_from_value (ecma_op_to_object (regexp_value)); ecma_value_t search_symbol = ecma_op_object_get_by_symbol_id (obj_p, LIT_GLOBAL_SYMBOL_SEARCH); ecma_deref_object (obj_p); if (ECMA_IS_VALUE_ERROR (search_symbol)) { return search_symbol; } if (!ecma_is_value_undefined (search_symbol) && !ecma_is_value_null (search_symbol)) { ecma_value_t search_result = ecma_op_function_validated_call (search_symbol, regexp_value, &this_value, 1); ecma_free_value (search_symbol); return search_result; } } ecma_value_t result = ECMA_VALUE_ERROR; ecma_string_t *string_p = ecma_op_to_string (this_value); if (string_p == NULL) { return result; } ecma_string_t *pattern_p = ecma_regexp_read_pattern_str_helper (regexp_value); if (pattern_p == NULL) { ecma_deref_ecma_string (string_p); return result; } ecma_object_t *new_regexp_obj_p = ecma_op_regexp_alloc (NULL); if (JERRY_UNLIKELY (new_regexp_obj_p == NULL)) { ecma_deref_ecma_string (string_p); ecma_deref_ecma_string (pattern_p); return result; } ecma_value_t new_regexp = ecma_op_create_regexp_from_pattern (new_regexp_obj_p, ecma_make_string_value (pattern_p), ECMA_VALUE_UNDEFINED); ecma_deref_ecma_string (pattern_p); if (ECMA_IS_VALUE_ERROR (new_regexp)) { ecma_deref_ecma_string (string_p); ecma_deref_object (new_regexp_obj_p); return result; } ecma_object_t *regexp_obj_p = ecma_get_object_from_value (new_regexp); ecma_value_t this_str_value = ecma_make_string_value (string_p); result = ecma_op_invoke_by_symbol_id (new_regexp, LIT_GLOBAL_SYMBOL_SEARCH, &this_str_value, 1); ecma_deref_object (regexp_obj_p); ecma_deref_ecma_string (string_p); return result; } /* ecma_builtin_string_prototype_object_search */ #endif /* JERRY_BUILTIN_REGEXP */ /** * The String.prototype object's 'slice' routine * * See also: * ECMA-262 v5, 15.5.4.13 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_slice (ecma_string_t *get_string_val, /**< this argument */ ecma_value_t arg1, /**< routine's first argument */ ecma_value_t arg2) /**< routine's second argument */ { const lit_utf8_size_t len = ecma_string_get_length (get_string_val); /* 4. 6. */ lit_utf8_size_t start = 0, end = len; if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (arg1, len, &start))) { return ECMA_VALUE_ERROR; } /* 5. 7. */ if (ecma_is_value_undefined (arg2)) { end = len; } else { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (arg2, len, &end))) { return ECMA_VALUE_ERROR; } } JERRY_ASSERT (start <= len && end <= len); /* 8-9. */ ecma_string_t *new_str_p = ecma_string_substr (get_string_val, start, end); return ecma_make_string_value (new_str_p); } /* ecma_builtin_string_prototype_object_slice */ /** * The String.prototype object's 'at' routine * * See also: * ECMA-262 Stage 3 Draft Relative Indexing Method proposal * from: https://tc39.es/proposal-relative-indexing-method * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_at (ecma_string_t *string_val, /**< this argument */ const ecma_value_t index) /**< index argument */ { ecma_length_t len = (ecma_length_t) ecma_string_get_length (string_val); ecma_length_t res_index; ecma_value_t return_value = ecma_builtin_helper_calculate_index (index, len, &res_index); if (return_value != ECMA_VALUE_EMPTY) { return return_value; } ecma_char_t character = ecma_string_get_char_at_pos (string_val, (lit_utf8_size_t) res_index); return ecma_make_string_value (ecma_new_ecma_string_from_code_unit (character)); } /* ecma_builtin_string_prototype_object_at */ /** * The String.prototype object's 'split' routine * * See also: * ECMA-262 v5, 15.5.4.14 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_split (ecma_value_t this_value, /**< this argument */ ecma_value_t separator_value, /**< separator */ ecma_value_t limit_value) /**< limit */ { if (!(ecma_is_value_undefined (separator_value) || ecma_is_value_null (separator_value))) { ecma_object_t *obj_p = ecma_get_object_from_value (ecma_op_to_object (separator_value)); ecma_value_t split_symbol = ecma_op_object_get_by_symbol_id (obj_p, LIT_GLOBAL_SYMBOL_SPLIT); ecma_deref_object (obj_p); if (ECMA_IS_VALUE_ERROR (split_symbol)) { return split_symbol; } if (!ecma_is_value_undefined (split_symbol) && !ecma_is_value_null (split_symbol)) { ecma_value_t arguments[] = { this_value, limit_value }; ecma_value_t split_result = ecma_op_function_validated_call (split_symbol, separator_value, arguments, 2); ecma_free_value (split_symbol); return split_result; } } ecma_value_t result = ECMA_VALUE_ERROR; ecma_string_t *end_substr_p; lit_utf8_size_t string_size; uint8_t string_flags; lit_utf8_byte_t *string_buffer_p; lit_utf8_size_t separator_size; uint8_t separator_flags; lit_utf8_byte_t *separator_buffer_p; lit_utf8_byte_t *string_end_p; lit_utf8_byte_t *compare_end_p; const lit_utf8_byte_t *current_p; const lit_utf8_byte_t *last_str_begin_p; lit_utf8_size_t array_length; uint32_t limit; ecma_string_t *separator_p; ecma_object_t *array_p; /* 4. */ ecma_string_t *string_p = ecma_op_to_string (this_value); if (string_p == NULL) { return result; } /* 8. */ limit = UINT32_MAX - 1; if (!ecma_is_value_undefined (limit_value)) { /* ECMA-262 v11, 21.1.3.20 6 */ ecma_number_t num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (limit_value, &num))) { goto cleanup_string; } limit = ecma_number_to_uint32 (num); } /* 12. */ separator_p = ecma_op_to_string (separator_value); if (separator_p == NULL) { goto cleanup_string; } /* 6. */ array_p = ecma_op_new_array_object (0); result = ecma_make_object_value (array_p); /* 14. */ if (limit == 0) { goto cleanup_separator; } /* 6. */ array_length = 0; /* 15. */ if (ecma_is_value_undefined (separator_value)) { ecma_builtin_helper_def_prop_by_index (array_p, array_length, ecma_make_string_value (string_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); goto cleanup_separator; } /* 16. */ if (ecma_string_is_empty (string_p)) { if (!ecma_string_is_empty (separator_p)) { ecma_builtin_helper_def_prop_by_index (array_p, array_length, ecma_make_string_value (string_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); } goto cleanup_separator; } string_flags = ECMA_STRING_FLAG_IS_ASCII; string_buffer_p = (lit_utf8_byte_t *) ecma_string_get_chars (string_p, &string_size, NULL, NULL, &string_flags); separator_flags = ECMA_STRING_FLAG_IS_ASCII; separator_buffer_p = (lit_utf8_byte_t *) ecma_string_get_chars (separator_p, &separator_size, NULL, NULL, &separator_flags); string_end_p = string_buffer_p + string_size; compare_end_p = JERRY_MIN (string_end_p - separator_size + 1, string_end_p); current_p = string_buffer_p; last_str_begin_p = string_buffer_p; while (current_p < compare_end_p) { if (!memcmp (current_p, separator_buffer_p, separator_size) && (last_str_begin_p != current_p + separator_size)) { auto substr_p = ecma_new_ecma_string_from_utf8 (last_str_begin_p, (lit_utf8_size_t) (current_p - last_str_begin_p)); ecma_builtin_helper_def_prop_by_index (array_p, array_length++, ecma_make_string_value (substr_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_deref_ecma_string (substr_p); if (array_length >= limit) { goto cleanup_buffers; } current_p += separator_size; last_str_begin_p = current_p; continue; } lit_utf8_incr (¤t_p); } end_substr_p = ecma_new_ecma_string_from_utf8 (last_str_begin_p, (lit_utf8_size_t) (string_end_p - last_str_begin_p)); ecma_builtin_helper_def_prop_by_index (array_p, array_length, ecma_make_string_value (end_substr_p), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); ecma_deref_ecma_string (end_substr_p); cleanup_buffers: if (string_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) string_buffer_p, string_size); } if (separator_flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) separator_buffer_p, separator_size); } cleanup_separator: ecma_deref_ecma_string (separator_p); cleanup_string: ecma_deref_ecma_string (string_p); return result; } /* ecma_builtin_string_prototype_object_split */ /** * The String.prototype object's 'substring' routine * * See also: * ECMA-262 v5, 15.5.4.15 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_substring (ecma_string_t *original_string_p, /**< this argument */ ecma_value_t arg1, /**< routine's first argument */ ecma_value_t arg2) /**< routine's second argument */ { /* 3 */ const lit_utf8_size_t len = ecma_string_get_length (original_string_p); lit_utf8_size_t start = 0, end = len; /* 4 */ ecma_number_t start_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (arg1, &start_num))) { return ECMA_VALUE_ERROR; } /* 6 */ start = (uint32_t) JERRY_MIN (JERRY_MAX (start_num, 0), len); /* 5 */ if (ecma_is_value_undefined (arg2)) { end = len; } else { /* 5 part 2 */ ecma_number_t end_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (arg2, &end_num))) { return ECMA_VALUE_ERROR; } /* 7 */ end = (uint32_t) JERRY_MIN (JERRY_MAX (end_num, 0), len); } JERRY_ASSERT (start <= len && end <= len); /* 8 */ uint32_t from = start < end ? start : end; /* 9 */ uint32_t to = start > end ? start : end; /* 10 */ ecma_string_t *new_str_p = ecma_string_substr (original_string_p, from, to); return ecma_make_string_value (new_str_p); } /* ecma_builtin_string_prototype_object_substring */ /** * The common implementation of the String.prototype object's * 'toLowerCase', 'toLocaleLowerCase', 'toUpperCase', 'toLocalUpperCase' routines * * See also: * ECMA-262 v5, 15.5.4.16 * ECMA-262 v5, 15.5.4.17 * ECMA-262 v5, 15.5.4.18 * ECMA-262 v5, 15.5.4.19 * * Helper function to convert a string to upper or lower case. * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_conversion_helper (ecma_string_t *input_string_p, /**< this argument */ bool lower_case) /**< convert to lower (true) * or upper (false) case */ { ecma_stringbuilder_t builder = ecma_stringbuilder_create (); ECMA_STRING_TO_UTF8_STRING (input_string_p, input_start_p, input_start_size); const lit_utf8_byte_t *input_curr_p = input_start_p; const lit_utf8_byte_t *input_str_end_p = input_start_p + input_start_size; while (input_curr_p < input_str_end_p) { lit_code_point_t cp = lit_cesu8_read_next (&input_curr_p); if (lit_is_code_point_utf16_high_surrogate (cp) && input_curr_p < input_str_end_p) { const ecma_char_t next_ch = lit_cesu8_peek_next (input_curr_p); if (lit_is_code_point_utf16_low_surrogate (next_ch)) { cp = lit_convert_surrogate_pair_to_code_point ((ecma_char_t) cp, next_ch); input_curr_p += LIT_UTF8_MAX_BYTES_IN_CODE_UNIT; } } if (lower_case) { lit_char_to_lower_case (cp, &builder); } else { lit_char_to_upper_case (cp, &builder); } } ECMA_FINALIZE_UTF8_STRING (input_start_p, input_start_size); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_string_prototype_object_conversion_helper */ /** * The String.prototype object's 'trim' routine * * See also: * ECMA-262 v5, 15.5.4.20 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_trim (ecma_string_t *original_string_p) /**< this argument */ { ecma_string_t *trimmed_string_p = ecma_string_trim (original_string_p); return ecma_make_string_value (trimmed_string_p); } /* ecma_builtin_string_prototype_object_trim */ /** * The String.prototype object's 'repeat' routine * * See also: * ECMA-262 v6, 21.1.3.13 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_repeat (ecma_string_t *original_string_p, /**< this argument */ ecma_value_t repeat) /**< times to repeat */ { ecma_string_t *ret_string_p; /* 4 */ ecma_number_t count_number; ecma_value_t count_value = ecma_op_to_integer (repeat, &count_number); /* 5 */ if (ECMA_IS_VALUE_ERROR (count_value)) { return count_value; } int32_t repeat_count = ecma_number_to_int32 (count_number); bool isNan = ecma_number_is_nan (count_number); /* 6, 7 */ if (count_number < 0 || (!isNan && ecma_number_is_infinity (count_number))) { return ecma_raise_range_error (ECMA_ERR_INVALID_COUNT_VALUE); } lit_utf8_size_t size = ecma_string_get_size (original_string_p); if (repeat_count == 0 || size == 0 || isNan) { return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } if ((uint32_t) repeat_count >= (ECMA_STRING_SIZE_LIMIT / size)) { return ecma_raise_range_error (ECMA_ERR_INVALID_STRING_); } lit_utf8_size_t total_size = size * (lit_utf8_size_t) repeat_count; JMEM_DEFINE_LOCAL_ARRAY (str_buffer, total_size, lit_utf8_byte_t); ecma_string_to_cesu8_bytes (original_string_p, str_buffer, size); lit_utf8_byte_t *buffer_ptr = str_buffer + size; for (int32_t n = 1; n < repeat_count; n++) { memcpy (buffer_ptr, str_buffer, size); buffer_ptr += size; } ret_string_p = ecma_new_ecma_string_from_utf8 (str_buffer, (lit_utf8_size_t) (buffer_ptr - str_buffer)); JMEM_FINALIZE_LOCAL_ARRAY (str_buffer); return ecma_make_string_value (ret_string_p); } /* ecma_builtin_string_prototype_object_repeat */ /** * The String.prototype object's 'codePointAt' routine * * See also: * ECMA-262 v6, 21.1.3.3 * * @return lit_code_point_t */ static ecma_value_t ecma_builtin_string_prototype_object_code_point_at (ecma_string_t *this_string_p, /**< this argument */ ecma_value_t pos) /**< given position */ { ecma_number_t pos_num; ecma_value_t error = ecma_op_to_integer (pos, &pos_num); if (ECMA_IS_VALUE_ERROR (error)) { return error; } lit_utf8_size_t length = ecma_string_get_length (this_string_p); if (pos_num < 0 || pos_num >= length) { return ECMA_VALUE_UNDEFINED; } uint32_t index = (uint32_t) pos_num; ecma_char_t first = ecma_string_get_char_at_pos (this_string_p, index); if (first < LIT_UTF16_HIGH_SURROGATE_MIN || first > LIT_UTF16_HIGH_SURROGATE_MAX || index + 1 == length) { return ecma_make_uint32_value (first); } ecma_char_t second = ecma_string_get_char_at_pos (this_string_p, index + 1); if (second < LIT_UTF16_LOW_SURROGATE_MARKER || second > LIT_UTF16_LOW_SURROGATE_MAX) { return ecma_make_uint32_value (first); } return ecma_make_uint32_value (lit_convert_surrogate_pair_to_code_point (first, second)); } /* ecma_builtin_string_prototype_object_code_point_at */ #if JERRY_BUILTIN_ANNEXB /** * The String.prototype object's 'substr' routine * * See also: * ECMA-262 v5, B.2.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_substr (ecma_string_t *this_string_p, /**< this argument */ ecma_value_t start, /**< routine's first argument */ ecma_value_t length) /**< routine's second argument */ { /* 2. */ ecma_number_t start_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (start, &start_num))) { return ECMA_VALUE_ERROR; } /* 3. */ ecma_number_t length_num = ecma_number_make_infinity (false); if (!ecma_is_value_undefined (length)) { ecma_number_t len; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (length, &len))) { return ECMA_VALUE_ERROR; } length_num = ecma_number_is_nan (len) ? 0 : len; } /* 4. */ lit_utf8_size_t this_len = ecma_string_get_length (this_string_p); /* 5. */ uint32_t from = (uint32_t) ((start_num < 0) ? JERRY_MAX (this_len + start_num, 0) : start_num); if (from > this_len) { from = this_len; } /* 6. */ ecma_number_t to_num = JERRY_MIN (JERRY_MAX (length_num, 0), this_len - from); /* 7. */ uint32_t to = from + (uint32_t) to_num; /* 8. */ ecma_string_t *new_str_p = ecma_string_substr (this_string_p, from, to); return ecma_make_string_value (new_str_p); } /* ecma_builtin_string_prototype_object_substr */ #endif /* JERRY_BUILTIN_ANNEXB */ /** * The String.prototype object's @@iterator routine * * See also: * ECMA-262 v6, 21.1.3.27 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_prototype_object_iterator (ecma_value_t to_string) /**< this argument */ { return ecma_op_create_iterator_object (ecma_copy_value (to_string), ecma_builtin_get (ECMA_BUILTIN_ID_STRING_ITERATOR_PROTOTYPE), ECMA_OBJECT_CLASS_STRING_ITERATOR, ECMA_ITERATOR_VALUES); } /* ecma_builtin_string_prototype_object_iterator */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_string_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { if (builtin_routine_id <= ECMA_STRING_PROTOTYPE_VALUE_OF) { return ecma_builtin_string_prototype_object_to_string (this_arg); } if (!ecma_op_require_object_coercible (this_arg)) { return ECMA_VALUE_ERROR; } ecma_value_t arg1 = arguments_list_p[0]; ecma_value_t arg2 = arguments_list_p[1]; #if JERRY_BUILTIN_REGEXP if (builtin_routine_id == ECMA_STRING_PROTOTYPE_MATCH) { return ecma_builtin_string_prototype_object_match (this_arg, arg1); } if (builtin_routine_id == ECMA_STRING_PROTOTYPE_MATCH_ALL) { return ecma_builtin_string_prototype_object_match_all (this_arg, arg1); } #endif /* JERRY_BUILTIN_REGEXP */ if (builtin_routine_id <= ECMA_STRING_PROTOTYPE_CHAR_CODE_AT) { return ecma_builtin_string_prototype_char_at_helper (this_arg, arg1, builtin_routine_id == ECMA_STRING_PROTOTYPE_CHAR_CODE_AT); } #if JERRY_BUILTIN_REGEXP if (builtin_routine_id == ECMA_STRING_PROTOTYPE_REPLACE) { return ecma_builtin_string_prototype_object_replace_helper (this_arg, arg1, arg2, false); } else if (builtin_routine_id == ECMA_STRING_PROTOTYPE_REPLACE_ALL) { return ecma_builtin_string_prototype_object_replace_helper (this_arg, arg1, arg2, true); } #endif /* JERRY_BUILTIN_REGEXP */ ecma_string_t *string_p = ecma_op_to_string (this_arg); if (JERRY_UNLIKELY (string_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t to_string_val = ecma_make_string_value (string_p); ecma_value_t ret_value = ECMA_VALUE_EMPTY; switch (builtin_routine_id) { case ECMA_STRING_PROTOTYPE_CONCAT: { ret_value = ecma_builtin_string_prototype_object_concat (string_p, arguments_list_p, arguments_number); break; } case ECMA_STRING_PROTOTYPE_SLICE: { ret_value = ecma_builtin_string_prototype_object_slice (string_p, arg1, arg2); break; } case ECMA_STRING_PROTOTYPE_AT: { ret_value = ecma_builtin_string_prototype_object_at (string_p, arg1); break; } case ECMA_STRING_PROTOTYPE_LAST_INDEX_OF: case ECMA_STRING_PROTOTYPE_INDEX_OF: case ECMA_STRING_PROTOTYPE_STARTS_WITH: case ECMA_STRING_PROTOTYPE_INCLUDES: case ECMA_STRING_PROTOTYPE_ENDS_WITH: { ecma_string_index_of_mode_t mode; mode = (ecma_string_index_of_mode_t) (builtin_routine_id - ECMA_STRING_PROTOTYPE_LAST_INDEX_OF); ret_value = ecma_builtin_helper_string_prototype_object_index_of (string_p, arg1, arg2, mode); break; } case ECMA_STRING_PROTOTYPE_LOCALE_COMPARE: { ret_value = ecma_builtin_string_prototype_object_locale_compare (string_p, arg1); break; } #if JERRY_BUILTIN_REGEXP case ECMA_STRING_PROTOTYPE_SEARCH: { ret_value = ecma_builtin_string_prototype_object_search (to_string_val, arg1); break; } #endif /* JERRY_BUILTIN_REGEXP */ case ECMA_STRING_PROTOTYPE_SPLIT: { ret_value = ecma_builtin_string_prototype_object_split (to_string_val, arg1, arg2); break; } case ECMA_STRING_PROTOTYPE_SUBSTRING: { ret_value = ecma_builtin_string_prototype_object_substring (string_p, arg1, arg2); break; } case ECMA_STRING_PROTOTYPE_TO_LOWER_CASE: case ECMA_STRING_PROTOTYPE_TO_LOCAL_LOWER_CASE: case ECMA_STRING_PROTOTYPE_TO_UPPER_CASE: case ECMA_STRING_PROTOTYPE_TO_LOCAL_UPPER_CASE: { bool is_lower_case = builtin_routine_id <= ECMA_STRING_PROTOTYPE_TO_LOCAL_LOWER_CASE; ret_value = ecma_builtin_string_prototype_object_conversion_helper (string_p, is_lower_case); break; } case ECMA_STRING_PROTOTYPE_TRIM: { ret_value = ecma_builtin_string_prototype_object_trim (string_p); break; } #if JERRY_BUILTIN_ANNEXB case ECMA_STRING_PROTOTYPE_SUBSTR: { ret_value = ecma_builtin_string_prototype_object_substr (string_p, arg1, arg2); break; } #endif /* JERRY_BUILTIN_ANNEXB */ case ECMA_STRING_PROTOTYPE_REPEAT: { ret_value = ecma_builtin_string_prototype_object_repeat (string_p, arg1); break; } case ECMA_STRING_PROTOTYPE_CODE_POINT_AT: { ret_value = ecma_builtin_string_prototype_object_code_point_at (string_p, arg1); break; } case ECMA_STRING_PROTOTYPE_ITERATOR: { ret_value = ecma_builtin_string_prototype_object_iterator (to_string_val); break; } case ECMA_STRING_PROTOTYPE_PAD_END: case ECMA_STRING_PROTOTYPE_PAD_START: { ret_value = ecma_string_pad (to_string_val, arg1, arg2, builtin_routine_id == ECMA_STRING_PROTOTYPE_PAD_START); break; } default: { JERRY_UNREACHABLE (); } } ecma_deref_ecma_string (string_p); return ret_value; } /* ecma_builtin_string_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_STRING */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgSaveModule.h000664 001750 001750 00000003054 15164251010 034552 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SAVE_MODULE_H_ #define _TVG_SAVE_MODULE_H_ #include "tvgIteratorAccessor.h" namespace tvg { class SaveModule { public: virtual ~SaveModule() {} virtual bool save(Paint* paint, Paint* bg, const char* filename, uint32_t quality) = 0; virtual bool save(Animation* animation, Paint* bg, const char* filename, uint32_t quality, uint32_t fps) = 0; virtual bool close() = 0; }; } #endif //_TVG_SAVE_MODULE_H_ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray.inc.h000664 001750 001750 00000003252 15164251010 053163 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %TypedArray% description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_TYPEDARRAY /* ES2015 22.2.2 */ /* ES11 22.2.1.1 - value of length changed to 0 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ES2015 22.2.2 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_TYPED_ARRAY_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ES2015 22.2.2.3 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, ECMA_PROPERTY_FIXED) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ /* ES2015 22.2.2.1 */ ROUTINE (LIT_MAGIC_STRING_FROM, ecma_builtin_typedarray_from, NON_FIXED, 1) /* ES2015 22.2.2.2 */ ROUTINE (LIT_MAGIC_STRING_OF, ecma_builtin_typedarray_of, NON_FIXED, 0) /* ES2015 22.2.2.4 */ ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_SPECIES, ecma_builtin_typedarray_species_get, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_TYPEDARRAY */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-arguments-object.cpp000664 001750 001750 00000042202 15164251010 047267 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-arguments-object.h" #include "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaargumentsobject ECMA arguments object related routines * @{ */ /** * Arguments object creation operation. * * See also: ECMA-262 v5, 10.6 * * @return ecma value of arguments object * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_arguments_object (vm_frame_ctx_shared_args_t *shared_p, /**< shared context data */ ecma_object_t *lex_env_p) /**< lexical environment the Arguments * object is created for */ { ecma_object_t *func_obj_p = shared_p->header.function_object_p; const ecma_compiled_code_t *bytecode_data_p = shared_p->header.bytecode_header_p; uint16_t formal_params_number; if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { formal_params_number = ((cbc_uint16_arguments_t *) bytecode_data_p)->argument_end; } else { formal_params_number = ((cbc_uint8_arguments_t *) bytecode_data_p)->argument_end; } uint32_t object_size = sizeof (ecma_unmapped_arguments_t); uint32_t saved_arg_count = JERRY_MAX (shared_p->arg_list_len, formal_params_number); if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED) { object_size = sizeof (ecma_mapped_arguments_t); } ecma_object_t *obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE), object_size + (saved_arg_count * sizeof (ecma_value_t)), ECMA_OBJECT_TYPE_CLASS); ecma_unmapped_arguments_t *arguments_p = (ecma_unmapped_arguments_t *) obj_p; arguments_p->header.u.cls.type = ECMA_OBJECT_CLASS_ARGUMENTS; arguments_p->header.u.cls.u1.arguments_flags = ECMA_ARGUMENTS_OBJECT_NO_FLAGS; arguments_p->header.u.cls.u2.formal_params_number = formal_params_number; arguments_p->header.u.cls.u3.arguments_number = 0; arguments_p->callee = ecma_make_object_value (func_obj_p); ecma_value_t *argv_p = (ecma_value_t *) (((uint8_t *) obj_p) + object_size); for (uint32_t i = 0; i < shared_p->arg_list_len; i++) { argv_p[i] = ecma_copy_value_if_not_object (shared_p->arg_list_p[i]); } for (uint32_t i = shared_p->arg_list_len; i < saved_arg_count; i++) { argv_p[i] = ECMA_VALUE_UNDEFINED; } arguments_p->header.u.cls.u3.arguments_number = shared_p->arg_list_len; if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED) { ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) obj_p; ECMA_SET_INTERNAL_VALUE_POINTER (mapped_arguments_p->lex_env, lex_env_p); arguments_p->header.u.cls.u1.arguments_flags |= ECMA_ARGUMENTS_OBJECT_MAPPED; #if JERRY_SNAPSHOT_EXEC if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION) { arguments_p->header.u.cls.u1.arguments_flags |= ECMA_ARGUMENTS_OBJECT_STATIC_BYTECODE; mapped_arguments_p->u.byte_code_p = (ecma_compiled_code_t *) bytecode_data_p; } else #endif /* JERRY_SNAPSHOT_EXEC */ { ECMA_SET_INTERNAL_VALUE_POINTER (mapped_arguments_p->u.byte_code, bytecode_data_p); } /* Static snapshots are not ref counted. */ if ((bytecode_data_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION) == 0) { ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_data_p); } ecma_value_t *formal_parameter_start_p; formal_parameter_start_p = ecma_compiled_code_resolve_arguments_start ((ecma_compiled_code_t *) bytecode_data_p); for (uint32_t i = 0; i < formal_params_number; i++) { /* For legacy (non-strict) argument definition the trailing duplicated arguments cannot be lazy instantiated E.g: function f (a,a,a,a) {} */ if (JERRY_UNLIKELY (ecma_is_value_empty (formal_parameter_start_p[i]))) { ecma_property_value_t *prop_value_p; ecma_string_t *prop_name_p = ecma_new_ecma_string_from_uint32 (i); prop_value_p = ecma_create_named_data_property (obj_p, prop_name_p, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); ecma_deref_ecma_string (prop_name_p); prop_value_p->value = argv_p[i]; argv_p[i] = ECMA_VALUE_EMPTY; } } } return ecma_make_object_value (obj_p); } /* ecma_op_create_arguments_object */ /** * [[DefineOwnProperty]] ecma Arguments object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * ECMA-262 v5, 10.6 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_arguments_object_define_own_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ const ecma_property_descriptor_t *property_desc_p) /**< property * descriptor */ { /* 3. */ ecma_value_t ret_value = ecma_op_general_object_define_own_property (object_p, property_name_p, property_desc_p); if (ECMA_IS_VALUE_ERROR (ret_value) || !(((ecma_extended_object_t *) object_p)->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED)) { return ret_value; } ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) object_p; uint32_t index = ecma_string_get_array_index (property_name_p); if (index >= mapped_arguments_p->unmapped.header.u.cls.u2.formal_params_number) { return ret_value; } ecma_value_t *argv_p = (ecma_value_t *) (mapped_arguments_p + 1); if (ecma_is_value_empty (argv_p[index]) || argv_p[index] == ECMA_VALUE_ARGUMENT_NO_TRACK) { return ret_value; } if (property_desc_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) { ecma_free_value_if_not_object (argv_p[index]); argv_p[index] = ECMA_VALUE_ARGUMENT_NO_TRACK; return ret_value; } if (property_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) { ecma_string_t *name_p = ecma_op_arguments_object_get_formal_parameter (mapped_arguments_p, index); ecma_object_t *lex_env_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, mapped_arguments_p->lex_env); ecma_op_set_mutable_binding (lex_env_p, name_p, property_desc_p->value, true); } if ((property_desc_p->flags & JERRY_PROP_IS_WRITABLE_DEFINED) && !(property_desc_p->flags & JERRY_PROP_IS_WRITABLE)) { ecma_free_value_if_not_object (argv_p[index]); argv_p[index] = ECMA_VALUE_ARGUMENT_NO_TRACK; } return ret_value; } /* ecma_op_arguments_object_define_own_property */ /** * Try to lazy instantiate the given property of a mapped/unmapped arguments object * * @return pointer property, if one was instantiated, * NULL - otherwise. */ ecma_property_t * ecma_op_arguments_object_try_to_lazy_instantiate_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property's name */ { JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARGUMENTS)); ecma_unmapped_arguments_t *arguments_p = (ecma_unmapped_arguments_t *) object_p; ecma_value_t *argv_p = (ecma_value_t *) (arguments_p + 1); ecma_property_value_t *prop_value_p; ecma_property_t *prop_p; uint32_t arguments_number = arguments_p->header.u.cls.u3.arguments_number; uint8_t flags = arguments_p->header.u.cls.u1.arguments_flags; if (flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { argv_p = (ecma_value_t *) (((ecma_mapped_arguments_t *) object_p) + 1); } uint32_t index = ecma_string_get_array_index (property_name_p); if (index != ECMA_STRING_NOT_ARRAY_INDEX) { if (index >= arguments_number || ecma_is_value_empty (argv_p[index])) { return NULL; } JERRY_ASSERT (argv_p[index] != ECMA_VALUE_ARGUMENT_NO_TRACK); prop_value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_ENUMERABLE_WRITABLE, &prop_p); /* Passing the reference */ prop_value_p->value = argv_p[index]; argv_p[index] = ECMA_VALUE_UNDEFINED; return prop_p; } if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH) && !(flags & ECMA_ARGUMENTS_OBJECT_LENGTH_INITIALIZED)) { prop_value_p = ecma_create_named_data_property (object_p, ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_WRITABLE, &prop_p); prop_value_p->value = ecma_make_uint32_value (arguments_number); return prop_p; } if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_CALLEE) && !(flags & ECMA_ARGUMENTS_OBJECT_CALLEE_INITIALIZED)) { if (flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { prop_value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_WRITABLE, &prop_p); prop_value_p->value = arguments_p->callee; } else { ecma_object_t *thrower_p = ecma_builtin_get (ECMA_BUILTIN_ID_TYPE_ERROR_THROWER); ecma_create_named_accessor_property (object_p, ecma_get_magic_string (LIT_MAGIC_STRING_CALLEE), thrower_p, thrower_p, ECMA_PROPERTY_BUILT_IN_FIXED, &prop_p); } return prop_p; } if (ecma_op_compare_string_to_global_symbol (property_name_p, LIT_GLOBAL_SYMBOL_ITERATOR) && !(flags & ECMA_ARGUMENTS_OBJECT_ITERATOR_INITIALIZED)) { prop_value_p = ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_WRITABLE, &prop_p); prop_value_p->value = ecma_op_object_get_by_magic_id (ecma_builtin_get (ECMA_BUILTIN_ID_INTRINSIC_OBJECT), LIT_INTERNAL_MAGIC_STRING_ARRAY_PROTOTYPE_VALUES); JERRY_ASSERT (ecma_is_value_object (prop_value_p->value)); ecma_deref_object (ecma_get_object_from_value (prop_value_p->value)); return prop_p; } return NULL; } /* ecma_op_arguments_object_try_to_lazy_instantiate_property */ /** * Delete configurable properties of arguments object */ void ecma_op_arguments_delete_built_in_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p) /**< property name */ { ecma_unmapped_arguments_t *arguments_p = (ecma_unmapped_arguments_t *) object_p; if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH)) { JERRY_ASSERT (!(arguments_p->header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_LENGTH_INITIALIZED)); arguments_p->header.u.cls.u1.arguments_flags |= ECMA_ARGUMENTS_OBJECT_LENGTH_INITIALIZED; return; } if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_CALLEE)) { JERRY_ASSERT (!(arguments_p->header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_CALLEE_INITIALIZED)); JERRY_ASSERT (arguments_p->header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED); arguments_p->header.u.cls.u1.arguments_flags |= ECMA_ARGUMENTS_OBJECT_CALLEE_INITIALIZED; return; } if (ecma_prop_name_is_symbol (property_name_p)) { JERRY_ASSERT (!(arguments_p->header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_ITERATOR_INITIALIZED)); JERRY_ASSERT (ecma_op_compare_string_to_global_symbol (property_name_p, LIT_GLOBAL_SYMBOL_ITERATOR)); arguments_p->header.u.cls.u1.arguments_flags |= ECMA_ARGUMENTS_OBJECT_ITERATOR_INITIALIZED; return; } uint32_t index = ecma_string_get_array_index (property_name_p); ecma_value_t *argv_p = (ecma_value_t *) (arguments_p + 1); if (arguments_p->header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { argv_p = (ecma_value_t *) (((ecma_mapped_arguments_t *) object_p) + 1); } JERRY_ASSERT (argv_p[index] == ECMA_VALUE_UNDEFINED || argv_p[index] == ECMA_VALUE_ARGUMENT_NO_TRACK); argv_p[index] = ECMA_VALUE_EMPTY; } /* ecma_op_arguments_delete_built_in_property */ /** * List names of an arguments object's lazy instantiated properties */ void ecma_op_arguments_object_list_lazy_property_names (ecma_object_t *obj_p, /**< arguments object */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< property name filter options */ { JERRY_ASSERT (ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_ARGUMENTS)); ecma_unmapped_arguments_t *arguments_p = (ecma_unmapped_arguments_t *) obj_p; uint32_t arguments_number = arguments_p->header.u.cls.u3.arguments_number; uint8_t flags = arguments_p->header.u.cls.u1.arguments_flags; if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_INTEGER_INDICES)) { ecma_value_t *argv_p = (ecma_value_t *) (arguments_p + 1); if (flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { argv_p = (ecma_value_t *) (((ecma_mapped_arguments_t *) obj_p) + 1); } for (uint32_t index = 0; index < arguments_number; index++) { if (!ecma_is_value_empty (argv_p[index])) { ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index); ecma_collection_push_back (prop_names_p, ecma_make_string_value (index_string_p)); prop_counter_p->array_index_named_props++; } } } if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS)) { if (!(flags & ECMA_ARGUMENTS_OBJECT_LENGTH_INITIALIZED)) { ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); prop_counter_p->string_named_props++; } if (!(flags & ECMA_ARGUMENTS_OBJECT_CALLEE_INITIALIZED)) { ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_CALLEE)); prop_counter_p->string_named_props++; } } if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS) && !(flags & ECMA_ARGUMENTS_OBJECT_ITERATOR_INITIALIZED)) { ecma_string_t *symbol_p = ecma_op_get_global_symbol (LIT_GLOBAL_SYMBOL_ITERATOR); ecma_collection_push_back (prop_names_p, ecma_make_symbol_value (symbol_p)); prop_counter_p->symbol_named_props++; } } /* ecma_op_arguments_object_list_lazy_property_names */ /** * Get the formal parameter name corresponding to the given property index * * @return pointer to the formal parameter name */ ecma_string_t * ecma_op_arguments_object_get_formal_parameter (ecma_mapped_arguments_t *mapped_arguments_p, /**< mapped arguments * object */ uint32_t index) /**< formal parameter index */ { JERRY_ASSERT (mapped_arguments_p->unmapped.header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED); JERRY_ASSERT (index < mapped_arguments_p->unmapped.header.u.cls.u2.formal_params_number); ecma_compiled_code_t *byte_code_p; #if JERRY_SNAPSHOT_EXEC if (mapped_arguments_p->unmapped.header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_STATIC_BYTECODE) { byte_code_p = mapped_arguments_p->u.byte_code_p; } else #endif /* JERRY_SNAPSHOT_EXEC */ { byte_code_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, mapped_arguments_p->u.byte_code); } ecma_value_t *formal_param_names_p = ecma_compiled_code_resolve_arguments_start (byte_code_p); return ecma_get_string_from_value (formal_param_names_p[index]); } /* ecma_op_arguments_object_get_formal_parameter */ /** * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-array-object.cpp000664 001750 001750 00000115633 15164251010 046411 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-array-object.h" #include "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "ecma-property-hashmap.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaarrayobject ECMA Array object related routines * @{ */ /** * Property name type flag for array indices. */ #define ECMA_FAST_ARRAY_UINT_DIRECT_STRING_PROP_TYPE (ECMA_DIRECT_STRING_UINT << ECMA_PROPERTY_NAME_TYPE_SHIFT) /** * Allocate a new array object with the given length * * @return pointer to the constructed array object */ static ecma_object_t * ecma_op_alloc_array_object (uint32_t length) /**< length of the new array */ { #if JERRY_BUILTIN_ARRAY ecma_object_t *array_prototype_object_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAY_PROTOTYPE); #else /* !JERRY_BUILTIN_ARRAY */ ecma_object_t *array_prototype_object_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); #endif /* JERRY_BUILTIN_ARRAY */ ecma_object_t *object_p = ecma_create_object (array_prototype_object_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_ARRAY); /* * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_ARRAY type. * * See also: ecma_object_get_class_name */ ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; ext_obj_p->u.array.length = length; ext_obj_p->u.array.length_prop_and_hole_count = ECMA_PROPERTY_FLAG_WRITABLE | ECMA_PROPERTY_VIRTUAL; return object_p; } /* ecma_op_alloc_array_object */ /** * Check whether the given object is fast-access mode array * * @return true - if the object is fast-access mode array * false, otherwise */ bool ecma_op_object_is_fast_array (ecma_object_t *object_p) /**< ecma-object */ { return (ecma_get_object_base_type (object_p) == ECMA_OBJECT_BASE_TYPE_ARRAY && ecma_op_array_is_fast_array ((ecma_extended_object_t *) object_p)); } /* ecma_op_object_is_fast_array */ /** * Check whether the given array object is fast-access mode array * * @return true - if the array object is fast-access mode array * false, otherwise */ bool ecma_op_array_is_fast_array (ecma_extended_object_t *array_p) /**< ecma-array-object */ { JERRY_ASSERT (ecma_get_object_base_type ((ecma_object_t *) array_p) == ECMA_OBJECT_BASE_TYPE_ARRAY); return array_p->u.array.length_prop_and_hole_count & ECMA_FAST_ARRAY_FLAG; } /* ecma_op_array_is_fast_array */ /** * Allocate a new array object with the given length * * Note: The returned array can be normal of fast access mode * * @return pointer to the constructed array object */ ecma_object_t * ecma_op_new_array_object (uint32_t length) /**< length of the new array */ { ecma_object_t *object_p = ecma_op_alloc_array_object (length); const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (length); ecma_value_t *values_p = NULL; if (length > 0) { if (length >= ECMA_FAST_ARRAY_MAX_HOLE_COUNT) { return object_p; } values_p = (ecma_value_t *) jmem_heap_alloc_block_null_on_error (aligned_length * sizeof (ecma_value_t)); if (JERRY_UNLIKELY (values_p == NULL)) { return object_p; } } ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; ext_obj_p->u.array.length_prop_and_hole_count |= ECMA_FAST_ARRAY_FLAG; ext_obj_p->u.array.length_prop_and_hole_count += length * ECMA_FAST_ARRAY_HOLE_ONE; for (uint32_t i = 0; i < aligned_length; i++) { values_p[i] = ECMA_VALUE_ARRAY_HOLE; } JERRY_ASSERT (object_p->u1.property_list_cp == JMEM_CP_NULL); ECMA_SET_POINTER (object_p->u1.property_list_cp, values_p); return object_p; } /* ecma_op_new_array_object */ /** * Allocate a new array object from the given length * * Note: The returned array can be normal of fast access mode * * @return NULL - if the given length is invalid * pointer to the constructed array object - otherwise */ ecma_object_t * ecma_op_new_array_object_from_length (ecma_length_t length) /**< length of the new array */ { if (length > UINT32_MAX) { ecma_raise_range_error (ECMA_ERR_INVALID_ARRAY_LENGTH); return NULL; } return ecma_op_new_array_object ((uint32_t) length); } /* ecma_op_new_array_object_from_length */ /** * Allocate a new array object from the given buffer * * Note: The returned array can be normal of fast access mode * * @return ecma_value - constructed array object */ ecma_value_t ecma_op_new_array_object_from_buffer (const ecma_value_t *args_p, /**< array element list */ uint32_t length) /**< number of array elements */ { if (length == 0) { return ecma_make_object_value (ecma_op_new_array_object (0)); } ecma_object_t *object_p = ecma_op_alloc_array_object (length); const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (length); ecma_value_t *values_p; values_p = (ecma_value_t *) jmem_heap_alloc_block_null_on_error (aligned_length * sizeof (ecma_value_t)); if (values_p != NULL) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; ext_obj_p->u.array.length_prop_and_hole_count |= ECMA_FAST_ARRAY_FLAG; JERRY_ASSERT (object_p->u1.property_list_cp == JMEM_CP_NULL); for (uint32_t i = 0; i < length; i++) { JERRY_ASSERT (!ecma_is_value_array_hole (args_p[i])); values_p[i] = ecma_copy_value_if_not_object (args_p[i]); } for (uint32_t i = length; i < aligned_length; i++) { values_p[i] = ECMA_VALUE_ARRAY_HOLE; } ECMA_SET_POINTER (object_p->u1.property_list_cp, values_p); } else { for (uint32_t i = 0; i < length; i++) { JERRY_ASSERT (!ecma_is_value_array_hole (args_p[i])); ecma_string_t *prop_name_p = ecma_new_ecma_string_from_uint32 (i); ecma_property_value_t *prop_value_p; prop_value_p = ecma_create_named_data_property (object_p, prop_name_p, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); ecma_deref_ecma_string (prop_name_p); prop_value_p->value = ecma_copy_value_if_not_object (args_p[i]); } } return ecma_make_object_value (object_p); } /* ecma_op_new_array_object_from_buffer */ /** * Allocate a new fast access mode array object from the given collection * * Note: The given collection will be unavailable after and it's underlying buffer is reused * * @return ecma_value - constructed fast access mode array object */ ecma_value_t ecma_op_new_array_object_from_collection (ecma_collection_t *collection_p, /**< collection to create array from */ bool unref_objects) /**< true - if the collection potentially contains objects false - otherwise */ { const uint32_t item_count = collection_p->item_count; if (item_count == 0) { ecma_collection_destroy (collection_p); return ecma_make_object_value (ecma_op_new_array_object (0)); } ecma_object_t *object_p; ecma_value_t *buffer_p = collection_p->buffer_p; const uint32_t old_size = ECMA_COLLECTION_ALLOCATED_SIZE (collection_p->capacity); const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (collection_p->item_count); jmem_heap_free_block (collection_p, sizeof (ecma_collection_t)); buffer_p = (ecma_value_t *) jmem_heap_realloc_block (buffer_p, old_size, aligned_length * sizeof (ecma_value_t)); object_p = (ecma_object_t *) ecma_op_alloc_array_object (item_count); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; ext_obj_p->u.array.length_prop_and_hole_count |= ECMA_FAST_ARRAY_FLAG; JERRY_ASSERT (object_p->u1.property_list_cp == JMEM_CP_NULL); JERRY_ASSERT (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE); ECMA_SET_POINTER (object_p->u1.property_list_cp, buffer_p); if (JERRY_UNLIKELY (unref_objects)) { for (uint32_t i = 0; i < item_count; i++) { /* Strong references from the collection are no longer needed since GC will mark these object as a fast access mode array properties */ ecma_deref_if_object (buffer_p[i]); } } for (uint32_t i = item_count; i < aligned_length; i++) { buffer_p[i] = ECMA_VALUE_ARRAY_HOLE; } return ecma_make_object_value (object_p); } /* ecma_op_new_array_object_from_collection */ /** * Converts a fast access mode array back to a normal property list based array */ void ecma_fast_array_convert_to_normal (ecma_object_t *object_p) /**< fast access mode array object */ { JERRY_ASSERT (ecma_op_object_is_fast_array (object_p)); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; if (object_p->u1.property_list_cp == JMEM_CP_NULL) { ext_obj_p->u.array.length_prop_and_hole_count &= (uint32_t) ~ECMA_FAST_ARRAY_FLAG; return; } uint32_t length = ext_obj_p->u.array.length; const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (length); ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); ecma_ref_object (object_p); ecma_property_pair_t *property_pair_p = NULL; jmem_cpointer_t next_property_pair_cp = JMEM_CP_NULL; uint32_t prop_index = 1; int32_t index = (int32_t) (length - 1); while (index >= 0) { if (ecma_is_value_array_hole (values_p[index])) { index--; continue; } if (prop_index == 1) { property_pair_p = ecma_alloc_property_pair (); property_pair_p->header.next_property_cp = next_property_pair_cp; property_pair_p->names_cp[0] = LIT_INTERNAL_MAGIC_STRING_DELETED; property_pair_p->header.types[0] = ECMA_PROPERTY_TYPE_DELETED; ECMA_SET_NON_NULL_POINTER (next_property_pair_cp, property_pair_p); } JERRY_ASSERT (index <= ECMA_DIRECT_STRING_MAX_IMM); property_pair_p->names_cp[prop_index] = (jmem_cpointer_t) index; property_pair_p->header.types[prop_index] = (ecma_property_t) (ECMA_PROPERTY_FLAG_DATA | ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | ECMA_FAST_ARRAY_UINT_DIRECT_STRING_PROP_TYPE); property_pair_p->values[prop_index].value = values_p[index]; index--; prop_index = !prop_index; } ext_obj_p->u.array.length_prop_and_hole_count &= (uint32_t) ~ECMA_FAST_ARRAY_FLAG; jmem_heap_free_block (values_p, aligned_length * sizeof (ecma_value_t)); ECMA_SET_POINTER (object_p->u1.property_list_cp, property_pair_p); ecma_deref_object (object_p); } /* ecma_fast_array_convert_to_normal */ /** * [[Put]] operation for a fast access mode array * * @return false - If the property name is not array index, or the requested index to be set * would result too much array hole in the underlying buffer. The these cases * the array is converted back to normal property list based array. * true - If the indexed property can be set with/without resizing the underlying buffer. */ bool ecma_fast_array_set_property (ecma_object_t *object_p, /**< fast access mode array object */ uint32_t index, /**< property name index */ ecma_value_t value) /**< value to be set */ { JERRY_ASSERT (ecma_op_object_is_fast_array (object_p)); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; uint32_t old_length = ext_obj_p->u.array.length; ecma_value_t *values_p; if (JERRY_LIKELY (index < old_length)) { JERRY_ASSERT (object_p->u1.property_list_cp != JMEM_CP_NULL); values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); if (ecma_is_value_array_hole (values_p[index])) { ext_obj_p->u.array.length_prop_and_hole_count -= ECMA_FAST_ARRAY_HOLE_ONE; } else { ecma_free_value_if_not_object (values_p[index]); } values_p[index] = ecma_copy_value_if_not_object (value); return true; } uint32_t old_holes = ext_obj_p->u.array.length_prop_and_hole_count; uint32_t new_holes = index - old_length; if (JERRY_UNLIKELY (new_holes > (ECMA_FAST_ARRAY_MAX_HOLE_COUNT - (old_holes >> ECMA_FAST_ARRAY_HOLE_SHIFT)))) { ecma_fast_array_convert_to_normal (object_p); return false; } uint32_t new_length = index + 1; JERRY_ASSERT (new_length < UINT32_MAX); const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (old_length); if (JERRY_LIKELY (index < aligned_length)) { JERRY_ASSERT (object_p->u1.property_list_cp != JMEM_CP_NULL); values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); /* This area is filled with ECMA_VALUE_ARRAY_HOLE, but not counted in u.array.length_prop_and_hole_count */ JERRY_ASSERT (ecma_is_value_array_hole (values_p[index])); ext_obj_p->u.array.length_prop_and_hole_count += new_holes * ECMA_FAST_ARRAY_HOLE_ONE; ext_obj_p->u.array.length = new_length; } else { values_p = ecma_fast_array_extend (object_p, new_length); ext_obj_p->u.array.length_prop_and_hole_count -= ECMA_FAST_ARRAY_HOLE_ONE; } values_p[index] = ecma_copy_value_if_not_object (value); return true; } /* ecma_fast_array_set_property */ /** * Get the number of array holes in a fast access array object * * @return number of array holes in a fast access array object */ uint32_t ecma_fast_array_get_hole_count (ecma_object_t *obj_p) /**< fast access mode array object */ { JERRY_ASSERT (ecma_op_object_is_fast_array (obj_p)); return ((ecma_extended_object_t *) obj_p)->u.array.length_prop_and_hole_count >> ECMA_FAST_ARRAY_HOLE_SHIFT; } /* ecma_fast_array_get_hole_count */ /** * Extend the underlying buffer of a fast mode access array for the given new length * * @return pointer to the extended underlying buffer */ ecma_value_t * ecma_fast_array_extend (ecma_object_t *object_p, /**< fast access mode array object */ uint32_t new_length) /**< new length of the fast access mode array */ { JERRY_ASSERT (ecma_op_object_is_fast_array (object_p)); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; uint32_t old_length = ext_obj_p->u.array.length; JERRY_ASSERT (old_length < new_length); ecma_ref_object (object_p); ecma_value_t *new_values_p; const uint32_t old_length_aligned = ECMA_FAST_ARRAY_ALIGN_LENGTH (old_length); const uint32_t new_length_aligned = ECMA_FAST_ARRAY_ALIGN_LENGTH (new_length); if (object_p->u1.property_list_cp == JMEM_CP_NULL) { new_values_p = (ecma_value_t *) jmem_heap_alloc_block (new_length_aligned * sizeof (ecma_value_t)); } else { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); new_values_p = (ecma_value_t *) jmem_heap_realloc_block (values_p, old_length_aligned * sizeof (ecma_value_t), new_length_aligned * sizeof (ecma_value_t)); } for (uint32_t i = old_length; i < new_length_aligned; i++) { new_values_p[i] = ECMA_VALUE_ARRAY_HOLE; } ext_obj_p->u.array.length_prop_and_hole_count += (new_length - old_length) * ECMA_FAST_ARRAY_HOLE_ONE; ext_obj_p->u.array.length = new_length; ECMA_SET_NON_NULL_POINTER (object_p->u1.property_list_cp, new_values_p); ecma_deref_object (object_p); return new_values_p; } /* ecma_fast_array_extend */ /** * Delete the array object's property referenced by its value pointer. * * Note: specified property must be owned by specified object. * * @return true, if the property is deleted * false, otherwise */ bool ecma_array_object_delete_property (ecma_object_t *object_p, /**< object */ ecma_string_t *property_name_p) /**< property name */ { JERRY_ASSERT (ecma_get_object_base_type (object_p) == ECMA_OBJECT_BASE_TYPE_ARRAY); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; if (!ecma_op_object_is_fast_array (object_p)) { return false; } JERRY_ASSERT (object_p->u1.property_list_cp != JMEM_CP_NULL); uint32_t index = ecma_string_get_array_index (property_name_p); JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); JERRY_ASSERT (index < ext_obj_p->u.array.length); ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); if (ecma_is_value_array_hole (values_p[index])) { return true; } ecma_free_value_if_not_object (values_p[index]); values_p[index] = ECMA_VALUE_ARRAY_HOLE; ext_obj_p->u.array.length_prop_and_hole_count += ECMA_FAST_ARRAY_HOLE_ONE; return true; } /* ecma_array_object_delete_property */ /** * Low level delete of fast access mode array items * * @return the updated value of new_length */ uint32_t ecma_delete_fast_array_properties (ecma_object_t *object_p, /**< fast access mode array */ uint32_t new_length) /**< new length of the fast access mode array */ { JERRY_ASSERT (ecma_op_object_is_fast_array (object_p)); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; ecma_ref_object (object_p); ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); uint32_t old_length = ext_obj_p->u.array.length; const uint32_t old_aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (old_length); JERRY_ASSERT (new_length < old_length); for (uint32_t i = new_length; i < old_length; i++) { if (ecma_is_value_array_hole (values_p[i])) { ext_obj_p->u.array.length_prop_and_hole_count -= ECMA_FAST_ARRAY_HOLE_ONE; } else { ecma_free_value_if_not_object (values_p[i]); } } jmem_cpointer_t new_property_list_cp; if (new_length == 0) { jmem_heap_free_block (values_p, old_aligned_length * sizeof (ecma_value_t)); new_property_list_cp = JMEM_CP_NULL; } else { const uint32_t new_aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (new_length); ecma_value_t *new_values_p; new_values_p = (ecma_value_t *) jmem_heap_realloc_block (values_p, old_aligned_length * sizeof (ecma_value_t), new_aligned_length * sizeof (ecma_value_t)); for (uint32_t i = new_length; i < new_aligned_length; i++) { new_values_p[i] = ECMA_VALUE_ARRAY_HOLE; } ECMA_SET_NON_NULL_POINTER (new_property_list_cp, new_values_p); } ext_obj_p->u.array.length = new_length; object_p->u1.property_list_cp = new_property_list_cp; ecma_deref_object (object_p); return new_length; } /* ecma_delete_fast_array_properties */ /** * Update the length of a fast access mode array to a new length * * Note: if the new length would result too much array hole in the underlying arraybuffer * the array is converted back to normal property list based array */ static void ecma_fast_array_set_length (ecma_object_t *object_p, /**< fast access mode array object */ uint32_t new_length) /**< new length of the fast access mode array object*/ { JERRY_ASSERT (ecma_op_object_is_fast_array (object_p)); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; uint32_t old_length = ext_obj_p->u.array.length; JERRY_ASSERT (new_length >= old_length); if (new_length == old_length) { return; } uint32_t old_holes = ext_obj_p->u.array.length_prop_and_hole_count; uint32_t new_holes = new_length - old_length; if (JERRY_UNLIKELY (new_holes > (ECMA_FAST_ARRAY_MAX_HOLE_COUNT - (old_holes >> ECMA_FAST_ARRAY_HOLE_SHIFT)))) { ecma_fast_array_convert_to_normal (object_p); } else { ecma_fast_array_extend (object_p, new_length); } } /* ecma_fast_array_set_length */ /** * Get collection of property names of a fast access mode array object * * Note: Since the fast array object only contains indexed, enumerable, writable, configurable properties * we can return a collection of non-array hole array indices * * @return collection of strings - property names */ ecma_collection_t * ecma_fast_array_object_own_property_keys (ecma_object_t *object_p, /**< fast access mode array object */ jerry_property_filter_t filter) /**< property name filter options */ { JERRY_ASSERT (ecma_op_object_is_fast_array (object_p)); ecma_collection_t *ret_p = ecma_new_collection (); if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_INTEGER_INDICES)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; uint32_t length = ext_obj_p->u.array.length; if (length != 0) { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); for (uint32_t i = 0; i < length; i++) { if (ecma_is_value_array_hole (values_p[i])) { continue; } ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (i); ecma_collection_push_back (ret_p, ecma_make_string_value (index_str_p)); } } } if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS)) { ecma_collection_push_back (ret_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); } return ret_p; } /* ecma_fast_array_object_own_property_keys */ /** * Array object creation with custom prototype. * * See also: ECMA-262 v6, 9.4.2.3 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_object_t * ecma_op_array_species_create (ecma_object_t *original_array_p, /**< The object from whom the new array object * is being created */ ecma_length_t length) /**< length of the array */ { ecma_value_t constructor = ECMA_VALUE_UNDEFINED; ecma_value_t original_array = ecma_make_object_value (original_array_p); ecma_value_t is_array = ecma_is_value_array (original_array); if (ECMA_IS_VALUE_ERROR (is_array)) { return NULL; } if (ecma_is_value_true (is_array)) { constructor = ecma_op_object_get_by_magic_id (original_array_p, LIT_MAGIC_STRING_CONSTRUCTOR); if (ECMA_IS_VALUE_ERROR (constructor)) { return NULL; } #if JERRY_BUILTIN_REALMS if (ecma_is_constructor (constructor)) { ecma_object_t *constructor_p = ecma_get_object_from_value (constructor); ecma_global_object_t *global_object_p = ecma_op_function_get_function_realm (constructor_p); if ((ecma_object_t *) global_object_p != ecma_builtin_get_global () && constructor_p == ecma_builtin_get_from_realm (global_object_p, ECMA_BUILTIN_ID_ARRAY)) { ecma_deref_object (constructor_p); constructor = ECMA_VALUE_UNDEFINED; } } #endif /* JERRY_BUILTIN_REALMS */ if (ecma_is_value_object (constructor)) { ecma_object_t *ctor_object_p = ecma_get_object_from_value (constructor); constructor = ecma_op_object_get_by_symbol_id (ctor_object_p, LIT_GLOBAL_SYMBOL_SPECIES); ecma_deref_object (ctor_object_p); if (ECMA_IS_VALUE_ERROR (constructor)) { return NULL; } if (ecma_is_value_null (constructor)) { constructor = ECMA_VALUE_UNDEFINED; } } } if (ecma_is_value_undefined (constructor)) { return ecma_op_new_array_object_from_length (length); } if (!ecma_is_constructor (constructor)) { ecma_free_value (constructor); ecma_raise_type_error (ECMA_ERR_INVALID_SPECIES_CONSTRUCTOR); return NULL; } ecma_value_t len_val = ecma_make_length_value (length); ecma_object_t *ctor_object_p = ecma_get_object_from_value (constructor); ecma_value_t ret_val = ecma_op_function_construct (ctor_object_p, ctor_object_p, &len_val, 1); ecma_deref_object (ctor_object_p); ecma_free_value (len_val); if (ECMA_IS_VALUE_ERROR (ret_val)) { return NULL; } return ecma_get_object_from_value (ret_val); } /* ecma_op_array_species_create */ /** * CreateArrayIterator Abstract Operation * * See also: * ECMA-262 v6, 22.1.5.1 * * Referenced by: * ECMA-262 v6, 22.1.3.4 * ECMA-262 v6, 22.1.3.13 * ECMA-262 v6, 22.1.3.29 * ECMA-262 v6, 22.1.3.30 * * Note: * Returned value must be freed with ecma_free_value. * * @return array iterator object */ ecma_value_t ecma_op_create_array_iterator (ecma_object_t *obj_p, /**< array object */ ecma_iterator_kind_t kind) /**< array iterator kind */ { ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAY_ITERATOR_PROTOTYPE); return ecma_op_create_iterator_object (ecma_make_object_value (obj_p), prototype_obj_p, ECMA_OBJECT_CLASS_ARRAY_ITERATOR, kind); } /* ecma_op_create_array_iterator */ /** * Low level delete of array items from new_length to old_length * * Note: new_length must be less than old_length * * @return the updated value of new_length */ static uint32_t ecma_delete_array_properties (ecma_object_t *object_p, /**< object */ uint32_t new_length, /**< new length */ uint32_t old_length) /**< old length */ { JERRY_ASSERT (new_length < old_length); JERRY_ASSERT (ecma_get_object_base_type (object_p) == ECMA_OBJECT_BASE_TYPE_ARRAY); if (ecma_op_object_is_fast_array (object_p)) { return ecma_delete_fast_array_properties (object_p, new_length); } /* First the minimum value of new_length is updated. */ jmem_cpointer_t current_prop_cp = object_p->u1.property_list_cp; if (current_prop_cp == JMEM_CP_NULL) { return new_length; } ecma_property_header_t *current_prop_p; #if JERRY_PROPERTY_HASHMAP current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { current_prop_cp = current_prop_p->next_property_cp; } #endif /* JERRY_PROPERTY_HASHMAP */ while (current_prop_cp != JMEM_CP_NULL) { current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (current_prop_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) current_prop_p; for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if (current_prop_p->types[i] != ECMA_PROPERTY_TYPE_DELETED && !ecma_is_property_configurable (current_prop_p->types[i])) { uint32_t index = ecma_string_get_property_index (current_prop_p->types[i], prop_pair_p->names_cp[i]); if (index < old_length && index >= new_length) { JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); new_length = index + 1; if (new_length == old_length) { /* Early return. */ return new_length; } } } } current_prop_cp = current_prop_p->next_property_cp; } /* Second all properties between new_length and old_length are deleted. */ current_prop_cp = object_p->u1.property_list_cp; ecma_property_header_t *prev_prop_p = NULL; #if JERRY_PROPERTY_HASHMAP JERRY_ASSERT (current_prop_cp != JMEM_CP_NULL); ecma_property_hashmap_delete_status hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_NO_HASHMAP; current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prev_prop_p = current_prop_p; current_prop_cp = current_prop_p->next_property_cp; hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP; } #endif /* JERRY_PROPERTY_HASHMAP */ while (current_prop_cp != JMEM_CP_NULL) { current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (current_prop_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) current_prop_p; for (uint32_t i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { if (current_prop_p->types[i] != ECMA_PROPERTY_TYPE_DELETED && ecma_is_property_configurable (current_prop_p->types[i])) { uint32_t index = ecma_string_get_property_index (current_prop_p->types[i], prop_pair_p->names_cp[i]); if (index < old_length && index >= new_length) { JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); #if JERRY_PROPERTY_HASHMAP if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP) { hashmap_status = ecma_property_hashmap_delete (object_p, prop_pair_p->names_cp[i], current_prop_p->types + i); } #endif /* JERRY_PROPERTY_HASHMAP */ ecma_gc_free_property (object_p, prop_pair_p, i); current_prop_p->types[i] = ECMA_PROPERTY_TYPE_DELETED; prop_pair_p->names_cp[i] = LIT_INTERNAL_MAGIC_STRING_DELETED; } } } if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_DELETED && current_prop_p->types[1] == ECMA_PROPERTY_TYPE_DELETED) { if (prev_prop_p == NULL) { object_p->u1.property_list_cp = current_prop_p->next_property_cp; } else { prev_prop_p->next_property_cp = current_prop_p->next_property_cp; } jmem_cpointer_t next_prop_cp = current_prop_p->next_property_cp; ecma_dealloc_property_pair ((ecma_property_pair_t *) current_prop_p); current_prop_cp = next_prop_cp; } else { prev_prop_p = current_prop_p; current_prop_cp = current_prop_p->next_property_cp; } } #if JERRY_PROPERTY_HASHMAP if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP) { ecma_property_hashmap_free (object_p); ecma_property_hashmap_create (object_p); } #endif /* JERRY_PROPERTY_HASHMAP */ return new_length; } /* ecma_delete_array_properties */ /** * Update the length of an array to a new length * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_array_object_set_length (ecma_object_t *object_p, /**< the array object */ ecma_value_t new_value, /**< new length value */ uint16_t flags) /**< property descriptor flags */ { ecma_number_t new_len_num; ecma_value_t completion = ecma_op_to_number (new_value, &new_len_num); if (ECMA_IS_VALUE_ERROR (completion)) { return completion; } JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (completion)); if (ecma_is_value_object (new_value)) { ecma_value_t compared_num_val = ecma_op_to_number (new_value, &new_len_num); if (ECMA_IS_VALUE_ERROR (compared_num_val)) { return compared_num_val; } } uint32_t new_len_uint32 = ecma_number_to_uint32 (new_len_num); if (((ecma_number_t) new_len_uint32) != new_len_num) { return ecma_raise_range_error (ECMA_ERR_INVALID_ARRAY_LENGTH); } /* Only the writable and data properties can be modified. */ if (flags & (JERRY_PROP_IS_CONFIGURABLE | JERRY_PROP_IS_ENUMERABLE | JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) { return ecma_raise_property_redefinition (ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), flags); } ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; uint32_t old_len_uint32 = ext_object_p->u.array.length; if (new_len_num == old_len_uint32) { /* Only the writable flag must be updated. */ if (flags & JERRY_PROP_IS_WRITABLE_DEFINED) { if (!(flags & JERRY_PROP_IS_WRITABLE)) { if (ecma_op_array_is_fast_array (ext_object_p)) { ecma_fast_array_convert_to_normal (object_p); } ext_object_p->u.array.length_prop_and_hole_count &= (uint32_t) ~ECMA_PROPERTY_FLAG_WRITABLE; } else if (!ecma_is_property_writable ((ecma_property_t) ext_object_p->u.array.length_prop_and_hole_count)) { return ecma_raise_property_redefinition (ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), flags); } } return ECMA_VALUE_TRUE; } else if (!ecma_is_property_writable ((ecma_property_t) ext_object_p->u.array.length_prop_and_hole_count)) { return ecma_raise_property_redefinition (ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), flags); } uint32_t current_len_uint32 = new_len_uint32; if (new_len_uint32 < old_len_uint32) { current_len_uint32 = ecma_delete_array_properties (object_p, new_len_uint32, old_len_uint32); } else if (ecma_op_object_is_fast_array (object_p)) { ecma_fast_array_set_length (object_p, new_len_uint32); } ext_object_p->u.array.length = current_len_uint32; if ((flags & JERRY_PROP_IS_WRITABLE_DEFINED) && !(flags & JERRY_PROP_IS_WRITABLE)) { if (ecma_op_array_is_fast_array (ext_object_p)) { ecma_fast_array_convert_to_normal (object_p); } ext_object_p->u.array.length_prop_and_hole_count &= (uint32_t) ~ECMA_PROPERTY_FLAG_WRITABLE; } if (current_len_uint32 == new_len_uint32) { return ECMA_VALUE_TRUE; } return ecma_raise_property_redefinition (ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), flags); } /* ecma_op_array_object_set_length */ /** * Property descriptor bitset for fast array data properties. * If the property descriptor fields contains all the flags below * attempt to stay fast access array during [[DefineOwnProperty]] operation. */ #define ECMA_FAST_ARRAY_DATA_PROP_FLAGS \ (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE \ | JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE | JERRY_PROP_IS_WRITABLE_DEFINED \ | JERRY_PROP_IS_WRITABLE) /** * [[DefineOwnProperty]] ecma array object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * ECMA-262 v5, 15.4.5.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_array_object_define_own_property (ecma_object_t *object_p, /**< the array object */ ecma_string_t *property_name_p, /**< property name */ const ecma_property_descriptor_t *property_desc_p) /**< property descriptor */ { if (ecma_string_is_length (property_name_p)) { JERRY_ASSERT ((property_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE_DEFINED) || !(property_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE)); JERRY_ASSERT ((property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE_DEFINED) || !(property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE)); JERRY_ASSERT ((property_desc_p->flags & JERRY_PROP_IS_WRITABLE_DEFINED) || !(property_desc_p->flags & JERRY_PROP_IS_WRITABLE)); if (property_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) { return ecma_op_array_object_set_length (object_p, property_desc_p->value, property_desc_p->flags); } ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ecma_value_t length_value = ecma_make_uint32_value (ext_object_p->u.array.length); ecma_value_t result = ecma_op_array_object_set_length (object_p, length_value, property_desc_p->flags); ecma_fast_free_value (length_value); return result; } ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (ecma_op_object_is_fast_array (object_p)) { if ((property_desc_p->flags & ECMA_FAST_ARRAY_DATA_PROP_FLAGS) == ECMA_FAST_ARRAY_DATA_PROP_FLAGS) { uint32_t index = ecma_string_get_array_index (property_name_p); if (JERRY_UNLIKELY (index == ECMA_STRING_NOT_ARRAY_INDEX)) { ecma_fast_array_convert_to_normal (object_p); } else if (ecma_fast_array_set_property (object_p, index, property_desc_p->value)) { return ECMA_VALUE_TRUE; } JERRY_ASSERT (!ecma_op_array_is_fast_array (ext_object_p)); } else { ecma_fast_array_convert_to_normal (object_p); } } JERRY_ASSERT (!ecma_op_object_is_fast_array (object_p)); uint32_t index = ecma_string_get_array_index (property_name_p); if (index == ECMA_STRING_NOT_ARRAY_INDEX) { return ecma_op_general_object_define_own_property (object_p, property_name_p, property_desc_p); } bool update_length = (index >= ext_object_p->u.array.length); if (update_length && !ecma_is_property_writable ((ecma_property_t) ext_object_p->u.array.length_prop_and_hole_count)) { return ecma_raise_property_redefinition (property_name_p, property_desc_p->flags); } ecma_property_descriptor_t prop_desc; prop_desc = *property_desc_p; prop_desc.flags &= (uint16_t) ~JERRY_PROP_SHOULD_THROW; ecma_value_t completion = ecma_op_general_object_define_own_property (object_p, property_name_p, &prop_desc); JERRY_ASSERT (ecma_is_value_boolean (completion)); if (ecma_is_value_false (completion)) { return ecma_raise_property_redefinition (property_name_p, property_desc_p->flags); } if (update_length) { ext_object_p->u.array.length = index + 1; } return ECMA_VALUE_TRUE; } /* ecma_op_array_object_define_own_property */ /** * Get the length of the an array object * * @return the array length */ uint32_t ecma_array_get_length (ecma_object_t *array_p) /**< array object */ { JERRY_ASSERT (ecma_get_object_base_type (array_p) == ECMA_OBJECT_BASE_TYPE_ARRAY); return ((ecma_extended_object_t *) array_p)->u.array.length; } /* ecma_array_get_length */ /** * The Array.prototype and %TypedArray%.prototype objects' 'toString' routine. * * See also: * ECMA-262 v5, 15.4.4.2 * ECMA-262 v6, 22.1.3.7 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_array_object_to_string (ecma_value_t this_arg) /**< this argument */ { JERRY_ASSERT (ecma_is_value_object (this_arg)); ecma_value_t ret_value = ECMA_VALUE_EMPTY; ecma_object_t *obj_p = ecma_get_object_from_value (this_arg); /* 2. */ ecma_value_t join_value = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_JOIN); if (ECMA_IS_VALUE_ERROR (join_value)) { return join_value; } if (!ecma_op_is_callable (join_value)) { /* 3. */ ret_value = ecma_builtin_helper_object_to_string (this_arg); } else { /* 4. */ ecma_object_t *join_func_obj_p = ecma_get_object_from_value (join_value); ret_value = ecma_op_function_call (join_func_obj_p, this_arg, NULL, 0); } ecma_free_value (join_value); return ret_value; } /* ecma_array_object_to_string */ /** * @} * @} */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-urierror.cpp000664 001750 001750 00000005306 15164251010 050251 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-urierror.inc.h" #define BUILTIN_UNDERSCORED_ID uri_error #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup urierror ECMA UriError object built-in * @{ */ /** * Handle calling [[Call]] of built-in UriError object * * @return ecma value */ ecma_value_t ecma_builtin_uri_error_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_helper_error_dispatch_call (JERRY_ERROR_URI, arguments_list_p, arguments_list_len); } /* ecma_builtin_uri_error_dispatch_call */ /** * Handle calling [[Construct]] of built-in UriError object * * @return ecma value */ ecma_value_t ecma_builtin_uri_error_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_URI_ERROR_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_uri_error_dispatch_call (arguments_list_p, arguments_list_len); if (!ECMA_IS_VALUE_ERROR (result)) { ecma_object_t *object_p = ecma_get_object_from_value (result); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, proto_p); } ecma_deref_object (proto_p); return result; } /* ecma_builtin_uri_error_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ERRORS */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgRenderData.h000664 001750 001750 00000017647 15164251010 036715 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_RENDER_DATA_H_ #define _TVG_WG_RENDER_DATA_H_ #include "tvgWgPipelines.h" #include "tvgWgGeometry.h" #include "tvgWgShaderTypes.h" struct WgImageData { WGPUTexture texture{}; WGPUTextureView textureView{}; WGPUBindGroup bindGroup{}; void update(WgContext& context, const RenderSurface* surface); void update(WgContext& context, const Fill* fill); void release(WgContext& context); }; enum class WgRenderSettingsType { None = 0, Solid = 1, Linear = 2, Radial = 3 }; enum class WgRenderRasterType { Solid = 0, Gradient, Image }; struct WgRenderSettings { uint32_t bindGroupInd{}; WgShaderTypePaintSettings settings; WgImageData gradientData; WgRenderSettingsType fillType{}; WgRenderRasterType rasterType{}; float opacityMultiplier = 1.0f; bool skip{}; void update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity); void update(WgContext& context, const Fill* fill); void update(WgContext& context, const RenderColor& c); void release(WgContext& context); }; struct WgRenderDataPaint { BBox aabb{{},{}}; RenderRegion viewport{}; Array clips; Matrix transform; virtual ~WgRenderDataPaint() {}; virtual void release(WgContext& context); virtual Type type() { return Type::Undefined; }; void updateClips(tvg::Array &clips); }; struct WgRenderDataShape: public WgRenderDataPaint { WgRenderSettings renderSettingsShape{}; WgRenderSettings renderSettingsStroke{}; WgMeshData meshBBox{}; WgMeshData meshShape{}; WgMeshData meshShapeBBox{}; WgMeshData meshStrokes{}; WgMeshData meshStrokesBBox{}; bool strokeFirst{}; FillRule fillRule{}; bool convex{}; BBox bbox; void updateBBox(BBox bb); void updateAABB(const Matrix& matrix); void updateVisibility(const RenderShape& rshape, uint8_t opacity); void updateMeshes(const RenderShape& rshape, RenderUpdateFlag flag, const Matrix& matrix); void releaseMeshes(); void release(WgContext& context) override; Type type() override { return Type::Shape; }; }; class WgRenderDataShapePool { private: Array mPool; Array mList; public: WgRenderDataShape* allocate(WgContext& context); void free(WgContext& context, WgRenderDataShape* renderData); void release(WgContext& context); }; struct WgRenderDataPicture: public WgRenderDataPaint { WgRenderSettings renderSettings{}; WgImageData imageData{}; WgMeshData meshData{}; void updateSurface(WgContext& context, const RenderSurface* surface); void release(WgContext& context) override; Type type() override { return Type::Picture; }; }; class WgRenderDataPicturePool { private: Array mPool; Array mList; public: WgRenderDataPicture* allocate(WgContext& context); void free(WgContext& context, WgRenderDataPicture* dataPicture); void release(WgContext& context); }; // gaussian blur, drop shadow, fill, tint, tritone #define WG_GAUSSIAN_MAX_LEVEL 3 struct WgRenderDataEffectParams { WGPUBindGroup bindGroupParams{}; WGPUBuffer bufferParams{}; uint32_t extend{}; Point offset{}; void update(WgContext& context, WgShaderTypeEffectParams& effectParams); void update(WgContext& context, RenderEffectGaussianBlur* gaussian, const Matrix& transform); void update(WgContext& context, RenderEffectDropShadow* dropShadow, const Matrix& transform); void update(WgContext& context, RenderEffectFill* fill); void update(WgContext& context, RenderEffectTint* tint); void update(WgContext& context, RenderEffectTritone* tritone); void release(WgContext& context); }; // effect params pool class WgRenderDataEffectParamsPool { private: // pool contains all created but unused render data for params Array mPool; // list contains all created render data for params // to ensure that all created instances will be released Array mList; public: WgRenderDataEffectParams* allocate(WgContext& context); void free(WgContext& context, WgRenderDataEffectParams* renderData); void release(WgContext& context); }; class WgStageBufferGeometry { private: Array vbuffer; Array ibuffer; public: WGPUBuffer vbuffer_gpu{}; WGPUBuffer ibuffer_gpu{}; void append(WgMeshData* meshData); void append(WgRenderDataShape* renderDataShape); void append(WgRenderDataPicture* renderDataPicture); void initialize(WgContext& context){}; void release(WgContext& context); void clear(); void flush(WgContext& context); }; // typed uniform stage buffer with related bind groups handling template class WgStageBufferUniform { private: Array ubuffer; WGPUBuffer ubuffer_gpu{}; Array bbuffer; public: // append uniform data to cpu staged buffer and return related bind group index uint32_t append(const T& value) { ubuffer.push(value); return ubuffer.count - 1; } void flush(WgContext& context) { // flush data to gpu buffer from cpu memory including reserved data to prevent future gpu buffer reallocations bool bufferChanged = context.allocateBufferUniform(ubuffer_gpu, (void*)ubuffer.data, ubuffer.reserved*sizeof(T)); // if gpu buffer handle was changed we must to remove all created binding groups if (bufferChanged) releaseBindGroups(context); // allocate bind groups for all new data items for (uint32_t i = bbuffer.count; i < ubuffer.count; i++) bbuffer.push(context.layouts.createBindGroupBuffer1Un(ubuffer_gpu, i*sizeof(T), sizeof(T))); assert(bbuffer.count >= ubuffer.count); } // please, use index that was returned from append method WGPUBindGroup operator[](const uint32_t index) const { return bbuffer[index]; } void clear() { ubuffer.clear(); } void release(WgContext& context) { context.releaseBuffer(ubuffer_gpu); releaseBindGroups(context); } void releaseBindGroups(WgContext& context) { ARRAY_FOREACH(p, bbuffer) context.layouts.releaseBindGroup(*p); bbuffer.clear(); } }; struct WgIntersector { bool isPointInTriangle(const Point& p, const Point& a, const Point& b, const Point& c); bool isPointInTris(const Point& p, const WgMeshData& mesh, const Matrix& tr); bool isPointInMesh(const Point& p, const WgMeshData& mesh, const Matrix& tr); bool intersectClips(const Point& pt, const Array& clips); bool intersectShape(const RenderRegion region, const WgRenderDataShape* shape); bool intersectImage(const RenderRegion region, const WgRenderDataPicture* image); }; #endif // _TVG_WG_RENDER_DATA_H_ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-lexer.h000664 001750 001750 00000030053 15164251010 043132 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JS_LEXER_H #define JS_LEXER_H #include "ecma-globals.h" #include "common.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_lexer Lexer * @{ */ /** * Flags for lexer_parse_identifier. */ typedef enum { LEXER_PARSE_NO_OPTS = 0, /**< no options */ LEXER_PARSE_CHECK_KEYWORDS = (1 << 0), /**< check keywords */ LEXER_PARSE_NO_STRICT_IDENT_ERROR = (1 << 1), /**< do not throw exception for strict mode keywords */ LEXER_PARSE_CHECK_START_AND_RETURN = (1 << 2), /**< check identifier start and return */ LEXER_PARSE_CHECK_PART_AND_RETURN = (1 << 3), /**< check identifier part and return */ } lexer_parse_options_t; /** * Lexer token types. */ typedef enum { LEXER_EOS, /**< end of source */ /* Primary expressions */ LEXER_LITERAL, /**< literal token */ LEXER_KEYW_THIS, /**< this */ LEXER_LIT_TRUE, /**< true (not a keyword!) */ LEXER_LIT_FALSE, /**< false (not a keyword!) */ LEXER_LIT_NULL, /**< null (not a keyword!) */ LEXER_TEMPLATE_LITERAL, /**< multi segment template literal */ LEXER_THREE_DOTS, /**< ... (rest or spread operator) */ /* Unary operators * IMPORTANT: update CBC_UNARY_OP_TOKEN_TO_OPCODE and * CBC_UNARY_LVALUE_OP_TOKEN_TO_OPCODE after changes. */ #define LEXER_IS_UNARY_OP_TOKEN(token_type) ((token_type) >= LEXER_PLUS && (token_type) <= LEXER_DECREASE) #define LEXER_IS_UNARY_LVALUE_OP_TOKEN(token_type) ((token_type) >= LEXER_KEYW_DELETE && (token_type) <= LEXER_DECREASE) LEXER_PLUS, /**< "+" */ LEXER_NEGATE, /**< "-" */ LEXER_LOGICAL_NOT, /**< "!" */ LEXER_BIT_NOT, /**< "~" */ LEXER_KEYW_VOID, /**< void */ LEXER_KEYW_TYPEOF, /**< typeof */ LEXER_KEYW_AWAIT, /**< await */ LEXER_KEYW_DELETE, /**< delete */ LEXER_INCREASE, /**< "++" */ LEXER_DECREASE, /**< "--" */ /* Binary operators * IMPORTANT: update CBC_BINARY_OP_TOKEN_TO_OPCODE, * CBC_BINARY_LVALUE_OP_TOKEN_TO_OPCODE and * parser_binary_precedence_table after changes. */ /** * Index of first binary operation opcode. */ #define LEXER_FIRST_BINARY_OP LEXER_ASSIGN /** * Index of last binary operation opcode. */ #define LEXER_LAST_BINARY_OP LEXER_EXPONENTIATION /** * Checks whether the token is a binary operation token. */ #define LEXER_IS_BINARY_OP_TOKEN(token_type) \ ((token_type) >= LEXER_FIRST_BINARY_OP && (token_type) <= LEXER_LAST_BINARY_OP) /** * Checks whether the token is an lvalue (assignment) operation token. */ #define LEXER_IS_BINARY_LVALUE_OP_TOKEN(token_type) \ ((token_type) >= LEXER_ASSIGN && (token_type) <= LEXER_ASSIGN_BIT_XOR) /** * Checks whether the token is a non-lvalue (assignment) operation token. */ #define LEXER_IS_BINARY_NON_LVALUE_OP_TOKEN(token_type) \ ((token_type) >= LEXER_QUESTION_MARK && (token_type) <= LEXER_LAST_BINARY_OP) LEXER_ASSIGN, /**< "=" (prec: 3) */ LEXER_ASSIGN_ADD, /**< "+=" (prec: 3) */ LEXER_ASSIGN_SUBTRACT, /**< "-=" (prec: 3) */ LEXER_ASSIGN_MULTIPLY, /**< "*=" (prec: 3) */ LEXER_ASSIGN_DIVIDE, /**< "/=" (prec: 3) */ LEXER_ASSIGN_MODULO, /**< "%=" (prec: 3) */ LEXER_ASSIGN_EXPONENTIATION, /**< "**=" (prec: 3) */ LEXER_ASSIGN_NULLISH_COALESCING, /**< "??=" (prec: 3) */ LEXER_ASSIGN_LOGICAL_OR, /**< "||=" (prec: 3) */ LEXER_ASSIGN_LOGICAL_AND, /**< "&&=" (prec: 3) */ LEXER_ASSIGN_LEFT_SHIFT, /**< "<<=" (prec: 3) */ LEXER_ASSIGN_RIGHT_SHIFT, /**< ">>=" (prec: 3) */ LEXER_ASSIGN_UNS_RIGHT_SHIFT, /**< ">>>=" (prec: 3) */ LEXER_ASSIGN_BIT_AND, /**< "&=" (prec: 3) */ LEXER_ASSIGN_BIT_OR, /**< "|=" (prec: 3) */ LEXER_ASSIGN_BIT_XOR, /**< "^=" (prec: 3) */ LEXER_QUESTION_MARK, /**< "?" (prec: 4) */ LEXER_NULLISH_COALESCING, /**< "??" (prec: 5) */ LEXER_LOGICAL_OR, /**< "||" (prec: 6) */ LEXER_LOGICAL_AND, /**< "&&" (prec: 7) */ LEXER_BIT_OR, /**< "|" (prec: 8) */ LEXER_BIT_XOR, /**< "^" (prec: 9) */ LEXER_BIT_AND, /**< "&" (prec: 10) */ LEXER_EQUAL, /**< "==" (prec: 11) */ LEXER_NOT_EQUAL, /**< "!=" (prec: 11) */ LEXER_STRICT_EQUAL, /**< "===" (prec: 11) */ LEXER_STRICT_NOT_EQUAL, /**< "!==" (prec: 11) */ LEXER_LESS, /**< "<" (prec: 12) */ LEXER_GREATER, /**< ">" (prec: 12) */ LEXER_LESS_EQUAL, /**< "<=" (prec: 12) */ LEXER_GREATER_EQUAL, /**< ">=" (prec: 12) */ LEXER_KEYW_IN, /**< in (prec: 12) */ LEXER_KEYW_INSTANCEOF, /**< instanceof (prec: 12) */ LEXER_LEFT_SHIFT, /**< "<<" (prec: 13) */ LEXER_RIGHT_SHIFT, /**< ">>" (prec: 13) */ LEXER_UNS_RIGHT_SHIFT, /**< ">>>" (prec: 13) */ LEXER_ADD, /**< "+" (prec: 14) */ LEXER_SUBTRACT, /**< "-" (prec: 14) */ LEXER_MULTIPLY, /**< "*" (prec: 15) */ LEXER_DIVIDE, /**< "/" (prec: 15) */ LEXER_MODULO, /**< "%" (prec: 15) */ LEXER_EXPONENTIATION, /**< "**" (prec: 16) */ LEXER_LEFT_BRACE, /**< "{" */ LEXER_LEFT_PAREN, /**< "(" */ LEXER_LEFT_SQUARE, /**< "[" */ LEXER_RIGHT_BRACE, /**< "}" */ LEXER_RIGHT_PAREN, /**< ")" */ LEXER_RIGHT_SQUARE, /**< "]" */ LEXER_DOT, /**< "." */ LEXER_SEMICOLON, /**< ";" */ LEXER_COLON, /**< ":" */ LEXER_COMMA, /**< "," */ LEXER_ARROW, /**< "=>" */ LEXER_HASHMARK, /**< "#" */ LEXER_KEYW_BREAK, /**< break */ LEXER_KEYW_DO, /**< do */ LEXER_KEYW_CASE, /**< case */ LEXER_KEYW_ELSE, /**< else */ LEXER_KEYW_NEW, /**< new */ LEXER_KEYW_VAR, /**< var */ LEXER_KEYW_CATCH, /**< catch */ LEXER_KEYW_FINALLY, /**< finally */ LEXER_KEYW_RETURN, /**< return */ LEXER_KEYW_CONTINUE, /**< continue */ LEXER_KEYW_FOR, /**< for */ LEXER_KEYW_SWITCH, /**< switch */ LEXER_KEYW_WHILE, /**< while */ LEXER_KEYW_DEBUGGER, /**< debugger */ LEXER_KEYW_FUNCTION, /**< function */ LEXER_KEYW_WITH, /**< with */ LEXER_KEYW_DEFAULT, /**< default */ LEXER_KEYW_IF, /**< if */ LEXER_KEYW_THROW, /**< throw */ LEXER_KEYW_TRY, /**< try */ LEXER_KEYW_CLASS, /**< class */ LEXER_KEYW_EXTENDS, /**< extends */ LEXER_KEYW_SUPER, /**< super */ LEXER_KEYW_CONST, /**< const */ LEXER_KEYW_EXPORT, /**< export */ LEXER_KEYW_IMPORT, /**< import */ LEXER_KEYW_ENUM, /**< enum */ #define LEXER_FIRST_NON_RESERVED_KEYWORD LEXER_EXPRESSION_START /* These are virtual tokens. */ LEXER_EXPRESSION_START, /**< expression start */ LEXER_PROPERTY_GETTER, /**< property getter function */ LEXER_PROPERTY_SETTER, /**< property setter function */ LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */ LEXER_ASSIGN_GROUP_EXPR, /**< identifier for the assignment is located in a group expression */ LEXER_ASSIGN_CONST, /**< a const binding is reassigned */ LEXER_INVALID_PATTERN, /**< special value for invalid destructuring pattern */ LEXER_PRIVATE_PRIMARY_EXPR, /**< private field in primary expression position */ LEXER_ASSIGN_REFERENCE, /**< special value for reference assignment */ /* Keywords which are not keyword tokens. */ LEXER_KEYW_ASYNC, /**< async */ #if JERRY_MODULE_SYSTEM LEXER_KEYW_META, /**< meta */ #endif /* JERRY_MODULE_SYSTEM */ /* Keywords which cannot be assigned in strict mode. */ #define LEXER_FIRST_NON_STRICT_ARGUMENTS LEXER_KEYW_EVAL LEXER_KEYW_EVAL, /**< eval */ LEXER_KEYW_ARGUMENTS, /**< arguments */ /* Future strict reserved words: these keywords * must form a group after non-reserved keywords. */ #define LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD LEXER_KEYW_IMPLEMENTS LEXER_KEYW_IMPLEMENTS, /**< implements */ LEXER_KEYW_PRIVATE, /**< private */ LEXER_KEYW_PUBLIC, /**< public */ LEXER_KEYW_INTERFACE, /**< interface */ LEXER_KEYW_PACKAGE, /**< package */ LEXER_KEYW_PROTECTED, /**< protected */ /* Context dependent future strict reserved words: * See also: ECMA-262 v6, 11.6.2.1 */ LEXER_KEYW_LET, /**< let */ LEXER_KEYW_YIELD, /**< yield */ LEXER_KEYW_STATIC, /**< static */ } lexer_token_type_t; #define LEXER_NEWLINE_LS_PS_BYTE_1 0xe2 #define LEXER_NEWLINE_LS_PS_BYTE_23(source) \ ((source)[1] == LIT_UTF8_2_BYTE_CODE_POINT_MIN && ((source)[2] | 0x1) == 0xa9) #define LEXER_IS_LEFT_BRACKET(type) \ ((type) == LEXER_LEFT_BRACE || (type) == LEXER_LEFT_PAREN || (type) == LEXER_LEFT_SQUARE) #define LEXER_IS_RIGHT_BRACKET(type) \ ((type) == LEXER_RIGHT_BRACE || (type) == LEXER_RIGHT_PAREN || (type) == LEXER_RIGHT_SQUARE) #define LEXER_UNARY_OP_TOKEN_TO_OPCODE(token_type) ((((token_type) -LEXER_PLUS) * 2) + CBC_PLUS) #define LEXER_UNARY_LVALUE_OP_TOKEN_TO_OPCODE(token_type) ((((token_type) -LEXER_INCREASE) * 6) + CBC_PRE_INCR) #define LEXER_BINARY_OP_TOKEN_TO_OPCODE(token_type) ((uint16_t) ((((token_type) -LEXER_BIT_OR) * 3) + CBC_BIT_OR)) #define LEXER_BINARY_LVALUE_OP_TOKEN_TO_OPCODE(token_type) \ ((cbc_opcode_t) ((((token_type) -LEXER_ASSIGN_ADD) * 2) + CBC_ASSIGN_ADD)) /** * Maximum local buffer size for identifiers which contains escape sequences. */ #define LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE 48 /** * Lexer newline flags. */ typedef enum { LEXER_WAS_NEWLINE = (1u << 0), /**< newline was seen */ LEXER_NO_SKIP_SPACES = (1u << 1) /**< ignore skip spaces */ } lexer_newline_flags_t; /** * Lexer object identifier parse options. */ typedef enum { LEXER_OBJ_IDENT_NO_OPTS = 0, /**< no options */ LEXER_OBJ_IDENT_ONLY_IDENTIFIERS = (1u << 0), /**< only identifiers are accepted */ LEXER_OBJ_IDENT_CLASS_IDENTIFIER = (1u << 1), /**< expect identifier inside a class body */ LEXER_OBJ_IDENT_CLASS_NO_STATIC = (1u << 2), /**< static keyword was not present before the identifier */ LEXER_OBJ_IDENT_OBJECT_PATTERN = (1u << 3), /**< parse "get"/"set" as string literal in object pattern */ LEXER_OBJ_IDENT_CLASS_PRIVATE = (1u << 4), /**< static keyword was not present before the identifier */ LEXER_OBJ_IDENT_SET_FUNCTION_START = 0, /**< set function start (disabled) */ } lexer_obj_ident_opts_t; /** * Lexer string options. */ typedef enum { LEXER_STRING_NO_OPTS = (1u << 0), /**< no options */ LEXER_STRING_RAW = (1u << 1), /**< raw string ECMAScript v6, 11.8.6.1: TVR */ } lexer_string_options_t; /** * Lexer number types. */ typedef enum { LEXER_NUMBER_DECIMAL, /**< decimal number */ LEXER_NUMBER_HEXADECIMAL, /**< hexadecimal number */ LEXER_NUMBER_OCTAL, /**< octal number */ LEXER_NUMBER_BINARY, /**< binary number */ #if JERRY_BUILTIN_BIGINT LEXER_NUMBER_BIGINT, /**< bigint number */ #endif /* JERRY_BUILTIN_BIGINT */ } lexer_number_type_t; /** * Lexer literal flags. **/ typedef enum { LEXER_LIT_LOCATION_NO_OPTS = 0, /**< no options */ LEXER_LIT_LOCATION_HAS_ESCAPE = (1 << 0), /**< binding has escape */ LEXER_LIT_LOCATION_IS_ASCII = (1 << 1), /**< all characters are ascii characters */ } lexer_lit_location_flags_t; /** * Lexer character (string / identifier) literal data. */ typedef struct { const uint8_t *char_p; /**< start of identifier or string token */ prop_length_t length; /**< length or index of a literal */ uint8_t type; /**< type of the current literal */ uint8_t status_flags; /**< any combination of lexer_lit_location_flags_t status bits */ } lexer_lit_location_t; /** * Lexer token. */ typedef struct { uint8_t type; /**< token type */ uint8_t keyword_type; /**< keyword type for identifiers */ uint8_t extra_value; /**< helper value for different purposes */ uint8_t flags; /**< flag bits for the current token */ parser_line_counter_t line; /**< token start line */ parser_line_counter_t column; /**< token start column */ lexer_lit_location_t lit_location; /**< extra data for character literals */ } lexer_token_t; /** * Literal data set by lexer_construct_literal_object. */ typedef struct { lexer_literal_t *literal_p; /**< pointer to the literal object */ uint16_t index; /**< literal index */ } lexer_lit_object_t; /** * @} * @} * @} */ #endif /* !JS_LEXER_H */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-exceptions.h000664 001750 001750 00000003655 15164251010 045655 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_EXCEPTIONS_H #define ECMA_EXCEPTIONS_H #include "ecma-globals.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup exceptions Exceptions * @{ */ jerry_error_t ecma_get_error_type (ecma_object_t *error_object_p); ecma_object_t *ecma_new_standard_error (jerry_error_t error_type, ecma_string_t *message_string_p); #if JERRY_ERROR_MESSAGES ecma_value_t ecma_raise_standard_error_with_format (jerry_error_t error_type, const char *msg_p, ...); #endif /* JERRY_ERROR_MESSAGES */ ecma_value_t ecma_raise_standard_error (jerry_error_t error_type, ecma_error_msg_t msg); ecma_value_t ecma_raise_common_error (ecma_error_msg_t msg); ecma_value_t ecma_raise_range_error (ecma_error_msg_t msg); ecma_value_t ecma_raise_reference_error (ecma_error_msg_t msg); ecma_value_t ecma_raise_syntax_error (ecma_error_msg_t msg); ecma_value_t ecma_raise_type_error (ecma_error_msg_t msg); ecma_value_t ecma_raise_uri_error (ecma_error_msg_t msg); #if (JERRY_STACK_LIMIT != 0) ecma_value_t ecma_raise_maximum_callstack_error (void); #endif /* (JERRY_STACK_LIMIT != 0) */ ecma_value_t ecma_new_aggregate_error (ecma_value_t error_list_val, ecma_value_t message_val); ecma_value_t ecma_raise_aggregate_error (ecma_value_t error_list_val, ecma_value_t message_val); /** * @} * @} */ #endif /* !ECMA_EXCEPTIONS_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_png/tvgPngLoader.h000664 001750 001750 00000003042 15164251010 036610 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_PNG_LOADER_H_ #define _TVG_PNG_LOADER_H_ #include #include "tvgLoader.h" class PngLoader : public ImageLoader { public: PngLoader(); ~PngLoader(); bool open(const char* path) override; bool open(const char* data, uint32_t size, const char* rpath, bool copy) override; bool read() override; private: void clear(); png_imagep image = nullptr; }; #endif //_TVG_PNG_LOADER_H_ lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-referenceerror-prototype.inc.h000664 001750 001750 00000002477 15164251010 053676 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * ReferenceError.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.7.8 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_REFERENCE_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.9 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_REFERENCE_ERROR_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.10 */ STRING_VALUE (LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/lossless.h000664 001750 001750 00000027654 15164251010 035216 0ustar00ddennedyddennedy000000 000000 // Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Image transforms and color space conversion methods for lossless decoder. // // Authors: Vikas Arora (vikaas.arora@gmail.com) // Jyrki Alakuijala (jyrki@google.com) #ifndef WEBP_DSP_LOSSLESS_H_ #define WEBP_DSP_LOSSLESS_H_ #include "../webp/types.h" #include "../webp/decode.h" // #include "../enc/histogram.h" #include "../utils/utils.h" #ifdef __cplusplus extern "C" { #endif // Not a trivial literal symbol. #define VP8L_NON_TRIVIAL_SYM (0xffffffff) //------------------------------------------------------------------------------ // Decoding typedef uint32_t (*VP8LPredictorFunc)(uint32_t left, const uint32_t* const top); extern VP8LPredictorFunc VP8LPredictors[16]; typedef void (*VP8LProcessBlueAndRedFunc)(uint32_t* argb_data, int num_pixels); extern VP8LProcessBlueAndRedFunc VP8LAddGreenToBlueAndRed; typedef struct { // Note: the members are uint8_t, so that any negative values are // automatically converted to "mod 256" values. uint8_t green_to_red_; uint8_t green_to_blue_; uint8_t red_to_blue_; } VP8LMultipliers; typedef void (*VP8LTransformColorFunc)(const VP8LMultipliers* const m, uint32_t* argb_data, int num_pixels); extern VP8LTransformColorFunc VP8LTransformColorInverse; struct VP8LTransform; // Defined in dec/vp8li.h. // Performs inverse transform of data given transform information, start and end // rows. Transform will be applied to rows [row_start, row_end[. // The *in and *out pointers refer to source and destination data respectively // corresponding to the intermediate row (row_start). void VP8LInverseTransform(const struct VP8LTransform* const transform, int row_start, int row_end, const uint32_t* const in, uint32_t* const out); // Color space conversion. typedef void (*VP8LConvertFunc)(const uint32_t* src, int num_pixels, uint8_t* dst); extern VP8LConvertFunc VP8LConvertBGRAToRGB; extern VP8LConvertFunc VP8LConvertBGRAToRGBA; extern VP8LConvertFunc VP8LConvertBGRAToRGBA4444; extern VP8LConvertFunc VP8LConvertBGRAToRGB565; extern VP8LConvertFunc VP8LConvertBGRAToBGR; // Converts from BGRA to other color spaces. void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, WEBP_CSP_MODE out_colorspace, uint8_t* const rgba); // color mapping related functions. static WEBP_INLINE uint32_t VP8GetARGBIndex(uint32_t idx) { return (idx >> 8) & 0xff; } static WEBP_INLINE uint8_t VP8GetAlphaIndex(uint8_t idx) { return idx; } static WEBP_INLINE uint32_t VP8GetARGBValue(uint32_t val) { return val; } static WEBP_INLINE uint8_t VP8GetAlphaValue(uint32_t val) { return (val >> 8) & 0xff; } typedef void (*VP8LMapARGBFunc)(const uint32_t* src, const uint32_t* const color_map, uint32_t* dst, int y_start, int y_end, int width); typedef void (*VP8LMapAlphaFunc)(const uint8_t* src, const uint32_t* const color_map, uint8_t* dst, int y_start, int y_end, int width); extern VP8LMapARGBFunc VP8LMapColor32b; extern VP8LMapAlphaFunc VP8LMapColor8b; // Similar to the static method ColorIndexInverseTransform() that is part of // lossless.c, but used only for alpha decoding. It takes uint8_t (rather than // uint32_t) arguments for 'src' and 'dst'. void VP8LColorIndexInverseTransformAlpha( const struct VP8LTransform* const transform, int y_start, int y_end, const uint8_t* src, uint8_t* dst); // Expose some C-only fallback functions void VP8LTransformColorInverse_C(const VP8LMultipliers* const m, uint32_t* data, int num_pixels); void VP8LConvertBGRAToRGB_C(const uint32_t* src, int num_pixels, uint8_t* dst); void VP8LConvertBGRAToRGBA_C(const uint32_t* src, int num_pixels, uint8_t* dst); void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src, int num_pixels, uint8_t* dst); void VP8LConvertBGRAToRGB565_C(const uint32_t* src, int num_pixels, uint8_t* dst); void VP8LConvertBGRAToBGR_C(const uint32_t* src, int num_pixels, uint8_t* dst); void VP8LAddGreenToBlueAndRed_C(uint32_t* data, int num_pixels); // Must be called before calling any of the above methods. void VP8LDspInit(void); //------------------------------------------------------------------------------ // Encoding extern VP8LProcessBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed; extern VP8LTransformColorFunc VP8LTransformColor; typedef void (*VP8LCollectColorBlueTransformsFunc)( const uint32_t* argb, int stride, int tile_width, int tile_height, int green_to_blue, int red_to_blue, int histo[]); extern VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms; typedef void (*VP8LCollectColorRedTransformsFunc)( const uint32_t* argb, int stride, int tile_width, int tile_height, int green_to_red, int histo[]); extern VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms; // Expose some C-only fallback functions void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data, int num_pixels); void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels); //------------------------------------------------------------------------------ // Image transforms. void VP8LResidualImage(int width, int height, int bits, int low_effort, uint32_t* const argb, uint32_t* const argb_scratch, uint32_t* const image); void VP8LColorSpaceTransform(int width, int height, int bits, int quality, uint32_t* const argb, uint32_t* image); //------------------------------------------------------------------------------ // Misc methods. // Computes sampled size of 'size' when sampling using 'sampling bits'. static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size, uint32_t sampling_bits) { return (size + (1 << sampling_bits) - 1) >> sampling_bits; } // ----------------------------------------------------------------------------- // Faster logarithm for integers. Small values use a look-up table. #define LOG_LOOKUP_IDX_MAX 256 extern const float kLog2Table[LOG_LOOKUP_IDX_MAX]; extern const float kSLog2Table[LOG_LOOKUP_IDX_MAX]; typedef float (*VP8LFastLog2SlowFunc)(uint32_t v); extern VP8LFastLog2SlowFunc VP8LFastLog2Slow; extern VP8LFastLog2SlowFunc VP8LFastSLog2Slow; static WEBP_INLINE float VP8LFastLog2(uint32_t v) { return (v < LOG_LOOKUP_IDX_MAX) ? kLog2Table[v] : VP8LFastLog2Slow(v); } // Fast calculation of v * log2(v) for integer input. static WEBP_INLINE float VP8LFastSLog2(uint32_t v) { return (v < LOG_LOOKUP_IDX_MAX) ? kSLog2Table[v] : VP8LFastSLog2Slow(v); } // ----------------------------------------------------------------------------- // Huffman-cost related functions. typedef double (*VP8LCostFunc)(const uint32_t* population, int length); typedef double (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y, int length); extern VP8LCostFunc VP8LExtraCost; extern VP8LCostCombinedFunc VP8LExtraCostCombined; typedef struct { // small struct to hold counters int counts[2]; // index: 0=zero steak, 1=non-zero streak int streaks[2][2]; // [zero/non-zero][streak<3 / streak>=3] } VP8LStreaks; typedef VP8LStreaks (*VP8LCostCountFunc)(const uint32_t* population, int length); typedef VP8LStreaks (*VP8LCostCombinedCountFunc)(const uint32_t* X, const uint32_t* Y, int length); extern VP8LCostCountFunc VP8LHuffmanCostCount; extern VP8LCostCombinedCountFunc VP8LHuffmanCostCombinedCount; // Get the symbol entropy for the distribution 'population'. // Set 'trivial_sym', if there's only one symbol present in the distribution. double VP8LPopulationCost(const uint32_t* const population, int length, uint32_t* const trivial_sym); // Get the combined symbol entropy for the distributions 'X' and 'Y'. double VP8LGetCombinedEntropy(const uint32_t* const X, const uint32_t* const Y, int length); // ----------------------------------------------------------------------------- // PrefixEncode() static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) { const int log_floor = BitsLog2Floor(n); if (n == (n & ~(n - 1))) // zero or a power of two. return log_floor; else return log_floor + 1; } // Splitting of distance and length codes into prefixes and // extra bits. The prefixes are encoded with an entropy code // while the extra bits are stored just as normal bits. static WEBP_INLINE void VP8LPrefixEncodeBitsNoLUT(int distance, int* const code, int* const extra_bits) { const int highest_bit = BitsLog2Floor(--distance); const int second_highest_bit = (distance >> (highest_bit - 1)) & 1; *extra_bits = highest_bit - 1; *code = 2 * highest_bit + second_highest_bit; } static WEBP_INLINE void VP8LPrefixEncodeNoLUT(int distance, int* const code, int* const extra_bits, int* const extra_bits_value) { const int highest_bit = BitsLog2Floor(--distance); const int second_highest_bit = (distance >> (highest_bit - 1)) & 1; *extra_bits = highest_bit - 1; *extra_bits_value = distance & ((1 << *extra_bits) - 1); *code = 2 * highest_bit + second_highest_bit; } #define PREFIX_LOOKUP_IDX_MAX 512 typedef struct { int8_t code_; int8_t extra_bits_; } VP8LPrefixCode; // These tables are derived using VP8LPrefixEncodeNoLUT. extern const VP8LPrefixCode kPrefixEncodeCode[PREFIX_LOOKUP_IDX_MAX]; extern const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX]; static WEBP_INLINE void VP8LPrefixEncodeBits(int distance, int* const code, int* const extra_bits) { if (distance < PREFIX_LOOKUP_IDX_MAX) { const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance]; *code = prefix_code.code_; *extra_bits = prefix_code.extra_bits_; } else { VP8LPrefixEncodeBitsNoLUT(distance, code, extra_bits); } } static WEBP_INLINE void VP8LPrefixEncode(int distance, int* const code, int* const extra_bits, int* const extra_bits_value) { if (distance < PREFIX_LOOKUP_IDX_MAX) { const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance]; *code = prefix_code.code_; *extra_bits = prefix_code.extra_bits_; *extra_bits_value = kPrefixEncodeExtraBitsValue[distance]; } else { VP8LPrefixEncodeNoLUT(distance, code, extra_bits, extra_bits_value); } } // In-place difference of each component with mod 256. static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { const uint32_t alpha_and_green = 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u); const uint32_t red_and_blue = 0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu); return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); } void VP8LBundleColorMap(const uint8_t* const row, int width, int xbits, uint32_t* const dst); // Must be called before calling any of the above methods. void VP8LEncDspInit(void); //------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" #endif #endif // WEBP_DSP_LOSSLESS_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/000775 001750 001750 00000000000 15164251010 032735 5ustar00ddennedyddennedy000000 000000 src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/tvgWebpLoader.cpp000664 001750 001750 00000007420 15164251010 035567 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "webp/decode.h" #include "tvgWebpLoader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ void WebpLoader::clear() { if (freeData) tvg::free(data); data = nullptr; freeData = false; } void WebpLoader::run(unsigned tid) { if (surface.cs == ColorSpace::ARGB8888 || surface.cs == ColorSpace::ARGB8888S) { surface.buf8 = WebPDecodeBGRA(data, size, nullptr, nullptr); surface.cs = ColorSpace::ARGB8888; } else { surface.buf8 = WebPDecodeRGBA(data, size, nullptr, nullptr); surface.cs = ColorSpace::ABGR8888; } surface.stride = static_cast(w); surface.w = static_cast(w); surface.h = static_cast(h); surface.channelSize = sizeof(uint32_t); surface.premultiplied = true; clear(); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ WebpLoader::WebpLoader() : ImageLoader(FileType::Webp) { } WebpLoader::~WebpLoader() { done(); clear(); tvg::free(surface.buf8); } bool WebpLoader::open(const char* path) { #ifdef THORVG_FILE_IO_SUPPORT if (!(data = (uint8_t*)LoadModule::open(path, size))) return false; int width, height; if (!WebPGetInfo(data, size, &width, &height)) return false; w = static_cast(width); h = static_cast(height); freeData = true; return true; #else return false; #endif } bool WebpLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy) { if (copy) { this->data = tvg::malloc(size); if (!this->data) return false; memcpy((uint8_t*)this->data, data, size); freeData = true; } else { this->data = (uint8_t*) data; freeData = false; } int width, height; if (!WebPGetInfo(this->data, size, &width, &height)) return false; w = static_cast(width); h = static_cast(height); this->size = size; return true; } bool WebpLoader::read() { if (!LoadModule::read()) return true; if (!data || w == 0 || h == 0) return false; surface.cs = ImageLoader::cs; TaskScheduler::request(this); return true; } bool WebpLoader::close() { if (!LoadModule::close()) return false; this->done(); return true; } RenderSurface* WebpLoader::bitmap() { this->done(); return ImageLoader::bitmap(); }src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlGpuBuffer.h000664 001750 001750 00000004312 15164251010 036524 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_GPU_BUFFER_H_ #define _TVG_GL_GPU_BUFFER_H_ #include "tvgGlCommon.h" class GlGpuBuffer { public: enum class Target { ARRAY_BUFFER = GL_ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER = GL_ELEMENT_ARRAY_BUFFER, UNIFORM_BUFFER = GL_UNIFORM_BUFFER, }; GlGpuBuffer(); ~GlGpuBuffer(); void updateBufferData(Target target, uint32_t size, const void* data); void bind(Target target); void unbind(Target target); uint32_t getBufferId() { return mGlBufferId; } private: uint32_t mGlBufferId = 0; }; class GlStageBuffer { public: GlStageBuffer(); ~GlStageBuffer(); uint32_t push(void* data, uint32_t size, bool alignGpuOffset = false); uint32_t pushIndex(void* data, uint32_t size); bool flushToGPU(); void bind(); void unbind(); GLuint getBufferId(); private: void alignOffset(uint32_t size); GLuint mVao = 0; GlGpuBuffer mGpuBuffer = {}; GlGpuBuffer mGpuIndexBuffer = {}; Array mStageBuffer = {}; Array mIndexBuffer = {}; }; #endif /* _TVG_GL_GPU_BUFFER_H_ */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-promise-prototype.cpp000664 001750 001750 00000005374 15164251010 052126 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-globals.h" #include "ecma-promise-object.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_PROMISE_PROTOTYPE_ROUTINE_START = 0, ECMA_PROMISE_PROTOTYPE_ROUTINE_THEN, ECMA_PROMISE_PROTOTYPE_ROUTINE_CATCH, ECMA_PROMISE_PROTOTYPE_ROUTINE_FINALLY }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-promise-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID promise_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup promiseprototype ECMA Promise.prototype object built-in * @{ */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_promise_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); switch (builtin_routine_id) { case ECMA_PROMISE_PROTOTYPE_ROUTINE_THEN: { return ecma_promise_then (this_arg, arguments_list_p[0], arguments_list_p[1]); } case ECMA_PROMISE_PROTOTYPE_ROUTINE_CATCH: { ecma_value_t args[] = { ECMA_VALUE_UNDEFINED, arguments_list_p[0] }; return ecma_op_invoke_by_magic_id (this_arg, LIT_MAGIC_STRING_THEN, args, 2); } case ECMA_PROMISE_PROTOTYPE_ROUTINE_FINALLY: { return ecma_promise_finally (this_arg, arguments_list_p[0]); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_promise_prototype_dispatch_routine */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/cross/ios_simulator_x86_64.txt000664 001750 001750 00000001157 15164251010 035022 0ustar00ddennedyddennedy000000 000000 # build for the ios simulator (Intel) [binaries] cpp = ['clang++', '-arch', 'x86_64', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] ar = 'ar' strip = 'strip' [properties] root = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer' has_function_printf = true [built-in options] cpp_args = ['-mios-simulator-version-min=11.0'] cpp_link_args = ['-mios-simulator-version-min=11.0'] [host_machine] system = 'darwin' subsystem = 'ios' kernel = 'xnu' cpu_family = 'x86_64' cpu = 'x86_64' endian = 'little' src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgShaderSrc.h000664 001750 001750 00000004004 15164251010 036541 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_SHADER_SRC_H_ #define _TVG_WG_SHADER_SRC_H_ // helper shaders extern const char* cShaderSrc_Stencil; extern const char* cShaderSrc_Depth; // shaders normal blend extern const char* cShaderSrc_Solid; extern const char* cShaderSrc_Linear; extern const char* cShaderSrc_Radial; extern const char* cShaderSrc_Image; extern const char* cShaderSrc_Scene; // shaders custrom blend extern const char* cShaderSrc_Solid_Blend; extern const char* cShaderSrc_Linear_Blend; extern const char* cShaderSrc_Radial_Blend; extern const char* cShaderSrc_Image_Blend; extern const char* cShaderSrc_Scene_Blend; extern const char* cShaderSrc_BlendFuncs; // shaders scene compose extern const char* cShaderSrc_Scene_Compose; // shaders blit extern const char* cShaderSrc_Blit; // shader sources effects extern const char* cShaderSrc_Shadow; extern const char* cShaderSrc_Effects; #endif // _TVG_WG_SHEDER_SRC_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgRenderData.cpp000664 001750 001750 00000050730 15164251010 037236 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgWgTessellator.h" #include "tvgWgRenderData.h" #include "tvgWgShaderTypes.h" //*********************************************************************** // WgImageData //*********************************************************************** void WgImageData::update(WgContext& context, const RenderSurface* surface) { // get appropriate texture format from color space WGPUTextureFormat texFormat = WGPUTextureFormat_BGRA8Unorm; if (surface->cs == ColorSpace::ABGR8888S) texFormat = WGPUTextureFormat_RGBA8Unorm; if (surface->cs == ColorSpace::Grayscale8) texFormat = WGPUTextureFormat_R8Unorm; // allocate new texture handle bool texHandleChanged = context.allocateTexture(texture, surface->w, surface->h, texFormat, surface->data); // update texture view of texture handle was changed if (texHandleChanged) { context.releaseTextureView(textureView); textureView = context.createTextureView(texture); // update bind group context.layouts.releaseBindGroup(bindGroup); bindGroup = context.layouts.createBindGroupTexSampled(context.samplerLinearClamp, textureView); } }; void WgImageData::update(WgContext& context, const Fill* fill) { // compute gradient data WgShaderTypeGradientData gradientData; gradientData.update(fill); // allocate new texture handle bool texHandleChanged = context.allocateTexture(texture, WG_TEXTURE_GRADIENT_SIZE, 1, WGPUTextureFormat_RGBA8Unorm, gradientData.data); // update texture view of texture handle was changed if (texHandleChanged) { context.releaseTextureView(textureView); textureView = context.createTextureView(texture); // get sampler by spread type WGPUSampler sampler = context.samplerLinearClamp; if (fill->spread() == FillSpread::Reflect) sampler = context.samplerLinearMirror; if (fill->spread() == FillSpread::Repeat) sampler = context.samplerLinearRepeat; // update bind group context.layouts.releaseBindGroup(bindGroup); bindGroup = context.layouts.createBindGroupTexSampled(sampler, textureView); } }; void WgImageData::release(WgContext& context) { context.layouts.releaseBindGroup(bindGroup); context.releaseTextureView(textureView); context.releaseTexture(texture); }; //*********************************************************************** // WgRenderSettings //*********************************************************************** void WgRenderSettings::update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity) { //TODO: Update separately according to the RenderUpdateFlag settings.transform.update(transform); settings.options.update(cs, opacity * opacityMultiplier); } void WgRenderSettings::update(WgContext& context, const Fill* fill) { assert(fill); settings.gradient.update(fill); gradientData.update(context, fill); // get gradient rasterisation settings rasterType = WgRenderRasterType::Gradient; if (fill->type() == Type::LinearGradient) fillType = WgRenderSettingsType::Linear; else if (fill->type() == Type::RadialGradient) fillType = WgRenderSettingsType::Radial; }; void WgRenderSettings::update(WgContext& context, const RenderColor& c) { settings.color.update(c); rasterType = WgRenderRasterType::Solid; fillType = WgRenderSettingsType::Solid; }; void WgRenderSettings::release(WgContext& context) { gradientData.release(context); }; //*********************************************************************** // WgRenderDataPaint //*********************************************************************** void WgRenderDataPaint::release(WgContext& context) { clips.clear(); }; void WgRenderDataPaint::updateClips(tvg::Array &clips) { this->clips.clear(); ARRAY_FOREACH(p, clips) { this->clips.push((WgRenderDataPaint*)(*p)); } } //*********************************************************************** // WgRenderDataShape //*********************************************************************** void WgRenderDataShape::updateBBox(BBox bb) { bbox.min = tvg::min(bbox.min, bb.min); bbox.max = tvg::max(bbox.max, bb.max); } void WgRenderDataShape::updateAABB(const Matrix& matrix) { auto p0 = Point{bbox.min.x, bbox.min.y} * matrix; auto p1 = Point{bbox.max.x, bbox.min.y} * matrix; auto p2 = Point{bbox.min.x, bbox.max.y} * matrix; auto p3 = Point{bbox.max.x, bbox.max.y} * matrix; aabb.min = {std::min(std::min(p0.x, p1.x), std::min(p2.x, p3.x)), std::min(std::min(p0.y, p1.y), std::min(p2.y, p3.y))}; aabb.max = {std::max(std::max(p0.x, p1.x), std::max(p2.x, p3.x)), std::max(std::max(p0.y, p1.y), std::max(p2.y, p3.y))}; } void WgRenderDataShape::updateVisibility(const RenderShape& rshape, uint8_t opacity) { renderSettingsShape.skip = (rshape.color.a * opacity == 0) && (!rshape.fill); renderSettingsStroke.skip = rshape.stroke ? (rshape.stroke->color.a * opacity == 0) && (!rshape.stroke->fill) : true; } void WgRenderDataShape::updateMeshes(const RenderShape &rshape, RenderUpdateFlag flag, const Matrix& matrix) { releaseMeshes(); //Optimize: bad idea to reset meshes always. it could re-use the meshes if there haven't been any path changes. convex = false; strokeFirst = rshape.strokeFirst(); renderSettingsShape.opacityMultiplier = 1.0f; renderSettingsStroke.opacityMultiplier = 1.0f; // optimize path RenderPath optPath; if (rshape.trimpath()) { RenderPath trimmedPath; if (rshape.stroke->trim.trim(rshape.path, trimmedPath)) { trimmedPath.optimizeWG(optPath, matrix); } else { optPath.clear(); } } else rshape.path.optimizeWG(optPath, matrix); auto updatePath = flag & (RenderUpdateFlag::Transform | RenderUpdateFlag::Path); // update fill shapes if (updatePath || (flag & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient))) { BBox bbox; // in a case of single line shape we must tesselate it as a single line stroke with minimal width if (optPath.pts.count == 2 && tvg::zero(rshape.strokeWidth())) { WgStroker stroker(&meshShape, MIN_WG_STROKE_WIDTH / scaling(matrix), StrokeCap::Butt, StrokeJoin::Bevel); stroker.run(rshape, optPath, matrix); bbox = stroker.getBBox(); renderSettingsShape.opacityMultiplier = MIN_WG_STROKE_ALPHA; } else { WgBWTessellator bwTess{&meshShape}; bwTess.tessellate(optPath, matrix); convex = bwTess.convex; bbox = bwTess.getBBox(); } if (meshShape.ibuffer.empty()) { meshShape.clear(); } else { meshShapeBBox.bbox(bbox.min, bbox.max); updateBBox(bbox); } } // update strokes shapes if (rshape.stroke && (updatePath || (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke)))) { auto strokeWidth = 0.0f; if (isinf(matrix.e11)) { strokeWidth = rshape.strokeWidth() * scaling(matrix); if (strokeWidth <= MIN_WG_STROKE_WIDTH) strokeWidth = MIN_WG_STROKE_WIDTH; strokeWidth = strokeWidth / matrix.e11; } else { strokeWidth = rshape.strokeWidth(); } //run stroking only if it's valid if (!tvg::zero(strokeWidth)) { WgStroker stroker(&meshStrokes, strokeWidth, rshape.strokeCap(), rshape.strokeJoin()); stroker.run(rshape, optPath, matrix); renderSettingsStroke.opacityMultiplier = 1.0f; if (meshStrokes.ibuffer.empty()) { meshStrokes.clear(); } else { auto bbox = stroker.getBBox(); meshStrokesBBox.bbox(bbox.min, bbox.max); updateBBox(bbox); } } } // update shapes bbox (with empty path handling) if (!meshShape.vbuffer.empty() || !meshStrokes.vbuffer.empty()) updateAABB(matrix); else bbox = aabb = {{0, 0}, {0, 0}}; meshBBox.bbox(bbox.min, bbox.max); } void WgRenderDataShape::releaseMeshes() { meshStrokes.clear(); meshShape.clear(); bbox.min = {FLT_MAX, FLT_MAX}; bbox.max = {0.0f, 0.0f}; aabb = {{0, 0}, {0, 0}}; clips.clear(); } void WgRenderDataShape::release(WgContext& context) { releaseMeshes(); renderSettingsStroke.release(context); renderSettingsShape.release(context); WgRenderDataPaint::release(context); }; //*********************************************************************** // WgRenderDataShapePool //*********************************************************************** WgRenderDataShape* WgRenderDataShapePool::allocate(WgContext& context) { WgRenderDataShape* renderData{}; if (mPool.count > 0) { renderData = mPool.last(); mPool.pop(); } else { renderData = new WgRenderDataShape(); mList.push(renderData); } return renderData; } void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* renderData) { renderData->releaseMeshes(); renderData->clips.clear(); mPool.push(renderData); } void WgRenderDataShapePool::release(WgContext& context) { ARRAY_FOREACH(p, mList) { (*p)->release(context); delete(*p); } mPool.clear(); mList.clear(); } //*********************************************************************** // WgRenderDataPicture //*********************************************************************** void WgRenderDataPicture::updateSurface(WgContext& context, const RenderSurface* surface) { // upoate mesh data meshData.imageBox(surface->w, surface->h); // update texture data imageData.update(context, surface); } void WgRenderDataPicture::release(WgContext& context) { renderSettings.release(context); imageData.release(context); WgRenderDataPaint::release(context); } //*********************************************************************** // WgRenderDataPicturePool //*********************************************************************** WgRenderDataPicture* WgRenderDataPicturePool::allocate(WgContext& context) { WgRenderDataPicture* renderData{}; if (mPool.count > 0) { renderData = mPool.last(); mPool.pop(); } else { renderData = new WgRenderDataPicture(); mList.push(renderData); } return renderData; } void WgRenderDataPicturePool::free(WgContext& context, WgRenderDataPicture* renderData) { renderData->clips.clear(); mPool.push(renderData); } void WgRenderDataPicturePool::release(WgContext& context) { ARRAY_FOREACH(p, mList) { (*p)->release(context); delete(*p); } mPool.clear(); mList.clear(); } //*********************************************************************** // WgRenderDataEffectParams //*********************************************************************** void WgRenderDataEffectParams::update(WgContext& context, WgShaderTypeEffectParams& effectParams) { if (context.allocateBufferUniform(bufferParams, &effectParams.params, sizeof(effectParams.params))) { context.layouts.releaseBindGroup(bindGroupParams); bindGroupParams = context.layouts.createBindGroupBuffer1Un(bufferParams); } } void WgRenderDataEffectParams::update(WgContext& context, RenderEffectGaussianBlur* gaussian, const Matrix& transform) { assert(gaussian); WgShaderTypeEffectParams effectParams; if (!effectParams.update(gaussian, transform)) return; update(context, effectParams); extend = effectParams.extend; } void WgRenderDataEffectParams::update(WgContext& context, RenderEffectDropShadow* dropShadow, const Matrix& transform) { assert(dropShadow); WgShaderTypeEffectParams effectParams; if (!effectParams.update(dropShadow, transform)) return; update(context, effectParams); extend = effectParams.extend; offset = effectParams.offset; } void WgRenderDataEffectParams::update(WgContext& context, RenderEffectFill* fill) { assert(fill); WgShaderTypeEffectParams effectParams; if (!effectParams.update(fill)) return; update(context, effectParams); } void WgRenderDataEffectParams::update(WgContext& context, RenderEffectTint* tint) { assert(tint); WgShaderTypeEffectParams effectParams; if (!effectParams.update(tint)) return; update(context, effectParams); } void WgRenderDataEffectParams::update(WgContext& context, RenderEffectTritone* tritone) { assert(tritone); WgShaderTypeEffectParams effectParams; if (!effectParams.update(tritone)) return; update(context, effectParams); } void WgRenderDataEffectParams::release(WgContext& context) { context.releaseBuffer(bufferParams); context.layouts.releaseBindGroup(bindGroupParams); } //*********************************************************************** // WgRenderDataColorsPool //*********************************************************************** WgRenderDataEffectParams* WgRenderDataEffectParamsPool::allocate(WgContext& context) { WgRenderDataEffectParams* renderData{}; if (mPool.count > 0) { renderData = mPool.last(); mPool.pop(); } else { renderData = new WgRenderDataEffectParams(); mList.push(renderData); } return renderData; } void WgRenderDataEffectParamsPool::free(WgContext& context, WgRenderDataEffectParams* renderData) { if (renderData) mPool.push(renderData); } void WgRenderDataEffectParamsPool::release(WgContext& context) { ARRAY_FOREACH(p, mList) { (*p)->release(context); delete(*p); } mPool.clear(); mList.clear(); } //*********************************************************************** // WgStageBufferGeometry //*********************************************************************** void WgStageBufferGeometry::append(WgMeshData* meshData) { assert(meshData); uint32_t vsize = meshData->vbuffer.count * sizeof(meshData->vbuffer[0]); uint32_t tsize = meshData->tbuffer.count * sizeof(meshData->tbuffer[0]); uint32_t isize = meshData->ibuffer.count * sizeof(meshData->ibuffer[0]); // append vertex data if (vbuffer.reserved < vbuffer.count + vsize) vbuffer.grow(std::max(vsize, vbuffer.reserved)); if (meshData->vbuffer.count > 0) { meshData->voffset = vbuffer.count; memcpy(vbuffer.data + vbuffer.count, meshData->vbuffer.data, vsize); vbuffer.count += vsize; } // append tex coords data if (vbuffer.reserved < vbuffer.count + tsize) vbuffer.grow(std::max(tsize, vbuffer.reserved)); if (meshData->tbuffer.count > 0) { meshData->toffset = vbuffer.count; memcpy(vbuffer.data + vbuffer.count, meshData->tbuffer.data, tsize); vbuffer.count += tsize; } // append index data if (ibuffer.reserved < ibuffer.count + isize) ibuffer.grow(std::max(isize, ibuffer.reserved)); if (meshData->ibuffer.count > 0) { meshData->ioffset = ibuffer.count; memcpy(ibuffer.data + ibuffer.count, meshData->ibuffer.data, isize); ibuffer.count += isize; } } void WgStageBufferGeometry::append(WgRenderDataShape* renderDataShape) { append(&renderDataShape->meshShape); append(&renderDataShape->meshShapeBBox); append(&renderDataShape->meshStrokes); append(&renderDataShape->meshStrokesBBox); append(&renderDataShape->meshBBox); } void WgStageBufferGeometry::append(WgRenderDataPicture* renderDataPicture) { append(&renderDataPicture->meshData); } void WgStageBufferGeometry::release(WgContext& context) { context.releaseBuffer(vbuffer_gpu); context.releaseBuffer(ibuffer_gpu); } void WgStageBufferGeometry::clear() { vbuffer.clear(); ibuffer.clear(); } void WgStageBufferGeometry::flush(WgContext& context) { context.allocateBufferVertex(vbuffer_gpu, (float *)vbuffer.data, vbuffer.count); context.allocateBufferIndex(ibuffer_gpu, (uint32_t *)ibuffer.data, ibuffer.count); } //*********************************************************************** // WgIntersector //*********************************************************************** bool WgIntersector::isPointInTriangle(const Point& p, const Point& a, const Point& b, const Point& c) { auto d1 = tvg::cross(p - a, p - b); auto d2 = tvg::cross(p - b, p - c); auto d3 = tvg::cross(p - c, p - a); auto has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0); auto has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0); return !(has_neg && has_pos); } // triangle list bool WgIntersector::isPointInTris(const Point& p, const WgMeshData& mesh, const Matrix& tr) { for (uint32_t i = 0; i < mesh.ibuffer.count; i += 3) { auto p0 = mesh.vbuffer[mesh.ibuffer[i+0]] * tr; auto p1 = mesh.vbuffer[mesh.ibuffer[i+1]] * tr; auto p2 = mesh.vbuffer[mesh.ibuffer[i+2]] * tr; if (isPointInTriangle(p, p0, p1, p2)) return true; } return false; } // even-odd triangle list bool WgIntersector::isPointInMesh(const Point& p, const WgMeshData& mesh, const Matrix& tr) { uint32_t crossings = 0; for (uint32_t i = 0; i < mesh.ibuffer.count; i += 3) { Point triangle[3] = { mesh.vbuffer[mesh.ibuffer[i+0]] * tr, mesh.vbuffer[mesh.ibuffer[i+1]] * tr, mesh.vbuffer[mesh.ibuffer[i+2]] * tr }; for (uint32_t j = 0; j < 3; j++) { auto p1 = triangle[j]; auto p2 = triangle[(j + 1) % 3]; if (p1.y == p2.y) continue; if (p1.y > p2.y) std::swap(p1, p2); if ((p.y > p1.y) && (p.y <= p2.y)) { auto intersectionX = (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x; if (intersectionX > p.x) crossings++; } } } return (crossings % 2) == 1; } bool WgIntersector::intersectClips(const Point& pt, const Array& clips) { for (uint32_t i = 0; i < clips.count; i++) { auto clip = (WgRenderDataShape*)clips[i]; if (!isPointInMesh(pt, clip->meshShape, clip->transform)) return false; } return true; } bool WgIntersector::intersectShape(const RenderRegion region, const WgRenderDataShape* shape) { if (!shape || ((shape->meshShape.ibuffer.count == 0) && (shape->meshStrokes.ibuffer.count == 0))) return false; auto sizeX = region.sw(); auto sizeY = region.sh(); for (int32_t y = 0; y <= sizeY; y++) { for (int32_t x = 0; x <= sizeX; x++) { Point pt{(float)x + region.min.x, (float)y + region.min.y}; if (y % 2 == 1) pt.y = (float) sizeY - y - sizeY % 2 + region.min.y; if (intersectClips(pt, shape->clips)) { if (!shape->renderSettingsShape.skip && isPointInMesh(pt, shape->meshShape, shape->transform)) return true; if (!shape->renderSettingsStroke.skip && isPointInTris(pt, shape->meshStrokes, shape->transform)) return true; } } } return false; } bool WgIntersector::intersectImage(const RenderRegion region, const WgRenderDataPicture* image) { if (!image) return false; auto sizeX = region.sw(); auto sizeY = region.sh(); for (int32_t y = 0; y <= sizeY; y++) { for (int32_t x = 0; x <= sizeX; x++) { Point pt{(float)x + region.min.x, (float)y + region.min.y}; if (y % 2 == 1) pt.y = (float) sizeY - y - sizeY % 2 + region.min.y; if (intersectClips(pt, image->clips)) { if (isPointInTris(pt, image->meshData, image->transform)) return true; } } } return false; } external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-proxy-object.h000664 001750 001750 00000006332 15164251010 046114 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_PROXY_OBJECT_H #define ECMA_PROXY_OBJECT_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaproxyobject ECMA Proxy object related routines * @{ */ #if JERRY_BUILTIN_PROXY ecma_object_t *ecma_proxy_create (ecma_value_t target, ecma_value_t handler, uint32_t options); ecma_object_t *ecma_proxy_create_revocable (ecma_value_t target, ecma_value_t handler); ecma_value_t ecma_proxy_revoke_cb (ecma_object_t *function_obj_p, const ecma_value_t args_p[], const uint32_t args_count); ecma_value_t ecma_proxy_object_find (ecma_object_t *obj_p, ecma_string_t *prop_name_p); /* Internal operations */ ecma_value_t ecma_proxy_object_get_prototype_of (ecma_object_t *obj_p); ecma_value_t ecma_proxy_object_set_prototype_of (ecma_object_t *obj_p, ecma_value_t proto); ecma_value_t ecma_proxy_object_is_extensible (ecma_object_t *obj_p); ecma_value_t ecma_proxy_object_prevent_extensions (ecma_object_t *obj_p); ecma_value_t ecma_proxy_object_get_own_property_descriptor (ecma_object_t *obj_p, ecma_string_t *prop_name_p, ecma_property_descriptor_t *prop_desc_p); ecma_value_t ecma_proxy_object_define_own_property (ecma_object_t *obj_p, ecma_string_t *prop_name_p, const ecma_property_descriptor_t *prop_desc_p); ecma_value_t ecma_proxy_object_has (ecma_object_t *obj_p, ecma_string_t *prop_name_p); ecma_value_t ecma_proxy_object_get (ecma_object_t *obj_p, ecma_string_t *prop_name_p, ecma_value_t receiver); ecma_value_t ecma_proxy_object_set (ecma_object_t *obj_p, ecma_string_t *prop_name_p, ecma_value_t name, ecma_value_t receiver, bool is_strict); ecma_value_t ecma_proxy_object_delete_property (ecma_object_t *obj_p, ecma_string_t *prop_name_p, bool is_strict); ecma_collection_t *ecma_proxy_object_own_property_keys (ecma_object_t *obj_p); ecma_value_t ecma_proxy_object_call (ecma_object_t *obj_p, ecma_value_t this_argument, const ecma_value_t *args_p, uint32_t argc); ecma_value_t ecma_proxy_object_construct (ecma_object_t *obj_p, ecma_object_t *new_target_p, const ecma_value_t *args_p, uint32_t argc); #endif /* JERRY_BUILTIN_PROXY */ /** * @} * @} */ #endif /* !ECMA_PROXY_OBJECT_H */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp000664 001750 001750 00000053574 15164251010 047652 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-eval.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "jcontext.h" #include "jrt-bit-fields.h" #include "jrt-libc-includes.h" #include "jrt.h" #include "lit-char-helpers.h" #include "lit-magic-strings.h" #include "lit-strings.h" #include "vm.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_GLOBAL_ROUTINE_START = 0, /* Note: these 5 routine ids must be in this order */ ECMA_GLOBAL_IS_NAN, ECMA_GLOBAL_IS_FINITE, ECMA_GLOBAL_EVAL, ECMA_GLOBAL_PARSE_INT, ECMA_GLOBAL_PARSE_FLOAT, ECMA_GLOBAL_DECODE_URI, ECMA_GLOBAL_DECODE_URI_COMPONENT, ECMA_GLOBAL_ENCODE_URI, ECMA_GLOBAL_ENCODE_URI_COMPONENT, ECMA_GLOBAL_ESCAPE, ECMA_GLOBAL_UNESCAPE, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-global.inc.h" #define BUILTIN_UNDERSCORED_ID global #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup global ECMA Global object built-in * @{ */ /** * The Global object's 'eval' routine * * See also: * ECMA-262 v5, 15.1.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_global_object_eval (ecma_value_t x) /**< routine's first argument */ { if (JERRY_UNLIKELY (!ecma_is_value_string (x))) { /* step 1 */ return ecma_copy_value (x); } uint32_t parse_opts = vm_is_direct_eval_form_call () ? ECMA_PARSE_DIRECT_EVAL : ECMA_PARSE_NO_OPTS; /* See also: ECMA-262 v5, 10.1.1 */ if (parse_opts && vm_is_strict_mode ()) { JERRY_ASSERT (parse_opts & ECMA_PARSE_DIRECT_EVAL); parse_opts |= ECMA_PARSE_STRICT_MODE; } if (vm_is_direct_eval_form_call ()) { parse_opts |= ECMA_GET_LOCAL_PARSE_OPTS (); } /* steps 2 to 8 */ return ecma_op_eval (x, parse_opts); } /* ecma_builtin_global_object_eval */ /** * The Global object's 'isNaN' routine * * See also: * ECMA-262 v5, 15.1.2.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_global_object_is_nan (ecma_number_t arg_num) /**< routine's first argument */ { return ecma_make_boolean_value (ecma_number_is_nan (arg_num)); } /* ecma_builtin_global_object_is_nan */ /** * The Global object's 'isFinite' routine * * See also: * ECMA-262 v5, 15.1.2.5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_global_object_is_finite (ecma_number_t arg_num) /**< routine's first argument */ { bool is_finite = !(ecma_number_is_nan (arg_num) || ecma_number_is_infinity (arg_num)); return ecma_make_boolean_value (is_finite); } /* ecma_builtin_global_object_is_finite */ /** * Helper function to check whether a character is in a character bitset. * * @return true if the character is in the character bitset. */ static bool ecma_builtin_global_object_character_is_in (uint32_t character, /**< character */ const uint8_t *bitset) /**< character set */ { JERRY_ASSERT (character < 128); return (bitset[character >> 3] & (1u << (character & 0x7))) != 0; } /* ecma_builtin_global_object_character_is_in */ /** * Unescaped URI characters bitset: * One bit for each character between 0 - 127. * Bit is set if the character is in the unescaped URI set. */ static const uint8_t unescaped_uri_set[16] = { 0x0, 0x0, 0x0, 0x0, 0xda, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x47 }; /** * Unescaped URI component characters bitset: * One bit for each character between 0 - 127. * Bit is set if the character is in the unescaped component URI set. */ static const uint8_t unescaped_uri_component_set[16] = { 0x0, 0x0, 0x0, 0x0, 0x82, 0x67, 0xff, 0x3, 0xfe, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x47 }; /** * Format is a percent sign followed by two hex digits. */ #define URI_ENCODED_BYTE_SIZE (3) /** * The Global object's 'decodeURI' and 'decodeURIComponent' routines * * See also: * ECMA-262 v5, 15.1.3.1 * ECMA-262 v5, 15.1.3.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_global_object_decode_uri_helper (lit_utf8_byte_t *input_start_p, /**< routine's first argument's * string buffer */ lit_utf8_size_t input_size, /**< routine's first argument's * string buffer's size */ const uint8_t *reserved_uri_bitset) /**< reserved characters bitset */ { lit_utf8_byte_t *input_char_p = input_start_p; lit_utf8_byte_t *input_end_p = input_start_p + input_size; ecma_stringbuilder_t builder = ecma_stringbuilder_create (); while (input_char_p < input_end_p) { if (*input_char_p != '%') { ecma_stringbuilder_append_byte (&builder, *input_char_p++); continue; } uint32_t hex_value = lit_char_hex_lookup (input_char_p + 1, input_end_p, 2); if (hex_value == UINT32_MAX) { ecma_stringbuilder_destroy (&builder); return ecma_raise_uri_error (ECMA_ERR_INVALID_HEXADECIMAL_VALUE); } ecma_char_t decoded_byte = (ecma_char_t) hex_value; input_char_p += URI_ENCODED_BYTE_SIZE; if (decoded_byte <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { if (ecma_builtin_global_object_character_is_in (decoded_byte, reserved_uri_bitset) && !ecma_builtin_global_object_character_is_in (decoded_byte, unescaped_uri_component_set)) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_PERCENT); input_char_p -= 2; } else { ecma_stringbuilder_append_byte (&builder, (lit_utf8_byte_t) decoded_byte); } } else { uint32_t bytes_count; if ((decoded_byte & LIT_UTF8_2_BYTE_MASK) == LIT_UTF8_2_BYTE_MARKER) { bytes_count = 2; } else if ((decoded_byte & LIT_UTF8_3_BYTE_MASK) == LIT_UTF8_3_BYTE_MARKER) { bytes_count = 3; } else if ((decoded_byte & LIT_UTF8_4_BYTE_MASK) == LIT_UTF8_4_BYTE_MARKER) { bytes_count = 4; } else { ecma_stringbuilder_destroy (&builder); return ecma_raise_uri_error (ECMA_ERR_INVALID_UTF8_CHARACTER); } lit_utf8_byte_t octets[LIT_UTF8_MAX_BYTES_IN_CODE_POINT]; octets[0] = (lit_utf8_byte_t) decoded_byte; bool is_valid = true; for (uint32_t i = 1; i < bytes_count; i++) { if (input_char_p >= input_end_p || *input_char_p != '%') { is_valid = false; break; } else { hex_value = lit_char_hex_lookup (input_char_p + 1, input_end_p, 2); if (hex_value == UINT32_MAX || (hex_value & LIT_UTF8_EXTRA_BYTE_MASK) != LIT_UTF8_EXTRA_BYTE_MARKER) { is_valid = false; break; } input_char_p += URI_ENCODED_BYTE_SIZE; octets[i] = (lit_utf8_byte_t) hex_value; } } if (!is_valid || !lit_is_valid_utf8_string (octets, bytes_count, true)) { ecma_stringbuilder_destroy (&builder); return ecma_raise_uri_error (ECMA_ERR_INVALID_UTF8_STRING); } lit_code_point_t cp; lit_read_code_point_from_utf8 (octets, bytes_count, &cp); if (lit_is_code_point_utf16_high_surrogate (cp) || lit_is_code_point_utf16_low_surrogate (cp)) { ecma_stringbuilder_destroy (&builder); return ecma_raise_uri_error (ECMA_ERR_INVALID_UTF8_CODEPOINT); } lit_utf8_byte_t result_chars[LIT_CESU8_MAX_BYTES_IN_CODE_POINT]; lit_utf8_size_t cp_size = lit_code_point_to_cesu8 (cp, result_chars); ecma_stringbuilder_append_raw (&builder, result_chars, cp_size); } } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_global_object_decode_uri_helper */ /** * Helper function to encode byte as hexadecimal values. */ static void ecma_builtin_global_object_byte_to_hex (lit_utf8_byte_t *dest_p, /**< destination pointer */ uint32_t byte) /**< value */ { JERRY_ASSERT (byte < 256); dest_p[0] = LIT_CHAR_PERCENT; ecma_char_t hex_digit = (ecma_char_t) (byte >> 4); dest_p[1] = (lit_utf8_byte_t) ((hex_digit > 9) ? (hex_digit + ('A' - 10)) : (hex_digit + '0')); hex_digit = (lit_utf8_byte_t) (byte & 0xf); dest_p[2] = (lit_utf8_byte_t) ((hex_digit > 9) ? (hex_digit + ('A' - 10)) : (hex_digit + '0')); } /* ecma_builtin_global_object_byte_to_hex */ /** * The Global object's 'encodeURI' and 'encodeURIComponent' routines * * See also: * ECMA-262 v5, 15.1.3.3 * ECMA-262 v5, 15.1.3.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_global_object_encode_uri_helper (lit_utf8_byte_t *input_start_p, /**< routine's first argument's * string buffer */ lit_utf8_size_t input_size, /**< routine's first argument's * string buffer's size */ const uint8_t *unescaped_uri_bitset_p) /**< unescaped bitset */ { lit_utf8_byte_t *input_char_p = input_start_p; const lit_utf8_byte_t *input_end_p = input_start_p + input_size; ecma_char_t ch; ecma_stringbuilder_t builder = ecma_stringbuilder_create (); lit_utf8_byte_t octets[LIT_UTF8_MAX_BYTES_IN_CODE_POINT]; memset (octets, LIT_BYTE_NULL, LIT_UTF8_MAX_BYTES_IN_CODE_POINT); while (input_char_p < input_end_p) { input_char_p += lit_read_code_unit_from_cesu8 (input_char_p, &ch); if (lit_is_code_point_utf16_low_surrogate (ch)) { ecma_stringbuilder_destroy (&builder); return ecma_raise_uri_error (ECMA_ERR_UNICODE_SURROGATE_PAIR_MISSING); } lit_code_point_t cp = ch; if (lit_is_code_point_utf16_high_surrogate (ch)) { if (input_char_p == input_end_p) { ecma_stringbuilder_destroy (&builder); return ecma_raise_uri_error (ECMA_ERR_UNICODE_SURROGATE_PAIR_MISSING); } ecma_char_t next_ch; lit_utf8_size_t read_size = lit_read_code_unit_from_cesu8 (input_char_p, &next_ch); if (lit_is_code_point_utf16_low_surrogate (next_ch)) { cp = lit_convert_surrogate_pair_to_code_point (ch, next_ch); input_char_p += read_size; } else { ecma_stringbuilder_destroy (&builder); return ecma_raise_uri_error (ECMA_ERR_UNICODE_SURROGATE_PAIR_MISSING); } } lit_utf8_size_t utf_size = lit_code_point_to_utf8 (cp, octets); lit_utf8_byte_t result_chars[URI_ENCODED_BYTE_SIZE]; if (utf_size == 1) { if (ecma_builtin_global_object_character_is_in (octets[0], unescaped_uri_bitset_p)) { ecma_stringbuilder_append_byte (&builder, octets[0]); } else { ecma_builtin_global_object_byte_to_hex (result_chars, octets[0]); ecma_stringbuilder_append_raw (&builder, result_chars, URI_ENCODED_BYTE_SIZE); } } else { for (uint32_t i = 0; i < utf_size; i++) { JERRY_ASSERT (utf_size <= LIT_UTF8_MAX_BYTES_IN_CODE_POINT); ecma_builtin_global_object_byte_to_hex (result_chars, octets[i]); ecma_stringbuilder_append_raw (&builder, result_chars, URI_ENCODED_BYTE_SIZE); } } } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_global_object_encode_uri_helper */ #if JERRY_BUILTIN_ANNEXB /** * Maximum value of a byte. */ #define ECMA_ESCAPE_MAXIMUM_BYTE_VALUE (255) /** * Format is a percent sign followed by lowercase u and four hex digits. */ #define ECMA_ESCAPE_ENCODED_UNICODE_CHARACTER_SIZE (6) /** * Escape characters bitset: * One bit for each character between 0 - 127. * Bit is set if the character does not need to be converted to %xx form. * These characters are: a-z A-Z 0-9 @ * _ + - . / */ static const uint8_t ecma_escape_set[16] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0xec, 0xff, 0x3, 0xff, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x7 }; /** * The Global object's 'escape' routine * * See also: * ECMA-262 v5, B.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_global_object_escape (lit_utf8_byte_t *input_start_p, /**< routine's first argument's * string buffer */ lit_utf8_size_t input_size) /**< routine's first argument's * string buffer's size */ { const lit_utf8_byte_t *input_curr_p = input_start_p; const lit_utf8_byte_t *input_end_p = input_start_p + input_size; ecma_stringbuilder_t builder = ecma_stringbuilder_create (); lit_utf8_byte_t result_chars[URI_ENCODED_BYTE_SIZE]; while (input_curr_p < input_end_p) { ecma_char_t chr = lit_cesu8_read_next (&input_curr_p); if (chr <= LIT_UTF8_1_BYTE_CODE_POINT_MAX) { if (ecma_builtin_global_object_character_is_in ((uint32_t) chr, ecma_escape_set)) { ecma_stringbuilder_append_char (&builder, chr); } else { ecma_builtin_global_object_byte_to_hex (result_chars, chr); ecma_stringbuilder_append_raw (&builder, result_chars, URI_ENCODED_BYTE_SIZE); } } else if (chr > ECMA_ESCAPE_MAXIMUM_BYTE_VALUE) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_PERCENT); ecma_stringbuilder_append_char (&builder, LIT_CHAR_LOWERCASE_U); ecma_builtin_global_object_byte_to_hex (result_chars, (chr >> JERRY_BITSINBYTE)); ecma_stringbuilder_append_raw (&builder, result_chars + 1, 2); ecma_builtin_global_object_byte_to_hex (result_chars, (chr & 0xff)); ecma_stringbuilder_append_raw (&builder, result_chars + 1, 2); } else { ecma_builtin_global_object_byte_to_hex (result_chars, chr); ecma_stringbuilder_append_raw (&builder, result_chars, URI_ENCODED_BYTE_SIZE); } } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_global_object_escape */ /** * Utility method to resolve character sequences for the 'unescape' method. * * Expected formats: %uxxxx or %yy * * @return number of characters processed during the escape resolve */ static uint8_t ecma_builtin_global_object_unescape_resolve_escape (const lit_utf8_byte_t *buffer_p, /**< character buffer */ bool unicode_sequence, /**< true if unescaping unicode sequence */ ecma_char_t *out_result_p) /**< [out] resolved character */ { JERRY_ASSERT (buffer_p != NULL); JERRY_ASSERT (out_result_p != NULL); ecma_char_t unescaped_chr = 0; uint8_t sequence_length = unicode_sequence ? 5 : 2; uint8_t start = unicode_sequence ? 1 : 0; for (uint8_t i = start; i < sequence_length; i++) { const lit_utf8_byte_t current_char = buffer_p[i]; if (!lit_char_is_hex_digit (current_char)) { /* This was not an escape sequence, skip processing */ return 0; } unescaped_chr = (ecma_char_t) ((unescaped_chr << 4) + (ecma_char_t) lit_char_hex_to_int (current_char)); } *out_result_p = unescaped_chr; return sequence_length; } /* ecma_builtin_global_object_unescape_resolve_escape */ /** * The Global object's 'unescape' routine * * See also: * ECMA-262 v5, B.2.2 * ECMA-262 v11, B.2.1.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_global_object_unescape (lit_utf8_byte_t *input_start_p, /**< routine's first argument's * string buffer */ lit_utf8_size_t input_size) /**< routine's first argument's * string buffer's size */ { if (input_size == 0) { return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } const lit_utf8_byte_t *input_curr_p = input_start_p; const lit_utf8_byte_t *input_end_p = input_start_p + input_size; ecma_stringbuilder_t builder = ecma_stringbuilder_create (); while (input_curr_p < input_end_p) { ecma_char_t chr = lit_cesu8_read_next (&input_curr_p); // potential pattern if (chr == LIT_CHAR_PERCENT) { const lit_utf8_size_t chars_leftover = (lit_utf8_size_t) (input_end_p - input_curr_p); // potential unicode sequence if (chars_leftover >= 5 && input_curr_p[0] == LIT_CHAR_LOWERCASE_U) { input_curr_p += ecma_builtin_global_object_unescape_resolve_escape (input_curr_p, true, &chr); } // potential two hexa sequence else if (chars_leftover >= 2) { input_curr_p += ecma_builtin_global_object_unescape_resolve_escape (input_curr_p, false, &chr); } } ecma_stringbuilder_append_char (&builder, chr); } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_global_object_unescape */ #endif /* JERRY_BUILTIN_ANNEXB */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_global_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (this_arg, arguments_number); ecma_value_t routine_arg_1 = arguments_list_p[0]; if (builtin_routine_id == ECMA_GLOBAL_EVAL) { return ecma_builtin_global_object_eval (routine_arg_1); } if (builtin_routine_id <= ECMA_GLOBAL_IS_FINITE) { ecma_number_t arg_num; routine_arg_1 = ecma_op_to_number (routine_arg_1, &arg_num); if (!ecma_is_value_empty (routine_arg_1)) { return routine_arg_1; } if (builtin_routine_id == ECMA_GLOBAL_IS_NAN) { return ecma_builtin_global_object_is_nan (arg_num); } JERRY_ASSERT (builtin_routine_id == ECMA_GLOBAL_IS_FINITE); return ecma_builtin_global_object_is_finite (arg_num); } ecma_string_t *str_p = ecma_op_to_string (routine_arg_1); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t ret_value; if (builtin_routine_id <= ECMA_GLOBAL_PARSE_FLOAT) { ECMA_STRING_TO_UTF8_STRING (str_p, string_buff, string_buff_size); if (builtin_routine_id == ECMA_GLOBAL_PARSE_INT) { ret_value = ecma_number_parse_int (string_buff, string_buff_size, arguments_list_p[1]); } else { JERRY_ASSERT (builtin_routine_id == ECMA_GLOBAL_PARSE_FLOAT); ret_value = ecma_number_parse_float (string_buff, string_buff_size); } ECMA_FINALIZE_UTF8_STRING (string_buff, string_buff_size); ecma_deref_ecma_string (str_p); return ret_value; } lit_utf8_size_t input_size = ecma_string_get_size (str_p); JMEM_DEFINE_LOCAL_ARRAY (input_start_p, input_size + 1, lit_utf8_byte_t); ecma_string_to_cesu8_bytes (str_p, input_start_p, input_size); input_start_p[input_size] = LIT_BYTE_NULL; switch (builtin_routine_id) { #if JERRY_BUILTIN_ANNEXB case ECMA_GLOBAL_ESCAPE: { ret_value = ecma_builtin_global_object_escape (input_start_p, input_size); break; } case ECMA_GLOBAL_UNESCAPE: { ret_value = ecma_builtin_global_object_unescape (input_start_p, input_size); break; } #endif /* JERRY_BUILTIN_ANNEXB */ case ECMA_GLOBAL_DECODE_URI: case ECMA_GLOBAL_DECODE_URI_COMPONENT: { const uint8_t *uri_set = (builtin_routine_id == ECMA_GLOBAL_DECODE_URI ? unescaped_uri_set : unescaped_uri_component_set); ret_value = ecma_builtin_global_object_decode_uri_helper (input_start_p, input_size, uri_set); break; } default: { JERRY_ASSERT (builtin_routine_id == ECMA_GLOBAL_ENCODE_URI || builtin_routine_id == ECMA_GLOBAL_ENCODE_URI_COMPONENT); const uint8_t *uri_set = (builtin_routine_id == ECMA_GLOBAL_ENCODE_URI ? unescaped_uri_set : unescaped_uri_component_set); ret_value = ecma_builtin_global_object_encode_uri_helper (input_start_p, input_size, uri_set); break; } } JMEM_FINALIZE_LOCAL_ARRAY (input_start_p); ecma_deref_ecma_string (str_p); return ret_value; } /* ecma_builtin_global_dispatch_routine */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/build_ios.yml000664 001750 001750 00000005420 15164251010 035307 0ustar00ddennedyddennedy000000 000000 name: iOS on: pull_request: branches: - main push: branches: - main permissions: contents: read jobs: build_simulator_x86_64: runs-on: macos-latest steps: - uses: actions/checkout@v4 with: submodules: true - name: Unbreak Python in GitHub Actions run: | find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete sudo rm -rf /Library/Frameworks/Python.framework/ brew install --force python3 && brew unlink python3 && brew link --overwrite python3 - name: Install Packages run: | export HOMEBREW_NO_INSTALL_FROM_API=1 brew update brew install meson - name: Build run: | meson setup build -Dlog=true -Dloaders=all -Dsavers=all -Dbindings=capi -Dstatic=true -Dextra="lottie_exp" --cross-file ./cross/ios_simulator_x86_64.txt ninja -C build install - uses: actions/upload-artifact@v4 with: name: result_simulator_x86_64 path: build/src/libthorvg* build_simulator_arm64: runs-on: macos-latest steps: - uses: actions/checkout@v4 with: submodules: true - name: Unbreak Python in GitHub Actions run: | find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete sudo rm -rf /Library/Frameworks/Python.framework/ brew install --force python3 && brew unlink python3 && brew link --overwrite python3 - name: Install Packages run: | export HOMEBREW_NO_INSTALL_FROM_API=1 brew update brew install meson - name: Build run: | meson setup build -Dlog=true -Dloaders=all -Dsavers=all -Dbindings=capi -Dstatic=true -Dextra="lottie_exp" --cross-file ./cross/ios_simulator_arm64.txt ninja -C build install - uses: actions/upload-artifact@v4 with: name: result_simulator_arm64 path: build/src/libthorvg* build_arm64: runs-on: macos-latest steps: - uses: actions/checkout@v4 with: submodules: true - name: Unbreak Python in GitHub Actions run: | find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete sudo rm -rf /Library/Frameworks/Python.framework/ brew install --force python3 && brew unlink python3 && brew link --overwrite python3 - name: Install Packages run: | export HOMEBREW_NO_INSTALL_FROM_API=1 brew update brew install meson - name: Build run: | meson setup build -Dlog=true -Dloaders=all -Dsavers=all -Dbindings=capi -Dstatic=true -Dextra="lottie_exp" --cross-file ./cross/ios_arm64.txt ninja -C build install - uses: actions/upload-artifact@v4 with: name: result_arm64 path: build/src/libthorvg*src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_png/meson.build000664 001750 001750 00000000460 15164251010 036206 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0source_file = [ 'tvgPngLoader.h', 'tvgPngLoader.cpp', ] png_dep = dependency('libpng', required: false) if png_dep.found() subloader_dep += [declare_dependency( include_directories : include_directories('.'), dependencies : png_dep, sources : source_file )] endif mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgFrameModule.h000664 001750 001750 00000004060 15164251010 034704 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_FRAME_MODULE_H_ #define _TVG_FRAME_MODULE_H_ #include "tvgLoadModule.h" namespace tvg { class FrameModule: public ImageLoader { public: float segmentBegin = 0.0f; float segmentEnd; //Initialize the value with the total frame number FrameModule(FileType type) : ImageLoader(type) {} virtual ~FrameModule() {} virtual bool frame(float no) = 0; //set the current frame number virtual float totalFrame() = 0; //return the total frame count virtual float curFrame() = 0; //return the current frame number virtual float duration() = 0; //return the animation duration in seconds virtual Result segment(float begin, float end) = 0; void segment(float* begin, float* end) { if (begin) *begin = segmentBegin; if (end) *end = segmentEnd; } virtual bool animatable() override { return true; } }; } #endif //_TVG_FRAME_MODULE_H_ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/000775 001750 001750 00000000000 15164251010 037741 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/srcmlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/000775 001750 001750 00000000000 15164251010 031274 5ustar00ddennedyddennedy000000 000000 src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieExpressions.cpp000664 001750 001750 00000141143 15164251010 037612 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgCompressor.h" #include "tvgLottieModel.h" #include "tvgLottieExpressions.h" #ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ struct ExpContent { LottieExpression* exp; union { LottieObject* obj; LottieEffect* effect; LottieProperty* property; void *data; }; float frameNo; size_t refCnt; }; static jerry_value_t _content(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt); //reserved expressions specifiers static const char* EXP_NAME = "name"; static const char* EXP_CONTENT = "content"; static const char* EXP_WIDTH = "width"; static const char* EXP_HEIGHT = "height"; static const char* EXP_CYCLE = "cycle"; static const char* EXP_PINGPONG = "pingpong"; static const char* EXP_OFFSET = "offset"; static const char* EXP_CONTINUE = "continue"; static const char* EXP_TIME = "time"; static const char* EXP_VALUE = "value"; static const char* EXP_INDEX = "index"; static const char* EXP_EFFECT= "effect"; static LottieExpressions* exps = nullptr; //singleton instance engine static ExpContent* _expcontent(LottieExpression* exp, float frameNo, void* data, size_t refCnt = 1) { auto ret = tvg::malloc(sizeof(ExpContent)); ret->exp = exp; ret->frameNo = frameNo; ret->data = data; ret->refCnt = refCnt; return ret; } static inline ExpContent* _expcontent(ExpContent* data) { ++data->refCnt; return data; } static float _rand() { return (float)(rand() % 10000001) * 0.0000001f; } static jerry_value_t _point2d(const Point& pt) { auto obj = jerry_object(); auto v1 = jerry_number(pt.x); auto v2 = jerry_number(pt.y); jerry_object_set_index(obj, 0, v1); jerry_object_set_index(obj, 1, v2); jerry_value_free(v1); jerry_value_free(v2); return obj; } static jerry_value_t _color(RGB32 rgb) { auto value = jerry_object(); auto r = jerry_number((float)rgb.r); auto g = jerry_number((float)rgb.g); auto b = jerry_number((float)rgb.b); jerry_object_set_index(value, 0, r); jerry_object_set_index(value, 1, g); jerry_object_set_index(value, 2, b); jerry_value_free(r); jerry_value_free(g); jerry_value_free(b); return value; } static Point _point2d(jerry_value_t obj) { auto v1 = jerry_object_get_index(obj, 0); auto v2 = jerry_object_get_index(obj, 1); Point pt = {jerry_value_as_number(v1), jerry_value_as_number(v2)}; jerry_value_free(v1); jerry_value_free(v2); return pt; } static RGB32 _color(jerry_value_t obj) { RGB32 out; auto r = jerry_object_get_index(obj, 0); auto g = jerry_object_get_index(obj, 1); auto b = jerry_object_get_index(obj, 2); out = {jerry_value_as_int32(r), jerry_value_as_int32(g), jerry_value_as_int32(b)}; jerry_value_free(r); jerry_value_free(g); jerry_value_free(b); return out; } static void contentFree(void *native_p, struct jerry_object_native_info_t *info_p) { if (--static_cast(native_p)->refCnt == 0) { tvg::free(native_p); } } static jerry_object_native_info_t freeCb {contentFree, 0, 0}; static uint32_t engineRefCnt = 0; //Expressions Engine reference count static char* _name(jerry_value_t args) { auto arg0 = jerry_value_to_string(args); auto len = jerry_string_length(arg0); auto name = tvg::malloc(len * sizeof(jerry_char_t) + 1); jerry_string_to_buffer(arg0, JERRY_ENCODING_UTF8, name, len); name[len] = '\0'; jerry_value_free(arg0); return (char*) name; } static unsigned long _idByName(jerry_value_t args) { auto name = _name(args); auto id = djb2Encode(name); tvg::free(name); return id; } static jerry_value_t _toComp(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto layer = static_cast(jerry_object_get_native_ptr(info->function, nullptr)); return _point2d(_point2d(args[0]) * layer->cache.matrix); } static jerry_value_t _point(const Point& v) { auto obj = _point2d(v); auto value = _point2d(v); jerry_object_set_sz(obj, EXP_VALUE, value); jerry_value_free(value); return obj; } static jerry_value_t _buildValue(float frameNo, LottieProperty* property) { switch (property->type) { case LottieProperty::Type::Integer: return jerry_number((*static_cast(property))(frameNo)); case LottieProperty::Type::Float: return jerry_number((*static_cast(property))(frameNo)); case LottieProperty::Type::Scalar: return _point((*static_cast(property))(frameNo)); case LottieProperty::Type::Vector: return _point((*static_cast(property))(frameNo)); case LottieProperty::Type::PathSet: { auto obj = jerry_object(); jerry_object_set_native_ptr(obj, nullptr, property); return obj; } case LottieProperty::Type::Color: return _color((*static_cast(property))(frameNo)); case LottieProperty::Type::Opacity: return jerry_number((*static_cast(property))(frameNo)); default: TVGERR("LOTTIE", "Non supported type for value? = %d", (int) property->type); } return jerry_undefined(); } static void _buildTransform(jerry_value_t context, float frameNo, LottieTransform* transform) { if (!transform) return; auto obj = jerry_object(); jerry_object_set_sz(context, "transform", obj); auto anchorPoint = _buildValue(frameNo, &transform->anchor); jerry_object_set_sz(obj, "anchorPoint", anchorPoint); jerry_value_free(anchorPoint); auto position = _buildValue(frameNo, &transform->position); jerry_object_set_sz(obj, "position", position); jerry_value_free(position); auto scale = _buildValue(frameNo, &transform->scale); jerry_object_set_sz(obj, "scale", scale); jerry_value_free(scale); auto rotation = _buildValue(frameNo, &transform->rotation); jerry_object_set_sz(obj, "rotation", rotation); jerry_value_free(rotation); auto opacity = _buildValue(frameNo, &transform->opacity); jerry_object_set_sz(obj, "opacity", opacity); jerry_value_free(opacity); jerry_value_free(obj); } static jerry_value_t _buildGroup(LottieGroup* group, float frameNo) { auto obj = jerry_function_external(_content); //attach a transform ARRAY_FOREACH(p, group->children) { if ((*p)->type == LottieObject::Type::Transform) { _buildTransform(obj, frameNo, static_cast(*p)); break; } } jerry_object_set_native_ptr(obj, &freeCb, _expcontent(nullptr, frameNo, group)); jerry_object_set_sz(obj, EXP_CONTENT, obj); return obj; } static jerry_value_t _buildPolystar(LottiePolyStar* polystar, float frameNo) { auto obj = jerry_object(); auto position = jerry_object(); jerry_object_set_native_ptr(position, nullptr, &polystar->position); jerry_object_set_sz(obj, "position", position); jerry_value_free(position); auto innerRadius = jerry_number(polystar->innerRadius(frameNo)); jerry_object_set_sz(obj, "innerRadius", innerRadius); jerry_value_free(innerRadius); auto outerRadius = jerry_number(polystar->outerRadius(frameNo)); jerry_object_set_sz(obj, "outerRadius", outerRadius); jerry_value_free(outerRadius); auto innerRoundness = jerry_number(polystar->innerRoundness(frameNo)); jerry_object_set_sz(obj, "innerRoundness", innerRoundness); jerry_value_free(innerRoundness); auto outerRoundness = jerry_number(polystar->outerRoundness(frameNo)); jerry_object_set_sz(obj, "outerRoundness", outerRoundness); jerry_value_free(outerRoundness); auto rotation = jerry_number(polystar->rotation(frameNo)); jerry_object_set_sz(obj, "rotation", rotation); jerry_value_free(rotation); auto ptsCnt = jerry_number(polystar->ptsCnt(frameNo)); jerry_object_set_sz(obj, "points", ptsCnt); jerry_value_free(ptsCnt); return obj; } static jerry_value_t _buildTrimpath(LottieTrimpath* trimpath, float frameNo) { jerry_value_t obj = jerry_object(); auto start = jerry_number(trimpath->start(frameNo)); jerry_object_set_sz(obj, "start", start); jerry_value_free(start); auto end = jerry_number(trimpath->end(frameNo)); jerry_object_set_sz(obj, "end", end); jerry_value_free(end); auto offset = jerry_number(trimpath->offset(frameNo)); jerry_object_set_sz(obj, EXP_OFFSET, offset); jerry_value_free(offset); return obj; } static jerry_value_t _effectProperty(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto name = _name(args[0]); auto property = static_cast(data->effect)->property(name); tvg::free(name); if (!property) return jerry_undefined(); return _buildValue(data->frameNo, property); } static jerry_value_t _effect(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto layer = static_cast(data->obj); LottieEffect* effect = nullptr; //either name or index if (jerry_value_is_string(args[0])) { auto name = _name(args[0]); effect = layer->effectById(djb2Encode(name)); tvg::free(name); } else { effect = layer->effectByIdx((int16_t)jerry_value_as_int32(args[0])); } if (!effect) return jerry_undefined(); //find a effect property auto obj = jerry_function_external(_effectProperty); jerry_object_set_native_ptr(obj, &freeCb, _expcontent(data->exp, data->frameNo, effect)); return obj; } static void _buildLayer(jerry_value_t context, float frameNo, LottieLayer* layer, LottieLayer* comp, LottieExpression* exp) { auto width = jerry_number(layer->w); jerry_object_set_sz(context, EXP_WIDTH, width); jerry_value_free(width); auto height = jerry_number(layer->h); jerry_object_set_sz(context, EXP_HEIGHT, height); jerry_value_free(height); auto index = jerry_number(layer->ix); jerry_object_set_sz(context, EXP_INDEX, index); jerry_value_free(index); auto parent = jerry_object(); jerry_object_set_native_ptr(parent, nullptr, layer->parent); jerry_object_set_sz(context, "parent", parent); jerry_value_free(parent); auto hasParent = jerry_boolean(layer->parent ? true : false); jerry_object_set_sz(context, "hasParent", hasParent); jerry_value_free(hasParent); auto inPoint = jerry_number(layer->inFrame); jerry_object_set_sz(context, "inPoint", inPoint); jerry_value_free(inPoint); auto outPoint = jerry_number(layer->outFrame); jerry_object_set_sz(context, "outPoint", outPoint); jerry_value_free(outPoint); //TODO: Confirm exp->layer->comp->timeAtFrame() ? auto startTime = jerry_number(exp->comp->timeAtFrame(layer->startFrame)); jerry_object_set_sz(context, "startTime", startTime); jerry_value_free(startTime); auto hasVideo = jerry_boolean(false); jerry_object_set_sz(context, "hasVideo", hasVideo); jerry_value_free(hasVideo); auto hasAudio = jerry_boolean(false); jerry_object_set_sz(context, "hasAudio", hasAudio); jerry_value_free(hasAudio); //active, #current in the animation range? auto enabled = jerry_boolean(!layer->hidden); jerry_object_set_sz(context, "enabled", enabled); jerry_value_free(enabled); auto audioActive = jerry_boolean(false); jerry_object_set_sz(context, "audioActive", audioActive); jerry_value_free(audioActive); //sampleImage(point, radius = [.5, .5], postEffect=true, t=time) _buildTransform(context, frameNo, layer->transform); //audioLevels, #the value of the Audio Levels property of the layer in decibels auto timeRemap = jerry_object(); jerry_object_set_native_ptr(timeRemap, nullptr, &layer->timeRemap); jerry_object_set_sz(context, "timeRemap", timeRemap); jerry_value_free(timeRemap); //marker.key(index) //marker.key(name) //marker.nearestKey(t) //marker.numKeys //FIXME: This name conflicts with the function object in _layer(). No idea. Maybe jerryscript issue. #if 0 if (layer->name) { auto name = jerry_string_sz(layer->name); jerry_object_set_sz(context, EXP_NAME, name); jerry_value_free(name); } #endif auto toComp = jerry_function_external(_toComp); jerry_object_set_sz(context, "toComp", toComp); jerry_object_set_native_ptr(toComp, nullptr, layer); jerry_value_free(toComp); //content("name"), #look for the named property from a layer auto data = _expcontent(exp, frameNo, layer, 2); auto content = jerry_function_external(_content); jerry_object_set_sz(context, EXP_CONTENT, content); jerry_object_set_native_ptr(content, &freeCb, data); jerry_value_free(content); auto effect = jerry_function_external(_effect); jerry_object_set_sz(context, EXP_EFFECT, effect); jerry_object_set_native_ptr(effect, &freeCb, data); jerry_value_free(effect); } static jerry_value_t _addsub(const jerry_value_t args[], float addsub) { //string + string if (jerry_value_is_string(args[0]) || jerry_value_is_string(args[1])) { auto a = _name(args[0]); auto b = _name(args[1]); auto ret = tvg::concat(a, b); auto val = jerry_string_sz(ret); tvg::free(ret); tvg::free(a); tvg::free(b); return val; } //number + number auto n1 = jerry_value_is_number(args[0]); auto n2 = jerry_value_is_number(args[1]); //1d + 1d if (n1 && n2) return jerry_number(jerry_value_as_number(args[0]) + addsub * jerry_value_as_number(args[1])); auto pt = _point2d(args[n1 ? 1 : 0]); //2d + 1d if (n1 || n2) { auto secondary = n1 ? 0 : 1; auto val3 = jerry_value_as_number(args[secondary]); if (secondary == 0) pt.x = (pt.x * addsub) + val3; else pt.x += (addsub * val3); //2d + 2d } else { pt += _point2d(args[1]) * addsub; } return _point2d(pt); } static jerry_value_t _muldiv(const jerry_value_t arg1, float arg2) { //1d if (jerry_value_is_number(arg1)) return jerry_number(jerry_value_as_number(arg1) * arg2); //2d return _point2d(_point2d(arg1) * arg2); } static jerry_value_t _add(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return _addsub(args, 1.0f); } static jerry_value_t _sub(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return _addsub(args, -1.0f); } static jerry_value_t _mul(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return _muldiv(args[0], jerry_value_as_number(args[1])); } static jerry_value_t _div(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return _muldiv(args[0], 1.0f / jerry_value_as_number(args[1])); } static jerry_value_t _interp(float t, const jerry_value_t args[], int argsCnt) { auto tMin = 0.0f; auto tMax = 1.0f; int idx = 0; tMin = jerry_value_as_number(args[1]); tMax = jerry_value_as_number(args[2]); idx += 2; t = (t - tMin) / (tMax - tMin); if (t < 0) t = 0.0f; else if (t > 1) t = 1.0f; //2d if (jerry_value_is_object(args[idx + 1]) && jerry_value_is_object(args[idx + 2])) { return _point2d(tvg::lerp(_point2d(args[idx + 1]), _point2d(args[idx + 2]), t)); } //1d return jerry_number(tvg::lerp(jerry_value_as_number(args[idx + 1]), jerry_value_as_number(args[idx + 2]), t)); } static jerry_value_t _linear(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto t = jerry_value_as_number(args[0]); return _interp(t, args, jerry_value_as_uint32(argsCnt)); } static jerry_value_t _ease(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto t = jerry_value_as_number(args[0]); t = (t < 0.5f) ? (4 * t * t * t) : (1.0f - powf(-2.0f * t + 2.0f, 3) * 0.5f); return _interp(t, args, jerry_value_as_uint32(argsCnt)); } static jerry_value_t _easeIn(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto t = jerry_value_as_number(args[0]); t = t * t * t; return _interp(t, args, jerry_value_as_uint32(argsCnt)); } static jerry_value_t _easeOut(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto t = jerry_value_as_number(args[0]); t = 1.0f - powf(1.0f - t, 3); return _interp(t, args, jerry_value_as_uint32(argsCnt)); } static jerry_value_t _clamp(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto num = jerry_value_as_number(args[0]); auto limit1 = jerry_value_as_number(args[1]); auto limit2 = jerry_value_as_number(args[2]); //clamping if (num < limit1) num = limit1; if (num > limit2) num = limit2; return jerry_number(num); } static jerry_value_t _dot(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return jerry_number(tvg::dot(_point2d(args[0]), _point2d(args[1]))); } static jerry_value_t _cross(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return jerry_number(tvg::cross(_point2d(args[0]), _point2d(args[1]))); } static jerry_value_t _normalize(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto pt = _point2d(args[0]); return _point2d(pt / tvg::length(pt)); } static jerry_value_t _length(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return jerry_number(tvg::length(_point2d(args[0]))); } static jerry_value_t _random(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return jerry_number(_rand()); } static jerry_value_t _deg2rad(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return jerry_number(deg2rad(jerry_value_as_number(args[0]))); } static jerry_value_t _rad2deg(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { return jerry_number(rad2deg(jerry_value_as_number(args[0]))); } static jerry_value_t _content(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto group = static_cast(data->obj); auto target = group->content(_idByName(args[0])); if (!target) return jerry_undefined(); //find the a path property(sh) in the group layer? switch (target->type) { case LottieObject::Group: return _buildGroup(static_cast(target), data->frameNo); case LottieObject::Path: { auto obj = jerry_object(); jerry_object_set_native_ptr(obj, nullptr, &static_cast(target)->pathset); jerry_object_set_sz(obj, "path", obj); return obj; } case LottieObject::Polystar: return _buildPolystar(static_cast(target), data->frameNo); case LottieObject::Trimpath: return _buildTrimpath(static_cast(target), data->frameNo); default: break; } return jerry_undefined(); } static jerry_value_t _points(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { /* TODO: ThorVG prebuilds the path data for performance. It actually need to constructs the Array for points, inTangents, outTangents and then return here... */ auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto obj = jerry_object(); jerry_object_set_native_ptr(obj, nullptr, data->property); return obj; } static jerry_value_t _pointOnPath(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto pathset = static_cast(data->property); auto progress = jerry_value_as_number(args[0]); RenderPath out; (*pathset)(data->frameNo, out, nullptr, nullptr); return _point2d(out.point(progress)); } static void _buildPath(jerry_value_t context, float frameNo, LottieProperty* pathset) { auto data = _expcontent(nullptr, frameNo, pathset, 2); //Trick for fast building path. auto points = jerry_function_external(_points); jerry_object_set_native_ptr(points, &freeCb, data); jerry_object_set_sz(context, "points", points); jerry_value_free(points); auto pointOnPath = jerry_function_external(_pointOnPath); jerry_object_set_native_ptr(pointOnPath, &freeCb, data); jerry_object_set_sz(context, "pointOnPath", pointOnPath); jerry_value_free(pointOnPath); //inTangents //outTangents //isClosed } static jerry_value_t _layerChild(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); jerry_value_t obj = jerry_undefined(); //find a member by index if (jerry_value_is_number(args[0])) { auto idx = (uint32_t)jerry_value_as_int32(args[0]) - 1; auto children = static_cast*>(data->data); if (idx < children->count) { obj = jerry_function_external(_layerChild); jerry_object_set_native_ptr(obj, &freeCb, _expcontent(data->exp, data->frameNo, (*children)[idx])); } //find a member by name } else { auto name = _name(args[0]); if (name) { //for backward compatibility: reserved ADOBE keyword if (!strcmp(name, "ADBE Root Vectors Group") || !strcmp(name, "ADBE Vectors Group")) { auto group = static_cast(data->obj); if (group->type == LottieObject::Type::Group || group->type == LottieObject::Type::Layer) { obj = jerry_function_external(_layerChild); jerry_object_set_native_ptr(obj, &freeCb, _expcontent(data->exp, data->frameNo, &group->children)); } } else if (!strcmp(name, "ADBE Vector Shape")) { obj = jerry_object(); _buildPath(obj, data->frameNo, &static_cast(data->obj)->pathset); } tvg::free(name); } } return obj; } static jerry_value_t _layer(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto comp = static_cast(data->obj); //either index or name auto layer = jerry_value_is_number(args[0]) ? comp->layerByIdx((uint16_t)jerry_value_as_int32(args[0])) : comp->layerById(_idByName(args[0])); if (!layer) return jerry_undefined(); auto obj = jerry_function_external(_layerChild); jerry_object_set_native_ptr(obj, &freeCb, _expcontent(data->exp, data->frameNo, layer)); _buildLayer(obj, data->frameNo, layer, comp, data->exp); return obj; } static jerry_value_t _nearestKey(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto exp = static_cast(jerry_object_get_native_ptr(info->function, nullptr)); auto time = jerry_value_as_number(args[0]); auto frameNo = exp->comp->frameAtTime(time); auto index = jerry_number((float)exp->property->nearest(frameNo)); auto obj = jerry_object(); jerry_object_set_sz(obj, EXP_INDEX, index); jerry_value_free(index); return obj; } static jerry_value_t _property(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto property = data->obj->property(jerry_value_as_int32(args[0])); if (!property) return jerry_undefined(); return _buildValue(data->frameNo, property); } static jerry_value_t _propertyGroup(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto level = jerry_value_as_int32(args[0]); //intermediate group if (level == 1) { auto group = jerry_function_external(_property); jerry_object_set_native_ptr(group, &freeCb, _expcontent(data)); return group; } TVGLOG("LOTTIE", "propertyGroup(%d)?", level); return jerry_undefined(); } static jerry_value_t _valueAtTime(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto exp = static_cast(jerry_object_get_native_ptr(info->function, nullptr)); auto time = jerry_value_as_number(args[0]); auto frameNo = exp->comp->frameAtTime(time); return _buildValue(frameNo, exp->property); } static jerry_value_t _velocity(Point& prv, Point& cur, float elapsed) { return _point2d({(cur.x - prv.x) / elapsed, (cur.y - prv.y) / elapsed}); } static jerry_value_t _velocityAtTime(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto exp = static_cast(jerry_object_get_native_ptr(info->function, nullptr)); auto key = exp->property->nearest(exp->comp->frameAtTime(jerry_value_as_number(args[0]))); auto pframe = exp->property->frameNo(key - 1); auto cframe = exp->property->frameNo(key); auto elapsed = (cframe - pframe) / (exp->comp->frameRate); //compute the velocity switch (exp->property->type) { case LottieProperty::Type::Float: { auto prv = (*static_cast(exp->property))(pframe); auto cur = (*static_cast(exp->property))(cframe); return jerry_number((cur - prv) / elapsed); } case LottieProperty::Type::Scalar: { auto prv = (*static_cast(exp->property))(pframe); auto cur = (*static_cast(exp->property))(cframe); return _velocity(prv, cur, elapsed); } case LottieProperty::Type::Vector: { auto prv = (*static_cast(exp->property))(pframe); auto cur = (*static_cast(exp->property))(cframe); return _velocity(prv, cur, elapsed); } default: TVGLOG("LOTTIE", "Non supported type for velocityAtTime?"); } return jerry_undefined(); } static jerry_value_t _speedAtTime(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto exp = static_cast(jerry_object_get_native_ptr(info->function, nullptr)); auto key = exp->property->nearest(exp->comp->frameAtTime(jerry_value_as_number(args[0]))); auto pframe = exp->property->frameNo(key - 1); auto cframe = exp->property->frameNo(key); Point cur, prv; //compute the velocity switch (exp->property->type) { case LottieProperty::Type::Scalar: { prv = (*static_cast(exp->property))(pframe); cur = (*static_cast(exp->property))(cframe); break; } case LottieProperty::Type::Vector: { prv = (*static_cast(exp->property))(pframe); cur = (*static_cast(exp->property))(cframe); break; } default: { TVGLOG("LOTTIE", "Non supported type for speedAtTime?"); return jerry_undefined(); } } auto elapsed = (cframe - pframe) / (exp->comp->frameRate); auto speed = sqrtf(pow(cur.x - prv.x, 2) + pow(cur.y - prv.y, 2)) / elapsed; return jerry_number(speed); } static jerry_value_t _wiggle(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto freq = jerry_value_as_number(args[0]); auto amp = jerry_value_as_number(args[1]); auto octaves = (argsCnt > 2) ? jerry_value_as_int32(args[2]) : 1; auto ampm = (argsCnt > 3) ? jerry_value_as_number(args[3]) : 0.5f; auto time = (argsCnt > 4) ? jerry_value_as_number(args[4]) : data->exp->comp->timeAtFrame(data->frameNo); Point result = {0.0f, 0.0f}; auto property = data->exp->property; if (property->type == LottieProperty::Type::Vector) { result = (*static_cast(property))(data->frameNo); } else if (property->type == LottieProperty::Type::Scalar) { result = (*static_cast(property))(data->frameNo); } for (int o = 0; o < octaves; ++o) { auto repeat = (int)ceil(time * freq); for (int i = 0; i < repeat; ++i) { result.x += (_rand() * 2.0f - 1.0f) * amp; result.y += (_rand() * 2.0f - 1.0f) * amp; } freq *= 2.0f; amp *= ampm; } return _point2d(result); } static jerry_value_t _temporalWiggle(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto freq = jerry_value_as_number(args[0]); auto amp = jerry_value_as_number(args[1]); auto octaves = (argsCnt > 2) ? jerry_value_as_int32(args[2]) : 1; auto ampm = (argsCnt > 3) ? jerry_value_as_number(args[3]) : 5.0f; auto time = (argsCnt > 4) ? jerry_value_as_number(args[4]) : data->exp->comp->timeAtFrame(data->frameNo); auto wiggleTime = time; for (int o = 0; o < octaves; ++o) { auto repeat = int(time * freq); auto frac = (time * freq - float(repeat)); for (int i = 0; i < repeat; ++i) { wiggleTime += (_rand() * 2.0f - 1.0f) * amp * frac; } freq *= 2.0f; amp *= ampm; } return _buildValue(data->exp->comp->frameAtTime(wiggleTime), data->exp->property); } static LottieProperty::Loop _loopCommon(const jerry_value_t args[], const jerry_length_t argsCnt) { auto mode = LottieProperty::Loop::InCycle; if (argsCnt > 0) { auto name = _name(args[0]); if (!strcmp(name, EXP_CYCLE)) mode = LottieProperty::Loop::InCycle; else if (!strcmp(name, EXP_PINGPONG)) mode = LottieProperty::Loop::InPingPong; else if (!strcmp(name, EXP_OFFSET)) mode = LottieProperty::Loop::InOffset; else if (!strcmp(name, EXP_CONTINUE)) mode = LottieProperty::Loop::InContinue; tvg::free(name); } if (mode != LottieProperty::Loop::InCycle && mode != LottieProperty::Loop::InPingPong) { TVGLOG("LOTTIE", "Not supported loopIn type = %d", (int) mode); } return mode; } #define LOOP_OUT_OFFSET 4 static jerry_value_t _loopOut(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto mode = static_cast((int) _loopCommon(args, argsCnt) + LOOP_OUT_OFFSET); auto key = (argsCnt > 1) ? jerry_value_as_int32(args[1]) : 0; return _buildValue(data->exp->property->loop(data->frameNo, key, mode, data->exp->layer->outFrame), data->exp->property); } static jerry_value_t _loopOutDuration(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto mode = static_cast((int) _loopCommon(args, argsCnt) + LOOP_OUT_OFFSET); auto out = (argsCnt > 1) ? data->exp->comp->frameAtTime(jerry_value_as_number(args[1])) : FLT_MAX; return _buildValue(data->exp->property->loop(data->frameNo, 0, mode, out), data->exp->property); } static jerry_value_t _loopIn(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto mode = _loopCommon(args, argsCnt); auto key = (argsCnt > 1) ? jerry_value_as_int32(args[1]) : 0; return _buildValue(data->exp->property->loop(data->frameNo, key, mode, data->exp->layer->outFrame), data->exp->property); } static jerry_value_t _loopInDuration(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto mode = _loopCommon(args, argsCnt); auto in = (argsCnt > 1) ? data->exp->comp->frameAtTime(jerry_value_as_number(args[1])) : FLT_MAX; return _buildValue(data->exp->property->loop(data->frameNo, 0, mode, in), data->exp->property); } static jerry_value_t _key(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto exp = static_cast(jerry_object_get_native_ptr(info->function, nullptr)); auto frameNo = exp->property->frameNo(jerry_value_as_int32(args[0])); auto time = jerry_number(exp->comp->timeAtFrame(frameNo)); auto value = _buildValue(frameNo, exp->property); auto obj = jerry_object(); jerry_object_set_sz(obj, EXP_TIME, time); jerry_object_set_sz(obj, EXP_INDEX, args[0]); jerry_object_set_sz(obj, EXP_VALUE, value); //direct access, key[0], key[1] if (exp->property->type == LottieProperty::Type::Float) { jerry_object_set_index(obj, 0, value); } else if (exp->property->type == LottieProperty::Type::Scalar || exp->property->type == LottieProperty::Type::Vector) { jerry_object_set_index(obj, 0, jerry_object_get_index(value, 0)); jerry_object_set_index(obj, 1, jerry_object_get_index(value, 1)); } jerry_value_free(time); jerry_value_free(value); return obj; } static void _buildProperty(float frameNo, jerry_value_t context, LottieExpression* exp) { auto value = _buildValue(frameNo, exp->property); jerry_object_set_sz(context, EXP_VALUE, value); jerry_value_free(value); auto valueAtTime = jerry_function_external(_valueAtTime); jerry_object_set_sz(context, "valueAtTime", valueAtTime); jerry_object_set_native_ptr(valueAtTime, nullptr, exp); jerry_value_free(valueAtTime); auto velocity = jerry_number(0.0f); jerry_object_set_sz(context, "velocity", velocity); jerry_value_free(velocity); auto velocityAtTime = jerry_function_external(_velocityAtTime); jerry_object_set_sz(context, "velocityAtTime", velocityAtTime); jerry_object_set_native_ptr(velocityAtTime, nullptr, exp); jerry_value_free(velocityAtTime); auto speed = jerry_number(0.0f); jerry_object_set_sz(context, "speed", speed); jerry_value_free(speed); auto speedAtTime = jerry_function_external(_speedAtTime); jerry_object_set_sz(context, "speedAtTime", speedAtTime); jerry_object_set_native_ptr(speedAtTime, nullptr, exp); jerry_value_free(speedAtTime); auto propertyIndex = jerry_number(exp->property->ix); jerry_object_set_sz(context, "propertyIndex", propertyIndex); jerry_value_free(propertyIndex); { auto data = _expcontent(exp, frameNo, exp->object, 7); auto wiggle = jerry_function_external(_wiggle); jerry_object_set_sz(context, "wiggle", wiggle); jerry_object_set_native_ptr(wiggle, &freeCb, data); jerry_value_free(wiggle); auto temporalWiggle = jerry_function_external(_temporalWiggle); jerry_object_set_sz(context, "temporalWiggle", temporalWiggle); jerry_object_set_native_ptr(temporalWiggle, &freeCb, data); jerry_value_free(temporalWiggle); auto propertyGroup = jerry_function_external(_propertyGroup); jerry_object_set_native_ptr(propertyGroup, &freeCb, data); jerry_object_set_sz(context, "propertyGroup", propertyGroup); jerry_value_free(propertyGroup); auto loopIn = jerry_function_external(_loopIn); jerry_object_set_sz(context, "loopIn", loopIn); jerry_object_set_native_ptr(loopIn, &freeCb, data); jerry_value_free(loopIn); auto loopOut = jerry_function_external(_loopOut); jerry_object_set_sz(context, "loopOut", loopOut); jerry_object_set_native_ptr(loopOut, &freeCb, data); jerry_value_free(loopOut); auto loopInDuration = jerry_function_external(_loopInDuration); jerry_object_set_sz(context, "loopInDuration", loopInDuration); jerry_object_set_native_ptr(loopInDuration, &freeCb, data); jerry_value_free(loopInDuration); auto loopOutDuration = jerry_function_external(_loopOutDuration); jerry_object_set_sz(context, "loopOutDuration", loopOutDuration); jerry_object_set_native_ptr(loopOutDuration, &freeCb, data); jerry_value_free(loopOutDuration); } //smooth(width=.2, samples=5, t=time) auto key = jerry_function_external(_key); jerry_object_set_sz(context, "key", key); jerry_object_set_native_ptr(key, nullptr, exp); jerry_value_free(key); //key(markerName) auto nearestKey = jerry_function_external(_nearestKey); jerry_object_set_native_ptr(nearestKey, nullptr, exp); jerry_object_set_sz(context, "nearestKey", nearestKey); jerry_value_free(nearestKey); auto numKeys = jerry_number((float)exp->property->frameCnt()); jerry_object_set_sz(context, "numKeys", numKeys); jerry_value_free(numKeys); //name { auto data = _expcontent(exp, frameNo, exp->layer, 2); //content("name"), #look for the named property from a layer auto content = jerry_function_external(_content); jerry_object_set_sz(context, EXP_CONTENT, content); jerry_object_set_native_ptr(content, &freeCb, data); jerry_value_free(content); auto effect = jerry_function_external(_effect); jerry_object_set_sz(context, EXP_EFFECT, effect); jerry_object_set_native_ptr(effect, &freeCb, data); jerry_value_free(effect); } auto effect = jerry_function_external(_effect); jerry_object_set_sz(context, EXP_EFFECT, effect); jerry_object_set_native_ptr(effect, &freeCb, _expcontent(exp, frameNo, exp->layer)); jerry_value_free(effect); //expansions per types if (exp->property->type == LottieProperty::Type::PathSet) _buildPath(context, frameNo, exp->property); } static jerry_value_t _comp(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt) { auto data = static_cast(jerry_object_get_native_ptr(info->function, &freeCb)); auto comp = static_cast(data->obj); auto layer = comp->layerById(_idByName(args[0])); if (!layer) return jerry_undefined(); auto obj = jerry_object(); jerry_object_set_native_ptr(obj, nullptr, layer); _buildLayer(obj, data->frameNo, layer, comp, data->exp); return obj; } static void _buildMath(jerry_value_t context) { auto bm_mul = jerry_function_external(_mul); jerry_object_set_sz(context, "$bm_mul", bm_mul); jerry_value_free(bm_mul); auto bm_sum = jerry_function_external(_add); jerry_object_set_sz(context, "$bm_sum", bm_sum); jerry_value_free(bm_sum); auto bm_add = jerry_function_external(_add); jerry_object_set_sz(context, "$bm_add", bm_add); jerry_value_free(bm_add); auto bm_sub = jerry_function_external(_sub); jerry_object_set_sz(context, "$bm_sub", bm_sub); jerry_value_free(bm_sub); auto bm_div = jerry_function_external(_div); jerry_object_set_sz(context, "$bm_div", bm_div); jerry_value_free(bm_div); auto mul = jerry_function_external(_mul); jerry_object_set_sz(context, "mul", mul); jerry_value_free(mul); auto sum = jerry_function_external(_add); jerry_object_set_sz(context, "sum", sum); jerry_value_free(sum); auto add = jerry_function_external(_add); jerry_object_set_sz(context, "add", add); jerry_value_free(add); auto sub = jerry_function_external(_sub); jerry_object_set_sz(context, "sub", sub); jerry_value_free(sub); auto div = jerry_function_external(_div); jerry_object_set_sz(context, "div", div); jerry_value_free(div); auto clamp = jerry_function_external(_clamp); jerry_object_set_sz(context, "clamp", clamp); jerry_value_free(clamp); auto dot = jerry_function_external(_dot); jerry_object_set_sz(context, "dot", dot); jerry_value_free(dot); auto cross = jerry_function_external(_cross); jerry_object_set_sz(context, "cross", cross); jerry_value_free(cross); auto normalize = jerry_function_external(_normalize); jerry_object_set_sz(context, "normalize", normalize); jerry_value_free(normalize); auto length = jerry_function_external(_length); jerry_object_set_sz(context, "length", length); jerry_value_free(length); auto random = jerry_function_external(_random); jerry_object_set_sz(context, "random", random); jerry_value_free(random); auto deg2rad = jerry_function_external(_deg2rad); jerry_object_set_sz(context, "degreesToRadians", deg2rad); jerry_value_free(deg2rad); auto rad2deg = jerry_function_external(_rad2deg); jerry_object_set_sz(context, "radiansToDegrees", rad2deg); jerry_value_free(rad2deg); auto linear = jerry_function_external(_linear); jerry_object_set_sz(context, "linear", linear); jerry_value_free(linear); auto ease = jerry_function_external(_ease); jerry_object_set_sz(context, "ease", ease); jerry_value_free(ease); auto easeIn = jerry_function_external(_easeIn); jerry_object_set_sz(context, "easeIn", easeIn); jerry_value_free(easeIn); auto easeOut = jerry_function_external(_easeOut); jerry_object_set_sz(context, "easeOut", easeOut); jerry_value_free(easeOut); //lookAt } void LottieExpressions::buildGlobal(float frameNo, LottieExpression* exp) { tvg::free(static_cast(jerry_object_get_native_ptr(comp, &freeCb))); jerry_object_set_native_ptr(comp, &freeCb, _expcontent(exp, frameNo, exp->layer)); auto index = jerry_number(exp->layer->ix); jerry_object_set_sz(global, EXP_INDEX, index); jerry_value_free(index); } void LottieExpressions::buildComp(jerry_value_t context, float frameNo, LottieLayer* comp, LottieExpression* exp) { //layer(index) / layer(name) / layer(otherLayer, reIndex) auto layer = jerry_function_external(_layer); jerry_object_set_sz(context, "layer", layer); jerry_object_set_native_ptr(layer, &freeCb, _expcontent(exp, frameNo, comp)); jerry_value_free(layer); auto numLayers = jerry_number((float)comp->children.count); jerry_object_set_sz(context, "numLayers", numLayers); jerry_value_free(numLayers); } void LottieExpressions::buildComp(LottieComposition* comp, float frameNo, LottieExpression* exp) { buildComp(this->comp, frameNo, comp->root, exp); //marker //marker.key(index) //marker.key(name) //marker.nearestKey(t) //marker.numKeys //activeCamera auto width = jerry_number(comp->w); jerry_object_set_sz(thisComp, EXP_WIDTH, width); jerry_value_free(width); auto height = jerry_number(comp->h); jerry_object_set_sz(thisComp, EXP_HEIGHT, height); jerry_value_free(height); auto duration = jerry_number(comp->duration()); jerry_object_set_sz(thisComp, "duration", duration); jerry_value_free(duration); //ntscDropFrame //displayStartTime auto frameDuration = jerry_number(1.0f / comp->frameRate); jerry_object_set_sz(thisComp, "frameDuration", frameDuration); jerry_value_free(frameDuration); //shutterAngle //shutterPhase //bgColor //pixelAspect if (comp->name) { auto name = jerry_string_sz(comp->name); jerry_object_set_sz(thisComp, EXP_NAME, name); jerry_value_free(name); } } jerry_value_t LottieExpressions::buildGlobal() { global = jerry_current_realm(); //comp(name) comp = jerry_function_external(_comp); jerry_object_set_sz(global, "comp", comp); //footage(name) thisComp = jerry_object(); jerry_object_set_sz(global, "thisComp", thisComp); thisLayer = jerry_object(); jerry_object_set_sz(global, "thisLayer", thisLayer); thisProperty = jerry_object(); jerry_object_set_sz(global, "thisProperty", thisProperty); //fromCompToSurface //createPath //posterizeTime(framesPerSecond) //value return global; } void LottieExpressions::buildWritables(LottieExpression* exp) { if (exp->writables.empty()) return; ARRAY_FOREACH(p, exp->writables) { auto writable = jerry_number(p->val); jerry_object_set_sz(global, p->var, writable); jerry_value_free(writable); } } jerry_value_t LottieExpressions::evaluate(float frameNo, LottieExpression* exp) { if (exp->disabled && exp->writables.empty()) return jerry_undefined(); buildGlobal(frameNo, exp); //main composition buildComp(exp->comp, frameNo, exp); //this composition buildComp(thisComp, frameNo, exp->layer->comp, exp); //update global context values _buildProperty(frameNo, global, exp); //this layer jerry_object_set_native_ptr(thisLayer, nullptr, exp->layer); _buildLayer(thisLayer, frameNo, exp->layer, exp->comp->root, exp); //this property jerry_object_set_native_ptr(thisProperty, nullptr, exp->property); _buildProperty(frameNo, thisProperty, exp); //expansions per object type if (exp->object->type == LottieObject::Transform) _buildTransform(global, frameNo, static_cast(exp->object)); //update writable values buildWritables(exp); //evaluate the code auto eval = jerry_eval((jerry_char_t *) exp->code, strlen(exp->code), JERRY_PARSE_NO_OPTS); if (jerry_value_is_exception(eval)) { TVGERR("LOTTIE", "Failed to dispatch the expressions!"); exp->disabled = true; return jerry_undefined(); } jerry_value_free(eval); return jerry_object_get_sz(global, "$bm_rt"); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ LottieExpressions::~LottieExpressions() { jerry_value_free(thisProperty); jerry_value_free(thisLayer); jerry_value_free(thisComp); jerry_value_free(comp); jerry_value_free(global); jerry_cleanup(); } LottieExpressions::LottieExpressions() { jerry_init(JERRY_INIT_EMPTY); _buildMath(buildGlobal()); } void LottieExpressions::update(float curTime) { //time, #current time in seconds auto time = jerry_number(curTime); jerry_object_set_sz(global, EXP_TIME, time); jerry_value_free(time); } //FIXME: Threads support #include "tvgTaskScheduler.h" LottieExpressions* LottieExpressions::instance() { //FIXME: Threads support if (TaskScheduler::threads() > 0) { TVGLOG("LOTTIE", "Lottie Expressions are not supported with tvg threads"); return nullptr; } if (!exps) exps = new LottieExpressions; ++engineRefCnt; return exps; } void LottieExpressions::retrieve(LottieExpressions* instance) { if (--engineRefCnt == 0) { delete(instance); exps = nullptr; } } Point LottieExpressions::toPoint2d(jerry_value_t obj) { return _point2d(obj); } RGB32 LottieExpressions::toColor(jerry_value_t obj) { return _color(obj); } #endif //THORVG_LOTTIE_EXPRESSIONS_SUPPORT thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-function-object.h000664 001750 001750 00000013625 15164251010 046563 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_FUNCTION_OBJECT_H #define ECMA_FUNCTION_OBJECT_H #include "ecma-builtin-handlers.h" #include "ecma-builtins.h" #include "ecma-globals.h" #include "vm.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmafunctionobject ECMA Function object related routines * @{ */ ecma_value_t ecma_op_function_form_name (ecma_string_t *prop_name_p, char *prefix_p, lit_utf8_size_t prefix_size); bool ecma_op_is_callable (ecma_value_t value); #if JERRY_BUILTIN_PROXY bool ecma_op_proxy_object_is_callable (ecma_object_t *obj_p); #endif /* JERRY_BUILTIN_PROXY */ bool ecma_op_object_is_callable (ecma_object_t *obj_p); bool ecma_is_constructor (ecma_value_t value); bool ecma_object_is_constructor (ecma_object_t *obj_p); /** * Special constant indicating that the value is a valid constructor * * Use after the ecma_*_check_constructor calls. */ ecma_error_msg_t ecma_object_check_constructor (ecma_object_t *obj_p); ecma_error_msg_t ecma_check_constructor (ecma_value_t value); ecma_object_t *ecma_op_create_simple_function_object (ecma_object_t *scope_p, const ecma_compiled_code_t *bytecode_data_p); ecma_object_t *ecma_op_create_external_function_object (ecma_native_handler_t handler_cb); const ecma_compiled_code_t *ecma_op_function_get_compiled_code (ecma_extended_object_t *function_p); #if JERRY_BUILTIN_REALMS ecma_global_object_t *ecma_op_function_get_realm (const ecma_compiled_code_t *bytecode_header_p); ecma_global_object_t *ecma_op_function_get_function_realm (ecma_object_t *func_obj_p); #endif /* JERRY_BUILTIN_REALMS */ ecma_value_t ecma_op_create_dynamic_function (const ecma_value_t *arguments_list_p, uint32_t arguments_list_len, ecma_parse_opts_t opts); ecma_value_t ecma_op_function_get_super_constructor (ecma_object_t *func_obj_p); ecma_object_t *ecma_op_create_any_function_object (ecma_object_t *scope_p, const ecma_compiled_code_t *bytecode_data_p); ecma_object_t *ecma_op_create_arrow_function_object (ecma_object_t *scope_p, const ecma_compiled_code_t *bytecode_data_p, ecma_value_t this_binding); ecma_object_t *ecma_op_create_native_handler (ecma_native_handler_id_t id, size_t object_size); ecma_object_t *ecma_op_get_prototype_from_constructor (ecma_object_t *ctor_obj_p, ecma_builtin_id_t default_proto_id); ecma_value_t ecma_op_function_has_instance (ecma_object_t *func_obj_p, ecma_value_t value); ecma_value_t ecma_op_function_validated_call (ecma_value_t callee, ecma_value_t this_arg_value, const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_value_t ecma_op_function_call (ecma_object_t *func_obj_p, ecma_value_t this_arg_value, const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_value_t ecma_op_function_construct (ecma_object_t *func_obj_p, ecma_object_t *new_target_p, const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_property_t *ecma_op_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_property_t *ecma_op_external_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_property_t *ecma_op_bound_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, ecma_string_t *property_name_p); void ecma_op_function_delete_built_in_property (ecma_object_t *object_p, ecma_string_t *property_name_p); void ecma_op_bound_function_delete_built_in_property (ecma_object_t *object_p, ecma_string_t *property_name_p); void ecma_op_function_list_lazy_property_names (ecma_object_t *object_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p, jerry_property_filter_t filter); void ecma_op_external_function_list_lazy_property_names (ecma_object_t *object_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p, jerry_property_filter_t filter); void ecma_op_bound_function_list_lazy_property_names (ecma_object_t *object_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p, jerry_property_filter_t filter); /** * @} * @} */ #endif /* !ECMA_FUNCTION_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test8.lot000664 001750 001750 00000044521 15164251010 034003 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"84_Text_Range selector (Shape)","assets":[],"fonts":{"list":[{"fName":"AvertaStd-Black","fFamily":"AvertaStd-Black","fStyle":"?","ascent":73.599}]},"layers":[{"ind":1,"ty":5,"nm":"Range selector (Shape) Triangle","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[256,234.319,0],"l":2},"a":{"a":0,"k":[-0.028,14.749,0],"l":2},"s":{"a":0,"k":[362.31,362.31,100],"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":19,"f":"AvertaStd-Black","t":"Range\rselector\r(Shape)\rTriangle","j":2,"tr":0,"lh":20,"ls":0,"fc":[0.286,0.796,0.804]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0]}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0},"ne":{"a":0,"k":0},"a":{"a":0,"k":100},"b":1,"rn":0,"sh":4,"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":90,"s":[100]},{"t":179,"s":[0]}]},"r":1},"a":{"s":{"a":0,"k":[40,40,100]}}}]},"ip":0,"op":180,"st":0},{"ind":2,"ty":5,"nm":"text","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[9.982,27.699,0],"l":2},"a":{"a":0,"k":[0.232,-7.801,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":29,"f":"AvertaStd-Black","t":"84_Text_Range selector (Shape)","j":0,"tr":0,"lh":34.8,"ls":0,"fc":[1,1,1]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0]}},"a":[]},"ip":0,"op":180,"st":0},{"ind":3,"ty":4,"nm":"Container","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[256,256,0],"l":2},"a":{"a":0,"k":[0,0,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[256,-25.5],[256,25.5],[-256,25.5],[-256,-25.5]],"c":true}},"nm":"Path 1"},{"ty":"fl","c":{"a":0,"k":[0.286,0.796,0.804,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1"},{"ty":"tr","p":{"a":0,"k":[0,-230.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle 2"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[256,-256],[256,256],[-256,256],[-256,-256]],"c":true}},"nm":"Path 1"},{"ty":"st","c":{"a":0,"k":[0.286,0.796,0.804,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":7},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle 1"},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1"}],"ip":0,"op":180,"st":0}],"markers":[{"tm":45,"cm":"2","dr":0},{"tm":90,"cm":"1","dr":0},{"tm":135,"cm":"3","dr":0}],"chars":[{"ch":"8","size":29,"style":"?","w":55.4,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.567,2.567],[0,3.134],[4.1,3.2],[6.266,0],[4.033,-3.166],[0,-5.933],[-1.6,-2.566],[-2.334,-1],[0,0],[2.266,-3.133],[0,-4.466],[-4.834,-3.633],[-7.334,0],[-4.867,3.6],[0,6.134],[2.233,3.134],[3.666,1.334]],"o":[[2.333,-1.066],[1.566,-2.566],[0,-5.866],[-4.1,-3.2],[-6.334,0],[-4.034,3.167],[0,3.2],[1.6,2.567],[0,0],[-3.6,1.4],[-2.267,3.134],[0,6.067],[4.833,3.634],[7.4,0],[4.866,-3.6],[0,-4.533],[-2.234,-3.133],[0,0]],"v":[[41.1,-37.3],[46.95,-42.75],[49.3,-51.3],[43.15,-64.9],[27.6,-69.7],[12.05,-64.95],[6,-51.3],[8.4,-42.65],[14.3,-37.3],[14.3,-37.2],[5.5,-30.4],[2.1,-19],[9.35,-4.45],[27.6,1],[46,-4.4],[53.3,-19],[49.95,-30.5],[41.1,-37.2]],"c":true}},"nm":"8"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[-1,-1.166],[0,-1.666],[1.033,-1.1],[1.466,0],[1,1.1],[0,1.6],[-1,1.167],[-1.467,0]],"o":[[1,1.167],[0,1.6],[-1.034,1.1],[-1.467,0],[-1,-1.1],[0,-1.666],[1,-1.166],[1.533,0]],"v":[[31.4,-53.85],[32.9,-49.6],[31.35,-45.55],[27.6,-43.9],[23.9,-45.55],[22.4,-49.6],[23.9,-53.85],[27.6,-55.6]],"c":true}},"nm":"8"},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[1.366,1.5],[0,2.134],[-1.334,1.4],[-2,0],[-1.334,-1.4],[0,-2.133],[1.333,-1.5],[2,0]],"o":[[-1.367,-1.5],[0,-2.133],[1.333,-1.4],[2,0],[1.333,1.4],[0,2.134],[-1.334,1.5],[-1.934,0]],"v":[[22.65,-16.15],[20.6,-21.6],[22.6,-26.9],[27.6,-29],[32.6,-26.9],[34.6,-21.6],[32.6,-16.15],[27.6,-13.9]],"c":true}},"nm":"8"}],"nm":"8"}]},"fFamily":"AvertaStd-Black"},{"ch":"4","size":29,"style":"?","w":55.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[45.2,0],[45.2,-10.4],[53.7,-10.4],[53.7,-25.4],[45.2,-25.4],[45.2,-68.7],[31.1,-68.7],[0.7,-21],[0.7,-10.4],[27.3,-10.4],[27.3,0]],"c":true}},"nm":"4"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[29.3,-40.755],[29.3,-24],[18.739,-24]],"c":true}},"nm":"4"}],"nm":"4"}]},"fFamily":"AvertaStd-Black"},{"ch":"_","size":29,"style":"?","w":52.6,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[53.5,14.4],[53.5,2.7],[-0.9,2.7],[-0.9,14.4]],"c":true}},"nm":"_"}],"nm":"_"}]},"fFamily":"AvertaStd-Black"},{"ch":"T","size":29,"style":"?","w":58.4,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[38.7,0],[38.7,-54.1],[56.7,-54.1],[56.7,-71],[1.8,-71],[1.8,-54.1],[19.8,-54.1],[19.8,0]],"c":true}},"nm":"T"}],"nm":"T"}]},"fFamily":"AvertaStd-Black"},{"ch":"e","size":29,"style":"?","w":54.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[4.666,4.7],[7.066,0],[4.766,-4.8],[0,-7.4],[-4.867,-4.766],[-7.4,0],[-3.867,1.733],[-2.267,2.734],[0,0],[4.066,0],[1.833,1.2],[0.4,2.267],[0,0],[0,1.6]],"o":[[-4.667,-4.7],[-7.4,0],[-4.767,4.8],[0,7.467],[4.866,4.767],[4.933,0],[3.866,-1.733],[0,0],[-3.467,3.6],[-2.667,0],[-1.834,-1.2],[0,0],[0.266,-1.6],[0,-7.333]],"v":[[45.3,-42.95],[27.7,-50],[9.45,-42.8],[2.3,-24.5],[9.6,-6.15],[28,1],[41.2,-1.6],[50.4,-8.3],[41.2,-18.5],[29.9,-13.1],[23.15,-14.9],[19.8,-20.1],[51.9,-20.1],[52.3,-24.9]],"c":true}},"nm":"e"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.867,-4.4],[0,0],[-1.4,1.167],[-2,0]],"o":[[0,0],[0.4,-2.066],[1.4,-1.166],[4.066,0]],"v":[[35,-29.8],[19.8,-29.8],[22.5,-34.65],[27.6,-36.4]],"c":true}},"nm":"e"}],"nm":"e"}]},"fFamily":"AvertaStd-Black"},{"ch":"x","size":29,"style":"?","w":52.4,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[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]],"o":[[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]],"v":[[52.1,0],[36,-25.3],[51.8,-49],[32,-49],[26.2,-38.5],[26.1,-38.5],[20.4,-49],[0.5,-49],[16.3,-25.4],[0.2,0],[19.1,0],[26.1,-10.6],[26.3,-10.6],[33.1,0]],"c":true}},"nm":"x"}],"nm":"x"}]},"fFamily":"AvertaStd-Black"},{"ch":"t","size":29,"style":"?","w":39.1,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.2,2.4],[0,0],[1.2,0],[0,3.267],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-2.7,-2.766],[-4.4,0]],"o":[[0,0],[-1.2,0.467],[-2.134,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,5.4],[2.7,2.767],[5.4,0]],"v":[[35.4,-2.6],[31,-15],[27.4,-14.3],[24.2,-19.2],[24.2,-34.8],[34.8,-34.8],[34.8,-49],[24.2,-49],[24.2,-66.2],[6.3,-64.4],[6.3,-49],[1,-49],[1,-34.8],[6.3,-34.8],[6.3,-15.4],[10.35,-3.15],[21,1]],"c":true}},"nm":"t"}],"nm":"t"}]},"fFamily":"AvertaStd-Black"},{"ch":"R","size":29,"style":"?","w":64.1,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,10.267],[4.333,4.567],[8.6,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[8.066,-3.8],[0,-7.666],[-4.334,-4.566],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[47.5,-24.6],[47.5,-24.7],[59.6,-45.8],[53.1,-64.15],[33.7,-71],[5.3,-71],[5.3,0],[23.7,0],[23.7,-21.7],[28.1,-21.7],[41.5,0],[63.6,0]],"c":true}},"nm":"R"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.8,-1.533],[0,-2.333],[1.933,-1.4],[3.066,0],[0,0],[0,0]],"o":[[3.333,0],[1.8,1.534],[0,2.6],[-1.934,1.4],[0,0],[0,0],[0,0]],"v":[[29.5,-54.3],[37.2,-52],[39.9,-46.2],[37,-40.2],[29.5,-38.1],[23.7,-38.1],[23.7,-54.3]],"c":true}},"nm":"R"}],"nm":"R"}]},"fFamily":"AvertaStd-Black"},{"ch":"a","size":29,"style":"?","w":58.8,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[5.133,0],[4.133,-4.766],[0,-7.466],[-4.234,-4.733],[-6.734,0],[-2.867,3.734],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-3.4,-3.933],[-6.2,0],[-4.134,4.767],[0,7.534],[4.233,4.734],[5.533,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[36.7,-44.1],[36.6,-44.1],[23.8,-50],[8.3,-42.85],[2.1,-24.5],[8.45,-6.1],[24.9,1],[37.5,-4.6],[37.6,-4.6],[37.6,0],[54.6,0],[54.6,-49],[36.7,-49]],"c":true}},"nm":"a"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[1.6,1.8],[0,3],[-1.634,1.834],[-2.534,0],[-1.667,-1.833],[0,-2.866],[1.666,-1.833],[2.4,0]],"o":[[-1.6,-1.8],[0,-2.866],[1.633,-1.833],[2.4,0],[1.666,1.834],[0,2.934],[-1.667,1.834],[-2.6,0]],"v":[[22.6,-17.3],[20.2,-24.5],[22.65,-31.55],[28.9,-34.3],[35,-31.55],[37.5,-24.5],[35,-17.35],[28.9,-14.6]],"c":true}},"nm":"a"}],"nm":"a"}]},"fFamily":"AvertaStd-Black"},{"ch":"n","size":29,"style":"?","w":57.6,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.6,-4.266],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-4.734,0],[-1,-1.266],[0,-3.4],[0,0],[0,0],[0,0],[3.066,3.067],[5.466,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-6.4],[2.266,0],[1,1.267],[0,0],[0,0],[0,0],[0,-7.466],[-3.067,-3.066],[-5.667,0]],"v":[[22.4,-43.6],[22.3,-43.6],[22.3,-49],[4.4,-49],[4.4,0],[22.3,0],[22.3,-24.6],[29.4,-34.2],[34.3,-32.3],[35.8,-25.3],[35.8,0],[53.7,0],[53.7,-29.6],[49.1,-45.4],[36.3,-50]],"c":true}},"nm":"n"}],"nm":"n"}]},"fFamily":"AvertaStd-Black"},{"ch":"g","size":29,"style":"?","w":58.8,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[5.466,0],[4.033,-4.7],[0,-7.266],[-4.1,-4.566],[-6.467,0],[-3.134,3.467],[0,0],[0,0],[2.1,-1.5],[3.8,0],[2.666,0.766],[1.8,1.133],[0,0],[-7.8,0],[-4.167,4.866],[0,8.866],[0,0],[0,0]],"o":[[0,0],[-3.534,-3.933],[-5.934,0],[-4.034,4.7],[0,6.867],[4.1,4.567],[5.533,0],[0,0],[0,0],[0,2.266],[-2.1,1.5],[-2.267,0],[-2.667,-0.767],[0,0],[5.733,3],[9.733,0],[4.166,-4.867],[0,0],[0,0],[0,0]],"v":[[36.7,-44.1],[36.6,-44.1],[23.1,-50],[8.15,-42.95],[2.1,-25],[8.25,-7.85],[24.1,-1],[37.1,-6.2],[37.2,-6.2],[37.2,-1.7],[34.05,3.95],[25.2,6.2],[17.8,5.05],[11.1,2.2],[7.2,16.3],[27.5,20.8],[48.35,13.5],[54.6,-7.1],[54.6,-49],[36.7,-49]],"c":true}},"nm":"g"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,2.534],[-1.667,1.634],[-2.467,0],[-1.634,-1.666],[0,-2.533],[1.633,-1.633],[2.466,0],[1.666,1.634]],"o":[[0,-2.6],[1.666,-1.633],[2.466,0],[1.633,1.667],[0,2.534],[-1.634,1.634],[-2.467,0],[-1.667,-1.633]],"v":[[20.2,-25.4],[22.7,-31.75],[28.9,-34.2],[35.05,-31.7],[37.5,-25.4],[35.05,-19.15],[28.9,-16.7],[22.7,-19.15]],"c":true}},"nm":"g"}],"nm":"g"}]},"fFamily":"AvertaStd-Black"},{"ch":" ","size":29,"style":"?","w":22.9,"data":{},"fFamily":"AvertaStd-Black"},{"ch":"s","size":29,"style":"?","w":43.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.534,2.934],[0,4.934],[2.5,2.2],[6.066,1.934],[0,1],[-2.067,0],[-3.2,-3.4],[0,0],[6.933,0],[3.6,-2.9],[0,-5.2],[-10.4,-3],[-0.834,-0.5],[0,-0.666],[2.466,0],[4.133,3.667],[0,0],[-7.867,0]],"o":[[3.533,-2.933],[0,-4.533],[-2.5,-2.2],[-4.067,-1.333],[0,-1.333],[3.666,0],[0,0],[-5.867,-4.133],[-5.334,0],[-3.6,2.9],[0,7.6],[2.8,0.867],[0.833,0.5],[0,1.334],[-4,0],[0,0],[5.466,4.867],[6.733,0]],"v":[[36.2,-3.4],[41.5,-15.2],[37.75,-25.3],[24.9,-31.5],[18.8,-35],[21.9,-37],[32.2,-31.9],[40.5,-43.8],[21.3,-50],[7.9,-45.65],[2.5,-33.5],[18.1,-17.6],[23.55,-15.55],[24.8,-13.8],[21.1,-11.8],[8.9,-17.3],[0.8,-6.3],[20.8,1]],"c":true}},"nm":"s"}],"nm":"s"}]},"fFamily":"AvertaStd-Black"},{"ch":"l","size":29,"style":"?","w":27.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[22.7,0],[22.7,-73.6],[4.8,-73.6],[4.8,0]],"c":true}},"nm":"l"}],"nm":"l"}]},"fFamily":"AvertaStd-Black"},{"ch":"c","size":29,"style":"?","w":51.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.9,1.7],[-2.267,2.8],[0,0],[3.666,0],[1.766,1.834],[0,2.867],[-1.7,1.8],[-2.8,0],[-2.467,-2.933],[0,0],[3.8,1.734],[4.2,0],[5,-4.733],[0,-7.533],[-4.867,-4.766],[-7.4,0]],"o":[[3.9,-1.7],[0,0],[-2.2,2.667],[-2.8,0],[-1.767,-1.833],[0,-2.8],[1.7,-1.8],[3.6,0],[0,0],[-2.4,-3.266],[-3.8,-1.733],[-7.934,0],[-5,4.734],[0,7.467],[4.866,4.767],[5.133,0]],"v":[[41.55,-1.55],[50.8,-8.3],[38.6,-18.8],[29.8,-14.8],[22.95,-17.55],[20.3,-24.6],[22.85,-31.5],[29.6,-34.2],[38.7,-29.8],[50.5,-39.9],[41.2,-47.4],[29.2,-50],[9.8,-42.9],[2.3,-24.5],[9.6,-6.15],[28,1]],"c":true}},"nm":"c"}],"nm":"c"}]},"fFamily":"AvertaStd-Black"},{"ch":"o","size":29,"style":"?","w":58.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5,4.734],[0,7.534],[5,4.734],[7.866,0],[5,-4.733],[0,-7.533],[-5,-4.733],[-7.934,0]],"o":[[5,-4.733],[0,-7.533],[-5,-4.733],[-7.934,0],[-5,4.734],[0,7.534],[5,4.734],[7.866,0]],"v":[[48.5,-6.1],[56,-24.5],[48.5,-42.9],[29.2,-50],[9.8,-42.9],[2.3,-24.5],[9.8,-6.1],[29.2,1]],"c":true}},"nm":"o"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,2.867],[-1.667,1.8],[-2.6,0],[-1.634,-1.8],[0,-2.866],[1.666,-1.833],[2.533,0],[1.7,1.834]],"o":[[0,-2.866],[1.666,-1.8],[2.6,0],[1.633,1.8],[0,2.867],[-1.667,1.834],[-2.534,0],[-1.7,-1.833]],"v":[[20.3,-24.5],[22.8,-31.5],[29.2,-34.2],[35.55,-31.5],[38,-24.5],[35.5,-17.45],[29.2,-14.7],[22.85,-17.45]],"c":true}},"nm":"o"}],"nm":"o"}]},"fFamily":"AvertaStd-Black"},{"ch":"r","size":29,"style":"?","w":37.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.6,-5.2],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.7,1.734],[-3.334,0],[-1,-0.066],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-2.933],[1.7,-1.733],[1.533,0],[0,0],[0,0],[-5.534,0]],"v":[[22.4,-42.2],[22.3,-42.2],[22.3,-49],[4.4,-49],[4.4,0],[22.3,0],[22.3,-21.4],[24.85,-28.4],[32.4,-31],[36.2,-30.9],[36.2,-50],[36.1,-50]],"c":true}},"nm":"r"}],"nm":"r"}]},"fFamily":"AvertaStd-Black"},{"ch":"(","size":29,"style":"?","w":31.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,14.734],[-6.534,12.067],[0,0],[0,-18.4],[-8.534,-12.267]],"o":[[-6.534,-12.133],[0,-14.866],[0,0],[-8.534,12.2],[0,18.334],[0,0]],"v":[[29.2,10.9],[19.4,-29.4],[29.2,-69.8],[16.7,-75.3],[3.9,-29.4],[16.7,16.5]],"c":true}},"nm":"("}],"nm":"("}]},"fFamily":"AvertaStd-Black"},{"ch":"S","size":29,"style":"?","w":55.9,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.967,0.8],[-2.734,1.734],[-1.634,3.167],[0,4.267],[5,3.4],[7.4,2.8],[1.2,1.067],[0,1.267],[-3.334,0],[-5.534,-4.266],[0,0],[9.4,-0.2],[4.5,-4.066],[0,-6.066],[-1.167,-2.666],[-2.267,-1.766],[-2.167,-1.1],[-3.2,-1.066],[-1.167,-0.933],[0,-1.466],[1,-0.9],[2.066,0],[5.2,5.267],[0,0],[-10.134,0]],"o":[[2.966,-0.8],[2.733,-1.733],[1.633,-3.166],[0,-6.6],[-2.334,-1.6],[-3.4,-1.333],[-1.2,-1.066],[0,-2.8],[3.466,0],[0,0],[-7.334,-6.533],[-6.8,0.134],[-4.5,4.067],[0,3.467],[1.166,2.667],[2.266,1.767],[2.166,1.1],[3.666,1.2],[1.166,0.934],[0,1.267],[-1,0.9],[-5.6,0],[0,0],[7.4,7.6],[3.2,0]],"v":[[36.35,-0.2],[44.9,-4],[51.45,-11.35],[53.9,-22.5],[46.4,-37.5],[31.8,-44.1],[24.9,-47.7],[23.1,-51.2],[28.1,-55.4],[41.6,-49],[52.1,-62.5],[27,-72],[10.05,-65.7],[3.3,-50.5],[5.05,-41.3],[10.2,-34.65],[16.85,-30.35],[24.9,-27.1],[32.15,-23.9],[33.9,-20.3],[32.4,-17.05],[27.8,-15.7],[11.6,-23.6],[0.8,-10.4],[27.1,1]],"c":true}},"nm":"S"}],"nm":"S"}]},"fFamily":"AvertaStd-Black"},{"ch":"h","size":29,"style":"?","w":58.1,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.733,-4.333],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-4.734,0],[-1.034,-1.266],[0,-3.4],[0,0],[0,0],[0,0],[3.066,3.067],[5.466,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-6.4],[2.533,0],[1.033,1.267],[0,0],[0,0],[0,0],[0,-7.466],[-3.067,-3.066],[-5.867,0]],"v":[[22.4,-43.5],[22.3,-43.5],[22.3,-73.6],[4.4,-73.6],[4.4,0],[22.3,0],[22.3,-24.6],[29.4,-34.2],[34.75,-32.3],[36.3,-25.3],[36.3,0],[54.2,0],[54.2,-29.6],[49.6,-45.4],[36.8,-50]],"c":true}},"nm":"h"}],"nm":"h"}]},"fFamily":"AvertaStd-Black"},{"ch":"p","size":29,"style":"?","w":59.6,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.4,-4],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-5,0],[-4.234,4.734],[0,7.534],[4.133,4.767],[6.2,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[3.133,3.534],[6.733,0],[4.233,-4.733],[0,-7.466],[-4.134,-4.766],[-5.4,0]],"v":[[22.4,-44],[22.3,-44],[22.3,-49],[4.4,-49],[4.4,19.8],[22.2,19.8],[22.2,-4.3],[22.3,-4.3],[34.5,1],[50.95,-6.1],[57.3,-24.5],[51.1,-42.85],[35.6,-50]],"c":true}},"nm":"p"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[1.7,1.8],[0,3],[-1.7,1.834],[-2.667,0],[-1.634,-1.833],[0,-2.866],[1.633,-1.833],[2.533,0]],"o":[[-1.7,-1.8],[0,-2.866],[1.7,-1.833],[2.533,0],[1.633,1.834],[0,2.934],[-1.634,1.834],[-2.667,0]],"v":[[24.05,-17.3],[21.5,-24.5],[24.05,-31.55],[30.6,-34.3],[36.85,-31.55],[39.3,-24.5],[36.85,-17.35],[30.6,-14.6]],"c":true}},"nm":"p"}],"nm":"p"}]},"fFamily":"AvertaStd-Black"},{"ch":")","size":29,"style":"?","w":31.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,18.334],[8.533,12.2],[0,0],[0,-14.866],[6.533,-12.133],[0,0]],"o":[[0,-18.4],[0,0],[6.533,12.067],[0,14.734],[0,0],[8.533,-12.267]],"v":[[28.9,-29.4],[16.1,-75.3],[3.6,-69.8],[13.4,-29.4],[3.6,10.9],[16.1,16.5]],"c":true}},"nm":")"}],"nm":")"}]},"fFamily":"AvertaStd-Black"},{"ch":"\r","size":19,"style":"?","w":0,"fFamily":"AvertaStd-Black"},{"ch":"i","size":19,"style":"?","w":27.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,2.8],[1.9,1.8],[3,0],[1.933,-1.833],[0,-2.666],[-1.934,-1.833],[-3,0],[-1.9,1.834]],"o":[[0,-2.733],[-1.9,-1.8],[-3,0],[-1.934,1.834],[0,2.8],[1.933,1.834],[3,0],[1.9,-1.833]],"v":[[24,-64.2],[21.15,-71],[13.8,-73.7],[6.4,-70.95],[3.5,-64.2],[6.4,-57.25],[13.8,-54.5],[21.15,-57.25]],"c":true}},"nm":"i"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[22.7,0],[22.7,-49],[4.8,-49],[4.8,0]],"c":true}},"nm":"i"}],"nm":"i"}]},"fFamily":"AvertaStd-Black"}]}mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/svg2png/meson.build000664 001750 001750 00000000364 15164251010 034110 0ustar00ddennedyddennedy000000 000000 svg2png_src = files('svg2png.cpp', 'lodepng.cpp') executable('tvg-svg2png', svg2png_src, include_directories : headers, cpp_args : compiler_flags, install : true, link_with : thorvg_lib) src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-generator-function.cpp000664 001750 001750 00000004430 15164251010 052206 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-globals.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #include "ecma-function-object.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-generator-function.inc.h" #define BUILTIN_UNDERSCORED_ID generator_function #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup generatorfunction ECMA GeneratorFunction object built-in * @{ */ /** * Handle calling [[Call]] of built-in GeneratorFunction object * * @return constructed generator function object - if success * raised error otherwise */ ecma_value_t ecma_builtin_generator_function_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_op_create_dynamic_function (arguments_list_p, arguments_list_len, ECMA_PARSE_GENERATOR_FUNCTION); } /* ecma_builtin_generator_function_dispatch_call */ /** * Handle calling [[Construct]] of built-in GeneratorFunction object * * @return constructed generator function object - if success * raised error otherwise */ ecma_value_t ecma_builtin_generator_function_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_generator_function_dispatch_call (arguments_list_p, arguments_list_len); } /* ecma_builtin_generator_function_dispatch_construct */ /** * @} * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-objects-general.cpp000664 001750 001750 00000057271 15164251010 047076 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-objects-general.h" #include "ecma-arguments-object.h" #include "ecma-array-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-proxy-object.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaobjectsinternalops ECMA objects' operations * @{ */ /** * 'Object' object creation operation with no arguments. * * See also: ECMA-262 v5, 15.2.2.1 * * @return pointer to newly created 'Object' object */ ecma_object_t * ecma_op_create_object_object_noarg (void) { ecma_object_t *object_prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); /* 3., 4., 6., 7. */ return ecma_op_create_object_object_noarg_and_set_prototype (object_prototype_p); } /* ecma_op_create_object_object_noarg */ /** * Object creation operation with no arguments. * It sets the given prototype to the newly created object. * * See also: ECMA-262 v5, 15.2.2.1, 15.2.3.5 * * @return pointer to newly created object */ ecma_object_t * ecma_op_create_object_object_noarg_and_set_prototype (ecma_object_t *object_prototype_p) /**< pointer to prototype of the object (can be NULL) */ { ecma_object_t *obj_p = ecma_create_object (object_prototype_p, 0, ECMA_OBJECT_TYPE_GENERAL); /* * [[Class]] property of ECMA_OBJECT_TYPE_GENERAL type objects * without ECMA_INTERNAL_PROPERTY_CLASS internal property * is "Object". * * See also: ecma_object_get_class_name */ return obj_p; } /* ecma_op_create_object_object_noarg_and_set_prototype */ /** * [[Delete]] ecma general object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * ECMA-262 v5, 8.12.7 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_general_object_delete (ecma_object_t *obj_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ bool is_throw) /**< flag that controls failure handling */ { JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); JERRY_ASSERT (property_name_p != NULL); /* 1. */ ecma_property_ref_t property_ref; ecma_property_t property = ecma_op_object_get_own_property (obj_p, property_name_p, &property_ref, ECMA_PROPERTY_GET_NO_OPTIONS); /* 2. */ if (!ECMA_PROPERTY_IS_FOUND (property)) { JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_NOT_FOUND || property == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP); return ECMA_VALUE_TRUE; } /* 3. */ if (!ecma_is_property_configurable (property)) { /* 4. */ if (is_throw) { return ecma_raise_type_error (ECMA_ERR_EXPECTED_A_CONFIGURABLE_PROPERTY); } /* 5. */ return ECMA_VALUE_FALSE; } ecma_object_type_t type = ecma_get_object_type (obj_p); if (type == ECMA_OBJECT_TYPE_ARRAY && ecma_array_object_delete_property (obj_p, property_name_p)) { return ECMA_VALUE_TRUE; } /* a. */ ecma_delete_property (obj_p, property_ref.value_p); if (property & ECMA_PROPERTY_FLAG_BUILT_IN) { switch (type) { case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { if (ecma_builtin_function_is_routine (obj_p)) { ecma_builtin_routine_delete_built_in_property (obj_p, property_name_p); break; } /* FALLTHRU */ } case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { ecma_builtin_delete_built_in_property (obj_p, property_name_p); break; } case ECMA_OBJECT_TYPE_CLASS: { JERRY_ASSERT (ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_ARGUMENTS)); ecma_op_arguments_delete_built_in_property (obj_p, property_name_p); break; } case ECMA_OBJECT_TYPE_FUNCTION: { ecma_op_function_delete_built_in_property (obj_p, property_name_p); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { ecma_op_bound_function_delete_built_in_property (obj_p, property_name_p); break; } default: { JERRY_UNREACHABLE (); break; } } } /* b. */ return ECMA_VALUE_TRUE; } /* ecma_op_general_object_delete */ /** * Property invocation order during [[DefaultValue]] operation with string hint */ static const lit_magic_string_id_t to_primitive_string_hint_method_names[2] = { LIT_MAGIC_STRING_TO_STRING_UL, /**< toString operation */ LIT_MAGIC_STRING_VALUE_OF_UL, /**< valueOf operation */ }; /** * Property invocation order during [[DefaultValue]] operation with non string hint */ static const lit_magic_string_id_t to_primitive_non_string_hint_method_names[2] = { LIT_MAGIC_STRING_VALUE_OF_UL, /**< valueOf operation */ LIT_MAGIC_STRING_TO_STRING_UL, /**< toString operation */ }; /** * Hints for the ecma general object's toPrimitive operation */ static const lit_magic_string_id_t hints[3] = { LIT_MAGIC_STRING_DEFAULT, /**< "default" hint */ LIT_MAGIC_STRING_NUMBER, /**< "number" hint */ LIT_MAGIC_STRING_STRING, /**< "string" hint */ }; /** * [[DefaultValue]] ecma general object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * ECMA-262 v5, 8.12.8 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_general_object_default_value (ecma_object_t *obj_p, /**< the object */ ecma_preferred_type_hint_t hint) /**< hint on preferred result type */ { JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); ecma_value_t obj_value = ecma_make_object_value (obj_p); ecma_value_t exotic_to_prim = ecma_op_get_method_by_symbol_id (obj_value, LIT_GLOBAL_SYMBOL_TO_PRIMITIVE); if (ECMA_IS_VALUE_ERROR (exotic_to_prim)) { return exotic_to_prim; } if (!ecma_is_value_undefined (exotic_to_prim)) { ecma_object_t *call_func_p = ecma_get_object_from_value (exotic_to_prim); ecma_value_t argument = ecma_make_magic_string_value (hints[hint]); ecma_value_t result = ecma_op_function_call (call_func_p, obj_value, &argument, 1); ecma_free_value (exotic_to_prim); if (ECMA_IS_VALUE_ERROR (result) || !ecma_is_value_object (result)) { return result; } ecma_free_value (result); return ecma_raise_type_error (ECMA_ERR_RESULT_OF_DEFAULTVALUE_IS_INVALID); } ecma_free_value (exotic_to_prim); if (hint == ECMA_PREFERRED_TYPE_NO) { hint = ECMA_PREFERRED_TYPE_NUMBER; } return ecma_op_general_object_ordinary_value (obj_p, hint); } /* ecma_op_general_object_default_value */ /** * Ecma general object's OrdinaryToPrimitive operation * * See also: * ECMA-262 v6 7.1.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_general_object_ordinary_value (ecma_object_t *obj_p, /**< the object */ ecma_preferred_type_hint_t hint) /**< hint on preferred result type */ { const lit_magic_string_id_t *function_name_ids_p = (hint == ECMA_PREFERRED_TYPE_STRING ? to_primitive_string_hint_method_names : to_primitive_non_string_hint_method_names); for (uint32_t i = 0; i < 2; i++) { ecma_value_t function_value = ecma_op_object_get_by_magic_id (obj_p, function_name_ids_p[i]); if (ECMA_IS_VALUE_ERROR (function_value)) { return function_value; } ecma_value_t call_completion = ECMA_VALUE_EMPTY; if (ecma_op_is_callable (function_value)) { ecma_object_t *func_obj_p = ecma_get_object_from_value (function_value); call_completion = ecma_op_function_call (func_obj_p, ecma_make_object_value (obj_p), NULL, 0); } ecma_free_value (function_value); if (ECMA_IS_VALUE_ERROR (call_completion) || (!ecma_is_value_empty (call_completion) && !ecma_is_value_object (call_completion))) { return call_completion; } ecma_free_value (call_completion); } return ecma_raise_type_error (ECMA_ERR_RESULT_OF_DEFAULTVALUE_IS_INVALID); } /* ecma_op_general_object_ordinary_value */ /** * Special types for ecma_op_general_object_define_own_property. */ typedef enum { ECMA_OP_OBJECT_DEFINE_GENERIC = 1, /**< generic property */ ECMA_OP_OBJECT_DEFINE_ACCESSOR = 0, /**< accessor property */ ECMA_OP_OBJECT_DEFINE_DATA = ECMA_PROPERTY_FLAG_DATA /**< data property */ } ecma_op_object_define_own_property_type_t; /** * [[DefineOwnProperty]] ecma general object's operation * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * ECMA-262 v5, 8.12.9 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_general_object_define_own_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ const ecma_property_descriptor_t *property_desc_p) /**< property * descriptor */ { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (object_p)) { return ecma_proxy_object_define_own_property (object_p, property_name_p, property_desc_p); } #endif /* JERRY_BUILTIN_PROXY */ JERRY_ASSERT (object_p != NULL && !ecma_is_lexical_environment (object_p)); JERRY_ASSERT (!ecma_op_object_is_fast_array (object_p)); JERRY_ASSERT (property_name_p != NULL); uint8_t property_desc_type = ECMA_OP_OBJECT_DEFINE_GENERIC; if (property_desc_p->flags & (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE_DEFINED)) { /* A property descriptor cannot be both named data and named accessor. */ JERRY_ASSERT ((property_desc_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) != (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)); property_desc_type = ECMA_OP_OBJECT_DEFINE_DATA; } else if (property_desc_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) { JERRY_ASSERT (!(property_desc_p->flags & JERRY_PROP_IS_WRITABLE_DEFINED)); property_desc_type = ECMA_OP_OBJECT_DEFINE_ACCESSOR; } /* These three asserts ensures that a new property is created with the appropriate default flags. * E.g. if JERRY_PROP_IS_CONFIGURABLE_DEFINED is false, the newly created property must be non-configurable. */ JERRY_ASSERT ((property_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE_DEFINED) || !(property_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE)); JERRY_ASSERT ((property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE_DEFINED) || !(property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE)); JERRY_ASSERT ((property_desc_p->flags & JERRY_PROP_IS_WRITABLE_DEFINED) || !(property_desc_p->flags & JERRY_PROP_IS_WRITABLE)); /* 1. */ ecma_extended_property_ref_t ext_property_ref = { {NULL, }, NULL }; ecma_property_t current_prop; current_prop = ecma_op_object_get_own_property (object_p, property_name_p, &ext_property_ref.property_ref, ECMA_PROPERTY_GET_VALUE | ECMA_PROPERTY_GET_EXT_REFERENCE); if (!ECMA_PROPERTY_IS_FOUND (current_prop)) { JERRY_ASSERT (current_prop == ECMA_PROPERTY_TYPE_NOT_FOUND || current_prop == ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP); /* 3. */ if (!ecma_op_ordinary_object_is_extensible (object_p)) { /* 2. */ return ECMA_REJECT_WITH_FORMAT (property_desc_p->flags & JERRY_PROP_SHOULD_THROW, "Cannot define property '%', object is not extensible", ecma_make_prop_name_value (property_name_p)); } /* 4. */ uint8_t prop_attributes = (uint8_t) (property_desc_p->flags & ECMA_PROPERTY_FLAGS_MASK); if (property_desc_type != ECMA_OP_OBJECT_DEFINE_ACCESSOR) { /* a. */ JERRY_ASSERT (property_desc_type == ECMA_OP_OBJECT_DEFINE_GENERIC || property_desc_type == ECMA_OP_OBJECT_DEFINE_DATA); ecma_property_value_t *new_prop_value_p = ecma_create_named_data_property (object_p, property_name_p, prop_attributes, NULL); JERRY_ASSERT ((property_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) || ecma_is_value_undefined (property_desc_p->value)); new_prop_value_p->value = ecma_copy_value_if_not_object (property_desc_p->value); } else { /* b. */ ecma_create_named_accessor_property (object_p, property_name_p, property_desc_p->get_p, property_desc_p->set_p, prop_attributes, NULL); } return ECMA_VALUE_TRUE; } /* 6. */ const bool is_current_configurable = ecma_is_property_configurable (current_prop); /* 7. a., b. */ bool is_enumerable = (property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE) != 0; if (!is_current_configurable && ((property_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE) || ((property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE_DEFINED) && (is_enumerable != ecma_is_property_enumerable (current_prop))))) { if (ECMA_PROPERTY_IS_VIRTUAL (current_prop)) { ecma_free_value (ext_property_ref.property_ref.virtual_value); } return ecma_raise_property_redefinition (property_name_p, property_desc_p->flags); } if (ECMA_PROPERTY_IS_VIRTUAL (current_prop)) { bool writable_check_failed = (property_desc_p->flags & JERRY_PROP_IS_WRITABLE); #if JERRY_MODULE_SYSTEM if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_MODULE_NAMESPACE)) { if (JERRY_UNLIKELY (ext_property_ref.property_ref.virtual_value == ECMA_VALUE_UNINITIALIZED)) { return ecma_raise_reference_error (ECMA_ERR_LET_CONST_NOT_INITIALIZED); } if (property_desc_p->flags & JERRY_PROP_IS_WRITABLE_DEFINED) { writable_check_failed = ((property_desc_p->flags ^ current_prop) & JERRY_PROP_IS_WRITABLE) != 0; } } else { JERRY_ASSERT (!is_current_configurable && !ecma_is_property_writable (current_prop)); } #else /* !JERRY_MODULE_SYSTEM */ JERRY_ASSERT (!is_current_configurable && !ecma_is_property_writable (current_prop)); #endif /* JERRY_MODULE_SYSTEM */ ecma_value_t result = ECMA_VALUE_TRUE; if (property_desc_type == ECMA_OP_OBJECT_DEFINE_ACCESSOR || writable_check_failed || ((property_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) && !ecma_op_same_value (property_desc_p->value, ext_property_ref.property_ref.virtual_value))) { result = ecma_raise_property_redefinition (property_name_p, property_desc_p->flags); } ecma_free_value (ext_property_ref.property_ref.virtual_value); return result; } /* 8. */ if (property_desc_type == ECMA_OP_OBJECT_DEFINE_GENERIC) { /* No action required. */ } else if (JERRY_LIKELY (property_desc_type == (current_prop & ECMA_PROPERTY_FLAG_DATA))) { /* If property is configurable, there is no need for checks. */ if (JERRY_UNLIKELY (!is_current_configurable)) { if (property_desc_type == ECMA_OP_OBJECT_DEFINE_DATA) { /* 10. a. i. & ii. */ if (!ecma_is_property_writable (current_prop) && ((property_desc_p->flags & JERRY_PROP_IS_WRITABLE) || ((property_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) && !ecma_op_same_value (property_desc_p->value, ext_property_ref.property_ref.value_p->value)))) { return ecma_raise_property_redefinition (property_name_p, property_desc_p->flags); } } else { /* 11. */ /* a. */ ecma_property_value_t *value_p = ext_property_ref.property_ref.value_p; ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (value_p); jmem_cpointer_t prop_desc_getter_cp, prop_desc_setter_cp; ECMA_SET_POINTER (prop_desc_getter_cp, property_desc_p->get_p); ECMA_SET_POINTER (prop_desc_setter_cp, property_desc_p->set_p); if (((property_desc_p->flags & JERRY_PROP_IS_GET_DEFINED) && prop_desc_getter_cp != get_set_pair_p->getter_cp) || ((property_desc_p->flags & JERRY_PROP_IS_SET_DEFINED) && prop_desc_setter_cp != get_set_pair_p->setter_cp)) { /* i., ii. */ return ecma_raise_property_redefinition (property_name_p, property_desc_p->flags); } } } } else { /* 9. */ if (!is_current_configurable) { /* a. */ return ecma_raise_property_redefinition (property_name_p, property_desc_p->flags); } ecma_property_value_t *value_p = ext_property_ref.property_ref.value_p; if (property_desc_type == ECMA_OP_OBJECT_DEFINE_ACCESSOR) { JERRY_ASSERT (current_prop & ECMA_PROPERTY_FLAG_DATA); ecma_free_value_if_not_object (value_p->value); value_p->getter_setter_pair.getter_cp = JMEM_CP_NULL; value_p->getter_setter_pair.setter_cp = JMEM_CP_NULL; } else { JERRY_ASSERT (!(current_prop & ECMA_PROPERTY_FLAG_DATA)); value_p->value = ECMA_VALUE_UNDEFINED; } /* Update flags */ ecma_property_t prop_flags = *(ext_property_ref.property_p); prop_flags = (ecma_property_t) (prop_flags & ~ECMA_PROPERTY_FLAG_WRITABLE); prop_flags ^= ECMA_PROPERTY_FLAG_DATA; *(ext_property_ref.property_p) = prop_flags; } /* 12. */ if (property_desc_type == ECMA_OP_OBJECT_DEFINE_DATA) { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW_DATA (*ext_property_ref.property_p)); if (property_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) { ecma_named_data_property_assign_value (object_p, ext_property_ref.property_ref.value_p, property_desc_p->value); } if (property_desc_p->flags & JERRY_PROP_IS_WRITABLE_DEFINED) { ecma_set_property_writable_attr (ext_property_ref.property_p, (property_desc_p->flags & JERRY_PROP_IS_WRITABLE)); } } else if (property_desc_type == ECMA_OP_OBJECT_DEFINE_ACCESSOR) { JERRY_ASSERT (!(*ext_property_ref.property_p & ECMA_PROPERTY_FLAG_DATA)); if (property_desc_p->flags & JERRY_PROP_IS_GET_DEFINED) { ecma_set_named_accessor_property_getter (object_p, ext_property_ref.property_ref.value_p, property_desc_p->get_p); } if (property_desc_p->flags & JERRY_PROP_IS_SET_DEFINED) { ecma_set_named_accessor_property_setter (object_p, ext_property_ref.property_ref.value_p, property_desc_p->set_p); } } if (property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE_DEFINED) { ecma_set_property_enumerable_attr (ext_property_ref.property_p, (property_desc_p->flags & JERRY_PROP_IS_ENUMERABLE)); } if (property_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE_DEFINED) { ecma_set_property_configurable_attr (ext_property_ref.property_p, (property_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE)); } return ECMA_VALUE_TRUE; } /* ecma_op_general_object_define_own_property */ /** * The IsCompatiblePropertyDescriptor method for Proxy object internal methods * * See also: * ECMAScript v6, 9.1.6.2 * * @return bool */ bool ecma_op_is_compatible_property_descriptor (const ecma_property_descriptor_t *desc_p, /**< target descriptor */ const ecma_property_descriptor_t *current_p, /**< current descriptor */ bool is_extensible) /**< true - if target object is extensible false - otherwise */ { JERRY_ASSERT (desc_p != NULL); /* 2. */ if (current_p == NULL) { return is_extensible; } /* 3. */ if (desc_p->flags == 0) { return true; } /* 4. */ if ((current_p->flags & desc_p->flags) == desc_p->flags) { if ((current_p->flags & JERRY_PROP_IS_VALUE_DEFINED) && ecma_op_same_value (current_p->value, desc_p->value)) { return true; } if ((current_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED) && current_p->get_p == desc_p->get_p && current_p->set_p == desc_p->set_p)) { return true; } } /* 5. */ if (!(current_p->flags & JERRY_PROP_IS_CONFIGURABLE)) { if (desc_p->flags & JERRY_PROP_IS_CONFIGURABLE) { return false; } if ((desc_p->flags & JERRY_PROP_IS_ENUMERABLE_DEFINED) && ((current_p->flags & JERRY_PROP_IS_ENUMERABLE) != (desc_p->flags & JERRY_PROP_IS_ENUMERABLE))) { return false; } } const uint32_t accessor_desc_flags = (JERRY_PROP_IS_SET_DEFINED | JERRY_PROP_IS_GET_DEFINED); const uint32_t data_desc_flags = (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE_DEFINED); bool desc_is_accessor = (desc_p->flags & accessor_desc_flags) != 0; bool desc_is_data = (desc_p->flags & data_desc_flags) != 0; bool current_is_data = (current_p->flags & data_desc_flags) != 0; /* 6. */ if (!desc_is_accessor && !desc_is_data) { return true; } /* 7. */ if (current_is_data != desc_is_data) { return (current_p->flags & JERRY_PROP_IS_CONFIGURABLE) != 0; } /* 8. */ if (current_is_data) { if (!(current_p->flags & JERRY_PROP_IS_CONFIGURABLE)) { if (!(current_p->flags & JERRY_PROP_IS_WRITABLE) && (desc_p->flags & JERRY_PROP_IS_WRITABLE)) { return false; } if (!(current_p->flags & JERRY_PROP_IS_WRITABLE) && (desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED) && !ecma_op_same_value (desc_p->value, current_p->value)) { return false; } } return true; } JERRY_ASSERT ((current_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) != 0); JERRY_ASSERT ((desc_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) != 0); /* 9. */ if (!(current_p->flags & JERRY_PROP_IS_CONFIGURABLE)) { if ((desc_p->flags & JERRY_PROP_IS_SET_DEFINED) && desc_p->set_p != current_p->set_p) { return false; } if ((desc_p->flags & JERRY_PROP_IS_GET_DEFINED) && desc_p->get_p != current_p->get_p) { return false; } } return true; } /* ecma_op_is_compatible_property_descriptor */ /** * CompletePropertyDescriptor method for proxy internal method * * See also: * ECMA-262 v6, 6.2.4.5 */ void ecma_op_to_complete_property_descriptor (ecma_property_descriptor_t *desc_p) /**< target descriptor */ { /* 4. */ if (!(desc_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED))) { /* a. */ desc_p->flags |= JERRY_PROP_IS_VALUE_DEFINED; } /* 5. */ else { desc_p->flags |= (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED); } } /* ecma_op_to_complete_property_descriptor */ /** * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/uri.h000664 001750 001750 00000046450 15164251010 035630 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Tencent is pleased to support the open source community by making RapidJSON available. // // (C) Copyright IBM Corporation 2021 // // 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_URI_H_ #define RAPIDJSON_URI_H_ #include "internal/strfunc.h" #if defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(c++98-compat) #elif defined(_MSC_VER) RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // GenericUri template class GenericUri { public: typedef typename ValueType::Ch Ch; #if RAPIDJSON_HAS_STDSTRING typedef std::basic_string String; #endif //! Constructors GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { } GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { Parse(uri, len); } GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { Parse(uri, internal::StrLen(uri)); } // Use with specializations of GenericValue template GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { const Ch* u = uri.template Get(); // TypeHelper from document.h Parse(u, internal::StrLen(u)); } #if RAPIDJSON_HAS_STDSTRING GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { Parse(uri.c_str(), internal::StrLen(uri.c_str())); } #endif //! Copy constructor GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() { *this = rhs; } //! Copy constructor GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { *this = rhs; } //! Destructor. ~GenericUri() { Free(); RAPIDJSON_DELETE(ownAllocator_); } //! Assignment operator GenericUri& operator=(const GenericUri& rhs) { if (this != &rhs) { // Do not delete ownAllocator Free(); Allocate(rhs.GetStringLength()); auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength()); path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength()); query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength()); frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength()); base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength()); uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength()); CopyPart(uri_, rhs.uri_, rhs.GetStringLength()); } return *this; } //! Getters // Use with specializations of GenericValue template void Get(T& uri, Allocator& allocator) { uri.template Set(this->GetString(), allocator); // TypeHelper from document.h } const Ch* GetString() const { return uri_; } SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen(uri_); } const Ch* GetBaseString() const { return base_; } SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen(base_); } const Ch* GetSchemeString() const { return scheme_; } SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen(scheme_); } const Ch* GetAuthString() const { return auth_; } SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen(auth_); } const Ch* GetPathString() const { return path_; } SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen(path_); } const Ch* GetQueryString() const { return query_; } SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen(query_); } const Ch* GetFragString() const { return frag_; } SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen(frag_); } #if RAPIDJSON_HAS_STDSTRING static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); } static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); } static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); } static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); } static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); } static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); } static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); } #endif //! Equality operators bool operator==(const GenericUri& rhs) const { return Match(rhs, true); } bool operator!=(const GenericUri& rhs) const { return !Match(rhs, true); } bool Match(const GenericUri& uri, bool full = true) const { Ch* s1; Ch* s2; if (full) { s1 = uri_; s2 = uri.uri_; } else { s1 = base_; s2 = uri.base_; } if (s1 == s2) return true; if (s1 == 0 || s2 == 0) return false; return internal::StrCmp(s1, s2) == 0; } //! Resolve this URI against another (base) URI in accordance with URI resolution rules. // See https://tools.ietf.org/html/rfc3986 // Use for resolving an id or $ref with an in-scope id. // Returns a new GenericUri for the resolved URI. GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) { GenericUri resuri; resuri.allocator_ = allocator; // Ensure enough space for combining paths resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash if (!(GetSchemeStringLength() == 0)) { // Use all of this URI resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength()); resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); resuri.RemoveDotSegments(); } else { // Use the base scheme resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength()); if (!(GetAuthStringLength() == 0)) { // Use this auth, path, query resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); resuri.RemoveDotSegments(); } else { // Use the base auth resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength()); if (GetPathStringLength() == 0) { // Use the base path resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength()); if (GetQueryStringLength() == 0) { // Use the base query resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength()); } else { // Use this query resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); } } else { if (path_[0] == '/') { // Absolute path - use all of this path resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); resuri.RemoveDotSegments(); } else { // Relative path - append this path to base path after base path's last slash size_t pos = 0; if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) { resuri.path_[pos] = '/'; pos++; } size_t lastslashpos = baseuri.GetPathStringLength(); while (lastslashpos > 0) { if (baseuri.path_[lastslashpos - 1] == '/') break; lastslashpos--; } std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch)); pos += lastslashpos; resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength()); resuri.RemoveDotSegments(); } // Use this query resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); } } } // Always use this frag resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength()); // Re-constitute base_ and uri_ resuri.SetBase(); resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1; resuri.SetUri(); return resuri; } //! Get the allocator of this GenericUri. Allocator& GetAllocator() { return *allocator_; } private: // Allocate memory for a URI // Returns total amount allocated std::size_t Allocate(std::size_t len) { // Create own allocator if user did not supply. if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated. // Order: scheme, auth, path, query, frag, base, uri // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. size_t total = (3 * len + 7) * sizeof(Ch); scheme_ = static_cast(allocator_->Malloc(total)); *scheme_ = '\0'; auth_ = scheme_; auth_++; *auth_ = '\0'; path_ = auth_; path_++; *path_ = '\0'; query_ = path_; query_++; *query_ = '\0'; frag_ = query_; frag_++; *frag_ = '\0'; base_ = frag_; base_++; *base_ = '\0'; uri_ = base_; uri_++; *uri_ = '\0'; return total; } // Free memory for a URI void Free() { if (scheme_) { Allocator::Free(scheme_); scheme_ = 0; } } // Parse a URI into constituent scheme, authority, path, query, & fragment parts // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per // https://tools.ietf.org/html/rfc3986 void Parse(const Ch* uri, std::size_t len) { std::size_t start = 0, pos1 = 0, pos2 = 0; Allocate(len); // Look for scheme ([^:/?#]+):)? if (start < len) { while (pos1 < len) { if (uri[pos1] == ':') break; pos1++; } if (pos1 != len) { while (pos2 < len) { if (uri[pos2] == '/') break; if (uri[pos2] == '?') break; if (uri[pos2] == '#') break; pos2++; } if (pos1 < pos2) { pos1++; std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch)); scheme_[pos1] = '\0'; start = pos1; } } } // Look for auth (//([^/?#]*))? // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. auth_ = scheme_ + GetSchemeStringLength(); auth_++; *auth_ = '\0'; if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') { pos2 = start + 2; while (pos2 < len) { if (uri[pos2] == '/') break; if (uri[pos2] == '?') break; if (uri[pos2] == '#') break; pos2++; } std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); auth_[pos2 - start] = '\0'; start = pos2; } // Look for path ([^?#]*) // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. path_ = auth_ + GetAuthStringLength(); path_++; *path_ = '\0'; if (start < len) { pos2 = start; while (pos2 < len) { if (uri[pos2] == '?') break; if (uri[pos2] == '#') break; pos2++; } if (start != pos2) { std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch)); path_[pos2 - start] = '\0'; if (path_[0] == '/') RemoveDotSegments(); // absolute path - normalize start = pos2; } } // Look for query (\?([^#]*))? // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. query_ = path_ + GetPathStringLength(); query_++; *query_ = '\0'; if (start < len && uri[start] == '?') { pos2 = start + 1; while (pos2 < len) { if (uri[pos2] == '#') break; pos2++; } if (start != pos2) { std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch)); query_[pos2 - start] = '\0'; start = pos2; } } // Look for fragment (#(.*))? // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. frag_ = query_ + GetQueryStringLength(); frag_++; *frag_ = '\0'; if (start < len && uri[start] == '#') { std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); frag_[len - start] = '\0'; } // Re-constitute base_ and uri_ base_ = frag_ + GetFragStringLength() + 1; SetBase(); uri_ = base_ + GetBaseStringLength() + 1; SetUri(); } // Reconstitute base void SetBase() { Ch* next = base_; std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch)); next+= GetSchemeStringLength(); std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch)); next+= GetAuthStringLength(); std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch)); next+= GetPathStringLength(); std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch)); next+= GetQueryStringLength(); *next = '\0'; } // Reconstitute uri void SetUri() { Ch* next = uri_; std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch)); next+= GetBaseStringLength(); std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch)); next+= GetFragStringLength(); *next = '\0'; } // Copy a part from one GenericUri to another // Return the pointer to the next part to be copied to Ch* CopyPart(Ch* to, Ch* from, std::size_t len) { RAPIDJSON_ASSERT(to != 0); RAPIDJSON_ASSERT(from != 0); std::memcpy(to, from, len * sizeof(Ch)); to[len] = '\0'; Ch* next = to + len + 1; return next; } // Remove . and .. segments from the path_ member. // https://tools.ietf.org/html/rfc3986 // This is done in place as we are only removing segments. void RemoveDotSegments() { std::size_t pathlen = GetPathStringLength(); std::size_t pathpos = 0; // Position in path_ std::size_t newpos = 0; // Position in new path_ // Loop through each segment in original path_ while (pathpos < pathlen) { // Get next segment, bounded by '/' or end size_t slashpos = 0; while ((pathpos + slashpos) < pathlen) { if (path_[pathpos + slashpos] == '/') break; slashpos++; } // Check for .. and . segments if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { // Backup a .. segment in the new path_ // We expect to find a previously added slash at the end or nothing RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/'); size_t lastslashpos = newpos; // Make sure we don't go beyond the start segment if (lastslashpos > 1) { // Find the next to last slash and back up to it lastslashpos--; while (lastslashpos > 0) { if (path_[lastslashpos - 1] == '/') break; lastslashpos--; } // Set the new path_ position newpos = lastslashpos; } } else if (slashpos == 1 && path_[pathpos] == '.') { // Discard . segment, leaves new path_ unchanged } else { // Move any other kind of segment to the new path_ RAPIDJSON_ASSERT(newpos <= pathpos); std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch)); newpos += slashpos; // Add slash if not at end if ((pathpos + slashpos) < pathlen) { path_[newpos] = '/'; newpos++; } } // Move to next segment pathpos += slashpos + 1; } path_[newpos] = '\0'; } Ch* uri_; // Everything Ch* base_; // Everything except fragment Ch* scheme_; // Includes the : Ch* auth_; // Includes the // Ch* path_; // Absolute if starts with / Ch* query_; // Includes the ? Ch* frag_; // Includes the # Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. Allocator* ownAllocator_; //!< Allocator owned by this Uri. }; //! GenericUri for Value (UTF-8, default allocator). typedef GenericUri Uri; RAPIDJSON_NAMESPACE_END #if defined(__clang__) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_URI_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/segment.lot000664 001750 001750 00000066713 15164251010 034405 0ustar00ddennedyddennedy000000 000000 {"v":"5.1.20","fr":60,"ip":0,"op":120,"w":256,"h":256,"nm":"square wheel","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"roda","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":[810]},{"t":119}],"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":[-24,231.5,0],"e":[-21.87,229.6,0],"to":[0.35499998927116,-0.31666666269302,0],"ti":[-0.74666666984558,0.58666664361954,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":1,"s":[-21.87,229.6,0],"e":[-19.52,227.98,0],"to":[0.74666666984558,-0.58666664361954,0],"ti":[-0.8116666674614,0.49333333969116,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":2,"s":[-19.52,227.98,0],"e":[-17,226.64,0],"to":[0.8116666674614,-0.49333333969116,0],"ti":[-0.86333334445953,0.39500001072884,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":3,"s":[-17,226.64,0],"e":[-14.34,225.61,0],"to":[0.86333334445953,-0.39500001072884,0],"ti":[-0.90499997138977,0.28999999165535,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":4,"s":[-14.34,225.61,0],"e":[-11.57,224.9,0],"to":[0.90499997138977,-0.28999999165535,0],"ti":[-0.93333333730698,0.18166667222977,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[-11.57,224.9,0],"e":[-8.74,224.52,0],"to":[0.93333333730698,-0.18166667222977,0],"ti":[-0.94666665792465,0.07000000029802,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[-8.74,224.52,0],"e":[-5.89,224.48,0],"to":[0.94666665792465,-0.07000000029802,0],"ti":[-0.94833332300186,-0.04333333298564,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[-5.89,224.48,0],"e":[-3.05,224.78,0],"to":[0.94833332300186,0.04333333298564,0],"ti":[-0.9366666674614,-0.1566666662693,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[-3.05,224.78,0],"e":[-0.27,225.42,0],"to":[0.9366666674614,0.1566666662693,0],"ti":[-0.91166669130325,-0.26666668057442,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":9,"s":[-0.27,225.42,0],"e":[2.42,226.38,0],"to":[0.91166669130325,0.26666668057442,0],"ti":[-0.87333333492279,-0.37166666984558,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[2.42,226.38,0],"e":[4.97,227.65,0],"to":[0.87333333492279,0.37166666984558,0],"ti":[-0.82333332300186,-0.47333332896233,0]},{"i":{"x":0.833,"y":0.832},"o":{"x":0.167,"y":0.166},"n":"0p833_0p832_0p167_0p166","t":11,"s":[4.97,227.65,0],"e":[7.36,229.22,0],"to":[0.82333332300186,0.47333332896233,0],"ti":[-0.76333332061768,-0.56833332777023,0]},{"i":{"x":0.833,"y":0.847},"o":{"x":0.167,"y":0.166},"n":"0p833_0p847_0p167_0p166","t":12,"s":[7.36,229.22,0],"e":[9.55,231.06,0],"to":[0.76333332061768,0.56833332777023,0],"ti":[-0.71333330869675,-0.12999999523163,0]},{"i":{"x":0.833,"y":0.818},"o":{"x":0.167,"y":0.184},"n":"0p833_0p818_0p167_0p184","t":13,"s":[9.55,231.06,0],"e":[11.64,230,0],"to":[0.71333330869675,0.12999999523163,0],"ti":[-0.7316666841507,0.45833334326744,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.153},"n":"0p833_0p833_0p167_0p153","t":14,"s":[11.64,230,0],"e":[13.94,228.31,0],"to":[0.7316666841507,-0.45833334326744,0],"ti":[-0.79833334684372,0.51499998569489,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":15,"s":[13.94,228.31,0],"e":[16.43,226.91,0],"to":[0.79833334684372,-0.51499998569489,0],"ti":[-0.85333335399628,0.41666665673256,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[16.43,226.91,0],"e":[19.06,225.81,0],"to":[0.85333335399628,-0.41666665673256,0],"ti":[-0.89666664600372,0.3133333325386,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":17,"s":[19.06,225.81,0],"e":[21.81,225.03,0],"to":[0.89666664600372,-0.3133333325386,0],"ti":[-0.92666667699814,0.20499999821186,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18,"s":[21.81,225.03,0],"e":[24.62,224.58,0],"to":[0.92666667699814,-0.20499999821186,0],"ti":[-0.94499999284744,0.09499999880791,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":19,"s":[24.62,224.58,0],"e":[27.48,224.46,0],"to":[0.94499999284744,-0.09499999880791,0],"ti":[-0.94999998807907,-0.01833333261311,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20,"s":[27.48,224.46,0],"e":[30.32,224.69,0],"to":[0.94999998807907,0.01833333261311,0],"ti":[-0.93999999761581,-0.13166666030884,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":21,"s":[30.32,224.69,0],"e":[33.12,225.25,0],"to":[0.93999999761581,0.13166666030884,0],"ti":[-0.91833335161209,-0.24166665971279,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[33.12,225.25,0],"e":[35.83,226.14,0],"to":[0.91833335161209,0.24166665971279,0],"ti":[-0.88333332538605,-0.34833332896233,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":23,"s":[35.83,226.14,0],"e":[38.42,227.34,0],"to":[0.88333332538605,0.34833332896233,0],"ti":[-0.83666664361954,-0.45166665315628,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":24,"s":[38.42,227.34,0],"e":[40.85,228.85,0],"to":[0.83666664361954,0.45166665315628,0],"ti":[-0.77666664123535,-0.54833334684372,0]},{"i":{"x":0.833,"y":0.854},"o":{"x":0.167,"y":0.167},"n":"0p833_0p854_0p167_0p167","t":25,"s":[40.85,228.85,0],"e":[43.08,230.63,0],"to":[0.77666664123535,0.54833334684372,0],"ti":[-0.71833330392838,-0.26166665554047,0]},{"i":{"x":0.833,"y":0.806},"o":{"x":0.167,"y":0.194},"n":"0p833_0p806_0p167_0p194","t":26,"s":[43.08,230.63,0],"e":[45.16,230.42,0],"to":[0.71833330392838,0.26166665554047,0],"ti":[-0.72166669368744,0.32666665315628,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.146},"n":"0p833_0p833_0p167_0p146","t":27,"s":[45.16,230.42,0],"e":[47.41,228.67,0],"to":[0.72166669368744,-0.32666665315628,0],"ti":[-0.78333336114883,0.53666669130325,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":28,"s":[47.41,228.67,0],"e":[49.86,227.2,0],"to":[0.78333336114883,-0.53666669130325,0],"ti":[-0.84166663885117,0.43999999761581,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":29,"s":[49.86,227.2,0],"e":[52.46,226.03,0],"to":[0.84166663885117,-0.43999999761581,0],"ti":[-0.88833332061768,0.33833333849907,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":30,"s":[52.46,226.03,0],"e":[55.19,225.17,0],"to":[0.88833332061768,-0.33833333849907,0],"ti":[-0.92166668176651,0.23000000417233,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":31,"s":[55.19,225.17,0],"e":[57.99,224.65,0],"to":[0.92166668176651,-0.23000000417233,0],"ti":[-0.94166666269302,0.11833333224058,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":32,"s":[57.99,224.65,0],"e":[60.84,224.46,0],"to":[0.94166666269302,-0.11833333224058,0],"ti":[-0.94999998807907,0.0066666668281,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":33,"s":[60.84,224.46,0],"e":[63.69,224.61,0],"to":[0.94999998807907,-0.0066666668281,0],"ti":[-0.94333332777023,-0.10666666924953,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":34,"s":[63.69,224.61,0],"e":[66.5,225.1,0],"to":[0.94333332777023,0.10666666924953,0],"ti":[-0.92500001192093,-0.21666666865349,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":35,"s":[66.5,225.1,0],"e":[69.24,225.91,0],"to":[0.92500001192093,0.21666666865349,0],"ti":[-0.8933333158493,-0.32499998807907,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":36,"s":[69.24,225.91,0],"e":[71.86,227.05,0],"to":[0.8933333158493,0.32499998807907,0],"ti":[-0.84666669368744,-0.43000000715256,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":37,"s":[71.86,227.05,0],"e":[74.32,228.49,0],"to":[0.84666669368744,0.43000000715256,0],"ti":[-0.79000002145767,-0.52666664123535,0]},{"i":{"x":0.833,"y":0.851},"o":{"x":0.167,"y":0.166},"n":"0p833_0p851_0p167_0p166","t":38,"s":[74.32,228.49,0],"e":[76.6,230.21,0],"to":[0.79000002145767,0.52666664123535,0],"ti":[-0.72833335399628,-0.39166668057442,0]},{"i":{"x":0.833,"y":0.81},"o":{"x":0.167,"y":0.19},"n":"0p833_0p81_0p167_0p19","t":39,"s":[76.6,230.21,0],"e":[78.69,230.84,0],"to":[0.72833335399628,0.39166668057442,0],"ti":[-0.71499997377396,0.19666667282581,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.148},"n":"0p833_0p834_0p167_0p148","t":40,"s":[78.69,230.84,0],"e":[80.89,229.03,0],"to":[0.71499997377396,-0.19666667282581,0],"ti":[-0.7683333158493,0.55666667222977,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":41,"s":[80.89,229.03,0],"e":[83.3,227.5,0],"to":[0.7683333158493,-0.55666667222977,0],"ti":[-0.8299999833107,0.46166667342186,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[83.3,227.5,0],"e":[85.87,226.26,0],"to":[0.8299999833107,-0.46166667342186,0],"ti":[-0.87833333015442,0.36166667938232,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":43,"s":[85.87,226.26,0],"e":[88.57,225.33,0],"to":[0.87833333015442,-0.36166667938232,0],"ti":[-0.91500002145767,0.25499999523163,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":44,"s":[88.57,225.33,0],"e":[91.36,224.73,0],"to":[0.91500002145767,-0.25499999523163,0],"ti":[-0.93999999761581,0.14333333075047,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":45,"s":[91.36,224.73,0],"e":[94.21,224.47,0],"to":[0.93999999761581,-0.14333333075047,0],"ti":[-0.94999998807907,0.02999999932945,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":46,"s":[94.21,224.47,0],"e":[97.06,224.55,0],"to":[0.94999998807907,-0.02999999932945,0],"ti":[-0.94499999284744,-0.08166666328907,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":47,"s":[97.06,224.55,0],"e":[99.88,224.96,0],"to":[0.94499999284744,0.08166666328907,0],"ti":[-0.93000000715256,-0.19166666269302,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":48,"s":[99.88,224.96,0],"e":[102.64,225.7,0],"to":[0.93000000715256,0.19166666269302,0],"ti":[-0.90166664123535,-0.30166667699814,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":49,"s":[102.64,225.7,0],"e":[105.29,226.77,0],"to":[0.90166664123535,0.30166667699814,0],"ti":[-0.85833334922791,-0.4066666662693,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":50,"s":[105.29,226.77,0],"e":[107.79,228.14,0],"to":[0.85833334922791,0.4066666662693,0],"ti":[-0.80333334207535,-0.50499999523163,0]},{"i":{"x":0.833,"y":0.839},"o":{"x":0.167,"y":0.167},"n":"0p833_0p839_0p167_0p167","t":51,"s":[107.79,228.14,0],"e":[110.11,229.8,0],"to":[0.80333334207535,0.50499999523163,0],"ti":[-0.74000000953674,-0.52333331108093,0]},{"i":{"x":0.833,"y":0.825},"o":{"x":0.167,"y":0.173},"n":"0p833_0p825_0p167_0p173","t":52,"s":[110.11,229.8,0],"e":[112.23,231.28,0],"to":[0.74000000953674,0.52333331108093,0],"ti":[-0.71333330869675,0.06499999761581,0]},{"i":{"x":0.833,"y":0.835},"o":{"x":0.167,"y":0.159},"n":"0p833_0p835_0p167_0p159","t":53,"s":[112.23,231.28,0],"e":[114.39,229.41,0],"to":[0.71333330869675,-0.06499999761581,0],"ti":[-0.75333333015442,0.57833331823349,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.168},"n":"0p833_0p833_0p167_0p168","t":54,"s":[114.39,229.41,0],"e":[116.75,227.81,0],"to":[0.75333333015442,-0.57833331823349,0],"ti":[-0.81666666269302,0.48333331942558,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":55,"s":[116.75,227.81,0],"e":[119.29,226.51,0],"to":[0.81666666269302,-0.48333331942558,0],"ti":[-0.86833333969116,0.38333332538605,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":56,"s":[119.29,226.51,0],"e":[121.96,225.51,0],"to":[0.86833333969116,-0.38333332538605,0],"ti":[-0.90833336114883,0.27833333611488,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":57,"s":[121.96,225.51,0],"e":[124.74,224.84,0],"to":[0.90833336114883,-0.27833333611488,0],"ti":[-0.93500000238419,0.16833333671093,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":58,"s":[124.74,224.84,0],"e":[127.57,224.5,0],"to":[0.93500000238419,-0.16833333671093,0],"ti":[-0.94833332300186,0.05666666850448,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":59,"s":[127.57,224.5,0],"e":[130.43,224.5,0],"to":[0.94833332300186,-0.05666666850448,0],"ti":[-0.94833332300186,-0.05666666850448,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":60,"s":[130.43,224.5,0],"e":[133.26,224.84,0],"to":[0.94833332300186,0.05666666850448,0],"ti":[-0.93500000238419,-0.16833333671093,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":61,"s":[133.26,224.84,0],"e":[136.04,225.51,0],"to":[0.93500000238419,0.16833333671093,0],"ti":[-0.90833336114883,-0.27833333611488,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":62,"s":[136.04,225.51,0],"e":[138.71,226.51,0],"to":[0.90833336114883,0.27833333611488,0],"ti":[-0.86833333969116,-0.38333332538605,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":63,"s":[138.71,226.51,0],"e":[141.25,227.81,0],"to":[0.86833333969116,0.38333332538605,0],"ti":[-0.81666666269302,-0.48333331942558,0]},{"i":{"x":0.833,"y":0.832},"o":{"x":0.167,"y":0.167},"n":"0p833_0p832_0p167_0p167","t":64,"s":[141.25,227.81,0],"e":[143.61,229.41,0],"to":[0.81666666269302,0.48333331942558,0],"ti":[-0.75333333015442,-0.57833331823349,0]},{"i":{"x":0.833,"y":0.841},"o":{"x":0.167,"y":0.165},"n":"0p833_0p841_0p167_0p165","t":65,"s":[143.61,229.41,0],"e":[145.77,231.28,0],"to":[0.75333333015442,0.57833331823349,0],"ti":[-0.71333330869675,-0.06499999761581,0]},{"i":{"x":0.833,"y":0.827},"o":{"x":0.167,"y":0.175},"n":"0p833_0p827_0p167_0p175","t":66,"s":[145.77,231.28,0],"e":[147.89,229.8,0],"to":[0.71333330869675,0.06499999761581,0],"ti":[-0.74000000953674,0.52333331108093,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.161},"n":"0p833_0p833_0p167_0p161","t":67,"s":[147.89,229.8,0],"e":[150.21,228.14,0],"to":[0.74000000953674,-0.52333331108093,0],"ti":[-0.80333334207535,0.50499999523163,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":68,"s":[150.21,228.14,0],"e":[152.71,226.77,0],"to":[0.80333334207535,-0.50499999523163,0],"ti":[-0.85833334922791,0.4066666662693,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":69,"s":[152.71,226.77,0],"e":[155.36,225.7,0],"to":[0.85833334922791,-0.4066666662693,0],"ti":[-0.90166664123535,0.30166667699814,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":70,"s":[155.36,225.7,0],"e":[158.12,224.96,0],"to":[0.90166664123535,-0.30166667699814,0],"ti":[-0.93000000715256,0.19166666269302,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":71,"s":[158.12,224.96,0],"e":[160.94,224.55,0],"to":[0.93000000715256,-0.19166666269302,0],"ti":[-0.94499999284744,0.08166666328907,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":72,"s":[160.94,224.55,0],"e":[163.79,224.47,0],"to":[0.94499999284744,-0.08166666328907,0],"ti":[-0.94999998807907,-0.02999999932945,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":73,"s":[163.79,224.47,0],"e":[166.64,224.73,0],"to":[0.94999998807907,0.02999999932945,0],"ti":[-0.93999999761581,-0.14333333075047,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":74,"s":[166.64,224.73,0],"e":[169.43,225.33,0],"to":[0.93999999761581,0.14333333075047,0],"ti":[-0.91500002145767,-0.25499999523163,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":75,"s":[169.43,225.33,0],"e":[172.13,226.26,0],"to":[0.91500002145767,0.25499999523163,0],"ti":[-0.87833333015442,-0.36166667938232,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":76,"s":[172.13,226.26,0],"e":[174.7,227.5,0],"to":[0.87833333015442,0.36166667938232,0],"ti":[-0.8299999833107,-0.46166667342186,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":77,"s":[174.7,227.5,0],"e":[177.11,229.03,0],"to":[0.8299999833107,0.46166667342186,0],"ti":[-0.7683333158493,-0.55666667222977,0]},{"i":{"x":0.833,"y":0.852},"o":{"x":0.167,"y":0.166},"n":"0p833_0p852_0p167_0p166","t":78,"s":[177.11,229.03,0],"e":[179.31,230.84,0],"to":[0.7683333158493,0.55666667222977,0],"ti":[-0.71499997377396,-0.19666667282581,0]},{"i":{"x":0.833,"y":0.81},"o":{"x":0.167,"y":0.19},"n":"0p833_0p81_0p167_0p19","t":79,"s":[179.31,230.84,0],"e":[181.4,230.21,0],"to":[0.71499997377396,0.19666667282581,0],"ti":[-0.72833335399628,0.39166668057442,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.149},"n":"0p833_0p834_0p167_0p149","t":80,"s":[181.4,230.21,0],"e":[183.68,228.49,0],"to":[0.72833335399628,-0.39166668057442,0],"ti":[-0.79000002145767,0.52666664123535,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":81,"s":[183.68,228.49,0],"e":[186.14,227.05,0],"to":[0.79000002145767,-0.52666664123535,0],"ti":[-0.84666669368744,0.43000000715256,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":82,"s":[186.14,227.05,0],"e":[188.76,225.91,0],"to":[0.84666669368744,-0.43000000715256,0],"ti":[-0.8933333158493,0.32499998807907,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":83,"s":[188.76,225.91,0],"e":[191.5,225.1,0],"to":[0.8933333158493,-0.32499998807907,0],"ti":[-0.92500001192093,0.21666666865349,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":84,"s":[191.5,225.1,0],"e":[194.31,224.61,0],"to":[0.92500001192093,-0.21666666865349,0],"ti":[-0.94333332777023,0.10666666924953,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":85,"s":[194.31,224.61,0],"e":[197.16,224.46,0],"to":[0.94333332777023,-0.10666666924953,0],"ti":[-0.94999998807907,-0.0066666668281,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":86,"s":[197.16,224.46,0],"e":[200.01,224.65,0],"to":[0.94999998807907,0.0066666668281,0],"ti":[-0.94166666269302,-0.11833333224058,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":87,"s":[200.01,224.65,0],"e":[202.81,225.17,0],"to":[0.94166666269302,0.11833333224058,0],"ti":[-0.92166668176651,-0.23000000417233,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":88,"s":[202.81,225.17,0],"e":[205.54,226.03,0],"to":[0.92166668176651,0.23000000417233,0],"ti":[-0.88833332061768,-0.33833333849907,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":89,"s":[205.54,226.03,0],"e":[208.14,227.2,0],"to":[0.88833332061768,0.33833333849907,0],"ti":[-0.84166663885117,-0.43999999761581,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":90,"s":[208.14,227.2,0],"e":[210.59,228.67,0],"to":[0.84166663885117,0.43999999761581,0],"ti":[-0.78333336114883,-0.53666669130325,0]},{"i":{"x":0.833,"y":0.854},"o":{"x":0.167,"y":0.167},"n":"0p833_0p854_0p167_0p167","t":91,"s":[210.59,228.67,0],"e":[212.84,230.42,0],"to":[0.78333336114883,0.53666669130325,0],"ti":[-0.72166669368744,-0.32666665315628,0]},{"i":{"x":0.833,"y":0.806},"o":{"x":0.167,"y":0.194},"n":"0p833_0p806_0p167_0p194","t":92,"s":[212.84,230.42,0],"e":[214.92,230.63,0],"to":[0.72166669368744,0.32666665315628,0],"ti":[-0.71833330392838,0.26166665554047,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.146},"n":"0p833_0p833_0p167_0p146","t":93,"s":[214.92,230.63,0],"e":[217.15,228.85,0],"to":[0.71833330392838,-0.26166665554047,0],"ti":[-0.77666664123535,0.54833334684372,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":94,"s":[217.15,228.85,0],"e":[219.58,227.34,0],"to":[0.77666664123535,-0.54833334684372,0],"ti":[-0.83666664361954,0.45166665315628,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":95,"s":[219.58,227.34,0],"e":[222.17,226.14,0],"to":[0.83666664361954,-0.45166665315628,0],"ti":[-0.88333332538605,0.34833332896233,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":96,"s":[222.17,226.14,0],"e":[224.88,225.25,0],"to":[0.88333332538605,-0.34833332896233,0],"ti":[-0.91833335161209,0.24166665971279,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":97,"s":[224.88,225.25,0],"e":[227.68,224.69,0],"to":[0.91833335161209,-0.24166665971279,0],"ti":[-0.93999999761581,0.13166666030884,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":98,"s":[227.68,224.69,0],"e":[230.52,224.46,0],"to":[0.93999999761581,-0.13166666030884,0],"ti":[-0.94999998807907,0.01833333261311,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":99,"s":[230.52,224.46,0],"e":[233.38,224.58,0],"to":[0.94999998807907,-0.01833333261311,0],"ti":[-0.94499999284744,-0.09499999880791,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":100,"s":[233.38,224.58,0],"e":[236.19,225.03,0],"to":[0.94499999284744,0.09499999880791,0],"ti":[-0.92666667699814,-0.20499999821186,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.166},"n":"0p833_0p834_0p167_0p166","t":101,"s":[236.19,225.03,0],"e":[238.94,225.81,0],"to":[0.92666667699814,0.20499999821186,0],"ti":[-0.89666664600372,-0.3133333325386,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":102,"s":[238.94,225.81,0],"e":[241.57,226.91,0],"to":[0.89666664600372,0.3133333325386,0],"ti":[-0.85333335399628,-0.41666665673256,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":103,"s":[241.57,226.91,0],"e":[244.06,228.31,0],"to":[0.85333335399628,0.41666665673256,0],"ti":[-0.79833334684372,-0.51499998569489,0]},{"i":{"x":0.833,"y":0.847},"o":{"x":0.167,"y":0.167},"n":"0p833_0p847_0p167_0p167","t":104,"s":[244.06,228.31,0],"e":[246.36,230,0],"to":[0.79833334684372,0.51499998569489,0],"ti":[-0.7316666841507,-0.45833334326744,0]},{"i":{"x":0.833,"y":0.816},"o":{"x":0.167,"y":0.182},"n":"0p833_0p816_0p167_0p182","t":105,"s":[246.36,230,0],"e":[248.45,231.06,0],"to":[0.7316666841507,0.45833334326744,0],"ti":[-0.71333330869675,0.12999999523163,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.153},"n":"0p833_0p834_0p167_0p153","t":106,"s":[248.45,231.06,0],"e":[250.64,229.22,0],"to":[0.71333330869675,-0.12999999523163,0],"ti":[-0.76333332061768,0.56833332777023,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.168},"n":"0p833_0p834_0p167_0p168","t":107,"s":[250.64,229.22,0],"e":[253.03,227.65,0],"to":[0.76333332061768,-0.56833332777023,0],"ti":[-0.82333332300186,0.47333332896233,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":108,"s":[253.03,227.65,0],"e":[255.58,226.38,0],"to":[0.82333332300186,-0.47333332896233,0],"ti":[-0.87333333492279,0.37166666984558,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"n":"0p833_0p833_0p167_0p166","t":109,"s":[255.58,226.38,0],"e":[258.27,225.42,0],"to":[0.87333333492279,-0.37166666984558,0],"ti":[-0.91166669130325,0.26666668057442,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":110,"s":[258.27,225.42,0],"e":[261.05,224.78,0],"to":[0.91166669130325,-0.26666668057442,0],"ti":[-0.9366666674614,0.1566666662693,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":111,"s":[261.05,224.78,0],"e":[263.89,224.48,0],"to":[0.9366666674614,-0.1566666662693,0],"ti":[-0.94833332300186,0.04333333298564,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":112,"s":[263.89,224.48,0],"e":[266.74,224.52,0],"to":[0.94833332300186,-0.04333333298564,0],"ti":[-0.94666665792465,-0.07000000029802,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":113,"s":[266.74,224.52,0],"e":[269.57,224.9,0],"to":[0.94666665792465,0.07000000029802,0],"ti":[-0.93333333730698,-0.18166667222977,0]},{"i":{"x":0.833,"y":0.834},"o":{"x":0.167,"y":0.167},"n":"0p833_0p834_0p167_0p167","t":114,"s":[269.57,224.9,0],"e":[272.34,225.61,0],"to":[0.93333333730698,0.18166667222977,0],"ti":[-0.90499997138977,-0.28999999165535,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":115,"s":[272.34,225.61,0],"e":[275,226.64,0],"to":[0.90499997138977,0.28999999165535,0],"ti":[-0.86333334445953,-0.39500001072884,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":116,"s":[275,226.64,0],"e":[277.52,227.98,0],"to":[0.86333334445953,0.39500001072884,0],"ti":[-0.8116666674614,-0.49333333969116,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":117,"s":[277.52,227.98,0],"e":[279.87,229.6,0],"to":[0.8116666674614,0.49333333969116,0],"ti":[-0.74666666984558,-0.58666664361954,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":118,"s":[279.87,229.6,0],"e":[282,231.5,0],"to":[0.74666666984558,0.58666664361954,0],"ti":[-0.35499998927116,-0.31666666269302,0]},{"t":119}],"ix":2},"a":{"a":0,"k":[-96,98.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[34,34],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.243137000589,0.082353001015,0.768627989526,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":[-96,98.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":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"_square wheel","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[128,128,0],"ix":2},"a":{"a":0,"k":[128,128,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":256,"h":256,"ip":0,"op":120,"st":0,"bm":0}],"markers":[{"tm":0,"cm":"sectionA","dr":22},{"tm":22,"cm":"sectionB","dr":11},{"tm":33,"cm":"sectionC","dr":30}]}thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-evalerror.cpp000664 001750 001750 00000005325 15164251010 050402 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-evalerror.inc.h" #define BUILTIN_UNDERSCORED_ID eval_error #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup evalerror ECMA EvalError object built-in * @{ */ /** * Handle calling [[Call]] of built-in EvalError object * * @return ecma value */ ecma_value_t ecma_builtin_eval_error_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_helper_error_dispatch_call (JERRY_ERROR_EVAL, arguments_list_p, arguments_list_len); } /* ecma_builtin_eval_error_dispatch_call */ /** * Handle calling [[Construct]] of built-in EvalError object * * @return ecma value */ ecma_value_t ecma_builtin_eval_error_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_EVAL_ERROR_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_eval_error_dispatch_call (arguments_list_p, arguments_list_len); if (!ECMA_IS_VALUE_ERROR (result)) { ecma_object_t *object_p = ecma_get_object_from_value (result); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, proto_p); } ecma_deref_object (proto_p); return result; } /* ecma_builtin_eval_error_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ERRORS */ jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-bigint64array-prototype.cpp000664 001750 001750 00000002331 15164251010 055307 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_BUILTIN_BIGINT #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-bigint64array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID bigint64array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup bigint64arrayprototype ECMA BigInt64Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_BIGINT */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint16array.cpp000664 001750 001750 00000004362 15164251010 052752 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-uint16array.inc.h" #define BUILTIN_UNDERSCORED_ID uint16array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup uint16array ECMA Uint16Array object built-in * @{ */ /** * Handle calling [[Call]] of Uint16Array * * @return ecma value */ ecma_value_t ecma_builtin_uint16array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_UINT16_ARRAY_REQUIRES_NEW); } /* ecma_builtin_uint16array_dispatch_call */ /** * Handle calling [[Construct]] of Uint16Array * * @return ecma value */ ecma_value_t ecma_builtin_uint16array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_UINT16_ARRAY); } /* ecma_builtin_uint16array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/writer.h000664 001750 001750 00000064350 15164251010 036344 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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/clzll.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 // placement new #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) #include #pragma intrinsic(_BitScanForward) #endif #ifdef RAPIDJSON_SSE42 #include #elif defined(RAPIDJSON_SSE2) #include #elif defined(RAPIDJSON_NEON) #include #endif #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(unreachable-code) RAPIDJSON_DIAG_OFF(c++98-compat) #elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #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 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 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& str) { return String(str.data(), SizeType(str.size())); } #endif bool StartObject() { Prefix(kObjectType); new (level_stack_.template Push()) 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& 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()->inArray); // currently inside an Array, not Object RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value level_stack_.template Pop(1); return EndValue(WriteEndObject()); } bool StartArray() { Prefix(kArrayType); new (level_stack_.template Push()) Level(true); return WriteStartArray(); } bool EndArray(SizeType elementCount = 0) { (void)elementCount; RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); level_stack_.template Pop(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(); } static const size_t kDefaultLevelDepth = 32; 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 }; 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(end - buffer)); for (const char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteUint(unsigned u) { char buffer[10]; const char* end = internal::u32toa(u, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteInt64(int64_t i64) { char buffer[21]; const char* end = internal::i64toa(i64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteUint64(uint64_t u64) { char buffer[20]; char* end = internal::u64toa(u64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*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(end - buffer)); for (char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*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 is(str); while (ScanWriteUnescapedString(is, length)) { const Ch c = is.Peek(); if (!TargetEncoding::supportUnicode && static_cast(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(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { is.Take(); PutUnsafe(*os_, '\\'); PutUnsafe(*os_, static_cast(escape[static_cast(c)])); if (escape[static_cast(c)] == 'u') { PutUnsafe(*os_, '0'); PutUnsafe(*os_, '0'); PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); } } else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? Transcoder::Validate(is, *os_) : Transcoder::TranscodeUnsafe(is, *os_)))) return false; } PutUnsafe(*os_, '\"'); return true; } bool ScanWriteUnescapedString(GenericStringStream& 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); GenericStringStream is(json); while (RAPIDJSON_LIKELY(is.Tell() < length)) { RAPIDJSON_ASSERT(is.Peek() != '\0'); if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? Transcoder::Validate(is, *os_) : Transcoder::TranscodeUnsafe(is, *os_)))) return false; } 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(); 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 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::WriteInt(int i) { char *buffer = os_->Push(11); const char* end = internal::i32toa(i, buffer); os_->Pop(static_cast(11 - (end - buffer))); return true; } template<> inline bool Writer::WriteUint(unsigned u) { char *buffer = os_->Push(10); const char* end = internal::u32toa(u, buffer); os_->Pop(static_cast(10 - (end - buffer))); return true; } template<> inline bool Writer::WriteInt64(int64_t i64) { char *buffer = os_->Push(21); const char* end = internal::i64toa(i64, buffer); os_->Pop(static_cast(21 - (end - buffer))); return true; } template<> inline bool Writer::WriteUint64(uint64_t u) { char *buffer = os_->Push(20); const char* end = internal::u64toa(u, buffer); os_->Pop(static_cast(20 - (end - buffer))); return true; } template<> inline bool Writer::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(25 - (end - buffer))); return true; } #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) template<> inline bool Writer::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((reinterpret_cast(p) + 15) & static_cast(~15)); const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~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(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); for (; p != endAligned; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(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(_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(__builtin_ffs(r) - 1); #endif char* q = reinterpret_cast(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::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((reinterpret_cast(p) + 15) & static_cast(~15)); const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~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(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(vreinterpretq_u64_u8(x), 0); // extract uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract SizeType len = 0; bool escaped = false; if (low == 0) { if (high != 0) { uint32_t lz = internal::clzll(high); len = 8 + (lz >> 3); escaped = true; } } else { uint32_t lz = internal::clzll(low); len = lz >> 3; escaped = true; } if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped char* q = reinterpret_cast(os_->PushUnsafe(len)); for (size_t i = 0; i < len; i++) q[i] = p[i]; p += len; break; } vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); } is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } #endif // RAPIDJSON_NEON RAPIDJSON_NAMESPACE_END #if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_RAPIDJSON_H_ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-number.cpp000664 001750 001750 00000014673 15164251010 047677 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include "ecma-alloc.h" #include "ecma-bigint.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "ecma-number-object.h" #include "ecma-objects.h" #include "jrt.h" #if JERRY_BUILTIN_NUMBER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_NUMBER_OBJECT_ROUTINE_START = 0, ECMA_NUMBER_OBJECT_ROUTINE_IS_FINITE, ECMA_NUMBER_OBJECT_ROUTINE_IS_NAN, ECMA_NUMBER_OBJECT_ROUTINE_IS_INTEGER, ECMA_NUMBER_OBJECT_ROUTINE_IS_SAFE_INTEGER }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-number.inc.h" #define BUILTIN_UNDERSCORED_ID number #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup number ECMA Number object built-in * @{ */ /** * Handle calling [[Call]] of built-in Number object * * @return ecma value */ ecma_value_t ecma_builtin_number_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_value_t ret_value = ECMA_VALUE_EMPTY; if (arguments_list_len == 0) { ret_value = ecma_make_integer_value (0); } else { ecma_number_t num; ret_value = ecma_op_to_numeric (arguments_list_p[0], &num, ECMA_TO_NUMERIC_ALLOW_BIGINT); if (ECMA_IS_VALUE_ERROR (ret_value)) { return ret_value; } #if JERRY_BUILTIN_BIGINT if (ecma_is_value_bigint (ret_value)) { ecma_value_t bigint = ret_value; ret_value = ecma_bigint_to_number (bigint); ecma_free_value (bigint); } else #endif /* JERRY_BUILTIN_BIGINT */ { ret_value = ecma_make_number_value (num); } } return ret_value; } /* ecma_builtin_number_dispatch_call */ /** * Handle calling [[Construct]] of built-in Number object * * @return ecma value */ ecma_value_t ecma_builtin_number_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); if (arguments_list_len == 0) { return ecma_op_create_number_object (ecma_make_integer_value (0)); } #if JERRY_BUILTIN_BIGINT ecma_number_t num; ecma_value_t value = ecma_op_to_numeric (arguments_list_p[0], &num, ECMA_TO_NUMERIC_ALLOW_BIGINT); if (ECMA_IS_VALUE_ERROR (value)) { return value; } if (ecma_is_value_bigint (value)) { ecma_value_t bigint = value; value = ecma_bigint_to_number (bigint); ecma_free_value (bigint); } else { value = ecma_make_number_value (num); } ecma_value_t result = ecma_op_create_number_object (value); ecma_free_value (value); return result; #else /* !JERRY_BUILTIN_BIGINT */ return ecma_op_create_number_object (arguments_list_p[0]); #endif /* JERRY_BUILTIN_BIGINT */ } /* ecma_builtin_number_dispatch_construct */ /** * The Number object 'isInteger' and 'isSafeInteger' routine * * See also: * ECMA-262 v6, 20.1.2.3 * ECMA-262 v6, 20.1.2.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_number_object_is_integer_helper (ecma_value_t arg, /**< routine's argument */ ecma_number_t num, /**< this number */ bool is_safe) /**< is the number safe */ { if (ecma_number_is_nan (num) || ecma_number_is_infinity (num)) { return ECMA_VALUE_FALSE; } ecma_number_t int_num; if (is_safe) { int_num = ecma_number_trunc (num); if (fabs (int_num) > ECMA_NUMBER_MAX_SAFE_INTEGER) { return ECMA_VALUE_FALSE; } } else { ecma_op_to_integer (arg, &int_num); } return (int_num == num) ? ECMA_VALUE_TRUE : ECMA_VALUE_FALSE; } /* ecma_builtin_number_object_is_integer_helper */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_number_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (this_arg, arguments_number); if (!ecma_is_value_number (arguments_list_p[0])) { return ECMA_VALUE_FALSE; } ecma_number_t num = ecma_get_number_from_value (arguments_list_p[0]); switch (builtin_routine_id) { case ECMA_NUMBER_OBJECT_ROUTINE_IS_FINITE: { return ecma_make_boolean_value (!(ecma_number_is_nan (num) || ecma_number_is_infinity (num))); } case ECMA_NUMBER_OBJECT_ROUTINE_IS_NAN: { return ecma_make_boolean_value (ecma_number_is_nan (num)); } case ECMA_NUMBER_OBJECT_ROUTINE_IS_INTEGER: case ECMA_NUMBER_OBJECT_ROUTINE_IS_SAFE_INTEGER: { bool is_safe = (builtin_routine_id == ECMA_NUMBER_OBJECT_ROUTINE_IS_SAFE_INTEGER); return ecma_builtin_number_object_is_integer_helper (arguments_list_p[0], num, is_safe); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_number_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_NUMBER */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-expr.cpp000664 001750 001750 00000410366 15164251010 044627 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "js-parser-internal.h" #if JERRY_PARSER #include "ecma-helpers.h" #include "jcontext.h" #include "js-parser-tagged-template-literal.h" #include "lit-char-helpers.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_expr Expression parser * @{ */ /** * Maximum precedence for right-to-left binary operation evaluation. */ #define PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE 7 /** * Precedence for ternary operation. */ #define PARSER_RIGHT_TO_LEFT_ORDER_TERNARY_PRECEDENCE 4 /** * Precedence for exponentiation operation. */ #define PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION 16 /** * Value of grouping level increase and decrease. */ #define PARSER_GROUPING_LEVEL_INCREASE 2 /** * Precedence of the binary tokens. * * See also: * lexer_token_type_t */ static const uint8_t parser_binary_precedence_table[] = { 3, /**< "=" */ 3, /**< "+=" */ 3, /**< "-=" */ 3, /**< "*=" */ 3, /**< "/=" */ 3, /**< "=" */ 3, /**< "<<=" */ 3, /**< ">>=" */ 3, /**< ">>>=" */ 3, /**< "&=" */ 3, /**< "|=" */ 3, /**< "^=" */ 3, /**< "**=" */ 3, /**< "??=" */ 3, /**< "||=" */ 3, /**< "&&=" */ 4, /**< "?"*/ 5, /**< "??" */ 6, /**< "||" */ 7, /**< "&&" */ 8, /**< "|" */ 9, /**< "^" */ 10, /**< "&" */ 11, /**< "==" */ 11, /**< "!=" */ 11, /**< "===" */ 11, /**< "!==" */ 12, /**< "<" */ 12, /**< ">" */ 12, /**< "<=" */ 12, /**< ">=" */ 12, /**< in */ 12, /**< instanceof */ 13, /**< "<<" */ 13, /**< ">>" */ 13, /**< ">>>" */ 14, /**< "+" */ 14, /**< "-" */ 15, /**< "*" */ 15, /**< "/" */ 15, /**< "%" */ 16, /**< "**" */ }; JERRY_STATIC_ASSERT ((sizeof (parser_binary_precedence_table) == 42), parser_binary_precedence_table_should_have_39_values_in_es2015); /** * Generate byte code for operators with lvalue. */ static inline void parser_push_result (parser_context_t *context_p) /**< context */ { if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 1)); if ((context_p->last_cbc_opcode == CBC_POST_INCR || context_p->last_cbc_opcode == CBC_POST_DECR) && context_p->stack_depth >= context_p->stack_limit) { /* Stack limit is increased for CBC_POST_INCR_PUSH_RESULT * and CBC_POST_DECR_PUSH_RESULT opcodes. Needed by vm.c. */ JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit); context_p->stack_limit++; if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } context_p->last_cbc_opcode++; parser_flush_cbc (context_p); } } /* parser_push_result */ /** * Check for invalid assignment for "eval" and "arguments" */ static void parser_check_invalid_assign (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL); if (JERRY_UNLIKELY (context_p->status_flags & PARSER_IS_STRICT)) { if (context_p->last_cbc.literal_keyword_type == LEXER_KEYW_EVAL) { parser_raise_error (context_p, PARSER_ERR_EVAL_CANNOT_ASSIGNED); } else if (context_p->last_cbc.literal_keyword_type == LEXER_KEYW_ARGUMENTS) { parser_raise_error (context_p, PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED); } } } /* parser_check_invalid_assign */ /** * Check and throw an error if the "new.target" is invalid as a left-hand side expression. */ static void parser_check_invalid_new_target (parser_context_t *context_p, /**< parser context */ cbc_opcode_t opcode) /**< current opcode under parsing */ { /* new.target is an invalid left-hand side target */ if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_NEW_TARGET)) { /* Make sure that the call side is a post/pre increment or an assignment expression. * There should be no other ways the "new.target" expression should be here. */ JERRY_ASSERT ( (opcode >= CBC_PRE_INCR && opcode <= CBC_POST_DECR) || (opcode == CBC_ASSIGN && (context_p->token.type == LEXER_ASSIGN || LEXER_IS_BINARY_LVALUE_OP_TOKEN (context_p->token.type)))); parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_NOT_ALLOWED); } } /* parser_check_invalid_new_target */ /** * Emit identifier reference */ static void parser_emit_ident_reference (parser_context_t *context_p, /**< context */ uint16_t opcode) /* opcode */ { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = opcode; return; } uint16_t literal_index; if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; literal_index = context_p->last_cbc.value; } else if (context_p->last_cbc_opcode == CBC_PUSH_THIS_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_THIS; literal_index = context_p->last_cbc.literal_index; } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; literal_index = context_p->last_cbc.third_literal_index; } parser_emit_cbc_literal (context_p, opcode, literal_index); } /* parser_emit_ident_reference */ /** * Generate byte code for operators with lvalue. */ static void parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */ cbc_opcode_t opcode) /**< opcode */ { if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { parser_check_invalid_assign (context_p); uint16_t unary_opcode; if (opcode == CBC_DELETE_PUSH_RESULT) { if (JERRY_UNLIKELY (context_p->status_flags & PARSER_IS_STRICT)) { parser_raise_error (context_p, PARSER_ERR_DELETE_IDENT_NOT_ALLOWED); } unary_opcode = CBC_DELETE_IDENT_PUSH_RESULT; } else { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, opcode + CBC_UNARY_LVALUE_WITH_IDENT)); unary_opcode = (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT); } parser_emit_ident_reference (context_p, unary_opcode); if (unary_opcode != CBC_DELETE_IDENT_PUSH_RESULT && scanner_literal_is_const_reg (context_p, context_p->last_cbc.literal_index)) { /* The current value must be read, but it cannot be changed. */ context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_emit_cbc_ext (context_p, CBC_EXT_THROW_ASSIGN_CONST_ERROR); } return; } if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, opcode)); context_p->last_cbc_opcode = (uint16_t) opcode; return; } if (PARSER_IS_PUSH_PROP_LITERAL (context_p->last_cbc_opcode)) { context_p->last_cbc_opcode = PARSER_PUSH_PROP_LITERAL_TO_PUSH_LITERAL (context_p->last_cbc_opcode); } else { /* Invalid LeftHandSide expression. */ if (opcode == CBC_DELETE_PUSH_RESULT) { if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL)) { parser_raise_error (context_p, PARSER_ERR_DELETE_PRIVATE_FIELD); } if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL) || context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) { parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); parser_emit_cbc (context_p, CBC_POP); return; } parser_emit_cbc (context_p, CBC_POP); parser_emit_cbc (context_p, CBC_PUSH_TRUE); return; } parser_check_invalid_new_target (context_p, opcode); if (opcode == CBC_PRE_INCR || opcode == CBC_PRE_DECR) { parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_PREFIX_OP); } else { parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_POSTFIX_OP); } } parser_emit_cbc (context_p, (uint16_t) opcode); } /* parser_emit_unary_lvalue_opcode */ /** * Parse array literal. */ static void parser_parse_array_literal (parser_context_t *context_p) /**< context */ { uint32_t pushed_items = 0; uint16_t opcode = (uint16_t) CBC_ARRAY_APPEND; JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); parser_emit_cbc (context_p, CBC_CREATE_ARRAY); lexer_next_token (context_p); while (true) { if (context_p->token.type == LEXER_RIGHT_SQUARE) { if (pushed_items > 0) { parser_emit_cbc_call (context_p, opcode, pushed_items); } return; } pushed_items++; if (context_p->token.type == LEXER_COMMA) { parser_emit_cbc (context_p, CBC_PUSH_ELISION); lexer_next_token (context_p); } else { if (context_p->token.type == LEXER_THREE_DOTS) { opcode = (uint16_t) (PARSER_TO_EXT_OPCODE (CBC_EXT_SPREAD_ARRAY_APPEND)); pushed_items++; lexer_next_token (context_p); parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT); } parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->last_cbc_opcode == CBC_PUSH_THIS) { parser_flush_cbc (context_p); } if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); } else if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED); } } if (pushed_items >= 64) { parser_emit_cbc_call (context_p, opcode, pushed_items); opcode = (uint16_t) CBC_ARRAY_APPEND; pushed_items = 0; } } } /* parser_parse_array_literal */ /** Forward definition of parse array initializer. */ static void parser_parse_array_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); /** Forward definition of parse object initializer. */ static void parser_parse_object_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); /** * Class literal parsing options. */ typedef enum { PARSER_CLASS_LITERAL_NO_OPTS = 0, /**< no options are provided */ PARSER_CLASS_LITERAL_CTOR_PRESENT = (1 << 0), /**< class constructor is present */ PARSER_CLASS_LITERAL_HERITAGE_PRESENT = (1 << 1), /**< class heritage is present */ } parser_class_literal_opts_t; /** * Checks whether the current string or identifier literal is constructor * * @return true, if constructor and false otherwise */ static inline bool parser_is_constructor_literal (parser_context_t *context_p) /**< context */ { return (LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) && lexer_compare_literal_to_string (context_p, "constructor", 11)); } /* parser_is_constructor_literal */ /** * Checks if current private field is already declared */ static void parser_check_duplicated_private_field (parser_context_t *context_p, /**< context */ uint8_t opts) /**< options */ { JERRY_ASSERT (context_p->token.type == LEXER_LITERAL); JERRY_ASSERT (context_p->private_context_p); scanner_class_private_member_t *iter = context_p->private_context_p->members_p; bool search_for_property = (opts & SCANNER_PRIVATE_FIELD_PROPERTY); while (iter != NULL) { if (lexer_compare_identifiers (context_p, &context_p->token.lit_location, &iter->loc) && (iter->u8_arg & opts)) { if (iter->u8_arg & SCANNER_PRIVATE_FIELD_SEEN) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_PRIVATE_FIELD); } iter->u8_arg |= SCANNER_PRIVATE_FIELD_SEEN; if (!search_for_property) { break; } } iter = iter->prev_p; } } /* parser_check_duplicated_private_field */ /** * Parse class literal. * * @return true - if the class has static fields, false - otherwise */ static bool parser_parse_class_body (parser_context_t *context_p, /**< context */ parser_class_literal_opts_t opts, /**< class literal parsing options */ uint16_t class_name_index) /**< class literal index */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); lexer_literal_t *ctor_literal_p = NULL; lexer_literal_t *static_fields_literal_p = NULL; if (opts & PARSER_CLASS_LITERAL_CTOR_PRESENT) { ctor_literal_p = lexer_construct_unused_literal (context_p); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, (uint16_t) (context_p->literal_count++)); } else if (opts & PARSER_CLASS_LITERAL_HERITAGE_PRESENT) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR_HERITAGE); } else { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR); } if (class_name_index != PARSER_INVALID_LITERAL_INDEX) { parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, class_name_index); } parser_emit_cbc_ext (context_p, CBC_EXT_INIT_CLASS); bool is_static = false; bool is_private = false; size_t fields_size = 0; uint32_t computed_field_count = 0; while (true) { if (!is_static) { lexer_skip_empty_statements (context_p); } uint32_t flags = (LEXER_OBJ_IDENT_CLASS_IDENTIFIER | LEXER_OBJ_IDENT_SET_FUNCTION_START); if (!is_static) { flags |= LEXER_OBJ_IDENT_CLASS_NO_STATIC; } if (is_private) { flags |= LEXER_OBJ_IDENT_CLASS_PRIVATE; } lexer_expect_object_literal_id (context_p, flags); if (context_p->token.type == LEXER_RIGHT_BRACE) { JERRY_ASSERT (!is_static); break; } if (context_p->token.type == LEXER_HASHMARK) { is_private = true; lexer_next_token (context_p); context_p->token.flags |= LEXER_NO_SKIP_SPACES; continue; } if (context_p->token.type == LEXER_KEYW_STATIC) { JERRY_ASSERT (!is_static); is_static = true; continue; } bool is_constructor_literal = false; if (context_p->token.type == LEXER_LITERAL) { is_constructor_literal = parser_is_constructor_literal (context_p); if (is_private) { if (is_constructor_literal && lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { parser_raise_error (context_p, PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR); } parser_check_duplicated_private_field (context_p, SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER); } } if (!is_static && is_constructor_literal) { JERRY_ASSERT (!is_static); JERRY_ASSERT (opts & PARSER_CLASS_LITERAL_CTOR_PRESENT); JERRY_ASSERT (ctor_literal_p != NULL); if (ctor_literal_p->type == LEXER_FUNCTION_LITERAL) { /* 14.5.1 */ parser_raise_error (context_p, PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS); } uint32_t constructor_status_flags = (PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER | PARSER_CLASS_CONSTRUCTOR | PARSER_LEXICAL_ENV_NEEDED); if (opts & PARSER_CLASS_LITERAL_HERITAGE_PRESENT) { constructor_status_flags |= PARSER_ALLOW_SUPER_CALL; } if (context_p->status_flags & PARSER_INSIDE_WITH) { constructor_status_flags |= PARSER_INSIDE_WITH; } parser_flush_cbc (context_p); ecma_compiled_code_t *compiled_code_p = parser_parse_function (context_p, constructor_status_flags); ctor_literal_p->u.bytecode_p = compiled_code_p; ctor_literal_p->type = LEXER_FUNCTION_LITERAL; continue; } bool is_computed = false; if (context_p->token.type == LEXER_PROPERTY_GETTER || context_p->token.type == LEXER_PROPERTY_SETTER) { uint16_t literal_index, function_literal_index; bool is_getter = (context_p->token.type == LEXER_PROPERTY_GETTER); uint32_t accessor_status_flags = PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER; accessor_status_flags |= (is_getter ? PARSER_IS_PROPERTY_GETTER : PARSER_IS_PROPERTY_SETTER); uint8_t ident_opts = LEXER_OBJ_IDENT_ONLY_IDENTIFIERS; if (lexer_check_next_character (context_p, LIT_CHAR_HASHMARK)) { lexer_next_token (context_p); context_p->token.flags |= LEXER_NO_SKIP_SPACES; ident_opts |= LEXER_OBJ_IDENT_CLASS_PRIVATE; is_private = true; } lexer_expect_object_literal_id (context_p, ident_opts); if (is_private) { parser_check_duplicated_private_field (context_p, is_getter ? SCANNER_PRIVATE_FIELD_GETTER : SCANNER_PRIVATE_FIELD_SETTER); } literal_index = context_p->lit_object.index; if (context_p->token.type == LEXER_RIGHT_SQUARE) { is_computed = true; } else if (is_static && !is_private) { if (LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) && lexer_compare_identifier_to_string (&context_p->token.lit_location, (uint8_t *) "prototype", 9)) { parser_raise_error (context_p, PARSER_ERR_CLASS_STATIC_PROTOTYPE); } } else if (parser_is_constructor_literal (context_p)) { JERRY_ASSERT (!is_static || is_private); parser_raise_error (context_p, PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR); } function_literal_index = lexer_construct_function_object (context_p, accessor_status_flags); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, literal_index); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); cbc_ext_opcode_t opcode; if (is_computed) { context_p->last_cbc.literal_index = function_literal_index; if (is_getter) { opcode = is_static ? CBC_EXT_SET_STATIC_COMPUTED_GETTER : CBC_EXT_SET_COMPUTED_GETTER; } else { opcode = is_static ? CBC_EXT_SET_STATIC_COMPUTED_SETTER : CBC_EXT_SET_COMPUTED_SETTER; } } else { context_p->last_cbc.value = function_literal_index; if (is_getter) { opcode = is_static ? (is_private ? CBC_EXT_COLLECT_PRIVATE_STATIC_GETTER : CBC_EXT_SET_STATIC_GETTER) : (is_private ? CBC_EXT_COLLECT_PRIVATE_GETTER : CBC_EXT_SET_GETTER); } else { opcode = is_static ? (is_private ? CBC_EXT_COLLECT_PRIVATE_STATIC_SETTER : CBC_EXT_SET_STATIC_SETTER) : (is_private ? CBC_EXT_COLLECT_PRIVATE_SETTER : CBC_EXT_SET_SETTER); } } if (is_computed) { parser_emit_cbc_ext (context_p, is_getter ? CBC_EXT_SET_COMPUTED_GETTER_NAME : CBC_EXT_SET_COMPUTED_SETTER_NAME); parser_emit_cbc_ext (context_p, opcode); } else { if (is_private) { accessor_status_flags |= PARSER_PRIVATE_FUNCTION_NAME; } parser_set_function_name (context_p, function_literal_index, literal_index, accessor_status_flags); context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); } is_static = false; is_private = false; continue; } uint32_t status_flags = PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER; if (context_p->token.type == LEXER_KEYW_ASYNC) { status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; uint8_t ident_opts = LEXER_OBJ_IDENT_ONLY_IDENTIFIERS; if (lexer_check_next_character (context_p, LIT_CHAR_HASHMARK)) { lexer_next_token (context_p); context_p->token.flags |= LEXER_NO_SKIP_SPACES; ident_opts |= LEXER_OBJ_IDENT_CLASS_PRIVATE; is_private = true; } if (!lexer_consume_generator (context_p)) { lexer_expect_object_literal_id (context_p, ident_opts); } if (is_private && context_p->token.type == LEXER_LITERAL) { if (parser_is_constructor_literal (context_p)) { parser_raise_error (context_p, PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR); } parser_check_duplicated_private_field (context_p, SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER); } } if (context_p->token.type == LEXER_MULTIPLY) { uint8_t ident_opts = LEXER_OBJ_IDENT_ONLY_IDENTIFIERS; if (lexer_check_next_character (context_p, LIT_CHAR_HASHMARK)) { lexer_next_token (context_p); context_p->token.flags |= LEXER_NO_SKIP_SPACES; ident_opts |= LEXER_OBJ_IDENT_CLASS_PRIVATE; is_private = true; } lexer_expect_object_literal_id (context_p, ident_opts); status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; if (is_private && context_p->token.type == LEXER_LITERAL) { if (parser_is_constructor_literal (context_p)) { parser_raise_error (context_p, PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR); } parser_check_duplicated_private_field (context_p, SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER); } } bool is_static_block = context_p->token.type == LEXER_LEFT_BRACE; if (context_p->token.type == LEXER_RIGHT_SQUARE) { is_computed = true; } else if (!is_static_block && LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type)) { if (is_static && !is_private) { if (lexer_compare_identifier_to_string (&context_p->token.lit_location, (uint8_t *) "prototype", 9)) { parser_raise_error (context_p, PARSER_ERR_CLASS_STATIC_PROTOTYPE); } } else if ((status_flags & (PARSER_IS_ASYNC_FUNCTION | PARSER_IS_GENERATOR_FUNCTION)) && lexer_compare_literal_to_string (context_p, "constructor", 11)) { parser_raise_error (context_p, PARSER_ERR_INVALID_CLASS_CONSTRUCTOR); } } if (!(status_flags & (PARSER_IS_ASYNC_FUNCTION | PARSER_IS_GENERATOR_FUNCTION))) { if (is_static_block || !lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { /* Class field. */ if (fields_size == 0) { parser_stack_push_uint8 (context_p, PARSER_CLASS_FIELD_END); } scanner_range_t range; uint8_t class_field_type = is_static ? PARSER_CLASS_FIELD_STATIC : 0; if (!is_computed) { if (is_private) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); uint8_t field_opcode = is_static ? CBC_EXT_COLLECT_PRIVATE_STATIC_FIELD : CBC_EXT_COLLECT_PRIVATE_FIELD; parser_emit_cbc_ext_literal_from_token (context_p, field_opcode); } if (is_static && !is_static_block && parser_is_constructor_literal (context_p)) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED); } range.start_location.source_p = context_p->token.lit_location.char_p; range.start_location.line = context_p->token.line; range.start_location.column = context_p->token.column; class_field_type |= PARSER_CLASS_FIELD_NORMAL; if (context_p->token.lit_location.type == LEXER_STRING_LITERAL) { range.start_location.source_p--; } } else { if (++computed_field_count > ECMA_INTEGER_NUMBER_MAX) { parser_raise_error (context_p, PARSER_ERR_TOO_MANY_CLASS_FIELDS); } if (is_static && static_fields_literal_p == NULL) { static_fields_literal_p = lexer_construct_unused_literal (context_p); parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_STATIC_COMPUTED_FIELD_FUNC, (uint16_t) (context_p->literal_count++)); } else { parser_emit_cbc_ext (context_p, (is_static ? CBC_EXT_ADD_STATIC_COMPUTED_FIELD : CBC_EXT_ADD_COMPUTED_FIELD)); } } if (is_static_block) { class_field_type |= PARSER_CLASS_FIELD_STATIC_BLOCK; if (context_p->next_scanner_info_p->type != SCANNER_TYPE_CLASS_STATIC_BLOCK_END) { parser_flush_cbc (context_p); parser_parse_class_static_block (context_p); } JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CLASS_STATIC_BLOCK_END); scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); scanner_release_next (context_p, sizeof (scanner_location_info_t)); JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); range.start_location.source_p = context_p->next_scanner_info_p->source_p - 1; scanner_seek (context_p); parser_stack_push (context_p, &range.start_location, sizeof (scanner_location_t)); fields_size += sizeof (scanner_location_t); lexer_consume_next_character (context_p); } else if (lexer_consume_assign (context_p)) { class_field_type |= PARSER_CLASS_FIELD_INITIALIZED; if (context_p->next_scanner_info_p->source_p != context_p->source_p) { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } if (is_computed) { scanner_get_location (&range.start_location, context_p); } JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END); range.source_end_p = ((scanner_location_info_t *) context_p->next_scanner_info_p)->location.source_p; scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); scanner_release_next (context_p, sizeof (scanner_location_info_t)); scanner_seek (context_p); parser_stack_push (context_p, &range, sizeof (scanner_range_t)); fields_size += sizeof (scanner_range_t); } else { if (!(context_p->token.flags & LEXER_WAS_NEWLINE) && !lexer_check_next_characters (context_p, LIT_CHAR_SEMICOLON, LIT_CHAR_RIGHT_BRACE)) { lexer_next_token (context_p); parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } if (!is_computed) { parser_stack_push (context_p, &range.start_location, sizeof (scanner_location_t)); fields_size += sizeof (scanner_location_t); } } parser_stack_push_uint8 (context_p, class_field_type); fields_size++; is_static = false; is_private = false; continue; } if (!is_computed) { if (context_p->token.lit_location.type != LEXER_NUMBER_LITERAL) { JERRY_ASSERT (context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_STRING_LITERAL); if (is_private) { parser_resolve_private_identifier (context_p); } else { lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); } } else { lexer_construct_number_object (context_p, false, false); } } } uint16_t literal_index = context_p->lit_object.index; uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags | PARSER_IS_METHOD); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); if (is_computed) { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_FUNCTION_NAME); parser_emit_cbc_ext (context_p, is_static ? CBC_EXT_SET_STATIC_COMPUTED_PROPERTY : CBC_EXT_SET_COMPUTED_PROPERTY); is_static = false; continue; } uint32_t function_name_status_flags = 0; if (is_private) { function_name_status_flags = PARSER_PRIVATE_FUNCTION_NAME; } parser_set_function_name (context_p, function_literal_index, literal_index, function_name_status_flags); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); context_p->last_cbc.value = literal_index; if (is_static) { context_p->last_cbc_opcode = (is_private ? PARSER_TO_EXT_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_METHOD) : PARSER_TO_EXT_OPCODE (CBC_EXT_SET_STATIC_PROPERTY_LITERAL)); is_static = false; } else if (is_private) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_COLLECT_PRIVATE_METHOD); } else { context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; } is_private = false; } if (fields_size == 0) { return false; } parser_reverse_class_fields (context_p, fields_size); /* Since PARSER_IS_ARROW_FUNCTION and PARSER_CLASS_CONSTRUCTOR bits cannot * be set at the same time, this bit combination triggers class field parsing. */ if (!(context_p->stack_top_uint8 & PARSER_CLASS_FIELD_STATIC)) { lexer_literal_t *literal_p = lexer_construct_unused_literal (context_p); uint16_t function_literal_index = (uint16_t) (context_p->literal_count++); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_FIELD_INIT, function_literal_index); parser_flush_cbc (context_p); literal_p->u.bytecode_p = parser_parse_class_fields (context_p); literal_p->type = LEXER_FUNCTION_LITERAL; } bool has_static_field = false; if (context_p->stack_top_uint8 & PARSER_CLASS_FIELD_STATIC) { if (static_fields_literal_p == NULL) { static_fields_literal_p = lexer_construct_unused_literal (context_p); uint16_t function_literal_index = (uint16_t) (context_p->literal_count++); parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_STATIC_FIELD_FUNC, function_literal_index); } parser_flush_cbc (context_p); static_fields_literal_p->u.bytecode_p = parser_parse_class_fields (context_p); static_fields_literal_p->type = LEXER_FUNCTION_LITERAL; has_static_field = true; } parser_stack_pop_uint8 (context_p); return has_static_field; } /* parser_parse_class_body */ /** * Parse class statement or expression. */ void parser_parse_class (parser_context_t *context_p, /**< context */ bool is_statement) /**< true - if class is parsed as a statement * false - otherwise (as an expression) */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_CLASS); uint16_t class_ident_index = PARSER_INVALID_LITERAL_INDEX; uint16_t class_name_index = PARSER_INVALID_LITERAL_INDEX; parser_class_literal_opts_t opts = PARSER_CLASS_LITERAL_NO_OPTS; scanner_info_t *scanner_info_p = context_p->next_scanner_info_p; scanner_class_info_t *class_info_p = (scanner_class_info_t *) scanner_info_p; parser_private_context_t private_ctx; if (scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_CLASS_CONSTRUCTOR); parser_save_private_context (context_p, &private_ctx, class_info_p); if (scanner_info_p->u8_arg & SCANNER_CONSTRUCTOR_EXPLICIT) { opts = (parser_class_literal_opts_t) ((int) opts | (int) PARSER_CLASS_LITERAL_CTOR_PRESENT); } scanner_release_next (context_p, sizeof (scanner_class_info_t)); } if (is_statement) { /* Class statement must contain an identifier. */ lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } class_ident_index = context_p->lit_object.index; lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; class_name_index = context_p->lit_object.index; #if JERRY_MODULE_SYSTEM parser_module_append_export_name (context_p); context_p->status_flags &= (uint32_t) ~PARSER_MODULE_STORE_IDENT; #endif /* JERRY_MODULE_SYSTEM */ lexer_next_token (context_p); } else { lexer_next_token (context_p); /* Class expression may contain an identifier. */ if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; class_name_index = context_p->lit_object.index; lexer_next_token (context_p); } } if (class_name_index != PARSER_INVALID_LITERAL_INDEX) { if (JERRY_UNLIKELY (context_p->scope_stack_top >= context_p->scope_stack_size)) { JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); } parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top; PARSER_PLUS_EQUAL_U16 (context_p->scope_stack_top, 1); scope_stack_p->map_from = class_name_index; scope_stack_p->map_to = 0; parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_NAMED_CLASS_ENV, class_name_index); } else { parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); } bool is_strict = (context_p->status_flags & PARSER_IS_STRICT) != 0; /* 14.5. A ClassBody is always strict code. */ context_p->status_flags |= PARSER_IS_STRICT; if (context_p->token.type == LEXER_KEYW_EXTENDS) { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR | PARSE_EXPR_LEFT_HAND_SIDE); opts = (parser_class_literal_opts_t) ((int) opts | (int) PARSER_CLASS_LITERAL_HERITAGE_PRESENT); } else { /* Elisions represents that the classHeritage is not present */ parser_emit_cbc (context_p, CBC_PUSH_ELISION); } if (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } context_p->private_context_p->opts |= SCANNER_PRIVATE_FIELD_ACTIVE; /* ClassDeclaration is parsed. Continue with class body. */ bool has_static_field = parser_parse_class_body (context_p, opts, class_name_index); if (class_name_index != PARSER_INVALID_LITERAL_INDEX) { parser_emit_cbc_ext_literal (context_p, CBC_EXT_FINALIZE_NAMED_CLASS, class_name_index); PARSER_MINUS_EQUAL_U16 (context_p->scope_stack_top, 1); } else { parser_emit_cbc_ext (context_p, CBC_EXT_FINALIZE_ANONYMOUS_CLASS); } if (has_static_field) { parser_emit_cbc_ext (context_p, CBC_EXT_RUN_STATIC_FIELD_INIT); } if (is_statement) { cbc_opcode_t opcode = CBC_MOV_IDENT; if (class_ident_index < PARSER_REGISTER_START) { opcode = (scanner_literal_is_created (context_p, class_ident_index) ? CBC_ASSIGN_LET_CONST : CBC_INIT_LET); } parser_emit_cbc_literal (context_p, (uint16_t) opcode, class_ident_index); parser_flush_cbc (context_p); } if (!is_strict) { /* Restore flag */ context_p->status_flags &= (uint32_t) ~PARSER_IS_STRICT; } context_p->status_flags &= (uint32_t) ~PARSER_ALLOW_SUPER; parser_restore_private_context (context_p, &private_ctx); lexer_next_token (context_p); } /* parser_parse_class */ /** * Parse object initializer method definition. * * See also: ES2015 14.3 */ static void parser_parse_object_method (parser_context_t *context_p) /**< context */ { context_p->source_p--; context_p->column--; uint16_t function_literal_index = lexer_construct_function_object (context_p, (PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER | PARSER_IS_METHOD)); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL; lexer_next_token (context_p); } /* parser_parse_object_method */ /** * Reparse the current literal as a common identifier. */ static void parser_reparse_as_common_identifier (parser_context_t *context_p, /**< context */ parser_line_counter_t start_line, /**< start line */ parser_line_counter_t start_column) /**< start column */ { /* context_p->token.lit_location.char_p is showing the character after the string start, so it is not suitable for reparsing as identifier. e.g.: { 'foo' } */ if (context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } context_p->source_p = context_p->token.lit_location.char_p; context_p->line = start_line; context_p->column = start_column; lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } JERRY_ASSERT (context_p->token.lit_location.type == LEXER_IDENT_LITERAL); lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); } /* parser_reparse_as_common_identifier */ /** * Parse object literal. */ static void parser_parse_object_literal (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); parser_emit_cbc (context_p, CBC_CREATE_OBJECT); bool proto_seen = false; bool has_super_env = false; if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS); if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_SUPER) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_OBJECT_SUPER_ENVIRONMENT); has_super_env = true; } scanner_release_next (context_p, sizeof (scanner_info_t)); } while (true) { lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_SET_FUNCTION_START); switch (context_p->token.type) { case LEXER_RIGHT_BRACE: { break; } case LEXER_PROPERTY_GETTER: case LEXER_PROPERTY_SETTER: { uint32_t status_flags; cbc_ext_opcode_t opcode; bool is_getter = context_p->token.type == LEXER_PROPERTY_GETTER; if (is_getter) { status_flags = PARSER_FUNCTION_CLOSURE | PARSER_IS_PROPERTY_GETTER; opcode = CBC_EXT_SET_GETTER; } else { status_flags = PARSER_FUNCTION_CLOSURE | PARSER_IS_PROPERTY_SETTER; opcode = CBC_EXT_SET_SETTER; } status_flags |= PARSER_ALLOW_SUPER; lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); /* This assignment is a nop for computed getters/setters. */ uint16_t literal_index = context_p->lit_object.index; bool is_computed = context_p->token.type == LEXER_RIGHT_SQUARE; if (is_computed) { opcode = ((opcode == CBC_EXT_SET_GETTER) ? CBC_EXT_SET_COMPUTED_GETTER : CBC_EXT_SET_COMPUTED_SETTER); } uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags); if (opcode >= CBC_EXT_SET_COMPUTED_GETTER) { literal_index = function_literal_index; } parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, literal_index); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); if (is_computed) { parser_emit_cbc_ext (context_p, is_getter ? CBC_EXT_SET_COMPUTED_GETTER_NAME : CBC_EXT_SET_COMPUTED_SETTER_NAME); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED); } parser_emit_cbc_ext (context_p, opcode); lexer_next_token (context_p); break; } parser_set_function_name (context_p, function_literal_index, literal_index, status_flags); if (has_super_env) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.value = function_literal_index; parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED); parser_emit_cbc_ext (context_p, is_getter ? CBC_EXT_SET_COMPUTED_GETTER : CBC_EXT_SET_COMPUTED_SETTER); } else { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); context_p->last_cbc.value = function_literal_index; } lexer_next_token (context_p); break; } case LEXER_RIGHT_SQUARE: { lexer_next_token (context_p); if (context_p->token.type == LEXER_LEFT_PAREN) { parser_parse_object_method (context_p); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); if (parser_check_anonymous_function_declaration (context_p) < PARSER_NAMED_FUNCTION) { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_FUNCTION_NAME); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED); } parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_PROPERTY); } else { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL); } break; } if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (parser_check_anonymous_function_declaration (context_p) < PARSER_NAMED_FUNCTION) { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_FUNCTION_NAME); } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL); } else { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_PROPERTY); } break; } case LEXER_THREE_DOTS: { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_emit_cbc_ext (context_p, CBC_EXT_COPY_DATA_PROPERTIES); break; } case LEXER_KEYW_ASYNC: case LEXER_MULTIPLY: { uint32_t status_flags = PARSER_FUNCTION_CLOSURE; if (context_p->token.type == LEXER_KEYW_ASYNC) { status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; lexer_consume_generator (context_p); } if (context_p->token.type == LEXER_MULTIPLY) { status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } if (has_super_env) { status_flags |= PARSER_ALLOW_SUPER; } lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); uint16_t opcode = CBC_SET_LITERAL_PROPERTY; /* This assignment is a nop for CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL. */ uint16_t literal_index = context_p->lit_object.index; bool is_computed = context_p->token.type == LEXER_RIGHT_SQUARE; if (is_computed) { opcode = CBC_EXT_SET_COMPUTED_PROPERTY; } uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); if (is_computed) { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_FUNCTION_NAME); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED); } parser_emit_cbc_ext (context_p, opcode); lexer_next_token (context_p); break; } parser_set_function_name (context_p, function_literal_index, literal_index, status_flags); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT); parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index); } else { context_p->last_cbc_opcode = opcode; context_p->last_cbc.value = literal_index; } lexer_next_token (context_p); break; } default: { const lexer_lit_location_t *literal_p = (const lexer_lit_location_t *) context_p->lit_object.literal_p; bool is_proto = ((context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_STRING_LITERAL) && lexer_compare_identifier_to_string (literal_p, (uint8_t *) "__proto__", 9) && lexer_check_next_character (context_p, LIT_CHAR_COLON)); if (is_proto) { if (proto_seen) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_PROTO); } proto_seen = true; } uint16_t literal_index = context_p->lit_object.index; parser_line_counter_t start_line = context_p->token.line; parser_line_counter_t start_column = context_p->token.column; lexer_next_token (context_p); if (context_p->token.type == LEXER_LEFT_PAREN && !is_proto) { parser_parse_object_method (context_p); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); parser_set_function_name (context_p, context_p->last_cbc.literal_index, literal_index, 0); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT); parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index); break; } context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; context_p->last_cbc.value = literal_index; break; } if ((context_p->token.type == LEXER_RIGHT_BRACE || context_p->token.type == LEXER_COMMA) && !is_proto) { parser_reparse_as_common_identifier (context_p, start_line, start_column); parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; context_p->last_cbc.value = literal_index; lexer_next_token (context_p); break; } if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (is_proto) { parser_emit_cbc_ext (context_p, CBC_EXT_SET__PROTO__); break; } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { if (context_p->last_cbc.literal_type == LEXER_FUNCTION_LITERAL) { parser_set_function_name (context_p, context_p->last_cbc.literal_index, literal_index, 0); } context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; context_p->last_cbc.value = literal_index; } else { if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_FINALIZE_ANONYMOUS_CLASS)) { uint16_t name_index = scanner_save_literal (context_p, literal_index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index); } parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index); } break; } } if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } else if (context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); } } if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_POP_OBJECT_SUPER_ENVIRONMENT); } } /* parser_parse_object_literal */ /** * Parse function literal. */ static void parser_parse_function_expression (parser_context_t *context_p, /**< context */ uint32_t status_flags) /**< function status flags */ { int literals = 0; uint16_t literal1 = 0; uint16_t literal2 = 0; uint16_t function_literal_index; int32_t function_name_index = -1; if (status_flags & PARSER_IS_FUNC_EXPRESSION) { uint32_t parent_status_flags = context_p->status_flags; context_p->status_flags &= (uint32_t) ~(PARSER_IS_ASYNC_FUNCTION | PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD); if (status_flags & PARSER_IS_ASYNC_FUNCTION) { /* The name of the function cannot be await. */ context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } if (lexer_consume_generator (context_p)) { /* The name of the function cannot be yield. */ context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } if (!lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { /* The `await` keyword is interpreted as an IdentifierReference within function expressions */ context_p->status_flags &= (uint32_t) ~PARSER_IS_CLASS_STATIC_BLOCK; lexer_next_token (context_p); context_p->status_flags |= parent_status_flags & PARSER_IS_CLASS_STATIC_BLOCK; if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } parser_flush_cbc (context_p); lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); if (context_p->token.keyword_type >= LEXER_FIRST_NON_STRICT_ARGUMENTS) { status_flags |= PARSER_HAS_NON_STRICT_ARG; } function_name_index = context_p->lit_object.index; } context_p->status_flags = parent_status_flags; } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { literals = 1; literal1 = context_p->last_cbc.literal_index; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { literals = 2; literal1 = context_p->last_cbc.literal_index; literal2 = context_p->last_cbc.value; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } function_literal_index = lexer_construct_function_object (context_p, status_flags); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); if (function_name_index != -1) { parser_set_function_name (context_p, function_literal_index, (uint16_t) function_name_index, 0); } if (literals == 1) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.literal_index = literal1; context_p->last_cbc.value = function_literal_index; } else if (literals == 2) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.literal_index = literal1; context_p->last_cbc.value = literal2; context_p->last_cbc.third_literal_index = function_literal_index; } else { parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); if (function_name_index != -1) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_NAMED_FUNC_EXPRESSION); context_p->last_cbc.value = (uint16_t) function_name_index; } } context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL; context_p->last_cbc.literal_keyword_type = LEXER_EOS; } /* parser_parse_function_expression */ /** * Parse template literal. */ static void parser_parse_template_literal (parser_context_t *context_p) /**< context */ { bool is_empty_head = true; if (context_p->token.lit_location.length > 0) { is_empty_head = false; lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } if (!is_empty_head) { if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS); } else if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL); } else { parser_emit_cbc_ext (context_p, CBC_EXT_STRING_CONCAT); } } context_p->source_p--; context_p->column--; lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); if (is_empty_head || context_p->token.lit_location.length > 0) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS); context_p->last_cbc.value = context_p->lit_object.index; context_p->last_cbc.literal_type = context_p->token.lit_location.type; context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type; } else { parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_STRING_CONCAT_RIGHT_LITERAL); } } while (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL); } else { parser_emit_cbc_ext (context_p, CBC_EXT_STRING_CONCAT); } context_p->source_p--; context_p->column--; lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); if (context_p->token.lit_location.length > 0) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_STRING_CONCAT_RIGHT_LITERAL); } } } /* parser_parse_template_literal */ /** * Parse tagged template literal. */ static size_t parser_parse_tagged_template_literal (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_TEMPLATE_LITERAL); uint32_t call_arguments = 0; ecma_collection_t *collection_p; if (context_p->tagged_template_literal_cp == JMEM_CP_NULL) { collection_p = ecma_new_collection (); ECMA_SET_INTERNAL_VALUE_POINTER (context_p->tagged_template_literal_cp, collection_p); } else { collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, context_p->tagged_template_literal_cp); if (collection_p->item_count > CBC_MAXIMUM_BYTE_VALUE) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } } const uint32_t tagged_id = collection_p->item_count; uint32_t prop_idx = 0; ecma_object_t *raw_strings_p; ecma_object_t *template_obj_p = parser_new_tagged_template_literal (&raw_strings_p); ecma_collection_push_back (collection_p, ecma_make_object_value (template_obj_p)); parser_tagged_template_literal_append_strings (context_p, template_obj_p, raw_strings_p, prop_idx++); call_arguments++; parser_emit_cbc_ext_call (context_p, CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, tagged_id); while (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) { JERRY_ASSERT (context_p->source_p[-1] == LIT_CHAR_LEFT_BRACE); lexer_next_token (context_p); if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } context_p->source_p--; context_p->column--; lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); parser_tagged_template_literal_append_strings (context_p, template_obj_p, raw_strings_p, prop_idx++); } parser_tagged_template_literal_finalize (template_obj_p, raw_strings_p); return call_arguments; } /* parser_parse_tagged_template_literal */ /** * Checks whether the current expression can be an assignment expression. * * @return true if the current expression can be an assignment expression, false otherwise */ static inline bool parser_is_assignment_expr (parser_context_t *context_p) { return (context_p->stack_top_uint8 == LEXER_EXPRESSION_START || context_p->stack_top_uint8 == LEXER_LEFT_PAREN || context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST || LEXER_IS_BINARY_LVALUE_OP_TOKEN (context_p->stack_top_uint8)); } /* parser_is_assignment_expr */ /** * Throws an error if the current expression is not an assignment expression. */ static inline void parser_check_assignment_expr (parser_context_t *context_p) { if (!parser_is_assignment_expr (context_p)) { parser_raise_error (context_p, PARSER_ERR_ASSIGNMENT_EXPECTED); } } /* parser_check_assignment_expr */ /** * Checks whether the next token is a valid continuation token after an AssignmentExpression. */ static inline bool parser_abort_parsing_after_assignment_expression (parser_context_t *context_p) { return (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_COMMA); } /* parser_abort_parsing_after_assignment_expression */ /** * Parse and record unary operators, and parse the primary literal. * * @return true if parsing should be aborted, true otherwise */ static bool parser_parse_unary_expression (parser_context_t *context_p, /**< context */ size_t *grouping_level_p) /**< grouping level */ { bool new_was_seen = false; /* Collect unary operators. */ while (true) { /* Convert plus and minus binary operators to unary operators. */ switch (context_p->token.type) { case LEXER_ADD: { context_p->token.type = LEXER_PLUS; break; } case LEXER_SUBTRACT: { context_p->token.type = LEXER_NEGATE; break; } case LEXER_KEYW_AWAIT: { #if JERRY_MODULE_SYSTEM if ((context_p->global_status_flags & ECMA_PARSE_MODULE) && !(context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)) { parser_raise_error (context_p, PARSER_ERR_AWAIT_NOT_ALLOWED); } #endif /* JERRY_MODULE_SYSTEM */ if (JERRY_UNLIKELY (context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } break; } } /* Bracketed expressions are primary expressions. At this * point their left paren is pushed onto the stack and * they are processed when their closing paren is reached. */ if (context_p->token.type == LEXER_LEFT_PAREN) { if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); break; } (*grouping_level_p) += PARSER_GROUPING_LEVEL_INCREASE; new_was_seen = false; } else if (context_p->token.type == LEXER_KEYW_NEW) { /* After 'new' unary operators are not allowed. */ new_was_seen = true; /* Check if "new.target" is written here. */ if (scanner_try_scan_new_target (context_p)) { if (!(context_p->status_flags & PARSER_ALLOW_NEW_TARGET)) { parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_NOT_ALLOWED); } parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_NEW_TARGET); lexer_next_token (context_p); /* Found "new.target" return here */ return false; } } else if (new_was_seen || (*grouping_level_p == PARSE_EXPR_LEFT_HAND_SIDE) || !LEXER_IS_UNARY_OP_TOKEN (context_p->token.type)) { break; } parser_stack_push_uint8 (context_p, context_p->token.type); lexer_next_token (context_p); } /* Parse primary expression. */ switch (context_p->token.type) { case LEXER_HASHMARK: { if (!lexer_scan_private_identifier (context_p)) { parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); } parser_resolve_private_identifier (context_p); lexer_next_token (context_p); if (context_p->token.type != LEXER_KEYW_IN) { parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); } parser_stack_push_uint16 (context_p, context_p->lit_object.index); parser_stack_push_uint8 (context_p, LEXER_PRIVATE_PRIMARY_EXPR); return false; } case LEXER_TEMPLATE_LITERAL: { if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) { parser_parse_template_literal (context_p); break; } /* The string is a normal string literal. */ /* FALLTHRU */ } case LEXER_LITERAL: { if (JERRY_UNLIKELY (context_p->next_scanner_info_p->source_p == context_p->source_p)) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); uint32_t arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | (context_p->status_flags & (PARSER_INSIDE_CLASS_FIELD | PARSER_IS_CLASS_STATIC_BLOCK))); if (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC) { JERRY_ASSERT (lexer_token_is_async (context_p)); JERRY_ASSERT (!(context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_STATEMENT)); uint32_t saved_status_flags = context_p->status_flags; context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; lexer_next_token (context_p); context_p->status_flags = saved_status_flags; if (context_p->token.type == LEXER_KEYW_FUNCTION) { uint32_t status_flags = (PARSER_FUNCTION_CLOSURE | PARSER_IS_FUNC_EXPRESSION | PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD); parser_parse_function_expression (context_p, status_flags); break; } arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD); } parser_check_assignment_expr (context_p); parser_parse_function_expression (context_p, arrow_status_flags); return parser_abort_parsing_after_assignment_expression (context_p); } uint8_t type = context_p->token.lit_location.type; if (type == LEXER_IDENT_LITERAL || type == LEXER_STRING_LITERAL) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); } else if (type == LEXER_NUMBER_LITERAL) { bool is_negative_number = false; if ((context_p->stack_top_uint8 == LEXER_PLUS || context_p->stack_top_uint8 == LEXER_NEGATE) && !lexer_check_post_primary_exp (context_p)) { do { if (context_p->stack_top_uint8 == LEXER_NEGATE) { is_negative_number = !is_negative_number; } #if JERRY_BUILTIN_BIGINT else if (JERRY_LIKELY (context_p->token.extra_value == LEXER_NUMBER_BIGINT)) { break; } #endif /* JERRY_BUILTIN_BIGINT */ parser_stack_pop_uint8 (context_p); } while (context_p->stack_top_uint8 == LEXER_PLUS || context_p->stack_top_uint8 == LEXER_NEGATE); } if (lexer_construct_number_object (context_p, true, is_negative_number)) { JERRY_ASSERT (context_p->lit_object.index <= CBC_PUSH_NUMBER_BYTE_RANGE_END); parser_emit_cbc_push_number (context_p, is_negative_number); break; } } cbc_opcode_t opcode = CBC_PUSH_LITERAL; if (context_p->token.keyword_type != LEXER_KEYW_EVAL) { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.value = context_p->lit_object.index; context_p->last_cbc.literal_type = context_p->token.lit_location.type; context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type; break; } if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.third_literal_index = context_p->lit_object.index; context_p->last_cbc.literal_type = context_p->token.lit_location.type; context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type; break; } if (context_p->last_cbc_opcode == CBC_PUSH_THIS) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_PUSH_THIS_LITERAL; } } parser_emit_cbc_literal_from_token (context_p, (uint16_t) opcode); break; } case LEXER_KEYW_FUNCTION: { parser_parse_function_expression (context_p, PARSER_FUNCTION_CLOSURE | PARSER_IS_FUNC_EXPRESSION); break; } case LEXER_LEFT_BRACE: { if (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER) { if (parser_is_assignment_expr (context_p)) { uint32_t flags = PARSER_PATTERN_NO_OPTS; if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { flags |= PARSER_PATTERN_HAS_REST_ELEMENT; } parser_parse_object_initializer (context_p, (parser_pattern_flags_t) flags); return parser_abort_parsing_after_assignment_expression (context_p); } scanner_release_next (context_p, sizeof (scanner_location_info_t)); } parser_parse_object_literal (context_p); break; } case LEXER_LEFT_SQUARE: { if (context_p->next_scanner_info_p->source_p == context_p->source_p) { if (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER) { if (parser_is_assignment_expr (context_p)) { parser_parse_array_initializer (context_p, PARSER_PATTERN_NO_OPTS); return parser_abort_parsing_after_assignment_expression (context_p); } scanner_release_next (context_p, sizeof (scanner_location_info_t)); } else { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS && context_p->next_scanner_info_p->u8_arg == SCANNER_LITERAL_NO_DESTRUCTURING); scanner_release_next (context_p, sizeof (scanner_info_t)); } } parser_parse_array_literal (context_p); break; } case LEXER_DIVIDE: case LEXER_ASSIGN_DIVIDE: { lexer_construct_regexp_object (context_p, false); uint16_t literal_index = (uint16_t) (context_p->literal_count - 1); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.value = literal_index; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.third_literal_index = literal_index; } else { parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, literal_index); } context_p->last_cbc.literal_type = LEXER_REGEXP_LITERAL; context_p->last_cbc.literal_keyword_type = LEXER_EOS; break; } case LEXER_KEYW_THIS: { if (context_p->status_flags & PARSER_ALLOW_SUPER_CALL) { parser_emit_cbc_ext (context_p, CBC_EXT_RESOLVE_LEXICAL_THIS); } else { parser_emit_cbc (context_p, CBC_PUSH_THIS); } break; } case LEXER_LIT_TRUE: { parser_emit_cbc (context_p, CBC_PUSH_TRUE); break; } case LEXER_LIT_FALSE: { parser_emit_cbc (context_p, CBC_PUSH_FALSE); break; } case LEXER_LIT_NULL: { parser_emit_cbc (context_p, CBC_PUSH_NULL); break; } case LEXER_KEYW_CLASS: { parser_parse_class (context_p, false); return false; } case LEXER_KEYW_SUPER: { if (context_p->status_flags & PARSER_ALLOW_SUPER) { if (lexer_check_next_characters (context_p, LIT_CHAR_DOT, LIT_CHAR_LEFT_SQUARE)) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SUPER); break; } if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN) && (context_p->status_flags & PARSER_ALLOW_SUPER_CALL)) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SUPER_CONSTRUCTOR); break; } } parser_raise_error (context_p, PARSER_ERR_UNEXPECTED_SUPER_KEYWORD); } case LEXER_LEFT_PAREN: { JERRY_ASSERT (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); parser_check_assignment_expr (context_p); uint32_t arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | (context_p->status_flags & (PARSER_INSIDE_CLASS_FIELD | PARSER_IS_CLASS_STATIC_BLOCK))); parser_parse_function_expression (context_p, arrow_status_flags); return parser_abort_parsing_after_assignment_expression (context_p); } case LEXER_KEYW_YIELD: { JERRY_ASSERT ((context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) && !(context_p->status_flags & PARSER_DISALLOW_AWAIT_YIELD)); if (context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } parser_check_assignment_expr (context_p); lexer_next_token (context_p); cbc_ext_opcode_t opcode = ((context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) ? CBC_EXT_ASYNC_YIELD : CBC_EXT_YIELD); if (!lexer_check_yield_no_arg (context_p)) { if (context_p->token.type == LEXER_MULTIPLY) { lexer_next_token (context_p); opcode = ((context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) ? CBC_EXT_ASYNC_YIELD_ITERATOR : CBC_EXT_YIELD_ITERATOR); } parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); } else { parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); } parser_emit_cbc_ext (context_p, opcode); return (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_COMMA); } #if JERRY_MODULE_SYSTEM case LEXER_KEYW_IMPORT: { lexer_next_token (context_p); if (context_p->token.type == LEXER_DOT) { lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL || context_p->token.keyword_type != LEXER_KEYW_META || (context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)) { parser_raise_error (context_p, PARSER_ERR_META_EXPECTED); } if (!(context_p->global_status_flags & ECMA_PARSE_MODULE)) { parser_raise_error (context_p, PARSER_ERR_IMPORT_META_REQUIRE_MODULE); } JERRY_ASSERT (context_p->global_status_flags & ECMA_PARSE_INTERNAL_HAS_IMPORT_META); parser_emit_cbc_ext (context_p, CBC_EXT_MODULE_IMPORT_META); break; } if (context_p->token.type != LEXER_LEFT_PAREN) { parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } if (new_was_seen) { parser_raise_error (context_p, PARSER_ERR_IMPORT_AFTER_NEW); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } parser_emit_cbc_ext (context_p, CBC_EXT_MODULE_IMPORT); break; } #endif /* JERRY_MODULE_SYSTEM */ default: { bool is_left_hand_side = (*grouping_level_p == PARSE_EXPR_LEFT_HAND_SIDE); parser_raise_error (context_p, (is_left_hand_side ? PARSER_ERR_LEFT_HAND_SIDE_EXP_EXPECTED : PARSER_ERR_UNEXPECTED_END)); break; } } lexer_next_token (context_p); return false; } /* parser_parse_unary_expression */ /** * Parse the postfix part of unary operators, and * generate byte code for the whole expression. */ static void parser_process_unary_expression (parser_context_t *context_p, /**< context */ size_t grouping_level) /**< grouping level */ { /* Parse postfix part of a primary expression. */ while (true) { /* Since break would only break the switch, we use * continue to continue this loop. Without continue, * the code abandons the loop. */ switch (context_p->token.type) { case LEXER_DOT: { parser_push_result (context_p); if (lexer_check_next_character (context_p, LIT_CHAR_HASHMARK)) { lexer_next_token (context_p); if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER)) { parser_raise_error (context_p, PARSER_ERR_UNEXPECTED_PRIVATE_FIELD); } if (!lexer_scan_private_identifier (context_p)) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } parser_resolve_private_identifier (context_p); parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_PRIVATE_PROP_LITERAL, context_p->lit_object.index); lexer_next_token (context_p); continue; } lexer_expect_identifier (context_p, LEXER_STRING_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->lit_object.literal_p->type == LEXER_STRING_LITERAL); context_p->token.lit_location.type = LEXER_STRING_LITERAL; if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { JERRY_ASSERT (CBC_ARGS_EQ (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL; context_p->last_cbc.value = context_p->lit_object.index; } else if (context_p->last_cbc_opcode == CBC_PUSH_THIS) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_THIS_LITERAL); } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL); context_p->last_cbc.literal_index = context_p->lit_object.index; } else { parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_LITERAL); } lexer_next_token (context_p); continue; } case LEXER_LEFT_SQUARE: { parser_push_result (context_p); uint16_t last_cbc_opcode = context_p->last_cbc_opcode; if (last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER)) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED); } lexer_next_token (context_p); if (last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER)) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SUPER_PROP); continue; } if (PARSER_IS_MUTABLE_PUSH_LITERAL (context_p->last_cbc_opcode)) { context_p->last_cbc_opcode = PARSER_PUSH_LITERAL_TO_PUSH_PROP_LITERAL (context_p->last_cbc_opcode); } else { parser_emit_cbc (context_p, CBC_PUSH_PROP); } continue; } case LEXER_TEMPLATE_LITERAL: case LEXER_LEFT_PAREN: { size_t call_arguments = 0; uint16_t opcode = CBC_CALL; bool is_eval = false; parser_push_result (context_p); if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) { if (context_p->token.type == LEXER_LEFT_PAREN) { parser_stack_pop_uint8 (context_p); opcode = CBC_NEW; } } else { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && context_p->last_cbc.literal_keyword_type == LEXER_KEYW_EVAL && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { is_eval = true; } if (PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode)) { opcode = CBC_CALL_PROP; context_p->last_cbc_opcode = PARSER_PUSH_PROP_TO_PUSH_PROP_REFERENCE (context_p->last_cbc_opcode); } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_CONSTRUCTOR)) { opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL); } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_REFERENCE); opcode = CBC_CALL_PROP; } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_REFERENCE); opcode = CBC_CALL_PROP; } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_REFERENCE); opcode = CBC_CALL_PROP; } else if (JERRY_UNLIKELY (context_p->status_flags & PARSER_INSIDE_WITH) && PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { opcode = CBC_CALL_PROP; parser_emit_ident_reference (context_p, CBC_PUSH_IDENT_REFERENCE); parser_emit_cbc_ext (context_p, CBC_EXT_RESOLVE_BASE); } } bool has_spread_element = false; if (context_p->token.type == LEXER_TEMPLATE_LITERAL) { call_arguments = parser_parse_tagged_template_literal (context_p); } else { lexer_next_token (context_p); while (context_p->token.type != LEXER_RIGHT_PAREN) { if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } if (context_p->token.type == LEXER_THREE_DOTS) { has_spread_element = true; call_arguments++; parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT); lexer_next_token (context_p); } parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); continue; } if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } break; } } lexer_next_token (context_p); if (is_eval) { context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; uint16_t eval_flags = PARSER_SAVE_STATUS_FLAGS (context_p->status_flags); const uint32_t required_flags = PARSER_IS_FUNCTION | PARSER_LEXICAL_BLOCK_NEEDED; if (context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS) { context_p->status_flags |= PARSER_LEXICAL_BLOCK_NEEDED; } else if (((context_p->status_flags & (required_flags | PARSER_IS_STRICT)) == required_flags) || ((context_p->global_status_flags & ECMA_PARSE_FUNCTION_CONTEXT) && !(context_p->status_flags & PARSER_IS_FUNCTION))) { eval_flags |= PARSER_GET_EVAL_FLAG (ECMA_PARSE_FUNCTION_CONTEXT); } if (eval_flags != 0) { parser_emit_cbc_ext_call (context_p, CBC_EXT_LOCAL_EVAL, eval_flags); } else { parser_emit_cbc (context_p, CBC_EVAL); } } if (has_spread_element) { uint16_t spread_opcode; if (opcode == CBC_CALL) { spread_opcode = CBC_EXT_SPREAD_CALL; } else if (opcode == CBC_CALL_PROP) { spread_opcode = CBC_EXT_SPREAD_CALL_PROP; } else if (opcode == CBC_NEW) { spread_opcode = CBC_EXT_SPREAD_NEW; } else { /* opcode is unchanged */ JERRY_ASSERT (opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL)); spread_opcode = CBC_EXT_SPREAD_SUPER_CALL; } parser_emit_cbc_ext_call (context_p, spread_opcode, call_arguments); continue; } if (call_arguments <= 1) { if (opcode == CBC_CALL) { parser_emit_cbc (context_p, (uint16_t) (CBC_CALL0 + (call_arguments * 6))); continue; } if (opcode == CBC_CALL_PROP) { parser_emit_cbc (context_p, (uint16_t) (CBC_CALL0_PROP + (call_arguments * 6))); continue; } if (opcode == CBC_NEW) { parser_emit_cbc (context_p, (uint16_t) (CBC_NEW0 + call_arguments)); continue; } } else if (call_arguments == 2) { if (opcode == CBC_CALL) { parser_emit_cbc (context_p, CBC_CALL2); continue; } if (opcode == CBC_CALL_PROP) { parser_flush_cbc (context_p); /* Manually adjusting stack usage. */ JERRY_ASSERT (context_p->stack_depth > 0); context_p->stack_depth--; parser_emit_cbc (context_p, CBC_CALL2_PROP); continue; } } parser_emit_cbc_call (context_p, opcode, call_arguments); continue; } default: { if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) { parser_push_result (context_p); parser_emit_cbc (context_p, CBC_NEW0); parser_stack_pop_uint8 (context_p); continue; } if (!(context_p->token.flags & LEXER_WAS_NEWLINE) && (context_p->token.type == LEXER_INCREASE || context_p->token.type == LEXER_DECREASE) && grouping_level != PARSE_EXPR_LEFT_HAND_SIDE) { cbc_opcode_t opcode = (context_p->token.type == LEXER_INCREASE) ? CBC_POST_INCR : CBC_POST_DECR; parser_push_result (context_p); parser_emit_unary_lvalue_opcode (context_p, opcode); lexer_next_token (context_p); } break; } } break; } uint8_t last_unary_token = LEXER_INCREASE; /* Generate byte code for the unary operators. */ while (true) { uint8_t token = context_p->stack_top_uint8; if (!LEXER_IS_UNARY_OP_TOKEN (token)) { if (context_p->token.type == LEXER_EXPONENTIATION && last_unary_token != LEXER_INCREASE && last_unary_token != LEXER_DECREASE) { parser_raise_error (context_p, PARSER_ERR_INVALID_EXPONENTIATION); } break; } last_unary_token = token; parser_push_result (context_p); parser_stack_pop_uint8 (context_p); if (LEXER_IS_UNARY_LVALUE_OP_TOKEN (token)) { if (token == LEXER_KEYW_DELETE) { token = CBC_DELETE_PUSH_RESULT; } else { token = (uint8_t) (LEXER_UNARY_LVALUE_OP_TOKEN_TO_OPCODE (token)); } parser_emit_unary_lvalue_opcode (context_p, (cbc_opcode_t) token); } else if (JERRY_UNLIKELY (token == LEXER_KEYW_AWAIT)) { cbc_ext_opcode_t opcode = ((context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) ? CBC_EXT_GENERATOR_AWAIT : CBC_EXT_AWAIT); parser_emit_cbc_ext (context_p, opcode); } else { token = (uint8_t) (LEXER_UNARY_OP_TOKEN_TO_OPCODE (token)); if (token == CBC_TYPEOF) { if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { parser_emit_ident_reference (context_p, CBC_TYPEOF_IDENT); } else { parser_emit_cbc (context_p, token); } } else { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { /* It is not worth to combine with push multiple literals * since the byte code size will not decrease. */ JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, token + 1)); context_p->last_cbc_opcode = (uint16_t) (token + 1); } else { parser_emit_cbc (context_p, token); } } } } } /* parser_process_unary_expression */ /** * Append a binary '=' token. * * @return - pushed assignment opcode onto the parser stack */ static void parser_append_binary_single_assignment_token (parser_context_t *context_p, /**< context */ uint32_t pattern_flags) /**< pattern flags */ { JERRY_UNUSED (pattern_flags); /* Unlike other tokens, the whole byte code is saved for binary * assignment, since it has multiple forms depending on the * previous instruction. */ uint8_t assign_opcode = CBC_ASSIGN; if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { parser_check_invalid_assign (context_p); uint16_t literal_index; switch (context_p->last_cbc_opcode) { case CBC_PUSH_LITERAL: { literal_index = context_p->last_cbc.literal_index; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; break; } case CBC_PUSH_TWO_LITERALS: { literal_index = context_p->last_cbc.value; context_p->last_cbc_opcode = CBC_PUSH_LITERAL; break; } case CBC_PUSH_THIS_LITERAL: { literal_index = context_p->last_cbc.literal_index; context_p->last_cbc_opcode = CBC_PUSH_THIS; parser_flush_cbc (context_p); break; } default: { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); literal_index = context_p->last_cbc.third_literal_index; context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; break; } } assign_opcode = CBC_ASSIGN_SET_IDENT; if (!(pattern_flags & (PARSER_PATTERN_LET | PARSER_PATTERN_CONST | PARSER_PATTERN_LOCAL))) { if (scanner_literal_is_const_reg (context_p, literal_index)) { parser_stack_push_uint8 (context_p, LEXER_ASSIGN_CONST); } } else if (literal_index < PARSER_REGISTER_START) { assign_opcode = CBC_INIT_LET; if (scanner_literal_is_created (context_p, literal_index)) { assign_opcode = CBC_ASSIGN_LET_CONST; } else if (pattern_flags & PARSER_PATTERN_CONST) { assign_opcode = CBC_INIT_CONST; } else if (pattern_flags & PARSER_PATTERN_LOCAL) { assign_opcode = CBC_INIT_ARG_OR_CATCH; } } parser_stack_push_uint16 (context_p, literal_index); JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, assign_opcode)); } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, CBC_ASSIGN)); context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL) { if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_ASSIGN_PROP_LITERAL)); parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); assign_opcode = CBC_ASSIGN_PROP_LITERAL; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; } } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_TWO_LITERALS)); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL) { if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_ASSIGN_PROP_THIS_LITERAL)); parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); assign_opcode = CBC_ASSIGN_PROP_THIS_LITERAL; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else { context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL; } } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_ASSIGNMENT_REFERENCE); parser_stack_push_uint8 (context_p, CBC_EXT_ASSIGN_SUPER); assign_opcode = CBC_EXT_OPCODE; } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE); parser_stack_push_uint8 (context_p, CBC_EXT_ASSIGN_SUPER); assign_opcode = CBC_EXT_OPCODE; } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL)) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_stack_push_uint8 (context_p, CBC_EXT_ASSIGN_PRIVATE); assign_opcode = CBC_EXT_OPCODE; } else { /* Invalid LeftHandSide expression. */ // 3820, 3815 parser_check_invalid_new_target (context_p, CBC_ASSIGN); parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_ASSIGNMENT); } parser_stack_push_uint8 (context_p, assign_opcode); parser_stack_push_uint8 (context_p, LEXER_ASSIGN); } /* parser_append_binary_single_assignment_token */ /** * Check for invalid chain of logical operators */ static void parser_check_invalid_logical_op (parser_context_t *context_p, /**< context */ uint8_t invalid_token1, /**< token id of first invalid token */ uint8_t invalid_token2) /**< token id of second invalid token */ { parser_stack_iterator_t iterator; parser_stack_iterator_init (context_p, &iterator); while (true) { uint8_t token = parser_stack_iterator_read_uint8 (&iterator); if (!LEXER_IS_BINARY_NON_LVALUE_OP_TOKEN (token)) { return; } if (token == invalid_token1 || token == invalid_token2) { parser_raise_error (context_p, PARSER_ERR_INVALID_NULLISH_COALESCING); } /* If a logical operator is found, and there is no SyntaxError, the scan can be terminated * since there was no SyntaxError when the logical operator was pushed onto the stack. */ if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND || token == LEXER_NULLISH_COALESCING) { return; } parser_stack_iterator_skip (&iterator, sizeof (uint8_t)); } } /* parser_check_invalid_logical_op */ /** * Append a binary lvalue token. */ static void parser_append_binary_lvalue_token (parser_context_t *context_p, /**< context */ bool is_logical_assignment) /**< true - if form logical assignment reference * false - otherwise */ { if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { parser_check_invalid_assign (context_p); parser_emit_ident_reference (context_p, CBC_PUSH_IDENT_REFERENCE); if (!is_logical_assignment && scanner_literal_is_const_reg (context_p, context_p->last_cbc.literal_index)) { parser_stack_push_uint8 (context_p, LEXER_ASSIGN_CONST); } } else if (PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode)) { context_p->last_cbc_opcode = PARSER_PUSH_PROP_TO_PUSH_PROP_REFERENCE (context_p->last_cbc_opcode); } else { /* Invalid LeftHandSide expression. */ parser_check_invalid_new_target (context_p, CBC_ASSIGN); parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_ASSIGNMENT); parser_emit_cbc (context_p, CBC_PUSH_PROP_REFERENCE); } if (!is_logical_assignment) { parser_stack_push_uint8 (context_p, (uint8_t) context_p->token.type); } } /* parser_append_binary_lvalue_token */ /** * Append a logical token. */ static void parser_append_logical_token (parser_context_t *context_p, /**< context */ uint16_t opcode) /**< opcode */ { if (opcode != PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH)) { parser_check_invalid_logical_op (context_p, LEXER_NULLISH_COALESCING, LEXER_NULLISH_COALESCING); } parser_branch_t branch; parser_emit_cbc_forward_branch (context_p, opcode, &branch); parser_stack_push (context_p, &branch, sizeof (parser_branch_t)); parser_stack_push_uint8 (context_p, (uint8_t) context_p->token.type); } /* parser_append_logical_token */ /** * Append a logical token. */ static void parser_append_logical_assignment_token (parser_context_t *context_p, /**< context */ uint16_t opcode) /**< opcode */ { uint16_t last_cbc_opcode = context_p->last_cbc_opcode; parser_append_binary_single_assignment_token (context_p, 0); parser_stack_change_last_uint8 (context_p, LEXER_ASSIGN_REFERENCE); context_p->last_cbc_opcode = last_cbc_opcode; parser_append_binary_lvalue_token (context_p, true); parser_append_logical_token (context_p, opcode); } /* parser_append_logical_assignment_token */ /** * Append a binary token. */ static void parser_append_binary_token (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)); parser_push_result (context_p); switch (context_p->token.type) { case LEXER_ASSIGN: { parser_append_binary_single_assignment_token (context_p, 0); break; } /* Binary lvalue-opcodes */ case LEXER_ASSIGN_ADD: case LEXER_ASSIGN_SUBTRACT: case LEXER_ASSIGN_MULTIPLY: case LEXER_ASSIGN_DIVIDE: case LEXER_ASSIGN_MODULO: case LEXER_ASSIGN_EXPONENTIATION: case LEXER_ASSIGN_LEFT_SHIFT: case LEXER_ASSIGN_RIGHT_SHIFT: case LEXER_ASSIGN_UNS_RIGHT_SHIFT: case LEXER_ASSIGN_BIT_AND: case LEXER_ASSIGN_BIT_OR: case LEXER_ASSIGN_BIT_XOR: { parser_append_binary_lvalue_token (context_p, false); break; } case LEXER_ASSIGN_NULLISH_COALESCING: { parser_append_logical_assignment_token (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH)); break; } case LEXER_ASSIGN_LOGICAL_OR: { parser_append_logical_assignment_token (context_p, CBC_BRANCH_IF_LOGICAL_TRUE); break; } case LEXER_ASSIGN_LOGICAL_AND: { parser_append_logical_assignment_token (context_p, CBC_BRANCH_IF_LOGICAL_FALSE); break; } case LEXER_NULLISH_COALESCING: { parser_append_logical_token (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH)); break; } case LEXER_LOGICAL_OR: { parser_append_logical_token (context_p, CBC_BRANCH_IF_LOGICAL_TRUE); break; } case LEXER_LOGICAL_AND: { parser_append_logical_token (context_p, CBC_BRANCH_IF_LOGICAL_FALSE); break; } default: { parser_stack_push_uint8 (context_p, (uint8_t) context_p->token.type); break; } } } /* parser_append_binary_token */ /** * Emit opcode for binary assignment token. */ static void parser_process_binary_assignment_token (parser_context_t *context_p, /**< context */ uint8_t token) /**< token */ { uint16_t index = PARSER_INVALID_LITERAL_INDEX; uint16_t opcode = context_p->stack_top_uint8; if (JERRY_UNLIKELY (opcode == CBC_EXT_OPCODE)) { parser_stack_pop_uint8 (context_p); JERRY_ASSERT (context_p->stack_top_uint8 == CBC_EXT_ASSIGN_SUPER || context_p->stack_top_uint8 == CBC_EXT_ASSIGN_PRIVATE); opcode = PARSER_TO_EXT_OPCODE (context_p->stack_top_uint8); parser_stack_pop_uint8 (context_p); } else { parser_stack_pop_uint8 (context_p); if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG) { JERRY_ASSERT (opcode == CBC_ASSIGN_SET_IDENT || opcode == CBC_ASSIGN_PROP_LITERAL || opcode == CBC_ASSIGN_PROP_THIS_LITERAL || opcode == CBC_ASSIGN_LET_CONST || opcode == CBC_INIT_ARG_OR_CATCH || opcode == CBC_INIT_LET || opcode == CBC_INIT_CONST); index = parser_stack_pop_uint16 (context_p); } } bool group_expr_assignment = false; if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_GROUP_EXPR)) { group_expr_assignment = true; parser_stack_pop_uint8 (context_p); } if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_CONST)) { parser_stack_pop_uint8 (context_p); parser_emit_cbc_ext (context_p, CBC_EXT_THROW_ASSIGN_CONST_ERROR); } if (index == PARSER_INVALID_LITERAL_INDEX) { if (JERRY_UNLIKELY (token == LEXER_ASSIGN_REFERENCE)) { opcode = CBC_ASSIGN_PUSH_RESULT; } parser_emit_cbc (context_p, opcode); return; } if (!group_expr_assignment) { uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); if (function_literal_index == PARSER_ANONYMOUS_CLASS) { uint16_t name_index = scanner_save_literal (context_p, index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index); } else if (function_literal_index < PARSER_NAMED_FUNCTION) { parser_set_function_name (context_p, function_literal_index, (uint16_t) index, 0); } } if (JERRY_UNLIKELY (token == LEXER_ASSIGN_REFERENCE)) { parser_emit_cbc (context_p, CBC_ASSIGN_PUSH_RESULT); return; } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && opcode == CBC_ASSIGN_SET_IDENT) { JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc.value = index; context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT; return; } parser_emit_cbc_literal (context_p, (uint16_t) opcode, index); if (opcode == CBC_ASSIGN_PROP_THIS_LITERAL && (context_p->stack_depth >= context_p->stack_limit)) { /* Stack limit is increased for VM_OC_ASSIGN_PROP_THIS. Needed by vm.c. */ JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit); context_p->stack_limit++; if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } } /* parser_process_binary_opcodes */ /** * Emit opcode for logical tokens. */ static void parser_process_logical_token (parser_context_t *context_p) /**< context */ { parser_branch_t branch; parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); parser_set_branch_to_current_position (context_p, &branch); } /* parser_process_logical_token */ /** * Emit opcode for logical assignment tokens. */ static void parser_process_logical_assignment_token (parser_context_t *context_p) /**< context */ { parser_branch_t condition_branch; parser_stack_pop (context_p, &condition_branch, sizeof (parser_branch_t)); uint8_t token = context_p->stack_top_uint8; JERRY_ASSERT (token == LEXER_ASSIGN_REFERENCE); parser_stack_pop_uint8 (context_p); parser_process_binary_assignment_token (context_p, token); parser_branch_t prop_reference_branch; parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &prop_reference_branch); parser_set_branch_to_current_position (context_p, &condition_branch); parser_emit_cbc_ext (context_p, CBC_EXT_POP_REFERENCE); JERRY_ASSERT (context_p->stack_limit - context_p->stack_depth >= 2); PARSER_PLUS_EQUAL_U16 (context_p->stack_depth, 2); parser_set_branch_to_current_position (context_p, &prop_reference_branch); } /* parser_process_logical_assignment_token */ /** * Emit opcode for binary tokens. */ static void parser_process_binary_token (parser_context_t *context_p, /**< context */ uint8_t token) /**< token */ { uint16_t opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token); if (PARSER_IS_PUSH_NUMBER (context_p->last_cbc_opcode)) { lexer_convert_push_number_to_push_literal (context_p); } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, opcode + CBC_BINARY_WITH_LITERAL)); context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_LITERAL); return; } if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_WITH_TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_TWO_LITERALS); return; } parser_emit_cbc (context_p, opcode); } /* parser_process_binary_token */ /** * Emit opcode for binary lvalue tokens. */ static void parser_process_binary_lvalue_token (parser_context_t *context_p, /**< context */ uint8_t token) /**< token */ { parser_stack_push_uint8 (context_p, CBC_ASSIGN); parser_stack_push_uint8 (context_p, LEXER_ASSIGN); parser_stack_push_uint8 (context_p, lexer_convert_binary_lvalue_token_to_binary (token)); } /* parser_process_binary_lvalue_token */ /** * Emit opcode for binary computations. */ static void parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ uint8_t min_prec_threshhold) /**< minimal precedence of tokens */ { while (true) { uint8_t token = context_p->stack_top_uint8; /* For left-to-right operators (all binary operators except assignment * and logical operators), the byte code is flushed if the precedence * of the next operator is less or equal than the current operator. For * assignment and logical operators, we add 1 to the min precedence to * force right-to-left evaluation order. */ if (!LEXER_IS_BINARY_OP_TOKEN (token) || parser_binary_precedence_table[token - LEXER_FIRST_BINARY_OP] < min_prec_threshhold) { return; } parser_push_result (context_p); parser_stack_pop_uint8 (context_p); switch (token) { case LEXER_ASSIGN: { parser_process_binary_assignment_token (context_p, token); continue; } /* Binary lvalue-opcodes */ case LEXER_ASSIGN_ADD: case LEXER_ASSIGN_SUBTRACT: case LEXER_ASSIGN_MULTIPLY: case LEXER_ASSIGN_DIVIDE: case LEXER_ASSIGN_MODULO: case LEXER_ASSIGN_EXPONENTIATION: case LEXER_ASSIGN_LEFT_SHIFT: case LEXER_ASSIGN_RIGHT_SHIFT: case LEXER_ASSIGN_UNS_RIGHT_SHIFT: case LEXER_ASSIGN_BIT_AND: case LEXER_ASSIGN_BIT_OR: case LEXER_ASSIGN_BIT_XOR: { parser_process_binary_lvalue_token (context_p, token); continue; } case LEXER_ASSIGN_NULLISH_COALESCING: case LEXER_ASSIGN_LOGICAL_OR: case LEXER_ASSIGN_LOGICAL_AND: { parser_process_logical_assignment_token (context_p); continue; } case LEXER_NULLISH_COALESCING: case LEXER_LOGICAL_OR: case LEXER_LOGICAL_AND: { parser_process_logical_token (context_p); continue; } case LEXER_KEYW_IN: { if (context_p->stack_top_uint8 == LEXER_PRIVATE_PRIMARY_EXPR) { parser_stack_pop_uint8 (context_p); uint16_t lit_id = parser_stack_pop_uint16 (context_p); parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_IN, lit_id); continue; } /* FALLTHRU */ } default: { parser_process_binary_token (context_p, token); continue; } } } } /* parser_process_binary_opcodes */ /** * End position marker of a pattern. */ typedef struct { scanner_location_t location; /**< end position of the pattern */ lexer_token_t token; /**< token at the end position */ } parser_pattern_end_marker_t; /** * Literal index should not be emitted while processing rhs target value */ #define PARSER_PATTERN_RHS_NO_LIT PARSER_INVALID_LITERAL_INDEX /** * Process the target of an initializer pattern. */ static parser_pattern_end_marker_t parser_pattern_get_target (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { parser_pattern_end_marker_t end_marker; end_marker.token.type = LEXER_INVALID_PATTERN; parser_branch_t skip_init; if (flags & PARSER_PATTERN_TARGET_DEFAULT) { JERRY_ASSERT (flags & PARSER_PATTERN_TARGET_ON_STACK); parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_DEFAULT_INITIALIZER, &skip_init); } if ((flags & (PARSER_PATTERN_TARGET_ON_STACK | PARSER_PATTERN_TARGET_DEFAULT)) != PARSER_PATTERN_TARGET_ON_STACK) { scanner_location_t start_location; if (context_p->next_scanner_info_p->source_p != context_p->source_p || context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED || (flags & PARSER_PATTERN_REST_ELEMENT)) { /* Found invalid pattern, push null value to fake the rhs target. */ parser_emit_cbc (context_p, CBC_PUSH_NULL); } else { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER); scanner_get_location (&start_location, context_p); scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); scanner_release_next (context_p, sizeof (scanner_location_info_t)); scanner_seek (context_p); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); scanner_get_location (&(end_marker.location), context_p); end_marker.token = context_p->token; scanner_set_location (context_p, &start_location); scanner_seek (context_p); parser_flush_cbc (context_p); } } if (flags & PARSER_PATTERN_TARGET_DEFAULT) { parser_set_branch_to_current_position (context_p, &skip_init); } return end_marker; } /* parser_pattern_get_target */ /** * Finalize an assignment/binding pattern. */ static void parser_pattern_finalize (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags, /**< flags */ parser_pattern_end_marker_t *end_marker_p) /**< pattern end position */ { if ((flags & (PARSER_PATTERN_TARGET_ON_STACK | PARSER_PATTERN_TARGET_DEFAULT)) != PARSER_PATTERN_TARGET_ON_STACK) { if (end_marker_p->token.type == LEXER_INVALID_PATTERN) { parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); } scanner_set_location (context_p, &(end_marker_p->location)); context_p->token = end_marker_p->token; } else { JERRY_ASSERT (!(flags & PARSER_PATTERN_TARGET_DEFAULT)); lexer_next_token (context_p); } if ((flags & (PARSER_PATTERN_BINDING | PARSER_PATTERN_NESTED_PATTERN)) == PARSER_PATTERN_BINDING) { /* Pop the result of the expression. */ parser_emit_cbc (context_p, CBC_POP); } parser_flush_cbc (context_p); } /* parser_pattern_finalize */ /** * Emit right-hand-side target value. */ static void parser_pattern_emit_rhs (parser_context_t *context_p, /**< context */ uint16_t rhs_opcode, /**< opcode to process the rhs value */ uint16_t literal_index) /**< literal index for object pattern */ { if (literal_index != PARSER_PATTERN_RHS_NO_LIT) { parser_emit_cbc_ext_literal (context_p, rhs_opcode, literal_index); } else { parser_emit_cbc_ext (context_p, rhs_opcode); } } /* parser_pattern_emit_rhs */ /** * Form an assignment from a pattern. */ static void parser_pattern_form_assignment (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags, /**< flags */ uint16_t rhs_opcode, /**< opcode to process the rhs value */ uint16_t literal_index, /**< literal index for object pattern */ parser_line_counter_t ident_line_counter) /**< identifier line counter */ { JERRY_UNUSED (ident_line_counter); uint16_t name_index = PARSER_INVALID_LITERAL_INDEX; if ((flags & PARSER_PATTERN_BINDING) || (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)) { name_index = context_p->lit_object.index; } parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START); parser_append_binary_single_assignment_token (context_p, flags); parser_pattern_emit_rhs (context_p, rhs_opcode, literal_index); if (context_p->token.type == LEXER_ASSIGN && !(flags & PARSER_PATTERN_REST_ELEMENT)) { parser_branch_t skip_init; lexer_next_token (context_p); parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_DEFAULT_INITIALIZER, &skip_init); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (name_index != PARSER_INVALID_LITERAL_INDEX) { uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); if (function_literal_index == PARSER_ANONYMOUS_CLASS) { name_index = scanner_save_literal (context_p, name_index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index); } else if (function_literal_index < PARSER_NAMED_FUNCTION) { parser_set_function_name (context_p, function_literal_index, name_index, 0); } } parser_set_branch_to_current_position (context_p, &skip_init); } parser_process_binary_opcodes (context_p, 0); JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START); parser_stack_pop_uint8 (context_p); } /* parser_pattern_form_assignment */ /** * Parse pattern inside a pattern. */ static void parser_pattern_process_nested_pattern (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags, /**< flags */ uint16_t rhs_opcode, /**< opcode to process the rhs value */ uint16_t literal_index) /**< literal index for object pattern */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE || context_p->token.type == LEXER_LEFT_SQUARE); parser_pattern_flags_t options = (parser_pattern_flags_t) ((int) PARSER_PATTERN_NESTED_PATTERN | (int) PARSER_PATTERN_TARGET_ON_STACK | ((int) flags & ((int) PARSER_PATTERN_BINDING | (int) PARSER_PATTERN_LET | (int) PARSER_PATTERN_CONST | (int) PARSER_PATTERN_LOCAL | (int) PARSER_PATTERN_ARGUMENTS))); JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p || context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER || context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS); if (context_p->next_scanner_info_p->source_p == context_p->source_p) { if (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER) { if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { options = (parser_pattern_flags_t) ((int) options | (int) PARSER_PATTERN_HAS_REST_ELEMENT); } if (!(flags & PARSER_PATTERN_REST_ELEMENT)) { options = (parser_pattern_flags_t) ((int) options | (int) PARSER_PATTERN_TARGET_DEFAULT); } else { scanner_release_next (context_p, sizeof (scanner_location_info_t)); } } else { if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { options = (parser_pattern_flags_t) ((int) options | (int) PARSER_PATTERN_HAS_REST_ELEMENT); } scanner_release_next (context_p, sizeof (scanner_info_t)); } } parser_pattern_emit_rhs (context_p, rhs_opcode, literal_index); if (context_p->token.type == LEXER_LEFT_BRACE) { parser_parse_object_initializer (context_p, options); } else { parser_parse_array_initializer (context_p, options); } parser_emit_cbc (context_p, CBC_POP); } /* parser_pattern_process_nested_pattern */ /** * Process the current {Binding, Assignment}Property * * @return true, if a nested pattern is processed, false otherwise */ static bool parser_pattern_process_assignment (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags, /**< flags */ uint16_t rhs_opcode, /**< opcode to process the rhs value */ uint16_t literal_index, /**< literal index for object pattern */ lexer_token_type_t end_type) /**< end type token */ { if ((context_p->token.type == LEXER_LEFT_BRACE || context_p->token.type == LEXER_LEFT_SQUARE) && (context_p->next_scanner_info_p->source_p != context_p->source_p || !(context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_NO_DESTRUCTURING))) { parser_pattern_process_nested_pattern (context_p, flags, rhs_opcode, literal_index); return true; } parser_line_counter_t ident_line_counter = context_p->token.line; if (flags & PARSER_PATTERN_BINDING) { if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); if (flags & (PARSER_PATTERN_LET | PARSER_PATTERN_CONST) && context_p->token.keyword_type == LEXER_KEYW_LET) { parser_raise_error (context_p, PARSER_ERR_LEXICAL_LET_BINDING); } if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } if (flags & PARSER_PATTERN_ARGUMENTS) { if (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) { parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT; } #if JERRY_MODULE_SYSTEM parser_module_append_export_name (context_p); #endif /* JERRY_MODULE_SYSTEM */ parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); lexer_next_token (context_p); if (context_p->token.type != end_type && context_p->token.type != LEXER_ASSIGN && context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_ILLEGAL_PROPERTY_IN_DECLARATION); } } else { parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_LEFT_HAND_SIDE); if (!PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && !PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode)) { parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); } } parser_pattern_form_assignment (context_p, flags, rhs_opcode, literal_index, ident_line_counter); return false; } /* parser_pattern_process_assignment */ /** * Parse array initializer. */ static void parser_parse_array_initializer (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { parser_pattern_end_marker_t end_pos = parser_pattern_get_target (context_p, flags); lexer_next_token (context_p); parser_emit_cbc_ext (context_p, CBC_EXT_ITERATOR_CONTEXT_CREATE); while (context_p->token.type != LEXER_RIGHT_SQUARE) { uint16_t rhs_opcode = CBC_EXT_ITERATOR_STEP; if (context_p->token.type == LEXER_COMMA) { parser_emit_cbc_ext (context_p, rhs_opcode); parser_emit_cbc (context_p, CBC_POP); lexer_next_token (context_p); continue; } parser_pattern_flags_t options = flags; if (context_p->token.type == LEXER_THREE_DOTS) { lexer_next_token (context_p); rhs_opcode = CBC_EXT_REST_INITIALIZER; options = (parser_pattern_flags_t) ((int) options | (int) PARSER_PATTERN_REST_ELEMENT); } parser_pattern_process_assignment (context_p, options, rhs_opcode, PARSER_PATTERN_RHS_NO_LIT, LEXER_RIGHT_SQUARE); if (context_p->token.type == LEXER_COMMA && rhs_opcode != CBC_EXT_REST_INITIALIZER) { lexer_next_token (context_p); } else if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); } } /* close the iterator */ parser_emit_cbc_ext (context_p, CBC_EXT_ITERATOR_CONTEXT_END); parser_pattern_finalize (context_p, flags, &end_pos); } /* parser_parse_array_initializer */ /** * Parse object initializer. */ static void parser_parse_object_initializer (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { parser_pattern_end_marker_t end_pos = parser_pattern_get_target (context_p, flags); /* 12.14.5.2: ObjectAssignmentPattern : { } */ if (lexer_check_next_character (context_p, LIT_CHAR_RIGHT_BRACE)) { parser_emit_cbc_ext (context_p, CBC_EXT_REQUIRE_OBJECT_COERCIBLE); lexer_consume_next_character (context_p); parser_pattern_finalize (context_p, flags, &end_pos); return; } #ifndef JERRY_NDEBUG bool rest_found = false; #endif /* !defined(JERRY_NDEBUG) */ cbc_ext_opcode_t context_opcode = CBC_EXT_OBJ_INIT_CONTEXT_CREATE; if (flags & PARSER_PATTERN_HAS_REST_ELEMENT) { context_opcode = CBC_EXT_OBJ_INIT_REST_CONTEXT_CREATE; } parser_emit_cbc_ext (context_p, context_opcode); while (true) { lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_OBJECT_PATTERN); uint16_t prop_index = context_p->lit_object.index; parser_line_counter_t start_line = context_p->token.line; parser_line_counter_t start_column = context_p->token.column; uint16_t push_prop_opcode = CBC_EXT_INITIALIZER_PUSH_PROP_LITERAL; if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } if (context_p->token.type == LEXER_THREE_DOTS) { lexer_next_token (context_p); flags = (parser_pattern_flags_t) ((int) flags | (int) PARSER_PATTERN_REST_ELEMENT); if (parser_pattern_process_assignment (context_p, flags, CBC_EXT_OBJ_INIT_PUSH_REST, PARSER_PATTERN_RHS_NO_LIT, LEXER_RIGHT_BRACE)) { parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_ASSIGNMENT); } if (context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } #ifndef JERRY_NDEBUG rest_found = true; #endif /* !defined(JERRY_NDEBUG) */ break; } if (context_p->token.type == LEXER_RIGHT_SQUARE) { prop_index = PARSER_PATTERN_RHS_NO_LIT; push_prop_opcode = ((flags & PARSER_PATTERN_HAS_REST_ELEMENT) ? CBC_EXT_INITIALIZER_PUSH_NAME : CBC_EXT_INITIALIZER_PUSH_PROP); } else if (flags & PARSER_PATTERN_HAS_REST_ELEMENT) { push_prop_opcode = CBC_EXT_INITIALIZER_PUSH_NAME_LITERAL; } if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } lexer_next_token (context_p); if (context_p->token.type == LEXER_COLON) { lexer_next_token (context_p); parser_pattern_process_assignment (context_p, flags, push_prop_opcode, prop_index, LEXER_RIGHT_BRACE); } else { if (push_prop_opcode == CBC_EXT_INITIALIZER_PUSH_NAME || push_prop_opcode == CBC_EXT_INITIALIZER_PUSH_PROP) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } if (context_p->token.type != LEXER_RIGHT_BRACE && context_p->token.type != LEXER_ASSIGN && context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); } parser_reparse_as_common_identifier (context_p, start_line, start_column); if (flags & PARSER_PATTERN_ARGUMENTS) { if (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) { parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT; } #if JERRY_MODULE_SYSTEM parser_module_append_export_name (context_p); #endif /* JERRY_MODULE_SYSTEM */ parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); lexer_next_token (context_p); JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE || context_p->token.type == LEXER_ASSIGN || context_p->token.type == LEXER_COMMA); parser_pattern_form_assignment (context_p, flags, push_prop_opcode, prop_index, start_line); } if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } else if (context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); } } if (flags & PARSER_PATTERN_HAS_REST_ELEMENT) { PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, (PARSER_OBJ_INIT_REST_CONTEXT_STACK_ALLOCATION - PARSER_OBJ_INIT_CONTEXT_STACK_ALLOCATION)); } parser_emit_cbc_ext (context_p, CBC_EXT_OBJ_INIT_CONTEXT_END); parser_pattern_finalize (context_p, flags, &end_pos); #ifndef JERRY_NDEBUG /* Checked at the end because there might be syntax errors before. */ JERRY_ASSERT (!!(flags & PARSER_PATTERN_HAS_REST_ELEMENT) == rest_found); #endif /* !defined(JERRY_NDEBUG) */ } /* parser_parse_object_initializer */ /** * Parse an initializer. */ void parser_parse_initializer (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { if (context_p->token.type == LEXER_LEFT_BRACE) { parser_parse_object_initializer (context_p, flags); } else { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); parser_parse_array_initializer (context_p, flags); } } /* parser_parse_initializer */ /** * Parse an initializer using the next character. */ void parser_parse_initializer_by_next_char (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { JERRY_ASSERT (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE)); if (lexer_consume_next_character (context_p) == LIT_CHAR_LEFT_BRACE) { if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER || context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS); if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { flags = (parser_pattern_flags_t) ((int) flags | (int) PARSER_PATTERN_HAS_REST_ELEMENT); } if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS) { scanner_release_next (context_p, sizeof (scanner_info_t)); } } parser_parse_object_initializer (context_p, flags); } else { parser_parse_array_initializer (context_p, flags); } } /* parser_parse_initializer_by_next_char */ /** * Process ternary expression. */ static void parser_process_ternary_expression (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_QUESTION_MARK); cbc_opcode_t opcode = CBC_BRANCH_IF_FALSE_FORWARD; parser_branch_t cond_branch; parser_branch_t uncond_branch; parser_push_result (context_p); if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_BRANCH_IF_TRUE_FORWARD; } parser_emit_cbc_forward_branch (context_p, (uint16_t) opcode, &cond_branch); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &uncond_branch); parser_set_branch_to_current_position (context_p, &cond_branch); /* Although byte code is constructed for two branches, * only one of them will be executed. To reflect this * the stack is manually adjusted. */ JERRY_ASSERT (context_p->stack_depth > 0); context_p->stack_depth--; if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_set_branch_to_current_position (context_p, &uncond_branch); /* Last opcode rewrite is not allowed because * the result may come from the first branch. */ parser_flush_cbc (context_p); parser_process_binary_opcodes (context_p, 0); } /* parser_process_ternary_expression */ /** * Process expression sequence. */ static void parser_process_expression_sequence (parser_context_t *context_p) /**< context */ { if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { parser_emit_cbc (context_p, CBC_POP); } if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN) { parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL); page_p->bytes[context_p->stack.last_position - 1] = LEXER_COMMA_SEP_LIST; context_p->stack_top_uint8 = LEXER_COMMA_SEP_LIST; } lexer_next_token (context_p); } /* parser_process_expression_sequence */ /** * Process group expression. */ static void parser_process_group_expression (parser_context_t *context_p, /**< context */ size_t *grouping_level_p) /**< grouping level */ { JERRY_ASSERT (*grouping_level_p >= PARSER_GROUPING_LEVEL_INCREASE); (*grouping_level_p) -= PARSER_GROUPING_LEVEL_INCREASE; uint8_t token = context_p->stack_top_uint8; if (token == LEXER_COMMA_SEP_LIST) { parser_push_result (context_p); parser_flush_cbc (context_p); } parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); /* Lookahead for anonymous function declaration after '=' token when the assignment base is LHS expression with a single identifier in it. e.g.: (a) = function () {} */ if (JERRY_UNLIKELY (context_p->token.type == LEXER_ASSIGN && PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL && parser_is_assignment_expr (context_p) && *grouping_level_p != PARSE_EXPR_LEFT_HAND_SIDE)) { parser_stack_push_uint8 (context_p, LEXER_ASSIGN_GROUP_EXPR); } } /* parser_process_group_expression */ /** * Parse block expression. */ void parser_parse_block_expression (parser_context_t *context_p, /**< context */ int options) /**< option flags */ { parser_parse_expression (context_p, options | PARSE_EXPR_NO_PUSH_RESULT); if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 2)); PARSER_PLUS_EQUAL_U16 (context_p->last_cbc_opcode, 2); parser_flush_cbc (context_p); } else { parser_emit_cbc (context_p, CBC_POP_BLOCK); } } /* parser_parse_block_expression */ /** * Parse expression statement. */ void parser_parse_expression_statement (parser_context_t *context_p, /**< context */ int options) /**< option flags */ { parser_parse_expression (context_p, options | PARSE_EXPR_NO_PUSH_RESULT); if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { parser_emit_cbc (context_p, CBC_POP); } } /* parser_parse_expression_statement */ JERRY_STATIC_ASSERT ((PARSE_EXPR_LEFT_HAND_SIDE == 0x1), value_of_parse_expr_left_hand_side_must_be_1); /** * Parse expression. */ void parser_parse_expression (parser_context_t *context_p, /**< context */ int options) /**< option flags */ { size_t grouping_level = (options & PARSE_EXPR_LEFT_HAND_SIDE); parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START); if (options & PARSE_EXPR_HAS_LITERAL) { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); goto process_unary_expression; } while (true) { if (parser_parse_unary_expression (context_p, &grouping_level)) { parser_process_binary_opcodes (context_p, 0); break; } while (true) { process_unary_expression: parser_process_unary_expression (context_p, grouping_level); if (JERRY_LIKELY (grouping_level != PARSE_EXPR_LEFT_HAND_SIDE)) { uint8_t min_prec_threshhold = 0; if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)) { if (JERRY_UNLIKELY (context_p->token.type == LEXER_NULLISH_COALESCING)) { parser_check_invalid_logical_op (context_p, LEXER_LOGICAL_OR, LEXER_LOGICAL_AND); } min_prec_threshhold = parser_binary_precedence_table[context_p->token.type - LEXER_FIRST_BINARY_OP]; /* Check for BINARY_LVALUE tokens + LEXER_LOGICAL_OR + LEXER_LOGICAL_AND + LEXER_EXPONENTIATION */ if ((min_prec_threshhold == PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION) || (min_prec_threshhold <= PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE && min_prec_threshhold != PARSER_RIGHT_TO_LEFT_ORDER_TERNARY_PRECEDENCE)) { /* Right-to-left evaluation order. */ min_prec_threshhold++; } } parser_process_binary_opcodes (context_p, min_prec_threshhold); } if (context_p->token.type == LEXER_RIGHT_PAREN && (context_p->stack_top_uint8 == LEXER_LEFT_PAREN || context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST)) { parser_process_group_expression (context_p, &grouping_level); continue; } break; } if (grouping_level == PARSE_EXPR_LEFT_HAND_SIDE) { break; } if (JERRY_UNLIKELY (context_p->token.type == LEXER_QUESTION_MARK)) { parser_process_ternary_expression (context_p); if (context_p->token.type == LEXER_RIGHT_PAREN) { goto process_unary_expression; } } else if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)) { parser_append_binary_token (context_p); lexer_next_token (context_p); continue; } if (JERRY_UNLIKELY (context_p->token.type == LEXER_COMMA) && (!(options & PARSE_EXPR_NO_COMMA) || grouping_level >= PARSER_GROUPING_LEVEL_INCREASE)) { parser_process_expression_sequence (context_p); continue; } break; } if (grouping_level >= PARSER_GROUPING_LEVEL_INCREASE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START); parser_stack_pop_uint8 (context_p); if (!(options & PARSE_EXPR_NO_PUSH_RESULT)) { parser_push_result (context_p); } } /* parser_parse_expression */ /** * @} * @} * @} */ #endif /* JERRY_PARSER */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/clzll.h000664 001750 001750 00000003775 15164251010 037770 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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_CLZLL_H_ #define RAPIDJSON_CLZLL_H_ #include "../rapidjson.h" #if defined(_MSC_VER) && !defined(UNDER_CE) #include #if defined(_WIN64) #pragma intrinsic(_BitScanReverse64) #else #pragma intrinsic(_BitScanReverse) #endif #endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { inline uint32_t clzll(uint64_t x) { // Passing 0 to __builtin_clzll is UB in GCC and results in an // infinite loop in the software implementation. RAPIDJSON_ASSERT(x != 0); #if defined(_MSC_VER) && !defined(UNDER_CE) unsigned long r = 0; #if defined(_WIN64) _BitScanReverse64(&r, x); #else // Scan the high 32 bits. if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 - (r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); #endif // _WIN64 return 63 - r; #elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) // __builtin_clzll wrapper return static_cast(__builtin_clzll(x)); #else // naive version uint32_t r = 0; while (!(x & (static_cast(1) << 63))) { x <<= 1; ++r; } return r; #endif // _MSC_VER } #define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_CLZLL_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgTaskScheduler.cpp000664 001750 001750 00000012771 15164251010 035531 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgArray.h" #include "tvgInlist.h" #include "tvgTaskScheduler.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ namespace tvg { #ifdef THORVG_THREAD_SUPPORT struct TaskQueue { Inlist taskDeque; mutex mtx; condition_variable ready; bool done = false; bool tryPop(Task** task) { unique_lock lock{mtx, try_to_lock}; if (!lock || taskDeque.empty()) return false; *task = taskDeque.front(); return true; } bool tryPush(Task* task) { { unique_lock lock{mtx, try_to_lock}; if (!lock) return false; taskDeque.back(task); } ready.notify_one(); return true; } void complete() { { lock_guard lock{mtx}; done = true; } ready.notify_all(); } bool pop(Task** task) { unique_lock lock{mtx}; while (taskDeque.empty() && !done) { ready.wait(lock); } if (taskDeque.empty()) return false; *task = taskDeque.front(); return true; } void push(Task* task) { { lock_guard lock{mtx}; taskDeque.back(task); } ready.notify_one(); } }; struct TaskSchedulerImpl { Array threads; Array taskQueues; atomic idx{0}; TaskSchedulerImpl(uint32_t threadCnt) { threads.reserve(threadCnt); taskQueues.reserve(threadCnt); for (uint32_t i = 0; i < threadCnt; ++i) { taskQueues.push(new TaskQueue); threads.push(new thread); } for (uint32_t i = 0; i < threadCnt; ++i) { *threads.data[i] = thread([&, i] { run(i); }); } } ~TaskSchedulerImpl() { ARRAY_FOREACH(p, taskQueues) { (*p)->complete(); } ARRAY_FOREACH(p, threads) { (*p)->join(); delete(*p); } ARRAY_FOREACH(p, taskQueues) { delete(*p); } } void run(unsigned i) { Task* task; //Thread Loop while (true) { auto success = false; for (uint32_t x = 0; x < threads.count * 2; ++x) { if (taskQueues[(i + x) % threads.count]->tryPop(&task)) { success = true; break; } } if (!success && !taskQueues[i]->pop(&task)) break; (*task)(i + 1); } } void request(Task* task) { //Async if (threads.count > 0) { task->prepare(); auto i = idx++; for (uint32_t n = 0; n < threads.count; ++n) { if (taskQueues[(i + n) % threads.count]->tryPush(task)) return; } taskQueues[i % threads.count]->push(task); //Sync } else { task->run(0); } } uint32_t threadCnt() { return threads.count; } }; #else //THORVG_THREAD_SUPPORT struct TaskSchedulerImpl { TaskSchedulerImpl(TVG_UNUSED uint32_t threadCnt) {} void request(Task* task) { task->run(0); } uint32_t threadCnt() { return 0; } }; #endif //THORVG_THREAD_SUPPORT } //namespace /************************************************************************/ /* External Class Implementation */ /************************************************************************/ static TaskSchedulerImpl* _inst = nullptr; static ThreadID _tid; //dominant thread id void TaskScheduler::init(uint32_t threads) { if (_inst) return; _inst = new TaskSchedulerImpl(threads); _tid = tid(); } void TaskScheduler::term() { delete(_inst); _inst = nullptr; } void TaskScheduler::request(Task* task) { if (_inst) _inst->request(task); } uint32_t TaskScheduler::threads() { return _inst ? _inst->threadCnt() : 0; } bool TaskScheduler::onthread() { return _tid != tid(); } ThreadID TaskScheduler::tid() { #ifdef THORVG_THREAD_SUPPORT return std::this_thread::get_id(); #else return 0; #endif }mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/vp8li.h000664 001750 001750 00000011205 15164251010 034337 0ustar00ddennedyddennedy000000 000000 // Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Lossless decoder: internal header. // // Author: Skal (pascal.massimino@gmail.com) // Vikas Arora(vikaas.arora@gmail.com) #ifndef WEBP_DEC_VP8LI_H_ #define WEBP_DEC_VP8LI_H_ #include // for memcpy() #include "./webpi.h" #include "../utils/bit_reader.h" #include "../utils/color_cache.h" #include "../utils/huffman.h" #ifdef __cplusplus extern "C" { #endif typedef enum { READ_DATA = 0, READ_HDR = 1, READ_DIM = 2 } VP8LDecodeState; typedef struct VP8LTransform VP8LTransform; struct VP8LTransform { VP8LImageTransformType type_; // transform type. int bits_; // subsampling bits defining transform window. int xsize_; // transform window X index. int ysize_; // transform window Y index. uint32_t *data_; // transform data. }; typedef struct { int color_cache_size_; VP8LColorCache color_cache_; VP8LColorCache saved_color_cache_; // for incremental int huffman_mask_; int huffman_subsample_bits_; int huffman_xsize_; uint32_t *huffman_image_; int num_htree_groups_; HTreeGroup *htree_groups_; HuffmanCode *huffman_tables_; } VP8LMetadata; typedef struct VP8LDecoder VP8LDecoder; struct VP8LDecoder { VP8StatusCode status_; VP8LDecodeState state_; VP8Io *io_; const WebPDecBuffer *output_; // shortcut to io->opaque->output uint32_t *pixels_; // Internal data: either uint8_t* for alpha // or uint32_t* for BGRA. uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage. VP8LBitReader br_; int incremental_; // if true, incremental decoding is expected VP8LBitReader saved_br_; // note: could be local variables too int saved_last_pixel_; int width_; int height_; int last_row_; // last input row decoded so far. int last_pixel_; // last pixel decoded so far. However, it may // not be transformed, scaled and // color-converted yet. int last_out_row_; // last row output so far. VP8LMetadata hdr_; int next_transform_; VP8LTransform transforms_[NUM_TRANSFORMS]; // or'd bitset storing the transforms types. uint32_t transforms_seen_; uint8_t *rescaler_memory; // Working memory for rescaling work. WebPRescaler *rescaler; // Common rescaler for all channels. }; //------------------------------------------------------------------------------ // internal functions. Not public. struct ALPHDecoder; // Defined in dec/alphai.h. // in vp8l.c // Decodes image header for alpha data stored using lossless compression. // Returns false in case of error. int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec, const uint8_t* const data, size_t data_size, uint8_t* const output); // Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are // already decoded in previous call(s), it will resume decoding from where it // was paused. // Returns false in case of bitstream error. int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec, int last_row); // Allocates and initialize a new lossless decoder instance. VP8LDecoder* VP8LNew(void); // Decodes the image header. Returns false in case of error. int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io); // Decodes an image. It's required to decode the lossless header before calling // this function. Returns false in case of error, with updated dec->status_. int VP8LDecodeImage(VP8LDecoder* const dec); // Resets the decoder in its initial state, reclaiming memory. // Preserves the dec->status_ value. void VP8LClear(VP8LDecoder* const dec); // Clears and deallocate a lossless decoder instance. void VP8LDelete(VP8LDecoder* const dec); //------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_DEC_VP8LI_H_ */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-typeerror-prototype.inc.h000664 001750 001750 00000002460 15164251010 052711 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * TypeError.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.7.8 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_TYPE_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.9 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_TYPE_ERROR_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.10 */ STRING_VALUE (LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-string.cpp000664 001750 001750 00000025544 15164251010 047714 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "ecma-symbol-object.h" #include "jrt.h" #include "lit-strings.h" #if JERRY_BUILTIN_STRING #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BUILTIN_STRING_ROUTINE_START = 0, ECMA_BUILTIN_STRING_OBJECT_FROM_CHAR_CODE, ECMA_BUILTIN_STRING_OBJECT_FROM_CODE_POINT, ECMA_BUILTIN_STRING_OBJECT_RAW, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-string.inc.h" #define BUILTIN_UNDERSCORED_ID string #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup string ECMA String object built-in * @{ */ /** * The String object's 'fromCharCode' routine * * See also: * ECMA-262 v5, 15.5.3.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_object_from_char_code (const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { if (args_number == 0) { return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } lit_utf8_size_t utf8_buf_size = args_number * LIT_CESU8_MAX_BYTES_IN_CODE_UNIT; ecma_string_t *ret_string_p = NULL; bool isError = false; JMEM_DEFINE_LOCAL_ARRAY (utf8_buf_p, utf8_buf_size, lit_utf8_byte_t); lit_utf8_size_t utf8_buf_used = 0; for (uint32_t arg_index = 0; arg_index < args_number; arg_index++) { ecma_number_t arg_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (args[arg_index], &arg_num))) { isError = true; break; } uint32_t uint32_char_code = ecma_number_to_uint32 (arg_num); ecma_char_t code_unit = (uint16_t) uint32_char_code; JERRY_ASSERT (utf8_buf_used <= utf8_buf_size - LIT_UTF8_MAX_BYTES_IN_CODE_UNIT); utf8_buf_used += lit_code_unit_to_utf8 (code_unit, utf8_buf_p + utf8_buf_used); JERRY_ASSERT (utf8_buf_used <= utf8_buf_size); } if (!isError) { ret_string_p = ecma_new_ecma_string_from_utf8 (utf8_buf_p, utf8_buf_used); } JMEM_FINALIZE_LOCAL_ARRAY (utf8_buf_p); return isError ? ECMA_VALUE_ERROR : ecma_make_string_value (ret_string_p); } /* ecma_builtin_string_object_from_char_code */ /** * The String object's 'raw' routine * * See also: * ECMA-262 v6, 21.1.2.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_object_raw (const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { /* 1 - 2. */ const ecma_value_t *substitutions; uint32_t number_of_substitutions; ecma_length_t next_index; ecma_stringbuilder_t builder; if (args_number > 1) { substitutions = args + 1; number_of_substitutions = args_number - 1; } else { substitutions = NULL; number_of_substitutions = 0; } /* 3. */ ecma_value_t templated = args_number > 0 ? args[0] : ECMA_VALUE_UNDEFINED; ecma_value_t cooked = ecma_op_to_object (templated); /* 4. */ if (ECMA_IS_VALUE_ERROR (cooked)) { return cooked; } ecma_object_t *cooked_obj_p = ecma_get_object_from_value (cooked); /* 5. */ ecma_value_t raw = ecma_op_object_get_by_magic_id (cooked_obj_p, LIT_MAGIC_STRING_RAW); ecma_deref_object (cooked_obj_p); if (ECMA_IS_VALUE_ERROR (raw)) { return raw; } ecma_value_t raw_obj = ecma_op_to_object (raw); /* 6. */ if (ECMA_IS_VALUE_ERROR (raw_obj)) { ecma_free_value (raw); return raw_obj; } ecma_object_t *raw_obj_p = ecma_get_object_from_value (raw_obj); ecma_value_t ret_value = ECMA_VALUE_ERROR; /* 7 - 8. */ ecma_length_t literal_segments; if (ECMA_IS_VALUE_ERROR (ecma_op_object_get_length (raw_obj_p, &literal_segments))) { goto cleanup; } /* 9. */ if (literal_segments == 0) { ret_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); goto cleanup; } /* 10. */ builder = ecma_stringbuilder_create (); /* 11. */ next_index = 0; /* 12. */ while (true) { /* 12.a,b */ ecma_value_t next_seg = ecma_op_object_get_by_index (raw_obj_p, next_index); if (ECMA_IS_VALUE_ERROR (next_seg)) { goto builder_cleanup; } ecma_string_t *next_seg_srt_p = ecma_op_to_string (next_seg); /* 12.c */ if (JERRY_UNLIKELY (next_seg_srt_p == NULL)) { ecma_free_value (next_seg); goto builder_cleanup; } /* 12.d */ ecma_stringbuilder_append (&builder, next_seg_srt_p); ecma_deref_ecma_string (next_seg_srt_p); ecma_free_value (next_seg); /* 12.e */ if (next_index + 1 == literal_segments) { ret_value = ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); goto cleanup; } /* 12.f-g */ if (next_index >= number_of_substitutions) { next_index++; continue; } /* 12.h */ ecma_string_t *next_sub_p = ecma_op_to_string (substitutions[next_index]); /* 12.i */ if (JERRY_UNLIKELY (next_sub_p == NULL)) { goto builder_cleanup; } /* 12.j */ ecma_stringbuilder_append (&builder, next_sub_p); ecma_deref_ecma_string (next_sub_p); /* 12.k */ next_index++; } builder_cleanup: ecma_stringbuilder_destroy (&builder); cleanup: ecma_deref_object (raw_obj_p); ecma_free_value (raw); return ret_value; } /* ecma_builtin_string_object_raw */ /** * The String object's 'fromCodePoint' routine * * See also: * ECMA-262 v6, 21.1.2.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_string_object_from_code_point (const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { if (args_number == 0) { return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } ecma_stringbuilder_t builder = ecma_stringbuilder_create (); for (uint32_t index = 0; index < args_number; index++) { ecma_number_t to_number_num; ecma_value_t to_number_value = ecma_op_to_number (args[index], &to_number_num); if (ECMA_IS_VALUE_ERROR (to_number_value)) { ecma_stringbuilder_destroy (&builder); return to_number_value; } if (!ecma_op_is_integer (to_number_num)) { ecma_stringbuilder_destroy (&builder); return ecma_raise_range_error (ECMA_ERR_INVALID_CODE_POINT_ERROR); } ecma_free_value (to_number_value); if (to_number_num < 0 || to_number_num > LIT_UNICODE_CODE_POINT_MAX) { ecma_stringbuilder_destroy (&builder); return ecma_raise_range_error (ECMA_ERR_INVALID_CODE_POINT); } lit_code_point_t code_point = (lit_code_point_t) to_number_num; ecma_char_t converted_cp[2]; uint8_t encoded_size = lit_utf16_encode_code_point (code_point, converted_cp); for (uint8_t i = 0; i < encoded_size; i++) { ecma_stringbuilder_append_char (&builder, converted_cp[i]); } } ecma_string_t *ret_str_p = ecma_stringbuilder_finalize (&builder); return ecma_make_string_value (ret_str_p); } /* ecma_builtin_string_object_from_code_point */ /** * Handle calling [[Call]] of built-in String object * * See also: * ECMA-262 v6, 21.1.1.1 * * @return ecma value */ ecma_value_t ecma_builtin_string_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_value_t ret_value = ECMA_VALUE_EMPTY; /* 1. */ if (arguments_list_len == 0) { ret_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } /* 2.a */ else if (ecma_is_value_symbol (arguments_list_p[0])) { ret_value = ecma_get_symbol_descriptive_string (arguments_list_p[0]); } /* 2.b */ else { ecma_string_t *str_p = ecma_op_to_string (arguments_list_p[0]); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } ret_value = ecma_make_string_value (str_p); } return ret_value; } /* ecma_builtin_string_dispatch_call */ /** * Handle calling [[Construct]] of built-in String object * * @return ecma value */ ecma_value_t ecma_builtin_string_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_op_create_string_object (arguments_list_p, arguments_list_len); } /* ecma_builtin_string_dispatch_construct */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_string_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (this_arg); switch (builtin_routine_id) { case ECMA_BUILTIN_STRING_OBJECT_FROM_CHAR_CODE: { return ecma_builtin_string_object_from_char_code (arguments_list_p, arguments_number); } case ECMA_BUILTIN_STRING_OBJECT_FROM_CODE_POINT: { return ecma_builtin_string_object_from_code_point (arguments_list_p, arguments_number); } case ECMA_BUILTIN_STRING_OBJECT_RAW: { return ecma_builtin_string_object_raw (arguments_list_p, arguments_number); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_string_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_STRING */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakset-prototype.inc.h000664 001750 001750 00000003001 15164251010 052311 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * WeakSet.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 23.4.3.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_WEAKSET, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.4.3.5 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_WEAKSET_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_ADD, ECMA_CONTAINER_ROUTINE_ADD, 1, 1) ROUTINE (LIT_MAGIC_STRING_DELETE, ECMA_CONTAINER_ROUTINE_DELETE_WEAK, 1, 1) ROUTINE (LIT_MAGIC_STRING_HAS, ECMA_CONTAINER_ROUTINE_HAS, 1, 1) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-referenceerror-prototype.cpp000664 001750 001750 00000002277 15164251010 053457 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-referenceerror-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID reference_error_prototype #include "ecma-builtin-internal-routines-template.inc.h" #endif /* JERRY_BUILTIN_ERRORS */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testAnimation.cpp000664 001750 001750 00000014525 15164251010 033526 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; TEST_CASE("Animation Basic", "[tvgAnimation]") { auto animation = unique_ptr(Animation::gen()); REQUIRE(animation); auto picture = animation->picture(); REQUIRE(picture->type() == Type::Picture); //Negative cases REQUIRE(animation->frame(0.0f) == Result::InsufficientCondition); REQUIRE(animation->curFrame() == 0.0f); REQUIRE(animation->totalFrame() == 0.0f); REQUIRE(animation->duration() == 0.0f); } #ifdef THORVG_LOTTIE_LOADER_SUPPORT TEST_CASE("Lottie Raw Data", "[tvgAnimation]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = unique_ptr(Animation::gen()); REQUIRE(animation); auto picture = animation->picture(); ifstream file(TEST_DIR"/test.lot"); REQUIRE(file.is_open()); file.seekg(0, std::ios::end); auto size = file.tellg(); file.seekg(0, std::ios::beg); auto data = (char*)malloc(size); file.seekg(0, ios::beg); file.read(data, size); file.close(); REQUIRE(picture->load(data, size, "lot", "", true) == Result::Success); free(data); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Lottie Frames Counting", "[tvgAnimation]") { REQUIRE(Initializer::init(1) == Result::Success); { auto animation = unique_ptr(Animation::gen()); REQUIRE(animation); auto picture = animation->picture(); REQUIRE(picture->load(TEST_DIR"/invalid.lot") == Result::InvalidArguments); REQUIRE(picture->load(TEST_DIR"/test.lot") == Result::Success); REQUIRE(animation->totalFrame() == Approx(120).margin(0.001f)); REQUIRE(animation->curFrame() == 0); REQUIRE(animation->duration() == Approx(4.004).margin(0.001f)); //120/29.97 REQUIRE(animation->frame(20.0f) == Result::Success); for (float i = 1.0f; i < 120.0f; i += 10.0f) { REQUIRE(animation->frame(i) == Result::Success); REQUIRE(animation->curFrame() == i); } REQUIRE(animation->frame(102.8f) == Result::Success); REQUIRE(animation->curFrame() == Approx(102.8f)); REQUIRE(animation->frame(13.32f) == Result::Success); REQUIRE(animation->curFrame() == Approx(13.32f)); REQUIRE(animation->frame(27.1232f) == Result::Success); REQUIRE(animation->curFrame() == Approx(27.1232f)); REQUIRE(animation->frame(87.0004f) == Result::Success); REQUIRE(animation->curFrame() == Approx(87.0004f)); REQUIRE(animation->frame(88.0005f) == Result::Success); REQUIRE(animation->curFrame() == Approx(88.0005f)); REQUIRE(animation->frame(89.0009f) == Result::Success); REQUIRE(animation->curFrame() == Approx(89.0009f)); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Lottie Accessor", "[tvgAnimation]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = unique_ptr(Animation::gen()); REQUIRE(animation); auto picture = animation->picture(); REQUIRE(picture->load(TEST_DIR"/test2.lot") == Result::Success); //specify the lottie scene first REQUIRE(animation->frame(20.0f) == Result::Success); //Find specific paint nodes REQUIRE(!picture->paint(Accessor::id("test1"))); REQUIRE(!picture->paint(Accessor::id("abcd"))); REQUIRE(!picture->paint(Accessor::id("abcd"))); REQUIRE(picture->paint(Accessor::id("bar"))); REQUIRE(picture->paint(Accessor::id("pad1"))); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Lottie Segment", "[tvgAnimation]") { REQUIRE(Initializer::init() == Result::Success); { auto animation = unique_ptr(Animation::gen()); REQUIRE(animation); auto picture = animation->picture(); float begin, end; //Segment by range before loaded REQUIRE(animation->segment(0, 0.5) == Result::InsufficientCondition); //Get current segment before loaded REQUIRE(animation->segment(&begin, &end) == Result::InsufficientCondition); //Animation load REQUIRE(picture->load(TEST_DIR"/segment.lot") == Result::Success); //Get current segment before segment REQUIRE(animation->segment(&begin, &end) == Result::Success); REQUIRE(begin == 0.0f); REQUIRE(end == animation->totalFrame()); //Segment by range REQUIRE(animation->segment(0.25, 0.5) == Result::Success); //Get current segment REQUIRE(animation->segment(&begin, &end) == Result::Success); REQUIRE(begin == 0.25f); REQUIRE(end == 0.5f); //Get only segment begin REQUIRE(animation->segment(&begin) == Result::Success); REQUIRE(begin == 0.25f); //Get only segment end REQUIRE(animation->segment(nullptr, &end) == Result::Success); REQUIRE(end == 0.5f); //Segment by invalid range REQUIRE(animation->segment(1.5, -0.5) == Result::InvalidArguments); } REQUIRE(Initializer::term() == Result::Success); } #endifthorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.cpp000664 001750 001750 00000011553 15164251010 051043 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-shared-arraybuffer-object.h" #include "ecma-arraybuffer-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-typedarray-object.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmasharedarraybufferobject ECMA SharedArrayBuffer object related routines * @{ */ #if JERRY_BUILTIN_SHAREDARRAYBUFFER /** * Creating SharedArrayBuffer objects based on the array length * * @return new SharedArrayBuffer object */ ecma_object_t * ecma_shared_arraybuffer_new_object (uint32_t length) /**< length of the SharedArrayBuffer */ { if (length > 0) { return ecma_arraybuffer_create_object_with_buffer (ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER, length); } return ecma_arraybuffer_create_object (ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER, length); } /* ecma_shared_arraybuffer_new_object */ /** * SharedArrayBuffer object creation operation. * * See also: ES11 24.1.1.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_shared_arraybuffer_object (const ecma_value_t *arguments_list_p, /**< list of arguments that * are passed to String constructor */ uint32_t arguments_list_len) /**< length of the arguments' list */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_number_t length_num = 0; if (arguments_list_len > 0) { if (ecma_is_value_number (arguments_list_p[0])) { length_num = ecma_get_number_from_value (arguments_list_p[0]); } else { ecma_value_t to_number_value = ecma_op_to_number (arguments_list_p[0], &length_num); if (ECMA_IS_VALUE_ERROR (to_number_value)) { ecma_deref_object (proto_p); return to_number_value; } } if (ecma_number_is_nan (length_num)) { length_num = 0; } const uint32_t maximum_size_in_byte = UINT32_MAX - sizeof (ecma_extended_object_t) - JMEM_ALIGNMENT + 1; if (length_num <= -1.0 || length_num > (ecma_number_t) maximum_size_in_byte + 0.5) { ecma_deref_object (proto_p); return ecma_raise_range_error (ECMA_ERR_INVALID_SHARED_ARRAYBUFFER_LENGTH); } } uint32_t length_uint32 = ecma_number_to_uint32 (length_num); ecma_object_t *shared_array_buffer = ecma_shared_arraybuffer_new_object (length_uint32); ECMA_SET_NON_NULL_POINTER (shared_array_buffer->u2.prototype_cp, proto_p); ecma_deref_object (proto_p); return ecma_make_object_value (shared_array_buffer); } /* ecma_op_create_shared_arraybuffer_object */ #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ /** * Helper function: check if the target is SharedArrayBuffer * * See also: ES11 24.1.1.4 * * @return true - if value is a SharedArrayBuffer object * false - otherwise */ bool ecma_is_shared_arraybuffer (ecma_value_t target) /**< the target value */ { #if JERRY_BUILTIN_SHAREDARRAYBUFFER return (ecma_is_value_object (target) && ecma_object_class_is (ecma_get_object_from_value (target), ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER)); #else /* !JERRY_BUILTIN_SHAREDARRAYBUFFER */ JERRY_UNUSED (target); return false; #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ } /* ecma_is_shared_arraybuffer */ /** * Helper function: check if the target is SharedArrayBuffer Object * * @return true - if value is a SharedArrayBuffer object * false - otherwise */ bool ecma_object_is_shared_arraybuffer (ecma_object_t *object_p) /**< the target object */ { #if JERRY_BUILTIN_SHAREDARRAYBUFFER return ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER); #else /* !JERRY_BUILTIN_SHAREDARRAYBUFFER */ JERRY_UNUSED (object_p); return false; #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ } /* ecma_object_is_shared_arraybuffer */ /** * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/webp/meson.build000664 001750 001750 00000000271 15164251010 035412 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0source_file = [ 'decode.h', 'format_constants.h', 'types.h' ] webp_deb += [declare_dependency( include_directories : include_directories('.'), sources : source_file )]modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgRenderTarget.cpp000664 001750 001750 00000006325 15164251010 037614 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgRenderTarget.h" void WgRenderTarget::initialize(WgContext& context, uint32_t width, uint32_t height) { this->width = width; this->height = height; texture = context.createTexStorage(width, height, WGPUTextureFormat_RGBA8Unorm); textureMS = context.createTexAttachement(width, height, WGPUTextureFormat_RGBA8Unorm, 4); texView = context.createTextureView(texture); texViewMS = context.createTextureView(textureMS); bindGroupRead = context.layouts.createBindGroupStrorage1RO(texView); bindGroupWrite = context.layouts.createBindGroupStrorage1WO(texView); bindGroupTexture = context.layouts.createBindGroupTexSampled(context.samplerNearestRepeat, texView); } void WgRenderTarget::release(WgContext& context) { context.layouts.releaseBindGroup(bindGroupTexture); context.layouts.releaseBindGroup(bindGroupWrite); context.layouts.releaseBindGroup(bindGroupRead); context.releaseTextureView(texViewMS); context.releaseTexture(textureMS); context.releaseTextureView(texView); context.releaseTexture(texture); height = 0; width = 0; } //***************************************************************************** // render target pool //***************************************************************************** WgRenderTarget* WgRenderTargetPool::allocate(WgContext& context) { WgRenderTarget* renderTarget{}; if (pool.count > 0) { renderTarget = pool.last(); pool.pop(); } else { renderTarget = new WgRenderTarget; renderTarget->initialize(context, width, height); list.push(renderTarget); } return renderTarget; }; void WgRenderTargetPool::free(WgContext& context, WgRenderTarget* renderTarget) { pool.push(renderTarget); }; void WgRenderTargetPool::initialize(WgContext& context, uint32_t width, uint32_t height) { this->width = width; this->height = height; } void WgRenderTargetPool::release(WgContext& context) { ARRAY_FOREACH(p, list) { (*p)->release(context); delete(*p); } list.clear(); pool.clear(); height = 0; width = 0; }; src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-type-error-thrower.inc.h000664 001750 001750 00000002044 15164251010 052411 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * [[ThrowTypeError]] description * * See also: ECMA-262 v5, 13.2.3 */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_FIXED) #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers-errol.cpp000664 001750 001750 00000015116 15164251010 045334 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is based on work under the following copyright and permission * notice: * * Copyright (c) 2016 Marc Andrysco * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "ecma-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ /** * Printing Floating-Point Numbers * * available at http://cseweb.ucsd.edu/~mandrysc/pub/dtoa.pdf */ /** * Floating point format definitions (next float value) */ #define ECMA_NEXT_FLOAT(value) (nextafter ((value), INFINITY)) /** * Floating point format definitions (previous float value) */ #define ECMA_PREV_FLOAT(value) (nextafter ((value), -INFINITY)) /** * Value of epsilon */ #define ERROL0_EPSILON 0.0000001 /** * High-precision data structure. */ typedef struct { double value; /**< value */ double offset; /**< offset */ } ecma_high_prec_t; /** * Normalize the number by factoring in the error. * * @return void */ static inline void ecma_normalize_high_prec_data (ecma_high_prec_t *hp_data_p) /**< [in, out] float pair */ { double val = hp_data_p->value; hp_data_p->value += hp_data_p->offset; hp_data_p->offset += val - hp_data_p->value; } /* ecma_normalize_high_prec_data */ /** * Multiply the high-precision number by ten. * * @return void */ static inline void ecma_multiply_high_prec_by_10 (ecma_high_prec_t *hp_data_p) /**< [in, out] high-precision number */ { double value = hp_data_p->value; hp_data_p->value *= 10.0; hp_data_p->offset *= 10.0; double offset = hp_data_p->value; offset -= value * 8.0; offset -= value * 2.0; hp_data_p->offset -= offset; ecma_normalize_high_prec_data (hp_data_p); } /* ecma_multiply_high_prec_by_10 */ /** * Divide the high-precision number by ten. */ static void ecma_divide_high_prec_by_10 (ecma_high_prec_t *hp_data_p) /**< [in, out] high-precision number */ { double value = hp_data_p->value; hp_data_p->value /= 10.0; hp_data_p->offset /= 10.0; value -= hp_data_p->value * 8.0; value -= hp_data_p->value * 2.0; hp_data_p->offset += value / 10.0; ecma_normalize_high_prec_data (hp_data_p); } /* ecma_divide_high_prec_by_10 */ /** * Errol0 double to ASCII conversion, guaranteed correct but possibly not optimal. * * @return number of generated digits */ lit_utf8_size_t ecma_errol0_dtoa (double val, /**< ecma number */ lit_utf8_byte_t *buffer_p, /**< buffer to generate digits into */ int32_t *exp_p) /**< [out] exponent */ { double power_of_10 = 1.0; int32_t exponent = 1; /* normalize the midpoint */ ecma_high_prec_t mid; mid.value = val; mid.offset = 0.0; while (((mid.value > 10.0) || ((mid.value == 10.0) && (mid.offset >= 0.0))) && (exponent < 308)) { exponent++; ecma_divide_high_prec_by_10 (&mid); power_of_10 /= 10.0; } while (((mid.value < 1.0) || ((mid.value == 1.0) && (mid.offset < 0.0))) && (exponent > -307)) { exponent--; ecma_multiply_high_prec_by_10 (&mid); power_of_10 *= 10.0; } ecma_high_prec_t high_bound, low_bound; high_bound.value = mid.value; high_bound.offset = mid.offset; if ((float) ECMA_NEXT_FLOAT (val) != (float) INFINITY) { high_bound.offset += (ECMA_NEXT_FLOAT (val) - val) * power_of_10 / (2.0 + ERROL0_EPSILON); } low_bound.value = mid.value; low_bound.offset = mid.offset + (ECMA_PREV_FLOAT (val) - val) * power_of_10 / (2.0 + ERROL0_EPSILON); ecma_normalize_high_prec_data (&high_bound); ecma_normalize_high_prec_data (&low_bound); /* normalized boundaries */ while (high_bound.value > 10.0 || (high_bound.value == 10.0 && (high_bound.offset >= 0.0))) { exponent++; ecma_divide_high_prec_by_10 (&high_bound); ecma_divide_high_prec_by_10 (&low_bound); } while (high_bound.value < 1.0 || (high_bound.value == 1.0 && (high_bound.offset < 0.0))) { exponent--; ecma_multiply_high_prec_by_10 (&high_bound); ecma_multiply_high_prec_by_10 (&low_bound); } /* digit generation */ lit_utf8_byte_t *dst_p = buffer_p; while (high_bound.value != 0.0 || high_bound.offset != 0.0) { uint8_t high_digit = (uint8_t) high_bound.value; if ((high_bound.value == high_digit) && (high_bound.offset < 0)) { high_digit = (uint8_t) (high_digit - 1u); } uint8_t low_digit = (uint8_t) low_bound.value; if ((low_bound.value == low_digit) && (low_bound.offset < 0)) { low_digit = (uint8_t) (low_digit - 1u); } if (low_digit != high_digit) { break; } *dst_p++ = (lit_utf8_byte_t) ('0' + high_digit); high_bound.value -= high_digit; ecma_multiply_high_prec_by_10 (&high_bound); low_bound.value -= low_digit; ecma_multiply_high_prec_by_10 (&low_bound); } double mdig = (high_bound.value + low_bound.value) / 2.0 + 0.5; *dst_p++ = (lit_utf8_byte_t) ('0' + (uint8_t) mdig); *exp_p = exponent; return (lit_utf8_size_t) (dst_p - buffer_p); } /* ecma_errol0_dtoa */ /** * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers-external-pointers.cpp000664 001750 001750 00000024355 15164251010 047701 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ /** * Create a native pointer property to store the native pointer and its type info. * * @return true - if property was just created with specified value, * false - otherwise, if property existed before the call, it's value was updated */ bool ecma_create_native_pointer_property (ecma_object_t *obj_p, /**< object to create property in */ void *native_p, /**< native pointer */ const jerry_object_native_info_t *native_info_p) /**< native type info */ { ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER); if (native_info_p != NULL && native_info_p->number_of_references > 0) { name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES); } if (ecma_op_object_is_fast_array (obj_p)) { ecma_fast_array_convert_to_normal (obj_p); } ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p); bool is_new = (property_p == NULL); ecma_native_pointer_t *native_pointer_p; if (property_p == NULL) { native_pointer_p = (ecma_native_pointer_t *) jmem_heap_alloc_block (sizeof (ecma_native_pointer_t)); ecma_property_value_t *value_p; ECMA_CREATE_INTERNAL_PROPERTY (obj_p, name_p, property_p, value_p); ECMA_SET_INTERNAL_VALUE_POINTER (value_p->value, native_pointer_p); *property_p |= ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL; } else if (*property_p & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL) { ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p); native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value_p->value); if (native_pointer_p->native_info_p == native_info_p) { native_pointer_p->native_p = native_p; return false; } value_p->value = JMEM_CP_NULL; (void) value_p->value; /* Make cppcheck happy. */ *property_p &= (ecma_property_t) ~ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL; ecma_native_pointer_chain_t *item_p; item_p = (ecma_native_pointer_chain_t *) jmem_heap_alloc_block (sizeof (ecma_native_pointer_chain_t)); item_p->data = *native_pointer_p; jmem_heap_free_block (native_pointer_p, sizeof (ecma_native_pointer_t)); item_p->next_p = (ecma_native_pointer_chain_t *) jmem_heap_alloc_block (sizeof (ecma_native_pointer_chain_t)); item_p->next_p->next_p = NULL; native_pointer_p = &item_p->next_p->data; ECMA_SET_INTERNAL_VALUE_POINTER (value_p->value, item_p); } else { ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (value_p->value == JMEM_CP_NULL) { native_pointer_p = (ecma_native_pointer_t *) jmem_heap_alloc_block (sizeof (ecma_native_pointer_t)); ECMA_SET_INTERNAL_VALUE_POINTER (value_p->value, native_pointer_p); *property_p |= ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL; } else { ecma_native_pointer_chain_t *item_p; item_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_chain_t, value_p->value); /* There should be at least 2 native pointers in the chain */ JERRY_ASSERT (item_p != NULL && item_p->next_p != NULL); while (true) { if (item_p->data.native_info_p == native_info_p) { /* The native info already exists -> update the corresponding data */ item_p->data.native_p = native_p; return false; } if (item_p->next_p == NULL) { /* The native info does not exist -> append a new element to the chain */ break; } item_p = item_p->next_p; } ecma_native_pointer_chain_t *new_item_p; new_item_p = (ecma_native_pointer_chain_t *) jmem_heap_alloc_block (sizeof (ecma_native_pointer_chain_t)); item_p->next_p = new_item_p; new_item_p->next_p = NULL; native_pointer_p = &new_item_p->data; } } native_pointer_p->native_p = native_p; native_pointer_p->native_info_p = (jerry_object_native_info_t *) native_info_p; return is_new; } /* ecma_create_native_pointer_property */ /** * Get value of native package stored in the object's property with specified identifier * * Note: * property identifier should be one of the following: * - LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER * * @return native pointer data if property exists * NULL otherwise */ ecma_native_pointer_t * ecma_get_native_pointer_value (ecma_object_t *obj_p, /**< object to get property value from */ const jerry_object_native_info_t *native_info_p) /**< native type info */ { if (ecma_op_object_is_fast_array (obj_p)) { /* Fast access mode array cannot have native pointer properties */ return NULL; } ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER); if (native_info_p != NULL && native_info_p->number_of_references > 0) { name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES); } ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p); if (property_p == NULL) { return NULL; } ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (JERRY_LIKELY (*property_p & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL)) { ecma_native_pointer_t *native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value_p->value); if (native_pointer_p->native_info_p == native_info_p) { return native_pointer_p; } return NULL; } if (value_p->value == JMEM_CP_NULL) { return NULL; } ecma_native_pointer_chain_t *item_p; item_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_chain_t, value_p->value); /* There should be at least 2 native pointers in the chain */ JERRY_ASSERT (item_p != NULL && item_p->next_p != NULL); do { if (item_p->data.native_info_p == native_info_p) { return &item_p->data; } item_p = item_p->next_p; } while (item_p != NULL); return NULL; } /* ecma_get_native_pointer_value */ /** * Delete the previously set native pointer by the native type info from the specified object. * * Note: * If the specified object has no matching native pointer for the given native type info * the function has no effect. * * @return true - if the native pointer has been deleted successfully * false - otherwise */ bool ecma_delete_native_pointer_property (ecma_object_t *obj_p, /**< object to delete property from */ const jerry_object_native_info_t *native_info_p) /**< native type info */ { if (ecma_op_object_is_fast_array (obj_p)) { /* Fast access mode array cannot have native pointer properties */ return false; } ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER); if (native_info_p != NULL && native_info_p->number_of_references > 0) { name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES); } ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p); if (property_p == NULL) { return false; } ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (JERRY_LIKELY (*property_p & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL)) { ecma_native_pointer_t *native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value_p->value); if (native_pointer_p->native_info_p != native_info_p) { return false; } value_p->value = JMEM_CP_NULL; *property_p &= (ecma_property_t) ~ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL; jmem_heap_free_block (native_pointer_p, sizeof (ecma_native_pointer_t)); return true; } if (value_p->value == JMEM_CP_NULL) { return false; } ecma_native_pointer_chain_t *first_p; first_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_chain_t, value_p->value); /* There should be at least 2 native pointers in the chain */ JERRY_ASSERT (first_p != NULL && first_p->next_p != NULL); ecma_native_pointer_chain_t *item_p = first_p; ecma_native_pointer_chain_t *prev_p = NULL; do { if (item_p->data.native_info_p == native_info_p) { if (prev_p == NULL) { /* The first element is deleted from the chain: change the property value. */ first_p = item_p->next_p; ECMA_SET_INTERNAL_VALUE_POINTER (value_p->value, first_p); } else { /* A non-first element is deleted from the chain: update the previous pointer. */ prev_p->next_p = item_p->next_p; } jmem_heap_free_block (item_p, sizeof (ecma_native_pointer_chain_t)); if (first_p->next_p != NULL) { return true; } /* Only one item remained. The ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL flag is * set early to avoid using the chain if the allocation below triggers a GC. */ *property_p |= ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL; ecma_native_pointer_t *native_pointer_p; native_pointer_p = (ecma_native_pointer_t *) jmem_heap_alloc_block (sizeof (ecma_native_pointer_t)); *native_pointer_p = first_p->data; ECMA_SET_INTERNAL_VALUE_POINTER (value_p->value, native_pointer_p); jmem_heap_free_block (first_p, sizeof (ecma_native_pointer_chain_t)); return true; } prev_p = item_p; item_p = item_p->next_p; } while (item_p != NULL); return false; } /* ecma_delete_native_pointer_property */ /** * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieParser.cpp000664 001750 001750 00000150376 15164251010 036534 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgStr.h" #include "tvgCompressor.h" #include "tvgLottieModel.h" #include "tvgLottieParser.h" #include "tvgLottieExpressions.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #define KEY_AS(name) !strcmp(key, name) static unsigned long _int2str(int num) { char str[20]; snprintf(str, 20, "%d", num); return djb2Encode(str); } void LottieParser::getExpression(char* code, LottieComposition* comp, LottieLayer* layer, LottieObject* object, LottieProperty* property) { if (!comp->expressions) comp->expressions = true; auto inst = new LottieExpression; inst->code = code; inst->comp = comp; inst->layer = layer; inst->object = object; inst->property = property; property->exp = inst; } LottieEffect* LottieParser::getEffect(int type) { switch (type) { case LottieEffect::Custom: return new LottieFxCustom; case LottieEffect::Tint: return new LottieFxTint; case LottieEffect::Fill: return new LottieFxFill; case LottieEffect::Stroke: return new LottieFxStroke; case LottieEffect::Tritone: return new LottieFxTritone; case LottieEffect::DropShadow: return new LottieFxDropShadow; case LottieEffect::GaussianBlur: return new LottieFxGaussianBlur; default: return nullptr; } } MaskMethod LottieParser::getMaskMethod(bool inversed) { auto mode = getString(); if (!mode) return MaskMethod::None; switch (mode[0]) { case 'a': { if (inversed) return MaskMethod::InvAlpha; else return MaskMethod::Add; } case 's': { if (inversed) return MaskMethod::Intersect; return MaskMethod::Subtract; } case 'i': { if (inversed) return MaskMethod::Difference; return MaskMethod::Intersect; } case 'f': { if (inversed) return MaskMethod::Intersect; return MaskMethod::Difference; } case 'l': return MaskMethod::Lighten; case 'd': return MaskMethod::Darken; default: return MaskMethod::None; } } RGB32 LottieParser::getColor(const char *str) { RGB32 color = {0, 0, 0}; if (!str) return color; auto len = strlen(str); // some resource has empty color string, return a default color for those cases. if (len != 7 || str[0] != '#') return color; char tmp[3] = {'\0', '\0', '\0'}; tmp[0] = str[1]; tmp[1] = str[2]; color.r = uint8_t(strtol(tmp, nullptr, 16)); tmp[0] = str[3]; tmp[1] = str[4]; color.g = uint8_t(strtol(tmp, nullptr, 16)); tmp[0] = str[5]; tmp[1] = str[6]; color.b = uint8_t(strtol(tmp, nullptr, 16)); return color; } bool LottieParser::getValue(TextDocument& doc) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("s")) doc.size = getFloat() * 0.01f; else if (KEY_AS("f")) doc.name = getStringCopy(); else if (KEY_AS("t")) doc.text = getStringCopy(); else if (KEY_AS("j")) { auto val = getInt(); if (val == 1) doc.justify = 1.0f; //right align else if (val == 2) doc.justify = 0.5f; //center align } else if (KEY_AS("ca")) doc.caps = getInt(); else if (KEY_AS("tr")) doc.tracking = getFloat() * 0.1f; else if (KEY_AS("lh")) doc.height = getFloat(); else if (KEY_AS("ls")) doc.shift = getFloat(); else if (KEY_AS("fc")) getValue(doc.color); else if (KEY_AS("ps")) getValue(doc.bbox.pos); else if (KEY_AS("sz")) getValue(doc.bbox.size); else if (KEY_AS("sc")) getValue(doc.stroke.color); else if (KEY_AS("sw")) doc.stroke.width = getFloat(); else if (KEY_AS("of")) doc.stroke.below = !getBool(); else skip(); } return false; } bool LottieParser::getValue(PathSet& path) { Array outs, ins, pts; bool closed = false; /* The shape object could be wrapped by a array if its part of the keyframe object */ auto arrayWrapper = (peekType() == kArrayType) ? true : false; if (arrayWrapper) enterArray(); enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("i")) getValue(ins); else if (KEY_AS("o")) getValue(outs); else if (KEY_AS("v")) getValue(pts); else if (KEY_AS("c")) closed = getBool(); else skip(); } //exit properly from the array if (arrayWrapper) nextArrayValue(); //valid path data? if (ins.empty() || outs.empty() || pts.empty()) return false; if (ins.count != outs.count || outs.count != pts.count) return false; //convert path auto out = outs.begin(); auto in = ins.begin(); auto pt = pts.begin(); //Store manipulated results RenderPath temp; //Reuse the buffers temp.pts.data = path.pts; temp.pts.reserved = path.ptsCnt; temp.cmds.data = path.cmds; temp.cmds.reserved = path.cmdsCnt; size_t extra = closed ? 3 : 0; temp.pts.reserve(pts.count * 3 + 1 + extra); temp.cmds.reserve(pts.count + 2); temp.moveTo(*pt); for (++pt, ++out, ++in; pt < pts.end(); ++pt, ++out, ++in) { temp.cubicTo(*(pt - 1) + *(out - 1), *pt + *in, *pt); } if (closed) { temp.cubicTo(pts.last() + outs.last(), pts.first() + ins.first(), pts.first()); temp.close(); } path.pts = temp.pts.data; path.cmds = temp.cmds.data; path.ptsCnt = temp.pts.count; path.cmdsCnt = temp.cmds.count; temp.pts.data = nullptr; temp.cmds.data = nullptr; return false; } bool LottieParser::getValue(ColorStop& color) { if (peekType() == kArrayType) { enterArray(); if (!nextArrayValue()) return true; } if (!color.input) color.input = new Array(static_cast(context.parent)->colorStops.count * 6); else color.input->clear(); do color.input->push(getFloat()); while (nextArrayValue()); return true; } bool LottieParser::getValue(Array& pts) { Point pt{}; enterArray(); while (nextArrayValue()) { enterArray(); getValue(pt); pts.push(pt); } return false; } bool LottieParser::getValue(int8_t& val) { if (peekType() == kArrayType) { enterArray(); if (nextArrayValue()) val = getInt(); //discard rest while (nextArrayValue()) getInt(); } else { val = (int8_t) getFloat(); } return false; } bool LottieParser::getValue(uint8_t& val) { if (peekType() == kArrayType) { enterArray(); if (nextArrayValue()) val = (uint8_t)(getFloat() * 2.55f); //discard rest while (nextArrayValue()) getFloat(); } else { val = (uint8_t)(getFloat() * 2.55f); } return false; } bool LottieParser::getValue(float& val) { if (peekType() == kArrayType) { enterArray(); if (nextArrayValue()) val = getFloat(); //discard rest while (nextArrayValue()) getFloat(); } else { val = getFloat(); } return false; } bool LottieParser::getValue(Point& pt) { if (peekType() == kNullType) return false; if (peekType() == kArrayType) { enterArray(); if (!nextArrayValue()) return false; } pt.x = getFloat(); pt.y = getFloat(); while (nextArrayValue()) getFloat(); //drop return true; } bool LottieParser::getValue(RGB32& color) { if (peekType() == kArrayType) { enterArray(); if (!nextArrayValue()) return false; } color.r = REMAP255(getFloat()); color.g = REMAP255(getFloat()); color.b = REMAP255(getFloat()); while (nextArrayValue()) getFloat(); //drop //TODO: color filter? return true; } void LottieParser::getInterpolatorPoint(Point& pt) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("x")) getValue(pt.x); else if (KEY_AS("y")) getValue(pt.y); } } template void LottieParser::parseSlotProperty(T& prop) { while (auto key = nextObjectKey()) { if (KEY_AS("p")) parseProperty(prop); else skip(); } } template bool LottieParser::parseTangent(const char *key, LottieVectorFrame& value) { if (KEY_AS("ti") && getValue(value.inTangent)) ; else if (KEY_AS("to") && getValue(value.outTangent)) ; else return false; value.hasTangent = true; return true; } template bool LottieParser::parseTangent(const char *key, LottieScalarFrame& value) { return false; } LottieInterpolator* LottieParser::getInterpolator(const char* key, Point& in, Point& out) { char buf[20]; if (!key) { snprintf(buf, sizeof(buf), "%.2f_%.2f_%.2f_%.2f", (double)in.x, (double)in.y, (double)out.x, (double)out.y); key = buf; } LottieInterpolator* interpolator = nullptr; //get a cached interpolator if it has any. ARRAY_FOREACH(p, comp->interpolators) { if (!strncmp((*p)->key, key, sizeof(buf))) interpolator = *p; } //new interpolator if (!interpolator) { interpolator = tvg::malloc(sizeof(LottieInterpolator)); interpolator->set(key, in, out); comp->interpolators.push(interpolator); } return interpolator; } template void LottieParser::parseKeyFrame(T& prop) { Point inTangent, outTangent; const char* interpolatorKey = nullptr; auto& frame = prop.newFrame(); auto interpolator = false; enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("i")) { interpolator = true; getInterpolatorPoint(inTangent); } else if (KEY_AS("o")) { getInterpolatorPoint(outTangent); } else if (KEY_AS("n")) { if (peekType() == kStringType) { interpolatorKey = getString(); } else { enterArray(); while (nextArrayValue()) { if (!interpolatorKey) interpolatorKey = getString(); else skip(); } } } else if (KEY_AS("t")) { frame.no = getFloat(); } else if (KEY_AS("s")) { getValue(frame.value); } else if (KEY_AS("e")) { //current end frame and the next start frame is duplicated, //We propagate the end value to the next frame to avoid having duplicated values. auto& frame2 = prop.nextFrame(); getValue(frame2.value); } else if (parseTangent(key, frame)) { continue; } else if (KEY_AS("h")) { frame.hold = getInt(); } else skip(); } if (interpolator) { frame.interpolator = getInterpolator(interpolatorKey, inTangent, outTangent); } } template void LottieParser::parsePropertyInternal(T& prop) { //single value property if (peekType() == kNumberType) { getValue(prop.value); //multi value property } else { enterArray(); while (nextArrayValue()) { if (peekType() == kObjectType) parseKeyFrame(prop); //keyframes value else if (getValue(prop.value)) break; //multi value property with no keyframes } prop.prepare(); } } void LottieParser::registerSlot(LottieObject* obj, const char* sid, LottieProperty& prop) { auto val = djb2Encode(sid); //append object if the slot already exists. ARRAY_FOREACH(p, comp->slots) { if ((*p)->sid != val) continue; (*p)->pairs.push({obj}); prop.sid = val; return; } comp->slots.push(new LottieSlot(context.layer, context.parent, val, obj, prop.type)); prop.sid = val; } template void LottieParser::parseProperty(T& prop, LottieObject* obj) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("k")) parsePropertyInternal(prop); else if (parseCommon(obj, prop, key)) continue; else skip(); } } bool LottieParser::parseCommon(LottieObject* obj, const char* key) { if (KEY_AS("nm")) { obj->id = djb2Encode(getString()); return true; } else if (KEY_AS("hd")) { obj->hidden = getBool(); return true; } else return false; } bool LottieParser::parseCommon(LottieObject* obj, LottieProperty& prop, const char* key) { if (KEY_AS("ix")) { prop.ix = getInt(); return true; } else if (KEY_AS("x") && expressions) { getExpression(getStringCopy(), comp, context.layer, context.parent, &prop); return true; } else if (KEY_AS("sid")) { registerSlot(obj, getString(), prop); return true; } else return false; } bool LottieParser::parseDirection(LottieShape* shape, const char* key) { if (KEY_AS("d")) { if (getInt() == 3) { shape->clockwise = false; //default is true } return true; } return false; } LottieRect* LottieParser::parseRect() { auto rect = new LottieRect; context.parent = rect; while (auto key = nextObjectKey()) { if (parseCommon(rect, key)) continue; else if (KEY_AS("s")) parseProperty(rect->size); else if (KEY_AS("p")) parseProperty(rect->position); else if (KEY_AS("r")) parseProperty(rect->radius); else if (parseDirection(rect, key)) continue; else skip(); } return rect; } LottieEllipse* LottieParser::parseEllipse() { auto ellipse = new LottieEllipse; context.parent = ellipse; while (auto key = nextObjectKey()) { if (parseCommon(ellipse, key)) continue; else if (KEY_AS("p")) parseProperty(ellipse->position); else if (KEY_AS("s")) parseProperty(ellipse->size); else if (parseDirection(ellipse, key)) continue; else skip(); } return ellipse; } LottieTransform* LottieParser::parseTransform(bool ddd) { auto transform = new LottieTransform; context.parent = transform; if (ddd) { transform->rotationEx = new LottieTransform::RotationEx; TVGLOG("LOTTIE", "3D transform(ddd) is not totally compatible."); } while (auto key = nextObjectKey()) { if (parseCommon(transform, key)) continue; else if (KEY_AS("p")) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("k")) parsePropertyInternal(transform->position); else if (KEY_AS("x")) //must be prior to parseCommon() { //check separateCoord to figure out whether "x(expression)" / "x(coord)" if (peekType() == kStringType) { if (expressions) getExpression(getStringCopy(), comp, context.layer, context.parent, &transform->position); else skip(); } else parseProperty(transform->separateCoord()->x); } else if (KEY_AS("y")) parseProperty(transform->separateCoord()->y); else if (parseCommon(transform, transform->position, key)) continue; else skip(); } } else if (KEY_AS("a")) parseProperty(transform->anchor); else if (KEY_AS("s")) parseProperty(transform->scale, transform); else if (KEY_AS("r")) parseProperty(transform->rotation, transform); else if (KEY_AS("o")) parseProperty(transform->opacity, transform); else if (transform->rotationEx && KEY_AS("rx")) parseProperty(transform->rotationEx->x); else if (transform->rotationEx && KEY_AS("ry")) parseProperty(transform->rotationEx->y); else if (transform->rotationEx && KEY_AS("rz")) parseProperty(transform->rotation); else if (KEY_AS("sk")) parseProperty(transform->skewAngle, transform); else if (KEY_AS("sa")) parseProperty(transform->skewAxis, transform); else skip(); } return transform; } LottieSolidFill* LottieParser::parseSolidFill() { auto fill = new LottieSolidFill; context.parent = fill; while (auto key = nextObjectKey()) { if (parseCommon(fill, key)) continue; else if (KEY_AS("c")) parseProperty(fill->color, fill); else if (KEY_AS("o")) parseProperty(fill->opacity, fill); else if (KEY_AS("fillEnabled")) fill->hidden |= !getBool(); else if (KEY_AS("r")) fill->rule = (getInt() == 1) ? FillRule::NonZero : FillRule::EvenOdd; else skip(); } return fill; } void LottieParser::parseStrokeDash(LottieStroke* stroke) { enterArray(); while (nextArrayValue()) { enterObject(); const char* style = nullptr; while (auto key = nextObjectKey()) { if (KEY_AS("n")) style = getString(); else if (KEY_AS("v")) { if (style && !strcmp("o", style)) parseProperty(stroke->dashOffset()); else parseProperty(stroke->dashValue()); } else skip(); } } } LottieSolidStroke* LottieParser::parseSolidStroke() { auto stroke = new LottieSolidStroke; context.parent = stroke; while (auto key = nextObjectKey()) { if (parseCommon(stroke, key)) continue; else if (KEY_AS("c")) parseProperty(stroke->color, stroke); else if (KEY_AS("o")) parseProperty(stroke->opacity, stroke); else if (KEY_AS("w")) parseProperty(stroke->width, stroke); else if (KEY_AS("lc")) stroke->cap = (StrokeCap) (getInt() - 1); else if (KEY_AS("lj")) stroke->join = (StrokeJoin) (getInt() - 1); else if (KEY_AS("ml")) stroke->miterLimit = getFloat(); else if (KEY_AS("fillEnabled")) stroke->hidden |= !getBool(); else if (KEY_AS("d")) parseStrokeDash(stroke); else skip(); } return stroke; } void LottieParser::getPathSet(LottiePath* obj, LottiePathSet& path) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("k")) { if (peekType() == kArrayType) { enterArray(); while (nextArrayValue()) parseKeyFrame(path); } else { getValue(path.value); } } else if (parseCommon(obj, path, key)) continue; else skip(); } } LottiePath* LottieParser::parsePath() { auto path = new LottiePath; while (auto key = nextObjectKey()) { if (parseCommon(path, key)) continue; else if (KEY_AS("ks")) getPathSet(path, path->pathset); else if (parseDirection(path, key)) continue; else skip(); } return path; } LottiePolyStar* LottieParser::parsePolyStar() { auto star = new LottiePolyStar; context.parent = star; while (auto key = nextObjectKey()) { if (parseCommon(star, key)) continue; else if (KEY_AS("p")) parseProperty(star->position); else if (KEY_AS("pt")) parseProperty(star->ptsCnt); else if (KEY_AS("ir")) parseProperty(star->innerRadius); else if (KEY_AS("is")) parseProperty(star->innerRoundness); else if (KEY_AS("or")) parseProperty(star->outerRadius); else if (KEY_AS("os")) parseProperty(star->outerRoundness); else if (KEY_AS("r")) parseProperty(star->rotation); else if (KEY_AS("sy")) star->type = (LottiePolyStar::Type) getInt(); else if (parseDirection(star, key)) continue; else skip(); } return star; } LottieRoundedCorner* LottieParser::parseRoundedCorner() { auto corner = new LottieRoundedCorner; context.parent = corner; while (auto key = nextObjectKey()) { if (parseCommon(corner, key)) continue; else if (KEY_AS("r")) parseProperty(corner->radius); else skip(); } return corner; } void LottieParser::parseColorStop(LottieGradient* gradient) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("p")) gradient->colorStops.count = getInt(); else if (KEY_AS("k")) parseProperty(gradient->colorStops, gradient); else if (parseCommon(gradient, gradient->colorStops, key)) continue; else skip(); } } void LottieParser::parseGradient(LottieGradient* gradient, const char* key) { if (KEY_AS("t")) gradient->id = getInt(); else if (KEY_AS("o")) parseProperty(gradient->opacity, gradient); else if (KEY_AS("g")) parseColorStop(gradient); else if (KEY_AS("s")) parseProperty(gradient->start, gradient); else if (KEY_AS("e")) parseProperty(gradient->end, gradient); else if (KEY_AS("h")) parseProperty(gradient->height, gradient); else if (KEY_AS("a")) parseProperty(gradient->angle, gradient); else skip(); } LottieGradientFill* LottieParser::parseGradientFill() { auto fill = new LottieGradientFill; context.parent = fill; while (auto key = nextObjectKey()) { if (parseCommon(fill, key)) continue; else if (KEY_AS("r")) fill->rule = (getInt() == 1) ? FillRule::NonZero : FillRule::EvenOdd; else parseGradient(fill, key); } fill->prepare(); return fill; } LottieGradientStroke* LottieParser::parseGradientStroke() { auto stroke = new LottieGradientStroke; context.parent = stroke; while (auto key = nextObjectKey()) { if (parseCommon(stroke, key)) continue; else if (KEY_AS("lc")) stroke->cap = (StrokeCap) (getInt() - 1); else if (KEY_AS("lj")) stroke->join = (StrokeJoin) (getInt() - 1); else if (KEY_AS("ml")) stroke->miterLimit = getFloat(); else if (KEY_AS("w")) parseProperty(stroke->width); else if (KEY_AS("d")) parseStrokeDash(stroke); else parseGradient(stroke, key); } stroke->prepare(); return stroke; } LottieTrimpath* LottieParser::parseTrimpath() { auto trim = new LottieTrimpath; context.parent = trim; while (auto key = nextObjectKey()) { if (parseCommon(trim, key)) continue; else if (KEY_AS("s")) parseProperty(trim->start); else if (KEY_AS("e")) parseProperty(trim->end); else if (KEY_AS("o")) parseProperty(trim->offset); else if (KEY_AS("m")) trim->type = static_cast(getInt()); else skip(); } return trim; } LottieRepeater* LottieParser::parseRepeater() { auto repeater = new LottieRepeater; context.parent = repeater; while (auto key = nextObjectKey()) { if (parseCommon(repeater, key)) continue; else if (KEY_AS("c")) parseProperty(repeater->copies); else if (KEY_AS("o")) parseProperty(repeater->offset); else if (KEY_AS("m")) repeater->inorder = getInt() == 2; else if (KEY_AS("tr")) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("a")) parseProperty(repeater->anchor); else if (KEY_AS("p")) parseProperty(repeater->position); else if (KEY_AS("r")) parseProperty(repeater->rotation); else if (KEY_AS("s")) parseProperty(repeater->scale); else if (KEY_AS("so")) parseProperty(repeater->startOpacity); else if (KEY_AS("eo")) parseProperty(repeater->endOpacity); else skip(); } } else skip(); } return repeater; } LottieOffsetPath* LottieParser::parseOffsetPath() { auto offsetPath = new LottieOffsetPath; context.parent = offsetPath; while (auto key = nextObjectKey()) { if (parseCommon(offsetPath, key)) continue; else if (KEY_AS("a")) parseProperty(offsetPath->offset); else if (KEY_AS("lj")) offsetPath->join = (StrokeJoin) (getInt() - 1); else if (KEY_AS("ml")) parseProperty(offsetPath->miterLimit); else skip(); } return offsetPath; } LottieObject* LottieParser::parseObject(const char* type) { if (!strcmp(type, "gr")) return parseGroup(); else if (!strcmp(type, "rc")) return parseRect(); else if (!strcmp(type, "el")) return parseEllipse(); else if (!strcmp(type, "tr")) return parseTransform(); else if (!strcmp(type, "fl")) return parseSolidFill(); else if (!strcmp(type, "st")) return parseSolidStroke(); else if (!strcmp(type, "sh")) return parsePath(); else if (!strcmp(type, "sr")) return parsePolyStar(); else if (!strcmp(type, "rd")) return parseRoundedCorner(); else if (!strcmp(type, "gf")) return parseGradientFill(); else if (!strcmp(type, "gs")) return parseGradientStroke(); else if (!strcmp(type, "tm")) return parseTrimpath(); else if (!strcmp(type, "rp")) return parseRepeater(); else if (!strcmp(type, "mm")) TVGLOG("LOTTIE", "MergePath(mm) is not supported yet"); else if (!strcmp(type, "pb")) TVGLOG("LOTTIE", "Puker/Bloat(pb) is not supported yet"); else if (!strcmp(type, "tw")) TVGLOG("LOTTIE", "Twist(tw) is not supported yet"); else if (!strcmp(type, "op")) return parseOffsetPath(); else if (!strcmp(type, "zz")) TVGLOG("LOTTIE", "ZigZag(zz) is not supported yet"); return nullptr; } //capture the type name if the upcoming type is irregularly addressed after the actual properties. char* LottieParser::captureType() { if (!isPrimitive() || !strcmp(val.GetString(), "ty")) return nullptr; auto level = 0; for (auto p = getPos(); *p != '\0'; ++p) { if (*p == '{') level++; else if (*p == '}') { if (--level < 0) break; } else if (level == 0) { if (!strncmp(p, "\"ty\"", 4)) { p += 4; while (*p != '\0' && (isspace(*p) || *p == '\n')) ++p; if (*p++ != ':') return nullptr; while (*p != '\0' && (isspace(*p) || *p == '\n')) ++p; if (*p++ != '\"') return nullptr; const char* start = p; while (*p != '\0' && *p != '\"') ++p; if (*p == '\"') return tvg::duplicate(start, p - start); return nullptr; } } } return nullptr; } void LottieParser::parseObject(Array& parent) { enterObject(); auto type = captureType(); auto freeType = false; if (!type) { if (auto key = nextObjectKey()) { if (KEY_AS("ty")) type = (char*)getString(); else skip(); } } else freeType = true; if (type) { if (auto child = parseObject(type)) { if (child->hidden) delete(child); else parent.push(child); } } if (freeType) tvg::free(type); while(nextObjectKey()) skip(); } void LottieParser::parseImage(LottieImage* image, const char* data, const char* subPath, bool embedded, float width, float height) { //embedded image resource. should start with "data:" //header look like "data:image/png;base64," so need to skip till ','. if (embedded && !strncmp(data, "data:", 5)) { //figure out the mimetype auto mimeType = data + 11; auto needle = strstr(mimeType, ";"); image->bitmap.mimeType = duplicate(mimeType, needle - mimeType); //b64 data auto b64Data = strstr(data, ",") + 1; size_t length = strlen(data) - (b64Data - data); image->bitmap.size = b64Decode(b64Data, length, &image->bitmap.data); //external image resource } else { auto len = strlen(dirName) + strlen(subPath) + strlen(data) + 2; image->bitmap.path = tvg::malloc(len); snprintf(image->bitmap.path, len, "%s/%s%s", dirName, subPath, data); } image->bitmap.width = width; image->bitmap.height = height; image->prepare(); } LottieObject* LottieParser::parseAsset() { enterObject(); LottieObject* obj = nullptr; unsigned long id = 0; //Used for Image Asset const char* sid = nullptr; const char* data = nullptr; const char* subPath = nullptr; float width = 0.0f; float height = 0.0f; auto embedded = false; while (auto key = nextObjectKey()) { if (KEY_AS("id")) { if (peekType() == kStringType) { id = djb2Encode(getString()); } else { id = _int2str(getInt()); } } else if (KEY_AS("layers")) obj = parseLayers(comp->root); else if (KEY_AS("u")) subPath = getString(); else if (KEY_AS("p")) data = getString(); else if (KEY_AS("w")) width = getFloat(); else if (KEY_AS("h")) height = getFloat(); else if (KEY_AS("e")) embedded = getInt(); else if (KEY_AS("sid")) sid = getString(); else skip(); } if (data) { obj = new LottieImage; parseImage(static_cast(obj), data, subPath, embedded, width, height); if (sid) registerSlot(obj, sid, static_cast(obj)->bitmap); } if (obj) obj->id = id; return obj; } void LottieParser::parseFontData(LottieFont* font, const char* data) { if (!data) return; //handle base64 font data if (!strncmp(data, "data:font/", sizeof("data:font/") - 1)) { data += sizeof("data:font/") - 1; if (!strncmp(data, "ttf", 3)) { data += 3; } else { TVGLOG("LOTTIE", "TODO: Support a new font type!"); return; } data += sizeof(";base64,") - 1; font->size = b64Decode(data, strlen(data), &font->b64src); //external font resource } else { font->path = duplicate(data); } } LottieFont* LottieParser::parseFont() { enterObject(); auto font = new LottieFont; while (auto key = nextObjectKey()) { if (KEY_AS("fName")) font->name = getStringCopy(); else if (KEY_AS("fFamily")) font->family = getStringCopy(); else if (KEY_AS("fStyle")) font->style = getStringCopy(); else if (KEY_AS("fPath")) parseFontData(font, getString()); else if (KEY_AS("ascent")) font->ascent = getFloat(); else if (KEY_AS("origin")) font->origin = (LottieFont::Origin) getInt(); else skip(); } font->prepare(); return font; } void LottieParser::parseAssets() { enterArray(); while (nextArrayValue()) { auto asset = parseAsset(); if (asset) comp->assets.push(asset); else TVGERR("LOTTIE", "Invalid Asset!"); } } LottieMarker* LottieParser::parseMarker() { enterObject(); auto marker = new LottieMarker; while (auto key = nextObjectKey()) { if (KEY_AS("cm")) marker->name = getStringCopy(); else if (KEY_AS("tm")) marker->time = getFloat(); else if (KEY_AS("dr")) marker->duration = getFloat(); else skip(); } return marker; } void LottieParser::parseMarkers() { enterArray(); while (nextArrayValue()) { comp->markers.push(parseMarker()); } } void LottieParser::parseChars(Array& glyphs) { enterArray(); while (nextArrayValue()) { enterObject(); //a new glyph auto glyph = new LottieGlyph; while (auto key = nextObjectKey()) { if (KEY_AS("ch")) glyph->code = getStringCopy(); else if (KEY_AS("size")) glyph->size = static_cast(getFloat()); else if (KEY_AS("style")) glyph->style = getStringCopy(); else if (KEY_AS("w")) glyph->width = getFloat(); else if (KEY_AS("fFamily")) glyph->family = getStringCopy(); else if (KEY_AS("data")) { //glyph shapes enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("shapes")) parseShapes(glyph->children); } } else skip(); } glyph->prepare(); glyphs.push(glyph); } } void LottieParser::parseFonts() { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("list")) { enterArray(); while (nextArrayValue()) { comp->fonts.push(parseFont()); } } else skip(); } } LottieObject* LottieParser::parseGroup() { auto group = new LottieGroup; while (auto key = nextObjectKey()) { if (parseCommon(group, key)) continue; else if (KEY_AS("it")) { enterArray(); while (nextArrayValue()) parseObject(group->children); } else if (KEY_AS("bm")) { group->blendMethod = (BlendMethod) getInt(); } else skip(); } group->prepare(); return group; } void LottieParser::parseTimeRemap(LottieLayer* layer) { parseProperty(layer->timeRemap); } void LottieParser::parseShapes(Array& parent) { enterArray(); while (nextArrayValue()) parseObject(parent); } void LottieParser::parseTextAlignmentOption(LottieText* text) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("g")) text->alignOp.group = (LottieText::AlignOption::Group) getInt(); else if (KEY_AS("a")) parseProperty(text->alignOp.anchor); else skip(); } } void LottieParser::parseTextRange(LottieText* text) { enterArray(); while (nextArrayValue()) { enterObject(); auto selector = new LottieTextRange; context.parent = selector; while (auto key = nextObjectKey()) { if (KEY_AS("s")) { // text range selector enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("t")) selector->expressible = (bool) getInt(); else if (KEY_AS("xe")) { parseProperty(selector->maxEase); selector->interpolator = tvg::malloc(sizeof(LottieInterpolator)); } else if (KEY_AS("ne")) parseProperty(selector->minEase); else if (KEY_AS("a")) parseProperty(selector->maxAmount); else if (KEY_AS("b")) selector->based = (LottieTextRange::Based) getInt(); else if (KEY_AS("rn")) selector->random = getInt() ? rand() : 0; else if (KEY_AS("sh")) selector->shape = (LottieTextRange::Shape) getInt(); else if (KEY_AS("o")) parseProperty(selector->offset); else if (KEY_AS("r")) selector->rangeUnit = (LottieTextRange::Unit) getInt(); else if (KEY_AS("sm")) parseProperty(selector->smoothness); else if (KEY_AS("s")) parseProperty(selector->start); else if (KEY_AS("e")) parseProperty(selector->end); else skip(); } } else if (KEY_AS("a")) { // text style enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("t")) parseProperty(selector->style.letterSpace, selector); else if (KEY_AS("ls")) parseProperty(selector->style.lineSpace, selector); else if (KEY_AS("fc")) { parseProperty(selector->style.fillColor, selector); selector->style.flags.fillColor = true; } else if (KEY_AS("fo")) parseProperty(selector->style.fillOpacity, selector); else if (KEY_AS("sw")) { parseProperty(selector->style.strokeWidth, selector); selector->style.flags.strokeWidth = true; } else if (KEY_AS("sc")) { parseProperty(selector->style.strokeColor, selector); selector->style.flags.strokeColor = true; } else if (KEY_AS("so")) parseProperty(selector->style.strokeOpacity, selector); else if (KEY_AS("o")) parseProperty(selector->style.opacity, selector); else if (KEY_AS("p")) parseProperty(selector->style.position, selector); else if (KEY_AS("s")) parseProperty(selector->style.scale, selector); else if (KEY_AS("r")) parseProperty(selector->style.rotation, selector); else skip(); } } else skip(); } text->ranges.push(selector); } } void LottieParser::parseTextFollowPath(LottieText* text) { enterObject(); auto key = nextObjectKey(); if (!key) return; if (!text->follow) text->follow = new LottieTextFollowPath; do { if (KEY_AS("m")) text->follow->maskIdx = getInt(); else if (KEY_AS("f")) parseProperty(text->follow->firstMargin); else skip(); } while ((key = nextObjectKey())); } void LottieParser::parseText(Array& parent) { enterObject(); auto text = new LottieText; while (auto key = nextObjectKey()) { if (KEY_AS("d")) parseProperty(text->doc, text); else if (KEY_AS("a")) parseTextRange(text); else if (KEY_AS("m")) parseTextAlignmentOption(text); else if (KEY_AS("p")) parseTextFollowPath(text); else skip(); } parent.push(text); } void LottieParser::getLayerSize(float& val) { if (val == 0.0f) { val = getFloat(); } else { //layer might have both w(width) & sw(solid color width) //override one if the a new size is smaller. auto w = getFloat(); if (w < val) val = w; } } LottieMask* LottieParser::parseMask() { auto mask = new LottieMask; enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("inv")) mask->inverse = getBool(); else if (KEY_AS("mode")) mask->method = getMaskMethod(mask->inverse); else if (KEY_AS("pt")) getPathSet(nullptr, mask->pathset); else if (KEY_AS("o")) parseProperty(mask->opacity); else if (KEY_AS("x")) parseProperty(mask->expand); else skip(); } return mask; } void LottieParser::parseMasks(LottieLayer* layer) { enterArray(); while (nextArrayValue()) { if (auto mask = parseMask()) { layer->masks.push(mask); } } } bool LottieParser::parseEffect(LottieEffect* effect, void(LottieParser::*func)(LottieEffect*, int)) { //custom effect expects dynamic property allocations auto custom = (effect->type == LottieEffect::Custom) ? true : false; LottieFxCustom::Property* property = nullptr; enterArray(); int idx = 0; while (nextArrayValue()) { enterObject(); while (auto key = nextObjectKey()) { if (custom && KEY_AS("ty")) property = static_cast(effect)->property(getInt()); else if (KEY_AS("v")) { if (peekType() == kObjectType) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("k")) (this->*func)(effect, idx++); else skip(); } } else (this->*func)(effect, idx++); } else if (property && KEY_AS("nm")) property->nm = djb2Encode(getString()); else if (property && KEY_AS("mn")) property->mn = djb2Encode(getString()); else skip(); } } return true; } void LottieParser::parseCustom(LottieEffect* effect, int idx) { if ((uint32_t)idx >= static_cast(effect)->props.count) { TVGERR("LOTTIE", "Parsing error in Custom effect!"); skip(); return; } auto prop = static_cast(effect)->props[idx].property; switch (prop->type) { case LottieProperty::Type::Integer: { parsePropertyInternal(*static_cast(prop)); break; } case LottieProperty::Type::Float: { parsePropertyInternal(*static_cast(prop)); break; } case LottieProperty::Type::Vector: { parsePropertyInternal(*static_cast(prop)); break; } case LottieProperty::Type::Color: { parsePropertyInternal(*static_cast(prop)); break; } default: { TVGLOG("LOTTIE", "Missing Property Type? = %d", (int) prop->type); skip(); } } } void LottieParser::parseTint(LottieEffect* effect, int idx) { auto tint = static_cast(effect); if (idx == 0) parsePropertyInternal(tint->black); else if (idx == 1) parsePropertyInternal(tint->white); else if (idx == 2) parsePropertyInternal(tint->intensity); else skip(); } void LottieParser::parseTritone(LottieEffect* effect, int idx) { auto tritone = static_cast(effect); if (idx == 0) parsePropertyInternal(tritone->bright); else if (idx == 1) parsePropertyInternal(tritone->midtone); else if (idx == 2) parsePropertyInternal(tritone->dark); else if (idx == 3) parsePropertyInternal(tritone->blend); else skip(); } void LottieParser::parseFill(LottieEffect* effect, int idx) { auto fill = static_cast(effect); if (idx == 2) parsePropertyInternal(fill->color); else if (idx == 6) parsePropertyInternal(fill->opacity); else skip(); } void LottieParser::parseGaussianBlur(LottieEffect* effect, int idx) { auto blur = static_cast(effect); if (idx == 0) parsePropertyInternal(blur->blurness); else if (idx == 1) parsePropertyInternal(blur->direction); else if (idx == 2) parsePropertyInternal(blur->wrap); else skip(); } void LottieParser::parseDropShadow(LottieEffect* effect, int idx) { auto shadow = static_cast(effect); if (idx == 0) parsePropertyInternal(shadow->color); else if (idx == 1) parsePropertyInternal(shadow->opacity); else if (idx == 2) parsePropertyInternal(shadow->angle); else if (idx == 3) parsePropertyInternal(shadow->distance); else if (idx == 4) parsePropertyInternal(shadow->blurness); else skip(); } void LottieParser::parseStroke(LottieEffect* effect, int idx) { auto stroke = static_cast(effect); if (idx == 0) parsePropertyInternal(stroke->mask); else if (idx == 1) parsePropertyInternal(stroke->allMask); else if (idx == 3) parsePropertyInternal(stroke->color); else if (idx == 4) parsePropertyInternal(stroke->size); else if (idx == 6) parsePropertyInternal(stroke->opacity); else if (idx == 7) parsePropertyInternal(stroke->begin); else if (idx == 8) parsePropertyInternal(stroke->end); else skip(); } bool LottieParser::parseEffect(LottieEffect* effect) { switch (effect->type) { case LottieEffect::Custom: return parseEffect(effect, &LottieParser::parseCustom); case LottieEffect::Tint: return parseEffect(effect, &LottieParser::parseTint); case LottieEffect::Fill: return parseEffect(effect, &LottieParser::parseFill); case LottieEffect::Stroke: return parseEffect(effect, &LottieParser::parseStroke); case LottieEffect::Tritone: return parseEffect(effect, &LottieParser::parseTritone); case LottieEffect::DropShadow: return parseEffect(effect, &LottieParser::parseDropShadow); case LottieEffect::GaussianBlur: return parseEffect(effect, &LottieParser::parseGaussianBlur); default: return false; } } void LottieParser::parseEffects(LottieLayer* layer) { enterArray(); while (nextArrayValue()) { LottieEffect* effect = nullptr; auto invalid = true; enterObject(); while (auto key = nextObjectKey()) { //type must be prioritized. if (KEY_AS("ty")) { effect = getEffect(getInt()); if (!effect) break; else invalid = false; } else if (effect && KEY_AS("nm")) effect->nm = djb2Encode(getString()); else if (effect && KEY_AS("mn")) effect->mn = djb2Encode(getString()); else if (effect && KEY_AS("ix")) effect->ix = getInt(); else if (effect && KEY_AS("en")) effect->enable = getInt(); else if (effect && KEY_AS("ef")) parseEffect(effect); else skip(); } //TODO: remove when all effects were guaranteed. if (invalid) { TVGLOG("LOTTIE", "Not supported Layer Effect = %d", effect ? (int)effect->type : -1); while (nextObjectKey()) skip(); } else layer->effects.push(effect); } } LottieLayer* LottieParser::parseLayer(LottieLayer* precomp) { auto layer = new LottieLayer; layer->comp = precomp; context.layer = layer; auto ddd = false; RGB32 color; enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("nm")) { layer->name = getStringCopy(); layer->id = djb2Encode(layer->name); } else if (KEY_AS("ddd")) ddd = getInt(); //3d layer else if (KEY_AS("ind")) layer->ix = getInt(); else if (KEY_AS("ty")) layer->type = (LottieLayer::Type) getInt(); else if (KEY_AS("sr")) layer->timeStretch = getFloat(); else if (KEY_AS("ks")) { enterObject(); layer->transform = parseTransform(ddd); } else if (KEY_AS("ao")) layer->autoOrient = getInt(); else if (KEY_AS("shapes")) parseShapes(layer->children); else if (KEY_AS("ip")) layer->inFrame = getFloat(); else if (KEY_AS("op")) layer->outFrame = getFloat(); else if (KEY_AS("st")) layer->startFrame = getFloat(); else if (KEY_AS("bm")) layer->blendMethod = (BlendMethod) getInt(); else if (KEY_AS("parent")) layer->pix = getInt(); else if (KEY_AS("tm")) parseTimeRemap(layer); else if (KEY_AS("w") || KEY_AS("sw")) getLayerSize(layer->w); else if (KEY_AS("h") || KEY_AS("sh")) getLayerSize(layer->h); else if (KEY_AS("sc")) color = getColor(getString()); else if (KEY_AS("tt")) layer->matteType = (MaskMethod) getInt(); else if (KEY_AS("tp")) layer->mix = getInt(); else if (KEY_AS("masksProperties")) parseMasks(layer); else if (KEY_AS("hd")) layer->hidden = getBool(); else if (KEY_AS("refId")) layer->rid = djb2Encode(getString()); else if (KEY_AS("td")) layer->matteSrc = getInt(); //used for matte layer else if (KEY_AS("t")) parseText(layer->children); else if (KEY_AS("ef")) parseEffects(layer); else skip(); } layer->prepare(&color); return layer; } LottieLayer* LottieParser::parseLayers(LottieLayer* root) { auto precomp = new LottieLayer; precomp->type = LottieLayer::Precomp; precomp->comp = root; enterArray(); while (nextArrayValue()) { precomp->children.push(parseLayer(precomp)); } precomp->prepare(); return precomp; } void LottieParser::postProcess(Array& glyphs) { //aggregate font characters for (uint32_t g = 0; g < glyphs.count; ++g) { auto glyph = glyphs[g]; for (uint32_t i = 0; i < comp->fonts.count; ++i) { auto& font = comp->fonts[i]; if (!strcmp(font->family, glyph->family) && !strcmp(font->style, glyph->style)) { font->chars.push(glyph); tvg::free(glyph->family); tvg::free(glyph->style); break; } } } } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ const char* LottieParser::sid(bool first) { if (first) { //verify json if (!parseNext()) return nullptr; enterObject(); } return nextObjectKey(); } LottieProperty* LottieParser::parse(LottieSlot* slot) { enterObject(); LottieProperty* prop = nullptr; context = {slot->context.layer, slot->context.parent}; switch (slot->type) { case LottieProperty::Type::Float: { prop = new LottieFloat; parseSlotProperty(*static_cast(prop)); break; } case LottieProperty::Type::Scalar: { prop = new LottieScalar; parseSlotProperty(*static_cast(prop)); break; } case LottieProperty::Type::Vector: { prop = new LottieVector; parseSlotProperty(*static_cast(prop)); break; } case LottieProperty::Type::Opacity: { prop = new LottieOpacity; parseSlotProperty(*static_cast(prop)); break; } case LottieProperty::Type::Color: { prop = new LottieColor; parseSlotProperty(*static_cast(prop)); break; } case LottieProperty::Type::ColorStop: { auto obj = new LottieGradient; while (auto key = nextObjectKey()) { if (KEY_AS("p")) parseColorStop(obj); else skip(); } obj->prepare(); prop = new LottieColorStop(obj->colorStops); delete(obj); break; } case LottieProperty::Type::TextDoc: { prop = new LottieTextDoc; parseSlotProperty(*static_cast(prop)); break; } case LottieProperty::Type::Image: { LottieObject* obj = nullptr; while (auto key = nextObjectKey()) { if (KEY_AS("p")) obj = parseAsset(); else skip(); } if (!obj) return nullptr; prop = new LottieBitmap(static_cast(obj)->bitmap); delete(obj); break; } default: break; } prop->sid = slot->sid; return prop; } void LottieParser::captureSlots(const char* key) { tvg::free(slots); // TODO: Replace with immediate parsing, once the slot spec is confirmed by the LAC auto begin = getPos(); auto end = getPos(); auto depth = 1; auto invalid = true; //get slots string while (++end) { if (*end == '}') { --depth; if (depth == 0) { invalid = false; break; } } else if (*end == '{') { ++depth; } } if (invalid) { TVGERR("LOTTIE", "Invalid Slots!"); skip(); return; } //composite '{' + slots + '}' auto len = (end - begin + 2); slots = tvg::malloc(sizeof(char) * len + 1); slots[0] = '{'; memcpy(slots + 1, begin, len); slots[len] = '\0'; skip(); } bool LottieParser::parse() { //verify json. if (!parseNext()) return false; enterObject(); if (comp) delete(comp); comp = new LottieComposition; Array glyphs; auto startFrame = 0.0f; auto endFrame = 0.0f; while (auto key = nextObjectKey()) { if (KEY_AS("v")) comp->version = getStringCopy(); else if (KEY_AS("fr")) comp->frameRate = getFloat(); else if (KEY_AS("ip")) startFrame = getFloat(); else if (KEY_AS("op")) endFrame = getFloat(); else if (KEY_AS("w")) comp->w = getFloat(); else if (KEY_AS("h")) comp->h = getFloat(); else if (KEY_AS("nm")) comp->name = getStringCopy(); else if (KEY_AS("assets")) parseAssets(); else if (KEY_AS("layers")) comp->root = parseLayers(comp->root); else if (KEY_AS("fonts")) parseFonts(); else if (KEY_AS("chars")) parseChars(glyphs); else if (KEY_AS("markers")) parseMarkers(); else if (KEY_AS("slots")) captureSlots(key); else skip(); } if (Invalid() || !comp->root) { delete(comp); return false; } comp->root->inFrame = startFrame; comp->root->outFrame = endFrame; postProcess(glyphs); return true; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/jpg/tvgJpgd.h000664 001750 001750 00000003070 15164251010 033773 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ // jpgd.h - C++ class for JPEG decompression. // Public domain, Rich Geldreich #ifndef _TVG_JPGD_H_ #define _TVG_JPGD_H_ class jpeg_decoder; jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height); jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height); unsigned char* jpgdDecompress(jpeg_decoder* decoder, ColorSpace cs); void jpgdDelete(jpeg_decoder* decoder); #endif //_TVG_JPGD_H_ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray.cpp000664 001750 001750 00000013763 15164251010 052756 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-arraybuffer-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-typedarray.inc.h" #define BUILTIN_UNDERSCORED_ID typedarray #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup typedarray ECMA %TypedArray% object built-in * @{ */ /** * The %TypedArray%.from routine * * See also: * ES2015 22.2.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_from (ecma_value_t this_arg, /**< 'this' argument */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); if (!ecma_is_constructor (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_CONSTRUCTOR); } ecma_value_t source; ecma_value_t map_fn = ECMA_VALUE_UNDEFINED; ecma_value_t this_in_fn = ECMA_VALUE_UNDEFINED; if (arguments_list_len == 0) { return ecma_raise_type_error (ECMA_ERR_NO_SOURCE_ARGUMENT); } source = arguments_list_p[0]; if (arguments_list_len > 1) { map_fn = arguments_list_p[1]; if (!ecma_op_is_callable (map_fn)) { return ecma_raise_type_error (ECMA_ERR_THE_MAPFN_ARGUMENT_IS_NOT_CALLABLE); } if (arguments_list_len > 2) { this_in_fn = arguments_list_p[2]; } } return ecma_op_typedarray_from (this_arg, source, map_fn, this_in_fn); } /* ecma_builtin_typedarray_from */ /** * The %TypedArray%.of routine * * See also: * ES2015 22.2.2.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_of (ecma_value_t this_arg, /**< 'this' argument */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { if (!ecma_is_constructor (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_CONSTRUCTOR); } ecma_object_t *constructor_obj_p = ecma_get_object_from_value (this_arg); ecma_value_t len_val = ecma_make_uint32_value (arguments_list_len); ecma_value_t ret_val = ecma_typedarray_create (constructor_obj_p, &len_val, 1); ecma_free_value (len_val); if (ECMA_IS_VALUE_ERROR (ret_val)) { return ret_val; } uint32_t k = 0; ecma_object_t *ret_obj_p = ecma_get_object_from_value (ret_val); ecma_typedarray_info_t info = ecma_typedarray_get_info (ret_obj_p); ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { ecma_deref_object (ret_obj_p); return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { ecma_deref_object (ret_obj_p); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } lit_utf8_byte_t *buffer_p = ecma_typedarray_get_buffer (&info); while (k < arguments_list_len) { ecma_value_t set_element = setter_cb (buffer_p, arguments_list_p[k]); if (ECMA_IS_VALUE_ERROR (set_element)) { ecma_deref_object (ret_obj_p); return set_element; } k++; buffer_p += info.element_size; } return ret_val; } /* ecma_builtin_typedarray_of */ /** * Handle calling [[Call]] of built-in %TypedArray% object * * ES2015 22.2.1 If %TypedArray% is directly called or * called as part of a new expression an exception is thrown * * @return ecma value */ ecma_value_t ecma_builtin_typedarray_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_TYPEDARRAY_INTRINSIC_DIRECTLY_CALLED); } /* ecma_builtin_typedarray_dispatch_call */ /** * Handle calling [[Construct]] of built-in %TypedArray% object * * ES2015 22.2.1 If %TypedArray% is directly called or * called as part of a new expression an exception is thrown * * @return ecma value */ ecma_value_t ecma_builtin_typedarray_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_TYPEDARRAY_INTRINSIC_CALLED_BY_NEW_EXPRESSION); } /* ecma_builtin_typedarray_dispatch_construct */ /** * 22.2.2.4 get %TypedArray% [ @@species ] accessor * * @return ecma_value * returned value must be freed with ecma_free_value */ ecma_value_t ecma_builtin_typedarray_species_get (ecma_value_t this_value) /**< This Value */ { return ecma_copy_value (this_value); } /* ecma_builtin_typedarray_species_get */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/strfunc.h000664 001750 001750 00000005246 15164251010 040327 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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 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 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)); } //! Custom strcmpn() which works on different character types. /*! \tparam Ch Character type (e.g. char, wchar_t, short) \param s1 Null-terminated input string. \param s2 Null-terminated input string. \return 0 if equal */ template inline int StrCmp(const Ch* s1, const Ch* s2) { RAPIDJSON_ASSERT(s1 != 0); RAPIDJSON_ASSERT(s2 != 0); while(*s1 && (*s1 == *s2)) { s1++; s2++; } return static_cast(*s1) < static_cast(*s2) ? -1 : static_cast(*s1) > static_cast(*s2); } //! Returns number of code points in a encoded string. template bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { RAPIDJSON_ASSERT(s != 0); RAPIDJSON_ASSERT(outCount != 0); GenericStringStream 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_ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-set.cpp000664 001750 001750 00000004712 15164251010 047173 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-container-object.h" #include "ecma-exceptions.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-set.inc.h" #define BUILTIN_UNDERSCORED_ID set #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup set ECMA Set object built-in * @{ */ /** * Handle calling [[Call]] of built-in Set object * * @return ecma value */ ecma_value_t ecma_builtin_set_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_SET_REQUIRES_NEW); } /* ecma_builtin_set_dispatch_call */ /** * Handle calling [[Construct]] of built-in Map object * * @return ecma value */ ecma_value_t ecma_builtin_set_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_op_container_create (arguments_list_p, arguments_list_len, LIT_MAGIC_STRING_SET_UL, ECMA_BUILTIN_ID_SET_PROTOTYPE); } /* ecma_builtin_set_dispatch_construct */ /** * 23.2.2.2 get Set [ @@species ] accessor * * @return ecma_value * returned value must be freed with ecma_free_value */ ecma_value_t ecma_builtin_set_species_get (ecma_value_t this_value) /**< This Value */ { return ecma_copy_value (this_value); } /* ecma_builtin_set_species_get */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-syntaxerror-prototype.inc.h000664 001750 001750 00000002466 15164251010 053264 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * SyntaxError.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.7.8 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_SYNTAX_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.9 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_SYNTAX_ERROR_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.10 */ STRING_VALUE (LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test3.lot000664 001750 001750 00000016131 15164251010 033772 0ustar00ddennedyddennedy000000 000000 {"v":"5.9.0","fr":59.9700012207031,"ip":0,"op":360.000036657751,"w":1200,"h":1200,"nm":"TVG","ddd":0,"assets":[],"fonts":{"list":[{"fName":"Helvetica","fFamily":"Helvetica","fStyle":"Regular","ascent":71.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"ThorVG","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[432,436,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[0,0],[-259.912,7.27],[-121.255,9.112],[-108.042,19.179],[-112.693,41.191],[-63.206,59.879],[-11.167,93.323],[57.248,58.644],[85.108,15.531],[100.711,-26.083],[50.89,-77.977],[-26.964,-76.745],[-71.423,-46.772],[-98.628,-27.755],[-170.142,-33.105],[-127.268,-7.905],[-201.893,-5.741]],"o":[[173.363,-0.243],[121.549,-3.4],[109.422,-8.223],[118.138,-20.971],[81.775,-29.89],[68.231,-64.64],[9.737,-81.373],[-60.432,-61.906],[-102.344,-18.676],[-90.14,23.346],[-44.458,68.121],[28.301,80.548],[85.715,56.132],[166.852,46.953],[125.166,24.354],[302.322,18.778],[0,0]],"v":[[-1228,444],[-448,436],[-84,412],[244,384],[592,292],[824,172],[972,-68],[880,-296],[643.999,-404],[332,-396],[88,-260],[84,-16],[244,184],[536,280],[1039.999,408],[1420,452],[2328,488]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"s":200,"f":"Helvetica","t":"ThorVG","ca":0,"j":0,"tr":0,"lh":240.000015258789,"ls":0,"fc":[0.991,0.407,0.016]},"t":0}]},"p":{"m":0,"f":{"a":1,"k":[{"i":{"x":[0.499],"y":[1.746]},"o":{"x":[0.225],"y":[-0.308]},"t":0,"s":[0]},{"i":{"x":[0.58],"y":[0.978]},"o":{"x":[0.257],"y":[0.11]},"t":0,"s":[0]},{"i":{"x":[0.828],"y":[0.874]},"o":{"x":[0.462],"y":[0.016]},"t":201,"s":[2329.661]},{"i":{"x":[0.673],"y":[1.006]},"o":{"x":[0.34],"y":[-0.007]},"t":424,"s":[7000]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":426,"s":[5500]},{"t":695.00002830793,"s":[7000]}],"ix":5},"l":{"a":0,"k":0,"ix":6},"a":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":1,"ix":3},"r":{"a":0,"k":0,"ix":2}},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":900.000036657751,"st":0,"bm":0}],"markers":[],"chars":[{"ch":"T","size":200,"style":"Regular","w":61.08,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.611,-71.729],[1.611,-63.184],[25.781,-63.184],[25.781,0],[35.596,0],[35.596,-63.184],[59.766,-63.184],[59.766,-71.729]],"c":true},"ix":2},"nm":"T","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"T","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"h","size":200,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.962,2.507],[-3.646,0],[-1.595,-2.864],[0,-3.841],[0,0],[0,0],[0,0],[1.465,2.898],[7.584,0],[2.832,-1.855],[2.083,-2.637],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-6.966],[2.962,-2.506],[4.395,0],[0.977,1.791],[0,0],[0,0],[0,0],[0,-5.143],[-2.702,-5.305],[-4.232,0],[-1.66,1.074],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.234,0],[15.234,-27.734],[19.678,-41.943],[29.59,-45.703],[38.574,-41.406],[40.039,-32.959],[40.039,0],[49.072,0],[49.072,-33.545],[46.875,-45.605],[31.445,-53.564],[20.85,-50.781],[15.234,-45.215],[15.234,-71.973],[6.445,-71.973]],"c":true},"ix":2},"nm":"h","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"h","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"o","size":200,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.761,0],[2.414,3.706],[0,5.56],[-2.414,4.097],[-5.312,0],[-2.446,-4.812],[0,-4.877],[2.141,-4.405]],"o":[[-5.247,0],[-2.414,-3.706],[0,-5.787],[2.414,-4.097],[5.987,0],[1.545,3.056],[0,5.397],[-2.141,4.405]],"v":[[27.026,-5.713],[15.535,-11.272],[11.914,-25.172],[15.535,-39.997],[27.122,-46.143],[39.772,-38.924],[42.09,-27.025],[38.879,-12.321]],"c":true},"ix":2},"nm":"o","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[6.691,0],[4.428,-5.203],[0,-8.781],[-4.202,-4.862],[-7.144,0],[-4.073,5.496],[0,8.424],[4.622,4.488]],"o":[[-7.467,0],[-4.428,5.204],[0,8.196],[4.202,4.862],[8.566,0],[4.073,-5.496],[0,-8.716],[-4.623,-4.488]],"v":[[27.366,-53.809],[9.523,-46.003],[2.881,-25.025],[9.184,-5.437],[26.202,1.855],[45.16,-6.389],[51.27,-27.27],[44.336,-47.076]],"c":true},"ix":2},"nm":"o","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"o","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"r","size":200,"style":"Regular","w":33.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.377,2.849],[-4.427,0],[-0.439,-0.032],[-0.521,-0.098],[0,0],[0.391,0.033],[0.163,0],[2.669,-2.522],[0.684,-1.758],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-3.711],[2.376,-2.848],[0.52,0],[0.439,0.033],[0,0],[-0.945,-0.098],[-0.391,-0.032],[-3.484,0],[-2.67,2.523],[0,0],[0,0],[0,0]],"v":[[6.689,0],[15.479,0],[15.479,-30.078],[19.043,-39.917],[29.248,-44.189],[30.688,-44.141],[32.129,-43.945],[32.129,-53.223],[30.127,-53.418],[29.297,-53.467],[20.068,-49.683],[15.039,-43.262],[15.039,-52.295],[6.689,-52.295]],"c":true},"ix":2},"nm":"r","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"r","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"V","size":200,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.563,-71.729],[28.687,0],[38.989,0],[65.161,-71.729],[54.272,-71.729],[33.911,-10.645],[13.306,-71.729]],"c":true},"ix":2},"nm":"V","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"V","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"G","size":200,"style":"Regular","w":77.78,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.641,0],[6.096,-6.77],[0,-12.012],[-6.564,-6.901],[-8.358,0],[-4.195,2.409],[-3.235,3.613],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[4.163,-3.76],[6.348,0],[3.668,5.355],[0,8.529],[-4.447,4.98],[-6.503,0],[-3.603,-2.393],[-1.147,-5.241],[0,0],[7.026,3.744]],"o":[[-9.689,0],[-6.416,7.097],[0,12.045],[6.02,5.502],[6.468,0],[2.497,-1.399],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.098,8.398],[-4.163,3.76],[-8.295,0],[-3.669,-5.354],[0,-9.863],[4.446,-4.98],[5.482,0],[3.603,2.393],[0,0],[-1.572,-9.375],[-4.845,-2.604]],"v":[[38.136,-73.584],[14.458,-63.428],[4.834,-34.766],[14.68,-6.348],[36.247,1.904],[52.242,-1.709],[60.84,-9.229],[63.184,0],[69.434,0],[69.434,-38.379],[37.939,-38.379],[37.939,-30.322],[60.4,-30.322],[54.009,-12.085],[38.242,-6.445],[20.298,-14.478],[14.795,-35.303],[21.465,-57.568],[37.891,-65.039],[51.517,-61.45],[58.643,-50],[68.262,-50],[55.364,-69.678]],"c":true},"ix":2},"nm":"G","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"G","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"}]}src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp000664 001750 001750 00000005256 15164251010 036630 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgSwCommon.h" /************************************************************************/ /* External Class Implementation */ /************************************************************************/ SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx) { mpool->outline[idx].pts.clear(); mpool->outline[idx].cntrs.clear(); mpool->outline[idx].types.clear(); mpool->outline[idx].closed.clear(); return &mpool->outline[idx]; } SwStrokeBorder* mpoolReqStrokeLBorder(SwMpool* mpool, unsigned idx) { mpool->leftBorder[idx].pts.clear(); mpool->leftBorder[idx].start = -1; return &mpool->leftBorder[idx]; } SwStrokeBorder* mpoolReqStrokeRBorder(SwMpool* mpool, unsigned idx) { mpool->rightBorder[idx].pts.clear(); mpool->rightBorder[idx].start = -1; return &mpool->rightBorder[idx]; } SwCellPool* mpoolReqCellPool(SwMpool* mpool, unsigned idx) { return &mpool->cellPool[idx]; } SwMpool* mpoolInit(uint32_t threads) { auto allocSize = threads + 1; auto mpool = tvg::malloc(sizeof(SwMpool)); mpool->outline = new SwOutline[allocSize]; mpool->leftBorder = new SwStrokeBorder[allocSize]; mpool->rightBorder = new SwStrokeBorder[allocSize]; mpool->cellPool = new SwCellPool[allocSize]; mpool->allocSize = allocSize; return mpool; } void mpoolTerm(SwMpool* mpool) { if (!mpool) return; delete[](mpool->outline); delete[](mpool->leftBorder); delete[](mpool->rightBorder); delete[](mpool->cellPool); tvg::free(mpool); } src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-boolean-prototype.cpp000664 001750 001750 00000007737 15164251010 052074 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #if JERRY_BUILTIN_BOOLEAN #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BOOLEAN_PROTOTYPE_ROUTINE_START = 0, ECMA_BOOLEAN_PROTOTYPE_ROUTINE_TO_STRING, ECMA_BOOLEAN_PROTOTYPE_ROUTINE_VALUE_OF }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-boolean-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID boolean_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup booleanprototype ECMA Boolean.prototype object built-in * @{ */ /** * The Boolean.prototype object's 'valueOf' routine * * See also: * ECMA-262 v5, 15.6.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_boolean_prototype_object_value_of (ecma_value_t this_arg) /**< this argument */ { if (ecma_is_value_boolean (this_arg)) { return this_arg; } else if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_BOOLEAN)) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; JERRY_ASSERT (ecma_is_value_boolean (ext_object_p->u.cls.u3.value)); return ext_object_p->u.cls.u3.value; } } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_BOOLEAN_OBJECT); } /* ecma_builtin_boolean_prototype_object_value_of */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_boolean_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_number, arguments_list_p); ecma_value_t value_of_ret = ecma_builtin_boolean_prototype_object_value_of (this_arg); if (builtin_routine_id == ECMA_BOOLEAN_PROTOTYPE_ROUTINE_VALUE_OF) { return value_of_ret; } JERRY_ASSERT (builtin_routine_id == ECMA_BOOLEAN_PROTOTYPE_ROUTINE_TO_STRING); if (ECMA_IS_VALUE_ERROR (value_of_ret)) { return value_of_ret; } if (ecma_is_value_true (value_of_ret)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_TRUE); } JERRY_ASSERT (ecma_is_value_false (value_of_ret)); return ecma_make_magic_string_value (LIT_MAGIC_STRING_FALSE); } /* ecma_builtin_boolean_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_BOOLEAN */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-jobqueue.h000664 001750 001750 00000003753 15164251010 045312 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_JOB_QUEUE_H #define ECMA_JOB_QUEUE_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmajobqueue ECMA Job Queue related routines * @{ */ /** * Job queue item types. */ typedef enum { ECMA_JOB_PROMISE_REACTION, /**< promise reaction job */ ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED, /**< fulfilled promise async reaction job */ ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED, /**< rejected promise async reaction job */ ECMA_JOB_PROMISE_ASYNC_GENERATOR, /**< continue async generator */ ECMA_JOB_PROMISE_THENABLE, /**< promise thenable job */ } ecma_job_queue_item_type_t; /** * Description of the job queue item. */ typedef struct { uintptr_t next_and_type; /**< next and type members of a queue item */ } ecma_job_queue_item_t; void ecma_job_queue_init (void); void ecma_enqueue_promise_reaction_job (ecma_value_t capability, ecma_value_t handler, ecma_value_t argument); void ecma_enqueue_promise_async_reaction_job (ecma_value_t executable_object, ecma_value_t argument, bool is_rejected); void ecma_enqueue_promise_async_generator_job (ecma_value_t executable_object); void ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, ecma_value_t thenable, ecma_value_t then); void ecma_free_all_enqueued_jobs (void); ecma_value_t ecma_process_all_enqueued_jobs (void); /** * @} * @} */ #endif /* !ECMA_JOB_QUEUE_H */ lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-prototype.inc.h000664 001750 001750 00000003002 15164251010 053750 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * AsyncGenerator.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 25.3.1.5 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_ASYNC_GENERATOR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.2.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_ASYNC_GENERATOR, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_NEXT, ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_NEXT, 1, 1) ROUTINE (LIT_MAGIC_STRING_RETURN, ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_RETURN, 1, 1) ROUTINE (LIT_MAGIC_STRING_THROW, ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_THROW, 1, 1) #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-syntaxerror.inc.h000664 001750 001750 00000002453 15164251010 051215 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * SyntaxError built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_SYNTAX_ERROR_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_SYNTAX_ERROR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-intrinsic.cpp000664 001750 001750 00000022441 15164251010 050401 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-array-object.h" #include "ecma-arraybuffer-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-container-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-string-object.h" #include "ecma-typedarray-object.h" #include "lit-char-helpers.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_INTRINSIC_ROUTINE_START = 0, ECMA_INTRINSIC_ARRAY_PROTOTYPE_VALUES, ECMA_INTRINSIC_TYPEDARRAY_PROTOTYPE_VALUES, ECMA_INTRINSIC_MAP_PROTOTYPE_ENTRIES, ECMA_INTRINSIC_SET_PROTOTYPE_VALUES, ECMA_INTRINSIC_ARRAY_TO_STRING, ECMA_INTRINSIC_DATE_TO_UTC_STRING, ECMA_INTRINSIC_PARSE_FLOAT, ECMA_INTRINSIC_PARSE_INT, ECMA_INTRINSIC_STRING_TRIM_START, ECMA_INTRINSIC_STRING_TRIM_END, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-intrinsic.inc.h" #define BUILTIN_UNDERSCORED_ID intrinsic #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup intrinsic ECMA Intrinsic object built-in * @{ */ /** * The %ArrayProto_values% intrinsic routine * * See also: * ECMA-262 v5, 15.4.4.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_intrinsic_array_prototype_values (ecma_value_t this_value) /**< this argument */ { ecma_value_t this_obj = ecma_op_to_object (this_value); if (ECMA_IS_VALUE_ERROR (this_obj)) { return this_obj; } ecma_object_t *this_obj_p = ecma_get_object_from_value (this_obj); ecma_value_t ret_value = ecma_op_create_array_iterator (this_obj_p, ECMA_ITERATOR_VALUES); ecma_deref_object (this_obj_p); return ret_value; } /* ecma_builtin_intrinsic_array_prototype_values */ /** * The Map.prototype entries and [@@iterator] routines * * See also: * ECMA-262 v6, 23.1.3.4 * ECMA-262 v6, 23.1.3.12 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_intrinsic_map_prototype_entries (ecma_value_t this_value) /**< this value */ { ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_value, LIT_MAGIC_STRING_MAP_UL); if (map_object_p == NULL) { return ECMA_VALUE_ERROR; } return ecma_op_container_create_iterator (this_value, ECMA_BUILTIN_ID_MAP_ITERATOR_PROTOTYPE, ECMA_OBJECT_CLASS_MAP_ITERATOR, ECMA_ITERATOR_ENTRIES); } /* ecma_builtin_intrinsic_map_prototype_entries */ /** * The Set.prototype values, keys and [@@iterator] routines * * See also: * ECMA-262 v6, 23.2.3.8 * ECMA-262 v6, 23.2.3.10 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_intrinsic_set_prototype_values (ecma_value_t this_value) /**< this value */ { ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_value, LIT_MAGIC_STRING_SET_UL); if (map_object_p == NULL) { return ECMA_VALUE_ERROR; } return ecma_op_container_create_iterator (this_value, ECMA_BUILTIN_ID_SET_ITERATOR_PROTOTYPE, ECMA_OBJECT_CLASS_SET_ITERATOR, ECMA_ITERATOR_VALUES); } /* ecma_builtin_intrinsic_set_prototype_values */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_intrinsic_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); switch (builtin_routine_id) { #if JERRY_BUILTIN_ARRAY case ECMA_INTRINSIC_ARRAY_PROTOTYPE_VALUES: { return ecma_builtin_intrinsic_array_prototype_values (this_arg); } case ECMA_INTRINSIC_ARRAY_TO_STRING: { ecma_value_t this_obj = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (this_obj)) { return this_obj; } ecma_value_t result = ecma_array_object_to_string (this_obj); ecma_deref_object (ecma_get_object_from_value (this_obj)); return result; } #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_TYPEDARRAY case ECMA_INTRINSIC_TYPEDARRAY_PROTOTYPE_VALUES: { if (!ecma_is_typedarray (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_TYPED_ARRAY); } if (ecma_arraybuffer_is_detached (ecma_typedarray_get_arraybuffer (ecma_get_object_from_value (this_arg)))) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } return ecma_typedarray_iterators_helper (this_arg, ECMA_ITERATOR_VALUES); } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_CONTAINER case ECMA_INTRINSIC_SET_PROTOTYPE_VALUES: { return ecma_builtin_intrinsic_set_prototype_values (this_arg); } case ECMA_INTRINSIC_MAP_PROTOTYPE_ENTRIES: { return ecma_builtin_intrinsic_map_prototype_entries (this_arg); } #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_DATE case ECMA_INTRINSIC_DATE_TO_UTC_STRING: { if (!ecma_is_value_object (this_arg) || !ecma_object_class_is (ecma_get_object_from_value (this_arg), ECMA_OBJECT_CLASS_DATE)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_DATE_OBJECT); } ecma_number_t *date_value_p = &((ecma_date_object_t *) ecma_get_object_from_value (this_arg))->date_value; if (ecma_number_is_nan (*date_value_p)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_INVALID_DATE_UL); } return ecma_date_value_to_utc_string (*date_value_p); } #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_STRING case ECMA_INTRINSIC_STRING_TRIM_START: case ECMA_INTRINSIC_STRING_TRIM_END: { if (!ecma_op_require_object_coercible (this_arg)) { return ECMA_VALUE_ERROR; } ecma_string_t *to_str_p = ecma_op_to_string (this_arg); if (to_str_p == NULL) { return ECMA_VALUE_ERROR; } ECMA_STRING_TO_UTF8_STRING (to_str_p, start_p, input_start_size); lit_utf8_size_t size; const lit_utf8_byte_t *input_start_p = start_p; const lit_utf8_byte_t *input_str_end_p = start_p + input_start_size; ecma_string_t *ret_str_p; if (builtin_routine_id == ECMA_INTRINSIC_STRING_TRIM_START) { const lit_utf8_byte_t *new_start_p = ecma_string_trim_front (input_start_p, input_str_end_p); size = (lit_utf8_size_t) (input_str_end_p - new_start_p); ret_str_p = ecma_new_ecma_string_from_utf8 (new_start_p, size); } else { const lit_utf8_byte_t *new_end_p = ecma_string_trim_back (input_start_p, input_str_end_p); size = (lit_utf8_size_t) (new_end_p - input_start_p); ret_str_p = ecma_new_ecma_string_from_utf8 (input_start_p, size); } ECMA_FINALIZE_UTF8_STRING (start_p, input_start_size); ecma_value_t result = ecma_make_string_value (ret_str_p); ecma_deref_ecma_string (to_str_p); return result; } #endif /* JERRY_BUILTIN_STRING */ default: { JERRY_ASSERT (builtin_routine_id == ECMA_INTRINSIC_PARSE_INT || builtin_routine_id == ECMA_INTRINSIC_PARSE_FLOAT); ecma_string_t *str_p = ecma_op_to_string (arguments_list_p[0]); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t result; ECMA_STRING_TO_UTF8_STRING (str_p, string_buff, string_buff_size); if (builtin_routine_id == ECMA_INTRINSIC_PARSE_INT) { result = ecma_number_parse_int (string_buff, string_buff_size, arguments_list_p[1]); } else { JERRY_ASSERT (builtin_routine_id == ECMA_INTRINSIC_PARSE_FLOAT); result = ecma_number_parse_float (string_buff, string_buff_size); } ECMA_FINALIZE_UTF8_STRING (string_buff, string_buff_size); ecma_deref_ecma_string (str_p); return result; } } } /* ecma_builtin_intrinsic_dispatch_routine */ /** * @} * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/opcodes-ecma-relational-equality.cpp000664 001750 001750 00000011324 15164251010 046770 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-comparison.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "opcodes.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup vm_opcodes Opcodes * @{ */ /** * Equality opcode handler. * * See also: ECMA-262 v5, 11.9.1, 11.9.2 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t opfunc_equality (ecma_value_t left_value, /**< left value */ ecma_value_t right_value) /**< right value */ { JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (left_value) && !ECMA_IS_VALUE_ERROR (right_value)); ecma_value_t compare_result = ecma_op_abstract_equality_compare (left_value, right_value); JERRY_ASSERT (ecma_is_value_boolean (compare_result) || ECMA_IS_VALUE_ERROR (compare_result)); return compare_result; } /* opfunc_equality */ /** * Relation opcode handler. * * See also: ECMA-262 v5, 11.8.1, 11.8.2, 11.8.3, 11.8.4 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t opfunc_relation (ecma_value_t left_value, /**< left value */ ecma_value_t right_value, /**< right value */ bool left_first, /**< 'LeftFirst' flag */ bool is_invert) /**< is invert */ { JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (left_value) && !ECMA_IS_VALUE_ERROR (right_value)); ecma_value_t ret_value = ecma_op_abstract_relational_compare (left_value, right_value, left_first); if (ECMA_IS_VALUE_ERROR (ret_value)) { return ret_value; } if (ecma_is_value_undefined (ret_value)) { ret_value = ECMA_VALUE_FALSE; } else { JERRY_ASSERT (ecma_is_value_boolean (ret_value)); if (is_invert) { ret_value = ecma_invert_boolean_value (ret_value); } } return ret_value; } /* opfunc_relation */ /** * 'instanceof' opcode handler. * * See also: ECMA-262 v5, 11.8.6 * * @return ecma value * returned value must be freed with ecma_free_value. */ ecma_value_t opfunc_instanceof (ecma_value_t left_value, /**< left value */ ecma_value_t right_value) /**< right value */ { if (!ecma_is_value_object (right_value)) { return ecma_raise_type_error (ECMA_ERR_RIGHT_VALUE_OF_INSTANCEOF_MUST_BE_AN_OBJECT); } ecma_value_t has_instance_method = ecma_op_get_method_by_symbol_id (right_value, LIT_GLOBAL_SYMBOL_HAS_INSTANCE); if (ECMA_IS_VALUE_ERROR (has_instance_method)) { return has_instance_method; } if (JERRY_UNLIKELY (!ecma_is_value_undefined (has_instance_method))) { ecma_object_t *method_obj_p = ecma_get_object_from_value (has_instance_method); ecma_value_t has_instance_result = ecma_op_function_call (method_obj_p, right_value, &left_value, 1); ecma_free_value (has_instance_method); if (ECMA_IS_VALUE_ERROR (has_instance_result)) { return has_instance_result; } bool has_instance = ecma_op_to_boolean (has_instance_result); ecma_free_value (has_instance_result); return ecma_make_boolean_value (has_instance); } ecma_object_t *right_value_obj_p = ecma_get_object_from_value (right_value); return ecma_op_object_has_instance (right_value_obj_p, left_value); } /* opfunc_instanceof */ /** * 'in' opcode handler. * * See also: * * ECMA-262 v5, 11.8.7 * * ECAM-262 v6, 12.9.3 * * @return ecma value * returned value must be freed with ecma_free_value. */ ecma_value_t opfunc_in (ecma_value_t left_value, /**< left value */ ecma_value_t right_value) /**< right value */ { if (!ecma_is_value_object (right_value)) { return ecma_raise_type_error (ECMA_ERR_RIGHT_VALUE_OF_IN_MUST_BE_AN_OBJECT); } ecma_string_t *property_name_p = ecma_op_to_property_key (left_value); if (JERRY_UNLIKELY (property_name_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *right_value_obj_p = ecma_get_object_from_value (right_value); ecma_value_t result = ecma_op_object_has_property (right_value_obj_p, property_name_p); ecma_deref_ecma_string (property_name_p); return result; } /* opfunc_in */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/alphai.h000664 001750 001750 00000003076 15164251010 034542 0ustar00ddennedyddennedy000000 000000 // Copyright 2013 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Alpha decoder: internal header. // // Author: Urvang (urvang@google.com) #ifndef WEBP_DEC_ALPHAI_H_ #define WEBP_DEC_ALPHAI_H_ #include "./webpi.h" #include "../dsp/dsp.h" #ifdef __cplusplus extern "C" { #endif struct VP8LDecoder; // Defined in dec/vp8li.h. typedef struct ALPHDecoder ALPHDecoder; struct ALPHDecoder { int width_; int height_; int method_; WEBP_FILTER_TYPE filter_; int pre_processing_; struct VP8LDecoder* vp8l_dec_; VP8Io io_; int use_8b_decode; // Although alpha channel requires only 1 byte per // pixel, sometimes VP8LDecoder may need to allocate // 4 bytes per pixel internally during decode. }; //------------------------------------------------------------------------------ // internal functions. Not public. // Allocates a new alpha decoder instance. ALPHDecoder* ALPHNew(void); // Clears and deallocates an alpha decoder instance. void ALPHDelete(ALPHDecoder* const dec); //------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_DEC_ALPHAI_H_ */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-error-prototype.inc.h000664 001750 001750 00000002700 15164251010 052004 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Error.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.4.2 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_ERROR_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.4.3 */ STRING_VALUE (LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_ERROR_PROTOTYPE_ROUTINE_TO_STRING, 0, 0) #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testAccessor.cpp000664 001750 001750 00000005674 15164251010 033356 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2022 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; TEST_CASE("Accessor Creation", "[tvgAccessor]") { auto accessor = unique_ptr(Accessor::gen()); REQUIRE(accessor); auto accessor2 = unique_ptr(Accessor::gen()); REQUIRE(accessor2); } #ifdef THORVG_SVG_LOADER_SUPPORT TEST_CASE("Set", "[tvgAccessor]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); auto picture = Picture::gen(); REQUIRE(picture); REQUIRE(picture->load(TEST_DIR"/test0.svg") == Result::Success); auto accessor = unique_ptr(Accessor::gen()); REQUIRE(accessor); //Case 1 REQUIRE(accessor->set(picture, nullptr, nullptr) == Result::InvalidArguments); //Case 2 Shape* ret = nullptr; auto f = [](const tvg::Paint* paint, void* data) -> bool { if (paint->type() == Type::Shape) { auto shape = (tvg::Shape*) paint; uint8_t r, g, b; shape->fill(&r, &g, &b); if (r == 255 && g == 255 && b == 255) { shape->fill(0, 0, 255); shape->id = Accessor::id("TestAccessor"); *static_cast(data) = shape; return false; } } return true; }; REQUIRE(accessor->set(picture, f, &ret) == Result::Success); REQUIRE((ret && ret->id == Accessor::id("TestAccessor"))); Picture::rel(picture); } REQUIRE(Initializer::term() == Result::Success); } #endifmlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgPaint.h000664 001750 001750 00000017530 15164251010 033565 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_PAINT_H_ #define _TVG_PAINT_H_ #include "tvgCommon.h" #include "tvgRender.h" #include "tvgMath.h" #define PAINT(A) ((Paint::Impl*)A->pImpl) namespace tvg { enum ContextFlag : uint8_t {Default = 0, FastTrack = 1}; struct Iterator { virtual ~Iterator() {} virtual const Paint* next() = 0; virtual uint32_t count() = 0; virtual void begin() = 0; }; struct Mask { Paint* target; Paint* source; MaskMethod method; }; struct Paint::Impl { Paint* paint = nullptr; Paint* parent = nullptr; Mask* maskData = nullptr; Shape* clipper = nullptr; RenderMethod* renderer = nullptr; RenderData rd = nullptr; struct { Matrix m; //input matrix float degree; //rotation degree float scale; //scale factor bool overriding; //user transform? void update() { if (overriding) return; m.e11 = 1.0f; m.e12 = 0.0f; m.e21 = 0.0f; m.e22 = 1.0f; m.e31 = 0.0f; m.e32 = 0.0f; m.e33 = 1.0f; tvg::scale(&m, {scale, scale}); tvg::rotate(&m, degree); } } tr; RenderUpdateFlag renderFlag = RenderUpdateFlag::None; CompositionFlag cmpFlag = CompositionFlag::Invalid; BlendMethod blendMethod; uint16_t refCnt = 0; //reference count uint8_t ctxFlag; //See enum ContextFlag uint8_t opacity; bool hidden : 1; Impl(Paint* pnt) : paint(pnt) { pnt->pImpl = this; hidden = false; reset(); } virtual ~Impl() { if (maskData) { PAINT(maskData->target)->unref(); tvg::free(maskData); } if (clipper) PAINT(clipper)->unref(); if (renderer) { if (rd) renderer->dispose(rd); if (renderer->unref() == 0) delete(renderer); } } uint16_t ref() { return ++refCnt; } uint16_t unref(bool free = true) { parent = nullptr; return unrefx(free); } uint16_t unrefx(bool free) { if (refCnt > 0) --refCnt; if (free && refCnt == 0) { delete(paint); return 0; } return refCnt; } void damage(const RenderRegion& vport) { if (renderer) renderer->damage(rd, vport); } void damage() { if (renderer) renderer->damage(rd, bounds()); } void mark(CompositionFlag flag) { cmpFlag = CompositionFlag(uint8_t(cmpFlag) | uint8_t(flag)); } bool marked(CompositionFlag flag) { return (uint8_t(cmpFlag) & uint8_t(flag)) ? true : false; } bool marked(RenderUpdateFlag flag) { return (renderFlag & flag) ? true : false; } void mark(RenderUpdateFlag flag) { renderFlag |= flag; } bool transform(const Matrix& m) { if (&tr.m != &m) tr.m = m; tr.overriding = true; mark(RenderUpdateFlag::Transform); return true; } Matrix& transform() { //update transform if (renderFlag & RenderUpdateFlag::Transform) tr.update(); return tr.m; } Matrix ptransform() { auto p = this; auto tm = tvg::identity(); while (p->parent) { p = PAINT(p->parent); tm = p->transform() * tm; } return tm; } Result clip(Shape* clp) { if (clp && PAINT(clp)->parent) return Result::InsufficientCondition; if (clipper) PAINT(clipper)->unref(clipper != clp); clipper = clp; if (clp) { clp->ref(); PAINT(clp)->parent = parent; } return Result::Success; } Result mask(Paint* target, MaskMethod method) { if (target && PAINT(target)->parent) return Result::InsufficientCondition; if (maskData) { PAINT(maskData->target)->unref(maskData->target != target); tvg::free(maskData); maskData = nullptr; } if (method == MaskMethod::None) return (target ? Result::InvalidArguments : Result::Success); maskData = tvg::malloc(sizeof(Mask)); target->ref(); maskData->target = target; PAINT(target)->parent = parent; maskData->source = paint; maskData->method = method; return Result::Success; } MaskMethod mask(const Paint** target) const { if (maskData) { if (target) *target = maskData->target; return maskData->method; } else { if (target) *target = nullptr; return MaskMethod::None; } } void reset() { if (clipper) { PAINT(clipper)->unref(); clipper = nullptr; } if (maskData) { PAINT(maskData->target)->unref(); tvg::free(maskData); maskData = nullptr; } tvg::identity(&tr.m); tr.degree = 0.0f; tr.scale = 1.0f; tr.overriding = false; parent = nullptr; blendMethod = BlendMethod::Normal; renderFlag = RenderUpdateFlag::None; ctxFlag = ContextFlag::Default; opacity = 255; paint->id = 0; } bool rotate(float degree) { if (tr.overriding) return false; if (tvg::equal(degree, tr.degree)) return true; tr.degree = degree; mark(RenderUpdateFlag::Transform); return true; } bool scale(float factor) { if (tr.overriding) return false; if (tvg::equal(factor, tr.scale)) return true; tr.scale = factor; mark(RenderUpdateFlag::Transform); return true; } bool translate(float x, float y) { if (tr.overriding) return false; if (tvg::equal(x, tr.m.e13) && tvg::equal(y, tr.m.e23)) return true; tr.m.e13 = x; tr.m.e23 = y; mark(RenderUpdateFlag::Transform); return true; } void blend(BlendMethod method) { if (blendMethod != method) { blendMethod = method; mark(RenderUpdateFlag::Blend); } } Result visible(bool hidden) { if (this->hidden != hidden) { this->hidden = hidden; damage(); } return Result::Success; } bool intersects(const RenderRegion& region); RenderRegion bounds(); bool bounds(Point* pt4, const Matrix* pm, bool obb); Iterator* iterator(); RenderData update(RenderMethod* renderer, const Matrix& pm, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); bool render(RenderMethod* renderer); Paint* duplicate(Paint* ret = nullptr); }; } #endif //_TVG_PAINT_H_lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int16array-prototype.cpp000664 001750 001750 00000002222 15164251010 054621 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-int16array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID int16array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup int16arrayprototype ECMA Int16Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/CMakeLists.txt000664 001750 001750 00000003402 15165022620 030447 0ustar00ddennedyddennedy000000 000000 # SPDX-FileCopyrightText: 2019-2026 Mattia Basaglia # SPDX-License-Identifier: BSD-2-Clause file( GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/common/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/renderer/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/renderer/sw_engine/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/loaders/raw/*.cpp ) if ( EMSCRIPTEN ) set(THORVG_THREAD_SUPPORT OFF) else() set(THORVG_THREAD_SUPPORT ON) endif() if ( OPENGL_ENABLED ) set(THORVG_GL_RASTER_SUPPORT ON) set(THORVG_GL_TARGET_GL ON) file( GLOB GL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/renderer/gl_engine/*.cpp ) list(APPEND SOURCES ${GL_SOURCES}) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h ${CMAKE_CURRENT_BINARY_DIR}/config.h) add_library(thorvg STATIC ${SOURCES}) target_include_directories( thorvg PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/inc ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/common ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/renderer ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/renderer/sw_engine/ ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/renderer/gl_engine/ ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/thorvg/src/loaders/raw ) target_compile_definitions(thorvg PUBLIC TVG_STATIC NOMINMAX) if ( OPENGL_ENABLED ) find_package(OpenGL REQUIRED) target_link_libraries(thorvg PUBLIC ${OPENGL_gl_LIBRARY}) endif() # Disable warnings if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") target_compile_options(thorvg PRIVATE -Wno-unused-parameter -Wno-zero-as-null-pointer-constant -Wno-sized-deallocation ) endif() find_package(OpenMP) if(OpenMP_FOUND) target_link_libraries(thorvg PRIVATE OpenMP::OpenMP_CXX) endif() thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/parser-error-messages.inc.h000664 001750 001750 00000035613 15164251010 046410 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is automatically generated by the gen-strings.py script * from parser-error-messages.ini. Do not edit! */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_LABEL, "Duplicated label") PARSER_ERROR_DEF (PARSER_ERR_LEFT_PAREN_EXPECTED, "Expected '(' token") PARSER_ERROR_DEF (PARSER_ERR_RIGHT_PAREN_EXPECTED, "Expected ')' token") PARSER_ERROR_DEF (PARSER_ERR_COLON_EXPECTED, "Expected ':' token") PARSER_ERROR_DEF (PARSER_ERR_SEMICOLON_EXPECTED, "Expected ';' token") PARSER_ERROR_DEF (PARSER_ERR_RIGHT_SQUARE_EXPECTED, "Expected ']' token") PARSER_ERROR_DEF (PARSER_ERR_LEFT_BRACE_EXPECTED, "Expected '{' token") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM || JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_RIGHT_BRACE_EXPECTED, "Expected '}' token") #endif /* JERRY_MODULE_SYSTEM \ || JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_NUMBER_TOO_LONG, "Number is too long") #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_REGEXP && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_REGEXP_TOO_LONG, "Regexp is too long") #endif /* JERRY_BUILTIN_REGEXP && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_STATEMENT_EXPECTED, "Statement expected") PARSER_ERROR_DEF (PARSER_ERR_STRING_TOO_LONG, "String is too long") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_AS_EXPECTED, "Expected 'as' token") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_IN_EXPECTED, "Expected 'in' token") PARSER_ERROR_DEF (PARSER_ERR_OF_EXPECTED, "Expected 'of' token") PARSER_ERROR_DEF (PARSER_ERR_EXPRESSION_EXPECTED, "Expression expected") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM || JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_IDENTIFIER_EXPECTED, "Identifier expected") #endif /* JERRY_MODULE_SYSTEM \ || JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_INVALID_OCTAL_DIGIT, "Invalid octal digit") PARSER_ERROR_DEF (PARSER_ERR_INVALID_SWITCH, "Invalid switch body") #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_REGEXP && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_UNKNOWN_REGEXP_FLAG, "Unknown regexp flag") #endif /* JERRY_BUILTIN_REGEXP && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_INVALID_BIN_DIGIT, "Invalid binary digit") PARSER_ERROR_DEF (PARSER_ERR_INVALID_RIGHT_SQUARE, "Unexpected '}' token") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_FROM_EXPECTED, "Expected 'from' token") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_MISSING_EXPONENT, "Missing exponent part") #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_REGEXP && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_REGEXP_FLAG, "Duplicated regexp flag") #endif /* JERRY_BUILTIN_REGEXP && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_ARGUMENT_LIST_EXPECTED, "Expected argument list") PARSER_ERROR_DEF (PARSER_ERR_IDENTIFIER_TOO_LONG, "Identifier is too long") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_META_EXPECTED, "Expected 'meta' keyword") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_UNEXPECTED_END, "Unexpected end of input") PARSER_ERROR_DEF (PARSER_ERR_UNEXPECTED_PRIVATE_FIELD, "Unexpected private field") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_LEFT_BRACE_MULTIPLY_EXPECTED, "Expected '{' or '*' token") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ #if JERRY_MODULE_SYSTEM PARSER_ERROR_DEF (PARSER_ERR_RIGHT_BRACE_COMMA_EXPECTED, "Expected '}' or ',' token") PARSER_ERROR_DEF (PARSER_ERR_STRING_EXPECTED, "Expected a string literal") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_INVALID_HEX_DIGIT, "Invalid hexadecimal digit") #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_REGEXP && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_UNTERMINATED_REGEXP, "Unterminated regexp literal") #endif /* JERRY_BUILTIN_REGEXP && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_UNTERMINATED_STRING, "Unterminated string literal") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_FROM_COMMA_EXPECTED, "Expected 'from' or ',' token") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ #if JERRY_MODULE_SYSTEM PARSER_ERROR_DEF (PARSER_ERR_EXPORT_NOT_DEFINED, "Export not defined in module") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_MODULE_SYSTEM || JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_VARIABLE_REDECLARED, "Local variable is redeclared") #endif /* JERRY_MODULE_SYSTEM \ || JERRY_PARSER */ #if JERRY_BUILTIN_BIGINT && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_INVALID_BIGINT, "Number is not a valid BigInt") #endif /* JERRY_BUILTIN_BIGINT && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED, "Property identifier expected") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER, "Duplicate exported identifier") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_NEW_TARGET_EXPECTED, "Expected new.target expression") PARSER_ERROR_DEF (PARSER_ERR_INVALID_CHARACTER, "Invalid (unexpected) character") PARSER_ERROR_DEF (PARSER_ERR_NON_STRICT_ARG_DEFINITION, "Non-strict argument definition") PARSER_ERROR_DEF (PARSER_ERR_UNTERMINATED_MULTILINE_COMMENT, "Unterminated multiline comment") PARSER_ERROR_DEF (PARSER_ERR_CATCH_FINALLY_EXPECTED, "Catch or finally block expected") PARSER_ERROR_DEF (PARSER_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE, "Invalid unicode escape sequence") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_IMPORT_BINDING, "Duplicated imported binding name") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_WHILE_EXPECTED, "While expected for do-while loop") PARSER_ERROR_DEF (PARSER_ERR_DELETE_PRIVATE_FIELD, "Private fields cannot be deleted") PARSER_ERROR_DEF (PARSER_ERR_INVALID_LHS_FOR_LOOP, "Invalid left-hand-side in for-loop") PARSER_ERROR_DEF (PARSER_ERR_LEFT_HAND_SIDE_EXP_EXPECTED, "Left-hand-side expression expected") PARSER_ERROR_DEF (PARSER_ERR_LITERAL_LIMIT_REACHED, "Maximum number of literals reached") PARSER_ERROR_DEF (PARSER_ERR_STACK_LIMIT_REACHED, "Maximum function stack size reached") PARSER_ERROR_DEF (PARSER_ERR_AWAIT_NOT_ALLOWED, "Await expression is not allowed here") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_LEFT_BRACE_MULTIPLY_LITERAL_EXPECTED, "Expected '{' or '*' or literal token") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_INVALID_LHS_ASSIGNMENT, "Invalid left-hand side in assignment") PARSER_ERROR_DEF (PARSER_ERR_SCOPE_STACK_LIMIT_REACHED, "Maximum depth of scope stack reached") PARSER_ERROR_DEF (PARSER_ERR_UNEXPECTED_SUPER_KEYWORD, "Super is not allowed to be used here") PARSER_ERROR_DEF (PARSER_ERR_YIELD_NOT_ALLOWED, "Yield expression is not allowed here") PARSER_ERROR_DEF (PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS, "Multiple constructors are not allowed") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM PARSER_ERROR_DEF (PARSER_ERR_MODULE_UNEXPECTED, "Unexpected import or export statement") #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_IDENTIFIER_AFTER_NUMBER, "Identifier cannot start after a number") PARSER_ERROR_DEF (PARSER_ERR_MULTIPLE_DEFAULTS_NOT_ALLOWED, "Multiple default cases are not allowed") PARSER_ERROR_DEF (PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED, "Expected ',' or ']' after an array item") PARSER_ERROR_DEF (PARSER_ERR_ILLEGAL_PROPERTY_IN_DECLARATION, "Illegal property in declaration context") PARSER_ERROR_DEF (PARSER_ERR_INVALID_DESTRUCTURING_PATTERN, "Invalid destructuring assignment target") PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_PRIVATE_FIELD, "Private field has already been declared") PARSER_ERROR_DEF (PARSER_ERR_NO_ARGUMENTS_EXPECTED, "Property getters must have no arguments") PARSER_ERROR_DEF (PARSER_ERR_ONE_ARGUMENT_EXPECTED, "Property setters must have one argument") PARSER_ERROR_DEF (PARSER_ERR_CASE_NOT_IN_SWITCH, "Case statement must be in a switch block") PARSER_ERROR_DEF (PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR, "Class constructor may not be an accessor") PARSER_ERROR_DEF (PARSER_ERR_INVALID_CONTINUE, "Continue statement must be inside a loop") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_IMPORT_META_REQUIRE_MODULE, "Cannot use 'import.meta' outside a module") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_INVALID_IDENTIFIER_PART, "Character cannot be part of an identifier") PARSER_ERROR_DEF (PARSER_ERR_EVAL_CANNOT_ASSIGNED, "Eval cannot be assigned to in strict mode") PARSER_ERROR_DEF (PARSER_ERR_WITH_NOT_ALLOWED, "With statement not allowed in strict mode") PARSER_ERROR_DEF (PARSER_ERR_NEW_TARGET_NOT_ALLOWED, "new.target expression is not allowed here") PARSER_ERROR_DEF (PARSER_ERR_INVALID_IDENTIFIER_START, "Character cannot be start of an identifier") PARSER_ERROR_DEF (PARSER_ERR_STRICT_IDENT_NOT_ALLOWED, "Identifier name is reserved in strict mode") PARSER_ERROR_DEF (PARSER_ERR_INVALID_NULLISH_COALESCING, "Cannot chain nullish with logical AND or OR") PARSER_ERROR_DEF (PARSER_ERR_DEFAULT_NOT_IN_SWITCH, "Default statement must be in a switch block") #endif /* JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_IMPORT_AFTER_NEW, "Module import call is not allowed after new") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_TOO_MANY_CLASS_FIELDS, "Too many computed class fields are declared") PARSER_ERROR_DEF (PARSER_ERR_INVALID_KEYWORD, "Escape sequences are not allowed in keywords") PARSER_ERROR_DEF (PARSER_ERR_ARGUMENT_LIMIT_REACHED, "Maximum number of function arguments reached") PARSER_ERROR_DEF (PARSER_ERR_NEWLINE_NOT_ALLOWED, "Newline is not allowed in strings or regexps") PARSER_ERROR_DEF (PARSER_ERR_OCTAL_NUMBER_NOT_ALLOWED, "Octal numbers are not allowed in strict mode") PARSER_ERROR_DEF (PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR, "Class constructor may not be a private method") PARSER_ERROR_DEF (PARSER_ERR_FOR_AWAIT_NO_OF, "only 'of' form is allowed for for-await loops") PARSER_ERROR_DEF (PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED, "Arguments cannot be assigned to in strict mode") PARSER_ERROR_DEF (PARSER_ERR_INVALID_BREAK, "Break statement must be inside a loop or switch") PARSER_ERROR_DEF (PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED, "Expected ',' or '}' after a property definition") PARSER_ERROR_DEF (PARSER_ERR_INVALID_BREAK_LABEL, "Labeled statement targeted by a break not found") #endif /* JERRY_PARSER */ #if JERRY_PARSER && !(JERRY_BUILTIN_REGEXP) PARSER_ERROR_DEF (PARSER_ERR_UNSUPPORTED_REGEXP, "Regexp is not supported in the selected profile") #endif /* JERRY_PARSER && !(JERRY_BUILTIN_REGEXP) */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_INVALID_RETURN, "Return statement must be inside a function body") PARSER_ERROR_DEF (PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED, "Expected ':' token for ?: conditional expression") PARSER_ERROR_DEF (PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER, "Rest parameter must be the last formal parameter") PARSER_ERROR_DEF (PARSER_ERR_DELETE_IDENT_NOT_ALLOWED, "Deleting identifier is not allowed in strict mode") PARSER_ERROR_DEF (PARSER_ERR_LABELLED_FUNC_NOT_IN_BLOCK, "Labelled functions are only allowed inside blocks") PARSER_ERROR_DEF (PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER, "Rest parameter may not have a default initializer") PARSER_ERROR_DEF (PARSER_ERR_EVAL_NOT_ALLOWED, "Eval is not allowed to be used here in strict mode") PARSER_ERROR_DEF (PARSER_ERR_INVALID_CONTINUE_LABEL, "Labeled statement targeted by a continue not found") PARSER_ERROR_DEF (PARSER_ERR_LEXICAL_LET_BINDING, "Let binding cannot appear in let/const declarations") PARSER_ERROR_DEF (PARSER_ERR_UNDECLARED_PRIVATE_FIELD, "Private field must be declared in an enclosing class") PARSER_ERROR_DEF (PARSER_ERR_INVALID_LHS_PREFIX_OP, "Invalid left-hand side expression in prefix operation") PARSER_ERROR_DEF (PARSER_ERR_OCTAL_ESCAPE_NOT_ALLOWED, "Octal escape sequences are not allowed in strict mode") PARSER_ERROR_DEF (PARSER_ERR_SETTER_REST_PARAMETER, "Setter function argument must not be a rest parameter") PARSER_ERROR_DEF (PARSER_ERR_ARGUMENTS_IN_CLASS_FIELD, "In class field declarations 'arguments' is not allowed") PARSER_ERROR_DEF (PARSER_ERR_INVALID_LHS_POSTFIX_OP, "Invalid left-hand side expression in postfix operation") PARSER_ERROR_DEF (PARSER_ERR_INVALID_UNDERSCORE_IN_NUMBER, "Invalid use of underscore character in number literals") PARSER_ERROR_DEF (PARSER_ERR_INVALID_EXPONENTIATION, "Left operand of ** operator cannot be unary expression") PARSER_ERROR_DEF (PARSER_ERR_MISSING_ASSIGN_AFTER_CONST, "Value assignment is expected after a const declaration") PARSER_ERROR_DEF (PARSER_ERR_ARGUMENTS_NOT_ALLOWED, "Arguments is not allowed to be used here in strict mode") PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_ARGUMENT_NAMES, "Duplicated function argument names are not allowed here") PARSER_ERROR_DEF (PARSER_ERR_CLASS_STATIC_PROTOTYPE, "Classes may not have a static property called 'prototype'") PARSER_ERROR_DEF (PARSER_ERR_INVALID_CLASS_CONSTRUCTOR, "Class constructor may not be a generator or async function") PARSER_ERROR_DEF (PARSER_ERR_TEMPLATE_STR_OCTAL_ESCAPE, "Octal escape sequences are not allowed in template strings") PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_PROTO, "Duplicate __proto__ fields are not allowed in object literals") PARSER_ERROR_DEF (PARSER_ERR_GENERATOR_IN_SINGLE_STATEMENT_POS, "Generator function cannot appear in a single-statement context") PARSER_ERROR_DEF (PARSER_ERR_LEXICAL_SINGLE_STATEMENT, "Lexical declaration cannot appear in a single-statement context") PARSER_ERROR_DEF (PARSER_ERR_FOR_IN_OF_DECLARATION, "for in-of loop variable declaration may not have an initializer") PARSER_ERROR_DEF (PARSER_ERR_FOR_AWAIT_NO_ASYNC, "for-await-of is only allowed inside async functions and generators") PARSER_ERROR_DEF (PARSER_ERR_USE_STRICT_NOT_ALLOWED, "The 'use strict' directive is not allowed for functions with non-simple arguments") PARSER_ERROR_DEF (PARSER_ERR_ASSIGNMENT_EXPECTED, "Unexpected arrow function or yield expression (parentheses around the expression may help)") #endif /* JERRY_PARSER */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/meta.h000664 001750 001750 00000014734 15164251010 037573 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(6334) #endif #if RAPIDJSON_HAS_CXX11_TYPETRAITS #include #endif //@cond RAPIDJSON_INTERNAL RAPIDJSON_NAMESPACE_BEGIN namespace internal { // Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching template struct Void { typedef void Type; }; /////////////////////////////////////////////////////////////////////////////// // BoolType, TrueType, FalseType // template struct BoolType { static const bool Value = Cond; typedef BoolType Type; }; typedef BoolType TrueType; typedef BoolType FalseType; /////////////////////////////////////////////////////////////////////////////// // SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr // template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; template struct SelectIfCond : SelectIfImpl::template Apply {}; template struct SelectIf : SelectIfCond {}; template struct AndExprCond : FalseType {}; template <> struct AndExprCond : TrueType {}; template struct OrExprCond : TrueType {}; template <> struct OrExprCond : FalseType {}; template struct BoolExpr : SelectIf::Type {}; template struct NotExpr : SelectIf::Type {}; template struct AndExpr : AndExprCond::Type {}; template struct OrExpr : OrExprCond::Type {}; /////////////////////////////////////////////////////////////////////////////// // AddConst, MaybeAddConst, RemoveConst template struct AddConst { typedef const T Type; }; template struct MaybeAddConst : SelectIfCond {}; template struct RemoveConst { typedef T Type; }; template struct RemoveConst { typedef T Type; }; /////////////////////////////////////////////////////////////////////////////// // IsSame, IsConst, IsMoreConst, IsPointer // template struct IsSame : FalseType {}; template struct IsSame : TrueType {}; template struct IsConst : FalseType {}; template struct IsConst : TrueType {}; template struct IsMoreConst : AndExpr::Type, typename RemoveConst::Type>, BoolType::Value >= IsConst::Value> >::Type {}; template struct IsPointer : FalseType {}; template struct IsPointer : TrueType {}; /////////////////////////////////////////////////////////////////////////////// // IsBaseOf // #if RAPIDJSON_HAS_CXX11_TYPETRAITS template struct IsBaseOf : BoolType< ::std::is_base_of::value> {}; #else // simplified version adopted from Boost template struct IsBaseOfImpl { RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); typedef char (&Yes)[1]; typedef char (&No) [2]; template 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 struct IsBaseOf : OrExpr, BoolExpr > >::Type {}; #endif // RAPIDJSON_HAS_CXX11_TYPETRAITS ////////////////////////////////////////////////////////////////////////// // EnableIf / DisableIf // template struct EnableIfCond { typedef T Type; }; template struct EnableIfCond { /* empty */ }; template struct DisableIfCond { typedef T Type; }; template struct DisableIfCond { /* empty */ }; template struct EnableIf : EnableIfCond {}; template struct DisableIf : DisableIfCond {}; // SFINAE helpers struct SfinaeTag {}; template struct RemoveSfinaeTag; template struct RemoveSfinaeTag { 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 \ ::Type * = NULL #define RAPIDJSON_DISABLEIF(cond) \ typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ ::Type * = NULL #define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ ::Type #define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ ::Type } // namespace internal RAPIDJSON_NAMESPACE_END //@endcond #if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_POP #endif #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_INTERNAL_META_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/meson.build000664 001750 001750 00000001565 15164251010 035646 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0source_file = [ 'tvgWgBindGroups.h', 'tvgWgCommon.h', 'tvgWgCompositor.h', 'tvgWgGeometry.h', 'tvgWgPipelines.h', 'tvgWgRenderData.h', 'tvgWgRenderer.h', 'tvgWgRenderTarget.h', 'tvgWgRenderTask.h', 'tvgWgShaderSrc.h', 'tvgWgShaderTypes.h', 'tvgWgTessellator.h', 'tvgWgBindGroups.cpp', 'tvgWgCommon.cpp', 'tvgWgCompositor.cpp', 'tvgWgGeometry.cpp', 'tvgWgPipelines.cpp', 'tvgWgRenderData.cpp', 'tvgWgRenderer.cpp', 'tvgWgRenderTarget.cpp', 'tvgWgRenderTask.cpp', 'tvgWgShaderSrc.cpp', 'tvgWgShaderTypes.cpp', 'tvgWgTessellator.cpp' ] wgpu_dep = [] if host_machine.system() != 'emscripten' wgpu_dep = dependency('wgpu_native') endif engine_dep += [declare_dependency( dependencies : wgpu_dep, include_directories : include_directories('.'), sources : source_file )] jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-float64array-prototype.inc.h000664 001750 001750 00000001727 15164251010 055365 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Float64Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_NUMBER_TYPE_FLOAT64 #define TYPEDARRAY_BYTES_PER_ELEMENT 8 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_FLOAT64ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-dataview.inc.h000664 001750 001750 00000002524 15164251010 050420 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * DataView built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_DATAVIEW /* Number properties: * (property name, number value, writable, enumerable, configurable) */ /* ECMA-262 v6, 23.1.2 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 23.1 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_DATAVIEW_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 23.1.2.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_DATAVIEW_PROTOTYPE, ECMA_PROPERTY_FIXED) #endif /* JERRY_BUILTIN_DATAVIEW */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakmap.inc.h000664 001750 001750 00000002523 15164251010 050240 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * WeakMap built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER /* Number properties: * (property name, number value, writable, enumerable, configurable) */ /* ECMA-262 v6, 23.3.2 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 23.1 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_WEAKMAP_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 23.3.2.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_WEAKMAP_PROTOTYPE, ECMA_PROPERTY_FIXED) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-lex-env.cpp000664 001750 001750 00000046461 15164251010 045407 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-lex-env.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-proxy-object.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup lexicalenvironment Lexical environment * @{ * * \addtogroup globallexicalenvironment Global lexical environment * @{ */ /** * Initialize Global environment */ void ecma_init_global_environment (void) { JERRY_CONTEXT (global_object_p) = ecma_builtin_create_global_object (); } /* ecma_init_global_environment */ /** * Finalize Global environment */ void ecma_finalize_global_environment (void) { /* After this point the gc can free the global object, but the global_object_p pointer * is not set to NULL because the global object might still be used before the free. */ ecma_deref_object ((ecma_object_t *) JERRY_CONTEXT (global_object_p)); } /* ecma_finalize_global_environment */ /** * Get reference to Global lexical environment * without increasing its reference count. * * @return pointer to the object's instance */ ecma_object_t * ecma_get_global_environment (ecma_object_t *global_object_p) /**< global object */ { JERRY_ASSERT (global_object_p != NULL && ecma_builtin_is_global (global_object_p)); return ECMA_GET_NON_NULL_POINTER (ecma_object_t, ((ecma_global_object_t *) global_object_p)->global_env_cp); } /* ecma_get_global_environment */ /** * Create the global lexical block on top of the global environment. */ void ecma_create_global_lexical_block (ecma_object_t *global_object_p) /**< global object */ { JERRY_ASSERT (global_object_p != NULL && ecma_builtin_is_global (global_object_p)); ecma_global_object_t *real_global_object_p = (ecma_global_object_t *) global_object_p; if (real_global_object_p->global_scope_cp == real_global_object_p->global_env_cp) { ecma_object_t *global_scope_p = ecma_create_decl_lex_env (ecma_get_global_environment (global_object_p)); global_scope_p->type_flags_refs |= ECMA_OBJECT_FLAG_BLOCK; ECMA_SET_NON_NULL_POINTER (real_global_object_p->global_scope_cp, global_scope_p); ecma_deref_object (global_scope_p); } } /* ecma_create_global_lexical_block */ /** * Raise the appropriate error when setting a binding is failed * * @return ECMA_VALUE_EMPTY or ECMA_VALUE_ERROR */ ecma_value_t ecma_op_raise_set_binding_error (ecma_property_t *property_p, /**< property */ bool is_strict) /**< flag indicating strict mode */ { JERRY_UNUSED (property_p); const ecma_property_t expected_bits = (ECMA_PROPERTY_FLAG_DATA | ECMA_PROPERTY_FLAG_ENUMERABLE); if ((*property_p & expected_bits) == expected_bits) { ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); if (JERRY_UNLIKELY (property_value_p->value == ECMA_VALUE_UNINITIALIZED)) { return ecma_raise_reference_error (ECMA_ERR_LET_CONST_NOT_INITIALIZED); } JERRY_ASSERT (!ecma_is_property_writable (*property_p)); return ecma_raise_type_error (ECMA_ERR_CONSTANT_BINDINGS_CANNOT_BE_REASSIGNED); } if (is_strict) { return ecma_raise_type_error (ECMA_ERR_BINDING_CANNOT_SET); } return ECMA_VALUE_EMPTY; } /* ecma_op_raise_set_binding_error */ /** * Get reference to Global lexical scope * without increasing its reference count. * * @return pointer to the object's instance */ ecma_object_t * ecma_get_global_scope (ecma_object_t *global_object_p) /**< global object */ { JERRY_ASSERT (global_object_p != NULL && ecma_builtin_is_global (global_object_p)); return ECMA_GET_NON_NULL_POINTER (ecma_object_t, ((ecma_global_object_t *) global_object_p)->global_scope_cp); } /* ecma_get_global_scope */ /** * @} */ /** * HasBinding operation. * * See also: ECMA-262 v5, 10.2.1 * * @return true / false */ ecma_value_t ecma_op_has_binding (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p) /**< argument N */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); ecma_lexical_environment_type_t lex_env_type = ecma_get_lex_env_type (lex_env_p); switch (lex_env_type) { case ECMA_LEXICAL_ENVIRONMENT_CLASS: { if (!ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { return ECMA_VALUE_FALSE; } /* FALLTHRU */ } case ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE: { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); return ecma_make_boolean_value (property_p != NULL); } default: { JERRY_ASSERT (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); return ecma_op_object_has_property (binding_obj_p, name_p); } } } /* ecma_op_has_binding */ /** * CreateMutableBinding operation. * * See also: ECMA-262 v5, 10.2.1 * * @return ECMA_PROPERTY_POINTER_ERROR - if the operation raises error * pointer to the created property - if the binding was created into a declarative environment * NULL - otherwise */ ecma_property_t * ecma_op_create_mutable_binding (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p, /**< argument N */ bool is_deletable) /**< argument D */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); JERRY_ASSERT (name_p != NULL); if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { uint8_t prop_attributes = ECMA_PROPERTY_FLAG_WRITABLE; if (is_deletable) { prop_attributes = (uint8_t) (prop_attributes | ECMA_PROPERTY_FLAG_CONFIGURABLE); } ecma_property_t *prop_p; ecma_create_named_data_property (lex_env_p, name_p, prop_attributes, &prop_p); return prop_p; } else { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); #if JERRY_BUILTIN_PROXY && JERRY_BUILTIN_REALMS if (ECMA_OBJECT_IS_PROXY (binding_obj_p)) { ecma_value_t result = ecma_proxy_object_is_extensible (binding_obj_p); if (ECMA_IS_VALUE_ERROR (result)) { return ECMA_PROPERTY_POINTER_ERROR; } if (result == ECMA_VALUE_FALSE) { return NULL; } } else if (!ecma_op_ordinary_object_is_extensible (binding_obj_p)) { return NULL; } #else /* !JERRY_BUILTIN_PROXY || !JERRY_BUILTIN_REALMS */ if (!ecma_op_ordinary_object_is_extensible (binding_obj_p)) { return NULL; } #endif /* JERRY_BUILTIN_PROXY && JERRY_BUILTIN_REALMS */ const uint32_t flags = ECMA_PROPERTY_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW; ecma_value_t completion = ecma_builtin_helper_def_prop (binding_obj_p, name_p, ECMA_VALUE_UNDEFINED, is_deletable ? flags | ECMA_PROPERTY_FLAG_CONFIGURABLE : flags); if (ECMA_IS_VALUE_ERROR (completion)) { return ECMA_PROPERTY_POINTER_ERROR; } else { JERRY_ASSERT (ecma_is_value_boolean (completion)); } } return NULL; } /* ecma_op_create_mutable_binding */ /** * SetMutableBinding operation. * * See also: ECMA-262 v5, 10.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_set_mutable_binding (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p, /**< argument N */ ecma_value_t value, /**< argument V */ bool is_strict) /**< argument S */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); JERRY_ASSERT (name_p != NULL); switch (ecma_get_lex_env_type (lex_env_p)) { case ECMA_LEXICAL_ENVIRONMENT_CLASS: { if (!ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { return ECMA_VALUE_EMPTY; } /* FALLTHRU */ } case ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE: { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); if (JERRY_UNLIKELY (property_p == NULL)) { property_p = ecma_op_create_mutable_binding (lex_env_p, name_p, is_strict); JERRY_ASSERT (property_p != ECMA_PROPERTY_POINTER_ERROR); } JERRY_ASSERT (property_p != NULL && ECMA_PROPERTY_IS_RAW_DATA (*property_p)); JERRY_ASSERT (!(*property_p & ECMA_PROPERTY_FLAG_WRITABLE) || (*property_p & ECMA_PROPERTY_FLAG_DATA)); if ((*property_p & ECMA_PROPERTY_FLAG_WRITABLE)) { ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); JERRY_ASSERT (property_value_p->value != ECMA_VALUE_UNINITIALIZED); ecma_named_data_property_assign_value (lex_env_p, property_value_p, value); return ECMA_VALUE_EMPTY; } return ecma_op_raise_set_binding_error (property_p, is_strict); } default: { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); ecma_value_t completion = ecma_op_object_put (binding_obj_p, name_p, value, is_strict); if (ECMA_IS_VALUE_ERROR (completion)) { return completion; } JERRY_ASSERT (ecma_is_value_boolean (completion)); return ECMA_VALUE_EMPTY; } } } /* ecma_op_set_mutable_binding */ /** * GetBindingValue operation. * * See also: ECMA-262 v5, 10.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_get_binding_value (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p, /**< argument N */ bool is_strict) /**< argument S */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); JERRY_ASSERT (name_p != NULL); if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_value_t *prop_value_p = ecma_get_named_data_property (lex_env_p, name_p); return ecma_copy_value (prop_value_p->value); } else { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); ecma_value_t result = ecma_op_object_find (binding_obj_p, name_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } if (!ecma_is_value_found (result)) { if (is_strict) { result = ecma_raise_reference_error (ECMA_ERR_BINDING_NOT_EXIST_OR_UNINITIALIZED); } else { result = ECMA_VALUE_UNDEFINED; } } return result; } } /* ecma_op_get_binding_value */ /** * DeleteBinding operation. * * See also: ECMA-262 v5, 10.2.1 * * @return ecma value * Return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE/FALSE} - depends on whether the binding can be deleted */ ecma_value_t ecma_op_delete_binding (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p) /**< argument N */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); JERRY_ASSERT (name_p != NULL); if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *prop_p = ecma_find_named_property (lex_env_p, name_p); ecma_value_t ret_val; if (prop_p == NULL) { ret_val = ECMA_VALUE_TRUE; } else { JERRY_ASSERT (ECMA_PROPERTY_IS_RAW_DATA (*prop_p)); if (!ecma_is_property_configurable (*prop_p)) { ret_val = ECMA_VALUE_FALSE; } else { ecma_delete_property (lex_env_p, ECMA_PROPERTY_VALUE_PTR (prop_p)); ret_val = ECMA_VALUE_TRUE; } } return ret_val; } else { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); return ecma_op_object_delete (binding_obj_p, name_p, false); } } /* ecma_op_delete_binding */ /** * ImplicitThisValue operation. * * See also: ECMA-262 v5, 10.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_implicit_this_value (ecma_object_t *lex_env_p) /**< lexical environment */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { return ECMA_VALUE_UNDEFINED; } else { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); ecma_ref_object (binding_obj_p); return ecma_make_object_value (binding_obj_p); } } /* ecma_op_implicit_this_value */ /** * CreateImmutableBinding operation. * * See also: ECMA-262 v5, 10.2.1 */ void ecma_op_create_immutable_binding (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p, /**< argument N */ ecma_value_t value) /**< argument V */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); /* * Warning: * Whether immutable bindings are deletable seems not to be defined by ECMA v5. */ ecma_property_value_t *prop_value_p = ecma_create_named_data_property (lex_env_p, name_p, ECMA_PROPERTY_FIXED, NULL); prop_value_p->value = ecma_copy_value_if_not_object (value); } /* ecma_op_create_immutable_binding */ /** * InitializeBinding operation. * * See also: ECMA-262 v6, 8.1.1.1.4 */ void ecma_op_initialize_binding (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *name_p, /**< argument N */ ecma_value_t value) /**< argument V */ { JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); ecma_property_t *prop_p = ecma_find_named_property (lex_env_p, name_p); JERRY_ASSERT (prop_p != NULL && ECMA_PROPERTY_IS_RAW_DATA (*prop_p)); ecma_property_value_t *prop_value_p = ECMA_PROPERTY_VALUE_PTR (prop_p); JERRY_ASSERT (prop_value_p->value == ECMA_VALUE_UNINITIALIZED); prop_value_p->value = ecma_copy_value_if_not_object (value); } /* ecma_op_initialize_binding */ /** * BindThisValue operation for an empty lexical environment * * See also: ECMA-262 v6, 8.1.1.3.1 */ void ecma_op_create_environment_record (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_value_t this_binding, /**< this binding value */ ecma_object_t *func_obj_p) /**< function object */ { JERRY_ASSERT (lex_env_p != NULL); JERRY_ASSERT (ecma_is_value_object (this_binding) || this_binding == ECMA_VALUE_UNINITIALIZED); ecma_environment_record_t *environment_record_p; environment_record_p = (ecma_environment_record_t *) jmem_heap_alloc_block (sizeof (ecma_environment_record_t)); environment_record_p->this_binding = this_binding; environment_record_p->function_object = ecma_make_object_value (func_obj_p); ecma_string_t *property_name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD); ecma_property_t *property_p; ecma_property_value_t *prop_value_p; ECMA_CREATE_INTERNAL_PROPERTY (lex_env_p, property_name_p, property_p, prop_value_p); ECMA_SET_INTERNAL_VALUE_POINTER (prop_value_p->value, environment_record_p); } /* ecma_op_create_environment_record */ /** * GetThisEnvironment operation. * * See also: ECMA-262 v6, 8.3.2 * * @return property pointer for the internal [[ThisBindingValue]] property */ ecma_environment_record_t * ecma_op_get_environment_record (ecma_object_t *lex_env_p) /**< lexical environment */ { JERRY_ASSERT (lex_env_p != NULL); ecma_string_t *property_name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD); while (true) { if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, property_name_p); if (property_p != NULL) { ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_environment_record_t, property_value_p->value); } } if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) { break; } lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } return NULL; } /* ecma_op_get_environment_record */ /** * Get the environment record [[ThisBindingStatus]] internal property. * * See also: ECMA-262 v6, 8.1.1.3 * * @return true - if the status is "initialized" * false - otherwise */ bool ecma_op_this_binding_is_initialized (ecma_environment_record_t *environment_record_p) /**< environment record */ { JERRY_ASSERT (environment_record_p != NULL); return environment_record_p->this_binding != ECMA_VALUE_UNINITIALIZED; } /* ecma_op_this_binding_is_initialized */ /** * BindThisValue operation. * * See also: ECMA-262 v6, 8.1.1.3.1 */ void ecma_op_bind_this_value (ecma_environment_record_t *environment_record_p, /**< environment record */ ecma_value_t this_binding) /**< this binding value */ { JERRY_ASSERT (environment_record_p != NULL); JERRY_ASSERT (ecma_is_value_object (this_binding)); JERRY_ASSERT (!ecma_op_this_binding_is_initialized (environment_record_p)); environment_record_p->this_binding = this_binding; } /* ecma_op_bind_this_value */ /** * GetThisBinding operation. * * See also: ECMA-262 v6, 8.1.1.3.4 * * @return ECMA_VALUE_ERROR - if the operation fails * ecma-object - otherwise */ ecma_value_t ecma_op_get_this_binding (ecma_object_t *lex_env_p) /**< lexical environment */ { JERRY_ASSERT (lex_env_p != NULL); ecma_environment_record_t *environment_record_p = ecma_op_get_environment_record (lex_env_p); JERRY_ASSERT (environment_record_p != NULL); ecma_value_t this_value = environment_record_p->this_binding; if (this_value == ECMA_VALUE_UNINITIALIZED) { return ecma_raise_reference_error (ECMA_ERR_CALL_SUPER_CONSTRUCTOR_DERIVED_CLASS_BEFORE_THIS); } ecma_ref_object (ecma_get_object_from_value (this_value)); return this_value; } /* ecma_op_get_this_binding */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/000775 001750 001750 00000000000 15164251010 033125 5ustar00ddennedyddennedy000000 000000 thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-symbol-object.h000664 001750 001750 00000002440 15164251010 046234 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_SYMBOL_H #define ECMA_SYMBOL_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmasymbolobject ECMA Symbol object related routines * @{ */ ecma_value_t ecma_op_create_symbol (const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_value_t ecma_op_create_symbol_object (const ecma_value_t value); bool ecma_prop_name_is_symbol (ecma_string_t *string_p); ecma_value_t ecma_get_symbol_description (ecma_string_t *symbol_p); ecma_value_t ecma_symbol_this_value (ecma_value_t this_arg); ecma_value_t ecma_get_symbol_descriptive_string (ecma_value_t symbol_value); /** * @} * @} */ #endif /* !ECMA_SYMBOL_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/labeler.yml000664 001750 001750 00000001370 15164251010 032707 0ustar00ddennedyddennedy000000 000000 "APIs": - inc/**/* "binding": - src/bindings/**/* "documentation": - README.md "font": - src/loaders/ttf/**/* "image": - src/loaders/jpg/**/* - src/loaders/external_jpg/**/* - src/loaders/png/**/* - src/loaders/external_png/**/* - src/loaders/webp/**/* - src/loaders/external_webp/**/* - src/loaders/raw/**/* - src/savers/gif/**/* "svg": - src/loaders/svg/**/* "tvg": - src/loaders/tvg/**/* "test": - test/**/* "tools": - tools/**/* "infrastructure": - .github/**/* - meson.build - meson_option.txt "lottie": - src/loaders/lottie/**/* "cpu": - src/renderer/sw_engine/**/* "webgpu": - src/renderer/wg_engine/**/* "gl": - src/renderer/gl_engine/**/* "renderer": - src/renderer/* - src/common/**/* glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-scanner.cpp000664 001750 001750 00000357515 15164251010 044016 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "jcontext.h" #include "js-parser-internal.h" #include "js-scanner-internal.h" #include "lit-char-helpers.h" #if JERRY_PARSER /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_scanner Scanner * @{ */ /** * Scan return types. */ typedef enum { SCAN_NEXT_TOKEN, /**< get next token after return */ SCAN_KEEP_TOKEN, /**< keep the current token after return */ } scan_return_types_t; /** * Checks whether token type is "of". */ #define SCANNER_IDENTIFIER_IS_OF() (lexer_token_is_identifier (context_p, "of", 2)) JERRY_STATIC_ASSERT ((SCANNER_FROM_LITERAL_POOL_TO_COMPUTED (SCANNER_LITERAL_POOL_GENERATOR) == SCAN_STACK_COMPUTED_GENERATOR), scanner_invalid_conversion_from_literal_pool_generator_to_computed_generator); JERRY_STATIC_ASSERT ((SCANNER_FROM_LITERAL_POOL_TO_COMPUTED (SCANNER_LITERAL_POOL_ASYNC) == SCAN_STACK_COMPUTED_ASYNC), scanner_invalid_conversion_from_literal_pool_async_to_computed_async); JERRY_STATIC_ASSERT ((SCANNER_FROM_COMPUTED_TO_LITERAL_POOL (SCAN_STACK_COMPUTED_GENERATOR) == SCANNER_LITERAL_POOL_GENERATOR), scanner_invalid_conversion_from_computed_generator_to_literal_pool_generator); JERRY_STATIC_ASSERT ((SCANNER_FROM_COMPUTED_TO_LITERAL_POOL (SCAN_STACK_COMPUTED_ASYNC) == SCANNER_LITERAL_POOL_ASYNC), scanner_invalid_conversion_from_computed_async_to_literal_pool_async); /** * Change scanner mode from primary expression to post primary expression. * * @return SCAN_NEXT_TOKEN to read the next token, or SCAN_KEEP_TOKEN to do nothing */ static scan_return_types_t scanner_primary_to_post_primary_expression (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /* scanner context */ { scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; if (JERRY_UNLIKELY (context_p->stack_top_uint8 == SCAN_STACK_CLASS_FIELD_INITIALIZER && (context_p->status_flags & PARSER_IS_STRICT))) { lexer_scan_identifier (context_p, (lexer_parse_options_t) ((unsigned int) LEXER_PARSE_CHECK_KEYWORDS | (unsigned int) LEXER_PARSE_NO_STRICT_IDENT_ERROR)); if (context_p->token.type == LEXER_LITERAL && lexer_compare_literal_to_string (context_p, "static", 6)) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; } return SCAN_KEEP_TOKEN; } return SCAN_NEXT_TOKEN; } /* scanner_primary_to_post_primary_expression */ /** * Scan primary expression. * * @return SCAN_NEXT_TOKEN to read the next token, or SCAN_KEEP_TOKEN to do nothing */ static scan_return_types_t scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /* scanner context */ lexer_token_type_t type, /**< current token type */ scan_stack_modes_t stack_top) /**< current stack top */ { switch (type) { case LEXER_KEYW_NEW: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW; if (scanner_try_scan_new_target (context_p)) { return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } break; } case LEXER_DIVIDE: case LEXER_ASSIGN_DIVIDE: { lexer_construct_regexp_object (context_p, true); return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } case LEXER_KEYW_FUNCTION: { uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION; #if JERRY_MODULE_SYSTEM bool is_export_default = stack_top == SCAN_STACK_EXPORT_DEFAULT; #endif /* JERRY_MODULE_SYSTEM */ if (scanner_context_p->async_source_p != NULL) { status_flags |= SCANNER_LITERAL_POOL_ASYNC; } if (lexer_consume_generator (context_p)) { status_flags |= SCANNER_LITERAL_POOL_GENERATOR; } scanner_push_literal_pool (context_p, scanner_context_p, status_flags); lexer_next_token (context_p); if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { #if JERRY_MODULE_SYSTEM if (is_export_default) { lexer_lit_location_t *location_p; location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p->prev_p, &context_p->token.lit_location); scanner_detect_invalid_let (context_p, location_p); location_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET; } #endif /* JERRY_MODULE_SYSTEM */ lexer_next_token (context_p); } #if JERRY_MODULE_SYSTEM else if (is_export_default) { lexer_lit_location_t *location_p; location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p->prev_p, &lexer_default_literal); location_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET; } #endif /* JERRY_MODULE_SYSTEM */ parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_EXPRESSION); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; } case LEXER_LEFT_PAREN: { scanner_scan_bracket (context_p, scanner_context_p); return SCAN_KEEP_TOKEN; } case LEXER_LEFT_SQUARE: { scanner_push_destructuring_pattern (context_p, scanner_context_p, SCANNER_BINDING_NONE, false); parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } case LEXER_LEFT_BRACE: { scanner_push_destructuring_pattern (context_p, scanner_context_p, SCANNER_BINDING_NONE, false); parser_stack_push_uint8 (context_p, 0); parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; return SCAN_KEEP_TOKEN; } case LEXER_HASHMARK: { if (!lexer_scan_private_identifier (context_p)) { scanner_raise_error (context_p); } return SCAN_KEEP_TOKEN; } case LEXER_TEMPLATE_LITERAL: { if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) { parser_stack_push_uint8 (context_p, SCAN_STACK_TEMPLATE_STRING); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } /* The string is a normal string literal. */ /* FALLTHRU */ } case LEXER_LITERAL: { const uint8_t *source_p = context_p->source_p; if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL && lexer_check_arrow (context_p)) { scanner_scan_simple_arrow (context_p, scanner_context_p, source_p); return SCAN_KEEP_TOKEN; } if (JERRY_UNLIKELY (lexer_token_is_async (context_p))) { scanner_context_p->async_source_p = source_p; scanner_check_async_function (context_p, scanner_context_p); return SCAN_KEEP_TOKEN; } if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { #if JERRY_MODULE_SYSTEM if (stack_top == SCAN_STACK_EXPORT_DEFAULT) { lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); location_p->type |= (SCANNER_LITERAL_IS_USED | SCANNER_LITERAL_IS_VAR); scanner_detect_eval_call (context_p, scanner_context_p); return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } #endif /* JERRY_MODULE_SYSTEM */ scanner_add_reference (context_p, scanner_context_p); } /* FALLTHRU */ } case LEXER_KEYW_THIS: case LEXER_LIT_TRUE: case LEXER_LIT_FALSE: case LEXER_LIT_NULL: { return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } case LEXER_KEYW_SUPER: { scanner_context_p->active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE; return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } case LEXER_KEYW_CLASS: { scanner_push_class_declaration (context_p, scanner_context_p, SCAN_STACK_CLASS_EXPRESSION); if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { return SCAN_KEEP_TOKEN; } break; } case LEXER_RIGHT_SQUARE: { if (stack_top != SCAN_STACK_ARRAY_LITERAL) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; return SCAN_KEEP_TOKEN; } case LEXER_THREE_DOTS: { /* Elision or spread arguments */ if (stack_top != SCAN_STACK_PAREN_EXPRESSION && stack_top != SCAN_STACK_ARRAY_LITERAL) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } case LEXER_COMMA: { if (stack_top != SCAN_STACK_ARRAY_LITERAL) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; if (scanner_context_p->binding_type != SCANNER_BINDING_NONE) { scanner_context_p->mode = SCAN_MODE_BINDING; } break; } case LEXER_KEYW_YIELD: { lexer_next_token (context_p); if (lexer_check_yield_no_arg (context_p)) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; } if (context_p->token.type == LEXER_MULTIPLY) { return SCAN_NEXT_TOKEN; } return SCAN_KEEP_TOKEN; } #if JERRY_MODULE_SYSTEM case LEXER_KEYW_IMPORT: { lexer_next_token (context_p); if (context_p->token.type == LEXER_DOT) { scanner_check_import_meta (context_p); } else if (context_p->token.type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } #endif /* JERRY_MODULE_SYSTEM */ case LEXER_RIGHT_PAREN: { if (stack_top == SCAN_STACK_PAREN_EXPRESSION) { parser_stack_pop_uint8 (context_p); if (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC) { scanner_add_async_literal (context_p, scanner_context_p); } return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } /* FALLTHRU */ } default: { scanner_raise_error (context_p); } } return SCAN_NEXT_TOKEN; } /* scanner_scan_primary_expression */ /** * Scan the tokens after the primary expression. * * @return true for break, false for fall through */ static bool scanner_scan_post_primary_expression (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ lexer_token_type_t type, /**< current token type */ scan_stack_modes_t stack_top) /**< current stack top */ { switch (type) { case LEXER_DOT: { lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); if (context_p->token.type == LEXER_HASHMARK) { context_p->token.flags |= LEXER_NO_SKIP_SPACES; lexer_next_token (context_p); } if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } return true; } case LEXER_LEFT_PAREN: { parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return true; } case LEXER_TEMPLATE_LITERAL: { if (JERRY_UNLIKELY (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT)) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; parser_stack_push_uint8 (context_p, SCAN_STACK_TAGGED_TEMPLATE_LITERAL); } return true; } case LEXER_LEFT_SQUARE: { parser_stack_push_uint8 (context_p, SCAN_STACK_PROPERTY_ACCESSOR); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return true; } case LEXER_INCREASE: case LEXER_DECREASE: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; if (context_p->token.flags & LEXER_WAS_NEWLINE) { return false; } lexer_next_token (context_p); type = (lexer_token_type_t) context_p->token.type; if (type != LEXER_QUESTION_MARK) { break; } /* FALLTHRU */ } case LEXER_QUESTION_MARK: { parser_stack_push_uint8 (context_p, SCAN_STACK_COLON_EXPRESSION); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return true; } default: { break; } } if (LEXER_IS_BINARY_OP_TOKEN (type) && (type != LEXER_KEYW_IN || !SCANNER_IS_FOR_START (stack_top))) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return true; } return false; } /* scanner_scan_post_primary_expression */ /** * Scan the tokens after the primary expression. * * @return SCAN_NEXT_TOKEN to read the next token, or SCAN_KEEP_TOKEN to do nothing */ static scan_return_types_t scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ lexer_token_type_t type, /**< current token type */ scan_stack_modes_t stack_top) /**< current stack top */ { if (type == LEXER_COMMA) { switch (stack_top) { case SCAN_STACK_VAR: case SCAN_STACK_LET: case SCAN_STACK_CONST: case SCAN_STACK_FOR_VAR_START: case SCAN_STACK_FOR_LET_START: case SCAN_STACK_FOR_CONST_START: { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; return SCAN_NEXT_TOKEN; } case SCAN_STACK_COLON_EXPRESSION: { scanner_raise_error (context_p); break; } case SCAN_STACK_BINDING_INIT: case SCAN_STACK_BINDING_LIST_INIT: { break; } case SCAN_STACK_ARROW_ARGUMENTS: { lexer_next_token (context_p); scanner_check_arrow_arg (context_p, scanner_context_p); return SCAN_KEEP_TOKEN; } case SCAN_STACK_ARROW_EXPRESSION: { break; } case SCAN_STACK_CLASS_FIELD_INITIALIZER: { scanner_raise_error (context_p); break; } case SCAN_STACK_FUNCTION_PARAMETERS: { scanner_context_p->mode = SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS; parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; } case SCAN_STACK_ARRAY_LITERAL: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; if (scanner_context_p->binding_type != SCANNER_BINDING_NONE) { scanner_context_p->mode = SCAN_MODE_BINDING; } return SCAN_NEXT_TOKEN; } case SCAN_STACK_OBJECT_LITERAL: { scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; return SCAN_KEEP_TOKEN; } default: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_NEXT_TOKEN; } } } switch (stack_top) { case SCAN_STACK_WITH_EXPRESSION: { if (type != LEXER_RIGHT_PAREN) { break; } parser_stack_pop_uint8 (context_p); uint16_t status_flags = scanner_context_p->active_literal_pool_p->status_flags; parser_stack_push_uint8 (context_p, (status_flags & SCANNER_LITERAL_POOL_IN_WITH) ? 1 : 0); parser_stack_push_uint8 (context_p, SCAN_STACK_WITH_STATEMENT); status_flags |= SCANNER_LITERAL_POOL_IN_WITH; scanner_context_p->active_literal_pool_p->status_flags = status_flags; scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_NEXT_TOKEN; } case SCAN_STACK_DO_EXPRESSION: { if (type != LEXER_RIGHT_PAREN) { break; } scanner_context_p->mode = SCAN_MODE_STATEMENT_END; return SCAN_NEXT_TOKEN; } case SCAN_STACK_WHILE_EXPRESSION: { if (type != LEXER_RIGHT_PAREN) { break; } scanner_source_start_t source_start; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t)); scanner_location_info_t *location_info_p; location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_location_info_t)); location_info_p->info.type = SCANNER_TYPE_WHILE; scanner_get_location (&location_info_p->location, context_p); scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_NEXT_TOKEN; } case SCAN_STACK_PAREN_EXPRESSION: { if (type != LEXER_RIGHT_PAREN) { break; } parser_stack_pop_uint8 (context_p); if (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC) { scanner_add_async_literal (context_p, scanner_context_p); } scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_NEXT_TOKEN; } case SCAN_STACK_STATEMENT_WITH_EXPR: { if (type != LEXER_RIGHT_PAREN) { break; } parser_stack_pop_uint8 (context_p); if (context_p->stack_top_uint8 == SCAN_STACK_IF_STATEMENT) { scanner_check_function_after_if (context_p, scanner_context_p); return SCAN_KEEP_TOKEN; } scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_NEXT_TOKEN; } case SCAN_STACK_BINDING_LIST_INIT: { parser_stack_pop_uint8 (context_p); JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_ARRAY_LITERAL || context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL || context_p->stack_top_uint8 == SCAN_STACK_LET || context_p->stack_top_uint8 == SCAN_STACK_CONST || context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START || context_p->stack_top_uint8 == SCAN_STACK_FOR_CONST_START || context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_PARAMETERS || context_p->stack_top_uint8 == SCAN_STACK_ARROW_ARGUMENTS); scanner_binding_item_t *item_p = scanner_context_p->active_binding_list_p->items_p; while (item_p != NULL) { if (item_p->literal_p->type & SCANNER_LITERAL_IS_USED) { item_p->literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; } item_p = item_p->next_p; } scanner_pop_binding_list (scanner_context_p); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; return SCAN_KEEP_TOKEN; } case SCAN_STACK_BINDING_INIT: { scanner_binding_literal_t binding_literal; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_ARRAY_LITERAL || context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL || context_p->stack_top_uint8 == SCAN_STACK_LET || context_p->stack_top_uint8 == SCAN_STACK_CONST || context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START || context_p->stack_top_uint8 == SCAN_STACK_FOR_CONST_START || context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_PARAMETERS || context_p->stack_top_uint8 == SCAN_STACK_ARROW_ARGUMENTS); JERRY_ASSERT (SCANNER_NEEDS_BINDING_LIST (scanner_context_p->binding_type) || (stack_top != SCAN_STACK_ARRAY_LITERAL && stack_top != SCAN_STACK_OBJECT_LITERAL)); if (binding_literal.literal_p->type & SCANNER_LITERAL_IS_USED) { binding_literal.literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; return SCAN_KEEP_TOKEN; } case SCAN_STACK_VAR: case SCAN_STACK_LET: case SCAN_STACK_CONST: { #if JERRY_MODULE_SYSTEM scanner_context_p->active_literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_EXPORT; #endif /* JERRY_MODULE_SYSTEM */ parser_stack_pop_uint8 (context_p); return SCAN_KEEP_TOKEN; } case SCAN_STACK_FOR_VAR_START: case SCAN_STACK_FOR_LET_START: case SCAN_STACK_FOR_CONST_START: case SCAN_STACK_FOR_START: { if (type == LEXER_KEYW_IN || SCANNER_IDENTIFIER_IS_OF ()) { scanner_for_statement_t for_statement; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &for_statement, sizeof (scanner_for_statement_t)); scanner_location_info_t *location_info; location_info = (scanner_location_info_t *) scanner_insert_info (context_p, for_statement.u.source_p, sizeof (scanner_location_info_t)); location_info->info.type = (type == LEXER_KEYW_IN) ? SCANNER_TYPE_FOR_IN : SCANNER_TYPE_FOR_OF; if (stack_top == SCAN_STACK_FOR_LET_START || stack_top == SCAN_STACK_FOR_CONST_START) { parser_stack_push_uint8 (context_p, SCAN_STACK_PRIVATE_BLOCK_EARLY); } scanner_get_location (&location_info->location, context_p); parser_stack_push_uint8 (context_p, SCAN_STACK_STATEMENT_WITH_EXPR); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_NEXT_TOKEN; } if (type != LEXER_SEMICOLON) { break; } scanner_for_statement_t for_statement; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, NULL, sizeof (scanner_for_statement_t)); if (stack_top == SCAN_STACK_FOR_LET_START || stack_top == SCAN_STACK_FOR_CONST_START) { parser_stack_push_uint8 (context_p, SCAN_STACK_PRIVATE_BLOCK); } for_statement.u.source_p = context_p->source_p; parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_CONDITION); lexer_next_token (context_p); if (context_p->token.type != LEXER_SEMICOLON) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } type = LEXER_SEMICOLON; /* FALLTHRU */ } case SCAN_STACK_FOR_CONDITION: { if (type != LEXER_SEMICOLON) { break; } scanner_for_statement_t for_statement; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &for_statement, sizeof (scanner_for_statement_t)); scanner_for_info_t *for_info_p; for_info_p = (scanner_for_info_t *) scanner_insert_info (context_p, for_statement.u.source_p, sizeof (scanner_for_info_t)); for_info_p->info.type = SCANNER_TYPE_FOR; scanner_get_location (&for_info_p->expression_location, context_p); for_info_p->end_location.source_p = NULL; for_statement.u.for_info_p = for_info_p; parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_EXPRESSION); lexer_next_token (context_p); if (context_p->token.type != LEXER_RIGHT_PAREN) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } type = LEXER_RIGHT_PAREN; /* FALLTHRU */ } case SCAN_STACK_FOR_EXPRESSION: { if (type != LEXER_RIGHT_PAREN) { break; } scanner_for_statement_t for_statement; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &for_statement, sizeof (scanner_for_statement_t)); scanner_get_location (&for_statement.u.for_info_p->end_location, context_p); scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_NEXT_TOKEN; } case SCAN_STACK_SWITCH_EXPRESSION: { if (type != LEXER_RIGHT_PAREN) { break; } lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { break; } scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); literal_pool_p->source_p = context_p->source_p - 1; parser_stack_pop_uint8 (context_p); scanner_switch_statement_t switch_statement = scanner_context_p->active_switch_statement; parser_stack_push (context_p, &switch_statement, sizeof (scanner_switch_statement_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_SWITCH_BLOCK); scanner_switch_info_t *switch_info_p; switch_info_p = (scanner_switch_info_t *) scanner_insert_info (context_p, context_p->source_p, sizeof (scanner_switch_info_t)); switch_info_p->info.type = SCANNER_TYPE_SWITCH; switch_info_p->case_p = NULL; scanner_context_p->active_switch_statement.last_case_p = &switch_info_p->case_p; lexer_next_token (context_p); if (context_p->token.type != LEXER_RIGHT_BRACE && context_p->token.type != LEXER_KEYW_CASE && context_p->token.type != LEXER_KEYW_DEFAULT) { break; } scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; return SCAN_KEEP_TOKEN; } case SCAN_STACK_CASE_STATEMENT: { if (type != LEXER_COLON) { break; } scanner_source_start_t source_start; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t)); scanner_location_info_t *location_info_p; location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_location_info_t)); location_info_p->info.type = SCANNER_TYPE_CASE; scanner_get_location (&location_info_p->location, context_p); scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; return SCAN_NEXT_TOKEN; } case SCAN_STACK_COLON_EXPRESSION: { if (type != LEXER_COLON) { break; } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; } case SCAN_STACK_ARRAY_LITERAL: case SCAN_STACK_OBJECT_LITERAL: { if ((stack_top == SCAN_STACK_ARRAY_LITERAL && type != LEXER_RIGHT_SQUARE) || (stack_top == SCAN_STACK_OBJECT_LITERAL && type != LEXER_RIGHT_BRACE)) { break; } scanner_source_start_t source_start; uint8_t binding_type = scanner_context_p->binding_type; uint8_t object_literal_flags = 0; parser_stack_pop_uint8 (context_p); if (stack_top == SCAN_STACK_OBJECT_LITERAL) { object_literal_flags = context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); } scanner_context_p->binding_type = context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t)); lexer_next_token (context_p); stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; if (binding_type == SCANNER_BINDING_CATCH && stack_top == SCAN_STACK_CATCH_STATEMENT) { scanner_pop_binding_list (scanner_context_p); if (object_literal_flags != 0) { scanner_info_t *info_p = scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_info_t)); info_p->type = SCANNER_TYPE_LITERAL_FLAGS; info_p->u8_arg = object_literal_flags; } if (context_p->token.type != LEXER_RIGHT_PAREN) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; return SCAN_NEXT_TOKEN; } if (stack_top == SCAN_STACK_FOR_START_PATTERN) { JERRY_ASSERT (binding_type == SCANNER_BINDING_NONE); parser_stack_change_last_uint8 (context_p, SCAN_STACK_FOR_START); if (context_p->token.type == LEXER_KEYW_IN || SCANNER_IDENTIFIER_IS_OF ()) { scanner_info_t *info_p = scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_info_t)); info_p->type = SCANNER_TYPE_LITERAL_FLAGS; info_p->u8_arg = object_literal_flags | SCANNER_LITERAL_DESTRUCTURING_FOR; return SCAN_KEEP_TOKEN; } } if (context_p->token.type != LEXER_ASSIGN) { if (SCANNER_NEEDS_BINDING_LIST (binding_type)) { scanner_pop_binding_list (scanner_context_p); } if ((stack_top == SCAN_STACK_ARRAY_LITERAL || stack_top == SCAN_STACK_OBJECT_LITERAL) && (binding_type == SCANNER_BINDING_NONE || binding_type == SCANNER_BINDING_ARROW_ARG) && context_p->token.type != LEXER_EOS && context_p->token.type != LEXER_COMMA && context_p->token.type != LEXER_RIGHT_BRACE && context_p->token.type != LEXER_RIGHT_SQUARE) { object_literal_flags |= SCANNER_LITERAL_NO_DESTRUCTURING; } if (object_literal_flags != 0) { scanner_info_t *info_p = scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_info_t)); info_p->type = SCANNER_TYPE_LITERAL_FLAGS; info_p->u8_arg = object_literal_flags; } scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } scanner_location_info_t *location_info_p; location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_location_info_t)); location_info_p->info.type = SCANNER_TYPE_INITIALIZER; location_info_p->info.u8_arg = object_literal_flags; scanner_get_location (&location_info_p->location, context_p); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; if (SCANNER_NEEDS_BINDING_LIST (binding_type)) { scanner_binding_item_t *item_p = scanner_context_p->active_binding_list_p->items_p; while (item_p != NULL) { item_p->literal_p->type &= (uint8_t) ~SCANNER_LITERAL_IS_USED; item_p = item_p->next_p; } parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_LIST_INIT); } return SCAN_NEXT_TOKEN; } case SCAN_STACK_PROPERTY_ACCESSOR: { if (type != LEXER_RIGHT_SQUARE) { break; } scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; } case SCAN_STACK_COMPUTED_PROPERTY: { if (type != LEXER_RIGHT_SQUARE) { break; } lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); parser_stack_pop_uint8 (context_p); stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; if (stack_top == SCAN_STACK_FUNCTION_PROPERTY) { scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; } if (stack_top == SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR || stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR) { JERRY_ASSERT (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_CLASS_NAME); if (context_p->token.type == LEXER_LEFT_PAREN) { scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; } if (context_p->token.type == LEXER_ASSIGN) { scanner_push_class_field_initializer (context_p, scanner_context_p); return SCAN_NEXT_TOKEN; } scanner_context_p->mode = (context_p->token.type != LEXER_SEMICOLON ? SCAN_MODE_CLASS_BODY_NO_SCAN : SCAN_MODE_CLASS_BODY); return SCAN_KEEP_TOKEN; } JERRY_ASSERT (stack_top == SCAN_STACK_OBJECT_LITERAL); if (context_p->token.type == LEXER_LEFT_PAREN) { scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; } if (context_p->token.type != LEXER_COLON) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; if (scanner_context_p->binding_type != SCANNER_BINDING_NONE) { scanner_context_p->mode = SCAN_MODE_BINDING; } return SCAN_NEXT_TOKEN; } case SCAN_STACK_COMPUTED_GENERATOR: case SCAN_STACK_COMPUTED_ASYNC: case SCAN_STACK_COMPUTED_ASYNC_GENERATOR: { if (type != LEXER_RIGHT_SQUARE) { break; } lexer_next_token (context_p); parser_stack_pop_uint8 (context_p); JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL || context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_PROPERTY); uint16_t status_flags = (uint16_t) (SCANNER_LITERAL_POOL_FUNCTION | SCANNER_LITERAL_POOL_GENERATOR | SCANNER_FROM_COMPUTED_TO_LITERAL_POOL (stack_top)); scanner_push_literal_pool (context_p, scanner_context_p, status_flags); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; } case SCAN_STACK_TEMPLATE_STRING: case SCAN_STACK_TAGGED_TEMPLATE_LITERAL: { if (type != LEXER_RIGHT_BRACE) { break; } context_p->source_p--; context_p->column--; lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; } else { parser_stack_pop_uint8 (context_p); scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; } return SCAN_NEXT_TOKEN; } case SCAN_STACK_ARROW_ARGUMENTS: { if (type != LEXER_RIGHT_PAREN) { break; } scanner_check_arrow (context_p, scanner_context_p); return SCAN_KEEP_TOKEN; } case SCAN_STACK_ARROW_EXPRESSION: { scanner_pop_literal_pool (context_p, scanner_context_p); parser_stack_pop_uint8 (context_p); lexer_update_await_yield (context_p, context_p->status_flags); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; return SCAN_KEEP_TOKEN; } case SCAN_STACK_CLASS_EXTENDS: { if (type != LEXER_LEFT_BRACE) { break; } scanner_context_p->mode = SCAN_MODE_CLASS_BODY; parser_stack_pop_uint8 (context_p); return SCAN_KEEP_TOKEN; } case SCAN_STACK_CLASS_FIELD_INITIALIZER: { scanner_source_start_t source_start; const uint8_t *source_p = NULL; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t)); scanner_pop_literal_pool (context_p, scanner_context_p); scanner_context_p->mode = SCAN_MODE_CLASS_BODY_NO_SCAN; switch (type) { case LEXER_SEMICOLON: { source_p = context_p->source_p - 1; scanner_context_p->mode = SCAN_MODE_CLASS_BODY; break; } case LEXER_RIGHT_BRACE: { source_p = context_p->source_p - 1; break; } default: { if (!(context_p->token.flags & LEXER_WAS_NEWLINE)) { break; } if (type == LEXER_LEFT_SQUARE) { source_p = context_p->source_p - 1; break; } if (type == LEXER_LITERAL) { if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_NUMBER_LITERAL) { source_p = context_p->token.lit_location.char_p; } else if (context_p->token.lit_location.type == LEXER_STRING_LITERAL) { source_p = context_p->token.lit_location.char_p - 1; } break; } if (type == context_p->token.keyword_type && type != LEXER_EOS) { /* Convert keyword to literal. */ source_p = context_p->token.lit_location.char_p; context_p->token.type = LEXER_LITERAL; } break; } } if (JERRY_UNLIKELY (source_p == NULL)) { scanner_raise_error (context_p); } scanner_location_info_t *location_info_p; location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_location_info_t)); location_info_p->info.type = SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END; location_info_p->location.source_p = source_p; location_info_p->location.line = context_p->token.line; location_info_p->location.column = context_p->token.column; return SCAN_KEEP_TOKEN; } case SCAN_STACK_FUNCTION_PARAMETERS: { parser_stack_pop_uint8 (context_p); if (type != LEXER_RIGHT_PAREN && (type != LEXER_EOS || context_p->stack_top_uint8 != SCAN_STACK_SCRIPT_FUNCTION)) { break; } scanner_context_p->mode = SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; } default: { scanner_context_p->mode = SCAN_MODE_STATEMENT_END; return SCAN_KEEP_TOKEN; } } scanner_raise_error (context_p); return SCAN_NEXT_TOKEN; } /* scanner_scan_primary_expression_end */ /** * Scan statements. * * @return SCAN_NEXT_TOKEN to read the next token, or SCAN_KEEP_TOKEN to do nothing */ static scan_return_types_t scanner_scan_statement (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ lexer_token_type_t type, /**< current token type */ scan_stack_modes_t stack_top) /**< current stack top */ { switch (type) { case LEXER_SEMICOLON: { scanner_context_p->mode = SCAN_MODE_STATEMENT_END; return SCAN_KEEP_TOKEN; } case LEXER_LEFT_BRACE: { scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); literal_pool_p->source_p = context_p->source_p; scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_STATEMENT); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_DO: { scanner_context_p->mode = SCAN_MODE_STATEMENT; parser_stack_push_uint8 (context_p, SCAN_STACK_DO_STATEMENT); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_TRY: { lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { scanner_raise_error (context_p); } scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); literal_pool_p->source_p = context_p->source_p; scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; parser_stack_push_uint8 (context_p, SCAN_STACK_TRY_STATEMENT); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_DEBUGGER: { scanner_context_p->mode = SCAN_MODE_STATEMENT_END; return SCAN_NEXT_TOKEN; } case LEXER_KEYW_IF: case LEXER_KEYW_WITH: case LEXER_KEYW_SWITCH: { lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); } uint8_t mode = SCAN_STACK_STATEMENT_WITH_EXPR; if (type == LEXER_KEYW_IF) { parser_stack_push_uint8 (context_p, SCAN_STACK_IF_STATEMENT); } else if (type == LEXER_KEYW_WITH) { mode = SCAN_STACK_WITH_EXPRESSION; } else if (type == LEXER_KEYW_SWITCH) { mode = SCAN_STACK_SWITCH_EXPRESSION; } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; parser_stack_push_uint8 (context_p, mode); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_WHILE: { lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; scanner_source_start_t source_start; source_start.source_p = context_p->source_p; parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_WHILE_EXPRESSION); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_FOR: { lexer_next_token (context_p); if (context_p->token.type == LEXER_KEYW_AWAIT) { lexer_next_token (context_p); } if (context_p->token.type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); } scanner_for_statement_t for_statement; for_statement.u.source_p = context_p->source_p; uint8_t stack_mode = SCAN_STACK_FOR_START; scan_return_types_t return_type = SCAN_KEEP_TOKEN; lexer_next_token (context_p); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; const uint8_t *source_p = context_p->source_p; switch (context_p->token.type) { case LEXER_SEMICOLON: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; break; } case LEXER_KEYW_VAR: { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; stack_mode = SCAN_STACK_FOR_VAR_START; return_type = SCAN_NEXT_TOKEN; break; } case LEXER_LEFT_BRACE: case LEXER_LEFT_SQUARE: { stack_mode = SCAN_STACK_FOR_START_PATTERN; break; } case LEXER_LITERAL: { if (!lexer_token_is_let (context_p)) { break; } parser_line_counter_t line = context_p->line; parser_line_counter_t column = context_p->column; if (lexer_check_arrow (context_p)) { context_p->source_p = source_p; context_p->line = line; context_p->column = column; context_p->token.flags &= (uint8_t) ~LEXER_NO_SKIP_SPACES; break; } lexer_next_token (context_p); type = (lexer_token_type_t) context_p->token.type; if (type != LEXER_LEFT_SQUARE && type != LEXER_LEFT_BRACE && (type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL)) { scanner_info_t *info_p = scanner_insert_info (context_p, source_p, sizeof (scanner_info_t)); info_p->type = SCANNER_TYPE_LET_EXPRESSION; scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; break; } scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; /* FALLTHRU */ } case LEXER_KEYW_LET: case LEXER_KEYW_CONST: { scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); literal_pool_p->source_p = source_p; if (scanner_context_p->mode == SCAN_MODE_PRIMARY_EXPRESSION) { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; return_type = SCAN_NEXT_TOKEN; } stack_mode = ((context_p->token.type == LEXER_KEYW_CONST) ? SCAN_STACK_FOR_CONST_START : SCAN_STACK_FOR_LET_START); break; } } parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t)); parser_stack_push_uint8 (context_p, stack_mode); return return_type; } case LEXER_KEYW_VAR: { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; parser_stack_push_uint8 (context_p, SCAN_STACK_VAR); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_LET: { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; parser_stack_push_uint8 (context_p, SCAN_STACK_LET); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_CONST: { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; parser_stack_push_uint8 (context_p, SCAN_STACK_CONST); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_THROW: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_NEXT_TOKEN; } case LEXER_KEYW_RETURN: { lexer_next_token (context_p); if (!(context_p->token.flags & LEXER_WAS_NEWLINE) && context_p->token.type != LEXER_SEMICOLON && context_p->token.type != LEXER_EOS && context_p->token.type != LEXER_RIGHT_BRACE) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } scanner_context_p->mode = SCAN_MODE_STATEMENT_END; return SCAN_KEEP_TOKEN; } case LEXER_KEYW_BREAK: case LEXER_KEYW_CONTINUE: { lexer_next_token (context_p); scanner_context_p->mode = SCAN_MODE_STATEMENT_END; if (!(context_p->token.flags & LEXER_WAS_NEWLINE) && context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { return SCAN_NEXT_TOKEN; } return SCAN_KEEP_TOKEN; } case LEXER_KEYW_CASE: case LEXER_KEYW_DEFAULT: { if (stack_top != SCAN_STACK_SWITCH_BLOCK) { scanner_raise_error (context_p); } scanner_case_info_t *case_info_p; case_info_p = (scanner_case_info_t *) scanner_malloc (context_p, sizeof (scanner_case_info_t)); *(scanner_context_p->active_switch_statement.last_case_p) = case_info_p; scanner_context_p->active_switch_statement.last_case_p = &case_info_p->next_p; case_info_p->next_p = NULL; scanner_get_location (&case_info_p->location, context_p); if (type == LEXER_KEYW_DEFAULT) { lexer_next_token (context_p); if (context_p->token.type != LEXER_COLON) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; return SCAN_NEXT_TOKEN; } scanner_source_start_t source_start; source_start.source_p = context_p->source_p; parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_CASE_STATEMENT); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_NEXT_TOKEN; } case LEXER_KEYW_FUNCTION: { uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION | SCANNER_LITERAL_POOL_FUNCTION_STATEMENT; if (scanner_context_p->async_source_p != NULL) { scanner_context_p->status_flags |= SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION; status_flags |= SCANNER_LITERAL_POOL_ASYNC; } lexer_next_token (context_p); if (context_p->token.type == LEXER_MULTIPLY) { status_flags |= SCANNER_LITERAL_POOL_GENERATOR; lexer_next_token (context_p); } if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } lexer_lit_location_t *literal_p = scanner_add_literal (context_p, scanner_context_p); const uint8_t mask = (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LOCAL); if ((literal_p->type & SCANNER_LITERAL_IS_LOCAL) && (literal_p->type & mask) != (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_DESTRUCTURED_ARG) && (literal_p->type & mask) != SCANNER_LITERAL_IS_LOCAL_FUNC) { scanner_raise_redeclaration_error (context_p); } scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; if (!(literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION) && (literal_p->type & (SCANNER_LITERAL_IS_VAR))) { scanner_raise_redeclaration_error (context_p); } literal_p->type |= SCANNER_LITERAL_IS_LOCAL_FUNC; scanner_context_p->status_flags &= (uint16_t) ~SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION; scanner_push_literal_pool (context_p, scanner_context_p, status_flags); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_STATEMENT); return SCAN_NEXT_TOKEN; } case LEXER_KEYW_CLASS: { lexer_lit_location_t *literal_p; literal_p = scanner_push_class_declaration (context_p, scanner_context_p, SCAN_STACK_CLASS_STATEMENT); if (literal_p == NULL) { scanner_raise_error (context_p); } scanner_detect_invalid_let (context_p, literal_p); literal_p->type |= SCANNER_LITERAL_IS_LET; if (literal_p->type & SCANNER_LITERAL_IS_USED) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; } #if JERRY_MODULE_SYSTEM if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_EXPORT) { literal_p->type |= SCANNER_LITERAL_NO_REG; scanner_context_p->active_literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_EXPORT; } #endif /* JERRY_MODULE_SYSTEM */ return SCAN_NEXT_TOKEN; } #if JERRY_MODULE_SYSTEM case LEXER_KEYW_IMPORT: { lexer_next_token (context_p); if (context_p->token.type == LEXER_DOT) { scanner_check_import_meta (context_p); scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } if (context_p->token.type == LEXER_LEFT_PAREN) { scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } if (stack_top != SCAN_STACK_SCRIPT) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_STATEMENT_END; if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_STRING_LITERAL) { return SCAN_NEXT_TOKEN; } bool parse_imports = true; if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { lexer_lit_location_t *literal_p = scanner_add_literal (context_p, scanner_context_p); scanner_detect_invalid_let (context_p, literal_p); literal_p->type |= SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_NO_REG; lexer_next_token (context_p); if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); } else { parse_imports = false; } } if (parse_imports) { if (context_p->token.type == LEXER_MULTIPLY) { lexer_next_token (context_p); if (!lexer_token_is_identifier (context_p, "as", 2)) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } lexer_lit_location_t *literal_p = scanner_add_literal (context_p, scanner_context_p); scanner_detect_invalid_let (context_p, literal_p); literal_p->type |= SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_NO_REG; lexer_next_token (context_p); } else if (context_p->token.type == LEXER_LEFT_BRACE) { lexer_next_token (context_p); while (context_p->token.type != LEXER_RIGHT_BRACE) { if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } const uint8_t *source_p = context_p->source_p; if (lexer_check_next_character (context_p, LIT_CHAR_LOWERCASE_A)) { lexer_next_token (context_p); if (!lexer_token_is_identifier (context_p, "as", 2)) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } source_p = context_p->source_p; } lexer_lit_location_t *literal_p = scanner_add_literal (context_p, scanner_context_p); if (literal_p->type & (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_LOCAL)) { context_p->source_p = source_p; scanner_raise_redeclaration_error (context_p); } if (literal_p->type & SCANNER_LITERAL_IS_FUNC) { literal_p->type &= (uint8_t) ~SCANNER_LITERAL_IS_FUNC; } literal_p->type |= SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_NO_REG; lexer_next_token (context_p); if (context_p->token.type != LEXER_RIGHT_BRACE) { if (context_p->token.type != LEXER_COMMA) { scanner_raise_error (context_p); } lexer_next_token (context_p); } } lexer_next_token (context_p); } else { scanner_raise_error (context_p); } } if (!lexer_token_is_identifier (context_p, "from", 4)) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL && context_p->token.lit_location.type != LEXER_STRING_LITERAL) { scanner_raise_error (context_p); } return SCAN_NEXT_TOKEN; } case LEXER_KEYW_EXPORT: { if (stack_top != SCAN_STACK_SCRIPT) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type == LEXER_KEYW_DEFAULT) { lexer_next_token (context_p); parser_stack_push_uint8 (context_p, SCAN_STACK_EXPORT_DEFAULT); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } scanner_context_p->mode = SCAN_MODE_STATEMENT_END; if (context_p->token.type == LEXER_MULTIPLY) { lexer_next_token (context_p); if (lexer_token_is_identifier (context_p, "as", 2)) { lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } lexer_next_token (context_p); } if (!lexer_token_is_identifier (context_p, "from", 4)) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL && context_p->token.lit_location.type == LEXER_STRING_LITERAL) { scanner_raise_error (context_p); } return SCAN_NEXT_TOKEN; } scanner_source_start_t source_start; source_start.source_p = context_p->source_p; if (context_p->token.type == LEXER_LEFT_BRACE) { lexer_next_token (context_p); while (context_p->token.type != LEXER_RIGHT_BRACE) { if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (lexer_token_is_identifier (context_p, "as", 2)) { lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } lexer_next_token (context_p); } if (context_p->token.type != LEXER_RIGHT_BRACE) { if (context_p->token.type != LEXER_COMMA) { scanner_raise_error (context_p); } lexer_next_token (context_p); } } lexer_next_token (context_p); if (!lexer_token_is_identifier (context_p, "from", 4)) { return SCAN_KEEP_TOKEN; } scanner_info_t *info_p = scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_info_t)); info_p->type = SCANNER_TYPE_EXPORT_MODULE_SPECIFIER; lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL && context_p->token.lit_location.type == LEXER_STRING_LITERAL) { scanner_raise_error (context_p); } return SCAN_NEXT_TOKEN; } switch (context_p->token.type) { case LEXER_KEYW_CLASS: case LEXER_KEYW_LET: case LEXER_KEYW_CONST: case LEXER_KEYW_VAR: { scanner_context_p->active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_IN_EXPORT; break; } } scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_KEEP_TOKEN; } #endif /* JERRY_MODULE_SYSTEM */ default: { break; } } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; if (type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { if (JERRY_UNLIKELY (lexer_check_next_character (context_p, LIT_CHAR_COLON))) { lexer_consume_next_character (context_p); scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_NEXT_TOKEN; } JERRY_ASSERT (context_p->token.flags & LEXER_NO_SKIP_SPACES); /* The colon needs to be checked first because the parser also checks * it first, and this check skips the spaces which affects source_p. */ if (JERRY_UNLIKELY (lexer_check_arrow (context_p))) { scanner_scan_simple_arrow (context_p, scanner_context_p, context_p->source_p); return SCAN_KEEP_TOKEN; } if (JERRY_UNLIKELY (lexer_token_is_let (context_p))) { lexer_lit_location_t let_literal = context_p->token.lit_location; const uint8_t *source_p = context_p->source_p; lexer_next_token (context_p); type = (lexer_token_type_t) context_p->token.type; if (type == LEXER_LEFT_SQUARE || type == LEXER_LEFT_BRACE || (type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL)) { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; parser_stack_push_uint8 (context_p, SCAN_STACK_LET); return SCAN_KEEP_TOKEN; } scanner_info_t *info_p = scanner_insert_info (context_p, source_p, sizeof (scanner_info_t)); info_p->type = SCANNER_TYPE_LET_EXPRESSION; lexer_lit_location_t *lit_location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &let_literal); lit_location_p->type |= SCANNER_LITERAL_IS_USED; if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) { lit_location_p->type |= SCANNER_LITERAL_NO_REG; } scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; } if (JERRY_UNLIKELY (lexer_token_is_async (context_p))) { scanner_context_p->async_source_p = context_p->source_p; if (scanner_check_async_function (context_p, scanner_context_p)) { scanner_context_p->mode = SCAN_MODE_STATEMENT; } return SCAN_KEEP_TOKEN; } scanner_add_reference (context_p, scanner_context_p); scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_NEXT_TOKEN; } return SCAN_KEEP_TOKEN; } /* scanner_scan_statement */ /** * Scan statement terminator. * * @return SCAN_NEXT_TOKEN to read the next token, or SCAN_KEEP_TOKEN to do nothing */ static scan_return_types_t scanner_scan_statement_end (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ lexer_token_type_t type) /**< current token type */ { bool terminator_found = false; if (type == LEXER_SEMICOLON) { lexer_next_token (context_p); terminator_found = true; } while (true) { type = (lexer_token_type_t) context_p->token.type; switch (context_p->stack_top_uint8) { case SCAN_STACK_SCRIPT: case SCAN_STACK_SCRIPT_FUNCTION: { if (type == LEXER_EOS) { return SCAN_NEXT_TOKEN; } break; } case SCAN_STACK_BLOCK_STATEMENT: case SCAN_STACK_CLASS_STATEMENT: case SCAN_STACK_FUNCTION_STATEMENT: { if (type != LEXER_RIGHT_BRACE) { break; } if (context_p->stack_top_uint8 != SCAN_STACK_CLASS_STATEMENT) { scanner_pop_literal_pool (context_p, scanner_context_p); } terminator_found = true; parser_stack_pop_uint8 (context_p); #if JERRY_MODULE_SYSTEM scanner_context_p->active_literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_EXPORT; #endif /* JERRY_MODULE_SYSTEM */ lexer_next_token (context_p); continue; } case SCAN_STACK_FUNCTION_EXPRESSION: case SCAN_STACK_FUNCTION_ARROW: { if (type != LEXER_RIGHT_BRACE) { break; } scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; if (context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_ARROW) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; } scanner_pop_literal_pool (context_p, scanner_context_p); parser_stack_pop_uint8 (context_p); #if JERRY_MODULE_SYSTEM if (context_p->stack_top_uint8 == SCAN_STACK_EXPORT_DEFAULT) { terminator_found = true; parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); continue; } #endif /* JERRY_MODULE_SYSTEM */ return SCAN_NEXT_TOKEN; } case SCAN_STACK_FUNCTION_PROPERTY: { if (type != LEXER_RIGHT_BRACE) { break; } bool has_super_reference = (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE) != 0; scanner_pop_literal_pool (context_p, scanner_context_p); parser_stack_pop_uint8 (context_p); if (context_p->stack_top_uint8 == SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR || context_p->stack_top_uint8 == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR) { scanner_context_p->mode = SCAN_MODE_CLASS_BODY; return SCAN_KEEP_TOKEN; } if (has_super_reference && context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL) { *parser_stack_get_prev_uint8 (context_p) |= SCANNER_LITERAL_OBJECT_HAS_SUPER; } lexer_next_token (context_p); if (context_p->token.type == LEXER_RIGHT_BRACE) { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; return SCAN_KEEP_TOKEN; } if (context_p->token.type != LEXER_COMMA) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; return SCAN_KEEP_TOKEN; } case SCAN_STACK_SWITCH_BLOCK: { if (type != LEXER_RIGHT_BRACE) { break; } scanner_switch_statement_t switch_statement; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &switch_statement, sizeof (scanner_switch_statement_t)); scanner_context_p->active_switch_statement = switch_statement; scanner_pop_literal_pool (context_p, scanner_context_p); terminator_found = true; lexer_next_token (context_p); continue; } case SCAN_STACK_IF_STATEMENT: { parser_stack_pop_uint8 (context_p); if (type == LEXER_KEYW_ELSE && (terminator_found || (context_p->token.flags & LEXER_WAS_NEWLINE))) { scanner_check_function_after_if (context_p, scanner_context_p); return SCAN_KEEP_TOKEN; } continue; } case SCAN_STACK_WITH_STATEMENT: { scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; JERRY_ASSERT (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH); parser_stack_pop_uint8 (context_p); if (context_p->stack_top_uint8 == 0) { literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_WITH; } parser_stack_pop_uint8 (context_p); continue; } case SCAN_STACK_DO_STATEMENT: { parser_stack_pop_uint8 (context_p); if (type != LEXER_KEYW_WHILE || (!terminator_found && !(context_p->token.flags & LEXER_WAS_NEWLINE))) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); } parser_stack_push_uint8 (context_p, SCAN_STACK_DO_EXPRESSION); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return SCAN_NEXT_TOKEN; } case SCAN_STACK_DO_EXPRESSION: { parser_stack_pop_uint8 (context_p); terminator_found = true; continue; } case SCAN_STACK_CLASS_STATIC_BLOCK: { if (type != LEXER_RIGHT_BRACE) { break; } scanner_pop_literal_pool (context_p, scanner_context_p); scanner_source_start_t start_range; parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, &start_range, sizeof (scanner_source_start_t)); scanner_context_p->mode = SCAN_MODE_CLASS_BODY_NO_SCAN; scanner_location_info_t *location_info_p; location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p, start_range.source_p, sizeof (scanner_location_info_t)); location_info_p->info.type = SCANNER_TYPE_CLASS_STATIC_BLOCK_END; location_info_p->location.source_p = context_p->source_p; location_info_p->location.line = context_p->token.line; location_info_p->location.column = context_p->token.column; lexer_scan_identifier (context_p, (lexer_parse_options_t) ((unsigned int) LEXER_PARSE_CHECK_KEYWORDS | (unsigned int) LEXER_PARSE_NO_STRICT_IDENT_ERROR)); return SCAN_KEEP_TOKEN; } case SCAN_STACK_PRIVATE_BLOCK_EARLY: { parser_list_iterator_t literal_iterator; lexer_lit_location_t *literal_p; parser_list_iterator_init (&scanner_context_p->active_literal_pool_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if ((literal_p->type & (SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_IS_CONST)) && (literal_p->type & SCANNER_LITERAL_IS_USED)) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; } } /* FALLTHRU */ } case SCAN_STACK_PRIVATE_BLOCK: { parser_stack_pop_uint8 (context_p); scanner_pop_literal_pool (context_p, scanner_context_p); continue; } #if JERRY_MODULE_SYSTEM case SCAN_STACK_EXPORT_DEFAULT: { parser_stack_pop_uint8 (context_p); lexer_lit_location_t *location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &lexer_default_literal); location_p->type |= SCANNER_LITERAL_IS_VAR; continue; } #endif /* JERRY_MODULE_SYSTEM */ default: { JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_TRY_STATEMENT || context_p->stack_top_uint8 == SCAN_STACK_CATCH_STATEMENT); if (type != LEXER_RIGHT_BRACE) { break; } uint8_t stack_top = context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); scanner_pop_literal_pool (context_p, scanner_context_p); /* A finally statement is optional after a try or catch statement. */ if (context_p->token.type == LEXER_KEYW_FINALLY) { lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { scanner_raise_error (context_p); } scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); literal_pool_p->source_p = context_p->source_p; parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_STATEMENT); scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; return SCAN_NEXT_TOKEN; } if (stack_top == SCAN_STACK_CATCH_STATEMENT) { terminator_found = true; continue; } /* A catch statement must be present after a try statement unless a finally is provided. */ if (context_p->token.type != LEXER_KEYW_CATCH) { scanner_raise_error (context_p); } lexer_next_token (context_p); scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); literal_pool_p->source_p = context_p->source_p; parser_stack_push_uint8 (context_p, SCAN_STACK_CATCH_STATEMENT); if (context_p->token.type == LEXER_LEFT_BRACE) { scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; return SCAN_NEXT_TOKEN; } if (context_p->token.type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type == LEXER_LEFT_SQUARE || context_p->token.type == LEXER_LEFT_BRACE) { scanner_push_destructuring_pattern (context_p, scanner_context_p, SCANNER_BINDING_CATCH, false); if (context_p->token.type == LEXER_LEFT_SQUARE) { parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); scanner_context_p->mode = SCAN_MODE_BINDING; return SCAN_NEXT_TOKEN; } parser_stack_push_uint8 (context_p, 0); parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; return SCAN_KEEP_TOKEN; } if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } lexer_lit_location_t *lit_location_p = scanner_add_literal (context_p, scanner_context_p); lit_location_p->type |= SCANNER_LITERAL_IS_LOCAL; lexer_next_token (context_p); if (context_p->token.type != LEXER_RIGHT_PAREN) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; return SCAN_NEXT_TOKEN; } } if (!terminator_found && !(context_p->token.flags & LEXER_WAS_NEWLINE)) { scanner_raise_error (context_p); } scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_KEEP_TOKEN; } } /* scanner_scan_statement_end */ /** * Scan the whole source code. */ void JERRY_ATTR_NOINLINE scanner_scan_all (parser_context_t *context_p) /**< context */ { scanner_context_t scanner_context; #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- Scanning start ---\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ scanner_context.context_status_flags = context_p->status_flags; scanner_context.status_flags = SCANNER_CONTEXT_NO_FLAGS; scanner_context.binding_type = SCANNER_BINDING_NONE; scanner_context.active_binding_list_p = NULL; scanner_context.active_literal_pool_p = NULL; scanner_context.active_switch_statement.last_case_p = NULL; scanner_context.end_arguments_p = NULL; scanner_context.async_source_p = NULL; /* This assignment must be here because of Apple compilers. */ context_p->u.scanner_context_p = &scanner_context; context_p->global_status_flags |= ECMA_PARSE_INTERNAL_PRE_SCANNING; parser_stack_init (context_p); PARSER_TRY (context_p->try_buffer) { if (context_p->arguments_start_p == NULL) { context_p->source_p = context_p->source_start_p; context_p->source_end_p = context_p->source_start_p + context_p->source_size; uint16_t status_flags = (SCANNER_LITERAL_POOL_FUNCTION | SCANNER_LITERAL_POOL_NO_ARGUMENTS | SCANNER_LITERAL_POOL_CAN_EVAL); if (context_p->status_flags & PARSER_IS_STRICT) { status_flags |= SCANNER_LITERAL_POOL_IS_STRICT; } scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, &scanner_context, status_flags); literal_pool_p->source_p = context_p->source_start_p; parser_stack_push_uint8 (context_p, SCAN_STACK_SCRIPT); lexer_next_token (context_p); scanner_check_directives (context_p, &scanner_context); } else { context_p->source_p = context_p->arguments_start_p; context_p->source_end_p = context_p->arguments_start_p + context_p->arguments_size; uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION; if (context_p->status_flags & PARSER_IS_STRICT) { status_flags |= SCANNER_LITERAL_POOL_IS_STRICT; } if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) { status_flags |= SCANNER_LITERAL_POOL_GENERATOR; } if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { status_flags |= SCANNER_LITERAL_POOL_ASYNC; } scanner_push_literal_pool (context_p, &scanner_context, status_flags); scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; parser_stack_push_uint8 (context_p, SCAN_STACK_SCRIPT_FUNCTION); /* Faking the first token. */ context_p->token.type = LEXER_LEFT_PAREN; } while (true) { lexer_token_type_t type = (lexer_token_type_t) context_p->token.type; scan_stack_modes_t stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; switch (scanner_context.mode) { case SCAN_MODE_PRIMARY_EXPRESSION: { if (type == LEXER_ADD || type == LEXER_SUBTRACT || LEXER_IS_UNARY_OP_TOKEN (type)) { break; } /* FALLTHRU */ } case SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW: { if (scanner_scan_primary_expression (context_p, &scanner_context, type, stack_top) != SCAN_NEXT_TOKEN) { continue; } break; } case SCAN_MODE_CLASS_DECLARATION: { if (context_p->token.type == LEXER_KEYW_EXTENDS) { parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_EXTENDS); scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } else if (context_p->token.type != LEXER_LEFT_BRACE) { scanner_raise_error (context_p); } scanner_context.mode = SCAN_MODE_CLASS_BODY; /* FALLTHRU */ } case SCAN_MODE_CLASS_BODY: { lexer_skip_empty_statements (context_p); lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); /* FALLTHRU */ } case SCAN_MODE_CLASS_BODY_NO_SCAN: { JERRY_ASSERT (stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR || stack_top == SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR); JERRY_ASSERT (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_CLASS_NAME); if (context_p->token.type == LEXER_RIGHT_BRACE) { parser_stack_pop_uint8 (context_p); scanner_class_info_t *private_members_p; parser_stack_pop (context_p, &private_members_p, sizeof (scanner_class_info_t *)); private_members_p->info.u8_arg |= SCANNER_SUCCESSFUL_CLASS_SCAN; scanner_pop_literal_pool (context_p, &scanner_context); JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_CLASS_STATEMENT || context_p->stack_top_uint8 == SCAN_STACK_CLASS_EXPRESSION); if (context_p->stack_top_uint8 == SCAN_STACK_CLASS_STATEMENT) { /* The token is kept to disallow consuming a semicolon after it. */ scanner_context.mode = SCAN_MODE_STATEMENT_END; continue; } scanner_context.mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; parser_stack_pop_uint8 (context_p); #if JERRY_MODULE_SYSTEM if (context_p->stack_top_uint8 == SCAN_STACK_EXPORT_DEFAULT) { /* The token is kept to disallow consuming a semicolon after it. */ parser_stack_change_last_uint8 (context_p, SCAN_STACK_CLASS_STATEMENT); scanner_context.mode = SCAN_MODE_STATEMENT_END; continue; } #endif /* JERRY_MODULE_SYSTEM */ break; } bool is_private = false; scanner_private_field_flags_t private_field_flags = SCANNER_PRIVATE_FIELD_PROPERTY; if (context_p->token.type == LEXER_HASHMARK) { is_private = true; context_p->token.flags |= LEXER_NO_SKIP_SPACES; lexer_next_token (context_p); } bool identifier_found = false; if (context_p->token.type == LEXER_LITERAL && LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) && lexer_compare_literal_to_string (context_p, "constructor", 11) && stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR) { parser_stack_pop_uint8 (context_p); scanner_class_info_t *private_members_p; parser_stack_pop (context_p, &private_members_p, sizeof (scanner_class_info_t *)); private_members_p->info.u8_arg = SCANNER_CONSTRUCTOR_EXPLICIT; parser_stack_push (context_p, &private_members_p, sizeof (scanner_class_info_t *)); parser_stack_push_uint8 (context_p, SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR); } else if (lexer_token_is_identifier (context_p, "static", 6)) { scanner_source_start_t static_start; static_start.source_p = context_p->source_p - 1; lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); identifier_found = true; private_field_flags = (scanner_private_field_flags_t) ((unsigned int) private_field_flags | (unsigned int) SCANNER_PRIVATE_FIELD_STATIC); if (!is_private && context_p->token.type == LEXER_LEFT_BRACE) { parser_stack_push (context_p, &static_start, sizeof (scanner_source_start_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_STATIC_BLOCK); scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); literal_pool_p->source_p = context_p->source_p - 1; lexer_next_token (context_p); scanner_context.mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; continue; } } scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; uint16_t literal_pool_flags = SCANNER_LITERAL_POOL_FUNCTION; private_field_flags = ((bool)private_field_flags || lexer_token_is_identifier (context_p, "get", 3) ? SCANNER_PRIVATE_FIELD_GETTER : (scanner_private_field_flags_t) 0); private_field_flags = ((bool)private_field_flags || lexer_token_is_identifier (context_p, "set", 3) ? SCANNER_PRIVATE_FIELD_SETTER : (scanner_private_field_flags_t) 0); if (private_field_flags & SCANNER_PRIVATE_FIELD_GETTER_SETTER) { private_field_flags = (scanner_private_field_flags_t) ((uint32_t) private_field_flags & ~(uint32_t) SCANNER_PRIVATE_FIELD_PROPERTY); lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); identifier_found = true; if (context_p->token.type == LEXER_LEFT_PAREN) { if (is_private) { private_field_flags = (scanner_private_field_flags_t) ((int) private_field_flags | (int) SCANNER_PRIVATE_FIELD_METHOD); scanner_add_private_identifier (context_p, private_field_flags); } parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); continue; } } else if (lexer_token_is_identifier (context_p, "async", 5)) { lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); identifier_found = true; if (!(context_p->token.flags & LEXER_WAS_NEWLINE)) { if (context_p->token.type == LEXER_LEFT_PAREN) { if (is_private) { private_field_flags = (scanner_private_field_flags_t) ((int) private_field_flags | (int) SCANNER_PRIVATE_FIELD_METHOD); scanner_add_private_identifier (context_p, private_field_flags); } parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); continue; } literal_pool_flags |= SCANNER_LITERAL_POOL_ASYNC; if (context_p->token.type == LEXER_MULTIPLY) { lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR; } } } else if (context_p->token.type == LEXER_MULTIPLY) { if (is_private) { scanner_raise_error (context_p); } lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR; } if (context_p->token.type == LEXER_LEFT_SQUARE) { if (is_private) { scanner_raise_error (context_p); } if (literal_pool_flags != SCANNER_LITERAL_POOL_FUNCTION) { parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); } parser_stack_push_uint8 (context_p, SCANNER_FROM_LITERAL_POOL_TO_COMPUTED (literal_pool_flags)); scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } if (context_p->token.type == LEXER_HASHMARK) { if (is_private) { scanner_raise_error (context_p); } is_private = true; context_p->token.flags |= LEXER_NO_SKIP_SPACES; lexer_next_token (context_p); } if (is_private) { if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { private_field_flags = (scanner_private_field_flags_t) ((int) private_field_flags | (int) SCANNER_PRIVATE_FIELD_METHOD); } scanner_add_private_identifier (context_p, private_field_flags); } if (context_p->token.type == LEXER_LITERAL) { lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); identifier_found = true; } if (!identifier_found) { scanner_raise_error (context_p); } if (context_p->token.type == LEXER_LEFT_PAREN) { if (literal_pool_flags & SCANNER_LITERAL_POOL_GENERATOR) { context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION; } parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_push_literal_pool (context_p, &scanner_context, literal_pool_flags); continue; } if (literal_pool_flags & SCANNER_LITERAL_POOL_GENERATOR) { scanner_raise_error (context_p); } if (context_p->token.type == LEXER_ASSIGN) { scanner_push_class_field_initializer (context_p, &scanner_context); break; } if (context_p->token.type == LEXER_SEMICOLON) { scanner_context.mode = SCAN_MODE_CLASS_BODY; continue; } if (context_p->token.type != LEXER_RIGHT_BRACE && !(context_p->token.flags & LEXER_WAS_NEWLINE)) { scanner_raise_error (context_p); } scanner_context.mode = SCAN_MODE_CLASS_BODY_NO_SCAN; continue; } case SCAN_MODE_POST_PRIMARY_EXPRESSION: { if (scanner_scan_post_primary_expression (context_p, &scanner_context, type, stack_top)) { break; } type = (lexer_token_type_t) context_p->token.type; /* FALLTHRU */ } case SCAN_MODE_PRIMARY_EXPRESSION_END: { if (scanner_scan_primary_expression_end (context_p, &scanner_context, type, stack_top) != SCAN_NEXT_TOKEN) { continue; } break; } case SCAN_MODE_STATEMENT_OR_TERMINATOR: { if (type == LEXER_RIGHT_BRACE || type == LEXER_EOS) { scanner_context.mode = SCAN_MODE_STATEMENT_END; continue; } /* FALLTHRU */ } case SCAN_MODE_STATEMENT: { if (scanner_scan_statement (context_p, &scanner_context, type, stack_top) != SCAN_NEXT_TOKEN) { continue; } break; } case SCAN_MODE_STATEMENT_END: { if (scanner_scan_statement_end (context_p, &scanner_context, type) != SCAN_NEXT_TOKEN) { continue; } if (context_p->token.type == LEXER_EOS) { goto scan_completed; } break; } case SCAN_MODE_VAR_STATEMENT: { if (type == LEXER_LEFT_SQUARE || type == LEXER_LEFT_BRACE) { uint8_t binding_type = SCANNER_BINDING_VAR; if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_FOR_LET_START) { binding_type = SCANNER_BINDING_LET; } else if (stack_top == SCAN_STACK_CONST || stack_top == SCAN_STACK_FOR_CONST_START) { binding_type = SCANNER_BINDING_CONST; } scanner_push_destructuring_pattern (context_p, &scanner_context, binding_type, false); if (type == LEXER_LEFT_SQUARE) { parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); scanner_context.mode = SCAN_MODE_BINDING; break; } parser_stack_push_uint8 (context_p, 0); parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); scanner_context.mode = SCAN_MODE_PROPERTY_NAME; continue; } if (type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } lexer_lit_location_t *literal_p = scanner_add_literal (context_p, &scanner_context); if (stack_top != SCAN_STACK_VAR && stack_top != SCAN_STACK_FOR_VAR_START) { scanner_detect_invalid_let (context_p, literal_p); if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_FOR_LET_START) { literal_p->type |= SCANNER_LITERAL_IS_LET; } else { JERRY_ASSERT (stack_top == SCAN_STACK_CONST || stack_top == SCAN_STACK_FOR_CONST_START); literal_p->type |= SCANNER_LITERAL_IS_CONST; } lexer_next_token (context_p); if (literal_p->type & SCANNER_LITERAL_IS_USED) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; } else if (context_p->token.type == LEXER_ASSIGN) { scanner_binding_literal_t binding_literal; binding_literal.literal_p = literal_p; parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); } } else { if (!(literal_p->type & SCANNER_LITERAL_IS_VAR)) { scanner_detect_invalid_var (context_p, &scanner_context, literal_p); literal_p->type |= SCANNER_LITERAL_IS_VAR; if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) { literal_p->type |= SCANNER_LITERAL_NO_REG; } } lexer_next_token (context_p); } #if JERRY_MODULE_SYSTEM if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_EXPORT) { literal_p->type |= SCANNER_LITERAL_NO_REG; } #endif /* JERRY_MODULE_SYSTEM */ switch (context_p->token.type) { case LEXER_ASSIGN: { scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; /* FALLTHRU */ } case LEXER_COMMA: { lexer_next_token (context_p); continue; } } if (SCANNER_IS_FOR_START (stack_top)) { #if JERRY_MODULE_SYSTEM JERRY_ASSERT (!(scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_EXPORT)); #endif /* JERRY_MODULE_SYSTEM */ if (context_p->token.type != LEXER_SEMICOLON && context_p->token.type != LEXER_KEYW_IN && !SCANNER_IDENTIFIER_IS_OF ()) { scanner_raise_error (context_p); } scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION_END; continue; } JERRY_ASSERT (stack_top == SCAN_STACK_VAR || stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_CONST); #if JERRY_MODULE_SYSTEM scanner_context.active_literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_EXPORT; #endif /* JERRY_MODULE_SYSTEM */ scanner_context.mode = SCAN_MODE_STATEMENT_END; parser_stack_pop_uint8 (context_p); continue; } case SCAN_MODE_FUNCTION_ARGUMENTS: { JERRY_ASSERT (stack_top == SCAN_STACK_SCRIPT_FUNCTION || stack_top == SCAN_STACK_FUNCTION_STATEMENT || stack_top == SCAN_STACK_FUNCTION_EXPRESSION || stack_top == SCAN_STACK_FUNCTION_PROPERTY); scanner_literal_pool_t *literal_pool_p = scanner_context.active_literal_pool_p; JERRY_ASSERT (literal_pool_p != NULL && (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION)); literal_pool_p->source_p = context_p->source_p; if (JERRY_UNLIKELY (scanner_context.async_source_p != NULL)) { literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ASYNC; literal_pool_p->source_p = scanner_context.async_source_p; scanner_context.async_source_p = NULL; } if (type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); } lexer_next_token (context_p); /* FALLTHRU */ } case SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS: { if (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_EOS) { lexer_lit_location_t *argument_literal_p; do { if (context_p->token.type == LEXER_THREE_DOTS) { scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT; lexer_next_token (context_p); } if (context_p->token.type == LEXER_LEFT_SQUARE || context_p->token.type == LEXER_LEFT_BRACE) { argument_literal_p = NULL; break; } if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { scanner_raise_error (context_p); } argument_literal_p = scanner_append_argument (context_p, &scanner_context); lexer_next_token (context_p); if (context_p->token.type != LEXER_COMMA) { break; } lexer_next_token (context_p); } while (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_EOS); if (argument_literal_p == NULL) { scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT; parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PARAMETERS); scanner_append_hole (context_p, &scanner_context); scanner_push_destructuring_pattern (context_p, &scanner_context, SCANNER_BINDING_ARG, false); if (context_p->token.type == LEXER_LEFT_SQUARE) { parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); scanner_context.mode = SCAN_MODE_BINDING; break; } parser_stack_push_uint8 (context_p, 0); parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); scanner_context.mode = SCAN_MODE_PROPERTY_NAME; continue; } if (context_p->token.type == LEXER_ASSIGN) { scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT; parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PARAMETERS); scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; if (argument_literal_p->type & SCANNER_LITERAL_IS_USED) { JERRY_ASSERT (argument_literal_p->type & SCANNER_LITERAL_EARLY_CREATE); break; } scanner_binding_literal_t binding_literal; binding_literal.literal_p = argument_literal_p; parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); break; } } if (context_p->token.type == LEXER_EOS && stack_top == SCAN_STACK_SCRIPT_FUNCTION) { /* End of argument parsing. */ scanner_info_t *scanner_info_p = (scanner_info_t *) scanner_malloc (context_p, sizeof (scanner_info_t)); scanner_info_p->next_p = context_p->next_scanner_info_p; scanner_info_p->source_p = NULL; scanner_info_p->type = SCANNER_TYPE_END_ARGUMENTS; scanner_context.end_arguments_p = scanner_info_p; context_p->next_scanner_info_p = scanner_info_p; context_p->source_p = context_p->source_start_p; context_p->source_end_p = context_p->source_start_p + context_p->source_size; lexer_init_line_info (context_p); scanner_filter_arguments (context_p, &scanner_context); lexer_next_token (context_p); scanner_check_directives (context_p, &scanner_context); continue; } if (context_p->token.type != LEXER_RIGHT_PAREN) { scanner_raise_error (context_p); } lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_BRACE) { scanner_raise_error (context_p); } scanner_filter_arguments (context_p, &scanner_context); lexer_next_token (context_p); scanner_check_directives (context_p, &scanner_context); continue; } case SCAN_MODE_PROPERTY_NAME: { JERRY_ASSERT (stack_top == SCAN_STACK_OBJECT_LITERAL); if (lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS)) { lexer_check_property_modifier (context_p); } if (context_p->token.type == LEXER_LEFT_SQUARE) { parser_stack_push_uint8 (context_p, SCAN_STACK_COMPUTED_PROPERTY); scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } if (context_p->token.type == LEXER_THREE_DOTS) { *parser_stack_get_prev_uint8 (context_p) |= SCANNER_LITERAL_OBJECT_HAS_REST; scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; if (scanner_context.binding_type != SCANNER_BINDING_NONE) { scanner_context.mode = SCAN_MODE_BINDING; } break; } if (context_p->token.type == LEXER_RIGHT_BRACE) { scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION_END; continue; } if (context_p->token.type == LEXER_PROPERTY_GETTER || context_p->token.type == LEXER_KEYW_ASYNC || context_p->token.type == LEXER_MULTIPLY || context_p->token.type == LEXER_PROPERTY_SETTER) { uint16_t literal_pool_flags = SCANNER_LITERAL_POOL_FUNCTION; if (context_p->token.type == LEXER_MULTIPLY) { literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR; } else if (context_p->token.type == LEXER_KEYW_ASYNC) { literal_pool_flags |= SCANNER_LITERAL_POOL_ASYNC; if (lexer_consume_generator (context_p)) { literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR; } } parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); if (context_p->token.type == LEXER_LEFT_SQUARE) { parser_stack_push_uint8 (context_p, SCANNER_FROM_LITERAL_POOL_TO_COMPUTED (literal_pool_flags)); scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } if (context_p->token.type != LEXER_LITERAL) { scanner_raise_error (context_p); } scanner_push_literal_pool (context_p, &scanner_context, literal_pool_flags); scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; break; } if (context_p->token.type != LEXER_LITERAL) { scanner_raise_error (context_p); } parser_line_counter_t start_line = context_p->token.line; parser_line_counter_t start_column = context_p->token.column; bool is_ident = (context_p->token.lit_location.type == LEXER_IDENT_LITERAL); lexer_next_token (context_p); if (context_p->token.type == LEXER_LEFT_PAREN) { scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; continue; } if (is_ident && (context_p->token.type == LEXER_COMMA || context_p->token.type == LEXER_RIGHT_BRACE || context_p->token.type == LEXER_ASSIGN)) { context_p->source_p = context_p->token.lit_location.char_p; context_p->line = start_line; context_p->column = start_column; lexer_next_token (context_p); JERRY_ASSERT (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type == LEXER_IDENT_LITERAL); if (context_p->token.type != LEXER_LITERAL) { scanner_raise_error (context_p); } if (scanner_context.binding_type != SCANNER_BINDING_NONE) { scanner_context.mode = SCAN_MODE_BINDING; continue; } scanner_add_reference (context_p, &scanner_context); lexer_next_token (context_p); if (context_p->token.type == LEXER_ASSIGN) { scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION_END; continue; } if (context_p->token.type != LEXER_COLON) { scanner_raise_error (context_p); } scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; if (scanner_context.binding_type != SCANNER_BINDING_NONE) { scanner_context.mode = SCAN_MODE_BINDING; } break; } case SCAN_MODE_BINDING: { JERRY_ASSERT (scanner_context.binding_type == SCANNER_BINDING_VAR || scanner_context.binding_type == SCANNER_BINDING_LET || scanner_context.binding_type == SCANNER_BINDING_CATCH || scanner_context.binding_type == SCANNER_BINDING_CONST || scanner_context.binding_type == SCANNER_BINDING_ARG || scanner_context.binding_type == SCANNER_BINDING_ARROW_ARG); if (type == LEXER_THREE_DOTS) { lexer_next_token (context_p); type = (lexer_token_type_t) context_p->token.type; } if (type == LEXER_LEFT_SQUARE || type == LEXER_LEFT_BRACE) { scanner_push_destructuring_pattern (context_p, &scanner_context, scanner_context.binding_type, true); if (type == LEXER_LEFT_SQUARE) { parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); break; } parser_stack_push_uint8 (context_p, 0); parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); scanner_context.mode = SCAN_MODE_PROPERTY_NAME; continue; } if (type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; continue; } lexer_lit_location_t *literal_p = scanner_add_literal (context_p, &scanner_context); scanner_context.mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; if (scanner_context.binding_type == SCANNER_BINDING_VAR) { if (!(literal_p->type & SCANNER_LITERAL_IS_VAR)) { scanner_detect_invalid_var (context_p, &scanner_context, literal_p); literal_p->type |= SCANNER_LITERAL_IS_VAR; if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) { literal_p->type |= SCANNER_LITERAL_NO_REG; } } break; } if (scanner_context.binding_type == SCANNER_BINDING_ARROW_ARG) { literal_p->type |= SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG; if (literal_p->type & SCANNER_LITERAL_IS_USED) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; break; } } else { scanner_detect_invalid_let (context_p, literal_p); if (scanner_context.binding_type <= SCANNER_BINDING_CATCH) { JERRY_ASSERT ((scanner_context.binding_type == SCANNER_BINDING_LET) || (scanner_context.binding_type == SCANNER_BINDING_CATCH)); literal_p->type |= SCANNER_LITERAL_IS_LET; } else { literal_p->type |= SCANNER_LITERAL_IS_CONST; if (scanner_context.binding_type == SCANNER_BINDING_ARG) { literal_p->type |= SCANNER_LITERAL_IS_ARG; if (literal_p->type & SCANNER_LITERAL_IS_USED) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; break; } } } if (literal_p->type & SCANNER_LITERAL_IS_USED) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; break; } } scanner_binding_item_t *binding_item_p; binding_item_p = (scanner_binding_item_t *) scanner_malloc (context_p, sizeof (scanner_binding_item_t)); binding_item_p->next_p = scanner_context.active_binding_list_p->items_p; binding_item_p->literal_p = literal_p; scanner_context.active_binding_list_p->items_p = binding_item_p; lexer_next_token (context_p); if (context_p->token.type != LEXER_ASSIGN) { continue; } scanner_binding_literal_t binding_literal; binding_literal.literal_p = literal_p; parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } } lexer_next_token (context_p); } scan_completed: if (context_p->stack_top_uint8 != SCAN_STACK_SCRIPT && context_p->stack_top_uint8 != SCAN_STACK_SCRIPT_FUNCTION) { scanner_raise_error (context_p); } scanner_pop_literal_pool (context_p, &scanner_context); JERRY_ASSERT (scanner_context.active_binding_list_p == NULL); JERRY_ASSERT (scanner_context.active_literal_pool_p == NULL); #ifndef JERRY_NDEBUG scanner_context.context_status_flags |= PARSER_SCANNING_SUCCESSFUL; #endif /* !JERRY_NDEBUG */ } PARSER_CATCH { while (scanner_context.active_binding_list_p != NULL) { scanner_pop_binding_list (&scanner_context); } if (JERRY_UNLIKELY (context_p->error != PARSER_ERR_OUT_OF_MEMORY)) { /* Ignore the errors thrown by the lexer. */ context_p->error = PARSER_ERR_NO_ERROR; /* The following code may allocate memory, so it is enclosed in a try/catch. */ PARSER_TRY (context_p->try_buffer) { if (scanner_context.status_flags & SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION) { JERRY_ASSERT (scanner_context.async_source_p != NULL); scanner_info_t *info_p; info_p = scanner_insert_info (context_p, scanner_context.async_source_p, sizeof (scanner_info_t)); info_p->type = SCANNER_TYPE_ERR_ASYNC_FUNCTION; } while (scanner_context.active_literal_pool_p != NULL) { scanner_pop_literal_pool (context_p, &scanner_context); } } PARSER_CATCH { JERRY_ASSERT (context_p->error == PARSER_ERR_OUT_OF_MEMORY); } PARSER_TRY_END } JERRY_ASSERT (context_p->error == PARSER_ERR_NO_ERROR || context_p->error == PARSER_ERR_OUT_OF_MEMORY); if (context_p->error == PARSER_ERR_OUT_OF_MEMORY) { while (scanner_context.active_literal_pool_p != NULL) { scanner_literal_pool_t *literal_pool_p = scanner_context.active_literal_pool_p; scanner_context.active_literal_pool_p = literal_pool_p->prev_p; parser_list_free (&literal_pool_p->literal_pool); scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t)); } parser_stack_free (context_p); return; } } PARSER_TRY_END context_p->status_flags = scanner_context.context_status_flags; context_p->global_status_flags &= (uint32_t) ~ECMA_PARSE_INTERNAL_PRE_SCANNING; scanner_reverse_info_list (context_p); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { scanner_info_t *info_p = context_p->next_scanner_info_p; const uint8_t *source_start_p = (context_p->arguments_start_p == NULL ? context_p->source_start_p : context_p->arguments_start_p); while (info_p->type != SCANNER_TYPE_END) { const char *name_p = NULL; bool print_location = false; switch (info_p->type) { case SCANNER_TYPE_END_ARGUMENTS: { JERRY_DEBUG_MSG (" END_ARGUMENTS\n"); source_start_p = context_p->source_start_p; break; } case SCANNER_TYPE_FUNCTION: case SCANNER_TYPE_BLOCK: { const uint8_t *prev_source_p = info_p->source_p - 1; const uint8_t *data_p; if (info_p->type == SCANNER_TYPE_FUNCTION) { data_p = (const uint8_t *) (info_p + 1); JERRY_DEBUG_MSG (" FUNCTION: flags: 0x%x declarations: %d", (int) info_p->u8_arg, (int) info_p->u16_arg); } else { data_p = (const uint8_t *) (info_p + 1); JERRY_DEBUG_MSG (" BLOCK:"); } JERRY_DEBUG_MSG (" source:%d\n", (int) (info_p->source_p - source_start_p)); while (data_p[0] != SCANNER_STREAM_TYPE_END) { switch (data_p[0] & SCANNER_STREAM_TYPE_MASK) { case SCANNER_STREAM_TYPE_HOLE: { JERRY_DEBUG_MSG (" HOLE\n"); data_p++; continue; } case SCANNER_STREAM_TYPE_ARGUMENTS: { JERRY_DEBUG_MSG (" ARGUMENTS%s%s\n", (data_p[0] & SCANNER_STREAM_NO_REG) ? " *" : "", (data_p[0] & SCANNER_STREAM_LOCAL_ARGUMENTS) ? " L" : ""); data_p++; continue; } case SCANNER_STREAM_TYPE_ARGUMENTS_FUNC: { JERRY_DEBUG_MSG (" ARGUMENTS_FUNC%s%s\n", (data_p[0] & SCANNER_STREAM_NO_REG) ? " *" : "", (data_p[0] & SCANNER_STREAM_LOCAL_ARGUMENTS) ? " L" : ""); data_p++; continue; } case SCANNER_STREAM_TYPE_VAR: { JERRY_DEBUG_MSG (" VAR "); break; } case SCANNER_STREAM_TYPE_LET: { JERRY_DEBUG_MSG (" LET "); break; } case SCANNER_STREAM_TYPE_CONST: { JERRY_DEBUG_MSG (" CONST "); break; } case SCANNER_STREAM_TYPE_LOCAL: { JERRY_DEBUG_MSG (" LOCAL "); break; } #if JERRY_MODULE_SYSTEM case SCANNER_STREAM_TYPE_IMPORT: { JERRY_DEBUG_MSG (" IMPORT "); break; } #endif /* JERRY_MODULE_SYSTEM */ case SCANNER_STREAM_TYPE_ARG: { JERRY_DEBUG_MSG (" ARG "); break; } case SCANNER_STREAM_TYPE_ARG_VAR: { JERRY_DEBUG_MSG (" ARG_VAR "); break; } case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: { JERRY_DEBUG_MSG (" DESTRUCTURED_ARG "); break; } case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR: { JERRY_DEBUG_MSG (" DESTRUCTURED_ARG_VAR "); break; } case SCANNER_STREAM_TYPE_ARG_FUNC: { JERRY_DEBUG_MSG (" ARG_FUNC "); break; } case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: { JERRY_DEBUG_MSG (" DESTRUCTURED_ARG_FUNC "); break; } case SCANNER_STREAM_TYPE_FUNC: { JERRY_DEBUG_MSG (" FUNC "); break; } default: { JERRY_UNREACHABLE (); data_p++; continue; } } size_t length; if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF)) { if (data_p[2] != 0) { prev_source_p += data_p[2]; length = 2 + 1; } else { memcpy (&prev_source_p, data_p + 2 + 1, sizeof (uintptr_t)); length = 2 + 1 + sizeof (uintptr_t); } } else { int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8; if (diff <= UINT8_MAX) { diff = -diff; } prev_source_p += diff; length = 2 + 2; } if (data_p[0] & SCANNER_STREAM_EARLY_CREATE) { JERRY_ASSERT (data_p[0] & SCANNER_STREAM_NO_REG); JERRY_DEBUG_MSG ("*"); } if (data_p[0] & SCANNER_STREAM_NO_REG) { JERRY_DEBUG_MSG ("* "); } JERRY_DEBUG_MSG ("'%.*s'\n", data_p[1], (char *) prev_source_p); prev_source_p += data_p[1]; data_p += length; } break; } case SCANNER_TYPE_WHILE: { name_p = "WHILE"; print_location = true; break; } case SCANNER_TYPE_FOR: { scanner_for_info_t *for_info_p = (scanner_for_info_t *) info_p; JERRY_DEBUG_MSG (" FOR: source:%d expression:%d[%d:%d] end:%d[%d:%d]\n", (int) (for_info_p->info.source_p - source_start_p), (int) (for_info_p->expression_location.source_p - source_start_p), (int) for_info_p->expression_location.line, (int) for_info_p->expression_location.column, (int) (for_info_p->end_location.source_p - source_start_p), (int) for_info_p->end_location.line, (int) for_info_p->end_location.column); break; } case SCANNER_TYPE_FOR_IN: { name_p = "FOR-IN"; print_location = true; break; } case SCANNER_TYPE_FOR_OF: { name_p = "FOR-OF"; print_location = true; break; } case SCANNER_TYPE_SWITCH: { JERRY_DEBUG_MSG (" SWITCH: source:%d\n", (int) (info_p->source_p - source_start_p)); scanner_case_info_t *current_case_p = ((scanner_switch_info_t *) info_p)->case_p; while (current_case_p != NULL) { JERRY_DEBUG_MSG (" CASE: location:%d[%d:%d]\n", (int) (current_case_p->location.source_p - source_start_p), (int) current_case_p->location.line, (int) current_case_p->location.column); current_case_p = current_case_p->next_p; } break; } case SCANNER_TYPE_CASE: { name_p = "CASE"; print_location = true; break; } case SCANNER_TYPE_INITIALIZER: { scanner_location_info_t *location_info_p = (scanner_location_info_t *) info_p; JERRY_DEBUG_MSG (" INITIALIZER: flags: 0x%x source:%d location:%d[%d:%d]\n", (int) info_p->u8_arg, (int) (location_info_p->info.source_p - source_start_p), (int) (location_info_p->location.source_p - source_start_p), (int) location_info_p->location.line, (int) location_info_p->location.column); break; } case SCANNER_TYPE_CLASS_CONSTRUCTOR: { JERRY_DEBUG_MSG (" CLASS: source:%d\n", (int) (info_p->source_p - source_start_p)); print_location = false; break; } case SCANNER_TYPE_CLASS_STATIC_BLOCK_END: { name_p = "SCANNER_TYPE_CLASS_STATIC_BLOCK_END"; print_location = true; break; } case SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END: { name_p = "SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END"; print_location = true; break; } case SCANNER_TYPE_LET_EXPRESSION: { JERRY_DEBUG_MSG (" LET_EXPRESSION: source:%d\n", (int) (info_p->source_p - source_start_p)); break; } case SCANNER_TYPE_ERR_REDECLARED: { JERRY_DEBUG_MSG (" JERRY_FATAL_REDECLARED: source:%d\n", (int) (info_p->source_p - source_start_p)); break; } case SCANNER_TYPE_ERR_ASYNC_FUNCTION: { JERRY_DEBUG_MSG (" JERRY_FATAL_ASYNC_FUNCTION: source:%d\n", (int) (info_p->source_p - source_start_p)); break; } case SCANNER_TYPE_LITERAL_FLAGS: { JERRY_DEBUG_MSG (" SCANNER_TYPE_LITERAL_FLAGS: flags: 0x%x source:%d\n", (int) info_p->u8_arg, (int) (info_p->source_p - source_start_p)); print_location = false; break; } case SCANNER_TYPE_EXPORT_MODULE_SPECIFIER: { JERRY_DEBUG_MSG (" EXPORT_WITH_MODULE_SPECIFIER: source:%d\n", (int) (info_p->source_p - source_start_p)); print_location = false; break; } } if (print_location) { scanner_location_info_t *location_info_p = (scanner_location_info_t *) info_p; JERRY_DEBUG_MSG (" %s: source:%d location:%d[%d:%d]\n", name_p, (int) (location_info_p->info.source_p - source_start_p), (int) (location_info_p->location.source_p - source_start_p), (int) location_info_p->location.line, (int) location_info_p->location.column); } info_p = info_p->next_p; } JERRY_DEBUG_MSG ("\n--- Scanning end ---\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ parser_stack_free (context_p); } /* scanner_scan_all */ /** * @} * @} * @} */ #endif /* JERRY_PARSER */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp000664 001750 001750 00000245046 15164251010 051570 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-comparison.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jcontext.h" #include "jrt.h" #include "lit-char-helpers.h" #if JERRY_BUILTIN_ARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_ARRAY_PROTOTYPE_ROUTINE_START = 0, /* Note: these 2 routine ids must be in this order */ ECMA_ARRAY_PROTOTYPE_SORT, ECMA_ARRAY_PROTOTYPE_CONCAT, ECMA_ARRAY_PROTOTYPE_TO_LOCALE_STRING, ECMA_ARRAY_PROTOTYPE_JOIN, ECMA_ARRAY_PROTOTYPE_POP, ECMA_ARRAY_PROTOTYPE_PUSH, ECMA_ARRAY_PROTOTYPE_REVERSE, ECMA_ARRAY_PROTOTYPE_SHIFT, ECMA_ARRAY_PROTOTYPE_SLICE, ECMA_ARRAY_PROTOTYPE_SPLICE, ECMA_ARRAY_PROTOTYPE_UNSHIFT, ECMA_ARRAY_PROTOTYPE_AT, ECMA_ARRAY_PROTOTYPE_INDEX_OF, ECMA_ARRAY_PROTOTYPE_LAST_INDEX_OF, /* Note these 3 routines must be in this order */ ECMA_ARRAY_PROTOTYPE_EVERY, ECMA_ARRAY_PROTOTYPE_SOME, ECMA_ARRAY_PROTOTYPE_FOR_EACH, ECMA_ARRAY_PROTOTYPE_MAP, ECMA_ARRAY_PROTOTYPE_FILTER, /* Note these 2 routines must be in this order */ ECMA_ARRAY_PROTOTYPE_REDUCE, ECMA_ARRAY_PROTOTYPE_REDUCE_RIGHT, ECMA_ARRAY_PROTOTYPE_FIND, ECMA_ARRAY_PROTOTYPE_FIND_INDEX, ECMA_ARRAY_PROTOTYPE_ENTRIES, ECMA_ARRAY_PROTOTYPE_KEYS, ECMA_ARRAY_PROTOTYPE_SYMBOL_ITERATOR, ECMA_ARRAY_PROTOTYPE_FILL, ECMA_ARRAY_PROTOTYPE_COPY_WITHIN, ECMA_ARRAY_PROTOTYPE_INCLUDES, ECMA_ARRAY_PROTOTYPE_FLAT, ECMA_ARRAY_PROTOTYPE_FLATMAP, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup arrayprototype ECMA Array.prototype object built-in * @{ */ /** * Helper function to set an object's length property * * @return ecma value (return value of the [[Put]] method) * Calling ecma_free_value on the returned value is optional if it is not abrupt completion. */ static ecma_value_t ecma_builtin_array_prototype_helper_set_length (ecma_object_t *object, /**< object*/ ecma_number_t length) /**< new length */ { ecma_value_t length_value = ecma_make_number_value (length); ecma_value_t ret_value = ecma_op_object_put (object, ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH), length_value, true); ecma_free_value (length_value); JERRY_ASSERT (ecma_is_value_boolean (ret_value) || ecma_is_value_empty (ret_value) || ECMA_IS_VALUE_ERROR (ret_value)); return ret_value; } /* ecma_builtin_array_prototype_helper_set_length */ /** * The Array.prototype object's 'toLocaleString' routine * * See also: * ECMA-262 v5, 15.4.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_to_locale_string (ecma_object_t *obj_p, /**< object */ ecma_length_t length) /**< object's length */ { /* 5. */ if (length == 0) { return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } /* 7-8. */ ecma_string_t *first_string_p = ecma_builtin_helper_get_to_locale_string_at_index (obj_p, 0); if (JERRY_UNLIKELY (first_string_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (first_string_p); ecma_deref_ecma_string (first_string_p); /* 9-10. */ for (ecma_length_t k = 1; k < length; k++) { /* 4. Implementation-defined: set the separator to a single comma character. */ ecma_stringbuilder_append_byte (&builder, LIT_CHAR_COMMA); ecma_string_t *next_string_p = ecma_builtin_helper_get_to_locale_string_at_index (obj_p, k); if (JERRY_UNLIKELY (next_string_p == NULL)) { ecma_stringbuilder_destroy (&builder); return ECMA_VALUE_ERROR; } ecma_stringbuilder_append (&builder, next_string_p); ecma_deref_ecma_string (next_string_p); } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_array_prototype_object_to_locale_string */ /** * The Array.prototype object's 'concat' routine * * See also: * ECMA-262 v5, 15.4.4.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_concat (const ecma_value_t args[], /**< arguments list */ uint32_t args_number, /**< number of arguments */ ecma_object_t *obj_p) /**< array object */ { /* 2. */ ecma_object_t *new_array_p = ecma_op_array_species_create (obj_p, 0); if (JERRY_UNLIKELY (new_array_p == NULL)) { return ECMA_VALUE_ERROR; } /* 3. */ ecma_length_t new_length = 0; /* 5.b - 5.c for this_arg */ ecma_value_t concat_this_value = ecma_builtin_helper_array_concat_value (new_array_p, &new_length, ecma_make_object_value (obj_p)); if (ECMA_IS_VALUE_ERROR (concat_this_value)) { ecma_deref_object (new_array_p); return concat_this_value; } JERRY_ASSERT (ecma_is_value_empty (concat_this_value)); /* 5. */ for (uint32_t arg_index = 0; arg_index < args_number; arg_index++) { ecma_value_t concat_value = ecma_builtin_helper_array_concat_value (new_array_p, &new_length, args[arg_index]); if (ECMA_IS_VALUE_ERROR (concat_value)) { ecma_deref_object (new_array_p); return concat_value; } JERRY_ASSERT (ecma_is_value_empty (concat_value)); } ecma_value_t set_length_value = ecma_builtin_array_prototype_helper_set_length (new_array_p, ((ecma_number_t) new_length)); if (ECMA_IS_VALUE_ERROR (set_length_value)) { ecma_deref_object (new_array_p); return set_length_value; } return ecma_make_object_value (new_array_p); } /* ecma_builtin_array_prototype_object_concat */ /** * The Array.prototype.toString's separator creation routine * * See also: * ECMA-262 v5.1, 15.4.4.2 4th step * * @return NULL - if the conversion fails * ecma_string_t * - otherwise */ static ecma_string_t * ecma_op_array_get_separator_string (ecma_value_t separator) /**< possible separator */ { if (ecma_is_value_undefined (separator)) { return ecma_get_magic_string (LIT_MAGIC_STRING_COMMA_CHAR); } return ecma_op_to_string (separator); } /* ecma_op_array_get_separator_string */ /** * The Array.prototype's 'toString' single element operation routine * * See also: * ECMA-262 v5.1, 15.4.4.2 * * @return NULL - if the conversion fails * ecma_string_t * - otherwise */ static ecma_string_t * ecma_op_array_get_to_string_at_index (ecma_object_t *obj_p, /**< this object */ ecma_length_t index) /**< array index */ { ecma_value_t index_value = ecma_op_object_get_by_index (obj_p, index); if (ECMA_IS_VALUE_ERROR (index_value)) { return NULL; } if (ecma_is_value_undefined (index_value) || ecma_is_value_null (index_value)) { return ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } ecma_string_t *ret_str_p = ecma_op_to_string (index_value); ecma_free_value (index_value); return ret_str_p; } /* ecma_op_array_get_to_string_at_index */ /** * The Array.prototype object's 'join' routine * * See also: * ECMA-262 v5, 15.4.4.5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_join (ecma_value_t separator_arg, /**< separator argument */ ecma_object_t *obj_p, /**< object */ ecma_length_t length) /**< object's length */ { /* 4-5. */ ecma_string_t *separator_string_p = ecma_op_array_get_separator_string (separator_arg); if (JERRY_UNLIKELY (separator_string_p == NULL)) { return ECMA_VALUE_ERROR; } if (length == 0) { /* 6. */ ecma_deref_ecma_string (separator_string_p); return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } /* 7-8. */ ecma_string_t *first_string_p = ecma_op_array_get_to_string_at_index (obj_p, 0); if (JERRY_UNLIKELY (first_string_p == NULL)) { ecma_deref_ecma_string (separator_string_p); return ECMA_VALUE_ERROR; } ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (first_string_p); ecma_deref_ecma_string (first_string_p); /* 9-10. */ for (ecma_length_t k = 1; k < length; k++) { /* 10.a */ ecma_stringbuilder_append (&builder, separator_string_p); /* 10.d */ ecma_string_t *next_string_p = ecma_op_array_get_to_string_at_index (obj_p, k); if (JERRY_UNLIKELY (next_string_p == NULL)) { ecma_deref_ecma_string (separator_string_p); ecma_stringbuilder_destroy (&builder); return ECMA_VALUE_ERROR; } ecma_stringbuilder_append (&builder, next_string_p); ecma_deref_ecma_string (next_string_p); } ecma_deref_ecma_string (separator_string_p); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_array_prototype_join */ /** * The Array.prototype object's 'pop' routine * * See also: * ECMA-262 v5, 15.4.4.6 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_pop (ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 4. */ if (len == 0) { /* 4.a */ ecma_value_t set_length_value = ecma_builtin_array_prototype_helper_set_length (obj_p, ECMA_NUMBER_ZERO); /* 4.b */ return ECMA_IS_VALUE_ERROR (set_length_value) ? set_length_value : ECMA_VALUE_UNDEFINED; } /* 5.b */ len--; ecma_value_t get_value = ecma_op_object_get_by_index (obj_p, len); if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } if (ecma_op_object_is_fast_array (obj_p)) { ecma_delete_fast_array_properties (obj_p, (uint32_t) len); return get_value; } /* 5.c */ ecma_value_t del_value = ecma_op_object_delete_by_index (obj_p, len, true); if (ECMA_IS_VALUE_ERROR (del_value)) { ecma_free_value (get_value); return del_value; } ecma_free_value (del_value); /* 5.d */ ecma_value_t set_length_value = ecma_builtin_array_prototype_helper_set_length (obj_p, ((ecma_number_t) len)); if (ECMA_IS_VALUE_ERROR (set_length_value)) { ecma_free_value (get_value); return set_length_value; } return get_value; } /* ecma_builtin_array_prototype_object_pop */ /** * The Array.prototype object's 'push' routine * * See also: * ECMA-262 v5, 15.4.4.7 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_push (const ecma_value_t *argument_list_p, /**< arguments list */ uint32_t arguments_number, /**< number of arguments */ ecma_object_t *obj_p, /**< object */ ecma_length_t length) /**< object's length */ { if (ecma_op_object_is_fast_array (obj_p)) { if ((ecma_number_t) (length + arguments_number) > UINT32_MAX) { return ecma_raise_range_error (ECMA_ERR_INVALID_ARRAY_LENGTH); } if (arguments_number == 0) { return ecma_make_uint32_value ((uint32_t) length); } uint32_t new_length = ((uint32_t) length) + arguments_number; ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; ecma_value_t *buffer_p = ecma_fast_array_extend (obj_p, new_length) + length; for (uint32_t index = 0; index < arguments_number; index++) { buffer_p[index] = ecma_copy_value_if_not_object (argument_list_p[index]); } ext_obj_p->u.array.length_prop_and_hole_count -= ECMA_FAST_ARRAY_HOLE_ONE * arguments_number; return ecma_make_uint32_value (new_length); } /* 5. */ if ((ecma_number_t) (length + arguments_number) > ECMA_NUMBER_MAX_SAFE_INTEGER) { return ecma_raise_type_error (ECMA_ERR_PUSHING_TOO_HIGH_ELEMENT); } /* 6. */ for (ecma_length_t index = 0; index < arguments_number; index++, length++) { /* 6.b */ ecma_value_t put_value = ecma_op_object_put_by_index (obj_p, length, argument_list_p[index], true); if (ECMA_IS_VALUE_ERROR (put_value)) { return put_value; } } /* 6 - 7. */ ecma_value_t set_length_value = ecma_builtin_array_prototype_helper_set_length (obj_p, (ecma_number_t) length); if (ECMA_IS_VALUE_ERROR (set_length_value)) { return set_length_value; } return ecma_make_length_value (length); } /* ecma_builtin_array_prototype_object_push */ /** * The Array.prototype object's 'reverse' routine * * See also: * ECMA-262 v5, 15.4.4.8 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_reverse (ecma_value_t this_arg, /**< this argument */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { if (ecma_op_object_is_fast_array (obj_p)) { uint32_t middle = (uint32_t) len / 2; ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE && len != 0) { ecma_value_t *buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); for (uint32_t i = 0; i < middle; i++) { ecma_value_t tmp = buffer_p[i]; buffer_p[i] = buffer_p[len - 1 - i]; buffer_p[len - 1 - i] = tmp; } return ecma_copy_value (this_arg); } } ecma_length_t middle = len / 2; for (ecma_length_t lower = 0; lower < middle; lower++) { ecma_length_t upper = len - lower - 1; ecma_value_t ret_value = ECMA_VALUE_ERROR; ecma_string_t *lower_str_p = ecma_new_ecma_string_from_length (lower); ecma_string_t *upper_str_p = ecma_new_ecma_string_from_length (upper); ecma_value_t lower_value = ECMA_VALUE_EMPTY; ecma_value_t upper_value = ECMA_VALUE_EMPTY; ecma_value_t has_lower = ecma_op_object_has_property (obj_p, lower_str_p); ecma_value_t has_upper; bool upper_exist; bool lower_exist; #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (has_lower)) { goto clean_up; } #endif /* JERRY_BUILTIN_PROXY */ lower_exist = ecma_is_value_true (has_lower); if (lower_exist) { lower_value = ecma_op_object_get (obj_p, lower_str_p); if (ECMA_IS_VALUE_ERROR (lower_value)) { goto clean_up; } } has_upper = ecma_op_object_has_property (obj_p, upper_str_p); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (has_upper)) { goto clean_up; } #endif /* JERRY_BUILTIN_PROXY */ upper_exist = ecma_is_value_true (has_upper); if (upper_exist) { upper_value = ecma_op_object_get (obj_p, upper_str_p); if (ECMA_IS_VALUE_ERROR (upper_value)) { goto clean_up; } } if (lower_exist && upper_exist) { ecma_value_t outer_put_value = ecma_op_object_put (obj_p, lower_str_p, upper_value, true); if (ECMA_IS_VALUE_ERROR (outer_put_value)) { goto clean_up; } ecma_value_t inner_put_value = ecma_op_object_put (obj_p, upper_str_p, lower_value, true); if (ECMA_IS_VALUE_ERROR (inner_put_value)) { goto clean_up; } } else if (!lower_exist && upper_exist) { ecma_value_t put_value = ecma_op_object_put (obj_p, lower_str_p, upper_value, true); if (ECMA_IS_VALUE_ERROR (put_value)) { goto clean_up; } ecma_value_t del_value = ecma_op_object_delete (obj_p, upper_str_p, true); if (ECMA_IS_VALUE_ERROR (del_value)) { goto clean_up; } } else if (lower_exist) { ecma_value_t del_value = ecma_op_object_delete (obj_p, lower_str_p, true); if (ECMA_IS_VALUE_ERROR (del_value)) { goto clean_up; } ecma_value_t put_value = ecma_op_object_put (obj_p, upper_str_p, lower_value, true); if (ECMA_IS_VALUE_ERROR (put_value)) { goto clean_up; } } ret_value = ECMA_VALUE_EMPTY; clean_up: ecma_free_value (upper_value); ecma_free_value (lower_value); ecma_deref_ecma_string (lower_str_p); ecma_deref_ecma_string (upper_str_p); if (ECMA_IS_VALUE_ERROR (ret_value)) { return ret_value; } } return ecma_copy_value (this_arg); } /* ecma_builtin_array_prototype_object_reverse */ /** * The Array.prototype object's 'shift' routine * * See also: * ECMA-262 v5, 15.4.4.9 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_shift (ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 4. */ if (len == 0) { ecma_value_t set_length_value = ecma_builtin_array_prototype_helper_set_length (obj_p, ECMA_NUMBER_ZERO); return ECMA_IS_VALUE_ERROR (set_length_value) ? set_length_value : ECMA_VALUE_UNDEFINED; } if (ecma_op_object_is_fast_array (obj_p)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE && len != 0) { ecma_value_t *buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); ecma_value_t ret_value = buffer_p[0]; if (ecma_is_value_object (ret_value)) { ecma_ref_object (ecma_get_object_from_value (ret_value)); } memmove (buffer_p, buffer_p + 1, (size_t) (sizeof (ecma_value_t) * (len - 1))); buffer_p[len - 1] = ECMA_VALUE_UNDEFINED; ecma_delete_fast_array_properties (obj_p, (uint32_t) (len - 1)); return ret_value; } } /* 5. */ ecma_value_t first_value = ecma_op_object_get_by_index (obj_p, 0); if (ECMA_IS_VALUE_ERROR (first_value)) { return first_value; } /* 6. and 7. */ for (ecma_length_t k = 1; k < len; k++) { /* 7.a - 7.c */ ecma_value_t curr_value = ecma_op_object_find_by_index (obj_p, k); if (ECMA_IS_VALUE_ERROR (curr_value)) { ecma_free_value (first_value); return curr_value; } /* 7.b */ ecma_length_t to = k - 1; ecma_value_t operation_value; if (ecma_is_value_found (curr_value)) { /* 7.d.i, 7.d.ii */ operation_value = ecma_op_object_put_by_index (obj_p, to, curr_value, true); ecma_free_value (curr_value); } else { /* 7.e.i */ operation_value = ecma_op_object_delete_by_index (obj_p, to, true); } if (ECMA_IS_VALUE_ERROR (operation_value)) { ecma_free_value (first_value); return operation_value; } } /* 8. */ ecma_value_t del_value = ecma_op_object_delete_by_index (obj_p, --len, true); if (ECMA_IS_VALUE_ERROR (del_value)) { ecma_free_value (first_value); return del_value; } /* 9. */ ecma_value_t set_length_value = ecma_builtin_array_prototype_helper_set_length (obj_p, ((ecma_number_t) len)); if (ECMA_IS_VALUE_ERROR (set_length_value)) { ecma_free_value (first_value); return set_length_value; } /* 10. */ return first_value; } /* ecma_builtin_array_prototype_object_shift */ /** * The Array.prototype object's 'slice' routine * * See also: * ECMA-262 v5, 15.4.4.10 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_slice (ecma_value_t arg1, /**< start */ ecma_value_t arg2, /**< end */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { ecma_length_t start = 0, end = len; /* 5. 6.*/ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (arg1, len, &start))) { return ECMA_VALUE_ERROR; } /* 7. */ if (ecma_is_value_undefined (arg2)) { end = len; } else { /* 7. part 2, 8.*/ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (arg2, len, &end))) { return ECMA_VALUE_ERROR; } } JERRY_ASSERT (start <= len && end <= len); bool use_fast_path = ecma_op_object_is_fast_array (obj_p); ecma_length_t copied_length = (end > start) ? end - start : 0; ecma_object_t *new_array_p = ecma_op_array_species_create (obj_p, copied_length); if (JERRY_UNLIKELY (new_array_p == NULL)) { return ECMA_VALUE_ERROR; } use_fast_path &= ecma_op_object_is_fast_array (new_array_p); if (use_fast_path && copied_length > 0) { ecma_extended_object_t *ext_from_obj_p = (ecma_extended_object_t *) obj_p; if (ext_from_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE) { if (JERRY_UNLIKELY (obj_p->u1.property_list_cp == JMEM_CP_NULL)) { /** * Very unlikely case: the buffer copied from is a fast buffer and the property list was deleted. * There is no need to do any copy. */ return ecma_make_object_value (new_array_p); } /* Source array's length could be changed during the start/end normalization. * If the "end" value is greater than the current length, clamp the value to avoid buffer-overflow. */ if (ext_from_obj_p->u.array.length < end) { end = ext_from_obj_p->u.array.length; } ecma_extended_object_t *ext_to_obj_p = (ecma_extended_object_t *) new_array_p; uint32_t target_length = ext_to_obj_p->u.array.length; ecma_value_t *to_buffer_p; JERRY_ASSERT (copied_length <= UINT32_MAX); if (copied_length == target_length) { to_buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, new_array_p->u1.property_list_cp); } else if (copied_length > target_length) { to_buffer_p = ecma_fast_array_extend (new_array_p, (uint32_t) copied_length); } else { ecma_delete_fast_array_properties (new_array_p, (uint32_t) copied_length); to_buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, new_array_p->u1.property_list_cp); } ecma_value_t *from_buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); /* 9. */ uint32_t n = 0; for (uint32_t k = (uint32_t) start; k < (uint32_t) end; k++, n++) { ecma_free_value_if_not_object (to_buffer_p[n]); to_buffer_p[n] = ecma_copy_value_if_not_object (from_buffer_p[k]); } ext_to_obj_p->u.array.length_prop_and_hole_count &= ECMA_FAST_ARRAY_HOLE_ONE - 1; return ecma_make_object_value (new_array_p); } } /* 9. */ ecma_length_t n = 0; /* 10. */ for (ecma_length_t k = start; k < end; k++, n++) { /* 10.c */ ecma_value_t get_value = ecma_op_object_find_by_index (obj_p, k); if (ECMA_IS_VALUE_ERROR (get_value)) { ecma_deref_object (new_array_p); return get_value; } if (ecma_is_value_found (get_value)) { /* 10.c.ii */ ecma_value_t put_comp; put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p, n, get_value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW); ecma_free_value (get_value); if (ECMA_IS_VALUE_ERROR (put_comp)) { ecma_deref_object (new_array_p); return put_comp; } } } ecma_value_t set_length_value = ecma_builtin_array_prototype_helper_set_length (new_array_p, ((ecma_number_t) n)); if (ECMA_IS_VALUE_ERROR (set_length_value)) { ecma_deref_object (new_array_p); return set_length_value; } return ecma_make_object_value (new_array_p); } /* ecma_builtin_array_prototype_object_slice */ /** * SortCompare abstract method * * See also: * ECMA-262 v5, 15.4.4.11 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_sort_compare_helper (ecma_value_t lhs, /**< left value */ ecma_value_t rhs, /**< right value */ ecma_value_t compare_func, /**< compare function */ ecma_object_t *array_buffer_p) /**< arrayBuffer */ { JERRY_UNUSED (array_buffer_p); /* * ECMA-262 v5, 15.4.4.11 NOTE1: Because nonexistent property values always * compare greater than undefined property values, and undefined always * compares greater than any other value, undefined property values always * sort to the end of the result, followed by nonexistent property values. */ bool lhs_is_undef = ecma_is_value_undefined (lhs); bool rhs_is_undef = ecma_is_value_undefined (rhs); if (lhs_is_undef) { return ecma_make_integer_value (rhs_is_undef ? 0 : 1); } if (rhs_is_undef) { return ecma_make_integer_value (-1); } ecma_number_t result = ECMA_NUMBER_ZERO; if (ecma_is_value_undefined (compare_func)) { /* Default comparison when no compare_func is passed. */ ecma_string_t *lhs_str_p = ecma_op_to_string (lhs); if (JERRY_UNLIKELY (lhs_str_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_string_t *rhs_str_p = ecma_op_to_string (rhs); if (JERRY_UNLIKELY (rhs_str_p == NULL)) { ecma_deref_ecma_string (lhs_str_p); return ECMA_VALUE_ERROR; } if (ecma_compare_ecma_strings_relational (lhs_str_p, rhs_str_p)) { result = ECMA_NUMBER_MINUS_ONE; } else if (!ecma_compare_ecma_strings (lhs_str_p, rhs_str_p)) { result = ECMA_NUMBER_ONE; } else { result = ECMA_NUMBER_ZERO; } ecma_deref_ecma_string (rhs_str_p); ecma_deref_ecma_string (lhs_str_p); } else { /* * compare_func, if not undefined, will always contain a callable function object. * We checked this previously, before this function was called. */ JERRY_ASSERT (ecma_op_is_callable (compare_func)); ecma_object_t *comparefn_obj_p = ecma_get_object_from_value (compare_func); ecma_value_t compare_args[] = { lhs, rhs }; ecma_value_t call_value = ecma_op_function_call (comparefn_obj_p, ECMA_VALUE_UNDEFINED, compare_args, 2); if (ECMA_IS_VALUE_ERROR (call_value)) { return call_value; } if (!ecma_is_value_number (call_value)) { ecma_number_t ret_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (call_value, &ret_num))) { ecma_free_value (call_value); return ECMA_VALUE_ERROR; } result = ret_num; } else { result = ecma_get_number_from_value (call_value); } ecma_free_value (call_value); } return ecma_make_number_value (result); } /* ecma_builtin_array_prototype_object_sort_compare_helper */ /** * The Array.prototype object's 'sort' routine * * See also: * ECMA-262 v5, 15.4.4.11 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_sort (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg1, /**< comparefn */ ecma_object_t *obj_p) /**< object */ { /* Check if the provided compare function is callable. */ if (!ecma_is_value_undefined (arg1) && !ecma_op_is_callable (arg1)) { return ecma_raise_type_error (ECMA_ERR_COMPARE_FUNC_NOT_CALLABLE); } ecma_length_t len; ecma_value_t len_value = ecma_op_object_get_length (obj_p, &len); if (ECMA_IS_VALUE_ERROR (len_value)) { return len_value; } ecma_collection_t *array_index_props_p = ecma_new_collection (); for (uint32_t i = 0; i < len; i++) { ecma_string_t *prop_name_p = ecma_new_ecma_string_from_uint32 (i); ecma_property_descriptor_t prop_desc; ecma_value_t get_desc = ecma_op_object_get_own_property_descriptor (obj_p, prop_name_p, &prop_desc); if (ECMA_IS_VALUE_ERROR (get_desc)) { ecma_collection_free (array_index_props_p); ecma_deref_ecma_string (prop_name_p); return get_desc; } if (ecma_is_value_true (get_desc)) { ecma_ref_ecma_string (prop_name_p); ecma_collection_push_back (array_index_props_p, ecma_make_string_value (prop_name_p)); ecma_free_property_descriptor (&prop_desc); continue; } } uint32_t defined_prop_count = array_index_props_p->item_count; ecma_value_t ret_value = ECMA_VALUE_ERROR; uint32_t copied_num = 0; JMEM_DEFINE_LOCAL_ARRAY (values_buffer, defined_prop_count, ecma_value_t); ecma_value_t *buffer_p = array_index_props_p->buffer_p; /* Copy unsorted array into a native c array. */ for (uint32_t i = 0; i < array_index_props_p->item_count; i++) { ecma_string_t *property_name_p = ecma_get_string_from_value (buffer_p[i]); uint32_t index = ecma_string_get_array_index (property_name_p); JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); if (index >= len) { break; } ecma_value_t index_value = ecma_op_object_get (obj_p, property_name_p); if (ECMA_IS_VALUE_ERROR (index_value)) { goto clean_up; } values_buffer[copied_num++] = index_value; } JERRY_ASSERT (copied_num == defined_prop_count); /* Sorting. */ if (copied_num > 1) { const ecma_builtin_helper_sort_compare_fn_t sort_cb = &ecma_builtin_array_prototype_object_sort_compare_helper; ecma_value_t sort_value = ecma_builtin_helper_array_merge_sort_helper (values_buffer, (uint32_t) (copied_num), arg1, sort_cb, NULL); if (ECMA_IS_VALUE_ERROR (sort_value)) { goto clean_up; } ecma_free_value (sort_value); } /* Put sorted values to the front of the array. */ for (uint32_t index = 0; index < copied_num; index++) { ecma_value_t put_value = ecma_op_object_put_by_index (obj_p, index, values_buffer[index], true); if (ECMA_IS_VALUE_ERROR (put_value)) { goto clean_up; } } ret_value = ECMA_VALUE_EMPTY; clean_up: /* Free values that were copied to the local array. */ for (uint32_t index = 0; index < copied_num; index++) { ecma_free_value (values_buffer[index]); } JMEM_FINALIZE_LOCAL_ARRAY (values_buffer); if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_collection_free (array_index_props_p); return ret_value; } JERRY_ASSERT (ecma_is_value_empty (ret_value)); /* Undefined properties should be in the back of the array. */ ecma_value_t *buffer_p = array_index_props_p->buffer_p; for (uint32_t i = 0; i < array_index_props_p->item_count; i++) { ecma_string_t *property_name_p = ecma_get_string_from_value (buffer_p[i]); uint32_t index = ecma_string_get_array_index (property_name_p); JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); if (index >= copied_num && index < len) { ecma_value_t del_value = ecma_op_object_delete (obj_p, property_name_p, true); if (ECMA_IS_VALUE_ERROR (del_value)) { ecma_collection_free (array_index_props_p); return del_value; } } } ecma_collection_free (array_index_props_p); return ecma_copy_value (this_arg); } /* ecma_builtin_array_prototype_object_sort */ /** * The Array.prototype object's 'splice' routine * * See also: * ECMA-262 v11, 22.1.3.28 * ECMA-262 v5, 15.4.4.12 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_splice (const ecma_value_t args[], /**< arguments list */ uint32_t args_number, /**< number of arguments */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { ecma_length_t actual_start = 0; ecma_length_t actual_delete_count = 0; ecma_length_t insert_count = 0; if (args_number > 0) { /* ES5.1: 6, ES11: 4. */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (args[0], len, &actual_start))) { return ECMA_VALUE_ERROR; } /* ES11: 6. */ if (args_number == 1) { actual_delete_count = len - actual_start; } /* ES11: 7. */ else { insert_count = args_number - 2; ecma_number_t delete_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (args[1], &delete_num))) { return ECMA_VALUE_ERROR; } /* ES5.1: 7 */ actual_delete_count = (ecma_length_t) (JERRY_MIN (JERRY_MAX (delete_num, 0), (ecma_number_t) (len - actual_start))); } } ecma_length_t new_length = len + insert_count - actual_delete_count; /* ES11: 8. */ if ((ecma_number_t) new_length > ECMA_NUMBER_MAX_SAFE_INTEGER) { return ecma_raise_type_error (ECMA_ERR_INVALID_NEW_ARRAY_LENGTH); } /* ES11: 9. */ ecma_object_t *new_array_p = ecma_op_array_species_create (obj_p, actual_delete_count); if (JERRY_UNLIKELY (new_array_p == NULL)) { return ECMA_VALUE_ERROR; } /* ES5.1: 8, ES11: 10. */ ecma_length_t k = 0; /* ES5.1: 9, ES11: 11. */ for (; k < actual_delete_count; k++) { ecma_length_t from = actual_start + k; ecma_value_t from_present = ecma_op_object_find_by_index (obj_p, from); if (ECMA_IS_VALUE_ERROR (from_present)) { ecma_deref_object (new_array_p); return from_present; } if (ecma_is_value_found (from_present)) { ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p, k, from_present, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW); ecma_free_value (from_present); if (ECMA_IS_VALUE_ERROR (put_comp)) { ecma_deref_object (new_array_p); return put_comp; } } } /* ES11: 12. */ ecma_value_t set_length = ecma_builtin_array_prototype_helper_set_length (new_array_p, ((ecma_number_t) actual_delete_count)); if (ECMA_IS_VALUE_ERROR (set_length)) { ecma_deref_object (new_array_p); return set_length; } /* ES5.1: 12, ES11: 15. */ if (insert_count < actual_delete_count) { for (k = actual_start; k < len - actual_delete_count; k++) { ecma_length_t from = k + actual_delete_count; ecma_length_t to = k + insert_count; ecma_value_t from_present = ecma_op_object_find_by_index (obj_p, from); if (ECMA_IS_VALUE_ERROR (from_present)) { ecma_deref_object (new_array_p); return from_present; } ecma_value_t operation_value; if (ecma_is_value_found (from_present)) { operation_value = ecma_op_object_put_by_index (obj_p, to, from_present, true); ecma_free_value (from_present); } else { operation_value = ecma_op_object_delete_by_index (obj_p, to, true); } if (ECMA_IS_VALUE_ERROR (operation_value)) { ecma_deref_object (new_array_p); return operation_value; } } k = len; for (k = len; k > new_length; k--) { ecma_value_t del_value = ecma_op_object_delete_by_index (obj_p, k - 1, true); if (ECMA_IS_VALUE_ERROR (del_value)) { ecma_deref_object (new_array_p); return del_value; } } } /* ES5.1: 13, ES11: 16. */ else if (insert_count > actual_delete_count) { for (k = len - actual_delete_count; k > actual_start; k--) { ecma_length_t from = k + actual_delete_count - 1; ecma_length_t to = k + insert_count - 1; ecma_value_t from_present = ecma_op_object_find_by_index (obj_p, from); if (ECMA_IS_VALUE_ERROR (from_present)) { ecma_deref_object (new_array_p); return from_present; } ecma_value_t operation_value; if (ecma_is_value_found (from_present)) { operation_value = ecma_op_object_put_by_index (obj_p, to, from_present, true); ecma_free_value (from_present); } else { operation_value = ecma_op_object_delete_by_index (obj_p, to, true); } if (ECMA_IS_VALUE_ERROR (operation_value)) { ecma_deref_object (new_array_p); return operation_value; } } } /* ES5.1: 14, ES11: 17. */ k = actual_start; /* ES5.1: 15, ES11: 18. */ uint32_t idx = 0; for (uint32_t arg_index = 2; arg_index < args_number; arg_index++, idx++) { ecma_value_t put_value = ecma_op_object_put_by_index (obj_p, actual_start + idx, args[arg_index], true); if (ECMA_IS_VALUE_ERROR (put_value)) { ecma_deref_object (new_array_p); return put_value; } } /* ES5.1: 16, ES11: 19. */ ecma_value_t set_new_length = ecma_builtin_array_prototype_helper_set_length (obj_p, ((ecma_number_t) new_length)); if (ECMA_IS_VALUE_ERROR (set_new_length)) { ecma_deref_object (new_array_p); return set_new_length; } /* ES5.1: 17, ES11: 20. */ return ecma_make_object_value (new_array_p); } /* ecma_builtin_array_prototype_object_splice */ /** * The Array.prototype object's 'unshift' routine * * See also: * ECMA-262 v5, 15.4.4.13 * ECMA-262 v11, 22.1.3.31 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_unshift (const ecma_value_t args[], /**< arguments list */ uint32_t args_number, /**< number of arguments */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { if (ecma_op_object_is_fast_array (obj_p)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE && len != 0) { if (args_number > UINT32_MAX - len) { return ecma_raise_range_error (ECMA_ERR_INVALID_ARRAY_LENGTH); } if (args_number == 0) { return ecma_make_uint32_value ((uint32_t) len); } uint32_t new_length = ((uint32_t) len) + args_number; ecma_value_t *buffer_p = ecma_fast_array_extend (obj_p, new_length); memmove (buffer_p + args_number, buffer_p, (size_t) (sizeof (ecma_value_t) * len)); uint32_t index = 0; while (index < args_number) { buffer_p[index] = ecma_copy_value_if_not_object (args[index]); index++; } ext_obj_p->u.array.length_prop_and_hole_count -= args_number * ECMA_FAST_ARRAY_HOLE_ONE; return ecma_make_uint32_value (new_length); } } if (args_number > 0) { /* ES11:4.a. */ if ((ecma_number_t) (len + args_number) > ECMA_NUMBER_MAX_SAFE_INTEGER) { return ecma_raise_type_error (ECMA_ERR_UNSHIFT_TOO_HIGH); } /* ES5.1:5.,6. ES11: 4.b, 4.c */ for (ecma_length_t k = len; k > 0; k--) { /* ES5.1:6.a, 6.c, ES11:4.c.i., 4.c.iii. */ ecma_value_t get_value = ecma_op_object_find_by_index (obj_p, k - 1); if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } /* ES5.1:6.b, ES11:4.c.ii. */ ecma_number_t new_idx = ((ecma_number_t) k) + ((ecma_number_t) args_number) - 1; ecma_string_t *index_str_p = ecma_new_ecma_string_from_number (new_idx); ecma_value_t operation_value; if (ecma_is_value_found (get_value)) { /* ES5.1:6.d.i, 6.d.ii, ES11:4.c.iv. */ operation_value = ecma_op_object_put (obj_p, index_str_p, get_value, true); ecma_free_value (get_value); } else { /* ES5.1:6.e.i, ES11:4.c.v. */ operation_value = ecma_op_object_delete (obj_p, index_str_p, true); } ecma_deref_ecma_string (index_str_p); if (ECMA_IS_VALUE_ERROR (operation_value)) { return operation_value; } } for (uint32_t arg_index = 0; arg_index < args_number; arg_index++) { /* ES5.1:9.b, ES11:4.f.ii. */ ecma_value_t put_value = ecma_op_object_put_by_index (obj_p, arg_index, args[arg_index], true); if (ECMA_IS_VALUE_ERROR (put_value)) { return put_value; } } } /* ES5.1:10., ES11:5. */ ecma_number_t new_len = ((ecma_number_t) len) + ((ecma_number_t) args_number); ecma_value_t set_length_value = ecma_builtin_array_prototype_helper_set_length (obj_p, new_len); if (ECMA_IS_VALUE_ERROR (set_length_value)) { return set_length_value; } return ecma_make_number_value (new_len); } /* ecma_builtin_array_prototype_object_unshift */ /** * The Array.prototype object's 'at' routine * * See also: * ECMA-262 Stage 3 Draft Relative Indexing Method proposal * from: https://tc39.es/proposal-relative-indexing-method * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_at (const ecma_value_t index, /**< index argument */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { ecma_length_t res_index; ecma_value_t return_value = ecma_builtin_helper_calculate_index (index, len, &res_index); if (return_value != ECMA_VALUE_EMPTY) { return return_value; } return ecma_op_object_get_by_index (obj_p, res_index); } /* ecma_builtin_array_prototype_object_at */ /** * The Array.prototype object's 'indexOf' routine * * See also: * ECMA-262 v5, 15.4.4.14 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_index_of (const ecma_value_t args[], /**< arguments list */ uint32_t args_number, /**< number of arguments */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 4. */ if (len == 0) { return ecma_make_integer_value (-1); } /* 5. */ ecma_number_t idx = 0; if (args_number > 1) { if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (args[1], &idx))) { return ECMA_VALUE_ERROR; } } /* 6. */ if (idx >= (ecma_number_t) len) { return ecma_make_number_value (-1); } /* 7. */ ecma_length_t from_idx = (ecma_length_t) idx; /* 8. */ if (idx < 0) { from_idx = (ecma_length_t) JERRY_MAX ((ecma_number_t) len + idx, 0); } if (ecma_op_object_is_fast_array (obj_p)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE) { if (JERRY_UNLIKELY (obj_p->u1.property_list_cp == JMEM_CP_NULL)) { return ecma_make_integer_value (-1); } len = JERRY_MIN (ext_obj_p->u.array.length, len); ecma_value_t *buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); while (from_idx < len) { if (ecma_op_strict_equality_compare (args[0], buffer_p[from_idx])) { return ecma_make_uint32_value ((uint32_t) from_idx); } from_idx++; } return ecma_make_integer_value (-1); } } /* 6. */ while (from_idx < len) { /* 9.a */ ecma_value_t get_value = ecma_op_object_find_by_index (obj_p, from_idx); if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } /* 9.b.i, 9.b.ii */ if (ecma_is_value_found (get_value) && ecma_op_strict_equality_compare (args[0], get_value)) { ecma_free_value (get_value); return ecma_make_length_value (from_idx); } from_idx++; ecma_free_value (get_value); } return ecma_make_integer_value (-1); } /* ecma_builtin_array_prototype_object_index_of */ /** * The Array.prototype object's 'lastIndexOf' routine * * See also: * ECMA-262 v5, 15.4.4.15 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_last_index_of (const ecma_value_t args[], /**< arguments list */ uint32_t args_number, /**< number of arguments */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 4. */ if (len == 0) { return ecma_make_integer_value (-1); } /* 5. */ ecma_number_t idx = (ecma_number_t) len - 1; if (args_number > 1) { if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (args[1], &idx))) { return ECMA_VALUE_ERROR; } } ecma_length_t from_idx; /* 6 */ if (idx >= 0) { from_idx = (ecma_length_t) (JERRY_MIN (idx, (ecma_number_t) (len - 1))); } else { ecma_number_t k = (ecma_number_t) len + idx; if (k < 0) { return ecma_make_integer_value (-1); } from_idx = (ecma_length_t) k; } ecma_value_t search_element = (args_number > 0) ? args[0] : ECMA_VALUE_UNDEFINED; if (ecma_op_object_is_fast_array (obj_p)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE) { if (JERRY_UNLIKELY (obj_p->u1.property_list_cp == JMEM_CP_NULL)) { return ecma_make_integer_value (-1); } len = JERRY_MIN (ext_obj_p->u.array.length, len); ecma_value_t *buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); while (from_idx < len) { if (ecma_op_strict_equality_compare (search_element, buffer_p[from_idx])) { return ecma_make_uint32_value ((uint32_t) from_idx); } from_idx--; } return ecma_make_integer_value (-1); } } /* 8. */ while (from_idx < len) { /* 8.a */ ecma_value_t get_value = ecma_op_object_find_by_index (obj_p, from_idx); if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } /* 8.b.i, 8.b.ii */ if (ecma_is_value_found (get_value) && ecma_op_strict_equality_compare (search_element, get_value)) { ecma_free_value (get_value); return ecma_make_length_value (from_idx); } from_idx--; ecma_free_value (get_value); } return ecma_make_integer_value (-1); } /* ecma_builtin_array_prototype_object_last_index_of */ /** * Type of array routine. */ typedef enum { ARRAY_ROUTINE_EVERY, /**< Array.every: ECMA-262 v5, 15.4.4.16 */ ARRAY_ROUTINE_SOME, /**< Array.some: ECMA-262 v5, 15.4.4.17 */ ARRAY_ROUTINE_FOREACH, /**< Array.forEach: ECMA-262 v5, 15.4.4.18 */ ARRAY_ROUTINE__COUNT /**< count of the modes */ } array_routine_mode; /** * Applies the provided function to each element of the array as long as * the return value stays empty. The common function for 'every', 'some' * and 'forEach' of the Array prototype. * * See also: * ECMA-262 v5, 15.4.4.16 * ECMA-262 v5, 15.4.4.17 * ECMA-262 v5, 15.4.4.18 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_apply (ecma_value_t arg1, /**< callbackfn */ ecma_value_t arg2, /**< thisArg */ array_routine_mode mode, /**< array routine mode */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { JERRY_ASSERT (mode < ARRAY_ROUTINE__COUNT); /* 4. */ if (!ecma_op_is_callable (arg1)) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } /* We already checked that arg1 is callable */ ecma_object_t *func_object_p = ecma_get_object_from_value (arg1); ecma_value_t current_index; /* 7. */ for (ecma_length_t index = 0; index < len; index++) { /* 7.a - 7.c */ ecma_value_t get_value = ecma_op_object_find_by_index (obj_p, index); if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } if (ecma_is_value_found (get_value)) { /* 7.c.i */ current_index = ecma_make_length_value (index); ecma_value_t call_args[] = { get_value, current_index, ecma_make_object_value (obj_p) }; /* 7.c.ii */ ecma_value_t call_value = ecma_op_function_call (func_object_p, arg2, call_args, 3); if (ECMA_IS_VALUE_ERROR (call_value)) { ecma_free_value (get_value); return call_value; } bool to_boolean = ecma_op_to_boolean (call_value); ecma_free_value (call_value); ecma_free_value (get_value); /* 7.c.iii */ if (mode == ARRAY_ROUTINE_EVERY && !to_boolean) { return ECMA_VALUE_FALSE; } else if (mode == ARRAY_ROUTINE_SOME && to_boolean) { return ECMA_VALUE_TRUE; } } } /* 8. */ if (mode == ARRAY_ROUTINE_EVERY) { return ECMA_VALUE_TRUE; } else if (mode == ARRAY_ROUTINE_SOME) { return ECMA_VALUE_FALSE; } JERRY_ASSERT (mode == ARRAY_ROUTINE_FOREACH); return ECMA_VALUE_UNDEFINED; } /* ecma_builtin_array_apply */ /** * The Array.prototype object's 'map' routine * * See also: * ECMA-262 v5, 15.4.4.19 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_map (ecma_value_t arg1, /**< callbackfn */ ecma_value_t arg2, /**< thisArg */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 4. */ if (!ecma_op_is_callable (arg1)) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } /* 6. */ ecma_object_t *new_array_p = ecma_op_array_species_create (obj_p, len); if (JERRY_UNLIKELY (new_array_p == NULL)) { return ECMA_VALUE_ERROR; } JERRY_ASSERT (ecma_is_value_object (arg1)); ecma_object_t *func_object_p = ecma_get_object_from_value (arg1); /* 7-8. */ ecma_value_t current_index; for (ecma_length_t index = 0; index < len; index++) { /* 8.a - 8.b */ ecma_value_t current_value = ecma_op_object_find_by_index (obj_p, index); if (ECMA_IS_VALUE_ERROR (current_value)) { ecma_deref_object (new_array_p); return current_value; } if (ecma_is_value_found (current_value)) { /* 8.c.i, 8.c.ii */ current_index = ecma_make_length_value (index); ecma_value_t call_args[] = { current_value, current_index, ecma_make_object_value (obj_p) }; ecma_value_t mapped_value = ecma_op_function_call (func_object_p, arg2, call_args, 3); if (ECMA_IS_VALUE_ERROR (mapped_value)) { ecma_free_value (current_value); ecma_deref_object (new_array_p); return mapped_value; } /* 8.c.iii */ ecma_value_t put_comp; put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p, index, mapped_value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW); ecma_free_value (mapped_value); ecma_free_value (current_value); if (ECMA_IS_VALUE_ERROR (put_comp)) { ecma_deref_object (new_array_p); return put_comp; } } } return ecma_make_object_value (new_array_p); } /* ecma_builtin_array_prototype_object_map */ /** * The Array.prototype object's 'filter' routine * * See also: * ECMA-262 v5, 15.4.4.20 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_filter (ecma_value_t arg1, /**< callbackfn */ ecma_value_t arg2, /**< thisArg */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 4. */ if (!ecma_op_is_callable (arg1)) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } /* 6. */ ecma_object_t *new_array_p = ecma_op_array_species_create (obj_p, 0); if (JERRY_UNLIKELY (new_array_p == NULL)) { return ECMA_VALUE_ERROR; } /* ES11: 22.1.3.7. 7.c.iii.1 */ const uint32_t prop_flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW; /* We already checked that arg1 is callable, so it will always be an object. */ JERRY_ASSERT (ecma_is_value_object (arg1)); ecma_object_t *func_object_p = ecma_get_object_from_value (arg1); /* 8. */ ecma_length_t new_array_index = 0; ecma_value_t current_index; /* 9. */ for (ecma_length_t index = 0; index < len; index++) { /* 9.a - 9.c */ ecma_value_t get_value = ecma_op_object_find_by_index (obj_p, index); if (ECMA_IS_VALUE_ERROR (get_value)) { ecma_deref_object (new_array_p); return get_value; } if (ecma_is_value_found (get_value)) { /* 9.c.i */ current_index = ecma_make_length_value (index); ecma_value_t call_args[] = { get_value, current_index, ecma_make_object_value (obj_p) }; /* 9.c.ii */ ecma_value_t call_value = ecma_op_function_call (func_object_p, arg2, call_args, 3); if (ECMA_IS_VALUE_ERROR (call_value)) { ecma_free_value (get_value); ecma_deref_object (new_array_p); return call_value; } /* 9.c.iii */ if (ecma_op_to_boolean (call_value)) { ecma_value_t put_comp; put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p, new_array_index, get_value, prop_flags); if (ECMA_IS_VALUE_ERROR (put_comp)) { ecma_free_value (call_value); ecma_free_value (get_value); ecma_deref_object (new_array_p); return put_comp; } new_array_index++; } ecma_free_value (call_value); ecma_free_value (get_value); } } return ecma_make_object_value (new_array_p); } /* ecma_builtin_array_prototype_object_filter */ /** * The Array.prototype object's 'reduce' and 'reduceRight' routine * * See also: * ECMA-262 v5, 15.4.4.21 * ECMA-262 v5, 15.4.4.22 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_reduce_from (const ecma_value_t args_p[], /**< routine's arguments */ uint32_t args_number, /**< arguments list length */ bool start_from_left, /**< whether the reduce starts from left or right */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 4. */ if (!ecma_op_is_callable (args_p[0])) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } /* 5. */ if (len == 0 && args_number == 1) { return ecma_raise_type_error (ECMA_ERR_REDUCE_OF_EMPTY_ARRAY_WITH_NO_INITIAL_VALUE); } JERRY_ASSERT (ecma_is_value_object (args_p[0])); ecma_object_t *func_object_p = ecma_get_object_from_value (args_p[0]); ecma_value_t accumulator = ECMA_VALUE_UNDEFINED; /* 6. */ ecma_length_t index = 0; const ecma_length_t last_index = len - 1; /* 7.a */ if (args_number > 1) { accumulator = ecma_copy_value (args_p[1]); } else { /* 8.a */ bool k_present = false; /* 8.b */ while (!k_present && index < len) { /* 8.b.i */ k_present = true; /* 8.b.ii-iii */ ecma_value_t current_value = ecma_op_object_find_by_index (obj_p, start_from_left ? index : last_index - index); if (ECMA_IS_VALUE_ERROR (current_value)) { return current_value; } if (ecma_is_value_found (current_value)) { accumulator = current_value; } else { k_present = false; } /* 8.b.iv */ index++; } /* 8.c */ if (!k_present) { return ecma_raise_type_error (ECMA_ERR_MISSING_ARRAY_ELEMENT); } } /* 9. */ ecma_value_t current_index; for (; index < len; index++) { const ecma_length_t corrected_index = start_from_left ? index : last_index - index; /* 9.a - 9.b */ ecma_value_t current_value = ecma_op_object_find_by_index (obj_p, corrected_index); if (ECMA_IS_VALUE_ERROR (current_value)) { ecma_free_value (accumulator); return current_value; } if (ecma_is_value_found (current_value)) { /* 9.c.i, 9.c.ii */ current_index = ecma_make_length_value (corrected_index); ecma_value_t call_args[] = { accumulator, current_value, current_index, ecma_make_object_value (obj_p) }; ecma_value_t call_value = ecma_op_function_call (func_object_p, ECMA_VALUE_UNDEFINED, call_args, 4); ecma_free_value (current_index); ecma_free_value (accumulator); ecma_free_value (current_value); if (ECMA_IS_VALUE_ERROR (call_value)) { return call_value; } accumulator = call_value; } } return accumulator; } /* ecma_builtin_array_reduce_from */ /** * The Array.prototype object's 'fill' routine * * Note: this method only supports length up to uint32, instead of max_safe_integer * * See also: * ECMA-262 v6, 22.1.3.6 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_fill (ecma_value_t value, /**< value */ ecma_value_t start_val, /**< start value */ ecma_value_t end_val, /**< end value */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { ecma_length_t k, final; /* 5. 6. 7. */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (start_val, len, &k))) { return ECMA_VALUE_ERROR; } /* 8. */ if (ecma_is_value_undefined (end_val)) { final = len; } else { /* 8 part 2, 9, 10 */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (end_val, len, &final))) { return ECMA_VALUE_ERROR; } } if (ecma_op_object_is_fast_array (obj_p)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE) { if (JERRY_UNLIKELY (obj_p->u1.property_list_cp == JMEM_CP_NULL)) { ecma_ref_object (obj_p); return ecma_make_object_value (obj_p); } ecma_value_t *buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); while (k < final) { ecma_free_value_if_not_object (buffer_p[k]); buffer_p[k] = ecma_copy_value_if_not_object (value); k++; } ecma_ref_object (obj_p); return ecma_make_object_value (obj_p); } } /* 11. */ while (k < final) { /* 11.a - 11.b */ ecma_value_t put_val = ecma_op_object_put_by_index (obj_p, k, value, true); /* 11. c */ if (ECMA_IS_VALUE_ERROR (put_val)) { return put_val; } /* 11.d */ k++; } ecma_ref_object (obj_p); return ecma_make_object_value (obj_p); } /* ecma_builtin_array_prototype_fill */ /** * The Array.prototype object's 'find' and 'findIndex' routine * * See also: * ECMA-262 v6, 22.1.3.8 * ECMA-262 v6, 22.1.3.9 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_find (ecma_value_t predicate, /**< callback function */ ecma_value_t predicate_this_arg, /**< this argument for * invoke predicate */ bool is_find, /**< true - find routine * false - findIndex routine */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 5. */ if (!ecma_op_is_callable (predicate)) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } /* We already checked that predicate is callable, so it will always be an object. */ JERRY_ASSERT (ecma_is_value_object (predicate)); ecma_object_t *func_object_p = ecma_get_object_from_value (predicate); /* 7 - 8. */ for (ecma_length_t index = 0; index < len; index++) { /* 8.a - 8.c */ ecma_value_t get_value = ecma_op_object_get_by_index (obj_p, index); if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } /* 8.d - 8.e */ ecma_value_t current_index = ecma_make_length_value (index); ecma_value_t call_args[] = { get_value, current_index, ecma_make_object_value (obj_p) }; ecma_value_t call_value = ecma_op_function_call (func_object_p, predicate_this_arg, call_args, 3); if (ECMA_IS_VALUE_ERROR (call_value)) { ecma_free_value (get_value); return call_value; } bool call_value_to_bool = ecma_op_to_boolean (call_value); ecma_free_value (call_value); if (call_value_to_bool) { /* 8.f */ if (is_find) { ecma_free_value (current_index); return get_value; } ecma_free_value (get_value); return current_index; } ecma_free_value (get_value); ecma_free_value (current_index); } /* 9. */ return is_find ? ECMA_VALUE_UNDEFINED : ecma_make_integer_value (-1); } /* ecma_builtin_array_prototype_object_find */ /** * The Array.prototype object's 'copyWithin' routine * * See also: * ECMA-262 v6, 22.1.3.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_copy_within (const ecma_value_t args[], /**< arguments list */ uint32_t args_number, /**< number of arguments */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { if (args_number == 0) { return ecma_copy_value (ecma_make_object_value (obj_p)); } /* 5 - 7 */ ecma_length_t target; if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (args[0], len, &target))) { return ECMA_VALUE_ERROR; } ecma_length_t start = 0; ecma_length_t end = len; if (args_number > 1) { /* 8 - 10 */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (args[1], len, &start))) { return ECMA_VALUE_ERROR; } if (args_number > 2) { /* 11 */ if (ecma_is_value_undefined (args[2])) { end = len; } else { /* 11 part 2, 12, 13 */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (args[2], len, &end))) { return ECMA_VALUE_ERROR; } } } } ecma_length_t count = JERRY_MIN (end - start, len - target); if (end <= start || len <= target) /* count <= 0 check, but variables are unsigned */ { ecma_ref_object (obj_p); return ecma_make_object_value (obj_p); } bool forward = true; if (start < target && target < start + count) { start = start + count - 1; target = target + count - 1; forward = false; } if (ecma_op_object_is_fast_array (obj_p)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; const uint32_t actual_length = ext_obj_p->u.array.length; if (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE && ((forward && (target + count - 1 < actual_length)) || (!forward && (target < actual_length)))) { if (obj_p->u1.property_list_cp != JMEM_CP_NULL) { ecma_value_t *buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); for (; count > 0; count--) { ecma_value_t copy_value = ecma_copy_value_if_not_object (buffer_p[start]); ecma_free_value_if_not_object (buffer_p[target]); buffer_p[target] = copy_value; if (forward) { start++; target++; } else { start--; target--; } } } ecma_ref_object (obj_p); return ecma_make_object_value (obj_p); } } while (count > 0) { ecma_value_t get_value = ecma_op_object_find_by_index (obj_p, start); if (ECMA_IS_VALUE_ERROR (get_value)) { return get_value; } ecma_value_t op_value; if (ecma_is_value_found (get_value)) { op_value = ecma_op_object_put_by_index (obj_p, target, get_value, true); } else { op_value = ecma_op_object_delete_by_index (obj_p, target, true); } ecma_free_value (get_value); if (ECMA_IS_VALUE_ERROR (op_value)) { return op_value; } ecma_free_value (op_value); if (forward) { start++; target++; } else { start--; target--; } count--; } return ecma_copy_value (ecma_make_object_value (obj_p)); } /* ecma_builtin_array_prototype_object_copy_within */ /** * The Array.prototype object's 'includes' routine * * See also: * ECMA-262 v11, 22.1.3.13 * * @return ECMA_VALUE_ERROR -if the operation fails * ECMA_VALUE_{TRUE/FALSE} - depends on whether the search element is in the array or not */ static ecma_value_t ecma_builtin_array_prototype_includes (const ecma_value_t args[], /**< arguments list */ uint32_t args_number, /**< number of arguments */ ecma_object_t *obj_p, /**< object */ ecma_length_t len) /**< object's length */ { /* 3. */ if (len == 0) { return ECMA_VALUE_FALSE; } ecma_length_t from_index = 0; /* 4-7. */ if (args_number > 1) { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_array_index_normalize (args[1], len, &from_index))) { return ECMA_VALUE_ERROR; } } /* Fast array path */ if (ecma_op_object_is_fast_array (obj_p)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (ext_obj_p->u.array.length_prop_and_hole_count < ECMA_FAST_ARRAY_HOLE_ONE) { if (obj_p->u1.property_list_cp != JMEM_CP_NULL) { len = JERRY_MIN (ext_obj_p->u.array.length, len); ecma_value_t *buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); while (from_index < len) { if (ecma_op_same_value_zero (buffer_p[from_index], args[0], false)) { return ECMA_VALUE_TRUE; } from_index++; } } return ECMA_VALUE_FALSE; } } /* 8. */ while (from_index < len) { ecma_value_t element = ecma_op_object_get_by_index (obj_p, from_index); if (ECMA_IS_VALUE_ERROR (element)) { return element; } if (ecma_op_same_value_zero (element, args[0], false)) { ecma_free_value (element); return ECMA_VALUE_TRUE; } ecma_free_value (element); from_index++; } /* 9. */ return ECMA_VALUE_FALSE; } /* ecma_builtin_array_prototype_includes */ /** * Abstract operation: FlattenIntoArray * * See also: * ECMA-262 v10, 22.1.3.10.1 * * @return ECMA_VALUE_ERROR -if the operation fails * ecma value which contains target_index */ static ecma_value_t ecma_builtin_array_flatten_into_array (ecma_value_t target, /**< target will contains source's elements */ ecma_object_t *source, /**< source object */ ecma_length_t source_len, /**< source object length */ ecma_length_t start, /**< remaining recursion depth */ ecma_number_t depth, /**< start index offset */ ecma_value_t mapped_value, /**< mapped value */ ecma_value_t thisArg) /**< this arg */ { ECMA_CHECK_STACK_USAGE (); /* 7. */ ecma_length_t target_index = start; /* 9. */ for (ecma_length_t source_index = 0; source_index < source_len; source_index++) { /* a. */ ecma_value_t element = ecma_op_object_find_by_index (source, source_index); if (ECMA_IS_VALUE_ERROR (element)) { return element; } if (!ecma_is_value_found (element)) { continue; } /* b-c. */ if (!ecma_is_value_undefined (mapped_value)) { /* i-ii. */ ecma_value_t source_val = ecma_make_length_value (source_index); ecma_value_t args[] = { element, source_val, ecma_make_object_value (source) }; ecma_value_t temp_element = ecma_op_function_call (ecma_get_object_from_value (mapped_value), thisArg, args, 3); ecma_free_value (element); ecma_free_value (source_val); if (ECMA_IS_VALUE_ERROR (temp_element)) { return temp_element; } element = temp_element; } /* iv-v. */ if (depth > 0) { ecma_value_t is_array = ecma_is_value_array (element); if (ECMA_IS_VALUE_ERROR (is_array)) { ecma_free_value (element); return is_array; } if (ecma_is_value_true (is_array)) { ecma_object_t *element_obj = ecma_get_object_from_value (element); ecma_length_t element_len; ecma_value_t len_value = ecma_op_object_get_length (element_obj, &element_len); if (ECMA_IS_VALUE_ERROR (len_value)) { ecma_deref_object (element_obj); return len_value; } ecma_value_t target_index_val = ecma_builtin_array_flatten_into_array (target, element_obj, element_len, target_index, depth - 1, ECMA_VALUE_UNDEFINED, ECMA_VALUE_UNDEFINED); ecma_deref_object (element_obj); if (ECMA_IS_VALUE_ERROR (target_index_val)) { return target_index_val; } target_index = (ecma_length_t) ecma_get_number_from_value (target_index_val); continue; } } /* vi. */ const uint32_t flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | JERRY_PROP_SHOULD_THROW; ecma_value_t element_temp = ecma_builtin_helper_def_prop_by_index (ecma_get_object_from_value (target), target_index, element, flags); ecma_free_value (element); if (ECMA_IS_VALUE_ERROR (element_temp)) { return element_temp; } target_index++; } /* 10. */ return ecma_make_length_value (target_index); } /* ecma_builtin_array_flatten_into_array */ /** * The Array.prototype object's 'flat' routine * * See also: * ECMA-262 v10, 22.1.3.10 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_flat (const ecma_value_t args[], /**< arguments list */ uint32_t args_number, /**< number of arguments */ ecma_object_t *obj_p, /**< array object */ ecma_length_t len) /**< array object's length */ { /* 3. */ ecma_number_t depth_num = 1; /* 4. */ if (args_number > 0 && ECMA_IS_VALUE_ERROR (ecma_op_to_integer (args[0], &depth_num))) { return ECMA_VALUE_ERROR; } /* 5. */ ecma_object_t *new_array_p = ecma_op_array_species_create (obj_p, 0); if (JERRY_UNLIKELY (new_array_p == NULL)) { return ECMA_VALUE_ERROR; } /* 6. */ ecma_value_t flatten_val = ecma_builtin_array_flatten_into_array (ecma_make_object_value (new_array_p), obj_p, len, 0, depth_num, ECMA_VALUE_UNDEFINED, ECMA_VALUE_UNDEFINED); if (ECMA_IS_VALUE_ERROR (flatten_val)) { ecma_deref_object (new_array_p); return flatten_val; } /* 7. */ return ecma_make_object_value (new_array_p); } /* ecma_builtin_array_prototype_object_flat */ /** * The Array.prototype object's 'flatMap' routine * * See also: * ECMA-262 v10, 22.1.3.11 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_array_prototype_object_flat_map (ecma_value_t callback, /**< callbackFn */ ecma_value_t this_arg, /**< thisArg */ ecma_object_t *obj_p, /**< array object */ ecma_length_t len) /**< array object's length */ { if (!ecma_op_is_callable (callback)) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } /* 4. */ ecma_object_t *new_array_p = ecma_op_array_species_create (obj_p, 0); if (JERRY_UNLIKELY (new_array_p == NULL)) { return ECMA_VALUE_ERROR; } /* 5. */ ecma_value_t flatten_val = ecma_builtin_array_flatten_into_array (ecma_make_object_value (new_array_p), obj_p, len, 0, 1, callback, this_arg); if (ECMA_IS_VALUE_ERROR (flatten_val)) { ecma_deref_object (new_array_p); return flatten_val; } /* 6. */ return ecma_make_object_value (new_array_p); } /* ecma_builtin_array_prototype_object_flat_map */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_array_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { ecma_value_t obj_this = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (obj_this)) { return obj_this; } ecma_object_t *obj_p = ecma_get_object_from_value (obj_this); if (JERRY_UNLIKELY (builtin_routine_id <= ECMA_ARRAY_PROTOTYPE_CONCAT)) { ecma_value_t ret_value = ECMA_VALUE_EMPTY; if (builtin_routine_id == ECMA_ARRAY_PROTOTYPE_SORT) { ret_value = ecma_builtin_array_prototype_object_sort (this_arg, arguments_list_p[0], obj_p); } else if (builtin_routine_id == ECMA_ARRAY_PROTOTYPE_CONCAT) { ret_value = ecma_builtin_array_prototype_object_concat (arguments_list_p, arguments_number, obj_p); } ecma_deref_object (obj_p); return ret_value; } if (JERRY_UNLIKELY (builtin_routine_id >= ECMA_ARRAY_PROTOTYPE_ENTRIES && builtin_routine_id <= ECMA_ARRAY_PROTOTYPE_KEYS)) { ecma_value_t ret_value; if (builtin_routine_id == ECMA_ARRAY_PROTOTYPE_ENTRIES) { ret_value = ecma_op_create_array_iterator (obj_p, ECMA_ITERATOR_ENTRIES); } else { JERRY_ASSERT (builtin_routine_id == ECMA_ARRAY_PROTOTYPE_KEYS); ret_value = ecma_op_create_array_iterator (obj_p, ECMA_ITERATOR_KEYS); } ecma_deref_object (obj_p); return ret_value; } ecma_length_t length; ecma_value_t len_value = ecma_op_object_get_length (obj_p, &length); if (ECMA_IS_VALUE_ERROR (len_value)) { ecma_deref_object (obj_p); return len_value; } ecma_value_t ret_value; switch (builtin_routine_id) { case ECMA_ARRAY_PROTOTYPE_TO_LOCALE_STRING: { ret_value = ecma_builtin_array_prototype_object_to_locale_string (obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_JOIN: { ret_value = ecma_builtin_array_prototype_join (arguments_list_p[0], obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_POP: { ret_value = ecma_builtin_array_prototype_object_pop (obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_PUSH: { ret_value = ecma_builtin_array_prototype_object_push (arguments_list_p, arguments_number, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_REVERSE: { ret_value = ecma_builtin_array_prototype_object_reverse (this_arg, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_SHIFT: { ret_value = ecma_builtin_array_prototype_object_shift (obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_SLICE: { ret_value = ecma_builtin_array_prototype_object_slice (arguments_list_p[0], arguments_list_p[1], obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_SPLICE: { ret_value = ecma_builtin_array_prototype_object_splice (arguments_list_p, arguments_number, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_UNSHIFT: { ret_value = ecma_builtin_array_prototype_object_unshift (arguments_list_p, arguments_number, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_AT: { ret_value = ecma_builtin_array_prototype_object_at (arguments_list_p[0], obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_INDEX_OF: { ret_value = ecma_builtin_array_prototype_object_index_of (arguments_list_p, arguments_number, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_LAST_INDEX_OF: { ret_value = ecma_builtin_array_prototype_object_last_index_of (arguments_list_p, arguments_number, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_EVERY: case ECMA_ARRAY_PROTOTYPE_SOME: case ECMA_ARRAY_PROTOTYPE_FOR_EACH: { ret_value = ecma_builtin_array_apply (arguments_list_p[0], arguments_list_p[1], (array_routine_mode) (builtin_routine_id - (uint8_t) ECMA_ARRAY_PROTOTYPE_EVERY), obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_MAP: { ret_value = ecma_builtin_array_prototype_object_map (arguments_list_p[0], arguments_list_p[1], obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_REDUCE: case ECMA_ARRAY_PROTOTYPE_REDUCE_RIGHT: { ret_value = ecma_builtin_array_reduce_from (arguments_list_p, arguments_number, builtin_routine_id == ECMA_ARRAY_PROTOTYPE_REDUCE, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_COPY_WITHIN: { ret_value = ecma_builtin_array_prototype_object_copy_within (arguments_list_p, arguments_number, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_FIND: case ECMA_ARRAY_PROTOTYPE_FIND_INDEX: { ret_value = ecma_builtin_array_prototype_object_find (arguments_list_p[0], arguments_list_p[1], builtin_routine_id == ECMA_ARRAY_PROTOTYPE_FIND, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_FILL: { ret_value = ecma_builtin_array_prototype_fill (arguments_list_p[0], arguments_list_p[1], arguments_list_p[2], obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_INCLUDES: { ret_value = ecma_builtin_array_prototype_includes (arguments_list_p, arguments_number, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_FLAT: { ret_value = ecma_builtin_array_prototype_object_flat (arguments_list_p, arguments_number, obj_p, length); break; } case ECMA_ARRAY_PROTOTYPE_FLATMAP: { ret_value = ecma_builtin_array_prototype_object_flat_map (arguments_list_p[0], arguments_list_p[1], obj_p, length); break; } default: { JERRY_ASSERT (builtin_routine_id == ECMA_ARRAY_PROTOTYPE_FILTER); ret_value = ecma_builtin_array_prototype_object_filter (arguments_list_p[0], arguments_list_p[1], obj_p, length); break; } } ecma_free_value (len_value); ecma_deref_object (obj_p); return ret_value; } /* ecma_builtin_array_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test.png000664 001750 001750 00001635137 15164251010 033713 0ustar00ddennedyddennedy000000 000000 PNG  IHDR{CsRGB:IDATx[mYe3wc{gVg맽#iDzU_eGoϥǏϵ2^[?>k#TȀd8l3[2nlj5B= yKGbio.ڨ7ea>?w/w=~w9>i_YW&x}U#Nܟ5Hyfm*Uʖʤ2?bPW۴kcQZFK}hmݥlWvg=fVqocSܻi07GD^1S|T L8 b8Ѧan0`116zlY%7e]j4wշq.O{ݧk=ٓ\%j̨tzߴu;_.j5/ d>zѪEs{>.s+NZYYu:{]fٵv qe4+9OU+VZ_[{я`BU/'U'ᕱi, sUԎ1RO]j}^8~>&2Dw7?am2X7Σ}}?7_WIZD]\?4>2 sЍn8{+Њwl_͇f}o-lp0FZmQ o>v gw0w+(Њ2en "lջY,3_xx\.G'X:*`~ϩǛE]y}kwN&VwV3P~ >J_ڝ1NVҶ,9eQ ekaf<koo`u?6|&|`V_6B[Ti セ,ӌKF eKae{I- kߪwRbvuB|f4{FG9|5|<19k=ܲtF\KMh3dX%<Qci`}a<}Iioe^%mŽYm^>ʫGHr( a4 -kjZT8_o^F6F>u!pB ^9Tic2z oCqW\vO߱"a:G5QWyqT+wGV7qpZZAì{Ϩ. hT:* vWTu1ZUzo5OpN^xd֝(nrfZ͹kAjR^ uF1{9#!b_Ԡ̮Y* Aa${rˆsЄN m[JIg0nx0 _T'mw"/ %k/ GطYhr$4h֔ LhW/vu/ySn֘QfU„njMBgp˄SYV o>gvZ6A&nc,;rUo<)x-?5hgΆw$5l)t9|YJ<)6ust7.nЮdDU[}OV)k1m!!A7w.Xt]ǻeO ^*zݚu[5N 󫁬;3hZFc3z٦q5\e#8 o+YJ7Q9O)fUZHK=Ɓ; wp! "t&s/o0k8{!%N`{zk߻E* ܯ_jw ˇߤ0BiŠeLyHJPⶰN`}g&-RnMPc3^y*X[Fxu |Ҫ\Ck׳LoLrǻ竽cuY1ǜiWrkC=o+ɛv]& d[ӕ&N9 ^ ĂE{% sQ۫oY_jBmFvvS߼&L`ef6'FP%c\W[!eǬ0frhf »Tx=ru1Za}C4Kcَj H<8 ?=ozu X\/Zf Y|GɖnA-MH6B6Vh{,ðyݭp,AkCݸfaXp#ji PPeT}ԋDCQ:>FҜ5 5k@=B:FfsbyTw?ntp; #MóuXJ{[qd([1fFGx 07BXֺϯ?ϯO??}ya>nO}+`.ʜv<rǤD C8̞]3|XZ6FK`X8cA>qD%ϟ/%.Mkx@&]"ͺ(QhYQW"`m} P8 ih5E+ǔAS2/[D KmC'`0CJI! [qo>Qtľy xqzMWw匀پ a`7'Jvd-b C c-Lvb 䜖c|WG/؋sNd^Wv{+ݨXxF퀲IYe#( nX`< U>PT&b\#e_W^u>?Y3֯]cQ],ak +QII*FnCp^ɽ|ϟmR[MMr.|k_6KZ-]);xFcL6 KnQ* DI@PW U@}]| q>PfHaޥcƦ:1eDZ7LlVPgX; ,N5.v~M%Ѭ_zlj.~!a Lf lu I+;4Ȕhm6}`2t*R  f-` ;Dxw?GuY]pO{m~y|oXXdmGy2xM>*.eb[6Wz2~aKuT >xx;D0Ob ;e컳n[n'n]֌1rcEU:֫#8Y9"91ΟbXG&|N=rQIGgobgS 0Ô8`vzS^Ù1ل) 6)#FJ*v+PÉ1 nRQdȫ7};Wtеe9S3<]L_1'1dAN*?~x֢VBq=V%4$W]C@K@rt9VCw/:$ GA;)n[Ee>&ph4+vїF@9x 0<XfkI}ݢP -};uս< AʦDafF,8iö0ï'qkAzFG3g C <:+p}Y<&ꇁY >deJg=ۛnr'%f`>e?g:crOhv|hlha8oOem9;I _H\>U]P 05Ь`n՛"t eFdsKjy~I7%5?RƦKd 27Md޹%y)ȼ'C3™$/W j5ρ9flp'no nPt 5=b,K7JN毖(gP82%zfhΨ"2PG"vL7~n5W^W9~ҵvuws} ]گ n?@ȔuIP4s޻NqjwJKH;U5J63rY8nȄ?ffWّ-s-3Yg}{~kb aC^{` RDߺ2T|u ,mBK1t^IHdXhǜ5}1͢xp4Y-y]:"$}t}3Ko#?=Jf6vK*- gD7 aU@i>@mVB88{XNɾ?m ˖-C0o^wG~lȈ~Epl` ,i0D5|d|4F?myHhm"oae1[VW 8K %uh"b,.JBkaxud:$~Qfmؽny[֤-Y>HCMeu}Bg;367dՇ|Cf!ZwXF#F $.`߈-Lzf̝e6* 4{ artFxx׮b}n0#d-l;@8cuy)-MȴaR8k\ ˔Mv\:Us/n~ƺܛAJ?e{7XG%9YtI] ݛ #xy+epw<}Fc7dW^ 5qǎKwe[Ze%]7rծ]v [SrF}~xH2cXxhSpѩ}$j`AuѠ[-AK5BVcxɱ"̋d7כwavNk-/d؛]KF!$O ז'2'= <.d8#D>iʕMڎٵ;ײǁ,͊v7!mfk#{fM8cXpd z41DS;M$v5L$oٿQ{Ybxg N`al"4I!3k4F(Zܞ n{Hcv{&C2?ѢgUr'i_88oʄOPt՞۰o|}Qp0s.SByz#q'f㻾^Ə_׿]z#aO.0'7Ѷ0y UiY|͍W e[pbNg4D~Y15}ٺ|wGˆmO4y|ҴsZ;W ݤ$f[{ HQ{pf&?7j Gf lk7T-ЗHn6z}L_tD1[yDO~Ύ3*NW`Fʅ1|/YV阪~v@ِ]f6~+.3 -؏w˗0o9xCܴ`1+/C[g/'<i]ކaeW#ȠmK4=Z=׫7k`_f "kOP6/.2t^ZTfE)/p;kYWJq z=W=Cz|/tyYŘfr{Ctb C Vmڠ 6A{z@ 1ifѝРiѾ.^i5̒h_0lx/ekR>VVx}xP7' 99K(T[]68nAe'XD@ ][.q͇!8"-G}>9LŲ8=Wˏ $=#^D8 !yϣ]3Wr< &*{7G´a7&qحuuUo??SޅL!@¼ #x{XߣjI{5w7s466 zAk2͏/Ld27Q3O{+zsս_;c%ݙ:mjj nPVP}*H Um4:;s΄+#|-uKJq]_ٽˆ-*3 h*y8j-Ex^]>jDF눉, ( mg q_}ӽU,z<̺1 ~ʫuu+Nac!H6lX+`t>߭|Nݓ<ӑOGJ6Ll ՘kk*O!9!`{=1@bՏ4@Fod-GG i%q( vr2< /?]mVw8!oG*[gI_/<[6ʎMb4@WzY;|D[::&u,rWu^?wyކmúQN5:BCMg%,>aH^"fnDRvle=? A.VgY#p ZH`N{!{袍W }?S&ӪFh5SË `YE 6vӱ_w9 n$k׫gX'l@^1|Ѓc3KZ/Oˏ~k5B0|A5o_Є:^5N9nѽNؠ'wx|uLqn^ij__oۣ?B|ﶊt&whD^?5Q+e~dǾ;u?>t.VN T7UՊ1d10W#̌[Ds,戵 5}ɦt ^1Yח(\*ӴGMwsȲ]Ekmzo^.CN,9*w T4~"p>;g 9Fhl(Q By]tld{ #078$X9Hgiz̬7"RsUߣm Oe}J1]8:\Ø 7;T<|zY2[3٭"LJ]bdnm#5ǒq̬3X9a]fZL~ݪ.e_T" ! UN~Drw=&I][8XZv~}sWgP7A' j^t- r}O=EF`-`pʺԖKffd/9_AЮX5W]Ѻ<;l:-߿s՞0_? A| N7az"v>Efc4lC?JQ Ƒ+MW17x[Ѫp?#Nj8!=WZy/ @ieɌ.$}<r otiymV>':ca`yAg؍~. 1u%+&v1`۝gC{T98*vpêeE.˂C.Ɓjiax7w7c8t[>o_ll[6;ZwGK;`n=o\eynivp?J$REs'֛Ua":>F/txq' j6E%z<+:}UbT5t1Ŭ SQ{ϟS68]?z~s [WJU R oI/\'a E#Je1-5TmˬK /a/w-nU@#ȥ˫c-k'RW7<99<{Ճm ͒ "f1 u}AYEPHbFUB6Jix|)?۾HX# aL;`u5p„agK1y^E6OaHCa|DnTu%GZ֎mfUi}1M0k>@7ZtK; >fB2Fhn{ļ[D|;xm FrLsgh v˧ UۇK^Z#o:F8|hػFr >? i=8õh1QM+ӈft3Xֻ:[7z/6¬d{Ů۷/gp=a2YFkw8_t06[k?ʇ~ȹ'LoNzz{B;aZʪ1iҔ?:7Cn]u9DC1l}q3g/L\YBf=ޱSӣWDaB흄T9v4F{M(ɽiG^ư9>?!uۛA'Bb7^E *ƾzGZ UIK 68Vgݥ"vdI00 ""37!33333peY~K  mʌrIK8('AK"uI 0'DjމpM7 ,+n%pB܉T3 _F9CXC#'ҏ!EF'#3S٣J#;`FNPtJuvwFG --ɋJ K %$R8(k) *ؓYFmMtb+,y^$J1#=G>Nɕţ(RD.u22cL)J19Ys8NɓgEqcb1хJ $a) HIR;A_Q2#Vy@lc/#_C fHR吳*[Xrb7*Hp>\p Ő~( Q ΊRU YEMUAaݎN>o;GY(b%[FSY05>C .p3z R'"R0/+ xxD~#BGAC$ddNzX%)N+yKpτE qWyH`{ p=C S-( / d2v"!5Hᣭ!3019kPBٻЈB)%Y+)ptP3MASqdL,,\ `PVNY4 yY .%4#1KR28kwJa"cNPHfD9Ƥ'G*R?"Z*ls2yeJ)6d(|?ߴ?_9zUaKs7 1ъ)Ti99 i2b${K;ʃ,V^aN3X'-c+JYI+HZFO|&GgxR II J$4w}flsO>;N߮x܋e"K&$UCDi$`OZ9w192R>3@A(Y@DgNR5g32aj\˩HFDeX@ sLT'xZL$<(x,ܿ%_(=T"DNiHO0K %&,0.3dqMn\-̓s$&O@m$dA$^f0mG.ߧZ62)UANO $hHJP$4JGw*͵$ M4iIIAI!=Qև$q IW.}S%42'qdZtUD*13 ]]qI̡D2 DA$A#,-g?zR%I_l~>K 8e!R 1X'9R"Rʋ&AhFrLfJNQ N<<$Ud$@2ɔ.An$| ?kOFh[d(+JuXȕ3%C/$ bNw2H  H,KքAg2xK%ZN:c9QW& $`I R7 (oa4O/~Ҩn|I֕]ϣJADbHKA7D~X[ ՓRQXґdBy@5BE'W$ fGí70`9F|jDx-zFR1'QL1L9H0&"颳؎Y ܣ>b:Hָ)xy2"kf M7STFJfęR͸1GHˌFFӺS9oaΙ6qEl.D2^OޖN)&=ezȄ.1Vo7RX"Vc2 sIPM$ᬤmVQ-Sw'sL[i`-y̵#ӹ* Mr•AHc^499LfAA舤(73c"PO hY'js !PU##P,Yh)!|e1cg٨2 ͜IA q˅=shGT4ٴ˴Dp& 0-#&R2@^XB#=.IDLTVr>d"eb/@įt QRmXm<" ׬"-Gg]]""SD:bMEdC%ғ2(#()p^P~7c(B#&{撃(K4 JĄeL {r!%]Fm8LB J0Q&%`#8\`J "3Ő'+ՋȔRQADL@!DdڏۘNr:bL`FUH͡YApx9'>S=ٞ T[ t9:hHNC&E8r"I"2ҕD :_}/}ޏ=Bs?0uIJï=-B'Q9b]wO 9Y˼*QCր _hgxeF@Kr<\LTk1san%` ,WBG7*t}7e3䭉ޞn1s çPVA{ݸHrG;OYY-zTD I*YKDYMR$HlR-(ȝ"Qi:G"6Z$$Eiwjˑ,NZ0LU $0٨dSp7%"Epu EF'Q*ġF$S`d&>g4"PS黀RڕEhܘ7^4<Rn"l d*Yn9<,lPM Jq\I*2)2I3Ɲ Lp &$ܸYH`=8&AD$E)CN)vpt,LeT89K% aNo9I"1T`hɀ$GflA!%sp˜#Dg0OInB.BUؓ]VrSpH ijSyi "*FR5J 39Y6DYu2Ig/4ZH9@ْ'SRv`lx4 eؠ,I uIsJ& !LJ΄>br%7ޥ.RpT0h#+@6;偳kxvG\_yDFIc{Dz6?"p%.lH)U=Iu!P)6HiTc|t޶B~hmփ h1jK>/W~Seaq""6")}E1JtJr[,H lROAI=waY@ "'gȃrniC4zP0ғAN+A5J:Qq#5B((.3\z>' $]%@l46# @3j 71%\V 1epg.fLL Ta2=MK{kM>Y2lu5ԳD̶fϚ;F0,` rE]G.S];GZ}8)4ktsM!N$ ({pH&fVL&")BbØibR՜y_եZr՜vM̙9`,IH!,nsr $sN ֒s\J@I\<$ mWTVD.b!"kˑ=Ę,be BJ%M.9̐$1Qsav VktήT@jx%BL%l-2'ͻ'&b ZgeN]WI\-LrnÜHZ<8ݤHYkfʥrҒ,RH%R|flگ)HS(B<)=R`u19>XN3GJ1*֒g LCNp)L4 1\Kx6B824+ӝEaH&Q$$TI R#a bTPfD@JbFFJM2μ`c=Ujk}fۜ4ٔtn^dr@Pf 0sc"yJٝUE{{dLGQ0Ep8s,4K͗5{ry(7Bzto9xJ) crzzOCA4( O bԳte0#+ S8ȉj)2ڄZ)C(\1,q$SY6,f82L!D9I`ݹCԽpܠoEY=RbZD9QXG-c$`B U4GcZ#J!KcCBH& :3$U]hM3\`inݲU=L2Fs ÙQYh읝)LDJ5ݘ$u:#>+rD[D8D`)Q<`fac:*Q#TIxZT4q4.Kca@Rt'$Hb&-3hP*ؙH6u#í;ӗ19lRU"@^IT~AR(U(c,TEbK^Xf7F"-'iư Fd0 {Ğ,!\gc3G/|{ՏG'Կ\}Z &v7UDJTk¶'A 8y%w*C%M 0#%X V 9ʉD $Ă)9)}{d"%hnālܜDRC#{i6q{X:͈sNwf%*_AY%E(md`LSqL*D-bX.'AE(+SiȺ$_TPVP&aB2e8eJȰɒ`-l.OJC(*R  o'Q3BN(qܩ/Ob@3%D&Έd%ì !bMcd}*qiA@Yx&9Ҧ{"B{O.čm}+H"$﬍ $$;Ρ!AJ:iЊp$*<,VX? 8ݾq+v.8v DJsEL"wq0.vK&&B 23YpadF09R'YTɈbB'sdz YHhQ:")A9d&$NLXKNβD!cd9Y4ᳪ9I 1 }nkݤ(L``ޅ!tNˆ[0-6ҵT) Fd*[תHq7a!Z)69#^F#P 2{zu %Y";lfmz9;澇9I#jQx c.)1\t)L'2A"B\="r8=T`nL|M|+rH\I1LA5r\JD:ghU#申 T<42LkdG%cyaa٣(3d( % by0q' 9 CL32܅!-]BF7! pkBNI@Oo,&fA9iI3f ,Zܱyd[1DR:{QfRweQ-މf0F1HS0\`M" g2",hTaֈ6n@re#I"=%ɼ:Y(Mx:- , FNR#H.VI9w# =~ݨ,v!_X+IgL % p(rwмRc)h RV '3]:kBӸ$dCBV<@[MKGZ͊ AC6[R5m2``aOeh3wrt7T=G^Fc.:m#dƐV&Z&*# g6P? ]TPNʅ(A HfZ)1PtNUkI$,TN[\(r ]Yf.@&Hr@6vE}1D:EB5(`v#[e"A H4No9g2!' B!aǰ偰ɸ;Wݞ8x ]ӝL @YNsza;t*`%g6^b:zK@*ӘpRPfb,';G/!n$4e䜜DřؙZV$KG$a| Y@ɭt"M'/fǽ0>YөrQt(+k"iUD93ھCR2HDr_0| ·K-1#l-C^r%ZBD gSrm 2 J"@ojb rhgLcH~%ʔF v. lk ?2D,)l0&(vPUE$`Pb~ Vf'WD32 &M+KUfu-'7f95x-r/ $)u8,99aa5U\K䯻Eئ(%7@h樊$J,sç7eT3V<FDhS4wJc" )ǟ3)Ǥܸ29+NipW  Z~!Җe+q ?%H"O0 /TA .Ф6$Ib Zs$sɺjZZ L" tɜFKXfBQVp ,3A)lT2O j=KN-j#'sSKae "aId$ ó r:άIR%I()L̢ d" y ${ńhI;( #4͗IqPȻPpD]C:T3^-v)g2&JI:Ha)5"{ԥBV *(&- 2 "JEt5ŒR !` jf$o+tfr@ u֔^'N = Q ,$EBR 2=LAH8+ϜĒINCDȄ[Fr$0{5wň\`LĜR(& 8Tvz5s.j!SLjpQʜ3sI4,y N:8P8pRYC(ĔEܼ2,˚tPV"W||pi@GRGiH'cYrGs\s.۟G yzCxsIl{hEv6$ H,I*JiAzɤDvr"k++E{ yiucn;gyk0gO,w?dFȩCHI T"p(p;Z2aUII<9.[X1t R%\dJp832ӚTswD1$R ,df[*SDy;$ɑ3 %H'ę9#J!da\JUn;Ns&?Ɍ@3ݒkw 27 Ť8BR+PNc)56;@$]I|PӼOB0v Z B)`yQDG#3}r8#2AJ%qb '-V TeAYJH*s)G&G$clMG[-)A!`0DI 8!)9GȰ>_^ҲlaAjl45ElDLCh "EVwa|ꚑTtN" 8$pBz 3Q"1ms68in@d 6 ML%)"2JX ÂN&=XI8(D#w^õd*z6LUGLӜ*ųwH`"}&VG*$ԒRII @VEK1RX=R%%Ĉ\М.Dci IAQ̜#ʦэq@dԋ!4 `fOC$ZP7AESLf o X2,rb:xYT4Ꞑ1Aӽ LdR(=)) NJo,ĔmQf3q#}p$g( 3UL~O;BlvGYŀr'9}d"ȕA`K4gF:ygL!Lܠ$;a:ɼHq:+ŝRU 3(NR"6 rFVJ}4j05"d0G;!!࣏/Fhtn͈hP)&p6V}37r뉆\'䵭g&c [>٤MIax|ݟ3 iDK)INme;1) -i#Y -3-d"Sh"aY7veQ0T؞P -M$#%*.C8I sa2Zm/cH'Hg!$0,V-A*A6[73#i 3>z0X8fDɚ"R8̑S)4J爤RCG?(=C.iQ^MOxLDQee`,1Drɕ9KdN1TʚHFD]"FfYSX2)6 ޳@ѕOIBΎ)j j< »6JAFv19DfNɘXPGJ f]p$)bowHMPLH.-Mjӓl&T*=Ht}XYN# ߇2(-0 @ PDU} 8]` V2dLM"KQ~A .U٫%`,PJD@.PE[ a(EPHxNY<4q3 1T8,RpIa0<=4+eh*Szv"Q#)`% E\YPq)'6'ɉFq¸Bp$2W@P]3-} )ID4c)TO@.ll!$0s&XHumd QoA!LPAUtRY JNtxd%#qx&K2g9R1^1?طKrD0d) dQ#Q-9VJqPCV)#2$?fȜK&!a E%?PMk!r0X:\<3%"bDt&YEGg5"aZf˦qfG -6H,%sֺ9SZx8)̴1]xj5e1 -2); ˴9NI$晼'S-y22d%ˋ'[ bOQ]Q.5)$IFqҀO 'av&uEyɱ3LI iY)DZ 8Si3νZ ?s{]N-QnYXc0Ӗ4¤H LDU&IBAtI6L Hn8gy.ApD%N/f H_ x(L-aS8 ?ǟ%c7 շH g(9cĝ6A50%c}{3R?SO3>(:i@Ji)J JYeHC(r5qD6ūFT]a&1=%tX?zt䧴cx @ PLװD:H&J815Qm$zmdBP×%4cwT*XK$ OD]PvJ`/,;9"Y؞d g&LD+HY,`>)>qc{ܥEKy0x",g郦2F>"4A$*_ 14xz }Q >Ktʉ &T heງ-ɹV%ɴ2+$H$ 7$WP%&zH̨LGFTL9`=vA攔~ɜ) ά6\ݤq3s;Lj> |P=Jߑ*9{ M8Hn]P}$ =ZdkJ3ک{ 2&M=B$kzꞺz:g\Yzɔ‡̥4F&PۜM,,xKG(Oɕdfzfo"0 ;|x"QHP:K?mO^Pm3ا#H qQVST}OZ=HklZ2'/~~>^_!W*w(9JQuɊ]-9>N1;rc E?돇u'1EOn*cѓq2¡/2%bbz"MꌜIfaH"?RlHe6Ǣq ʄ4q 7pd 6>£K9w#{P=/! IV|iN*KM"ʕy1!Dl>@rLGD(ݖ lHIK#faLᱏVRp9 Fq\$GJfY/k a %{P (lϼS.E` XIh9Mi24pN#oi3 (Cʪ'[Y[c]>;GP$DIDBLp B%}d$Q$ e>(̡L3dJ4^3 J9JTd4y<$C^ qsT2\"#f]ker (e(Hꑩ31GI=cr!.b$L3-kae6 ۹Y x9 FHr&'{Vt=st&i]DԁIiUsxL4yw#JEU`NÈ g?bmᦋATW o ]U4 A 1X@pkUp  =IN X[ EDJ"{dGp@ql^̓WWv$)HR -$J3Wga8EI2$1]U"(͹Qr4/>&> "R.9z(1XKnB| 5ƨ`PR®%Xys:q_t~ 'vjCS(EKr&lPE-"KdԘ7n?/f?_x@aD>di!E3Y4N?>yxF$q-B,UGpoS$@Y`tEDLpR70!TpAqFD '  Yd5OL((z])J9Tr#spr0B莻a>Ixema,4`Dt1]p DG,#(Dl QIFIea:|&LG&Spgs ƺ bs2%Zt]"p;9.)Qm@R n_r+EIš+4أ.2FQDtaYVȀԅ &rG*dY9rz0@Zudhf͍%GਥftFK4Sit~gOJe;d)4cЋ .3]&5CDN:5ؔfipL.ba+W1J2lB!Տrp$ٳ1*D\AX)әJ)̘碆iEٻA!vJ aGf,I @FL[:SRI!EHk JF.9*Tk01W1 {qI#8QvjRҜx;feOQʉkP>BX % t 93-R*QG%1da$iFslA93i.mH}$ 8iH" )&*3}EiqUf(uLf).X1)ySmJ"pj~iَo?W DfJ#겑M" ;3݊fDZ99" ;?B8s{0\q*Շdx$ۉƤ *a 3^Ip0OA2=X FrPV&d*9= sJf% /9l$!N4;a>v9f1%f|u"gͥZ2<Ý#CfFeKF8R=XpEt-4=&Ry oʤx-M |]hZB1j#7~Q[E!NuK'"c!\F.YxQߩ. fxddS-)D$`J P"dHw[26dL"!;L4PV#8zVa`%IpK11#zA!3D`1 v"6'7 2)k}r֚\TA 6LdaSQL Wqw/E8=l-k*`L>P1R(A#ſ7ǷS$f2k$5mSn1@RٺFkIB<,i|b-3)=T]w;L(M.B&)T(Fk Fa|'*N>R`yReW9$FŇGK6#$%öBN"%^"EQ *b>) T68G ($KJf=c *P>A4 J"K ȅܵteLMrs:9"@6Db$0YIA`*1h . ; GcY>MO`*y^2o fBYDPbQk;"yIUL;0D>9aENN\O3+Y@#\4xR@2-7_IHΡ> \Db-FШ9Kf6a `rBOx!Fx5 dN tyddVh G]0GFH&r $v ȓΚ),AɔN)2Ӄ(Y c )9eD !0H d;OTL)PN,5)PFH Hyvc?%OgaBw!\ܭY#F^fl1vb# 7f><kL$ys@Q#؇L "0 B*u dAdR5iD+IEzfINhD\y,\>> lnfNB3ȳ[jdȸ)u. DIvcת9k"wSLRق$1=)'ΜR"KY b4lJ 4V!Jo,D3'ə@Bp̢@[alpt} -3i+8'x3 c)|DU)Kv^W.,l%J3t vZ%h؅WM! H )@!"aPAwÑHPS2K#j4w6y Ĥl.y{4CZ/J(9QVꁓaXh bPee;S04+"Z ALFYUV%xad!($ D Cc#C%bRYT,,E赭焈Uat\Oe;f6;QJ)TZɪMWJ}x˩~ u+ZVpX%N5AQǤ ٶ&'(CdJ6&-IS 1DNMg B(r1I Hf`deq{XxL&*TO~2)M횘$P%dORMe+VaH@%,{l7d~0zAB1qZ%F&{+1{GH Pt N.Gx ȷ3$͏oUy$`,m/?y~9ԃ{j.4xw#b2^_뗼}+힃& D:e0ܢVrJk:3S$ #ɀx_\JZY)k:`T 7rɝ81F#4)& DJ,BHHBSX#cREߧ{r;2EKcw[|~.GfF2D* SQl ow0yI"lݸV*0S\!5r0&q=Z=O;,I®:M4Ey䙠_+Qݕ`3-T50'9t0H\؉ 3T% $B Lj9߅JeVq*d21.,ir  ZrTQ",G <2!V L È= #jO.T4[x eβZ9Yy_9rV1IY]!RGœpN^|fiXPk>I;϶iHHh j>UՑkYei%ՒuhIAV]$'wPbf#K#3W4\BKe,r쉖sa!$Q!) lPx'vO#$( $+&H%IPj|tB$9&2%w^UWN, &jD M0SNN)»N6ؖwU,ϬetO»r"(E!ocmb,c 4 <%#- ,Ji!t$S^63:G *\D LN`(Kc"oSڈ젘D5gLʩ7ԍH>15 3~ vգW"4Qj5cg^H"0k#8tv4aĪ*Gyr}*\ ~Ujasz 2&*[)a3ΛMNQP pB)JD:IT"ZPp#S2Jg: WYXUQ{ 8vȦ)-qd}dў3H}['g&>t])R-lx+ؙ{3v*)sB554܁b>* a>qH18*b'ZB-XjXsRFPP M2͛32f0W L܈2D  La:/æB\` @!FXJedÒQFNbR549dHrI`0 Krc,y8Frjp,Eye'S$Q<O'*Hܖ{RHf $18,=d->sRIlbUNVc hHhuwLqG) e"FJ1 `Bz&nq38BfqI߈!82H1 JWĠ*^+<"JDA@%Lix~ Pa0d $ $˜B4yϙ= YS8>~4ÄFݢȒ>@3CH b #j2+0PAW.R&%@2ك +)(vǢJ%i(*IHHߝF\$%J1ҋ!X022<#A}RQWP{4[$(H2# $d2XR^Nʉ, i4 86#!B`>سT²h#z !*,= r- xBtAAiI88dEh63R+kZ2A8 0Hp5e5wtkT=2  0&l>>~ͲX $JJU\R i[8y.'/d EHң\)C'铩8¬g"o3D"& PF8 $Y&)<%)aAN\-""fdB<naݸ sP!'D>\9ǻ{И}X4( Q'O-Y+KXwO DP4dM{""jl]ʘT̒(3#-50%Q.CZ}1%#.^biLauF0q1'!2KPL%ʚb$+xѴIP;灀FaL Nږp0gUD%JIܔէEXQ9E.]'#%V[ZIv'p2͑L*9U(!Q&AepB4~Y $X,$3PhBĄ@R$cv~aJM)+sT,L e}9~ivΈ.$RUE4mhc' @8V)HN<;tR"JDAH%o'H%XGB̳P0 T*gsVIq f6 v cCLi@L/o Gw䯹T八3X)I+I% X؃ivb`)j"dXOg܏1͉c.BE 03[ d JFfhiF䘎ޡ2kδx:eekR*YBHRM9\%\& ) ̀jTr>D\뤊6328̎U+3.10kk?y;*ALZ?,'AP luufN1#ql[0Jh48@N0іt#|OR }G\,D1zz˿-?A~?|]O8ʻ1}J.=F:*+e *,$G"8\<2"-& _y'Y*dY 2BJ EJ:&"HKaLi0`l4_Gz.A\*UqD4fdΒ10mZq\Fgiv O{Yyjimv̢AKDy4N ֬ 4*ƙN3e!!ql7%X/ !DZd3eV.Thx(=Q31`0PĀ E<ۙjBPX>X(wG( EXWHDt $(@䓢ZkǮV+iSQTqhVәiݮiGw˥fh&yEr2(^VVK!ٻ}Gu qڜ LLBT"*Sd-ƅ'MK\ $w2g6O*s$6ó(HŤ6y-K3#)EVV W_N flsDzZ-yY

Ғ/ %֍9Qv>m-|_c-=|~}+{=~wwNhK^Ϗ;wz߶8iOPNs>]{YLkl'5# Edx@'ZsY8\Ý k ea0ч&SFԄ Ez@gU,lSr%a6FpIpdJE&{43~|}ݗvETAY $NUaL eYELa6U$rbK<{ڜ. W=M#wEAc9)A@' 瘴 d̤ohưY'Ğ}g[T'"$RRBD\)=N1yPJ-V' 0>o&8҃2T2#eO<"U12͑R,TRr͜11d-nQ䑠Қ' kV]A" N7ə5o EH;,$-PBJw#!: QɈrC\YTt43~,G{O<R3N"?b-J Ϸ_ۋ:WcOoOߞ;q' uzs~;28!9*Qp8gܦ,$D)S62,9epF,$I*H ]@T#9`@"bO[$U!"$& SzJ1RsW4Y[f4A>8QJ?RKf\Ed@'7;-5QDABt 0$L|UB"@ "pgZ"Y: (4-}%Qc(43"ddAi3£}PHx$Pු=8r4-n>k!" 4bp" n@!$KUy3AԐ3+m'JYtQT߾o߿=M8F`m $ 4y%:"6L9UڼH}zX y(DRL͙%FFDtuLʑtvx)<(OHRAKPأh%1)BT W_^dd}wо{:"<, "œGt ;!d6Q hT&)QY#Uݩ { ǵ 7 iVv&66aIMrY*5 &Z3ɝP3$4h"D"ydY"$.3*١ETԌ{hJ*,*>I)Ʌ-laɤ"u~BIA,rwv#u 8Dl,F1k._'Ï=ٛ1zywzl>yTzǧYKƛ=[\Co~>%g(_s~G꫅ws7'c j~O_p y>\3ZxLJ<=x|Μ4 |)VSX 35",әһ(-;B.!`'GT*͎$dL&$DR!R\_4s{4J  j`:cDSNYt yU0;𑲉s“$uNw@qVA"I453M3A (g򤙢{tj4,|@HhfQWORSAC@ Df~_lKs3d"_^>Î;ފ 1)q$sTQ"Zk#3[ӞIaB caYr8 IU((iR'VE?OPUrN5DCs aбfi&7FAtuOY/* w*dNKb/GH$iIH4݉ i"=)mnwmR59|/N\ޙKB6fBLމ&%y¼#(#TׅxVR`P*1a-r} ;ŏ#3UZ!wZ]!&n-Qr8R5$l`0E`VfZh18/V^)h!Bm32Q!m,*Oփi1{<P i:#z$%4b4SD\b2D C#Orc. Vʙt)[xiÅJKUmzjtIb颼XZ#26BMw淗/|,>opV_tcPZy~KgjI{ieǛNmymc_Kص僾[c|r<ӽbۆnZQkՇ[Vs\1ŜM6)aD I)^22(օFRcΖ%s, l M o4o ,P3ʹnIpE"`< E7$%/yX +B.k+1p#0 oaeIr"\yno12'XA'=P:  1LH g3*rt-ӉX&@0^Y5 fRBC&4M"r I|]$Qd.H->DFD>bm,HvBMސ Np 'fJI3`z&l$s\NnW}>>_6,]N^e@Ӥ0Cj mrTn~jF쳨Yf>ѼY`)48mN{X&۟GIftC,9oR3(II@Qh¹qs$}8E 2J`U>,䒹ÍK%[^ՕEн. @[KPפ YS 3D)˂43c"YBDR-4t.R|$&I\%MB(;Mn31 |mE6+gFȠJ,LA%ALޔ8@q7'r VHQJlOB#(I,b2#IwJJVԅsX= [Z&AB:e]-ٓד?U`Nlr/Ώ w|24E/FWʫ`?*/ݯO| ڞMO~Bzq|q)=.}ϟ39R[>5Z5ɃBy4c-JӢo;Oc EANi~@g\q/Q8U`O ICOZ<0 ϽӓaQ:!I@OtTpDՈĖ(,Gp$* }=,K 0]V*5bRy ]<,yEAl!u UId2=FJR!XָQƭPg^H$0DI 3f::zPCk%p6H(25K%M-A)aN?A4sC[wp6Z*Xt- K-% O$E ҉ @ aY3K-dHf{_=#s ϰ@BdsRFFMhI[ץ2RY"39م9H"1&C؏N*B%qj"j L#;?% )BɍH~9 ƵƱ'4\yE գRa֤f3r2SO& Y(n[{12"Q372_Bd:Iѓ5(P\ qP2 &6 %<*R9^8-0&#y)ݟIP1N: 8'L#+X=XGt'"bP#yhUVіov[pyPy6wxpD_<}>2UR7/~Z+|nE=og>)ϳ47#>|9 91x=U?~b;S) 9Zg|6+%sVY||cudS+S]<LQ\f-Q0y$4  LjzэPrT*흈B,y#PP!E#yQ14;GP*<)Ӓgg wfZ|Z u_P.]HRPIA %'..p!J98 P`JA$~0Bӳ`2%>0J@Gy0;9B,1sa 4;E+S8Sfة,R+$ĖD<$CHR:B\h(RVJb' P0P`aL\Vï||/#[FXJE(%I4@Ɗ%b83ͨϮF6{pm5V08V.kfT Ն!@6fO JݒIH;ky%;ϙqK H-|",e n1W$EU DY\Ɯ݋Ҙ)NI 5l>"TYÆL3=it-_mK8 }aΒj2 ɢ4ӳ4'9|DDaT]VLdNQ6aҶca˥E)5mPJ5+)(iEb4V 9Q )fI5"1`#Y$89גB`!a>-,U=MS"s^S:aZ1Y$]F7I<zx gOP[OGn>hضI.7dvDZ^Γ60%iÝfzKR~Y]o?~K|1fS>߮<~e]=~oe~;F,trZͪ"_曷ò_m2퇜m<\HƔ1؆d(c>>,/&^I@Vtw1%%)3H= y5Ϋ"crY2&0X1gqs-1nT5D(z%ӵљiX /CKRR)ו}03(h@7Mt )쎺= Q$"xh$!,Nc{ 4|<-,LaCҪHzJl$wLd !HD9&@PpPx_3̒Џ$%L эJ9X9ĬĖtbʌ"4"qэYIHL ;N\(Afi;v/|oo/7;`{* 8rw_Rs "(rH0HLdD8e0R \Mr]Cg[">alz6NZ-aS`TH3Ȕ \rtzq`~t,a նԓ)/fEmy^nw;2O^yIYŏR ~w_㱽hW\ZNSMΏ?QN?AޞZyŗ_3{F|*B=;,KlŸghrUbb,be^McbKX _"$GP9R7焇[ejP DLb3isD wf-U38 ӸBH(#rf[>= B\ZBCΝ,NXIĴ1qreS 쎕jQX wq뾻 ("'yJ4*W#k<݉!3` XL4rRTPf݋RM%mIBJcV)$ _P!$""PR##e<OpPqfϾh0|a, $P$)q\`0W 4HJYNU3g1|&-zĊE fIpϐ7" K GfBcA9 tf9hKYHȊ&*$\A6@3y&(<453#[z ez}{J" *>\O+ȹh00Ydd&YCGxJNOEFPR2U;dS -ބ,]2odJN澐Mee2,Ap&^:$cFmnĚȨB 8|"*4: 1Hx|q"Y098vb"r'&Od8zxr3UJX>}@yY0 ՟;o_߭?-O?w>J?:/>c"t!|\MUo$}So(cM ?P.Zo߿7^_LcQzBYt:[%ιPy婼)M6#n=iCPh[ 'Y ,%+3aѝ̒Iɥ$%E״iJU'%ii3C+'0!bB9fN>L"͢DBƮВMSjJc-Lȃ,$):ȉ(IN”$iZɌGns)AAhIL \{*(R W6'* Ff;#f GmWJhH'/4cZ8L ̌WNR# 7g;|t]L kYDkr!L0!%_>owyC{PJt%VF)VZ%"Vg_ʴlLN=.SgJ,FVهH2ʕLpa6.NdD!ǟU , C!6Y'4 LGb qqkZ0bXj)eASq1 Y9Ĥ0gXDdQ9zeؠ{R}9qwsNN.k&"X$&>Fh#P)EJDR# 4֒"QFB(:3ǁZW7U%$""r̚ԢUjk\ꗟ`A'싊e)i*MNlgp#\f1"r`h] 4lU a N阆 G.gfëݜΙ;7bZ"&JaWbu". >F*SLSG{qz@VUJ,p![L&!4V`f-  9 5,@*I=89͕C3 Jn< ܄JJ6Dk"ҷf$-)™p cqe gBj. :Q!a DDD&8Fz~~F dImY_QsR>){aNq^txIPЋ 2϶QRTaP9,w˰w_zHNaR! mJo~mۭ% *a0V")g@{8|Jqh(Dă݅Ds$\$k-1`d2&;w#`N4'IK+ci`N#!'@駥]x ԠIlȎ̒w ~G| C~}=nj4N>J*S).Kv6[ h.1>9[|=>=I9&zSk??Q~}ۇV(]=U/)drA#?|S>_|ԓ=^79-/[ nR+ܹ}ǜ'+X F8/8aj}˃qLJ;u.t$T&"*NC0_Ρנ%1Qx)"-|>ɖ<1h%n>_9Jbn9rDpsKX¸!cT)jw Ќ 򬠝0${ zyGqim6Тr) )Ǘ||yc"&hti$dW`e=DEBy5|9)e^~ŠSMu)"8>*#Ex K`&gY*,3ąLLs@dRKSZ+ ChElq2cy\[>n#SU;:;W)T1Y<. Zߊ]WZ%Myxs8tD@I$ ,]X<90 8"iy#~ kU΍nށ0ޮCsxmDni3\DW'$g#8#f ˢ,os|dHvZZЗOu>|yG#ၧE"Xhx|oʇ7gݟ:>c.J_7U2dY!vsj.3MDpVbkӧEFwJ7 FqC d!N`bd!*{f\~߅FfUE$EtZo3Hp U1A){yYf\-̫ݟ=1!Ð*YTHL&pAL2oF33 dIF1&|x<Er%3M>Ly}=09^{d7#ps~ E1R+E鞲0IIL1F>N!R9"4T<^#s]9 yQeti,ɔTJ^9XIgNdN$E#csIqJlymf=q$YJNdwh wM|v~|j8’.oέ|7صoF}ycן\m?N;/ސG/u I豈k*eRŜ4[FzZ =, q7Tp,nd6HY]#r~X޷<FІ8y챮2 :`L\cr U),AJ(NpB%3YR`ps`EGuV=)k "PY!cS3Hs#nN7TINa@쬢l$ tO!8D"g2i(]Lw{$ >9-C\PU N\X#ɗ%`D(tf&1RRL…n=y4k/OI)"=,l"=7Y- 2.`@ 0cv?ȺVrkQU*dRP_֊E; idRbҸ$>ӥFe _j",~ZpR(]i#fdnZFCI(,''qc({FpyWL8co*~DϋFy]0p PqeY N ׹GYJAb"/;oK9orHh.kL|;ѱr 1y9 ؘGGRZ$<7J8 8sKwB2n"i=bR|wjz cM,@ `)PQPMT((ӲH>Bq'ʰ.#Qc.-_{ J|˯ޤٯ}PI=L|Ւ[;-F¶;/F龜{Um\pH%i;VLvq7;l9EdQ.}?m{_9&_}UokpRC_?}lٮf;Ԗo{Z=o{.OG~oޔ7 ]LC2} ^4Zcd]5>Sd--§ڏ4K=G:^)ͳ%ZS LHD "dPzfP ='E!Hj;EP̜cٹLK!J9= y0ΰ^**Z:5"+y GH0H"$DL'!"u])-ɏ$60' Ie0f%8E5spl ۅdd fNS5, RU X #P0&i{%#O ee1Qd)ZCκ/Ʌd0@ *Eb>3k0r& dZ XrmL33'DDdZcɖYi G[dExmq롒sp,H@ mYcg1R&NԒD˸9q 5'x%!u7=nثM֤hm"3CNԢ>ܙ\C6D~P!` 2sdszwZy|R.1sSF,TQx܌X22IB Uʑa"(yb6,Ce2IyXϙU{'e̘ekJ-(z1&We֖v7ݺ(FdI'Hc=w#!Y L6]Z{͉_ Xn\^zN߿TY,黍^GZt!WoƋf_7PASk};,qS16~)Nr9|~Ko͗嵾~ǶOO6u1vm C=b_\O-q̣6[ek%]twx<5FO2OtJE?>g}GR%a앥E A&L aMʩEks#Zi Qq Ό8IZdF9($(IqLqB.;KW"">Ut I!u2s0jcjar0ɜIrg>3ex'2#[4aIo2@v$w%~D{>8NT,(3)jr 푵zOMZ,NZc3t" -Es5DW?fHrJ>vme)\ $L 4u~~y;N A̤P8ihYGd CI~m~q濿f^Ӹ< ъM!fH "^xs3KJB^Kj-U[rco2' XD!ve=eKp̱u^#ƻ5X=r<|8dOOEկJ8}oT{u>oHSTY2S֨]M\N %b74u3Ϭ-` =MQfyHUc]|-mޙFDenD΂ Lj”.$^u:c4cs*NlNdFYF4BҏGTn|z{ʼP *SRCx]RjRS XBN ALDB%,v@s d!!T$ v$N); .tR p`b`P2@|~4ciDl #`DP0idB1SYm9<Be߭hpP Rb^ s+ Vnǫ__o_/0(N! b ܒcR7qQ!Fte؈/~݂j6w}>(Jލ?6 Mƍ7/B<@Lhj{~:4zSKda\(E8/,Epx">D89"=(9GHnm}C*Ljmcj ({']9^$W*{@ReR*1J,TdDlQ DHLe ?7Piɔ-m0'2}f3J|wnpu ܨ2dR2ۙ#p& G*q!tXf|b~Ug ?^IZިގ^)#_g9U\W=]:3Ƣ=xX5x6]pᝊ_ߞ5? }m3TmO;˃iF_=IO=zLk =~oC凭h)=ӯ4w&_ ex}M}_.@cw[lk(4iDJGs4k4?<2cexOg<W:\s\lpyz/A14ʤY4thcN~8L)@LJ% Ѵ\JAu- V:(Wd)LYA>)I [ tZ H^532aP٥3,=)b/ox^E"88tPS܉$ O KҜƒDJ\9)ç8 'JRrfLH˨ h!mS@'#cd#$ dfF-䬑d "\>*  O8o9i=pJREILT"!eawJ1gpAD&VLeqcHAFpS& E(r&w4&H%Ju@ET6>H{?9 ۍ9CN;Ȓ7{8r lYzy>?lQN,0VeueJ3Y2Qu%# ,9!,i6-1{!G 6fiƼg,5ʤJ#L _x1\)H`42 10ҧ h$ttR/wdHYHbMpy4"t vɒEH"D[,kɣ2 ~cW,T.Q40`d $!P']~߿,'X /Ҷp$7."澬jhǸ-#8XA>Ehm֎Ǥ(B^}p~u~jQkW wcNzJK wuy3;Z~EͷOۧnl˧o׻?卵2u>]\%}~'O:Ux/X廇mF}s{mYQKC'2sQd@en P 8-^M=W*U9KT(E8yP:0@H* ٗG"w¶92 8 Pr4sr=ۡl,сF> Yp0n NT`"e&1I!`S LITԳFeq8P+ UGgX2STTD D~_?e de*wR -`c_sm$薰 $a^ qŶ;"4bd5Ljyp 6F`Az)@\hcfL:B0N8&ZTHP;/!JdT!:BNԂ9H%" QF|i!ЍA82HNg=0iX3>G!7?,L:%IT.$}j5G;1pzHwة(eB&%hrZ+R<@:fp +B QLN2PF4}#uq3I:%i) (A N[^Q L Sp(L9 W.k,cHZN)R#͇Y؊PG}1N˱VvG%V|,gh/\VE}Jr /uYg=+GXvk_rjb5'g/_l\NXE[|y91K%x7;VdhpcOҨ@P\h\ 82;`(h|\ (,ҼC4C: 5xxe9\(d)⯠4Gx*|S. uvFs%Bv̉~?7z}6s2 .9#)$-=WY2 :pƴ0&ʫ{N-կ6gN@Db\6De + %1lJ;Uhe> b{?>)YvgNQw>_H`nDK[-',!f Eq&1hz9"g 4(\{L~3",g3wc}th; 9mdcpjEը}ӳ7NT&k[$D#\KpS/[4=VTLeI7Tԥ(@$aGM8hQ@cp eڂ˔$$cbHPA˴-,RftRE9%TZNj 9ЅOcjJQOcB8woޯ=!r*_!'yS|at_FvtzǝOW9r.ovkc>ꛋ'TxY 4o;ٔе3uPq>M"oucuA qw=G:9p1}uY O9;>`VH`!!CZh?y6:"#*OB8izBg Ca0NԥC*RM*bA {$rީ`lwZ9^V( RWMb䃉 y2=xSB.al& {x@z&1ipBdVF1grI7@ )+)2"Gה`Eq3*bQp"pp#=((TEK'6, Kf0s&´%_AKOz~7kr*DiHQIIV9=&6 :0g^_ͤhPZR/u]bHs&V--"+XptA9>;! \y)*q nA ~M m/Ԟoz=H#BQi۶EVu_*K%j#F$Y0,S=[[m4#kts>~zZ}wBT!'Yפz=Z$uhӌimZ$`lEҩ,6:L&M&X,Ц .LDH<8XRlR:aӓ{,P dd@&*iCiu$Y9sg]q fq|!5_?ܙ9i.QDa!#u޳>ܥWFg{☱QHQr%F)GyS}4nM==HKkmߣt-7S5/S cَ{Զ[6^xh,FVɋzTrS5~h.U)y2iWдj-"طZe~;=j/X*ULYS9)Ur Vq>9Ǒ&YD9<3ôqYdXDYMͱtIMXl]0s6hajBVU(ɇT&=wjɖL4)ⳓδvN!F|~C^@B'yAvD]|(EL*QW[he{`#Ngt'B6DЂىk̓ɂ vP6@P3  Z$:r]2TBu9ikc_o_?~O9= KKƚ$%%TY,LZO ɼ͗6,h2YlNK6Fk̔ʹ>gp+x8DVn -(8>ᙌZU8("Vi A8:&mxk=~>AJInQP+͂,,E4J.]4!O=B)-99'@dפn3gfMH{^*Tdb)@ JXX1'2y N`BJ S6Ui6BxxJ8Pj Dg6%{sTD)԰  dޜ gHF"`JhABZ CqKPrƱ1*L dH$CJ^^__c mKY)<ڹn K0e8"ΑL_/F!Zdv VTlv@N+zV8uvz(?_.yBAm+^>-oO->{+?Y](ct7/XP>fx CħoXzòUٿoC歑=^>f'9U]89y`9ph(4x<8ڌ$uMyO,DtyS_^1uER8J39 C;KZґX2B( kh {ԍ!RH,Y=eծ__?vϾ(T)%dNCQԥr&'sFD(̻cN֔Gr$nVAJ>8Ӻ2Sc#qcXu2[̫X5nw<" "()<isĴ{nG܇C#sjYJJZl8+oN@DyۃNDHRpldd@dYiAnbva{.pf $xv)7ahXLo/gO 9c)d&LLdHppX6JR"֘IcYKTkQFDZnn9܁i~x:i oCے}%v4!JRXHZ;a ʹVDQVg)ey$KTZ $L L-ED Kj5‘IΣO-2nj휣sL@0gpdpB&4"@ME*ǁ,J,{ڸ2`X.IpFYxܘ3gԽ IA`O9 Y(B_"OIHI"d$3 S…|}͏ZhPGD,قZZ)IYnI6Nt,ka3"uh !.ZhQwnPHƮL .-rXyXӺ]Z)BdǸ^7k,߼xJ@(nN6lKv|zV葄~+D. #V`WVgYDb"6j̸H>.YN.~ t#MƜiVyx0̞M&ĦJ KR;.etMIۣURK}lspNe2AEeV։M# c ^%Q8uVNLh[B,+e)2Ie=gϹ#JqWm~Rzj5h<t:Y}̯b¥k~*E9>㚣X>ZlzHx33AN!& !gAVM]!:z#.4 H^ "2&! b̤p,Q2œAlq >gdt9gwӜ̨13D4*9Œr`L\D˛ #K %@̹7tHX[kS+ȑ UP-S%#Q;>j#+i9]ɫ=nD\mh+.y55l)DᅨY}c'یH ¡* (`1=" < P#&x!Fu˱t{6~g*n) bxd!WLthnrDvw!B*aY7yXEu$f綰HHj@9*0cxY i ܥ򸺹d wC)7q=L 7|Ttþ4?e{__V鱞%,wE~l|0=W /izP ǝz5Noʩa_#϶Ϭ942f!̙*ALEi5L*MgY5)P &,}N,vjg$UXFQ]H؎w}~Ol2<&4#sXưҪݻY7TuBU3s:C| eV heY'YZQӨ _;WBT! !.JCf}t{ VKwp9<)~Pۚ6}1!8&RQɮbU^MpTUՒRU.;5?\Զљ?9ZO]F[Kz=xsn^V|8~ͯ7lR#=-S&-7v8YtnrQ\igČ0;=m>2vm,ﭮd=r,j(9|u+w^Hlf{m Q2,,&2)RXt۸O u)%ݘYp0Vs"cR؏LBv>ӆ,}ӯGDd .Z, 2=5*KA[g#k"1W nr'8Ӧ+xtS@Za1gRruAG/dt'e)Yr9h!$B.=d!fam~1G2zJ&@ $C7ˏ_F4P#K}?z>E-~?s[R˜'[V#Fh{yˏO\J$~ウ/LDe{k1@Qluu G޾LlXYzɰcmʖE[w}gzSwy+s[YgOriTNE8R k$OL{T뢢ݣd_>?|wf/;}'sy,P*4d۸M7Jpn_18?xUw}EzEVR51vfENb_~/ʻ7.otSS5U95V*k9/qnaGYߌ;$4D˺dOUĠӘ liG<&gxiB,Jc^.:3O WA1Y!HdfzqskRF[#)q}R4f$DB2I(8m7d[ I®Zn"##Rrb&D" yⰃ%`(#ܕ{'D)]*1eY"eTP v|~5-9J n+KrTu>aP̬SOO?P\HO-CN.sNsrxվ6a}METX8T'lrTp%2l ]Hb϶*_OL$9+l#sqf֩PR8vT+mf yɹ` qnz>}/yN>X$BE%;-2' FPe 8G"k$IS*1%7wn ),CE9@ wdYиU!D wcw(CsR9"dP&$ԝ Ode[c9iw"~A.*teF7N>޾}uT&8 ~FKqdCp7o}:nӹDQs%Z׸5ۖ|^ʪ,oǯgsxGk+<G)βҔƕ^?]+8KAmu) yvoZQǸFYpy,wREB\EEdD Ng4Ve 鰃,fbҠI##) qsϵlSE:O֚{לZ#JdU$qBJ%|%ՉlLHE,a;iAinj0rp؝D]n@39ʖ Cg,Ί(F*b BƧy[#PwdXv*"OK frjhZVr9<BQAK%D4$K!3fdtsdGGqQ1<<52[)1GRU+ 8=,9zheh,T3xҎ=CZIHQ=E""`Yj#gT)Ƿ\X6O ‚[UF.A{rA\$h ZR5$Y% U).B!Fg|q⡋>;SfqM79WAxj2"^ _խx$icf2^0)gK4o*^y{}x -@fd:Ǽ$y)J%Lwx*?TMZF&ǫzs+|z\On=7v]OI<>}Sڃ=gNH2Uwe9=\e." q$'{&Sx =xC^sqXtQbLLnA^ ФdgB5BxwPQpԡӲ.{mJ:;- 2$L%kWa(])=(dQNdT d3yt#8}Y0 kRHJs"RQbC؂ =vqDpMTÁr5&; =3` fPJ,/'S j4YREK"Jx,P >~~@HaRLKKc샂h dZK\(GJФQ>:y΄dy!互YQR}݈୙y!FIT$-klq0Ý&}D9ox228Υ$T*۠Y;+M3A"E2-%]! e`s`#t䕇)|4׷FHXw*#yk•"hAԖF2FQOPmj=IHg!؍d\/k'#gr~nI5HT -BQziev7BVqv=nF-,CCv:—j ˛*|e\϶˥wZ~vZ_x\88,D6ylK|x̓\irݾ*]0/|[CҚSLzyqoTT0 }uqEylo5isxo~?Yߞ/u] †&%'g@ wĤ2uݟG k,DdޡkȲRIDd sI"9==\jdDTnEK 2Lʲ.l\U pm4!A2% 9~N*QkQKQNL)Ű~w,؏1@cNpx""HY $݉,*@2J$WF<N<6aw_Փ{8sgT4(D45)žLopY,cޣU̇@zEaQ<:Jt&Z (BJIY*g=gyv9Zz+PV}ic:{E܏Ⱥ߬[8n) ެ]_MNY>|S3M8`˫ .,Ǜm 9>bͯĴ7tZtT 4oVV|\~sM/'x7t'Y?Ə>O}ECYk|S%ײ.9;nqgCUj<|Q\Ф7x i}xdTמv0WA\[+W"%r( ;Ӛ1H*C #93krPbj]K;ى+(`Z#HE@Q( F~x] WTc~||M,"~*ϟP}<(X "2KkRZ1WrDJ1.a.cAIղPa;:l+Bhp-K aβ";z ?^jKp/*O ߣ+ֿ|R ~yԷrwwf{os=UڇēW2}r/7Ql綮\e]ϾjS[ؤ+vScn2ߜj9?~Vپ '{x'R:4O]r9r1m<5[i MnDtc!1fV8s=`NBp@BBLʽ|rݨ$"iri 0wkӧyPT\ ]тw^,.F%rC_eQ2du ʺp$Y%13ҹGI!q$qp\{,} X92唔&i^V#XspԅBjI\cU`.XN|1;Y[RDNI[VGN˞&Pd1n^e? 6J?9C,/όW>Ԛ~-';jՅ^vxv!fT~V=tymq>H|u1N2a/+=n?=wpw[ׁ7Km"CG>njO-ZCI;w}U9|&?COqΌǮg;O[}zYNzfi1!47HL䋇K(<(+%Ge~˽xSB* Aq8{#Yܧn,ֆJتƖm'oiN)C3QDF`fhpxM bTgB&%&V>=; c<ӥhΩS$'Gd=yqr92=3D} y@g !%J*)E"4- i3(HK$/%fR~;,M0sd0ҍqԥ&/U(#Jrz}o޿7mY6aa]?y#fqDvtg|pҤi$*ZjI`RBR7QAYJL.@::)N@BI#$DPg"W (&sH#:ۛi"ל2@+Ux&2Ζfic/hubzDgHYjXFnMRA ^#jEk%D0?<vNMߡM*\YZ^wHNEeIl2-D)pۍzr ﹪Q OoI< kr9TbtI<,#߾Vڽ|^Tei)+O,?1[wnk֯,swd1z7X塉]:>he:xot-U.[~|^ߖ7^%[ekP~ӚrJ /ᅭ!je<_?$kk_ˇi.U-WVAhDOU0#(Gxht{R NR3e Fm-<g4 `%&M 9sHUʜGeU,jQ,FK>3%Qr"BΥV#KL\Ƃ !$9YQ>f>쵽g )bYN֖[r@ >yys &gf.ӹ7 qr sKe6x[$ьt &fe$J|RH*э9`%0]K#x{ݿ_~2FM܏iU1M4h$BrT=e<ѣHp1$W>UV"MCv1%QƒJf/m( Jiǜq^\y"6j4-ȇE*ǟ<ǜLjO7q}XxKT!e EEj$dU*T J cT=#IL9WpyȌ@އ&ԸA( [#O=& I"!iT߃sjtϵP.*}x q%sk W.PgdHraE`"Ŧv(Zz=(.R}Ys*rIJhX[)4|JT}>ɏFR8YL[Qk)rcBu+W. |2›7ki<"xyX.Zp$ V^ZLR~2,hQ.fp>YgǼ]|5g]bAܸk]+}[>smuŸo~]V]>G=Rm\|򴲬b?>_my%|@xm|/eDNC?Uy'. "8haE*„XO_WAf/"(sJ"N6eS\VUEC0_opV(&ucS4/W(\(mmVf4܀D`R D*у,Т4+-ll,P\JSRq_ׯ<9'VLp`cT"<ϑIDj#v͊divRPnP낤e@ݻOZI ۉN0nםޟN޽yq{xu[)d$3b{_}ʤiQ8VU=G dZs].K#DdegMf:%ELɮEt,5g (y=Hw;7q+PdNOOfZ  `a\ɂM"L&đD`>$ĄAw1:zD*;9$! 'SIfPeN(&d:imqp." nH|z<,V^_hB< mK9| a\߿=問CB,o/[ח,m,"܄rcMjYʜi,%x~*`jǧ<= i@O ͩM[eI}wǛOK}U[B⇏txXcYj|~1H3r_^g{[oo~3У}5Ir75>wWIkkK D dJJGPc7ݹѴ QL.i8^ %#zPi8TZ?v׎ ^K㲡[(hYbxu4ENu116Y$2_U,K:hM+!'*ʔ -xvR@T=s!?BKbqJ7f^@1{\^_exQ;M}hNQ":HL(tUWR)=D;)A6LDX 1(g BPR,e3 N$eU dy\ C N_}: J LiTOU3U|f Y*I-g[rQ&e,)4 AqͮA#my!ԏAF#2FG1ƤA$,U-)K!gn &L3)% D¯#63D$y*ZJ:YB.DYآȵMœaNqb֥@P_2i2{ ?y9/K pBL!' pbj4;Y}:F&"6X2 Aʶ}}y|ܞR/zY :X7ѿgƐz}ne# 6H)2P1͌P G`)ܤRdƇMh91:+{kFr#$ ٜiw5KF%T&ePEOa#Cy"_{(@{@kH- 54UETBYP21mBA&D 0^[s\+1x10ExVIbsQjU]f9lq9^׵XJ.DF ǫ_3<ǒwIq܏z[=#ƣ-z\-̱-wrzs[lۉC[ʌSf/Y \VqߏkY9/MiO)o ⃈buF59.y)gC=V9xʣ,ݿӼnUq|c~1tZ: cW':ʃԚx^<>{.ᢥ]X+9# }a0n/0{H6$`va63P3e5ybr.RlH-S2eԴB =!H221 Pf0F1&=jW6GZLfk-<\`D̙&K˙ MYЯмiC/KE"1X+eRVL%tKu&MBwBU\=U,Q*%ɸԂX)6ηîckD$rQK\#3fm19_#CaO{,Aj=FiBX7Zs9N4ю^ַkau_^aRj.)a1U\&}񞗍Jp$܈{{S=y}*ғ3i ۝u%w"7!HDqN#%eD€+HDxQ-m'e-!U$- 8hr `pihUi)!G$ DHIhxmjP'*4O\jQ|{NƷp/01@3fʹkYG3Et`I ` x%p`C .-)dα1!I 4 z I4\Τ wa vpJb$QzW&?pd.`/p_OO7 /GZ |5-ʞq?6m!IGYX .L=isR͒$z:)5Z)ַ?i^l5fP"l "TYp9ǜ2=V߅H;qk7CH-H*"H$)^ڋEJ)6T!wòZRɜAH$g [b"\0;<nݓed=%ͧJXc JD`Nwa9PsX"#K-2K~#O kj}/j=!ˆr dq jpFcnԍqL6|9q{ gy{:dm꼿̽v)*sZn6{zvK/v%vortͭݞj{ |>gjh+)Z>Æq>#25([:}[?Jw!?\9W_q~|95,ڃO Ä Y\g.Kf9 Kʄ ȋ S.B Ł$eIZ6&.Q†83c!kpIUs $R/)B'7el\'))@†;QdWMu'OGZj>-RH9H)'k`!¸OWn6-7F Ggɻee#SVJɪ\$rL g*V+;עٻ'U8 ]+0OB&=`Q cV!pSx}cI<+XWY=BA b%Ԓn9vrVW֥,xM΋.׳Ӥ'OlZP;qyCsyy+(9^,kYVAR|'v" }N)'Zzz<\1EZѡdJ]4?WZ7ʃM HQhd1h$WII̴l,E#eipb 榍kCwp"wfKx܊ F1Gr3x}Dd'u%m+30b$N6AucA+X&r@ J9,L\1']Ծ{3E[\)αHt E%^|-ӫ%vӗxv,BzZJSHQ 3O pVAj>“Y(iZPUN;j,uj;gENҊl\6ZOi)%Ou-Bī0M:MPmR}%q =]J4*͌'5rn]R}^Tת\Ep jۦmq9Xٮ z;e$[ /d#21$FdW9𰙓1=iQmP*JLU`*29I#B.5#L!$%rZaɜP*w@BA,%Sf! ΄f Ëwp& ͔)R`*ޅK,j;QPZH\i)E)8E3L.r|mӼF<HUNF$;mQg~y6]ʉ<9t,\AwIȱ}R|`mNz{4BhC 5LD9^:Ū_s}e[>/s{w{'aqiK%V .%!K~)fz P>$*ӝ#|FxtGN+*7B1LRN9_&RNHA'kFh237yPRH~T7,d P 䉳c]I. Ly2rQ$v"2:x[8Ȧ/̎5yc4#av5~r3= 5EFN8= G<=nϰ]B$[2}}nVHYHD%k޻)m6Db2r0 .!Gv.J)I9KF`} V}dLNdsNr xaa< I5G ƬDUXRR=  x8a,#K]Lf9(!MdqT%!e%9j\cr~||vy{v[F8%œ4KUp3EX-vZѧLv*3Cg,F>7KsbG}GsTMgc.>}?I_^JɅx}{{ -HQ7w8XUcq{AouָϞPlcW]yu{h 6L˹~DË3}mK'ڍ˗wO[9%`vŰqS+(rs/)^^=1z) 69=1GBNޓV 'nw ȦgOfB,%ReU>I!֜-HK@U%@ւi*yPK,k+yH< ˜$3QC2LSB@`ΐqI_Be|gL> [څId.@iQFJYV;T5S33.'PFT2KȼgWF9t(U( a TBtذ_;1nYDmδ*2Q*Iӣ2I~%UؐE*4{PQOv6]r`aYG|5 Sqr~Ӻq[lʃIĞAz#)s[T+k-L8_{(s)Yaβh"5rtqz ;Qᙂ C2i)JHakA6Bak?AsЖ'|qBR/Z (b2P#2`3-3K~tp"8ӠdVc 'R<&/8ӘHl: t!R/*V$9,0/)!.DI!bJ30 eA3}6mYsd^JH[y"^K9L*T5jnɝh|"J4%ENDa 0e$") JdhdCD)5K//ϟ_7?>g6%qMZ̅HKR@$ i-~ Jɨҏ)93.fDqhn\[g 'ZJ]**%.UO\*ܯGiwe{ZP)zt;fs>G\NoD?q줧3Nj=IøV &rF, `$>Q3`֐dl "JgCV:giw΀E- )ykd B (wӺՐXK]N0樴 ey<Σn-)$u,ESR=FBs!8s,ԸqlBX_~iTj "@{r•cHp1e,Ka&:"5Q'uXh"")+Q)KFr9etr&vQяIG'}{(56w'Anm+:"*Rfξr9Dw![A]p\fw(qPZِ+@M,%31(O@p .]ҙ%"d|kRdc'2|8|lk᭬vMʼn=r '}nc"rν<4:bI_[y]$<ПyoĮO~WQNg_^_?l4~?M-~K|]6}SAvsJ.\["QCgWl_WV䫟~wEݶe at- bQ #gy׉j}˫W 塵B9F~tAyY|Pt~DltrGK)[ĞY)@Ęr3 B) ZAT/,괏l'&l#LFъBtHvuq9É)Is:R&i] -Ybb>ZB؉iefPRD_m[iI90BĮ `ThmcR(H%D倭JUeYvee_TُQUzJ,Tˀ5C26 *!ĘDʊ":-[;PSC&߃^8nK "aNYUpFT1D%PQ ̑-;#i2^SX3NHDe0@3 EP#!`r.T:b$RF,5r:9BIhFH}2mf#fH\E~j[.mXqS)m7MNEQXD4\H>!i; k΃CJ}YQ0=9m}k_?pRzeԧEu(~ݔWKIc[͛E1˗/a}q2n/.&[|O~-d'GAp.a,VEh٢au|2ny\{_~s9]>Ky5Hxy4JVswҜH%,t׵E̚DqW)E"ݍ ;01"beS`tG!Bk׬E0IE.2*ER YyX[9}?Kz-,rJ%5##CdVŇ($!JYAYZe*I`^xk# Kt]3|$<w!7 5S>z~{G6I3x>ߡ*YjDNq q&Rh @}$Ү'DsU=[ ʘ̚(S0c/|z^;Q͓NP@r.+Z_s}:zG8őLK[#1V{0Q". ȈH,<& ˙2G$0rXǬa>-ylFd9&&D,R=`1r!.,e8=LTI>/wMYs)k3 \icfdFe ExhFd1MU8 XNox%E(A0. !F'&X$H LKs0<%᳒8PcW+ iTNw/c.4dyQ/W  48߸=q^haQR@SU=C2i;Bf!j;.-NFZJ1u)V7w9cViNJe fa<"a(jU0۩ru,y0,u$b(~!"'伏{FTOC۲/~<.[1ںw[ mٿs)4{>s/~ }yp}LJ,'"ۣ2S()>AC0?~qyo7Sn~>^P""-dKKIaA̙3bY9YSj:m7_BpU LŰutJDV몼%Aǝ @N5gBa:-'=&'V.~S<>oV9_ʲ=I^59)Gz kZۖvIz) d?g϶6i F|H"h2mLTa:R,Meii #ÙAdnrQ@r$[Ox\Cɖ^OLiRk;l""փG}s4Z).R!pͫeY<= C'ypS 'ejɢC ͙IDg@Z5iY=~ke aޱJMՎ[ZؘD=vO uˣx?g?ɛùBK bA/7ڌ#T@hLXhG #U*uDɸHU[;e9ZE*ecbf=;jsZ ^]+FazN}AL󚒈"tf$J%!)L`F˛3x~(IIPP%b1B.RKM$.2ETJvJ* ĝ, .-TC܍-zxܽNs1>>tw) ]U[:R&K) jd(QbE4NAJID$1Ȩ ʈ(4q{D$R( (>{tc1 tMgaYVFJ=[e)H[XI15z2(k[4sFdZf32lS0-hXc{k'!o_۶N9<~~wy_'oludN\v ȻJs8cix(21r[Ưe9K{,:@B Q O$MгpPE,JfS6h$$$X< ,9T*gyXڻ{S04 MG!9{L6"EKSj҄43[B)B`fR!d!ߓ.M﬏vg}fw6O6pG)<#V'SEڰ\i@D6ԲV(2"sޒNxerL͌ڔ(B%|gҌqv{W\si!kb``@3~P)u-\)Տ+h$GPaAt }*%He2s>YPX5#G]<`<%Kwj^ԓ޾7*[=eD2t@}6P%I5#8=eH).!h!Ԫ),єA0"gB͝*%NTVI85ɡ *"41kWl(B31b1a%'^23e#)-T*ԬzLU_:Cpt03ts#.McfYANH4Ja#C0Χ{R!V,QCԚ.Ks3d. L U!{-H(4B6j'Ww`ޣ*a>e,3SPv"6EXbCWcбi߽QVxfNV3VRdiNِ8P/;έ0_o&*_7 d/G zy=vZ.^>Џ~t^2d[C/֩x{+A絿|s'ߖ/Qa7rRqtjuXܾ ɰVMߙybI徇 KHAY ^ŧˏ"QsiH疿 M4 6[RDSstI©hrKBP8>BaĖ#7JԌAmY|z:_93AIIQE%ʥi嬋@ |K~OɤH6N䓸F0ra׺YqRH~>qXc#onc!ӅP8" %ϧ: PMߚHX,\PR%lH])!u9%Rc;'PYKRLh0>w|_|#"@FQlֶ;g$-߾/߿}N=="1BfY;(dRf ::sG[=͍n^$~+50EiG Rжe޽߿ys9ڲZh^@T n>I䲵r9:Xo#$w DǐmL H"-Q$(C8DwOmABdTe-'U*/DTM)\S{ gXPpJ8=*c'/ s'!+eB~S0J "5R <>(1 VFh:f%`qf82 U ZZ] OL v $FʻO(TeD#q̚FK}Aմf0'XLiPR%)88 %q`V(#Gp1;њlI DE\)R>caLCU려CRt!2gevW.,)\43x20CW*c6agҦ~d @Č;ra"r idr^߽{xajMQKI6=C`8<h;hB&we$Oq&ZcBl@]rK4MRDy-5O*XZ*sё%_QP#ب{~,jƤ{8 PxjnA s}1. Ba=\*Dra̬gqe+瓤>ooR(JJqeFIÕeCM3)'6 iq͈`: D%Jɜ3P)~grXO}wXMf29%0Iْ9E( a9Yd)H`Z))1 ̒Hd2"raEj?~_n$wOɺ`Yٲ%=>=d 9h>yvs1\ZVPO!@qaAMܟ NMܽ:gIHRj$`Uň؈Q+XZ.uTrrdC"|x&*X,in@)nޟ?֜h3Y Y4ۑ!>(6kM,q\荗<ٮ<ȑuJ%iM0:xo +L#t\[+g>aδɃR]eʺ+ŞPHTwJDef $wxJ](WԳ`t~~ue.dj: ڈFZ Y"ċ Ҕq3f 3 )%J"gOs>>>mY`(1 iAr^I\YY+IvAAR-,e?ldFDeD %zSE> ""e]Zʈ:DFHؔ*Zݨ${sIID1wރ@WZN`&@$s&P(Lg+KH0f[6Z鷑e'FDN 0+,HJ.TAϰ" Q9I' JwV'"9[c؜ӴLzxfFk9$tZ0iM]֜6 3gѲ,pyBOL^*XRH($ux&f&D#rL_.}Q  Hm/B)c?'_|G\Z %ari^EmÖM{a R\)9l-k=;9is*TnZfy/ږ#>x1s$XX?ߦVsP.$/tX:{my˧SӽOylR~ Mw?/~ZA+Q%?% w|u$mDe[yMnJ$m Z%dA*tɲF]tDB>@ R(2n(89>g{ipAiAT.tL [l4&qDDS# 2I"3=%ֶT[cb<񐶴j}eb2k*ɨ2'c* $2K "#QH@.@Ӓ@"]'.=0}c^{ۄ !p;wȅ)jz8Fj2Z<͗%3s̘SBc97Dvz??f~r7CS$=+T{6s޶BNQ m9V}D"lWO9 Um}V{'s4jKE s!Rn';N(Ϗodz¼jvRcxBPRxC[R 1GD)#%#CDeA,pgys7U0%,YcD /&4xEjh:H#Hc@[9)R;QKAzKbƞde&˘Ëmi܏3SSGNìKC$.E ,1iFw%JAEZwZce CR؝hQu5F6fZ٧'9K :A"E@wwY<3݃ 8e!_}Nd*̱B=]9ZDLn#꺔0{Ǧ{1z n緥ܮV9eO=|IsߪTh lV~y9x<O\?K\}o͸HDzZfi?|Puч1W8_=~gZW<1HOoU؇)Kzm6 S˴7ײ}'}WmbY VU3 seDUDN*Y#dm#qXȏ,ZfI()30 &00a#c gP D DLT*\ .ʱ՛r:aPlk*2X,o4)E薅36O% pf3E3<%53Ry^1Ұ>R(b잉LlƈpQXgbTEtciV""@$`JH bpc.p$IKHВ *(c?cȲጻ2Nӳl&GHQES7cƘIP&Ygl߹*E y>-̉ڶЪ h~R߾pjOoEJᢌ} G <:eJZ2dS&$nR%R J EŸ"c+J'sYZ?idfta3<.Xmn֢`Ir͔m"Q2;P(;aSWϳLGݍnG0۽N3eQ-JP1SrnQ%kWVH `$rס)1*/"j1BkHNpag72)*)A씥gX7$K.Ox?CF.ܾNϽr*{Q>ۤJ8nf[Έ=Om2m}>pd/T#>t nRZVGMF}\vpY7(oKx1~[<*}|_n~ojrw12zx%>,޿]˞?m~>wM4o?<..߷ q5i{3iT?W׼zt?n'GBj|x\Qҟ3"mQʤpi jeu 2Sr3aay-o"ffkdYXQ䘴jN̘~߯_ti0ٍ4p^`MPz ^eK%ʲqϻeY̬mxo ߯:X);䪍22eAR>~ogzÕΣ;'eu7c]+= >8ʅk|z~2>wH|Ei;E},cxaٝގy-VOߖ-AkM$9ϳ.XW:kemHb_RJτҜLf@WzEU9ʅ)O b%!Bٺ S2aUx8?M\X*1Q *Pb&2@H1s.DCWie8B^8KR9%0[+"XDf|2j#uaL%v"zzߓ_d=>MZ8(2I3)5s Sgg)*ۂ4NgÅ &Y5==a{ӛx ;C"2PKZ]Wi?? RARZO!58CDi.ۃB=DFFv>^Rc$ڐkLHI*u. R8ߏ >ͭ!"x}ӛq^³S{r.i+.R\AIVʔ`N0@\$?!t_ ґ.Agu clqcY""TiqZ2VXS"!"z\Y`dPܙ(BIaNZ0'9s˛c"c #2˽Oq=H*+e) s~\PP&1VeDBz".0)"C;aw]+wv62 /\@ n!fV*G:&0HYNC~??@YKЫʤY" \ mNӪQq2(V,$a[0:pDG}|*~X%n"oGl}\Ov խ}W4_Ѝk}w)ۑ}j͉^_ǖ"u?]ᜫIn/$X5f-߼9>7.'_볗Kyq!I1s_wP|?qD%.%2ϿøLJM%}:'Y~\IB2\WAUbg*lB1AL><W}HۂcLYS3낅)xƺ!CJcv(%lk{VnڴH(("(BY$q ْ@/x Y-yIsd:|[VP84",[p"QcvgBʐ$&?Մ'\N,%El2[@L ޑDYJ{G=TW{O}Ǐ耇BɵΐhLe֗}q"-K+Mp &؁R={;.dcFKkuccrFf$3oy.ozK;Oo!BGvשl_i_|;:_x9^^{w=~ on^ߪ %Ȧ*EeJa9^wZ K^o,.u¢Z/+צTk YVՈ (Y,Ms[8<9 " 3LԪ@ o .pc %(Ja" !ٙ1ĐB3Gg bOݜ2)pRۭnIDTdѢ\W,%ChO`J$fQ W-59[4"DSds^sy6Ma9Qc*dNLaLsg -w=GCC?,~vymxޖ/~?~V}wG@e?eJ+ [J!W1=YI!E(=TҎ P{,`b_M(0ՅlWeC0WװP]zya}͚k 'p~AEi^k) IcR!* Cǖ3aO ,xcE/l-X/wa~)1Asd9%JnGZ4Ij*5H&XAMO@R8DK!Dd=DD3b̞tp=)$\r2婖#j&π2Ptز 9V+zN;J :JCڒvZ njL @ .DzqN-_ete!V .II%9e)NL"6,EURJ&!}~Rݭmp,ky SŒb=QwӒ1[Lج>/۶DRu^Jytz9//P|>Emʷ8Mڏf|鴌}O~m|Rv(O ~ 5~oF.Py`:XKtS?2_z8m AZڲpOa( ɺpi#'W8I;UyBù((i9PZ2b Geϐ(|4ά/uD>ԟ(kė96~yakq9{Mԓte wLZCưspI`s^ 7ۋb16#qL3z* )媖ɻ6h=gawZD%~o_v|b{vw Xzguٽoyn؍_sl_1\1zQpզטqIď_e9Ky<|c|TQң&Dq~H,$ MR"r]5(0CטP"`>Yvj<sAVN\뙊DxMKFKL .)i0k$kq@D pD仰Y?i!R==Ŷ#jRRXӲ, 2DP{p#bܓ$13H #y\ҍrG˾A*fM%CeTo;tx]~w Ÿ>NRj<Әֽ/ΓT߿ٞεXiś'=|__)$jv" ~8mofDކ72GU[Q\DљhPΘsi\r=P &`RŦnfpr~1?|7ox<_*K4Q"9Z[m4VhF+H EQ G+Ӡ D3JVD zH.){T䍗ŧ&a<`FDR=ےRaN衪Nbb()֥ +@˓# LQWG'&$B)B9G;NS*L^0ƴ=sXҘ>@ D*k!PFOcCАlpLB#aYVZ]&+a{KJ 2dDd=Ӈ2_xo[pхeZES 6E-xc5QESk_>ގ:(.3M-)d2[Oeqgbr g_,1V=G3fԥ\I,6qȷuz<߿O޽ys)k)-ΖFn#s%eYVZNBmyPQə&#ġI ӂd?gKDZsJaTj}&=Ӣ%/$Q`IX!r')+5Yzj\d.T$7 %DQY86N`]GFbQ5fϴ}ֱ9K{- b Ѫ2T)$nD%IҮN4pMZs:Q}&TT08sE]&=gUVjUdcc֠fC}; F/{Ǘ\hRtIӬrNVnQr1e}BÓ4Dy{ԓu#zy<,R!4.ZK XB [I$ E;-mu)Byy~vmz)K 2?6|y#޷~utx~A꼥%%!7OǖPqU{@MTZdmrmWy&e -lZ+F*ٓ-%ˣi§T/Q 6"@घQaE󙠙)DD™lAyM+&yeaJN2'+<(A,Dk`g?_~5a(KSjm % HL.$ c(r#Hvt=<էǶQ+G:2vWrUM챁  q4K"3 L-{Q0qR8RK<"dZx2mcfG\)4EF0dMҝURj`$Xmٝh@'C"{ NkVE`fܤMAu^wG+qM&y|G?[ ԦΣTfeV}LJZV./= ̘xz+fW,﷽^̷rWZN[LPOP/K.5[Nl-Ϳ],ڶsχsQNO}4Jp}M'[nvܰFH~qZBw:<JdPnso_Ňˊ3R.^[,琖F1) L"/ip;_}2 $ၐ)Esa/d{D/HuK$`dL*=BP D>"ۂpVG(AA@#dfpVE'}_ۉ j,4%eB7`V `]ȉEM<t\3Oq<'1;={q[7_Wn}mi+_߬"۲"ʵRYlwp=o[!VR8 VYI} ?&,O6ف,N(zNP}%"E+W; +#e%Tͥyxv}xRjY5$Gf8y&ԓV!mQT$9#P0q%!Uf)I  /a"MK`u Fh?B[tc.zn\lDh5D!^ "N{E@:nY\Y !Ƃy7[=^34.ZYM N$9"+n*'#BE¬s1Q)zT/B.d(+gZaL1ǡ !Ȅ*J}{NrN݇*Ӌ.O ViiGF<-J1FVx?E^ʦu㢢4WN^xXN?t"n51Hb!)µY?JRr(y ɺmMa"9+7G@ؠr]w6 IG7 2,>޼}=-4)eyMdaWDU1oKS.ԔύU'g$EL ")1,qHaw< _Fz؜ ɧG!L,t҄Bh*dȄ2S;fLbba$3V gH@9JD[hʼnrtZ4'şo/[޿m6n3I^ C$ǜú(nayd),&ѼVO6bף/odzuyg%orڶ04_NU7DMyOu/.\7R%afrq7gyŲ&=}! p':[!@!VNsT" 0o+m'nV( `\-6*@*ñq8hTI,Ip{B |X=RH1P9C@A>~3˹p=ho&Z OBR%@_rm%i/|[2fGs/ O&:`cRj#?sw7Cm4n~|cӥӅmj f"M&' UZ*1ןTYYp-nj)FhT;6L T&Rc$z8lHyl-E"{Զ7K {-v~ޖ(k$eOf bvZ/kշdWa:2fNBuB(]X3EЅÕρ 1Pqvy>o.OzQL:놗/> ]N*__4P:іwyzs)'f`j,~-L}c{4~$&q4~b^ޞ"\9_"PgEOT ʙM{fG%1KDb~'iitLq9]"m'/s&ɏsڤYEmw?/w<5Kߤٿ܄dG䯤CfbҳC*ké *Cڼ6tsNDA#nrKeY\ɣl0$B/L;|Acdg.dH@(G4J !&ЕErb!bʃַBChAjIOo,2^F Ir@,0s2bN Is}$Q`EK  U7槂LL&63I$-K,uMciPf@SdLY4"&$S#aiDVN_-oIZ>y|{k 8Kw )P>[ܨ颾H sy%c0o|u}R3S1n[~㺞CF?-ŷ<{%zeqc<Ute#ם-,uv,_}xjp4*Y{SgWzՎwI%)=>a{ب]~@Ot>ƼG <qϯHNuF=ҏoKہMns׸zW߹1ׇ[UxF?ۯ79^ۋ=F\W8z9Kx~U) Cqki19؆p^>*)Vq+{9=0iP!T,HT!#Jb<L$D0۬BA]4au{ YD\kAf0Aç™՝YLP?:u+l]/rfEIB灢$ĀGxTbOjq "cΞAdgwѯxO"W,Bdƭ-;~oҞ__=?~?{~ߜjMNZ8nLr@iF$3(PDXӬg?wvW+P@SL-pEӖw4,,c̰](i@w9'["J!OhIEr(rV_}}}m #[ ="5,@NZ`lurżqBacF [W3$BX9/C`!1jr2;\2!&EdcUˌ V6F\9 5aMiRxA`т{zQ!~d8k@2d췙: nCk@41k+e]`޹5DQ."deEfzN ύ0Xfx)(ZdT3s-aY4=TY a̴2~{}7GorF)#sވ)#)5mލ:OJ˨A|ԼoMnO 8t=u1bZu߯^/7O(e>Qz7R. n-&ֱT>7XOɓ\NLO<\8R_L1n^s[>8O|ft}3?$tc+^(nYBtDž9"H۫./o'V|=ey'S9,E:G$r`vs u"!.S)@ J4)g~ٍq0!2fR^]yVnq//g;?ΥxF <=QDiv{* hau?9iVHA-+sDuiP>^EgDqt6]Xi xraEHD( !scrѥ,,"D(9 7&'W.֓V$G(0UCx%*^?LTn$U!u" ^B;Ⱦ%{=[jIVUs/R"G-)8E5 Ԙshrp _*bC%=b#5MR:E7kŻϤWKߏA~4]H\),EbjD."TŒҪ.y,eeaX+`US c0Q)PF* Eaz[f5JK}#}N{}=Ow/;~ȑ^O+آ,*yLBbn0Ea|Ngs/:svL]9ōc9z)rn=}#՞_g-*UU.q[C7<>ǭ_M.6o]>TR9V ~Hc$\~jg/*?Gu+9n~>I7_̞`QCMEo!$N¶TvkaO|Je?cjwz)L&F*Ez_7Iw%$ ;J"w*AHgYOLIg@Z X8e )?HB^@x$*Ui}||>ke\9eV2P{ 8]H;u81LqY$?Rkuًz,R7?K~?JM]o_O~w˻eђ/L O"IA#L"DS@o=, [,&qpu%hPmEF N&ξHn, e᭲@$`,*3RX#rB鴜O߽yPZOٟ])jæÏiB$cFOY"y+Is°Q$*;d &kENҽk-jD3mJjR,4{fνX@KNGW xS,/놘qMTK? ӊ^}zޞNK_ N+uўW>RO[<ӶvMNg8{o孮KIz ^'~,DׂXj?=X3/__R^ _/ﺵ&wz/bnx`4K=qe ̗G,gޭloПs49O -_e-hGem~e+˒2ˠyuYDԄV\ ҤNLɒ*̩2B%"J ,0I0_^">ITQD)LS7 5 rl'rY4YzWPgbFMRfHh΁GՓLE(/ .y<I=4q1"qGՋJTfvua X23$&Q#Jyx W3_k KG4bFdk3r1WuL/6{YwV?(qWVOKowv?Rp ) 3+ ۾U&pY YF?MQ8hDOR|n'V\S eGzܓTYpi'1ں\W,kyt2aW|b:[|·|xl檸ycyM}+s&{Si___N~vkcn8ϨP֑탏oWz?nA7)cƹW[ȋ_{طӦRf[ gZ-<ReLlDH;I{ISPI"Y;ŴsN$(''Kw #B32<9Ogp)BIpb„I\A! J$3xyf9l_}|ç/v>X' ׶l{YCm'b{///~~7?V$`H 0YD:#)b2b`"L"H˜A?@b)Z0l6ĵ~ܜ`AD$`Z}Ҧ,-6; ƞ)Fq_(I Ic77_}O|^צB *Ԣa)(|P@y!'aZuFdiw*S#E.NU1f=y-sHQuU3H!P9~s+z+pYZhQbJ*D+iVR삍.$/HtdM8z yFw7riTRϕEO ~7]T"ಘi3TFzR%({pC͝YR*Jv`8 @BBYVty"~T'XCZݿ|wd ef5$!k,q\g,4cNoowo?ӷ#/M6H:Q9S܏}QE }ayiyzx_)xu ڶ' ayuԕzwY}lX5>ZanmyQ{i|Q-gBG5ie\i/%;Uފib\ =Р9yVNʴVNl2dk9:>6YΥĂi_.Wj5{> )9Tb^APݥR0C)4#9D$$V)YND04: Eeſq!MALISpܩSLR!iEt>d%Se&$fΕ﴾ DEf&ٌU2ؒ–IL=H7HJ4'=+XQvmO"&!D XmBLR)cY>0M))Ě6|d0s Eȓ,m-"#pC9r3w Rǀ9z>vyqK/e9C!iJifzʮ_/χǷm.gy'Zr)3̟g˅*~9%5'\+k޾-o._u\^~ֿ+!#&k>_KoGfwҾΥz|Md]?/Oe)v1x$ $w39gAѥā 4K&%/ !(*yZi$4zbscolS9ljEP\B($?6e0r"x0…Uh8l9tZզDIɠ&q)b’9Ԡʅ,ԏvp?#xj.A&YRr⼜gΪq{5>g&Ûwo\VْNf0ܦ&ZĝQrCLJTr3d0(<]i"@a,ZH-hpߛgÜ$+"S`,1`t"J4\V&5&yl$sR-…SJrN 0(U'Gqh.N=Pᕑ121γav#&"B=keMJNX[-$>TcIJȘsdAfuDkϲD$d^#('qjQdlE("&[8h[C"=;Q?w[^dknӵYxu7왯ëW˥X~>mwB"Wxk3/ |MQסUlzl ߥ-oQtNʔ͛SQbz\_r|ӛOߵUީ]?6r8c|o?ҏ_^M;}4?y9yxnjpumy]uNkۖEdz )rqvA \S\ >}_R ~symӸxUU]x %%)8Yu+ኴ$"$1Dn&#e g.cŤU3gY:!2م,UA9=C:fDu~t9~?wx4/Go~[eʖ^#nV >\J)9d!** 1~׀JZ2*"YZhdy[&T3B~oz& ɜ´I=2oNB_}oޜOm1Nn.2J; w,d "S7-sOd\'H"3Ik l˜9uqd@沵.Z3QT5@+!!D51ĦfLZWKĪ"!Ă'lG Q<#JFJI؉Q"J׀R9ÇݵY8|n$#tBD$ƬP.£-ZO2JƦB(|)[:+yr,vQxr lo_}i7}½O;2%D3ңE)Bn۲.9Yޏ$ #ޤ<a99ݿ̫1[,g~uX쉷xcPkvC0nƕW]}纞~t%\L%- z;`ޝ{'O_]}RǺ%lwӵ߁<<wuiB >KݥrN7çy3Y~7u}mOdv:T7K/Z#ldPFcX5&i8D$Q=y!ME895F®NDϟerM^JPQ(Ax&t)R%oCE#ɕ %!4-݆o~R!.d%=1Ӽ$gH)J=](ӭ_{ǎܯ!xsO=v:b_u?KD4u~-ee)i 2DANL9Ge 7 3&1;20o}_I0 LU*kTf4`> ܿ F9I%@g #EZ+r%EX@h d] N 3=xbGZRjM-k -31(93&,GTb$$pPU VyP2M=ß񇖵CY-2­~;-ח,nY;76@f{Vc6̷(Vx=(?jns̗e䓅:ŗ_}vC-2;{| }}62"Ss'hvf?+~NW\/IFFdLI9(&h'wgR Fr[.bU&@sߟ-b. bP';՚thBƉI)AQݘ p$(Q֕3W[y['Y<ӘY982w(3$lL{yQ.g;7Ç7緵rƏ9tzCJY*RL270pHO!SM dݫHSpdFaABHVLrCw?/v*].`"sÙ[p zfĔ Qz$u;#BW sG'pzܞ~هeRȴ4 ]Y ALbHK0E2q "jyA콬BU" 3%=p ۗE3rͶw?J27Bi4Ԑ&S2^U6-LVR*MrΜ3RbQq1Kܽ=;<,(H%aQ k$\#ĂPF2Pr<!6D@qlXy%* &)Uʤ䬪pYq|PJ|3m>D|qF힔U yL"[i;+{9gzqaz^W~-Nqu}f}8=Yb/x:ARHZt:⮀~; z{OeV|8}s9B|N9n( xߥ)?{OwG?-oFHFw}l/ˬ"qYZk[I% =4oPSNEW?qi!ehͽ>:t͉%eˀb Br[bNE"D=#X@D)MIʑdޟ [~S{dVIY)ј"& !=BH'Ag P0 R ~tU)$Jf$Rd¥|!Y05nuͻO~'?-uWpnqzهuESYEqdhf' 'fiDܓȘXEBIT9(L!?W$"xj  c} .p#ie:b,3 EbT T22=BO}_b]ˑ'#`vU˝ZFRl˜?XIq# Ϭ )GA%U€/]*5 _IdR0y&:T9jIDSc̅S)JV)הWbeit({jA['lABEIBjC֯^s]:;fRN DQB GI1y`!F+bֆ $Ha*gK?/~1MIMñ۩>p$'~ٳ08?pA3n&9W҄+w eVnw$KuOL{/w%Oeomm[;t|L?~ۧz_wڸ.mFdȐ@8,<tv"E"! b &a 11()ٱZ?(*,4s$yӠ4CኚK`63GJaюpC`NO t,~X~Շo<6=[R4#Dh&%9XBDz8dniI)\Wi3OhG/#rQ;\_xѲߺSֽz貝|>:ZJ} m keewS^9IpcmDvi?ė{ mËP/WTVvz'9~G\6edI&Q0y,o6YSr mlN܇w-V_f*Wc 8Icjg{pPg.2{Ƞxw)R5ic\+H *q)ƹ2SfG\xAdID$IFVXWj6 Qç;OPϔnUa58)YHXҁ($A$B”@"9Ӓ؃2&JW9<8yj;J JY ._S2bMw}N;t˵oϯ?r+9Ƶ7qi@4FPR#{ Q9% NĜ"(#2U 6&I) "d$B ĿK)%@[u&RPDo<@h usbcTq,cXk*pJva>ĵL_c[ڼJG.<>sZZKG)%5HfPMLD:d711a0?iL!G ݪi\ jPMdl^"2)3IIjfm(-e]xsY䶈OlprJz.nH*Yڏ!|N[o)t:T *3)홏}Ssm=Rt㖷RWbaz)"=[A6T%%|>w<5wc~Mˢ߽=jDRggoy'wG(]-mS}x-7"^h#Z67!+X0r|;Sqhtu`Ioos,~s!IWE&jmVqiT 3_r`ns~>!-;\R2(hMq e(z|x?w[˦Iw)aa\JRI 尔n$*cn A$pd385FbIFLj_щI&EyYI~ZM.2_.I- / 9߷?mT9~۸w~/Q~ڤuΔd<%FIHifHtM昙 Tf($BP1kQ HRa ht&ἬSNh ALJnGldU # NZۡLs2Tɣ#bRЈ^݄xS~sZǯa=MuZFF&-qV'Lj~˶L,b §;r R⧿ק} ~9߮.:e[nf0T3I*u#)ܯZ/S 'G)WrfA{q鐂4W>.69G6Co]W75}D,orq}zΧu/a%j_Vuu:Q/oI7;/g^k|̺li*/R/y9a>=}c״FoBF܉/Ɓ^>Z{SwiNԒj8-i$GYkH%&8*fiNBeRF:qup'4eӛ"KJC`(3.ɢl^ƞBݳ Ԥ=2D ;-`0$s@ \WS.2IGY^^zO"ԧlj%-恑1J~Qݲk{x}=n/s̱e1.oө3"Y3sHxpwMT% X@ HRHe`J)?OHD2)ZSk,;p"1;wc3̙u_uHG@/rx,%ݖ޾S6YJAmDsyۚ\C薅=i\WSaSc`O]A#4K%R"!|L0#0&p_ߓ=Q&EQ2<tX Pp`1euFOR`HB؊("C[=7d>Һ(ɉd{6dDY9^@ȵEϘ>#&0fGO&) +Rr*0gQ$#S9FҐn2e?DmF[ qe]R]T+Ge˃'"[Lܖ'/oܿϟDϳ6ic۱HM7C%yLT)|XOmq /|wMcqk-kra\XlUR]by8f-}]g|mn^quYkufVrܲL~UuzKbq\ er{|eǏʏߐ{'!ƤDžs&D>SS mЍT‹Z&L)&Ƥ ̂Ex}u97 ;J1Y"m6i$b@ BRԅNrN\VK%recX '%d.ru$4er6(-'Ug81'K88'ZEQAYYgO ?>n Ik47mR>R][l{y[6D2ˌѸ5Yu`֪b:z坘NeүӭHf{>[N{1-`y`xZ'_=G[߮?+i85;,jX|7.__ǭ_zhbL_>tJOETL3Y94~y7˺|)eN 꿡u.=kP|!'W'B Ą2$&WJ6o/u<''orפ2+- %#bHt09 H"K7iC rAaxfRK9h["A$$oJ!@%Xc⎠s(1?T9V^jG=[6S՗Þ]ۇr޸$2"G0B~0yMKB<bDQ2bIdfHz2Pr_w6A*+3-%,弔I ZgQFzco3*-ੵ2>T}V.S]Ii ?;}7|,Zx8 S:\ID"XOR\3ℬjNᤕ) 0$Ȭ S!F9tCSf_;nwF*ln. gc؝̂aX*cTtuRժ dT`1B]w0l 19 I3 pO/W'"3ipqĜڻŌA$$UevU LeM՘'2xSXși6S84R}Gi=4(BT,v1coobR%ǧWjtA\UKل1@VFzYϛpx=0X7emDU^~v:j{etyGQwvACJ{~̞"&/oO?z[0,6z9q#BD ay9}}==μjSm__{9V[ox7j]vhI~˹\'"Sr/Ǡ5 ՟VAװ{&8|՟R@+=$N83{F(-{ZcH^UB`d!$9#ǧeoBq}{]*a~q-Jq?OR&II9ept  dx!0(DI |0*adn)%u%yjт|o]~FoX._c//~ߟ>&ѷ?!C;g]kmS$X 2'n=SLA1 $12"LJ fF:%L f *3-$BElL_}rkK[y7;@ÁZjƱ, "(0O'ipeeac* ae;s !.2\ӻTWUJq7AbbR; 3o5˒N$pmpLJE7_X EUI,l3oõ^nǟ.1vq/]gѯxn 7_!\}l_)GP” ~7Yxw嚆7%6h^n8'֟Šxyl+},U[{o֌~;~x*|X_.7wv7ܾc H+/svof_ sлSkkwGྵUԇLѺ,@ko5Vc~ &.3E8ٯw=W=8﷝R[-bo#tAg,0B8-'5D]RQNvsy1XwJ!>AA jvQ- nKF')%=0۸M!)!(֟VNm}y{?xW?<˗/W[Y W;%"L13%D \"lydV ⒔ f "Jx&"e%N-t4^>_@(Pr0Úp4dJS;xC2Z3GX"&$iB$\ bٴXȲz>xx~̵5Hʈ gψN, p$P7 ⤙xLd({艙ԝ v\dK3$Mt w]3\]*OWZdVA :e!4!UxLs]+M8U*Pp29*XW!&nW($L!kx"+B4 A}^{2]+):X鞕 ),S[ yLsFmXJ+։)T5 ٸiֈL$4Q)ͮ'O~>UwGk׻u:]*`Ld^u[7ݰ-l*s._MQd:S=_prAczoZת,~0R}\oJyzC9'=Rjw#pRP9&3| h㯯 tE%AC ~T>%T.8Fs,]{M?S1\n_BStIOl{8)Ex[tm}~v簑9&V'NY$R;3%汰@V%u#N SH:2,(*fT2F8&@aLIb}}#@׿Gkgms\n}K'PkK9& L M$!2b'$E(!ܝ bp"PH"e$f%@dcXo)$5 B,?X~sjs>Kd~(ǯW_j}FE~m}}R}]e<q2[vϏ5qe9I-G *\$ުȞbjZnP$ w>]]گ vNgC ^7`--'eŏܿ};z&߼#\7u+:~em\ۨo+q:s]DͥJQH]SUla̠#snE(DPKi;R#EsʿY+ϭ޴Y}4>zϟ l_jOxyY!Ki U2J7KTPaa$N W!r&&%JF8!L{xœ H&x$_?{q&)Z%ǐ FtbfhC; [=]U8M=υ8qе(ӹ=Nͤzw%07W9F[.&ʼQ y~x^x}n8QvK-ywx߾>\q Zx,cJT(WZ? ?niÞ_~R/׾(Z=#;Ӷz9j)|m+QЊ)%Wlܵ׷9 .ZJƸCjo5J QskC1t=g>l-3ڶL{ԳH GF7<"}!2?DIDjCpr'0,Z]ɚ~1uN(L~zRyϿ~ze[/^V^w[9U-J0!s(1AɓFrarD0Tf&@L9# &fOL'1"Y@B L*?W6BA kl 1!u]ˢe v6J&C\(pzRAL>5f'y<vޞ.mY H"qvYƙL^/BAFUiÝR:jDDK8 "A 2\8Q)daP U=} rS|$ewAe#*@R"M"BRQ="gSɢKv\Br#(pcK cs"t%'Wqvuq1ąsl_oKDYԉn>R19 a%҇B{j>IRyXJ҇+ьN 7,)g&?zmĘq[~'TSU}p'3l5͓UZE lVV=[ڈÏVu]:`7\]4/󞠾}R͛K "GQW܍N!iza6;T-gӃ_^Q_^Njr;W(҃N’$9)?qb8U )?/Ǚ񡜾C{"_7~|G1m>~].½YQR>T-ҋ2vz<:<GܞqN^V+KS5EO69Ci)+3OXwmvfr.i7k.vSIp΅$&wd9`cLm p0QdJm#"RRFB< N䮒,2SUhg_~qW_-ߞ^=x~Q0n[LnN*:eq=Gj'Qz6̆ u]*1~ 3N a=X)&3Ŕa%Qx *sG积G /ӵQjB.KMfTnBL8?IT9pa C 2 &G-4i5G8xaӪ.>sZTkT>@!uI܊FPNx 1ڤx2;E.N[RF9AKCJO*1riﲼ{'7k6y}01,g.5g*eA569V~ 5 ֜ i&'^yg!zOMYNj3~8v }2g軷>gJlmy\,l')'wo_W2?tJ3JK|zW.,1 ϑ/,Tww}/O%@[~~R>poT^U}&|>RK'I/=<qyq`R\%标!5adZi۞+W+r?sFR Km“Dag٫Edc{/@B 1,`$Ldk(s2%wE6/+gcNh̊TΉٿT^(u쵿|uy\*: Od9J v˙abFfzPR&qDftP' M+ qb2yF+=!,BȘ&qd8SJ)OdD$bvF`b!1sYZCt{`x ę{ВUWjSIŝ$UiIu۩||5Z><|REU.F LLf 9uzjM3h*ȉdllCXcl+>=ʝOUWN)sgvܯiTz\(?t.ȷ|GmhZVIs&;vw}z*y)*5^+/}Y>_=z3u>|4;E|ks}[71w'[տvh'-<>zfL"5D:8ڔ8h^JPʽH۩lYFZ0d=k Hh[~(98d%MV {a'\Zd+ݦt)g' u$C˥k c[/q7~rh-q|tZx$R:c0jn,`K"I0N$Hve6s'S=@Lc݂4hB `$" % J¡<#3hGƽ(ؔ4 >,&de٬ٙy1h$KtK)2<{=-sZ 71J+|>Y]ڐ$qMHu0͞eI4w $ 9lJN8U"Ի0ʈ(@& TPf'#UrMHЖ iCկ'vdh*w?,K-p2jy nLk.fsA,Zz&u]qp[1 ' Gܘ0I*i;x&~ihژìM瘚GGL V B:B)vZE`wdSbHmA"ۅ/P=)lN9<"\uY)(}lؿ{ao?kzoﯧS9:_Pmrcv~[zcƲ"I69[AYW歏Cor*8/ioimvw)zÛF}W.fs_E=} ǭR$߽`xNebS+vy>e,uP.r ,y;Agv ߮ǎN ~'-tX=O'/wA.vlo%N>dGͩ(.&3b }>L ۮp]tۗI]MJDQ$wʞI"[4f\wnAk#4,ء1y6=5L[ɜy$z&t%"`!U1;Q$QBgӜLh2IpR~x[-E:pGھҋJ9e-ZۉR*TB"IwdNɐ%$f&9pIpP$湛T$P"MLp&aaN7;-, J 3As"*)s)'n/B?Ւ>O9b,Јd,Jى 8fH;v'n!4AQvn'ܼ, ǯ57O Ers޾U>QDڲN%7dr -SCsrj%i ֌N$*a!% *8eQ#I"vP"b"e:Wn(8wO> 'RH9 a'$QB:'V?J@!ħUBxARgJ R_dOé[%m{`Vjb`4s8!ɕJek\ DDbP.JfYk=& tBn KO3u"iJHD-\Ls!yC^4>ǯ}ϟ;])N')sveVx&l2xj3+OtE9__g.QZױu{<DL QѵTY7?|.k=WE!B#ߣ]į-~+ث9I2e2zEZ?寤|Ў?1J~/3=OMf~M.0[%W dȼ-t\NTIGb! \DTΡ~3-e&?hus(aȝjwKUf9;ƽW{w$xtή?ܿ.TkPPv\8#gfz8xzXLGᴒ! A 8 '‚ *01:KR*)G6'r?&'1S !$;4sGoR@6&,2$DJ"G ]eF!Zn.>}(z859קr9zOK5IsA0)4!'6Rbn„{5kW()+$;PR& &Wsepʅ1~A|iF,BNy4kջDq I&J"^ MeU3J%E}6hdȏC5 6֥{U) ;>̩4 &)Qp-qE{ȢBXz#q&⒎cj1A4NIXP~BcoO+)gi}7}V(!Z/Kp9zZT=\ᱱ(ێFO)Vj?N=߼97^7;Ƨbsz7v[UO{yXrngiF>j^nsVnR)NP"MO!Sm_`_I_o6' };d8}иs0F{!K}oˮ![k,)IW]~v9\>2|?D'.suZ=:SREB2)9rDGFHf=&ԅBHxq1# ;s+%A5 I"$s˗WK!Q.3 1=¢EK(ʼn q"JâHMV)R.iFHEӉ'S{@Vd^2f_{:R cOQ0TlPwYE7O~r4nBua*(D"Bu杭'bJ^9GF%q1!a$:ҡ%M%kRbIe2 !dR4MHBA^UDTG"~?|@몙R}v(EHP9P9DJ>1-|}t?ۇ1>gIo}x'ʔt9{h6wtcoe[^1˲Te^7>v9_޶ ѱTD0UThWϿE{,Z˝d ۣLR\J< U\?Ч4*CƳ(pt>xS.Esm+gm|ܗuϻ$?3(܋\ۚ{MIV/ڿ .YfF_eU=YɈF8(N.R3OhK>^\SgkǶ1} @jn,'z~67S_? ~,?K}TbEXH'9QS(̎Y38"09KRJ  qbdBg+?ps$%.@bWՅ=K/n;4+հ=-ɑ٨ tfvJ өe8݃s˺{'ӻ浖H=sy-\.TK) $tG`~L8 .DReEdA #) @ jrkR9F 81,)$^bvK~'r(ڜe .&'hME1ZpJ[WQ%1Q'ϜUWCƔ' LLi,~dPƾJG>~=LI2+h VF@$&`J $jhJL#X ba'½66cUT5/M=L߼;?]j9cmB$1>C2S*$oܭ.7m OS3ӞM9>^)^k}Іěw;϶}nBmw7%(ѱ#]_˛*z @Q+맨B7D7o_K a2gI DkEu] Q%QMMtʲM_ Ck! F:MtV,r+)XATJ$0Dٲ5‰%?z|UY9ًX&֗ekI eJBٙa hyN8i$@LB5# s Q@”Lc)_חiw@#GhV+u7c:T GQp| >k_]MH0q,*"LU tz!B sHgRaL !>/}x>>=<.]PK@S2gNce.܄=*)[:'$& (epp`ϬA$tF6L'nFf0!g`"Ct)E"=WwsB+grwNLekAg[2O"jc@}eQ8ƥDvY6 ٕ ջ҉L/%% EҙdR10V a[X}q-G"jPQZJU*9]5#A1f YL%94TcLa6pIɀI ډ*(JdVSUSDTRD:pHZug=A/Q|b_}wSN_gL~y씅+@V~Vbᴜ@}2#O|yws1~K ]5ˣN"[)^1Qm]:9Ҟasy\B.x0e';ǐ㾖b}+9+^KTTknt#Zh,o2CqMQN,z>_;%7t&Tأ2 lRs'E%KA-;ف  \3)}bWC^~26=gᛇ )ϟ|&yO]]KNHѦ%qZ"1CSHI:4 w⤨H,_BTa Re$@06p!f>^܇͝"Ds1#}KݨR!vy`$ޮKE"c.%bDaD2ڇÛOA /@@saw.ͅsp .X {@X(-3 SdFvE@@eL" /DPRÀdݽ_;nR$㎲q șuIFI I"i-2n^Y@FegRT‹0mUvbmN&!H 6,am,E6)kZZawGqUslV(1C uVԂC= i`ݑd# t~W/IG܏~uߴύW 7֚ףǒ5k?rY3^[D#q21p|πt&x7*D(%\|>=N⍃%}K^IǗ*Yʊ=uakB3)It5c,q n'=cYyUƔ~vj[qG-rھB_K}~9:ih=KVߺV^$ZU^I;}eQb2@u]3K uxO\cY($Ji{9#&QDݝ+'\P \W,edT*N~[.Os"7p\<rì}V|lo>N"ڻʇ}4aB+YZs:E8,n$>),)$œ5#pK*dSj0I(@ ԔLsڞ?'+Ƥ (DMO"ۼ *1AuVpܳ_r~f<:rpm5#ô9 xPZ8iv^(T.qy=۟}k5gɜ3f9 MQBai1)2 VqBsN"U-s3` fIN)=!PiX#e(0: qPNM(>9;Pit`>3a$CW1(aRN*UJЅ%m49ӓIIЕ9s0P'`0^19J>ƘT1TcizGx@YԦ4%ORgf(I!b.J$L]M2%t$ZdӖGt1SQ<1Q:t?c?4\fS?_v=۷_x+'нscc Rhʠg.O벂iޮ{-S*qWKaQD /(Ug򅽇T >eucdTXŮi#r%;l^\9$,\hO/y b;_,l|:-oޭ"{[UƌH{P wqKHӃ $)$ ,%1%3)=AR@Z`xHR@TeAu|d#r> YPR__QQh{Bf6D/^tH^+ײر#Dc$$xqO5^TzRw'.r~ᩔ $yq'i, fQD؝S>W΂<8pt5uc h-9%Rl®1" U""pqe)=1AWOU#:ҫ(%3bV, -9)'%K*9,"bL1'+ ?ixy3?_|?yDzHYIj2]?ăHo[Yȩ]ɄYܕҌ!ҍW60gKoe徇etN_qtgt>?|~t#zyζv؋.[yڛWR[wǗz/;TjxiJNLL=g؜9-N !41Q{&H8, bD陒 Bhcq/0'iF+2|*_=bėGi1z>E}Buۯҏ lk%KU~1O*t>kJ1ݍRST/ri,t9w6’M/6ݎy.P>cDt6gb*j fse@BRfgJT `v`aJtp3؃7~eÈB0f`!Kd$s d1=ˉ3!"%ٴ hBT2̳N^jshqBsZ1wK7.ل#gYJsx 1JIHq@QJd& p*j]n,DJUb"čFF(Aqj7D #u'y4Ll~d??nc# W˲Z|8nq8,q9yr~~ЖlQBU("}G{-k-LJFT7F`&|>Eqp>_\Iac,7/#>׃\Q`噹 y¼K>1%2ND局Tu7z(>cG.wWM,_6A\({Gڻ?hS$**]ln>ٮ'_&vEi^ ^ZQy"@  ib!:xhQBhxx,8f'9`n ӑ5l8ʕ^w߷KY:ooò:>_\YJ Ҳ-ĭgR %4=QvA0rr+L"0K ;B4V"HY/= 7i$[} yz[ח?d=eEzQϟ4h=30W}y OmqGTMvvΜ ڭ☌h¤NM.`e9koo,g" BBBC ®+MT",ERjf&1HFt/3 \20 ;@ 7fb*#CB­r: q:כrO0Ɔ:YO.wKfYEVKKXpD!V)g/$)#fsBU}D#1i>ᘇIM%Z :I% E0ID⴨צ=-a=ZWg$ g3V6 `BR3T0kوc$ 9fMo%[~r8-cjoޮ/yO[^:wzg2_\#9s$GՎc9۹Ηc^ˍͶW:R"/x ewc2TȓRpRލK]=.+934K@h E3<>f~kFo?>լnwƃ ^p@Y8ڻE*ĤCd-ȷW2&vGi.W̼c%6._2u{<|K7C.e6ۤQH"-'֚@7XwTOHgD$UK$- VB( Y™̤Iœ*ZM N{]7^c: mOfeSyx,?o\uYԵs'lWoͽql*)A~f$7 _ vhH9<9mJ!urɵ2݇k(g>?>mOq[=V] n9~pfJHY2PB S>Z,8 I@}0r3&rMԆw:)eS")9 -Dw<_;cfOQkPNøQRh&3 6A~Pi̙ݰ. OT[-(p+)|TMBE) \:T)5+s2rRG8@%gn>ı{\yN䀜 (WIp7Q%#<~@r$!> IQBL*݋a0& DH*wqL`ePf"m{{WywɆ=.R￲x>?ST^c2<2~6}a.E|p#rWv{8s)1q(LiGE޿ALS*׉wxXJb4<59ݟ_w$×ad鵢-ZlIѧb1..um ^mGYBw<ە.{9.Z]v>o+v/Lz(N9 q#<Ԙy氇&[6G<o_> ~FўJ 4cɳrhsۯSGdr\ެk͕`]h--IW\jڃ~ufPsd]h{߈-DIe^yan- zS^K:'[۝K9ٞɧ}x|FޓM7M*;5A(kOTAeZ"IpUaF@ڈIT0?8̨E ьL S>]5PBy];|:i99UPn篾_>Pc$^;M`X-@CϏ2 ܺ9*5 ,eCѠxY VCw1"raBia"E sJJ<R@;8GRR`7*ێ! arlw+NJƮp$st~߃,9ewHD'83n6%OWwh!7)h)`̐{rβt4 @rC`J9  H{sΠǤ6: U:! FpKR-("#|$V&7Eq!2cȥ>cd 3UOSLE&<Gh0!wJN;0 {}7.rnͷP.ˏa{Ctm󥇴s6!پ>>:uD0XE evJb$_T=?cnvY|,㶬︤84Is4!cӓ|Bɟ߿ة=Aѕhg/ âi4(_YP!#fJyrɜ%h:(7s4yí#"+t+d.Wd-ns9UNBڴ"dW'^/e 3#mOLQCA˪f (!s'#bwbe!^̧xU5>zA[@D8xU*1z&þNj.Jz۟J\s9oNV2gZZTБA'ջx{VL7TRd"i) Q'4d#ωX$R7P5DO&W!N1 }.)t9D"#UN:Pݖ?cQ&)\e)/pbqz켜ie?or^ZieuF' Q^QNajg*O?ʧlc<S>jvoe)L(BOo3ꚨ8G2?g=kXv={~ :>XG-4HOmw^loOEW"Mic|~KcTXe'ٻ/cYXSKZ<3_9gm B5o/[Pe;KCz#t1]o{ADr<ߗ|yXs|9.rՃȗVt/1b.\D0\$Q81gnUOa!8tu(pJ̩j+JxNoN텤JF1@A t"n.Z D$id<4@@bTZj!!!xdI,욙 X8)BhĝL?e3p0\bPe1r6݃PIiP;X&ͤ&06"UIʺ1{S*RLY0IOL: H*rz`P$b̚N9vO>sf}2O0MhE B,PI"bضIn{p.E,3=b&idHE5uKrv{4_2|?㏨*z_N?toxƯxޟ~VN/y{u_7FoX΍N$Hz<\uY_m,T)+).֕Ip _|*MBFrɉ"|B3Fa}$tP.6w⌱J{t ݺzYzs=Gi,,#qWWcSρ/փ?sq!d|\h\\drSJ'ӂȂ7QRէO{-5Bv̖з5mDrǵ̊;}dީ^?m{L&p#|ه-"b0@I̱SBF=,kWoQәybv7OW} "5B"983A9 f\.U`NI(Q&1V`H RɈ GQD01Ud0k0"bLP=@R%ym޲|~oOuӅڲ{{D8d&Mo׫Pׂv1$q& D(5~ܲsq{7CPV4- KۇCԖ&Y閎M%HfN)d TYf$qLgg:)29YZ@P&z 5 4Pbў/]䌻Kqbw;erD&AHNLe5kW\Cr֥YE*@ R&:XV##?{& ML"2fp*}3Mm܍n#"QĄ*3#)SeqP[H"w'FA(X%ɼ޳xYcPrRQQg0"XK>eRРdˬ@JY4O$Eߧeexxw%z wϧ$moNoD?p"c/emqib8^֐Dht^{*my:h_W?^ߝNGvf}3+ex7X)'۹׼}~Z!63pqtzRʻǍYF_^tؓ\p+KZ|ۈA' ;Lvȶ=R" 7_;n9mt\~t.0}-?BZ΂g+8ߓ4~=LIoExͬVL9d}Vr3ɍr=!?M4_ι#RTU˲mˉ@䳱N<~CoMC=?_Y(P&4dx&őxJ3E8M* Z#>ٶڐRB\7$Lw$< =gX#a/7zALs*aQY**[_3UJ1ų\̩qP62K!ɞ&j#Dbv*iJ ݩ1L3)t+`'p$Mr&#߱3˯.NgMk|}:RrAkY1|tUT,gW[bDv{wr'P6 tҼ hkM˨^uߟ_oߝ n<]ж1fn :,[[Ce]Ex<;wSK{\9c~^0NǕ$y{D[1ntY7yƫt1kЃsR}T;^jV3{?G?lo_<}LJlv3=Iu[9ijۜ td|,hݺϯ6w]=h䘃wS=en,;.QznS9h<㘁mo~o'+CsYDYt~"%.`|NR@p!2(ʓAt `(#Dũ GZHb0'8BD;@98"\sn69ߒ"Z @ -]5qG(h@`T0@\ӄ\3*PsPH<J@=ihAp..4@ǔS#2#{6_~!"q[``=T4TȦ3IZ[cn&ֺŝ]X]@fBq@tzXN/jf*No4̈́"သ.?;b'$gD4~s=y|a8^>~lVfrSnoZB #cee4JY3xEeSZIE0笍vr yP/o+l^q?'61RO ǥ{mxNK^Mi<߽.Ǭk6ףbilAc̞[8OˈïS8|s9ݏ븚s6H%)'PD09giVSYĝI eDfs&D2xFp xR$"WwE+^\ޒ?loҀ1̞_n›7oK~xm*uoeHNJ))#lI9\lZ IqTݮKö}^w\$Ò$G$ZX[ɲJLKH8uLgEWTNhUr@1Ib\ Љ x JəTC48؝H2S2S5c r7"\!L_RzRQG16}CQRFd&)AY6FV ƍ/)pj I7fm䓋3='\H׆%lGػGӽ1[mN/ nLLZ!I#(89e^KT!XK#>(9% LdrF.G^!ݳ2]ar.?쏶OYz6yc4)fO/ůݻ; ?|5ǗWJ6ۗ{!D`/CZY?|(r>NCYvD/KtZy:b۶{XEէG~wj/ػxH\=~}|6\d=(Rε=ggKn<&J_V/Y| |Wgfz*vC8|aԷt~ N7sih|8'/GǛ9Ç/<\~ zRae6֙+NֲH?seY vȢ1>ޡϴ t^Ɨ./9d|a^_Z~.Qߞ鲝.zgR~aR~yzCz<C'׼bEGiG_?G/(w{ڤP3!bOwPZRo޶Ĝl%.E|ʙZ_l\5uI}o=۲<+?>+Ayb_1֛ooQ_bYtH9YQ euاճ2Ou 53F>>q]/_޿;*^|i/To_+O]^<Gl>p*磞'nFt!)t{Sj]9-=YA+w(ty֗-2g/DlDn?K˛*w/?i/ZѪ4*82sח)qs %$ wGO\ڲdb`˷n33,ٲeOAd=W_7-̈1<@9S7Da):G -#4d0H䀀 -DЌ̈ݚ{Sn) ѻ+_,/ا]ߴWmn~ 󛆒w[boXNK]}=} fa+;#qQln:8n. HrzW KW%<\"}Q^q]_9ҥ2]\ 64,;n|p;yؕhlۉNܕ}!%}7W{mA2Rr8Ov窶Z˳wonzM ۗ4\{Ts{ciC4^zg=zצ'Ѻf%Dd3C*Z[2XlY&?<Bcp>F _b;+My9B GCoϮ6We -zc#( V.Ž#_<! 諛ju30Qޒs))I"Mz ΄@D Q0")09caB7;8%FyD$ A,Dѐ6tWR!Mqvnrwq5YyWB/7W\^^囋|/Z92c8RnP1d.EED! ɂ1@?aA$ZMazZ.꒷nLjD"\HE:rH1@29apcN=Ar>Ta'`  tD=1Єm%LA= )?@t2+{`#{@`A#Ɛ 2 K2%&SIG'];ОP ;h BgP65 ?U/vZ?~p}=m}߿]lB2K*vS=Ř_'?W/ \#$H !,`V-/.7߾~ٌ{/lF"@@Ý#m !ӫ{cKVɹIm@bi oe܎c 0fmi#x8'h^ׇg`3 2A{gs/~`,]_}\)ե&g۩EtVR JG'^Vsa> &a`뚟 Xl;hAkiqχD-"F#αugy$_&x|5>FF6~..$5+А4 ljF ` eA*ʞGvG&  Q89)"YPB pQ; jD9000ѪcmMIRݪ2}9{E{t~3y__<>Rz\ޡq?pPNCG >nj￁\3=$»:j]$eN ix ?*gA{WW9h I<- :'q@D )d kdE[1 Ё)] #1 Q$Bt B0Zn<_t=˘pq/o{7t.{bjt]l6/Ƨ`64Ե7F!V𜇢p:b 4Hp!Ժvn{ĴKBX^ QnP vǜ@ѱaxbٰ.>egѐJR, 3Ge*J 0UvIbz$=u`0'06A oj`YU3IJ%cԼ d]2 $љe`JBYB+DaBhNX\:19 *[ л#҈kز4ojͻTջJV23JpAnC#nPB' jff`l`)IYT 6֕ ES# $"?=(>7coO__Ja:}v~Z||\ͺ8Ixn?Bцچ.s!'i߿zq.yd=~^@ l0Dɠ 1p.j15!ury>޺u_Q"uAXcaAAa8Qx FP[$@6(!'4fH"O~ݔP*(%ȀSavG\(usl+&*PBpŒ(P-=! "7rbtqOٳGE 2 xh]5?<2iu}U2FB"N]Ss ּdG0HfHFG4N\ JI"1"@ټS7 fhYèZ]U#q~3G~jmz'<W^_PQp7--ݦ^qf>+w}|O.tA|i[Z-,*{xz64y̝p)Y*lGgXvIk߮}h4yw {,)Z8X#9xhH0ewơ@{M4eT9(.H@J)R3 #A BFɑgBD"gE! 1 GN_rYa>}W_kG,DMFn]zz{>{6T0d]?s";9eN]S f%sjg4eWMu~ʉ0izGARH#r"9Q i <rqJaPCƠDQHĘ@4:HG3vC012'0/ ݆o i]4a6JeHZP0x'" p!dJK4Q a) ńr(A`F \9<1)R`̤+_`fp6T2` Z 9% [#!jԘ3BP SARUst whd䡞8iH_W7CBWĤ/~_c_;lxɇ?j޸I'aH8dž)dLS.{lg.v%޽?͸Aq7/F*ʼnӦFp!ܮ7ƫz?glʵ3ؽتN^LY-ݾrwsIby<=Mi;=gn_߾bOp]s`q>X_YzN]2w0[87ycnx;J@]wPZabFcH 3Q4 RRpJdFĀ1`(h0#S`9D:F23-W.pzZ/~ͫ̋ 'n lqpeO?wi0,` Ya8Z 8A!g\" A 1@O|yI$3Xp/@QGF"::EY:JS 0ĕ1Dהb*A=k0s*AlĄ:G@d W̝ffASjgHgA&7PPg R: !="有H9@ARm`:(De .#c H,w+*+6ܡh0x8ET.mUuYxbc&`/ip.Ww tDTrn3PBa"8 vD0Fg"DL s̭gJ5@%<{ho;a])5p}z φtz.19h=0>Ȩéo-7.z1{ xqv>H&zJsd9-~79_&(v)MTIkYj0o2MwwT⛢ۖ;kyB4#|=cM yڴVUez㬋>ᄑ[<=}vuxm"\9d-c zwc?6ӆן}&ד,ԯ" ޗmo_N|~rJίae8X(v~ЏxMxaLdWn: l}b%e>AJ;+L   oE;ـMiՈ6\v m#+teUn;=?'O e&[f]Kp}NwHSHqdn;'9`%xv٦ R|BWZ[ } <+|ׯ滻<[md5Շy{sS&qjowߜ߾w^}oۤ2Ȫ#o/^~NU2~8 /g}Yp4Kr)O&NV-QUo.9<ѩ@AP89A,ÉEّY@1DL@ 1 7=C8,)0cDIxB#[ڲmHz<_?¸/ Bm}av|zy$ݔ2%.؜.o=J b܉<ߧr^SV`@#4e$\u_U¬b?]Pf_}f*7~8̯~|3 Mxցn"6ܵcX"AKlS<뮐vy7,[)6S:yZ}>QW)pup6.Oa$dcjCR6~˽ghݾ'.PL~[ǥzh8v\zl|ZyT_|1i6SUc΀}mW/ aS2o2(c[j={{_y~}{߾NJ|sūwo/~{[vo{AߟN+b<,oڥ+w]+4 C&|x϶4_}~{lĔq~@|wD^lN8jޘԵk0$)]Iu"Q !2@ NZse`PCÍ22d%%"@ d $Ew_ϯ׶8;}W~GRW O(v)N= X(?,WW_W;BQpؿ|a0V hF YRA㨋S@0O{mTj*D4fbdwREN s_9(Hiب f1G1jf.S#@H~!0aA: nAT ".c،2!8n8m ;(!+%b~ DE" ly6{8HejFr3)L . XD0M"(4"P6Ar`ʩ`8 ܠ\2܁Sdy^(:W@ [޸:,Xj幚#1ZI%ef@NC8Ho-Zknc"fh+GRl֥鼼=y1xLR"$1Gf?0]>Zfjcx~yepq|8#F5:[+ (ˑO\WKyu,_7C7nכ{~䃂/=|:~8><=޾ ;fmpfV:BẈtۯi2XD^7zb8?HP mY@ʯɮxЖkXC. z Sւٍ]]1pN"L[$Ak̊1`rrBB 2 !7,A)! ÂN9c @(H#sBmίainwӫcYC)hHJ[A4@$X @ 57?#^r)}U=o|Oan?ay)y} mC}Q lMp\c|5LifHW4bgMsiLnsO=}ַBc]dH "qkS?W QُjtJ 5Z R@YVm}^br־0Qp~3'.'%Zu\`~<)e#,An>'B ]빭44+⺠x>_s;;MǻqFoChݩݳ7;VD>\Χ`W.ɇcmWJy'+bx5(>dO~ѓ`ܿ?/_^-eS"SJ VŽGwNJKqiR c8ڀ!/2Xa:Ga*+EX 1ayHaFD iuN@FT#*;|/_i]ouC0(&N 7LG\!6vys3mtB$SN3/7kJY;NtIEeä#0Sv. ;*A.Sґ.!xppM{g0i6GxuF@5ztvDa"@ԕ<)}c!&`s5@@%'A6T=ր Hܚ1x8 @6FJJ@3$$J!ޢľ@H #P&0K{|v#t3Aa](Z@ĒiHeQrDˆ܈() xUBX:6D '/N{h-xnP[NaV`қÂDJ)]C[HʵusX@Ԡ&ť94w! P` 5 1#&HC njh$="zI?ܦO4j\˳_ݛs{x3Oo˅bwۇ?{۹>gνZi5EXMpݻ["a،88?y1ҁ{Gb.CLJwofi,Sk3,i0a"EVӴ#9C$:(V$Ҷoק ']*`g\P7Gnl/Q62qiu$ˏ.<^'狋0H<?vw(ݵo|}~ˀ,{_=W=.խ-=P lH.e?OPz-K:<$1,l@"0A8Č1G1/ Df1[5b{˧l~|7DGx7O\ F=~[E֔zg]s]ʻ'W!_wy<k^}Xv|[ޢ.FkKe&@Muaˏ~_Wg_+Wˇ?{/{IX)b&k54BtPN͔(WD Xm"; @+ 4h4 *(x$0Hb31 կ2cpj\QH1VMꋆ*32p,4 no>5\ DSJ$Eu|_D#!C H=RA.A!xrZ*nJ5,T~eM1﷣%|p!3"8= KJDnOէ~Wopvp_.kD8 Gq4]3PU|VvBɕycTc.(!lANgFfʔC-36wAW"u@(!R{ !HtuH;v$Z"DjL jEBgQ =!? "4'BAI &\M) L`b02nC'D 'N@eC &aAF'/Jؑ jEdbc[`m ΍n;kYk(g\ &P zE@)&ꊣ`Cg&Pž"Q&C(2AM JH %D+s,4K %Uɺ&֨! ɺ?OHǎpuQO#j} :<1?~5dmkmwgkO?H\lq't<3Le?EVwk(M[J%EB47m:~X "T+aQ56wۛ5 eAWoUwrKIb/M+K2no{߽zRq;rZa B3=_ɧ[=> W/~4]l-^ew_~'/_|&~.ʴ|߀gj Ef+F,xX_77k:`~wǷ"8|,Anxn宭/u"G+*әvSbK/KICctұye]UqsKS=c/ož\>V77i> ~3NUuVpU N΀Im%yTa>I1qiW;?ݤ,}=NGk>7maP.2Z ?f/?aw59&T6z҆s/"7u]Ylf餾 r1큨O)']hľ;.V3g`nИ ̶LNvWw 9R1 #2׳E`H<**EwH%X""+9C hM9r w~_oG9QV=qddms*)%LA  VT1%L<$Z''"6$e4z^oqwLFhg,\z.[S"FK?f.qm:VNۡ/݇embdyqQ3%;+Ş |n\ gRՂC;xMEsp$Fo q8M@M#!Y@){ P?D<'8'F8e!\<+!g[>, rD,@h<8"mкv\ j9c'b%*S"2."mքe!`шR0UX# :s,۪Zs1zU熊BeW⍄RxeÍ:Z"kajUq!]ބ P 3e(`D,802\@ȣ9P@*cx=rⲛχ"a#mB@" VD̒ A.8yK%ljPwo.h+]AB'ju~|7>^{t8}77êCټ"`bG oywQw>MiK>n~ӗ?xoO?o޼sڔO D?=}(j:{# %*9O3?]ռ 6~ݟIȨY uFѢ5Ww,( i  ]՛q¢3L#_( Y @BE#VEB \֠X)V@!'n䠊$5}X%Jقlj2B=H DN’yj~nJnvWײ osM^a*H D2aoF9%E[(2Yx!P)&iۺ[Wˋ|=lM2iJJX"j#%)"U` N#zSF{+ &3H= 1DAhyG\WLDaF؂6`3bxL\SĸZDJ\vzO~*Jx= S*;UCa C3dc`hDsGy+E)@.5)p07dC1. #.3:d&ΪP#44k,ʁ2DC<(a5;Ah LJM8Q |Y$S),A1GV5Tu&fѺ(|p})zM?E\7ƓOgt/mEkO:vz,?<[B>B3k\|1 ˮOÑ:~{^b܌c j39hD D7ӶR5|ys1>S=Zԏ*{|}q{÷8|2?_^mI,FWXjDʐ* !R0$G$<:Skڙ#  b@Hw!PqnLL];f m/<\~slZ HR iĽ/aͩ jSlw޽?\|TpL e'y޷˟:kGH(8[/>?7_W~f!(}vZ1kГh8L5T+b8(Pu GaKBm"̎ 0t"2W-hll#CaB'ڗwz}Z"= j@`aHւ 1at|򤌼(nx~.1qtArr.v7O~Pl TWN @CNX;ĵuh%Tkœ(xqYr@di? ϟld7mf2X1 @AHy+ A1P8$g L$6KHF=`x A6itg!5gp`i%O+sboș0 Éo8:@A=ngV22 ,B`fծD M`_2 x;WTL` d`lń | f7JkW7ojYxUyfG fj@ojP̌B9ES )!Q@@0!hW$ h/ye1ybQmӄސ7 jͬ?U{F\@2ÇrRӷg&Nͮo(WzF.DžtZ/>Zfjv<ܝN`%dz=$ FD>,|]sd[. GL20 0% YskZib![i99&9;h 5NiOZPD`Nup^ɖG1_힏//>*? Ns%D>o]~]noe/wW4BZPk`@=g?'|۟_}?Yd.mN]s@FQh )<˰O9=y1iͳr3aڐJӞR0#SlOo~zc-yH9@`"a{ hmu4&WEDF]P=Ϋs_5˗We~u锞?0pdN+B4\V@D7^v ơn0P8dJ #pA Ɓ  ]̜M@J6{ue hL0C15-j sOM84$aa VPH H ֚OB>LءIt΅S͒9'%a4`aOz` p&NAaȨ ѩ;{N=ɬKpv48Z1LP [mإU $a *Cw`f)'%E1޼0D3fI(Hװ$lju*J?w.](/Ϫ/'|]M/_F^Noh9?Kۻi_zy{[ROz?܌_ aGB >j_9BieCRd,T &9[ N#NփL[WXՇ)*=:#֣AO6<#xo§yo{{:p#7e{9^_<xartxxwuWg,;d״\.>x ?|vw_ipb\'4le>}iaH?O'26H]?'?sO"߻NN, ^ß>}RȆPt^ѡl% ͆Sw'v*y`0Z+`,c 9+N#`D؎D@=A\#6 =鲞w~m}[6e4)WDz:!{W\t&!oW×ONjnnx`&A!d.R``"67/j܈rCwW(Fn{vr:2o1 ۼzʗ-R8Ptv}~%g7卬ݙu4zYʀ8aĸk`H#2:H!d xAB᐀ A4<9-@Ժa0lޜZ{20B LaFB39H3 WODW4k!i,5WؓX(X m/ 9j.AHYQxM쫏ޥʆmu? pf!!nɌM=ںDI)Q74UjkYU982H7!XDeO9Ͷ"sCBD$M`2m`U Q; 1;*+{FPPfpz_t 3.&-tM xvϯ.]οh'dx;m5S_-n&/"~\}lέLnQp/zϼ?ٍKsKӰ+28s+|"H Q fgL!@#$aI>*$AQtf-`HdARw4d3bIА9K DeOߝ&oK&<4(5g8+qק('Ne6b g}y뛌G E W;^f$axEiqW?_Kip"|](z~?LzD~YJ=,(Z1!3Rs 2 VꧡLx<F zcÈ`(Oh>a9<Ú0UR* lIDʄjIj=G?EeLݙQ)L:Lf!)b%(#E3&1drt!axX:̏() '\ 2wwJ$h dN җlny๒x lΛK#$H#1 ;P}Coo_7qM;gWYŧOo{퟾lh~~y@@4N3/O?Os?O{~⛻oR~ɨDC-U0BcFsN(9Gڜ (JwDܽ8D`1q!NLdQġAرů?=."M<ij ]yz9e)jׯ/tGe Iq@3=p»(?_?W"osl7NRŒʰ . . k)^d$IC;h^D؍^}pyyRP0g4́U,n!ęQ!0v#NXO0DP ;"qDBf!n v;WFj4pX(ERl#YZܑ+1FiLVQ@8@6)#?=Ycfa0hJ$ \0S^2 2$f~bB=AYM'D8"gBpp4EoNՃF;|N=74sX,2ꂎ(l%xdGpʌZA3yWCP0!ΖSXabָGubU @I @LbOt? ߂_\˖o_Ez-Z\~O?aYCO~=tdHT2 XQ6' żr{HU{ejߔW]i!\ayAaS6C o\ hxR *rfC{sw|{x<_^7<wn akSK@%0X@ `]-8S$Գw@/2L9Ij6C@? X7@{$ 'L HydAu1P뺜Gx A%0hbDu$~N8^q3B{هleAI4s(6PF4O7R:NLp3?~'UYOsJD@yܳ7=oV AF ŦM]JAlDfP Nܛ7yJY z~çvvL Vrb=[wD)^@xhJP#h@k wةgA+bSuWڐV&r'BBމe 0C&41CGpo`@ĔHC⬚oɟ@VvAz3["d GBQ88cBSn," KYvKF J˯s2'r>=ܿkEΦbyﯟx}zW+~i@##xyO?ѫ]/B}2j~_p_~?~,I~.ou)x~bmR쁁ahKshV ꝱ-p7`4j0H)ظ4҈fS"Z<81Psp!FBVhȤ#©;?/}*yd䁪VץfԵOӰR511y6&G8EC[W{aOoq1,LHH@Wh6˘}!0Z׺*1H\H9+KN"^;$G0`YœO.| PڭC)JYqd 0P wNaLCcIZ !"pC2F,Gĺedn,də N>`;G < eW* ' A91ȃ h;0iΔ-Id#u":A8 F5Caɣm&:c BE!gX]B5jw tokmsjSQU\]g.UǏ w{=یR;߱%:>xIv6x<ۻө*HJ)qC!"&0Ȝ4L<|x<֦$B;2%rvĖE^5rjEp]urw=VZBm:z+Izx4XcHO?|"_|7edp4KBq3vtkoc4]0Em'Ÿ}r=}wcjo~<̽?Oy;_|Le[z_B.t4~,?S'?K7o^׻/>7,@z:A {S5X$up[!ꆸbԖ9+b¾eo @Գ $|f&K≵aͳf仯z؜}zeF{:QKP.?PZ\-qqYqJO.IؖuuJFWRHIH&~/Od0vPws9 2Ūm9}NˣvK6Wo9][?};] Ӗ·[0dɉDŰq.v}=w.4^<_`wC8h^>IO_|_:.?3i_fv~{g??x>;YZi'y}E@${k\6GK!y=t#pƐҖHLQ#XHF=M]I\SĄ9; T8Q?_+| Sd>jh1kd d)[]~w=(8N@b$@Hc!s`J8Td7q1d%?0W`#j(g̒Fb1g B)g FXM52ƌ Ng1%`2f]*an!٤>0sKjsbhLih8t30"Ch(Y8t J@HJ ę!P"w$DB(C !3uw(<x#Cg%! '] !k@и* -?CC"_ؕ`U  ,1 =" Qb(e@$`J#(Ji ND61-&d!&wD"@yFdafdmmMVZ{9#$sJͅ6*E".qm]rɭ;{Ȯ PgsGas;'?|~rͧf=Pw}4>2`d & 3:@S\J:B  ~>C޺ S v$<&>YmbL;Fi I,*=f  )=yie5\W-pAH4Lc?FlLB$ J<@w7P vÃѿ_}4Œg%1xDGm!EF ,#wԵu`&͉}d|j6 Y`PxDI< D$59dn vd#:8@wqBkP1AkG1 he d\gd`C&A^A$#F0,R5@]A=*.Хks BȞm #K5fh036<80; aC-GvMBsO!"B! 'o' f]F{;uvdlyJ!uD$]āyځ.މ,a .D©7U `hrADDĵQMV*[pm%H8ݴŅG|}ui|h4 Nzǥ{1PwV?*0ʰj{׆ maA@̱fgu6!Ob9Rs^ 4yBC mzvͦ0g;` *o_W2nQ0A\#{ST.7o v(Iaf^O(t{ͷq_]EF ?{=r??czAo~||9~>~m4HjI?WO?gۧ~6}~?Oͽ?>yeL@و͐\ hxG 2%*b` p 攣) b @qHҝ9&T 6S w d!mVɻ??$鳫rϛ Ipӫ뫧U_ci* þ?k.NyϺCpRJI 1(x.I@ .rOaŽn[`EӺb' 9 knD`M;{ &Da& ND) {\:ziI& -z] =(E:]H,aO[yvY6tqa>7q#ΐ"g4oӛoϏdzGJ'EC[`]]{gS&ζ>._$mt0a^;2S!KB[3$6lA^yqlYxm.DzCe,{~8~ݭd»noazE~-LiKBZ鳏?xǯ_O_^|DRݷn{m;l/K{wy4FgJ$M6"uq[#ZXۊb=di,YL 2C1d€%K4aA- CP te(TN"jgu=ku㉓ F,Of!5p>$MNC7+I 9;uؕ3G֞}Pk8 *U.E>l#'14`'=_'7m`\ H$2!jÙ;A"jl85R&4n6OkFj-xH]WM;n7mF= EUP(1Hn֙rqwtn.N?@`o~85natb氦Cs/\/6p̹FIu^ z]`]쓯)ȹzDjD yEJuQ⫒H[LS=@Fnw/r/y4SKJSyxqi|:ر]] C|߿~xQ& DO7/bx޾~WF֮(Br?{Ljp|Yɏ>ٮ߮u7?r?:}!+$xyo^6r A#@bJ >i .uC`H4a`ݔ6dgv*{ Г{gdž^)`'=Ż7/㧇#W" N n/sӻ7 #INj+sF6cɌiJrlטyʽ?_/Uwv1( $2)A`AАXBPu@2dՎ#Hn$޺2SK\_n72)5(PyEu.$ab (:Uc`J3 p\L,-`C4Q8*9t+>bs7H:hBA4r0T@DDB @x@+ l5[Q: $+`"0 xPl Q%$Dh$ {9&#mh gabD6ʘ<)؝#!jZVF%ujP^5։ adU+L"Bc1ǔ(NC*lk<"gBB ЉA%blPO`'z7y]QEo7.'p}钐&&e o)]lyROL (DPK2e"_rǿ_C랐Kj HLQ a bJe; `JOvIL;P݅[gnTSAB`wd=&4ka[h}2 e.hM/Ʊ8&]6М^)0vټ apC)"0%脆- atA& 7K[̃CsC,a8!DA p $3 fBę?OM} )cֺP4P;U' E D%J(LKr)A2b& +0Z ߐˀ N1Ȫq=%%3RfWWM$BB`h uAX&" :^XA\53:H!QdFkF , ,AŠHnh(aGތn>xʗ?k8 EBWH!gIXd9Hc s޽]|qÚG缿\8:\sT 4j:ǻS;݁b>㹁40.7/?z4<~߬ݰ;咽.Ttu3mkW|?Xu Rj4Z[ܽzw~8w|s_nKs7/o/'ǟ~?჏ܽۋ7/n6??ݢ$Y Nڀ@C!/l ء+y%Խ`Vs*;{#J(lvH=l; 3sD1Vp2v"۔1ab`Cs2H=O_m;4[ys:W yhˣ2%C1F`(ғ=rO/oٝGwQ2 4UN Zp$Φ"0#6;'3cN@E1r"  + ǐAbvJЌB8["?"B ᭟B]OXSJ5 A$cf mrG @U<8u@%) H@F1yI z7vB c&Hed[`5#R0r埀̯i':KOפ b|%JP=?kN=(; OY;b~4y,&KX݋ U,E=Zː |3J(86= /F\ CCjRie3թ=+-ˉjd. Mi~8Z|{X ų8^\~GlL4  ۋiG2HW߿x_SvMMpz\s|iYZ7WէLݻcnد=|~O~x}Voooj`g2}ŏo~rcZX0 i حٽ+.[j-1;F H2\u?19k3z@taP2 :Dir:%%ttQ s¶CeY: LACŽMiL'#q6.[lK옛u!:\Gɓ@VDBvrNj ɳ-@(@&,$Stb80'cD`G)  ;C Q0+*5DV#>{W5pghH08U Қր|6`PO԰ҽ d.I(ID KĞ&F 9ga#5\+9CX+yE8 RB#H-ˌ#fD8d"H"}fe1h@$"DiO&@,:8`F$F9 hc&n$DpLÃѻ[À*.Mپ;c;:?m_:ký~xw7T~aRGHZx*y,]B3N3`6/W"*YncTm]\6Oo ;.fsg-PkAh˩Y򘇫<׃?ܭss9ܭ66ۧO4ʰ~8?~7QPBIJa$rz2K>o4SEۺQRH蹤T>My|_rO|rGԶ_"'ɿ]vxJekٟ{|z1mglw h=0aUYMsaTɣ=4#7g@y6|JVC&-9@71͐LLipmN!4qs`Nf6`vfM+hų7n쟰\6{*8,sptyU( ,EzlS'ݳ<8m{_[v] 31ĉID r$=(#@*CKRs2PM)%ach8<]S0KagWEp3FU a$0A(AwgbR'T`dpHSF:1`A4BC+&T0q``DA*𨽒fFJ " vÜ5v 2 "3w,pGIR@ 8wGa%A֫a@sgK&$s H03U#Ss q؇P%k(4wl(H(L=#([8D.)BZ 0$}A9L:FDP=TCD}ParZxٟ>qkc\K ZC#~e/쓫g)q̎ N{ô%^FbDq+8jP`3@,ZV3`@/ .DvBa7>?]Al|:U \<_NW:+׆ "%#ŖM|L6#Mw}w0U&[_>y#EX.L5!5zmz1q|hKnwяSkoÝMiI?<>0}qI}/~C{:Oծ5%PD[86'0` 1g jn@j * " D % @^h)b(DĐF2x  IӉOгlapp_M!{o{wn'm=YG޲H\}*bc 9aҐRB 6w.mw1:РZlc(%AEFf#6N@{.sr(R!0 XvwG)後#F\\LܕI.[DF5ۺ yl0E8ĀX "F e fr!3mԁ.EDxpt0 `'w FsDC \*jH$-rCψF 3 aJ 1TbtA 'O6 ' daQ`64td\C='n)I( 0FL4;#bdHe{$ےTVw"NlB0b@^OXuR \`] %b9{$*J(b<vIHELJ bFtL,!~v<dHB$aO^~>|Mu9u>ѳs'zo_?߽ff O_nFb ATi $aNbI"fb Q;+n%攈0ձ=D+ T&icE19hudf"⬭c)栒ƑH|G/z`+f2Č,,ىKHMB} VB ,RIhuwt4bJGɌLkU2ѷWi1-! q56sB7H0aC!T bD6 ;%F$` Nf  `a%0B:8pW,=T # D3Ra8F9 ԓR(P'xGqG"yu !0j D,B !i &3 IΨR2X%:$Ʋ#f$|=u% V q\TKACpÕLVP0Z[U[9rm֪`FFedLc)u s $nn]N QhY=#wGZ k:M:8RgA E(0ZM 3[v:}2 L糷;L6)&MP}׳~b}=^=2mIɵ꜆KHƉVJ{=q-;;}jMȧ$p;[ynǩÕϧ9`|_鰒9O7O,iYVf?ef/8Fpf*_.u^U<ɴi?nbOo=u׿<* &&ܢ<|Ak&{_grt{;O~qzqulؼ\n>|;E~Vf\'?zhyx6?%]0C1 n8m- +Z0 Fatn*KX#c& `)۽5J0#MZg ǼْmPW)w(L&ip݈Ǻpm.KZتo|>Z羜o.e?u3H% z ӐkLWׯ_O}+w|iSbLQp.=O/j?nwUV6N2Ʋ.cd32[R"W[d$[Wмݳ%A=}>aOܴ8 o);SB˘򴣝4jd0l6㴽I|5;\6;_]|ckUٿ:U[x'TovK E*١A@@CD#Lj`\ H 0z)4\gҀΧe}QlYj\ f1F`G4da3b4>5 qP#$Btb(҈ynj&"0P%F x;NAd4<{w?!nOHǾHAi~݇V˓m\uEp2",6 q-HȥCqΦZǨ iՕKKc),q-$}滲foϑ՛y=mmdڌR=›ݖyJ~T=av>nk W,z|v|: Sh?H9W==pxx3?;-߽x8}!<DXo^C.? M?<|~~я/}qxbM gfXWn^#S7uY9aBLrFk@waJ w2źF v0P 4i #yʂ \&=X2lRj P]rLMh~aR47?d(̆NqouAIeyqw׿;a#C7KD),D"I$#0J1TUzks"Trr!s]|a8#X\7 bjXf~=6:&C6 u9g*q%/"rC` yP3(Q-Xؼ EQ3!0G(H \p)ʼn#u"` `RAPƂ[# *GTp7 *΁g? ƀ 85'Hl E:;Kt#wIl^E}3@JiGaAC`>SPadb+tpf*5<@="#m} Vcq*%sI8a0UՂ @\;֜@%ºbR{BHm L18wy@wdj.yPP <3%֔M]rBj53u)Vz&7< %lfzkإ"j[ɇmio}yzZ;MG7^8lG1/`ܝ"o@&f |UVGګa BIB&,NĒxJB]pYJ۔1V=􈨬L&ANL-!=dLc5hnkE.Ԣ[~z3</A~l/]E@$sNN! … 5}4́M8$x"Z%DJ9mql< avtwq< I;0e1f*S3 1 SpZD"bLdL)V%36oXSJ` QMyɠFdB%8j Ld$Ȯ8NjE YsyWD3BCx`t|9᭮~Sa/8pIT%p*m4iJlD8Fg9U{u{\/k\-DaZ_ץ;V]Bp}-]fofˌTh:W7X|\%2i.xitl_Ó?Dw]۷wdKBbwu7&00\J u5p\y*0.R\x֭Xͫohm^n ,5/Zg?8X(u示 e`TH@DmU]Ͼ3{IWm(^Ȁ@Dxs͢EpF ]͉o6`Ȁ:)A TL 2 d(ГB^y|G[8=_N7]́rp~JbwI!;suj][|{_Mոb6wACs$ =6Q@ {u!L(\!CmbI[ET%LXW&03%Sƽb㻫%I S4C1LYT$"B:J;'0DD jv38:p4΂тs0'Ha@倀D[z T p($Hj't-ÙyX͌ٵa6cdf+ ffkt +w I,e6@ SFr%cbh,"`@L-=ZVΫ/Zy2 i6!yg{f`sShwhfLТBPstf!Гl,⾹s+ "yCCwM\J hROIvlyTmϺFND{ּٖ<^ mhm@p/u-EHlsFR_z,w7v^#tgpJIWGRS]Z吆=$u^"6umo<#ewV[{KR6ϧezq.-rmq0s )2sJc13WCN}]b1K}yчy4e= }Hf,C48a""kuCfN#kK޻ i3j5B A{H iq]}Fxov_LEL"&B`@$wH) @::#2J`"%},ϫh#rH\^?헗On2gWp{ue҄a8R?Y<-`-mZ5w Qx`mw)!Hi؅ubB#J\[U_rɩ[OB@9jd(4DqS$ 9XےDiyД'OPCrf C+;0Ɩi DR02Vch$c] 0*6͙I)dt3'"hGbtqsFP;t)Kcm,\s v_%ahژ2\4$Ȩ]<ԔM;o!dh( ," ]L @=(@̠LP0#Qz4pkE;-RCMEP @J 9qጥVJͥ$B%H$0A) xJ02aZdFBY8#w>RA&BtkH;VzPus~F3ԁ"&{mK2q!z:۹} Q=HFEEhځ.EaÄ^\~>Փ^im|ɇi?.|!m* >-RU/D#r@üm$' FII)3BEDT?w" ֛0'0XF)Zz{O\m/<'ٗ˻o/2>s:Tiu X!+*u#ryBc>n$ZxE>t'죚 Ɉ2ڂM"%pe@d d#b6@JD[GPǺ@ܟμ85nf.myCPh4gZ4nb'uL?{t~|\[Eל 8@Ȕ  D1rl}Qu_䠳FI R5 ˘xh0]ps5n.(w]I%aMe;1󈥠9q6 $ZfD1sp0; zv<^ [ga G 2Dw  LEQ@&% ;Nn = @I)c $ "$Jꑠ;@nĆ r@f\;6 R6]Rr_3 ޣa8P`& T0!`+S ܀Dz<[k)OZGLiau|n @n~Z-..izv!T7i$َ'RMWsvhƔ$w~;~t=?~ |=~9 fǻ/ץ?=Q6H RG1)Ȅefhj h񒼅3I qVȨzw40N` o 7mHXPLᅰ9%7eI a`Ld .so./0ݝյ>ٻE|uUװdŲZ Ѱ.&>f/M}V&]<g/shR.ճw@l٪ՈCfqƾP;b]<ϩ>jWknHwaFp^@ڇ7G8sG34D]< 5Fl q J@PJ0(L! \dcDEXEWg0#%m Y/3{ísHG]_ȴu30b~ij|x<. HF.QĔ 3BޅH? 0> I$ď=@09X|%)"D=@&eܒ 8D=G ~$ !6a8HdGHDB>xZk6V۔iّC L TSD ׀ zJv8'``GOj;*6 4@eXGH@q$dkp!0160䘓*400aH&;=m1xm]K" ሦy`e|`D9 #R $c 2G G`&pӚ9Ajlݰw"Z:m{. z0Db8'H 1DĬjl XhC՘R:XV/.ڨhpf0\㌅uhI@ !6H !OAH X5r Dh9D AWSp97 >{Եqծsu%6?.]@uHYe%Jɦont}s~Ӕ4 o0Hq2m .Q%pTjCxEPD1&BeuLn+&rm-cXUd7") %͓>FO;gH֒ZŵCNLyHg-(7 X3' hœxk@Sx 0$O1r8FaJ n[cX*n6 :8zU #a8"Gdd DLᗡ m?SrG q]`bF0h]PQ3q&q0- #$'hZ.(-77:B߀U*fnz_}^97P%e17ByW,#xWMh(РG%X/"攒J@7bHޕ !c!DwsXf:A1io ӓe <;޽1'C"ePk'e 5B%Ú/k;wSpO1Fix7|;/Fy]}ŷlsp/iې[y^j^V#`A(ԭZWN D:R)q])ٖŃY1Fu\0+D@]M%sވ@r(DCydJ&n\wPgz4*d "ltmY Qspn;G` vFVPƒKѬE &t$`2V =(B)p NlO~I&Εi KG  @ sK).GI$hIzRhp+ˆ1h ĀP"h)b A#4t3I4| X;f$ L@HEkwlڒ[ ]9!Q$"uāpGIU"Ku6vù%b1k cP ̎%*ĀԂbpKgBE/fH !"у8b,BJ8 ' 16@!56tW AH1H@# fB"{ַ% IOCI%)K 1/ fNf. K!b3xڅLeDaJ ROH#I0]0" 0@[*b BH(,-TvŎ|+y(ZPO0\Sh{e`֙y6<^HP$z+H-XiDz2580eLahnJA%HAW1ޱhT$V iԀz{gkT$!٤$䘒eӴ.}!d&sea_~Rj~^l:4nDgޚPhmO>p85r^󴯫νR]kF rNKEڞg'(R$/L;4N47 sm㻶:TӋu3]17E&jy?чM6ctJGOO|XYȼypx/~X'Ti;E ˌ^\㬺mEg+Y/4CJ~jh|.\&4̂N(4<2HRFc0!#6"f2P@Pb[@ v $lV}X":aYtLǐ ;lw8iG3pp%%*é6f}wgq w)Z*(!JNW.9\#<$ꘓ@vbDp>`wMXev95 o݁) [;ۇ9]\`ψ 4yylWkT˜b{swq~滷ơS5 kwhnk9[Fiu롄I|yl_Øovϐ}n*iJ7?g,Ûׇb08PR?~{~tF9l$GO>yxt (~5xuޅ}&hі-`El1ox2DKF;!3q#E2fc!J0 5lj 8SaPڠ*fDG"`DhR@ !$LA԰E{xau/[E5"(@,Of[vϮY&Oe>o߿_ *FN 9P'b6g. %cccC)^PUrbpm [ gj= en/..6} `-L=Rd !fB>R a 8!Pp?li]D-;U )+ 0sP @ Ai [BjD1Aq&Pg"2g?n%Uig4{lJ 9MBϷ6P]c^!cöLE(vs*/᏿W3ڴ5dR9pB' cXQx w,N.  Q)NAᲶ(ϋ-sPg [IP{72^CTgz|é'a+Z 9 B'`HCv@*% eY[S{ku^-̫bS.2-0dC0@C_OaDdfacNkP](:R[meQH8m0ZĊRt̀g:svu@clwLbh cBB[݁ZF@N1p1N ^- ++;3Ct""D$)@J ʨ}p!S!}upNKzSxCAʀhf@h10xE2 0-,L 8Y:J}h'v,I~h5֊b{@  /,4wSDZWj!&3 |?kpiQnVѴ{Gהhp/}( )8Z_Mn0u }?a-ˉC>olmCJm{߼w/;⡾Z `C+Q2 Ҵt!1r q lLcjjogح7mڬ[|Yfk=@AJ `8l>N<U3$UL FL J@@[~Wo}B®)sIҼ:jx 7Wgx|b9Z=>;S2ºJ,!:1h^c\^m#=QTMo;x~׏31ʦ:Bi]$~zy] [huޥtR};0@k@/.^̕]j7[zOYB&!>t=;tnM^&`c*[yß>ń7ڿw9; h Cƈv]h'׆w_NS"dBA  ګ#w5hjL\]jE+;y3(HM#rN "*zDLDr766CBn-v%e*Jً`J>Q=k $[ޘ3(ACeb`Rȍ1*x ((|&B Wjw` (02DRzGw`#g H0eWHN^'ce9&2!#$Daa]{K{vKb6B6fǡK”^Y08881b@201p]Ͻ+K4d ]G@AM9+vI0VM"{ժB bVMK *$̄^{3zDA2oj-}G>={ y*dfSڬG2?O؛"9ն^rfv}2~4xbԵ}shgCN'WRy/©͸ fy LĐWQk[zHe3撦S7e;xo~eL\q؆_2R}@F7@*|7Y֟?\Wp"B]^~gcs}TUFDye_L(/z !O)aNRJ[_]D+#AvID0ej']2py#؈["&WBD.\OmrcцX(]F$A0P&L#sDs5ax8-ҹy?#!G a9 aŖ'%3%=v>.͙`$dgD) P}m\ f+lKL6rZU5zf&{ˉ'O7Sv-7/ɾJsog*$ D*i]?Ogxܝν']O_w瞌xBk!nFF ureۧ`7QCg)m%k#50%` A8m9<crf&ψa4JUd (eHAh1ZcN 8  s)haтLA N`e2vOD cXep䤝5;g nj1vNiy 5Jf \0VТ,J%I6@=IBR!290M! $]O|[mB .VPրDAy;t(nBBHNN S垬?R^!VEhB;ޣ  -z"T LYW =0{X0(NhVݥG0@.g?ٰ׎*AzU͈Hq v!n-zJ,>dL "bjtk"26WLHNH}QC& Q5i+;i5\C'٘a !yIxfafσ+HJImr !sJi `V v1@ldJdhl`8`\ rG Xf}'2xB#,$1_DMC]Gϯy÷$gN\8`2i\n5H -K@naP#ds0HxNXcY9s15&ݤ Ph2>m{Y8"Tmb@3ertPw5m@Ok;}"\RR|ji98n.7WO[XFx:J3*py;4;kJ7HXYU IImy.uSp掞 b)=Զe]~Yv$dSgH  !qo>]gEJ;vZj×O00wޡy,U?nC{2ۼjOiޜ SC mMǴ<)Ts"eukdrFh,uOxR"c{@DɑYP9)#);4F!ࠈ݂%E):!HBV60 ,І¼p΁7D `&` 9V:-4vGXj+usvn&/ݸSu'`*nYB C%:P[Pr9u5(;䡺wZC !uۜ/.2O 2Iol]:&K-vgųyʜ78ll}<'g\`NoK@2mğ|?ُvS_o8Ӌ߿O6|z'.OE(1pyq>/XgNL2N!Rԡ aZTjxO-瑑JBLA\9R(3Xv 4[`&FD d@LagdB `m H =:a.3 #SD#aDAN5A[عVIrn*",!UTı<ŏ?dwfOy˻(xjLz~H[ gXD[ޥ;񞐱h-Ǫ)k/ŇXIj̥q=#%JEHųIvORb(LLe 2dN0rua9f@0[;%j'fHK qh7ZEN#bZB;5Dl:@4CtެKVB E m@F@a+1 g*1dI ZHD C(AIY ͱC( Ԁ̕7f PUk J7O=R4PJ()D}Drr <mC`2" @Ilϟ=c`/ևCW"G>.+f'HZvm%,BTkMC ݤi眂zVwp<vNaLTPA];Ov'f%pԦRl.4)MkL{NiHdEfm! )(kK hp蠤mE,F6 zDj`ʎ SZIjx 5Y+`r0jAހ HHh ݑ)TyBN;ԊYA @ C8e&r D7p1 iH0WRP8!]j6Zj)wWT 1Y CC܌ 8""GA"/ַ{b Q@n&Oأ i(u]*:z :)A%7DBZE{ `wC(N@fS*$F)TsBϝrϏ0x4g G>1 j/\,HHn-v&aTzՀ4%v;<yѽu4ُg_9wߞ:]n6ryr5>;<{t\sVpXڪχ2WLB9sI =Քd,a0Z+ 7<&׳+ B``TR JJ<p8TOƱ M,#"IY$)K٘䌆 F=x'Eى$9DdAS9N"2dr@CgLJL@ZO>Ft+V+bo!EP"iA] MHUdA;Jp{x*څ.h`Iջb CEeuID3`aD$&qrHgq `IZHMZnjBa&C cZ GC3TU2jxk57s]{vȎ0ӆ LT 5W  /^Gۀ!-!aD &"qdH`D Ɖ=`DFà  4\( d-In }?|}mDD=G]= y1" @Q<U|8{Sۇ_U6c,r vy˄`wVf>,յf}j; esMc3kT&~>n{ m<^i+jDaD?o@C|Uhaǻ?ѓ~Z'?Ov_>ޟJ(gte<2Y6&;Wo r|n;z}KiT\3Xv8D7h UUavWR^hE*,L\Qv(F\ B|݈(HDGk`)@N:C`XH .L̓bUb $0FpH耨=9zFQTF١sf@BfI(\zL/Wp`%G2xSA qa8 +v!V$##^/@d.ӀeUӀ %"2L;t!:C*I3{`TT) _|Dה\:meඦ'E%j5M>;Ww!kU}=X0tŏvWqĞD"*cWF*6 l A`ȘrYtMѭL9LBjL ]C% Pwl!JI acw׳I{ ]tzTRͣG~ƫuH no_Kk֯0]\>oz<>*OrrzW\;׵͓˾Ʋ|js~^OW]7(i]9fgp^ɳ˧ ŧY߼y)~s,0 u{]wm=~쓟= ߟ҆c-/>?xeLc${% R [oix|Cx6;6Uu]|ĥiJp7HIE 9a1&D(itLB!Ni]*Ʉ#1: S EDBh(( @qs p^5h:NnjKekk4Yk DXO޾5EW#P}at[v@[8 M]xru(LYEqT"XlT2i CT3gSXY6bwri  mH1 \1pjw`׎=v D EL%*#@paRiT8   d0iP @ٳb.!HЁPWZbi"bϸ 6`( "OA޼8&E$!GDݺC2x@s hH` )T%gAre9o7L [ "pF^ ܙA;`AZXçj祵Z*lwDj4R"k5w$1v BNfyl"=k+fQ@C,M t2.DTvhB! ք1]Q\\R&Hzx׭ };\9M:ހlĩ^}4k,]6 *r|)e;4Wsm]^k P[}/_|kQ/|t>\f-g&a̓0~||p_}pu&)N$Yrl.<ܾ>8DN$xssI^^^~qC~|'-ȸ?bx0 &cXHwuyu@NO yRPOKVC' ApN"{Q\$1ܙBB "P.[$ `FXj@uu޲aLѝs!*9Pf@ Cb#B@"Do@N ϳisצVuFDˊh $ @x|f}?zjfFջC2X(!rWd276b 1@1BC  ;ZN)HN$lnƫO=5m3׎}!n\T0% bݜSpT<(&<:ޠ@"v@)(ZF 5 oPG Y eu)S8y1meJU!:b biB]<™xsFL#2k`@i, tiZ#(҅ؗӆ{u"!f鬉*L 6zy+[|A{$@@ްuSSIg=auS!c(8mZ|BC!N.6/^:R1g%0h ,p(I- + Y!@&aqSZ`Qt:;mZxJ@nwF{K˴MC?{xwW(YC36L<}\b@dŬoKe&k։3>LQ l"׈ jρ}!,Xd(e{G;z`نH SDK HI 9"9h 3b3fF  I#z@()ː'4]v-ILIY@-@/.sJDV<ж+ }$i}|0wj5slv%eHZYoNat(tje~/ 7W e^ΡN$8]qƔWvuyq?+(`{iy\]~'9A{VDi"FI2%>yndz+r=) >kz_׋'?/iwm50׹~[2bïP W?T\s'.yXML!3e,4]XpȔ;Ge#y`ȠJ@n "DA} 1ljGh/P rA+^#`PB]b6$g6#'ԌȉR!![o n-B X1uqZ}55@aAiT͞#G{*YSdb 2)D&&lݹw&#c( (D3mEBЄħc#f`^+Cuj0@1Cv!5cp[ l="S9'TVRH-ސz ~\{P2)xN.n$)DJ $| Cd 5D 0) jCQeShZF¼ItO}SJ/Z}?Ѵᏽ\ie=ZPǴYoNaʨяVMcMT./kSJشC@'iw=>ZpNWϞiw.yVOwG|(MCQ6=zy~ëo_}w\{?w_ݙ\&78(2 v]Z1!d>[tsM-*2rs<u! ؑ|f!)lޱdh+ @rw]ɇT -%a E*'aEmż A5OEA]Ti Á F La@qX2nĺ c 6Δ܍҄ٱ-5)GVsVāp3#=!w(e2 wlRҁ2D2MVX SH0 gJVFÿdm:.u%8)we<}w5ə֥ok΅P|,b7?So7ӜdJ{v~zspy9POZv`̀> j0=0u"R";"42a_"[XSiӀxYM"bqa"0F8&!r 4:QІ@{#6b ĽX\y V "8@@c2tDT |"p2f\Ĩ2:;̧t <@HTƉmvJe⊆R$0w/F{p.s0 Kq:A"C1 LʊL0m&'ÜJY@]:9=hW^Ѣ (1 /%)##u5A&2DF= n4H0$$vdXH皊lwۦV8nnnҔ>?1.^|J[.Ξ=sqbγ56 p>ޝ9'O?ݷ*{67eR^i~v$o_棏?{>-k@-f@{"M( x)  :I|55٤퀘|OKGPw3XIX"xH8 ("%$Hfљ"E`1 [f:|0kH#1 3{#bDCb]!0bDZwJzFp 4 MChb;6Ĝ7%Ap*ݳsq/2xߪf488gNlD.I"zv8/͆<<.@ymNh "oXJN, ) Ɍ%y= y@#hA}jtϣhpe!ГЦG"&A{h}a dF2v< DX7$+v][b:a P0HAv$J2ee3@ިkJn;GLBx5̇S9ly<|Wa=B\IQkznbQ Q{ZM2E^Ní~~G?Xگ3\޽цUcAmy_xbz)4a|XF]efek6k>R,bC˧tgmwQ!ad|h*ad^fiɿه?yIY~uxu=pؤH!$R {%*ek;C֚009OB(ߢ r2m|H7):E4 #"+8\6D=#L[޵r"y10#j`<6QFr6wkxqY\`Fa*5K$y_~i+#N~{a_}'>U-d;=hg?momnYv V{?F팴 rp$8tU\p(r~ۯJw϶@#s(_=a}`~20 4 ~C\7@a@I @.Vs5.ju(3zsٓxtNlUu4 ČPuAQ7SFhgvf6TudD7LJ' rf~/jQ/!M@WNfYsSP,d~c=#F776d4X\(T ʔ $0V6aJB1/i)Ep%C Wr N%@dB+9@1" x9yU: h}uPAhf 3b67@$Zw|eI!HTC]1|DrX+ݤb[ǔ`h@sLFN\1jJRL3YDχr`D0d8Nf-[f"Fja{3$ %pn &tdGCԭVp:vI4,٢aqerOO7+XL*:2T|"l`\F}@Z /㔌0 Yu<m<ؑ`wsio;Yswߝ c5|u.qZRNeL˻ۉ5c,Վ繙Mc:ݿ{x;/o8)OOGwu~8KM@]r>Om79xqᇷ@)kB@wln_jF+:!]T;]{mRwhe|˧O>U7><;n\œ ~9o.t?ᖭH0l tbNrhrFf2v2ai=dG:nN$OinHFI5YvOi4z lđ#8q 4(GP#yGJ;( F \AY . )@͂UxHD9,[;}vhG{y ͅzU}.ecb>ⓏxXHcF0i"~:Uѻw,b!2@8+h챶L,, )#c6=$( u:2FߒpZlM B9zK !Pĕ#)y.zlRo+ ̵#"@ub%6@Gq)` Pqu"”S tйRplAP>Cd 5R-Hk$,9&j̈0TD`LYIѥH\:%G޺X%Cw#aaZ)Bޑɻud)ff$0{J]!g#,C^ab`p䎜hC+u hL+jO;fډEv!ǴѺ2q|Sroe)1RG,PF.FiJVsXE L3LZ5y$kаn@e\K0vןK//n kn]BGLjer% WcءתCIx>G;h&̗}X\\^}{N!N֫iڧ uϫn0$Aj]6 M4݋$h<x0Rw 7o[ hJ=?.vS_:fM&9i?l9 h3 FBaLiP2 ,;[n3P#%j510;dȁ`(B}EO1XKFC #G`@Ef _<ܣVGz[wczxC3y(܌NZGB0Bfߞ)1|kA~r_?0y?]/g]84m@d*q3D9l dsD.J]Bƭv!09 +yڔ7[*edI]B Qi !F0"T(0,Fb ֽ'ڄ"g_vrYa Έ &uH仇wBoH#6$[{|My;^ҳ?mO>I|DÐSNs$k4n jL1>R+aG4\>v*8]>S/q u9mKW :iLuY6n29ճ=zm|ywmt*8l>ǟ/^oJyOܾ =o2n)@\)1%F0W K_7w˫xDeʞŗT+c*}^l]6~uG?_$I"-Auݖz^^<Ç.A_nNxr4?jgShgێ2Jb-?ۯ]h9s%'7ro-XAki9 է߽:ncڶXj]eor;^<~|67<`he^ifƏͧևߎ|LI6Bu0I_Oq xpPw @ P ;W9>4Nei'tzlUי.yn1W#8e az.7o=^_xG:\6 6n-c?#FZ5j e}.u7DB+Â-%+֣+CC4)R{ǻcǖqYvܛ kڪ:İ#"J~>ywy;$+ D@wGLÀM0s_@vI֞ް64W/Pv{ AcqQY!Sv@f,5p\r&d;;vm@@=RNH..S8O9)1to"=tKBћ C;xz wATC {1m5萌 ITta$c c۴~I`CYVŐQ9=}:HjK jdj|Qؐϒ 9*ԥ{%<"Ѣ<׈S`ʀCyqڄ$Гd,1K0 =d˄m!F+!B:)E| O [m#D1Xf[i]f!v]C%lB-v[xo]ތv9J%8'SYED}bT2׿[\x];|A75qGumvK3XmdXC$4gaow7'Y,W˧}v~'!1!W'/Ǐo{9`"V2aNdc,Ur ({hBv`7z{?~鿱Ar A.v,qOz4uw@kk'zjtOy8pOd #KȄ;d+"B+ )r gGEjl5WA8HD氺iޝN5-z4eM^CX 96̖L,p^mrҞi V]p<~X6bM3RPj; J-4{J|p1׻ζBN il6ϞOA_o{A~de/ P\NU ִ{zuQvON"i0~~|׿}'ZG) 3u]\x J{{m]}6~Hy3l|B]%nY*E<6ϦeIC8ʔX?ӟ^|lO/'߷g+-QLM{ְgXG))iLy1Vm]<}vIPF*p4fLlv%GVD밽 x]v `wpVDL.,[<i  "8J:P(LLD8њS}ǯW޽y}꛻WYWQG"7ݐ 7rM!'"I~O>[Er0-gњrBF.H#raJ,ĩ3M+NPЉd:0Ap2!j0PSsnjǩg02WDΎ 2hZM%#`F]7 &6cZx1!V^0d1>@j+ MɃ)'6wC~X//uX#:EY'O>Z߿N}6۰)DpKrx'jf;\p7;O?Giz_oO{pCL'O?zlڗ21}<g>i]oO4&enXb`fgMy ?Gib0a6Sc\|ik)C$Cn( k4_%"MÔ VF5IW;+ $v[Ds+sۋTIp}cJdKB ty5UGs&41 b0`(Al΁CN0dΐ'i"]@":qwS=ׇᄌW_w'z>Ǿvb|}z1&mŖr!;4 2#hzs.pN.06};ì@f;`vӜAAəzIVŅXF|y3nfDJ󾃙NVI0ҧ~|x+liOTCZ_Mܤ{*/|DaTxG}{//Wo>lwg{YO)㉼r3wU{yB&~fu>ke% D6( `ENWt7qw_fK./>gOR}m˛|wC)g -.93]F`bD&u9xC'63`,uN8Z)9^bTt)}~<7[YN) 6i[fRos"8Q& X 3s 5x A,v@+fЪ@/v>,wW~ws /.q{fjsbxlqeǾs)aB5 S&ei> :gfmf N1'aE]GJD⮦ʈݢ3Zwpfa4 ND6cb3 +pݖ\v[L]}E}mM}0}vG| o %X9"M<(;d6`-Xg);P۱HnD7o%{B}me,+k_#tYEw<\v5ڶw7+_\xx8fq7.i4YǥvH ^SB8VnHȌ4fq ^)2Y J8Wf=}ëu]WOcqoGV=i=<϶ḡ'% kByʰz0^^O?ywW~9?y}L@V3 c#QFZ0^ I7O+k0 ԣv؊&]~4JE 'Fh.iT薜Ѭ3B0 h摊;b LHgǝ?yO&`Lr0D sN _F@@hVlؠVNDdUD\]1 ^ H2"FWU-^XD3@(g hX41iБ6t-Ivy4H%&krww!UChmOaL⼟+b.DT`4r2G {H$侂UktܡmP|/d޼*ز'Ma k'k%pPb#DDr0pLz)QӦ'@"wLЇ9Y%(;DLȎe4bx5iBd֞|I/^8x}]}_^ǖ.~zq9멭ۉhGG}\܅XRzzĎ4 mSveoM_9GXĺFI4u.y:s1+a72 XWOaednjM~|aq~ew:k"znsT L:ԍI{O(hVAA-vVƌ)4\>NCR' uQ2ɽ ȘBY 0B"/f)ID.hsSWaQ"Ƞ=[t²,L]1Et X̄(!<BEFA77o,=-kGIdzLfR(08D)b2Ct",@ЋI(¦gõ5wHȭvVHBkNBG< @̸4 !`N BQ]%ϱN=[=9a='E* ` 9{fYo0Plw^F[cGY{Ȼw^yx:[!Z2[쏑docst4`JI"Jam`tzaB $-|5?{~Bcگ'O)m?ӫ7۩ 巯GލqwsL4Fx~(r3{I[[[G@?aZAN혶Fȵ"89J2ZڰW_7bX)%OƋmSH\~|Tz{v[2$! \)ƣ% LW\0Yz/WݠV@, ֔ԣ w˛7~u/~|oφͼ~s|vYcގy鋿|@ӆ|bz^8X z;.pűe g]ƶtrۂ)ac`tpcꀺ<"bT(VG[u9y̔v!;oe<~~u\;<{~>?,/3s&kH,1Q׻ai/4LN[TOiah~a|<<zfB:;I )`4瀮|J;"iC2zCӪ#=`I`qaDZɱ4 9CJx4lA0#i@hӅJ%F90/=I60l^\S^](g39M%O#JNI[ADyB `KUIɫEv$ `+1 bEA pQk87 l@轅Kz[{"`A `P]sV9e$ [^HX<4C(L`(% 98w <( 05# d1d a$'18|%s|x/ŻLߜun?xcu}PՂ)]l29ZL8_8@D,\i!7#;m&4yG u )H1TUdzN5J?HI:1Nwk8o#C,1a8LJzrgsUGq.CyS_'?,DŽTB|0"X$@Q֠!X2j O-vŗY:jn]um^PRQ H0i)1odh>G&RHAF(N]mZF5-C]-Iv= F$$N#Y#!X,TZ{j.Ebf,Ln <{!kJCj uu"2⢦Ecɭ[)sEC(fzHZʎ4f[<`=kUT|Pew ,[؅E7- $`꬜ Pn>a: 4BT 8:5T3Plipzb;q/zט-@4wTJbq6 p]c8@Gf "YgZEz6bKz Ttc$%lkl`d#ٖgwE)5h$|XZU;V; 8a~w{{HOq{<@L||f9G0\.o_oN|d|i(~nj YaӻoˋJK!Eآ}uwwD Wǂ5ֶ]|d7}LRV1!gNk@dSٮ#OC?- mxb%bg}ds՟{H_ CD]f3,7 ڰ7 SR6R'pJz?e*aHEk TN!̭wI:5z`< T4 d*&it7R[Svr7Ԍx"CtdLdXE%-fYu7 3$-][ HЂaI%ܷ'eVY٥;hF9R1y@-k5tLUF`s9`h5 %IufIZ$JA)JS+MFUz;@0C#ZPqh#bT1(yf7l|I.\LLvBLVq2:Bg[%[Be}l?zsp>=眶+I\`9KfLRnz:}rnM'_(L#Nz#Kք;o>a&յU-#n YZȅɏ%}f5'b)$};`tiPC~䃟|t3W_>c >3J7I1 Ėx$U@(/O'#kכQ6tǔX Z5Ȑ @/Z]ZTݺEzah?= t+ !שc8']%CT0K1@(KF R_{;/8?۷wwKtg[C*bCp6$;By?Fi=䀙6HÛw1'Njj q'Kr5YGl\0w n= J YjUineں`bmx1^P{7^213D5HZ) ɍQhy$!wDPB^"L"vuξd'>`"dn=2{4s&E#ZFpNarMPoy}c!RUYHv$X7=/mLok{'U2ȧ%|Bƅ<}qfwf/$O^./,2/<0d9<{@e{ F T,C0!B$@]ۻ_x?Op3md3vdf=͋ HH.]N4wT?~p1|~lUc}|nwxBcp ʀ"#):6njΥ*$_O?~#Nۇ^uyo?6ÊpR;{kS$:/8/ jiap )R7,7_݅TDC$=G{$p]|\Pr葛ZU9;kЩH4 ٘ \xX￾ݿ[ C]IUօ9YC dLڌ̎XW]~:0(Du%2rMFM(a>&y9p: )ب6j%::zr},BhV@hgiip & oQ^|vྊp;ȀiBC'l6_?w￿i[8T i O|gpT=n _?ܝ͋aiؖ'%ۗ2!Q&(O 7ޕ/7ObWLV!֐q#~?vZ ҟw_iZA$C"־ک[a.?|t}OϮioG 5qƼL`N,Y2aq0My~5 zԇ}Aa{'PVRՁPJ;]N8O?Oi(9pyzqW_b9՘T-j2ݿ#Ҡ-ԣAPg'('E|@ yH =RQE7e=-/?z}w2,PPu Xap: $AƦ⌶R~^bJq^O D܎+v_묰 @%NVKbVhNMJ J; 5Qh+-z,=u! {B64ah<9EÆbH4 c ΃L)0a%Bq ܁ !rPTtצ0aIqJ)"53[l2V:=t{s_wfK׷w }ӗWg_xg/zh6e}0GW?|ݿzl:Ln<=<ͻy{'Wl H Q|(Um܌'%b;43G n@vGiM`gWŸ/}~'pU,Sf`Lט*l.4$u[(4"LL8/}k!갴qJ%$Aje80uŏ>'?'? ;i VM6Xih QǼKI\ϖ.D=ܨ00()gB) ъyOYK 9B*suWzv\՝EY3Г~HIک{5H"bkSb\#lmk.@NI.@<ӏixsa>~yZWeV^N6lP&!j犇S}TiZ7pozP/lKߟ;fO+_Û~W7ofMm،8\N`h~}{ñ4S6WIz85\8y\\gro9G6IÔIS,飫iTE5!b{rSO~ы:O6_+{c0Iqe81{(sPlzi"A>G$3$ǶԂZѹe$4+}|{^;6dۋ>/ `tSlqo~GjL3m5QWkգ-k߽%ú' HT%z/"裟W/oO qAh)?'wџ䳧§;4@=NFRR8Y y7l__[N$ 2p)0:gڬeirij| S??|G7 c.8nX[[\eٸ'6f42[#:ao^ncp@Z0`G`epDف9u~ubU\`7ݼ?͆8@vh!͙Rtm[ Q&6~suYz|'}$hswܟz:{ \.G'DFD`AAIe"rfƉR!ıxD%S$a "xDD$$ ` @&`O Q+brmu(нZ JN[>u iRb&HlX@/*{ZogD H5L0d]6G nJq1Y#F9Dd}mR7 q"l.b V5R8;(A5E0)'= JeiDRVSMdޑVZ1,'wHR 0,DC?8NyD!t dC3)db& 0oץ ZZ{pWZ5g;. ]4 @M h궬ׇZ~9_V-hz|o/7l}8xֵwcÛ\~-:C&\n?K/x|uafX(yk~2 8sޥR߆V4CmhZ6O>A B3 }zx q{_skege7ʾsW?~ޠlJVh2JIfQ -˄e_߾[,ٽ5z[Yq˨ӄHDywd9/5Co펖fd/clz3_}wo848K't0jFƒĠMw#5rE<26Sde&~__}'!Oa/?oQ괽ZDM8ǃ):r D1z")nG7ϘTH XB`3<ǼdО.7M*Z땢S@ vtfP1X єT{5p@\‰m^O)C&HZIt1\ ף-cǕ@P{g6WJ gFP1d}ZFﮆzViɄ4(cH'[kr@ #Z(!90Zm@Lct! !T"ԢPENn`K>sX2v`ݡY`ހAZ%܄Si V`ݗQ5H=9|.DdH5AjA%hjg` "z`]q@Hn iACHb\P'I9DY!ȍSpA;P 2pSսbUq LP@CEi ӐCQ}|,3\#­EDE:%`sK}~Xϯ{hK4>jwu\(PHsDq;ɹnuS6m8q;c]Zf@.S||~6nE?ss{s?¸NHeɻ'ϧ$ <9s{|U^wVU6K5oEeዛצn"/5־*:e9y=ԷoIXJ JO?mK9ћ7͇pxe 'g!T"R 0" U;_}=\crBV~n]iZRr"Tɋ'bJ4^nnO%b_$W $md?~>`-'/LyeYUU;%@W\g^:e vDKaB"惽_ݲ!f܌"L]wer*a,ew1<_l{-`{54B ֊LPݖ4>4'q9ê9YYٞO GNj9[D {0HB(c77<2 z$VMAܨNLD45A{e΂2Vi̳HUS es3St@/a=B^8F<  9(ZcWAԲj22~Ϲ8nqHk=AqvJ+kp{98 nUn@+7v"ekM$:8/fK*_#)p=>ܔrW.?}|\ln~<7&;8Lem4¢H֕6 i[=,Psʹ˩޼n7rxSs"uהޕܘ3sF]cGbz\ɻ.>~ o ދYη_>~@ 䗔·ij q;1iS~~ O0u]{8ϧ̫ $$_vn4MEП]p\m/YW8nȉX#"C9>jXz?Y/z3;+=B}G Ic{'}NCi|2gD'#A;Wx98{\lK"+Q4ueAZe=ӌ¶Z˹g~md ԼILAn޿8{`D@ÄU=|HFȡ#9SGT5I!6=x #b4 %=F pV11օaC.RU  .ks:@6 RUF 8(' {`uH'ꁒ0( ")Fl{Ҕ:w#B h1 =ԑ l%:A,gK:3҂4!pm#hVU;h4Wd4\\R.!@sLX2P̥'i9Y,Pk<ޯߞC~G mo~pLL+@[Oxp_}qS˯}斘ǒi~Ʌ~wݛeE.O?6nnrnr ]$wq-g!~O 1vB).S=0[՚k/iMYB:ƺ6EK 4Xh3M\w=*zg?w\iG/$/s#Eg(i-B0;%>᪯~~[.>dՐSti/u: dajv7O7/&vO_~O ճ瑲4cHi,W|kvOp~vil84oڻvW Un!O@g IB4PW~s/=M^jyuTT/'zb?_-70idOØ#o34 {TS S\>Y~2˦rU07̂l~ȌÄS>b]fM&ю -[fľ>G3X0'#ds&SreHTHmdzSq59*pp` |?7˺B_V@M=l♱ #PWgcojMz@ANaAKbkUcBxXμ #fQ#ه_ۯٔoi>ˏ4^rƭ3!o G_oΩC-BUIO'079f'p@9:v2b3WwEr宮{՞<=)t FdP-'Q!ŒhD۩,2EoQ2l'$ǁ5Kc W= x.ј$j5цՉ-Gpnv$MѰPf PLgOH<4є>NB QR8pÔ)@~<,.>ÈY7F EsJ{1Rc+hBhI%2*Y  ЏXOG`$(8^m),kJߞ @9vncز?ӳ?'|>bfߦwK|xͷjY4c $:{Pd2mwF|=lM[vN[?/8Ul$3CZݦ=Iry[Ɣe;bqa7;Ϋiecw%k tlDC\}-<ֵwţz^N1 `)eEXZ afļd ^`食~|﮲_o/>louqwxqQ`~NEvꄶ8h@ӻ>O2,AB&i>6z>JA[;OޞE~׾ww7I?Qo7]K]bO_~gޟG2D5hUrgq} o&xzgգ8vpՂ 简ٕc$h2W.l3zh얝ͻAqwt5QȺ\n2a]ې␴z:Z9@FFtmHAiLy!-GF^!'4sܸ*k'wc 2&Ȏ:iRTHU UB;FȢ. tNl8f 5,m<0c|e O3f(TBM'w>;V45HYBs86'͉9Y",v )YNjhkLzPv긃;  v> 3{o& [ĵ4 PE2B F@ Dx %Is n`0 NJaБEsAbnaQ]Rˉfa paH eNQ0mFɽvzroo܍jI{YiVvx8?CipzG叕{Z׀dnFIM( ɳO=U7u8uZ>p8<sB|xt=q 1DǑᆳGUl3/5SqMSۭ@Pȅk])[%Z[}#;n][d Ky)v֣Il!VGO.ow|ѰQ^ )F=d z"@t8?}??Oë1KyA0e*pя"@WVDJ 8c46HvDn"Ò@ K)y5HoYC<%Ro ; ` @n@bN1biIp#j,[bYl5 M 4YHP3<`!!C{ڰ50I%0 L6,K_3Ak# n[h018h2R4\df ]zD PfNZ\7&P`5EB0*FSi3-C T$SH)\-kɴE@g%$4H;XZ[QegfCMIpV O  LR;(DBqBRowhmщмS Xpˡ 5er (=iLy"hdgEBܚ`?2p0: }&s#J芅2Ag^w0V8~3>b'ͫy'Euivyx,\n|/>gOSo88uCKKK(Hѻ sȹW>|z 4)րݒ_nx y.;"Mo̡ i4^oBuڜ 0ɓ͇ϟOҌ5EB+N =t1O.ir1|LE-ǁ)2B$LgKEdB劂(ɗwBw퇗=a8RO/=gO>vκG[XzsX)]qkw O, c*SB2@)_&f`rz켩O߽BUWV[n$TSHIz;υRSA$l3 EcKSəU R\/6cdRn+Ahn[Wpn$HΠIN#ثE& PAyXp'UR񼉞X[Qhnm)i ]gM!T[QՕրJa et. 9M"ęPR.<0 f`‰.SF(:P¦ (QoTCXf&sIRnaqNTfᮬx.1ș1\ - M-FIrmAkV649]؄|IqO= sA4limRHSM@ Ç,:*ׯm9sk9UNGO6tlyf{}O>wR%oa,ssC=\?Y,8s[:z>X=y* EWlU;pHY.#9 wHE}D=}&!%-Cj"1;boApLBaí`fSꎹ9:k8iH9#@ =7ƎJX|>2B`&n1B0p׈hm2=NiC<9pEˡCF V,E8q$M q”Rd DfGlf]@u&x) aSdAv؁Aff2x,`= 2.*" N:[q&=͠A":)y;a@ (NX ϫ QͻoZ)uCє=(R }'Q߽x}Q'D"?= iW˼ҧ?(~=_n~lo߽_N|^n?曛߽rA9'O?zlbo|nxO'Yxg(;a Ӧ`gr]1QL 2>qyۦ5ȽUmսxl #CnzxL"R8ӹ.|uk;'.MV ُg~,oo{ )]}?fֻ^{PD*Ag5;\|0$a rW4'i ֎kړlM| 9"Xϵ"-S!6Ƹx$-\O b.*K*)((C08 c^Q tD$NSG Ċd1=@ύOyrYnK5$aLyܜ6dm޽շ7V织Qm MCnC[GS iLmU@QJ 20 @tix{[1[ӗ~ C,^ 5 N] ڑ  -,ܻ́4n)K/[8r `V68xĠHz AjlLT#K_# \8w"j昐٫a.q G?v؎Xp<$̐3YbɢdO@*A нQ^ q10D b5&E::tdܬhK_)iu!q͹Ul \tb14L$%PaDH@fD"2O"YهG ,'bs[gMi.u]6 jU@!M"\.=8Pioxs>|zo[zAk@.^?Sr.ӧ}~S/u㼞6Um˴M0KcߪcAϫ]:]\^ Cvaiwirih/ۥ_y?%jh:uíCqiI47prygWK"! tɃFbb8kr@ FAXvtz`ae/DB $<"8ј<&wg\&4Vdyכnuh xĵQg# `_ C?]W 0fwr؀YhO_tZ: xusb}Ԃ5ǨznzW+ξj @)8$ 7֌3XENV8>~'CFhǾ-eʃɂ˘wh#ᭃ3P2ziLY=)qm)Hu*B =@sհI%cuB9Rm6(ù͈8'UX!x?MO氱Cܓ [=4JQu"c'3Ifb;͘3tNelr8EvCS:E$gfFD(#2;yr D j-z3L2afW"S+twә 9RRÂgKf;XwM°D HQB]k_-3:OyeΙ ?x÷9>Xl~ e~8؀v"b'4䑚|??Jl"c2cypr4 H"nZe@w]Fl`t~/~I.EL Z>xgo^g~ċ˸h)Ox6{,`"paaE%I( 0<">Slvi1ύ~G miz>UtuVSpzwKá>x7lӛASVL#F[vVo70Z9BCW(n|Sx^eO#/'ͣ )t̤N .c1tմo_;fJ[M!BC.a! 5a%Q#!5"7+p݁} }0G[Ej&̼x; R`.ȉ\Q[D!38Z[!`#.QkPOD(6}=qsTTDK./9_\.X1.3o_~gdaz|uz&:P4aH).QI2Pl9:\\>7i=V#-w֫?7WOwG H?xVؗk$]!@X2wᙴ?x?NjQwCBaEBN@'%7 8t*0%J0j[e:tmԉU]ݻ/[Zttr=`޷6[e[W[NG@BAPFSQ-) T rҡ_ ]L/2`E)ˊP'|]n_@ S8'2vrĻH4lʹh{qVBF.xL- xm[f؍mBfLUd(4\$< TҐ }{8;ՌAvCsNKϴ^x_z>TK:0w*( +uB]!UgC ٥wA8I8 í*nd]m;'_`@"r1ܜZ@2E`;2걊o5tVҼXXBH-4-V3T! & KI䲓5gIKfd1 9qFL@5OD+Bfo5`eDȺ4D|$Rx,ĖD42cb&XXgU[3ɶ <,NaπCƶbԖf`JĒYk!lǃ#bZ[,ο\ ڑ;aJA)z- ANY?ӯ߶J/]I0fAD٠{3V+G\wT[-A>l=PzNzHZŦOv}e>k tݴ.{S*m=tsyG{N`VF|ѵJNDXŜ=,I_mprdD  \R!zd5 #??==>}<|KIt'W2䫛 1 ٌeтCmM= -hY\;Af 7m̛0Iv%Zt.0{%GT8- ̺#"j"[cl>Y؉kExp0 x1m.6S,GF(5 )Čb)f$B&#*X0@5a 9Fd;>As]`C34V&«xO aj>b8APWwGȉbf/yޣkOh$H,괉ĥ6I΀}ߋ/\o9 $H D #JCJNh A1hvP8  R NNF[8`FDhnhj@ W 10qP VtsF̌~y!ʟ/ uv G_N#D%A \d2,)ºCӯ_x{#{L8VHj g.p@5Q#)H~Ɣj0Q X)Eo6pq}9DH`59>^\mii,vkŴVҔ`#si`"1Kæ,rAR aDomB]0o-9=YysR3 fRkm}b=nTg[B1eY;',k`Ga5%h5=Hi}t~yɸ=v>8/r8/]B(!KElzҵk֡n哋by`kbIb.RW0!`UNGSXUMLM@9e4;0reA1Gf]GHha dk úЀnL6)&\V@?#uމ(H[ %ggҀPb:Fd\5b%R]?"lYՖDT͵< &@@L-B!m.E)qH<عd8*I0Еt hQ\!4d41gkYj{CYܑB$LУtKLBDF"@!)2vtK37C>'g u^d+}w?N0_w~eo*>\zl+i@Aqniڏi?8? ni]<8Le4?+~}qA~NE3MmxžCy {`W_qmFn֚::]1--%%!7@bR3T??x_{ۢn3I^|+yD' >eJDa?dDT mI2ΐ"P"CI1M$N.!"ىH 8cG i1 Qde X$tJ磗}MqS(Pp&PJ#a2nR$'GkքO8zb#bwǰ؃ !?Oa` rƜ묡T5 Rk7˸21Q4 VBܦXz3ن6sS(%և*Iip;ujj(ޢgf8mZIn=RI ӏ|>iOPiwoumޑ( ]E*ɷㆠ; t ۛTRرwuT4 {HC?tO(N]("JY6ͰH2W\ɣ TL= AѣJ"Z0Olp = A@=ТsxEFe6 l鰥XDLڼ ̜BDi;yޒp&]F"Y $vB‰|dX AZt 0?p:iZOpp$ MbLXWuXdUNƒ.'@!`C0Br5`΀ )ʌ{{S@-jz< L[I|]kQlZqP/6w|\˗Ϧ2nyO>/'l>~?/ѩW/^MONVu]B$)v>j=Xt{l%ǰlEp+q>_={Ke,W/×yZ}~_ֶ~(k)KL7R_pVϜVb>&a0p5Xkaq( /Gpr]NkwD00'O.~O{7c}U.~}'NߠO| '>rA$o.xHSb LrgI@F̰ YX#I%1ʆ + @@3 PwSN0ReYr"]M!Ü"F$(cFP=ҀC8 h%靐%&( (V XQ1; ,"XFd0KNz Zr>i]R&J2 fqZ6/|OBu=ץwMMm_itq~n1 nr WTr>.s]] ͮnP2d u0BMeQ|6<+9XxQTWt4Ab9c b! NW)f'mR*wE@բ~<} 1_raL)y!fBhY`8W-R) pcBHD-"iCv؅l (2 4 tJ҈̻ 3JfI1q4űF ٖN)3[<ƴ6 +!tƑ80 sj{gEf'GLY: YB"^9mR0v(B cs5~Z$ E.30R6NxXOc/ J..*4WS5"Ӯ\>.=rm/Y$x6|_e^)B=䜦Di=}vWҐNw_aw|>=n;r\gOwis=$5="0x譶NY(%$ΈnLfkwEpO_|ջw{W3:!HyO6m&aI;/?yNtU@ yL$,DH8aZ2BDXyB.Go30!#pb`4O%4PA 3,De 2ZX9e"甩tH[" ]Gd"fT [UP{ k%!$iKa T.q A@iuF 7vB7D#1$tzFf34!%ẐT̉8 `;`@Təxi6׏}yxc|TKI9V[fI-2,D70kf?\_NS!ڑ; S4FB8Pn hFh<(: X&]F#7Gy$ԓa)G0!Xd:g-r^=m11ܝJ8xka0@/% "@/ӸǰMybP6)1f1;hF8(F6*5; 'Hp$!R!112CԵ/koQo4K%NnKAHH$0+ @p"3Na !eA%(Lyb@+ta> 3ff 31R`NA DkK (e8_)]<6WwW Q.]~ۇȍӛO^}4wݰE!vz:ϯΧc[];<V ku拋K4PYW^7[Nؖxy~"C;k=]ߴlgo֤~zY;11"xGP7(&f#UPT]9pH=~v߽}&}zrebFb<0&^_+/Á7L<Ɣ0`J]i@&"USޣL v'tI6D@4{@ii/4 ) cN"aR&c1#cc ?~^)V'N8(@[H(#ƥJ)llv(bH=|Bq/{F'@FQ =E$hk&Zn򠳳PRU @.C=(iÍ 12Hf&F-X\nȃjFGE:0}XV?ݽ-ߟ#nts R/_n/ʏog.ݥle>. %R|{Oj}?,ed+t;ʃz[_ w/ۇڛ/^=)g?LB%X_#?ڐB;XrU־Nxa{:="eUGHTfg9aX3r7Q:!xa]Neoޯy{Yj0{+`DJf-wv g'xS@Uζ߿;=>^?l?OlZ$pɩ3T\@)=&/g x@ ~/>"Jv@N @ƺb7?[w!2e6'DJD5d?P$)j مbQNxXsaGbO#j˜2lvIHl Ol7 L}iPy2W"%ҸC\mQ^YwN` qӖ7(Ă@LLCWR+ )0tA SMꏜa6§ou SNIPOgDRar;+fD=bX ytiAh uv|/ܗ8+K4()z# A'`י]Ѫ>EHI-yJD˞I*j*'gA&5]"'r!cPFb{ήNn1K !M2;9P* 5+a 8QlX׹)к!SCXQbpHid95~t <\,]c,dnh$DH:7fSzvl7SNtXLo:,?|&KP78&Tk!iC}p  @23 21D64%ap=spH HpČ}-r򙄲B4pDk?AA-o.PڜcLm9z0)5'vi3E>1UBCԈywb05#La>i;aچ"cNf@]p[|ц!Ei#rAԈ`0!H vZ :.m(M4CPRAEE1VFvv`~%06 gF2@!H [޸gò eLHu؈7LC6(2mkř+CXOHކU'3>B9<@ha!@$nHUK i[Gb^9ȻZ;y4&l7o}ؒwoO#o}z1X?=S?_>ߍ>xz|T QPi[哛( "d;֏b?Ovͼ_yTR88H"ψ%Aww8=z哧7_|vSa͗_~>y_\~>xct}xo~{9v$N8ULI1a@Epd@%Kµ6_O.LfNk0 S^ %? ;?l滓kz.z?v>=*P1D H֙@Jڥ[B/_Tk\w?YYNu'?S~6"<;1N#Pb$ރATgmM%>}Û?8a ˋ#ܾE4:ʆ9Ei2ͷem |i3RFXi"4eBe肎G(pV=Vu<:P#]t9`z̀@lK ȹ/: "bqs`||G)'8>"l ::A$Ir lIŠ;!J0T sof/1jVLH+UJ%xA+ge +\d g[kfǻ~ו3*N, ڵӰ=7''{6k5hmnhB 06du_aL83a|.A{Gl onR#qY8߷?^s}_'Oq:>qm _ )R8b7@HoG?1:miǗ2$]d .ׂIڏ絩Ӻ|/xzviE|4To?S3>yLJsw14%#ZŒ|i0!M;AD8~p>d'cdjNj<VX0Qa(h=5(rG5*j,#3as֑9u3"w͈R#eNL&$&X 3SwLڛdS8J*C4sPAEPM*r bGR{_68^t>}}jx_?c1%?Yo߿{ԉP1ν3dIU) ۥfۋ?/>>m_Og*0}>L:Dm RO6M|y gMC®giW/l^T~5bYC]ޱSIn ݸ>̌25pdmO./zۻQodtw'8FNLfО~{/Osڭg2Q(yMXV <=6mK<>,kŰ{XKĞfJUxtQWH)ˊ#q3pA޼{$:NC,1CSBFu@1hɡ3Nj) RNq̵.(ax>Ŗ{[hM%3oIj$ع5mI =.vL 4L5lI88;@( V #j2\ `XVT<#{f 3@cdi^'wLjBWIJND\ǂF'΍p./R&#YdzR*H]Ӟ.,Rh4D] r4M]+*tXmPO[$R 2 5mvs%O6.|3~@_U˹w2c8嫹ڊ"X"g@Z%,p"9$ ]g.ꐊ\4Ch>2qE"^VM.n ]1#H{l?j`j+F6;F+e AYŠk;@\;AN\NFxCStxJE$"efgdžnH 8!:Ąՠm"ѵH#DPVLXDH@d8bjӶ֜Dq3F3tyHI`M9*zM22z#a#u hD[q55s"X#5&@FD\0G`CLs1qH GX})?z!v; ߽?<.c3z_w?_O?aog=u< ] G0S\zSœrisq᱔'?luМtܽ_jK PjFB6Xy-'&^mzZ۫ \}{y?;ݯ߾R3Ǽr1fCRNA;5?{Kz[)y@vX`uPpRb|QyEx4x_Dxw ’(G1E/_TcfI&iLl$ܙFI"!(E{ah  6X֐SĈ8&DfZW0r 0 W,%tȁ"DnA.+F%`9 ,̺ҬDA/g ه%`iFAŰH :6-2Du,Q;Q*LNc' =݅[SQ2Ol=lvN= eQI΅#GgK]|0[ke$݆Hr޺ ;'1܁KzldapN riڠ+ahc,.R8;ː|Y;X&c1G6 -[[fYFL1 ;n" x( IB0agX @Ft  耄QIzH;!"3H̉W *Rj[`2cZyk `]՜WFX @A ClEk#:Q-iD Sv0b'`B]czjRt bL@b;̥Lfs0o/woϧá ',~x(\zpg_>on\(kWЮma=5]=)NHؕR^|>O}s6/z9m-Nh{, : i[$OcDP5~x7s>~?!#x IZ[Q<1Ph Y?'E0BJĹǵF7Ȉ??`;?yHͳ3䱛3)E:OphR2$flC "8FXNtjHѺm;C2ja8 D2eFp<)*;\̃9e.5 X;xIJF'ް1xwm;sJj}@YQ-tvǫw-WW@ le]5w"PrPvN@+("b#W ,`etP蝸@g30r`k˹k8~j%@F9,zM1VqsjʽEf8_aG*EktUi 7ǑҔmJ .>!D`H0'2y @h ))6(j^ 2@ lQ\pPֶH@-_规Ř%d5bQiABu`!<$Qea@ `' <Q%"6F0g9#)0LK@4{x.j@wCDɃVg DPNIFbPs.1*(bxb|3*:2L-" Ӆk3אָLWcZSS.HϳӬA`dww]=myeׯW[9dK닛0۫ru!qf'la7@yrqُLZ14 >Om9Q17)ȥj1 !m6l@{h-z5{vX QNHTLwkDI=Б!}(af7g{HޢBY/ 6 i͇(>xNηw+Tt8:yOӴOO^<+W8oZ]a3%N/.Md7-mWχ͓'4"BA:.{ FD5oza#W?z/v:WkڥI#gU2OH]7cȸKarm_ t< T2}Y; hz|@o>}>nn08'R.ӔIh.o?fhn79 6iXqxV3"C 94@ F@D]L6ky=LeؐVLK~ wI' ༄ knySH -XA FJM'L!z$=ul -zGP 6APm(ۉxi@&hWdlHc b[iѨqC0ܸTUFIb40y! B:WU=P{# ]>|rk=S7@GLeJse(.A4 D9 L;դL"@tK S҈CLM'G:C*`Fzp_HWf1 Wސt45bN(G)Hd2z π懵GRDO\8(B\8񉨁lX;[,V "tqH 1FI}0Ac(p2fޱ5 $|jkOB@!eSu94$7Q lxLC[-T] k34u  10c`cu4C7H,e]7F2F_XNA A5 % u~xښ>#O8?nyٿ*˻oxo~O2XS<0lwyv߽Ғk?HFyǾ=ݧpX+q>u훲uBa] =z%#i$q̫jK@[e^=}~fZ[oi$P4c'Y9FQ\|rB$|\$41;syē[:ߝWG d6J0f_.c)vN_oM#AJ81C(Qli  ŗ[ TܺBEvrjq!OcUXlt+  e5Ŏu{2a J0s caB5Do!,z&,({WټLC ;ɀfrx`b'&d,KH ܇hI[md(Fn) 2k8@z`&5FڤA9F8^8W>9 dM7Rg譩iH* ;yp-w+!7 zy8p>x=򓟍>Χ'~OWe'/GO/>xN|ww"Cİ{szmoS-bso Go~0N ÆsC-(#Q38dvӀ*ҧ*0@7s燥AJ.wob|nO 6Ųx ,[wND@묂^ 'LބS@J$L(C3о3oJ@M]M5`tΑ7^'ד=9W'Nn}]-|Q('3Q "-l!=ճ|?xvYVZS/NQW?k]xF^O]Ҳ"]]B+I27 -^jEӡG|F>-QJ*{/jڼ%b?/v+ yKkCuu=?ܩY`7<@O#Fz⃱z1vnX"šY9ܐ 5wu#ٴ gNzH LU,<1ѾZJL"*ff@˽ ! B*{f@JOd "h{ {xs aQK50 8zQl55#7q'.HZQFOc-m=U764-*)bC$N(p2+wI:Ek"My.n#f"zŒr"P"E3V;ֻSIxT6Ro+ Ω;[P" Ri`|q3nw[ < Ï~Hp d20L\#ޏ-Ίpi)vs[JFLqmh h+ MbB=KG!HCR}¹-V_1V vf`.H'"#WTQ tDm:zgGhc`~':'!;!l3θDGF)\Ƃ503 [oVTޱϡj=CHyŒ= xt]EM}3twnQu)[um sӺ%܉Rַ7e*r7!tBRԴyt44L(f6^vȬ07œ?i[=36$6h$jP TYìLaܻ7G|E9lFkj% PʜH!M8"FzDд E=&V)P6hAHA»SF,3хv@Cp0ʈ'N@tِc7(i#u[Za朧nZH;8Ԍ&I\C8%>'O3.zQn =KE=~Û{Jz2)~Q/n>)OۇׇS&&m.kُ>ku͏O_ӂqq:b@țHM)mӐF(Tdj6wU y ߟɟrnZAΊ\b\C,c#howzli`O/irk7TqX;6i/ <~_vvh3e? ?GM*hp/?g5#k]pLDm Oy+$hU;%0H#KtBH-J o{DМepK|Fn7 P--)&:9*(}XS]*g@273fc"m k/4?zco6h,pB~w CD$&J9`j (EΜ2%H-OfNWH 0FNbUꁘ @!AڎyAj֝tuC[Yzb m64$([eh yգQkLӺ~i;$ +{?Xx}noě+졭Qw: yB ֦+#}A<AJ(]˘JHJyƈ90h!iy* RH {b}H6p GǞxllA@aD;E`~(l%>H(AA0]L`Bs(dL-#ABC)N&Q :@ѻwuHL{?G7G]mYV\mD`bAf!ÈRH; &0?' phI'Ru`kVN b;F.=VAs ;)2{~є}n_ޟ?u˱y,vO&T+v'?[j~'(0;,E4>l;eȄ( uR"qj]{ôUUIp/ܜ-RkȷD: )|!\^>{Am?)䃛ث[C*NGa|z?L/xc*)c2tei~%"ryu9۬Y ,: tC1!&v^'!2C&&wF0^0F ΂.QN`c G-XsՑ" :CT$uP 7!6(vg@ 4֓Aڱ'^[&L) #}Y[E-< 2V pϴQ*")DMG$$4 gځ$1)Z7hB$ኬKSRJDQNv$Nb5ɄHW񺢓pbɀKl'#h]hzH8jgBIr!,AYO`)rG}ӎaow $E$&big7 68dYF8Os:Ґ"/ N!:GbvF^H^1MP*դ/Uh P6)GI]# !et 0oHIW24 9  '89m9BHz8"p mg B B P"XU%dɀ1M9Rxʐ;d0qXG K%wǾZ(T6TG@]#<j]+$J@c٭7!93{) UƶM.Ӌ)E S"MjQPqE?}Ë?fR_oW?_~m O?{ao6'Ÿv_\]26!92/KavϷ>bdyo7 hۮ']5wfsg($ X,i1wuHsΜGXoz\r%-e^mIy\0]Y^@N?{E,/~'|ӟrj5E0@K-Ez.`06 Dڃ~?7;lkFVG rwMm̳yuv=+z^9thCԩ$2LatΩ";xX0 tWVz-$EC QM Ӛ"2'^l-`F !'(Hxu D^""XS[ػB0A%D dؕ@Y DЪB+HH"))@=H:4$Q0y2#>8SNRBAR)}NcPOamFx8 U8cFF L#ʵ'ˎ Q, ӥ:R;8:i 6D 2 a!y L-H/4R{WphD`|c!49`s#>hx,1[it[I #Ѕ'Z+6e#ÖRyRED—[fs x@4#);1@ ygr"HQP=& '#] ca栒72.%nG?Uzoփ0oPp q=ד,iXlY:,`^/HǼzF#)ՇVȦ!E;p=/RkyOfW-wlE7W+)_o͓-Zl1s/T?KZuMO&/Rz'zh_XT1D@rpphM,gN3C(u)Y0f B ϳZEt2S5AQ q,wUXcqG;/f2͏F̘H +dB)<EW# kA pա % uvGG'『Tj@ 0 QȊ#H8S)ÆСVYvrcVlܡ+AmB7ͅښq;͠ Y5 bC&ðABDZC!(Vc wGյǹ73PAܡ)nȰ. q8{zyx'(Xm!%S%0P  )QDM9$RZ$aSLeلϞF@%WtpGJQ!.$SKA;P 8J11A@b5I@LQ-f P$M)%32!ȃ5U óv hw}%{ =$+  2N/>qں=h[C/)vOn>C'jJ"C?bb0P0@[Hő*`cvv#\M77i6)OW1sEy8݇rd.ʧ6nL\hhml"?7Ǿ"nt뽍/~{:W>VlΏXԜ%=Qk*wn@f#+`76OhfI=Z8yg"maRZ#N[Ic@W;J Tg!yqfPv=dG3e('I\A ZDIj ?Q'Dn#q 'B 0 #g a@`E-h$+Ep"0$ e LPkѰ67{y8@gͶ|Q~OowǿxǾy: [DUjr~$ٛ?OO?c<|?!>mP`M"pyֻiOFWϷ}v~s:;4>y5=G>ENNu4 =w `ajafez:nḛ΋0vp>?//|y7Ϯs_j}m=̀<ⴞ{xPZ~etsD ֩@ tp{7"X4Xдs6N)%p(ז% XH1R`N`S; `75Bq$)3 X0 3x \%a ^,aKqZ=zTA6\;C_%`"2[25o@:Zb)isB"@:l9S0 s5d"!DFv﫞8ܡv t_#ڽ 9CzH7$wځ\7+=# v뎝T"NiȰIJݲ, ƤHfjIucNν[#LF51R` pIpV@rڸ3%aL0!LQV7uoJq =֑xBU@gYD%a$22$"#`A\ e-1"^2m28!e}0$ 턴 (I10 P[m=ҘЀ;5"Z5 m>W,`55sC eEg1ٽ "0UTÐ|d M-uFU.)ޔ5 untc5t1 <%NC Ղ 6Ǿ0ZDwqovB X{ha<@Ы-PѺUiYIZ7ڳALAΑLB 0B wnYu`4n$Le,iRJICBI"@o5B8!!)pt@6@qD\5"bP蠁)"4(#ca10IbI 4 :qڌֳaj F=\׶VCfôl#)E"-mXFT,9:NA2aW|vd.jH/?^n~itdY| $J_7cuK  ).f`]fc4pI%W@@ /nH51jIFG@-G%(<f멙ksTl\b`~eB躢=zϱmApAZ&n@yYcҰ{4E  oAQ 3`®J#B ! ܮPI7"㰞wߟ洽>l0ZDu-pZ<|a?q{!(3ݼs~3= wO_?-t8^siL}É8 ӗכ}?h8*NWcx?y}g N!CFE)zI)W)GPr(=6X;ٱBNHMP`q5RXG 5I"5<Đ;B+@GP E[vGuV[c]@Zw5| F.Yܧm6$xN B` ૣ F `r1@tXD踡}wq#]-pB PvTWЮb[pvb7kǻ&l#^FmQ1f[A"*Cꃌo!0s&~V] j]C"0X08'LSrĥЮ\ {FC7ڣ8 D.JJjuNU(feȃ(Ll *cT)"!n31=+P"#M`b7 DtäƁBЀr#' dGh};=~k6Jf|9?|ü;eax\-Q璳lwE4\x\knb<>u2bŁxmJ ج 1OK!k %_V0Bi"q371$ӫ~4"n:__uiM_{_o_S/7?o e i>p=R^NI?yзl<i|3o#}=4{xͱ(EfD9oa$fK W[i_g`=6,N<%Y%A#l-0z09J{@z2GZ0@]И"1ȄqhŠ`=Y URqQ(hHD+aH-j9`խG]#3y%  \D@ Z @<:qv=-WS:R/$)7,p*h *Pe"XQ@$ 9,,]ICG0Cbf2d`4(G@Lz0إ.<D.9wը,y(.k8@)kk(|.O9t?6 S0g%6.~TĐ(mCmD/3XHΠXKHXi;@*}kw3)e,A[\+}{wFJ=^p}( Szpzw{|~ G$1wqe}mV7>v83@hp~1PY4ՖgEVdETmeニ7wkAUq7솾/~_<۷o=~_lwW6޵qyuq'?z~uS:^i2$_Dt?V/Η^ѭ:Aaz^=e-M}ǤHLH+@:`c Z=Z)CUz KR6􄘐$p膇|AIxDd u'_}Nݿ6аedεa S <ƻ~{zGB8rϞ?=扆x ߔ~~|8|͏j{Ia)_OۛϮǗ'yFq}߾HϞ~'?'WP G s9^gϠ 1Y?pSTO6y}r_?z*R ֮,Zu:&˒:P2'4%B[7YH*xC8Zvlb4@apN@`D7qvu#B 0Fo,w_0āu !ðuQΌ1_` ODbJ1Ԙ $p:lnTQ܅N>n-3SA`%ef`fy š1 c 7Ek [AF0R$Xg,t͏3VlNQ{757,KfÎ0%_nbd{1ei P0DIx; !Df *$T! Y<PIV=qh HnA=l-q VfTP^%l)KhhXa#43& D@x >#8z'%ѐ"k膞!WCH 4;xVѬ^sC9Y%z>L@[E =9i*N8My X+%LDl%+c*V5E#5oq KnFOx%Eq"o(4*2+ p>f#EglE8—6әjk`t%lX_-xɬ\?<lqn˶\/3>I~OAObbsɧd"'Wey;ywf#w/嗄o>Qk]K},\eqwU,mS: ;\tO~r;I?|.GXiU`ӎ#m8$>,m`~?)(Y8]2GûW '?o߿}=bcIxSfa Ʊ`Jl=I 33DlkA@ fAց+DZ% 0SE'tqJǂ0.zD#tĀA :n r@d`c0W p \Y#7j!@F[Ҟ!5 wlj!'Cfsh 4Bl)Ta *.'s@ ѾHֹ9O66a:u %#f; cѕUaiPfňkOS^lY ve&m(MiӻzzuWqN`=TD-akF\ҵ SvsP7*ƍWFNdv=)+PhM!&P䦖hdk:]Sî@ثYҶҕ!f s$ZP"Xl1&E6 !P&b "o( "Љ- @a2! `PT1 f P ȒL`fB0:K^ZG C( ;E$6$agA9yBL" ٣QhӚF3:{ i^l{\$f @Nk+q/74Nk-V E\4)c_lf_<mzôA0޾emop8 OVO?sˋ_jU=?=r%ݰǽmS1 pR'}O}zs^Vct 1ouy{:/K#M(m&|{>.g[}|H"Kd\=k_yoﻣ7 18(2vOoU;vjq+6/̈́d 5HX7`"PdADڣ5` Bt lcHЙøcc # '4w$W[g YR_VFLE֞ 'vCД\OtPF8idEW4P `X#L %<Сa$#5m@mwɘjscwA2S,wf3vb X9yQ\?KD VRʶ'< v֐(pvw| k=PƩD0]$cj@î?nj Q9W?t/hzj#Sb$Ҏ//(: SܽOl0'> Vݾ^Ҳ4(Th>,f짙DkWp{MӘ>5d.=q)>?<嵸#k{[~_Bi i( 0ĜD/Sq]/oNG_#T{.ڇW7!Ky :/Џ4?>O{zuwy(lΗ^@W".qyӏ~%=BzwiPjb &T˺C%f־λ7E l!̼ᔀr0Rcs- AF`9tz",;o\1jLjt%) aˉ%#)c8b5ٽ3LE E9%ya(N;fcoH#Na%lU`)0zDs2!:#$% A]4phוjUfZ d|R*ԴyN0{=?rmyT'l0ѓ@BxUy[0998%Q=H.D}\26ΈV Ba)PJ>$ib I"^a0ׇ!YoMܺ(1W[WzfnN!!vȡyyMT4 c8UiF(A7i|ӒDy[!b[8 ";(>q=(l 95`t#52@h98p;i8402RG70Pvԝv/ ͆(S;̹I[pTƭl6#!Bx h0AM!t< a N>nªKbȎ.`GV mG cyQX#&Hp&}kw~Qz,F_uعj=j&@ שl|3~2:mm>k_ J]vxg8\MAX|w~ĘIVO?>(]iN,60yO^Ûµ.d)qd?q.8{~==B =q8ϧj ߾3;cGFQDEڌP㨺`6SJAoLC移29 Io߼[[P]#F3 5<;rSҹn,y(E*(#g@G&8LFUa@h-dFFAxΑ](L`G`YhǾ @BJCV6/NmwP#ƽkx:يtO^n@tnwku!]MF Zw(`p_ԴMO:xmaSFǯO9A{ ꡾V5 CFpr~~ئqG>xˬ r>F=Pd0N[kQ4 JBTqBvE#j oonv0/YBJZN:~hIFL _FEm> J~}?_7a3'\;%d6|8x*8ҰiOez $-hg@@@,.}E*Pb=d ArZn :0"d نDdc # d"rBVa3^0CL`_%2 Tëwo>\kH#MGm0WaKa4]V_a?3-@Tdcbh1,VYO]wњqpxF R}u̓WAyn5iZUkpX$ ][ۻjs4UFt[NrR~e> g.u*9M-XbF?uI &D"LrkP`[R GMB]@I*iޯs5zRϢ8(v6I"p:!C4',!Ep"0 P8wn _Pai@w" !$(8V#0Œ'(;"p" #hD3W;:UNvl%w%hr *4hjg!XZ܋pnjQ0 KP*E{B{H%s#H\*`jA4B jKknӺVmJe=.,i"^,L!4jAJp~:c-ЂMgA[al c6d  @WZ8U'?0P]a5b@CA|=Ilڟ޽oonF ^ M<n-ڂMķ dR\`EA!J)N!chK9;3H?u`0@Ĥؘ6 {2yYZIIG]8hfg@7mqa\ڡBD#Oys'*)a;Ų}Ǵ*` |V0twWf-QՑtQ xk^$^,Jjiy>/0`YEs<'j +E&[w`[º81PO(!nN {@@ZS$;@ D K5F`'Z"FL 92E0d" g~:I ZNkgpǵVIqikٸJb^ Q@d2 <"l#5-jaQ+ypvTSh tM&C)F87\7vxy`,Ď" E/ z_ߡnR\4rx<o4&S)y7MG HЖs_})n; Uv}Yjt]~w_%/2[x^(%$߼of#2;#5}u>?WN~Gُs&}d~Yw{ Y1AM" 9GFd?nK+9HU v*3'(9_?/<7 eI M;{/*exK8/4^&y޲AdQD; gj]+R 'p$5(:CoaX! ='wJԾo7Y " Qr.Ū 1\GO.OlK{Y,1n=dq(Sֵk( AS'iDN$ד$9P )cm5p" a'4d\,d< G)8$}uo, ̉q , fv" NLA ӊ@$v̡+vl={fO/Lpq|߾XcX"4_jGWNuuEA S9_VP3888DKưͭ,(vDcXϦK%OStcm5ǒ-)0{&AĥYχθ#ˆT1dHkJt9.6iw#7vF5d} sˬڻ.Y-+S@@ `T$`Hs YL,Na䔋Ԃ TON[f0M6Eo鬘2 qbD9@gj%!$s`"6@P6 Ȉ A1(_`Dx P I(R v iSXZ"iTX AHā Eoۖ]JH"n a`.`@e,A@P;I-#2nJ C J^D`S)<$$ ,RZ֓sZ֛ZO:st(M7puWH (81e,'[N6IZ׏߾~h6H}ٗ<i^p:7|rkzs8kW__>"Pyi&oF?v|}Ȁ#zGd,7ݴ@ jޖ{npT:@$",$Dңe6G@3 `$ɂěe\@kEo4+@@X-81PCH]/>ˏ>\<^ڒOwz0Xܬwow:7;)@bEMáչ͞ԓ |^,кYRGllLD,2$y:}>ݡ׵n_.l7 >qcqs?]"zi"TdӺr мHpf[6Qt$QuJmy’t *%gLD)}qȁPF9U 촐,!udIeiD, "!3cyUP"͖]=zlB<`8=8n/^ᚬ$I`I瞶Daz>nq;_Wc~GrmjSM{J&t<ӫ+ASPq)|T'91'ҾZAĄ.$ع/+{2” S5W̑[~ z;;g}|;8}bA2d9@q {P1 3:րBV4RrҽYa*NzΨM*\'!ل€)K{lfOH fwDF[Ցhd0F!fbtdk` ā B F0'@OF( AbnC"PDxtBhF1*aZ4'+ɍk84n2ΖqՔCW W)4Q)c F_@ac P$#1aidmDh~G:z0.QNJ| :iЊd31;q _29^hF ݰ{;ڼGhk4Ot\z8C_+}vO~7mp4NJaNXOv:,E@iz77gaOɇ?W;Kva{j6O_O7~yhdpHL{%ڲ6eg޽y/|\9<~>/>|:e˓}*Ln#ߵG;%t']{[̨UĜaL 5z8Uc a"OPd 1Jk3[@jHDZSPZ]RCF( f1!J@N"v$3" F&195)RVSsE--'[~S#.0%^H^`&P`To~>eȅgG$=VI[H qy+KA}|wsҏ~Oڧ;}8ӗ?Txվ=]=wl/b|[=`p{[bKu4iڼwߝ~vaƥYژ~V)?(PZ~l=JS&>`^nyڵ2m/^ϒxsˬq)̱kSF&< RA%VB篾(5 fg,˫qMCGɻmc֛s뽴S^3 Ȣ-Ϙw\NL}jKj+fjdPID+z7/`=0̹"݅`!J΂>7EX܈[e,ȒbAI&)=uY=Lj6{ hzfhi].l{.wpYj`L>H!F=2Lh!*ZCOi]w-kO E( ݺ5țgWJ\k/*L%Qc=z$ЬяZ 'UO 1%a|y>=/ ,h;}èL+d` $OTY1Rk{$PѠ3pg( ;'@^CY(%!%V rt:>~by , CG ,`pñZPn?|>ͯS&ϗ۽5= ^~Ծcc]DsC':o|χ=.9ۻG^~Cg[ 01aVWٟ[l_F>V?}7_w?>ǫwA2i3A%pC!92ӻw*+&3ok ӄhԈ.^*p"ĎxFxm~mzE'7)X;ql 1sdV V;UkKn.#q 2 3c7y?aSO,0O0P"6TW!!@7$jtYTM%A)gK({mkkudv#7 vhAH)~+&!#b<C`Q21Asbp"t@*` C<p׀NQkG"*vTrP&l{7UFWMAC2eGɥW* HH@9,2*uI`,c쀁D,ctJP"r$`;0 Bؘ2x)-ͻcFYWJМ 9;];ڶlY3RN+%iPs_] ϸ߿~7ow;r/#I[{7W{|Pov_~B4\LAtyɏ'?-QJ;kredgO?]qgcRjAޒuχxw ޛD1vQ}9ٺ!Z_1_ֶJ ($j93k3=8!RRz84, <x冤3A1#AEj&kDP Y@=)cxs.j5('vOtaA AYM8$H 5a/Kl2Ea6ʹx\+ _^k'v ɉ D;_:GknBL0 Àփb9wBD= q(CFE=A6a _fG, 2 KNTK20jsH}=5 8x&96> $\2Lc䀨86r7_|?vFwi/ˡ.ճr9}2x!sKmePɁ"3 A)Á 9zF=85ʍs䨆`w]eI9qt0ꚄIV6)_ ~V}_6̞zs٥aa&΅La؏] Ċ4  B<@`@HS10t3"vp &@tS`{#R\WbFA50!!XU)RlAZ|]]QDs$mEj. sK}uQ3eNaFg w]BB]aب9`(HpjI C1r`RB1AGcdhlq'T;RJ8 xwtż :wq~<ҭB=MlBӠqݽZ 3o9F>OyyWϿxx\+aMW:#iyHX@ a!|z|`!.򴹈g^fO~_MGOH4[cimǹ ~խ^sG7!Be d}qB,#oJ<Vù.r{?헿EEa'n6o~󯿻?~] ej6׹OT9;*L_0C^O!^+YNZ3I,XX' W 7bdS5y0<ޝdNH!c{-8Ir"s@;$Dy#!,OF Bɐՠ17nqFNUPhY([Swrנ )j%vj=29d4cڱbz@XC\1|QwJ=!wF{2y@ e` C: #f3t=ӫwFI7'7v߿#x] P$QpMeY+xۋmXטhm|Nd@=[.n\ꔏʻų?gS~>zɇ{z~y]q\Eal]޾}XyIL3WL7ex^>.Gi3Ja{ٞr8_ !5v PwuF`unaZZsKh@y`]!yK9Wƍ*HX 5rkHMLfZTnٕȊmzAMd".%$@)z @!ΐ 3ހѷU>\-"XOx0Xa Lð^opr>B܎ N~~j{uzw'S? xYW)z(dBGX;Ou4أuY6Yv7 ib}'8 /x+'p#7Mii+׻M?Xxysև*I>/VWǴ.'O6N׻I.rK*3o-G}6._׿.C̯OZl?ʇ.y?y8S64hA0(JdIclX%Fq)uNcc‚Ҁ}JB0 P4 !N̈2Bbx #9l<Fiu,ɪ!bm#ȡxoM}z,I b;\#<GpD-V=]`hlV #4 BA V6AH@SqB(z5L*.&6p4$JfL(b+~@a Il5hVxu8e]Êjf%u~aBn'f aּ0N˒wׅp$mҷcaSzƲRX N np*PR$;4Q} KF=(a 3 & ttЕ@bgZ h~k K_+k5z8Lw(ud("K!rF!c9q!22C(>P!ZpD!1@Z(;EDh1qa5$ HJH*#8KXw=9L)#+`R43H ĠcDSU04&uƱ4R,Xml- ʐn.ʮ溞]#m6rLNaa|bbw>}/~s7tg%\₆CxG=>ܡmH٫{;I yD> aUvOLų'q{E57ǻ΀<2osNO=7O?/y i-J.-,s-\&"g.yʰ|/?==Λ)3&BiO_oᶾ{^Vm>] ί_ߞgJqO3Rg:qzQ.HmeVPFuauAZWP1qiҫ?BtނAi]h-[' 憭!BEP ,+8 m3,$ewDD6AH++6(1BuFb`X:L[2PvnrX`Fms_#1;-Aƒ# KJ[0Zr8ϧ1߭DP, ȱv!b+ 3s̸Pgww;)ΨR1sp7M-tM $o}^==0pkّUD5 4Dl؛1P⩰ &vXRF*>l"oY90( gK@$@ @$,>#%<Dȃ R@)1 E$ Ĝ9Z#&`G0d[퀎TrRBU+@ƈ H,C`RNþ2``nwdWCL BU1!UF,6xN.a6b"Ckś\&b]BTL vOe3a~}I"Xz$y#5>ڒ͋~|CqofA|r .D6IMZMda NC[}>=2 bD3mS^I%]J~[ܗO[yH)aw5W/b7lz^Oo^߽ǓZw<s\a\M˧/?xxLe2m G~QC,79ah} Fó-il_=º:PIx>4@Rf&vDCMQi/@"༉ C2V[?KthJU%5svK"JHH(o +l(a\; hDR( :\ ySVL4%`wp43["6n+0"LN[:[.^;P E0+;[KE8&2idyp3HƄC$ʎl`BL!2vyDzNlCmv5 Û9Z2kUU6hڨ{C5;MAZ@" Ð7וêu7 3c,}t6H4` #9 *($0E ډ;ȍCy1]], nW#,j& N 6!n%YBUS,CZplcXpM BjI VJF9gΉHB@NLQ0` JA0996$V ` ALq0šB :@Oރ "z8L3`F6l{`!Z[s A_! ;8.90"Ĭ@lDH[$ Lڹ8L5Kpv=mh![+ 6<e9'p 6I3uowe3tI$=gozCߜw{=Iַwo4Q@~ᄏ}`xNiiH@ G23&.:d\,CȌ*a @SEY5L (;5-8.ݫِz>n҈kp,u!)b069CM֖{,!,$H[x 5.9D3ddhFPA<ByP"D bDXA !h)\ (=0P 1Gpx $ w ptuOh=;yFLU"Z;N irrLnC˃ҵ 98F8HA=036ܶ(cHHH,SD!pVA렊SFDAvXYpڗ-ΫR”zʹIk]Y_)C<j.ŏHǿw[1e3ʳe7l;KJvgY q1L=jrAHhUٛc]899ZeZ#蔡@01Frs  iSIlBͫc@HB`!0!*YgppjFFaZkp8:D'IͼkZ y0@ r3x5sJ2hfvkTJSfngd)80e6 ^9pvfCeSQE _N&q)nɐٍS̬IWWPn2dhIP yDD[)=!)K Gw8vB` Wu%NBEaͼ1"#([h]؁)R@f % AD$ )Ph 3DG\"8RP  @ @ 2Bx@ @(Q0ֆBW8Rzbo <3[m -9 V2`{UЕQC›e&j8Xpn'| H蕀 )1Y.[oK-y^-cAhI6ۂW1г˴{~Ӎɺmm?5pXn>}!"y>fN7M >I2NS*T1 ᭹Ϯ'yP&{NCi 9=SW%^xN>&?~ySwz|cZkJj{rSR|`(bq b2z{u:UpJ)hl?[*5inܑZ0 'AOXLaIثØU @!PƨMWs10%'vE4 F„D,@DGc jxe{Gt69/VG` jXG@- 'X)!JMr ٙH]MPTAvDdXV&p>R! ;xvVdB뎫z?;*C_"N_}swy[ !z#C%J!#PFi^ j8Ƙu],(IF\%|n#:fΎ(eM.nb=vQ"% N E Jngs3 %C8*%QVmpYRs. {`̪GV 9Ab.i=Wׇ>l= ``F'ĊAI"# R )Gf$pJ ԏ 04w2@Sٻj ta5FBst؁$ jÀ` vD@Qw'td cѪ6kd$\`;o0o690g,=yֵ-)m톨y] st}a5 8 jmm퀦${PPiXwb[uф̀vLX}a ; `FP c;'ɇ-msQ0L], N JH(M6l\FaE8`*0dbi7Y;pi3c%DBڗJs t??~6 f`4HBk؆xh fcDK5k!v"Q kEr.lԏ4H) -swPw \ݥ#C ZsL2$ }v Wr0Ek8WJ ԫiXvdWmdCWGJk@zLPǣ "2@M/ly<}kΈ(iCq=&Qb]Aj7VU!Pb mi! QbbafLu]`]LɁZ^K= V5)436+AFsVєRVɬ?j4'Ԇn+dYzS`s׀Xq h$deR&Ip#IڍWƂuA7Iģ0p bFsA80RRul)mgoMD# ' LAh B PxHEl@t q@DW`HD)4 9 @B18 c,h }Dlm>;6c4lfSI Lc$BWD♩;Rs &EkAelہ;MUP.R8@ xrr@dTV(~@U@ "uuW Km0C];[6J́,`Lzy3>{H:%T`̊kg" qWo߿wY.cs;3"x*ۀut)øVtѕR$ w˲Z /oWql7 ռ_}!BF`O^|ѳas%SJ8DC(&e>H6Uڛ7_ۗWg\T^pM [xX->&hkwBՐĭ5bqG } V|Há]>3=ϧ[$`@ A?+D+['Ňcpr#Afig ‚n ++(@)Ӗ#-00(<@mq ( kXKHAJ8&a˭ 0I5 #_`NcE{xxlU- BV J`\ 0:Хޞg֓<7[_k#7BAٚs+t0817u<>OSbS]>Qܪa 2OO6iJt]_~ 9Sv1w`,T4_1O @J̾, =h4( `D@g™T$`Ċ440YgDp5 3%&-4 R$A b( D G&513e06!=1 =2G8 5D DCHHY90y#%OZŐ3 P|@ 5;MhP@ bf fQ2򙜜"[ CNr)զ`#Gǜ ^pH޺ B (|wq}rN:j h:/2=ޔx hm*:wӉ֎8֫{ZOD4}0gO{g>%<]]]n;™ƚҸٴя_< !E?==O`tP_|o5 4P.7ۏ峏^?+.æjyaflM;0m Ǵsj>ޭP6:cbJNRT_F5* -idq%C 32tpHC 1vy~A# h tS`]X7"h ;HZCv0`v1X ,iVQ; 6)TOg>|eH^x,|IR{Bց*^>uG܆AjySHt0Ru3sX#,l2Op{@lk&N2LnVhN5, 0pBۭgLEZS;Ӭm/gj=[KCO@\0[`]x\#oģ"( Z 6MgN#.sd(6{+-4SR]đ[BWP`eؓ$.D+*@<*YF" 5UŘjMQ($Y(1 PCd nJc* #2a8ɡ#3hA8p&'BL1Gt$ţE!8" tB6PB]9W uL圇0K00m$yJ !L GB[`"u(jHPw SX0JW x>:3X5I4DZ6yFz{aIJ_mOF/-:@O_?t2\|>D?0甇~I ! f4=ufw޽ok7 uJZ%@SBGe rE&N3UVL@-OU\B|m|\}ApL:Ph,u1 Ϋ=>̷:J *)M<_qT!PS9``'057dB-6[ֺaLdmsɭd`f:@bp^k,J2evEeZK[:+4ړO~`?A> u A5 .q*ydb831` 7t @ Ϟ;3#zkQ c@p4@c!zx"Uc=zļ~nCB YW%eja Ѓ5`:Pǐ2FOg(Ð=0C85߉d]C t }SzD=?cz|\{(hS.}\Oy:qndٚe!0gؤjiY7 &wȢg50!`%;IX@mHe I99+-˔ұ!sT.Y4onD.+ `Emu JEڣc=4au5hi8ͶocsRϷ~>ױ9~GȇqJrn g~SSoA4`[$M2 SIWA.9[t6s]?/m͌ 4|<t*zd 76Ў6arakuHSHpx\.C:Nle d DU➈!b3ZvAa]J<Gfi$e-pBb) e]x6e#`L jҌͺYKEP@ e!D  E "B|dս%#D:8ER P@w,h3 $y Z🕞{kĊ`s32F$Lɱf@-z0А9gfHDDsN"ے%AJ8{E! @X)%llAg&8M-jc`͜l9ii*Vxb@Ng7.~tߖ{paGC^^wwol4WFyqͯNsKO(/>{4OØyLyb}ne}?nWHӓamcie//\</OڢͦȔc(4[hv>>|20vKh6sJBld 1%lFrs7O=1 DA()1q!"C!O1x0"k!]a;PB ܐ+y@! R4gX!ePqߴ7ten'm*磣ZC5 a~>;e8l'w};U=mhk" }#Ա΋_9^)l8Jd;INn4uX w GT%C!Hc Z jt)yJW)o hxӘ'S^*9MݼJEk+i2ԥD݊b x ;ŧ|r4B]"QI. 123SFDI!1iĻ3 qb@Pb2Hoh$f*Ȍ5_1 +pa)٠ ;`焐J2E䴡'CAA" J @1 # 5@PJd`A0!P2 %@@D@ݭhh6SXjv ȴxo$PW ,[LDJ1(fT7Vƀ |R LPr މfcĶ(DXaB;2@]cZ6Sfoߝ|aʻ]~$?jI al,䓧p+Q<goxD(XC7(,;qD)eXxs=.NFs?oS.Or>?Ivt|86Qߏ~'~0}>^߽nZݍ:r\7ϩ}|s7norqm˟z<%#v߿?}2Tx&S>DZRcW8ߙ8n(}^d.6^i&RLU} -o/ KWbj-`Pѱe{%R=<捇M2h#r+Ap&|d  dPE&d;EÐ%*e!V0H ZZ:Lף|C2¥$*Q\#NJ@#x3)u]zB@=#ET fwևwfʆSl',:6yV%m ,ݻò_MV]-y$'0 ȊKRJvP*:6,e- yrMR.m! y;D++~^*Rqy-7Q39xt]#U`V̌XsGLy =@9i H- 8נ scǠMFV&鑙}PYU2ca8z[1:`=gaqyee,9} @Tbtp tPDpc`3Z Rg+<50| |;qm~h߾;)y8MO͋O?\_/9Q썚SDAs4Oveb7nNQ'OcYPC-h ELuuf ӾG=5 ZFԕ'-P&!52P Y^aP]F;S4 ưvd.@}PP7LKF lEŻ!V{XG!_=t_=rK!ilО7ZjNfظsn1ܷH]FGv&t&@'DhPrDEՄyaJOOI՛3 )4B7IJ9J$ 2v0&ʑ!TQzhN@ˣ[ N `F!l Dʈy xh Ѓ ڭaD7!` P1@D0D4 ax`BHh-#2 kh'i Ǣ1$#O8Wu_ȄtqdyED(Ȇ-pA ;vCx0p [܊:̤ ǀ9z0Pif&|&ɟxׯ_ޙQ. ˏ-!nǫͻ NmrO}NBTnn5 1ޓÉh#t~?̥=k=uSI Dh{KCy 1/][mTG8w*)qJE UY}Bym3H"̭l!}7o^ ,'Bhz0Jb}\ f +AXu6!D@js&GC$9ǘ4&/ݱ< Y84` e;?&o6f%DŽEɛB# W0 `JP#rYB9,XԽ0*&V&^WC8vƲzϙ;!bࠈl0yfS)%#2`h0LW hJn=˰:pMiB'ؠlH5bÈA@sХ ,;( 0$@ dEӠ "2   cHPD :vt 'sg!kyU) 5tƜ#[1fdD pa;-WtxG_{ʆ.( AV  )РHCt=`ڢceDThQyx*pcݯn߽~=Ra!gcµsͦe;89 CeYOe9 %ck#'0ctES ?jw߽WlJ9Ut5\ )7Y]ay}5f?)W_<>yqܽÝმqQֲqcNї÷w|ۆW׾tGs/0pެm sbѳ:LScR?HfAd 4@t, 6bJp>9!X~whu,4]6jH\A0B#0dE lV1P;eSDŢ$1Cj unW#,Ga04K؂V^W V @tP>jp̼!xG$Y6>KhuGL\lGfC#QU2TL0>Li~_;Ɣ)uem}Q$Lc"NDeNW'rDΧh;&oup6u= a.٫ܭ3#̛ _ ]J98I5da"UDo8%"ֈ@@"SQh3rc&C8M/nG•59 }QDzpqH Ilu< lrΰ Gu`#F.G- 0Lh,bDP!P /H ,"C=_@ (E G`DJ@Ȉ=wH {0BGBp@sH\ &FS-L"D}) ҏMcoiq,cCRd٪$C K0"4PW $ľRsPҐծ/'oďo޼{3C1?ڗSr?|񺁗uifӀEUwkˋrqnq@1XwĘ6c̦y-Jm)w{xFƂݻ9›M$ƪ}˝2SlOo=hW4nB]J[GKh(d39x7vDNPl @&3'5eaϽX- Ao*:DBu+b P CF0 |aP@(AA$) .c(1D y )"3aH` jR8Y401) "fdvjǶXI=Wr Gsզz TcA,L1YxPh  U9ܺ1ov&&Qcph| /IԷ/^~q " t1I .Gzd#}ޕ!Uy9O6r6[]CMuOi"($z||gm"Eל\+-Kyz~TeHYXRHyhgC<* wo~4> Xγpo8ss27Uoޗz|}ƻ Tɋ|tϽ yԺ)a5SzZ)uׂ` AܼΧȘ߾JX*UW+ ,}>.IryiWIzm0-GFKemt)[7Cko<|DDo Q; yEHZ SD Y(bs Slp~- 6xUpJ'ܮ =KK_r%2e}Xf0-P8ɥX鼚~0tͦD6FeK{djۡG!3rBb7OޡW +8;x1j{3ac` F4eè"dssf,e ]ULҐFS 8i4"(!c%d"tVDX qEbu5D(1l4ӆ(G^%M@ C`"k Z 1Q! Ha@_]U@s#E_/ PU R).my83a_gJ°T8Kd$e Tٔ2@ 1 kwA+vp j>hhūѥ<$_o4/wo` F$E;DwO:"k7ֺC[?{NyCUmOaa&,ez4 !\{o%e=i8̃"@jb6x ~7<Nj4 LX(ƛƒo o纞/< ؘ H|b'(nPYd<͐GDXumIuYn3N:ӢQ3Ѵ%0]+O#{!ҾLi zZyyKE0h+VeM[z^zV%Pٍv{Hѝ"Z"X phVwnY{-t eS@Hvb*^^`m2AȺV(Ap_|A߾{ ov~Mow컷n6X)0>KQwWW+oaŷ_?NH98HC/j^xXD$HɃ(Hf3o˰tq#˘lW4t \ OۑFYf6m!r24$}Vꦴ>BXv2pAdp(&;&p4` rA_D]Q [XAR_z6DEdshGHs(Xu!H 04 %sT H&"!rGRJG@$b(D8;Z " 0l"@H 3#AƐDD;`8vֱ*Bޝ R@" "2jWVoڱ1ݻc2T{7jҘ̤Gu7tCDZ |zٶӆ0\{Va¶` c˪LosJWɍL{Gͳ γzoi؉vSfLUZ !k3T0ᓛ'7xW6< DP@ӥ/B `6n개*unݽ.uY>I$s*p2(y(NbkP f>:E '8S̾ϒPTFHعX8{@7o@R7(` iS0"(fxd (bDEȥ):K6T {,$({$@0D* r@5Avp`@T +B9CDE N=W4oaHÎg@r Y0(K0d),`ޢ9ޠfwSj6Ehƕ{@{ed_?+-)TRoժr LYrFUgZҘq`T[78_ L3gnsC=q٢dغ: &[tF ճc~<@չ#Vмtjڠ$1 fsJQ4J@6D*B`e  :%)ŎsQ6ٗcx<%s R3c+spx{]5UۃvR}=ήnYë hk`^mo+m&elu%H.LO5w"%ݼ$[$ i$3(G2L APmUk7ϫ[%e7{k#eW"+vf[H\%0:a8芺;0"'w@'2c򒡠sB֊ P$jI$m%#,  :bCFJ I@D!@VIKh3 V(zL "*BFO:ctD)!H"XC A$EXU@D^! Xc_!"!U!"0*u _! ڑ[r1ih ֡QWSwn Kթ@]0Rr Xz@@@m9 ufr :q .|BRD)Ӱ]<92:wow׾B81J@)1Mew_736n<]|>+p>h^r~q//v0r@[g?x0{\vEA=\O^Fx/yŪxHWSpG?@v?޼,`|:6p۝.K.lnբ:~l;77W!n|'yO?Yx}_mx(ܭ/5HP̬\F4!f״`\TJcND(Rl+"r2벒u +nh]c"bZҽ"aR£9tuH6 RǩL݌oK ;r{><F5{@E  eC<-HB`c8v8|ϩ;Sc%W_!ΊHvjaYuu#iiI8T7Z[[b_=.2,KXd3P c A2@R4$`wc" hj[J6RFz:piГ|$u 545.M !$0|yR$]ȉrrDs`_$4) . "gK La$q&@v睂)46^ a,Q!uŰ8JTqlCQ'$ 0. ᆒP6LD$xP%BnD 0##78B ׀M b(#@4 L1F*7Gtp@&B"$ l)vɜ Yw@k )RcⶶD]5 LC;"ܑyoz]{j{s;ۥ-ݬgl ]3_7%Teҋ͋A&8}B|1]rz^ϮPXr~e<]>\/{{b?||xُx&oഽ~>^)C8s?|91!z̧ oӂ%O"q'Rv""Ȝ876@)Q ϵ{ l(eP=]˶Pm1ĉ# À©S w'S6 i +pjFthBuW@'U)vm`Vj/*#;wSٕ!apG,^"14_=jTk:$moC _?T+;y= vgjOv۽'%ҫvtj5ʸH$y ! 5l[LCdh̽p``IԳDe,7 |ŀq;%4Hsm.&C7̵h  @:Lku*AN.Č R ;»\/aUT-  SJcD5 g= Xzy c&5aPÀA7$N ЃR D1f D0p#m AqBV-DKZNk`Zc3(؜ɝXp N%M88n RHn !z3wVU&d \;( d`̉`Xb$PC,KЖ."B Ԅid`÷{rfTtȒL{qFZ:ez_ϽȐF~n+e]fu^ۻ7,zuQ?t%lwT4oo/>@t3]MEN-w?*+//9崥abnwwm~'rp\u6gO4=ٍf'{/^/~MoDۺ>K@o͠ELz++P\al-m =1-@xPN%cZ 7$aRh$Cy1DarWeAmuM)p;){׵Z檀P#i.91 ډ!ʀ9$Xo`^w$Td9J%ΥifV"))іNyShFgS>ޖ˻O}'VLGdZ\C[,3:e̅)VqzsOZLH8ub2yf l@COs Q_HzfΛar) G7gԬ:0:L#`Dhn !{:R3)E428wD4pT|N!VDmy q:bN03Db&o6 אm:QDD ЀX2]Oq@i쭷U[kڲy7L^݆iyYq(D \b{u|}?^non Bk.#вَ°_?Pׇ_/>l"^ûz{XwO^?Vƽ/?p?y'6o_7{o4Mm)U3sm.> X]s%3% 8>2 s8a(C :iRUGϷ&覭HʦScNaUZ%u"ür⶟۶lmm5T {`FL)aS*,֝jƹ{Zۈq]/bK!rmmќڹgd έAGg*02Ez:cޱwf&Y`jPהX("D%k(.]ukos>m}}^/-nu;1y #/4,1%닜yĹ$SpG%0J=:d."}8{[mM{C:ƜyYјKLw )B}l:gxuoX79ޠĤ>qbūQ1MDFraOl$h A|L NΉWg (5JDCgn@#*a fFqQ4.4U$B$Cwo6U|$Ǽ}j}u4S]ϫ_gj`^;{I(I.il]];_^o~oi(/'GhE-~_|EQK?ooooW.^W]9m<cTmixjvA?|oֳb,.]`w֯;c{hQ!G8_X+Z ,uľkD/t{$,u4<'=e nu},4O9ӳugVǥHm[ᱭL݊hmX7C͎ =$,r`0 Q}YܕǘJE܂0y=Ϸ4SA-SEo/f驜s޽\]7󐇫_5}b/Vˬ'AUfIٴHpNػnD}̳dogo X{mBu!η7RZbtȈ5ON_fKOk?uupC7V- d؞0vby>IaR5sȈ, -+>pTBa P!୺}^}swsXuy-7)N:h!hFt%zXmt,y#鰗↞mǔGo;3h!!TL^)|dщMYH^5-8T"G>kQɆ",pd#KBErA<"bMTrA$4c$1Rl59: fZrA Lh EQJ,cPy%5dT۾t+z|>fE -Xܩ* D A Pƒl`pLd!a@(B4L()qMD H.DqcH(Y03R,W 9Ĵ,O>wL@JS3˧:6t ht01wx$p IMDHXpdkaFi6&VNdQ ̡=jBo n/eغhp]+O\Dw$֝i>n[?-/mYZԴJqm3/}կͧɋs>4r*Ut??>qeۏ7-7c!?y뇡/Kwy?ɔ·?MwOo/޿~ΞiĨS1 lJ[f2PQ%0S )=#00Ⱥh:N˭ L)JEP(P4)4JL@ޡP޽>0ಲMrYk>d;|UZTE-GkIRDl CZ#0: !344 jɵ#0|/q2t%/GtdGj,G^CP&tN' a ip#S4Ȗ;g]>K3#;mzLb{/rw9]YE^M%BowU6c& uޢY-lDr=β%ֶ<'}SV;|a.H }dl>ꁭȈyG 'kv~| ,&/߸Z,ч.GJc@dߝ= թ5chQ kMR ̃Eԕ30zg}'.;=;\@+ Fo @ UFAF" tNo^z.Ye =UnF$%HOCi# :k4S#P} F>x9v9.aa%'a st)eĤcSo#SѺѯ,Y(Ke' 2M!$//nOnru0ihx"ϙ9pHyv•r䔢{>q'#_?_Oij _4}__4._o߆viꎬWZ?*|Vv-_qq}uv|#|Xﭹ V}z ]W>ֳmz{kaz­վ{pQŭ1 $*jom^ao^Gc&k}[6,.W*8Otnl\+bzc~=.N6=S ׵!ObMRH;:}?*W˟Ntz߼Aʚ7?|.¯1%Eq7uO6hl29#]o~}9OǏu/Ua۾D91! l~D5! ɤEND;IBB‹sʁ!@K!t#J P4 ̌, NcCd k)|Ⱥ1F8 !"E2"cF Nc8b "OHJnd 9B2`߭0oмi9Jf[aty(bAX{ a4OQD1y"15 9ߖIǞ6<,\.GJk>vˬgq_hܲӄS.Lbm' Pl@^nbZ.6Tx.j:"N*Ba\j;q"pMR-Ib  ݡ;w#y ʥY|0#;sun-)q6^c8jLS`'V6Pn2ic^z>9IS9`{H2 #<,!|1Ng y%c*iN ƳD=t;n9T ʪmɆ{H>:jN7HBttİ%lTomVK1JPe@tc!z9;) nʅT֬f$𑝨[KSm2|@4gr4ih)XNœ;]€1SXS2! W b" 5UZ'Nd)(,FjU2%Ls1mg{blѽ!ys5E-! rJ9ĠT\`C!I=b00}`<󰆫E)B4 ID2@'!*QH(aWd=̘Q7SYY`hQtH:_j#`Dܻ涂!)Jt$1&YeK6KZ3U۠AC9[/ 9G֔(32$5h6rmZqm]f/ۏK'Ȭe:8eطj-ls4b-]9`CXZ6zkv\)X< ~C=~~\Y:CߴmO:4K]/Y6<ݽǧw?%_N8OwqN78?W}0Cz,!DĚs~8gu{ Ecķd J"Hy4 䝽'FB$*Vu`Dw#`kwkWrkpɹTJ28,ckm>rZUWuQ4h5t%Pcleri0yF3Svl dP>hPtΙ)ݨ/F15'17%͉?Uo^sBXXk@r.29 ҨyG;5o4lﯪ*i0Ϫ޺%Y[OŪdg%bJd!ϺD7bLD6#DP6`UN,FL,\+q'LIPH~/FjԄ>#dWwAlۯÔ!9A\G:fq0! J`P &!<Ѹ 0P N3G#䬄!`L$Ķ bpGI`GLb,1%h8%KN$Nꦅ T)wA@< t\y:Y%Lj" =,hdpGSPrYƃ4L#ۆnu C5ǭ^=[Vm>_A.KEi}b2eبu\%PzX(<=+q<͢ju `}ϗ]W?iI8pУy K]zg엱?siz?U|N6wg[>Oo?|p_c7󷹃7-m"/f ƊL#Rf)1 d$RX(䎁e*)ݲ(y[hCSBI3bJDFehIU=SB'\45R'ٶa4(r}J'9>wz_Rjڳ=𽆥ɔ:ؠaOˑxuknF(ؘ}COܻ8[sIyC3T0yy)a.ܙ "N Dy&(zwK(=䡓3oD9 )5G乓s4)I@kӬ0e8_jtU5t.b%4M%%4 B5h*rfJzsf䤦4Is]{`iJl׶ 1dpLn@bBLCG\zsZ2L1s4@ƭsl:`j!0Nģg6Cl CalSsN@dr䝒pwP0qxX4H)-c_Ye]d(2FX{Ld; `wwxHpX=BA>@AFe) J!9pakwv •B,7{|a‰kj9%8'[u)`$>y8.B`c8#zb#̈o_=EnaѬׯJYC<;lyb ~(uW-V߻$}_/?>3K3ҙsю:ư˙ VK)< VUy;gPSTE33c*J]IhlB%`IYjۍkioSui֣Eӕm0d{mEmub}m冬K;g)uj$ct̝!$J,9z~ DANHHҜA>|bjLj! l>:5D~onrHTݍ 3)u|(<1gU)aA>2bdzPuPv9̒4-~wJ0ܛ;w, $NPVcx42!/Ys%8dѹb@9<4.jʓL6Z^:(8ch%/+ǓW^~|\?~WLJaz|SIo?nNps;ݽ f]}Eÿ-xOQJN%t>>=f-t.ĺ(ǥ3*GJ3P^qi]?~ݢsεS<<[#Pzn()l^y7xj(%LF]5dqD%r1 &<)3+/|AN0v)oAPkp|1B0h %3FR `t*7%i:M)- 2L9f L&=t>N,[`D?>0c)Ӵ$L))CqN2F$J#Ab.a\vj/!VpPb7Vbmvk0M,UXJS$}gkl"&cJdS{+c@O eDk4{x} BtLCzװGgNAFA)y-]Y--DM"ICD! 0ĎHpes8Eh"$@NN< 3DPɓy Xw.{e9Pd'&Gɸ:elR3h*K$G|YB˵>=w~~ҘMgyKݷ&ݲ,fIDwwnQGlQ7Qr6I2汴ݛm<~}~_?o?x=wVC:e$1L<8sW'[lwײ%jyV4mٹp-ܠ%O} {} %7fnm%ޙ,;bx'eJ &D47"t!BhA=v+| [FP+Q&"3J;H O6 a@#a"@GlD4¡&fjHkE#aթN]tL9ǵ^F feG..Yw#FgP&4K 9!vS"QfKڮPXs wBVe6XHʪ2E5x~ޯVRn^k=.C<ۥHfs';^ ÝME @\9].}WNgwmVX?!Vl^~?xKk_|c E8oo>q8@N7o֕.[y}<7^>lKJ?n:)4RX 5#%7"Z>+^>qF:x*DH3GOmxoO"p ĥ>훆v)Ϗf%B.SW_ZHci^HhJ{1!oklkҾYmۉM%3"x9yi(B2 'CaV~C =ʁbK!>;KRKQ恥zM<=o5_yý!r:X͢~^{ :ia63ʘ _+dROBU %6KKi3DY%/s|8wBzMHϜY%' {2\j7.^3h4LqK1\@>3ee{r =cpQt TpʀZu"nM9,ï|?TߪqU|wx}ܶ¦W8dԱk3 -IxPu%SS:{1gⅽGL.!3som͇psj,_69`b`dAi*$Y PQgȬ DC:ҩ2$)).EKмD%+܇2UPu(}PdjC&Pe()܋n;|ddp P `"bD7=hD`o8@S0 ( tek7rV J2'6 kEbXP87`eM*ac)jYlTnawFLC %NJpVi $ɍy1YL&F'*lXFL:=] NQjNq15kc34Y>ȠLwo^Ο,^M&i)ǹ^d۰?= 'IBLSS)Dxmݥ>1z<$z|筡M)cջ_=!2ݓa40囲6om6yyO0ӏrO~š_gW߷ȏVHHB'ka=u,13rH2.TS} ^E/I fNL'Q>,YklD0 n CZ]/g\GT dVghVG>za۽Vz8[7~25#*نa%ί^#TjޠFTl&#Stސ#-Q=LLiփP)QTE*a֋S~a؟f<`{z5E&UD]8eՒUih1MdcDZŘC03q6h{Xo'oOX$QK䲇ө dfؚpKkczwa"8$t=(yc튼Tш 7ؖ{yM`6xAmN06vQ,'"}ߌŭyX;Bx=Lc(u} `NąDU9zO`D\]. FNG>R" !(AUuJ.N"3@$ R;"Gp?8X 9N ;:c0VG};fv^i4A Ae"n1vZ0S`-"favcQ$RK7J.6|QŇ;#ܽlFZ$@3x/{P r)%7xj>eJa͇Nh*pnzy21B@W/7w0p:LwԶ#YK~mT;,w4Rb%^2Rc^n_)>jvM! ޞ-{{Jr:?\ϗ݂=1ށ>JbYC*\S>~l4z;O=1[-ѫƈrQlbkݡQ;גI>P4Hpaq%TNK`6re%XL.Lb콆3 ?m4F<%7w0muw iNwKqAɛu jջ}U_`wkv@XOtN1@}7kts B Z-DGO5o]}{罝?~~=o_˟xF@33YK`xWA]DC dpJK%M3MCP:-3 1{s$(3B $A+3w𙢄 VM8 yizV>꣎c9MN!M? ,v!sm5Sm?)ojJ>VG& 2䩯3^MZ=J^}o)9 36ݗ>rP5&TVt"Yķz?S!F/"yNy".;y$i,AǻZ?|܆KSQ܈:>FӉmG5Gồ x0'cL)I2yGmbijqWЈ.E*BކN/Y/ eو 9h4aD1|B>܂ { K$ DwkYh&6 )&\EGhsc@- u0c*ብAi04x ӑ؆lLE)!Qt-w)! FFUxhՃ\u=!~{I 2JKI*:Ɔci DԬ,\ 2SnDcfrVʒtR4)|Y@B,-b4E[ [Di\}(9@:|n$jMR+uL$HLJA,rAD3)A%E4ݜ>G4bP0EOm2- EQ6ױw>2tЩ+ 8xrnOѬo.u^> JL2:R` CQRb)$i̔±hp& w$  "#DXLNK sZa:,<Ҝ&MB4\B`NED@fy*C,[@:"(0ROE(b`@b>"}1sxOh(7%c6%MPᚲGЅPrlEzw+_?6Sd.z5pV1ȍ0/phѻH 5r10v1'jNePPB.*>B V%anA Խ AAC@u$"0tc/^seZ$ aL g7 {P! "'. ˆ !+";#p#f)`˓o_eoߵ?j˟:}Jևуt @QõXKsDYST.)U1YL#79(Pp2]x$4EHeN  RzЊj&k>0ڟX;W''}x*L??-Sk N4MO߿}<ֱw呦.2ɔb=czPZ!9Io>lʧY-yO+)V^pMwv>?<>~h%!݃'֗~JiNz:}:s]i7Qq[{U\X #1Fw %FBd &gE%u #R:,OS4uGRHeZu*Tв&5L{".&u_"Fl= G$} IG,lgh=$u 1D]m~PK'nbXuض_eHlkoiug)et/Q%Η}i^ھuC3w;P2$m<m$rue{N^`c[kq}t_/~\Z#4ѣz߮K[ GF6Y !):;SL1}HVC aF,pBI; i_LaD@nTGj-$̽Fo>IWs&f&FA܀v &Q #tXACSx 2)*#2y9P hG1[s>.VͶ]=<>j_{|1\i F$reN4T}G{$2 9T=KbNPEҝdX1. g aLatwNH)ݣNN]vx'w/>c}mog_#M ޮv{SN^'8_wt4`=^׹_w}{:^:"KA=I>kc񉽊%/gJ1FoQQ'oؕ5QWO?]{Y=2#=1G9K^/?ͻޡLc[ 2;X*eNV$;붷e"rpyZj0J}/݌m gV#gysIfRȭz Vp0l=kظ[2^y4AeoL{Y84lA['HԿ\Sg_?\+8=Yu\ OLit:h>>hCM)oC瓍Y!S yA Q[+sPEhv؞EjQ?)E+5zL^[]߻39%gϡHD朒o)1mU^.u @XFp Cjyȑr@*{Js$a15<Ԉ\vQWLQb)ߥYTH_vkss5U2a H{vMڬFVuGK*}=8% %e2O##QWQ!&dxzzAmg.,=@y9R4a}XH,^njC9pɚ\Hgp`AF "![Q  L42BPw6AA G' @ɚ ag'wA4ouW3aGnw??>>+O|%&cU~23KɢAA]ȴC;HȉtpRSr)%nd>-2IۂE'*4&FOGw 7?H PB$H=8"K=GflѫGWi}xS;o>OQ*}?'}yͤ |Sz{u^KQë~{zh >(6r?hɟ'|_L罝udqz&+HmǾp0*S^lrLzz~}7?YM4CS?Zot?/Ex9y|<_ug p΀#dNddp{[McaB?u,Œ ni}Nc*A&q^GA6bpEPS laQ)iĩI䀨ڴPd0MhADcL ,V|o2DSG7utGSau.Sw+E1v7Y0k̔K^,Oz6 Vk9AF>HY60%0a=$0-F![/i?st~F8C%I,XƔ\Aގ:X))ȇHFfa&JX&iʇM}~g%f2uKu0$MSFz] #^:Ա;|5ٛד;xnԺQ+QGlCFTLDdDUwh@u' 1y8RrprrNF؂Ss`AA*ވ|ɠ b,zPpӾw(i0 (!l'(|p8SdPD '`#KĠ.37m^nGո<Տ_~w]}uP؍@ D#XӤ(` #9'rP'LC}~*fzJȹ*:ȜKwdŤ(yF 2HQjYlV'rfߓmkݸV,7t?#GO>Ϳ.vKo?{G>gCx)6mOO3,Vvc4 WMۅB7/^jpiGJs#*Ye)o~ekIxɇD1j[>G_o_/r۾ˏ|~oWn*ŢYY&Yo2u_^?}oO O?-ͷ攍̜\THui"E(_ZJ9.#Rd6 ݨ.um*!.w)ˌ1PG"f裙 b+L[:> CzP56/3**ֻM@t.on*Ģ:$z7O6{4jDZ\Pn׈j%xb%M6>8YHX2ǏuPO#yOݛư ؞mg|:mn]:<=J6慤S?mmɒ=`fLE{sI%mѯ<#"&C0YzAj9yB41-DP TNwG,atP8"@aAD1_CHPl#21Fc78nkO߿ߟS:jP H6 )0GVͺ>hjg [z"N6s4c7ZظN^M-lLXݙf ]:Ӯ|o|ͼ 쌧 $xHF)? #.%a5R0v?>ß}W?/_ 5o֖˾=7?xdܗmOeX 嚦&xPAjciI<ֶ{VwUyјNQ2Z\ztIS.t<ߥ̷o/?FY{@vrOyKU>nGOzܯ(1lGo7cdXĸOG^tC}Ȉ6GX`_dY5Q14hV"r馚i]7_'}6AM[;a^%S1/>m{4kQ)Svb=IHN3֧`%-Z ]40%3)3W2dOCO 18<]x ςP=6nz)Ne$#FPb=ނLR},HOm1.)mo?>hy:F1?y9NHmm.BGzmGc=}{Uּ?҂_ 4Y%f,˩0*1%CA#G쀲0 &v3D fAA=:AL NCC׀xa6d5}˻޷-]w.4d]F})7-D;Cz9&u8`sp0( 9UvxC3[l9Et)PcLGqdH=3(,\ 1ξ#<,eԁHYF;W֒&/_ß?OfmǷxGo$O {lnaF{JZ0Vń阉XG Yü7&؀Tx v;4PJD0?|OOw ~Rdn^oo\^Oq9Ƿ~>OśϸW}6q1T42aZ UHv5C{T̚4Qd6؄ l12Mu(]&ChAK l43l4vjs0Gs[.Z: : .:ϞF55L61)Ǘpb)xM73AvqNFb3xe}IpI'du<ǫ5S(+1tݙx"NS!o{0ӹw/)eyߣ|p__ĝ7Xl%kL% [K"ww ˁI~~4 0{_CrEk#'YGeC'3m;- FߙOx-.lu$$蔰Ig?>C^# efoDKO*𶻦Aɷ3 :HЗr:>ƪƈ:$ GneWje3{3~Lnh>خAh8dPb3d)iR51F7_ɧ vÝzP #ʑY((`=X5A _1*[|yww|kFn!KmdRHv&(1k1ܼj(YRJYȋ` q7Jedmm*LJ/H.Apā0#t#Dp$4| \]G%!6/'iוe`R$?'ܟ~'Q_|AOOw{}PNhEs{{+kh;=;‡%/'7aRHZZb $6B8z2Gι[}9XiXR?8Ė.W4/W_~2=__OB'Ӕ>ǔW~~O^7o߼ݯWNKB<CA}s36un8%gQ}Hv>|ʌp^5>,ps,7ED13s r)_kʁ#i6v1r}t.Iz)-4p?,阬(:E MFc2%vmj:;>u1Z$'~jpPiߋw`aM,υ"r߉,g0ANiRro(i" 攩ەPkĊJ%(",2w2s0B8H Q 1Ç5`oڰT|,7?y'}>?Wobu=Wm_)/V˂e7ӬWPJƺ<nʤ9/˜iC M5̣g?y[+G>2/c1[6ohWDk}euٟȺM_~ڞ|ݞ|8̯_#-sJA7{Ko O̯ߨE^m3X940C!9g͞wtZ_z'2 $Sw>|\ $.W3@..)Q<,G 3 K! qLՋ $78XȒpLSY p1sp`M']ua9͠WJs^N"n}dId^ <1~6)cE] Ãu@)A-z$wmu&sB뮑c5FЀ]%mxd9d$.CtdR)ԗ)IxDƶwM%0nY#q)jX sZP/i5hr} NPA}2CSy頦,hϻN%jY2>E$4ii1>Y_~Ot5ݼm\i·qNHոFa69܋Phۃno4y8zR9y[515ޣ+O]٣ !J>J gxMIɠz #6ɩ2PXcTc2˛tiӤE%q&!b2#KDIFs8QD0 ,)b@0zpbKӰ}ׇޣ_Ïq|x~n7Y^Ǐc=ѩ̒E0dGc"ÃJFM,E.|I;ITs,cnu{ $竉"DRno[^--G-9crm y}_H'y~ݏ9/c鴠7!]~ҭ\?|6߽ZIdx8\ @?HAcqF̙m\^BknILV"8E[(niN Nh{D0qA0- u77piJ pwZ1)MS|ȉ4Š#}՜90 lP5nM.|]sq40D>CB0ly=c~"n>[_\^|ӛz1vw L: .pJZ2jl>3e){盃(YJ.ib!t b[{*6v4!-L==\=/cazRN!,x~a_8rDon;IQ31S{ML-"ɀD;%Xݹ>:eG{kS:h9ixdnp"X7CZЯ䩛i:m@GS2N\͢~v(we:$ ((Ũ#F^E pL؉'Qˁ`$Fp ẅ́&Į[jݮL>߷:^aZ(KNt#¹,,Vn? $re|mp!1SM3&K9Xx0#[V) <)+S֘(Ib !#f$ht QNy4woF6:Y*TO?_w a{yzu+ӱ|ޡ ,$L1pH8TN6*LRr=sN0dliDݍ-~?+FTڵf6NZ ērԛ'MDYSLpD͑!J[,ŇGl>e!ᢔ9T3:;hI!"wvNf;m+D}u r gg@2ert#R̉01$ q0u0PݺXhnFFồf`Hdݶ9Dk"{*âBbL>F|;kä-2͙s& D%=;`h1%81qPp nDv1|5XDsd֟q~ÐLPPJ䙘\K)S0"f/ $r%ېfC2IDpֻ(Ē=D=޹UNLj5sLJ&6m)V"58bdh8"2<,,Z?b_Ο{[JyqǼ4o+E0i#r F KUJIE6%UyIZ-)x >Iz}~, S4,wpC>M/9f7ۏd*4e}śh\t̟7)(>w4o|YX{2sZ86Bz>j/Ur{i~hM={gJ/ȡz{+O#Pm2-a=n6a"oĠ^QsX2'4I#t*DLLM}lF`(% i[w ꔏζV"N4R"0y3NSfУcx:Hok1p~xx43UNJZQ/N12{vݫ[4}-KR{J<B;͋̾:, >t@EuX% 1-b;1σ|wZ1SmÒ nZ V$vd- 1rDus&)ol>,)CX)͇L#Q!D;"bG'͛W>~j\X#,ڊo2e0ܷЭkG͉-|b. g1 !ia$S&U#D%y=b`R9M%/'d$J9t`Qb`t7cfHʹ3cDgED~O_ݿd/O7ϏƯn?/^5IT5X'͜sɇOmo1ЭqܟN, "K>2^zyj<%!.\8o̘O/}קeYNoRon^~Oݧe^f8wO+t]k>ΟNwuW9|rwR__]>>O7_w~/>g?yWǟ,q>}xzi5OSBEcއ$.bn%ţ%ryEaD#a:%2uHI ԩG8*;4a)h{)yN Bj׉Pά}NIL'؞GfO>I NP1&atߝȦsh|Cs))@4y*ШADP&&DiD@d2N99 8A)ED &dQڎ$/Z<^|z;ݟ(m}#`6 b+qZW aMC8@vwn!^$?^-IeueKWcQ )q^PWk9܏lq=_?Co^z:ެ_W)$}>-{OJJIC;Ϛ-\ cPw7eu` xhWʾZw F,'-6# 3yc\L;/?~޿߿6 hKYE̅is&:Gᩤ{I).g.4DXcN+'24iËf]pm)j)夑IoT;A9x7gBCU2`` !خd"y3ӯOkGiKO%џ oɷ{SڭEP^dZw_54vx٫o#MۼGjw`I7/O]Ad޷7Zrs+]ޟTgLۻO?y_Ool|Wyp YX=|ywOR|ĖC}zڶ(Y?'o?Gdzfwmf}證E4r$eKjERRS7BmF3y4уXɜ;FsD$ N!LA9u$mo@pf&v 4q>(=$Z"$q$%c<<z3pevHj_|/ן'3{ 3}hm72ÚŔ0F&Lr>?ק=ghނȇtlN4kM.K[sO7l'oc߆1"cQBÐ2:/'IAᚊfe.̉< 26s-]}BuUA|P1Ft uHz2;]~8A{x#ĝ3v>ya'^ĢL{#06ѝ ܘQ@AGDD < BLGnQ8ۇ߼>|wOZ0Y(G>"gJE4Y< Y;9gX&+!ht!B&oA4F "}*BX*FvGZ) 4 3O3hbmĕa,$8Qۅ ٦cxtJ^O/z|?q flr{w/?SOn7nݲh“}蔱=xYV c%˝,7d.Y@Y:<oWύCD>L6(/G]Y{χʖge,՟:屯%M4zS&* En=ήG. 0J=tRդ %R"],S5=ĆC$iN|P4$H((z&m-KcL{58&d&4c"stAc@$0V Q4ADA9s hĕ+A!3- ai  @F5TSeD;uaUY. _丼|9k]G5{Z;P;rSʾn8^z`4~YˬXwʲknsb^`A׵YW?Ol -B)O\d)y[f |$cAW56/b-y"hb3ȩD'nk5$+%Lэekz 1ض?YܪxtoSeeNd*g/N-P rK@)h0SskD񚼟%}\xzۏO-VM#%2/9G0!唖yʒ4gb ,y@?<-" {͓SX23+Iuq#Mqy g wVM&)>DI3 Zc(s'BW1pL!;?W[ v_jvꗁ(k9b􏙗_vwo?n*:夣!-%pN3ݽxͷUSIʼnrcoos!"m>?oZ/3q}Kc|\^?c]߽q)"?獧%72ۻd.fA)k-.okWGTjD&627Uxb 'a3rCFÔ(M9pҒ:'um,4Jc:QyMiʥ4{8sʰ9bW6a" {AbN<׉oʣ +F^^xg͛XajaF$ P 1q| Z(L)<9Rykռ_?=?s<"y!ʙF&xzf&N<YTT&M}S:NLa`8RBdSʈCeTJ!52CJCxrQ4B3ܝ2сH w$_^騟r!mfGkZ<-zsDE wQN*N$pz{D8_q;}Ppcȩan^dѓ)a|4^gkۇërsɸ>y7b}s[w{_/|auv-z% eQ$,:j?,/bl{ QLo7_"l;~_/g_~?bHJKJ4epxZ#AkT"6JX4ZZk>|˻/?}}/Fxاyd;]B%DVc,K= MIyYh#l ) *Cž-qyRTp1(*b nUCiͶy,Ӵa b};Wkc}|;͍"XD&IDd&A0hT^c0rXGۣo~Ǿv,J,1/ES4'q*IOsVJF3S$C'v?"?AIR"a |`wQ3 )cw nAC¶F{tr.Hd7)K&P NV%Nv#yȘV !MNKI%\迿u;Zk6>Ɛmt:~e~is{ccn-"!Шe)8n|~p cZ4,O彇1мqֶg4 d{xI3bχ9ORRYtsn{|sD>-ɸ쳛/]%mӍ"~'Ǜ0ӌ]ǏߖWr>~_=<(v}~C|͋?lLJBu#߮Y%? n(jm%<ˋ)1{ )Ͻw"#aLaLD(q8.iu`\guÀ4/a#3WpnIhΝdY*Kjnf0`LY*őJ>X،sn$I>>/?9|{D%8rm]Ow#@Dpp\33XQ9itp ul< gv)۵Nvy" 4SGޝٍ7 ,y#V:Yߧ5u ˅ɀ N7⽧<-zysz5iɉt .'Xf0fbÃY)xHAhܫYq?/{QcDohbYkJiJSiLR\AE(uX֐kt69e۲]x=nQ8Ueœ/~>|?ɟo+{RLap %IZ|6S?{|կxwn44s14L_ιEXw*)t.BpDqsM 3/Or*`x/^<>osѯK9O DYΩ-\o)L\BhtL ͇Y7ݿ9 O/>]wǗ_8?}[^&Dg,9ȌDjݟ+w#QРP=(^Hԇ>t %hXgI 2Z8=FہjÜD ` I$bfgbE'hEY"ro5H &!V @%ht$jr{RADr+^=dgA g l2s!^SpN ܜ3g0gr03Áp l‰JDFDyp(DK?{0ۛS{q7Eu#,NUGH,Š {zv"0D̓j?{ʉ5t*yMƔmcEnJ=L{N wֿ0ϯ>KoG-7ɞCϽپE2ixzG~qtzO?OW @2KK7}_ŠH_ܗrM]&тc ihT25rpSyN.k` $rLABe>1FLKaVٕkQh>)4-)QJ*dSL2FLLɍ$D.0œDp0-$܍сsGV4 ə9#fh\'J(|i"Y)UTb%*84 ‡"c0|IXq4Bt&"4(R4כ7}~w;Bu0u6)uiBt<{lmX%s%`29޽!y_~I:4-@Äm[ 2'DTs-h-8K[ &=uPM &0뒘K4$qjXhDc8$Ac׽LţH)c91YbBr,Fw@m4i3+I b%@5a0 D4l,)c'Bs.baL)Q, 'cPe>(!-|qO-ΜQU Qu<͋Irޯ51|S-X2;pֵvd?G|=u9_1k5(UQb 0%}L'AxB$)HC2rP :L{XYzH'➴ǔ ` f 2Q2rTF7#vD}/gÛe)OLH U0St fnW'[7o/w J@9uvˤDpYPhAAe*_W#r@IHٚ,! 4hX =ޮAQ1g"1iiZG*QD!DnգL,[Dl!S1=6m8_j,'?Qp_9>O_3 m]L޷)x8b}?)nqiOX/E)}?O[^X7߼pr?{=}x4kaR^ryg?eU?R{>d̼o_wt˽LSR%Ia3!"1E)/gׂqu q„R"a 83h"9x442TljY(FpX%9$3!AJΠFgQp @,og?~:||< Ir)c4"5LL ']T,ꑎ/amw)}yТg>ڟ<&jS̙S}/N)Yp8b*SVҠrc GCC.Ð[C?dF>* 9ǩd6&妰FK$6e^r{_>ż' fDh:Hգ>}ՏRRR:.YJԑ91099RP@ ^zWQK݂D*nN*y,wW*Ǭ.09:6p`Myʺ9i;f,'Y)3ϳ$ " #вPokf>yƟ>ď?|_/?Ol R;}@~ON7o ͷCph)SxM׿u]c9[K7~HgOXnsqy*jl4-Ga=twO筙- ,r{n~3bGV?o}+ke?Ϗ~#LTܮ̈́w+ u V6~`B{#|fnCf̬p}y>IIa9 Oe-O*y1ʒJpk# 3;\ s"֎Kyb!а D!̞2B!{ĸdZPb&IbApfډ1Q0dSf"vTxDH3`"6pGan^IpR-$ p IhKH EfpLP̈0 Oчo>tN^֭y|g¾?B{:rx7_]wWl{.~Y$F*9wģch,dgx2lH6yEֿ/[a L QF9EtDsw3ƺy R0o2M22Ns9 w,ph#BuD`"o(86 ֻ1F擟,n^1޼~o7g֫E#sܛ#l0n/>}zo?5X|?|t8Yoq}n?yُ?mG?#W.ңʒ?;\4.\[rxº{k)Vͳ ˛fY>i^ܧǶ!egIZ,w7ip* \xX+RLb efP 0 Jm7 g4\3>N M2[YBcDK˄3&n"j* !ZW)9I0ā}%X?y>Nf *rt-iZ(t$1H0: (q= A}4<>o}U߯N7%_773֬K31 sM#$5t}>3M( $gw \B$(q`Y(ift8(voPiV pSK Ɓpfi>(4upCfɖ2Ŧt$xp(Cg,dhaAbaûv>ݿ/g/1-s8w')o~vʉ~~L$iY˟#|y^|E9G ^>av2MtΝ,n]F9էT{\}|ii'Ic>ݦ7?|o1Szur$r"ɫey5n[hQ7X ;Cs,1c0m TEeLuyti.Ϗ"`uNvR+F*" !cPrPZfdV9'ALS.fѥd c)dɇ #r&obpbi!2(vaB1'1ThopX#T2krQ;FbRJTDGaL.A3(;E<91Xtl9wB" ,Щ0lPִAӄCAzHSjnsr;cmk7|ysGߓ]m:L&]g%z5I<jƑ81D`+;-`) yTݳp,.)N0ujI" .$=0 \bQLjk-]9>^OYRabD( fE8YpFF.?>=ؾ??>yM/$Nq;嗷6k( jhytcIYfb<bG؈mdƚxܞl@${S9oϗmz4J I$2Aމ i?G}ۿz6,{^wnn0 ']$%5"l+|Ps!Oм [il!%98b\V {>ࣦe yZrR0M*2By|HP6qcs y" 7;Ju V[=lt'W?~x}۷څ0M†p6PM2q]uok}x폿>mE"y>,w^~G+}n|4Q/HA%E?nPkPVm#Įp1><&^ _eȠ>]]>~O>yUپ_?|ؓu'8_oO~ޕ%NA.7scB=k}12cM,Ea]&c\SB(WU4'm%IZӑ]X D33EXӶ]$mc&o_`Ar^F2X82"0cSp:d,7r!@yf’ZEs#N1ۣ!1+I才E¡"&TH& 0R /D M`&T+  % " I!A 1%rg` "2vzuE?xx?>+1ikQLF 'I'߮19"$r#G ӜF4EX#9 }" $=EI` zpݚkA]óR^SOJe3 h2E sISD!iN,?yy1`RXU!"E`bbw"vP>Bm|xw_~{/u͇d'tR_ud݆Zp1fe*Y3TޅHI' Dr)^@. GV!Ѐ 9 j~iïa7ۼw.nQJHSvgadOAtHPEW'O g@eːs o__.q}a1[b؉b$b6YRYR91S)M݋>gԬg=Lsbz]{4s$IyfsCi^zxՋvNKݯӇ>u4ɗ4|}^Ho^~ǔg%xtEu*aejt38~ k64ڧrNAsѻaw'3uOÉu fxoO9sr A*ւJ3A<EL<)g"8Zth5dTL2baD3ԥ^ֺ&%k&!g%ҤJCi((jC@}430!9}LyL^'IDטm@ r%$pN P R4pBA iO#8S 3(fKv?>mDJӔ2O95^H8sJwr9|@q4/ͫ)޽To-ݧ "A>03Nj٭Q1 = $YR7g-AH"sѢg"Ξ#g챙 d$6)K֤`&6G3?yg&ę;88|'Ċh :GA@]kz_}ۏۯ.oՇ}Фz/t|]Eo}02qDx{ 46N*YJ&.ảl4::#&Oצ"SbM MaP5eG66 xأwn EцҰ0O K!䋎t4er`$Ma$-2uRp@,HN@K>iWyyI8'߯>uAA\rI2&T/d\Y|{ތeNn+ i: %(y]7zٮ>>~?w>sSJ|/~r?Ңp;t"(O (DQ$NJ 0ԡ 仳8d~w#@:Ι_?>^SmۻuD]=hSlSU%eG>\z piVH)1ltz:H0;KLޯ`S# "UHĕ-=h8%1<wˇ,S6ݹS#"10d''%g???N*娉YF'{^qJ}D!R>&<'-B/ ͳ˙X5T8{4%%?Z DCR"2jcN]y}+%G ʁTZPw8BG!-`JcؽZ`&s~mi92%'A,$B\2 ' :x NL;\5E/'"w^<䳥H_ݵuE3e $Got9ڸ(a~OnyhnΤYy%i9n03aꖦevrI$@bn붝:7IM)4iD8ƶz߇m̧yJ8,a-"oWt?No^0upz?>Qc~|Ϟ?}=w߾߹pEn[$YRѬM^A6hsR4RRC-PI2+% ,A! Т :(γ_&|`RwwN;H(.tLx?t:f-%=\#\FTw|ػH~QtPz(B{+kN#9`0Q2AC6s$jH# bãqg83 lLeE;{84*EٹS3 !2!9b8(@؂48g|>޽6nnHh4WIy;iJ%s`Ao;T)C o87IxⰘy+{(4!щ8By'LcS3c@( 4#"3dN!,lMK/><|2d2eq<sMr:ۙ4 $ `2Xp3OfvҴ?yf1 sE ki)@"O~IU<,Yx@5=h* Y:C[mc^X$mɧrRiL#$v.$GL H!(3%=|"fsTiv5c/NO۷1?;?'?oSɀi"0#׺s5ݷ`z4&)}H<O:I߶"ww˫/ՠ{~64P9;C>nNB=ۮ8gGɦZUS5ܨnp+Pǘp;k0Kikous.uܾ9*2ݖI~tG!/,oD|_߿s(9dCJ{z~nQRf;NuU}zvFm hr QJ1Owb~o[JuPw~st ZoŶ X 7,"4>vT'eD4~VICB{G7VB!J3ij !AE +) 6י"h&6J$f N+фI`*܉)dص@*q"9 #r&-HH}SG#q`&0҉CI::cDLIecH =P.%In}Çmx:6,2˾7T06;@v/>>Gco4kI<#ИzpRuxkLPB11)['u3ps$Q X`rbrfN1ܠ9E0QLDj&)CUIS*sR*21664Orⰼ>aB F b8z {^"6ZW.ó<ϝ||(7e<)9koa#(;yJI)5 @H|p1d1e%|;l8 H(*<M,㜎7)/41zLdz:K JY i"fÊH܅Y g&,D`RfI1-"MDB 6s?.ig__*>B~Qjm*s8]G<WSKuH3nrbCvu4p_+)uڟF8IyAИo|% [z=ׇz}e`?Ls دZ)l2O772NSa?`DX@v(Hi:ݹA@FN??~gO _oǟgc!}QyDw[ #D9}Qs<$Nf4R$J$YI sVq~0g[+Ài眭.ZmC1\"4>^P DufRmx=LSԌ D2 ra W"dͧF"uC$r.[CI'ojp"iqխ1Sw VaB :@Q:wct|qlir%aٍH,P,AcQ# &dqx<yHaFe?ǧ?o>3tNȚ5lan$ֺ b8܆j%%/ǘnȍ(sK쭭 w):`oCYr`sDՃ a )]x'&hj"3S$1OIwO 4%{e9p=A4NstӥH=!D"h Q(90ޓ}>lkuan\LVt/yI#T\Q9T]LEGD)HikPݍښSV'^tx.`_?}ګy9l28`r\(i)iCP1XbZZ5N{#z݆‰&2lddKFi"")&cgɅi'ׯߦ4k?`}9n5Q* BkZr&VktzbPl{圣|E(,77$FS\s^3mK7tqBΗmv49|J?DkxbE[M_{XX@yaJJ|8w@UͥPnoh}z?/A$#iq>QG6 UGMޫr3&Y|%*0u:]6/ɨ̴LMESFT[dRJ#%| Q.zXOɅe=%-sUÍ32 4B/Qp2|| 0ӳlA4D9H\7H04d[|tj6CBAR<]I8BZcC8Pxoh qB_ܻ p#*0e!fT5YαɈx0ɏkͬ-|Lb|^ZVv'#6ЂēI@$N=2$Ebx[D*È8b BtO`spAEnx jLF cj!/51?~_o$%q]>ضz,)o=x;ݼ8}y Z-QG l&OhaxC#\Rxs` (˄!G(TckaI$A6 hV/y\;TY/P sdQȬD%(c`b{9aL, 1`|WhP4 b>:Z/ݯf'cgdB)se) QBЋǶtO Øvc-!"`ԌGܼ2!9G)^R#Gu*Y4i6c AtgޭO2yҡ~E'D^ݙ8)hpD&sڰ]9ND"jPCcz||wz^)7srWХR/߼Ju#mOE9qnxyvlm}{5׏e9 鴜7/_,?#OĿ{{g3Nc_~ hh9'J=L) ):GQgI:!2i%Q$@U Ut|   @Lz릛^w%[\@4T,՞n.Nw/4S(@uf\:z#8TU#5 DwqLa`"՝#.0Ff<@hF7dAYFYdjj d}\^_"chK˩}t#hAl!vNIX48,RXn¤̪e8XY;3@789%Rx<SlnYK1 J&V6n1 d@ abj)L3σ\*/>vnmFIM$k znf,Ȋ4b`r"ω8N %vIT(K$!ȝ}ޭX{70!"Ҩ6ZiLf"r fNx}ܶ{%g=¢s*3>n[n[. 8LFԬ뻻!s^GmϜgF^m{?{K`Nu}>ݼ7z:z`|d1e}Uk}NVSa7#x;?_?L)nr-bndu> 5A}CԋmQʁvE[O[w AR}@M°w}k;| UNNB0Cfe)Cٚ譎VUW庇ɜ'.N`"mWF9,ɪ֊h{-S8&)A!0`e7ˮ%" Hu8Qv>1mD "! NdDIςmpѨ-9v? S'[ s2G #tg)ax@"(;m)o~IWޥ)z6l䭲x+[n#c}#"6n%nG]}0ډO%W0m e،Fk^n"Q̣6+ ) L&A)HmX-:zx7c̙VyD77:HH"b xs)`Vpf3ADcb#Zm]/5=b!o9Ċ*Mu840itN)>ߤzJiZ־QG#TF 6&x5[HgpsknSoöQ3 CMhNpP`NLl1 ')ϩ7qs6 Z[AZR;@(3ig!a9bIk="45;kc^ޔ=_S^nQ^~Ӟ ;\j.[z$#MGHS>.\F˺]twWZfuQ)K!dFH̾v"ѱ0u5-ʂNcߒS ј$ćeZD$0H{,-}7=hN5b‘²BGZ "Cd^C7aR] a0 i0  VΡU[0#[7 8)EG )6ht- A8 )UB 2"=(A8doE?lJ&{X|.#ÂZs:s ׌x89.#d=A@q9+&!n"h]W&nUN,Ƚ~q rfkL FK {M&-1eHJ7$Di$A#$ʪeCU=B;9 (!b$󍌝QZ.]ֶ{}9Ie2fd4X%Dbc0:c#ƶØR #n9" Sl=L Lhfy\=m{6A\.B4,UY1F:xN*$Ue#  |0Xu8ySؽ1ML&B0qA8a3ٹݧKoЛFՇHnS>ۤ677/鑏ܓr:1FeL38Oy䵎qzFS?IiB&yGj^p{H sʒpEЛGN:QC`)6vB2F)YˈK:)[InN{ah!9ИtI8'e}sQ tJza1؅pl@FmYF f>Z3bxp"S/:MCC#$>a$Ɛ :pR aM:2a"&@"CѣA2!ò牢D| qwQ_Y3N;5L` p' x 3`(48"Y?O;4/ɨ57N<,]xrTJZ$*(+X Q)}Eס.9 R8F1d^Wf-T!s<.М02zOmp#7:Hq2;{u'i+"K$eJPBR5'haEzPwbJb{r{^폗z(ٲLs"xAHf&K UY@<6\kc quxpZ J҄4#&u\zǺ[h>w +M̤Bʩ@e]ݱYbNl#D%zF%XbqØ"舘̽Ɍ9*79h{w~yˣ6_)撘E|ZĬ!Zf^g?8!s㴜Þc%$nw•{5wLS=-/|rV//'>ūMo/W+YlN)"cA=Q+ϋsx/Nb#jvy]7? qmAKyI3$*,H7/QUWw(wBx kS}mi>Zէ u٨!e934-I)4E8U 9ֽz\뾥+#q$dMi%!<2unԉ2د CdgipGP Z&ƘTex)fhZѯfvprHH`Ii>hA'Ԁ)|%JA0'GA$r-$<\@Jz??$O%;hF?H"P޷sbg Ơhf&qZXg.:k2j  F[ Gm6N,6@b5BHQ X5-) 閭9Иu&::3YK#Rts:]^fK ̄#G Rp9bPX2E%Fz.}Q1YG;sۙI&*KJIf'hڟ+Z7OgJ18I:IhxkrԠ&{/\iO^X"{I4oS)y*Ҭz>??5="!QMس5u_4.DA֐o;U,k$rcQ$B ܧ++:Nϸ[>wO~J<4'VF"aDK\Ct,w #Kl\w`^wl2P<@ރfB+zץL"2/w,/{(J)Pi)O?}ucd=ڶwRQs4dy3ίħ"}zl\׏>Zf?}Xٟe_wm\<&OM0Ջ9eF72PC3{ү!&Li\X虂o#QyРVuDNB$܉JBV%I3S ja`FvhpVR܏f A#ȉ\!VuamXaW-uɣPphP"eB)P@-`PEaa9ͯ9~zZ^tR흂5v*m lHxy%r08"Fʾ"eh]N,DH,&sB@f{H1Xlֆo;!("9Ge!ڵ0=lΙid*eZ2}Fe!MDA:x׈ sGX nVu{\}8#M9qQ/ |՘)ͥsiQO290$FF6v$#ը`2%"GR{ڠnNcuo^[2eu19@FHBCoQffm{w@l$X8.44=- {hg1SL:9癈%#$ vo*_-1}S{DJjdIͮ}Z YL}#%t`9˙@FG(U8ַ!}OݝL\'=ɻyn{(_~Ge\na2mz~ߍ,|~/EFԧy7t@~xGD5~s|y?_׿ZPݲ8\7菤<< dfy@EHSjrث@m'̳rYLYqE(2/PfNY!ˡp&hfyB-"Nd:0O%=6WrxOjTΏ㙦9{TZ¤LQ{Z'2pSNnh-~8,cJo:{ph bxZ?}L_NNBp;ؕbq |8sYpP)C|,-fIMK .cI sW:yP,-7d)јxbvmK#Ј}_mҍvQiiL77SK*7r]fd1$4ub28DA)(Q 4jOmv$)I_`7{IZ8wK= >hTnֈ\{ܑ@|,wb BlV}oz3*Oy&b9ԭ}Cr g⃊PɡfZ@H }㳷GLǘ4dhNVC'@ذ6FA1Opa!!&.6@p?}ß?^*l1%i^5D|5t)6}U^2srMSI3A!M=AcLi.~DYhrfm>R>/>KlLڿy=w}C[>A^'il[ZN6;E0k?<4S}xv<>ź>'?e܌\1.϶;?r^KNF)3J«lyyq")4DIVp݋dVn@:h1T%-7wD2gq;)lP]x<^)M ͍PL⾻y['}?$(B@gvGmeKnU"В$$~{n>sNo;=GXO<՛S40U3||0Naz߽ۯy7M|{|{8޵nOrty!h^3Ltc,cx:u/^wO>Etf豯bi]yTqJiV1fNڶ\wԷ vWs>"9,*`drhw$ͷLz:ܥ:4`͛˒L%_[$dMrmW8[L%ASsʡNȕŴ.%h^#3YE kX=hlty~Qf*yu%9lp²*e*5m ߅n=0 "PeԽhB;p tG9Fr<"i8n P#qw!XAbA0Ix2ʂ%˸?}u<<̣W4x `4l.e$5"ᤉit=Dg'{*SpF+[JN$N}\h6֯F,#H&^i݁Z33cNsغSYff4MPbG g&ФȒG-Lh! j#Z/u c P-#UNi)<{2 C` k6,myÜeT83@C$3p$&A)Qv'f0bJZ NsQ"6:JDROATXM $ig!Uؕr54{yuQ_!_x)DÓݳWnkec2$r$rFda:b;RJGQ<'Ί!O_$D&%ɱUj]!anstm~׭,oJ&ōjBSNӤ~Y#k$g Cp` Ed* v*ل2Sb֐!&LpcR1N%UB:i#ъ>ybn>1 Ս}3kJ@^K@oW8"/w)cNJ#bd'Dc]H15(E(PD1Oy}'g3ּy"#>(Gd\dM)b|qMYA3 )T$2A+ G ^R̔gJP9"[24%auum(L,s޶g!I2ߕt[}ZnNrbD*'I3t%`$FpPV?C&Ž ʭš5}~^IiFWhc"oDW"T'J"") p~prlX_϶5B T4/(4ΙRɇ?>zT@k]gG}[2'$ӡ6yh$.ns)mz}\!P̌9(n˼x{usbIKw} }y|Sm7ߚ=엿<E{ھF5~~o oOc{o{~Y3J(,Em[*?^pyLl,ٰLN[}{rT.3@nǻIe֜i*ix_jϫi>y0Zn:e5NZ>)%ˤILaNa0=4H)C`EuݣXa7Jq=ty3no÷(^Y|gI8F7 B!YqLfzK04^ʫInNÉRk"]EȖnnKXϑ[#KΙP0rܦRRo.^Gp;^(9j#IRȲ==jFh,BkuyAYwcT7R']!~>>mC{4tws* s>L#zb||iCCM'_|2~y>ŧ_NSg=LpڸAĐa4J] M@f @ #j+"SIw攵䴝%ԯX;0.}:AGSNc*y 2xVÈk'w6"vIIY  9܌IWg Vƴ5f'&508YlȪ0.GNmCQj6H<PIY:/7>0£\"7w  fYpH`G Bf[ g^ w8񪝙ٝyItxJiƺ^p%3Iӄe kY}^l>9֩1haۤ=FOlU{nm)n1™3#%km#&i~q!۬ESQR)Q )B8S` f~n#L^1bsF쑏D#4ȕyFZ  5Q;Q+R 53c4 [dk>.|SˀoyF"YUԝ!)J>GEАp:NcfAO-_)ȍnYp6n):4w!$LKf2Ȉc ©5@0.SDNe#IU!T?uɣ:sHK,iJQhFEdNi9}y<=FԲ$1,!TmݽIo$EdQ!,ۯ-oV/jڟ޿_ߟo7A`~&ͩ'4S9kYyIrI4wq>??;(1~ Nuݯ?;'??`HKjq{y cꄁ +|\"O(e*{Ҡ$44|9IPI3dfdrL)+oD=ԝ#vmlŞ ቂ()f""-muL\K_58Rd1ɶ#7gI!:eP'IU㲒n`݇PMn";i -!99P8Qp>eW۞:!&`nYt.$6 &TǠBf ɂl <ܴ` xIh722 =9FR~NN؊n^S2PKN:)]|t#((('?_ѿ4ⲱ*]|O͙]S˭YnBt8 "-;UKt|Hʒ9@w4.BDWVhCy};xomөO׏6wyxt RۗO#?/#c^n^޽./>̅Zss8XhazA2/ri;NŗHR)BfB,cy_}Ҷ[4 Da$9ޣ[ LԔqɔ{ya)6;n+K--V# Ť dYz^nAтS'\b8{x1v"@\ ` %F%IK`vbv!,"Jș|"E! 7k=_ꥯTv Tlt-VȀq9|~&ֶ9SbA{hʽiaԈd`ζ*HeFnN]WAmFf2E؞yRJ< 0.-MO41%֔8%TDštC3%@" e {[bTf9A"h{vwL%ђxИ&WH4Fx) u8U3G&وK[ ^IHVk)H{vM 1g#WF N9`L)c&1_2մ4"k$ q\ S'&+ b Qk'ޫ!_ᛯsKɔ$ޱ,JB91.}}[A$e 7tyɒh>(f zHOuG,BFڰ>|% //-uN2k)[{/w=(O˰pspwWgt9_iy[awI9?O篾sN_gԿ_ZKg?T>W\u*+_^;۳U;)PW"sb.GZSO$cGDžaW yZʛit"(t&A$C}$NYF׽1M%)$ 6K4{y̓!#$ Ձ+si/tzV]ς 4S0* F f@\#""*2i֜д1,a8rh; ݭhK'_Iă-O0C# & N{SD%qrO$m6N͌{uOܲB;)[BFTQi&j[ Q fRF* & BJBEFa# &̒ ]|uAn9M) G9' S B*$,s#fF  NLL@Ƹ:Fc5RDyJ:,}F;SoқUkh[7΃ks24u-(XfΜKbj6CqD{٣y4#c c4wn剠$ɪ1=eViA)j>F1b0<3x(LA{4kؖH N1xw;!12 .! mk)yi(|wԣ_W fIJa^tҐiZҤ~lmk5H7g7̫u}m۷wOyOR:,99ID^/yc}ͼ>/ dJeHX>6Ͳ^/?|nOx]͒'%"yraaAY(F_"3F{yݷ][8&{}yWקgߟOonoc*ɭL`n^)&zFsٟN~?gY/?:_W?WƤi19*3;W 7Nor1ywkϐq,Em F#O fsG0$u{kk+3TG0LLj@ Iĭ9;oW0ykub!VB "Qɹw+%rj\ yR f$KF.PJbbFt%x c_#8v77L\BpN`0l$/a8#50O\Փu"# FDp#"<(! "Nr7Q'P߂L{)L9i'Fw+ژÉez5&Jp 6ڜ(0P-Rc*wfe0Y"PPr"} 'h dNm2O9dᔍr)AFxl]P "FaEڣ1]'Tᦣ#=M6p43r.V#ʂPaam3&4'PڙB`A.!PXPr Ѻ~=]F{jB&:A$^EKIK\8OBB$wO3l`aAh6!DIs Ag dqF<8FU1s1,A SX`{XcKfu!uFAD{nk{"鐕M./{. Dצz=/?㵋lxBTLb_3Ϻ=!]{`vm}JA-?FF]? f3۾?'޾ ύ~~'ݼyU&6](Fpmxx7_}]ye'~ޗo_o>WǯbT~ےIS ѽc턢{JqC{c}t"b:8$[<ȲP oiC:Hns*Q%IPQ$7O n۾^K>n=( Y3@ -3s !r8kzaM!%)L.Rݢ0U)a)TF03T(ψ-D 5Kx42F)λpjZiX NY0$` $hgoL @81@(Bf`< փ2°N0B/0%QszDMjyp)%##!ccJ̷ ann{ZT|z)rzYćDE9lyxB@&*"2['ViC!&[-,/B `bݛGԇf׾ƺޔKH|9$fS(Rʙ7=)J"2s{4;̆pY%pPyl͸$Ͷ-(IYd h{pmS= Gs.dl*4/i!9k1.l ΌiIݮZ:Ew2N\Cea/޶Bž݅x9tGIe*粤E X TᮨDx5&ԽJptwf)~t>7?<Оմ𓗇|nb-4gK4p6MDcHpfC~ޣ*J(7w2D΁hcX.eOC]/Iwe]9APg@t^7?Oo0:O}z?ᓟ^K>ެܜ+]*j]?9u{IW}cf>A]`yZjR=bkW8qʯ7nꆴ݃y~M =2y!"AEH{SyP)4pwr(a'B ( M1ìseN<1)ݍ"l"A6}IK#JaG:Q41Gp?̌#a6LxÂ@VԚl&w0K;P&$B -ưyHja#Pdj$% J,FDl0`#(P apI'>+DDt%;o(JP}P9Ҭ}$X$bKrdJ a*î>h\kaveWeWJ@W!ImzWx"mx聞Q'  b4g(pԝ@S$# 6]z`v!Hr7Zd^l6"پK4* Zu덅D#cGk!vuN}ZɷFv?8**ԍiAቦa#`WbpG(j|8oD|@ճ%%5\".>8{p!f=(JKpgraԶŮכCeRmCL k%'J14I9kۆCn4*?N}>=ne1ǔ2/9ЬDT m 2(Dž'rXFxy8^~dؾ_ӯ?Xz<ӫӋ/: yzLǟ_;ۖ?^u=y;v߶'?S^晃^v}c}Jwu\DӬLƭpPҙ9+ydO%({嶾@W y(He ".LfJLc^4Rgᬬ1|Xш $!KD9qSFqRzj f3JQ'Bx삢0VC8 c4tۣ 43yryxo-ݕBZi1 n..jS  k FP83e`WpH`U8@h ޜfs<$qg91Cȝ"T?Db]Lɛ!Q4A̝I$DryYӛ>yu/x~w&IӋoa˜ĕ{oCsےǏ׵rBF=xl{J`w@%Ťk Ht'-Db{̋~O_|^}vY[{Ҙf^^rA xpDA?^xw?B_?,߽?~;7WƟ1?^91>~4ƒױ:um"cq8 1v:11A@L' "2hIc5Is(̅B$w_W'9s-.a{G)F# l*e})@&Rk^ :̆:)Q7=B9\DG6lޞ6n9EG-0"I$(zFVYZbdr'9uGqДidRn`9Q c Sd[q}>~?c?|x>ciğ1g('T!"j=:|#G#3_:nP30b$\9w$)#2vO]Fn[̋ K!ڀ)rKwy SPu<{ `b49O#)N/9B&SJ4C (#{f,LvvZ#3YE9DH" +6f5] `:A\U"UiAR`WZlS:uK/I9dD# `k&"+BZEDpg@A0a#l( "O%?f $2y*"16Q@Y[pz4\SXCh̋b 8EGH\U'-Wy`$Bŭzc=x7)qĺ= Fr%RdN*iɣJ W 0'WAh<բ xQ~ijK0D䑳er趭%n"G)!j!^FTAduxIi(1 7 9h69#\Jf `#b3ڵdq: ݢH(l""s9xɽkr:4Tb:P>@%d[A44Rl\\lʍ6u熤Zw'vt]vZR+;u`xF(`e0",>?Ooaۮ4yLcG4ZL&q4r,,s}hCˌž^?WߌᅪLO1?}}~_'UOϾ|pSͫlL$o\cuG5DpI(kdA[U*<3 sTQ;r65 I1F\̻7Rq9kd2ݰo N39$Aq![,n.c y[L "IÍp{OƁ8IM8%(S N lV@WO· 21RNX_:xP6'"lDSe3MqK, VW!yyѣ{#RY*%!g2$t7ўω >q&(8KQ\#C IfA o);ǩ|,-y({mg2nR`O4"$nɫH/498G^4&C%Jڻ{{rCS^J6xw?byqx9b78+%\[!:޾hÄ$[;؊q/mEy}_nܼ z4H3^왲^~o{jv=f:}f_;RNotO;Fm/@|o~÷/_?퟾~e%Oş|/||=|X[N"K[h`WI,-Qs x`n26P‡ B08FD_19'lK0Pu&j]XP4o1EX5Xt *+I;@讙Dt@ sIiN?CUEc{9h*)oTZ%Zry'~79 UZԮ&{Lmk9ihv)xA<;hq~ȍK'h`4t$ZAY$l'S1hQLIξ4D0R| H4A[xm8to1jD:749p:lByFj[[bF Im$ARD!LڹчIa\xm딡9k'JAHʡlԄ8ʮ#Z&Jݬ|@?&0Bajk˜BՃ5Lkbqt %IԺv^}VT q^,z3]{&&'Z=IyMx\S+a31ԂKރ-IX -,&"/ H<]Gđ<'.– р#z〸O<&)±#2j}Vm1j-Ȭw`BB8,<&!m6CbbI|AHmN͙zG=sQIw2`҃\lT ̅(C}[. '1Kq4f|]/b{G7 fHb27< <eiWtq;bDhL29:Y@pL&[ WJ)ZI`u؎~Rj/V.\Ut<W} )}:Dsᩮö1+JI[ ~>?>um[Vtp q-VӴ3&yڟù_ T8ݞ@L]"Ͳ4]U=KX}]lϾc_.tIR0}}쾟?x a㷿/$]OQDE% ^ݱdֽf~=y(,'Rk[xۇ?/|G?|KuJ׍ɨYn uM9TxDXú;:ۈ u5QjNFSfp E(B4k9 A  Q@Fd67j}c߈T=P(4Xl: r4p pr$F83Uֵp)4 Gy8B킵x pT ޽ H9Ɔ"plL(4u3 ؉EA(8(:R@#ؐn'އ{ER`抰0ض$8d2)Vt(<ؑ%jfSϘ)4΃;.)m l՝u.g4QMޥ}zӷadאJ>ܞ=N' p:|rkp΀ j5Xl^wlKG v(噃FHa ug:LYFf+bp ˙ {9%s#lĸ* "IPXFj&"ICP+;ռhǘKs3fSZ\Cs`4d"7K< !p:/1x6Uڦy Dʃ *fzCd&EvNΌ}EboȑfNVxr*.ymOU9WS>.ĨV,3!/3(OOW/n̘3N1:2:gdX1:nN"%4¦]AvI2=}y @K|y=xeF\_1^Q'{'ŏ;zAo~g`#Ino_~r?\.qDžsf~Lծ{~_>_pٟ˻9dRF4bk&ʶ< {FDnqe %8Am@f ]k,ʜo(y %, 2P3U,D&b(_cj.d{x&:H><+A(|$<$2hΞutnBfJJ&) 7,Ev")ÇPiR=G 1k&ZzH'߃GpA5Ɛ FJD`b  "G(EO˝~9'%VC#yE5\PI3Ţ8iUFLYhϚ'A*IM'1x">@dc:nZ{OjM¶Au|$ K#-Jv،SN j@F ,;GDka"uRx\4BratSVbskۅRrCG' >au'8E4J4EB$<Gl$FZD1q&d!b+唩msDdEMd֢ ƦˍI8W r>-IZ,ETCMqv8 !2;0 lpx,Q q\4sA:Ae_~?,YXH{J^^?m/o\di>| ' ˡ_Dx>|YR}7%L}xd8 -ig4b?bΝY,T}b&wŰ7p`0\S"8;ЈmY0:!(5&ӀP\(`% w>DY*RapKza!%0- ;AaQ)$dxs i A\V :<?6>*"(®\X3U:y *y{olJ>L* Sf3Le?LjfcHmupr@XU8GUV1Vk܈"9D6m0vCi{PFW1B&Rt$:,$4%a$s4iӨ;,*I*ĵNh4Oz/̚U Er.XB}Y(y߲WKFmv 48:kYvexF,'5͌d2&0)]%-v$2m!JsؠAzBLȉextH 5v'C*Hi.uI=)z(As&> m,xxmx<Mg |0c=tyyyKMD 6hx'b|Gɯfܗׯ~fcDܖc~zY$f4ԉ%%&gHJF>ᴜҦ(xN,)q4p0 6 %V$X43ORj$r]#dw߰8PzJbnFJ%drUF@°pFU.Ww2)9:61 ,F } O "[0౻u906HvgIg aiJe|}r fa$:n9e%ꡁHH}݄T"Ik>uD$""VʓQ0 qnz_)-|u L*Yr.mo2]z2 Mθse T,F̓PY3@SƜ{S v 6?L3iCg+nx~m/'??}s޽_^~?\Rf<~y74@Ft_6yr`<GȧxE$1jN>-Eҹ_s[ISpCRb1}ϏɣIqV]Th.rĐp#N:@-zX}(OlcC>7Xvkڛ5笊gu͑m!I)QFJ4Lnکv5`K jґʛ S]:ALKvtQ!#$ )I1ݰ]5+Mؠ|=҄ ug#YBBb'y0|PvAPP(! $<%39 Ő"#HgF#I#vr`c#l w_?c%;+ɟ҅1'HtK,A,zOƒBțITLN Q>h'A0ƤԞXH1nclnmv}EH fD /J0w'u֬YT]a) T$].Fy6V p d^fQApt#I"lJQ ;}:4sr,v1 #`ąEO7i)f%"Ns&PӰ `F~]k6 ^R>~<9Ns,[x%9Jj@ Wp"!M#x7OڨT5fr8Ͷ<7O-EE.9Oclrm;+n=Bb$i\mo9NO^LDoW4oc8wiLZ.sLlaɂi`ĺv)e)tR22~%~m&(h@O۔̅|yڇr/]ǧׯ^0??oǛ/BG16߾;?-9ʀNr9?,2Mr.k\ Kd؇[jx5{uy^W;SiSnn0Q%=sۦ):4HC(F團D> Oc:m9 nt|^sT s)H5)gR@$"9;4d  X.ʝ q jnd\J#H678:8L1;͐=H̓D{ɋQl||*DdÂ4z<2q58}]K`l#ƋӋ|sIدZo>>k)#HҨѸ^*7T؇ߐ[i:~}zLJğ2-/2 _{7\%,~ootO5W.4x!{h uL*/oh(u$i1̚E8)]9 ({[e帯Khы؜<$CJ9 }"hrQa!'.ALJ߭f^M0I"G 37S(G= 2K0Åx1SP&uAcķctQ^:GSF b1^f>x*̣QQQbgnvsf\ Q;79.NndW3PQa ^$D@; Hݝ9%'H1IwHʂ`a< Da=`0Q_~6MsRp:v9t16Kg+-98,St1)SӶoLp *QsB\~4R$[#|mTɃݜ3̍4{e!'P)E# $ wCDa=((*cN(Lh:;{t7! JԂ4). "i@@"#*bo_h azsQyݭ=mcɮOmπMOdä5-sj{DXWQ&4:49Z Hro NB:B ö~ {=˳G!Ge ոnf, cs ۠{ҽERĚy:!SJ74/I;H탌ZXDkccd6Z`K?oNFA, gnwLF)k>yʛG`ҐEx^=Fm[jcɗOxP7r0"7}}&tTݞSwݏF.dC$i);w`Z׺z_[Iŧy &Nsn\/e||x<…J^o^Mr?=|ȣ: 6l] mɇ7ׇnKo?/_on!co[ y[lO?Z=iw@\~g;Q@Dai|X:U[GHDžf {T2ucFQs%2'̎eR:8  &Kv+(YK.$3=M?1#uwD1eGp5/J`xD@б3 6-7…Va;"FJ{YPfS0Vј58.)+BNBΤ XB@N:INUsJ0 U v;Ll48DLAr" Saoo'N!5rnҮaԭWkJ$N"na$ fOh1.!)A/$DDs"DILԷR]gѾѐٯcxuohXh0{]` 㹝[ k3R!gD1j| ͬT0 SJm|}È,]:R8G)sOe4n&d< I1UE\)Ȼq֧Wc m_yfax0ߝt2@y Dwc1jyoǎsZn 5%F$k 4F0jG1Rticxa}#_F&z  Pҫ7?- .|y|gkOF9#̛JzS+%m1O:ۼ&չ:dmO=ַ}7;|pp6HpZzoק{/7ዾ>|'{{_|g'o}{]&E뀹aj]95"\:e8'͓ J&^ҁ1eg1i~Q&9H>dQ !1#<( HdFBXȃ DA3V0,5ya@ew'D 'O@݀!\ɍ< `(Pc2;[>ٙ@BN V9@gf2y1@! b[sJ \`pbiJd|÷ǽgGE(a4Ƚ^4fl ѝzbC&&#*'p4`[ 9٘<#Q.HZ⤣U ý:*5Q8hE qΐȉh'ޚ=?K/z#;q1*1r((u˪ Hi}ڌc.%, %RÍŻc\GwkDc1<17)#^\a\2O}D%8ì$ے$-eZN$J77y.*f8|Tk6?>u(y̅˼,BJ eD#1_=1LDq&'ge-l9؈b[҄f^nq{>nzs#(ivk9S3qo8'wIdfN\tJy.S9Ƹx|zZc%h4d;*iRÝXqZk\>\jx|q?Te{9iOi9M+Yl/^Q}yDݟ/LNs)z#y݊%J]y9}>~Lw2A!bNdr +I5Β(CIcMS/ۜc8dsYH3܉!N#ШFY9r Fd)#Hhn PbxȠy$Q@F #xI9eb&l`"K4x0bh-/*A$)4 D B4˒a 2iA,D 0u  ;@=8&+ 5uV}]kyutuӸ\\~8Y!=lj֬WF zNp'>W #m*TJ&4=ι@̣ua|O&N:6e2+'ƀNo^N78q9A @e=Z7C"R2qldR*Q$mUj!֫9##ifVm#»Ėֻ1>"r2A ƅ\ lmG0HA}رVۮD:b՝( ԇ7BɲXklabL  TU}O^ &!|}f kGӘŠYzG q3ů]_Ҡ1f bR8Gc#z,ѾRA\4FNc"\)Ϭu;?={w={pz)UP&AE"Z{^#hͽ=1MfݍrqÔQv=[ Z|U7dTw?KCQ`ּ%N*YJ9}Zrv?%^>y=_'~)5VO?|wt|lLJRcr O^0O)%jˋ7F.)OD?i 9f3o={\!et"\YӢ@=\í0$nj6F>>^g2VK:/ A2$щE.IF7顲0GPCf/b9@}-Ҟid KIwVM"/gE@0歓pᜇKh7-)2{hFG,d\Bqq<{2؉Q1PÈFXEB7wý܆WpgEHPc >NFȐDԃ(9gpRgx$ |O`I|q߷}cQqmuqn}j}KbqÆ?u^r!`dMF$).D"xȨ# !p5Z<(UG p}f;I2$[qЛ)"/7)K=upBm彙y )W7~qURARlaAu=X盎+%Pdb G[a)*>N|݊B4ڇLl"d,Tݩ$'B_~v:>޿O73%b - )b^@?pw Vs9s%<|I+D625br ́Zb[gwH\Fg׺Ipw?~OWt w8K1+2m V[ȍ%tg׍CR^ymgi˅FgOonҰۻC@>>.! #/>^Jg/׿mևKD//c~FN/^?;suuӏ$2ͼ^2Qykι>xݱ?_y~v:=wsZ&},|1q1^wQfûì'Y씲 )n02SW9gQp8(A19m\!|3B2%;0 P !pG؈ :Q-1F;7F0'IJ &H3$ 7x%wG9n7<6p A@Cψ@0cD4!B'P^2K$L Z3 `"5 vV`D\F`p 8C"H"%P, DJp (PXx08@ۯTcr^b_tI% gCm0Н/d`Jͷm 9[ hb8z.D> Aӕ= oTr&Q3Fu\ *_ґ)/EHSDB}Pv?Zn1@9Љ^G8K&6ϋHq}Vч&ႴL :2H"e)<ʇ=NL9#eۮ_ưqi۞ʁ=h=FEL+MP @\BTn v0X*akŠn,Oyw"ɅiRORՌl d0uV:Ӌ8N&FTnJp0!<(ɦ!~`f\`a潑R8_X bC"_rЅ*L,Ac#"7X5:@)uY"!ȸL$ia83;%@nxߘNH(HcM Ĥpv{ġnmg=16Ct{{?nOҶvߩ&Ol:U(zku[lbbh@aky;19&{O{g~|뺎|!>Z9xoWvs?7LK/߾钡'f5i8sJompfo=B fgs>$t:qHS֒<34Q+Q&H bxR LJ@(NWcRH|"^m͑(rKYe_6iwvpBֽ|^F[GCmއyu޷qn-X{`PRkuGųN,:#D1{{c {Rk:1<ω/Jr{~iöbw(,I 0Sr /RxR?+,j>i.SEA&Ch^Zm>-cYK]r*|D\GXI!AbBȈ+`j)À*ZVa>q],Gi{W'u Ȯ5I"LyɇQNS(A9!I)Yxŧi:Ns&w|%zㅽ oxkUIYq.n牂! Ù5f@\Ĩwũ0Lz Lm͖ԍJ,S !BbV]=|H1 /r7dhc97vK*_ل2GL"IoA&LGwpiD8̠D3a:u"!RgI   ,AɃyaDJ b)mk_}o뎾 4q'°8ޢELbƣa|}a3:le^[ߨ77BsJ#: pu,.R(#ҽHHO7=L™qͧ.G}'`*Y!ʺ|M> I 'D e|ĸD0F>m{7#eV5;89SVUɅ6po;lN)ecf$K:BhDFÝ/.4K^9Oc>m}hݠFs;zYG@4 !YRSYTtАPv kk c}s9)l&_Ҵ -$7+%w%A.,. 8 ct\07j$g}8קkVk0qH01< I:|^Ir{.eii|v1fvTxZ O:畒7<ˈۅ\xDfϷWsZۂVn_Y41pGP"w%,T0@Tke͐F)>LsZJY4cQY0sfJf,JI0נQY N[ǧ?vdzNȤuYYML *Ɩrا8 V+xY)ff9c$NIbB@"+2P%%zʬ0BehjL-a1Qtp1 $9rl[C Ns p!8X@,i;iDXCCE "ND<B<nHs 'asP())B^?><1k\Mu>4}$>:BnH^X<ҽw-juq79o#Eݑon2:gk؃zښ%#yJ4XyOl:p@'])Tf(dkې)c|ph[[{"tfb`eLa"2ͫӤ2H]wIhnjkm>FƒqST#P t9z(`QBá*VPWRPoÌ=O̗֦˺IdS9?G.4d:Tϗm`4AR 19q~y\CtYD%ۮlI[Won^]g@BJt'Xk yihc+өmY/n?~~ezimyyN.銺eO|8|6 /gjuc;//C&7?w?5&ͻX$Beu2"&7}5\5@H% g1O$F2hznݜ[ݱdz{ւI8w:7Hh> M^6ccHK(,1(\BhVu2RE@ (sdc>971HC~p' cÏ$x5DFfϧt8 IP-eJ}_fm84 [ՉHơETGniHfS)Me ag%Jp8bu֣I I}Hs}vXkvɧ'߼z?=ܼ/?k"Gw7]oK<__S2q׬ m9Z仛?LhJ7'=.} Q`:tI0Y}9$K!O#uUU ܮ$I*Д˜g+IJ2,{7eAii YGd|B 2O 3fU"@g9 4+ V#33Ipv0DB05;"BI-%2WN1BB^E<D9Qt6c1"@PG#k*–hTy_ LytJH $ٜ&x06i}eHPMDў;.1 XGy >ʎ eAGBJZwI GWr2)BX}jm^[/ZGnxouy=?jZޜe'Jr{1I y)&<|HegLN.RT$x!L!.´zE~Qf^kn+w0H^ me{#eYߝ:w UwJMRE|wA%H9rq"ԳScGɴԧn=yvgj gD9r:De&B1 VG "b 0iީc6Xx,KKyYz)'Nk-ҍdP!DD$`pcc=ҪGb֩M&6.m_:MɜT`#x {W)'QP(8p1h7)-cK"xpk~T5XxC$sp:tN^ӜPH)ݲx8O?w{w󟒮Aa:n?O_ݦ{V.eyۺ?Nw7SI,"9K>@\@{m~ܻѬ5 \juP *@Ydsr2n78x (j:-un1•DԅxAݝK:)N&17 ğ{x $F/1iZnb{&xnBӛ\7i1$i<_DRiT (^"r1泇Ap,am 1o_tZ3{ؘn[bHEq({Ha4LND+yogDO ˵}vۻO?nާ7o/&/'_%^nW mpwGΙ[Á?V/QtvT]wH: [^la%H)R*5ozl}`dD rNUgQh$E>$:\UcǪ2 ,S-7/DS9y#̀#xXnCMHAT ؕܛHDI"C0'O-ϼ)eAa̱)"QڣѐP|P•hPPL{H)w%uD,*2()uΏHt\T u3$iݞJV . %QSEF@ESr9/>?gKS("޻>;#)DS\ s4E9s  ʜL&[cMl"p*#vp̸{w󸻿[]ŬVt!|m< C S0(g |i1:"J N; HG9F 5S8="_X&!ҮUMCGrVZ{ǜx|폻i2%/.9/7)ͯ߼-bp|Dᅨ n?|oo>/> _?|wW dxxhUDWk- V.Ӣ0y `1Zs2O2zDu_(,=KC`Zى#*Ýُw[r꧗%-#ű`V*n' Mhp$D$NGEw7x8 @уč% Ӌk;Z3-G*D>w$A'8B& {%9A9LB ADLp AD0JA %q3;drA@ F`&BhLrߏLSEo>xaU'A3DEFgL$_+m+T^#n^Im쫛=oc9BW,m Fa_n= .CneASFTht BP.*En[,9 GD m} J~^xvǫmm-Ώ{o !|$,Jm(Aޚ"ž%e4CXz-Vʋy0\"tGDޮ+y>hxT)Aa\䏜7D 4sYrN~"YD4LElTdpP8ϑR$` i"Gt2aUza;akTuN>%t,ś+š}1Ԇ 3pXB. K]j/^ ‰x4BqcTDb=t "i9ݔCHФEI Ҁ|M˓^~#Ng4T :yz5;T"%2¬ xDb,L>;3bz9ęJP<;Am3{.<6RwFrLl Q(zČhE21A3cGaQ7A9 0F2sGE(")X@4"È%P r"2L.`  epRgAɃVpvC>€F"p1ٴ~{uĠi&m9HJ=sgSonydsN:Zx{u͚9֞{_ܨ_;/qXɄ}5S9M§u&['3NRnrI:d, mX}?VO?^]zc{0tn78ED4!rѡ>@r&ڶUCXsm ЖLC#sp I`t *d붭u@P %Ys{$lCXS(|r>}v9 THCS,'G0C0^K`B܈=(w$)ԃrV=|Ğ߷rW=?W'v~}6M9MGvqwEJDrV=ܽ^11F>ū7|T/كm\@fpN鰐دk* )ЬQS.4oO|8UkNzޮ}K޻ >Uz|C}{)-ާ<$-MbOg?{26?Te@2_% IP<4(l:gkk>df%2"L!^TOOR)4Iq"E) 35CGڿG=r#}>f)̲^I6Ib#R CZ:E_CXc=3#ڀ2!&"Σz^h{v # "LIܡAs VwB.Pr .n1Ebxą#)eh&&X0# `%!0ҋ+_C2!"MAT2 RhBP *A f"؀0 sSS|H^X2K=<\w,IYy8y[#E;E6MGCpqۚKZ[{uu7]k_W9KןӔM^ԡ%aZ2(/$u)j=H+xFk^)PbH`1f:lw7:F%̯@ OZ>a1NJ,s*(yI|Yt[t\ 'EɼN՜tBS4$ŘCƒ8+RRSDÛAå$4Wq{MoW8Gjfks{5/Y&zWkڨc䇹ܿՈ>3Ubn=jkr&9/q>7^9BNyĖKBVcgFʷϵn4%Rͮt!R5njmVkkn~Gr<7r|b{5egbռ:n׷;8|IaX5{Kf֣uukhM&s1@bbB)pJ :2o߃ℛz.⺩:11&NlH0[<x!~ o>9%͹K9R ^$:s8!=f&Dى$p%EhP'2+G(Q@tA3 4*fB"r` %[]D<8kJ~IVCaM`aۃuAl!A4 ΢w9Sx00;LBނK$H8)3<#"]0;B sQJ$lpDAH`c @ QL5~wyXusː>yYigfS< sbD Ɛڶs©_.-̧D0676mR=do>9\7nՑʔ2Sr$f)@4wfc}c{8O0>5~)gh݆)( r2y%-%M.)S'Ǯ> c1bHbJ"A`;b0]?ӰvnuӠ͑ĥӐ?\)q*LJΡI3{|rak{0Xmk#ڵo 10"ȟ:P4\2z cZWyfNQr*ַzm}F[oTf~m%sQ&FiJǗ7c/NB8)bəPn MI8SxR B{ vu7+ol딼 7H+1iݫ[TG[Ohay F~n;yYt#~G=YN}7^\׷hڟ޺'iԴ(3lE4o|k˝+)kͼG p6rcPnFH]" a,]ӂzL2: p'AI ͅs(KY"L TB{DA9ښ;NջEd r,*x@ŒdD̖T˜-BȉiaZ]s”yf&M#89J<, v -*["B" 0A f&320W,(W%/Y9e#Iɺl3y; )iA#v7L:18/h}Oޤ_/owS:L<0n)6YLAhHGG6Z}{_دKެI! ؄!I}Vc(On9Q 7VV7H2Q?D65`x*!LV#JvG:c;>6 WӜ:!DIO>9^Xf'¢9D8,SYJ0- %H9("X 2?<_?7?_GhXnX/ת^Ͳb:y.q]6 ú:9T ]<< wdd5MQ% q\t\BnDysO$-zfNq9-e9zOgHT;( /r*3/-lSw/]Hq'Ks/_ۻ7JKoon"Z607]&_/28_zc01}AS7 M#yZrJC=ŔTrKJGTS"DdFbϕ|׽$\ۓ|zӒ&BN(qTx;@l)leۍ`K2Qx@g6 Rt MϠD-D PNq/|NA ;pqvR0#01d)TbQ'˙FvPKxIjpB5 `N#qJ=I2Y L a)8=D{HCpq8r_c&&"qc{z<>ܤDsbɚݮc^J{1gmm2c30rx)M4+1n tsqtZG#3 w0=km8R)BeAu.FmkOt_p5XQ(q>.SI%(4źtO&%Pi:-)9Ou$%D7ڃڵ JkM<f%a "5 ͬ`!w8і;:yc뤷rzcISzD֬Gp3J)ļ/^MKjާ!e=es7SsX[_>4NɆiߝǜ4yvaJ y:t8ן\T B>?~߾Y޿߷w&Y<z?c׳m}u~GɛݿD$K Vzt{}1 \}0ˬj6MytwOD)Hќh @>b%05o_/kh4@A)D<NEL(/ ;BG7j]3oŋ=%"ZLӈD܇U V &w(”G44v% e4%OAa݇)ɐEۓ즁Ygkgn+r-$9z" GԝFPl"aN>=On; ENXb,>z,`#  lssDrka$ $or9 )'~q @ rH2Ôs2HJ*,d.#FYI|I4FFHo_]Φv˝J&"|J?~}}~٫Fya5I N>m(iGM’C4/SNJ"KlyF`Um# u8M"rND$G$sY|ѣѾ;I͘y^MyrQN:ϣ-3ϒU J>iNKT樂t4ҒGLFNHVfӾ^ն~FY&h|nBŢCMI5rDr($GsEn>&R^CJ͞;k'@9Ò\Ï?v *%)XQll\mxw}C9䅅qe9Ƥ"s֤&F g树Kro}58EJSȒnoh$sҒXK߶mBQy N6]N~\N/W.5ӗvT>22 Ws10FuUp[(G3a#r@{>}'NQ2t[Bƈș'4GRR1P q焒hy㯿~ʴwIe~ywe~b],,9c:Y@ D.  PDGW 9N MDB, ±Z)&,c݆^ )Mv0ff ȭC9sI"1RR[) a Tgn`&2B!N:U@\ȻYPHiv }0B"!TB( !3;(3X)g"'8H\bko0Yi8ظt ΃F>f#%C9Lb35e>-I4̐%hw0:y]{_Nc s!0@SGIAfuy۶lޞs~cjֵX0O:-FnWk}xT2O2iN%m؟n: . !U\[tdkCiytHBz91,|2BӜt0cbQՋOtԐ?ps󁕈ECutZH(K^K$ɒL9 }T\ 3gΚ##qmDu?by؟e}sp~osJּUYqL0͟/~-z:*Bאn6շaL8P6;-Ap)$,s%3bI&t٣ 8W0'ġy<1 "4ok{B4?mϐC){8Dt:ASNIHW) H1 m羮[ܯOי.$rSJ eI^,&:<"r͜1`m Q4~}55-'zʇc׵z|&y9tVy w!yÆ{#,$ _ܤ[A k 8HMT-г,u{޶ R{n=Itȥubi(q҉3FZaqA{>7~Z%qIEHk"_/"&(i위]#.FHR O4ňvHW`ev]$l9F C=%V[oX|&-`Lr\j#0嬙a}mME}dÝX[׫VK^$TGuJ9xňAP#]+oꈥԴṫW}B4ę5Y$R74-^<8$w7,JHBNR31yYBjLEwo/oοħ)|vsgǼ$s*L(UȻ 6 &Ѡ)l}3aTb1gr vmg5WN i3y S TxzCZ݉inNN\2#ώ N0 51nG@5MDn<P?PްQp<(SGO $# &D`d2@\QWƢAa q H zx_E,9NY5!:_]ߵ?٭VlN-PD@:-$ZxV+ԞmFeYpLI41~~"eNƁ6&iR)a8\,+#?_{|OzvOk-a}*r;X:\U-iJ9\פ$EJY]Woގ)-;$=qιvZ((׵tZzצ4DՃ7^ϊԵ;Q O>^ޒ2-ksWo]u:POic:}{z1_ޗn훂AĜt}a٫LB%t@'7.HILrZGt٧b4SlSឧLĴ|~m_6Hr(O^Xe̘Y1ah9AA744څҜ9 r6BgA` ph`abb$!x; ya&ɃDPb߈]JbE5„|*"ÄAGQ=0cXcd݂V’HdgII$6{/9B,lRx' oA3g,q*3  "T@gbYF7O>fͻ{3ƜU$ W>X*<;<\'H#- 3NQĩI>p̚9%z}WWt$"2ܕYLW=F闷o߽zÇlܐuoI'\ұ$*n:20w_O^Yţj[֭ؔLnNLg]#+%9LD9:lAXkkc 2ưwg#d$ǔ'-EmCjt%OL$b©EWa&RRc1bpP)cI٩7LQBv~KveY0t>KD‹9Bzz`mK><kfBpy]ek)c_3TAy 08l{}Ct N{[wdʳ%yݫ[7gTi)|\}Zx_s*/>qm;cN?}ZW ˗^/틗_t}ww/>#QqzYׇ|zNK%hKĶPx[Q;֨r .)yIA,1>ls))eYe x=|L&jըxw_WgFetLiO_Qa%QFv,p 0o(R!3qm4a)$XDԜ8E&K8&Kjph"Lh!Q}HIbJAZHD< p5& n|x0iU"|IA N4.Td37Ln#6 #z#78̍)zPpс>9Y'6Ճ=-zDDWfng"G`xx%68JX܍̙/_޾u$c1/nSv߰9+.CC:IٺQF9[Bʜ%ɼ,4KRKɜӒ~zz,˜%g1(1%QUU_T=P??7?GrJcbNEZmsWއ(Ǜr(7[)ߞOa}0ww1ؙ2 !A%z:䃝^I'PMf-#" dVb rڌ DJ!D$ L ̒_h[̉K5H'p騽^.yLK┩t|rs6@V>3./ױ un@Cۙ"#|ns{v0?]V'rq>b*S<&F׍#;9P|祵GN:ߔj tS\΃"<ׇgͷz#З7O?]>_$ob%ȸenn]ƳB2ء&FRe S3-w;j8BT;G0 2"'!t F(H`Hgpg c+%>L`ṣ,yf;GKbL(mØs0z‡D'J0,9GKD0RCz F%Z2G jx@50q&j4a fQDFpr0Xܜ 3`@&@ Ia&pFox^L +%q9z!@eP{Z%]oѽH>rVG3eH1-s"e)tУnr;ӜnJIzdH}~|~f2m{dLQ[K),eɫÆ1d\rN\RɺL'"nVc=z/[7CeaJ.4$Ĕ bЈo֟ydVG69 ٭Em&^|9|ᦜ^R&ICw9oQEa SP[6Z}Ay^df94&$i/1 CNB-"iYݢ]{|3T$QǴ_?RޣևAnLb}V[7ux~׎AZp'_>}MZpF 23UвĆp$tcVQLB%mW cLSLI_,|?{>1CE!YfUR"TX52pfl:g(3;OfJnM"Ҵp -x`B`@ Q2J7Z唙wO<|Hޝ&STh Q `s1l1 l1 V#I"C2`bH=R0Y|OBD&sa!\{ UøF'!9A8XX$(BYyX`Q'LD`0"8; .bN456gb#P>_ A |5Q6K:dI:ZB8'`,G-Vw^;L2;Z}_8廟}A;HFtqᔭ|?=tgLb׬Z=nZ)rM}Omm=~xM~wt:t̢:U|tH??=FNʫB #%sswxNC(;ȸ-s>i9<cN m~>~~ov/noO/'?oy*Ӓ)q@D}ž[ЂGy9ජU;W :a|>@0wx9}IR̠Icls `7[XC'vw}v1S9SJ~)&$ =L:$!nUIS' `%f]zv15ypnt`s}m3Whwd1jlSfiLK0bmL0 P([n60aDLlp`v@@J0hT 9Gi,ovo5*\hD3L*+ O'xKD5jZK#J@(BpGS2-KJKB D!dɭVc=?_o?>z>#'VE! wگr @gK)P'E [߶r}X_,ǡt~6*p7̚:a1^fpiP{-=]4y:OeZtȖ2nY% S{.z: Qؾxx[h|#m>I׷mS9M{o?Z,c1?}zkԯϧ_ʍZ=|l52;)te0ޫ0k){Fk™h] hLrӥmj=d-p~W_owA|XLAQR}-ׇۛ >MSa1yR t)1`i2ao-G}YՅ9џ`p 'ጮ6l$7hg,#Dx)HTK3AX2*#bADT2 erDTId;{a#@Rt >a#Յ=H>$R&Pu(@:!ytpȺGy &(C'X꼁.5(i(Q`PC2Dd3+@1q8 Fh g;#лaly4"̓Cj^Ϫ9<'"MZI>͂/~ݞM c[Hu qD$,P S&51h?]R[V4&)9$V߷z?v$%z0Y0^GA-x!!TEyt`ee#%ءjZGk]LSZʪ)i0BD+)4QD=04 |b6ک}j}?<}<<dﻧ|懷@S2&l]Vs"6"˞gLZ^mGjZosrhګ=+K1SoxXnNi]*7?Z'dStz8oҾ?MC ;W3 ta}PZxq71$S?_R*S{ϗ4+/^v>|ҋy;X*Ƿ7-J 6@Ë~xw|,~=xcbIRFIbN9YY3Q\Rv%dTFu|G\ڀ0h%s bnqPC"'RAp`}7LTa4w"9PH\GQSB0QD !jDpԻP278QMI@ Bll: ܩLޯ fٺ2B`0&%sDB$) y bP.,.F1%@Bif0"%p8 3G b(@Dl HCs5D&('RE2 $7b$%!'M9ibQ-AˢBjIm.t(3"M}ַ?|ξ~Yc}۫ϧ8bl!*A#Q%yU18G0:$KI2QIrj4J\CfVԱڥڶE)Ap><)e>,IB,ލ0*H }`S>Tu\nm^zsnFf2$ ?!}L feB)Nrht՜rY!𘃌G֕?[>B42Q]7gfFls1\+4QbguNwũQ=^6}uo?\jIG7}X#rεY:S{f3jDV14;utt2M?_6bN"j۔(4==7tr~: ] }AZ:.B5$6Tӽ0xhHr{(s~џ ?Sq⏺~Ë?o}>}V6-'ѢS=q>ӻwQˏ?VϿ~HǰIuz&k3;A$$r4i /nŏ7yb\=ƈj-h&ѫo /IT2MIfQ*$)S.l: b7b wvc#H F.\fD6؈=h16E9T х @*٨H1cX1 a3K\$c I9 Q(,1;1f*P΅ ̮쑩 vDHB99C1L#HDRBA(@ !F=Xr8Ȃp((A7hD$ܞïރ'䜜ahpu$M˜E89!BSmqtpISM2~Ab͓J'>b<砜(1D}uݖ |^=Y@RVOKʙYB&gV  #8$l"Ao}fƈAVIԤrd{"4!)JsL J  `2oL1|H16~ƻ:ؽ#ʟpXDB8EߛM;qi"t2IΤ `棢jj]=K9Oi|. HN  rq*Ԃp;P.W~|ܮu KOc'ΥdHOmp%'=`m:If8 "mxH `o^ef3JOtq[WߝڳN/m!cdD:8xO?|_Z۽zQN /旷faiicGZt`n\Hki)"CBS ,o>߸}R#X0s)N/O˛/>=,,pKJ¤ZJOeV.kK bÈq$1 NcbVxݝ`^#:lĉrO%v;*<n(,J & P(s$H0R"y,X7,B-zQ#݇yyxva-sX#4٪YY4)AؙOd"N e3X qgV1C3#9VnA#``"av BIB ZU/`"%=Tl'ZވT̜IH8F!.{F6`t%L>_τi**#Bn|Hirwmu7=|xZv}'9r~yw"^{ן}ﺏLqw3ݼG_KȱNI6C,\H׭qMV > %aTBID5<ȭu7 -`zZ /^r{/_q `xJJ|4+sщcY^ݞF#jew}StjT_䧧Sbf24t2JAZ/+Qsݶ޽ۗ~:Ҭ:~Ѯm}?lׇ{Gx~/[,`{|*:A#m|d- tPjQ(I1ѢuĽ(R#Nٱܽ-:ۗxw~Yd&t(Xfn?UmV5g_qQn|=^mj5/$P?/{'nح$7Ͽ}q7LJm0_Lm?/w9] ;psB݇+y()f͔2M _0XwD0eC4/qC~ly9OsE)݉{iC2[B`2@Jn6 |ĤV(!LGGsA8H, w YwdHii83 cDElZRI9B(3@J2SzXh)2L0&sʅ(4,,d4A=S3)l$1\)836J"@)藰B0RȔADa4@Ypia"Ƣ%-:@IHS Fy(8f DB@Lp'y˯yX/z;c8g5JiKV9e${ׂC5ʁ-('xᰨ|%)-8(( aNDc:{츶WQdv #.$ qa@c8fyZfr8&N}%~xjo{gRoM0wMd`4C1K$M!L0.7#bI(o;}G>*ٻؕsнV[X`?nixyB,Ipp GkbKCʁy.3'"6Lb۶Fޮ|y1R$>-1iJ9 krƒ b>Ki( pw>em{>ݺA=W:i[醈72'Z |:M y*:OPL2IxҢ8(//P~zzz}*ƀ Z8<ݻ~1cʎ'H&W}GGc>mۧR!ʸ=728Ny9%ha@Ǜ?;|ߜ^|z<~~z'{Iszͷv*ts|w6D[wqbr}~q4tG8F%M_0re9n/oqN64Q K&.sYSgfxV"R-CIF:7y`]tsN:Rpwq#<fH z B LS1o>MшRÄtmM)МZ{=ۊѭG%q(3ty;DI<X =LF҅Ç.9A= "{PBX@SVL*1q&D> &;Ac81nP#0"`$ #i Z41ũ7prfH#XhTD&x0XSP F(I G~N?E`ީ1fI[Oy6JAkK|ՁQtlaIyMEQn"P V[o,uDH#\I*1%\N'Qܽ,%Km_ TrbQJ,~(eQg֯Y1ݼٛfx\rII>LI|H6e S2˨պ!}5|po[4Daؤq! j@>؉<|A'~%Y$n:|]p@[9N.:rP ,ZERpچF_Uh+QRIΒ'f$&Z\ӒKPcVXp\-G`^^ߏךitՑGۙ=)=_)O,lUPkZX5X[kRt>_V)|~o,|r:ޒպ|_tʧ|I'û~sOR{4߬{Ւuݛr:8,K?l5:/?m߾C>~Ǐ=})~~q98o1I9ћnO_{|yxnA}G?74||۞uk\XM2LTr9&42LϓNP8p3AI^0;1Zk) IIE|3*\kpAJ#1!{:X*=JgY$`>c )))ψ-S^I}/YV7eވ8cA{D#-+qb.Q4$c!$B1#.A»o?<&\1'%[k&"ii"aB>D>KSITLdYHSCp&eA_JOn~T}5ϖ up%C Rps?Dhn%ZF(R?]g}n!oƺ{;fceۛ?1+h)ɧSpPrF虶|)䴈&b+('ɖ,H*p)D83r;iʮIb뤙ƀ!21$Fx3{cȅ#h*z%8+:9% ?pfkMo?QhAl)h"s %CfD;&!)'Et&Uk\ C)DrafQ"ug@-1`@µ"eȱY+9R!KD PD@g*:눮ѯBppg&W67`gv F2' 1:4r~,ês!#לEpX,[t$e '_v#UFH,Ҕ`d[xS#Q& g`Bc^F/] ȈIHp>hK|j,+0AY)d*K&>{TrjhHr8ҲX t|Q %7oK2 Ejj>0 )>6GhmhhYGwe.$O?Ӎ[tD"b&NJY<Jct锒·LK|ڶ- M棔c^޾<yz=~=-^p۳pbǁׯ\x?=.}a:d1އQ!T>zyݺ}\>;yIѯ6_XAgΒ7L'*Sے@a̋Jyų zP;#BZ69vZf "Icx4Ibw&"hL}@P"@J(8$TN t'GA !cAD$'sWO®!2F  8gALЙj-7B^郤Ĝ&gp  g @BhS S "O J4*R6<{b 'ZAɻYR@S(X\]} PJE pYw"3 ͈.DzDe"F\\%*C4!DvOmj6y:޶A)Ưiu7GIuJP/Z h@ | H&DX M@R9NaDaDGwD[ၜw0SA:#(0JA VeAERp2+ X`4 zfnp̸m(HY TFa4@L B8j-%a;!+iԅRJut }cRwh Iet"vy8Z)a΋V4|nh6z;<[E$0 <޺I $FBG*Q^[Wy4?8γtXNxsɊNrQXҬLk?7LEBQݔn}rv?Clo秱wWn)x/ត=]ˌ_!ASG+燷urOO_a=_YƢ\ڂۇ˷?}sz3Ǐ?n[iպKͧo>y?VI\vMXJhL=g6!R8̔ޗi*GUbB}VHSHP-|TǸ !m2T`!i j4$i|`1CMc1 4+#"Lu%@S»q!6If6N*kDlHXN.( !Gތ%Ew7@2a\HT%10\ ` 9vbD EFx& t)88cd2 1 9Q@21(qt_{siOxS, ^O<,IO=T٩k9R۩uA"&L.ѨtL9$L^w61QwHNTyΚo@ ^ ZmE6in4j/?R)'ηwS0t%zg;ުlOOn]uE=ƀBH#"ڤ%r˟A501Z L9s]c~x8 K”Q;sXj[<|:c4%7]tq Dgօ#O)Yu2AvF=߼-;:RI Ұ7~Q|zɩޚm޺褚PCxm4OcNNsl9Czc exw)ݓXpG vc򐃲;pP.Z'gXd]0á]#`A3+`v@# $ʑ l3t r|G ŐH9y$hO`0-J@wZAx^xY$ Pz.kB`ނ R,<\'\(@S!s>RPB_QLjEP@R$8` F,  qO]۰dcw}w`shgܖ,Gd08nWȤM۵h1iJel=H5=,$:\&꤬Ć{j5*"\dPf`Z䩶h@#'6BnkVԾ?z魞S^@գ gA~)g[&%)KfHf%N=".ჼ^cL\@^/(E,I$sRS$m8PL$ӧtX&)@ .́FygȾeiNʚSY9\ؠ%4#[Έg1$57Sjakcg)~Z=vYQ&`aWN^r*$^m 1N/NNkL ӼoxDt|1}ӛsݟ"$r??My~OƥOBm9O?=?o~_ސ.$_).˟on:Oy),;]N/.?yG}8qcCư}"7~;g8&Y5ٽp#]XOF΃/`Ց訕$|D jeb0y Q@RoS` RɉA&lY0@@$Ijh`9԰JePԝ@! FG>ШiDDϙ^򽠇gOGCE:"yfEt`'"6b @艰C&H网=(C ear)6 A"`WB̠jq g55 a}"T)< rV!GN`53qKAÙ@NZ~~5R#$UF1*}ߟ׽|czeGeUo;%QJx+HG(` Q%H}UV )bf6)bK";u43%Y4cGQ[&u1HEMDya GK +:-yW:Jt>uKѧVPI$d}wa}hՁdAr\]ϔo(@J,HB@qiOO>DfVKFAPPmRܼ:r8-w?f$AƢ6Fb{ͨ1j>~)LZ6$DC]mt鉰F`MH"mdǢKFkÛVpֽ\{?O/ȷ:bH.6K /9Jn5D{}o^ܕfs</?>ok}|"J57_%:ycE:.{O]]s_nCt[ Ñ1㇔?Bw{6(r|sszxs0}wճ]oo_gBcYMJ*n0"v|9ҾJsmI:0@DzǜY2cfW`fb%U#$VJ.@=BA爌,t "&Mhl' Wc4 a !h%gV C< b& &E)#1AhCU* PGLq!(i4:2B[`&f 7!!J r)y% #KZuEd"!9,9rGXB08 RN~n 1b1Tu KA լQ%$]$]pL6F$TR(T)!xւ#ƶ9 梓0F"ӛWa;o]R͋ 4)Mx푼5:j~׾oב [#vdXovi;, -$)w@c6ŋ39!5#oވ 'k#1K1ϳ /#/)3¹7 1$83LĘM ᓲ!m>fv/w/0 Mĩ/ cV-f}zʰ#,ߪN9Pa6d䗎.dc4"֦tkc>{} ʔʲaUX\A K;%ytԭRl㫸^?_^tb 2&&yxig_?>ի?y9Y?oGӯ.?}uxyxRҫR-O9^f1Z߫ʾ+W"ʲ 6rwBJTRB^7fJJhF@ 4#( N$\J"#w4- 5nL\[8)Q9x2k! q2prB일2 [G JCz:3~vIv3!$<&fJ&2Q;Gn=aFA-̨aҕ@ln{LQ0ְθ6h }(7J!cqض BD ZF<8tDrɄaDD!V è= x'ǝ 6i-ȅ؂l;-LF8EֵZwGL$AR", smPP*NEGJvåb6ȒbX+T{(I[bTR D,]gƭp!ڨ;EFpA~^ƺ =&2Ģ'̹%ձ,ɂt|(7IdL7EZ[.N3%7#LJm@a7sG*mv6}#NwNN?i2Mr:)- r>ʟp T-<[Xūn_Mwo"H!eV=uمdNSV0qY8D4g*sƠcC6Ԩ2_Ӄa凇&)|uǽuKbDf@n_sy*I쵧2U>([I8Maj'/np(›gOjI], <ݧ޼z%B~O"MZ?lOqje{%n/>|:Dǯby  N9/V@]8{.]y5,%Pؖ\f5ꥵ-!Qb1=qp(*49$귇21G)3U̦W]p3NpxN9IN#L܃%GzUZ@.̮<'*dbzT!08qa[]TDG2L!9+S3O-*,YB}rRqa#bic<"%:A9WE ij.DcFJAue]+fC6ոNAc&&8 G`TI),e~mi>cDx,7w+BV+DDFu)ɻ!b]")F,|ɣv'jJ䡬^kJYSDuNebR&"׫73okX<* sX]wΠCFR" ضk }K[.cmߍձZErs 1o8$/%~˔%OD)pϜ9lȬ%r %N$(Of!OA$u HiT#FXirr&."AyDYT z LvDDI:aNa7߬J}x_f# n,Qgm^?; @0S)ע>nn/7/CAfR4/r# n_ޤ哴vyeqb֜#GC82qD1ht NJevnF92S;{JNxD"PAtNHdD12GA/FM 9B$]ى`;0Eۤyd;AH /yJP+kÔB"!Y>28Ba%FN>'Μ;I =+_cqK>$ 9&qw{2c1 h %4 VcRҝ@ "H} vjh[B24DŃ"p<pe Ea &GDQph,% AHDNڞW߼{eU'787p&\KT&NB" )Rg8|"3Fwn0ifKdE8|HL)iazJsW@,aBrxK]cI{A<]!uT՘6tL%ʹx9}!Z1 [LjṰu:jk7;\n-I{>skhc*eI.ӫ]{p*DҶ;lwfNA.wr(ś #8ona֛7=^%xΓn޼yТ߾/wq7mXrxz)/G-zO~GߤGAY׭/m|8.צ\Zmry?ƾ}bOas<ɨSқ4eZf=M!f: bM:{GjY ƒ,LgC0)~~wI4rФ)ii)MBE=H~p$Y} [UxB)_/u7D [XM.G_(zYKۯk7o_ݴp\}VrA2iĜn=_9e:jWٟzz쏽ZZO‰2zys|yFҭdM8; 1BIX*Td e N @%1]mUP\Y !Qߠ K4𠰐YDpswQTdP SĠ 3S$"+I4 abf"PpcDx. ]U}lD켙x<.9m\XHqDhwIibfaV6!w>,b=֢6,6yy*ǛC[kk9'N MA#s}̷Y0hkyOX!"@H'Ms_m 㴼nOFdoP'z5߯:-s=_T@bw_^>*?|e]RBJ5/~=_RHrju4#z'_?>'RVqOﷇ/ȣXn'ڬMR|H" "FD`v#DH1 bVn&ic7QӍ3ά &KYVIYAQGGZR a컃D  ٍGI+0vH ,P'꒝`74%#*pXW'20vN3Eb`%H$; &4PQ3$9d=R1(WxGQnh`9`AN,]/)0 ;c"p !3#(w`xfGtHY4mOɚj$1Ev!g4 vمrM &)fU9I(&pL佹X1Z`mzPN꽹eIz8pqsTm>*wEJCYg^< S`eep"94ٶCݞچ};[k4mN[[ FuPR Q5#FlQ^DH*>&1O)dffadމCc:ݽ `xG`'X?>^CY"ETWQVAJ)Q(%WF&R{DA!䵐Nr~<cNH_aJ^$WH˟,AnMOVǛ;0?=}IIO6/[h{4oOܽT28=?>/yzq7_]ˇ]/S3%>K6җƹLj*y\5=L簖'򧅖~=}Q)_O?mLI2K8BXj"Ԝ&qJ;v@=K)b6*݂,bD.d B$LNѫp2wD PGXfLoXc,bsΤs8H 1k6Bt$o8d -0%@B4X9&";sD:fRj3Ċ<[DGLL=M@C$F*fHf DA !„D~FEt:f,!fȧivf 1)91(8$N1lBFNA491W)ɛE 0y /xԟ*2ݲ3) '.aS?EBĒ&~ %Qe@aAiuJ qD憀nٶ>%rnSX0ڼM:ZDJ:0a #b=1פZ2,%炉4pn\0bpG^ޝ~u>zpLBf 1* Oit;w\̷7zH~(EGU̚'bڎP6IENDB`mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgCompressor.cpp000664 001750 001750 00000007051 15164251010 034660 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include "tvgCommon.h" #include "tvgCompressor.h" namespace tvg { /************************************************************************/ /* B64 Implementation */ /************************************************************************/ size_t b64Decode(const char* encoded, const size_t len, char** decoded) { static constexpr const char B64_INDEX[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; if (!decoded || !encoded || len == 0) return 0; auto reserved = 3 * (1 + (len >> 2)) + 1; auto output = tvg::malloc(reserved * sizeof(char)); if (!output) return 0; output[reserved - 1] = '\0'; size_t idx = 0; while (*encoded && *(encoded + 1)) { if (*encoded <= 0x20) { ++encoded; continue; } auto value1 = B64_INDEX[(size_t)encoded[0]]; auto value2 = B64_INDEX[(size_t)encoded[1]]; output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4); if (!encoded[2] || encoded[3] < 0 || encoded[2] == '=' || encoded[2] == '.') break; auto value3 = B64_INDEX[(size_t)encoded[2]]; output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2); if (!encoded[3] || encoded[3] < 0 || encoded[3] == '=' || encoded[3] == '.') break; auto value4 = B64_INDEX[(size_t)encoded[3]]; output[idx++] = ((value3 & 0x03) << 6) + value4; encoded += 4; } *decoded = output; return idx; } /************************************************************************/ /* DJB2 Implementation */ /************************************************************************/ unsigned long djb2Encode(const char* str) { if (!str) return 0; unsigned long hash = 5381; int c; while ((c = *str++)) { hash = ((hash << 5) + hash) + c; // hash * 33 + c } return hash; } }mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/ttf/tvgTtfReader.h000664 001750 001750 00000006203 15164251010 035005 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_TTF_READER_H #define _TVG_TTF_READER_H #include #include "tvgRender.h" #define INVALID_GLYPH ((uint32_t)-1) struct TtfGlyph { uint32_t idx; //glyph index float advance; //advance width/height float lsb; //left side bearing float y; //y-offset float w, h; //bounding box }; struct TtfGlyphMetrics : TtfGlyph { RenderPath path; //outline path }; struct TtfReader { public: uint8_t* data = nullptr; uint32_t size = 0; struct { TextMetrics hhea; //horizontal header info uint16_t unitsPerEm; uint16_t numHmtx; //the number of Horizontal metrics table uint8_t locaFormat; //0 for short offsets, 1 for long } metrics; bool header(); uint32_t glyph(uint32_t codepoint, TtfGlyphMetrics* tgm); bool kerning(uint32_t lglyph, uint32_t rglyph, Point& out); bool convert(RenderPath& path, TtfGlyph& glyph, uint32_t glyphOffset, const Point& offset, uint16_t depth); private: //table offsets atomic cmap{}; atomic hmtx{}; atomic loca{}; atomic glyf{}; atomic kern{}; atomic maxp{}; uint32_t cmap_12_13(uint32_t table, uint32_t codepoint, int which) const; uint32_t cmap_4(uint32_t table, uint32_t codepoint) const; uint32_t cmap_6(uint32_t table, uint32_t codepoint) const; bool validate(uint32_t offset, uint32_t margin) const; uint32_t table(const char* tag); uint32_t outlineOffset(uint32_t glyph); uint32_t glyph(uint32_t codepoint); uint32_t glyphMetrics(TtfGlyph& glyph); bool convertComposite(RenderPath& path, TtfGlyph& glyph, uint32_t glyphOffset, const Point& offset, uint16_t depth); bool genPath(uint8_t* flags, uint16_t basePoint, uint16_t count); bool points(uint32_t outline, uint8_t* flags, Point* pts, uint32_t ptsCnt, const Point& offset); bool flags(uint32_t *outline, uint8_t* flags, uint32_t flagsCnt); }; #endif //_TVG_TTF_READER_Hexternal/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-lcache.cpp000664 001750 001750 00000014335 15164251010 043772 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-lcache.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmalcache Property lookup cache * @{ */ #if JERRY_LCACHE /** * Bitshift index for calculating hash. */ #define ECMA_LCACHE_HASH_BITSHIFT_INDEX 0 /** * Mask for hash bits */ #define ECMA_LCACHE_HASH_MASK ((ECMA_LCACHE_HASH_ROWS_COUNT - 1) << ECMA_LCACHE_HASH_BITSHIFT_INDEX) /** * Bitshift index for creating property identifier */ #define ECMA_LCACHE_HASH_ENTRY_ID_SHIFT (8 * sizeof (jmem_cpointer_t)) /** * Create property identifier */ #define ECMA_LCACHE_CREATE_ID(object_cp, name_cp) \ (((ecma_lcache_hash_entry_id_t) (object_cp) << ECMA_LCACHE_HASH_ENTRY_ID_SHIFT) | (name_cp)) /** * Invalidate specified LCache entry */ static inline void ecma_lcache_invalidate_entry (ecma_lcache_hash_entry_t *entry_p) /**< entry to invalidate */ { JERRY_ASSERT (entry_p != NULL); JERRY_ASSERT (entry_p->id != 0); JERRY_ASSERT (entry_p->prop_p != NULL); entry_p->id = 0; ecma_set_property_lcached (entry_p->prop_p, false); } /* ecma_lcache_invalidate_entry */ /** * Compute the row index of object / property name pair * * @return row index */ static inline size_t ecma_lcache_row_index (jmem_cpointer_t object_cp, /**< compressed pointer to object */ jmem_cpointer_t name_cp) /**< compressed pointer to property name */ { /* Randomize the property name with the object pointer using a xor operation, * so properties of different objects with the same name can be cached effectively. */ return (size_t) (((name_cp ^ object_cp) & ECMA_LCACHE_HASH_MASK) >> ECMA_LCACHE_HASH_BITSHIFT_INDEX); } /* ecma_lcache_row_index */ /** * Insert an entry into LCache */ void ecma_lcache_insert (const ecma_object_t *object_p, /**< object */ const jmem_cpointer_t name_cp, /**< property name */ ecma_property_t *prop_p) /**< property */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_p != NULL && !ecma_is_property_lcached (prop_p)); JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*prop_p)); jmem_cpointer_t object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); size_t row_index = ecma_lcache_row_index (object_cp, name_cp); ecma_lcache_hash_entry_t *entry_p = JERRY_CONTEXT (lcache)[row_index]; ecma_lcache_hash_entry_t *entry_end_p = entry_p + ECMA_LCACHE_HASH_ROW_LENGTH; do { if (entry_p->id == 0) { goto insert; } entry_p++; } while (entry_p < entry_end_p); /* Invalidate the last entry. */ ecma_lcache_invalidate_entry (--entry_p); /* Shift other entries towards the end. */ for (uint32_t i = 0; i < ECMA_LCACHE_HASH_ROW_LENGTH - 1; i++) { entry_p->id = entry_p[-1].id; entry_p->prop_p = entry_p[-1].prop_p; entry_p--; } insert: entry_p->prop_p = prop_p; entry_p->id = ECMA_LCACHE_CREATE_ID (object_cp, name_cp); ecma_set_property_lcached (entry_p->prop_p, true); } /* ecma_lcache_insert */ /** * Lookup property in the LCache * * @return a pointer to an ecma_property_t if the lookup is successful * NULL otherwise */ ecma_property_t * ecma_lcache_lookup (const ecma_object_t *object_p, /**< object */ const ecma_string_t *prop_name_p) /**< property's name */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_name_p != NULL); jmem_cpointer_t object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); ecma_property_t prop_name_type = ECMA_DIRECT_STRING_PTR; jmem_cpointer_t prop_name_cp; if (JERRY_UNLIKELY (ECMA_IS_DIRECT_STRING (prop_name_p))) { prop_name_type = (ecma_property_t) ECMA_GET_DIRECT_STRING_TYPE (prop_name_p); prop_name_cp = (jmem_cpointer_t) ECMA_GET_DIRECT_STRING_VALUE (prop_name_p); } else { ECMA_SET_NON_NULL_POINTER (prop_name_cp, prop_name_p); } size_t row_index = ecma_lcache_row_index (object_cp, prop_name_cp); ecma_lcache_hash_entry_t *entry_p = JERRY_CONTEXT (lcache)[row_index]; ecma_lcache_hash_entry_t *entry_end_p = entry_p + ECMA_LCACHE_HASH_ROW_LENGTH; ecma_lcache_hash_entry_id_t id = ECMA_LCACHE_CREATE_ID (object_cp, prop_name_cp); do { if (entry_p->id == id && JERRY_LIKELY (ECMA_PROPERTY_GET_NAME_TYPE (*entry_p->prop_p) == prop_name_type)) { JERRY_ASSERT (entry_p->prop_p != NULL && ecma_is_property_lcached (entry_p->prop_p)); return entry_p->prop_p; } entry_p++; } while (entry_p < entry_end_p); return NULL; } /* ecma_lcache_lookup */ /** * Invalidate LCache entries associated with given object and property name / property */ void ecma_lcache_invalidate (const ecma_object_t *object_p, /**< object */ const jmem_cpointer_t name_cp, /**< property name */ ecma_property_t *prop_p) /**< property */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_p != NULL && ecma_is_property_lcached (prop_p)); JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*prop_p)); jmem_cpointer_t object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); size_t row_index = ecma_lcache_row_index (object_cp, name_cp); ecma_lcache_hash_entry_t *entry_p = JERRY_CONTEXT (lcache)[row_index]; while (true) { /* The property must be present. */ JERRY_ASSERT (entry_p - JERRY_CONTEXT (lcache)[row_index] < ECMA_LCACHE_HASH_ROW_LENGTH); if (entry_p->id != 0 && entry_p->prop_p == prop_p) { JERRY_ASSERT (entry_p->id == ECMA_LCACHE_CREATE_ID (object_cp, name_cp)); ecma_lcache_invalidate_entry (entry_p); return; } entry_p++; } } /* ecma_lcache_invalidate */ #endif /* JERRY_LCACHE */ /** * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgPipelines.cpp000664 001750 001750 00000075061 15164251010 037161 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgShaderSrc.h" #include "tvgWgPipelines.h" #include #include WGPUShaderModule WgPipelines::createShaderModule(WGPUDevice device, const char* label, const char* code) { WGPUShaderSourceWGSL shaderSourceWGSL { .chain = { .sType = WGPUSType_ShaderSourceWGSL }, .code = { .data = code, .length = WGPU_STRLEN } }; const WGPUShaderModuleDescriptor shaderModuleDesc { .nextInChain = &shaderSourceWGSL.chain, .label = { .data = label, .length = WGPU_STRLEN } }; return wgpuDeviceCreateShaderModule(device, &shaderModuleDesc); } WGPUPipelineLayout WgPipelines::createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, const uint32_t bindGroupLayoutsCount) { const WGPUPipelineLayoutDescriptor pipelineLayoutDesc { .bindGroupLayoutCount = bindGroupLayoutsCount, .bindGroupLayouts = bindGroupLayouts }; return wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc); } WGPURenderPipeline WgPipelines::createRenderPipeline( WGPUDevice device, const char* pipelineLabel, const WGPUShaderModule shaderModule, const char* vsEntryPoint, const char* fsEntryPoint, const WGPUPipelineLayout pipelineLayout, const WGPUVertexBufferLayout *vertexBufferLayouts, const uint32_t vertexBufferLayoutsCount, const WGPUColorWriteMask writeMask, const WGPUTextureFormat colorTargetFormat, const WGPUBlendState blendState, const WGPUDepthStencilState depthStencilState, const WGPUMultisampleState multisampleState) { const WGPUColorTargetState colorTargetState { .format = colorTargetFormat, .blend = &blendState, .writeMask = writeMask }; const WGPUColorTargetState colorTargetStates[] { colorTargetState }; const WGPUPrimitiveState primitiveState { .topology = WGPUPrimitiveTopology_TriangleList }; const WGPUVertexState vertexState { .module = shaderModule, .entryPoint = { .data = vsEntryPoint, .length = WGPU_STRLEN }, .bufferCount = vertexBufferLayoutsCount, .buffers = vertexBufferLayouts }; const WGPUFragmentState fragmentState { .module = shaderModule, .entryPoint = { .data = fsEntryPoint, .length = WGPU_STRLEN }, .targetCount = 1, .targets = colorTargetStates }; const WGPURenderPipelineDescriptor renderPipelineDesc { .label = { .data = pipelineLabel, .length = WGPU_STRLEN }, .layout = pipelineLayout, .vertex = vertexState, .primitive = primitiveState, .depthStencil = &depthStencilState, .multisample = multisampleState, .fragment = &fragmentState }; return wgpuDeviceCreateRenderPipeline(device, &renderPipelineDesc); } WGPUComputePipeline WgPipelines::createComputePipeline( WGPUDevice device, const char* pipelineLabel, const WGPUShaderModule shaderModule, const char* entryPoint, const WGPUPipelineLayout pipelineLayout) { const WGPUComputePipelineDescriptor computePipelineDesc{ .label = { .data = pipelineLabel, .length = WGPU_STRLEN }, .layout = pipelineLayout, .compute = { .module = shaderModule, .entryPoint = { .data = entryPoint, .length = WGPU_STRLEN } } }; return wgpuDeviceCreateComputePipeline(device, &computePipelineDesc); } void WgPipelines::releaseComputePipeline(WGPUComputePipeline& computePipeline) { if (computePipeline) { wgpuComputePipelineRelease(computePipeline); computePipeline = nullptr; } } void WgPipelines::releaseRenderPipeline(WGPURenderPipeline& renderPipeline) { if (renderPipeline) { wgpuRenderPipelineRelease(renderPipeline); renderPipeline = nullptr; } } void WgPipelines::releasePipelineLayout(WGPUPipelineLayout& pipelineLayout) { if (pipelineLayout) { wgpuPipelineLayoutRelease(pipelineLayout); pipelineLayout = nullptr; } } void WgPipelines::releaseShaderModule(WGPUShaderModule& shaderModule) { if (shaderModule) { wgpuShaderModuleRelease(shaderModule); shaderModule = nullptr; } } WGPUDepthStencilState WgPipelines::makeDepthStencilState( const WGPUCompareFunction depthCompare, WGPUOptionalBool depthWriteEnabled, const WGPUCompareFunction stencilFunction, const WGPUStencilOperation stencilOperation) { return makeDepthStencilState(depthCompare, depthWriteEnabled, stencilFunction, stencilOperation, stencilFunction, stencilOperation); } WGPUDepthStencilState WgPipelines::makeDepthStencilState( const WGPUCompareFunction depthCompare, WGPUOptionalBool depthWriteEnabled, const WGPUCompareFunction stencilFunctionFrnt, const WGPUStencilOperation stencilOperationFrnt, const WGPUCompareFunction stencilFunctionBack, const WGPUStencilOperation stencilOperationBack) { const WGPUDepthStencilState depthStencilState { .format = WGPUTextureFormat_Depth24PlusStencil8, .depthWriteEnabled = depthWriteEnabled, .depthCompare = depthCompare, .stencilFront = { .compare = stencilFunctionFrnt, .failOp = stencilOperationFrnt, .depthFailOp = WGPUStencilOperation_Zero, .passOp = stencilOperationFrnt }, .stencilBack = { .compare = stencilFunctionBack, .failOp = stencilOperationBack, .depthFailOp = WGPUStencilOperation_Zero, .passOp = stencilOperationBack }, .stencilReadMask = 0xFFFFFFFF, .stencilWriteMask = 0xFFFFFFFF }; return depthStencilState; } void WgPipelines::initialize(WgContext& context) { // common pipeline settings const WGPUVertexAttribute vertexAttributePos { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 0 }; const WGPUVertexAttribute vertexAttributeTex { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 1 }; const WGPUVertexAttribute vertexAttributesPos[] { vertexAttributePos }; const WGPUVertexAttribute vertexAttributesTex[] { vertexAttributeTex }; const WGPUVertexBufferLayout vertexBufferLayoutPos { .stepMode = WGPUVertexStepMode_Vertex, .arrayStride = 8, .attributeCount = 1, .attributes = vertexAttributesPos }; const WGPUVertexBufferLayout vertexBufferLayoutTex { .stepMode = WGPUVertexStepMode_Vertex, .arrayStride = 8, .attributeCount = 1, .attributes = vertexAttributesTex }; const WGPUVertexBufferLayout vertexBufferLayoutsShape[] { vertexBufferLayoutPos }; const WGPUVertexBufferLayout vertexBufferLayoutsImage[] { vertexBufferLayoutPos, vertexBufferLayoutTex }; const WGPUMultisampleState multisampleState { .count = 4, .mask = 0xFFFFFFFF, .alphaToCoverageEnabled = false }; const WGPUMultisampleState multisampleStateX1 { .count = 1, .mask = 0xFFFFFFFF, .alphaToCoverageEnabled = false }; const WGPUTextureFormat offscreenTargetFormat = WGPUTextureFormat_RGBA8Unorm; // blend states WGPUBlendComponent blendComponentSrc { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_Zero }; WGPUBlendComponent blendComponentNrm { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha }; const WGPUBlendState blendStateSrc { .color = blendComponentSrc, .alpha = blendComponentSrc }; const WGPUBlendState blendStateNrm { .color = blendComponentNrm, .alpha = blendComponentNrm }; const WgBindGroupLayouts& layouts = context.layouts; // bind group layouts helpers const WGPUBindGroupLayout bindGroupLayoutsStencil[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un }; const WGPUBindGroupLayout bindGroupLayoutsDepth[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutBuffer1Un }; // bind group layouts normal blend const WGPUBindGroupLayout bindGroupLayoutsSolid[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un }; const WGPUBindGroupLayout bindGroupLayoutsGradient[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsImage[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsScene[] { layouts.layoutTexSampled, layouts.layoutBuffer1Un }; // bind group layouts custom blend const WGPUBindGroupLayout bindGroupLayoutsSolidBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsGradientBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsImageBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsSceneBlend[] { layouts.layoutTexSampled, layouts.layoutTexSampled, layouts.layoutBuffer1Un }; // bind group layouts scene compose const WGPUBindGroupLayout bindGroupLayoutsSceneCompose[] { layouts.layoutTexSampled, layouts.layoutTexSampled }; // bind group layouts blit const WGPUBindGroupLayout bindGroupLayoutsBlit[] { layouts.layoutTexSampled }; // bind group layouts effects const WGPUBindGroupLayout bindGroupLayoutsShadow[] { layouts.layoutTexSampled, layouts.layoutTexSampled, layouts.layoutBuffer1Un }; const WGPUBindGroupLayout bindGroupLayoutsEffects[] { layouts.layoutTexSampled, layouts.layoutBuffer1Un }; // depth stencil state markup const WGPUDepthStencilState depthStencilStateNonZero = makeDepthStencilState(WGPUCompareFunction_Always, WGPUOptionalBool_False, WGPUCompareFunction_Always, WGPUStencilOperation_IncrementWrap, WGPUCompareFunction_Always, WGPUStencilOperation_DecrementWrap); const WGPUDepthStencilState depthStencilStateEvenOdd = makeDepthStencilState(WGPUCompareFunction_Always, WGPUOptionalBool_False, WGPUCompareFunction_Always, WGPUStencilOperation_Invert); const WGPUDepthStencilState depthStencilStateDirect = makeDepthStencilState(WGPUCompareFunction_Always, WGPUOptionalBool_False, WGPUCompareFunction_Always, WGPUStencilOperation_Replace); // depth stencil state clip path const WGPUDepthStencilState depthStencilStateCopyStencilToDepth = makeDepthStencilState(WGPUCompareFunction_Always, WGPUOptionalBool_True, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero); const WGPUDepthStencilState depthStencilStateCopyStencilToDepthInt = makeDepthStencilState(WGPUCompareFunction_Greater, WGPUOptionalBool_True, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero); const WGPUDepthStencilState depthStencilStateCopyDepthToStencil = makeDepthStencilState(WGPUCompareFunction_Equal, WGPUOptionalBool_False, WGPUCompareFunction_Always, WGPUStencilOperation_Replace); const WGPUDepthStencilState depthStencilStateMergeDepthStencil = makeDepthStencilState(WGPUCompareFunction_Equal, WGPUOptionalBool_True, WGPUCompareFunction_Always, WGPUStencilOperation_Keep); const WGPUDepthStencilState depthStencilStateClearDepth = makeDepthStencilState(WGPUCompareFunction_Always, WGPUOptionalBool_True, WGPUCompareFunction_Always, WGPUStencilOperation_Keep); // depth stencil state blend, compose and blit const WGPUDepthStencilState depthStencilStateShape = makeDepthStencilState(WGPUCompareFunction_Always, WGPUOptionalBool_False, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero); const WGPUDepthStencilState depthStencilStateScene = makeDepthStencilState(WGPUCompareFunction_Always, WGPUOptionalBool_False, WGPUCompareFunction_Always, WGPUStencilOperation_Zero); // shaders char shaderSourceBuff[16384]{}; shader_stencil = createShaderModule(context.device, "The shader stencil", cShaderSrc_Stencil); shader_depth = createShaderModule(context.device, "The shader depth", cShaderSrc_Depth); // shader normal blend shader_solid = createShaderModule(context.device, "The shader solid", cShaderSrc_Solid); shader_radial = createShaderModule(context.device, "The shader radial", cShaderSrc_Radial); shader_linear = createShaderModule(context.device, "The shader linear", cShaderSrc_Linear); shader_image = createShaderModule(context.device, "The shader image", cShaderSrc_Image); shader_scene = createShaderModule(context.device, "The shader scene", cShaderSrc_Scene); // shader custom blend shader_solid_blend = createShaderModule(context.device, "The shader blend solid", strcat(strcpy(shaderSourceBuff, cShaderSrc_Solid_Blend), cShaderSrc_BlendFuncs)); shader_linear_blend = createShaderModule(context.device, "The shader blend linear", strcat(strcpy(shaderSourceBuff, cShaderSrc_Linear_Blend), cShaderSrc_BlendFuncs)); shader_radial_blend = createShaderModule(context.device, "The shader blend radial", strcat(strcpy(shaderSourceBuff, cShaderSrc_Radial_Blend), cShaderSrc_BlendFuncs)); shader_image_blend = createShaderModule(context.device, "The shader blend image", strcat(strcpy(shaderSourceBuff, cShaderSrc_Image_Blend), cShaderSrc_BlendFuncs)); shader_scene_blend = createShaderModule(context.device, "The shader blend scene", strcat(strcpy(shaderSourceBuff, cShaderSrc_Scene_Blend), cShaderSrc_BlendFuncs)); // shader compose shader_scene_compose = createShaderModule(context.device, "The shader scene composition", cShaderSrc_Scene_Compose); // shader blit shader_blit = createShaderModule(context.device, "The shader blit", cShaderSrc_Blit); // shader effects shader_shadow = createShaderModule(context.device, "The shader effects", cShaderSrc_Shadow); shader_effects = createShaderModule(context.device, "The shader effects", cShaderSrc_Effects); // layouts layout_stencil = createPipelineLayout(context.device, bindGroupLayoutsStencil, 2); layout_depth = createPipelineLayout(context.device, bindGroupLayoutsDepth, 3); // layouts normal blend layout_solid = createPipelineLayout(context.device, bindGroupLayoutsSolid, 2); layout_gradient = createPipelineLayout(context.device, bindGroupLayoutsGradient, 3); layout_image = createPipelineLayout(context.device, bindGroupLayoutsImage, 3); layout_scene = createPipelineLayout(context.device, bindGroupLayoutsScene, 2); // layouts custom blend layout_solid_blend = createPipelineLayout(context.device, bindGroupLayoutsSolidBlend, 4); layout_gradient_blend = createPipelineLayout(context.device, bindGroupLayoutsGradientBlend, 4); layout_image_blend = createPipelineLayout(context.device, bindGroupLayoutsImageBlend, 4); layout_scene_blend = createPipelineLayout(context.device, bindGroupLayoutsSceneBlend, 3); // layout compose layout_scene_compose = createPipelineLayout(context.device, bindGroupLayoutsSceneCompose, 2); // layout blit layout_blit = createPipelineLayout(context.device, bindGroupLayoutsBlit, 1); // layout effects layout_shadow = createPipelineLayout(context.device, bindGroupLayoutsShadow, 3); layout_effects = createPipelineLayout(context.device, bindGroupLayoutsEffects, 2); // render pipeline nonzero nonzero = createRenderPipeline( context.device, "The render pipeline nonzero", shader_stencil, "vs_main", "fs_main", layout_stencil, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_None, offscreenTargetFormat, blendStateSrc, depthStencilStateNonZero, multisampleState); // render pipeline even-odd evenodd = createRenderPipeline( context.device, "The render pipeline even-odd", shader_stencil, "vs_main", "fs_main", layout_stencil, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_None, offscreenTargetFormat, blendStateSrc, depthStencilStateEvenOdd, multisampleState); // render pipeline direct direct = createRenderPipeline( context.device, "The render pipeline direct", shader_stencil, "vs_main", "fs_main", layout_stencil, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_None, offscreenTargetFormat, blendStateSrc, depthStencilStateDirect, multisampleState); // render pipeline copy stencil to depth (front) copy_stencil_to_depth = createRenderPipeline( context.device, "The render pipeline copy stencil to depth front", shader_depth, "vs_main", "fs_main", layout_depth, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_None, offscreenTargetFormat, blendStateSrc, depthStencilStateCopyStencilToDepth , multisampleState); // render pipeline copy stencil to depth (intermediate) copy_stencil_to_depth_interm = createRenderPipeline( context.device, "The render pipeline copy stencil to depth intermediate", shader_depth, "vs_main", "fs_main", layout_depth, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_None, offscreenTargetFormat, blendStateSrc, depthStencilStateCopyStencilToDepthInt, multisampleState); // render pipeline depth to stencil copy_depth_to_stencil = createRenderPipeline( context.device, "The render pipeline depth to stencil", shader_depth, "vs_main", "fs_main", layout_depth, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_None, offscreenTargetFormat, blendStateSrc, depthStencilStateCopyDepthToStencil, multisampleState); // render pipeline merge depth with stencil merge_depth_stencil = createRenderPipeline( context.device, "The render pipeline merge depth with stencil", shader_depth, "vs_main", "fs_main", layout_depth, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_None, offscreenTargetFormat, blendStateSrc, depthStencilStateMergeDepthStencil, multisampleState); // render pipeline clear depth clear_depth = createRenderPipeline( context.device, "The render pipeline clear depth", shader_depth, "vs_main", "fs_main", layout_depth, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_None, offscreenTargetFormat, blendStateSrc, depthStencilStateClearDepth, multisampleState); // render pipeline solid solid = createRenderPipeline( context.device, "The render pipeline solid", shader_solid, "vs_main", "fs_main", layout_solid, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateNrm, depthStencilStateShape, multisampleState); // render pipeline radial radial = createRenderPipeline( context.device, "The render pipeline radial", shader_radial, "vs_main", "fs_main", layout_gradient, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateNrm, depthStencilStateShape, multisampleState); // render pipeline linear linear = createRenderPipeline( context.device, "The render pipeline linear", shader_linear, "vs_main", "fs_main", layout_gradient, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateNrm, depthStencilStateShape, multisampleState); // render pipeline solid (no stencil) solid_conv = createRenderPipeline( context.device, "The render pipeline solid", shader_solid, "vs_main", "fs_main", layout_solid, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateNrm, depthStencilStateScene, multisampleState); // render pipeline radial (no stencil) radial_conv = createRenderPipeline( context.device, "The render pipeline radial", shader_radial, "vs_main", "fs_main", layout_gradient, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateNrm, depthStencilStateScene, multisampleState); // render pipeline linear (no stencil) linear_conv = createRenderPipeline( context.device, "The render pipeline linear", shader_linear, "vs_main", "fs_main", layout_gradient, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateNrm, depthStencilStateScene, multisampleState); // render pipeline image image = createRenderPipeline( context.device, "The render pipeline image", shader_image, "vs_main", "fs_main", layout_image, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateNrm, depthStencilStateShape, multisampleState); // render pipeline scene scene = createRenderPipeline( context.device, "The render pipeline scene", shader_scene, "vs_main", "fs_main", layout_scene, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateNrm, depthStencilStateScene, multisampleState); // blend shader names const char* shaderBlendNames[] { "fs_main_Normal", "fs_main_Multiply", "fs_main_Screen", "fs_main_Overlay", "fs_main_Darken", "fs_main_Lighten", "fs_main_ColorDodge", "fs_main_ColorBurn", "fs_main_HardLight", "fs_main_SoftLight", "fs_main_Difference", "fs_main_Exclusion", "fs_main_Hue", "fs_main_Saturation", "fs_main_Color", "fs_main_Luminosity", "fs_main_Add", "fs_main_Normal" //TODO: a padding for reserved Hardmix. }; // render pipeline shape blend for (uint32_t i = 0; i < 18; i++) { // blend solid solid_blend[i] = createRenderPipeline( context.device, "The render pipeline solid blend", shader_solid_blend, "vs_main", shaderBlendNames[i], layout_solid_blend, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateShape, multisampleState); // blend radial radial_blend[i] = createRenderPipeline( context.device, "The render pipeline radial blend", shader_radial_blend, "vs_main", shaderBlendNames[i], layout_gradient_blend, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateShape, multisampleState); // blend linear linear_blend[i] = createRenderPipeline( context.device, "The render pipeline linear blend", shader_linear_blend, "vs_main", shaderBlendNames[i], layout_gradient_blend, vertexBufferLayoutsShape, 1, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateShape, multisampleState); // blend image image_blend[i] = createRenderPipeline( context.device, "The render pipeline image blend", shader_image_blend, "vs_main", shaderBlendNames[i], layout_image_blend, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateShape, multisampleState); // blend scene scene_blend[i] = createRenderPipeline( context.device, "The render pipeline scene blend", shader_scene_blend, "vs_main", shaderBlendNames[i], layout_scene_blend, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateScene, multisampleState); } // compose shader names const char* shaderComposeNames[] { "fs_main_None", "fs_main_AlphaMask", "fs_main_InvAlphaMask", "fs_main_LumaMask", "fs_main_InvLumaMask", "fs_main_AddMask", "fs_main_SubtractMask", "fs_main_IntersectMask", "fs_main_DifferenceMask", "fs_main_LightenMask", "fs_main_DarkenMask" }; // compose shader blend states const WGPUBlendState composeBlends[] { blendStateNrm, // None blendStateNrm, // AlphaMask blendStateNrm, // InvAlphaMask blendStateNrm, // LumaMask blendStateNrm, // InvLumaMask blendStateSrc, // AddMask blendStateSrc, // SubtractMask blendStateSrc, // IntersectMask blendStateSrc, // DifferenceMask blendStateSrc, // LightenMask blendStateSrc // DarkenMask }; // render pipeline scene composition for (uint32_t i = 0; i < 11; i++) { scene_compose[i] = createRenderPipeline( context.device, "The render pipeline scene composition", shader_scene_compose, "vs_main", shaderComposeNames[i], layout_scene_compose, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, composeBlends[i], depthStencilStateScene, multisampleState); } // render pipeline blit blit = createRenderPipeline( context.device, "The render pipeline blit", shader_blit, "vs_main", "fs_main", layout_blit, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, context.preferredFormat, blendStateSrc, // must be preferred screen pixel format depthStencilStateScene, multisampleStateX1); // effects dropshadow = createRenderPipeline( context.device, "The render pipeline drop shadow", shader_shadow, "vs_main", "fs_main_shadow", layout_shadow, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateScene, multisampleStateX1); gaussian_vert = createRenderPipeline( context.device, "The render pipeline gaussian vert", shader_effects, "vs_main", "fs_main_vert", layout_effects, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateScene, multisampleStateX1); gaussian_horz = createRenderPipeline( context.device, "The render pipeline gaussian horz", shader_effects, "vs_main", "fs_main_horz", layout_effects, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateScene, multisampleStateX1); fill_effect = createRenderPipeline( context.device, "The render pipeline fill effect", shader_effects, "vs_main", "fs_main_fill", layout_effects, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateScene, multisampleStateX1); tint_effect = createRenderPipeline( context.device, "The render pipeline tint effect", shader_effects, "vs_main", "fs_main_tint", layout_effects, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateScene, multisampleStateX1); tritone_effect = createRenderPipeline( context.device, "The render pipeline tritone effect", shader_effects, "vs_main", "fs_main_tritone", layout_effects, vertexBufferLayoutsImage, 2, WGPUColorWriteMask_All, offscreenTargetFormat, blendStateSrc, depthStencilStateScene, multisampleStateX1); } void WgPipelines::releaseGraphicHandles(WgContext& context) { // pipeline effects releaseRenderPipeline(tritone_effect); releaseRenderPipeline(tint_effect); releaseRenderPipeline(fill_effect); releaseRenderPipeline(gaussian_horz); releaseRenderPipeline(gaussian_vert); releaseRenderPipeline(dropshadow); // pipeline blit releaseRenderPipeline(blit); // pipelines compose for (uint32_t i = 0; i < 11; i++) releaseRenderPipeline(scene_compose[i]); // pipelines custom blend for (uint32_t i = 0; i < 18; i++) { releaseRenderPipeline(scene_blend[i]); releaseRenderPipeline(image_blend[i]); releaseRenderPipeline(linear_blend[i]); releaseRenderPipeline(radial_blend[i]); releaseRenderPipeline(solid_blend[i]); } // pipelines normal blend releaseRenderPipeline(scene); releaseRenderPipeline(image); releaseRenderPipeline(linear_conv); releaseRenderPipeline(radial_conv); releaseRenderPipeline(solid_conv); releaseRenderPipeline(linear); releaseRenderPipeline(radial); releaseRenderPipeline(solid); // pipelines clip path markup releaseRenderPipeline(clear_depth); releaseRenderPipeline(merge_depth_stencil); releaseRenderPipeline(copy_depth_to_stencil); releaseRenderPipeline(copy_stencil_to_depth_interm); releaseRenderPipeline(copy_stencil_to_depth); // pipelines stencil markup releaseRenderPipeline(direct); releaseRenderPipeline(evenodd); releaseRenderPipeline(nonzero); // layouts releasePipelineLayout(layout_effects); releasePipelineLayout(layout_shadow); releasePipelineLayout(layout_blit); releasePipelineLayout(layout_scene_compose); releasePipelineLayout(layout_scene_blend); releasePipelineLayout(layout_image_blend); releasePipelineLayout(layout_gradient_blend); releasePipelineLayout(layout_solid_blend); releasePipelineLayout(layout_scene); releasePipelineLayout(layout_image); releasePipelineLayout(layout_gradient); releasePipelineLayout(layout_solid); releasePipelineLayout(layout_depth); releasePipelineLayout(layout_stencil); // shaders releaseShaderModule(shader_effects); releaseShaderModule(shader_shadow); releaseShaderModule(shader_blit); releaseShaderModule(shader_scene_compose); releaseShaderModule(shader_scene_blend); releaseShaderModule(shader_image_blend); releaseShaderModule(shader_linear_blend); releaseShaderModule(shader_radial_blend); releaseShaderModule(shader_solid_blend); releaseShaderModule(shader_scene); releaseShaderModule(shader_image); releaseShaderModule(shader_linear); releaseShaderModule(shader_radial); releaseShaderModule(shader_solid); releaseShaderModule(shader_depth); releaseShaderModule(shader_stencil); } void WgPipelines::release(WgContext& context) { releaseGraphicHandles(context); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgColor.h000664 001750 001750 00000002725 15164251010 033252 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_COLOR_H_ #define _TVG_COLOR_H_ #include "tvgCommon.h" namespace tvg { struct RGB { uint8_t r, g, b; }; struct RGBA { uint8_t r, g, b, a; }; struct HSL { float h, s, l; }; void hsl2rgb(float h, float s, float l, uint8_t& r, uint8_t& g, uint8_t& b); } #endif //_TVG_COLOR_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwRenderer.h000664 001750 001750 00000010110 15164251010 036454 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SW_RENDERER_H_ #define _TVG_SW_RENDERER_H_ #include "tvgRender.h" struct SwSurface; struct SwTask; struct SwCompositor; struct SwMpool; namespace tvg { struct SwRenderer : RenderMethod { //main features bool preUpdate() override; RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; bool postUpdate() override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; bool postRender() override; void dispose(RenderData data) override; RenderRegion region(RenderData data) override; bool bounds(RenderData data, Point* pt4, const Matrix& m) override; bool blend(BlendMethod method) override; ColorSpace colorSpace() override; const RenderSurface* mainSurface() override; bool clear() override; bool sync() override; bool intersectsShape(RenderData data, const RenderRegion& region) override; bool intersectsImage(RenderData data, const RenderRegion& region) override; bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); //composition SwSurface* request(int channelSize, bool square); RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override; bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override; bool endComposite(RenderCompositor* cmp) override; void clearCompositors(); //post effects void prepare(RenderEffect* effect, const Matrix& transform) override; bool region(RenderEffect* effect) override; bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override; void dispose(RenderEffect* effect) override; //partial rendering void damage(RenderData rd, const RenderRegion& region) override; bool partial(bool disable) override; SwRenderer(uint32_t threads, EngineOption op); static bool term(); private: SwSurface* surface = nullptr; //active surface Array tasks; //async task list Array compositors; //render targets cache list RenderDirtyRegion dirtyRegion; //partial rendering support SwMpool* mpool; //private memory pool bool sharedMpool; //memory-pool behavior policy bool fulldraw = true; //buffer is cleared (need to redraw full screen) ~SwRenderer(); RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags); }; } #endif /* _TVG_SW_RENDERER_H_ */ thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-tagged-template-literal.h000664 001750 001750 00000003010 15164251010 047774 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_TAGGED_TEMPLATE_LITERAL_H #define ECMA_TAGGED_TEMPLATE_LITERAL_H /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_tagged_template_literal Tagged template literal * @{ */ #include "ecma-globals.h" #include "common.h" #include "js-parser-internal.h" ecma_object_t *parser_new_tagged_template_literal (ecma_object_t **raw_strings_p); void parser_tagged_template_literal_append_strings (parser_context_t *context_p, ecma_object_t *template_obj_p, ecma_object_t *raw_strings_p, uint32_t prop_index); void parser_tagged_template_literal_finalize (ecma_object_t *template_obj_p, ecma_object_t *raw_strings_p); /** * @} * @} * @} */ #endif /* ECMA_TAGGED_TEMPLATE_LITERAL_H */ lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-function.inc.h000664 001750 001750 00000002221 15164251010 053532 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %AsyncGeneratorFunction% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* ECMA-262 v10, 25.3.2 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v10, 25.3.2.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.3.2.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_ASYNC_GENERATOR, ECMA_PROPERTY_FIXED) #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/svg2png/lodepng.cpp000664 001750 001750 00000773764 15164251010 034127 0ustar00ddennedyddennedy000000 000000 /* LodePNG version 20200306 Copyright (c) 2005-2020 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* The manual and changelog are in the header file "lodepng.h" Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. */ #include "lodepng.h" #ifdef LODEPNG_COMPILE_DISK #include /* LONG_MAX */ #include /* file handling */ #endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ALLOCATORS #include /* allocations */ #endif /* LODEPNG_COMPILE_ALLOCATORS */ #if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ const char* LODEPNG_VERSION_STRING = "20200306"; /* This source file is built up in the following large parts. The code sections with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. -Tools for C and common code for PNG and Zlib -C Code for Zlib (huffman, deflate, ...) -C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) -The C++ wrapper around all of the above */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // Tools for C, and common code for PNG and Zlib. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /*The malloc, realloc and free functions defined here with "lodepng_" in front of the name, so that you can easily change them to others related to your platform if needed. Everything else in the code calls these. Pass -DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out #define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and define them in your own project's source files without needing to change lodepng source code. Don't forget to remove "static" if you copypaste them from here.*/ #ifdef LODEPNG_COMPILE_ALLOCATORS static void* lodepng_malloc(size_t size) { #ifdef LODEPNG_MAX_ALLOC if(size > LODEPNG_MAX_ALLOC) return 0; #endif return malloc(size); } /* NOTE: when realloc returns NULL, it leaves the original memory untouched */ static void* lodepng_realloc(void* ptr, size_t new_size) { #ifdef LODEPNG_MAX_ALLOC if(new_size > LODEPNG_MAX_ALLOC) return 0; #endif return realloc(ptr, new_size); } static void lodepng_free(void* ptr) { free(ptr); } #else /*LODEPNG_COMPILE_ALLOCATORS*/ /* TODO: support giving additional void* payload to the custom allocators */ void* lodepng_malloc(size_t size); void* lodepng_realloc(void* ptr, size_t new_size); void lodepng_free(void* ptr); #endif /*LODEPNG_COMPILE_ALLOCATORS*/ /* convince the compiler to inline a function, for use when this measurably improves performance */ /* inline is not available in C90, but use it when supported by the compiler */ #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L)) #define LODEPNG_INLINE inline #else #define LODEPNG_INLINE /* not available */ #endif /* restrict is not available in C90, but use it when supported by the compiler */ #if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\ (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \ (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus)) #define LODEPNG_RESTRICT __restrict #else #define LODEPNG_RESTRICT /* not available */ #endif /* Replacements for C library functions such as memcpy and strlen, to support platforms where a full C library is not available. The compiler can recognize them and compile to something as fast. */ static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, const void* LODEPNG_RESTRICT src, size_t size) { size_t i; for(i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i]; } static void lodepng_memset(void* LODEPNG_RESTRICT dst, int value, size_t num) { size_t i; for(i = 0; i < num; i++) ((char*)dst)[i] = (char)value; } /* does not check memory out of bounds, do not use on untrusted data */ static size_t lodepng_strlen(const char* a) { const char* orig = a; /* avoid warning about unused function in case of disabled COMPILE... macros */ (void)(&lodepng_strlen); while(*a) a++; return (size_t)(a - orig); } #define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x)) #if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER) /* Safely check if adding two integers will overflow (no undefined behavior, compiler removing the code, etc...) and output result. */ static int lodepng_addofl(size_t a, size_t b, size_t* result) { *result = a + b; /* Unsigned addition is well defined and safe in C90 */ return *result < a; } #endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)*/ #ifdef LODEPNG_COMPILE_DECODER /* Safely check if multiplying two integers will overflow (no undefined behavior, compiler removing the code, etc...) and output result. */ static int lodepng_mulofl(size_t a, size_t b, size_t* result) { *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */ return (a != 0 && *result / a != b); } #ifdef LODEPNG_COMPILE_ZLIB /* Safely check if a + b > c, even if overflow could happen. */ static int lodepng_gtofl(size_t a, size_t b, size_t c) { size_t d; if(lodepng_addofl(a, b, &d)) return 1; return d > c; } #endif /*LODEPNG_COMPILE_ZLIB*/ #endif /*LODEPNG_COMPILE_DECODER*/ /* Often in case of an error a value is assigned to a variable and then it breaks out of a loop (to go to the cleanup phase of a function). This macro does that. It makes the error handling code shorter and more readable. Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83); */ #define CERROR_BREAK(errorvar, code){\ errorvar = code;\ break;\ } /*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ #define ERROR_BREAK(code) CERROR_BREAK(error, code) /*Set error var to the error code, and return it.*/ #define CERROR_RETURN_ERROR(errorvar, code){\ errorvar = code;\ return code;\ } /*Try the code, if it returns error, also return the error.*/ #define CERROR_TRY_RETURN(call){\ unsigned error = call;\ if(error) return error;\ } /*Set error var to the error code, and return from the void function.*/ #define CERROR_RETURN(errorvar, code){\ errorvar = code;\ return;\ } /* About uivector, ucvector and string: -All of them wrap dynamic arrays or text strings in a similar way. -LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. -The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. -They're not used in the interface, only internally in this file as static functions. -As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_ENCODER /*dynamic vector of unsigned ints*/ typedef struct uivector { unsigned* data; size_t size; /*size in number of unsigned longs*/ size_t allocsize; /*allocated size in bytes*/ } uivector; static void uivector_cleanup(void* p) { ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; lodepng_free(((uivector*)p)->data); ((uivector*)p)->data = NULL; } /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned uivector_resize(uivector* p, size_t size) { size_t allocsize = size * sizeof(unsigned); if(allocsize > p->allocsize) { size_t newsize = allocsize + (p->allocsize >> 1u); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned*)data; } else return 0; /*error: not enough memory*/ } p->size = size; return 1; /*success*/ } static void uivector_init(uivector* p) { p->data = NULL; p->size = p->allocsize = 0; } /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned uivector_push_back(uivector* p, unsigned c) { if(!uivector_resize(p, p->size + 1)) return 0; p->data[p->size - 1] = c; return 1; } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ /* /////////////////////////////////////////////////////////////////////////// */ /*dynamic vector of unsigned chars*/ typedef struct ucvector { unsigned char* data; size_t size; /*used size*/ size_t allocsize; /*allocated size*/ } ucvector; /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned ucvector_resize(ucvector* p, size_t size) { if(size > p->allocsize) { size_t newsize = size + (p->allocsize >> 1u); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned char*)data; } else return 0; /*error: not enough memory*/ } p->size = size; return 1; /*success*/ } static ucvector ucvector_init(unsigned char* buffer, size_t size) { ucvector v; v.data = buffer; v.allocsize = v.size = size; return v; } /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*free string pointer and set it to NULL*/ static void string_cleanup(char** out) { lodepng_free(*out); *out = NULL; } static char* alloc_string_sized(const char* in, size_t insize) { char* out = (char*)lodepng_malloc(insize + 1); if(out) { lodepng_memcpy(out, in, insize); out[insize] = 0; } return out; } /* dynamically allocates a new string with a copy of the null terminated input text */ static char* alloc_string(const char* in) { return alloc_string_sized(in, lodepng_strlen(in)); } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_PNG*/ /* ////////////////////////////////////////////////////////////////////////// */ #if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG) static unsigned lodepng_read32bitInt(const unsigned char* buffer) { return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) | ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]); } #endif /*defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG)*/ #if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) /*buffer must have at least 4 allocated bytes available*/ static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) { buffer[0] = (unsigned char)((value >> 24) & 0xff); buffer[1] = (unsigned char)((value >> 16) & 0xff); buffer[2] = (unsigned char)((value >> 8) & 0xff); buffer[3] = (unsigned char)((value ) & 0xff); } #endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ /* ////////////////////////////////////////////////////////////////////////// */ /* / File IO / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_DISK /* returns negative value on error. This should be pure C compatible, so no fstat. */ static long lodepng_filesize(const char* filename) { FILE* file; long size; file = fopen(filename, "rb"); if(!file) return -1; if(fseek(file, 0, SEEK_END) != 0) { fclose(file); return -1; } size = ftell(file); /* It may give LONG_MAX as directory size, this is invalid for us. */ if(size == LONG_MAX) size = -1; fclose(file); return size; } /* load file into buffer that already has the correct allocated size. Returns error code.*/ static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) { FILE* file; size_t readsize; file = fopen(filename, "rb"); if(!file) return 78; readsize = fread(out, 1, size, file); fclose(file); if(readsize != size) return 78; return 0; } unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) { long size = lodepng_filesize(filename); if(size < 0) return 78; *outsize = (size_t)size; *out = (unsigned char*)lodepng_malloc((size_t)size); if(!(*out) && size > 0) return 83; /*the above malloc failed*/ return lodepng_buffer_file(*out, (size_t)size, filename); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) { FILE* file; file = fopen(filename, "wb" ); if(!file) return 79; fwrite(buffer, 1, buffersize, file); fclose(file); return 0; } #endif /*LODEPNG_COMPILE_DISK*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // End of common code and tools. Begin of Zlib related code. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_ENCODER typedef struct { ucvector* data; unsigned char bp; /*ok to overflow, indicates bit pos inside byte*/ } LodePNGBitWriter; static void LodePNGBitWriter_init(LodePNGBitWriter* writer, ucvector* data) { writer->data = data; writer->bp = 0; } /*TODO: this ignores potential out of memory errors*/ #define WRITEBIT(writer, bit){\ /* append new byte */\ if(((writer->bp) & 7u) == 0) {\ if(!ucvector_resize(writer->data, writer->data->size + 1)) return;\ writer->data->data[writer->data->size - 1] = 0;\ }\ (writer->data->data[writer->data->size - 1]) |= (bit << ((writer->bp) & 7u));\ ++writer->bp;\ } /* LSB of value is written first, and LSB of bytes is used first */ static void writeBits(LodePNGBitWriter* writer, unsigned value, size_t nbits) { if(nbits == 1) { /* compiler should statically compile this case if nbits == 1 */ WRITEBIT(writer, value); } else { /* TODO: increase output size only once here rather than in each WRITEBIT */ size_t i; for(i = 0; i != nbits; ++i) { WRITEBIT(writer, (unsigned char)((value >> i) & 1)); } } } /* This one is to use for adding huffman symbol, the value bits are written MSB first */ static void writeBitsReversed(LodePNGBitWriter* writer, unsigned value, size_t nbits) { size_t i; for(i = 0; i != nbits; ++i) { /* TODO: increase output size only once here rather than in each WRITEBIT */ WRITEBIT(writer, (unsigned char)((value >> (nbits - 1u - i)) & 1u)); } } #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_DECODER typedef struct { const unsigned char* data; size_t size; /*size of data in bytes*/ size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/ size_t bp; unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/ } LodePNGBitReader; /* data size argument is in bytes. Returns error if size too large causing overflow */ static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size) { size_t temp; reader->data = data; reader->size = size; /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */ if(lodepng_mulofl(size, 8u, &reader->bitsize)) return 105; /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and trying to ensure 32 more bits*/ if(lodepng_addofl(reader->bitsize, 64u, &temp)) return 105; reader->bp = 0; reader->buffer = 0; return 0; /*ok*/ } /* ensureBits functions: Ensures the reader can at least read nbits bits in one or more readBits calls, safely even if not enough bits are available. Returns 1 if there are enough bits available, 0 if not. */ /*See ensureBits documentation above. This one ensures exactly 1 bit */ /*static unsigned ensureBits1(LodePNGBitReader* reader) { if(reader->bp >= reader->bitsize) return 0; reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u); return 1; }*/ /*See ensureBits documentation above. This one ensures up to 9 bits */ static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits) { size_t start = reader->bp >> 3u; size_t size = reader->size; if(start + 1u < size) { reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u); reader->buffer >>= (reader->bp & 7u); return 1; } else { reader->buffer = 0; if(start + 0u < size) reader->buffer |= reader->data[start + 0]; reader->buffer >>= (reader->bp & 7u); return reader->bp + nbits <= reader->bitsize; } } /*See ensureBits documentation above. This one ensures up to 17 bits */ static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits) { size_t start = reader->bp >> 3u; size_t size = reader->size; if(start + 2u < size) { reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u); reader->buffer >>= (reader->bp & 7u); return 1; } else { reader->buffer = 0; if(start + 0u < size) reader->buffer |= reader->data[start + 0]; if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); reader->buffer >>= (reader->bp & 7u); return reader->bp + nbits <= reader->bitsize; } } /*See ensureBits documentation above. This one ensures up to 25 bits */ static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits) { size_t start = reader->bp >> 3u; size_t size = reader->size; if(start + 3u < size) { reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); reader->buffer >>= (reader->bp & 7u); return 1; } else { reader->buffer = 0; if(start + 0u < size) reader->buffer |= reader->data[start + 0]; if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); reader->buffer >>= (reader->bp & 7u); return reader->bp + nbits <= reader->bitsize; } } /*See ensureBits documentation above. This one ensures up to 32 bits */ static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits) { size_t start = reader->bp >> 3u; size_t size = reader->size; if(start + 4u < size) { reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); reader->buffer >>= (reader->bp & 7u); reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u))); return 1; } else { reader->buffer = 0; if(start + 0u < size) reader->buffer |= reader->data[start + 0]; if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); if(start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u); reader->buffer >>= (reader->bp & 7u); return reader->bp + nbits <= reader->bitsize; } } /* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */ static unsigned peekBits(LodePNGBitReader* reader, size_t nbits) { /* The shift allows nbits to be only up to 31. */ return reader->buffer & ((1u << nbits) - 1u); } /* Must have enough bits available with ensureBits */ static void advanceBits(LodePNGBitReader* reader, size_t nbits) { reader->buffer >>= nbits; reader->bp += nbits; } /* Must have enough bits available with ensureBits */ static unsigned readBits(LodePNGBitReader* reader, size_t nbits) { unsigned result = peekBits(reader, nbits); advanceBits(reader, nbits); return result; } /* Public for testing only. steps and result must have numsteps values. */ unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, size_t numsteps, const size_t* steps, unsigned* result) { size_t i; LodePNGBitReader reader; unsigned error = LodePNGBitReader_init(&reader, data, size); if(error) return 0; for(i = 0; i < numsteps; i++) { size_t step = steps[i]; unsigned ok; if(step > 25) ok = ensureBits32(&reader, step); else if(step > 17) ok = ensureBits25(&reader, step); else if(step > 9) ok = ensureBits17(&reader, step); else ok = ensureBits9(&reader, step); if(!ok) return 0; result[i] = readBits(&reader, step); } return 1; } #endif /*LODEPNG_COMPILE_DECODER*/ static unsigned reverseBits(unsigned bits, unsigned num) { /*TODO: implement faster lookup table based version when needed*/ unsigned i, result = 0; for(i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i; return result; } /* ////////////////////////////////////////////////////////////////////////// */ /* / Deflate - Huffman / */ /* ////////////////////////////////////////////////////////////////////////// */ #define FIRST_LENGTH_CODE_INDEX 257 #define LAST_LENGTH_CODE_INDEX 285 /*256 literals, the end code, some length codes, and 2 unused codes*/ #define NUM_DEFLATE_CODE_SYMBOLS 288 /*the distance codes have their own symbols, 30 used, 2 unused*/ #define NUM_DISTANCE_SYMBOLS 32 /*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ #define NUM_CODE_LENGTH_CODES 19 /*the base lengths represented by codes 257-285*/ static const unsigned LENGTHBASE[29] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; /*the extra bits used by codes 257-285 (added to base length)*/ static const unsigned LENGTHEXTRA[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; /*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ static const unsigned DISTANCEBASE[30] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; /*the extra bits of backwards distances (added to base)*/ static const unsigned DISTANCEEXTRA[30] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman tree of the dynamic huffman tree lengths is generated*/ static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* ////////////////////////////////////////////////////////////////////////// */ /* Huffman tree struct, containing multiple representations of the tree */ typedef struct HuffmanTree { unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/ unsigned* lengths; /*the lengths of the huffman codes*/ unsigned maxbitlen; /*maximum number of bits a single code can get*/ unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ /* for reading only */ unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/ unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/ } HuffmanTree; static void HuffmanTree_init(HuffmanTree* tree) { tree->codes = 0; tree->lengths = 0; tree->table_len = 0; tree->table_value = 0; } static void HuffmanTree_cleanup(HuffmanTree* tree) { lodepng_free(tree->codes); lodepng_free(tree->lengths); lodepng_free(tree->table_len); lodepng_free(tree->table_value); } /* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/ /* values 8u and 9u work the fastest */ #define FIRSTBITS 9u /* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination, which is possible in case of only 0 or 1 present symbols. */ #define INVALIDSYMBOL 65535u /* make table for huffman decoding */ static unsigned HuffmanTree_makeTable(HuffmanTree* tree) { static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/ static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u; size_t i, numpresent, pointer, size; /*total table size*/ unsigned* maxlens = (unsigned*)lodepng_malloc(headsize * sizeof(unsigned)); if(!maxlens) return 83; /*alloc fail*/ /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/ lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens)); for(i = 0; i < tree->numcodes; i++) { unsigned symbol = tree->codes[i]; unsigned l = tree->lengths[i]; unsigned index; if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/ /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/ index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS); maxlens[index] = LODEPNG_MAX(maxlens[index], l); } /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */ size = headsize; for(i = 0; i < headsize; ++i) { unsigned l = maxlens[i]; if(l > FIRSTBITS) size += (1u << (l - FIRSTBITS)); } tree->table_len = (unsigned char*)lodepng_malloc(size * sizeof(*tree->table_len)); tree->table_value = (unsigned short*)lodepng_malloc(size * sizeof(*tree->table_value)); if(!tree->table_len || !tree->table_value) { lodepng_free(maxlens); /* freeing tree->table values is done at a higher scope */ return 83; /*alloc fail*/ } /*initialize with an invalid length to indicate unused entries*/ for(i = 0; i < size; ++i) tree->table_len[i] = 16; /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/ pointer = headsize; for(i = 0; i < headsize; ++i) { unsigned l = maxlens[i]; if(l <= FIRSTBITS) continue; tree->table_len[i] = l; tree->table_value[i] = pointer; pointer += (1u << (l - FIRSTBITS)); } lodepng_free(maxlens); /*fill in the first table for short symbols, or secondary table for long symbols*/ numpresent = 0; for(i = 0; i < tree->numcodes; ++i) { unsigned l = tree->lengths[i]; unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/ /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/ unsigned reverse = reverseBits(symbol, l); if(l == 0) continue; numpresent++; if(l <= FIRSTBITS) { /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/ unsigned num = 1u << (FIRSTBITS - l); unsigned j; for(j = 0; j < num; ++j) { /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/ unsigned index = reverse | (j << l); if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ tree->table_len[index] = l; tree->table_value[index] = i; } } else { /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/ /*the FIRSTBITS MSBs of the symbol are the first table index*/ unsigned index = reverse & mask; unsigned maxlen = tree->table_len[index]; /*log2 of secondary table length, should be >= l - FIRSTBITS*/ unsigned tablelen = maxlen - FIRSTBITS; unsigned start = tree->table_value[index]; /*starting index in secondary table*/ unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/ unsigned j; if(maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ for(j = 0; j < num; ++j) { unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */ unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS))); tree->table_len[index2] = l; tree->table_value[index2] = i; } } } if(numpresent < 2) { /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits, but deflate uses 1 bit instead. In case of 0 symbols, no symbols can appear at all, but such huffman tree could still exist (e.g. if distance codes are never used). In both cases, not all symbols of the table will be filled in. Fill them in with an invalid symbol value so returning them from huffmanDecodeSymbol will cause error. */ for(i = 0; i < size; ++i) { if(tree->table_len[i] == 16) { /* As length, use a value smaller than FIRSTBITS for the head table, and a value larger than FIRSTBITS for the secondary table, to ensure valid behavior for advanceBits when reading this symbol. */ tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1); tree->table_value[i] = INVALIDSYMBOL; } } } else { /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. If that is not the case (due to too long length codes), the table will not have been fully used, and this is an error (not all bit combinations can be decoded): an oversubscribed huffman tree, indicated by error 55. */ for(i = 0; i < size; ++i) { if(tree->table_len[i] == 16) return 55; } } return 0; } /* Second step for the ...makeFromLengths and ...makeFromFrequencies functions. numcodes, lengths and maxbitlen must already be filled in correctly. return value is error. */ static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) { unsigned* blcount; unsigned* nextcode; unsigned error = 0; unsigned bits, n; tree->codes = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); blcount = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned)); nextcode = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned)); if(!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/ if(!error) { for(n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0; /*step 1: count number of instances of each code length*/ for(bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]]; /*step 2: generate the nextcode values*/ for(bits = 1; bits <= tree->maxbitlen; ++bits) { nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u; } /*step 3: generate all the codes*/ for(n = 0; n != tree->numcodes; ++n) { if(tree->lengths[n] != 0) { tree->codes[n] = nextcode[tree->lengths[n]]++; /*remove superfluous bits from the code*/ tree->codes[n] &= ((1u << tree->lengths[n]) - 1u); } } } lodepng_free(blcount); lodepng_free(nextcode); if(!error) error = HuffmanTree_makeTable(tree); return error; } /* given the code lengths (as stored in the PNG file), generate the tree as defined by Deflate. maxbitlen is the maximum bits that a code in the tree can have. return value is error. */ static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen) { unsigned i; tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); if(!tree->lengths) return 83; /*alloc fail*/ for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->maxbitlen = maxbitlen; return HuffmanTree_makeFromLengths2(tree); } #ifdef LODEPNG_COMPILE_ENCODER /*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ /*chain node for boundary package merge*/ typedef struct BPMNode { int weight; /*the sum of all weights in this chain*/ unsigned index; /*index of this leaf node (called "count" in the paper)*/ struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ int in_use; } BPMNode; /*lists of chains*/ typedef struct BPMLists { /*memory pool*/ unsigned memsize; BPMNode* memory; unsigned numfree; unsigned nextfree; BPMNode** freelist; /*two heads of lookahead chains per list*/ unsigned listsize; BPMNode** chains0; BPMNode** chains1; } BPMLists; /*creates a new chain node with the given parameters, from the memory in the lists */ static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) { unsigned i; BPMNode* result; /*memory full, so garbage collect*/ if(lists->nextfree >= lists->numfree) { /*mark only those that are in use*/ for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; for(i = 0; i != lists->listsize; ++i) { BPMNode* node; for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; } /*collect those that are free*/ lists->numfree = 0; for(i = 0; i != lists->memsize; ++i) { if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; } lists->nextfree = 0; } result = lists->freelist[lists->nextfree++]; result->weight = weight; result->index = index; result->tail = tail; return result; } /*sort the leaves with stable mergesort*/ static void bpmnode_sort(BPMNode* leaves, size_t num) { BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); size_t width, counter = 0; for(width = 1; width < num; width *= 2) { BPMNode* a = (counter & 1) ? mem : leaves; BPMNode* b = (counter & 1) ? leaves : mem; size_t p; for(p = 0; p < num; p += 2 * width) { size_t q = (p + width > num) ? num : (p + width); size_t r = (p + 2 * width > num) ? num : (p + 2 * width); size_t i = p, j = q, k; for(k = p; k < r; k++) { if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; else b[k] = a[j++]; } } counter++; } if(counter & 1) lodepng_memcpy(leaves, mem, sizeof(*leaves) * num); lodepng_free(mem); } /*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) { unsigned lastindex = lists->chains1[c]->index; if(c == 0) { if(lastindex >= numpresent) return; lists->chains0[c] = lists->chains1[c]; lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); } else { /*sum of the weights of the head nodes of the previous lookahead chains.*/ int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; lists->chains0[c] = lists->chains1[c]; if(lastindex < numpresent && sum > leaves[lastindex].weight) { lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); return; } lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); /*in the end we are only interested in the chain of the last list, so no need to recurse if we're at the last one (this gives measurable speedup)*/ if(num + 1 < (int)(2 * numpresent - 2)) { boundaryPM(lists, leaves, numpresent, c - 1, num); boundaryPM(lists, leaves, numpresent, c - 1, num); } } } unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, size_t numcodes, unsigned maxbitlen) { unsigned error = 0; unsigned i; size_t numpresent = 0; /*number of symbols with non-zero frequency*/ BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/ leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); if(!leaves) return 83; /*alloc fail*/ for(i = 0; i != numcodes; ++i) { if(frequencies[i] > 0) { leaves[numpresent].weight = (int)frequencies[i]; leaves[numpresent].index = i; ++numpresent; } } lodepng_memset(lengths, 0, numcodes * sizeof(*lengths)); /*ensure at least two present symbols. There should be at least one symbol according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To make these work as well ensure there are at least two symbols. The Package-Merge code below also doesn't work correctly if there's only one symbol, it'd give it the theoretical 0 bits but in practice zlib wants 1 bit*/ if(numpresent == 0) { lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ } else if(numpresent == 1) { lengths[leaves[0].index] = 1; lengths[leaves[0].index == 0 ? 1 : 0] = 1; } else { BPMLists lists; BPMNode* node; bpmnode_sort(leaves, numpresent); lists.listsize = maxbitlen; lists.memsize = 2 * maxbitlen * (maxbitlen + 1); lists.nextfree = 0; lists.numfree = lists.memsize; lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ if(!error) { for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; bpmnode_create(&lists, leaves[0].weight, 1, 0); bpmnode_create(&lists, leaves[1].weight, 2, 0); for(i = 0; i != lists.listsize; ++i) { lists.chains0[i] = &lists.memory[0]; lists.chains1[i] = &lists.memory[1]; } /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) { for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; } } lodepng_free(lists.memory); lodepng_free(lists.freelist); lodepng_free(lists.chains0); lodepng_free(lists.chains1); } lodepng_free(leaves); return error; } /*Create the Huffman tree given the symbol frequencies*/ static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, size_t mincodes, size_t numcodes, unsigned maxbitlen) { unsigned error = 0; while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); if(!tree->lengths) return 83; /*alloc fail*/ tree->maxbitlen = maxbitlen; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); if(!error) error = HuffmanTree_makeFromLengths2(tree); return error; } #endif /*LODEPNG_COMPILE_ENCODER*/ /*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ static unsigned generateFixedLitLenTree(HuffmanTree* tree) { unsigned i, error = 0; unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); if(!bitlen) return 83; /*alloc fail*/ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ for(i = 0; i <= 143; ++i) bitlen[i] = 8; for(i = 144; i <= 255; ++i) bitlen[i] = 9; for(i = 256; i <= 279; ++i) bitlen[i] = 7; for(i = 280; i <= 287; ++i) bitlen[i] = 8; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); lodepng_free(bitlen); return error; } /*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ static unsigned generateFixedDistanceTree(HuffmanTree* tree) { unsigned i, error = 0; unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen) return 83; /*alloc fail*/ /*there are 32 distance codes, but 30-31 are unused*/ for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); lodepng_free(bitlen); return error; } #ifdef LODEPNG_COMPILE_DECODER /* returns the code. The bit reader must already have been ensured at least 15 bits */ static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree) { unsigned short code = peekBits(reader, FIRSTBITS); unsigned short l = codetree->table_len[code]; unsigned short value = codetree->table_value[code]; if(l <= FIRSTBITS) { advanceBits(reader, l); return value; } else { unsigned index2; advanceBits(reader, FIRSTBITS); index2 = value + peekBits(reader, l - FIRSTBITS); advanceBits(reader, codetree->table_len[index2] - FIRSTBITS); return codetree->table_value[index2]; } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_DECODER /* ////////////////////////////////////////////////////////////////////////// */ /* / Inflator (Decompressor) / */ /* ////////////////////////////////////////////////////////////////////////// */ /*get the tree of a deflated block with fixed tree, as specified in the deflate specification Returns error code.*/ static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) { unsigned error = generateFixedLitLenTree(tree_ll); if(error) return error; return generateFixedDistanceTree(tree_d); } /*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, LodePNGBitReader* reader) { /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ unsigned error = 0; unsigned n, HLIT, HDIST, HCLEN, i; /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ unsigned* bitlen_ll = 0; /*lit,len code lengths*/ unsigned* bitlen_d = 0; /*dist code lengths*/ /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ unsigned* bitlen_cl = 0; HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ if(!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ HLIT = readBits(reader, 5) + 257; /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ HDIST = readBits(reader, 5) + 1; /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ HCLEN = readBits(reader, 4) + 4; bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); if(!bitlen_cl) return 83 /*alloc fail*/; HuffmanTree_init(&tree_cl); while(!error) { /*read the code length codes out of 3 * (amount of code length codes) bits*/ if(lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) { ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/ } for(i = 0; i != HCLEN; ++i) { ensureBits9(reader, 3); /*out of bounds already checked above */ bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3); } for(i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) { bitlen_cl[CLCL_ORDER[i]] = 0; } error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); if(error) break; /*now we can use this tree to read the lengths for the tree that this function will return*/ bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll)); lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d)); /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ i = 0; while(i < HLIT + HDIST) { unsigned code; ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/ code = huffmanDecodeSymbol(reader, &tree_cl); if(code <= 15) /*a length code*/ { if(i < HLIT) bitlen_ll[i] = code; else bitlen_d[i - HLIT] = code; ++i; } else if(code == 16) /*repeat previous*/ { unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ unsigned value; /*set value to the previous code*/ if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ replength += readBits(reader, 2); if(i < HLIT + 1) value = bitlen_ll[i - 1]; else value = bitlen_d[i - HLIT - 1]; /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = value; else bitlen_d[i - HLIT] = value; ++i; } } else if(code == 17) /*repeat "0" 3-10 times*/ { unsigned replength = 3; /*read in the bits that indicate repeat length*/ replength += readBits(reader, 3); /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; ++i; } } else if(code == 18) /*repeat "0" 11-138 times*/ { unsigned replength = 11; /*read in the bits that indicate repeat length*/ replength += readBits(reader, 7); /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; ++i; } } else /*if(code == INVALIDSYMBOL)*/ { ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ } /*check if any of the ensureBits above went out of bounds*/ if(reader->bp > reader->bitsize) { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ } } if(error) break; if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); if(error) break; error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); break; /*end of error-while*/ } lodepng_free(bitlen_cl); lodepng_free(bitlen_ll); lodepng_free(bitlen_d); HuffmanTree_cleanup(&tree_cl); return error; } /*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, unsigned btype) { unsigned error = 0; HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ HuffmanTree tree_d; /*the huffman tree for distance codes*/ HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); if(btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d); else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader); while(!error) /*decode all symbols until end reached, breaks at end code*/ { /*code_ll is literal, length or end code*/ unsigned code_ll; ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */ code_ll = huffmanDecodeSymbol(reader, &tree_ll); if(code_ll <= 255) /*literal symbol*/ { if(!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/); out->data[out->size - 1] = (unsigned char)code_ll; } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { unsigned code_d, distance; unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ size_t start, backward, length; /*part 1: get length base*/ length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; /*part 2: get extra bits and add the value of that to length*/ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; if(numextrabits_l != 0) { /* bits already ensured above */ length += readBits(reader, numextrabits_l); } /*part 3: get distance code*/ ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */ code_d = huffmanDecodeSymbol(reader, &tree_d); if(code_d > 29) { if(code_d <= 31) { ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/ } else /* if(code_d == INVALIDSYMBOL) */{ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ } } distance = DISTANCEBASE[code_d]; /*part 4: get extra bits from distance*/ numextrabits_d = DISTANCEEXTRA[code_d]; if(numextrabits_d != 0) { /* bits already ensured above */ distance += readBits(reader, numextrabits_d); } /*part 5: fill in all the out[n] values based on the length and dist*/ start = out->size; if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ backward = start - distance; if(!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/); if(distance < length) { size_t forward; lodepng_memcpy(out->data + start, out->data + backward, distance); start += distance; for(forward = distance; forward < length; ++forward) { out->data[start++] = out->data[backward++]; } } else { lodepng_memcpy(out->data + start, out->data + backward, length); } } else if(code_ll == 256) { break; /*end code, break the loop*/ } else /*if(code_ll == INVALIDSYMBOL)*/ { ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ } /*check if any of the ensureBits above went out of bounds*/ if(reader->bp > reader->bitsize) { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ ERROR_BREAK(51); /*error, bit pointer jumps past memory*/ } } HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); return error; } static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, const LodePNGDecompressSettings* settings) { size_t bytepos; size_t size = reader->size; unsigned LEN, NLEN, error = 0; /*go to first boundary of byte*/ bytepos = (reader->bp + 7u) >> 3u; /*read LEN (2 bytes) and NLEN (2 bytes)*/ if(bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/ LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; /*check if 16-bit NLEN is really the one's complement of LEN*/ if(!settings->ignore_nlen && LEN + NLEN != 65535) { return 21; /*error: NLEN is not one's complement of LEN*/ } if(!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/ /*read the literal data: LEN bytes are now stored in the out buffer*/ if(bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/ lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN); bytepos += LEN; reader->bp = bytepos << 3u; return error; } static unsigned lodepng_inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned BFINAL = 0; LodePNGBitReader reader; unsigned error = LodePNGBitReader_init(&reader, in, insize); if(error) return error; while(!BFINAL) { unsigned BTYPE; if(!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/ BFINAL = readBits(&reader, 1); BTYPE = readBits(&reader, 2); if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/ else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/ if(error) return error; } return error; } unsigned lodepng_inflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { ucvector v = ucvector_init(*out, *outsize); unsigned error = lodepng_inflatev(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(settings->custom_inflate) { unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings); out->allocsize = out->size; return error; } else { return lodepng_inflatev(out, in, insize, settings); } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* ////////////////////////////////////////////////////////////////////////// */ /* / Deflator (Compressor) / */ /* ////////////////////////////////////////////////////////////////////////// */ static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; /*search the index in the array, that has the largest value smaller than or equal to the given value, given array must be sorted (if no value is smaller, it returns the size of the given array)*/ static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) { /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ size_t left = 1; size_t right = array_size - 1; while(left <= right) { size_t mid = (left + right) >> 1; if(array[mid] >= value) right = mid - 1; else left = mid + 1; } if(left >= array_size || array[left] > value) left--; return left; } static void addLengthDistance(uivector* values, size_t length, size_t distance) { /*values in encoded vector are those used by deflate: 0-255: literal bytes 256: end 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) 286-287: invalid*/ unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); size_t pos = values->size; /*TODO: return error when this fails (out of memory)*/ unsigned ok = uivector_resize(values, values->size + 4); if(ok) { values->data[pos + 0] = length_code + FIRST_LENGTH_CODE_INDEX; values->data[pos + 1] = extra_length; values->data[pos + 2] = dist_code; values->data[pos + 3] = extra_distance; } } /*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 bytes as input because 3 is the minimum match length for deflate*/ static const unsigned HASH_NUM_VALUES = 65536; static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ typedef struct Hash { int* head; /*hash value to head circular pos - can be outdated if went around window*/ /*circular pos to prev circular pos*/ unsigned short* chain; int* val; /*circular pos to hash value*/ /*TODO: do this not only for zeros but for any repeated byte. However for PNG it's always going to be the zeros that dominate, so not important for PNG*/ int* headz; /*similar to head, but for chainz*/ unsigned short* chainz; /*those with same amount of zeros*/ unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ } Hash; static unsigned hash_init(Hash* hash, unsigned windowsize) { unsigned i; hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) { return 83; /*alloc fail*/ } /*initialize hash table*/ for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; for(i = 0; i != windowsize; ++i) hash->val[i] = -1; for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ return 0; } static void hash_cleanup(Hash* hash) { lodepng_free(hash->head); lodepng_free(hash->val); lodepng_free(hash->chain); lodepng_free(hash->zeros); lodepng_free(hash->headz); lodepng_free(hash->chainz); } static unsigned getHash(const unsigned char* data, size_t size, size_t pos) { unsigned result = 0; if(pos + 2 < size) { /*A simple shift and xor hash is used. Since the data of PNGs is dominated by zeroes due to the filters, a better hash does not have a significant effect on speed in traversing the chain, and causes more time spend on calculating the hash.*/ result ^= ((unsigned)data[pos + 0] << 0u); result ^= ((unsigned)data[pos + 1] << 4u); result ^= ((unsigned)data[pos + 2] << 8u); } else { size_t amount, i; if(pos >= size) return 0; amount = size - pos; for(i = 0; i != amount; ++i) result ^= ((unsigned)data[pos + i] << (i * 8u)); } return result & HASH_BIT_MASK; } static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) { const unsigned char* start = data + pos; const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; if(end > data + size) end = data + size; data = start; while(data != end && *data == 0) ++data; /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ return (unsigned)(data - start); } /*wpos = pos & (windowsize - 1)*/ static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) { hash->val[wpos] = (int)hashval; if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; hash->head[hashval] = (int)wpos; hash->zeros[wpos] = numzeros; if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; hash->headz[numzeros] = (int)wpos; } /* LZ77-encode the data. Return value is error code. The input are raw bytes, the output is in the form of unsigned integers with codes representing for example literal bytes, or length/distance pairs. It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a sliding window (of windowsize) is used, and all past bytes in that window can be used as the "dictionary". A brute force search through all possible distances would be slow, and this hash technique is one out of several ways to speed this up. */ static unsigned encodeLZ77(uivector* out, Hash* hash, const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, unsigned minmatch, unsigned nicematch, unsigned lazymatching) { size_t pos; unsigned i, error = 0; /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8u; unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ unsigned numzeros = 0; unsigned offset; /*the offset represents the distance in LZ77 terminology*/ unsigned length; unsigned lazy = 0; unsigned lazylength = 0, lazyoffset = 0; unsigned hashval; unsigned current_offset, current_length; unsigned prev_offset; const unsigned char *lastptr, *foreptr, *backptr; unsigned hashpos; if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; for(pos = inpos; pos < insize; ++pos) { size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ unsigned chainlength = 0; hashval = getHash(in, insize, pos); if(usezeros && hashval == 0) { if(numzeros == 0) numzeros = countZeros(in, insize, pos); else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { numzeros = 0; } updateHashChain(hash, wpos, hashval, numzeros); /*the length and offset found for the current position*/ length = 0; offset = 0; hashpos = hash->chain[wpos]; lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; /*search for the longest string*/ prev_offset = 0; for(;;) { if(chainlength++ >= maxchainlength) break; current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize); if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ prev_offset = current_offset; if(current_offset > 0) { /*test the next characters*/ foreptr = &in[pos]; backptr = &in[pos - current_offset]; /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ if(numzeros >= 3) { unsigned skip = hash->zeros[hashpos]; if(skip > numzeros) skip = numzeros; backptr += skip; foreptr += skip; } while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ { ++backptr; ++foreptr; } current_length = (unsigned)(foreptr - &in[pos]); if(current_length > length) { length = current_length; /*the longest length*/ offset = current_offset; /*the offset that is related to this longest length*/ /*jump out once a length of max length is found (speed gain). This also jumps out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ if(current_length >= nicematch) break; } } if(hashpos == hash->chain[hashpos]) break; if(numzeros >= 3 && length > numzeros) { hashpos = hash->chainz[hashpos]; if(hash->zeros[hashpos] != numzeros) break; } else { hashpos = hash->chain[hashpos]; /*outdated hash value, happens if particular value was not encountered in whole last window*/ if(hash->val[hashpos] != (int)hashval) break; } } if(lazymatching) { if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) { lazy = 1; lazylength = length; lazyoffset = offset; continue; /*try the next byte*/ } if(lazy) { lazy = 0; if(pos == 0) ERROR_BREAK(81); if(length > lazylength + 1) { /*push the previous character as literal*/ if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); } else { length = lazylength; offset = lazyoffset; hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ hash->headz[numzeros] = -1; /*idem*/ --pos; } } } if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); /*encode it as length/distance pair or literal value*/ if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ { if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); } else if(length < minmatch || (length == 3 && offset > 4096)) { /*compensate for the fact that longer offsets have more extra bits, a length of only 3 may be not worth it then*/ if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); } else { addLengthDistance(out, length, offset); for(i = 1; i < length; ++i) { ++pos; wpos = pos & (windowsize - 1); hashval = getHash(in, insize, pos); if(usezeros && hashval == 0) { if(numzeros == 0) numzeros = countZeros(in, insize, pos); else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { numzeros = 0; } updateHashChain(hash, wpos, hashval, numzeros); } } } /*end of the loop through each character of input*/ return error; } /* /////////////////////////////////////////////////////////////////////////// */ static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) { /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ size_t i, numdeflateblocks = (datasize + 65534u) / 65535u; unsigned datapos = 0; for(i = 0; i != numdeflateblocks; ++i) { unsigned BFINAL, BTYPE, LEN, NLEN; unsigned char firstbyte; size_t pos = out->size; BFINAL = (i == numdeflateblocks - 1); BTYPE = 0; LEN = 65535; if(datasize - datapos < 65535u) LEN = (unsigned)datasize - datapos; NLEN = 65535 - LEN; if(!ucvector_resize(out, out->size + LEN + 5)) return 83; /*alloc fail*/ firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1u) << 1u) + ((BTYPE & 2u) << 1u)); out->data[pos + 0] = firstbyte; out->data[pos + 1] = (unsigned char)(LEN & 255); out->data[pos + 2] = (unsigned char)(LEN >> 8u); out->data[pos + 3] = (unsigned char)(NLEN & 255); out->data[pos + 4] = (unsigned char)(NLEN >> 8u); lodepng_memcpy(out->data + pos + 5, data + datapos, LEN); datapos += LEN; } return 0; } /* write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. tree_ll: the tree for lit and len codes. tree_d: the tree for distance codes. */ static void writeLZ77data(LodePNGBitWriter* writer, const uivector* lz77_encoded, const HuffmanTree* tree_ll, const HuffmanTree* tree_d) { size_t i = 0; for(i = 0; i != lz77_encoded->size; ++i) { unsigned val = lz77_encoded->data[i]; writeBitsReversed(writer, tree_ll->codes[val], tree_ll->lengths[val]); if(val > 256) /*for a length code, 3 more things have to be added*/ { unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; unsigned length_extra_bits = lz77_encoded->data[++i]; unsigned distance_code = lz77_encoded->data[++i]; unsigned distance_index = distance_code; unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; unsigned distance_extra_bits = lz77_encoded->data[++i]; writeBits(writer, length_extra_bits, n_length_extra_bits); writeBitsReversed(writer, tree_d->codes[distance_code], tree_d->lengths[distance_code]); writeBits(writer, distance_extra_bits, n_distance_extra_bits); } } } /*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ static unsigned deflateDynamic(LodePNGBitWriter* writer, Hash* hash, const unsigned char* data, size_t datapos, size_t dataend, const LodePNGCompressSettings* settings, unsigned final) { unsigned error = 0; /* A block is compressed as follows: The PNG data is lz77 encoded, resulting in literal bytes and length/distance pairs. This is then huffman compressed with two huffman trees. One huffman tree is used for the lit and len values ("ll"), another huffman tree is used for the dist values ("d"). These two trees are stored using their code lengths, and to compress even more these code lengths are also run-length encoded and huffman compressed. This gives a huffman tree of code lengths "cl". The code lengths used to describe this third tree are the code length code lengths ("clcl"). */ /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ uivector lz77_encoded; HuffmanTree tree_ll; /*tree for lit,len values*/ HuffmanTree tree_d; /*tree for distance codes*/ HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ unsigned* frequencies_ll = 0; /*frequency of lit,len codes*/ unsigned* frequencies_d = 0; /*frequency of dist codes*/ unsigned* frequencies_cl = 0; /*frequency of code length codes*/ unsigned* bitlen_lld = 0; /*lit,len,dist code lengths (int bits), literally (without repeat codes).*/ unsigned* bitlen_lld_e = 0; /*bitlen_lld encoded with repeat codes (this is a rudimentary run length compression)*/ size_t datasize = dataend - datapos; /* If we could call "bitlen_cl" the code length code lengths ("clcl"), that is the bit lengths of codes to represent tree_cl in CLCL_ORDER, then due to the huffman compression of huffman tree representations ("two levels"), there are some analogies: bitlen_lld is to tree_cl what data is to tree_ll and tree_d. bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. */ unsigned BFINAL = final; size_t i; size_t numcodes_ll, numcodes_d, numcodes_lld, numcodes_lld_e, numcodes_cl; unsigned HLIT, HDIST, HCLEN; uivector_init(&lz77_encoded); HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); HuffmanTree_init(&tree_cl); /* could fit on stack, but >1KB is on the larger side so allocate instead */ frequencies_ll = (unsigned*)lodepng_malloc(286 * sizeof(*frequencies_ll)); frequencies_d = (unsigned*)lodepng_malloc(30 * sizeof(*frequencies_d)); frequencies_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl)); if(!frequencies_ll || !frequencies_d || !frequencies_cl) error = 83; /*alloc fail*/ /*This while loop never loops due to a break at the end, it is here to allow breaking out of it to the cleanup phase on error conditions.*/ while(!error) { lodepng_memset(frequencies_ll, 0, 286 * sizeof(*frequencies_ll)); lodepng_memset(frequencies_d, 0, 30 * sizeof(*frequencies_d)); lodepng_memset(frequencies_cl, 0, NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl)); if(settings->use_lz77) { error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, settings->minmatch, settings->nicematch, settings->lazymatching); if(error) break; } else { if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ } /*Count the frequencies of lit, len and dist codes*/ for(i = 0; i != lz77_encoded.size; ++i) { unsigned symbol = lz77_encoded.data[i]; ++frequencies_ll[symbol]; if(symbol > 256) { unsigned dist = lz77_encoded.data[i + 2]; ++frequencies_d[dist]; i += 3; } } frequencies_ll[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll, 257, 286, 15); if(error) break; /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d, 2, 30, 15); if(error) break; numcodes_ll = LODEPNG_MIN(tree_ll.numcodes, 286); numcodes_d = LODEPNG_MIN(tree_d.numcodes, 30); /*store the code lengths of both generated trees in bitlen_lld*/ numcodes_lld = numcodes_ll + numcodes_d; bitlen_lld = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld)); /*numcodes_lld_e never needs more size than bitlen_lld*/ bitlen_lld_e = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld_e)); if(!bitlen_lld || !bitlen_lld_e) ERROR_BREAK(83); /*alloc fail*/ numcodes_lld_e = 0; for(i = 0; i != numcodes_ll; ++i) bitlen_lld[i] = tree_ll.lengths[i]; for(i = 0; i != numcodes_d; ++i) bitlen_lld[numcodes_ll + i] = tree_d.lengths[i]; /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), 17 (3-10 zeroes), 18 (11-138 zeroes)*/ for(i = 0; i != numcodes_lld; ++i) { unsigned j = 0; /*amount of repetitions*/ while(i + j + 1 < numcodes_lld && bitlen_lld[i + j + 1] == bitlen_lld[i]) ++j; if(bitlen_lld[i] == 0 && j >= 2) /*repeat code for zeroes*/ { ++j; /*include the first zero*/ if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ { bitlen_lld_e[numcodes_lld_e++] = 17; bitlen_lld_e[numcodes_lld_e++] = j - 3; } else /*repeat code 18 supports max 138 zeroes*/ { if(j > 138) j = 138; bitlen_lld_e[numcodes_lld_e++] = 18; bitlen_lld_e[numcodes_lld_e++] = j - 11; } i += (j - 1); } else if(j >= 3) /*repeat code for value other than zero*/ { size_t k; unsigned num = j / 6u, rest = j % 6u; bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i]; for(k = 0; k < num; ++k) { bitlen_lld_e[numcodes_lld_e++] = 16; bitlen_lld_e[numcodes_lld_e++] = 6 - 3; } if(rest >= 3) { bitlen_lld_e[numcodes_lld_e++] = 16; bitlen_lld_e[numcodes_lld_e++] = rest - 3; } else j -= rest; i += j; } else /*too short to benefit from repeat code*/ { bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i]; } } /*generate tree_cl, the huffmantree of huffmantrees*/ for(i = 0; i != numcodes_lld_e; ++i) { ++frequencies_cl[bitlen_lld_e[i]]; /*after a repeat code come the bits that specify the number of repetitions, those don't need to be in the frequencies_cl calculation*/ if(bitlen_lld_e[i] >= 16) ++i; } error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl, NUM_CODE_LENGTH_CODES, NUM_CODE_LENGTH_CODES, 7); if(error) break; /*compute amount of code-length-code-lengths to output*/ numcodes_cl = NUM_CODE_LENGTH_CODES; /*trim zeros at the end (using CLCL_ORDER), but minimum size must be 4 (see HCLEN below)*/ while(numcodes_cl > 4u && tree_cl.lengths[CLCL_ORDER[numcodes_cl - 1u]] == 0) { numcodes_cl--; } /* Write everything into the output After the BFINAL and BTYPE, the dynamic block consists out of the following: - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN - (HCLEN+4)*3 bits code lengths of code length alphabet - HLIT + 257 code lengths of lit/length alphabet (encoded using the code length alphabet, + possible repetition codes 16, 17, 18) - HDIST + 1 code lengths of distance alphabet (encoded using the code length alphabet, + possible repetition codes 16, 17, 18) - compressed data - 256 (end code) */ /*Write block type*/ writeBits(writer, BFINAL, 1); writeBits(writer, 0, 1); /*first bit of BTYPE "dynamic"*/ writeBits(writer, 1, 1); /*second bit of BTYPE "dynamic"*/ /*write the HLIT, HDIST and HCLEN values*/ /*all three sizes take trimmed ending zeroes into account, done either by HuffmanTree_makeFromFrequencies or in the loop for numcodes_cl above, which saves space. */ HLIT = (unsigned)(numcodes_ll - 257); HDIST = (unsigned)(numcodes_d - 1); HCLEN = (unsigned)(numcodes_cl - 4); writeBits(writer, HLIT, 5); writeBits(writer, HDIST, 5); writeBits(writer, HCLEN, 4); /*write the code lengths of the code length alphabet ("bitlen_cl")*/ for(i = 0; i != numcodes_cl; ++i) writeBits(writer, tree_cl.lengths[CLCL_ORDER[i]], 3); /*write the lengths of the lit/len AND the dist alphabet*/ for(i = 0; i != numcodes_lld_e; ++i) { writeBitsReversed(writer, tree_cl.codes[bitlen_lld_e[i]], tree_cl.lengths[bitlen_lld_e[i]]); /*extra bits of repeat codes*/ if(bitlen_lld_e[i] == 16) writeBits(writer, bitlen_lld_e[++i], 2); else if(bitlen_lld_e[i] == 17) writeBits(writer, bitlen_lld_e[++i], 3); else if(bitlen_lld_e[i] == 18) writeBits(writer, bitlen_lld_e[++i], 7); } /*write the compressed data symbols*/ writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); /*error: the length of the end code 256 must be larger than 0*/ if(tree_ll.lengths[256] == 0) ERROR_BREAK(64); /*write the end code*/ writeBitsReversed(writer, tree_ll.codes[256], tree_ll.lengths[256]); break; /*end of error-while*/ } /*cleanup*/ uivector_cleanup(&lz77_encoded); HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); HuffmanTree_cleanup(&tree_cl); lodepng_free(frequencies_ll); lodepng_free(frequencies_d); lodepng_free(frequencies_cl); lodepng_free(bitlen_lld); lodepng_free(bitlen_lld_e); return error; } static unsigned deflateFixed(LodePNGBitWriter* writer, Hash* hash, const unsigned char* data, size_t datapos, size_t dataend, const LodePNGCompressSettings* settings, unsigned final) { HuffmanTree tree_ll; /*tree for literal values and length codes*/ HuffmanTree tree_d; /*tree for distance codes*/ unsigned BFINAL = final; unsigned error = 0; size_t i; HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); error = generateFixedLitLenTree(&tree_ll); if(!error) error = generateFixedDistanceTree(&tree_d); if(!error) { writeBits(writer, BFINAL, 1); writeBits(writer, 1, 1); /*first bit of BTYPE*/ writeBits(writer, 0, 1); /*second bit of BTYPE*/ if(settings->use_lz77) /*LZ77 encoded*/ { uivector lz77_encoded; uivector_init(&lz77_encoded); error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, settings->minmatch, settings->nicematch, settings->lazymatching); if(!error) writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); uivector_cleanup(&lz77_encoded); } else /*no LZ77, but still will be Huffman compressed*/ { for(i = datapos; i < dataend; ++i) { writeBitsReversed(writer, tree_ll.codes[data[i]], tree_ll.lengths[data[i]]); } } /*add END code*/ if(!error) writeBitsReversed(writer,tree_ll.codes[256], tree_ll.lengths[256]); } /*cleanup*/ HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); return error; } static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { unsigned error = 0; size_t i, blocksize, numdeflateblocks; Hash hash; LodePNGBitWriter writer; LodePNGBitWriter_init(&writer, out); if(settings->btype > 2) return 61; else if(settings->btype == 0) return deflateNoCompression(out, in, insize); else if(settings->btype == 1) blocksize = insize; else /*if(settings->btype == 2)*/ { /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ blocksize = insize / 8u + 8; if(blocksize < 65536) blocksize = 65536; if(blocksize > 262144) blocksize = 262144; } numdeflateblocks = (insize + blocksize - 1) / blocksize; if(numdeflateblocks == 0) numdeflateblocks = 1; error = hash_init(&hash, settings->windowsize); if(!error) { for(i = 0; i != numdeflateblocks && !error; ++i) { unsigned final = (i == numdeflateblocks - 1); size_t start = i * blocksize; size_t end = start + blocksize; if(end > insize) end = insize; if(settings->btype == 1) error = deflateFixed(&writer, &hash, in, start, end, settings, final); else if(settings->btype == 2) error = deflateDynamic(&writer, &hash, in, start, end, settings, final); } } hash_cleanup(&hash); return error; } unsigned lodepng_deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { ucvector v = ucvector_init(*out, *outsize); unsigned error = lodepng_deflatev(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } static unsigned deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(settings->custom_deflate) { return settings->custom_deflate(out, outsize, in, insize, settings); } else { return lodepng_deflate(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_DECODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* / Adler32 / */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) { unsigned s1 = adler & 0xffffu; unsigned s2 = (adler >> 16u) & 0xffffu; while(len != 0u) { unsigned i; /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/ unsigned amount = len > 5552u ? 5552u : len; len -= amount; for(i = 0; i != amount; ++i) { s1 += (*data++); s2 += s1; } s1 %= 65521u; s2 %= 65521u; } return (s2 << 16u) | s1; } /*Return the adler32 of the bytes data[0..len-1]*/ static unsigned adler32(const unsigned char* data, unsigned len) { return update_adler32(1u, data, len); } /* ////////////////////////////////////////////////////////////////////////// */ /* / Zlib / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_DECODER static unsigned lodepng_zlib_decompressv(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned error = 0; unsigned CM, CINFO, FDICT; if(insize < 2) return 53; /*error, size of zlib data too small*/ /*read information from zlib header*/ if((in[0] * 256 + in[1]) % 31 != 0) { /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ return 24; } CM = in[0] & 15; CINFO = (in[0] >> 4) & 15; /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ FDICT = (in[1] >> 5) & 1; /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ if(CM != 8 || CINFO > 7) { /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ return 25; } if(FDICT != 0) { /*error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary."*/ return 26; } error = inflatev(out, in + 2, insize - 2, settings); if(error) return error; if(!settings->ignore_adler32) { unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); unsigned checksum = adler32(out->data, (unsigned)(out->size)); if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ } return 0; /*no error*/ } unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { ucvector v = ucvector_init(*out, *outsize); unsigned error = lodepng_zlib_decompressv(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } /*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(settings->custom_zlib) { return settings->custom_zlib(out, outsize, in, insize, settings); } else { unsigned error; ucvector v = ucvector_init(*out, *outsize); if(expected_size) { /*reserve the memory to avoid intermediate reallocations*/ ucvector_resize(&v, *outsize + expected_size); v.size = *outsize; } error = lodepng_zlib_decompressv(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { size_t i; unsigned error; unsigned char* deflatedata = 0; size_t deflatesize = 0; error = deflate(&deflatedata, &deflatesize, in, insize, settings); *out = NULL; *outsize = 0; if(!error) { *outsize = deflatesize + 6; *out = (unsigned char*)lodepng_malloc(*outsize); if(!*out) error = 83; /*alloc fail*/ } if(!error) { unsigned ADLER32 = adler32(in, (unsigned)insize); /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ unsigned FLEVEL = 0; unsigned FDICT = 0; unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; unsigned FCHECK = 31 - CMFFLG % 31; CMFFLG += FCHECK; (*out)[0] = (unsigned char)(CMFFLG >> 8); (*out)[1] = (unsigned char)(CMFFLG & 255); for(i = 0; i != deflatesize; ++i) (*out)[i + 2] = deflatedata[i]; lodepng_set32bitInt(&(*out)[*outsize - 4], ADLER32); } lodepng_free(deflatedata); return error; } /* compress using the default or custom zlib function */ static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(settings->custom_zlib) { return settings->custom_zlib(out, outsize, in, insize, settings); } else { return lodepng_zlib_compress(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_ENCODER*/ #else /*no LODEPNG_COMPILE_ZLIB*/ #ifdef LODEPNG_COMPILE_DECODER static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ (void)expected_size; return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_ENCODER /*this is a good tradeoff between speed and compression ratio*/ #define DEFAULT_WINDOWSIZE 2048 void lodepng_compress_settings_init(LodePNGCompressSettings* settings) { /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ settings->btype = 2; settings->use_lz77 = 1; settings->windowsize = DEFAULT_WINDOWSIZE; settings->minmatch = 3; settings->nicematch = 128; settings->lazymatching = 1; settings->custom_zlib = 0; settings->custom_deflate = 0; settings->custom_context = 0; } const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_DECODER void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { settings->ignore_adler32 = 0; settings->ignore_nlen = 0; settings->custom_zlib = 0; settings->custom_inflate = 0; settings->custom_context = 0; } const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0}; #endif /*LODEPNG_COMPILE_DECODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // End of Zlib related code. Begin of PNG related code. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_PNG /* ////////////////////////////////////////////////////////////////////////// */ /* / CRC32 / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifndef LODEPNG_NO_COMPILE_CRC /* CRC polynomial: 0xedb88320 */ static unsigned lodepng_crc32_table[256] = { 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u }; /*Return the CRC of the bytes buf[0..len-1].*/ unsigned lodepng_crc32(const unsigned char* data, size_t length) { unsigned r = 0xffffffffu; size_t i; for(i = 0; i < length; ++i) { r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u); } return r ^ 0xffffffffu; } #else /* !LODEPNG_NO_COMPILE_CRC */ unsigned lodepng_crc32(const unsigned char* data, size_t length); #endif /* !LODEPNG_NO_COMPILE_CRC */ /* ////////////////////////////////////////////////////////////////////////// */ /* / Reading and writing PNG color channel bits / */ /* ////////////////////////////////////////////////////////////////////////// */ /* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first, so LodePNGBitWriter and LodePNGBitReader can't be used for those. */ static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); ++(*bitpointer); return result; } /* TODO: make this faster */ static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { unsigned result = 0; size_t i; for(i = 0 ; i < nbits; ++i) { result <<= 1u; result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); } return result; } static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) { /*the current bit in bitstream may be 0 or 1 for this to work*/ if(bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u)))); else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u))); ++(*bitpointer); } /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG chunks / */ /* ////////////////////////////////////////////////////////////////////////// */ unsigned lodepng_chunk_length(const unsigned char* chunk) { return lodepng_read32bitInt(&chunk[0]); } void lodepng_chunk_type(char type[5], const unsigned char* chunk) { unsigned i; for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; type[4] = 0; /*null termination char*/ } unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) { if(lodepng_strlen(type) != 4) return 0; return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); } unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) { return((chunk[4] & 32) != 0); } unsigned char lodepng_chunk_private(const unsigned char* chunk) { return((chunk[6] & 32) != 0); } unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) { return((chunk[7] & 32) != 0); } unsigned char* lodepng_chunk_data(unsigned char* chunk) { return &chunk[8]; } const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) { return &chunk[8]; } unsigned lodepng_chunk_check_crc(const unsigned char* chunk) { unsigned length = lodepng_chunk_length(chunk); unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ unsigned checksum = lodepng_crc32(&chunk[4], length + 4); if(CRC != checksum) return 1; else return 0; } void lodepng_chunk_generate_crc(unsigned char* chunk) { unsigned length = lodepng_chunk_length(chunk); unsigned CRC = lodepng_crc32(&chunk[4], length + 4); lodepng_set32bitInt(chunk + 8 + length, CRC); } unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end) { if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/ if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ return chunk + 8; } else { size_t total_chunk_length; unsigned char* result; if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end; result = chunk + total_chunk_length; if(result < chunk) return end; /*pointer overflow*/ return result; } } const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) { if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/ if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ return chunk + 8; } else { size_t total_chunk_length; const unsigned char* result; if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end; result = chunk + total_chunk_length; if(result < chunk) return end; /*pointer overflow*/ return result; } } unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]) { for(;;) { if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */ if(lodepng_chunk_type_equals(chunk, type)) return chunk; chunk = lodepng_chunk_next(chunk, end); } } const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) { for(;;) { if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */ if(lodepng_chunk_type_equals(chunk, type)) return chunk; chunk = lodepng_chunk_next_const(chunk, end); } } unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk) { unsigned i; size_t total_chunk_length, new_length; unsigned char *chunk_start, *new_buffer; if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return 77; if(lodepng_addofl(*outsize, total_chunk_length, &new_length)) return 77; new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); if(!new_buffer) return 83; /*alloc fail*/ (*out) = new_buffer; (*outsize) = new_length; chunk_start = &(*out)[new_length - total_chunk_length]; for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; return 0; } /*Sets length and name and allocates the space for data and crc but does not set data or crc yet. Returns the start of the chunk in chunk. The start of the data is at chunk + 8. To finalize chunk, add the data, then use lodepng_chunk_generate_crc */ static unsigned lodepng_chunk_init(unsigned char** chunk, ucvector* out, unsigned length, const char* type) { size_t new_length = out->size; if(lodepng_addofl(new_length, length, &new_length)) return 77; if(lodepng_addofl(new_length, 12, &new_length)) return 77; if(!ucvector_resize(out, new_length)) return 83; /*alloc fail*/ *chunk = out->data + new_length - length - 12u; /*1: length*/ lodepng_set32bitInt(*chunk, length); /*2: chunk name (4 letters)*/ lodepng_memcpy(*chunk + 4, type, 4); return 0; } /* like lodepng_chunk_create but with custom allocsize */ static unsigned lodepng_chunk_createv(ucvector* out, unsigned length, const char* type, const unsigned char* data) { unsigned char* chunk; CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, length, type)); /*3: the data*/ lodepng_memcpy(chunk + 8, data, length); /*4: CRC (of the chunkname characters and the data)*/ lodepng_chunk_generate_crc(chunk); return 0; } unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length, const char* type, const unsigned char* data) { ucvector v = ucvector_init(*out, *outsize); unsigned error = lodepng_chunk_createv(&v, length, type, data); *out = v.data; *outsize = v.size; return error; } /* ////////////////////////////////////////////////////////////////////////// */ /* / Color types, channels, bits / */ /* ////////////////////////////////////////////////////////////////////////// */ /*checks if the colortype is valid and the bitdepth bd is allowed for this colortype. Return value is a LodePNG error code.*/ static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) { switch(colortype) { case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break; case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break; case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break; case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */ default: return 31; /* invalid color type */ } return 0; /*allowed color type / bits combination*/ } static unsigned getNumColorChannels(LodePNGColorType colortype) { switch(colortype) { case LCT_GREY: return 1; case LCT_RGB: return 3; case LCT_PALETTE: return 1; case LCT_GREY_ALPHA: return 2; case LCT_RGBA: return 4; case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */ default: return 0; /*invalid color type*/ } } static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) { /*bits per pixel is amount of channels * bits per channel*/ return getNumColorChannels(colortype) * bitdepth; } /* ////////////////////////////////////////////////////////////////////////// */ void lodepng_color_mode_init(LodePNGColorMode* info) { info->key_defined = 0; info->key_r = info->key_g = info->key_b = 0; info->colortype = LCT_RGBA; info->bitdepth = 8; info->palette = 0; info->palettesize = 0; } /*allocates palette memory if needed, and initializes all colors to black*/ static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info) { size_t i; /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/ /*the palette must have room for up to 256 colors with 4 bytes each.*/ if(!info->palette) info->palette = (unsigned char*)lodepng_malloc(1024); if(!info->palette) return; /*alloc fail*/ for(i = 0; i != 256; ++i) { /*Initialize all unused colors with black, the value used for invalid palette indices. This is an error according to the PNG spec, but common PNG decoders make it black instead. That makes color conversion slightly faster due to no error handling needed.*/ info->palette[i * 4 + 0] = 0; info->palette[i * 4 + 1] = 0; info->palette[i * 4 + 2] = 0; info->palette[i * 4 + 3] = 255; } } void lodepng_color_mode_cleanup(LodePNGColorMode* info) { lodepng_palette_clear(info); } unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) { lodepng_color_mode_cleanup(dest); lodepng_memcpy(dest, source, sizeof(LodePNGColorMode)); if(source->palette) { dest->palette = (unsigned char*)lodepng_malloc(1024); if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4); } return 0; } LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth) { LodePNGColorMode result; lodepng_color_mode_init(&result); result.colortype = colortype; result.bitdepth = bitdepth; return result; } static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) { size_t i; if(a->colortype != b->colortype) return 0; if(a->bitdepth != b->bitdepth) return 0; if(a->key_defined != b->key_defined) return 0; if(a->key_defined) { if(a->key_r != b->key_r) return 0; if(a->key_g != b->key_g) return 0; if(a->key_b != b->key_b) return 0; } if(a->palettesize != b->palettesize) return 0; for(i = 0; i != a->palettesize * 4; ++i) { if(a->palette[i] != b->palette[i]) return 0; } return 1; } void lodepng_palette_clear(LodePNGColorMode* info) { if(info->palette) lodepng_free(info->palette); info->palette = 0; info->palettesize = 0; } unsigned lodepng_palette_add(LodePNGColorMode* info, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { if(!info->palette) /*allocate palette if empty*/ { lodepng_color_mode_alloc_palette(info); if(!info->palette) return 83; /*alloc fail*/ } if(info->palettesize >= 256) { return 108; /*too many palette values*/ } info->palette[4 * info->palettesize + 0] = r; info->palette[4 * info->palettesize + 1] = g; info->palette[4 * info->palettesize + 2] = b; info->palette[4 * info->palettesize + 3] = a; ++info->palettesize; return 0; } /*calculate bits per pixel out of colortype and bitdepth*/ unsigned lodepng_get_bpp(const LodePNGColorMode* info) { return lodepng_get_bpp_lct(info->colortype, info->bitdepth); } unsigned lodepng_get_channels(const LodePNGColorMode* info) { return getNumColorChannels(info->colortype); } unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) { return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; } unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) { return (info->colortype & 4) != 0; /*4 or 6*/ } unsigned lodepng_is_palette_type(const LodePNGColorMode* info) { return info->colortype == LCT_PALETTE; } unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) { size_t i; for(i = 0; i != info->palettesize; ++i) { if(info->palette[i * 4 + 3] < 255) return 1; } return 0; } unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) { return info->key_defined || lodepng_is_alpha_type(info) || lodepng_has_palette_alpha(info); } static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); size_t n = (size_t)w * (size_t)h; return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u; } size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth); } #ifdef LODEPNG_COMPILE_PNG /*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, and in addition has one extra byte per line: the filter byte. So this gives a larger result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */ static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp) { /* + 1 for the filter byte, and possibly plus padding bits per line. */ /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */ size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u; return (size_t)h * line; } #ifdef LODEPNG_COMPILE_DECODER /*Safely checks whether size_t overflow can be caused due to amount of pixels. This check is overcautious rather than precise. If this check indicates no overflow, you can safely compute in a size_t (but not an unsigned): -(size_t)w * (size_t)h * 8 -amount of bytes in IDAT (including filter, padding and Adam7 bytes) -amount of bytes in raw color model Returns 1 if overflow possible, 0 if not. */ static int lodepng_pixel_overflow(unsigned w, unsigned h, const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) { size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor)); size_t numpixels, total; size_t line; /* bytes per line in worst case */ if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1; if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */ /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */ if(lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1; if(lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1; if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */ if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */ return 0; /* no overflow */ } #endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static void LodePNGUnknownChunks_init(LodePNGInfo* info) { unsigned i; for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; } static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) { unsigned i; for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); } static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) { unsigned i; LodePNGUnknownChunks_cleanup(dest); for(i = 0; i != 3; ++i) { size_t j; dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ for(j = 0; j < src->unknown_chunks_size[i]; ++j) { dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; } } return 0; } /******************************************************************************/ static void LodePNGText_init(LodePNGInfo* info) { info->text_num = 0; info->text_keys = NULL; info->text_strings = NULL; } static void LodePNGText_cleanup(LodePNGInfo* info) { size_t i; for(i = 0; i != info->text_num; ++i) { string_cleanup(&info->text_keys[i]); string_cleanup(&info->text_strings[i]); } lodepng_free(info->text_keys); lodepng_free(info->text_strings); } static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { size_t i = 0; dest->text_keys = 0; dest->text_strings = 0; dest->text_num = 0; for(i = 0; i != source->text_num; ++i) { CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); } return 0; } static unsigned lodepng_add_text_sized(LodePNGInfo* info, const char* key, const char* str, size_t size) { char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); if(new_keys) info->text_keys = new_keys; if(new_strings) info->text_strings = new_strings; if(!new_keys || !new_strings) return 83; /*alloc fail*/ ++info->text_num; info->text_keys[info->text_num - 1] = alloc_string(key); info->text_strings[info->text_num - 1] = alloc_string_sized(str, size); if(!info->text_keys[info->text_num - 1] || !info->text_strings[info->text_num - 1]) return 83; /*alloc fail*/ return 0; } unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) { return lodepng_add_text_sized(info, key, str, lodepng_strlen(str)); } void lodepng_clear_text(LodePNGInfo* info) { LodePNGText_cleanup(info); } /******************************************************************************/ static void LodePNGIText_init(LodePNGInfo* info) { info->itext_num = 0; info->itext_keys = NULL; info->itext_langtags = NULL; info->itext_transkeys = NULL; info->itext_strings = NULL; } static void LodePNGIText_cleanup(LodePNGInfo* info) { size_t i; for(i = 0; i != info->itext_num; ++i) { string_cleanup(&info->itext_keys[i]); string_cleanup(&info->itext_langtags[i]); string_cleanup(&info->itext_transkeys[i]); string_cleanup(&info->itext_strings[i]); } lodepng_free(info->itext_keys); lodepng_free(info->itext_langtags); lodepng_free(info->itext_transkeys); lodepng_free(info->itext_strings); } static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { size_t i = 0; dest->itext_keys = 0; dest->itext_langtags = 0; dest->itext_transkeys = 0; dest->itext_strings = 0; dest->itext_num = 0; for(i = 0; i != source->itext_num; ++i) { CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], source->itext_transkeys[i], source->itext_strings[i])); } return 0; } void lodepng_clear_itext(LodePNGInfo* info) { LodePNGIText_cleanup(info); } static unsigned lodepng_add_itext_sized(LodePNGInfo* info, const char* key, const char* langtag, const char* transkey, const char* str, size_t size) { char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); if(new_keys) info->itext_keys = new_keys; if(new_langtags) info->itext_langtags = new_langtags; if(new_transkeys) info->itext_transkeys = new_transkeys; if(new_strings) info->itext_strings = new_strings; if(!new_keys || !new_langtags || !new_transkeys || !new_strings) return 83; /*alloc fail*/ ++info->itext_num; info->itext_keys[info->itext_num - 1] = alloc_string(key); info->itext_langtags[info->itext_num - 1] = alloc_string(langtag); info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey); info->itext_strings[info->itext_num - 1] = alloc_string_sized(str, size); return 0; } unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, const char* transkey, const char* str) { return lodepng_add_itext_sized(info, key, langtag, transkey, str, lodepng_strlen(str)); } /* same as set but does not delete */ static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) { if(profile_size == 0) return 100; /*invalid ICC profile size*/ info->iccp_name = alloc_string(name); info->iccp_profile = (unsigned char*)lodepng_malloc(profile_size); if(!info->iccp_name || !info->iccp_profile) return 83; /*alloc fail*/ lodepng_memcpy(info->iccp_profile, profile, profile_size); info->iccp_profile_size = profile_size; return 0; /*ok*/ } unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) { if(info->iccp_name) lodepng_clear_icc(info); info->iccp_defined = 1; return lodepng_assign_icc(info, name, profile, profile_size); } void lodepng_clear_icc(LodePNGInfo* info) { string_cleanup(&info->iccp_name); lodepng_free(info->iccp_profile); info->iccp_profile = NULL; info->iccp_profile_size = 0; info->iccp_defined = 0; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ void lodepng_info_init(LodePNGInfo* info) { lodepng_color_mode_init(&info->color); info->interlace_method = 0; info->compression_method = 0; info->filter_method = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS info->background_defined = 0; info->background_r = info->background_g = info->background_b = 0; LodePNGText_init(info); LodePNGIText_init(info); info->time_defined = 0; info->phys_defined = 0; info->gama_defined = 0; info->chrm_defined = 0; info->srgb_defined = 0; info->iccp_defined = 0; info->iccp_name = NULL; info->iccp_profile = NULL; LodePNGUnknownChunks_init(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } void lodepng_info_cleanup(LodePNGInfo* info) { lodepng_color_mode_cleanup(&info->color); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS LodePNGText_cleanup(info); LodePNGIText_cleanup(info); lodepng_clear_icc(info); LodePNGUnknownChunks_cleanup(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) { lodepng_info_cleanup(dest); lodepng_memcpy(dest, source, sizeof(LodePNGInfo)); lodepng_color_mode_init(&dest->color); CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); if(source->iccp_defined) { CERROR_TRY_RETURN(lodepng_assign_icc(dest, source->iccp_name, source->iccp_profile, source->iccp_profile_size)); } LodePNGUnknownChunks_init(dest); CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ return 0; } /* ////////////////////////////////////////////////////////////////////////// */ /*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) { unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ unsigned p = index & m; in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ in = in << (bits * (m - p)); if(p == 0) out[index * bits / 8u] = in; else out[index * bits / 8u] |= in; } typedef struct ColorTree ColorTree; /* One node of a color tree This is the data structure used to count the number of unique colors and to get a palette index for a color. It's like an octree, but because the alpha channel is used too, each node has 16 instead of 8 children. */ struct ColorTree { ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ int index; /*the payload. Only has a meaningful value if this is in the last level*/ }; static void color_tree_init(ColorTree* tree) { lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children)); tree->index = -1; } static void color_tree_cleanup(ColorTree* tree) { int i; for(i = 0; i != 16; ++i) { if(tree->children[i]) { color_tree_cleanup(tree->children[i]); lodepng_free(tree->children[i]); } } } /*returns -1 if color not present, its index otherwise*/ static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { int bit = 0; for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) return -1; else tree = tree->children[i]; } return tree ? tree->index : -1; } #ifdef LODEPNG_COMPILE_ENCODER static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { return color_tree_get(tree, r, g, b, a) >= 0; } #endif /*LODEPNG_COMPILE_ENCODER*/ /*color is not allowed to already exist. Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist") Returns error code, or 0 if ok*/ static unsigned color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) { int bit; for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) { tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); if(!tree->children[i]) return 83; /*alloc fail*/ color_tree_init(tree->children[i]); } tree = tree->children[i]; } tree->index = (int)index; return 0; } /*put a pixel, given its RGBA color, into image of any color type*/ static unsigned rgba8ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { if(mode->colortype == LCT_GREY) { unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ if(mode->bitdepth == 8) out[i] = gray; else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray; else { /*take the most significant bits of gray*/ gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u); addColorBits(out, i, mode->bitdepth, gray); } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { out[i * 3 + 0] = r; out[i * 3 + 1] = g; out[i * 3 + 2] = b; } else { out[i * 6 + 0] = out[i * 6 + 1] = r; out[i * 6 + 2] = out[i * 6 + 3] = g; out[i * 6 + 4] = out[i * 6 + 5] = b; } } else if(mode->colortype == LCT_PALETTE) { int index = color_tree_get(tree, r, g, b, a); if(index < 0) return 82; /*color not in palette*/ if(mode->bitdepth == 8) out[i] = index; else addColorBits(out, i, mode->bitdepth, (unsigned)index); } else if(mode->colortype == LCT_GREY_ALPHA) { unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ if(mode->bitdepth == 8) { out[i * 2 + 0] = gray; out[i * 2 + 1] = a; } else if(mode->bitdepth == 16) { out[i * 4 + 0] = out[i * 4 + 1] = gray; out[i * 4 + 2] = out[i * 4 + 3] = a; } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { out[i * 4 + 0] = r; out[i * 4 + 1] = g; out[i * 4 + 2] = b; out[i * 4 + 3] = a; } else { out[i * 8 + 0] = out[i * 8 + 1] = r; out[i * 8 + 2] = out[i * 8 + 3] = g; out[i * 8 + 4] = out[i * 8 + 5] = b; out[i * 8 + 6] = out[i * 8 + 7] = a; } } return 0; /*no error*/ } /*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ static void rgba16ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r, unsigned short g, unsigned short b, unsigned short a) { if(mode->colortype == LCT_GREY) { unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ out[i * 2 + 0] = (gray >> 8) & 255; out[i * 2 + 1] = gray & 255; } else if(mode->colortype == LCT_RGB) { out[i * 6 + 0] = (r >> 8) & 255; out[i * 6 + 1] = r & 255; out[i * 6 + 2] = (g >> 8) & 255; out[i * 6 + 3] = g & 255; out[i * 6 + 4] = (b >> 8) & 255; out[i * 6 + 5] = b & 255; } else if(mode->colortype == LCT_GREY_ALPHA) { unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ out[i * 4 + 0] = (gray >> 8) & 255; out[i * 4 + 1] = gray & 255; out[i * 4 + 2] = (a >> 8) & 255; out[i * 4 + 3] = a & 255; } else if(mode->colortype == LCT_RGBA) { out[i * 8 + 0] = (r >> 8) & 255; out[i * 8 + 1] = r & 255; out[i * 8 + 2] = (g >> 8) & 255; out[i * 8 + 3] = g & 255; out[i * 8 + 4] = (b >> 8) & 255; out[i * 8 + 5] = b & 255; out[i * 8 + 6] = (a >> 8) & 255; out[i * 8 + 7] = a & 255; } } /*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) { if(mode->colortype == LCT_GREY) { if(mode->bitdepth == 8) { *r = *g = *b = in[i]; if(mode->key_defined && *r == mode->key_r) *a = 0; else *a = 255; } else if(mode->bitdepth == 16) { *r = *g = *b = in[i * 2 + 0]; if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; else *a = 255; } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = i * mode->bitdepth; unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); *r = *g = *b = (value * 255) / highest; if(mode->key_defined && value == mode->key_r) *a = 0; else *a = 255; } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; else *a = 255; } else { *r = in[i * 6 + 0]; *g = in[i * 6 + 2]; *b = in[i * 6 + 4]; if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 255; } } else if(mode->colortype == LCT_PALETTE) { unsigned index; if(mode->bitdepth == 8) index = in[i]; else { size_t j = i * mode->bitdepth; index = readBitsFromReversedStream(&j, in, mode->bitdepth); } /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ *r = mode->palette[index * 4 + 0]; *g = mode->palette[index * 4 + 1]; *b = mode->palette[index * 4 + 2]; *a = mode->palette[index * 4 + 3]; } else if(mode->colortype == LCT_GREY_ALPHA) { if(mode->bitdepth == 8) { *r = *g = *b = in[i * 2 + 0]; *a = in[i * 2 + 1]; } else { *r = *g = *b = in[i * 4 + 0]; *a = in[i * 4 + 2]; } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { *r = in[i * 4 + 0]; *g = in[i * 4 + 1]; *b = in[i * 4 + 2]; *a = in[i * 4 + 3]; } else { *r = in[i * 8 + 0]; *g = in[i * 8 + 2]; *b = in[i * 8 + 4]; *a = in[i * 8 + 6]; } } } /*Similar to getPixelColorRGBA8, but with all the for loops inside of the color mode test cases, optimized to convert the colors much faster, when converting to the common case of RGBA with 8 bit per channel. buffer must be RGBA with enough memory.*/ static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode) { unsigned num_channels = 4; size_t i; if(mode->colortype == LCT_GREY) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i]; buffer[3] = 255; } if(mode->key_defined) { buffer -= numpixels * num_channels; for(i = 0; i != numpixels; ++i, buffer += num_channels) { if(buffer[0] == mode->key_r) buffer[3] = 0; } } } else if(mode->bitdepth == 16) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2]; buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; } } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = 0; for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; } } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { lodepng_memcpy(buffer, &in[i * 3], 3); buffer[3] = 255; } if(mode->key_defined) { buffer -= numpixels * num_channels; for(i = 0; i != numpixels; ++i, buffer += num_channels) { if(buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0; } } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 6 + 0]; buffer[1] = in[i * 6 + 2]; buffer[2] = in[i * 6 + 4]; buffer[3] = mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; } } } else if(mode->colortype == LCT_PALETTE) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned index = in[i]; /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ lodepng_memcpy(buffer, &mode->palette[index * 4], 4); } } else { size_t j = 0; for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ lodepng_memcpy(buffer, &mode->palette[index * 4], 4); } } } else if(mode->colortype == LCT_GREY_ALPHA) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; buffer[3] = in[i * 2 + 1]; } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; buffer[3] = in[i * 4 + 2]; } } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { lodepng_memcpy(buffer, in, numpixels * 4); } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 8 + 0]; buffer[1] = in[i * 8 + 2]; buffer[2] = in[i * 8 + 4]; buffer[3] = in[i * 8 + 6]; } } } } /*Similar to getPixelColorsRGBA8, but with 3-channel RGB output.*/ static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode) { const unsigned num_channels = 3; size_t i; if(mode->colortype == LCT_GREY) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i]; } } else if(mode->bitdepth == 16) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2]; } } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = 0; for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; } } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { lodepng_memcpy(buffer, in, numpixels * 3); } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 6 + 0]; buffer[1] = in[i * 6 + 2]; buffer[2] = in[i * 6 + 4]; } } } else if(mode->colortype == LCT_PALETTE) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned index = in[i]; /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ lodepng_memcpy(buffer, &mode->palette[index * 4], 3); } } else { size_t j = 0; for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ lodepng_memcpy(buffer, &mode->palette[index * 4], 3); } } } else if(mode->colortype == LCT_GREY_ALPHA) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; } } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { lodepng_memcpy(buffer, &in[i * 4], 3); } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 8 + 0]; buffer[1] = in[i * 8 + 2]; buffer[2] = in[i * 8 + 4]; } } } } /*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with given color type, but the given color type must be 16-bit itself.*/ static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) { if(mode->colortype == LCT_GREY) { *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; else *a = 65535; } else if(mode->colortype == LCT_RGB) { *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; if(mode->key_defined && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 65535; } else if(mode->colortype == LCT_GREY_ALPHA) { *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; } else if(mode->colortype == LCT_RGBA) { *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; } } unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h) { size_t i; ColorTree tree; size_t numpixels = (size_t)w * (size_t)h; unsigned error = 0; if(mode_in->colortype == LCT_PALETTE && !mode_in->palette) { return 107; /* error: must provide palette if input mode is palette */ } if(lodepng_color_mode_equal(mode_out, mode_in)) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); lodepng_memcpy(out, in, numbytes); return 0; } if(mode_out->colortype == LCT_PALETTE) { size_t palettesize = mode_out->palettesize; const unsigned char* palette = mode_out->palette; size_t palsize = (size_t)1u << mode_out->bitdepth; /*if the user specified output palette but did not give the values, assume they want the values of the input color type (assuming that one is palette). Note that we never create a new palette ourselves.*/ if(palettesize == 0) { palettesize = mode_in->palettesize; palette = mode_in->palette; /*if the input was also palette with same bitdepth, then the color types are also equal, so copy literally. This to preserve the exact indices that were in the PNG even in case there are duplicate colors in the palette.*/ if(mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); lodepng_memcpy(out, in, numbytes); return 0; } } if(palettesize < palsize) palsize = palettesize; color_tree_init(&tree); for(i = 0; i != palsize; ++i) { const unsigned char* p = &palette[i * 4]; error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); if(error) break; } } if(!error) { if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { for(i = 0; i != numpixels; ++i) { unsigned short r = 0, g = 0, b = 0, a = 0; getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); rgba16ToPixel(out, i, mode_out, r, g, b, a); } } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) { getPixelColorsRGBA8(out, numpixels, in, mode_in); } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) { getPixelColorsRGB8(out, numpixels, in, mode_in); } else { unsigned char r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); if(error) break; } } } if(mode_out->colortype == LCT_PALETTE) { color_tree_cleanup(&tree); } return error; } /* Converts a single rgb color without alpha from one type to another, color bits truncated to their bitdepth. In case of single channel (gray or palette), only the r channel is used. Slow function, do not use to process all pixels of an image. Alpha channel not supported on purpose: this is for bKGD, supporting alpha may prevent it from finding a color in the palette, from the specification it looks like bKGD should ignore the alpha values of the palette since it can use any palette index but doesn't have an alpha channel. Idem with ignoring color key. */ unsigned lodepng_convert_rgb( unsigned* r_out, unsigned* g_out, unsigned* b_out, unsigned r_in, unsigned g_in, unsigned b_in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in) { unsigned r = 0, g = 0, b = 0; unsigned mul = 65535 / ((1u << mode_in->bitdepth) - 1u); /*65535, 21845, 4369, 257, 1*/ unsigned shift = 16 - mode_out->bitdepth; if(mode_in->colortype == LCT_GREY || mode_in->colortype == LCT_GREY_ALPHA) { r = g = b = r_in * mul; } else if(mode_in->colortype == LCT_RGB || mode_in->colortype == LCT_RGBA) { r = r_in * mul; g = g_in * mul; b = b_in * mul; } else if(mode_in->colortype == LCT_PALETTE) { if(r_in >= mode_in->palettesize) return 82; r = mode_in->palette[r_in * 4 + 0] * 257u; g = mode_in->palette[r_in * 4 + 1] * 257u; b = mode_in->palette[r_in * 4 + 2] * 257u; } else { return 31; } /* now convert to output format */ if(mode_out->colortype == LCT_GREY || mode_out->colortype == LCT_GREY_ALPHA) { *r_out = r >> shift ; } else if(mode_out->colortype == LCT_RGB || mode_out->colortype == LCT_RGBA) { *r_out = r >> shift ; *g_out = g >> shift ; *b_out = b >> shift ; } else if(mode_out->colortype == LCT_PALETTE) { unsigned i; /* a 16-bit color cannot be in the palette */ if((r >> 8) != (r & 255) || (g >> 8) != (g & 255) || (b >> 8) != (b & 255)) return 82; for(i = 0; i < mode_out->palettesize; i++) { unsigned j = i * 4; if((r >> 8) == mode_out->palette[j + 0] && (g >> 8) == mode_out->palette[j + 1] && (b >> 8) == mode_out->palette[j + 2]) { *r_out = i; return 0; } } return 82; } else { return 31; } return 0; } #ifdef LODEPNG_COMPILE_ENCODER void lodepng_color_stats_init(LodePNGColorStats* stats) { /*stats*/ stats->colored = 0; stats->key = 0; stats->key_r = stats->key_g = stats->key_b = 0; stats->alpha = 0; stats->numcolors = 0; stats->bits = 1; stats->numpixels = 0; /*settings*/ stats->allow_palette = 1; stats->allow_greyscale = 1; } /*function used for debug purposes with C++*/ /*void printColorStats(LodePNGColorStats* p) { std::cout << "colored: " << (int)p->colored << ", "; std::cout << "key: " << (int)p->key << ", "; std::cout << "key_r: " << (int)p->key_r << ", "; std::cout << "key_g: " << (int)p->key_g << ", "; std::cout << "key_b: " << (int)p->key_b << ", "; std::cout << "alpha: " << (int)p->alpha << ", "; std::cout << "numcolors: " << (int)p->numcolors << ", "; std::cout << "bits: " << (int)p->bits << std::endl; }*/ /*Returns how many bits needed to represent given value (max 8 bit)*/ static unsigned getValueRequiredBits(unsigned char value) { if(value == 0 || value == 255) return 1; /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; return 8; } /*stats must already have been inited. */ unsigned lodepng_compute_color_stats(LodePNGColorStats* stats, const unsigned char* in, unsigned w, unsigned h, const LodePNGColorMode* mode_in) { size_t i; ColorTree tree; size_t numpixels = (size_t)w * (size_t)h; unsigned error = 0; /* mark things as done already if it would be impossible to have a more expensive case */ unsigned colored_done = lodepng_is_greyscale_type(mode_in) ? 1 : 0; unsigned alpha_done = lodepng_can_have_alpha(mode_in) ? 0 : 1; unsigned numcolors_done = 0; unsigned bpp = lodepng_get_bpp(mode_in); unsigned bits_done = (stats->bits == 1 && bpp == 1) ? 1 : 0; unsigned sixteen = 0; /* whether the input image is 16 bit */ unsigned maxnumcolors = 257; if(bpp <= 8) maxnumcolors = LODEPNG_MIN(257, stats->numcolors + (1u << bpp)); stats->numpixels += numpixels; /*if palette not allowed, no need to compute numcolors*/ if(!stats->allow_palette) numcolors_done = 1; color_tree_init(&tree); /*If the stats was already filled in from previous data, fill its palette in tree and mark things as done already if we know they are the most expensive case already*/ if(stats->alpha) alpha_done = 1; if(stats->colored) colored_done = 1; if(stats->bits == 16) numcolors_done = 1; if(stats->bits >= bpp) bits_done = 1; if(stats->numcolors >= maxnumcolors) numcolors_done = 1; if(!numcolors_done) { for(i = 0; i < stats->numcolors; i++) { const unsigned char* color = &stats->palette[i * 4]; error = color_tree_add(&tree, color[0], color[1], color[2], color[3], i); if(error) goto cleanup; } } /*Check if the 16-bit input is truly 16-bit*/ if(mode_in->bitdepth == 16 && !sixteen) { unsigned short r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ { stats->bits = 16; sixteen = 1; bits_done = 1; numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ break; } } } if(sixteen) { unsigned short r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); if(!colored_done && (r != g || r != b)) { stats->colored = 1; colored_done = 1; } if(!alpha_done) { unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b); if(a != 65535 && (a != 0 || (stats->key && !matchkey))) { stats->alpha = 1; stats->key = 0; alpha_done = 1; } else if(a == 0 && !stats->alpha && !stats->key) { stats->key = 1; stats->key_r = r; stats->key_g = g; stats->key_b = b; } else if(a == 65535 && stats->key && matchkey) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ stats->alpha = 1; stats->key = 0; alpha_done = 1; } } if(alpha_done && numcolors_done && colored_done && bits_done) break; } if(stats->key && !stats->alpha) { for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ stats->alpha = 1; stats->key = 0; alpha_done = 1; } } } } else /* < 16-bit */ { unsigned char r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); if(!bits_done && stats->bits < 8) { /*only r is checked, < 8 bits is only relevant for grayscale*/ unsigned bits = getValueRequiredBits(r); if(bits > stats->bits) stats->bits = bits; } bits_done = (stats->bits >= bpp); if(!colored_done && (r != g || r != b)) { stats->colored = 1; colored_done = 1; if(stats->bits < 8) stats->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ } if(!alpha_done) { unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b); if(a != 255 && (a != 0 || (stats->key && !matchkey))) { stats->alpha = 1; stats->key = 0; alpha_done = 1; if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } else if(a == 0 && !stats->alpha && !stats->key) { stats->key = 1; stats->key_r = r; stats->key_g = g; stats->key_b = b; } else if(a == 255 && stats->key && matchkey) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ stats->alpha = 1; stats->key = 0; alpha_done = 1; if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } } if(!numcolors_done) { if(!color_tree_has(&tree, r, g, b, a)) { error = color_tree_add(&tree, r, g, b, a, stats->numcolors); if(error) goto cleanup; if(stats->numcolors < 256) { unsigned char* p = stats->palette; unsigned n = stats->numcolors; p[n * 4 + 0] = r; p[n * 4 + 1] = g; p[n * 4 + 2] = b; p[n * 4 + 3] = a; } ++stats->numcolors; numcolors_done = stats->numcolors >= maxnumcolors; } } if(alpha_done && numcolors_done && colored_done && bits_done) break; } if(stats->key && !stats->alpha) { for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ stats->alpha = 1; stats->key = 0; alpha_done = 1; if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } } } /*make the stats's key always 16-bit for consistency - repeat each byte twice*/ stats->key_r += (stats->key_r << 8); stats->key_g += (stats->key_g << 8); stats->key_b += (stats->key_b << 8); } cleanup: color_tree_cleanup(&tree); return error; } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*Adds a single color to the color stats. The stats must already have been inited. The color must be given as 16-bit (with 2 bytes repeating for 8-bit and 65535 for opaque alpha channel). This function is expensive, do not call it for all pixels of an image but only for a few additional values. */ static unsigned lodepng_color_stats_add(LodePNGColorStats* stats, unsigned r, unsigned g, unsigned b, unsigned a) { unsigned error = 0; unsigned char image[8]; LodePNGColorMode mode; lodepng_color_mode_init(&mode); image[0] = r >> 8; image[1] = r; image[2] = g >> 8; image[3] = g; image[4] = b >> 8; image[5] = b; image[6] = a >> 8; image[7] = a; mode.bitdepth = 16; mode.colortype = LCT_RGBA; error = lodepng_compute_color_stats(stats, image, 1, 1, &mode); lodepng_color_mode_cleanup(&mode); return error; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*Computes a minimal PNG color model that can contain all colors as indicated by the stats. The stats should be computed with lodepng_compute_color_stats. mode_in is raw color profile of the image the stats were computed on, to copy palette order from when relevant. Minimal PNG color model means the color type and bit depth that gives smallest amount of bits in the output image, e.g. gray if only grayscale pixels, palette if less than 256 colors, color key if only single transparent color, ... This is used if auto_convert is enabled (it is by default). */ static unsigned auto_choose_color(LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, const LodePNGColorStats* stats) { unsigned error = 0; unsigned palettebits; size_t i, n; size_t numpixels = stats->numpixels; unsigned palette_ok, gray_ok; unsigned alpha = stats->alpha; unsigned key = stats->key; unsigned bits = stats->bits; mode_out->key_defined = 0; if(key && numpixels <= 16) { alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ key = 0; if(bits < 8) bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } gray_ok = !stats->colored; if(!stats->allow_greyscale) gray_ok = 0; if(!gray_ok && bits < 8) bits = 8; n = stats->numcolors; palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); palette_ok = n <= 256 && bits <= 8 && n != 0; /*n==0 means likely numcolors wasn't computed*/ if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ if(gray_ok && !alpha && bits <= palettebits) palette_ok = 0; /*gray is less overhead*/ if(!stats->allow_palette) palette_ok = 0; if(palette_ok) { const unsigned char* p = stats->palette; lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ for(i = 0; i != stats->numcolors; ++i) { error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); if(error) break; } mode_out->colortype = LCT_PALETTE; mode_out->bitdepth = palettebits; if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize && mode_in->bitdepth == mode_out->bitdepth) { /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ lodepng_color_mode_cleanup(mode_out); lodepng_color_mode_copy(mode_out, mode_in); } } else /*8-bit or 16-bit per channel*/ { mode_out->bitdepth = bits; mode_out->colortype = alpha ? (gray_ok ? LCT_GREY_ALPHA : LCT_RGBA) : (gray_ok ? LCT_GREY : LCT_RGB); if(key) { unsigned mask = (1u << mode_out->bitdepth) - 1u; /*stats always uses 16-bit, mask converts it*/ mode_out->key_r = stats->key_r & mask; mode_out->key_g = stats->key_g & mask; mode_out->key_b = stats->key_b & mask; mode_out->key_defined = 1; } } return error; } #endif /* #ifdef LODEPNG_COMPILE_ENCODER */ /* Paeth predictor, used by PNG filter type 4 The parameters are of type short, but should come from unsigned chars, the shorts are only needed to make the paeth calculation correct. */ static unsigned char paethPredictor(short a, short b, short c) { short pa = LODEPNG_ABS(b - c); short pb = LODEPNG_ABS(a - c); short pc = LODEPNG_ABS(a + b - c - c); /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */ if(pb < pa) { a = b; pa = pb; } return (pc < pa) ? c : a; } /*shared values used by multiple Adam7 related functions*/ static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ /* Outputs various dimensions and positions in the image related to the Adam7 reduced images. passw: output containing the width of the 7 passes passh: output containing the height of the 7 passes filter_passstart: output containing the index of the start and end of each reduced image with filter bytes padded_passstart output containing the index of the start and end of each reduced image when without filter bytes but with padded scanlines passstart: output containing the index of the start and end of each reduced image without padding between scanlines, but still padding between the images w, h: width and height of non-interlaced image bpp: bits per pixel "padded" is only relevant if bpp is less than 8 and a scanline or image does not end at a full byte */ static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) { /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ unsigned i; /*calculate width and height in pixels of each pass*/ for(i = 0; i != 7; ++i) { passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; if(passw[i] == 0) passh[i] = 0; if(passh[i] == 0) passw[i] = 0; } filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; for(i = 0; i != 7; ++i) { /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ filter_passstart[i + 1] = filter_passstart[i] + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0); /*bits padded if needed to fill full byte at end of each scanline*/ padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u); /*only padded at end of reduced image*/ passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u; } } #ifdef LODEPNG_COMPILE_DECODER /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG Decoder / */ /* ////////////////////////////////////////////////////////////////////////// */ /*read the information from the header and store it in the LodePNGInfo. return value is error*/ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { unsigned width, height; LodePNGInfo* info = &state->info_png; if(insize == 0 || in == 0) { CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ } if(insize < 33) { CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ } /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ /* TODO: remove this. One should use a new LodePNGState for new sessions */ lodepng_info_cleanup(info); lodepng_info_init(info); if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ } if(lodepng_chunk_length(in + 8) != 13) { CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ } if(!lodepng_chunk_type_equals(in + 8, "IHDR")) { CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ } /*read the values given in the header*/ width = lodepng_read32bitInt(&in[16]); height = lodepng_read32bitInt(&in[20]); /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/ if(w) *w = width; if(h) *h = height; info->color.bitdepth = in[24]; info->color.colortype = (LodePNGColorType)in[25]; info->compression_method = in[26]; info->filter_method = in[27]; info->interlace_method = in[28]; /*errors returned only after the parsing so other values are still output*/ /*error: invalid image size*/ if(width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93); /*error: invalid colortype or bitdepth combination*/ state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); if(state->error) return state->error; /*error: only compression method 0 is allowed in the specification*/ if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); /*error: only filter method 0 is allowed in the specification*/ if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); /*error: only interlace methods 0 and 1 exist in the specification*/ if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); if(!state->decoder.ignore_crc) { unsigned CRC = lodepng_read32bitInt(&in[29]); unsigned checksum = lodepng_crc32(&in[12], 17); if(CRC != checksum) { CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ } } return state->error; } static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length) { /* For PNG filter method 0 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) precon is the previous unfiltered scanline, recon the result, scanline the current one the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead recon and scanline MAY be the same memory address! precon must be disjoint. */ size_t i; switch(filterType) { case 0: for(i = 0; i != length; ++i) recon[i] = scanline[i]; break; case 1: for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; break; case 2: if(precon) { for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; } else { for(i = 0; i != length; ++i) recon[i] = scanline[i]; } break; case 3: if(precon) { for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u); for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u); } else { for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u); } break; case 4: if(precon) { for(i = 0; i != bytewidth; ++i) { recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ } /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */ if(bytewidth >= 4) { for(; i + 3 < length; i += 4) { size_t j = i - bytewidth; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3]; unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3]; recon[i + 0] = s0 + paethPredictor(r0, p0, q0); recon[i + 1] = s1 + paethPredictor(r1, p1, q1); recon[i + 2] = s2 + paethPredictor(r2, p2, q2); recon[i + 3] = s3 + paethPredictor(r3, p3, q3); } } else if(bytewidth >= 3) { for(; i + 2 < length; i += 3) { size_t j = i - bytewidth; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2]; unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2]; recon[i + 0] = s0 + paethPredictor(r0, p0, q0); recon[i + 1] = s1 + paethPredictor(r1, p1, q1); recon[i + 2] = s2 + paethPredictor(r2, p2, q2); } } else if(bytewidth >= 2) { for(; i + 1 < length; i += 2) { size_t j = i - bytewidth; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1]; unsigned char q0 = precon[j + 0], q1 = precon[j + 1]; recon[i + 0] = s0 + paethPredictor(r0, p0, q0); recon[i + 1] = s1 + paethPredictor(r1, p1, q1); } } for(; i != length; ++i) { recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); } } else { for(i = 0; i != bytewidth; ++i) { recon[i] = scanline[i]; } for(i = bytewidth; i < length; ++i) { /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ recon[i] = (scanline[i] + recon[i - bytewidth]); } } break; default: return 36; /*error: invalid filter type given*/ } return 0; } static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { /* For PNG filter method 0 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) */ unsigned y; unsigned char* prevline = 0; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ size_t bytewidth = (bpp + 7u) / 8u; /*the width of a scanline in bytes, not including the filter type*/ size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; for(y = 0; y < h; ++y) { size_t outindex = linebytes * y; size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ unsigned char filterType = in[inindex]; CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); prevline = &out[outindex]; } return 0; } /* in: Adam7 interlaced image, with no padding bits between scanlines, but between reduced images so that each reduced image starts at a byte. out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h bpp: bits per pixel out has the following size in bits: w * h * bpp. in is possibly bigger due to padding bits between reduced images. out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation (because that's likely a little bit faster) NOTE: comments about padding bits are only relevant if bpp < 8 */ static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); if(bpp >= 8) { for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8u; for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth; for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } } } } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp; for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } } } } } static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) { /* After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers for the Adam7 code, the color convert code and the output to the user. in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 only useful if (ilinebits - olinebits) is a value in the range 1..7 */ unsigned y; size_t diff = ilinebits - olinebits; size_t ibp = 0, obp = 0; /*input and output bit pointers*/ for(y = 0; y < h; ++y) { size_t x; for(x = 0; x < olinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } ibp += diff; } } /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks (with filter index bytes and possible padding bits) return value is error*/ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png) { /* This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps: *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8) *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace NOTE: the in buffer will be overwritten with intermediate data! */ unsigned bpp = lodepng_get_bpp(&info_png->color); if(bpp == 0) return 31; /*error: invalid colortype*/ if(info_png->interlace_method == 0) { if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) { CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h); } /*we can immediately filter into the out buffer, no other steps needed*/ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); } else /*interlace_method is 1 (Adam7)*/ { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); for(i = 0; i != 7; ++i) { CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, move bytes instead of bits or move not at all*/ if(bpp < 8) { /*remove padding bits in scanlines; after this there still may be padding bits between the different reduced images: each reduced image still starts nicely at a byte*/ removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]); } } Adam7_deinterlace(out, in, w, h, bpp); } return 0; } static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { unsigned pos = 0, i; color->palettesize = chunkLength / 3u; if(color->palettesize == 0 || color->palettesize > 256) return 38; /*error: palette too small or big*/ lodepng_color_mode_alloc_palette(color); if(!color->palette && color->palettesize) { color->palettesize = 0; return 83; /*alloc fail*/ } for(i = 0; i != color->palettesize; ++i) { color->palette[4 * i + 0] = data[pos++]; /*R*/ color->palette[4 * i + 1] = data[pos++]; /*G*/ color->palette[4 * i + 2] = data[pos++]; /*B*/ color->palette[4 * i + 3] = 255; /*alpha*/ } return 0; /* OK */ } static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { unsigned i; if(color->colortype == LCT_PALETTE) { /*error: more alpha values given than there are palette entries*/ if(chunkLength > color->palettesize) return 39; for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; } else if(color->colortype == LCT_GREY) { /*error: this chunk must be 2 bytes for grayscale image*/ if(chunkLength != 2) return 30; color->key_defined = 1; color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; } else if(color->colortype == LCT_RGB) { /*error: this chunk must be 6 bytes for RGB image*/ if(chunkLength != 6) return 41; color->key_defined = 1; color->key_r = 256u * data[0] + data[1]; color->key_g = 256u * data[2] + data[3]; color->key_b = 256u * data[4] + data[5]; } else return 42; /*error: tRNS chunk not allowed for other color models*/ return 0; /* OK */ } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*background color chunk (bKGD)*/ static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(info->color.colortype == LCT_PALETTE) { /*error: this chunk must be 1 byte for indexed color image*/ if(chunkLength != 1) return 43; /*error: invalid palette index, or maybe this chunk appeared before PLTE*/ if(data[0] >= info->color.palettesize) return 103; info->background_defined = 1; info->background_r = info->background_g = info->background_b = data[0]; } else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { /*error: this chunk must be 2 bytes for grayscale image*/ if(chunkLength != 2) return 44; /*the values are truncated to bitdepth in the PNG file*/ info->background_defined = 1; info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { /*error: this chunk must be 6 bytes for grayscale image*/ if(chunkLength != 6) return 45; /*the values are truncated to bitdepth in the PNG file*/ info->background_defined = 1; info->background_r = 256u * data[0] + data[1]; info->background_g = 256u * data[2] + data[3]; info->background_b = 256u * data[4] + data[5]; } return 0; /* OK */ } /*text chunk (tEXt)*/ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { unsigned error = 0; char *key = 0, *str = 0; while(!error) /*not really a while loop, only used to break on error*/ { unsigned length, string2_begin; length = 0; while(length < chunkLength && data[length] != 0) ++length; /*even though it's not allowed by the standard, no error is thrown if there's no null termination char, if the text is empty*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ lodepng_memcpy(key, data, length); key[length] = 0; string2_begin = length + 1; /*skip keyword null terminator*/ length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin); str = (char*)lodepng_malloc(length + 1); if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ lodepng_memcpy(str, data + string2_begin, length); str[length] = 0; error = lodepng_add_text(info, key, str); break; } lodepng_free(key); lodepng_free(str); return error; } /*compressed text chunk (zTXt)*/ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned length, string2_begin; char *key = 0; unsigned char* str = 0; size_t size = 0; while(!error) /*not really a while loop, only used to break on error*/ { for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ lodepng_memcpy(key, data, length); key[length] = 0; if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ string2_begin = length + 2; if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ length = (unsigned)chunkLength - string2_begin; /*will fail if zlib error, e.g. if length is too small*/ error = zlib_decompress(&str, &size, 0, &data[string2_begin], length, zlibsettings); if(error) break; error = lodepng_add_text_sized(info, key, (char*)str, size); break; } lodepng_free(key); lodepng_free(str); return error; } /*international text chunk (iTXt)*/ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned i; unsigned length, begin, compressed; char *key = 0, *langtag = 0, *transkey = 0; while(!error) /*not really a while loop, only used to break on error*/ { /*Quick check if the chunk length isn't too small. Even without check it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ /*read the key*/ for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ lodepng_memcpy(key, data, length); key[length] = 0; /*read the compression method*/ compressed = data[length + 1]; if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ /*even though it's not allowed by the standard, no error is thrown if there's no null termination char, if the text is empty for the next 3 texts*/ /*read the langtag*/ begin = length + 3; length = 0; for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; langtag = (char*)lodepng_malloc(length + 1); if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ lodepng_memcpy(langtag, data + begin, length); langtag[length] = 0; /*read the transkey*/ begin += length + 1; length = 0; for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; transkey = (char*)lodepng_malloc(length + 1); if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ lodepng_memcpy(transkey, data + begin, length); transkey[length] = 0; /*read the actual text*/ begin += length + 1; length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin; if(compressed) { unsigned char* str = 0; size_t size = 0; /*will fail if zlib error, e.g. if length is too small*/ error = zlib_decompress(&str, &size, 0, &data[begin], length, zlibsettings); if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size); lodepng_free(str); } else { error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)(data + begin), length); } break; } lodepng_free(key); lodepng_free(langtag); lodepng_free(transkey); return error; } static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ info->time_defined = 1; info->time.year = 256u * data[0] + data[1]; info->time.month = data[2]; info->time.day = data[3]; info->time.hour = data[4]; info->time.minute = data[5]; info->time.second = data[6]; return 0; /* OK */ } static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ info->phys_defined = 1; info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; info->phys_unit = data[8]; return 0; /* OK */ } static unsigned readChunk_gAMA(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 4) return 96; /*invalid gAMA chunk size*/ info->gama_defined = 1; info->gama_gamma = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; return 0; /* OK */ } static unsigned readChunk_cHRM(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 32) return 97; /*invalid cHRM chunk size*/ info->chrm_defined = 1; info->chrm_white_x = 16777216u * data[ 0] + 65536u * data[ 1] + 256u * data[ 2] + data[ 3]; info->chrm_white_y = 16777216u * data[ 4] + 65536u * data[ 5] + 256u * data[ 6] + data[ 7]; info->chrm_red_x = 16777216u * data[ 8] + 65536u * data[ 9] + 256u * data[10] + data[11]; info->chrm_red_y = 16777216u * data[12] + 65536u * data[13] + 256u * data[14] + data[15]; info->chrm_green_x = 16777216u * data[16] + 65536u * data[17] + 256u * data[18] + data[19]; info->chrm_green_y = 16777216u * data[20] + 65536u * data[21] + 256u * data[22] + data[23]; info->chrm_blue_x = 16777216u * data[24] + 65536u * data[25] + 256u * data[26] + data[27]; info->chrm_blue_y = 16777216u * data[28] + 65536u * data[29] + 256u * data[30] + data[31]; return 0; /* OK */ } static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 1) return 98; /*invalid sRGB chunk size (this one is never ignored)*/ info->srgb_defined = 1; info->srgb_intent = data[0]; return 0; /* OK */ } static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned i; size_t size = 0; unsigned length, string2_begin; info->iccp_defined = 1; if(info->iccp_name) lodepng_clear_icc(info); for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 2 >= chunkLength) return 75; /*no null termination, corrupt?*/ if(length < 1 || length > 79) return 89; /*keyword too short or long*/ info->iccp_name = (char*)lodepng_malloc(length + 1); if(!info->iccp_name) return 83; /*alloc fail*/ info->iccp_name[length] = 0; for(i = 0; i != length; ++i) info->iccp_name[i] = (char)data[i]; if(data[length + 1] != 0) return 72; /*the 0 byte indicating compression must be 0*/ string2_begin = length + 2; if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/ length = (unsigned)chunkLength - string2_begin; error = zlib_decompress(&info->iccp_profile, &size, 0, &data[string2_begin], length, zlibsettings); info->iccp_profile_size = size; if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/ return error; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos, const unsigned char* in, size_t insize) { const unsigned char* chunk = in + pos; unsigned chunkLength; const unsigned char* data; unsigned unhandled = 0; unsigned error = 0; if(pos + 4 > insize) return 30; chunkLength = lodepng_chunk_length(chunk); if(chunkLength > 2147483647) return 63; data = lodepng_chunk_data_const(chunk); if(data + chunkLength + 4 > in + insize) return 30; if(lodepng_chunk_type_equals(chunk, "PLTE")) { error = readChunk_PLTE(&state->info_png.color, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "tRNS")) { error = readChunk_tRNS(&state->info_png.color, data, chunkLength); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS } else if(lodepng_chunk_type_equals(chunk, "bKGD")) { error = readChunk_bKGD(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "tEXt")) { error = readChunk_tEXt(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "zTXt")) { error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "iTXt")) { error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "tIME")) { error = readChunk_tIME(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { error = readChunk_pHYs(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "gAMA")) { error = readChunk_gAMA(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "cHRM")) { error = readChunk_cHRM(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "sRGB")) { error = readChunk_sRGB(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "iCCP")) { error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } else { /* unhandled chunk is ok (is not an error) */ unhandled = 1; } if(!error && !unhandled && !state->decoder.ignore_crc) { if(lodepng_chunk_check_crc(chunk)) return 57; /*invalid CRC*/ } return error; } /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { unsigned char IEND = 0; const unsigned char* chunk; unsigned char* idat; /*the data from idat chunks, zlib compressed*/ size_t idatsize = 0; unsigned char* scanlines = 0; size_t scanlines_size = 0, expected_size = 0; size_t outsize = 0; /*for unknown chunk order*/ unsigned unknown = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /* safe output values in case error happens */ *out = 0; *w = *h = 0; state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ if(state->error) return; if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) { CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ } /*the input filesize is a safe upper bound for the sum of idat chunks size*/ idat = (unsigned char*)lodepng_malloc(insize); if(!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/ chunk = &in[33]; /*first byte of the first chunk after the header*/ /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer*/ while(!IEND && !state->error) { unsigned chunkLength; const unsigned char* data; /*the data in the chunk*/ /*error: size of the in buffer too small to contain next chunk*/ if((size_t)((chunk - in) + 12) > insize || chunk < in) { if(state->decoder.ignore_end) break; /*other errors may still happen though*/ CERROR_BREAK(state->error, 30); } /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ chunkLength = lodepng_chunk_length(chunk); /*error: chunk length larger than the max PNG chunk size*/ if(chunkLength > 2147483647) { if(state->decoder.ignore_end) break; /*other errors may still happen though*/ CERROR_BREAK(state->error, 63); } if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) { CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ } data = lodepng_chunk_data_const(chunk); unknown = 0; /*IDAT chunk, containing compressed image data*/ if(lodepng_chunk_type_equals(chunk, "IDAT")) { size_t newsize; if(lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); if(newsize > insize) CERROR_BREAK(state->error, 95); lodepng_memcpy(idat + idatsize, data, chunkLength); idatsize += chunkLength; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 3; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } else if(lodepng_chunk_type_equals(chunk, "IEND")) { /*IEND chunk*/ IEND = 1; } else if(lodepng_chunk_type_equals(chunk, "PLTE")) { /*palette chunk (PLTE)*/ state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); if(state->error) break; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 2; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } else if(lodepng_chunk_type_equals(chunk, "tRNS")) { /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that affects the alpha channel of pixels. */ state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); if(state->error) break; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*background color chunk (bKGD)*/ } else if(lodepng_chunk_type_equals(chunk, "bKGD")) { state->error = readChunk_bKGD(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "tEXt")) { /*text chunk (tEXt)*/ if(state->decoder.read_text_chunks) { state->error = readChunk_tEXt(&state->info_png, data, chunkLength); if(state->error) break; } } else if(lodepng_chunk_type_equals(chunk, "zTXt")) { /*compressed text chunk (zTXt)*/ if(state->decoder.read_text_chunks) { state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); if(state->error) break; } } else if(lodepng_chunk_type_equals(chunk, "iTXt")) { /*international text chunk (iTXt)*/ if(state->decoder.read_text_chunks) { state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); if(state->error) break; } } else if(lodepng_chunk_type_equals(chunk, "tIME")) { state->error = readChunk_tIME(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { state->error = readChunk_pHYs(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "gAMA")) { state->error = readChunk_gAMA(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "cHRM")) { state->error = readChunk_cHRM(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "sRGB")) { state->error = readChunk_sRGB(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "iCCP")) { state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); if(state->error) break; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) { CERROR_BREAK(state->error, 69); } unknown = 1; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS if(state->decoder.remember_unknown_chunks) { state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ { if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ } if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize); } if(state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) { state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */ } if(!state->error) { /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. If the decompressed size does not match the prediction, the image must be corrupt.*/ if(state->info_png.interlace_method == 0) { size_t bpp = lodepng_get_bpp(&state->info_png.color); expected_size = lodepng_get_raw_size_idat(*w, *h, bpp); } else { size_t bpp = lodepng_get_bpp(&state->info_png.color); /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/ expected_size = 0; expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp); if(*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp); expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp); if(*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp); expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp); if(*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp); expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp); } state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings); } if(!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/ lodepng_free(idat); if(!state->error) { outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); *out = (unsigned char*)lodepng_malloc(outsize); if(!*out) state->error = 83; /*alloc fail*/ } if(!state->error) { lodepng_memset(*out, 0, outsize); state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png); } lodepng_free(scanlines); } unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { *out = 0; decodeGeneric(out, w, h, state, in, insize); if(state->error) return state->error; if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) { /*same color type, no copying or converting of data needed*/ /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype the raw image has to the end user*/ if(!state->decoder.color_convert) { state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); if(state->error) return state->error; } } else { /*color conversion needed*/ unsigned char* data = *out; size_t outsize; /*TODO: check if this works according to the statement in the documentation: "The converter can convert from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/ if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) && !(state->info_raw.bitdepth == 8)) { return 56; /*unsupported color mode conversion*/ } outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); *out = (unsigned char*)lodepng_malloc(outsize); if(!(*out)) { state->error = 83; /*alloc fail*/ } else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h); lodepng_free(data); } return state->error; } unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth) { unsigned error; LodePNGState state; lodepng_state_init(&state); state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; error = lodepng_decode(out, w, h, &state, in, insize); lodepng_state_cleanup(&state); return error; } unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); } unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); } #ifdef LODEPNG_COMPILE_DISK unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer = 0; size_t buffersize; unsigned error; /* safe output values in case error happens */ *out = 0; *w = *h = 0; error = lodepng_load_file(&buffer, &buffersize, filename); if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); lodepng_free(buffer); return error; } unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); } unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); } #endif /*LODEPNG_COMPILE_DISK*/ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) { settings->color_convert = 1; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS settings->read_text_chunks = 1; settings->remember_unknown_chunks = 0; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ settings->ignore_crc = 0; settings->ignore_critical = 0; settings->ignore_end = 0; lodepng_decompress_settings_init(&settings->zlibsettings); } #endif /*LODEPNG_COMPILE_DECODER*/ #if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) void lodepng_state_init(LodePNGState* state) { #ifdef LODEPNG_COMPILE_DECODER lodepng_decoder_settings_init(&state->decoder); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER lodepng_encoder_settings_init(&state->encoder); #endif /*LODEPNG_COMPILE_ENCODER*/ lodepng_color_mode_init(&state->info_raw); lodepng_info_init(&state->info_png); state->error = 1; } void lodepng_state_cleanup(LodePNGState* state) { lodepng_color_mode_cleanup(&state->info_raw); lodepng_info_cleanup(&state->info_png); } void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) { lodepng_state_cleanup(dest); *dest = *source; lodepng_color_mode_init(&dest->info_raw); lodepng_info_init(&dest->info_png); dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; } #endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ #ifdef LODEPNG_COMPILE_ENCODER /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG Encoder / */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned writeSignature(ucvector* out) { size_t pos = out->size; const unsigned char signature[] = {137, 80, 78, 71, 13, 10, 26, 10}; /*8 bytes PNG signature, aka the magic bytes*/ if(!ucvector_resize(out, out->size + 8)) return 83; /*alloc fail*/ lodepng_memcpy(out->data + pos, signature, 8); return 0; } static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) { unsigned char *chunk, *data; CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 13, "IHDR")); data = chunk + 8; lodepng_set32bitInt(data + 0, w); /*width*/ lodepng_set32bitInt(data + 4, h); /*height*/ data[8] = (unsigned char)bitdepth; /*bit depth*/ data[9] = (unsigned char)colortype; /*color type*/ data[10] = 0; /*compression method*/ data[11] = 0; /*filter method*/ data[12] = interlace_method; /*interlace method*/ lodepng_chunk_generate_crc(chunk); return 0; } /* only adds the chunk if needed (there is a key or palette with alpha) */ static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) { unsigned char* chunk; size_t i, j = 8; CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, info->palettesize * 3, "PLTE")); for(i = 0; i != info->palettesize; ++i) { /*add all channels except alpha channel*/ chunk[j++] = info->palette[i * 4 + 0]; chunk[j++] = info->palette[i * 4 + 1]; chunk[j++] = info->palette[i * 4 + 2]; } lodepng_chunk_generate_crc(chunk); return 0; } static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) { unsigned char* chunk = 0; if(info->colortype == LCT_PALETTE) { size_t i, amount = info->palettesize; /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ for(i = info->palettesize; i != 0; --i) { if(info->palette[4 * (i - 1) + 3] != 255) break; --amount; } if(amount) { CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, amount, "tRNS")); /*add the alpha channel values from the palette*/ for(i = 0; i != amount; ++i) chunk[8 + i] = info->palette[4 * i + 3]; } } else if(info->colortype == LCT_GREY) { if(info->key_defined) { CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "tRNS")); chunk[8] = (unsigned char)(info->key_r >> 8); chunk[9] = (unsigned char)(info->key_r & 255); } } else if(info->colortype == LCT_RGB) { if(info->key_defined) { CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "tRNS")); chunk[8] = (unsigned char)(info->key_r >> 8); chunk[9] = (unsigned char)(info->key_r & 255); chunk[10] = (unsigned char)(info->key_g >> 8); chunk[11] = (unsigned char)(info->key_g & 255); chunk[12] = (unsigned char)(info->key_b >> 8); chunk[13] = (unsigned char)(info->key_b & 255); } } if(chunk) lodepng_chunk_generate_crc(chunk); return 0; } static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; unsigned char* zlib = 0; size_t zlibsize = 0; error = zlib_compress(&zlib, &zlibsize, data, datasize, zlibsettings); if(!error) { error = lodepng_chunk_createv(out, zlibsize, "IDAT", zlib); } lodepng_free(zlib); return error; } static unsigned addChunk_IEND(ucvector* out) { return lodepng_chunk_createv(out, 0, "IEND", 0); } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) { unsigned char* chunk = 0; size_t keysize = lodepng_strlen(keyword), textsize = lodepng_strlen(textstring); size_t size = keysize + 1 + textsize; if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, size, "tEXt")); lodepng_memcpy(chunk + 8, keyword, keysize); chunk[8 + keysize] = 0; /*null termination char*/ lodepng_memcpy(chunk + 9 + keysize, textstring, textsize); lodepng_chunk_generate_crc(chunk); return 0; } static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; unsigned char* chunk = 0; unsigned char* compressed = 0; size_t compressedsize = 0; size_t textsize = lodepng_strlen(textstring); size_t keysize = lodepng_strlen(keyword); if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ error = zlib_compress(&compressed, &compressedsize, (const unsigned char*)textstring, textsize, zlibsettings); if(!error) { size_t size = keysize + 2 + compressedsize; error = lodepng_chunk_init(&chunk, out, size, "zTXt"); } if(!error) { lodepng_memcpy(chunk + 8, keyword, keysize); chunk[8 + keysize] = 0; /*null termination char*/ chunk[9 + keysize] = 0; /*compression method: 0*/ lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize); lodepng_chunk_generate_crc(chunk); } lodepng_free(compressed); return error; } static unsigned addChunk_iTXt(ucvector* out, unsigned compress, const char* keyword, const char* langtag, const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; unsigned char* chunk = 0; unsigned char* compressed = 0; size_t compressedsize = 0; size_t textsize = lodepng_strlen(textstring); size_t keysize = lodepng_strlen(keyword), langsize = lodepng_strlen(langtag), transsize = lodepng_strlen(transkey); if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ if(compress) { error = zlib_compress(&compressed, &compressedsize, (const unsigned char*)textstring, textsize, zlibsettings); } if(!error) { size_t size = keysize + 3 + langsize + 1 + transsize + 1 + (compress ? compressedsize : textsize); error = lodepng_chunk_init(&chunk, out, size, "iTXt"); } if(!error) { size_t pos = 8; lodepng_memcpy(chunk + pos, keyword, keysize); pos += keysize; chunk[pos++] = 0; /*null termination char*/ chunk[pos++] = (compress ? 1 : 0); /*compression flag*/ chunk[pos++] = 0; /*compression method: 0*/ lodepng_memcpy(chunk + pos, langtag, langsize); pos += langsize; chunk[pos++] = 0; /*null termination char*/ lodepng_memcpy(chunk + pos, transkey, transsize); pos += transsize; chunk[pos++] = 0; /*null termination char*/ if(compress) { lodepng_memcpy(chunk + pos, compressed, compressedsize); } else { lodepng_memcpy(chunk + pos, textstring, textsize); } lodepng_chunk_generate_crc(chunk); } lodepng_free(compressed); return error; } static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) { unsigned char* chunk = 0; if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "bKGD")); chunk[8] = (unsigned char)(info->background_r >> 8); chunk[9] = (unsigned char)(info->background_r & 255); } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "bKGD")); chunk[8] = (unsigned char)(info->background_r >> 8); chunk[9] = (unsigned char)(info->background_r & 255); chunk[10] = (unsigned char)(info->background_g >> 8); chunk[11] = (unsigned char)(info->background_g & 255); chunk[12] = (unsigned char)(info->background_b >> 8); chunk[13] = (unsigned char)(info->background_b & 255); } else if(info->color.colortype == LCT_PALETTE) { CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 1, "bKGD")); chunk[8] = (unsigned char)(info->background_r & 255); /*palette index*/ } if(chunk) lodepng_chunk_generate_crc(chunk); return 0; } static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) { unsigned char* chunk; CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 7, "tIME")); chunk[8] = (unsigned char)(time->year >> 8); chunk[9] = (unsigned char)(time->year & 255); chunk[10] = (unsigned char)time->month; chunk[11] = (unsigned char)time->day; chunk[12] = (unsigned char)time->hour; chunk[13] = (unsigned char)time->minute; chunk[14] = (unsigned char)time->second; lodepng_chunk_generate_crc(chunk); return 0; } static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) { unsigned char* chunk; CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 9, "pHYs")); lodepng_set32bitInt(chunk + 8, info->phys_x); lodepng_set32bitInt(chunk + 12, info->phys_y); chunk[16] = info->phys_unit; lodepng_chunk_generate_crc(chunk); return 0; } static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) { unsigned char* chunk; CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 4, "gAMA")); lodepng_set32bitInt(chunk + 8, info->gama_gamma); lodepng_chunk_generate_crc(chunk); return 0; } static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) { unsigned char* chunk; CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 32, "cHRM")); lodepng_set32bitInt(chunk + 8, info->chrm_white_x); lodepng_set32bitInt(chunk + 12, info->chrm_white_y); lodepng_set32bitInt(chunk + 16, info->chrm_red_x); lodepng_set32bitInt(chunk + 20, info->chrm_red_y); lodepng_set32bitInt(chunk + 24, info->chrm_green_x); lodepng_set32bitInt(chunk + 28, info->chrm_green_y); lodepng_set32bitInt(chunk + 32, info->chrm_blue_x); lodepng_set32bitInt(chunk + 36, info->chrm_blue_y); lodepng_chunk_generate_crc(chunk); return 0; } static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) { unsigned char data = info->srgb_intent; return lodepng_chunk_createv(out, 1, "sRGB", &data); } static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; unsigned char* chunk = 0; unsigned char* compressed = 0; size_t compressedsize = 0; size_t keysize = lodepng_strlen(info->iccp_name); if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ error = zlib_compress(&compressed, &compressedsize, info->iccp_profile, info->iccp_profile_size, zlibsettings); if(!error) { size_t size = keysize + 2 + compressedsize; error = lodepng_chunk_init(&chunk, out, size, "iCCP"); } if(!error) { lodepng_memcpy(chunk + 8, info->iccp_name, keysize); chunk[8 + keysize] = 0; /*null termination char*/ chunk[9 + keysize] = 0; /*compression method: 0*/ lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize); lodepng_chunk_generate_crc(chunk); } lodepng_free(compressed); return error; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, size_t length, size_t bytewidth, unsigned char filterType) { size_t i; switch(filterType) { case 0: /*None*/ for(i = 0; i != length; ++i) out[i] = scanline[i]; break; case 1: /*Sub*/ for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; break; case 2: /*Up*/ if(prevline) { for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; } else { for(i = 0; i != length; ++i) out[i] = scanline[i]; } break; case 3: /*Average*/ if(prevline) { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); } else { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); } break; case 4: /*Paeth*/ if(prevline) { /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); for(i = bytewidth; i < length; ++i) { out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); } } else { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); } break; default: return; /*invalid filter type given*/ } } /* integer binary logarithm, max return value is 31 */ static size_t ilog2(size_t i) { size_t result = 0; if(i >= 65536) { result += 16; i >>= 16; } if(i >= 256) { result += 8; i >>= 8; } if(i >= 16) { result += 4; i >>= 4; } if(i >= 4) { result += 2; i >>= 2; } if(i >= 2) { result += 1; /*i >>= 1;*/ } return result; } /* integer approximation for i * log2(i), helper function for LFS_ENTROPY */ static size_t ilog2i(size_t i) { size_t l; if(i == 0) return 0; l = ilog2(i); /* approximate i*log2(i): l is integer logarithm, ((i - (1u << l)) << 1u) linearly approximates the missing fractional part multiplied by i */ return i * l + ((i - (1u << l)) << 1u); } static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, const LodePNGColorMode* color, const LodePNGEncoderSettings* settings) { /* For PNG filter method 0 out must be a buffer with as size: h + (w * h * bpp + 7u) / 8u, because there are the scanlines with 1 extra byte per scanline */ unsigned bpp = lodepng_get_bpp(color); /*the width of a scanline in bytes, not including the filter type*/ size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ size_t bytewidth = (bpp + 7u) / 8u; const unsigned char* prevline = 0; unsigned x, y; unsigned error = 0; LodePNGFilterStrategy strategy = settings->filter_strategy; /* There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. use fixed filtering, with the filter None). * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply all five filters and select the filter that produces the smallest sum of absolute values per row. This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum heuristic is used. */ if(settings->filter_palette_zero && (color->colortype == LCT_PALETTE || color->bitdepth < 8)) strategy = LFS_ZERO; if(bpp == 0) return 31; /*error: invalid color type*/ if(strategy >= LFS_ZERO && strategy <= LFS_FOUR) { unsigned char type = (unsigned char)strategy; for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; out[outindex] = type; /*filter type byte*/ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); prevline = &in[inindex]; } } else if(strategy == LFS_MINSUM) { /*adaptive filtering*/ unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned char type, bestType = 0; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) error = 83; /*alloc fail*/ } if(!error) { for(y = 0; y != h; ++y) { /*try the 5 filter types*/ for(type = 0; type != 5; ++type) { size_t sum = 0; filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); /*calculate the sum of the result*/ if(type == 0) { for(x = 0; x != linebytes; ++x) sum += (unsigned char)(attempt[type][x]); } else { for(x = 0; x != linebytes; ++x) { /*For differences, each byte should be treated as signed, values above 127 are negative (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. This means filtertype 0 is almost never chosen, but that is justified.*/ unsigned char s = attempt[type][x]; sum += s < 128 ? s : (255U - s); } } /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || sum < smallest) { bestType = type; smallest = sum; } } prevline = &in[y * linebytes]; /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_ENTROPY) { unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t bestSum = 0; unsigned type, bestType = 0; unsigned count[256]; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) error = 83; /*alloc fail*/ } if(!error) { for(y = 0; y != h; ++y) { /*try the 5 filter types*/ for(type = 0; type != 5; ++type) { size_t sum = 0; filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); lodepng_memset(count, 0, 256 * sizeof(*count)); for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; ++count[type]; /*the filter type itself is part of the scanline*/ for(x = 0; x != 256; ++x) { sum += ilog2i(count[x]); } /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || sum > bestSum) { bestType = type; bestSum = sum; } } prevline = &in[y * linebytes]; /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_PREDEFINED) { for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; unsigned char type = settings->predefined_filters[y]; out[outindex] = type; /*filter type byte*/ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); prevline = &in[inindex]; } } else if(strategy == LFS_BRUTE_FORCE) { /*brute force filter chooser. deflate the scanline after every filter attempt to see which one deflates best. This is very slow and gives only slightly smaller, sometimes even larger, result*/ size_t size[5]; unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned type = 0, bestType = 0; unsigned char* dummy; LodePNGCompressSettings zlibsettings; lodepng_memcpy(&zlibsettings, &settings->zlibsettings, sizeof(LodePNGCompressSettings)); /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, to simulate the true case where the tree is the same for the whole image. Sometimes it gives better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare cases better compression. It does make this a bit less slow, so it's worth doing this.*/ zlibsettings.btype = 1; /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG images only, so disable it*/ zlibsettings.custom_zlib = 0; zlibsettings.custom_deflate = 0; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) error = 83; /*alloc fail*/ } if(!error) { for(y = 0; y != h; ++y) /*try the 5 filter types*/ { for(type = 0; type != 5; ++type) { unsigned testsize = (unsigned)linebytes; /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); size[type] = 0; dummy = 0; zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); lodepng_free(dummy); /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || size[type] < smallest) { bestType = type; smallest = size[type]; } } prevline = &in[y * linebytes]; out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else return 88; /* unknown filter strategy */ return error; } static void addPaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) { /*The opposite of the removePaddingBits function olinebits must be >= ilinebits*/ unsigned y; size_t diff = olinebits - ilinebits; size_t obp = 0, ibp = 0; /*bit pointers*/ for(y = 0; y != h; ++y) { size_t x; for(x = 0; x < ilinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } /*obp += diff; --> no, fill in some value in the padding bits too, to avoid "Use of uninitialised value of size ###" warning from valgrind*/ for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); } } /* in: non-interlaced image with size w*h out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with no padding bits between scanlines, but between reduced images so that each reduced image starts at a byte. bpp: bits per pixel there are no padding bits, not between scanlines, not between reduced images in has the following size in bits: w * h * bpp. out is possibly bigger due to padding bits between reduced images NOTE: comments about padding bits are only relevant if bpp < 8 */ static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); if(bpp >= 8) { for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8u; for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } } } } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } } } } } /*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. return value is error**/ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) { /* This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: *) if no Adam7: 1) add padding bits (= possible extra bits per scanline if bpp < 8) 2) filter *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter */ unsigned bpp = lodepng_get_bpp(&info_png->color); unsigned error = 0; if(info_png->interlace_method == 0) { *outsize = h + (h * ((w * bpp + 7u) / 8u)); /*image size plus an extra byte per scanline + possible padding bits*/ *out = (unsigned char*)lodepng_malloc(*outsize); if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ if(!error) { /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) { unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7u) / 8u)); if(!padded) error = 83; /*alloc fail*/ if(!error) { addPaddingBits(padded, in, ((w * bpp + 7u) / 8u) * 8u, w * bpp, h); error = filter(*out, padded, w, h, &info_png->color, settings); } lodepng_free(padded); } else { /*we can immediately filter into the out buffer, no other steps needed*/ error = filter(*out, in, w, h, &info_png->color, settings); } } } else /*interlace_method is 1 (Adam7)*/ { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned char* adam7; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ *out = (unsigned char*)lodepng_malloc(*outsize); if(!(*out)) error = 83; /*alloc fail*/ adam7 = (unsigned char*)lodepng_malloc(passstart[7]); if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ if(!error) { unsigned i; Adam7_interlace(adam7, in, w, h, bpp); for(i = 0; i != 7; ++i) { if(bpp < 8) { unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); if(!padded) ERROR_BREAK(83); /*alloc fail*/ addPaddingBits(padded, &adam7[passstart[i]], ((passw[i] * bpp + 7u) / 8u) * 8u, passw[i] * bpp, passh[i]); error = filter(&(*out)[filter_passstart[i]], padded, passw[i], passh[i], &info_png->color, settings); lodepng_free(padded); } else { error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], passw[i], passh[i], &info_png->color, settings); } if(error) break; } } lodepng_free(adam7); } return error; } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) { unsigned char* inchunk = data; while((size_t)(inchunk - data) < datasize) { CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); out->allocsize = out->size; /*fix the allocsize again*/ inchunk = lodepng_chunk_next(inchunk, data + datasize); } return 0; } static unsigned isGrayICCProfile(const unsigned char* profile, unsigned size) { /* It is a gray profile if bytes 16-19 are "GRAY", rgb profile if bytes 16-19 are "RGB ". We do not perform any full parsing of the ICC profile here, other than check those 4 bytes to grayscale profile. Other than that, validity of the profile is not checked. This is needed only because the PNG specification requires using a non-gray color model if there is an ICC profile with "RGB " (sadly limiting compression opportunities if the input data is grayscale RGB data), and requires using a gray color model if it is "GRAY". */ if(size < 20) return 0; return profile[16] == 'G' && profile[17] == 'R' && profile[18] == 'A' && profile[19] == 'Y'; } static unsigned isRGBICCProfile(const unsigned char* profile, unsigned size) { /* See comment in isGrayICCProfile*/ if(size < 20) return 0; return profile[16] == 'R' && profile[17] == 'G' && profile[18] == 'B' && profile[19] == ' '; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ unsigned lodepng_encode(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGState* state) { unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ size_t datasize = 0; ucvector outv = ucvector_init(NULL, 0); LodePNGInfo info; const LodePNGInfo* info_png = &state->info_png; lodepng_info_init(&info); /*provide some proper output values if error will happen*/ *out = 0; *outsize = 0; state->error = 0; /*check input values validity*/ if((info_png->color.colortype == LCT_PALETTE || state->encoder.force_palette) && (info_png->color.palettesize == 0 || info_png->color.palettesize > 256)) { state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/ goto cleanup; } if(state->encoder.zlibsettings.btype > 2) { state->error = 61; /*error: invalid btype*/ goto cleanup; } if(info_png->interlace_method > 1) { state->error = 71; /*error: invalid interlace mode*/ goto cleanup; } state->error = checkColorValidity(info_png->color.colortype, info_png->color.bitdepth); if(state->error) goto cleanup; /*error: invalid color type given*/ state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); if(state->error) goto cleanup; /*error: invalid color type given*/ /* color convert and compute scanline filter types */ lodepng_info_copy(&info, &state->info_png); if(state->encoder.auto_convert) { LodePNGColorStats stats; lodepng_color_stats_init(&stats); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS if(info_png->iccp_defined && isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) { /*the PNG specification does not allow to use palette with a GRAY ICC profile, even if the palette has only gray colors, so disallow it.*/ stats.allow_palette = 0; } if(info_png->iccp_defined && isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) { /*the PNG specification does not allow to use grayscale color with RGB ICC profile, so disallow gray.*/ stats.allow_greyscale = 0; } #endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ state->error = lodepng_compute_color_stats(&stats, image, w, h, &state->info_raw); if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS if(info_png->background_defined) { /*the background chunk's color must be taken into account as well*/ unsigned r = 0, g = 0, b = 0; LodePNGColorMode mode16 = lodepng_color_mode_make(LCT_RGB, 16); lodepng_convert_rgb(&r, &g, &b, info_png->background_r, info_png->background_g, info_png->background_b, &mode16, &info_png->color); state->error = lodepng_color_stats_add(&stats, r, g, b, 65535); if(state->error) goto cleanup; } #endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ state->error = auto_choose_color(&info.color, &state->info_raw, &stats); if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*also convert the background chunk*/ if(info_png->background_defined) { if(lodepng_convert_rgb(&info.background_r, &info.background_g, &info.background_b, info_png->background_r, info_png->background_g, info_png->background_b, &info.color, &info_png->color)) { state->error = 104; goto cleanup; } } #endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS if(info_png->iccp_defined) { unsigned gray_icc = isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size); unsigned rgb_icc = isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size); unsigned gray_png = info.color.colortype == LCT_GREY || info.color.colortype == LCT_GREY_ALPHA; if(!gray_icc && !rgb_icc) { state->error = 100; /* Disallowed profile color type for PNG */ goto cleanup; } if(gray_icc != gray_png) { /*Not allowed to use RGB/RGBA/palette with GRAY ICC profile or vice versa, or in case of auto_convert, it wasn't possible to find appropriate model*/ state->error = state->encoder.auto_convert ? 102 : 101; goto cleanup; } } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) { unsigned char* converted; size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7u) / 8u; converted = (unsigned char*)lodepng_malloc(size); if(!converted && size) state->error = 83; /*alloc fail*/ if(!state->error) { state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); } if(!state->error) { state->error = preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); } lodepng_free(converted); if(state->error) goto cleanup; } else { state->error = preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); if(state->error) goto cleanup; } /* output all PNG chunks */ { #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS size_t i; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*write signature and chunks*/ state->error = writeSignature(&outv); if(state->error) goto cleanup; /*IHDR*/ state->error = addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*unknown chunks between IHDR and PLTE*/ if(info.unknown_chunks_data[0]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); if(state->error) goto cleanup; } /*color profile chunks must come before PLTE */ if(info.iccp_defined) { state->error = addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings); if(state->error) goto cleanup; } if(info.srgb_defined) { state->error = addChunk_sRGB(&outv, &info); if(state->error) goto cleanup; } if(info.gama_defined) { state->error = addChunk_gAMA(&outv, &info); if(state->error) goto cleanup; } if(info.chrm_defined) { state->error = addChunk_cHRM(&outv, &info); if(state->error) goto cleanup; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*PLTE*/ if(info.color.colortype == LCT_PALETTE) { state->error = addChunk_PLTE(&outv, &info.color); if(state->error) goto cleanup; } if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) { /*force_palette means: write suggested palette for truecolor in PLTE chunk*/ state->error = addChunk_PLTE(&outv, &info.color); if(state->error) goto cleanup; } /*tRNS (this will only add if when necessary) */ state->error = addChunk_tRNS(&outv, &info.color); if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*bKGD (must come between PLTE and the IDAt chunks*/ if(info.background_defined) { state->error = addChunk_bKGD(&outv, &info); if(state->error) goto cleanup; } /*pHYs (must come before the IDAT chunks)*/ if(info.phys_defined) { state->error = addChunk_pHYs(&outv, &info); if(state->error) goto cleanup; } /*unknown chunks between PLTE and IDAT*/ if(info.unknown_chunks_data[1]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); if(state->error) goto cleanup; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*IDAT (multiple IDAT chunks must be consecutive)*/ state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*tIME*/ if(info.time_defined) { state->error = addChunk_tIME(&outv, &info.time); if(state->error) goto cleanup; } /*tEXt and/or zTXt*/ for(i = 0; i != info.text_num; ++i) { if(lodepng_strlen(info.text_keys[i]) > 79) { state->error = 66; /*text chunk too large*/ goto cleanup; } if(lodepng_strlen(info.text_keys[i]) < 1) { state->error = 67; /*text chunk too small*/ goto cleanup; } if(state->encoder.text_compression) { state->error = addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); if(state->error) goto cleanup; } else { state->error = addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); if(state->error) goto cleanup; } } /*LodePNG version id in text chunk*/ if(state->encoder.add_id) { unsigned already_added_id_text = 0; for(i = 0; i != info.text_num; ++i) { const char* k = info.text_keys[i]; /* Could use strcmp, but we're not calling or reimplementing this C library function for this use only */ if(k[0] == 'L' && k[1] == 'o' && k[2] == 'd' && k[3] == 'e' && k[4] == 'P' && k[5] == 'N' && k[6] == 'G' && k[7] == '\0') { already_added_id_text = 1; break; } } if(already_added_id_text == 0) { state->error = addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ if(state->error) goto cleanup; } } /*iTXt*/ for(i = 0; i != info.itext_num; ++i) { if(lodepng_strlen(info.itext_keys[i]) > 79) { state->error = 66; /*text chunk too large*/ goto cleanup; } if(lodepng_strlen(info.itext_keys[i]) < 1) { state->error = 67; /*text chunk too small*/ goto cleanup; } state->error = addChunk_iTXt( &outv, state->encoder.text_compression, info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], &state->encoder.zlibsettings); if(state->error) goto cleanup; } /*unknown chunks between IDAT and IEND*/ if(info.unknown_chunks_data[2]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); if(state->error) goto cleanup; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ state->error = addChunk_IEND(&outv); if(state->error) goto cleanup; } cleanup: lodepng_info_cleanup(&info); lodepng_free(data); /*instead of cleaning the vector up, give it to the output*/ *out = outv.data; *outsize = outv.size; return state->error; } unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned error; LodePNGState state; lodepng_state_init(&state); state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; state.info_png.color.colortype = colortype; state.info_png.color.bitdepth = bitdepth; lodepng_encode(out, outsize, image, w, h, &state); error = state.error; lodepng_state_cleanup(&state); return error; } unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); } unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); } #ifdef LODEPNG_COMPILE_DISK unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); if(!error) error = lodepng_save_file(buffer, buffersize, filename); lodepng_free(buffer); return error; } unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); } unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); } #endif /*LODEPNG_COMPILE_DISK*/ void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) { lodepng_compress_settings_init(&settings->zlibsettings); settings->filter_palette_zero = 1; settings->filter_strategy = LFS_MINSUM; settings->auto_convert = 1; settings->force_palette = 0; settings->predefined_filters = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS settings->add_id = 0; settings->text_compression = 1; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ERROR_TEXT /* This returns the description of a numerical error code in English. This is also the documentation of all the error codes. */ const char* lodepng_error_text(unsigned code) { switch(code) { case 0: return "no error, everything went ok"; case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ case 13: return "problem while processing dynamic deflate block"; case 14: return "problem while processing dynamic deflate block"; case 15: return "problem while processing dynamic deflate block"; /*this error could happen if there are only 0 or 1 symbols present in the huffman code:*/ case 16: return "invalid code while processing dynamic deflate block"; case 17: return "end of out buffer memory reached while inflating"; case 18: return "invalid distance code while inflating"; case 19: return "end of out buffer memory reached while inflating"; case 20: return "invalid deflate block BTYPE encountered while decoding"; case 21: return "NLEN is not ones complement of LEN in a deflate block"; /*end of out buffer memory reached while inflating: This can happen if the inflated deflate data is longer than the amount of bytes required to fill up all the pixels of the image, given the color depth and image dimensions. Something that doesn't happen in a normal, well encoded, PNG image.*/ case 22: return "end of out buffer memory reached while inflating"; case 23: return "end of in buffer memory reached while inflating"; case 24: return "invalid FCHECK in zlib header"; case 25: return "invalid compression method in zlib header"; case 26: return "FDICT encountered in zlib header while it's not used for PNG"; case 27: return "PNG file is smaller than a PNG header"; /*Checks the magic file header, the first 8 bytes of the PNG file*/ case 28: return "incorrect PNG signature, it's no PNG or corrupted"; case 29: return "first chunk is not the header chunk"; case 30: return "chunk length too large, chunk broken off at end of file"; case 31: return "illegal PNG color type or bpp"; case 32: return "illegal PNG compression method"; case 33: return "illegal PNG filter method"; case 34: return "illegal PNG interlace method"; case 35: return "chunk length of a chunk is too large or the chunk too small"; case 36: return "illegal PNG filter type encountered"; case 37: return "illegal bit depth for this color type given"; case 38: return "the palette is too small or too big"; /*0, or more than 256 colors*/ case 39: return "tRNS chunk before PLTE or has more entries than palette size"; case 40: return "tRNS chunk has wrong size for grayscale image"; case 41: return "tRNS chunk has wrong size for RGB image"; case 42: return "tRNS chunk appeared while it was not allowed for this color type"; case 43: return "bKGD chunk has wrong size for palette image"; case 44: return "bKGD chunk has wrong size for grayscale image"; case 45: return "bKGD chunk has wrong size for RGB image"; case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; case 49: return "jumped past memory while generating dynamic huffman tree"; case 50: return "jumped past memory while generating dynamic huffman tree"; case 51: return "jumped past memory while inflating huffman block"; case 52: return "jumped past memory while inflating"; case 53: return "size of zlib data too small"; case 54: return "repeat symbol in tree while there was no value symbol yet"; /*jumped past tree while generating huffman tree, this could be when the tree will have more leaves than symbols after generating it out of the given lengths. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ case 55: return "jumped past tree while generating huffman tree"; case 56: return "given output image colortype or bitdepth not supported for color conversion"; case 57: return "invalid CRC encountered (checking CRC can be disabled)"; case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; case 59: return "requested color conversion not supported"; case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; /*LodePNG leaves the choice of RGB to grayscale conversion formula to the user.*/ case 62: return "conversion from color to grayscale not supported"; /*(2^31-1)*/ case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; case 71: return "invalid interlace mode given to encoder (must be 0 or 1)"; case 72: return "while decoding, invalid compression method encountering in zTXt or iTXt chunk (it must be 0)"; case 73: return "invalid tIME chunk size"; case 74: return "invalid pHYs chunk size"; /*length could be wrong, or data chopped off*/ case 75: return "no null termination char found while decoding text chunk"; case 76: return "iTXt chunk too short to contain required bytes"; case 77: return "integer overflow in buffer size"; case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ case 79: return "failed to open file for writing"; case 80: return "tried creating a tree of 0 symbols"; case 81: return "lazy matching at pos 0 is impossible"; case 82: return "color conversion to palette requested while a color isn't in palette, or index out of bounds"; case 83: return "memory allocation failed"; case 84: return "given image too small to contain all pixels to be encoded"; case 86: return "impossible offset in lz77 encoding (internal bug)"; case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; case 89: return "text chunk keyword too short or long: must have size 1-79"; /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ case 90: return "windowsize must be a power of two"; case 91: return "invalid decompressed idat size"; case 92: return "integer overflow due to too many pixels"; case 93: return "zero width or height is invalid"; case 94: return "header chunk must have a size of 13 bytes"; case 95: return "integer overflow with combined idat chunk size"; case 96: return "invalid gAMA chunk size"; case 97: return "invalid cHRM chunk size"; case 98: return "invalid sRGB chunk size"; case 99: return "invalid sRGB rendering intent"; case 100: return "invalid ICC profile color type, the PNG specification only allows RGB or GRAY"; case 101: return "PNG specification does not allow RGB ICC profile on gray color types and vice versa"; case 102: return "not allowed to set grayscale ICC profile with colored pixels by PNG specification"; case 103: return "invalid palette index in bKGD chunk. Maybe it came before PLTE chunk?"; case 104: return "invalid bKGD color while encoding (e.g. palette index out of range)"; case 105: return "integer overflow of bitsize"; case 106: return "PNG file must have PLTE chunk if color type is palette"; case 107: return "color convert from palette mode requested without setting the palette data in it"; case 108: return "tried to add more than 256 values to a palette"; } return "unknown error code"; } #endif /*LODEPNG_COMPILE_ERROR_TEXT*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // C++ Wrapper // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_CPP namespace lodepng { #ifdef LODEPNG_COMPILE_DISK unsigned load_file(std::vector& buffer, const std::string& filename) { long size = lodepng_filesize(filename.c_str()); if(size < 0) return 78; buffer.resize((size_t)size); return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ unsigned save_file(const std::vector& buffer, const std::string& filename) { return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); } #endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings& settings) { unsigned char* buffer = 0; size_t buffersize = 0; unsigned error = zlib_decompress(&buffer, &buffersize, 0, in, insize, &settings); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned decompress(std::vector& out, const std::vector& in, const LodePNGDecompressSettings& settings) { return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); } #endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER unsigned compress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGCompressSettings& settings) { unsigned char* buffer = 0; size_t buffersize = 0; unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned compress(std::vector& out, const std::vector& in, const LodePNGCompressSettings& settings) { return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); } #endif /* LODEPNG_COMPILE_ENCODER */ #endif /* LODEPNG_COMPILE_ZLIB */ #ifdef LODEPNG_COMPILE_PNG State::State() { lodepng_state_init(this); } State::State(const State& other) { lodepng_state_init(this); lodepng_state_copy(this, &other); } State::~State() { lodepng_state_cleanup(this); } State& State::operator=(const State& other) { lodepng_state_copy(this, &other); return *this; } #ifdef LODEPNG_COMPILE_DECODER unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer = 0; unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); if(buffer && !error) { State state; state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); out.insert(out.end(), &buffer[0], &buffer[buffersize]); } lodepng_free(buffer); return error; } unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::vector& in, LodePNGColorType colortype, unsigned bitdepth) { return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); } unsigned decode(std::vector& out, unsigned& w, unsigned& h, State& state, const unsigned char* in, size_t insize) { unsigned char* buffer = NULL; unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); if(buffer && !error) { size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); out.insert(out.end(), &buffer[0], &buffer[buffersize]); } lodepng_free(buffer); return error; } unsigned decode(std::vector& out, unsigned& w, unsigned& h, State& state, const std::vector& in) { return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); } #ifdef LODEPNG_COMPILE_DISK unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, LodePNGColorType colortype, unsigned bitdepth) { std::vector buffer; /* safe output values in case error happens */ w = h = 0; unsigned error = load_file(buffer, filename); if(error) return error; return decode(out, w, h, buffer, colortype, bitdepth); } #endif /* LODEPNG_COMPILE_DECODER */ #endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ENCODER unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned encode(std::vector& out, const std::vector& in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); } unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, State& state) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned encode(std::vector& out, const std::vector& in, unsigned w, unsigned h, State& state) { if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; return encode(out, in.empty() ? 0 : &in[0], w, h, state); } #ifdef LODEPNG_COMPILE_DISK unsigned encode(const std::string& filename, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { std::vector buffer; unsigned error = encode(buffer, in, w, h, colortype, bitdepth); if(!error) error = save_file(buffer, filename); return error; } unsigned encode(const std::string& filename, const std::vector& in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); } #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_ENCODER */ #endif /* LODEPNG_COMPILE_PNG */ } /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/meson.build000664 001750 001750 00000000036 15164251010 042754 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesubdir('js') subdir('regexp') mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testFill.cpp000664 001750 001750 00000023503 15164251010 032471 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; TEST_CASE("Filling Creation", "[tvgFill]") { auto linear = unique_ptr(LinearGradient::gen()); REQUIRE(linear); REQUIRE(linear->type() == Type::LinearGradient); auto radial = unique_ptr(RadialGradient::gen()); REQUIRE(radial); REQUIRE(radial->type() == Type::RadialGradient); } TEST_CASE("Common Filling", "[tvgFill]") { auto fill = LinearGradient::gen(); REQUIRE(fill); //Options REQUIRE(fill->spread() == FillSpread::Pad); REQUIRE(fill->spread(FillSpread::Pad) == Result::Success); REQUIRE(fill->spread(FillSpread::Reflect) == Result::Success); REQUIRE(fill->spread(FillSpread::Repeat) == Result::Success); REQUIRE(fill->spread() == FillSpread::Repeat); //ColorStops const Fill::ColorStop* cs = nullptr; REQUIRE(fill->colorStops(nullptr) == 0); REQUIRE(fill->colorStops(&cs) == 0); REQUIRE(cs == nullptr); Fill::ColorStop cs2[4] = { {0.0f, 0, 0, 0, 0}, {0.2f, 50, 25, 50, 25}, {0.5f, 100, 100, 100, 125}, {1.0f, 255, 255, 255, 255} }; REQUIRE(fill->colorStops(nullptr, 4) == Result::InvalidArguments); REQUIRE(fill->colorStops(cs2, 0) == Result::InvalidArguments); REQUIRE(fill->colorStops(cs2, 4) == Result::Success); REQUIRE(fill->colorStops(&cs) == 4); for (int i = 0; i < 4; ++i) { REQUIRE(cs[i].offset == cs2[i].offset); REQUIRE(cs[i].r == cs2[i].r); REQUIRE(cs[i].g == cs2[i].g); REQUIRE(cs[i].b == cs2[i].b); }; //Reset ColorStop REQUIRE(fill->colorStops(nullptr, 0) == Result::Success); REQUIRE(fill->colorStops(&cs) == 0); //Set to Shape auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->fill(fill) == Result::Success); REQUIRE(shape->fill() == fill); Paint::rel(shape); } TEST_CASE("Fill Transformation", "[tvgFill]") { auto fill = unique_ptr(LinearGradient::gen()); REQUIRE(fill); //no transformation auto mGet = fill->transform(); REQUIRE(mGet.e11 == Approx(1.0f).margin(0.000001)); REQUIRE(mGet.e12 == Approx(0.0f).margin(0.000001)); REQUIRE(mGet.e13 == Approx(0.0f).margin(0.000001)); REQUIRE(mGet.e21 == Approx(0.0f).margin(0.000001)); REQUIRE(mGet.e22 == Approx(1.0f).margin(0.000001)); REQUIRE(mGet.e23 == Approx(0.0f).margin(0.000001)); REQUIRE(mGet.e31 == Approx(0.0f).margin(0.000001)); REQUIRE(mGet.e32 == Approx(0.0f).margin(0.000001)); REQUIRE(mGet.e33 == Approx(1.0f).margin(0.000001)); auto mSet = Matrix{1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f, -7.7f, -8.8f, -9.9f}; REQUIRE(fill->transform(mSet) == Result::Success); //transformation was set mGet = fill->transform(); REQUIRE(mGet.e11 == Approx(mSet.e11).margin(0.000001)); REQUIRE(mGet.e12 == Approx(mSet.e12).margin(0.000001)); REQUIRE(mGet.e13 == Approx(mSet.e13).margin(0.000001)); REQUIRE(mGet.e21 == Approx(mSet.e21).margin(0.000001)); REQUIRE(mGet.e22 == Approx(mSet.e22).margin(0.000001)); REQUIRE(mGet.e23 == Approx(mSet.e23).margin(0.000001)); REQUIRE(mGet.e31 == Approx(mSet.e31).margin(0.000001)); REQUIRE(mGet.e32 == Approx(mSet.e32).margin(0.000001)); REQUIRE(mGet.e33 == Approx(mSet.e33).margin(0.000001)); } TEST_CASE("Linear Filling", "[tvgFill]") { auto fill = unique_ptr(LinearGradient::gen()); REQUIRE(fill); float x1, y1, x2, y2; REQUIRE(fill->linear(nullptr, nullptr, nullptr, nullptr) == Result::Success); REQUIRE(fill->linear(0, 0, 0, 0) == Result::Success); REQUIRE(fill->linear(&x1, nullptr, &x2, nullptr) == Result::Success); REQUIRE(x1 == 0.0f); REQUIRE(x2 == 0.0f); REQUIRE(fill->linear(-1.0f, -1.0f, 100.0f, 100.0f) == Result::Success); REQUIRE(fill->linear(&x1, &y1, &x2, &y2) == Result::Success); REQUIRE(x1 == -1.0f); REQUIRE(y1 == -1.0f); REQUIRE(x2 == 100.0f); REQUIRE(y2 == 100.0f); } TEST_CASE("Radial Filling", "[tvgFill]") { auto fill = unique_ptr(RadialGradient::gen()); REQUIRE(fill); float cx, cy, r, fx, fy, fr; REQUIRE(fill->radial(0, 0, -1, 0, 0, 0) == Result::InvalidArguments); REQUIRE(fill->radial(0, 0, 0, 0, 0, -1) == Result::InvalidArguments); REQUIRE(fill->radial(nullptr, nullptr, nullptr) == Result::Success); REQUIRE(fill->radial(100, 120, 50, 10, 20, 5) == Result::Success); REQUIRE(fill->radial(&cx, nullptr, &r) == Result::Success); REQUIRE(cx == 100.0f); REQUIRE(r == 50.0f); REQUIRE(fill->radial(nullptr, &cy, nullptr, &fx, &fy, &fr) == Result::Success); REQUIRE(cy == 120); REQUIRE(fx == 10); REQUIRE(fy == 20); REQUIRE(fr == 5); REQUIRE(fill->radial(0, 0, 0, 0, 0, 0) == Result::Success); REQUIRE(fill->radial(&cx, &cy, &r, &fx, &fy, &fr) == Result::Success); REQUIRE(cx == 0.0f); REQUIRE(cy == 0.0f); REQUIRE(r == 0.0f); REQUIRE(fx == 0.0f); REQUIRE(fy == 0.0f); REQUIRE(fr == 0.0f); } TEST_CASE("Linear Filling Duplication", "[tvgFill]") { auto fill = unique_ptr(LinearGradient::gen()); REQUIRE(fill); //Setup Fill::ColorStop cs[4] = { {0.0f, 0, 0, 0, 0}, {0.2f, 50, 25, 50, 25}, {0.5f, 100, 100, 100, 125}, {1.0f, 255, 255, 255, 255} }; REQUIRE(fill->colorStops(cs, 4) == Result::Success); REQUIRE(fill->spread(FillSpread::Reflect) == Result::Success); REQUIRE(fill->linear(-10.0f, 10.0f, 100.0f, 120.0f) == Result::Success); auto m = Matrix{1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f, -7.7f, -8.8f, -9.9f}; REQUIRE(fill->transform(m) == Result::Success); //Duplication auto dup = unique_ptr((LinearGradient*)fill->duplicate()); REQUIRE(dup); REQUIRE(dup->spread() == FillSpread::Reflect); float x1, y1, x2, y2; REQUIRE(fill->linear(&x1, &y1, &x2, &y2) == Result::Success); REQUIRE(x1 == -10.0f); REQUIRE(y1 == 10.0f); REQUIRE(x2 == 100.0f); REQUIRE(y2 == 120.0f); const Fill::ColorStop* cs2 = nullptr; REQUIRE(fill->colorStops(&cs2) == 4); for (int i = 0; i < 4; ++i) { REQUIRE(cs[i].offset == cs2[i].offset); REQUIRE(cs[i].r == cs2[i].r); REQUIRE(cs[i].g == cs2[i].g); REQUIRE(cs[i].b == cs2[i].b); } auto mDup = dup->transform(); REQUIRE(mDup.e11 == Approx(m.e11).margin(0.000001)); REQUIRE(mDup.e12 == Approx(m.e12).margin(0.000001)); REQUIRE(mDup.e13 == Approx(m.e13).margin(0.000001)); REQUIRE(mDup.e21 == Approx(m.e21).margin(0.000001)); REQUIRE(mDup.e22 == Approx(m.e22).margin(0.000001)); REQUIRE(mDup.e23 == Approx(m.e23).margin(0.000001)); REQUIRE(mDup.e31 == Approx(m.e31).margin(0.000001)); REQUIRE(mDup.e32 == Approx(m.e32).margin(0.000001)); REQUIRE(mDup.e33 == Approx(m.e33).margin(0.000001)); } TEST_CASE("Radial Filling Duplication", "[tvgFill]") { auto fill = unique_ptr(RadialGradient::gen()); REQUIRE(fill); //Setup Fill::ColorStop cs[4] = { {0.0f, 0, 0, 0, 0}, {0.2f, 50, 25, 50, 25}, {0.5f, 100, 100, 100, 125}, {1.0f, 255, 255, 255, 255} }; REQUIRE(fill->colorStops(cs, 4) == Result::Success); REQUIRE(fill->spread(FillSpread::Reflect) == Result::Success); REQUIRE(fill->radial(100.0f, 120.0f, 50.0f, 10.0f, 20.0f, 5.0f) == Result::Success); auto m = Matrix{1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f, -7.7f, -8.8f, -9.9f}; REQUIRE(fill->transform(m) == Result::Success); //Duplication auto dup = unique_ptr((RadialGradient*)fill->duplicate()); REQUIRE(dup); REQUIRE(dup->spread() == FillSpread::Reflect); float cx, cy, r, fx, fy, fr; REQUIRE(dup->radial(&cx, &cy, &r, &fx, &fy, &fr) == Result::Success); REQUIRE(cx == 100.0f); REQUIRE(cy == 120.0f); REQUIRE(r == 50.0f); REQUIRE(fx == 10.0f); REQUIRE(fy == 20.0f); REQUIRE(fr == 5.0f); const Fill::ColorStop* cs2 = nullptr; REQUIRE(fill->colorStops(&cs2) == 4); for (int i = 0; i < 4; ++i) { REQUIRE(cs[i].offset == cs2[i].offset); REQUIRE(cs[i].r == cs2[i].r); REQUIRE(cs[i].g == cs2[i].g); REQUIRE(cs[i].b == cs2[i].b); } auto mDup = dup->transform(); REQUIRE(mDup.e11 == Approx(m.e11).margin(0.000001)); REQUIRE(mDup.e12 == Approx(m.e12).margin(0.000001)); REQUIRE(mDup.e13 == Approx(m.e13).margin(0.000001)); REQUIRE(mDup.e21 == Approx(m.e21).margin(0.000001)); REQUIRE(mDup.e22 == Approx(m.e22).margin(0.000001)); REQUIRE(mDup.e23 == Approx(m.e23).margin(0.000001)); REQUIRE(mDup.e31 == Approx(m.e31).margin(0.000001)); REQUIRE(mDup.e32 == Approx(m.e32).margin(0.000001)); REQUIRE(mDup.e33 == Approx(m.e33).margin(0.000001)); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/lottie2gif/000775 001750 001750 00000000000 15164251010 032425 5ustar00ddennedyddennedy000000 000000 thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-math.inc.h000664 001750 001750 00000010305 15164251010 047541 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Math built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_MATH /* Number properties: * (property name, number value, writable, enumerable, configurable) */ /* ECMA-262 v5, 15.8.1.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_E_U, ECMA_BUILTIN_NUMBER_E, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.8.1.2 */ NUMBER_VALUE (LIT_MAGIC_STRING_LN10_U, ECMA_BUILTIN_NUMBER_LN10, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.8.1.3 */ NUMBER_VALUE (LIT_MAGIC_STRING_LN2_U, ECMA_BUILTIN_NUMBER_LN2, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.8.1.4 */ NUMBER_VALUE (LIT_MAGIC_STRING_LOG2E_U, ECMA_BUILTIN_NUMBER_LOG2E, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.8.1.5 */ NUMBER_VALUE (LIT_MAGIC_STRING_LOG10E_U, ECMA_BUILTIN_NUMBER_LOG10E, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.8.1.6 */ NUMBER_VALUE (LIT_MAGIC_STRING_PI_U, ECMA_BUILTIN_NUMBER_PI, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.8.1.7 */ NUMBER_VALUE (LIT_MAGIC_STRING_SQRT1_2_U, ECMA_BUILTIN_NUMBER_SQRT_1_2, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.8.1.8 */ NUMBER_VALUE (LIT_MAGIC_STRING_SQRT2_U, ECMA_BUILTIN_NUMBER_SQRT2, ECMA_PROPERTY_FIXED) /* ECMA-262 v6, 20.2.1.9 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_MATH_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_ABS, ECMA_MATH_OBJECT_ABS, 1, 1) ROUTINE (LIT_MAGIC_STRING_ACOS, ECMA_MATH_OBJECT_ACOS, 1, 1) ROUTINE (LIT_MAGIC_STRING_ASIN, ECMA_MATH_OBJECT_ASIN, 1, 1) ROUTINE (LIT_MAGIC_STRING_ATAN, ECMA_MATH_OBJECT_ATAN, 1, 1) ROUTINE (LIT_MAGIC_STRING_ATAN2, ECMA_MATH_OBJECT_ATAN2, 2, 2) ROUTINE (LIT_MAGIC_STRING_CEIL, ECMA_MATH_OBJECT_CEIL, 1, 1) ROUTINE (LIT_MAGIC_STRING_COS, ECMA_MATH_OBJECT_COS, 1, 1) ROUTINE (LIT_MAGIC_STRING_EXP, ECMA_MATH_OBJECT_EXP, 1, 1) ROUTINE (LIT_MAGIC_STRING_FLOOR, ECMA_MATH_OBJECT_FLOOR, 1, 1) ROUTINE (LIT_MAGIC_STRING_LOG, ECMA_MATH_OBJECT_LOG, 1, 1) ROUTINE (LIT_MAGIC_STRING_MAX, ECMA_MATH_OBJECT_MAX, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_MIN, ECMA_MATH_OBJECT_MIN, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_POW, ECMA_MATH_OBJECT_POW, 2, 2) ROUTINE (LIT_MAGIC_STRING_RANDOM, ECMA_MATH_OBJECT_RANDOM, 0, 0) ROUTINE (LIT_MAGIC_STRING_ROUND, ECMA_MATH_OBJECT_ROUND, 1, 1) ROUTINE (LIT_MAGIC_STRING_SIN, ECMA_MATH_OBJECT_SIN, 1, 1) ROUTINE (LIT_MAGIC_STRING_SQRT, ECMA_MATH_OBJECT_SQRT, 1, 1) ROUTINE (LIT_MAGIC_STRING_TAN, ECMA_MATH_OBJECT_TAN, 1, 1) ROUTINE (LIT_MAGIC_STRING_ACOSH, ECMA_MATH_OBJECT_ACOSH, 1, 1) ROUTINE (LIT_MAGIC_STRING_ASINH, ECMA_MATH_OBJECT_ASINH, 1, 1) ROUTINE (LIT_MAGIC_STRING_ATANH, ECMA_MATH_OBJECT_ATANH, 1, 1) ROUTINE (LIT_MAGIC_STRING_CBRT, ECMA_MATH_OBJECT_CBRT, 1, 1) ROUTINE (LIT_MAGIC_STRING_CLZ32, ECMA_MATH_OBJECT_CLZ32, 1, 1) ROUTINE (LIT_MAGIC_STRING_COSH, ECMA_MATH_OBJECT_COSH, 1, 1) ROUTINE (LIT_MAGIC_STRING_EXPM1, ECMA_MATH_OBJECT_EXPM1, 1, 1) ROUTINE (LIT_MAGIC_STRING_FROUND, ECMA_MATH_OBJECT_FROUND, 1, 1) ROUTINE (LIT_MAGIC_STRING_HYPOT, ECMA_MATH_OBJECT_HYPOT, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_IMUL, ECMA_MATH_OBJECT_IMUL, 2, 2) ROUTINE (LIT_MAGIC_STRING_LOG1P, ECMA_MATH_OBJECT_LOG1P, 1, 1) ROUTINE (LIT_MAGIC_STRING_LOG10, ECMA_MATH_OBJECT_LOG10, 1, 1) ROUTINE (LIT_MAGIC_STRING_LOG2, ECMA_MATH_OBJECT_LOG2, 1, 1) ROUTINE (LIT_MAGIC_STRING_SIGN, ECMA_MATH_OBJECT_SIGN, 1, 1) ROUTINE (LIT_MAGIC_STRING_SINH, ECMA_MATH_OBJECT_SINH, 1, 1) ROUTINE (LIT_MAGIC_STRING_TANH, ECMA_MATH_OBJECT_TANH, 1, 1) ROUTINE (LIT_MAGIC_STRING_TRUNC, ECMA_MATH_OBJECT_TRUNC, 1, 1) #endif /* JERRY_BUILTIN_MATH */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/huffman.cpp000664 001750 001750 00000015113 15164251010 035604 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Utilities for building and looking up Huffman trees. // // Author: Urvang Joshi (urvang@google.com) #include #include "tvgCommon.h" #include "./huffman.h" #include "../utils/utils.h" #include "../webp/format_constants.h" // Huffman data read via DecodeImageStream is represented in two (red and green) // bytes. #define MAX_HTREE_GROUPS 0x10000 HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups) { HTreeGroup* const htree_groups = tvg::malloc(num_htree_groups * sizeof(*htree_groups)); if (htree_groups == NULL) { return NULL; } assert(num_htree_groups <= MAX_HTREE_GROUPS); return htree_groups; } void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups) { if (htree_groups != NULL) { tvg::free(htree_groups); } } // Returns reverse(reverse(key, len) + 1, len), where reverse(key, len) is the // bit-wise reversal of the len least significant bits of key. static WEBP_INLINE uint32_t GetNextKey(uint32_t key, int len) { uint32_t step = 1 << (len - 1); while (key & step) { step >>= 1; } return (key & (step - 1)) + step; } // Stores code in table[0], table[step], table[2*step], ..., table[end]. // Assumes that end is an integer multiple of step. static WEBP_INLINE void ReplicateValue(HuffmanCode* table, int step, int end, HuffmanCode code) { assert(end % step == 0); do { end -= step; table[end] = code; } while (end > 0); } // Returns the table width of the next 2nd level table. count is the histogram // of bit lengths for the remaining symbols, len is the code length of the next // processed symbol static WEBP_INLINE int NextTableBitSize(const int* const count, int len, int root_bits) { int left = 1 << (len - root_bits); while (len < MAX_ALLOWED_CODE_LENGTH) { left -= count[len]; if (left <= 0) break; ++len; left <<= 1; } return len - root_bits; } int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits, const int code_lengths[], int code_lengths_size) { HuffmanCode* table = root_table; // next available space in table int total_size = 1 << root_bits; // total size root table + 2nd level table int* sorted = NULL; // symbols sorted by code length int len; // current code length int symbol; // symbol index in original or sorted table // number of codes of each length: int count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 }; // offsets in sorted table for each length: int offset[MAX_ALLOWED_CODE_LENGTH + 1]; assert(code_lengths_size != 0); assert(code_lengths != NULL); assert(root_table != NULL); assert(root_bits > 0); // Build histogram of code lengths. for (symbol = 0; symbol < code_lengths_size; ++symbol) { if (code_lengths[symbol] > MAX_ALLOWED_CODE_LENGTH) { return 0; } ++count[code_lengths[symbol]]; } // Error, all code lengths are zeros. if (count[0] == code_lengths_size) { return 0; } // Generate offsets into sorted symbol table by code length. offset[1] = 0; for (len = 1; len < MAX_ALLOWED_CODE_LENGTH; ++len) { if (count[len] > (1 << len)) { return 0; } offset[len + 1] = offset[len] + count[len]; } sorted = tvg::malloc(code_lengths_size * sizeof(*sorted)); if (sorted == NULL) { return 0; } // Sort symbols by length, by symbol order within each length. for (symbol = 0; symbol < code_lengths_size; ++symbol) { const int symbol_code_length = code_lengths[symbol]; if (code_lengths[symbol] > 0) { sorted[offset[symbol_code_length]++] = symbol; } } // Special case code with only one value. if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) { HuffmanCode code; code.bits = 0; code.value = (uint16_t)sorted[0]; ReplicateValue(table, 1, total_size, code); tvg::free(sorted); return total_size; } { int step; // step size to replicate values in current table uint32_t low = -1; // low bits for current root entry uint32_t mask = total_size - 1; // mask for low bits uint32_t key = 0; // reversed prefix code int num_nodes = 1; // number of Huffman tree nodes int num_open = 1; // number of open branches in current tree level int table_bits = root_bits; // key length of current table int table_size = 1 << table_bits; // size of current table symbol = 0; // Fill in root table. for (len = 1, step = 2; len <= root_bits; ++len, step <<= 1) { num_open <<= 1; num_nodes += num_open; num_open -= count[len]; if (num_open < 0) { tvg::free(sorted); return 0; } for (; count[len] > 0; --count[len]) { HuffmanCode code; code.bits = (uint8_t)len; code.value = (uint16_t)sorted[symbol++]; ReplicateValue(&table[key], step, table_size, code); key = GetNextKey(key, len); } } // Fill in 2nd level tables and add pointers to root table. for (len = root_bits + 1, step = 2; len <= MAX_ALLOWED_CODE_LENGTH; ++len, step <<= 1) { num_open <<= 1; num_nodes += num_open; num_open -= count[len]; if (num_open < 0) { tvg::free(sorted); return 0; } for (; count[len] > 0; --count[len]) { HuffmanCode code; if ((key & mask) != low) { table += table_size; table_bits = NextTableBitSize(count, len, root_bits); table_size = 1 << table_bits; total_size += table_size; low = key & mask; root_table[low].bits = (uint8_t)(table_bits + root_bits); root_table[low].value = (uint16_t)((table - root_table) - low); } code.bits = (uint8_t)(len - root_bits); code.value = (uint16_t)sorted[symbol++]; ReplicateValue(&table[key >> root_bits], step, table_size, code); key = GetNextKey(key, len); } } // Check if tree is full. if (num_nodes != 2 * offset[MAX_ALLOWED_CODE_LENGTH] - 1) { tvg::free(sorted); return 0; } } tvg::free(sorted); return total_size; } src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakref-prototype.inc.h000664 001750 001750 00000002513 15164251010 052301 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * WeakSet.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_WEAKREF /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_WEAKREF, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_WEAKREF_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_DEREF, ECMA_BUILTIN_WEAKREF_PROTOTYPE_OBJECT_DEREF, 0, 0) #endif /* JERRY_BUILTIN_WEAKREF */ #include "ecma-builtin-helpers-macro-undefs.inc.h" glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/meson.build000664 001750 001750 00000000213 15164251010 041455 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modulessubdir('api') subdir('include') subdir('ecma') subdir('jcontext') subdir('jmem') subdir('jrt') subdir('lit') subdir('parser') subdir('vm') mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/bindings/capi/tvgCapi.cpp000664 001750 001750 00000114541 15164251010 034624 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include #include #include "thorvg_capi.h" #ifdef THORVG_LOTTIE_LOADER_SUPPORT #include #endif using namespace std; using namespace tvg; #ifdef __cplusplus extern "C" { #endif /************************************************************************/ /* Engine API */ /************************************************************************/ TVG_API Tvg_Result tvg_engine_init(unsigned threads) { return (Tvg_Result) Initializer::init(threads); } TVG_API Tvg_Result tvg_engine_term() { return (Tvg_Result) Initializer::term(); } TVG_API Tvg_Result tvg_engine_version(uint32_t* major, uint32_t* minor, uint32_t* micro, const char** version) { if (version) *version = Initializer::version(major, minor, micro); return TVG_RESULT_SUCCESS; } /************************************************************************/ /* Canvas API */ /************************************************************************/ TVG_API Tvg_Canvas tvg_swcanvas_create(Tvg_Engine_Option op) { return (Tvg_Canvas) SwCanvas::gen(static_cast(op)); } TVG_API Tvg_Canvas tvg_glcanvas_create(Tvg_Engine_Option op) { return (Tvg_Canvas) GlCanvas::gen(static_cast(op)); } TVG_API Tvg_Canvas tvg_wgcanvas_create(Tvg_Engine_Option op) { return (Tvg_Canvas) WgCanvas::gen(static_cast(op)); } TVG_API Tvg_Result tvg_canvas_destroy(Tvg_Canvas canvas) { if (canvas) { delete(reinterpret_cast(canvas)); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_swcanvas_set_target(Tvg_Canvas canvas, uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Tvg_Colorspace cs) { if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->target(buffer, stride, w, h, static_cast(cs)); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_glcanvas_set_target(Tvg_Canvas canvas, void* display, void* surface, void* context, int32_t id, uint32_t w, uint32_t h, Tvg_Colorspace cs) { if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->target(display, surface, context, id, w, h, static_cast(cs)); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_wgcanvas_set_target(Tvg_Canvas canvas, void* device, void* instance, void* target, uint32_t w, uint32_t h, Tvg_Colorspace cs, int type) { if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->target(device, instance, target, w, h, static_cast(cs), type); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_canvas_add(Tvg_Canvas canvas, Tvg_Paint paint) { if (canvas && paint) return (Tvg_Result) reinterpret_cast(canvas)->add((Paint*)paint); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_canvas_insert(Tvg_Canvas canvas, Tvg_Paint target, Tvg_Paint at) { if (canvas && target && at) return (Tvg_Result) reinterpret_cast(canvas)->add((Paint*)target, (Paint*) at); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_canvas_remove(Tvg_Canvas canvas, Tvg_Paint paint) { if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->remove((Paint*) paint); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_canvas_update(Tvg_Canvas canvas) { if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->update(); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_canvas_draw(Tvg_Canvas canvas, bool clear) { if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->draw(clear); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_canvas_sync(Tvg_Canvas canvas) { if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->sync(); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_canvas_set_viewport(Tvg_Canvas canvas, int32_t x, int32_t y, int32_t w, int32_t h) { if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->viewport(x, y, w, h); return TVG_RESULT_INVALID_ARGUMENT; } /************************************************************************/ /* Paint API */ /************************************************************************/ TVG_API Tvg_Paint tvg_paint_get_parent(const Tvg_Paint paint) { return (Tvg_Paint) reinterpret_cast(paint)->parent(); } TVG_API Tvg_Result tvg_paint_rel(Tvg_Paint paint) { Paint::rel(reinterpret_cast(paint)); return TVG_RESULT_SUCCESS; } TVG_API Tvg_Result tvg_paint_set_visible(Tvg_Paint paint, bool visible) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->visible(visible); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API bool tvg_paint_get_visible(const Tvg_Paint paint) { if (paint) return reinterpret_cast(paint)->visible(); return false; } TVG_API uint16_t tvg_paint_ref(Tvg_Paint paint) { if (paint) return reinterpret_cast(paint)->ref(); return 0; } TVG_API uint16_t tvg_paint_unref(Tvg_Paint paint, bool free) { if (paint) return reinterpret_cast(paint)->unref(free); return 0; } TVG_API uint16_t tvg_paint_get_ref(const Tvg_Paint paint) { if (paint) return reinterpret_cast(paint)->refCnt(); return 0; } TVG_API Tvg_Result tvg_paint_scale(Tvg_Paint paint, float factor) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->scale(factor); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_rotate(Tvg_Paint paint, float degree) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->rotate(degree); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_translate(Tvg_Paint paint, float x, float y) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->translate(x, y); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_set_transform(Tvg_Paint paint, const Tvg_Matrix* m) { if (paint && m) return (Tvg_Result) reinterpret_cast(paint)->transform(*(reinterpret_cast(m))); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_get_transform(Tvg_Paint paint, Tvg_Matrix* m) { if (paint && m) { *reinterpret_cast(m) = reinterpret_cast(paint)->transform(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Paint tvg_paint_duplicate(Tvg_Paint paint) { if (paint) return (Tvg_Paint) reinterpret_cast(paint)->duplicate(); return nullptr; } TVG_API bool tvg_paint_intersects(Tvg_Paint paint, int32_t x, int32_t y, int32_t w, int32_t h) { if (paint) return reinterpret_cast(paint)->intersects(x, y, w, h); return false; } TVG_API Tvg_Result tvg_paint_set_opacity(Tvg_Paint paint, uint8_t opacity) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->opacity(opacity); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_get_opacity(const Tvg_Paint paint, uint8_t* opacity) { if (paint && opacity) { *opacity = reinterpret_cast(paint)->opacity(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_get_aabb(Tvg_Paint paint, float* x, float* y, float* w, float* h) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->bounds(x, y, w, h); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_get_obb(Tvg_Paint paint, Tvg_Point* pt4) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->bounds((Point*)pt4); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_set_mask_method(Tvg_Paint paint, Tvg_Paint target, Tvg_Mask_Method method) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->mask((Paint*)target, (MaskMethod)method); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint paint, const Tvg_Paint target, Tvg_Mask_Method* method) { if (paint && target && method) { *reinterpret_cast(method) = reinterpret_cast(paint)->mask(reinterpret_cast(target)); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_set_blend_method(Tvg_Paint paint, Tvg_Blend_Method method) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->blend((BlendMethod)method); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_get_type(const Tvg_Paint paint, Tvg_Type* type) { if (paint && type) { *type = static_cast(reinterpret_cast(paint)->type()); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_paint_set_clip(Tvg_Paint paint, Tvg_Paint clipper) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->clip((Shape*)(clipper)); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Paint tvg_paint_get_clip(const Tvg_Paint paint) { if (paint) return (Tvg_Paint) reinterpret_cast(paint)->clip(); return nullptr; } /************************************************************************/ /* Shape API */ /************************************************************************/ TVG_API Tvg_Paint tvg_shape_new() { return (Tvg_Paint) Shape::gen(); } TVG_API Tvg_Result tvg_shape_reset(Tvg_Paint paint) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->reset(); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_move_to(Tvg_Paint paint, float x, float y) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->moveTo(x, y); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_line_to(Tvg_Paint paint, float x, float y) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->lineTo(x, y); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_cubic_to(Tvg_Paint paint, float cx1, float cy1, float cx2, float cy2, float x, float y) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->cubicTo(cx1, cy1, cx2, cy2, x, y); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_close(Tvg_Paint paint) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->close(); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint paint, float x, float y, float w, float h, float rx, float ry, bool cw) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->appendRect(x, y, w, h, rx, ry, cw); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint paint, float cx, float cy, float rx, float ry, bool cw) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->appendCircle(cx, cy, rx, ry, cw); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_append_path(Tvg_Paint paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->appendPath((const PathCommand*)cmds, cmdCnt, (const Point*)pts, ptsCnt); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_path(const Tvg_Paint paint, const Tvg_Path_Command** cmds, uint32_t* cmdsCnt, const Tvg_Point** pts, uint32_t* ptsCnt) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->path((const PathCommand**)cmds, cmdsCnt, (const Point**)pts, ptsCnt); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint paint, float width) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->strokeWidth(width); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_stroke_width(const Tvg_Paint paint, float* width) { if (paint && width) { *width = reinterpret_cast(paint)->strokeWidth(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->strokeFill(r, g, b, a); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_stroke_color(const Tvg_Paint paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->strokeFill(r, g, b, a); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_stroke_gradient(Tvg_Paint paint, Tvg_Gradient gradient) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->strokeFill((Fill*)(gradient)); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_stroke_gradient(const Tvg_Paint paint, Tvg_Gradient* gradient) { if (paint && gradient) { *gradient = (Tvg_Gradient)(reinterpret_cast(paint)->strokeFill()); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint paint, const float* dashPattern, uint32_t cnt, float offset) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->strokeDash(dashPattern, cnt, offset); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_stroke_dash(const Tvg_Paint paint, const float** dashPattern, uint32_t* cnt, float* offset) { if (paint) { *cnt = reinterpret_cast(paint)->strokeDash(dashPattern, offset); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_stroke_cap(Tvg_Paint paint, Tvg_Stroke_Cap cap) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->strokeCap((StrokeCap)cap); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_stroke_cap(const Tvg_Paint paint, Tvg_Stroke_Cap* cap) { if (paint && cap) { *cap = (Tvg_Stroke_Cap) reinterpret_cast(paint)->strokeCap(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint paint, Tvg_Stroke_Join join) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->strokeJoin((StrokeJoin)join); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_stroke_join(const Tvg_Paint paint, Tvg_Stroke_Join* join) { if (paint && join) { *join = (Tvg_Stroke_Join) reinterpret_cast(paint)->strokeJoin(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_stroke_miterlimit(Tvg_Paint paint, float ml) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->strokeMiterlimit(ml); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_stroke_miterlimit(const Tvg_Paint paint, float* ml) { if (paint && ml) { *ml = reinterpret_cast(paint)->strokeMiterlimit(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_trimpath(Tvg_Paint paint, float begin, float end, bool simultaneous) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->trimpath(begin, end, simultaneous); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_fill_color(Tvg_Paint paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->fill(r, g, b, a); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_fill_color(const Tvg_Paint paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->fill(r, g, b, a); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_fill_rule(Tvg_Paint paint, Tvg_Fill_Rule rule) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->fillRule((FillRule)rule); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_fill_rule(const Tvg_Paint paint, Tvg_Fill_Rule* rule) { if (paint && rule) { *rule = (Tvg_Fill_Rule) reinterpret_cast(paint)->fillRule(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_paint_order(Tvg_Paint paint, bool strokeFirst) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->order(strokeFirst); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_set_gradient(Tvg_Paint paint, Tvg_Gradient gradient) { if (paint) return (Tvg_Result) reinterpret_cast(paint)->fill((Fill*)gradient); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_shape_get_gradient(const Tvg_Paint paint, Tvg_Gradient* gradient) { if (paint && gradient) { *gradient = (Tvg_Gradient)(reinterpret_cast(paint)->fill()); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } /************************************************************************/ /* Picture API */ /************************************************************************/ TVG_API Tvg_Paint tvg_picture_new() { return (Tvg_Paint) Picture::gen(); } TVG_API Tvg_Result tvg_picture_load(Tvg_Paint picture, const char* path) { if (picture) return (Tvg_Result) reinterpret_cast(picture)->load(path); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_picture_load_raw(Tvg_Paint picture, const uint32_t *data, uint32_t w, uint32_t h, Tvg_Colorspace cs, bool copy) { if (picture) return (Tvg_Result) reinterpret_cast(picture)->load(data, w, h, static_cast(cs), copy); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_picture_load_data(Tvg_Paint picture, const char *data, uint32_t size, const char *mimetype, const char* rpath, bool copy) { if (picture) return (Tvg_Result) reinterpret_cast(picture)->load(data, size, mimetype ? mimetype : "", rpath ? rpath : "", copy); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_picture_set_asset_resolver(Tvg_Paint picture, Tvg_Picture_Asset_Resolver resolver, void* data) { if (picture) return (Tvg_Result) reinterpret_cast(picture)->resolver([resolver](Paint* paint, const char* src, void* data) -> bool { return resolver(reinterpret_cast(paint), src, data); }, data); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_picture_set_size(Tvg_Paint picture, float w, float h) { if (picture) return (Tvg_Result) reinterpret_cast(picture)->size(w, h); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_picture_get_size(const Tvg_Paint picture, float* w, float* h) { if (picture) return (Tvg_Result) reinterpret_cast(picture)->size(w, h); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API const Tvg_Paint tvg_picture_get_paint(Tvg_Paint picture, uint32_t id) { if (picture) return (Tvg_Paint) reinterpret_cast(picture)->paint(id); return nullptr; } TVG_API Tvg_Result tvg_picture_set_origin(Tvg_Paint picture, float x, float y) { if (picture) return (Tvg_Result) reinterpret_cast(picture)->origin(x, y); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_picture_get_origin(const Tvg_Paint picture, float* x, float* y) { if (picture) return (Tvg_Result) reinterpret_cast(picture)->origin(x, y); return TVG_RESULT_INVALID_ARGUMENT; } /************************************************************************/ /* Gradient API */ /************************************************************************/ TVG_API Tvg_Gradient tvg_linear_gradient_new() { return (Tvg_Gradient)LinearGradient::gen(); } TVG_API Tvg_Gradient tvg_radial_gradient_new() { return (Tvg_Gradient)RadialGradient::gen(); } TVG_API Tvg_Gradient tvg_gradient_duplicate(Tvg_Gradient grad) { if (grad) return (Tvg_Gradient) reinterpret_cast(grad)->duplicate(); return nullptr; } TVG_API Tvg_Result tvg_gradient_del(Tvg_Gradient grad) { if (grad) { delete(reinterpret_cast(grad)); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_linear_gradient_set(Tvg_Gradient grad, float x1, float y1, float x2, float y2) { if (grad) return (Tvg_Result) reinterpret_cast(grad)->linear(x1, y1, x2, y2); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_linear_gradient_get(Tvg_Gradient grad, float* x1, float* y1, float* x2, float* y2) { if (grad) return (Tvg_Result) reinterpret_cast(grad)->linear(x1, y1, x2, y2); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_radial_gradient_set(Tvg_Gradient grad, float cx, float cy, float r, float fx, float fy, float fr) { if (grad) return (Tvg_Result) reinterpret_cast(grad)->radial(cx, cy, r, fx, fy, fr); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_radial_gradient_get(Tvg_Gradient grad, float* cx, float* cy, float* r, float* fx, float* fy, float* fr) { if (grad) return (Tvg_Result) reinterpret_cast(grad)->radial(cx, cy, r, fx, fy, fr); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_gradient_set_color_stops(Tvg_Gradient grad, const Tvg_Color_Stop* color_stop, uint32_t cnt) { if (grad) return (Tvg_Result) reinterpret_cast(grad)->colorStops(reinterpret_cast(color_stop), cnt); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_gradient_get_color_stops(const Tvg_Gradient grad, const Tvg_Color_Stop** color_stop, uint32_t* cnt) { if (grad && color_stop && cnt) { *cnt = reinterpret_cast(grad)->colorStops(reinterpret_cast(color_stop)); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_gradient_set_spread(Tvg_Gradient grad, const Tvg_Stroke_Fill spread) { if (grad) return (Tvg_Result) reinterpret_cast(grad)->spread((FillSpread)spread); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_gradient_get_spread(const Tvg_Gradient grad, Tvg_Stroke_Fill* spread) { if (grad && spread) { *spread = (Tvg_Stroke_Fill) reinterpret_cast(grad)->spread(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_gradient_set_transform(Tvg_Gradient grad, const Tvg_Matrix* m) { if (grad && m) return (Tvg_Result) reinterpret_cast(grad)->transform(*(reinterpret_cast(m))); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_gradient_get_transform(const Tvg_Gradient grad, Tvg_Matrix* m) { if (grad && m) { *reinterpret_cast(m) = reinterpret_cast(const_cast(grad))->transform(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_gradient_get_type(const Tvg_Gradient grad, Tvg_Type* type) { if (grad && type) { *type = static_cast(reinterpret_cast(grad)->type()); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } /************************************************************************/ /* Scene API */ /************************************************************************/ TVG_API Tvg_Paint tvg_scene_new() { return (Tvg_Paint) Scene::gen(); } TVG_API Tvg_Result tvg_scene_add(Tvg_Paint scene, Tvg_Paint paint) { if (scene && paint) return (Tvg_Result) reinterpret_cast(scene)->add((Paint*)paint); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_scene_insert(Tvg_Paint scene, Tvg_Paint paint, Tvg_Paint at) { if (scene && paint && at) return (Tvg_Result) reinterpret_cast(scene)->add((Paint*)paint, (Paint*)at); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_scene_remove(Tvg_Paint scene, Tvg_Paint paint) { if (scene) return (Tvg_Result) reinterpret_cast(scene)->remove((Paint*)paint); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_scene_clear_effects(Tvg_Paint scene) { if (scene) return (Tvg_Result) reinterpret_cast(scene)->add(SceneEffect::Clear); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_scene_add_effect_drop_shadow(Tvg_Paint scene, int r, int g, int b, int a, double angle, double distance, double sigma, int quality) { if (scene) return (Tvg_Result) reinterpret_cast(scene)->add(SceneEffect::DropShadow, r, g, b, a, angle, distance, sigma, quality); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_scene_add_effect_gaussian_blur(Tvg_Paint scene, double sigma, int direction, int border, int quality) { if (scene) return (Tvg_Result) reinterpret_cast(scene)->add(SceneEffect::GaussianBlur, sigma, direction, border, quality); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_scene_add_effect_fill(Tvg_Paint scene, int r, int g, int b, int a) { if (scene) return (Tvg_Result) reinterpret_cast(scene)->add(SceneEffect::Fill, r, g, b, a); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_scene_add_effect_tint(Tvg_Paint scene, int black_r, int black_g, int black_b, int white_r, int white_g, int white_b, double intensity) { if (scene) return (Tvg_Result) reinterpret_cast(scene)->add(SceneEffect::Tint, black_r, black_g, black_b, white_r, white_g, white_b, intensity); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_scene_add_effect_tritone(Tvg_Paint scene, int shadow_r, int shadow_g, int shadow_b, int midtone_r, int midtone_g, int midtone_b, int highlight_r, int highlight_g, int highlight_b, int blend) { if (scene) return (Tvg_Result) reinterpret_cast(scene)->add(SceneEffect::Tritone, shadow_r, shadow_g, shadow_b, midtone_r, midtone_g, midtone_b, highlight_r, highlight_g, highlight_b, blend); return TVG_RESULT_INVALID_ARGUMENT; } /************************************************************************/ /* Text API */ /************************************************************************/ TVG_API Tvg_Paint tvg_text_new() { return (Tvg_Paint)Text::gen(); } TVG_API Tvg_Result tvg_text_set_font(Tvg_Paint text, const char* name) { if (text) return (Tvg_Result) reinterpret_cast(text)->font(name); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_set_size(Tvg_Paint text, float size) { if (text) return (Tvg_Result) reinterpret_cast(text)->size(size); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_set_text(Tvg_Paint text, const char* utf8) { if (text) return (Tvg_Result) reinterpret_cast(text)->text(utf8); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_align(Tvg_Paint text, float x, float y) { if (text) return (Tvg_Result) reinterpret_cast(text)->align(x, y); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_layout(Tvg_Paint text, float w, float h) { if (text) return (Tvg_Result) reinterpret_cast(text)->layout(w, h); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_set_outline(Tvg_Paint text, float width, uint8_t r, uint8_t g, uint8_t b) { if (text) return (Tvg_Result) reinterpret_cast(text)->outline(width, r, g, b); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_set_color(Tvg_Paint text, uint8_t r, uint8_t g, uint8_t b) { if (text) return (Tvg_Result) reinterpret_cast(text)->fill(r, g, b); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_set_italic(Tvg_Paint text, float shear) { if (text) return (Tvg_Result) reinterpret_cast(text)->italic(shear); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_set_gradient(Tvg_Paint text, Tvg_Gradient gradient) { if (text) return (Tvg_Result) reinterpret_cast(text)->fill((Fill*)(gradient)); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_wrap_mode(Tvg_Paint text, Tvg_Text_Wrap mode) { if (text) return (Tvg_Result) reinterpret_cast(text)->wrap(TextWrap(mode)); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_spacing(Tvg_Paint text, float letter, float line) { if (text) return (Tvg_Result) reinterpret_cast(text)->spacing(letter, line); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_text_get_metrics(const Tvg_Paint text, Tvg_Text_Metrics* metrics) { if (text && metrics) return (Tvg_Result) reinterpret_cast(text)->metrics(*reinterpret_cast(metrics)); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_font_load(const char* path) { return (Tvg_Result) Text::load(path); } TVG_API Tvg_Result tvg_font_load_data(const char* name, const char* data, uint32_t size, const char *mimetype, bool copy) { return (Tvg_Result) Text::load(name, data, size, mimetype ? mimetype : "", copy); } TVG_API Tvg_Result tvg_font_unload(const char* path) { return (Tvg_Result) Text::unload(path); } /************************************************************************/ /* Saver API */ /************************************************************************/ TVG_API Tvg_Saver tvg_saver_new() { return (Tvg_Saver) Saver::gen(); } TVG_API Tvg_Result tvg_saver_save_paint(Tvg_Saver saver, Tvg_Paint paint, const char* path, uint32_t quality) { if (saver) return (Tvg_Result) reinterpret_cast(saver)->save((Paint*)paint, path, quality); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_saver_save_animation(Tvg_Saver saver, Tvg_Animation animation, const char* path, uint32_t quality, uint32_t fps) { if (saver) return (Tvg_Result) reinterpret_cast(saver)->save((Animation*)animation, path, quality, fps); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_saver_sync(Tvg_Saver saver) { if (saver) return (Tvg_Result) reinterpret_cast(saver)->sync(); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_saver_del(Tvg_Saver saver) { if (saver) { delete(reinterpret_cast(saver)); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } /************************************************************************/ /* Animation API */ /************************************************************************/ TVG_API Tvg_Animation tvg_animation_new() { return (Tvg_Animation) Animation::gen(); } TVG_API Tvg_Result tvg_animation_set_frame(Tvg_Animation animation, float no) { if (animation) return (Tvg_Result) reinterpret_cast(animation)->frame(no); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation animation, float* no) { if (animation && no) { *no = reinterpret_cast(animation)->curFrame(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_animation_get_total_frame(Tvg_Animation animation, float* cnt) { if (animation && cnt) { *cnt = reinterpret_cast(animation)->totalFrame(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Paint tvg_animation_get_picture(Tvg_Animation animation) { if (animation) return (Tvg_Paint) reinterpret_cast(animation)->picture(); return nullptr; } TVG_API Tvg_Result tvg_animation_get_duration(Tvg_Animation animation, float* duration) { if (animation && duration) { *duration = reinterpret_cast(animation)->duration(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_animation_set_segment(Tvg_Animation animation, float start, float end) { if (animation) return (Tvg_Result) reinterpret_cast(animation)->segment(start, end); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_animation_get_segment(Tvg_Animation animation, float* start, float* end) { if (animation) return (Tvg_Result) reinterpret_cast(animation)->segment(start, end); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_animation_del(Tvg_Animation animation) { if (animation) { delete(reinterpret_cast(animation)); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } /************************************************************************/ /* Accessor API */ /************************************************************************/ TVG_API Tvg_Accessor tvg_accessor_new() { return (Tvg_Accessor) Accessor::gen(); } TVG_API Tvg_Result tvg_accessor_del(Tvg_Accessor accessor) { if (accessor) { delete(reinterpret_cast(accessor)); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; } TVG_API Tvg_Result tvg_accessor_set(Tvg_Accessor accessor, Tvg_Paint paint, bool (*func)(Tvg_Paint paint, void* data), void* data) { if (accessor) return (Tvg_Result) reinterpret_cast(accessor)->set(static_cast(reinterpret_cast(paint)), [func](const Paint* paint, void* data) { return func((Tvg_Paint) paint, data); }, data); return TVG_RESULT_INVALID_ARGUMENT; } TVG_API uint32_t tvg_accessor_generate_id(const char* name) { return Accessor::id(name); } /************************************************************************/ /* Lottie Animation API */ /************************************************************************/ TVG_API Tvg_Animation tvg_lottie_animation_new() { #ifdef THORVG_LOTTIE_LOADER_SUPPORT return (Tvg_Animation) LottieAnimation::gen(); #endif return nullptr; } TVG_API uint32_t tvg_lottie_animation_gen_slot(Tvg_Animation animation, const char* slot) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation) return reinterpret_cast(animation)->gen(slot); #endif return 0; } TVG_API Tvg_Result tvg_lottie_animation_apply_slot(Tvg_Animation animation, uint32_t id) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation) return (Tvg_Result) reinterpret_cast(animation)->apply(id); return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; } TVG_API Tvg_Result tvg_lottie_animation_del_slot(Tvg_Animation animation, uint32_t id) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation) return (Tvg_Result) reinterpret_cast(animation)->del(id); return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; } TVG_API Tvg_Result tvg_lottie_animation_set_marker(Tvg_Animation animation, const char* marker) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation) return (Tvg_Result) reinterpret_cast(animation)->segment(marker); return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; } TVG_API Tvg_Result tvg_lottie_animation_get_markers_cnt(Tvg_Animation animation, uint32_t* cnt) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation && cnt) { *cnt = reinterpret_cast(animation)->markersCnt(); return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; } TVG_API Tvg_Result tvg_lottie_animation_get_marker(Tvg_Animation animation, uint32_t idx, const char** name) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation && name) { *name = reinterpret_cast(animation)->marker(idx); if (!(*name)) return TVG_RESULT_INVALID_ARGUMENT; return TVG_RESULT_SUCCESS; } return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; } TVG_API Tvg_Result tvg_lottie_animation_tween(Tvg_Animation animation, float from, float to, float progress) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation) return (Tvg_Result) reinterpret_cast(animation)->tween(from, to, progress); return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; } TVG_API Tvg_Result tvg_lottie_animation_assign(Tvg_Animation animation, const char* layer, uint32_t ix, const char* var, float val) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation) return (Tvg_Result) reinterpret_cast(animation)->assign(layer, ix, var, val); return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; } TVG_API Tvg_Result tvg_lottie_animation_set_quality(Tvg_Animation animation, uint8_t value) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT if (animation) return (Tvg_Result) reinterpret_cast(animation)->quality(value); return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; } #ifdef __cplusplus } #endif thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-container-object.h000664 001750 001750 00000010323 15164251010 046710 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_CONTAINER_OBJECT_H #define ECMA_CONTAINER_OBJECT_H #include "ecma-builtins.h" #include "ecma-globals.h" #if JERRY_BUILTIN_CONTAINER /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmamaphelpers ECMA builtin map/set helper functions * @{ */ /** * List of built-in routine identifiers. */ enum { ECMA_CONTAINER_ROUTINE_START = 0, ECMA_CONTAINER_ROUTINE_ADD, ECMA_CONTAINER_ROUTINE_CLEAR, ECMA_CONTAINER_ROUTINE_DELETE_WEAK, ECMA_CONTAINER_ROUTINE_DELETE, ECMA_CONTAINER_ROUTINE_FOREACH, ECMA_CONTAINER_ROUTINE_HAS, ECMA_CONTAINER_ROUTINE_SIZE_GETTER, ECMA_CONTAINER_ROUTINE_GET, ECMA_CONTAINER_ROUTINE_SET, /* Note: These 3 routines MUST be in this order */ ECMA_CONTAINER_ROUTINE_KEYS, ECMA_CONTAINER_ROUTINE_VALUES, ECMA_CONTAINER_ROUTINE_ENTRIES }; uint8_t ecma_op_container_entry_size (lit_magic_string_id_t lit_id); ecma_extended_object_t *ecma_op_container_get_object (ecma_value_t this_arg, lit_magic_string_id_t lit_id); ecma_value_t ecma_op_container_create (const ecma_value_t *arguments_list_p, uint32_t arguments_list_len, lit_magic_string_id_t lit_id, ecma_builtin_id_t proto_id); ecma_value_t ecma_op_container_size (ecma_extended_object_t *map_object_p); ecma_value_t ecma_op_container_get (ecma_extended_object_t *map_object_p, ecma_value_t key_arg, lit_magic_string_id_t lit_id); ecma_value_t ecma_op_container_foreach (ecma_extended_object_t *map_object_p, ecma_value_t predicate, ecma_value_t predicate_this_arg, lit_magic_string_id_t lit_id); ecma_value_t ecma_op_container_has (ecma_extended_object_t *map_object_p, ecma_value_t key_arg, lit_magic_string_id_t lit_id); ecma_value_t ecma_op_container_set (ecma_extended_object_t *map_object_p, ecma_value_t key_arg, ecma_value_t value_arg, lit_magic_string_id_t lit_id); ecma_value_t ecma_op_container_clear (ecma_extended_object_t *map_object_p); ecma_value_t ecma_op_container_delete (ecma_extended_object_t *map_object_p, ecma_value_t key_arg, lit_magic_string_id_t lit_id); ecma_value_t ecma_op_container_delete_weak (ecma_extended_object_t *map_object_p, ecma_value_t key_arg, lit_magic_string_id_t lit_id); ecma_value_t ecma_op_container_find_weak_value (ecma_object_t *object_p, ecma_value_t key_arg); void ecma_op_container_remove_weak_entry (ecma_object_t *object_p, ecma_value_t key_arg); void ecma_op_container_free_entries (ecma_object_t *object_p); ecma_value_t ecma_op_container_create_iterator (ecma_value_t this_arg, ecma_builtin_id_t proto_id, ecma_object_class_type_t iterator_type, ecma_iterator_kind_t kind); ecma_value_t ecma_op_container_iterator_next (ecma_value_t this_val, ecma_object_class_type_t iterator_type); ecma_value_t ecma_builtin_container_dispatch_routine (uint16_t builtin_routine_id, ecma_value_t this_arg, const ecma_value_t arguments_list_p[], lit_magic_string_id_t lit_id); /** * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ #endif /* !ECMA_CONTAINER_OBJECT_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlCommon.h000664 001750 001750 00000012706 15164251010 036075 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_COMMON_H_ #define _TVG_GL_COMMON_H_ #include #include "tvgGl.h" #include "tvgRender.h" #include "tvgMath.h" constexpr float MIN_GL_STROKE_WIDTH = 1.0f; constexpr float MIN_GL_STROKE_ALPHA = 0.25f; constexpr uint32_t GL_MAT3_STD140_SIZE = 12; // mat3 is 3 vec4 columns in std140 constexpr uint32_t GL_MAT3_STD140_BYTES = GL_MAT3_STD140_SIZE * sizeof(float); // All GPU matrices use column major order. static inline void getMatrix3(const Matrix& mat3, float* matOut) { matOut[0] = mat3.e11; matOut[3] = mat3.e12; matOut[6] = mat3.e13; matOut[1] = mat3.e21; matOut[4] = mat3.e22; matOut[7] = mat3.e23; matOut[2] = mat3.e31; matOut[5] = mat3.e32; matOut[8] = mat3.e33; } // All GPU matrices use column major order. std140 mat3 packs each column into a vec4 stride. static inline void getMatrix3Std140(const Matrix& mat3, float* matOut) { matOut[0] = mat3.e11; matOut[4] = mat3.e12; matOut[8] = mat3.e13; matOut[1] = mat3.e21; matOut[5] = mat3.e22; matOut[9] = mat3.e23; matOut[2] = mat3.e31; matOut[6] = mat3.e32; matOut[10] = mat3.e33; matOut[3] = 0.0f; matOut[7] = 0.0f; matOut[11] = 0.0f; } enum class GlStencilMode { None, FillNonZero, FillEvenOdd, Stroke, }; class GlStageBuffer; class GlRenderTask; struct GlGeometryBuffer { Array vertex; Array index; void clear() { vertex.clear(); index.clear(); } }; struct GlGeometry { const Matrix* inverseMatrix() { if (!inverseMatrixDirty) return &cachedInverseMatrix; inverse(&matrix, &cachedInverseMatrix); inverseMatrixDirty = false; return &cachedInverseMatrix; } void setMatrix(const Matrix& tr) { matrix = tr; inverseMatrixDirty = true;} void prepare(const RenderShape& rshape); bool tesselateShape(const RenderShape& rshape, float* opacityMultiplier = nullptr); bool tesselateStroke(const RenderShape& rshape); bool tesselateLine(const RenderPath& path); void tesselateImage(const RenderSurface* image); bool draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdateFlag flag); GlStencilMode getStencilMode(RenderUpdateFlag flag); RenderRegion getBounds() const; GlGeometryBuffer fill, stroke; Matrix matrix = {}; RenderRegion viewport = {}; RenderRegion fillBounds = {}; RenderRegion strokeBounds = {}; FillRule fillRule = FillRule::NonZero; RenderPath optPath; //optimal path float strokeRenderWidth = 0.0f; bool fillWorld = false; bool convex; Matrix cachedInverseMatrix = {}; bool inverseMatrixDirty = true; }; struct GlShape { const RenderShape* rshape = nullptr; float viewWd; float viewHt; uint32_t opacity = 0; GLuint texId = 0; uint32_t texFlipY = 0; ColorSpace texColorSpace = ColorSpace::ABGR8888; GlGeometry geometry; Array clips; bool validFill = false; bool validStroke = false; }; struct GlIntersector { bool isPointInTriangle(const Point& p, const Point& a, const Point& b, const Point& c); bool isPointInImage(const Point& p, const GlGeometryBuffer& mesh, const Matrix& tr); bool isPointInTris(const Point& p, const GlGeometryBuffer& mesh, const Matrix& tr); bool isPointInMesh(const Point& p, const GlGeometryBuffer& mesh, const Matrix& tr); bool intersectClips(const Point& pt, const tvg::Array& clips); bool intersectShape(const RenderRegion region, const GlShape* shape); bool intersectImage(const RenderRegion region, const GlShape* image); }; #define MAX_GRADIENT_STOPS 16 struct GlLinearGradientBlock { alignas(16) float nStops[4] = {}; alignas(16) float startPos[2] = {}; alignas(8) float stopPos[2] = {}; alignas(8) float stopPoints[MAX_GRADIENT_STOPS] = {}; alignas(16) float stopColors[4 * MAX_GRADIENT_STOPS] = {}; }; struct GlRadialGradientBlock { alignas(16) float nStops[4] = {}; alignas(16) float centerPos[4] = {}; alignas(16) float radius[2] = {}; alignas(16) float stopPoints[MAX_GRADIENT_STOPS] = {}; alignas(16) float stopColors[4 * MAX_GRADIENT_STOPS] = {}; }; struct GlCompositor : RenderCompositor { RenderRegion bbox; CompositionFlag flags; BlendMethod blendMethod = BlendMethod::Normal; GlCompositor(const RenderRegion& box, CompositionFlag flags) : bbox(box), flags(flags) {} }; #endif /* _TVG_GL_COMMON_H_ */glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jrt/jrt-types.h000664 001750 001750 00000001416 15164251010 042232 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JRT_TYPES_H #define JRT_TYPES_H #include #include #include #include #endif /* !JRT_TYPES_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlProgram.cpp000664 001750 001750 00000011027 15164251010 036602 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlProgram.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ uint32_t GlProgram::mCurrentProgram = 0; /************************************************************************/ /* External Class Implementation */ /************************************************************************/ GlProgram::GlProgram(const char* vertSrc, const char* fragSrc) { auto shader = GlShader(vertSrc, fragSrc); // Create the program object uint32_t progObj = glCreateProgram(); assert(progObj); glAttachShader(progObj, shader.getVertexShader()); glAttachShader(progObj, shader.getFragmentShader()); // Link the program glLinkProgram(progObj); // Check the link status GLint linked; glGetProgramiv(progObj, GL_LINK_STATUS, &linked); if (!linked) { GLint infoLen = 0; glGetProgramiv(progObj, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 0) { auto infoLog = tvg::malloc(sizeof(char) * infoLen); glGetProgramInfoLog(progObj, infoLen, NULL, infoLog); TVGERR("GL_ENGINE", "Error linking shader: %s", infoLog); tvg::free(infoLog); } glDeleteProgram(progObj); progObj = 0; assert(0); } mProgramObj = progObj; } GlProgram::~GlProgram() { if (mCurrentProgram == mProgramObj) unload(); glDeleteProgram(mProgramObj); } void GlProgram::load() { if (mCurrentProgram == mProgramObj) return; mCurrentProgram = mProgramObj; GL_CHECK(glUseProgram(mProgramObj)); } void GlProgram::unload() { mCurrentProgram = 0; } int32_t GlProgram::getAttributeLocation(const char* name) { GL_CHECK(int32_t location = glGetAttribLocation(mCurrentProgram, name)); return location; } int32_t GlProgram::getUniformLocation(const char* name) { GL_CHECK(int32_t location = glGetUniformLocation(mProgramObj, name)); return location; } int32_t GlProgram::getUniformBlockIndex(const char* name) { GL_CHECK(int32_t index = glGetUniformBlockIndex(mProgramObj, name)); return index; } uint32_t GlProgram::getProgramId() { return mProgramObj; } void GlProgram::setUniform1Value(int32_t location, int count, const int* values) { GL_CHECK(glUniform1iv(location, count, values)); } void GlProgram::setUniform2Value(int32_t location, int count, const int* values) { GL_CHECK(glUniform2iv(location, count, values)); } void GlProgram::setUniform3Value(int32_t location, int count, const int* values) { GL_CHECK(glUniform3iv(location, count, values)); } void GlProgram::setUniform4Value(int32_t location, int count, const int* values) { GL_CHECK(glUniform4iv(location, count, values)); } void GlProgram::setUniform1Value(int32_t location, int count, const float* values) { GL_CHECK(glUniform1fv(location, count, values)); } void GlProgram::setUniform2Value(int32_t location, int count, const float* values) { GL_CHECK(glUniform2fv(location, count, values)); } void GlProgram::setUniform3Value(int32_t location, int count, const float* values) { GL_CHECK(glUniform3fv(location, count, values)); } void GlProgram::setUniform4Value(int32_t location, int count, const float* values) { GL_CHECK(glUniform4fv(location, count, values)); }glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/meson.build000664 001750 001750 00000001402 15164251010 043366 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'byte-code.h', 'common.h', 'js-lexer.h', 'js-parser-limits.h', 'js-parser.h', 'js-scanner.h', 'js-parser-internal.h', 'js-parser-tagged-template-literal.h', 'js-scanner-internal.h', 'parser-errors.h', 'parser-error-messages.inc.h', 'byte-code.cpp', 'common.cpp', 'js-lexer.cpp', 'js-parser.cpp', 'js-parser-expr.cpp', 'js-parser-mem.cpp', 'js-parser-module.cpp', 'js-parser-statm.cpp', 'js-parser-tagged-template-literal.cpp', 'js-parser-util.cpp', 'js-scanner.cpp', 'js-scanner-ops.cpp', 'js-scanner-util.cpp', 'parser-errors.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/png/tvgPngLoader.cpp000664 001750 001750 00000007240 15164251010 035324 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgLoader.h" #include "tvgPngLoader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ void PngLoader::run(unsigned tid) { auto width = static_cast(w); auto height = static_cast(h); state.info_raw.colortype = LCT_RGBA; //request this image format if (lodepng_decode(&surface.buf8, &width, &height, &state, data, size)) { TVGERR("PNG", "Failed to decode image"); } //setup the surface surface.stride = width; surface.w = width; surface.h = height; surface.cs = ColorSpace::ABGR8888S; surface.channelSize = sizeof(uint32_t); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ PngLoader::PngLoader() : ImageLoader(FileType::Png) { lodepng_state_init(&state); } PngLoader::~PngLoader() { done(); if (freeData) tvg::free(data); tvg::free(surface.buf8); lodepng_state_cleanup(&state); } bool PngLoader::open(const char* path) { #ifdef THORVG_FILE_IO_SUPPORT if (!(data = (unsigned char*)LoadModule::open(path, size))) return false; lodepng_state_init(&state); unsigned int width, height; if (lodepng_inspect(&width, &height, &state, data, size) > 0) return false; w = static_cast(width); h = static_cast(height); freeData = true; return true; #else return false; #endif } bool PngLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy) { unsigned int width, height; if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false; if (copy) { this->data = tvg::malloc(size); if (!this->data) return false; memcpy((unsigned char *)this->data, data, size); freeData = true; } else { this->data = (unsigned char *) data; freeData = false; } w = static_cast(width); h = static_cast(height); this->size = size; return true; } bool PngLoader::read() { if (!data || w == 0 || h == 0) return false; if (!LoadModule::read()) return true; TaskScheduler::request(this); return true; } RenderSurface* PngLoader::bitmap() { this->done(); return ImageLoader::bitmap(); }external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-module.cpp000664 001750 001750 00000037012 15164251010 045127 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "js-parser-internal.h" #if JERRY_MODULE_SYSTEM #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" #include "ecma-module.h" #include "jcontext.h" /** * Description of "*default*" literal string. */ const lexer_lit_location_t lexer_default_literal = { (const uint8_t *) "*default*", 9, LEXER_IDENT_LITERAL, LEXER_LIT_LOCATION_IS_ASCII }; /** * Check for duplicated imported binding names. * * @return true - if the given name is a duplicate * false - otherwise */ bool parser_module_check_duplicate_import (parser_context_t *context_p, /**< parser context */ ecma_string_t *local_name_p) /**< newly imported name */ { ecma_module_names_t *module_names_p = context_p->module_names_p; while (module_names_p != NULL) { if (ecma_compare_ecma_strings (module_names_p->local_name_p, local_name_p)) { return true; } module_names_p = module_names_p->next_p; } ecma_module_node_t *module_node_p = JERRY_CONTEXT (module_current_p)->imports_p; while (module_node_p != NULL) { module_names_p = module_node_p->module_names_p; while (module_names_p != NULL) { if (ecma_compare_ecma_strings (module_names_p->local_name_p, local_name_p)) { return true; } module_names_p = module_names_p->next_p; } module_node_p = module_node_p->next_p; } return false; } /* parser_module_check_duplicate_import */ /** * Append an identifier to the exported bindings. */ void parser_module_append_export_name (parser_context_t *context_p) /**< parser context */ { if (!(context_p->status_flags & PARSER_MODULE_STORE_IDENT)) { return; } context_p->module_identifier_lit_p = context_p->lit_object.literal_p; ecma_string_t *name_p = parser_new_ecma_string_from_literal (context_p->lit_object.literal_p); if (parser_module_check_duplicate_export (context_p, name_p)) { ecma_deref_ecma_string (name_p); parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); } parser_module_add_names_to_node (context_p, name_p, name_p); ecma_deref_ecma_string (name_p); } /* parser_module_append_export_name */ /** * Check for duplicated exported bindings. * @return - true - if the exported name is a duplicate * false - otherwise */ bool parser_module_check_duplicate_export (parser_context_t *context_p, /**< parser context */ ecma_string_t *export_name_p) /**< exported identifier */ { /* We have to check in the currently constructed node, as well as all of the already added nodes. */ ecma_module_names_t *current_names_p = context_p->module_names_p; while (current_names_p != NULL) { if (ecma_compare_ecma_strings (current_names_p->imex_name_p, export_name_p)) { return true; } current_names_p = current_names_p->next_p; } ecma_module_names_t *name_p = JERRY_CONTEXT (module_current_p)->local_exports_p; while (name_p != NULL) { if (ecma_compare_ecma_strings (name_p->imex_name_p, export_name_p)) { return true; } name_p = name_p->next_p; } ecma_module_node_t *export_node_p = JERRY_CONTEXT (module_current_p)->indirect_exports_p; while (export_node_p != NULL) { name_p = export_node_p->module_names_p; while (name_p != NULL) { if (ecma_compare_ecma_strings (name_p->imex_name_p, export_name_p)) { return true; } name_p = name_p->next_p; } export_node_p = export_node_p->next_p; } /* Star exports don't have any names associated with them, so no need to check those. */ return false; } /* parser_module_check_duplicate_export */ /** * Add module names to current module node. */ void parser_module_add_names_to_node (parser_context_t *context_p, /**< parser context */ ecma_string_t *imex_name_p, /**< import/export name */ ecma_string_t *local_name_p) /**< local name */ { ecma_module_names_t *new_name_p = (ecma_module_names_t *) parser_malloc (context_p, sizeof (ecma_module_names_t)); new_name_p->next_p = context_p->module_names_p; context_p->module_names_p = new_name_p; JERRY_ASSERT (imex_name_p != NULL); ecma_ref_ecma_string (imex_name_p); new_name_p->imex_name_p = imex_name_p; JERRY_ASSERT (local_name_p != NULL); ecma_ref_ecma_string (local_name_p); new_name_p->local_name_p = local_name_p; } /* parser_module_add_names_to_node */ /** * Parse an ExportClause. */ void parser_module_parse_export_clause (parser_context_t *context_p) /**< parser context */ { bool has_module_specifier = false; if (context_p->source_p == context_p->next_scanner_info_p->source_p) { has_module_specifier = true; JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_EXPORT_MODULE_SPECIFIER); scanner_release_next (context_p, sizeof (scanner_info_t)); } JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); lexer_next_token (context_p); while (true) { if (context_p->token.type == LEXER_RIGHT_BRACE) { lexer_next_token (context_p); break; } /* 15.2.3.1 The referenced binding cannot be a reserved word. */ if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL || context_p->token.keyword_type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } ecma_string_t *export_name_p = NULL; ecma_string_t *local_name_p = NULL; lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); if (!has_module_specifier && !scanner_literal_exists (context_p, context_p->lit_object.index)) { parser_raise_error (context_p, PARSER_ERR_EXPORT_NOT_DEFINED); } uint16_t local_name_index = context_p->lit_object.index; uint16_t export_name_index = PARSER_MAXIMUM_NUMBER_OF_LITERALS; lexer_next_token (context_p); if (lexer_token_is_identifier (context_p, "as", 2)) { lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); export_name_index = context_p->lit_object.index; lexer_next_token (context_p); } local_name_p = parser_new_ecma_string_from_literal (PARSER_GET_LITERAL (local_name_index)); if (export_name_index != PARSER_MAXIMUM_NUMBER_OF_LITERALS) { export_name_p = parser_new_ecma_string_from_literal (PARSER_GET_LITERAL (export_name_index)); } else { export_name_p = local_name_p; ecma_ref_ecma_string (local_name_p); } if (parser_module_check_duplicate_export (context_p, export_name_p)) { ecma_deref_ecma_string (local_name_p); ecma_deref_ecma_string (export_name_p); parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); } parser_module_add_names_to_node (context_p, export_name_p, local_name_p); ecma_deref_ecma_string (local_name_p); ecma_deref_ecma_string (export_name_p); if (context_p->token.type != LEXER_COMMA && context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_COMMA_EXPECTED); } else if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); } if (lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } } } /* parser_module_parse_export_clause */ /** * Parse an ImportClause */ void parser_module_parse_import_clause (parser_context_t *context_p) /**< parser context */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); lexer_next_token (context_p); while (true) { if (context_p->token.type == LEXER_RIGHT_BRACE) { lexer_next_token (context_p); break; } if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } ecma_string_t *import_name_p = NULL; ecma_string_t *local_name_p = NULL; lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); uint16_t import_name_index = context_p->lit_object.index; uint16_t local_name_index = PARSER_MAXIMUM_NUMBER_OF_LITERALS; lexer_next_token (context_p); if (lexer_token_is_identifier (context_p, "as", 2)) { lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); local_name_index = context_p->lit_object.index; lexer_next_token (context_p); } import_name_p = parser_new_ecma_string_from_literal (PARSER_GET_LITERAL (import_name_index)); if (local_name_index != PARSER_MAXIMUM_NUMBER_OF_LITERALS) { local_name_p = parser_new_ecma_string_from_literal (PARSER_GET_LITERAL (local_name_index)); } else { local_name_p = import_name_p; ecma_ref_ecma_string (local_name_p); } if (parser_module_check_duplicate_import (context_p, local_name_p)) { ecma_deref_ecma_string (local_name_p); ecma_deref_ecma_string (import_name_p); parser_raise_error (context_p, PARSER_ERR_DUPLICATED_IMPORT_BINDING); } parser_module_add_names_to_node (context_p, import_name_p, local_name_p); ecma_deref_ecma_string (local_name_p); ecma_deref_ecma_string (import_name_p); if (context_p->token.type != LEXER_COMMA && (context_p->token.type != LEXER_RIGHT_BRACE)) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_COMMA_EXPECTED); } else if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); } if (lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } } } /* parser_module_parse_import_clause */ /** * Raises parser error if the import or export statement is not in the global scope. */ void parser_module_check_request_place (parser_context_t *context_p) /**< parser context */ { if (context_p->last_context_p != NULL || context_p->stack_top_uint8 != 0 || (context_p->status_flags & PARSER_IS_FUNCTION) || (context_p->global_status_flags & ECMA_PARSE_EVAL) || (context_p->global_status_flags & ECMA_PARSE_MODULE) == 0) { parser_raise_error (context_p, PARSER_ERR_MODULE_UNEXPECTED); } } /* parser_module_check_request_place */ /** * Append names to the names list. */ void parser_module_append_names (parser_context_t *context_p, /**< parser context */ ecma_module_names_t **module_names_p) /**< target names */ { ecma_module_names_t *last_name_p = context_p->module_names_p; if (last_name_p == NULL) { return; } if (*module_names_p != NULL) { while (last_name_p->next_p != NULL) { last_name_p = last_name_p->next_p; } last_name_p->next_p = *module_names_p; } *module_names_p = context_p->module_names_p; context_p->module_names_p = NULL; } /* parser_module_append_names */ /** * Handle module specifier at the end of the import / export statement. */ void parser_module_handle_module_specifier (parser_context_t *context_p, /**< parser context */ ecma_module_node_t **node_list_p) /**< target node list */ { if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_STRING_LITERAL || context_p->token.lit_location.length == 0) { parser_raise_error (context_p, PARSER_ERR_STRING_EXPECTED); } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); lexer_literal_t *path_p = context_p->lit_object.literal_p; lexer_next_token (context_p); /* The lexer_next_token may throw an error, so the path is constructed after its call. */ ecma_string_t *path_string_p = parser_new_ecma_string_from_literal (path_p); ecma_module_node_t *node_p = JERRY_CONTEXT (module_current_p)->imports_p; ecma_module_node_t *last_node_p = NULL; /* Check if we have an import node with the same module request. */ while (node_p != NULL) { if (ecma_compare_ecma_strings (ecma_get_string_from_value (node_p->u.path_or_module), path_string_p)) { ecma_deref_ecma_string (path_string_p); break; } last_node_p = node_p; node_p = node_p->next_p; } if (node_p == NULL) { node_p = (ecma_module_node_t *) jmem_heap_alloc_block_null_on_error (sizeof (ecma_module_node_t)); if (node_p == NULL) { ecma_deref_ecma_string (path_string_p); parser_raise_error (context_p, PARSER_ERR_OUT_OF_MEMORY); } if (last_node_p == NULL) { JERRY_CONTEXT (module_current_p)->imports_p = node_p; } else { last_node_p->next_p = node_p; } node_p->next_p = NULL; node_p->module_names_p = NULL; node_p->u.path_or_module = ecma_make_string_value (path_string_p); } /* Append to imports. */ if (node_list_p == NULL) { parser_module_append_names (context_p, &node_p->module_names_p); return; } ecma_value_t *module_object_p = &node_p->u.path_or_module; node_p = *node_list_p; last_node_p = NULL; while (node_p != NULL) { if (node_p->u.module_object_p == module_object_p) { parser_module_append_names (context_p, &node_p->module_names_p); return; } last_node_p = node_p; node_p = node_p->next_p; } node_p = (ecma_module_node_t *) parser_malloc (context_p, sizeof (ecma_module_node_t)); if (last_node_p == NULL) { *node_list_p = node_p; } else { last_node_p->next_p = node_p; } node_p->next_p = NULL; node_p->module_names_p = context_p->module_names_p; node_p->u.module_object_p = module_object_p; context_p->module_names_p = NULL; } /* parser_module_handle_module_specifier */ #endif /* JERRY_MODULE_SYSTEM */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-literal-storage.cpp000664 001750 001750 00000055111 15164251010 045646 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-literal-storage.h" #include "ecma-alloc.h" #include "ecma-big-uint.h" #include "ecma-bigint.h" #include "ecma-helpers.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmalitstorage Literal storage * @{ */ /** * Free symbol list */ static void ecma_free_symbol_list (jmem_cpointer_t symbol_list_cp) /**< symbol list */ { while (symbol_list_cp != JMEM_CP_NULL) { ecma_lit_storage_item_t *symbol_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, symbol_list_cp); for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (symbol_list_p->values[i] != JMEM_CP_NULL) { ecma_string_t *string_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, symbol_list_p->values[i]); JERRY_ASSERT (ECMA_STRING_IS_REF_EQUALS_TO_ONE (string_p)); ecma_deref_ecma_string (string_p); } } jmem_cpointer_t next_item_cp = symbol_list_p->next_cp; jmem_pools_free (symbol_list_p, sizeof (ecma_lit_storage_item_t)); symbol_list_cp = next_item_cp; } } /* ecma_free_symbol_list */ /** * Free string list */ static void ecma_free_string_list (jmem_cpointer_t string_list_cp) /**< string list */ { while (string_list_cp != JMEM_CP_NULL) { ecma_lit_storage_item_t *string_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, string_list_cp); for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (string_list_p->values[i] != JMEM_CP_NULL) { ecma_string_t *string_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, string_list_p->values[i]); JERRY_ASSERT (ECMA_STRING_IS_REF_EQUALS_TO_ONE (string_p)); ecma_destroy_ecma_string (string_p); } } jmem_cpointer_t next_item_cp = string_list_p->next_cp; jmem_pools_free (string_list_p, sizeof (ecma_lit_storage_item_t)); string_list_cp = next_item_cp; } } /* ecma_free_string_list */ /** * Free number list */ static void ecma_free_number_list (jmem_cpointer_t number_list_cp) /**< number list */ { while (number_list_cp != JMEM_CP_NULL) { ecma_lit_storage_item_t *number_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, number_list_cp); for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (number_list_p->values[i] != JMEM_CP_NULL) { ecma_dealloc_number (JMEM_CP_GET_NON_NULL_POINTER (ecma_number_t, number_list_p->values[i])); } } jmem_cpointer_t next_item_cp = number_list_p->next_cp; jmem_pools_free (number_list_p, sizeof (ecma_lit_storage_item_t)); number_list_cp = next_item_cp; } } /* ecma_free_number_list */ #if JERRY_BUILTIN_BIGINT /** * Free bigint list */ static void ecma_free_bigint_list (jmem_cpointer_t bigint_list_cp) /**< bigint list */ { while (bigint_list_cp != JMEM_CP_NULL) { ecma_lit_storage_item_t *bigint_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, bigint_list_cp); for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (bigint_list_p->values[i] != JMEM_CP_NULL) { ecma_extended_primitive_t *bigint_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_extended_primitive_t, bigint_list_p->values[i]); JERRY_ASSERT (ECMA_EXTENDED_PRIMITIVE_IS_REF_EQUALS_TO_ONE (bigint_p)); ecma_deref_bigint (bigint_p); } } jmem_cpointer_t next_item_cp = bigint_list_p->next_cp; jmem_pools_free (bigint_list_p, sizeof (ecma_lit_storage_item_t)); bigint_list_cp = next_item_cp; } } /* ecma_free_bigint_list */ #endif /* JERRY_BUILTIN_BIGINT */ /** * Finalize literal storage */ void ecma_finalize_lit_storage (void) { ecma_free_symbol_list (JERRY_CONTEXT (symbol_list_first_cp)); ecma_free_string_list (JERRY_CONTEXT (string_list_first_cp)); ecma_free_number_list (JERRY_CONTEXT (number_list_first_cp)); #if JERRY_BUILTIN_BIGINT ecma_free_bigint_list (JERRY_CONTEXT (bigint_list_first_cp)); #endif /* JERRY_BUILTIN_BIGINT */ } /* ecma_finalize_lit_storage */ /** * Find or create a literal string. * * @return ecma_string_t compressed pointer */ ecma_value_t ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string to be searched */ lit_utf8_size_t size, /**< size of the string */ bool is_ascii) /**< encode of the string */ { ecma_string_t *string_p = (is_ascii ? ecma_new_ecma_string_from_ascii (chars_p, size) : ecma_new_ecma_string_from_utf8 (chars_p, size)); if (ECMA_IS_DIRECT_STRING (string_p)) { return ecma_make_string_value (string_p); } jmem_cpointer_t string_list_cp = JERRY_CONTEXT (string_list_first_cp); jmem_cpointer_t *empty_cpointer_p = NULL; while (string_list_cp != JMEM_CP_NULL) { ecma_lit_storage_item_t *string_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, string_list_cp); for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (string_list_p->values[i] == JMEM_CP_NULL) { if (empty_cpointer_p == NULL) { empty_cpointer_p = string_list_p->values + i; } } else { ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, string_list_p->values[i]); if (ecma_compare_ecma_strings (string_p, value_p)) { /* Return with string if found in the list. */ ecma_deref_ecma_string (string_p); return ecma_make_string_value (value_p); } } } string_list_cp = string_list_p->next_cp; } ECMA_SET_STRING_AS_STATIC (string_p); jmem_cpointer_t result; JMEM_CP_SET_NON_NULL_POINTER (result, string_p); if (empty_cpointer_p != NULL) { *empty_cpointer_p = result; return ecma_make_string_value (string_p); } ecma_lit_storage_item_t *new_item_p; new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t)); new_item_p->values[0] = result; for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { new_item_p->values[i] = JMEM_CP_NULL; } new_item_p->next_cp = JERRY_CONTEXT (string_list_first_cp); JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (string_list_first_cp), new_item_p); return ecma_make_string_value (string_p); } /* ecma_find_or_create_literal_string */ /** * Find or create a literal number. * * @return ecma value */ ecma_value_t ecma_find_or_create_literal_number (ecma_number_t number_arg) /**< number to be searched */ { ecma_value_t num = ecma_make_number_value (number_arg); if (ecma_is_value_integer_number (num)) { return num; } JERRY_ASSERT (ecma_is_value_float_number (num)); jmem_cpointer_t number_list_cp = JERRY_CONTEXT (number_list_first_cp); jmem_cpointer_t *empty_cpointer_p = NULL; while (number_list_cp != JMEM_CP_NULL) { ecma_lit_storage_item_t *number_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, number_list_cp); for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (number_list_p->values[i] == JMEM_CP_NULL) { if (empty_cpointer_p == NULL) { empty_cpointer_p = number_list_p->values + i; } } else { ecma_number_t *number_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_number_t, number_list_p->values[i]); if (*number_p == number_arg) { ecma_free_value (num); return ecma_make_float_value (number_p); } } } number_list_cp = number_list_p->next_cp; } jmem_cpointer_t result; JMEM_CP_SET_NON_NULL_POINTER (result, ecma_get_pointer_from_float_value (num)); if (empty_cpointer_p != NULL) { *empty_cpointer_p = result; return num; } ecma_lit_storage_item_t *new_item_p; new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t)); new_item_p->values[0] = result; for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { new_item_p->values[i] = JMEM_CP_NULL; } new_item_p->next_cp = JERRY_CONTEXT (number_list_first_cp); JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (number_list_first_cp), new_item_p); return num; } /* ecma_find_or_create_literal_number */ #if JERRY_BUILTIN_BIGINT /** * Find or create a literal BigInt. * * @return BigInt value */ ecma_value_t ecma_find_or_create_literal_bigint (ecma_value_t bigint) /**< bigint to be searched */ { JERRY_ASSERT (ecma_is_value_bigint (bigint)); if (bigint == ECMA_BIGINT_ZERO) { return bigint; } jmem_cpointer_t bigint_list_cp = JERRY_CONTEXT (bigint_list_first_cp); jmem_cpointer_t *empty_cpointer_p = NULL; while (bigint_list_cp != JMEM_CP_NULL) { ecma_lit_storage_item_t *bigint_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, bigint_list_cp); for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (bigint_list_p->values[i] == JMEM_CP_NULL) { if (empty_cpointer_p == NULL) { empty_cpointer_p = bigint_list_p->values + i; } } else { ecma_extended_primitive_t *other_bigint_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_extended_primitive_t, bigint_list_p->values[i]); ecma_value_t other_bigint = ecma_make_extended_primitive_value (other_bigint_p, ECMA_TYPE_BIGINT); if (ecma_bigint_is_equal_to_bigint (bigint, other_bigint)) { ecma_free_value (bigint); return other_bigint; } } } bigint_list_cp = bigint_list_p->next_cp; } jmem_cpointer_t result; JMEM_CP_SET_NON_NULL_POINTER (result, ecma_get_extended_primitive_from_value (bigint)); if (empty_cpointer_p != NULL) { *empty_cpointer_p = result; return bigint; } ecma_lit_storage_item_t *new_item_p; new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t)); new_item_p->values[0] = result; for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { new_item_p->values[i] = JMEM_CP_NULL; } new_item_p->next_cp = JERRY_CONTEXT (bigint_list_first_cp); JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (bigint_list_first_cp), new_item_p); return bigint; } /* ecma_find_or_create_literal_bigint */ #endif /* JERRY_BUILTIN_BIGINT */ /** * Log2 of snapshot literal alignment. */ #define JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG 1 /** * Snapshot literal alignment. */ #define JERRY_SNAPSHOT_LITERAL_ALIGNMENT (1u << JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG) /** * Literal offset shift. */ #define JERRY_SNAPSHOT_LITERAL_SHIFT (ECMA_VALUE_SHIFT + 2) /** * Literal value is number. */ #define JERRY_SNAPSHOT_LITERAL_IS_NUMBER (1u << ECMA_VALUE_SHIFT) #if JERRY_BUILTIN_BIGINT /** * Literal value is BigInt. */ #define JERRY_SNAPSHOT_LITERAL_IS_BIGINT (2u << ECMA_VALUE_SHIFT) #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_SNAPSHOT_SAVE /** * Append the value at the end of the appropriate list if it is not present there. */ void ecma_save_literals_append_value (ecma_value_t value, /**< value to be appended */ ecma_collection_t *lit_pool_p) /**< list of known values */ { /* Unlike direct numbers, direct strings are converted to character literals. */ if (!ecma_is_value_string (value) #if JERRY_BUILTIN_BIGINT && (!ecma_is_value_bigint (value) || value == ECMA_BIGINT_ZERO) #endif /* JERRY_BUILTIN_BIGINT */ && !ecma_is_value_float_number (value)) { return; } ecma_value_t *buffer_p = lit_pool_p->buffer_p; for (uint32_t i = 0; i < lit_pool_p->item_count; i++) { /* Strings / numbers are direct strings or stored in the literal storage. * Therefore direct comparison is enough to find the same strings / numbers. */ if (buffer_p[i] == value) { return; } } ecma_collection_push_back (lit_pool_p, value); } /* ecma_save_literals_append_value */ /** * Add names from a byte-code data to a list. */ void ecma_save_literals_add_compiled_code (const ecma_compiled_code_t *compiled_code_p, /**< byte-code data */ ecma_collection_t *lit_pool_p) /**< list of known values */ { ecma_value_t *literal_p; uint32_t argument_end; uint32_t register_end; uint32_t const_literal_end; uint32_t literal_end; JERRY_ASSERT (CBC_IS_FUNCTION (compiled_code_p->status_flags)); if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p; uint8_t *byte_p = (uint8_t *) compiled_code_p; literal_p = (ecma_value_t *) (byte_p + sizeof (cbc_uint16_arguments_t)); register_end = args_p->register_end; const_literal_end = args_p->const_literal_end - register_end; literal_end = args_p->literal_end - register_end; argument_end = args_p->argument_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p; uint8_t *byte_p = (uint8_t *) compiled_code_p; literal_p = (ecma_value_t *) (byte_p + sizeof (cbc_uint8_arguments_t)); register_end = args_p->register_end; const_literal_end = args_p->const_literal_end - register_end; literal_end = args_p->literal_end - register_end; argument_end = args_p->argument_end; } if (compiled_code_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED) { for (uint32_t i = 0; i < argument_end; i++) { ecma_save_literals_append_value (literal_p[i], lit_pool_p); } } for (uint32_t i = 0; i < const_literal_end; i++) { ecma_save_literals_append_value (literal_p[i], lit_pool_p); } for (uint32_t i = const_literal_end; i < literal_end; i++) { ecma_compiled_code_t *bytecode_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, literal_p[i]); if (CBC_IS_FUNCTION (bytecode_p->status_flags) && bytecode_p != compiled_code_p) { ecma_save_literals_add_compiled_code (bytecode_p, lit_pool_p); } } uint8_t *byte_p = ((uint8_t *) compiled_code_p) + (((size_t) compiled_code_p->size) << JMEM_ALIGNMENT_LOG); literal_p = ecma_snapshot_resolve_serializable_values ((ecma_compiled_code_t *) compiled_code_p, byte_p); while (literal_p < (ecma_value_t *) byte_p) { ecma_save_literals_append_value (*literal_p, lit_pool_p); literal_p++; } } /* ecma_save_literals_add_compiled_code */ /** * Save literals to specified snapshot buffer. * * Note: * Frees 'lit_pool_p' regardless of success. * * @return true - if save was performed successfully (i.e. buffer size is sufficient), * false - otherwise */ bool ecma_save_literals_for_snapshot (ecma_collection_t *lit_pool_p, /**< list of known values */ uint32_t *buffer_p, /**< [out] output snapshot buffer */ size_t buffer_size, /**< size of the buffer */ size_t *in_out_buffer_offset_p, /**< [in,out] write position in the buffer */ lit_mem_to_snapshot_id_map_entry_t **out_map_p, /**< [out] map from literal identifiers * to the literal offsets * in snapshot */ uint32_t *out_map_len_p) /**< [out] number of literals */ { if (lit_pool_p->item_count == 0) { *out_map_p = NULL; *out_map_len_p = 0; } uint32_t lit_table_size = 0; size_t max_lit_table_size = buffer_size - *in_out_buffer_offset_p; if (max_lit_table_size > (UINT32_MAX >> JERRY_SNAPSHOT_LITERAL_SHIFT)) { max_lit_table_size = (UINT32_MAX >> JERRY_SNAPSHOT_LITERAL_SHIFT); } ecma_value_t *lit_buffer_p = lit_pool_p->buffer_p; /* Compute the size of the literal pool. */ for (uint32_t i = 0; i < lit_pool_p->item_count; i++) { if (ecma_is_value_float_number (lit_buffer_p[i])) { lit_table_size += (uint32_t) sizeof (ecma_number_t); } #if JERRY_BUILTIN_BIGINT else if (ecma_is_value_bigint (lit_buffer_p[i])) { ecma_extended_primitive_t *bigint_p = ecma_get_extended_primitive_from_value (lit_buffer_p[i]); lit_table_size += (uint32_t) JERRY_ALIGNUP (sizeof (uint32_t) + ECMA_BIGINT_GET_SIZE (bigint_p), JERRY_SNAPSHOT_LITERAL_ALIGNMENT); } #endif /* JERRY_BUILTIN_BIGINT */ else { ecma_string_t *string_p = ecma_get_string_from_value (lit_buffer_p[i]); lit_table_size += (uint32_t) JERRY_ALIGNUP (sizeof (uint16_t) + ecma_string_get_size (string_p), JERRY_SNAPSHOT_LITERAL_ALIGNMENT); } /* Check whether enough space is available and the maximum size is not reached. */ if (lit_table_size > max_lit_table_size) { ecma_collection_destroy (lit_pool_p); return false; } } lit_mem_to_snapshot_id_map_entry_t *map_p; uint32_t total_count = lit_pool_p->item_count; map_p = jmem_heap_alloc_block (total_count * sizeof (lit_mem_to_snapshot_id_map_entry_t)); /* Set return values (no error is possible from here). */ JERRY_ASSERT ((*in_out_buffer_offset_p % sizeof (uint32_t)) == 0); uint8_t *destination_p = (uint8_t *) (buffer_p + (*in_out_buffer_offset_p / sizeof (uint32_t))); uint32_t literal_offset = 0; *in_out_buffer_offset_p += lit_table_size; *out_map_p = map_p; *out_map_len_p = total_count; lit_buffer_p = lit_pool_p->buffer_p; /* Generate literal pool data. */ for (uint32_t i = 0; i < lit_pool_p->item_count; i++) { map_p->literal_id = lit_buffer_p[i]; map_p->literal_offset = (literal_offset << JERRY_SNAPSHOT_LITERAL_SHIFT) | ECMA_TYPE_SNAPSHOT_OFFSET; lit_utf8_size_t length; if (ecma_is_value_float_number (lit_buffer_p[i])) { map_p->literal_offset |= JERRY_SNAPSHOT_LITERAL_IS_NUMBER; ecma_number_t num = ecma_get_float_from_value (lit_buffer_p[i]); memcpy (destination_p, &num, sizeof (ecma_number_t)); length = JERRY_ALIGNUP (sizeof (ecma_number_t), JERRY_SNAPSHOT_LITERAL_ALIGNMENT); } #if JERRY_BUILTIN_BIGINT else if (ecma_is_value_bigint (lit_buffer_p[i])) { map_p->literal_offset |= JERRY_SNAPSHOT_LITERAL_IS_BIGINT; ecma_extended_primitive_t *bigint_p = ecma_get_extended_primitive_from_value (lit_buffer_p[i]); uint32_t size = ECMA_BIGINT_GET_SIZE (bigint_p); memcpy (destination_p, &bigint_p->u.bigint_sign_and_size, sizeof (uint32_t)); memcpy (destination_p + sizeof (uint32_t), ECMA_BIGINT_GET_DIGITS (bigint_p, 0), size); length = JERRY_ALIGNUP (sizeof (uint32_t) + size, JERRY_SNAPSHOT_LITERAL_ALIGNMENT); } #endif /* JERRY_BUILTIN_BIGINT */ else { ecma_string_t *string_p = ecma_get_string_from_value (lit_buffer_p[i]); length = ecma_string_get_size (string_p); *(uint16_t *) destination_p = (uint16_t) length; ecma_string_to_cesu8_bytes (string_p, destination_p + sizeof (uint16_t), length); length = JERRY_ALIGNUP (sizeof (uint16_t) + length, JERRY_SNAPSHOT_LITERAL_ALIGNMENT); } JERRY_ASSERT ((length % sizeof (uint16_t)) == 0); destination_p += length; literal_offset += length; map_p++; } ecma_collection_destroy (lit_pool_p); return true; } /* ecma_save_literals_for_snapshot */ #endif /* JERRY_SNAPSHOT_SAVE */ #if JERRY_SNAPSHOT_EXEC || JERRY_SNAPSHOT_SAVE /** * Get the compressed pointer of a given literal. * * @return literal compressed pointer */ ecma_value_t ecma_snapshot_get_literal (const uint8_t *literal_base_p, /**< literal start */ ecma_value_t literal_value) /**< string / number offset */ { JERRY_ASSERT ((literal_value & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_SNAPSHOT_OFFSET); const uint8_t *literal_p = literal_base_p + (literal_value >> JERRY_SNAPSHOT_LITERAL_SHIFT); if (literal_value & JERRY_SNAPSHOT_LITERAL_IS_NUMBER) { ecma_number_t num; memcpy (&num, literal_p, sizeof (ecma_number_t)); return ecma_find_or_create_literal_number (num); } #if JERRY_BUILTIN_BIGINT if (literal_value & JERRY_SNAPSHOT_LITERAL_IS_BIGINT) { uint32_t bigint_sign_and_size = *(uint32_t *) literal_p; uint32_t size = bigint_sign_and_size & ~(uint32_t) (sizeof (ecma_bigint_digit_t) - 1); ecma_extended_primitive_t *bigint_p = ecma_bigint_create (size); if (bigint_p == NULL) { jerry_fatal (JERRY_FATAL_OUT_OF_MEMORY); } /* Only the sign bit can differ. */ JERRY_ASSERT (bigint_p->u.bigint_sign_and_size == (bigint_sign_and_size & ~(uint32_t) ECMA_BIGINT_SIGN)); bigint_p->u.bigint_sign_and_size = bigint_sign_and_size; memcpy (ECMA_BIGINT_GET_DIGITS (bigint_p, 0), literal_p + sizeof (uint32_t), size); return ecma_find_or_create_literal_bigint (ecma_make_extended_primitive_value (bigint_p, ECMA_TYPE_BIGINT)); } #endif /* JERRY_BUILTIN_BIGINT */ uint16_t length = *(const uint16_t *) literal_p; return ecma_find_or_create_literal_string (literal_p + sizeof (uint16_t), length, false); } /* ecma_snapshot_get_literal */ /** * Compute the start of the serializable ecma-values of the bytecode * Related values: * - function argument names, if CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED is present * - function name, if CBC_CODE_FLAGS_CLASS_CONSTRUCTOR is not present and es.next profile is enabled * * @return pointer to the beginning of the serializable ecma-values */ ecma_value_t * ecma_snapshot_resolve_serializable_values (const ecma_compiled_code_t *compiled_code_p, /**< compiled code */ uint8_t *bytecode_end_p) /**< end of the bytecode */ { ecma_value_t *base_p = (ecma_value_t *) bytecode_end_p; if (compiled_code_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED) { uint32_t argument_end; if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { argument_end = ((cbc_uint16_arguments_t *) compiled_code_p)->argument_end; } else { argument_end = ((cbc_uint8_arguments_t *) compiled_code_p)->argument_end; } base_p -= argument_end; } /* function name */ if (CBC_FUNCTION_GET_TYPE (compiled_code_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR) { base_p--; } return base_p; } /* ecma_snapshot_resolve_serializable_values */ #endif /* JERRY_SNAPSHOT_EXEC || JERRY_SNAPSHOT_SAVE */ /** * @} * @} */ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/msinttypes/stdint.h000664 001750 001750 00000022252 15164251010 040547 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules// 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 #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 . // 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 // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we have to wrap 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 #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 #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 . // 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_ ] thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-arraybuffer-object.h000664 001750 001750 00000004771 15164251010 047250 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_ARRAYBUFFER_OBJECT_H #define ECMA_ARRAYBUFFER_OBJECT_H #include "ecma-globals.h" #if JERRY_BUILTIN_TYPEDARRAY /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaarraybufferobject ECMA ArrayBuffer object related routines * @{ */ /** * Get array buffer flags. */ #define ECMA_ARRAYBUFFER_GET_FLAGS(arraybuffer_p) \ (((ecma_extended_object_t *) (arraybuffer_p))->u.cls.u1.array_buffer_flags) /** * Check whether the backing store is allocated for an array buffer. */ #define ECMA_ARRAYBUFFER_LAZY_ALLOC(arraybuffer_p) \ (JERRY_UNLIKELY (!(ECMA_ARRAYBUFFER_GET_FLAGS (arraybuffer_p) & ECMA_ARRAYBUFFER_ALLOCATED)) \ && ecma_arraybuffer_allocate_buffer_throw (arraybuffer_p) == ECMA_VALUE_ERROR) ecma_value_t ecma_op_create_arraybuffer_object (const ecma_value_t *, uint32_t); /** * Helper functions for arraybuffer. */ ecma_object_t *ecma_arraybuffer_create_object (uint8_t type, uint32_t length); ecma_object_t *ecma_arraybuffer_create_object_with_buffer (uint8_t type, uint32_t length); ecma_object_t *ecma_arraybuffer_new_object (uint32_t length); ecma_value_t ecma_arraybuffer_allocate_buffer (ecma_object_t *arraybuffer_p); ecma_value_t ecma_arraybuffer_allocate_buffer_throw (ecma_object_t *arraybuffer_p); void ecma_arraybuffer_release_buffer (ecma_object_t *arraybuffer_p); uint8_t *JERRY_ATTR_PURE ecma_arraybuffer_get_buffer (ecma_object_t *obj_p); uint32_t JERRY_ATTR_PURE ecma_arraybuffer_get_length (ecma_object_t *obj_p); bool JERRY_ATTR_PURE ecma_arraybuffer_is_detached (ecma_object_t *obj_p); bool ecma_arraybuffer_detach (ecma_object_t *obj_p); bool ecma_is_arraybuffer (ecma_value_t val); ecma_value_t ecma_builtin_arraybuffer_slice (ecma_value_t this_arg, const ecma_value_t *argument_list_p, uint32_t arguments_number); /** * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ #endif /* !ECMA_ARRAYBUFFER_OBJECT_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/huffman.h000664 001750 001750 00000004472 15164251010 035336 0ustar00ddennedyddennedy000000 000000 // Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Utilities for building and looking up Huffman trees. // // Author: Urvang Joshi (urvang@google.com) #ifndef WEBP_UTILS_HUFFMAN_H_ #define WEBP_UTILS_HUFFMAN_H_ #include #include "../webp/format_constants.h" #include "../webp/types.h" #ifdef __cplusplus extern "C" { #endif #define HUFFMAN_TABLE_BITS 8 #define HUFFMAN_TABLE_MASK ((1 << HUFFMAN_TABLE_BITS) - 1) #define LENGTHS_TABLE_BITS 7 #define LENGTHS_TABLE_MASK ((1 << LENGTHS_TABLE_BITS) - 1) // Huffman lookup table entry typedef struct { uint8_t bits; // number of bits used for this symbol uint16_t value; // symbol value or table offset } HuffmanCode; // Huffman table group. typedef struct HTreeGroup HTreeGroup; struct HTreeGroup { HuffmanCode* htrees[HUFFMAN_CODES_PER_META_CODE]; int is_trivial_literal; // True, if huffman trees for Red, Blue & Alpha // Symbols are trivial (have a single code). uint32_t literal_arb; // If is_trivial_literal is true, this is the // ARGB value of the pixel, with Green channel // being set to zero. }; // Creates the instance of HTreeGroup with specified number of tree-groups. HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups); // Releases the memory allocated for HTreeGroup. void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups); // Builds Huffman lookup table assuming code lengths are in symbol order. // The 'code_lengths' is pre-allocated temporary memory buffer used for creating // the huffman table. // Returns built table size or 0 in case of error (invalid tree or // memory error). int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits, const int code_lengths[], int code_lengths_size); #ifdef __cplusplus } // extern "C" #endif #endif // WEBP_UTILS_HUFFMAN_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_jpg/tvgJpgLoader.h000664 001750 001750 00000003220 15164251010 036576 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_JPG_LOADER_H_ #define _TVG_JPG_LOADER_H_ #include "tvgLoader.h" using tjhandle = void*; //TODO: Use Task? class JpgLoader : public ImageLoader { public: JpgLoader(); ~JpgLoader(); bool open(const char* path) override; bool open(const char* data, uint32_t size, const char* rpath, bool copy) override; bool read() override; private: void clear(); tjhandle jpegDecompressor; unsigned char* data = nullptr; uint32_t size = 0; bool freeData = false; }; #endif //_TVG_JPG_LOADER_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/png/tvgLodePng.cpp000664 001750 001750 00000335506 15164251010 035012 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* LodePNG version 20200306 Copyright (c) 2005-2020 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any sourcedistribution. */ #include "tvgCommon.h" #include "tvgLodePng.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ /* convince the compiler to inline a function, for use when this measurably improves performance */ /* inline is not available in C90, but use it when supported by the compiler */ #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L)) #define LODEPNG_INLINE inline #else #define LODEPNG_INLINE /* not available */ #endif /* restrict is not available in C90, but use it when supported by the compiler */ #if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\ (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \ (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus)) #define LODEPNG_RESTRICT __restrict #else #define LODEPNG_RESTRICT /* not available */ #endif #define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x)) /* Replacements for C library functions such as memcpy and strlen, to support platforms where a full C library is not available. The compiler can recognize them and compile to something as fast. */ static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, const void* LODEPNG_RESTRICT src, size_t size) { size_t i; for (i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i]; } static void lodepng_memset(void* LODEPNG_RESTRICT dst, int value, size_t num) { size_t i; for (i = 0; i < num; i++) ((char*)dst)[i] = (char)value; } /* does not check memory out of bounds, do not use on untrusted data */ static size_t lodepng_strlen(const char* a) { const char* orig = a; /* avoid warning about unused function in case of disabled COMPILE... macros */ (void)(&lodepng_strlen); while (*a) a++; return (size_t)(a - orig); } /* Safely check if adding two integers will overflow (no undefined behavior, compiler removing the code, etc...) and output result. */ static int lodepng_addofl(size_t a, size_t b, size_t* result) { *result = a + b; /* Unsigned addition is well defined and safe in C90 */ return *result < a; } /* Safely check if multiplying two integers will overflow (no undefined behavior, compiler removing the code, etc...) and output result. */ static int lodepng_mulofl(size_t a, size_t b, size_t* result) { *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */ return (a != 0 && *result / a != b); } /* Safely check if a + b > c, even if overflow could happen. */ static int lodepng_gtofl(size_t a, size_t b, size_t c) { size_t d; if (lodepng_addofl(a, b, &d)) return 1; return d > c; } /* Often in case of an error a value is assigned to a variable and then it breaks out of a loop (to go to the cleanup phase of a function). This macro does that. It makes the error handling code shorter and more readable. Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83); */ #define CERROR_BREAK(errorvar, code){\ errorvar = code;\ break;\ } /* version of CERROR_BREAK that assumes the common case where the error variable is named "error" */ #define ERROR_BREAK(code) CERROR_BREAK(error, code) /* Set error var to the error code, and return it.*/ #define CERROR_RETURN_ERROR(errorvar, code){\ errorvar = code;\ return code;\ } /* Try the code, if it returns error, also return the error. */ #define CERROR_TRY_RETURN(call){\ unsigned error = call;\ if(error) return error;\ } /* Set error var to the error code, and return from the void function. */ #define CERROR_RETURN(errorvar, code){\ errorvar = code;\ return;\ } /* dynamic vector of unsigned chars */ struct ucvector { unsigned char* data; size_t size; /*used size*/ size_t allocsize; /*allocated size*/ }; /* returns 1 if success, 0 if failure ==> nothing done */ static unsigned ucvector_resize(ucvector* p, size_t size) { if (size > p->allocsize) { size_t newsize = size + (p->allocsize >> 1u); auto data = tvg::realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned char*)data; } else return 0; /*error: not enough memory*/ } p->size = size; return 1; /*success*/ } static ucvector ucvector_init(unsigned char* buffer, size_t size) { ucvector v; v.data = buffer; v.allocsize = v.size = size; return v; } static unsigned lodepng_read32bitInt(const unsigned char* buffer) { return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) | ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]); } /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // End of common code and tools. Begin of Zlib related code. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ struct LodePNGBitReader { const unsigned char* data; size_t size; /*size of data in bytes*/ size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/ size_t bp; unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/ }; /* data size argument is in bytes. Returns error if size too large causing overflow */ static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size) { size_t temp; reader->data = data; reader->size = size; /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */ if (lodepng_mulofl(size, 8u, &reader->bitsize)) return 105; /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and trying to ensure 32 more bits*/ if (lodepng_addofl(reader->bitsize, 64u, &temp)) return 105; reader->bp = 0; reader->buffer = 0; return 0; /*ok*/ } /* ensureBits functions: Ensures the reader can at least read nbits bits in one or more readBits calls, safely even if not enough bits are available. Returns 1 if there are enough bits available, 0 if not. */ /*See ensureBits documentation above. This one ensures exactly 1 bit */ /*static unsigned ensureBits1(LodePNGBitReader* reader) { if(reader->bp >= reader->bitsize) return 0; reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u); return 1; }*/ /*See ensureBits documentation above. This one ensures up to 9 bits */ static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits) { size_t start = reader->bp >> 3u; size_t size = reader->size; if (start + 1u < size) { reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u); reader->buffer >>= (reader->bp & 7u); return 1; } else { reader->buffer = 0; if (start + 0u < size) reader->buffer |= reader->data[start + 0]; reader->buffer >>= (reader->bp & 7u); return reader->bp + nbits <= reader->bitsize; } } /*See ensureBits documentation above. This one ensures up to 17 bits */ static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits) { size_t start = reader->bp >> 3u; size_t size = reader->size; if (start + 2u < size) { reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u); reader->buffer >>= (reader->bp & 7u); return 1; } else { reader->buffer = 0; if (start + 0u < size) reader->buffer |= reader->data[start + 0]; if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); reader->buffer >>= (reader->bp & 7u); return reader->bp + nbits <= reader->bitsize; } } /*See ensureBits documentation above. This one ensures up to 25 bits */ static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits) { size_t start = reader->bp >> 3u; size_t size = reader->size; if (start + 3u < size) { reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); reader->buffer >>= (reader->bp & 7u); return 1; } else { reader->buffer = 0; if (start + 0u < size) reader->buffer |= reader->data[start + 0]; if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); reader->buffer >>= (reader->bp & 7u); return reader->bp + nbits <= reader->bitsize; } } /*See ensureBits documentation above. This one ensures up to 32 bits */ static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits) { size_t start = reader->bp >> 3u; size_t size = reader->size; if(start + 4u < size) { reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); reader->buffer >>= (reader->bp & 7u); reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u))); return 1; } else { reader->buffer = 0; if (start + 0u < size) reader->buffer |= reader->data[start + 0]; if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); if (start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u); reader->buffer >>= (reader->bp & 7u); return reader->bp + nbits <= reader->bitsize; } } /* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */ static unsigned peekBits(LodePNGBitReader* reader, size_t nbits) { /* The shift allows nbits to be only up to 31. */ return reader->buffer & ((1u << nbits) - 1u); } /* Must have enough bits available with ensureBits */ static void advanceBits(LodePNGBitReader* reader, size_t nbits) { reader->buffer >>= nbits; reader->bp += nbits; } /* Must have enough bits available with ensureBits */ static unsigned readBits(LodePNGBitReader* reader, size_t nbits) { unsigned result = peekBits(reader, nbits); advanceBits(reader, nbits); return result; } /* Public for testing only. steps and result must have numsteps values. */ unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, size_t numsteps, const size_t* steps, unsigned* result) { size_t i; LodePNGBitReader reader; unsigned error = LodePNGBitReader_init(&reader, data, size); if (error) return 0; for (i = 0; i < numsteps; i++) { size_t step = steps[i]; unsigned ok; if (step > 25) ok = ensureBits32(&reader, step); else if (step > 17) ok = ensureBits25(&reader, step); else if (step > 9) ok = ensureBits17(&reader, step); else ok = ensureBits9(&reader, step); if (!ok) return 0; result[i] = readBits(&reader, step); } return 1; } static unsigned reverseBits(unsigned bits, unsigned num) { /*TODO: implement faster lookup table based version when needed*/ unsigned i, result = 0; for (i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i; return result; } /* ////////////////////////////////////////////////////////////////////////// */ /* / Deflate - Huffman / */ /* ////////////////////////////////////////////////////////////////////////// */ #define FIRST_LENGTH_CODE_INDEX 257 #define LAST_LENGTH_CODE_INDEX 285 /*256 literals, the end code, some length codes, and 2 unused codes*/ #define NUM_DEFLATE_CODE_SYMBOLS 288 /*the distance codes have their own symbols, 30 used, 2 unused*/ #define NUM_DISTANCE_SYMBOLS 32 /*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ #define NUM_CODE_LENGTH_CODES 19 /*the base lengths represented by codes 257-285*/ static const unsigned LENGTHBASE[29] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; /*the extra bits used by codes 257-285 (added to base length)*/ static const unsigned LENGTHEXTRA[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; /*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ static const unsigned DISTANCEBASE[30] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; /*the extra bits of backwards distances (added to base)*/ static const unsigned DISTANCEEXTRA[30] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman tree of the dynamic huffman tree lengths is generated*/ static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* ////////////////////////////////////////////////////////////////////////// */ /* Huffman tree struct, containing multiple representations of the tree */ struct HuffmanTree { unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/ unsigned* lengths; /*the lengths of the huffman codes*/ unsigned maxbitlen; /*maximum number of bits a single code can get*/ unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ /* for reading only */ unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/ unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/ }; static void HuffmanTree_init(HuffmanTree* tree) { tree->codes = 0; tree->lengths = 0; tree->table_len = 0; tree->table_value = 0; } static void HuffmanTree_cleanup(HuffmanTree* tree) { tvg::free(tree->codes); tvg::free(tree->lengths); tvg::free(tree->table_len); tvg::free(tree->table_value); } /* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/ /* values 8u and 9u work the fastest */ #define FIRSTBITS 9u /* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination, which is possible in case of only 0 or 1 present symbols. */ #define INVALIDSYMBOL 65535u /* make table for huffman decoding */ static unsigned HuffmanTree_makeTable(HuffmanTree* tree) { static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/ static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u; size_t i, numpresent, pointer, size; /*total table size*/ unsigned* maxlens = tvg::malloc(headsize * sizeof(unsigned)); if (!maxlens) return 83; /*alloc fail*/ /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/ lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens)); for (i = 0; i < tree->numcodes; i++) { unsigned symbol = tree->codes[i]; unsigned l = tree->lengths[i]; unsigned index; if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/ /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/ index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS); maxlens[index] = LODEPNG_MAX(maxlens[index], l); } /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */ size = headsize; for (i = 0; i < headsize; ++i) { unsigned l = maxlens[i]; if (l > FIRSTBITS) size += (1u << (l - FIRSTBITS)); } tree->table_len = tvg::malloc(size * sizeof(*tree->table_len)); tree->table_value = tvg::malloc(size * sizeof(*tree->table_value)); if (!tree->table_len || !tree->table_value) { tvg::free(maxlens); /* freeing tree->table values is done at a higher scope */ return 83; /*alloc fail*/ } /*initialize with an invalid length to indicate unused entries*/ for (i = 0; i < size; ++i) tree->table_len[i] = 16; /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/ pointer = headsize; for (i = 0; i < headsize; ++i) { unsigned l = maxlens[i]; if(l <= FIRSTBITS) continue; tree->table_len[i] = l; tree->table_value[i] = pointer; pointer += (1u << (l - FIRSTBITS)); } tvg::free(maxlens); /*fill in the first table for short symbols, or secondary table for long symbols*/ numpresent = 0; for (i = 0; i < tree->numcodes; ++i) { unsigned l = tree->lengths[i]; unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/ /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/ unsigned reverse = reverseBits(symbol, l); if (l == 0) continue; numpresent++; if (l <= FIRSTBITS) { /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/ unsigned num = 1u << (FIRSTBITS - l); unsigned j; for (j = 0; j < num; ++j) { /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/ unsigned index = reverse | (j << l); if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ tree->table_len[index] = l; tree->table_value[index] = i; } } else { /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/ /*the FIRSTBITS MSBs of the symbol are the first table index*/ unsigned index = reverse & mask; unsigned maxlen = tree->table_len[index]; /*log2 of secondary table length, should be >= l - FIRSTBITS*/ unsigned tablelen = maxlen - FIRSTBITS; unsigned start = tree->table_value[index]; /*starting index in secondary table*/ unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/ unsigned j; if (maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ for (j = 0; j < num; ++j) { unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */ unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS))); tree->table_len[index2] = l; tree->table_value[index2] = i; } } } if (numpresent < 2) { /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits, but deflate uses 1 bit instead. In case of 0 symbols, no symbols can appear at all, but such huffman tree could still exist (e.g. if distance codes are never used). In both cases, not all symbols of the table will be filled in. Fill them in with an invalid symbol value so returning them from huffmanDecodeSymbol will cause error. */ for (i = 0; i < size; ++i) { if (tree->table_len[i] == 16) { /* As length, use a value smaller than FIRSTBITS for the head table, and a value larger than FIRSTBITS for the secondary table, to ensure valid behavior for advanceBits when reading this symbol. */ tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1); tree->table_value[i] = INVALIDSYMBOL; } } } else { /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. If that is not the case (due to too long length codes), the table will not have been fully used, and this is an error (not all bit combinations can be decoded): an oversubscribed huffman tree, indicated by error 55. */ for (i = 0; i < size; ++i) { if (tree->table_len[i] == 16) return 55; } } return 0; } /* Second step for the ...makeFromLengths and ...makeFromFrequencies functions. numcodes, lengths and maxbitlen must already be filled in correctly. return value is error. */ static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) { unsigned* blcount; unsigned* nextcode; unsigned error = 0; unsigned bits, n; tree->codes = tvg::malloc(tree->numcodes * sizeof(unsigned)); blcount = tvg::malloc((tree->maxbitlen + 1) * sizeof(unsigned)); nextcode = tvg::malloc((tree->maxbitlen + 1) * sizeof(unsigned)); if (!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/ if (!error) { for (n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0; /*step 1: count number of instances of each code length*/ for (bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]]; /*step 2: generate the nextcode values*/ for(bits = 1; bits <= tree->maxbitlen; ++bits) { nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u; } /*step 3: generate all the codes*/ for (n = 0; n != tree->numcodes; ++n) { if (tree->lengths[n] != 0) { tree->codes[n] = nextcode[tree->lengths[n]]++; /*remove superfluous bits from the code*/ tree->codes[n] &= ((1u << tree->lengths[n]) - 1u); } } } tvg::free(blcount); tvg::free(nextcode); if (!error) error = HuffmanTree_makeTable(tree); return error; } /* given the code lengths (as stored in the PNG file), generate the tree as defined by Deflate. maxbitlen is the maximum bits that a code in the tree can have. return value is error. */ static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen) { unsigned i; tree->lengths = tvg::malloc(numcodes * sizeof(unsigned)); if (!tree->lengths) return 83; /*alloc fail*/ for (i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->maxbitlen = maxbitlen; return HuffmanTree_makeFromLengths2(tree); } /*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ static unsigned generateFixedLitLenTree(HuffmanTree* tree) { unsigned i, error = 0; unsigned* bitlen = tvg::malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); if (!bitlen) return 83; /*alloc fail*/ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ for (i = 0; i <= 143; ++i) bitlen[i] = 8; for (i = 144; i <= 255; ++i) bitlen[i] = 9; for (i = 256; i <= 279; ++i) bitlen[i] = 7; for (i = 280; i <= 287; ++i) bitlen[i] = 8; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); tvg::free(bitlen); return error; } /*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ static unsigned generateFixedDistanceTree(HuffmanTree* tree) { unsigned i, error = 0; unsigned* bitlen = tvg::malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if (!bitlen) return 83; /*alloc fail*/ /*there are 32 distance codes, but 30-31 are unused*/ for (i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); tvg::free(bitlen); return error; } /* returns the code. The bit reader must already have been ensured at least 15 bits */ static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree) { unsigned short code = peekBits(reader, FIRSTBITS); unsigned short l = codetree->table_len[code]; unsigned short value = codetree->table_value[code]; if (l <= FIRSTBITS) { advanceBits(reader, l); return value; } else { unsigned index2; advanceBits(reader, FIRSTBITS); index2 = value + peekBits(reader, l - FIRSTBITS); advanceBits(reader, codetree->table_len[index2] - FIRSTBITS); return codetree->table_value[index2]; } } /* ////////////////////////////////////////////////////////////////////////// */ /* / Inflator (Decompressor) / */ /* ////////////////////////////////////////////////////////////////////////// */ /*get the tree of a deflated block with fixed tree, as specified in the deflate specification Returns error code.*/ static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) { unsigned error = generateFixedLitLenTree(tree_ll); if (error) return error; return generateFixedDistanceTree(tree_d); } /*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, LodePNGBitReader* reader) { /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ unsigned error = 0; unsigned n, HLIT, HDIST, HCLEN, i; /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ unsigned* bitlen_ll = 0; /*lit,len code lengths*/ unsigned* bitlen_d = 0; /*dist code lengths*/ /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ unsigned* bitlen_cl = 0; HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ if (!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ HLIT = readBits(reader, 5) + 257; /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ HDIST = readBits(reader, 5) + 1; /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ HCLEN = readBits(reader, 4) + 4; bitlen_cl = tvg::malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); if(!bitlen_cl) return 83 /*alloc fail*/; HuffmanTree_init(&tree_cl); while (!error) { /*read the code length codes out of 3 * (amount of code length codes) bits*/ if (lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) { ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/ } for (i = 0; i != HCLEN; ++i) { ensureBits9(reader, 3); /*out of bounds already checked above */ bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3); } for (i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) { bitlen_cl[CLCL_ORDER[i]] = 0; } error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); if(error) break; /*now we can use this tree to read the lengths for the tree that this function will return*/ bitlen_ll = tvg::malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); bitlen_d = tvg::malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if (!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll)); lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d)); /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ i = 0; while (i < HLIT + HDIST) { unsigned code; ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/ code = huffmanDecodeSymbol(reader, &tree_cl); if (code <= 15) /*a length code*/ { if (i < HLIT) bitlen_ll[i] = code; else bitlen_d[i - HLIT] = code; ++i; } else if (code == 16) /*repeat previous*/ { unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ unsigned value; /*set value to the previous code*/ if (i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ replength += readBits(reader, 2); if (i < HLIT + 1) value = bitlen_ll[i - 1]; else value = bitlen_d[i - HLIT - 1]; /*repeat this value in the next lengths*/ for (n = 0; n < replength; ++n) { if (i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ if (i < HLIT) bitlen_ll[i] = value; else bitlen_d[i - HLIT] = value; ++i; } } else if(code == 17) /*repeat "0" 3-10 times*/ { unsigned replength = 3; /*read in the bits that indicate repeat length*/ replength += readBits(reader, 3); /*repeat this value in the next lengths*/ for (n = 0; n < replength; ++n) { if (i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ if (i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; ++i; } } else if(code == 18) /*repeat "0" 11-138 times*/ { unsigned replength = 11; /*read in the bits that indicate repeat length*/ replength += readBits(reader, 7); /*repeat this value in the next lengths*/ for (n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; ++i; } } else /*if(code == INVALIDSYMBOL)*/ { ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ } /*check if any of the ensureBits above went out of bounds*/ if (reader->bp > reader->bitsize) { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ } } if (error) break; if (bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); if (error) break; error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); break; /*end of error-while*/ } tvg::free(bitlen_cl); tvg::free(bitlen_ll); tvg::free(bitlen_d); HuffmanTree_cleanup(&tree_cl); return error; } /*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, unsigned btype) { unsigned error = 0; HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ HuffmanTree tree_d; /*the huffman tree for distance codes*/ HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); if (btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d); else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader); while (!error) /*decode all symbols until end reached, breaks at end code*/ { /*code_ll is literal, length or end code*/ unsigned code_ll; ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */ code_ll = huffmanDecodeSymbol(reader, &tree_ll); if (code_ll <= 255) /*literal symbol*/ { if (!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/); out->data[out->size - 1] = (unsigned char)code_ll; } else if (code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { unsigned code_d, distance; unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ size_t start, backward, length; /*part 1: get length base*/ length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; /*part 2: get extra bits and add the value of that to length*/ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; if (numextrabits_l != 0) { /* bits already ensured above */ length += readBits(reader, numextrabits_l); } /*part 3: get distance code*/ ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */ code_d = huffmanDecodeSymbol(reader, &tree_d); if (code_d > 29) { if (code_d <= 31) { ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/ } else /* if(code_d == INVALIDSYMBOL) */{ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ } } distance = DISTANCEBASE[code_d]; /*part 4: get extra bits from distance*/ numextrabits_d = DISTANCEEXTRA[code_d]; if (numextrabits_d != 0) { /* bits already ensured above */ distance += readBits(reader, numextrabits_d); } /*part 5: fill in all the out[n] values based on the length and dist*/ start = out->size; if (distance > start) ERROR_BREAK(52); /*too long backward distance*/ backward = start - distance; if (!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/); if (distance < length) { size_t forward; lodepng_memcpy(out->data + start, out->data + backward, distance); start += distance; for (forward = distance; forward < length; ++forward) { out->data[start++] = out->data[backward++]; } } else { lodepng_memcpy(out->data + start, out->data + backward, length); } } else if (code_ll == 256) { break; /*end code, break the loop*/ } else /*if(code_ll == INVALIDSYMBOL)*/ { ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ } /*check if any of the ensureBits above went out of bounds*/ if (reader->bp > reader->bitsize) { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ ERROR_BREAK(51); /*error, bit pointer jumps past memory*/ } } HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); return error; } static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, const LodePNGDecompressSettings* settings) { size_t bytepos; size_t size = reader->size; unsigned LEN, NLEN, error = 0; /*go to first boundary of byte*/ bytepos = (reader->bp + 7u) >> 3u; /*read LEN (2 bytes) and NLEN (2 bytes)*/ if (bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/ LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; /*check if 16-bit NLEN is really the one's complement of LEN*/ if (!settings->ignore_nlen && LEN + NLEN != 65535) { return 21; /*error: NLEN is not one's complement of LEN*/ } if (!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/ /*read the literal data: LEN bytes are now stored in the out buffer*/ if (bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/ lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN); bytepos += LEN; reader->bp = bytepos << 3u; return error; } static unsigned lodepng_inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned BFINAL = 0; LodePNGBitReader reader; unsigned error = LodePNGBitReader_init(&reader, in, insize); if (error) return error; while (!BFINAL) { unsigned BTYPE; if (!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/ BFINAL = readBits(&reader, 1); BTYPE = readBits(&reader, 2); if (BTYPE == 3) return 20; /*error: invalid BTYPE*/ else if (BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/ else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/ if (error) return error; } return error; } static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if (settings->custom_inflate) { unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings); out->allocsize = out->size; return error; } else { return lodepng_inflatev(out, in, insize, settings); } } /* ////////////////////////////////////////////////////////////////////////// */ /* / Adler32 / */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) { unsigned s1 = adler & 0xffffu; unsigned s2 = (adler >> 16u) & 0xffffu; while (len != 0u) { unsigned i; /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/ unsigned amount = len > 5552u ? 5552u : len; len -= amount; for (i = 0; i != amount; ++i) { s1 += (*data++); s2 += s1; } s1 %= 65521u; s2 %= 65521u; } return (s2 << 16u) | s1; } /*Return the adler32 of the bytes data[0..len-1]*/ static unsigned adler32(const unsigned char* data, unsigned len) { return update_adler32(1u, data, len); } /* ////////////////////////////////////////////////////////////////////////// */ /* / Zlib / */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned lodepng_zlib_decompressv(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned error = 0; unsigned CM, CINFO, FDICT; if (insize < 2) return 53; /*error, size of zlib data too small*/ /*read information from zlib header*/ if ((in[0] * 256 + in[1]) % 31 != 0) { /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ return 24; } CM = in[0] & 15; CINFO = (in[0] >> 4) & 15; /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ FDICT = (in[1] >> 5) & 1; /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ if (CM != 8 || CINFO > 7) { /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ return 25; } if (FDICT != 0) { /*error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary."*/ return 26; } error = inflatev(out, in + 2, insize - 2, settings); if (error) return error; if (!settings->ignore_adler32) { unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); unsigned checksum = adler32(out->data, (unsigned)(out->size)); if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ } return 0; /*no error*/ } /*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(settings->custom_zlib) { return settings->custom_zlib(out, outsize, in, insize, settings); } else { unsigned error; ucvector v = ucvector_init(*out, *outsize); if (expected_size) { /*reserve the memory to avoid intermediate reallocations*/ ucvector_resize(&v, *outsize + expected_size); v.size = *outsize; } error = lodepng_zlib_decompressv(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } } static void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { settings->ignore_adler32 = 0; settings->ignore_nlen = 0; settings->custom_zlib = 0; settings->custom_inflate = 0; settings->custom_context = 0; } /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // End of Zlib related code. Begin of PNG related code. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #if 0 //thorvg don't use crc /* CRC polynomial: 0xedb88320 */ static unsigned lodepng_crc32_table[256] = { 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u }; /* Calculate CRC32 of buffer Return the CRC of the bytes buf[0..len-1]. */ static unsigned lodepng_crc32(const unsigned char* data, size_t length) { unsigned r = 0xffffffffu; size_t i; for (i = 0; i < length; ++i) { r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u); } return r ^ 0xffffffffu; } #endif /* ////////////////////////////////////////////////////////////////////////// */ /* / Reading and writing PNG color channel bits / */ /* ////////////////////////////////////////////////////////////////////////// */ /* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first, so LodePNGBitWriter and LodePNGBitReader can't be used for those. */ static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); ++(*bitpointer); return result; } /* TODO: make this faster */ static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { unsigned result = 0; size_t i; for (i = 0 ; i < nbits; ++i) { result <<= 1u; result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); } return result; } static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) { /*the current bit in bitstream may be 0 or 1 for this to work*/ if (bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u)))); else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u))); ++(*bitpointer); } /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG chunks / */ /* ////////////////////////////////////////////////////////////////////////// */ /* The lodepng_chunk functions are normally not needed, except to traverse the unknown chunks stored in the LodePNGInfo struct, or add new ones to it. It also allows traversing the chunks of an encoded PNG file yourself. The chunk pointer always points to the beginning of the chunk itself, that is the first byte of the 4 length bytes. In the PNG file format, chunks have the following format: -4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer) -4 bytes chunk type (ASCII a-z,A-Z only, see below) -length bytes of data (may be 0 bytes if length was 0) -4 bytes of CRC, computed on chunk name + data The first chunk starts at the 8th byte of the PNG file, the entire rest of the file exists out of concatenated chunks with the above format. PNG standard chunk ASCII naming conventions: -First byte: uppercase = critical, lowercase = ancillary -Second byte: uppercase = public, lowercase = private -Third byte: must be uppercase -Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy */ /* Gets the length of the data of the chunk. Total chunk length has 12 bytes more. There must be at least 4 bytes to read from. If the result value is too large, it may be corrupt data. */ static unsigned lodepng_chunk_length(const unsigned char* chunk) { return lodepng_read32bitInt(&chunk[0]); } /* check if the type is the given type */ static unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) { if (lodepng_strlen(type) != 4) return 0; return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); } /* 0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard) */ static unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) { return ((chunk[4] & 32) != 0); } static const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) { return &chunk[8]; } #if 0 //thorvg don't use crc /* returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!) */ static unsigned lodepng_chunk_check_crc(const unsigned char* chunk) { unsigned length = lodepng_chunk_length(chunk); unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ unsigned checksum = lodepng_crc32(&chunk[4], length + 4); if (CRC != checksum) return 1; else return 0; } #endif static const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) { if (chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/ if (chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ return chunk + 8; } else { size_t total_chunk_length; const unsigned char* result; if (lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end; result = chunk + total_chunk_length; if (result < chunk) return end; /*pointer overflow*/ return result; } } /* ////////////////////////////////////////////////////////////////////////// */ /* / Color types, channels, bits / */ /* ////////////////////////////////////////////////////////////////////////// */ /*checks if the colortype is valid and the bitdepth bd is allowed for this colortype. Return value is a LodePNG error code.*/ static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) { switch(colortype) { case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break; case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break; case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break; case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */ default: return 31; /* invalid color type */ } return 0; /*allowed color type / bits combination*/ } static unsigned getNumColorChannels(LodePNGColorType colortype) { switch(colortype) { case LCT_GREY: return 1; case LCT_RGB: return 3; case LCT_PALETTE: return 1; case LCT_GREY_ALPHA: return 2; case LCT_RGBA: return 4; case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */ default: return 0; /*invalid color type*/ } } static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) { /*bits per pixel is amount of channels * bits per channel*/ return getNumColorChannels(colortype) * bitdepth; } static void lodepng_color_mode_init(LodePNGColorMode* info) { info->key_defined = 0; info->key_r = info->key_g = info->key_b = 0; info->colortype = LCT_RGBA; info->bitdepth = 8; info->palette = 0; info->palettesize = 0; } /*allocates palette memory if needed, and initializes all colors to black*/ static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info) { size_t i; /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/ /*the palette must have room for up to 256 colors with 4 bytes each.*/ if (!info->palette) info->palette = tvg::malloc(1024); if (!info->palette) return; /*alloc fail*/ for (i = 0; i != 256; ++i) { /*Initialize all unused colors with black, the value used for invalid palette indices. This is an error according to the PNG spec, but common PNG decoders make it black instead. That makes color conversion slightly faster due to no error handling needed.*/ info->palette[i * 4 + 0] = 0; info->palette[i * 4 + 1] = 0; info->palette[i * 4 + 2] = 0; info->palette[i * 4 + 3] = 255; } } static void lodepng_palette_clear(LodePNGColorMode* info) { if (info->palette) tvg::free(info->palette); info->palette = 0; info->palettesize = 0; } static void lodepng_color_mode_cleanup(LodePNGColorMode* info) { lodepng_palette_clear(info); } /*return value is error code (0 means no error)*/ static unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) { lodepng_color_mode_cleanup(dest); lodepng_memcpy(dest, source, sizeof(LodePNGColorMode)); if (source->palette) { dest->palette = tvg::malloc(1024); if (!dest->palette && source->palettesize) return 83; /*alloc fail*/ lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4); } return 0; } static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) { size_t i; if (a->colortype != b->colortype) return 0; if (a->bitdepth != b->bitdepth) return 0; if (a->key_defined != b->key_defined) return 0; if (a->key_defined) { if(a->key_r != b->key_r) return 0; if(a->key_g != b->key_g) return 0; if(a->key_b != b->key_b) return 0; } if (a->palettesize != b->palettesize) return 0; for (i = 0; i != a->palettesize * 4; ++i) { if (a->palette[i] != b->palette[i]) return 0; } return 1; } static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); size_t n = (size_t)w * (size_t)h; return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u; } /* Returns the byte size of a raw image buffer with given width, height and color mode */ static size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth); } /*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, and in addition has one extra byte per line: the filter byte. So this gives a larger result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */ static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp) { /* + 1 for the filter byte, and possibly plus padding bits per line. */ /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */ size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u; return (size_t)h * line; } /* Safely checks whether size_t overflow can be caused due to amount of pixels. This check is overcautious rather than precise. If this check indicates no overflow, you can safely compute in a size_t (but not an unsigned): -(size_t)w * (size_t)h * 8 -amount of bytes in IDAT (including filter, padding and Adam7 bytes) -amount of bytes in raw color model Returns 1 if overflow possible, 0 if not. */ static int lodepng_pixel_overflow(unsigned w, unsigned h, const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) { size_t bpp = LODEPNG_MAX(lodepng_get_bpp_lct(pngcolor->colortype, pngcolor->bitdepth), lodepng_get_bpp_lct(rawcolor->colortype, rawcolor->bitdepth)); size_t numpixels, total; size_t line; /* bytes per line in worst case */ if (lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1; if (lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */ /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */ if (lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1; if (lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1; if (lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */ if (lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */ return 0; /* no overflow */ } static void lodepng_info_init(LodePNGInfo* info) { lodepng_color_mode_init(&info->color); info->interlace_method = 0; info->compression_method = 0; info->filter_method = 0; } static void lodepng_info_cleanup(LodePNGInfo* info) { lodepng_color_mode_cleanup(&info->color); } /* index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to */ static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) { unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ unsigned p = index & m; in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ in = in << (bits * (m - p)); if(p == 0) out[index * bits / 8u] = in; else out[index * bits / 8u] |= in; } /* One node of a color tree This is the data structure used to count the number of unique colors and to get a palette index for a color. It's like an octree, but because the alpha channel is used too, each node has 16 instead of 8 children. */ struct ColorTree { ColorTree* children[16]; /* up to 16 pointers to ColorTree of next level */ int index; /* the payload. Only has a meaningful value if this is in the last level */ }; static void color_tree_init(ColorTree* tree) { lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children)); tree->index = -1; } static void color_tree_cleanup(ColorTree* tree) { int i; for (i = 0; i != 16; ++i) { if(tree->children[i]) { color_tree_cleanup(tree->children[i]); tvg::free(tree->children[i]); } } } /* returns -1 if color not present, its index otherwise */ static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { int bit = 0; for (bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if (!tree->children[i]) return -1; else tree = tree->children[i]; } return tree ? tree->index : -1; } /* color is not allowed to already exist. Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist") Returns error code, or 0 if ok */ static unsigned color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) { int bit; for (bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if (!tree->children[i]) { tree->children[i] = tvg::malloc(sizeof(ColorTree)); if (!tree->children[i]) return 83; /*alloc fail*/ color_tree_init(tree->children[i]); } tree = tree->children[i]; } tree->index = (int)index; return 0; } /* put a pixel, given its RGBA color, into image of any color type */ static unsigned rgba8ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { if (mode->colortype == LCT_GREY) { unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ if (mode->bitdepth == 8) out[i] = gray; else if (mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray; else { /*take the most significant bits of gray*/ gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u); addColorBits(out, i, mode->bitdepth, gray); } } else if (mode->colortype == LCT_RGB) { if (mode->bitdepth == 8) { out[i * 3 + 0] = r; out[i * 3 + 1] = g; out[i * 3 + 2] = b; } else { out[i * 6 + 0] = out[i * 6 + 1] = r; out[i * 6 + 2] = out[i * 6 + 3] = g; out[i * 6 + 4] = out[i * 6 + 5] = b; } } else if(mode->colortype == LCT_PALETTE) { int index = color_tree_get(tree, r, g, b, a); if (index < 0) return 82; /*color not in palette*/ if (mode->bitdepth == 8) out[i] = index; else addColorBits(out, i, mode->bitdepth, (unsigned)index); } else if (mode->colortype == LCT_GREY_ALPHA) { unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ if (mode->bitdepth == 8) { out[i * 2 + 0] = gray; out[i * 2 + 1] = a; } else if (mode->bitdepth == 16) { out[i * 4 + 0] = out[i * 4 + 1] = gray; out[i * 4 + 2] = out[i * 4 + 3] = a; } } else if (mode->colortype == LCT_RGBA) { if (mode->bitdepth == 8) { out[i * 4 + 0] = r; out[i * 4 + 1] = g; out[i * 4 + 2] = b; out[i * 4 + 3] = a; } else { out[i * 8 + 0] = out[i * 8 + 1] = r; out[i * 8 + 2] = out[i * 8 + 3] = g; out[i * 8 + 4] = out[i * 8 + 5] = b; out[i * 8 + 6] = out[i * 8 + 7] = a; } } return 0; /*no error*/ } /* put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type */ static void rgba16ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r, unsigned short g, unsigned short b, unsigned short a) { if (mode->colortype == LCT_GREY) { unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ out[i * 2 + 0] = (gray >> 8) & 255; out[i * 2 + 1] = gray & 255; } else if (mode->colortype == LCT_RGB) { out[i * 6 + 0] = (r >> 8) & 255; out[i * 6 + 1] = r & 255; out[i * 6 + 2] = (g >> 8) & 255; out[i * 6 + 3] = g & 255; out[i * 6 + 4] = (b >> 8) & 255; out[i * 6 + 5] = b & 255; } else if (mode->colortype == LCT_GREY_ALPHA) { unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ out[i * 4 + 0] = (gray >> 8) & 255; out[i * 4 + 1] = gray & 255; out[i * 4 + 2] = (a >> 8) & 255; out[i * 4 + 3] = a & 255; } else if (mode->colortype == LCT_RGBA) { out[i * 8 + 0] = (r >> 8) & 255; out[i * 8 + 1] = r & 255; out[i * 8 + 2] = (g >> 8) & 255; out[i * 8 + 3] = g & 255; out[i * 8 + 4] = (b >> 8) & 255; out[i * 8 + 5] = b & 255; out[i * 8 + 6] = (a >> 8) & 255; out[i * 8 + 7] = a & 255; } } /* Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type. */ static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) { if (mode->colortype == LCT_GREY) { if (mode->bitdepth == 8) { *r = *g = *b = in[i]; if (mode->key_defined && *r == mode->key_r) *a = 0; else *a = 255; } else if (mode->bitdepth == 16) { *r = *g = *b = in[i * 2 + 0]; if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; else *a = 255; } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */ size_t j = i * mode->bitdepth; unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); *r = *g = *b = (value * 255) / highest; if (mode->key_defined && value == mode->key_r) *a = 0; else *a = 255; } } else if (mode->colortype == LCT_RGB) { if (mode->bitdepth == 8) { *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; if (mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; else *a = 255; } else { *r = in[i * 6 + 0]; *g = in[i * 6 + 2]; *b = in[i * 6 + 4]; if (mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 255; } } else if (mode->colortype == LCT_PALETTE) { unsigned index; if (mode->bitdepth == 8) index = in[i]; else { size_t j = i * mode->bitdepth; index = readBitsFromReversedStream(&j, in, mode->bitdepth); } /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ *r = mode->palette[index * 4 + 0]; *g = mode->palette[index * 4 + 1]; *b = mode->palette[index * 4 + 2]; *a = mode->palette[index * 4 + 3]; } else if (mode->colortype == LCT_GREY_ALPHA) { if (mode->bitdepth == 8) { *r = *g = *b = in[i * 2 + 0]; *a = in[i * 2 + 1]; } else { *r = *g = *b = in[i * 4 + 0]; *a = in[i * 4 + 2]; } } else if (mode->colortype == LCT_RGBA) { if (mode->bitdepth == 8) { *r = in[i * 4 + 0]; *g = in[i * 4 + 1]; *b = in[i * 4 + 2]; *a = in[i * 4 + 3]; } else { *r = in[i * 8 + 0]; *g = in[i * 8 + 2]; *b = in[i * 8 + 4]; *a = in[i * 8 + 6]; } } } /* Similar to getPixelColorRGBA8, but with all the for loops inside of the color mode test cases, optimized to convert the colors much faster, when converting to the common case of RGBA with 8 bit per channel. buffer must be RGBA with enough memory.*/ static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode) { unsigned num_channels = 4; size_t i; if (mode->colortype == LCT_GREY) { if (mode->bitdepth == 8) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i]; buffer[3] = 255; } if (mode->key_defined) { buffer -= numpixels * num_channels; for (i = 0; i != numpixels; ++i, buffer += num_channels) { if(buffer[0] == mode->key_r) buffer[3] = 0; } } } else if (mode->bitdepth == 16) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2]; buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; } } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */ size_t j = 0; for (i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; } } } else if (mode->colortype == LCT_RGB) { if (mode->bitdepth == 8) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { lodepng_memcpy(buffer, &in[i * 3], 3); buffer[3] = 255; } if (mode->key_defined) { buffer -= numpixels * num_channels; for (i = 0; i != numpixels; ++i, buffer += num_channels) { if (buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0; } } } else { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 6 + 0]; buffer[1] = in[i * 6 + 2]; buffer[2] = in[i * 6 + 4]; buffer[3] = mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; } } } else if (mode->colortype == LCT_PALETTE) { if (mode->bitdepth == 8) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned index = in[i]; /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ lodepng_memcpy(buffer, &mode->palette[index * 4], 4); } } else { size_t j = 0; for (i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ lodepng_memcpy(buffer, &mode->palette[index * 4], 4); } } } else if (mode->colortype == LCT_GREY_ALPHA) { if (mode->bitdepth == 8) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; buffer[3] = in[i * 2 + 1]; } } else { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; buffer[3] = in[i * 4 + 2]; } } } else if (mode->colortype == LCT_RGBA) { if (mode->bitdepth == 8) { lodepng_memcpy(buffer, in, numpixels * 4); } else { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 8 + 0]; buffer[1] = in[i * 8 + 2]; buffer[2] = in[i * 8 + 4]; buffer[3] = in[i * 8 + 6]; } } } } /* Similar to getPixelColorsRGBA8, but with 3-channel RGB output. */ static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode) { const unsigned num_channels = 3; size_t i; if (mode->colortype == LCT_GREY) { if (mode->bitdepth == 8) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i]; } } else if (mode->bitdepth == 16) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2]; } } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */ size_t j = 0; for (i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; } } } else if (mode->colortype == LCT_RGB) { if (mode->bitdepth == 8) { lodepng_memcpy(buffer, in, numpixels * 3); } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 6 + 0]; buffer[1] = in[i * 6 + 2]; buffer[2] = in[i * 6 + 4]; } } } else if (mode->colortype == LCT_PALETTE) { if (mode->bitdepth == 8) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned index = in[i]; /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ lodepng_memcpy(buffer, &mode->palette[index * 4], 3); } } else { size_t j = 0; for (i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ lodepng_memcpy(buffer, &mode->palette[index * 4], 3); } } } else if (mode->colortype == LCT_GREY_ALPHA) { if (mode->bitdepth == 8) { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; } } else { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; } } } else if (mode->colortype == LCT_RGBA) { if (mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { lodepng_memcpy(buffer, &in[i * 4], 3); } } else { for (i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 8 + 0]; buffer[1] = in[i * 8 + 2]; buffer[2] = in[i * 8 + 4]; } } } } /* Get RGBA16 color of pixel with index i (y * width + x) from the raw image with given color type, but the given color type must be 16-bit itself. */ static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) { if (mode->colortype == LCT_GREY) { *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; else *a = 65535; } else if (mode->colortype == LCT_RGB) { *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; if (mode->key_defined && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 65535; } else if (mode->colortype == LCT_GREY_ALPHA) { *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; } else if (mode->colortype == LCT_RGBA) { *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; } } /* Converts raw buffer from one color type to another color type, based on LodePNGColorMode structs to describe the input and output color type. See the reference manual at the end of this header file to see which color conversions are supported. return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel of the output color type (lodepng_get_bpp). For < 8 bpp images, there should not be padding bits at the end of scanlines. For 16-bit per channel colors, uses big endian format like PNG does. Return value is LodePNG error code */ static unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h) { size_t i; ColorTree tree; size_t numpixels = (size_t)w * (size_t)h; unsigned error = 0; if (mode_in->colortype == LCT_PALETTE && !mode_in->palette) { return 107; /* error: must provide palette if input mode is palette */ } if (lodepng_color_mode_equal(mode_out, mode_in)) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); lodepng_memcpy(out, in, numbytes); return 0; } if (mode_out->colortype == LCT_PALETTE) { size_t palettesize = mode_out->palettesize; const unsigned char* palette = mode_out->palette; size_t palsize = (size_t)1u << mode_out->bitdepth; /* if the user specified output palette but did not give the values, assume they want the values of the input color type (assuming that one is palette). Note that we never create a new palette ourselves.*/ if (palettesize == 0) { palettesize = mode_in->palettesize; palette = mode_in->palette; /* if the input was also palette with same bitdepth, then the color types are also equal, so copy literally. This to preserve the exact indices that were in the PNG even in case there are duplicate colors in the palette.*/ if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); lodepng_memcpy(out, in, numbytes); return 0; } } if (palettesize < palsize) palsize = palettesize; color_tree_init(&tree); for (i = 0; i != palsize; ++i) { const unsigned char* p = &palette[i * 4]; error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); if (error) break; } } if (!error) { if (mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { for (i = 0; i != numpixels; ++i) { unsigned short r = 0, g = 0, b = 0, a = 0; getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); rgba16ToPixel(out, i, mode_out, r, g, b, a); } } else if (mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) { getPixelColorsRGBA8(out, numpixels, in, mode_in); } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) { getPixelColorsRGB8(out, numpixels, in, mode_in); } else { unsigned char r = 0, g = 0, b = 0, a = 0; for (i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); if (error) break; } } } if (mode_out->colortype == LCT_PALETTE) { color_tree_cleanup(&tree); } return error; } /* Paeth predictor, used by PNG filter type 4 The parameters are of type short, but should come from unsigned chars, the shorts are only needed to make the paeth calculation correct. */ static unsigned char paethPredictor(short a, short b, short c) { short pa = LODEPNG_ABS(b - c); short pb = LODEPNG_ABS(a - c); short pc = LODEPNG_ABS(a + b - c - c); /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */ if (pb < pa) { a = b; pa = pb; } return (pc < pa) ? c : a; } /*shared values used by multiple Adam7 related functions*/ static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ /* Outputs various dimensions and positions in the image related to the Adam7 reduced images. passw: output containing the width of the 7 passes passh: output containing the height of the 7 passes filter_passstart: output containing the index of the start and end of each reduced image with filter bytes padded_passstart output containing the index of the start and end of each reduced image when without filter bytes but with padded scanlines passstart: output containing the index of the start and end of each reduced image without padding between scanlines, but still padding between the images w, h: width and height of non-interlaced image bpp: bits per pixel "padded" is only relevant if bpp is less than 8 and a scanline or image does not end at a full byte */ static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) { /* the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass */ unsigned i; /* calculate width and height in pixels of each pass */ for (i = 0; i != 7; ++i) { passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; if(passw[i] == 0) passh[i] = 0; if(passh[i] == 0) passw[i] = 0; } filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; for (i = 0; i != 7; ++i) { /* if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte) */ filter_passstart[i + 1] = filter_passstart[i] + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0); /* bits padded if needed to fill full byte at end of each scanline */ padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u); /* only padded at end of reduced image */ passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u; } } /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG Decoder / */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length) { /* For PNG filter method 0 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) precon is the previous unfiltered scanline, recon the result, scanline the current one the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead recon and scanline MAY be the same memory address! precon must be disjoint. */ size_t i; switch (filterType) { case 0: for (i = 0; i != length; ++i) recon[i] = scanline[i]; break; case 1: for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; break; case 2: if (precon) { for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; } else { for(i = 0; i != length; ++i) recon[i] = scanline[i]; } break; case 3: if (precon) { for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u); for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u); } else { for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u); } break; case 4: if (precon) { for (i = 0; i != bytewidth; ++i) { recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ } /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */ if (bytewidth >= 4) { for (; i + 3 < length; i += 4) { size_t j = i - bytewidth; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3]; unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3]; recon[i + 0] = s0 + paethPredictor(r0, p0, q0); recon[i + 1] = s1 + paethPredictor(r1, p1, q1); recon[i + 2] = s2 + paethPredictor(r2, p2, q2); recon[i + 3] = s3 + paethPredictor(r3, p3, q3); } } else if (bytewidth >= 3) { for (; i + 2 < length; i += 3) { size_t j = i - bytewidth; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2]; unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2]; recon[i + 0] = s0 + paethPredictor(r0, p0, q0); recon[i + 1] = s1 + paethPredictor(r1, p1, q1); recon[i + 2] = s2 + paethPredictor(r2, p2, q2); } } else if (bytewidth >= 2) { for (; i + 1 < length; i += 2) { size_t j = i - bytewidth; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1]; unsigned char q0 = precon[j + 0], q1 = precon[j + 1]; recon[i + 0] = s0 + paethPredictor(r0, p0, q0); recon[i + 1] = s1 + paethPredictor(r1, p1, q1); } } for (; i != length; ++i) { recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); } } else { for (i = 0; i != bytewidth; ++i) { recon[i] = scanline[i]; } for (i = bytewidth; i < length; ++i) { /* paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth] */ recon[i] = (scanline[i] + recon[i - bytewidth]); } } break; default: return 36; /* error: invalid filter type given */ } return 0; } static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { /* For PNG filter method 0 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) */ unsigned y; unsigned char* prevline = 0; /* bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise */ size_t bytewidth = (bpp + 7u) / 8u; /* the width of a scanline in bytes, not including the filter type */ size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; for (y = 0; y < h; ++y) { size_t outindex = linebytes * y; size_t inindex = (1 + linebytes) * y; /* the extra filterbyte added to each row */ unsigned char filterType = in[inindex]; CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); prevline = &out[outindex]; } return 0; } /* in: Adam7 interlaced image, with no padding bits between scanlines, but between reduced images so that each reduced image starts at a byte. out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h bpp: bits per pixel out has the following size in bits: w * h * bpp. in is possibly bigger due to padding bits between reduced images. out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation (because that's likely a little bit faster) NOTE: comments about padding bits are only relevant if bpp < 8 */ static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); if (bpp >= 8) { for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8u; for (y = 0; y < passh[i]; ++y) for (x = 0; x < passw[i]; ++x) { size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth; for (b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } } } } else /* bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers */ { for (i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /* bit pointers (for out and in buffer) */ for (y = 0; y < passh[i]; ++y) for (x = 0; x < passw[i]; ++x) { ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp; for (b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } } } } } static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) { /* After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers for the Adam7 code, the color convert code and the output to the user. in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 only useful if (ilinebits - olinebits) is a value in the range 1..7 */ unsigned y; size_t diff = ilinebits - olinebits; size_t ibp = 0, obp = 0; /*input and output bit pointers*/ for (y = 0; y < h; ++y) { size_t x; for (x = 0; x < olinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } ibp += diff; } } /* out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks (with filter index bytes and possible padding bits) return value is error */ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png) { /* This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps: *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8) *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace NOTE: the in buffer will be overwritten with intermediate data! */ unsigned bpp = lodepng_get_bpp_lct(info_png->color.colortype, info_png->color.bitdepth); if (bpp == 0) return 31; /* error: invalid colortype */ if (info_png->interlace_method == 0) { if (bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) { CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h); } /* we can immediately filter into the out buffer, no other steps needed */ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); } else /* interlace_method is 1 (Adam7) */ { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); for (i = 0; i != 7; ++i) { CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); /* TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, move bytes instead of bits or move not at all */ if (bpp < 8) { /* remove padding bits in scanlines; after this there still may be padding bits between the different reduced images: each reduced image still starts nicely at a byte */ removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]); } } Adam7_deinterlace(out, in, w, h, bpp); } return 0; } static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { unsigned pos = 0, i; color->palettesize = chunkLength / 3u; if (color->palettesize == 0 || color->palettesize > 256) return 38; /* error: palette too small or big */ lodepng_color_mode_alloc_palette(color); if (!color->palette && color->palettesize) { color->palettesize = 0; return 83; /* alloc fail */ } for (i = 0; i != color->palettesize; ++i) { color->palette[4 * i + 0] = data[pos++]; /*R*/ color->palette[4 * i + 1] = data[pos++]; /*G*/ color->palette[4 * i + 2] = data[pos++]; /*B*/ color->palette[4 * i + 3] = 255; /*alpha*/ } return 0; /* OK */ } static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { unsigned i; if (color->colortype == LCT_PALETTE) { /* error: more alpha values given than there are palette entries */ if (chunkLength > color->palettesize) return 39; for (i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; } else if (color->colortype == LCT_GREY) { /* error: this chunk must be 2 bytes for grayscale image */ if (chunkLength != 2) return 30; color->key_defined = 1; color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; } else if (color->colortype == LCT_RGB) { /* error: this chunk must be 6 bytes for RGB image */ if (chunkLength != 6) return 41; color->key_defined = 1; color->key_r = 256u * data[0] + data[1]; color->key_g = 256u * data[2] + data[3]; color->key_b = 256u * data[4] + data[5]; } else return 42; /* error: tRNS chunk not allowed for other color models */ return 0; /* OK */ } /* read a PNG, the result will be in the same color type as the PNG (hence "generic") */ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { unsigned char IEND = 0; const unsigned char* chunk; unsigned char* idat; /*the data from idat chunks, zlib compressed*/ size_t idatsize = 0; unsigned char* scanlines = 0; size_t scanlines_size = 0, expected_size = 0; size_t outsize = 0; /* safe output values in case error happens */ *out = 0; *w = *h = 0; state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ if (state->error) return; if (lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) { CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ } /*the input filesize is a safe upper bound for the sum of idat chunks size*/ idat = tvg::malloc(insize); if (!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/ chunk = &in[33]; /*first byte of the first chunk after the header*/ /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer*/ while (!IEND && !state->error) { unsigned chunkLength; const unsigned char* data; /*the data in the chunk*/ /*error: size of the in buffer too small to contain next chunk*/ if ((size_t)((chunk - in) + 12) > insize || chunk < in) { if (state->decoder.ignore_end) break; /*other errors may still happen though*/ CERROR_BREAK(state->error, 30); } /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ chunkLength = lodepng_chunk_length(chunk); /*error: chunk length larger than the max PNG chunk size*/ if (chunkLength > 2147483647) { if (state->decoder.ignore_end) break; /*other errors may still happen though*/ CERROR_BREAK(state->error, 63); } if ((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) { CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ } data = lodepng_chunk_data_const(chunk); /*for unknown chunk order*/ //unsigned unknown = 0; /*IDAT chunk, containing compressed image data*/ if (lodepng_chunk_type_equals(chunk, "IDAT")) { size_t newsize; if (lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); if (newsize > insize) CERROR_BREAK(state->error, 95); lodepng_memcpy(idat + idatsize, data, chunkLength); idatsize += chunkLength; } else if (lodepng_chunk_type_equals(chunk, "IEND")) { /*IEND chunk*/ IEND = 1; } else if (lodepng_chunk_type_equals(chunk, "PLTE")) { /*palette chunk (PLTE)*/ state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); if (state->error) break; } else if (lodepng_chunk_type_equals(chunk, "tRNS")) { /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that affects the alpha channel of pixels. */ state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); if (state->error) break; } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ if (!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) { CERROR_BREAK(state->error, 69); } //unknown = 1; } #if 0 //We don't use CRC if (!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ { if (lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ } #endif if (!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize); } if (state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) { state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */ } if (!state->error) { /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. If the decompressed size does not match the prediction, the image must be corrupt.*/ if (state->info_png.interlace_method == 0) { size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth); expected_size = lodepng_get_raw_size_idat(*w, *h, bpp); } else { size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth); /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/ expected_size = 0; expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp); if (*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp); expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp); if (*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp); expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp); if (*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp); expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp); } state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings); } if (!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/ tvg::free(idat); if (!state->error) { outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); *out = tvg::malloc(outsize); if (!*out) state->error = 83; /*alloc fail*/ } if (!state->error) { lodepng_memset(*out, 0, outsize); state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png); } tvg::free(scanlines); } static void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) { settings->color_convert = 1; settings->ignore_crc = 0; settings->ignore_critical = 0; settings->ignore_end = 0; lodepng_decompress_settings_init(&settings->zlibsettings); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ /*read the information from the header and store it in the LodePNGInfo. return value is error*/ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { unsigned width, height; LodePNGInfo* info = &state->info_png; if (insize == 0 || in == 0) { CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ } if (insize < 33) { CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ } /* when decoding a new PNG image, make sure all parameters created after previous decoding are reset */ /* TODO: remove this. One should use a new LodePNGState for new sessions */ lodepng_info_cleanup(info); lodepng_info_init(info); if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ } if (lodepng_chunk_length(in + 8) != 13) { CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ } if (!lodepng_chunk_type_equals(in + 8, "IHDR")) { CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ } /*read the values given in the header*/ width = lodepng_read32bitInt(&in[16]); height = lodepng_read32bitInt(&in[20]); /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/ if (w) *w = width; if (h) *h = height; info->color.bitdepth = in[24]; info->color.colortype = (LodePNGColorType)in[25]; info->compression_method = in[26]; info->filter_method = in[27]; info->interlace_method = in[28]; /*errors returned only after the parsing so other values are still output*/ /*error: invalid image size*/ if (width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93); /*error: invalid colortype or bitdepth combination*/ state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); if (state->error) return state->error; /*error: only compression method 0 is allowed in the specification*/ if (info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); /*error: only filter method 0 is allowed in the specification*/ if (info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); /*error: only interlace methods 0 and 1 exist in the specification*/ if (info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); #if 0 //thorvg don't use crc if (!state->decoder.ignore_crc) { unsigned CRC = lodepng_read32bitInt(&in[29]); unsigned checksum = lodepng_crc32(&in[12], 17); if (CRC != checksum) { CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ } } #endif return state->error; } unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { *out = 0; decodeGeneric(out, w, h, state, in, insize); if (state->error) return state->error; if (!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) { /*same color type, no copying or converting of data needed*/ /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype the raw image has to the end user*/ if (!state->decoder.color_convert) { state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); if (state->error) return state->error; } } else { /*color conversion needed*/ unsigned char* data = *out; size_t outsize; /*TODO: check if this works according to the statement in the documentation: "The converter can convert from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/ if (!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) && !(state->info_raw.bitdepth == 8)) { return 56; /*unsupported color mode conversion*/ } outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); *out = tvg::malloc(outsize); if (!(*out)) { state->error = 83; /*alloc fail*/ } else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h); tvg::free(data); } return state->error; } void lodepng_state_init(LodePNGState* state) { lodepng_decoder_settings_init(&state->decoder); lodepng_color_mode_init(&state->info_raw); lodepng_info_init(&state->info_png); state->error = 1; } void lodepng_state_cleanup(LodePNGState* state) { lodepng_color_mode_cleanup(&state->info_raw); lodepng_info_cleanup(&state->info_png); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/vp8i.h000664 001750 001750 00000025210 15164251010 034164 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // VP8 decoder: internal header. // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_DEC_VP8I_H_ #define WEBP_DEC_VP8I_H_ #include // for memcpy() #include "./common.h" #include "./vp8li.h" #include "../utils/bit_reader.h" #include "../utils/random.h" #include "../dsp/dsp.h" #ifdef __cplusplus extern "C" { #endif //------------------------------------------------------------------------------ // Various defines and enums // version numbers #define DEC_MAJ_VERSION 0 #define DEC_MIN_VERSION 4 #define DEC_REV_VERSION 3 // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). // Constraints are: We need to store one 16x16 block of luma samples (y), // and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned, // in order to be SIMD-friendly. We also need to store the top, left and // top-left samples (from previously decoded blocks), along with four // extra top-right samples for luma (intra4x4 prediction only). // One possible layout is, using 32 * (17 + 9) bytes: // // .+------ <- only 1 pixel high // .|yyyyt. // .|yyyyt. // .|yyyyt. // .|yyyy.. // .+--.+-- <- only 1 pixel high // .|uu.|vv // .|uu.|vv // // Every character is a 4x4 block, with legend: // '.' = unused // 'y' = y-samples 'u' = u-samples 'v' = u-samples // '|' = left sample, '-' = top sample, '+' = top-left sample // 't' = extra top-right sample for 4x4 modes #define YUV_SIZE (BPS * 17 + BPS * 9) #define Y_SIZE (BPS * 17) #define Y_OFF (BPS * 1 + 8) #define U_OFF (Y_OFF + BPS * 16 + BPS) #define V_OFF (U_OFF + 16) // minimal width under which lossy multi-threading is always disabled #define MIN_WIDTH_FOR_THREADS 512 //------------------------------------------------------------------------------ // Headers typedef struct { uint8_t key_frame_; uint8_t profile_; uint8_t show_; uint32_t partition_length_; } VP8FrameHeader; typedef struct { uint16_t width_; uint16_t height_; uint8_t xscale_; uint8_t yscale_; uint8_t colorspace_; // 0 = YCbCr uint8_t clamp_type_; } VP8PictureHeader; // segment features typedef struct { int use_segment_; int update_map_; // whether to update the segment map or not int absolute_delta_; // absolute or delta values for quantizer and filter int8_t quantizer_[NUM_MB_SEGMENTS]; // quantization changes int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments } VP8SegmentHeader; // probas associated to one of the contexts typedef uint8_t VP8ProbaArray[NUM_PROBAS]; typedef struct { // all the probas associated to one band VP8ProbaArray probas_[NUM_CTX]; } VP8BandProbas; // Struct collecting all frame-persistent probabilities. typedef struct { uint8_t segments_[MB_FEATURE_TREE_PROBS]; // Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4 VP8BandProbas bands_[NUM_TYPES][NUM_BANDS]; const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1]; } VP8Proba; // Filter parameters typedef struct { int simple_; // 0=complex, 1=simple int level_; // [0..63] int sharpness_; // [0..7] int use_lf_delta_; int ref_lf_delta_[NUM_REF_LF_DELTAS]; int mode_lf_delta_[NUM_MODE_LF_DELTAS]; } VP8FilterHeader; //------------------------------------------------------------------------------ // Information about the macroblocks. typedef struct { // filter specs uint8_t f_limit_; // filter limit in [3..189], or 0 if no filtering uint8_t f_ilevel_; // inner limit in [1..63] uint8_t f_inner_; // do inner filtering? uint8_t hev_thresh_; // high edge variance threshold in [0..2] } VP8FInfo; typedef struct { // Top/Left Contexts used for syntax-parsing uint8_t nz_; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma) uint8_t nz_dc_; // non-zero DC coeff (1bit) } VP8MB; // Dequantization matrices typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower). typedef struct { quant_t y1_mat_, y2_mat_, uv_mat_; int uv_quant_; // U/V quantizer value int dither_; // dithering amplitude (0 = off, max=255) } VP8QuantMatrix; // Data needed to reconstruct a macroblock typedef struct { int16_t coeffs_[384]; // 384 coeffs = (16+4+4) * 4*4 uint8_t is_i4x4_; // true if intra4x4 uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes uint8_t uvmode_; // chroma prediction mode // bit-wise info about the content of each sub-4x4 blocks (in decoding order). // Each of the 4x4 blocks for y/u/v is associated with a 2b code according to: // code=0 -> no coefficient // code=1 -> only DC // code=2 -> first three coefficients are non-zero // code=3 -> more than three coefficients are non-zero // This allows to call specialized transform functions. uint32_t non_zero_y_; uint32_t non_zero_uv_; uint8_t dither_; // local dithering strength (deduced from non_zero_*) uint8_t skip_; uint8_t segment_; } VP8MBData; // Persistent information needed by the parallel processing typedef struct { int id_; // cache row to process (in [0..2]) int mb_y_; // macroblock position of the row int filter_row_; // true if row-filtering is needed VP8FInfo* f_info_; // filter strengths (swapped with dec->f_info_) VP8MBData* mb_data_; // reconstruction data (swapped with dec->mb_data_) VP8Io io_; // copy of the VP8Io to pass to put() } VP8ThreadContext; // Saved top samples, per macroblock. Fits into a cache-line. typedef struct { uint8_t y[16], u[8], v[8]; } VP8TopSamples; //------------------------------------------------------------------------------ // VP8Decoder: the main opaque structure handed over to user struct VP8Decoder { VP8StatusCode status_; int ready_; // true if ready to decode a picture with VP8Decode() const char* error_msg_; // set when status_ is not OK. // Main data source VP8BitReader br_; // headers VP8FrameHeader frm_hdr_; VP8PictureHeader pic_hdr_; VP8FilterHeader filter_hdr_; VP8SegmentHeader segment_hdr_; int cache_id_; // current cache row int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3) VP8ThreadContext thread_ctx_; // Thread context // dimension, in macroblock units. int mb_w_, mb_h_; // Macroblock to process/filter, depending on cropping and filter_type. int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded // number of partitions. int num_parts_; // per-partition boolean decoders. VP8BitReader parts_[MAX_NUM_PARTITIONS]; // Dithering strength, deduced from decoding options int dither_; // whether to use dithering or not VP8Random dithering_rg_; // random generator for dithering // dequantization (one set of DC/AC dequant factor per segment) VP8QuantMatrix dqm_[NUM_MB_SEGMENTS]; // probabilities VP8Proba proba_; int use_skip_proba_; uint8_t skip_p_; // Boundary data cache and persistent buffers. uint8_t* intra_t_; // top intra modes values: 4 * mb_w_ uint8_t intra_l_[4]; // left intra modes values VP8TopSamples* yuv_t_; // top y/u/v samples VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1) VP8FInfo* f_info_; // filter strength info uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE) uint8_t* cache_y_; // macroblock row for storing unfiltered samples uint8_t* cache_u_; uint8_t* cache_v_; int cache_y_stride_; int cache_uv_stride_; // main memory chunk for the above data. Persistent. void* mem_; size_t mem_size_; // Per macroblock non-persistent infos. int mb_x_, mb_y_; // current position, in macroblock units VP8MBData* mb_data_; // parsed reconstruction data // Filtering side-info int filter_type_; // 0=off, 1=simple, 2=complex VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type // Alpha struct ALPHDecoder* alph_dec_; // alpha-plane decoder object const uint8_t* alpha_data_; // compressed alpha data (if present) size_t alpha_data_size_; int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_ uint8_t* alpha_plane_; // output. Persistent, contains the whole data. int alpha_dithering_; // derived from decoding options (0=off, 100=full). }; //------------------------------------------------------------------------------ // internal functions. Not public. // in vp8.c int VP8SetError(VP8Decoder* const dec, VP8StatusCode error, const char* const msg); // in tree.c void VP8ResetProba(VP8Proba* const proba); void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec); // parses one row of intra mode data in partition 0, returns !eof int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec); // in quant.c void VP8ParseQuant(VP8Decoder* const dec); // in frame.c int VP8InitFrame(VP8Decoder* const dec, VP8Io* io); // Call io->setup() and finish setting up scan parameters. // After this call returns, one must always call VP8ExitCritical() with the // same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK // if ok, otherwise sets and returns the error status on *dec. VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io); // Must always be called in pair with VP8EnterCritical(). // Returns false in case of error. int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io); // Initialize dithering post-process if needed. void VP8InitDithering(const WebPDecoderOptions* const options, VP8Decoder* const dec); // Process the last decoded row (filtering + output). int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io); // To be called at the start of a new scanline, to initialize predictors. void VP8InitScanline(VP8Decoder* const dec); // Decode one macroblock. Returns false if there is not enough data. int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br); // in alpha.c const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, int row, int num_rows); //------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_DEC_VP8I_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/rescaler.h000664 001750 001750 00000005743 15164251010 035435 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Rescaling functions // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_UTILS_RESCALER_H_ #define WEBP_UTILS_RESCALER_H_ #ifdef __cplusplus extern "C" { #endif #include "../webp/types.h" #define WEBP_RESCALER_RFIX 30 // fixed-point precision for multiplies // Structure used for on-the-fly rescaling typedef struct WebPRescaler WebPRescaler; struct WebPRescaler { int x_expand; // true if we're expanding in the x direction int num_channels; // bytes to jump between pixels int fy_scale, fx_scale; // fixed-point scaling factor int64_t fxy_scale; // '' // we need hpel-precise add/sub increments, for the downsampled U/V planes. int y_accum; // vertical accumulator int y_add, y_sub; // vertical increments (add ~= src, sub ~= dst) int x_add, x_sub; // horizontal increments (add ~= src, sub ~= dst) int src_width, src_height; // source dimensions int dst_width, dst_height; // destination dimensions uint8_t* dst; int dst_stride; int32_t* irow, *frow; // work buffer }; // Initialize a rescaler given scratch area 'work' and dimensions of src & dst. void WebPRescalerInit(WebPRescaler* const rescaler, int src_width, int src_height, uint8_t* const dst, int dst_width, int dst_height, int dst_stride, int num_channels, int x_add, int x_sub, int y_add, int y_sub, int32_t* const work); // Returns the number of input lines needed next to produce one output line, // considering that the maximum available input lines are 'max_num_lines'. int WebPRescaleNeededLines(const WebPRescaler* const rescaler, int max_num_lines); // Import multiple rows over all channels, until at least one row is ready to // be exported. Returns the actual number of lines that were imported. int WebPRescalerImport(WebPRescaler* const rescaler, int num_rows, const uint8_t* src, int src_stride); // Return true if there is pending output rows ready. static WEBP_INLINE int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) { return (rescaler->y_accum <= 0); } // Export as many rows as possible. Return the numbers of rows written. int WebPRescalerExport(WebPRescaler* const rescaler); //------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_UTILS_RESCALER_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlRenderTask.h000664 001750 001750 00000021162 15164251010 036703 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_RENDER_TASK_H_ #define _TVG_GL_RENDER_TASK_H_ #include "tvgGlCommon.h" #include "tvgGlProgram.h" struct GlVertexLayout { uint32_t index; uint32_t size; uint32_t stride; size_t offset; }; enum class GlBindingType { kUniformBuffer, kTexture, }; struct GlBindingResource { GlBindingType type; /** * Binding point index. * Can be a uniform location for a texture * Can be a uniform buffer binding index for a uniform block */ uint32_t bindPoint = 0; GLint location = 0; GLuint gBufferId = 0; uint32_t bufferOffset = 0; uint32_t bufferRange = 0; GlBindingResource() = default; GlBindingResource(uint32_t index, GLint location, GLuint bufferId, uint32_t offset, uint32_t range) : type(GlBindingType::kUniformBuffer), bindPoint(index), location(location), gBufferId(bufferId), bufferOffset(offset), bufferRange(range) { } GlBindingResource(uint32_t bindPoint, GLuint texId, GLint location) : type(GlBindingType::kTexture), bindPoint(bindPoint), location(location), gBufferId(texId) { } }; class GlRenderTask { public: GlRenderTask(GlProgram* program): mProgram(program) {} GlRenderTask(GlProgram* program, GlRenderTask* other); virtual ~GlRenderTask() = default; virtual void run(); void addVertexLayout(const GlVertexLayout& layout); void addBindResource(const GlBindingResource& binding); void setDrawRange(uint32_t offset, uint32_t count); void setViewport(const RenderRegion& viewport); void setDrawDepth(int32_t depth) { mDrawDepth = static_cast(depth); } void setViewMatrix(const Matrix& matrix) { mViewMatrix = matrix; mUseViewMatrix = true; } virtual void normalizeDrawDepth(int32_t maxDepth) { mDrawDepth /= static_cast(maxDepth); } GlProgram* getProgram() { return mProgram; } const RenderRegion& getViewport() const { return mViewport; } float getDrawDepth() const { return mDrawDepth; } private: GlProgram* mProgram; RenderRegion mViewport = {}; uint32_t mIndexOffset = {}; uint32_t mIndexCount = {}; Array mVertexLayout = {}; Array mBindingResources = {}; float mDrawDepth = 0.f; Matrix mViewMatrix = {}; bool mUseViewMatrix = false; }; class GlStencilCoverTask : public GlRenderTask { public: GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode); ~GlStencilCoverTask() override; void run() override; void normalizeDrawDepth(int32_t maxDepth) override; private: GlRenderTask* mStencilTask; GlRenderTask* mCoverTask; GlStencilMode mStencilMode; }; struct GlRenderTarget; class GlComposeTask : public GlRenderTask { public: GlComposeTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array&& tasks); ~GlComposeTask() override; void run() override; void setRenderSize(uint32_t width, uint32_t height) { mRenderWidth = width; mRenderHeight = height; } bool mClearBuffer = true; protected: GLuint getTargetFbo() { return mTargetFbo; } GLuint getSelfFbo(); GLuint getResolveFboId(); void onResolve(); private: GLuint mTargetFbo; GlRenderTarget* mFbo; Array mTasks; uint32_t mRenderWidth = 0; uint32_t mRenderHeight = 0; }; class GlBlitTask : public GlComposeTask { public: GlBlitTask(GlProgram*, GLuint target, GlRenderTarget* fbo, Array&& tasks); ~GlBlitTask() override = default; void run() override; GLuint getColorTexture() const { return mColorTex; } void setTargetViewport(const RenderRegion& vp) { mTargetViewport = vp; } private: GLuint mColorTex = 0; RenderRegion mTargetViewport = {}; }; class GlDrawBlitTask : public GlComposeTask { public: GlDrawBlitTask(GlProgram*, GLuint target, GlRenderTarget* fbo, Array&& tasks); ~GlDrawBlitTask() override; void setPrevTask(GlRenderTask* task) { mPrevTask = task; } void setParentSize(uint32_t width, uint32_t height) { mParentWidth = width; mParentHeight = height; } void run() override; private: GlRenderTask* mPrevTask = nullptr; uint32_t mParentWidth = 0; uint32_t mParentHeight = 0; }; class GlSceneBlendTask : public GlComposeTask { public: GlSceneBlendTask(GlProgram*, GLuint target, GlRenderTarget* fbo, Array&& tasks); ~GlSceneBlendTask() override; void setParentSize(uint32_t width, uint32_t height) { mParentWidth = width; mParentHeight = height; } void setSrcTarget(GlRenderTarget* srcFbo) { mSrcFbo = srcFbo; } void setDstCopy(GlRenderTarget* dstCopyFbo) { mDstCopyFbo = dstCopyFbo; } void run() override; private: GlRenderTarget* mSrcFbo = nullptr; GlRenderTarget* mDstCopyFbo = nullptr; uint32_t mParentWidth = 0; uint32_t mParentHeight = 0; }; class GlClipTask : public GlRenderTask { public: GlClipTask(GlRenderTask* clip, GlRenderTask* mask); ~GlClipTask() override; void run() override; void normalizeDrawDepth(int32_t maxDepth) override; private: GlRenderTask* mClipTask; GlRenderTask* mMaskTask; }; class GlDirectBlendTask : public GlRenderTask { public: GlDirectBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, const RenderRegion& copyRegion); ~GlDirectBlendTask() override = default; void run() override; private: GlRenderTarget* mDstFbo = nullptr; GlRenderTarget* mDstCopyFbo = nullptr; RenderRegion mCopyRegion{}; }; class GlComplexBlendTask: public GlRenderTask { public: GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask); ~GlComplexBlendTask() override; void run() override; void normalizeDrawDepth(int32_t maxDepth) override; private: GlRenderTarget* mDstFbo; GlRenderTarget* mDstCopyFbo; GlRenderTask* mStencilTask; GlComposeTask* mComposeTask; }; class GlGaussianBlurTask: public GlRenderTask { public: GlGaussianBlurTask(GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo0, GlRenderTarget* dstCopyFbo1): GlRenderTask(nullptr), mDstFbo(dstFbo), mDstCopyFbo0(dstCopyFbo0), mDstCopyFbo1(dstCopyFbo1) {}; ~GlGaussianBlurTask(){ delete horzTask; delete vertTask; }; void run() override; GlRenderTask* horzTask; GlRenderTask* vertTask; RenderEffectGaussianBlur* effect; private: GlRenderTarget* mDstFbo; GlRenderTarget* mDstCopyFbo0; GlRenderTarget* mDstCopyFbo1; }; class GlEffectDropShadowTask: public GlRenderTask { public: GlEffectDropShadowTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo0, GlRenderTarget* dstCopyFbo1): GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo0(dstCopyFbo0), mDstCopyFbo1(dstCopyFbo1) {}; ~GlEffectDropShadowTask(){ delete horzTask; delete vertTask; }; void run() override; GlRenderTask* horzTask; GlRenderTask* vertTask; RenderEffectDropShadow* effect; private: GlRenderTarget* mDstFbo; GlRenderTarget* mDstCopyFbo0; GlRenderTarget* mDstCopyFbo1; }; class GlEffectColorTransformTask: public GlRenderTask { public: GlEffectColorTransformTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo): GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo) {}; ~GlEffectColorTransformTask() {}; void run() override; private: GlRenderTarget* mDstFbo; GlRenderTarget* mDstCopyFbo; }; #endif /* _TVG_GL_RENDER_TASK_H_ */ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/msinttypes/inttypes.h000664 001750 001750 00000020264 15164251010 041122 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules// 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 #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_ ] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/000775 001750 001750 00000000000 15164251010 030004 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_jpg/000775 001750 001750 00000000000 15164251010 034117 5ustar00ddennedyddennedy000000 000000 thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-map-prototype.cpp000664 001750 001750 00000004327 15164251010 051222 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-container-object.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH #define BUILTIN_INC_HEADER_NAME "ecma-builtin-map-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID map_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup map ECMA Map object built-in * @{ */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_map_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); return ecma_builtin_container_dispatch_routine (builtin_routine_id, this_arg, arguments_list_p, LIT_MAGIC_STRING_MAP_UL); } /* ecma_builtin_map_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-number.inc.h000664 001750 001750 00000005730 15164251010 050106 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Number built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_NUMBER /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* ECMA-262 v5, 15.7.3.4 */ NUMBER_VALUE (LIT_MAGIC_STRING_NAN, ECMA_BUILTIN_NUMBER_NAN, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.7.3.2 */ NUMBER_VALUE (LIT_MAGIC_STRING_MAX_VALUE_U, ECMA_BUILTIN_NUMBER_MAX, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.7.3.3 */ NUMBER_VALUE (LIT_MAGIC_STRING_MIN_VALUE_U, ECMA_BUILTIN_NUMBER_MIN, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.7.3.5 */ NUMBER_VALUE (LIT_MAGIC_STRING_POSITIVE_INFINITY_U, ECMA_BUILTIN_NUMBER_POSITIVE_INFINITY, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.7.3.6 */ NUMBER_VALUE (LIT_MAGIC_STRING_NEGATIVE_INFINITY_U, ECMA_BUILTIN_NUMBER_NEGATIVE_INFINITY, ECMA_PROPERTY_FIXED) /* ECMA-262 v6, 20.1.2.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_EPSILON_U, ECMA_BUILTIN_NUMBER_EPSILON, ECMA_PROPERTY_FIXED) /* ECMA-262 v6, 20.1.2.6 */ NUMBER_VALUE (LIT_MAGIC_STRING_MAX_SAFE_INTEGER_U, ECMA_BUILTIN_NUMBER_MAX_SAFE_INTEGER, ECMA_PROPERTY_FIXED) /* ECMA-262 v6, 20.1.2.8 */ NUMBER_VALUE (LIT_MAGIC_STRING_MIN_SAFE_INTEGER_U, ECMA_BUILTIN_NUMBER_MIN_SAFE_INTEGER, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_NUMBER_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.7.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_NUMBER_PROTOTYPE, ECMA_PROPERTY_FIXED) #endif /* JERRY_BUILTIN_NUMBER */ /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_IS_FINITE, ECMA_NUMBER_OBJECT_ROUTINE_IS_FINITE, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_NAN, ECMA_NUMBER_OBJECT_ROUTINE_IS_NAN, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_INTEGER, ECMA_NUMBER_OBJECT_ROUTINE_IS_INTEGER, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_SAFE_INTEGER, ECMA_NUMBER_OBJECT_ROUTINE_IS_SAFE_INTEGER, 1, 1) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_PARSE_FLOAT, LIT_MAGIC_STRING_PARSE_FLOAT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_PARSE_INT, LIT_MAGIC_STRING_PARSE_INT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-string-iterator-prototype.inc.h000664 001750 001750 00000002162 15164251010 054012 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %StringIteratorPrototype% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_STRING_ITERATOR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_NEXT, ECMA_BUILTIN_STRING_ITERATOR_PROTOTYPE_OBJECT_NEXT, 0, 0) #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/slot.lot000664 001750 001750 00000026351 15164251010 033716 0ustar00ddennedyddennedy000000 000000 {"v":"5.8.1","fr":60,"ip":0,"op":180,"w":800,"h":800,"nm":"Unified Slot Test","ddd":0,"assets":[{"id":"image_0","w":200,"h":300,"u":"images/","p":"img_0.png","e":0,"sid":"path_img"}],"fonts":{"list":[{"fPath":"","fFamily":"Ubuntu","fStyle":"Light Italic","fName":"Ubuntu Light Italic","origin":3}]},"slots":{},"layers":[{"ind":1,"ty":4,"nm":"GradientFillShape","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":120,"s":[360]}]},"p":{"a":0,"k":[275,275,0]},"a":{"a":0,"k":[-7.886,88.719,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[282.019,134.888]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"R"},{"ty":"st","c":{"a":0,"k":[0.991,0,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":3},"lc":1,"lj":1,"ml":4,"nm":"S"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":9,"k":{"a":0,"k":[0,0.514,0.373,0.984,0.141,0.478,0.412,0.984,0.283,0.443,0.451,0.984,0.379,0.408,0.49,0.984,0.475,0.373,0.529,0.984,0.606,0.278,0.647,0.925,0.737,0.184,0.765,0.867,0.868,0.092,0.882,0.808,1,0,1,0.749]},"sid":"gradient_fill"},"s":{"a":0,"k":[-159.51,23.531]},"e":{"a":0,"k":[183.084,8.059]},"t":1,"nm":"G"},{"ty":"tr","p":{"a":0,"k":[-7.886,88.719]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"R"}],"ip":0,"op":300,"st":0},{"ddd":0,"ind":2,"ty":3,"nm":"IconNull","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[400,400,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.475,0.475,0.833],"y":[1,1,1]},"o":{"x":[0.42,0.42,0.167],"y":[0,0,0]},"t":0,"s":[20,20,100]},{"i":{"x":[0.577,0.577,0.833],"y":[1,1,1]},"o":{"x":[0.32,0.32,0.167],"y":[0,0,0]},"t":5,"s":[18.113,18.113,100]},{"i":{"x":[0.482,0.482,0.833],"y":[1.02,1.02,1]},"o":{"x":[0.366,0.366,0.167],"y":[0,0,0]},"t":12,"s":[20.681,20.681,100]},{"t":20,"s":[20,20,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":20,"st":-120,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".lottie-icon-gradient","cl":"lottie-icon-gradient","parent":2,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[20]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[60]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.125,0.125,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[1000,1000,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.495,-1.003],[0,0],[0,0],[-0.801,-0.781],[0,0],[0,0],[-0.99,0.521],[0,0],[0,0],[0.189,1.103],[0,0],[0,0],[1.107,0.161],[0,0]],"o":[[-0.495,-1.003],[0,0],[0,0],[-1.107,0.161],[0,0],[0,0],[-0.189,1.103],[0,0],[0,0],[0.99,0.521],[0,0],[0,0],[0.801,-0.781],[0,0],[0,0]],"v":[[1.211,-8.823],[-1.211,-8.823],[-3.569,-4.045],[-8.841,-3.279],[-9.589,-0.976],[-5.774,2.743],[-6.675,7.994],[-4.716,9.417],[0,6.938],[4.716,9.417],[6.675,7.994],[5.774,2.743],[9.589,-0.976],[8.841,-3.279],[3.569,-4.045]],"c":true},"ix":2},"nm":"Path3","mn":"ADBEVectorShape-Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0.334,0.202,0.549,0.852,0.557,0.219,0.41,0.761,0.699,0.235,0.271,0.671],"sid":"lottie-icon-gradient","ix":9}},"s":{"a":0,"k":[-10.849,8.931],"ix":5},"e":{"a":0,"k":[-0.523,-5.049],"ix":6},"t":1,"nm":"GradientFill1","mn":"ADBEVectorGraphic-G-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":"Shape2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBEVectorGroup","hd":false}],"ip":3,"op":60,"st":-16,"bm":8},{"ddd":0,"ind":4,"ty":4,"nm":".lottie-icon-solid","cl":"lottie-icon-solid","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.125,0.125,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"t":3,"s":[1000,1000,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.495,-1.003],[0,0],[0,0],[-0.801,-0.781],[0,0],[0,0],[-0.99,0.521],[0,0],[0,0],[0.189,1.103],[0,0],[0,0],[1.107,0.161],[0,0]],"o":[[-0.495,-1.003],[0,0],[0,0],[-1.107,0.161],[0,0],[0,0],[-0.189,1.103],[0,0],[0,0],[0.99,0.521],[0,0],[0,0],[0.801,-0.781],[0,0],[0,0]],"v":[[1.211,-8.823],[-1.211,-8.823],[-3.569,-4.045],[-8.841,-3.279],[-9.589,-0.976],[-5.774,2.743],[-6.675,7.994],[-4.716,9.417],[0,6.938],[4.716,9.417],[6.675,7.994],[5.774,2.743],[9.589,-0.976],[8.841,-3.279],[3.569,-4.045]],"c":true},"ix":2},"nm":"Path3","mn":"ADBEVectorShape-Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":21,"s":[0.356862745098,0.372549019608,0.780392156863,1]},{"t":60,"s":[0.058823529631,0.423529416323,0.741176486015,1]}],"sid":"lottie-icon-solid","ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill1","mn":"ADBEVectorGraphic-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":"Shape","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBEVectorGroup","hd":false}],"ip":2,"op":60,"st":-17,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".lottie-icon-outline","cl":"lottie-icon-outline","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.125,0.125,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[1000,1000,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.495,-1.003],[0,0],[0,0],[-0.801,-0.781],[0,0],[0,0],[-0.99,0.521],[0,0],[0,0],[0.189,1.103],[0,0],[0,0],[1.107,0.161],[0,0]],"o":[[-0.495,-1.003],[0,0],[0,0],[-1.107,0.161],[0,0],[0,0],[-0.189,1.103],[0,0],[0,0],[0.99,0.521],[0,0],[0,0],[0.801,-0.781],[0,0],[0,0]],"v":[[1.211,-8.823],[-1.211,-8.823],[-3.569,-4.045],[-8.841,-3.279],[-9.589,-0.976],[-5.774,2.743],[-6.675,7.994],[-4.716,9.417],[0,6.938],[4.716,9.417],[6.675,7.994],[5.774,2.743],[9.589,-0.976],[8.841,-3.279],[3.569,-4.045]],"c":true},"ix":2},"nm":"Path1","mn":"ADBEVectorShape-Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-0.197,0.398],[0,0],[0,0],[-0.44,-0.064],[0,0],[0,0],[-0.075,-0.438],[0,0],[0,0],[0.393,-0.207],[0,0],[0,0],[0.318,0.31],[0,0],[0,0]],"o":[[0,0],[0,0],[0.197,0.398],[0,0],[0,0],[-0.318,0.31],[0,0],[0,0],[-0.393,-0.207],[0,0],[0,0],[0.075,-0.438],[0,0],[0,0],[0.44,-0.064]],"v":[[-2.258,-3.31],[0,-7.886],[2.258,-3.31],[3.275,-2.572],[8.325,-1.838],[4.67,1.724],[4.282,2.919],[5.145,7.948],[0.628,5.573],[-0.628,5.573],[-5.145,7.948],[-4.282,2.919],[-4.671,1.724],[-8.325,-1.838],[-3.275,-2.572]],"c":true},"ix":2},"nm":"Path2","mn":"ADBEVectorShape-Group","hd":false},{"ty":"mm","mm":1,"nm":"MergePaths1","mn":"ADBEVectorFilter-Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258823529412,0.258823529412,0.258823529412,1],"sid":"lottie-icon-outline","ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill1","mn":"ADBEVectorGraphic-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":"Shape","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBEVectorGroup","hd":false}],"ip":0,"op":4,"st":-17,"bm":0},{"ddd":0,"ty":4,"ind":6,"st":0,"ip":0,"op":180,"nm":"Anchor","mn":"{04f9b742-3603-49fa-9552-ea04de1a3f33}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256],"sid":"transform_id"},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"el","nm":"Ellipse","mn":"{4251e46a-bb13-464b-913c-e67c44a218da}","p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[32,32]}},{"ty":"fl","nm":"Fill","mn":"{7d3070ed-88a3-41aa-a62e-7db8df1bd312}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.9411764705882353,0.11372549019607843,0.0392156862745098]},"r":1}]},{"ddd":0,"ty":4,"ind":7,"st":0,"ip":0,"op":180,"nm":"Transformed","mn":"{d00298c4-66b4-4ae4-a730-22c1eb85c188}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"shapes":[{"ty":"rc","nm":"Rectangle1","mn":"{bf8ad877-113b-4df8-a2e2-3bb4af32edf7}","p":{"a":0,"k":[252.75223880597017,250.60298507462684]},"s":{"a":0,"k":[319.8089552238806,330.98507462686564]},"r":{"a":0,"k":0}},{"ty":"fl","nm":"Fill1","mn":"{b9040dc8-0753-4a6e-b5f1-d508d17bbd4f}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1}]},{"ddd":0,"ty":4,"ind":8,"st":0,"ip":0,"op":180,"nm":"Reference","mn":"{8f351be7-8a51-4310-9dc3-59ed21594815}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"rc","nm":"Rectangle","mn":"{cb4f7b74-bed1-493b-a0e6-01b00566aedd}","p":{"a":0,"k":[252.75223880597017,250.60298507462684]},"s":{"a":0,"k":[319.8089552238806,330.98507462686564]},"r":{"a":0,"k":0}},{"ty":"fl","nm":"Fill","mn":"{05064670-7e14-4141-89c1-e0f0f3a1c57d}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.1607843137254902,0.1843137254901961,0.4588235294117647]},"r":1}]},{"ddd":0,"ty":4,"ind":9,"st":0,"ip":0,"op":180,"nm":"ExpressionLayer","mn":"{85f37d8b-1792-4a4f-82d2-1b3b6d829c07}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Group","it":[{"ty":"rc","nm":"Rectangle","p":{"a":0,"k":[250,227]},"s":{"a":0,"k":[334,239]},"r":{"a":0,"k":0}},{"ty":"st","nm":"Stroke","mn":"{0930ce27-c8f9-4371-b0cf-111a859abfaf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"tr","a":{"a":0,"k":[249.3134328358209,254.47164179104476],"sid":"rect_anchor_point"},"p":{"a":0,"k":[249.3134328358209,254.47164179104476],"sid":"rect_position"},"s":{"a":0,"k":[100,100],"sid":"rect_scale"},"r":{"a":0,"k":0,"sid":"rect_rotation"},"o":{"a":0,"k":100,"sid":"rect_opacity"},"sk":{"a":0,"k":180,"sid":"rect_skew"},"sa":{"a":0,"k":0,"sid":"rect_skew_angle"}}]}]},{"nm":"TextLayer","ty":5,"ind":10,"sr":1,"ks":{"p":{"k":[5,80],"a":0}},"ip":0,"op":120,"st":0,"t":{"a":[],"d":{"k":[{"s":{"f":"Ubuntu Light Italic","fc":[0,0,0],"s":100,"t":"Hello","j":0},"t":0}],"sid":"text_doc"},"m":{"a":{"k":[0,0],"a":0},"sid":"text_alignment"},"p":{}}},{"ddd":0,"ind":11,"ty":2,"nm":"img_0.png","cl":"png","refId":"image_0","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":[56]},{"t":50}],"ix":10},"p":{"a":0,"k":[400,400,0],"ix":2},"a":{"a":0,"k":[100,150,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":[99,99,100],"e":[152,152,100]},{"t":50}],"ix":6}},"ao":0,"ip":0,"op":60,"st":0,"bm":0}],"markers":[],"meta":{"g":"Unified Slot Test"}}glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/include/jerryscript.h000664 001750 001750 00000001722 15164251010 043475 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JERRYSCRIPT_H #define JERRYSCRIPT_H /** * Major version of JerryScript API. */ #define JERRY_API_MAJOR_VERSION 3 /** * Minor version of JerryScript API. */ #define JERRY_API_MINOR_VERSION 0 /** * Patch version of JerryScript API. */ #define JERRY_API_PATCH_VERSION 0 #include "jerryscript-core.h" #endif /* !JERRYSCRIPT_H */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jmem/jmem-allocator-internal.h000664 001750 001750 00000003457 15164251010 045151 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JMEM_ALLOCATOR_INTERNAL_H #define JMEM_ALLOCATOR_INTERNAL_H #ifndef JMEM_ALLOCATOR_INTERNAL #error "The header is for internal routines of memory allocator component. Please, don't use the routines directly." #endif /* !JMEM_ALLOCATOR_INTERNAL */ /** \addtogroup mem Memory allocation * @{ */ /** * @{ * Valgrind-related options and headers */ #define JMEM_VALGRIND_NOACCESS_SPACE(p, s) #define JMEM_VALGRIND_UNDEFINED_SPACE(p, s) #define JMEM_VALGRIND_DEFINED_SPACE(p, s) #define JMEM_VALGRIND_MALLOCLIKE_SPACE(p, s) #define JMEM_VALGRIND_RESIZE_SPACE(p, o, n) #define JMEM_VALGRIND_FREELIKE_SPACE(p) /** @} */ void jmem_heap_init (void); void jmem_heap_finalize (void); bool jmem_is_heap_pointer (const void *pointer); void *jmem_heap_alloc_block_internal (const size_t size); void jmem_heap_free_block_internal (void *ptr, const size_t size); /** * \addtogroup poolman Memory pool manager * @{ */ void jmem_pools_finalize (void); /** * @} * @} */ /** * @{ * Jerry mem-stat definitions */ #define JMEM_HEAP_STAT_INIT() #define JMEM_HEAP_STAT_ALLOC(v1) JERRY_UNUSED (v1) #define JMEM_HEAP_STAT_FREE(v1) JERRY_UNUSED (v1) /** @} */ #endif /* !JMEM_ALLOCATOR_INTERNAL_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgAllocator.h000664 001750 001750 00000003522 15164251010 034110 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_ALLOCATOR_H_ #define _TVG_ALLOCATOR_H_ #include #include //separate memory alloators for clean customization namespace tvg { template static inline T* malloc(size_t size) { return static_cast(std::malloc(size)); } template static inline T* calloc(size_t nmem, size_t size) { return static_cast(std::calloc(nmem, size)); } template static inline T* realloc(T* ptr, size_t size) { return static_cast(std::realloc(ptr, size)); } template static inline void free(T* ptr) { std::free(ptr); } } #endif //_TVG_ALLOCATOR_H_src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakset-prototype.cpp000664 001750 001750 00000004525 15164251010 052110 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-container-object.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH #define BUILTIN_INC_HEADER_NAME "ecma-builtin-weakset-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID weakset_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup weakset ECMA WeakSet object built-in * @{ */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_weakset_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); return ecma_builtin_container_dispatch_routine (builtin_routine_id, this_arg, arguments_list_p, LIT_MAGIC_STRING_WEAKSET_UL); } /* ecma_builtin_weakset_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwMath.cpp000664 001750 001750 00000017476 15164251010 036160 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgSwCommon.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static float TO_RADIAN(int64_t angle) { return (float(angle) / 65536.0f) * (MATH_PI / 180.0f); } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ int64_t mathMean(int64_t angle1, int64_t angle2) { return angle1 + mathDiff(angle1, angle2) / 2; } int mathCubicAngle(const SwPoint* base, int64_t& angleIn, int64_t& angleMid, int64_t& angleOut) { auto d1 = base[2] - base[3]; auto d2 = base[1] - base[2]; auto d3 = base[0] - base[1]; if (d1.tiny()) { if (d2.tiny()) { if (d3.tiny()) { angleIn = angleMid = angleOut = 0; return -1; //ignoreable } else { angleIn = angleMid = angleOut = mathAtan(d3); } } else { if (d3.tiny()) { angleIn = angleMid = angleOut = mathAtan(d2); } else { angleIn = angleMid = mathAtan(d2); angleOut = mathAtan(d3); } } } else { if (d2.tiny()) { if (d3.tiny()) { angleIn = angleMid = angleOut = mathAtan(d1); } else { angleIn = mathAtan(d1); angleOut = mathAtan(d3); angleMid = mathMean(angleIn, angleOut); } } else { if (d3.tiny()) { angleIn = mathAtan(d1); angleMid = angleOut = mathAtan(d2); } else { angleIn = mathAtan(d1); angleMid = mathAtan(d2); angleOut = mathAtan(d3); } } } auto theta1 = abs(mathDiff(angleIn, angleMid)); auto theta2 = abs(mathDiff(angleMid, angleOut)); if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return 0; //small size return 1; } int64_t mathMultiply(int64_t a, int64_t b) { int32_t s = 1; //move sign if (a < 0) { a = -a; s = -s; } if (b < 0) { b = -b; s = -s; } int64_t c = (a * b + 0x8000L) >> 16; return (s > 0) ? c : -c; } int64_t mathDivide(int64_t a, int64_t b) { int32_t s = 1; //move sign if (a < 0) { a = -a; s = -s; } if (b < 0) { b = -b; s = -s; } int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL; return (s < 0 ? -q : q); } int64_t mathMulDiv(int64_t a, int64_t b, int64_t c) { int32_t s = 1; //move sign if (a < 0) { a = -a; s = -s; } if (b < 0) { b = -b; s = -s; } if (c < 0) { c = -c; s = -s; } int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL; return (s > 0 ? d : -d); } void mathRotate(SwPoint& pt, int64_t angle) { if (angle == 0 || pt.zero()) return; Point v = pt.toPoint(); auto radian = TO_RADIAN(angle); auto cosv = cosf(radian); auto sinv = sinf(radian); pt.x = int32_t(nearbyint((v.x * cosv - v.y * sinv) * 64.0f)); pt.y = int32_t(nearbyint((v.x * sinv + v.y * cosv) * 64.0f)); } int64_t mathTan(int64_t angle) { if (angle == 0) return 0; return int64_t(tanf(TO_RADIAN(angle)) * 65536.0f); } int64_t mathAtan(const SwPoint& pt) { if (pt.zero()) return 0; return int64_t(tvg::atan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f); } int64_t mathSin(int64_t angle) { if (angle == 0) return 0; return mathCos(SW_ANGLE_PI2 - angle); } int64_t mathCos(int64_t angle) { return int64_t(cosf(TO_RADIAN(angle)) * 65536.0f); } int64_t mathLength(const SwPoint& pt) { if (pt.zero()) return 0; //trivial case if (pt.x == 0) return abs(pt.y); if (pt.y == 0) return abs(pt.x); auto v = pt.toPoint(); //return static_cast(sqrtf(v.x * v.x + v.y * v.y) * 65536.0f); /* 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. */ if (v.x < 0) v.x = -v.x; if (v.y < 0) v.y = -v.y; return static_cast((v.x > v.y) ? (v.x + v.y * 0.375f) : (v.y + v.x * 0.375f)); } void mathSplitCubic(SwPoint* base) { int32_t 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) >> 1; base[5].x = b = (base[3].x + d) >> 1; c = (c + d) >> 1; base[2].x = a = (a + c) >> 1; base[4].x = b = (b + c) >> 1; base[3].x = (a + b) >> 1; base[6].y = base[3].y; c = base[1].y; d = base[2].y; base[1].y = a = (base[0].y + c) >> 1; base[5].y = b = (base[3].y + d) >> 1; c = (c + d) >> 1; base[2].y = a = (a + c) >> 1; base[4].y = b = (b + c) >> 1; base[3].y = (a + b) >> 1; } void mathSplitLine(SwPoint* base) { base[2] = base[1]; base[1] = {(base[0].x >> 1) + (base[1].x >> 1), (base[0].y >> 1) + (base[1].y >> 1)}; } int64_t mathDiff(int64_t angle1, int64_t angle2) { auto delta = angle2 - angle1; delta %= SW_ANGLE_2PI; if (delta < 0) delta += SW_ANGLE_2PI; if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI; return delta; } SwPoint mathTransform(const Point* to, const Matrix& transform) { auto tx = to->x * transform.e11 + to->y * transform.e12 + transform.e13; auto ty = to->x * transform.e21 + to->y * transform.e22 + transform.e23; return {TO_SWCOORD(tx), TO_SWCOORD(ty)}; } bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack) { if (!outline || outline->pts.empty() || outline->cntrs.empty()) { renderBox.reset(); return false; } auto pt = outline->pts.begin(); auto xMin = pt->x; auto xMax = pt->x; auto yMin = pt->y; auto yMax = pt->y; for (++pt; pt < outline->pts.end(); ++pt) { if (xMin > pt->x) xMin = pt->x; if (xMax < pt->x) xMax = pt->x; if (yMin > pt->y) yMin = pt->y; if (yMax < pt->y) yMax = pt->y; } if (fastTrack) { renderBox.min = {int32_t(round(xMin / 64.0f)), int32_t(round(yMin / 64.0f))}; renderBox.max = {int32_t(round(xMax / 64.0f)), int32_t(round(yMax / 64.0f))}; } else { renderBox.min = {xMin >> 6, yMin >> 6}; renderBox.max = {(xMax + 63) >> 6, (yMax + 63) >> 6}; } renderBox.intersect(clipBox); return renderBox.valid(); } external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-mem.cpp000664 001750 001750 00000050344 15164251010 044423 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "js-parser-internal.h" #if JERRY_PARSER /** \addtogroup mem Memory allocation * @{ * * \addtogroup mem_parser Parser memory manager * @{ */ /**********************************************************************/ /* Memory allocation */ /**********************************************************************/ /** * Allocate memory. * * @return allocated memory. */ void * parser_malloc (parser_context_t *context_p, /**< context */ size_t size) /**< size of the memory block */ { void *result; JERRY_ASSERT (size > 0); result = jmem_heap_alloc_block_null_on_error (size); if (result == NULL) { parser_raise_error (context_p, PARSER_ERR_OUT_OF_MEMORY); } return result; } /* parser_malloc */ /** * Free memory allocated by parser_malloc. */ void parser_free (void *ptr, /**< pointer to free */ size_t size) /**< size of the memory block */ { jmem_heap_free_block (ptr, size); } /* parser_free */ /** * Allocate local memory for short term use. * * @return allocated memory. */ void * parser_malloc_local (parser_context_t *context_p, /**< context */ size_t size) /**< size of the memory */ { void *result; JERRY_ASSERT (size > 0); result = jmem_heap_alloc_block (size); if (result == 0) { parser_raise_error (context_p, PARSER_ERR_OUT_OF_MEMORY); } return result; } /* parser_malloc_local */ /** * Free memory allocated by parser_malloc_local. */ void parser_free_local (void *ptr, /**< pointer to free */ size_t size) /**< size of the memory */ { jmem_heap_free_block (ptr, size); } /* parser_free_local */ /** * Free the dynamically allocated buffer stored in the context */ void parser_free_allocated_buffer (parser_context_t *context_p) /**< context */ { if (context_p->u.allocated_buffer_p != NULL) { parser_free_local (context_p->u.allocated_buffer_p, context_p->allocated_buffer_size); context_p->u.allocated_buffer_p = NULL; } } /* parser_free_allocated_buffer */ /**********************************************************************/ /* Parser data management functions */ /**********************************************************************/ /** * Initialize parse data. */ static void parser_data_init (parser_mem_data_t *data_p, /**< memory manager */ uint32_t page_size) /**< size of each page */ { data_p->first_p = NULL; data_p->last_p = NULL; data_p->last_position = page_size; } /* parser_data_init */ /** * Free parse data. */ static void parser_data_free (parser_mem_data_t *data_p, /**< memory manager */ uint32_t page_size) /**< size of each page */ { parser_mem_page_t *page_p = data_p->first_p; while (page_p != NULL) { parser_mem_page_t *next_p = page_p->next_p; parser_free (page_p, page_size); page_p = next_p; } } /* parser_data_free */ /**********************************************************************/ /* Parser byte stream management functions */ /**********************************************************************/ /** * Initialize byte stream. */ void parser_cbc_stream_init (parser_mem_data_t *data_p) /**< memory manager */ { parser_data_init (data_p, PARSER_CBC_STREAM_PAGE_SIZE); } /* parser_cbc_stream_init */ /** * Free byte stream. */ void parser_cbc_stream_free (parser_mem_data_t *data_p) /**< memory manager */ { parser_data_free (data_p, sizeof (parser_mem_page_t *) + PARSER_CBC_STREAM_PAGE_SIZE); } /* parser_cbc_stream_free */ /** * Appends a byte at the end of the byte stream. */ void parser_cbc_stream_alloc_page (parser_context_t *context_p, /**< context */ parser_mem_data_t *data_p) /**< memory manager */ { size_t size = sizeof (parser_mem_page_t *) + PARSER_CBC_STREAM_PAGE_SIZE; parser_mem_page_t *page_p = (parser_mem_page_t *) parser_malloc (context_p, size); page_p->next_p = NULL; data_p->last_position = 0; if (data_p->last_p != NULL) { data_p->last_p->next_p = page_p; } else { data_p->first_p = page_p; } data_p->last_p = page_p; } /* parser_cbc_stream_alloc_page */ /**********************************************************************/ /* Parser list management functions */ /**********************************************************************/ /** * Initialize parser list. */ void parser_list_init (parser_list_t *list_p, /**< parser list */ uint32_t item_size, /**< size for each page */ uint32_t item_count) /**< number of items on each page */ { /* Align to pointer size. */ item_size = (uint32_t) (((item_size) + sizeof (void *) - 1) & ~(sizeof (void *) - 1)); parser_data_init (&list_p->data, item_size * item_count); list_p->page_size = item_size * item_count; list_p->item_size = item_size; list_p->item_count = item_count; } /* parser_list_init */ /** * Free parser list. */ void parser_list_free (parser_list_t *list_p) /**< parser list */ { parser_data_free (&list_p->data, (uint32_t) (sizeof (parser_mem_page_t *) + list_p->page_size)); } /* parser_list_free */ /** * Reset parser list. */ void parser_list_reset (parser_list_t *list_p) /**< parser list */ { parser_data_init (&list_p->data, list_p->page_size); } /* parser_list_reset */ /** * Allocate space for the next item. * * @return pointer to the appended item. */ void * parser_list_append (parser_context_t *context_p, /**< context */ parser_list_t *list_p) /**< parser list */ { parser_mem_page_t *page_p = list_p->data.last_p; void *result; if (list_p->data.last_position + list_p->item_size > list_p->page_size) { size_t size = sizeof (parser_mem_page_t *) + list_p->page_size; page_p = (parser_mem_page_t *) parser_malloc (context_p, size); page_p->next_p = NULL; list_p->data.last_position = 0; if (list_p->data.last_p != NULL) { list_p->data.last_p->next_p = page_p; } else { list_p->data.first_p = page_p; } list_p->data.last_p = page_p; } result = page_p->bytes + list_p->data.last_position; list_p->data.last_position += list_p->item_size; return result; } /* parser_list_append */ /** * Return the nth item of the list. * * @return pointer to the item. */ void * parser_list_get (parser_list_t *list_p, /**< parser list */ size_t index) /**< item index */ { size_t item_count = list_p->item_count; parser_mem_page_t *page_p = list_p->data.first_p; while (index >= item_count) { JERRY_ASSERT (page_p != NULL); page_p = page_p->next_p; index -= item_count; } JERRY_ASSERT (page_p != NULL); JERRY_ASSERT (page_p != list_p->data.last_p || (index * list_p->item_size < list_p->data.last_position)); return page_p->bytes + (index * list_p->item_size); } /* parser_list_get */ /** * Initialize a parser list iterator. */ void parser_list_iterator_init (parser_list_t *list_p, /**< parser list */ parser_list_iterator_t *iterator_p) /**< iterator */ { iterator_p->list_p = list_p; iterator_p->current_p = list_p->data.first_p; iterator_p->current_position = 0; } /* parser_list_iterator_init */ /** * Next iterator step. * * @return the address of the current item, or NULL at the end. */ void * parser_list_iterator_next (parser_list_iterator_t *iterator_p) /**< iterator */ { void *result; if (iterator_p->current_p == NULL) { return NULL; } result = iterator_p->current_p->bytes + iterator_p->current_position; iterator_p->current_position += iterator_p->list_p->item_size; if (iterator_p->current_p->next_p == NULL) { if (iterator_p->current_position >= iterator_p->list_p->data.last_position) { iterator_p->current_p = NULL; iterator_p->current_position = 0; } } else if (iterator_p->current_position >= iterator_p->list_p->page_size) { iterator_p->current_p = iterator_p->current_p->next_p; iterator_p->current_position = 0; } return result; } /* parser_list_iterator_next */ /**********************************************************************/ /* Parser stack management functions */ /**********************************************************************/ /* Stack is a reversed storage. */ /** * Initialize parser stack. */ void parser_stack_init (parser_context_t *context_p) /**< context */ { parser_data_init (&context_p->stack, PARSER_STACK_PAGE_SIZE); context_p->free_page_p = NULL; } /* parser_stack_init */ /** * Free parser stack. */ void parser_stack_free (parser_context_t *context_p) /**< context */ { parser_data_free (&context_p->stack, sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE); if (context_p->free_page_p != NULL) { parser_free (context_p->free_page_p, sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE); } } /* parser_stack_free */ /** * Pushes an uint8_t value onto the stack. */ void parser_stack_push_uint8 (parser_context_t *context_p, /**< context */ uint8_t uint8_value) /**< value pushed onto the stack */ { parser_mem_page_t *page_p = context_p->stack.first_p; /* This assert might trigger false positive valgrind errors, when * parser_stack_push() pushes not fully initialized structures. * More precisely when the last byte of the structure is uninitialized. */ JERRY_ASSERT (page_p == NULL || context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); if (context_p->stack.last_position >= PARSER_STACK_PAGE_SIZE) { if (context_p->free_page_p != NULL) { page_p = context_p->free_page_p; context_p->free_page_p = NULL; } else { size_t size = sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE; page_p = (parser_mem_page_t *) parser_malloc (context_p, size); } page_p->next_p = context_p->stack.first_p; context_p->stack.last_position = 0; context_p->stack.first_p = page_p; } page_p->bytes[context_p->stack.last_position++] = uint8_value; context_p->stack_top_uint8 = uint8_value; } /* parser_stack_push_uint8 */ /** * Pops the last uint8_t value from the stack. */ void parser_stack_pop_uint8 (parser_context_t *context_p) /**< context */ { parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL && context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); context_p->stack.last_position--; if (context_p->stack.last_position == 0) { context_p->stack.first_p = page_p->next_p; context_p->stack.last_position = PARSER_STACK_PAGE_SIZE; if (context_p->free_page_p == NULL) { context_p->free_page_p = page_p; } else { parser_free (page_p, sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE); } page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL); } context_p->stack_top_uint8 = page_p->bytes[context_p->stack.last_position - 1]; } /* parser_stack_pop_uint8 */ /** * Change last byte of the stack. */ void parser_stack_change_last_uint8 (parser_context_t *context_p, /**< context */ uint8_t new_value) /**< new value */ { parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL && context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); page_p->bytes[context_p->stack.last_position - 1] = new_value; context_p->stack_top_uint8 = new_value; } /* parser_stack_change_last_uint8 */ /** * Get the uint8 value before the top of the stack. * * Pointer to the uint8 value */ uint8_t * parser_stack_get_prev_uint8 (parser_context_t *context_p) /**< context */ { parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL && (context_p->stack.last_position >= 2 || page_p->next_p != NULL)); if (context_p->stack.last_position >= 2) { return page_p->bytes + (context_p->stack.last_position - 2); } return page_p->next_p->bytes + (PARSER_STACK_PAGE_SIZE - 1); } /* parser_stack_get_prev_uint8 */ /** * Pushes an uint16_t value onto the stack. */ void parser_stack_push_uint16 (parser_context_t *context_p, /**< context */ uint16_t uint16_value) /**< value pushed onto the stack */ { if (context_p->stack.last_position + 2 <= PARSER_STACK_PAGE_SIZE) { parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL && context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); page_p->bytes[context_p->stack.last_position++] = (uint8_t) (uint16_value >> 8); page_p->bytes[context_p->stack.last_position++] = (uint8_t) uint16_value; context_p->stack_top_uint8 = (uint8_t) uint16_value; } else { parser_stack_push_uint8 (context_p, (uint8_t) (uint16_value >> 8)); parser_stack_push_uint8 (context_p, (uint8_t) uint16_value); } } /* parser_stack_push_uint16 */ /** * Pops the last uint16_t value from the stack. * * @return the value popped from the stack. */ uint16_t parser_stack_pop_uint16 (parser_context_t *context_p) /**< context */ { uint32_t value = context_p->stack_top_uint8; if (context_p->stack.last_position >= 3) { parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL && context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); value |= ((uint32_t) page_p->bytes[context_p->stack.last_position - 2]) << 8; context_p->stack_top_uint8 = page_p->bytes[context_p->stack.last_position - 3]; context_p->stack.last_position -= 2; } else { parser_stack_pop_uint8 (context_p); value |= ((uint32_t) context_p->stack_top_uint8) << 8; parser_stack_pop_uint8 (context_p); } return (uint16_t) value; } /* parser_stack_pop_uint16 */ /** * Pushes a data onto the stack. */ void parser_stack_push (parser_context_t *context_p, /**< context */ const void *data_p, /**< data pushed onto the stack */ uint32_t length) /**< length of the data */ { uint32_t fragment_length = PARSER_STACK_PAGE_SIZE - context_p->stack.last_position; const uint8_t *bytes_p = (const uint8_t *) data_p; parser_mem_page_t *page_p; JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); context_p->stack_top_uint8 = bytes_p[length - 1]; if (fragment_length > 0) { /* Fill the remaining bytes. */ if (fragment_length > length) { fragment_length = length; } memcpy (context_p->stack.first_p->bytes + context_p->stack.last_position, bytes_p, fragment_length); if (fragment_length == length) { context_p->stack.last_position += length; return; } bytes_p += fragment_length; length -= fragment_length; } if (context_p->free_page_p != NULL) { page_p = context_p->free_page_p; context_p->free_page_p = NULL; } else { size_t size = sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE; page_p = (parser_mem_page_t *) parser_malloc (context_p, size); } page_p->next_p = context_p->stack.first_p; context_p->stack.first_p = page_p; memcpy (page_p->bytes, bytes_p, length); context_p->stack.last_position = length; } /* parser_stack_push */ /** * Pop bytes from the top of the stack. */ void parser_stack_pop (parser_context_t *context_p, /**< context */ void *data_p, /**< destination buffer, can be NULL */ uint32_t length) /**< length of the data */ { uint8_t *bytes_p = (uint8_t *) data_p; parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); if (context_p->stack.last_position > length) { context_p->stack.last_position -= length; context_p->stack_top_uint8 = page_p->bytes[context_p->stack.last_position - 1]; if (bytes_p != NULL) { memcpy (bytes_p, context_p->stack.first_p->bytes + context_p->stack.last_position, length); } return; } JERRY_ASSERT (page_p->next_p != NULL); length -= context_p->stack.last_position; if (bytes_p != NULL) { memcpy (bytes_p + length, page_p->bytes, context_p->stack.last_position); } context_p->stack.first_p = page_p->next_p; context_p->stack.last_position = PARSER_STACK_PAGE_SIZE - length; context_p->stack_top_uint8 = page_p->next_p->bytes[context_p->stack.last_position - 1]; if (bytes_p != NULL && length > 0) { memcpy (bytes_p, page_p->next_p->bytes + context_p->stack.last_position, length); } JERRY_ASSERT (context_p->stack.last_position > 0); if (context_p->free_page_p == NULL) { context_p->free_page_p = page_p; } else { parser_free (page_p, sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE); } } /* parser_stack_pop */ /** * Initialize stack iterator. */ void parser_stack_iterator_init (parser_context_t *context_p, /**< context */ parser_stack_iterator_t *iterator) /**< iterator */ { iterator->current_p = context_p->stack.first_p; iterator->current_position = context_p->stack.last_position; } /* parser_stack_iterator_init */ /** * Read the next byte from the stack. * * @return byte */ uint8_t parser_stack_iterator_read_uint8 (parser_stack_iterator_t *iterator) /**< iterator */ { JERRY_ASSERT (iterator->current_position > 0 && iterator->current_position <= PARSER_STACK_PAGE_SIZE); return iterator->current_p->bytes[iterator->current_position - 1]; } /* parser_stack_iterator_read_uint8 */ /** * Skip the next n bytes of the stack. */ void parser_stack_iterator_skip (parser_stack_iterator_t *iterator, /**< iterator */ size_t length) /**< number of skipped bytes */ { JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); if (length < iterator->current_position) { iterator->current_position -= length; } else { iterator->current_position = PARSER_STACK_PAGE_SIZE - (length - iterator->current_position); iterator->current_p = iterator->current_p->next_p; } } /* parser_stack_iterator_skip */ /** * Read bytes from the stack. */ void parser_stack_iterator_read (parser_stack_iterator_t *iterator, /**< iterator */ void *data_p, /**< destination buffer */ size_t length) /**< length of the data */ { uint8_t *bytes_p = (uint8_t *) data_p; JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); if (length <= iterator->current_position) { memcpy (bytes_p, iterator->current_p->bytes + iterator->current_position - length, length); } else { JERRY_ASSERT (iterator->current_p->next_p != NULL); length -= iterator->current_position; memcpy (bytes_p + length, iterator->current_p->bytes, iterator->current_position); memcpy (bytes_p, iterator->current_p->next_p->bytes + PARSER_STACK_PAGE_SIZE - length, length); } } /* parser_stack_iterator_read */ /** * Write bytes onto the stack. */ void parser_stack_iterator_write (parser_stack_iterator_t *iterator, /**< iterator */ const void *data_p, /**< destination buffer */ size_t length) /**< length of the data */ { const uint8_t *bytes_p = (const uint8_t *) data_p; JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); if (length <= iterator->current_position) { memcpy (iterator->current_p->bytes + iterator->current_position - length, bytes_p, length); } else { JERRY_ASSERT (iterator->current_p->next_p != NULL); length -= iterator->current_position; memcpy (iterator->current_p->bytes, bytes_p + length, iterator->current_position); memcpy (iterator->current_p->next_p->bytes + PARSER_STACK_PAGE_SIZE - length, bytes_p, length); } } /* parser_stack_iterator_write */ /** * @} * @} */ #endif /* JERRY_PARSER */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-symbol-prototype.inc.h000664 001750 001750 00000002742 15164251010 052166 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Symbol prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_SYMBOL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_SYMBOL_PROTOTYPE_TO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_VALUE_OF_UL, ECMA_SYMBOL_PROTOTYPE_VALUE_OF, 0, 0) ROUTINE_CONFIGURABLE_ONLY (LIT_GLOBAL_SYMBOL_TO_PRIMITIVE, ECMA_SYMBOL_PROTOTYPE_TO_PRIMITIVE, 0, 1) /* ECMA-262 v6, 19.4.3.4 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_SYMBOL_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262, v11, 19.4.3.2 */ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_DESCRIPTION, ECMA_SYMBOL_PROTOTYPE_DESCRIPTION, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-set-iterator-prototype.inc.h000664 001750 001750 00000002254 15164251010 053301 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %SetIteratorPrototype% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_CONTAINER STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_SET_ITERATOR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_NEXT, ECMA_BUILTIN_SET_ITERATOR_PROTOTYPE_OBJECT_NEXT, 0, 0) #endif /* JERRY_BUILTIN_CONTAINER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-unicode-ranges-sup.inc.h000664 001750 001750 00000020434 15164251010 045331 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is automatically generated by the gen-unicode.py script * from DerivedCoreProperties.txt. Do not edit! */ /** * Character interval starting points for ID_Start. */ static const uint32_t lit_unicode_id_start_interval_starts_sup[] JERRY_ATTR_CONST_DATA = { 0x010000, 0x01000d, 0x010028, 0x01003c, 0x01003f, 0x010050, 0x010080, 0x010140, 0x010280, 0x0102a0, 0x010300, 0x01032d, 0x010350, 0x010380, 0x0103a0, 0x0103c8, 0x0103d1, 0x010400, 0x0104b0, 0x0104d8, 0x010500, 0x010530, 0x010600, 0x010740, 0x010760, 0x010800, 0x01080a, 0x010837, 0x01083f, 0x010860, 0x010880, 0x0108e0, 0x0108f4, 0x010900, 0x010920, 0x010980, 0x0109be, 0x010a10, 0x010a15, 0x010a19, 0x010a60, 0x010a80, 0x010ac0, 0x010ac9, 0x010b00, 0x010b40, 0x010b60, 0x010b80, 0x010c00, 0x010c80, 0x010cc0, 0x010d00, 0x010e80, 0x010eb0, 0x010f00, 0x010f30, 0x010fb0, 0x010fe0, 0x011003, 0x011083, 0x0110d0, 0x011103, 0x011150, 0x011183, 0x0111c1, 0x011200, 0x011213, 0x011280, 0x01128a, 0x01128f, 0x01129f, 0x0112b0, 0x011305, 0x01130f, 0x011313, 0x01132a, 0x011332, 0x011335, 0x01135d, 0x011400, 0x011447, 0x01145f, 0x011480, 0x0114c4, 0x011580, 0x0115d8, 0x011600, 0x011680, 0x011700, 0x011800, 0x0118a0, 0x0118ff, 0x01190c, 0x011915, 0x011918, 0x0119a0, 0x0119aa, 0x011a0b, 0x011a5c, 0x011ac0, 0x011c00, 0x011c0a, 0x011c72, 0x011d00, 0x011d08, 0x011d0b, 0x011d60, 0x011d67, 0x011d6a, 0x011ee0, 0x012000, 0x012400, 0x012480, 0x013000, 0x014400, 0x016800, 0x016a40, 0x016ad0, 0x016b00, 0x016b40, 0x016b63, 0x016b7d, 0x016e40, 0x016f00, 0x016f93, 0x016fe0, 0x017000, 0x018800, 0x018d00, 0x01b000, 0x01b150, 0x01b164, 0x01b170, 0x01bc00, 0x01bc70, 0x01bc80, 0x01bc90, 0x01d400, 0x01d456, 0x01d49e, 0x01d4a5, 0x01d4a9, 0x01d4ae, 0x01d4bd, 0x01d4c5, 0x01d507, 0x01d50d, 0x01d516, 0x01d51e, 0x01d53b, 0x01d540, 0x01d54a, 0x01d552, 0x01d6a8, 0x01d6c2, 0x01d6dc, 0x01d6fc, 0x01d716, 0x01d736, 0x01d750, 0x01d770, 0x01d78a, 0x01d7aa, 0x01d7c4, 0x01e100, 0x01e137, 0x01e2c0, 0x01e800, 0x01e900, 0x01ee00, 0x01ee05, 0x01ee21, 0x01ee29, 0x01ee34, 0x01ee4d, 0x01ee51, 0x01ee61, 0x01ee67, 0x01ee6c, 0x01ee74, 0x01ee79, 0x01ee80, 0x01ee8b, 0x01eea1, 0x01eea5, 0x01eeab, 0x020000, 0x02a700, 0x02b740, 0x02b820, 0x02ceb0, 0x02f800, 0x030000 }; /** * Character interval lengths for ID_Start. */ static const uint16_t lit_unicode_id_start_interval_lengths_sup[] JERRY_ATTR_CONST_DATA = { 0x00000b, 0x000019, 0x000012, 0x000001, 0x00000e, 0x00000d, 0x00007a, 0x000034, 0x00001c, 0x000030, 0x00001f, 0x00001d, 0x000025, 0x00001d, 0x000023, 0x000007, 0x000004, 0x00009d, 0x000023, 0x000023, 0x000027, 0x000033, 0x000136, 0x000015, 0x000007, 0x000005, 0x00002b, 0x000001, 0x000016, 0x000016, 0x00001e, 0x000012, 0x000001, 0x000015, 0x000019, 0x000037, 0x000001, 0x000003, 0x000002, 0x00001c, 0x00001c, 0x00001c, 0x000007, 0x00001b, 0x000035, 0x000015, 0x000012, 0x000011, 0x000048, 0x000032, 0x000032, 0x000023, 0x000029, 0x000001, 0x00001c, 0x000015, 0x000014, 0x000016, 0x000034, 0x00002c, 0x000018, 0x000023, 0x000022, 0x00002f, 0x000003, 0x000011, 0x000018, 0x000006, 0x000003, 0x00000e, 0x000009, 0x00002e, 0x000007, 0x000001, 0x000015, 0x000006, 0x000001, 0x000004, 0x000004, 0x000034, 0x000003, 0x000002, 0x00002f, 0x000001, 0x00002e, 0x000003, 0x00002f, 0x00002a, 0x00001a, 0x00002b, 0x00003f, 0x000007, 0x000007, 0x000001, 0x000017, 0x000007, 0x000026, 0x000027, 0x00002d, 0x000038, 0x000008, 0x000024, 0x00001d, 0x000006, 0x000001, 0x000025, 0x000005, 0x000001, 0x00001f, 0x000012, 0x000399, 0x00006e, 0x0000c3, 0x00042e, 0x000246, 0x000238, 0x00001e, 0x00001d, 0x00002f, 0x000003, 0x000014, 0x000012, 0x00003f, 0x00004a, 0x00000c, 0x000001, 0x0017f7, 0x0004d5, 0x000008, 0x00011e, 0x000002, 0x000003, 0x00018b, 0x00006a, 0x00000c, 0x000008, 0x000009, 0x000054, 0x000046, 0x000001, 0x000001, 0x000003, 0x00000b, 0x000006, 0x000040, 0x000003, 0x000007, 0x000006, 0x00001b, 0x000003, 0x000004, 0x000006, 0x000153, 0x000018, 0x000018, 0x00001e, 0x000018, 0x00001e, 0x000018, 0x00001e, 0x000018, 0x00001e, 0x000018, 0x000007, 0x00002c, 0x000006, 0x00002b, 0x0000c4, 0x000043, 0x000003, 0x00001a, 0x000001, 0x000009, 0x000003, 0x000002, 0x000001, 0x000001, 0x000003, 0x000006, 0x000003, 0x000003, 0x000009, 0x000010, 0x000002, 0x000004, 0x000010, 0x00a6dd, 0x001034, 0x0000dd, 0x001681, 0x001d30, 0x00021d, 0x00134a }; /** * Non-interval characters for ID_Start. */ static const uint32_t lit_unicode_id_start_chars_sup[] JERRY_ATTR_CONST_DATA = { 0x010808, 0x01083c, 0x010a00, 0x010f27, 0x011144, 0x011147, 0x011176, 0x0111da, 0x0111dc, 0x011288, 0x01133d, 0x011350, 0x0114c7, 0x011644, 0x0116b8, 0x011909, 0x01193f, 0x011941, 0x0119e1, 0x0119e3, 0x011a00, 0x011a3a, 0x011a50, 0x011a9d, 0x011c40, 0x011d46, 0x011d98, 0x011fb0, 0x016f50, 0x016fe3, 0x01d4a2, 0x01d4bb, 0x01d546, 0x01e14e, 0x01e94b, 0x01ee24, 0x01ee27, 0x01ee39, 0x01ee3b, 0x01ee42, 0x01ee47, 0x01ee49, 0x01ee4b, 0x01ee54, 0x01ee57, 0x01ee59, 0x01ee5b, 0x01ee5d, 0x01ee5f, 0x01ee64, 0x01ee7e }; /** * Character interval starting points for ID_Continue. */ static const uint32_t lit_unicode_id_continue_interval_starts_sup[] JERRY_ATTR_CONST_DATA = { 0x010376, 0x0104a0, 0x010a01, 0x010a05, 0x010a0c, 0x010a38, 0x010ae5, 0x010d24, 0x010d30, 0x010eab, 0x010f46, 0x011000, 0x011038, 0x011066, 0x01107f, 0x0110b0, 0x0110f0, 0x011100, 0x011127, 0x011136, 0x011145, 0x011180, 0x0111b3, 0x0111c9, 0x0111ce, 0x01122c, 0x0112df, 0x0112f0, 0x011300, 0x01133b, 0x01133e, 0x011347, 0x01134b, 0x011362, 0x011366, 0x011370, 0x011435, 0x011450, 0x0114b0, 0x0114d0, 0x0115af, 0x0115b8, 0x0115dc, 0x011630, 0x011650, 0x0116ab, 0x0116c0, 0x01171d, 0x011730, 0x01182c, 0x0118e0, 0x011930, 0x011937, 0x01193b, 0x011942, 0x011950, 0x0119d1, 0x0119da, 0x011a01, 0x011a33, 0x011a3b, 0x011a51, 0x011a8a, 0x011c2f, 0x011c38, 0x011c50, 0x011c92, 0x011ca9, 0x011d31, 0x011d3c, 0x011d3f, 0x011d50, 0x011d8a, 0x011d90, 0x011d93, 0x011da0, 0x011ef3, 0x016a60, 0x016af0, 0x016b30, 0x016b50, 0x016f51, 0x016f8f, 0x016ff0, 0x01bc9d, 0x01d165, 0x01d16d, 0x01d17b, 0x01d185, 0x01d1aa, 0x01d242, 0x01d7ce, 0x01da00, 0x01da3b, 0x01da9b, 0x01daa1, 0x01e000, 0x01e008, 0x01e01b, 0x01e023, 0x01e026, 0x01e130, 0x01e140, 0x01e2ec, 0x01e8d0, 0x01e944, 0x01e950, 0x01fbf0, 0x0e0100 }; /** * Character interval lengths for ID_Continue. */ static const uint16_t lit_unicode_id_continue_interval_lengths_sup[] JERRY_ATTR_CONST_DATA = { 0x000004, 0x000009, 0x000002, 0x000001, 0x000003, 0x000002, 0x000001, 0x000003, 0x000009, 0x000001, 0x00000a, 0x000002, 0x00000e, 0x000009, 0x000003, 0x00000a, 0x000009, 0x000002, 0x00000d, 0x000009, 0x000001, 0x000002, 0x00000d, 0x000003, 0x00000b, 0x00000b, 0x00000b, 0x000009, 0x000003, 0x000001, 0x000006, 0x000001, 0x000002, 0x000001, 0x000006, 0x000004, 0x000011, 0x000009, 0x000013, 0x000009, 0x000006, 0x000008, 0x000001, 0x000010, 0x000009, 0x00000c, 0x000009, 0x00000e, 0x000009, 0x00000e, 0x000009, 0x000005, 0x000001, 0x000003, 0x000001, 0x000009, 0x000006, 0x000006, 0x000009, 0x000006, 0x000003, 0x00000a, 0x00000f, 0x000007, 0x000007, 0x000009, 0x000015, 0x00000d, 0x000005, 0x000001, 0x000006, 0x000009, 0x000004, 0x000001, 0x000004, 0x000009, 0x000003, 0x000009, 0x000004, 0x000006, 0x000009, 0x000036, 0x000003, 0x000001, 0x000001, 0x000004, 0x000005, 0x000007, 0x000006, 0x000003, 0x000002, 0x000031, 0x000036, 0x000031, 0x000004, 0x00000e, 0x000006, 0x000010, 0x000006, 0x000001, 0x000004, 0x000006, 0x000009, 0x00000d, 0x000006, 0x000006, 0x000009, 0x000009, 0x0000ef }; /** * Non-interval characters for ID_Continue. */ static const uint32_t lit_unicode_id_continue_chars_sup[] JERRY_ATTR_CONST_DATA = { 0x0101fd, 0x0102e0, 0x010a3f, 0x011173, 0x01123e, 0x011357, 0x01145e, 0x011940, 0x0119e4, 0x011a47, 0x011d3a, 0x011d47, 0x016f4f, 0x016fe4, 0x01da75, 0x01da84 }; jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype-template.inc.h000664 001750 001750 00000002533 15164251010 057040 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ #if JERRY_BUILTIN_TYPEDARRAY #ifndef TYPEDARRAY_BYTES_PER_ELEMENT #error "Please define TYPEDARRAY_BYTES_PER_ELEMENT" #endif /* !TYPEDARRAY_BYTES_PER_ELEMENT */ #ifndef TYPEDARRAY_BUILTIN_ID #error "Please define TYPEDARRAY_BUILTIN_ID" #endif /* !TYPEDARRAY_BUILTIN_ID */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* ES2015 22.2.3.4 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, TYPEDARRAY_BUILTIN_ID, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ES2015 22.2.6.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_BYTES_PER_ELEMENT_U, TYPEDARRAY_BYTES_PER_ELEMENT, ECMA_PROPERTY_FIXED) #include "ecma-builtin-helpers-macro-undefs.inc.h" #undef TYPEDARRAY_BUILTIN_ID #undef TYPEDARRAY_BYTES_PER_ELEMENT #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/CODEOWNERS000664 001750 001750 00000000505 15164251010 030610 0ustar00ddennedyddennedy000000 000000 # ThorVG uses the CODEOWNERS file to automatically assign designated reviewers within the GitHub system. # Being listed as a code owner does not necessarily indicate copyright ownership of the code. * @hermet /src/renderer/gl_engine @wenjieshen @SergeyLebedkin /src/renderer/wg_engine @SergeyLebedkin /src/loaders/svg @JSUYAexternal/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-reference.h000664 001750 001750 00000002344 15164251010 045424 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_REFERENCE_H #define ECMA_REFERENCE_H #include "ecma-globals.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup references ECMA-Reference * @{ */ ecma_object_t *ecma_op_resolve_reference_base (ecma_object_t *lex_env_p, ecma_string_t *name_p); ecma_value_t ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, ecma_string_t *name_p); ecma_value_t ecma_op_object_bound_environment_resolve_reference_value (ecma_object_t *lex_env_p, ecma_string_t *name_p); ecma_value_t ecma_op_resolve_super_base (ecma_object_t *lex_env_p); /** * @} * @} */ #endif /* !ECMA_REFERENCE_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/fwd.h000664 001750 001750 00000007655 15164251010 035615 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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 struct UTF8; template struct UTF16; template struct UTF16BE; template struct UTF16LE; template struct UTF32; template struct UTF32BE; template struct UTF32LE; template struct ASCII; template struct AutoUTF; template struct Transcoder; // allocators.h class CrtAllocator; template class MemoryPoolAllocator; // stream.h template struct GenericStringStream; typedef GenericStringStream > StringStream; template struct GenericInsituStringStream; typedef GenericInsituStringStream > InsituStringStream; // stringbuffer.h template class GenericStringBuffer; typedef GenericStringBuffer, CrtAllocator> StringBuffer; // filereadstream.h class FileReadStream; // filewritestream.h class FileWriteStream; // memorybuffer.h template struct GenericMemoryBuffer; typedef GenericMemoryBuffer MemoryBuffer; // memorystream.h struct MemoryStream; // reader.h template struct BaseReaderHandler; template class GenericReader; typedef GenericReader, UTF8, CrtAllocator> Reader; // writer.h template class Writer; // prettywriter.h template class PrettyWriter; // document.h template class GenericMember; template class GenericMemberIterator; template struct GenericStringRef; template class GenericValue; typedef GenericValue, MemoryPoolAllocator > Value; template class GenericDocument; typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; // pointer.h template class GenericPointer; typedef GenericPointer Pointer; // schema.h template class IGenericRemoteSchemaDocumentProvider; template class GenericSchemaDocument; typedef GenericSchemaDocument SchemaDocument; typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; template < typename SchemaDocumentType, typename OutputHandler, typename StateAllocator> class GenericSchemaValidator; typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_RAPIDJSONFWD_H_ glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/vm.h000664 001750 001750 00000037245 15164251010 040547 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 VM_H #define VM_H #include "ecma-globals.h" #include "ecma-module.h" #include "jrt.h" #include "vm-defines.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup vm_executor Executor * @{ */ /** * Each CBC opcode is transformed to three vm opcodes: * * - first opcode is a "get arguments" opcode which specifies * the type (register, literal, stack, etc.) and number * (from 0 to 2) of input arguments * - second opcode is a "group" opcode which specifies * the actual operation (add, increment, call, etc.) * - third opcode is a "put result" opcode which specifies * the destination where the result is stored (register, * stack, etc.) */ /** * If VM_OC_GET_ARGS_INDEX(opcode) == VM_OC_GET_BRANCH, * this flag signals that the branch is a backward branch. */ #define VM_OC_BACKWARD_BRANCH (1 << 15) /** * Position of "get arguments" opcode. */ #define VM_OC_GET_ARGS_SHIFT 8 /** * Mask of "get arguments" opcode. */ #define VM_OC_GET_ARGS_MASK 0x7 /** * Generate the binary representation of a "get arguments" opcode. */ #define VM_OC_GET_ARGS_CREATE_INDEX(V) (((V) &VM_OC_GET_ARGS_MASK) << VM_OC_GET_ARGS_SHIFT) /** * Extract the "get arguments" opcode. */ #define VM_OC_GET_ARGS_INDEX(O) ((O) & (VM_OC_GET_ARGS_MASK << VM_OC_GET_ARGS_SHIFT)) /** * Checks whether the result is stored somewhere. */ #define VM_OC_HAS_GET_ARGS(V) ((V) & (VM_OC_GET_ARGS_MASK << VM_OC_GET_ARGS_SHIFT)) /** * Argument getters that are part of the opcodes. */ typedef enum { VM_OC_GET_NONE = VM_OC_GET_ARGS_CREATE_INDEX (0), /**< do nothing */ VM_OC_GET_BRANCH = VM_OC_GET_ARGS_CREATE_INDEX (1), /**< branch argument */ VM_OC_GET_STACK = VM_OC_GET_ARGS_CREATE_INDEX (2), /**< pop one element from the stack */ VM_OC_GET_STACK_STACK = VM_OC_GET_ARGS_CREATE_INDEX (3), /**< pop two elements from the stack */ VM_OC_GET_LITERAL = VM_OC_GET_ARGS_CREATE_INDEX (4), /**< resolve literal */ VM_OC_GET_LITERAL_LITERAL = VM_OC_GET_ARGS_CREATE_INDEX (5), /**< resolve two literals */ VM_OC_GET_STACK_LITERAL = VM_OC_GET_ARGS_CREATE_INDEX (6), /**< pop one element from the stack * and resolve a literal */ VM_OC_GET_THIS_LITERAL = VM_OC_GET_ARGS_CREATE_INDEX (7), /**< get this and resolve a literal */ } vm_oc_get_types; /** * Mask of "group" opcode. */ #define VM_OC_GROUP_MASK 0xff /** * Extract the "group" opcode. */ #define VM_OC_GROUP_GET_INDEX(O) ((O) &VM_OC_GROUP_MASK) /** * Opcodes. */ typedef enum { VM_OC_POP, /**< pop from stack */ VM_OC_POP_BLOCK, /**< pop block */ VM_OC_PUSH, /**< push one literal */ VM_OC_PUSH_TWO, /**< push two literals */ VM_OC_PUSH_THREE, /**< push three literals */ VM_OC_PUSH_UNDEFINED, /**< push undefined value */ VM_OC_PUSH_TRUE, /**< push true value */ VM_OC_PUSH_FALSE, /**< push false value */ VM_OC_PUSH_NULL, /**< push null value */ VM_OC_PUSH_THIS, /**< push this */ VM_OC_PUSH_0, /**< push number zero */ VM_OC_PUSH_POS_BYTE, /**< push number between 1 and 256 */ VM_OC_PUSH_NEG_BYTE, /**< push number between -1 and -256 */ VM_OC_PUSH_LIT_0, /**< push literal and number zero */ VM_OC_PUSH_LIT_POS_BYTE, /**< push literal and number between 1 and 256 */ VM_OC_PUSH_LIT_NEG_BYTE, /**< push literal and number between -1 and -256 */ VM_OC_PUSH_OBJECT, /**< push object */ VM_OC_PUSH_NAMED_FUNC_EXPR, /**< push named function expression */ VM_OC_SET_PROPERTY, /**< set property */ VM_OC_SET_GETTER, /**< set getter */ VM_OC_SET_SETTER, /**< set setter */ VM_OC_PUSH_ARRAY, /**< push array */ VM_OC_PUSH_ELISON, /**< push elison */ VM_OC_APPEND_ARRAY, /**< append array */ VM_OC_IDENT_REFERENCE, /**< ident reference */ VM_OC_PROP_REFERENCE, /**< prop reference */ VM_OC_PROP_GET, /**< prop get */ /* These eight opcodes must be in this order. */ VM_OC_PROP_PRE_INCR, /**< prefix increment of a property */ VM_OC_PROP_PRE_DECR, /**< prop prefix decrement of a property */ VM_OC_PROP_POST_INCR, /**< prop postfix increment of a property */ VM_OC_PROP_POST_DECR, /**< prop postfix decrement of a property */ VM_OC_PRE_INCR, /**< prefix increment */ VM_OC_PRE_DECR, /**< prefix decrement */ VM_OC_POST_INCR, /**< postfix increment */ VM_OC_POST_DECR, /**< postfix decrement */ VM_OC_PROP_DELETE, /**< delete property */ VM_OC_DELETE, /**< delete */ VM_OC_MOV_IDENT, /**< move identifier register reference */ VM_OC_ASSIGN, /**< assign */ VM_OC_ASSIGN_PROP, /**< assign property */ VM_OC_ASSIGN_PROP_THIS, /**< assign prop this */ VM_OC_RETURN, /**< return */ VM_OC_RETURN_FUNCTION_END, /**< return at the end of a function/script */ VM_OC_THROW, /**< throw */ VM_OC_THROW_REFERENCE_ERROR, /**< throw reference error */ VM_OC_EVAL, /**< eval */ VM_OC_CALL, /**< call */ VM_OC_NEW, /**< new */ VM_OC_RESOLVE_BASE_FOR_CALL, /**< resolve base value before call */ VM_OC_ERROR, /**< error while the vm_loop is suspended */ VM_OC_JUMP, /**< jump */ VM_OC_BRANCH_IF_NULLISH, /** branch if undefined or null */ VM_OC_POP_REFERENCE, /** prop identifier or property reference from the stack */ VM_OC_BRANCH_IF_STRICT_EQUAL, /**< branch if strict equal */ /* These four opcodes must be in this order. */ VM_OC_BRANCH_IF_TRUE, /**< branch if true */ VM_OC_BRANCH_IF_FALSE, /**< branch if false */ VM_OC_BRANCH_IF_LOGICAL_TRUE, /**< branch if logical true */ VM_OC_BRANCH_IF_LOGICAL_FALSE, /**< branch if logical false */ VM_OC_PLUS, /**< unary plus */ VM_OC_MINUS, /**< unary minus */ VM_OC_NOT, /**< not */ VM_OC_BIT_NOT, /**< bitwise not */ VM_OC_VOID, /**< void */ VM_OC_TYPEOF_IDENT, /**< typeof identifier */ VM_OC_TYPEOF, /**< typeof */ VM_OC_ADD, /**< binary add */ VM_OC_SUB, /**< binary sub */ VM_OC_MUL, /**< mul */ VM_OC_DIV, /**< div */ VM_OC_MOD, /**< mod */ VM_OC_EXP, /**< exponentiation */ VM_OC_EQUAL, /**< equal */ VM_OC_NOT_EQUAL, /**< not equal */ VM_OC_STRICT_EQUAL, /**< strict equal */ VM_OC_STRICT_NOT_EQUAL, /**< strict not equal */ VM_OC_LESS, /**< less */ VM_OC_GREATER, /**< greater */ VM_OC_LESS_EQUAL, /**< less equal */ VM_OC_GREATER_EQUAL, /**< greater equal */ VM_OC_IN, /**< in */ VM_OC_INSTANCEOF, /**< instanceof */ VM_OC_BIT_OR, /**< bitwise or */ VM_OC_BIT_XOR, /**< bitwise xor */ VM_OC_BIT_AND, /**< bitwise and */ VM_OC_LEFT_SHIFT, /**< left shift */ VM_OC_RIGHT_SHIFT, /**< right shift */ VM_OC_UNS_RIGHT_SHIFT, /**< unsigned right shift */ VM_OC_BLOCK_CREATE_CONTEXT, /**< create lexical environment for blocks enclosed in braces */ VM_OC_WITH, /**< with */ VM_OC_FOR_IN_INIT, /**< for-in init context */ VM_OC_FOR_IN_GET_NEXT, /**< get next */ VM_OC_FOR_IN_HAS_NEXT, /**< has next */ VM_OC_TRY, /**< try */ VM_OC_CATCH, /**< catch */ VM_OC_FINALLY, /**< finally */ VM_OC_CONTEXT_END, /**< context end */ VM_OC_JUMP_AND_EXIT_CONTEXT, /**< jump and exit context */ VM_OC_CREATE_BINDING, /**< create variables */ VM_OC_CREATE_ARGUMENTS, /**< create arguments object */ VM_OC_SET_BYTECODE_PTR, /**< setting bytecode pointer */ VM_OC_VAR_EVAL, /**< variable and function evaluation */ VM_OC_EXT_VAR_EVAL, /**< variable and function evaluation for * functions with separate argument context */ VM_OC_INIT_ARG_OR_FUNC, /**< create and init a function or argument binding */ VM_OC_CLASS_CALL_STATIC_BLOCK, /**< call the class static block */ VM_OC_DEFINE_FIELD, /**< define class field */ VM_OC_PRIVATE_PROP_REFERENCE, /**< reference to class private method */ VM_OC_ASSIGN_PRIVATE, /**< assign to private field */ VM_OC_PRIVATE_FIELD_ADD, /**< add private field */ VM_OC_PRIVATE_PROP_GET, /**< get private field */ VM_OC_PRIVATE_IN, /**< 'in' opcode handler for private identifiers */ VM_OC_COLLECT_PRIVATE_PROPERTY, /**< collect private properties */ VM_OC_CHECK_VAR, /**< check redeclared vars in the global scope */ VM_OC_CHECK_LET, /**< check redeclared lets in the global scope */ VM_OC_ASSIGN_LET_CONST, /**< assign values to let/const declarations */ VM_OC_INIT_BINDING, /**< create and initialize a binding */ VM_OC_THROW_CONST_ERROR, /**< throw invalid assignment to const variable error */ VM_OC_COPY_TO_GLOBAL, /**< copy value to global lex env */ VM_OC_COPY_FROM_ARG, /**< copy value from arg lex env */ VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */ VM_OC_COPY_DATA_PROPERTIES, /**< copy data properties of an object */ VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */ VM_OC_FOR_OF_INIT, /**< for-of init context */ VM_OC_FOR_OF_GET_NEXT, /**< for-of get next */ VM_OC_FOR_OF_HAS_NEXT, /**< for-of has next */ VM_OC_FOR_AWAIT_OF_INIT, /**< for-await-of init context */ VM_OC_FOR_AWAIT_OF_HAS_NEXT, /**< for-await-of has next */ VM_OC_LOCAL_EVAL, /**< eval in local context */ VM_OC_SUPER_CALL, /**< call the 'super' constructor */ VM_OC_PUSH_CLASS_ENVIRONMENT, /**< push class environment */ VM_OC_PUSH_IMPLICIT_CTOR, /**< create implicit class constructor */ VM_OC_INIT_CLASS, /**< initialize class */ VM_OC_FINALIZE_CLASS, /**< finalize class */ VM_OC_SET_FIELD_INIT, /**< store the class field initializer function */ VM_OC_SET_STATIC_FIELD_INIT, /**< store the static class field initializer function */ VM_OC_RUN_FIELD_INIT, /**< run the class field initializer function */ VM_OC_RUN_STATIC_FIELD_INIT, /**< run the static class field initializer function */ VM_OC_SET_NEXT_COMPUTED_FIELD, /**< set the next computed field of a class */ VM_OC_PUSH_SUPER_CONSTRUCTOR, /**< getSuperConstructor operation */ VM_OC_RESOLVE_LEXICAL_THIS, /**< resolve this_binding from the lexical environment */ VM_OC_SUPER_REFERENCE, /**< push super reference */ VM_OC_SET_HOME_OBJECT, /**< set the [[HomeObject]] environment in an object literal */ VM_OC_OBJECT_LITERAL_HOME_ENV, /**< create/destroy [[HomeObject]] environment of an object literal */ VM_OC_SET_FUNCTION_NAME, /**< set function name property */ VM_OC_PUSH_SPREAD_ELEMENT, /**< push spread element */ VM_OC_PUSH_REST_OBJECT, /**< push rest object */ VM_OC_ITERATOR_CONTEXT_CREATE, /**< create iterator context */ VM_OC_ITERATOR_CONTEXT_END, /**< finalize iterator context */ VM_OC_ITERATOR_STEP, /**< IteratorStep abstract operation */ VM_OC_OBJ_INIT_CONTEXT_CREATE, /**< create object initializer context */ VM_OC_OBJ_INIT_CONTEXT_END, /**< finalize object initializer context */ VM_OC_OBJ_INIT_PUSH_REST, /**< push the object with the rest properties */ VM_OC_INITIALIZER_PUSH_NAME, /**< append string to name list array and push the string */ VM_OC_DEFAULT_INITIALIZER, /**< default initializer inside a pattern */ VM_OC_REST_INITIALIZER, /**< create rest object inside an array pattern */ VM_OC_INITIALIZER_PUSH_PROP, /**< push property for object initializer */ VM_OC_SPREAD_ARGUMENTS, /**< perform function call/construct with spread arguments */ VM_OC_CREATE_GENERATOR, /**< create a generator object */ VM_OC_YIELD, /**< yield operation */ VM_OC_ASYNC_YIELD, /**< async yield operation */ VM_OC_ASYNC_YIELD_ITERATOR, /**< async yield iterator operation */ VM_OC_AWAIT, /**< await operation */ VM_OC_GENERATOR_AWAIT, /**< generator await operation */ VM_OC_EXT_RETURN, /**< return which also clears the stack */ VM_OC_ASYNC_EXIT, /**< return from async function */ VM_OC_STRING_CONCAT, /**< string concatenation */ VM_OC_GET_TEMPLATE_OBJECT, /**< GetTemplateObject operation */ VM_OC_PUSH_NEW_TARGET, /**< push new.target onto the stack */ VM_OC_REQUIRE_OBJECT_COERCIBLE, /**< RequireObjectCoercible operation */ VM_OC_ASSIGN_SUPER, /**< assign super reference */ VM_OC_SET__PROTO__, /**< set prototype when __proto__: form is used */ VM_OC_PUSH_STATIC_FIELD_FUNC, /**< push static field initializer function */ VM_OC_ADD_COMPUTED_FIELD, /**< add computed field name */ #if JERRY_MODULE_SYSTEM VM_OC_MODULE_IMPORT, /**< module dynamic import */ VM_OC_MODULE_IMPORT_META, /**< module import.meta */ #endif /* JERRY_MODULE_SYSTEM */ VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; /** * Unused opcodes, but required by byte-code types. */ typedef enum { #if !JERRY_MODULE_SYSTEM VM_OC_MODULE_IMPORT = VM_OC_NONE, /**< module dynamic import */ VM_OC_MODULE_IMPORT_META = VM_OC_NONE, /**< module import.meta */ #endif /* JERRY_MODULE_SYSTEM */ VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ } vm_oc_unused_types; /** * Decrement operator. */ #define VM_OC_DECREMENT_OPERATOR_FLAG 0x1 /** * Postfix increment/decrement operator. */ #define VM_OC_POST_INCR_DECR_OPERATOR_FLAG 0x2 /** * An named variable is updated by the increment/decrement operator. */ #define VM_OC_IDENT_INCR_DECR_OPERATOR_FLAG 0x4 /** * Jump to target offset if input value is logical false. */ #define VM_OC_BRANCH_IF_FALSE_FLAG 0x1 /** * Branch optimized for logical and/or opcodes. */ #define VM_OC_LOGICAL_BRANCH_FLAG 0x2 /** * Bit index shift for non-static property initializers. */ #define VM_OC_NON_STATIC_SHIFT 15 /** * This flag is set for static property initializers. */ #define VM_OC_NON_STATIC_FLAG (0x1 << VM_OC_NON_STATIC_SHIFT) /** * Position of "put result" opcode. */ #define VM_OC_PUT_RESULT_SHIFT 11 /** * Mask of "put result" opcode. */ #define VM_OC_PUT_RESULT_MASK 0xf /** * Generate a "put result" opcode flag bit. */ #define VM_OC_PUT_RESULT_CREATE_FLAG(V) (((V) &VM_OC_PUT_RESULT_MASK) << VM_OC_PUT_RESULT_SHIFT) /** * Checks whether the result is stored somewhere. */ #define VM_OC_HAS_PUT_RESULT(V) ((V) & (VM_OC_PUT_RESULT_MASK << VM_OC_PUT_RESULT_SHIFT)) /** * Specify where the result is stored */ typedef enum { VM_OC_PUT_IDENT = VM_OC_PUT_RESULT_CREATE_FLAG (0x1), VM_OC_PUT_REFERENCE = VM_OC_PUT_RESULT_CREATE_FLAG (0x2), VM_OC_PUT_STACK = VM_OC_PUT_RESULT_CREATE_FLAG (0x4), VM_OC_PUT_BLOCK = VM_OC_PUT_RESULT_CREATE_FLAG (0x8), } vm_oc_put_types; /** * Non-recursive vm_loop: the vm_loop can be suspended * to execute a call /construct operation. These return * types of the vm_loop tells whether a call operation * is in progress or the vm_loop is finished. */ typedef enum { VM_NO_EXEC_OP, /**< do nothing */ VM_EXEC_CALL, /**< invoke a function */ VM_EXEC_SUPER_CALL, /**< invoke a function through 'super' keyword */ VM_EXEC_SPREAD_OP, /**< call/construct operation with spread argument list */ VM_EXEC_RETURN, /**< return with the completion value without freeing registers */ VM_EXEC_CONSTRUCT, /**< construct a new object */ } vm_call_operation; ecma_value_t vm_run_global (const ecma_compiled_code_t *bytecode_p, ecma_object_t *function_object_p); ecma_value_t vm_run_eval (ecma_compiled_code_t *bytecode_data_p, uint32_t parse_opts); #if JERRY_MODULE_SYSTEM ecma_value_t vm_run_module (ecma_module_t *module_p); ecma_value_t vm_init_module_scope (ecma_module_t *module_p); #endif /* JERRY_MODULE_SYSTEM */ ecma_value_t vm_run (vm_frame_ctx_shared_t *shared_p, ecma_value_t this_binding_value, ecma_object_t *lex_env_p); ecma_value_t vm_execute (vm_frame_ctx_t *frame_ctx_p); bool vm_is_strict_mode (void); bool vm_is_direct_eval_form_call (void); ecma_value_t vm_get_backtrace (uint32_t max_depth); /** * @} * @} */ #endif /* !VM_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgShape.h000664 001750 001750 00000041060 15164251010 033545 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SHAPE_H_ #define _TVG_SHAPE_H_ #include "tvgCommon.h" #include "tvgMath.h" #include "tvgPaint.h" namespace tvg { struct ShapeImpl : Shape { Paint::Impl impl; RenderShape rs; uint8_t opacity; //for composition ShapeImpl() : impl(Paint::Impl(this)) { } bool render(RenderMethod* renderer) { if (!impl.rd) return false; RenderCompositor* cmp = nullptr; renderer->blend(impl.blendMethod); if (impl.cmpFlag) { cmp = renderer->target(bounds(), renderer->colorSpace(), impl.cmpFlag); renderer->beginComposite(cmp, MaskMethod::None, opacity); } auto ret = renderer->renderShape(impl.rd); if (cmp) renderer->endComposite(cmp); return ret; } bool needComposition(uint8_t opacity) { if (opacity == 0) return false; //Shape composition is only necessary when stroking & fill are valid. if (!rs.stroke || rs.stroke->width < FLOAT_EPSILON || (!rs.stroke->fill && rs.stroke->color.a == 0)) return false; if (!rs.fill && rs.color.a == 0) return false; //translucent fill & stroke if (opacity < 255) { impl.mark(CompositionFlag::Opacity); return true; } //Composition test const Paint* target; auto method = PAINT(this)->mask(&target); if (!target) return false; if ((target->pImpl->opacity == 255 || target->pImpl->opacity == 0) && target->type() == tvg::Type::Shape) { auto shape = static_cast(target); if (!shape->fill()) { uint8_t r, g, b, a; shape->fill(&r, &g, &b, &a); if (a == 0 || a == 255) { if (method == MaskMethod::Luma || method == MaskMethod::InvLuma) { if ((r == 255 && g == 255 && b == 255) || (r == 0 && g == 0 && b == 0)) return false; } else return false; } } } impl.mark(CompositionFlag::Masking); return true; } bool skip(RenderUpdateFlag flag) { if (flag == RenderUpdateFlag::None) return true; return false; } bool update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) { if (needComposition(opacity)) { /* Overriding opacity value. If this scene is half-translucent, It must do intermediate composition with that opacity value. */ this->opacity = opacity; opacity = 255; } impl.rd = renderer->prepare(rs, impl.rd, transform, clips, opacity, flag, clipper); return true; } RenderRegion bounds() { return impl.renderer->region(impl.rd); } bool bounds(Point* pt4, const Matrix& m, bool obb) { auto fallback = true; //TODO: remove this when all backend engines support bounds() if (impl.renderer && rs.strokeWidth() > 0.0f) { if (impl.renderer->bounds(impl.rd, pt4, obb ? tvg::identity() : m)) { fallback = false; } } //Keep this for legacy. loaders still depend on this logic, remove it if possible. if (fallback) { BBox box = {{FLT_MAX, FLT_MAX}, {-FLT_MAX, -FLT_MAX}}; if (!rs.path.bounds(obb ? nullptr : &m, box)) return false; if (rs.stroke) { //Use geometric mean for feathering. //Join, Cap wouldn't be considered. Generate stroke outline and compute bbox for accurate size? auto sx = sqrt(m.e11 * m.e11 + m.e21 * m.e21); auto sy = sqrt(m.e12 * m.e12 + m.e22 * m.e22); auto feather = rs.stroke->width * sqrt(sx * sy); box.min.x -= feather * 0.5f; box.min.y -= feather * 0.5f; box.max.x += feather * 0.5f; box.max.y += feather * 0.5f; } pt4[0] = box.min; pt4[1] = {box.max.x, box.min.y}; pt4[2] = box.max; pt4[3] = {box.min.x, box.max.y}; } if (obb) { pt4[0] *= m; pt4[1] *= m; pt4[2] *= m; pt4[3] *= m; } return true; } void reserveCmd(uint32_t cmdCnt) { rs.path.cmds.reserve(cmdCnt); } void reservePts(uint32_t ptsCnt) { rs.path.pts.reserve(ptsCnt); } void grow(uint32_t cmdCnt, uint32_t ptsCnt) { rs.path.cmds.grow(cmdCnt); rs.path.pts.grow(ptsCnt); } void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) { memcpy(rs.path.cmds.end(), cmds, sizeof(PathCommand) * cmdCnt); memcpy(rs.path.pts.end(), pts, sizeof(Point) * ptsCnt); rs.path.cmds.count += cmdCnt; rs.path.pts.count += ptsCnt; } void strokeWidth(float width) { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->width = width; impl.mark(RenderUpdateFlag::Stroke); } void trimpath(const RenderTrimPath& trim) { if (!rs.stroke) { if (trim.begin == 0.0f && trim.end == 1.0f) return; rs.stroke = new RenderStroke(); } if (tvg::equal(rs.stroke->trim.begin, trim.begin) && tvg::equal(rs.stroke->trim.end, trim.end) && rs.stroke->trim.simultaneous == trim.simultaneous) return; rs.stroke->trim = trim; impl.mark(RenderUpdateFlag::Path); } bool trimpath(float* begin, float* end) { if (rs.stroke) { if (begin) *begin = rs.stroke->trim.begin; if (end) *end = rs.stroke->trim.end; return rs.stroke->trim.simultaneous; } else { if (begin) *begin = 0.0f; if (end) *end = 1.0f; return false; } } void strokeCap(StrokeCap cap) { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->cap = cap; impl.mark(RenderUpdateFlag::Stroke); } void strokeJoin(StrokeJoin join) { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->join = join; impl.mark(RenderUpdateFlag::Stroke); } Result strokeMiterlimit(float miterlimit) { // https://www.w3.org/TR/SVG2/painting.html#LineJoin // - A negative value for stroke-miterlimit must be treated as an illegal value. if (miterlimit < 0.0f) return Result::InvalidArguments; if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->miterlimit = miterlimit; impl.mark(RenderUpdateFlag::Stroke); return Result::Success; } bool intersects(const RenderRegion& region) { if (!impl.rd || !impl.renderer) return false; return impl.renderer->intersectsShape(impl.rd, region); } void strokeFill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (!rs.stroke) rs.stroke = new RenderStroke(); if (rs.stroke->fill) { delete(rs.stroke->fill); rs.stroke->fill = nullptr; impl.mark(RenderUpdateFlag::GradientStroke); } rs.stroke->color = {r, g, b, a}; impl.mark(RenderUpdateFlag::Stroke); } Result strokeFill(Fill* f) { if (!f) return Result::InvalidArguments; if (!rs.stroke) rs.stroke = new RenderStroke(); if (rs.stroke->fill && rs.stroke->fill != f) delete(rs.stroke->fill); rs.stroke->fill = f; rs.stroke->color.a = 0; impl.mark(RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke); return Result::Success; } Result strokeDash(const float* pattern, uint32_t cnt, float offset) { if ((!pattern && cnt > 0) || (pattern && cnt == 0)) return Result::InvalidArguments; if (!rs.stroke) rs.stroke = new RenderStroke; //Reset dash auto& dash = rs.stroke->dash; if (dash.count != cnt) { tvg::free(dash.pattern); dash.pattern = nullptr; } if (cnt > 0) { if (!dash.pattern) dash.pattern = tvg::malloc(sizeof(float) * cnt); dash.length = 0.0f; for (uint32_t i = 0; i < cnt; ++i) { dash.pattern[i] = pattern[i] < 0.0f ? 0.0f : pattern[i]; dash.length += dash.pattern[i]; } } rs.stroke->dash.count = cnt; rs.stroke->dash.offset = offset; impl.mark(RenderUpdateFlag::Stroke); return Result::Success; } bool strokeFirst() { if (!rs.stroke) return true; return rs.stroke->first; } void strokeFirst(bool first) { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->first = first; impl.mark(RenderUpdateFlag::Stroke); } Result fill(Fill* f) { if (!f) return Result::InvalidArguments; if (rs.fill && rs.fill != f) delete(rs.fill); rs.fill = f; impl.mark(RenderUpdateFlag::Gradient); return Result::Success; } void fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (rs.fill) { delete(rs.fill); rs.fill = nullptr; impl.mark(RenderUpdateFlag::Gradient); } if (r == rs.color.r && g == rs.color.g && b == rs.color.b && a == rs.color.a) return; rs.color = {r, g, b, a}; impl.mark(RenderUpdateFlag::Color); } void resetPath() { rs.path.cmds.clear(); rs.path.pts.clear(); impl.mark(RenderUpdateFlag::Path); } Result appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) { if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments; grow(cmdCnt, ptsCnt); append(cmds, cmdCnt, pts, ptsCnt); impl.mark(RenderUpdateFlag::Path); return Result::Success; } void appendCircle(float cx, float cy, float rx, float ry, bool cw) { auto rxKappa = rx * PATH_KAPPA; auto ryKappa = ry * PATH_KAPPA; rs.path.cmds.grow(6); auto cmds = rs.path.cmds.end(); cmds[0] = PathCommand::MoveTo; cmds[1] = PathCommand::CubicTo; cmds[2] = PathCommand::CubicTo; cmds[3] = PathCommand::CubicTo; cmds[4] = PathCommand::CubicTo; cmds[5] = PathCommand::Close; rs.path.cmds.count += 6; int table[2][13] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, {0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 12}}; int* idx = cw ? table[0] : table[1]; rs.path.pts.grow(13); auto pts = rs.path.pts.end(); pts[idx[0]] = {cx, cy - ry}; //moveTo pts[idx[1]] = {cx + rxKappa, cy - ry}; pts[idx[2]] = {cx + rx, cy - ryKappa}; pts[idx[3]] = {cx + rx, cy}; //cubicTo pts[idx[4]] = {cx + rx, cy + ryKappa}; pts[idx[5]] = {cx + rxKappa, cy + ry}; pts[idx[6]] = {cx, cy + ry}; //cubicTo pts[idx[7]] = {cx - rxKappa, cy + ry}; pts[idx[8]] = {cx - rx, cy + ryKappa}; pts[idx[9]] = {cx - rx, cy}; //cubicTo pts[idx[10]] = {cx - rx, cy - ryKappa}; pts[idx[11]] = {cx - rxKappa, cy - ry}; pts[idx[12]] = {cx, cy - ry}; //cubicTo rs.path.pts.count += 13; impl.mark(RenderUpdateFlag::Path); } void appendRect(float x, float y, float w, float h, float rx, float ry, bool cw) { //sharp rect if (tvg::zero(rx) && tvg::zero(ry)) { rs.path.cmds.grow(5); rs.path.pts.grow(4); auto cmds = rs.path.cmds.end(); auto pts = rs.path.pts.end(); cmds[0] = PathCommand::MoveTo; cmds[1] = cmds[2] = cmds[3] = PathCommand::LineTo; cmds[4] = PathCommand::Close; pts[0] = {x + w, y}; pts[2] = {x, y + h}; if (cw) { pts[1] = {x + w, y + h}; pts[3] = {x, y}; } else { pts[1] = {x, y}; pts[3] = {x + w, y + h}; } rs.path.cmds.count += 5; rs.path.pts.count += 4; //round rect } else { auto hsize = Point{w * 0.5f, h * 0.5f}; rx = (rx > hsize.x) ? hsize.x : rx; ry = (ry > hsize.y) ? hsize.y : ry; auto hr = Point{rx * PATH_KAPPA, ry * PATH_KAPPA}; rs.path.cmds.grow(10); rs.path.pts.grow(17); auto cmds = rs.path.cmds.end(); auto pts = rs.path.pts.end(); cmds[0] = PathCommand::MoveTo; cmds[9] = PathCommand::Close; pts[0] = {x + w, y + ry}; //move if (cw) { cmds[1] = cmds[3] = cmds[5] = cmds[7] = PathCommand::LineTo; cmds[2] = cmds[4] = cmds[6] = cmds[8] = PathCommand::CubicTo; pts[1] = {x + w, y + h - ry}; //line pts[2] = {x + w, y + h - ry + hr.y}; pts[3] = {x + w - rx + hr.x, y + h}; pts[4] = {x + w - rx, y + h}; //cubic pts[5] = {x + rx, y + h}, //line pts[6] = {x + rx - hr.x, y + h}; pts[7] = {x, y + h - ry + hr.y}; pts[8] = {x, y + h - ry}; //cubic pts[9] = {x, y + ry}, //line pts[10] = {x, y + ry - hr.y}; pts[11] = {x + rx - hr.x, y}; pts[12] = {x + rx, y}; //cubic pts[13] = {x + w - rx, y}; //line pts[14] = {x + w - rx + hr.x, y}; pts[15] = {x + w, y + ry - hr.y}; pts[16] = {x + w, y + ry}; //cubic } else { cmds[1] = cmds[3] = cmds[5] = cmds[7] = PathCommand::CubicTo; cmds[2] = cmds[4] = cmds[6] = cmds[8] = PathCommand::LineTo; pts[1] = {x + w, y + ry - hr.y}; pts[2] = {x + w - rx + hr.x, y}; pts[3] = {x + w - rx, y}; //cubic pts[4] = {x + rx, y}; //line pts[5] = {x + rx - hr.x, y}; pts[6] = {x, y + ry - hr.y}; pts[7] = {x, y + ry}; //cubic pts[8] = {x, y + h - ry}; //line pts[9] = {x, y + h - ry + hr.y}; pts[10] = {x + rx - hr.x, y + h}; pts[11] = {x + rx, y + h}; //cubic pts[12] = {x + w - rx, y + h}; //line pts[13] = {x + w - rx + hr.x, y + h}; pts[14] = {x + w, y + h - ry + hr.y}; pts[15] = {x + w, y + h - ry}; //cubic pts[16] = {x + w, y + ry}; //line } rs.path.cmds.count += 10; rs.path.pts.count += 17; } impl.mark(RenderUpdateFlag::Path); } Paint* duplicate(Paint* ret) { auto shape = static_cast(ret); if (!shape) shape = Shape::gen(); auto dup = to(shape); //Path dup->rs.path.clear(); dup->rs.path.cmds.push(rs.path.cmds); dup->rs.path.pts.push(rs.path.pts); //Fill delete(dup->rs.fill); if (rs.fill) dup->rs.fill = rs.fill->duplicate(); else dup->rs.fill = nullptr; //Stroke if (rs.stroke) { if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke; *dup->rs.stroke = *rs.stroke; } else { delete(dup->rs.stroke); dup->rs.stroke = nullptr; } dup->rs.color = rs.color; dup->rs.rule = rs.rule; return shape; } void reset() { PAINT(this)->reset(); rs.path.cmds.clear(); rs.path.pts.clear(); rs.color.a = 0; rs.rule = FillRule::NonZero; delete(rs.stroke); rs.stroke = nullptr; delete(rs.fill); rs.fill = nullptr; } Iterator* iterator() { return nullptr; } }; } #endif //_TVG_SHAPE_H_ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/parser-errors.h000664 001750 001750 00000002600 15164251010 044204 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 PARSER_ERRORS_H #define PARSER_ERRORS_H #include "lit-globals.h" typedef enum { PARSER_ERR_EMPTY, /** @cond doxygen_suppress */ #if JERRY_ERROR_MESSAGES #define PARSER_ERROR_DEF(id, ascii_zt_string) id, #else /* !JERRY_ERROR_MESSAGES */ #define PARSER_ERROR_DEF(id, ascii_zt_string) id = PARSER_ERR_EMPTY, #endif /* JERRY_ERROR_MESSAGES */ #include "parser-error-messages.inc.h" #undef PARSER_ERROR_DEF /** @endcond */ PARSER_ERR_OUT_OF_MEMORY, PARSER_ERR_INVALID_REGEXP, #if (JERRY_STACK_LIMIT != 0) PARSER_ERR_STACK_OVERFLOW, #endif /* JERRY_STACK_LIMIT != 0 */ PARSER_ERR_NO_ERROR, } parser_error_msg_t; const lit_utf8_byte_t* parser_get_error_utf8 (uint32_t id); lit_utf8_size_t parser_get_error_size (uint32_t id); #endif /* !PARSER_ERRORS_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-syntaxerror.cpp000664 001750 001750 00000005462 15164251010 051003 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-syntaxerror.inc.h" #define BUILTIN_UNDERSCORED_ID syntax_error #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup syntaxerror ECMA SyntaxError object built-in * @{ */ /** * Handle calling [[Call]] of built-in SyntaxError object * * @return ecma value */ ecma_value_t ecma_builtin_syntax_error_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_helper_error_dispatch_call (JERRY_ERROR_SYNTAX, arguments_list_p, arguments_list_len); } /* ecma_builtin_syntax_error_dispatch_call */ /** * Handle calling [[Construct]] of built-in SyntaxError object * * @return ecma value */ ecma_value_t ecma_builtin_syntax_error_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_SYNTAX_ERROR_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_syntax_error_dispatch_call (arguments_list_p, arguments_list_len); if (!ECMA_IS_VALUE_ERROR (result)) { ecma_object_t *object_p = ecma_get_object_from_value (result); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, proto_p); } ecma_deref_object (proto_p); return result; } /* ecma_builtin_syntax_error_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ERRORS */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp000664 001750 001750 00000047743 15164251010 051750 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-regexp-object.h" #include "lit-char-helpers.h" #if JERRY_BUILTIN_REGEXP #include "ecma-regexp-object.h" #include "re-compiler.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH #define BUILTIN_INC_HEADER_NAME "ecma-builtin-regexp-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID regexp_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup regexpprototype ECMA RegExp.prototype object built-in * @{ */ /** * Helper function to retrieve the flags associated with a RegExp object * * @return ECMA_VALUE_{TRUE,FALSE} depends on whether the given flag is present. */ static ecma_value_t ecma_builtin_regexp_prototype_flags_helper (ecma_extended_object_t *re_obj_p, /**< this object */ uint16_t builtin_routine_id) /**< id of the flag */ { re_compiled_code_t *bc_p = ECMA_GET_INTERNAL_VALUE_POINTER (re_compiled_code_t, re_obj_p->u.cls.u3.value); uint16_t flags = bc_p->header.status_flags; static const uint8_t re_flags[] = { RE_FLAG_GLOBAL, RE_FLAG_IGNORE_CASE, RE_FLAG_MULTILINE, RE_FLAG_STICKY, RE_FLAG_UNICODE, RE_FLAG_DOTALL, }; uint16_t offset = (uint16_t) (builtin_routine_id - ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_GLOBAL); return ecma_make_boolean_value ((flags & re_flags[offset]) != 0); } /* ecma_builtin_regexp_prototype_flags_helper */ /** * The RegExp.prototype object's 'flags' accessor property * * See also: * ECMA-262 v6, 21.2.5.3 * * @return ECMA_VALUE_ERROR - if 'this' is not a RegExp object * string value - otherwise * * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_regexp_prototype_get_flags (ecma_object_t *object_p) /**< this object */ { static const lit_magic_string_id_t flag_lit_ids[] = { LIT_MAGIC_STRING_GLOBAL, LIT_MAGIC_STRING_IGNORECASE_UL, LIT_MAGIC_STRING_MULTILINE, LIT_MAGIC_STRING_DOTALL, LIT_MAGIC_STRING_UNICODE, LIT_MAGIC_STRING_STICKY }; static const lit_utf8_byte_t flag_chars[] = { LIT_CHAR_LOWERCASE_G, LIT_CHAR_LOWERCASE_I, LIT_CHAR_LOWERCASE_M, LIT_CHAR_LOWERCASE_S, LIT_CHAR_LOWERCASE_U, LIT_CHAR_LOWERCASE_Y }; ecma_stringbuilder_t builder = ecma_stringbuilder_create (); for (uint32_t i = 0; i < sizeof (flag_lit_ids) / sizeof (lit_magic_string_id_t); i++) { ecma_value_t result = ecma_op_object_get_by_magic_id (object_p, flag_lit_ids[i]); if (ECMA_IS_VALUE_ERROR (result)) { ecma_stringbuilder_destroy (&builder); return result; } if (ecma_op_to_boolean (result)) { ecma_stringbuilder_append_byte (&builder, flag_chars[i]); } ecma_free_value (result); } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_regexp_prototype_get_flags */ /** * The EscapeRegExpPattern method. * * See also: * ECMA-262 v6, 21.2.3.2.4 * * @return ecma_value_t */ static ecma_value_t ecma_op_escape_regexp_pattern (ecma_string_t *pattern_str_p) /**< RegExp pattern */ { ecma_stringbuilder_t builder = ecma_stringbuilder_create (); ECMA_STRING_TO_UTF8_STRING (pattern_str_p, pattern_start_p, pattern_start_size); const lit_utf8_byte_t *pattern_str_curr_p = pattern_start_p; const lit_utf8_byte_t *pattern_str_end_p = pattern_start_p + pattern_start_size; while (pattern_str_curr_p < pattern_str_end_p) { ecma_char_t c = lit_cesu8_read_next (&pattern_str_curr_p); switch (c) { case LIT_CHAR_SLASH: { ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) "\\/", 2); break; } case LIT_CHAR_LF: { ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) "\\n", 2); break; } case LIT_CHAR_CR: { ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) "\\r", 2); break; } case LIT_CHAR_LS: { ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) "\\u2028", 6); break; } case LIT_CHAR_PS: { ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) "\\u2029", 6); break; } case LIT_CHAR_BACKSLASH: { JERRY_ASSERT (pattern_str_curr_p < pattern_str_end_p); ecma_stringbuilder_append_char (&builder, LIT_CHAR_BACKSLASH); ecma_stringbuilder_append_char (&builder, lit_cesu8_read_next (&pattern_str_curr_p)); break; } default: { ecma_stringbuilder_append_char (&builder, c); break; } } } ECMA_FINALIZE_UTF8_STRING (pattern_start_p, pattern_start_size); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_op_escape_regexp_pattern */ /** * The RegExp.prototype object's 'source' accessor property * * See also: * ECMA-262 v6, 21.2.5.10 * * @return ECMA_VALUE_ERROR - if 'this' is not a RegExp object * string value - otherwise * * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_regexp_prototype_get_source (ecma_extended_object_t *re_obj_p) /**< this argument */ { re_compiled_code_t *bc_p = ECMA_GET_INTERNAL_VALUE_POINTER (re_compiled_code_t, re_obj_p->u.cls.u3.value); return ecma_op_escape_regexp_pattern (ecma_get_string_from_value (bc_p->source)); } /* ecma_builtin_regexp_prototype_get_source */ #if JERRY_BUILTIN_ANNEXB /** * The RegExp.prototype object's 'compile' routine * * See also: * ECMA-262 v11, B.2.5.1 * * @return undefined - if compiled successfully * error ecma value - otherwise * * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_regexp_prototype_compile (ecma_value_t this_arg, /**< this */ ecma_value_t pattern_arg, /**< pattern or RegExp object */ ecma_value_t flags_arg) /**< flags */ { ecma_object_t *this_obj_p = ecma_get_object_from_value (this_arg); ecma_extended_object_t *re_obj_p = (ecma_extended_object_t *) this_obj_p; re_compiled_code_t *old_bc_p = ECMA_GET_INTERNAL_VALUE_POINTER (re_compiled_code_t, re_obj_p->u.cls.u3.value); ecma_value_t ret_value; if (ecma_object_is_regexp_object (pattern_arg)) { if (!ecma_is_value_undefined (flags_arg)) { return ecma_raise_type_error (ECMA_ERR_INVALID_ARGUMENT); } ecma_extended_object_t *pattern_obj_p = (ecma_extended_object_t *) ecma_get_object_from_value (pattern_arg); re_compiled_code_t *bc_p = ECMA_GET_INTERNAL_VALUE_POINTER (re_compiled_code_t, pattern_obj_p->u.cls.u3.value); ret_value = ecma_op_create_regexp_from_bytecode (this_obj_p, bc_p); } else { ret_value = ecma_op_create_regexp_from_pattern (this_obj_p, pattern_arg, flags_arg); } if (!ECMA_IS_VALUE_ERROR (ret_value)) { ecma_value_t status = ecma_builtin_helper_def_prop (this_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), ecma_make_uint32_value (0), ECMA_PROPERTY_FLAG_WRITABLE | JERRY_PROP_SHOULD_THROW); ecma_bytecode_deref ((ecma_compiled_code_t *) old_bc_p); if (ECMA_IS_VALUE_ERROR (status)) { return status; } ecma_ref_object (this_obj_p); } return ret_value; } /* ecma_builtin_regexp_prototype_compile */ #endif /* JERRY_BUILTIN_ANNEXB */ /** * The RegExp.prototype object's 'exec' routine * * See also: * ECMA-262 v5, 15.10.6.2 * * @return array object containing the results - if the matched * null - otherwise * * May raise error, so returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_regexp_prototype_exec (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg) /**< routine's argument */ { ecma_value_t obj_this = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (obj_this)) { return obj_this; } ecma_string_t *input_str_p = ecma_op_to_string (arg); if (JERRY_UNLIKELY (input_str_p == NULL)) { ecma_free_value (obj_this); return ECMA_VALUE_ERROR; } ecma_value_t ret_value = ecma_regexp_exec_helper (ecma_get_object_from_value (obj_this), input_str_p); ecma_free_value (obj_this); ecma_deref_ecma_string (input_str_p); return ret_value; } /* ecma_builtin_regexp_prototype_exec */ /** * The RegExp.prototype object's 'test' routine * * See also: * ECMA-262 v5, 15.10.6.3 * ECMA-262 v6, 21.2.5.13 * * @return true - if match is not null * false - otherwise * * May raise error, so returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_regexp_prototype_test (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg) /**< routine's argument */ { ecma_string_t *arg_str_p = ecma_op_to_string (arg); if (JERRY_UNLIKELY (arg_str_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_op_regexp_exec (this_arg, arg_str_p); ecma_deref_ecma_string (arg_str_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } ecma_value_t ret_value = ecma_make_boolean_value (!ecma_is_value_null (result)); ecma_free_value (result); return ret_value; } /* ecma_builtin_regexp_prototype_test */ /** * The RegExp.prototype object's 'toString' routine * * See also: * ECMA-262 v5, 15.10.6.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_regexp_prototype_to_string (ecma_object_t *object_p) /**< this object */ { ecma_value_t result = ecma_op_object_get_by_magic_id (object_p, LIT_MAGIC_STRING_SOURCE); if (ECMA_IS_VALUE_ERROR (result)) { return result; } ecma_string_t *source_p = ecma_op_to_string (result); ecma_free_value (result); if (source_p == NULL) { return ECMA_VALUE_ERROR; } result = ecma_op_object_get_by_magic_id (object_p, LIT_MAGIC_STRING_FLAGS); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_ecma_string (source_p); return result; } ecma_string_t *flags_p = ecma_op_to_string (result); ecma_free_value (result); if (flags_p == NULL) { ecma_deref_ecma_string (source_p); return ECMA_VALUE_ERROR; } ecma_stringbuilder_t builder = ecma_stringbuilder_create (); ecma_stringbuilder_append_byte (&builder, LIT_CHAR_SLASH); ecma_stringbuilder_append (&builder, source_p); ecma_stringbuilder_append_byte (&builder, LIT_CHAR_SLASH); ecma_stringbuilder_append (&builder, flags_p); ecma_deref_ecma_string (source_p); ecma_deref_ecma_string (flags_p); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_regexp_prototype_to_string */ /** * The RegExp.prototype object's 'matchAll' routine * * See also: * ECMA-262 v11, 21.2.5.8 * * @return ecma_value_t */ static ecma_value_t ecma_builtin_regexp_prototype_match_all (ecma_object_t *regexp_obj_p, /**< this argument */ ecma_value_t string_arg) /**< source string */ { /* 3. */ ecma_string_t *str_p = ecma_op_to_string (string_arg); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } /* 4. */ ecma_value_t constructor = ecma_op_species_constructor (regexp_obj_p, ECMA_BUILTIN_ID_REGEXP); if (ECMA_IS_VALUE_ERROR (constructor)) { ecma_deref_ecma_string (str_p); return constructor; } /* 5. */ ecma_value_t get_flag = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_FLAGS); if (ECMA_IS_VALUE_ERROR (get_flag)) { ecma_deref_ecma_string (str_p); ecma_free_value (constructor); return get_flag; } ecma_string_t *flags = ecma_op_to_string (get_flag); ecma_free_value (get_flag); if (JERRY_UNLIKELY (flags == NULL)) { ecma_deref_ecma_string (str_p); ecma_free_value (constructor); return ECMA_VALUE_ERROR; } /* 6. */ ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor); ecma_value_t flags_value = ecma_make_string_value (flags); ecma_value_t match_args[] = { ecma_make_object_value (regexp_obj_p), flags_value }; ecma_value_t matcher = ecma_op_function_construct (constructor_obj_p, constructor_obj_p, match_args, 2); ecma_deref_object (constructor_obj_p); if (ECMA_IS_VALUE_ERROR (matcher)) { ecma_deref_ecma_string (str_p); ecma_deref_ecma_string (flags); return matcher; } /* 7. */ ecma_value_t get_last_index = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_LASTINDEX_UL); if (ECMA_IS_VALUE_ERROR (get_last_index)) { ecma_deref_ecma_string (str_p); ecma_deref_ecma_string (flags); ecma_free_value (matcher); return get_last_index; } ecma_length_t last_index; ecma_value_t to_len = ecma_op_to_length (get_last_index, &last_index); ecma_free_value (get_last_index); if (ECMA_IS_VALUE_ERROR (to_len)) { ecma_deref_ecma_string (str_p); ecma_deref_ecma_string (flags); ecma_free_value (matcher); return to_len; } /* 8. */ ecma_object_t *matcher_obj_p = ecma_get_object_from_value (matcher); ecma_value_t last_index_value = ecma_make_length_value (last_index); ecma_value_t set = ecma_op_object_put (matcher_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), last_index_value, true); ecma_free_value (last_index_value); if (ECMA_IS_VALUE_ERROR (set)) { ecma_deref_ecma_string (str_p); ecma_deref_ecma_string (flags); ecma_deref_object (matcher_obj_p); } uint16_t parsed_flag; ecma_value_t flag_parse = ecma_regexp_parse_flags (flags, &parsed_flag); ecma_deref_ecma_string (flags); if (ECMA_IS_VALUE_ERROR (flag_parse)) { ecma_deref_ecma_string (str_p); ecma_deref_object (matcher_obj_p); return flag_parse; } /* 13. */ ecma_object_t *result_obj; ecma_object_t *proto_p = ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP_STRING_ITERATOR_PROTOTYPE); result_obj = ecma_create_object (proto_p, sizeof (ecma_regexp_string_iterator_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) result_obj; ext_obj_p->u.cls.type = ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR; ext_obj_p->u.cls.u1.regexp_string_iterator_flags = (uint8_t) (parsed_flag & (RE_FLAG_GLOBAL | RE_FLAG_UNICODE)); ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) result_obj; regexp_string_iterator_obj->iterating_regexp = matcher; regexp_string_iterator_obj->iterated_string = ecma_make_string_value (str_p); ecma_deref_object (matcher_obj_p); return ecma_make_object_value (result_obj); } /* ecma_builtin_regexp_prototype_match_all */ /** * Dispatcher of the Regexp built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_regexp_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); ecma_object_t *obj_p = NULL; /* 1. && 2. */ if (ecma_is_value_object (this_arg)) { obj_p = ecma_get_object_from_value (this_arg); if (builtin_routine_id < ECMA_REGEXP_PROTOTYPE_ROUTINE_TEST && !ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_REGEXP)) { obj_p = NULL; } } if (obj_p == NULL) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } switch (builtin_routine_id) { #if JERRY_BUILTIN_ANNEXB case ECMA_REGEXP_PROTOTYPE_ROUTINE_COMPILE: { return ecma_builtin_regexp_prototype_compile (this_arg, arguments_list_p[0], arguments_list_p[1]); } #endif /* JERRY_BUILTIN_ANNEXB */ case ECMA_REGEXP_PROTOTYPE_ROUTINE_TEST: { return ecma_builtin_regexp_prototype_test (this_arg, arguments_list_p[0]); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_EXEC: { return ecma_builtin_regexp_prototype_exec (this_arg, arguments_list_p[0]); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_TO_STRING: { return ecma_builtin_regexp_prototype_to_string (obj_p); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SEARCH: { return ecma_regexp_search_helper (this_arg, arguments_list_p[0]); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH: { return ecma_regexp_match_helper (this_arg, arguments_list_p[0]); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH_ALL: { return ecma_builtin_regexp_prototype_match_all (obj_p, arguments_list_p[0]); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_REPLACE: { return ecma_regexp_replace_helper (this_arg, arguments_list_p[0], arguments_list_p[1]); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SPLIT: { return ecma_regexp_split_helper (this_arg, arguments_list_p[0], arguments_list_p[1]); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_FLAGS: { return ecma_builtin_regexp_prototype_get_flags (obj_p); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_SOURCE: { if (!ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_REGEXP)) { if (obj_p == ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP_PROTOTYPE)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_EMPTY_NON_CAPTURE_GROUP); } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_REGEXP); } ecma_extended_object_t *re_obj_p = (ecma_extended_object_t *) obj_p; return ecma_builtin_regexp_prototype_get_source (re_obj_p); } case ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_GLOBAL: case ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_IGNORE_CASE: case ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_MULTILINE: case ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_STICKY: case ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_UNICODE: case ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_DOT_ALL: { if (!ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_REGEXP)) { if (obj_p == ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP_PROTOTYPE)) { return ECMA_VALUE_UNDEFINED; } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_REGEXP); } ecma_extended_object_t *re_obj_p = (ecma_extended_object_t *) obj_p; return ecma_builtin_regexp_prototype_flags_helper (re_obj_p, builtin_routine_id); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_regexp_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-container-object.cpp000664 001750 001750 00000106612 15164251010 047252 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-container-object.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #include "ecma-property-hashmap.h" #include "jcontext.h" #if JERRY_BUILTIN_CONTAINER /** \addtogroup ecma ECMA * @{ * * \addtogroup \addtogroup ecmamaphelpers ECMA builtin Map/Set helper functions * @{ */ /** * Create a new internal buffer. * * Note: * The first element of the collection tracks the size of the buffer. * ECMA_VALUE_EMPTY values are not calculated into the size. * * @return pointer to the internal buffer */ static inline ecma_collection_t * ecma_op_create_internal_buffer (void) { ecma_collection_t *collection_p = ecma_new_collection (); ecma_collection_push_back (collection_p, (ecma_value_t) 0); return collection_p; } /* ecma_op_create_internal_buffer */ /** * Append values to the internal buffer. */ static void ecma_op_internal_buffer_append (ecma_collection_t *container_p, /**< internal container pointer */ ecma_value_t key_arg, /**< key argument */ ecma_value_t value_arg, /**< value argument */ lit_magic_string_id_t lit_id) /**< class id */ { JERRY_ASSERT (container_p != NULL); if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) { ecma_value_t values[] = { ecma_copy_value_if_not_object (key_arg), ecma_copy_value_if_not_object (value_arg) }; ecma_collection_append (container_p, values, 2); } else { ecma_collection_push_back (container_p, ecma_copy_value_if_not_object (key_arg)); } ECMA_CONTAINER_SET_SIZE (container_p, ECMA_CONTAINER_GET_SIZE (container_p) + 1); } /* ecma_op_internal_buffer_append */ /** * Update the value of a given entry. */ static inline void ecma_op_internal_buffer_update (ecma_value_t *entry_p, /**< entry pointer */ ecma_value_t value_arg, /**< value argument */ lit_magic_string_id_t lit_id) /**< class id */ { JERRY_ASSERT (entry_p != NULL); if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) { ecma_free_value_if_not_object (((ecma_container_pair_t *) entry_p)->value); ((ecma_container_pair_t *) entry_p)->value = ecma_copy_value_if_not_object (value_arg); } } /* ecma_op_internal_buffer_update */ /** * Delete element from the internal buffer. */ static void ecma_op_internal_buffer_delete (ecma_collection_t *container_p, /**< internal container pointer */ ecma_container_pair_t *entry_p, /**< entry pointer */ lit_magic_string_id_t lit_id) /**< class id */ { JERRY_ASSERT (container_p != NULL); JERRY_ASSERT (entry_p != NULL); ecma_free_value_if_not_object (entry_p->key); entry_p->key = ECMA_VALUE_EMPTY; if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) { ecma_free_value_if_not_object (entry_p->value); entry_p->value = ECMA_VALUE_EMPTY; } ECMA_CONTAINER_SET_SIZE (container_p, ECMA_CONTAINER_GET_SIZE (container_p) - 1); } /* ecma_op_internal_buffer_delete */ /** * Find an entry in the collection. * * @return pointer to the appropriate entry. */ static ecma_value_t * ecma_op_internal_buffer_find (ecma_collection_t *container_p, /**< internal container pointer */ ecma_value_t key_arg, /**< key argument */ lit_magic_string_id_t lit_id) /**< class id */ { JERRY_ASSERT (container_p != NULL); uint8_t entry_size = ecma_op_container_entry_size (lit_id); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); for (uint32_t i = 0; i < entry_count; i += entry_size) { ecma_value_t *entry_p = start_p + i; if (ecma_op_same_value_zero (*entry_p, key_arg, false)) { return entry_p; } } return NULL; } /* ecma_op_internal_buffer_find */ /** * Get the value that belongs to the key. * * Note: in case of Set containers, the values are the same as the keys. * * @return ecma value */ static ecma_value_t ecma_op_container_get_value (ecma_value_t *entry_p, /**< entry (key) pointer */ lit_magic_string_id_t lit_id) /**< class id */ { JERRY_ASSERT (entry_p != NULL); if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) { return ((ecma_container_pair_t *) entry_p)->value; } return *entry_p; } /* ecma_op_container_get_value */ /** * Get the size (in ecma_value_t) of the stored entries. * * @return size of the entries. */ uint8_t ecma_op_container_entry_size (lit_magic_string_id_t lit_id) /**< class id */ { if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) { return ECMA_CONTAINER_PAIR_SIZE; } return ECMA_CONTAINER_VALUE_SIZE; } /* ecma_op_container_entry_size */ /** * Release the entries in the WeakSet container. */ static void ecma_op_container_free_weakset_entries (ecma_object_t *object_p, /**< object pointer */ ecma_collection_t *container_p) /** internal buffer pointer */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (container_p != NULL); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_VALUE_SIZE) { ecma_value_t *entry_p = start_p + i; if (ecma_is_value_empty (*entry_p)) { continue; } ecma_op_object_unref_weak (ecma_get_object_from_value (*entry_p), ecma_make_object_value (object_p)); ecma_op_container_remove_weak_entry (object_p, *entry_p); *entry_p = ECMA_VALUE_EMPTY; } } /* ecma_op_container_free_weakset_entries */ /** * Release the entries in the WeakMap container. */ static void ecma_op_container_free_weakmap_entries (ecma_object_t *object_p, /**< object pointer */ ecma_collection_t *container_p) /**< internal buffer pointer */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (container_p != NULL); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE) { ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i); if (ecma_is_value_empty (entry_p->key)) { continue; } ecma_op_object_unref_weak (ecma_get_object_from_value (entry_p->key), ecma_make_object_value (object_p)); ecma_op_container_remove_weak_entry (object_p, entry_p->key); ecma_free_value_if_not_object (entry_p->value); entry_p->key = ECMA_VALUE_EMPTY; entry_p->value = ECMA_VALUE_EMPTY; } } /* ecma_op_container_free_weakmap_entries */ /** * Release the entries in the Set container. */ static void ecma_op_container_free_set_entries (ecma_collection_t *container_p) { JERRY_ASSERT (container_p != NULL); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_VALUE_SIZE) { ecma_value_t *entry_p = start_p + i; if (ecma_is_value_empty (*entry_p)) { continue; } ecma_free_value_if_not_object (*entry_p); *entry_p = ECMA_VALUE_EMPTY; } } /* ecma_op_container_free_set_entries */ /** * Release the entries in the Map container. */ static void ecma_op_container_free_map_entries (ecma_collection_t *container_p) { JERRY_ASSERT (container_p != NULL); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE) { ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i); if (ecma_is_value_empty (entry_p->key)) { continue; } ecma_free_value_if_not_object (entry_p->key); ecma_free_value_if_not_object (entry_p->value); entry_p->key = ECMA_VALUE_EMPTY; entry_p->value = ECMA_VALUE_EMPTY; } } /* ecma_op_container_free_map_entries */ /** * Release the internal buffer and the stored entries. */ void ecma_op_container_free_entries (ecma_object_t *object_p) /**< collection object pointer */ { JERRY_ASSERT (object_p != NULL); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); switch (map_object_p->u.cls.u2.container_id) { case LIT_MAGIC_STRING_WEAKSET_UL: { ecma_op_container_free_weakset_entries (object_p, container_p); break; } case LIT_MAGIC_STRING_WEAKMAP_UL: { ecma_op_container_free_weakmap_entries (object_p, container_p); break; } case LIT_MAGIC_STRING_SET_UL: { ecma_op_container_free_set_entries (container_p); break; } case LIT_MAGIC_STRING_MAP_UL: { ecma_op_container_free_map_entries (container_p); break; } default: { break; } } ECMA_CONTAINER_SET_SIZE (container_p, 0); } /* ecma_op_container_free_entries */ /** * Handle calling [[Construct]] of built-in Map/Set like objects * * @return ecma value */ ecma_value_t ecma_op_container_create (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len, /**< number of arguments */ lit_magic_string_id_t lit_id, /**< internal class id */ ecma_builtin_id_t proto_id) /**< prototype builtin id */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); JERRY_ASSERT (lit_id == LIT_MAGIC_STRING_MAP_UL || lit_id == LIT_MAGIC_STRING_SET_UL || lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_WEAKSET_UL); JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p) != NULL); ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), proto_id); if (JERRY_UNLIKELY (proto_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_collection_t *container_p = ecma_op_create_internal_buffer (); ecma_object_t *object_p = ecma_create_object (proto_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_deref_object (proto_p); ecma_extended_object_t *map_obj_p = (ecma_extended_object_t *) object_p; map_obj_p->u.cls.type = ECMA_OBJECT_CLASS_CONTAINER; map_obj_p->u.cls.u1.container_flags = ECMA_CONTAINER_FLAGS_EMPTY; map_obj_p->u.cls.u2.container_id = (uint16_t) lit_id; ecma_value_t iterator; ecma_object_t *adder_func_p; if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_WEAKSET_UL) { map_obj_p->u.cls.u1.container_flags |= ECMA_CONTAINER_FLAGS_WEAK; } ECMA_SET_INTERNAL_VALUE_POINTER (map_obj_p->u.cls.u3.value, container_p); ecma_value_t set_value = ecma_make_object_value (object_p); ecma_value_t result = set_value; if (arguments_list_len == 0) { return result; } ecma_value_t iterable = arguments_list_p[0]; if (ecma_is_value_undefined (iterable) || ecma_is_value_null (iterable)) { return result; } lit_magic_string_id_t adder_string_id; if (lit_id == LIT_MAGIC_STRING_MAP_UL || lit_id == LIT_MAGIC_STRING_WEAKMAP_UL) { adder_string_id = LIT_MAGIC_STRING_SET; } else { adder_string_id = LIT_MAGIC_STRING_ADD; } result = ecma_op_object_get_by_magic_id (object_p, adder_string_id); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_object; } if (!ecma_op_is_callable (result)) { ecma_free_value (result); result = ecma_raise_type_error (ECMA_ERR_FUNCTION_ADD_ORSET_IS_NOT_CALLABLE); goto cleanup_object; } adder_func_p = ecma_get_object_from_value (result); ecma_value_t next_method; result = ecma_op_get_iterator (iterable, ECMA_VALUE_SYNC_ITERATOR, &next_method); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_adder; } iterator = result; while (true) { result = ecma_op_iterator_step (iterator, next_method); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_iterator; } if (ecma_is_value_false (result)) { break; } const ecma_value_t next = result; result = ecma_op_iterator_value (next); ecma_free_value (next); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_iterator; } if (lit_id == LIT_MAGIC_STRING_SET_UL || lit_id == LIT_MAGIC_STRING_WEAKSET_UL) { const ecma_value_t value = result; ecma_value_t arguments[] = { value }; result = ecma_op_function_call (adder_func_p, set_value, arguments, 1); ecma_free_value (value); } else { if (!ecma_is_value_object (result)) { ecma_free_value (result); ecma_raise_type_error (ECMA_ERR_ITERATOR_VALUE_IS_NOT_AN_OBJECT); result = ecma_op_iterator_close (iterator); JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result)); goto cleanup_iterator; } ecma_object_t *next_object_p = ecma_get_object_from_value (result); result = ecma_op_object_get_by_index (next_object_p, 0); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (next_object_p); ecma_op_iterator_close (iterator); goto cleanup_iterator; } const ecma_value_t key = result; result = ecma_op_object_get_by_index (next_object_p, 1); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (next_object_p); ecma_free_value (key); ecma_op_iterator_close (iterator); goto cleanup_iterator; } const ecma_value_t value = result; ecma_value_t arguments[] = { key, value }; result = ecma_op_function_call (adder_func_p, set_value, arguments, 2); ecma_free_value (key); ecma_free_value (value); ecma_deref_object (next_object_p); } if (ECMA_IS_VALUE_ERROR (result)) { ecma_op_iterator_close (iterator); goto cleanup_iterator; } ecma_free_value (result); } ecma_ref_object (object_p); result = ecma_make_object_value (object_p); cleanup_iterator: ecma_free_value (iterator); ecma_free_value (next_method); cleanup_adder: ecma_deref_object (adder_func_p); cleanup_object: ecma_deref_object (object_p); return result; } /* ecma_op_container_create */ /** * Get Map/Set object pointer * * Note: * If the function returns with NULL, the error object has * already set, and the caller must return with ECMA_VALUE_ERROR * * @return pointer to the Map/Set if this_arg is a valid Map/Set object * NULL otherwise */ ecma_extended_object_t * ecma_op_container_get_object (ecma_value_t this_arg, /**< this argument */ lit_magic_string_id_t lit_id) /**< internal class id */ { if (ecma_is_value_object (this_arg)) { ecma_object_t *map_object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (map_object_p, ECMA_OBJECT_CLASS_CONTAINER) && ((ecma_extended_object_t *) map_object_p)->u.cls.u2.container_id == lit_id) { return (ecma_extended_object_t *) map_object_p; } } #if JERRY_ERROR_MESSAGES ecma_raise_standard_error_with_format (JERRY_ERROR_TYPE, "Expected a % object", ecma_make_string_value (ecma_get_magic_string (lit_id))); #else /* !JERRY_ERROR_MESSAGES */ ecma_raise_type_error (ECMA_ERR_EMPTY); #endif /* JERRY_ERROR_MESSAGES */ return NULL; } /* ecma_op_container_get_object */ /** * Returns with the size of the Map/Set object. * * @return size of the Map/Set object as ecma-value. */ ecma_value_t ecma_op_container_size (ecma_extended_object_t *map_object_p) /**< internal class id */ { ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); return ecma_make_uint32_value (ECMA_CONTAINER_GET_SIZE (container_p)); } /* ecma_op_container_size */ /** * The generic Map/WeakMap prototype object's 'get' routine * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_container_get (ecma_extended_object_t *map_object_p, /**< map object */ ecma_value_t key_arg, /**< key argument */ lit_magic_string_id_t lit_id) /**< internal class id */ { if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL && !ecma_is_value_object (key_arg)) { return ECMA_VALUE_UNDEFINED; } ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); if (ECMA_CONTAINER_GET_SIZE (container_p) == 0) { return ECMA_VALUE_UNDEFINED; } ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); if (entry_p == NULL) { return ECMA_VALUE_UNDEFINED; } return ecma_copy_value (((ecma_container_pair_t *) entry_p)->value); } /* ecma_op_container_get */ /** * The generic Map/Set prototype object's 'has' routine * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_container_has (ecma_extended_object_t *map_object_p, /**< map object */ ecma_value_t key_arg, /**< key argument */ lit_magic_string_id_t lit_id) /**< internal class id */ { ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); if ((map_object_p->u.cls.u1.container_flags & ECMA_CONTAINER_FLAGS_WEAK) != 0 && !ecma_is_value_object (key_arg)) { return ECMA_VALUE_FALSE; } if (ECMA_CONTAINER_GET_SIZE (container_p) == 0) { return ECMA_VALUE_FALSE; } ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); return ecma_make_boolean_value (entry_p != NULL); } /* ecma_op_container_has */ /** * Helper method for the Map.prototype.set and Set.prototype.add methods to swap the sign of the given value if needed * * See also: * ECMA-262 v6, 23.2.3.1 step 6 * ECMA-262 v6, 23.1.3.9 step 6 * * @return ecma value */ static ecma_value_t ecma_op_container_set_normalize_zero (ecma_value_t this_arg) /*< this arg */ { if (ecma_is_value_number (this_arg)) { ecma_number_t number_value = ecma_get_number_from_value (this_arg); if (JERRY_UNLIKELY (ecma_number_is_zero (number_value) && ecma_number_is_negative (number_value))) { return ecma_make_integer_value (0); } } return this_arg; } /* ecma_op_container_set_normalize_zero */ /** * The generic Map prototype object's 'set' and Set prototype object's 'add' routine * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_container_set (ecma_extended_object_t *map_object_p, /**< map object */ ecma_value_t key_arg, /**< key argument */ ecma_value_t value_arg, /**< value argument */ lit_magic_string_id_t lit_id) /**< internal class id */ { ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); if ((map_object_p->u.cls.u1.container_flags & ECMA_CONTAINER_FLAGS_WEAK) != 0 && !ecma_is_value_object (key_arg)) { return ecma_raise_type_error (ECMA_ERR_KEY_MUST_BE_AN_OBJECT); } ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); if (entry_p == NULL) { ecma_op_internal_buffer_append (container_p, ecma_op_container_set_normalize_zero (key_arg), value_arg, lit_id); if ((map_object_p->u.cls.u1.container_flags & ECMA_CONTAINER_FLAGS_WEAK) != 0) { ecma_object_t *key_p = ecma_get_object_from_value (key_arg); ecma_op_object_set_weak (key_p, (ecma_object_t *) map_object_p); } } else { ecma_op_internal_buffer_update (entry_p, ecma_op_container_set_normalize_zero (value_arg), lit_id); } ecma_ref_object ((ecma_object_t *) map_object_p); return ecma_make_object_value ((ecma_object_t *) map_object_p); } /* ecma_op_container_set */ /** * The generic Map/Set prototype object's 'forEach' routine * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_container_foreach (ecma_extended_object_t *map_object_p, /**< map object */ ecma_value_t predicate, /**< callback function */ ecma_value_t predicate_this_arg, /**< this argument for * invoke predicate */ lit_magic_string_id_t lit_id) /**< internal class id */ { if (!ecma_op_is_callable (predicate)) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } JERRY_ASSERT (ecma_is_value_object (predicate)); ecma_object_t *func_object_p = ecma_get_object_from_value (predicate); ecma_value_t ret_value = ECMA_VALUE_UNDEFINED; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); uint8_t entry_size = ecma_op_container_entry_size (lit_id); for (uint32_t i = 0; i < ECMA_CONTAINER_ENTRY_COUNT (container_p); i += entry_size) { ecma_value_t *entry_p = ECMA_CONTAINER_START (container_p) + i; if (ecma_is_value_empty (*entry_p)) { continue; } ecma_value_t key_arg = *entry_p; ecma_value_t value_arg = ecma_op_container_get_value (entry_p, lit_id); ecma_value_t this_arg = ecma_make_object_value ((ecma_object_t *) map_object_p); ecma_value_t call_args[] = { value_arg, key_arg, this_arg }; ecma_value_t call_value = ecma_op_function_call (func_object_p, predicate_this_arg, call_args, 3); if (ECMA_IS_VALUE_ERROR (call_value)) { ret_value = call_value; break; } ecma_free_value (call_value); } return ret_value; } /* ecma_op_container_foreach */ /** * The Map/Set prototype object's 'clear' routine * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_container_clear (ecma_extended_object_t *map_object_p) /**< this argument */ { ecma_op_container_free_entries ((ecma_object_t *) map_object_p); return ECMA_VALUE_UNDEFINED; } /* ecma_op_container_clear */ /** * The generic Map/Set prototype object's 'delete' routine * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_container_delete (ecma_extended_object_t *map_object_p, /**< map object */ ecma_value_t key_arg, /**< key argument */ lit_magic_string_id_t lit_id) /**< internal class id */ { ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); if (entry_p == NULL) { return ECMA_VALUE_FALSE; } ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, lit_id); return ECMA_VALUE_TRUE; } /* ecma_op_container_delete */ /** * The generic WeakMap/WeakSet prototype object's 'delete' routine * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_op_container_delete_weak (ecma_extended_object_t *map_object_p, /**< map object */ ecma_value_t key_arg, /**< key argument */ lit_magic_string_id_t lit_id) /**< internal class id */ { if (!ecma_is_value_object (key_arg)) { return ECMA_VALUE_FALSE; } ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); if (entry_p == NULL) { return ECMA_VALUE_FALSE; } ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, lit_id); ecma_object_t *key_object_p = ecma_get_object_from_value (key_arg); ecma_op_object_unref_weak (key_object_p, ecma_make_object_value ((ecma_object_t *) map_object_p)); return ECMA_VALUE_TRUE; } /* ecma_op_container_delete_weak */ /** * Helper function to get the value from a weak container object * * @return value property */ ecma_value_t ecma_op_container_find_weak_value (ecma_object_t *object_p, /**< internal container object */ ecma_value_t key_arg) /**< key */ { ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; JERRY_ASSERT (map_object_p->u.cls.type == ECMA_OBJECT_CLASS_CONTAINER && map_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_WEAKMAP_UL); ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, (lit_magic_string_id_t) map_object_p->u.cls.u2.container_id); JERRY_ASSERT (entry_p != NULL); return entry_p[1]; } /* ecma_op_container_find_weak_value */ /** * Helper function to remove a key/value pair from a weak container object */ void ecma_op_container_remove_weak_entry (ecma_object_t *object_p, /**< internal container object */ ecma_value_t key_arg) /**< key */ { ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; JERRY_ASSERT (map_object_p->u.cls.type == ECMA_OBJECT_CLASS_CONTAINER && (map_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_WEAKSET_UL || map_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_WEAKMAP_UL)); ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, (lit_magic_string_id_t) map_object_p->u.cls.u2.container_id); JERRY_ASSERT (entry_p != NULL); ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, (lit_magic_string_id_t) map_object_p->u.cls.u2.container_id); } /* ecma_op_container_remove_weak_entry */ /** * The Create{Set, Map}Iterator Abstract operation * * See also: * ECMA-262 v6, 23.1.5.1 * ECMA-262 v6, 23.2.5.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return Map/Set iterator object, if success * error - otherwise */ ecma_value_t ecma_op_container_create_iterator (ecma_value_t this_arg, /**< this argument */ ecma_builtin_id_t proto_id, /**< prototype builtin id */ ecma_object_class_type_t iterator_type, /**< iterator type */ ecma_iterator_kind_t kind) /**< iterator kind */ { return ecma_op_create_iterator_object (this_arg, ecma_builtin_get (proto_id), iterator_type, kind); } /* ecma_op_container_create_iterator */ /** * Get the index of the iterator object. * * @return index of the iterator. */ static uint32_t ecma_op_iterator_get_index (ecma_object_t *iter_obj_p) /**< iterator object pointer */ { uint32_t index = ((ecma_extended_object_t *) iter_obj_p)->u.cls.u2.iterator_index; if (JERRY_UNLIKELY (index == ECMA_ITERATOR_INDEX_LIMIT)) { ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX); ecma_property_t *property_p = ecma_find_named_property (iter_obj_p, prop_name_p); ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p); return (uint32_t) (ecma_get_number_from_value (value_p->value)); } return index; } /* ecma_op_iterator_get_index */ /** * Set the index of the iterator object. */ static void ecma_op_iterator_set_index (ecma_object_t *iter_obj_p, /**< iterator object pointer */ uint32_t index) /* iterator index to set */ { if (JERRY_UNLIKELY (index >= ECMA_ITERATOR_INDEX_LIMIT)) { /* After the ECMA_ITERATOR_INDEX_LIMIT limit is reached the [[%Iterator%NextIndex]] property is stored as an internal property */ ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX); ecma_property_t *property_p = ecma_find_named_property (iter_obj_p, prop_name_p); ecma_property_value_t *value_p; if (property_p == NULL) { value_p = ecma_create_named_data_property (iter_obj_p, prop_name_p, ECMA_PROPERTY_FLAG_WRITABLE, &property_p); value_p->value = ecma_make_uint32_value (index); } else { value_p = ECMA_PROPERTY_VALUE_PTR (property_p); value_p->value = ecma_make_uint32_value (index); } } else { ((ecma_extended_object_t *) iter_obj_p)->u.cls.u2.iterator_index = (uint16_t) index; } } /* ecma_op_iterator_set_index */ /** * The %{Set, Map}IteratorPrototype% object's 'next' routine * * See also: * ECMA-262 v6, 23.1.5.2.1 * ECMA-262 v6, 23.2.5.2.1 * * Note: * Returned value must be freed with ecma_free_value. * * @return iterator result object, if success * error - otherwise */ ecma_value_t ecma_op_container_iterator_next (ecma_value_t this_val, /**< this argument */ ecma_object_class_type_t iterator_type) /**< type of the iterator */ { if (!ecma_is_value_object (this_val)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (this_val); ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; if (!ecma_object_class_is (obj_p, iterator_type)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_ITERATOR); } ecma_value_t iterated_value = ext_obj_p->u.cls.u3.iterated_value; if (ecma_is_value_empty (iterated_value)) { return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) (ecma_get_object_from_value (iterated_value)); lit_magic_string_id_t lit_id = (lit_magic_string_id_t) map_object_p->u.cls.u2.container_id; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); uint32_t index = ecma_op_iterator_get_index (obj_p); if (index == entry_count) { ext_obj_p->u.cls.u3.iterated_value = ECMA_VALUE_EMPTY; return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } uint8_t entry_size = ecma_op_container_entry_size (lit_id); uint8_t iterator_kind = ext_obj_p->u.cls.u1.iterator_kind; ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); ecma_value_t ret_value = ECMA_VALUE_UNDEFINED; for (uint32_t i = index; i < entry_count; i += entry_size) { ecma_value_t *entry_p = start_p + i; if (ecma_is_value_empty (*entry_p)) { if (i == (entry_count - entry_size)) { ret_value = ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); break; } continue; } ecma_op_iterator_set_index (obj_p, i + entry_size); ecma_value_t key_arg = *entry_p; ecma_value_t value_arg = ecma_op_container_get_value (entry_p, lit_id); if (iterator_kind == ECMA_ITERATOR_KEYS) { ret_value = ecma_create_iter_result_object (key_arg, ECMA_VALUE_FALSE); } else if (iterator_kind == ECMA_ITERATOR_VALUES) { ret_value = ecma_create_iter_result_object (value_arg, ECMA_VALUE_FALSE); } else { JERRY_ASSERT (iterator_kind == ECMA_ITERATOR_ENTRIES); ecma_value_t entry_array_value; entry_array_value = ecma_create_array_from_iter_element (value_arg, key_arg); ret_value = ecma_create_iter_result_object (entry_array_value, ECMA_VALUE_FALSE); ecma_free_value (entry_array_value); } break; } return ret_value; } /* ecma_op_container_iterator_next */ /** * Dispatcher of builtin container routines. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_container_dispatch_routine (uint16_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ lit_magic_string_id_t lit_id) /**< internal class id */ { ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id); if (map_object_p == NULL) { return ECMA_VALUE_ERROR; } switch (builtin_routine_id) { case ECMA_CONTAINER_ROUTINE_DELETE: { return ecma_op_container_delete (map_object_p, arguments_list_p[0], lit_id); } case ECMA_CONTAINER_ROUTINE_DELETE_WEAK: { return ecma_op_container_delete_weak (map_object_p, arguments_list_p[0], lit_id); } case ECMA_CONTAINER_ROUTINE_GET: { return ecma_op_container_get (map_object_p, arguments_list_p[0], lit_id); } case ECMA_CONTAINER_ROUTINE_SET: { return ecma_op_container_set (map_object_p, arguments_list_p[0], arguments_list_p[1], lit_id); } case ECMA_CONTAINER_ROUTINE_HAS: { return ecma_op_container_has (map_object_p, arguments_list_p[0], lit_id); } case ECMA_CONTAINER_ROUTINE_FOREACH: { return ecma_op_container_foreach (map_object_p, arguments_list_p[0], arguments_list_p[1], lit_id); } case ECMA_CONTAINER_ROUTINE_SIZE_GETTER: { return ecma_op_container_size (map_object_p); } case ECMA_CONTAINER_ROUTINE_ADD: { return ecma_op_container_set (map_object_p, arguments_list_p[0], arguments_list_p[0], lit_id); } case ECMA_CONTAINER_ROUTINE_CLEAR: { return ecma_op_container_clear (map_object_p); } case ECMA_CONTAINER_ROUTINE_KEYS: case ECMA_CONTAINER_ROUTINE_VALUES: case ECMA_CONTAINER_ROUTINE_ENTRIES: { ecma_builtin_id_t builtin_iterator_prototype = ECMA_BUILTIN_ID_MAP_ITERATOR_PROTOTYPE; ecma_object_class_type_t iterator_type = ECMA_OBJECT_CLASS_MAP_ITERATOR; if (lit_id != LIT_MAGIC_STRING_MAP_UL) { builtin_iterator_prototype = ECMA_BUILTIN_ID_SET_ITERATOR_PROTOTYPE; iterator_type = ECMA_OBJECT_CLASS_SET_ITERATOR; } ecma_iterator_kind_t kind = (ecma_iterator_kind_t) (builtin_routine_id - ECMA_CONTAINER_ROUTINE_KEYS); return ecma_op_container_create_iterator (this_arg, builtin_iterator_prototype, iterator_type, kind); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_container_dispatch_routine */ /** * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-number-prototype.inc.h000664 001750 001750 00000003235 15164251010 052147 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Number.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_NUMBER /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.7.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_NUMBER, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_NUMBER_PROTOTYPE_TO_STRING, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_VALUE_OF_UL, ECMA_NUMBER_PROTOTYPE_VALUE_OF, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, ECMA_NUMBER_PROTOTYPE_TO_LOCALE_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_TO_FIXED_UL, ECMA_NUMBER_PROTOTYPE_TO_FIXED, 1, 1) ROUTINE (LIT_MAGIC_STRING_TO_EXPONENTIAL_UL, ECMA_NUMBER_PROTOTYPE_TO_EXPONENTIAL, 1, 1) ROUTINE (LIT_MAGIC_STRING_TO_PRECISION_UL, ECMA_NUMBER_PROTOTYPE_TO_PRECISION, 1, 1) #endif /* JERRY_BUILTIN_NUMBER */ #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgRenderer.h000664 001750 001750 00000010566 15164251010 036443 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_RENDERER_H_ #define _TVG_WG_RENDERER_H_ #include "tvgWgRenderTask.h" struct WgRenderer : RenderMethod { //main features bool preUpdate() override; RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; bool postUpdate() override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; bool postRender() override; void dispose(RenderData data) override; RenderRegion region(RenderData data) override; bool bounds(RenderData data, Point* pt4, const Matrix& m) override; bool blend(BlendMethod method) override; ColorSpace colorSpace() override; const RenderSurface* mainSurface() override; bool clear() override; bool sync() override; bool intersectsImage(RenderData data, const RenderRegion& region) override; bool intersectsShape(RenderData data, const RenderRegion& region) override; bool target(WGPUDevice device, WGPUInstance instance, void* target, uint32_t w, uint32_t h, ColorSpace cs, int type = 0); //composition RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override; bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override; bool endComposite(RenderCompositor* cmp) override; //post effects void prepare(RenderEffect* effect, const Matrix& transform) override; bool region(RenderEffect* effect) override; bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override; void dispose(RenderEffect* effect) override; //partial rendering void damage(RenderData rd, const RenderRegion& region) override; bool partial(bool disable) override; static WgRenderer* gen(uint32_t threads); static bool term(); private: WgRenderer(); ~WgRenderer(); void release(); void disposeObjects(); void releaseSurfaceTexture(); void clearTargets(); bool surfaceConfigure(WGPUSurface surface, WgContext& context, uint32_t width, uint32_t height); // render tree stacks WgRenderTarget mRenderTargetRoot; Array mCompositorList; Array mRenderTargetStack; Array mSceneTaskStack; Array mRenderTaskList; // render target pool WgRenderTargetPool mRenderTargetPool; // render data paint pools WgRenderDataShapePool mRenderDataShapePool; WgRenderDataPicturePool mRenderDataPicturePool; WgRenderDataEffectParamsPool mRenderDataEffectParamsPool; // rendering context WgContext mContext; WgCompositor mCompositor; // rendering states RenderSurface mTargetSurface; BlendMethod mBlendMethod{}; // disposable data list Array mDisposeRenderDatas{}; Key mDisposeKey{}; // gpu handles WGPUTexture targetTexture{}; // external handle WGPUSurfaceTexture surfaceTexture{}; WGPUSurface surface{}; // external handle }; #endif /* _TVG_WG_RENDERER_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlRenderer.h000664 001750 001750 00000021063 15164251010 036407 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_RENDERER_H_ #define _TVG_GL_RENDERER_H_ #include "tvgArray.h" #include "tvgGlRenderTarget.h" #include "tvgGlRenderTask.h" #include "tvgGlGpuBuffer.h" #include "tvgGlRenderPass.h" #include "tvgGlEffect.h" struct GlRenderer : RenderMethod { enum RenderTypes { RT_Color = 0, RT_LinGradient, RT_RadGradient, RT_Image, RT_MaskAlpha, RT_MaskAlphaInv, RT_MaskLuma, RT_MaskLumaInv, RT_MaskAdd, RT_MaskSub, RT_MaskIntersect, RT_MaskDifference, RT_MaskLighten, RT_MaskDarken, RT_Stencil, RT_Blit, // blends (image) RT_Blend_Image_Normal, RT_Blend_Image_Multiply, RT_Blend_Image_Screen, RT_Blend_Image_Overlay, RT_Blend_Image_Darken, RT_Blend_Image_Lighten, RT_Blend_Image_ColorDodge, RT_Blend_Image_ColorBurn, RT_Blend_Image_HardLight, RT_Blend_Image_SoftLight, RT_Blend_Image_Difference, RT_Blend_Image_Exclusion, RT_Blend_Image_Hue, RT_Blend_Image_Saturation, RT_Blend_Image_Color, RT_Blend_Image_Luminosity, RT_Blend_Image_Add, // blends (scene) RT_Blend_Scene_Normal, RT_Blend_Scene_Multiply, RT_Blend_Scene_Screen, RT_Blend_Scene_Overlay, RT_Blend_Scene_Darken, RT_Blend_Scene_Lighten, RT_Blend_Scene_ColorDodge, RT_Blend_Scene_ColorBurn, RT_Blend_Scene_HardLight, RT_Blend_Scene_SoftLight, RT_Blend_Scene_Difference, RT_Blend_Scene_Exclusion, RT_Blend_Scene_Hue, RT_Blend_Scene_Saturation, RT_Blend_Scene_Color, RT_Blend_Scene_Luminosity, RT_Blend_Scene_Add, // shape blends (solid) RT_ShapeBlend_Solid_Normal, RT_ShapeBlend_Solid_Multiply, RT_ShapeBlend_Solid_Screen, RT_ShapeBlend_Solid_Overlay, RT_ShapeBlend_Solid_Darken, RT_ShapeBlend_Solid_Lighten, RT_ShapeBlend_Solid_ColorDodge, RT_ShapeBlend_Solid_ColorBurn, RT_ShapeBlend_Solid_HardLight, RT_ShapeBlend_Solid_SoftLight, RT_ShapeBlend_Solid_Difference, RT_ShapeBlend_Solid_Exclusion, RT_ShapeBlend_Solid_Hue, RT_ShapeBlend_Solid_Saturation, RT_ShapeBlend_Solid_Color, RT_ShapeBlend_Solid_Luminosity, RT_ShapeBlend_Solid_Add, // shape blends (linear gradient) RT_ShapeBlend_Linear_Normal, RT_ShapeBlend_Linear_Multiply, RT_ShapeBlend_Linear_Screen, RT_ShapeBlend_Linear_Overlay, RT_ShapeBlend_Linear_Darken, RT_ShapeBlend_Linear_Lighten, RT_ShapeBlend_Linear_ColorDodge, RT_ShapeBlend_Linear_ColorBurn, RT_ShapeBlend_Linear_HardLight, RT_ShapeBlend_Linear_SoftLight, RT_ShapeBlend_Linear_Difference, RT_ShapeBlend_Linear_Exclusion, RT_ShapeBlend_Linear_Hue, RT_ShapeBlend_Linear_Saturation, RT_ShapeBlend_Linear_Color, RT_ShapeBlend_Linear_Luminosity, RT_ShapeBlend_Linear_Add, // shape blends (radial gradient) RT_ShapeBlend_Radial_Normal, RT_ShapeBlend_Radial_Multiply, RT_ShapeBlend_Radial_Screen, RT_ShapeBlend_Radial_Overlay, RT_ShapeBlend_Radial_Darken, RT_ShapeBlend_Radial_Lighten, RT_ShapeBlend_Radial_ColorDodge, RT_ShapeBlend_Radial_ColorBurn, RT_ShapeBlend_Radial_HardLight, RT_ShapeBlend_Radial_SoftLight, RT_ShapeBlend_Radial_Difference, RT_ShapeBlend_Radial_Exclusion, RT_ShapeBlend_Radial_Hue, RT_ShapeBlend_Radial_Saturation, RT_ShapeBlend_Radial_Color, RT_ShapeBlend_Radial_Luminosity, RT_ShapeBlend_Radial_Add, RT_None }; //main features bool preUpdate() override; RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; bool postUpdate() override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; bool postRender() override; void dispose(RenderData data) override;; RenderRegion region(RenderData data) override; bool bounds(RenderData data, Point* pt4, const Matrix& m) override; bool blend(BlendMethod method) override; ColorSpace colorSpace() override; const RenderSurface* mainSurface() override; bool sync() override; bool clear() override; bool intersectsShape(RenderData data, const RenderRegion& region) override; bool intersectsImage(RenderData data, const RenderRegion& region) override; bool target(void* display, void* surface, void* context, int32_t id, uint32_t w, uint32_t h, ColorSpace cs); //composition RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override; bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override; bool endComposite(RenderCompositor* cmp) override; //post effects void prepare(RenderEffect* effect, const Matrix& transform) override; bool region(RenderEffect* effect) override; bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override; void dispose(RenderEffect* effect) override; //partial rendering void damage(RenderData rd, const RenderRegion& region) override; bool partial(bool disable) override; static GlRenderer* gen(uint32_t threads); static bool term(); private: enum class BlendSource { Image, Scene, Solid, LinearGradient, RadialGradient }; GlRenderer(); ~GlRenderer(); void initShaders(); void drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdateFlag flag, int32_t depth); void drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFlag flag, int32_t depth); void drawClip(Array& clips); GlRenderPass* currentPass(); bool beginComplexBlending(const RenderRegion& vp, RenderRegion bounds); void endBlendingCompose(GlRenderTask* stencilTask); GlProgram* getBlendProgram(BlendMethod method, BlendSource source); void prepareBlitTask(GlBlitTask* task); void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight); void endRenderPass(RenderCompositor* cmp); void flush(); void clearDisposes(); bool currentContext(); void* mDisplay = nullptr; // EGLDisplay for EGL; unused for other app-managed contexts. void* mSurface = nullptr; // EGLSurface for EGL, HDC for WGL; unused for other app-managed contexts. void* mContext = nullptr; RenderSurface surface; GLint mTargetFboId = 0; GlStageBuffer mGpuBuffer; GlRenderTarget mRootTarget; GlEffect mEffect; Array mPrograms; Array mComposePool; Array mBlendPool; Array mRenderPassStack; Array mComposeStack; //Disposed resources. They should be released on synced call. struct { Array textures; Key key; } mDisposed; BlendMethod mBlendMethod = BlendMethod::Normal; bool mClearBuffer = false; }; #endif /* _TVG_GL_RENDERER_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/quant_levels_dec.cpp000664 001750 001750 00000021024 15164251010 037473 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2013 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Implement gradient smoothing: we replace a current alpha value by its // surrounding average if it's close enough (that is: the change will be less // than the minimum distance between two quantized level). // We use sliding window for computing the 2d moving average. // // Author: Skal (pascal.massimino@gmail.com) #include "tvgCommon.h" #include "./quant_levels_dec.h" #include "./utils.h" // #define USE_DITHERING // uncomment to enable ordered dithering (not vital) #define FIX 16 // fix-point precision for averaging #define LFIX 2 // extra precision for look-up table #define LUT_SIZE ((1 << (8 + LFIX)) - 1) // look-up table size #if defined(USE_DITHERING) #define DFIX 4 // extra precision for ordered dithering #define DSIZE 4 // dithering size (must be a power of two) // cf. http://en.wikipedia.org/wiki/Ordered_dithering static const uint8_t kOrderedDither[DSIZE][DSIZE] = { { 0, 8, 2, 10 }, // coefficients are in DFIX fixed-point precision { 12, 4, 14, 6 }, { 3, 11, 1, 9 }, { 15, 7, 13, 5 } }; #else #define DFIX 0 #endif typedef struct { int width_, height_; // dimension int row_; // current input row being processed uint8_t* src_; // input pointer uint8_t* dst_; // output pointer int radius_; // filter radius (=delay) int scale_; // normalization factor, in FIX bits precision void* mem_; // all memory // various scratch buffers uint16_t* start_; uint16_t* cur_; uint16_t* end_; uint16_t* top_; uint16_t* average_; // input levels distribution int num_levels_; // number of quantized levels int min_, max_; // min and max level values int min_level_dist_; // smallest distance between two consecutive levels int16_t* correction_; // size = 1 + 2*LUT_SIZE -> ~4k memory } SmoothParams; //------------------------------------------------------------------------------ static WEBP_INLINE uint8_t clip_8b(int v) { return (!(v & (int)(~0U << (8 + DFIX)))) ? (uint8_t)(v >> DFIX) : (v < 0) ? 0u : 255u; } // vertical accumulation static void VFilter(SmoothParams* const p) { const uint8_t* src = p->src_; const int w = p->width_; uint16_t* const cur = p->cur_; const uint16_t* const top = p->top_; uint16_t* const out = p->end_; uint16_t sum = 0; // all arithmetic is modulo 16bit int x; for (x = 0; x < w; ++x) { uint16_t new_value; sum += src[x]; new_value = top[x] + sum; out[x] = new_value - cur[x]; // vertical sum of 'r' pixels. cur[x] = new_value; } // move input pointers one row down p->top_ = p->cur_; p->cur_ += w; if (p->cur_ == p->end_) p->cur_ = p->start_; // roll-over // We replicate edges, as it's somewhat easier as a boundary condition. // That's why we don't update the 'src' pointer on top/bottom area: if (p->row_ >= 0 && p->row_ < p->height_ - 1) { p->src_ += p->width_; } } // horizontal accumulation. We use mirror replication of missing pixels, as it's // a little easier to implement (surprisingly). static void HFilter(SmoothParams* const p) { const uint16_t* const in = p->end_; uint16_t* const out = p->average_; const uint32_t scale = p->scale_; const int w = p->width_; const int r = p->radius_; int x; for (x = 0; x <= r; ++x) { // left mirroring const uint16_t delta = in[x + r - 1] + in[r - x]; out[x] = (delta * scale) >> FIX; } for (; x < w - r; ++x) { // bulk middle run const uint16_t delta = in[x + r] - in[x - r - 1]; out[x] = (delta * scale) >> FIX; } for (; x < w; ++x) { // right mirroring const uint16_t delta = 2 * in[w - 1] - in[2 * w - 2 - r - x] - in[x - r - 1]; out[x] = (delta * scale) >> FIX; } } // emit one filtered output row static void ApplyFilter(SmoothParams* const p) { const uint16_t* const average = p->average_; const int w = p->width_; const int16_t* const correction = p->correction_; #if defined(USE_DITHERING) const uint8_t* const dither = kOrderedDither[p->row_ % DSIZE]; #endif uint8_t* const dst = p->dst_; int x; for (x = 0; x < w; ++x) { const int v = dst[x]; if (v < p->max_ && v > p->min_) { const int c = (v << DFIX) + correction[average[x] - (v << LFIX)]; #if defined(USE_DITHERING) dst[x] = clip_8b(c + dither[x % DSIZE]); #else dst[x] = clip_8b(c); #endif } } p->dst_ += w; // advance output pointer } //------------------------------------------------------------------------------ // Initialize correction table static void InitCorrectionLUT(int16_t* const lut, int min_dist) { // The correction curve is: // f(x) = x for x <= threshold2 // f(x) = 0 for x >= threshold1 // and a linear interpolation for range x=[threshold2, threshold1] // (along with f(-x) = -f(x) symmetry). // Note that: threshold2 = 3/4 * threshold1 const int threshold1 = min_dist << LFIX; const int threshold2 = (3 * threshold1) >> 2; const int max_threshold = threshold2 << DFIX; const int delta = threshold1 - threshold2; int i; for (i = 1; i <= LUT_SIZE; ++i) { int c = (i <= threshold2) ? (i << DFIX) : (i < threshold1) ? max_threshold * (threshold1 - i) / delta : 0; c >>= LFIX; lut[+i] = +c; lut[-i] = -c; } lut[0] = 0; } static void CountLevels(const uint8_t* const data, int size, SmoothParams* const p) { int i, last_level; uint8_t used_levels[256] = { 0 }; p->min_ = 255; p->max_ = 0; for (i = 0; i < size; ++i) { const int v = data[i]; if (v < p->min_) p->min_ = v; if (v > p->max_) p->max_ = v; used_levels[v] = 1; } // Compute the minimum distance between two non-zero levels. p->min_level_dist_ = p->max_ - p->min_; last_level = -1; for (i = 0; i < 256; ++i) { if (used_levels[i]) { ++p->num_levels_; if (last_level >= 0) { const int level_dist = i - last_level; if (level_dist < p->min_level_dist_) { p->min_level_dist_ = level_dist; } } last_level = i; } } } // Initialize all params. static int InitParams(uint8_t* const data, int width, int height, int radius, SmoothParams* const p) { const int R = 2 * radius + 1; // total size of the kernel const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start_); const size_t size_m = width * sizeof(*p->average_); const size_t size_lut = (1 + 2 * LUT_SIZE) * sizeof(*p->correction_); const size_t total_size = size_scratch_m + size_m + size_lut; uint8_t* mem = tvg::malloc(1U * total_size); if (mem == NULL) return 0; p->mem_ = (void*)mem; p->start_ = (uint16_t*)mem; p->cur_ = p->start_; p->end_ = p->start_ + R * width; p->top_ = p->end_ - width; memset(p->top_, 0, width * sizeof(*p->top_)); mem += size_scratch_m; p->average_ = (uint16_t*)mem; mem += size_m; p->width_ = width; p->height_ = height; p->src_ = data; p->dst_ = data; p->radius_ = radius; p->scale_ = (1 << (FIX + LFIX)) / (R * R); // normalization constant p->row_ = -radius; // analyze the input distribution so we can best-fit the threshold CountLevels(data, width * height, p); // correction table p->correction_ = ((int16_t*)mem) + LUT_SIZE; InitCorrectionLUT(p->correction_, p->min_level_dist_); return 1; } static void CleanupParams(SmoothParams* const p) { tvg::free(p->mem_); } int WebPDequantizeLevels(uint8_t* const data, int width, int height, int strength) { const int radius = 4 * strength / 100; if (strength < 0 || strength > 100) return 0; if (data == NULL || width <= 0 || height <= 0) return 0; // bad params if (radius > 0) { SmoothParams p; memset(&p, 0, sizeof(p)); if (!InitParams(data, width, height, radius, &p)) return 0; if (p.num_levels_ > 2) { for (; p.row_ < p.height_; ++p.row_) { VFilter(&p); // accumulate average of input // Need to wait few rows in order to prime the filter, // before emitting some output. if (p.row_ >= p.radius_) { HFilter(&p); ApplyFilter(&p); } } } CleanupParams(&p); } return 1; } lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int8array-prototype.cpp000664 001750 001750 00000002216 15164251010 054545 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-int8array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID int8array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup int8arrayprototype ECMA Int8Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlEffect.cpp000664 001750 001750 00000036265 15164251010 036402 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgGlRenderTask.h" #include "tvgGlGpuBuffer.h" #include "tvgGlRenderPass.h" #include "tvgGlShaderSrc.h" #include "tvgGlEffect.h" /************************************************************************/ /* Gaussian Blur */ /************************************************************************/ struct GlGaussianBlur { float sigma{}; float scale{}; float extend{}; float dummy0{}; }; bool GlEffect::region(RenderEffectGaussianBlur* effect) { auto gaussianBlur = (GlGaussianBlur*)effect->rd; if (effect->direction != 2) { effect->extend.min.x = -gaussianBlur->extend; effect->extend.max.x = +gaussianBlur->extend; } if (effect->direction != 1) { effect->extend.min.y = -gaussianBlur->extend; effect->extend.max.y = +gaussianBlur->extend; } return true; }; void GlEffect::update(RenderEffectGaussianBlur* effect, const Matrix& transform) { GlGaussianBlur* blur = (GlGaussianBlur*)effect->rd; if (!blur) blur = tvg::malloc(sizeof(GlGaussianBlur)); blur->sigma = effect->sigma; blur->scale = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); blur->extend = 2 * blur->sigma * blur->scale; effect->rd = blur; effect->valid = (blur->extend > 0); } GlRenderTask* GlEffect::render(RenderEffectGaussianBlur* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset) { if (!pBlurV) pBlurV = new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL); if (!pBlurH) pBlurH = new GlProgram(EFFECT_VERTEX, GAUSSIAN_HORIZONTAL); // get current and intermediate framebuffers auto dstCopyFbo0 = blendPool[0]->getRenderTarget(vp); auto dstCopyFbo1 = blendPool[1]->getRenderTarget(vp); // add uniform data float viewport[4] {(float)vp.min.x, (float)vp.min.y, (float)vp.max.x, (float)vp.max.y}; auto blurOffset = gpuBuffer->push((GlGaussianBlur*)(effect->rd), sizeof(GlGaussianBlur), true); auto viewportOffset = gpuBuffer->push(viewport, sizeof(viewport), true); // create gaussian blur tasks auto task = new GlGaussianBlurTask(dstFbo, dstCopyFbo0, dstCopyFbo1); task->effect = effect; task->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); // horizontal blur task and geometry task->horzTask = new GlRenderTask(pBlurH); task->horzTask->addBindResource(GlBindingResource{0, pBlurH->getUniformBlockIndex("Gaussian"), gpuBuffer->getBufferId(), blurOffset, sizeof(GlGaussianBlur)}); task->horzTask->addBindResource(GlBindingResource{1, pBlurH->getUniformBlockIndex("Viewport"), gpuBuffer->getBufferId(), viewportOffset, sizeof(viewport)}); task->horzTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); task->horzTask->setDrawRange(ioffset, 6); // vertical blur task and geometry task->vertTask = new GlRenderTask(pBlurV); task->vertTask->addBindResource(GlBindingResource{0, pBlurV->getUniformBlockIndex("Gaussian"), gpuBuffer->getBufferId(), blurOffset, sizeof(GlGaussianBlur)}); task->vertTask->addBindResource(GlBindingResource{1, pBlurV->getUniformBlockIndex("Viewport"), gpuBuffer->getBufferId(), viewportOffset, sizeof(viewport)}); task->vertTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); task->vertTask->setDrawRange(ioffset, 6); return task; } /************************************************************************/ /* DropShadow */ /************************************************************************/ struct GlDropShadow: GlGaussianBlur { float color[4]; float offset[2]; }; bool GlEffect::region(RenderEffectDropShadow* effect) { auto gaussianBlur = (GlDropShadow*)effect->rd; effect->extend.min.x = -gaussianBlur->extend; effect->extend.max.x = +gaussianBlur->extend; effect->extend.min.y = -gaussianBlur->extend; effect->extend.max.y = +gaussianBlur->extend; return true; }; void GlEffect::update(RenderEffectDropShadow* effect, const Matrix& transform) { GlDropShadow* dropShadow = (GlDropShadow*)effect->rd; if (!dropShadow) dropShadow = tvg::malloc(sizeof(GlDropShadow)); const auto scale = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); const auto radian = tvg::deg2rad(90.0f - effect->angle) - tvg::radian(transform); const Point offset = {-effect->distance * cosf(radian) * scale, -effect->distance * sinf(radian) * scale}; dropShadow->sigma = effect->sigma; dropShadow->scale = scale; dropShadow->color[3] = effect->color[3] / 255.0f; //Drop shadow effect applies blending in the shader (GL_BLEND disabled), so the color should be premultiplied: dropShadow->color[0] = effect->color[0] / 255.0f * dropShadow->color[3]; dropShadow->color[1] = effect->color[1] / 255.0f * dropShadow->color[3]; dropShadow->color[2] = effect->color[2] / 255.0f * dropShadow->color[3]; dropShadow->offset[0] = offset.x; dropShadow->offset[1] = offset.y; dropShadow->extend = 2 * std::max(effect->sigma * scale + std::abs(offset.x), effect->sigma * scale + std::abs(offset.y)); effect->rd = dropShadow; effect->valid = (dropShadow->extend >= 0); } GlRenderTask* GlEffect::render(RenderEffectDropShadow* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset) { if (!pBlurV) pBlurV = new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL); if (!pBlurH) pBlurH = new GlProgram(EFFECT_VERTEX, GAUSSIAN_HORIZONTAL); if (!pDropShadow) pDropShadow = new GlProgram(EFFECT_VERTEX, EFFECT_DROPSHADOW); // get current and intermediate framebuffers auto dstCopyFbo0 = blendPool[0]->getRenderTarget(vp); auto dstCopyFbo1 = blendPool[1]->getRenderTarget(vp); // add uniform data float viewport[4] {(float)vp.min.x, (float)vp.min.y, (float)vp.max.x, (float)vp.max.y}; GlDropShadow* params = (GlDropShadow*)(effect->rd); auto paramsOffset = gpuBuffer->push(params, sizeof(GlDropShadow), true); auto viewportOffset = gpuBuffer->push(viewport, sizeof(viewport), true); // create gaussian blur tasks auto task = new GlEffectDropShadowTask(pDropShadow, dstFbo, dstCopyFbo0, dstCopyFbo1); task->effect = (RenderEffectDropShadow*)effect; task->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); task->addBindResource(GlBindingResource{0, pDropShadow->getUniformBlockIndex("DropShadow"), gpuBuffer->getBufferId(), paramsOffset, sizeof(GlDropShadow)}); task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); task->setDrawRange(ioffset, 6); // horizontal blur task and geometry task->horzTask = new GlRenderTask(pBlurH); task->horzTask->addBindResource(GlBindingResource{0, pBlurH->getUniformBlockIndex("Gaussian"), gpuBuffer->getBufferId(), paramsOffset, sizeof(GlGaussianBlur)}); task->horzTask->addBindResource(GlBindingResource{1, pBlurH->getUniformBlockIndex("Viewport"), gpuBuffer->getBufferId(), viewportOffset, sizeof(viewport)}); task->horzTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); task->horzTask->setDrawRange(ioffset, 6); // vertical blur task and geometry task->vertTask = new GlRenderTask(pBlurV); task->vertTask->addBindResource(GlBindingResource{0, pBlurV->getUniformBlockIndex("Gaussian"), gpuBuffer->getBufferId(), paramsOffset, sizeof(GlGaussianBlur)}); task->vertTask->addBindResource(GlBindingResource{1, pBlurV->getUniformBlockIndex("Viewport"), gpuBuffer->getBufferId(), viewportOffset, sizeof(viewport)}); task->vertTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); task->vertTask->setDrawRange(ioffset, 6); return task; } /************************************************************************/ /* ColorReplacement */ /************************************************************************/ struct GlEffectParams { // fill: [0..3]: color // tint: [0..2]: black, [4..6]: white, [8]: intensity // tritone: [0..2]: shadow, [4..6]: midtone, [8..10]: highlight [11]: blender float params[4+4+4]; }; void GlEffect::update(RenderEffectFill* effect, const Matrix& transform) { auto params = (GlEffectParams*)effect->rd; if (!params) params = tvg::malloc(sizeof(GlEffectParams)); params->params[0] = effect->color[0] / 255.0f; params->params[1] = effect->color[1] / 255.0f; params->params[2] = effect->color[2] / 255.0f; params->params[3] = effect->color[3] / 255.0f; effect->rd = params; effect->valid = true; } void GlEffect::update(RenderEffectTint* effect, const Matrix& transform) { effect->valid = (effect->intensity > 0); if (!effect->valid) return; auto params = (GlEffectParams*)effect->rd; if (!params) params = tvg::malloc(sizeof(GlEffectParams)); params->params[0] = effect->black[0] / 255.0f; params->params[1] = effect->black[1] / 255.0f; params->params[2] = effect->black[2] / 255.0f; params->params[3] = 0.0f; params->params[4] = effect->white[0] / 255.0f; params->params[5] = effect->white[1] / 255.0f; params->params[6] = effect->white[2] / 255.0f; params->params[7] = 0.0f; params->params[8] = effect->intensity / 255.0f; effect->rd = params; } void GlEffect::update(RenderEffectTritone* effect, const Matrix& transform) { effect->valid = (effect->blender < 255); if (!effect->valid) return; auto params = (GlEffectParams*)effect->rd; if (!params) params = tvg::malloc(sizeof(GlEffectParams)); params->params[0] = effect->shadow[0] / 255.0f; params->params[1] = effect->shadow[1] / 255.0f; params->params[2] = effect->shadow[2] / 255.0f; params->params[3] = 0.0f; params->params[4] = effect->midtone[0] / 255.0f; params->params[5] = effect->midtone[1] / 255.0f; params->params[6] = effect->midtone[2] / 255.0f; params->params[7] = 0.0f; params->params[8] = effect->highlight[0] / 255.0f; params->params[9] = effect->highlight[1] / 255.0f; params->params[10] = effect->highlight[2] / 255.0f; params->params[11] = effect->blender / 255.0f; effect->rd = params; } GlRenderTask* GlEffect::render(RenderEffect* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset) { //common color replacement effects GlProgram* program = nullptr; if (effect->type == SceneEffect::Fill) { if (!pFill) pFill = new GlProgram(EFFECT_VERTEX, EFFECT_FILL); program = pFill; } else if (effect->type == SceneEffect::Tint) { if (!pTint) pTint = new GlProgram(EFFECT_VERTEX, EFFECT_TINT); program = pTint; } else if (effect->type == SceneEffect::Tritone) { if (!pTritone) pTritone = new GlProgram(EFFECT_VERTEX, EFFECT_TRITONE); program = pTritone; } else return nullptr; // get current and intermediate framebuffers auto dstCopyFbo = blendPool[0]->getRenderTarget(vp); // add uniform data auto params = (GlEffectParams*)(effect->rd); auto paramsOffset = gpuBuffer->push(params, sizeof(GlEffectParams), true); // create and setup task auto task = new GlEffectColorTransformTask(program, dstFbo, dstCopyFbo); task->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); task->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("Params"), gpuBuffer->getBufferId(), paramsOffset, sizeof(GlEffectParams)}); task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); task->setDrawRange(ioffset, 6); return task; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ void GlEffect::update(RenderEffect* effect, const Matrix& transform) { switch (effect->type) { case SceneEffect::GaussianBlur: update(static_cast(effect), transform); break; case SceneEffect::DropShadow : update(static_cast(effect), transform); break; case SceneEffect::Fill: update(static_cast(effect), transform); break; case SceneEffect::Tint: update(static_cast(effect), transform); break; case SceneEffect::Tritone: update(static_cast(effect), transform); break; default: break; } } bool GlEffect::region(RenderEffect* effect) { switch (effect->type) { case SceneEffect::GaussianBlur: return region(static_cast(effect)); case SceneEffect::DropShadow : return region(static_cast(effect)); default: return false; } } bool GlEffect::render(RenderEffect* effect, GlRenderPass* pass, Array& blendPool) { if (pass->isEmpty()) return false; auto vp = pass->getViewport(); // add render geometry const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f}; const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 }; auto voffset = gpuBuffer->push((void*)vdata, sizeof(vdata)); auto ioffset = gpuBuffer->pushIndex((void*)idata, sizeof(idata)); GlRenderTask* output = nullptr; if (effect->type == SceneEffect::GaussianBlur) { output = render(static_cast(effect), pass->getFbo(), blendPool, vp, voffset, ioffset); } else if (effect->type == SceneEffect::DropShadow) { output = render(static_cast(effect), pass->getFbo(), blendPool, vp, voffset, ioffset); } else { output = render(effect, pass->getFbo(), blendPool, vp, voffset, ioffset); } if (!output) return false; pass->addRenderTask(output); return true; } GlEffect::GlEffect(GlStageBuffer* buffer) : gpuBuffer(buffer) { } GlEffect::~GlEffect() { delete(pBlurV); delete(pBlurH); delete(pDropShadow); delete(pFill); delete(pTint); delete(pTritone); }external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-jobqueue.cpp000664 001750 001750 00000045503 15164251010 045644 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-jobqueue.h" #include "ecma-async-generator-object.h" #include "ecma-function-object.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-promise-object.h" #include "jcontext.h" #include "opcodes.h" #include "vm-stack.h" /** * Mask for job queue type. */ #define ECMA_JOB_QUEUE_TYPE_MASK ((uintptr_t) 0x07) /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmajobqueue ECMA Job Queue related routines * @{ */ /** * Description of the PromiseReactionJob */ typedef struct { ecma_job_queue_item_t header; /**< job queue item header */ ecma_value_t capability; /**< capability object */ ecma_value_t handler; /**< handler function */ ecma_value_t argument; /**< argument for the reaction */ } ecma_job_promise_reaction_t; /** * Description of the PromiseAsyncReactionJob */ typedef struct { ecma_job_queue_item_t header; /**< job queue item header */ ecma_value_t executable_object; /**< executable object */ ecma_value_t argument; /**< argument for the reaction */ } ecma_job_promise_async_reaction_t; /** * Description of the PromiseAsyncGeneratorJob */ typedef struct { ecma_job_queue_item_t header; /**< job queue item header */ ecma_value_t executable_object; /**< executable object */ } ecma_job_promise_async_generator_t; /** * Description of the PromiseResolveThenableJob */ typedef struct { ecma_job_queue_item_t header; /**< job queue item header */ ecma_value_t promise; /**< promise to be resolved */ ecma_value_t thenable; /**< thenable object */ ecma_value_t then; /**< 'then' function */ } ecma_job_promise_resolve_thenable_t; /** * Initialize the jobqueue. */ void ecma_job_queue_init (void) { JERRY_CONTEXT (job_queue_head_p) = NULL; JERRY_CONTEXT (job_queue_tail_p) = NULL; } /* ecma_job_queue_init */ /** * Get the type of the job. * * @return type of the job */ static inline ecma_job_queue_item_type_t ecma_job_queue_get_type (ecma_job_queue_item_t *job_p) /**< the job */ { return (ecma_job_queue_item_type_t) (job_p->next_and_type & ECMA_JOB_QUEUE_TYPE_MASK); } /* ecma_job_queue_get_type */ /** * Get the next job of the job queue. * * @return next job */ static inline ecma_job_queue_item_t * ecma_job_queue_get_next (ecma_job_queue_item_t *job_p) /**< the job */ { return (ecma_job_queue_item_t *) (job_p->next_and_type & ~ECMA_JOB_QUEUE_TYPE_MASK); } /* ecma_job_queue_get_next */ /** * Free the heap and the member of the PromiseReactionJob. */ static void ecma_free_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< points to the PromiseReactionJob */ { JERRY_ASSERT (job_p != NULL); ecma_free_value (job_p->capability); ecma_free_value (job_p->handler); ecma_free_value (job_p->argument); jmem_heap_free_block (job_p, sizeof (ecma_job_promise_reaction_t)); } /* ecma_free_promise_reaction_job */ /** * Free the heap and the member of the PromiseAsyncReactionJob. */ static void ecma_free_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_p) /**< points to the * PromiseAsyncReactionJob */ { JERRY_ASSERT (job_p != NULL); ecma_free_value (job_p->executable_object); ecma_free_value (job_p->argument); jmem_heap_free_block (job_p, sizeof (ecma_job_promise_async_reaction_t)); } /* ecma_free_promise_async_reaction_job */ /** * Free the heap and the member of the PromiseAsyncGeneratorJob. */ static void ecma_free_promise_async_generator_job (ecma_job_promise_async_generator_t *job_p) /**< points to the * PromiseAsyncReactionJob */ { JERRY_ASSERT (job_p != NULL); ecma_free_value (job_p->executable_object); jmem_heap_free_block (job_p, sizeof (ecma_job_promise_async_generator_t)); } /* ecma_free_promise_async_generator_job */ /** * Free the heap and the member of the PromiseResolveThenableJob. */ static void ecma_free_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *job_p) /**< points to the * PromiseResolveThenableJob */ { JERRY_ASSERT (job_p != NULL); ecma_free_value (job_p->promise); ecma_free_value (job_p->thenable); ecma_free_value (job_p->then); jmem_heap_free_block (job_p, sizeof (ecma_job_promise_resolve_thenable_t)); } /* ecma_free_promise_resolve_thenable_job */ /** * The processor for PromiseReactionJob. * * See also: ES2015 25.4.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_process_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< the job to be operated */ { /* 2. */ JERRY_ASSERT ( ecma_object_class_is (ecma_get_object_from_value (job_p->capability), ECMA_OBJECT_CLASS_PROMISE_CAPABILITY)); ecma_promise_capability_t *capability_p; capability_p = (ecma_promise_capability_t *) ecma_get_object_from_value (job_p->capability); /* 3. */ ecma_value_t handler = job_p->handler; JERRY_ASSERT (ecma_is_value_boolean (handler) || ecma_op_is_callable (handler)); ecma_value_t handler_result; if (ecma_is_value_boolean (handler)) { /* 4-5. True indicates "identity" and false indicates "thrower" */ handler_result = ecma_copy_value (job_p->argument); } else { /* 6. */ handler_result = ecma_op_function_call (ecma_get_object_from_value (handler), ECMA_VALUE_UNDEFINED, &(job_p->argument), 1); } ecma_value_t status; if (ecma_is_value_false (handler) || ECMA_IS_VALUE_ERROR (handler_result)) { if (ECMA_IS_VALUE_ERROR (handler_result)) { handler_result = jcontext_take_exception (); } /* 7. */ status = ecma_op_function_call (ecma_get_object_from_value (capability_p->reject), ECMA_VALUE_UNDEFINED, &handler_result, 1); } else { /* 8. */ status = ecma_op_function_call (ecma_get_object_from_value (capability_p->resolve), ECMA_VALUE_UNDEFINED, &handler_result, 1); } ecma_free_value (handler_result); ecma_free_promise_reaction_job (job_p); return status; } /* ecma_process_promise_reaction_job */ /** * The processor for PromiseAsyncReactionJob. * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_process_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_p) /**< the job to be operated */ { ecma_object_t *object_p = ecma_get_object_from_value (job_p->executable_object); vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; if (ecma_job_queue_get_type (&job_p->header) == ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED) { if (!(executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD)) { executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; } else if (ECMA_AWAIT_GET_STATE (executable_object_p) == ECMA_AWAIT_YIELD_RETURN) { /* Unlike other operations, return captures rejected promises as well. */ ECMA_AWAIT_CHANGE_STATE (executable_object_p, YIELD_RETURN, YIELD_OPERATION); } else { if (ECMA_AWAIT_GET_STATE (executable_object_p) <= ECMA_AWAIT_YIELD_END) { JERRY_ASSERT (ecma_is_value_object (executable_object_p->iterator)); executable_object_p->iterator = ECMA_VALUE_UNDEFINED; JERRY_ASSERT (executable_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED || ecma_is_value_object (executable_object_p->frame_ctx.stack_top_p[-1])); executable_object_p->frame_ctx.stack_top_p--; } else if (ECMA_AWAIT_GET_STATE (executable_object_p) == ECMA_AWAIT_FOR_CLOSE && VM_GET_CONTEXT_TYPE (executable_object_p->frame_ctx.stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW) { ecma_free_value (job_p->argument); job_p->argument = ecma_copy_value (executable_object_p->frame_ctx.stack_top_p[-2]); } /* Exception: Abort iterators, clear all status. */ executable_object_p->extended_object.u.cls.u2.executable_obj_flags &= ECMA_AWAIT_CLEAR_MASK; executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; } } ecma_value_t result; uint16_t expected_bits; if (executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) { job_p->argument = ecma_await_continue (executable_object_p, job_p->argument); if (ECMA_IS_VALUE_ERROR (job_p->argument)) { job_p->argument = jcontext_take_exception (); executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; } else if (executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) { /* Continue iteration. */ JERRY_ASSERT (job_p->argument == ECMA_VALUE_UNDEFINED); result = ECMA_VALUE_UNDEFINED; goto free_job; } if (ECMA_AWAIT_GET_STATE (executable_object_p) <= ECMA_AWAIT_YIELD_END) { JERRY_ASSERT (ecma_is_value_object (executable_object_p->iterator)); executable_object_p->iterator = ECMA_VALUE_UNDEFINED; JERRY_ASSERT (executable_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED || ecma_is_value_object (executable_object_p->frame_ctx.stack_top_p[-1])); executable_object_p->frame_ctx.stack_top_p--; } /* Clear all status. */ executable_object_p->extended_object.u.cls.u2.executable_obj_flags &= ECMA_AWAIT_CLEAR_MASK; } result = opfunc_resume_executable_object (executable_object_p, job_p->argument); /* Argument reference has been taken by opfunc_resume_executable_object. */ job_p->argument = ECMA_VALUE_UNDEFINED; expected_bits = (ECMA_EXECUTABLE_OBJECT_COMPLETED | ECMA_ASYNC_GENERATOR_CALLED); if ((executable_object_p->extended_object.u.cls.u2.executable_obj_flags & expected_bits) == expected_bits) { ecma_async_generator_finalize (executable_object_p, result); result = ECMA_VALUE_UNDEFINED; } free_job: ecma_free_promise_async_reaction_job (job_p); return result; } /* ecma_process_promise_async_reaction_job */ /** * The processor for PromiseAsyncGeneratorJob. * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_process_promise_async_generator_job (ecma_job_promise_async_generator_t *job_p) /**< the job to be operated */ { ecma_object_t *object_p = ecma_get_object_from_value (job_p->executable_object); ecma_value_t result = ecma_async_generator_run ((vm_executable_object_t *) object_p); ecma_free_value (job_p->executable_object); jmem_heap_free_block (job_p, sizeof (ecma_job_promise_async_generator_t)); return result; } /* ecma_process_promise_async_generator_job */ /** * Process the PromiseResolveThenableJob. * * See also: ES2015 25.4.2.2 * * @return ecma value * Returned value must be freed with ecma_free_value */ static ecma_value_t ecma_process_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *job_p) /**< the job to be operated */ { ecma_promise_object_t *promise_p = (ecma_promise_object_t *) ecma_get_object_from_value (job_p->promise); promise_p->header.u.cls.u1.promise_flags &= (uint8_t) ~ECMA_PROMISE_ALREADY_RESOLVED; ecma_value_t ret = ecma_promise_run_executor ((ecma_object_t *) promise_p, job_p->then, job_p->thenable); if (ECMA_IS_VALUE_ERROR (ret)) { ret = jcontext_take_exception (); ecma_reject_promise_with_checks (job_p->promise, ret); ecma_free_value (ret); ret = ECMA_VALUE_UNDEFINED; } ecma_free_promise_resolve_thenable_job (job_p); return ret; } /* ecma_process_promise_resolve_thenable_job */ /** * Enqueue a Promise job into the jobqueue. */ static void ecma_enqueue_job (ecma_job_queue_item_t *job_p) /**< the job */ { JERRY_ASSERT (job_p->next_and_type <= ECMA_JOB_QUEUE_TYPE_MASK); if (JERRY_CONTEXT (job_queue_head_p) == NULL) { JERRY_CONTEXT (job_queue_head_p) = job_p; JERRY_CONTEXT (job_queue_tail_p) = job_p; } else { JERRY_ASSERT ((JERRY_CONTEXT (job_queue_tail_p)->next_and_type & ~ECMA_JOB_QUEUE_TYPE_MASK) == 0); JERRY_CONTEXT (job_queue_tail_p)->next_and_type |= (uintptr_t) job_p; JERRY_CONTEXT (job_queue_tail_p) = job_p; } } /* ecma_enqueue_job */ /** * Enqueue a PromiseReactionJob into the job queue. */ void ecma_enqueue_promise_reaction_job (ecma_value_t capability, /**< capability object */ ecma_value_t handler, /**< handler function */ ecma_value_t argument) /**< argument for the reaction */ { ecma_job_promise_reaction_t *job_p; job_p = (ecma_job_promise_reaction_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_reaction_t)); job_p->header.next_and_type = ECMA_JOB_PROMISE_REACTION; job_p->capability = ecma_copy_value (capability); job_p->handler = ecma_copy_value (handler); job_p->argument = ecma_copy_value (argument); ecma_enqueue_job (&job_p->header); } /* ecma_enqueue_promise_reaction_job */ /** * Enqueue a PromiseAsyncReactionJob into the job queue. */ void ecma_enqueue_promise_async_reaction_job (ecma_value_t executable_object, /**< executable object */ ecma_value_t argument, /**< argument */ bool is_rejected) /**< is_fulfilled */ { ecma_job_promise_async_reaction_t *job_p; job_p = (ecma_job_promise_async_reaction_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_async_reaction_t)); job_p->header.next_and_type = (is_rejected ? ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED : ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED); job_p->executable_object = ecma_copy_value (executable_object); job_p->argument = ecma_copy_value (argument); ecma_enqueue_job (&job_p->header); } /* ecma_enqueue_promise_async_reaction_job */ /** * Enqueue a PromiseAsyncGeneratorJob into the job queue. */ void ecma_enqueue_promise_async_generator_job (ecma_value_t executable_object) /**< executable object */ { ecma_job_promise_async_generator_t *job_p; job_p = (ecma_job_promise_async_generator_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_async_generator_t)); job_p->header.next_and_type = ECMA_JOB_PROMISE_ASYNC_GENERATOR; job_p->executable_object = ecma_copy_value (executable_object); ecma_enqueue_job (&job_p->header); } /* ecma_enqueue_promise_async_generator_job */ /** * Enqueue a PromiseResolveThenableJob into the job queue. */ void ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, /**< promise to be resolved */ ecma_value_t thenable, /**< thenable object */ ecma_value_t then) /**< 'then' function */ { JERRY_ASSERT (ecma_is_promise (ecma_get_object_from_value (promise))); JERRY_ASSERT (ecma_is_value_object (thenable)); JERRY_ASSERT (ecma_op_is_callable (then)); ecma_job_promise_resolve_thenable_t *job_p; job_p = (ecma_job_promise_resolve_thenable_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_resolve_thenable_t)); job_p->header.next_and_type = ECMA_JOB_PROMISE_THENABLE; job_p->promise = ecma_copy_value (promise); job_p->thenable = ecma_copy_value (thenable); job_p->then = ecma_copy_value (then); ecma_enqueue_job (&job_p->header); } /* ecma_enqueue_promise_resolve_thenable_job */ /** * Process enqueued Promise jobs until the first thrown error or until the * jobqueue becomes empty. * * @return result of the last processed job - if the jobqueue was non-empty, * undefined - otherwise. */ ecma_value_t ecma_process_all_enqueued_jobs (void) { ecma_value_t ret = ECMA_VALUE_UNDEFINED; while (JERRY_CONTEXT (job_queue_head_p) != NULL) { ecma_job_queue_item_t *job_p = JERRY_CONTEXT (job_queue_head_p); JERRY_CONTEXT (job_queue_head_p) = ecma_job_queue_get_next (job_p); ecma_fast_free_value (ret); switch (ecma_job_queue_get_type (job_p)) { case ECMA_JOB_PROMISE_REACTION: { ret = ecma_process_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p); break; } case ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED: case ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED: { ret = ecma_process_promise_async_reaction_job ((ecma_job_promise_async_reaction_t *) job_p); break; } case ECMA_JOB_PROMISE_ASYNC_GENERATOR: { ret = ecma_process_promise_async_generator_job ((ecma_job_promise_async_generator_t *) job_p); break; } default: { JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE); ret = ecma_process_promise_resolve_thenable_job ((ecma_job_promise_resolve_thenable_t *) job_p); break; } } if (ECMA_IS_VALUE_ERROR (ret)) { return ret; } } ecma_free_value (ret); return ECMA_VALUE_UNDEFINED; } /* ecma_process_all_enqueued_jobs */ /** * Release enqueued Promise jobs. */ void ecma_free_all_enqueued_jobs (void) { while (JERRY_CONTEXT (job_queue_head_p) != NULL) { ecma_job_queue_item_t *job_p = JERRY_CONTEXT (job_queue_head_p); JERRY_CONTEXT (job_queue_head_p) = ecma_job_queue_get_next (job_p); switch (ecma_job_queue_get_type (job_p)) { case ECMA_JOB_PROMISE_REACTION: { ecma_free_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p); break; } case ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED: case ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED: { ecma_free_promise_async_reaction_job ((ecma_job_promise_async_reaction_t *) job_p); break; } case ECMA_JOB_PROMISE_ASYNC_GENERATOR: { ecma_free_promise_async_generator_job ((ecma_job_promise_async_generator_t *) job_p); break; } default: { JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE); ecma_free_promise_resolve_thenable_job ((ecma_job_promise_resolve_thenable_t *) job_p); break; } } } } /* ecma_free_all_enqueued_jobs */ /** * @} * @} */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-referenceerror.inc.h000664 001750 001750 00000002464 15164251010 051627 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * ReferenceError built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_REFERENCE_ERROR_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_REFERENCE_ERROR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/webpi.h000664 001750 001750 00000011110 15164251010 034376 0ustar00ddennedyddennedy000000 000000 // Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Internal header: WebP decoding parameters and custom IO on buffer // // Author: somnath@google.com (Somnath Banerjee) #ifndef WEBP_DEC_WEBPI_H_ #define WEBP_DEC_WEBPI_H_ #ifdef __cplusplus extern "C" { #endif #include "../utils/rescaler.h" #include "./decode_vp8.h" //------------------------------------------------------------------------------ // WebPDecParams: Decoding output parameters. Transient internal object. typedef struct WebPDecParams WebPDecParams; typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p); typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos); struct WebPDecParams { WebPDecBuffer* output; // output buffer. uint8_t* tmp_y, *tmp_u, *tmp_v; // cache for the fancy upsampler // or used for tmp rescaling int last_y; // coordinate of the line that was last output const WebPDecoderOptions* options; // if not NULL, use alt decoding features // rescalers WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a; void* memory; // overall scratch memory for the output work. OutputFunc emit; // output RGB or YUV samples OutputFunc emit_alpha; // output alpha channel OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values }; //------------------------------------------------------------------------------ // Header parsing helpers // Structure storing a description of the RIFF headers. typedef struct { const uint8_t* data; // input buffer size_t data_size; // input buffer size int have_all_data; // true if all data is known to be available size_t offset; // offset to main data chunk (VP8 or VP8L) const uint8_t* alpha_data; // points to alpha chunk (if present) size_t alpha_data_size; // alpha chunk size size_t compressed_size; // VP8/VP8L compressed data size size_t riff_size; // size of the riff payload (or 0 if absent) int is_lossless; // true if a VP8L chunk is present } WebPHeaderStructure; //------------------------------------------------------------------------------ // Misc utils // Initializes VP8Io with custom setup, io and teardown functions. The default // hooks will use the supplied 'params' as io->opaque handle. void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io); // Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers // to the *compressed* format, not the output one. int WebPIoInitFromOptions(const WebPDecoderOptions* const options, VP8Io* const io, WEBP_CSP_MODE src_colorspace); //------------------------------------------------------------------------------ // Internal functions regarding WebPDecBuffer memory (in buffer.c). // Don't really need to be externally visible for now. // Prepare 'buffer' with the requested initial dimensions width/height. // If no external storage is supplied, initializes buffer by allocating output // memory and setting up the stride information. Validate the parameters. Return // an error code in case of problem (no memory, or invalid stride / size / // dimension / etc.). If *options is not NULL, also verify that the options' // parameters are valid and apply them to the width/height dimensions of the // output buffer. This takes cropping / scaling / rotation into account. // Also incorporates the options->flip flag to flip the buffer parameters if // needed. VP8StatusCode WebPAllocateDecBuffer(int width, int height, const WebPDecoderOptions* const options, WebPDecBuffer* const buffer); // Flip buffer vertically by negating the various strides. VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); // Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the // memory (still held by 'src'). void WebPCopyDecBuffer(const WebPDecBuffer* const src, WebPDecBuffer* const dst); //------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_DEC_WEBPI_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlShader.cpp000664 001750 001750 00000006462 15164251010 036410 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlShader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ uint32_t GlShader::compileShader(uint32_t type, char* shaderSrc) { GLuint shader; GLint compiled; // Create the shader object shader = glCreateShader(type); /** * [0] shader version string * [1] precision declaration * [2] shader source */ const char* shaderPack[3]; // but in general All Desktop GPU should use OpenGL version ( #version 330 core ) #if defined (THORVG_GL_TARGET_GLES) shaderPack[0] ="#version 300 es\n"; #else shaderPack[0] ="#version 330 core\n"; #endif shaderPack[1] = "precision highp float;\n precision highp int;\n"; shaderPack[2] = shaderSrc; // Load the shader source glShaderSource(shader, 3, shaderPack, NULL); // Compile the shader glCompileShader(shader); // Check the compile status glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 0) { auto infoLog = tvg::malloc(sizeof(char)*infoLen); glGetShaderInfoLog(shader, infoLen, NULL, infoLog); TVGERR("GL_ENGINE", "Error compiling shader: %s", infoLog); tvg::free(infoLog); } glDeleteShader(shader); } return shader; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ GlShader::GlShader(const char* vertSrc, const char* fragSrc) { mVtShader = compileShader(GL_VERTEX_SHADER, const_cast(vertSrc)); mFrShader = compileShader(GL_FRAGMENT_SHADER, const_cast(fragSrc)); } GlShader::~GlShader() { glDeleteShader(mVtShader); glDeleteShader(mFrShader); } uint32_t GlShader::getVertexShader() { return mVtShader; } uint32_t GlShader::getFragmentShader() { return mFrShader; }src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwFill.cpp000664 001750 001750 00000071063 15164251010 036145 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgSwCommon.h" #include "tvgFill.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #define RADIAL_A_THRESHOLD 0.0005f #define FIXPT_BITS 8 #define FIXPT_SIZE (1<radial.a * B = 2 * (dr * fr + rx * dx + ry * dy) * C = fr^2 - rx^2 - ry^2 * Derivatives are computed with respect to dx. * This procedure aims to optimize and eliminate the need to calculate all values from the beginning * for consecutive x values with a constant y. The Taylor series expansions are computed as long as * its terms are non-zero. */ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet) { auto radial = &fill->radial; auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA; deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA; auto rr = rx * rx + ry * ry; auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA; auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA; det = b * b + (rr - radial->fr * radial->fr) * radial->invA; deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr * 0.5f; deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr; } static uint32_t _estimateAAMargin(const Fill* fdata) { constexpr float marginScalingFactor = 800.0f; if (fdata->type() == Type::RadialGradient) { auto radius = CONST_RADIAL(fdata)->r; return tvg::zero(radius) ? 0 : static_cast(marginScalingFactor / radius); } else { auto grad = CONST_LINEAR(fdata); auto len = length(grad->p1, grad->p2); return tvg::zero(len) ? 0 : static_cast(marginScalingFactor / len); } } static void _adjustAAMargin(uint32_t& iMargin, uint32_t index) { constexpr float threshold = 0.1f; constexpr uint32_t iMarginMax = 40; auto iThreshold = static_cast(index * threshold); if (iMargin > iThreshold) iMargin = iThreshold; if (iMargin > iMarginMax) iMargin = iMarginMax; } static inline uint32_t _alphaUnblend(uint32_t c) { auto a = (c >> 24); if (a == 255 || a == 0) return c; auto invA = 255.0f / static_cast(a); auto c0 = static_cast(static_cast((c >> 16) & 0xFF) * invA); auto c1 = static_cast(static_cast((c >> 8) & 0xFF) * invA); auto c2 = static_cast(static_cast(c & 0xFF) * invA); return (a << 24) | (c0 << 16) | (c1 << 8) | c2; } static void _applyAA(SwFill* fill, uint32_t begin, uint32_t end) { if (begin == 0 || end == 0) return; auto i = SW_COLOR_TABLE - end; auto rgbaEnd = _alphaUnblend(fill->ctable[i]); auto rgbaBegin = _alphaUnblend(fill->ctable[begin]); auto dt = 1.0f / (begin + end + 1.0f); float t = dt; while (i != begin) { auto dist = 255 - static_cast(255 * t); auto color = INTERPOLATE(rgbaEnd, rgbaBegin, dist); fill->ctable[i++] = ALPHA_BLEND((color | 0xff000000), (color >> 24)); if (i == SW_COLOR_TABLE) i = 0; t += dt; } } static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity) { if (fill->solid) return true; const Fill::ColorStop* colors; auto cnt = fdata->colorStops(&colors); if (cnt == 0 || !colors) return false; auto pColors = colors; auto a = MULTIPLY(pColors->a, opacity); if (a < 255) fill->translucent = true; auto r = pColors->r; auto g = pColors->g; auto b = pColors->b; auto rgba = surface->join(r, g, b, a); auto inc = 1.0f / static_cast(SW_COLOR_TABLE); auto pos = 1.5f * inc; uint32_t i = 0; //If repeat is true, anti-aliasing must be applied between the last and the first colors. auto repeat = fill->spread == FillSpread::Repeat; uint32_t iAABegin = repeat ? _estimateAAMargin(fdata) : 0; uint32_t iAAEnd = 0; fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a); while (pos <= pColors->offset) { fill->ctable[i] = fill->ctable[i - 1]; ++i; pos += inc; } for (uint32_t j = 0; j < cnt - 1; ++j) { if (repeat && j == cnt - 2 && iAAEnd == 0) { iAAEnd = iAABegin; _adjustAAMargin(iAAEnd, SW_COLOR_TABLE - i); } auto curr = colors + j; auto next = curr + 1; auto div = next->offset - curr->offset; auto delta = div != 0.0f ? (1.0f / div) : 0.0f; auto a2 = MULTIPLY(next->a, opacity); if (!fill->translucent && a2 < 255) fill->translucent = true; auto rgba2 = surface->join(next->r, next->g, next->b, a2); while (pos < next->offset && i < SW_COLOR_TABLE) { auto t = (pos - curr->offset) * delta; auto dist = static_cast(255 * t); auto dist2 = 255 - dist; auto color = INTERPOLATE(rgba, rgba2, dist2); fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24)); ++i; pos += inc; } rgba = rgba2; a = a2; if (repeat && j == 0) _adjustAAMargin(iAABegin, i - 1); } rgba = ALPHA_BLEND((rgba | 0xff000000), a); for (; i < SW_COLOR_TABLE; ++i) { fill->ctable[i] = rgba; } //For repeat fill spread apply anti-aliasing between the last and first colors, //otherwise make sure the last color stop is represented at the end of the table. if (repeat) _applyAA(fill, iAABegin, iAAEnd); else fill->ctable[SW_COLOR_TABLE - 1] = rgba; return true; } bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& pTransform) { float x1, x2, y1, y2; linear->linear(&x1, &y1, &x2, &y2); fill->linear.dx = x2 - x1; fill->linear.dy = y2 - y1; auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy; if (len < FLOAT_EPSILON) { if (tvg::zero(fill->linear.dx) && tvg::zero(fill->linear.dy)) { fill->solid = true; } return true; } fill->linear.dx /= len; fill->linear.dy /= len; fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1; const auto& transform = pTransform * linear->transform(); Matrix itransform; if (!inverse(&transform, &itransform)) return false; fill->linear.offset += fill->linear.dx * itransform.e13 + fill->linear.dy * itransform.e23; auto dx = fill->linear.dx; fill->linear.dx = dx * itransform.e11 + fill->linear.dy * itransform.e21; fill->linear.dy = dx * itransform.e12 + fill->linear.dy * itransform.e22; return true; } bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& pTransform) { float cx, cy, r, fx, fy, fr; radial->radial(&cx, &cy, &r, &fx, &fy, &fr); if ((fill->solid = !CONST_RADIAL(radial)->correct(fx, fy, fr))) return true; fill->radial.dr = r - fr; fill->radial.dx = cx - fx; fill->radial.dy = cy - fy; fill->radial.fr = fr; fill->radial.fx = fx; fill->radial.fy = fy; fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy; constexpr float precision = 0.01f; if (fill->radial.a < precision) fill->radial.a = precision; fill->radial.invA = 1.0f / fill->radial.a; const auto& transform = pTransform * radial->transform(); Matrix itransform; if (!inverse(&transform, &itransform)) return false; fill->radial.a11 = itransform.e11; fill->radial.a12 = itransform.e12; fill->radial.a13 = itransform.e13; fill->radial.a21 = itransform.e21; fill->radial.a22 = itransform.e22; fill->radial.a23 = itransform.e23; return true; } static inline uint32_t _clamp(const SwFill* fill, int32_t pos) { switch (fill->spread) { case FillSpread::Pad: { if (pos >= SW_COLOR_TABLE) pos = SW_COLOR_TABLE - 1; else if (pos < 0) pos = 0; break; } case FillSpread::Repeat: { pos = pos % SW_COLOR_TABLE; if (pos < 0) pos = SW_COLOR_TABLE + pos; break; } case FillSpread::Reflect: { auto limit = SW_COLOR_TABLE * 2; pos = pos % limit; if (pos < 0) pos = limit + pos; if (pos >= SW_COLOR_TABLE) pos = (limit - pos - 1); break; } } return pos; } static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos) { int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; return fill->ctable[_clamp(fill, i)]; } static inline uint32_t _pixel(const SwFill* fill, float pos) { auto i = static_cast(pos * (SW_COLOR_TABLE - 1) + 0.5f); return fill->ctable[_clamp(fill, i)]; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity) { //edge case if (fill->radial.a < RADIAL_A_THRESHOLD) { auto radial = &fill->radial; auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; if (opacity == 255) { for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); *dst = opBlendNormal(_pixel(fill, x0), *dst, alpha(cmp)); rx += radial->a11; ry += radial->a21; } } else { for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); *dst = opBlendNormal(_pixel(fill, x0), *dst, MULTIPLY(opacity, alpha(cmp))); rx += radial->a11; ry += radial->a21; } } } else { float b, deltaB, det, deltaDet, deltaDeltaDet; _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); if (opacity == 255) { for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, alpha(cmp)); det += deltaDet; deltaDet += deltaDeltaDet; b += deltaB; } } else { for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, MULTIPLY(opacity, alpha(cmp))); det += deltaDet; deltaDet += deltaDeltaDet; b += deltaB; } } } } void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, uint8_t a) { if (fill->radial.a < RADIAL_A_THRESHOLD) { auto radial = &fill->radial; auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; for (uint32_t i = 0; i < len; ++i, ++dst) { auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); *dst = op(_pixel(fill, x0), *dst, a); rx += radial->a11; ry += radial->a21; } } else { float b, deltaB, det, deltaDet, deltaDeltaDet; _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); for (uint32_t i = 0; i < len; ++i, ++dst) { *dst = op(_pixel(fill, sqrtf(det) - b), *dst, a); det += deltaDet; deltaDet += deltaDeltaDet; b += deltaB; } } } void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a) { if (fill->radial.a < RADIAL_A_THRESHOLD) { auto radial = &fill->radial; auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; for (uint32_t i = 0 ; i < len ; ++i, ++dst) { auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); auto src = MULTIPLY(a, A(_pixel(fill, x0))); *dst = maskOp(src, *dst, ~src); rx += radial->a11; ry += radial->a21; } } else { float b, deltaB, det, deltaDet, deltaDeltaDet; _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); for (uint32_t i = 0 ; i < len ; ++i, ++dst) { auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det) - b))); *dst = maskOp(src, *dst, ~src); det += deltaDet; deltaDet += deltaDeltaDet; b += deltaB; } } } void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a) { if (fill->radial.a < RADIAL_A_THRESHOLD) { auto radial = &fill->radial; auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) { auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); auto src = MULTIPLY(A(A(_pixel(fill, x0))), a); auto tmp = maskOp(src, *cmp, 0); *dst = tmp + MULTIPLY(*dst, ~tmp); rx += radial->a11; ry += radial->a21; } } else { float b, deltaB, det, deltaDet, deltaDeltaDet; _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) { auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a); auto tmp = maskOp(src, *cmp, 0); *dst = tmp + MULTIPLY(*dst, ~tmp); det += deltaDet; deltaDet += deltaDeltaDet; b += deltaB; } } } void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, SwBlender op2, uint8_t a) { if (fill->radial.a < RADIAL_A_THRESHOLD) { auto radial = &fill->radial; auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; if (a == 255) { for (uint32_t i = 0; i < len; ++i, ++dst) { auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); auto tmp = op(_pixel(fill, x0), *dst, 255); *dst = op2(tmp, *dst); rx += radial->a11; ry += radial->a21; } } else { for (uint32_t i = 0; i < len; ++i, ++dst) { auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); auto tmp = op(_pixel(fill, x0), *dst, 255); auto tmp2 = op2(tmp, *dst); *dst = INTERPOLATE(tmp2, *dst, a); rx += radial->a11; ry += radial->a21; } } } else { float b, deltaB, det, deltaDet, deltaDeltaDet; _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); if (a == 255) { for (uint32_t i = 0 ; i < len ; ++i, ++dst) { auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255); *dst = op2(tmp, *dst); det += deltaDet; deltaDet += deltaDeltaDet; b += deltaB; } } else { for (uint32_t i = 0 ; i < len ; ++i, ++dst) { auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255); auto tmp2 = op2(tmp, *dst); *dst = INTERPOLATE(tmp2, *dst, a); det += deltaDet; deltaDet += deltaDeltaDet; b += deltaB; } } } } void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity) { //Rotation float rx = x + 0.5f; float ry = y + 0.5f; float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (opacity == 255) { if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { *dst = opBlendNormal(color, *dst, alpha(cmp)); } return; } auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); auto vMin = -vMax; auto v = t + (inc * len); //we can use fixed point math if (v < vMax && v > vMin) { auto t2 = static_cast(t * FIXPT_SIZE); auto inc2 = static_cast(inc * FIXPT_SIZE); for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) { *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, alpha(cmp)); t2 += inc2; } //we have to fallback to float math } else { uint32_t counter = 0; while (counter++ < len) { *dst = opBlendNormal(_pixel(fill, t / SW_COLOR_TABLE), *dst, alpha(cmp)); ++dst; t += inc; cmp += csize; } } } else { if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity)); } return; } auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); auto vMin = -vMax; auto v = t + (inc * len); //we can use fixed point math if (v < vMax && v > vMin) { auto t2 = static_cast(t * FIXPT_SIZE); auto inc2 = static_cast(inc * FIXPT_SIZE); for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) { *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, MULTIPLY(alpha(cmp), opacity)); t2 += inc2; } //we have to fallback to float math } else { uint32_t counter = 0; while (counter++ < len) { *dst = opBlendNormal(_pixel(fill, t / SW_COLOR_TABLE), *dst, MULTIPLY(opacity, alpha(cmp))); ++dst; t += inc; cmp += csize; } } } } void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a) { //Rotation float rx = x + 0.5f; float ry = y + 0.5f; float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (tvg::zero(inc)) { auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE)))); for (uint32_t i = 0; i < len; ++i, ++dst) { *dst = maskOp(src, *dst, ~src); } return; } auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); auto vMin = -vMax; auto v = t + (inc * len); //we can use fixed point math if (v < vMax && v > vMin) { auto t2 = static_cast(t * FIXPT_SIZE); auto inc2 = static_cast(inc * FIXPT_SIZE); for (uint32_t j = 0; j < len; ++j, ++dst) { auto src = MULTIPLY(A(_fixedPixel(fill, t2)), a); *dst = maskOp(src, *dst, ~src); t2 += inc2; } //we have to fallback to float math } else { uint32_t counter = 0; while (counter++ < len) { auto src = MULTIPLY(A(_pixel(fill, t / SW_COLOR_TABLE)), a); *dst = maskOp(src, *dst, ~src); ++dst; t += inc; } } } void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a) { //Rotation float rx = x + 0.5f; float ry = y + 0.5f; float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (tvg::zero(inc)) { auto src = A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE))); src = MULTIPLY(src, a); for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) { auto tmp = maskOp(src, *cmp, 0); *dst = tmp + MULTIPLY(*dst, ~tmp); } return; } auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); auto vMin = -vMax; auto v = t + (inc * len); //we can use fixed point math if (v < vMax && v > vMin) { auto t2 = static_cast(t * FIXPT_SIZE); auto inc2 = static_cast(inc * FIXPT_SIZE); for (uint32_t j = 0; j < len; ++j, ++dst, ++cmp) { auto src = MULTIPLY(a, A(_fixedPixel(fill, t2))); auto tmp = maskOp(src, *cmp, 0); *dst = tmp + MULTIPLY(*dst, ~tmp); t2 += inc2; } //we have to fallback to float math } else { uint32_t counter = 0; while (counter++ < len) { auto src = MULTIPLY(A(_pixel(fill, t / SW_COLOR_TABLE)), a); auto tmp = maskOp(src, *cmp, 0); *dst = tmp + MULTIPLY(*dst, ~tmp); ++dst; ++cmp; t += inc; } } } void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, uint8_t a) { //Rotation float rx = x + 0.5f; float ry = y + 0.5f; float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); for (uint32_t i = 0; i < len; ++i, ++dst) { *dst = op(color, *dst, a); } return; } auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); auto vMin = -vMax; auto v = t + (inc * len); //we can use fixed point math if (v < vMax && v > vMin) { auto t2 = static_cast(t * FIXPT_SIZE); auto inc2 = static_cast(inc * FIXPT_SIZE); for (uint32_t j = 0; j < len; ++j, ++dst) { *dst = op(_fixedPixel(fill, t2), *dst, a); t2 += inc2; } //we have to fallback to float math } else { uint32_t counter = 0; while (counter++ < len) { *dst = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, a); ++dst; t += inc; } } } void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlenderA op, SwBlender op2, uint8_t a) { //Rotation float rx = x + 0.5f; float ry = y + 0.5f; float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); if (a == 255) { for (uint32_t i = 0; i < len; ++i, ++dst) { auto tmp = op(color, *dst, a); *dst = op2(tmp, *dst); } } else { for (uint32_t i = 0; i < len; ++i, ++dst) { auto tmp = op(color, *dst, a); auto tmp2 = op2(tmp, *dst); *dst = INTERPOLATE(tmp2, *dst, a); } } return; } auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); auto vMin = -vMax; auto v = t + (inc * len); if (a == 255) { //we can use fixed point math if (v < vMax && v > vMin) { auto t2 = static_cast(t * FIXPT_SIZE); auto inc2 = static_cast(inc * FIXPT_SIZE); for (uint32_t j = 0; j < len; ++j, ++dst) { auto tmp = op(_fixedPixel(fill, t2), *dst, 255); *dst = op2(tmp, *dst); t2 += inc2; } //we have to fallback to float math } else { uint32_t counter = 0; while (counter++ < len) { auto tmp = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, 255); *dst = op2(tmp, *dst); ++dst; t += inc; } } } else { //we can use fixed point math if (v < vMax && v > vMin) { auto t2 = static_cast(t * FIXPT_SIZE); auto inc2 = static_cast(inc * FIXPT_SIZE); for (uint32_t j = 0; j < len; ++j, ++dst) { auto tmp = op(_fixedPixel(fill, t2), *dst, 255); auto tmp2 = op2(tmp, *dst); *dst = INTERPOLATE(tmp2, *dst, a); t2 += inc2; } //we have to fallback to float math } else { uint32_t counter = 0; while (counter++ < len) { auto tmp = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, 255); auto tmp2 = op2(tmp, *dst); *dst = INTERPOLATE(tmp2, *dst, a); ++dst; t += inc; } } } } bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable) { if (!fill) return false; fill->spread = fdata->spread(); if (fdata->type() == Type::LinearGradient) { if (!_prepareLinear(fill, static_cast(fdata), transform)) return false; } else if (fdata->type() == Type::RadialGradient) { if (!_prepareRadial(fill, static_cast(fdata), transform)) return false; } if (ctable) return _updateColorTable(fill, fdata, surface, opacity); return true; } const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata) { if (!fill->solid) return nullptr; const Fill::ColorStop* colors; auto cnt = fdata->colorStops(&colors); if (cnt == 0 || !colors) return nullptr; return colors + cnt - 1; } void fillReset(SwFill* fill) { fill->translucent = false; fill->solid = false; } void fillFree(SwFill* fill) { if (fill) tvg::free(fill); } src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h000664 001750 001750 00000005524 15164251010 052154 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * RegExp.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_REGEXP /* ECMA-262 v5, 15.10.6.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_REGEXP, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_FLAGS, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_FLAGS, ECMA_PROPERTY_FLAG_CONFIGURABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_SOURCE, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_SOURCE, ECMA_PROPERTY_FLAG_CONFIGURABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_GLOBAL, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_GLOBAL, ECMA_PROPERTY_FLAG_CONFIGURABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_IGNORECASE_UL, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_IGNORE_CASE, ECMA_PROPERTY_FLAG_CONFIGURABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_DOTALL, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_DOT_ALL, ECMA_PROPERTY_FLAG_CONFIGURABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_MULTILINE, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_MULTILINE, ECMA_PROPERTY_FLAG_CONFIGURABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_UNICODE, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_UNICODE, ECMA_PROPERTY_FLAG_CONFIGURABLE) ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_STICKY, ECMA_REGEXP_PROTOTYPE_ROUTINE_GET_STICKY, ECMA_PROPERTY_FLAG_CONFIGURABLE) ROUTINE (LIT_GLOBAL_SYMBOL_REPLACE, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_REPLACE, 2, 2) ROUTINE (LIT_GLOBAL_SYMBOL_SEARCH, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SEARCH, 1, 1) ROUTINE (LIT_GLOBAL_SYMBOL_SPLIT, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SPLIT, 2, 2) ROUTINE (LIT_GLOBAL_SYMBOL_MATCH, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH, 1, 1) ROUTINE (LIT_GLOBAL_SYMBOL_MATCH_ALL, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH_ALL, 1, 1) #if JERRY_BUILTIN_ANNEXB ROUTINE (LIT_MAGIC_STRING_COMPILE, ECMA_REGEXP_PROTOTYPE_ROUTINE_COMPILE, 2, 2) #endif /* JERRY_BUILTIN_ANNEXB */ ROUTINE (LIT_MAGIC_STRING_EXEC, ECMA_REGEXP_PROTOTYPE_ROUTINE_EXEC, 1, 1) ROUTINE (LIT_MAGIC_STRING_TEST, ECMA_REGEXP_PROTOTYPE_ROUTINE_TEST, 1, 1) ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_REGEXP_PROTOTYPE_ROUTINE_TO_STRING, 0, 0) #endif /* JERRY_BUILTIN_REGEXP */ #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/tvgWebpLoader.h000664 001750 001750 00000003321 15164251010 035307 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WEBP_LOADER_H_ #define _TVG_WEBP_LOADER_H_ #include "tvgLoader.h" #include "tvgTaskScheduler.h" class WebpLoader : public ImageLoader, public Task { private: uint8_t* data = nullptr; uint32_t size = 0; bool freeData = false; void clear(); void run(unsigned tid) override; public: WebpLoader(); ~WebpLoader(); bool open(const char* path) override; bool open(const char* data, uint32_t size, const char* rpath, bool copy) override; bool read() override; bool close() override; RenderSurface* bitmap() override; }; #endif //_TVG_WEBP_LOADER_H_mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/quant.cpp000664 001750 001750 00000007365 15164251010 034774 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Quantizer initialization // // Author: Skal (pascal.massimino@gmail.com) #include "./vp8i.h" static WEBP_INLINE int clip(int v, int M) { return v < 0 ? 0 : v > M ? M : v; } // Paragraph 14.1 static const uint8_t kDcTable[128] = { 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 91, 93, 95, 96, 98, 100, 101, 102, 104, 106, 108, 110, 112, 114, 116, 118, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 143, 145, 148, 151, 154, 157 }; static const uint16_t kAcTable[128] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 119, 122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164, 167, 170, 173, 177, 181, 185, 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 234, 239, 245, 249, 254, 259, 264, 269, 274, 279, 284 }; //------------------------------------------------------------------------------ // Paragraph 9.6 void VP8ParseQuant(VP8Decoder* const dec) { VP8BitReader* const br = &dec->br_; const int base_q0 = VP8GetValue(br, 7); const int dqy1_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; const int dqy2_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; const int dqy2_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; const int dquv_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; const int dquv_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; const VP8SegmentHeader* const hdr = &dec->segment_hdr_; int i; for (i = 0; i < NUM_MB_SEGMENTS; ++i) { int q; if (hdr->use_segment_) { q = hdr->quantizer_[i]; if (!hdr->absolute_delta_) { q += base_q0; } } else { if (i > 0) { dec->dqm_[i] = dec->dqm_[0]; continue; } else { q = base_q0; } } { VP8QuantMatrix* const m = &dec->dqm_[i]; m->y1_mat_[0] = kDcTable[clip(q + dqy1_dc, 127)]; m->y1_mat_[1] = kAcTable[clip(q + 0, 127)]; m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2; // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16. // The smallest precision for that is '(x*6349) >> 12' but 16 is a good // word size. m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16; if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8; m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)]; m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)]; m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation } } } //------------------------------------------------------------------------------ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/000775 001750 001750 00000000000 15164251010 040107 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/srcmodules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/ostreamwrapper.h000664 001750 001750 00000004406 15164251010 040077 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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 #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 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 OStreamWrapper; typedef BasicOStreamWrapper WOStreamWrapper; #ifdef __clang__ RAPIDJSON_DIAG_POP #endif RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_OSTREAMWRAPPER_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp000664 001750 001750 00000010165 15164251010 037137 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgJpgLoader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ void JpgLoader::clear() { if (freeData) tvg::free(data); data = nullptr; size = 0; freeData = false; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ JpgLoader::JpgLoader() : ImageLoader(FileType::Jpg) { jpegDecompressor = tjInitDecompress(); } JpgLoader::~JpgLoader() { clear(); tjDestroy(jpegDecompressor); //This image is shared with raster engine. tjFree(surface.buf8); } bool JpgLoader::open(const char* path) { #ifdef THORVG_FILE_IO_SUPPORT if (!(data = (unsigned char*) LoadModule::open(path, size))) return false; int width, height, subSample, colorSpace; if (tjDecompressHeader3(jpegDecompressor, data, size, &width, &height, &subSample, &colorSpace) < 0) return false; w = static_cast(width); h = static_cast(height); freeData = true; return true; #else return false; #endif } bool JpgLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy) { int width, height, subSample, colorSpace; if (tjDecompressHeader3(jpegDecompressor, (unsigned char *) data, size, &width, &height, &subSample, &colorSpace) < 0) return false; if (copy) { this->data = tvg::malloc(size); if (!this->data) return false; memcpy((unsigned char *)this->data, data, size); freeData = true; } else { this->data = (unsigned char *) data; freeData = false; } w = static_cast(width); h = static_cast(height); this->size = size; return true; } bool JpgLoader::read() { if (!LoadModule::read()) return true; if (w == 0 || h == 0) return false; //determine the image format TJPF format; if (ImageLoader::cs == ColorSpace::ARGB8888 || ImageLoader::cs == ColorSpace::ARGB8888S) { format = TJPF_BGRX; surface.cs = ColorSpace::ARGB8888; } else { format = TJPF_RGBX; surface.cs = ColorSpace::ABGR8888; } auto image = (unsigned char *)tjAlloc(static_cast(w) * static_cast(h) * tjPixelSize[format]); if (!image) return false; //decompress jpg image if (tjDecompress2(jpegDecompressor, data, size, image, static_cast(w), 0, static_cast(h), format, 0) < 0) { TVGERR("JPG LOADER", "%s", tjGetErrorStr()); tjFree(image); image = nullptr; return false; } //setup the surface surface.buf8 = image; surface.stride = w; surface.w = w; surface.h = h; surface.channelSize = sizeof(uint32_t); surface.premultiplied = true; clear(); return true; } src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.cpp000664 001750 001750 00000033246 15164251010 052274 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-function-prototype.h" #include "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-extended-info.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-proxy-object.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_FUNCTION_PROTOTYPE_ROUTINE_START = 0, ECMA_FUNCTION_PROTOTYPE_TO_STRING, ECMA_FUNCTION_PROTOTYPE_CALL, ECMA_FUNCTION_PROTOTYPE_APPLY, ECMA_FUNCTION_PROTOTYPE_BIND, ECMA_FUNCTION_PROTOTYPE_SYMBOL_HAS_INSTANCE, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-function-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID function_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup functionprototype ECMA Function.prototype object built-in * @{ */ /** * Maximum number of arguments for an apply function. */ #define ECMA_FUNCTION_APPLY_ARGUMENT_COUNT_LIMIT 65535 /** * The Function.prototype object's 'toString' routine * * See also: * ECMA-262 v5, 15.3.4.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_function_prototype_object_to_string (ecma_object_t *func_obj_p) /**< this argument object */ { if (ecma_get_object_type (func_obj_p) != ECMA_OBJECT_TYPE_FUNCTION) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE); } return ecma_make_magic_string_value (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA); } /* ecma_builtin_function_prototype_object_to_string */ /** * The Function.prototype object's 'apply' routine * * See also: * ECMA-262 v5, 15.3.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_function_prototype_object_apply (ecma_object_t *func_obj_p, /**< this argument object */ ecma_value_t arg1, /**< first argument */ ecma_value_t arg2) /**< second argument */ { /* 2. */ if (ecma_is_value_null (arg2) || ecma_is_value_undefined (arg2)) { return ecma_op_function_call (func_obj_p, arg1, NULL, 0); } /* 3. */ if (!ecma_is_value_object (arg2)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (arg2); /* 4-5. */ ecma_length_t length; ecma_value_t len_value = ecma_op_object_get_length (obj_p, &length); if (ECMA_IS_VALUE_ERROR (len_value)) { return len_value; } if (length >= ECMA_FUNCTION_APPLY_ARGUMENT_COUNT_LIMIT) { return ecma_raise_range_error (ECMA_ERR_TOO_MANY_ARGUMENTS_DECLARED_FOR_FUNCTION_APPLY); } /* 6. */ ecma_value_t ret_value = ECMA_VALUE_EMPTY; JMEM_DEFINE_LOCAL_ARRAY (arguments_list_p, length, ecma_value_t); ecma_length_t index = 0; /* 7. */ for (index = 0; index < length; index++) { ecma_value_t get_value = ecma_op_object_get_by_index (obj_p, index); if (ECMA_IS_VALUE_ERROR (get_value)) { ret_value = get_value; break; } arguments_list_p[index] = get_value; } if (ecma_is_value_empty (ret_value)) { JERRY_ASSERT (index == length); ret_value = ecma_op_function_call (func_obj_p, arg1, arguments_list_p, (uint32_t) length); } for (uint32_t remove_index = 0; remove_index < index; remove_index++) { ecma_free_value (arguments_list_p[remove_index]); } JMEM_FINALIZE_LOCAL_ARRAY (arguments_list_p); return ret_value; } /* ecma_builtin_function_prototype_object_apply */ /** * The Function.prototype object's 'call' routine * * See also: * ECMA-262 v5, 15.3.4.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_function_prototype_object_call (ecma_object_t *func_obj_p, /**< this argument object */ const ecma_value_t *arguments_list_p, /**< list of arguments */ uint32_t arguments_number) /**< number of arguments */ { if (arguments_number == 0) { /* Even a 'this' argument is missing. */ return ecma_op_function_call (func_obj_p, ECMA_VALUE_UNDEFINED, NULL, 0); } return ecma_op_function_call (func_obj_p, arguments_list_p[0], arguments_list_p + 1, (uint32_t) (arguments_number - 1u)); } /* ecma_builtin_function_prototype_object_call */ /** * The Function.prototype object's 'bind' routine * * See also: * ECMA-262 v5, 15.3.4.5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_function_prototype_object_bind (ecma_object_t *this_arg_obj_p, /**< this argument object */ const ecma_value_t *arguments_list_p, /**< list of arguments */ uint32_t arguments_number) /**< number of arguments */ { /* 4. 11. 18. */ ecma_object_t *prototype_obj_p; #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (this_arg_obj_p)) { ecma_value_t proto = ecma_proxy_object_get_prototype_of (this_arg_obj_p); if (ECMA_IS_VALUE_ERROR (proto)) { return proto; } prototype_obj_p = ecma_is_value_null (proto) ? NULL : ecma_get_object_from_value (proto); } else { #endif /* JERRY_BUILTIN_PROXY */ jmem_cpointer_t proto_cp = ecma_op_ordinary_object_get_prototype_of (this_arg_obj_p); if (proto_cp != JMEM_CP_NULL) { prototype_obj_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp); ecma_ref_object (prototype_obj_p); } else { prototype_obj_p = NULL; } #if JERRY_BUILTIN_PROXY } #endif /* JERRY_BUILTIN_PROXY */ ecma_object_t *function_p; ecma_bound_function_t *bound_func_p; if (arguments_number == 0 || (arguments_number == 1 && !ecma_is_value_integer_number (arguments_list_p[0]))) { function_p = ecma_create_object (prototype_obj_p, sizeof (ecma_bound_function_t), ECMA_OBJECT_TYPE_BOUND_FUNCTION); /* 8. */ bound_func_p = (ecma_bound_function_t *) function_p; ECMA_SET_NON_NULL_POINTER_TAG (bound_func_p->header.u.bound_function.target_function, this_arg_obj_p, 0); bound_func_p->header.u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED; if (arguments_number != 0) { bound_func_p->header.u.bound_function.args_len_or_this = ecma_copy_value_if_not_object (arguments_list_p[0]); } } else { JERRY_ASSERT (arguments_number > 0); size_t obj_size = sizeof (ecma_bound_function_t) + (arguments_number * sizeof (ecma_value_t)); function_p = ecma_create_object (prototype_obj_p, obj_size, ECMA_OBJECT_TYPE_BOUND_FUNCTION); /* 8. */ bound_func_p = (ecma_bound_function_t *) function_p; ECMA_SET_NON_NULL_POINTER_TAG (bound_func_p->header.u.bound_function.target_function, this_arg_obj_p, 0); /* NOTE: This solution provides temporary false data about the object's size but prevents GC from freeing it until it's not fully initialized. */ bound_func_p->header.u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED; ecma_value_t *args_p = (ecma_value_t *) (bound_func_p + 1); for (uint32_t i = 0; i < arguments_number; i++) { *args_p++ = ecma_copy_value_if_not_object (arguments_list_p[i]); } ecma_value_t args_len_or_this = ecma_make_integer_value ((ecma_integer_value_t) arguments_number); bound_func_p->header.u.bound_function.args_len_or_this = args_len_or_this; } if (prototype_obj_p != NULL) { ecma_deref_object (prototype_obj_p); } bound_func_p->target_length = ecma_make_integer_value (0); ecma_string_t *len_string = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH); ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (this_arg_obj_p, len_string, &prop_desc); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (status)) { ecma_deref_object (function_p); return status; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_true (status)) { ecma_free_property_descriptor (&prop_desc); ecma_value_t len_value = ecma_op_object_get (this_arg_obj_p, len_string); if (ECMA_IS_VALUE_ERROR (len_value)) { ecma_deref_object (function_p); return len_value; } if (ecma_is_value_number (len_value)) { ecma_number_t len_num; ecma_op_to_integer (len_value, &len_num); bound_func_p->target_length = ecma_make_number_value (len_num); } ecma_free_value (len_value); } /* 12. */ ecma_value_t name_value = ecma_op_object_get_by_magic_id (this_arg_obj_p, LIT_MAGIC_STRING_NAME); if (ECMA_IS_VALUE_ERROR (name_value)) { ecma_deref_object (function_p); return name_value; } ecma_string_t *name_p; if (ecma_is_value_string (name_value)) { name_p = ecma_get_string_from_value (name_value); } else { ecma_free_value (name_value); name_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } ecma_value_t bound_function_name = ecma_op_function_form_name (name_p, (char*)"bound ", 6); ecma_deref_ecma_string (name_p); ecma_property_value_t *name_prop_value_p; name_prop_value_p = ecma_create_named_data_property (function_p, ecma_get_magic_string (LIT_MAGIC_STRING_NAME), ECMA_PROPERTY_FLAG_CONFIGURABLE, NULL); name_prop_value_p->value = bound_function_name; /* * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_FUNCTION type. * * See also: ecma_object_get_class_name */ /* 22. */ return ecma_make_object_value (function_p); } /* ecma_builtin_function_prototype_object_bind */ /** * Handle calling [[Call]] of built-in Function.prototype object * * @return ecma value */ ecma_value_t ecma_builtin_function_prototype_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ECMA_VALUE_UNDEFINED; } /* ecma_builtin_function_prototype_dispatch_call */ /** * Handle calling [[Construct]] of built-in Function.prototype object * * @return ecma value */ ecma_value_t ecma_builtin_function_prototype_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_FUNCTION_PROTOTYPE_NOT_A_CONSTRUCTOR); } /* ecma_builtin_function_prototype_dispatch_construct */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_function_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { if (!ecma_op_is_callable (this_arg)) { if (JERRY_UNLIKELY (builtin_routine_id == ECMA_FUNCTION_PROTOTYPE_SYMBOL_HAS_INSTANCE)) { return ECMA_VALUE_FALSE; } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_FUNCTION); } ecma_object_t *func_obj_p = ecma_get_object_from_value (this_arg); switch (builtin_routine_id) { case ECMA_FUNCTION_PROTOTYPE_TO_STRING: { return ecma_builtin_function_prototype_object_to_string (func_obj_p); } case ECMA_FUNCTION_PROTOTYPE_APPLY: { return ecma_builtin_function_prototype_object_apply (func_obj_p, arguments_list_p[0], arguments_list_p[1]); } case ECMA_FUNCTION_PROTOTYPE_CALL: { return ecma_builtin_function_prototype_object_call (func_obj_p, arguments_list_p, arguments_number); } case ECMA_FUNCTION_PROTOTYPE_BIND: { return ecma_builtin_function_prototype_object_bind (func_obj_p, arguments_list_p, arguments_number); } case ECMA_FUNCTION_PROTOTYPE_SYMBOL_HAS_INSTANCE: { return ecma_op_object_has_instance (func_obj_p, arguments_list_p[0]); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_function_prototype_dispatch_routine */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.gitattributes000664 001750 001750 00000000316 15164251010 032110 0ustar00ddennedyddennedy000000 000000 # Normalize EOL for all files that Git considers text files * text=auto eol=lf # Except for bat files, which are Windows only files *.bat eol=crlf # And some test files where the EOL matters *.json -text glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/api/meson.build000664 001750 001750 00000000240 15164251010 042226 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'jerryscript.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test0.svg000664 001750 001750 00000250726 15164251010 034002 0ustar00ddennedyddennedy000000 000000 thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-proxy-object.cpp000664 001750 001750 00000146311 15164251010 046451 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-proxy-object.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-handlers.h" #include "ecma-builtin-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaproxyobject ECMA Proxy object related routines * @{ */ #if JERRY_BUILTIN_PROXY /** * ProxyCreate operation for create a new proxy object * * See also: * ES2015 9.5.15 * ES11+: 9.5.14 ProxyCreate * * @return created Proxy object as an ecma-value - if success * raised error - otherwise */ ecma_object_t * ecma_proxy_create (ecma_value_t target, /**< proxy target */ ecma_value_t handler, /**< proxy handler */ uint32_t options) /**< ecma_proxy_flag_types_t option bits */ { /* ES2015: 1, 3. */ /* ES11+: 1 - 2. */ if (!ecma_is_value_object (target) || !ecma_is_value_object (handler)) { ecma_raise_type_error (ECMA_ERR_CANNOT_CREATE_PROXY); return NULL; } /* ES2015: 5 - 6. */ /* ES11+: 3 - 4. */ /* A Proxy does not have [[Prototype]] value as per standard */ ecma_object_t *obj_p = ecma_create_object (NULL, sizeof (ecma_proxy_object_t), ECMA_OBJECT_TYPE_PROXY); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; obj_p->u2.prototype_cp = (jmem_cpointer_t) options; /* ES2015: 7. */ /* ES11+: 5. */ if (ecma_op_is_callable (target)) { obj_p->u2.prototype_cp |= ECMA_PROXY_IS_CALLABLE; /* ES2015: 7.b. */ /* ES11+: 5.b. */ if (ecma_is_constructor (target)) { obj_p->u2.prototype_cp |= ECMA_PROXY_IS_CONSTRUCTABLE; } } /* ES2015: 8. */ /* ES11+: 6. */ proxy_obj_p->target = target; /* ES2015: 9. */ /* ES11+: 7. */ proxy_obj_p->handler = handler; /* ES2015: 10. */ /* ES11+: 8 */ return obj_p; } /* ecma_proxy_create */ /** * Definition of Proxy Revocation Function * * See also: * ES2015 26.2.2.1.1 * * @return ECMA_VALUE_UNDEFINED */ ecma_value_t ecma_proxy_revoke_cb (ecma_object_t *function_obj_p, /**< function object */ const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { JERRY_UNUSED_2 (args_p, args_count); /* 1. */ ecma_revocable_proxy_object_t *rev_proxy_p = (ecma_revocable_proxy_object_t *) function_obj_p; /* 2. */ if (ecma_is_value_null (rev_proxy_p->proxy)) { return ECMA_VALUE_UNDEFINED; } /* 4. */ ecma_proxy_object_t *proxy_p = (ecma_proxy_object_t *) ecma_get_object_from_value (rev_proxy_p->proxy); JERRY_ASSERT (ECMA_OBJECT_IS_PROXY ((ecma_object_t *) proxy_p)); /* 3. */ rev_proxy_p->proxy = ECMA_VALUE_NULL; /* 5. */ proxy_p->target = ECMA_VALUE_NULL; /* 6. */ proxy_p->handler = ECMA_VALUE_NULL; /* 7. */ return ECMA_VALUE_UNDEFINED; } /* ecma_proxy_revoke_cb */ /** * Proxy.revocable operation for create a new revocable proxy object * * See also: * ES2015 26.2.2.1 * * @return NULL - if the operation fails * pointer to the newly created revocable proxy object - otherwise */ ecma_object_t * ecma_proxy_create_revocable (ecma_value_t target, /**< target argument */ ecma_value_t handler) /**< handler argument */ { /* 1. */ ecma_object_t *proxy_p = ecma_proxy_create (target, handler, 0); /* 2. */ if (proxy_p == NULL) { return proxy_p; } ecma_value_t proxy_value = ecma_make_object_value (proxy_p); /* 3. */ ecma_object_t *func_obj_p; func_obj_p = ecma_op_create_native_handler (ECMA_NATIVE_HANDLER_PROXY_REVOKE, sizeof (ecma_revocable_proxy_object_t)); /* 4. */ ecma_revocable_proxy_object_t *rev_proxy_p = (ecma_revocable_proxy_object_t *) func_obj_p; rev_proxy_p->proxy = proxy_value; ecma_property_value_t *prop_value_p; ecma_value_t revoker = ecma_make_object_value (func_obj_p); /* 5. */ ecma_object_t *obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE), 0, ECMA_OBJECT_TYPE_GENERAL); /* 6. */ prop_value_p = ecma_create_named_data_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_PROXY), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); prop_value_p->value = proxy_value; /* 7. */ prop_value_p = ecma_create_named_data_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_REVOKE), ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); prop_value_p->value = revoker; ecma_deref_object (proxy_p); ecma_deref_object (func_obj_p); /* 8. */ return obj_p; } /* ecma_proxy_create_revocable */ /** * Internal find property operation for Proxy object * * Note: Returned value must be freed with ecma_free_value. * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_NOT_FOUND - if the property is not found * value of the property - otherwise */ ecma_value_t ecma_proxy_object_find (ecma_object_t *obj_p, /**< proxy object */ ecma_string_t *prop_name_p) /**< property name */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ecma_value_t has_result = ecma_proxy_object_has (obj_p, prop_name_p); if (ECMA_IS_VALUE_ERROR (has_result)) { return has_result; } if (ecma_is_value_false (has_result)) { return ECMA_VALUE_NOT_FOUND; } return ecma_proxy_object_get (obj_p, prop_name_p, ecma_make_object_value (obj_p)); } /* ecma_proxy_object_find */ /** * Helper method for validate the proxy object * * @return proxy trap - if the validation is successful * ECMA_VALUE_ERROR - otherwise */ static ecma_value_t ecma_validate_proxy_object (ecma_value_t handler, /**< proxy handler */ lit_magic_string_id_t magic_id) /**< routine magic id */ { if (ecma_is_value_null (handler)) { return ecma_raise_type_error (ECMA_ERR_HANDLER_CANNOT_BE_NULL); } JERRY_ASSERT (ecma_is_value_object (handler)); return ecma_op_get_method_by_magic_id (handler, magic_id); } /* ecma_validate_proxy_object */ /* Internal operations */ /** * The Proxy object [[GetPrototypeOf]] internal routine * * See also: * ECMAScript v6, 9.5.1 * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_NULL or valid object (prototype) otherwise */ ecma_value_t ecma_proxy_object_get_prototype_of (ecma_object_t *obj_p) /**< proxy object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 1. */ ecma_value_t handler = proxy_obj_p->handler; /* 2-5. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_GET_PROTOTYPE_OF_UL); /* 6. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 7. */ if (ecma_is_value_undefined (trap)) { ecma_value_t result = ecma_builtin_object_object_get_prototype_of (target_obj_p); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); /* 8. */ ecma_value_t handler_proto = ecma_op_function_call (func_obj_p, handler, &target, 1); ecma_deref_object (func_obj_p); /* 9. */ if (ECMA_IS_VALUE_ERROR (handler_proto)) { return handler_proto; } /* 10. */ if (!ecma_is_value_object (handler_proto) && !ecma_is_value_null (handler_proto)) { ecma_free_value (handler_proto); return ecma_raise_type_error (ECMA_ERR_TRAP_RETURNED_NEITHER_OBJECT_NOR_NULL); } if (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION) { return handler_proto; } /* 11. */ ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); /* 12. */ if (ECMA_IS_VALUE_ERROR (extensible_target)) { ecma_free_value (handler_proto); return extensible_target; } /* 13. */ if (ecma_is_value_true (extensible_target)) { return handler_proto; } /* 14. */ ecma_value_t target_proto = ecma_builtin_object_object_get_prototype_of (target_obj_p); /* 15. */ if (ECMA_IS_VALUE_ERROR (target_proto)) { return target_proto; } ecma_value_t ret_value = handler_proto; /* 16. */ if (handler_proto != target_proto) { ecma_free_value (handler_proto); ret_value = ecma_raise_type_error (ECMA_ERR_TARGET_NOT_EXTENSIBLE_NOT_RETURNED_ITS_PROTOTYPE); } ecma_free_value (target_proto); /* 17. */ return ret_value; } /* ecma_proxy_object_get_prototype_of */ /** * The Proxy object [[SetPrototypeOf]] internal routine * * See also: * ECMAScript v6, 9.5.2 * ECMAScript v11: 9.5.2 * * Note: Returned value is always a simple value so freeing it is unnecessary. * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE/FALSE} - depends on whether the new prototype can be set for the given object */ ecma_value_t ecma_proxy_object_set_prototype_of (ecma_object_t *obj_p, /**< proxy object */ ecma_value_t proto) /**< new prototype object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); /* 1. */ JERRY_ASSERT (ecma_is_value_object (proto) || ecma_is_value_null (proto)); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 2. */ ecma_value_t handler = proxy_obj_p->handler; /* 3-6. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_SET_PROTOTYPE_OF_UL); /* 7.*/ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 8. */ if (ecma_is_value_undefined (trap)) { if (ECMA_OBJECT_IS_PROXY (target_obj_p)) { ecma_value_t result = ecma_proxy_object_set_prototype_of (target_obj_p, proto); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_value_t result = ecma_op_ordinary_object_set_prototype_of (target_obj_p, proto); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); ecma_value_t args[] = { target, proto }; /* 9. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2); ecma_deref_object (func_obj_p); /* 10. */ if (ECMA_IS_VALUE_ERROR (trap_result)) { return trap_result; } bool boolean_trap_result = ecma_op_to_boolean (trap_result); ecma_free_value (trap_result); /* ES11: 9 */ if (!boolean_trap_result) { return ecma_make_boolean_value (false); } if (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION) { return ecma_make_boolean_value (boolean_trap_result); } /* 11. */ ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); /* 12. */ if (ECMA_IS_VALUE_ERROR (extensible_target)) { return extensible_target; } /* 13. */ if (ecma_is_value_true (extensible_target)) { return ecma_make_boolean_value (boolean_trap_result); } /* 14. */ ecma_value_t target_proto = ecma_builtin_object_object_get_prototype_of (target_obj_p); /* 15. */ if (ECMA_IS_VALUE_ERROR (target_proto)) { return target_proto; } ecma_value_t ret_value = ecma_make_boolean_value (boolean_trap_result); /* 16. */ if (boolean_trap_result && (target_proto != proto)) { ret_value = ecma_raise_type_error (ECMA_ERR_TARGET_NOT_EXTENSIBLE_DIFFERENT_PROTOTYPE_RETURNED); } ecma_free_value (target_proto); /* 17. */ return ret_value; } /* ecma_proxy_object_set_prototype_of */ /** * The Proxy object [[isExtensible]] internal routine * * See also: * ECMAScript v6, 9.5.3 * * Note: Returned value is always a simple value so freeing it is unnecessary. * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE/FALSE} - depends on whether the object is extensible */ ecma_value_t ecma_proxy_object_is_extensible (ecma_object_t *obj_p) /**< proxy object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 1. */ ecma_value_t handler = proxy_obj_p->handler; /* 2-5. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_IS_EXTENSIBLE); /* 6. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 7. */ if (ecma_is_value_undefined (trap)) { ecma_value_t result = ecma_builtin_object_object_is_extensible (target_obj_p); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); /* 8. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, &target, 1); ecma_deref_object (func_obj_p); /* 9. */ if (ECMA_IS_VALUE_ERROR (trap_result)) { return trap_result; } bool boolean_trap_result = ecma_op_to_boolean (trap_result); ecma_free_value (trap_result); if (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION) { return ecma_make_boolean_value (boolean_trap_result); } bool target_result; /* 10. */ if (ECMA_OBJECT_IS_PROXY (target_obj_p)) { ecma_value_t proxy_is_ext = ecma_proxy_object_is_extensible (target_obj_p); if (ECMA_IS_VALUE_ERROR (proxy_is_ext)) { return proxy_is_ext; } target_result = ecma_is_value_true (proxy_is_ext); } else { target_result = ecma_op_ordinary_object_is_extensible (target_obj_p); } /* 12. */ if (boolean_trap_result != target_result) { return ecma_raise_type_error (ECMA_ERR_TRAP_RESULT_NOT_REFLECT_TARGET_EXTENSIBILITY); } return ecma_make_boolean_value (boolean_trap_result); } /* ecma_proxy_object_is_extensible */ /** * The Proxy object [[PreventExtensions]] internal routine * * See also: * ECMAScript v6, 9.5.4 * * Note: Returned value is always a simple value so freeing it is unnecessary. * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE/FALSE} - depends on whether the object can be set as inextensible */ ecma_value_t ecma_proxy_object_prevent_extensions (ecma_object_t *obj_p) /**< proxy object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 1. */ ecma_value_t handler = proxy_obj_p->handler; /* 2-5. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_PREVENT_EXTENSIONS_UL); /* 6. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 7. */ if (ecma_is_value_undefined (trap)) { ecma_value_t ret_value = ecma_builtin_object_object_prevent_extensions (target_obj_p); if (ECMA_IS_VALUE_ERROR (ret_value)) { return ret_value; } ecma_deref_object (target_obj_p); return ECMA_VALUE_TRUE; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); /* 8. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, &target, 1); ecma_deref_object (func_obj_p); /* 9. */ if (ECMA_IS_VALUE_ERROR (trap_result)) { return trap_result; } bool boolean_trap_result = ecma_op_to_boolean (trap_result); ecma_free_value (trap_result); /* 10. */ if (boolean_trap_result && !(obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION)) { ecma_value_t target_is_ext = ecma_builtin_object_object_is_extensible (target_obj_p); if (ECMA_IS_VALUE_ERROR (target_is_ext)) { return target_is_ext; } if (ecma_is_value_true (target_is_ext)) { return ecma_raise_type_error (ECMA_ERR_TRAP_RESULT_NOT_REFLECT_TARGET_INEXTENSIBILITY); } } /* 11. */ return ecma_make_boolean_value (boolean_trap_result); } /* ecma_proxy_object_prevent_extensions */ /** * The Proxy object [[GetOwnProperty]] internal routine * * See also: * ECMAScript v6, 9.5.5 * * Note: - Returned value is always a simple value so freeing it is unnecessary. * - If the operation does not fail, freeing the filled property descriptor is the caller's responsibility * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE_FALSE} - depends on whether object has property with the given name */ ecma_value_t ecma_proxy_object_get_own_property_descriptor (ecma_object_t *obj_p, /**< proxy object */ ecma_string_t *prop_name_p, /**< property name */ ecma_property_descriptor_t *prop_desc_p) /**< [out] property * descriptor */ { ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 2. */ ecma_value_t handler = proxy_obj_p->handler; /* 3-6. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL); ecma_value_t target = proxy_obj_p->target; /* 7. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 8. */ if (ecma_is_value_undefined (trap)) { ecma_value_t result = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, prop_desc_p); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p); ecma_value_t args[] = { target, prop_value }; /* 9. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2); ecma_deref_object (func_obj_p); /* 10. */ if (ECMA_IS_VALUE_ERROR (trap_result)) { return trap_result; } /* 11. */ if (!ecma_is_value_object (trap_result) && !ecma_is_value_undefined (trap_result)) { ecma_free_value (trap_result); return ecma_raise_type_error (ECMA_ERR_TRAP_IS_NEITHER_AN_OBJECT_NOR_UNDEFINED); } if (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION) { if (ecma_is_value_undefined (trap_result)) { return ECMA_VALUE_FALSE; } ecma_value_t result_val = ecma_op_to_property_descriptor (trap_result, prop_desc_p); ecma_free_value (trap_result); if (ECMA_IS_VALUE_ERROR (result_val)) { return result_val; } ecma_op_to_complete_property_descriptor (prop_desc_p); return ECMA_VALUE_TRUE; } /* 12. */ ecma_property_descriptor_t target_desc; ecma_value_t target_status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc); /* 13. */ if (ECMA_IS_VALUE_ERROR (target_status)) { ecma_free_value (trap_result); return target_status; } /* 14. */ if (ecma_is_value_undefined (trap_result)) { /* .a */ if (ecma_is_value_false (target_status)) { return ECMA_VALUE_FALSE; } /* .b */ if (!(target_desc.flags & JERRY_PROP_IS_CONFIGURABLE)) { ecma_free_property_descriptor (&target_desc); return ecma_raise_type_error (ECMA_ERR_GIVEN_PROPERTY_IS_A_NON_CONFIGURABLE); } /* .c */ ecma_free_property_descriptor (&target_desc); ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); /* .d */ if (ECMA_IS_VALUE_ERROR (extensible_target)) { return extensible_target; } /* .e */ JERRY_ASSERT (ecma_is_value_boolean (extensible_target)); /* .f */ if (ecma_is_value_false (extensible_target)) { return ecma_raise_type_error (ECMA_ERR_TARGET_NOT_EXTENSIBLE); } /* .g */ return ECMA_VALUE_FALSE; } /* 15. */ ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); /* 16. */ if (ECMA_IS_VALUE_ERROR (extensible_target)) { if (ecma_is_value_true (target_status)) { ecma_free_property_descriptor (&target_desc); } ecma_free_value (trap_result); return extensible_target; } /* 17, 19 */ ecma_value_t result_val = ecma_op_to_property_descriptor (trap_result, prop_desc_p); ecma_op_to_complete_property_descriptor (prop_desc_p); ecma_free_value (trap_result); /* 18. */ if (ECMA_IS_VALUE_ERROR (result_val)) { if (ecma_is_value_true (target_status)) { ecma_free_property_descriptor (&target_desc); } return result_val; } /* 20. */ bool is_extensible = ecma_is_value_true (extensible_target); bool is_valid = ecma_op_is_compatible_property_descriptor (prop_desc_p, (ecma_is_value_true (target_status) ? &target_desc : NULL), is_extensible); bool target_has_desc = ecma_is_value_true (target_status); bool target_is_writable = (target_desc.flags & JERRY_PROP_IS_WRITABLE); bool target_is_configurable = false; if (target_has_desc) { target_is_configurable = ((target_desc.flags & JERRY_PROP_IS_CONFIGURABLE) != 0); ecma_free_property_descriptor (&target_desc); } /* 21. */ if (!is_valid) { ecma_free_property_descriptor (prop_desc_p); return ecma_raise_type_error (ECMA_ERR_THE_TWO_DESCRIPTORS_ARE_INCOMPATIBLE); } /* 22. */ else if (!(prop_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE)) { const uint16_t mask = (JERRY_PROP_IS_WRITABLE_DEFINED | JERRY_PROP_IS_WRITABLE); if (!target_has_desc || target_is_configurable || ((prop_desc_p->flags & mask) == JERRY_PROP_IS_WRITABLE_DEFINED && target_is_writable)) { ecma_free_property_descriptor (prop_desc_p); return ecma_raise_type_error (ECMA_ERR_THE_TWO_DESCRIPTORS_ARE_INCOMPATIBLE); } } return ECMA_VALUE_TRUE; } /* ecma_proxy_object_get_own_property_descriptor */ /** * The Proxy object [[DefineOwnProperty]] internal routine * * See also: * ECMAScript v6, 9.5.6 * * Note: Returned value is always a simple value so freeing it is unnecessary. * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE_FALSE} - depends on whether the property can be defined for the given object */ ecma_value_t ecma_proxy_object_define_own_property (ecma_object_t *obj_p, /**< proxy object */ ecma_string_t *prop_name_p, /**< property name */ const ecma_property_descriptor_t *prop_desc_p) /**< property descriptor */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 2. */ ecma_value_t handler = proxy_obj_p->handler; /* 3-6. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_DEFINE_PROPERTY_UL); /* 7. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 8. */ if (ecma_is_value_undefined (trap)) { ecma_value_t result = ecma_op_object_define_own_property (target_obj_p, prop_name_p, prop_desc_p); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } /* 9. */ ecma_object_t *desc_obj = ecma_op_from_property_descriptor (prop_desc_p); ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p); ecma_value_t desc_obj_value = ecma_make_object_value (desc_obj); ecma_value_t args[] = { target, prop_value, desc_obj_value }; /* 10. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 3); ecma_deref_object (func_obj_p); ecma_deref_object (desc_obj); /* 11. */ if (ECMA_IS_VALUE_ERROR (trap_result)) { return trap_result; } bool boolean_trap_result = ecma_op_to_boolean (trap_result); ecma_free_value (trap_result); /* 12. */ if (!boolean_trap_result) { return ECMA_VALUE_FALSE; } if (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION) { return ECMA_VALUE_TRUE; } /* 13. */ ecma_property_descriptor_t target_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc); /* 14. */ if (ECMA_IS_VALUE_ERROR (status)) { return status; } bool target_prop_found = ecma_is_value_true (status); /* 15. */ ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); bool is_target_ext = ecma_is_value_true (extensible_target); /* 16. */ if (ECMA_IS_VALUE_ERROR (extensible_target)) { if (target_prop_found) { ecma_free_property_descriptor (&target_desc); } return extensible_target; } /* 17. */ bool setting_config_false = ((prop_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE_DEFINED) && !(prop_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE)); /* 19. */ if (!target_prop_found) { if (!is_target_ext) { return ecma_raise_type_error (ECMA_ERR_TRAP_TRUISH_ADDING_PROPERTY_NON_EXTENSIBLE_TARGET); } if (setting_config_false) { return ecma_raise_type_error (ECMA_ERR_TRAP_TRUISH_DEFINING_NON_EXISTENT_PROPERTY); } } /* 20. */ else { ecma_value_t ret_value = ECMA_VALUE_EMPTY; if (!ecma_op_is_compatible_property_descriptor (prop_desc_p, &target_desc, is_target_ext)) { ret_value = ecma_raise_type_error (ECMA_ERR_TRAP_TRUISH_ADD_PROPERTY_INCOMPATIBLE_OTHER_PROP); } else if (setting_config_false && (target_desc.flags & JERRY_PROP_IS_CONFIGURABLE)) { ret_value = ecma_raise_type_error (ECMA_ERR_TRAP_TRUISH_DEFINING_NON_EXISTENT_PROPERTY); } /* ES11: 16.c */ else if ((target_desc.flags & (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE_DEFINED)) != 0 && (prop_desc_p->flags & (JERRY_PROP_IS_WRITABLE_DEFINED | JERRY_PROP_IS_WRITABLE)) == JERRY_PROP_IS_WRITABLE_DEFINED && (target_desc.flags & (JERRY_PROP_IS_WRITABLE | JERRY_PROP_IS_CONFIGURABLE)) == JERRY_PROP_IS_WRITABLE) { ret_value = ecma_raise_type_error (ECMA_ERR_TRAP_TRUISH_DEFINING_NON_EXISTENT_PROPERTY); } ecma_free_property_descriptor (&target_desc); if (ECMA_IS_VALUE_ERROR (ret_value)) { return ret_value; } } return ECMA_VALUE_TRUE; } /* ecma_proxy_object_define_own_property */ /** * The Proxy object [[HasProperty]] internal routine * * See also: * ECMAScript v6, 9.5.7 * * Note: Returned value is always a simple value so freeing it is unnecessary. * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE_FALSE} - depends on whether the property is found */ ecma_value_t ecma_proxy_object_has (ecma_object_t *obj_p, /**< proxy object */ ecma_string_t *prop_name_p) /**< property name */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 2. */ ecma_value_t handler = proxy_obj_p->handler; /* 3-6. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_HAS); /* 7. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 8. */ if (ecma_is_value_undefined (trap)) { ecma_value_t result = ecma_op_object_has_property (target_obj_p, prop_name_p); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p); ecma_value_t args[] = { target, prop_value }; /* 9. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2); ecma_deref_object (func_obj_p); /* 10. */ if (ECMA_IS_VALUE_ERROR (trap_result)) { return trap_result; } bool boolean_trap_result = ecma_op_to_boolean (trap_result); ecma_free_value (trap_result); /* 11. */ if (!boolean_trap_result && !(obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION)) { ecma_property_descriptor_t target_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc); if (ECMA_IS_VALUE_ERROR (status)) { return status; } if (ecma_is_value_true (status)) { bool prop_is_configurable = target_desc.flags & JERRY_PROP_IS_CONFIGURABLE; ecma_free_property_descriptor (&target_desc); if (!prop_is_configurable) { return ecma_raise_type_error (ECMA_ERR_TRAP_FALSISH_PROPERTY_NON_CONFIGURABLE); } ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); if (ECMA_IS_VALUE_ERROR (extensible_target)) { return extensible_target; } if (ecma_is_value_false (extensible_target)) { return ecma_raise_type_error (ECMA_ERR_TRAP_FALSISH_PROPERTY_TARGET_NOT_EXTENSIBLE); } } } /* 12. */ return ecma_make_boolean_value (boolean_trap_result); } /* ecma_proxy_object_has */ /** * The Proxy object [[Get]] internal routine * * See also: * ECMAScript v6, 9.5.8 * * Note: Returned value is always a simple value so freeing it is unnecessary. * * @return ECMA_VALUE_ERROR - if the operation fails * value of the given named data property or the result of the getter function call - otherwise */ ecma_value_t ecma_proxy_object_get (ecma_object_t *obj_p, /**< proxy object */ ecma_string_t *prop_name_p, /**< property name */ ecma_value_t receiver) /**< receiver to invoke getter function */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 2. */ ecma_value_t handler = proxy_obj_p->handler; /* 3-6. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_GET); /* 7. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } /* 8. */ if (ecma_is_value_undefined (trap)) { ecma_object_t *target_obj_p = ecma_get_object_from_value (proxy_obj_p->target); ecma_value_t result = ecma_op_object_get_with_receiver (target_obj_p, prop_name_p, receiver); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p); ecma_value_t args[] = { proxy_obj_p->target, prop_value, receiver }; /* 9. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 3); ecma_deref_object (func_obj_p); /* 10. */ if (ECMA_IS_VALUE_ERROR (trap_result) || (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION)) { return trap_result; } /* 11. */ ecma_property_descriptor_t target_desc; ecma_value_t status = ecma_op_get_own_property_descriptor (proxy_obj_p->target, prop_name_p, &target_desc); /* 12. */ if (ECMA_IS_VALUE_ERROR (status)) { ecma_free_value (trap_result); return status; } /* 13. */ if (ecma_is_value_true (status)) { ecma_value_t ret_value = ECMA_VALUE_EMPTY; if ((target_desc.flags & JERRY_PROP_IS_VALUE_DEFINED) && !(target_desc.flags & JERRY_PROP_IS_CONFIGURABLE) && !(target_desc.flags & JERRY_PROP_IS_WRITABLE) && !ecma_op_same_value (trap_result, target_desc.value)) { ret_value = ecma_raise_type_error (ECMA_ERR_INCORRECT_RETURN_PROXY_GET_TRAP); } else if (!(target_desc.flags & JERRY_PROP_IS_CONFIGURABLE) && (target_desc.flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) && target_desc.get_p == NULL && !ecma_is_value_undefined (trap_result)) { ret_value = ecma_raise_type_error (ECMA_ERR_PROXY_PROPERTY_NOT_CONFIGURABLE_NOT_HAVE_GETTER); } ecma_free_property_descriptor (&target_desc); if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_free_value (trap_result); return ret_value; } } /* 14. */ return trap_result; } /* ecma_proxy_object_get */ /** * The Proxy object [[Set]] internal routine * * See also: * ECMAScript v6, 9.5.9 * * Note: Returned value is always a simple value so freeing it is unnecessary. * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE/FALSE} - depends on whether the property can be set to the given object */ ecma_value_t ecma_proxy_object_set (ecma_object_t *obj_p, /**< proxy object */ ecma_string_t *prop_name_p, /**< property name */ ecma_value_t value, /**< value to set */ ecma_value_t receiver, /**< receiver to invoke setter function */ bool is_strict) /**< indicate strict mode */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 2. */ ecma_value_t handler = proxy_obj_p->handler; /* 3-6. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_SET); /* 7. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 8. */ if (ecma_is_value_undefined (trap)) { ecma_value_t result = ecma_op_object_put_with_receiver (target_obj_p, prop_name_p, value, receiver, is_strict); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); ecma_value_t prop_name_value = ecma_make_prop_name_value (prop_name_p); ecma_value_t args[] = { target, prop_name_value, value, receiver }; /* 9. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 4); ecma_deref_object (func_obj_p); /* 10. */ if (ECMA_IS_VALUE_ERROR (trap_result)) { return trap_result; } bool boolean_trap_result = ecma_op_to_boolean (trap_result); ecma_free_value (trap_result); /* 11. */ if (!boolean_trap_result) { if (is_strict) { return ecma_raise_type_error (ECMA_ERR_PROXY_TRAP_RETURNED_FALSISH); } return ECMA_VALUE_FALSE; } if (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION) { return ECMA_VALUE_TRUE; } /* 12. */ ecma_property_descriptor_t target_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc); /* 13. */ if (ECMA_IS_VALUE_ERROR (status)) { return status; } /* 14. */ if (ecma_is_value_true (status)) { ecma_value_t ret_value = ECMA_VALUE_EMPTY; if ((target_desc.flags & JERRY_PROP_IS_VALUE_DEFINED) && !(target_desc.flags & JERRY_PROP_IS_CONFIGURABLE) && !(target_desc.flags & JERRY_PROP_IS_WRITABLE) && !ecma_op_same_value (value, target_desc.value)) { ret_value = ecma_raise_type_error (ECMA_ERR_INCORRECT_RETURN_PROXY_SET_TRAP); } else if (!(target_desc.flags & JERRY_PROP_IS_CONFIGURABLE) && (target_desc.flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) && target_desc.set_p == NULL) { ret_value = ecma_raise_type_error (ECMA_ERR_TARGET_PROPERTY_CONFIGURE_ACCESSOR_WITHOUT_SETTER); } ecma_free_property_descriptor (&target_desc); if (ECMA_IS_VALUE_ERROR (ret_value)) { return ret_value; } } /* 15. */ return ECMA_VALUE_TRUE; } /* ecma_proxy_object_set */ /** * The Proxy object [[Delete]] internal routine * * See also: * ECMAScript v6, 9.5.10 * * Note: Returned value is always a simple value so freeing it is unnecessary. * * @return ECMA_VALUE_ERROR - if the operation fails * ECMA_VALUE_{TRUE/FALSE} - depends on whether the property can be deleted */ ecma_value_t ecma_proxy_object_delete_property (ecma_object_t *obj_p, /**< proxy object */ ecma_string_t *prop_name_p, /**< property name */ bool is_strict) /**< delete in strict mode? */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 2. */ ecma_value_t handler = proxy_obj_p->handler; /* 3-6.*/ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_DELETE_PROPERTY_UL); /* 7. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 8. */ if (ecma_is_value_undefined (trap)) { ecma_value_t result = ecma_op_object_delete (target_obj_p, prop_name_p, is_strict); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); ecma_value_t prop_name_value = ecma_make_prop_name_value (prop_name_p); ecma_value_t args[] = { target, prop_name_value }; /* 9. */ ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2); ecma_deref_object (func_obj_p); /* 10. */ if (ECMA_IS_VALUE_ERROR (trap_result)) { return trap_result; } bool boolean_trap_result = ecma_op_to_boolean (trap_result); ecma_free_value (trap_result); /* 11. */ if (!boolean_trap_result) { return ECMA_VALUE_FALSE; } if (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION) { return ECMA_VALUE_TRUE; } /* 12. */ ecma_property_descriptor_t target_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc); /* 13. */ if (ECMA_IS_VALUE_ERROR (status)) { return status; } /* 14. */ if (ecma_is_value_false (status)) { return ECMA_VALUE_TRUE; } ecma_value_t ret_value = ECMA_VALUE_TRUE; /* 15. */ if (!(target_desc.flags & JERRY_PROP_IS_CONFIGURABLE)) { ret_value = ecma_raise_type_error (ECMA_ERR_TRAP_TRUISH_PROPERTY_NON_CONFIGURABLE); } /* ES11: 13-14 */ ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); if (!ecma_is_value_true (extensible_target)) { ret_value = ecma_raise_type_error (ECMA_ERR_TRAP_TRUISH_TARGET_NOT_EXTENSIBLE); } ecma_free_property_descriptor (&target_desc); /* 16. */ return ret_value; } /* ecma_proxy_object_delete_property */ /** * Helper method for the Proxy object [[OwnPropertyKeys]] operation * * See also: * ECMAScript v6, 9.5.12 steps 21. 23. * * @return ECMA_VALUE_ERROR - if a target key is not in the unchecked_result_keys collection * ECMA_VALUE_EMPTY - otherwise */ static ecma_value_t ecma_proxy_object_own_property_keys_helper (ecma_collection_t *target_collection, /**< target keys */ ecma_collection_t *unchecked_result_keys, /**< unchecked keys */ uint32_t *counter) /**< unchecked property counter */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; for (uint32_t i = 0; i < target_collection->item_count; i++) { ecma_string_t *current_prop_name = ecma_get_prop_name_from_value (target_collection->buffer_p[i]); ret_value = ECMA_VALUE_ERROR; for (uint32_t j = 0; j < unchecked_result_keys->item_count; j++) { if (ecma_is_value_empty (unchecked_result_keys->buffer_p[j])) { continue; } ecma_string_t *unchecked_prop_name = ecma_get_prop_name_from_value (unchecked_result_keys->buffer_p[j]); if (ecma_compare_ecma_strings (current_prop_name, unchecked_prop_name)) { ecma_deref_ecma_string (unchecked_prop_name); ret_value = ECMA_VALUE_EMPTY; unchecked_result_keys->buffer_p[j] = ECMA_VALUE_EMPTY; (*counter)++; } } if (ECMA_IS_VALUE_ERROR (ret_value)) { break; } } return ret_value; } /* ecma_proxy_object_own_property_keys_helper */ /** * Helper method for checking the invariants in the Proxy object [[OwnPropertyKeys]] operation * * See also: * ECMAScript v6, 9.5.12 steps 20-25. * * @return true - if none of the invariants got violated * false - otherwise */ static bool ecma_proxy_check_invariants_for_own_prop_keys (ecma_collection_t *trap_result, ecma_collection_t *target_non_configurable_keys, ecma_collection_t *target_configurable_keys, ecma_value_t extensible_target) { /* 20. */ ecma_collection_t *unchecked_result_keys = ecma_new_collection (); ecma_collection_append (unchecked_result_keys, trap_result->buffer_p, trap_result->item_count); for (uint32_t i = 0; i < unchecked_result_keys->item_count; i++) { ecma_string_t *unchecked_prop_name = ecma_get_prop_name_from_value (unchecked_result_keys->buffer_p[i]); ecma_ref_ecma_string (unchecked_prop_name); } bool check_ok = false; uint32_t unchecked_prop_name_counter = 0; /* 21. */ if (ECMA_IS_VALUE_ERROR (ecma_proxy_object_own_property_keys_helper (target_non_configurable_keys, unchecked_result_keys, &unchecked_prop_name_counter))) { ecma_raise_type_error (ECMA_ERR_TRAP_RESULT_NOT_INCLUDE_ALL_NON_CONFIGURABLE_KEYS); } /* 22. */ else if (ecma_is_value_true (extensible_target)) { check_ok = true; } /* 23. */ else if (ECMA_IS_VALUE_ERROR (ecma_proxy_object_own_property_keys_helper (target_configurable_keys, unchecked_result_keys, &unchecked_prop_name_counter))) { ecma_raise_type_error (ECMA_ERR_TRAP_RESULT_NOT_INCLUDE_ALL_CONFIGURABLE_KEYS); } /* 24. */ else if (unchecked_result_keys->item_count != unchecked_prop_name_counter) { ecma_raise_type_error (ECMA_ERR_TRAP_EXTRA_KEYS_FOR_A_NON_EXTENSIBLE_TARGET); } /* 25. */ else { check_ok = true; } ecma_collection_free (unchecked_result_keys); return check_ok; } /* ecma_proxy_check_invariants_for_own_prop_keys */ /** * The Proxy object [[OwnPropertyKeys]] internal routine * * See also: * ECMAScript v11, 9.5.11 * * Note: If the returned collection is not NULL, it must be freed with * ecma_collection_free if it is no longer needed * * @return NULL - if the operation fails * pointer to a newly allocated list of property names - otherwise */ ecma_collection_t * ecma_proxy_object_own_property_keys (ecma_object_t *obj_p) /**< proxy object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE_RETURN (NULL); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 1. */ ecma_value_t handler = proxy_obj_p->handler; /* 2-5. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_OWN_KEYS_UL); if (ECMA_IS_VALUE_ERROR (trap)) { return NULL; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 6. */ if (ecma_is_value_undefined (trap)) { ecma_collection_t *result = ecma_op_object_own_property_keys (target_obj_p, JERRY_PROPERTY_FILTER_ALL); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); /* 7. */ ecma_value_t trap_result_array = ecma_op_function_call (func_obj_p, handler, &target, 1); ecma_deref_object (func_obj_p); if (ECMA_IS_VALUE_ERROR (trap_result_array)) { return NULL; } /* 8. */ ecma_collection_t *trap_result = ecma_op_create_list_from_array_like (trap_result_array, true); ecma_free_value (trap_result_array); if (trap_result == NULL || (obj_p->u2.prototype_cp & JERRY_PROXY_SKIP_RESULT_VALIDATION)) { return trap_result; } /* 9. */ if (ecma_collection_check_duplicated_entries (trap_result)) { ecma_collection_free (trap_result); ecma_raise_type_error (ECMA_ERR_TRAP_WITH_DUPLICATED_ENTRIES); return NULL; } /* 10. */ ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); if (ECMA_IS_VALUE_ERROR (extensible_target)) { ecma_collection_free (trap_result); return NULL; } /* 11. */ ecma_collection_t *target_keys = ecma_op_object_own_property_keys (target_obj_p, JERRY_PROPERTY_FILTER_ALL); if (target_keys == NULL) { ecma_collection_free (trap_result); return target_keys; } /* 14. */ ecma_collection_t *target_configurable_keys = ecma_new_collection (); /* 15. */ ecma_collection_t *target_non_configurable_keys = ecma_new_collection (); ecma_collection_t *ret_value = NULL; /* 16. */ for (uint32_t i = 0; i < target_keys->item_count; i++) { ecma_property_descriptor_t target_desc; ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (target_keys->buffer_p[i]); ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc); if (ECMA_IS_VALUE_ERROR (status)) { ecma_collection_free (trap_result); goto free_target_collections; } ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p); if (ecma_is_value_true (status) && !(target_desc.flags & JERRY_PROP_IS_CONFIGURABLE)) { ecma_collection_push_back (target_non_configurable_keys, prop_value); } else { ecma_collection_push_back (target_configurable_keys, prop_value); } if (ecma_is_value_true (status)) { ecma_free_property_descriptor (&target_desc); } } /* 17. */ if (ecma_is_value_true (extensible_target) && target_non_configurable_keys->item_count == 0) { ret_value = trap_result; } /* 18-22. */ else if (ecma_proxy_check_invariants_for_own_prop_keys (trap_result, target_non_configurable_keys, target_configurable_keys, extensible_target)) { ret_value = trap_result; } else { JERRY_ASSERT (ret_value == NULL); ecma_collection_free (trap_result); } free_target_collections: ecma_collection_destroy (target_keys); ecma_collection_free (target_configurable_keys); ecma_collection_free (target_non_configurable_keys); /* 23. */ return ret_value; } /* ecma_proxy_object_own_property_keys */ /** * The Proxy object [[Call]] internal routine * * See also: * ECMAScript v6, 9.5.13 * * Note: Returned value must be freed with ecma_free_value. * * @return ECMA_VALUE_ERROR - if the operation fails * result of the function call - otherwise */ ecma_value_t ecma_proxy_object_call (ecma_object_t *obj_p, /**< proxy object */ ecma_value_t this_argument, /**< this argument to invoke the function */ const ecma_value_t *args_p, /**< argument list */ uint32_t argc) /**< number of arguments */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); if (!ecma_op_proxy_object_is_callable (obj_p)) { return ecma_raise_type_error (ECMA_ERR_EXPECTED_A_FUNCTION); } ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 1. */ ecma_value_t handler = proxy_obj_p->handler; /* 2-5.*/ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_APPLY); /* 6. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; /* 7. */ if (ecma_is_value_undefined (trap)) { ecma_object_t *target_obj_p = ecma_get_object_from_value (target); ecma_value_t result = ecma_op_function_call (target_obj_p, this_argument, args_p, argc); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } /* 8. */ ecma_value_t args_array = ecma_op_new_array_object_from_buffer (args_p, argc); ecma_value_t value_array[] = { target, this_argument, args_array }; ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); /* 9. */ ecma_value_t ret_value = ecma_op_function_call (func_obj_p, handler, value_array, 3); ecma_deref_object (func_obj_p); ecma_free_object (args_array); return ret_value; } /* ecma_proxy_object_call */ /** * The Proxy object [[Construct]] internal routine * * See also: * ECMAScript v6, 9.5.14 * * Note: Returned value must be freed with ecma_free_value. * * @return ECMA_VALUE_ERROR - if the operation fails * result of the construct call - otherwise */ ecma_value_t ecma_proxy_object_construct (ecma_object_t *obj_p, /**< proxy object */ ecma_object_t *new_target_p, /**< new target */ const ecma_value_t *args_p, /**< argument list */ uint32_t argc) /**< number of arguments */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); ECMA_CHECK_STACK_USAGE (); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; /* 1. */ ecma_value_t handler = proxy_obj_p->handler; /* 2-5. */ ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_CONSTRUCT); /* 6. */ if (ECMA_IS_VALUE_ERROR (trap)) { return trap; } ecma_value_t target = proxy_obj_p->target; ecma_object_t *target_obj_p = ecma_get_object_from_value (target); /* 7. */ if (ecma_is_value_undefined (trap)) { JERRY_ASSERT (ecma_object_is_constructor (target_obj_p)); ecma_value_t result = ecma_op_function_construct (target_obj_p, new_target_p, args_p, argc); JERRY_BLOCK_TAIL_CALL_OPTIMIZATION (); return result; } /* 8. */ ecma_value_t args_array = ecma_op_new_array_object_from_buffer (args_p, argc); ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); ecma_value_t new_target_value = ecma_make_object_value (new_target_p); ecma_value_t function_call_args[] = { target, args_array, new_target_value }; /* 9. */ ecma_value_t new_obj = ecma_op_function_call (func_obj_p, handler, function_call_args, 3); ecma_free_object (args_array); ecma_deref_object (func_obj_p); /* 10 .*/ if (ECMA_IS_VALUE_ERROR (new_obj)) { return new_obj; } /* 11. */ if (!ecma_is_value_object (new_obj)) { ecma_free_value (new_obj); return ecma_raise_type_error (ECMA_ERR_TRAP_MUST_RETURN_WITH_AN_OBJECT); } /* 12. */ return new_obj; } /* ecma_proxy_object_construct */ #endif /* JERRY_BUILTIN_PROXY */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/meson.build000664 001750 001750 00000001533 15164251010 033601 0ustar00ddennedyddennedy000000 000000 subloader_dep = [] if svg_loader subdir('svg') endif if ttf_loader subdir('ttf') endif if lottie_loader subdir('lottie') endif if png_loader if get_option('static') subdir('png') else subdir('external_png') if not png_dep.found() subdir('png') endif endif endif if jpg_loader if get_option('static') subdir('jpg') else subdir('external_jpg') if not jpg_dep.found() subdir('jpg') endif endif endif if webp_loader if get_option('static') subdir('webp') else subdir('external_webp') if not webp_dep.found() subdir('webp') endif endif endif subdir('raw') loader_dep = declare_dependency( dependencies: subloader_dep, include_directories : include_directories('.'), ) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/jpg/000775 001750 001750 00000000000 15164251010 032215 5ustar00ddennedyddennedy000000 000000 thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-string.inc.h000664 001750 001750 00000003272 15164251010 050123 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * String built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_STRING /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.7.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_STRING_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_STRING_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_FROM_CHAR_CODE_UL, ECMA_BUILTIN_STRING_OBJECT_FROM_CHAR_CODE, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_FROM_CODE_POINT_UL, ECMA_BUILTIN_STRING_OBJECT_FROM_CODE_POINT, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_RAW, ECMA_BUILTIN_STRING_OBJECT_RAW, NON_FIXED, 1) #endif /* JERRY_BUILTIN_STRING */ #include "ecma-builtin-helpers-macro-undefs.inc.h" loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint8array.inc.h000664 001750 001750 00000001711 15164251010 053103 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Uint8Array description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 1 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_UINT8_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_UINT8ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieModifier.h000664 001750 001750 00000007257 15164251010 036502 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_MODIFIER_H_ #define _TVG_LOTTIE_MODIFIER_H_ #include "tvgCommon.h" #include "tvgArray.h" #include "tvgMath.h" #include "tvgRender.h" struct LottieModifier { enum Type : uint8_t {Roundness = 0, Offset}; LottieModifier* next = nullptr; Type type; virtual ~LottieModifier() {} virtual bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) = 0; virtual bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) = 0; LottieModifier* decorate(LottieModifier* next) { /* TODO: build the decorative chaining here. currently we only have roundness and offset. */ //roundness -> offset if (next->type == Roundness) { next->next = this; return next; } //just in the order. this->next = next; return this; } }; struct LottieRoundnessModifier : LottieModifier { static constexpr float ROUNDNESS_EPSILON = 1.0f; RenderPath* buffer; float r; LottieRoundnessModifier(RenderPath* buffer, float r) : buffer(buffer), r(r) { type = Roundness; } bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) override; bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) override; bool modifyRect(Point& size, float& r); }; struct LottieOffsetModifier : LottieModifier { float offset; float miterLimit; StrokeJoin join; LottieOffsetModifier(float offset, float miter = 4.0f, StrokeJoin join = StrokeJoin::Round) : offset(offset), miterLimit(miter), join(join) { type = Offset; } bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) override; bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) override; bool modifyRect(RenderPath& in, RenderPath& out); bool modifyEllipse(Point& radius); private: struct State { Line line{}; Line firstLine{}; bool moveto = false; uint32_t movetoOutIndex = 0; uint32_t movetoInIndex = 0; }; void line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated); void corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoIndex, bool nextClose); }; #endifsrc/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlTessellator.h000664 001750 001750 00000005747 15164251010 037155 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_TESSELLATOR_H_ #define _TVG_GL_TESSELLATOR_H_ #include "tvgGlCommon.h" namespace tvg { class Stroker { struct State { Point firstPt; Point firstPtDir; Point prevPt; Point prevPtDir; }; public: Stroker(GlGeometryBuffer* buffer, float strokeWidth, StrokeCap cap, StrokeJoin join, float miterLimit = 4.0f); void run(const RenderPath& path); RenderRegion bounds() const; private: float radius() const { return mWidth * 0.5f; } void cap(); void lineTo(const Point& curr); void cubicTo(const Point& cnt1, const Point& cnt2, const Point& end); void close(); void join(const Point& dir); void round(const Point& prev, const Point& curr, const Point& center); void miter(const Point& prev, const Point& curr, const Point& center); void bevel(const Point& prev, const Point& curr, const Point& center); void square(const Point& p, const Point& outDir); void squarePoint(const Point& p); void round(const Point& p, const Point& outDir); void roundPoint(const Point& p); GlGeometryBuffer* mBuffer; float mWidth = 0.0f; float mMiterLimit = 4.f; StrokeCap mCap = StrokeCap::Square; StrokeJoin mJoin = StrokeJoin::Bevel; State mState = {}; Point mLeftTop = {0.0f, 0.0f}; Point mRightBottom = {0.0f, 0.0f}; }; class BWTessellator { public: BWTessellator(GlGeometryBuffer* buffer); void tessellate(const RenderPath& path); RenderRegion bounds() const; bool convex = true; private: uint32_t pushVertex(float x, float y); void pushTriangle(uint32_t a, uint32_t b, uint32_t c); GlGeometryBuffer* mBuffer; BBox bbox = {}; Point firstPt = {}; Point prevPt = {}; Point prevEdge = {}; int8_t winding = -1; //0: unknown, 1: CW, -1: CCW }; } // namespace tvg #endif /* _TVG_GL_TESSELLATOR_H_ */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/jpg/meson.build000664 001750 001750 00000000330 15164251010 034353 0ustar00ddennedyddennedy000000 000000 source_file = [ 'tvgJpgd.h', 'tvgJpgLoader.h', 'tvgJpgd.cpp', 'tvgJpgLoader.cpp', ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/cross/ios_arm64.txt000664 001750 001750 00000001127 15164251010 032713 0ustar00ddennedyddennedy000000 000000 # build for the ios devices (Apple Silicon) [binaries] cpp = ['clang++', '-arch', 'arm64', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] ar = 'ar' strip = 'strip' [properties] root = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer' has_function_printf = true [built-in options] cpp_args = ['-miphoneos-version-min=11.0'] cpp_link_args = ['-miphoneos-version-min=11.0'] [host_machine] system = 'darwin' subsystem = 'ios' kernel = 'xnu' cpu_family = 'aarch64' cpu = 'aarch64' endian = 'little' mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/meson.build000664 001750 001750 00000000231 15164251010 032513 0ustar00ddennedyddennedy000000 000000 if lib_type == 'static' compiler_flags += ['-DTVG_STATIC'] endif if svg2png subdir('svg2png') endif if lottie2gif subdir('lottie2gif') endif mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testSwCanvas.cpp000664 001750 001750 00000020414 15164251010 033326 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; #ifdef THORVG_SW_RASTER_SUPPORT TEST_CASE("Missing Initialization", "[tvgSwCanvas]") { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas == nullptr); } TEST_CASE("Basic Creation", "[tvgSwCanvas]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); auto canvas2 = unique_ptr(SwCanvas::gen()); REQUIRE(canvas2); auto canvas3 = unique_ptr(SwCanvas::gen()); REQUIRE(canvas3); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Target Buffer", "[tvgSwCanvas]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); REQUIRE(canvas->target(nullptr, 100, 100, 100, ColorSpace::ARGB8888) == Result::InvalidArguments); REQUIRE(canvas->target(buffer, 0, 100, 100, ColorSpace::ARGB8888) == Result::InvalidArguments); REQUIRE(canvas->target(buffer, 100, 0, 100, ColorSpace::ARGB8888) == Result::InvalidArguments); REQUIRE(canvas->target(buffer, 100, 200, 100, ColorSpace::ARGB8888) == Result::InvalidArguments); REQUIRE(canvas->target(buffer, 100, 100, 0, ColorSpace::ARGB8888) == Result::InvalidArguments); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Pushing Paints", "[tvgSwCanvas]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); //Try all types of paints. REQUIRE(canvas->add(Shape::gen()) == Result::Success); REQUIRE(canvas->add(Picture::gen()) == Result::Success); REQUIRE(canvas->add(Scene::gen()) == Result::Success); //Cases by contexts. REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->add(Shape::gen()) == Result::Success); REQUIRE(canvas->add(Shape::gen()) == Result::Success); REQUIRE(canvas->remove() == Result::Success); Paint* paints[2]; paints[0] = Shape::gen(); REQUIRE(canvas->add(paints[0]) == Result::Success); //Negative case 1 REQUIRE(canvas->add(nullptr) == Result::InvalidArguments); paints[1] = Shape::gen(); REQUIRE(canvas->add(paints[1]) == Result::Success); REQUIRE(canvas->draw() == Result::Success); //Check list of paints auto list = canvas->paints(); REQUIRE(list.size() == 2); int idx = 0; for (auto paint : list) { REQUIRE(paints[idx] == paint); ++idx; } } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Update", "[tvgSwCanvas]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->add(Shape::gen()) == Result::Success); //No added shape auto shape = Shape::gen(); //Normal case REQUIRE(canvas->add(shape) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->update() == Result::InsufficientCondition); REQUIRE(canvas->sync() == Result::Success); REQUIRE(canvas->update() == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Synchronized Drawing", "[tvgSwCanvas]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); REQUIRE(canvas->sync() == Result::Success); REQUIRE(canvas->draw() == Result::InsufficientCondition); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); //Invalid Shape auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(canvas->add(shape) == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); auto shape2 = Shape::gen(); REQUIRE(shape2); REQUIRE(shape2->appendRect(0, 0, 100, 100) == Result::Success); REQUIRE(shape2->fill(255, 255, 255, 255) == Result::Success); REQUIRE(canvas->add(shape2) == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Asynchronous Drawing", "[tvgSwCanvas]") { //Use multi-threading REQUIRE(Initializer::init(2) == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); for (int i = 0; i < 3; ++i) { auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->appendRect(0, 0, 100, 100) == Result::Success); REQUIRE(shape->fill(255, 255, 255, 255) == Result::Success); REQUIRE(canvas->add(shape) == Result::Success); } REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Viewport", "[tvgSwCanvas]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); REQUIRE(canvas->viewport(25, 25, 100, 100) == Result::Success); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); REQUIRE(canvas->viewport(25, 25, 50, 50) == Result::Success); auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->appendRect(0, 0, 100, 100) == Result::Success); REQUIRE(shape->fill(255, 255, 255, 255) == Result::Success); REQUIRE(canvas->add(shape) == Result::Success); //Negative, not allowed REQUIRE(canvas->viewport(15, 25, 5, 5) == Result::InsufficientCondition); REQUIRE(canvas->draw() == Result::Success); //Negative, not allowed REQUIRE(canvas->viewport(25, 25, 10, 10) == Result::InsufficientCondition); REQUIRE(canvas->sync() == Result::Success); } REQUIRE(Initializer::term() == Result::Success); } #endifmlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgMath.h000664 001750 001750 00000026256 15164251010 033072 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_MATH_H_ #define _TVG_MATH_H_ #define _USE_MATH_DEFINES #include #include #include "tvgCommon.h" namespace tvg { #define MATH_PI 3.14159265358979323846f #define MATH_PI2 1.57079632679489661923f #define FLOAT_EPSILON 1.0e-06f //1.192092896e-07f #define PATH_KAPPA 0.552284f /************************************************************************/ /* General functions */ /************************************************************************/ float atan2(float y, float x); float length(const PathCommand* cmds, uint32_t cmdsCnt, const Point* pts, uint32_t ptsCnt); static inline float deg2rad(float degree) { return degree * (MATH_PI / 180.0f); } static inline float rad2deg(float radian) { return radian * (180.0f / MATH_PI); } static inline bool zero(float a) { return (fabsf(a) <= FLOAT_EPSILON) ? true : false; } static inline bool equal(float a, float b) { return tvg::zero(a - b); } template static inline constexpr const T& clamp(const T& v, const T& min, const T& max) { if (v < min) return min; else if (v > max) return max; return v; } /************************************************************************/ /* Matrix functions */ /************************************************************************/ void rotate(Matrix* m, float degree); bool inverse(const Matrix* m, Matrix* out); bool identity(const Matrix* m); Matrix operator*(const Matrix& lhs, const Matrix& rhs); bool operator==(const Matrix& lhs, const Matrix& rhs); static inline float radian(const Matrix& m) { return fabsf(tvg::atan2(m.e21, m.e11)); } static inline bool rightAngle(const Matrix& m) { auto radian = tvg::radian(m); if (tvg::zero(radian) || tvg::zero(radian - MATH_PI2) || tvg::zero(radian - MATH_PI)) return true; return false; } static inline bool skewed(const Matrix& m) { return !tvg::zero(m.e21 + m.e12); } static inline void identity(Matrix* m) { m->e11 = 1.0f; m->e12 = 0.0f; m->e13 = 0.0f; m->e21 = 0.0f; m->e22 = 1.0f; m->e23 = 0.0f; m->e31 = 0.0f; m->e32 = 0.0f; m->e33 = 1.0f; } static inline constexpr const Matrix identity() { return {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}; } static inline float scaling(const Matrix& m) { return sqrtf(m.e11 * m.e11 + m.e21 * m.e21); } static inline void scale(Matrix* m, const Point& p) { m->e11 *= p.x; m->e22 *= p.y; } static inline void scaleR(Matrix* m, const Point& p) { if (p.x != 1.0f) { m->e11 *= p.x; m->e21 *= p.x; } if (p.y != 1.0f) { m->e22 *= p.y; m->e12 *= p.y; } } static inline void translate(Matrix* m, const Point& p) { m->e13 += p.x; m->e23 += p.y; } static inline void translateR(Matrix* m, const Point& p) { if (p.x == 0.0f && p.y == 0.0f) return; m->e13 += (p.x * m->e11 + p.y * m->e12); m->e23 += (p.x * m->e21 + p.y * m->e22); } static inline bool operator!=(const Matrix& lhs, const Matrix& rhs) { return !(lhs == rhs); } static inline void operator*=(Matrix& lhs, const Matrix& rhs) { lhs = lhs * rhs; } static inline Matrix operator*(const Matrix* lhs, const Matrix& rhs) { if (lhs) return *lhs * rhs; return rhs; } static inline void log(const Matrix& m) { TVGLOG("COMMON", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m.e11, m.e12, m.e13, m.e21, m.e22, m.e23, m.e31, m.e32, m.e33); } /************************************************************************/ /* Point functions */ /************************************************************************/ void operator*=(Point& pt, const Matrix& m); Point operator*(const Point& pt, const Matrix& m); Point normal(const Point& p1, const Point& p2); void normalize(Point& pt); static inline constexpr const Point operator*=(Point& pt, const Matrix* m) { if (m) pt *= *m; return pt; } static inline Point operator*(const Point& pt, const Matrix* m) { if (m) return pt * *m; return pt; } static inline Point min(const Point& lhs, const Point& rhs) { return {std::min(lhs.x, rhs.x), std::min(lhs.y, rhs.y)}; } static inline Point max(const Point& lhs, const Point& rhs) { return {std::max(lhs.x, rhs.x), std::max(lhs.y, rhs.y)}; } static inline float dot(const Point& lhs, const Point& rhs) { return lhs.x * rhs.x + lhs.y * rhs.y; } static inline float cross(const Point& lhs, const Point& rhs) { return lhs.x * rhs.y - rhs.x * lhs.y; } static inline bool zero(const Point& p) { return tvg::zero(p.x) && tvg::zero(p.y); } static inline float length(const Point& a, const Point& b) { auto x = b.x - a.x; auto y = b.y - a.y; if (x < 0) x = -x; if (y < 0) y = -y; return (x > y) ? (x + 0.375f * y) : (y + 0.375f * x); } static inline float length(const Point& a) { return sqrtf(a.x * a.x + a.y * a.y); } static inline float length2(const Point& a) { return a.x * a.x + a.y * a.y; }; static inline bool operator==(const Point& lhs, const Point& rhs) { return tvg::equal(lhs.x, rhs.x) && tvg::equal(lhs.y, rhs.y); } static inline bool operator!=(const Point& lhs, const Point& rhs) { return !(lhs == rhs); } static inline Point operator-(const Point& lhs, const Point& rhs) { return {lhs.x - rhs.x, lhs.y - rhs.y}; } static inline Point operator-(const Point& lhs, const float rhs) { return {lhs.x - rhs, lhs.y - rhs}; } static inline Point operator+(const Point& lhs, const Point& rhs) { return {lhs.x + rhs.x, lhs.y + rhs.y}; } static inline Point operator+(const Point& lhs, const float rhs) { return {lhs.x + rhs, lhs.y + rhs}; } static inline void operator+=(Point& lhs, const Point& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; } static inline Point operator*(const Point& lhs, const Point& rhs) { return {lhs.x * rhs.x, lhs.y * rhs.y}; } static inline void operator*=(Point& lhs, const Point& rhs) { lhs.x *= rhs.x; lhs.y *= rhs.y; } static inline Point operator*(const Point& lhs, const float rhs) { return {lhs.x * rhs, lhs.y * rhs}; } static inline void operator*=(Point& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; } static inline Point operator*(const float& lhs, const Point& rhs) { return {lhs * rhs.x, lhs * rhs.y}; } static inline Point operator/(const Point& lhs, const Point& rhs) { return {lhs.x / rhs.x, lhs.y / rhs.y}; } static inline Point operator/(const Point& lhs, const float rhs) { return {lhs.x / rhs, lhs.y / rhs}; } static inline Point operator-(const Point& a) { return {-a.x, -a.y}; } enum class Orientation { Linear = 0, Clockwise, CounterClockwise, }; static inline Orientation orientation(const Point& p1, const Point& p2, const Point& p3) { auto val = cross(p2 - p1, p3 - p1); if (zero(val)) return Orientation::Linear; else return val > 0 ? Orientation::Clockwise : Orientation::CounterClockwise; } static inline void log(const Point& pt) { TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y); } static inline bool closed(const Point& lhs, const Point& rhs, float tolerance) { float dx = lhs.x - rhs.x; float dy = lhs.y - rhs.y; return (dx * dx + dy * dy) < (tolerance * tolerance); } /************************************************************************/ /* Line functions */ /************************************************************************/ struct Line { Point pt1; Point pt2; void split(float at, Line& left, Line& right) const; float length() const; }; /************************************************************************/ /* Geometry functions */ /************************************************************************/ struct BBox { Point min, max; void init() { min = {FLT_MAX, FLT_MAX}; max = {-FLT_MAX, -FLT_MAX}; } }; static inline uint32_t arcSegmentsCnt(float arcAngle, float pixelRadius) { if (pixelRadius < FLOAT_EPSILON) return 2; static constexpr auto PX_TOLERANCE = 0.25f; // Sagitta-based formula Approximation: 1 - cos(θ/2) ≈ (θ/2)²/2, so θ ≈ 2 * sqrt(2 * s/r) auto segmentAngle = 2.0f * sqrtf(2.0f * PX_TOLERANCE / pixelRadius); return static_cast(ceilf(fabsf(arcAngle) / segmentAngle)) + 1; } /************************************************************************/ /* Bezier functions */ /************************************************************************/ struct Bezier { Point start; Point ctrl1; Point ctrl2; Point end; Bezier() {} Bezier(const Point& p0, const Point& p1, const Point& p2, const Point& p3): start(p0), ctrl1(p1), ctrl2(p2), end(p3) {} // Constructor that approximates a quarter-circle segment of arc between 'start' and 'end' points // using a cubic Bezier curve with a given 'radius'. Bezier(const Point& start, const Point& end, float radius); void split(float t, Bezier& left); void split(Bezier& left, Bezier& right) const; void split(float at, Bezier& left, Bezier& right) const; float length() const; float lengthApprox() const; float at(float at, float length) const; float atApprox(float at, float length) const; Point at(float t) const; float angle(float t) const; bool flatten() const; uint32_t segments() const; Bezier operator*(const Matrix& m); static void bounds(BBox& box, const Point& start, const Point& ctrl1, const Point& ctrl2, const Point& end); }; /************************************************************************/ /* Interpolation functions */ /************************************************************************/ template static inline T lerp(const T &start, const T &end, float t) { return static_cast(start + (end - start) * t); } uint8_t lerp(const uint8_t &start, const uint8_t &end, float t); } #endif //_TVG_MATH_H_mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/savers/gif/tvgGifEncoder.h000664 001750 001750 00000005205 15164251010 034755 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef TVG_GIF_ENCODER_H #define TVG_GIF_ENCODER_H #include "tvgCommon.h" typedef struct { uint8_t r[256]; uint8_t g[256]; uint8_t b[256]; // k-d tree over RGB space, organized in heap fashion // i.e. left child of node i is node i*2, right child is node i*2+1 // nodes 256-511 are implicitly the leaves, containing a color uint8_t treeSplitElt[256]; uint8_t treeSplit[256]; } GifPalette; typedef struct { FILE* f; uint8_t* oldImage; uint8_t* tmpImage; GifPalette pal; bool firstFrame; } GifWriter; // Creates a gif file. // The input GIFWriter is assumed to be uninitialized. // The delay value is the time between frames in hundredths of a second - note that not all viewers pay much attention to this value. bool gifBegin(GifWriter* writer, const char* filename, uint32_t width, uint32_t height, uint32_t delay); // Writes out a new frame to a GIF in progress. // The GIFWriter should have been created by GIFBegin. // AFAIK, it is legal to use different bit depths for different frames of an image - // this may be handy to save bits in animations that don't change much. bool gifWriteFrame(GifWriter* writer, const uint8_t* image, uint32_t width, uint32_t height, uint32_t delay, bool transparent); // Writes the EOF code, closes the file handle, and frees temp memory used by a GIF. // Many if not most viewers will still display a GIF properly if the EOF code is missing, // but it's still a good idea to write it out. bool gifEnd(GifWriter* writer); #endif //TVG_GIF_ENCODER_H src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h000664 001750 001750 00000004571 15164251010 052510 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Function.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.3.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_FUNCTION, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* Number properties: * (property name, object pointer getter) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_FUNCTION_PROTOTYPE_TO_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_APPLY, ECMA_FUNCTION_PROTOTYPE_APPLY, 2, 2) ROUTINE (LIT_MAGIC_STRING_CALL, ECMA_FUNCTION_PROTOTYPE_CALL, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_BIND, ECMA_FUNCTION_PROTOTYPE_BIND, NON_FIXED, 1) /** * ECMA-262 v6.0 19.2.3.6 @@hasInstance * the property attributes are: { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }. */ ROUTINE_WITH_FLAGS (LIT_GLOBAL_SYMBOL_HAS_INSTANCE, ECMA_FUNCTION_PROTOTYPE_SYMBOL_HAS_INSTANCE, 1, 1, 0 /* flags */) ACCESSOR_BUILTIN_FUNCTION (LIT_MAGIC_STRING_ARGUMENTS, ECMA_BUILTIN_ID_TYPE_ERROR_THROWER, ECMA_BUILTIN_ID_TYPE_ERROR_THROWER, ECMA_PROPERTY_FLAG_CONFIGURABLE) ACCESSOR_BUILTIN_FUNCTION (LIT_MAGIC_STRING_CALLER, ECMA_BUILTIN_ID_TYPE_ERROR_THROWER, ECMA_BUILTIN_ID_TYPE_ERROR_THROWER, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.cpp000664 001750 001750 00000047327 15164251010 050766 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-exceptions.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "lit-char-helpers.h" #if JERRY_BUILTIN_DATE /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltinhelpers ECMA builtin helper operations * @{ */ /** * Day names */ const char* day_names_p[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; /** * Month names */ const char* month_names_p[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /** * Calculate the elapsed days since Unix Epoch * * @return elapsed days since Unix Epoch */ int32_t ecma_date_day_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); if (time < 0) { time -= ECMA_DATE_MS_PER_DAY - 1; } return (int32_t) (time / ECMA_DATE_MS_PER_DAY); } /* ecma_date_day_from_time */ /** * Abstract operation: DayFromYear * * See also: * ECMA-262 v11, 20.4.1.3 * * @return first of day in the given year */ static int32_t ecma_date_day_from_year (int32_t year) /**< year value */ { if (JERRY_LIKELY (year >= 1970)) { return (int32_t) (365 * (year - 1970) + ((year - 1969) / 4) - ((year - 1901) / 100) + ((year - 1601) / 400)); } return (int32_t) (365 * (year - 1970) + floor ((year - 1969) / 4.0) - floor ((year - 1901) / 100.0) + floor ((year - 1601) / 400.0)); } /* ecma_date_day_from_year */ /** * Abstract operation: DaysInYear * * See also: * ECMA-262 v11, 20.4.1.3 * * @return number of days in the given year */ static int ecma_date_days_in_year (int32_t year) /**< year */ { if (year % 4 != 0 || (year % 100 == 0 && (year % 400 != 0))) { return ECMA_DATE_DAYS_IN_YEAR; } return ECMA_DATE_DAYS_IN_LEAP_YEAR; } /* ecma_date_days_in_year */ /** * Abstract operation: InLeapYear * * See also: * ECMA-262 v11, 20.4.1.3 * * @return 1 - if the year is leap * 0 - otherwise */ static int32_t ecma_date_in_leap_year (int32_t year) /**< time value */ { return ecma_date_days_in_year (year) - ECMA_DATE_DAYS_IN_YEAR; } /* ecma_date_in_leap_year */ /** * First days of months in normal and leap years */ static const uint16_t first_day_in_month[2][12] = { { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, /* normal year */ }, { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 /* leap year */ } }; /** * Abstract operation: YearFromTime * * See also: * ECMA-262 v11, 20.4.1.3 * * @return year corresponds to the given time */ int32_t ecma_date_year_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); int32_t approx = (int32_t) (floor (time / ECMA_DATE_MS_PER_DAY / 365.2425) + 1970); int64_t year_ms = ecma_date_day_from_year (approx) * ((int64_t) ECMA_DATE_MS_PER_DAY); if ((ecma_number_t) year_ms > time) { approx--; } if ((ecma_number_t) (year_ms + ecma_date_days_in_year (approx) * ((int64_t) ECMA_DATE_MS_PER_DAY)) <= time) { approx++; } return approx; } /* ecma_date_year_from_time */ /** * Abstract operation: MonthFromTime * * See also: * ECMA-262 v11, 20.4.1.4 * * @return month corresponds to the given time */ int32_t ecma_date_month_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); int32_t year = ecma_date_year_from_time (time); int32_t day_within_year = ecma_date_day_from_time (time) - ecma_date_day_from_year (year); JERRY_ASSERT (day_within_year >= 0 && day_within_year < ECMA_DATE_DAYS_IN_LEAP_YEAR); int32_t in_leap_year = ecma_date_in_leap_year (year); for (int i = 1; i < 12; i++) { if (day_within_year < first_day_in_month[in_leap_year][i]) { return i - 1; } } return 11; } /* ecma_date_month_from_time */ /** * Abstract operation: DateFromTime * * See also: * ECMA-262 v11, 20.4.1.4 * * @return date corresponds to the given time */ int32_t ecma_date_date_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); int32_t year = ecma_date_year_from_time (time); int32_t day_within_year = ecma_date_day_from_time (time) - ecma_date_day_from_year (year); JERRY_ASSERT (day_within_year >= 0 && day_within_year < ECMA_DATE_DAYS_IN_LEAP_YEAR); int32_t in_leap_year = ecma_date_in_leap_year (year); int32_t month = 11; for (int i = 1; i < 12; i++) { if (day_within_year < first_day_in_month[in_leap_year][i]) { month = i - 1; break; } } return day_within_year + 1 - first_day_in_month[in_leap_year][month]; } /* ecma_date_date_from_time */ /** * Abstract operation: WeekDay * * See also: * ECMA-262 v11, 20.4.1.4 * * @return weekday corresponds to the given time */ int32_t ecma_date_week_day (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); int32_t day = ecma_date_day_from_time (time); int week_day = (day + 4) % 7; return week_day >= 0 ? week_day : week_day + 7; } /* ecma_date_week_day */ /** * Abstract operation: LocalTZA * * See also: * ECMA-262 v11, 20.4.1.7 * * @return local time zone adjustment */ int32_t ecma_date_local_time_zone_adjustment (ecma_number_t time) /**< time value */ { return jerry_port_local_tza (time); } /* ecma_date_local_time_zone_adjustment */ /** * Abstract operation: UTC * * See also: * ECMA-262 v11, 20.4.1.9 * * @return UTC time */ ecma_number_t ecma_date_utc (ecma_number_t time) /**< time value */ { return time - jerry_port_local_tza (time); } /* ecma_date_utc */ /** * Calculate the time component from the given time * * @return time component of the given time */ int32_t ecma_date_time_in_day_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); ecma_number_t day = ecma_date_day_from_time (time); return (int32_t) (time - (day * ECMA_DATE_MS_PER_DAY)); } /* ecma_date_time_in_day_from_time */ /** * Abstract operation: HourFromTime * * See also: * ECMA-262 v11, 20.4.1.10 * * @return hours component of the given time */ int32_t ecma_date_hour_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); int32_t time_in_day = ecma_date_time_in_day_from_time (time); return (int32_t) (time_in_day / ECMA_DATE_MS_PER_HOUR); } /* ecma_date_hour_from_time */ /** * Abstract operation: HourFromTime * * See also: * ECMA-262 v11, 20.4.1.10 * * @return minutes component of the given time */ int32_t ecma_date_min_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); int32_t time_in_day = ecma_date_time_in_day_from_time (time); return ((int32_t) (time_in_day / ECMA_DATE_MS_PER_MINUTE)) % ECMA_DATE_MINUTES_PER_HOUR; } /* ecma_date_min_from_time */ /** * Abstract operation: HourFromTime * * See also: * ECMA-262 v11, 20.4.1.10 * * @return seconds component of the given time */ int32_t ecma_date_sec_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); int32_t time_in_day = ecma_date_time_in_day_from_time (time); return ((int32_t) (time_in_day / ECMA_DATE_MS_PER_SECOND)) % ECMA_DATE_SECONDS_PER_MINUTE; } /* ecma_date_sec_from_time */ /** * Abstract operation: HourFromTime * * See also: * ECMA-262 v11, 20.4.1.10 * * @return milliseconds component of the given time */ int32_t ecma_date_ms_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); int32_t time_in_day = ecma_date_time_in_day_from_time (time); return (int32_t) (time_in_day % ECMA_DATE_MS_PER_SECOND); } /* ecma_date_ms_from_time */ /** * Abstract operation: MakeTime * * See also: * ECMA-262 v11, 20.4.1.11 * * @return constructed time in milliseconds */ ecma_number_t ecma_date_make_time (ecma_number_t hour, /**< hour value */ ecma_number_t min, /**< minute value */ ecma_number_t sec, /**< second value */ ecma_number_t ms) /**< millisecond value */ { if (!ecma_number_is_finite (hour) || !ecma_number_is_finite (min) || !ecma_number_is_finite (sec) || !ecma_number_is_finite (ms)) { return ecma_number_make_nan (); } ecma_number_t h = ecma_number_trunc (hour); ecma_number_t m = ecma_number_trunc (min); ecma_number_t s = ecma_number_trunc (sec); ecma_number_t milli = ecma_number_trunc (ms); return h * ECMA_DATE_MS_PER_HOUR + m * ECMA_DATE_MS_PER_MINUTE + s * ECMA_DATE_MS_PER_SECOND + milli; } /* ecma_date_make_time */ /** * Abstract operation: MakeDay * * See also: * ECMA-262 v11, 20.4.1.12 * * @return elapsed number of days since Unix Epoch */ ecma_number_t ecma_date_make_day (ecma_number_t year, /**< year value */ ecma_number_t month, /**< month value */ ecma_number_t date) /**< date value */ { /* 1. */ if (!ecma_number_is_finite (year) || !ecma_number_is_finite (month) || !ecma_number_is_finite (date) || fabs (year) > INT32_MAX) { return ecma_number_make_nan (); } /* 2., 3., 4. */ int32_t y = (int32_t) (year); ecma_number_t m = ecma_number_trunc (month); ecma_number_t dt = ecma_number_trunc (date); /* 5. */ int32_t ym = y + (int32_t) (floor (m / 12)); /* 6. */ int32_t mn = (int32_t) fmod (m, 12); if (mn < 0) { mn += 12; } /* 7. */ ecma_number_t days = (ecma_date_day_from_year (ym) + first_day_in_month[ecma_date_in_leap_year (ym)][mn] + (dt - 1)); return days * ECMA_DATE_MS_PER_DAY; } /* ecma_date_make_day */ /** * Abstract operation: MakeTime * * See also: * ECMA-262 v11, 20.4.1.13 * * @return elapsed number of milliseconds since Unix Epoch */ ecma_number_t ecma_date_make_date (ecma_number_t day, /**< day value */ ecma_number_t time) /**< time value */ { if (!ecma_number_is_finite (day) || !ecma_number_is_finite (time)) { return ecma_number_make_nan (); } return day + time; } /* ecma_date_make_date */ /** * Abstract operation: TimeClip * * See also: * ECMA-262 v11, 20.4.1.14 * * @return elapsed number of milliseconds since Unix Epoch */ ecma_number_t ecma_date_time_clip (ecma_number_t time) /**< time value */ { if (!ecma_number_is_finite (time) || fabs (time) > ECMA_DATE_MAX_VALUE) { return ecma_number_make_nan (); } return ecma_number_trunc (time); } /* ecma_date_time_clip */ /** * Common function to convert date to string. * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ const char *format_p) /**< format buffer */ { const uint32_t date_buffer_length = 37; JERRY_VLA (lit_utf8_byte_t, date_buffer, date_buffer_length); lit_utf8_byte_t *dest_p = date_buffer; while (*format_p != LIT_CHAR_NULL) { if (*format_p != LIT_CHAR_DOLLAR_SIGN) { *dest_p++ = (lit_utf8_byte_t) *format_p++; continue; } format_p++; const char *str_p = NULL; int32_t number = 0; int32_t number_length = 0; switch (*format_p) { case LIT_CHAR_UPPERCASE_Y: /* Year. */ { number = ecma_date_year_from_time (datetime_number); if (number >= 100000 || number <= -100000) { number_length = 6; } else if (number >= 10000 || number <= -10000) { number_length = 5; } else { number_length = 4; } break; } case LIT_CHAR_LOWERCASE_Y: /* ISO Year: -000001, 0000, 0001, 9999, +012345 */ { number = ecma_date_year_from_time (datetime_number); if (0 <= number && number <= 9999) { number_length = 4; } else { number_length = 6; } break; } case LIT_CHAR_UPPERCASE_M: /* Month. */ { int32_t month = ecma_date_month_from_time (datetime_number); JERRY_ASSERT (month >= 0 && month <= 11); str_p = month_names_p[month]; break; } case LIT_CHAR_UPPERCASE_O: /* Month as number. */ { /* The 'ecma_date_month_from_time' (ECMA 262 v5, 15.9.1.4) returns a * number from 0 to 11, but we have to print the month from 1 to 12 * for ISO 8601 standard (ECMA 262 v5, 15.9.1.15). */ number = ecma_date_month_from_time (datetime_number) + 1; number_length = 2; break; } case LIT_CHAR_UPPERCASE_D: /* Day. */ { number = ecma_date_date_from_time (datetime_number); number_length = 2; break; } case LIT_CHAR_UPPERCASE_W: /* Day of week. */ { int32_t day = ecma_date_week_day (datetime_number); JERRY_ASSERT (day >= 0 && day <= 6); str_p = day_names_p[day]; break; } case LIT_CHAR_LOWERCASE_H: /* Hour. */ { number = ecma_date_hour_from_time (datetime_number); number_length = 2; break; } case LIT_CHAR_LOWERCASE_M: /* Minutes. */ { number = ecma_date_min_from_time (datetime_number); number_length = 2; break; } case LIT_CHAR_LOWERCASE_S: /* Seconds. */ { number = ecma_date_sec_from_time (datetime_number); number_length = 2; break; } case LIT_CHAR_LOWERCASE_I: /* Milliseconds. */ { number = ecma_date_ms_from_time (datetime_number); number_length = 3; break; } case LIT_CHAR_LOWERCASE_Z: /* Time zone hours part. */ { int32_t time_zone = ecma_date_local_time_zone_adjustment (datetime_number); if (time_zone >= 0) { *dest_p++ = LIT_CHAR_PLUS; } else { *dest_p++ = LIT_CHAR_MINUS; time_zone = -time_zone; } number = time_zone / ECMA_DATE_MS_PER_HOUR; number_length = 2; break; } default: { JERRY_ASSERT (*format_p == LIT_CHAR_UPPERCASE_Z); /* Time zone minutes part. */ int32_t time_zone = ecma_date_local_time_zone_adjustment (datetime_number); if (time_zone < 0) { time_zone = -time_zone; } number = (time_zone % ECMA_DATE_MS_PER_HOUR) / ECMA_DATE_MS_PER_MINUTE; number_length = 2; break; } } format_p++; if (str_p != NULL) { /* Print string values: month or day name which is always 3 characters */ memcpy (dest_p, str_p, 3); dest_p += 3; continue; } /* Print right aligned number values. */ JERRY_ASSERT (number_length > 0); if (number < 0) { number = -number; *dest_p++ = '-'; } else if (*(format_p - 1) == LIT_CHAR_LOWERCASE_Y && number_length == 6) { /* positive sign is compulsory for extended years */ *dest_p++ = '+'; } dest_p += number_length; lit_utf8_byte_t *buffer_p = dest_p; do { buffer_p--; *buffer_p = (lit_utf8_byte_t) ((number % 10) + (int32_t) LIT_CHAR_0); number /= 10; } while (--number_length); } JERRY_ASSERT (dest_p <= date_buffer + date_buffer_length); return ecma_make_string_value ( ecma_new_ecma_string_from_ascii (date_buffer, (lit_utf8_size_t) (dest_p - date_buffer))); } /* ecma_date_to_string_format */ /** * Common function to create a time zone specific string from a numeric value. * * Used by: * - The Date routine. * - The Date.prototype.toString routine. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_date_value_to_string (ecma_number_t datetime_number) /**< datetime */ { datetime_number += ecma_date_local_time_zone_adjustment (datetime_number); return ecma_date_to_string_format (datetime_number, "$W $M $D $Y $h:$m:$s GMT$z$Z"); } /* ecma_date_value_to_string */ /** * Common function to create a time zone specific string from a numeric value. * * Used by: * - The Date.prototype.toUTCString routine. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_date_value_to_utc_string (ecma_number_t datetime_number) /**< datetime */ { return ecma_date_to_string_format (datetime_number, "$W, $D $M $Y $h:$m:$s GMT"); } /* ecma_date_value_to_utc_string */ /** * Common function to create a ISO specific string from a numeric value. * * Used by: * - The Date.prototype.toISOString routine. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_date_value_to_iso_string (ecma_number_t datetime_number) /**u.cls.type = type; ext_object_p->u.cls.u1.array_buffer_flags = ECMA_ARRAYBUFFER_ALLOCATED; ext_object_p->u.cls.u3.length = length; memset ((uint8_t *) (ext_object_p + 1), 0, length); return object_p; } /* ecma_arraybuffer_create_object */ /** * Creating ArrayBuffer objects with a pointer to its buffer * * @return new ArrayBuffer object */ ecma_object_t * ecma_arraybuffer_create_object_with_buffer (uint8_t type, /**< type of the arraybuffer */ uint32_t length) { ecma_builtin_id_t prototype_id; #if JERRY_BUILTIN_SHAREDARRAYBUFFER JERRY_ASSERT (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER || type == ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER); prototype_id = (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER ? ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE : ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER_PROTOTYPE); #else /* !JERRY_BUILTIN_SHAREDARRAYBUFFER */ JERRY_ASSERT (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER); prototype_id = ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE; #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ ecma_object_t *object_p = ecma_create_object (ecma_builtin_get (prototype_id), sizeof (ecma_arraybuffer_pointer_t), ECMA_OBJECT_TYPE_CLASS); ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) object_p; arraybuffer_pointer_p->extended_object.u.cls.type = type; arraybuffer_pointer_p->extended_object.u.cls.u1.array_buffer_flags = ECMA_ARRAYBUFFER_HAS_POINTER; arraybuffer_pointer_p->extended_object.u.cls.u3.length = length; arraybuffer_pointer_p->buffer_p = NULL; arraybuffer_pointer_p->arraybuffer_user_p = NULL; return object_p; } /* ecma_arraybuffer_create_object_with_buffer */ /** * Creating ArrayBuffer objects based on the array length * * @return new ArrayBuffer object */ ecma_object_t * ecma_arraybuffer_new_object (uint32_t length) /**< length of the arraybuffer */ { if (length > JERRY_CONTEXT (arraybuffer_compact_allocation_limit)) { return ecma_arraybuffer_create_object_with_buffer (ECMA_OBJECT_CLASS_ARRAY_BUFFER, length); } return ecma_arraybuffer_create_object (ECMA_OBJECT_CLASS_ARRAY_BUFFER, length); } /* ecma_arraybuffer_new_object */ /** * Allocate a backing store for an array buffer. * * @return buffer pointer on success, * NULL otherwise */ ecma_value_t ecma_arraybuffer_allocate_buffer (ecma_object_t *arraybuffer_p) /**< ArrayBuffer object */ { JERRY_ASSERT (!(ECMA_ARRAYBUFFER_GET_FLAGS (arraybuffer_p) & ECMA_ARRAYBUFFER_ALLOCATED)); ecma_extended_object_t *extended_object_p = (ecma_extended_object_t *) arraybuffer_p; if (ECMA_ARRAYBUFFER_GET_FLAGS (arraybuffer_p) & ECMA_ARRAYBUFFER_DETACHED) { extended_object_p->u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_ALLOCATED; return ECMA_VALUE_UNDEFINED; } uint32_t arraybuffer_length = extended_object_p->u.cls.u3.length; ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) arraybuffer_p; jerry_arraybuffer_allocate_cb_t arraybuffer_allocate_callback = JERRY_CONTEXT (arraybuffer_allocate_callback); uint8_t *buffer_p; if (arraybuffer_allocate_callback != NULL) { jerry_arraybuffer_type_t type = JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER; #if JERRY_BUILTIN_SHAREDARRAYBUFFER if (extended_object_p->u.cls.type == ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER) { type = JERRY_ARRAYBUFFER_TYPE_SHARED_ARRAYBUFFER; } #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ buffer_p = arraybuffer_allocate_callback (type, arraybuffer_length, &arraybuffer_pointer_p->arraybuffer_user_p, JERRY_CONTEXT (arraybuffer_allocate_callback_user_p)); } else { buffer_p = (uint8_t *) jmem_heap_alloc_block_null_on_error (arraybuffer_length); } if (buffer_p == NULL) { return ecma_raise_range_error (ECMA_ERR_ALLOCATE_ARRAY_BUFFER); } arraybuffer_pointer_p->buffer_p = buffer_p; extended_object_p->u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_ALLOCATED; memset (buffer_p, 0, arraybuffer_length); return ECMA_VALUE_UNDEFINED; } /* ecma_arraybuffer_allocate_buffer */ /** * Allocate a backing store for an array buffer, throws an error if the allocation fails. * * @return ECMA_VALUE_UNDEFINED on success, * ECMA_VALUE_ERROR otherwise */ ecma_value_t ecma_arraybuffer_allocate_buffer_throw (ecma_object_t *arraybuffer_p) { JERRY_ASSERT (!(ECMA_ARRAYBUFFER_GET_FLAGS (arraybuffer_p) & ECMA_ARRAYBUFFER_ALLOCATED)); return ecma_arraybuffer_allocate_buffer (arraybuffer_p); } /* ecma_arraybuffer_allocate_buffer_throw */ /** * Release the backing store allocated by an array buffer. */ void ecma_arraybuffer_release_buffer (ecma_object_t *arraybuffer_p) /**< ArrayBuffer object */ { JERRY_ASSERT (ecma_object_class_is (arraybuffer_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (arraybuffer_p)); jerry_arraybuffer_free_cb_t free_callback = JERRY_CONTEXT (arraybuffer_free_callback); ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) arraybuffer_p; if (arraybuffer_pointer_p->buffer_p == NULL) { return; } uint32_t arraybuffer_length = arraybuffer_pointer_p->extended_object.u.cls.u3.length; if (free_callback == NULL) { jmem_heap_free_block (arraybuffer_pointer_p->buffer_p, arraybuffer_length); return; } jerry_arraybuffer_type_t type = JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER; #if JERRY_BUILTIN_SHAREDARRAYBUFFER if (arraybuffer_pointer_p->extended_object.u.cls.type == ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER) { type = JERRY_ARRAYBUFFER_TYPE_SHARED_ARRAYBUFFER; } #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ free_callback (type, (uint8_t*) arraybuffer_pointer_p->buffer_p, arraybuffer_length, arraybuffer_pointer_p->arraybuffer_user_p, JERRY_CONTEXT (arraybuffer_allocate_callback_user_p)); } /* ecma_arraybuffer_release_buffer */ /** * ArrayBuffer object creation operation. * * See also: ES2015 24.1.1.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_arraybuffer_object (const ecma_value_t *arguments_list_p, /**< list of arguments that * are passed to String constructor */ uint32_t arguments_list_len) /**< length of the arguments' list */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_number_t length_num = 0; if (arguments_list_len > 0) { if (ecma_is_value_number (arguments_list_p[0])) { length_num = ecma_get_number_from_value (arguments_list_p[0]); } else { ecma_value_t to_number_value = ecma_op_to_number (arguments_list_p[0], &length_num); if (ECMA_IS_VALUE_ERROR (to_number_value)) { ecma_deref_object (proto_p); return to_number_value; } } if (ecma_number_is_nan (length_num)) { length_num = 0; } const uint32_t maximum_size_in_byte = UINT32_MAX - sizeof (ecma_extended_object_t) - JMEM_ALIGNMENT + 1; if (length_num <= -1.0 || length_num > (ecma_number_t) maximum_size_in_byte + 0.5) { ecma_deref_object (proto_p); return ecma_raise_range_error (ECMA_ERR_INVALID_ARRAYBUFFER_LENGTH); } } uint32_t length_uint32 = ecma_number_to_uint32 (length_num); ecma_object_t *array_buffer = ecma_arraybuffer_new_object (length_uint32); ECMA_SET_NON_NULL_POINTER (array_buffer->u2.prototype_cp, proto_p); ecma_deref_object (proto_p); return ecma_make_object_value (array_buffer); } /* ecma_op_create_arraybuffer_object */ /** * Helper function: check if the target is ArrayBuffer * * * See also: ES2015 24.1.1.4 * * @return true - if value is an ArrayBuffer object * false - otherwise */ bool ecma_is_arraybuffer (ecma_value_t target) /**< the target value */ { return (ecma_is_value_object (target) && ecma_object_class_is (ecma_get_object_from_value (target), ECMA_OBJECT_CLASS_ARRAY_BUFFER)); } /* ecma_is_arraybuffer */ /** * Helper function: return the length of the buffer inside the arraybuffer object * * @return uint32_t, the length of the arraybuffer */ uint32_t JERRY_ATTR_PURE ecma_arraybuffer_get_length (ecma_object_t *object_p) /**< pointer to the ArrayBuffer object */ { JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (object_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; return ecma_arraybuffer_is_detached (object_p) ? 0 : ext_object_p->u.cls.u3.length; } /* ecma_arraybuffer_get_length */ /** * Helper function: return the pointer to the data buffer inside the arraybuffer object * * @return pointer to the data buffer */ uint8_t *JERRY_ATTR_PURE ecma_arraybuffer_get_buffer (ecma_object_t *object_p) /**< pointer to the ArrayBuffer object */ { JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (object_p)); if (!(ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_HAS_POINTER)) { return (uint8_t *) object_p + sizeof (ecma_extended_object_t); } ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) object_p; return (uint8_t *) arraybuffer_pointer_p->buffer_p; } /* ecma_arraybuffer_get_buffer */ /** * Helper function: check if the target ArrayBuffer is detached * * @return true - if value is an detached ArrayBuffer object * false - otherwise */ bool JERRY_ATTR_PURE ecma_arraybuffer_is_detached (ecma_object_t *object_p) /**< pointer to the ArrayBuffer object */ { JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (object_p)); return (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_DETACHED) != 0; } /* ecma_arraybuffer_is_detached */ /** * ArrayBuffer object detaching operation * * See also: ES2015 24.1.1.3 * * @return true - if detach operation is succeeded * false - otherwise */ bool ecma_arraybuffer_detach (ecma_object_t *object_p) /**< pointer to the ArrayBuffer object */ { JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER)); if (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_DETACHED) { return false; } ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_DETACHED; if (!(ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_ALLOCATED)) { return true; } ext_object_p->u.cls.u1.array_buffer_flags &= (uint8_t) ~ECMA_ARRAYBUFFER_ALLOCATED; if (!(ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_HAS_POINTER)) { return true; } ecma_arraybuffer_release_buffer (object_p); return true; } /* ecma_arraybuffer_detach */ /** * ArrayBuffer slice operation * * See also: * ECMA-262 v11, 24.1.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_arraybuffer_slice (ecma_value_t this_arg, const ecma_value_t *argument_list_p, uint32_t arguments_number) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); /* 3-4. */ if (ECMA_ARRAYBUFFER_LAZY_ALLOC (object_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (object_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } /* 5. */ uint32_t len = ecma_arraybuffer_get_length (object_p); uint32_t start = 0; uint32_t end = len; if (arguments_number > 0) { /* 6-7. */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (argument_list_p[0], len, &start))) { return ECMA_VALUE_ERROR; } if (arguments_number > 1 && !ecma_is_value_undefined (argument_list_p[1])) { /* 8-9. */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (argument_list_p[1], len, &end))) { return ECMA_VALUE_ERROR; } } } /* 10. */ uint32_t new_len = (end >= start) ? (end - start) : 0; /* 11. */ ecma_builtin_id_t buffer_builtin_id = ECMA_BUILTIN_ID_ARRAYBUFFER; ecma_value_t ctor = ecma_op_species_constructor (object_p, buffer_builtin_id); if (ECMA_IS_VALUE_ERROR (ctor)) { return ctor; } /* 12. */ ecma_object_t *ctor_obj_p = ecma_get_object_from_value (ctor); ecma_value_t new_len_value = ecma_make_uint32_value (new_len); ecma_value_t new_arraybuffer = ecma_op_function_construct (ctor_obj_p, ctor_obj_p, &new_len_value, 1); ecma_deref_object (ctor_obj_p); ecma_free_value (new_len_value); if (ECMA_IS_VALUE_ERROR (new_arraybuffer)) { return new_arraybuffer; } ecma_object_t *new_arraybuffer_p = ecma_get_object_from_value (new_arraybuffer); ecma_value_t ret_value = ECMA_VALUE_EMPTY; lit_utf8_byte_t *old_buf; lit_utf8_byte_t *new_buf; /* 13. */ if (!(ecma_object_class_is (new_arraybuffer_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (new_arraybuffer_p))) { ret_value = ecma_raise_type_error (ECMA_ERR_RETURN_VALUE_IS_NOT_AN_ARRAYBUFFER_OBJECT); goto free_new_arraybuffer; } /* 14-15. */ if (ECMA_ARRAYBUFFER_LAZY_ALLOC (new_arraybuffer_p)) { ret_value = ECMA_VALUE_ERROR; goto free_new_arraybuffer; } if (ecma_arraybuffer_is_detached (new_arraybuffer_p)) { ret_value = ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); goto free_new_arraybuffer; } /* 16. */ if (new_arraybuffer == this_arg) { ret_value = ecma_raise_type_error (ECMA_ERR_ARRAY_BUFFER_RETURNED_THIS_FROM_CONSTRUCTOR); goto free_new_arraybuffer; } /* 17. */ if (ecma_arraybuffer_get_length (new_arraybuffer_p) < new_len) { ret_value = ecma_raise_type_error (ECMA_ERR_DERIVED_ARRAY_BUFFER_CTOR_BUFFER_TOO_SMALL); goto free_new_arraybuffer; } /* 19. */ if (ecma_arraybuffer_is_detached (object_p)) { ret_value = ECMA_VALUE_ERROR; goto free_new_arraybuffer; } /* 20. */ old_buf = ecma_arraybuffer_get_buffer (object_p); /* 21. */ new_buf = ecma_arraybuffer_get_buffer (new_arraybuffer_p); /* 22. */ memcpy (new_buf, old_buf + start, new_len); free_new_arraybuffer: if (ret_value != ECMA_VALUE_EMPTY) { ecma_deref_object (new_arraybuffer_p); } else { /* 23. */ ret_value = ecma_make_object_value (new_arraybuffer_p); } return ret_value; } /* ecma_builtin_arraybuffer_slice */ /** * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgGeometry.cpp000664 001750 001750 00000006162 15164251010 037020 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgGeometry.h" //*********************************************************************** // WgMeshData //*********************************************************************** void WgMeshData::bbox(const Point pmin, const Point pmax) { const float vdata[] = {pmin.x, pmin.y, pmax.x, pmin.y, pmax.x, pmax.y, pmin.x, pmax.y}; const uint32_t idata[] = {0, 1, 2, 0, 2, 3}; // setup vertex data vbuffer.reserve(4); vbuffer.count = 4; memcpy(vbuffer.data, vdata, sizeof(vdata)); // setup tex coords data tbuffer.clear(); // setup indexes data ibuffer.reserve(6); ibuffer.count = 6; memcpy(ibuffer.data, idata, sizeof(idata)); } void WgMeshData::imageBox(float w, float h) { const float vdata[] = {0.0f, 0.0f, w, 0.0f, w, h, 0.0f, h}; const float tdata[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; const uint32_t idata[] = {0, 1, 2, 0, 2, 3}; // setup vertex data vbuffer.reserve(4); vbuffer.count = 4; memcpy(vbuffer.data, vdata, sizeof(vdata)); // setup tex coords data tbuffer.reserve(4); tbuffer.count = 4; memcpy(tbuffer.data, tdata, sizeof(tdata)); // setup indexes data ibuffer.reserve(6); ibuffer.count = 6; memcpy(ibuffer.data, idata, sizeof(idata)); } void WgMeshData::blitBox() { const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f}; const float tdata[] = {+0.0f, +0.0f, +1.0f, +0.0f, +1.0f, +1.0f, +0.0f, +1.0f}; const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 }; // setup vertex data vbuffer.reserve(4); vbuffer.count = 4; memcpy(vbuffer.data, vdata, sizeof(vdata)); // setup tex coords data tbuffer.reserve(4); tbuffer.count = 4; memcpy(tbuffer.data, tdata, sizeof(tdata)); // setup indexes data ibuffer.reserve(6); ibuffer.count = 6; memcpy(ibuffer.data, idata, sizeof(idata)); } void WgMeshData::clear() { vbuffer.clear(); tbuffer.clear(); ibuffer.clear(); voffset = 0; toffset = 0; ioffset = 0; }src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgBindGroups.h000664 001750 001750 00000006111 15164251010 036740 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_BIND_GROUPS_H_ #define _TVG_WG_BIND_GROUPS_H_ #include class WgBindGroupLayouts { private: WGPUDevice device{}; public: WGPUBindGroupLayout layoutTexSampled{}; WGPUBindGroupLayout layoutTexSampledBuff1Un{}; WGPUBindGroupLayout layoutTexSampledBuff2Un{}; WGPUBindGroupLayout layoutTexStrorage1WO{}; WGPUBindGroupLayout layoutTexStrorage1RO{}; WGPUBindGroupLayout layoutTexStrorage2RO{}; WGPUBindGroupLayout layoutTexStrorage3RO{}; WGPUBindGroupLayout layoutBuffer1Un{}; WGPUBindGroupLayout layoutBuffer2Un{}; WGPUBindGroupLayout layoutBuffer3Un{}; WGPUBindGroup createBindGroupTexSampled(WGPUSampler sampler, WGPUTextureView texView); WGPUBindGroup createBindGroupTexSampledBuff1Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff); WGPUBindGroup createBindGroupTexSampledBuff2Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff0, WGPUBuffer buff1); WGPUBindGroup createBindGroupStrorage1WO(WGPUTextureView texView); // for read-only access in compute shaders, use texture_2d instead of texture_storage_2d WGPUBindGroup createBindGroupStrorage1RO(WGPUTextureView texView); WGPUBindGroup createBindGroupStrorage2RO(WGPUTextureView texView0, WGPUTextureView texView1); WGPUBindGroup createBindGroupStrorage3RO(WGPUTextureView texView0, WGPUTextureView texView1, WGPUTextureView texView2); WGPUBindGroup createBindGroupBuffer1Un(WGPUBuffer buff); WGPUBindGroup createBindGroupBuffer1Un(WGPUBuffer buff, uint64_t offset, uint64_t size); WGPUBindGroup createBindGroupBuffer2Un(WGPUBuffer buff0, WGPUBuffer buff1); WGPUBindGroup createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2); void releaseBindGroup(WGPUBindGroup& bindGroup); void releaseBindGroupLayout(WGPUBindGroupLayout& bindGroupLayout); void initialize(WGPUDevice device); void release(); }; #endif // _TVG_WG_BIND_GROUPS_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgCanvas.cpp000664 001750 001750 00000017650 15164251010 034263 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgCanvas.h" #include "tvgTaskScheduler.h" #include "tvgLoadModule.h" #ifdef THORVG_SW_RASTER_SUPPORT #include "tvgSwRenderer.h" #endif #ifdef THORVG_GL_RASTER_SUPPORT #include "tvgGlRenderer.h" #endif #ifdef THORVG_WG_RASTER_SUPPORT #include "tvgWgRenderer.h" #endif /************************************************************************/ /* Canvas Class Implementation */ /************************************************************************/ Canvas::Canvas():pImpl(new Impl) { } Canvas::~Canvas() { delete(pImpl); } const list& Canvas::paints() const noexcept { return pImpl->scene->paints(); } Result Canvas::add(Paint* target, Paint* at) noexcept { if (target) return pImpl->add(target, at); return Result::InvalidArguments; } Result Canvas::draw(bool clear) noexcept { TVGLOG("RENDERER", "Draw S. -------------------------------- Canvas(%p)", this); auto ret = pImpl->draw(clear); TVGLOG("RENDERER", "Draw E. -------------------------------- Canvas(%p)", this); return ret; } Result Canvas::update() noexcept { TVGLOG("RENDERER", "Update S. ------------------------------ Canvas(%p)", this); auto ret = pImpl->update(); TVGLOG("RENDERER", "Update E. ------------------------------ Canvas(%p)", this); return ret; } Result Canvas::remove(Paint* paint) noexcept { return pImpl->remove(paint); } Result Canvas::viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept { return pImpl->viewport(x, y, w, h); } Result Canvas::sync() noexcept { return pImpl->sync(); } /************************************************************************/ /* SwCanvas Class Implementation */ /************************************************************************/ SwCanvas::SwCanvas() = default; SwCanvas::~SwCanvas() { #ifdef THORVG_SW_RASTER_SUPPORT SwRenderer::term(); #endif } Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs) noexcept { #ifdef THORVG_SW_RASTER_SUPPORT if (cs == ColorSpace::Unknown) return Result::InvalidArguments; if (cs == ColorSpace::Grayscale8) return Result::NonSupport; if (pImpl->status == Status::Updating || pImpl->status == Status::Drawing) { return Result::InsufficientCondition; } //We know renderer type, avoid dynamic_cast for performance. auto renderer = static_cast(pImpl->renderer); if (!renderer) return Result::MemoryCorruption; if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments; pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}}; renderer->viewport(pImpl->vport); //FIXME: The value must be associated with an individual canvas instance. ImageLoader::cs = static_cast(cs); //Paints must be updated again with this new target. pImpl->status = Status::Damaged; return Result::Success; #endif return Result::NonSupport; } SwCanvas* SwCanvas::gen(EngineOption op) noexcept { #ifdef THORVG_SW_RASTER_SUPPORT if (engineInit > 0) { auto renderer = new SwRenderer(TaskScheduler::threads(), op); renderer->ref(); auto ret = new SwCanvas; ret->pImpl->renderer = renderer; return ret; } #endif return nullptr; } /************************************************************************/ /* GlCanvas Class Implementation */ /************************************************************************/ GlCanvas::GlCanvas() = default; GlCanvas::~GlCanvas() { #ifdef THORVG_GL_RASTER_SUPPORT GlRenderer::term(); #endif } Result GlCanvas::target(void* display, void* surface, void* context, int32_t id, uint32_t w, uint32_t h, ColorSpace cs) noexcept { #ifdef THORVG_GL_RASTER_SUPPORT if (cs != ColorSpace::ABGR8888S) return Result::NonSupport; if (pImpl->status == Status::Updating || pImpl->status == Status::Drawing) { return Result::InsufficientCondition; } //We know renderer type, avoid dynamic_cast for performance. auto renderer = static_cast(pImpl->renderer); if (!renderer) return Result::MemoryCorruption; if (!renderer->target(display, surface, context, id, w, h, cs)) return Result::Unknown; pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}}; renderer->viewport(pImpl->vport); //Paints must be updated again with this new target. pImpl->status = Status::Damaged; return Result::Success; #endif return Result::NonSupport; } GlCanvas* GlCanvas::gen(EngineOption op) noexcept { #ifdef THORVG_GL_RASTER_SUPPORT if (engineInit > 0) { if (op == EngineOption::SmartRender) TVGLOG("RENDERER", "GlCanvas doesn't support Smart Rendering"); auto renderer = GlRenderer::gen(TaskScheduler::threads()); if (!renderer) return nullptr; renderer->ref(); auto ret = new GlCanvas; ret->pImpl->renderer = renderer; return ret; } #endif return nullptr; } /************************************************************************/ /* WgCanvas Class Implementation */ /************************************************************************/ WgCanvas::WgCanvas() = default; WgCanvas::~WgCanvas() { #ifdef THORVG_WG_RASTER_SUPPORT auto renderer = static_cast(pImpl->renderer); renderer->target(nullptr, nullptr, nullptr, 0, 0, ColorSpace::Unknown); WgRenderer::term(); #endif } Result WgCanvas::target(void* device, void* instance, void* target, uint32_t w, uint32_t h, ColorSpace cs, int type) noexcept { #ifdef THORVG_WG_RASTER_SUPPORT if (cs != ColorSpace::ABGR8888S) return Result::NonSupport; if (pImpl->status == Status::Updating || pImpl->status == Status::Drawing) { return Result::InsufficientCondition; } //We know renderer type, avoid dynamic_cast for performance. auto renderer = static_cast(pImpl->renderer); if (!renderer) return Result::MemoryCorruption; if (!renderer->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, cs, type)) return Result::Unknown; pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}}; renderer->viewport(pImpl->vport); //Paints must be updated again with this new target. pImpl->status = Status::Damaged; return Result::Success; #endif return Result::NonSupport; } WgCanvas* WgCanvas::gen(EngineOption op) noexcept { #ifdef THORVG_WG_RASTER_SUPPORT if (engineInit > 0) { if (op == EngineOption::SmartRender) TVGLOG("RENDERER", "WgCanvas doesn't support Smart Rendering"); auto renderer = WgRenderer::gen(TaskScheduler::threads()); renderer->ref(); auto ret = new WgCanvas; ret->pImpl->renderer = renderer; return ret; } #endif return nullptr; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test9.lot000664 001750 001750 00000003241 15164251010 033776 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":30,"ip":0,"op":60,"w":400,"h":400,"nm":"MultipleModifiersCombination","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"RoundedCorner+TrimPath+Repeater","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":60,"s":[360]}]},"p":{"a":0,"k":[200,200,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[30,30]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":3},"nm":"SmallRectangle"},{"ty":"rd","r":{"a":1,"k":[{"t":0,"s":[0],"e":[15]},{"t":30,"s":[15],"e":[0]},{"t":60,"s":[0]}]},"nm":"RoundCornersAnimated"},{"ty":"tm","s":{"a":1,"k":[{"t":0,"s":[0],"e":[50]},{"t":60,"s":[50]}]},"e":{"a":0,"k":[100]},"o":{"a":0,"k":[0]},"m":1,"nm":"TrimPath"},{"ty":"st","c":{"a":0,"k":[0.2,0.6,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"ml":4,"nm":"Stroke"},{"ty":"rp","c":{"a":0,"k":6},"o":{"a":0,"k":0},"m":1,"tr":{"ty":"tr","p":{"a":0,"k":[50,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":60},"so":{"a":0,"k":100},"eo":{"a":0,"k":100}},"nm":"Repeater"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"OffsetPath+RoundedCorner","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[200,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[60,60]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"rd","r":{"a":0,"k":20},"nm":"RoundCorners"},{"ty":"op","a":{"a":1,"k":[{"t":0,"s":[-5],"e":[5]},{"t":30,"s":[5],"e":[-5]},{"t":60,"s":[-5]}]},"lj":1,"ml":4,"nm":"OffsetPathAnimated"},{"ty":"fl","c":{"a":0,"k":[1,0.3,0.3,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":0}]}loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int32array.inc.h000664 001750 001750 00000001711 15164251010 052773 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Int32Array description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 4 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_INT32_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_INT32ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/re-token.h000664 001750 001750 00000003603 15164251010 044004 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 RE_TOKEN_H #define RE_TOKEN_H #include "ecma-globals.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup parser Parser * @{ * * \addtogroup regexparser Regular expression * @{ * * \addtogroup regexparser_parser Parser * @{ */ /** * RegExp token type definitions */ typedef enum { RE_TOK_EOF, /**< EOF */ RE_TOK_BACKREFERENCE, /**< "\[0..9]" */ RE_TOK_ALTERNATIVE, /**< "|" */ RE_TOK_ASSERT_START, /**< "^" */ RE_TOK_ASSERT_END, /**< "$" */ RE_TOK_PERIOD, /**< "." */ RE_TOK_START_CAPTURE_GROUP, /**< "(" */ RE_TOK_START_NON_CAPTURE_GROUP, /**< "(?:" */ RE_TOK_END_GROUP, /**< ")" */ RE_TOK_ASSERT_LOOKAHEAD, /**< "(?=" */ RE_TOK_ASSERT_WORD_BOUNDARY, /**< "\b" */ RE_TOK_ASSERT_NOT_WORD_BOUNDARY, /**< "\B" */ RE_TOK_CLASS_ESCAPE, /**< "\d \D \w \W \s \S" */ RE_TOK_CHAR_CLASS, /**< "[ ]" */ RE_TOK_CHAR, /**< any character */ } re_token_type_t; /** * RegExp token */ typedef struct { uint32_t value; /**< value of the token */ uint32_t qmin; /**< minimum number of token iterations */ uint32_t qmax; /**< maximum number of token iterations */ re_token_type_t type; /**< type of the token */ bool greedy; /**< type of iteration */ } re_token_t; /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ #endif /* !RE_TOKEN_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieExpressions.h000664 001750 001750 00000014370 15164251010 037260 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_EXPRESSIONS_H_ #define _TVG_LOTTIE_EXPRESSIONS_H_ #include "tvgCommon.h" #include "tvgLottieData.h" struct LottieExpression; struct LottieComposition; struct LottieLayer; struct LottieModifier; #ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT #include "jerryscript.h" struct LottieExpressions { template bool result(float frameNo, NumType& out, LottieExpression* exp) { auto bm_rt = evaluate(frameNo, exp); if (jerry_value_is_undefined(bm_rt)) return false; if (jerry_value_is_number(bm_rt)) { out = (NumType) jerry_value_as_number(bm_rt); } else if (auto prop = static_cast(jerry_object_get_native_ptr(bm_rt, nullptr))) { out = (*prop)(frameNo); } jerry_value_free(bm_rt); return true; } template bool result(float frameNo, Point& out, LottieExpression* exp) { auto bm_rt = evaluate(frameNo, exp); if (jerry_value_is_undefined(bm_rt)) return false; if (auto prop = static_cast(jerry_object_get_native_ptr(bm_rt, nullptr))) { out = (*prop)(frameNo); } else { out = toPoint2d(bm_rt); } jerry_value_free(bm_rt); return true; } template bool result(float frameNo, RGB32& out, LottieExpression* exp) { auto bm_rt = evaluate(frameNo, exp); if (jerry_value_is_undefined(bm_rt)) return false; if (auto color = static_cast(jerry_object_get_native_ptr(bm_rt, nullptr))) { out = (*color)(frameNo); } else { out = toColor(bm_rt); } jerry_value_free(bm_rt); return true; } template bool result(float frameNo, Fill* fill, LottieExpression* exp) { auto bm_rt = evaluate(frameNo, exp); if (jerry_value_is_undefined(bm_rt)) return false; if (auto colorStop = static_cast(jerry_object_get_native_ptr(bm_rt, nullptr))) { (*colorStop)(frameNo, fill, this); } jerry_value_free(bm_rt); return true; } template bool result(float frameNo, RenderPath& out, Matrix* transform, LottieModifier* modifier, LottieExpression* exp) { auto bm_rt = evaluate(frameNo, exp); if (jerry_value_is_undefined(bm_rt)) return false; if (auto pathset = static_cast(jerry_object_get_native_ptr(bm_rt, nullptr))) { (*pathset)(frameNo, out, transform, nullptr, modifier); } jerry_value_free(bm_rt); return true; } bool result(float frameNo, TextDocument& doc, LottieExpression* exp) { auto bm_rt = evaluate(frameNo, exp); if (jerry_value_is_undefined(bm_rt)) return false; if (jerry_value_is_string(bm_rt)) { auto len = jerry_string_length(bm_rt); doc.text = tvg::realloc(doc.text, (len + 1) * sizeof(jerry_char_t)); jerry_string_to_buffer(bm_rt, JERRY_ENCODING_UTF8, (jerry_char_t*)doc.text, len); doc.text[len] = '\0'; } jerry_value_free(bm_rt); return true; } void update(float curTime); //singleton (no thread safety) static LottieExpressions* instance(); static void retrieve(LottieExpressions* instance); private: LottieExpressions(); ~LottieExpressions(); jerry_value_t evaluate(float frameNo, LottieExpression* exp); jerry_value_t buildGlobal(); void buildComp(LottieComposition* comp, float frameNo, LottieExpression* exp); void buildComp(jerry_value_t context, float frameNo, LottieLayer* comp, LottieExpression* exp); void buildGlobal(float frameNo, LottieExpression* exp); void buildWritables(LottieExpression* exp); Point toPoint2d(jerry_value_t obj); RGB32 toColor(jerry_value_t obj); //global object, attributes, methods jerry_value_t global; jerry_value_t comp; jerry_value_t thisComp; jerry_value_t thisLayer; jerry_value_t thisProperty; }; #else struct LottieExpressions { template bool result(TVG_UNUSED float, TVG_UNUSED NumType&, TVG_UNUSED LottieExpression*) { return false; } template bool result(TVG_UNUSED float, TVG_UNUSED Point&, LottieExpression*) { return false; } template bool result(TVG_UNUSED float, TVG_UNUSED RGB32&, TVG_UNUSED LottieExpression*) { return false; } template bool result(TVG_UNUSED float, TVG_UNUSED Fill*, TVG_UNUSED LottieExpression*) { return false; } template bool result(TVG_UNUSED float, TVG_UNUSED RenderPath&, TVG_UNUSED Matrix*, TVG_UNUSED LottieModifier*, TVG_UNUSED LottieExpression*) { return false; } bool result(TVG_UNUSED float, TVG_UNUSED TextDocument& doc, TVG_UNUSED LottieExpression*) { return false; } void update(TVG_UNUSED float) {} static LottieExpressions* instance() { return nullptr; } static void retrieve(TVG_UNUSED LottieExpressions* instance) {} }; #endif //THORVG_LOTTIE_EXPRESSIONS_SUPPORT #endif //_TVG_LOTTIE_EXPRESSIONS_H_external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-magic-strings.cpp000664 001750 001750 00000032627 15164251010 044162 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "jerry-config.h" #include "lit-magic-strings.h" #include "jcontext.h" #include "lit-strings.h" /** * Maximum number of external magic strings that can be registered. */ #define LIT_EXTERNAL_MAGIC_STRING_LIMIT (UINT32_MAX / 2) /** * Get number of external magic strings * * @return number of the strings, if there were registered, * zero - otherwise. */ uint32_t lit_get_magic_string_ex_count (void) { return JERRY_CONTEXT (lit_magic_string_ex_count); } /* lit_get_magic_string_ex_count */ /** * Get specified magic string as zero-terminated string * * @return pointer to zero-terminated magic string */ const lit_utf8_byte_t * lit_get_magic_string_utf8 (uint32_t id) /**< magic string id */ { static const lit_utf8_byte_t *const lit_magic_strings[] JERRY_ATTR_CONST_DATA = { /** @cond doxygen_suppress */ #define LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE(size, id) #define LIT_MAGIC_STRING_DEF(id, utf8_string) (const lit_utf8_byte_t *) utf8_string, #include "lit-magic-strings.inc.h" #undef LIT_MAGIC_STRING_DEF #undef LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE /** @endcond */ }; JERRY_ASSERT (id < LIT_NON_INTERNAL_MAGIC_STRING__COUNT); return lit_magic_strings[id]; } /* lit_get_magic_string_utf8 */ /** * Get size of specified magic string * * @return size in bytes */ lit_utf8_size_t lit_get_magic_string_size (uint32_t id) /**< magic string id */ { static const lit_magic_size_t lit_magic_string_sizes[] JERRY_ATTR_CONST_DATA = { /** @cond doxygen_suppress */ #define LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE(size, id) #define LIT_MAGIC_STRING_DEF(id, utf8_string) sizeof (utf8_string) - 1, #include "lit-magic-strings.inc.h" #undef LIT_MAGIC_STRING_DEF #undef LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE /** @endcond */ }; JERRY_ASSERT (id < LIT_NON_INTERNAL_MAGIC_STRING__COUNT); return lit_magic_string_sizes[id]; } /* lit_get_magic_string_size */ /** * Get the block start element with the given size from * the list of ECMA and implementation-defined magic string constants * * @return magic string id */ static lit_magic_string_id_t lit_get_magic_string_size_block_start (lit_utf8_size_t size) /**< magic string size */ { static const lit_magic_string_id_t lit_magic_string_size_block_starts[] JERRY_ATTR_CONST_DATA = { /** @cond doxygen_suppress */ #define LIT_MAGIC_STRING_DEF(id, utf8_string) #define LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE(size, id) id, #include "lit-magic-strings.inc.h" LIT_NON_INTERNAL_MAGIC_STRING__COUNT #undef LIT_MAGIC_STRING_DEF #undef LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE /** @endcond */ }; JERRY_ASSERT (size <= (sizeof (lit_magic_string_size_block_starts) / sizeof (lit_magic_string_id_t))); return lit_magic_string_size_block_starts[size]; } /* lit_get_magic_string_size_block_start */ /** * Get specified magic string as zero-terminated string from external table * * @return pointer to zero-terminated magic string */ const lit_utf8_byte_t * lit_get_magic_string_ex_utf8 (uint32_t id) /**< extern magic string id */ { JERRY_ASSERT (JERRY_CONTEXT (lit_magic_string_ex_array) && id < JERRY_CONTEXT (lit_magic_string_ex_count)); return JERRY_CONTEXT (lit_magic_string_ex_array)[id]; } /* lit_get_magic_string_ex_utf8 */ /** * Get size of specified external magic string * * @return size in bytes */ lit_utf8_size_t lit_get_magic_string_ex_size (uint32_t id) /**< external magic string id */ { return JERRY_CONTEXT (lit_magic_string_ex_sizes)[id]; } /* lit_get_magic_string_ex_size */ /** * Register external magic strings */ void lit_magic_strings_ex_set (const lit_utf8_byte_t *const *ex_str_items, /**< character arrays, representing * external magic strings' contents */ uint32_t count, /**< number of the strings */ const lit_utf8_size_t *ex_str_sizes) /**< sizes of the strings */ { JERRY_ASSERT (ex_str_items != NULL); JERRY_ASSERT (count > 0); JERRY_ASSERT (ex_str_sizes != NULL); JERRY_ASSERT (JERRY_CONTEXT (lit_magic_string_ex_array) == NULL); JERRY_ASSERT (JERRY_CONTEXT (lit_magic_string_ex_count) == 0); JERRY_ASSERT (JERRY_CONTEXT (lit_magic_string_ex_sizes) == NULL); /* Limit the number of external magic strings */ if (count > LIT_EXTERNAL_MAGIC_STRING_LIMIT) { count = LIT_EXTERNAL_MAGIC_STRING_LIMIT; } /* Set external magic strings information */ JERRY_CONTEXT (lit_magic_string_ex_array) = ex_str_items; JERRY_CONTEXT (lit_magic_string_ex_count) = count; JERRY_CONTEXT (lit_magic_string_ex_sizes) = ex_str_sizes; #ifndef JERRY_NDEBUG for (lit_magic_string_ex_id_t id = (lit_magic_string_ex_id_t) 0; id < JERRY_CONTEXT (lit_magic_string_ex_count); id = (lit_magic_string_ex_id_t) (id + 1)) { lit_utf8_size_t string_size = JERRY_CONTEXT (lit_magic_string_ex_sizes)[id]; /** * Check whether the strings are sorted by size and lexicographically, * e.g., "Bb" < "aa" < "aaa" < "xyz0". */ if (id > 0) { const lit_magic_string_ex_id_t prev_id = id - 1; const lit_utf8_size_t prev_string_size = lit_get_magic_string_ex_size (prev_id); JERRY_ASSERT (lit_is_valid_cesu8_string (lit_get_magic_string_ex_utf8 (id), string_size)); JERRY_ASSERT (prev_string_size <= string_size); if (prev_string_size == string_size) { const lit_utf8_byte_t *prev_ex_string_p = lit_get_magic_string_ex_utf8 (prev_id); const lit_utf8_byte_t *curr_ex_string_p = lit_get_magic_string_ex_utf8 (id); JERRY_ASSERT (memcmp (prev_ex_string_p, curr_ex_string_p, string_size) < 0); } } } #endif /* !JERRY_NDEBUG */ } /* lit_magic_strings_ex_set */ /** * Returns the magic string id of the argument string if it is available. * * @return id - if magic string id is found, * LIT_MAGIC_STRING__COUNT - otherwise. */ lit_magic_string_id_t lit_is_utf8_string_magic (const lit_utf8_byte_t *string_p, /**< utf-8 string */ lit_utf8_size_t string_size) /**< string size in bytes */ { if (string_size > lit_get_magic_string_size (LIT_NON_INTERNAL_MAGIC_STRING__COUNT - 1)) { return LIT_MAGIC_STRING__COUNT; } /**< The string must be in this id range. */ lit_utf8_size_t first = lit_get_magic_string_size_block_start (string_size); lit_utf8_size_t last = lit_get_magic_string_size_block_start (string_size + 1); while (first < last) { lit_utf8_size_t middle = ((first + last) / 2); /**< mid point of search */ int compare = memcmp (lit_get_magic_string_utf8 ((lit_magic_string_id_t) middle), string_p, string_size); if (compare == 0) { return (lit_magic_string_id_t) middle; } else if (compare > 0) { last = middle; } else { first = middle + 1; } } return LIT_MAGIC_STRING__COUNT; } /* lit_is_utf8_string_magic */ /** * Returns the magic string id of the argument string pair if it is available. * * @return id - if magic string id is found, * LIT_MAGIC_STRING__COUNT - otherwise. */ lit_magic_string_id_t lit_is_utf8_string_pair_magic (const lit_utf8_byte_t *string1_p, /**< first utf-8 string */ lit_utf8_size_t string1_size, /**< first string size in bytes */ const lit_utf8_byte_t *string2_p, /**< second utf-8 string */ lit_utf8_size_t string2_size) /**< second string size in bytes */ { lit_utf8_size_t total_string_size = string1_size + string2_size; if (total_string_size > lit_get_magic_string_size (LIT_NON_INTERNAL_MAGIC_STRING__COUNT - 1)) { return LIT_MAGIC_STRING__COUNT; } /**< The string must be in this id range. */ lit_utf8_size_t first = lit_get_magic_string_size_block_start (total_string_size); lit_utf8_size_t last = lit_get_magic_string_size_block_start (total_string_size + 1); while (first < last) { lit_utf8_size_t middle = ((first + last) / 2); /**< mid point of search */ const lit_utf8_byte_t *middle_string_p = lit_get_magic_string_utf8 ((lit_magic_string_id_t) middle); int compare = memcmp (middle_string_p, string1_p, string1_size); if (compare == 0) { compare = memcmp (middle_string_p + string1_size, string2_p, string2_size); } if (compare == 0) { return (lit_magic_string_id_t) middle; } else if (compare > 0) { last = middle; } else { first = middle + 1; } } return LIT_MAGIC_STRING__COUNT; } /* lit_is_utf8_string_pair_magic */ /** * Returns the ex magic string id of the argument string if it is available. * * @return id - if magic string id is found, * lit_get_magic_string_ex_count () - otherwise. */ lit_magic_string_ex_id_t lit_is_ex_utf8_string_magic (const lit_utf8_byte_t *string_p, /**< utf-8 string */ lit_utf8_size_t string_size) /**< string size in bytes */ { const uint32_t magic_string_ex_count = lit_get_magic_string_ex_count (); if (magic_string_ex_count == 0 || string_size > lit_get_magic_string_ex_size (magic_string_ex_count - 1)) { return (lit_magic_string_ex_id_t) magic_string_ex_count; } lit_magic_string_ex_id_t first = 0; lit_magic_string_ex_id_t last = (lit_magic_string_ex_id_t) magic_string_ex_count; while (first < last) { const lit_magic_string_ex_id_t middle = (first + last) / 2; const lit_utf8_byte_t *ext_string_p = lit_get_magic_string_ex_utf8 (middle); const lit_utf8_size_t ext_string_size = lit_get_magic_string_ex_size (middle); if (string_size == ext_string_size) { const int string_compare = memcmp (ext_string_p, string_p, string_size); if (string_compare == 0) { return middle; } else if (string_compare < 0) { first = middle + 1; } else { last = middle; } } else if (string_size > ext_string_size) { first = middle + 1; } else { last = middle; } } return (lit_magic_string_ex_id_t) magic_string_ex_count; } /* lit_is_ex_utf8_string_magic */ /** * Returns the ex magic string id of the argument string pair if it is available. * * @return id - if magic string id is found, * lit_get_magic_string_ex_count () - otherwise. */ lit_magic_string_ex_id_t lit_is_ex_utf8_string_pair_magic (const lit_utf8_byte_t *string1_p, /**< first utf-8 string */ lit_utf8_size_t string1_size, /**< first string size in bytes */ const lit_utf8_byte_t *string2_p, /**< second utf-8 string */ lit_utf8_size_t string2_size) /**< second string size in bytes */ { const uint32_t magic_string_ex_count = lit_get_magic_string_ex_count (); const lit_utf8_size_t total_string_size = string1_size + string2_size; if (magic_string_ex_count == 0 || total_string_size > lit_get_magic_string_ex_size (magic_string_ex_count - 1)) { return (lit_magic_string_ex_id_t) magic_string_ex_count; } lit_magic_string_ex_id_t first = 0; lit_magic_string_ex_id_t last = (lit_magic_string_ex_id_t) magic_string_ex_count; while (first < last) { const lit_magic_string_ex_id_t middle = (first + last) / 2; const lit_utf8_byte_t *ext_string_p = lit_get_magic_string_ex_utf8 (middle); const lit_utf8_size_t ext_string_size = lit_get_magic_string_ex_size (middle); if (total_string_size == ext_string_size) { int string_compare = memcmp (ext_string_p, string1_p, string1_size); if (string_compare == 0) { string_compare = memcmp (ext_string_p + string1_size, string2_p, string2_size); } if (string_compare == 0) { return middle; } else if (string_compare < 0) { first = middle + 1; } else { last = middle; } } else if (total_string_size > ext_string_size) { first = middle + 1; } else { last = middle; } } return (lit_magic_string_ex_id_t) magic_string_ex_count; } /* lit_is_ex_utf8_string_pair_magic */ /** * Copy magic string to buffer * * Warning: * the routine requires that buffer size is enough * * @return pointer to the byte next to the last copied in the buffer */ lit_utf8_byte_t * lit_copy_magic_string_to_buffer (lit_magic_string_id_t id, /**< magic string id */ lit_utf8_byte_t *buffer_p, /**< destination buffer */ lit_utf8_size_t buffer_size) /**< size of buffer */ { const lit_utf8_byte_t *magic_string_bytes_p = lit_get_magic_string_utf8 (id); lit_utf8_size_t magic_string_bytes_count = lit_get_magic_string_size (id); const lit_utf8_byte_t *str_iter_p = magic_string_bytes_p; lit_utf8_byte_t *buf_iter_p = buffer_p; while (magic_string_bytes_count--) { *buf_iter_p++ = *str_iter_p++; } return buf_iter_p; } /* lit_copy_magic_string_to_buffer */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/schema.h000664 001750 001750 00000436552 15164251010 036277 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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 "stringbuffer.h" #include "error/en.h" #include "uri.h" #include // 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 #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 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) #elif defined(_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 PrintInvalidKeywordData(const char* keyword) { printf(" Fail keyword: '%s'\n", keyword); } inline void PrintInvalidKeywordData(const wchar_t* keyword) { wprintf(L" Fail keyword: '%ls'\n", keyword); } inline void PrintInvalidDocumentData(const char* document) { printf(" Fail document: '%s'\n", document); } inline void PrintInvalidDocumentData(const wchar_t* document) { wprintf(L" Fail document: '%ls'\n", document); } inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) { printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d); } inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) { wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d); } inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) { printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved); } inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) { wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved); } inline void PrintMethodData(const char* method) { printf("%s\n", method); } inline void PrintMethodData(const char* method, bool b) { printf("%s, Data: '%s'\n", method, b ? "true" : "false"); } inline void PrintMethodData(const char* method, int64_t i) { printf("%s, Data: '%" PRId64 "'\n", method, i); } inline void PrintMethodData(const char* method, uint64_t u) { printf("%s, Data: '%" PRIu64 "'\n", method, u); } inline void PrintMethodData(const char* method, double d) { printf("%s, Data: '%lf'\n", method, d); } inline void PrintMethodData(const char* method, const char* s) { printf("%s, Data: '%s'\n", method, s); } inline void PrintMethodData(const char* method, const wchar_t* s) { wprintf(L"%hs, Data: '%ls'\n", method, s); } inline void PrintMethodData(const char* method, const char* s1, const char* s2) { printf("%s, Data: '%s', '%s'\n", method, s1, s2); } inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) { wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2); } } // namespace internal #endif // RAPIDJSON_SCHEMA_VERBOSE #ifndef RAPIDJSON_SCHEMA_PRINT #if RAPIDJSON_SCHEMA_VERBOSE #define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__) #else #define RAPIDJSON_SCHEMA_PRINT(name, ...) #endif #endif /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_INVALID_KEYWORD_RETURN #define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ RAPIDJSON_MULTILINEMACRO_BEGIN\ context.invalidCode = code;\ context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\ return false;\ RAPIDJSON_MULTILINEMACRO_END /////////////////////////////////////////////////////////////////////////////// // ValidateFlag /*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS \ingroup RAPIDJSON_CONFIG \brief User-defined kValidateDefaultFlags definition. User can define this as any \c ValidateFlag combinations. */ #ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS #define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags #endif //! Combination of validate flags /*! \see */ enum ValidateFlag { kValidateNoFlags = 0, //!< No flags are set. kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. kValidateReadFlag = 2, //!< Validation is for a read semantic. kValidateWriteFlag = 4, //!< Validation is for a write semantic. kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS }; /////////////////////////////////////////////////////////////////////////////// // Specification enum SchemaDraft { kDraftUnknown = -1, kDraftNone = 0, kDraft03 = 3, kDraftMin = 4, //!< Current minimum supported draft kDraft04 = 4, kDraft05 = 5, kDraftMax = 5, //!< Current maximum supported draft kDraft06 = 6, kDraft07 = 7, kDraft2019_09 = 8, kDraft2020_12 = 9 }; enum OpenApiVersion { kVersionUnknown = -1, kVersionNone = 0, kVersionMin = 2, //!< Current minimum supported version kVersion20 = 2, kVersion30 = 3, kVersionMax = 3, //!< Current maximum supported version kVersion31 = 4, }; struct Specification { Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {} Specification(OpenApiVersion o) : oapi(o) { if (oapi == kVersion20) draft = kDraft04; else if (oapi == kVersion30) draft = kDraft05; else if (oapi == kVersion31) draft = kDraft2020_12; else draft = kDraft04; } ~Specification() {} bool IsSupported() const { return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax))); } SchemaDraft draft; OpenApiVersion oapi; }; /////////////////////////////////////////////////////////////////////////////// // Forward declarations template class GenericSchemaDocument; namespace internal { template class Schema; /////////////////////////////////////////////////////////////////////////////// // ISchemaValidator class ISchemaValidator { public: virtual ~ISchemaValidator() {} virtual bool IsValid() const = 0; virtual void SetValidateFlags(unsigned flags) = 0; virtual unsigned GetValidateFlags() const = 0; }; /////////////////////////////////////////////////////////////////////////////// // ISchemaStateFactory template class ISchemaStateFactory { public: virtual ~ISchemaStateFactory() {} virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0; virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; virtual void* CreateHasher() = 0; virtual uint64_t GetHashCode(void* hasher) = 0; virtual void DestroyHasher(void* hasher) = 0; virtual void* MallocState(size_t size) = 0; virtual void FreeState(void* p) = 0; }; /////////////////////////////////////////////////////////////////////////////// // IValidationErrorHandler template class IValidationErrorHandler { public: typedef typename SchemaType::Ch Ch; typedef typename SchemaType::SValue SValue; virtual ~IValidationErrorHandler() {} virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; virtual void NotMultipleOf(double actual, const SValue& expected) = 0; virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; virtual void DisallowedItem(SizeType index) = 0; virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; virtual void StartMissingProperties() = 0; virtual void AddMissingProperty(const SValue& name) = 0; virtual bool EndMissingProperties() = 0; virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; virtual void StartDependencyErrors() = 0; virtual void StartMissingDependentProperties() = 0; virtual void AddMissingDependentProperty(const SValue& targetName) = 0; virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; virtual void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) = 0; virtual bool EndDependencyErrors() = 0; virtual void DisallowedValue(const ValidateErrorCode code) = 0; virtual void StartDisallowedType() = 0; virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0; virtual void Disallowed() = 0; virtual void DisallowedWhenWriting() = 0; virtual void DisallowedWhenReading() = 0; }; /////////////////////////////////////////////////////////////////////////////// // Hasher // For comparison of compound value template 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(i); return WriteNumber(n); } bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } bool Double(double d) { Number n; if (d < 0) n.u.i = static_cast(d); else n.u.u = static_cast(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(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() = h; return true; } bool StartArray() { return true; } bool EndArray(SizeType elementCount) { uint64_t h = Hash(0, kArrayType); uint64_t* e = stack_.template Pop(elementCount); for (SizeType i = 0; i < elementCount; i++) h = Hash(h, e[i]); // Use hash to achieve element order sensitive *stack_.template Push() = h; return true; } bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } uint64_t GetHashCode() const { RAPIDJSON_ASSERT(IsValid()); return *stack_.template Top(); } 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(data); for (size_t i = 0; i < len; i++) h = Hash(h, d[i]); *stack_.template Push() = 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 stack_; }; /////////////////////////////////////////////////////////////////////////////// // SchemaValidationContext template struct SchemaValidationContext { typedef Schema SchemaType; typedef ISchemaStateFactory SchemaValidatorFactoryType; typedef IValidationErrorHandler ErrorHandlerType; typedef typename SchemaType::ValueType ValueType; typedef typename ValueType::Ch Ch; enum PatternValidatorType { kPatternValidatorOnly, kPatternValidatorWithProperty, kPatternValidatorWithAdditionalProperty }; SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) : factory(f), error_handler(eh), schema(s), flags(fl), valueSchema(), invalidKeyword(), invalidCode(), hasher(), arrayElementHashCodes(), validators(), validatorCount(), patternPropertiesValidators(), patternPropertiesValidatorCount(), patternPropertiesSchemas(), patternPropertiesSchemaCount(), valuePatternValidatorType(kPatternValidatorOnly), propertyExist(), inArray(false), valueUniqueness(false), arrayUniqueness(false) { } ~SchemaValidationContext() { if (hasher) factory.DestroyHasher(hasher); if (validators) { for (SizeType i = 0; i < validatorCount; i++) { if (validators[i]) { factory.DestroySchemaValidator(validators[i]); } } factory.FreeState(validators); } if (patternPropertiesValidators) { for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) { if (patternPropertiesValidators[i]) { factory.DestroySchemaValidator(patternPropertiesValidators[i]); } } factory.FreeState(patternPropertiesValidators); } if (patternPropertiesSchemas) factory.FreeState(patternPropertiesSchemas); if (propertyExist) factory.FreeState(propertyExist); } SchemaValidatorFactoryType& factory; ErrorHandlerType& error_handler; const SchemaType* schema; unsigned flags; const SchemaType* valueSchema; const Ch* invalidKeyword; ValidateErrorCode invalidCode; 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 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 Context; typedef Schema SchemaType; typedef GenericValue SValue; typedef IValidationErrorHandler ErrorHandler; typedef GenericUri UriType; friend class GenericSchemaDocument; Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) : allocator_(allocator), uri_(schemaDocument->GetURI(), *allocator), id_(id, allocator), spec_(schemaDocument->GetSpecification()), pointer_(p, 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), defaultValueLength_(0), readOnly_(false), writeOnly_(false), nullable_(false) { GenericStringBuffer sb; p.StringifyUriFragment(sb); RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString()); typedef typename ValueType::ConstValueIterator ConstValueIterator; typedef typename ValueType::ConstMemberIterator ConstMemberIterator; // PR #1393 // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite // recursion (with recursive schemas), since schemaDocument->getSchema() is always // checked before creating a new one. Don't cache typeless_, though. if (this != typeless_) { typedef typename SchemaDocumentType::SchemaEntry SchemaEntry; SchemaEntry *entry = schemaDocument->schemaMap_.template Push(); new (entry) SchemaEntry(pointer_, this, true, allocator_); schemaDocument->AddSchemaRefs(this); } if (!value.IsObject()) return; // If we have an id property, resolve it with the in-scope id // Not supported for open api 2.0 or 3.0 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (const ValueType* v = GetMember(value, GetIdString())) { if (v->IsString()) { UriType local(*v, allocator); id_ = local.Resolve(id_, allocator); RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString()); } } 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(allocator_->Malloc(sizeof(uint64_t) * v->Size())); for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { typedef Hasher > EnumHasherType; char buffer[256u + 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); // AnyOf, OneOf, Not not supported for open api 2.0 if (schemaDocument && spec_.oapi != kVersion20) { AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); if (const ValueType* v = GetMember(value, GetNotString())) { schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_); 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); // Dependencies not supported for open api 2.0 and 3.0 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) 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(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, id_); } } // PatternProperties not supported for open api 2.0 and 3.0 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { PointerType q = p.Append(GetPatternPropertiesString(), allocator_); patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); patternPropertyCount_ = 0; for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { new (&patternProperties_[patternPropertyCount_]) PatternProperty(); PointerType r = q.Append(itr->name, allocator_); patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r); schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_); 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; } } // Dependencies not supported for open api 2.0 and 3.0 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) 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(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, id_); 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, id_); } 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, id_); else if (v->IsArray()) { // Tuple validation itemsTuple_ = static_cast(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, id_); } } AssignIfExist(minItems_, value, GetMinItemsString()); AssignIfExist(maxItems_, value, GetMaxItemsString()); // AdditionalItems not supported for openapi 2.0 and 3.0 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) 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, id_); } AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); // String AssignIfExist(minLength_, value, GetMinLengthString()); AssignIfExist(maxLength_, value, GetMaxLengthString()); if (const ValueType* v = GetMember(value, GetPatternString())) pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_)); // 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_); // Default if (const ValueType* v = GetMember(value, GetDefaultValueString())) if (v->IsString()) defaultValueLength_ = v->GetStringLength(); // ReadOnly - open api only (until draft 7 supported) // WriteOnly - open api 3 only (until draft 7 supported) // Both can't be true if (spec_.oapi != kVersionNone) AssignIfExist(readOnly_, value, GetReadOnlyString()); if (spec_.oapi >= kVersion30) AssignIfExist(writeOnly_, value, GetWriteOnlyString()); if (readOnly_ && writeOnly_) schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p); // Nullable - open api 3 only // If true add 'null' as allowable type if (spec_.oapi >= kVersion30) { AssignIfExist(nullable_, value, GetNullableString()); if (nullable_) AddType(GetNullString()); } } ~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 } const SValue& GetURI() const { return uri_; } const UriType& GetId() const { return id_; } const Specification& GetSpecification() const { return spec_; } const PointerType& GetPointer() const { return pointer_; } bool BeginValue(Context& context) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue"); 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 { context.error_handler.DisallowedItem(context.arrayElementIndex); // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error context.valueSchema = typeless_; // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set context.arrayElementIndex++; RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems); } } else context.valueSchema = typeless_; context.arrayElementIndex++; } return true; } RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue"); // Only check pattern properties if we have validators 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) { context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); } } else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { if (!patternValid || !otherValid) { context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); } } else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); } } // For enums only check if we have a hasher if (enum_ && context.hasher) { const uint64_t h = context.factory.GetHashCode(context.hasher); for (SizeType i = 0; i < enumCount_; i++) if (enum_[i] == h) goto foundEnum; context.error_handler.DisallowedValue(kValidateErrorEnum); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum); foundEnum:; } // Only check allOf etc if we have validators if (context.validatorCount > 0) { if (allOf_.schemas) for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) if (!context.validators[i]->IsValid()) { context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf); } if (anyOf_.schemas) { for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) if (context.validators[i]->IsValid()) goto foundAny; context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf); foundAny:; } if (oneOf_.schemas) { bool oneValid = false; SizeType firstMatch = 0; for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) if (context.validators[i]->IsValid()) { if (oneValid) { context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch); } else { oneValid = true; firstMatch = i - oneOf_.begin; } } if (!oneValid) { context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf); } } if (not_ && context.validators[notValidatorIndex_]->IsValid()) { context.error_handler.Disallowed(); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot); } } return true; } bool Null(Context& context) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null"); if (!(type_ & (1 << kNullSchemaType))) { DisallowedType(context, GetNullString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); } return CreateParallelValidator(context); } bool Bool(Context& context, bool b) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b); if (!CheckBool(context, b)) return false; return CreateParallelValidator(context); } bool Int(Context& context, int i) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i); if (!CheckInt(context, i)) return false; return CreateParallelValidator(context); } bool Uint(Context& context, unsigned u) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u); if (!CheckUint(context, u)) return false; return CreateParallelValidator(context); } bool Int64(Context& context, int64_t i) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i); if (!CheckInt(context, i)) return false; return CreateParallelValidator(context); } bool Uint64(Context& context, uint64_t u) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u); if (!CheckUint(context, u)) return false; return CreateParallelValidator(context); } bool Double(Context& context, double d) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d); if (!(type_ & (1 << kNumberSchemaType))) { DisallowedType(context, GetNumberString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); } 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 { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str); if (!(type_ & (1 << kStringSchemaType))) { DisallowedType(context, GetStringString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); } if (minLength_ != 0 || maxLength_ != SizeType(~0)) { SizeType count; if (internal::CountStringCodePoint(str, length, &count)) { if (count < minLength_) { context.error_handler.TooShort(str, length, minLength_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength); } if (count > maxLength_) { context.error_handler.TooLong(str, length, maxLength_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength); } } } if (pattern_ && !IsPatternMatch(pattern_, str, length)) { context.error_handler.DoesNotMatch(str, length); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern); } return CreateParallelValidator(context); } bool StartObject(Context& context) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject"); if (!(type_ & (1 << kObjectSchemaType))) { DisallowedType(context, GetObjectString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); } if (hasDependencies_ || hasRequired_) { context.propertyExist = static_cast(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(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 { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str); 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 = 0; 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 (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 // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error context.valueSchema = typeless_; context.error_handler.DisallowedProperty(str, len); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties); } return true; } bool EndObject(Context& context, SizeType memberCount) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject"); if (hasRequired_) { context.error_handler.StartMissingProperties(); for (SizeType index = 0; index < propertyCount_; index++) if (properties_[index].required && !context.propertyExist[index]) if (properties_[index].schema->defaultValueLength_ == 0 ) context.error_handler.AddMissingProperty(properties_[index].name); if (context.error_handler.EndMissingProperties()) RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired); } if (memberCount < minProperties_) { context.error_handler.TooFewProperties(memberCount, minProperties_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties); } if (memberCount > maxProperties_) { context.error_handler.TooManyProperties(memberCount, maxProperties_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties); } if (hasDependencies_) { context.error_handler.StartDependencyErrors(); for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { const Property& source = properties_[sourceIndex]; if (context.propertyExist[sourceIndex]) { if (source.dependencies) { context.error_handler.StartMissingDependentProperties(); for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); context.error_handler.EndMissingDependentProperties(source.name); } else if (source.dependenciesSchema) { ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; if (!dependenciesValidator->IsValid()) context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); } } } if (context.error_handler.EndDependencyErrors()) RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); } return true; } bool StartArray(Context& context) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray"); context.arrayElementIndex = 0; context.inArray = true; // Ensure we note that we are in an array if (!(type_ & (1 << kArraySchemaType))) { DisallowedType(context, GetArrayString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); } return CreateParallelValidator(context); } bool EndArray(Context& context, SizeType elementCount) const { RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray"); context.inArray = false; if (elementCount < minItems_) { context.error_handler.TooFewItems(elementCount, minItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); } if (elementCount > maxItems_) { context.error_handler.TooManyItems(elementCount, maxItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); } return true; } static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) { switch (validateErrorCode) { case kValidateErrorMultipleOf: return GetMultipleOfString(); case kValidateErrorMaximum: return GetMaximumString(); case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same case kValidateErrorMinimum: return GetMinimumString(); case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same case kValidateErrorMaxLength: return GetMaxLengthString(); case kValidateErrorMinLength: return GetMinLengthString(); case kValidateErrorPattern: return GetPatternString(); case kValidateErrorMaxItems: return GetMaxItemsString(); case kValidateErrorMinItems: return GetMinItemsString(); case kValidateErrorUniqueItems: return GetUniqueItemsString(); case kValidateErrorAdditionalItems: return GetAdditionalItemsString(); case kValidateErrorMaxProperties: return GetMaxPropertiesString(); case kValidateErrorMinProperties: return GetMinPropertiesString(); case kValidateErrorRequired: return GetRequiredString(); case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString(); case kValidateErrorPatternProperties: return GetPatternPropertiesString(); case kValidateErrorDependencies: return GetDependenciesString(); case kValidateErrorEnum: return GetEnumString(); case kValidateErrorType: return GetTypeString(); case kValidateErrorOneOf: return GetOneOfString(); case kValidateErrorOneOfMatch: return GetOneOfString(); // Same case kValidateErrorAllOf: return GetAllOfString(); case kValidateErrorAnyOf: return GetAnyOfString(); case kValidateErrorNot: return GetNotString(); case kValidateErrorReadOnly: return GetReadOnlyString(); case kValidateErrorWriteOnly: return GetWriteOnlyString(); default: return GetNullString(); } } // 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(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') RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a') RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f') RAPIDJSON_STRING_(Id, 'i', 'd') RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r') RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i') RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y') RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e') #undef RAPIDJSON_STRING_ private: enum SchemaValueType { kNullSchemaType, kBooleanSchemaType, kObjectSchemaType, kArraySchemaType, kStringSchemaType, kNumberSchemaType, kIntegerSchemaType, kTotalSchemaType }; #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX typedef internal::GenericRegex RegexType; #elif RAPIDJSON_SCHEMA_USE_STDREGEX typedef std::basic_regex 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 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(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(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, id_); out.begin = validatorCount_; validatorCount_ += out.count; } } } #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX template RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { if (value.IsString()) { RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); if (!r->IsValid()) { sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); r->~RegexType(); AllocatorType::Free(r); r = 0; } return r; } return 0; } static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { GenericRegexSearch rs(*pattern); return rs.Search(str); } #elif RAPIDJSON_SCHEMA_USE_STDREGEX template RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { if (value.IsString()) { RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); try { return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); } catch (const std::regex_error& e) { sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); AllocatorType::Free(r); } } return 0; } static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { std::match_results r; return std::regex_search(str, str + length, r, *pattern); } #else template 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); } // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required. // Also creates a hasher for enums and array uniqueness, if required. // Also a useful place to add type-independent error checks. 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(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_); context.validatorCount = validatorCount_; // Always return after first failure for these sub-validators if (allOf_.schemas) CreateSchemaValidators(context, allOf_, false); if (anyOf_.schemas) CreateSchemaValidators(context, anyOf_, false); if (oneOf_.schemas) CreateSchemaValidators(context, oneOf_, false); if (not_) context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); if (hasSchemaDependencies_) { for (SizeType i = 0; i < propertyCount_; i++) if (properties_[i].dependenciesSchema) context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false); } } // Add any other type-independent checks here if (readOnly_ && (context.flags & kValidateWriteFlag)) { context.error_handler.DisallowedWhenWriting(); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly); } if (writeOnly_ && (context.flags & kValidateReadFlag)) { context.error_handler.DisallowedWhenReading(); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly); } return true; } void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const { for (SizeType i = 0; i < schemas.count; i++) context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors); } // 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 CheckBool(Context& context, bool) const { if (!(type_ & (1 << kBooleanSchemaType))) { DisallowedType(context, GetBooleanString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); } return true; } bool CheckInt(Context& context, int64_t i) const { if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { DisallowedType(context, GetIntegerString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); } if (!minimum_.IsNull()) { if (minimum_.IsInt64()) { if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); } } else if (minimum_.IsUint64()) { context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64() } else if (!CheckDoubleMinimum(context, static_cast(i))) return false; } if (!maximum_.IsNull()) { if (maximum_.IsInt64()) { if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); } } else if (maximum_.IsUint64()) { } /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { context.error_handler.NotMultipleOf(i, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; } return true; } bool CheckUint(Context& context, uint64_t i) const { if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { DisallowedType(context, GetIntegerString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); } if (!minimum_.IsNull()) { if (minimum_.IsUint64()) { if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); } } else if (minimum_.IsInt64()) /* do nothing */; // i >= 0 > minimum.Getint64() else if (!CheckDoubleMinimum(context, static_cast(i))) return false; } if (!maximum_.IsNull()) { if (maximum_.IsUint64()) { if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); } } else if (maximum_.IsInt64()) { context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_ } else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { if (i % multipleOf_.GetUint64() != 0) { context.error_handler.NotMultipleOf(i, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; } return true; } bool CheckDoubleMinimum(Context& context, double d) const { if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); } return true; } bool CheckDoubleMaximum(Context& context, double d) const { if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); } 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) { context.error_handler.NotMultipleOf(d, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); } return true; } void DisallowedType(Context& context, const ValueType& actualType) const { ErrorHandler& eh = context.error_handler; eh.StartDisallowedType(); if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); eh.EndDisallowedType(actualType); } 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_; SValue uri_; UriType id_; Specification spec_; PointerType pointer_; 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_; SizeType defaultValueLength_; bool readOnly_; bool writeOnly_; bool nullable_; }; template struct TokenHelper { RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { *documentStack.template Push() = '/'; char buffer[21]; size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); for (size_t i = 0; i < length; i++) *documentStack.template Push() = static_cast(buffer[i]); } }; // Partial specialized version for char to prevent buffer copying. template struct TokenHelper { RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { if (sizeof(SizeType) == 4) { char *buffer = documentStack.template Push(1 + 10); // '/' + uint *buffer++ = '/'; const char* end = internal::u32toa(index, buffer); documentStack.template Pop(static_cast(10 - (end - buffer))); } else { char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 *buffer++ = '/'; const char* end = internal::u64toa(index, buffer); documentStack.template Pop(static_cast(20 - (end - buffer))); } } }; } // namespace internal /////////////////////////////////////////////////////////////////////////////// // IGenericRemoteSchemaDocumentProvider template class IGenericRemoteSchemaDocumentProvider { public: typedef typename SchemaDocumentType::Ch Ch; typedef typename SchemaDocumentType::ValueType ValueType; typedef typename SchemaDocumentType::AllocatorType AllocatorType; virtual ~IGenericRemoteSchemaDocumentProvider() {} virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri uri, Specification& spec) { // Default implementation just calls through for compatibility // Following line suppresses unused parameter warning (void)spec; // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi); return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); } }; /////////////////////////////////////////////////////////////////////////////// // 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 class GenericSchemaDocument { public: typedef ValueT ValueType; typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; typedef Allocator AllocatorType; typedef typename ValueType::EncodingType EncodingType; typedef typename EncodingType::Ch Ch; typedef internal::Schema SchemaType; typedef GenericPointer PointerType; typedef GenericValue GValue; typedef GenericUri UriType; typedef GenericStringRef StringRefType; friend class internal::Schema; template friend class GenericSchemaValidator; //! Constructor. /*! Compile a JSON document into schema document. \param document A JSON document as source. \param uri The base URI of this schema document for purposes of violation reporting. \param uriLength Length of \c name, in code points. \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. \param pointer An optional JSON pointer to the start of the schema document \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04. */ explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, const PointerType& pointer = PointerType(), // PR #1393 const Specification& spec = Specification(kDraft04)) : remoteProvider_(remoteProvider), allocator_(allocator), ownAllocator_(), root_(), typeless_(), schemaMap_(allocator, kInitialSchemaMapSize), schemaRef_(allocator, kInitialSchemaRefSize), spec_(spec), error_(kObjectType), currentError_() { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument"); if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); Ch noUri[1] = {0}; uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); docId_ = UriType(uri_, allocator_); typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); // Establish the schema draft or open api version. // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document. SetSchemaSpecification(document); // Generate root schema, it will call CreateSchema() to create sub-schemas, // And call HandleRefSchema() if there are $ref. // PR #1393 use input pointer if supplied root_ = typeless_; if (pointer.GetTokenCount() == 0) { CreateSchemaRecursive(&root_, pointer, document, document, docId_); } else if (const ValueType* v = pointer.Get(document)) { CreateSchema(&root_, pointer, *v, document, docId_); } else { GenericStringBuffer sb; pointer.StringifyUriFragment(sb); SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch))); } 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_)), uri_(std::move(rhs.uri_)), docId_(std::move(rhs.docId_)), spec_(rhs.spec_), error_(std::move(rhs.error_)), currentError_(std::move(rhs.currentError_)) { rhs.remoteProvider_ = 0; rhs.allocator_ = 0; rhs.ownAllocator_ = 0; rhs.typeless_ = 0; } #endif //! Destructor ~GenericSchemaDocument() { while (!schemaMap_.Empty()) schemaMap_.template Pop(1)->~SchemaEntry(); if (typeless_) { typeless_->~SchemaType(); Allocator::Free(typeless_); } // these may contain some allocator data so clear before deleting ownAllocator_ uri_.SetNull(); error_.SetNull(); currentError_.SetNull(); RAPIDJSON_DELETE(ownAllocator_); } const GValue& GetURI() const { return uri_; } const Specification& GetSpecification() const { return spec_; } bool IsSupportedSpecification() const { return spec_.IsSupported(); } //! Static method to get the specification of any schema document // Returns kDraftNone if document is silent static const Specification GetSpecification(const ValueType& document) { SchemaDraft draft = GetSchemaDraft(document); if (draft != kDraftNone) return Specification(draft); else { OpenApiVersion oapi = GetOpenApiVersion(document); if (oapi != kVersionNone) return Specification(oapi); } return Specification(kDraftNone); } //! Get the root schema. const SchemaType& GetRoot() const { return *root_; } //! Gets the error object. GValue& GetError() { return error_; } const GValue& GetError() const { return error_; } static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) { switch (schemaErrorCode) { case kSchemaErrorStartUnknown: return GetStartUnknownString(); case kSchemaErrorRefPlainName: return GetRefPlainNameString(); case kSchemaErrorRefInvalid: return GetRefInvalidString(); case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString(); case kSchemaErrorRefUnknown: return GetRefUnknownString(); case kSchemaErrorRefCyclical: return GetRefCyclicalString(); case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString(); case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString(); case kSchemaErrorRegexInvalid: return GetRegexInvalidString(); case kSchemaErrorSpecUnknown: return GetSpecUnknownString(); case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString(); case kSchemaErrorSpecIllegal: return GetSpecIllegalString(); case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString(); default: return GetNullString(); } } //! Default error method void SchemaError(const SchemaErrorCode code, const PointerType& location) { currentError_ = GValue(kObjectType); AddCurrentError(code, location); } //! Method for error with single string value insert void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) { currentError_ = GValue(kObjectType); currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); AddCurrentError(code, location); } //! Method for error with invalid pointer void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) { currentError_ = GValue(kObjectType); currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); currentError_.AddMember(GetOffsetString(), static_cast(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_); AddCurrentError(code, location); } private: //! Prohibit copying GenericSchemaDocument(const GenericSchemaDocument&); //! Prohibit assignment GenericSchemaDocument& operator=(const GenericSchemaDocument&); typedef const PointerType* SchemaRefPtr; // PR #1393 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 AddErrorInstanceLocation(GValue& result, const PointerType& location) { GenericStringBuffer sb; location.StringifyUriFragment(sb); GValue instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), *allocator_); result.AddMember(GetInstanceRefString(), instanceRef, *allocator_); } void AddError(GValue& keyword, GValue& error) { typename GValue::MemberIterator member = error_.FindMember(keyword); if (member == error_.MemberEnd()) error_.AddMember(keyword, error, *allocator_); else { if (member->value.IsObject()) { GValue errors(kArrayType); errors.PushBack(member->value, *allocator_); member->value = errors; } member->value.PushBack(error, *allocator_); } } void AddCurrentError(const SchemaErrorCode code, const PointerType& location) { RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code)); currentError_.AddMember(GetErrorCodeString(), code, *allocator_); AddErrorInstanceLocation(currentError_, location); AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_); } #define RAPIDJSON_STRING_(name, ...) \ static const StringRefType& Get##name##String() {\ static const Ch s[] = { __VA_ARGS__, '\0' };\ static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ return v;\ } RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e') RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't') RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n') RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd') RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l') RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n') RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e') RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd') RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd') RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n') RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l') RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r') RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a') RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd') #undef RAPIDJSON_STRING_ // Static method to get schema draft of any schema document static SchemaDraft GetSchemaDraft(const ValueType& document) { static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; if (!document.IsObject()) { return kDraftNone; } // Get the schema draft from the $schema keyword at the supplied location typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString()); if (itr != document.MemberEnd()) { if (!itr->value.IsString()) return kDraftUnknown; const UriType draftUri(itr->value); // Check base uri for match if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04; if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05; if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06; if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07; if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03; if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09; if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12; return kDraftUnknown; } // $schema not found return kDraftNone; } // Get open api version of any schema document static OpenApiVersion GetOpenApiVersion(const ValueType& document) { static const Ch kVersion20String[] = { '2', '.', '0', '\0' }; static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level static SizeType len = internal::StrLen(kVersion30String); if (!document.IsObject()) { return kVersionNone; } // Get the open api version from the swagger / openapi keyword at the supplied location typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString()); if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString()); if (itr != document.MemberEnd()) { if (!itr->value.IsString()) return kVersionUnknown; const ValueType kVersion20Value(kVersion20String); if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly const ValueType kVersion30Value(kVersion30String); if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x const ValueType kVersion31Value(kVersion31String); if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x return kVersionUnknown; } // swagger or openapi not found return kVersionNone; } // Get the draft of the schema or the open api version (which implies the draft). // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on. void SetSchemaSpecification(const ValueType& document) { // Look for '$schema', 'swagger' or 'openapi' keyword at document root SchemaDraft docDraft = GetSchemaDraft(document); OpenApiVersion docOapi = GetOpenApiVersion(document); // Error if both in document if (docDraft != kDraftNone && docOapi != kVersionNone) SchemaError(kSchemaErrorSpecIllegal, PointerType()); // Use document draft or open api version if present or use spec from constructor if (docDraft != kDraftNone) spec_ = Specification(docDraft); else if (docOapi != kVersionNone) spec_ = Specification(docOapi); // Error if draft or version unknown if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown) SchemaError(kSchemaErrorSpecUnknown, PointerType()); else if (!spec_.IsSupported()) SchemaError(kSchemaErrorSpecUnsupported, PointerType()); } // Changed by PR #1393 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { if (v.GetType() == kObjectType) { UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_); for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid); } else if (v.GetType() == kArrayType) for (SizeType i = 0; i < v.Size(); i++) CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id); } // Changed by PR #1393 const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { RAPIDJSON_ASSERT(pointer.IsValid()); GenericStringBuffer sb; pointer.StringifyUriFragment(sb); RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString()); if (v.IsObject()) { if (const SchemaType* sc = GetSchema(pointer)) { if (schema) *schema = sc; AddSchemaRefs(const_cast(sc)); } else if (!HandleRefSchema(pointer, schema, v, document, id)) { // The new schema constructor adds itself and its $ref(s) to schemaMap_ SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id); if (schema) *schema = s; return s->GetId(); } } else { if (schema) *schema = typeless_; AddSchemaRefs(typeless_); } return id; } // Changed by PR #1393 // TODO should this return a UriType& ? bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) { typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString()); if (itr == v.MemberEnd()) return false; GenericStringBuffer sb; source.StringifyUriFragment(sb); RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString()); // Resolve the source pointer to the $ref'ed schema (finally) new (schemaRef_.template Push()) SchemaRefPtr(&source); if (itr->value.IsString()) { SizeType len = itr->value.GetStringLength(); if (len == 0) SchemaError(kSchemaErrorRefInvalid, source); else { // First resolve $ref against the in-scope id UriType scopeId = UriType(id, allocator_); UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString()); // See if the resolved $ref minus the fragment matches a resolved id in this document // Search from the root. Returns the subschema in the document and its absolute JSON pointer. PointerType basePointer = PointerType(); const ValueType *base = FindId(document, ref, basePointer, docId_, false); if (!base) { // Remote reference - call the remote document provider if (!remoteProvider_) SchemaError(kSchemaErrorRefNoRemoteProvider, source); else { if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) { const Ch* s = ref.GetFragString(); len = ref.GetFragStringLength(); if (len <= 1 || s[1] == '/') { // JSON pointer fragment, absolute in the remote schema const PointerType pointer(s, len, allocator_); if (!pointer.IsValid()) SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer); else { // Get the subschema if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) { if (schema) *schema = sc; AddSchemaRefs(const_cast(sc)); return true; } else SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); } } else // Plain name fragment, not allowed in remote schema SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); } else SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength()); } } else { // Local reference const Ch* s = ref.GetFragString(); len = ref.GetFragStringLength(); if (len <= 1 || s[1] == '/') { // JSON pointer fragment, relative to the resolved URI const PointerType relPointer(s, len, allocator_); if (!relPointer.IsValid()) SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer); else { // Get the subschema if (const ValueType *pv = relPointer.Get(*base)) { // Now get the absolute JSON pointer by adding relative to base PointerType pointer(basePointer, allocator_); for (SizeType i = 0; i < relPointer.GetTokenCount(); i++) pointer = pointer.Append(relPointer.GetTokens()[i], allocator_); if (IsCyclicRef(pointer)) SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); else { // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there // TODO: cache pointer <-> id mapping size_t unresolvedTokenIndex; scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); CreateSchema(schema, pointer, *pv, document, scopeId); return true; } } else SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); } } else { // Plain name fragment, relative to the resolved URI // Not supported in open api 2.0 and 3.0 PointerType pointer(allocator_); if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30) SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); // See if the fragment matches an id in this document. // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { if (IsCyclicRef(pointer)) SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); else { // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there // TODO: cache pointer <-> id mapping size_t unresolvedTokenIndex; scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); CreateSchema(schema, pointer, *pv, document, scopeId); return true; } } else SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); } } } } // Invalid/Unknown $ref if (schema) *schema = typeless_; AddSchemaRefs(typeless_); return true; } //! Find the first subschema with a resolved 'id' that matches the specified URI. // If full specified use all URI else ignore fragment. // If found, return a pointer to the subschema and its JSON pointer. // TODO cache pointer <-> id mapping ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const { SizeType i = 0; ValueType* resval = 0; UriType tempuri = UriType(finduri, allocator_); UriType localuri = UriType(baseuri, allocator_); if (doc.GetType() == kObjectType) { // Establish the base URI of this object typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); if (m != doc.MemberEnd() && m->value.GetType() == kStringType) { localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_); } // See if it matches if (localuri.Match(finduri, full)) { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString()); resval = const_cast(&doc); resptr = here; return resval; } // No match, continue looking for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); } if (resval) break; } } else if (doc.GetType() == kArrayType) { // Continue looking for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { if (v->GetType() == kObjectType || v->GetType() == kArrayType) { resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_)); } if (resval) break; i++; } } return resval; } // Added by PR #1393 void AddSchemaRefs(SchemaType* schema) { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs"); while (!schemaRef_.Empty()) { SchemaRefPtr *ref = schemaRef_.template Pop(1); SchemaEntry *entry = schemaMap_.template Push(); new (entry) SchemaEntry(**ref, schema, false, allocator_); } } // Added by PR #1393 bool IsCyclicRef(const PointerType& pointer) const { for (const SchemaRefPtr* ref = schemaRef_.template Bottom(); ref != schemaRef_.template End(); ++ref) if (pointer == **ref) return true; return false; } const SchemaType* GetSchema(const PointerType& pointer) const { for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) if (pointer == target->pointer) return target->schema; return 0; } PointerType GetPointer(const SchemaType* schema) const { for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++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 schemaMap_; // Stores created Pointer -> Schemas internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved GValue uri_; // Schema document URI UriType docId_; Specification spec_; GValue error_; GValue currentError_; }; //! GenericSchemaDocument using Value type. typedef GenericSchemaDocument SchemaDocument; //! IGenericRemoteSchemaDocumentProvider using SchemaDocument. typedef IGenericRemoteSchemaDocumentProvider 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 StateAllocator = CrtAllocator> class GenericSchemaValidator : public internal::ISchemaStateFactory, public internal::ISchemaValidator, public internal::IValidationErrorHandler { public: typedef typename SchemaDocumentType::SchemaType SchemaType; typedef typename SchemaDocumentType::PointerType PointerType; typedef typename SchemaType::EncodingType EncodingType; typedef typename SchemaType::SValue SValue; typedef typename EncodingType::Ch Ch; typedef GenericStringRef StringRefType; typedef GenericValue ValueType; //! 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), error_(kObjectType), currentError_(), missingDependents_(), valid_(true), flags_(kValidateDefaultFlags), depth_(0) { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator"); } //! 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), error_(kObjectType), currentError_(), missingDependents_(), valid_(true), flags_(kValidateDefaultFlags), depth_(0) { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)"); } //! Destructor. ~GenericSchemaValidator() { Reset(); RAPIDJSON_DELETE(ownStateAllocator_); } //! Reset the internal states. void Reset() { while (!schemaStack_.Empty()) PopSchema(); documentStack_.Clear(); ResetError(); } //! Reset the error state. void ResetError() { error_.SetObject(); currentError_.SetNull(); missingDependents_.SetNull(); valid_ = true; } //! Implementation of ISchemaValidator void SetValidateFlags(unsigned flags) { flags_ = flags; } virtual unsigned GetValidateFlags() const { return flags_; } virtual bool IsValid() const { if (!valid_) return false; if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false; return true; } //! End of Implementation of ISchemaValidator //! Gets the error object. ValueType& GetError() { return error_; } const ValueType& GetError() const { return error_; } //! Gets the JSON pointer pointed to the invalid schema. // If reporting all errors, the stack will be empty. PointerType GetInvalidSchemaPointer() const { return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); } //! Gets the keyword of invalid schema. // If reporting all errors, the stack will be empty, so return "errors". const Ch* GetInvalidSchemaKeyword() const { if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword; if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString(); return 0; } //! Gets the error code of invalid schema. // If reporting all errors, the stack will be empty, so return kValidateErrors. ValidateErrorCode GetInvalidSchemaCode() const { if (!schemaStack_.Empty()) return CurrentContext().invalidCode; if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors; return kValidateErrorNone; } //! Gets the JSON pointer pointed to the invalid value. // If reporting all errors, the stack will be empty. PointerType GetInvalidDocumentPointer() const { if (documentStack_.Empty()) { return PointerType(); } else { return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); } } void NotMultipleOf(int64_t actual, const SValue& expected) { AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); } void NotMultipleOf(uint64_t actual, const SValue& expected) { AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); } void NotMultipleOf(double actual, const SValue& expected) { AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); } void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMaximumString : 0); } void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMaximumString : 0); } void AboveMaximum(double actual, const SValue& expected, bool exclusive) { AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMaximumString : 0); } void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMinimumString : 0); } void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMinimumString : 0); } void BelowMinimum(double actual, const SValue& expected, bool exclusive) { AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMinimumString : 0); } void TooLong(const Ch* str, SizeType length, SizeType expected) { AddNumberError(kValidateErrorMaxLength, ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); } void TooShort(const Ch* str, SizeType length, SizeType expected) { AddNumberError(kValidateErrorMinLength, ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); } void DoesNotMatch(const Ch* str, SizeType length) { currentError_.SetObject(); currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); AddCurrentError(kValidateErrorPattern); } void DisallowedItem(SizeType index) { currentError_.SetObject(); currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); AddCurrentError(kValidateErrorAdditionalItems, true); } void TooFewItems(SizeType actualCount, SizeType expectedCount) { AddNumberError(kValidateErrorMinItems, ValueType(actualCount).Move(), SValue(expectedCount).Move()); } void TooManyItems(SizeType actualCount, SizeType expectedCount) { AddNumberError(kValidateErrorMaxItems, ValueType(actualCount).Move(), SValue(expectedCount).Move()); } void DuplicateItems(SizeType index1, SizeType index2) { ValueType duplicates(kArrayType); duplicates.PushBack(index1, GetStateAllocator()); duplicates.PushBack(index2, GetStateAllocator()); currentError_.SetObject(); currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); AddCurrentError(kValidateErrorUniqueItems, true); } void TooManyProperties(SizeType actualCount, SizeType expectedCount) { AddNumberError(kValidateErrorMaxProperties, ValueType(actualCount).Move(), SValue(expectedCount).Move()); } void TooFewProperties(SizeType actualCount, SizeType expectedCount) { AddNumberError(kValidateErrorMinProperties, ValueType(actualCount).Move(), SValue(expectedCount).Move()); } void StartMissingProperties() { currentError_.SetArray(); } void AddMissingProperty(const SValue& name) { currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); } bool EndMissingProperties() { if (currentError_.Empty()) return false; ValueType error(kObjectType); error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); currentError_ = error; AddCurrentError(kValidateErrorRequired); return true; } void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { for (SizeType i = 0; i < count; ++i) MergeError(static_cast(subvalidators[i])->GetError()); } void DisallowedProperty(const Ch* name, SizeType length) { currentError_.SetObject(); currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); AddCurrentError(kValidateErrorAdditionalProperties, true); } void StartDependencyErrors() { currentError_.SetObject(); } void StartMissingDependentProperties() { missingDependents_.SetArray(); } void AddMissingDependentProperty(const SValue& targetName) { missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); } void EndMissingDependentProperties(const SValue& sourceName) { if (!missingDependents_.Empty()) { // Create equivalent 'required' error ValueType error(kObjectType); ValidateErrorCode code = kValidateErrorRequired; error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator()); AddErrorCode(error, code); AddErrorInstanceLocation(error, false); // When appending to a pointer ensure its allocator is used PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator()); AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator())); ValueType wrapper(kObjectType); wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator()); currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator()); } } void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), static_cast(subvalidator)->GetError(), GetStateAllocator()); } bool EndDependencyErrors() { if (currentError_.ObjectEmpty()) return false; ValueType error(kObjectType); error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); currentError_ = error; AddCurrentError(kValidateErrorDependencies); return true; } void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) { currentError_.SetObject(); AddCurrentError(code); } void StartDisallowedType() { currentError_.SetArray(); } void AddExpectedType(const typename SchemaType::ValueType& expectedType) { currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); } void EndDisallowedType(const typename SchemaType::ValueType& actualType) { ValueType error(kObjectType); error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); currentError_ = error; AddCurrentError(kValidateErrorType); } void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf AddErrorArray(kValidateErrorAllOf, subvalidators, count); //for (SizeType i = 0; i < count; ++i) { // MergeError(static_cast(subvalidators[i])->GetError()); //} } void NoneOf(ISchemaValidator** subvalidators, SizeType count) { AddErrorArray(kValidateErrorAnyOf, subvalidators, count); } void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { AddErrorArray(kValidateErrorOneOf, subvalidators, count); } void MultipleOneOf(SizeType index1, SizeType index2) { ValueType matches(kArrayType); matches.PushBack(index1, GetStateAllocator()); matches.PushBack(index2, GetStateAllocator()); currentError_.SetObject(); currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator()); AddCurrentError(kValidateErrorOneOfMatch); } void Disallowed() { currentError_.SetObject(); AddCurrentError(kValidateErrorNot); } void DisallowedWhenWriting() { currentError_.SetObject(); AddCurrentError(kValidateErrorReadOnly); } void DisallowedWhenReading() { currentError_.SetObject(); AddCurrentError(kValidateErrorWriteOnly); } #define RAPIDJSON_STRING_(name, ...) \ static const StringRefType& Get##name##String() {\ static const Ch s[] = { __VA_ARGS__, '\0' };\ static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ return v;\ } RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e') RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's') #undef RAPIDJSON_STRING_ #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ if (!valid_) return false; \ if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ *documentStack_.template Push() = '\0';\ documentStack_.template Pop(1);\ RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom());\ valid_ = false;\ return valid_;\ } #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ if (context->hasher)\ static_cast(context->hasher)->method arg2;\ if (context->validators)\ for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ static_cast(context->validators[i_])->method arg2;\ if (context->patternPropertiesValidators)\ for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ static_cast(context->patternPropertiesValidators[i_])->method arg2;\ } #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ return valid_; #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_PRINT(Method, "GenericSchemaValidator::StartObject"); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); valid_ = !outputHandler_ || outputHandler_->StartObject(); return valid_; } bool Key(const Ch* str, SizeType len, bool copy) { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str); if (!valid_) return false; AppendToken(str, len); if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) { valid_ = false; return valid_; } RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); return valid_; } bool EndObject(SizeType memberCount) { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject"); if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) { valid_ = false; return valid_; } RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); } bool StartArray() { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray"); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); valid_ = !outputHandler_ || outputHandler_->StartArray(); return valid_; } bool EndArray(SizeType elementCount) { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray"); if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) { valid_ = false; return valid_; } RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); } #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ // Implementation of ISchemaStateFactory virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { *documentStack_.template Push() = '\0'; documentStack_.template Pop(1); ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), depth_ + 1, &GetStateAllocator()); sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag); return sv; } virtual void DestroySchemaValidator(ISchemaValidator* validator) { GenericSchemaValidator* v = static_cast(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(hasher)->GetHashCode(); } virtual void DestroyHasher(void* hasher) { HasherType* h = static_cast(hasher); h->~HasherType(); StateAllocator::Free(h); } virtual void* MallocState(size_t size) { return GetStateAllocator().Malloc(size); } virtual void FreeState(void* p) { StateAllocator::Free(p); } // End of implementation of ISchemaStateFactory private: typedef typename SchemaType::Context Context; typedef GenericValue, StateAllocator> HashCodeArray; typedef internal::Hasher HasherType; GenericSchemaValidator( const SchemaDocumentType& schemaDocument, const SchemaType& root, const char* basePath, size_t basePathSize, unsigned depth, 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), error_(kObjectType), currentError_(), missingDependents_(), valid_(true), flags_(kValidateDefaultFlags), depth_(depth) { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : ""); if (basePath && basePathSize) memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); } StateAllocator& GetStateAllocator() { if (!stateAllocator_) stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); return *stateAllocator_; } bool GetContinueOnErrors() const { return flags_ & kValidateContinueOnErrorFlag; } bool BeginValue() { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue"); if (schemaStack_.Empty()) PushSchema(root_); else { if (CurrentContext().inArray) internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors()) 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(MallocState(sizeof(ISchemaValidator*) * count)); std::memset(va, 0, sizeof(ISchemaValidator*) * count); for (SizeType i = 0; i < count; i++) va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError } CurrentContext().arrayUniqueness = valueUniqueness; } return true; } bool EndValue() { RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue"); if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) return false; GenericStringBuffer sb; schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb); *documentStack_.template Push() = '\0'; documentStack_.template Pop(1); RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom(), depth_); void* hasher = CurrentContext().hasher; uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; PopSchema(); if (!schemaStack_.Empty()) { Context& context = CurrentContext(); // Only check uniqueness if there is a hasher if (hasher && context.valueUniqueness) { HashCodeArray* a = static_cast(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) { DuplicateItems(static_cast(itr - a->Begin()), a->Size()); // Cleanup before returning if continuing if (GetContinueOnErrors()) { a->PushBack(h, GetStateAllocator()); while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/'); } RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems); } a->PushBack(h, GetStateAllocator()); } } // Remove the last token of document pointer while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') ; return true; } void AppendToken(const Ch* str, SizeType len) { documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters *documentStack_.template PushUnsafe() = '/'; for (SizeType i = 0; i < len; i++) { if (str[i] == '~') { *documentStack_.template PushUnsafe() = '~'; *documentStack_.template PushUnsafe() = '0'; } else if (str[i] == '/') { *documentStack_.template PushUnsafe() = '~'; *documentStack_.template PushUnsafe() = '1'; } else *documentStack_.template PushUnsafe() = str[i]; } } RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema, flags_); } RAPIDJSON_FORCEINLINE void PopSchema() { Context* c = schemaStack_.template Pop(1); if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { a->~HashCodeArray(); StateAllocator::Free(a); } c->~Context(); } void AddErrorInstanceLocation(ValueType& result, bool parent) { GenericStringBuffer sb; PointerType instancePointer = GetInvalidDocumentPointer(); ((parent && instancePointer.GetTokenCount() > 0) ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) : instancePointer).StringifyUriFragment(sb); ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), GetStateAllocator()); result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); } void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) { GenericStringBuffer sb; SizeType len = CurrentSchema().GetURI().GetStringLength(); if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch)); if (schema.GetTokenCount()) schema.StringifyUriFragment(sb); else GetInvalidSchemaPointer().StringifyUriFragment(sb); ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), GetStateAllocator()); result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); } void AddErrorCode(ValueType& result, const ValidateErrorCode code) { result.AddMember(GetErrorCodeString(), code, GetStateAllocator()); } void AddError(ValueType& keyword, ValueType& error) { typename ValueType::MemberIterator member = error_.FindMember(keyword); if (member == error_.MemberEnd()) error_.AddMember(keyword, error, GetStateAllocator()); else { if (member->value.IsObject()) { ValueType errors(kArrayType); errors.PushBack(member->value, GetStateAllocator()); member->value = errors; } member->value.PushBack(error, GetStateAllocator()); } } void AddCurrentError(const ValidateErrorCode code, bool parent = false) { AddErrorCode(currentError_, code); AddErrorInstanceLocation(currentError_, parent); AddErrorSchemaLocation(currentError_); AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_); } void MergeError(ValueType& other) { for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { AddError(it->name, it->value); } } void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected, const typename SchemaType::ValueType& (*exclusive)() = 0) { currentError_.SetObject(); currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); if (exclusive) currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); AddCurrentError(code); } void AddErrorArray(const ValidateErrorCode code, ISchemaValidator** subvalidators, SizeType count) { ValueType errors(kArrayType); for (SizeType i = 0; i < count; ++i) errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); currentError_.SetObject(); currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); AddCurrentError(code); } const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } const Context& CurrentContext() const { return *schemaStack_.template Top(); } static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; const SchemaDocumentType* schemaDocument_; const SchemaType& root_; StateAllocator* stateAllocator_; StateAllocator* ownStateAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) OutputHandler* outputHandler_; ValueType error_; ValueType currentError_; ValueType missingDependents_; bool valid_; unsigned flags_; unsigned depth_; }; typedef GenericSchemaValidator 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; typedef GenericValue ValueType; //! Constructor /*! \param is Input stream. \param sd Schema document. */ SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {} template bool operator()(Handler& handler) { GenericReader reader; GenericSchemaValidator validator(sd_, handler); parseResult_ = reader.template Parse(is_, validator); isValid_ = validator.IsValid(); if (isValid_) { invalidSchemaPointer_ = PointerType(); invalidSchemaKeyword_ = 0; invalidDocumentPointer_ = PointerType(); error_.SetObject(); } else { invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); invalidSchemaCode_ = validator.GetInvalidSchemaCode(); invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); error_.CopyFrom(validator.GetError(), allocator_); } 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_; } const ValueType& GetError() const { return error_; } ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; } private: InputStream& is_; const SchemaDocumentType& sd_; ParseResult parseResult_; PointerType invalidSchemaPointer_; const Ch* invalidSchemaKeyword_; PointerType invalidDocumentPointer_; ValidateErrorCode invalidSchemaCode_; StackAllocator allocator_; ValueType error_; bool isValid_; }; RAPIDJSON_NAMESPACE_END RAPIDJSON_DIAG_POP #endif // RAPIDJSON_SCHEMA_H_ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jrt/jrt-libc-includes.h000664 001750 001750 00000001514 15164251010 043602 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JRT_LIBC_INCLUDES_H #define JRT_LIBC_INCLUDES_H #include #include #include #include #include #include #endif /* !JRT_LIBC_INCLUDES_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-promise.inc.h000664 001750 001750 00000003551 15164251010 050273 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Promise built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_PROMISE_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_PROMISE_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_REJECT, ECMA_PROMISE_ROUTINE_REJECT, 1, 1) ROUTINE (LIT_MAGIC_STRING_RESOLVE, ECMA_PROMISE_ROUTINE_RESOLVE, 1, 1) ROUTINE (LIT_MAGIC_STRING_RACE, ECMA_PROMISE_ROUTINE_RACE, 1, 1) ROUTINE (LIT_MAGIC_STRING_ALL, ECMA_PROMISE_ROUTINE_ALL, 1, 1) ROUTINE (LIT_MAGIC_STRING_ALLSETTLED, ECMA_PROMISE_ROUTINE_ALLSETTLED, 1, 1) ROUTINE (LIT_MAGIC_STRING_ANY, ECMA_PROMISE_ROUTINE_ANY, 1, 1) /* ES2015 25.4.4.6 */ ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_SPECIES, ECMA_PROMISE_ROUTINE_SPECIES_GET, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-alloc.h000664 001750 001750 00000005712 15164251010 043311 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_ALLOC_H #define ECMA_ALLOC_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaalloc Routines for allocation/freeing memory for ECMA data types * @{ */ /** * Allocate memory for ecma-object * * @return pointer to allocated memory */ ecma_object_t *ecma_alloc_object (void); /** * Dealloc memory from an ecma-object */ void ecma_dealloc_object (ecma_object_t *object_p); /** * Allocate memory for extended object * * @return pointer to allocated memory */ ecma_extended_object_t *ecma_alloc_extended_object (size_t size); /** * Dealloc memory of an extended object */ void ecma_dealloc_extended_object (ecma_object_t *object_p, size_t size); /** * Allocate memory for ecma-number * * @return pointer to allocated memory */ ecma_number_t *ecma_alloc_number (void); /** * Dealloc memory from an ecma-number */ void ecma_dealloc_number (ecma_number_t *number_p); /** * Allocate memory for ecma-string descriptor * * @return pointer to allocated memory */ ecma_string_t *ecma_alloc_string (void); /** * Dealloc memory from ecma-string descriptor */ void ecma_dealloc_string (ecma_string_t *string_p); /** * Allocate memory for extended ecma-string descriptor * * @return pointer to allocated memory */ ecma_extended_string_t *ecma_alloc_extended_string (void); /** * Dealloc memory from extended ecma-string descriptor */ void ecma_dealloc_extended_string (ecma_extended_string_t *string_p); /** * Allocate memory for external ecma-string descriptor * * @return pointer to allocated memory */ ecma_external_string_t *ecma_alloc_external_string (void); /** * Dealloc memory from external ecma-string descriptor */ void ecma_dealloc_external_string (ecma_external_string_t *string_p); /** * Allocate memory for string with character data * * @return pointer to allocated memory */ ecma_string_t *ecma_alloc_string_buffer (size_t size); /** * Dealloc memory of a string with character data */ void ecma_dealloc_string_buffer (ecma_string_t *string_p, size_t size); /** * Allocate memory for ecma-property pair * * @return pointer to allocated memory */ ecma_property_pair_t *ecma_alloc_property_pair (void); /** * Dealloc memory from an ecma-property pair */ void ecma_dealloc_property_pair (ecma_property_pair_t *property_pair_p); /** * @} * @} */ #endif /* !ECMA_ALLOC_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/common.cpp000664 001750 001750 00000034662 15164251010 043236 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "common.h" #include "ecma-big-uint.h" #include "ecma-bigint.h" #include "ecma-extended-info.h" #include "ecma-helpers.h" #include "js-parser-internal.h" #include "lit-char-helpers.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_utils Utility * @{ */ #if JERRY_PARSER /** * Free literal. */ void util_free_literal (lexer_literal_t *literal_p) /**< literal */ { if (literal_p->type == LEXER_IDENT_LITERAL || literal_p->type == LEXER_STRING_LITERAL) { if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) { jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length); } } else if ((literal_p->type == LEXER_FUNCTION_LITERAL) || (literal_p->type == LEXER_REGEXP_LITERAL)) { ecma_bytecode_deref (literal_p->u.bytecode_p); } } /* util_free_literal */ #endif /* JERRY_PARSER */ #if JERRY_PARSER_DUMP_BYTE_CODE /** * Debug utility to print a character sequence. */ static void util_print_chars (const uint8_t *char_p, /**< character pointer */ size_t size) /**< size */ { while (size > 0) { JERRY_DEBUG_MSG ("%c", *char_p++); size--; } } /* util_print_chars */ /** * Debug utility to print a number. */ static void util_print_number (ecma_number_t num_p) /**< number to print */ { lit_utf8_byte_t str_buf[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; lit_utf8_size_t str_size = ecma_number_to_utf8_string (num_p, str_buf, sizeof (str_buf)); str_buf[str_size] = 0; JERRY_DEBUG_MSG ("%s", str_buf); } /* util_print_number */ #if JERRY_BUILTIN_BIGINT /** * Debug utility to print a bigint. */ static void util_print_bigint (ecma_value_t bigint) /**< bigint to print */ { if (bigint == ECMA_BIGINT_ZERO) { JERRY_DEBUG_MSG ("0"); return; } ecma_extended_primitive_t *bigint_p = ecma_get_extended_primitive_from_value (bigint); uint32_t char_start_p, char_size_p; lit_utf8_byte_t *string_buffer_p = ecma_big_uint_to_string (bigint_p, 10, &char_start_p, &char_size_p); if (JERRY_UNLIKELY (string_buffer_p == NULL)) { JERRY_DEBUG_MSG (""); return; } JERRY_ASSERT (char_start_p > 0); if (bigint_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) { string_buffer_p[--char_start_p] = LIT_CHAR_MINUS; } util_print_chars (string_buffer_p + char_start_p, char_size_p - char_start_p); jmem_heap_free_block (string_buffer_p, char_size_p); } /* util_print_bigint */ #endif /* JERRY_BUILTIN_BIGINT */ /** * Print literal */ void util_print_literal (lexer_literal_t *literal_p) /**< literal */ { switch (literal_p->type) { case LEXER_IDENT_LITERAL: { JERRY_DEBUG_MSG ("ident("); util_print_chars (literal_p->u.char_p, literal_p->prop.length); break; } case LEXER_FUNCTION_LITERAL: { JERRY_DEBUG_MSG ("function"); return; } case LEXER_STRING_LITERAL: { JERRY_DEBUG_MSG ("string("); util_print_chars (literal_p->u.char_p, literal_p->prop.length); break; } case LEXER_NUMBER_LITERAL: { #if JERRY_BUILTIN_BIGINT if (ecma_is_value_bigint (literal_p->u.value)) { JERRY_DEBUG_MSG ("bigint("); util_print_bigint (literal_p->u.value); break; } #endif /* JERRY_BUILTIN_BIGINT */ JERRY_DEBUG_MSG ("number("); util_print_number (ecma_get_number_from_value (literal_p->u.value)); break; } case LEXER_REGEXP_LITERAL: { JERRY_DEBUG_MSG ("regexp"); return; } default: { JERRY_DEBUG_MSG ("unknown"); return; } } JERRY_DEBUG_MSG (")"); } /* util_print_literal */ /** * Print literal. */ static void util_print_literal_value (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ uint16_t literal_index) /**< literal index */ { uint16_t argument_end; uint16_t register_end; uint16_t ident_end; uint16_t const_literal_end; ecma_value_t *literal_start_p; if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p; argument_end = args_p->argument_end; register_end = args_p->register_end; ident_end = args_p->ident_end; const_literal_end = args_p->const_literal_end; literal_start_p = (ecma_value_t *) (args_p + 1); } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p; argument_end = args_p->argument_end; register_end = args_p->register_end; ident_end = args_p->ident_end; const_literal_end = args_p->const_literal_end; literal_start_p = (ecma_value_t *) (args_p + 1); } if (literal_index < argument_end) { JERRY_DEBUG_MSG (" arg:%d", literal_index); return; } if (literal_index < register_end) { JERRY_DEBUG_MSG (" reg:%d", literal_index); return; } if (literal_index >= const_literal_end) { JERRY_DEBUG_MSG (" lit:%d", literal_index); return; } if (literal_index < ident_end) { JERRY_DEBUG_MSG (" ident:%d->", literal_index); } else { JERRY_DEBUG_MSG (" const:%d->", literal_index); } ecma_value_t value = literal_start_p[literal_index - register_end]; if (ecma_is_value_number (value)) { JERRY_DEBUG_MSG ("number("); util_print_number (ecma_get_number_from_value (value)); } #if JERRY_BUILTIN_BIGINT else if (ecma_is_value_bigint (value)) { JERRY_DEBUG_MSG ("bigint("); util_print_bigint (value); } #endif /* JERRY_BUILTIN_BIGINT */ else { ecma_string_t *literal_p = ecma_get_string_from_value (value); JERRY_DEBUG_MSG ("string("); ECMA_STRING_TO_UTF8_STRING (literal_p, chars_p, literal_size); util_print_chars (chars_p, literal_size); ECMA_FINALIZE_UTF8_STRING (chars_p, literal_size); } JERRY_DEBUG_MSG (")"); } /* util_print_literal_value */ #define PARSER_READ_IDENTIFIER_INDEX(name) \ name = *byte_code_p++; \ if (name >= encoding_limit) \ { \ name = (uint16_t) (((name << 8) | byte_code_p[0]) - encoding_delta); \ byte_code_p++; \ } /** * Print byte code. */ void util_print_cbc (ecma_compiled_code_t *compiled_code_p) /**< compiled code */ { uint8_t flags; uint8_t *byte_code_start_p; uint8_t *byte_code_end_p; uint8_t *byte_code_p; uint16_t encoding_limit; uint16_t encoding_delta; uint16_t stack_limit; uint16_t argument_end; uint16_t register_end; uint16_t ident_end; uint16_t const_literal_end; uint16_t literal_end; size_t size = ((size_t) compiled_code_p->size) << JMEM_ALIGNMENT_LOG; if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args = (cbc_uint16_arguments_t *) compiled_code_p; stack_limit = args->stack_limit; argument_end = args->argument_end; register_end = args->register_end; ident_end = args->ident_end; const_literal_end = args->const_literal_end; literal_end = args->literal_end; } else { cbc_uint8_arguments_t *args = (cbc_uint8_arguments_t *) compiled_code_p; stack_limit = args->stack_limit; argument_end = args->argument_end; register_end = args->register_end; ident_end = args->ident_end; const_literal_end = args->const_literal_end; literal_end = args->literal_end; } JERRY_DEBUG_MSG ("\nByte code dump:\n\n Maximum stack depth: %d\n Flags: [", (int) (stack_limit + register_end)); if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING)) { JERRY_DEBUG_MSG ("small_lit_enc"); encoding_limit = CBC_SMALL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_SMALL_LITERAL_ENCODING_DELTA; } else { JERRY_DEBUG_MSG ("full_lit_enc"); encoding_limit = CBC_FULL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_FULL_LITERAL_ENCODING_DELTA; } if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { JERRY_DEBUG_MSG (",uint16_arguments"); } if (compiled_code_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) { JERRY_DEBUG_MSG (",strict_mode"); } if (compiled_code_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED) { JERRY_DEBUG_MSG (",mapped_arguments_needed"); size -= argument_end * sizeof (ecma_value_t); } if (compiled_code_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) { JERRY_DEBUG_MSG (",no_lexical_env"); } switch (CBC_FUNCTION_GET_TYPE (compiled_code_p->status_flags)) { case CBC_FUNCTION_CONSTRUCTOR: { JERRY_DEBUG_MSG (",constructor"); break; } case CBC_FUNCTION_GENERATOR: { JERRY_DEBUG_MSG (",generator"); break; } case CBC_FUNCTION_ASYNC: { JERRY_DEBUG_MSG (",async"); break; } case CBC_FUNCTION_ASYNC_GENERATOR: { JERRY_DEBUG_MSG (",async_generator"); break; } case CBC_FUNCTION_ACCESSOR: { JERRY_DEBUG_MSG (",accessor"); break; } case CBC_FUNCTION_ARROW: { JERRY_DEBUG_MSG (",arrow"); break; } case CBC_FUNCTION_ASYNC_ARROW: { JERRY_DEBUG_MSG (",async_arrow"); break; } } JERRY_DEBUG_MSG ("]\n"); JERRY_DEBUG_MSG (" Argument range end: %d\n", (int) argument_end); JERRY_DEBUG_MSG (" Register range end: %d\n", (int) register_end); JERRY_DEBUG_MSG (" Identifier range end: %d\n", (int) ident_end); JERRY_DEBUG_MSG (" Const literal range end: %d\n", (int) const_literal_end); JERRY_DEBUG_MSG (" Literal range end: %d\n\n", (int) literal_end); if (compiled_code_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO) { uint8_t *extended_info_p = ecma_compiled_code_resolve_extended_info (compiled_code_p); uint8_t *extended_info_start_p = extended_info_p + sizeof (uint8_t); uint8_t extended_info = *extended_info_p; if (extended_info & CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH) { uint32_t argument_length = ecma_extended_info_decode_vlq (&extended_info_p); JERRY_DEBUG_MSG (" [Extended] Argument length: %d\n", (int) argument_length); } if (extended_info & CBC_EXTENDED_CODE_FLAGS_HAS_SOURCE_CODE_RANGE) { uint32_t range_start = ecma_extended_info_decode_vlq (&extended_info_p); uint32_t range_end = ecma_extended_info_decode_vlq (&extended_info_p) + range_start; JERRY_DEBUG_MSG (" [Extended] Source code range: %d - %d\n", (int) range_start, (int) range_end); } JERRY_DEBUG_MSG ("\n"); size -= (size_t) (extended_info_start_p - extended_info_p); } byte_code_start_p = (uint8_t *) compiled_code_p; if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { byte_code_start_p += sizeof (cbc_uint16_arguments_t); } else { byte_code_start_p += sizeof (cbc_uint8_arguments_t); } byte_code_start_p += (unsigned int) (literal_end - register_end) * sizeof (ecma_value_t); if (CBC_FUNCTION_GET_TYPE (compiled_code_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR) { size -= sizeof (ecma_value_t); } if (compiled_code_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS) { size -= sizeof (ecma_value_t); } if (compiled_code_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO) { size -= sizeof (ecma_value_t); } byte_code_end_p = ((uint8_t *) compiled_code_p) + size; byte_code_p = byte_code_start_p; while (byte_code_p < byte_code_end_p) { cbc_opcode_t opcode = (cbc_opcode_t) *byte_code_p; cbc_ext_opcode_t ext_opcode = CBC_EXT_NOP; size_t cbc_offset = (size_t) (byte_code_p - byte_code_start_p); if (opcode != CBC_EXT_OPCODE) { flags = cbc_flags[opcode]; JERRY_DEBUG_MSG (" %3d : %s", (int) cbc_offset, cbc_names[opcode]); byte_code_p++; } else { if (byte_code_p + 1 >= byte_code_end_p) { break; } ext_opcode = (cbc_ext_opcode_t) byte_code_p[1]; if (ext_opcode == CBC_EXT_NOP) { break; } flags = cbc_ext_flags[ext_opcode]; JERRY_DEBUG_MSG (" %3d : %s", (int) cbc_offset, cbc_ext_names[ext_opcode]); byte_code_p += 2; } if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) { uint16_t literal_index; PARSER_READ_IDENTIFIER_INDEX (literal_index); util_print_literal_value (compiled_code_p, literal_index); } if (flags & CBC_HAS_LITERAL_ARG2) { uint16_t literal_index; PARSER_READ_IDENTIFIER_INDEX (literal_index); util_print_literal_value (compiled_code_p, literal_index); if (!(flags & CBC_HAS_LITERAL_ARG)) { PARSER_READ_IDENTIFIER_INDEX (literal_index); util_print_literal_value (compiled_code_p, literal_index); } } if (flags & CBC_HAS_BYTE_ARG) { if (opcode == CBC_PUSH_NUMBER_POS_BYTE || opcode == CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE) { JERRY_DEBUG_MSG (" number:%d", (int) *byte_code_p + 1); } else if (opcode == CBC_PUSH_NUMBER_NEG_BYTE || opcode == CBC_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE) { JERRY_DEBUG_MSG (" number:%d", -((int) *byte_code_p + 1)); } else { JERRY_DEBUG_MSG (" byte_arg:%d", *byte_code_p); } byte_code_p++; } if (flags & CBC_HAS_BRANCH_ARG) { size_t branch_offset_length = (opcode != CBC_EXT_OPCODE ? CBC_BRANCH_OFFSET_LENGTH (opcode) : CBC_BRANCH_OFFSET_LENGTH (ext_opcode)); size_t offset = 0; do { offset = (offset << 8) | *byte_code_p++; } while (--branch_offset_length > 0); JERRY_DEBUG_MSG (" offset:%d(->%d)", (int) offset, (int) (cbc_offset + (CBC_BRANCH_IS_FORWARD (flags) ? offset : -offset))); } JERRY_DEBUG_MSG ("\n"); } } /* util_print_cbc */ #undef PARSER_READ_IDENTIFIER_INDEX #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/jpg/tvgJpgd.cpp000664 001750 001750 00000243037 15164251010 034337 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "tvgCommon.h" #include "tvgJpgd.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ #define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b)) #define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b)) typedef int16_t jpgd_quant_t; typedef int16_t jpgd_block_t; // Success/failure error codes. enum jpgd_status { JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1, JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE, JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS, JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH, JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMETIC_SUPPORT, JPGD_UNEXPECTED_MARKER, JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS, JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE, JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR, JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM }; enum { JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4, JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384 }; // Input stream interface. // Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available. // The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set. // It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer. // Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding. struct jpeg_decoder_stream { jpeg_decoder_stream() { } virtual ~jpeg_decoder_stream() { } // The read() method is called when the internal input buffer is empty. // Parameters: // pBuf - input buffer // max_bytes_to_read - maximum bytes that can be written to pBuf // pEOF_flag - set this to true if at end of stream (no more bytes remaining) // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0). // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full. virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0; }; // stdio FILE stream class. class jpeg_decoder_file_stream : public jpeg_decoder_stream { jpeg_decoder_file_stream(const jpeg_decoder_file_stream &); jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &); FILE *m_pFile = nullptr; bool m_eof_flag = false; bool m_error_flag = false; public: jpeg_decoder_file_stream() {} virtual ~jpeg_decoder_file_stream(); bool open(const char *Pfilename); void close(); virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag); }; // Memory stream class. class jpeg_decoder_mem_stream : public jpeg_decoder_stream { const uint8_t *m_pSrc_data; uint32_t m_ofs, m_size; public: jpeg_decoder_mem_stream() : m_pSrc_data(nullptr), m_ofs(0), m_size(0) {} jpeg_decoder_mem_stream(const uint8_t *pSrc_data, uint32_t size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) {} virtual ~jpeg_decoder_mem_stream() {} bool open(const uint8_t *pSrc_data, uint32_t size); void close() { m_pSrc_data = nullptr; m_ofs = 0; m_size = 0; } virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag); }; class jpeg_decoder { public: // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc. // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline. jpeg_decoder(jpeg_decoder_stream *pStream); ~jpeg_decoder(); // Call this method after constructing the object to begin decompression. // If JPGD_SUCCESS is returned you may then call decode() on each scanline. int begin_decoding(); // Returns the next scan line. // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1). // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4). // Returns JPGD_SUCCESS if a scan line has been returned. // Returns JPGD_DONE if all scan lines have been returned. // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info. int decode(const void** pScan_line); inline jpgd_status get_error_code() const { return m_error_code; } inline int get_width() const { return m_image_x_size; } inline int get_height() const { return m_image_y_size; } inline int get_num_components() const { return m_comps_in_frame; } inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; } inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); } // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file). inline int get_total_bytes_read() const { return m_total_bytes_read; } private: jpeg_decoder(const jpeg_decoder &); jpeg_decoder &operator =(const jpeg_decoder &); typedef bool (*pDecode_block_func)(jpeg_decoder*, int, int, int); struct huff_tables { bool ac_table; uint32_t look_up[256]; uint32_t look_up2[256]; uint8_t code_size[256]; uint32_t tree[512]; }; struct coeff_buf { uint8_t *pData; int block_num_x, block_num_y; int block_len_x, block_len_y; int block_size; }; struct mem_block { mem_block *m_pNext; size_t m_used_count; size_t m_size; char m_data[1]; }; mem_block *m_pMem_blocks; int m_image_x_size; int m_image_y_size; jpeg_decoder_stream *m_pStream; int m_progressive_flag; uint8_t m_huff_ac[JPGD_MAX_HUFF_TABLES]; uint8_t* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size uint8_t* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported) int m_comps_in_frame; // # of components in frame int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID int m_comp_h_blocks[JPGD_MAX_COMPONENTS]; int m_comp_v_blocks[JPGD_MAX_COMPONENTS]; int m_comps_in_scan; // # of components in scan int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector int m_spectral_start; // spectral selection start int m_spectral_end; // spectral selection end int m_successive_low; // successive approximation low int m_successive_high; // successive approximation high int m_max_mcu_x_size; // MCU's max. X size in pixels int m_max_mcu_y_size; // MCU's max. Y size in pixels int m_blocks_per_mcu; int m_max_blocks_per_row; int m_mcus_per_row, m_mcus_per_col; int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU]; int m_total_lines_left; // total # lines left in image int m_mcu_lines_left; // total # lines left in this MCU int m_real_dest_bytes_per_scan_line; int m_dest_bytes_per_scan_line; // rounded up int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y) huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES]; coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS]; coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS]; int m_eob_run; int m_block_y_mcu[JPGD_MAX_COMPONENTS]; uint8_t* m_pIn_buf_ofs; int m_in_buf_left; int m_tem_flag; bool m_eof_flag; uint8_t m_in_buf_pad_start[128]; uint8_t m_in_buf[JPGD_IN_BUF_SIZE + 128]; uint8_t m_in_buf_pad_end[128]; int m_bits_left; uint32_t m_bit_buf; int m_restart_interval; int m_restarts_left; int m_next_restart_num; int m_max_mcus_per_row; int m_max_blocks_per_mcu; int m_expanded_blocks_per_mcu; int m_expanded_blocks_per_row; int m_expanded_blocks_per_component; int m_max_mcus_per_col; uint32_t m_last_dc_val[JPGD_MAX_COMPONENTS]; jpgd_block_t* m_pMCU_coefficients; int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU]; uint8_t* m_pSample_buf; int m_crr[256]; int m_cbb[256]; int m_crg[256]; int m_cbg[256]; uint8_t* m_pScan_line_0; uint8_t* m_pScan_line_1; jpgd_status m_error_code; bool m_ready_flag; int m_total_bytes_read; void free_all_blocks(); bool stop_decoding(jpgd_status status); void *alloc(size_t n, bool zero = false); void word_clear(void *p, uint16_t c, uint32_t n); bool prep_in_buffer(); bool read_dht_marker(); bool read_dqt_marker(); bool read_sof_marker(); bool skip_variable_marker(); bool read_dri_marker(); bool read_sos_marker(); int next_marker(); int process_markers(); bool locate_soi_marker(); bool locate_sof_marker(); bool locate_sos_marker(); void init(jpeg_decoder_stream * pStream); void create_look_ups(); void fix_in_buffer(); void transform_mcu(int mcu_row); coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y); inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y); void load_next_row(); bool decode_next_row(); void make_huff_table(int index, huff_tables *pH); bool check_quant_tables(); bool check_huff_tables(); void calc_mcu_block_order(); int init_scan(); bool init_frame(); bool process_restart(); bool decode_scan(pDecode_block_func decode_block_func); bool init_progressive(); bool init_sequential(); bool decode_start(); void decode_init(jpeg_decoder_stream * pStream); void H2V2Convert(); void H2V1Convert(); void H1V2Convert(); void H1V1Convert(); void gray_convert(); void find_eoi(); inline uint32_t get_char(); inline uint32_t get_char(bool *pPadding_flag); inline void stuff_char(uint8_t q); inline uint8_t get_octet(); inline uint32_t get_bits(int num_bits); inline uint32_t get_bits_no_markers(int numbits); inline int huff_decode(huff_tables *pH); inline int huff_decode(huff_tables *pH, int& extrabits); static inline uint8_t clamp(int i); static bool decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y); static bool decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y); static bool decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y); static bool decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y); }; // DCT coefficients are stored in this sequence. static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 }; enum JPEG_MARKER { M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8, M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC, M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF, M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0 }; enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 }; #define CONST_BITS 13 #define PASS1_BITS 2 #define SCALEDONE ((int32_t)1) #define DESCALE(x,n) (((x) + (SCALEDONE << ((n)-1))) >> (n)) #define DESCALE_ZEROSHIFT(x,n) (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n)) #define MULTIPLY(var, cnst) ((var) * (cnst)) #define CLAMP(i) ((static_cast(i) > 255) ? (((~i) >> 31) & 0xFF) : (i)) #define FIX_0_298631336 ((int32_t)2446) /* FIX(0.298631336) */ #define FIX_0_390180644 ((int32_t)3196) /* FIX(0.390180644) */ #define FIX_0_541196100 ((int32_t)4433) /* FIX(0.541196100) */ #define FIX_0_765366865 ((int32_t)6270) /* FIX(0.765366865) */ #define FIX_0_899976223 ((int32_t)7373) /* FIX(0.899976223) */ #define FIX_1_175875602 ((int32_t)9633) /* FIX(1.175875602) */ #define FIX_1_501321110 ((int32_t)12299) /* FIX(1.501321110) */ #define FIX_1_847759065 ((int32_t)15137) /* FIX(1.847759065) */ #define FIX_1_961570560 ((int32_t)16069) /* FIX(1.961570560) */ #define FIX_2_053119869 ((int32_t)16819) /* FIX(2.053119869) */ #define FIX_2_562915447 ((int32_t)20995) /* FIX(2.562915447) */ #define FIX_3_072711026 ((int32_t)25172) /* FIX(3.072711026) */ // Compiler creates a fast path 1D IDCT for X non-zero columns template struct Row { static void idct(int* pTemp, const jpgd_block_t* pSrc) { // ACCESS_COL() will be optimized at compile time to either an array access, or 0. #define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0) const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6); const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100); const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); const int tmp0 = static_cast(ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS; const int tmp1 = static_cast(ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS; const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1); const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3; const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602); const int az1 = MULTIPLY(bz1, - FIX_0_899976223); const int az2 = MULTIPLY(bz2, - FIX_2_562915447); const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5; const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5; const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3; const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4; const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3; const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4; pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS); pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS); pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS); pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS); pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS); pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS); pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS); pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS); } }; template <> struct Row<0> { static void idct(int* pTemp, const jpgd_block_t* pSrc) { #ifdef _MSC_VER pTemp; pSrc; #endif } }; template <> struct Row<1> { static void idct(int* pTemp, const jpgd_block_t* pSrc) { const int dcval = pSrc[0] * PASS1_BITS * 2; pTemp[0] = dcval; pTemp[1] = dcval; pTemp[2] = dcval; pTemp[3] = dcval; pTemp[4] = dcval; pTemp[5] = dcval; pTemp[6] = dcval; pTemp[7] = dcval; } }; // Compiler creates a fast path 1D IDCT for X non-zero rows template struct Col { static void idct(uint8_t* pDst_ptr, const int* pTemp) { // ACCESS_ROW() will be optimized at compile time to either an array access, or 0. #define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0) const int z2 = ACCESS_ROW(2); const int z3 = ACCESS_ROW(6); const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100); const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); const int tmp0 = static_cast(ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS; const int tmp1 = static_cast(ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS; const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1); const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3; const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602); const int az1 = MULTIPLY(bz1, - FIX_0_899976223); const int az2 = MULTIPLY(bz2, - FIX_2_562915447); const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5; const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5; const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3; const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4; const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3; const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4; int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3); pDst_ptr[8*0] = (uint8_t)CLAMP(i); i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3); pDst_ptr[8*7] = (uint8_t)CLAMP(i); i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3); pDst_ptr[8*1] = (uint8_t)CLAMP(i); i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3); pDst_ptr[8*6] = (uint8_t)CLAMP(i); i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3); pDst_ptr[8*2] = (uint8_t)CLAMP(i); i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3); pDst_ptr[8*5] = (uint8_t)CLAMP(i); i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3); pDst_ptr[8*3] = (uint8_t)CLAMP(i); i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3); pDst_ptr[8*4] = (uint8_t)CLAMP(i); } }; template <> struct Col<1> { static void idct(uint8_t* pDst_ptr, const int* pTemp) { int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3); const uint8_t dcval_clamped = (uint8_t)CLAMP(dcval); pDst_ptr[0*8] = dcval_clamped; pDst_ptr[1*8] = dcval_clamped; pDst_ptr[2*8] = dcval_clamped; pDst_ptr[3*8] = dcval_clamped; pDst_ptr[4*8] = dcval_clamped; pDst_ptr[5*8] = dcval_clamped; pDst_ptr[6*8] = dcval_clamped; pDst_ptr[7*8] = dcval_clamped; } }; static const uint8_t s_idct_row_table[] = { 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0, 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0, 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0, 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0, 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2, 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2, 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4, 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8, }; static const uint8_t s_idct_col_table[] = { 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }; void idct(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr, int block_max_zag) { if (block_max_zag <= 1) { int k = ((pSrc_ptr[0] + 4) >> 3) + 128; k = CLAMP(k); k = k | (k<<8); k = k | (k<<16); for (int i = 8; i > 0; i--) { *(int*)&pDst_ptr[0] = k; *(int*)&pDst_ptr[4] = k; pDst_ptr += 8; } return; } int temp[64]; const jpgd_block_t* pSrc = pSrc_ptr; int* pTemp = temp; const uint8_t* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8]; int i; for (i = 8; i > 0; i--, pRow_tab++) { switch (*pRow_tab) { case 0: Row<0>::idct(pTemp, pSrc); break; case 1: Row<1>::idct(pTemp, pSrc); break; case 2: Row<2>::idct(pTemp, pSrc); break; case 3: Row<3>::idct(pTemp, pSrc); break; case 4: Row<4>::idct(pTemp, pSrc); break; case 5: Row<5>::idct(pTemp, pSrc); break; case 6: Row<6>::idct(pTemp, pSrc); break; case 7: Row<7>::idct(pTemp, pSrc); break; case 8: Row<8>::idct(pTemp, pSrc); break; } pSrc += 8; pTemp += 8; } pTemp = temp; const int nonzero_rows = s_idct_col_table[block_max_zag - 1]; for (i = 8; i > 0; i--) { switch (nonzero_rows) { case 1: Col<1>::idct(pDst_ptr, pTemp); break; case 2: Col<2>::idct(pDst_ptr, pTemp); break; case 3: Col<3>::idct(pDst_ptr, pTemp); break; case 4: Col<4>::idct(pDst_ptr, pTemp); break; case 5: Col<5>::idct(pDst_ptr, pTemp); break; case 6: Col<6>::idct(pDst_ptr, pTemp); break; case 7: Col<7>::idct(pDst_ptr, pTemp); break; case 8: Col<8>::idct(pDst_ptr, pTemp); break; } pTemp++; pDst_ptr++; } } // Retrieve one character from the input stream. inline uint32_t jpeg_decoder::get_char() { // Any bytes remaining in buffer? if (!m_in_buf_left) { // Try to get more bytes. if (!prep_in_buffer()) return 0xFF; // Still nothing to get? if (!m_in_buf_left) { // Pad the end of the stream with 0xFF 0xD9 (EOI marker) int t = m_tem_flag; m_tem_flag ^= 1; if (t) return 0xD9; else return 0xFF; } } uint32_t c = *m_pIn_buf_ofs++; m_in_buf_left--; return c; } // Same as previous method, except can indicate if the character is a pad character or not. inline uint32_t jpeg_decoder::get_char(bool *pPadding_flag) { if (!m_in_buf_left) { if (!prep_in_buffer()) { *pPadding_flag = false; return 0xFF; } if (!m_in_buf_left) { *pPadding_flag = true; int t = m_tem_flag; m_tem_flag ^= 1; if (t) return 0xD9; else return 0xFF; } } *pPadding_flag = false; uint32_t c = *m_pIn_buf_ofs++; m_in_buf_left--; return c; } // Inserts a previously retrieved character back into the input buffer. inline void jpeg_decoder::stuff_char(uint8_t q) { *(--m_pIn_buf_ofs) = q; m_in_buf_left++; } // Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered. inline uint8_t jpeg_decoder::get_octet() { bool padding_flag; int c = get_char(&padding_flag); if (c == 0xFF) { if (padding_flag) return 0xFF; c = get_char(&padding_flag); if (padding_flag) { stuff_char(0xFF); return 0xFF; } if (c == 0x00) return 0xFF; else { stuff_char(static_cast(c)); stuff_char(0xFF); return 0xFF; } } return static_cast(c); } // Retrieves a variable number of bits from the input stream. Does not recognize markers. inline uint32_t jpeg_decoder::get_bits(int num_bits) { if (!num_bits) return 0; uint32_t i = m_bit_buf >> (32 - num_bits); if ((m_bits_left -= num_bits) <= 0) { m_bit_buf <<= (num_bits += m_bits_left); uint32_t c1 = get_char(); uint32_t c2 = get_char(); m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2; m_bit_buf <<= -m_bits_left; m_bits_left += 16; } else m_bit_buf <<= num_bits; return i; } // Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered. inline uint32_t jpeg_decoder::get_bits_no_markers(int num_bits) { if (!num_bits)return 0; uint32_t i = m_bit_buf >> (32 - num_bits); if ((m_bits_left -= num_bits) <= 0) { m_bit_buf <<= (num_bits += m_bits_left); if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF)) { uint32_t c1 = get_octet(); uint32_t c2 = get_octet(); m_bit_buf |= (c1 << 8) | c2; } else { m_bit_buf |= ((uint32_t)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1]; m_in_buf_left -= 2; m_pIn_buf_ofs += 2; } m_bit_buf <<= -m_bits_left; m_bits_left += 16; } else m_bit_buf <<= num_bits; return i; } // Decodes a Huffman encoded symbol. inline int jpeg_decoder::huff_decode(huff_tables *pH) { int symbol; // Check first 8-bits: do we have a complete symbol? if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0) { // Decode more bits, use a tree traversal to find symbol. int ofs = 23; do { symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))]; ofs--; } while (symbol < 0); get_bits_no_markers(8 + (23 - ofs)); } else get_bits_no_markers(pH->code_size[symbol]); return symbol; } // Decodes a Huffman encoded symbol. inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits) { int symbol; // Check first 8-bits: do we have a complete symbol? if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0) { // Use a tree traversal to find symbol. int ofs = 23; do { symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))]; ofs--; } while (symbol < 0); get_bits_no_markers(8 + (23 - ofs)); extra_bits = get_bits_no_markers(symbol & 0xF); } else { if (symbol & 0x8000) { get_bits_no_markers((symbol >> 8) & 31); extra_bits = symbol >> 16; } else { int code_size = (symbol >> 8) & 31; int num_extra_bits = symbol & 0xF; int bits = code_size + num_extra_bits; if (bits <= (m_bits_left + 16)) extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1); else { get_bits_no_markers(code_size); extra_bits = get_bits_no_markers(num_extra_bits); } } symbol &= 0xFF; } return symbol; } // Tables and macro used to fully decode the DPCM differences. static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; static const unsigned int s_extend_offset[16] = { 0, ((~0u)<<1) + 1, ((~0u)<<2) + 1, ((~0u)<<3) + 1, ((~0u)<<4) + 1, ((~0u)<<5) + 1, ((~0u)<<6) + 1, ((~0u)<<7) + 1, ((~0u)<<8) + 1, ((~0u)<<9) + 1, ((~0u)<<10) + 1, ((~0u)<<11) + 1, ((~0u)<<12) + 1, ((~0u)<<13) + 1, ((~0u)<<14) + 1, ((~0u)<<15) + 1 }; // The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this) #define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x)) // Clamps a value between 0-255. inline uint8_t jpeg_decoder::clamp(int i) { if (static_cast(i) > 255) i = (((~i) >> 31) & 0xFF); return static_cast(i); } // Unconditionally frees all allocated m_blocks. void jpeg_decoder::free_all_blocks() { delete(m_pStream); m_pStream = nullptr; for (mem_block *b = m_pMem_blocks; b; ) { mem_block *n = b->m_pNext; tvg::free(b); b = n; } m_pMem_blocks = nullptr; } bool jpeg_decoder::stop_decoding(jpgd_status status) { #if 0 //for debugging m_error_code = status; #else m_error_code = JPGD_FAILED; #endif free_all_blocks(); return false; } void *jpeg_decoder::alloc(size_t nSize, bool zero) { //align the coeff_buf with the current platform architecture constexpr auto cbSize = alignof(coeff_buf); nSize = (nSize + cbSize - 1) & ~(cbSize - 1); char *rv = nullptr; for (mem_block *b = m_pMem_blocks; b; b = b->m_pNext) { if ((b->m_used_count + nSize) <= b->m_size) { rv = b->m_data + b->m_used_count; b->m_used_count += nSize; break; } } if (!rv) { int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047); auto b = tvg::malloc(sizeof(mem_block) + capacity); b->m_pNext = m_pMem_blocks; m_pMem_blocks = b; b->m_used_count = nSize; b->m_size = capacity; rv = b->m_data; } if (zero) memset(rv, 0, nSize); return rv; } void jpeg_decoder::word_clear(void *p, uint16_t c, uint32_t n) { uint8_t *pD = (uint8_t*)p; const uint8_t l = c & 0xFF, h = (c >> 8) & 0xFF; while (n) { pD[0] = l; pD[1] = h; pD += 2; n--; } } // Refill the input buffer. // This method will sit in a loop until (A) the buffer is full or (B) // the stream's read() method reports and end of file condition. bool jpeg_decoder::prep_in_buffer() { m_in_buf_left = 0; m_pIn_buf_ofs = m_in_buf; if (m_eof_flag) return true; do { auto bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag); if (bytes_read == -1) return stop_decoding(JPGD_STREAM_READ); m_in_buf_left += bytes_read; } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag)); m_total_bytes_read += m_in_buf_left; // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid). // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.) word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64); return true; } // Read a Huffman code table. bool jpeg_decoder::read_dht_marker() { int i, index, count; uint8_t huff_num[17]; uint8_t huff_val[256]; uint32_t num_left = get_bits(16); if (num_left < 2) return stop_decoding(JPGD_BAD_DHT_MARKER); num_left -= 2; while (num_left) { index = get_bits(8); huff_num[0] = 0; count = 0; for (i = 1; i <= 16; i++) { huff_num[i] = static_cast(get_bits(8)); count += huff_num[i]; } if (count > 255) return stop_decoding(JPGD_BAD_DHT_COUNTS); for (i = 0; i < count; i++) huff_val[i] = static_cast(get_bits(8)); i = 1 + 16 + count; if (num_left < (uint32_t)i) return stop_decoding(JPGD_BAD_DHT_MARKER); num_left -= i; if ((index & 0x10) > 0x10) return stop_decoding(JPGD_BAD_DHT_INDEX); index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1); if (index >= JPGD_MAX_HUFF_TABLES) return stop_decoding(JPGD_BAD_DHT_INDEX); if (!m_huff_num[index]) m_huff_num[index] = (uint8_t *)alloc(17); if (!m_huff_val[index]) m_huff_val[index] = (uint8_t *)alloc(256); m_huff_ac[index] = (index & 0x10) != 0; memcpy(m_huff_num[index], huff_num, 17); memcpy(m_huff_val[index], huff_val, 256); } return true; } // Read a quantization table. bool jpeg_decoder::read_dqt_marker() { int n, i, prec; uint32_t temp; uint32_t num_left = get_bits(16); if (num_left < 2) return stop_decoding(JPGD_BAD_DQT_MARKER); num_left -= 2; while (num_left) { n = get_bits(8); prec = n >> 4; n &= 0x0F; if (n >= JPGD_MAX_QUANT_TABLES) return stop_decoding(JPGD_BAD_DQT_TABLE); if (!m_quant[n]) m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t)); // read quantization entries, in zag order for (i = 0; i < 64; i++) { temp = get_bits(8); if (prec) temp = (temp << 8) + get_bits(8); m_quant[n][i] = static_cast(temp); } i = 64 + 1; if (prec) i += 64; if (num_left < (uint32_t)i) return stop_decoding(JPGD_BAD_DQT_LENGTH); num_left -= i; } return true; } // Read the start of frame (SOF) marker. bool jpeg_decoder::read_sof_marker() { int i; uint32_t num_left = get_bits(16); if (get_bits(8) != 8) return stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */ m_image_y_size = get_bits(16); if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) return stop_decoding(JPGD_BAD_HEIGHT); m_image_x_size = get_bits(16); if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) return stop_decoding(JPGD_BAD_WIDTH); m_comps_in_frame = get_bits(8); if (m_comps_in_frame > JPGD_MAX_COMPONENTS) return stop_decoding(JPGD_TOO_MANY_COMPONENTS); if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) return stop_decoding(JPGD_BAD_SOF_LENGTH); for (i = 0; i < m_comps_in_frame; i++) { m_comp_ident[i] = get_bits(8); m_comp_h_samp[i] = get_bits(4); m_comp_v_samp[i] = get_bits(4); m_comp_quant[i] = get_bits(8); } return true; } // Used to skip unrecognized markers. bool jpeg_decoder::skip_variable_marker() { uint32_t num_left = get_bits(16); if (num_left < 2) return stop_decoding(JPGD_BAD_VARIABLE_MARKER); num_left -= 2; while (num_left) { get_bits(8); num_left--; } return true; } // Read a define restart interval (DRI) marker. bool jpeg_decoder::read_dri_marker() { if (get_bits(16) != 4) return stop_decoding(JPGD_BAD_DRI_LENGTH); m_restart_interval = get_bits(16); return true; } // Read a start of scan (SOS) marker. bool jpeg_decoder::read_sos_marker() { int i, ci, c, cc; uint32_t num_left = get_bits(16); int n = get_bits(8); m_comps_in_scan = n; num_left -= 3; if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) return stop_decoding(JPGD_BAD_SOS_LENGTH); for (i = 0; i < n; i++) { cc = get_bits(8); c = get_bits(8); num_left -= 2; for (ci = 0; ci < m_comps_in_frame; ci++) if (cc == m_comp_ident[ci]) break; if (ci >= m_comps_in_frame) return stop_decoding(JPGD_BAD_SOS_COMP_ID); m_comp_list[i] = ci; m_comp_dc_tab[ci] = (c >> 4) & 15; m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1); } m_spectral_start = get_bits(8); m_spectral_end = get_bits(8); m_successive_high = get_bits(4); m_successive_low = get_bits(4); if (!m_progressive_flag) { m_spectral_start = 0; m_spectral_end = 63; } num_left -= 3; while (num_left) { /* read past whatever is num_left */ get_bits(8); num_left--; } return true; } // Finds the next marker. int jpeg_decoder::next_marker() { uint32_t c; do { do { c = get_bits(8); } while (c != 0xFF); do { c = get_bits(8); } while (c == 0xFF); } while (c == 0); return c; } // Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is // encountered. int jpeg_decoder::process_markers() { int c; for ( ; ; ) { c = next_marker(); switch (c) { case M_SOF0: case M_SOF1: case M_SOF2: case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: // case M_JPG: case M_SOF9: case M_SOF10: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: case M_SOI: case M_EOI: case M_SOS: return c; case M_DHT: { if (read_dht_marker()) break; else return M_EOI; } // No arithmetic support - dumb patents! case M_DAC: { stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT); return M_EOI; } case M_DQT: { if (read_dqt_marker()) break; else return M_EOI; } case M_DRI: { if (read_dri_marker()) break; else return M_EOI; } //case M_APP0: /* no need to read the JFIF marker */ case M_JPG: case M_RST0: /* no parameters */ case M_RST1: case M_RST2: case M_RST3: case M_RST4: case M_RST5: case M_RST6: case M_RST7: case M_TEM: { stop_decoding(JPGD_UNEXPECTED_MARKER); return M_EOI; } default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ if (skip_variable_marker()) break; else return M_EOI; } } } } // Finds the start of image (SOI) marker. // This code is rather defensive: it only checks the first 512 bytes to avoid // false positives. bool jpeg_decoder::locate_soi_marker() { uint32_t lastchar = get_bits(8); uint32_t thischar = get_bits(8); /* ok if it's a normal JPEG file without a special header */ if ((lastchar == 0xFF) && (thischar == M_SOI)) return true; uint32_t bytesleft = 4096; //512; while (true) { if (--bytesleft == 0) return stop_decoding(JPGD_NOT_JPEG); lastchar = thischar; thischar = get_bits(8); if (lastchar == 0xFF) { if (thischar == M_SOI) break; else if (thischar == M_EOI) return stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end } } // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad. thischar = (m_bit_buf >> 24) & 0xFF; if (thischar != 0xFF) return stop_decoding(JPGD_NOT_JPEG); return true; } // Find a start of frame (SOF) marker. bool jpeg_decoder::locate_sof_marker() { if (!locate_soi_marker()) return false; switch (process_markers()) { case M_SOF2: { m_progressive_flag = true; return read_sof_marker(); } case M_SOF0: /* baseline DCT */ case M_SOF1: { /* extended sequential DCT */ return read_sof_marker(); } case M_SOF9: return stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT); default: return stop_decoding(JPGD_UNSUPPORTED_MARKER); } return true; } // Find a start of scan (SOS) marker. bool jpeg_decoder::locate_sos_marker() { auto c = process_markers(); if (c == M_EOI) return false; else if (c != M_SOS) return stop_decoding(JPGD_UNEXPECTED_MARKER); return read_sos_marker(); } // Reset everything to default/uninitialized state. void jpeg_decoder::init(jpeg_decoder_stream *pStream) { m_pMem_blocks = nullptr; m_error_code = JPGD_SUCCESS; m_ready_flag = false; m_image_x_size = m_image_y_size = 0; m_pStream = pStream; m_progressive_flag = false; memset(m_huff_ac, 0, sizeof(m_huff_ac)); memset(m_huff_num, 0, sizeof(m_huff_num)); memset(m_huff_val, 0, sizeof(m_huff_val)); memset(m_quant, 0, sizeof(m_quant)); m_scan_type = 0; m_comps_in_frame = 0; memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp)); memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp)); memset(m_comp_quant, 0, sizeof(m_comp_quant)); memset(m_comp_ident, 0, sizeof(m_comp_ident)); memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks)); memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks)); m_comps_in_scan = 0; memset(m_comp_list, 0, sizeof(m_comp_list)); memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab)); memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab)); m_spectral_start = 0; m_spectral_end = 0; m_successive_low = 0; m_successive_high = 0; m_max_mcu_x_size = 0; m_max_mcu_y_size = 0; m_blocks_per_mcu = 0; m_max_blocks_per_row = 0; m_mcus_per_row = 0; m_mcus_per_col = 0; m_expanded_blocks_per_component = 0; m_expanded_blocks_per_mcu = 0; m_expanded_blocks_per_row = 0; memset(m_mcu_org, 0, sizeof(m_mcu_org)); m_total_lines_left = 0; m_mcu_lines_left = 0; m_real_dest_bytes_per_scan_line = 0; m_dest_bytes_per_scan_line = 0; m_dest_bytes_per_pixel = 0; memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs)); memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs)); memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs)); memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); m_eob_run = 0; memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); m_pIn_buf_ofs = m_in_buf; m_in_buf_left = 0; m_eof_flag = false; m_tem_flag = 0; memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start)); memset(m_in_buf, 0, sizeof(m_in_buf)); memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end)); m_restart_interval = 0; m_restarts_left = 0; m_next_restart_num = 0; m_max_mcus_per_row = 0; m_max_blocks_per_mcu = 0; m_max_mcus_per_col = 0; memset(m_last_dc_val, 0, sizeof(m_last_dc_val)); m_pMCU_coefficients = nullptr; m_pSample_buf = nullptr; m_total_bytes_read = 0; m_pScan_line_0 = nullptr; m_pScan_line_1 = nullptr; // Ready the input buffer. prep_in_buffer(); // Prime the bit buffer. m_bits_left = 16; m_bit_buf = 0; get_bits(16); get_bits(16); for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++) { m_mcu_block_max_zag[i] = 64; } } #define SCALEBITS 16 #define ONE_HALF ((int) 1 << (SCALEBITS-1)) #define FIX(x) ((int) ((x) * (1L<> SCALEBITS; m_cbb[i] = ( FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS; m_crg[i] = (-FIX(0.71414f)) * k; m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF; } } // This method throws back into the stream any bytes that where read // into the bit buffer during initial marker scanning. void jpeg_decoder::fix_in_buffer() { if (m_bits_left == 16) stuff_char( (uint8_t)(m_bit_buf & 0xFF)); if (m_bits_left >= 8) stuff_char( (uint8_t)((m_bit_buf >> 8) & 0xFF)); stuff_char((uint8_t)((m_bit_buf >> 16) & 0xFF)); stuff_char((uint8_t)((m_bit_buf >> 24) & 0xFF)); m_bits_left = 16; get_bits_no_markers(16); get_bits_no_markers(16); } void jpeg_decoder::transform_mcu(int mcu_row) { jpgd_block_t* pSrc_ptr = m_pMCU_coefficients; uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64; for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) { idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]); pSrc_ptr += 64; pDst_ptr += 64; } } // Loads and dequantizes the next row of (already decoded) coefficients. // Progressive images only. void jpeg_decoder::load_next_row() { int i; jpgd_block_t *p; jpgd_quant_t *q; int mcu_row, mcu_block; int component_num, component_id; int block_x_mcu[JPGD_MAX_COMPONENTS]; memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int)); for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) { int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) { component_id = m_mcu_org[mcu_block]; q = m_quant[m_comp_quant[component_id]]; p = m_pMCU_coefficients + 64 * mcu_block; jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); p[0] = pDC[0]; memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t)); for (i = 63; i > 0; i--) { if (p[g_ZAG[i]]) break; } m_mcu_block_max_zag[mcu_block] = i + 1; for ( ; i >= 0; i--) { if (p[g_ZAG[i]]) { p[g_ZAG[i]] = static_cast(p[g_ZAG[i]] * q[i]); } } if (m_comps_in_scan == 1) block_x_mcu[component_id]++; else { if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) { block_x_mcu_ofs = 0; if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) { block_y_mcu_ofs = 0; block_x_mcu[component_id] += m_comp_h_samp[component_id]; } } } } transform_mcu(mcu_row); } if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++; else { for (component_num = 0; component_num < m_comps_in_scan; component_num++) { component_id = m_comp_list[component_num]; m_block_y_mcu[component_id] += m_comp_v_samp[component_id]; } } } // Restart interval processing. bool jpeg_decoder::process_restart() { int i; int c = 0; // Align to a byte boundary // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers! //get_bits_no_markers(m_bits_left & 7); // Let's scan a little bit to find the marker, but not _too_ far. // 1536 is a "fudge factor" that determines how much to scan. for (i = 1536; i > 0; i--) { if (get_char() == 0xFF) break; } if (i == 0) return stop_decoding(JPGD_BAD_RESTART_MARKER); for ( ; i > 0; i--) { if ((c = get_char()) != 0xFF) break; } if (i == 0) return stop_decoding(JPGD_BAD_RESTART_MARKER); // Is it the expected marker? If not, something bad happened. if (c != (m_next_restart_num + M_RST0)) return stop_decoding(JPGD_BAD_RESTART_MARKER); // Reset each component's DC prediction values. memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t)); m_eob_run = 0; m_restarts_left = m_restart_interval; m_next_restart_num = (m_next_restart_num + 1) & 7; // Get the bit buffer going again... m_bits_left = 16; get_bits_no_markers(16); get_bits_no_markers(16); return true; } static inline int dequantize_ac(int c, int q) { c *= q; return c; } // Decodes and dequantizes the next row of coefficients. bool jpeg_decoder::decode_next_row() { for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) { if ((m_restart_interval) && (m_restarts_left == 0)) { if (!process_restart()) return false; } jpgd_block_t* p = m_pMCU_coefficients; for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64) { int component_id = m_mcu_org[mcu_block]; jpgd_quant_t* q = m_quant[m_comp_quant[component_id]]; int r, s; s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r); s = JPGD_HUFF_EXTEND(r, s); m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]); p[0] = static_cast(s * q[0]); int prev_num_set = m_mcu_block_max_zag[mcu_block]; huff_tables *pH = m_pHuff_tabs[m_comp_ac_tab[component_id]]; int k; for (k = 1; k < 64; k++) { int extra_bits; s = huff_decode(pH, extra_bits); r = s >> 4; s &= 15; if (s) { if (r) { if ((k + r) > 63) return stop_decoding(JPGD_DECODE_ERROR); if (k < prev_num_set) { int n = JPGD_MIN(r, prev_num_set - k); int kt = k; while (n--) p[g_ZAG[kt++]] = 0; } k += r; } s = JPGD_HUFF_EXTEND(extra_bits, s); p[g_ZAG[k]] = static_cast(dequantize_ac(s, q[k])); //s * q[k]; } else { if (r == 15) { if ((k + 16) > 64) return stop_decoding(JPGD_DECODE_ERROR); if (k < prev_num_set) { int n = JPGD_MIN(16, prev_num_set - k); int kt = k; while (n--) { p[g_ZAG[kt++]] = 0; } } k += 16 - 1; // - 1 because the loop counter is k } else break; } } if (k < prev_num_set) { int kt = k; while (kt < prev_num_set) p[g_ZAG[kt++]] = 0; } m_mcu_block_max_zag[mcu_block] = k; } transform_mcu(mcu_row); m_restarts_left--; } return true; } // YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB void jpeg_decoder::H1V1Convert() { int row = m_max_mcu_y_size - m_mcu_lines_left; uint8_t *d = m_pScan_line_0; uint8_t *s = m_pSample_buf + row * 8; for (int i = m_max_mcus_per_row; i > 0; i--) { for (int j = 0; j < 8; j++) { int y = s[j]; int cb = s[64+j]; int cr = s[128+j]; d[0] = clamp(y + m_crr[cr]); d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16)); d[2] = clamp(y + m_cbb[cb]); d[3] = 255; d += 4; } s += 64*3; } } // YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB void jpeg_decoder::H2V1Convert() { int row = m_max_mcu_y_size - m_mcu_lines_left; uint8_t *d0 = m_pScan_line_0; uint8_t *y = m_pSample_buf + row * 8; uint8_t *c = m_pSample_buf + 2*64 + row * 8; for (int i = m_max_mcus_per_row; i > 0; i--) { for (int l = 0; l < 2; l++) { for (int j = 0; j < 4; j++) { int cb = c[0]; int cr = c[64]; int rc = m_crr[cr]; int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); int bc = m_cbb[cb]; int yy = y[j<<1]; d0[0] = clamp(yy+rc); d0[1] = clamp(yy+gc); d0[2] = clamp(yy+bc); d0[3] = 255; yy = y[(j<<1)+1]; d0[4] = clamp(yy+rc); d0[5] = clamp(yy+gc); d0[6] = clamp(yy+bc); d0[7] = 255; d0 += 8; c++; } y += 64; } y += 64*4 - 64*2; c += 64*4 - 8; } } // YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB void jpeg_decoder::H1V2Convert() { int row = m_max_mcu_y_size - m_mcu_lines_left; uint8_t *d0 = m_pScan_line_0; uint8_t *d1 = m_pScan_line_1; uint8_t *y; uint8_t *c; if (row < 8) y = m_pSample_buf + row * 8; else y = m_pSample_buf + 64*1 + (row & 7) * 8; c = m_pSample_buf + 64*2 + (row >> 1) * 8; for (int i = m_max_mcus_per_row; i > 0; i--) { for (int j = 0; j < 8; j++) { int cb = c[0+j]; int cr = c[64+j]; int rc = m_crr[cr]; int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); int bc = m_cbb[cb]; int yy = y[j]; d0[0] = clamp(yy+rc); d0[1] = clamp(yy+gc); d0[2] = clamp(yy+bc); d0[3] = 255; yy = y[8+j]; d1[0] = clamp(yy+rc); d1[1] = clamp(yy+gc); d1[2] = clamp(yy+bc); d1[3] = 255; d0 += 4; d1 += 4; } y += 64*4; c += 64*4; } } // YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB void jpeg_decoder::H2V2Convert() { int row = m_max_mcu_y_size - m_mcu_lines_left; uint8_t *d0 = m_pScan_line_0; uint8_t *d1 = m_pScan_line_1; uint8_t *y; uint8_t *c; if (row < 8) y = m_pSample_buf + row * 8; else y = m_pSample_buf + 64*2 + (row & 7) * 8; c = m_pSample_buf + 64*4 + (row >> 1) * 8; for (int i = m_max_mcus_per_row; i > 0; i--) { for (int l = 0; l < 2; l++) { for (int j = 0; j < 8; j += 2) { int cb = c[0]; int cr = c[64]; int rc = m_crr[cr]; int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); int bc = m_cbb[cb]; int yy = y[j]; d0[0] = clamp(yy+rc); d0[1] = clamp(yy+gc); d0[2] = clamp(yy+bc); d0[3] = 255; yy = y[j+1]; d0[4] = clamp(yy+rc); d0[5] = clamp(yy+gc); d0[6] = clamp(yy+bc); d0[7] = 255; yy = y[j+8]; d1[0] = clamp(yy+rc); d1[1] = clamp(yy+gc); d1[2] = clamp(yy+bc); d1[3] = 255; yy = y[j+8+1]; d1[4] = clamp(yy+rc); d1[5] = clamp(yy+gc); d1[6] = clamp(yy+bc); d1[7] = 255; d0 += 8; d1 += 8; c++; } y += 64; } y += 64*6 - 64*2; c += 64*6 - 8; } } // Y (1 block per MCU) to 8-bit grayscale void jpeg_decoder::gray_convert() { int row = m_max_mcu_y_size - m_mcu_lines_left; uint8_t *d = m_pScan_line_0; uint8_t *s = m_pSample_buf + row * 8; for (int i = m_max_mcus_per_row; i > 0; i--) { *(uint32_t *)d = *(uint32_t *)s; *(uint32_t *)(&d[4]) = *(uint32_t *)(&s[4]); s += 64; d += 8; } } // Find end of image (EOI) marker, so we can return to the user the exact size of the input stream. void jpeg_decoder::find_eoi() { if (!m_progressive_flag) { // Attempt to read the EOI marker. //get_bits_no_markers(m_bits_left & 7); // Prime the bit buffer m_bits_left = 16; get_bits(16); get_bits(16); // The next marker _should_ be EOI process_markers(); } m_total_bytes_read -= m_in_buf_left; } int jpeg_decoder::decode(const void** pScan_line) { if ((m_error_code) || (!m_ready_flag)) return JPGD_FAILED; if (m_total_lines_left == 0) return JPGD_DONE; if (m_mcu_lines_left == 0) { if (m_progressive_flag) load_next_row(); else if (!decode_next_row()) return JPGD_FAILED; // Find the EOI marker if that was the last row. if (m_total_lines_left <= m_max_mcu_y_size) find_eoi(); m_mcu_lines_left = m_max_mcu_y_size; } switch (m_scan_type) { case JPGD_YH2V2: { if ((m_mcu_lines_left & 1) == 0) { H2V2Convert(); *pScan_line = m_pScan_line_0; } else *pScan_line = m_pScan_line_1; break; } case JPGD_YH2V1: { H2V1Convert(); *pScan_line = m_pScan_line_0; break; } case JPGD_YH1V2: { if ((m_mcu_lines_left & 1) == 0) { H1V2Convert(); *pScan_line = m_pScan_line_0; } else *pScan_line = m_pScan_line_1; break; } case JPGD_YH1V1: { H1V1Convert(); *pScan_line = m_pScan_line_0; break; } case JPGD_GRAYSCALE: { gray_convert(); *pScan_line = m_pScan_line_0; break; } } m_mcu_lines_left--; m_total_lines_left--; return JPGD_SUCCESS; } // Creates the tables needed for efficient Huffman decoding. void jpeg_decoder::make_huff_table(int index, huff_tables *pH) { int p, i, l, si; uint8_t huffsize[257]; uint32_t huffcode[257]; uint32_t code; uint32_t subtree; int code_size; int lastp; int nextfreeentry; int currententry; pH->ac_table = m_huff_ac[index] != 0; p = 0; for (l = 1; l <= 16; l++) { for (i = 1; i <= m_huff_num[index][l]; i++) { huffsize[p++] = static_cast(l); } } huffsize[p] = 0; lastp = p; code = 0; si = huffsize[0]; p = 0; while (huffsize[p]) { while (huffsize[p] == si) { huffcode[p++] = code; code++; } code <<= 1; si++; } memset(pH->look_up, 0, sizeof(pH->look_up)); memset(pH->look_up2, 0, sizeof(pH->look_up2)); memset(pH->tree, 0, sizeof(pH->tree)); memset(pH->code_size, 0, sizeof(pH->code_size)); nextfreeentry = -1; p = 0; while (p < lastp) { i = m_huff_val[index][p]; code = huffcode[p]; code_size = huffsize[p]; pH->code_size[i] = static_cast(code_size); if (code_size <= 8) { code <<= (8 - code_size); for (l = 1 << (8 - code_size); l > 0; l--) { pH->look_up[code] = i; bool has_extrabits = false; int extra_bits = 0; int num_extra_bits = i & 15; int bits_to_fetch = code_size; if (num_extra_bits) { int total_codesize = code_size + num_extra_bits; if (total_codesize <= 8) { has_extrabits = true; extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize)); bits_to_fetch += num_extra_bits; } } if (!has_extrabits) pH->look_up2[code] = i | (bits_to_fetch << 8); else pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8); code++; } } else { subtree = (code >> (code_size - 8)) & 0xFF; currententry = pH->look_up[subtree]; if (currententry == 0) { pH->look_up[subtree] = currententry = nextfreeentry; pH->look_up2[subtree] = currententry = nextfreeentry; nextfreeentry -= 2; } code <<= (16 - (code_size - 8)); for (l = code_size; l > 9; l--) { if ((code & 0x8000) == 0) currententry--; if (pH->tree[-currententry - 1] == 0) { pH->tree[-currententry - 1] = nextfreeentry; currententry = nextfreeentry; nextfreeentry -= 2; } else currententry = pH->tree[-currententry - 1]; code <<= 1; } if ((code & 0x8000) == 0) currententry--; pH->tree[-currententry - 1] = i; } p++; } } // Verifies the quantization tables needed for this scan are available. bool jpeg_decoder::check_quant_tables() { for (int i = 0; i < m_comps_in_scan; i++) { if (!m_quant[m_comp_quant[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_QUANT_TABLE); } return true; } // Verifies that all the Huffman tables needed for this scan are available. bool jpeg_decoder::check_huff_tables() { for (int i = 0; i < m_comps_in_scan; i++) { if ((m_spectral_start == 0) && !m_huff_num[m_comp_dc_tab[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_HUFF_TABLE); if ((m_spectral_end > 0) && !m_huff_num[m_comp_ac_tab[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_HUFF_TABLE); } for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) { if (m_huff_num[i]) { if (!m_pHuff_tabs[i]) m_pHuff_tabs[i] = (huff_tables *)alloc(sizeof(huff_tables)); make_huff_table(i, m_pHuff_tabs[i]); } } return true; } // Determines the component order inside each MCU. // Also calcs how many MCU's are on each row, etc. void jpeg_decoder::calc_mcu_block_order() { int component_num, component_id; int max_h_samp = 0, max_v_samp = 0; for (component_id = 0; component_id < m_comps_in_frame; component_id++) { if (m_comp_h_samp[component_id] > max_h_samp) { max_h_samp = m_comp_h_samp[component_id]; } if (m_comp_v_samp[component_id] > max_v_samp) { max_v_samp = m_comp_v_samp[component_id]; } } for (component_id = 0; component_id < m_comps_in_frame; component_id++) { m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8; m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8; } if (m_comps_in_scan == 1) { m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]]; m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]]; } else { m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp; m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp; } if (m_comps_in_scan == 1) { m_mcu_org[0] = m_comp_list[0]; m_blocks_per_mcu = 1; } else { m_blocks_per_mcu = 0; for (component_num = 0; component_num < m_comps_in_scan; component_num++) { int num_blocks; component_id = m_comp_list[component_num]; num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id]; while (num_blocks--) m_mcu_org[m_blocks_per_mcu++] = component_id; } } } // Starts a new scan. int jpeg_decoder::init_scan() { if (!locate_sos_marker()) return false; calc_mcu_block_order(); check_huff_tables(); if (!check_quant_tables()) return false; memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t)); m_eob_run = 0; if (m_restart_interval) { m_restarts_left = m_restart_interval; m_next_restart_num = 0; } fix_in_buffer(); return true; } // Starts a frame. Determines if the number of components or sampling factors // are supported. bool jpeg_decoder::init_frame() { int i; if (m_comps_in_frame == 1) { if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); m_scan_type = JPGD_GRAYSCALE; m_max_blocks_per_mcu = 1; m_max_mcu_x_size = 8; m_max_mcu_y_size = 8; } else if (m_comps_in_frame == 3) { if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1))) { return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); } if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) { m_scan_type = JPGD_YH1V1; m_max_blocks_per_mcu = 3; m_max_mcu_x_size = 8; m_max_mcu_y_size = 8; } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) { m_scan_type = JPGD_YH2V1; m_max_blocks_per_mcu = 4; m_max_mcu_x_size = 16; m_max_mcu_y_size = 8; } else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2)) { m_scan_type = JPGD_YH1V2; m_max_blocks_per_mcu = 4; m_max_mcu_x_size = 8; m_max_mcu_y_size = 16; } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) { m_scan_type = JPGD_YH2V2; m_max_blocks_per_mcu = 6; m_max_mcu_x_size = 16; m_max_mcu_y_size = 16; } else return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); } else return stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size; m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size; // These values are for the *destination* pixels: after conversion. if (m_scan_type == JPGD_GRAYSCALE) m_dest_bytes_per_pixel = 1; else m_dest_bytes_per_pixel = 4; m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel; m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel); // Initialize two scan line buffers. m_pScan_line_0 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true); if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2)) { m_pScan_line_1 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true); } m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu; // Should never happen if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) return stop_decoding(JPGD_ASSERTION_ERROR); // Allocate the coefficient buffer, enough for one MCU m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t)); for (i = 0; i < m_max_blocks_per_mcu; i++) { m_mcu_block_max_zag[i] = 64; } m_expanded_blocks_per_component = m_comp_h_samp[0] * m_comp_v_samp[0]; m_expanded_blocks_per_mcu = m_expanded_blocks_per_component * m_comps_in_frame; m_expanded_blocks_per_row = m_max_mcus_per_row * m_expanded_blocks_per_mcu; m_pSample_buf = (uint8_t *)alloc(m_max_blocks_per_row * 64); m_total_lines_left = m_image_y_size; m_mcu_lines_left = 0; create_look_ups(); return true; } // The coeff_buf series of methods originally stored the coefficients // into a "virtual" file which was located in EMS, XMS, or a disk file. A cache // was used to make this process more efficient. Now, we can store the entire // thing in RAM. jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y) { coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf)); cb->block_num_x = block_num_x; cb->block_num_y = block_num_y; cb->block_len_x = block_len_x; cb->block_len_y = block_len_y; cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t); cb->pData = (uint8_t *)alloc(cb->block_size * block_num_x * block_num_y, true); return cb; } inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, int block_y) { return (jpgd_block_t *)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x)); } // The following methods decode the various types of m_blocks encountered // in progressively encoded images. bool jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y) { int s, r; jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0) { r = pD->get_bits_no_markers(s); s = JPGD_HUFF_EXTEND(r, s); } pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]); p[0] = static_cast(static_cast(s) << pD->m_successive_low); return true; } bool jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y) { if (pD->get_bits_no_markers(1)) { jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); p[0] |= (1 << pD->m_successive_low); } return true; } bool jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y) { int k, s, r; if (pD->m_eob_run) { pD->m_eob_run--; return true; } jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++) { s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]); r = s >> 4; s &= 15; if (s) { if ((k += r) > 63) return pD->stop_decoding(JPGD_DECODE_ERROR); r = pD->get_bits_no_markers(s); s = JPGD_HUFF_EXTEND(r, s); p[g_ZAG[k]] = static_cast(static_cast(s) << pD->m_successive_low); } else { if (r == 15) { if ((k += 15) > 63) return pD->stop_decoding(JPGD_DECODE_ERROR); } else { pD->m_eob_run = 1 << r; if (r) pD->m_eob_run += pD->get_bits_no_markers(r); pD->m_eob_run--; break; } } } return true; } bool jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y) { int s, k, r; int p1 = 1 << pD->m_successive_low; int m1 = static_cast(-1) << pD->m_successive_low; jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); k = pD->m_spectral_start; if (pD->m_eob_run == 0) { for ( ; k <= pD->m_spectral_end; k++) { s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]); r = s >> 4; s &= 15; if (s) { if (s != 1) return pD->stop_decoding(JPGD_DECODE_ERROR); if (pD->get_bits_no_markers(1)) s = p1; else s = m1; } else { if (r != 15) { pD->m_eob_run = 1 << r; if (r) pD->m_eob_run += pD->get_bits_no_markers(r); break; } } do { jpgd_block_t *this_coef = p + g_ZAG[k & 63]; if (*this_coef != 0) { if (pD->get_bits_no_markers(1)) { if ((*this_coef & p1) == 0) { if (*this_coef >= 0) *this_coef = static_cast(*this_coef + p1); else *this_coef = static_cast(*this_coef + m1); } } } else { if (--r < 0) break; } k++; } while (k <= pD->m_spectral_end); if ((s) && (k < 64)) { p[g_ZAG[k]] = static_cast(s); } } } if (pD->m_eob_run > 0) { for ( ; k <= pD->m_spectral_end; k++) { jpgd_block_t *this_coef = p + g_ZAG[k & 63]; // logical AND to shut up static code analysis if (*this_coef != 0) { if (pD->get_bits_no_markers(1)) { if ((*this_coef & p1) == 0) { if (*this_coef >= 0) *this_coef = static_cast(*this_coef + p1); else *this_coef = static_cast(*this_coef + m1); } } } } pD->m_eob_run--; } return true; } // Decode a scan in a progressively encoded image. bool jpeg_decoder::decode_scan(pDecode_block_func decode_block_func) { int mcu_row, mcu_col, mcu_block; int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS]; memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++) { int component_num, component_id; memset(block_x_mcu, 0, sizeof(block_x_mcu)); for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) { int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; if ((m_restart_interval) && (m_restarts_left == 0)) { if (!process_restart()) return false; } for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) { component_id = m_mcu_org[mcu_block]; if (!decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs)) return false; if (m_comps_in_scan == 1) block_x_mcu[component_id]++; else { if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) { block_x_mcu_ofs = 0; if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) { block_y_mcu_ofs = 0; block_x_mcu[component_id] += m_comp_h_samp[component_id]; } } } } m_restarts_left--; } if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++; else { for (component_num = 0; component_num < m_comps_in_scan; component_num++) { component_id = m_comp_list[component_num]; m_block_y_mcu[component_id] += m_comp_v_samp[component_id]; } } } return true; } // Decode a progressively encoded image. bool jpeg_decoder::init_progressive() { int i; if (m_comps_in_frame == 4) return stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); // Allocate the coefficient buffers. for (i = 0; i < m_comps_in_frame; i++) { m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1); m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8); } while (true) { int dc_only_scan, refinement_scan; pDecode_block_func decode_block_func; if (!init_scan()) break; dc_only_scan = (m_spectral_start == 0); refinement_scan = (m_successive_high != 0); if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) return stop_decoding(JPGD_BAD_SOS_SPECTRAL); if (dc_only_scan) { if (m_spectral_end) return stop_decoding(JPGD_BAD_SOS_SPECTRAL); } else if (m_comps_in_scan != 1) return stop_decoding(JPGD_BAD_SOS_SPECTRAL); // AC scans can only contain one component if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) return stop_decoding(JPGD_BAD_SOS_SUCCESSIVE); if (dc_only_scan) { if (refinement_scan) decode_block_func = decode_block_dc_refine; else decode_block_func = decode_block_dc_first; } else { if (refinement_scan) decode_block_func = decode_block_ac_refine; else decode_block_func = decode_block_ac_first; } if (!decode_scan(decode_block_func)) return false; m_bits_left = 16; get_bits(16); get_bits(16); } m_comps_in_scan = m_comps_in_frame; for (i = 0; i < m_comps_in_frame; i++) { m_comp_list[i] = i; } calc_mcu_block_order(); return true; } bool jpeg_decoder::init_sequential() { if (!init_scan()) return stop_decoding(JPGD_UNEXPECTED_MARKER); return true; } bool jpeg_decoder::decode_start() { if (!init_frame()) return false; if (m_progressive_flag) return init_progressive(); return init_sequential(); } void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream) { init(pStream); locate_sof_marker(); } jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream) { decode_init(pStream); } int jpeg_decoder::begin_decoding() { if (m_ready_flag) return JPGD_SUCCESS; if (m_error_code) return JPGD_FAILED; if (!decode_start()) return JPGD_FAILED; m_ready_flag = true; return JPGD_SUCCESS; } jpeg_decoder::~jpeg_decoder() { free_all_blocks(); } void jpeg_decoder_file_stream::close() { if (m_pFile) { fclose(m_pFile); m_pFile = nullptr; } m_eof_flag = false; m_error_flag = false; } jpeg_decoder_file_stream::~jpeg_decoder_file_stream() { close(); } bool jpeg_decoder_file_stream::open(const char *Pfilename) { close(); m_eof_flag = false; m_error_flag = false; #if defined(_MSC_VER) m_pFile = nullptr; fopen_s(&m_pFile, Pfilename, "rb"); #else m_pFile = fopen(Pfilename, "rb"); #endif return m_pFile != nullptr; } int jpeg_decoder_file_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) { if (!m_pFile) return -1; if (m_eof_flag) { *pEOF_flag = true; return 0; } if (m_error_flag) return -1; int bytes_read = static_cast(fread(pBuf, 1, max_bytes_to_read, m_pFile)); if (bytes_read < max_bytes_to_read) { if (ferror(m_pFile)) { m_error_flag = true; return -1; } m_eof_flag = true; *pEOF_flag = true; } return bytes_read; } bool jpeg_decoder_mem_stream::open(const uint8_t *pSrc_data, uint32_t size) { close(); m_pSrc_data = pSrc_data; m_ofs = 0; m_size = size; return true; } int jpeg_decoder_mem_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) { *pEOF_flag = false; if (!m_pSrc_data) return -1; uint32_t bytes_remaining = m_size - m_ofs; if ((uint32_t)max_bytes_to_read > bytes_remaining) { max_bytes_to_read = bytes_remaining; *pEOF_flag = true; } memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read); m_ofs += max_bytes_to_read; return max_bytes_to_read; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height) { auto decoder = new jpeg_decoder(new jpeg_decoder_mem_stream((const uint8_t*)data, size)); if (decoder->get_error_code() != JPGD_SUCCESS) { delete(decoder); return nullptr; } if (width) *width = decoder->get_width(); if (height) *height = decoder->get_height(); return decoder; } jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height) { auto fileStream = new jpeg_decoder_file_stream(); if (!fileStream->open(filename)) { delete(fileStream); return nullptr; } auto decoder = new jpeg_decoder(fileStream); if (decoder->get_error_code() != JPGD_SUCCESS) { delete(decoder); return nullptr; } if (width) *width = decoder->get_width(); if (height) *height = decoder->get_height(); return decoder; } void jpgdDelete(jpeg_decoder* decoder) { delete(decoder); } unsigned char* jpgdDecompress(jpeg_decoder* decoder, ColorSpace cs) { if (!decoder || decoder->begin_decoding() != JPGD_SUCCESS) return nullptr; auto bgra = (cs == ColorSpace::ABGR8888S || cs == ColorSpace::ABGR8888); auto channel = 4; //OPTIMIZE: jpg is 3 channel format, not really need 4 channel components. auto width = decoder->get_width(); auto height = decoder->get_height(); //auto actual_comps = decoder->get_num_components(); const auto stride = width * channel; auto ret = tvg::malloc(stride * height); auto dst = ret; for (int y = 0; y < height; y++) { uint8_t* src = nullptr; if (decoder->decode((const void**)&src) != JPGD_SUCCESS) { tvg::free(ret); return nullptr; } if (decoder->get_num_components() == 3) { if (bgra) { memcpy(dst, src, stride); dst += stride; } else { for (int x = 0; x < width; x++, src += 4, dst += 4) { dst[0] = src[2]; dst[1] = src[1]; dst[2] = src[0]; dst[3] = 255; } } } else if (decoder->get_num_components() == 1) { for (int x = 0; x < width; x++, src++, dst += 4) { dst[0] = *src; dst[1] = *src; dst[2] = *src; dst[3] = 255; } } } return ret; } external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-init-finalize.cpp000664 001750 001750 00000005104 15164251010 045307 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-init-finalize.h" #include "ecma-builtins.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" #include "ecma-literal-storage.h" #include "jcontext.h" #include "jmem.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmainitfinalize Initialization and finalization of ECMA components * @{ */ /** * Maximum number of GC loops on cleanup. */ #define JERRY_GC_LOOP_LIMIT 100 /** * Initialize ECMA components */ void ecma_init (void) { #if (JERRY_GC_MARK_LIMIT != 0) JERRY_CONTEXT (ecma_gc_mark_recursion_limit) = JERRY_GC_MARK_LIMIT; #endif /* (JERRY_GC_MARK_LIMIT != 0) */ ecma_init_global_environment (); #if JERRY_PROPERTY_HASHMAP JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) = ECMA_PROP_HASHMAP_ALLOC_ON; JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_HIGH_PRESSURE_GC; #endif /* JERRY_PROPERTY_HASHMAP */ #if (JERRY_STACK_LIMIT != 0) volatile int sp; JERRY_CONTEXT (stack_base) = (uintptr_t) &sp; #endif /* (JERRY_STACK_LIMIT != 0) */ ecma_job_queue_init (); JERRY_CONTEXT (current_new_target_p) = NULL; #if JERRY_BUILTIN_TYPEDARRAY JERRY_CONTEXT (arraybuffer_compact_allocation_limit) = 256; #endif /* JERRY_BUILTIN_TYPEDARRAY */ } /* ecma_init */ /** * Finalize ECMA components */ void ecma_finalize (void) { JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p) == NULL); ecma_finalize_global_environment (); uint8_t runs = 0; do { ecma_gc_run (); if (++runs >= JERRY_GC_LOOP_LIMIT) { jerry_fatal (JERRY_FATAL_UNTERMINATED_GC_LOOPS); } } while (JERRY_CONTEXT (ecma_gc_new_objects) != 0); jmem_cpointer_t *global_symbols_cp = JERRY_CONTEXT (global_symbols_cp); for (uint32_t i = 0; i < ECMA_BUILTIN_GLOBAL_SYMBOL_COUNT; i++) { if (global_symbols_cp[i] != JMEM_CP_NULL) { ecma_deref_ecma_string (ECMA_GET_NON_NULL_POINTER (ecma_string_t, global_symbols_cp[i])); } } ecma_finalize_lit_storage (); } /* ecma_finalize */ /** * @} * @} */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakref-prototype.cpp000664 001750 001750 00000006252 15164251010 052070 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-container-object.h" #include "ecma-exceptions.h" #if JERRY_BUILTIN_WEAKREF #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_BUILTIN_WEAKREF_PROTOTYPE_OBJECT_ROUTINE_START = 0, ECMA_BUILTIN_WEAKREF_PROTOTYPE_OBJECT_DEREF }; /** * This object has a custom dispatch function. */ #define BUILTIN_INC_HEADER_NAME "ecma-builtin-weakref-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID weakref_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup weakref ECMA WeakRef object built-in * @{ */ /** * Deref checks weakRef target * * @return weakRef target * error - otherwise */ static ecma_value_t ecma_builtin_weakref_prototype_object_deref (ecma_value_t this_arg) /**< this argument */ { if (!ecma_is_value_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_TARGET_IS_NOT_OBJECT); } ecma_object_t *object_p = ecma_get_object_from_value (this_arg); ecma_extended_object_t *this_ext_obj = (ecma_extended_object_t *) object_p; if (!ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_WEAKREF)) { return ecma_raise_type_error (ECMA_ERR_TARGET_IS_NOT_WEAKREF); } return ecma_copy_value (this_ext_obj->u.cls.u3.target); } /* ecma_builtin_weakref_prototype_object_deref */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_weakref_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (arguments_list_p, arguments_number); switch (builtin_routine_id) { case ECMA_BUILTIN_WEAKREF_PROTOTYPE_OBJECT_DEREF: { return ecma_builtin_weakref_prototype_object_deref (this_arg); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_weakref_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_WEAKREF */ lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-shared-arraybuffer-prototype.cpp000664 001750 001750 00000006556 15164251010 054227 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-arraybuffer-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-shared-arraybuffer-object.h" #include "jrt-libc-includes.h" #include "jrt.h" #if JERRY_BUILTIN_SHAREDARRAYBUFFER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-shared-arraybuffer-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID shared_arraybuffer_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup sharedarraybufferprototype ECMA SharedArrayBuffer.prototype object built-in * @{ */ /** * The SharedArrayBuffer.prototype.bytelength accessor * * See also: * ES11, 24.2.4.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_shared_arraybuffer_prototype_bytelength_getter (ecma_value_t this_arg) /**< this argument */ { if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER)) { uint32_t len = ecma_arraybuffer_get_length (object_p); return ecma_make_uint32_value (len); } } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_SHARED_ARRAY_BUFFER); } /* ecma_builtin_shared_arraybuffer_prototype_bytelength_getter */ /** * The SharedArrayBuffer.prototype object's 'slice' routine * * See also: * ECMA-262 v11, 24.2.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_shared_arraybuffer_prototype_object_slice (ecma_value_t this_arg, /**< this argument */ const ecma_value_t *argument_list_p, /**< arguments list */ uint32_t arguments_number) /**< number of arguments */ { if (!ecma_is_value_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_OBJECT); } ecma_object_t *object_p = ecma_get_object_from_value (this_arg); /* 2. */ if (!ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_SHARED_ARRAY_BUFFER_OBJECT); } return ecma_builtin_arraybuffer_slice (this_arg, argument_list_p, arguments_number); } /* ecma_builtin_shared_arraybuffer_prototype_object_slice */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-error.cpp000664 001750 001750 00000005131 15164251010 047525 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-error.inc.h" #define BUILTIN_UNDERSCORED_ID error #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup error ECMA Error object built-in * @{ */ /** * Handle calling [[Call]] of built-in Error object * * @return ecma value */ ecma_value_t ecma_builtin_error_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_helper_error_dispatch_call (JERRY_ERROR_COMMON, arguments_list_p, arguments_list_len); } /* ecma_builtin_error_dispatch_call */ /** * Handle calling [[Construct]] of built-in Error object * * @return ecma value */ ecma_value_t ecma_builtin_error_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_ERROR_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_error_dispatch_call (arguments_list_p, arguments_list_len); if (!ECMA_IS_VALUE_ERROR (result)) { ecma_object_t *object_p = ecma_get_object_from_value (result); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, proto_p); } ecma_deref_object (proto_p); return result; } /* ecma_builtin_error_dispatch_construct */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/000775 001750 001750 00000000000 15164251010 032234 5ustar00ddennedyddennedy000000 000000 thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-symbol.inc.h000664 001750 001750 00000005462 15164251010 050125 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Symbol built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Number properties: * (property name, number value, writable, enumerable, configurable) */ /* ECMA-262 v6, 19.4.2 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FLAG_CONFIGURABLE) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_SYMBOL_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v6, 19.4.2.7 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_SYMBOL_PROTOTYPE, ECMA_PROPERTY_FIXED) /* ECMA-262 v10, 19.4.2.1 */ SYMBOL_VALUE (LIT_MAGIC_STRING_ASYNC_ITERATOR, LIT_GLOBAL_SYMBOL_ASYNC_ITERATOR) /* ECMA-262 v6, 19.4.2.2 */ SYMBOL_VALUE (LIT_MAGIC_STRING_HAS_INSTANCE, LIT_GLOBAL_SYMBOL_HAS_INSTANCE) /* ECMA-262 v6, 19.4.2.3 */ SYMBOL_VALUE (LIT_MAGIC_STRING_IS_CONCAT_SPREADABLE, LIT_GLOBAL_SYMBOL_IS_CONCAT_SPREADABLE) /* ECMA-262 v6, 19.4.2.4 */ SYMBOL_VALUE (LIT_MAGIC_STRING_ITERATOR, LIT_GLOBAL_SYMBOL_ITERATOR) /* ECMA-262 v6, 19.4.2.6 */ SYMBOL_VALUE (LIT_MAGIC_STRING_MATCH, LIT_GLOBAL_SYMBOL_MATCH) /* ECMA-262 v6, 19.4.2.8 */ SYMBOL_VALUE (LIT_MAGIC_STRING_REPLACE, LIT_GLOBAL_SYMBOL_REPLACE) /* ECMA-262 v6, 19.4.2.9 */ SYMBOL_VALUE (LIT_MAGIC_STRING_SEARCH, LIT_GLOBAL_SYMBOL_SEARCH) /* ECMA-262 v6, 19.4.2.10 */ SYMBOL_VALUE (LIT_MAGIC_STRING_SPECIES, LIT_GLOBAL_SYMBOL_SPECIES) /* ECMA-262 v6, 19.4.2.11 */ SYMBOL_VALUE (LIT_MAGIC_STRING_SPLIT, LIT_GLOBAL_SYMBOL_SPLIT) /* ECMA-262 v6, 19.4.2.12 */ SYMBOL_VALUE (LIT_MAGIC_STRING_TO_PRIMITIVE, LIT_GLOBAL_SYMBOL_TO_PRIMITIVE) /* ECMA-262 v6, 19.4.2.13 */ SYMBOL_VALUE (LIT_MAGIC_STRING_TO_STRING_TAG, LIT_GLOBAL_SYMBOL_TO_STRING_TAG) /* ECMA-262 v6, 19.4.2.14 */ SYMBOL_VALUE (LIT_MAGIC_STRING_UNSCOPABLES, LIT_GLOBAL_SYMBOL_UNSCOPABLES) /* ECMA-262 v11, 19.4.2.8 */ SYMBOL_VALUE (LIT_MAGIC_STRING_MATCH_ALL, LIT_GLOBAL_SYMBOL_MATCH_ALL) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_FOR, ecma_builtin_symbol_for, 1, 1) ROUTINE (LIT_MAGIC_STRING_KEY_FOR, ecma_builtin_symbol_key_for, 1, 1) #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtins.h000664 001750 001750 00000012423 15164251010 046230 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_BUILTINS_H #define ECMA_BUILTINS_H #include "ecma-globals.h" /** * A built-in object's identifier */ typedef enum { /** @cond doxygen_suppress */ #define BUILTIN(a, b, c, d, e) #define BUILTIN_ROUTINE(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) builtin_id, #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE #define BUILTIN_ROUTINE(a, b, c, d, e) #define BUILTIN(builtin_id, object_type, object_prototype_builtin_id, is_extensible, lowercase_name) builtin_id, #include "ecma-builtins.inc.h" #undef BUILTIN #undef BUILTIN_ROUTINE /** @endcond */ ECMA_BUILTIN_ID__COUNT /**< number of built-in objects */ } ecma_builtin_id_t; /** * Special id for handlers (handlers are not regular built-ins, but * they use the same ecma_built_in_props_t structure as other built-ins) */ #define ECMA_BUILTIN_ID_HANDLER ECMA_BUILTIN_ID__COUNT /** * Number of global symbols */ #define ECMA_BUILTIN_GLOBAL_SYMBOL_COUNT (LIT_GLOBAL_SYMBOL__LAST - LIT_GLOBAL_SYMBOL__FIRST + 1) /** * Construct a routine value */ #define ECMA_ROUTINE_VALUE(id, length) (((id) << 4) | length) /** * Get routine length */ #define ECMA_GET_ROUTINE_LENGTH(value) ((uint8_t) ((value) &0xf)) /** * Get routine ID */ #define ECMA_GET_ROUTINE_ID(value) ((uint8_t) ((value) >> 4)) /** * Construct a fully accessor value */ #define ECMA_ACCESSOR_READ_WRITE(getter, setter) (((getter) << 8) | (setter)) /** * Get accessor setter ID */ #define ECMA_ACCESSOR_READ_WRITE_GET_SETTER_ID(value) ((uint8_t) ((value) &0xff)) /** * Get accessor getter ID */ #define ECMA_ACCESSOR_READ_WRITE_GET_GETTER_ID(value) ((uint8_t) ((value) >> 8)) /** * Number ob built-in objects excluding global object */ #define ECMA_BUILTIN_OBJECTS_COUNT (ECMA_BUILTIN_ID__COUNT - 1) /** * Description of built-in global ECMA-object. */ typedef struct { ecma_extended_object_t extended_object; /**< extended object part */ uint32_t extra_instantiated_bitset[1]; /**< extra bit set for instantiated properties */ #if JERRY_BUILTIN_REALMS uint32_t extra_realms_bitset; /**< extra bit set for instantiated properties when realms is enabled */ ecma_value_t this_binding; /**< 'this' binding of this global object */ #endif /* JERRY_BUILTIN_REALMS */ jmem_cpointer_t global_env_cp; /**< global lexical environment */ jmem_cpointer_t global_scope_cp; /**< global lexical scope */ jmem_cpointer_t builtin_objects[ECMA_BUILTIN_OBJECTS_COUNT]; /**< pointer to instances of built-in objects */ } ecma_global_object_t; /* ecma-builtins.c */ ecma_global_object_t *ecma_builtin_create_global_object (void); ecma_value_t ecma_builtin_dispatch_call (ecma_object_t *obj_p, ecma_value_t this_arg_value, const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_value_t ecma_builtin_dispatch_construct (ecma_object_t *obj_p, const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_property_t *ecma_builtin_routine_try_to_instantiate_property (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_property_t *ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, ecma_string_t *property_name_p); void ecma_builtin_routine_delete_built_in_property (ecma_object_t *object_p, ecma_string_t *property_name_p); void ecma_builtin_delete_built_in_property (ecma_object_t *object_p, ecma_string_t *property_name_p); void ecma_builtin_routine_list_lazy_property_names (ecma_object_t *object_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p, jerry_property_filter_t filter); void ecma_builtin_list_lazy_property_names (ecma_object_t *object_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p, jerry_property_filter_t filter); bool ecma_builtin_is_global (ecma_object_t *object_p); ecma_object_t *ecma_builtin_get (ecma_builtin_id_t builtin_id); ecma_object_t *ecma_builtin_get_global (void); bool ecma_builtin_function_is_routine (ecma_object_t *func_obj_p); #if JERRY_BUILTIN_REALMS ecma_object_t *ecma_builtin_get_from_realm (ecma_global_object_t *global_object_p, ecma_builtin_id_t builtin_id); #endif /* JERRY_BUILTIN_REALMS */ #endif /* !ECMA_BUILTINS_H */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/regexp/re-bytecode.h000664 001750 001750 00000010473 15164251010 044465 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 RE_BYTECODE_H #define RE_BYTECODE_H #include "ecma-globals.h" #include "re-compiler-context.h" #if JERRY_BUILTIN_REGEXP /** \addtogroup parser Parser * @{ * * \addtogroup regexparser Regular expression * @{ * * \addtogroup regexparser_bytecode Bytecode * @{ */ /** * Size of the RegExp bytecode cache */ #define RE_CACHE_SIZE 8u /** * Maximum value that can be encoded in the RegExp bytecode as a single byte. */ #define RE_VALUE_1BYTE_MAX 0xFE /** * Marker that signals that the actual value is encoded in the following 4 bytes in the bytecode. */ #define RE_VALUE_4BYTE_MARKER 0xFF /** * RegExp opcodes */ typedef enum { RE_OP_EOF, /**< end of pattern */ RE_OP_ALTERNATIVE_START, /**< start of alternatives */ RE_OP_ALTERNATIVE_NEXT, /**< next alternative */ RE_OP_NO_ALTERNATIVE, /**< no alternative */ RE_OP_CAPTURING_GROUP_START, /**< start of a capturing group */ RE_OP_NON_CAPTURING_GROUP_START, /**< start of a non-capturing group */ RE_OP_GREEDY_CAPTURING_GROUP_END, /**< end of a greedy capturing group */ RE_OP_GREEDY_NON_CAPTURING_GROUP_END, /**< end of a greedy non-capturing group */ RE_OP_LAZY_CAPTURING_GROUP_END, /**< end of a lazy capturing group */ RE_OP_LAZY_NON_CAPTURING_GROUP_END, /**< end of a lazy non-capturing group */ RE_OP_GREEDY_ITERATOR, /**< greedy iterator */ RE_OP_LAZY_ITERATOR, /**< lazy iterator */ RE_OP_ITERATOR_END, /*** end of an iterator */ RE_OP_BACKREFERENCE, /**< backreference */ RE_OP_ASSERT_LINE_START, /**< line start assertion */ RE_OP_ASSERT_LINE_END, /**< line end assertion */ RE_OP_ASSERT_WORD_BOUNDARY, /**< word boundary assertion */ RE_OP_ASSERT_NOT_WORD_BOUNDARY, /**< not word boundary assertion */ RE_OP_ASSERT_LOOKAHEAD_POS, /**< positive lookahead assertion */ RE_OP_ASSERT_LOOKAHEAD_NEG, /**< negative lookahead assertion */ RE_OP_ASSERT_END, /**< end of an assertion */ RE_OP_CLASS_ESCAPE, /**< class escape */ RE_OP_CHAR_CLASS, /**< character class */ RE_OP_UNICODE_PERIOD, /**< period in full unicode mode */ RE_OP_PERIOD, /**< period in non-unicode mode */ RE_OP_CHAR, /**< any code point */ RE_OP_BYTE, /**< 1-byte utf8 character */ } re_opcode_t; /** * Compiled byte code data. */ typedef struct { ecma_compiled_code_t header; /**< compiled code header */ uint32_t captures_count; /**< number of capturing groups */ uint32_t non_captures_count; /**< number of non-capturing groups */ ecma_value_t source; /**< original RegExp pattern */ } re_compiled_code_t; void re_initialize_regexp_bytecode (re_compiler_ctx_t *re_ctx_p); uint32_t re_bytecode_size (re_compiler_ctx_t *re_ctx_p); void re_append_opcode (re_compiler_ctx_t *re_ctx_p, const re_opcode_t opcode); void re_append_byte (re_compiler_ctx_t *re_ctx_p, const uint8_t byte); void re_append_char (re_compiler_ctx_t *re_ctx_p, const lit_code_point_t cp); void re_append_value (re_compiler_ctx_t *re_ctx_p, const uint32_t value); void re_insert_opcode (re_compiler_ctx_t *re_ctx_p, const uint32_t offset, const re_opcode_t opcode); void re_insert_byte (re_compiler_ctx_t *re_ctx_p, const uint32_t offset, const uint8_t byte); void re_insert_char (re_compiler_ctx_t *re_ctx_p, const uint32_t offset, const lit_code_point_t cp); void re_insert_value (re_compiler_ctx_t *re_ctx_p, const uint32_t offset, const uint32_t value); re_opcode_t re_get_opcode (const uint8_t **bc_p); uint8_t re_get_byte (const uint8_t **bc_p); lit_code_point_t re_get_char (const uint8_t **bc_p, bool unicode); uint32_t re_get_value (const uint8_t **bc_p); #if JERRY_REGEXP_DUMP_BYTE_CODE void re_dump_bytecode (re_compiler_ctx_t *bc_ctx); #endif /* JERRY_REGEXP_DUMP_BYTE_CODE */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REGEXP */ #endif /* !RE_BYTECODE_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/stream.h000664 001750 001750 00000015114 15164251010 036315 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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 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. */ enum { copyOptimization = 0 }; }; //! Reserve n characters for writing to a stream. template inline void PutReserve(Stream& stream, size_t count) { (void)stream; (void)count; } //! Write character to a stream, presuming buffer is reserved. template inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { stream.Put(c); } //! Put N copies of a character to a stream. template 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 > 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 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(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 struct StreamTraits > { enum { copyOptimization = 1 }; }; //! String stream with UTF8 encoding. typedef GenericStringStream > StringStream; /////////////////////////////////////////////////////////////////////////////// // InsituStringStream //! A read-write string stream. /*! This string stream is particularly designed for in-situ parsing. \note implements Stream concept */ template 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(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(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 struct StreamTraits > { enum { copyOptimization = 1 }; }; //! Insitu string stream with UTF8 encoding. typedef GenericInsituStringStream > InsituStringStream; RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_STREAM_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgLoader.h000664 001750 001750 00000003566 15164251010 033724 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOADER_H_ #define _TVG_LOADER_H_ #include "tvgLoadModule.h" namespace tvg { struct LoaderMgr { static bool init(); static bool term(); static LoadModule* loader(const char* filename, bool* invalid); static LoadModule* loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy); static LoadModule* loader(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy); static LoadModule* loader(const char* name, const char* data, uint32_t size, const char* mimeType, bool copy); static LoadModule* font(const char* name); static LoadModule* anyfont(); static bool retrieve(const char* filename); static bool retrieve(LoadModule* loader); }; } #endif //_TVG_LOADER_H_ lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.cpp000664 001750 001750 00000201055 15164251010 055012 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include "ecma-arraybuffer-object.h" #include "ecma-bigint.h" #include "ecma-builtin-helpers.h" #include "ecma-builtin-typedarray-helpers.h" #include "ecma-builtins.h" #include "ecma-comparison.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-number-object.h" #include "ecma-objects.h" #include "ecma-typedarray-object.h" #include "jcontext.h" #include "jmem.h" #include "jrt-libc-includes.h" #include "jrt.h" #include "lit-char-helpers.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { /* These routines must be in this order */ ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_START = 0, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_MAP, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REDUCE, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REDUCE_RIGHT, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_EVERY, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SOME, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FOR_EACH, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FILTER, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FIND, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FIND_INDEX, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_INDEX_OF, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_AT, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_LAST_INDEX_OF, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_INCLUDES, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FILL, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SORT, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REVERSE, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_COPY_WITHIN, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SLICE, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SUBARRAY, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_TO_LOCALE_STRING, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_JOIN, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_KEYS, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_ENTRIES, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BUFFER_GETTER, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BYTELENGTH_GETTER, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BYTEOFFSET_GETTER, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_LENGTH_GETTER, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SET, ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_TO_STRING_TAG_GETTER, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-typedarray-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID typedarray_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup typedarrayprototype ECMA %TypedArray%.prototype object built-in * @{ */ /** * Type of routine. */ typedef enum { TYPEDARRAY_ROUTINE_EVERY, /**< routine: every ES2015, 22.2.3.7 */ TYPEDARRAY_ROUTINE_SOME, /**< routine: some ES2015, 22.2.3.9 */ TYPEDARRAY_ROUTINE_FOREACH, /**< routine: forEach ES2015, 15.4.4.18 */ TYPEDARRAY_ROUTINE__COUNT /**< count of the modes */ } typedarray_routine_mode; /** * The common function for 'every', 'some' and 'forEach' * because they have a similar structure. * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_exec_routine (ecma_value_t this_arg, /**< this argument */ ecma_typedarray_info_t *info_p, /**< object info */ ecma_value_t cb_func_val, /**< callback function */ ecma_value_t cb_this_arg, /**< 'this' of the callback function */ typedarray_routine_mode mode) /**< mode: which routine */ { JERRY_ASSERT (mode < TYPEDARRAY_ROUTINE__COUNT); ecma_object_t *typedarray_p = ecma_get_object_from_value (this_arg); ecma_typedarray_info_t info = ecma_typedarray_get_info (typedarray_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } ecma_typedarray_getter_fn_t typedarray_getter_cb = ecma_get_typedarray_getter_fn (info_p->id); ecma_object_t *func_object_p = ecma_get_object_from_value (cb_func_val); uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; uint32_t byte_pos = 0; ecma_value_t ret_value = ECMA_VALUE_EMPTY; for (uint32_t index = 0; index < info_p->length && ecma_is_value_empty (ret_value); index++) { ecma_value_t current_index = ecma_make_uint32_value (index); ecma_value_t element = typedarray_getter_cb (buffer_p + byte_pos); ecma_value_t call_args[] = { element, current_index, this_arg }; ecma_value_t call_value = ecma_op_function_call (func_object_p, cb_this_arg, call_args, 3); ecma_fast_free_value (current_index); ecma_fast_free_value (element); if (ECMA_IS_VALUE_ERROR (call_value)) { return call_value; } if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { ecma_free_value (call_value); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } bool to_bool_result = ecma_op_to_boolean (call_value); ecma_free_value (call_value); if (mode == TYPEDARRAY_ROUTINE_EVERY) { if (!to_bool_result) { return ECMA_VALUE_FALSE; } } else if (mode == TYPEDARRAY_ROUTINE_SOME && to_bool_result) { return ECMA_VALUE_TRUE; } byte_pos += info_p->element_size; } if (mode == TYPEDARRAY_ROUTINE_EVERY) { ret_value = ECMA_VALUE_TRUE; } else if (mode == TYPEDARRAY_ROUTINE_SOME) { ret_value = ECMA_VALUE_FALSE; } else { ret_value = ECMA_VALUE_UNDEFINED; } return ret_value; } /* ecma_builtin_typedarray_prototype_exec_routine */ /** * The %TypedArray%.prototype object's 'map' routine * * See also: * ES2015, 22.2.3.8 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_map (ecma_value_t this_arg, /**< this object */ ecma_typedarray_info_t *src_info_p, /**< object info */ ecma_value_t cb_func_val, /**< callback function */ ecma_value_t cb_this_arg) /**< this' of the callback function */ { ecma_object_t *func_object_p = ecma_get_object_from_value (cb_func_val); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (src_info_p->array_buffer_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (src_info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } // TODO: 22.2.3.18, 7-8. ecma_value_t len = ecma_make_number_value (src_info_p->length); ecma_value_t new_typedarray = ecma_typedarray_species_create (this_arg, &len, 1); ecma_free_value (len); if (ECMA_IS_VALUE_ERROR (new_typedarray)) { return new_typedarray; } ecma_object_t *target_obj_p = ecma_get_object_from_value (new_typedarray); uint8_t *src_buffer_p = ecma_typedarray_get_buffer (src_info_p); ecma_typedarray_info_t target_info = ecma_typedarray_get_info (target_obj_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (target_info.array_buffer_p)) { ecma_deref_object (target_obj_p); return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (target_info.array_buffer_p)) { ecma_deref_object (target_obj_p); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint8_t *target_buffer_p = ecma_typedarray_get_buffer (&target_info); ecma_typedarray_getter_fn_t src_typedarray_getter_cb = ecma_get_typedarray_getter_fn (src_info_p->id); ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (target_info.id); for (uint32_t index = 0; index < src_info_p->length; index++) { ecma_value_t current_index = ecma_make_uint32_value (index); ecma_value_t element = src_typedarray_getter_cb (src_buffer_p); src_buffer_p += src_info_p->element_size; ecma_value_t call_args[] = { element, current_index, this_arg }; ecma_value_t mapped_value = ecma_op_function_call (func_object_p, cb_this_arg, call_args, 3); ecma_free_value (current_index); ecma_free_value (element); if (ECMA_IS_VALUE_ERROR (mapped_value)) { ecma_free_value (new_typedarray); return mapped_value; } if (ecma_arraybuffer_is_detached (src_info_p->array_buffer_p)) { ecma_free_value (mapped_value); ecma_free_value (new_typedarray); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } ecma_value_t set_element = target_typedarray_setter_cb (target_buffer_p, mapped_value); target_buffer_p += target_info.element_size; ecma_free_value (mapped_value); if (ECMA_IS_VALUE_ERROR (set_element)) { ecma_free_value (new_typedarray); return set_element; } } return new_typedarray; } /* ecma_builtin_typedarray_prototype_map */ /** * Reduce and reduceRight routines share a similar structure. * And we use 'is_right' to distinguish between them. * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_reduce_with_direction (ecma_value_t this_arg, /**< this object */ ecma_typedarray_info_t *info_p, /**< object info */ const ecma_value_t arguments_list_p[], /**arg_list*/ uint32_t arguments_number, /**< length of arguments' list*/ bool is_right) /**< choose order, true is reduceRight */ { if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); uint32_t byte_pos; if (info_p->length == 0) { if (arguments_number < 2) { return ecma_raise_type_error (ECMA_ERR_INITIAL_VALUE_CANNOT_BE_UNDEFINED); } return ecma_copy_value (arguments_list_p[1]); } JERRY_ASSERT (info_p->length > 0); ecma_value_t accumulator = ECMA_VALUE_UNDEFINED; uint32_t index = is_right ? (info_p->length - 1) : 0; uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; if (ecma_is_value_undefined (arguments_list_p[1])) { byte_pos = index << info_p->shift; accumulator = getter_cb (buffer_p + byte_pos); if (is_right) { if (index == 0) { return accumulator; } index--; } else { index++; if (index == info_p->length) { return accumulator; } } } else { accumulator = ecma_copy_value (arguments_list_p[1]); } ecma_object_t *func_object_p = ecma_get_object_from_value (arguments_list_p[0]); while (true) { ecma_value_t current_index = ecma_make_uint32_value (index); byte_pos = index << info_p->shift; ecma_value_t get_value = getter_cb (buffer_p + byte_pos); ecma_value_t call_args[] = { accumulator, get_value, current_index, this_arg }; JERRY_ASSERT (ecma_is_value_number (get_value) || ecma_is_value_bigint (get_value)); ecma_value_t call_value = ecma_op_function_call (func_object_p, ECMA_VALUE_UNDEFINED, call_args, 4); ecma_fast_free_value (accumulator); ecma_fast_free_value (get_value); ecma_fast_free_value (current_index); if (ECMA_IS_VALUE_ERROR (call_value)) { return call_value; } if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { ecma_free_value (call_value); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } accumulator = call_value; if (is_right) { if (index == 0) { break; } index--; } else { index++; if (index == info_p->length) { break; } } } return accumulator; } /* ecma_builtin_typedarray_prototype_reduce_with_direction */ /** * The %TypedArray%.prototype object's 'filter' routine * * See also: * ES2015, 22.2.3.9 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_filter (ecma_value_t this_arg, /**< this object */ ecma_typedarray_info_t *info_p, /**< object info */ ecma_value_t cb_func_val, /**< callback function */ ecma_value_t cb_this_arg) /**< 'this' of the callback function */ { if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); ecma_object_t *func_object_p = ecma_get_object_from_value (cb_func_val); ecma_value_t ret_value = ECMA_VALUE_ERROR; // TODO: 22.2.3.9, 7-8. if (info_p->length == 0) { return ecma_op_create_typedarray_with_type_and_length (info_p->id, 0); } ecma_collection_t *collected_p = ecma_new_collection (); uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; ecma_value_t collected; for (uint32_t index = 0; index < info_p->length; index++) { ecma_value_t current_index = ecma_make_uint32_value (index); ecma_value_t get_value = getter_cb (buffer_p); JERRY_ASSERT (ecma_is_value_number (get_value) || ecma_is_value_bigint (get_value)); ecma_value_t call_args[] = { get_value, current_index, this_arg }; ecma_value_t call_value = ecma_op_function_call (func_object_p, cb_this_arg, call_args, 3); ecma_fast_free_value (current_index); if (ECMA_IS_VALUE_ERROR (call_value)) { ecma_fast_free_value (get_value); goto cleanup; } if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { ecma_free_value (call_value); ecma_fast_free_value (get_value); ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); goto cleanup; } if (ecma_op_to_boolean (call_value)) { ecma_collection_push_back (collected_p, get_value); } else { ecma_fast_free_value (get_value); } buffer_p += info_p->element_size; ecma_fast_free_value (call_value); } collected = ecma_make_number_value (collected_p->item_count); ret_value = ecma_typedarray_species_create (this_arg, &collected, 1); ecma_free_value (collected); if (!ECMA_IS_VALUE_ERROR (ret_value)) { ecma_object_t *new_typedarray_p = ecma_get_object_from_value (ret_value); ecma_typedarray_info_t target_info = ecma_typedarray_get_info (new_typedarray_p); JERRY_ASSERT (target_info.offset == 0); uint8_t *target_buffer_p = ecma_typedarray_get_buffer (&target_info); ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (target_info.id); for (uint32_t idx = 0; idx < collected_p->item_count; idx++) { ecma_value_t set_element = target_typedarray_setter_cb (target_buffer_p, collected_p->buffer_p[idx]); if (ECMA_IS_VALUE_ERROR (set_element)) { ecma_deref_object (new_typedarray_p); ret_value = ECMA_VALUE_ERROR; goto cleanup; } target_buffer_p += target_info.element_size; } } cleanup: ecma_collection_free (collected_p); return ret_value; } /* ecma_builtin_typedarray_prototype_filter */ /** * The %TypedArray%.prototype object's 'reverse' routine * * See also: * ES2015, 22.2.3.21 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_reverse (ecma_value_t this_arg, /**< this argument */ ecma_typedarray_info_t *info_p) /**< object info */ { if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; uint32_t middle = (info_p->length / 2) << info_p->shift; uint32_t buffer_last = (info_p->length << info_p->shift) - info_p->element_size; for (uint32_t lower = 0; lower < middle; lower += info_p->element_size) { uint32_t upper = buffer_last - lower; uint8_t *lower_p = buffer_p + lower; uint8_t *upper_p = buffer_p + upper; uint8_t tmp[8]; memcpy (&tmp[0], lower_p, info_p->element_size); memcpy (lower_p, upper_p, info_p->element_size); memcpy (upper_p, &tmp[0], info_p->element_size); } return ecma_copy_value (this_arg); } /* ecma_builtin_typedarray_prototype_reverse */ /** * The %TypedArray%.prototype object's 'set' routine for a typedArray source * * See also: * ES2015, 22.2.3.22, 22.2.3.22.2 * * @return ecma value of undefined if success, error otherwise. * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_op_typedarray_set_with_typedarray (ecma_value_t this_arg, /**< this argument */ ecma_value_t arr_val, /**< typedarray object */ ecma_value_t offset_val) /**< offset value */ { /* 6.~ 8. targetOffset */ ecma_number_t target_offset_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (offset_val, &target_offset_num))) { return ECMA_VALUE_ERROR; } if (target_offset_num <= -1.0 || target_offset_num >= (ecma_number_t) UINT32_MAX + 0.5) { return ecma_raise_range_error (ECMA_ERR_INVALID_OFFSET); } ecma_object_t *target_typedarray_p = ecma_get_object_from_value (this_arg); ecma_typedarray_info_t target_info = ecma_typedarray_get_info (target_typedarray_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (target_info.array_buffer_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (target_info.array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint8_t *target_buffer_p = ecma_typedarray_get_buffer (&target_info); ecma_object_t *src_typedarray_p = ecma_get_object_from_value (arr_val); ecma_typedarray_info_t src_info = ecma_typedarray_get_info (src_typedarray_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (src_info.array_buffer_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (src_info.array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint8_t *src_buffer_p = ecma_typedarray_get_buffer (&src_info); uint32_t target_offset_uint32 = ecma_number_to_uint32 (target_offset_num); if ((int64_t) src_info.length + target_offset_uint32 > target_info.length) { return ecma_raise_range_error (ECMA_ERR_INVALID_RANGE_OF_INDEX); } /* Fast path first. If the source and target arrays are the same we do not need to copy anything. */ if (this_arg == arr_val) { return ECMA_VALUE_UNDEFINED; } /* 26. targetByteIndex */ target_buffer_p += target_offset_uint32 << target_info.shift; /* 27. limit */ uint32_t limit = src_info.length << target_info.shift; if (src_info.id == target_info.id) { memmove (target_buffer_p, src_buffer_p, limit); } else { uint8_t *target_limit_p = target_buffer_p + limit; ecma_typedarray_getter_fn_t src_typedarray_getter_cb = ecma_get_typedarray_getter_fn (src_info.id); ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (target_info.id); while (target_buffer_p < target_limit_p) { ecma_value_t element = src_typedarray_getter_cb (src_buffer_p); ecma_value_t set_element = target_typedarray_setter_cb (target_buffer_p, element); ecma_free_value (element); if (ECMA_IS_VALUE_ERROR (set_element)) { return set_element; } src_buffer_p += src_info.element_size; target_buffer_p += target_info.element_size; } } return ECMA_VALUE_UNDEFINED; } /* ecma_op_typedarray_set_with_typedarray */ /** * The %TypedArray%.prototype object's 'set' routine * * See also: * ES2015, 22.2.3.22, 22.2.3.22.1 * * @return ecma value of undefined if success, error otherwise. * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_set (ecma_value_t this_arg, /**< this argument */ ecma_value_t arr_val, /**< array object */ ecma_value_t offset_val) /**< offset value */ { /* 1. */ if (ecma_is_typedarray (arr_val)) { /* 22.2.3.22.2 */ return ecma_op_typedarray_set_with_typedarray (this_arg, arr_val, offset_val); } /* 6.~ 8. targetOffset */ ecma_number_t target_offset_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (offset_val, &target_offset_num))) { return ECMA_VALUE_ERROR; } if (target_offset_num <= -1.0 || target_offset_num >= (ecma_number_t) UINT32_MAX + 0.5) { return ecma_raise_range_error (ECMA_ERR_INVALID_OFFSET); } uint32_t target_offset_uint32 = ecma_number_to_uint32 (target_offset_num); /* 11. ~ 15. */ ecma_object_t *typedarray_p = ecma_get_object_from_value (this_arg); ecma_typedarray_info_t target_info = ecma_typedarray_get_info (typedarray_p); if (ECMA_ARRAYBUFFER_LAZY_ALLOC (target_info.array_buffer_p)) { return ECMA_VALUE_ERROR; } if (ecma_arraybuffer_is_detached (target_info.array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint8_t *target_buffer_p = ecma_typedarray_get_buffer (&target_info); /* 16.~ 17. */ ecma_value_t source_obj = ecma_op_to_object (arr_val); if (ECMA_IS_VALUE_ERROR (source_obj)) { return source_obj; } /* 18.~ 19. */ ecma_object_t *source_obj_p = ecma_get_object_from_value (source_obj); ecma_length_t source_length; if (ECMA_IS_VALUE_ERROR (ecma_op_object_get_length (source_obj_p, &source_length))) { ecma_deref_object (source_obj_p); return ECMA_VALUE_ERROR; } /* 20. if srcLength + targetOffset > targetLength, throw a RangeError */ if ((int64_t) source_length + target_offset_uint32 > target_info.length) { ecma_deref_object (source_obj_p); return ecma_raise_range_error (ECMA_ERR_INVALID_RANGE_OF_INDEX); } JERRY_ASSERT (source_length <= UINT32_MAX); uint32_t source_length_uint32 = (uint32_t) source_length; /* 21.~ 25. */ target_buffer_p += target_offset_uint32 << target_info.shift; ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (target_info.id); uint32_t k = 0; while (k < source_length_uint32) { ecma_value_t elem = ecma_op_object_get_by_index (source_obj_p, k); if (ECMA_IS_VALUE_ERROR (elem)) { ecma_deref_object (source_obj_p); return elem; } ecma_value_t value_to_set; #if JERRY_BUILTIN_BIGINT if (ECMA_TYPEDARRAY_IS_BIGINT_TYPE (target_info.id)) { value_to_set = ecma_bigint_to_bigint (elem, false); if (ECMA_IS_VALUE_ERROR (value_to_set)) { ecma_deref_object (source_obj_p); ecma_free_value (elem); return value_to_set; } } else #endif /* JERRY_BUILTIN_BIGINT */ { ecma_number_t elem_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_numeric (elem, &elem_num, ECMA_TO_NUMERIC_NO_OPTS))) { ecma_free_value (elem); ecma_deref_object (source_obj_p); return ECMA_VALUE_ERROR; } value_to_set = ecma_make_number_value (elem_num); } ecma_free_value (elem); if (ecma_arraybuffer_is_detached (target_info.array_buffer_p)) { ecma_deref_object (source_obj_p); ecma_free_value (value_to_set); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } ecma_value_t set_element = target_typedarray_setter_cb (target_buffer_p, value_to_set); ecma_free_value (value_to_set); if (ECMA_IS_VALUE_ERROR (set_element)) { ecma_deref_object (source_obj_p); return set_element; } k++; target_buffer_p += target_info.element_size; } ecma_deref_object (source_obj_p); return ECMA_VALUE_UNDEFINED; } /* ecma_builtin_typedarray_prototype_set */ /** * TypedArray.prototype's 'toString' single element operation routine based * on the Array.prototype's 'toString' single element operation routine * * See also: * ECMA-262 v5.1, 15.4.4.2 * * @return NULL - if the conversion fails * ecma_string_t * - otherwise */ static ecma_string_t * ecma_op_typedarray_get_to_string_at_index (ecma_object_t *obj_p, /**< this object */ uint32_t index) /**< array index */ { ecma_value_t index_value = ecma_op_object_get_by_index (obj_p, index); if (ECMA_IS_VALUE_ERROR (index_value)) { return NULL; } if (ecma_is_value_undefined (index_value) || ecma_is_value_null (index_value)) { ecma_free_value (index_value); return ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } ecma_string_t *ret_str_p = ecma_op_to_string (index_value); ecma_free_value (index_value); return ret_str_p; } /* ecma_op_typedarray_get_to_string_at_index */ /** * The TypedArray.prototype.toString's separator creation routine based on * the Array.prototype.toString's separator routine * * See also: * ECMA-262 v5.1, 15.4.4.2 4th step * * @return NULL - if the conversion fails * ecma_string_t * - otherwise */ static ecma_string_t * ecma_op_typedarray_get_separator_string (ecma_value_t separator) /**< possible separator */ { if (ecma_is_value_undefined (separator)) { return ecma_get_magic_string (LIT_MAGIC_STRING_COMMA_CHAR); } return ecma_op_to_string (separator); } /* ecma_op_typedarray_get_separator_string */ /** * The TypedArray.prototype object's 'join' routine based on * the Array.prototype object's 'join' * * See also: * ECMA-262 v5, 15.4.4.5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_join (ecma_object_t *obj_p, /**< this object */ ecma_value_t separator_arg) /**< separator argument */ { ecma_typedarray_info_t info = ecma_typedarray_get_info (obj_p); if (ecma_arraybuffer_is_detached (info.array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } /* 2. */ uint32_t length = ecma_typedarray_get_length (obj_p); ecma_string_t *separator_string_p = ecma_op_typedarray_get_separator_string (separator_arg); ecma_value_t ret_value = ECMA_VALUE_ERROR; if (JERRY_UNLIKELY (separator_string_p == NULL)) { return ret_value; } /* 7-8. */ ecma_string_t *first_string_p = ecma_op_typedarray_get_to_string_at_index (obj_p, 0); if (JERRY_UNLIKELY (first_string_p == NULL)) { ecma_deref_ecma_string (separator_string_p); return ret_value; } ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (first_string_p); ecma_deref_ecma_string (first_string_p); /* 9-10. */ for (uint32_t k = 1; k < length; k++) { /* 10.a */ ecma_stringbuilder_append (&builder, separator_string_p); /* 10.d */ ecma_string_t *next_string_p = ecma_op_typedarray_get_to_string_at_index (obj_p, k); if (JERRY_UNLIKELY (next_string_p == NULL)) { ecma_stringbuilder_destroy (&builder); ecma_deref_ecma_string (separator_string_p); return ret_value; } ecma_stringbuilder_append (&builder, next_string_p); ecma_deref_ecma_string (next_string_p); } ecma_deref_ecma_string (separator_string_p); ret_value = ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); return ret_value; } /* ecma_builtin_typedarray_prototype_join */ /** * The %TypedArray%.prototype object's 'subarray' routine. * * See also: * ES2015, 22.2.3.26 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_subarray (ecma_value_t this_arg, /**< this object */ ecma_typedarray_info_t *info_p, /**< object info */ ecma_value_t begin, /**< begin */ ecma_value_t end) /**< end */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; /* 9. beginIndex, 12. endIndex */ uint32_t begin_index_uint32 = 0, end_index_uint32 = 0; /* 7. relativeBegin */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (begin, info_p->length, &begin_index_uint32))) { return ECMA_VALUE_ERROR; } if (ecma_is_value_undefined (end)) { end_index_uint32 = (uint32_t) info_p->length; } else { /* 10. relativeEnd */ if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (end, info_p->length, &end_index_uint32))) { return ECMA_VALUE_ERROR; } } /* 13. newLength */ uint32_t subarray_length = 0; if (end_index_uint32 > begin_index_uint32) { subarray_length = end_index_uint32 - begin_index_uint32; } /* 17. beginByteOffset */ uint32_t begin_byte_offset = info_p->offset + (begin_index_uint32 << info_p->shift); ecma_value_t arguments_p[3] = { ecma_make_object_value (info_p->array_buffer_p), ecma_make_uint32_value (begin_byte_offset), ecma_make_uint32_value (subarray_length) }; ret_value = ecma_typedarray_species_create (this_arg, arguments_p, 3); ecma_free_value (arguments_p[1]); ecma_free_value (arguments_p[2]); return ret_value; } /* ecma_builtin_typedarray_prototype_subarray */ /** * The %TypedArray%.prototype object's 'fill' routine. * * See also: * ES2015, 22.2.3.8, 22.1.3.6 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_fill (ecma_value_t this_arg, /**< this object */ ecma_typedarray_info_t *info_p, /**< object info */ ecma_value_t value, /**< value */ ecma_value_t begin, /**< begin */ ecma_value_t end) /**< end */ { ecma_value_t value_to_set; if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } #if JERRY_BUILTIN_BIGINT if (ECMA_TYPEDARRAY_IS_BIGINT_TYPE (info_p->id)) { value_to_set = ecma_bigint_to_bigint (value, true); if (ECMA_IS_VALUE_ERROR (value_to_set)) { return value_to_set; } } else #endif /* JERRY_BUILTIN_BIGINT */ { ecma_number_t value_num; ecma_value_t ret_value = ecma_op_to_numeric (value, &value_num, ECMA_TO_NUMERIC_NO_OPTS); if (!ecma_is_value_empty (ret_value)) { return ret_value; } value_to_set = ecma_make_number_value (value_num); } uint32_t begin_index_uint32 = 0, end_index_uint32 = 0; if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (begin, info_p->length, &begin_index_uint32))) { ecma_free_value (value_to_set); return ECMA_VALUE_ERROR; } if (ecma_is_value_undefined (end)) { end_index_uint32 = (uint32_t) info_p->length; } else { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (end, info_p->length, &end_index_uint32))) { ecma_free_value (value_to_set); return ECMA_VALUE_ERROR; } } uint32_t subarray_length = 0; if (end_index_uint32 > begin_index_uint32) { subarray_length = end_index_uint32 - begin_index_uint32; } if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); buffer_p += begin_index_uint32 << info_p->shift; uint8_t *limit_p = buffer_p + (subarray_length << info_p->shift); ecma_typedarray_setter_fn_t typedarray_setter_cb = ecma_get_typedarray_setter_fn (info_p->id); while (buffer_p < limit_p) { ecma_value_t set_element = typedarray_setter_cb (buffer_p, value_to_set); if (ECMA_IS_VALUE_ERROR (set_element)) { ecma_free_value (value_to_set); return set_element; } buffer_p += info_p->element_size; } ecma_free_value (value_to_set); return ecma_copy_value (this_arg); } /* ecma_builtin_typedarray_prototype_fill */ /** * SortCompare abstract method * * See also: * ECMA-262 v5, 15.4.4.11 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_sort_compare_helper (ecma_value_t lhs, /**< left value */ ecma_value_t rhs, /**< right value */ ecma_value_t compare_func, /**< compare function */ ecma_object_t *array_buffer_p) /**< array buffer */ { if (ecma_is_value_undefined (compare_func)) { /* Default comparison when no comparefn is passed. */ #if JERRY_BUILTIN_BIGINT if (ecma_is_value_bigint (lhs) && ecma_is_value_bigint (rhs)) { return ecma_make_number_value (ecma_bigint_compare_to_bigint (lhs, rhs)); } #endif /* JERRY_BUILTIN_BIGINT */ ecma_number_t result = ECMA_NUMBER_ZERO; double lhs_value = (double) ecma_get_number_from_value (lhs); double rhs_value = (double) ecma_get_number_from_value (rhs); if (ecma_number_is_nan (lhs_value)) { // Keep NaNs at the end of the array. result = ECMA_NUMBER_ONE; } else if (ecma_number_is_nan (rhs_value)) { // Keep NaNs at the end of the array. result = ECMA_NUMBER_MINUS_ONE; } else if (lhs_value < rhs_value) { result = ECMA_NUMBER_MINUS_ONE; } else if (lhs_value > rhs_value || (ecma_number_is_zero (rhs_value) && ecma_number_is_negative (rhs_value))) { result = ECMA_NUMBER_ONE; } else { result = ECMA_NUMBER_ZERO; } return ecma_make_number_value (result); } /* * compare_func, if not undefined, will always contain a callable function object. * We checked this previously, before this function was called. */ JERRY_ASSERT (ecma_op_is_callable (compare_func)); ecma_object_t *comparefn_obj_p = ecma_get_object_from_value (compare_func); ecma_value_t compare_args[] = { lhs, rhs }; ecma_value_t call_value = ecma_op_function_call (comparefn_obj_p, ECMA_VALUE_UNDEFINED, compare_args, 2); if (ECMA_IS_VALUE_ERROR (call_value) || ecma_is_value_number (call_value)) { return call_value; } ecma_number_t ret_num; ecma_value_t number_result = ecma_op_to_number (call_value, &ret_num); ecma_free_value (call_value); if (ECMA_IS_VALUE_ERROR (number_result)) { return number_result; } if (ecma_arraybuffer_is_detached (array_buffer_p)) { ecma_free_value (number_result); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } // If the coerced value can't be represented as a Number, compare them as equals. if (ecma_number_is_nan (ret_num)) { return ecma_make_number_value (ECMA_NUMBER_ZERO); } return ecma_make_number_value (ret_num); } /* ecma_builtin_typedarray_prototype_sort_compare_helper */ /** * The %TypedArray%.prototype object's 'sort' routine. * * See also: * ES2015, 22.2.3.25, 22.1.3.24 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_sort (ecma_value_t this_arg, /**< this argument */ ecma_typedarray_info_t *info_p, /**< object info */ ecma_value_t compare_func) /**< comparator fn */ { JERRY_ASSERT (ecma_is_typedarray (this_arg)); JERRY_ASSERT (ecma_is_value_undefined (compare_func) || ecma_op_is_callable (compare_func)); if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } if (!info_p->length) { return ecma_copy_value (this_arg); } ecma_value_t ret_value = ECMA_VALUE_EMPTY; JMEM_DEFINE_LOCAL_ARRAY (values_buffer, info_p->length, ecma_value_t); uint32_t buffer_index = 0; ecma_typedarray_getter_fn_t typedarray_getter_cb = ecma_get_typedarray_getter_fn (info_p->id); uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; uint8_t *limit_p = buffer_p + (info_p->length << info_p->shift); ecma_typedarray_setter_fn_t typedarray_setter_cb; /* Copy unsorted array into a native c array. */ while (buffer_p < limit_p) { JERRY_ASSERT (buffer_index < info_p->length); ecma_value_t element_value = typedarray_getter_cb (buffer_p); values_buffer[buffer_index++] = element_value; buffer_p += info_p->element_size; } JERRY_ASSERT (buffer_index == info_p->length); const ecma_builtin_helper_sort_compare_fn_t sort_cb = &ecma_builtin_typedarray_prototype_sort_compare_helper; ecma_value_t sort_value = ecma_builtin_helper_array_merge_sort_helper (values_buffer, (uint32_t) (info_p->length), compare_func, sort_cb, info_p->array_buffer_p); if (ECMA_IS_VALUE_ERROR (sort_value)) { ret_value = sort_value; goto free_values; } JERRY_ASSERT (sort_value == ECMA_VALUE_EMPTY); if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } typedarray_setter_cb = ecma_get_typedarray_setter_fn (info_p->id); buffer_p = limit_p - (info_p->length << info_p->shift); buffer_index = 0; /* Put sorted values from the native array back into the typedarray buffer. */ while (buffer_p < limit_p) { JERRY_ASSERT (buffer_index < info_p->length); ecma_value_t element_value = values_buffer[buffer_index++]; ecma_value_t set_element = typedarray_setter_cb (buffer_p, element_value); if (ECMA_IS_VALUE_ERROR (set_element)) { ret_value = set_element; goto free_values; } buffer_p += info_p->element_size; } JERRY_ASSERT (buffer_index == info_p->length); ret_value = ecma_copy_value (this_arg); free_values: /* Free values that were copied to the local array. */ for (uint32_t index = 0; index < info_p->length; index++) { ecma_free_value (values_buffer[index]); } JMEM_FINALIZE_LOCAL_ARRAY (values_buffer); return ret_value; } /* ecma_builtin_typedarray_prototype_sort */ /** * The %TypedArray%.prototype object's 'find' and 'findIndex' routine helper * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_find_helper (ecma_value_t this_arg, /**< this argument */ ecma_typedarray_info_t *info_p, /**< object info */ ecma_value_t predicate, /**< callback function */ ecma_value_t predicate_this_arg, /**< this argument for * invoke predicate */ bool is_find) /**< true - find routine * false - findIndex routine */ { if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } JERRY_ASSERT (ecma_is_value_object (predicate)); ecma_object_t *func_object_p = ecma_get_object_from_value (predicate); uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; uint8_t *limit_p = buffer_p + (info_p->length << info_p->shift); ecma_typedarray_getter_fn_t typedarray_getter_cb = ecma_get_typedarray_getter_fn (info_p->id); uint32_t buffer_index = 0; while (buffer_p < limit_p) { JERRY_ASSERT (buffer_index < info_p->length); ecma_value_t element_value = typedarray_getter_cb (buffer_p); buffer_p += info_p->element_size; ecma_value_t call_args[] = { element_value, ecma_make_uint32_value (buffer_index), this_arg }; ecma_value_t call_value = ecma_op_function_call (func_object_p, predicate_this_arg, call_args, 3); if (ECMA_IS_VALUE_ERROR (call_value)) { ecma_free_value (element_value); return call_value; } if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { ecma_free_value (element_value); ecma_free_value (call_value); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } bool call_result = ecma_op_to_boolean (call_value); ecma_free_value (call_value); if (call_result) { if (is_find) { return element_value; } ecma_free_value (element_value); return ecma_make_uint32_value (buffer_index); } buffer_index++; ecma_free_value (element_value); } return is_find ? ECMA_VALUE_UNDEFINED : ecma_make_integer_value (-1); } /* ecma_builtin_typedarray_prototype_find_helper */ /** * The %TypedArray%.prototype object's 'at' routine * * See also: * ECMA-262 Stage 3 Draft Relative Indexing Method proposal * from: https://tc39.es/proposal-relative-indexing-method * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_at (ecma_typedarray_info_t *info_p, /**< object info */ const ecma_value_t index) /**< index argument */ { ecma_length_t len = info_p->length; ecma_length_t res_index; ecma_value_t return_value = ecma_builtin_helper_calculate_index (index, len, &res_index); if (return_value != ECMA_VALUE_EMPTY) { return return_value; } if (res_index >= UINT32_MAX) { return ECMA_VALUE_UNDEFINED; } return ecma_get_typedarray_element (info_p, (uint32_t) res_index); } /* ecma_builtin_typedarray_prototype_at */ /** * The %TypedArray%.prototype object's 'indexOf' routine * * See also: * ECMA-262 v6, 22.2.3.13 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_index_of (ecma_typedarray_info_t *info_p, /**< object info */ const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } #if JERRY_BUILTIN_BIGINT bool is_bigint = ECMA_TYPEDARRAY_IS_BIGINT_TYPE (info_p->id); #else /* !JERRY_BUILTIN_BIGINT */ bool is_bigint = false; #endif /* JERRY_BUILTIN_BIGINT */ uint32_t from_index; /* 5. */ if (args_number == 0 || (!ecma_is_value_number (args[0]) && !is_bigint) || info_p->length == 0) { return ecma_make_integer_value (-1); } if (args_number == 1) { from_index = 0; } else { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (args[1], info_p->length, &from_index))) { return ECMA_VALUE_ERROR; } } uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); uint8_t *limit_p = buffer_p + (info_p->length << info_p->shift); ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); buffer_p += from_index << info_p->shift; /* 11. */ while (buffer_p < limit_p) { ecma_value_t element = getter_cb (buffer_p); if (ecma_op_same_value_zero (args[0], element, true)) { ecma_free_value (element); return ecma_make_number_value (from_index); } ecma_free_value (element); buffer_p += info_p->element_size; from_index++; } /* 12. */ return ecma_make_integer_value (-1); } /* ecma_builtin_typedarray_prototype_index_of */ /** * The %TypedArray%.prototype object's 'lastIndexOf' routine * * See also: * ECMA-262 v6, 22.2.3.16 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_last_index_of (ecma_typedarray_info_t *info_p, /**< object info */ const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } #if JERRY_BUILTIN_BIGINT bool is_bigint = ECMA_TYPEDARRAY_IS_BIGINT_TYPE (info_p->id); #else /* !JERRY_BUILTIN_BIGINT */ bool is_bigint = false; #endif /* JERRY_BUILTIN_BIGINT */ uint32_t from_index; /* 5. */ if (args_number == 0 || (!ecma_is_value_number (args[0]) && !is_bigint) || info_p->length == 0) { return ecma_make_integer_value (-1); } if (args_number == 1) { from_index = info_p->length - 1; } else { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (args[1], info_p->length, &from_index))) { return ECMA_VALUE_ERROR; } ecma_number_t to_int; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (args[1], &to_int))) { return ECMA_VALUE_ERROR; } if (info_p->length + to_int < 0) { return ecma_make_integer_value (-1); } from_index = JERRY_MIN (from_index, info_p->length - 1); } ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; uint8_t *current_element_p = buffer_p + (from_index << info_p->shift); /* 10. */ while (current_element_p >= buffer_p) { ecma_value_t element = getter_cb (current_element_p); if (ecma_op_same_value_zero (args[0], element, true)) { ecma_free_value (element); return ecma_make_number_value ((ecma_number_t) from_index); } ecma_free_value (element); current_element_p -= info_p->element_size; from_index--; } /* 11. */ return ecma_make_integer_value (-1); } /* ecma_builtin_typedarray_prototype_last_index_of */ /** * The %TypedArray%.prototype object's 'copyWithin' routine * * See also: * ECMA-262 v6, 22.2.3.5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_copy_within (ecma_value_t this_arg, /**< this argument */ ecma_typedarray_info_t *info_p, /**< object info */ const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint32_t relative_target = 0; uint32_t relative_start = 0; uint32_t relative_end = info_p->length; if (args_number > 0) { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (args[0], info_p->length, &relative_target))) { return ECMA_VALUE_ERROR; } if (args_number > 1) { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (args[1], info_p->length, &relative_start))) { return ECMA_VALUE_ERROR; } if (args_number > 2 && args[2] != ECMA_VALUE_UNDEFINED) { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (args[2], info_p->length, &relative_end))) { return ECMA_VALUE_ERROR; } } } } if (relative_target >= info_p->length || relative_start >= relative_end || relative_end == 0) { return ecma_copy_value (this_arg); } if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); uint32_t distance = relative_end - relative_start; uint32_t offset = info_p->length - relative_target; uint32_t count = JERRY_MIN (distance, offset); memmove (buffer_p + (relative_target << info_p->shift), buffer_p + (relative_start << info_p->shift), (size_t) (count << info_p->shift)); return ecma_copy_value (this_arg); } /* ecma_builtin_typedarray_prototype_copy_within */ /** * The %TypedArray%.prototype object's 'slice' routine * * See also: * ECMA-262 v6, 22.2.3.23 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_slice (ecma_value_t this_arg, /**< this argument */ ecma_typedarray_info_t *info_p, /**< object info */ const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { uint32_t relative_start = 0; uint32_t relative_end = info_p->length; if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } if (args_number > 0) { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (args[0], info_p->length, &relative_start))) { return ECMA_VALUE_ERROR; } if (args_number > 1 && args[1] != ECMA_VALUE_UNDEFINED && ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (args[1], info_p->length, &relative_end))) { return ECMA_VALUE_ERROR; } } uint8_t *src_buffer_p = ecma_typedarray_get_buffer (info_p); int32_t distance = (int32_t) (relative_end - relative_start); uint32_t count = distance > 0 ? (uint32_t) distance : 0; ecma_value_t len = ecma_make_number_value (count); // TODO: 22.2.3.23, 12-13. ecma_value_t new_typedarray = ecma_typedarray_species_create (this_arg, &len, 1); ecma_free_value (len); if (ECMA_IS_VALUE_ERROR (new_typedarray) || count == 0) { return new_typedarray; } ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); ecma_typedarray_info_t new_typedarray_info = ecma_typedarray_get_info (new_typedarray_p); if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { ecma_deref_object (new_typedarray_p); return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } uint8_t *dst_buffer_p = ecma_typedarray_get_buffer (&new_typedarray_info); JERRY_ASSERT (new_typedarray_info.offset == 0); src_buffer_p += relative_start << info_p->shift; if (info_p->id == new_typedarray_info.id) { // 22.2.3.23. Step 22. h-i. memcpy (dst_buffer_p, src_buffer_p, count << info_p->shift); } else { // 22.2.3.23. Step 21. b. ecma_typedarray_getter_fn_t src_typedarray_getter_cb = ecma_get_typedarray_getter_fn (info_p->id); ecma_typedarray_setter_fn_t new_typedarray_setter_cb = ecma_get_typedarray_setter_fn (new_typedarray_info.id); for (uint32_t idx = 0; idx < count; idx++) { ecma_value_t element = src_typedarray_getter_cb (src_buffer_p); ecma_value_t set_element = new_typedarray_setter_cb (dst_buffer_p, element); ecma_free_value (element); if (ECMA_IS_VALUE_ERROR (set_element)) { ecma_deref_object (new_typedarray_p); return set_element; } src_buffer_p += info_p->element_size; dst_buffer_p += new_typedarray_info.element_size; } } return new_typedarray; } /* ecma_builtin_typedarray_prototype_slice */ /** * The TypedArray.prototype's 'toLocaleString' single element operation routine. * * See also: * ECMA-262 v6, 22.1.3.26 steps 7-10 and 12.b-e * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_to_locale_string_helper (ecma_typedarray_info_t *info_p, /**< object info */ uint32_t index) /** array index */ { ecma_value_t element_value = ecma_get_typedarray_element (info_p, index); if (ECMA_IS_VALUE_ERROR (element_value)) { return element_value; } ecma_value_t call_value = ecma_op_invoke_by_magic_id (element_value, LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, NULL, 0); ecma_free_value (element_value); if (ECMA_IS_VALUE_ERROR (call_value)) { return call_value; } ecma_string_t *str_p = ecma_op_to_string (call_value); ecma_free_value (call_value); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } return ecma_make_string_value (str_p); } /* ecma_builtin_typedarray_prototype_to_locale_string_helper */ /** * The %TypedArray%.prototype object's 'toLocaleString' routine * * See also: * ECMA-262 v6, 22.2.3.27 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_typedarray_prototype_to_locale_string (ecma_typedarray_info_t *info_p) /**< object info */ { if (info_p->length == 0) { return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } ecma_value_t first_element = ecma_builtin_typedarray_prototype_to_locale_string_helper (info_p, 0); if (ECMA_IS_VALUE_ERROR (first_element)) { return first_element; } ecma_string_t *return_string_p = ecma_get_string_from_value (first_element); ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (return_string_p); ecma_deref_ecma_string (return_string_p); for (uint32_t k = 1; k < info_p->length; k++) { ecma_stringbuilder_append_byte (&builder, LIT_CHAR_COMMA); ecma_value_t next_element = ecma_builtin_typedarray_prototype_to_locale_string_helper (info_p, k); if (ECMA_IS_VALUE_ERROR (next_element)) { ecma_stringbuilder_destroy (&builder); return next_element; } ecma_string_t *next_element_p = ecma_get_string_from_value (next_element); ecma_stringbuilder_append (&builder, next_element_p); ecma_deref_ecma_string (next_element_p); } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_typedarray_prototype_to_locale_string */ /** * The %TypedArray%.prototype object's 'includes' routine * * See also: * ECMA-262 v11, 22.2.3.13. */ static ecma_value_t ecma_builtin_typedarray_prototype_includes (ecma_typedarray_info_t *info_p, /**< object info */ const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { #if JERRY_BUILTIN_BIGINT bool is_bigint = ECMA_TYPEDARRAY_IS_BIGINT_TYPE (info_p->id); #else /* !JERRY_BUILTIN_BIGINT */ bool is_bigint = false; #endif /* JERRY_BUILTIN_BIGINT */ if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) { return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED); } if (args_number == 0 || (!ecma_is_value_number (args[0]) && !is_bigint) || info_p->length == 0) { return ECMA_VALUE_FALSE; } uint32_t from_index = 0; if (args_number > 1) { if (ECMA_IS_VALUE_ERROR (ecma_builtin_helper_uint32_index_normalize (args[1], info_p->length, &from_index))) { return ECMA_VALUE_ERROR; } } uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); uint8_t *limit_p = buffer_p + (info_p->length << info_p->shift); buffer_p += from_index << info_p->shift; while (buffer_p < limit_p) { ecma_value_t element = getter_cb (buffer_p); if (ecma_op_same_value_zero (args[0], element, false)) { ecma_free_value (element); return ECMA_VALUE_TRUE; } ecma_free_value (element); buffer_p += info_p->element_size; } return ECMA_VALUE_FALSE; } /* ecma_builtin_typedarray_prototype_includes */ /** * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_typedarray_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide * routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { if (!ecma_is_typedarray (this_arg)) { if (builtin_routine_id == ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_TO_STRING_TAG_GETTER) { return ECMA_VALUE_UNDEFINED; } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_TYPED_ARRAY); } ecma_object_t *typedarray_p = ecma_get_object_from_value (this_arg); ecma_typedarray_info_t info = { 0 }; if (builtin_routine_id < ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BUFFER_GETTER) { info = ecma_typedarray_get_info (typedarray_p); if (builtin_routine_id != ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SUBARRAY && ECMA_ARRAYBUFFER_LAZY_ALLOC (info.array_buffer_p)) { return ECMA_VALUE_ERROR; } } if (builtin_routine_id < ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_INDEX_OF && !ecma_op_is_callable (arguments_list_p[0])) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } switch (builtin_routine_id) { case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_INCLUDES: { return ecma_builtin_typedarray_prototype_includes (&info, arguments_list_p, arguments_number); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_JOIN: { return ecma_builtin_typedarray_prototype_join (typedarray_p, arguments_list_p[0]); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_EVERY: case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SOME: case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FOR_EACH: { uint8_t offset = (uint8_t) (builtin_routine_id - ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_EVERY); return ecma_builtin_typedarray_prototype_exec_routine (this_arg, &info, arguments_list_p[0], arguments_list_p[1], (typedarray_routine_mode) offset); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_MAP: { return ecma_builtin_typedarray_prototype_map (this_arg, &info, arguments_list_p[0], arguments_list_p[1]); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REDUCE: case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REDUCE_RIGHT: { bool is_reduce = builtin_routine_id == ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REDUCE_RIGHT; return ecma_builtin_typedarray_prototype_reduce_with_direction (this_arg, &info, arguments_list_p, arguments_number, is_reduce); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FILTER: { return ecma_builtin_typedarray_prototype_filter (this_arg, &info, arguments_list_p[0], arguments_list_p[1]); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_REVERSE: { return ecma_builtin_typedarray_prototype_reverse (this_arg, &info); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SET: { return ecma_builtin_typedarray_prototype_set (this_arg, arguments_list_p[0], arguments_list_p[1]); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SUBARRAY: { return ecma_builtin_typedarray_prototype_subarray (this_arg, &info, arguments_list_p[0], arguments_list_p[1]); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FILL: { return ecma_builtin_typedarray_prototype_fill (this_arg, &info, arguments_list_p[0], arguments_list_p[1], arguments_list_p[2]); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SORT: { if (!ecma_is_value_undefined (arguments_list_p[0]) && !ecma_op_is_callable (arguments_list_p[0])) { return ecma_raise_type_error (ECMA_ERR_CALLBACK_IS_NOT_CALLABLE); } return ecma_builtin_typedarray_prototype_sort (this_arg, &info, arguments_list_p[0]); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FIND: case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FIND_INDEX: { bool is_find = builtin_routine_id == ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_FIND; return ecma_builtin_typedarray_prototype_find_helper (this_arg, &info, arguments_list_p[0], arguments_list_p[1], is_find); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_AT: { return ecma_builtin_typedarray_prototype_at (&info, arguments_list_p[0]); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_INDEX_OF: { return ecma_builtin_typedarray_prototype_index_of (&info, arguments_list_p, arguments_number); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_LAST_INDEX_OF: { return ecma_builtin_typedarray_prototype_last_index_of (&info, arguments_list_p, arguments_number); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_COPY_WITHIN: { return ecma_builtin_typedarray_prototype_copy_within (this_arg, &info, arguments_list_p, arguments_number); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SLICE: { return ecma_builtin_typedarray_prototype_slice (this_arg, &info, arguments_list_p, arguments_number); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_TO_LOCALE_STRING: { return ecma_builtin_typedarray_prototype_to_locale_string (&info); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_KEYS: case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_ENTRIES: { ecma_iterator_kind_t iter_id = (builtin_routine_id == ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_KEYS) ? ECMA_ITERATOR_KEYS : ECMA_ITERATOR_ENTRIES; return ecma_typedarray_iterators_helper (this_arg, iter_id); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BUFFER_GETTER: { ecma_object_t *buffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); ecma_ref_object (buffer_p); return ecma_make_object_value (buffer_p); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BYTELENGTH_GETTER: { ecma_object_t *buffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); if (ecma_arraybuffer_is_detached (buffer_p)) { return ecma_make_uint32_value (0); } uint32_t length = ecma_typedarray_get_length (typedarray_p); uint8_t shift = ecma_typedarray_get_element_size_shift (typedarray_p); return ecma_make_uint32_value (length << shift); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_BYTEOFFSET_GETTER: { return ecma_make_uint32_value (ecma_typedarray_get_offset (typedarray_p)); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_LENGTH_GETTER: { ecma_object_t *buffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); if (ecma_arraybuffer_is_detached (buffer_p)) { return ecma_make_uint32_value (0); } return ecma_make_uint32_value (ecma_typedarray_get_length (typedarray_p)); } case ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_TO_STRING_TAG_GETTER: { ecma_extended_object_t *object_p = (ecma_extended_object_t *) typedarray_p; return ecma_make_magic_string_value (ecma_get_typedarray_magic_string_id ((ecma_typedarray_type_t) object_p->u.cls.u1.typedarray_type)); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_typedarray_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-object.cpp000664 001750 001750 00000122365 15164251010 047653 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-object.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "ecma-proxy-object.h" #include "jcontext.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_OBJECT_ROUTINE_START = 0, ECMA_OBJECT_ROUTINE_CREATE, ECMA_OBJECT_ROUTINE_IS, ECMA_OBJECT_ROUTINE_SET_PROTOTYPE_OF, /* These should be in this order. */ ECMA_OBJECT_ROUTINE_DEFINE_PROPERTY, ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES, /* These should be in this order. */ ECMA_OBJECT_ROUTINE_ASSIGN, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTORS, ECMA_OBJECT_ROUTINE_HAS_OWN, ECMA_OBJECT_ROUTINE_GET_PROTOTYPE_OF, ECMA_OBJECT_ROUTINE_FROM_ENTRIES, ECMA_OBJECT_ROUTINE_KEYS, ECMA_OBJECT_ROUTINE_VALUES, ECMA_OBJECT_ROUTINE_ENTRIES, /* These should be in this order. */ ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_NAMES, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS, /* These should be in this order. */ ECMA_OBJECT_ROUTINE_FREEZE, ECMA_OBJECT_ROUTINE_PREVENT_EXTENSIONS, ECMA_OBJECT_ROUTINE_SEAL, /* These should be in this order. */ ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE, ECMA_OBJECT_ROUTINE_IS_FROZEN, ECMA_OBJECT_ROUTINE_IS_SEALED, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-object.inc.h" #define BUILTIN_UNDERSCORED_ID object #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup object ECMA Object object built-in * @{ */ /** * Handle calling [[Call]] of built-in Object object * * @return ecma value */ ecma_value_t ecma_builtin_object_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { if (arguments_list_len == 0 || ecma_is_value_undefined (arguments_list_p[0]) || ecma_is_value_null (arguments_list_p[0])) { return ecma_make_object_value (ecma_op_create_object_object_noarg ()); } return ecma_op_to_object (arguments_list_p[0]); } /* ecma_builtin_object_dispatch_call */ /** * Handle calling [[Construct]] of built-in Object object * * @return ecma value */ ecma_value_t ecma_builtin_object_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { if (JERRY_CONTEXT (current_new_target_p) != ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT)) { ecma_object_t *prototype_obj_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); if (JERRY_UNLIKELY (prototype_obj_p == NULL)) { return ECMA_VALUE_ERROR; } ecma_object_t *object_p = ecma_create_object (prototype_obj_p, 0, ECMA_OBJECT_TYPE_GENERAL); ecma_deref_object (prototype_obj_p); return ecma_make_object_value (object_p); } return ecma_builtin_object_dispatch_call (arguments_list_p, arguments_list_len); } /* ecma_builtin_object_dispatch_construct */ /** * The Object object's 'getPrototypeOf' routine * * See also: * ECMA-262 v5, 15.2.3.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_object_get_prototype_of (ecma_object_t *obj_p) /**< routine's argument */ { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { return ecma_proxy_object_get_prototype_of (obj_p); } #endif /* JERRY_BUILTIN_PROXY */ jmem_cpointer_t proto_cp = ecma_op_ordinary_object_get_prototype_of (obj_p); if (proto_cp != JMEM_CP_NULL) { ecma_object_t *prototype_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp); ecma_ref_object (prototype_p); return ecma_make_object_value (prototype_p); } return ECMA_VALUE_NULL; } /* ecma_builtin_object_object_get_prototype_of */ /** * The Object object's 'setPrototypeOf' routine * * See also: * ES2015 19.1.2.18 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_object_set_prototype_of (ecma_value_t arg1, /**< routine's first argument */ ecma_value_t arg2) /**< routine's second argument */ { /* 1., 2. */ if (!ecma_op_require_object_coercible (arg1)) { return ECMA_VALUE_ERROR; } /* 3. */ if (!ecma_is_value_object (arg2) && !ecma_is_value_null (arg2)) { return ecma_raise_type_error (ECMA_ERR_PROTOTYPE_IS_NEITHER_OBJECT_NOR_NULL); } /* 4. */ if (!ecma_is_value_object (arg1)) { return ecma_copy_value (arg1); } ecma_object_t *obj_p = ecma_get_object_from_value (arg1); ecma_value_t status; /* 5. */ #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { status = ecma_proxy_object_set_prototype_of (obj_p, arg2); if (ECMA_IS_VALUE_ERROR (status)) { return status; } } else { #endif /* JERRY_BUILTIN_PROXY */ status = ecma_op_ordinary_object_set_prototype_of (obj_p, arg2); #if JERRY_BUILTIN_PROXY } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_false (status)) { return ecma_raise_type_error (ECMA_ERR_SET_PROTOTYPE); } JERRY_ASSERT (ecma_is_value_true (status)); ecma_ref_object (obj_p); return arg1; } /* ecma_builtin_object_object_set_prototype_of */ /** * The Object object's set __proto__ routine * * See also: * ECMA-262 v6, B.2.2.1.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_object_set_proto (ecma_value_t arg1, /**< routine's first argument */ ecma_value_t arg2) /**< routine's second argument */ { /* 1., 2. */ if (!ecma_op_require_object_coercible (arg1)) { return ECMA_VALUE_ERROR; } /* 3. */ if (!ecma_is_value_object (arg2) && !ecma_is_value_null (arg2)) { return ECMA_VALUE_UNDEFINED; } /* 4. */ if (!ecma_is_value_object (arg1)) { return ECMA_VALUE_UNDEFINED; } ecma_object_t *obj_p = ecma_get_object_from_value (arg1); ecma_value_t status; /* 5. */ #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { status = ecma_proxy_object_set_prototype_of (obj_p, arg2); if (ECMA_IS_VALUE_ERROR (status)) { return status; } } else { #endif /* JERRY_BUILTIN_PROXY */ status = ecma_op_ordinary_object_set_prototype_of (obj_p, arg2); #if JERRY_BUILTIN_PROXY } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_false (status)) { return ecma_raise_type_error (ECMA_ERR_SET_PROTOTYPE); } JERRY_ASSERT (ecma_is_value_true (status)); return ECMA_VALUE_UNDEFINED; } /* ecma_builtin_object_object_set_proto */ /** * SetIntegrityLevel operation * * See also: * ECMA-262 v6, 7.3.14 * * @return ECMA_VALUE_ERROR - if the operation raised an error * ECMA_VALUE_{TRUE/FALSE} - depends on whether the integrity level has been set successfully */ static ecma_value_t ecma_builtin_object_set_integrity_level (ecma_object_t *obj_p, /**< object */ bool is_seal) /**< true - set "sealed" * false - set "frozen" */ { /* 3. */ #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { ecma_value_t status = ecma_proxy_object_prevent_extensions (obj_p); if (!ecma_is_value_true (status)) { return status; } } else #endif /* JERRY_BUILTIN_PROXY */ { ecma_op_ordinary_object_prevent_extensions (obj_p); } /* 6. */ ecma_collection_t *props_p = ecma_op_object_own_property_keys (obj_p, JERRY_PROPERTY_FILTER_ALL); #if JERRY_BUILTIN_PROXY if (props_p == NULL) { return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t *buffer_p = props_p->buffer_p; if (is_seal) { /* 8.a */ for (uint32_t i = 0; i < props_p->item_count; i++) { ecma_string_t *property_name_p = ecma_get_prop_name_from_value (buffer_p[i]); ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_p, property_name_p, &prop_desc); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (status)) { ecma_collection_free (props_p); return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_false (status)) { continue; } prop_desc.flags &= (uint16_t) ~JERRY_PROP_IS_CONFIGURABLE; prop_desc.flags |= JERRY_PROP_SHOULD_THROW; /* 8.a.i */ ecma_value_t define_own_prop_ret = ecma_op_object_define_own_property (obj_p, property_name_p, &prop_desc); ecma_free_property_descriptor (&prop_desc); /* 8.a.ii */ if (ECMA_IS_VALUE_ERROR (define_own_prop_ret)) { ecma_collection_free (props_p); return define_own_prop_ret; } ecma_free_value (define_own_prop_ret); } } else { /* 9.a */ for (uint32_t i = 0; i < props_p->item_count; i++) { ecma_string_t *property_name_p = ecma_get_prop_name_from_value (buffer_p[i]); /* 9.1 */ ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_p, property_name_p, &prop_desc); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (status)) { ecma_collection_free (props_p); return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_false (status)) { continue; } /* 9.2 */ if ((prop_desc.flags & (JERRY_PROP_IS_WRITABLE_DEFINED | JERRY_PROP_IS_WRITABLE)) == (JERRY_PROP_IS_WRITABLE_DEFINED | JERRY_PROP_IS_WRITABLE)) { prop_desc.flags &= (uint16_t) ~JERRY_PROP_IS_WRITABLE; } prop_desc.flags &= (uint16_t) ~JERRY_PROP_IS_CONFIGURABLE; prop_desc.flags |= JERRY_PROP_SHOULD_THROW; /* 9.3 */ ecma_value_t define_own_prop_ret = ecma_op_object_define_own_property (obj_p, property_name_p, &prop_desc); ecma_free_property_descriptor (&prop_desc); /* 9.4 */ if (ECMA_IS_VALUE_ERROR (define_own_prop_ret)) { ecma_collection_free (props_p); return define_own_prop_ret; } ecma_free_value (define_own_prop_ret); } } ecma_collection_free (props_p); return ECMA_VALUE_TRUE; } /* ecma_builtin_object_set_integrity_level */ /** * The Object object's 'seal' routine * * See also: * ECMA-262 v5, 15.2.3.8 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_object_seal (ecma_object_t *obj_p) /**< routine's argument */ { ecma_value_t status = ecma_builtin_object_set_integrity_level (obj_p, true); if (ECMA_IS_VALUE_ERROR (status)) { return status; } #if JERRY_BUILTIN_PROXY if (ecma_is_value_false (status)) { return ecma_raise_type_error (ECMA_ERR_OBJECT_CANNOT_BE_SEALED); } #endif /* JERRY_BUILTIN_PROXY */ /* 4. */ ecma_ref_object (obj_p); return ecma_make_object_value (obj_p); } /* ecma_builtin_object_object_seal */ /** * The Object object's 'freeze' routine * * See also: * ECMA-262 v5, 15.2.3.9 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_object_freeze (ecma_object_t *obj_p) /**< routine's argument */ { ecma_value_t status = ecma_builtin_object_set_integrity_level (obj_p, false); if (ECMA_IS_VALUE_ERROR (status)) { return status; } #if JERRY_BUILTIN_PROXY if (ecma_is_value_false (status)) { return ecma_raise_type_error (ECMA_ERR_OBJECT_CANNOT_BE_FROZEN); } #endif /* JERRY_BUILTIN_PROXY */ /* 4. */ ecma_ref_object (obj_p); return ecma_make_object_value (obj_p); } /* ecma_builtin_object_object_freeze */ /** * The Object object's 'preventExtensions' routine * * See also: * ECMA-262 v5, 15.2.3.10 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_object_prevent_extensions (ecma_object_t *obj_p) /**< routine's argument */ { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { ecma_value_t status = ecma_proxy_object_prevent_extensions (obj_p); if (ECMA_IS_VALUE_ERROR (status)) { return status; } if (ecma_is_value_false (status)) { return ecma_raise_type_error (ECMA_ERR_SET_EXTENSIBLE_PROPERTY); } JERRY_ASSERT (ecma_is_value_true (status)); } else { #endif /* JERRY_BUILTIN_PROXY */ ecma_op_ordinary_object_prevent_extensions (obj_p); #if JERRY_BUILTIN_PROXY } #endif /* JERRY_BUILTIN_PROXY */ ecma_ref_object (obj_p); return ecma_make_object_value (obj_p); } /* ecma_builtin_object_object_prevent_extensions */ /** * The Object object's 'isSealed' and 'isFrozen' routines * * See also: * ECMA-262 v5, 15.2.3.11 * ECMA-262 v5, 15.2.3.12 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_test_integrity_level (ecma_object_t *obj_p, /**< routine's argument */ int mode) /**< routine mode */ { JERRY_ASSERT (mode == ECMA_OBJECT_ROUTINE_IS_FROZEN || mode == ECMA_OBJECT_ROUTINE_IS_SEALED); /* 3. */ bool is_extensible; #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { ecma_value_t status = ecma_proxy_object_is_extensible (obj_p); if (ECMA_IS_VALUE_ERROR (status)) { return status; } is_extensible = ecma_is_value_true (status); } else #endif /* JERRY_BUILTIN_PROXY */ { is_extensible = ecma_op_ordinary_object_is_extensible (obj_p); } if (is_extensible) { return ECMA_VALUE_FALSE; } /* the value can be updated in the loop below */ ecma_value_t ret_value = ECMA_VALUE_TRUE; /* 2. */ ecma_collection_t *props_p = ecma_op_object_own_property_keys (obj_p, JERRY_PROPERTY_FILTER_ALL); #if JERRY_BUILTIN_PROXY if (props_p == NULL) { return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t *buffer_p = props_p->buffer_p; for (uint32_t i = 0; i < props_p->item_count; i++) { ecma_string_t *property_name_p = ecma_get_prop_name_from_value (buffer_p[i]); /* 2.a */ ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_p, property_name_p, &prop_desc); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (status)) { ret_value = status; break; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_false (status)) { continue; } bool is_writable_data = ((prop_desc.flags & (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE)) == (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE)); bool is_configurable = (prop_desc.flags & JERRY_PROP_IS_CONFIGURABLE); ecma_free_property_descriptor (&prop_desc); /* 2.b for isFrozen */ /* 2.b for isSealed, 2.c for isFrozen */ if ((mode == ECMA_OBJECT_ROUTINE_IS_FROZEN && is_writable_data) || is_configurable) { ret_value = ECMA_VALUE_FALSE; break; } } ecma_collection_free (props_p); return ret_value; } /* ecma_builtin_object_test_integrity_level */ /** * The Object object's 'isExtensible' routine * * See also: * ECMA-262 v5, 15.2.3.13 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_object_is_extensible (ecma_object_t *obj_p) /**< routine's argument */ { #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { return ecma_proxy_object_is_extensible (obj_p); } #endif /* JERRY_BUILTIN_PROXY */ return ecma_make_boolean_value (ecma_op_ordinary_object_is_extensible (obj_p)); } /* ecma_builtin_object_object_is_extensible */ /** * Common implementation of the Object object's 'keys', 'values', 'entries' routines * * See also: * ECMA-262 v11, 19.1.2.17 * ECMA-262 v11, 19.1.2.22 * ECMA-262 v11, 19.1.2.5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_object_keys_values_helper (ecma_object_t *obj_p, /**< routine's first argument */ ecma_enumerable_property_names_options_t option) /**< listing option */ { /* 2. */ ecma_collection_t *props_p = ecma_op_object_get_enumerable_property_names (obj_p, option); if (props_p == NULL) { return ECMA_VALUE_ERROR; } /* 3. */ return ecma_op_new_array_object_from_collection (props_p, option != ECMA_ENUMERABLE_PROPERTY_KEYS); } /* ecma_builtin_object_object_keys_values_helper */ /** * The Object object's 'getOwnPropertyDescriptor' routine * * See also: * ECMA-262 v5, 15.2.3.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_object_get_own_property_descriptor (ecma_object_t *obj_p, /**< routine's first argument */ ecma_string_t *name_str_p) /**< routine's second argument */ { /* 3. */ ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_p, name_str_p, &prop_desc); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (status)) { return status; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_true (status)) { /* 4. */ ecma_object_t *desc_obj_p = ecma_op_from_property_descriptor (&prop_desc); ecma_free_property_descriptor (&prop_desc); return ecma_make_object_value (desc_obj_p); } return ECMA_VALUE_UNDEFINED; } /* ecma_builtin_object_object_get_own_property_descriptor */ /** * The Object object's 'getOwnPropertyDescriptors' routine * * See also: * ECMA-262 v11, 19.1.2.9 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_object_get_own_property_descriptors (ecma_object_t *obj_p) /**< routine's first argument */ { /* 2 */ ecma_collection_t *prop_names_p = ecma_op_object_own_property_keys (obj_p, JERRY_PROPERTY_FILTER_ALL); #if JERRY_BUILTIN_PROXY if (prop_names_p == NULL) { return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t *names_buffer_p = prop_names_p->buffer_p; /* 3 */ ecma_object_t *object_prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); ecma_object_t *descriptors_p = ecma_create_object (object_prototype_p, 0, ECMA_OBJECT_TYPE_GENERAL); /* 4 */ for (uint32_t i = 0; i < prop_names_p->item_count; i++) { ecma_string_t *property_name_p = ecma_get_prop_name_from_value (names_buffer_p[i]); /* 4.a */ ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_p, property_name_p, &prop_desc); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (status)) { ecma_deref_object (descriptors_p); ecma_collection_free (prop_names_p); return status; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_true (status)) { /* 4.b */ ecma_object_t *desc_obj_p = ecma_op_from_property_descriptor (&prop_desc); /* 4.c */ ecma_property_value_t *value_p = ecma_create_named_data_property (descriptors_p, property_name_p, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); value_p->value = ecma_make_object_value (desc_obj_p); ecma_deref_object (desc_obj_p); ecma_free_property_descriptor (&prop_desc); } } ecma_collection_free (prop_names_p); return ecma_make_object_value (descriptors_p); } /* ecma_builtin_object_object_get_own_property_descriptors */ /** * The Object object's 'defineProperties' routine * * See also: * ECMA-262 v5, 15.2.3.7 * ECMA-262 v11, 19.1.2.3.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_object_define_properties (ecma_object_t *obj_p, /**< routine's first argument */ ecma_value_t arg2) /**< routine's second argument */ { /* 2. */ ecma_value_t props = ecma_op_to_object (arg2); if (ECMA_IS_VALUE_ERROR (props)) { return props; } ecma_object_t *props_p = ecma_get_object_from_value (props); /* 3. */ ecma_collection_t *prop_names_p = ecma_op_object_own_property_keys (props_p, JERRY_PROPERTY_FILTER_ALL); ecma_value_t ret_value = ECMA_VALUE_ERROR; #if JERRY_BUILTIN_PROXY if (prop_names_p == NULL) { ecma_deref_object (props_p); return ret_value; } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t *buffer_p = prop_names_p->buffer_p; /* 4. */ JMEM_DEFINE_LOCAL_ARRAY (property_descriptors, prop_names_p->item_count, ecma_property_descriptor_t); uint32_t property_descriptor_number = 0; ecma_collection_t *enum_prop_names = ecma_new_collection (); /* 5. */ for (uint32_t i = 0; i < prop_names_p->item_count; i++) { ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (buffer_p[i]); ecma_property_descriptor_t prop_desc; ecma_value_t get_desc = ecma_op_object_get_own_property_descriptor (props_p, prop_name_p, &prop_desc); if (ECMA_IS_VALUE_ERROR (get_desc)) { goto cleanup; } if (ecma_is_value_true (get_desc)) { if (prop_desc.flags & JERRY_PROP_IS_ENUMERABLE) { ecma_value_t desc_obj = ecma_op_object_get (props_p, prop_name_p); if (ECMA_IS_VALUE_ERROR (desc_obj)) { ecma_free_property_descriptor (&prop_desc); goto cleanup; } ecma_value_t conv_result = ecma_op_to_property_descriptor (desc_obj, &property_descriptors[property_descriptor_number]); property_descriptors[property_descriptor_number].flags |= JERRY_PROP_SHOULD_THROW; ecma_free_value (desc_obj); if (ECMA_IS_VALUE_ERROR (conv_result)) { ecma_free_property_descriptor (&prop_desc); goto cleanup; } property_descriptor_number++; ecma_free_value (conv_result); ecma_ref_ecma_string (prop_name_p); ecma_collection_push_back (enum_prop_names, buffer_p[i]); } ecma_free_property_descriptor (&prop_desc); } } /* 6. */ for (uint32_t i = 0; i < enum_prop_names->item_count; i++) { ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (enum_prop_names->buffer_p[i]); ecma_value_t define_own_prop_ret = ecma_op_object_define_own_property (obj_p, prop_name_p, &property_descriptors[i]); if (ECMA_IS_VALUE_ERROR (define_own_prop_ret)) { goto cleanup; } ecma_free_value (define_own_prop_ret); } ecma_ref_object (obj_p); ret_value = ecma_make_object_value (obj_p); cleanup: /* Clean up. */ for (uint32_t index = 0; index < property_descriptor_number; index++) { ecma_free_property_descriptor (&property_descriptors[index]); } ecma_collection_free (enum_prop_names); JMEM_FINALIZE_LOCAL_ARRAY (property_descriptors); ecma_collection_free (prop_names_p); ecma_deref_object (props_p); return ret_value; } /* ecma_builtin_object_object_define_properties */ /** * The Object object's 'create' routine * * See also: * ECMA-262 v5, 15.2.3.5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_object_create (ecma_value_t arg1, /**< routine's first argument */ ecma_value_t arg2) /**< routine's second argument */ { /* 1. */ if (!ecma_is_value_object (arg1) && !ecma_is_value_null (arg1)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); } ecma_object_t *obj_p = NULL; if (!ecma_is_value_null (arg1)) { obj_p = ecma_get_object_from_value (arg1); } /* 2-3. */ ecma_object_t *result_obj_p = ecma_op_create_object_object_noarg_and_set_prototype (obj_p); /* 4. */ if (!ecma_is_value_undefined (arg2)) { ecma_value_t obj = ecma_builtin_object_object_define_properties (result_obj_p, arg2); if (ECMA_IS_VALUE_ERROR (obj)) { ecma_deref_object (result_obj_p); return obj; } ecma_free_value (obj); } /* 5. */ return ecma_make_object_value (result_obj_p); } /* ecma_builtin_object_object_create */ /** * The Object object's 'defineProperty' routine * * See also: * ECMA-262 v5, 15.2.3.6 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_object_define_property (ecma_object_t *obj_p, /**< routine's first argument */ ecma_string_t *name_str_p, /**< routine's second argument */ ecma_value_t arg3) /**< routine's third argument */ { ecma_property_descriptor_t prop_desc; ecma_value_t conv_result = ecma_op_to_property_descriptor (arg3, &prop_desc); if (ECMA_IS_VALUE_ERROR (conv_result)) { return conv_result; } prop_desc.flags |= JERRY_PROP_SHOULD_THROW; ecma_value_t define_own_prop_ret = ecma_op_object_define_own_property (obj_p, name_str_p, &prop_desc); ecma_free_property_descriptor (&prop_desc); ecma_free_value (conv_result); if (ECMA_IS_VALUE_ERROR (define_own_prop_ret)) { return define_own_prop_ret; } if (ecma_is_value_false (define_own_prop_ret)) { return ecma_raise_type_error (ECMA_ERR_THE_REQUESTED_PROPERTY_UPDATE_CANNOT_BE_PERFORMED); } JERRY_ASSERT (ecma_is_value_true (define_own_prop_ret)); ecma_ref_object (obj_p); ecma_free_value (define_own_prop_ret); return ecma_make_object_value (obj_p); } /* ecma_builtin_object_object_define_property */ /** * The Object object's 'assign' routine * * See also: * ECMA-262 v6, 19.1.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_object_assign (ecma_object_t *target_p, /**< target object */ const ecma_value_t arguments_list_p[], /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; /* 4-5. */ for (uint32_t i = 0; i < arguments_list_len && ecma_is_value_empty (ret_value); i++) { ecma_value_t next_source = arguments_list_p[i]; /* 5.a */ if (ecma_is_value_undefined (next_source) || ecma_is_value_null (next_source)) { continue; } /* 5.b.i */ ecma_value_t from_value = ecma_op_to_object (next_source); /* null and undefined cases are handled above, so this must be a valid object */ JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (from_value)); ecma_object_t *from_obj_p = ecma_get_object_from_value (from_value); /* 5.b.iii */ ecma_collection_t *props_p = ecma_op_object_own_property_keys (from_obj_p, JERRY_PROPERTY_FILTER_ALL); #if JERRY_BUILTIN_PROXY if (props_p == NULL) { ecma_deref_object (from_obj_p); return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ ecma_value_t *buffer_p = props_p->buffer_p; for (uint32_t j = 0; (j < props_p->item_count) && ecma_is_value_empty (ret_value); j++) { ecma_string_t *property_name_p = ecma_get_prop_name_from_value (buffer_p[j]); /* 5.c.i-ii */ ecma_property_descriptor_t prop_desc; ecma_value_t desc_status = ecma_op_object_get_own_property_descriptor (from_obj_p, property_name_p, &prop_desc); #if JERRY_BUILTIN_PROXY if (ECMA_IS_VALUE_ERROR (desc_status)) { ret_value = desc_status; break; } #endif /* JERRY_BUILTIN_PROXY */ if (ecma_is_value_false (desc_status)) { continue; } /* 5.c.iii */ if (prop_desc.flags & JERRY_PROP_IS_ENUMERABLE) { /* 5.c.iii.1 */ ecma_value_t prop_value = ecma_op_object_get (from_obj_p, property_name_p); /* 5.c.iii.2 */ if (ECMA_IS_VALUE_ERROR (prop_value)) { ret_value = prop_value; } else { /* 5.c.iii.3 */ ecma_value_t status = ecma_op_object_put (target_p, property_name_p, prop_value, true); /* 5.c.iii.4 */ if (ECMA_IS_VALUE_ERROR (status)) { ret_value = status; } } ecma_free_value (prop_value); } ecma_free_property_descriptor (&prop_desc); } ecma_deref_object (from_obj_p); ecma_collection_free (props_p); } /* 6. */ if (ecma_is_value_empty (ret_value)) { ecma_ref_object (target_p); return ecma_make_object_value (target_p); } return ret_value; } /* ecma_builtin_object_object_assign */ /** * The Object object's 'is' routine * * See also: * ECMA-262 v6, 19.1.2.10 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_object_is (ecma_value_t arg1, /**< routine's first argument */ ecma_value_t arg2) /**< routine's second argument */ { return ecma_op_same_value (arg1, arg2) ? ECMA_VALUE_TRUE : ECMA_VALUE_FALSE; } /* ecma_builtin_object_object_is */ /** * The Object object's 'fromEntries' routine * * See also: * ECMA-262 v10, 19.1.2.7 * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_from_entries (ecma_value_t iterator) /**< object's iterator */ { JERRY_ASSERT (ecma_op_require_object_coercible (iterator)); /* 2 */ ecma_object_t *object_prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); ecma_object_t *obj_p = ecma_create_object (object_prototype_p, 0, ECMA_OBJECT_TYPE_GENERAL); /* 6.a */ ecma_value_t next_method; ecma_value_t result = ecma_op_get_iterator (iterator, ECMA_VALUE_SYNC_ITERATOR, &next_method); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (obj_p); return result; } const ecma_value_t original_iterator = result; /* 6.b */ while (true) { /* 6.a.i */ result = ecma_op_iterator_step (original_iterator, next_method); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_iterator; } /* 6.a.ii */ if (ecma_is_value_false (result)) { break; } /* 6.a.iii */ const ecma_value_t next = result; result = ecma_op_iterator_value (next); ecma_free_value (next); if (ECMA_IS_VALUE_ERROR (result)) { goto cleanup_iterator; } /* 6.a.iv */ if (!ecma_is_value_object (result)) { ecma_free_value (result); ecma_raise_type_error (ECMA_ERR_ITERATOR_VALUE_IS_NOT_AN_OBJECT); result = ecma_op_iterator_close (original_iterator); JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result)); goto cleanup_iterator; } /* 6.a.v-vi */ ecma_object_t *next_object_p = ecma_get_object_from_value (result); result = ecma_op_object_get_by_index (next_object_p, 0); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (next_object_p); ecma_op_iterator_close (original_iterator); goto cleanup_iterator; } const ecma_value_t key = result; result = ecma_op_object_get_by_index (next_object_p, 1); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (next_object_p); ecma_free_value (key); ecma_op_iterator_close (original_iterator); goto cleanup_iterator; } /* 6.a.vii */ const ecma_value_t value = result; ecma_string_t *property_key = ecma_op_to_property_key (key); if (property_key == NULL) { ecma_deref_object (next_object_p); ecma_free_value (key); ecma_op_iterator_close (original_iterator); result = ECMA_VALUE_ERROR; goto cleanup_iterator; } ecma_property_t *property_p = ecma_find_named_property (obj_p, property_key); if (property_p == NULL) { ecma_property_value_t *prop; prop = ecma_create_named_data_property (obj_p, property_key, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, NULL); prop->value = ecma_copy_value_if_not_object (value); } else { ecma_named_data_property_assign_value (obj_p, ECMA_PROPERTY_VALUE_PTR (property_p), value); } ecma_deref_ecma_string (property_key); ecma_free_value (key); ecma_free_value (value); ecma_deref_object (next_object_p); } ecma_ref_object (obj_p); result = ecma_make_object_value (obj_p); cleanup_iterator: ecma_free_value (original_iterator); ecma_free_value (next_method); ecma_deref_object (obj_p); return result; } /* ecma_builtin_object_from_entries */ /** * GetOwnPropertyKeys abstract method * * See also: * ECMA-262 v11, 19.1.2.11.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_op_object_get_own_property_keys (ecma_value_t this_arg, /**< this argument */ uint16_t type) /**< routine type */ { /* 1. */ ecma_value_t object = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (object)) { return object; } ecma_object_t *obj_p = ecma_get_object_from_value (object); /* 2. */ jerry_property_filter_t filter = JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS; if (type == ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS) { filter = (jerry_property_filter_t) ((unsigned int) JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS | (unsigned int) JERRY_PROPERTY_FILTER_EXCLUDE_INTEGER_INDICES); } ecma_collection_t *props_p = ecma_op_object_own_property_keys (obj_p, filter); if (props_p == NULL) { ecma_deref_object (obj_p); return ECMA_VALUE_ERROR; } /* 3. */ ecma_collection_t *name_list_p = ecma_new_collection (); /* 4. */ for (uint32_t i = 0; i < props_p->item_count; i++) { ecma_value_t prop_name = props_p->buffer_p[i]; ecma_string_t *name_p = ecma_get_prop_name_from_value (prop_name); if ((ecma_prop_name_is_symbol (name_p) && type == ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS) || (ecma_is_value_string (prop_name) && type == ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_NAMES)) { ecma_ref_ecma_string (name_p); ecma_collection_push_back (name_list_p, prop_name); } } ecma_value_t result_array = ecma_op_new_array_object_from_collection (name_list_p, false); ecma_deref_object (obj_p); ecma_collection_free (props_p); return result_array; } /* ecma_op_object_get_own_property_keys */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED_2 (this_arg, arguments_number); ecma_value_t arg1 = arguments_list_p[0]; ecma_value_t arg2 = arguments_list_p[1]; /* No specialization for the arguments */ switch (builtin_routine_id) { case ECMA_OBJECT_ROUTINE_CREATE: { return ecma_builtin_object_object_create (arg1, arg2); } case ECMA_OBJECT_ROUTINE_SET_PROTOTYPE_OF: { return ecma_builtin_object_object_set_prototype_of (arg1, arg2); } case ECMA_OBJECT_ROUTINE_IS: { return ecma_builtin_object_object_is (arg1, arg2); } default: { break; } } ecma_object_t *obj_p; if (builtin_routine_id <= ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES) { if (!ecma_is_value_object (arg1)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); } obj_p = ecma_get_object_from_value (arg1); if (builtin_routine_id == ECMA_OBJECT_ROUTINE_DEFINE_PROPERTY) { ecma_string_t *prop_name_p = ecma_op_to_property_key (arg2); if (prop_name_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_object_object_define_property (obj_p, prop_name_p, arguments_list_p[2]); ecma_deref_ecma_string (prop_name_p); return result; } JERRY_ASSERT (builtin_routine_id == ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES); return ecma_builtin_object_object_define_properties (obj_p, arg2); } else if (builtin_routine_id <= ECMA_OBJECT_ROUTINE_ENTRIES) { ecma_value_t object = ecma_op_to_object (arg1); if (ECMA_IS_VALUE_ERROR (object)) { return object; } obj_p = ecma_get_object_from_value (object); ecma_value_t result; switch (builtin_routine_id) { case ECMA_OBJECT_ROUTINE_GET_PROTOTYPE_OF: { result = ecma_builtin_object_object_get_prototype_of (obj_p); break; } case ECMA_OBJECT_ROUTINE_ASSIGN: { result = ecma_builtin_object_object_assign (obj_p, arguments_list_p + 1, arguments_number - 1); break; } case ECMA_OBJECT_ROUTINE_ENTRIES: case ECMA_OBJECT_ROUTINE_VALUES: case ECMA_OBJECT_ROUTINE_KEYS: { JERRY_ASSERT (builtin_routine_id - ECMA_OBJECT_ROUTINE_KEYS < ECMA_ENUMERABLE_PROPERTY__COUNT); const int option = builtin_routine_id - ECMA_OBJECT_ROUTINE_KEYS; result = ecma_builtin_object_object_keys_values_helper (obj_p, (ecma_enumerable_property_names_options_t) option); break; } case ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR: { ecma_string_t *prop_name_p = ecma_op_to_property_key (arg2); if (prop_name_p == NULL) { result = ECMA_VALUE_ERROR; break; } result = ecma_builtin_object_object_get_own_property_descriptor (obj_p, prop_name_p); ecma_deref_ecma_string (prop_name_p); break; } case ECMA_OBJECT_ROUTINE_HAS_OWN: { ecma_string_t *prop_name_p = ecma_op_to_property_key (arg2); if (prop_name_p == NULL) { result = ECMA_VALUE_ERROR; break; } result = ecma_op_object_has_own_property (obj_p, prop_name_p); ecma_deref_ecma_string (prop_name_p); break; } case ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTORS: { result = ecma_builtin_object_object_get_own_property_descriptors (obj_p); break; } case ECMA_OBJECT_ROUTINE_FROM_ENTRIES: { result = ecma_builtin_object_from_entries (arg1); break; } default: { JERRY_UNREACHABLE (); } } ecma_deref_object (obj_p); return result; } else if (builtin_routine_id <= ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS) { return ecma_op_object_get_own_property_keys (arg1, builtin_routine_id); } else if (builtin_routine_id <= ECMA_OBJECT_ROUTINE_SEAL) { if (!ecma_is_value_object (arg1)) { return ecma_copy_value (arg1); } obj_p = ecma_get_object_from_value (arg1); switch (builtin_routine_id) { case ECMA_OBJECT_ROUTINE_SEAL: { return ecma_builtin_object_object_seal (obj_p); } case ECMA_OBJECT_ROUTINE_FREEZE: { return ecma_builtin_object_object_freeze (obj_p); } case ECMA_OBJECT_ROUTINE_PREVENT_EXTENSIONS: { return ecma_builtin_object_object_prevent_extensions (obj_p); } default: { JERRY_UNREACHABLE (); } } } else { JERRY_ASSERT (builtin_routine_id <= ECMA_OBJECT_ROUTINE_IS_SEALED); if (!ecma_is_value_object (arg1)) { return ecma_make_boolean_value (builtin_routine_id != ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE); } obj_p = ecma_get_object_from_value (arg1); switch (builtin_routine_id) { case ECMA_OBJECT_ROUTINE_IS_SEALED: case ECMA_OBJECT_ROUTINE_IS_FROZEN: { return ecma_builtin_object_test_integrity_level (obj_p, builtin_routine_id); } case ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE: { return ecma_builtin_object_object_is_extensible (obj_p); } default: { JERRY_UNREACHABLE (); } } } } /* ecma_builtin_object_dispatch_routine */ /** * @} * @} * @} */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h000664 001750 001750 00000022075 15164251010 050057 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Global built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Simple value properties: * (property name, simple value, writable, enumerable, configurable) */ /* ECMA-262 v5, 15.1.1.3 */ SIMPLE_VALUE (LIT_MAGIC_STRING_UNDEFINED, ECMA_VALUE_UNDEFINED, ECMA_PROPERTY_FIXED) /* Number properties: * (property name, number value, writable, enumerable, configurable) */ /* ECMA-262 v5, 15.1.1.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_NAN, ECMA_BUILTIN_NUMBER_NAN, ECMA_PROPERTY_FIXED) /* ECMA-262 v5, 15.1.1.2 */ NUMBER_VALUE (LIT_MAGIC_STRING_INFINITY_UL, ECMA_BUILTIN_NUMBER_POSITIVE_INFINITY, ECMA_PROPERTY_FIXED) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.1.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_OBJECT_UL, ECMA_BUILTIN_ID_OBJECT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.1.4.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_FUNCTION_UL, ECMA_BUILTIN_ID_FUNCTION, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.1.4.3 */ #if JERRY_BUILTIN_ARRAY OBJECT_VALUE (LIT_MAGIC_STRING_ARRAY_UL, ECMA_BUILTIN_ID_ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_STRING /* ECMA-262 v5, 15.1.4.4 */ OBJECT_VALUE (LIT_MAGIC_STRING_STRING_UL, ECMA_BUILTIN_ID_STRING, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_BOOLEAN /* ECMA-262 v5, 15.1.4.5 */ OBJECT_VALUE (LIT_MAGIC_STRING_BOOLEAN_UL, ECMA_BUILTIN_ID_BOOLEAN, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_BOOLEAN */ #if JERRY_BUILTIN_NUMBER /* ECMA-262 v5, 15.1.4.6 */ OBJECT_VALUE (LIT_MAGIC_STRING_NUMBER_UL, ECMA_BUILTIN_ID_NUMBER, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_NUMBER */ #if JERRY_BUILTIN_DATE /* ECMA-262 v5, 15.1.4.7 */ OBJECT_VALUE (LIT_MAGIC_STRING_DATE_UL, ECMA_BUILTIN_ID_DATE, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_REGEXP /* ECMA-262 v5, 15.1.4.8 */ OBJECT_VALUE (LIT_MAGIC_STRING_REGEXP_UL, ECMA_BUILTIN_ID_REGEXP, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_REGEXP */ /* ECMA-262 v5, 15.1.4.9 */ OBJECT_VALUE (LIT_MAGIC_STRING_ERROR_UL, ECMA_BUILTIN_ID_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #if JERRY_BUILTIN_ERRORS /* ECMA-262 v5, 15.1.4.10 */ OBJECT_VALUE (LIT_MAGIC_STRING_EVAL_ERROR_UL, ECMA_BUILTIN_ID_EVAL_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.1.4.11 */ OBJECT_VALUE (LIT_MAGIC_STRING_RANGE_ERROR_UL, ECMA_BUILTIN_ID_RANGE_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.1.4.12 */ OBJECT_VALUE (LIT_MAGIC_STRING_REFERENCE_ERROR_UL, ECMA_BUILTIN_ID_REFERENCE_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.1.4.13 */ OBJECT_VALUE (LIT_MAGIC_STRING_SYNTAX_ERROR_UL, ECMA_BUILTIN_ID_SYNTAX_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.1.4.14 */ OBJECT_VALUE (LIT_MAGIC_STRING_TYPE_ERROR_UL, ECMA_BUILTIN_ID_TYPE_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.1.4.15 */ OBJECT_VALUE (LIT_MAGIC_STRING_URI_ERROR_UL, ECMA_BUILTIN_ID_URI_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ERRORS */ #if JERRY_BUILTIN_MATH /* ECMA-262 v5, 15.1.5.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_MATH_UL, ECMA_BUILTIN_ID_MATH, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_REFLECT /* ECMA-262 v6, 26.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_REFLECT_UL, ECMA_BUILTIN_ID_REFLECT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_REFLECT */ #if JERRY_BUILTIN_ATOMICS /* ECMA-262 v5, 15.1.5.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_ATOMICS_U, ECMA_BUILTIN_ID_ATOMICS, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_TYPEDARRAY OBJECT_VALUE (LIT_MAGIC_STRING_ARRAY_BUFFER_UL, ECMA_BUILTIN_ID_ARRAYBUFFER, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #if JERRY_BUILTIN_SHAREDARRAYBUFFER OBJECT_VALUE (LIT_MAGIC_STRING_SHARED_ARRAY_BUFFER_UL, ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ OBJECT_VALUE (LIT_MAGIC_STRING_INT8_ARRAY_UL, ECMA_BUILTIN_ID_INT8ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) OBJECT_VALUE (LIT_MAGIC_STRING_UINT8_ARRAY_UL, ECMA_BUILTIN_ID_UINT8ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) OBJECT_VALUE (LIT_MAGIC_STRING_INT16_ARRAY_UL, ECMA_BUILTIN_ID_INT16ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) OBJECT_VALUE (LIT_MAGIC_STRING_UINT16_ARRAY_UL, ECMA_BUILTIN_ID_UINT16ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) OBJECT_VALUE (LIT_MAGIC_STRING_INT32_ARRAY_UL, ECMA_BUILTIN_ID_INT32ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) OBJECT_VALUE (LIT_MAGIC_STRING_UINT32_ARRAY_UL, ECMA_BUILTIN_ID_UINT32ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) OBJECT_VALUE (LIT_MAGIC_STRING_FLOAT32_ARRAY_UL, ECMA_BUILTIN_ID_FLOAT32ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #if JERRY_NUMBER_TYPE_FLOAT64 OBJECT_VALUE (LIT_MAGIC_STRING_FLOAT64_ARRAY_UL, ECMA_BUILTIN_ID_FLOAT64ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT OBJECT_VALUE (LIT_MAGIC_STRING_BIGINT64_ARRAY_UL, ECMA_BUILTIN_ID_BIGINT64ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) OBJECT_VALUE (LIT_MAGIC_STRING_BIGUINT64_ARRAY_UL, ECMA_BUILTIN_ID_BIGUINT64ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_BIGINT */ OBJECT_VALUE (LIT_MAGIC_STRING_UINT8_CLAMPED_ARRAY_UL, ECMA_BUILTIN_ID_UINT8CLAMPEDARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_CONTAINER /* ECMA-262 v6, 23.1.1.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_WEAKSET_UL, ECMA_BUILTIN_ID_WEAKSET, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.1.1.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_MAP_UL, ECMA_BUILTIN_ID_MAP, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.1.1.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_SET_UL, ECMA_BUILTIN_ID_SET, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 23.1.1.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_WEAKMAP_UL, ECMA_BUILTIN_ID_WEAKMAP, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_WEAKREF OBJECT_VALUE (LIT_MAGIC_STRING_WEAKREF_UL, ECMA_BUILTIN_ID_WEAKREF, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_WEAKREF */ #if JERRY_BUILTIN_ERRORS OBJECT_VALUE (LIT_MAGIC_STRING_AGGREGATE_ERROR_UL, ECMA_BUILTIN_ID_AGGREGATE_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ERRORS */ OBJECT_VALUE (LIT_MAGIC_STRING_PROMISE_UL, ECMA_BUILTIN_ID_PROMISE, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 19.4.1.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_SYMBOL_UL, ECMA_BUILTIN_ID_SYMBOL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #if JERRY_BUILTIN_GLOBAL_THIS /* ECMA-262 v11, 18.1.1 */ SIMPLE_VALUE (LIT_MAGIC_STRING_GLOBAL_THIS_UL, ECMA_VALUE_GLOBAL_THIS, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_GLOBAL_THIS */ #if JERRY_BUILTIN_DATAVIEW /* ECMA-262 v6, 23.1.1.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_DATAVIEW_UL, ECMA_BUILTIN_ID_DATAVIEW, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_PROXY /* ECMA-262 v6, 26.2.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROXY_UL, ECMA_BUILTIN_ID_PROXY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_PROXY */ #if JERRY_BUILTIN_BIGINT /* ECMA-262 v11, 20.2.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_BIGINT_UL, ECMA_BUILTIN_ID_BIGINT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_BIGINT */ /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_EVAL, ECMA_GLOBAL_EVAL, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_NAN, ECMA_GLOBAL_IS_NAN, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_FINITE, ECMA_GLOBAL_IS_FINITE, 1, 1) ROUTINE (LIT_MAGIC_STRING_DECODE_URI, ECMA_GLOBAL_DECODE_URI, 1, 1) ROUTINE (LIT_MAGIC_STRING_DECODE_URI_COMPONENT, ECMA_GLOBAL_DECODE_URI_COMPONENT, 1, 1) ROUTINE (LIT_MAGIC_STRING_ENCODE_URI, ECMA_GLOBAL_ENCODE_URI, 1, 1) ROUTINE (LIT_MAGIC_STRING_ENCODE_URI_COMPONENT, ECMA_GLOBAL_ENCODE_URI_COMPONENT, 1, 1) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_PARSE_FLOAT, LIT_MAGIC_STRING_PARSE_FLOAT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_PARSE_INT, LIT_MAGIC_STRING_PARSE_INT, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #if JERRY_BUILTIN_ANNEXB ROUTINE (LIT_MAGIC_STRING_ESCAPE, ECMA_GLOBAL_ESCAPE, 1, 1) ROUTINE (LIT_MAGIC_STRING_UNESCAPE, ECMA_GLOBAL_UNESCAPE, 1, 1) #endif /* JERRY_BUILTIN_ANNEXB */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-atomics-object.cpp000664 001750 001750 00000015051 15164251010 046723 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-atomics-object.h" #include "ecma-arraybuffer-object.h" #include "ecma-bigint.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-shared-arraybuffer-object.h" #include "ecma-typedarray-object.h" #include "jcontext.h" #include "jmem.h" #if JERRY_BUILTIN_ATOMICS /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaatomicsobject ECMA builtin Atomics helper functions * @{ */ /** * Atomics validate Shared integer typedArray * * See also: ES11 24.4.1.1 * * @return ecma value */ ecma_value_t ecma_validate_shared_integer_typedarray (ecma_value_t typedarray, /**< typedArray argument */ bool waitable) /**< waitable argument */ { /* 2. */ if (!ecma_is_typedarray (typedarray)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_TYPED_ARRAY); } /* 3-4. */ ecma_object_t *typedarray_p = ecma_get_object_from_value (typedarray); ecma_typedarray_info_t target_info = ecma_typedarray_get_info (typedarray_p); /* 5-6. */ if (waitable) { if (!(target_info.id == ECMA_BIGINT64_ARRAY || target_info.id == ECMA_INT32_ARRAY)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_NOT_SUPPORTED); } } else { if (target_info.id == ECMA_UINT8_CLAMPED_ARRAY || target_info.id == ECMA_FLOAT32_ARRAY || target_info.id == ECMA_FLOAT64_ARRAY) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_NOT_SUPPORTED); } } /* 7. */ JERRY_ASSERT (target_info.array_buffer_p != NULL); /* 8-10. */ ecma_object_t *buffer = ecma_typedarray_get_arraybuffer (typedarray_p); if (!ecma_object_class_is (buffer, ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_NOT_SHARED_ARRAY_BUFFER); } return ecma_make_object_value (buffer); } /* ecma_validate_shared_integer_typedarray */ /** * Atomics validate Atomic Access * * See also: ES11 24.4.1.2 * * @return ecma value */ ecma_value_t ecma_validate_atomic_access (ecma_value_t typedarray, /**< typedArray argument */ ecma_value_t request_index) /**< request_index argument */ { /* 1. */ JERRY_ASSERT (ecma_is_value_object (typedarray) && ecma_typedarray_get_arraybuffer (ecma_get_object_from_value (typedarray)) != NULL); ecma_object_t *typedarray_p = ecma_get_object_from_value (typedarray); /* 2. */ ecma_number_t access_index; if (ECMA_IS_VALUE_ERROR (ecma_op_to_index (request_index, &access_index))) { return ECMA_VALUE_ERROR; } /* 3. */ ecma_typedarray_info_t target_info = ecma_typedarray_get_info (typedarray_p); /* 4. */ JERRY_ASSERT (access_index >= 0); /* 5-6. */ if (JERRY_UNLIKELY (access_index >= target_info.length)) { return ecma_raise_range_error (ECMA_ERR_INVALID_LENGTH); } return ecma_make_number_value (access_index); } /* ecma_validate_atomic_access */ /** * Atomics read, modify, write * * See also: ES11 24.4.1.11 * * @return ecma value */ ecma_value_t ecma_atomic_read_modify_write (ecma_value_t typedarray, /**< typedArray argument */ ecma_value_t index, /**< index argument */ ecma_value_t value, /**< value argument */ ecma_atomics_op_t op) /**< operation argument */ { /* 1. */ ecma_value_t buffer = ecma_validate_shared_integer_typedarray (typedarray, false); if (ECMA_IS_VALUE_ERROR (buffer)) { return buffer; } /* 2. */ ecma_value_t idx = ecma_validate_atomic_access (typedarray, index); if (ECMA_IS_VALUE_ERROR (idx)) { return idx; } /* 3. */ ecma_object_t *typedarray_p = ecma_get_object_from_value (typedarray); ecma_typedarray_info_t target_info = ecma_typedarray_get_info (typedarray_p); /* 4-5. */ ecma_value_t val = ECMA_VALUE_ERROR; ecma_number_t tmp; if (target_info.id == ECMA_BIGINT64_ARRAY || target_info.id == ECMA_BIGUINT64_ARRAY) { val = ecma_bigint_to_bigint (value, true); } else if (!ECMA_IS_VALUE_ERROR (ecma_op_to_integer (value, &tmp))) { val = ecma_make_number_value (tmp); } if (ECMA_IS_VALUE_ERROR (val)) { return val; } /* 6. */ uint8_t element_size = target_info.element_size; /* 7. */ ecma_typedarray_type_t element_type = target_info.id; /* 8. */ uint32_t offset = target_info.offset; /* 9. */ uint32_t indexed_position = ecma_number_to_uint32 (idx) * element_size + offset; ecma_free_value (idx); JERRY_UNUSED (indexed_position); JERRY_UNUSED (element_type); JERRY_UNUSED (val); JERRY_UNUSED (buffer); JERRY_UNUSED (op); ecma_free_value (val); /* 10. */ return ecma_make_uint32_value (0); } /* ecma_atomic_read_modify_write */ /** * Atomics load * * See also: ES11 24.4.1.12 * * @return ecma value */ ecma_value_t ecma_atomic_load (ecma_value_t typedarray, /**< typedArray argument */ ecma_value_t index) /**< index argument */ { ecma_value_t buffer = ecma_validate_shared_integer_typedarray (typedarray, false); if (ECMA_IS_VALUE_ERROR (buffer)) { return buffer; } /* 2. */ ecma_value_t idx = ecma_validate_atomic_access (typedarray, index); if (ECMA_IS_VALUE_ERROR (idx)) { return idx; } /* 3. */ ecma_object_t *typedarray_p = ecma_get_object_from_value (typedarray); ecma_typedarray_info_t target_info = ecma_typedarray_get_info (typedarray_p); /* 4. */ uint8_t element_size = target_info.element_size; /* 5. */ ecma_typedarray_type_t element_type = target_info.id; /* 6. */ uint32_t offset = target_info.offset; /* 7. */ uint32_t indexed_position = ecma_number_to_uint32 (idx) * element_size + offset; JERRY_UNUSED (indexed_position); JERRY_UNUSED (element_type); JERRY_UNUSED (buffer); /* 8. */ return ecma_make_uint32_value (0); } /* ecma_atomic_load */ /** * @} * @} */ #endif /* JERRY_BUILTIN_ATOMICS */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-aggregateerror.cpp000664 001750 001750 00000004222 15164251010 051374 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-aggregateerror.inc.h" #define BUILTIN_UNDERSCORED_ID aggregate_error #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup aggregateerror ECMA AggregateError object built-in * @{ */ /** * Handle calling [[Call]] of built-in AggregateError object * * @return ecma value */ ecma_value_t ecma_builtin_aggregate_error_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_value_t message_val = ECMA_VALUE_UNDEFINED; ecma_value_t error_val = ECMA_VALUE_UNDEFINED; if (arguments_list_len > 0) { error_val = arguments_list_p[0]; if (arguments_list_len > 1) { message_val = arguments_list_p[1]; } } return ecma_new_aggregate_error (error_val, message_val); } /* ecma_builtin_aggregate_error_dispatch_call */ /** * @} * @} * @} */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-referenceerror.cpp000664 001750 001750 00000005537 15164251010 051416 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-referenceerror.inc.h" #define BUILTIN_UNDERSCORED_ID reference_error #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup referenceerror ECMA ReferenceError object built-in * @{ */ /** * Handle calling [[Call]] of built-in ReferenceError object * * @return ecma value */ ecma_value_t ecma_builtin_reference_error_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_helper_error_dispatch_call (JERRY_ERROR_REFERENCE, arguments_list_p, arguments_list_len); } /* ecma_builtin_reference_error_dispatch_call */ /** * Handle calling [[Construct]] of built-in ReferenceError object * * @return ecma value */ ecma_value_t ecma_builtin_reference_error_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_REFERENCE_ERROR_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_reference_error_dispatch_call (arguments_list_p, arguments_list_len); if (!ECMA_IS_VALUE_ERROR (result)) { ecma_object_t *object_p = ecma_get_object_from_value (result); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, proto_p); } ecma_deref_object (proto_p); return result; } /* ecma_builtin_reference_error_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ERRORS */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgIteratorAccessor.h000664 001750 001750 00000002676 15164251010 035714 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_ITERATOR_ACCESSOR_H_ #define _TVG_ITERATOR_ACCESSOR_H_ #include "tvgPaint.h" namespace tvg { class IteratorAccessor { public: //Utility Method: Iterator Accessor static Iterator* iterator(const Paint* paint) { return PAINT(paint)->iterator(); } }; } #endif //_TVG_ITERATOR_ACCESSOR_H_ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers.h000664 001750 001750 00000067663 15164251010 043676 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_HELPERS_H #define ECMA_HELPERS_H #include "ecma-globals.h" #include "jmem.h" #include "lit-strings.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ /** * Get value of pointer from specified non-null compressed pointer. */ #define ECMA_GET_NON_NULL_POINTER(type, field) JMEM_CP_GET_NON_NULL_POINTER (type, field) /** * Extract value of pointer from specified pointer-tag value */ #define ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG(type, field) \ JMEM_CP_GET_NON_NULL_POINTER_FROM_POINTER_TAG (type, field) /** * Get value of pointer from specified compressed pointer. */ #define ECMA_GET_POINTER(type, field) JMEM_CP_GET_POINTER (type, field) /** * Set value of non-null compressed pointer so that it will correspond * to specified non_compressed_pointer. */ #define ECMA_SET_NON_NULL_POINTER(field, non_compressed_pointer) \ JMEM_CP_SET_NON_NULL_POINTER (field, non_compressed_pointer) /** * Set value of pointer-tag value so that it will correspond * to specified non_compressed_pointer along with tag */ #define ECMA_SET_NON_NULL_POINTER_TAG(field, non_compressed_pointer, tag) \ JMEM_CP_SET_NON_NULL_POINTER_TAG (field, non_compressed_pointer, tag) /** * Set value of compressed pointer so that it will correspond * to specified non_compressed_pointer. */ #define ECMA_SET_POINTER(field, non_compressed_pointer) JMEM_CP_SET_POINTER (field, non_compressed_pointer) /** * Get value of each tag bit from specified pointer-tag value */ #define ECMA_GET_FIRST_BIT_FROM_POINTER_TAG(field) \ JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (field) /**< get first tag bit from jmem_cpointer_tag_t **/ #define ECMA_GET_SECOND_BIT_FROM_POINTER_TAG(field) \ JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (field) /**< get second tag bit from jmem_cpointer_tag_t **/ #define ECMA_GET_THIRD_BIT_FROM_POINTER_TAG(field) \ JMEM_CP_GET_THIRD_BIT_FROM_POINTER_TAG (field) /**< get third tag bit from jmem_cpointer_tag_t **/ /** * Set value of each tag bit to specified pointer-tag value */ #define ECMA_SET_FIRST_BIT_TO_POINTER_TAG(field) \ JMEM_CP_SET_FIRST_BIT_TO_POINTER_TAG (field) /**< set first tag bit to jmem_cpointer_tag_t **/ #define ECMA_SET_SECOND_BIT_TO_POINTER_TAG(field) \ JMEM_CP_SET_SECOND_BIT_TO_POINTER_TAG (field) /**< set second tag bit to jmem_cpointer_tag_t **/ #define ECMA_SET_THIRD_BIT_TO_POINTER_TAG(field) \ JMEM_CP_SET_THIRD_BIT_TO_POINTER_TAG (field) /**< set third tag bit to jmem_cpointer_tag_t **/ /** * Status flags for ecma_string_get_chars function */ typedef enum { ECMA_STRING_FLAG_EMPTY = 0, /**< No options are provided. */ ECMA_STRING_FLAG_IS_ASCII = (1 << 0), /**< The string contains only ASCII characters. */ ECMA_STRING_FLAG_REHASH_NEEDED = (1 << 1), /**< The hash of the string must be recalculated. * For more details see ecma_append_chars_to_string */ ECMA_STRING_FLAG_IS_UINT32 = (1 << 2), /**< The string represents an UINT32 number */ ECMA_STRING_FLAG_MUST_BE_FREED = (1 << 3), /**< The returned buffer must be freed */ } ecma_string_flag_t; /** * Underscore is ignored when this option is passed. */ #define ECMA_CONVERSION_ALLOW_UNDERSCORE 0x1 /** * Convert ecma-string's contents to a cesu-8 string and put it into a buffer. */ #define ECMA_STRING_TO_UTF8_STRING(ecma_str_ptr, /**< ecma string pointer */ \ utf8_ptr, /**< [out] output buffer pointer */ \ utf8_str_size) /**< [out] output buffer size */ \ lit_utf8_size_t utf8_str_size; \ uint8_t utf8_ptr##flags = ECMA_STRING_FLAG_EMPTY; \ const lit_utf8_byte_t *utf8_ptr = ecma_string_get_chars (ecma_str_ptr, &utf8_str_size, NULL, NULL, &utf8_ptr##flags); /** * Free the cesu-8 string buffer allocated by 'ECMA_STRING_TO_UTF8_STRING' */ #define ECMA_FINALIZE_UTF8_STRING(utf8_ptr, /**< pointer to character buffer */ \ utf8_str_size) /**< buffer size */ \ if (utf8_ptr##flags & ECMA_STRING_FLAG_MUST_BE_FREED) \ { \ JERRY_ASSERT (utf8_ptr != NULL); \ jmem_heap_free_block ((void *) utf8_ptr, utf8_str_size); \ } #ifdef ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY /** * Set an internal property value from pointer. */ #define ECMA_SET_INTERNAL_VALUE_POINTER(field, pointer) ((field) = ((ecma_value_t) pointer)) /** * Set an internal property value from pointer. Pointer can be NULL. */ #define ECMA_SET_INTERNAL_VALUE_ANY_POINTER(field, pointer) ((field) = ((ecma_value_t) pointer)) /** * Convert an internal property value to pointer. */ #define ECMA_GET_INTERNAL_VALUE_POINTER(type, field) ((type *) field) /** * Convert an internal property value to pointer. Result can be NULL. */ #define ECMA_GET_INTERNAL_VALUE_ANY_POINTER(type, field) ((type *) field) /** * Checks whether an internal property is NULL. */ #define ECMA_IS_INTERNAL_VALUE_NULL(field) ((field) == ((ecma_value_t) NULL)) #else /* !ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ /** * Set an internal property value from pointer. */ #define ECMA_SET_INTERNAL_VALUE_POINTER(field, pointer) ECMA_SET_NON_NULL_POINTER (field, pointer) /** * Set an internal property value from pointer. Pointer can be NULL. */ #define ECMA_SET_INTERNAL_VALUE_ANY_POINTER(field, pointer) ECMA_SET_POINTER (field, pointer) /** * Convert an internal property value to pointer. */ #define ECMA_GET_INTERNAL_VALUE_POINTER(type, field) ECMA_GET_NON_NULL_POINTER (type, field) /** * Convert an internal property value to pointer. Result can be NULL. */ #define ECMA_GET_INTERNAL_VALUE_ANY_POINTER(type, field) ECMA_GET_POINTER (type, field) /** * Checks whether an internal property is NULL. */ #define ECMA_IS_INTERNAL_VALUE_NULL(field) ((field) == ((ecma_value_t) JMEM_CP_NULL)) #endif /* ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ /** * Convert boolean to bitfield value. */ #define ECMA_BOOL_TO_BITFIELD(x) ((x) ? 1 : 0) /** * Check whether the given type is ECMA_OBJECT_TYPE_PROXY * * @param type object type */ #define ECMA_OBJECT_TYPE_IS_PROXY(type) (JERRY_UNLIKELY ((type) == ECMA_OBJECT_TYPE_PROXY)) /** * Check whether the given object has [[ProxyHandler]] and [[ProxyTarger]] internal slots * * @param obj_p ecma-object */ #if JERRY_BUILTIN_PROXY #define ECMA_OBJECT_IS_PROXY(obj_p) (ECMA_OBJECT_TYPE_IS_PROXY (ecma_get_object_type ((obj_p)))) #else /* !JERRY_BUILTIN_PROXY */ #define ECMA_OBJECT_IS_PROXY(obj_p) (false) #endif /* JERRY_BUILTIN_PROXY */ /* ecma-helpers-value.c */ ecma_type_t JERRY_ATTR_CONST ecma_get_value_type_field (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_direct (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_simple (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_empty (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_undefined (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_null (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_boolean (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_true (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_false (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_found (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_array_hole (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_integer_number (ecma_value_t value); bool JERRY_ATTR_CONST ecma_are_values_integer_numbers (ecma_value_t first_value, ecma_value_t second_value); bool JERRY_ATTR_CONST ecma_is_value_float_number (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_number (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_string (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_symbol (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_magic_string (ecma_value_t value, lit_magic_string_id_t id); bool JERRY_ATTR_CONST ecma_is_value_bigint (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_prop_name (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_direct_string (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_non_direct_string (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_object (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_exception (ecma_value_t value); ecma_value_t ecma_is_value_array (ecma_value_t arg); void ecma_check_value_type_is_spec_defined (ecma_value_t value); ecma_value_t JERRY_ATTR_CONST ecma_make_boolean_value (bool boolean_value); ecma_value_t JERRY_ATTR_CONST ecma_make_integer_value (ecma_integer_value_t integer_value); ecma_value_t ecma_make_nan_value (void); ecma_value_t ecma_make_float_value (ecma_number_t *ecma_num_p); ecma_value_t ecma_make_length_value (ecma_length_t length); ecma_value_t ecma_make_number_value (ecma_number_t ecma_number); ecma_value_t ecma_make_int32_value (int32_t int32_number); ecma_value_t ecma_make_uint32_value (uint32_t uint32_number); ecma_value_t JERRY_ATTR_PURE ecma_make_string_value (const ecma_string_t *ecma_string_p); ecma_value_t JERRY_ATTR_PURE ecma_make_symbol_value (const ecma_string_t *ecma_symbol_p); ecma_value_t JERRY_ATTR_PURE ecma_make_prop_name_value (const ecma_string_t *ecma_prop_name_p); ecma_value_t JERRY_ATTR_PURE ecma_make_magic_string_value (lit_magic_string_id_t id); ecma_value_t JERRY_ATTR_PURE ecma_make_object_value (const ecma_object_t *object_p); ecma_value_t JERRY_ATTR_PURE ecma_make_extended_primitive_value (const ecma_extended_primitive_t *primitive_p, uint32_t type); ecma_integer_value_t JERRY_ATTR_CONST ecma_get_integer_from_value (ecma_value_t value); ecma_number_t JERRY_ATTR_PURE ecma_get_float_from_value (ecma_value_t value); ecma_number_t *ecma_get_pointer_from_float_value (ecma_value_t value); ecma_number_t JERRY_ATTR_PURE ecma_get_number_from_value (ecma_value_t value); ecma_string_t JERRY_ATTR_PURE *ecma_get_string_from_value (ecma_value_t value); ecma_string_t JERRY_ATTR_PURE *ecma_get_symbol_from_value (ecma_value_t value); ecma_string_t JERRY_ATTR_PURE *ecma_get_prop_name_from_value (ecma_value_t value); ecma_object_t JERRY_ATTR_PURE *ecma_get_object_from_value (ecma_value_t value); ecma_extended_primitive_t JERRY_ATTR_PURE *ecma_get_extended_primitive_from_value (ecma_value_t value); ecma_value_t JERRY_ATTR_CONST ecma_invert_boolean_value (ecma_value_t value); ecma_value_t ecma_copy_value (ecma_value_t value); ecma_value_t ecma_fast_copy_value (ecma_value_t value); ecma_value_t ecma_copy_value_if_not_object (ecma_value_t value); void ecma_ref_if_object (ecma_value_t value); void ecma_deref_if_object (ecma_value_t value); ecma_value_t ecma_update_float_number (ecma_value_t float_value, ecma_number_t new_number); void ecma_value_assign_value (ecma_value_t *value_p, ecma_value_t ecma_value); void ecma_value_assign_number (ecma_value_t *value_p, ecma_number_t ecma_number); void ecma_free_value (ecma_value_t value); void ecma_fast_free_value (ecma_value_t value); void ecma_free_value_if_not_object (ecma_value_t value); void ecma_free_object (ecma_value_t value); void ecma_free_number (ecma_value_t value); lit_magic_string_id_t ecma_get_typeof_lit_id (ecma_value_t value); /* ecma-helpers-string.c */ ecma_string_t *ecma_new_symbol_from_descriptor_string (ecma_value_t string_desc); bool ecma_prop_name_is_symbol (ecma_string_t *string_p); ecma_length_t ecma_op_advance_string_index (ecma_string_t *str_p, ecma_length_t index_num, bool is_unicode); #if JERRY_BUILTIN_CONTAINER ecma_string_t *ecma_new_map_key_string (ecma_value_t value); bool ecma_prop_name_is_map_key (ecma_string_t *string_p); #endif /* JERRY_BUILTIN_CONTAINER */ ecma_string_t *ecma_new_ecma_string_from_ascii (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size); ecma_string_t *ecma_new_ecma_string_from_utf8 (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size); ecma_string_t *ecma_new_ecma_string_from_utf8_converted_to_cesu8 (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size); ecma_string_t * ecma_new_ecma_external_string_from_cesu8 (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size, void *user_p); ecma_string_t *ecma_new_ecma_string_from_code_unit (ecma_char_t code_unit); ecma_string_t *ecma_new_ecma_string_from_code_units (ecma_char_t first_code_unit, ecma_char_t second_code_unit); ecma_string_t *ecma_new_ecma_string_from_length (ecma_length_t index); ecma_string_t *ecma_new_ecma_string_from_uint32 (uint32_t uint32_number); ecma_string_t *ecma_new_non_direct_string_from_uint32 (uint32_t uint32_number); ecma_string_t *ecma_get_ecma_string_from_uint32 (uint32_t uint32_number); ecma_string_t *ecma_new_ecma_string_from_number (ecma_number_t num); ecma_string_t *ecma_get_magic_string (lit_magic_string_id_t id); ecma_string_t *ecma_get_internal_string (lit_magic_string_id_t id); ecma_string_t *ecma_append_chars_to_string (ecma_string_t *string1_p, const lit_utf8_byte_t *cesu8_string2_p, lit_utf8_size_t cesu8_string2_size, lit_utf8_size_t cesu8_string2_length); ecma_string_t *ecma_concat_ecma_strings (ecma_string_t *string1_p, ecma_string_t *string2_p); void ecma_ref_ecma_string (ecma_string_t *string_p); void ecma_ref_ecma_string_non_direct (ecma_string_t *string_p); void ecma_deref_ecma_string (ecma_string_t *string_p); void ecma_deref_ecma_string_non_direct (ecma_string_t *string_p); void ecma_destroy_ecma_string (ecma_string_t *string_p); ecma_number_t ecma_string_to_number (const ecma_string_t *str_p); uint32_t ecma_string_get_array_index (const ecma_string_t *str_p); lit_utf8_size_t ecma_string_copy_to_buffer (const ecma_string_t *string_desc_p, lit_utf8_byte_t *buffer_p, lit_utf8_size_t buffer_size, jerry_encoding_t encoding); void ecma_string_to_cesu8_bytes (const ecma_string_t *string_desc_p, lit_utf8_byte_t *buffer_p, lit_utf8_size_t buffer_size); const lit_utf8_byte_t *ecma_string_get_chars (const ecma_string_t *string_p, lit_utf8_size_t *size_p, lit_utf8_size_t *length_p, lit_utf8_byte_t *uint32_buff_p, uint8_t *flags_p); bool ecma_compare_ecma_string_to_magic_id (const ecma_string_t *string_p, lit_magic_string_id_t id); bool ecma_string_is_empty (const ecma_string_t *string_p); bool ecma_string_is_length (const ecma_string_t *string_p); jmem_cpointer_t ecma_string_to_property_name (ecma_string_t *prop_name_p, ecma_property_t *name_type_p); ecma_string_t *ecma_string_from_property_name (ecma_property_t property, jmem_cpointer_t prop_name_cp); lit_string_hash_t ecma_string_get_property_name_hash (ecma_property_t property, jmem_cpointer_t prop_name_cp); uint32_t ecma_string_get_property_index (ecma_property_t property, jmem_cpointer_t prop_name_cp); bool ecma_string_compare_to_property_name (ecma_property_t property, jmem_cpointer_t prop_name_cp, const ecma_string_t *string_p); bool ecma_compare_ecma_strings (const ecma_string_t *string1_p, const ecma_string_t *string2_p); bool ecma_compare_ecma_non_direct_strings (const ecma_string_t *string1_p, const ecma_string_t *string2_p); bool ecma_compare_ecma_strings_relational (const ecma_string_t *string1_p, const ecma_string_t *string2_p); lit_utf8_size_t ecma_string_get_length (const ecma_string_t *string_p); lit_utf8_size_t ecma_string_get_utf8_length (const ecma_string_t *string_p); lit_utf8_size_t ecma_string_get_size (const ecma_string_t *string_p); lit_utf8_size_t ecma_string_get_utf8_size (const ecma_string_t *string_p); ecma_char_t ecma_string_get_char_at_pos (const ecma_string_t *string_p, lit_utf8_size_t index); lit_magic_string_id_t ecma_get_string_magic (const ecma_string_t *string_p); lit_string_hash_t ecma_string_hash (const ecma_string_t *string_p); ecma_string_t *ecma_string_substr (const ecma_string_t *string_p, lit_utf8_size_t start_pos, lit_utf8_size_t end_pos); const lit_utf8_byte_t *ecma_string_trim_front (const lit_utf8_byte_t *start_p, const lit_utf8_byte_t *end_p); const lit_utf8_byte_t *ecma_string_trim_back (const lit_utf8_byte_t *start_p, const lit_utf8_byte_t *end_p); void ecma_string_trim_helper (const lit_utf8_byte_t **utf8_str_p, lit_utf8_size_t *utf8_str_size); ecma_string_t *ecma_string_trim (const ecma_string_t *string_p); ecma_value_t ecma_string_pad (ecma_value_t original_string_p, ecma_value_t max_length, ecma_value_t fill_string, bool pad_on_start); ecma_stringbuilder_t ecma_stringbuilder_create (void); ecma_stringbuilder_t ecma_stringbuilder_create_from (ecma_string_t *string_p); ecma_stringbuilder_t ecma_stringbuilder_create_raw (const lit_utf8_byte_t *data_p, const lit_utf8_size_t data_size); lit_utf8_size_t ecma_stringbuilder_get_size (ecma_stringbuilder_t *builder_p); lit_utf8_byte_t *ecma_stringbuilder_get_data (ecma_stringbuilder_t *builder_p); void ecma_stringbuilder_revert (ecma_stringbuilder_t *builder_p, const lit_utf8_size_t size); void ecma_stringbuilder_append (ecma_stringbuilder_t *builder_p, const ecma_string_t *string_p); void ecma_stringbuilder_append_magic (ecma_stringbuilder_t *builder_p, const lit_magic_string_id_t id); void ecma_stringbuilder_append_raw (ecma_stringbuilder_t *builder_p, const lit_utf8_byte_t *data_p, const lit_utf8_size_t data_size); void ecma_stringbuilder_append_codepoint (ecma_stringbuilder_t *builder_p, lit_code_point_t cp); void ecma_stringbuilder_append_char (ecma_stringbuilder_t *builder_p, const ecma_char_t c); void ecma_stringbuilder_append_byte (ecma_stringbuilder_t *builder_p, const lit_utf8_byte_t); ecma_string_t *ecma_stringbuilder_finalize (ecma_stringbuilder_t *builder_p); void ecma_stringbuilder_destroy (ecma_stringbuilder_t *builder_p); /* ecma-helpers-number.c */ ecma_number_t ecma_number_make_nan (void); ecma_number_t ecma_number_make_infinity (bool sign); bool ecma_number_is_nan (ecma_number_t num); bool ecma_number_is_negative (ecma_number_t num); bool ecma_number_is_zero (ecma_number_t num); bool ecma_number_is_infinity (ecma_number_t num); bool ecma_number_is_finite (ecma_number_t num); ecma_number_t ecma_number_get_prev (ecma_number_t num); ecma_number_t ecma_number_get_next (ecma_number_t num); ecma_number_t ecma_number_trunc (ecma_number_t num); ecma_number_t ecma_number_remainder (ecma_number_t left_num, ecma_number_t right_num); ecma_number_t ecma_number_pow (ecma_number_t x, ecma_number_t y); ecma_value_t ecma_number_parse_int (const lit_utf8_byte_t *string_buff, lit_utf8_size_t string_buff_size, ecma_value_t radix); ecma_value_t ecma_number_parse_float (const lit_utf8_byte_t *string_buff, lit_utf8_size_t string_buff_size); ecma_value_t ecma_integer_multiply (ecma_integer_value_t left_integer, ecma_integer_value_t right_integer); lit_utf8_size_t ecma_number_to_decimal (ecma_number_t num, lit_utf8_byte_t *out_digits_p, int32_t *out_decimal_exp_p); /* ecma-helpers-collection.c */ ecma_collection_t *ecma_new_collection (void); void ecma_collection_push_back (ecma_collection_t *collection_p, ecma_value_t value); void ecma_collection_reserve (ecma_collection_t *collection_p, uint32_t count); void ecma_collection_append (ecma_collection_t *collection_p, const ecma_value_t *buffer_p, uint32_t count); void ecma_collection_destroy (ecma_collection_t *collection_p); void ecma_collection_free (ecma_collection_t *collection_p); void ecma_collection_free_if_not_object (ecma_collection_t *collection_p); void ecma_collection_free_objects (ecma_collection_t *collection_p); void ecma_collection_free_template_literal (ecma_collection_t *collection_p); bool ecma_collection_check_duplicated_entries (ecma_collection_t *collection_p); bool ecma_collection_has_string_value (ecma_collection_t *collection_p, ecma_string_t *string_p); ecma_value_t *ecma_new_compact_collection (void); ecma_value_t *ecma_compact_collection_push_back (ecma_value_t *compact_collection_p, ecma_value_t value); ecma_value_t *ecma_compact_collection_shrink (ecma_value_t *compact_collection_p); void ecma_compact_collection_free (ecma_value_t *compact_collection_p); ecma_value_t *ecma_compact_collection_end (ecma_value_t *compact_collection_p); void ecma_compact_collection_destroy (ecma_value_t *compact_collection_p); /* ecma-helpers.c */ ecma_object_t *ecma_create_object (ecma_object_t *prototype_object_p, size_t ext_object_size, ecma_object_type_t type); ecma_object_t *ecma_create_decl_lex_env (ecma_object_t *outer_lexical_environment_p); ecma_object_t *ecma_create_object_lex_env (ecma_object_t *outer_lexical_environment_p, ecma_object_t *binding_obj_p); ecma_object_t *ecma_create_lex_env_class (ecma_object_t *outer_lexical_environment_p, size_t lexical_env_size); bool JERRY_ATTR_PURE ecma_is_lexical_environment (const ecma_object_t *object_p); void ecma_op_ordinary_object_set_extensible (ecma_object_t *object_p); ecma_object_type_t JERRY_ATTR_PURE ecma_get_object_type (const ecma_object_t *object_p); ecma_object_base_type_t JERRY_ATTR_PURE ecma_get_object_base_type (const ecma_object_t *object_p); bool JERRY_ATTR_PURE ecma_object_class_is (ecma_object_t *object_p, ecma_object_class_type_t class_id); ecma_lexical_environment_type_t JERRY_ATTR_PURE ecma_get_lex_env_type (const ecma_object_t *object_p); ecma_object_t JERRY_ATTR_PURE *ecma_get_lex_env_binding_object (const ecma_object_t *object_p); ecma_object_t *ecma_clone_decl_lexical_environment (ecma_object_t *lex_env_p, bool copy_values); ecma_property_value_t *ecma_create_named_data_property (ecma_object_t *object_p, ecma_string_t *name_p, uint8_t prop_attributes, ecma_property_t **out_prop_p); ecma_property_value_t *ecma_create_named_accessor_property (ecma_object_t *object_p, ecma_string_t *name_p, ecma_object_t *get_p, ecma_object_t *set_p, uint8_t prop_attributes, ecma_property_t **out_prop_p); #if JERRY_MODULE_SYSTEM void ecma_create_named_reference_property (ecma_object_t *object_p, ecma_string_t *name_p, ecma_value_t reference); #endif /* JERRY_MODULE_SYSTEM */ ecma_property_t *ecma_find_named_property (ecma_object_t *obj_p, ecma_string_t *name_p); ecma_property_value_t *ecma_get_named_data_property (ecma_object_t *obj_p, ecma_string_t *name_p); void ecma_delete_property (ecma_object_t *object_p, ecma_property_value_t *prop_value_p); void ecma_named_data_property_assign_value (ecma_object_t *obj_p, ecma_property_value_t *prop_value_p, ecma_value_t value); ecma_getter_setter_pointers_t *ecma_get_named_accessor_property (const ecma_property_value_t *prop_value_p); void ecma_set_named_accessor_property_getter (ecma_object_t *object_p, ecma_property_value_t *prop_value_p, ecma_object_t *getter_p); void ecma_set_named_accessor_property_setter (ecma_object_t *object_p, ecma_property_value_t *prop_value_p, ecma_object_t *setter_p); #if JERRY_MODULE_SYSTEM ecma_value_t ecma_property_to_reference (ecma_property_t *property_p); ecma_property_value_t *ecma_get_property_value_from_named_reference (ecma_property_value_t *reference_p); #endif /* JERRY_MODULE_SYSTEM */ bool ecma_is_property_writable (ecma_property_t property); void ecma_set_property_writable_attr (ecma_property_t *property_p, bool is_writable); bool ecma_is_property_enumerable (ecma_property_t property); void ecma_set_property_enumerable_attr (ecma_property_t *property_p, bool is_enumerable); bool ecma_is_property_configurable (ecma_property_t property); void ecma_set_property_configurable_attr (ecma_property_t *property_p, bool is_configurable); #if JERRY_LCACHE bool ecma_is_property_lcached (ecma_property_t *property_p); void ecma_set_property_lcached (ecma_property_t *property_p, bool is_lcached); #endif /* JERRY_LCACHE */ ecma_property_descriptor_t ecma_make_empty_property_descriptor (void); void ecma_free_property_descriptor (ecma_property_descriptor_t *prop_desc_p); void ecma_ref_extended_primitive (ecma_extended_primitive_t *primitive_p); void ecma_deref_exception (ecma_extended_primitive_t *exception_p); #if JERRY_BUILTIN_BIGINT void ecma_deref_bigint (ecma_extended_primitive_t *bigint_p); #endif /* JERRY_BUILTIN_BIGINT */ ecma_value_t ecma_create_exception (ecma_value_t value, uint32_t options); ecma_value_t ecma_create_exception_from_context (void); ecma_value_t ecma_create_exception_from_object (ecma_object_t *object_p); void ecma_throw_exception (ecma_value_t value); void ecma_script_deref (ecma_value_t script_value); void ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p); void ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p); ecma_value_t ecma_script_get_from_value (ecma_value_t value); ecma_value_t *ecma_compiled_code_resolve_arguments_start (const ecma_compiled_code_t *bytecode_header_p); ecma_value_t *ecma_compiled_code_resolve_function_name (const ecma_compiled_code_t *bytecode_header_p); ecma_collection_t *ecma_compiled_code_get_tagged_template_collection (const ecma_compiled_code_t *bytecode_header_p); ecma_value_t ecma_get_source_name (const ecma_compiled_code_t *bytecode_p); #if (JERRY_STACK_LIMIT != 0) uintptr_t ecma_get_current_stack_usage (void); #endif /* (JERRY_STACK_LIMIT != 0) */ /* ecma-helpers-external-pointers.c */ bool ecma_create_native_pointer_property (ecma_object_t *obj_p, void *native_p, const jerry_object_native_info_t *native_info_p); ecma_native_pointer_t *ecma_get_native_pointer_value (ecma_object_t *obj_p, const jerry_object_native_info_t *native_info_p); bool ecma_delete_native_pointer_property (ecma_object_t *obj_p, const jerry_object_native_info_t *native_info_p); /* ecma-helpers-conversion.c */ ecma_number_t ecma_utf8_string_to_number (const lit_utf8_byte_t *str_p, lit_utf8_size_t str_size, uint32_t option); ecma_number_t ecma_utf8_string_to_number_by_radix (const lit_utf8_byte_t *str_p, lit_utf8_size_t str_size, uint32_t radix, uint32_t option); lit_utf8_size_t ecma_uint32_to_utf8_string (uint32_t value, lit_utf8_byte_t *out_buffer_p, lit_utf8_size_t buffer_size); uint32_t ecma_number_to_uint32 (ecma_number_t num); int32_t ecma_number_to_int32 (ecma_number_t num); lit_utf8_size_t ecma_number_to_utf8_string (ecma_number_t num, lit_utf8_byte_t *buffer_p, lit_utf8_size_t buffer_size); /* ecma-helpers-errol.c */ lit_utf8_size_t ecma_errol0_dtoa (double val, lit_utf8_byte_t *buffer_p, int32_t *exp_p); /** * @} * @} */ #endif /* !ECMA_HELPERS_H */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlRenderPass.cpp000664 001750 001750 00000004273 15164251010 037246 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlCommon.h" #include "tvgGlRenderPass.h" #include "tvgGlRenderTask.h" static Matrix _viewMatrix(const RenderRegion& vp) { Matrix postMatrix = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}; translate(&postMatrix, {(float)-vp.sx(), (float)-vp.sy()}); Matrix mvp = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}; mvp.e11 = 2.f / vp.w(); mvp.e22 = -2.f / vp.h(); mvp.e13 = -1.f; mvp.e23 = 1.f; return mvp * postMatrix; } GlRenderPass::GlRenderPass(GlRenderTarget* fbo): mFbo(fbo), mTasks(), mDrawDepth(0), mViewMatrix(tvg::identity()) { if (mFbo) mViewMatrix = _viewMatrix(mFbo->viewport); } GlRenderPass::GlRenderPass(GlRenderPass&& other): mFbo(other.mFbo), mTasks(), mDrawDepth(0), mViewMatrix(other.mViewMatrix) { mTasks.push(other.mTasks); other.mTasks.clear(); mDrawDepth = other.mDrawDepth; } GlRenderPass::~GlRenderPass() { if (mTasks.empty()) return; ARRAY_FOREACH(p, mTasks) delete(*p); mTasks.clear(); } void GlRenderPass::addRenderTask(GlRenderTask* task) { mTasks.push(task); } loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int16array.cpp000664 001750 001750 00000004344 15164251010 052565 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-int16array.inc.h" #define BUILTIN_UNDERSCORED_ID int16array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup int64array ECMA Int16Array object built-in * @{ */ /** * Handle calling [[Call]] of Int16Array * * @return ecma value */ ecma_value_t ecma_builtin_int16array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_INT16_ARRAY_REQUIRES_NEW); } /* ecma_builtin_int16array_dispatch_call */ /** * Handle calling [[Construct]] of Int16Array * * @return ecma value */ ecma_value_t ecma_builtin_int16array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_INT16_ARRAY); } /* ecma_builtin_int16array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_png/000775 001750 001750 00000000000 15164251010 034123 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test10.lot000664 001750 001750 00000030713 15164251010 034052 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":30,"ip":0,"op":90,"w":400,"h":400,"nm":"Gradient Advanced Tests","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Mask Layer - Alpha Matte","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":1,"k":[{"t":0,"s":[60,60],"e":[120,120]},{"t":45,"s":[120,120],"e":[60,60]},{"t":90,"s":[60,60]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Mask"},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Content with Alpha Matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":10},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[1,0.3,0.3,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Mask Layer - Inverted Matte","tt":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[300,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":1,"pt":{"a":0,"k":5},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"or":{"a":0,"k":[40]},"os":{"a":0,"k":[0]},"ir":{"a":0,"k":[20]},"is":{"a":0,"k":[0]},"nm":"Star Mask"},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Content with Inverted Matte","td":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[300,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[90,90]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.3,1,0.3,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Layer with Tint Effect","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":20,"nm":"Tint","np":6,"mn":"ADBE Tint","ix":1,"en":1,"ef":[{"ty":2,"nm":"Map Black To","mn":"ADBE Tint-0001","ix":1,"v":{"a":1,"k":[{"t":0,"s":[0,0,1,1],"e":[1,0,0,1]},{"t":45,"s":[1,0,0,1],"e":[0,0,1,1]},{"t":90,"s":[0,0,1,1]}]}},{"ty":2,"nm":"Map White To","mn":"ADBE Tint-0002","ix":2,"v":{"a":0,"k":[1,1,1,1]}},{"ty":0,"nm":"Amount to Tint","mn":"ADBE Tint-0003","ix":3,"v":{"a":0,"k":100}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[60,60]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":10},"nm":"Rectangle"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0,0,0,0.5,0.5,0.5,0.5,1,1,1,1]}},"s":{"a":0,"k":[-30,0]},"e":{"a":0,"k":[30,0]},"t":1,"nm":"Gradient Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Layer with Fill Effect","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[220,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":21,"nm":"Fill","np":9,"mn":"ADBE Fill","ix":1,"en":1,"ef":[{"ty":10,"nm":"Fill Mask","mn":"ADBE Fill-0001","ix":1,"v":{"a":0,"k":0}},{"ty":7,"nm":"All Masks","mn":"ADBE Fill-0007","ix":2,"v":{"a":0,"k":0}},{"ty":2,"nm":"Color","mn":"ADBE Fill-0002","ix":3,"v":{"a":1,"k":[{"t":0,"s":[1,1,0,1],"e":[1,0,1,1]},{"t":90,"s":[1,0,1,1]}]}},{"ty":7,"nm":"Invert","mn":"ADBE Fill-0006","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Horizontal Feather","mn":"ADBE Fill-0003","ix":5,"v":{"a":0,"k":0}},{"ty":0,"nm":"Vertical Feather","mn":"ADBE Fill-0004","ix":6,"v":{"a":0,"k":0}},{"ty":0,"nm":"Opacity","mn":"ADBE Fill-0005","ix":7,"v":{"a":0,"k":100}}]}],"shapes":[{"ty":"sr","d":1,"sy":2,"pt":{"a":0,"k":6},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"or":{"a":0,"k":[30]},"os":{"a":0,"k":[0]},"nm":"Polygon"},{"ty":"fl","c":{"a":0,"k":[0.5,0.5,0.5,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Layer with Stroke Effect","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[340,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":22,"nm":"Stroke","np":13,"mn":"ADBE Stroke","ix":1,"en":1,"ef":[{"ty":2,"nm":"Color","mn":"ADBE Stroke-0001","ix":1,"v":{"a":0,"k":[1,0.5,0,1]}},{"ty":7,"nm":"All Masks","mn":"ADBE Stroke-0009","ix":2,"v":{"a":0,"k":1}},{"ty":7,"nm":"Stroke Sequentially","mn":"ADBE Stroke-0010","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Brush Size","mn":"ADBE Stroke-0002","ix":4,"v":{"a":1,"k":[{"t":0,"s":[2],"e":[8]},{"t":45,"s":[8],"e":[2]},{"t":90,"s":[2]}]}},{"ty":0,"nm":"Brush Hardness","mn":"ADBE Stroke-0003","ix":5,"v":{"a":0,"k":100}},{"ty":0,"nm":"Opacity","mn":"ADBE Stroke-0004","ix":6,"v":{"a":0,"k":100}}]}],"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[50,50]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.2,0.8,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Multiply Blend Mode","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"nm":"Circle"},{"ty":"fl","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":80},"r":1,"nm":"Fill","bm":2}],"ip":0,"op":90,"st":0,"bm":2},{"ddd":0,"ind":9,"ty":4,"nm":"Screen Blend Mode","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[120,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"nm":"Circle"},{"ty":"fl","c":{"a":0,"k":[0,1,0,1]},"o":{"a":0,"k":80},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":3},{"ddd":0,"ind":10,"ty":4,"nm":"Overlay Blend Mode","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[110,120,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"nm":"Circle"},{"ty":"fl","c":{"a":0,"k":[0,0,1,1]},"o":{"a":0,"k":80},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":4},{"ddd":1,"ind":11,"ty":4,"nm":"3D Layer with Rotation","sr":1,"ks":{"o":{"a":0,"k":100},"rx":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"ry":{"a":1,"k":[{"t":0,"s":[0],"e":[180]},{"t":90,"s":[180]}]},"rz":{"a":0,"k":0},"or":{"a":0,"k":[0,0,0]},"p":{"a":0,"k":[300,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[60,60]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":5},"nm":"Rectangle"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,1,1,0,1,1,1,1]}},"s":{"a":0,"k":[0,-30]},"e":{"a":0,"k":[0,30]},"t":1,"nm":"Gradient Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Layer with Multiple Masks","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,280,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-40,-40],[40,-40],[40,40],[-40,40]],"c":true}},"o":{"a":1,"k":[{"t":0,"s":[100],"e":[50]},{"t":45,"s":[50],"e":[100]},{"t":90,"s":[100]}]},"x":{"a":1,"k":[{"t":0,"s":[0],"e":[10]},{"t":45,"s":[10],"e":[0]},{"t":90,"s":[0]}]},"nm":"Mask 1"},{"inv":false,"mode":"s","pt":{"a":1,"k":[{"t":0,"s":[{"i":[[0,-11],[11,0],[0,11],[-11,0]],"o":[[0,11],[-11,0],[0,-11],[11,0]],"v":[[20,0],[0,20],[-20,0],[0,-20]],"c":true}],"e":[{"i":[[0,-15],[15,0],[0,15],[-15,0]],"o":[[0,15],[-15,0],[0,-15],[15,0]],"v":[[27,0],[0,27],[-27,0],[0,-27]],"c":true}]},{"t":45,"s":[{"i":[[0,-15],[15,0],[0,15],[-15,0]],"o":[[0,15],[-15,0],[0,-15],[15,0]],"v":[[27,0],[0,27],[-27,0],[0,-27]],"c":true}],"e":[{"i":[[0,-11],[11,0],[0,11],[-11,0]],"o":[[0,11],[-11,0],[0,-11],[11,0]],"v":[[20,0],[0,20],[-20,0],[0,-20]],"c":true}]},{"t":90,"s":[{"i":[[0,-11],[11,0],[0,11],[-11,0]],"o":[[0,11],[-11,0],[0,-11],[11,0]],"v":[[20,0],[0,20],[-20,0],[0,-20]],"c":true}]}]},"o":{"a":0,"k":100},"x":{"a":0,"k":0},"nm":"Mask 2"}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":10},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[1,0.5,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Time Remapping Layer","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"p":{"a":0,"k":[280,280,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"tm":{"a":1,"k":[{"t":0,"s":[0],"e":[60]},{"t":60,"s":[60],"e":[0]},{"t":90,"s":[0]}]},"shapes":[{"ty":"sr","d":1,"sy":1,"pt":{"a":0,"k":6},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":1,"k":[{"t":0,"s":[30],"e":[50]},{"t":45,"s":[50],"e":[30]},{"t":90,"s":[30]}]},"os":{"a":0,"k":[0]},"ir":{"a":0,"k":[15]},"is":{"a":0,"k":[0]},"nm":"Star"},{"ty":"fl","c":{"a":0,"k":[0.8,0.2,0.8,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"TrimPath - Simultaneous Mode","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":1,"pt":{"a":0,"k":8},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[40]},"os":{"a":0,"k":[0]},"ir":{"a":0,"k":[20]},"is":{"a":0,"k":[0]},"nm":"Star"},{"ty":"tm","s":{"a":1,"k":[{"t":0,"s":[0],"e":[50]},{"t":45,"s":[50],"e":[0]},{"t":90,"s":[0]}]},"e":{"a":1,"k":[{"t":0,"s":[100],"e":[50]},{"t":45,"s":[50],"e":[100]},{"t":90,"s":[100]}]},"o":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"m":2,"nm":"TrimPath Simultaneous"},{"ty":"st","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"ml":4,"nm":"Stroke"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Radial Gradient with Zero Stop","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[250,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"el","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":2,"k":{"a":0,"k":[0,1,0.5,0,1,0,0.8,1]}},"s":{"a":0,"k":[0,0]},"e":{"a":0,"k":[40,40]},"t":2,"h":{"a":0,"k":0},"a":{"a":0,"k":0},"nm":"Radial Gradient Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Multiple Strokes with Dashes","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"t":0,"s":[0],"e":[360]},{"t":90,"s":[360]}]},"p":{"a":0,"k":[100,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[70,70]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":10},"nm":"Rectangle"},{"ty":"st","c":{"a":0,"k":[0,1,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":4,"d":[{"n":"d","nm":"dash","v":{"a":1,"k":[{"t":0,"s":[10],"e":[20]},{"t":45,"s":[20],"e":[10]},{"t":90,"s":[10]}]}},{"n":"g","nm":"gap","v":{"a":0,"k":5}},{"n":"o","nm":"offset","v":{"a":1,"k":[{"t":0,"s":[0],"e":[50]},{"t":90,"s":[50]}]}}],"nm":"Stroke with Dashes"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Gradient Stroke with Opacity Zero","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[280,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sr","d":1,"sy":2,"pt":{"a":0,"k":5},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"or":{"a":0,"k":[35]},"os":{"a":0,"k":[0]},"nm":"Pentagon"},{"ty":"gs","o":{"a":1,"k":[{"t":0,"s":[0],"e":[100]},{"t":45,"s":[100],"e":[0]},{"t":90,"s":[0]}]},"w":{"a":0,"k":6},"lc":2,"lj":2,"ml":4,"g":{"p":3,"k":{"a":0,"k":[0,1,0,1,0.5,0.5,0.5,0,1,0,1,0]}},"s":{"a":0,"k":[-35,0]},"e":{"a":0,"k":[35,0]},"t":1,"nm":"Gradient Stroke"}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Path with Multiple TrimPaths","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[200,320,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-50,-30],[50,-30],[50,30],[-50,30]],"c":true}},"nm":"Path"},{"ty":"tm","s":{"a":0,"k":[25]},"e":{"a":0,"k":[75]},"o":{"a":0,"k":[0]},"m":1,"nm":"TrimPath 1"},{"ty":"tm","s":{"a":1,"k":[{"t":0,"s":[0],"e":[50]},{"t":90,"s":[50]}]},"e":{"a":0,"k":[100]},"o":{"a":0,"k":[0]},"m":1,"nm":"TrimPath 2"},{"ty":"st","c":{"a":0,"k":[1,1,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":5},"lc":2,"lj":2,"ml":4,"nm":"Stroke"}],"ip":0,"op":90,"st":0,"bm":0}],"markers":[]}mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/tools/lottie2gif/lottie2gif.cpp000664 001750 001750 00000021476 15164251010 035213 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif #else #include #include #include #include #endif using namespace std; using namespace tvg; struct App { private: char full[PATH_MAX]; //full path uint32_t fps = 30; uint32_t width = 600; uint32_t height = 600; uint8_t r, g, b; //background color bool background = false; void helpMsg() { cout << "Usage: \n tvg-lottie2gif [Lottie file] or [Lottie folder] [-r resolution] [-f fps] [-b background color]\n\nExamples: \n $ tvg-lottie2gif input.json\n $ tvg-lottie2gif input.json -r 600x600\n $ tvg-lottie2gif input.json -f 30\n $ tvg-lottie2gif input.json -r 600x600 -f 30\n $ tvg-lottie2gif lottiefolder\n $ tvg-lottie2gif lottiefolder -r 600x600 -f 30 -b fa7410\n\n"; } bool validate(string& lottieName) { string extn = ".json"; if (lottieName.size() <= extn.size() || lottieName.substr(lottieName.size() - extn.size()) != extn) { cout << "Error: \"" << lottieName << "\" is invalid." << endl; return false; } return true; } bool convert(string& in, string& out) { if (Initializer::init() != Result::Success) return false; { auto animation = Animation::gen(); auto picture = animation->picture(); if (picture->load(in.c_str()) != Result::Success) return false; float width, height; picture->size(&width, &height); float scale = static_cast(this->width) / width; picture->size(width * scale, height * scale); auto saver = unique_ptr(Saver::gen()); //set a background color if (background) { auto bg = Shape::gen(); bg->fill(r, g, b); bg->appendRect(0, 0, width * scale, height * scale); saver->background(bg); } if (saver->save(animation, out.c_str(), 100, fps) != Result::Success) return false; if (saver->sync() != Result::Success) return false; } return Initializer::term() == Result::Success; } void convert(string& lottieName) { //Get gif file auto gifName = lottieName; gifName.replace(gifName.length() - 4, 4, "gif"); if (convert(lottieName, gifName)) { cout << "Generated Gif file : " << gifName << endl; } else { cout << "Failed Converting Gif file : " << lottieName << endl; } } const char* realPath(const char* path) { #ifdef _WIN32 return _fullpath(full, path, PATH_MAX); #else return realpath(path, full); #endif } bool isDirectory(const char* path) { #ifdef _WIN32 DWORD attr = GetFileAttributes(path); if (attr == INVALID_FILE_ATTRIBUTES) return false; return attr & FILE_ATTRIBUTE_DIRECTORY; #else struct stat buf; if (stat(path, &buf) != 0) return false; return S_ISDIR(buf.st_mode); #endif } bool handleDirectory(const string& path) { #ifdef _WIN32 //open directory WIN32_FIND_DATA fd; HANDLE h = FindFirstFileEx((path + "\\*").c_str(), FindExInfoBasic, &fd, FindExSearchNameMatch, NULL, 0); if (h == INVALID_HANDLE_VALUE) { cout << "Couldn't open directory \"" << path.c_str() << "\"." << endl; return false; } //List directories do { if (*fd.cFileName == '.' || *fd.cFileName == '$') continue; //sub directory if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { string subpath = string(path); subpath += '\\'; subpath += fd.cFileName; if (!handleDirectory(subpath)) continue; //file } else { string lottieName(fd.cFileName); if (!validate(lottieName)) continue; lottieName = string(path); lottieName += '\\'; lottieName += fd.cFileName; convert(lottieName); } } while (FindNextFile(h, &fd)); FindClose(h); #else //open directory auto dir = opendir(path.c_str()); if (!dir) { cout << "Couldn't open directory \"" << path.c_str() << "\"." << endl; return false; } //List directories while (auto entry = readdir(dir)) { if (*entry->d_name == '.' || *entry->d_name == '$') continue; //sub directory if (entry->d_type == DT_DIR) { string subpath = string(path); subpath += '/'; subpath += entry->d_name; if (!handleDirectory(subpath)) continue; //file } else { string svgName(entry->d_name); if (!validate(svgName)) continue; svgName = string(path); svgName += '/'; svgName += entry->d_name; convert(svgName); } } #endif return true; } public: int setup(int argc, char** argv) { //Collect input files vector inputs; for (int i = 1; i < argc; ++i) { const char* p = argv[i]; if (*p == '-') { const char* p_arg = (i + 1 < argc) ? argv[++i] : nullptr; //image resolution if (p[1] == 'r') { if (!p_arg) { cout << "Error: Missing resolution attribute. Expected eg. -r 600x600." << endl; return 1; } const char* x = strchr(p_arg, 'x'); if (x) { width = atoi(p_arg); height = atoi(x + 1); } if (!x || width <= 0 || height <= 0) { cout << "Error: Resolution (" << p_arg << ") is corrupted. Expected eg. -r 600x600." << endl; return 1; } //fps } else if (p[1] == 'f') { if (!p_arg) { cout << "Error: Missing fps value. Expected eg. -f 30." << endl; return 1; } fps = atoi(p_arg); } else if (p[1] == 'b') { //background color if (!p_arg) { cout << "Error: Missing background color attribute. Expected eg. -b fa7410." << endl; return 1; } auto bgColor = (uint32_t) strtol(p_arg, NULL, 16); r = (uint8_t)((bgColor & 0xff0000) >> 16); g = (uint8_t)((bgColor & 0x00ff00) >> 8); b = (uint8_t)((bgColor & 0x0000ff)); background = true; } else { cout << "Warning: Unknown flag (" << p << ")." << endl; } }else { inputs.push_back(argv[i]); } } //No Input Lottie if (inputs.empty()) { helpMsg(); return 0; } for (auto input : inputs) { auto path = realPath(input); if (!path) { cout << "Invalid file or path name: \"" << input << "\"" << endl; continue; } if (isDirectory(path)) { //load from directory cout << "Directory: \"" << path << "\"" << endl; if (!handleDirectory(path)) break; } else { string lottieName(input); if (!validate(lottieName)) continue; convert(lottieName); } } return 0; } }; int main(int argc, char **argv) { App app; return app.setup(argc, argv); } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/savers/meson.build000664 001750 001750 00000000255 15164251010 033453 0ustar00ddennedyddennedy000000 000000 subsaver_dep = [] if gif_saver subdir('gif') endif saver_dep = declare_dependency( dependencies: subsaver_dep, include_directories : include_directories('.'), ) mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgCssStyle.h000664 001750 001750 00000003023 15164251010 035355 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2022 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SVG_CSS_STYLE_H_ #define _TVG_SVG_CSS_STYLE_H_ #include "tvgSvgLoaderCommon.h" SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type); SvgNode* cssFindStyleNode(const SvgNode* style, const char* title); void cssUpdateStyle(SvgNode* doc, SvgNode* style); void cssCopyStyleAttr(SvgNode* to, const SvgNode* from, bool overwrite = false); #endif //_TVG_SVG_CSS_STYLE_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgSaver.cpp000664 001750 001750 00000011505 15164251010 034121 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgCommon.h" #include "tvgStr.h" #include "tvgSaveModule.h" #ifdef THORVG_GIF_SAVER_SUPPORT #include "tvgGifSaver.h" #endif /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ struct Saver::Impl { SaveModule* saveModule = nullptr; Paint* bg = nullptr; ~Impl() { delete(saveModule); if (bg) bg->unref(); } }; static SaveModule* _find(FileType type) { switch(type) { case FileType::Gif: { #ifdef THORVG_GIF_SAVER_SUPPORT return new GifSaver; #endif break; } default: { break; } } #ifdef THORVG_LOG_ENABLED const char *format; switch(type) { case FileType::Gif: { format = "GIF"; break; } default: { format = "???"; break; } } TVGLOG("RENDERER", "%s format is not supported", format); #endif return nullptr; } static SaveModule* _find(const char* filename) { auto ext = fileext(filename); if (ext && !strcmp(ext, "gif")) return _find(FileType::Gif); return nullptr; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ Saver::Saver() : pImpl(new Impl()) { } Saver::~Saver() { delete(pImpl); } Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcept { if (!paint) return Result::InvalidArguments; //Already on saving another resource. if (pImpl->saveModule) { Paint::rel(paint); return Result::InsufficientCondition; } if (auto saveModule = _find(filename)) { if (saveModule->save(paint, pImpl->bg, filename, quality)) { pImpl->saveModule = saveModule; return Result::Success; } else { Paint::rel(paint); delete(saveModule); return Result::Unknown; } } Paint::rel(paint); return Result::NonSupport; } Result Saver::background(Paint* paint) noexcept { if (!paint) return Result::InvalidArguments; if (pImpl->bg) pImpl->bg->unref(); paint->ref(); pImpl->bg = paint; return Result::Success; } Result Saver::save(Animation* animation, const char* filename, uint32_t quality, uint32_t fps) noexcept { if (!animation) return Result::InvalidArguments; //animation holds the picture, it must be 1 at the bottom. auto remove = animation->picture()->refCnt() <= 1 ? true : false; if (tvg::zero(animation->totalFrame())) { if (remove) delete(animation); return Result::InsufficientCondition; } //Already on saving another resource. if (pImpl->saveModule) { if (remove) delete(animation); return Result::InsufficientCondition; } if (auto saveModule = _find(filename)) { if (saveModule->save(animation, pImpl->bg, filename, quality, fps)) { pImpl->saveModule = saveModule; return Result::Success; } else { if (remove) delete(animation); delete(saveModule); return Result::Unknown; } } if (remove) delete(animation); return Result::NonSupport; } Result Saver::sync() noexcept { if (!pImpl->saveModule) return Result::InsufficientCondition; pImpl->saveModule->close(); delete(pImpl->saveModule); pImpl->saveModule = nullptr; return Result::Success; } Saver* Saver::gen() noexcept { return new Saver; } src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/workflows/pull_request_label.yml000664 001750 001750 00000000353 15164251010 037142 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0name: Pull Request Labeler on: [pull_request_target] jobs: triage: runs-on: ubuntu-latest steps: - uses: actions/labeler@v4 with: repo-token: ${{ secrets.GITHUB_TOKEN }} sync-labels: true thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/meson.build000664 001750 001750 00000004157 15164251010 047656 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/externalsource_file = [ 'ecma-builtin-bigint64array-prototype.inc.h', 'ecma-builtin-bigint64array.inc.h', 'ecma-builtin-biguint64array-prototype.inc.h', 'ecma-builtin-biguint64array.inc.h', 'ecma-builtin-float32array-prototype.inc.h', 'ecma-builtin-float32array.inc.h', 'ecma-builtin-float64array-prototype.inc.h', 'ecma-builtin-float64array.inc.h', 'ecma-builtin-int16array-prototype.inc.h', 'ecma-builtin-int16array.inc.h', 'ecma-builtin-int32array-prototype.inc.h', 'ecma-builtin-int32array.inc.h', 'ecma-builtin-int8array-prototype.inc.h', 'ecma-builtin-int8array.inc.h', 'ecma-builtin-typedarray-helpers.h', 'ecma-builtin-typedarray-prototype-template.inc.h', 'ecma-builtin-typedarray-prototype.inc.h', 'ecma-builtin-typedarray-template.inc.h', 'ecma-builtin-typedarray.inc.h', 'ecma-builtin-uint16array-prototype.inc.h', 'ecma-builtin-uint16array.inc.h', 'ecma-builtin-uint32array-prototype.inc.h', 'ecma-builtin-uint32array.inc.h', 'ecma-builtin-uint8array-prototype.inc.h', 'ecma-builtin-uint8array.inc.h', 'ecma-builtin-uint8clampedarray-prototype.inc.h', 'ecma-builtin-uint8clampedarray.inc.h', 'ecma-builtin-bigint64array-prototype.cpp', 'ecma-builtin-bigint64array.cpp', 'ecma-builtin-biguint64array-prototype.cpp', 'ecma-builtin-biguint64array.cpp', 'ecma-builtin-float32array-prototype.cpp', 'ecma-builtin-float32array.cpp', 'ecma-builtin-float64array-prototype.cpp', 'ecma-builtin-float64array.cpp', 'ecma-builtin-int16array-prototype.cpp', 'ecma-builtin-int16array.cpp', 'ecma-builtin-int32array-prototype.cpp', 'ecma-builtin-int32array.cpp', 'ecma-builtin-int8array-prototype.cpp', 'ecma-builtin-int8array.cpp', 'ecma-builtin-typedarray-helpers.cpp', 'ecma-builtin-typedarray-prototype.cpp', 'ecma-builtin-typedarray.cpp', 'ecma-builtin-uint16array-prototype.cpp', 'ecma-builtin-uint16array.cpp', 'ecma-builtin-uint32array-prototype.cpp', 'ecma-builtin-uint32array.cpp', 'ecma-builtin-uint8array-prototype.cpp', 'ecma-builtin-uint8array.cpp', 'ecma-builtin-uint8clampedarray-prototype.cpp', 'ecma-builtin-uint8clampedarray.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testMain.cpp000664 001750 001750 00000000320 15164251010 032457 0ustar00ddennedyddennedy000000 000000 // The only purpose of this file is to DEFINE the catch config so it can include main() #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/png/tvgPngLoader.h000664 001750 001750 00000003305 15164251010 034767 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_PNG_LOADER_H_ #define _TVG_PNG_LOADER_H_ #include "tvgLodePng.h" #include "tvgTaskScheduler.h" class PngLoader : public ImageLoader, public Task { private: LodePNGState state; unsigned char* data = nullptr; uint32_t size = 0; bool freeData = false; void run(unsigned tid) override; public: PngLoader(); ~PngLoader(); bool open(const char* path) override; bool open(const char* data, uint32_t size, const char* rpath, bool copy) override; bool read() override; RenderSurface* bitmap() override; }; #endif //_TVG_PNG_LOADER_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test4.lot000664 001750 001750 00000006201 15164251010 033770 0ustar00ddennedyddennedy000000 000000 {"v":"5.7.4","fr":30,"ip":0,"op":150,"w":500,"h":500,"nm":"Transform Tests (Skew & 3D)","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer with Skew","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]},"sk":{"a":1,"k":[{"t":0,"s":[0],"e":[30]},{"t":30,"s":[30],"e":[0]},{"t":60,"s":[0]}]},"sa":{"a":0,"k":0}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[1,0,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":1,"ind":2,"ty":4,"nm":"3D Rotated Shape","sr":1,"ks":{"o":{"a":0,"k":100},"rx":{"a":1,"k":[{"t":0,"s":[0],"e":[45]},{"t":30,"s":[45],"e":[0]},{"t":60,"s":[0]}]},"ry":{"a":1,"k":[{"t":0,"s":[0],"e":[30]},{"t":60,"s":[30]}]},"rz":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[60,60]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[0.2,0.5,0.9,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Skew Test Shape","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[100,100,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]},"sk":{"a":1,"k":[{"t":0,"s":[0],"e":[30]},{"t":15,"s":[30],"e":[60]},{"t":30,"s":[60],"e":[30]},{"t":45,"s":[30],"e":[45]},{"t":60,"s":[45],"e":[30]},{"t":75,"s":[30],"e":[0]},{"t":90,"s":[0]}]},"sa":{"a":1,"k":[{"t":0,"s":[0],"e":[0]},{"t":15,"s":[0],"e":[45]},{"t":30,"s":[45],"e":[90]},{"t":45,"s":[90],"e":[-90]},{"t":60,"s":[-90],"e":[180]},{"t":75,"s":[180],"e":[0]},{"t":90,"s":[0]}]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,80]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":10},"nm":"Rectangle"},{"ty":"fl","c":{"a":0,"k":[0.2,0.6,0.9,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill"}],"ip":0,"op":90,"st":0,"bm":0},{"ty":4,"nm":"Rectangle 2","sr":1,"st":0,"op":150,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[304.5,352.2827]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle Path 2","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[133,98.56532879503823]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[1,0.5569,0.9255],"t":0},{"s":[0.3176,0.0157,0.2667],"t":23}],"sid":"sdf"},"r":1,"o":{"a":0,"k":100}}],"ind":4},{"ty":4,"nm":"Rectangle 1","sr":1,"st":0,"op":150,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[133,210.8551]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.00000000000001,65.71021919669215]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[1,0.5569,0.9255],"sid":"sdf"},"r":1,"o":{"a":0,"k":100}}],"ind":5}]}src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h000664 001750 001750 00000026703 15164251010 036116 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SVG_LOADER_COMMON_H_ #define _TVG_SVG_LOADER_COMMON_H_ #include "tvgCommon.h" #include "tvgArray.h" #include "tvgInlist.h" #include "tvgColor.h" using SvgColor = tvg::RGB; #define STR_AS(A, B) !strcmp((A), (B)) struct Box { float x, y, w, h; void intersect(const Box& box) { auto x1 = x + w; auto y1 = y + h; auto x2 = box.x + box.w; auto y2 = box.y + box.h; x = x > box.x ? x : box.x; y = y > box.y ? y : box.y; w = (x1 < x2 ? x1 : x2) - x; h = (y1 < y2 ? y1 : y2) - y; if (w < 0.0f) w = 0.0f; if (h < 0.0f) h = 0.0f; } }; struct SvgNode; struct SvgStyleGradient; //NOTE: Please update simpleXmlNodeTypeToString() as well. enum class SvgNodeType { Doc, G, Defs, Animation, Arc, Circle, Ellipse, Image, Line, Path, Polygon, Polyline, Rect, Text, TextArea, Tspan, Use, Video, ClipPath, Mask, CssStyle, Symbol, Filter, GaussianBlur, Unknown }; /* // TODO - remove? enum class SvgLengthType { Percent, Px, Pc, Pt, Mm, Cm, In, }; */ enum class SvgFillFlags { Paint = 0x01, Opacity = 0x02, Gradient = 0x04, FillRule = 0x08, ClipPath = 0x16 }; constexpr bool operator&(SvgFillFlags a, SvgFillFlags b) { return int(a) & int(b); } constexpr SvgFillFlags operator|(SvgFillFlags a, SvgFillFlags b) { return SvgFillFlags(int(a) | int(b)); } constexpr void operator|=(SvgFillFlags& a, const SvgFillFlags b) { a = SvgFillFlags(int(a) | int(b)); } enum class SvgStrokeFlags { Paint = 0x1, Opacity = 0x2, Gradient = 0x4, Scale = 0x8, Width = 0x10, Cap = 0x20, Join = 0x40, Dash = 0x80, Miterlimit = 0x100, DashOffset = 0x200 }; constexpr bool operator&(SvgStrokeFlags a, SvgStrokeFlags b) { return int(a) & int(b); } constexpr SvgStrokeFlags operator|(SvgStrokeFlags a, SvgStrokeFlags b) { return SvgStrokeFlags(int(a) | int(b)); } constexpr void operator|=(SvgStrokeFlags& a, const SvgStrokeFlags b) { a = SvgStrokeFlags(int(a) | int(b)); } enum class SvgGradientType { Linear, Radial }; enum class SvgStyleFlags { Color = 0x01, Fill = 0x02, FillRule = 0x04, FillOpacity = 0x08, Opacity = 0x010, Stroke = 0x20, StrokeWidth = 0x40, StrokeLineJoin = 0x80, StrokeLineCap = 0x100, StrokeOpacity = 0x200, StrokeDashArray = 0x400, Transform = 0x800, ClipPath = 0x1000, Mask = 0x2000, MaskType = 0x4000, Display = 0x8000, PaintOrder = 0x10000, StrokeMiterlimit = 0x20000, StrokeDashOffset = 0x40000, Filter = 0x80000 }; constexpr bool operator&(SvgStyleFlags a, SvgStyleFlags b) { return int(a) & int(b); } constexpr SvgStyleFlags operator|(SvgStyleFlags a, SvgStyleFlags b) { return SvgStyleFlags(int(a) | int(b)); } constexpr void operator|=(SvgStyleFlags& a, const SvgStyleFlags b) { a = SvgStyleFlags(int(a) | int(b)); } enum class SvgStopStyleFlags { StopDefault = 0x0, StopOpacity = 0x01, StopColor = 0x02 }; constexpr bool operator&(SvgStopStyleFlags a, SvgStopStyleFlags b) { return int(a) & int(b); } constexpr SvgStopStyleFlags operator|(SvgStopStyleFlags a, SvgStopStyleFlags b) { return SvgStopStyleFlags(int(a) | int(b)); } enum class SvgGradientFlags { None = 0x0, GradientUnits = 0x1, SpreadMethod = 0x2, X1 = 0x4, X2 = 0x8, Y1 = 0x10, Y2 = 0x20, Cx = 0x40, Cy = 0x80, R = 0x100, Fx = 0x200, Fy = 0x400, Fr = 0x800 }; constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b) { return int(a) & int(b); } constexpr SvgGradientFlags operator |(SvgGradientFlags a, SvgGradientFlags b) { return SvgGradientFlags(int(a) | int(b)); } enum class SvgMaskType { Luminance = 0, Alpha }; enum class SvgXmlSpace { None, Default, Preserve }; //Length type to recalculate %, pt, pc, mm, cm etc enum class SvgParserLengthType { Vertical, Horizontal, Diagonal, //In case of, for example, radius of radial gradient Other }; enum class SvgViewFlag { None = 0x0, Width = 0x01, //viewPort width Height = 0x02, //viewPort height Viewbox = 0x04, //viewBox x,y,w,h - used only if all 4 are correctly set WidthInPercent = 0x08, HeightInPercent = 0x10 }; constexpr bool operator &(SvgViewFlag a, SvgViewFlag b) { return static_cast(a) & static_cast(b); } constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b) { return SvgViewFlag(int(a) | int(b)); } constexpr SvgViewFlag operator ^(SvgViewFlag a, SvgViewFlag b) { return SvgViewFlag(int(a) ^ int(b)); } enum class AspectRatioAlign { None, XMinYMin, XMidYMin, XMaxYMin, XMinYMid, XMidYMid, XMaxYMid, XMinYMax, XMidYMax, XMaxYMax }; enum class AspectRatioMeetOrSlice { Meet, Slice }; struct SvgDocNode { float w, h; //unit: point or in percentage see: SvgViewFlag Box vbox; SvgViewFlag viewFlag; SvgNode* defs; SvgNode* style; AspectRatioAlign align; AspectRatioMeetOrSlice meetOrSlice; }; struct SvgGNode { }; struct SvgDefsNode { Array gradients; }; struct SvgSymbolNode { float w, h; float vx, vy, vw, vh; AspectRatioAlign align; AspectRatioMeetOrSlice meetOrSlice; bool overflowVisible; bool hasViewBox; bool hasWidth; bool hasHeight; }; struct SvgUseNode { float x, y, w, h; bool isWidthSet; bool isHeightSet; SvgNode* symbol; }; struct SvgEllipseNode { float cx, cy, rx, ry; }; struct SvgCircleNode { float cx, cy, r; }; struct SvgRectNode { float x, y, w, h, rx, ry; bool hasRx, hasRy; }; struct SvgLineNode { float x1, y1, x2, y2; }; struct SvgImageNode { float x, y, w, h; char* href; }; struct SvgPathNode { char* path; }; struct SvgPolygonNode { Array pts; }; struct SvgClipNode { bool userSpace; }; struct SvgMaskNode { SvgMaskType type; bool userSpace; }; struct SvgCssStyleNode { }; struct SvgTextNode { char* text; char* fontFamily; float x, y; float fontSize; }; struct SvgGaussianBlurNode { float stdDevX, stdDevY; Box box; bool isPercentage[4]; bool hasBox; bool edgeModeWrap; }; struct SvgFilterNode { Box box; bool isPercentage[4]; bool filterUserSpace; bool primitiveUserSpace; }; struct SvgLinearGradient { float x1, y1, x2, y2; bool isX1Percentage; bool isY1Percentage; bool isX2Percentage; bool isY2Percentage; }; struct SvgRadialGradient { float cx, cy, fx, fy, r, fr; bool isCxPercentage; bool isCyPercentage; bool isFxPercentage; bool isFyPercentage; bool isRPercentage; bool isFrPercentage; }; struct SvgComposite { char *url; SvgNode* node; bool applying; //flag for checking circular dependency. }; struct SvgPaint { SvgStyleGradient* gradient; char *url; SvgColor color; bool none; bool curColor; }; struct SvgDash { Array array; float offset; }; struct SvgStyleGradient { SvgGradientType type; char* id; char* ref; FillSpread spread; SvgRadialGradient* radial; SvgLinearGradient* linear; Matrix* transform; Array stops; SvgGradientFlags flags; bool userSpace; void clear() { stops.reset(); tvg::free(transform); tvg::free(radial); tvg::free(linear); tvg::free(ref); tvg::free(id); } }; struct SvgStyleFill { SvgFillFlags flags; SvgPaint paint; int opacity; FillRule fillRule; }; struct SvgStyleStroke { SvgStrokeFlags flags; SvgPaint paint; int opacity; float scale; float width; float centered; StrokeCap cap; StrokeJoin join; float miterlimit; SvgDash dash; }; struct SvgFilter { char *url; SvgNode* node; }; struct SvgStyleProperty { SvgStyleFill fill; SvgStyleStroke stroke; SvgComposite clipPath; SvgComposite mask; SvgFilter filter; int opacity; SvgColor color; char* cssClass; SvgStyleFlags flags; SvgStyleFlags flagsImportance; //indicates the importance of the flag - if set, higher priority is applied (https://drafts.csswg.org/css-cascade-4/#importance) bool curColorSet; bool paintOrder; //true if default (fill, stroke), false otherwise bool display; }; struct SvgNode { SvgNodeType type; SvgNode* parent; Array child; char *id; SvgStyleProperty *style; Matrix* transform; union { SvgGNode g; SvgDocNode doc; SvgDefsNode defs; SvgUseNode use; SvgCircleNode circle; SvgEllipseNode ellipse; SvgPolygonNode polygon; SvgPolygonNode polyline; SvgRectNode rect; SvgPathNode path; SvgLineNode line; SvgImageNode image; SvgMaskNode mask; SvgClipNode clip; SvgCssStyleNode cssStyle; SvgSymbolNode symbol; SvgTextNode text; SvgFilterNode filter; SvgGaussianBlurNode gaussianBlur; } node; SvgXmlSpace xmlSpace = SvgXmlSpace::None; ~SvgNode(); }; struct SvgParser { SvgNode* node; SvgStyleGradient* styleGrad; Fill::ColorStop gradStop; SvgStopStyleFlags flags; Box global; struct { bool parsedFx; bool parsedFy; } gradient; }; struct SvgNodeIdPair { INLIST_ITEM(SvgNodeIdPair); SvgNodeIdPair(SvgNode* n, char* i) : node{n}, id{i} {} SvgNode* node; char *id; }; struct FontFace { char* name = nullptr; char* src = nullptr; size_t srcLen = 0; char* decoded = nullptr; }; enum class OpenedTagType : uint8_t { Other = 0, Style, Text }; struct SvgLoaderData { Array stack; SvgNode* doc = nullptr; SvgNode* def = nullptr; //also used to store nested graphic nodes SvgNode* cssStyle = nullptr; Array gradients; Array gradientStack; //For stops SvgParser* svgParse = nullptr; Inlist cloneNodes; Array nodesToStyle; Array images; //embedded images Array fonts; int level = 0; bool result = false; OpenedTagType openedTag = OpenedTagType::Other; SvgNode* currentGraphicsNode = nullptr; }; #endif thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-error.inc.h000664 001750 001750 00000002332 15164251010 047742 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Error built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.7.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_ERROR_PROTOTYPE, ECMA_PROPERTY_FIXED) STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_ERROR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/webp/decode.h000664 001750 001750 00000023176 15164251010 034734 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Main decoding functions for WebP images. // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_WEBP_DECODE_H_ #define WEBP_WEBP_DECODE_H_ #include "./types.h" #ifdef __cplusplus extern "C" { #endif #define WEBP_DECODER_ABI_VERSION 0x0205 // MAJOR(8b) + MINOR(8b) // Note: forward declaring enumerations is not allowed in (strict) C and C++, // the types are left here for reference. // typedef enum VP8StatusCode VP8StatusCode; // typedef enum WEBP_CSP_MODE WEBP_CSP_MODE; typedef struct WebPRGBABuffer WebPRGBABuffer; typedef struct WebPYUVABuffer WebPYUVABuffer; typedef struct WebPDecBuffer WebPDecBuffer; typedef struct WebPIDecoder WebPIDecoder; typedef struct WebPBitstreamFeatures WebPBitstreamFeatures; typedef struct WebPDecoderOptions WebPDecoderOptions; typedef struct WebPDecoderConfig WebPDecoderConfig; // Return the decoder's version number, packed in hexadecimal using 8bits for // each of major/minor/revision. E.g: v2.5.7 is 0x020507. WEBP_EXTERN(int) WebPGetDecoderVersion(void); // Retrieve basic header information: width, height. // This function will also validate the header and return 0 in // case of formatting error. // Pointers 'width' and 'height' can be passed NULL if deemed irrelevant. WEBP_EXTERN(int) WebPGetInfo(const uint8_t* data, size_t data_size, int* width, int* height); // Decodes WebP images pointed to by 'data' and returns RGBA samples, along // with the dimensions in *width and *height. The ordering of samples in // memory is R, G, B, A, R, G, B, A... in scan order (endian-independent). // The returned pointer should be deleted calling free(). // Returns NULL in case of error. WEBP_EXTERN(uint8_t*) WebPDecodeRGBA(const uint8_t* data, size_t data_size, int* width, int* height); // Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data. WEBP_EXTERN(uint8_t*) WebPDecodeBGRA(const uint8_t* data, size_t data_size, int* width, int* height); //------------------------------------------------------------------------------ // Output colorspaces and buffer // Colorspaces // Note: the naming describes the byte-ordering of packed samples in memory. // For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,... // Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels. // RGBA-4444 and RGB-565 colorspaces are represented by following byte-order: // RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ... // RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ... // In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for // these two modes: // RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ... // RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ... typedef enum WEBP_CSP_MODE { MODE_RGB = 0, MODE_RGBA = 1, MODE_BGR = 2, MODE_BGRA = 3, MODE_ARGB = 4, MODE_RGBA_4444 = 5, MODE_RGB_565 = 6, // RGB-premultiplied transparent modes (alpha value is preserved) MODE_rgbA = 7, MODE_bgrA = 8, MODE_Argb = 9, MODE_rgbA_4444 = 10, // YUV modes must come after RGB ones. MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0 MODE_LAST = 13 } WEBP_CSP_MODE; // Some useful macros: static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) { return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb || mode == MODE_rgbA_4444); } static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) { return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB || mode == MODE_RGBA_4444 || mode == MODE_YUVA || WebPIsPremultipliedMode(mode)); } static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) { return (mode < MODE_YUV); } //------------------------------------------------------------------------------ // WebPDecBuffer: Generic structure for describing the output sample buffer. struct WebPRGBABuffer { // view as RGBA uint8_t* rgba; // pointer to RGBA samples int stride; // stride in bytes from one scanline to the next. size_t size; // total size of the *rgba buffer. }; struct WebPYUVABuffer { // view as YUVA uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples int y_stride; // luma stride int u_stride, v_stride; // chroma strides int a_stride; // alpha stride size_t y_size; // luma plane size size_t u_size, v_size; // chroma planes size size_t a_size; // alpha-plane size }; // Output buffer struct WebPDecBuffer { WEBP_CSP_MODE colorspace; // Colorspace. int width, height; // Dimensions. int is_external_memory; // If true, 'internal_memory' pointer is not used. union { WebPRGBABuffer RGBA; WebPYUVABuffer YUVA; } u; // Nameless union of buffer parameters. uint32_t pad[4]; // padding for later use uint8_t* private_memory; // Internally allocated memory (only when // is_external_memory is false). Should not be used // externally, but accessed via the buffer union. }; // Internal, version-checked, entry point WEBP_EXTERN(int) WebPInitDecBufferInternal(WebPDecBuffer*, int); // Initialize the structure as empty. Must be called before any other use. // Returns false in case of version mismatch static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) { return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION); } // Free any memory associated with the buffer. Must always be called last. // Note: doesn't free the 'buffer' structure itself. WEBP_EXTERN(void) WebPFreeDecBuffer(WebPDecBuffer* buffer); //------------------------------------------------------------------------------ // Enumeration of the status codes typedef enum VP8StatusCode { VP8_STATUS_OK = 0, VP8_STATUS_OUT_OF_MEMORY, VP8_STATUS_INVALID_PARAM, VP8_STATUS_BITSTREAM_ERROR, VP8_STATUS_UNSUPPORTED_FEATURE, VP8_STATUS_SUSPENDED, VP8_STATUS_USER_ABORT, VP8_STATUS_NOT_ENOUGH_DATA } VP8StatusCode; //------------------------------------------------------------------------------ // Advanced decoding parametrization // // Code sample for using the advanced decoding API /* // A) Init a configuration object WebPDecoderConfig config; CHECK(WebPInitDecoderConfig(&config)); // B) optional: retrieve the bitstream's features. CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK); // C) Adjust 'config', if needed config.no_fancy_upsampling = 1; config.output.colorspace = MODE_BGRA; // etc. // Note that you can also make config.output point to an externally // supplied memory buffer, provided it's big enough to store the decoded // picture. Otherwise, config.output will just be used to allocate memory // and store the decoded picture. // D) Decode! CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK); // E) Decoded image is now in config.output (and config.output.u.RGBA) // F) Reclaim memory allocated in config's object. It's safe to call // this function even if the memory is external and wasn't allocated // by WebPDecode(). WebPFreeDecBuffer(&config.output); */ // Features gathered from the bitstream struct WebPBitstreamFeatures { int width; // Width in pixels, as read from the bitstream. int height; // Height in pixels, as read from the bitstream. int has_alpha; // True if the bitstream contains an alpha channel. int has_animation; // True if the bitstream is an animation. int format; // 0 = undefined (/mixed), 1 = lossy, 2 = lossless // Unused for now: int no_incremental_decoding; // if true, using incremental decoding is not // recommended. int rotate; // TODO(later) int uv_sampling; // should be 0 for now. TODO(later) uint32_t pad[2]; // padding for later use }; // Decoding options struct WebPDecoderOptions { int bypass_filtering; // if true, skip the in-loop filtering int no_fancy_upsampling; // if true, use faster pointwise upsampler int use_cropping; // if true, cropping is applied _first_ int crop_left, crop_top; // top-left position for cropping. // Will be snapped to even values. int crop_width, crop_height; // dimension of the cropping area int use_scaling; // if true, scaling is applied _afterward_ int scaled_width, scaled_height; // final resolution int use_threads; // if true, use multi-threaded decoding int dithering_strength; // dithering strength (0=Off, 100=full) int flip; // flip output vertically int alpha_dithering_strength; // alpha dithering strength in [0..100] // Unused for now: int force_rotation; // forced rotation (to be applied _last_) int no_enhancement; // if true, discard enhancement layer uint32_t pad[3]; // padding for later use }; #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_WEBP_DECODE_H_ */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers-number.h000664 001750 001750 00000014704 15164251010 045150 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_HELPERS_NUMBER_H #define ECMA_HELPERS_NUMBER_H #include "ecma-globals.h" #include "jerry-config.h" /** * Binary representation of an ecma-number */ #if JERRY_NUMBER_TYPE_FLOAT64 typedef uint64_t ecma_binary_num_t; #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ typedef uint32_t ecma_binary_num_t; #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Makes it possible to read/write the binary representation of an ecma_number_t * without strict aliasing rule violation. */ typedef union { ecma_number_t as_number; /**< ecma-number */ ecma_binary_num_t as_binary; /**< binary representation */ } ecma_number_accessor_t; ecma_binary_num_t ecma_number_to_binary (ecma_number_t number); ecma_number_t ecma_number_from_binary (ecma_binary_num_t binary); bool ecma_number_sign (ecma_binary_num_t binary); uint32_t ecma_number_biased_exp (ecma_binary_num_t binary); uint64_t ecma_number_fraction (ecma_binary_num_t binary); ecma_number_t ecma_number_create (bool sign, uint32_t biased_exp, uint64_t fraction); /** * Maximum number of significant decimal digits that an ecma-number can store */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_MAX_DIGITS (19) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_MAX_DIGITS (9) #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Width of sign field * * See also: * IEEE-754 2008, 3.6, Table 3.5 */ #define ECMA_NUMBER_SIGN_WIDTH (1) /** * Width of biased exponent field * * See also: * IEEE-754 2008, 3.6, Table 3.5 */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_BIASED_EXP_WIDTH (11) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_BIASED_EXP_WIDTH (8) #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Exponent bias */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_EXPONENT_BIAS (1023) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_EXPONENT_BIAS (127) #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Width of fraction field * * See also: * IEEE-754 2008, 3.6, Table 3.5 */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_FRACTION_WIDTH (52) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_FRACTION_WIDTH (23) #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Sign bit in ecma-numbers */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_SIGN_BIT 0x8000000000000000ull #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_SIGN_BIT 0x7f800000u #endif /* !JERRY_NUMBER_TYPE_FLOAT64 */ /** * Binary representation of an IEEE-754 QNaN value. */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_BINARY_QNAN 0x7ff8000000000000ull #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_BINARY_QNAN 0x7fc00000u #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Binary representation of an IEEE-754 Infinity value. */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_BINARY_INF 0x7ff0000000000000ull #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_BINARY_INF 0x7f800000u #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Binary representation of an IEEE-754 zero value. */ #define ECMA_NUMBER_BINARY_ZERO 0x0ull /** * Number.MIN_VALUE (i.e., the smallest positive value of ecma-number) * * See also: ECMA_262 v5, 15.7.3.3 */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_MIN_VALUE ((ecma_number_t) 5e-324) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_MIN_VALUE (FLT_MIN) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Number.MAX_VALUE (i.e., the maximum value of ecma-number) * * See also: ECMA_262 v5, 15.7.3.2 */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_MAX_VALUE ((ecma_number_t) 1.7976931348623157e+308) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_MAX_VALUE (FLT_MAX) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Number.EPSILON * * See also: ECMA_262 v6, 20.1.2.1 */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_EPSILON ((ecma_number_t) 2.2204460492503130808472633361816e-16) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_EPSILON ((ecma_number_t) 1.1920928955078125e-7) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Number.MAX_SAFE_INTEGER * * See also: ECMA_262 v6, 20.1.2.6 */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_MAX_SAFE_INTEGER ((ecma_number_t) 0x1FFFFFFFFFFFFF) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_MAX_SAFE_INTEGER ((ecma_number_t) 0xFFFFFF) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Number.MIN_SAFE_INTEGER * * See also: ECMA_262 v6, 20.1.2.8 */ #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_NUMBER_MIN_SAFE_INTEGER ((ecma_number_t) -0x1FFFFFFFFFFFFF) #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define ECMA_NUMBER_MIN_SAFE_INTEGER ((ecma_number_t) -0xFFFFFF) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Number.MAX_VALUE exponent part */ #if JERRY_NUMBER_TYPE_FLOAT64 #define NUMBER_MAX_DECIMAL_EXPONENT 308 #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define NUMBER_MAX_DECIMAL_EXPONENT 38 #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Number.MIN_VALUE exponent part */ #if JERRY_NUMBER_TYPE_FLOAT64 #define NUMBER_MIN_DECIMAL_EXPONENT -324 #else /* !JERRY_NUMBER_TYPE_FLOAT64 */ #define NUMBER_MIN_DECIMAL_EXPONENT -45 #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ /** * Euler number */ #define ECMA_NUMBER_E ((ecma_number_t) 2.7182818284590452354) /** * Natural logarithm of 10 */ #define ECMA_NUMBER_LN10 ((ecma_number_t) 2.302585092994046) /** * Natural logarithm of 2 */ #define ECMA_NUMBER_LN2 ((ecma_number_t) 0.6931471805599453) /** * Logarithm base 2 of the Euler number */ #define ECMA_NUMBER_LOG2E ((ecma_number_t) 1.4426950408889634) /** * Logarithm base 10 of the Euler number */ #define ECMA_NUMBER_LOG10E ((ecma_number_t) 0.4342944819032518) /** * Pi number */ #define ECMA_NUMBER_PI ((ecma_number_t) 3.1415926535897932) /** * Square root of 0.5 */ #define ECMA_NUMBER_SQRT_1_2 ((ecma_number_t) 0.7071067811865476) /** * Square root of 2 */ #define ECMA_NUMBER_SQRT2 ((ecma_number_t) 1.4142135623730951) #endif /* !ECMA_HELPERS_NUMBER_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-errors.h000664 001750 001750 00000002417 15164251010 043532 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_ERRORS_H #define ECMA_ERRORS_H #include "lit-globals.h" typedef enum { ECMA_ERR_EMPTY, /** @cond doxygen_suppress */ #if JERRY_ERROR_MESSAGES #define ECMA_ERROR_DEF(id, ascii_zt_string) id, #else /* !JERRY_ERROR_MESSAGES */ #define ECMA_ERROR_DEF(id, ascii_zt_string) id = ECMA_ERR_EMPTY, #endif /* JERRY_ERROR_MESSAGES */ #include "ecma-error-messages.inc.h" #undef ECMA_ERROR_DEF /** @endcond */ ECMA_IS_VALID_CONSTRUCTOR /* used as return value when checking constructor */ } ecma_error_msg_t; const char* ecma_get_error_msg (ecma_error_msg_t id); lit_utf8_size_t ecma_get_error_size (ecma_error_msg_t id); #endif /* !ECMA_ERRORS_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-weakmap.cpp000664 001750 001750 00000004327 15164251010 050027 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-container-object.h" #include "ecma-exceptions.h" #if JERRY_BUILTIN_CONTAINER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-weakmap.inc.h" #define BUILTIN_UNDERSCORED_ID weakmap #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup weakmap ECMA WeakMap object built-in * @{ */ /** * Handle calling [[Call]] of built-in WeakMap object * * @return ecma value */ ecma_value_t ecma_builtin_weakmap_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_WEAKMAP_REQUIRES_NEW); } /* ecma_builtin_weakmap_dispatch_call */ /** * Handle calling [[Construct]] of built-in WeakMap object * * @return ecma value */ ecma_value_t ecma_builtin_weakmap_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_op_container_create (arguments_list_p, arguments_list_len, LIT_MAGIC_STRING_WEAKMAP_UL, ECMA_BUILTIN_ID_WEAKMAP_PROTOTYPE); } /* ecma_builtin_weakmap_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_CONTAINER */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-float32array.cpp000664 001750 001750 00000004400 15164251010 053067 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-float32array.inc.h" #define BUILTIN_UNDERSCORED_ID float32array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup float32array ECMA Float32Array object built-in * @{ */ /** * Handle calling [[Call]] of Float32Array * * @return ecma value */ ecma_value_t ecma_builtin_float32array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_FLOAT32_ARRAY_REQUIRES_NEW); } /* ecma_builtin_float32array_dispatch_call */ /** * Handle calling [[Construct]] of Float32Array * * @return ecma value */ ecma_value_t ecma_builtin_float32array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_FLOAT32_ARRAY); } /* ecma_builtin_float32array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-function.cpp000664 001750 001750 00000004330 15164251010 051334 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-globals.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #include "ecma-function-object.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-function.inc.h" #define BUILTIN_UNDERSCORED_ID async_function #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup asyncfunction ECMA AsyncFunction object built-in * @{ */ /** * Handle calling [[Call]] of built-in AsyncFunction object * * @return constructed async function object - if success * raised error otherwise */ ecma_value_t ecma_builtin_async_function_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_op_create_dynamic_function (arguments_list_p, arguments_list_len, ECMA_PARSE_ASYNC_FUNCTION); } /* ecma_builtin_async_function_dispatch_call */ /** * Handle calling [[Construct]] of built-in AsyncFunction object * * @return constructed async function object - if success * raised error otherwise */ ecma_value_t ecma_builtin_async_function_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_async_function_dispatch_call (arguments_list_p, arguments_list_len); } /* ecma_builtin_async_function_dispatch_construct */ /** * @} * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/rapidjson.h000664 001750 001750 00000061672 15164251010 037025 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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 overridden 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 "tvgCommon.h" /////////////////////////////////////////////////////////////////////////////// // 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 ".." 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 /////////////////////////////////////////////////////////////////////////////// // __cplusplus macro //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #if defined(_MSC_VER) #define RAPIDJSON_CPLUSPLUS _MSVC_LANG #else #define RAPIDJSON_CPLUSPLUS __cplusplus #endif //!@endcond /////////////////////////////////////////////////////////////////////////////// // 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 #endif // RAPIDJSON_HAS_STDSTRING /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_USE_MEMBERSMAP /*! \def RAPIDJSON_USE_MEMBERSMAP \ingroup RAPIDJSON_CONFIG \brief Enable RapidJSON support for object members handling in a \c std::multimap By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object members are stored in a \c std::multimap for faster lookup and deletion times, a trade off with a slightly slower insertion time and a small object allocat(or)ed memory overhead. \hideinitializer */ #ifndef RAPIDJSON_USE_MEMBERSMAP #define RAPIDJSON_USE_MEMBERSMAP 0 // not by default #endif /////////////////////////////////////////////////////////////////////////////// // 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 #include #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 endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __BYTE_ORDER__ // Detect with GLIBC's endian.h # elif defined(__GLIBC__) # include # if (__BYTE_ORDER == __LITTLE_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif (__BYTE_ORDER == __BIG_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else # error Unknown machine endianness 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 endianness 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. The default is 8 bytes. User can customize by defining the RAPIDJSON_ALIGN function macro. */ #ifndef RAPIDJSON_ALIGN #define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) #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(high32) << 32) | static_cast(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((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) #define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(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 //#define RAPIDJSON_ASSERT(x) assert(x) #define RAPIDJSON_ASSERT(x) #endif // RAPIDJSON_ASSERT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT // Prefer C++11 static_assert, if available #ifndef RAPIDJSON_STATIC_ASSERT #if RAPIDJSON_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 struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END #if defined(__GNUC__) || defined(__clang__) #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)> \ 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)) #if defined(__has_builtin) #define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) #else #define RAPIDJSON_HAS_BUILTIN(x) 0 #endif /////////////////////////////////////////////////////////////////////////////// // 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 #ifndef RAPIDJSON_HAS_CXX11 #define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L) #endif #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS #if RAPIDJSON_HAS_CXX11 #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #elif defined(__clang__) #if __has_feature(cxx_rvalue_references) && \ (defined(_MSC_VER) || 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) || \ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 #endif #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS #if RAPIDJSON_HAS_CXX11_RVALUE_REFS #include // std::move #endif #ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT #if RAPIDJSON_HAS_CXX11 #define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 #elif 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 >= 1900) || \ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 #else #define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 #endif #endif #ifndef RAPIDJSON_NOEXCEPT #if RAPIDJSON_HAS_CXX11_NOEXCEPT #define RAPIDJSON_NOEXCEPT noexcept #else #define RAPIDJSON_NOEXCEPT throw() #endif // RAPIDJSON_HAS_CXX11_NOEXCEPT #endif // no automatic detection, yet #ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS #if (defined(_MSC_VER) && _MSC_VER >= 1700) #define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 #else #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif #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) || \ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 #else #define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 #endif #endif // RAPIDJSON_HAS_CXX11_RANGE_FOR /////////////////////////////////////////////////////////////////////////////// // C++17 features #ifndef RAPIDJSON_HAS_CXX17 #define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L) #endif #if RAPIDJSON_HAS_CXX17 # define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] #elif defined(__has_cpp_attribute) # if __has_cpp_attribute(clang::fallthrough) # define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]] # elif __has_cpp_attribute(fallthrough) # define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough)) # else # define RAPIDJSON_DELIBERATE_FALLTHROUGH # endif #else # define RAPIDJSON_DELIBERATE_FALLTHROUGH #endif //!@endcond //! Assertion (in non-throwing contexts). /*! \ingroup RAPIDJSON_CONFIG Some functions provide a \c noexcept guarantee, if the compiler supports it. In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to throw an exception. This macro adds a separate customization point for such cases. Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is supported, and to \ref RAPIDJSON_ASSERT otherwise. */ /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NOEXCEPT_ASSERT #ifndef RAPIDJSON_NOEXCEPT_ASSERT #ifdef RAPIDJSON_ASSERT_THROWS #include #define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) #else #define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) #endif // RAPIDJSON_ASSERT_THROWS #endif // RAPIDJSON_NOEXCEPT_ASSERT /////////////////////////////////////////////////////////////////////////////// // malloc/realloc/free #ifndef RAPIDJSON_MALLOC ///! customization point for global \c malloc #define RAPIDJSON_MALLOC(size) tvg::malloc(size) #endif #ifndef RAPIDJSON_REALLOC ///! customization point for global \c realloc #define RAPIDJSON_REALLOC(ptr, new_size) tvg::realloc(ptr, new_size) #endif #ifndef RAPIDJSON_FREE ///! customization point for global \c free #define RAPIDJSON_FREE(ptr) tvg::free(ptr) #endif /////////////////////////////////////////////////////////////////////////////// // 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_ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-reflect.cpp000664 001750 001750 00000023514 15164251010 050025 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-array-object.h" #include "ecma-builtin-function-prototype.h" #include "ecma-builtin-helpers.h" #include "ecma-builtin-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-iterator-object.h" #include "ecma-proxy-object.h" #include "jcontext.h" #if JERRY_BUILTIN_REFLECT #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_REFLECT_OBJECT_ROUTINE_START = 0, ECMA_REFLECT_OBJECT_GET, /* ECMA-262 v6, 26.1.6 */ ECMA_REFLECT_OBJECT_SET, /* ECMA-262 v6, 26.1.13 */ ECMA_REFLECT_OBJECT_HAS, /* ECMA-262 v6, 26.1.9 */ ECMA_REFLECT_OBJECT_DELETE_PROPERTY, /* ECMA-262 v6, 26.1.4 */ ECMA_REFLECT_OBJECT_CONSTRUCT, /* ECMA-262, 26.1.2 */ ECMA_REFLECT_OBJECT_OWN_KEYS, /* ECMA-262 v6, 26.1.11 */ ECMA_REFLECT_OBJECT_GET_PROTOTYPE_OF, /* ECMA-262 v6, 26.1.8 */ ECMA_REFLECT_OBJECT_SET_PROTOTYPE_OF, /* ECMA-262 v6, 26.1.14 */ ECMA_REFLECT_OBJECT_APPLY, /* ECMA-262 v6, 26.1.1 */ ECMA_REFLECT_OBJECT_DEFINE_PROPERTY, /* ECMA-262 v6, 26.1.3 */ ECMA_REFLECT_OBJECT_GET_OWN_PROPERTY_DESCRIPTOR, /* ECMA-262 v6, 26.1.7 */ ECMA_REFLECT_OBJECT_IS_EXTENSIBLE, /* ECMA-262 v6, 26.1.10 */ ECMA_REFLECT_OBJECT_PREVENT_EXTENSIONS, /* ECMA-262 v6, 26.1.12 */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-reflect.inc.h" #define BUILTIN_UNDERSCORED_ID reflect #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup object ECMA Reflect object built-in * @{ */ /** * Dispatcher for the built-in's routines. * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_reflect_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (this_arg); if (builtin_routine_id < ECMA_REFLECT_OBJECT_CONSTRUCT) { /* 1. */ if (arguments_number == 0 || !ecma_is_value_object (arguments_list[0])) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); } /* 2. */ ecma_string_t *name_str_p = ecma_op_to_property_key (arguments_list[1]); /* 3. */ if (name_str_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t ret_value; ecma_object_t *target_p = ecma_get_object_from_value (arguments_list[0]); switch (builtin_routine_id) { case ECMA_REFLECT_OBJECT_GET: { ecma_value_t receiver = arguments_list[0]; /* 4. */ if (arguments_number > 2) { receiver = arguments_list[2]; } ret_value = ecma_op_object_get_with_receiver (target_p, name_str_p, receiver); break; } case ECMA_REFLECT_OBJECT_HAS: { ret_value = ecma_op_object_has_property (target_p, name_str_p); break; } case ECMA_REFLECT_OBJECT_DELETE_PROPERTY: { ret_value = ecma_op_object_delete (target_p, name_str_p, false); break; } default: { JERRY_ASSERT (builtin_routine_id == ECMA_REFLECT_OBJECT_SET); ecma_value_t receiver = arguments_list[0]; if (arguments_number > 3) { receiver = arguments_list[3]; } ret_value = ecma_op_object_put_with_receiver (target_p, name_str_p, arguments_list[2], receiver, false); break; } } ecma_deref_ecma_string (name_str_p); return ret_value; } if (builtin_routine_id == ECMA_REFLECT_OBJECT_OWN_KEYS) { /* 1. */ if (arguments_number == 0 || !ecma_is_value_object (arguments_list[0])) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); } ecma_object_t *target_p = ecma_get_object_from_value (arguments_list[0]); /* 2. */ ecma_collection_t *prop_names = ecma_op_object_own_property_keys (target_p, JERRY_PROPERTY_FILTER_ALL); #if JERRY_BUILTIN_PROXY if (prop_names == NULL) { return ECMA_VALUE_ERROR; } #endif /* JERRY_BUILTIN_PROXY */ /* 3. */ return ecma_op_new_array_object_from_collection (prop_names, false); } if (builtin_routine_id == ECMA_REFLECT_OBJECT_CONSTRUCT) { /* 1. */ if (arguments_number < 1 || !ecma_is_constructor (arguments_list[0])) { return ecma_raise_type_error (ECMA_ERR_TARGET_IS_NOT_A_CONSTRUCTOR); } ecma_object_t *target_p = ecma_get_object_from_value (arguments_list[0]); /* 2. */ ecma_object_t *new_target_p = target_p; if (arguments_number > 2) { /* 3. */ if (!ecma_is_constructor (arguments_list[2])) { return ecma_raise_type_error (ECMA_ERR_TARGET_IS_NOT_A_CONSTRUCTOR); } new_target_p = ecma_get_object_from_value (arguments_list[2]); } /* 4. */ if (arguments_number < 2) { return ecma_raise_type_error (ECMA_ERR_REFLECT_EXPECTS_AN_OBJECT_AS_SECOND_ARGUMENT); } ecma_collection_t *coll_p = ecma_op_create_list_from_array_like (arguments_list[1], false); if (coll_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t ret_value = ecma_op_function_construct (target_p, new_target_p, coll_p->buffer_p, coll_p->item_count); ecma_collection_free (coll_p); return ret_value; } if (!ecma_is_value_object (arguments_list[0])) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); } switch (builtin_routine_id) { case ECMA_REFLECT_OBJECT_GET_PROTOTYPE_OF: { return ecma_builtin_object_object_get_prototype_of (ecma_get_object_from_value (arguments_list[0])); } case ECMA_REFLECT_OBJECT_SET_PROTOTYPE_OF: { if (!ecma_is_value_object (arguments_list[1]) && !ecma_is_value_null (arguments_list[1])) { return ecma_raise_type_error (ECMA_ERR_PROTOTYPE_IS_NEITHER_OBJECT_NOR_NULL); } ecma_object_t *obj_p = ecma_get_object_from_value (arguments_list[0]); ecma_value_t status; #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { status = ecma_proxy_object_set_prototype_of (obj_p, arguments_list[1]); } else #endif /* JERRY_BUILTIN_PROXY */ { status = ecma_op_ordinary_object_set_prototype_of (obj_p, arguments_list[1]); } return status; } case ECMA_REFLECT_OBJECT_APPLY: { if (!ecma_op_is_callable (arguments_list[0])) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_FUNCTION); } ecma_object_t *func_obj_p = ecma_get_object_from_value (arguments_list[0]); return ecma_builtin_function_prototype_object_apply (func_obj_p, arguments_list[1], arguments_list[2]); } case ECMA_REFLECT_OBJECT_DEFINE_PROPERTY: { ecma_object_t *obj_p = ecma_get_object_from_value (arguments_list[0]); ecma_string_t *name_str_p = ecma_op_to_property_key (arguments_list[1]); if (name_str_p == NULL) { return ECMA_VALUE_ERROR; } ecma_property_descriptor_t prop_desc; ecma_value_t conv_result = ecma_op_to_property_descriptor (arguments_list[2], &prop_desc); if (ECMA_IS_VALUE_ERROR (conv_result)) { ecma_deref_ecma_string (name_str_p); return conv_result; } ecma_value_t result = ecma_op_object_define_own_property (obj_p, name_str_p, &prop_desc); ecma_deref_ecma_string (name_str_p); ecma_free_property_descriptor (&prop_desc); if (ECMA_IS_VALUE_ERROR (result)) { return result; } bool boolean_result = ecma_op_to_boolean (result); return ecma_make_boolean_value (boolean_result); } case ECMA_REFLECT_OBJECT_GET_OWN_PROPERTY_DESCRIPTOR: { ecma_object_t *obj_p = ecma_get_object_from_value (arguments_list[0]); ecma_string_t *name_str_p = ecma_op_to_property_key (arguments_list[1]); if (name_str_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t ret_val = ecma_builtin_object_object_get_own_property_descriptor (obj_p, name_str_p); ecma_deref_ecma_string (name_str_p); return ret_val; } case ECMA_REFLECT_OBJECT_IS_EXTENSIBLE: { ecma_object_t *obj_p = ecma_get_object_from_value (arguments_list[0]); return ecma_builtin_object_object_is_extensible (obj_p); } default: { JERRY_ASSERT (builtin_routine_id == ECMA_REFLECT_OBJECT_PREVENT_EXTENSIONS); ecma_object_t *obj_p = ecma_get_object_from_value (arguments_list[0]); #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (obj_p)) { return ecma_proxy_object_prevent_extensions (obj_p); } #endif /* !JERRY_BUILTIN_PROXY */ ecma_op_ordinary_object_prevent_extensions (obj_p); return ECMA_VALUE_TRUE; } } } /* ecma_builtin_reflect_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_REFLECT */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers-collection.cpp000664 001750 001750 00000034071 15164251010 046345 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-conversion.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ /** * Allocate a collection of ecma values. * * @return pointer to the collection */ ecma_collection_t * ecma_new_collection (void) { ecma_collection_t *collection_p; collection_p = (ecma_collection_t *) jmem_heap_alloc_block (sizeof (ecma_collection_t)); collection_p->item_count = 0; collection_p->capacity = ECMA_COLLECTION_INITIAL_CAPACITY; const uint32_t size = ECMA_COLLECTION_ALLOCATED_SIZE (ECMA_COLLECTION_INITIAL_CAPACITY); collection_p->buffer_p = (ecma_value_t *) jmem_heap_alloc_block (size); return collection_p; } /* ecma_new_collection */ /** * Deallocate a collection of ecma values without freeing it's values * * @return void */ void ecma_collection_destroy (ecma_collection_t *collection_p) /**< value collection */ { JERRY_ASSERT (collection_p != NULL); jmem_heap_free_block (collection_p->buffer_p, ECMA_COLLECTION_ALLOCATED_SIZE (collection_p->capacity)); jmem_heap_free_block (collection_p, sizeof (ecma_collection_t)); } /* ecma_collection_destroy */ /** * Free the object collection elements and deallocate the collection */ void ecma_collection_free_objects (ecma_collection_t *collection_p) /**< value collection */ { JERRY_ASSERT (collection_p != NULL); ecma_value_t *buffer_p = collection_p->buffer_p; for (uint32_t i = 0; i < collection_p->item_count; i++) { if (ecma_is_value_object (buffer_p[i])) { ecma_deref_object (ecma_get_object_from_value (buffer_p[i])); } } ecma_collection_destroy (collection_p); } /* ecma_collection_free_objects */ /** * Free the template literal objects and deallocate the collection */ void ecma_collection_free_template_literal (ecma_collection_t *collection_p) /**< value collection */ { for (uint32_t i = 0; i < collection_p->item_count; i++) { ecma_object_t *object_p = ecma_get_object_from_value (collection_p->buffer_p[i]); JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); ecma_extended_object_t *array_object_p = (ecma_extended_object_t *) object_p; JERRY_ASSERT (array_object_p->u.array.length_prop_and_hole_count & ECMA_ARRAY_TEMPLATE_LITERAL); array_object_p->u.array.length_prop_and_hole_count &= (uint32_t) ~ECMA_ARRAY_TEMPLATE_LITERAL; ecma_property_value_t *property_value_p; property_value_p = ecma_get_named_data_property (object_p, ecma_get_magic_string (LIT_MAGIC_STRING_RAW)); ecma_object_t *raw_object_p = ecma_get_object_from_value (property_value_p->value); JERRY_ASSERT (ecma_get_object_type (raw_object_p) == ECMA_OBJECT_TYPE_ARRAY); array_object_p = (ecma_extended_object_t *) raw_object_p; JERRY_ASSERT (array_object_p->u.array.length_prop_and_hole_count & ECMA_ARRAY_TEMPLATE_LITERAL); array_object_p->u.array.length_prop_and_hole_count &= (uint32_t) ~ECMA_ARRAY_TEMPLATE_LITERAL; ecma_deref_object (raw_object_p); ecma_deref_object (object_p); } ecma_collection_destroy (collection_p); } /* ecma_collection_free_template_literal */ /** * Free the non-object collection elements and deallocate the collection */ void ecma_collection_free_if_not_object (ecma_collection_t *collection_p) /**< value collection */ { JERRY_ASSERT (collection_p != NULL); ecma_value_t *buffer_p = collection_p->buffer_p; for (uint32_t i = 0; i < collection_p->item_count; i++) { ecma_free_value_if_not_object (buffer_p[i]); } ecma_collection_destroy (collection_p); } /* ecma_collection_free_if_not_object */ /** * Free the collection elements and deallocate the collection */ void ecma_collection_free (ecma_collection_t *collection_p) /**< value collection */ { JERRY_ASSERT (collection_p != NULL); ecma_value_t *buffer_p = collection_p->buffer_p; for (uint32_t i = 0; i < collection_p->item_count; i++) { ecma_free_value (buffer_p[i]); } ecma_collection_destroy (collection_p); } /* ecma_collection_free */ /** * Append new value to ecma values collection * * Note: The reference count of the values are not increased */ void ecma_collection_push_back (ecma_collection_t *collection_p, /**< value collection */ ecma_value_t value) /**< ecma value to append */ { JERRY_ASSERT (collection_p != NULL); ecma_value_t *buffer_p = collection_p->buffer_p; if (JERRY_LIKELY (collection_p->item_count < collection_p->capacity)) { buffer_p[collection_p->item_count++] = value; return; } const uint32_t new_capacity = collection_p->capacity + ECMA_COLLECTION_GROW_FACTOR; const uint32_t old_size = ECMA_COLLECTION_ALLOCATED_SIZE (collection_p->capacity); const uint32_t new_size = ECMA_COLLECTION_ALLOCATED_SIZE (new_capacity); buffer_p = (ecma_value_t *) jmem_heap_realloc_block (buffer_p, old_size, new_size); buffer_p[collection_p->item_count++] = value; collection_p->capacity = new_capacity; collection_p->buffer_p = buffer_p; } /* ecma_collection_push_back */ /** * Reserve space for the given amount of ecma_values in the collection */ void ecma_collection_reserve (ecma_collection_t *collection_p, /**< value collection */ uint32_t count) /**< number of ecma values to reserve */ { JERRY_ASSERT (collection_p != NULL); JERRY_ASSERT (UINT32_MAX - count > collection_p->capacity); const uint32_t new_capacity = collection_p->capacity + count; const uint32_t old_size = ECMA_COLLECTION_ALLOCATED_SIZE (collection_p->capacity); const uint32_t new_size = ECMA_COLLECTION_ALLOCATED_SIZE (new_capacity); ecma_value_t *buffer_p = collection_p->buffer_p; buffer_p = (ecma_value_t *) jmem_heap_realloc_block (buffer_p, old_size, new_size); collection_p->capacity = new_capacity; collection_p->buffer_p = buffer_p; } /* ecma_collection_reserve */ /** * Append a list of values to the end of the collection */ void ecma_collection_append (ecma_collection_t *collection_p, /**< value collection */ const ecma_value_t *buffer_p, /**< values to append */ uint32_t count) /**< number of ecma values to append */ { JERRY_ASSERT (collection_p != NULL); JERRY_ASSERT (collection_p->capacity >= collection_p->item_count); uint32_t free_count = collection_p->capacity - collection_p->item_count; if (free_count < count) { ecma_collection_reserve (collection_p, count - free_count); } memcpy (collection_p->buffer_p + collection_p->item_count, buffer_p, count * sizeof (ecma_value_t)); collection_p->item_count += count; } /* ecma_collection_append */ /** * Helper function to check if a given collection have duplicated properties or not * * @return true - if there are duplicated properties in the collection * false - otherwise */ bool ecma_collection_check_duplicated_entries (ecma_collection_t *collection_p) /**< prop name collection */ { if (collection_p->item_count == 0) { return false; } ecma_value_t *buffer_p = collection_p->buffer_p; for (uint32_t i = 0; i < collection_p->item_count - 1; i++) { ecma_string_t *current_name_p = ecma_get_prop_name_from_value (buffer_p[i]); for (uint32_t j = i + 1; j < collection_p->item_count; j++) { if (ecma_compare_ecma_strings (current_name_p, ecma_get_prop_name_from_value (buffer_p[j]))) { return true; } } } return false; } /* ecma_collection_check_duplicated_entries */ /** * Check the string value existence in the collection. * * Used by: * - ecma_builtin_json_stringify step 4.b.ii.5 * - ecma_op_object_enumerate * * @return true, if the string is already in the collection. */ bool ecma_collection_has_string_value (ecma_collection_t *collection_p, /**< collection */ ecma_string_t *string_p) /**< string */ { ecma_value_t *buffer_p = collection_p->buffer_p; for (uint32_t i = 0; i < collection_p->item_count; i++) { ecma_string_t *current_p = ecma_get_string_from_value (buffer_p[i]); if (ecma_compare_ecma_strings (current_p, string_p)) { return true; } } return false; } /* ecma_collection_has_string_value */ /** * Initial capacity of an ecma-collection */ #define ECMA_COMPACT_COLLECTION_GROWTH 8 /** * Set the size of the compact collection */ #define ECMA_COMPACT_COLLECTION_SET_SIZE(compact_collection_p, item_count, unused_items) \ ((compact_collection_p)[0] = (((item_count) << ECMA_COMPACT_COLLECTION_SIZE_SHIFT) | (unused_items))) /** * Set the size of the compact collection */ #define ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT(compact_collection_p) \ ((compact_collection_p)[0] & ((1 << ECMA_COMPACT_COLLECTION_SIZE_SHIFT) - 1)) /** * Allocate a compact collection of ecma values * * @return pointer to the compact collection */ ecma_value_t * ecma_new_compact_collection (void) { size_t size = (ECMA_COMPACT_COLLECTION_GROWTH / 2) * sizeof (ecma_value_t); ecma_value_t *compact_collection_p = (ecma_value_t *) jmem_heap_alloc_block (size); ECMA_COMPACT_COLLECTION_SET_SIZE (compact_collection_p, ECMA_COMPACT_COLLECTION_GROWTH / 2, (ECMA_COMPACT_COLLECTION_GROWTH / 2) - 1); return compact_collection_p; } /* ecma_new_compact_collection */ /** * Append a value to the compact collection * * @return updated pointer to the compact collection */ ecma_value_t * ecma_compact_collection_push_back (ecma_value_t *compact_collection_p, /**< compact collection */ ecma_value_t value) /**< ecma value to append */ { ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); ecma_value_t unused_items = ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT (compact_collection_p); if (unused_items > 0) { compact_collection_p[size - unused_items] = value; (*compact_collection_p)--; return compact_collection_p; } if (size == ECMA_COMPACT_COLLECTION_GROWTH / 2) { size_t old_size = (ECMA_COMPACT_COLLECTION_GROWTH / 2) * sizeof (ecma_value_t); size_t new_size = ECMA_COMPACT_COLLECTION_GROWTH * sizeof (ecma_value_t); compact_collection_p = (ecma_value_t *) jmem_heap_realloc_block (compact_collection_p, old_size, new_size); compact_collection_p[ECMA_COMPACT_COLLECTION_GROWTH / 2] = value; ECMA_COMPACT_COLLECTION_SET_SIZE (compact_collection_p, ECMA_COMPACT_COLLECTION_GROWTH, (ECMA_COMPACT_COLLECTION_GROWTH / 2) - 1); return compact_collection_p; } size_t old_size = size * sizeof (ecma_value_t); size_t new_size = old_size + (ECMA_COMPACT_COLLECTION_GROWTH * sizeof (ecma_value_t)); compact_collection_p = (ecma_value_t *) jmem_heap_realloc_block (compact_collection_p, old_size, new_size); compact_collection_p[size] = value; ECMA_COMPACT_COLLECTION_SET_SIZE (compact_collection_p, size + ECMA_COMPACT_COLLECTION_GROWTH, ECMA_COMPACT_COLLECTION_GROWTH - 1); return compact_collection_p; } /* ecma_compact_collection_push_back */ /** * Discard the unused elements of a compact collection * * Note: * further items should not be added after this call * * @return updated pointer to the compact collection */ ecma_value_t * ecma_compact_collection_shrink (ecma_value_t *compact_collection_p) /**< compact collection */ { ecma_value_t unused_items = ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT (compact_collection_p); if (unused_items == 0) { return compact_collection_p; } ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); size_t old_size = size * sizeof (ecma_value_t); size_t new_size = (size - unused_items) * sizeof (ecma_value_t); compact_collection_p = (ecma_value_t *) jmem_heap_realloc_block (compact_collection_p, old_size, new_size); ECMA_COMPACT_COLLECTION_SET_SIZE (compact_collection_p, size - unused_items, 0); return compact_collection_p; } /* ecma_compact_collection_shrink */ /** * Free a compact collection */ void ecma_compact_collection_free (ecma_value_t *compact_collection_p) /**< compact collection */ { ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); ecma_value_t unused_items = ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT (compact_collection_p); ecma_value_t *end_p = compact_collection_p + size - unused_items; ecma_value_t *current_p = compact_collection_p + 1; while (current_p < end_p) { ecma_free_value (*current_p++); } jmem_heap_free_block (compact_collection_p, size * sizeof (ecma_value_t)); } /* ecma_compact_collection_free */ /** * Get the end of a compact collection * * @return pointer to the compact collection end */ ecma_value_t * ecma_compact_collection_end (ecma_value_t *compact_collection_p) /**< compact collection */ { ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); ecma_value_t unused_items = ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT (compact_collection_p); return compact_collection_p + size - unused_items; } /* ecma_compact_collection_end */ /** * Destroy a compact collection */ void ecma_compact_collection_destroy (ecma_value_t *compact_collection_p) /**< compact collection */ { ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); jmem_heap_free_block (compact_collection_p, size * sizeof (ecma_value_t)); } /* ecma_compact_collection_destroy */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/filters.cpp000664 001750 001750 00000017601 15164251010 035341 0ustar00ddennedyddennedy000000 000000 // Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Spatial prediction using various filters // // Author: Urvang (urvang@google.com) #include "./dsp.h" #include #include #include //------------------------------------------------------------------------------ // Helpful macro. # define SANITY_CHECK(in, out) \ assert(in != NULL); \ assert(out != NULL); \ assert(width > 0); \ assert(height > 0); \ assert(stride >= width); \ assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ (void)height; // Silence unused warning. static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred, uint8_t* dst, int length, int inverse) { int i; if (inverse) { for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i]; } else { for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i]; } } //------------------------------------------------------------------------------ // Horizontal filter. static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, int width, int height, int stride, int row, int num_rows, int inverse, uint8_t* out) { const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); in += start_offset; out += start_offset; preds = inverse ? out : in; if (row == 0) { // Leftmost pixel is the same as input for topmost scanline. out[0] = in[0]; PredictLine(in + 1, preds, out + 1, width - 1, inverse); row = 1; preds += stride; in += stride; out += stride; } // Filter line-by-line. while (row < last_row) { // Leftmost pixel is predicted from above. PredictLine(in, preds - stride, out, 1, inverse); PredictLine(in + 1, preds, out + 1, width - 1, inverse); ++row; preds += stride; in += stride; out += stride; } } //------------------------------------------------------------------------------ // Vertical filter. static WEBP_INLINE void DoVerticalFilter(const uint8_t* in, int width, int height, int stride, int row, int num_rows, int inverse, uint8_t* out) { const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); in += start_offset; out += start_offset; preds = inverse ? out : in; if (row == 0) { // Very first top-left pixel is copied. out[0] = in[0]; // Rest of top scan-line is left-predicted. PredictLine(in + 1, preds, out + 1, width - 1, inverse); row = 1; in += stride; out += stride; } else { // We are starting from in-between. Make sure 'preds' points to prev row. preds -= stride; } // Filter line-by-line. while (row < last_row) { PredictLine(in, preds, out, width, inverse); ++row; preds += stride; in += stride; out += stride; } } //------------------------------------------------------------------------------ // Gradient filter. static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { const int g = a + b - c; return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit } static WEBP_INLINE void DoGradientFilter(const uint8_t* in, int width, int height, int stride, int row, int num_rows, int inverse, uint8_t* out) { const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); in += start_offset; out += start_offset; preds = inverse ? out : in; // left prediction for top scan-line if (row == 0) { out[0] = in[0]; PredictLine(in + 1, preds, out + 1, width - 1, inverse); row = 1; preds += stride; in += stride; out += stride; } // Filter line-by-line. while (row < last_row) { int w; // leftmost pixel: predict from above. PredictLine(in, preds - stride, out, 1, inverse); for (w = 1; w < width; ++w) { const int pred = GradientPredictor(preds[w - 1], preds[w - stride], preds[w - stride - 1]); out[w] = in[w] + (inverse ? pred : -pred); } ++row; preds += stride; in += stride; out += stride; } } #undef SANITY_CHECK //------------------------------------------------------------------------------ static void HorizontalFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { DoHorizontalFilter(data, width, height, stride, 0, height, 0, filtered_data); } static void VerticalFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { DoVerticalFilter(data, width, height, stride, 0, height, 0, filtered_data); } static void GradientFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { DoGradientFilter(data, width, height, stride, 0, height, 0, filtered_data); } //------------------------------------------------------------------------------ static void VerticalUnfilter(int width, int height, int stride, int row, int num_rows, uint8_t* data) { DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data); } static void HorizontalUnfilter(int width, int height, int stride, int row, int num_rows, uint8_t* data) { DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data); } static void GradientUnfilter(int width, int height, int stride, int row, int num_rows, uint8_t* data) { DoGradientFilter(data, width, height, stride, row, num_rows, 1, data); } //------------------------------------------------------------------------------ // Init function WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; extern void VP8FiltersInitMIPSdspR2(void); extern void VP8FiltersInitSSE2(void); static volatile VP8CPUInfo filters_last_cpuinfo_used = (VP8CPUInfo)&filters_last_cpuinfo_used; WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInit(void) { if (filters_last_cpuinfo_used == VP8GetCPUInfo) return; WebPUnfilters[WEBP_FILTER_NONE] = NULL; WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter; WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter; WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter; WebPFilters[WEBP_FILTER_NONE] = NULL; WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter; WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter; WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter; if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) if (VP8GetCPUInfo(kSSE2)) { VP8FiltersInitSSE2(); } #endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { VP8FiltersInitMIPSdspR2(); } #endif } filters_last_cpuinfo_used = VP8GetCPUInfo; } src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.inc.h000664 001750 001750 00000010246 15164251010 051775 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Array.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ARRAY /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.4.4.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_ARRAY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 22.1.3.31 */ OBJECT_VALUE (LIT_GLOBAL_SYMBOL_UNSCOPABLES, ECMA_BUILTIN_ID_ARRAY_PROTOTYPE_UNSCOPABLES, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Number properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.4.4 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 0, ECMA_PROPERTY_FLAG_WRITABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_LOCALE_STRING_UL, ECMA_ARRAY_PROTOTYPE_TO_LOCALE_STRING, 0, 0) ROUTINE (LIT_MAGIC_STRING_CONCAT, ECMA_ARRAY_PROTOTYPE_CONCAT, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_JOIN, ECMA_ARRAY_PROTOTYPE_JOIN, 1, 1) ROUTINE (LIT_MAGIC_STRING_POP, ECMA_ARRAY_PROTOTYPE_POP, 0, 0) ROUTINE (LIT_MAGIC_STRING_PUSH, ECMA_ARRAY_PROTOTYPE_PUSH, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_REVERSE, ECMA_ARRAY_PROTOTYPE_REVERSE, 0, 0) ROUTINE (LIT_MAGIC_STRING_SHIFT, ECMA_ARRAY_PROTOTYPE_SHIFT, 0, 0) ROUTINE (LIT_MAGIC_STRING_SLICE, ECMA_ARRAY_PROTOTYPE_SLICE, 2, 2) ROUTINE (LIT_MAGIC_STRING_SORT, ECMA_ARRAY_PROTOTYPE_SORT, 1, 1) ROUTINE (LIT_MAGIC_STRING_AT, ECMA_ARRAY_PROTOTYPE_AT, 1, 1) ROUTINE (LIT_MAGIC_STRING_SPLICE, ECMA_ARRAY_PROTOTYPE_SPLICE, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_UNSHIFT, ECMA_ARRAY_PROTOTYPE_UNSHIFT, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_INDEX_OF_UL, ECMA_ARRAY_PROTOTYPE_INDEX_OF, 2, 1) ROUTINE (LIT_MAGIC_STRING_LAST_INDEX_OF_UL, ECMA_ARRAY_PROTOTYPE_LAST_INDEX_OF, NON_FIXED, 1) /* Note these 3 routines must be in this order */ ROUTINE (LIT_MAGIC_STRING_EVERY, ECMA_ARRAY_PROTOTYPE_EVERY, 2, 1) ROUTINE (LIT_MAGIC_STRING_SOME, ECMA_ARRAY_PROTOTYPE_SOME, 2, 1) ROUTINE (LIT_MAGIC_STRING_FOR_EACH_UL, ECMA_ARRAY_PROTOTYPE_FOR_EACH, 2, 1) ROUTINE (LIT_MAGIC_STRING_MAP, ECMA_ARRAY_PROTOTYPE_MAP, 2, 1) ROUTINE (LIT_MAGIC_STRING_FILTER, ECMA_ARRAY_PROTOTYPE_FILTER, 2, 1) /* Note these 2 routines must be in this order */ ROUTINE (LIT_MAGIC_STRING_REDUCE, ECMA_ARRAY_PROTOTYPE_REDUCE, 2, 1) ROUTINE (LIT_MAGIC_STRING_REDUCE_RIGHT_UL, ECMA_ARRAY_PROTOTYPE_REDUCE_RIGHT, 2, 1) ROUTINE (LIT_MAGIC_STRING_FIND, ECMA_ARRAY_PROTOTYPE_FIND, 2, 1) ROUTINE (LIT_MAGIC_STRING_FIND_INDEX, ECMA_ARRAY_PROTOTYPE_FIND_INDEX, 2, 1) ROUTINE (LIT_MAGIC_STRING_FILL, ECMA_ARRAY_PROTOTYPE_FILL, 3, 1) ROUTINE (LIT_MAGIC_STRING_COPY_WITHIN, ECMA_ARRAY_PROTOTYPE_COPY_WITHIN, NON_FIXED, 2) ROUTINE (LIT_MAGIC_STRING_ENTRIES, ECMA_ARRAY_PROTOTYPE_ENTRIES, 0, 0) ROUTINE (LIT_MAGIC_STRING_KEYS, ECMA_ARRAY_PROTOTYPE_KEYS, 0, 0) ROUTINE (LIT_MAGIC_STRING_INCLUDES, ECMA_ARRAY_PROTOTYPE_INCLUDES, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_FLAT, ECMA_ARRAY_PROTOTYPE_FLAT, 1, 0) ROUTINE (LIT_MAGIC_STRING_FLATMAP, ECMA_ARRAY_PROTOTYPE_FLATMAP, 2, 1) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TO_STRING_UL, LIT_MAGIC_STRING_TO_STRING_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_VALUES, LIT_INTERNAL_MAGIC_STRING_ARRAY_PROTOTYPE_VALUES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) INTRINSIC_PROPERTY (LIT_GLOBAL_SYMBOL_ITERATOR, LIT_INTERNAL_MAGIC_STRING_ARRAY_PROTOTYPE_VALUES, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ARRAY */ #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-comparison.cpp000664 001750 001750 00000033237 15164251010 046200 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-comparison.h" #include "ecma-bigint.h" #include "ecma-conversion.h" #include "ecma-globals.h" #include "ecma-objects.h" #include "jrt.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmacomparison ECMA comparison * @{ */ /** * ECMA abstract equality comparison routine. * * See also: ECMA-262 v5, 11.9.3 * * Note: * This function might raise an exception, so the * returned value must be freed with ecma_free_value. * * @return true - if values are equal, * false - otherwise * error - in case of any problems */ ecma_value_t ecma_op_abstract_equality_compare (ecma_value_t x, /**< first operand */ ecma_value_t y) /**< second operand */ { if (x == y) { return ECMA_VALUE_TRUE; } if (ecma_are_values_integer_numbers (x, y)) { /* Note: the (x == y) comparison captures the true case. */ return ECMA_VALUE_FALSE; } if (ecma_is_value_number (x)) { if (ecma_is_value_number (y)) { /* 1.c */ ecma_number_t x_num = ecma_get_number_from_value (x); ecma_number_t y_num = ecma_get_number_from_value (y); bool is_x_equal_to_y = (x_num == y_num); #ifndef JERRY_NDEBUG bool is_x_equal_to_y_check; if (ecma_number_is_nan (x_num) || ecma_number_is_nan (y_num)) { is_x_equal_to_y_check = false; } else if (x_num == y_num || (ecma_number_is_zero (x_num) && ecma_number_is_zero (y_num))) { is_x_equal_to_y_check = true; } else { is_x_equal_to_y_check = false; } JERRY_ASSERT (is_x_equal_to_y == is_x_equal_to_y_check); #endif /* !JERRY_NDEBUG */ return ecma_make_boolean_value (is_x_equal_to_y); } /* Swap values. */ x ^= y; y ^= x; x ^= y; } if (ecma_is_value_string (x)) { if (ecma_is_value_string (y)) { /* 1., d. */ ecma_string_t *x_str_p = ecma_get_string_from_value (x); ecma_string_t *y_str_p = ecma_get_string_from_value (y); bool is_equal = ecma_compare_ecma_strings (x_str_p, y_str_p); return ecma_make_boolean_value (is_equal); } if (ecma_is_value_number (y)) { /* 4. */ ecma_number_t num; ecma_value_t x_num_value = ecma_op_to_number (x, &num); if (ECMA_IS_VALUE_ERROR (x_num_value)) { return x_num_value; } ecma_value_t num_value = ecma_make_number_value (num); ecma_value_t compare_result = ecma_op_abstract_equality_compare (num_value, y); ecma_free_value (num_value); return compare_result; } /* Swap values. */ x ^= y; y ^= x; x ^= y; } if (ecma_is_value_boolean (y)) { if (ecma_is_value_boolean (x)) { /* 1., e. */ /* Note: the (x == y) comparison captures the true case. */ return ECMA_VALUE_FALSE; } /* 7. */ return ecma_op_abstract_equality_compare (x, ecma_make_integer_value (ecma_is_value_true (y) ? 1 : 0)); } if (ecma_is_value_boolean (x)) { /* 6. */ return ecma_op_abstract_equality_compare (ecma_make_integer_value (ecma_is_value_true (x) ? 1 : 0), y); } #if JERRY_BUILTIN_BIGINT if (JERRY_UNLIKELY (ecma_is_value_bigint (x))) { if (ecma_is_value_bigint (y)) { return ecma_make_boolean_value (ecma_bigint_is_equal_to_bigint (x, y)); } if (ecma_is_value_string (y)) { ecma_value_t bigint = ecma_bigint_parse_string_value (y, ECMA_BIGINT_PARSE_DISALLOW_SYNTAX_ERROR); if (ECMA_IS_VALUE_ERROR (bigint) || bigint == ECMA_VALUE_FALSE) { return bigint; } JERRY_ASSERT (ecma_is_value_bigint (bigint)); ecma_value_t result = ecma_make_boolean_value (ecma_bigint_is_equal_to_bigint (x, bigint)); ecma_free_value (bigint); return result; } if (ecma_is_value_number (y)) { return ecma_make_boolean_value (ecma_bigint_is_equal_to_number (x, ecma_get_number_from_value (y))); } /* Swap values. */ x ^= y; y ^= x; x ^= y; } #endif /* JERRY_BUILTIN_BIGINT */ if (ecma_is_value_undefined (x) || ecma_is_value_null (x)) { /* 1. a., b. */ /* 2., 3. */ bool is_equal = ecma_is_value_undefined (y) || ecma_is_value_null (y); return ecma_make_boolean_value (is_equal); } if (JERRY_UNLIKELY (ecma_is_value_symbol (x))) { if (!ecma_is_value_object (y)) { return ECMA_VALUE_FALSE; } /* Swap values. */ x ^= y; y ^= x; x ^= y; } JERRY_ASSERT (ecma_is_value_object (x)); if (ecma_is_value_string (y) || ecma_is_value_symbol (y) #if JERRY_BUILTIN_BIGINT || ecma_is_value_bigint (y) #endif /* JERRY_BUILTIN_BIGINT */ || ecma_is_value_number (y)) { /* 9. */ ecma_object_t *obj_p = ecma_get_object_from_value (x); ecma_value_t def_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NO); if (ECMA_IS_VALUE_ERROR (def_value)) { return def_value; } ecma_value_t compare_result = ecma_op_abstract_equality_compare (def_value, y); ecma_free_value (def_value); return compare_result; } return ECMA_VALUE_FALSE; } /* ecma_op_abstract_equality_compare */ /** * ECMA strict equality comparison routine. * * See also: ECMA-262 v5, 11.9.6 * * @return true - if values are strict equal, * false - otherwise */ bool ecma_op_strict_equality_compare (ecma_value_t x, /**< first operand */ ecma_value_t y) /**< second operand */ { if (ecma_is_value_direct (x) || ecma_is_value_direct (y) || ecma_is_value_symbol (x) || ecma_is_value_symbol (y) || ecma_is_value_object (x) || ecma_is_value_object (y)) { JERRY_ASSERT (!ecma_is_value_direct (x) || ecma_is_value_undefined (x) || ecma_is_value_null (x) || ecma_is_value_boolean (x) || ecma_is_value_integer_number (x)); JERRY_ASSERT (!ecma_is_value_direct (y) || ecma_is_value_undefined (y) || ecma_is_value_null (y) || ecma_is_value_boolean (y) || ecma_is_value_integer_number (y)); if ((x != ecma_make_integer_value (0) || !ecma_is_value_float_number (y)) && (y != ecma_make_integer_value (0) || !ecma_is_value_float_number (x))) { return (x == y); } /* The +0 === -0 case handled below. */ } JERRY_ASSERT (ecma_is_value_number (x) || ecma_is_value_string (x) || ecma_is_value_bigint (x)); JERRY_ASSERT (ecma_is_value_number (y) || ecma_is_value_string (y) || ecma_is_value_bigint (y)); if (ecma_is_value_string (x)) { if (!ecma_is_value_string (y)) { return false; } ecma_string_t *x_str_p = ecma_get_string_from_value (x); ecma_string_t *y_str_p = ecma_get_string_from_value (y); return ecma_compare_ecma_strings (x_str_p, y_str_p); } #if JERRY_BUILTIN_BIGINT if (JERRY_UNLIKELY (ecma_is_value_bigint (x))) { if (!ecma_is_value_bigint (y)) { return false; } return ecma_bigint_is_equal_to_bigint (x, y); } #endif /* JERRY_BUILTIN_BIGINT */ if (!ecma_is_value_number (y)) { return false; } ecma_number_t x_num = ecma_get_number_from_value (x); ecma_number_t y_num = ecma_get_number_from_value (y); bool is_x_equal_to_y = (x_num == y_num); #ifndef JERRY_NDEBUG bool is_x_equal_to_y_check; if (ecma_number_is_nan (x_num) || ecma_number_is_nan (y_num)) { is_x_equal_to_y_check = false; } else if (x_num == y_num || (ecma_number_is_zero (x_num) && ecma_number_is_zero (y_num))) { is_x_equal_to_y_check = true; } else { is_x_equal_to_y_check = false; } JERRY_ASSERT (is_x_equal_to_y == is_x_equal_to_y_check); #endif /* !JERRY_NDEBUG */ return is_x_equal_to_y; } /* ecma_op_strict_equality_compare */ /** * ECMA abstract relational comparison routine. * * See also: ECMA-262 v5, 11.8.5 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_abstract_relational_compare (ecma_value_t x, /**< first operand */ ecma_value_t y, /**< second operand */ bool left_first) /**< 'LeftFirst' flag */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; /* 1., 2. */ ecma_value_t prim_first_converted_value = ecma_op_to_primitive (x, ECMA_PREFERRED_TYPE_NUMBER); if (ECMA_IS_VALUE_ERROR (prim_first_converted_value)) { return prim_first_converted_value; } ecma_value_t prim_second_converted_value = ecma_op_to_primitive (y, ECMA_PREFERRED_TYPE_NUMBER); if (ECMA_IS_VALUE_ERROR (prim_second_converted_value)) { ecma_free_value (prim_first_converted_value); return prim_second_converted_value; } ecma_value_t px = left_first ? prim_first_converted_value : prim_second_converted_value; ecma_value_t py = left_first ? prim_second_converted_value : prim_first_converted_value; const bool is_px_string = ecma_is_value_string (px); const bool is_py_string = ecma_is_value_string (py); if (!(is_px_string && is_py_string)) { #if JERRY_BUILTIN_BIGINT if (JERRY_LIKELY (!ecma_is_value_bigint (px)) && JERRY_LIKELY (!ecma_is_value_bigint (py))) { #endif /* JERRY_BUILTIN_BIGINT */ /* 3. */ /* a. */ ecma_number_t nx; ecma_number_t ny; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (px, &nx)) || ECMA_IS_VALUE_ERROR (ecma_op_to_number (py, &ny))) { ret_value = ECMA_VALUE_ERROR; goto end; } /* b. */ if (ecma_number_is_nan (nx) || ecma_number_is_nan (ny)) { /* c., d. */ ret_value = ECMA_VALUE_UNDEFINED; } else { bool is_x_less_than_y = (nx < ny); #ifndef JERRY_NDEBUG bool is_x_less_than_y_check; if (nx == ny || (ecma_number_is_zero (nx) && ecma_number_is_zero (ny))) { /* e., f., g. */ is_x_less_than_y_check = false; } else if (ecma_number_is_infinity (nx) && !ecma_number_is_negative (nx)) { /* h. */ is_x_less_than_y_check = false; } else if (ecma_number_is_infinity (ny) && !ecma_number_is_negative (ny)) { /* i. */ is_x_less_than_y_check = true; } else if (ecma_number_is_infinity (ny) && ecma_number_is_negative (ny)) { /* j. */ is_x_less_than_y_check = false; } else if (ecma_number_is_infinity (nx) && ecma_number_is_negative (nx)) { /* k. */ is_x_less_than_y_check = true; } else { /* l. */ JERRY_ASSERT (!ecma_number_is_nan (nx) && !ecma_number_is_infinity (nx)); JERRY_ASSERT (!ecma_number_is_nan (ny) && !ecma_number_is_infinity (ny)); JERRY_ASSERT (!(ecma_number_is_zero (nx) && ecma_number_is_zero (ny))); if (nx < ny) { is_x_less_than_y_check = true; } else { is_x_less_than_y_check = false; } } JERRY_ASSERT (is_x_less_than_y_check == is_x_less_than_y); #endif /* !JERRY_NDEBUG */ ret_value = ecma_make_boolean_value (is_x_less_than_y); } #if JERRY_BUILTIN_BIGINT } else { bool invert_result = false; int compare_result = 0; if (!ecma_is_value_bigint (px)) { ecma_value_t tmp = px; px = py; py = tmp; invert_result = true; } JERRY_ASSERT (ecma_is_value_bigint (px)); if (ecma_is_value_bigint (py)) { compare_result = ecma_bigint_compare_to_bigint (px, py); } else if (ecma_is_value_string (py)) { ret_value = ecma_bigint_parse_string_value (py, ECMA_BIGINT_PARSE_DISALLOW_SYNTAX_ERROR); if (!ECMA_IS_VALUE_ERROR (ret_value)) { if (ret_value == ECMA_VALUE_FALSE) { ret_value = ECMA_VALUE_UNDEFINED; } else { compare_result = ecma_bigint_compare_to_bigint (px, ret_value); ecma_free_value (ret_value); ret_value = ECMA_VALUE_EMPTY; } } } else { ecma_number_t ny; if (ECMA_IS_VALUE_ERROR (ecma_op_to_number (py, &ny))) { ret_value = ECMA_VALUE_ERROR; goto end; } if (ecma_number_is_nan (ny)) { ret_value = ECMA_VALUE_UNDEFINED; } else { compare_result = ecma_bigint_compare_to_number (px, ny); } } if (ret_value == ECMA_VALUE_EMPTY) { if (invert_result) { compare_result = -compare_result; } ret_value = ecma_make_boolean_value (compare_result < 0); } } #endif /* JERRY_BUILTIN_BIGINT */ } else { /* 4. */ JERRY_ASSERT (is_px_string && is_py_string); ecma_string_t *str_x_p = ecma_get_string_from_value (px); ecma_string_t *str_y_p = ecma_get_string_from_value (py); bool is_px_less = ecma_compare_ecma_strings_relational (str_x_p, str_y_p); ret_value = ecma_make_boolean_value (is_px_less); } end: ecma_free_value (prim_second_converted_value); ecma_free_value (prim_first_converted_value); return ret_value; } /* ecma_op_abstract_relational_compare */ /** * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-bigint-object.cpp000664 001750 001750 00000003544 15164251010 046544 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-bigint-object.h" #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #if JERRY_BUILTIN_BIGINT /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabigintobject ECMA BigInt object related routines * @{ */ /** * BigInt object creation operation. * * See also: ECMA-262 v11, 7.1.18 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_bigint_object (ecma_value_t arg) /**< argument passed to the toObject operation */ { JERRY_ASSERT (ecma_is_value_bigint (arg)); ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_BIGINT_PROTOTYPE); ecma_object_t *object_p = ecma_create_object (prototype_obj_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_BIGINT; ext_object_p->u.cls.u3.value = ecma_copy_value (arg); return ecma_make_object_value (object_p); } /* ecma_op_create_bigint_object */ /** * @} * @} */ #endif /* JERRY_BUILTIN_BIGINT */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/000775 001750 001750 00000000000 15164251010 043321 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimateglaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jmem/jmem-allocator.cpp000664 001750 001750 00000004402 15164251010 043661 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /** * Allocator implementation */ #include "ecma-globals.h" #include "jcontext.h" #include "jmem.h" #include "jrt-libc-includes.h" #define JMEM_ALLOCATOR_INTERNAL #include "jmem-allocator-internal.h" /** * Initialize memory allocators. */ void jmem_init (void) { jmem_heap_init (); } /* jmem_init */ /** * Finalize memory allocators. */ void jmem_finalize (void) { jmem_pools_finalize (); jmem_heap_finalize (); } /* jmem_finalize */ /** * Compress pointer * * @return packed pointer */ jmem_cpointer_t JERRY_ATTR_PURE jmem_compress_pointer (const void *pointer_p) /**< pointer to compress */ { JERRY_ASSERT (pointer_p != NULL); JERRY_ASSERT (jmem_is_heap_pointer (pointer_p)); uintptr_t uint_ptr = (uintptr_t) pointer_p; JERRY_ASSERT (uint_ptr % JMEM_ALIGNMENT == 0); const uintptr_t heap_start = (uintptr_t) &JERRY_HEAP_CONTEXT (first); uint_ptr -= heap_start; uint_ptr >>= JMEM_ALIGNMENT_LOG; JERRY_ASSERT (uint_ptr <= UINT16_MAX); JERRY_ASSERT (uint_ptr != JMEM_CP_NULL); return (jmem_cpointer_t) uint_ptr; } /* jmem_compress_pointer */ /** * Decompress pointer * * @return unpacked pointer */ void *JERRY_ATTR_PURE jmem_decompress_pointer (uintptr_t compressed_pointer) /**< pointer to decompress */ { JERRY_ASSERT (compressed_pointer != JMEM_CP_NULL); uintptr_t uint_ptr = compressed_pointer; JERRY_ASSERT (((jmem_cpointer_t) uint_ptr) == uint_ptr); const uintptr_t heap_start = (uintptr_t) &JERRY_HEAP_CONTEXT (first); uint_ptr <<= JMEM_ALIGNMENT_LOG; uint_ptr += heap_start; JERRY_ASSERT (jmem_is_heap_pointer ((void *) uint_ptr)); return (void *) uint_ptr; } /* jmem_decompress_pointer */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/testScene.cpp000664 001750 001750 00000015476 15164251010 032652 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2021 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "config.h" #include "catch.hpp" using namespace tvg; using namespace std; TEST_CASE("Scene Creation", "[tvgScene]") { auto scene = Scene::gen(); REQUIRE(scene); REQUIRE(scene->type() == Type::Scene); Paint::rel(scene); } TEST_CASE("Pushing Paints Into Scene", "[tvgScene]") { auto scene = Scene::gen(); REQUIRE(scene); REQUIRE(scene->parent() == nullptr); Paint* paints[3]; //Pushing Paints paints[0] = Shape::gen(); paints[0]->ref(); REQUIRE(paints[0]->parent() == nullptr); REQUIRE(scene->add(paints[0]) == Result::Success); REQUIRE(paints[0]->parent() == scene); paints[1] = Picture::gen(); paints[1]->ref(); REQUIRE(paints[1]->parent() == nullptr); REQUIRE(scene->add(paints[1]) == Result::Success); REQUIRE(paints[1]->parent() == scene); paints[2] = Picture::gen(); paints[2]->ref(); REQUIRE(paints[2]->parent() == nullptr); REQUIRE(scene->add(paints[2]) == Result::Success); REQUIRE(paints[2]->parent() == scene); //Pushing Null Pointer REQUIRE(scene->add(nullptr) == Result::InvalidArguments); //Pushing Invalid Paint REQUIRE(scene->add(nullptr) == Result::InvalidArguments); REQUIRE(scene->remove(paints[0]) == Result::Success); REQUIRE(scene->remove(paints[0]) == Result::InsufficientCondition); REQUIRE(scene->add(paints[0], paints[1]) == Result::Success); REQUIRE(scene->remove(paints[1]) == Result::Success); REQUIRE(scene->remove(paints[1]) == Result::InsufficientCondition); REQUIRE(scene->remove(paints[2]) == Result::Success); REQUIRE(scene->remove(paints[0]) == Result::Success); paints[0]->unref(); paints[1]->unref(); paints[2]->unref(); Paint::rel(scene); } TEST_CASE("Scene Clear", "[tvgScene]") { auto scene = Scene::gen(); REQUIRE(scene); REQUIRE(scene->add(Shape::gen()) == Result::Success); REQUIRE(scene->remove() == Result::Success); Paint::rel(scene); } TEST_CASE("Scene Clear And Reuse Shape", "[tvgScene]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); uint32_t buffer[100*100]; canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888); auto scene = Scene::gen(); REQUIRE(scene); auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->ref() == 1); REQUIRE(scene->add(shape) == Result::Success); REQUIRE(canvas->add(scene) == Result::Success); REQUIRE(canvas->update() == Result::Success); //No deallocate shape. REQUIRE(scene->remove() == Result::Success); //Reuse shape. REQUIRE(scene->add(shape) == Result::Success); REQUIRE(shape->unref() == 1); //The scene still holds 1. } REQUIRE(Initializer::term() == Result::Success); } TEST_CASE("Scene Effects", "[tvgScene]") { REQUIRE(Initializer::init() == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); REQUIRE(canvas); uint32_t buffer[100*100]; REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ARGB8888) == Result::Success); auto shape = Shape::gen(); REQUIRE(shape); REQUIRE(shape->appendCircle(50, 50, 30, 30) == Result::Success); REQUIRE(shape->fill(0, 255, 0, 255) == Result::Success); auto scene = Scene::gen(); REQUIRE(scene); REQUIRE(scene->add(shape) == Result::Success); auto picture = tvg::Picture::gen(); picture->load(TEST_DIR"/tiger.svg"); scene->add(picture); REQUIRE(canvas->add(scene) == Result::Success); REQUIRE(scene->add(SceneEffect::Clear) == Result::Success); REQUIRE(scene->add(SceneEffect::GaussianBlur, 1.5, 0, 0, 75) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); REQUIRE(scene->add(SceneEffect::Clear) == Result::Success); REQUIRE(scene->add(SceneEffect::DropShadow, 128, 128, 128, 200, 45.0, 5.0, 2.0, 60) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); REQUIRE(scene->add(SceneEffect::Clear) == Result::Success); REQUIRE(scene->add(SceneEffect::Fill, 255, 0, 0, 128) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); REQUIRE(scene->add(SceneEffect::Clear) == Result::Success); REQUIRE(scene->add(SceneEffect::Tint, 0, 0, 0, 255, 255, 255, 50.0) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); REQUIRE(scene->add(SceneEffect::Clear) == Result::Success); REQUIRE(scene->add(SceneEffect::Tritone, 0, 0, 0, 128, 128, 128, 255, 255, 255, 128) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); REQUIRE(scene->add(SceneEffect::GaussianBlur, 1.5, 0, 0, 75) == Result::Success); REQUIRE(scene->add(SceneEffect::DropShadow, 128, 128, 128, 200, 45.0, 5.0, 2.0, 60) == Result::Success); REQUIRE(canvas->add(scene->duplicate()) == Result::Success); REQUIRE(canvas->update() == Result::Success); REQUIRE(canvas->draw() == Result::Success); REQUIRE(canvas->sync() == Result::Success); } REQUIRE(Initializer::term() == Result::Success); }src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlGpuBuffer.cpp000664 001750 001750 00000012217 15164251010 037062 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlGpuBuffer.h" #include #include /************************************************************************/ /* GlGpuBuffer Implementation */ /************************************************************************/ static GLint _getGpuBufferAlign() { static GLint offset = 0; if (!offset) { GL_CHECK(glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offset)); } return offset; } void GlGpuBuffer::updateBufferData(Target target, uint32_t size, const void* data) { GL_CHECK(glBufferData(static_cast(target), size, data, GL_STATIC_DRAW)); } void GlGpuBuffer::bind(Target target) { GL_CHECK(glBindBuffer(static_cast(target), mGlBufferId)); } void GlGpuBuffer::unbind(Target target) { GL_CHECK(glBindBuffer(static_cast(target), 0)); } GlGpuBuffer::GlGpuBuffer() { GL_CHECK(glGenBuffers(1, &mGlBufferId)); assert(mGlBufferId != 0); } GlGpuBuffer::~GlGpuBuffer() { if (mGlBufferId) { GL_CHECK(glDeleteBuffers(1, &mGlBufferId)); } } /************************************************************************/ /* GlStageBuffer Implementation */ /************************************************************************/ GlStageBuffer::GlStageBuffer() : mVao(0), mGpuBuffer(), mGpuIndexBuffer() { GL_CHECK(glGenVertexArrays(1, &mVao)); } GlStageBuffer::~GlStageBuffer() { if (mVao) { glDeleteVertexArrays(1, &mVao); mVao = 0; } } uint32_t GlStageBuffer::push(void *data, uint32_t size, bool alignGpuOffset) { if (alignGpuOffset) alignOffset(size); uint32_t offset = mStageBuffer.count; if (this->mStageBuffer.reserved - this->mStageBuffer.count < size) { this->mStageBuffer.grow(max(size, this->mStageBuffer.reserved)); } memcpy(this->mStageBuffer.data + offset, data, size); this->mStageBuffer.count += size; return offset; } uint32_t GlStageBuffer::pushIndex(void *data, uint32_t size) { uint32_t offset = mIndexBuffer.count; if (this->mIndexBuffer.reserved - this->mIndexBuffer.count < size) { this->mIndexBuffer.grow(max(size, this->mIndexBuffer.reserved)); } memcpy(this->mIndexBuffer.data + offset, data, size); this->mIndexBuffer.count += size; return offset; } bool GlStageBuffer::flushToGPU() { if (mStageBuffer.empty() || mIndexBuffer.empty()) { mStageBuffer.clear(); mIndexBuffer.clear(); return false; } mGpuBuffer.bind(GlGpuBuffer::Target::ARRAY_BUFFER); mGpuBuffer.updateBufferData(GlGpuBuffer::Target::ARRAY_BUFFER, mStageBuffer.count, mStageBuffer.data); mGpuBuffer.unbind(GlGpuBuffer::Target::ARRAY_BUFFER); mGpuIndexBuffer.bind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER); mGpuIndexBuffer.updateBufferData(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER, mIndexBuffer.count, mIndexBuffer.data); mGpuIndexBuffer.unbind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER); mStageBuffer.clear(); mIndexBuffer.clear(); return true; } void GlStageBuffer::bind() { glBindVertexArray(mVao); mGpuBuffer.bind(GlGpuBuffer::Target::ARRAY_BUFFER); mGpuBuffer.bind(GlGpuBuffer::Target::UNIFORM_BUFFER); mGpuIndexBuffer.bind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER); } void GlStageBuffer::unbind() { glBindVertexArray(0); mGpuBuffer.unbind(GlGpuBuffer::Target::ARRAY_BUFFER); mGpuBuffer.unbind(GlGpuBuffer::Target::UNIFORM_BUFFER); mGpuIndexBuffer.unbind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER); } GLuint GlStageBuffer::getBufferId() { return mGpuBuffer.getBufferId(); } void GlStageBuffer::alignOffset(uint32_t size) { uint32_t alignment = _getGpuBufferAlign(); if (mStageBuffer.count % alignment == 0) return; uint32_t offset = alignment - mStageBuffer.count % alignment; if (mStageBuffer.count + offset + size > mStageBuffer.reserved) { mStageBuffer.grow(max(offset + size, mStageBuffer.reserved)); } mStageBuffer.count += offset; } mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/vp8l.cpp000664 001750 001750 00000151300 15164251010 034522 0ustar00ddennedyddennedy000000 000000 // Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // main entry for the decoder // // Authors: Vikas Arora (vikaas.arora@gmail.com) // Jyrki Alakuijala (jyrki@google.com) #include "tvgCommon.h" #include "./alphai.h" #include "./vp8li.h" #include "../dsp/dsp.h" #include "../dsp/lossless.h" #include "../dsp/yuv.h" #include "../utils/huffman.h" #include "../utils/utils.h" #define NUM_ARGB_CACHE_ROWS 16 static const int kCodeLengthLiterals = 16; static const int kCodeLengthRepeatCode = 16; static const int kCodeLengthExtraBits[3] = { 2, 3, 7 }; static const int kCodeLengthRepeatOffsets[3] = { 3, 3, 11 }; // ----------------------------------------------------------------------------- // Five Huffman codes are used at each meta code: // 1. green + length prefix codes + color cache codes, // 2. alpha, // 3. red, // 4. blue, and, // 5. distance prefix codes. typedef enum { GREEN = 0, RED = 1, BLUE = 2, ALPHA = 3, DIST = 4 } HuffIndex; static const uint16_t kAlphabetSize[HUFFMAN_CODES_PER_META_CODE] = { NUM_LITERAL_CODES + NUM_LENGTH_CODES, NUM_LITERAL_CODES, NUM_LITERAL_CODES, NUM_LITERAL_CODES, NUM_DISTANCE_CODES }; static const uint8_t kLiteralMap[HUFFMAN_CODES_PER_META_CODE] = { 0, 1, 1, 1, 0 }; #define NUM_CODE_LENGTH_CODES 19 static const uint8_t kCodeLengthCodeOrder[NUM_CODE_LENGTH_CODES] = { 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; #define CODE_TO_PLANE_CODES 120 static const uint8_t kCodeToPlane[CODE_TO_PLANE_CODES] = { 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a, 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a, 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b, 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03, 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c, 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e, 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b, 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f, 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b, 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41, 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f, 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70 }; // Memory needed for lookup tables of one Huffman tree group. Red, blue, alpha // and distance alphabets are constant (256 for red, blue and alpha, 40 for // distance) and lookup table sizes for them in worst case are 630 and 410 // respectively. Size of green alphabet depends on color cache size and is equal // to 256 (green component values) + 24 (length prefix values) // + color_cache_size (between 0 and 2048). // All values computed for 8-bit first level lookup with Mark Adler's tool: // http://www.hdfgroup.org/ftp/lib-external/zlib/zlib-1.2.5/examples/enough.c #define FIXED_TABLE_SIZE (630 * 3 + 410) static const int kTableSize[12] = { FIXED_TABLE_SIZE + 654, FIXED_TABLE_SIZE + 656, FIXED_TABLE_SIZE + 658, FIXED_TABLE_SIZE + 662, FIXED_TABLE_SIZE + 670, FIXED_TABLE_SIZE + 686, FIXED_TABLE_SIZE + 718, FIXED_TABLE_SIZE + 782, FIXED_TABLE_SIZE + 912, FIXED_TABLE_SIZE + 1168, FIXED_TABLE_SIZE + 1680, FIXED_TABLE_SIZE + 2704 }; static int DecodeImageStream(int xsize, int ysize, int is_level0, VP8LDecoder* const dec, uint32_t** const decoded_data); //------------------------------------------------------------------------------ int VP8LCheckSignature(const uint8_t* const data, size_t size) { return (size >= VP8L_FRAME_HEADER_SIZE && data[0] == VP8L_MAGIC_BYTE && (data[4] >> 5) == 0); // version } static int ReadImageInfo(VP8LBitReader* const br, int* const width, int* const height, int* const has_alpha) { if (VP8LReadBits(br, 8) != VP8L_MAGIC_BYTE) return 0; *width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; *height = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; *has_alpha = VP8LReadBits(br, 1); if (VP8LReadBits(br, VP8L_VERSION_BITS) != 0) return 0; return !br->eos_; } int VP8LGetInfo(const uint8_t* data, size_t data_size, int* const width, int* const height, int* const has_alpha) { if (data == NULL || data_size < VP8L_FRAME_HEADER_SIZE) { return 0; // not enough data } else if (!VP8LCheckSignature(data, data_size)) { return 0; // bad signature } else { int w, h, a; VP8LBitReader br; VP8LInitBitReader(&br, data, data_size); if (!ReadImageInfo(&br, &w, &h, &a)) { return 0; } if (width != NULL) *width = w; if (height != NULL) *height = h; if (has_alpha != NULL) *has_alpha = a; return 1; } } //------------------------------------------------------------------------------ static WEBP_INLINE int GetCopyDistance(int distance_symbol, VP8LBitReader* const br) { int extra_bits, offset; if (distance_symbol < 4) { return distance_symbol + 1; } extra_bits = (distance_symbol - 2) >> 1; offset = (2 + (distance_symbol & 1)) << extra_bits; return offset + VP8LReadBits(br, extra_bits) + 1; } static WEBP_INLINE int GetCopyLength(int length_symbol, VP8LBitReader* const br) { // Length and distance prefixes are encoded the same way. return GetCopyDistance(length_symbol, br); } static WEBP_INLINE int PlaneCodeToDistance(int xsize, int plane_code) { if (plane_code > CODE_TO_PLANE_CODES) { return plane_code - CODE_TO_PLANE_CODES; } else { const int dist_code = kCodeToPlane[plane_code - 1]; const int yoffset = dist_code >> 4; const int xoffset = 8 - (dist_code & 0xf); const int dist = yoffset * xsize + xoffset; return (dist >= 1) ? dist : 1; // dist<1 can happen if xsize is very small } } //------------------------------------------------------------------------------ // Decodes the next Huffman code from bit-stream. // FillBitWindow(br) needs to be called at minimum every second call // to ReadSymbol, in order to pre-fetch enough bits. static WEBP_INLINE int ReadSymbol(const HuffmanCode* table, VP8LBitReader* const br) { int nbits; uint32_t val = VP8LPrefetchBits(br); table += val & HUFFMAN_TABLE_MASK; nbits = table->bits - HUFFMAN_TABLE_BITS; if (nbits > 0) { VP8LSetBitPos(br, br->bit_pos_ + HUFFMAN_TABLE_BITS); val = VP8LPrefetchBits(br); table += table->value; table += val & ((1 << nbits) - 1); } VP8LSetBitPos(br, br->bit_pos_ + table->bits); return table->value; } static int ReadHuffmanCodeLengths( VP8LDecoder* const dec, const int* const code_length_code_lengths, int num_symbols, int* const code_lengths) { int ok = 0; VP8LBitReader* const br = &dec->br_; int symbol; int max_symbol; int prev_code_len = DEFAULT_CODE_LENGTH; HuffmanCode table[1 << LENGTHS_TABLE_BITS]; if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS, code_length_code_lengths, NUM_CODE_LENGTH_CODES)) { goto End; } if (VP8LReadBits(br, 1)) { // use length const int length_nbits = 2 + 2 * VP8LReadBits(br, 3); max_symbol = 2 + VP8LReadBits(br, length_nbits); if (max_symbol > num_symbols) { goto End; } } else { max_symbol = num_symbols; } symbol = 0; while (symbol < num_symbols) { const HuffmanCode* p; int code_len; if (max_symbol-- == 0) break; VP8LFillBitWindow(br); p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK]; VP8LSetBitPos(br, br->bit_pos_ + p->bits); code_len = p->value; if (code_len < kCodeLengthLiterals) { code_lengths[symbol++] = code_len; if (code_len != 0) prev_code_len = code_len; } else { const int use_prev = (code_len == kCodeLengthRepeatCode); const int slot = code_len - kCodeLengthLiterals; const int extra_bits = kCodeLengthExtraBits[slot]; const int repeat_offset = kCodeLengthRepeatOffsets[slot]; int repeat = VP8LReadBits(br, extra_bits) + repeat_offset; if (symbol + repeat > num_symbols) { goto End; } else { const int length = use_prev ? prev_code_len : 0; while (repeat-- > 0) code_lengths[symbol++] = length; } } } ok = 1; End: if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR; return ok; } // 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman // tree. static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, int* const code_lengths, HuffmanCode* const table) { int ok = 0; int size = 0; VP8LBitReader* const br = &dec->br_; const int simple_code = VP8LReadBits(br, 1); memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths)); if (simple_code) { // Read symbols, codes & code lengths directly. const int num_symbols = VP8LReadBits(br, 1) + 1; const int first_symbol_len_code = VP8LReadBits(br, 1); // The first code is either 1 bit or 8 bit code. int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8); code_lengths[symbol] = 1; // The second code (if present), is always 8 bit long. if (num_symbols == 2) { symbol = VP8LReadBits(br, 8); code_lengths[symbol] = 1; } ok = 1; } else { // Decode Huffman-coded code lengths. int i; int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 }; const int num_codes = VP8LReadBits(br, 4) + 4; if (num_codes > NUM_CODE_LENGTH_CODES) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; return 0; } for (i = 0; i < num_codes; ++i) { code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3); } ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size, code_lengths); } ok = ok && !br->eos_; if (ok) { size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS, code_lengths, alphabet_size); } if (!ok || size == 0) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; return 0; } return size; } static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, int color_cache_bits, int allow_recursion) { int i, j; VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; uint32_t* huffman_image = NULL; HTreeGroup* htree_groups = NULL; HuffmanCode* huffman_tables = NULL; HuffmanCode* next = NULL; int num_htree_groups = 1; int max_alphabet_size = 0; int* code_lengths = NULL; const int table_size = kTableSize[color_cache_bits]; if (allow_recursion && VP8LReadBits(br, 1)) { // use meta Huffman codes. const int huffman_precision = VP8LReadBits(br, 3) + 2; const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision); const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); const int huffman_pixs = huffman_xsize * huffman_ysize; if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec, &huffman_image)) { goto Error; } hdr->huffman_subsample_bits_ = huffman_precision; for (i = 0; i < huffman_pixs; ++i) { // The huffman data is stored in red and green bytes. const int group = (huffman_image[i] >> 8) & 0xffff; huffman_image[i] = group; if (group >= num_htree_groups) { num_htree_groups = group + 1; } } } if (br->eos_) goto Error; // Find maximum alphabet size for the htree group. for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { int alphabet_size = kAlphabetSize[j]; if (j == 0 && color_cache_bits > 0) { alphabet_size += 1 << color_cache_bits; } if (max_alphabet_size < alphabet_size) { max_alphabet_size = alphabet_size; } } huffman_tables = tvg::malloc(num_htree_groups * table_size * sizeof(*huffman_tables)); htree_groups = VP8LHtreeGroupsNew(num_htree_groups); code_lengths = tvg::calloc((uint64_t)max_alphabet_size, sizeof(*code_lengths)); if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) { dec->status_ = VP8_STATUS_OUT_OF_MEMORY; goto Error; } next = huffman_tables; for (i = 0; i < num_htree_groups; ++i) { HTreeGroup* const htree_group = &htree_groups[i]; HuffmanCode** const htrees = htree_group->htrees; int size; int is_trivial_literal = 1; for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { int alphabet_size = kAlphabetSize[j]; htrees[j] = next; if (j == 0 && color_cache_bits > 0) { alphabet_size += 1 << color_cache_bits; } size = ReadHuffmanCode(alphabet_size, dec, code_lengths, next); if (is_trivial_literal && kLiteralMap[j] == 1) { is_trivial_literal = (next->bits == 0); } next += size; if (size == 0) { goto Error; } } htree_group->is_trivial_literal = is_trivial_literal; if (is_trivial_literal) { const int red = htrees[RED][0].value; const int blue = htrees[BLUE][0].value; const int alpha = htrees[ALPHA][0].value; htree_group->literal_arb = ((uint32_t)alpha << 24) | (red << 16) | blue; } } tvg::free(code_lengths); // All OK. Finalize pointers and return. hdr->huffman_image_ = huffman_image; hdr->num_htree_groups_ = num_htree_groups; hdr->htree_groups_ = htree_groups; hdr->huffman_tables_ = huffman_tables; return 1; Error: tvg::free(code_lengths); tvg::free(huffman_image); tvg::free(huffman_tables); VP8LHtreeGroupsFree(htree_groups); return 0; } //------------------------------------------------------------------------------ // Scaling. static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) { const int num_channels = 4; const int in_width = io->mb_w; const int out_width = io->scaled_width; const int in_height = io->mb_h; const int out_height = io->scaled_height; const uint64_t work_size = 2 * num_channels * (uint64_t)out_width; int32_t* work; // Rescaler work area. const uint64_t scaled_data_size = num_channels * (uint64_t)out_width; uint32_t* scaled_data; // Temporary storage for scaled BGRA data. const uint64_t memory_size = sizeof(*dec->rescaler) + work_size * sizeof(*work) + scaled_data_size * sizeof(*scaled_data); uint8_t* memory = tvg::calloc(memory_size, sizeof(*memory)); if (memory == NULL) { dec->status_ = VP8_STATUS_OUT_OF_MEMORY; return 0; } assert(dec->rescaler_memory == NULL); dec->rescaler_memory = memory; dec->rescaler = (WebPRescaler*)memory; memory += sizeof(*dec->rescaler); work = (int32_t*)memory; memory += work_size * sizeof(*work); scaled_data = (uint32_t*)memory; WebPRescalerInit(dec->rescaler, in_width, in_height, (uint8_t*)scaled_data, out_width, out_height, 0, num_channels, in_width, out_width, in_height, out_height, work); return 1; } //------------------------------------------------------------------------------ // Export to ARGB // We have special "export" function since we need to convert from BGRA static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace, int rgba_stride, uint8_t* const rgba) { uint32_t* const src = (uint32_t*)rescaler->dst; const int dst_width = rescaler->dst_width; int num_lines_out = 0; while (WebPRescalerHasPendingOutput(rescaler)) { uint8_t* const dst = rgba + num_lines_out * rgba_stride; WebPRescalerExportRow(rescaler, 0); WebPMultARGBRow(src, dst_width, 1); VP8LConvertFromBGRA(src, dst_width, colorspace, dst); ++num_lines_out; } return num_lines_out; } // Emit scaled rows. static int EmitRescaledRowsRGBA(const VP8LDecoder* const dec, uint8_t* in, int in_stride, int mb_h, uint8_t* const out, int out_stride) { const WEBP_CSP_MODE colorspace = dec->output_->colorspace; int num_lines_in = 0; int num_lines_out = 0; while (num_lines_in < mb_h) { uint8_t* const row_in = in + num_lines_in * in_stride; uint8_t* const row_out = out + num_lines_out * out_stride; const int lines_left = mb_h - num_lines_in; const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left); assert(needed_lines > 0 && needed_lines <= lines_left); WebPMultARGBRows(row_in, in_stride, dec->rescaler->src_width, needed_lines, 0); WebPRescalerImport(dec->rescaler, lines_left, row_in, in_stride); num_lines_in += needed_lines; num_lines_out += Export(dec->rescaler, colorspace, out_stride, row_out); } return num_lines_out; } // Emit rows without any scaling. static int EmitRows(WEBP_CSP_MODE colorspace, const uint8_t* row_in, int in_stride, int mb_w, int mb_h, uint8_t* const out, int out_stride) { int lines = mb_h; uint8_t* row_out = out; while (lines-- > 0) { VP8LConvertFromBGRA((const uint32_t*)row_in, mb_w, colorspace, row_out); row_in += in_stride; row_out += out_stride; } return mb_h; // Num rows out == num rows in. } //------------------------------------------------------------------------------ // Export to YUVA // TODO(skal): should be in yuv.c static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos, const WebPDecBuffer* const output) { const WebPYUVABuffer* const buf = &output->u.YUVA; // first, the luma plane { int i; uint8_t* const y = buf->y + y_pos * buf->y_stride; for (i = 0; i < width; ++i) { const uint32_t p = src[i]; y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff, YUV_HALF); } } // then U/V planes { uint8_t* const u = buf->u + (y_pos >> 1) * buf->u_stride; uint8_t* const v = buf->v + (y_pos >> 1) * buf->v_stride; const int uv_width = width >> 1; int i; for (i = 0; i < uv_width; ++i) { const uint32_t v0 = src[2 * i + 0]; const uint32_t v1 = src[2 * i + 1]; // VP8RGBToU/V expects four accumulated pixels. Hence we need to // scale r/g/b value by a factor 2. We just shift v0/v1 one bit less. const int r = ((v0 >> 15) & 0x1fe) + ((v1 >> 15) & 0x1fe); const int g = ((v0 >> 7) & 0x1fe) + ((v1 >> 7) & 0x1fe); const int b = ((v0 << 1) & 0x1fe) + ((v1 << 1) & 0x1fe); if (!(y_pos & 1)) { // even lines: store values u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2); v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2); } else { // odd lines: average with previous values const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2); const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2); // Approximated average-of-four. But it's an acceptable diff. u[i] = (u[i] + tmp_u + 1) >> 1; v[i] = (v[i] + tmp_v + 1) >> 1; } } if (width & 1) { // last pixel const uint32_t v0 = src[2 * i + 0]; const int r = (v0 >> 14) & 0x3fc; const int g = (v0 >> 6) & 0x3fc; const int b = (v0 << 2) & 0x3fc; if (!(y_pos & 1)) { // even lines u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2); v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2); } else { // odd lines (note: we could just skip this) const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2); const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2); u[i] = (u[i] + tmp_u + 1) >> 1; v[i] = (v[i] + tmp_v + 1) >> 1; } } } // Lastly, store alpha if needed. if (buf->a != NULL) { int i; uint8_t* const a = buf->a + y_pos * buf->a_stride; for (i = 0; i < width; ++i) a[i] = (src[i] >> 24); } } static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) { WebPRescaler* const rescaler = dec->rescaler; uint32_t* const src = (uint32_t*)rescaler->dst; const int dst_width = rescaler->dst_width; int num_lines_out = 0; while (WebPRescalerHasPendingOutput(rescaler)) { WebPRescalerExportRow(rescaler, 0); WebPMultARGBRow(src, dst_width, 1); ConvertToYUVA(src, dst_width, y_pos, dec->output_); ++y_pos; ++num_lines_out; } return num_lines_out; } static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec, uint8_t* in, int in_stride, int mb_h) { int num_lines_in = 0; int y_pos = dec->last_out_row_; while (num_lines_in < mb_h) { const int lines_left = mb_h - num_lines_in; const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left); WebPMultARGBRows(in, in_stride, dec->rescaler->src_width, needed_lines, 0); WebPRescalerImport(dec->rescaler, lines_left, in, in_stride); num_lines_in += needed_lines; in += needed_lines * in_stride; y_pos += ExportYUVA(dec, y_pos); } return y_pos; } static int EmitRowsYUVA(const VP8LDecoder* const dec, const uint8_t* in, int in_stride, int mb_w, int num_rows) { int y_pos = dec->last_out_row_; while (num_rows-- > 0) { ConvertToYUVA((const uint32_t*)in, mb_w, y_pos, dec->output_); in += in_stride; ++y_pos; } return y_pos; } //------------------------------------------------------------------------------ // Cropping. // Sets io->mb_y, io->mb_h & io->mb_w according to start row, end row and // crop options. Also updates the input data pointer, so that it points to the // start of the cropped window. Note that pixels are in ARGB format even if // 'in_data' is uint8_t*. // Returns true if the crop window is not empty. static int SetCropWindow(VP8Io* const io, int y_start, int y_end, uint8_t** const in_data, int pixel_stride) { assert(y_start < y_end); assert(io->crop_left < io->crop_right); if (y_end > io->crop_bottom) { y_end = io->crop_bottom; // make sure we don't overflow on last row. } if (y_start < io->crop_top) { const int delta = io->crop_top - y_start; y_start = io->crop_top; *in_data += delta * pixel_stride; } if (y_start >= y_end) return 0; // Crop window is empty. *in_data += io->crop_left * sizeof(uint32_t); io->mb_y = y_start - io->crop_top; io->mb_w = io->crop_right - io->crop_left; io->mb_h = y_end - y_start; return 1; // Non-empty crop window. } //------------------------------------------------------------------------------ static WEBP_INLINE int GetMetaIndex( const uint32_t* const image, int xsize, int bits, int x, int y) { if (bits == 0) return 0; return image[xsize * (y >> bits) + (x >> bits)]; } static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr, int x, int y) { const int meta_index = GetMetaIndex(hdr->huffman_image_, hdr->huffman_xsize_, hdr->huffman_subsample_bits_, x, y); assert(meta_index < hdr->num_htree_groups_); return hdr->htree_groups_ + meta_index; } //------------------------------------------------------------------------------ // Main loop, with custom row-processing function typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row); static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows, const uint32_t* const rows) { int n = dec->next_transform_; const int cache_pixs = dec->width_ * num_rows; const int start_row = dec->last_row_; const int end_row = start_row + num_rows; const uint32_t* rows_in = rows; uint32_t* const rows_out = dec->argb_cache_; // Inverse transforms. // TODO: most transforms only need to operate on the cropped region only. memcpy(rows_out, rows_in, cache_pixs * sizeof(*rows_out)); while (n-- > 0) { VP8LTransform* const transform = &dec->transforms_[n]; VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out); rows_in = rows_out; } } // Special method for paletted alpha data. static void ApplyInverseTransformsAlpha(VP8LDecoder* const dec, int num_rows, const uint8_t* const rows) { const int start_row = dec->last_row_; const int end_row = start_row + num_rows; const uint8_t* rows_in = rows; uint8_t* rows_out = (uint8_t*)dec->io_->opaque + dec->io_->width * start_row; VP8LTransform* const transform = &dec->transforms_[0]; assert(dec->next_transform_ == 1); assert(transform->type_ == COLOR_INDEXING_TRANSFORM); VP8LColorIndexInverseTransformAlpha(transform, start_row, end_row, rows_in, rows_out); } // Processes (transforms, scales & color-converts) the rows decoded after the // last call. static void ProcessRows(VP8LDecoder* const dec, int row) { const uint32_t* const rows = dec->pixels_ + dec->width_ * dec->last_row_; const int num_rows = row - dec->last_row_; if (num_rows <= 0) return; // Nothing to be done. ApplyInverseTransforms(dec, num_rows, rows); // Emit output. { VP8Io* const io = dec->io_; uint8_t* rows_data = (uint8_t*)dec->argb_cache_; const int in_stride = io->width * sizeof(uint32_t); // in unit of RGBA if (!SetCropWindow(io, dec->last_row_, row, &rows_data, in_stride)) { // Nothing to output (this time). } else { const WebPDecBuffer* const output = dec->output_; if (output->colorspace < MODE_YUV) { // convert to RGBA const WebPRGBABuffer* const buf = &output->u.RGBA; uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride; const int num_rows_out = io->use_scaling ? EmitRescaledRowsRGBA(dec, rows_data, in_stride, io->mb_h, rgba, buf->stride) : EmitRows(output->colorspace, rows_data, in_stride, io->mb_w, io->mb_h, rgba, buf->stride); // Update 'last_out_row_'. dec->last_out_row_ += num_rows_out; } else { // convert to YUVA dec->last_out_row_ = io->use_scaling ? EmitRescaledRowsYUVA(dec, rows_data, in_stride, io->mb_h) : EmitRowsYUVA(dec, rows_data, in_stride, io->mb_w, io->mb_h); } assert(dec->last_out_row_ <= output->height); } } // Update 'last_row_'. dec->last_row_ = row; assert(dec->last_row_ <= dec->height_); } // Row-processing for the special case when alpha data contains only one // transform (color indexing), and trivial non-green literals. static int Is8bOptimizable(const VP8LMetadata* const hdr) { int i; if (hdr->color_cache_size_ > 0) return 0; // When the Huffman tree contains only one symbol, we can skip the // call to ReadSymbol() for red/blue/alpha channels. for (i = 0; i < hdr->num_htree_groups_; ++i) { HuffmanCode** const htrees = hdr->htree_groups_[i].htrees; if (htrees[RED][0].bits > 0) return 0; if (htrees[BLUE][0].bits > 0) return 0; if (htrees[ALPHA][0].bits > 0) return 0; } return 1; } static void ExtractPalettedAlphaRows(VP8LDecoder* const dec, int row) { const int num_rows = row - dec->last_row_; const uint8_t* const in = (uint8_t*)dec->pixels_ + dec->width_ * dec->last_row_; if (num_rows > 0) { ApplyInverseTransformsAlpha(dec, num_rows, in); } dec->last_row_ = dec->last_out_row_ = row; } //------------------------------------------------------------------------------ // Helper functions for fast pattern copy (8b and 32b) // cyclic rotation of pattern word static WEBP_INLINE uint32_t Rotate8b(uint32_t V) { #if defined(WORDS_BIGENDIAN) return ((V & 0xff000000u) >> 24) | (V << 8); #else return ((V & 0xffu) << 24) | (V >> 8); #endif } // copy 1, 2 or 4-bytes pattern static WEBP_INLINE void CopySmallPattern8b(const uint8_t* src, uint8_t* dst, int length, uint32_t pattern) { int i; // align 'dst' to 4-bytes boundary. Adjust the pattern along the way. while ((uintptr_t)dst & 3) { *dst++ = *src++; pattern = Rotate8b(pattern); --length; } // Copy the pattern 4 bytes at a time. for (i = 0; i < (length >> 2); ++i) { ((uint32_t*)dst)[i] = pattern; } // Finish with left-overs. 'pattern' is still correctly positioned, // so no Rotate8b() call is needed. for (i <<= 2; i < length; ++i) { dst[i] = src[i]; } } static WEBP_INLINE void CopyBlock8b(uint8_t* const dst, int dist, int length) { const uint8_t* src = dst - dist; if (length >= 8) { uint32_t pattern = 0; switch (dist) { case 1: pattern = src[0]; #if defined(__arm__) || defined(_M_ARM) // arm doesn't like multiply that much pattern |= pattern << 8; pattern |= pattern << 16; #elif defined(WEBP_USE_MIPS_DSP_R2) __asm__ volatile ("replv.qb %0, %0" : "+r"(pattern)); #else pattern = 0x01010101u * pattern; #endif break; case 2: memcpy(&pattern, src, sizeof(uint16_t)); #if defined(__arm__) || defined(_M_ARM) pattern |= pattern << 16; #elif defined(WEBP_USE_MIPS_DSP_R2) __asm__ volatile ("replv.ph %0, %0" : "+r"(pattern)); #else pattern = 0x00010001u * pattern; #endif break; case 4: memcpy(&pattern, src, sizeof(uint32_t)); break; default: goto Copy; break; } CopySmallPattern8b(src, dst, length, pattern); return; } Copy: if (dist >= length) { // no overlap -> use memcpy() memcpy(dst, src, length * sizeof(*dst)); } else { int i; for (i = 0; i < length; ++i) dst[i] = src[i]; } } // copy pattern of 1 or 2 uint32_t's static WEBP_INLINE void CopySmallPattern32b(const uint32_t* src, uint32_t* dst, int length, uint64_t pattern) { int i; if ((uintptr_t)dst & 4) { // Align 'dst' to 8-bytes boundary. *dst++ = *src++; pattern = (pattern >> 32) | (pattern << 32); --length; } assert(0 == ((uintptr_t)dst & 7)); for (i = 0; i < (length >> 1); ++i) { ((uint64_t*)dst)[i] = pattern; // Copy the pattern 8 bytes at a time. } if (length & 1) { // Finish with left-over. dst[i << 1] = src[i << 1]; } } static WEBP_INLINE void CopyBlock32b(uint32_t* const dst, int dist, int length) { const uint32_t* const src = dst - dist; if (dist <= 2 && length >= 4 && ((uintptr_t)dst & 3) == 0) { uint64_t pattern; if (dist == 1) { pattern = (uint64_t)src[0]; pattern |= pattern << 32; } else { memcpy(&pattern, src, sizeof(pattern)); } CopySmallPattern32b(src, dst, length, pattern); } else if (dist >= length) { // no overlap memcpy(dst, src, length * sizeof(*dst)); } else { int i; for (i = 0; i < length; ++i) dst[i] = src[i]; } } //------------------------------------------------------------------------------ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, int width, int height, int last_row) { int ok = 1; int row = dec->last_pixel_ / width; int col = dec->last_pixel_ % width; VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; const HTreeGroup* htree_group = GetHtreeGroupForPos(hdr, col, row); int pos = dec->last_pixel_; // current position const int end = width * height; // End of data const int last = width * last_row; // Last pixel to decode const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; const int mask = hdr->huffman_mask_; assert(htree_group != NULL); assert(pos < end); assert(last_row <= height); assert(Is8bOptimizable(hdr)); while (!br->eos_ && pos < last) { int code; // Only update when changing tile. if ((col & mask) == 0) { htree_group = GetHtreeGroupForPos(hdr, col, row); } VP8LFillBitWindow(br); code = ReadSymbol(htree_group->htrees[GREEN], br); if (code < NUM_LITERAL_CODES) { // Literal data[pos] = code; ++pos; ++col; if (col >= width) { col = 0; ++row; if (row % NUM_ARGB_CACHE_ROWS == 0) { ExtractPalettedAlphaRows(dec, row); } } } else if (code < len_code_limit) { // Backward reference int dist_code, dist; const int length_sym = code - NUM_LITERAL_CODES; const int length = GetCopyLength(length_sym, br); const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br); VP8LFillBitWindow(br); dist_code = GetCopyDistance(dist_symbol, br); dist = PlaneCodeToDistance(width, dist_code); if (pos >= dist && end - pos >= length) { CopyBlock8b(data + pos, dist, length); } else { ok = 0; goto End; } pos += length; col += length; while (col >= width) { col -= width; ++row; if (row % NUM_ARGB_CACHE_ROWS == 0) { ExtractPalettedAlphaRows(dec, row); } } if (pos < last && (col & mask)) { htree_group = GetHtreeGroupForPos(hdr, col, row); } } else { // Not reached ok = 0; goto End; } assert(br->eos_ == VP8LIsEndOfStream(br)); } // Process the remaining rows corresponding to last row-block. ExtractPalettedAlphaRows(dec, row); End: if (!ok || (br->eos_ && pos < end)) { ok = 0; dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED : VP8_STATUS_BITSTREAM_ERROR; } else { dec->last_pixel_ = (int)pos; } return ok; } static void SaveState(VP8LDecoder* const dec, int last_pixel) { assert(dec->incremental_); dec->saved_br_ = dec->br_; dec->saved_last_pixel_ = last_pixel; if (dec->hdr_.color_cache_size_ > 0) { VP8LColorCacheCopy(&dec->hdr_.color_cache_, &dec->hdr_.saved_color_cache_); } } static void RestoreState(VP8LDecoder* const dec) { assert(dec->br_.eos_); dec->status_ = VP8_STATUS_SUSPENDED; dec->br_ = dec->saved_br_; dec->last_pixel_ = dec->saved_last_pixel_; if (dec->hdr_.color_cache_size_ > 0) { VP8LColorCacheCopy(&dec->hdr_.saved_color_cache_, &dec->hdr_.color_cache_); } } #define SYNC_EVERY_N_ROWS 8 // minimum number of rows between check-points static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, int width, int height, int last_row, ProcessRowsFunc process_func) { int row = dec->last_pixel_ / width; int col = dec->last_pixel_ % width; VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; HTreeGroup* htree_group = GetHtreeGroupForPos(hdr, col, row); uint32_t* src = data + dec->last_pixel_; uint32_t* last_cached = src; uint32_t* const src_end = data + width * height; // End of data uint32_t* const src_last = data + width * last_row; // Last pixel to decode const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; const int color_cache_limit = len_code_limit + hdr->color_cache_size_; int next_sync_row = dec->incremental_ ? row : 1 << 24; VP8LColorCache* const color_cache = (hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL; const int mask = hdr->huffman_mask_; assert(htree_group != NULL); assert(src < src_end); assert(src_last <= src_end); while (src < src_last) { int code; if (row >= next_sync_row) { SaveState(dec, (int)(src - data)); next_sync_row = row + SYNC_EVERY_N_ROWS; } // Only update when changing tile. Note we could use this test: // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed // but that's actually slower and needs storing the previous col/row. if ((col & mask) == 0) { htree_group = GetHtreeGroupForPos(hdr, col, row); } VP8LFillBitWindow(br); code = ReadSymbol(htree_group->htrees[GREEN], br); if (br->eos_) break; // early out if (code < NUM_LITERAL_CODES) { // Literal if (htree_group->is_trivial_literal) { *src = htree_group->literal_arb | (code << 8); } else { int red, blue, alpha; red = ReadSymbol(htree_group->htrees[RED], br); VP8LFillBitWindow(br); blue = ReadSymbol(htree_group->htrees[BLUE], br); alpha = ReadSymbol(htree_group->htrees[ALPHA], br); if (br->eos_) break; *src = ((uint32_t)alpha << 24) | (red << 16) | (code << 8) | blue; } AdvanceByOne: ++src; ++col; if (col >= width) { col = 0; ++row; if ((row % NUM_ARGB_CACHE_ROWS == 0) && (process_func != NULL)) { process_func(dec, row); } if (color_cache != NULL) { while (last_cached < src) { VP8LColorCacheInsert(color_cache, *last_cached++); } } } } else if (code < len_code_limit) { // Backward reference int dist_code, dist; const int length_sym = code - NUM_LITERAL_CODES; const int length = GetCopyLength(length_sym, br); const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br); VP8LFillBitWindow(br); dist_code = GetCopyDistance(dist_symbol, br); dist = PlaneCodeToDistance(width, dist_code); if (br->eos_) break; if (src - data < (ptrdiff_t)dist || src_end - src < (ptrdiff_t)length) { goto Error; } else { CopyBlock32b(src, dist, length); } src += length; col += length; while (col >= width) { col -= width; ++row; if ((row % NUM_ARGB_CACHE_ROWS == 0) && (process_func != NULL)) { process_func(dec, row); } } // Because of the check done above (before 'src' was incremented by // 'length'), the following holds true. assert(src <= src_end); if (col & mask) htree_group = GetHtreeGroupForPos(hdr, col, row); if (color_cache != NULL) { while (last_cached < src) { VP8LColorCacheInsert(color_cache, *last_cached++); } } } else if (code < color_cache_limit) { // Color cache const int key = code - len_code_limit; assert(color_cache != NULL); while (last_cached < src) { VP8LColorCacheInsert(color_cache, *last_cached++); } *src = VP8LColorCacheLookup(color_cache, key); goto AdvanceByOne; } else { // Not reached goto Error; } assert(br->eos_ == VP8LIsEndOfStream(br)); } if (dec->incremental_ && br->eos_ && src < src_end) { RestoreState(dec); } else if (!br->eos_) { // Process the remaining rows corresponding to last row-block. if (process_func != NULL) { process_func(dec, row); } dec->status_ = VP8_STATUS_OK; dec->last_pixel_ = (int)(src - data); // end-of-scan marker } else { // if not incremental, and we are past the end of buffer (eos_=1), then this // is a real bitstream error. goto Error; } return 1; Error: dec->status_ = VP8_STATUS_BITSTREAM_ERROR; return 0; } // ----------------------------------------------------------------------------- // VP8LTransform static void ClearTransform(VP8LTransform* const transform) { tvg::free(transform->data_); transform->data_ = NULL; } // For security reason, we need to remap the color map to span // the total possible bundled values, and not just the num_colors. static int ExpandColorMap(int num_colors, VP8LTransform* const transform) { int i; const int final_num_colors = 1 << (8 >> transform->bits_); uint32_t* const new_color_map = tvg::malloc((uint64_t)final_num_colors * sizeof(*new_color_map)); if (new_color_map == NULL) { return 0; } else { uint8_t* const data = (uint8_t*)transform->data_; uint8_t* const new_data = (uint8_t*)new_color_map; new_color_map[0] = transform->data_[0]; for (i = 4; i < 4 * num_colors; ++i) { // Equivalent to AddPixelEq(), on a byte-basis. new_data[i] = (data[i] + new_data[i - 4]) & 0xff; } for (; i < 4 * final_num_colors; ++i) new_data[i] = 0; // black tail. tvg::free(transform->data_); transform->data_ = new_color_map; } return 1; } static int ReadTransform(int* const xsize, int const* ysize, VP8LDecoder* const dec) { int ok = 1; VP8LBitReader* const br = &dec->br_; VP8LTransform* transform = &dec->transforms_[dec->next_transform_]; const VP8LImageTransformType type = (VP8LImageTransformType)VP8LReadBits(br, 2); // Each transform type can only be present once in the stream. if (dec->transforms_seen_ & (1U << type)) { return 0; // Already there, let's not accept the second same transform. } dec->transforms_seen_ |= (1U << type); transform->type_ = type; transform->xsize_ = *xsize; transform->ysize_ = *ysize; transform->data_ = NULL; ++dec->next_transform_; assert(dec->next_transform_ <= NUM_TRANSFORMS); switch (type) { case PREDICTOR_TRANSFORM: case CROSS_COLOR_TRANSFORM: transform->bits_ = VP8LReadBits(br, 3) + 2; ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_, transform->bits_), VP8LSubSampleSize(transform->ysize_, transform->bits_), 0, dec, &transform->data_); break; case COLOR_INDEXING_TRANSFORM: { const int num_colors = VP8LReadBits(br, 8) + 1; const int bits = (num_colors > 16) ? 0 : (num_colors > 4) ? 1 : (num_colors > 2) ? 2 : 3; *xsize = VP8LSubSampleSize(transform->xsize_, bits); transform->bits_ = bits; ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_); ok = ok && ExpandColorMap(num_colors, transform); break; } case SUBTRACT_GREEN: break; default: assert(0); // can't happen break; } return ok; } // ----------------------------------------------------------------------------- // VP8LMetadata static void InitMetadata(VP8LMetadata* const hdr) { assert(hdr != NULL); memset(hdr, 0, sizeof(*hdr)); } static void ClearMetadata(VP8LMetadata* const hdr) { assert(hdr != NULL); tvg::free(hdr->huffman_image_); tvg::free(hdr->huffman_tables_); VP8LHtreeGroupsFree(hdr->htree_groups_); VP8LColorCacheClear(&hdr->color_cache_); VP8LColorCacheClear(&hdr->saved_color_cache_); InitMetadata(hdr); } // ----------------------------------------------------------------------------- // VP8LDecoder VP8LDecoder* VP8LNew(void) { VP8LDecoder* const dec = tvg::calloc(1ULL, sizeof(*dec)); if (dec == NULL) return NULL; dec->status_ = VP8_STATUS_OK; dec->state_ = READ_DIM; VP8LDspInit(); // Init critical function pointers. return dec; } void VP8LClear(VP8LDecoder* const dec) { int i; if (dec == NULL) return; ClearMetadata(&dec->hdr_); tvg::free(dec->pixels_); dec->pixels_ = NULL; for (i = 0; i < dec->next_transform_; ++i) { ClearTransform(&dec->transforms_[i]); } dec->next_transform_ = 0; dec->transforms_seen_ = 0; tvg::free(dec->rescaler_memory); dec->rescaler_memory = NULL; dec->output_ = NULL; // leave no trace behind } void VP8LDelete(VP8LDecoder* const dec) { if (dec != NULL) { VP8LClear(dec); tvg::free(dec); } } static void UpdateDecoder(VP8LDecoder* const dec, int width, int height) { VP8LMetadata* const hdr = &dec->hdr_; const int num_bits = hdr->huffman_subsample_bits_; dec->width_ = width; dec->height_ = height; hdr->huffman_xsize_ = VP8LSubSampleSize(width, num_bits); hdr->huffman_mask_ = (num_bits == 0) ? ~0 : (1 << num_bits) - 1; } static int DecodeImageStream(int xsize, int ysize, int is_level0, VP8LDecoder* const dec, uint32_t** const decoded_data) { int ok = 1; int transform_xsize = xsize; int transform_ysize = ysize; VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; uint32_t* data = NULL; int color_cache_bits = 0; // Read the transforms (may recurse). if (is_level0) { while (ok && VP8LReadBits(br, 1)) { ok = ReadTransform(&transform_xsize, &transform_ysize, dec); } } // Color cache if (ok && VP8LReadBits(br, 1)) { color_cache_bits = VP8LReadBits(br, 4); ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS); if (!ok) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; goto End; } } // Read the Huffman codes (may recurse). ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize, color_cache_bits, is_level0); if (!ok) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; goto End; } // Finish setting up the color-cache if (color_cache_bits > 0) { hdr->color_cache_size_ = 1 << color_cache_bits; if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) { dec->status_ = VP8_STATUS_OUT_OF_MEMORY; ok = 0; goto End; } } else { hdr->color_cache_size_ = 0; } UpdateDecoder(dec, transform_xsize, transform_ysize); if (is_level0) { // level 0 complete dec->state_ = READ_HDR; goto End; } { const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize; data = tvg::malloc(total_size * sizeof(*data)); if (data == NULL) { dec->status_ = VP8_STATUS_OUT_OF_MEMORY; ok = 0; goto End; } } // Use the Huffman trees to decode the LZ77 encoded data. ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, transform_ysize, NULL); ok = ok && !br->eos_; End: if (!ok) { tvg::free(data); ClearMetadata(hdr); } else { if (decoded_data != NULL) { *decoded_data = data; } else { // We allocate image data in this function only for transforms. At level 0 // (that is: not the transforms), we shouldn't have allocated anything. assert(data == NULL); assert(is_level0); } dec->last_pixel_ = 0; // Reset for future DECODE_DATA_FUNC() calls. if (!is_level0) ClearMetadata(hdr); // Clean up temporary data behind. } return ok; } //------------------------------------------------------------------------------ // Allocate internal buffers dec->pixels_ and dec->argb_cache_. static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) { const uint64_t num_pixels = (uint64_t)dec->width_ * dec->height_; // Scratch buffer corresponding to top-prediction row for transforming the // first row in the row-blocks. Not needed for paletted alpha. const uint64_t cache_top_pixels = (uint16_t)final_width; // Scratch buffer for temporary BGRA storage. Not needed for paletted alpha. const uint64_t cache_pixels = (uint64_t)final_width * NUM_ARGB_CACHE_ROWS; const uint64_t total_num_pixels = num_pixels + cache_top_pixels + cache_pixels; assert(dec->width_ <= final_width); dec->pixels_ = tvg::malloc(total_num_pixels * sizeof(uint32_t)); if (dec->pixels_ == NULL) { dec->argb_cache_ = NULL; // for sanity check dec->status_ = VP8_STATUS_OUT_OF_MEMORY; return 0; } dec->argb_cache_ = dec->pixels_ + num_pixels + cache_top_pixels; return 1; } static int AllocateInternalBuffers8b(VP8LDecoder* const dec) { const uint64_t total_num_pixels = (uint64_t)dec->width_ * dec->height_; dec->argb_cache_ = NULL; // for sanity check dec->pixels_ = tvg::malloc(total_num_pixels * sizeof(uint8_t)); if (dec->pixels_ == NULL) { dec->status_ = VP8_STATUS_OUT_OF_MEMORY; return 0; } return 1; } //------------------------------------------------------------------------------ // Special row-processing that only stores the alpha data. static void ExtractAlphaRows(VP8LDecoder* const dec, int row) { const int num_rows = row - dec->last_row_; const uint32_t* const in = dec->pixels_ + dec->width_ * dec->last_row_; if (num_rows <= 0) return; // Nothing to be done. ApplyInverseTransforms(dec, num_rows, in); // Extract alpha (which is stored in the green plane). { const int width = dec->io_->width; // the final width (!= dec->width_) const int cache_pixs = width * num_rows; uint8_t* const dst = (uint8_t*)dec->io_->opaque + width * dec->last_row_; const uint32_t* const src = dec->argb_cache_; int i; for (i = 0; i < cache_pixs; ++i) dst[i] = (src[i] >> 8) & 0xff; } dec->last_row_ = dec->last_out_row_ = row; } int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, const uint8_t* const data, size_t data_size, uint8_t* const output) { int ok = 0; VP8LDecoder* dec; VP8Io* io; assert(alph_dec != NULL); alph_dec->vp8l_dec_ = VP8LNew(); if (alph_dec->vp8l_dec_ == NULL) return 0; dec = alph_dec->vp8l_dec_; dec->width_ = alph_dec->width_; dec->height_ = alph_dec->height_; dec->io_ = &alph_dec->io_; io = dec->io_; VP8InitIo(io); WebPInitCustomIo(NULL, io); // Just a sanity Init. io won't be used. io->opaque = output; io->width = alph_dec->width_; io->height = alph_dec->height_; dec->status_ = VP8_STATUS_OK; VP8LInitBitReader(&dec->br_, data, data_size); if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, 1, dec, NULL)) { goto Err; } // Special case: if alpha data uses only the color indexing transform and // doesn't use color cache (a frequent case), we will use DecodeAlphaData() // method that only needs allocation of 1 byte per pixel (alpha channel). if (dec->next_transform_ == 1 && dec->transforms_[0].type_ == COLOR_INDEXING_TRANSFORM && Is8bOptimizable(&dec->hdr_)) { alph_dec->use_8b_decode = 1; ok = AllocateInternalBuffers8b(dec); } else { // Allocate internal buffers (note that dec->width_ may have changed here). alph_dec->use_8b_decode = 0; ok = AllocateInternalBuffers32b(dec, alph_dec->width_); } if (!ok) goto Err; return 1; Err: VP8LDelete(alph_dec->vp8l_dec_); alph_dec->vp8l_dec_ = NULL; return 0; } int VP8LDecodeAlphaImageStream(ALPHDecoder* const alph_dec, int last_row) { VP8LDecoder* const dec = alph_dec->vp8l_dec_; assert(dec != NULL); assert(last_row <= dec->height_); if (dec->last_pixel_ == dec->width_ * dec->height_) { return 1; // done } // Decode (with special row processing). return alph_dec->use_8b_decode ? DecodeAlphaData(dec, (uint8_t*)dec->pixels_, dec->width_, dec->height_, last_row) : DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, last_row, ExtractAlphaRows); } //------------------------------------------------------------------------------ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { int width, height, has_alpha; if (dec == NULL) return 0; if (io == NULL) { dec->status_ = VP8_STATUS_INVALID_PARAM; return 0; } dec->io_ = io; dec->status_ = VP8_STATUS_OK; VP8LInitBitReader(&dec->br_, io->data, io->data_size); if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; goto Error; } dec->state_ = READ_DIM; io->width = width; io->height = height; if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error; return 1; Error: VP8LClear(dec); assert(dec->status_ != VP8_STATUS_OK); return 0; } int VP8LDecodeImage(VP8LDecoder* const dec) { VP8Io* io = NULL; WebPDecParams* params = NULL; // Sanity checks. if (dec == NULL) return 0; assert(dec->hdr_.huffman_tables_ != NULL); assert(dec->hdr_.htree_groups_ != NULL); assert(dec->hdr_.num_htree_groups_ > 0); io = dec->io_; assert(io != NULL); params = (WebPDecParams*)io->opaque; assert(params != NULL); // Initialization. if (dec->state_ != READ_DATA) { dec->output_ = params->output; assert(dec->output_ != NULL); if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) { dec->status_ = VP8_STATUS_INVALID_PARAM; goto Err; } if (!AllocateInternalBuffers32b(dec, io->width)) goto Err; if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err; if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) { // need the alpha-multiply functions for premultiplied output or rescaling WebPInitAlphaProcessing(); } if (dec->incremental_) { if (dec->hdr_.color_cache_size_ > 0 && dec->hdr_.saved_color_cache_.colors_ == NULL) { if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_, dec->hdr_.color_cache_.hash_bits_)) { dec->status_ = VP8_STATUS_OUT_OF_MEMORY; goto Err; } } } dec->state_ = READ_DATA; } // Decode. if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, dec->height_, ProcessRows)) { goto Err; } params->last_y = dec->last_out_row_; return 1; Err: VP8LClear(dec); assert(dec->status_ != VP8_STATUS_OK); return 0; } //------------------------------------------------------------------------------ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_webp/meson.build000664 001750 001750 00000000460 15164251010 036357 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0source_file = [ 'tvgWebpLoader.h', 'tvgWebpLoader.cpp', ] webp_dep = dependency('libwebp', required: false) if webp_dep.found() subloader_dep += [declare_dependency( include_directories : include_directories('.'), dependencies : webp_dep, sources : source_file )] endifsrc/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/reader.h000664 001750 001750 00000270174 15164251010 036275 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// 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. // // 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/clzll.h" #include "internal/meta.h" #include "internal/stack.h" #include "internal/strtod.h" #include #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) #include #pragma intrinsic(_BitScanForward) #endif #ifdef RAPIDJSON_SSE42 #include #elif defined(RAPIDJSON_SSE2) #include #elif defined(RAPIDJSON_NEON) #include #endif #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(old-style-cast) RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) #elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4702) // unreachable code #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 // 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. kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. 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 Derived = void> struct BaseReaderHandler { typedef typename Encoding::Ch Ch; typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; bool Default() { return true; } bool Null() { return static_cast(*this).Default(); } bool Bool(bool) { return static_cast(*this).Default(); } bool Int(int) { return static_cast(*this).Default(); } bool Uint(unsigned) { return static_cast(*this).Default(); } bool Int64(int64_t) { return static_cast(*this).Default(); } bool Uint64(uint64_t) { return static_cast(*this).Default(); } bool Double(double) { return static_cast(*this).Default(); } /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } bool StartObject() { return static_cast(*this).Default(); } bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool EndObject(SizeType) { return static_cast(*this).Default(); } bool StartArray() { return static_cast(*this).Default(); } bool EndArray(SizeType) { return static_cast(*this).Default(); } }; /////////////////////////////////////////////////////////////////////////////// // StreamLocalCopy namespace internal { template::copyOptimization> class StreamLocalCopy; //! Do copy optimization. template class StreamLocalCopy { public: StreamLocalCopy(Stream& original) : s(original), original_(original) {} ~StreamLocalCopy() { original_ = s; } Stream s; private: StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; Stream& original_; }; //! Keep reference. template class StreamLocalCopy { 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 void SkipWhitespace(InputStream& is) { internal::StreamLocalCopy 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((reinterpret_cast(p) + 15) & static_cast(~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(&whitespace[0])); for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(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(&whitespace[0])); for (; p <= end - 16; p += 16) { const __m128i s = _mm_loadu_si128(reinterpret_cast(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((reinterpret_cast(p) + 15) & static_cast(~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(&whitespaces[0][0])); const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(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(~_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(&whitespaces[0][0])); const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); for (; p <= end - 16; p += 16) { const __m128i s = _mm_loadu_si128(reinterpret_cast(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(~_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((reinterpret_cast(p) + 15) & static_cast(~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(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(vreinterpretq_u64_u8(x), 0); // extract uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract if (low == 0) { if (high != 0) { uint32_t lz = internal::clzll(high); return p + 8 + (lz >> 3); } } else { uint32_t lz = internal::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(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(vreinterpretq_u64_u8(x), 0); // extract uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract if (low == 0) { if (high != 0) { uint32_t lz = internal::clzll(high); return p + 8 + (lz >> 3); } } else { uint32_t lz = internal::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(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, 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 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_(), state_(IterativeParsingStartState) {} //! 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 ParseResult Parse(InputStream& is, Handler& handler) { if (parseFlags & kParseIterativeFlag) return IterativeParse(is, handler); parseResult_.Clear(); ClearStackOnExit scope(*this); SkipWhitespaceAndComments(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(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); if (!(parseFlags & kParseStopWhenDoneFlag)) { SkipWhitespaceAndComments(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 ParseResult Parse(InputStream& is, Handler& handler) { return Parse(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 bool IterativeParseNext(InputStream& is, Handler& handler) { while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { SkipWhitespaceAndComments(is); Token t = Tokenize(is.Peek()); IterativeParsingState n = Predict(state_, t); IterativeParsingState d = Transit(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(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() const { return IsIterativeParsingCompleteState(state_); } //! Whether a parse error has occurred 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 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 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(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(is, handler, true); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ParseValue(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++memberCount; switch (is.Peek()) { case ',': is.Take(); SkipWhitespaceAndComments(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 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(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(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++elementCount; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (Consume(is, ',')) { SkipWhitespaceAndComments(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 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 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 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 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 hexadecimal digits in \uXXXX in ParseString(). template 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(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 class StackStream { public: typedef CharType Ch; StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} RAPIDJSON_FORCEINLINE void Put(Ch c) { *stack_.template Push() = c; ++length_; } RAPIDJSON_FORCEINLINE void* Push(SizeType count) { length_ += count; return stack_.template Push(count); } size_t Length() const { return length_; } Ch* Pop() { return stack_.template Pop(length_); } private: StackStream(const StackStream&); StackStream& operator=(const StackStream&); internal::Stack& stack_; SizeType length_; }; // Parse string and generate String event. Different code paths for kParseInsituFlag. template void ParseString(InputStream& is, Handler& handler, bool isKey = false) { internal::StreamLocalCopy 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(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(head); success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); } else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SizeType length = static_cast(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 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 optimization. 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 initial '\\' as error offset is.Take(); Ch e = is.Peek(); if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { is.Take(); os.Put(static_cast(escape[static_cast(e)])); } else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe is.Take(); os.Put('\''); } 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 <= 0xDFFF)) { // high surrogate, check if followed by valid low surrogate if (RAPIDJSON_LIKELY(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; } // single low surrogate else { RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); } } 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(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::Validate(is, os) : !Transcoder::Transcode(is, os)))) RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); } } } template static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { // Do nothing for generic version } #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) // StringStream -> StackStream static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& 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((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*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(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(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(_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(__builtin_ffs(r) - 1); #endif if (length != 0) { char* q = reinterpret_cast(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((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*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(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); for (;; p += 16, q += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(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(_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(__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((reinterpret_cast(p) + 15) & static_cast(~15)); for (; p != nextAligned; p++) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*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(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(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(_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(__builtin_ffs(r) - 1); #endif p += length; break; } } is.src_ = is.dst_ = p; } #elif defined(RAPIDJSON_NEON) // StringStream -> StackStream static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& 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((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*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(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(vreinterpretq_u64_u8(x), 0); // extract uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract SizeType length = 0; bool escaped = false; if (low == 0) { if (high != 0) { uint32_t lz = internal::clzll(high); length = 8 + (lz >> 3); escaped = true; } } else { uint32_t lz = internal::clzll(low); length = lz >> 3; escaped = true; } if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped if (length != 0) { char* q = reinterpret_cast(os.Push(length)); for (size_t i = 0; i < length; i++) q[i] = p[i]; p += length; } break; } vst1q_u8(reinterpret_cast(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((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*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(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(vreinterpretq_u64_u8(x), 0); // extract uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract SizeType length = 0; bool escaped = false; if (low == 0) { if (high != 0) { uint32_t lz = internal::clzll(high); length = 8 + (lz >> 3); escaped = true; } } else { uint32_t lz = internal::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(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((reinterpret_cast(p) + 15) & static_cast(~15)); for (; p != nextAligned; p++) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*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(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(vreinterpretq_u64_u8(x), 0); // extract uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract if (low == 0) { if (high != 0) { uint32_t lz = internal::clzll(high); p += 8 + (lz >> 3); break; } } else { uint32_t lz = internal::clzll(low); p += lz >> 3; break; } } is.src_ = is.dst_ = p; } #endif // RAPIDJSON_NEON template class NumberStream; template class NumberStream { 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 StackCharacter* Pop() { return 0; } protected: NumberStream& operator=(const NumberStream&); InputStream& is; }; template class NumberStream : public NumberStream { typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {} RAPIDJSON_FORCEINLINE Ch TakePush() { stackStream.Put(static_cast(Base::is.Peek())); return Base::is.Take(); } RAPIDJSON_FORCEINLINE void Push(StackCharacter c) { stackStream.Put(c); } size_t Length() { return stackStream.Length(); } const StackCharacter* Pop() { stackStream.Put('\0'); return stackStream.Pop(); } private: StackStream stackStream; }; template class NumberStream : public NumberStream { typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s) {} RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } }; template void ParseNumber(InputStream& is, Handler& handler) { typedef typename internal::SelectIf, typename TargetEncoding::Ch, char>::Type NumberCharacter; internal::StreamLocalCopy copy(is); NumberStream 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(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(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(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::quiet_NaN(); useNanOrInf = true; } } else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { if (Consume(s, 'n') && Consume(s, 'f')) { d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::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(i64); useDouble = true; break; } i64 = i64 * 10 + static_cast(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(i64); useDouble = true; break; } i64 = i64 * 10 + static_cast(s.TakePush() - '0'); significandDigit++; } } // Force double for big integer if (useDouble) { while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { 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(s.TakePush() - '0'); --expFrac; if (i64 != 0) significandDigit++; } } d = static_cast(i64); #else // Use double to store significand in 32-bit architecture d = static_cast(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(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(s.Take() - '0'); if (expMinus) { // (exp + expFrac) must not underflow int => we're detecting when -exp gets // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into // underflow territory): // // -(exp * 10 + 9) + expFrac >= INT_MIN // <=> exp <= (expFrac - INT_MIN - 9) / 10 RAPIDJSON_ASSERT(expFrac <= 0); int maxExp = (expFrac + 2147483639) / 10; while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = exp * 10 + static_cast(s.Take() - '0'); if (RAPIDJSON_UNLIKELY(exp > maxExp)) { 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(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(head); cont = handler.RawNumber(str, SizeType(length), false); } else { SizeType numCharsToCopy = static_cast(s.Length()); GenericStringStream > srcStream(s.Pop()); StackStream dstStream(stack_); while (numCharsToCopy--) { Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); } dstStream.Put('\0'); const typename TargetEncoding::Ch* str = dstStream.Pop(); const SizeType length = static_cast(dstStream.Length()) - 1; cont = handler.RawNumber(str, SizeType(length), true); } } else { size_t length = s.Length(); const NumberCharacter* 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); // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal if (d > (std::numeric_limits::max)()) { // Overflow // TODO: internal::StrtodX should report overflow (or underflow) RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); } cont = handler.Double(minus ? -d : d); } else if (useNanOrInf) { cont = handler.Double(d); } else { if (use64bit) { if (minus) cont = handler.Int64(static_cast(~i64 + 1)); else cont = handler.Uint64(i64); } else { if (minus) cont = handler.Int(static_cast(~i + 1)); else cont = handler.Uint(i); } } } if (RAPIDJSON_UNLIKELY(!cont)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); } // Parse any JSON value template void ParseValue(InputStream& is, Handler& handler) { switch (is.Peek()) { case 'n': ParseNull (is, handler); break; case 't': ParseTrue (is, handler); break; case 'f': ParseFalse (is, handler); break; case '"': ParseString(is, handler); break; case '{': ParseObject(is, handler); break; case '[': ParseArray (is, handler); break; default : ParseNumber(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) const { //!@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(c) < 256) return static_cast(tokenMap[static_cast(c)]); else return NumberToken; } RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { // 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(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 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 MemberValue) 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(1) = n; // Initialize and push the member/element count. *stack_.template Push(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(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(is, handler); if (HasParseError()) { return IterativeParsingErrorState; } return dst; case IterativeParsingElementState: // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. ParseValue(is, handler); if (HasParseError()) { return IterativeParsingErrorState; } return dst; case IterativeParsingMemberDelimiterState: case IterativeParsingElementDelimiterState: is.Take(); // Update member/element count. *stack_.template Top() = *stack_.template Top() + 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(1); // If the object is not empty, count the last member. if (src == IterativeParsingMemberValueState) ++c; // Restore the state. IterativeParsingState n = static_cast(*stack_.template Pop(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(1); // If the array is not empty, count the last element. if (src == IterativeParsingElementState) ++c; // Restore the state. IterativeParsingState n = static_cast(*stack_.template Pop(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(is, handler); if (HasParseError()) { return IterativeParsingErrorState; } return IterativeParsingFinishState; } } template 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) const { return s >= IterativeParsingElementDelimiterState; } RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { return s <= IterativeParsingErrorState; } template ParseResult IterativeParse(InputStream& is, Handler& handler) { parseResult_.Clear(); ClearStackOnExit scope(*this); IterativeParsingState state = IterativeParsingStartState; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); while (is.Peek() != '\0') { Token t = Tokenize(is.Peek()); IterativeParsingState n = Predict(state, t); IterativeParsingState d = Transit(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(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 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<> > Reader; RAPIDJSON_NAMESPACE_END #if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_READER_H_ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/istreamwrapper.h000664 001750 001750 00000007735 15164251010 040101 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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 #include #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) #elif defined(_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 class BasicIStreamWrapper { public: typedef typename StreamType::char_type Ch; //! Constructor. /*! \param stream stream opened for read. */ BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { Read(); } //! Constructor. /*! \param stream stream opened for read. \param buffer user-supplied buffer. \param bufferSize size of buffer in bytes. Must >=4 bytes. */ BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { 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(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 - !eof_ <= bufferLast_) ? current_ : 0; } private: BasicIStreamWrapper(); BasicIStreamWrapper(const BasicIStreamWrapper&); BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); void Read() { if (current_ < bufferLast_) ++current_; else if (!eof_) { count_ += readCount_; readCount_ = bufferSize_; bufferLast_ = buffer_ + readCount_ - 1; current_ = buffer_; if (!stream_.read(buffer_, static_cast(bufferSize_))) { readCount_ = static_cast(stream_.gcount()); *(bufferLast_ = buffer_ + readCount_) = '\0'; eof_ = true; } } } StreamType &stream_; Ch peekBuffer_[4], *buffer_; size_t bufferSize_; Ch *bufferLast_; Ch *current_; size_t readCount_; size_t count_; //!< Number of characters read bool eof_; }; typedef BasicIStreamWrapper IStreamWrapper; typedef BasicIStreamWrapper WIStreamWrapper; #if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_ISTREAMWRAPPER_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/endian_inl.h000664 001750 001750 00000006073 15164251010 035732 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2014 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Endian related functions. #ifndef WEBP_UTILS_ENDIAN_INL_H_ #define WEBP_UTILS_ENDIAN_INL_H_ #ifdef HAVE_CONFIG_H #include "../webp/config.h" #endif #include "../dsp/dsp.h" #include "../webp/types.h" // some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__) #if !defined(WORDS_BIGENDIAN) && \ (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \ (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))) #define WORDS_BIGENDIAN #endif #if defined(WORDS_BIGENDIAN) #define HToLE32 BSwap32 #define HToLE16 BSwap16 #else #define HToLE32(x) (x) #define HToLE16(x) (x) #endif #if !defined(HAVE_CONFIG_H) // clang-3.3 and gcc-4.3 have builtin functions for swap32/swap64 #if LOCAL_GCC_PREREQ(4,3) || LOCAL_CLANG_PREREQ(3,3) #define HAVE_BUILTIN_BSWAP32 #define HAVE_BUILTIN_BSWAP64 #endif // clang-3.3 and gcc-4.8 have a builtin function for swap16 #if LOCAL_GCC_PREREQ(4,8) || LOCAL_CLANG_PREREQ(3,3) #define HAVE_BUILTIN_BSWAP16 #endif #endif // !HAVE_CONFIG_H static WEBP_INLINE uint16_t BSwap16(uint16_t x) { #if defined(HAVE_BUILTIN_BSWAP16) return __builtin_bswap16(x); #elif defined(_MSC_VER) return _byteswap_ushort(x); #else // gcc will recognize a 'rorw $8, ...' here: return (x >> 8) | ((x & 0xff) << 8); #endif // HAVE_BUILTIN_BSWAP16 } static WEBP_INLINE uint32_t BSwap32(uint32_t x) { #if defined(WEBP_USE_MIPS32_R2) uint32_t ret; __asm__ volatile ( "wsbh %[ret], %[x] \n\t" "rotr %[ret], %[ret], 16 \n\t" : [ret]"=r"(ret) : [x]"r"(x) ); return ret; #elif defined(HAVE_BUILTIN_BSWAP32) return __builtin_bswap32(x); #elif defined(__i386__) || defined(__x86_64__) uint32_t swapped_bytes; __asm__ volatile("bswap %0" : "=r"(swapped_bytes) : "0"(x)); return swapped_bytes; #elif defined(_MSC_VER) return (uint32_t)_byteswap_ulong(x); #else return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); #endif // HAVE_BUILTIN_BSWAP32 } static WEBP_INLINE uint64_t BSwap64(uint64_t x) { #if defined(HAVE_BUILTIN_BSWAP64) return __builtin_bswap64(x); #elif defined(__x86_64__) uint64_t swapped_bytes; __asm__ volatile("bswapq %0" : "=r"(swapped_bytes) : "0"(x)); return swapped_bytes; #elif defined(_MSC_VER) return (uint64_t)_byteswap_uint64(x); #else // generic code for swapping 64-bit values (suggested by bdb@) x = ((x & 0xffffffff00000000ull) >> 32) | ((x & 0x00000000ffffffffull) << 32); x = ((x & 0xffff0000ffff0000ull) >> 16) | ((x & 0x0000ffff0000ffffull) << 16); x = ((x & 0xff00ff00ff00ff00ull) >> 8) | ((x & 0x00ff00ff00ff00ffull) << 8); return x; #endif // HAVE_BUILTIN_BSWAP64 } #endif // WEBP_UTILS_ENDIAN_INL_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlEffect.h000664 001750 001750 00000005335 15164251010 036041 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_EFFECT_H_ #define _TVG_GL_EFFECT_H_ #include "tvgRender.h" #include "tvgGlProgram.h" class GlEffect { private: GlStageBuffer* gpuBuffer; //shared resource with GlRenderer GlProgram* pBlurV{}; GlProgram* pBlurH{}; GlProgram* pDropShadow{}; GlProgram* pFill{}; GlProgram* pTint{}; GlProgram* pTritone{}; void update(RenderEffectGaussianBlur* effect, const Matrix& transform); void update(RenderEffectDropShadow* effect, const Matrix& transform); void update(RenderEffectFill* effect, const Matrix& transform); void update(RenderEffectTint* effect, const Matrix& transform); void update(RenderEffectTritone* effect, const Matrix& transform); bool region(RenderEffectGaussianBlur* effect); bool region(RenderEffectDropShadow* effect); GlRenderTask* render(RenderEffectGaussianBlur* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset); GlRenderTask* render(RenderEffectDropShadow* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset); GlRenderTask* render(RenderEffect* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset); public: GlEffect(GlStageBuffer* buffer); ~GlEffect(); void update(RenderEffect* effect, const Matrix& transform); bool region(RenderEffect* effect); bool render(RenderEffect* effect, GlRenderPass* pass, Array& blendPool); }; #endif /* _TVG_GL_EFFECT_H_ */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/dsp.h000664 001750 001750 00000030646 15164251010 034130 0ustar00ddennedyddennedy000000 000000 // Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Speed-critical functions. // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_DSP_DSP_H_ #define WEBP_DSP_DSP_H_ #ifdef HAVE_CONFIG_H #include "../webp/config.h" #endif #include "../webp/types.h" #ifdef __cplusplus extern "C" { #endif #define BPS 32 // this is the common stride for enc/dec //------------------------------------------------------------------------------ // CPU detection #if defined(__GNUC__) # define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__) # define LOCAL_GCC_PREREQ(maj, min) \ (LOCAL_GCC_VERSION >= (((maj) << 8) | (min))) #else # define LOCAL_GCC_VERSION 0 # define LOCAL_GCC_PREREQ(maj, min) 0 #endif #ifdef __clang__ # define LOCAL_CLANG_VERSION ((__clang_major__ << 8) | __clang_minor__) # define LOCAL_CLANG_PREREQ(maj, min) \ (LOCAL_CLANG_VERSION >= (((maj) << 8) | (min))) #else # define LOCAL_CLANG_VERSION 0 # define LOCAL_CLANG_PREREQ(maj, min) 0 #endif // __clang__ #if defined(_MSC_VER) && _MSC_VER > 1310 && \ (defined(_M_X64) || defined(_M_IX86)) #define WEBP_MSC_SSE2 // Visual C++ SSE2 targets #endif // WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp // files without intrinsics, allowing the corresponding Init() to be called. // Files containing intrinsics will need to be built targeting the instruction // set so should succeed on one of the earlier tests. #if defined(__SSE2__) || defined(WEBP_MSC_SSE2) || defined(WEBP_HAVE_SSE2) //#define WEBP_USE_SSE2 #endif // This macro prevents thread_sanitizer from reporting known concurrent writes. #define WEBP_TSAN_IGNORE_FUNCTION #if defined(__has_feature) #if __has_feature(thread_sanitizer) #undef WEBP_TSAN_IGNORE_FUNCTION #define WEBP_TSAN_IGNORE_FUNCTION __attribute__((no_sanitize_thread)) #endif #endif typedef enum { kSSE2, } CPUFeature; // returns true if the CPU supports the feature. typedef int (*VP8CPUInfo)(CPUFeature feature); WEBP_EXTERN(VP8CPUInfo) VP8GetCPUInfo; //------------------------------------------------------------------------------ // Init stub generator // Defines an init function stub to ensure each module exposes a symbol, // avoiding a compiler warning. #define WEBP_DSP_INIT_STUB(func) \ extern void func(void); \ WEBP_TSAN_IGNORE_FUNCTION void func(void) {} //------------------------------------------------------------------------------ // Decoding typedef void (*VP8DecIdct)(const int16_t* coeffs, uint8_t* dst); // when doing two transforms, coeffs is actually int16_t[2][16]. typedef void (*VP8DecIdct2)(const int16_t* coeffs, uint8_t* dst, int do_two); extern VP8DecIdct2 VP8Transform; extern VP8DecIdct VP8TransformAC3; extern VP8DecIdct VP8TransformUV; extern VP8DecIdct VP8TransformDC; extern VP8DecIdct VP8TransformDCUV; // *dst is the destination block, with stride BPS. Boundary samples are // assumed accessible when needed. typedef void (*VP8PredFunc)(uint8_t* dst); extern VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */]; extern VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */]; extern VP8PredFunc VP8PredLuma4[/* NUM_BMODES */]; // clipping tables (for filtering) extern const int8_t* const VP8ksclip1; // clips [-1020, 1020] to [-128, 127] extern const int8_t* const VP8ksclip2; // clips [-112, 112] to [-16, 15] extern const uint8_t* const VP8kclip1; // clips [-255,511] to [0,255] extern const uint8_t* const VP8kabs0; // abs(x) for x in [-255,255] // must be called first void VP8InitClipTables(void); // simple filter (only for luma) typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh); extern VP8SimpleFilterFunc VP8SimpleVFilter16; extern VP8SimpleFilterFunc VP8SimpleHFilter16; extern VP8SimpleFilterFunc VP8SimpleVFilter16i; // filter 3 inner edges extern VP8SimpleFilterFunc VP8SimpleHFilter16i; // regular filter (on both macroblock edges and inner edges) typedef void (*VP8LumaFilterFunc)(uint8_t* luma, int stride, int thresh, int ithresh, int hev_t); typedef void (*VP8ChromaFilterFunc)(uint8_t* u, uint8_t* v, int stride, int thresh, int ithresh, int hev_t); // on outer edge extern VP8LumaFilterFunc VP8VFilter16; extern VP8LumaFilterFunc VP8HFilter16; extern VP8ChromaFilterFunc VP8VFilter8; extern VP8ChromaFilterFunc VP8HFilter8; // on inner edge extern VP8LumaFilterFunc VP8VFilter16i; // filtering 3 inner edges altogether extern VP8LumaFilterFunc VP8HFilter16i; extern VP8ChromaFilterFunc VP8VFilter8i; // filtering u and v altogether extern VP8ChromaFilterFunc VP8HFilter8i; // must be called before anything using the above void VP8DspInit(void); //------------------------------------------------------------------------------ // WebP I/O //#define FANCY_UPSAMPLING // undefined to remove fancy upsampling support // Convert a pair of y/u/v lines together to the output rgb/a colorspace. // bottom_y can be NULL if only one line of output is needed (at top/bottom). typedef void (*WebPUpsampleLinePairFunc)( const uint8_t* top_y, const uint8_t* bottom_y, const uint8_t* top_u, const uint8_t* top_v, const uint8_t* cur_u, const uint8_t* cur_v, uint8_t* top_dst, uint8_t* bottom_dst, int len); #ifdef FANCY_UPSAMPLING // Fancy upsampling functions to convert YUV to RGB(A) modes extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; #endif // FANCY_UPSAMPLING // Per-row point-sampling methods. typedef void (*WebPSamplerRowFunc)(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst, int len); // Generic function to apply 'WebPSamplerRowFunc' to the whole plane: void WebPSamplerProcessPlane(const uint8_t* y, int y_stride, const uint8_t* u, const uint8_t* v, int uv_stride, uint8_t* dst, int dst_stride, int width, int height, WebPSamplerRowFunc func); // Sampling functions to convert rows of YUV to RGB(A) extern WebPSamplerRowFunc WebPSamplers[/* MODE_LAST */]; // General function for converting two lines of ARGB or RGBA. // 'alpha_is_last' should be true if 0xff000000 is stored in memory as // as 0x00, 0x00, 0x00, 0xff (little endian). WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last); // YUV444->RGB converters typedef void (*WebPYUV444Converter)(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst, int len); extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */]; // Must be called before using the WebPUpsamplers[] (and for premultiplied // colorspaces like rgbA, rgbA4444, etc) void WebPInitUpsamplers(void); // Must be called before using WebPSamplers[] void WebPInitSamplers(void); // Must be called before using WebPYUV444Converters[] void WebPInitYUV444Converters(void); //------------------------------------------------------------------------------ // Rescaler struct WebPRescaler; // Import a row of data and save its contribution in the rescaler. // 'channel' denotes the channel number to be imported. extern void (*WebPRescalerImportRow)(struct WebPRescaler* const wrk, const uint8_t* const src, int channel); // Export one row (starting at x_out position) from rescaler. extern void (*WebPRescalerExportRow)(struct WebPRescaler* const wrk, int x_out); // Plain-C implementation, as fall-back. extern void WebPRescalerExportRowC(struct WebPRescaler* const wrk, int x_out); // Must be called first before using the above. void WebPRescalerDspInit(void); //------------------------------------------------------------------------------ // Utilities for processing transparent channel. // Apply alpha pre-multiply on an rgba, bgra or argb plane of size w * h. // alpha_first should be 0 for argb, 1 for rgba or bgra (where alpha is last). extern void (*WebPApplyAlphaMultiply)( uint8_t* rgba, int alpha_first, int w, int h, int stride); // Same, buf specifically for RGBA4444 format extern void (*WebPApplyAlphaMultiply4444)( uint8_t* rgba4444, int w, int h, int stride); // Dispatch the values from alpha[] plane to the ARGB destination 'dst'. // Returns true if alpha[] plane has non-trivial values different from 0xff. extern int (*WebPDispatchAlpha)(const uint8_t* alpha, int alpha_stride, int width, int height, uint8_t* dst, int dst_stride); // Transfer packed 8b alpha[] values to green channel in dst[], zero'ing the // A/R/B values. 'dst_stride' is the stride for dst[] in uint32_t units. extern void (*WebPDispatchAlphaToGreen)(const uint8_t* alpha, int alpha_stride, int width, int height, uint32_t* dst, int dst_stride); // Extract the alpha values from 32b values in argb[] and pack them into alpha[] // (this is the opposite of WebPDispatchAlpha). // Returns true if there's only trivial 0xff alpha values. extern int (*WebPExtractAlpha)(const uint8_t* argb, int argb_stride, int width, int height, uint8_t* alpha, int alpha_stride); // Pre-Multiply operation transforms x into x * A / 255 (where x=Y,R,G or B). // Un-Multiply operation transforms x into x * 255 / A. // Pre-Multiply or Un-Multiply (if 'inverse' is true) argb values in a row. extern void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse); // Same a WebPMultARGBRow(), but for several rows. void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows, int inverse); // Same for a row of single values, with side alpha values. extern void (*WebPMultRow)(uint8_t* const ptr, const uint8_t* const alpha, int width, int inverse); // Same a WebPMultRow(), but for several 'num_rows' rows. void WebPMultRows(uint8_t* ptr, int stride, const uint8_t* alpha, int alpha_stride, int width, int num_rows, int inverse); // Plain-C versions, used as fallback by some implementations. void WebPMultRowC(uint8_t* const ptr, const uint8_t* const alpha, int width, int inverse); void WebPMultARGBRowC(uint32_t* const ptr, int width, int inverse); // To be called first before using the above. void WebPInitAlphaProcessing(void); // ARGB packing function: a/r/g/b input is rgba or bgra order. extern void (*VP8PackARGB)(const uint8_t* a, const uint8_t* r, const uint8_t* g, const uint8_t* b, int len, uint32_t* out); // RGB packing function. 'step' can be 3 or 4. r/g/b input is rgb or bgr order. extern void (*VP8PackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b, int len, int step, uint32_t* out); //------------------------------------------------------------------------------ // Filter functions typedef enum { // Filter types. WEBP_FILTER_NONE = 0, WEBP_FILTER_HORIZONTAL, WEBP_FILTER_VERTICAL, WEBP_FILTER_GRADIENT, WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker WEBP_FILTER_BEST, // meta-types WEBP_FILTER_FAST } WEBP_FILTER_TYPE; typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height, int stride, uint8_t* out); typedef void (*WebPUnfilterFunc)(int width, int height, int stride, int row, int num_rows, uint8_t* data); // Filter the given data using the given predictor. // 'in' corresponds to a 2-dimensional pixel array of size (stride * height) // in raster order. // 'stride' is number of bytes per scan line (with possible padding). // 'out' should be pre-allocated. extern WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; // In-place reconstruct the original data from the given filtered data. // The reconstruction will be done for 'num_rows' rows starting from 'row' // (assuming rows upto 'row - 1' are already reconstructed). extern WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; // To be called first before using the above. void VP8FiltersInit(void); #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_DSP_DSP_H_ */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp000664 001750 001750 00000006764 15164251010 037503 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include "tvgWebpLoader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ void WebpLoader::run(unsigned tid) { //TODO: acquire the current colorspace format & pre-multiplied alpha image. surface.buf8 = WebPDecodeBGRA(data, size, nullptr, nullptr); surface.stride = (uint32_t)w; surface.w = (uint32_t)w; surface.h = (uint32_t)h; surface.channelSize = sizeof(uint32_t); surface.cs = ColorSpace::ARGB8888; surface.premultiplied = false; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ WebpLoader::WebpLoader() : ImageLoader(FileType::Webp) { } WebpLoader::~WebpLoader() { done(); if (freeData) tvg::free(data); data = nullptr; size = 0; freeData = false; WebPFree(surface.buf8); } bool WebpLoader::open(const char* path) { #ifdef THORVG_FILE_IO_SUPPORT if (!(data = (unsigned char*)LoadModule::open(path, size))) return false; int width, height; if (!WebPGetInfo(data, size, &width, &height)) return false; w = static_cast(width); h = static_cast(height); freeData = true; return true; #else return false; #endif } bool WebpLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy) { if (copy) { this->data = tvg::malloc(size); if (!this->data) return false; memcpy((unsigned char *)this->data, data, size); freeData = true; } else { this->data = (unsigned char *) data; freeData = false; } int width, height; if (!WebPGetInfo(this->data, size, &width, &height)) return false; w = static_cast(width); h = static_cast(height); surface.cs = ColorSpace::ARGB8888; this->size = size; return true; } bool WebpLoader::read() { if (!LoadModule::read()) return true; if (!data || w == 0 || h == 0) return false; TaskScheduler::request(this); return true; } RenderSurface* WebpLoader::bitmap() { this->done(); return ImageLoader::bitmap(); }external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-conversion.cpp000664 001750 001750 00000064106 15164251010 046212 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /** * Implementation of ECMA-defined conversion routines */ #include "ecma-conversion.h" #include #include "ecma-alloc.h" #include "ecma-bigint-object.h" #include "ecma-bigint.h" #include "ecma-boolean-object.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "ecma-number-object.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "ecma-symbol-object.h" #include "jrt-libc-includes.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaconversion ECMA conversion routines * @{ */ /** * RequireObjectCoercible operation. * * See also: * ECMA-262 v11, 7.2.1 * * @return true - if the value can be coerced to object without any exceptions * false - otherwise */ bool ecma_op_require_object_coercible (ecma_value_t value) /**< ecma value */ { ecma_check_value_type_is_spec_defined (value); if (ecma_is_value_undefined (value) || ecma_is_value_null (value)) { ecma_raise_type_error (ECMA_ERR_ARGUMENT_CANNOT_CONVERT_TO_OBJECT); return false; } return true; } /* ecma_op_require_object_coercible */ /** * SameValue operation. * * See also: * ECMA-262 v5, 9.12 * * @return true - if the value are same according to ECMA-defined SameValue algorithm, * false - otherwise */ bool ecma_op_same_value (ecma_value_t x, /**< ecma value */ ecma_value_t y) /**< ecma value */ { if (x == y) { return true; } ecma_type_t type_of_x = ecma_get_value_type_field (x); if (type_of_x != ecma_get_value_type_field (y) || type_of_x == ECMA_TYPE_DIRECT) { return false; } if (ecma_is_value_number (x)) { ecma_number_t x_num = ecma_get_number_from_value (x); ecma_number_t y_num = ecma_get_number_from_value (y); bool is_x_nan = ecma_number_is_nan (x_num); bool is_y_nan = ecma_number_is_nan (y_num); if (is_x_nan || is_y_nan) { return is_x_nan && is_y_nan; } if (ecma_number_is_zero (x_num) && ecma_number_is_zero (y_num) && ecma_number_is_negative (x_num) != ecma_number_is_negative (y_num)) { return false; } return (x_num == y_num); } if (ecma_is_value_string (x)) { ecma_string_t *x_str_p = ecma_get_string_from_value (x); ecma_string_t *y_str_p = ecma_get_string_from_value (y); return ecma_compare_ecma_strings (x_str_p, y_str_p); } #if JERRY_BUILTIN_BIGINT if (ecma_is_value_bigint (x)) { return (ecma_is_value_bigint (y) && ecma_bigint_compare_to_bigint (x, y) == 0); } #endif /* JERRY_BUILTIN_BIGINT */ JERRY_ASSERT (ecma_is_value_object (x) || ecma_is_value_symbol (x)); return false; } /* ecma_op_same_value */ #if JERRY_BUILTIN_CONTAINER /** * SameValueZero operation. * * See also: * ECMA-262 v6, 7.2.10 * * @return true - if the value are same according to ECMA-defined SameValueZero algorithm, * false - otherwise */ bool ecma_op_same_value_zero (ecma_value_t x, /**< ecma value */ ecma_value_t y, /**< ecma value */ bool strict_equality) /**< strict equality */ { if (ecma_is_value_number (x) && ecma_is_value_number (y)) { ecma_number_t x_num = ecma_get_number_from_value (x); ecma_number_t y_num = ecma_get_number_from_value (y); bool is_x_nan = ecma_number_is_nan (x_num); bool is_y_nan = ecma_number_is_nan (y_num); if (strict_equality && is_x_nan && is_y_nan) { return false; } if (is_x_nan || is_y_nan) { return (is_x_nan && is_y_nan); } if (ecma_number_is_zero (x_num) && ecma_number_is_zero (y_num) && ecma_number_is_negative (x_num) != ecma_number_is_negative (y_num)) { return true; } return (x_num == y_num); } return ecma_op_same_value (x, y); } /* ecma_op_same_value_zero */ #endif /* JERRY_BUILTIN_CONTAINER */ /** * ToPrimitive operation. * * See also: * ECMA-262 v5, 9.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_to_primitive (ecma_value_t value, /**< ecma value */ ecma_preferred_type_hint_t preferred_type) /**< preferred type hint */ { ecma_check_value_type_is_spec_defined (value); if (ecma_is_value_object (value)) { ecma_object_t *obj_p = ecma_get_object_from_value (value); return ecma_op_object_default_value (obj_p, preferred_type); } else { return ecma_copy_value (value); } } /* ecma_op_to_primitive */ /** * ToBoolean operation. Cannot throw an exception. * * See also: * ECMA-262 v5, 9.2 * * @return true - if the logical value is true * false - otherwise */ bool ecma_op_to_boolean (ecma_value_t value) /**< ecma value */ { ecma_check_value_type_is_spec_defined (value); if (ecma_is_value_simple (value)) { JERRY_ASSERT (ecma_is_value_boolean (value) || ecma_is_value_undefined (value) || ecma_is_value_null (value)); return ecma_is_value_true (value); } if (ecma_is_value_integer_number (value)) { return (value != ecma_make_integer_value (0)); } if (ecma_is_value_float_number (value)) { ecma_number_t num = ecma_get_float_from_value (value); return (!ecma_number_is_nan (num) && !ecma_number_is_zero (num)); } if (ecma_is_value_string (value)) { ecma_string_t *str_p = ecma_get_string_from_value (value); return !ecma_string_is_empty (str_p); } #if JERRY_BUILTIN_BIGINT if (ecma_is_value_bigint (value)) { return value != ECMA_BIGINT_ZERO; } #endif /* JERRY_BUILTIN_BIGINT */ JERRY_ASSERT (ecma_is_value_object (value) || ecma_is_value_symbol (value)); return true; } /* ecma_op_to_boolean */ /** * ToNumber operation. * * See also: * ECMA-262 v5, 9.3 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_to_number (ecma_value_t value, /**< ecma value */ ecma_number_t *number_p) /**< [out] ecma number */ { return ecma_op_to_numeric (value, number_p, ECMA_TO_NUMERIC_NO_OPTS); } /* ecma_op_to_number */ /** * Helper to get the numeric value of an ecma value * * See also: * ECMA-262 v11, 7.1.3 * * @return ECMA_VALUE_EMPTY if converted to number, BigInt if * converted to BigInt, and conversion error otherwise * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_to_numeric (ecma_value_t value, /**< ecma value */ ecma_number_t *number_p, /**< [out] ecma number */ ecma_to_numeric_options_t options) /**< option bits */ { JERRY_UNUSED (options); if (ecma_is_value_integer_number (value)) { *number_p = (ecma_number_t) ecma_get_integer_from_value (value); return ECMA_VALUE_EMPTY; } if (ecma_is_value_float_number (value)) { *number_p = ecma_get_float_from_value (value); return ECMA_VALUE_EMPTY; } if (ecma_is_value_string (value)) { ecma_string_t *str_p = ecma_get_string_from_value (value); *number_p = ecma_string_to_number (str_p); return ECMA_VALUE_EMPTY; } if (ecma_is_value_undefined (value)) { *number_p = ecma_number_make_nan (); return ECMA_VALUE_EMPTY; } if (ecma_is_value_null (value)) { *number_p = 0; return ECMA_VALUE_EMPTY; } if (ecma_is_value_true (value)) { *number_p = 1; return ECMA_VALUE_EMPTY; } if (ecma_is_value_false (value)) { *number_p = 0; return ECMA_VALUE_EMPTY; } if (ecma_is_value_symbol (value)) { return ecma_raise_type_error (ECMA_ERR_CONVERT_SYMBOL_TO_NUMBER); } #if JERRY_BUILTIN_BIGINT if (ecma_is_value_bigint (value)) { if (options & ECMA_TO_NUMERIC_ALLOW_BIGINT) { return ecma_copy_value (value); } return ecma_raise_type_error (ECMA_ERR_CONVERT_BIGINT_TO_NUMBER); } #endif /* JERRY_BUILTIN_BIGINT */ JERRY_ASSERT (ecma_is_value_object (value)); ecma_object_t *object_p = ecma_get_object_from_value (value); ecma_value_t def_value = ecma_op_object_default_value (object_p, ECMA_PREFERRED_TYPE_NUMBER); if (ECMA_IS_VALUE_ERROR (def_value)) { return def_value; } ecma_value_t ret_value = ecma_op_to_numeric (def_value, number_p, options); ecma_fast_free_value (def_value); return ret_value; } /* ecma_op_to_numeric */ /** * ToString operation. * * See also: * ECMA-262 v5, 9.8 * * @return NULL - if the conversion fails * pointer to the string descriptor - otherwise */ ecma_string_t * ecma_op_to_string (ecma_value_t value) /**< ecma value */ { ecma_check_value_type_is_spec_defined (value); if (ecma_is_value_string (value)) { ecma_string_t *res_p = ecma_get_string_from_value (value); ecma_ref_ecma_string (res_p); return res_p; } if (ecma_is_value_integer_number (value)) { ecma_integer_value_t num = ecma_get_integer_from_value (value); if (num < 0) { return ecma_new_ecma_string_from_number ((ecma_number_t) num); } else { return ecma_new_ecma_string_from_uint32 ((uint32_t) num); } } if (ecma_is_value_float_number (value)) { ecma_number_t num = ecma_get_float_from_value (value); return ecma_new_ecma_string_from_number (num); } if (ecma_is_value_undefined (value)) { return ecma_get_magic_string (LIT_MAGIC_STRING_UNDEFINED); } if (ecma_is_value_null (value)) { return ecma_get_magic_string (LIT_MAGIC_STRING_NULL); } if (ecma_is_value_true (value)) { return ecma_get_magic_string (LIT_MAGIC_STRING_TRUE); } if (ecma_is_value_false (value)) { return ecma_get_magic_string (LIT_MAGIC_STRING_FALSE); } if (ecma_is_value_symbol (value)) { ecma_raise_type_error (ECMA_ERR_CONVERT_SYMBOL_TO_STRING); return NULL; } #if JERRY_BUILTIN_BIGINT if (ecma_is_value_bigint (value)) { return ecma_bigint_to_string (value, 10); } #endif /* JERRY_BUILTIN_BIGINT */ JERRY_ASSERT (ecma_is_value_object (value)); ecma_object_t *obj_p = ecma_get_object_from_value (value); ecma_value_t def_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_STRING); if (ECMA_IS_VALUE_ERROR (def_value)) { return NULL; } ecma_string_t *ret_string_p = ecma_op_to_string (def_value); ecma_free_value (def_value); return ret_string_p; } /* ecma_op_to_string */ /** * ToPropertyKey operation. * * See also: * ECMA 262 v6, 7.1.14 * ECMA 262 v10, 7.1.14 * ECMA 262 v11, 7.1.19 * * @return NULL - if the conversion fails * ecma-string - otherwise */ ecma_string_t * ecma_op_to_property_key (ecma_value_t value) /**< ecma value */ { /* Fast path for strings and symbols */ if (JERRY_LIKELY (ecma_is_value_prop_name (value))) { ecma_string_t *key_p = ecma_get_prop_name_from_value (value); ecma_ref_ecma_string (key_p); return key_p; } ecma_value_t key = ecma_op_to_primitive (value, ECMA_PREFERRED_TYPE_STRING); if (ECMA_IS_VALUE_ERROR (key)) { return NULL; } if (ecma_is_value_symbol (key)) { ecma_string_t *symbol_p = ecma_get_symbol_from_value (key); return symbol_p; } ecma_string_t *result = ecma_op_to_string (key); ecma_free_value (key); return result; } /* ecma_op_to_property_key */ /** * ToObject operation. * * See also: * ECMA-262 v5, 9.9 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_to_object (ecma_value_t value) /**< ecma value */ { ecma_check_value_type_is_spec_defined (value); ecma_builtin_id_t proto_id = ECMA_BUILTIN_ID_OBJECT_PROTOTYPE; uint8_t class_type; if (ecma_is_value_number (value)) { #if JERRY_BUILTIN_NUMBER proto_id = ECMA_BUILTIN_ID_NUMBER_PROTOTYPE; #endif /* JERRY_BUILTIN_NUMBER */ class_type = ECMA_OBJECT_CLASS_NUMBER; } else if (ecma_is_value_string (value)) { #if JERRY_BUILTIN_STRING proto_id = ECMA_BUILTIN_ID_STRING_PROTOTYPE; #endif /* JERRY_BUILTIN_STRING */ class_type = ECMA_OBJECT_CLASS_STRING; } else if (ecma_is_value_object (value)) { return ecma_copy_value (value); } else if (ecma_is_value_symbol (value)) { proto_id = ECMA_BUILTIN_ID_SYMBOL_PROTOTYPE; class_type = ECMA_OBJECT_CLASS_SYMBOL; } #if JERRY_BUILTIN_BIGINT else if (ecma_is_value_bigint (value)) { return ecma_op_create_bigint_object (value); } #endif /* JERRY_BUILTIN_BIGINT */ else { if (ecma_is_value_undefined (value) || ecma_is_value_null (value)) { return ecma_raise_type_error (ECMA_ERR_ARGUMENT_CANNOT_CONVERT_TO_OBJECT); } else { JERRY_ASSERT (ecma_is_value_boolean (value)); #if JERRY_BUILTIN_BOOLEAN proto_id = ECMA_BUILTIN_ID_BOOLEAN_PROTOTYPE; #endif /* JERRY_BUILTIN_BOOLEAN */ class_type = ECMA_OBJECT_CLASS_BOOLEAN; } } ecma_object_t *object_p = ecma_create_object (ecma_builtin_get (proto_id), sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.type = class_type; ext_object_p->u.cls.u3.value = ecma_copy_value_if_not_object (value); return ecma_make_object_value (object_p); } /* ecma_op_to_object */ /** * FromPropertyDescriptor operation. * * See also: * ECMA-262 v5, 8.10.4 * * @return constructed object */ ecma_object_t * ecma_op_from_property_descriptor (const ecma_property_descriptor_t *src_prop_desc_p) /**< property descriptor */ { /* 2. */ ecma_object_t *obj_p = ecma_op_create_object_object_noarg (); ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); { prop_desc.flags = (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE_DEFINED | JERRY_PROP_IS_WRITABLE | JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE | JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE); } /* 3. */ if (src_prop_desc_p->flags & (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE_DEFINED)) { /* a. */ prop_desc.value = src_prop_desc_p->value; ecma_op_object_define_own_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_VALUE), &prop_desc); /* b. */ prop_desc.value = ecma_make_boolean_value (src_prop_desc_p->flags & JERRY_PROP_IS_WRITABLE); ecma_op_object_define_own_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_WRITABLE), &prop_desc); } else if (src_prop_desc_p->flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED)) { /* a. */ if (src_prop_desc_p->get_p == NULL) { prop_desc.value = ECMA_VALUE_UNDEFINED; } else { prop_desc.value = ecma_make_object_value (src_prop_desc_p->get_p); } ecma_op_object_define_own_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_GET), &prop_desc); /* b. */ if (src_prop_desc_p->set_p == NULL) { prop_desc.value = ECMA_VALUE_UNDEFINED; } else { prop_desc.value = ecma_make_object_value (src_prop_desc_p->set_p); } ecma_op_object_define_own_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_SET), &prop_desc); } prop_desc.value = ecma_make_boolean_value (src_prop_desc_p->flags & JERRY_PROP_IS_ENUMERABLE); ecma_op_object_define_own_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_ENUMERABLE), &prop_desc); prop_desc.value = ecma_make_boolean_value (src_prop_desc_p->flags & JERRY_PROP_IS_CONFIGURABLE); ecma_op_object_define_own_property (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_CONFIGURABLE), &prop_desc); return obj_p; } /* ecma_op_from_property_descriptor */ /** * ToPropertyDescriptor operation. * * See also: * ECMA-262 v5, 8.10.5 * * @return ECMA_VALUE_EMPTY if successful, ECMA_VALUE_ERROR otherwise */ ecma_value_t ecma_op_to_property_descriptor (ecma_value_t obj_value, /**< object value */ ecma_property_descriptor_t *out_prop_desc_p) /**< [out] filled property descriptor * if the operation is successful, * unmodified otherwise */ { /* 1. */ if (!ecma_is_value_object (obj_value)) { return ecma_raise_type_error (ECMA_ERR_EXPECTED_AN_OBJECT); } ecma_object_t *obj_p = ecma_get_object_from_value (obj_value); ecma_value_t ret_value = ECMA_VALUE_ERROR; ecma_value_t set_prop_value; ecma_value_t get_prop_value; ecma_value_t writable_prop_value; ecma_value_t value_prop_value; ecma_value_t configurable_prop_value; ecma_property_descriptor_t prop_desc; ecma_value_t enumerable_prop_value; /* 3. */ enumerable_prop_value = ecma_op_object_find (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_ENUMERABLE)); if (ECMA_IS_VALUE_ERROR (enumerable_prop_value)) { return enumerable_prop_value; } /* 2. */ prop_desc = ecma_make_empty_property_descriptor (); if (ecma_is_value_found (enumerable_prop_value)) { uint32_t is_enumerable = (ecma_op_to_boolean (enumerable_prop_value) ? JERRY_PROP_IS_ENUMERABLE : JERRY_PROP_NO_OPTS); prop_desc.flags |= (uint16_t) (JERRY_PROP_IS_ENUMERABLE_DEFINED | is_enumerable); ecma_free_value (enumerable_prop_value); } /* 4. */ configurable_prop_value = ecma_op_object_find (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_CONFIGURABLE)); if (ECMA_IS_VALUE_ERROR (configurable_prop_value)) { goto free_desc; } if (ecma_is_value_found (configurable_prop_value)) { uint32_t is_configurable = (ecma_op_to_boolean (configurable_prop_value) ? JERRY_PROP_IS_CONFIGURABLE : JERRY_PROP_NO_OPTS); prop_desc.flags |= (uint16_t) (JERRY_PROP_IS_CONFIGURABLE_DEFINED | is_configurable); ecma_free_value (configurable_prop_value); } /* 5. */ value_prop_value = ecma_op_object_find (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_VALUE)); if (ECMA_IS_VALUE_ERROR (value_prop_value)) { goto free_desc; } if (ecma_is_value_found (value_prop_value)) { prop_desc.flags |= JERRY_PROP_IS_VALUE_DEFINED; prop_desc.value = ecma_copy_value (value_prop_value); ecma_free_value (value_prop_value); } /* 6. */ writable_prop_value = ecma_op_object_find (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_WRITABLE)); if (ECMA_IS_VALUE_ERROR (writable_prop_value)) { goto free_desc; } if (ecma_is_value_found (writable_prop_value)) { uint32_t is_writable = (ecma_op_to_boolean (writable_prop_value) ? JERRY_PROP_IS_WRITABLE : JERRY_PROP_NO_OPTS); prop_desc.flags |= (uint16_t) (JERRY_PROP_IS_WRITABLE_DEFINED | is_writable); ecma_free_value (writable_prop_value); } /* 7. */ get_prop_value = ecma_op_object_find (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_GET)); if (ECMA_IS_VALUE_ERROR (get_prop_value)) { goto free_desc; } if (ecma_is_value_found (get_prop_value)) { if (!ecma_op_is_callable (get_prop_value) && !ecma_is_value_undefined (get_prop_value)) { ecma_free_value (get_prop_value); ret_value = ecma_raise_type_error (ECMA_ERR_EXPECTED_A_FUNCTION); goto free_desc; } prop_desc.flags |= JERRY_PROP_IS_GET_DEFINED; if (ecma_is_value_undefined (get_prop_value)) { prop_desc.get_p = NULL; } else { JERRY_ASSERT (ecma_is_value_object (get_prop_value)); ecma_object_t *get_p = ecma_get_object_from_value (get_prop_value); ecma_ref_object (get_p); prop_desc.get_p = get_p; } ecma_free_value (get_prop_value); } /* 8. */ set_prop_value = ecma_op_object_find (obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_SET)); if (ECMA_IS_VALUE_ERROR (set_prop_value)) { goto free_desc; } if (ecma_is_value_found (set_prop_value)) { if (!ecma_op_is_callable (set_prop_value) && !ecma_is_value_undefined (set_prop_value)) { ecma_free_value (set_prop_value); ret_value = ecma_raise_type_error (ECMA_ERR_EXPECTED_A_FUNCTION); goto free_desc; } prop_desc.flags |= JERRY_PROP_IS_SET_DEFINED; if (ecma_is_value_undefined (set_prop_value)) { prop_desc.set_p = NULL; } else { JERRY_ASSERT (ecma_is_value_object (set_prop_value)); ecma_object_t *set_p = ecma_get_object_from_value (set_prop_value); ecma_ref_object (set_p); prop_desc.set_p = set_p; } ecma_free_value (set_prop_value); } /* 9. */ if ((prop_desc.flags & (JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_IS_WRITABLE_DEFINED)) && (prop_desc.flags & (JERRY_PROP_IS_GET_DEFINED | JERRY_PROP_IS_SET_DEFINED))) { ret_value = ecma_raise_type_error (ECMA_ERR_ACCESSOR_WRITABLE); } else { *out_prop_desc_p = prop_desc; ret_value = ECMA_VALUE_EMPTY; } free_desc: if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_free_property_descriptor (&prop_desc); } return ret_value; } /* ecma_op_to_property_descriptor */ /** * IsInteger operation. * * See also: * ECMA-262 v5, 9.4 * ECMA-262 v6, 7.1.4 * * @return true - if the argument is integer * false - otherwise */ bool ecma_op_is_integer (ecma_number_t num) /**< ecma number */ { if (ecma_number_is_nan (num) || ecma_number_is_infinity (num)) { return false; } ecma_number_t floor_fabs = (ecma_number_t) floor (fabs (num)); ecma_number_t fabs_value = (ecma_number_t) fabs (num); return (floor_fabs == fabs_value); } /* ecma_op_is_integer */ /** * ToInteger operation. * * See also: * ECMA-262 v5, 9.4 * ECMA-262 v6, 7.1.4 * * @return ECMA_VALUE_EMPTY if successful * conversion error otherwise */ ecma_value_t ecma_op_to_integer (ecma_value_t value, /**< ecma value */ ecma_number_t *number_p) /**< [out] ecma number */ { if (ECMA_IS_VALUE_ERROR (value)) { return value; } /* 1 */ ecma_value_t to_number = ecma_op_to_number (value, number_p); /* 2 */ if (ECMA_IS_VALUE_ERROR (to_number)) { return to_number; } ecma_number_t number = *number_p; /* 3 */ if (ecma_number_is_nan (number)) { *number_p = ECMA_NUMBER_ZERO; return ECMA_VALUE_EMPTY; } /* 4 */ if (ecma_number_is_zero (number) || ecma_number_is_infinity (number)) { return ECMA_VALUE_EMPTY; } ecma_number_t floor_fabs = (ecma_number_t) floor (fabs (number)); /* 5 */ *number_p = ecma_number_is_negative (number) ? -floor_fabs : floor_fabs; return ECMA_VALUE_EMPTY; } /* ecma_op_to_integer */ /** * ToLength operation. * * See also: * ECMA-262 v6, 7.1.15 * * @return ECMA_VALUE_EMPTY if successful * conversion error otherwise */ ecma_value_t ecma_op_to_length (ecma_value_t value, /**< ecma value */ ecma_length_t *length) /**< [out] ecma number */ { /* 1 */ if (ECMA_IS_VALUE_ERROR (value)) { return value; } /* 2 */ ecma_number_t num; ecma_value_t length_num = ecma_op_to_integer (value, &num); /* 3 */ if (ECMA_IS_VALUE_ERROR (length_num)) { return length_num; } /* 4 */ if (num <= 0) { *length = 0; return ECMA_VALUE_EMPTY; } /* 5 */ if (num >= ECMA_NUMBER_MAX_SAFE_INTEGER) { *length = (ecma_length_t) ECMA_NUMBER_MAX_SAFE_INTEGER; return ECMA_VALUE_EMPTY; } /* 6 */ *length = (ecma_length_t) num; return ECMA_VALUE_EMPTY; } /* ecma_op_to_length */ /** * ToIndex operation. * * See also: * ECMA-262 v11, 7.1.22 * * @return ECMA_VALUE_EMPTY if successful * conversion error otherwise */ ecma_value_t ecma_op_to_index (ecma_value_t value, /**< ecma value */ ecma_number_t *index) /**< [out] ecma number */ { /* 1. */ if (ecma_is_value_undefined (value)) { *index = 0; return ECMA_VALUE_EMPTY; } /* 2.a */ ecma_number_t integer_index; ecma_value_t index_value = ecma_op_to_integer (value, &integer_index); if (ECMA_IS_VALUE_ERROR (index_value)) { return index_value; } /* 2.b - 2.d */ if (integer_index < 0 || integer_index > ECMA_NUMBER_MAX_SAFE_INTEGER) { return ecma_raise_range_error (ECMA_ERR_INVALID_OR_OUT_OF_RANGE_INDEX); } /* 3. */ *index = integer_index; return ECMA_VALUE_EMPTY; } /* ecma_op_to_index */ /** * CreateListFromArrayLike operation. * Different types are not handled yet. * * See also: * ECMA-262 v6, 7.3.17 * * @return ecma_collection_t if successful * NULL otherwise */ ecma_collection_t * ecma_op_create_list_from_array_like (ecma_value_t arr, /**< array value */ bool prop_names_only) /**< true - accept only property names false - otherwise */ { /* 1. */ JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (arr)); /* 3. */ if (!ecma_is_value_object (arr)) { ecma_raise_type_error (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); return NULL; } ecma_object_t *obj_p = ecma_get_object_from_value (arr); /* 4. 5. */ ecma_length_t len; if (ECMA_IS_VALUE_ERROR (ecma_op_object_get_length (obj_p, &len))) { return NULL; } /* 6. */ ecma_collection_t *list_ptr = ecma_new_collection (); /* 7. 8. */ for (ecma_length_t idx = 0; idx < len; idx++) { ecma_value_t next = ecma_op_object_get_by_index (obj_p, idx); if (ECMA_IS_VALUE_ERROR (next)) { ecma_collection_free (list_ptr); return NULL; } if (prop_names_only && !ecma_is_value_prop_name (next)) { ecma_free_value (next); ecma_collection_free (list_ptr); ecma_raise_type_error (ECMA_ERR_PROPERTY_NAME_IS_NEITHER_SYMBOL_NOR_STRING); return NULL; } ecma_collection_push_back (list_ptr, next); } /* 9. */ return list_ptr; } /* ecma_op_create_list_from_array_like */ /** * @} * @} */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h000664 001750 001750 00000002517 15164251010 050072 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * BigInt built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_BIGINT /* Number properties: * (property name, number value, writable, enumerable, configurable) */ /* ECMA-262 v11, 20.2.1 */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v11, 20.2.1 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_BIGINT_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v11, 20.2.2.3 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_BIGINT_PROTOTYPE, ECMA_PROPERTY_FIXED) #endif /* JERRY_BUILTIN_BIGINT */ #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h000664 001750 001750 00000061032 15164251010 047000 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* Description of built-in objects in format (ECMA_BUILTIN_ID_id, object_type, prototype_id, is_extensible, is_static, underscored_id) */ /* The Object.prototype object (15.2.4) */ BUILTIN (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID__COUNT /* no prototype */, true, object_prototype) /* The Object object (15.2.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_OBJECT, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, object) #if JERRY_BUILTIN_ARRAY /* The Array.prototype object (15.4.4) */ BUILTIN (ECMA_BUILTIN_ID_ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_ARRAY, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, array_prototype) /* The Array object (15.4.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, array) #endif /* JERRY_BUILTIN_ARRAY */ #if JERRY_BUILTIN_DATE /* The Date.prototype object (20.3.4) */ BUILTIN (ECMA_BUILTIN_ID_DATE_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, date_prototype) #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_REGEXP /* The RegExp.prototype object (21.2.5) */ BUILTIN (ECMA_BUILTIN_ID_REGEXP_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, regexp_prototype) #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_STRING /* The String.prototype object (15.5.4) */ BUILTIN (ECMA_BUILTIN_ID_STRING_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_CLASS, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, string_prototype) /* The String object (15.5.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_STRING, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, string) #endif /* JERRY_BUILTIN_STRING */ #if JERRY_BUILTIN_BOOLEAN /* The Boolean.prototype object (15.6.4) */ BUILTIN (ECMA_BUILTIN_ID_BOOLEAN_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_CLASS, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, boolean_prototype) /* The Boolean object (15.6.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_BOOLEAN, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, boolean) #endif /* JERRY_BUILTIN_BOOLEAN */ #if JERRY_BUILTIN_NUMBER /* The Number.prototype object (15.7.4) */ BUILTIN (ECMA_BUILTIN_ID_NUMBER_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_CLASS, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, number_prototype) /* The Number object (15.7.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_NUMBER, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, number) #endif /* JERRY_BUILTIN_NUMBER */ /* The Function.prototype object (15.3.4) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, function_prototype) /* The Function object (15.3.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_FUNCTION, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, function) #if JERRY_BUILTIN_MATH /* The Math object (15.8) */ BUILTIN (ECMA_BUILTIN_ID_MATH, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, math) #endif /* JERRY_BUILTIN_MATH */ #if JERRY_BUILTIN_REFLECT /* The Reflect object (26.1) */ BUILTIN (ECMA_BUILTIN_ID_REFLECT, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, reflect) #endif /* JERRY_BUILTIN_REFLECT */ #if JERRY_BUILTIN_ATOMICS /* The Atomics object (24.4) */ BUILTIN (ECMA_BUILTIN_ID_ATOMICS, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, atomics) #endif /* JERRY_BUILTIN_ATOMICS */ #if JERRY_BUILTIN_DATE /* The Date object (15.9.3) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_DATE, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, date) #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_REGEXP /* The RegExp object (15.10) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_REGEXP, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, regexp) #endif /* JERRY_BUILTIN_REGEXP */ #define ECMA_BUILTIN_NATIVE_ERROR_PROTOTYPE_ID ECMA_BUILTIN_ID_ERROR /* The Error object (15.11.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_ERROR, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, error) /* The Error.prototype object (15.11.4) */ BUILTIN (ECMA_BUILTIN_ID_ERROR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, error_prototype) #if JERRY_BUILTIN_ERRORS /* The EvalError.prototype object (15.11.6.1) */ BUILTIN (ECMA_BUILTIN_ID_EVAL_ERROR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ERROR_PROTOTYPE, true, eval_error_prototype) /* The EvalError object (15.11.6.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_EVAL_ERROR, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_NATIVE_ERROR_PROTOTYPE_ID, true, eval_error) /* The RangeError.prototype object (15.11.6.2) */ BUILTIN (ECMA_BUILTIN_ID_RANGE_ERROR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ERROR_PROTOTYPE, true, range_error_prototype) /* The RangeError object (15.11.6.2) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_RANGE_ERROR, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_NATIVE_ERROR_PROTOTYPE_ID, true, range_error) /* The ReferenceError.prototype object (15.11.6.3) */ BUILTIN (ECMA_BUILTIN_ID_REFERENCE_ERROR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ERROR_PROTOTYPE, true, reference_error_prototype) /* The ReferenceError object (15.11.6.3) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_REFERENCE_ERROR, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_NATIVE_ERROR_PROTOTYPE_ID, true, reference_error) /* The SyntaxError.prototype object (15.11.6.4) */ BUILTIN (ECMA_BUILTIN_ID_SYNTAX_ERROR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ERROR_PROTOTYPE, true, syntax_error_prototype) /* The SyntaxError object (15.11.6.4) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_SYNTAX_ERROR, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_NATIVE_ERROR_PROTOTYPE_ID, true, syntax_error) /* The TypeError.prototype object (15.11.6.5) */ BUILTIN (ECMA_BUILTIN_ID_TYPE_ERROR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ERROR_PROTOTYPE, true, type_error_prototype) /* The TypeError object (15.11.6.5) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_TYPE_ERROR, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_NATIVE_ERROR_PROTOTYPE_ID, true, type_error) /* The AggregateError.prototype object (15.11.6.5) */ BUILTIN (ECMA_BUILTIN_ID_AGGREGATE_ERROR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ERROR_PROTOTYPE, true, aggregate_error_prototype) /* The AggregateError object (15.11.6.5) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_AGGREGATE_ERROR, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_NATIVE_ERROR_PROTOTYPE_ID, true, aggregate_error) /* The URIError.prototype object (15.11.6.6) */ BUILTIN (ECMA_BUILTIN_ID_URI_ERROR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ERROR_PROTOTYPE, true, uri_error_prototype) /* The URIError object (15.11.6.6) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_URI_ERROR, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_NATIVE_ERROR_PROTOTYPE_ID, true, uri_error) #endif /* JERRY_BUILTIN_ERRORS */ /**< The [[ThrowTypeError]] object (13.2.3) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_TYPE_ERROR_THROWER, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, false, type_error_thrower) #if JERRY_BUILTIN_TYPEDARRAY /* The ArrayBuffer.prototype object (ES2015 24.1.4) */ BUILTIN (ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, arraybuffer_prototype) /* The ArrayBuffer object (ES2015 24.1.2) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_ARRAYBUFFER, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, arraybuffer) #if JERRY_BUILTIN_SHAREDARRAYBUFFER /* The SharedArrayBuffer.prototype object (ES2015 24.2.4) */ BUILTIN (ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, shared_arraybuffer_prototype) /* The SharedArrayBuffer object (ES2015 24.2.2) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, shared_arraybuffer) #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ /* The %TypedArrayPrototype% object (ES2015 24.2.3) */ BUILTIN (ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, typedarray_prototype) /* The %TypedArray% intrinsic object (ES2015 22.2.1) Note: The routines must be in this order. */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_TYPEDARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, typedarray) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_INT8ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, int8array) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_UINT8ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, uint8array) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_UINT8CLAMPEDARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, uint8clampedarray) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_INT16ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, int16array) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_UINT16ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, uint16array) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_INT32ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, int32array) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_UINT32ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, uint32array) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_FLOAT32ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, float32array) #if JERRY_NUMBER_TYPE_FLOAT64 BUILTIN_ROUTINE (ECMA_BUILTIN_ID_FLOAT64ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, float64array) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT BUILTIN_ROUTINE (ECMA_BUILTIN_ID_BIGINT64ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, bigint64array) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_BIGUINT64ARRAY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_TYPEDARRAY, true, biguint64array) #endif /* JERRY_BUILTIN_BIGINT */ BUILTIN (ECMA_BUILTIN_ID_INT8ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, int8array_prototype) BUILTIN (ECMA_BUILTIN_ID_UINT8ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, uint8array_prototype) BUILTIN (ECMA_BUILTIN_ID_UINT8CLAMPEDARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, uint8clampedarray_prototype) BUILTIN (ECMA_BUILTIN_ID_INT16ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, int16array_prototype) BUILTIN (ECMA_BUILTIN_ID_UINT16ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, uint16array_prototype) BUILTIN (ECMA_BUILTIN_ID_INT32ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, int32array_prototype) BUILTIN (ECMA_BUILTIN_ID_UINT32ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, uint32array_prototype) BUILTIN (ECMA_BUILTIN_ID_FLOAT32ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, float32array_prototype) #if JERRY_NUMBER_TYPE_FLOAT64 BUILTIN (ECMA_BUILTIN_ID_FLOAT64ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, float64array_prototype) #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #if JERRY_BUILTIN_BIGINT BUILTIN (ECMA_BUILTIN_ID_BIGINT64ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, bigint64array_prototype) BUILTIN (ECMA_BUILTIN_ID_BIGUINT64ARRAY_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_TYPEDARRAY_PROTOTYPE, true, biguint64array_prototype) #endif /* JERRY_BUILTIN_BIGINT */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ BUILTIN (ECMA_BUILTIN_ID_PROMISE_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, promise_prototype) BUILTIN_ROUTINE (ECMA_BUILTIN_ID_PROMISE, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, promise) #if JERRY_BUILTIN_CONTAINER /* The Map prototype object (23.1.3) */ BUILTIN (ECMA_BUILTIN_ID_MAP_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, map_prototype) /* The Map routine (ECMA-262 v6, 23.1.1.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_MAP, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, map) /* The Set prototype object (23.1.3) */ BUILTIN (ECMA_BUILTIN_ID_SET_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, set_prototype) /* The Set routine (ECMA-262 v6, 23.1.1.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_SET, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, set) /* The WeakMap prototype object (23.1.3) */ BUILTIN (ECMA_BUILTIN_ID_WEAKMAP_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, weakmap_prototype) /* The WeakMap routine (ECMA-262 v6, 23.1.1.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_WEAKMAP, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, weakmap) /* The WeakSet prototype object (23.1.3) */ BUILTIN (ECMA_BUILTIN_ID_WEAKSET_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, weakset_prototype) /* The WeakSet routine (ECMA-262 v6, 23.1.1.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_WEAKSET, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, weakset) #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_WEAKREF /* The WeakRef prototype object */ BUILTIN (ECMA_BUILTIN_ID_WEAKREF_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, weakref_prototype) /* The WeakRef routine */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_WEAKREF, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, weakref) #endif /* JERRY_BUILTIN_WEAKREF */ #if (JERRY_BUILTIN_PROXY) /* The Proxy routine (ECMA-262 v6, 26.2.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_PROXY, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, proxy) #endif /* JERRY_BUILTIN_PROXY */ /* Intrinsic hidden builtin object */ BUILTIN (ECMA_BUILTIN_ID_INTRINSIC_OBJECT, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID__COUNT /* no prototype */, true, intrinsic) /* The Array.prototype[@@unscopables] object */ BUILTIN (ECMA_BUILTIN_ID_ARRAY_PROTOTYPE_UNSCOPABLES, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID__COUNT /* no prototype */, true, array_prototype_unscopables) /* The Symbol prototype object (ECMA-262 v6, 19.4.2.7) */ BUILTIN (ECMA_BUILTIN_ID_SYMBOL_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, symbol_prototype) /* The Symbol routine (ECMA-262 v6, 19.4.2.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_SYMBOL, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, symbol) /* The %AsyncFunction% object (ECMA-262 v11, 25.7.2) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_ASYNC_FUNCTION, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION, true, async_function) /* The %AsyncFunctionPrototype% object (ECMA-262 v11, 25.7.3) */ BUILTIN (ECMA_BUILTIN_ID_ASYNC_FUNCTION_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, async_function_prototype) /* The %IteratorPrototype% object (ECMA-262 v6, 25.1.2) */ BUILTIN (ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, iterator_prototype) /* The %ArrayIteratorPrototype% object (ECMA-262 v6, 22.1.5.2) */ BUILTIN (ECMA_BUILTIN_ID_ARRAY_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, true, array_iterator_prototype) /* The %StringIteratorPrototype% object (ECMA-262 v6, 22.1.5.2) */ BUILTIN (ECMA_BUILTIN_ID_STRING_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, true, string_iterator_prototype) /* The %RegExpStringIteratorPrototype% object (ECMA-262 v11, 21.2.7.1) */ BUILTIN (ECMA_BUILTIN_ID_REGEXP_STRING_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, true, regexp_string_iterator_prototype) /* The %AsyncIteratorPrototype% object (ECMA-262 v10, 25.1.3) */ BUILTIN (ECMA_BUILTIN_ID_ASYNC_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, async_iterator_prototype) /* The %AsyncFromSyncIteratorPrototype% object (ECMA-262 v11, 25.1.4.2) */ BUILTIN (ECMA_BUILTIN_ID_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ASYNC_ITERATOR_PROTOTYPE, true, async_from_sync_iterator_prototype) /* The %(GeneratorFunction)% object */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_GENERATOR_FUNCTION, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION, true, generator_function) /* The %(Generator)% object */ BUILTIN (ECMA_BUILTIN_ID_GENERATOR, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, generator) /* The %(Generator).prototype% object */ BUILTIN (ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, true, generator_prototype) /* The %(AsyncGeneratorFunction)% object */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_ASYNC_GENERATOR_FUNCTION, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION, true, async_generator_function) /* The %(AsyncGenerator)% object */ BUILTIN (ECMA_BUILTIN_ID_ASYNC_GENERATOR, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, async_generator) /* The %(AsyncGenerator).prototype% object */ BUILTIN (ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ASYNC_ITERATOR_PROTOTYPE, true, async_generator_prototype) #if JERRY_BUILTIN_CONTAINER /* The %SetIteratorPrototype% object (ECMA-262 v6, 23.2.5.2) */ BUILTIN (ECMA_BUILTIN_ID_SET_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, true, set_iterator_prototype) /* The %MapIteratorPrototype% object (ECMA-262 v6, 23.1.5.2) */ BUILTIN (ECMA_BUILTIN_ID_MAP_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, true, map_iterator_prototype) #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_BIGINT /* The %BigInt.prototype% object */ BUILTIN (ECMA_BUILTIN_ID_BIGINT_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, bigint_prototype) /* The %BigInt% object */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_BIGINT, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, bigint) #endif /* JERRY_BUILTIN_BIGINT */ #if JERRY_BUILTIN_DATAVIEW /* The DataView prototype object (ECMA-262 v6, 24.2.3.1) */ BUILTIN (ECMA_BUILTIN_ID_DATAVIEW_PROTOTYPE, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, true, dataview_prototype) /* The DataView routine (ECMA-262 v6, 24.2.2.1) */ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_DATAVIEW, ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, true, dataview) #endif /* JERRY_BUILTIN_DATAVIEW */ /* The Global object (15.1) */ BUILTIN (ECMA_BUILTIN_ID_GLOBAL, ECMA_OBJECT_TYPE_BUILT_IN_GENERAL, ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, /* Implementation-dependent */ true, global) #undef BUILTIN src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-iterator-prototype.inc.h000664 001750 001750 00000001770 15164251010 052512 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %IteratorPrototype% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_GLOBAL_SYMBOL_ITERATOR, ECMA_BUILTIN_ITERATOR_PROTOTYPE_OBJECT_ITERATOR, 0, 0) #include "ecma-builtin-helpers-macro-undefs.inc.h" glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-magic-strings.h000664 001750 001750 00000014664 15164251010 043630 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 LIT_MAGIC_STRINGS_H #define LIT_MAGIC_STRINGS_H #include "lit-globals.h" /** * Identifiers of ECMA and implementation-defined magic string constants */ typedef enum { /** @cond doxygen_suppress */ #define LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE(size, id) #define LIT_MAGIC_STRING_DEF(id, ascii_zt_string) id, #include "lit-magic-strings.inc.h" #undef LIT_MAGIC_STRING_DEF #undef LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE /** @endcond */ LIT_NON_INTERNAL_MAGIC_STRING__COUNT, /**< number of non-internal magic strings */ LIT_INTERNAL_MAGIC_API_INTERNAL = LIT_NON_INTERNAL_MAGIC_STRING__COUNT, /**< Used to add non-visible JS properties * from the public API */ LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX, /**< [[%Iterator%NextIndex]] property */ LIT_INTERNAL_MAGIC_STRING_MAP_KEY, /**< Property key used when an object is a key in a map object */ LIT_INTERNAL_MAGIC_STRING_ARRAY_PROTOTYPE_VALUES, /**< %ArrayProto_values% intrinsic routine */ LIT_INTERNAL_MAGIC_STRING_TYPEDARRAY_PROTOTYPE_VALUES, /**< %TypedArray%.prototype values and [@@iterator] routine */ LIT_INTERNAL_MAGIC_STRING_SET_PROTOTYPE_VALUES, /**< Set.prototype values, keys and [@@iterator] routines */ LIT_INTERNAL_MAGIC_STRING_MAP_PROTOTYPE_ENTRIES, /**< Map.prototype entries and [@@iterator] routines */ /* List of well known symbols */ LIT_GLOBAL_SYMBOL_ASYNC_ITERATOR, /**< @@asyncIterator well known symbol */ LIT_GLOBAL_SYMBOL__FIRST = LIT_GLOBAL_SYMBOL_ASYNC_ITERATOR, /**< first global symbol */ LIT_GLOBAL_SYMBOL_HAS_INSTANCE, /**< @@hasInstance well known symbol */ LIT_GLOBAL_SYMBOL_IS_CONCAT_SPREADABLE, /**< @@isConcatSpreadable well known symbol */ LIT_GLOBAL_SYMBOL_ITERATOR, /**< @@iterator well known symbol */ LIT_GLOBAL_SYMBOL_MATCH, /**< @@match well known symbol */ LIT_GLOBAL_SYMBOL_REPLACE, /**< @@replace well known symbol */ LIT_GLOBAL_SYMBOL_SEARCH, /**< @@search well known symbol */ LIT_GLOBAL_SYMBOL_SPECIES, /**< @@species well known symbol */ LIT_GLOBAL_SYMBOL_SPLIT, /**< @@split well known symbol */ LIT_GLOBAL_SYMBOL_TO_PRIMITIVE, /**< @@toPrimitive well known symbol */ LIT_GLOBAL_SYMBOL_TO_STRING_TAG, /**< @@toStringTag well known symbol */ LIT_GLOBAL_SYMBOL_UNSCOPABLES, /**< @@unscopables well known symbol */ LIT_GLOBAL_SYMBOL_MATCH_ALL, /**< @@matchAll well known symbol */ LIT_GLOBAL_SYMBOL__LAST = LIT_GLOBAL_SYMBOL_MATCH_ALL, /**< last global symbol */ LIT_INTERNAL_MAGIC_STRING_DELETED, /**< special value for deleted properties */ LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_INIT, /**< function which initializes properties */ LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< native pointer info associated with an object */ LIT_INTERNAL_MAGIC_STRING_FIRST_DATA = LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< first index of special * data properties */ LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES, /**< native pointer info associated with an object * which contains references to other values */ LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD, /**< dynamic environment record needed by class constructors */ LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED, /**< computed class field name list */ LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS, /**< internal collection for storing private elements */ LIT_INTERNAL_MAGIC_STRING_CONTAINER_WEAK_REFS, /**< Weak references to the current container object */ LIT_INTERNAL_MAGIC_STRING_WEAK_REFS, /**< Weak references to the current object */ LIT_MAGIC_STRING__COUNT /**< number of magic strings */ } lit_magic_string_id_t; /** * Properties that are need to be ignored for opfunc_set_class_attributes */ #define LIT_INTERNAL_MAGIC_STRING_IGNORED(id) \ ((id) >= LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED && (id) <= LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS) /** * Checks whether the given id corresponds to a global symbol */ #define LIT_IS_GLOBAL_SYMBOL(id) ((id) >= LIT_GLOBAL_SYMBOL__FIRST && (id) <= LIT_GLOBAL_SYMBOL__LAST) /** * Identifiers of implementation-defined external magic string constants */ typedef uint32_t lit_magic_string_ex_id_t; uint32_t lit_get_magic_string_ex_count (void); const lit_utf8_byte_t *lit_get_magic_string_utf8 (uint32_t id); lit_utf8_size_t lit_get_magic_string_size (uint32_t id); const lit_utf8_byte_t *lit_get_magic_string_ex_utf8 (uint32_t id); lit_utf8_size_t lit_get_magic_string_ex_size (uint32_t id); void lit_magic_strings_ex_set (const lit_utf8_byte_t *const *ex_str_items, uint32_t count, const lit_utf8_size_t *ex_str_sizes); lit_magic_string_id_t lit_is_utf8_string_magic (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size); lit_magic_string_id_t lit_is_utf8_string_pair_magic (const lit_utf8_byte_t *string1_p, lit_utf8_size_t string1_size, const lit_utf8_byte_t *string2_p, lit_utf8_size_t string2_size); lit_magic_string_ex_id_t lit_is_ex_utf8_string_magic (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size); lit_magic_string_ex_id_t lit_is_ex_utf8_string_pair_magic (const lit_utf8_byte_t *string1_p, lit_utf8_size_t string1_size, const lit_utf8_byte_t *string2_p, lit_utf8_size_t string2_size); lit_utf8_byte_t * lit_copy_magic_string_to_buffer (lit_magic_string_id_t id, lit_utf8_byte_t *buffer_p, lit_utf8_size_t buffer_size); #endif /* !LIT_MAGIC_STRINGS_H */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/webp.cpp000664 001750 001750 00000057271 15164251010 034602 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Main decoding functions for WEBP images. // // Author: Skal (pascal.massimino@gmail.com) #include #include "./vp8i.h" #include "./vp8li.h" #include "./webpi.h" // VP8X Feature Flags. typedef enum WebPFeatureFlags { FRAGMENTS_FLAG = 0x00000001, ANIMATION_FLAG = 0x00000002, XMP_FLAG = 0x00000004, EXIF_FLAG = 0x00000008, ALPHA_FLAG = 0x00000010, ICCP_FLAG = 0x00000020 } WebPFeatureFlags; //------------------------------------------------------------------------------ // RIFF layout is: // Offset tag // 0...3 "RIFF" 4-byte tag // 4...7 size of image data (including metadata) starting at offset 8 // 8...11 "WEBP" our form-type signature // The RIFF container (12 bytes) is followed by appropriate chunks: // 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format // 16..19 size of the raw VP8 image data, starting at offset 20 // 20.... the VP8 bytes // Or, // 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format // 16..19 size of the raw VP8L image data, starting at offset 20 // 20.... the VP8L bytes // Or, // 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk. // 16..19 size of the VP8X chunk starting at offset 20. // 20..23 VP8X flags bit-map corresponding to the chunk-types present. // 24..26 Width of the Canvas Image. // 27..29 Height of the Canvas Image. // There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8, // VP8L, XMP, EXIF ...) // All sizes are in little-endian order. // Note: chunk data size must be padded to multiple of 2 when written. static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) { return data[0] | (data[1] << 8) | (data[2] << 16); } static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) { return (uint32_t)get_le24(data) | (data[3] << 24); } // Validates the RIFF container (if detected) and skips over it. // If a RIFF container is detected, returns: // VP8_STATUS_BITSTREAM_ERROR for invalid header, // VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true, // and VP8_STATUS_OK otherwise. // In case there are not enough bytes (partial RIFF container), return 0 for // *riff_size. Else return the RIFF size extracted from the header. static VP8StatusCode ParseRIFF(const uint8_t** const data, size_t* const data_size, int have_all_data, size_t* const riff_size) { assert(data != NULL); assert(data_size != NULL); assert(riff_size != NULL); *riff_size = 0; // Default: no RIFF present. if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) { if (memcmp(*data + 8, "WEBP", TAG_SIZE)) { return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature. } else { const uint32_t size = get_le32(*data + TAG_SIZE); // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn"). if (size < TAG_SIZE + CHUNK_HEADER_SIZE) { return VP8_STATUS_BITSTREAM_ERROR; } if (size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; } if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. } // We have a RIFF container. Skip it. *riff_size = size; *data += RIFF_HEADER_SIZE; *data_size -= RIFF_HEADER_SIZE; } } return VP8_STATUS_OK; } // Validates the VP8X header and skips over it. // Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header, // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and // VP8_STATUS_OK otherwise. // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr, // *height_ptr and *flags_ptr are set to the corresponding values extracted // from the VP8X chunk. static VP8StatusCode ParseVP8X(const uint8_t** const data, size_t* const data_size, int* const found_vp8x, int* const width_ptr, int* const height_ptr, uint32_t* const flags_ptr) { const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; assert(data != NULL); assert(data_size != NULL); assert(found_vp8x != NULL); *found_vp8x = 0; if (*data_size < CHUNK_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. } if (!memcmp(*data, "VP8X", TAG_SIZE)) { int width, height; uint32_t flags; const uint32_t chunk_size = get_le32(*data + TAG_SIZE); if (chunk_size != VP8X_CHUNK_SIZE) { return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. } // Verify if enough data is available to validate the VP8X chunk. if (*data_size < vp8x_size) { return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. } flags = get_le32(*data + 8); width = 1 + get_le24(*data + 12); height = 1 + get_le24(*data + 15); if (width * (uint64_t)height >= MAX_IMAGE_AREA) { return VP8_STATUS_BITSTREAM_ERROR; // image is too large } if (flags_ptr != NULL) *flags_ptr = flags; if (width_ptr != NULL) *width_ptr = width; if (height_ptr != NULL) *height_ptr = height; // Skip over VP8X header bytes. *data += vp8x_size; *data_size -= vp8x_size; *found_vp8x = 1; } return VP8_STATUS_OK; } // Skips to the next VP8/VP8L chunk header in the data given the size of the // RIFF chunk 'riff_size'. // Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered, // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and // VP8_STATUS_OK otherwise. // If an alpha chunk is found, *alpha_data and *alpha_size are set // appropriately. static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, size_t* const data_size, size_t const riff_size, const uint8_t** const alpha_data, size_t* const alpha_size) { const uint8_t* buf; size_t buf_size; uint32_t total_size = TAG_SIZE + // "WEBP". CHUNK_HEADER_SIZE + // "VP8Xnnnn". VP8X_CHUNK_SIZE; // data. assert(data != NULL); assert(data_size != NULL); buf = *data; buf_size = *data_size; assert(alpha_data != NULL); assert(alpha_size != NULL); *alpha_data = NULL; *alpha_size = 0; while (1) { uint32_t chunk_size; uint32_t disk_chunk_size; // chunk_size with padding *data = buf; *data_size = buf_size; if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data. return VP8_STATUS_NOT_ENOUGH_DATA; } chunk_size = get_le32(buf + TAG_SIZE); if (chunk_size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. } // For odd-sized chunk-payload, there's one byte padding at the end. disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1; total_size += disk_chunk_size; // Check that total bytes skipped so far does not exceed riff_size. if (riff_size > 0 && (total_size > riff_size)) { return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. } // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have // parsed all the optional chunks. // Note: This check must occur before the check 'buf_size < disk_chunk_size' // below to allow incomplete VP8/VP8L chunks. if (!memcmp(buf, "VP8 ", TAG_SIZE) || !memcmp(buf, "VP8L", TAG_SIZE)) { return VP8_STATUS_OK; } if (buf_size < disk_chunk_size) { // Insufficient data. return VP8_STATUS_NOT_ENOUGH_DATA; } if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header. *alpha_data = buf + CHUNK_HEADER_SIZE; *alpha_size = chunk_size; } // We have a full and valid chunk; skip it. buf += disk_chunk_size; buf_size -= disk_chunk_size; } } // Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it. // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than // riff_size) VP8/VP8L header, // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and // VP8_STATUS_OK otherwise. // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes // extracted from the VP8/VP8L chunk header. // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, size_t* const data_size, int have_all_data, size_t riff_size, size_t* const chunk_size, int* const is_lossless) { const uint8_t* const data = *data_ptr; const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE); const uint32_t minimal_size = TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR // "WEBP" + "VP8Lnnnn" assert(data != NULL); assert(data_size != NULL); assert(chunk_size != NULL); assert(is_lossless != NULL); if (*data_size < CHUNK_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. } if (is_vp8 || is_vp8l) { // Bitstream contains VP8/VP8L header. const uint32_t size = get_le32(data + TAG_SIZE); if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. } if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. } // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. *chunk_size = size; *data_ptr += CHUNK_HEADER_SIZE; *data_size -= CHUNK_HEADER_SIZE; *is_lossless = is_vp8l; } else { // Raw VP8/VP8L bitstream (no header). *is_lossless = VP8LCheckSignature(data, *data_size); *chunk_size = *data_size; } return VP8_STATUS_OK; } //------------------------------------------------------------------------------ // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the // minimal amount will be read to fetch the remaining parameters. // If 'headers' is non-NULL this function will attempt to locate both alpha // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L). // Note: The following chunk sequences (before the raw VP8/VP8L data) are // considered valid by this function: // RIFF + VP8(L) // RIFF + VP8X + (optional chunks) + VP8(L) // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. static VP8StatusCode ParseHeadersInternal(const uint8_t* data, size_t data_size, int* const width, int* const height, int* const has_alpha, int* const has_animation, int* const format, WebPHeaderStructure* const headers) { int canvas_width = 0; int canvas_height = 0; int image_width = 0; int image_height = 0; int found_riff = 0; int found_vp8x = 0; int animation_present = 0; int fragments_present = 0; const int have_all_data = (headers != NULL) ? headers->have_all_data : 0; VP8StatusCode status; WebPHeaderStructure hdrs; if (data == NULL || data_size < RIFF_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; } memset(&hdrs, 0, sizeof(hdrs)); hdrs.data = data; hdrs.data_size = data_size; // Skip over RIFF header. status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size); if (status != VP8_STATUS_OK) { return status; // Wrong RIFF header / insufficient data. } found_riff = (hdrs.riff_size > 0); // Skip over VP8X. { uint32_t flags = 0; status = ParseVP8X(&data, &data_size, &found_vp8x, &canvas_width, &canvas_height, &flags); if (status != VP8_STATUS_OK) { return status; // Wrong VP8X / insufficient data. } animation_present = !!(flags & ANIMATION_FLAG); fragments_present = !!(flags & FRAGMENTS_FLAG); if (!found_riff && found_vp8x) { // Note: This restriction may be removed in the future, if it becomes // necessary to send VP8X chunk to the decoder. return VP8_STATUS_BITSTREAM_ERROR; } if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); if (has_animation != NULL) *has_animation = animation_present; if (format != NULL) *format = 0; // default = undefined image_width = canvas_width; image_height = canvas_height; if (found_vp8x && (animation_present || fragments_present) && headers == NULL) { status = VP8_STATUS_OK; goto ReturnWidthHeight; // Just return features from VP8X header. } } if (data_size < TAG_SIZE) { status = VP8_STATUS_NOT_ENOUGH_DATA; goto ReturnWidthHeight; } // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". if ((found_riff && found_vp8x) || (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, &hdrs.alpha_data, &hdrs.alpha_data_size); if (status != VP8_STATUS_OK) { goto ReturnWidthHeight; // Invalid chunk size / insufficient data. } } // Skip over VP8/VP8L header. status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size, &hdrs.compressed_size, &hdrs.is_lossless); if (status != VP8_STATUS_OK) { goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data. } if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; } if (format != NULL && !(animation_present || fragments_present)) { *format = hdrs.is_lossless ? 2 : 1; } if (!hdrs.is_lossless) { if (data_size < VP8_FRAME_HEADER_SIZE) { status = VP8_STATUS_NOT_ENOUGH_DATA; goto ReturnWidthHeight; } // Validates raw VP8 data. if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, &image_width, &image_height)) { return VP8_STATUS_BITSTREAM_ERROR; } } else { if (data_size < VP8L_FRAME_HEADER_SIZE) { status = VP8_STATUS_NOT_ENOUGH_DATA; goto ReturnWidthHeight; } // Validates raw VP8L data. if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { return VP8_STATUS_BITSTREAM_ERROR; } } // Validates image size coherency. if (found_vp8x) { if (canvas_width != image_width || canvas_height != image_height) { return VP8_STATUS_BITSTREAM_ERROR; } } if (headers != NULL) { *headers = hdrs; headers->offset = data - headers->data; assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); assert(headers->offset == headers->data_size - data_size); } ReturnWidthHeight: if (status == VP8_STATUS_OK || (status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) { if (has_alpha != NULL) { // If the data did not contain a VP8X/VP8L chunk the only definitive way // to set this is by looking for alpha data (from an ALPH chunk). *has_alpha |= (hdrs.alpha_data != NULL); } if (width != NULL) *width = image_width; if (height != NULL) *height = image_height; return VP8_STATUS_OK; } else { return status; } } // Skips over all valid chunks prior to the first VP8/VP8L frame header. // Returns: VP8_STATUS_OK, VP8_STATUS_BITSTREAM_ERROR (invalid header/chunk), // VP8_STATUS_NOT_ENOUGH_DATA (partial input) or VP8_STATUS_UNSUPPORTED_FEATURE // in the case of non-decodable features (animation for instance). // In 'headers', compressed_size, offset, alpha_data, alpha_size, and lossless // fields are updated appropriately upon success. static VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { VP8StatusCode status; int has_animation = 0; assert(headers != NULL); // fill out headers, ignore width/height/has_alpha. status = ParseHeadersInternal(headers->data, headers->data_size, NULL, NULL, NULL, &has_animation, NULL, headers); if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) { // TODO(jzern): full support of animation frames will require API additions. if (has_animation) { status = VP8_STATUS_UNSUPPORTED_FEATURE; } } return status; } //------------------------------------------------------------------------------ // "Into" decoding variants // Main flow static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, WebPDecParams* const params) { VP8StatusCode status; VP8Io io; WebPHeaderStructure headers; headers.data = data; headers.data_size = data_size; headers.have_all_data = 1; status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks. if (status != VP8_STATUS_OK) { return status; } assert(params != NULL); VP8InitIo(&io); io.data = headers.data + headers.offset; io.data_size = headers.data_size - headers.offset; WebPInitCustomIo(params, &io); // Plug the I/O functions. if (!headers.is_lossless) { VP8Decoder* const dec = VP8New(); if (dec == NULL) { return VP8_STATUS_OUT_OF_MEMORY; } dec->alpha_data_ = headers.alpha_data; dec->alpha_data_size_ = headers.alpha_data_size; // Decode bitstream header, update io->width/io->height. if (!VP8GetHeaders(dec, &io)) { status = dec->status_; // An error occurred. Grab error status. } else { // Allocate/check output buffers. status = WebPAllocateDecBuffer(io.width, io.height, params->options, params->output); if (status == VP8_STATUS_OK) { // Decode VP8InitDithering(params->options, dec); if (!VP8Decode(dec, &io)) { status = dec->status_; } } } VP8Delete(dec); } else { VP8LDecoder* const dec = VP8LNew(); if (dec == NULL) { return VP8_STATUS_OUT_OF_MEMORY; } if (!VP8LDecodeHeader(dec, &io)) { status = dec->status_; // An error occurred. Grab error status. } else { // Allocate/check output buffers. status = WebPAllocateDecBuffer(io.width, io.height, params->options, params->output); if (status == VP8_STATUS_OK) { // Decode if (!VP8LDecodeImage(dec)) { status = dec->status_; } } } VP8LDelete(dec); } if (status != VP8_STATUS_OK) { WebPFreeDecBuffer(params->output); } if (params->options != NULL && params->options->flip) { status = WebPFlipBuffer(params->output); } return status; } //------------------------------------------------------------------------------ static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data, size_t data_size, int* const width, int* const height, WebPDecBuffer* const keep_info) { WebPDecParams params; WebPDecBuffer output; WebPInitDecBuffer(&output); memset(¶ms, 0, sizeof(params)); params.output = &output; output.colorspace = mode; // Retrieve (and report back) the required dimensions from bitstream. if (!WebPGetInfo(data, data_size, &output.width, &output.height)) { return NULL; } if (width != NULL) *width = output.width; if (height != NULL) *height = output.height; // Decode if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { return NULL; } if (keep_info != NULL) { // keep track of the side-info WebPCopyDecBuffer(&output, keep_info); } // return decoded samples (don't clear 'output'!) return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y; } static void DefaultFeatures(WebPBitstreamFeatures* const features) { assert(features != NULL); memset(features, 0, sizeof(*features)); } static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, WebPBitstreamFeatures* const features) { if (features == NULL || data == NULL) { return VP8_STATUS_INVALID_PARAM; } DefaultFeatures(features); // Only parse enough of the data to retrieve the features. return ParseHeadersInternal(data, data_size, &features->width, &features->height, &features->has_alpha, &features->has_animation, &features->format, NULL); } //------------------------------------------------------------------------------ // Cropping and rescaling. int WebPIoInitFromOptions(const WebPDecoderOptions* const options, VP8Io* const io, WEBP_CSP_MODE src_colorspace) { const int W = io->width; const int H = io->height; int x = 0, y = 0, w = W, h = H; // Cropping io->use_cropping = (options != NULL) && (options->use_cropping > 0); if (io->use_cropping) { w = options->crop_width; h = options->crop_height; x = options->crop_left; y = options->crop_top; if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 x &= ~1; y &= ~1; } if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) { return 0; // out of frame boundary error } } io->crop_left = x; io->crop_top = y; io->crop_right = x + w; io->crop_bottom = y + h; io->mb_w = w; io->mb_h = h; // Scaling io->use_scaling = (options != NULL) && (options->use_scaling > 0); if (io->use_scaling) { if (options->scaled_width <= 0 || options->scaled_height <= 0) { return 0; } io->scaled_width = options->scaled_width; io->scaled_height = options->scaled_height; } // Filter io->bypass_filtering = options && options->bypass_filtering; // Fancy upsampler #ifdef FANCY_UPSAMPLING io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling); #endif if (io->use_scaling) { // disable filter (only for large downscaling ratio). io->bypass_filtering = (io->scaled_width < W * 3 / 4) && (io->scaled_height < H * 3 / 4); io->fancy_upsampling = 0; } return 1; } //------------------------------------------------------------------------------ /************************************************************************/ /* External Class Implementation */ /************************************************************************/ uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, int* width, int* height) { return Decode(MODE_bgrA, data, data_size, width, height, NULL); } uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, int* width, int* height) { return Decode(MODE_rgbA, data, data_size, width, height, NULL); } int WebPGetInfo(const uint8_t* data, size_t data_size, int* width, int* height) { WebPBitstreamFeatures features; if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) { return 0; } if (width != NULL) { *width = features.width; } if (height != NULL) { *height = features.height; } return 1; } thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-string-object.h000664 001750 001750 00000002444 15164251010 046241 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_STRING_OBJECT_H #define ECMA_STRING_OBJECT_H #include "ecma-globals.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmastringobject ECMA String object related routines * @{ */ ecma_value_t ecma_op_create_string_object (const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); void ecma_op_string_list_lazy_property_names (ecma_object_t *obj_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p, jerry_property_filter_t filter); /** * @} * @} */ #endif /* !ECMA_STRING_OBJECT_H */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/include/meson.build000664 001750 001750 00000000433 15164251010 043104 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'jerry-config.h', 'jerryscript-compiler.h', 'jerryscript-core.h', 'jerryscript-port.h', 'jerryscript-types.h', 'jerryscript.h' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-unicode-ranges.inc.h000664 001750 001750 00000033065 15164251010 044530 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is automatically generated by the gen-unicode.py script * from DerivedCoreProperties.txt. Do not edit! */ /** * Character interval starting points for ID_Start. */ static const uint16_t lit_unicode_id_start_interval_starts[] JERRY_ATTR_CONST_DATA = { 0x00c0, 0x00d8, 0x00f8, 0x01f8, 0x02c6, 0x02e0, 0x0370, 0x0376, 0x037a, 0x0388, 0x038e, 0x03a3, 0x03f7, 0x048a, 0x0531, 0x0560, 0x05d0, 0x05ef, 0x0620, 0x066e, 0x0671, 0x06e5, 0x06ee, 0x06fa, 0x0712, 0x074d, 0x07ca, 0x07f4, 0x0800, 0x0840, 0x0860, 0x08a0, 0x08b6, 0x0904, 0x0958, 0x0971, 0x0985, 0x098f, 0x0993, 0x09aa, 0x09b6, 0x09dc, 0x09df, 0x09f0, 0x0a05, 0x0a0f, 0x0a13, 0x0a2a, 0x0a32, 0x0a35, 0x0a38, 0x0a59, 0x0a72, 0x0a85, 0x0a8f, 0x0a93, 0x0aaa, 0x0ab2, 0x0ab5, 0x0ae0, 0x0b05, 0x0b0f, 0x0b13, 0x0b2a, 0x0b32, 0x0b35, 0x0b5c, 0x0b5f, 0x0b85, 0x0b8e, 0x0b92, 0x0b99, 0x0b9e, 0x0ba3, 0x0ba8, 0x0bae, 0x0c05, 0x0c0e, 0x0c12, 0x0c2a, 0x0c58, 0x0c60, 0x0c85, 0x0c8e, 0x0c92, 0x0caa, 0x0cb5, 0x0ce0, 0x0cf1, 0x0d04, 0x0d0e, 0x0d12, 0x0d54, 0x0d5f, 0x0d7a, 0x0d85, 0x0d9a, 0x0db3, 0x0dc0, 0x0e01, 0x0e32, 0x0e40, 0x0e81, 0x0e86, 0x0e8c, 0x0ea7, 0x0eb2, 0x0ec0, 0x0edc, 0x0f40, 0x0f49, 0x0f88, 0x1000, 0x1050, 0x105a, 0x1065, 0x106e, 0x1075, 0x10a0, 0x10d0, 0x10fc, 0x11fc, 0x124a, 0x1250, 0x125a, 0x1260, 0x128a, 0x1290, 0x12b2, 0x12b8, 0x12c2, 0x12c8, 0x12d8, 0x1312, 0x1318, 0x1380, 0x13a0, 0x13f8, 0x1401, 0x1501, 0x1601, 0x166f, 0x1681, 0x16a0, 0x16ee, 0x1700, 0x170e, 0x1720, 0x1740, 0x1760, 0x176e, 0x1780, 0x1820, 0x1880, 0x18b0, 0x1900, 0x1950, 0x1970, 0x1980, 0x19b0, 0x1a00, 0x1a20, 0x1b05, 0x1b45, 0x1b83, 0x1bae, 0x1bba, 0x1c00, 0x1c4d, 0x1c5a, 0x1c80, 0x1c90, 0x1cbd, 0x1ce9, 0x1cee, 0x1cf5, 0x1d00, 0x1e00, 0x1f00, 0x1f18, 0x1f20, 0x1f48, 0x1f50, 0x1f5f, 0x1f80, 0x1fb6, 0x1fc2, 0x1fc6, 0x1fd0, 0x1fd6, 0x1fe0, 0x1ff2, 0x1ff6, 0x2090, 0x210a, 0x2118, 0x212a, 0x213c, 0x2145, 0x2160, 0x2c00, 0x2c30, 0x2c60, 0x2ceb, 0x2cf2, 0x2d00, 0x2d30, 0x2d80, 0x2da0, 0x2da8, 0x2db0, 0x2db8, 0x2dc0, 0x2dc8, 0x2dd0, 0x2dd8, 0x3005, 0x3021, 0x3031, 0x3038, 0x3041, 0x309b, 0x30a1, 0x30fc, 0x3105, 0x3131, 0x31a0, 0x31f0, 0x3400, 0x3500, 0x3600, 0x3700, 0x3800, 0x3900, 0x3a00, 0x3b00, 0x3c00, 0x3d00, 0x3e00, 0x3f00, 0x4000, 0x4100, 0x4200, 0x4300, 0x4400, 0x4500, 0x4600, 0x4700, 0x4800, 0x4900, 0x4a00, 0x4b00, 0x4c00, 0x4d00, 0x4e00, 0x4f00, 0x5000, 0x5100, 0x5200, 0x5300, 0x5400, 0x5500, 0x5600, 0x5700, 0x5800, 0x5900, 0x5a00, 0x5b00, 0x5c00, 0x5d00, 0x5e00, 0x5f00, 0x6000, 0x6100, 0x6200, 0x6300, 0x6400, 0x6500, 0x6600, 0x6700, 0x6800, 0x6900, 0x6a00, 0x6b00, 0x6c00, 0x6d00, 0x6e00, 0x6f00, 0x7000, 0x7100, 0x7200, 0x7300, 0x7400, 0x7500, 0x7600, 0x7700, 0x7800, 0x7900, 0x7a00, 0x7b00, 0x7c00, 0x7d00, 0x7e00, 0x7f00, 0x8000, 0x8100, 0x8200, 0x8300, 0x8400, 0x8500, 0x8600, 0x8700, 0x8800, 0x8900, 0x8a00, 0x8b00, 0x8c00, 0x8d00, 0x8e00, 0x8f00, 0x9000, 0x9100, 0x9200, 0x9300, 0x9400, 0x9500, 0x9600, 0x9700, 0x9800, 0x9900, 0x9a00, 0x9b00, 0x9c00, 0x9d00, 0x9e00, 0x9f00, 0xa000, 0xa100, 0xa200, 0xa300, 0xa400, 0xa4d0, 0xa500, 0xa600, 0xa610, 0xa62a, 0xa640, 0xa67f, 0xa6a0, 0xa717, 0xa722, 0xa78b, 0xa7c2, 0xa7f5, 0xa803, 0xa807, 0xa80c, 0xa840, 0xa882, 0xa8f2, 0xa8fd, 0xa90a, 0xa930, 0xa960, 0xa984, 0xa9e0, 0xa9e6, 0xa9fa, 0xaa00, 0xaa40, 0xaa44, 0xaa60, 0xaa7e, 0xaab5, 0xaab9, 0xaadb, 0xaae0, 0xaaf2, 0xab01, 0xab09, 0xab11, 0xab20, 0xab28, 0xab30, 0xab5c, 0xab70, 0xac00, 0xad00, 0xae00, 0xaf00, 0xb000, 0xb100, 0xb200, 0xb300, 0xb400, 0xb500, 0xb600, 0xb700, 0xb800, 0xb900, 0xba00, 0xbb00, 0xbc00, 0xbd00, 0xbe00, 0xbf00, 0xc000, 0xc100, 0xc200, 0xc300, 0xc400, 0xc500, 0xc600, 0xc700, 0xc800, 0xc900, 0xca00, 0xcb00, 0xcc00, 0xcd00, 0xce00, 0xcf00, 0xd000, 0xd100, 0xd200, 0xd300, 0xd400, 0xd500, 0xd600, 0xd700, 0xd7b0, 0xd7cb, 0xf900, 0xfa00, 0xfa70, 0xfb00, 0xfb13, 0xfb1f, 0xfb2a, 0xfb38, 0xfb40, 0xfb43, 0xfb46, 0xfbd3, 0xfcd3, 0xfd50, 0xfd92, 0xfdf0, 0xfe70, 0xfe76, 0xff21, 0xff41, 0xff66, 0xffc2, 0xffca, 0xffd2, 0xffda }; /** * Character interval lengths for ID_Start. */ static const uint8_t lit_unicode_id_start_interval_lengths[] JERRY_ATTR_CONST_DATA = { 0x0016, 0x001e, 0x00ff, 0x00c9, 0x000b, 0x0004, 0x0004, 0x0001, 0x0003, 0x0002, 0x0013, 0x0052, 0x008a, 0x00a5, 0x0025, 0x0028, 0x001a, 0x0003, 0x002a, 0x0001, 0x0062, 0x0001, 0x0001, 0x0002, 0x001d, 0x0058, 0x0020, 0x0001, 0x0015, 0x0018, 0x000a, 0x0014, 0x0011, 0x0035, 0x0009, 0x000f, 0x0007, 0x0001, 0x0015, 0x0006, 0x0003, 0x0001, 0x0002, 0x0001, 0x0005, 0x0001, 0x0015, 0x0006, 0x0001, 0x0001, 0x0001, 0x0003, 0x0002, 0x0008, 0x0002, 0x0015, 0x0006, 0x0001, 0x0004, 0x0001, 0x0007, 0x0001, 0x0015, 0x0006, 0x0001, 0x0004, 0x0001, 0x0002, 0x0005, 0x0002, 0x0003, 0x0001, 0x0001, 0x0001, 0x0002, 0x000b, 0x0007, 0x0002, 0x0016, 0x000f, 0x0002, 0x0001, 0x0007, 0x0002, 0x0016, 0x0009, 0x0004, 0x0001, 0x0001, 0x0008, 0x0002, 0x0028, 0x0002, 0x0002, 0x0005, 0x0011, 0x0017, 0x0008, 0x0006, 0x002f, 0x0001, 0x0006, 0x0001, 0x0004, 0x0017, 0x0009, 0x0001, 0x0004, 0x0003, 0x0007, 0x0023, 0x0004, 0x002a, 0x0005, 0x0003, 0x0001, 0x0002, 0x000c, 0x0025, 0x002a, 0x00ff, 0x004c, 0x0003, 0x0006, 0x0003, 0x0028, 0x0003, 0x0020, 0x0003, 0x0006, 0x0003, 0x000e, 0x0038, 0x0003, 0x0042, 0x000f, 0x0055, 0x0005, 0x00ff, 0x00ff, 0x006b, 0x0010, 0x0019, 0x004a, 0x000a, 0x000c, 0x0003, 0x0011, 0x0011, 0x000c, 0x0002, 0x0033, 0x0058, 0x0028, 0x0045, 0x001e, 0x001d, 0x0004, 0x002b, 0x0019, 0x0016, 0x0034, 0x002e, 0x0006, 0x001d, 0x0001, 0x002b, 0x0023, 0x0002, 0x0023, 0x0008, 0x002a, 0x0002, 0x0003, 0x0005, 0x0001, 0x00bf, 0x00ff, 0x0015, 0x0005, 0x0025, 0x0005, 0x0007, 0x001e, 0x0034, 0x0006, 0x0002, 0x0006, 0x0003, 0x0005, 0x000c, 0x0002, 0x0006, 0x000c, 0x0009, 0x0005, 0x000f, 0x0003, 0x0004, 0x0028, 0x002e, 0x002e, 0x0084, 0x0003, 0x0001, 0x0025, 0x0037, 0x0016, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0002, 0x0008, 0x0004, 0x0004, 0x0055, 0x0004, 0x0059, 0x0003, 0x002a, 0x005d, 0x001f, 0x000f, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00bf, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00fc, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x008c, 0x002d, 0x00ff, 0x000c, 0x000f, 0x0001, 0x002e, 0x001e, 0x004f, 0x0008, 0x0066, 0x0034, 0x0008, 0x000c, 0x0002, 0x0003, 0x0016, 0x0033, 0x0031, 0x0005, 0x0001, 0x001b, 0x0016, 0x001c, 0x002e, 0x0004, 0x0009, 0x0004, 0x0028, 0x0002, 0x0007, 0x0016, 0x0031, 0x0001, 0x0004, 0x0002, 0x000a, 0x0002, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x002a, 0x000d, 0x0072, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00a3, 0x0016, 0x0030, 0x00ff, 0x006d, 0x0069, 0x0006, 0x0004, 0x0009, 0x000c, 0x0004, 0x0001, 0x0001, 0x006b, 0x00ff, 0x006a, 0x003f, 0x0035, 0x000b, 0x0004, 0x0086, 0x0019, 0x0019, 0x0058, 0x0005, 0x0005, 0x0005, 0x0002 }; /** * Non-interval characters for ID_Start. */ static const uint16_t lit_unicode_id_start_chars[] JERRY_ATTR_CONST_DATA = { 0x00aa, 0x00b5, 0x00ba, 0x02ec, 0x02ee, 0x037f, 0x0386, 0x038c, 0x0559, 0x06d5, 0x06ff, 0x0710, 0x07b1, 0x07fa, 0x081a, 0x0824, 0x0828, 0x093d, 0x0950, 0x09b2, 0x09bd, 0x09ce, 0x09fc, 0x0a5e, 0x0abd, 0x0ad0, 0x0af9, 0x0b3d, 0x0b71, 0x0b83, 0x0b9c, 0x0bd0, 0x0c3d, 0x0c80, 0x0cbd, 0x0cde, 0x0d3d, 0x0d4e, 0x0dbd, 0x0e84, 0x0ea5, 0x0ebd, 0x0ec6, 0x0f00, 0x103f, 0x1061, 0x108e, 0x10c7, 0x10cd, 0x1258, 0x12c0, 0x17d7, 0x17dc, 0x18aa, 0x1aa7, 0x1cfa, 0x1f59, 0x1f5b, 0x1f5d, 0x1fbe, 0x2071, 0x207f, 0x2102, 0x2107, 0x2115, 0x2124, 0x2126, 0x2128, 0x214e, 0x2d27, 0x2d2d, 0x2d6f, 0xa8fb, 0xa9cf, 0xaa7a, 0xaab1, 0xaac0, 0xaac2, 0xfb1d, 0xfb3e }; /** * Character interval starting points for ID_Continue. */ static const uint16_t lit_unicode_id_continue_interval_starts[] JERRY_ATTR_CONST_DATA = { 0x0300, 0x0483, 0x0591, 0x05c1, 0x05c4, 0x0610, 0x064b, 0x06d6, 0x06df, 0x06e7, 0x06ea, 0x06f0, 0x0730, 0x07a6, 0x07c0, 0x07eb, 0x0816, 0x081b, 0x0825, 0x0829, 0x0859, 0x08d3, 0x08e3, 0x093a, 0x093e, 0x0951, 0x0962, 0x0966, 0x0981, 0x09be, 0x09c7, 0x09cb, 0x09e2, 0x09e6, 0x0a01, 0x0a3e, 0x0a47, 0x0a4b, 0x0a66, 0x0a81, 0x0abe, 0x0ac7, 0x0acb, 0x0ae2, 0x0ae6, 0x0afa, 0x0b01, 0x0b3e, 0x0b47, 0x0b4b, 0x0b55, 0x0b62, 0x0b66, 0x0bbe, 0x0bc6, 0x0bca, 0x0be6, 0x0c00, 0x0c3e, 0x0c46, 0x0c4a, 0x0c55, 0x0c62, 0x0c66, 0x0c81, 0x0cbe, 0x0cc6, 0x0cca, 0x0cd5, 0x0ce2, 0x0ce6, 0x0d00, 0x0d3b, 0x0d3e, 0x0d46, 0x0d4a, 0x0d62, 0x0d66, 0x0d81, 0x0dcf, 0x0dd8, 0x0de6, 0x0df2, 0x0e34, 0x0e47, 0x0e50, 0x0eb4, 0x0ec8, 0x0ed0, 0x0f18, 0x0f20, 0x0f3e, 0x0f71, 0x0f86, 0x0f8d, 0x0f99, 0x102b, 0x1040, 0x1056, 0x105e, 0x1062, 0x1067, 0x1071, 0x1082, 0x108f, 0x135d, 0x1369, 0x1712, 0x1732, 0x1752, 0x1772, 0x17b4, 0x17e0, 0x180b, 0x1810, 0x1920, 0x1930, 0x1946, 0x19d0, 0x1a17, 0x1a55, 0x1a60, 0x1a7f, 0x1a90, 0x1ab0, 0x1abf, 0x1b00, 0x1b34, 0x1b50, 0x1b6b, 0x1b80, 0x1ba1, 0x1bb0, 0x1be6, 0x1c24, 0x1c40, 0x1c50, 0x1cd0, 0x1cd4, 0x1cf7, 0x1dc0, 0x1dfb, 0x200c, 0x203f, 0x20d0, 0x20e5, 0x2cef, 0x2de0, 0x302a, 0x3099, 0xa620, 0xa674, 0xa69e, 0xa6f0, 0xa823, 0xa880, 0xa8b4, 0xa8d0, 0xa8e0, 0xa8ff, 0xa926, 0xa947, 0xa980, 0xa9b3, 0xa9d0, 0xa9f0, 0xaa29, 0xaa4c, 0xaa50, 0xaa7b, 0xaab2, 0xaab7, 0xaabe, 0xaaeb, 0xaaf5, 0xabe3, 0xabec, 0xabf0, 0xfe00, 0xfe20, 0xfe33, 0xfe4d, 0xff10 }; /** * Character interval lengths for ID_Continue. */ static const uint8_t lit_unicode_id_continue_interval_lengths[] JERRY_ATTR_CONST_DATA = { 0x006f, 0x0004, 0x002c, 0x0001, 0x0001, 0x000a, 0x001e, 0x0006, 0x0005, 0x0001, 0x0003, 0x0009, 0x001a, 0x000a, 0x0009, 0x0008, 0x0003, 0x0008, 0x0002, 0x0004, 0x0002, 0x000e, 0x0020, 0x0002, 0x0011, 0x0006, 0x0001, 0x0009, 0x0002, 0x0006, 0x0001, 0x0002, 0x0001, 0x0009, 0x0002, 0x0004, 0x0001, 0x0002, 0x000b, 0x0002, 0x0007, 0x0002, 0x0002, 0x0001, 0x0009, 0x0005, 0x0002, 0x0006, 0x0001, 0x0002, 0x0002, 0x0001, 0x0009, 0x0004, 0x0002, 0x0003, 0x0009, 0x0004, 0x0006, 0x0002, 0x0003, 0x0001, 0x0001, 0x0009, 0x0002, 0x0006, 0x0002, 0x0003, 0x0001, 0x0001, 0x0009, 0x0003, 0x0001, 0x0006, 0x0002, 0x0003, 0x0001, 0x0009, 0x0002, 0x0005, 0x0007, 0x0009, 0x0001, 0x0006, 0x0007, 0x0009, 0x0008, 0x0005, 0x0009, 0x0001, 0x0009, 0x0001, 0x0013, 0x0001, 0x000a, 0x0023, 0x0013, 0x0009, 0x0003, 0x0002, 0x0002, 0x0006, 0x0003, 0x000b, 0x000e, 0x0002, 0x0008, 0x0002, 0x0002, 0x0001, 0x0001, 0x001f, 0x0009, 0x0002, 0x0009, 0x000b, 0x000b, 0x0009, 0x000a, 0x0004, 0x0009, 0x001c, 0x000a, 0x0009, 0x000d, 0x0001, 0x0004, 0x0010, 0x0009, 0x0008, 0x0002, 0x000c, 0x0009, 0x000d, 0x0013, 0x0009, 0x0009, 0x0002, 0x0014, 0x0002, 0x0039, 0x0004, 0x0001, 0x0001, 0x000c, 0x000b, 0x0002, 0x001f, 0x0005, 0x0001, 0x0009, 0x0009, 0x0001, 0x0001, 0x0004, 0x0001, 0x0011, 0x0009, 0x0011, 0x000a, 0x0007, 0x000c, 0x0003, 0x000d, 0x0009, 0x0009, 0x000d, 0x0001, 0x0009, 0x0002, 0x0002, 0x0001, 0x0001, 0x0004, 0x0001, 0x0007, 0x0001, 0x0009, 0x000f, 0x000f, 0x0001, 0x0002, 0x0009 }; /** * Non-interval characters for ID_Continue. */ static const uint16_t lit_unicode_id_continue_chars[] JERRY_ATTR_CONST_DATA = { 0x00b7, 0x0387, 0x05bf, 0x05c7, 0x0670, 0x0711, 0x07fd, 0x09bc, 0x09d7, 0x09fe, 0x0a3c, 0x0a51, 0x0a75, 0x0abc, 0x0b3c, 0x0b82, 0x0bd7, 0x0cbc, 0x0d57, 0x0dca, 0x0dd6, 0x0e31, 0x0eb1, 0x0f35, 0x0f37, 0x0f39, 0x0fc6, 0x17dd, 0x18a9, 0x1ced, 0x1cf4, 0x2054, 0x20e1, 0x2d7f, 0xa66f, 0xa802, 0xa806, 0xa80b, 0xa82c, 0xa9e5, 0xaa43, 0xaab0, 0xaac1, 0xfb1e, 0xff3f }; /** * Character interval starting points for White_Space. */ static const uint16_t lit_unicode_white_space_interval_starts[] JERRY_ATTR_CONST_DATA = { 0x2000 }; /** * Character interval lengths for White_Space. */ static const uint8_t lit_unicode_white_space_interval_lengths[] JERRY_ATTR_CONST_DATA = { 0x000a }; /** * Non-interval characters for White_Space. */ static const uint16_t lit_unicode_white_space_chars[] JERRY_ATTR_CONST_DATA = { 0x00a0, 0x1680, 0x202f, 0x205f, 0x3000 }; external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-big-uint.cpp000664 001750 001750 00000140325 15164251010 045541 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-big-uint.h" #include "ecma-helpers.h" #include "jmem.h" #include "lit-char-helpers.h" #if JERRY_BUILTIN_BIGINT JERRY_STATIC_ASSERT ((sizeof (ecma_bigint_two_digits_t) == 2 * sizeof (ecma_bigint_digit_t)), ecma_big_int_two_digits_must_be_twice_as_long_as_ecma_big_int_digit); JERRY_STATIC_ASSERT (((1 << (int)ECMA_BIGINT_DIGIT_SHIFT) == (8 * sizeof (ecma_bigint_digit_t))), ecma_bigint_digit_shift_is_incorrect); JERRY_STATIC_ASSERT ((((int)ECMA_BIG_UINT_BITWISE_DECREASE_LEFT << 1) == (int)ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT), ecma_big_uint_bitwise_left_and_right_sub_option_bits_must_follow_each_other); /** * Create a new BigInt value * * @return new BigInt value, NULL on error */ ecma_extended_primitive_t * ecma_bigint_create (uint32_t size) /**< size of the new BigInt value */ { JERRY_ASSERT (size > 0); JERRY_ASSERT ((size % sizeof (ecma_bigint_digit_t)) == 0); if (JERRY_UNLIKELY (size > ECMA_BIGINT_MAX_SIZE)) { return NULL; } ecma_extended_primitive_t *value_p; size_t mem_size = ECMA_BIGINT_GET_BYTE_SIZE (size) + sizeof (ecma_extended_primitive_t); value_p = (ecma_extended_primitive_t *) jmem_heap_alloc_block_null_on_error (mem_size); if (JERRY_UNLIKELY (value_p == NULL)) { return NULL; } value_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT; value_p->u.bigint_sign_and_size = size; return value_p; } /* ecma_bigint_create */ /** * Extend a BigUInt value with a new data prefix value * * @return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_extend (ecma_extended_primitive_t *value_p, /**< BigUInt value */ ecma_bigint_digit_t digit) /**< new digit */ { uint32_t old_size = ECMA_BIGINT_GET_SIZE (value_p); if (ECMA_BIGINT_SIZE_IS_ODD (old_size)) { value_p->u.bigint_sign_and_size += (uint32_t) sizeof (ecma_bigint_digit_t); *ECMA_BIGINT_GET_DIGITS (value_p, old_size) = digit; return value_p; } ecma_extended_primitive_t *result_p = ecma_bigint_create (old_size + (uint32_t) sizeof (ecma_bigint_digit_t)); if (JERRY_UNLIKELY (result_p == NULL)) { ecma_deref_bigint (value_p); return NULL; } memcpy (result_p + 1, value_p + 1, old_size); ecma_deref_bigint (value_p); *ECMA_BIGINT_GET_DIGITS (result_p, old_size) = digit; return result_p; } /* ecma_big_uint_extend */ /** * Count the number of leading zero bits of a digit * * return number of leading zero bits */ ecma_bigint_digit_t ecma_big_uint_count_leading_zero (ecma_bigint_digit_t digit) /**< digit value */ { ecma_bigint_digit_t shift = 4 * sizeof (ecma_bigint_digit_t); ecma_bigint_digit_t result = 8 * sizeof (ecma_bigint_digit_t); do { ecma_bigint_digit_t value = digit >> shift; if (value > 0) { digit = value; result -= shift; } shift >>= 1; } while (shift > 0); return result - digit; } /* ecma_big_uint_count_leading_zero */ /** * Helper function which discards the leading zero digits of a BigUInt value * * @return new BigUInt value, NULL on error */ static ecma_extended_primitive_t * ecma_big_uint_normalize_result (ecma_extended_primitive_t *value_p, /**< BigUInt value */ ecma_bigint_digit_t *last_digit_p) /**< points to the end of BigUInt */ { JERRY_ASSERT (last_digit_p[-1] == 0); ecma_bigint_digit_t *first_digit_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); /* The following code is tricky. The value stored in first_digit_p[-1] is the size * of the BigUInt value, and it cannot be zero. Hence the loop below will terminate. */ JERRY_ASSERT (first_digit_p[-1] != 0); do { --last_digit_p; } while (last_digit_p[-1] == 0); JERRY_ASSERT (last_digit_p >= first_digit_p); if (first_digit_p == last_digit_p) { ecma_deref_bigint (value_p); return ECMA_BIGINT_POINTER_TO_ZERO; } uint32_t new_size = (uint32_t) ((uint8_t *) last_digit_p - (uint8_t *) first_digit_p); if (ECMA_BIGINT_SIZE_IS_ODD (new_size) && ((new_size + sizeof (ecma_bigint_digit_t)) == ECMA_BIGINT_GET_SIZE (value_p))) { value_p->u.bigint_sign_and_size -= (uint32_t) sizeof (ecma_bigint_digit_t); return value_p; } ecma_extended_primitive_t *result_p = ecma_bigint_create (new_size); if (JERRY_UNLIKELY (result_p == NULL)) { ecma_deref_bigint (value_p); return NULL; } memcpy (ECMA_BIGINT_GET_DIGITS (result_p, 0), ECMA_BIGINT_GET_DIGITS (value_p, 0), new_size); ecma_deref_bigint (value_p); return result_p; } /* ecma_big_uint_normalize_result */ /** * Helper function which increases the result by 1 and extends or shrinks the BigUInt when necessary * * @return new BigUInt value, NULL on error */ static ecma_extended_primitive_t * ecma_big_uint_increase_result (ecma_extended_primitive_t *value_p) /**< BigUInt value */ { uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); JERRY_ASSERT (size > 0); ecma_bigint_digit_t *first_digit_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); ecma_bigint_digit_t *last_digit_p = ECMA_BIGINT_GET_DIGITS (value_p, size); while (*first_digit_p == ~((ecma_bigint_digit_t) 0)) { *first_digit_p++ = 0; if (first_digit_p == last_digit_p) { return ecma_big_uint_extend (value_p, 1); } } (*first_digit_p)++; if (last_digit_p[-1] != 0) { return value_p; } return ecma_big_uint_normalize_result (value_p, last_digit_p); } /* ecma_big_uint_increase_result */ /** * Compare two BigUInt numbers * * return -1, if left value < right value, 0 if they are equal, and 1 otherwise */ int ecma_big_uint_compare (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ { uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); if (left_size > right_size) { return 1; } if (left_size < right_size) { return -1; } ecma_bigint_digit_t *start_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size); ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, left_size); do { ecma_bigint_digit_t left_value = *(--left_p); ecma_bigint_digit_t right_value = *(--right_p); if (left_value < right_value) { return -1; } if (left_value > right_value) { return 1; } } while (left_p > start_p); return 0; } /* ecma_big_uint_compare */ /** * In-place multiply and addition operation with digit * * return updated value on success, NULL if no memory is available */ ecma_extended_primitive_t * ecma_big_uint_mul_digit (ecma_extended_primitive_t *value_p, /**< BigUInt value */ ecma_bigint_digit_t mul, /**< multiply value */ ecma_bigint_digit_t add) /**< addition value */ { JERRY_ASSERT (mul > 1); JERRY_ASSERT (add < mul); if (JERRY_UNLIKELY (value_p == NULL)) { JERRY_ASSERT (add > 0); value_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); if (JERRY_UNLIKELY (value_p == NULL)) { return NULL; } *ECMA_BIGINT_GET_DIGITS (value_p, 0) = add; return value_p; } uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (value_p, size); ecma_bigint_digit_t carry = add; do { ecma_bigint_two_digits_t multiply_result = ((ecma_bigint_two_digits_t) *current_p) * mul; ecma_bigint_digit_t multiply_result_low, new_carry; multiply_result_low = (ecma_bigint_digit_t) multiply_result; new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t))); multiply_result_low += carry; if (multiply_result_low < carry) { new_carry++; } *current_p++ = multiply_result_low; carry = new_carry; } while (current_p < end_p); if (carry == 0) { return value_p; } return ecma_big_uint_extend (value_p, carry); } /* ecma_big_uint_mul_digit */ /** * Convert a BigUInt to a human readable number * * return char sequence on success, NULL otherwise */ lit_utf8_byte_t * ecma_big_uint_to_string (ecma_extended_primitive_t *value_p, /**< BigUInt value */ uint32_t radix, /**< radix number between 2 and 36 */ uint32_t *char_start_p, /**< [out] start offset of numbers */ uint32_t *char_size_p) /**< [out] size of the output buffer */ { uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); JERRY_ASSERT (radix >= 2 && radix <= 36); JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); uint32_t max_size = size * 8; if (radix < 16) { if (radix >= 8) { /* Most frequent case. */ max_size = (max_size + 2) / 3; } else if (radix >= 4) { max_size = (max_size + 1) >> 1; } } else if (radix < 32) { max_size = (max_size + 3) >> 2; } else { max_size = (max_size + 4) / 5; } /* This space can be used to store a sign. */ max_size += (uint32_t) (2 * sizeof (ecma_bigint_digit_t) - 1); max_size &= ~(uint32_t) (sizeof (ecma_bigint_digit_t) - 1); *char_size_p = max_size; lit_utf8_byte_t *result_p = (lit_utf8_byte_t *) jmem_heap_alloc_block_null_on_error (max_size); if (JERRY_UNLIKELY (result_p == NULL)) { return NULL; } memcpy (result_p, value_p + 1, size); ecma_bigint_digit_t *start_p = (ecma_bigint_digit_t *) (result_p + size); ecma_bigint_digit_t *end_p = (ecma_bigint_digit_t *) result_p; lit_utf8_byte_t *string_p = result_p + max_size; do { ecma_bigint_digit_t *current_p = (ecma_bigint_digit_t *) start_p; ecma_bigint_digit_t remainder = 0; if (sizeof (uintptr_t) == sizeof (ecma_bigint_two_digits_t)) { do { ecma_bigint_two_digits_t result = *(--current_p) | ECMA_BIGINT_HIGH_DIGIT (remainder); *current_p = (ecma_bigint_digit_t) (result / radix); remainder = (ecma_bigint_digit_t) (result % radix); } while (current_p > end_p); } else { if (ECMA_BIGINT_SIZE_IS_ODD ((uintptr_t) current_p - (uintptr_t) end_p)) { ecma_bigint_digit_t result = *(--current_p); *current_p = result / radix; remainder = result % radix; } while (current_p > end_p) { /* The following algorithm splits the 64 bit input into three numbers, extend * them with remainder, divide them by radix, and updates the three bit ranges * corresponding to the three numbers. */ const uint32_t extract_bits_low = 10; const uint32_t extract_bits_low_mask = (uint32_t) ((1 << extract_bits_low) - 1); const uint32_t extract_bits_high = (uint32_t) ((sizeof (ecma_bigint_digit_t) * 8) - extract_bits_low); const uint32_t extract_bits_high_mask = (uint32_t) ((1 << extract_bits_high) - 1); ecma_bigint_digit_t result_high = current_p[-1]; ecma_bigint_digit_t result_mid = (result_high & extract_bits_low_mask) << extract_bits_low; result_high = (result_high >> extract_bits_low) | (remainder << extract_bits_high); result_mid |= (result_high % radix) << (extract_bits_low * 2); result_high = (result_high / radix) << extract_bits_low; ecma_bigint_digit_t result_low = current_p[-2]; result_mid |= result_low >> extract_bits_high; result_low = (result_low & extract_bits_high_mask) | ((result_mid % radix) << extract_bits_high); result_mid = result_mid / radix; current_p[-1] = result_high | (result_mid >> extract_bits_low); current_p[-2] = (result_low / radix) | (result_mid << extract_bits_high); remainder = result_low % radix; current_p -= 2; } } *(--string_p) = (lit_utf8_byte_t) ((remainder < 10) ? (remainder + LIT_CHAR_0) : (remainder + (LIT_CHAR_LOWERCASE_A - 10))); JERRY_ASSERT (string_p >= (lit_utf8_byte_t *) start_p); if (start_p[-1] == 0) { start_p--; } } while (start_p > end_p); *char_start_p = (uint32_t) (string_p - result_p); return result_p; } /* ecma_big_uint_to_string */ /** * Increase the value of a BigUInt value by 1 * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_increase (ecma_extended_primitive_t *value_p) /**< BigUInt value */ { uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); ecma_bigint_digit_t *digits_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); ecma_bigint_digit_t *digits_end_p = ECMA_BIGINT_GET_DIGITS (value_p, size); if (JERRY_UNLIKELY (digits_p[0] == ~((ecma_bigint_digit_t) 0) && digits_end_p[-1] == ~((ecma_bigint_digit_t) 0))) { do { digits_p++; } while (digits_p < digits_end_p && digits_p[0] == ~((ecma_bigint_digit_t) 0)); if (digits_p == digits_end_p) { ecma_extended_primitive_t *result_value_p; result_value_p = ecma_bigint_create ((uint32_t) (size + sizeof (ecma_bigint_digit_t))); if (JERRY_UNLIKELY (result_value_p == NULL)) { return NULL; } memset (ECMA_BIGINT_GET_DIGITS (result_value_p, 0), 0, size); *ECMA_BIGINT_GET_DIGITS (result_value_p, size) = 1; return result_value_p; } digits_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); } ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size); if (JERRY_UNLIKELY (result_value_p == NULL)) { return NULL; } ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); while (digits_p[0] == ~((ecma_bigint_digit_t) 0)) { digits_p++; *result_p++ = 0; } *result_p++ = (*digits_p++) + 1; if (digits_p < digits_end_p) { memcpy (result_p, digits_p, (size_t) ((uint8_t *) digits_end_p - (uint8_t *) digits_p)); } return result_value_p; } /* ecma_big_uint_increase */ /** * Decrease the value of a BigUInt value by 1 * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_decrease (ecma_extended_primitive_t *value_p) /**< BigUInt value */ { uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); ecma_bigint_digit_t *digits_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); ecma_bigint_digit_t *digits_end_p = ECMA_BIGINT_GET_DIGITS (value_p, size); JERRY_ASSERT (size > sizeof (ecma_bigint_digit_t) || *digits_p > 1); if (JERRY_UNLIKELY (digits_p[0] == 0 && digits_end_p[-1] == 1)) { do { digits_p++; JERRY_ASSERT (digits_p < digits_end_p); } while (digits_p[0] == 0); if (digits_p + 1 == digits_end_p) { size -= (uint32_t) sizeof (ecma_bigint_digit_t); ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size); if (JERRY_UNLIKELY (result_value_p == NULL)) { return NULL; } memset (ECMA_BIGINT_GET_DIGITS (result_value_p, 0), 0xff, size); return result_value_p; } digits_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); } ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size); if (JERRY_UNLIKELY (result_value_p == NULL)) { return NULL; } ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); while (digits_p[0] == 0) { digits_p++; *result_p++ = ~((ecma_bigint_digit_t) 0); } *result_p++ = (*digits_p++) - 1; if (digits_p < digits_end_p) { memcpy (result_p, digits_p, (size_t) ((uint8_t *) digits_end_p - (uint8_t *) digits_p)); } return result_value_p; } /* ecma_big_uint_decrease */ /** * Add right BigUInt value to the left BigUInt value * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_add (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ { uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); if (left_size < right_size) { /* Swap values. */ ecma_extended_primitive_t *tmp_value_p = left_value_p; left_value_p = right_value_p; right_value_p = tmp_value_p; uint32_t tmp_size = left_size; left_size = right_size; right_size = tmp_size; } ecma_extended_primitive_t *result_p = ecma_bigint_create (left_size); if (JERRY_UNLIKELY (result_p == NULL)) { return NULL; } ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_p, right_size); ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0); ecma_bigint_digit_t carry = 0; left_size -= right_size; do { ecma_bigint_digit_t left = *left_p++; if (carry == 0 || left != ~(ecma_bigint_digit_t) 0) { left += carry; carry = 0; } else { left = 0; carry = 1; } ecma_bigint_digit_t right = *right_p++; left += right; if (left < right) { JERRY_ASSERT (carry == 0); carry = 1; } *current_p++ = left; } while (current_p < end_p); end_p = (ecma_bigint_digit_t *) (((uint8_t *) end_p) + left_size); if (carry != 0) { while (true) { if (JERRY_UNLIKELY (current_p == end_p)) { return ecma_big_uint_extend (result_p, 1); } ecma_bigint_digit_t value = *left_p++; if (value != ~(ecma_bigint_digit_t) 0) { *current_p++ = value + 1; break; } *current_p++ = 0; } } if (current_p < end_p) { memcpy (current_p, left_p, (size_t) ((uint8_t *) end_p - (uint8_t *) current_p)); } return result_p; } /* ecma_big_uint_add */ /** * Subtract right BigUInt value from the left BigUInt value * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_sub (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ { uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); JERRY_ASSERT (left_size >= right_size); ecma_extended_primitive_t *result_p = ecma_bigint_create (left_size); if (JERRY_UNLIKELY (result_p == NULL)) { return NULL; } ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_p, right_size); ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0); ecma_bigint_digit_t carry = 0; left_size -= right_size; do { ecma_bigint_digit_t left = *left_p++; ecma_bigint_digit_t right = *right_p++; if (carry == 0 || left != 0) { left -= carry; carry = left < right; } else { left = ~(ecma_bigint_digit_t) 0; carry = 1; } *current_p++ = left - right; } while (current_p < end_p); end_p = (ecma_bigint_digit_t *) (((uint8_t *) end_p) + left_size); if (carry != 0) { while (true) { JERRY_ASSERT (current_p < end_p); ecma_bigint_digit_t value = *left_p++; if (value != 0) { *current_p++ = value - 1; break; } *current_p++ = ~(ecma_bigint_digit_t) 0; } } if (current_p < end_p) { memcpy (current_p, left_p, (size_t) ((uint8_t *) end_p - (uint8_t *) current_p)); return result_p; } if (current_p[-1] != 0) { return result_p; } return ecma_big_uint_normalize_result (result_p, current_p); } /* ecma_big_uint_sub */ /** * Multiply two BigUInt values * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_mul (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ { uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); if (left_size < right_size) { /* Swap values. */ ecma_extended_primitive_t *tmp_value_p = left_value_p; left_value_p = right_value_p; right_value_p = tmp_value_p; uint32_t tmp_size = left_size; left_size = right_size; right_size = tmp_size; } uint32_t result_size = left_size + right_size - (uint32_t) sizeof (ecma_bigint_digit_t); ecma_extended_primitive_t *result_p = ecma_bigint_create (result_size); if (JERRY_UNLIKELY (result_p == NULL)) { return NULL; } memset (ECMA_BIGINT_GET_DIGITS (result_p, 0), 0, result_size); /* Lower amount of space is allocated by default. This value provides extra space if needed. */ ecma_bigint_digit_t extra_space[1] = { 0 }; ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0); ecma_bigint_digit_t *right_end_p = ECMA_BIGINT_GET_DIGITS (right_value_p, right_size); ecma_bigint_digit_t *left_start_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t *left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size); ecma_bigint_digit_t *result_start_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); ecma_bigint_digit_t *result_end_p = ECMA_BIGINT_GET_DIGITS (result_p, result_size); do { ecma_bigint_two_digits_t right = *right_p++; if (right == 0) { result_start_p++; continue; } ecma_bigint_digit_t *left_p = left_start_p; ecma_bigint_digit_t *destination_p = result_start_p; ecma_bigint_digit_t carry = 0; do { JERRY_ASSERT (destination_p != (ecma_bigint_digit_t *) (extra_space + 1)); ecma_bigint_two_digits_t multiply_result; ecma_bigint_digit_t multiply_result_low, new_carry; ecma_bigint_digit_t value = *destination_p; multiply_result = ((ecma_bigint_two_digits_t) (*left_p++)) * ((ecma_bigint_two_digits_t) right); multiply_result_low = (ecma_bigint_digit_t) multiply_result; value += multiply_result_low; new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t))); /* The new_carry can never overflow because: * a) If left or right is less than 0xff..ff, new_carry will be less than or equal to * 0xff...fd, and increasing it by maximum of two (carries) cannot overflow. * b) If left and right are both equal to 0xff..ff, multiply_result_low will be 1, * and computing value + carry + 1 can only increase new_carry at most once. */ if (value < multiply_result_low) { JERRY_ASSERT (new_carry < ~(ecma_bigint_digit_t) 0); new_carry++; } value += carry; if (value < carry) { JERRY_ASSERT (new_carry < ~(ecma_bigint_digit_t) 0); new_carry++; } carry = new_carry; *destination_p++ = value; if (destination_p == result_end_p) { destination_p = (ecma_bigint_digit_t *) extra_space; } } while (left_p < left_end_p); while (carry > 0) { JERRY_ASSERT (destination_p != (ecma_bigint_digit_t *) (extra_space + 1)); ecma_bigint_digit_t value = *destination_p; value += carry; carry = (value < carry); *destination_p++ = value; if (destination_p == result_end_p) { destination_p = (ecma_bigint_digit_t *) extra_space; } } result_start_p++; } while (right_p < right_end_p); if (extra_space[0] == 0) { return result_p; } return ecma_big_uint_extend (result_p, extra_space[0]); } /* ecma_big_uint_mul */ /** * Divide left BigUInt value with right digit value * * return new BigUInt value, NULL on error */ static ecma_extended_primitive_t * ecma_big_uint_div_digit (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ ecma_bigint_digit_t divisor_digit, /**< divisor value */ bool is_mod) /**< true if return with remainder */ { uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); JERRY_ASSERT (divisor_digit > 0); ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size - sizeof (ecma_bigint_digit_t)); ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t last_digit = *left_p; ecma_bigint_digit_t remainder = last_digit % divisor_digit; last_digit = last_digit / divisor_digit; ecma_extended_primitive_t *result_p = NULL; ecma_bigint_digit_t *current_p = NULL; if (!is_mod) { ecma_bigint_digit_t result_size = left_size; if (last_digit == 0) { result_size -= (uint32_t) sizeof (ecma_bigint_digit_t); } result_p = ecma_bigint_create (result_size); if (JERRY_UNLIKELY (result_p == NULL)) { return NULL; } current_p = ECMA_BIGINT_GET_DIGITS (result_p, result_size); if (last_digit != 0) { *(--current_p) = last_digit; } } while (left_p > end_p) { const uint32_t shift = 1 << ECMA_BIGINT_DIGIT_SHIFT; ecma_bigint_two_digits_t result = *(--left_p) | (((ecma_bigint_two_digits_t) remainder) << shift); if (!is_mod) { *(--current_p) = (ecma_bigint_digit_t) (result / divisor_digit); } remainder = (ecma_bigint_digit_t) (result % divisor_digit); } if (!is_mod) { JERRY_ASSERT (current_p == ECMA_BIGINT_GET_DIGITS (result_p, 0)); return result_p; } if (remainder == 0) { return ECMA_BIGINT_POINTER_TO_ZERO; } result_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); if (JERRY_UNLIKELY (result_p == NULL)) { return NULL; } *ECMA_BIGINT_GET_DIGITS (result_p, 0) = remainder; return result_p; } /* ecma_big_uint_div_digit */ /** * Shift left a BigUInt value by a digit value * * return newly allocated buffer, NULL on error */ static ecma_bigint_digit_t * ecma_big_uint_div_shift_left (ecma_extended_primitive_t *value_p, /**< BigUInt value */ ecma_bigint_digit_t shift_left, /**< left shift */ bool extend) /**< extend the result with an extra digit */ { uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); ecma_bigint_digit_t *source_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (value_p, size); if (extend) { size += (uint32_t) sizeof (ecma_bigint_digit_t); } ecma_bigint_digit_t *result_p = (ecma_bigint_digit_t *) jmem_heap_alloc_block_null_on_error (size); if (JERRY_UNLIKELY (result_p == NULL)) { return result_p; } if (shift_left == 0) { JERRY_ASSERT (extend); size -= (uint32_t) sizeof (ecma_bigint_digit_t); *(ecma_bigint_digit_t *) (((uint8_t *) result_p) + size) = 0; memcpy (result_p, source_p, size); return result_p; } ecma_bigint_digit_t *destination_p = result_p; ecma_bigint_digit_t carry = 0; uint32_t shift_right = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_left; do { ecma_bigint_digit_t value = *source_p++; *destination_p++ = (value << shift_left) | carry; carry = value >> shift_right; } while (source_p < end_p); if (extend) { *destination_p++ = carry; } return result_p; } /* ecma_big_uint_div_shift_left */ /** * Divide left BigUInt value with right BigUInt value * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_div_mod (ecma_extended_primitive_t *dividend_value_p, /**< divider BigUInt value */ ecma_extended_primitive_t *divisor_value_p, /**< divisor BigUInt value */ bool is_mod) /**< true if return with remainder instead of quotient */ { /* This algorithm is based on Donald Knuth’s "Algorithm D" */ uint32_t divisor_size = ECMA_BIGINT_GET_SIZE (divisor_value_p); JERRY_ASSERT (divisor_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (divisor_value_p, divisor_size) != 0); /* The divisor must have at least two digits, so the single digit case is handled separately. */ if (divisor_size == sizeof (ecma_bigint_digit_t)) { return ecma_big_uint_div_digit (dividend_value_p, *ECMA_BIGINT_GET_DIGITS (divisor_value_p, 0), is_mod); } /* D1. [Normalize] */ ecma_bigint_digit_t divisor_high = ECMA_BIGINT_GET_LAST_DIGIT (divisor_value_p, divisor_size); ecma_bigint_digit_t shift_left = ecma_big_uint_count_leading_zero (divisor_high); ecma_bigint_digit_t *buffer_p = ecma_big_uint_div_shift_left (dividend_value_p, shift_left, true); ecma_bigint_digit_t *dividend_end_p; ecma_bigint_digit_t *dividend_p; ecma_bigint_digit_t *divisor_end_p; ecma_bigint_digit_t divisor_low; if (JERRY_UNLIKELY (buffer_p == NULL)) { return NULL; } uint32_t dividend_size = ECMA_BIGINT_GET_SIZE (dividend_value_p); ecma_extended_primitive_t *result_p = NULL; ecma_bigint_digit_t *divisor_p; JERRY_ASSERT (dividend_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (dividend_value_p, dividend_size) != 0); JERRY_ASSERT (dividend_size >= divisor_size); if (shift_left > 0) { divisor_p = ecma_big_uint_div_shift_left (divisor_value_p, shift_left, false); if (JERRY_UNLIKELY (divisor_p == NULL)) { goto error; } } else { divisor_p = ECMA_BIGINT_GET_DIGITS (divisor_value_p, 0); } dividend_end_p = (ecma_bigint_digit_t *) (((uint8_t *) buffer_p) + dividend_size); dividend_p = (ecma_bigint_digit_t *) (((uint8_t *) dividend_end_p) - divisor_size); divisor_end_p = (ecma_bigint_digit_t *) (((uint8_t *) divisor_p) + divisor_size); divisor_low = divisor_end_p[-2]; divisor_high = divisor_end_p[-1]; JERRY_ASSERT ((divisor_high & (((ecma_bigint_digit_t) 1) << (8 * sizeof (ecma_bigint_digit_t) - 1))) != 0); do { /* D3. [Calculate Q′] */ ecma_bigint_digit_t result_div; /* This do-while(false) statement allows local declarations and early exit. */ do { ecma_bigint_digit_t result_mod; if (dividend_end_p[0] < divisor_high) { ecma_bigint_two_digits_t dividend = dividend_end_p[-1] | ECMA_BIGINT_HIGH_DIGIT (dividend_end_p[0]); result_div = (ecma_bigint_digit_t) (dividend / divisor_high); result_mod = (ecma_bigint_digit_t) (dividend % divisor_high); } else { JERRY_ASSERT (dividend_end_p[0] == divisor_high && dividend_end_p[-1] < divisor_high); result_div = ~((ecma_bigint_digit_t) 0); result_mod = dividend_end_p[-1] + divisor_high; if (result_mod < divisor_high) { break; } } ecma_bigint_two_digits_t low_digits = ((ecma_bigint_two_digits_t) result_div) * divisor_low; while (low_digits > (ECMA_BIGINT_HIGH_DIGIT (result_mod) | divisor_low)) { result_div--; result_mod += divisor_high; /* If result_mod becomes a two digit long number, the condition of the loop must be true, * so the loop can be aborted. This loop stops after maximum of two iterations, since * the highest bit of divisor_high is set. */ if (result_mod < divisor_high) { break; } /* Subtraction is faster than recomputing result_div * divisor_low. */ low_digits -= divisor_low; } } while (false); /* D4. [Multiply and subtract] */ ecma_bigint_digit_t *destination_p = dividend_p; ecma_bigint_digit_t *source_p = divisor_p; ecma_bigint_digit_t carry = 0; do { ecma_bigint_two_digits_t multiply_result = ((ecma_bigint_two_digits_t) (*source_p++)) * result_div; ecma_bigint_digit_t multiply_result_low, new_carry; ecma_bigint_digit_t value = *destination_p; /* The new carry never overflows. See the comment in ecma_big_uint_mul. */ new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t))); multiply_result_low = (ecma_bigint_digit_t) multiply_result; if (value < multiply_result_low) { new_carry++; } value -= multiply_result_low; if (value < carry) { new_carry++; } *destination_p++ = value - carry; carry = new_carry; } while (source_p < divisor_end_p); bool negative_result = *destination_p < carry; *destination_p -= carry; if (negative_result) { /* D6. [Add back] */ result_div--; destination_p = dividend_p; source_p = divisor_p; carry = 0; do { ecma_bigint_digit_t left = *destination_p; if (carry == 0 || left != ~(ecma_bigint_digit_t) 0) { left += carry; carry = 0; } else { left = 0; carry = 1; } ecma_bigint_digit_t right = *source_p++; left += right; if (left < right) { JERRY_ASSERT (carry == 0); carry = 1; } *destination_p++ = left; } while (source_p < divisor_end_p); } *dividend_end_p = result_div; dividend_p--; dividend_end_p--; } while (dividend_p >= buffer_p); ecma_bigint_digit_t *source_p; ecma_bigint_digit_t *source_end_p; if (is_mod) { source_p = buffer_p; source_end_p = dividend_end_p; while (source_end_p > source_p && *source_end_p == 0) { source_end_p--; } if ((*source_end_p >> shift_left) != 0) { source_end_p++; /* This is required to reset carry below. */ *source_end_p = 0; } } else { source_p = dividend_end_p + 1; source_end_p = (ecma_bigint_digit_t *) (((uint8_t *) buffer_p) + dividend_size); if (*source_end_p != 0) { source_end_p++; } } result_p = ECMA_BIGINT_POINTER_TO_ZERO; if (source_p < source_end_p) { result_p = ecma_bigint_create ((uint32_t) ((uint8_t *) source_end_p - (uint8_t *) source_p)); if (result_p != NULL) { ecma_bigint_digit_t *destination_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); if (is_mod && shift_left > 0) { ecma_bigint_digit_t shift_right = shift_left; shift_left = (ecma_bigint_digit_t) (8 * (sizeof (ecma_bigint_digit_t)) - shift_left); destination_p += source_end_p - source_p; ecma_bigint_digit_t carry = *source_end_p << shift_left; do { ecma_bigint_digit_t value = *(--source_end_p); *(--destination_p) = (value >> shift_right) | carry; carry = value << shift_left; } while (source_end_p > source_p); } else { memcpy (destination_p, source_p, (size_t) ((uint8_t *) source_end_p - (uint8_t *) source_p)); } } } error: jmem_heap_free_block (buffer_p, dividend_size + sizeof (ecma_bigint_digit_t)); if (shift_left > 0 && divisor_p != NULL) { jmem_heap_free_block (divisor_p, divisor_size); } return result_p; } /* ecma_big_uint_div_mod */ /** * Shift left BigUInt values by an uint32 value * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_shift_left (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ uint32_t right_value) /**< shift value */ { JERRY_ASSERT (right_value > 0); uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); uint32_t zero_size = (right_value >> ECMA_BIGINT_DIGIT_SHIFT) * (uint32_t) sizeof (ecma_bigint_digit_t); uint32_t result_size = left_size + zero_size; uint32_t shift_left = right_value & ((1 << ECMA_BIGINT_DIGIT_SHIFT) - 1); uint32_t shift_right = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_left; if (shift_left > 0 && (ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) >> shift_right) != 0) { result_size += (uint32_t) sizeof (ecma_bigint_digit_t); } if (result_size > ECMA_BIGINT_MAX_SIZE) { return NULL; } ecma_extended_primitive_t *result_value_p = ecma_bigint_create (result_size); if (JERRY_UNLIKELY (result_value_p == NULL)) { return NULL; } ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); if (zero_size > 0) { memset (result_p, 0, zero_size); result_p = (ecma_bigint_digit_t *) (((uint8_t *) result_p) + zero_size); } if (shift_left == 0) { /* Shift by full digits. */ memcpy (result_p, left_p, left_size); return result_value_p; } ecma_bigint_digit_t *left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size); ecma_bigint_digit_t carry = 0; do { ecma_bigint_digit_t value = *left_p++; *result_p++ = (value << shift_left) | carry; carry = value >> shift_right; } while (left_p < left_end_p); if (carry > 0) { *result_p = carry; } return result_value_p; } /* ecma_big_uint_shift_left */ /** * Shift right BigUInt values by an uint32 value * * @return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_shift_right (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ uint32_t right_value, /**< shift value */ bool increase_result) /**< increase result */ { JERRY_ASSERT (right_value > 0); uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); uint32_t crop_size = (right_value >> ECMA_BIGINT_DIGIT_SHIFT) * (uint32_t) sizeof (ecma_bigint_digit_t); uint32_t shift_right = right_value & ((1 << ECMA_BIGINT_DIGIT_SHIFT) - 1); uint32_t shift_left = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_right; ecma_bigint_digit_t carry = 0; if (shift_right > 0 && (ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) >> shift_right) == 0) { carry = ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) << shift_left; left_size -= (uint32_t) sizeof (ecma_bigint_digit_t); } if (left_size <= crop_size) { if (JERRY_LIKELY (!increase_result)) { return ECMA_BIGINT_POINTER_TO_ZERO; } ecma_extended_primitive_t *result_value_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); if (result_value_p != NULL) { *ECMA_BIGINT_GET_DIGITS (result_value_p, 0) = 1; } return result_value_p; } if (JERRY_UNLIKELY (increase_result) && (shift_right == 0 || (*ECMA_BIGINT_GET_DIGITS (left_value_p, crop_size) << shift_left) == 0)) { ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t *left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, crop_size); while (left_p < left_end_p) { if (*left_p != 0) { break; } left_p++; } if (left_p == left_end_p) { increase_result = false; } } uint32_t size = left_size - crop_size; ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size); if (JERRY_UNLIKELY (result_value_p == NULL)) { return NULL; } if (shift_right == 0) { memcpy (ECMA_BIGINT_GET_DIGITS (result_value_p, 0), ECMA_BIGINT_GET_DIGITS (left_value_p, crop_size), size); if (JERRY_LIKELY (!increase_result)) { return result_value_p; } return ecma_big_uint_increase_result (result_value_p); } ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size); ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, size); ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); do { ecma_bigint_digit_t value = *(--left_p); *(--result_p) = (value >> shift_right) | carry; carry = value << shift_left; } while (result_p > end_p); if (JERRY_LIKELY (!increase_result)) { return result_value_p; } return ecma_big_uint_increase_result (result_value_p); } /* ecma_big_uint_shift_right */ /** * Compute the left value raised to the power of right value * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_pow (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ uint32_t right_value) /**< power value */ { ecma_extended_primitive_t *result_p = ECMA_BIGINT_NUMBER_IS_ODD (right_value) ? left_value_p : NULL; ecma_extended_primitive_t *square_p = left_value_p; JERRY_ASSERT (right_value >= 2); while (true) { ecma_extended_primitive_t *new_square_p = ecma_big_uint_mul (square_p, square_p); if (JERRY_UNLIKELY (new_square_p == NULL)) { if (result_p != NULL && result_p != left_value_p) { ecma_deref_bigint (result_p); } result_p = NULL; break; } if (square_p != left_value_p) { ecma_deref_bigint (square_p); } square_p = new_square_p; right_value >>= 1; if (ECMA_BIGINT_NUMBER_IS_ODD (right_value)) { if (result_p != NULL) { ecma_extended_primitive_t *new_result_p = ecma_big_uint_mul (square_p, result_p); if (result_p != left_value_p) { ecma_deref_bigint (result_p); } result_p = new_result_p; } else { ecma_ref_extended_primitive (square_p); result_p = square_p; } if (JERRY_UNLIKELY (result_p == NULL) || right_value == 1) { break; } } } if (square_p != left_value_p) { ecma_deref_bigint (square_p); } return result_p; } /* ecma_big_uint_pow */ /** * Perform bitwise operations on two BigUInt numbers * * return new BigUInt value, NULL on error */ ecma_extended_primitive_t * ecma_big_uint_bitwise_op (uint32_t operation_and_options, /**< bitwise operation type and options */ ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ { uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); uint32_t operation_type = ECMA_BIGINT_BITWISE_GET_OPERATION_TYPE (operation_and_options); switch (operation_type) { case ECMA_BIG_UINT_BITWISE_AND: { if (left_size > right_size) { left_size = right_size; break; } /* FALLTHRU */ } case ECMA_BIG_UINT_BITWISE_AND_NOT: { if (right_size > left_size) { right_size = left_size; } break; } default: { JERRY_ASSERT (operation_type == ECMA_BIG_UINT_BITWISE_OR || operation_type == ECMA_BIG_UINT_BITWISE_XOR); if (right_size <= left_size) { break; } /* Swap values. */ ecma_extended_primitive_t *tmp_value_p = left_value_p; left_value_p = right_value_p; right_value_p = tmp_value_p; uint32_t tmp_size = left_size; left_size = right_size; right_size = tmp_size; uint32_t decrease_opts = (operation_and_options & ECMA_BIG_UINT_BITWISE_DECREASE_BOTH); /* When exactly one bit is set, invert both bits. */ if (decrease_opts >= ECMA_BIG_UINT_BITWISE_DECREASE_LEFT && decrease_opts <= ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT) { operation_and_options ^= ECMA_BIG_UINT_BITWISE_DECREASE_BOTH; } break; } } ecma_extended_primitive_t *result_value_p = ecma_bigint_create (left_size); if (JERRY_UNLIKELY (result_value_p == NULL)) { return NULL; } ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0); ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); ecma_bigint_digit_t *result_end_p = ECMA_BIGINT_GET_DIGITS (result_value_p, right_size); if (!(operation_and_options & ECMA_BIG_UINT_BITWISE_DECREASE_BOTH)) { JERRY_ASSERT (!(operation_and_options & ECMA_BIG_UINT_BITWISE_INCREASE_RESULT)); if (operation_type == ECMA_BIG_UINT_BITWISE_AND) { do { *result_p++ = *left_p++ & *right_p++; } while (result_p < result_end_p); if (result_p[-1] == 0) { return ecma_big_uint_normalize_result (result_value_p, result_p); } return result_value_p; } if (operation_type == ECMA_BIG_UINT_BITWISE_OR) { do { *result_p++ = *left_p++ | *right_p++; } while (result_p < result_end_p); if (left_size > right_size) { memcpy (result_p, left_p, left_size - right_size); } return result_value_p; } JERRY_ASSERT (operation_type == ECMA_BIG_UINT_BITWISE_XOR); do { *result_p++ = *left_p++ ^ *right_p++; } while (result_p < result_end_p); if (left_size > right_size) { memcpy (result_p, left_p, left_size - right_size); return result_value_p; } if (result_p[-1] == 0) { return ecma_big_uint_normalize_result (result_value_p, result_p); } return result_value_p; } uint32_t left_carry = 0, right_carry = 0; if (operation_and_options & ECMA_BIG_UINT_BITWISE_DECREASE_LEFT) { left_carry = 1; } if (operation_and_options & ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT) { right_carry = 1; } do { ecma_bigint_digit_t left = (*left_p++) - left_carry; if (left != ~((ecma_bigint_digit_t) 0)) { left_carry = 0; } ecma_bigint_digit_t right = (*right_p++) - right_carry; if (right != ~((ecma_bigint_digit_t) 0)) { right_carry = 0; } switch (operation_type) { case ECMA_BIG_UINT_BITWISE_AND: { *result_p++ = left & right; break; } case ECMA_BIG_UINT_BITWISE_OR: { *result_p++ = left | right; break; } case ECMA_BIG_UINT_BITWISE_XOR: { *result_p++ = left ^ right; break; } default: { JERRY_ASSERT (operation_type == ECMA_BIG_UINT_BITWISE_AND_NOT); *result_p++ = left & ~right; break; } } } while (result_p < result_end_p); if (operation_type != ECMA_BIG_UINT_BITWISE_AND) { result_end_p = ECMA_BIGINT_GET_DIGITS (result_value_p, left_size); if (left_carry > 0) { while (*left_p == 0) { *result_p++ = ~((ecma_bigint_digit_t) 0); left_p++; JERRY_ASSERT (result_p < result_end_p); } *result_p++ = *left_p++ - 1; } if (result_p < result_end_p) { memcpy (result_p, left_p, (size_t) ((uint8_t *) result_end_p - (uint8_t *) result_p)); if (operation_and_options & ECMA_BIG_UINT_BITWISE_INCREASE_RESULT) { return ecma_big_uint_increase_result (result_value_p); } return result_value_p; } } if (operation_and_options & ECMA_BIG_UINT_BITWISE_INCREASE_RESULT) { return ecma_big_uint_increase_result (result_value_p); } if (result_p[-1] == 0) { return ecma_big_uint_normalize_result (result_value_p, result_p); } return result_value_p; } /* ecma_big_uint_bitwise_op */ #endif /* JERRY_BUILTIN_BIGINT */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-scanner-util.cpp000664 001750 001750 00000270662 15164251010 044766 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-helpers.h" #include "ecma-lex-env.h" #include "jcontext.h" #include "js-parser-internal.h" #include "js-scanner-internal.h" #include "lit-char-helpers.h" #if JERRY_PARSER /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_scanner Scanner * @{ */ JERRY_STATIC_ASSERT (PARSER_MAXIMUM_NUMBER_OF_LITERALS + PARSER_MAXIMUM_NUMBER_OF_REGISTERS < PARSER_REGISTER_START, maximum_number_of_literals_plus_registers_must_be_less_than_register_start); JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG & SCANNER_LITERAL_IS_LOCAL) == 0, is_arrow_arg_binding_flag_must_not_use_local_flags); JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_LET & SCANNER_LITERAL_IS_LOCAL) != 0, is_let_flag_must_use_local_flags); JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_CONST & SCANNER_LITERAL_IS_LOCAL) != 0, is_const_flag_must_use_local_flags); JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_FUNC_DECLARATION & SCANNER_LITERAL_IS_LOCAL) != 0, is_func_declaration_flag_must_use_local_flags); JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_DESTRUCTURED_ARG & SCANNER_LITERAL_IS_LOCAL) != 0, is_arg_binding_flag_must_use_local_flags); JERRY_STATIC_ASSERT (((int)SCANNER_LITERAL_IS_FUNC_DECLARATION != (int)SCANNER_LITERAL_IS_DESTRUCTURED_ARG), is_func_declaration_must_be_different_from_is_arg_binding); JERRY_STATIC_ASSERT (((int)PARSER_SCOPE_STACK_IS_CONST_REG == (int)PARSER_SCOPE_STACK_IS_LOCAL_CREATED), scope_stack_is_const_reg_and_scope_stack_is_local_created_must_be_the_same); /** * Raise a scanner error. */ void scanner_raise_error (parser_context_t *context_p /**< context */) { PARSER_THROW (context_p->try_buffer); /* Should never been reached. */ JERRY_ASSERT (0); } /* scanner_raise_error */ /** * Raise a variable redeclaration error. */ void scanner_raise_redeclaration_error (parser_context_t *context_p) /**< context */ { scanner_info_t *info_p = scanner_insert_info (context_p, context_p->source_p, sizeof (scanner_info_t)); info_p->type = SCANNER_TYPE_ERR_REDECLARED; scanner_raise_error (context_p); } /* scanner_raise_redeclaration_error */ /** * Allocate memory for scanner. * * @return allocated memory */ void * scanner_malloc (parser_context_t *context_p, /**< context */ size_t size) /**< size of the memory block */ { void *result; JERRY_ASSERT (size > 0); result = jmem_heap_alloc_block_null_on_error (size); if (result == NULL) { scanner_cleanup (context_p); /* This is the only error which specify its reason. */ context_p->error = PARSER_ERR_OUT_OF_MEMORY; PARSER_THROW (context_p->try_buffer); } return result; } /* scanner_malloc */ /** * Free memory allocated by scanner_malloc. */ void scanner_free (void *ptr, /**< pointer to free */ size_t size) /**< size of the memory block */ { jmem_heap_free_block (ptr, size); } /* scanner_free */ /** * Count the size of a stream after an info block. * * @return the size in bytes */ size_t scanner_get_stream_size (scanner_info_t *info_p, /**< scanner info block */ size_t size) /**< size excluding the stream */ { const uint8_t *data_p = ((const uint8_t *) info_p) + size; const uint8_t *data_p_start = data_p; while (data_p[0] != SCANNER_STREAM_TYPE_END) { switch (data_p[0] & SCANNER_STREAM_TYPE_MASK) { case SCANNER_STREAM_TYPE_VAR: case SCANNER_STREAM_TYPE_LET: case SCANNER_STREAM_TYPE_CONST: case SCANNER_STREAM_TYPE_LOCAL: #if JERRY_MODULE_SYSTEM case SCANNER_STREAM_TYPE_IMPORT: #endif /* JERRY_MODULE_SYSTEM */ case SCANNER_STREAM_TYPE_ARG: case SCANNER_STREAM_TYPE_ARG_VAR: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR: case SCANNER_STREAM_TYPE_ARG_FUNC: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: case SCANNER_STREAM_TYPE_FUNC: { break; } default: { JERRY_ASSERT ((data_p[0] & SCANNER_STREAM_TYPE_MASK) == SCANNER_STREAM_TYPE_HOLE || SCANNER_STREAM_TYPE_IS_ARGUMENTS (data_p[0] & SCANNER_STREAM_TYPE_MASK)); data_p++; continue; } } data_p += 3; if (data_p[-3] & SCANNER_STREAM_UINT16_DIFF) { data_p++; } else if (data_p[-1] == 0) { data_p += sizeof (const uint8_t *); } } return size + 1 + (size_t) (data_p - data_p_start); } /* scanner_get_stream_size */ /** * Insert a scanner info block into the scanner info chain. * * @return newly allocated scanner info */ scanner_info_t * scanner_insert_info (parser_context_t *context_p, /**< context */ const uint8_t *source_p, /**< triggering position */ size_t size) /**< size of the memory block */ { scanner_info_t *new_scanner_info_p = (scanner_info_t *) scanner_malloc (context_p, size); scanner_info_t *scanner_info_p = context_p->next_scanner_info_p; scanner_info_t *prev_scanner_info_p = NULL; JERRY_ASSERT (scanner_info_p != NULL); JERRY_ASSERT (source_p != NULL); new_scanner_info_p->source_p = source_p; while (source_p < scanner_info_p->source_p) { prev_scanner_info_p = scanner_info_p; scanner_info_p = scanner_info_p->next_p; JERRY_ASSERT (scanner_info_p != NULL); } /* Multiple scanner info blocks cannot be assigned to the same position. */ JERRY_ASSERT (source_p != scanner_info_p->source_p); new_scanner_info_p->next_p = scanner_info_p; if (JERRY_LIKELY (prev_scanner_info_p == NULL)) { context_p->next_scanner_info_p = new_scanner_info_p; } else { prev_scanner_info_p->next_p = new_scanner_info_p; } return new_scanner_info_p; } /* scanner_insert_info */ /** * Insert a scanner info block into the scanner info chain before a given info block. * * @return newly allocated scanner info */ scanner_info_t * scanner_insert_info_before (parser_context_t *context_p, /**< context */ const uint8_t *source_p, /**< triggering position */ scanner_info_t *start_info_p, /**< first info position */ size_t size) /**< size of the memory block */ { JERRY_ASSERT (start_info_p != NULL); scanner_info_t *new_scanner_info_p = (scanner_info_t *) scanner_malloc (context_p, size); scanner_info_t *scanner_info_p = start_info_p->next_p; scanner_info_t *prev_scanner_info_p = start_info_p; new_scanner_info_p->source_p = source_p; while (source_p < scanner_info_p->source_p) { prev_scanner_info_p = scanner_info_p; scanner_info_p = scanner_info_p->next_p; JERRY_ASSERT (scanner_info_p != NULL); } /* Multiple scanner info blocks cannot be assigned to the same position. */ JERRY_ASSERT (source_p != scanner_info_p->source_p); new_scanner_info_p->next_p = scanner_info_p; prev_scanner_info_p->next_p = new_scanner_info_p; return new_scanner_info_p; } /* scanner_insert_info_before */ /** * Release the next scanner info. */ void scanner_release_next (parser_context_t *context_p, /**< context */ size_t size) /**< size of the memory block */ { scanner_info_t *next_p = context_p->next_scanner_info_p->next_p; jmem_heap_free_block (context_p->next_scanner_info_p, size); context_p->next_scanner_info_p = next_p; } /* scanner_release_next */ /** * Set the active scanner info to the next scanner info. */ void scanner_set_active (parser_context_t *context_p) /**< context */ { scanner_info_t *scanner_info_p = context_p->next_scanner_info_p; context_p->next_scanner_info_p = scanner_info_p->next_p; scanner_info_p->next_p = context_p->active_scanner_info_p; context_p->active_scanner_info_p = scanner_info_p; } /* scanner_set_active */ /** * Set the next scanner info to the active scanner info. */ void scanner_revert_active (parser_context_t *context_p) /**< context */ { scanner_info_t *scanner_info_p = context_p->active_scanner_info_p; context_p->active_scanner_info_p = scanner_info_p->next_p; scanner_info_p->next_p = context_p->next_scanner_info_p; context_p->next_scanner_info_p = scanner_info_p; } /* scanner_revert_active */ /** * Release the active scanner info. */ void scanner_release_active (parser_context_t *context_p, /**< context */ size_t size) /**< size of the memory block */ { scanner_info_t *next_p = context_p->active_scanner_info_p->next_p; jmem_heap_free_block (context_p->active_scanner_info_p, size); context_p->active_scanner_info_p = next_p; } /* scanner_release_active */ /** * Release switch cases. */ void scanner_release_switch_cases (scanner_case_info_t *case_p) /**< case list */ { while (case_p != NULL) { scanner_case_info_t *next_p = case_p->next_p; jmem_heap_free_block (case_p, sizeof (scanner_case_info_t)); case_p = next_p; } } /* scanner_release_switch_cases */ /** * Release private fields. */ void scanner_release_private_fields (scanner_class_private_member_t *member_p) /**< private member list */ { while (member_p != NULL) { scanner_class_private_member_t *prev_p = member_p->prev_p; jmem_heap_free_block (member_p, sizeof (scanner_class_private_member_t)); member_p = prev_p; } } /* scanner_release_private_fields */ /** * Seek to correct position in the scanner info list. */ void scanner_seek (parser_context_t *context_p) /**< context */ { const uint8_t *source_p = context_p->source_p; scanner_info_t *prev_p; if (context_p->skipped_scanner_info_p != NULL) { JERRY_ASSERT (context_p->skipped_scanner_info_p->source_p != NULL); context_p->skipped_scanner_info_end_p->next_p = context_p->next_scanner_info_p; if (context_p->skipped_scanner_info_end_p->source_p <= source_p) { prev_p = context_p->skipped_scanner_info_end_p; } else { prev_p = context_p->skipped_scanner_info_p; if (prev_p->source_p > source_p) { context_p->next_scanner_info_p = prev_p; context_p->skipped_scanner_info_p = NULL; return; } context_p->skipped_scanner_info_p = prev_p; } } else { prev_p = context_p->next_scanner_info_p; if (prev_p->source_p == NULL || prev_p->source_p > source_p) { return; } context_p->skipped_scanner_info_p = prev_p; } while (prev_p->next_p->source_p != NULL && prev_p->next_p->source_p <= source_p) { prev_p = prev_p->next_p; } context_p->skipped_scanner_info_end_p = prev_p; context_p->next_scanner_info_p = prev_p->next_p; } /* scanner_seek */ /** * Checks whether a literal is equal to "arguments". */ static inline bool scanner_literal_is_arguments (lexer_lit_location_t *literal_p) /**< literal */ { return lexer_compare_identifier_to_string (literal_p, (const uint8_t *) "arguments", 9); } /* scanner_literal_is_arguments */ /** * Find if there is a duplicated argument in the given context * * @return true - if there are duplicates, false - otherwise */ static bool scanner_find_duplicated_arg (parser_context_t *context_p, lexer_lit_location_t *lit_loc_p) { if (!(context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS)) { return false; } if (scanner_literal_is_arguments (lit_loc_p)) { return true; } uint16_t register_end, encoding_limit, encoding_delta; ecma_value_t *literal_p; ecma_value_t *literal_start_p; const ecma_compiled_code_t *bytecode_header_p = JERRY_CONTEXT (vm_top_context_p)->shared_p->bytecode_header_p; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; register_end = args_p->register_end; literal_p = (ecma_value_t *) (args_p + 1); literal_p -= register_end; literal_start_p = literal_p; literal_p += args_p->literal_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; register_end = args_p->register_end; literal_p = (ecma_value_t *) (args_p + 1); literal_p -= register_end; literal_start_p = literal_p; literal_p += args_p->literal_end; } if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING)) { encoding_limit = CBC_SMALL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_SMALL_LITERAL_ENCODING_DELTA; } else { encoding_limit = CBC_FULL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_FULL_LITERAL_ENCODING_DELTA; } uint8_t *byte_code_p = (uint8_t *) literal_p; bool found_duplicate = false; while (*byte_code_p == CBC_CREATE_LOCAL) { byte_code_p++; uint16_t literal_index = *byte_code_p++; if (literal_index >= encoding_limit) { literal_index = (uint16_t) (((literal_index << 8) | *byte_code_p++) - encoding_delta); } ecma_string_t *arg_string = ecma_get_string_from_value (literal_start_p[literal_index]); uint8_t *destination_p = (uint8_t *) parser_malloc (context_p, lit_loc_p->length); lexer_convert_ident_to_cesu8 (destination_p, lit_loc_p->char_p, lit_loc_p->length); ecma_string_t *search_key_p = ecma_new_ecma_string_from_utf8 (destination_p, lit_loc_p->length); scanner_free (destination_p, lit_loc_p->length); found_duplicate = ecma_compare_ecma_strings (arg_string, search_key_p); ecma_deref_ecma_string (search_key_p); if (found_duplicate) { break; } } return found_duplicate; } /* scanner_find_duplicated_arg */ /** * Find any let/const declaration of a given literal. * * @return true - if the literal is found, false - otherwise */ static bool scanner_scope_find_lexical_declaration (parser_context_t *context_p, /**< context */ lexer_lit_location_t *literal_p) /**< literal */ { ecma_string_t *name_p; uint32_t flags = context_p->global_status_flags; if (!(flags & ECMA_PARSE_EVAL) || (!(flags & ECMA_PARSE_DIRECT_EVAL) && (context_p->status_flags & PARSER_IS_STRICT))) { return false; } if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))) { name_p = ecma_new_ecma_string_from_utf8 (literal_p->char_p, literal_p->length); } else { uint8_t *destination_p = (uint8_t *) scanner_malloc (context_p, literal_p->length); lexer_convert_ident_to_cesu8 (destination_p, literal_p->char_p, literal_p->length); name_p = ecma_new_ecma_string_from_utf8 (destination_p, literal_p->length); scanner_free (destination_p, literal_p->length); } ecma_object_t *lex_env_p; if (flags & ECMA_PARSE_DIRECT_EVAL) { lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p; while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) { if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); if (property_p != NULL && ecma_is_property_enumerable (*property_p)) { ecma_deref_ecma_string (name_p); return true; } } JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } } else { lex_env_p = ecma_get_global_scope (ecma_builtin_get_global ()); } if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); if (property_p != NULL && (ecma_is_property_enumerable (*property_p) || scanner_find_duplicated_arg (context_p, literal_p))) { ecma_deref_ecma_string (name_p); return true; } } ecma_deref_ecma_string (name_p); return false; } /* scanner_scope_find_lexical_declaration */ /** * Push a new literal pool. * * @return the newly created literal pool */ scanner_literal_pool_t * scanner_push_literal_pool (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ uint16_t status_flags) /**< combination of scanner_literal_pool_flags_t flags */ { scanner_literal_pool_t *prev_literal_pool_p = scanner_context_p->active_literal_pool_p; scanner_literal_pool_t *literal_pool_p; literal_pool_p = (scanner_literal_pool_t *) scanner_malloc (context_p, sizeof (scanner_literal_pool_t)); if (!(status_flags & SCANNER_LITERAL_POOL_FUNCTION)) { JERRY_ASSERT (prev_literal_pool_p != NULL); status_flags |= SCANNER_LITERAL_POOL_NO_ARGUMENTS; const uint16_t copied_flags = (SCANNER_LITERAL_POOL_IN_WITH | SCANNER_LITERAL_POOL_GENERATOR | SCANNER_LITERAL_POOL_ASYNC); status_flags |= (uint16_t) (prev_literal_pool_p->status_flags & copied_flags); } else { context_p->status_flags &= (uint32_t) ~(PARSER_IS_GENERATOR_FUNCTION | PARSER_IS_ASYNC_FUNCTION); if (status_flags & SCANNER_LITERAL_POOL_GENERATOR) { context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION; } if (status_flags & SCANNER_LITERAL_POOL_ASYNC) { context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION; } } if (prev_literal_pool_p != NULL) { const uint16_t copied_flags = SCANNER_LITERAL_POOL_IS_STRICT; status_flags |= (uint16_t) (prev_literal_pool_p->status_flags & copied_flags); /* The logical value of these flags must be the same. */ JERRY_ASSERT (!(status_flags & SCANNER_LITERAL_POOL_IS_STRICT) == !(context_p->status_flags & PARSER_IS_STRICT)); } parser_list_init (&literal_pool_p->literal_pool, sizeof (lexer_lit_location_t), (uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_lit_location_t))); literal_pool_p->source_p = NULL; literal_pool_p->status_flags = status_flags; literal_pool_p->no_declarations = 0; literal_pool_p->prev_p = prev_literal_pool_p; scanner_context_p->active_literal_pool_p = literal_pool_p; return literal_pool_p; } /* scanner_push_literal_pool */ JERRY_STATIC_ASSERT (((uint8_t)PARSER_MAXIMUM_IDENT_LENGTH <= UINT8_MAX), maximum_ident_length_must_fit_in_a_byte); /** * Current status of arguments. */ typedef enum { SCANNER_ARGUMENTS_NOT_PRESENT, /**< arguments object must not be created */ SCANNER_ARGUMENTS_MAY_PRESENT, /**< arguments object can be created */ SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL, /**< arguments object must be present unless otherwise declared */ SCANNER_ARGUMENTS_PRESENT, /**< arguments object must be created */ SCANNER_ARGUMENTS_PRESENT_NO_REG, /**< arguments object must be created and cannot be stored in registers */ } scanner_arguments_type_t; /** * Pop the last literal pool from the end. */ void scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; scanner_literal_pool_t *prev_literal_pool_p = literal_pool_p->prev_p; const uint32_t arrow_super_flags = (SCANNER_LITERAL_POOL_ARROW | SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE); if ((literal_pool_p->status_flags & arrow_super_flags) == arrow_super_flags) { prev_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE; } if (JERRY_UNLIKELY (literal_pool_p->source_p == NULL)) { JERRY_ASSERT (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION); JERRY_ASSERT (literal_pool_p->literal_pool.data.first_p == NULL && literal_pool_p->literal_pool.data.last_p == NULL); scanner_context_p->active_literal_pool_p = literal_pool_p->prev_p; scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t)); return; } uint16_t status_flags = literal_pool_p->status_flags; scanner_arguments_type_t arguments_type = SCANNER_ARGUMENTS_MAY_PRESENT; if (status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) { arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT; } else if (status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) { arguments_type = SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL; } if (status_flags & SCANNER_LITERAL_POOL_ARGUMENTS_IN_ARGS) { arguments_type = SCANNER_ARGUMENTS_PRESENT; if (status_flags & (SCANNER_LITERAL_POOL_NO_ARGUMENTS | SCANNER_LITERAL_POOL_CAN_EVAL)) { arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG; status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_NO_ARGUMENTS; } } uint8_t can_eval_types = 0; if (prev_literal_pool_p == NULL && !(context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL)) { can_eval_types |= SCANNER_LITERAL_IS_FUNC; } if ((status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) && prev_literal_pool_p != NULL) { prev_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_CAN_EVAL; } parser_list_iterator_t literal_iterator; lexer_lit_location_t *literal_p; int32_t no_declarations = literal_pool_p->no_declarations; parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); uint8_t arguments_stream_type = SCANNER_STREAM_TYPE_ARGUMENTS; const uint8_t *prev_source_p = literal_pool_p->source_p - 1; lexer_lit_location_t *last_argument_p = NULL; size_t compressed_size = 1; while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { uint8_t type = literal_p->type; if (JERRY_UNLIKELY (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK)) { continue; } if (!(status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) && scanner_literal_is_arguments (literal_p)) { JERRY_ASSERT (arguments_type != SCANNER_ARGUMENTS_NOT_PRESENT); status_flags |= SCANNER_LITERAL_POOL_NO_ARGUMENTS; if (type & SCANNER_LITERAL_IS_ARG) { JERRY_ASSERT (arguments_type != SCANNER_ARGUMENTS_PRESENT && arguments_type != SCANNER_ARGUMENTS_PRESENT_NO_REG); arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT; last_argument_p = literal_p; } else if (type & SCANNER_LITERAL_IS_LOCAL) { if (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT || arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL) { arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT; } else { if (arguments_type == SCANNER_ARGUMENTS_PRESENT_NO_REG) { type |= SCANNER_LITERAL_NO_REG; } else if (type & (SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_EARLY_CREATE)) { arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG; } if ((type & SCANNER_LITERAL_IS_LOCAL_FUNC) == SCANNER_LITERAL_IS_LOCAL_FUNC) { type |= SCANNER_LITERAL_IS_ARG; literal_p->type = type; no_declarations--; arguments_stream_type = SCANNER_STREAM_TYPE_ARGUMENTS_FUNC; } else { arguments_stream_type |= SCANNER_STREAM_LOCAL_ARGUMENTS; } } } else { if ((type & SCANNER_LITERAL_IS_VAR) && (arguments_type == SCANNER_ARGUMENTS_PRESENT || arguments_type == SCANNER_ARGUMENTS_PRESENT_NO_REG)) { if (arguments_type == SCANNER_ARGUMENTS_PRESENT_NO_REG) { type |= SCANNER_LITERAL_NO_REG; } else if (type & (SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_EARLY_CREATE)) { arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG; } type |= SCANNER_LITERAL_IS_ARG; literal_p->type = type; no_declarations--; } if ((type & SCANNER_LITERAL_NO_REG) || arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL) { arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG; } else if (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT) { arguments_type = SCANNER_ARGUMENTS_PRESENT; } /* The SCANNER_LITERAL_IS_ARG may be set above. */ if (!(type & SCANNER_LITERAL_IS_ARG)) { literal_p->type = 0; continue; } } } else if (type & SCANNER_LITERAL_IS_ARG) { last_argument_p = literal_p; } if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION) && (type & SCANNER_LITERAL_IS_LOCAL_FUNC) == SCANNER_LITERAL_IS_FUNC) { if (prev_literal_pool_p == NULL && scanner_scope_find_lexical_declaration (context_p, literal_p)) { literal_p->type = 0; continue; } if (!(type & SCANNER_LITERAL_IS_ARG)) { type |= SCANNER_LITERAL_IS_VAR; } type &= (uint8_t) ~SCANNER_LITERAL_IS_FUNC; literal_p->type = type; } if ((type & SCANNER_LITERAL_IS_LOCAL) || ((type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG)) && (status_flags & SCANNER_LITERAL_POOL_FUNCTION))) { JERRY_ASSERT ((status_flags & SCANNER_LITERAL_POOL_FUNCTION) || !(literal_p->type & SCANNER_LITERAL_IS_ARG)); if (literal_p->length == 0) { compressed_size += 1; continue; } no_declarations++; if ((status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) || (type & can_eval_types)) { type |= SCANNER_LITERAL_NO_REG; literal_p->type = type; } if (type & SCANNER_LITERAL_IS_FUNC) { no_declarations++; if ((type & (SCANNER_LITERAL_IS_CONST | SCANNER_LITERAL_IS_ARG)) == SCANNER_LITERAL_IS_CONST) { JERRY_ASSERT (type & SCANNER_LITERAL_IS_LET); /* Catch parameters cannot be functions. */ literal_p->type = (uint8_t) (type & ~SCANNER_LITERAL_IS_FUNC); no_declarations--; } } intptr_t diff = (intptr_t) (literal_p->char_p - prev_source_p); if (diff >= 1 && diff <= (intptr_t) UINT8_MAX) { compressed_size += 2 + 1; } else if (diff >= -(intptr_t) UINT8_MAX && diff <= (intptr_t) UINT16_MAX) { compressed_size += 2 + 2; } else { compressed_size += 2 + 1 + sizeof (const uint8_t *); } prev_source_p = literal_p->char_p + literal_p->length; if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION) || ((type & SCANNER_LITERAL_IS_FUNC) && (status_flags & SCANNER_LITERAL_POOL_IS_STRICT)) || !(type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC))) { continue; } } if (prev_literal_pool_p != NULL && literal_p->length > 0) { /* Propagate literal to upper level. */ lexer_lit_location_t *literal_location_p = scanner_add_custom_literal (context_p, prev_literal_pool_p, literal_p); uint8_t extended_type = literal_location_p->type; if ((status_flags & (SCANNER_LITERAL_POOL_FUNCTION | SCANNER_LITERAL_POOL_CLASS_FIELD)) || (type & SCANNER_LITERAL_NO_REG)) { extended_type |= SCANNER_LITERAL_NO_REG; } extended_type |= SCANNER_LITERAL_IS_USED; if (status_flags & SCANNER_LITERAL_POOL_FUNCTION_STATEMENT) { extended_type |= SCANNER_LITERAL_EARLY_CREATE; } const uint8_t mask = (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_LOCAL); if ((type & SCANNER_LITERAL_IS_ARG) || (literal_location_p->type & mask) == SCANNER_LITERAL_IS_LET || (literal_location_p->type & mask) == SCANNER_LITERAL_IS_CONST) { /* Clears the SCANNER_LITERAL_IS_VAR and SCANNER_LITERAL_IS_FUNC flags * for speculative arrow parameters and local (non-var) functions. */ type = 0; } type = (uint8_t) (type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC)); JERRY_ASSERT (type == 0 || !(status_flags & SCANNER_LITERAL_POOL_FUNCTION)); literal_location_p->type = (uint8_t) (extended_type | type); } } if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION) || (compressed_size > 1)) { if (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT) { arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT; } else if (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL) { arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG; } if (arguments_type != SCANNER_ARGUMENTS_NOT_PRESENT) { compressed_size++; } compressed_size += sizeof (scanner_info_t); scanner_info_t *info_p; if (prev_literal_pool_p != NULL || scanner_context_p->end_arguments_p == NULL) { info_p = scanner_insert_info (context_p, literal_pool_p->source_p, compressed_size); } else { scanner_info_t *start_info_p = scanner_context_p->end_arguments_p; info_p = scanner_insert_info_before (context_p, literal_pool_p->source_p, start_info_p, compressed_size); } if (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) { no_declarations = PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK; } uint8_t *data_p = (uint8_t *) (info_p + 1); bool mapped_arguments = false; if (status_flags & SCANNER_LITERAL_POOL_FUNCTION) { info_p->type = SCANNER_TYPE_FUNCTION; uint8_t u8_arg = 0; if (arguments_type != SCANNER_ARGUMENTS_NOT_PRESENT) { u8_arg |= SCANNER_FUNCTION_ARGUMENTS_NEEDED; if (no_declarations < PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) { no_declarations++; } if (!(status_flags & (SCANNER_LITERAL_POOL_IS_STRICT | SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT))) { mapped_arguments = true; } if (arguments_type == SCANNER_ARGUMENTS_PRESENT_NO_REG) { arguments_stream_type |= SCANNER_STREAM_NO_REG; } if (last_argument_p == NULL) { *data_p++ = arguments_stream_type; } } else { last_argument_p = NULL; } if (status_flags & (SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT | SCANNER_LITERAL_POOL_ARROW)) { u8_arg |= SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT; } if (status_flags & SCANNER_LITERAL_POOL_ASYNC) { u8_arg |= SCANNER_FUNCTION_ASYNC; if (status_flags & SCANNER_LITERAL_POOL_FUNCTION_STATEMENT) { u8_arg |= SCANNER_FUNCTION_STATEMENT; } } if (status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) { u8_arg |= SCANNER_FUNCTION_LEXICAL_ENV_NEEDED; } if (status_flags & SCANNER_LITERAL_POOL_IS_STRICT) { u8_arg |= SCANNER_FUNCTION_IS_STRICT; } info_p->u8_arg = u8_arg; info_p->u16_arg = (uint16_t) no_declarations; } else { info_p->type = SCANNER_TYPE_BLOCK; JERRY_ASSERT (prev_literal_pool_p != NULL); } parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); prev_source_p = literal_pool_p->source_p - 1; no_declarations = literal_pool_p->no_declarations; while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (JERRY_UNLIKELY (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) || (!(literal_p->type & SCANNER_LITERAL_IS_LOCAL) && (!(literal_p->type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG)) || !(status_flags & SCANNER_LITERAL_POOL_FUNCTION)))) { continue; } if (literal_p->length == 0) { *data_p++ = SCANNER_STREAM_TYPE_HOLE; if (literal_p == last_argument_p) { *data_p++ = arguments_stream_type; } continue; } no_declarations++; uint8_t type = SCANNER_STREAM_TYPE_VAR; if (literal_p->type & SCANNER_LITERAL_IS_FUNC) { no_declarations++; type = SCANNER_STREAM_TYPE_FUNC; if (literal_p->type & SCANNER_LITERAL_IS_ARG) { type = SCANNER_STREAM_TYPE_ARG_FUNC; if (literal_p->type & SCANNER_LITERAL_IS_DESTRUCTURED_ARG) { type = SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC; } } } else if (literal_p->type & SCANNER_LITERAL_IS_ARG) { type = SCANNER_STREAM_TYPE_ARG; if (literal_p->type & SCANNER_LITERAL_IS_DESTRUCTURED_ARG) { type = SCANNER_STREAM_TYPE_DESTRUCTURED_ARG; } if (literal_p->type & SCANNER_LITERAL_IS_VAR) { type = (uint8_t) (type + 1); JERRY_ASSERT (type == SCANNER_STREAM_TYPE_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR); } } else if (literal_p->type & SCANNER_LITERAL_IS_LET) { if (!(literal_p->type & SCANNER_LITERAL_IS_CONST)) { type = SCANNER_STREAM_TYPE_LET; if ((status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) && (literal_p->type & SCANNER_LITERAL_NO_REG)) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; } } #if JERRY_MODULE_SYSTEM else if (prev_literal_pool_p == NULL) { type = SCANNER_STREAM_TYPE_IMPORT; } #endif /* JERRY_MODULE_SYSTEM */ else { type = SCANNER_STREAM_TYPE_LOCAL; } } else if (literal_p->type & SCANNER_LITERAL_IS_CONST) { type = SCANNER_STREAM_TYPE_CONST; if ((status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) && (literal_p->type & SCANNER_LITERAL_NO_REG)) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; } } if (literal_p->type & SCANNER_LITERAL_EARLY_CREATE) { type |= SCANNER_STREAM_NO_REG | SCANNER_STREAM_EARLY_CREATE; } if (literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE) { type |= SCANNER_STREAM_HAS_ESCAPE; } if ((literal_p->type & SCANNER_LITERAL_NO_REG) || (mapped_arguments && (literal_p->type & SCANNER_LITERAL_IS_ARG))) { type |= SCANNER_STREAM_NO_REG; } data_p[0] = type; data_p[1] = (uint8_t) literal_p->length; data_p += 3; intptr_t diff = (intptr_t) (literal_p->char_p - prev_source_p); if (diff >= 1 && diff <= (intptr_t) UINT8_MAX) { data_p[-1] = (uint8_t) diff; } else if (diff >= -(intptr_t) UINT8_MAX && diff <= (intptr_t) UINT16_MAX) { if (diff < 0) { diff = -diff; } data_p[-3] |= SCANNER_STREAM_UINT16_DIFF; data_p[-1] = (uint8_t) diff; data_p[0] = (uint8_t) (diff >> 8); data_p += 1; } else { data_p[-1] = 0; memcpy (data_p, &literal_p->char_p, sizeof (uintptr_t)); data_p += sizeof (uintptr_t); } if (literal_p == last_argument_p) { *data_p++ = arguments_stream_type; } prev_source_p = literal_p->char_p + literal_p->length; } data_p[0] = SCANNER_STREAM_TYPE_END; JERRY_ASSERT (((uint8_t *) info_p) + compressed_size == data_p + 1); } if (!(status_flags & SCANNER_LITERAL_POOL_FUNCTION) && (int32_t) prev_literal_pool_p->no_declarations < no_declarations) { prev_literal_pool_p->no_declarations = (uint16_t) no_declarations; } if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION) && prev_literal_pool_p != NULL) { if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IS_STRICT) { context_p->status_flags |= PARSER_IS_STRICT; } else { context_p->status_flags &= (uint32_t) ~PARSER_IS_STRICT; } if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_GENERATOR) { context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION; } else { context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION; } if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_ASYNC) { context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION; } else { context_p->status_flags &= (uint32_t) ~PARSER_IS_ASYNC_FUNCTION; } } scanner_context_p->active_literal_pool_p = literal_pool_p->prev_p; parser_list_free (&literal_pool_p->literal_pool); scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t)); } /* scanner_pop_literal_pool */ /** * Filter out the arguments from a literal pool. */ void scanner_filter_arguments (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { /* Fast case: check whether all literals are arguments. */ scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; scanner_literal_pool_t *prev_literal_pool_p = literal_pool_p->prev_p; parser_list_iterator_t literal_iterator; lexer_lit_location_t *literal_p; bool can_eval = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) != 0; bool has_arguments = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) == 0; JERRY_ASSERT (SCANNER_LITERAL_POOL_MAY_HAVE_ARGUMENTS (literal_pool_p->status_flags)); if (JERRY_UNLIKELY (can_eval)) { if (prev_literal_pool_p != NULL) { prev_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_CAN_EVAL; } literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_CAN_EVAL; } else { parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); while (true) { literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator); if (literal_p == NULL) { return; } if (can_eval || (literal_p->type & SCANNER_LITERAL_EARLY_CREATE)) { literal_p->type |= SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_EARLY_CREATE; } uint8_t type = literal_p->type; const uint8_t mask = (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_DESTRUCTURED_ARG | SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG); if ((type & mask) != SCANNER_LITERAL_IS_ARG) { break; } } } /* Destructured args are placed after the other arguments because of register assignments. */ bool has_destructured_arg = false; scanner_literal_pool_t *new_literal_pool_p; new_literal_pool_p = (scanner_literal_pool_t *) scanner_malloc (context_p, sizeof (scanner_literal_pool_t)); new_literal_pool_p->prev_p = literal_pool_p; scanner_context_p->active_literal_pool_p = new_literal_pool_p; *new_literal_pool_p = *literal_pool_p; parser_list_init (&new_literal_pool_p->literal_pool, sizeof (lexer_lit_location_t), (uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_lit_location_t))); parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { uint8_t type = literal_p->type; if (type & SCANNER_LITERAL_IS_ARG) { if (can_eval || (literal_p->type & SCANNER_LITERAL_EARLY_CREATE)) { type |= SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_EARLY_CREATE; literal_p->type = type; } if (has_arguments && scanner_literal_is_arguments (literal_p)) { has_arguments = false; } if (type & (SCANNER_LITERAL_IS_DESTRUCTURED_ARG | SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG)) { has_destructured_arg = true; if (type & SCANNER_LITERAL_IS_DESTRUCTURED_ARG) { continue; } type &= (uint8_t) ~SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG; type |= SCANNER_LITERAL_IS_DESTRUCTURED_ARG; literal_p->type = type; continue; } lexer_lit_location_t *new_literal_p; new_literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &new_literal_pool_p->literal_pool); *new_literal_p = *literal_p; } else if (has_arguments && scanner_literal_is_arguments (literal_p)) { /* Arguments object is directly referenced from the function arguments */ new_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ARGUMENTS_IN_ARGS; if (type & SCANNER_LITERAL_NO_REG) { new_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_NO_ARGUMENTS; } } else if (prev_literal_pool_p != NULL) { /* Propagate literal to upper level. */ lexer_lit_location_t *literal_location_p = scanner_add_custom_literal (context_p, prev_literal_pool_p, literal_p); type |= SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_IS_USED; literal_location_p->type |= type; } } if (has_destructured_arg) { parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { const uint8_t expected_flags = SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_DESTRUCTURED_ARG; if ((literal_p->type & expected_flags) == expected_flags) { lexer_lit_location_t *new_literal_p; new_literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &new_literal_pool_p->literal_pool); *new_literal_p = *literal_p; } } } if (has_arguments) { /* Force the lexically stored arguments object creation */ new_literal_pool_p->status_flags |= (SCANNER_LITERAL_POOL_ARGUMENTS_IN_ARGS | SCANNER_LITERAL_POOL_NO_ARGUMENTS); } new_literal_pool_p->prev_p = prev_literal_pool_p; parser_list_free (&literal_pool_p->literal_pool); scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t)); } /* scanner_filter_arguments */ /** * Add any literal to the specified literal pool. * * @return pointer to the literal */ lexer_lit_location_t * scanner_add_custom_literal (parser_context_t *context_p, /**< context */ scanner_literal_pool_t *literal_pool_p, /**< literal pool */ const lexer_lit_location_t *literal_location_p) /**< literal */ { while (true) { parser_list_iterator_t literal_iterator; parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); lexer_lit_location_t *literal_p; const uint8_t *char_p = literal_location_p->char_p; prop_length_t length = literal_location_p->length; if (JERRY_LIKELY (!(literal_location_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))) { while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (literal_p->length == length) { if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))) { if (memcmp (literal_p->char_p, char_p, length) == 0) { return literal_p; } } else if (lexer_compare_identifier_to_string (literal_p, char_p, length)) { /* The non-escaped version is preferred. */ literal_p->char_p = char_p; literal_p->status_flags = LEXER_LIT_LOCATION_NO_OPTS; return literal_p; } } } } else { while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (lexer_compare_identifiers (context_p, literal_p, literal_location_p)) { return literal_p; } } } if (JERRY_UNLIKELY (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_CLASS_NAME)) { literal_pool_p = literal_pool_p->prev_p; continue; } literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &literal_pool_p->literal_pool); *literal_p = *literal_location_p; literal_p->type = 0; return literal_p; } } /* scanner_add_custom_literal */ /** * Add the current literal token to the current literal pool. * * @return pointer to the literal */ lexer_lit_location_t * scanner_add_literal (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { return scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &context_p->token.lit_location); } /* scanner_add_literal */ /** * Add the current literal token to the current literal pool and * set SCANNER_LITERAL_NO_REG if it is inside a with statement. * * @return pointer to the literal */ void scanner_add_reference (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { lexer_lit_location_t *lit_location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &context_p->token.lit_location); lit_location_p->type |= SCANNER_LITERAL_IS_USED; if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) { lit_location_p->type |= SCANNER_LITERAL_NO_REG; } scanner_detect_eval_call (context_p, scanner_context_p); } /* scanner_add_reference */ /** * Append an argument to the literal pool. If the argument is already present, make it a "hole". * * @return newly created literal */ lexer_lit_location_t * scanner_append_argument (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; parser_list_iterator_t literal_iterator; parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); lexer_lit_location_t *literal_location_p = &context_p->token.lit_location; lexer_lit_location_t *literal_p; const uint8_t *char_p = literal_location_p->char_p; prop_length_t length = literal_location_p->length; JERRY_ASSERT (SCANNER_LITERAL_POOL_MAY_HAVE_ARGUMENTS (literal_pool_p->status_flags)); if (JERRY_LIKELY (!(context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))) { while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (literal_p->length == length) { if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))) { if (memcmp (literal_p->char_p, char_p, length) == 0) { break; } } else if (lexer_compare_identifier_to_string (literal_p, char_p, length)) { break; } } } } else { while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (lexer_compare_identifiers (context_p, literal_p, literal_location_p)) { break; } } } uint8_t literal_type = SCANNER_LITERAL_IS_ARG; if (literal_p != NULL) { literal_p->length = 0; if (literal_p->type & SCANNER_LITERAL_IS_USED) { literal_type = SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_EARLY_CREATE; } } literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &literal_pool_p->literal_pool); *literal_p = context_p->token.lit_location; literal_p->type = literal_type; return literal_p; } /* scanner_append_argument */ /** * Add private identifiers to private ident pool */ void scanner_add_private_identifier (parser_context_t *context_p, /**< context */ scanner_private_field_flags_t opts) /**< options */ { scan_stack_modes_t stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); scanner_class_info_t *class_info_p; parser_stack_pop (context_p, &class_info_p, sizeof (scanner_class_info_t *)); scanner_class_private_member_t *iter = class_info_p->members; scanner_private_field_flags_t search_flag = (((unsigned int) opts & (unsigned int) SCANNER_PRIVATE_FIELD_PROPERTY) ? SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER : (scanner_private_field_flags_t) ((unsigned int) opts & (unsigned int) SCANNER_PRIVATE_FIELD_GETTER_SETTER)); while (iter != NULL) { if (lexer_compare_identifiers (context_p, &context_p->token.lit_location, &iter->loc) && (iter->u8_arg & search_flag)) { scanner_raise_error (context_p); } iter = iter->prev_p; } scanner_class_private_member_t *p_member; p_member = (scanner_class_private_member_t *) scanner_malloc (context_p, sizeof (scanner_class_private_member_t)); p_member->loc = context_p->token.lit_location; p_member->u8_arg = (uint8_t) opts; p_member->prev_p = class_info_p->members; class_info_p->members = p_member; parser_stack_push (context_p, &class_info_p, sizeof (scanner_class_info_t *)); parser_stack_push_uint8 (context_p, (uint8_t) stack_top); } /* scanner_add_private_identifier */ /** * Check whether an eval call is performed and update the status flags accordingly. */ void scanner_detect_eval_call (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { if (context_p->token.keyword_type == LEXER_KEYW_EVAL && lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { scanner_context_p->active_literal_pool_p->status_flags |= (SCANNER_LITERAL_POOL_CAN_EVAL | SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE); } } /* scanner_detect_eval_call */ /** * Throws an error for invalid var statements. */ void scanner_detect_invalid_var (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ lexer_lit_location_t *var_literal_p) /**< var literal */ { if (var_literal_p->type & SCANNER_LITERAL_IS_LOCAL && !(var_literal_p->type & (SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_ARG)) && (var_literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL) { scanner_raise_redeclaration_error (context_p); } scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; if (!(literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION) && ((var_literal_p->type & SCANNER_LITERAL_IS_LOCAL_FUNC) == SCANNER_LITERAL_IS_LOCAL_FUNC)) { scanner_raise_redeclaration_error (context_p); } const uint8_t *char_p = var_literal_p->char_p; prop_length_t length = var_literal_p->length; while (!(literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION)) { literal_pool_p = literal_pool_p->prev_p; parser_list_iterator_t literal_iterator; parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); lexer_lit_location_t *literal_p; if (JERRY_LIKELY (!(context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))) { while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if ((literal_p->type & SCANNER_LITERAL_IS_LOCAL) && !(literal_p->type & SCANNER_LITERAL_IS_ARG) && !((literal_p->type & SCANNER_LITERAL_IS_FUNC) && (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION)) && (literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL && literal_p->length == length) { if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))) { if (memcmp (literal_p->char_p, char_p, length) == 0) { scanner_raise_redeclaration_error (context_p); return; } } else if (lexer_compare_identifier_to_string (literal_p, char_p, length)) { scanner_raise_redeclaration_error (context_p); return; } } } } else { while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if ((literal_p->type & SCANNER_LITERAL_IS_LOCAL) && !(literal_p->type & SCANNER_LITERAL_IS_ARG) && !((literal_p->type & SCANNER_LITERAL_IS_FUNC) && (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION)) && (literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL && lexer_compare_identifiers (context_p, literal_p, var_literal_p)) { scanner_raise_redeclaration_error (context_p); return; } } } } if (scanner_scope_find_lexical_declaration (context_p, var_literal_p)) { scanner_raise_redeclaration_error (context_p); } } /* scanner_detect_invalid_var */ /** * Throws an error for invalid let statements. */ void scanner_detect_invalid_let (parser_context_t *context_p, /**< context */ lexer_lit_location_t *let_literal_p) /**< let literal */ { if (let_literal_p->type & (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_LOCAL)) { scanner_raise_redeclaration_error (context_p); } if (let_literal_p->type & SCANNER_LITERAL_IS_FUNC) { let_literal_p->type &= (uint8_t) ~SCANNER_LITERAL_IS_FUNC; } } /* scanner_detect_invalid_let */ /** * Push the values required for class declaration parsing. * * @return literal reference created for class statements, NULL otherwise */ lexer_lit_location_t * scanner_push_class_declaration (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /* scanner context */ uint8_t stack_mode) /**< stack mode */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_CLASS); const uint8_t *source_p = context_p->source_p; lexer_lit_location_t *literal_p = NULL; #if JERRY_MODULE_SYSTEM bool is_export_default = context_p->stack_top_uint8 == SCAN_STACK_EXPORT_DEFAULT; JERRY_ASSERT (!is_export_default || stack_mode == SCAN_STACK_CLASS_EXPRESSION); #endif /* JERRY_MODULE_SYSTEM */ parser_stack_push_uint8 (context_p, stack_mode); lexer_next_token (context_p); bool class_has_name = (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); if (class_has_name) { literal_p = scanner_add_literal (context_p, scanner_context_p); scanner_context_p->active_literal_pool_p->no_declarations++; #if JERRY_MODULE_SYSTEM if (is_export_default) { scanner_detect_invalid_let (context_p, literal_p); if (literal_p->type & SCANNER_LITERAL_IS_USED) { literal_p->type |= SCANNER_LITERAL_EARLY_CREATE; } literal_p->type |= SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_NO_REG; } #endif /* JERRY_MODULE_SYSTEM */ } scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); if (class_has_name) { scanner_add_literal (context_p, scanner_context_p); scanner_context_p->active_literal_pool_p->no_declarations++; } #if JERRY_MODULE_SYSTEM else if (is_export_default) { lexer_lit_location_t *name_literal_p; name_literal_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p->prev_p, &lexer_default_literal); name_literal_p->type |= SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_NO_REG; scanner_context_p->active_literal_pool_p->no_declarations++; } #endif /* JERRY_MODULE_SYSTEM */ literal_pool_p->source_p = source_p; literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_CLASS_NAME; const uint8_t *class_source_p = scanner_context_p->active_literal_pool_p->source_p; scanner_class_info_t *class_info_p = (scanner_class_info_t *) scanner_insert_info (context_p, class_source_p, sizeof (scanner_class_info_t)); class_info_p->info.type = SCANNER_TYPE_CLASS_CONSTRUCTOR; class_info_p->members = NULL; class_info_p->info.u8_arg = SCANNER_CONSTRUCTOR_IMPLICIT; parser_stack_push (context_p, &class_info_p, sizeof (scanner_class_info_t *)); parser_stack_push_uint8 (context_p, SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR); scanner_context_p->mode = SCAN_MODE_CLASS_DECLARATION; return literal_p; } /* scanner_push_class_declaration */ /** * Push the start of a class field initializer. */ void scanner_push_class_field_initializer (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /* scanner context */ { scanner_source_start_t source_start; source_start.source_p = context_p->source_p; parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_FIELD_INITIALIZER); scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_CLASS_FIELD); literal_pool_p->source_p = context_p->source_p; scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; } /* scanner_push_class_field_initializer */ /** * Push the values required for destructuring assignment or binding parsing. */ void scanner_push_destructuring_pattern (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ uint8_t binding_type, /**< type of destructuring binding pattern */ bool is_nested) /**< nested declaration */ { JERRY_ASSERT (binding_type != SCANNER_BINDING_NONE || !is_nested); scanner_source_start_t source_start; source_start.source_p = context_p->source_p; parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t)); parser_stack_push_uint8 (context_p, scanner_context_p->binding_type); scanner_context_p->binding_type = binding_type; if (SCANNER_NEEDS_BINDING_LIST (binding_type)) { scanner_binding_list_t *binding_list_p; binding_list_p = (scanner_binding_list_t *) scanner_malloc (context_p, sizeof (scanner_binding_list_t)); binding_list_p->prev_p = scanner_context_p->active_binding_list_p; binding_list_p->items_p = NULL; binding_list_p->is_nested = is_nested; scanner_context_p->active_binding_list_p = binding_list_p; } } /* scanner_push_destructuring_pattern */ /** * Pop binding list. */ void scanner_pop_binding_list (scanner_context_t *scanner_context_p) /**< scanner context */ { scanner_binding_list_t *binding_list_p = scanner_context_p->active_binding_list_p; JERRY_ASSERT (binding_list_p != NULL); scanner_binding_item_t *item_p = binding_list_p->items_p; scanner_binding_list_t *prev_binding_list_p = binding_list_p->prev_p; bool is_nested = binding_list_p->is_nested; scanner_free (binding_list_p, sizeof (scanner_binding_list_t)); scanner_context_p->active_binding_list_p = prev_binding_list_p; if (!is_nested) { while (item_p != NULL) { scanner_binding_item_t *next_p = item_p->next_p; JERRY_ASSERT (item_p->literal_p->type & (SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_IS_ARG)); scanner_free (item_p, sizeof (scanner_binding_item_t)); item_p = next_p; } return; } JERRY_ASSERT (prev_binding_list_p != NULL); while (item_p != NULL) { scanner_binding_item_t *next_p = item_p->next_p; item_p->next_p = prev_binding_list_p->items_p; prev_binding_list_p->items_p = item_p; item_p = next_p; } } /* scanner_pop_binding_list */ /** * Append a hole into the literal pool. */ void scanner_append_hole (parser_context_t *context_p, scanner_context_t *scanner_context_p) { scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; lexer_lit_location_t *literal_p; literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &literal_pool_p->literal_pool); literal_p->char_p = NULL; literal_p->length = 0; literal_p->type = SCANNER_LITERAL_IS_ARG; literal_p->status_flags = LEXER_LIT_LOCATION_NO_OPTS; } /* scanner_append_hole */ /** * Reverse the scanner info chain after the scanning is completed. */ void scanner_reverse_info_list (parser_context_t *context_p) /**< context */ { scanner_info_t *scanner_info_p = context_p->next_scanner_info_p; scanner_info_t *last_scanner_info_p = NULL; if (scanner_info_p->type == SCANNER_TYPE_END) { return; } do { scanner_info_t *next_scanner_info_p = scanner_info_p->next_p; scanner_info_p->next_p = last_scanner_info_p; last_scanner_info_p = scanner_info_p; scanner_info_p = next_scanner_info_p; } while (scanner_info_p->type != SCANNER_TYPE_END); context_p->next_scanner_info_p->next_p = scanner_info_p; context_p->next_scanner_info_p = last_scanner_info_p; } /* scanner_reverse_info_list */ /** * Release unused scanner info blocks. * This should happen only if an error is occurred. */ void scanner_cleanup (parser_context_t *context_p) /**< context */ { if (context_p->skipped_scanner_info_p != NULL) { context_p->skipped_scanner_info_end_p->next_p = context_p->next_scanner_info_p; context_p->next_scanner_info_p = context_p->skipped_scanner_info_p; context_p->skipped_scanner_info_p = NULL; } scanner_info_t *scanner_info_p = context_p->next_scanner_info_p; while (scanner_info_p != NULL) { scanner_info_t *next_scanner_info_p = scanner_info_p->next_p; size_t size = sizeof (scanner_info_t); switch (scanner_info_p->type) { case SCANNER_TYPE_END: { scanner_info_p = context_p->active_scanner_info_p; continue; } case SCANNER_TYPE_FUNCTION: case SCANNER_TYPE_BLOCK: { size = scanner_get_stream_size (scanner_info_p, sizeof (scanner_info_t)); break; } case SCANNER_TYPE_WHILE: case SCANNER_TYPE_FOR_IN: case SCANNER_TYPE_FOR_OF: case SCANNER_TYPE_CASE: case SCANNER_TYPE_INITIALIZER: case SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END: case SCANNER_TYPE_CLASS_STATIC_BLOCK_END: { size = sizeof (scanner_location_info_t); break; } case SCANNER_TYPE_FOR: { size = sizeof (scanner_for_info_t); break; } case SCANNER_TYPE_SWITCH: { scanner_release_switch_cases (((scanner_switch_info_t *) scanner_info_p)->case_p); size = sizeof (scanner_switch_info_t); break; } case SCANNER_TYPE_CLASS_CONSTRUCTOR: { scanner_release_private_fields (((scanner_class_info_t *) scanner_info_p)->members); size = sizeof (scanner_class_info_t); break; } default: { JERRY_ASSERT ( scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS || scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS || scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION || scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED || scanner_info_p->type == SCANNER_TYPE_ERR_ASYNC_FUNCTION || scanner_info_p->type == SCANNER_TYPE_EXPORT_MODULE_SPECIFIER); break; } } scanner_free (scanner_info_p, size); scanner_info_p = next_scanner_info_p; } context_p->next_scanner_info_p = NULL; context_p->active_scanner_info_p = NULL; } /* scanner_cleanup */ /** * Checks whether a context needs to be created for a block. * * @return true - if context is needed, * false - otherwise */ bool scanner_is_context_needed (parser_context_t *context_p, /**< context */ parser_check_context_type_t check_type) /**< context type */ { scanner_info_t *info_p = context_p->next_scanner_info_p; const uint8_t *data_p = (const uint8_t *) (info_p + 1); JERRY_UNUSED (check_type); JERRY_ASSERT ((check_type == PARSER_CHECK_BLOCK_CONTEXT ? info_p->type == SCANNER_TYPE_BLOCK : info_p->type == SCANNER_TYPE_FUNCTION)); uint32_t scope_stack_reg_top = (check_type != PARSER_CHECK_GLOBAL_CONTEXT ? context_p->scope_stack_reg_top : 1); /* block result */ while (data_p[0] != SCANNER_STREAM_TYPE_END) { uint8_t data = data_p[0]; uint32_t type = data & SCANNER_STREAM_TYPE_MASK; if (JERRY_UNLIKELY (check_type == PARSER_CHECK_FUNCTION_CONTEXT)) { if (JERRY_UNLIKELY (type == SCANNER_STREAM_TYPE_HOLE)) { data_p++; continue; } if (JERRY_UNLIKELY (SCANNER_STREAM_TYPE_IS_ARGUMENTS (type))) { if ((data & SCANNER_STREAM_NO_REG) || scope_stack_reg_top >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { return true; } scope_stack_reg_top++; data_p++; continue; } } #ifndef JERRY_NDEBUG if (check_type == PARSER_CHECK_BLOCK_CONTEXT) { JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_LOCAL || type == SCANNER_STREAM_TYPE_FUNC); } else if (check_type == PARSER_CHECK_GLOBAL_CONTEXT) { #if JERRY_MODULE_SYSTEM const bool is_import = (type == SCANNER_STREAM_TYPE_IMPORT); #else /* !JERRY_MODULE_SYSTEM */ const bool is_import = true; #endif /* JERRY_MODULE_SYSTEM */ /* FIXME: a private declarative lexical environment should always be present * for modules. Remove SCANNER_STREAM_TYPE_IMPORT after it is implemented. */ JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_FUNC || is_import); /* Only let/const can be stored in registers */ JERRY_ASSERT ((data & SCANNER_STREAM_NO_REG) || (type == SCANNER_STREAM_TYPE_FUNC && (context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL)) || type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST); } else { JERRY_ASSERT (check_type == PARSER_CHECK_FUNCTION_CONTEXT); JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_LOCAL || type == SCANNER_STREAM_TYPE_ARG || type == SCANNER_STREAM_TYPE_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR || type == SCANNER_STREAM_TYPE_ARG_FUNC || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC || type == SCANNER_STREAM_TYPE_FUNC); } #endif /* !JERRY_NDEBUG */ if (!(data & SCANNER_STREAM_UINT16_DIFF)) { if (data_p[2] != 0) { data_p += 2 + 1; } else { data_p += 2 + 1 + sizeof (const uint8_t *); } } else { data_p += 2 + 2; } #if JERRY_MODULE_SYSTEM const bool is_import = (type == SCANNER_STREAM_TYPE_IMPORT); #else /* !JERRY_MODULE_SYSTEM */ const bool is_import = false; #endif /* JERRY_MODULE_SYSTEM */ if (JERRY_UNLIKELY (check_type == PARSER_CHECK_GLOBAL_CONTEXT) && (type == SCANNER_STREAM_TYPE_VAR || (type == SCANNER_STREAM_TYPE_FUNC && !(context_p->global_status_flags & ECMA_PARSE_EVAL)) || is_import)) { continue; } if (JERRY_UNLIKELY (check_type == PARSER_CHECK_FUNCTION_CONTEXT)) { if (SCANNER_STREAM_TYPE_IS_ARG_FUNC (type) || type == SCANNER_STREAM_TYPE_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR) { /* The return value is true, if the variable is stored in the lexical environment * or all registers have already been used for function arguments. This can be * imprecise in the latter case, but this is a very rare corner case. A more * sophisticated check would require to decode the literal. */ if ((data & SCANNER_STREAM_NO_REG) || scope_stack_reg_top >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { return true; } continue; } if (SCANNER_STREAM_TYPE_IS_ARG (type)) { continue; } } if ((data & SCANNER_STREAM_NO_REG) || scope_stack_reg_top >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { return true; } scope_stack_reg_top++; } return false; } /* scanner_is_context_needed */ /** * Try to scan/parse the ".target" part in the "new.target" expression. * * Upon exiting with "true" the current token will point to the "target" * literal. * * If the "target" literal is not after the "new." then a scanner/parser * error will be raised. * * @returns true if the ".target" part was found * false if there is no "." after the new. */ bool scanner_try_scan_new_target (parser_context_t *context_p) /**< parser/scanner context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_NEW); if (lexer_check_next_character (context_p, LIT_CHAR_DOT)) { lexer_next_token (context_p); if (context_p->token.type != LEXER_DOT) { parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); } lexer_next_token (context_p); if (!lexer_token_is_identifier (context_p, "target", 6)) { parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_EXPECTED); } return true; } return false; } /* scanner_try_scan_new_target */ /** * Description of "arguments" literal string. */ const lexer_lit_location_t lexer_arguments_literal = { (const uint8_t *) "arguments", 9, LEXER_IDENT_LITERAL, LEXER_LIT_LOCATION_IS_ASCII }; /** * Create an unused literal. */ static void scanner_create_unused_literal (parser_context_t *context_p, /**< context */ uint8_t status_flags) /**< initial status flags */ { if (JERRY_UNLIKELY (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS)) { parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->type = LEXER_UNUSED_LITERAL; literal_p->status_flags = status_flags; context_p->literal_count++; } /* scanner_create_unused_literal */ /** * Emit checks for redeclared bindings in the global lexical scope. */ void scanner_check_variables (parser_context_t *context_p) /**< context */ { scanner_info_t *info_p = context_p->next_scanner_info_p; const uint8_t *next_data_p = (const uint8_t *) (info_p + 1); lexer_lit_location_t literal; JERRY_ASSERT (info_p->type == SCANNER_TYPE_FUNCTION); literal.char_p = info_p->source_p - 1; while (next_data_p[0] != SCANNER_STREAM_TYPE_END) { uint32_t type = next_data_p[0] & SCANNER_STREAM_TYPE_MASK; const uint8_t *data_p = next_data_p; JERRY_ASSERT (type != SCANNER_STREAM_TYPE_HOLE && !SCANNER_STREAM_TYPE_IS_ARG (type) && !SCANNER_STREAM_TYPE_IS_ARG_FUNC (type)); JERRY_ASSERT (data_p[0] & SCANNER_STREAM_NO_REG); if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF)) { if (data_p[2] != 0) { literal.char_p += data_p[2]; next_data_p += 2 + 1; } else { memcpy (&literal.char_p, data_p + 2 + 1, sizeof (uintptr_t)); next_data_p += 2 + 1 + sizeof (uintptr_t); } } else { int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8; if (diff <= (intptr_t) UINT8_MAX) { diff = -diff; } literal.char_p += diff; next_data_p += 2 + 2; } literal.length = data_p[1]; literal.type = LEXER_IDENT_LITERAL; literal.status_flags = ((data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? LEXER_LIT_LOCATION_HAS_ESCAPE : LEXER_LIT_LOCATION_NO_OPTS); lexer_construct_literal_object (context_p, &literal, LEXER_NEW_IDENT_LITERAL); literal.char_p += data_p[1]; #if JERRY_MODULE_SYSTEM if (type == SCANNER_STREAM_TYPE_IMPORT) { continue; } #endif /* JERRY_MODULE_SYSTEM */ context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; uint16_t opcode; if (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_FUNC) { opcode = CBC_CHECK_VAR; } else { opcode = CBC_CHECK_LET; } parser_emit_cbc_literal (context_p, opcode, context_p->lit_object.index); } parser_flush_cbc (context_p); } /* scanner_check_variables */ /** * Create and/or initialize var/let/const/function/etc. variables. */ void scanner_create_variables (parser_context_t *context_p, /**< context */ uint32_t option_flags) /**< combination of scanner_create_variables_flags_t bits */ { scanner_info_t *info_p = context_p->next_scanner_info_p; const uint8_t *next_data_p = (const uint8_t *) (info_p + 1); uint8_t info_type = info_p->type; uint8_t info_u8_arg = info_p->u8_arg; lexer_lit_location_t literal; parser_scope_stack_t *scope_stack_p; parser_scope_stack_t *scope_stack_end_p; JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION || info_type == SCANNER_TYPE_BLOCK); JERRY_ASSERT (!(option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_ARGS) || !(option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY)); JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION || !(option_flags & (SCANNER_CREATE_VARS_IS_FUNCTION_ARGS | SCANNER_CREATE_VARS_IS_FUNCTION_BODY))); uint32_t scope_stack_reg_top = context_p->scope_stack_reg_top; if (info_type == SCANNER_TYPE_FUNCTION && !(option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY)) { JERRY_ASSERT (context_p->scope_stack_p == NULL); size_t stack_size = info_p->u16_arg * sizeof (parser_scope_stack_t); context_p->scope_stack_size = info_p->u16_arg; scope_stack_p = NULL; if (stack_size > 0) { scope_stack_p = (parser_scope_stack_t *) parser_malloc (context_p, stack_size); } context_p->scope_stack_p = scope_stack_p; scope_stack_end_p = scope_stack_p + context_p->scope_stack_size; if (option_flags & (SCANNER_CREATE_VARS_IS_SCRIPT | SCANNER_CREATE_VARS_IS_MODULE)) { scope_stack_reg_top++; /* block result */ } } else { JERRY_ASSERT (context_p->scope_stack_p != NULL || context_p->scope_stack_size == 0); scope_stack_p = context_p->scope_stack_p; scope_stack_end_p = scope_stack_p + context_p->scope_stack_size; scope_stack_p += context_p->scope_stack_top; } literal.char_p = info_p->source_p - 1; while (next_data_p[0] != SCANNER_STREAM_TYPE_END) { uint32_t type = next_data_p[0] & SCANNER_STREAM_TYPE_MASK; const uint8_t *data_p = next_data_p; JERRY_ASSERT ((option_flags & (SCANNER_CREATE_VARS_IS_FUNCTION_BODY | SCANNER_CREATE_VARS_IS_FUNCTION_ARGS)) || (type != SCANNER_STREAM_TYPE_HOLE && !SCANNER_STREAM_TYPE_IS_ARG (type) && !SCANNER_STREAM_TYPE_IS_ARG_FUNC (type))); #if JERRY_MODULE_SYSTEM JERRY_ASSERT (type != SCANNER_STREAM_TYPE_IMPORT || (data_p[0] & SCANNER_STREAM_NO_REG)); #endif /* JERRY_MODULE_SYSTEM */ if (JERRY_UNLIKELY (type == SCANNER_STREAM_TYPE_HOLE)) { JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION); next_data_p++; if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY) { continue; } uint8_t mask = SCANNER_FUNCTION_ARGUMENTS_NEEDED | SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT; if (!(context_p->status_flags & PARSER_IS_STRICT) && (info_u8_arg & mask) == SCANNER_FUNCTION_ARGUMENTS_NEEDED) { scanner_create_unused_literal (context_p, LEXER_FLAG_FUNCTION_ARGUMENT); } if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { scope_stack_reg_top++; } continue; } if (JERRY_UNLIKELY (SCANNER_STREAM_TYPE_IS_ARGUMENTS (type))) { JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION); next_data_p++; if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY) { continue; } context_p->status_flags |= PARSER_ARGUMENTS_NEEDED; if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p)) { JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); } lexer_construct_literal_object (context_p, &lexer_arguments_literal, LEXER_NEW_IDENT_LITERAL); scope_stack_p->map_from = context_p->lit_object.index; uint16_t map_to; if (!(data_p[0] & SCANNER_STREAM_NO_REG) && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top); scope_stack_p->map_to = (uint16_t) (scope_stack_reg_top + 1); scope_stack_reg_top++; } else { context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; map_to = context_p->lit_object.index; context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; if (data_p[0] & SCANNER_STREAM_LOCAL_ARGUMENTS) { context_p->status_flags |= PARSER_LEXICAL_BLOCK_NEEDED; } scope_stack_p->map_to = 0; } scope_stack_p++; #if JERRY_PARSER_DUMP_BYTE_CODE context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ parser_emit_cbc_ext_literal (context_p, CBC_EXT_CREATE_ARGUMENTS, map_to); if (type == SCANNER_STREAM_TYPE_ARGUMENTS_FUNC) { if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p)) { JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); } scope_stack_p->map_from = PARSER_SCOPE_STACK_FUNC; scope_stack_p->map_to = context_p->literal_count; scope_stack_p++; scanner_create_unused_literal (context_p, 0); } if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_ARGS) { break; } continue; } JERRY_ASSERT (context_p->scope_stack_size != 0); if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF)) { if (data_p[2] != 0) { literal.char_p += data_p[2]; next_data_p += 2 + 1; } else { memcpy (&literal.char_p, data_p + 2 + 1, sizeof (uintptr_t)); next_data_p += 2 + 1 + sizeof (uintptr_t); } } else { int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8; if (diff <= (intptr_t) UINT8_MAX) { diff = -diff; } literal.char_p += diff; next_data_p += 2 + 2; } if (SCANNER_STREAM_TYPE_IS_ARG (type)) { if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY) { if ((context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED) && (type == SCANNER_STREAM_TYPE_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR)) { literal.length = data_p[1]; literal.type = LEXER_IDENT_LITERAL; literal.status_flags = ((data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? LEXER_LIT_LOCATION_HAS_ESCAPE : LEXER_LIT_LOCATION_NO_OPTS); /* Literal must be exists. */ lexer_construct_literal_object (context_p, &literal, LEXER_IDENT_LITERAL); if (context_p->lit_object.index < PARSER_REGISTER_START) { parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_COPY_FROM_ARG); } } literal.char_p += data_p[1]; continue; } } else if ((option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_ARGS) && !SCANNER_STREAM_TYPE_IS_ARG_FUNC (type)) { /* Function arguments must come first. */ break; } literal.length = data_p[1]; literal.type = LEXER_IDENT_LITERAL; literal.status_flags = ((data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? LEXER_LIT_LOCATION_HAS_ESCAPE : LEXER_LIT_LOCATION_NO_OPTS); lexer_construct_literal_object (context_p, &literal, LEXER_NEW_IDENT_LITERAL); literal.char_p += data_p[1]; if (SCANNER_STREAM_TYPE_IS_ARG_FUNC (type) && (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY)) { JERRY_ASSERT (scope_stack_p >= context_p->scope_stack_p + 2); JERRY_ASSERT (context_p->status_flags & PARSER_IS_FUNCTION); JERRY_ASSERT (!(context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS)); parser_scope_stack_t *function_map_p = scope_stack_p - 2; uint16_t literal_index = context_p->lit_object.index; while (literal_index != function_map_p->map_from) { function_map_p--; JERRY_ASSERT (function_map_p >= context_p->scope_stack_p); } JERRY_ASSERT (function_map_p[1].map_from == PARSER_SCOPE_STACK_FUNC); cbc_opcode_t opcode = CBC_SET_VAR_FUNC; if (JERRY_UNLIKELY (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED) && (function_map_p[0].map_to & PARSER_SCOPE_STACK_REGISTER_MASK) == 0) { opcode = CBC_INIT_ARG_OR_FUNC; } parser_emit_cbc_literal_value (context_p, (uint16_t) opcode, function_map_p[1].map_to, scanner_decode_map_to (function_map_p)); continue; } if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p)) { JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); } scope_stack_p->map_from = context_p->lit_object.index; if (info_type == SCANNER_TYPE_FUNCTION) { if (type != SCANNER_STREAM_TYPE_LET #if JERRY_MODULE_SYSTEM && type != SCANNER_STREAM_TYPE_IMPORT #endif /* JERRY_MODULE_SYSTEM */ && type != SCANNER_STREAM_TYPE_CONST) { context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_GLOBAL; } } uint16_t map_to; uint16_t func_init_opcode = CBC_INIT_ARG_OR_FUNC; if (!(data_p[0] & SCANNER_STREAM_NO_REG) && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top); scope_stack_p->map_to = (uint16_t) (scope_stack_reg_top + 1); scope_stack_reg_top++; switch (type) { case SCANNER_STREAM_TYPE_CONST: { scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_CONST_REG; /* FALLTHRU */ } case SCANNER_STREAM_TYPE_LET: case SCANNER_STREAM_TYPE_ARG: case SCANNER_STREAM_TYPE_ARG_VAR: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR: case SCANNER_STREAM_TYPE_ARG_FUNC: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: { scope_stack_p->map_to |= PARSER_SCOPE_STACK_NO_FUNCTION_COPY; break; } } func_init_opcode = CBC_SET_VAR_FUNC; } else { context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; map_to = context_p->lit_object.index; uint16_t scope_stack_map_to = 0; if (info_type == SCANNER_TYPE_FUNCTION) { context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; } switch (type) { case SCANNER_STREAM_TYPE_LET: case SCANNER_STREAM_TYPE_CONST: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: { scope_stack_map_to |= PARSER_SCOPE_STACK_NO_FUNCTION_COPY; if (!(data_p[0] & SCANNER_STREAM_EARLY_CREATE)) { break; } scope_stack_map_to |= PARSER_SCOPE_STACK_IS_LOCAL_CREATED; /* FALLTHRU */ } case SCANNER_STREAM_TYPE_LOCAL: case SCANNER_STREAM_TYPE_VAR: { #if JERRY_PARSER_DUMP_BYTE_CODE context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ uint16_t opcode; switch (type) { case SCANNER_STREAM_TYPE_LET: { opcode = CBC_CREATE_LET; break; } case SCANNER_STREAM_TYPE_CONST: { opcode = CBC_CREATE_CONST; break; } case SCANNER_STREAM_TYPE_VAR: { opcode = CBC_CREATE_VAR; if (option_flags & SCANNER_CREATE_VARS_IS_SCRIPT) { opcode = CBC_CREATE_VAR_EVAL; if ((context_p->global_status_flags & ECMA_PARSE_FUNCTION_CONTEXT) && !(context_p->status_flags & PARSER_IS_STRICT)) { opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_CREATE_VAR_EVAL); } } break; } default: { JERRY_ASSERT (type == SCANNER_STREAM_TYPE_LOCAL || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC); opcode = CBC_CREATE_LOCAL; break; } } parser_emit_cbc_literal (context_p, opcode, map_to); break; } case SCANNER_STREAM_TYPE_ARG: case SCANNER_STREAM_TYPE_ARG_VAR: case SCANNER_STREAM_TYPE_ARG_FUNC: { #if JERRY_PARSER_DUMP_BYTE_CODE context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ scope_stack_map_to |= PARSER_SCOPE_STACK_NO_FUNCTION_COPY; /* Argument initializers of functions with simple arguments (e.g. function f(a,b,a) {}) are * generated here. The other initializers are handled by parser_parse_function_arguments(). */ if (!(info_u8_arg & SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT)) { parser_emit_cbc_literal_value (context_p, CBC_INIT_ARG_OR_FUNC, (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top), map_to); } else if (data_p[0] & SCANNER_STREAM_EARLY_CREATE) { parser_emit_cbc_literal (context_p, CBC_CREATE_LOCAL, map_to); scope_stack_map_to |= PARSER_SCOPE_STACK_IS_LOCAL_CREATED; } if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { scope_stack_reg_top++; } break; } } scope_stack_p->map_to = scope_stack_map_to; } scope_stack_p++; if (!SCANNER_STREAM_TYPE_IS_FUNCTION (type)) { continue; } if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p)) { JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); } #if JERRY_PARSER_DUMP_BYTE_CODE context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ if (!SCANNER_STREAM_TYPE_IS_ARG_FUNC (type)) { if (func_init_opcode == CBC_INIT_ARG_OR_FUNC && (option_flags & SCANNER_CREATE_VARS_IS_SCRIPT)) { literal.char_p -= data_p[1]; if (!scanner_scope_find_lexical_declaration (context_p, &literal)) { func_init_opcode = CBC_CREATE_VAR_FUNC_EVAL; if (context_p->global_status_flags & ECMA_PARSE_FUNCTION_CONTEXT) { func_init_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_CREATE_VAR_FUNC_EVAL); } } literal.char_p += data_p[1]; } parser_emit_cbc_literal_value (context_p, func_init_opcode, context_p->literal_count, map_to); } scope_stack_p->map_from = PARSER_SCOPE_STACK_FUNC; scope_stack_p->map_to = context_p->literal_count; scope_stack_p++; scanner_create_unused_literal (context_p, 0); } context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); context_p->scope_stack_reg_top = (uint16_t) scope_stack_reg_top; if (info_type == SCANNER_TYPE_FUNCTION) { context_p->scope_stack_global_end = context_p->scope_stack_top; } if (context_p->register_count < scope_stack_reg_top) { context_p->register_count = (uint16_t) scope_stack_reg_top; } if (!(option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_ARGS)) { scanner_release_next (context_p, (size_t) (next_data_p + 1 - ((const uint8_t *) info_p))); } parser_flush_cbc (context_p); } /* scanner_create_variables */ /** * Get location from context. */ void scanner_get_location (scanner_location_t *location_p, /**< location */ parser_context_t *context_p) /**< context */ { location_p->source_p = context_p->source_p; location_p->line = context_p->line; location_p->column = context_p->column; } /* scanner_get_location */ /** * Set context location. */ void scanner_set_location (parser_context_t *context_p, /**< context */ scanner_location_t *location_p) /**< location */ { context_p->source_p = location_p->source_p; context_p->line = location_p->line; context_p->column = location_p->column; } /* scanner_set_location */ /** * Get the real map_to value. */ uint16_t scanner_decode_map_to (parser_scope_stack_t *stack_item_p) /**< scope stack item */ { JERRY_ASSERT (stack_item_p->map_from != PARSER_SCOPE_STACK_FUNC); uint16_t value = (stack_item_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK); return (value == 0) ? stack_item_p->map_from : (uint16_t) (value + (PARSER_REGISTER_START - 1)); } /* scanner_decode_map_to */ /** * Find the given literal index in the scope stack * and save it the constant literal pool if the literal is register stored * * @return given literal index - if literal corresponds to this index is not register stored * literal index on which literal index has been mapped - otherwise */ uint16_t scanner_save_literal (parser_context_t *context_p, /**< context */ uint16_t literal_index) /**< literal index */ { if (literal_index >= PARSER_REGISTER_START) { literal_index = (uint16_t) (literal_index - (PARSER_REGISTER_START - 1)); parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top; do { /* Registers must be found in the scope stack. */ JERRY_ASSERT (scope_stack_p > context_p->scope_stack_p); scope_stack_p--; } while (scope_stack_p->map_from == PARSER_SCOPE_STACK_FUNC || literal_index != (scope_stack_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK)); literal_index = scope_stack_p->map_from; PARSER_GET_LITERAL (literal_index)->status_flags |= LEXER_FLAG_USED; } return literal_index; } /* scanner_save_literal */ /** * Checks whether the literal is a const in the current scope. * * @return true if the literal is a const, false otherwise */ bool scanner_literal_is_const_reg (parser_context_t *context_p, /**< context */ uint16_t literal_index) /**< literal index */ { if (literal_index < PARSER_REGISTER_START) { /* Re-assignment of non-register const bindings are detected elsewhere. */ return false; } parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top; literal_index = (uint16_t) (literal_index - (PARSER_REGISTER_START - 1)); do { /* Registers must be found in the scope stack. */ JERRY_ASSERT (scope_stack_p > context_p->scope_stack_p); scope_stack_p--; } while (scope_stack_p->map_from == PARSER_SCOPE_STACK_FUNC || literal_index != (scope_stack_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK)); return (scope_stack_p->map_to & PARSER_SCOPE_STACK_IS_CONST_REG) != 0; } /* scanner_literal_is_const_reg */ /** * Checks whether the literal is created before. * * @return true if the literal is created before, false otherwise */ bool scanner_literal_is_created (parser_context_t *context_p, /**< context */ uint16_t literal_index) /**< literal index */ { JERRY_ASSERT (literal_index < PARSER_REGISTER_START); parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top; do { /* These literals must be found in the scope stack. */ JERRY_ASSERT (scope_stack_p > context_p->scope_stack_p); scope_stack_p--; } while (literal_index != scope_stack_p->map_from); JERRY_ASSERT ((scope_stack_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK) == 0); return (scope_stack_p->map_to & PARSER_SCOPE_STACK_IS_LOCAL_CREATED) != 0; } /* scanner_literal_is_created */ /** * Checks whether the literal exists. * * @return true if the literal exists, false otherwise */ bool scanner_literal_exists (parser_context_t *context_p, /**< context */ uint16_t literal_index) /**< literal index */ { JERRY_ASSERT (literal_index < PARSER_REGISTER_START); parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top; while (scope_stack_p-- > context_p->scope_stack_p) { if (scope_stack_p->map_from != PARSER_SCOPE_STACK_FUNC && scanner_decode_map_to (scope_stack_p) == literal_index) { return true; } } return false; } /* scanner_literal_exists */ /** * @} * @} * @} */ #endif /* JERRY_PARSER */ jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.inc.h000664 001750 001750 00000002307 15164251010 055303 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %RegExpStringIteratorPrototype% built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #ifdef JERRY_BUILTIN_REGEXP STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_NEXT, ecma_builtin_regexp_string_iterator_prototype_object_next, 0, 0) #endif #include "ecma-builtin-helpers-macro-undefs.inc.h" loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int8array.inc.h000664 001750 001750 00000001706 15164251010 052722 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Int8Array description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 1 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_INT8_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_INT8ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dec/common.h000664 001750 001750 00000003212 15164251010 034564 0ustar00ddennedyddennedy000000 000000 // Copyright 2015 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Definitions and macros common to encoding and decoding // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_DEC_COMMON_H_ #define WEBP_DEC_COMMON_H_ // intra prediction modes enum { B_DC_PRED = 0, // 4x4 modes B_TM_PRED = 1, B_VE_PRED = 2, B_HE_PRED = 3, B_RD_PRED = 4, B_VR_PRED = 5, B_LD_PRED = 6, B_VL_PRED = 7, B_HD_PRED = 8, B_HU_PRED = 9, NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10 // Luma16 or UV modes DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED, H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED, B_PRED = NUM_BMODES, // refined I4x4 mode NUM_PRED_MODES = 4, // special modes B_DC_PRED_NOTOP = 4, B_DC_PRED_NOLEFT = 5, B_DC_PRED_NOTOPLEFT = 6, NUM_B_DC_MODES = 7 }; enum { MB_FEATURE_TREE_PROBS = 3, NUM_MB_SEGMENTS = 4, NUM_REF_LF_DELTAS = 4, NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT MAX_NUM_PARTITIONS = 8, // Probabilities NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC NUM_BANDS = 8, NUM_CTX = 3, NUM_PROBAS = 11 }; #endif // WEBP_DEC_COMMON_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/png/000775 001750 001750 00000000000 15164251010 032221 5ustar00ddennedyddennedy000000 000000 glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jcontext/jcontext.h000664 001750 001750 00000027247 15164251010 043200 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Engine context for JerryScript */ #ifndef JCONTEXT_H #define JCONTEXT_H #include "ecma-builtins.h" #include "ecma-helpers.h" #include "ecma-jobqueue.h" #include "jmem.h" #include "js-parser-internal.h" #include "re-bytecode.h" #include "vm-defines.h" /** \addtogroup context Context * @{ */ /** * Advanced allocator configurations. */ /** * Maximum global heap size in bytes */ #define CONFIG_MEM_HEAP_SIZE (JERRY_GLOBAL_HEAP_SIZE * 1024) /** * Maximum stack usage size in bytes */ #define CONFIG_MEM_STACK_LIMIT (JERRY_STACK_LIMIT * 1024) /** * Max heap usage limit */ #define CONFIG_MAX_GC_LIMIT 8192 /** * Allowed heap usage limit until next garbage collection * * Whenever the total allocated memory size reaches the current heap limit, garbage collection will be triggered * to try and reduce clutter from unreachable objects. If the allocated memory can't be reduced below the limit, * then the current limit will be incremented by CONFIG_MEM_HEAP_LIMIT. */ #if defined(JERRY_GC_LIMIT) && (JERRY_GC_LIMIT != 0) #define CONFIG_GC_LIMIT JERRY_GC_LIMIT #else /* !(defined(JERRY_GC_LIMIT) && (JERRY_GC_LIMIT != 0)) */ #define CONFIG_GC_LIMIT (JERRY_MIN (CONFIG_MEM_HEAP_SIZE / 32, CONFIG_MAX_GC_LIMIT)) #endif /* defined(JERRY_GC_LIMIT) && (JERRY_GC_LIMIT != 0) */ /** * Amount of newly allocated objects since the last GC run, represented as a fraction of all allocated objects, * which when reached will trigger garbage collection to run with a low pressure setting. * * The fraction is calculated as: * 1.0 / CONFIG_ECMA_GC_NEW_OBJECTS_FRACTION */ #define CONFIG_ECMA_GC_NEW_OBJECTS_FRACTION (16) #if !JERRY_SYSTEM_ALLOCATOR /** * Heap structure * * Memory blocks returned by the allocator must not start from the * beginning of the heap area because offset 0 is reserved for * JMEM_CP_NULL. This special constant is used in several places, * e.g. it marks the end of the property chain list, so it cannot * be eliminated from the project. Although the allocator cannot * use the first 8 bytes of the heap, nothing prevents to use it * for other purposes. Currently the free region start is stored * there. */ typedef struct jmem_heap_t jmem_heap_t; #endif /* !JERRY_SYSTEM_ALLOCATOR */ /** * User context item */ typedef struct jerry_context_data_header { struct jerry_context_data_header *next_p; /**< pointer to next context item */ const jerry_context_data_manager_t *manager_p; /**< manager responsible for deleting this item */ } jerry_context_data_header_t; #define JERRY_CONTEXT_DATA_HEADER_USER_DATA(item_p) ((uint8_t *) (item_p + 1)) /** * JerryScript context * * The purpose of this header is storing * all global variables for Jerry */ struct jerry_context_t { /* The value of external context members must be preserved across initializations and cleanups. */ #if JERRY_EXTERNAL_CONTEXT #if !JERRY_SYSTEM_ALLOCATOR jmem_heap_t *heap_p; /**< point to the heap aligned to JMEM_ALIGNMENT. */ uint32_t heap_size; /**< size of the heap */ #endif /* !JERRY_SYSTEM_ALLOCATOR */ #endif /* JERRY_EXTERNAL_CONTEXT */ ecma_global_object_t *global_object_p; /**< current global object */ jmem_heap_free_t *jmem_heap_list_skip_p; /**< improves deallocation performance */ jmem_pools_chunk_t *jmem_free_8_byte_chunk_p; /**< list of free eight byte pool chunks */ #if JERRY_BUILTIN_REGEXP re_compiled_code_t *re_cache[RE_CACHE_SIZE]; /**< regex cache */ #endif /* JERRY_BUILTIN_REGEXP */ const lit_utf8_byte_t *const *lit_magic_string_ex_array; /**< array of external magic strings */ const lit_utf8_size_t *lit_magic_string_ex_sizes; /**< external magic string lengths */ jmem_cpointer_t ecma_gc_objects_cp; /**< List of currently alive objects. */ jmem_cpointer_t string_list_first_cp; /**< first item of the literal string list */ jmem_cpointer_t symbol_list_first_cp; /**< first item of the global symbol list */ jmem_cpointer_t number_list_first_cp; /**< first item of the literal number list */ #if JERRY_BUILTIN_BIGINT jmem_cpointer_t bigint_list_first_cp; /**< first item of the literal bigint list */ #endif /* JERRY_BUILTIN_BIGINT */ jmem_cpointer_t global_symbols_cp[ECMA_BUILTIN_GLOBAL_SYMBOL_COUNT]; /**< global symbols */ #if JERRY_MODULE_SYSTEM ecma_module_t *module_current_p; /**< current module context */ jerry_module_state_changed_cb_t module_state_changed_callback_p; /**< callback which is called after the * state of a module is changed */ void *module_state_changed_callback_user_p; /**< user pointer for module_state_changed_callback_p */ jerry_module_import_meta_cb_t module_import_meta_callback_p; /**< callback which is called when an * import.meta expression of a module * is evaluated the first time */ void *module_import_meta_callback_user_p; /**< user pointer for module_import_meta_callback_p */ jerry_module_import_cb_t module_import_callback_p; /**< callback for dynamic module import */ void *module_import_callback_user_p; /**< user pointer for module_import_callback_p */ #endif /* JERRY_MODULE_SYSTEM */ vm_frame_ctx_t *vm_top_context_p; /**< top (current) interpreter context */ jerry_context_data_header_t *context_data_p; /**< linked list of user-provided context-specific pointers */ jerry_external_string_free_cb_t external_string_free_callback_p; /**< free callback for external strings */ void *error_object_created_callback_user_p; /**< user pointer for error_object_update_callback_p */ jerry_error_object_created_cb_t error_object_created_callback_p; /**< decorator callback for Error objects */ size_t ecma_gc_objects_number; /**< number of currently allocated objects */ size_t ecma_gc_new_objects; /**< number of newly allocated objects since last GC session */ size_t jmem_heap_allocated_size; /**< size of allocated regions */ size_t jmem_heap_limit; /**< current limit of heap usage, that is upon being reached, * causes call of "try give memory back" callbacks */ ecma_value_t error_value; /**< currently thrown error value */ uint32_t lit_magic_string_ex_count; /**< external magic strings count */ uint32_t jerry_init_flags; /**< run-time configuration flags */ uint32_t status_flags; /**< run-time flags (the top 8 bits are used for passing class parsing options) */ #if (JERRY_GC_MARK_LIMIT != 0) uint32_t ecma_gc_mark_recursion_limit; /**< GC mark recursion limit */ #endif /* (JERRY_GC_MARK_LIMIT != 0) */ #if JERRY_PROPERTY_HASHMAP uint8_t ecma_prop_hashmap_alloc_state; /**< property hashmap allocation state: 0-4, * if !0 property hashmap allocation is disabled */ #endif /* JERRY_PROPERTY_HASHMAP */ #if JERRY_BUILTIN_REGEXP uint8_t re_cache_idx; /**< evicted item index when regex cache is full (round-robin) */ #endif /* JERRY_BUILTIN_REGEXP */ ecma_job_queue_item_t *job_queue_head_p; /**< points to the head item of the job queue */ ecma_job_queue_item_t *job_queue_tail_p; /**< points to the tail item of the job queue */ #if JERRY_BUILTIN_TYPEDARRAY uint32_t arraybuffer_compact_allocation_limit; /**< maximum size of compact allocation */ jerry_arraybuffer_allocate_cb_t arraybuffer_allocate_callback; /**< callback for allocating * arraybuffer memory */ jerry_arraybuffer_free_cb_t arraybuffer_free_callback; /**< callback for freeing arraybuffer memory */ void *arraybuffer_allocate_callback_user_p; /**< user pointer passed to arraybuffer_allocate_callback * and arraybuffer_free_callback functions */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_VM_HALT uint32_t vm_exec_stop_frequency; /**< reset value for vm_exec_stop_counter */ uint32_t vm_exec_stop_counter; /**< down counter for reducing the calls of vm_exec_stop_cb */ void *vm_exec_stop_user_p; /**< user pointer for vm_exec_stop_cb */ ecma_vm_exec_stop_callback_t vm_exec_stop_cb; /**< user function which returns whether the * ECMAScript execution should be stopped */ #endif /* JERRY_VM_HALT */ #if JERRY_VM_THROW void *vm_throw_callback_user_p; /**< user pointer for vm_throw_callback_p */ jerry_throw_cb_t vm_throw_callback_p; /**< callback for capturing throws */ #endif /* JERRY_VM_THROW */ #if (JERRY_STACK_LIMIT != 0) uintptr_t stack_base; /**< stack base marker */ #endif /* (JERRY_STACK_LIMIT != 0) */ /* This must be at the end of the context for performance reasons */ #if JERRY_LCACHE /** hash table for caching the last access of properties */ ecma_lcache_hash_entry_t lcache[ECMA_LCACHE_HASH_ROWS_COUNT][ECMA_LCACHE_HASH_ROW_LENGTH]; #endif /* JERRY_LCACHE */ /** * Allowed values and it's meaning: * * NULL (0x0): the current "new.target" is undefined, that is the execution is inside a normal method. * * Any other valid function object pointer: the current "new.target" is valid and it is constructor call. */ ecma_object_t *current_new_target_p; }; #if JERRY_EXTERNAL_CONTEXT /* * This part is for JerryScript which uses external context. */ #define JERRY_CONTEXT_STRUCT (*jerry_port_context_get ()) #define JERRY_CONTEXT(field) (jerry_port_context_get ()->field) #if !JERRY_SYSTEM_ALLOCATOR #define JMEM_HEAP_SIZE (JERRY_CONTEXT (heap_size)) #define JMEM_HEAP_AREA_SIZE (JMEM_HEAP_SIZE - JMEM_ALIGNMENT) struct jmem_heap_t { jmem_heap_free_t first; /**< first node in free region list */ uint8_t area[]; /**< heap area */ }; #define JERRY_HEAP_CONTEXT(field) (JERRY_CONTEXT (heap_p)->field) #endif /* !JERRY_SYSTEM_ALLOCATOR */ #else /* !JERRY_EXTERNAL_CONTEXT */ /* * This part is for JerryScript which uses default context. */ /** * Global context. */ extern jerry_context_t jerry_global_context; /** * Config-independent name for context. */ #define JERRY_CONTEXT_STRUCT (jerry_global_context) /** * Provides a reference to a field in the current context. */ #define JERRY_CONTEXT(field) (jerry_global_context.field) #if !JERRY_SYSTEM_ALLOCATOR /** * Size of heap */ #define JMEM_HEAP_SIZE ((size_t) (CONFIG_MEM_HEAP_SIZE)) /** * Calculate heap area size, leaving space for a pointer to the free list */ #define JMEM_HEAP_AREA_SIZE (JMEM_HEAP_SIZE - JMEM_ALIGNMENT) struct jmem_heap_t { jmem_heap_free_t first; /**< first node in free region list */ uint8_t area[JMEM_HEAP_AREA_SIZE]; /**< heap area */ }; /** * Global heap. */ extern jmem_heap_t jerry_global_heap; /** * Provides a reference to a field of the heap. */ #define JERRY_HEAP_CONTEXT(field) (jerry_global_heap.field) #endif /* !JERRY_SYSTEM_ALLOCATOR */ #endif /* JERRY_EXTERNAL_CONTEXT */ void jcontext_set_exception_flag (bool is_exception); void jcontext_set_abort_flag (bool is_abort); bool jcontext_has_pending_exception (void); bool jcontext_has_pending_abort (void); void jcontext_raise_exception (ecma_value_t error); void jcontext_release_exception (void); ecma_value_t jcontext_take_exception (void); /** * @} */ #endif /* !JCONTEXT_H */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-promise-prototype.inc.h000664 001750 001750 00000002406 15164251010 052334 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers-macro-defines.inc.h" /* Object properties: * (property name, object pointer getter) */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_PROMISE, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v6, 25.4.5.4 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_PROMISE_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) ROUTINE (LIT_MAGIC_STRING_THEN, ECMA_PROMISE_PROTOTYPE_ROUTINE_THEN, 2, 2) ROUTINE (LIT_MAGIC_STRING_CATCH, ECMA_PROMISE_PROTOTYPE_ROUTINE_CATCH, 1, 1) ROUTINE (LIT_MAGIC_STRING_FINALLY, ECMA_PROMISE_PROTOTYPE_ROUTINE_FINALLY, 1, 1) #include "ecma-builtin-helpers-macro-undefs.inc.h" thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-arraybuffer.cpp000664 001750 001750 00000006444 15164251010 050714 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-arraybuffer-object.h" #include "ecma-builtins.h" #include "ecma-dataview-object.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-arraybuffer.inc.h" #define BUILTIN_UNDERSCORED_ID arraybuffer #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup arraybuffer ECMA ArrayBuffer object built-in * @{ */ /** * The ArrayBuffer object's 'isView' routine * * See also: * ES2015 24.1.3.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_arraybuffer_object_is_view (ecma_value_t this_arg, /**< 'this' argument */ ecma_value_t arg) /**< argument 1 */ { JERRY_UNUSED (this_arg); return ecma_make_boolean_value (ecma_is_typedarray (arg)); } /* ecma_builtin_arraybuffer_object_is_view */ /** * Handle calling [[Call]] of built-in ArrayBuffer object * * ES2015 24.1.2 ArrayBuffer is not intended to be called as * a function and will throw an exception when called in * that manner. * * @return ecma value */ ecma_value_t ecma_builtin_arraybuffer_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_ARRAYBUFFER_REQUIRES_NEW); } /* ecma_builtin_arraybuffer_dispatch_call */ /** * Handle calling [[Construct]] of built-in ArrayBuffer object * * @return ecma value */ ecma_value_t ecma_builtin_arraybuffer_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_op_create_arraybuffer_object (arguments_list_p, arguments_list_len); } /* ecma_builtin_arraybuffer_dispatch_construct */ /** * 24.1.3.3 get ArrayBuffer [ @@species ] accessor * * @return ecma_value * returned value must be freed with ecma_free_value */ ecma_value_t ecma_builtin_arraybuffer_species_get (ecma_value_t this_value) /**< This Value */ { return ecma_copy_value (this_value); } /* ecma_builtin_arraybuffer_species_get */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/common/tvgStr.h000664 001750 001750 00000003643 15164251010 032744 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_STR_H_ #define _TVG_STR_H_ #include "tvgCommon.h" namespace tvg { static inline bool equal(const char* a, const char* b) { return !strcmp(a, b) && strlen(a) == strlen(b); } char* concat(const char* a, const char* b); float toFloat(const char *str, char **end); //convert to float char* duplicate(const char *str, size_t n = SIZE_MAX); //copy the string char* append(char* lhs, const char* rhs, size_t n); //append the rhs to the lhs char* dirname(const char* path); //return the full directory name char* filename(const char* path); //return the file name without extension const char* fileext(const char* path); //return the file extension name } #endif //_TVG_STR_H_ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-generator.inc.h000664 001750 001750 00000002342 15164251010 050600 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %Generator% built-in description (GeneratorFunction.prototype) */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* ECMA-262 v6, 25.3.2.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_GENERATOR_FUNCTION, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.3.2.3.2 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v6, 25.3.2.3.3 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_GENERATOR_FUNCTION_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/meson.build000664 001750 001750 00000015625 15164251010 045474 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimatesubdir('typedarray') source_file = [ 'ecma-builtin-aggregateerror-prototype.inc.h', 'ecma-builtin-aggregateerror.inc.h', 'ecma-builtin-array-iterator-prototype.inc.h', 'ecma-builtin-array-prototype-unscopables.inc.h', 'ecma-builtin-array-prototype.inc.h', 'ecma-builtin-array.inc.h', 'ecma-builtin-arraybuffer-prototype.inc.h', 'ecma-builtin-arraybuffer.inc.h', 'ecma-builtin-async-from-sync-iterator-prototype.inc.h', 'ecma-builtin-async-function-prototype.inc.h', 'ecma-builtin-async-function.inc.h', 'ecma-builtin-async-generator-function.inc.h', 'ecma-builtin-async-generator-prototype.inc.h', 'ecma-builtin-async-generator.inc.h', 'ecma-builtin-async-iterator-prototype.inc.h', 'ecma-builtin-atomics.inc.h', 'ecma-builtin-bigint-prototype.inc.h', 'ecma-builtin-bigint.inc.h', 'ecma-builtin-boolean-prototype.inc.h', 'ecma-builtin-boolean.inc.h', 'ecma-builtin-dataview-prototype.inc.h', 'ecma-builtin-dataview.inc.h', 'ecma-builtin-date-prototype.inc.h', 'ecma-builtin-date.inc.h', 'ecma-builtin-error-prototype.inc.h', 'ecma-builtin-error.inc.h', 'ecma-builtin-evalerror-prototype.inc.h', 'ecma-builtin-evalerror.inc.h', 'ecma-builtin-function-prototype.h', 'ecma-builtin-function-prototype.inc.h', 'ecma-builtin-function.inc.h', 'ecma-builtin-generator-function.inc.h', 'ecma-builtin-generator-prototype.inc.h', 'ecma-builtin-generator.inc.h', 'ecma-builtin-global.inc.h', 'ecma-builtin-handlers.h', 'ecma-builtin-handlers.inc.h', 'ecma-builtin-helpers-macro-defines.inc.h', 'ecma-builtin-helpers-macro-undefs.inc.h', 'ecma-builtin-helpers.h', 'ecma-builtin-internal-routines-template.inc.h', 'ecma-builtin-intrinsic.inc.h', 'ecma-builtin-iterator-prototype.inc.h', 'ecma-builtin-map-iterator-prototype.inc.h', 'ecma-builtin-map-prototype.inc.h', 'ecma-builtin-map.inc.h', 'ecma-builtin-math.inc.h', 'ecma-builtin-number-prototype.inc.h', 'ecma-builtin-number.inc.h', 'ecma-builtin-object-prototype.inc.h', 'ecma-builtin-object.h', 'ecma-builtin-object.inc.h', 'ecma-builtin-promise-prototype.inc.h', 'ecma-builtin-promise.inc.h', 'ecma-builtin-proxy.inc.h', 'ecma-builtin-rangeerror-prototype.inc.h', 'ecma-builtin-rangeerror.inc.h', 'ecma-builtin-referenceerror-prototype.inc.h', 'ecma-builtin-referenceerror.inc.h', 'ecma-builtin-reflect.inc.h', 'ecma-builtin-regexp-prototype.inc.h', 'ecma-builtin-regexp-string-iterator-prototype.inc.h', 'ecma-builtin-regexp.inc.h', 'ecma-builtin-set-iterator-prototype.inc.h', 'ecma-builtin-set-prototype.inc.h', 'ecma-builtin-set.inc.h', 'ecma-builtin-shared-arraybuffer-prototype.inc.h', 'ecma-builtin-shared-arraybuffer.inc.h', 'ecma-builtin-string-iterator-prototype.inc.h', 'ecma-builtin-string-prototype.inc.h', 'ecma-builtin-string.inc.h', 'ecma-builtin-symbol-prototype.inc.h', 'ecma-builtin-symbol.inc.h', 'ecma-builtin-syntaxerror-prototype.inc.h', 'ecma-builtin-syntaxerror.inc.h', 'ecma-builtin-type-error-thrower.inc.h', 'ecma-builtin-typeerror-prototype.inc.h', 'ecma-builtin-typeerror.inc.h', 'ecma-builtin-urierror-prototype.inc.h', 'ecma-builtin-urierror.inc.h', 'ecma-builtin-weakmap-prototype.inc.h', 'ecma-builtin-weakmap.inc.h', 'ecma-builtin-weakref-prototype.inc.h', 'ecma-builtin-weakref.inc.h', 'ecma-builtin-weakset-prototype.inc.h', 'ecma-builtin-weakset.inc.h', 'ecma-builtins-internal.h', 'ecma-builtins.h', 'ecma-builtins.inc.h', 'ecma-builtin-aggregateerror-prototype.cpp', 'ecma-builtin-aggregateerror.cpp', 'ecma-builtin-array-iterator-prototype.cpp', 'ecma-builtin-array-prototype-unscopables.cpp', 'ecma-builtin-array-prototype.cpp', 'ecma-builtin-array.cpp', 'ecma-builtin-arraybuffer-prototype.cpp', 'ecma-builtin-arraybuffer.cpp', 'ecma-builtin-async-from-sync-iterator-prototype.cpp', 'ecma-builtin-async-function-prototype.cpp', 'ecma-builtin-async-function.cpp', 'ecma-builtin-async-generator-function.cpp', 'ecma-builtin-async-generator-prototype.cpp', 'ecma-builtin-async-generator.cpp', 'ecma-builtin-async-iterator-prototype.cpp', 'ecma-builtin-atomics.cpp', 'ecma-builtin-bigint-prototype.cpp', 'ecma-builtin-bigint.cpp', 'ecma-builtin-boolean-prototype.cpp', 'ecma-builtin-boolean.cpp', 'ecma-builtin-dataview-prototype.cpp', 'ecma-builtin-dataview.cpp', 'ecma-builtin-date-prototype.cpp', 'ecma-builtin-date.cpp', 'ecma-builtin-error-prototype.cpp', 'ecma-builtin-error.cpp', 'ecma-builtin-evalerror-prototype.cpp', 'ecma-builtin-evalerror.cpp', 'ecma-builtin-function-prototype.cpp', 'ecma-builtin-function.cpp', 'ecma-builtin-generator-function.cpp', 'ecma-builtin-generator-prototype.cpp', 'ecma-builtin-generator.cpp', 'ecma-builtin-global.cpp', 'ecma-builtin-handlers.cpp', 'ecma-builtin-helpers-date.cpp', 'ecma-builtin-helpers-error.cpp', 'ecma-builtin-helpers-sort.cpp', 'ecma-builtin-helpers.cpp', 'ecma-builtin-intrinsic.cpp', 'ecma-builtin-iterator-prototype.cpp', 'ecma-builtin-map-iterator-prototype.cpp', 'ecma-builtin-map-prototype.cpp', 'ecma-builtin-map.cpp', 'ecma-builtin-math.cpp', 'ecma-builtin-number-prototype.cpp', 'ecma-builtin-number.cpp', 'ecma-builtin-object-prototype.cpp', 'ecma-builtin-object.cpp', 'ecma-builtin-promise-prototype.cpp', 'ecma-builtin-promise.cpp', 'ecma-builtin-proxy.cpp', 'ecma-builtin-rangeerror-prototype.cpp', 'ecma-builtin-rangeerror.cpp', 'ecma-builtin-referenceerror-prototype.cpp', 'ecma-builtin-referenceerror.cpp', 'ecma-builtin-reflect.cpp', 'ecma-builtin-regexp-prototype.cpp', 'ecma-builtin-regexp-string-iterator-prototype.cpp', 'ecma-builtin-regexp.cpp', 'ecma-builtin-set-iterator-prototype.cpp', 'ecma-builtin-set-prototype.cpp', 'ecma-builtin-set.cpp', 'ecma-builtin-shared-arraybuffer-prototype.cpp', 'ecma-builtin-shared-arraybuffer.cpp', 'ecma-builtin-string-iterator-prototype.cpp', 'ecma-builtin-string-prototype.cpp', 'ecma-builtin-string.cpp', 'ecma-builtin-symbol-prototype.cpp', 'ecma-builtin-symbol.cpp', 'ecma-builtin-syntaxerror-prototype.cpp', 'ecma-builtin-syntaxerror.cpp', 'ecma-builtin-type-error-thrower.cpp', 'ecma-builtin-typeerror-prototype.cpp', 'ecma-builtin-typeerror.cpp', 'ecma-builtin-urierror-prototype.cpp', 'ecma-builtin-urierror.cpp', 'ecma-builtin-weakmap-prototype.cpp', 'ecma-builtin-weakmap.cpp', 'ecma-builtin-weakref-prototype.cpp', 'ecma-builtin-weakref.cpp', 'ecma-builtin-weakset-prototype.cpp', 'ecma-builtin-weakset.cpp', 'ecma-builtins.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgBindGroups.cpp000664 001750 001750 00000027331 15164251010 037302 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgWgBindGroups.h" #include WGPUBindGroup WgBindGroupLayouts::createBindGroupTexSampled(WGPUSampler sampler, WGPUTextureView texView) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .sampler = sampler }, { .binding = 1, .textureView = texView } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutTexSampled, .entryCount = 2, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupTexSampledBuff1Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .sampler = sampler }, { .binding = 1, .textureView = texView }, { .binding = 2, .buffer = buff, .size = wgpuBufferGetSize(buff) } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutTexSampledBuff1Un, .entryCount = 3, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupTexSampledBuff2Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff0, WGPUBuffer buff1) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .sampler = sampler }, { .binding = 1, .textureView = texView }, { .binding = 2, .buffer = buff0, .size = wgpuBufferGetSize(buff0) }, { .binding = 3, .buffer = buff1, .size = wgpuBufferGetSize(buff1) } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutTexSampledBuff2Un, .entryCount = 4, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupStrorage1WO(WGPUTextureView texView) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .textureView = texView } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutTexStrorage1WO, .entryCount = 1, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupStrorage1RO(WGPUTextureView texView) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .textureView = texView } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutTexStrorage1RO, .entryCount = 1, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupStrorage2RO(WGPUTextureView texView0, WGPUTextureView texView1) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .textureView = texView0 }, { .binding = 1, .textureView = texView1 } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutTexStrorage2RO, .entryCount = 2, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupStrorage3RO(WGPUTextureView texView0, WGPUTextureView texView1, WGPUTextureView texView2) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .textureView = texView0 }, { .binding = 1, .textureView = texView1 }, { .binding = 2, .textureView = texView2 } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutTexStrorage3RO, .entryCount = 3, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer1Un(WGPUBuffer buff) { return createBindGroupBuffer1Un(buff, 0, wgpuBufferGetSize(buff)); } WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer1Un(WGPUBuffer buff, uint64_t offset, uint64_t size) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .buffer = buff, .offset = offset, .size = size } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutBuffer1Un, .entryCount = 1, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer2Un(WGPUBuffer buff0, WGPUBuffer buff1) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .buffer = buff0, .size = wgpuBufferGetSize(buff0) }, { .binding = 1, .buffer = buff1, .size = wgpuBufferGetSize(buff1) } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutBuffer2Un, .entryCount = 2, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2) { const WGPUBindGroupEntry bindGroupEntrys[] = { { .binding = 0, .buffer = buff0, .size = wgpuBufferGetSize(buff0) }, { .binding = 1, .buffer = buff1, .size = wgpuBufferGetSize(buff1) }, { .binding = 2, .buffer = buff2, .size = wgpuBufferGetSize(buff2) } }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutBuffer3Un, .entryCount = 3, .entries = bindGroupEntrys }; return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); } void WgBindGroupLayouts::releaseBindGroup(WGPUBindGroup& bindGroup) { if (bindGroup) wgpuBindGroupRelease(bindGroup); bindGroup = nullptr; } void WgBindGroupLayouts::releaseBindGroupLayout(WGPUBindGroupLayout& bindGroupLayout) { if (bindGroupLayout) wgpuBindGroupLayoutRelease(bindGroupLayout); bindGroupLayout = nullptr; } void WgBindGroupLayouts::initialize(WGPUDevice device) { // store device handle assert(device); this->device = device; // common bind group settings const WGPUShaderStage visibility_vert = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment | WGPUShaderStage_Compute; const WGPUShaderStage visibility_frag = WGPUShaderStage_Fragment | WGPUShaderStage_Compute; const WGPUSamplerBindingLayout sampler = { .type = WGPUSamplerBindingType_Filtering }; const WGPUTextureBindingLayout texture = { .sampleType = WGPUTextureSampleType_Float, .viewDimension = WGPUTextureViewDimension_2D }; const WGPUStorageTextureBindingLayout storageTextureWO { .access = WGPUStorageTextureAccess_WriteOnly, .format = WGPUTextureFormat_RGBA8Unorm, .viewDimension = WGPUTextureViewDimension_2D }; const WGPUBufferBindingLayout bufferUniform { .type = WGPUBufferBindingType_Uniform }; // bind group layout tex sampled with buffer uniforms const WGPUBindGroupLayoutEntry entriesTexSampledBufferUniforms[] { { .binding = 0, .visibility = visibility_frag, .sampler = sampler }, { .binding = 1, .visibility = visibility_frag, .texture = texture }, { .binding = 2, .visibility = visibility_vert, .buffer = bufferUniform }, { .binding = 3, .visibility = visibility_vert, .buffer = bufferUniform } }; const WGPUBindGroupLayoutDescriptor layoutDescTexSambled { .entryCount = 2, .entries = entriesTexSampledBufferUniforms }; const WGPUBindGroupLayoutDescriptor layoutDescTexSampledBuff1Un { .entryCount = 3, .entries = entriesTexSampledBufferUniforms }; const WGPUBindGroupLayoutDescriptor layoutDescTexSampledBuff2Un { .entryCount = 4, .entries = entriesTexSampledBufferUniforms }; layoutTexSampled = wgpuDeviceCreateBindGroupLayout(device, &layoutDescTexSambled); layoutTexSampledBuff1Un = wgpuDeviceCreateBindGroupLayout(device, &layoutDescTexSampledBuff1Un); layoutTexSampledBuff2Un = wgpuDeviceCreateBindGroupLayout(device, &layoutDescTexSampledBuff2Un); assert(layoutTexSampled); assert(layoutTexSampledBuff1Un); assert(layoutTexSampledBuff2Un); // bind group layout tex storages WO const WGPUBindGroupLayoutEntry entriesTexStoragesWO[] { { .binding = 0, .visibility = visibility_frag, .storageTexture = storageTextureWO } }; const WGPUBindGroupLayoutDescriptor layoutDescTexStrorage1WO { .entryCount = 1, .entries = entriesTexStoragesWO }; layoutTexStrorage1WO = wgpuDeviceCreateBindGroupLayout(device, &layoutDescTexStrorage1WO); assert(layoutTexStrorage1WO); // bind group layout tex storages RO const WGPUBindGroupLayoutEntry entriesTexStoragesRO[] { { .binding = 0, .visibility = visibility_frag, .texture = texture }, { .binding = 1, .visibility = visibility_frag, .texture = texture }, { .binding = 2, .visibility = visibility_frag, .texture = texture } }; const WGPUBindGroupLayoutDescriptor layoutDescTexStorages1RO { .entryCount = 1, .entries = entriesTexStoragesRO }; const WGPUBindGroupLayoutDescriptor layoutDescTexStorages2RO { .entryCount = 2, .entries = entriesTexStoragesRO }; const WGPUBindGroupLayoutDescriptor layoutDescTexStorages3RO { .entryCount = 3, .entries = entriesTexStoragesRO }; layoutTexStrorage1RO = wgpuDeviceCreateBindGroupLayout(device, &layoutDescTexStorages1RO); layoutTexStrorage2RO = wgpuDeviceCreateBindGroupLayout(device, &layoutDescTexStorages2RO); layoutTexStrorage3RO = wgpuDeviceCreateBindGroupLayout(device, &layoutDescTexStorages3RO); assert(layoutTexStrorage1RO); assert(layoutTexStrorage2RO); assert(layoutTexStrorage3RO); // bind group layout buffer uniforms const WGPUBindGroupLayoutEntry entriesBufferUniform[] { { .binding = 0, .visibility = visibility_vert, .buffer = bufferUniform }, { .binding = 1, .visibility = visibility_vert, .buffer = bufferUniform }, { .binding = 2, .visibility = visibility_vert, .buffer = bufferUniform } }; const WGPUBindGroupLayoutDescriptor layoutDescBufferUniforms1Un { .entryCount = 1, .entries = entriesBufferUniform }; const WGPUBindGroupLayoutDescriptor layoutDescBufferUniforms2Un { .entryCount = 2, .entries = entriesBufferUniform }; const WGPUBindGroupLayoutDescriptor layoutDescBufferUniforms3Un { .entryCount = 3, .entries = entriesBufferUniform }; layoutBuffer1Un = wgpuDeviceCreateBindGroupLayout(device, &layoutDescBufferUniforms1Un); layoutBuffer2Un = wgpuDeviceCreateBindGroupLayout(device, &layoutDescBufferUniforms2Un); layoutBuffer3Un = wgpuDeviceCreateBindGroupLayout(device, &layoutDescBufferUniforms3Un); assert(layoutBuffer1Un); assert(layoutBuffer2Un); assert(layoutBuffer3Un); } void WgBindGroupLayouts::release() { releaseBindGroupLayout(layoutBuffer3Un); releaseBindGroupLayout(layoutBuffer2Un); releaseBindGroupLayout(layoutBuffer1Un); releaseBindGroupLayout(layoutTexStrorage3RO); releaseBindGroupLayout(layoutTexStrorage2RO); releaseBindGroupLayout(layoutTexStrorage1RO); releaseBindGroupLayout(layoutTexStrorage1WO); releaseBindGroupLayout(layoutTexSampledBuff1Un); releaseBindGroupLayout(layoutTexSampledBuff2Un); releaseBindGroupLayout(layoutTexSampled); device = nullptr; } thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.cpp000664 001750 001750 00000004377 15164251010 047663 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-bigint.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #if JERRY_BUILTIN_BIGINT #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-bigint.inc.h" #define BUILTIN_UNDERSCORED_ID bigint #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup bigint ECMA BigInt object built-in * @{ */ /** * Handle calling [[Call]] of built-in BigInt object * * See also: * ECMA-262 v11, 20.2.1.1 * * @return ecma value */ ecma_value_t ecma_builtin_bigint_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_value_t value = (arguments_list_len == 0) ? ECMA_VALUE_UNDEFINED : arguments_list_p[0]; return ecma_bigint_to_bigint (value, true); } /* ecma_builtin_bigint_dispatch_call */ /** * Handle calling [[Construct]] of built-in BigInt object * * See also: * ECMA-262 v11, 20.2.1 * * @return ecma value */ ecma_value_t ecma_builtin_bigint_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_BIGINT_FUNCTION_NOT_CONSTRUCTOR); } /* ecma_builtin_bigint_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_BIGINT */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/000775 001750 001750 00000000000 15165022620 025710 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/yuv.h000664 001750 001750 00000026120 15164251010 034155 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // inline YUV<->RGB conversion function // // The exact naming is Y'CbCr, following the ITU-R BT.601 standard. // More information at: http://en.wikipedia.org/wiki/YCbCr // Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16 // U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128 // V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128 // We use 16bit fixed point operations for RGB->YUV conversion (YUV_FIX). // // For the Y'CbCr to RGB conversion, the BT.601 specification reads: // R = 1.164 * (Y-16) + 1.596 * (V-128) // G = 1.164 * (Y-16) - 0.813 * (V-128) - 0.391 * (U-128) // B = 1.164 * (Y-16) + 2.018 * (U-128) // where Y is in the [16,235] range, and U/V in the [16,240] range. // In the table-lookup version (WEBP_YUV_USE_TABLE), the common factor // "1.164 * (Y-16)" can be handled as an offset in the VP8kClip[] table. // So in this case the formulae should read: // R = 1.164 * [Y + 1.371 * (V-128) ] - 18.624 // G = 1.164 * [Y - 0.698 * (V-128) - 0.336 * (U-128)] - 18.624 // B = 1.164 * [Y + 1.733 * (U-128)] - 18.624 // once factorized. // For YUV->RGB conversion, only 14bit fixed precision is used (YUV_FIX2). // That's the maximum possible for a convenient ARM implementation. // // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_DSP_YUV_H_ #define WEBP_DSP_YUV_H_ #include "./dsp.h" #include "../dec/decode_vp8.h" // Define the following to use the LUT-based code: // #define WEBP_YUV_USE_TABLE #if defined(WEBP_EXPERIMENTAL_FEATURES) // Do NOT activate this feature for real compression. This is only experimental! // This flag is for comparison purpose against JPEG's "YUVj" natural colorspace. // This colorspace is close to Rec.601's Y'CbCr model with the notable // difference of allowing larger range for luma/chroma. // See http://en.wikipedia.org/wiki/YCbCr#JPEG_conversion paragraph, and its // difference with http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion // #define USE_YUVj #endif //------------------------------------------------------------------------------ // YUV -> RGB conversion #ifdef __cplusplus extern "C" { #endif enum { YUV_FIX = 16, // fixed-point precision for RGB->YUV YUV_HALF = 1 << (YUV_FIX - 1), YUV_MASK = (256 << YUV_FIX) - 1, YUV_RANGE_MIN = -227, // min value of r/g/b output YUV_RANGE_MAX = 256 + 226, // max value of r/g/b output YUV_FIX2 = 14, // fixed-point precision for YUV->RGB YUV_HALF2 = 1 << (YUV_FIX2 - 1), YUV_MASK2 = (256 << YUV_FIX2) - 1 }; // These constants are 14b fixed-point version of ITU-R BT.601 constants. #define kYScale 19077 // 1.164 = 255 / 219 #define kVToR 26149 // 1.596 = 255 / 112 * 0.701 #define kUToG 6419 // 0.391 = 255 / 112 * 0.886 * 0.114 / 0.587 #define kVToG 13320 // 0.813 = 255 / 112 * 0.701 * 0.299 / 0.587 #define kUToB 33050 // 2.018 = 255 / 112 * 0.886 #define kRCst (-kYScale * 16 - kVToR * 128 + YUV_HALF2) #define kGCst (-kYScale * 16 + kUToG * 128 + kVToG * 128 + YUV_HALF2) #define kBCst (-kYScale * 16 - kUToB * 128 + YUV_HALF2) //------------------------------------------------------------------------------ #if !defined(WEBP_YUV_USE_TABLE) // slower on x86 by ~7-8%, but bit-exact with the SSE2 version static WEBP_INLINE int VP8Clip8(int v) { return ((v & ~YUV_MASK2) == 0) ? (v >> YUV_FIX2) : (v < 0) ? 0 : 255; } static WEBP_INLINE int VP8YUVToR(int y, int v) { return VP8Clip8(kYScale * y + kVToR * v + kRCst); } static WEBP_INLINE int VP8YUVToG(int y, int u, int v) { return VP8Clip8(kYScale * y - kUToG * u - kVToG * v + kGCst); } static WEBP_INLINE int VP8YUVToB(int y, int u) { return VP8Clip8(kYScale * y + kUToB * u + kBCst); } static WEBP_INLINE void VP8YuvToRgb(int y, int u, int v, uint8_t* const rgb) { rgb[0] = VP8YUVToR(y, v); rgb[1] = VP8YUVToG(y, u, v); rgb[2] = VP8YUVToB(y, u); } static WEBP_INLINE void VP8YuvToBgr(int y, int u, int v, uint8_t* const bgr) { bgr[0] = VP8YUVToB(y, u); bgr[1] = VP8YUVToG(y, u, v); bgr[2] = VP8YUVToR(y, v); } static WEBP_INLINE void VP8YuvToRgb565(int y, int u, int v, uint8_t* const rgb) { const int r = VP8YUVToR(y, v); // 5 usable bits const int g = VP8YUVToG(y, u, v); // 6 usable bits const int b = VP8YUVToB(y, u); // 5 usable bits const int rg = (r & 0xf8) | (g >> 5); const int gb = ((g << 3) & 0xe0) | (b >> 3); #ifdef WEBP_SWAP_16BIT_CSP rgb[0] = gb; rgb[1] = rg; #else rgb[0] = rg; rgb[1] = gb; #endif } static WEBP_INLINE void VP8YuvToRgba4444(int y, int u, int v, uint8_t* const argb) { const int r = VP8YUVToR(y, v); // 4 usable bits const int g = VP8YUVToG(y, u, v); // 4 usable bits const int b = VP8YUVToB(y, u); // 4 usable bits const int rg = (r & 0xf0) | (g >> 4); const int ba = (b & 0xf0) | 0x0f; // overwrite the lower 4 bits #ifdef WEBP_SWAP_16BIT_CSP argb[0] = ba; argb[1] = rg; #else argb[0] = rg; argb[1] = ba; #endif } #else // Table-based version, not totally equivalent to the SSE2 version. // Rounding diff is only +/-1 though. extern int16_t VP8kVToR[256], VP8kUToB[256]; extern int32_t VP8kVToG[256], VP8kUToG[256]; extern uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN]; extern uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN]; static WEBP_INLINE void VP8YuvToRgb(int y, int u, int v, uint8_t* const rgb) { const int r_off = VP8kVToR[v]; const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; const int b_off = VP8kUToB[u]; rgb[0] = VP8kClip[y + r_off - YUV_RANGE_MIN]; rgb[1] = VP8kClip[y + g_off - YUV_RANGE_MIN]; rgb[2] = VP8kClip[y + b_off - YUV_RANGE_MIN]; } static WEBP_INLINE void VP8YuvToBgr(int y, int u, int v, uint8_t* const bgr) { const int r_off = VP8kVToR[v]; const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; const int b_off = VP8kUToB[u]; bgr[0] = VP8kClip[y + b_off - YUV_RANGE_MIN]; bgr[1] = VP8kClip[y + g_off - YUV_RANGE_MIN]; bgr[2] = VP8kClip[y + r_off - YUV_RANGE_MIN]; } static WEBP_INLINE void VP8YuvToRgb565(int y, int u, int v, uint8_t* const rgb) { const int r_off = VP8kVToR[v]; const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; const int b_off = VP8kUToB[u]; const int rg = ((VP8kClip[y + r_off - YUV_RANGE_MIN] & 0xf8) | (VP8kClip[y + g_off - YUV_RANGE_MIN] >> 5)); const int gb = (((VP8kClip[y + g_off - YUV_RANGE_MIN] << 3) & 0xe0) | (VP8kClip[y + b_off - YUV_RANGE_MIN] >> 3)); #ifdef WEBP_SWAP_16BIT_CSP rgb[0] = gb; rgb[1] = rg; #else rgb[0] = rg; rgb[1] = gb; #endif } static WEBP_INLINE void VP8YuvToRgba4444(int y, int u, int v, uint8_t* const argb) { const int r_off = VP8kVToR[v]; const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; const int b_off = VP8kUToB[u]; const int rg = ((VP8kClip4Bits[y + r_off - YUV_RANGE_MIN] << 4) | VP8kClip4Bits[y + g_off - YUV_RANGE_MIN]); const int ba = (VP8kClip4Bits[y + b_off - YUV_RANGE_MIN] << 4) | 0x0f; #ifdef WEBP_SWAP_16BIT_CSP argb[0] = ba; argb[1] = rg; #else argb[0] = rg; argb[1] = ba; #endif } #endif // WEBP_YUV_USE_TABLE //----------------------------------------------------------------------------- // Alpha handling variants static WEBP_INLINE void VP8YuvToArgb(uint8_t y, uint8_t u, uint8_t v, uint8_t* const argb) { argb[0] = 0xff; VP8YuvToRgb(y, u, v, argb + 1); } static WEBP_INLINE void VP8YuvToBgra(uint8_t y, uint8_t u, uint8_t v, uint8_t* const bgra) { VP8YuvToBgr(y, u, v, bgra); bgra[3] = 0xff; } static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v, uint8_t* const rgba) { VP8YuvToRgb(y, u, v, rgba); rgba[3] = 0xff; } // Must be called before everything, to initialize the tables. void VP8YUVInit(void); //----------------------------------------------------------------------------- // SSE2 extra functions (mostly for upsampling_sse2.c) #if defined(WEBP_USE_SSE2) // When the following is defined, tables are initialized statically, adding ~12k // to the binary size. Otherwise, they are initialized at run-time (small cost). #define WEBP_YUV_USE_SSE2_TABLES #if defined(FANCY_UPSAMPLING) // Process 32 pixels and store the result (24b or 32b per pixel) in *dst. void VP8YuvToRgba32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst); void VP8YuvToRgb32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst); void VP8YuvToBgra32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst); void VP8YuvToBgr32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst); #endif // FANCY_UPSAMPLING // Must be called to initialize tables before using the functions. void VP8YUVInitSSE2(void); #endif // WEBP_USE_SSE2 //------------------------------------------------------------------------------ // RGB -> YUV conversion // Stub functions that can be called with various rounding values: static WEBP_INLINE int VP8ClipUV(int uv, int rounding) { uv = (uv + rounding + (128 << (YUV_FIX + 2))) >> (YUV_FIX + 2); return ((uv & ~0xff) == 0) ? uv : (uv < 0) ? 0 : 255; } #ifndef USE_YUVj static WEBP_INLINE int VP8RGBToY(int r, int g, int b, int rounding) { const int luma = 16839 * r + 33059 * g + 6420 * b; return (luma + rounding + (16 << YUV_FIX)) >> YUV_FIX; // no need to clip } static WEBP_INLINE int VP8RGBToU(int r, int g, int b, int rounding) { const int u = -9719 * r - 19081 * g + 28800 * b; return VP8ClipUV(u, rounding); } static WEBP_INLINE int VP8RGBToV(int r, int g, int b, int rounding) { const int v = +28800 * r - 24116 * g - 4684 * b; return VP8ClipUV(v, rounding); } #else // This JPEG-YUV colorspace, only for comparison! // These are also 16bit precision coefficients from Rec.601, but with full // [0..255] output range. static WEBP_INLINE int VP8RGBToY(int r, int g, int b, int rounding) { const int luma = 19595 * r + 38470 * g + 7471 * b; return (luma + rounding) >> YUV_FIX; // no need to clip } static WEBP_INLINE int VP8RGBToU(int r, int g, int b, int rounding) { const int u = -11058 * r - 21710 * g + 32768 * b; return VP8ClipUV(u, rounding); } static WEBP_INLINE int VP8RGBToV(int r, int g, int b, int rounding) { const int v = 32768 * r - 27439 * g - 5329 * b; return VP8ClipUV(v, rounding); } #endif // USE_YUVj #ifdef __cplusplus } // extern "C" #endif #endif /* WEBP_DSP_YUV_H_ */ loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-defines.inc.h000664 001750 001750 00000004222 15164251010 052765 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 SIMPLE_VALUE #define SIMPLE_VALUE(name, simple_value, prop_attributes) #endif /* !SIMPLE_VALUE */ #ifndef NUMBER_VALUE #define NUMBER_VALUE(name, number_value, prop_attributes) #endif /* !NUMBER_VALUE */ #ifndef STRING_VALUE #define STRING_VALUE(name, magic_string_id, prop_attributes) #endif /* !STRING_VALUE */ #ifndef SYMBOL_VALUE #define SYMBOL_VALUE(symbol, desc_magic_string_id) #endif /* !SYMBOL_VALUE */ #ifndef INTRINSIC_PROPERTY #define INTRINSIC_PROPERTY(name, magic_string_id, prop_attributes) #endif /* !INTRINSIC_PROPERTY */ #ifndef ACCESSOR_BUILTIN_FUNCTION_OBJECT #define ACCESSOR_BUILTIN_FUNCTION_OBJECT(name, getter_builtin_id, setter_builtin_id, prop_attributes) #endif /* !ACCESSOR_BUILTIN_FUNCTION_OBJECT */ #ifndef OBJECT_VALUE #define OBJECT_VALUE(name, obj_builtin_id, prop_attributes) #endif /* !OBJECT_VALUE */ #ifndef ROUTINE #define ROUTINE(name, c_function_name, args_number, length_prop_value) #endif /* !ROUTINE */ #ifndef ROUTINE_CONFIGURABLE_ONLY #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) #endif /* !ROUTINE_CONFIGURABLE_ONLY */ #ifndef ROUTINE_WITH_FLAGS #define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) #endif /* !ROUTINE_WITH_FLAGS */ #ifndef ACCESSOR_READ_WRITE #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) #endif /* !ACCESSOR_READ_WRITE */ #ifndef ACCESSOR_READ_ONLY #define ACCESSOR_READ_ONLY(name, c_getter_func_name, prop_attributes) #endif /* !ACCESSOR_READ_ONLY */ jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-float32array-prototype.inc.h000664 001750 001750 00000001622 15164251010 055352 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Float32Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 4 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_FLOAT32ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/ieee754.h000664 001750 001750 00000005635 15164251010 040014 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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(((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_ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.cpp000664 001750 001750 00000035626 15164251010 051721 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtin-object.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-proxy-object.h" #include "ecma-string-object.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { /* Note: these 6 routines must be in this order */ ECMA_OBJECT_PROTOTYPE_ROUTINE_START = 0, ECMA_OBJECT_PROTOTYPE_TO_STRING, ECMA_OBJECT_PROTOTYPE_VALUE_OF, ECMA_OBJECT_PROTOTYPE_TO_LOCALE_STRING, ECMA_OBJECT_PROTOTYPE_GET_PROTO, ECMA_OBJECT_PROTOTYPE_IS_PROTOTYPE_OF, ECMA_OBJECT_PROTOTYPE_HAS_OWN_PROPERTY, ECMA_OBJECT_PROTOTYPE_PROPERTY_IS_ENUMERABLE, ECMA_OBJECT_PROTOTYPE_SET_PROTO, #if JERRY_BUILTIN_ANNEXB ECMA_OBJECT_PROTOTYPE_DEFINE_GETTER, ECMA_OBJECT_PROTOTYPE_DEFINE_SETTER, ECMA_OBJECT_PROTOTYPE_LOOKUP_GETTER, ECMA_OBJECT_PROTOTYPE_LOOKUP_SETTER, #endif /* JERRY_BUILTIN_ANNEXB */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-object-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID object_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup objectprototype ECMA Object.prototype object built-in * @{ */ /** * The Object.prototype object's 'toString' routine * * See also: * ECMA-262 v5, 15.2.4.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_prototype_object_to_string (ecma_value_t this_arg) /**< this argument */ { return ecma_builtin_helper_object_to_string (this_arg); } /* ecma_builtin_object_prototype_object_to_string */ /** * The Object.prototype object's 'valueOf' routine * * See also: * ECMA-262 v5, 15.2.4.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_prototype_object_value_of (ecma_value_t this_arg) /**< this argument */ { return ecma_op_to_object (this_arg); } /* ecma_builtin_object_prototype_object_value_of */ /** * The Object.prototype object's 'toLocaleString' routine * * See also: * ECMA-262 v5, 15.2.4.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_prototype_object_to_locale_string (ecma_value_t this_arg) /**< this argument */ { return ecma_op_invoke_by_magic_id (this_arg, LIT_MAGIC_STRING_TO_STRING_UL, &this_arg, 1); } /* ecma_builtin_object_prototype_object_to_locale_string */ /** * The Object.prototype object's 'hasOwnProperty' routine * * See also: * ECMA-262 v5, 15.2.4.5 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_prototype_object_has_own_property (ecma_object_t *obj_p, /**< this argument */ ecma_string_t *prop_name_p) /**< first argument */ { return ecma_op_object_has_own_property (obj_p, prop_name_p); } /* ecma_builtin_object_prototype_object_has_own_property */ /** * The Object.prototype object's 'isPrototypeOf' routine * * See also: * ECMA-262 v5, 15.2.4.6 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_prototype_object_is_prototype_of (ecma_object_t *obj_p, /**< this argument */ ecma_value_t arg) /**< routine's first argument */ { /* 3. Compare prototype to object */ ecma_value_t v_obj_value = ecma_op_to_object (arg); if (ECMA_IS_VALUE_ERROR (v_obj_value)) { return v_obj_value; } ecma_object_t *v_obj_p = ecma_get_object_from_value (v_obj_value); ecma_value_t ret_value = ecma_op_object_is_prototype_of (obj_p, v_obj_p); ecma_deref_object (v_obj_p); return ret_value; } /* ecma_builtin_object_prototype_object_is_prototype_of */ /** * The Object.prototype object's 'propertyIsEnumerable' routine * * See also: * ECMA-262 v5, 15.2.4.7 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_object_prototype_object_property_is_enumerable (ecma_object_t *obj_p, /**< this argument */ ecma_string_t *prop_name_p) /**< first argument */ { ecma_property_descriptor_t prop_desc; ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_p, prop_name_p, &prop_desc); if (!ecma_is_value_true (status)) { return status; } bool is_enumerable = (prop_desc.flags & JERRY_PROP_IS_ENUMERABLE); ecma_free_property_descriptor (&prop_desc); return ecma_make_boolean_value (is_enumerable); } /* ecma_builtin_object_prototype_object_property_is_enumerable */ #if JERRY_BUILTIN_ANNEXB /** * The Object.prototype object's '__defineGetter__' and '__defineSetter__' routine * * See also: * ECMA-262 v11, B.2.2.2 * ECMA-262 v11, B.2.2.3 * * @return ECMA_VALUE_ERROR - if the operation fails, * ECMA_VALUE_UNDEFINED - otherwise */ static ecma_value_t ecma_builtin_object_prototype_define_getter_setter (ecma_value_t this_arg, /**< this argument */ ecma_value_t prop, /**< property */ ecma_value_t accessor, /**< getter/setter function */ bool define_getter) /**< true - defineGetter method false - defineSetter method */ { /* 1. */ ecma_value_t to_obj = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (to_obj)) { return to_obj; } ecma_object_t *obj_p = ecma_get_object_from_value (to_obj); /* 2. */ if (!ecma_op_is_callable (accessor)) { ecma_deref_object (obj_p); return ecma_raise_type_error (ECMA_ERR_GETTER_IS_NOT_CALLABLE); } ecma_object_t *accessor_obj_p = ecma_get_object_from_value (accessor); /* 3. */ ecma_property_descriptor_t desc = ecma_make_empty_property_descriptor (); desc.flags |= (JERRY_PROP_IS_ENUMERABLE | JERRY_PROP_IS_CONFIGURABLE | JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_SHOULD_THROW); if (define_getter) { desc.get_p = accessor_obj_p; desc.flags |= JERRY_PROP_IS_GET_DEFINED; } else { desc.set_p = accessor_obj_p; desc.flags |= JERRY_PROP_IS_SET_DEFINED; } /* 4. */ ecma_string_t *prop_name_p = ecma_op_to_property_key (prop); if (JERRY_UNLIKELY (prop_name_p == NULL)) { ecma_deref_object (obj_p); return ECMA_VALUE_ERROR; } /* 5. */ ecma_value_t define_prop = ecma_op_object_define_own_property (obj_p, prop_name_p, &desc); ecma_deref_object (obj_p); ecma_deref_ecma_string (prop_name_p); if (ECMA_IS_VALUE_ERROR (define_prop)) { return define_prop; } /* 6. */ return ECMA_VALUE_UNDEFINED; } /* ecma_builtin_object_prototype_define_getter_setter */ /** * The Object.prototype object's '__lookupGetter__' and '__lookupSetter__' routine * * See also: * ECMA-262 v11, B.2.2.4 * ECMA-262 v11, B.2.2.5 * * @return ECMA_VALUE_ERROR - if the operation fails, * ECMA_VALUE_UNDEFINED - if the property was not found * Accessor property - otherwise */ static ecma_value_t ecma_builtin_object_prototype_lookup_getter_setter (ecma_value_t this_arg, /**< this argument */ ecma_value_t prop, /**< property */ bool lookup_getter) /**< true - lookupGetter method false - lookupSetter method */ { /* 1. */ ecma_value_t to_obj = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (to_obj)) { return to_obj; } ecma_object_t *obj_p = ecma_get_object_from_value (to_obj); /* 2. */ ecma_string_t *prop_name_p = ecma_op_to_property_key (prop); if (JERRY_UNLIKELY (prop_name_p == NULL)) { ecma_deref_object (obj_p); return ECMA_VALUE_ERROR; } ecma_value_t ret_value = ECMA_VALUE_UNDEFINED; ecma_ref_object (obj_p); /* 3. */ while (true) { /* 3.a */ ecma_property_descriptor_t desc; ecma_value_t get_desc = ecma_op_object_get_own_property_descriptor (obj_p, prop_name_p, &desc); if (ECMA_IS_VALUE_ERROR (get_desc)) { ret_value = get_desc; ecma_deref_object (obj_p); break; } /* 3.b */ if (ecma_is_value_true (get_desc)) { if ((desc.flags & JERRY_PROP_IS_SET_DEFINED) || (desc.flags & JERRY_PROP_IS_GET_DEFINED)) { if (lookup_getter && desc.get_p != NULL) { ecma_ref_object (desc.get_p); ret_value = ecma_make_object_value (desc.get_p); } else if (!lookup_getter && desc.set_p != NULL) { ecma_ref_object (desc.set_p); ret_value = ecma_make_object_value (desc.set_p); } } ecma_free_property_descriptor (&desc); ecma_deref_object (obj_p); break; } /* 3.c */ ecma_object_t *proto_p = ecma_op_object_get_prototype_of (obj_p); ecma_deref_object (obj_p); if (proto_p == NULL) { break; } else if (JERRY_UNLIKELY (proto_p == ECMA_OBJECT_POINTER_ERROR)) { ret_value = ECMA_VALUE_ERROR; break; } /* Advance up on prototype chain. */ obj_p = proto_p; } ecma_free_value (to_obj); ecma_deref_ecma_string (prop_name_p); return ret_value; } /* ecma_builtin_object_prototype_lookup_getter_setter */ #endif /* JERRY_BUILTIN_ANNEXB */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_object_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (arguments_number); /* no specialization */ if (builtin_routine_id <= ECMA_OBJECT_PROTOTYPE_VALUE_OF) { if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_TO_STRING) { return ecma_builtin_object_prototype_object_to_string (this_arg); } JERRY_ASSERT (builtin_routine_id <= ECMA_OBJECT_PROTOTYPE_VALUE_OF); return ecma_builtin_object_prototype_object_value_of (this_arg); } if (builtin_routine_id <= ECMA_OBJECT_PROTOTYPE_IS_PROTOTYPE_OF) { if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_IS_PROTOTYPE_OF) { /* 15.2.4.6.1. */ if (!ecma_is_value_object (arguments_list_p[0])) { return ECMA_VALUE_FALSE; } } if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_TO_LOCALE_STRING) { return ecma_builtin_object_prototype_object_to_locale_string (this_arg); } ecma_value_t to_object = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (to_object)) { return to_object; } ecma_object_t *obj_p = ecma_get_object_from_value (to_object); ecma_value_t ret_value; if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_GET_PROTO) { ret_value = ecma_builtin_object_object_get_prototype_of (obj_p); } else { ret_value = ecma_builtin_object_prototype_object_is_prototype_of (obj_p, arguments_list_p[0]); } ecma_deref_object (obj_p); return ret_value; } JERRY_ASSERT (builtin_routine_id >= ECMA_OBJECT_PROTOTYPE_HAS_OWN_PROPERTY); if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_SET_PROTO) { return ecma_builtin_object_object_set_proto (this_arg, arguments_list_p[0]); } #if JERRY_BUILTIN_ANNEXB else if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_LOOKUP_GETTER) { return ecma_builtin_object_prototype_lookup_getter_setter (this_arg, arguments_list_p[0], true); } else if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_LOOKUP_SETTER) { return ecma_builtin_object_prototype_lookup_getter_setter (this_arg, arguments_list_p[0], false); } else if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_DEFINE_GETTER) { return ecma_builtin_object_prototype_define_getter_setter (this_arg, arguments_list_p[0], arguments_list_p[1], true); } else if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_DEFINE_SETTER) { return ecma_builtin_object_prototype_define_getter_setter (this_arg, arguments_list_p[0], arguments_list_p[1], false); } #endif /* JERRY_BUILTIN_ANNEXB */ ecma_string_t *prop_name_p = ecma_op_to_property_key (arguments_list_p[0]); if (prop_name_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t to_object = ecma_op_to_object (this_arg); if (ECMA_IS_VALUE_ERROR (to_object)) { ecma_deref_ecma_string (prop_name_p); return to_object; } ecma_object_t *obj_p = ecma_get_object_from_value (to_object); ecma_value_t ret_value; if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_HAS_OWN_PROPERTY) { ret_value = ecma_builtin_object_prototype_object_has_own_property (obj_p, prop_name_p); } else { ret_value = ecma_builtin_object_prototype_object_property_is_enumerable (obj_p, prop_name_p); } ecma_deref_ecma_string (prop_name_p); ecma_deref_object (obj_p); return ret_value; } /* ecma_builtin_object_prototype_dispatch_routine */ /** * @} * @} * @} */ lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-function-prototype.inc.h000664 001750 001750 00000002122 15164251010 053611 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * %AsyncFunctionPrototype% built-in description (AsyncFunction.prototype) */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* ECMA-262 v11, 25.7.3.1 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_ASYNC_FUNCTION, ECMA_PROPERTY_FLAG_CONFIGURABLE) /* ECMA-262 v11, 25.7.3.2 */ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, LIT_MAGIC_STRING_ASYNC_FUNCTION_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/vm-defines.h000664 001750 001750 00000013037 15164251010 042153 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 VM_DEFINES_H #define VM_DEFINES_H #include "ecma-globals.h" #include "byte-code.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup vm_executor Executor * @{ */ /** * Helper for += on uint16_t values. */ #define VM_PLUS_EQUAL_U16(base, value) (base) = (uint16_t) ((base) + (value)) /** * Helper for -= on uint16_t values. */ #define VM_MINUS_EQUAL_U16(base, value) (base) = (uint16_t) ((base) - (value)) /** * Flag bits of vm_frame_ctx_shared_t */ typedef enum { VM_FRAME_CTX_SHARED_HAS_ARG_LIST = (1 << 0), /**< has argument list */ VM_FRAME_CTX_SHARED_DIRECT_EVAL = (1 << 1), /**< direct eval call */ VM_FRAME_CTX_SHARED_FREE_THIS = (1 << 2), /**< free this binding */ VM_FRAME_CTX_SHARED_FREE_LOCAL_ENV = (1 << 3), /**< free local environment */ VM_FRAME_CTX_SHARED_NON_ARROW_FUNC = (1 << 4), /**< non-arrow function */ VM_FRAME_CTX_SHARED_HERITAGE_PRESENT = (1 << 5), /**< class heritage present */ VM_FRAME_CTX_SHARED_HAS_CLASS_FIELDS = (1 << 6), /**< has class fields */ VM_FRAME_CTX_SHARED_EXECUTABLE = (1 << 7), /**< frame is an executable object constructed * with opfunc_create_executable_object */ } vm_frame_ctx_shared_flags_t; /** * Shared data between the interpreter and the caller */ typedef struct { const ecma_compiled_code_t *bytecode_header_p; /**< currently executed byte-code data */ ecma_object_t *function_object_p; /**< function obj */ uint32_t status_flags; /**< combination of vm_frame_ctx_shared_flags_t bits */ } vm_frame_ctx_shared_t; /** * Shared data extended with arguments */ typedef struct { vm_frame_ctx_shared_t header; /**< shared data header */ const ecma_value_t *arg_list_p; /**< arguments list */ uint32_t arg_list_len; /**< arguments list length */ } vm_frame_ctx_shared_args_t; /** * Shared data extended with computed class fields */ typedef struct { vm_frame_ctx_shared_t header; /**< shared data header */ ecma_value_t *computed_class_fields_p; /**< names of the computed class fields */ } vm_frame_ctx_shared_class_fields_t; /** * Get the computed class field */ #define VM_GET_COMPUTED_CLASS_FIELDS(frame_ctx_p) \ (((vm_frame_ctx_shared_class_fields_t *) ((frame_ctx_p)->shared_p))->computed_class_fields_p) /** * Flag bits of vm_frame_ctx_t */ typedef enum { VM_FRAME_CTX_DIRECT_EVAL = (1 << 1), /**< direct eval call */ VM_FRAME_CTX_IS_STRICT = (1 << 2), /**< strict mode */ } vm_frame_ctx_flags_t; /** * Context of interpreter, related to a JS stack frame */ typedef struct vm_frame_ctx_t { vm_frame_ctx_shared_t *shared_p; /**< shared information */ const uint8_t *byte_code_p; /**< current byte code pointer */ const uint8_t *byte_code_start_p; /**< byte code start pointer */ ecma_value_t *stack_top_p; /**< stack top pointer */ ecma_value_t *literal_start_p; /**< literal list start pointer */ ecma_object_t *lex_env_p; /**< current lexical environment */ struct vm_frame_ctx_t *prev_context_p; /**< previous context */ ecma_value_t this_binding; /**< this binding */ uint16_t context_depth; /**< current context depth */ uint8_t status_flags; /**< combination of vm_frame_ctx_flags_t bits */ uint8_t call_operation; /**< perform a call or construct operation */ /* Registers start immediately after the frame context. */ } vm_frame_ctx_t; /** * Get register list corresponding to the frame context. */ #define VM_GET_REGISTERS(frame_ctx_p) ((ecma_value_t *) ((frame_ctx_p) + 1)) /** * Read or write a specific register. */ #define VM_GET_REGISTER(frame_ctx_p, i) (((ecma_value_t *) ((frame_ctx_p) + 1))[i]) /** * Calculate the executable object from a vm_executable_object frame context. */ #define VM_GET_EXECUTABLE_OBJECT(frame_ctx_p) \ ((ecma_extended_object_t *) ((uintptr_t) (frame_ctx_p) - (uintptr_t) offsetof (vm_executable_object_t, frame_ctx))) /** * Calculate the shared_part from a vm_executable_object frame context. */ #define VM_GET_EXECUTABLE_ITERATOR(frame_ctx_p) \ ((ecma_value_t *) ((uintptr_t) (frame_ctx_p) - (uintptr_t) offsetof (vm_executable_object_t, frame_ctx) \ + (uintptr_t) offsetof (vm_executable_object_t, iterator))) /** * Generator frame context. */ typedef struct { ecma_extended_object_t extended_object; /**< extended object part */ vm_frame_ctx_shared_t shared; /**< shared part */ ecma_value_t iterator; /**< executable object's iterator */ vm_frame_ctx_t frame_ctx; /**< frame context part */ } vm_executable_object_t; /** * Real backtrace frame data passed to the jerry_backtrace_cb_t handler. */ struct jerry_frame_internal_t { vm_frame_ctx_t *context_p; /**< context pointer */ uint8_t frame_type; /**< frame type */ jerry_frame_location_t location; /**< location information */ ecma_value_t function; /**< function reference */ ecma_value_t this_binding; /**< this binding passed to the function */ }; /** * @} * @} */ #endif /* !VM_DEFINES_H */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-async-generator-object.cpp000664 001750 001750 00000042542 15164251010 050372 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-async-generator-object.h" #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-errors.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #include "ecma-promise-object.h" #include "jcontext.h" #include "opcodes.h" #include "vm-stack.h" #include "vm.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmaasyncgeneratorobject ECMA AsyncGenerator object related routines * @{ */ /** * Enqueue a task into the command queue of an async generator * * @return ecma Promise value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_async_generator_enqueue (vm_executable_object_t *async_generator_object_p, /**< async generator */ ecma_async_generator_operation_type_t operation, /**< operation */ ecma_value_t value) /**< value argument of operation */ { ecma_async_generator_task_t *task_p = (ecma_async_generator_task_t *) jmem_heap_alloc_block (sizeof (ecma_async_generator_task_t)); ECMA_SET_INTERNAL_VALUE_ANY_POINTER (task_p->next, NULL); task_p->operation_value = ecma_copy_value_if_not_object (value); task_p->operation_type = (uint8_t) operation; ecma_value_t result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_VALUE_UNDEFINED, NULL); task_p->promise = result; ecma_value_t head = async_generator_object_p->extended_object.u.cls.u3.head; if (ECMA_IS_INTERNAL_VALUE_NULL (head)) { ECMA_SET_INTERNAL_VALUE_POINTER (async_generator_object_p->extended_object.u.cls.u3.head, task_p); if (async_generator_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_ASYNC_GENERATOR_CALLED) { ecma_value_t executable_object = ecma_make_object_value ((ecma_object_t *) async_generator_object_p); ecma_enqueue_promise_async_generator_job (executable_object); return result; } async_generator_object_p->extended_object.u.cls.u2.executable_obj_flags |= ECMA_ASYNC_GENERATOR_CALLED; ecma_async_generator_run (async_generator_object_p); return result; } /* Append the new task at the end. */ ecma_async_generator_task_t *prev_task_p; prev_task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, head); while (!ECMA_IS_INTERNAL_VALUE_NULL (prev_task_p->next)) { prev_task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, prev_task_p->next); } ECMA_SET_INTERNAL_VALUE_POINTER (prev_task_p->next, task_p); return result; } /* ecma_async_generator_enqueue */ /** * Call a function and await its return value * * @return ECMA_VALUE_UNDEFINED on success, error otherwise */ static ecma_value_t ecma_async_yield_call (ecma_value_t function, /**< function (takes reference) */ vm_executable_object_t *async_generator_object_p, /**< async generator */ ecma_value_t argument) /**< argument passed to the function */ { ecma_value_t iterator = async_generator_object_p->iterator; ecma_value_t result; if (argument == ECMA_VALUE_EMPTY) { result = ecma_op_function_validated_call (function, iterator, NULL, 0); } else { result = ecma_op_function_validated_call (function, iterator, &argument, 1); } ecma_free_value (function); if (ECMA_IS_VALUE_ERROR (result)) { return result; } return ecma_promise_async_await ((ecma_extended_object_t *) async_generator_object_p, result); } /* ecma_async_yield_call */ /** * Perform an exception throw and call the appropriate handler * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_async_yield_throw (vm_executable_object_t *async_generator_object_p, /**< async generator */ ecma_value_t value) /**< thrown value */ { ecma_object_t *obj_p = ecma_get_object_from_value (async_generator_object_p->iterator); ecma_value_t result = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_THROW); if (ECMA_IS_VALUE_ERROR (result)) { return result; } if (result == ECMA_VALUE_UNDEFINED) { result = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_RETURN); if (result == ECMA_VALUE_UNDEFINED) { return ecma_raise_type_error (ECMA_ERR_ITERATOR_THROW_IS_NOT_AVAILABLE); } result = ecma_async_yield_call (result, async_generator_object_p, ECMA_VALUE_EMPTY); if (ECMA_IS_VALUE_ERROR (result)) { return result; } ECMA_AWAIT_CHANGE_STATE (async_generator_object_p, YIELD_OPERATION, YIELD_CLOSE); return ECMA_VALUE_UNDEFINED; } result = ecma_async_yield_call (result, async_generator_object_p, value); if (ECMA_IS_VALUE_ERROR (result)) { return result; } ECMA_AWAIT_CHANGE_STATE (async_generator_object_p, YIELD_OPERATION, YIELD_NEXT); return ECMA_VALUE_UNDEFINED; } /* ecma_async_yield_throw */ /** * Execute the next task in the command queue of the async generator * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_async_generator_run (vm_executable_object_t *async_generator_object_p) /**< async generator */ { JERRY_ASSERT (async_generator_object_p->extended_object.u.cls.type == ECMA_OBJECT_CLASS_ASYNC_GENERATOR); JERRY_ASSERT (!ECMA_IS_INTERNAL_VALUE_NULL (async_generator_object_p->extended_object.u.cls.u3.head)); ecma_value_t head = async_generator_object_p->extended_object.u.cls.u3.head; ecma_async_generator_task_t *task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, head); ecma_value_t result; if (async_generator_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) { switch (task_p->operation_type) { case ECMA_ASYNC_GENERATOR_DO_NEXT: { result = ecma_op_iterator_next (async_generator_object_p->iterator, async_generator_object_p->frame_ctx.stack_top_p[-1], task_p->operation_value); if (ECMA_IS_VALUE_ERROR (result)) { break; } result = ecma_promise_async_await ((ecma_extended_object_t *) async_generator_object_p, result); if (ECMA_IS_VALUE_ERROR (result)) { break; } ECMA_AWAIT_CHANGE_STATE (async_generator_object_p, YIELD_OPERATION, YIELD_NEXT); break; } case ECMA_ASYNC_GENERATOR_DO_THROW: { result = ecma_async_yield_throw (async_generator_object_p, task_p->operation_value); break; } default: { JERRY_ASSERT (task_p->operation_type == ECMA_ASYNC_GENERATOR_DO_RETURN); result = ecma_copy_value (task_p->operation_value); result = ecma_promise_async_await ((ecma_extended_object_t *) async_generator_object_p, result); if (ECMA_IS_VALUE_ERROR (result)) { break; } ECMA_AWAIT_CHANGE_STATE (async_generator_object_p, YIELD_OPERATION, YIELD_RETURN); break; } } ecma_free_value_if_not_object (task_p->operation_value); task_p->operation_value = ECMA_VALUE_UNDEFINED; if (result == ECMA_VALUE_UNDEFINED) { return ECMA_VALUE_UNDEFINED; } JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result)); async_generator_object_p->extended_object.u.cls.u2.executable_obj_flags &= ECMA_AWAIT_CLEAR_MASK; async_generator_object_p->iterator = ECMA_VALUE_UNDEFINED; async_generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; JERRY_ASSERT (async_generator_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED || ecma_is_value_object (async_generator_object_p->frame_ctx.stack_top_p[-1])); async_generator_object_p->frame_ctx.stack_top_p--; result = jcontext_take_exception (); } else { if (task_p->operation_type == ECMA_ASYNC_GENERATOR_DO_RETURN) { async_generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; } else if (task_p->operation_type == ECMA_ASYNC_GENERATOR_DO_THROW) { async_generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; } result = task_p->operation_value; ecma_ref_if_object (result); task_p->operation_value = ECMA_VALUE_UNDEFINED; } result = opfunc_resume_executable_object (async_generator_object_p, result); if (async_generator_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_COMPLETED) { JERRY_ASSERT (head == async_generator_object_p->extended_object.u.cls.u3.head); ecma_async_generator_finalize (async_generator_object_p, result); result = ECMA_VALUE_UNDEFINED; } return result; } /* ecma_async_generator_run */ /** * Finalize the promises of an executable generator */ void ecma_async_generator_finalize (vm_executable_object_t *async_generator_object_p, /**< async generator */ ecma_value_t value) /**< final value (takes reference) */ { ecma_value_t next = async_generator_object_p->extended_object.u.cls.u3.head; ecma_async_generator_task_t *task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, next); if (ECMA_IS_VALUE_ERROR (value)) { value = jcontext_take_exception (); ecma_reject_promise (task_p->promise, value); } else { ecma_value_t result = ecma_create_iter_result_object (value, ECMA_VALUE_TRUE); ecma_fulfill_promise (task_p->promise, result); ecma_free_value (result); } ecma_free_value (value); next = task_p->next; async_generator_object_p->extended_object.u.cls.u3.head = next; jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); while (!ECMA_IS_INTERNAL_VALUE_NULL (next)) { task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, next); if (task_p->operation_type != ECMA_ASYNC_GENERATOR_DO_THROW) { value = ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); ecma_fulfill_promise (task_p->promise, value); ecma_free_value (value); } else { ecma_reject_promise (task_p->promise, task_p->operation_value); } ecma_free_value_if_not_object (task_p->operation_value); next = task_p->next; async_generator_object_p->extended_object.u.cls.u3.head = next; jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); } } /* ecma_async_generator_finalize */ /** * Continue after an await operation is completed. * * @return an updated value for the value argument */ ecma_value_t ecma_await_continue (vm_executable_object_t *executable_object_p, /**< executable object */ ecma_value_t value) /**< job value (takes reference) */ { ecma_await_states_t state = (ecma_await_states_t) ECMA_AWAIT_GET_STATE (executable_object_p); switch (state) { case ECMA_AWAIT_YIELD_NEXT: case ECMA_AWAIT_YIELD_NEXT_RETURN: { if (!ecma_is_value_object (value)) { ecma_free_value (value); return ecma_raise_type_error (ECMA_ERR_VALUE_RECEIVED_BY_YIELD_IS_NOT_OBJECT); } ecma_object_t *result_obj_p = ecma_get_object_from_value (value); ecma_value_t result = ecma_op_object_get_by_magic_id (result_obj_p, LIT_MAGIC_STRING_DONE); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (result_obj_p); return result; } bool done = ecma_op_to_boolean (result); ecma_free_value (result); result = ecma_op_object_get_by_magic_id (result_obj_p, LIT_MAGIC_STRING_VALUE); ecma_deref_object (result_obj_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } if (!done) { ECMA_AWAIT_SET_STATE (executable_object_p, YIELD_NEXT_VALUE); return ecma_promise_async_await ((ecma_extended_object_t *) executable_object_p, result); } ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); if (state == ECMA_AWAIT_YIELD_NEXT_RETURN) { executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; } return result; } case ECMA_AWAIT_YIELD_RETURN: { ecma_object_t *obj_p = ecma_get_object_from_value (executable_object_p->iterator); ecma_value_t result = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_RETURN); if (ECMA_IS_VALUE_ERROR (result)) { ecma_free_value (value); return result; } if (result == ECMA_VALUE_UNDEFINED) { ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; return value; } result = ecma_async_yield_call (result, executable_object_p, value); ecma_free_value (value); if (ECMA_IS_VALUE_ERROR (result)) { return result; } JERRY_ASSERT (result == ECMA_VALUE_UNDEFINED); ECMA_AWAIT_CHANGE_STATE (executable_object_p, YIELD_RETURN, YIELD_NEXT_RETURN); return ECMA_VALUE_UNDEFINED; } case ECMA_AWAIT_YIELD_NEXT_VALUE: { ECMA_AWAIT_CHANGE_STATE (executable_object_p, YIELD_NEXT_VALUE, YIELD_OPERATION); opfunc_async_generator_yield ((ecma_extended_object_t *) executable_object_p, value); return ECMA_VALUE_UNDEFINED; } case ECMA_AWAIT_YIELD_OPERATION: { /* Currently this is always a throw exception case. */ ecma_value_t result = ecma_async_yield_throw (executable_object_p, value); ecma_free_value (value); return result; } case ECMA_AWAIT_YIELD_CLOSE: { ecma_error_msg_t msg = (ecma_is_value_object (value) ? ECMA_ERR_ITERATOR_THROW_IS_NOT_AVAILABLE : ECMA_ERR_VALUE_RECEIVED_BY_YIELD_IS_NOT_OBJECT); ecma_free_value (value); return ecma_raise_type_error (msg); } case ECMA_AWAIT_FOR_CLOSE: { bool is_value_object = ecma_is_value_object (value); ecma_free_value (value); ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); if (!is_value_object && VM_GET_CONTEXT_TYPE (executable_object_p->frame_ctx.stack_top_p[-1]) != VM_CONTEXT_FINALLY_THROW) { return ecma_raise_type_error (ECMA_ERR_ITERATOR_RETURN_RESULT_IS_NOT_OBJECT); } return ECMA_VALUE_EMPTY; } default: { JERRY_ASSERT (state == ECMA_AWAIT_FOR_NEXT); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (executable_object_p->frame_ctx.stack_top_p[-1]) == VM_CONTEXT_FOR_AWAIT_OF); JERRY_ASSERT (!(executable_object_p->frame_ctx.stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR)); if (!ecma_is_value_object (value)) { ecma_free_value (value); return ecma_raise_type_error (ECMA_ERR_VALUE_RECEIVED_BY_FOR_ASYNC_OF_IS_NOT_OBJECT); } ecma_object_t *result_obj_p = ecma_get_object_from_value (value); ecma_value_t result = ecma_op_object_get_by_magic_id (result_obj_p, LIT_MAGIC_STRING_DONE); if (ECMA_IS_VALUE_ERROR (result)) { ecma_deref_object (result_obj_p); return result; } bool done = ecma_op_to_boolean (result); ecma_free_value (result); ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p; JERRY_ASSERT (stack_top_p[-2] == ECMA_VALUE_UNDEFINED); JERRY_ASSERT (ecma_is_value_object (stack_top_p[-3])); JERRY_ASSERT (stack_top_p[-4] == ECMA_VALUE_UNDEFINED || ecma_is_value_object (stack_top_p[-4])); if (!done) { result = ecma_op_object_get_by_magic_id (result_obj_p, LIT_MAGIC_STRING_VALUE); ecma_deref_object (result_obj_p); if (ECMA_IS_VALUE_ERROR (result)) { return result; } /* It seems browsers call Await(result) here, although the standard does not * requests to do so. The following code might follow browsers in the future. */ ecma_deref_if_object (result); stack_top_p[-1] |= VM_CONTEXT_CLOSE_ITERATOR; stack_top_p[-2] = result; ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); return ECMA_VALUE_EMPTY; } ecma_deref_object (result_obj_p); /* This code jumps to the end regardless of the byte code which triggered this await. */ uint32_t context_end = VM_GET_CONTEXT_END (stack_top_p[-1]); executable_object_p->frame_ctx.byte_code_p = executable_object_p->frame_ctx.byte_code_start_p + context_end; VM_MINUS_EQUAL_U16 (executable_object_p->frame_ctx.context_depth, PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION); stack_top_p -= PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION; executable_object_p->frame_ctx.stack_top_p = stack_top_p; ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); return ECMA_VALUE_EMPTY; } } } /* ecma_await_continue */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/inc/meson.build000664 001750 001750 00000000174 15164251010 032132 0ustar00ddennedyddennedy000000 000000 header_files = ['thorvg.h'] thorvg_inc = include_directories('.') install_headers(header_files, subdir: 'thorvg-' + vmaj) lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-biguint64array.inc.h000664 001750 001750 00000002050 15164251010 053644 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * BigUInt64Array description */ #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_BUILTIN_BIGINT #define TYPEDARRAY_BYTES_PER_ELEMENT 8 #define TYPEDARRAY_MAGIC_STRING_ID LIT_MAGIC_STRING_BIGUINT64_ARRAY_UL #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_BIGUINT64ARRAY_PROTOTYPE #include "ecma-builtin-typedarray-template.inc.h" #endif /* JERRY_BUILTIN_BIGINT */ #endif /* JERRY_BUILTIN_TYPEDARRAY && JERRY_BUILTIN_BIGINT */ jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-async-from-sync-iterator-prototype.cpp000664 001750 001750 00000030073 15164251010 055321 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "jerryscript-types.h" #include "ecma-builtin-handlers.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #include "ecma-promise-object.h" #include "jcontext.h" #include "jrt.h" #include "lit-magic-strings.h" #include "lit-strings.h" #include "opcodes.h" #include "vm-defines.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_START = 0, /**< builtin routine start id */ ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_NEXT, /**< 'next' routine v11, 25.1.4.2.1 */ ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_RETURN, /**< 'return' routine v11, 25.1.4.2.2 */ ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_THROW /**< 'throw' routine v11, 25.1.4.2.3 */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-from-sync-iterator-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID async_from_sync_iterator_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup asyncfromsynciteratorprototype ECMA %AsyncFromSyncIteratorPrototype% object built-in * @{ */ /** * AsyncFromSyncIteratorContinuation operation * * See also: * ECMAScript v11, 25.1.4.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_op_async_from_sync_iterator_prototype_continuation (ecma_value_t result, /**< routine's 'result' argument */ ecma_object_t *capability_obj_p) /**< promise capability */ { /* 1. */ ecma_value_t done = ecma_op_iterator_complete (result); /* 2. */ if (ECMA_IS_VALUE_ERROR (ecma_op_if_abrupt_reject_promise (&done, capability_obj_p))) { return done; } uint16_t done_flag = ecma_is_value_false (done) ? 0 : (1 << ECMA_NATIVE_HANDLER_COMMON_FLAGS_SHIFT); ecma_free_value (done); /* 3. */ ecma_value_t value = ecma_op_iterator_value (result); /* 4. */ if (ECMA_IS_VALUE_ERROR (ecma_op_if_abrupt_reject_promise (&value, capability_obj_p))) { return value; } /* 5. */ ecma_value_t builtin_promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); ecma_value_t value_wrapper = ecma_promise_reject_or_resolve (builtin_promise, value, true); ecma_free_value (value); /* 6. */ if (ECMA_IS_VALUE_ERROR (ecma_op_if_abrupt_reject_promise (&value_wrapper, capability_obj_p))) { return value_wrapper; } /* 8 - 9. */ ecma_object_t *on_fulfilled = ecma_op_create_native_handler (ECMA_NATIVE_HANDLER_ASYNC_FROM_SYNC_ITERATOR_UNWRAP, sizeof (ecma_extended_object_t)); ((ecma_extended_object_t *) on_fulfilled)->u.built_in.u2.routine_flags = (uint8_t) done_flag; /* 10. */ ecma_value_t then_result = ecma_promise_perform_then (value_wrapper, ecma_make_object_value (on_fulfilled), ECMA_VALUE_UNDEFINED, capability_obj_p); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (then_result)); ecma_deref_object (on_fulfilled); ecma_free_value (value_wrapper); /* 11. */ return then_result; } /* ecma_op_async_from_sync_iterator_prototype_continuation */ /** * The %AsyncFromSyncIteratorPrototype% object's 'next' routine * * See also: * ECMAScript v11, 25.1.4.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_async_from_sync_iterator_prototype_next (ecma_async_from_sync_iterator_object_t *iter_p, /**< iterator * record*/ ecma_object_t *capability_p, /**< promise capability */ ecma_value_t value) /**< routine's 'value' argument */ { /* 5. */ ecma_value_t next_result = ecma_op_iterator_next (iter_p->header.u.cls.u3.sync_iterator, iter_p->sync_next_method, value); /* 6. */ if (ECMA_IS_VALUE_ERROR (ecma_op_if_abrupt_reject_promise (&next_result, capability_p))) { return next_result; } /* 7. */ ecma_value_t result = ecma_op_async_from_sync_iterator_prototype_continuation (next_result, capability_p); ecma_free_value (next_result); return result; } /* ecma_builtin_async_from_sync_iterator_prototype_next */ /** * The %AsyncFromSyncIteratorPrototype% object's 'return' and 'throw' routines * * See also: * ECMAScript v11, 25.1.4.2.2 * ECMAScript v11, 25.1.4.2.3 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_async_from_sync_iterator_prototype_do (ecma_async_from_sync_iterator_object_t *iter_p, /**< iterator * record*/ ecma_object_t *capability_obj_p, /**< promise capability */ ecma_value_t value, /**< routine's 'value' argument */ lit_magic_string_id_t method_id) /**< method id */ { /* 5. */ ecma_value_t sync_iterator = iter_p->header.u.cls.u3.sync_iterator; ecma_value_t method = ecma_op_get_method_by_magic_id (sync_iterator, method_id); /* 6. */ if (ECMA_IS_VALUE_ERROR (ecma_op_if_abrupt_reject_promise (&method, capability_obj_p))) { return method; } ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) capability_obj_p; ecma_value_t call_arg; uint32_t arg_size; if (ecma_is_value_empty (value)) { arg_size = 0; call_arg = ECMA_VALUE_UNDEFINED; } else { arg_size = 1; call_arg = value; } /* 7. */ if (ecma_is_value_undefined (method)) { ecma_value_t func_obj; if (method_id == LIT_MAGIC_STRING_RETURN) { /* 7.a. */ call_arg = ecma_create_iter_result_object (call_arg, ECMA_VALUE_TRUE); arg_size = 1; func_obj = capability_p->resolve; } else { func_obj = capability_p->reject; } /* 7.b. */ ecma_value_t resolve = ecma_op_function_call (ecma_get_object_from_value (func_obj), ECMA_VALUE_UNDEFINED, &call_arg, arg_size); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (resolve)); ecma_free_value (resolve); if (method_id == LIT_MAGIC_STRING_RETURN) { ecma_free_value (call_arg); } /* 7.c. */ return ecma_copy_value (capability_p->header.u.cls.u3.promise); } /* 8. */ ecma_value_t call_result = ecma_op_function_validated_call (method, sync_iterator, &call_arg, arg_size); ecma_free_value (method); /* 9. */ if (ECMA_IS_VALUE_ERROR (ecma_op_if_abrupt_reject_promise (&call_result, capability_obj_p))) { return call_result; } /* 10. */ if (!ecma_is_value_object (call_result)) { ecma_free_value (call_result); #if JERRY_ERROR_MESSAGES const lit_utf8_byte_t *msg_p = (lit_utf8_byte_t *) ecma_get_error_msg (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); lit_utf8_size_t msg_size = ecma_get_error_size (ECMA_ERR_ARGUMENT_IS_NOT_AN_OBJECT); ecma_string_t *error_msg_p = ecma_new_ecma_string_from_ascii (msg_p, msg_size); #else /* !JERRY_ERROR_MESSAGES */ ecma_string_t *error_msg_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); #endif /* JERRY_ERROR_MESSAGES */ ecma_object_t *type_error_obj_p = ecma_new_standard_error (JERRY_ERROR_TYPE, error_msg_p); #if JERRY_ERROR_MESSAGES ecma_deref_ecma_string (error_msg_p); #endif /* JERRY_ERROR_MESSAGES */ ecma_value_t type_error = ecma_make_object_value (type_error_obj_p); /* 10.a. */ ecma_value_t reject = ecma_op_function_call (ecma_get_object_from_value (capability_p->reject), ECMA_VALUE_UNDEFINED, &type_error, 1); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (reject)); ecma_deref_object (type_error_obj_p); ecma_free_value (reject); /* 10.b. */ return ecma_copy_value (capability_p->header.u.cls.u3.promise); } ecma_value_t result = ecma_op_async_from_sync_iterator_prototype_continuation (call_result, capability_obj_p); ecma_free_value (call_result); return result; } /* ecma_builtin_async_from_sync_iterator_prototype_do */ /** * Dispatcher of the %AsyncFromSyncIteratorPrototype% built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_async_from_sync_iterator_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide * routine * identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of * arguments * passed to * routine */ uint32_t arguments_number) /**< length of * arguments' list */ { JERRY_UNUSED (arguments_number); JERRY_ASSERT (ecma_is_value_object (this_arg)); ecma_object_t *this_obj_p = ecma_get_object_from_value (this_arg); JERRY_ASSERT (ecma_object_class_is (this_obj_p, ECMA_OBJECT_CLASS_ASYNC_FROM_SYNC_ITERATOR)); ecma_async_from_sync_iterator_object_t *iter_p = (ecma_async_from_sync_iterator_object_t *) this_obj_p; ecma_value_t builtin_promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); ecma_object_t *capability_p = ecma_promise_new_capability (builtin_promise, ECMA_VALUE_UNDEFINED); JERRY_ASSERT (capability_p != NULL); ecma_value_t result; ecma_value_t arg = (arguments_number == 0 ? ECMA_VALUE_EMPTY : arguments_list_p[0]); switch (builtin_routine_id) { case ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_NEXT: { result = ecma_builtin_async_from_sync_iterator_prototype_next (iter_p, capability_p, arg); break; } case ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_RETURN: { result = ecma_builtin_async_from_sync_iterator_prototype_do (iter_p, capability_p, arg, LIT_MAGIC_STRING_RETURN); break; } case ECMA_ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_ROUTINE_THROW: { result = ecma_builtin_async_from_sync_iterator_prototype_do (iter_p, capability_p, arg, LIT_MAGIC_STRING_THROW); break; } default: { JERRY_UNREACHABLE (); break; } } ecma_deref_object (capability_p); return result; } /* ecma_builtin_async_from_sync_iterator_prototype_dispatch_routine */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/cross/ios_simulator_arm64.txt000664 001750 001750 00000001171 15164251010 035011 0ustar00ddennedyddennedy000000 000000 # build for the ios simulator (Apple Silicon) [binaries] cpp = ['clang++', '-arch', 'arm64', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] ar = 'ar' strip = 'strip' [properties] root = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer' has_function_printf = true [built-in options] cpp_args = ['-mios-simulator-version-min=11.0'] cpp_link_args = ['-mios-simulator-version-min=11.0'] [host_machine] system = 'darwin' subsystem = 'ios' kernel = 'xnu' cpu_family = 'aarch64' cpu = 'aarch64' endian = 'little' loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-rangeerror-prototype.inc.h000664 001750 001750 00000002463 15164251010 053027 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * RangeError.prototype built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" #if JERRY_BUILTIN_ERRORS /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.7.8 */ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_RANGE_ERROR, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.9 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_RANGE_ERROR_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) /* ECMA-262 v5, 15.11.7.10 */ STRING_VALUE (LIT_MAGIC_STRING_MESSAGE, LIT_MAGIC_STRING__EMPTY, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* JERRY_BUILTIN_ERRORS */ #include "ecma-builtin-helpers-macro-undefs.inc.h" glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jrt/jrt-fatals.cpp000664 001750 001750 00000005401 15164251010 042671 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /** * Implementation of exit with specified status code. */ #include "jrt-libc-includes.h" #include "jrt.h" /* * Exit with specified status code. * * If !JERRY_NDEBUG and code != 0, print status code with description * and call assertion fail handler. */ void JERRY_ATTR_NORETURN jerry_fatal (jerry_fatal_code_t code) /**< status code */ { #ifndef JERRY_NDEBUG switch (code) { case JERRY_FATAL_OUT_OF_MEMORY: { JERRY_ERROR_MSG ("Error: JERRY_FATAL_OUT_OF_MEMORY\n"); break; } case JERRY_FATAL_REF_COUNT_LIMIT: { JERRY_ERROR_MSG ("Error: JERRY_FATAL_REF_COUNT_LIMIT\n"); break; } case JERRY_FATAL_UNTERMINATED_GC_LOOPS: { JERRY_ERROR_MSG ("Error: JERRY_FATAL_UNTERMINATED_GC_LOOPS\n"); break; } case JERRY_FATAL_DISABLED_BYTE_CODE: { JERRY_ERROR_MSG ("Error: JERRY_FATAL_DISABLED_BYTE_CODE\n"); break; } case JERRY_FATAL_FAILED_ASSERTION: { JERRY_ERROR_MSG ("Error: JERRY_FATAL_FAILED_ASSERTION\n"); break; } } #endif /* !JERRY_NDEBUG */ /* to make compiler happy for some RTOS: 'control reaches end of non-void function' */ while (true) { } } /* jerry_fatal */ #ifndef JERRY_NDEBUG /** * Handle failed assertion */ void JERRY_ATTR_NORETURN jerry_assert_fail (const char *assertion, /**< assertion condition string */ const char *file, /**< file name */ const char *function, /**< function name */ const uint32_t line) /**< line */ { JERRY_ERROR_MSG ("ICE: Assertion '%s' failed at %s(%s):%u.\n", assertion, file, function, line); jerry_fatal (JERRY_FATAL_FAILED_ASSERTION); } /* jerry_assert_fail */ /** * Handle execution of control path that should be unreachable */ void JERRY_ATTR_NORETURN jerry_unreachable (const char *file, /**< file name */ const char *function, /**< function name */ const uint32_t line) /**< line */ { JERRY_ERROR_MSG ("ICE: Unreachable control path at %s(%s):%u was executed.\n", file, function, line); jerry_fatal (JERRY_FATAL_FAILED_ASSERTION); } /* jerry_unreachable */ #endif /* !JERRY_NDEBUG */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/itoa.h000664 001750 001750 00000023576 15164251010 037605 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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) { RAPIDJSON_ASSERT(buffer != 0); 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('0' + static_cast(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) { RAPIDJSON_ASSERT(buffer != 0); uint32_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; u = ~u + 1; } return u32toa(u, buffer); } inline char* u64toa(uint64_t value, char* buffer) { RAPIDJSON_ASSERT(buffer != 0); 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(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(value / kTen8); const uint32_t v1 = static_cast(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]; *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(value / kTen16); // 1 to 1844 value %= kTen16; if (a < 10) *buffer++ = static_cast('0' + static_cast(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('0' + static_cast(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(value / kTen8); const uint32_t v1 = static_cast(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) { RAPIDJSON_ASSERT(buffer != 0); uint64_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; u = ~u + 1; } return u64toa(u, buffer); } } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_ITOA_ jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint8array-prototype.inc.h000664 001750 001750 00000001616 15164251010 055152 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Uint8Array prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 1 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_UINT8ARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-eval.cpp000664 001750 001750 00000004756 15164251010 044761 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-eval.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" #include "jcontext.h" #include "js-parser.h" #include "vm.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup eval eval * @{ */ /** * Perform 'eval' with code stored in ecma-string * * See also: * ecma_op_eval_chars_buffer * ECMA-262 v5, 15.1.2.1 (steps 2 to 8) * * @return ecma value */ ecma_value_t ecma_op_eval (ecma_value_t source_code, /**< source code */ uint32_t parse_opts) /**< ecma_parse_opts_t option bits */ { JERRY_ASSERT (ecma_is_value_string (source_code)); if (ecma_is_value_magic_string (source_code, LIT_MAGIC_STRING__EMPTY)) { return ECMA_VALUE_UNDEFINED; } return ecma_op_eval_chars_buffer ((void *) &source_code, parse_opts | ECMA_PARSE_HAS_SOURCE_VALUE); } /* ecma_op_eval */ /** * Perform 'eval' with code stored in continuous character buffer * * See also: * ecma_op_eval * ECMA-262 v5, 15.1.2.1 (steps 2 to 8) * * @return ecma value */ ecma_value_t ecma_op_eval_chars_buffer (void *source_p, /**< source code */ uint32_t parse_opts) /**< ecma_parse_opts_t option bits */ { #if JERRY_PARSER JERRY_ASSERT (source_p != NULL); uint32_t is_strict_call = ECMA_PARSE_STRICT_MODE | ECMA_PARSE_DIRECT_EVAL; if ((parse_opts & is_strict_call) != is_strict_call) { parse_opts &= (uint32_t) ~ECMA_PARSE_STRICT_MODE; } parse_opts |= ECMA_PARSE_EVAL; ECMA_CLEAR_LOCAL_PARSE_OPTS (); ecma_compiled_code_t *bytecode_p = parser_parse_script (source_p, parse_opts, NULL); if (JERRY_UNLIKELY (bytecode_p == NULL)) { return ECMA_VALUE_ERROR; } return vm_run_eval (bytecode_p, parse_opts); #endif /* JERRY_PARSER */ } /* ecma_op_eval_chars_buffer */ /** * @} * @} */ lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint8clampedarray.cpp000664 001750 001750 00000004617 15164251010 054224 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-uint8clampedarray.inc.h" #define BUILTIN_UNDERSCORED_ID uint8clampedarray #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup uint8clampedarray ECMA Uint8ClampedArray object built-in * @{ */ /** * Handle calling [[Call]] of Uint8ClampedArray * * @return ecma value */ ecma_value_t ecma_builtin_uint8clampedarray_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_UINT8_CLAMPED_ARRAY_REQUIRES_NEW); } /* ecma_builtin_uint8clampedarray_dispatch_call */ /** * Handle calling [[Construct]] of Uint8ClampedArray * * @return ecma value */ ecma_value_t ecma_builtin_uint8clampedarray_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_UINT8_CLAMPED_ARRAY); } /* ecma_builtin_uint8clampedarray_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/byte-code.cpp000664 001750 001750 00000004773 15164251010 043621 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "js-parser-internal.h" /* These two checks only checks the compiler, they have no effect on the code. */ JERRY_STATIC_ASSERT ((sizeof (cbc_uint8_arguments_t) == 16), sizeof_cbc_uint8_arguments_t_must_be_16_byte_long); JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) == 24), sizeof_cbc_uint16_arguments_t_must_be_24_byte_long); JERRY_STATIC_ASSERT ((offsetof (cbc_uint8_arguments_t, script_value) == offsetof (cbc_uint16_arguments_t, script_value)), script_value_in_cbc_uint8_arguments_and_cbc_uint16_arguments_must_be_in_the_same_offset); /** * The reason of these two static asserts to notify the developer to increase the JERRY_SNAPSHOT_VERSION * whenever new bytecodes are introduced or existing ones have been deleted. */ JERRY_STATIC_ASSERT ((((int) CBC_END) == 238), number_of_cbc_opcodes_changed); JERRY_STATIC_ASSERT ((((int) CBC_EXT_END) == 167), number_of_cbc_ext_opcodes_changed); /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_bytecode Bytecode * @{ */ #if JERRY_PARSER || JERRY_PARSER_DUMP_BYTE_CODE /** * Compact bytecode definition */ #define CBC_OPCODE(arg1, arg2, arg3, arg4) ((arg2) | (((arg3) + CBC_STACK_ADJUST_BASE) << CBC_STACK_ADJUST_SHIFT)), /** * Flags of the opcodes. */ const uint8_t cbc_flags[] JERRY_ATTR_CONST_DATA = { CBC_OPCODE_LIST }; /** * Flags of the extended opcodes. */ const uint8_t cbc_ext_flags[] = { CBC_EXT_OPCODE_LIST }; #undef CBC_OPCODE #endif /* JERRY_PARSER || JERRY_PARSER_DUMP_BYTE_CODE */ #if JERRY_PARSER_DUMP_BYTE_CODE #define CBC_OPCODE(arg1, arg2, arg3, arg4) #arg1, /** * Names of the opcodes. */ const char* const cbc_names[] = { CBC_OPCODE_LIST }; /** * Names of the extended opcodes. */ const char* const cbc_ext_names[] = { CBC_EXT_OPCODE_LIST }; #undef CBC_OPCODE #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ /** * @} * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers-number.cpp000664 001750 001750 00000042265 15164251010 045506 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-helpers-number.h" #include #include "ecma-conversion.h" #include "lit-char-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ JERRY_STATIC_ASSERT ((sizeof (ecma_value_t) == sizeof (ecma_integer_value_t)), size_of_ecma_value_t_must_be_equal_to_the_size_of_ecma_integer_value_t); JERRY_STATIC_ASSERT ((((int)ECMA_DIRECT_SHIFT) == (((int)ECMA_VALUE_SHIFT) + 1)), currently_directly_encoded_values_has_one_extra_flag); JERRY_STATIC_ASSERT ((((1 << (ECMA_DIRECT_SHIFT - 1)) | ECMA_TYPE_DIRECT) == ECMA_DIRECT_TYPE_SIMPLE_VALUE), currently_directly_encoded_values_start_after_direct_type_simple_value); JERRY_STATIC_ASSERT ((sizeof (ecma_number_t) == sizeof (ecma_binary_num_t)), size_of_ecma_number_t_must_be_equal_to_binary_representation); /** * Convert an ecma-number to it's binary representation. * * @return binary representation */ ecma_binary_num_t JERRY_ATTR_CONST ecma_number_to_binary (ecma_number_t number) /**< ecma number */ { ecma_number_accessor_t f; f.as_number = number; return f.as_binary; } /* ecma_number_to_binary */ /** * Convert a binary representation to the corresponding ecma-number. * * @return ecma-number */ ecma_number_t JERRY_ATTR_CONST ecma_number_from_binary (ecma_binary_num_t binary) /**< binary representation */ { ecma_number_accessor_t f; f.as_binary = binary; return f.as_number; } /* ecma_number_from_binary */ /** * Check signedness of the binary number. * * @return true - if sign bit is set * false - otherwise */ bool JERRY_ATTR_CONST ecma_number_sign (ecma_binary_num_t binary) /**< binary representation */ { return (binary & ECMA_NUMBER_SIGN_BIT) != 0; } /* ecma_number_sign */ /** * Get biased exponent field of the binary number. * * @return unsigned integer value of the biased exponent field */ uint32_t JERRY_ATTR_CONST ecma_number_biased_exp (ecma_binary_num_t binary) /**< binary representation */ { return (uint32_t) ((binary & ~ECMA_NUMBER_SIGN_BIT) >> ECMA_NUMBER_FRACTION_WIDTH); } /* ecma_number_biased_exp */ /** * Get fraction field of the binary number. * * @return unsigned integer value of the fraction field */ uint64_t JERRY_ATTR_CONST ecma_number_fraction (ecma_binary_num_t binary) /**< binary representation */ { return binary & ((1ull << ECMA_NUMBER_FRACTION_WIDTH) - 1); } /* ecma_number_fraction */ /** * Packing sign, fraction and biased exponent to ecma-number * * @return ecma-number with specified sign, biased_exponent and fraction */ ecma_number_t ecma_number_create (bool sign, /**< sign */ uint32_t biased_exp, /**< biased exponent */ uint64_t fraction) /**< fraction */ { JERRY_ASSERT ((biased_exp & ~((1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1)) == 0); JERRY_ASSERT ((fraction & ~((1ull << ECMA_NUMBER_FRACTION_WIDTH) - 1)) == 0); ecma_binary_num_t binary = biased_exp; binary <<= ECMA_NUMBER_FRACTION_WIDTH; binary |= fraction; if (sign) { binary |= ECMA_NUMBER_SIGN_BIT; } return ecma_number_from_binary (binary); } /* ecma_number_create */ /** * Check if ecma-number is NaN * * @return true - if biased exponent is filled with 1 bits and fraction is filled with anything but not all zero bits, * false - otherwise */ bool ecma_number_is_nan (ecma_number_t num) /**< ecma-number */ { bool is_nan = (num != num); #ifndef JERRY_NDEBUG /* IEEE-754 2008, 3.4, a */ ecma_binary_num_t binary = ecma_number_to_binary (num); bool is_nan_exponent = (ecma_number_biased_exp (binary) == (1 << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1); bool is_nan_fraction = (ecma_number_fraction (binary) > 0); bool is_nan_ieee754 = is_nan_exponent && is_nan_fraction; JERRY_ASSERT (is_nan == is_nan_ieee754); #endif /* !JERRY_NDEBUG */ return is_nan; } /* ecma_number_is_nan */ /** * Make a NaN. * * @return NaN value */ ecma_number_t JERRY_ATTR_CONST ecma_number_make_nan (void) { ecma_number_accessor_t f; f.as_binary = ECMA_NUMBER_BINARY_QNAN; return f.as_number; } /* ecma_number_make_nan */ /** * Make an Infinity. * * @return if !sign - +Infinity value, * else - -Infinity value. */ ecma_number_t JERRY_ATTR_CONST ecma_number_make_infinity (bool sign) /**< sign of the value */ { ecma_number_accessor_t f; f.as_binary = ECMA_NUMBER_BINARY_INF; if (sign) { f.as_binary |= ECMA_NUMBER_SIGN_BIT; } return f.as_number; } /* ecma_number_make_infinity */ /** * Check if ecma-number is negative * * @return true - if sign bit of ecma-number is set * false - otherwise */ bool JERRY_ATTR_CONST ecma_number_is_negative (ecma_number_t num) /**< ecma-number */ { JERRY_ASSERT (!ecma_number_is_nan (num)); return (ecma_number_to_binary (num) & ECMA_NUMBER_SIGN_BIT) != 0; } /* ecma_number_is_negative */ /** * Check if ecma-number is zero * * @return true - if fraction is zero and biased exponent is zero, * false - otherwise */ bool JERRY_ATTR_CONST ecma_number_is_zero (ecma_number_t num) /**< ecma-number */ { bool is_zero = (num == ECMA_NUMBER_ZERO); #ifndef JERRY_NDEBUG bool is_zero_ieee754 = ((ecma_number_to_binary (num) & ~ECMA_NUMBER_SIGN_BIT) == 0); JERRY_ASSERT (is_zero == is_zero_ieee754); #endif /* !JERRY_NDEBUG */ return is_zero; } /* ecma_number_is_zero */ /** * Check if number is infinity * * @return true - if biased exponent is filled with 1 bits and * fraction is filled with zero bits, * false - otherwise */ bool JERRY_ATTR_CONST ecma_number_is_infinity (ecma_number_t num) /**< ecma-number */ { return (ecma_number_to_binary (num) & ~ECMA_NUMBER_SIGN_BIT) == ECMA_NUMBER_BINARY_INF; } /* ecma_number_is_infinity */ /** * Check if number is finite * * @return true - if number is finite * false - if number is NaN or infinity */ bool JERRY_ATTR_CONST ecma_number_is_finite (ecma_number_t num) /**< ecma-number */ { #if defined(__GNUC__) || defined(__clang__) return __builtin_isfinite (num); #elif defined(_WIN32) return isfinite (num); #else /* !(defined(__GNUC__) || defined(__clang__) || defined(_WIN32)) */ return !ecma_number_is_nan (num) && !ecma_number_is_infinity (num); #endif /* defined (__GNUC__) || defined (__clang__) */ } /* ecma_number_is_finite */ /** * Get previous representable ecma-number * * @return maximum ecma-number that is less compared to passed argument */ ecma_number_t JERRY_ATTR_CONST ecma_number_get_prev (ecma_number_t num) /**< ecma-number */ { #if defined(__GNUC__) || defined(__clang__) return __builtin_nextafter ((double)num, (double)-INFINITY); #else /* !defined (__GNUC__) && !defined (__clang__) */ JERRY_ASSERT (!ecma_number_is_nan (num)); ecma_binary_num_t binary = ecma_number_to_binary (num); /* If -Infinity, return self */ if (binary == (ECMA_NUMBER_SIGN_BIT | ECMA_NUMBER_BINARY_INF)) { return num; } /* If +0.0, return -0.0 */ if (binary == ECMA_NUMBER_BINARY_ZERO) { return -num; } if (ecma_number_sign (binary)) { return ecma_number_from_binary (binary + 1); } return ecma_number_from_binary (binary - 1); #endif /* !defined (__GNUC__) && !defined (__clang__) */ } /* ecma_number_get_prev */ /** * Get next representable ecma-number * * @return minimum ecma-number that is greater compared to passed argument */ ecma_number_t JERRY_ATTR_CONST ecma_number_get_next (ecma_number_t num) /**< ecma-number */ { #if defined(__GNUC__) || defined(__clang__) return __builtin_nextafter ((double)num, (double)INFINITY); #else /* !defined (__GNUC__) && !defined (__clang__) */ JERRY_ASSERT (!ecma_number_is_nan (num)); ecma_binary_num_t binary = ecma_number_to_binary (num); /* If +Infinity, return self */ if (binary == ECMA_NUMBER_BINARY_INF) { return num; } /* If -0.0, return +0.0 */ if (binary == (ECMA_NUMBER_SIGN_BIT | ECMA_NUMBER_BINARY_ZERO)) { return -num; } if (ecma_number_sign (binary)) { return ecma_number_from_binary (binary - 1); } return ecma_number_from_binary (binary + 1); #endif /* !defined (__GNUC__) && !defined (__clang__) */ } /* ecma_number_get_next */ /** * Truncate fractional part of the number * * @return integer part of the number */ ecma_number_t JERRY_ATTR_CONST ecma_number_trunc (ecma_number_t num) /**< ecma-number */ { JERRY_ASSERT (!ecma_number_is_nan (num)); ecma_binary_num_t binary = ecma_number_to_binary (num); uint32_t exponent = ecma_number_biased_exp (binary); if (exponent < ECMA_NUMBER_EXPONENT_BIAS) { return ECMA_NUMBER_ZERO; } uint32_t unbiased_exp = exponent - ECMA_NUMBER_EXPONENT_BIAS; if (unbiased_exp >= ECMA_NUMBER_FRACTION_WIDTH) { return num; } binary &= ~((1ull << (ECMA_NUMBER_FRACTION_WIDTH - unbiased_exp)) - 1); return ecma_number_from_binary (binary); } /* ecma_number_trunc */ /** * Calculate remainder of division of two numbers, * as specified in ECMA-262 v5, 11.5.3, item 6. * * Note: * operands shouldn't contain NaN, Infinity, or zero. * * @return number - calculated remainder. */ ecma_number_t JERRY_ATTR_CONST ecma_number_remainder (ecma_number_t left_num, /**< left operand */ ecma_number_t right_num) /**< right operand */ { JERRY_ASSERT (ecma_number_is_finite (left_num) && !ecma_number_is_zero (left_num)); JERRY_ASSERT (ecma_number_is_finite (right_num) && !ecma_number_is_zero (right_num)); const ecma_number_t q = ecma_number_trunc (left_num / right_num); ecma_number_t r = left_num - right_num * q; if (ecma_number_is_zero (r) && ecma_number_is_negative (left_num)) { r = -r; } return r; } /* ecma_number_remainder */ /** * Compute power operation according to the ES standard. * * @return x ** y */ ecma_number_t JERRY_ATTR_CONST ecma_number_pow (ecma_number_t x, /**< left operand */ ecma_number_t y) /**< right operand */ { if (ecma_number_is_nan (y) || (ecma_number_is_infinity (y) && (x == ECMA_NUMBER_ONE || x == ECMA_NUMBER_MINUS_ONE))) { /* Handle differences between ES5.1 and ISO C standards for pow. */ return ecma_number_make_nan (); } if (ecma_number_is_zero (y)) { /* Handle differences between ES5.1 and ISO C standards for pow. */ return ECMA_NUMBER_ONE; } return DOUBLE_TO_ECMA_NUMBER_T (pow (x, y)); } /* ecma_number_pow */ /** * ECMA-integer number multiplication. * * @return number - result of multiplication. */ ecma_value_t JERRY_ATTR_CONST ecma_integer_multiply (ecma_integer_value_t left_integer, /**< left operand */ ecma_integer_value_t right_integer) /**< right operand */ { #if defined(__GNUC__) || defined(__clang__) /* Check if either integer is power of 2 */ if (JERRY_UNLIKELY ((left_integer & (left_integer - 1)) == 0)) { /* Right shift right_integer with log2 (left_integer) */ return ecma_make_integer_value ( (int32_t) ((uint32_t) right_integer << (__builtin_ctz ((unsigned int) left_integer)))); } if (JERRY_UNLIKELY ((right_integer & (right_integer - 1)) == 0)) { /* Right shift left_integer with log2 (right_integer) */ return ecma_make_integer_value ( (int32_t) ((uint32_t) left_integer << (__builtin_ctz ((unsigned int) right_integer)))); } #endif /* defined (__GNUC__) || defined (__clang__) */ return ecma_make_integer_value (left_integer * right_integer); } /* ecma_integer_multiply */ /** * The Number object's 'parseInt' routine * * See also: * ECMA-262 v5, 15.1.2.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_number_parse_int (const lit_utf8_byte_t *str_p, /**< routine's first argument's * string buffer */ lit_utf8_size_t str_size, /**< routine's first argument's * string buffer's size */ ecma_value_t radix_value) /**< routine's second argument */ { /* 2. Remove leading whitespace. */ ecma_string_trim_helper (&str_p, &str_size); if (str_size == 0) { return ecma_make_nan_value (); } const lit_utf8_byte_t *str_end_p = str_p + str_size; /* 3. */ bool sign = false; /* 4. */ if (*str_p == LIT_CHAR_MINUS) { sign = true; str_p++; } /* 5. */ else if (*str_p == LIT_CHAR_PLUS) { str_p++; } /* 6. */ ecma_number_t radix_num; radix_value = ecma_op_to_number (radix_value, &radix_num); if (ECMA_IS_VALUE_ERROR (radix_value)) { return ECMA_VALUE_ERROR; } int32_t radix = ecma_number_to_int32 (radix_num); /* 7.*/ bool strip_prefix = true; /* 8. */ if (radix != 0) { /* 8.a */ if (radix < 2 || radix > 36) { return ecma_make_nan_value (); } /* 8.b */ else if (radix != 16) { strip_prefix = false; } } /* 9. */ else { radix = 10; } /* 10. */ if (strip_prefix && ((str_end_p - str_p) >= 2) && (str_p[0] == LIT_CHAR_0) && (LEXER_TO_ASCII_LOWERCASE (str_p[1]) == LIT_CHAR_LOWERCASE_X)) { str_p += 2; radix = 16; } ecma_number_t value = ECMA_NUMBER_ZERO; const lit_utf8_byte_t *digit_start_p = str_p; /* 11. Check if characters are in [0, Radix - 1]. We also convert them to number values in the process. */ while (str_p < str_end_p) { ecma_char_t ch = *str_p; int32_t digit = 0; if (lit_char_is_decimal_digit (ch)) { digit = ch - LIT_CHAR_0; } else if (LEXER_TO_ASCII_LOWERCASE (ch) >= LIT_CHAR_LOWERCASE_A && LEXER_TO_ASCII_LOWERCASE (ch) <= LIT_CHAR_LOWERCASE_Z) { digit = LEXER_TO_ASCII_LOWERCASE (ch) - LIT_CHAR_LOWERCASE_A + 10; } else { /* Not a valid digit char, set to invalid value */ digit = radix; } if (digit >= radix) { break; } value *= radix; value += digit; str_p++; } /* 12. */ if (str_p == digit_start_p) { return ecma_make_nan_value (); } /* 15. */ if (sign) { value *= ECMA_NUMBER_MINUS_ONE; } return ecma_make_number_value (value); } /* ecma_number_parse_int */ /** * The Number object's 'parseFloat' routine * * See also: * ECMA-262 v5, 15.1.2.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_number_parse_float (const lit_utf8_byte_t *str_p, /**< routine's first argument's * string buffer */ lit_utf8_size_t str_size) /**< routine's first argument's * string buffer's size */ { /* 2. Remove leading whitespace. */ ecma_string_trim_helper (&str_p, &str_size); const lit_utf8_byte_t *str_end_p = str_p + str_size; bool sign = false; if (str_size == 0) { return ecma_make_nan_value (); } if (*str_p == LIT_CHAR_PLUS) { str_p++; } else if (*str_p == LIT_CHAR_MINUS) { sign = true; str_p++; } /* Check if string is equal to "Infinity". */ const lit_utf8_byte_t *infinity_str_p = lit_get_magic_string_utf8 (LIT_MAGIC_STRING_INFINITY_UL); const lit_utf8_size_t infinity_length = lit_get_magic_string_size (LIT_MAGIC_STRING_INFINITY_UL); /* The input string should be at least the length of "Infinity" to be correctly processed as * the infinity value. */ if ((lit_utf8_size_t) (str_end_p - str_p) >= infinity_length && memcmp (infinity_str_p, str_p, infinity_length) == 0) { return ecma_make_number_value (ecma_number_make_infinity (sign)); } const lit_utf8_byte_t *num_start_p = str_p; const lit_utf8_byte_t *num_end_p = str_p; while (str_p < str_end_p && lit_char_is_decimal_digit (*str_p)) { str_p++; } if (str_p < str_end_p && *str_p == LIT_CHAR_DOT) { str_p++; while (str_p < str_end_p && lit_char_is_decimal_digit (*str_p)) { str_p++; } } num_end_p = str_p; if (str_p < str_end_p && LEXER_TO_ASCII_LOWERCASE (*str_p) == LIT_CHAR_LOWERCASE_E) { str_p++; if (str_p < str_end_p && (*str_p == LIT_CHAR_PLUS || *str_p == LIT_CHAR_MINUS)) { str_p++; } if (str_p < str_end_p && lit_char_is_decimal_digit (*str_p)) { str_p++; while (str_p < str_end_p && lit_char_is_decimal_digit (*str_p)) { str_p++; } num_end_p = str_p; } } lit_utf8_size_t num_size = (lit_utf8_size_t) (num_end_p - num_start_p); if (num_size == 0) { return ecma_make_nan_value (); } /* 5. */ ecma_number_t ret_num = ecma_utf8_string_to_number (num_start_p, num_size, 0); if (sign) { ret_num *= ECMA_NUMBER_MINUS_ONE; } return ecma_make_number_value (ret_num); } /* ecma_number_parse_float */ /** * @} * @} */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-rangeerror.cpp000664 001750 001750 00000005443 15164251010 050550 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "jcontext.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-rangeerror.inc.h" #define BUILTIN_UNDERSCORED_ID range_error #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup rangeerror ECMA RangeError object built-in * @{ */ /** * Handle calling [[Call]] of built-in RangeError object * * @return ecma value */ ecma_value_t ecma_builtin_range_error_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_builtin_helper_error_dispatch_call (JERRY_ERROR_RANGE, arguments_list_p, arguments_list_len); } /* ecma_builtin_range_error_dispatch_call */ /** * Handle calling [[Construct]] of built-in RangeError object * * @return ecma value */ ecma_value_t ecma_builtin_range_error_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_RANGE_ERROR_PROTOTYPE); if (proto_p == NULL) { return ECMA_VALUE_ERROR; } ecma_value_t result = ecma_builtin_range_error_dispatch_call (arguments_list_p, arguments_list_len); if (!ECMA_IS_VALUE_ERROR (result)) { ecma_object_t *object_p = ecma_get_object_from_value (result); ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, proto_p); } ecma_deref_object (proto_p); return result; } /* ecma_builtin_range_error_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ERRORS */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jrt/000775 001750 001750 00000000000 15164251010 040116 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/srcsrc/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-number-prototype.cpp000664 001750 001750 00000046251 15164251010 051737 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt-libc-includes.h" #include "jrt.h" #include "lit-char-helpers.h" #if JERRY_BUILTIN_NUMBER #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_NUMBER_PROTOTYPE_ROUTINE_START = 0, ECMA_NUMBER_PROTOTYPE_VALUE_OF, ECMA_NUMBER_PROTOTYPE_TO_STRING, ECMA_NUMBER_PROTOTYPE_TO_LOCALE_STRING, ECMA_NUMBER_PROTOTYPE_TO_FIXED, ECMA_NUMBER_PROTOTYPE_TO_EXPONENTIAL, ECMA_NUMBER_PROTOTYPE_TO_PRECISION, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-number-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID number_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup numberprototype ECMA Number.prototype object built-in * @{ */ /** * Helper for rounding numbers * * @return rounded number */ static inline lit_utf8_size_t ecma_builtin_number_prototype_helper_round (lit_utf8_byte_t *digits_p, /**< [in,out] number as a string in decimal * form */ lit_utf8_size_t num_digits, /**< length of the string representation */ int32_t round_num, /**< number of digits to keep */ int32_t *exponent_p, /**< [in, out] decimal exponent */ bool zero) /**< true if digits_p represents zero */ { if (round_num == 0 && *exponent_p == 0) { if (digits_p[0] >= 5) { digits_p[0] = '1'; } else { digits_p[0] = '0'; } return 1; } if (round_num < 1) { return 0; } if ((lit_utf8_size_t) round_num >= num_digits || zero) { return num_digits; } if (digits_p[round_num] >= '5') { digits_p[round_num] = '0'; int i = 1; /* Handle carry number. */ for (; i <= round_num; i++) { if (++digits_p[round_num - i] <= '9') { break; } digits_p[round_num - i] = '0'; } /* Prepend highest digit */ if (i > round_num) { memmove (digits_p + 1, digits_p, num_digits); digits_p[0] = '1'; *exponent_p += 1; } } return (lit_utf8_size_t) round_num; } /* ecma_builtin_number_prototype_helper_round */ /** * Size of Number toString digit buffers. */ #define NUMBER_TO_STRING_MAX_DIGIT_COUNT 64u /** * The Number.prototype object's 'toString' and 'toLocaleString' routines * * See also: * ECMA-262 v5, 15.7.4.2 * ECMA-262 v5, 15.7.4.7 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_number_prototype_object_to_string (ecma_number_t this_arg_number, /**< this argument number */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { static const lit_utf8_byte_t digit_chars[36] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; uint32_t radix = 10; if (arguments_list_len > 0 && !ecma_is_value_undefined (arguments_list_p[0])) { ecma_number_t arg_num; if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (arguments_list_p[0], &arg_num))) { return ECMA_VALUE_ERROR; } radix = ecma_number_to_uint32 (arg_num); if (radix < 2 || radix > 36) { return ecma_raise_range_error (ECMA_ERR_RADIX_IS_OUT_OF_RANGE); } } if (ecma_number_is_nan (this_arg_number) || ecma_number_is_infinity (this_arg_number) || ecma_number_is_zero (this_arg_number) || radix == 10) { ecma_string_t *ret_str_p = ecma_new_ecma_string_from_number (this_arg_number); return ecma_make_string_value (ret_str_p); } uint8_t integer_digits[NUMBER_TO_STRING_MAX_DIGIT_COUNT]; uint8_t fraction_digits[NUMBER_TO_STRING_MAX_DIGIT_COUNT]; uint32_t integer_zeros = 0; uint32_t fraction_zeros = 0; bool is_number_negative = false; if (ecma_number_is_negative (this_arg_number)) { this_arg_number = -this_arg_number; is_number_negative = true; } ecma_number_t integer_part = floor (this_arg_number); ecma_number_t fraction_part = this_arg_number - integer_part; uint8_t *integer_cursor_p = integer_digits + NUMBER_TO_STRING_MAX_DIGIT_COUNT; uint8_t *fraction_cursor_p = fraction_digits; if (fraction_part > 0.0) { uint8_t digit; ecma_number_t precision = (ecma_number_get_next (this_arg_number) - this_arg_number) * 0.5; precision = JERRY_MAX (precision, ECMA_NUMBER_MIN_VALUE); do { fraction_part *= radix; precision *= radix; digit = (uint8_t) floor (fraction_part); if (digit == 0 && fraction_cursor_p == fraction_digits) { fraction_zeros++; continue; } JERRY_ASSERT (fraction_cursor_p < fraction_digits + NUMBER_TO_STRING_MAX_DIGIT_COUNT); *fraction_cursor_p++ = digit; fraction_part -= (ecma_number_t) digit; } while (fraction_part >= precision); /* Round to even */ if (fraction_part > 0.5 || (fraction_part == 0.5 && (digit & 1) != 0)) { /* Add carry and remove overflowing trailing digits */ while (true) { (*(--fraction_cursor_p))++; if (*fraction_cursor_p < radix) { /* Re-adjust cursor to point after the last significant digit */ fraction_cursor_p++; break; } if (fraction_cursor_p == fraction_digits) { /* Carry overflowed to integer part */ integer_part += 1; break; } } } /* Convert fraction digits to characters. */ for (uint8_t *digit_p = fraction_digits; digit_p < fraction_cursor_p; digit_p++) { *digit_p = digit_chars[*digit_p]; } } while (ecma_number_biased_exp (ecma_number_to_binary (integer_part / radix)) > ECMA_NUMBER_EXPONENT_BIAS + ECMA_NUMBER_FRACTION_WIDTH) { integer_zeros++; integer_part /= radix; } uint64_t integer_u64 = (uint64_t) integer_part; do { uint64_t remainder = integer_u64 % radix; *(--integer_cursor_p) = (uint8_t) digit_chars[remainder]; integer_u64 /= radix; } while (integer_u64 > 0); const uint32_t integer_digit_count = (uint32_t) (integer_digits + NUMBER_TO_STRING_MAX_DIGIT_COUNT - integer_cursor_p); JERRY_ASSERT (integer_digit_count > 0); ecma_stringbuilder_t builder = ecma_stringbuilder_create (); if (is_number_negative) { ecma_stringbuilder_append_byte (&builder, LIT_CHAR_MINUS); } ecma_stringbuilder_append_raw (&builder, integer_cursor_p, integer_digit_count); while (integer_zeros--) { ecma_stringbuilder_append_byte (&builder, LIT_CHAR_0); } if (fraction_cursor_p != fraction_digits) { ecma_stringbuilder_append_byte (&builder, LIT_CHAR_DOT); while (fraction_zeros--) { ecma_stringbuilder_append_byte (&builder, LIT_CHAR_0); } const uint32_t fraction_digit_count = (uint32_t) (fraction_cursor_p - fraction_digits); JERRY_ASSERT (fraction_digit_count > 0); ecma_stringbuilder_append_raw (&builder, fraction_digits, fraction_digit_count); } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_number_prototype_object_to_string */ /** * The Number.prototype object's 'valueOf' routine * * See also: * ECMA-262 v5, 15.7.4.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_number_prototype_object_value_of (ecma_value_t this_arg) /**< this argument */ { if (ecma_is_value_number (this_arg)) { return this_arg; } else if (ecma_is_value_object (this_arg)) { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); if (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_NUMBER)) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; JERRY_ASSERT (ecma_is_value_number (ext_object_p->u.cls.u3.value)); return ext_object_p->u.cls.u3.value; } } return ecma_raise_type_error (ECMA_ERR_ARGUMENT_THIS_NOT_NUMBER); } /* ecma_builtin_number_prototype_object_value_of */ /** * Type of number routine */ typedef enum { NUMBER_ROUTINE_TO_FIXED, /**< Number.prototype.toFixed: ECMA-262 v11, 20.1.3.3 */ NUMBER_ROUTINE_TO_EXPONENTIAL, /**< Number.prototype.toExponential: ECMA-262 v11, 20.1.3.2 */ NUMBER_ROUTINE_TO_PRECISION, /**< Number.prototype.toPrecision: ECMA-262 v11, 20.1.3.5 */ NUMBER_ROUTINE__COUNT, /**< count of the modes */ } number_routine_mode_t; /** * Helper method to convert a number based on the given routine. */ static ecma_value_t ecma_builtin_number_prototype_object_to_number_convert (ecma_number_t this_num, /**< this argument number */ ecma_value_t arg, /**< routine's argument */ number_routine_mode_t mode) /**< number routine mode */ { if (ecma_is_value_undefined (arg) && mode == NUMBER_ROUTINE_TO_PRECISION) { return ecma_builtin_number_prototype_object_to_string (this_num, NULL, 0); } ecma_number_t arg_num; ecma_value_t to_integer = ecma_op_to_integer (arg, &arg_num); if (ECMA_IS_VALUE_ERROR (to_integer)) { return to_integer; } /* Argument boundary check for toFixed method */ if (mode == NUMBER_ROUTINE_TO_FIXED && (arg_num <= -1 || arg_num >= 101)) { return ecma_raise_range_error (ECMA_ERR_FRACTION_DIGITS_OUT_OF_RANGE); } /* Handle NaN separately */ if (ecma_number_is_nan (this_num)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_NAN); } /* Get the parameters of the number */ lit_utf8_byte_t digits[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; lit_utf8_size_t num_of_digits; int32_t exponent; int32_t arg_int = ecma_number_to_int32 (arg_num); bool is_zero = ecma_number_is_zero (this_num); bool is_negative = ecma_number_is_negative (this_num); ecma_stringbuilder_t builder = ecma_stringbuilder_create (); if (is_negative) { if (!is_zero) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_MINUS); } this_num *= -1; } /* Handle zero separately */ if (is_zero) { if (mode == NUMBER_ROUTINE_TO_PRECISION) { arg_int--; } ecma_stringbuilder_append_char (&builder, LIT_CHAR_0); if (arg_int > 0) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_DOT); } for (int32_t i = 0; i < arg_int; i++) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_0); } if (mode == NUMBER_ROUTINE_TO_EXPONENTIAL) { ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) "e+0", 3); } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* Handle infinity separately */ if (ecma_number_is_infinity (this_num)) { ecma_stringbuilder_append_magic (&builder, LIT_MAGIC_STRING_INFINITY_UL); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* Argument boundary check for toExponential and toPrecision methods */ if (mode == NUMBER_ROUTINE_TO_EXPONENTIAL && (arg_num <= -1 || arg_num >= 101)) { ecma_stringbuilder_destroy (&builder); return ecma_raise_range_error (ECMA_ERR_FRACTION_DIGITS_OUT_OF_RANGE); } else if (mode == NUMBER_ROUTINE_TO_PRECISION && (arg_num < 1 || arg_num > 100)) { ecma_stringbuilder_destroy (&builder); return ecma_raise_range_error (ECMA_ERR_PRECISION_DIGITS_MUST_BE_BETWEEN_IN_RANGE); } num_of_digits = ecma_number_to_decimal (this_num, digits, &exponent); /* Handle undefined argument */ if (ecma_is_value_undefined (arg) && mode == NUMBER_ROUTINE_TO_EXPONENTIAL) { arg_int = (int32_t) num_of_digits - 1; } if (mode == NUMBER_ROUTINE_TO_FIXED && exponent > 21) { ecma_stringbuilder_destroy (&builder); if (is_negative) { this_num *= -1; } return ecma_builtin_number_prototype_object_to_string (this_num, NULL, 0); } int32_t digits_to_keep = arg_int; if (mode == NUMBER_ROUTINE_TO_FIXED) { digits_to_keep += exponent; } else if (mode == NUMBER_ROUTINE_TO_EXPONENTIAL) { digits_to_keep += 1; } num_of_digits = ecma_builtin_number_prototype_helper_round (digits, num_of_digits, digits_to_keep, &exponent, false); /* toExponent routine and toPrecision cases where the exponent > precision or exponent < -5 */ if (mode == NUMBER_ROUTINE_TO_EXPONENTIAL || (mode == NUMBER_ROUTINE_TO_PRECISION && (exponent < -5 || exponent > arg_int))) { /* Append first digit */ ecma_stringbuilder_append_byte (&builder, *digits); if (mode == NUMBER_ROUTINE_TO_PRECISION) { arg_int--; } if (arg_int > 0) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_DOT); } /* Append significant fraction digits */ ecma_stringbuilder_append_raw (&builder, digits + 1, num_of_digits - 1); /* Append leading zeros */ for (int32_t i = (int32_t) (num_of_digits); i < arg_int + 1; i++) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_0); } ecma_stringbuilder_append_char (&builder, LIT_CHAR_LOWERCASE_E); if (exponent <= 0) { exponent = (-exponent) + 1; ecma_stringbuilder_append_char (&builder, LIT_CHAR_MINUS); } else { exponent -= 1; ecma_stringbuilder_append_char (&builder, LIT_CHAR_PLUS); } /* Append exponent part */ lit_utf8_size_t exp_size = ecma_uint32_to_utf8_string ((uint32_t) exponent, digits, 3); ecma_stringbuilder_append_raw (&builder, digits, exp_size); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* toFixed routine and toPrecision cases where the exponent <= precision and exponent >= -5 */ lit_utf8_size_t result_digits; if (mode == NUMBER_ROUTINE_TO_FIXED) { result_digits = ((exponent > 0) ? (lit_utf8_size_t) (exponent + arg_int) : (lit_utf8_size_t) (arg_int + 1)); } else { result_digits = ((exponent <= 0) ? (lit_utf8_size_t) (1 - exponent + arg_int) : (lit_utf8_size_t) arg_int); } /* Number of digits we copied from digits array */ lit_utf8_size_t copied_digits = 0; if (exponent == 0 && digits_to_keep == 0) { ecma_stringbuilder_append_char (&builder, *digits); return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } if (exponent <= 0) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_0); result_digits--; if (result_digits > 0) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_DOT); /* Append leading zeros to the fraction part */ for (int32_t i = 0; i < -exponent && result_digits > 0; i++) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_0); result_digits--; } } } else { /* Append significant digits of integer part */ copied_digits = JERRY_MIN (JERRY_MIN (num_of_digits, result_digits), (lit_utf8_size_t) exponent); ecma_stringbuilder_append_raw (&builder, digits, copied_digits); result_digits -= copied_digits; num_of_digits -= copied_digits; exponent -= (int32_t) copied_digits; /* Append zeros before decimal point */ while (exponent > 0 && result_digits > 0) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_0); result_digits--; exponent--; } if (result_digits > 0) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_DOT); } } if (result_digits > 0) { /* Append significant digits to the fraction part */ lit_utf8_size_t to_copy = JERRY_MIN (num_of_digits, result_digits); ecma_stringbuilder_append_raw (&builder, digits + copied_digits, to_copy); result_digits -= to_copy; /* Append leading zeros */ while (result_digits > 0) { ecma_stringbuilder_append_char (&builder, LIT_CHAR_0); result_digits--; } } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_builtin_number_prototype_object_to_number_convert */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_number_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { ecma_value_t this_value = ecma_builtin_number_prototype_object_value_of (this_arg); if (ECMA_IS_VALUE_ERROR (this_value)) { return this_value; } if (builtin_routine_id == ECMA_NUMBER_PROTOTYPE_VALUE_OF) { return ecma_copy_value (this_value); } ecma_number_t this_arg_number = ecma_get_number_from_value (this_value); switch (builtin_routine_id) { case ECMA_NUMBER_PROTOTYPE_TO_STRING: { return ecma_builtin_number_prototype_object_to_string (this_arg_number, arguments_list_p, arguments_number); } case ECMA_NUMBER_PROTOTYPE_TO_LOCALE_STRING: { return ecma_builtin_number_prototype_object_to_string (this_arg_number, NULL, 0); } case ECMA_NUMBER_PROTOTYPE_TO_FIXED: case ECMA_NUMBER_PROTOTYPE_TO_EXPONENTIAL: case ECMA_NUMBER_PROTOTYPE_TO_PRECISION: { const int option = NUMBER_ROUTINE_TO_FIXED + (builtin_routine_id - ECMA_NUMBER_PROTOTYPE_TO_FIXED); return ecma_builtin_number_prototype_object_to_number_convert (this_arg_number, arguments_list_p[0], (number_routine_mode_t) option); } default: { JERRY_UNREACHABLE (); } } } /* ecma_builtin_number_prototype_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_NUMBER */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/utils/rescaler.cpp000664 001750 001750 00000005610 15164251010 035761 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0// Copyright 2012 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Rescaling functions // // Author: Skal (pascal.massimino@gmail.com) #include #include #include "../dsp/dsp.h" #include "./rescaler.h" //------------------------------------------------------------------------------ void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height, uint8_t* const dst, int dst_width, int dst_height, int dst_stride, int num_channels, int x_add, int x_sub, int y_add, int y_sub, int32_t* const work) { wrk->x_expand = (src_width < dst_width); wrk->src_width = src_width; wrk->src_height = src_height; wrk->dst_width = dst_width; wrk->dst_height = dst_height; wrk->dst = dst; wrk->dst_stride = dst_stride; wrk->num_channels = num_channels; // for 'x_expand', we use bilinear interpolation wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add - x_sub; wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub; wrk->y_accum = y_add; wrk->y_add = y_add; wrk->y_sub = y_sub; wrk->fx_scale = (1 << WEBP_RESCALER_RFIX) / x_sub; wrk->fy_scale = (1 << WEBP_RESCALER_RFIX) / y_sub; wrk->fxy_scale = wrk->x_expand ? ((int64_t)dst_height << WEBP_RESCALER_RFIX) / (x_sub * src_height) : ((int64_t)dst_height << WEBP_RESCALER_RFIX) / (x_add * src_height); wrk->irow = work; wrk->frow = work + num_channels * dst_width; WebPRescalerDspInit(); } //------------------------------------------------------------------------------ // all-in-one calls int WebPRescaleNeededLines(const WebPRescaler* const wrk, int max_num_lines) { const int num_lines = (wrk->y_accum + wrk->y_sub - 1) / wrk->y_sub; return (num_lines > max_num_lines) ? max_num_lines : num_lines; } int WebPRescalerImport(WebPRescaler* const wrk, int num_lines, const uint8_t* src, int src_stride) { int total_imported = 0; while (total_imported < num_lines && wrk->y_accum > 0) { int channel; for (channel = 0; channel < wrk->num_channels; ++channel) { WebPRescalerImportRow(wrk, src, channel); } src += src_stride; ++total_imported; wrk->y_accum -= wrk->y_sub; } return total_imported; } int WebPRescalerExport(WebPRescaler* const rescaler) { int total_exported = 0; while (WebPRescalerHasPendingOutput(rescaler)) { WebPRescalerExportRow(rescaler, 0); ++total_exported; } return total_exported; } //------------------------------------------------------------------------------ jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint8clampedarray-prototype.inc.h000664 001750 001750 00000001634 15164251010 056500 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * Uint8ClampedArray prototype description */ #if JERRY_BUILTIN_TYPEDARRAY #define TYPEDARRAY_BYTES_PER_ELEMENT 1 #define TYPEDARRAY_BUILTIN_ID ECMA_BUILTIN_ID_UINT8CLAMPEDARRAY #include "ecma-builtin-typedarray-prototype-template.inc.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-atomics.cpp000664 001750 001750 00000016441 15164251010 050041 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-atomics-object.h" #include "ecma-builtins.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "jrt.h" #if JERRY_BUILTIN_ATOMICS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" /** * This object has a custom dispatch function. */ #define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. */ enum { ECMA_ATOMICS_ROUTINE_START = 0, /**< Special value, should be ignored */ ECMA_ATOMICS_ROUTINE_ADD, /**< Atomics add routine */ ECMA_ATOMICS_ROUTINE_AND, /**< Atomics and routine */ ECMA_ATOMICS_ROUTINE_COMPAREEXCHANGE, /**< Atomics compare exchange routine */ ECMA_ATOMICS_ROUTINE_EXCHANGE, /**< Atomics exchange routine */ ECMA_ATOMICS_ROUTINE_ISLOCKFREE, /**< Atomics is lock free routine */ ECMA_ATOMICS_ROUTINE_LOAD, /**< Atomics load routine */ ECMA_ATOMICS_ROUTINE_OR, /**< Atomics or routine */ ECMA_ATOMICS_ROUTINE_STORE, /**< Atomics store routine */ ECMA_ATOMICS_ROUTINE_SUB, /**< Atomics sub routine */ ECMA_ATOMICS_ROUTINE_WAIT, /**< Atomics wait routine */ ECMA_ATOMICS_ROUTINE_NOTIFY, /**< Atomics notify routine */ ECMA_ATOMICS_ROUTINE_XOR, /**< Atomics xor routine */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-atomics.inc.h" #define BUILTIN_UNDERSCORED_ID atomics #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup atomics ECMA Atomics object built-in * @{ */ /** * The Atomics object's 'compareExchange' routine * * See also: ES11 24.4.4 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_atomics_compare_exchange (ecma_value_t typedarray, /**< typedArray argument */ ecma_value_t index, /**< index argument */ ecma_value_t expected_value, /**< expectedValue argument */ ecma_value_t replacement_value) /**< replacementValue argument*/ { JERRY_UNUSED (typedarray); JERRY_UNUSED (index); JERRY_UNUSED (expected_value); JERRY_UNUSED (replacement_value); return ecma_make_uint32_value (0); } /* ecma_builtin_atomics_compare_exchange */ /** * The Atomics object's 'isLockFree' routine * * See also: ES11 24.4.6 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_atomics_is_lock_free (ecma_value_t size) /**< size argument */ { JERRY_UNUSED (size); return ECMA_VALUE_FALSE; } /* ecma_builtin_atomics_is_lock_free */ /** * The Atomics object's 'store' routine * * See also: ES11 24.4.9 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_atomics_store (ecma_value_t typedarray, /**< typedArray argument */ ecma_value_t index, /**< index argument */ ecma_value_t value) /**< value argument */ { JERRY_UNUSED (typedarray); JERRY_UNUSED (index); JERRY_UNUSED (value); return ecma_make_uint32_value (0); } /* ecma_builtin_atomics_store */ /** * The Atomics object's 'wait' routine * * See also: ES11 24.4.11 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_atomics_wait (ecma_value_t typedarray, /**< typedArray argument */ ecma_value_t index, /**< index argument */ ecma_value_t value, /**< value argument */ ecma_value_t timeout) /**< timeout argument */ { JERRY_UNUSED (typedarray); JERRY_UNUSED (index); JERRY_UNUSED (value); JERRY_UNUSED (timeout); return ecma_make_uint32_value (0); } /* ecma_builtin_atomics_wait */ /** * The Atomics object's 'notify' routine * * See also: ES11 24.4.12 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_atomics_notify (ecma_value_t typedarray, /**< typedArray argument */ ecma_value_t index, /**< index argument */ ecma_value_t count) /**< count argument */ { JERRY_UNUSED (typedarray); JERRY_UNUSED (index); JERRY_UNUSED (count); return ecma_make_uint32_value (0); } /* ecma_builtin_atomics_notify */ /** * Dispatcher of the built-in's routines * * @return ecma value * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_atomics_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ ecma_value_t this_arg, /**< 'this' argument value */ const ecma_value_t arguments_list_p[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ { JERRY_UNUSED (this_arg); ecma_value_t arg1 = arguments_list_p[0]; ecma_value_t arg2 = arguments_list_p[1]; ecma_value_t arg3 = arguments_list_p[2]; ecma_value_t arg4 = (arguments_number > 3) ? arguments_list_p[3] : ECMA_VALUE_UNDEFINED; ecma_atomics_op_t type; switch (builtin_routine_id) { case ECMA_ATOMICS_ROUTINE_ADD: { type = ECMA_ATOMICS_ADD; break; } case ECMA_ATOMICS_ROUTINE_AND: { type = ECMA_ATOMICS_AND; break; } case ECMA_ATOMICS_ROUTINE_COMPAREEXCHANGE: { return ecma_builtin_atomics_compare_exchange (arg1, arg2, arg3, arg4); } case ECMA_ATOMICS_ROUTINE_EXCHANGE: { type = ECMA_ATOMICS_EXCHANGE; break; } case ECMA_ATOMICS_ROUTINE_ISLOCKFREE: { return ecma_builtin_atomics_is_lock_free (arg1); } case ECMA_ATOMICS_ROUTINE_LOAD: { return ecma_atomic_load (arg1, arg2); } case ECMA_ATOMICS_ROUTINE_OR: { type = ECMA_ATOMICS_OR; break; } case ECMA_ATOMICS_ROUTINE_STORE: { return ecma_builtin_atomics_store (arg1, arg2, arg3); } case ECMA_ATOMICS_ROUTINE_SUB: { type = ECMA_ATOMICS_SUBTRACT; break; } case ECMA_ATOMICS_ROUTINE_WAIT: { return ecma_builtin_atomics_wait (arg1, arg2, arg3, arg4); } case ECMA_ATOMICS_ROUTINE_NOTIFY: { return ecma_builtin_atomics_notify (arg1, arg2, arg3); } case ECMA_ATOMICS_ROUTINE_XOR: { type = ECMA_ATOMICS_XOR; break; } default: { JERRY_UNREACHABLE (); } } return ecma_atomic_read_modify_write (arg1, arg2, arg3, type); } /* ecma_builtin_atomics_dispatch_routine */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_ATOMICS */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/meson.build000664 001750 001750 00000000444 15164251010 034536 0ustar00ddennedyddennedy000000 000000 webp_deb = [] subdir('dec') subdir('dsp') subdir('utils') subdir('webp') source_file = [ 'tvgWebpLoader.h', 'tvgWebpLoader.cpp', ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), dependencies : webp_deb, sources : source_file )] external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers-string.cpp000664 001750 001750 00000256621 15164251010 045527 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "jcontext.h" #include "jrt-libc-includes.h" #include "jrt.h" #include "lit-char-helpers.h" #include "lit-magic-strings.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ JERRY_STATIC_ASSERT ((((int)ECMA_STRING_CONTAINER_MASK) >= ((int)ECMA_STRING_CONTAINER__MAX)), ecma_string_container_types_must_be_lower_than_the_container_mask); JERRY_STATIC_ASSERT (((ECMA_STRING_MAX_REF | ECMA_STRING_CONTAINER_MASK | ECMA_STATIC_STRING_FLAG) == UINT32_MAX), ecma_string_ref_and_container_fields_should_fill_the_32_bit_field); JERRY_STATIC_ASSERT (((uint32_t)ECMA_STRING_NOT_ARRAY_INDEX == UINT32_MAX), ecma_string_not_array_index_must_be_equal_to_uint32_max); JERRY_STATIC_ASSERT ((ECMA_TYPE_DIRECT_STRING & 0x1) != 0, ecma_type_direct_string_must_be_odd_number); JERRY_STATIC_ASSERT ((((int)LIT_MAGIC_STRING__COUNT) <= ((int)ECMA_DIRECT_STRING_MAX_IMM)), all_magic_strings_must_be_encoded_as_direct_string); JERRY_STATIC_ASSERT (((int) ECMA_DIRECT_STRING_UINT == (int) ECMA_STRING_CONTAINER_UINT32_IN_DESC), ecma_direct_and_container_types_must_match); JERRY_STATIC_ASSERT ((((int)ECMA_PROPERTY_NAME_TYPE_SHIFT) > ((int)ECMA_VALUE_SHIFT)), ecma_property_name_type_shift_must_be_greater_than_ecma_value_shift); JERRY_STATIC_ASSERT ((sizeof (ecma_stringbuilder_header_t) <= ECMA_ASCII_STRING_HEADER_SIZE), ecma_stringbuilder_header_must_not_be_larger_than_ecma_ascii_string); /** * Convert a string to an unsigned 32 bit value if possible * * @return true if the conversion is successful * false otherwise */ static bool ecma_string_to_array_index (const lit_utf8_byte_t *string_p, /**< utf-8 string */ lit_utf8_size_t string_size, /**< string size */ uint32_t *result_p) /**< [out] converted value */ { JERRY_ASSERT (string_size > 0 && *string_p >= LIT_CHAR_0 && *string_p <= LIT_CHAR_9); if (*string_p == LIT_CHAR_0) { *result_p = 0; return (string_size == 1); } if (string_size > ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32) { return false; } uint32_t index = 0; const lit_utf8_byte_t *string_end_p = string_p + string_size; if (string_size == ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32) { string_end_p--; } do { if (*string_p > LIT_CHAR_9 || *string_p < LIT_CHAR_0) { return false; } index = (index * 10) + (uint32_t) (*string_p++ - LIT_CHAR_0); } while (string_p < string_end_p); if (string_size < ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32) { *result_p = index; return true; } /* Overflow must be checked as well when size is * equal to ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32. */ if (*string_p > LIT_CHAR_9 || *string_p < LIT_CHAR_0 || index > (UINT32_MAX / 10) || (index == (UINT32_MAX / 10) && *string_p > LIT_CHAR_5)) { return false; } *result_p = (index * 10) + (uint32_t) (*string_p - LIT_CHAR_0); return true; } /* ecma_string_to_array_index */ /** * Returns the characters and size of a string. * * Note: * UINT type is not supported * * @return byte array start - if the byte array of a string is available * NULL - otherwise */ static const lit_utf8_byte_t * ecma_string_get_chars_fast (const ecma_string_t *string_p, /**< ecma-string */ lit_utf8_size_t *size_p) /**< [out] size of the ecma string */ { if (ECMA_IS_DIRECT_STRING (string_p)) { if (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_MAGIC) { uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); if (id >= LIT_MAGIC_STRING__COUNT) { id -= LIT_MAGIC_STRING__COUNT; *size_p = lit_get_magic_string_ex_size (id); return lit_get_magic_string_ex_utf8 (id); } *size_p = lit_get_magic_string_size (id); return lit_get_magic_string_utf8 (id); } } JERRY_ASSERT (string_p->refs_and_container >= ECMA_STRING_REF_ONE); switch (ECMA_STRING_GET_CONTAINER (string_p)) { case ECMA_STRING_CONTAINER_HEAP_UTF8_STRING: { *size_p = ((ecma_short_string_t *) string_p)->size; return ECMA_SHORT_STRING_GET_BUFFER (string_p); } case ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING: { ecma_long_string_t *long_string_p = (ecma_long_string_t *) string_p; *size_p = long_string_p->size; return long_string_p->string_p; } case ECMA_STRING_CONTAINER_HEAP_ASCII_STRING: { *size_p = ECMA_ASCII_STRING_GET_SIZE (string_p); return ECMA_ASCII_STRING_GET_BUFFER (string_p); } default: { JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX); lit_magic_string_ex_id_t id = LIT_MAGIC_STRING__COUNT - string_p->u.magic_string_ex_id; *size_p = lit_get_magic_string_ex_size (id); return lit_get_magic_string_ex_utf8 (id); } } } /* ecma_string_get_chars_fast */ /** * Allocate new ecma-string and fill it with reference to ECMA magic string * * @return pointer to ecma-string descriptor */ static ecma_string_t * ecma_new_ecma_string_from_magic_string_ex_id (lit_magic_string_ex_id_t id) /**< identifier of external magic string */ { JERRY_ASSERT (id < lit_get_magic_string_ex_count ()); uintptr_t string_id = (uintptr_t) (id + LIT_MAGIC_STRING__COUNT); if (JERRY_LIKELY (string_id <= ECMA_DIRECT_STRING_MAX_IMM)) { return (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_MAGIC, string_id); } ecma_string_t *string_desc_p = ecma_alloc_string (); string_desc_p->refs_and_container = ECMA_STRING_CONTAINER_MAGIC_STRING_EX | ECMA_STRING_REF_ONE; string_desc_p->u.magic_string_ex_id = id + LIT_MAGIC_STRING__COUNT; return string_desc_p; } /* ecma_new_ecma_string_from_magic_string_ex_id */ /** * Allocate new ecma-string and fill it with reference to the symbol descriptor * * Note: * Takes the reference to the string_desc * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_symbol_from_descriptor_string (ecma_value_t string_desc) /**< ecma-string */ { JERRY_ASSERT (!ecma_is_value_symbol (string_desc)); ecma_extended_string_t *symbol_p = ecma_alloc_extended_string (); symbol_p->header.refs_and_container = ECMA_STRING_REF_ONE | ECMA_STRING_CONTAINER_SYMBOL; symbol_p->u.symbol_descriptor = string_desc; symbol_p->header.u.hash = (lit_string_hash_t) (((uintptr_t) symbol_p) & (uintptr_t) ~ECMA_SYMBOL_FLAGS_MASK); return (ecma_string_t *) symbol_p; } /* ecma_new_symbol_from_descriptor_string */ /** * Check whether an ecma-string contains an ecma-symbol * * @return true - if the ecma-string contains an ecma-symbol * false - otherwise */ bool ecma_prop_name_is_symbol (ecma_string_t *string_p) /**< ecma-string */ { JERRY_ASSERT (string_p != NULL); return (!ECMA_IS_DIRECT_STRING (string_p) && ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_SYMBOL); } /* ecma_prop_name_is_symbol */ /** * Allocate new UTF8 ecma-string and fill it with characters from the given utf8 buffer * * @return pointer to ecma-string descriptor */ static inline ecma_string_t * ecma_new_ecma_string_from_utf8_buffer (lit_utf8_size_t length, /**< length of the buffer */ lit_utf8_size_t size, /**< size of the buffer */ lit_utf8_byte_t **data_p) /**< [out] pointer to the start of the string buffer */ { if (JERRY_LIKELY (size <= UINT16_MAX)) { if (JERRY_LIKELY (length == size) && size <= (UINT8_MAX + 1)) { ecma_string_t *string_desc_p; string_desc_p = (ecma_string_t *) ecma_alloc_string_buffer (size + ECMA_ASCII_STRING_HEADER_SIZE); string_desc_p->refs_and_container = ECMA_STRING_CONTAINER_HEAP_ASCII_STRING | ECMA_STRING_REF_ONE; ECMA_ASCII_STRING_SET_SIZE (string_desc_p, size); *data_p = ECMA_ASCII_STRING_GET_BUFFER (string_desc_p); return (ecma_string_t *) string_desc_p; } ecma_short_string_t *string_desc_p; string_desc_p = (ecma_short_string_t *) ecma_alloc_string_buffer (size + sizeof (ecma_short_string_t)); string_desc_p->header.refs_and_container = ECMA_STRING_CONTAINER_HEAP_UTF8_STRING | ECMA_STRING_REF_ONE; string_desc_p->size = (uint16_t) size; string_desc_p->length = (uint16_t) length; *data_p = ECMA_SHORT_STRING_GET_BUFFER (string_desc_p); return (ecma_string_t *) string_desc_p; } ecma_long_string_t *long_string_p; long_string_p = (ecma_long_string_t *) ecma_alloc_string_buffer (size + sizeof (ecma_long_string_t)); long_string_p->header.refs_and_container = ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING | ECMA_STRING_REF_ONE; long_string_p->string_p = ECMA_LONG_STRING_BUFFER_START (long_string_p); long_string_p->size = size; long_string_p->length = length; *data_p = ECMA_LONG_STRING_BUFFER_START (long_string_p); return (ecma_string_t *) long_string_p; } /* ecma_new_ecma_string_from_utf8_buffer */ /** * Checks whether a string has a special representation, that is, the string is either a magic string, * an external magic string, or an uint32 number, and creates an ecma string using the special representation, * if available. * * @return pointer to ecma string with the special representation * NULL, if there is no special representation for the string */ static ecma_string_t * ecma_find_special_string (const lit_utf8_byte_t *string_p, /**< utf8 string */ lit_utf8_size_t string_size) /**< string size */ { JERRY_ASSERT (string_p != NULL || string_size == 0); lit_magic_string_id_t magic_string_id = lit_is_utf8_string_magic (string_p, string_size); if (magic_string_id != LIT_MAGIC_STRING__COUNT) { return ecma_get_magic_string (magic_string_id); } JERRY_ASSERT (string_size > 0); if (*string_p >= LIT_CHAR_0 && *string_p <= LIT_CHAR_9) { uint32_t array_index; if (ecma_string_to_array_index (string_p, string_size, &array_index)) { return ecma_new_ecma_string_from_uint32 (array_index); } } if (lit_get_magic_string_ex_count () > 0) { lit_magic_string_ex_id_t magic_string_ex_id = lit_is_ex_utf8_string_magic (string_p, string_size); if (magic_string_ex_id < lit_get_magic_string_ex_count ()) { return ecma_new_ecma_string_from_magic_string_ex_id (magic_string_ex_id); } } return NULL; } /* ecma_find_special_string */ /** * Allocate new ecma-string and fill it with characters from ascii characters * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_string_from_ascii (const lit_utf8_byte_t *string_p, /**< ascii string */ lit_utf8_size_t string_size) /**< string size */ { JERRY_ASSERT (string_p != NULL || string_size == 0); ecma_string_t *string_desc_p = ecma_find_special_string (string_p, string_size); if (string_desc_p != NULL) { return string_desc_p; } lit_utf8_byte_t *data_p; string_desc_p = ecma_new_ecma_string_from_utf8_buffer (string_size, string_size, &data_p); string_desc_p->u.hash = lit_utf8_string_calc_hash (string_p, string_size); memcpy (data_p, string_p, string_size); return string_desc_p; } /* ecma_new_ecma_string_from_ascii */ /** * Allocate new ecma-string and fill it with characters from the utf8 string * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_string_from_utf8 (const lit_utf8_byte_t *string_p, /**< utf-8 string */ lit_utf8_size_t string_size) /**< string size */ { JERRY_ASSERT (string_p != NULL || string_size == 0); JERRY_ASSERT (lit_is_valid_cesu8_string (string_p, string_size)); ecma_string_t *string_desc_p = ecma_find_special_string (string_p, string_size); if (string_desc_p != NULL) { return string_desc_p; } lit_utf8_byte_t *data_p; string_desc_p = ecma_new_ecma_string_from_utf8_buffer (lit_utf8_string_length (string_p, string_size), string_size, &data_p); string_desc_p->u.hash = lit_utf8_string_calc_hash (string_p, string_size); memcpy (data_p, string_p, string_size); return string_desc_p; } /* ecma_new_ecma_string_from_utf8 */ /** * Allocate a new ecma-string and initialize it from the utf8 string argument. * All 4-bytes long unicode sequences are converted into two 3-bytes long sequences. * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_string_from_utf8_converted_to_cesu8 (const lit_utf8_byte_t *string_p, /**< utf-8 string */ lit_utf8_size_t string_size) /**< utf-8 string size */ { JERRY_ASSERT (string_p != NULL || string_size == 0); lit_utf8_size_t converted_string_length = 0; lit_utf8_size_t converted_string_size = 0; lit_utf8_size_t pos = 0; /* Calculate the required length and size information of the converted cesu-8 encoded string */ while (pos < string_size) { if ((string_p[pos] & LIT_UTF8_1_BYTE_MASK) == LIT_UTF8_1_BYTE_MARKER) { pos++; } else if ((string_p[pos] & LIT_UTF8_2_BYTE_MASK) == LIT_UTF8_2_BYTE_MARKER) { pos += 2; } else if ((string_p[pos] & LIT_UTF8_3_BYTE_MASK) == LIT_UTF8_3_BYTE_MARKER) { pos += 3; } else { JERRY_ASSERT ((string_p[pos] & LIT_UTF8_4_BYTE_MASK) == LIT_UTF8_4_BYTE_MARKER); pos += 4; converted_string_size += 2; converted_string_length++; } converted_string_length++; } JERRY_ASSERT (pos == string_size); if (converted_string_size == 0) { return ecma_new_ecma_string_from_utf8 (string_p, string_size); } converted_string_size += string_size; JERRY_ASSERT (lit_is_valid_utf8_string (string_p, string_size, false)); lit_utf8_byte_t *data_p; ecma_string_t *string_desc_p = ecma_new_ecma_string_from_utf8_buffer (converted_string_length, converted_string_size, &data_p); const lit_utf8_byte_t *const begin_data_p = data_p; pos = 0; while (pos < string_size) { if ((string_p[pos] & LIT_UTF8_4_BYTE_MASK) == LIT_UTF8_4_BYTE_MARKER) { /* Processing 4 byte unicode sequence. Always converted to two 3 byte long sequence. */ lit_four_byte_utf8_char_to_cesu8 (data_p, string_p + pos); data_p += 3 * 2; pos += 4; } else { *data_p++ = string_p[pos++]; } } JERRY_ASSERT (pos == string_size); string_desc_p->u.hash = lit_utf8_string_calc_hash (begin_data_p, converted_string_size); return (ecma_string_t *) string_desc_p; } /* ecma_new_ecma_string_from_utf8_converted_to_cesu8 */ /** * Allocate new ecma-external-string and fill it with characters from the cesu8 string * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_external_string_from_cesu8 (const lit_utf8_byte_t *string_p, /**< cesu-8 string */ lit_utf8_size_t string_size, /**< string size */ void *user_p) /**< user pointer passed to the callback * when the string is freed */ { JERRY_ASSERT (string_p != NULL || string_size == 0); JERRY_ASSERT (lit_is_valid_cesu8_string (string_p, string_size)); if (string_size < (sizeof (ecma_external_string_t) - sizeof (ecma_short_string_t))) { /* Normal strings are created for short strings. */ ecma_string_t *string_desc_p = ecma_new_ecma_string_from_utf8 (string_p, string_size); jerry_external_string_free_cb_t free_cb = JERRY_CONTEXT (external_string_free_callback_p); if (free_cb != NULL) { free_cb ((lit_utf8_byte_t *) string_p, string_size, user_p); } return string_desc_p; } ecma_string_t *string_desc_p = ecma_find_special_string (string_p, string_size); if (string_desc_p != NULL) { jerry_external_string_free_cb_t free_cb = JERRY_CONTEXT (external_string_free_callback_p); if (free_cb != NULL) { free_cb ((lit_utf8_byte_t *) string_p, string_size, user_p); } return string_desc_p; } ecma_external_string_t *external_string_p = ecma_alloc_external_string (); ecma_long_string_t *long_string_p = (ecma_long_string_t *) external_string_p; long_string_p->header.refs_and_container = ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING | ECMA_STRING_REF_ONE; long_string_p->header.u.hash = lit_utf8_string_calc_hash (string_p, string_size); long_string_p->string_p = string_p; long_string_p->size = string_size; long_string_p->length = lit_utf8_string_length (string_p, string_size); external_string_p->user_p = user_p; return (ecma_string_t *) external_string_p; } /* ecma_new_ecma_external_string_from_cesu8 */ /** * Allocate new ecma-string and fill it with cesu-8 character which represents specified code unit * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_string_from_code_unit (ecma_char_t code_unit) /**< code unit */ { lit_utf8_byte_t lit_utf8_bytes[LIT_UTF8_MAX_BYTES_IN_CODE_UNIT]; lit_utf8_size_t bytes_size = lit_code_unit_to_utf8 (code_unit, lit_utf8_bytes); return ecma_new_ecma_string_from_utf8 (lit_utf8_bytes, bytes_size); } /* ecma_new_ecma_string_from_code_unit */ /** * Allocate new ecma-string and fill it with cesu-8 character which represents specified code units * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_string_from_code_units (ecma_char_t first_code_unit, /**< code unit */ ecma_char_t second_code_unit) /**< code unit */ { lit_utf8_byte_t lit_utf8_bytes[2 * LIT_UTF8_MAX_BYTES_IN_CODE_UNIT]; lit_utf8_size_t bytes_size = lit_code_unit_to_utf8 (first_code_unit, lit_utf8_bytes); bytes_size += lit_code_unit_to_utf8 (second_code_unit, lit_utf8_bytes + bytes_size); return ecma_new_ecma_string_from_utf8 (lit_utf8_bytes, bytes_size); } /* ecma_new_ecma_string_from_code_units */ /** * Allocate new ecma-string and fill it with ecma-number * * Note: the number cannot be represented as direct string * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_non_direct_string_from_uint32 (uint32_t uint32_number) /**< uint32 value of the string */ { JERRY_ASSERT (uint32_number > ECMA_DIRECT_STRING_MAX_IMM); ecma_string_t *string_p = ecma_alloc_string (); string_p->refs_and_container = ECMA_STRING_CONTAINER_UINT32_IN_DESC | ECMA_STRING_REF_ONE; string_p->u.uint32_number = uint32_number; return string_p; } /* ecma_new_non_direct_string_from_uint32 */ /** * Allocate new ecma-string and fill it with property length number * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_string_from_length (ecma_length_t number) /**< property length */ { if (JERRY_LIKELY (number <= ECMA_DIRECT_STRING_MAX_IMM)) { return (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_UINT, (uintptr_t) number); } JERRY_ASSERT ((ecma_number_t) number <= ECMA_NUMBER_MAX_SAFE_INTEGER); if (JERRY_UNLIKELY (number > UINT32_MAX)) { return ecma_new_ecma_string_from_number ((ecma_number_t) number); } return ecma_new_non_direct_string_from_uint32 ((uint32_t) number); } /* ecma_new_ecma_string_from_length */ /** * Allocate new ecma-string and fill it with uint32 number * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_string_from_uint32 (uint32_t uint32_number) /**< uint32 value of the string */ { if (JERRY_LIKELY (uint32_number <= ECMA_DIRECT_STRING_MAX_IMM)) { return (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_UINT, (uintptr_t) uint32_number); } return ecma_new_non_direct_string_from_uint32 (uint32_number); } /* ecma_new_ecma_string_from_uint32 */ /** * Returns the constant assigned to the uint32 number. * * Note: * Calling ecma_deref_ecma_string on the returned pointer is optional. * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_get_ecma_string_from_uint32 (uint32_t uint32_number) /**< input number */ { JERRY_ASSERT (uint32_number <= ECMA_DIRECT_STRING_MAX_IMM); return (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_UINT, (uintptr_t) uint32_number); } /* ecma_get_ecma_string_from_uint32 */ /** * Allocate new ecma-string and fill it with ecma-number * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_new_ecma_string_from_number (ecma_number_t num) /**< ecma-number */ { uint32_t uint32_num = ecma_number_to_uint32 (num); if (num == ((ecma_number_t) uint32_num)) { return ecma_new_ecma_string_from_uint32 (uint32_num); } if (ecma_number_is_nan (num)) { return ecma_get_magic_string (LIT_MAGIC_STRING_NAN); } if (ecma_number_is_infinity (num)) { lit_magic_string_id_t id = (ecma_number_is_negative (num) ? LIT_MAGIC_STRING_NEGATIVE_INFINITY_UL : LIT_MAGIC_STRING_INFINITY_UL); return ecma_get_magic_string (id); } lit_utf8_byte_t str_buf[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; lit_utf8_size_t str_size = ecma_number_to_utf8_string (num, str_buf, sizeof (str_buf)); JERRY_ASSERT (str_size > 0); #ifndef JERRY_NDEBUG JERRY_ASSERT (lit_is_utf8_string_magic (str_buf, str_size) == LIT_MAGIC_STRING__COUNT && lit_is_ex_utf8_string_magic (str_buf, str_size) == lit_get_magic_string_ex_count ()); #endif /* !JERRY_NDEBUG */ lit_utf8_byte_t *data_p; ecma_string_t *string_desc_p = ecma_new_ecma_string_from_utf8_buffer (lit_utf8_string_length (str_buf, str_size), str_size, &data_p); string_desc_p->u.hash = lit_utf8_string_calc_hash (str_buf, str_size); memcpy (data_p, str_buf, str_size); return string_desc_p; } /* ecma_new_ecma_string_from_number */ /** * Returns the constant assigned to the magic string id. * * Note: * Calling ecma_deref_ecma_string on the returned pointer is optional. * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_get_magic_string (lit_magic_string_id_t id) /**< identifier of magic string */ { JERRY_ASSERT (id < LIT_MAGIC_STRING__COUNT); return (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_MAGIC, (uintptr_t) id); } /* ecma_get_magic_string */ /** * Returns the constant assigned to the internal magic string id. * * Note: * Calling ecma_deref_ecma_string on the returned pointer is optional. * * @return pointer to ecma-string descriptor */ ecma_string_t * ecma_get_internal_string (lit_magic_string_id_t id) /**< identifier of magic string */ { JERRY_ASSERT (id >= LIT_NON_INTERNAL_MAGIC_STRING__COUNT && id < LIT_MAGIC_STRING__COUNT); return (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_SPECIAL, (uintptr_t) id); } /* ecma_get_internal_string */ /** * Append a cesu8 string after an ecma-string * * Note: * The string1_p argument is freed. If it needs to be preserved, * call ecma_ref_ecma_string with string1_p before the call. * * @return concatenation of an ecma-string and a cesu8 string */ ecma_string_t * ecma_append_chars_to_string (ecma_string_t *string1_p, /**< base ecma-string */ const lit_utf8_byte_t *cesu8_string2_p, /**< characters to be appended */ lit_utf8_size_t cesu8_string2_size, /**< byte size of cesu8_string2_p */ lit_utf8_size_t cesu8_string2_length) /**< character length of cesu8_string2_p */ { JERRY_ASSERT (string1_p != NULL && cesu8_string2_size > 0 && cesu8_string2_length > 0); if (JERRY_UNLIKELY (ecma_string_is_empty (string1_p))) { return ecma_new_ecma_string_from_utf8 (cesu8_string2_p, cesu8_string2_size); } lit_utf8_size_t cesu8_string1_size; lit_utf8_size_t cesu8_string1_length; uint8_t flags = ECMA_STRING_FLAG_IS_ASCII; lit_utf8_byte_t uint32_to_string_buffer[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; const lit_utf8_byte_t *cesu8_string1_p = ecma_string_get_chars (string1_p, &cesu8_string1_size, &cesu8_string1_length, uint32_to_string_buffer, &flags); JERRY_ASSERT (!(flags & ECMA_STRING_FLAG_MUST_BE_FREED)); JERRY_ASSERT (cesu8_string1_length > 0); JERRY_ASSERT (cesu8_string1_length <= cesu8_string1_size); lit_utf8_size_t new_size = cesu8_string1_size + cesu8_string2_size; /* Poor man's carry flag check: it is impossible to allocate this large string. */ if (new_size < (cesu8_string1_size | cesu8_string2_size)) { jerry_fatal (JERRY_FATAL_OUT_OF_MEMORY); } lit_magic_string_id_t magic_string_id; magic_string_id = lit_is_utf8_string_pair_magic (cesu8_string1_p, cesu8_string1_size, cesu8_string2_p, cesu8_string2_size); if (magic_string_id != LIT_MAGIC_STRING__COUNT) { ecma_deref_ecma_string (string1_p); return ecma_get_magic_string (magic_string_id); } if ((flags & ECMA_STRING_FLAG_IS_UINT32) && new_size <= ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32) { memcpy (uint32_to_string_buffer + cesu8_string1_size, cesu8_string2_p, cesu8_string2_size); uint32_t array_index; if (ecma_string_to_array_index (uint32_to_string_buffer, new_size, &array_index)) { ecma_deref_ecma_string (string1_p); return ecma_new_ecma_string_from_uint32 (array_index); } } if (lit_get_magic_string_ex_count () > 0) { lit_magic_string_ex_id_t magic_string_ex_id; magic_string_ex_id = lit_is_ex_utf8_string_pair_magic (cesu8_string1_p, cesu8_string1_size, cesu8_string2_p, cesu8_string2_size); if (magic_string_ex_id < lit_get_magic_string_ex_count ()) { ecma_deref_ecma_string (string1_p); return ecma_new_ecma_string_from_magic_string_ex_id (magic_string_ex_id); } } lit_utf8_byte_t *data_p; ecma_string_t *string_desc_p = ecma_new_ecma_string_from_utf8_buffer (cesu8_string1_length + cesu8_string2_length, new_size, &data_p); lit_string_hash_t hash_start; if (JERRY_UNLIKELY (flags & ECMA_STRING_FLAG_REHASH_NEEDED)) { hash_start = lit_utf8_string_calc_hash (cesu8_string1_p, cesu8_string1_size); } else { JERRY_ASSERT (!ECMA_IS_DIRECT_STRING (string1_p)); hash_start = string1_p->u.hash; } string_desc_p->u.hash = lit_utf8_string_hash_combine (hash_start, cesu8_string2_p, cesu8_string2_size); memcpy (data_p, cesu8_string1_p, cesu8_string1_size); memcpy (data_p + cesu8_string1_size, cesu8_string2_p, cesu8_string2_size); ecma_deref_ecma_string (string1_p); return (ecma_string_t *) string_desc_p; } /* ecma_append_chars_to_string */ /** * Concatenate ecma-strings * * Note: * The string1_p argument is freed. If it needs to be preserved, * call ecma_ref_ecma_string with string1_p before the call. * * @return concatenation of two ecma-strings */ ecma_string_t * ecma_concat_ecma_strings (ecma_string_t *string1_p, /**< first ecma-string */ ecma_string_t *string2_p) /**< second ecma-string */ { JERRY_ASSERT (string1_p != NULL && string2_p != NULL); if (JERRY_UNLIKELY (ecma_string_is_empty (string1_p))) { ecma_ref_ecma_string (string2_p); return string2_p; } else if (JERRY_UNLIKELY (ecma_string_is_empty (string2_p))) { return string1_p; } lit_utf8_size_t cesu8_string2_size; lit_utf8_size_t cesu8_string2_length; lit_utf8_byte_t uint32_to_string_buffer[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; uint8_t flags = ECMA_STRING_FLAG_IS_ASCII; const lit_utf8_byte_t *cesu8_string2_p = ecma_string_get_chars (string2_p, &cesu8_string2_size, &cesu8_string2_length, uint32_to_string_buffer, &flags); JERRY_ASSERT (cesu8_string2_p != NULL); ecma_string_t *result_p = ecma_append_chars_to_string (string1_p, cesu8_string2_p, cesu8_string2_size, cesu8_string2_length); JERRY_ASSERT (!(flags & ECMA_STRING_FLAG_MUST_BE_FREED)); return result_p; } /* ecma_concat_ecma_strings */ /** * Increase reference counter of non-direct ecma-string. * * @return void */ void ecma_ref_ecma_string_non_direct (ecma_string_t *string_p) /**< string descriptor */ { JERRY_ASSERT (string_p != NULL); JERRY_ASSERT (!ECMA_IS_DIRECT_STRING (string_p)); #ifdef JERRY_NDEBUG if (ECMA_STRING_IS_STATIC (string_p)) { return; } #endif /* JERRY_NDEBUG */ JERRY_ASSERT (string_p->refs_and_container >= ECMA_STRING_REF_ONE); if (JERRY_LIKELY (string_p->refs_and_container < ECMA_STRING_MAX_REF)) { /* Increase reference counter. */ string_p->refs_and_container += ECMA_STRING_REF_ONE; } else { jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT); } } /* ecma_ref_ecma_string_non_direct */ /** * Increase reference counter of ecma-string. */ void ecma_ref_ecma_string (ecma_string_t *string_p) /**< string descriptor */ { JERRY_ASSERT (string_p != NULL); if (ECMA_IS_DIRECT_STRING (string_p)) { return; } ecma_ref_ecma_string_non_direct (string_p); } /* ecma_ref_ecma_string */ /** * Decrease reference counter and deallocate a non-direct ecma-string * if the counter becomes zero. * * @return void */ void ecma_deref_ecma_string_non_direct (ecma_string_t *string_p) /**< ecma-string */ { JERRY_ASSERT (!ECMA_IS_DIRECT_STRING (string_p)); #ifdef JERRY_NDEBUG if (ECMA_STRING_IS_STATIC (string_p)) { return; } #endif /* JERRY_NDEBUG */ JERRY_ASSERT (string_p->refs_and_container >= ECMA_STRING_REF_ONE); /* Decrease reference counter. */ string_p->refs_and_container -= ECMA_STRING_REF_ONE; if (string_p->refs_and_container >= ECMA_STRING_REF_ONE) { return; } ecma_destroy_ecma_string (string_p); } /* ecma_deref_ecma_string_non_direct */ /** * Decrease reference counter and deallocate ecma-string * if the counter becomes zero. */ void ecma_deref_ecma_string (ecma_string_t *string_p) /**< ecma-string */ { JERRY_ASSERT (string_p != NULL); if (ECMA_IS_DIRECT_STRING (string_p)) { return; } ecma_deref_ecma_string_non_direct (string_p); } /* ecma_deref_ecma_string */ /** * Deallocate an ecma-string */ void ecma_destroy_ecma_string (ecma_string_t *string_p) /**< ecma-string */ { JERRY_ASSERT (string_p != NULL); JERRY_ASSERT (!ECMA_IS_DIRECT_STRING (string_p)); JERRY_ASSERT ((string_p->refs_and_container < ECMA_STRING_REF_ONE) || ECMA_STRING_IS_STATIC (string_p)); switch (ECMA_STRING_GET_CONTAINER (string_p)) { case ECMA_STRING_CONTAINER_HEAP_UTF8_STRING: { ecma_dealloc_string_buffer (string_p, ((ecma_short_string_t *) string_p)->size + sizeof (ecma_short_string_t)); return; } case ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING: { ecma_long_string_t *long_string_p = (ecma_long_string_t *) string_p; if (long_string_p->string_p == ECMA_LONG_STRING_BUFFER_START (long_string_p)) { ecma_dealloc_string_buffer (string_p, long_string_p->size + sizeof (ecma_long_string_t)); return; } ecma_external_string_t *external_string_p = (ecma_external_string_t *) string_p; jerry_external_string_free_cb_t free_cb = JERRY_CONTEXT (external_string_free_callback_p); if (free_cb != NULL) { free_cb ((lit_utf8_byte_t *) external_string_p->header.string_p, external_string_p->header.size, external_string_p->user_p); } ecma_dealloc_external_string (external_string_p); return; } case ECMA_STRING_CONTAINER_HEAP_ASCII_STRING: { ecma_dealloc_string_buffer (string_p, ECMA_ASCII_STRING_GET_SIZE (string_p) + ECMA_ASCII_STRING_HEADER_SIZE); return; } case ECMA_STRING_CONTAINER_SYMBOL: { ecma_extended_string_t *symbol_p = (ecma_extended_string_t *) string_p; ecma_free_value (symbol_p->u.symbol_descriptor); ecma_dealloc_extended_string (symbol_p); return; } default: { JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC || ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX); /* only the string descriptor itself should be freed */ ecma_dealloc_string (string_p); } } } /* ecma_destroy_ecma_string */ /** * Convert ecma-string to number * * @return converted ecma-number */ ecma_number_t ecma_string_to_number (const ecma_string_t *string_p) /**< ecma-string */ { JERRY_ASSERT (string_p != NULL); if (ECMA_IS_DIRECT_STRING (string_p)) { if (ECMA_IS_DIRECT_STRING_WITH_TYPE (string_p, ECMA_DIRECT_STRING_UINT)) { return (ecma_number_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); } } else if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC) { return ((ecma_number_t) string_p->u.uint32_number); } lit_utf8_size_t size; const lit_utf8_byte_t *chars_p = ecma_string_get_chars_fast (string_p, &size); JERRY_ASSERT (chars_p != NULL); if (size == 0) { return ECMA_NUMBER_ZERO; } return ecma_utf8_string_to_number (chars_p, size, 0); } /* ecma_string_to_number */ /** * Check if string is array index. * * @return ECMA_STRING_NOT_ARRAY_INDEX if string is not array index * the array index otherwise */ uint32_t ecma_string_get_array_index (const ecma_string_t *str_p) /**< ecma-string */ { if (ECMA_IS_DIRECT_STRING (str_p)) { if (ECMA_IS_DIRECT_STRING_WITH_TYPE (str_p, ECMA_DIRECT_STRING_UINT)) { /* Value cannot be equal to the maximum value of a 32 bit unsigned number. */ return (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (str_p); } return ECMA_STRING_NOT_ARRAY_INDEX; } if (ECMA_STRING_GET_CONTAINER (str_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC) { /* When the uint32_number is equal to the maximum value of 32 bit unsigned integer number, * it is also an invalid array index. The comparison to ECMA_STRING_NOT_ARRAY_INDEX will * be true in this case. */ return str_p->u.uint32_number; } return ECMA_STRING_NOT_ARRAY_INDEX; } /* ecma_string_get_array_index */ /** * Copy digits of uint32 number, truncating if buffer is not large enough. * * @return number of digits copied */ static lit_utf8_size_t ecma_uint32_to_buffer (uint32_t num, /**< number */ lit_utf8_byte_t *buffer_p /**< destination buffer */, lit_utf8_size_t buffer_size /**< buffer size */) { lit_utf8_byte_t digits[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; lit_utf8_size_t digit_count = ecma_uint32_to_utf8_string (num, digits, ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); digit_count = JERRY_MIN (buffer_size, digit_count); memcpy (buffer_p, digits, digit_count); return digit_count; } /* ecma_uint32_to_buffer */ /** * Convert ecma-string's contents to the specified encoding and copy it to the buffer. * String data will be truncated to fit the buffer. * * @return number of bytes copied to the buffer. */ lit_utf8_size_t ecma_string_copy_to_buffer (const ecma_string_t *string_p, /**< ecma-string descriptor */ lit_utf8_byte_t *buffer_p, /**< destination buffer pointer * (can be NULL if buffer_size == 0) */ lit_utf8_size_t buffer_size, /**< size of buffer */ jerry_encoding_t encoding) /**< encoding */ { if (ECMA_IS_DIRECT_STRING (string_p)) { if (ECMA_IS_DIRECT_STRING_WITH_TYPE (string_p, ECMA_DIRECT_STRING_UINT)) { return ecma_uint32_to_buffer ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p), buffer_p, buffer_size); } } else { JERRY_ASSERT (string_p->refs_and_container >= ECMA_STRING_REF_ONE); if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC) { return ecma_uint32_to_buffer (string_p->u.uint32_number, buffer_p, buffer_size); } } lit_utf8_size_t string_size; const lit_utf8_byte_t *chars_p = ecma_string_get_chars_fast (string_p, &string_size); lit_utf8_size_t copy_size = 0; JERRY_ASSERT (chars_p != NULL); switch (encoding) { case JERRY_ENCODING_CESU8: { copy_size = JERRY_MIN (string_size, buffer_size); if (copy_size < string_size) { /* Do not copy partial characters */ while ((chars_p[copy_size] & LIT_UTF8_EXTRA_BYTE_MASK) == LIT_UTF8_EXTRA_BYTE_MARKER) { copy_size--; } } memcpy (buffer_p, chars_p, copy_size); break; } case JERRY_ENCODING_UTF8: { if (string_size == ecma_string_get_length (string_p)) { copy_size = JERRY_MIN (string_size, buffer_size); memcpy (buffer_p, chars_p, copy_size); break; } copy_size = lit_convert_cesu8_string_to_utf8_string (chars_p, string_size, buffer_p, buffer_size); break; } default: { break; } } return copy_size; } /* ecma_string_copy_to_buffer */ /** * Convert ecma-string's contents to a cesu-8 string and put it to the buffer. * It is the caller's responsibility to make sure that the string fits in the buffer. * Check if the size of the string is equal with the size of the buffer. * * @return void */ void ecma_string_to_cesu8_bytes (const ecma_string_t *string_desc_p, /**< ecma-string descriptor */ lit_utf8_byte_t *buffer_p, /**< destination buffer pointer (can be NULL if buffer_size == 0) */ lit_utf8_size_t buffer_size) /**< size of buffer */ { ecma_string_copy_to_buffer (string_desc_p, buffer_p, buffer_size, JERRY_ENCODING_CESU8); } /** * Get size of the uint32 number stored locally in the string's descriptor * * Note: the represented number size and length are equal * * @return size in bytes */ static inline lit_utf8_size_t ecma_string_get_uint32_size (const uint32_t uint32_number) /**< number in the string-descriptor */ { uint32_t prev_number = 1; uint32_t next_number = 100; lit_utf8_size_t size = 1; const uint32_t max_size = 9; while (size < max_size && uint32_number >= next_number) { prev_number = next_number; next_number *= 100; size += 2; } if (uint32_number >= prev_number * 10) { size++; } return size; } /* ecma_string_get_uint32_size */ /** * Checks whether the given string is a sequence of ascii characters. */ #define ECMA_STRING_IS_ASCII(char_p, size) ((size) == lit_utf8_string_length ((char_p), (size))) /** * Returns with the cesu8 character array of a string. * * Note: * - This function returns with a newly allocated buffer for uint32 strings, * which must be freed if the optional uint32_buff_p parameter is NULL. * - The ASCII check only happens if the flags parameter gets * 'ECMA_STRING_FLAG_IS_ASCII' as an input. * * @return start of cesu8 characters */ const lit_utf8_byte_t * ecma_string_get_chars (const ecma_string_t *string_p, /**< ecma-string */ lit_utf8_size_t *size_p, /**< [out] size of the ecma string */ lit_utf8_size_t *length_p, /**< [out] optional argument. If the pointer is not NULL the pointed * memory area is filled with the length of the ecma string */ lit_utf8_byte_t *uint32_buff_p, /**< [out] optional argument. If the pointer is not NULL the * pointed memory area is filled with the string converted * uint32 string descriptor */ uint8_t *flags_p) /**< [in,out] any combination of ecma_string_flag_t bits */ { lit_utf8_size_t length; lit_utf8_size_t size; const lit_utf8_byte_t *result_p; if (ECMA_IS_DIRECT_STRING (string_p)) { *flags_p |= ECMA_STRING_FLAG_REHASH_NEEDED; switch (ECMA_GET_DIRECT_STRING_TYPE (string_p)) { case ECMA_DIRECT_STRING_MAGIC: { uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); if (id >= LIT_MAGIC_STRING__COUNT) { id -= LIT_MAGIC_STRING__COUNT; size = lit_get_magic_string_ex_size (id); result_p = lit_get_magic_string_ex_utf8 (id); length = 0; if (JERRY_UNLIKELY (*flags_p & ECMA_STRING_FLAG_IS_ASCII)) { length = lit_utf8_string_length (result_p, size); } } else { size = lit_get_magic_string_size (id); length = size; result_p = lit_get_magic_string_utf8 (id); /* All magic strings must be ascii strings. */ JERRY_ASSERT (ECMA_STRING_IS_ASCII (result_p, size)); } break; } default: { JERRY_ASSERT (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_UINT); uint32_t uint32_number = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); size = (lit_utf8_size_t) ecma_string_get_uint32_size (uint32_number); if (uint32_buff_p != NULL) { result_p = uint32_buff_p; } else { result_p = (const lit_utf8_byte_t *) jmem_heap_alloc_block (size); *flags_p |= ECMA_STRING_FLAG_MUST_BE_FREED; } length = ecma_uint32_to_utf8_string (uint32_number, (lit_utf8_byte_t *) result_p, size); JERRY_ASSERT (length == size); *flags_p |= ECMA_STRING_FLAG_IS_UINT32; break; } } } else { JERRY_ASSERT (string_p->refs_and_container >= ECMA_STRING_REF_ONE); switch (ECMA_STRING_GET_CONTAINER (string_p)) { case ECMA_STRING_CONTAINER_HEAP_UTF8_STRING: { ecma_short_string_t *short_string_p = (ecma_short_string_t *) string_p; size = short_string_p->size; length = short_string_p->length; result_p = ECMA_SHORT_STRING_GET_BUFFER (short_string_p); break; } case ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING: { ecma_long_string_t *long_string_desc_p = (ecma_long_string_t *) string_p; size = long_string_desc_p->size; length = long_string_desc_p->length; result_p = long_string_desc_p->string_p; break; } case ECMA_STRING_CONTAINER_HEAP_ASCII_STRING: { size = ECMA_ASCII_STRING_GET_SIZE (string_p); length = size; result_p = ECMA_ASCII_STRING_GET_BUFFER (string_p); break; } case ECMA_STRING_CONTAINER_UINT32_IN_DESC: { size = (lit_utf8_size_t) ecma_string_get_uint32_size (string_p->u.uint32_number); if (uint32_buff_p != NULL) { result_p = uint32_buff_p; } else { result_p = (const lit_utf8_byte_t *) jmem_heap_alloc_block (size); *flags_p |= ECMA_STRING_FLAG_MUST_BE_FREED; } length = ecma_uint32_to_utf8_string (string_p->u.uint32_number, (lit_utf8_byte_t *) result_p, size); JERRY_ASSERT (length == size); *flags_p |= ECMA_STRING_FLAG_IS_UINT32 | ECMA_STRING_FLAG_REHASH_NEEDED; break; } default: { JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX); lit_magic_string_ex_id_t id = LIT_MAGIC_STRING__COUNT - string_p->u.magic_string_ex_id; size = lit_get_magic_string_ex_size (id); length = 0; if (JERRY_UNLIKELY (*flags_p & ECMA_STRING_FLAG_IS_ASCII)) { length = lit_utf8_string_length (lit_get_magic_string_ex_utf8 (id), size); } result_p = lit_get_magic_string_ex_utf8 (id); *flags_p |= ECMA_STRING_FLAG_REHASH_NEEDED; break; } } } *size_p = size; if (length_p != NULL) { *length_p = length; } if ((*flags_p & ECMA_STRING_FLAG_IS_ASCII) && length != size) { *flags_p = (uint8_t) (*flags_p & (uint8_t) ~ECMA_STRING_FLAG_IS_ASCII); } return result_p; } /* ecma_string_get_chars */ /** * Checks whether the string equals to the magic string id. * * @return true - if the string equals to the magic string id * false - otherwise */ bool ecma_compare_ecma_string_to_magic_id (const ecma_string_t *string_p, /**< property name */ lit_magic_string_id_t id) /**< magic string id */ { return (string_p == ecma_get_magic_string (id)); } /* ecma_compare_ecma_string_to_magic_id */ /** * Checks whether ecma string is empty or not * * @return true - if the string is an empty string * false - otherwise */ bool ecma_string_is_empty (const ecma_string_t *string_p) /**< ecma-string */ { return ecma_compare_ecma_string_to_magic_id (string_p, LIT_MAGIC_STRING__EMPTY); } /* ecma_string_is_empty */ /** * Checks whether the string equals to "length". * * @return true - if the string equals to "length" * false - otherwise */ bool ecma_string_is_length (const ecma_string_t *string_p) /**< property name */ { return ecma_compare_ecma_string_to_magic_id (string_p, LIT_MAGIC_STRING_LENGTH); } /* ecma_string_is_length */ /** * Converts a property name into a string * * @return pointer to the converted ecma string */ static inline ecma_string_t * ecma_property_to_string (ecma_property_t property, /**< property name type */ jmem_cpointer_t prop_name_cp) /**< property name compressed pointer */ { uintptr_t property_string = ((uintptr_t) (property)) & (0x3 << ECMA_PROPERTY_NAME_TYPE_SHIFT); property_string = (property_string >> ECMA_STRING_TYPE_CONVERSION_SHIFT) | ECMA_TYPE_DIRECT_STRING; return (ecma_string_t *) (property_string | (((uintptr_t) prop_name_cp) << ECMA_DIRECT_STRING_SHIFT)); } /* ecma_property_to_string */ /** * Converts a string into a property name * * @return the compressed pointer part of the name */ jmem_cpointer_t ecma_string_to_property_name (ecma_string_t *prop_name_p, /**< property name */ ecma_property_t *name_type_p) /**< [out] property name type */ { if (ECMA_IS_DIRECT_STRING (prop_name_p)) { *name_type_p = (ecma_property_t) ECMA_DIRECT_STRING_TYPE_TO_PROP_NAME_TYPE (prop_name_p); return (jmem_cpointer_t) ECMA_GET_DIRECT_STRING_VALUE (prop_name_p); } *name_type_p = ECMA_DIRECT_STRING_PTR << ECMA_PROPERTY_NAME_TYPE_SHIFT; ecma_ref_ecma_string (prop_name_p); jmem_cpointer_t prop_name_cp; ECMA_SET_NON_NULL_POINTER (prop_name_cp, prop_name_p); return prop_name_cp; } /* ecma_string_to_property_name */ /** * Converts a property name into a string * * @return the string pointer * string must be released with ecma_deref_ecma_string */ ecma_string_t * ecma_string_from_property_name (ecma_property_t property, /**< property name type */ jmem_cpointer_t prop_name_cp) /**< property name compressed pointer */ { if (ECMA_PROPERTY_GET_NAME_TYPE (property) != ECMA_DIRECT_STRING_PTR) { return ecma_property_to_string (property, prop_name_cp); } ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_name_cp); ecma_ref_ecma_string (prop_name_p); return prop_name_p; } /* ecma_string_from_property_name */ /** * Get hash code of property name * * @return hash code of property name */ lit_string_hash_t ecma_string_get_property_name_hash (ecma_property_t property, /**< property name type */ jmem_cpointer_t prop_name_cp) /**< property name compressed pointer */ { if (ECMA_PROPERTY_GET_NAME_TYPE (property) == ECMA_DIRECT_STRING_PTR) { ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_name_cp); return prop_name_p->u.hash; } return (lit_string_hash_t) prop_name_cp; } /* ecma_string_get_property_name_hash */ /** * Check if property name is array index. * * @return ECMA_STRING_NOT_ARRAY_INDEX if string is not array index * the array index otherwise */ uint32_t ecma_string_get_property_index (ecma_property_t property, /**< property name type */ jmem_cpointer_t prop_name_cp) /**< property name compressed pointer */ { switch (ECMA_PROPERTY_GET_NAME_TYPE (property)) { case ECMA_DIRECT_STRING_UINT: { return (uint32_t) prop_name_cp; } case ECMA_DIRECT_STRING_PTR: { ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_name_cp); return ecma_string_get_array_index (prop_name_p); } default: { return ECMA_STRING_NOT_ARRAY_INDEX; } } } /* ecma_string_get_property_index */ /** * Compare a property name to a string * * @return true if they are equals * false otherwise */ bool ecma_string_compare_to_property_name (ecma_property_t property, /**< property name type */ jmem_cpointer_t prop_name_cp, /**< property name compressed pointer */ const ecma_string_t *string_p) /**< other string */ { if (ECMA_PROPERTY_GET_NAME_TYPE (property) != ECMA_DIRECT_STRING_PTR) { return ecma_property_to_string (property, prop_name_cp) == string_p; } if (ECMA_IS_DIRECT_STRING (string_p)) { return false; } ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_name_cp); return ecma_compare_ecma_non_direct_strings (prop_name_p, string_p); } /* ecma_string_compare_to_property_name */ /** * Helper for ecma_compare_ecma_strings_longpath to get string data * * @return string characters */ static const lit_utf8_byte_t * ecma_compare_get_string_chars (const ecma_string_t *string_p, /**< ecma-string */ lit_utf8_size_t *size_and_length_p) /**< [out] size and length */ { switch (ECMA_STRING_GET_CONTAINER (string_p)) { case ECMA_STRING_CONTAINER_HEAP_UTF8_STRING: { ecma_short_string_t *short_string_p = (ecma_short_string_t *) string_p; size_and_length_p[0] = short_string_p->size; size_and_length_p[1] = short_string_p->length; return ECMA_SHORT_STRING_GET_BUFFER (string_p); } case ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING: { ecma_long_string_t *long_string_p = (ecma_long_string_t *) string_p; size_and_length_p[0] = long_string_p->size; size_and_length_p[1] = long_string_p->length; return long_string_p->string_p; } case ECMA_STRING_CONTAINER_HEAP_ASCII_STRING: { size_and_length_p[0] = ECMA_ASCII_STRING_GET_SIZE (string_p); size_and_length_p[1] = size_and_length_p[0]; return ECMA_ASCII_STRING_GET_BUFFER (string_p); } default: { return NULL; } } } /* ecma_compare_get_string_chars */ /** * Long path part of ecma-string to ecma-string comparison routine * * See also: * ecma_compare_ecma_strings * * @return true - if strings are equal; * false - otherwise */ static bool JERRY_ATTR_NOINLINE ecma_compare_ecma_strings_longpath (const ecma_string_t *string1_p, /**< ecma-string */ const ecma_string_t *string2_p) /**< ecma-string */ { const lit_utf8_byte_t *utf8_string1_p, *utf8_string2_p; lit_utf8_size_t string1_size_and_length[2] = { 0, 0 }, string2_size_and_length[2] = { 0, 0 }; utf8_string1_p = ecma_compare_get_string_chars (string1_p, string1_size_and_length); utf8_string2_p = ecma_compare_get_string_chars (string2_p, string2_size_and_length); if (utf8_string1_p == NULL || utf8_string2_p == NULL) { return false; } if (string1_size_and_length[0] != string2_size_and_length[0] || string1_size_and_length[1] != string2_size_and_length[1]) { return false; } return !memcmp ((char *) utf8_string1_p, (char *) utf8_string2_p, string1_size_and_length[0]); } /* ecma_compare_ecma_strings_longpath */ /** * Compare two ecma-strings * * @return true - if strings are equal; * false - otherwise */ bool ecma_compare_ecma_strings (const ecma_string_t *string1_p, /**< ecma-string */ const ecma_string_t *string2_p) /**< ecma-string */ { JERRY_ASSERT (string1_p != NULL && string2_p != NULL); /* Fast paths first. */ if (string1_p == string2_p) { return true; } /* Either string is direct, return with false. */ if (ECMA_IS_DIRECT_STRING (((uintptr_t) string1_p) | ((uintptr_t) string2_p))) { return false; } /* Also compares uint32 values in descriptor. */ if (string1_p->u.hash != string2_p->u.hash) { return false; } if (ECMA_STRING_GET_CONTAINER (string1_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC) { return ECMA_STRING_GET_CONTAINER (string2_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC; } return ecma_compare_ecma_strings_longpath (string1_p, string2_p); } /* ecma_compare_ecma_strings */ /** * Compare two non-direct ecma-strings * * @return true - if strings are equal; * false - otherwise */ bool ecma_compare_ecma_non_direct_strings (const ecma_string_t *string1_p, /**< ecma-string */ const ecma_string_t *string2_p) /**< ecma-string */ { JERRY_ASSERT (string1_p != NULL && string2_p != NULL); JERRY_ASSERT (!ECMA_IS_DIRECT_STRING (string1_p) && !ECMA_IS_DIRECT_STRING (string2_p)); /* Fast paths first. */ if (string1_p == string2_p) { return true; } if (string1_p->u.hash != string2_p->u.hash) { return false; } if (ECMA_STRING_GET_CONTAINER (string1_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC) { return ECMA_STRING_GET_CONTAINER (string2_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC; } return ecma_compare_ecma_strings_longpath (string1_p, string2_p); } /* ecma_compare_ecma_non_direct_strings */ /** * Relational compare of ecma-strings. * * First string is less than second string if: * - strings are not equal; * - first string is prefix of second or is lexicographically less than second. * * @return true - if first string is less than second string, * false - otherwise */ bool ecma_compare_ecma_strings_relational (const ecma_string_t *string1_p, /**< ecma-string */ const ecma_string_t *string2_p) /**< ecma-string */ { if (ecma_compare_ecma_strings (string1_p, string2_p)) { return false; } const lit_utf8_byte_t *utf8_string1_p, *utf8_string2_p; lit_utf8_size_t utf8_string1_size, utf8_string2_size; lit_utf8_byte_t uint32_to_string_buffer1[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; lit_utf8_byte_t uint32_to_string_buffer2[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; if (ECMA_IS_DIRECT_STRING (string1_p)) { if (ECMA_GET_DIRECT_STRING_TYPE (string1_p) != ECMA_DIRECT_STRING_UINT) { utf8_string1_p = ecma_string_get_chars_fast (string1_p, &utf8_string1_size); } else { utf8_string1_size = ecma_uint32_to_utf8_string ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string1_p), uint32_to_string_buffer1, ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); utf8_string1_p = uint32_to_string_buffer1; } } else { JERRY_ASSERT (string1_p->refs_and_container >= ECMA_STRING_REF_ONE); if (ECMA_STRING_GET_CONTAINER (string1_p) != ECMA_STRING_CONTAINER_UINT32_IN_DESC) { utf8_string1_p = ecma_string_get_chars_fast (string1_p, &utf8_string1_size); } else { utf8_string1_size = ecma_uint32_to_utf8_string (string1_p->u.uint32_number, uint32_to_string_buffer1, ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); utf8_string1_p = uint32_to_string_buffer1; } } if (ECMA_IS_DIRECT_STRING (string2_p)) { if (ECMA_GET_DIRECT_STRING_TYPE (string2_p) != ECMA_DIRECT_STRING_UINT) { utf8_string2_p = ecma_string_get_chars_fast (string2_p, &utf8_string2_size); } else { utf8_string2_size = ecma_uint32_to_utf8_string ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string2_p), uint32_to_string_buffer2, ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); utf8_string2_p = uint32_to_string_buffer2; } } else { JERRY_ASSERT (string2_p->refs_and_container >= ECMA_STRING_REF_ONE); if (ECMA_STRING_GET_CONTAINER (string2_p) != ECMA_STRING_CONTAINER_UINT32_IN_DESC) { utf8_string2_p = ecma_string_get_chars_fast (string2_p, &utf8_string2_size); } else { utf8_string2_size = ecma_uint32_to_utf8_string (string2_p->u.uint32_number, uint32_to_string_buffer2, ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); utf8_string2_p = uint32_to_string_buffer2; } } return lit_compare_utf8_strings_relational (utf8_string1_p, utf8_string1_size, utf8_string2_p, utf8_string2_size); } /* ecma_compare_ecma_strings_relational */ /** * Special value to represent that no size is available. */ #define ECMA_STRING_NO_ASCII_SIZE 0xffffffff /** * Return the size of uint32 and magic strings. * The length of these strings are equal to their size. * * @return number of characters in the string */ static lit_utf8_size_t ecma_string_get_ascii_size (const ecma_string_t *string_p) /**< ecma-string */ { if (ECMA_IS_DIRECT_STRING (string_p)) { switch (ECMA_GET_DIRECT_STRING_TYPE (string_p)) { case ECMA_DIRECT_STRING_MAGIC: { uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); if (id >= LIT_MAGIC_STRING__COUNT) { return ECMA_STRING_NO_ASCII_SIZE; } JERRY_ASSERT (ECMA_STRING_IS_ASCII (lit_get_magic_string_utf8 (id), lit_get_magic_string_size (id))); return lit_get_magic_string_size (id); } default: { JERRY_ASSERT (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_UINT); uint32_t uint32_number = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); return ecma_string_get_uint32_size (uint32_number); } } } JERRY_ASSERT (string_p->refs_and_container >= ECMA_STRING_REF_ONE); if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC) { return ecma_string_get_uint32_size (string_p->u.uint32_number); } else if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_HEAP_ASCII_STRING) { return ECMA_ASCII_STRING_GET_SIZE (string_p); } return ECMA_STRING_NO_ASCII_SIZE; } /* ecma_string_get_ascii_size */ /** * Get length of ecma-string * * @return number of characters in the string */ lit_utf8_size_t ecma_string_get_length (const ecma_string_t *string_p) /**< ecma-string */ { lit_utf8_size_t length = ecma_string_get_ascii_size (string_p); if (length != ECMA_STRING_NO_ASCII_SIZE) { return length; } if (ECMA_IS_DIRECT_STRING (string_p)) { JERRY_ASSERT (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_MAGIC); JERRY_ASSERT ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p) >= LIT_MAGIC_STRING__COUNT); uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p) - LIT_MAGIC_STRING__COUNT; return lit_utf8_string_length (lit_get_magic_string_ex_utf8 (id), lit_get_magic_string_ex_size (id)); } if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_HEAP_UTF8_STRING) { return ((ecma_short_string_t *) string_p)->length; } if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING) { return ((ecma_long_string_t *) string_p)->length; } JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX); lit_magic_string_ex_id_t id = LIT_MAGIC_STRING__COUNT - string_p->u.magic_string_ex_id; return lit_utf8_string_length (lit_get_magic_string_ex_utf8 (id), lit_get_magic_string_ex_size (id)); } /* ecma_string_get_length */ /** * Get length of UTF-8 encoded string length from ecma-string * * @return number of characters in the UTF-8 encoded string */ lit_utf8_size_t ecma_string_get_utf8_length (const ecma_string_t *string_p) /**< ecma-string */ { lit_utf8_size_t length = ecma_string_get_ascii_size (string_p); if (length != ECMA_STRING_NO_ASCII_SIZE) { return length; } if (ECMA_IS_DIRECT_STRING (string_p)) { JERRY_ASSERT (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_MAGIC); JERRY_ASSERT ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p) >= LIT_MAGIC_STRING__COUNT); uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p) - LIT_MAGIC_STRING__COUNT; return lit_get_utf8_length_of_cesu8_string (lit_get_magic_string_ex_utf8 (id), lit_get_magic_string_ex_size (id)); } if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_HEAP_UTF8_STRING) { ecma_short_string_t *short_string_p = (ecma_short_string_t *) string_p; lit_utf8_size_t size = short_string_p->size; if (size == short_string_p->length) { return size; } return lit_get_utf8_length_of_cesu8_string (ECMA_SHORT_STRING_GET_BUFFER (string_p), size); } if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING) { ecma_long_string_t *long_string_p = (ecma_long_string_t *) string_p; lit_utf8_size_t size = long_string_p->size; if (size == long_string_p->length) { return size; } return lit_get_utf8_length_of_cesu8_string (long_string_p->string_p, size); } JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX); lit_magic_string_ex_id_t id = LIT_MAGIC_STRING__COUNT - string_p->u.magic_string_ex_id; return lit_get_utf8_length_of_cesu8_string (lit_get_magic_string_ex_utf8 (id), lit_get_magic_string_ex_size (id)); } /* ecma_string_get_utf8_length */ /** * Get size of ecma-string * * @return number of bytes in the buffer needed to represent the string */ lit_utf8_size_t ecma_string_get_size (const ecma_string_t *string_p) /**< ecma-string */ { lit_utf8_size_t length = ecma_string_get_ascii_size (string_p); if (length != ECMA_STRING_NO_ASCII_SIZE) { return length; } if (ECMA_IS_DIRECT_STRING (string_p)) { JERRY_ASSERT (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_MAGIC); JERRY_ASSERT ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p) >= LIT_MAGIC_STRING__COUNT); return lit_get_magic_string_ex_size ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p) - LIT_MAGIC_STRING__COUNT); } if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_HEAP_UTF8_STRING) { return ((ecma_short_string_t *) string_p)->size; } if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING) { return ((ecma_long_string_t *) string_p)->size; } JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX); return lit_get_magic_string_ex_size (LIT_MAGIC_STRING__COUNT - string_p->u.magic_string_ex_id); } /* ecma_string_get_size */ /** * Get the UTF-8 encoded string size from ecma-string * * @return number of bytes in the buffer needed to represent an UTF-8 encoded string */ lit_utf8_size_t ecma_string_get_utf8_size (const ecma_string_t *string_p) /**< ecma-string */ { lit_utf8_size_t length = ecma_string_get_ascii_size (string_p); if (length != ECMA_STRING_NO_ASCII_SIZE) { return length; } if (ECMA_IS_DIRECT_STRING (string_p)) { JERRY_ASSERT (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_MAGIC); JERRY_ASSERT ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p) >= LIT_MAGIC_STRING__COUNT); uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p) - LIT_MAGIC_STRING__COUNT; return lit_get_utf8_size_of_cesu8_string (lit_get_magic_string_ex_utf8 (id), lit_get_magic_string_ex_size (id)); } if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_HEAP_UTF8_STRING) { ecma_short_string_t *short_string_p = (ecma_short_string_t *) string_p; lit_utf8_size_t size = short_string_p->size; if (size == short_string_p->length) { return size; } return lit_get_utf8_size_of_cesu8_string (ECMA_SHORT_STRING_GET_BUFFER (string_p), size); } if (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING) { ecma_long_string_t *long_string_p = (ecma_long_string_t *) string_p; if (long_string_p->size == long_string_p->length) { return long_string_p->size; } return lit_get_utf8_size_of_cesu8_string (long_string_p->string_p, long_string_p->size); } JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX); lit_magic_string_ex_id_t id = LIT_MAGIC_STRING__COUNT - string_p->u.magic_string_ex_id; return lit_get_utf8_size_of_cesu8_string (lit_get_magic_string_ex_utf8 (id), lit_get_magic_string_ex_size (id)); } /* ecma_string_get_utf8_size */ /** * Get character from specified position in an external ecma-string. * * @return character value */ static ecma_char_t JERRY_ATTR_NOINLINE ecma_external_string_get_char_at_pos (lit_utf8_size_t id, /**< id of the external magic string */ lit_utf8_size_t index) /**< index of character */ { id -= LIT_MAGIC_STRING__COUNT; const lit_utf8_byte_t *data_p = lit_get_magic_string_ex_utf8 (id); lit_utf8_size_t size = lit_get_magic_string_ex_size (id); lit_utf8_size_t length = lit_utf8_string_length (data_p, size); if (JERRY_LIKELY (size == length)) { return (ecma_char_t) data_p[index]; } return lit_utf8_string_code_unit_at (data_p, size, index); } /* ecma_external_string_get_char_at_pos */ /** * Get character from specified position in the ecma-string. * * @return character value */ ecma_char_t ecma_string_get_char_at_pos (const ecma_string_t *string_p, /**< ecma-string */ lit_utf8_size_t index) /**< index of character */ { JERRY_ASSERT (index < ecma_string_get_length (string_p)); lit_utf8_byte_t uint32_to_string_buffer[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; if (ECMA_IS_DIRECT_STRING (string_p)) { switch (ECMA_GET_DIRECT_STRING_TYPE (string_p)) { case ECMA_DIRECT_STRING_MAGIC: { uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); if (JERRY_LIKELY (id < LIT_MAGIC_STRING__COUNT)) { /* All magic strings must be ascii strings. */ const lit_utf8_byte_t *data_p = lit_get_magic_string_utf8 (id); return (ecma_char_t) data_p[index]; } return ecma_external_string_get_char_at_pos (id, index); } default: { JERRY_ASSERT (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_UINT); uint32_t uint32_number = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); ecma_uint32_to_utf8_string (uint32_number, uint32_to_string_buffer, ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); return (ecma_char_t) uint32_to_string_buffer[index]; } } } JERRY_ASSERT (string_p->refs_and_container >= ECMA_STRING_REF_ONE); switch (ECMA_STRING_GET_CONTAINER (string_p)) { case ECMA_STRING_CONTAINER_HEAP_UTF8_STRING: { ecma_short_string_t *short_string_p = (ecma_short_string_t *) string_p; lit_utf8_size_t size = short_string_p->size; const lit_utf8_byte_t *data_p = ECMA_SHORT_STRING_GET_BUFFER (string_p); if (JERRY_LIKELY (size == short_string_p->length)) { return (ecma_char_t) data_p[index]; } return lit_utf8_string_code_unit_at (data_p, size, index); } case ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING: { ecma_long_string_t *long_string_p = (ecma_long_string_t *) string_p; lit_utf8_size_t size = long_string_p->size; const lit_utf8_byte_t *data_p = long_string_p->string_p; if (JERRY_LIKELY (size == long_string_p->length)) { return (ecma_char_t) data_p[index]; } return lit_utf8_string_code_unit_at (data_p, size, index); } case ECMA_STRING_CONTAINER_HEAP_ASCII_STRING: { const lit_utf8_byte_t *data_p = ECMA_ASCII_STRING_GET_BUFFER (string_p); return (ecma_char_t) data_p[index]; } case ECMA_STRING_CONTAINER_UINT32_IN_DESC: { ecma_uint32_to_utf8_string (string_p->u.uint32_number, uint32_to_string_buffer, ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); return (ecma_char_t) uint32_to_string_buffer[index]; } default: { JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX); return ecma_external_string_get_char_at_pos (string_p->u.magic_string_ex_id, index); } } } /* ecma_string_get_char_at_pos */ /** * Check if passed string equals to one of magic strings * and if equal magic string was found, return it's id in 'out_id_p' argument. * * @return id - if magic string equal to passed string was found, * LIT_MAGIC_STRING__COUNT - otherwise. */ lit_magic_string_id_t ecma_get_string_magic (const ecma_string_t *string_p) /**< ecma-string */ { if (ECMA_IS_DIRECT_STRING_WITH_TYPE (string_p, ECMA_DIRECT_STRING_MAGIC)) { uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); if (id < LIT_MAGIC_STRING__COUNT) { return (lit_magic_string_id_t) id; } } return LIT_MAGIC_STRING__COUNT; } /* ecma_get_string_magic */ /** * Try to calculate hash of the ecma-string * * @return calculated hash */ lit_string_hash_t ecma_string_hash (const ecma_string_t *string_p) /**< ecma-string to calculate hash for */ { if (ECMA_IS_DIRECT_STRING (string_p)) { return (lit_string_hash_t) ECMA_GET_DIRECT_STRING_VALUE (string_p); } return (lit_string_hash_t) string_p->u.hash; } /* ecma_string_hash */ /** * Create a substring from an ecma string * * @return a newly constructed ecma string with its value initialized to a copy of a substring of the first argument */ ecma_string_t * ecma_string_substr (const ecma_string_t *string_p, /**< pointer to an ecma string */ lit_utf8_size_t start_pos, /**< start position, should be less or equal than string length */ lit_utf8_size_t end_pos) /**< end position, should be less or equal than string length */ { const lit_utf8_size_t string_length = ecma_string_get_length (string_p); JERRY_ASSERT (start_pos <= string_length); JERRY_ASSERT (end_pos <= string_length); if (start_pos >= end_pos) { return ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } ecma_string_t *ecma_string_p = NULL; end_pos -= start_pos; ECMA_STRING_TO_UTF8_STRING (string_p, start_p, buffer_size); if (string_length == buffer_size) { ecma_string_p = ecma_new_ecma_string_from_utf8 (start_p + start_pos, (lit_utf8_size_t) end_pos); } else { while (start_pos--) { start_p += lit_get_unicode_char_size_by_utf8_first_byte (*start_p); } const lit_utf8_byte_t *end_p = start_p; while (end_pos--) { end_p += lit_get_unicode_char_size_by_utf8_first_byte (*end_p); } ecma_string_p = ecma_new_ecma_string_from_utf8 (start_p, (lit_utf8_size_t) (end_p - start_p)); } ECMA_FINALIZE_UTF8_STRING (start_p, buffer_size); return ecma_string_p; } /* ecma_string_substr */ /** * Helper function for trimming. * * Used by: * - ecma_string_trim_helper * - ecma_builtin_global_object_parse_int * - ecma_builtin_global_object_parse_float * * @return position of the first non whitespace character. */ const lit_utf8_byte_t * ecma_string_trim_front (const lit_utf8_byte_t *start_p, /**< current string's start position */ const lit_utf8_byte_t *end_p) /**< current string's end position */ { ecma_char_t ch; while (start_p < end_p) { lit_utf8_size_t read_size = lit_read_code_unit_from_cesu8 (start_p, &ch); if (!lit_char_is_white_space (ch)) { break; } start_p += read_size; } return start_p; } /* ecma_string_trim_front */ /** * Helper function for trimming. * * Used by: * - ecma_string_trim_helper * * @return position of the last non whitespace character. */ const lit_utf8_byte_t * ecma_string_trim_back (const lit_utf8_byte_t *start_p, /**< current string's start position */ const lit_utf8_byte_t *end_p) /**< current string's end position */ { ecma_char_t ch; while (end_p > start_p) { lit_utf8_size_t read_size = lit_read_prev_code_unit_from_utf8 (end_p, &ch); if (!lit_char_is_white_space (ch)) { break; } end_p -= read_size; } return end_p; } /* ecma_string_trim_back */ /** * Helper function for trimming. * * Used by: * - ecma_string_trim * - ecma_utf8_string_to_number * * @return void */ void ecma_string_trim_helper (const lit_utf8_byte_t **utf8_str_p, /**< [in, out] current string position */ lit_utf8_size_t *utf8_str_size) /**< [in, out] size of the given string */ { const lit_utf8_byte_t *end_p = *utf8_str_p + *utf8_str_size; const lit_utf8_byte_t *start_p = *utf8_str_p; const lit_utf8_byte_t *new_start_p = ecma_string_trim_front (start_p, end_p); const lit_utf8_byte_t *new_end_p = ecma_string_trim_back (new_start_p, end_p); *utf8_str_size = (lit_utf8_size_t) (new_end_p - new_start_p); *utf8_str_p = new_start_p; } /* ecma_string_trim_helper */ /** * Trim leading and trailing whitespace characters from string. * * @return trimmed ecma string */ ecma_string_t * ecma_string_trim (const ecma_string_t *string_p) /**< pointer to an ecma string */ { ecma_string_t *ret_string_p; lit_utf8_size_t utf8_str_size; uint8_t flags = ECMA_STRING_FLAG_IS_ASCII; const lit_utf8_byte_t *utf8_str_p = ecma_string_get_chars (string_p, &utf8_str_size, NULL, NULL, &flags); if (utf8_str_size > 0) { ecma_string_trim_helper (&utf8_str_p, &utf8_str_size); ret_string_p = ecma_new_ecma_string_from_utf8 (utf8_str_p, utf8_str_size); } else { ret_string_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); } if (flags & ECMA_STRING_FLAG_MUST_BE_FREED) { jmem_heap_free_block ((void *) utf8_str_p, utf8_str_size); } return ret_string_p; } /* ecma_string_trim */ /** * Pad the beginning or the end of string with parameter given in fill_string to the length of max_length. * * @return new string from original, padded with given parameters */ ecma_value_t ecma_string_pad (ecma_value_t original_string_p, /**< Input ecma string */ ecma_value_t max_length, /**< Length to pad to, including original length */ ecma_value_t fill_string, /**< The string to pad with */ bool pad_on_start) /**< true - if we are padding to the start, calling with padStart false - if we are padding to the end, calling with padEnd */ { /* 3 */ ecma_length_t int_max_length; if (ECMA_IS_VALUE_ERROR (ecma_op_to_length (max_length, &int_max_length))) { return ECMA_VALUE_ERROR; } /* 4 */ ecma_string_t *original_str_val_p = ecma_get_string_from_value (original_string_p); const uint32_t string_length = ecma_string_get_length (original_str_val_p); /* 5 */ if (int_max_length <= string_length) { ecma_ref_ecma_string (original_str_val_p); return original_string_p; } ecma_string_t *filler_p = ecma_get_magic_string (LIT_MAGIC_STRING_SPACE_CHAR); /* 6 - 7 */ if (!ecma_is_value_undefined (fill_string)) { filler_p = ecma_op_to_string (fill_string); if (filler_p == NULL) { return ECMA_VALUE_ERROR; } if (ecma_string_is_empty (filler_p)) { ecma_ref_ecma_string (original_str_val_p); return original_string_p; } } if (int_max_length >= UINT32_MAX) { ecma_deref_ecma_string (filler_p); return ecma_raise_range_error (ECMA_ERR_MAXIMUM_STRING_LENGTH_IS_REACHED); } /* 9 */ uint32_t fill_len = (uint32_t) int_max_length - string_length; /* 10 */ uint32_t filler_length = ecma_string_get_length (filler_p); uint32_t prepend_count = fill_len / filler_length; ecma_stringbuilder_t builder = ecma_stringbuilder_create (); if (!pad_on_start) { ecma_stringbuilder_append (&builder, original_str_val_p); } for (uint32_t i = 0; i < prepend_count; i++) { ecma_stringbuilder_append (&builder, filler_p); } uint32_t remaining = fill_len - (prepend_count * filler_length); ECMA_STRING_TO_UTF8_STRING (filler_p, start_p, utf8_str_size); const lit_utf8_byte_t *temp_start_p = start_p; while (remaining > 0) { ecma_char_t ch; lit_utf8_size_t read_size = lit_read_code_unit_from_cesu8 (temp_start_p, &ch); ecma_stringbuilder_append_char (&builder, ch); temp_start_p += read_size; remaining--; } ECMA_FINALIZE_UTF8_STRING (start_p, utf8_str_size); ecma_deref_ecma_string (filler_p); /* 11 - 12 */ if (pad_on_start) { ecma_stringbuilder_append (&builder, original_str_val_p); } return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); } /* ecma_string_pad */ /** * Create an empty string builder * * @return new string builder */ ecma_stringbuilder_t ecma_stringbuilder_create (void) { const lit_utf8_size_t initial_size = ECMA_ASCII_STRING_HEADER_SIZE; ecma_stringbuilder_header_t *header_p = (ecma_stringbuilder_header_t *) jmem_heap_alloc_block (initial_size); header_p->current_size = initial_size; ecma_stringbuilder_t ret = { header_p }; return ret; } /* ecma_stringbuilder_create */ /** * Create a string builder from an ecma string * * @return new string builder */ ecma_stringbuilder_t ecma_stringbuilder_create_from (ecma_string_t *string_p) /**< ecma string */ { const lit_utf8_size_t string_size = ecma_string_get_size (string_p); const lit_utf8_size_t initial_size = string_size + ECMA_ASCII_STRING_HEADER_SIZE; ecma_stringbuilder_header_t *header_p = (ecma_stringbuilder_header_t *) jmem_heap_alloc_block (initial_size); header_p->current_size = initial_size; ecma_string_to_cesu8_bytes (string_p, ECMA_STRINGBUILDER_STRING_PTR (header_p), string_size); ecma_stringbuilder_t ret = { header_p }; return ret; } /* ecma_stringbuilder_create_from */ /** * Create a string builder from a raw string * * @return new string builder */ ecma_stringbuilder_t ecma_stringbuilder_create_raw (const lit_utf8_byte_t *data_p, /**< pointer to data */ const lit_utf8_size_t data_size) /**< size of the data */ { const lit_utf8_size_t initial_size = data_size + ECMA_ASCII_STRING_HEADER_SIZE; ecma_stringbuilder_header_t *header_p = (ecma_stringbuilder_header_t *) jmem_heap_alloc_block (initial_size); header_p->current_size = initial_size; memcpy (ECMA_STRINGBUILDER_STRING_PTR (header_p), data_p, data_size); ecma_stringbuilder_t ret = { header_p }; return ret; } /* ecma_stringbuilder_create_raw */ /** * Grow the underlying buffer of a string builder * * @return pointer to the end of the data in the underlying buffer */ static lit_utf8_byte_t * ecma_stringbuilder_grow (ecma_stringbuilder_t *builder_p, /**< string builder */ lit_utf8_size_t required_size) /**< required size */ { ecma_stringbuilder_header_t *header_p = builder_p->header_p; JERRY_ASSERT (header_p != NULL); const lit_utf8_size_t new_size = header_p->current_size + required_size; header_p = (ecma_stringbuilder_header_t *) jmem_heap_realloc_block (header_p, header_p->current_size, new_size); header_p->current_size = new_size; builder_p->header_p = header_p; return ((lit_utf8_byte_t *) header_p) + header_p->current_size - required_size; } /* ecma_stringbuilder_grow */ /** * Get the current size of the string in a string builder * * @return the size of the string data */ lit_utf8_size_t ecma_stringbuilder_get_size (ecma_stringbuilder_t *builder_p) /**< string builder */ { ecma_stringbuilder_header_t *header_p = builder_p->header_p; JERRY_ASSERT (header_p != NULL); return ECMA_STRINGBUILDER_STRING_SIZE (header_p); } /* ecma_stringbuilder_get_size */ /** * Get pointer to the raw string data in a string builder * * @return pointer to the string data */ lit_utf8_byte_t * ecma_stringbuilder_get_data (ecma_stringbuilder_t *builder_p) /**< string builder */ { ecma_stringbuilder_header_t *header_p = builder_p->header_p; JERRY_ASSERT (header_p != NULL); return ECMA_STRINGBUILDER_STRING_PTR (header_p); } /* ecma_stringbuilder_get_data */ /** * Revert the string builder to a smaller size */ void ecma_stringbuilder_revert (ecma_stringbuilder_t *builder_p, /**< string builder */ const lit_utf8_size_t size) /**< new size */ { ecma_stringbuilder_header_t *header_p = builder_p->header_p; JERRY_ASSERT (header_p != NULL); const lit_utf8_size_t new_size = size + ECMA_ASCII_STRING_HEADER_SIZE; JERRY_ASSERT (new_size <= header_p->current_size); header_p = (ecma_stringbuilder_header_t *) jmem_heap_realloc_block (header_p, header_p->current_size, new_size); header_p->current_size = new_size; builder_p->header_p = header_p; } /* ecma_stringbuilder_revert */ /** * Append an ecma_string_t to a string builder */ void ecma_stringbuilder_append (ecma_stringbuilder_t *builder_p, /**< string builder */ const ecma_string_t *string_p) /**< ecma string */ { const lit_utf8_size_t string_size = ecma_string_get_size (string_p); lit_utf8_byte_t *dest_p = ecma_stringbuilder_grow (builder_p, string_size); ecma_string_to_cesu8_bytes (string_p, dest_p, string_size); } /* ecma_stringbuilder_append */ /** * Append a magic string to a string builder */ void ecma_stringbuilder_append_magic (ecma_stringbuilder_t *builder_p, /**< string builder */ const lit_magic_string_id_t id) /**< magic string id */ { const lit_utf8_size_t string_size = lit_get_magic_string_size (id); lit_utf8_byte_t *dest_p = ecma_stringbuilder_grow (builder_p, string_size); const lit_utf8_byte_t *string_data_p = lit_get_magic_string_utf8 (id); memcpy (dest_p, string_data_p, string_size); } /* ecma_stringbuilder_append_magic */ /** * Append raw string data to a string builder */ void ecma_stringbuilder_append_raw (ecma_stringbuilder_t *builder_p, /**< string builder */ const lit_utf8_byte_t *data_p, /**< pointer to data */ const lit_utf8_size_t data_size) /**< size of the data */ { lit_utf8_byte_t *dest_p = ecma_stringbuilder_grow (builder_p, data_size); memcpy (dest_p, data_p, data_size); } /* ecma_stringbuilder_append_raw */ /** * Append a codepoint to a string builder */ void ecma_stringbuilder_append_codepoint (ecma_stringbuilder_t *builder_p, /**< string builder */ lit_code_point_t cp) /**< code point */ { const lit_utf8_size_t size = (lit_utf8_size_t) lit_code_point_get_cesu8_length (cp); lit_utf8_byte_t *dest_p = ecma_stringbuilder_grow (builder_p, size); lit_code_point_to_cesu8_bytes (dest_p, cp); } /* ecma_stringbuilder_append_codepoint */ /** * Append an ecma_char_t to a string builder */ void ecma_stringbuilder_append_char (ecma_stringbuilder_t *builder_p, /**< string builder */ const ecma_char_t c) /**< ecma char */ { ecma_stringbuilder_append_codepoint (builder_p, c); } /* ecma_stringbuilder_append_char */ /** * Append a single byte to a string builder */ void ecma_stringbuilder_append_byte (ecma_stringbuilder_t *builder_p, /**< string builder */ const lit_utf8_byte_t byte) /**< byte */ { lit_utf8_byte_t *dest_p = ecma_stringbuilder_grow (builder_p, 1); *dest_p = byte; } /* ecma_stringbuilder_append_byte */ /** * Finalize a string builder, returning the created string, and releasing the underlying buffer. * * Note: * The builder should no longer be used. * * @return the created string */ ecma_string_t * ecma_stringbuilder_finalize (ecma_stringbuilder_t *builder_p) /**< string builder */ { ecma_stringbuilder_header_t *header_p = builder_p->header_p; JERRY_ASSERT (header_p != NULL); const lit_utf8_size_t string_size = ECMA_STRINGBUILDER_STRING_SIZE (header_p); lit_utf8_byte_t *string_begin_p = ECMA_STRINGBUILDER_STRING_PTR (header_p); ecma_string_t *string_p = ecma_find_special_string (string_begin_p, string_size); if (JERRY_UNLIKELY (string_p != NULL)) { ecma_stringbuilder_destroy (builder_p); return string_p; } #ifndef JERRY_NDEBUG builder_p->header_p = NULL; #endif /* !defined (JERRY_NDEBUG) */ size_t container_size = sizeof (ecma_short_string_t); const lit_string_hash_t hash = lit_utf8_string_calc_hash (string_begin_p, string_size); const lit_utf8_size_t length = lit_utf8_string_length (string_begin_p, string_size); if (JERRY_LIKELY (string_size <= UINT16_MAX)) { if (JERRY_LIKELY (length == string_size) && string_size <= (UINT8_MAX + 1)) { string_p = (ecma_string_t *) header_p; string_p->refs_and_container = ECMA_STRING_CONTAINER_HEAP_ASCII_STRING | ECMA_STRING_REF_ONE; string_p->u.hash = hash; ECMA_ASCII_STRING_SET_SIZE (string_p, string_size); return (ecma_string_t *) string_p; } } else { container_size = sizeof (ecma_long_string_t); } const size_t utf8_string_size = string_size + container_size; header_p = (ecma_stringbuilder_header_t *) jmem_heap_realloc_block (header_p, header_p->current_size, utf8_string_size); memmove (((lit_utf8_byte_t *) header_p + container_size), ECMA_STRINGBUILDER_STRING_PTR (header_p), string_size); if (JERRY_LIKELY (string_size <= UINT16_MAX)) { ecma_short_string_t *short_string_p = (ecma_short_string_t *) header_p; short_string_p->header.refs_and_container = ECMA_STRING_CONTAINER_HEAP_UTF8_STRING | ECMA_STRING_REF_ONE; short_string_p->header.u.hash = hash; short_string_p->size = (uint16_t) string_size; short_string_p->length = (uint16_t) length; return (ecma_string_t *) short_string_p; } ecma_long_string_t *long_string_p = (ecma_long_string_t *) header_p; long_string_p->header.refs_and_container = ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING | ECMA_STRING_REF_ONE; long_string_p->header.u.hash = hash; long_string_p->string_p = ECMA_LONG_STRING_BUFFER_START (long_string_p); long_string_p->size = string_size; long_string_p->length = length; return (ecma_string_t *) long_string_p; } /* ecma_stringbuilder_finalize */ /** * Destroy a string builder that is no longer needed without creating a string from the contents. */ void ecma_stringbuilder_destroy (ecma_stringbuilder_t *builder_p) /**< string builder */ { JERRY_ASSERT (builder_p->header_p != NULL); const lit_utf8_size_t size = builder_p->header_p->current_size; jmem_heap_free_block (builder_p->header_p, size); #ifndef JERRY_NDEBUG builder_p->header_p = NULL; #endif /* !defined (JERRY_NDEBUG) */ } /* ecma_stringbuilder_destroy */ /** * AdvanceStringIndex operation * * See also: * ECMA-262 v6.0, 21.2.5.2.3 * * @return uint32_t - the proper character index based on the operation */ ecma_length_t ecma_op_advance_string_index (ecma_string_t *str_p, /**< input string */ ecma_length_t index, /**< given character index */ bool is_unicode) /**< true - if regexp object's "unicode" flag is set false - otherwise */ { JERRY_ASSERT ((ecma_number_t) index <= ECMA_NUMBER_MAX_SAFE_INTEGER); ecma_length_t next_index = index + 1; if (!is_unicode) { return next_index; } lit_utf8_size_t str_len = ecma_string_get_length (str_p); if (next_index >= str_len) { return next_index; } JERRY_ASSERT (index < UINT32_MAX); ecma_char_t first = ecma_string_get_char_at_pos (str_p, (lit_utf8_size_t) index); if (!lit_is_code_point_utf16_high_surrogate (first)) { return next_index; } ecma_char_t second = ecma_string_get_char_at_pos (str_p, (lit_utf8_size_t) next_index); if (!lit_is_code_point_utf16_low_surrogate (second)) { return next_index; } return next_index + 1; } /* ecma_op_advance_string_index */ /** * @} * @} */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/vm/vm-stack.cpp000664 001750 001750 00000042707 15164251010 042204 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "vm-stack.h" #include "ecma-alloc.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" #include "ecma-objects.h" #include "ecma-promise-object.h" #include "jcontext.h" #include "vm-defines.h" /** \addtogroup vm Virtual machine * @{ * * \addtogroup stack VM stack * @{ */ JERRY_STATIC_ASSERT (((int)PARSER_WITH_CONTEXT_STACK_ALLOCATION == (int)PARSER_BLOCK_CONTEXT_STACK_ALLOCATION), with_context_stack_allocation_must_be_equal_to_block_context_stack_allocation); JERRY_STATIC_ASSERT (((int)PARSER_WITH_CONTEXT_STACK_ALLOCATION == (int)PARSER_TRY_CONTEXT_STACK_ALLOCATION), with_context_stack_allocation_must_be_equal_to_block_context_stack_allocation); JERRY_STATIC_ASSERT (((int)PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION == (int)PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION), for_of_context_stack_allocation_must_be_equal_to_for_await_of_context_stack_allocation); /** * Abort (finalize) the current variable length stack context, and remove it. * * @return new stack top */ ecma_value_t * vm_stack_context_abort_variable_length (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t *vm_stack_top_p, /**< current stack top */ uint32_t context_stack_allocation) /**< 0 - if all context element * should be released * context stack allocation - otherwise */ { JERRY_ASSERT (VM_CONTEXT_IS_VARIABLE_LENGTH (VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1]))); uint32_t context_size = VM_GET_CONTEXT_END (vm_stack_top_p[-1]); VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, context_size); JERRY_ASSERT (context_size > 0); --vm_stack_top_p; if (context_stack_allocation == 0) { context_stack_allocation = context_size; } for (uint32_t i = 1; i < context_stack_allocation; i++) { ecma_free_value (*(--vm_stack_top_p)); } return vm_stack_top_p; } /* vm_stack_context_abort_variable_length */ /** * Abort (finalize) the current stack context, and remove it. * * @return new stack top */ ecma_value_t * vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t *vm_stack_top_p) /**< current stack top */ { ecma_value_t context_info = vm_stack_top_p[-1]; if (context_info & VM_CONTEXT_HAS_LEX_ENV) { ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); ecma_deref_object (lex_env_p); } switch (VM_GET_CONTEXT_TYPE (context_info)) { case VM_CONTEXT_FINALLY_THROW: case VM_CONTEXT_FINALLY_RETURN: { ecma_free_value (vm_stack_top_p[-2]); /* FALLTHRU */ } case VM_CONTEXT_FINALLY_JUMP: { VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FINALLY_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_FINALLY_CONTEXT_STACK_ALLOCATION; break; } case VM_CONTEXT_TRY: case VM_CONTEXT_CATCH: case VM_CONTEXT_BLOCK: case VM_CONTEXT_WITH: { VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_WITH_CONTEXT_STACK_ALLOCATION; break; } case VM_CONTEXT_ITERATOR: case VM_CONTEXT_OBJ_INIT: case VM_CONTEXT_OBJ_INIT_REST: { vm_stack_top_p = vm_stack_context_abort_variable_length (frame_ctx_p, vm_stack_top_p, 0); break; } case VM_CONTEXT_FOR_OF: case VM_CONTEXT_FOR_AWAIT_OF: { ecma_free_value (vm_stack_top_p[-2]); ecma_free_value (vm_stack_top_p[-3]); ecma_free_value (vm_stack_top_p[-4]); VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION; break; } default: { JERRY_ASSERT (VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1]) == VM_CONTEXT_FOR_IN); ecma_collection_t *collection_p; collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, vm_stack_top_p[-2]); ecma_value_t *buffer_p = collection_p->buffer_p; for (uint32_t index = vm_stack_top_p[-3]; index < collection_p->item_count; index++) { ecma_free_value (buffer_p[index]); } ecma_collection_destroy (collection_p); ecma_free_value (vm_stack_top_p[-4]); VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; break; } } return vm_stack_top_p; } /* vm_stack_context_abort */ /** * Decode branch offset. * * @return branch offset */ static uint32_t vm_decode_branch_offset (const uint8_t *branch_offset_p, /**< start offset of byte code */ uint32_t length) /**< length of the branch */ { uint32_t branch_offset = *branch_offset_p; JERRY_ASSERT (length >= 1 && length <= 3); switch (length) { case 3: { branch_offset <<= 8; branch_offset |= *(++branch_offset_p); /* FALLTHRU */ } case 2: { branch_offset <<= 8; branch_offset |= *(++branch_offset_p); break; } } return branch_offset; } /* vm_decode_branch_offset */ /** * Byte code which resumes an executable object with throw */ static const uint8_t vm_stack_resume_executable_object_with_context_end[1] = { CBC_CONTEXT_END }; /** * Find a finally up to the end position. * * @return value specified in vm_stack_found_type */ vm_stack_found_type vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t *stack_top_p, /**< current stack top */ vm_stack_context_type_t finally_type, /**< searching this finally */ uint32_t search_limit) /**< search up-to this byte code */ { JERRY_ASSERT (finally_type <= VM_CONTEXT_FINALLY_RETURN); if (finally_type != VM_CONTEXT_FINALLY_JUMP) { search_limit = 0xffffffffu; } while (frame_ctx_p->context_depth > 0) { vm_stack_context_type_t context_type = VM_GET_CONTEXT_TYPE (stack_top_p[-1]); uint32_t context_end = VM_GET_CONTEXT_END (stack_top_p[-1]); JERRY_ASSERT (!VM_CONTEXT_IS_VARIABLE_LENGTH (context_type) || finally_type != VM_CONTEXT_FINALLY_JUMP); if (!VM_CONTEXT_IS_VARIABLE_LENGTH (context_type) && search_limit < context_end) { frame_ctx_p->stack_top_p = stack_top_p; return VM_CONTEXT_FOUND_EXPECTED; } if (context_type == VM_CONTEXT_TRY || context_type == VM_CONTEXT_CATCH) { const uint8_t *byte_code_p; uint32_t branch_offset_length; uint32_t branch_offset; if (search_limit == context_end) { frame_ctx_p->stack_top_p = stack_top_p; return VM_CONTEXT_FOUND_EXPECTED; } if (stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) { ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); ecma_deref_object (lex_env_p); } byte_code_p = frame_ctx_p->byte_code_start_p + context_end; if (context_type == VM_CONTEXT_TRY) { JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE); if (byte_code_p[1] >= CBC_EXT_CATCH && byte_code_p[1] <= CBC_EXT_CATCH_3) { branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (byte_code_p[1]); branch_offset = vm_decode_branch_offset (byte_code_p + 2, branch_offset_length); if (finally_type == VM_CONTEXT_FINALLY_THROW) { branch_offset += (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p); stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_CATCH, branch_offset); byte_code_p += 2 + branch_offset_length; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = stack_top_p; return VM_CONTEXT_FOUND_FINALLY; } byte_code_p += branch_offset; if (*byte_code_p == CBC_CONTEXT_END) { VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; continue; } } } else { JERRY_ASSERT (context_type == VM_CONTEXT_CATCH); if (byte_code_p[0] == CBC_CONTEXT_END) { VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; continue; } } JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION); stack_top_p += PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION; if (JERRY_UNLIKELY (byte_code_p[1] == CBC_EXT_ASYNC_EXIT)) { branch_offset = (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p); stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset); frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = stack_top_p; return VM_CONTEXT_FOUND_FINALLY; } JERRY_ASSERT (byte_code_p[1] >= CBC_EXT_FINALLY && byte_code_p[1] <= CBC_EXT_FINALLY_3); branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (byte_code_p[1]); branch_offset = vm_decode_branch_offset (byte_code_p + 2, branch_offset_length); branch_offset += (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p); stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset); byte_code_p += 2 + branch_offset_length; frame_ctx_p->byte_code_p = byte_code_p; frame_ctx_p->stack_top_p = stack_top_p; return VM_CONTEXT_FOUND_FINALLY; } else if (stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR) { JERRY_ASSERT (context_type == VM_CONTEXT_FOR_OF || context_type == VM_CONTEXT_FOR_AWAIT_OF || context_type == VM_CONTEXT_ITERATOR); JERRY_ASSERT (finally_type == VM_CONTEXT_FINALLY_THROW || !jcontext_has_pending_exception ()); ecma_value_t exception = ECMA_VALUE_UNDEFINED; if (finally_type == VM_CONTEXT_FINALLY_THROW) { exception = jcontext_take_exception (); } ecma_value_t result; if (context_type == VM_CONTEXT_ITERATOR) { result = ecma_op_iterator_close (stack_top_p[-2]); } else { ecma_value_t iterator = stack_top_p[-3]; result = ecma_op_get_method_by_magic_id (iterator, LIT_MAGIC_STRING_RETURN); if (!ECMA_IS_VALUE_ERROR (result) && !ecma_is_value_undefined (result)) { ecma_object_t *return_obj_p = ecma_get_object_from_value (result); result = ecma_op_function_validated_call (result, iterator, NULL, 0); ecma_deref_object (return_obj_p); if (context_type == VM_CONTEXT_FOR_AWAIT_OF && !ECMA_IS_VALUE_ERROR (result)) { ecma_extended_object_t *async_generator_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); result = ecma_promise_async_await (async_generator_object_p, result); if (!ECMA_IS_VALUE_ERROR (result)) { uint16_t extra_flags = (ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD | (ECMA_AWAIT_FOR_CLOSE << ECMA_AWAIT_STATE_SHIFT)); async_generator_object_p->u.cls.u2.executable_obj_flags |= extra_flags; stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FINALLY_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_FINALLY_CONTEXT_STACK_ALLOCATION; stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, context_end); if (finally_type == VM_CONTEXT_FINALLY_THROW) { stack_top_p[-2] = exception; } frame_ctx_p->call_operation = VM_EXEC_RETURN; frame_ctx_p->byte_code_p = vm_stack_resume_executable_object_with_context_end; frame_ctx_p->stack_top_p = stack_top_p; return VM_CONTEXT_FOUND_AWAIT; } } if (!ECMA_IS_VALUE_ERROR (result)) { bool is_object = ecma_is_value_object (result); ecma_free_value (result); result = ECMA_VALUE_UNDEFINED; if (!is_object) { result = ecma_raise_type_error (ECMA_ERR_ITERATOR_RETURN_RESULT_IS_NOT_OBJECT); } } } } JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result) || result == ECMA_VALUE_UNDEFINED); if (ECMA_IS_VALUE_ERROR (result)) { if (finally_type != VM_CONTEXT_FINALLY_THROW) { frame_ctx_p->stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); return VM_CONTEXT_FOUND_ERROR; } ecma_free_value (jcontext_take_exception ()); jcontext_raise_exception (exception); } else if (finally_type == VM_CONTEXT_FINALLY_THROW) { jcontext_raise_exception (exception); } } stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); } frame_ctx_p->stack_top_p = stack_top_p; return VM_CONTEXT_FOUND_EXPECTED; } /* vm_stack_find_finally */ /** * Get the offsets of ecma values corresponding to the passed context. * * @return array of offsets, last item represents the size of the context item */ uint32_t vm_get_context_value_offsets (ecma_value_t *context_item_p) /**< any item of a context */ { switch (VM_GET_CONTEXT_TYPE (context_item_p[-1])) { case VM_CONTEXT_FINALLY_THROW: case VM_CONTEXT_FINALLY_RETURN: { return (PARSER_FINALLY_CONTEXT_STACK_ALLOCATION << VM_CONTEXT_OFFSET_SHIFT) | 2; } case VM_CONTEXT_FINALLY_JUMP: { return PARSER_FINALLY_CONTEXT_STACK_ALLOCATION; } case VM_CONTEXT_TRY: case VM_CONTEXT_CATCH: case VM_CONTEXT_BLOCK: case VM_CONTEXT_WITH: { return PARSER_WITH_CONTEXT_STACK_ALLOCATION; } case VM_CONTEXT_FOR_IN: { return (PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION << VM_CONTEXT_OFFSET_SHIFT) | 4; } default: { JERRY_ASSERT (VM_GET_CONTEXT_TYPE (context_item_p[-1]) == VM_CONTEXT_FOR_OF || VM_GET_CONTEXT_TYPE (context_item_p[-1]) == VM_CONTEXT_FOR_AWAIT_OF); return ((PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION << (VM_CONTEXT_OFFSET_SHIFT * 3)) | (4 << (VM_CONTEXT_OFFSET_SHIFT * 2)) | (3 << VM_CONTEXT_OFFSET_SHIFT) | 2); } } } /* vm_get_context_value_offsets */ /** * Ref / deref lexical environments in the chain using the current context. */ void vm_ref_lex_env_chain (ecma_object_t *lex_env_p, /**< top of lexical environment */ uint16_t context_depth, /**< depth of function context */ ecma_value_t *context_end_p, /**< end of function context */ bool do_ref) /**< ref or deref lexical environments */ { ecma_value_t *context_top_p = context_end_p + context_depth; JERRY_ASSERT (context_top_p > context_end_p); do { if (context_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) { JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); ecma_object_t *next_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); if (do_ref) { ecma_ref_object (lex_env_p); } else { ecma_deref_object (lex_env_p); } lex_env_p = next_lex_env_p; } if (VM_CONTEXT_IS_VARIABLE_LENGTH (VM_GET_CONTEXT_TYPE (context_top_p[-1]))) { ecma_value_t *last_item_p = context_top_p - VM_GET_CONTEXT_END (context_top_p[-1]); JERRY_ASSERT (last_item_p >= context_end_p); context_top_p--; do { if (do_ref) { ecma_ref_if_object (*(--context_top_p)); } else { ecma_deref_if_object (*(--context_top_p)); } } while (context_top_p > last_item_p); continue; } uint32_t offsets = vm_get_context_value_offsets (context_top_p); while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets)) { int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets); if (do_ref) { ecma_ref_if_object (context_top_p[offset]); } else { ecma_deref_if_object (context_top_p[offset]); } offsets >>= VM_CONTEXT_OFFSET_SHIFT; } JERRY_ASSERT (context_top_p >= context_end_p + offsets); context_top_p -= offsets; } while (context_top_p > context_end_p); } /* vm_ref_lex_env_chain */ /** * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/cross/android_aarch64.txt000664 001750 001750 00000001265 15164251010 034043 0ustar00ddennedyddennedy000000 000000 # Android developer (Use the NDK with other build systems) # https://developer.android.com/ndk/guides/other_build_systems [binaries] cpp = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/aarch64-linux-androidAPI-clang++' ar = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/llvm-ar' as = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/llvm-as' ranlib = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/llvm-ranlib' ld = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/ld' strip = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/bin/llvm-strip' [properties] sys_root = 'NDK/toolchains/llvm/prebuilt/HOST_TAG/sysroot' [host_machine] system = 'android' cpu_family = 'arm' cpu = 'aarch64' endian = 'little'mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/cross/000775 001750 001750 00000000000 15164251010 030346 5ustar00ddennedyddennedy000000 000000 glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/jcontext/meson.build000664 001750 001750 00000000257 15164251010 043323 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'jcontext.h', 'jcontext.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test.lot000664 001750 001750 00000010436 15164251010 033711 0ustar00ddennedyddennedy000000 000000 {"v":"5.2.1","fr":29.9700012207031,"ip":0,"op":120.0000048877,"w":150,"h":120,"nm":"logo 2","ddd":1,"assets":[],"layers":[{"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":[98,45.5,0],"ix":2},"a":{"a":0,"k":[-23,-14.5,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],[19.25,-19.25],[0,0],[0,0]],"o":[[0,0],[0,0],[-20.25,20.25],[0,0],[0,0]],"v":[[19.5,-14],[-12.5,-45.25],[-51.25,-42.25],[-55.75,-5.25],[-23,28.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":8,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,0.584,0.894,0.929,0.5,0.692,0.733,0.965,1,0.8,0.573,1],"ix":8}},"s":{"a":0,"k":[-64.303,-9.492],"ix":4},"e":{"a":0,"k":[23.721,-13.33],"ix":5},"t":1,"lc":2,"lj":2,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","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":"Shape 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":100,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":30,"s":[100],"e":[0]},{"t":60.0000024438501}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"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":[75,60,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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[22.75,-19.25],[0,0],[0,0]],"o":[[0,0],[0,0],[-19.75,19.75],[0,0],[0,0]],"v":[[19.5,-14],[-12.5,-45.25],[-51.25,-42.25],[-55.75,-5.25],[-23,28.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":8,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,1,0.788,0.6,0.5,0.902,0.682,0.798,1,0.804,0.576,0.996],"ix":8}},"s":{"a":0,"k":[-64.303,-9.492],"ix":4},"e":{"a":0,"k":[23.721,-13.33],"ix":5},"t":1,"lc":2,"lj":2,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","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":"Shape 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":100,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[100],"e":[0]},{"t":30.0000012219251}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":0,"ind":4,"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":[75.5,103,0],"ix":2},"a":{"a":0,"k":[0,42.5,0],"ix":1},"s":{"a":0,"k":[91.956,91.956,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18.648,18.648],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.8,0.572549019608,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"lc":2,"lj":2,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,42.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":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":100,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":60,"s":[100],"e":[0]},{"t":89.0000036250443}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0}],"markers":[]}mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/000775 001750 001750 00000000000 15164251010 033541 5ustar00ddennedyddennedy000000 000000 jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-biguint64array-prototype.cpp000664 001750 001750 00000002335 15164251010 055500 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_BUILTIN_BIGINT #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-biguint64array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID biguint64array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup biguint64arrayprototype ECMA BigUInt64Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_BIGINT */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieModel.h000664 001750 001750 00000101043 15164251010 035770 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_MODEL_H_ #define _TVG_LOTTIE_MODEL_H_ #include "tvgCommon.h" #include "tvgStr.h" #include "tvgCompressor.h" #include "tvgInlist.h" #include "tvgRender.h" #include "tvgLottieProperty.h" #include "tvgLottieRenderPooler.h" struct LottieComposition; struct LottieStroke { struct DashAttr { LottieFloat offset = 0.0f; LottieFloat* values = nullptr; uint8_t size = 0; uint8_t allocated = 0; }; virtual ~LottieStroke() { if (dashattr) delete[] dashattr->values; delete(dashattr); } LottieFloat& dashValue() { if (!dashattr) dashattr = new DashAttr; if (dashattr->size + 1 > dashattr->allocated) { dashattr->allocated = dashattr->size + 2; auto newValues = new LottieFloat[dashattr->allocated]; for (uint8_t i = 0; i < dashattr->size; ++i) { newValues[i].copy(dashattr->values[i]); } delete[] dashattr->values; dashattr->values = newValues; } return dashattr->values[dashattr->size++]; } LottieFloat& dashOffset() { if (!dashattr) dashattr = new DashAttr; return dashattr->offset; } LottieFloat width = 0.0f; DashAttr* dashattr = nullptr; float miterLimit = 0; StrokeCap cap = StrokeCap::Round; StrokeJoin join = StrokeJoin::Round; }; struct LottieEffect { enum Type : uint8_t {Custom = 5, Tint = 20, Fill, Stroke, Tritone, DropShadow = 25, GaussianBlur = 29}; virtual ~LottieEffect() {} unsigned long nm; //encoded by djb2 unsigned long mn; //encoded by djb2 int16_t ix; Type type; bool enable = false; }; struct LottieFxCustom : LottieEffect { struct Property { LottieProperty* property; unsigned long nm = 0; //encoded by djb2 unsigned long mn = 0; //encoded by djb2 }; char* name = nullptr; Array props; LottieFxCustom() { type = LottieEffect::Custom; } ~LottieFxCustom() { ARRAY_FOREACH(p, props) delete(p->property); } Property* property(int type) { LottieProperty* prop = nullptr; switch (type) { case 0: //slider case 1: prop = new LottieFloat; break; //angle case 2: prop = new LottieColor; break; //color case 3: prop = new LottieVector; break; //point case 4: //checkbox case 7: //dropdown case 10: prop = new LottieInteger; break; //effect layer case 6: TVGLOG("LOTTIE", "custom ignored?"); break; default: TVGLOG("LOTTIE", "missing custom property = %d\n", type); return nullptr; } if (prop) { props.push({prop, }); return &props.last(); } return nullptr; } LottieProperty* property(const char* name) { auto id = djb2Encode(name); ARRAY_FOREACH(p, props) { if (p->mn == id || p->nm == id) return p->property; } return nullptr; } }; struct LottieFxFill : LottieEffect { //LottieInteger mask; //LottieInteger allMask; LottieColor color; //LottieInteger invert; //LottieSlider hFeather; //LottieSlider vFeather; LottieFloat opacity; LottieFxFill() { type = LottieEffect::Fill; } }; struct LottieFxStroke : LottieEffect { LottieInteger mask; LottieInteger allMask; //LottieInteger sequential; LottieColor color; LottieFloat size; //LottieFloat hardness; //should support with the blurness? LottieFloat opacity; LottieFloat begin; LottieFloat end; //LottieFloat space; //LottieInteger style; LottieFxStroke() { type = LottieEffect::Stroke; } }; struct LottieFxTint : LottieEffect { LottieColor black; LottieColor white; LottieFloat intensity; LottieFxTint() { type = LottieEffect::Tint; } }; struct LottieFxTritone : LottieEffect { LottieColor bright; LottieColor midtone; LottieColor dark; LottieOpacity blend; LottieFxTritone() { type = LottieEffect::Tritone; } }; struct LottieFxDropShadow : LottieEffect { LottieColor color; LottieFloat opacity = 0; LottieFloat angle = 0.0f; LottieFloat distance = 0.0f; LottieFloat blurness = 0.0f; LottieFxDropShadow() { type = LottieEffect::DropShadow; } }; struct LottieFxGaussianBlur : LottieEffect { LottieFloat blurness = 0.0f; LottieInteger direction = 0; LottieInteger wrap = 0; LottieFxGaussianBlur() { type = LottieEffect::GaussianBlur; } }; struct LottieMask { LottiePathSet pathset; LottieFloat expand = 0.0f; LottieOpacity opacity = 255; MaskMethod method; bool inverse = false; }; struct LottieObject { enum Type : uint8_t { Composition = 0, Layer, Group, Transform, SolidFill, SolidStroke, GradientFill, GradientStroke, Rect, Ellipse, Path, Polystar, Image, Trimpath, Text, Repeater, RoundedCorner, OffsetPath, TextRange }; virtual ~LottieObject() { } virtual LottieProperty* override(LottieProperty* prop, bool release) { TVGERR("LOTTIE", "Unsupported slot type"); return nullptr; } virtual bool mergeable() { return false; } virtual LottieProperty* property(uint16_t ix) { return nullptr; } unsigned long id = 0; //unique id by name generated by djb2 encoding Type type; bool hidden = false; //remove? }; struct LottieGlyph { Array children; //glyph shapes. float width; char* code; char* family = nullptr; char* style = nullptr; uint16_t size; uint8_t len; void prepare() { len = strlen(code); } ~LottieGlyph() { ARRAY_FOREACH(p, children) delete(*p); tvg::free(code); } }; struct LottieTextRange : LottieObject { enum Based : uint8_t { Chars = 1, CharsExcludingSpaces, Words, Lines }; enum Shape : uint8_t { Square = 1, RampUp, RampDown, Triangle, Round, Smooth }; enum Unit : uint8_t { Percent = 1, Index }; LottieTextRange() { LottieObject::type = LottieObject::TextRange; style.flags.fillColor = 0; style.flags.strokeColor = 0; style.flags.strokeWidth = 0; } ~LottieTextRange() { tvg::free(interpolator); } struct { LottieColor fillColor = RGB32{255, 255, 255}; LottieColor strokeColor = RGB32{255, 255, 255}; LottieVector position = Point{0, 0}; LottieScalar scale = Point{100, 100}; LottieFloat letterSpace = 0.0f; LottieFloat lineSpace = 0.0f; LottieFloat strokeWidth = 0.0f; LottieFloat rotation = 0.0f; LottieOpacity fillOpacity = 255; LottieOpacity strokeOpacity = 255; LottieOpacity opacity = 255; struct { bool fillColor : 1; bool strokeColor : 1; bool strokeWidth : 1; } flags; } style; LottieFloat offset = 0.0f; LottieFloat maxEase = 0.0f; LottieFloat minEase = 0.0f; LottieFloat maxAmount = 0.0f; LottieFloat smoothness = 0.0f; LottieFloat start = 0.0f; LottieFloat end = FLT_MAX; LottieInterpolator* interpolator = nullptr; Based based = Chars; Shape shape = Square; Unit rangeUnit = Percent; uint8_t random = 0; bool expressible = false; float factor(float frameNo, float totalLen, float idx); void color(float frameNo, RGB32& fillColor, RGB32& strokeColor, float factor, Tween& tween, LottieExpressions* exps) { if (style.flags.fillColor) { auto color = style.fillColor(frameNo, tween, exps); fillColor.r = tvg::lerp(fillColor.r, color.r, factor); fillColor.g = tvg::lerp(fillColor.g, color.g, factor); fillColor.b = tvg::lerp(fillColor.b, color.b, factor); } if (style.flags.strokeColor) { auto color = style.strokeColor(frameNo, tween, exps); strokeColor.r = tvg::lerp(strokeColor.r, color.r, factor); strokeColor.g = tvg::lerp(strokeColor.g, color.g, factor); strokeColor.b = tvg::lerp(strokeColor.b, color.b, factor); } } LottieProperty* override(LottieProperty* prop, bool release) override { LottieProperty* backup = nullptr; if (style.fillColor.sid == prop->sid) { if (release) style.fillColor.release(); else backup = new LottieColor(style.fillColor); style.fillColor.copy(*static_cast(prop), false); } else if (style.strokeColor.sid == prop->sid) { if (release) style.strokeColor.release(); else backup = new LottieColor(style.strokeColor); style.strokeColor.copy(*static_cast(prop), false); } else if (style.position.sid == prop->sid) { if (release) style.position.release(); else backup = new LottieVector(style.position); style.position.copy(*static_cast(prop), false); } else if (style.scale.sid == prop->sid) { if (release) style.scale.release(); else backup = new LottieScalar(style.scale); style.scale.copy(*static_cast(prop), false); } else if (style.rotation.sid == prop->sid) { if (release) style.rotation.release(); else backup = new LottieFloat(style.rotation); style.rotation.copy(*static_cast(prop), false); } else if (style.letterSpace.sid == prop->sid) { if (release) style.letterSpace.release(); else backup = new LottieFloat(style.letterSpace); style.letterSpace.copy(*static_cast(prop), false); } else if (style.lineSpace.sid == prop->sid) { if (release) style.lineSpace.release(); else backup = new LottieFloat(style.lineSpace); style.lineSpace.copy(*static_cast(prop), false); } else if (style.strokeWidth.sid == prop->sid) { if (release) style.strokeWidth.release(); else backup = new LottieFloat(style.strokeWidth); style.strokeWidth.copy(*static_cast(prop), false); } else if (style.fillOpacity.sid == prop->sid) { if (release) style.fillOpacity.release(); else backup = new LottieOpacity(style.fillOpacity); style.fillOpacity.copy(*static_cast(prop), false); } else if (style.strokeOpacity.sid == prop->sid) { if (release) style.strokeOpacity.release(); else backup = new LottieOpacity(style.strokeOpacity); style.strokeOpacity.copy(*static_cast(prop), false); } else if (style.opacity.sid == prop->sid) { if (release) style.opacity.release(); else backup = new LottieOpacity(style.opacity); style.opacity.copy(*static_cast(prop), false); } return backup; } }; struct LottieFont { enum Origin : uint8_t {Local = 0, CssURL, ScriptURL, FontURL}; ~LottieFont() { ARRAY_FOREACH(p, chars) delete(*p); tvg::free(style); tvg::free(family); tvg::free(name); tvg::free(b64src); } union { char* b64src = nullptr; char* path; }; Array chars; char* name = nullptr; char* family = nullptr; char* style = nullptr; uint32_t size = 0; float ascent = 0.0f; Origin origin = Local; void prepare(); }; struct LottieMarker { char* name = nullptr; float time = 0.0f; float duration = 0.0f; ~LottieMarker() { tvg::free(name); } }; struct LottieTextFollowPath { private: RenderPath path; PathCommand* cmds; uint32_t cmdsCnt; Point* pts; Point* start; float totalLen; float currentLen; Point split(float dLen, float lenSearched, float& angle); public: LottieFloat firstMargin = 0.0f; LottieMask* mask; int8_t maskIdx = -1; Point position(float lenSearched, float& angle); float prepare(LottieMask* mask, float frameNo, float scale, Tween& tween, LottieExpressions* exps); }; struct LottieText : LottieObject, LottieRenderPooler { struct AlignOption { enum Group : uint8_t { Chars = 1, Word = 2, Line = 3, All = 4 }; Group group = Chars; LottieScalar anchor{}; } alignOp; LottieText() { LottieObject::type = LottieObject::Text; } LottieProperty* override(LottieProperty* prop, bool release) override { LottieProperty* backup = nullptr; if (release) doc.release(); else backup = new LottieTextDoc(doc); doc.copy(*static_cast(prop), false); return backup; } LottieProperty* property(uint16_t ix) override { if (doc.ix == ix) return &doc; return nullptr; } LottieTextDoc doc; LottieFont* font = nullptr; LottieTextFollowPath* follow = nullptr; Array ranges; ~LottieText() { ARRAY_FOREACH(p, ranges) delete(*p); delete(follow); } }; struct LottieTrimpath : LottieObject { enum Type : uint8_t { Simultaneous = 1, Individual = 2 }; LottieTrimpath() { LottieObject::type = LottieObject::Trimpath; } bool mergeable() override { if (!start.frames && start.value == 0.0f && !end.frames && end.value == 100.0f && !offset.frames && offset.value == 0.0f) return true; return false; } LottieProperty* property(uint16_t ix) override { if (start.ix == ix) return &start; if (end.ix == ix) return &end; if (offset.ix == ix) return &offset; return nullptr; } void segment(float frameNo, float& start, float& end, Tween& tween, LottieExpressions* exps); LottieFloat start = 0.0f; LottieFloat end = 100.0f; LottieFloat offset = 0.0f; Type type = Simultaneous; }; struct LottieShape : LottieObject, LottieRenderPooler { bool clockwise = true; //clockwise or counter-clockwise virtual ~LottieShape() {} bool mergeable() override { return true; } LottieShape(LottieObject::Type type) { LottieObject::type = type; } }; struct LottieRoundedCorner : LottieObject { LottieRoundedCorner() { LottieObject::type = LottieObject::RoundedCorner; } LottieProperty* property(uint16_t ix) override { if (radius.ix == ix) return &radius; return nullptr; } LottieFloat radius = 0.0f; }; struct LottiePath : LottieShape { LottiePath() : LottieShape(LottieObject::Path) {} LottieProperty* property(uint16_t ix) override { if (pathset.ix == ix) return &pathset; return nullptr; } LottiePathSet pathset; }; struct LottieRect : LottieShape { LottieRect() : LottieShape(LottieObject::Rect) {} LottieProperty* property(uint16_t ix) override { if (position.ix == ix) return &position; if (size.ix == ix) return &size; if (radius.ix == ix) return &radius; return nullptr; } LottieVector position = Point{0.0f, 0.0f}; LottieScalar size = Point{0.0f, 0.0f}; LottieFloat radius = 0.0f; //rounded corner radius }; struct LottiePolyStar : LottieShape { enum Type : uint8_t {Star = 1, Polygon}; LottiePolyStar() : LottieShape(LottieObject::Polystar) {} LottieProperty* property(uint16_t ix) override { if (position.ix == ix) return &position; if (innerRadius.ix == ix) return &innerRadius; if (outerRadius.ix == ix) return &outerRadius; if (innerRoundness.ix == ix) return &innerRoundness; if (outerRoundness.ix == ix) return &outerRoundness; if (rotation.ix == ix) return &rotation; if (ptsCnt.ix == ix) return &ptsCnt; return nullptr; } LottieVector position = Point{0.0f, 0.0f}; LottieFloat innerRadius = 0.0f; LottieFloat outerRadius = 0.0f; LottieFloat innerRoundness = 0.0f; LottieFloat outerRoundness = 0.0f; LottieFloat rotation = 0.0f; LottieFloat ptsCnt = 0.0f; Type type = Polygon; }; struct LottieEllipse : LottieShape { LottieEllipse() : LottieShape(LottieObject::Ellipse) {} LottieProperty* property(uint16_t ix) override { if (position.ix == ix) return &position; if (size.ix == ix) return &size; return nullptr; } LottieVector position = Point{0.0f, 0.0f}; LottieScalar size = Point{0.0f, 0.0f}; }; struct LottieTransform : LottieObject { struct SeparateCoord { LottieFloat x = 0.0f; LottieFloat y = 0.0f; }; SeparateCoord* separateCoord() { if (!coords) coords = new SeparateCoord; return coords; } struct RotationEx { LottieFloat x = 0.0f; LottieFloat y = 0.0f; }; ~LottieTransform() { delete(coords); delete(rotationEx); } LottieTransform() { LottieObject::type = LottieObject::Transform; } bool mergeable() override { return true; } LottieProperty* property(uint16_t ix) override { if (position.ix == ix) return &position; if (rotation.ix == ix) return &rotation; if (scale.ix == ix) return &scale; if (anchor.ix == ix) return &anchor; if (opacity.ix == ix) return &opacity; if (skewAngle.ix == ix) return &skewAngle; if (skewAxis.ix == ix) return &skewAxis; if (coords) { if (coords->x.ix == ix) return &coords->x; if (coords->y.ix == ix) return &coords->y; } return nullptr; } LottieProperty* override(LottieProperty* prop, bool release) override { LottieProperty* backup = nullptr; if (rotation.sid == prop->sid) { if (release) rotation.release(); else backup = new LottieFloat(rotation); rotation.copy(*static_cast(prop), false); } else if (scale.sid == prop->sid) { if (release) scale.release(); else backup = new LottieScalar(scale); scale.copy(*static_cast(prop), false); } else if (position.sid == prop->sid) { if (release) position.release(); else backup = new LottieVector(position); position.copy(*static_cast(prop), false); } else if (opacity.sid == prop->sid) { if (release) opacity.release(); else backup = new LottieOpacity(opacity); opacity.copy(*static_cast(prop), false); } else if (skewAngle.sid == prop->sid) { if (release) skewAngle.release(); else backup = new LottieFloat(skewAngle); skewAngle.copy(*static_cast(prop), false); } else if (skewAxis.sid == prop->sid) { if (release) skewAxis.release(); else backup = new LottieFloat(skewAxis); skewAxis.copy(*static_cast(prop), false); } return backup; } LottieVector position = Point{0.0f, 0.0f}; LottieFloat rotation = 0.0f; //z rotation LottieScalar scale = Point{100.0f, 100.0f}; LottieScalar anchor = Point{0.0f, 0.0f}; LottieOpacity opacity = 255; LottieFloat skewAngle = 0.0f; LottieFloat skewAxis = 0.0f; SeparateCoord* coords = nullptr; //either a position or separate coordinates RotationEx* rotationEx = nullptr; //extension for 3d rotation }; struct LottieSolid : LottieObject { LottieColor color = RGB32{255, 255, 255}; LottieOpacity opacity = 255; LottieProperty* property(uint16_t ix) override { if (color.ix == ix) return &color; if (opacity.ix == ix) return &opacity; return nullptr; } }; struct LottieSolidStroke : LottieSolid, LottieStroke { LottieSolidStroke() { LottieObject::type = LottieObject::SolidStroke; } LottieProperty* property(uint16_t ix) override { if (width.ix == ix) return &width; if (dashattr) { for (uint8_t i = 0; i < dashattr->size ; ++i) if (dashattr->values[i].ix == ix) return &dashattr->values[i]; } return LottieSolid::property(ix); } LottieProperty* override(LottieProperty* prop, bool release) override { LottieProperty* backup = nullptr; if (release) color.release(); else backup = new LottieColor(color); color.copy(*static_cast(prop), false); return backup; } }; struct LottieSolidFill : LottieSolid { LottieSolidFill() { LottieObject::type = LottieObject::SolidFill; } LottieProperty* override(LottieProperty* prop, bool release) override { LottieProperty* backup = nullptr; if (color.sid == prop->sid) { if (release) color.release(); else backup = new LottieColor(color); color.copy(*static_cast(prop), false); } else if (opacity.sid == prop->sid) { if (release) opacity.release(); else backup = new LottieOpacity(opacity); opacity.copy(*static_cast(prop), false); } return backup; } FillRule rule = FillRule::NonZero; }; struct LottieGradient : LottieObject { bool prepare() { if (!colorStops.populated) { auto count = colorStops.count; //colorstop count can be modified after population if (colorStops.frames) { ARRAY_FOREACH(v, *colorStops.frames) { colorStops.count = populate(v->value, count); } } else { colorStops.count = populate(colorStops.value, count); } colorStops.populated = true; } if (start.frames || end.frames || height.frames || angle.frames || opacity.frames || colorStops.frames) return true; return false; } LottieProperty* property(uint16_t ix) override { if (start.ix == ix) return &start; if (end.ix == ix) return &end; if (height.ix == ix) return &height; if (angle.ix == ix) return ∠ if (opacity.ix == ix) return &opacity; if (colorStops.ix == ix) return &colorStops; return nullptr; } LottieProperty* override(LottieProperty* prop, bool release) override { LottieProperty* backup = nullptr; if (release) colorStops.release(); else backup = new LottieColorStop(colorStops); colorStops.copy(*static_cast(prop), false); prepare(); return backup; } uint32_t populate(ColorStop& color, size_t count); Fill* fill(float frameNo, uint8_t opacity, Tween& tween, LottieExpressions* exps); LottieScalar start = Point{0.0f, 0.0f}; LottieScalar end = Point{0.0f, 0.0f}; LottieFloat height = 0.0f; LottieFloat angle = 0.0f; LottieOpacity opacity = 255; LottieColorStop colorStops; uint8_t id = 0; //1: linear, 2: radial bool opaque = true; //fully opaque or not }; struct LottieGradientFill : LottieGradient { LottieGradientFill() { LottieObject::type = LottieObject::GradientFill; } FillRule rule = FillRule::NonZero; }; struct LottieGradientStroke : LottieGradient, LottieStroke { LottieGradientStroke() { LottieObject::type = LottieObject::GradientStroke; } LottieProperty* property(uint16_t ix) override { if (width.ix == ix) return &width; if (dashattr) { for (uint8_t i = 0; i < dashattr->size ; ++i) if (dashattr->values[i].ix == ix) return &dashattr->values[i]; } return LottieGradient::property(ix); } }; struct LottieImage : LottieObject { LottieBitmap bitmap; bool resolved = false; LottieProperty* override(LottieProperty* prop, bool release) override { LottieProperty* backup = nullptr; if (release) bitmap.release(); else backup = new LottieBitmap(bitmap); bitmap.copy(*static_cast(prop), false); return backup; } void prepare(); }; struct LottieRepeater : LottieObject { LottieRepeater() { LottieObject::type = LottieObject::Repeater; } LottieProperty* property(uint16_t ix) override { if (copies.ix == ix) return &copies; if (offset.ix == ix) return &offset; if (position.ix == ix) return &position; if (rotation.ix == ix) return &rotation; if (scale.ix == ix) return &scale; if (anchor.ix == ix) return &anchor; if (startOpacity.ix == ix) return &startOpacity; if (endOpacity.ix == ix) return &endOpacity; return nullptr; } LottieFloat copies = 0.0f; LottieFloat offset = 0.0f; //Transform LottieVector position = Point{0.0f, 0.0f}; LottieFloat rotation = 0.0f; LottieScalar scale = Point{100.0f, 100.0f}; LottieScalar anchor = Point{0.0f, 0.0f}; LottieOpacity startOpacity = 255; LottieOpacity endOpacity = 255; bool inorder = true; //true: higher, false: lower }; struct LottieOffsetPath : LottieObject { LottieOffsetPath() { LottieObject::type = LottieObject::OffsetPath; } LottieFloat offset = 0.0f; LottieFloat miterLimit = 4.0f; StrokeJoin join = StrokeJoin::Miter; }; struct LottieGroup : LottieObject, LottieRenderPooler { LottieGroup(); virtual ~LottieGroup() { ARRAY_FOREACH(p, children) delete(*p); } void prepare(LottieObject::Type type = LottieObject::Group); bool mergeable() override { return allowMerge; } LottieProperty* property(uint16_t ix) override; LottieObject* content(unsigned long id) { if (this->id == id) return this; //source has children, find recursively. ARRAY_FOREACH(p, children) { auto child = *p; if (child->type == LottieObject::Type::Group || child->type == LottieObject::Type::Layer) { if (auto ret = static_cast(child)->content(id)) return ret; } else if (child->id == id) return child; } return nullptr; } Scene* scene = nullptr; Array children; BlendMethod blendMethod = BlendMethod::Normal; bool reqFragment : 1; //requirement to fragment the render context bool buildDone : 1; //completed in building the composition. bool trimpath : 1; //this group has a trimpath. bool visible : 1; //this group has visible contents. bool allowMerge : 1; //if this group is consisted of simple (transformed) shapes. }; struct LottieLayer : LottieGroup { enum Type : uint8_t {Precomp = 0, Solid, Image, Null, Shape, Text}; ~LottieLayer(); bool mergeable() override { return false; } void prepare(RGB32* color = nullptr); float remap(LottieComposition* comp, float frameNo, LottieExpressions* exp); LottieProperty* property(uint16_t ix) override; bool assign(const char* layer, uint32_t ix, const char* var, float val); char* name = nullptr; LottieLayer* parent = nullptr; LottieFloat timeRemap = -1.0f; LottieLayer* comp = nullptr; //Precompositor, current layer is belonges. LottieTransform* transform = nullptr; Array masks; Array effects; LottieLayer* matteTarget = nullptr; LottieRenderPooler statical; //static pooler for solid fill and clipper float timeStretch = 1.0f; float w = 0.0f, h = 0.0f; float inFrame = 0.0f; float outFrame = 0.0f; float startFrame = 0.0f; unsigned long rid = 0; //pre-composition reference id. int16_t mix = -1; //index of the matte layer. int16_t pix = -1; //index of the parent layer. int16_t ix = -1; //index of the current layer. struct { float frameNo = -1.0f; Matrix matrix; uint8_t opacity; } cache; MaskMethod matteType = MaskMethod::None; Type type = Null; bool autoOrient = false; bool matteSrc = false; LottieEffect* effectById(unsigned long id) { ARRAY_FOREACH(p, effects) { if (id == (*p)->nm || id == (*p)->mn) return *p; } return nullptr; } LottieEffect* effectByIdx(int16_t ix) { ARRAY_FOREACH(p, effects) { if (ix == (*p)->ix) return *p; } return nullptr; } LottieLayer* layerById(unsigned long id) { ARRAY_FOREACH(p, children) { if ((*p)->type != LottieObject::Type::Layer) continue; auto layer = static_cast(*p); if (layer->id == id) return layer; } return nullptr; } LottieLayer* layerByIdx(int16_t ix) { ARRAY_FOREACH(p, children) { if ((*p)->type != LottieObject::Type::Layer) continue; auto layer = static_cast(*p); if (layer->ix == ix) return layer; } return nullptr; } }; struct LottieSlot { struct Pair { LottieObject* obj; LottieProperty* prop; }; void add(uint32_t slotcode, LottieProperty* prop); void apply(LottieProperty* prop, bool byDefault = false); void reset(); LottieSlot(LottieLayer* layer, LottieObject* parent, unsigned long sid, LottieObject* obj, LottieProperty::Type type) : context{layer, parent}, sid(sid), type(type) { pairs.push({obj, nullptr}); } ~LottieSlot() { if (!overridden) return; ARRAY_FOREACH(pair, pairs) delete(pair->prop); } struct { LottieLayer* layer; LottieObject* parent; } context; unsigned long sid; // djb2 encoded Array pairs; // object-property pairs that can be overridden by this slot LottieProperty::Type type; bool overridden = false; }; struct LottieComposition { ~LottieComposition(); void clear() { if (root && root->scene) root->scene->remove(); } float duration() const { return frameCnt() / frameRate; // in second } float frameAtTime(float timeInSec) const { auto p = timeInSec / duration(); if (p < 0.0f) p = 0.0f; return p * frameCnt(); } float timeAtFrame(float frameNo) { return (frameNo - root->inFrame) / frameRate; } float frameCnt() const { return root->outFrame - root->inFrame; } LottieLayer* asset(unsigned long id) { ARRAY_FOREACH(p, assets) { auto layer = static_cast(*p); if (layer->id == id) return layer; } return nullptr; } void clamp(float& frameNo) { frameNo += root->inFrame; if (frameNo < root->inFrame) frameNo = root->inFrame; if (frameNo >= root->outFrame) frameNo = root->outFrame - 1; } LottieLayer* root = nullptr; char* version = nullptr; char* name = nullptr; float w, h; float frameRate; Array assets; Array interpolators; Array fonts; Array slots; Array markers; bool expressions = false; bool initiated = false; uint8_t quality = 50; }; #endif //_TVG_LOTTIE_MODEL_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/sw_engine/tvgSwImage.cpp000664 001750 001750 00000010227 15164251010 036274 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgMath.h" #include "tvgSwCommon.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static inline bool _onlyShifted(const Matrix& m) { if (tvg::equal(m.e11, 1.0f) && tvg::equal(m.e22, 1.0f) && tvg::zero(m.e12) && tvg::zero(m.e21)) return true; return false; } static bool _genOutline(SwImage& image, const Matrix& transform, SwMpool* mpool, unsigned tid) { image.outline = mpoolReqOutline(mpool, tid); auto outline = image.outline; outline->pts.reserve(5); outline->types.reserve(5); outline->cntrs.reserve(1); outline->closed.reserve(1); Point to[4]; auto w = static_cast(image.w); auto h = static_cast(image.h); to[0] = {0, 0}; to[1] = {w, 0}; to[2] = {w, h}; to[3] = {0, h}; for (int i = 0; i < 4; i++) { outline->pts.push(mathTransform(&to[i], transform)); outline->types.push(SW_CURVE_TYPE_POINT); } outline->pts.push(outline->pts[0]); outline->types.push(SW_CURVE_TYPE_POINT); outline->cntrs.push(outline->pts.count - 1); outline->closed.push(true); image.outline = outline; image.outline->fillRule = FillRule::NonZero; return true; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ bool imagePrepare(SwImage& image, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid) { image.direct = _onlyShifted(transform); //Fast track: Non-transformed image but just shifted. if (image.direct) { image.ox = -static_cast(nearbyint(transform.e13)); image.oy = -static_cast(nearbyint(transform.e23)); //Figure out the scale factor by transform matrix } else { auto scaleX = sqrtf((transform.e11 * transform.e11) + (transform.e21 * transform.e21)); auto scaleY = sqrtf((transform.e22 * transform.e22) + (transform.e12 * transform.e12)); image.scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX; if (tvg::zero(transform.e12) && tvg::zero(transform.e21)) image.scaled = true; else image.scaled = false; } if (!_genOutline(image, transform, mpool, tid)) return false; return mathUpdateOutlineBBox(image.outline, clipBox, renderBox, image.direct); } bool imageGenRle(SwImage& image, const RenderRegion& renderBox, SwMpool* mpool, unsigned tid, bool antiAlias) { if ((image.rle = rleRender(image.rle, image.outline, renderBox, mpool, tid, antiAlias))) return true; return false; } void imageDelOutline(SwImage& image, SwMpool* mpool, uint32_t tid) { image.outline = nullptr; } void imageReset(SwImage& image) { rleReset(image.rle); } void imageFree(SwImage& image) { rleFree(image.rle); image.rle = nullptr; } glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser.cpp000664 001750 001750 00000276731 15164251010 043661 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-exceptions.h" #include "ecma-extended-info.h" #include "ecma-helpers.h" #include "ecma-literal-storage.h" #include "ecma-module.h" #include "jcontext.h" #include "js-parser-internal.h" #include "lit-char-helpers.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_parser Parser * @{ */ #if JERRY_PARSER JERRY_STATIC_ASSERT (((int) ECMA_PARSE_STRICT_MODE == (int) PARSER_IS_STRICT), ecma_parse_strict_mode_must_be_equal_to_parser_is_strict); JERRY_STATIC_ASSERT (PARSER_SAVE_STATUS_FLAGS (PARSER_ALLOW_SUPER) == 0x1, incorrect_saving_of_ecma_parse_allow_super); JERRY_STATIC_ASSERT ((PARSER_RESTORE_STATUS_FLAGS (ECMA_PARSE_ALLOW_SUPER) == PARSER_ALLOW_SUPER), incorrect_restoring_of_ecma_parse_allow_super); JERRY_STATIC_ASSERT ((PARSER_RESTORE_STATUS_FLAGS (ECMA_PARSE_FUNCTION_CONTEXT) == 0), ecma_parse_function_context_must_not_be_transformed); /** * Compute real literal indices. * * @return length of the prefix opcodes */ static void parser_compute_indices (parser_context_t *context_p, /**< context */ uint16_t *ident_end, /**< end of the identifier group */ uint16_t *const_literal_end) /**< end of the const literal group */ { parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; uint16_t ident_count = 0; uint16_t const_literal_count = 0; uint16_t ident_index; uint16_t const_literal_index; uint16_t literal_index; /* First phase: count the number of items in each group. */ parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) { switch (literal_p->type) { case LEXER_IDENT_LITERAL: { if (literal_p->status_flags & LEXER_FLAG_USED) { ident_count++; break; } else if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) { jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length); /* This literal should not be freed even if an error is encountered later. */ literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR; } continue; } case LEXER_STRING_LITERAL: { const_literal_count++; break; } case LEXER_NUMBER_LITERAL: { const_literal_count++; continue; } case LEXER_FUNCTION_LITERAL: case LEXER_REGEXP_LITERAL: { continue; } default: { JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL); continue; } } const uint8_t *char_p = literal_p->u.char_p; uint32_t status_flags = context_p->status_flags; if ((literal_p->status_flags & LEXER_FLAG_SOURCE_PTR) && literal_p->prop.length < 0xfff) { size_t bytes_to_end = (size_t) (context_p->source_end_p - char_p); if (bytes_to_end < 0xfffff) { literal_p->u.source_data = ((uint32_t) bytes_to_end) | (((uint32_t) literal_p->prop.length) << 20); literal_p->status_flags |= LEXER_FLAG_LATE_INIT; status_flags |= PARSER_HAS_LATE_LIT_INIT; context_p->status_flags = status_flags; char_p = NULL; } } if (char_p != NULL) { literal_p->u.value = ecma_find_or_create_literal_string (char_p, literal_p->prop.length, (literal_p->status_flags & LEXER_FLAG_ASCII) != 0); if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) { jmem_heap_free_block ((void *) char_p, literal_p->prop.length); /* This literal should not be freed even if an error is encountered later. */ literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR; } } } ident_index = context_p->register_count; const_literal_index = (uint16_t) (ident_index + ident_count); literal_index = (uint16_t) (const_literal_index + const_literal_count); /* Second phase: Assign an index to each literal. */ parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) { switch (literal_p->type) { case LEXER_IDENT_LITERAL: { if (literal_p->status_flags & LEXER_FLAG_USED) { literal_p->prop.index = ident_index; ident_index++; } break; } case LEXER_STRING_LITERAL: case LEXER_NUMBER_LITERAL: { JERRY_ASSERT ((literal_p->status_flags & ~(LEXER_FLAG_SOURCE_PTR | LEXER_FLAG_LATE_INIT)) == 0); literal_p->prop.index = const_literal_index; const_literal_index++; break; } case LEXER_FUNCTION_LITERAL: case LEXER_REGEXP_LITERAL: { JERRY_ASSERT (literal_p->status_flags == 0); literal_p->prop.index = literal_index; literal_index++; break; } default: { JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL && literal_p->status_flags == LEXER_FLAG_FUNCTION_ARGUMENT); break; } } } JERRY_ASSERT (ident_index == context_p->register_count + ident_count); JERRY_ASSERT (const_literal_index == ident_index + const_literal_count); JERRY_ASSERT (literal_index <= context_p->register_count + context_p->literal_count); context_p->literal_count = literal_index; *ident_end = ident_index; *const_literal_end = const_literal_index; } /* parser_compute_indices */ /** * Initialize literal pool. */ static void parser_init_literal_pool (parser_context_t *context_p, /**< context */ ecma_value_t *literal_pool_p) /**< start of literal pool */ { parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) { switch (literal_p->type) { case LEXER_IDENT_LITERAL: { if (!(literal_p->status_flags & LEXER_FLAG_USED)) { break; } /* FALLTHRU */ } case LEXER_STRING_LITERAL: { ecma_value_t lit_value = literal_p->u.value; JERRY_ASSERT (literal_p->prop.index >= context_p->register_count); literal_pool_p[literal_p->prop.index] = lit_value; break; } case LEXER_NUMBER_LITERAL: { JERRY_ASSERT (literal_p->prop.index >= context_p->register_count); literal_pool_p[literal_p->prop.index] = literal_p->u.value; break; } case LEXER_FUNCTION_LITERAL: case LEXER_REGEXP_LITERAL: { JERRY_ASSERT (literal_p->prop.index >= context_p->register_count); ECMA_SET_INTERNAL_VALUE_POINTER (literal_pool_p[literal_p->prop.index], literal_p->u.bytecode_p); break; } default: { JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL); break; } } } } /* parser_init_literal_pool */ /* * During byte code post processing certain bytes are not * copied into the final byte code buffer. For example, if * one byte is enough for encoding a literal index, the * second byte is not copied. However, when a byte is skipped, * the offsets of those branches which crosses (jumps over) * that byte code should also be decreased by one. Instead * of finding these jumps every time when a byte is skipped, * all branch offset updates are computed in one step. * * Branch offset mapping example: * * Let's assume that each parser_mem_page of the byte_code * buffer is 8 bytes long and only 4 bytes are kept for a * given page: * * +---+---+---+---+---+---+---+---+ * | X | 1 | 2 | 3 | X | 4 | X | X | * +---+---+---+---+---+---+---+---+ * * X marks those bytes which are removed. The resulting * offset mapping is the following: * * +---+---+---+---+---+---+---+---+ * | 0 | 1 | 2 | 3 | 3 | 4 | 4 | 4 | * +---+---+---+---+---+---+---+---+ * * Each X is simply replaced by the index of the previous * index starting from zero. This shows the number of * copied bytes before a given byte including the byte * itself. The last byte always shows the number of bytes * copied from this page. * * This mapping allows recomputing all branch targets, * since mapping[to] - mapping[from] is the new argument * for forward branches. As for backward branches, the * equation is reversed to mapping[from] - mapping[to]. * * The mapping is relative to one page, so distance * computation affecting multiple pages requires a loop. * We should also note that only argument bytes can * be skipped, so removed bytes cannot be targeted by * branches. Valid branches always target instruction * starts only. */ /** * Recompute the argument of a forward branch. * * @return the new distance */ static size_t parser_update_forward_branch (parser_mem_page_t *page_p, /**< current page */ size_t full_distance, /**< full distance */ uint8_t bytes_copied_before_jump) /**< bytes copied before jump */ { size_t new_distance = 0; while (full_distance > PARSER_CBC_STREAM_PAGE_SIZE) { new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK; full_distance -= PARSER_CBC_STREAM_PAGE_SIZE; page_p = page_p->next_p; } new_distance += page_p->bytes[full_distance - 1] & CBC_LOWER_SEVEN_BIT_MASK; return new_distance - bytes_copied_before_jump; } /* parser_update_forward_branch */ /** * Recompute the argument of a backward branch. * * @return the new distance */ static size_t parser_update_backward_branch (parser_mem_page_t *page_p, /**< current page */ size_t full_distance, /**< full distance */ uint8_t bytes_copied_before_jump) /**< bytes copied before jump */ { size_t new_distance = bytes_copied_before_jump; while (full_distance >= PARSER_CBC_STREAM_PAGE_SIZE) { JERRY_ASSERT (page_p != NULL); new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK; full_distance -= PARSER_CBC_STREAM_PAGE_SIZE; page_p = page_p->next_p; } if (full_distance > 0) { size_t offset = PARSER_CBC_STREAM_PAGE_SIZE - full_distance; JERRY_ASSERT (page_p != NULL); new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK; new_distance -= page_p->bytes[offset - 1] & CBC_LOWER_SEVEN_BIT_MASK; } return new_distance; } /* parser_update_backward_branch */ /** * Update targets of all branches in one step. */ static void parse_update_branches (parser_context_t *context_p, /**< context */ uint8_t *byte_code_p) /**< byte code */ { parser_mem_page_t *page_p = context_p->byte_code.first_p; parser_mem_page_t *prev_page_p = NULL; parser_mem_page_t *last_page_p = context_p->byte_code.last_p; size_t last_position = context_p->byte_code.last_position; size_t offset = 0; size_t bytes_copied = 0; if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE) { last_page_p = NULL; last_position = 0; } while (page_p != last_page_p || offset < last_position) { /* Branch instructions are marked to improve search speed. */ if (page_p->bytes[offset] & CBC_HIGHEST_BIT_MASK) { uint8_t *bytes_p = byte_code_p + bytes_copied; uint8_t flags; uint8_t bytes_copied_before_jump = 0; size_t branch_argument_length; size_t target_distance; size_t length; if (offset > 0) { bytes_copied_before_jump = page_p->bytes[offset - 1] & CBC_LOWER_SEVEN_BIT_MASK; } bytes_p += bytes_copied_before_jump; if (*bytes_p == CBC_EXT_OPCODE) { bytes_p++; flags = cbc_ext_flags[*bytes_p]; } else { flags = cbc_flags[*bytes_p]; } JERRY_ASSERT (flags & CBC_HAS_BRANCH_ARG); branch_argument_length = CBC_BRANCH_OFFSET_LENGTH (*bytes_p); bytes_p++; /* Decoding target. */ length = branch_argument_length; target_distance = 0; do { target_distance = (target_distance << 8) | *bytes_p; bytes_p++; } while (--length > 0); if (CBC_BRANCH_IS_FORWARD (flags)) { /* Branch target was not set. */ JERRY_ASSERT (target_distance > 0); target_distance = parser_update_forward_branch (page_p, offset + target_distance, bytes_copied_before_jump); } else { if (target_distance < offset) { uint8_t bytes_copied_before_target = page_p->bytes[offset - target_distance - 1]; bytes_copied_before_target = bytes_copied_before_target & CBC_LOWER_SEVEN_BIT_MASK; target_distance = (size_t) (bytes_copied_before_jump - bytes_copied_before_target); } else if (target_distance == offset) { target_distance = bytes_copied_before_jump; } else { target_distance = parser_update_backward_branch (prev_page_p, target_distance - offset, bytes_copied_before_jump); } } /* Encoding target again. */ do { bytes_p--; *bytes_p = (uint8_t) (target_distance & 0xff); target_distance >>= 8; } while (--branch_argument_length > 0); } offset++; if (offset >= PARSER_CBC_STREAM_PAGE_SIZE) { parser_mem_page_t *next_p = page_p->next_p; /* We reverse the pages before the current page. */ page_p->next_p = prev_page_p; prev_page_p = page_p; bytes_copied += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK; page_p = next_p; offset = 0; } } /* After this point the pages of the byte code stream are * not used anymore. However, they needs to be freed during * cleanup, so the first and last pointers of the stream * descriptor are reversed as well. */ if (last_page_p != NULL) { JERRY_ASSERT (last_page_p == context_p->byte_code.last_p); last_page_p->next_p = prev_page_p; } else { last_page_p = context_p->byte_code.last_p; } context_p->byte_code.last_p = context_p->byte_code.first_p; context_p->byte_code.first_p = last_page_p; } /* parse_update_branches */ /** * Forward iterator: move to the next byte code * * @param page_p page * @param offset offset */ #define PARSER_NEXT_BYTE(page_p, offset) \ do \ { \ if (++(offset) >= PARSER_CBC_STREAM_PAGE_SIZE) \ { \ offset = 0; \ page_p = page_p->next_p; \ } \ } while (0) /** * Forward iterator: move to the next byte code. Also updates the offset of the previous byte code. * * @param page_p page * @param offset offset * @param real_offset real offset */ #define PARSER_NEXT_BYTE_UPDATE(page_p, offset, real_offset) \ do \ { \ page_p->bytes[offset] = real_offset; \ if (++(offset) >= PARSER_CBC_STREAM_PAGE_SIZE) \ { \ offset = 0; \ real_offset = 0; \ page_p = page_p->next_p; \ } \ } while (0) /** * Post processing main function. * * @return compiled code */ static ecma_compiled_code_t * parser_post_processing (parser_context_t *context_p) /**< context */ { uint16_t literal_one_byte_limit; uint16_t ident_end; uint16_t const_literal_end; parser_mem_page_t *page_p; parser_mem_page_t *last_page_p; size_t last_position; size_t offset; size_t length; size_t literal_length; size_t total_size; uint8_t real_offset; uint8_t *byte_code_p; bool needs_uint16_arguments; cbc_opcode_t last_opcode = CBC_EXT_OPCODE; ecma_compiled_code_t *compiled_code_p; ecma_value_t *literal_pool_p; uint8_t *dst_p; if ((context_p->status_flags & (PARSER_IS_FUNCTION | PARSER_LEXICAL_BLOCK_NEEDED)) == (PARSER_IS_FUNCTION | PARSER_LEXICAL_BLOCK_NEEDED)) { PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); #ifndef JERRY_NDEBUG PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ context_p->status_flags &= (uint32_t) ~PARSER_LEXICAL_BLOCK_NEEDED; parser_emit_cbc (context_p, CBC_CONTEXT_END); parser_branch_t branch; parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); parser_set_branch_to_current_position (context_p, &branch); JERRY_ASSERT (!(context_p->status_flags & PARSER_NO_END_LABEL)); } if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags)) { PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); #ifndef JERRY_NDEBUG PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ if (context_p->stack_limit < PARSER_FINALLY_CONTEXT_STACK_ALLOCATION) { context_p->stack_limit = PARSER_FINALLY_CONTEXT_STACK_ALLOCATION; } parser_branch_t branch; parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); parser_set_branch_to_current_position (context_p, &branch); JERRY_ASSERT (!(context_p->status_flags & PARSER_NO_END_LABEL)); } JERRY_ASSERT (context_p->stack_depth == 0); #ifndef JERRY_NDEBUG JERRY_ASSERT (context_p->context_stack_depth == 0); #endif /* !JERRY_NDEBUG */ if ((size_t) context_p->stack_limit + (size_t) context_p->register_count > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } if (JERRY_UNLIKELY (context_p->script_p->refs_and_type >= CBC_SCRIPT_REF_MAX)) { /* This is probably never happens in practice. */ jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT); } context_p->script_p->refs_and_type += CBC_SCRIPT_REF_ONE; JERRY_ASSERT (context_p->literal_count <= PARSER_MAXIMUM_NUMBER_OF_LITERALS); parser_compute_indices (context_p, &ident_end, &const_literal_end); if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE) { literal_one_byte_limit = CBC_MAXIMUM_BYTE_VALUE - 1; } else { literal_one_byte_limit = CBC_LOWER_SEVEN_BIT_MASK; } last_page_p = context_p->byte_code.last_p; last_position = context_p->byte_code.last_position; if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE) { last_page_p = NULL; last_position = 0; } page_p = context_p->byte_code.first_p; offset = 0; length = 0; while (page_p != last_page_p || offset < last_position) { uint8_t *opcode_p; uint8_t flags; size_t branch_offset_length; opcode_p = page_p->bytes + offset; last_opcode = (cbc_opcode_t) (*opcode_p); PARSER_NEXT_BYTE (page_p, offset); branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (last_opcode); flags = cbc_flags[last_opcode]; length++; switch (last_opcode) { case CBC_EXT_OPCODE: { cbc_ext_opcode_t ext_opcode; ext_opcode = (cbc_ext_opcode_t) page_p->bytes[offset]; branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (ext_opcode); flags = cbc_ext_flags[ext_opcode]; PARSER_NEXT_BYTE (page_p, offset); length++; break; } case CBC_POST_DECR: { *opcode_p = CBC_PRE_DECR; break; } case CBC_POST_INCR: { *opcode_p = CBC_PRE_INCR; break; } case CBC_POST_DECR_IDENT: { *opcode_p = CBC_PRE_DECR_IDENT; break; } case CBC_POST_INCR_IDENT: { *opcode_p = CBC_PRE_INCR_IDENT; break; } default: { break; } } while (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) { uint8_t *first_byte = page_p->bytes + offset; uint32_t literal_index = *first_byte; PARSER_NEXT_BYTE (page_p, offset); length++; literal_index |= ((uint32_t) page_p->bytes[offset]) << 8; if (literal_index >= PARSER_REGISTER_START) { literal_index -= PARSER_REGISTER_START; } else { literal_index = (PARSER_GET_LITERAL (literal_index))->prop.index; } if (literal_index <= literal_one_byte_limit) { *first_byte = (uint8_t) literal_index; } else { if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE) { JERRY_ASSERT (literal_index <= CBC_MAXIMUM_SMALL_VALUE); *first_byte = CBC_MAXIMUM_BYTE_VALUE; page_p->bytes[offset] = (uint8_t) (literal_index - CBC_MAXIMUM_BYTE_VALUE); length++; } else { JERRY_ASSERT (literal_index <= CBC_MAXIMUM_FULL_VALUE); *first_byte = (uint8_t) ((literal_index >> 8) | CBC_HIGHEST_BIT_MASK); page_p->bytes[offset] = (uint8_t) (literal_index & 0xff); length++; } } PARSER_NEXT_BYTE (page_p, offset); if (flags & CBC_HAS_LITERAL_ARG2) { if (flags & CBC_HAS_LITERAL_ARG) { flags = CBC_HAS_LITERAL_ARG; } else { flags = CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2; } } else { break; } } if (flags & CBC_HAS_BYTE_ARG) { /* This argument will be copied without modification. */ PARSER_NEXT_BYTE (page_p, offset); length++; } if (flags & CBC_HAS_BRANCH_ARG) { bool prefix_zero = true; /* The leading zeroes are dropped from the stream. * Although dropping these zeroes for backward * branches are unnecessary, we use the same * code path for simplicity. */ JERRY_ASSERT (branch_offset_length > 0 && branch_offset_length <= 3); while (--branch_offset_length > 0) { uint8_t byte = page_p->bytes[offset]; if (byte > 0 || !prefix_zero) { prefix_zero = false; length++; } else { JERRY_ASSERT (CBC_BRANCH_IS_FORWARD (flags)); } PARSER_NEXT_BYTE (page_p, offset); } if (last_opcode == (cbc_opcode_t) (CBC_JUMP_FORWARD + PARSER_MAX_BRANCH_LENGTH - 1) && prefix_zero && page_p->bytes[offset] == PARSER_MAX_BRANCH_LENGTH + 1) { /* Unconditional jumps which jump right after the instruction * are effectively NOPs. These jumps are removed from the * stream. The 1 byte long CBC_JUMP_FORWARD form marks these * instructions, since this form is constructed during post * processing and cannot be emitted directly. */ *opcode_p = CBC_JUMP_FORWARD; length--; } else { /* Other last bytes are always copied. */ length++; } PARSER_NEXT_BYTE (page_p, offset); } } if (!(context_p->status_flags & PARSER_NO_END_LABEL) || !(PARSER_OPCODE_IS_RETURN (last_opcode))) { context_p->status_flags &= (uint32_t) ~PARSER_NO_END_LABEL; if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags)) { length++; } length++; } needs_uint16_arguments = false; total_size = sizeof (cbc_uint8_arguments_t); if (context_p->stack_limit > CBC_MAXIMUM_BYTE_VALUE || context_p->register_count > CBC_MAXIMUM_BYTE_VALUE || context_p->literal_count > CBC_MAXIMUM_BYTE_VALUE) { needs_uint16_arguments = true; total_size = sizeof (cbc_uint16_arguments_t); } literal_length = (size_t) (context_p->literal_count - context_p->register_count) * sizeof (ecma_value_t); total_size += literal_length + length; if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags)) { total_size += context_p->argument_count * sizeof (ecma_value_t); } /* function.name */ if (!(context_p->status_flags & PARSER_CLASS_CONSTRUCTOR)) { total_size += sizeof (ecma_value_t); } if (context_p->tagged_template_literal_cp != JMEM_CP_NULL) { total_size += sizeof (ecma_value_t); } uint8_t extended_info = 0; if (context_p->argument_length != UINT16_MAX) { extended_info |= CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH; total_size += ecma_extended_info_get_encoded_length (context_p->argument_length); } if (extended_info != 0) { total_size += sizeof (uint8_t); } total_size = JERRY_ALIGNUP (total_size, JMEM_ALIGNMENT); compiled_code_p = (ecma_compiled_code_t *) parser_malloc (context_p, total_size); #if JERRY_SNAPSHOT_SAVE || JERRY_PARSER_DUMP_BYTE_CODE // Avoid getting junk bytes memset (compiled_code_p, 0, total_size); #endif /* JERRY_SNAPSHOT_SAVE || JERRY_PARSER_DUMP_BYTE_CODE */ byte_code_p = (uint8_t *) compiled_code_p; compiled_code_p->size = (uint16_t) (total_size >> JMEM_ALIGNMENT_LOG); compiled_code_p->refs = 1; compiled_code_p->status_flags = 0; if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) { JERRY_ASSERT (context_p->argument_count > 0); context_p->argument_count--; } if (needs_uint16_arguments) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p; args_p->stack_limit = context_p->stack_limit; args_p->script_value = context_p->script_value; args_p->argument_end = context_p->argument_count; args_p->register_end = context_p->register_count; args_p->ident_end = ident_end; args_p->const_literal_end = const_literal_end; args_p->literal_end = context_p->literal_count; compiled_code_p->status_flags |= CBC_CODE_FLAGS_UINT16_ARGUMENTS; byte_code_p += sizeof (cbc_uint16_arguments_t); } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p; args_p->stack_limit = (uint8_t) context_p->stack_limit; args_p->argument_end = (uint8_t) context_p->argument_count; args_p->script_value = context_p->script_value; args_p->register_end = (uint8_t) context_p->register_count; args_p->ident_end = (uint8_t) ident_end; args_p->const_literal_end = (uint8_t) const_literal_end; args_p->literal_end = (uint8_t) context_p->literal_count; byte_code_p += sizeof (cbc_uint8_arguments_t); } uint16_t encoding_limit; uint16_t encoding_delta; if (context_p->literal_count > CBC_MAXIMUM_SMALL_VALUE) { compiled_code_p->status_flags |= CBC_CODE_FLAGS_FULL_LITERAL_ENCODING; encoding_limit = CBC_FULL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_FULL_LITERAL_ENCODING_DELTA; } else { encoding_limit = CBC_SMALL_LITERAL_ENCODING_LIMIT; encoding_delta = CBC_SMALL_LITERAL_ENCODING_DELTA; } if (context_p->status_flags & PARSER_IS_STRICT) { compiled_code_p->status_flags |= CBC_CODE_FLAGS_STRICT_MODE; } if ((context_p->status_flags & PARSER_ARGUMENTS_NEEDED) && PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags)) { compiled_code_p->status_flags |= CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED; } if (!(context_p->status_flags & PARSER_LEXICAL_ENV_NEEDED)) { compiled_code_p->status_flags |= CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED; } uint16_t function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_NORMAL); if (context_p->status_flags & (PARSER_IS_PROPERTY_GETTER | PARSER_IS_PROPERTY_SETTER)) { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ACCESSOR); } else if (!(context_p->status_flags & PARSER_IS_FUNCTION)) { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_SCRIPT); } else if (context_p->status_flags & PARSER_IS_ARROW_FUNCTION) { if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ASYNC_ARROW); } else { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ARROW); } } else if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) { if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ASYNC_GENERATOR); } else { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_GENERATOR); } } else if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ASYNC); } else if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_CONSTRUCTOR); } else if (context_p->status_flags & PARSER_IS_METHOD) { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_METHOD); } if (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED) { JERRY_ASSERT (!(context_p->status_flags & PARSER_IS_FUNCTION)); compiled_code_p->status_flags |= CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED; } compiled_code_p->status_flags |= function_type; literal_pool_p = ((ecma_value_t *) byte_code_p) - context_p->register_count; byte_code_p += literal_length; dst_p = byte_code_p; parser_init_literal_pool (context_p, literal_pool_p); page_p = context_p->byte_code.first_p; offset = 0; real_offset = 0; uint8_t last_register_index = (uint8_t) JERRY_MIN (context_p->register_count, (PARSER_MAXIMUM_NUMBER_OF_REGISTERS - 1)); while (page_p != last_page_p || offset < last_position) { uint8_t flags; uint8_t *opcode_p; uint8_t *branch_mark_p; cbc_opcode_t opcode; size_t branch_offset_length; opcode_p = dst_p; branch_mark_p = page_p->bytes + offset; opcode = (cbc_opcode_t) (*branch_mark_p); branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (opcode); if (opcode == CBC_JUMP_FORWARD) { /* These opcodes are deleted from the stream. */ size_t counter = PARSER_MAX_BRANCH_LENGTH + 1; do { PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); } while (--counter > 0); continue; } /* Storing the opcode */ *dst_p++ = (uint8_t) opcode; real_offset++; PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); flags = cbc_flags[opcode]; if (opcode == CBC_EXT_OPCODE) { cbc_ext_opcode_t ext_opcode; ext_opcode = (cbc_ext_opcode_t) page_p->bytes[offset]; flags = cbc_ext_flags[ext_opcode]; branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (ext_opcode); /* Storing the extended opcode */ *dst_p++ = (uint8_t) ext_opcode; opcode_p++; real_offset++; PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); } /* Only literal and call arguments can be combined. */ JERRY_ASSERT (!(flags & CBC_HAS_BRANCH_ARG) || !(flags & (CBC_HAS_BYTE_ARG | CBC_HAS_LITERAL_ARG))); while (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) { uint16_t first_byte = page_p->bytes[offset]; uint8_t *opcode_pos_p = dst_p - 1; *dst_p++ = (uint8_t) first_byte; real_offset++; PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); if (first_byte > literal_one_byte_limit) { *dst_p++ = page_p->bytes[offset]; if (first_byte >= encoding_limit) { first_byte = (uint16_t) (((first_byte << 8) | dst_p[-1]) - encoding_delta); } real_offset++; } PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); if (flags & CBC_HAS_LITERAL_ARG2) { if (flags & CBC_HAS_LITERAL_ARG) { flags = CBC_HAS_LITERAL_ARG; } else { flags = CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2; } } else { if (opcode == CBC_ASSIGN_SET_IDENT && JERRY_LIKELY (first_byte < last_register_index)) { *opcode_pos_p = CBC_MOV_IDENT; } break; } } if (flags & CBC_HAS_BYTE_ARG) { /* This argument will be copied without modification. */ *dst_p++ = page_p->bytes[offset]; real_offset++; PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); continue; } if (flags & CBC_HAS_BRANCH_ARG) { *branch_mark_p |= CBC_HIGHEST_BIT_MASK; bool prefix_zero = true; /* The leading zeroes are dropped from the stream. */ JERRY_ASSERT (branch_offset_length > 0 && branch_offset_length <= 3); while (--branch_offset_length > 0) { uint8_t byte = page_p->bytes[offset]; if (byte > 0 || !prefix_zero) { prefix_zero = false; *dst_p++ = page_p->bytes[offset]; real_offset++; } else { /* When a leading zero is dropped, the branch * offset length must be decreased as well. */ (*opcode_p)--; } PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); } *dst_p++ = page_p->bytes[offset]; real_offset++; PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); continue; } } if (!(context_p->status_flags & PARSER_NO_END_LABEL)) { *dst_p++ = CBC_RETURN_FUNCTION_END; if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags)) { dst_p[-1] = CBC_EXT_OPCODE; dst_p[0] = CBC_EXT_ASYNC_EXIT; dst_p++; } } JERRY_ASSERT (dst_p == byte_code_p + length); parse_update_branches (context_p, byte_code_p); parser_cbc_stream_free (&context_p->byte_code); if (context_p->status_flags & PARSER_HAS_LATE_LIT_INIT) { parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; uint16_t register_count = context_p->register_count; parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) { if ((literal_p->status_flags & LEXER_FLAG_LATE_INIT) && literal_p->prop.index >= register_count) { uint32_t source_data = literal_p->u.source_data; const uint8_t *char_p = context_p->source_end_p - (source_data & 0xfffff); ecma_value_t lit_value = ecma_find_or_create_literal_string (char_p, source_data >> 20, (literal_p->status_flags & LEXER_FLAG_ASCII) != 0); literal_pool_p[literal_p->prop.index] = lit_value; } } } ecma_value_t *base_p = (ecma_value_t *) (((uint8_t *) compiled_code_p) + total_size); if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags)) { parser_list_iterator_t literal_iterator; uint16_t argument_count = 0; base_p -= context_p->argument_count; parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while (argument_count < context_p->argument_count) { lexer_literal_t *literal_p; literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); if (!(literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)) { continue; } /* All arguments must be moved to initialized registers. */ if (literal_p->type == LEXER_UNUSED_LITERAL) { base_p[argument_count] = ECMA_VALUE_EMPTY; argument_count++; continue; } base_p[argument_count] = literal_pool_p[literal_p->prop.index]; argument_count++; } } if (!(context_p->status_flags & PARSER_CLASS_CONSTRUCTOR)) { *(--base_p) = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } if (context_p->tagged_template_literal_cp != JMEM_CP_NULL) { compiled_code_p->status_flags |= CBC_CODE_FLAGS_HAS_TAGGED_LITERALS; *(--base_p) = (ecma_value_t) context_p->tagged_template_literal_cp; } if (extended_info != 0) { uint8_t *extended_info_p = ((uint8_t *) base_p) - 1; compiled_code_p->status_flags |= CBC_CODE_FLAGS_HAS_EXTENDED_INFO; *extended_info_p = extended_info; if (context_p->argument_length != UINT16_MAX) { ecma_extended_info_encode_vlq (&extended_info_p, context_p->argument_length); } } #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { util_print_cbc (compiled_code_p); JERRY_DEBUG_MSG ("\nByte code size: %d bytes\n", (int) length); context_p->total_byte_code_size += (uint32_t) length; } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ return compiled_code_p; } /* parser_post_processing */ #undef PARSER_NEXT_BYTE #undef PARSER_NEXT_BYTE_UPDATE /** * Resolve private identifier in direct eval context */ static bool parser_resolve_private_identifier_eval (parser_context_t *context_p) /**< context */ { ecma_string_t *search_key_p; uint8_t *destination_p = (uint8_t *) parser_malloc (context_p, context_p->token.lit_location.length); lexer_convert_ident_to_cesu8 (destination_p, context_p->token.lit_location.char_p, context_p->token.lit_location.length); search_key_p = ecma_new_ecma_string_from_utf8 (destination_p, context_p->token.lit_location.length); parser_free (destination_p, context_p->token.lit_location.length); ecma_object_t *lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p; while (true) { JERRY_ASSERT (lex_env_p != NULL); if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) != 0 && !ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { ecma_object_t *class_object_p = ((ecma_lexical_environment_class_t *) lex_env_p)->object_p; ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); ecma_property_t *prop_p = ecma_find_named_property (class_object_p, internal_string_p); if (prop_p != NULL) { ecma_value_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, ECMA_PROPERTY_VALUE_PTR (prop_p)->value); ecma_value_t *current_p = collection_p + 1; ecma_value_t *end_p = ecma_compact_collection_end (collection_p); while (current_p < end_p) { current_p++; /* skip kind */ ecma_string_t *private_key_p = ecma_get_prop_name_from_value (*current_p++); current_p++; /* skip value */ JERRY_ASSERT (ecma_prop_name_is_symbol (private_key_p)); ecma_string_t *private_key_desc_p = ecma_get_string_from_value (((ecma_extended_string_t *) private_key_p)->u.symbol_descriptor); if (ecma_compare_ecma_strings (private_key_desc_p, search_key_p)) { ecma_deref_ecma_string (search_key_p); lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); return true; } } } } if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) { break; } lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } ecma_deref_ecma_string (search_key_p); return false; } /* parser_resolve_private_identifier_eval */ /** * Resolve private identifier */ void parser_resolve_private_identifier (parser_context_t *context_p) /**< context */ { if ((context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL) && parser_resolve_private_identifier_eval (context_p)) { return; } parser_private_context_t *context_iter_p = context_p->private_context_p; while (context_iter_p) { if (context_iter_p == NULL || !(context_iter_p->opts & SCANNER_PRIVATE_FIELD_ACTIVE)) { parser_raise_error (context_p, PARSER_ERR_UNDECLARED_PRIVATE_FIELD); } if (!(context_iter_p->opts & SCANNER_SUCCESSFUL_CLASS_SCAN)) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); return; } parser_private_context_t *private_context_p = context_iter_p; if (private_context_p == NULL) { parser_raise_error (context_p, PARSER_ERR_UNDECLARED_PRIVATE_FIELD); } scanner_class_private_member_t *ident_iter = private_context_p->members_p; while (ident_iter) { if (lexer_compare_identifiers (context_p, &context_p->token.lit_location, &ident_iter->loc)) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); return; } ident_iter = ident_iter->prev_p; } context_iter_p = context_iter_p->prev_p; } parser_raise_error (context_p, PARSER_ERR_UNDECLARED_PRIVATE_FIELD); } /* parser_resolve_private_identifier */ /** * Save private field context */ void parser_save_private_context (parser_context_t *context_p, /**< context */ parser_private_context_t *private_ctx_p, /**< private context */ scanner_class_info_t *class_info_p) /**< class scanner info */ { private_ctx_p->prev_p = context_p->private_context_p; context_p->private_context_p = private_ctx_p; context_p->private_context_p->members_p = class_info_p->members; context_p->private_context_p->opts = class_info_p->info.u8_arg; class_info_p->members = NULL; } /* parser_save_private_context */ /** * Release contexts private fields */ static void parser_free_private_fields (parser_context_t *context_p) /**< context */ { parser_private_context_t *iter = context_p->private_context_p; while (iter != NULL) { parser_private_context_t *prev_p = iter->prev_p; scanner_release_private_fields (iter->members_p); iter = prev_p; } } /* parser_free_private_fields */ /** * Restore contexts private fields */ void parser_restore_private_context (parser_context_t *context_p, /**< context */ parser_private_context_t *private_ctx_p) /**< private context */ { scanner_release_private_fields (context_p->private_context_p->members_p); context_p->private_context_p = private_ctx_p->prev_p; } /* parser_restore_private_context */ /** * Free identifiers and literals. */ static void parser_free_literals (parser_list_t *literal_pool_p) /**< literals */ { parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; parser_list_iterator_init (literal_pool_p, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { util_free_literal (literal_p); } parser_list_free (literal_pool_p); } /* parser_free_literals */ /** * Parse function arguments */ static void parser_parse_function_arguments (parser_context_t *context_p, /**< context */ lexer_token_type_t end_type) /**< expected end type */ { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); JERRY_ASSERT (context_p->status_flags & PARSER_IS_FUNCTION); JERRY_ASSERT (!(context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED)); bool has_duplicated_arg_names = false; if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags)) { parser_branch_t branch; parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_TRY_CREATE_CONTEXT, &branch); parser_stack_push (context_p, &branch, sizeof (parser_branch_t)); #ifndef JERRY_NDEBUG context_p->context_stack_depth = PARSER_TRY_CONTEXT_STACK_ALLOCATION; #endif /* !JERRY_NDEBUG */ } if (context_p->token.type == end_type) { context_p->status_flags &= (uint32_t) ~PARSER_DISALLOW_AWAIT_YIELD; if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) { scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_ARGS); parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_GENERATOR); parser_emit_cbc (context_p, CBC_POP); scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_BODY); return; } scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); return; } bool has_complex_argument = (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT) != 0; bool is_strict = (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_IS_STRICT) != 0; scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_ARGS); scanner_set_active (context_p); context_p->status_flags |= PARSER_FUNCTION_IS_PARSING_ARGS; while (true) { if (context_p->token.type == LEXER_THREE_DOTS) { if (context_p->status_flags & PARSER_IS_PROPERTY_SETTER) { parser_raise_error (context_p, PARSER_ERR_SETTER_REST_PARAMETER); } lexer_next_token (context_p); if (has_duplicated_arg_names) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES); } context_p->status_flags |= PARSER_FUNCTION_HAS_REST_PARAM | PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT; } if (context_p->token.type == LEXER_LEFT_SQUARE || context_p->token.type == LEXER_LEFT_BRACE) { if (has_duplicated_arg_names) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES); } context_p->status_flags |= PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT; if (!(context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)) { parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, (uint16_t) (PARSER_REGISTER_START + context_p->argument_count)); } else { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_REST_OBJECT); } uint32_t flags = (PARSER_PATTERN_BINDING | PARSER_PATTERN_TARGET_ON_STACK | PARSER_PATTERN_LOCAL | PARSER_PATTERN_ARGUMENTS); if (context_p->next_scanner_info_p->source_p == context_p->source_p) { if (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER) { if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { flags |= PARSER_PATTERN_HAS_REST_ELEMENT; } if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) { parser_raise_error (context_p, PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER); } if (context_p->argument_length == UINT16_MAX) { context_p->argument_length = context_p->argument_count; } flags |= PARSER_PATTERN_TARGET_DEFAULT; } else if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS) { if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { flags |= PARSER_PATTERN_HAS_REST_ELEMENT; } scanner_release_next (context_p, sizeof (scanner_info_t)); } else { parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); } } parser_parse_initializer (context_p, (parser_pattern_flags_t) flags); context_p->argument_count++; if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } if (context_p->token.type != LEXER_COMMA) { if (context_p->token.type != end_type) { parser_error_msg_t error = ((end_type == LEXER_RIGHT_PAREN) ? PARSER_ERR_RIGHT_PAREN_EXPECTED : PARSER_ERR_IDENTIFIER_EXPECTED); parser_raise_error (context_p, error); } break; } lexer_next_token (context_p); if (context_p->token.type == end_type) { break; } continue; } if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); if (context_p->token.keyword_type >= LEXER_FIRST_NON_STRICT_ARGUMENTS) { context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; } if (JERRY_UNLIKELY (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)) { if ((context_p->status_flags & PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT) || (context_p->status_flags & PARSER_IS_ARROW_FUNCTION)) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES); } has_duplicated_arg_names = true; context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; } else { context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT; } lexer_next_token (context_p); uint16_t literal_index = context_p->lit_object.index; if (context_p->token.type == LEXER_ASSIGN) { JERRY_ASSERT (has_complex_argument); if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) { parser_raise_error (context_p, PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER); } if (context_p->argument_length == UINT16_MAX) { context_p->argument_length = context_p->argument_count; } parser_branch_t skip_init; if (has_duplicated_arg_names) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES); } context_p->status_flags |= PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT; /* LEXER_ASSIGN does not overwrite lit_object. */ parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, (uint16_t) (PARSER_REGISTER_START + context_p->argument_count)); parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_DEFAULT_INITIALIZER, &skip_init); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_set_branch_to_current_position (context_p, &skip_init); uint16_t opcode = CBC_ASSIGN_LET_CONST; if (literal_index >= PARSER_REGISTER_START) { opcode = CBC_MOV_IDENT; } else if (!scanner_literal_is_created (context_p, literal_index)) { opcode = CBC_INIT_ARG_OR_CATCH; } parser_emit_cbc_literal (context_p, opcode, literal_index); } else if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_REST_OBJECT); uint16_t opcode = CBC_MOV_IDENT; if (literal_index < PARSER_REGISTER_START) { opcode = CBC_INIT_ARG_OR_CATCH; if (scanner_literal_is_created (context_p, literal_index)) { opcode = CBC_ASSIGN_LET_CONST; } } parser_emit_cbc_literal (context_p, opcode, literal_index); } else if (has_complex_argument && literal_index < PARSER_REGISTER_START) { uint16_t opcode = CBC_INIT_ARG_OR_FUNC; if (scanner_literal_is_created (context_p, literal_index)) { opcode = CBC_ASSIGN_LET_CONST_LITERAL; } parser_emit_cbc_literal_value (context_p, opcode, (uint16_t) (PARSER_REGISTER_START + context_p->argument_count), literal_index); } context_p->argument_count++; if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } if (context_p->token.type != LEXER_COMMA) { if (context_p->token.type != end_type) { parser_error_msg_t error = ((end_type == LEXER_RIGHT_PAREN) ? PARSER_ERR_RIGHT_PAREN_EXPECTED : PARSER_ERR_IDENTIFIER_EXPECTED); parser_raise_error (context_p, error); } break; } if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) { parser_raise_error (context_p, PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER); } lexer_next_token (context_p); if (context_p->token.type == end_type) { break; } } scanner_revert_active (context_p); JERRY_ASSERT (has_complex_argument || !(context_p->status_flags & PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT)); if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) { parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_GENERATOR); parser_emit_cbc (context_p, CBC_POP); } if (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED) { if ((context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_LEXICAL_ENV_NEEDED) || scanner_is_context_needed (context_p, PARSER_CHECK_FUNCTION_CONTEXT)) { context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; parser_branch_t branch; parser_emit_cbc_forward_branch (context_p, CBC_BLOCK_CREATE_CONTEXT, &branch); parser_stack_push (context_p, &branch, sizeof (parser_branch_t)); #ifndef JERRY_NDEBUG PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ } else { context_p->status_flags &= (uint32_t) ~PARSER_LEXICAL_BLOCK_NEEDED; } } context_p->status_flags &= (uint32_t) ~(PARSER_DISALLOW_AWAIT_YIELD | PARSER_FUNCTION_IS_PARSING_ARGS); scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_BODY); if (is_strict) { context_p->status_flags |= PARSER_IS_STRICT; } } /* parser_parse_function_arguments */ #ifndef JERRY_NDEBUG JERRY_STATIC_ASSERT ((PARSER_SCANNING_SUCCESSFUL == PARSER_HAS_LATE_LIT_INIT), parser_scanning_successful_should_share_the_bit_position_with_parser_has_late_lit_init); #endif /* !JERRY_NDEBUG */ /** * Parser script size */ static size_t parser_script_size (parser_context_t *context_p) /**< context */ { size_t script_size = sizeof (cbc_script_t); if (context_p->user_value != ECMA_VALUE_EMPTY) { script_size += sizeof (ecma_value_t); } #if JERRY_MODULE_SYSTEM if (context_p->global_status_flags & ECMA_PARSE_INTERNAL_HAS_IMPORT_META) { script_size += sizeof (ecma_value_t); } #endif /* JERRY_MODULE_SYSTEM */ return script_size; } /* parser_script_size */ #if JERRY_SOURCE_NAME /** * Parser resource name */ static ecma_value_t parser_source_name (parser_context_t *context_p) /**< context */ { if (context_p->options_p != NULL && (context_p->options_p->options & JERRY_PARSE_HAS_SOURCE_NAME)) { JERRY_ASSERT (ecma_is_value_string (context_p->options_p->source_name)); ecma_ref_ecma_string (ecma_get_string_from_value (context_p->options_p->source_name)); return context_p->options_p->source_name; } if (context_p->global_status_flags & ECMA_PARSE_EVAL) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_SOURCE_NAME_EVAL); } return ecma_make_magic_string_value (LIT_MAGIC_STRING_SOURCE_NAME_ANON); } /* parser_source_name */ #endif /* JERRY_SOURCE_NAME */ /** * Parse and compile EcmaScript source code * * Note: source must be a valid UTF-8 string * * @return compiled code */ static ecma_compiled_code_t * parser_parse_source (void *source_p, /**< source code */ uint32_t parse_opts, /**< ecma_parse_opts_t option bits */ const jerry_parse_options_t *options_p) /**< additional configuration options */ { parser_context_t context; ecma_compiled_code_t *compiled_code_p; context.error = PARSER_ERR_NO_ERROR; context.status_flags = parse_opts & PARSER_STRICT_MODE_MASK; context.global_status_flags = parse_opts; context.status_flags |= PARSER_RESTORE_STATUS_FLAGS (parse_opts); context.tagged_template_literal_cp = JMEM_CP_NULL; context.stack_depth = 0; context.stack_limit = 0; context.options_p = options_p; context.script_p = NULL; context.arguments_start_p = NULL; context.arguments_size = 0; #if JERRY_MODULE_SYSTEM if (context.global_status_flags & ECMA_PARSE_MODULE) { context.status_flags |= PARSER_IS_STRICT; } context.module_names_p = NULL; #endif /* JERRY_MODULE_SYSTEM */ context.argument_list = ECMA_VALUE_EMPTY; if (context.options_p != NULL && (context.options_p->options & JERRY_PARSE_HAS_ARGUMENT_LIST)) { context.argument_list = context.options_p->argument_list; } else if (context.global_status_flags & ECMA_PARSE_HAS_ARGUMENT_LIST_VALUE) { JERRY_ASSERT (context.global_status_flags & ECMA_PARSE_HAS_SOURCE_VALUE); context.argument_list = ((ecma_value_t *) source_p)[1]; } if (context.argument_list != ECMA_VALUE_EMPTY) { JERRY_ASSERT (ecma_is_value_string (context.argument_list)); context.status_flags |= PARSER_IS_FUNCTION; if (parse_opts & ECMA_PARSE_GENERATOR_FUNCTION) { context.status_flags |= PARSER_IS_GENERATOR_FUNCTION; } if (parse_opts & ECMA_PARSE_ASYNC_FUNCTION) { context.status_flags |= PARSER_IS_ASYNC_FUNCTION; } ecma_string_t *string_p = ecma_get_string_from_value (context.argument_list); uint8_t flags = ECMA_STRING_FLAG_EMPTY; context.arguments_start_p = ecma_string_get_chars (string_p, &context.arguments_size, NULL, NULL, &flags); if (flags & ECMA_STRING_FLAG_MUST_BE_FREED) { context.global_status_flags |= ECMA_PARSE_INTERNAL_FREE_ARG_LIST; } } if (!(context.global_status_flags & ECMA_PARSE_HAS_SOURCE_VALUE)) { context.source_start_p = ((parser_source_char_t *) source_p)->source_p; context.source_size = (lit_utf8_size_t) ((parser_source_char_t *) source_p)->source_size; } else { ecma_value_t source = ((ecma_value_t *) source_p)[0]; JERRY_ASSERT (ecma_is_value_string (source)); ecma_string_t *string_p = ecma_get_string_from_value (source); uint8_t flags = ECMA_STRING_FLAG_EMPTY; context.source_start_p = ecma_string_get_chars (string_p, &context.source_size, NULL, NULL, &flags); if (flags & ECMA_STRING_FLAG_MUST_BE_FREED) { context.global_status_flags |= ECMA_PARSE_INTERNAL_FREE_SOURCE; } } context.user_value = ECMA_VALUE_EMPTY; if ((context.global_status_flags & ECMA_PARSE_EVAL) && JERRY_CONTEXT (vm_top_context_p) != NULL) { const ecma_compiled_code_t *bytecode_header_p = JERRY_CONTEXT (vm_top_context_p)->shared_p->bytecode_header_p; #if JERRY_SNAPSHOT_EXEC if (JERRY_LIKELY (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION))) { #endif /* JERRY_SNAPSHOT_EXEC */ ecma_value_t parent_script_value = ((cbc_uint8_arguments_t *) bytecode_header_p)->script_value; ; cbc_script_t *parent_script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, parent_script_value); if (parent_script_p->refs_and_type & CBC_SCRIPT_HAS_USER_VALUE) { context.user_value = CBC_SCRIPT_GET_USER_VALUE (parent_script_p); } #if JERRY_SNAPSHOT_EXEC } #endif /* JERRY_SNAPSHOT_EXEC */ } else if (context.options_p != NULL && (context.options_p->options & JERRY_PARSE_HAS_USER_VALUE)) { context.user_value = context.options_p->user_value; } context.last_context_p = NULL; context.last_statement.current_p = NULL; context.token.flags = 0; lexer_init_line_info (&context); scanner_info_t scanner_info_end; scanner_info_end.next_p = NULL; scanner_info_end.source_p = NULL; scanner_info_end.type = SCANNER_TYPE_END; context.next_scanner_info_p = &scanner_info_end; context.active_scanner_info_p = NULL; context.skipped_scanner_info_p = NULL; context.skipped_scanner_info_end_p = NULL; context.last_cbc_opcode = PARSER_CBC_UNAVAILABLE; context.argument_count = 0; context.argument_length = UINT16_MAX; context.register_count = 0; context.literal_count = 0; parser_cbc_stream_init (&context.byte_code); context.byte_code_size = 0; parser_list_init (&context.literal_pool, sizeof (lexer_literal_t), (uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_literal_t))); context.scope_stack_p = NULL; context.scope_stack_size = 0; context.scope_stack_top = 0; context.scope_stack_reg_top = 0; context.scope_stack_global_end = 0; context.tagged_template_literal_cp = JMEM_CP_NULL; context.private_context_p = NULL; #ifndef JERRY_NDEBUG context.context_stack_depth = 0; #endif /* !JERRY_NDEBUG */ #if JERRY_PARSER_DUMP_BYTE_CODE context.is_show_opcodes = (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_SHOW_OPCODES); context.total_byte_code_size = 0; if (context.is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- %s parsing start ---\n\n", (context.arguments_start_p == NULL) ? "Script" : "Function"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ scanner_scan_all (&context); if (JERRY_UNLIKELY (context.error != PARSER_ERR_NO_ERROR)) { JERRY_ASSERT (context.error == PARSER_ERR_OUT_OF_MEMORY); /* It is unlikely that memory can be allocated in an out-of-memory * situation. However, a simple value can still be thrown. */ jcontext_raise_exception (ECMA_VALUE_NULL); return NULL; } if (context.arguments_start_p == NULL) { context.source_p = context.source_start_p; context.source_end_p = context.source_start_p + context.source_size; } else { context.source_p = context.arguments_start_p; context.source_end_p = context.arguments_start_p + context.arguments_size; } context.u.allocated_buffer_p = NULL; context.token.flags = 0; lexer_init_line_info (&context); parser_stack_init (&context); JERRY_ASSERT (context.next_scanner_info_p->source_p == context.source_p); JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); if (context.next_scanner_info_p->u8_arg & SCANNER_FUNCTION_IS_STRICT) { context.status_flags |= PARSER_IS_STRICT; } PARSER_TRY (context.try_buffer) { context.script_p = (cbc_script_t *) parser_malloc (&context, parser_script_size (&context)); CBC_SCRIPT_SET_TYPE (context.script_p, context.user_value, CBC_SCRIPT_REF_ONE); if (context.global_status_flags & (ECMA_PARSE_EVAL | ECMA_PARSE_HAS_ARGUMENT_LIST_VALUE)) { context.script_p->refs_and_type |= CBC_SCRIPT_IS_EVAL_CODE; } #if JERRY_BUILTIN_REALMS context.script_p->realm_p = (ecma_object_t *) JERRY_CONTEXT (global_object_p); #endif /* JERRY_BUILTIN_REALMS */ #if JERRY_SOURCE_NAME context.script_p->source_name = parser_source_name (&context); #endif /* JERRY_SOURCE_NAME */ ECMA_SET_INTERNAL_VALUE_POINTER (context.script_value, context.script_p); /* Pushing a dummy value ensures the stack is never empty. * This simplifies the stack management routines. */ parser_stack_push_uint8 (&context, CBC_MAXIMUM_BYTE_VALUE); /* The next token must always be present to make decisions * in the parser. Therefore when a token is consumed, the * lexer_next_token() must be immediately called. */ lexer_next_token (&context); if (context.arguments_start_p != NULL) { parser_parse_function_arguments (&context, LEXER_EOS); JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS); scanner_release_next (&context, sizeof (scanner_info_t)); context.source_p = context.source_start_p; context.source_end_p = context.source_start_p + context.source_size; lexer_init_line_info (&context); lexer_next_token (&context); } #if JERRY_MODULE_SYSTEM else if (parse_opts & ECMA_PARSE_MODULE) { parser_branch_t branch; parser_emit_cbc_forward_branch (&context, CBC_JUMP_FORWARD, &branch); scanner_create_variables (&context, SCANNER_CREATE_VARS_IS_MODULE); parser_emit_cbc (&context, CBC_RETURN_FUNCTION_END); parser_set_branch_to_current_position (&context, &branch); } #endif /* JERRY_MODULE_SYSTEM */ else { JERRY_ASSERT (context.next_scanner_info_p->source_p == context.source_start_p && context.next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); if (scanner_is_context_needed (&context, PARSER_CHECK_GLOBAL_CONTEXT)) { context.status_flags |= PARSER_LEXICAL_BLOCK_NEEDED; } if (!(parse_opts & ECMA_PARSE_EVAL)) { scanner_check_variables (&context); } scanner_create_variables (&context, SCANNER_CREATE_VARS_IS_SCRIPT); } parser_parse_statements (&context); JERRY_ASSERT (context.last_statement.current_p == NULL); JERRY_ASSERT (context.last_cbc_opcode == PARSER_CBC_UNAVAILABLE); JERRY_ASSERT (context.u.allocated_buffer_p == NULL); #ifndef JERRY_NDEBUG JERRY_ASSERT (context.status_flags & PARSER_SCANNING_SUCCESSFUL); JERRY_ASSERT (!(context.global_status_flags & ECMA_PARSE_INTERNAL_FOR_IN_OFF_CONTEXT_ERROR)); context.status_flags &= (uint32_t) ~PARSER_SCANNING_SUCCESSFUL; #endif /* !JERRY_NDEBUG */ JERRY_ASSERT (!(context.status_flags & PARSER_HAS_LATE_LIT_INIT)); compiled_code_p = parser_post_processing (&context); parser_list_free (&context.literal_pool); /* When parsing is successful, only the dummy value can be remained on the stack. */ JERRY_ASSERT (context.stack_top_uint8 == CBC_MAXIMUM_BYTE_VALUE && context.stack.last_position == 1 && context.stack.first_p != NULL && context.stack.first_p->next_p == NULL && context.stack.last_p == NULL); JERRY_ASSERT (context.arguments_start_p != NULL || !(context.status_flags & PARSER_ARGUMENTS_NEEDED)); context.script_p->refs_and_type -= CBC_SCRIPT_REF_ONE; if (context.user_value != ECMA_VALUE_EMPTY) { CBC_SCRIPT_GET_USER_VALUE (context.script_p) = ecma_copy_value_if_not_object (context.user_value); } #if JERRY_MODULE_SYSTEM if (context.global_status_flags & ECMA_PARSE_INTERNAL_HAS_IMPORT_META) { int idx = (context.user_value != ECMA_VALUE_EMPTY) ? 1 : 0; ecma_value_t module = ecma_make_object_value ((ecma_object_t *) JERRY_CONTEXT (module_current_p)); CBC_SCRIPT_GET_OPTIONAL_VALUES (context.script_p)[idx] = module; context.script_p->refs_and_type |= CBC_SCRIPT_HAS_IMPORT_META; } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_PARSER_DUMP_BYTE_CODE if (context.is_show_opcodes) { JERRY_DEBUG_MSG ("\n%s parsing successfully completed. Total byte code size: %d bytes\n", (context.arguments_start_p == NULL) ? "Script" : "Function", (int) context.total_byte_code_size); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ } PARSER_CATCH { if (context.last_statement.current_p != NULL) { parser_free_jumps (context.last_statement); } parser_free_allocated_buffer (&context); scanner_cleanup (&context); #if JERRY_MODULE_SYSTEM if (context.module_names_p != NULL) { ecma_module_release_module_names (context.module_names_p); } #endif /* JERRY_MODULE_SYSTEM */ compiled_code_p = NULL; parser_free_literals (&context.literal_pool); parser_cbc_stream_free (&context.byte_code); #if JERRY_SOURCE_NAME ecma_deref_ecma_string (ecma_get_string_from_value (context.script_p->source_name)); #endif /* JERRY_SOURCE_NAME */ if (context.script_p != NULL) { JERRY_ASSERT (context.script_p->refs_and_type >= CBC_SCRIPT_REF_ONE); jmem_heap_free_block (context.script_p, parser_script_size (&context)); } } PARSER_TRY_END if (context.scope_stack_p != NULL) { parser_free (context.scope_stack_p, context.scope_stack_size * sizeof (parser_scope_stack_t)); } #if JERRY_PARSER_DUMP_BYTE_CODE if (context.is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- %s parsing end ---\n\n", (context.arguments_start_p == NULL) ? "Script" : "Function"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ parser_stack_free (&context); if (context.global_status_flags & ECMA_PARSE_INTERNAL_FREE_SOURCE) { jmem_heap_free_block ((void *) context.source_start_p, context.source_size); } if (context.global_status_flags & ECMA_PARSE_INTERNAL_FREE_ARG_LIST) { jmem_heap_free_block ((void *) context.arguments_start_p, context.arguments_size); } if (compiled_code_p != NULL) { return compiled_code_p; } if (context.error == PARSER_ERR_OUT_OF_MEMORY) { /* It is unlikely that memory can be allocated in an out-of-memory * situation. However, a simple value can still be thrown. */ jcontext_raise_exception (ECMA_VALUE_NULL); return NULL; } #if (JERRY_STACK_LIMIT != 0) if (context.error == PARSER_ERR_STACK_OVERFLOW) { ecma_raise_standard_error (JERRY_ERROR_RANGE, ECMA_ERR_MAXIMUM_CALL_STACK_SIZE_EXCEEDED); return NULL; } #endif /* JERRY_STACK_LIMIT != 0 */ #if JERRY_ERROR_MESSAGES ecma_string_t *err_str_p; if (context.error == PARSER_ERR_INVALID_REGEXP) { ecma_value_t error = jcontext_take_exception (); ecma_property_t *prop_p = ecma_find_named_property (ecma_get_object_from_value (error), ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE)); ecma_free_value (error); JERRY_ASSERT (prop_p); err_str_p = ecma_get_string_from_value (ECMA_PROPERTY_VALUE_PTR (prop_p)->value); ecma_ref_ecma_string (err_str_p); } else { err_str_p = ecma_new_ecma_external_string_from_cesu8 (parser_get_error_utf8 (context.error), parser_get_error_size (context.error), NULL); } ecma_value_t err_str_val = ecma_make_string_value (err_str_p); ecma_value_t line_str_val = ecma_make_uint32_value (context.token.line); ecma_value_t col_str_val = ecma_make_uint32_value (context.token.column); ecma_value_t source_name = parser_source_name (&context); ecma_raise_standard_error_with_format (JERRY_ERROR_SYNTAX, "% [%:%:%]", err_str_val, source_name, line_str_val, col_str_val); ecma_free_value (source_name); ecma_free_value (col_str_val); ecma_free_value (line_str_val); ecma_deref_ecma_string (err_str_p); #else /* !JERRY_ERROR_MESSAGES */ if (context.error == PARSER_ERR_INVALID_REGEXP) { jcontext_release_exception (); } ecma_raise_syntax_error (ECMA_ERR_EMPTY); #endif /* JERRY_ERROR_MESSAGES */ return NULL; } /* parser_parse_source */ /** * Save parser context before function parsing. */ static void parser_save_context (parser_context_t *context_p, /**< context */ parser_saved_context_t *saved_context_p) /**< target for saving the context */ { JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); if (context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS) { context_p->status_flags |= PARSER_LEXICAL_BLOCK_NEEDED; } /* Save private part of the context. */ saved_context_p->status_flags = context_p->status_flags; saved_context_p->stack_depth = context_p->stack_depth; saved_context_p->stack_limit = context_p->stack_limit; saved_context_p->prev_context_p = context_p->last_context_p; saved_context_p->last_statement = context_p->last_statement; saved_context_p->argument_count = context_p->argument_count; saved_context_p->argument_length = context_p->argument_length; saved_context_p->register_count = context_p->register_count; saved_context_p->literal_count = context_p->literal_count; saved_context_p->byte_code = context_p->byte_code; saved_context_p->byte_code_size = context_p->byte_code_size; saved_context_p->literal_pool_data = context_p->literal_pool.data; saved_context_p->scope_stack_p = context_p->scope_stack_p; saved_context_p->scope_stack_size = context_p->scope_stack_size; saved_context_p->scope_stack_top = context_p->scope_stack_top; saved_context_p->scope_stack_reg_top = context_p->scope_stack_reg_top; saved_context_p->scope_stack_global_end = context_p->scope_stack_global_end; saved_context_p->tagged_template_literal_cp = context_p->tagged_template_literal_cp; #ifndef JERRY_NDEBUG saved_context_p->context_stack_depth = context_p->context_stack_depth; #endif /* !JERRY_NDEBUG */ /* Reset private part of the context. */ context_p->status_flags &= PARSER_IS_STRICT; context_p->stack_depth = 0; context_p->stack_limit = 0; context_p->last_context_p = saved_context_p; context_p->last_statement.current_p = NULL; context_p->argument_count = 0; context_p->argument_length = UINT16_MAX; context_p->register_count = 0; context_p->literal_count = 0; parser_cbc_stream_init (&context_p->byte_code); context_p->byte_code_size = 0; parser_list_reset (&context_p->literal_pool); context_p->scope_stack_p = NULL; context_p->scope_stack_size = 0; context_p->scope_stack_top = 0; context_p->scope_stack_reg_top = 0; context_p->scope_stack_global_end = 0; context_p->tagged_template_literal_cp = JMEM_CP_NULL; #ifndef JERRY_NDEBUG context_p->context_stack_depth = 0; #endif /* !JERRY_NDEBUG */ } /* parser_save_context */ /** * Restore parser context after function parsing. */ static void parser_restore_context (parser_context_t *context_p, /**< context */ parser_saved_context_t *saved_context_p) /**< target for saving the context */ { parser_list_free (&context_p->literal_pool); if (context_p->scope_stack_p != NULL) { parser_free (context_p->scope_stack_p, context_p->scope_stack_size * sizeof (parser_scope_stack_t)); } /* Restore private part of the context. */ JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); context_p->status_flags = saved_context_p->status_flags; context_p->stack_depth = saved_context_p->stack_depth; context_p->stack_limit = saved_context_p->stack_limit; context_p->last_context_p = saved_context_p->prev_context_p; context_p->last_statement = saved_context_p->last_statement; context_p->argument_count = saved_context_p->argument_count; context_p->argument_length = saved_context_p->argument_length; context_p->register_count = saved_context_p->register_count; context_p->literal_count = saved_context_p->literal_count; context_p->byte_code = saved_context_p->byte_code; context_p->byte_code_size = saved_context_p->byte_code_size; context_p->literal_pool.data = saved_context_p->literal_pool_data; context_p->scope_stack_p = saved_context_p->scope_stack_p; context_p->scope_stack_size = saved_context_p->scope_stack_size; context_p->scope_stack_top = saved_context_p->scope_stack_top; context_p->scope_stack_reg_top = saved_context_p->scope_stack_reg_top; context_p->scope_stack_global_end = saved_context_p->scope_stack_global_end; context_p->tagged_template_literal_cp = saved_context_p->tagged_template_literal_cp; #ifndef JERRY_NDEBUG context_p->context_stack_depth = saved_context_p->context_stack_depth; #endif /* !JERRY_NDEBUG */ } /* parser_restore_context */ /** * Parse function code * * @return compiled code */ ecma_compiled_code_t * parser_parse_function (parser_context_t *context_p, /**< context */ uint32_t status_flags) /**< extra status flags */ { parser_saved_context_t saved_context; ecma_compiled_code_t *compiled_code_p; JERRY_ASSERT (status_flags & PARSER_IS_FUNCTION); parser_save_context (context_p, &saved_context); context_p->status_flags |= status_flags; context_p->status_flags |= PARSER_ALLOW_NEW_TARGET; #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- %s parsing start ---\n\n", (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) ? "Class constructor" : "Function"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_PAREN) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED); } lexer_next_token (context_p); parser_parse_function_arguments (context_p, LEXER_RIGHT_PAREN); lexer_next_token (context_p); if ((context_p->status_flags & PARSER_IS_PROPERTY_GETTER) && context_p->argument_count != 0) { parser_raise_error (context_p, PARSER_ERR_NO_ARGUMENTS_EXPECTED); } if ((context_p->status_flags & PARSER_IS_PROPERTY_SETTER) && context_p->argument_count != 1) { parser_raise_error (context_p, PARSER_ERR_ONE_ARGUMENT_EXPECTED); } if ((context_p->status_flags & (PARSER_CLASS_CONSTRUCTOR | PARSER_ALLOW_SUPER_CALL)) == PARSER_CLASS_CONSTRUCTOR) { parser_emit_cbc_ext (context_p, CBC_EXT_RUN_FIELD_INIT); parser_flush_cbc (context_p); } #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes && (context_p->status_flags & PARSER_HAS_NON_STRICT_ARG)) { JERRY_DEBUG_MSG (" Note: legacy (non-strict) argument definition\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ if (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } lexer_next_token (context_p); parser_parse_statements (context_p); compiled_code_p = parser_post_processing (context_p); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- %s parsing end ---\n\n", (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) ? "Class constructor" : "Function"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ parser_restore_context (context_p, &saved_context); return compiled_code_p; } /* parser_parse_function */ /** * Parse static class block code * * @return compiled code */ ecma_compiled_code_t * parser_parse_class_static_block (parser_context_t *context_p) /**< context */ { parser_saved_context_t saved_context; ecma_compiled_code_t *compiled_code_p; parser_save_context (context_p, &saved_context); context_p->status_flags |= (PARSER_IS_CLASS_STATIC_BLOCK | PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER | PARSER_INSIDE_CLASS_FIELD | PARSER_ALLOW_NEW_TARGET | PARSER_DISALLOW_AWAIT_YIELD); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- Static class block parsing start ---\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); lexer_next_token (context_p); parser_parse_statements (context_p); compiled_code_p = parser_post_processing (context_p); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- Static class block parsing end ---\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ parser_restore_context (context_p, &saved_context); return compiled_code_p; } /* parser_parse_class_static_block */ /** * Parse arrow function code * * @return compiled code */ ecma_compiled_code_t * parser_parse_arrow_function (parser_context_t *context_p, /**< context */ uint32_t status_flags) /**< extra status flags */ { parser_saved_context_t saved_context; ecma_compiled_code_t *compiled_code_p; JERRY_ASSERT (status_flags & PARSER_IS_FUNCTION); JERRY_ASSERT (status_flags & PARSER_IS_ARROW_FUNCTION); parser_save_context (context_p, &saved_context); context_p->status_flags |= status_flags; context_p->status_flags |= saved_context.status_flags & (PARSER_ALLOW_NEW_TARGET | PARSER_ALLOW_SUPER | PARSER_ALLOW_SUPER_CALL); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- Arrow function parsing start ---\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ /* The `await` keyword is disallowed in the IdentifierReference position */ if (status_flags & PARSER_IS_CLASS_STATIC_BLOCK) { context_p->status_flags |= PARSER_DISALLOW_AWAIT_YIELD; } if (context_p->token.type == LEXER_LEFT_PAREN) { lexer_next_token (context_p); parser_parse_function_arguments (context_p, LEXER_RIGHT_PAREN); lexer_next_token (context_p); } else { parser_parse_function_arguments (context_p, LEXER_ARROW); } /* The `await` keyword is interpreted as an identifier within the body of arrow functions */ if (status_flags & PARSER_IS_CLASS_STATIC_BLOCK) { context_p->status_flags &= (uint32_t) ~(PARSER_DISALLOW_AWAIT_YIELD | PARSER_IS_CLASS_STATIC_BLOCK); } JERRY_ASSERT (context_p->token.type == LEXER_ARROW); lexer_next_token (context_p); bool next_token_needed = false; if (context_p->token.type == LEXER_LEFT_BRACE) { lexer_next_token (context_p); context_p->status_flags |= PARSER_IS_CLOSURE; parser_parse_statements (context_p); /* Unlike normal function, arrow functions consume their close brace. */ JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE); next_token_needed = true; } else { if (context_p->status_flags & PARSER_IS_STRICT && context_p->status_flags & PARSER_HAS_NON_STRICT_ARG) { parser_raise_error (context_p, PARSER_ERR_NON_STRICT_ARG_DEFINITION); } parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL; } else { parser_emit_cbc (context_p, CBC_RETURN); } parser_flush_cbc (context_p); lexer_update_await_yield (context_p, saved_context.status_flags); } compiled_code_p = parser_post_processing (context_p); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- Arrow function parsing end ---\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ parser_restore_context (context_p, &saved_context); if (next_token_needed) { lexer_next_token (context_p); } return compiled_code_p; } /* parser_parse_arrow_function */ /** * Parse class fields * * @return compiled code */ ecma_compiled_code_t * parser_parse_class_fields (parser_context_t *context_p) /**< context */ { parser_saved_context_t saved_context; ecma_compiled_code_t *compiled_code_p; uint32_t extra_status_flags = context_p->status_flags & PARSER_INSIDE_WITH; parser_save_context (context_p, &saved_context); context_p->status_flags |= (PARSER_IS_FUNCTION | PARSER_ALLOW_SUPER | PARSER_INSIDE_CLASS_FIELD | PARSER_ALLOW_NEW_TARGET | extra_status_flags); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- Class fields parsing start ---\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ const uint8_t *source_end_p = context_p->source_end_p; bool first_computed_class_field = true; scanner_location_t end_location; scanner_get_location (&end_location, context_p); do { uint8_t class_field_type = context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); scanner_range_t range = { 0 }; if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) { parser_stack_pop (context_p, &range, sizeof (scanner_range_t)); } else if (class_field_type & PARSER_CLASS_FIELD_NORMAL) { parser_stack_pop (context_p, &range.start_location, sizeof (scanner_location_t)); } uint16_t literal_index = 0; bool is_private = false; if (class_field_type & PARSER_CLASS_FIELD_NORMAL) { scanner_set_location (context_p, &range.start_location); if (class_field_type & PARSER_CLASS_FIELD_STATIC_BLOCK) { scanner_seek (context_p); JERRY_ASSERT (context_p->source_p[1] == LIT_CHAR_LEFT_BRACE); context_p->source_p += 2; context_p->source_end_p = source_end_p; uint16_t func_index = lexer_construct_class_static_block_function (context_p); parser_emit_cbc_ext_literal (context_p, CBC_EXT_CLASS_CALL_STATIC_BLOCK, func_index); continue; } uint32_t ident_opts = LEXER_OBJ_IDENT_ONLY_IDENTIFIERS; is_private = context_p->source_p[-1] == LIT_CHAR_HASHMARK; if (is_private) { ident_opts |= LEXER_OBJ_IDENT_CLASS_PRIVATE; } context_p->source_end_p = source_end_p; scanner_seek (context_p); lexer_expect_object_literal_id (context_p, ident_opts); literal_index = context_p->lit_object.index; if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) { lexer_next_token (context_p); JERRY_ASSERT (context_p->token.type == LEXER_ASSIGN); } } else if (first_computed_class_field) { parser_emit_cbc (context_p, CBC_PUSH_NUMBER_0); first_computed_class_field = false; } if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) { if (!(class_field_type & PARSER_CLASS_FIELD_NORMAL)) { scanner_set_location (context_p, &range.start_location); scanner_seek (context_p); } context_p->source_end_p = range.source_end_p; lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->token.type != LEXER_EOS) { parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } } else { parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); } if (class_field_type & PARSER_CLASS_FIELD_NORMAL) { uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); if (function_literal_index == PARSER_ANONYMOUS_CLASS) { parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, literal_index); } else if (function_literal_index < PARSER_NAMED_FUNCTION) { uint32_t function_name_status_flags = is_private ? PARSER_PRIVATE_FUNCTION_NAME : 0; parser_set_function_name (context_p, function_literal_index, literal_index, function_name_status_flags); } if (is_private) { parser_emit_cbc_ext_literal (context_p, CBC_EXT_PRIVATE_FIELD_ADD, literal_index); } else { parser_emit_cbc_ext_literal (context_p, CBC_EXT_DEFINE_FIELD, literal_index); } /* Prepare stack slot for assignment property reference base. Needed by vm.c */ if (context_p->stack_limit == context_p->stack_depth) { context_p->stack_limit++; JERRY_ASSERT (context_p->stack_limit <= PARSER_MAXIMUM_STACK_LIMIT); } } else { uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); uint16_t opcode = CBC_EXT_SET_NEXT_COMPUTED_FIELD; if (function_literal_index < PARSER_NAMED_FUNCTION || function_literal_index == PARSER_ANONYMOUS_CLASS) { opcode = CBC_EXT_SET_NEXT_COMPUTED_FIELD_ANONYMOUS_FUNC; } parser_flush_cbc (context_p); /* The next opcode pushes two more temporary values onto the stack */ if (context_p->stack_depth + 1 > context_p->stack_limit) { context_p->stack_limit = (uint16_t) (context_p->stack_depth + 1); if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } parser_emit_cbc_ext (context_p, opcode); } } while (!(context_p->stack_top_uint8 & PARSER_CLASS_FIELD_END)); if (!first_computed_class_field) { parser_emit_cbc (context_p, CBC_POP); } parser_flush_cbc (context_p); context_p->source_end_p = source_end_p; scanner_set_location (context_p, &end_location); compiled_code_p = parser_post_processing (context_p); #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { JERRY_DEBUG_MSG ("\n--- Class fields parsing end ---\n\n"); } #endif /* JERRY_PARSER_DUMP_BYTE_CODE */ parser_restore_context (context_p, &saved_context); return compiled_code_p; } /* parser_parse_class_fields */ /** * Check whether the last emitted cbc opcode was an anonymous function declaration * * @return PARSER_NOT_FUNCTION_LITERAL - if the last opcode is not a function literal * PARSER_NAMED_FUNCTION - if the last opcode is not a named function declaration * PARSER_ANONYMOUS_CLASS - if the last opcode is an anonymous class declaration * literal index of the anonymous function literal - otherwise */ uint16_t parser_check_anonymous_function_declaration (parser_context_t *context_p) /**< context */ { if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_FINALIZE_ANONYMOUS_CLASS)) { return PARSER_ANONYMOUS_CLASS; } if (context_p->last_cbc.literal_type != LEXER_FUNCTION_LITERAL) { return PARSER_NOT_FUNCTION_LITERAL; } uint16_t literal_index = PARSER_NOT_FUNCTION_LITERAL; if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { literal_index = context_p->last_cbc.literal_index; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { literal_index = context_p->last_cbc.value; } else if (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS) { literal_index = context_p->last_cbc.third_literal_index; } else { return PARSER_NOT_FUNCTION_LITERAL; } const ecma_compiled_code_t *bytecode_p; bytecode_p = (const ecma_compiled_code_t *) (PARSER_GET_LITERAL (literal_index)->u.bytecode_p); bool is_anon = ecma_is_value_magic_string (*ecma_compiled_code_resolve_function_name (bytecode_p), LIT_MAGIC_STRING__EMPTY); return (is_anon ? literal_index : PARSER_NAMED_FUNCTION); } /* parser_check_anonymous_function_declaration */ /** * Set the function name of the function literal corresponds to the given function literal index * to the given character buffer of literal corresponds to the given name index. */ void parser_set_function_name (parser_context_t *context_p, /**< context */ uint16_t function_literal_index, /**< function literal index */ uint16_t name_index, /**< function name literal index */ uint32_t status_flags) /**< status flags */ { ecma_compiled_code_t *bytecode_p; bytecode_p = (ecma_compiled_code_t *) (PARSER_GET_LITERAL (function_literal_index)->u.bytecode_p); parser_compiled_code_set_function_name (context_p, bytecode_p, name_index, status_flags); } /* parser_set_function_name */ /** * Prepend the given prefix into the current function name literal * * @return pointer to the newly allocated buffer */ static uint8_t * parser_add_function_name_prefix (parser_context_t *context_p, /**< context */ const char *prefix_p, /**< prefix */ uint32_t prefix_size, /**< prefix's length */ uint32_t *name_length_p, /**< [out] function name's size */ lexer_literal_t *name_lit_p) /**< function name literal */ { *name_length_p += prefix_size; uint8_t *name_buffer_p = (uint8_t *) parser_malloc (context_p, *name_length_p * sizeof (uint8_t)); memcpy (name_buffer_p, prefix_p, prefix_size); memcpy (name_buffer_p + prefix_size, name_lit_p->u.char_p, name_lit_p->prop.length); return name_buffer_p; } /* parser_add_function_name_prefix */ /** * Set the function name of the given compiled code * to the given character buffer of literal corresponds to the given name index. */ void parser_compiled_code_set_function_name (parser_context_t *context_p, /**< context */ ecma_compiled_code_t *bytecode_p, /**< function literal index */ uint16_t name_index, /**< function name literal index */ uint32_t status_flags) /**< status flags */ { ecma_value_t *func_name_start_p; func_name_start_p = ecma_compiled_code_resolve_function_name ((const ecma_compiled_code_t *) bytecode_p); if (JERRY_UNLIKELY (!ecma_is_value_magic_string (*func_name_start_p, LIT_MAGIC_STRING__EMPTY))) { return; } parser_scope_stack_t *scope_stack_start_p = context_p->scope_stack_p; parser_scope_stack_t *scope_stack_p = scope_stack_start_p + context_p->scope_stack_top; while (scope_stack_p > scope_stack_start_p) { scope_stack_p--; if (scope_stack_p->map_from != PARSER_SCOPE_STACK_FUNC && scanner_decode_map_to (scope_stack_p) == name_index) { name_index = scope_stack_p->map_from; break; } } lexer_literal_t *name_lit_p = (lexer_literal_t *) PARSER_GET_LITERAL (name_index); if (name_lit_p->type != LEXER_IDENT_LITERAL && name_lit_p->type != LEXER_STRING_LITERAL) { return; } uint8_t *name_buffer_p = (uint8_t *) name_lit_p->u.char_p; uint32_t name_length = name_lit_p->prop.length; if (status_flags & PARSER_PRIVATE_FUNCTION_NAME) { name_buffer_p = parser_add_function_name_prefix (context_p, "#", 1, &name_length, name_lit_p); } else if (status_flags & (PARSER_IS_PROPERTY_GETTER | PARSER_IS_PROPERTY_SETTER)) { name_buffer_p = parser_add_function_name_prefix (context_p, (status_flags & PARSER_IS_PROPERTY_GETTER) ? "get " : "set ", 4, &name_length, name_lit_p); } *func_name_start_p = ecma_find_or_create_literal_string (name_buffer_p, name_length, (status_flags & LEXER_FLAG_ASCII) != 0); if (name_buffer_p != name_lit_p->u.char_p) { parser_free (name_buffer_p, name_length); } } /* parser_compiled_code_set_function_name */ /** * Raise a parse error. */ void parser_raise_error (parser_context_t *context_p, /**< context */ parser_error_msg_t error) /**< error code */ { /* Must be compatible with the scanner because * the lexer might throws errors during prescanning. */ parser_saved_context_t *saved_context_p = context_p->last_context_p; while (saved_context_p != NULL) { parser_cbc_stream_free (&saved_context_p->byte_code); /* First the current literal pool is freed, and then it is replaced * by the literal pool coming from the saved context. Since literals * are not used anymore, this is a valid replacement. The last pool * is freed by parser_parse_source. */ parser_free_literals (&context_p->literal_pool); context_p->literal_pool.data = saved_context_p->literal_pool_data; if (context_p->scope_stack_p != NULL) { parser_free (context_p->scope_stack_p, context_p->scope_stack_size * sizeof (parser_scope_stack_t)); } context_p->scope_stack_p = saved_context_p->scope_stack_p; context_p->scope_stack_size = saved_context_p->scope_stack_size; if (saved_context_p->last_statement.current_p != NULL) { parser_free_jumps (saved_context_p->last_statement); } if (saved_context_p->tagged_template_literal_cp != JMEM_CP_NULL) { ecma_collection_t *collection = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, saved_context_p->tagged_template_literal_cp); ecma_collection_free_template_literal (collection); } saved_context_p = saved_context_p->prev_context_p; } parser_free_private_fields (context_p); if (context_p->tagged_template_literal_cp != JMEM_CP_NULL) { ecma_collection_t *collection = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, context_p->tagged_template_literal_cp); ecma_collection_free_template_literal (collection); } context_p->error = error; PARSER_THROW (context_p->try_buffer); /* Should never been reached. */ JERRY_ASSERT (0); } /* parser_raise_error */ #endif /* JERRY_PARSER */ /** * Parse EcmaScript source code * * Note: * if arg_list_p is not NULL, a function body is parsed * returned value must be freed with ecma_free_value * * @return pointer to compiled byte code - if success * NULL - otherwise */ ecma_compiled_code_t * parser_parse_script (void *source_p, /**< source code */ uint32_t parse_opts, /**< ecma_parse_opts_t option bits */ const jerry_parse_options_t *options_p) /**< additional configuration options */ { #if JERRY_PARSER ecma_compiled_code_t *bytecode_p = parser_parse_source (source_p, parse_opts, options_p); if (JERRY_UNLIKELY (bytecode_p == NULL)) { /* Exception has already thrown. */ return NULL; } return bytecode_p; #else /* !JERRY_PARSER */ JERRY_UNUSED (arg_list_p); JERRY_UNUSED (arg_list_size); JERRY_UNUSED (source_p); JERRY_UNUSED (source_size); JERRY_UNUSED (parse_opts); JERRY_UNUSED (source_name); ecma_raise_syntax_error (ECMA_ERR_PARSER_NOT_SUPPORTED); return NULL; #endif /* JERRY_PARSER */ } /* parser_parse_script */ /** * @} * @} * @} */ thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-symbol.cpp000664 001750 001750 00000016076 15164251010 047713 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-literal-storage.h" #include "ecma-objects.h" #include "ecma-symbol-object.h" #include "jcontext.h" #include "jrt.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-symbol.inc.h" #define BUILTIN_UNDERSCORED_ID symbol #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup symbol ECMA Symbol object built-in * @{ */ /** * Handle calling [[Call]] of built-in Symbol object. * * @return ecma value */ ecma_value_t ecma_builtin_symbol_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_op_create_symbol (arguments_list_p, arguments_list_len); } /* ecma_builtin_symbol_dispatch_call */ /** * Handle calling [[Construct]] of built-in Symbol object. * * Symbol constructor is not intended to be used * with the new operator or to be subclassed. * * See also: * ECMA-262 v6, 19.4.1 * @return ecma value */ ecma_value_t ecma_builtin_symbol_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_SYMBOL_IS_NOT_A_CONSTRUCTOR); } /* ecma_builtin_symbol_dispatch_construct */ /** * Helper function for Symbol object's 'for' and `keyFor` * routines common parts * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_symbol_for_helper (ecma_value_t value_to_find) /**< symbol or ecma-string */ { ecma_string_t *string_p; bool is_for = ecma_is_value_string (value_to_find); if (is_for) { string_p = ecma_get_string_from_value (value_to_find); } else { string_p = ecma_get_symbol_from_value (value_to_find); } jmem_cpointer_t symbol_list_cp = JERRY_CONTEXT (symbol_list_first_cp); jmem_cpointer_t *empty_cpointer_p = NULL; while (symbol_list_cp != JMEM_CP_NULL) { ecma_lit_storage_item_t *symbol_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, symbol_list_cp); for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (symbol_list_p->values[i] != JMEM_CP_NULL) { ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, symbol_list_p->values[i]); if (is_for) { ecma_value_t symbol_desc = ecma_get_symbol_description (value_p); if (ecma_is_value_undefined (symbol_desc)) { ecma_ref_ecma_string (value_p); return ecma_make_symbol_value (value_p); } ecma_string_t *symbol_desc_p = ecma_get_string_from_value (symbol_desc); if (ecma_compare_ecma_strings (symbol_desc_p, string_p)) { /* The current symbol's descriptor matches with the value_to_find, so the value is no longer needed. */ ecma_deref_ecma_string (string_p); return ecma_copy_value (ecma_make_symbol_value (value_p)); } } else { if (string_p == value_p) { ecma_value_t symbol_desc = ecma_get_symbol_description (string_p); if (ecma_is_value_undefined (symbol_desc)) { return symbol_desc; } ecma_string_t *symbol_desc_p = ecma_get_string_from_value (symbol_desc); ecma_ref_ecma_string (symbol_desc_p); return symbol_desc; } } } else { if (empty_cpointer_p == NULL) { empty_cpointer_p = symbol_list_p->values + i; } } } symbol_list_cp = symbol_list_p->next_cp; } if (!is_for) { return ECMA_VALUE_UNDEFINED; } /* There was no matching, sp a new symbol should be added the global symbol list. The symbol creation requires an extra reference to the descriptor string, but this reference has already been added. */ ecma_string_t *new_symbol_p = ecma_new_symbol_from_descriptor_string (value_to_find); jmem_cpointer_t result; JMEM_CP_SET_NON_NULL_POINTER (result, new_symbol_p); if (empty_cpointer_p != NULL) { *empty_cpointer_p = result; return ecma_copy_value (ecma_make_symbol_value (new_symbol_p)); } ecma_lit_storage_item_t *new_item_p; new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t)); new_item_p->values[0] = result; for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { new_item_p->values[i] = JMEM_CP_NULL; } new_item_p->next_cp = JERRY_CONTEXT (symbol_list_first_cp); JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (symbol_list_first_cp), new_item_p); return ecma_copy_value (ecma_make_symbol_value (new_symbol_p)); } /* ecma_builtin_symbol_for_helper */ /** * The Symbol object's 'for' routine * * See also: * ECMA-262 v6, 19.4.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_symbol_for (ecma_value_t this_arg, /**< this argument */ ecma_value_t key) /**< key string */ { JERRY_UNUSED (this_arg); ecma_string_t *string_desc_p = ecma_op_to_string (key); /* 1. */ if (JERRY_UNLIKELY (string_desc_p == NULL)) { /* 2. */ return ECMA_VALUE_ERROR; } return ecma_builtin_symbol_for_helper (ecma_make_string_value (string_desc_p)); } /* ecma_builtin_symbol_for */ /** * The Symbol object's 'keyFor' routine * * See also: * ECMA-262 v6, 19.4.2. * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_symbol_key_for (ecma_value_t this_arg, /**< this argument */ ecma_value_t symbol) /**< symbol */ { JERRY_UNUSED (this_arg); /* 1. */ if (!ecma_is_value_symbol (symbol)) { return ecma_raise_type_error (ECMA_ERR_THE_GIVEN_ARGUMENT_IS_NOT_A_SYMBOL); } /* 2-4. */ return ecma_builtin_symbol_for_helper (symbol); } /* ecma_builtin_symbol_key_for */ /** * @} * @} * @} */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/000775 001750 001750 00000000000 15164251010 034726 5ustar00ddennedyddennedy000000 000000 loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-float64array.cpp000664 001750 001750 00000004505 15164251010 053102 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #if JERRY_NUMBER_TYPE_FLOAT64 #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-float64array.inc.h" #define BUILTIN_UNDERSCORED_ID float64array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup float64array ECMA Float64Array object built-in * @{ */ /** * Handle calling [[Call]] of Float64Array * * @return ecma value */ ecma_value_t ecma_builtin_float64array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_FLOAT64_ARRAY_REQUIRES_NEW); } /* ecma_builtin_float64array_dispatch_call */ /** * Handle calling [[Construct]] of Float64Array * * @return ecma value */ ecma_value_t ecma_builtin_float64array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_FLOAT64_ARRAY); } /* ecma_builtin_float64array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_NUMBER_TYPE_FLOAT64 */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/meson.build000664 001750 001750 00000001012 15164251010 042243 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimatesource_file = [ 'lit-char-helpers.h', 'lit-globals.h', 'lit-magic-strings.h', 'lit-magic-strings.inc.h', 'lit-strings.h', 'lit-unicode-conversions-sup.inc.h', 'lit-unicode-conversions.inc.h', 'lit-unicode-folding.inc.h', 'lit-unicode-ranges-sup.inc.h', 'lit-unicode-ranges.inc.h', 'lit-char-helpers.cpp', 'lit-magic-strings.cpp', 'lit-strings.cpp' ] subloader_dep += [declare_dependency( include_directories : include_directories('.'), sources : source_file )] glaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-gc.cpp000664 001750 001750 00000222340 15164251010 043141 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /** * Garbage collector implementation */ #include "ecma-gc.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-arraybuffer-object.h" #include "ecma-builtin-handlers.h" #include "ecma-container-object.h" #include "ecma-function-object.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lcache.h" #include "ecma-objects.h" #include "ecma-property-hashmap.h" #include "ecma-proxy-object.h" #include "jcontext.h" #include "jrt-bit-fields.h" #include "jrt-libc-includes.h" #include "jrt.h" #include "re-compiler.h" #include "vm-defines.h" #include "vm-stack.h" #if JERRY_BUILTIN_TYPEDARRAY #include "ecma-typedarray-object.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ #include "ecma-promise-object.h" /* TODO: Extract GC to a separate component */ /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmagc Garbage collector * @{ */ /* * The garbage collector uses the reference counter * of object: it increases the counter by one when * the object is marked at the first time. */ /** * Get visited flag of the object. * * @return true - if visited * false - otherwise */ static inline bool ecma_gc_is_object_visited (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); return (object_p->type_flags_refs < ECMA_OBJECT_NON_VISITED); } /* ecma_gc_is_object_visited */ /** * Mark objects as visited starting from specified object as root */ static void ecma_gc_mark (ecma_object_t *object_p); /** * Set visited flag of the object. */ static void ecma_gc_set_object_visited (ecma_object_t *object_p) /**< object */ { if (object_p->type_flags_refs >= ECMA_OBJECT_NON_VISITED) { #if (JERRY_GC_MARK_LIMIT != 0) if (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) != 0) { JERRY_CONTEXT (ecma_gc_mark_recursion_limit)--; /* Set the reference count of gray object to 0 */ object_p->type_flags_refs &= (ecma_object_descriptor_t) (ECMA_OBJECT_REF_ONE - 1); ecma_gc_mark (object_p); JERRY_CONTEXT (ecma_gc_mark_recursion_limit)++; } else { /* Set the reference count of the non-marked gray object to 1 */ object_p->type_flags_refs &= (ecma_object_descriptor_t) ((ECMA_OBJECT_REF_ONE << 1) - 1); JERRY_ASSERT (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE); } #else /* (JERRY_GC_MARK_LIMIT == 0) */ /* Set the reference count of gray object to 0 */ object_p->type_flags_refs &= (ecma_object_descriptor_t) (ECMA_OBJECT_REF_ONE - 1); #endif /* (JERRY_GC_MARK_LIMIT != 0) */ } } /* ecma_gc_set_object_visited */ /** * Initialize GC information for the object */ void ecma_init_gc_info (ecma_object_t *object_p) /**< object */ { JERRY_CONTEXT (ecma_gc_objects_number)++; JERRY_CONTEXT (ecma_gc_new_objects)++; JERRY_ASSERT (object_p->type_flags_refs < ECMA_OBJECT_REF_ONE); object_p->type_flags_refs |= ECMA_OBJECT_REF_ONE; object_p->gc_next_cp = JERRY_CONTEXT (ecma_gc_objects_cp); ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_gc_objects_cp), object_p); } /* ecma_init_gc_info */ /** * Increase reference counter of an object * * @return void */ void ecma_ref_object_inline (ecma_object_t *object_p) /**< object */ { if (JERRY_LIKELY (object_p->type_flags_refs < ECMA_OBJECT_MAX_REF)) { object_p->type_flags_refs = (ecma_object_descriptor_t) (object_p->type_flags_refs + ECMA_OBJECT_REF_ONE); } else { jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT); } } /* ecma_ref_object_inline */ /** * Increase reference counter of an object */ void ecma_ref_object (ecma_object_t *object_p) /**< object */ { ecma_ref_object_inline (object_p); } /* ecma_ref_object */ /** * Decrease reference counter of an object * * @return void */ void ecma_deref_object (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE); object_p->type_flags_refs = (ecma_object_descriptor_t) (object_p->type_flags_refs - ECMA_OBJECT_REF_ONE); } /* ecma_deref_object */ /** * Mark objects referenced by global object */ static void ecma_gc_mark_global_object (ecma_global_object_t *global_object_p) /**< global object */ { JERRY_ASSERT (global_object_p->extended_object.u.built_in.routine_id == 0); ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, global_object_p->global_env_cp)); #if JERRY_BUILTIN_REALMS ecma_gc_set_object_visited (ecma_get_object_from_value (global_object_p->this_binding)); #endif /* JERRY_BUILTIN_REALMS */ if (global_object_p->global_scope_cp != global_object_p->global_env_cp) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, global_object_p->global_scope_cp)); } jmem_cpointer_t *builtin_objects_p = global_object_p->builtin_objects; for (int i = 0; i < ECMA_BUILTIN_OBJECTS_COUNT; i++) { if (builtin_objects_p[i] != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, builtin_objects_p[i])); } } } /* ecma_gc_mark_global_object */ /** * Mark objects referenced by arguments object */ static void ecma_gc_mark_arguments_object (ecma_extended_object_t *ext_object_p) /**< arguments object */ { JERRY_ASSERT (ecma_get_object_type ((ecma_object_t *) ext_object_p) == ECMA_OBJECT_TYPE_CLASS); ecma_unmapped_arguments_t *arguments_p = (ecma_unmapped_arguments_t *) ext_object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (arguments_p->callee)); ecma_value_t *argv_p = (ecma_value_t *) (arguments_p + 1); if (ext_object_p->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) ext_object_p; argv_p = (ecma_value_t *) (mapped_arguments_p + 1); ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, mapped_arguments_p->lex_env)); } uint32_t arguments_number = arguments_p->header.u.cls.u3.arguments_number; for (uint32_t i = 0; i < arguments_number; i++) { if (ecma_is_value_object (argv_p[i])) { ecma_gc_set_object_visited (ecma_get_object_from_value (argv_p[i])); } } } /* ecma_gc_mark_arguments_object */ /** * Mark referenced object from property */ static void ecma_gc_mark_properties (ecma_object_t *object_p, /**< object */ bool mark_references) /**< mark references */ { JERRY_UNUSED (mark_references); #if !JERRY_MODULE_SYSTEM JERRY_ASSERT (!mark_references); #endif /* !JERRY_MODULE_SYSTEM */ jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prop_iter_cp = prop_iter_p->next_property_cp; } } #endif /* JERRY_PROPERTY_HASHMAP */ while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *property_pair_p = (ecma_property_pair_t *) prop_iter_p; for (uint32_t index = 0; index < ECMA_PROPERTY_PAIR_ITEM_COUNT; index++) { uint8_t property = property_pair_p->header.types[index]; if (JERRY_LIKELY (ECMA_PROPERTY_IS_RAW (property))) { if (property & ECMA_PROPERTY_FLAG_DATA) { ecma_value_t value = property_pair_p->values[index].value; if (ecma_is_value_object (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (value)); } continue; } #if JERRY_MODULE_SYSTEM if (mark_references) { continue; } #endif /* JERRY_MODULE_SYSTEM */ ecma_property_value_t *accessor_objs_p = property_pair_p->values + index; ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (accessor_objs_p); if (get_set_pair_p->getter_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp)); } if (get_set_pair_p->setter_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp)); } continue; } if (!ECMA_PROPERTY_IS_INTERNAL (property)) { JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_DELETED || property == ECMA_PROPERTY_TYPE_HASHMAP); continue; } JERRY_ASSERT (property_pair_p->names_cp[index] >= LIT_INTERNAL_MAGIC_STRING_FIRST_DATA && property_pair_p->names_cp[index] < LIT_MAGIC_STRING__COUNT); switch (property_pair_p->names_cp[index]) { case LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD: { ecma_environment_record_t *environment_record_p; environment_record_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_environment_record_t, property_pair_p->values[index].value); if (environment_record_p->this_binding != ECMA_VALUE_UNINITIALIZED) { JERRY_ASSERT (ecma_is_value_object (environment_record_p->this_binding)); ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->this_binding)); } JERRY_ASSERT (ecma_is_value_object (environment_record_p->function_object)); ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->function_object)); break; } case LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS: { ecma_value_t *compact_collection_p; compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, property_pair_p->values[index].value); ecma_value_t *end_p = ecma_compact_collection_end (compact_collection_p); ecma_value_t *current_p = compact_collection_p + 1; while (end_p - current_p >= ECMA_PRIVATE_ELEMENT_LIST_SIZE) { current_p++; /* skip the type */ current_p++; /* skip the name */ ecma_value_t value = *current_p++; if (!ecma_is_value_undefined (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (value)); } } break; } #if JERRY_BUILTIN_CONTAINER case LIT_INTERNAL_MAGIC_STRING_WEAK_REFS: { ecma_value_t key_arg = ecma_make_object_value (object_p); ecma_collection_t *refs_p; refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, property_pair_p->values[index].value); for (uint32_t j = 0; j < refs_p->item_count; j++) { const ecma_value_t reference_value = refs_p->buffer_p[j]; if (ecma_is_value_empty (reference_value)) { continue; } ecma_object_t *reference_object_p = ecma_get_object_from_value (reference_value); JERRY_ASSERT (ecma_get_object_type (reference_object_p) == ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) reference_object_p; if (map_object_p->u.cls.type != ECMA_OBJECT_CLASS_CONTAINER || map_object_p->u.cls.u2.container_id != LIT_MAGIC_STRING_WEAKMAP_UL || !ecma_gc_is_object_visited (reference_object_p)) { continue; } ecma_value_t value = ecma_op_container_find_weak_value (reference_object_p, key_arg); if (ecma_is_value_object (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (value)); } } break; } #endif /* JERRY_BUILTIN_CONTAINER */ case LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES: { jerry_value_t value = property_pair_p->values[index].value; if (value == JMEM_CP_NULL) { JERRY_ASSERT (!(property & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL)); break; } ecma_native_pointer_t *item_p; item_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value); do { jerry_object_native_info_t *native_info_p = item_p->native_info_p; JERRY_ASSERT (native_info_p != NULL && native_info_p->number_of_references > 0); uint8_t *start_p = ((uint8_t *) item_p->native_p) + native_info_p->offset_of_references; ecma_value_t *value_p = (ecma_value_t *) start_p; ecma_value_t *end_p = value_p + native_info_p->number_of_references; do { if (ecma_is_value_object (*value_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*value_p)); } } while (++value_p < end_p); if (property & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL) { break; } item_p = &(((ecma_native_pointer_chain_t *) item_p)->next_p->data); } while (item_p != NULL); break; } } } prop_iter_cp = prop_iter_p->next_property_cp; } } /* ecma_gc_mark_properties */ /** * Mark compiled code. */ static void ecma_gc_mark_compiled_code (ecma_value_t script_value) /**< script value */ { cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); if (script_p->refs_and_type & CBC_SCRIPT_USER_VALUE_IS_OBJECT) { ecma_value_t user_value = CBC_SCRIPT_GET_USER_VALUE (script_p); JERRY_ASSERT (ecma_is_value_object (user_value)); ecma_gc_set_object_visited (ecma_get_object_from_value (user_value)); } #if JERRY_MODULE_SYSTEM if (script_p->refs_and_type & CBC_SCRIPT_HAS_IMPORT_META) { ecma_value_t import_meta = CBC_SCRIPT_GET_IMPORT_META (script_p, script_p->refs_and_type); JERRY_ASSERT (ecma_is_value_object (import_meta)); ecma_gc_set_object_visited (ecma_get_object_from_value (import_meta)); } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_REALMS ecma_gc_set_object_visited (script_p->realm_p); #endif /* JERRY_BUILTIN_REALMS */ } /* ecma_gc_mark_compiled_code */ /** * Mark objects referenced by bound function object. * * @return void */ static void JERRY_ATTR_NOINLINE ecma_gc_mark_bound_function_object (ecma_object_t *object_p) /**< bound function object */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p; ecma_object_t *target_func_p; target_func_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, bound_func_p->header.u.bound_function.target_function); ecma_gc_set_object_visited (target_func_p); ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this; if (!ecma_is_value_integer_number (args_len_or_this)) { if (ecma_is_value_object (args_len_or_this)) { ecma_gc_set_object_visited (ecma_get_object_from_value (args_len_or_this)); } return; } ecma_integer_value_t args_length = ecma_get_integer_from_value (args_len_or_this); ecma_value_t *args_p = (ecma_value_t *) (bound_func_p + 1); JERRY_ASSERT (args_length > 0); for (ecma_integer_value_t i = 0; i < args_length; i++) { if (ecma_is_value_object (args_p[i])) { ecma_gc_set_object_visited (ecma_get_object_from_value (args_p[i])); } } } /* ecma_gc_mark_bound_function_object */ /** * Mark objects referenced by Promise built-in. */ static void ecma_gc_mark_promise_object (ecma_extended_object_t *ext_object_p) /**< extended object */ { /* Mark promise result. */ ecma_value_t result = ext_object_p->u.cls.u3.value; if (ecma_is_value_object (result)) { ecma_gc_set_object_visited (ecma_get_object_from_value (result)); } /* Mark all reactions. */ ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) ext_object_p; ecma_collection_t *collection_p = promise_object_p->reactions; if (collection_p != NULL) { ecma_value_t *buffer_p = collection_p->buffer_p; ecma_value_t *buffer_end_p = buffer_p + collection_p->item_count; while (buffer_p < buffer_end_p) { ecma_value_t value = *buffer_p++; ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, value)); if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*buffer_p++)); } if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*buffer_p++)); } } } } /* ecma_gc_mark_promise_object */ #if JERRY_BUILTIN_CONTAINER /** * Mark objects referenced by Map built-in. */ static void ecma_gc_mark_map_object (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE) { ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i); if (ecma_is_value_empty (entry_p->key)) { continue; } if (ecma_is_value_object (entry_p->key)) { ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->key)); } if (ecma_is_value_object (entry_p->value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->value)); } } } /* ecma_gc_mark_map_object */ /** * Mark objects referenced by WeakMap built-in. */ static void ecma_gc_mark_weakmap_object (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE) { ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i); if (ecma_is_value_empty (entry_p->key)) { continue; } JERRY_ASSERT (ecma_is_value_object (entry_p->key)); if (ecma_is_value_object (entry_p->value) && ecma_gc_is_object_visited (ecma_get_object_from_value (entry_p->key))) { ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->value)); } } } /* ecma_gc_mark_weakmap_object */ /** * Mark objects referenced by Set built-in. */ static void ecma_gc_mark_set_object (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_VALUE_SIZE) { ecma_value_t *entry_p = start_p + i; if (ecma_is_value_empty (*entry_p)) { continue; } if (ecma_is_value_object (*entry_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*entry_p)); } } } /* ecma_gc_mark_set_object */ #endif /* JERRY_BUILTIN_CONTAINER */ /** * Mark objects referenced by inactive generator functions, async functions, etc. */ static void ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */ { vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; if (executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_ASYNC_GENERATOR_CALLED) { ecma_value_t task = executable_object_p->extended_object.u.cls.u3.head; while (!ECMA_IS_INTERNAL_VALUE_NULL (task)) { ecma_async_generator_task_t *task_p; task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, task); JERRY_ASSERT (ecma_is_value_object (task_p->promise)); ecma_gc_set_object_visited (ecma_get_object_from_value (task_p->promise)); if (ecma_is_value_object (task_p->operation_value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (task_p->operation_value)); } task = task_p->next; } } ecma_gc_set_object_visited (executable_object_p->frame_ctx.lex_env_p); ecma_gc_set_object_visited (executable_object_p->shared.function_object_p); if (!ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p) && !(executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD)) { /* All objects referenced by running executable objects are strong roots, * and a finished executable object cannot refer to other values. */ return; } if (ecma_is_value_object (executable_object_p->frame_ctx.this_binding)) { ecma_gc_set_object_visited (ecma_get_object_from_value (executable_object_p->frame_ctx.this_binding)); } const ecma_compiled_code_t *bytecode_header_p = executable_object_p->shared.bytecode_header_p; size_t register_end; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; register_end = args_p->register_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; register_end = args_p->register_end; } ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); ecma_value_t *register_end_p = register_p + register_end; while (register_p < register_end_p) { if (ecma_is_value_object (*register_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*register_p)); } register_p++; } if (executable_object_p->frame_ctx.context_depth > 0) { ecma_value_t *context_end_p = register_p; register_p += executable_object_p->frame_ctx.context_depth; ecma_value_t *context_top_p = register_p; do { if (VM_CONTEXT_IS_VARIABLE_LENGTH (VM_GET_CONTEXT_TYPE (context_top_p[-1]))) { ecma_value_t *last_item_p = context_top_p - VM_GET_CONTEXT_END (context_top_p[-1]); JERRY_ASSERT (last_item_p >= context_end_p); context_top_p--; do { --context_top_p; if (ecma_is_value_object (*context_top_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*context_top_p)); } } while (context_top_p > last_item_p); continue; } uint32_t offsets = vm_get_context_value_offsets (context_top_p); while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets)) { int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets); if (ecma_is_value_object (context_top_p[offset])) { ecma_gc_set_object_visited (ecma_get_object_from_value (context_top_p[offset])); } offsets >>= VM_CONTEXT_OFFSET_SHIFT; } JERRY_ASSERT (context_top_p >= context_end_p + offsets); context_top_p -= offsets; } while (context_top_p > context_end_p); } register_end_p = executable_object_p->frame_ctx.stack_top_p; while (register_p < register_end_p) { if (ecma_is_value_object (*register_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*register_p)); } register_p++; } if (ecma_is_value_object (executable_object_p->iterator)) { ecma_gc_set_object_visited (ecma_get_object_from_value (executable_object_p->iterator)); } } /* ecma_gc_mark_executable_object */ #if JERRY_BUILTIN_PROXY /** * Mark the objects referenced by a proxy object */ static void ecma_gc_mark_proxy_object (ecma_object_t *object_p) /**< proxy object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (object_p)); ecma_proxy_object_t *proxy_p = (ecma_proxy_object_t *) object_p; if (!ecma_is_value_null (proxy_p->target)) { ecma_gc_set_object_visited (ecma_get_object_from_value (proxy_p->target)); } if (!ecma_is_value_null (proxy_p->handler)) { ecma_gc_set_object_visited (ecma_get_object_from_value (proxy_p->handler)); } } /* ecma_gc_mark_proxy_object */ #endif /* JERRY_BUILTIN_PROXY */ /** * Mark objects as visited starting from specified object as root */ static void ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (ecma_gc_is_object_visited (object_p)); if (ecma_is_lexical_environment (object_p)) { jmem_cpointer_t outer_lex_env_cp = object_p->u2.outer_reference_cp; if (outer_lex_env_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, outer_lex_env_cp)); } switch (ecma_get_lex_env_type (object_p)) { case ECMA_LEXICAL_ENVIRONMENT_CLASS: { #if JERRY_MODULE_SYSTEM if (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) { if (ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)) { ecma_gc_mark_properties (object_p, true); } ecma_gc_set_object_visited (((ecma_lexical_environment_class_t *) object_p)->object_p); return; } #endif /* JERRY_MODULE_SYSTEM */ /* FALLTHRU */ } case ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND: { ecma_object_t *binding_object_p = ecma_get_lex_env_binding_object (object_p); ecma_gc_set_object_visited (binding_object_p); return; } default: { break; } } } else { /** * Have the object's prototype here so the object could set it to JMEM_CP_NULL * if the prototype should be ignored (like in case of PROXY). */ jmem_cpointer_t proto_cp = object_p->u2.prototype_cp; switch (ecma_get_object_type (object_p)) { case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: { ecma_extended_object_t *extended_object_p = (ecma_extended_object_t *) object_p; if (extended_object_p->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL) { ecma_gc_mark_global_object ((ecma_global_object_t *) object_p); } #if JERRY_BUILTIN_REALMS ecma_value_t realm_value = extended_object_p->u.built_in.realm_value; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); #endif /* JERRY_BUILTIN_REALMS */ break; } case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: { #if JERRY_BUILTIN_REALMS ecma_value_t realm_value = ((ecma_extended_built_in_object_t *) object_p)->built_in.realm_value; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); #endif /* JERRY_BUILTIN_REALMS */ /* FALLTHRU */ } case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_ARGUMENTS: { ecma_gc_mark_arguments_object (ext_object_p); break; } #if JERRY_PARSER case ECMA_OBJECT_CLASS_SCRIPT: { const ecma_compiled_code_t *compiled_code_p; compiled_code_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, ext_object_p->u.cls.u3.value); JERRY_ASSERT (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)); ecma_gc_mark_compiled_code (((cbc_uint8_arguments_t *) compiled_code_p)->script_value); break; } #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_TYPEDARRAY case ECMA_OBJECT_CLASS_TYPEDARRAY: { ecma_gc_set_object_visited (ecma_typedarray_get_arraybuffer (object_p)); break; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE_NAMESPACE: { JERRY_ASSERT (proto_cp == JMEM_CP_NULL); ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, ext_object_p->u.cls.u3.value)); ecma_gc_mark_properties (object_p, true); return; } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE: { ecma_module_t *module_p = ((ecma_module_t *) ext_object_p); if (module_p->scope_p != NULL) { ecma_gc_set_object_visited (((ecma_module_t *) ext_object_p)->scope_p); } if (module_p->namespace_object_p != NULL) { ecma_gc_set_object_visited (((ecma_module_t *) ext_object_p)->namespace_object_p); } if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE) && module_p->u.compiled_code_p != NULL) { const ecma_compiled_code_t *compiled_code_p = module_p->u.compiled_code_p; JERRY_ASSERT (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)); ecma_gc_mark_compiled_code (((cbc_uint8_arguments_t *) compiled_code_p)->script_value); } ecma_module_node_t *node_p = module_p->imports_p; while (node_p != NULL) { if (ecma_is_value_object (node_p->u.path_or_module)) { ecma_gc_set_object_visited (ecma_get_object_from_value (node_p->u.path_or_module)); } node_p = node_p->next_p; } break; } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_DATAVIEW case ECMA_OBJECT_CLASS_DATAVIEW: { ecma_dataview_object_t *dataview_p = (ecma_dataview_object_t *) object_p; ecma_gc_set_object_visited (dataview_p->buffer_p); break; } #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_CONTAINER case ECMA_OBJECT_CLASS_CONTAINER: { if (ext_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_MAP_UL) { ecma_gc_mark_map_object (object_p); break; } if (ext_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_WEAKMAP_UL) { ecma_gc_mark_weakmap_object (object_p); break; } if (ext_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_SET_UL) { ecma_gc_mark_set_object (object_p); break; } JERRY_ASSERT (ext_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_WEAKSET_UL); break; } #endif /* JERRY_BUILTIN_CONTAINER */ case ECMA_OBJECT_CLASS_GENERATOR: case ECMA_OBJECT_CLASS_ASYNC_GENERATOR: { ecma_gc_mark_executable_object (object_p); break; } case ECMA_OBJECT_CLASS_PROMISE: { ecma_gc_mark_promise_object (ext_object_p); break; } case ECMA_OBJECT_CLASS_PROMISE_CAPABILITY: { ecma_promise_capability_t *capability_p = (ecma_promise_capability_t *) object_p; if (ecma_is_value_object (capability_p->header.u.cls.u3.promise)) { ecma_gc_set_object_visited (ecma_get_object_from_value (capability_p->header.u.cls.u3.promise)); } if (ecma_is_value_object (capability_p->resolve)) { ecma_gc_set_object_visited (ecma_get_object_from_value (capability_p->resolve)); } if (ecma_is_value_object (capability_p->reject)) { ecma_gc_set_object_visited (ecma_get_object_from_value (capability_p->reject)); } break; } case ECMA_OBJECT_CLASS_ASYNC_FROM_SYNC_ITERATOR: { ecma_async_from_sync_iterator_object_t *iter_p = (ecma_async_from_sync_iterator_object_t *) ext_object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (iter_p->header.u.cls.u3.sync_iterator)); if (!ecma_is_value_undefined (iter_p->sync_next_method)) { ecma_gc_set_object_visited (ecma_get_object_from_value (iter_p->sync_next_method)); } break; } case ECMA_OBJECT_CLASS_ARRAY_ITERATOR: case ECMA_OBJECT_CLASS_SET_ITERATOR: case ECMA_OBJECT_CLASS_MAP_ITERATOR: { ecma_value_t iterated_value = ext_object_p->u.cls.u3.iterated_value; if (!ecma_is_value_empty (iterated_value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (iterated_value)); } break; } #if JERRY_BUILTIN_REGEXP case ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR: { ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) object_p; ecma_value_t regexp = regexp_string_iterator_obj->iterating_regexp; ecma_gc_set_object_visited (ecma_get_object_from_value (regexp)); break; } #endif /* JERRY_BUILTIN_REGEXP */ default: { /* The ECMA_OBJECT_CLASS__MAX type represents an uninitialized class. */ JERRY_ASSERT (ext_object_p->u.cls.type <= ECMA_OBJECT_CLASS__MAX); break; } } break; } case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { #if JERRY_BUILTIN_REALMS ecma_value_t realm_value = ((ecma_extended_built_in_object_t *) object_p)->built_in.realm_value; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); #endif /* JERRY_BUILTIN_REALMS */ /* FALLTHRU */ } case ECMA_OBJECT_TYPE_ARRAY: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (JERRY_UNLIKELY (ext_object_p->u.array.length_prop_and_hole_count & ECMA_ARRAY_TEMPLATE_LITERAL)) { /* Template objects are never marked. */ JERRY_ASSERT (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE); return; } if (ecma_op_array_is_fast_array (ext_object_p)) { if (object_p->u1.property_list_cp != JMEM_CP_NULL) { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); for (uint32_t i = 0; i < ext_object_p->u.array.length; i++) { if (ecma_is_value_object (values_p[i])) { ecma_gc_set_object_visited (ecma_get_object_from_value (values_p[i])); } } } if (proto_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp)); } return; } break; } #if JERRY_BUILTIN_PROXY case ECMA_OBJECT_TYPE_PROXY: { ecma_gc_mark_proxy_object (object_p); /* Prototype of proxy object is a bit set. */ proto_cp = JMEM_CP_NULL; break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_OBJECT_TYPE_FUNCTION: { ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; ecma_gc_set_object_visited ( ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_func_p->u.function.scope_cp)); const ecma_compiled_code_t *compiled_code_p = ecma_op_function_get_compiled_code (ext_func_p); if (CBC_FUNCTION_IS_ARROW (compiled_code_p->status_flags)) { ecma_arrow_function_t *arrow_func_p = (ecma_arrow_function_t *) object_p; if (ecma_is_value_object (arrow_func_p->this_binding)) { ecma_gc_set_object_visited (ecma_get_object_from_value (arrow_func_p->this_binding)); } if (ecma_is_value_object (arrow_func_p->new_target)) { ecma_gc_set_object_visited (ecma_get_object_from_value (arrow_func_p->new_target)); } } #if JERRY_SNAPSHOT_EXEC if (JERRY_UNLIKELY (compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) { /* Static snapshot functions have a global realm */ break; } #endif /* JERRY_SNAPSHOT_EXEC */ JERRY_ASSERT (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)); ecma_gc_mark_compiled_code (((cbc_uint8_arguments_t *) compiled_code_p)->script_value); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { ecma_gc_mark_bound_function_object (object_p); break; } case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; #if JERRY_BUILTIN_REALMS ecma_value_t realm_value = ext_func_p->u.built_in.realm_value; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); #endif /* JERRY_BUILTIN_REALMS */ if (ext_func_p->u.built_in.id == ECMA_BUILTIN_ID_HANDLER) { switch (ext_func_p->u.built_in.routine_id) { case ECMA_NATIVE_HANDLER_PROMISE_RESOLVE: case ECMA_NATIVE_HANDLER_PROMISE_REJECT: { ecma_promise_resolver_t *resolver_obj_p = (ecma_promise_resolver_t *) object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (resolver_obj_p->promise)); break; } case ECMA_NATIVE_HANDLER_PROMISE_THEN_FINALLY: case ECMA_NATIVE_HANDLER_PROMISE_CATCH_FINALLY: { ecma_promise_finally_function_t *finally_obj_p = (ecma_promise_finally_function_t *) object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (finally_obj_p->constructor)); ecma_gc_set_object_visited (ecma_get_object_from_value (finally_obj_p->on_finally)); break; } case ECMA_NATIVE_HANDLER_PROMISE_CAPABILITY_EXECUTOR: { ecma_promise_capability_executor_t *executor_p = (ecma_promise_capability_executor_t *) object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->capability)); break; } case ECMA_NATIVE_HANDLER_PROMISE_ALL_HELPER: { ecma_promise_all_executor_t *executor_p = (ecma_promise_all_executor_t *) object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->capability)); ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->values)); ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->remaining_elements)); break; } #if JERRY_BUILTIN_PROXY case ECMA_NATIVE_HANDLER_PROXY_REVOKE: { ecma_revocable_proxy_object_t *rev_proxy_p = (ecma_revocable_proxy_object_t *) object_p; if (!ecma_is_value_null (rev_proxy_p->proxy)) { ecma_gc_set_object_visited (ecma_get_object_from_value (rev_proxy_p->proxy)); } break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_NATIVE_HANDLER_VALUE_THUNK: case ECMA_NATIVE_HANDLER_VALUE_THROWER: { ecma_promise_value_thunk_t *thunk_obj_p = (ecma_promise_value_thunk_t *) object_p; if (ecma_is_value_object (thunk_obj_p->value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (thunk_obj_p->value)); } break; } case ECMA_NATIVE_HANDLER_ASYNC_FROM_SYNC_ITERATOR_UNWRAP: { break; } default: { JERRY_UNREACHABLE (); } } } break; } case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION: { ecma_gc_mark_compiled_code (((ecma_extended_object_t *) object_p)->u.constructor_function.script_value); break; } #if JERRY_BUILTIN_REALMS case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { ecma_native_function_t *native_function_p = (ecma_native_function_t *) object_p; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, native_function_p->realm_value)); break; } #endif /* JERRY_BUILTIN_REALMS */ default: { break; } } if (proto_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp)); } } ecma_gc_mark_properties (object_p, false); } /* ecma_gc_mark */ /** * Free the native handle/pointer by calling its free callback. */ static void ecma_gc_free_native_pointer (ecma_property_t property, /**< property descriptor */ ecma_value_t value) /**< property value */ { if (JERRY_LIKELY (property & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL)) { JERRY_ASSERT (value != JMEM_CP_NULL); ecma_native_pointer_t *native_pointer_p; native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value); if (native_pointer_p->native_info_p != NULL) { jerry_object_native_free_cb_t free_cb = native_pointer_p->native_info_p->free_cb; if (free_cb != NULL) { free_cb (native_pointer_p->native_p, native_pointer_p->native_info_p); } } jmem_heap_free_block (native_pointer_p, sizeof (ecma_native_pointer_t)); return; } if (value == JMEM_CP_NULL) { return; } ecma_native_pointer_chain_t *item_p; item_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_chain_t, value); do { if (item_p->data.native_info_p != NULL) { jerry_object_native_free_cb_t free_cb = item_p->data.native_info_p->free_cb; if (free_cb != NULL) { free_cb (item_p->data.native_p, item_p->data.native_info_p); } } ecma_native_pointer_chain_t *next_p = item_p->next_p; jmem_heap_free_block (item_p, sizeof (ecma_native_pointer_chain_t)); item_p = next_p; } while (item_p != NULL); } /* ecma_gc_free_native_pointer */ /** * Free specified arguments object. * * @return allocated object's size */ static size_t ecma_free_arguments_object (ecma_extended_object_t *ext_object_p) /**< arguments object */ { JERRY_ASSERT (ecma_get_object_type ((ecma_object_t *) ext_object_p) == ECMA_OBJECT_TYPE_CLASS); size_t object_size = sizeof (ecma_unmapped_arguments_t); if (ext_object_p->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) ext_object_p; object_size = sizeof (ecma_mapped_arguments_t); #if JERRY_SNAPSHOT_EXEC if (!(mapped_arguments_p->unmapped.header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_STATIC_BYTECODE)) #endif /* JERRY_SNAPSHOT_EXEC */ { ecma_compiled_code_t *byte_code_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, mapped_arguments_p->u.byte_code); ecma_bytecode_deref (byte_code_p); } } ecma_value_t *argv_p = (ecma_value_t *) (((uint8_t *) ext_object_p) + object_size); ecma_unmapped_arguments_t *arguments_p = (ecma_unmapped_arguments_t *) ext_object_p; uint32_t arguments_number = arguments_p->header.u.cls.u3.arguments_number; for (uint32_t i = 0; i < arguments_number; i++) { ecma_free_value_if_not_object (argv_p[i]); } uint32_t saved_argument_count = JERRY_MAX (arguments_number, arguments_p->header.u.cls.u2.formal_params_number); return object_size + (saved_argument_count * sizeof (ecma_value_t)); } /* ecma_free_arguments_object */ /** * Free specified fast access mode array object. */ static void ecma_free_fast_access_array (ecma_object_t *object_p) /**< fast access mode array object to free */ { JERRY_ASSERT (ecma_op_object_is_fast_array (object_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (ext_object_p->u.array.length); if (object_p->u1.property_list_cp != JMEM_CP_NULL) { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); for (uint32_t i = 0; i < aligned_length; i++) { ecma_free_value_if_not_object (values_p[i]); } jmem_heap_free_block (values_p, aligned_length * sizeof (ecma_value_t)); } ecma_dealloc_extended_object (object_p, sizeof (ecma_extended_object_t)); } /* ecma_free_fast_access_array */ /** * Free non-objects referenced by inactive generator functions, async functions, etc. * * @return total object size */ static size_t ecma_gc_free_executable_object (ecma_object_t *object_p) /**< object */ { vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; const ecma_compiled_code_t *bytecode_header_p = executable_object_p->shared.bytecode_header_p; size_t size, register_end; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; register_end = args_p->register_end; size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; register_end = args_p->register_end; size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); } size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t)); ecma_bytecode_deref ((ecma_compiled_code_t *) bytecode_header_p); uint16_t executable_obj_flags = executable_object_p->extended_object.u.cls.u2.executable_obj_flags; JERRY_ASSERT (!(executable_obj_flags & ECMA_EXECUTABLE_OBJECT_RUNNING)); if (executable_obj_flags & ECMA_ASYNC_GENERATOR_CALLED) { ecma_value_t task = executable_object_p->extended_object.u.cls.u3.head; while (!ECMA_IS_INTERNAL_VALUE_NULL (task)) { ecma_async_generator_task_t *task_p; task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, task); JERRY_ASSERT (ecma_is_value_object (task_p->promise)); ecma_free_value_if_not_object (task_p->operation_value); task = task_p->next; jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); } } if (executable_obj_flags & ECMA_EXECUTABLE_OBJECT_COMPLETED) { return size; } ecma_free_value_if_not_object (executable_object_p->frame_ctx.this_binding); ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); ecma_value_t *register_end_p = register_p + register_end; while (register_p < register_end_p) { ecma_free_value_if_not_object (*register_p++); } if (executable_object_p->frame_ctx.context_depth > 0) { ecma_value_t *context_end_p = register_p; register_p += executable_object_p->frame_ctx.context_depth; ecma_value_t *context_top_p = register_p; do { context_top_p[-1] &= (uint32_t) ~VM_CONTEXT_HAS_LEX_ENV; if (VM_CONTEXT_IS_VARIABLE_LENGTH (VM_GET_CONTEXT_TYPE (context_top_p[-1]))) { ecma_value_t *last_item_p = context_top_p - VM_GET_CONTEXT_END (context_top_p[-1]); JERRY_ASSERT (last_item_p >= context_end_p); context_top_p--; do { ecma_free_value_if_not_object (*(--context_top_p)); } while (context_top_p > last_item_p); continue; } uint32_t offsets = vm_get_context_value_offsets (context_top_p); while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets)) { int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets); if (ecma_is_value_object (context_top_p[offset])) { context_top_p[offset] = ECMA_VALUE_UNDEFINED; } offsets >>= VM_CONTEXT_OFFSET_SHIFT; } context_top_p = vm_stack_context_abort (&executable_object_p->frame_ctx, context_top_p); } while (context_top_p > context_end_p); } register_end_p = executable_object_p->frame_ctx.stack_top_p; while (register_p < register_end_p) { ecma_free_value_if_not_object (*register_p++); } return size; } /* ecma_gc_free_executable_object */ JERRY_STATIC_ASSERT (!ECMA_PROPERTY_IS_RAW (ECMA_PROPERTY_TYPE_DELETED), ecma_property_type_deleted_must_not_be_raw_property); JERRY_STATIC_ASSERT ((((int)ECMA_PROPERTY_TYPE_DELETED & (int)ECMA_PROPERTY_FLAG_LCACHED) == 0), ecma_property_type_deleted_must_not_have_lcached_flag); JERRY_STATIC_ASSERT ((((int)ECMA_GC_FREE_SECOND_PROPERTY) == 1), ecma_gc_free_second_must_be_one); /** * Free property of an object */ void ecma_gc_free_property (ecma_object_t *object_p, /**< object */ ecma_property_pair_t *prop_pair_p, /**< property pair */ uint32_t options) /**< option bits including property index */ { /* Both cannot be deleted. */ JERRY_ASSERT (prop_pair_p->header.types[0] != ECMA_PROPERTY_TYPE_DELETED || prop_pair_p->header.types[1] != ECMA_PROPERTY_TYPE_DELETED); JERRY_ASSERT (prop_pair_p->header.types[0] != ECMA_PROPERTY_TYPE_HASHMAP); uint32_t index = (options & ECMA_GC_FREE_SECOND_PROPERTY); jmem_cpointer_t name_cp = prop_pair_p->names_cp[index]; ecma_property_t *property_p = prop_pair_p->header.types + index; ecma_property_t property = *property_p; #if JERRY_LCACHE if ((property & ECMA_PROPERTY_FLAG_LCACHED) != 0) { ecma_lcache_invalidate (object_p, name_cp, property_p); } #endif /* JERRY_LCACHE */ if (ECMA_PROPERTY_IS_RAW (property)) { if (ECMA_PROPERTY_GET_NAME_TYPE (property) == ECMA_DIRECT_STRING_PTR) { ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, name_cp); ecma_deref_ecma_string (prop_name_p); } if (property & ECMA_PROPERTY_FLAG_DATA) { ecma_free_value_if_not_object (prop_pair_p->values[index].value); return; } if (JERRY_UNLIKELY (options & ECMA_GC_FREE_REFERENCES)) { return; } return; } if (property == ECMA_PROPERTY_TYPE_DELETED) { return; } ecma_value_t value = prop_pair_p->values[index].value; switch (name_cp) { case LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD: { ecma_environment_record_t *environment_record_p; environment_record_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_environment_record_t, value); jmem_heap_free_block (environment_record_p, sizeof (ecma_environment_record_t)); break; } case LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED: { ecma_value_t *compact_collection_p; compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, value); ecma_compact_collection_free (compact_collection_p); break; } case LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS: { ecma_value_t *compact_collection_p; compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, value); ecma_value_t *end_p = ecma_compact_collection_end (compact_collection_p); ecma_value_t *current_p = compact_collection_p + 1; JERRY_ASSERT ((end_p - current_p) % ECMA_PRIVATE_ELEMENT_LIST_SIZE == 0); while (current_p < end_p) { current_p++; /* skip the type */ ecma_deref_ecma_string (ecma_get_prop_name_from_value (*current_p++)); current_p++; /* skip the value */ } ecma_compact_collection_destroy (compact_collection_p); break; } default: { JERRY_ASSERT (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER || name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES); ecma_gc_free_native_pointer (property, value); break; } } } /* ecma_gc_free_property */ /** * Free properties of an object */ void ecma_gc_free_properties (ecma_object_t *object_p, /**< object */ uint32_t options) /**< option bits */ { jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { ecma_property_hashmap_free (object_p); prop_iter_cp = object_p->u1.property_list_cp; } } #endif /* JERRY_PROPERTY_HASHMAP */ while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; for (uint32_t i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { ecma_gc_free_property (object_p, prop_pair_p, i | options); } prop_iter_cp = prop_iter_p->next_property_cp; ecma_dealloc_property_pair (prop_pair_p); } } /* ecma_gc_free_properties */ /** * Free specified object. */ static void ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ { JERRY_ASSERT (object_p != NULL && !ecma_gc_is_object_visited (object_p)); JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_objects_number) > 0); JERRY_CONTEXT (ecma_gc_objects_number)--; if (ecma_is_lexical_environment (object_p)) { #if JERRY_MODULE_SYSTEM if (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA)) { if (ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)) { ecma_gc_free_properties (object_p, ECMA_GC_FREE_REFERENCES); } ecma_dealloc_extended_object (object_p, sizeof (ecma_lexical_environment_class_t)); return; } #endif /* JERRY_MODULE_SYSTEM */ if (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_gc_free_properties (object_p, ECMA_GC_FREE_NO_OPTIONS); } ecma_dealloc_object (object_p); return; } ecma_object_type_t object_type = ecma_get_object_type (object_p); size_t ext_object_size = sizeof (ecma_extended_object_t); switch (object_type) { case ECMA_OBJECT_TYPE_GENERAL: { ecma_gc_free_properties (object_p, ECMA_GC_FREE_NO_OPTIONS); ecma_dealloc_object (object_p); return; } case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: { if (((ecma_extended_object_t *) object_p)->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL) { ext_object_size = sizeof (ecma_global_object_t); break; } uint8_t bitset_size = ((ecma_extended_object_t *) object_p)->u.built_in.u.length_and_bitset_size; ext_object_size += sizeof (uint64_t) * (bitset_size >> ECMA_BUILT_IN_BITSET_SHIFT); break; } case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: { ext_object_size = sizeof (ecma_extended_built_in_object_t); uint8_t bitset_size = ((ecma_extended_built_in_object_t *) object_p)->built_in.u.length_and_bitset_size; ext_object_size += sizeof (uint64_t) * (bitset_size >> ECMA_BUILT_IN_BITSET_SHIFT); /* FALLTHRU */ } case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_STRING: case ECMA_OBJECT_CLASS_NUMBER: case ECMA_OBJECT_CLASS_SYMBOL: #if JERRY_BUILTIN_BIGINT case ECMA_OBJECT_CLASS_BIGINT: #endif /* JERRY_BUILTIN_BIGINT */ { ecma_free_value (ext_object_p->u.cls.u3.value); break; } case ECMA_OBJECT_CLASS_ARGUMENTS: { ext_object_size = ecma_free_arguments_object (ext_object_p); break; } #if JERRY_BUILTIN_TYPEDARRAY case ECMA_OBJECT_CLASS_TYPEDARRAY: { if (ext_object_p->u.cls.u2.typedarray_flags & ECMA_TYPEDARRAY_IS_EXTENDED) { ext_object_size = sizeof (ecma_extended_typedarray_object_t); } break; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE_NAMESPACE: { ecma_gc_free_properties (object_p, ECMA_GC_FREE_REFERENCES); ecma_dealloc_extended_object (object_p, sizeof (ecma_extended_object_t)); return; } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_PARSER case ECMA_OBJECT_CLASS_SCRIPT: { ecma_compiled_code_t *compiled_code_p; compiled_code_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, ext_object_p->u.cls.u3.value); ecma_bytecode_deref (compiled_code_p); break; } #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_DATE case ECMA_OBJECT_CLASS_DATE: { ext_object_size = sizeof (ecma_date_object_t); break; } #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_REGEXP case ECMA_OBJECT_CLASS_REGEXP: { ecma_compiled_code_t *bytecode_p = ECMA_GET_INTERNAL_VALUE_ANY_POINTER (ecma_compiled_code_t, ext_object_p->u.cls.u3.value); ecma_bytecode_deref (bytecode_p); break; } #endif /* JERRY_BUILTIN_REGEXP */ case ECMA_OBJECT_CLASS_STRING_ITERATOR: { ecma_value_t iterated_value = ext_object_p->u.cls.u3.iterated_value; if (!ecma_is_value_empty (iterated_value)) { ecma_deref_ecma_string (ecma_get_string_from_value (iterated_value)); } break; } #if JERRY_BUILTIN_REGEXP case ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR: { ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) object_p; ecma_value_t iterated_string = regexp_string_iterator_obj->iterated_string; if (!ecma_is_value_empty (iterated_string)) { ecma_deref_ecma_string (ecma_get_string_from_value (iterated_string)); } ext_object_size = sizeof (ecma_regexp_string_iterator_t); break; } #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_TYPEDARRAY case ECMA_OBJECT_CLASS_ARRAY_BUFFER: #if JERRY_BUILTIN_SHAREDARRAYBUFFER case ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER: #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ { if (!(ECMA_ARRAYBUFFER_GET_FLAGS (ext_object_p) & ECMA_ARRAYBUFFER_HAS_POINTER)) { ext_object_size += ext_object_p->u.cls.u3.length; break; } ext_object_size = sizeof (ecma_arraybuffer_pointer_t); if (ECMA_ARRAYBUFFER_GET_FLAGS (ext_object_p) & ECMA_ARRAYBUFFER_ALLOCATED) { ecma_arraybuffer_release_buffer (object_p); } break; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_WEAKREF case ECMA_OBJECT_CLASS_WEAKREF: { ecma_value_t target = ext_object_p->u.cls.u3.target; if (!ecma_is_value_undefined (target)) { ecma_op_object_unref_weak (ecma_get_object_from_value (target), ecma_make_object_value (object_p)); } break; } #endif /* JERRY_BUILTIN_WEAKREF */ #if JERRY_BUILTIN_CONTAINER case ECMA_OBJECT_CLASS_CONTAINER: { ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_op_container_free_entries (object_p); ecma_collection_destroy (container_p); break; } #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_DATAVIEW case ECMA_OBJECT_CLASS_DATAVIEW: { ext_object_size = sizeof (ecma_dataview_object_t); break; } #endif /* JERRY_BUILTIN_DATAVIEW */ case ECMA_OBJECT_CLASS_GENERATOR: case ECMA_OBJECT_CLASS_ASYNC_GENERATOR: { ext_object_size = ecma_gc_free_executable_object (object_p); break; } case ECMA_OBJECT_CLASS_PROMISE: { ecma_free_value_if_not_object (ext_object_p->u.cls.u3.value); /* Reactions only contains objects. */ ecma_collection_destroy (((ecma_promise_object_t *) object_p)->reactions); ext_object_size = sizeof (ecma_promise_object_t); break; } case ECMA_OBJECT_CLASS_PROMISE_CAPABILITY: { ext_object_size = sizeof (ecma_promise_capability_t); break; } case ECMA_OBJECT_CLASS_ASYNC_FROM_SYNC_ITERATOR: { ext_object_size = sizeof (ecma_async_from_sync_iterator_object_t); break; } #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE: { ecma_module_release_module ((ecma_module_t *) ext_object_p); ext_object_size = sizeof (ecma_module_t); break; } #endif /* JERRY_MODULE_SYSTEM */ default: { /* The ECMA_OBJECT_CLASS__MAX type represents an uninitialized class. */ JERRY_ASSERT (ext_object_p->u.cls.type <= ECMA_OBJECT_CLASS__MAX); break; } } break; } case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { ext_object_size = sizeof (ecma_extended_built_in_object_t); uint8_t bitset_size = ((ecma_extended_built_in_object_t *) object_p)->built_in.u.length_and_bitset_size; ext_object_size += sizeof (uint64_t) * (bitset_size >> ECMA_BUILT_IN_BITSET_SHIFT); /* FALLTHRU */ } case ECMA_OBJECT_TYPE_ARRAY: { if (ecma_op_array_is_fast_array ((ecma_extended_object_t *) object_p)) { ecma_free_fast_access_array (object_p); return; } break; } #if JERRY_BUILTIN_PROXY case ECMA_OBJECT_TYPE_PROXY: { ext_object_size = sizeof (ecma_proxy_object_t); break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_OBJECT_TYPE_FUNCTION: { /* Function with byte-code (not a built-in function). */ ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; #if JERRY_SNAPSHOT_EXEC if (ext_func_p->u.function.bytecode_cp != ECMA_NULL_POINTER) { #endif /* JERRY_SNAPSHOT_EXEC */ ecma_compiled_code_t *byte_code_p = (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, ext_func_p->u.function.bytecode_cp)); if (CBC_FUNCTION_IS_ARROW (byte_code_p->status_flags)) { ecma_free_value_if_not_object (((ecma_arrow_function_t *) object_p)->this_binding); ecma_free_value_if_not_object (((ecma_arrow_function_t *) object_p)->new_target); ext_object_size = sizeof (ecma_arrow_function_t); } ecma_bytecode_deref (byte_code_p); #if JERRY_SNAPSHOT_EXEC } else { ext_object_size = sizeof (ecma_static_function_t); } #endif /* JERRY_SNAPSHOT_EXEC */ break; } case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { ecma_extended_object_t *extended_func_p = (ecma_extended_object_t *) object_p; if (!ecma_builtin_function_is_routine (object_p)) { uint8_t bitset_size = extended_func_p->u.built_in.u.length_and_bitset_size; ext_object_size += sizeof (uint64_t) * (bitset_size >> ECMA_BUILT_IN_BITSET_SHIFT); break; } if (extended_func_p->u.built_in.id != ECMA_BUILTIN_ID_HANDLER) { break; } switch (extended_func_p->u.built_in.routine_id) { case ECMA_NATIVE_HANDLER_PROMISE_RESOLVE: case ECMA_NATIVE_HANDLER_PROMISE_REJECT: { ext_object_size = sizeof (ecma_promise_resolver_t); break; } case ECMA_NATIVE_HANDLER_PROMISE_THEN_FINALLY: case ECMA_NATIVE_HANDLER_PROMISE_CATCH_FINALLY: { ext_object_size = sizeof (ecma_promise_finally_function_t); break; } case ECMA_NATIVE_HANDLER_PROMISE_CAPABILITY_EXECUTOR: { ext_object_size = sizeof (ecma_promise_capability_executor_t); break; } case ECMA_NATIVE_HANDLER_PROMISE_ALL_HELPER: { ext_object_size = sizeof (ecma_promise_all_executor_t); break; } #if JERRY_BUILTIN_PROXY case ECMA_NATIVE_HANDLER_PROXY_REVOKE: { ext_object_size = sizeof (ecma_revocable_proxy_object_t); break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_NATIVE_HANDLER_VALUE_THUNK: case ECMA_NATIVE_HANDLER_VALUE_THROWER: { ecma_free_value_if_not_object (((ecma_promise_value_thunk_t *) object_p)->value); ext_object_size = sizeof (ecma_promise_value_thunk_t); break; } case ECMA_NATIVE_HANDLER_ASYNC_FROM_SYNC_ITERATOR_UNWRAP: { break; } default: { JERRY_UNREACHABLE (); } } break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { ext_object_size = sizeof (ecma_bound_function_t); ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p; ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this; ecma_free_value (bound_func_p->target_length); if (!ecma_is_value_integer_number (args_len_or_this)) { ecma_free_value_if_not_object (args_len_or_this); break; } ecma_integer_value_t args_length = ecma_get_integer_from_value (args_len_or_this); ecma_value_t *args_p = (ecma_value_t *) (bound_func_p + 1); for (ecma_integer_value_t i = 0; i < args_length; i++) { ecma_free_value_if_not_object (args_p[i]); } size_t args_size = ((size_t) args_length) * sizeof (ecma_value_t); ext_object_size += args_size; break; } case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION: { ecma_script_deref (((ecma_extended_object_t *) object_p)->u.constructor_function.script_value); break; } case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { ext_object_size = sizeof (ecma_native_function_t); break; } default: { JERRY_UNREACHABLE (); } } ecma_gc_free_properties (object_p, ECMA_GC_FREE_NO_OPTIONS); ecma_dealloc_extended_object (object_p, ext_object_size); } /* ecma_gc_free_object */ /** * Run garbage collection, freeing objects that are no longer referenced. */ void ecma_gc_run (void) { #if (JERRY_GC_MARK_LIMIT != 0) JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) == JERRY_GC_MARK_LIMIT); #endif /* (JERRY_GC_MARK_LIMIT != 0) */ JERRY_CONTEXT (ecma_gc_new_objects) = 0; ecma_object_t black_list_head; black_list_head.gc_next_cp = JMEM_CP_NULL; ecma_object_t *black_end_p = &black_list_head; ecma_object_t white_gray_list_head; white_gray_list_head.gc_next_cp = JERRY_CONTEXT (ecma_gc_objects_cp); ecma_object_t *obj_prev_p = &white_gray_list_head; jmem_cpointer_t obj_iter_cp = obj_prev_p->gc_next_cp; ecma_object_t *obj_iter_p; /* Move root objects (i.e. they have global or stack references) to the black list. */ while (obj_iter_cp != JMEM_CP_NULL) { obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); const jmem_cpointer_t obj_next_cp = obj_iter_p->gc_next_cp; JERRY_ASSERT (obj_prev_p == NULL || ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_prev_p->gc_next_cp) == obj_iter_p); if (obj_iter_p->type_flags_refs >= ECMA_OBJECT_REF_ONE) { /* Moving the object to list of marked objects. */ obj_prev_p->gc_next_cp = obj_next_cp; black_end_p->gc_next_cp = obj_iter_cp; black_end_p = obj_iter_p; } else { obj_iter_p->type_flags_refs |= ECMA_OBJECT_NON_VISITED; obj_prev_p = obj_iter_p; } obj_iter_cp = obj_next_cp; } black_end_p->gc_next_cp = JMEM_CP_NULL; /* Mark root objects. */ obj_iter_cp = black_list_head.gc_next_cp; while (obj_iter_cp != JMEM_CP_NULL) { obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); ecma_gc_mark (obj_iter_p); obj_iter_cp = obj_iter_p->gc_next_cp; } /* Mark non-root objects. */ bool marked_anything_during_current_iteration; do { #if (JERRY_GC_MARK_LIMIT != 0) JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) == JERRY_GC_MARK_LIMIT); #endif /* (JERRY_GC_MARK_LIMIT != 0) */ marked_anything_during_current_iteration = false; obj_prev_p = &white_gray_list_head; obj_iter_cp = obj_prev_p->gc_next_cp; while (obj_iter_cp != JMEM_CP_NULL) { obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); const jmem_cpointer_t obj_next_cp = obj_iter_p->gc_next_cp; JERRY_ASSERT (obj_prev_p == NULL || ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_prev_p->gc_next_cp) == obj_iter_p); if (ecma_gc_is_object_visited (obj_iter_p)) { /* Moving the object to list of marked objects */ obj_prev_p->gc_next_cp = obj_next_cp; black_end_p->gc_next_cp = obj_iter_cp; black_end_p = obj_iter_p; #if (JERRY_GC_MARK_LIMIT != 0) if (obj_iter_p->type_flags_refs >= ECMA_OBJECT_REF_ONE) { /* Set the reference count of non-marked gray object to 0 */ obj_iter_p->type_flags_refs &= (ecma_object_descriptor_t) (ECMA_OBJECT_REF_ONE - 1); ecma_gc_mark (obj_iter_p); marked_anything_during_current_iteration = true; } #else /* (JERRY_GC_MARK_LIMIT == 0) */ marked_anything_during_current_iteration = true; #endif /* (JERRY_GC_MARK_LIMIT != 0) */ } else { obj_prev_p = obj_iter_p; } obj_iter_cp = obj_next_cp; } } while (marked_anything_during_current_iteration); black_end_p->gc_next_cp = JMEM_CP_NULL; JERRY_CONTEXT (ecma_gc_objects_cp) = black_list_head.gc_next_cp; /* Sweep objects that are currently unmarked. */ obj_iter_cp = white_gray_list_head.gc_next_cp; while (obj_iter_cp != JMEM_CP_NULL) { obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); const jmem_cpointer_t obj_next_cp = obj_iter_p->gc_next_cp; JERRY_ASSERT (!ecma_gc_is_object_visited (obj_iter_p)); ecma_gc_free_object (obj_iter_p); obj_iter_cp = obj_next_cp; } #if JERRY_BUILTIN_REGEXP /* Free RegExp bytecodes stored in cache */ re_cache_gc (); #endif /* JERRY_BUILTIN_REGEXP */ } /* ecma_gc_run */ /** * Try to free some memory (depending on memory pressure). * * When called with JMEM_PRESSURE_FULL, the engine will be terminated with JERRY_FATAL_OUT_OF_MEMORY. */ void ecma_free_unused_memory (jmem_pressure_t pressure) /**< current pressure */ { if (JERRY_LIKELY (pressure == JMEM_PRESSURE_LOW)) { #if JERRY_PROPERTY_HASHMAP if (JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) > ECMA_PROP_HASHMAP_ALLOC_ON) { --JERRY_CONTEXT (ecma_prop_hashmap_alloc_state); } JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_HIGH_PRESSURE_GC; #endif /* JERRY_PROPERTY_HASHMAP */ /* * If there is enough newly allocated objects since last GC, probably it is worthwhile to start GC now. * Otherwise, probability to free sufficient space is considered to be low. */ size_t new_objects_fraction = CONFIG_ECMA_GC_NEW_OBJECTS_FRACTION; if (JERRY_CONTEXT (ecma_gc_new_objects) * new_objects_fraction > JERRY_CONTEXT (ecma_gc_objects_number)) { ecma_gc_run (); } return; } else if (pressure == JMEM_PRESSURE_HIGH) { /* Freeing as much memory as we currently can */ #if JERRY_PROPERTY_HASHMAP if (JERRY_CONTEXT (status_flags) & ECMA_STATUS_HIGH_PRESSURE_GC) { JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) = ECMA_PROP_HASHMAP_ALLOC_MAX; } else if (JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) < ECMA_PROP_HASHMAP_ALLOC_MAX) { ++JERRY_CONTEXT (ecma_prop_hashmap_alloc_state); JERRY_CONTEXT (status_flags) |= ECMA_STATUS_HIGH_PRESSURE_GC; } #endif /* JERRY_PROPERTY_HASHMAP */ ecma_gc_run (); #if JERRY_PROPERTY_HASHMAP /* Free hashmaps of remaining objects. */ jmem_cpointer_t obj_iter_cp = JERRY_CONTEXT (ecma_gc_objects_cp); while (obj_iter_cp != JMEM_CP_NULL) { ecma_object_t *obj_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); if (!ecma_is_lexical_environment (obj_iter_p) || ecma_get_lex_env_type (obj_iter_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { if (!ecma_is_lexical_environment (obj_iter_p) && ecma_op_object_is_fast_array (obj_iter_p)) { obj_iter_cp = obj_iter_p->gc_next_cp; continue; } jmem_cpointer_t prop_iter_cp = obj_iter_p->u1.property_list_cp; if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { ecma_property_hashmap_free (obj_iter_p); } } } obj_iter_cp = obj_iter_p->gc_next_cp; } #endif /* JERRY_PROPERTY_HASHMAP */ jmem_pools_collect_empty (); return; } else if (JERRY_UNLIKELY (pressure == JMEM_PRESSURE_FULL)) { jerry_fatal (JERRY_FATAL_OUT_OF_MEMORY); } else { JERRY_ASSERT (pressure == JMEM_PRESSURE_NONE); JERRY_UNREACHABLE (); } } /* ecma_free_unused_memory */ /** * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-helpers-value.cpp000664 001750 001750 00000105360 15164251010 045326 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers-number.h" #include "ecma-helpers.h" #include "jrt-bit-fields.h" #include "jrt.h" #include "vm-defines.h" JERRY_STATIC_ASSERT (((int)ECMA_TYPE___MAX <= (int)ECMA_VALUE_TYPE_MASK), ecma_types_must_be_less_than_mask); JERRY_STATIC_ASSERT (((ECMA_VALUE_TYPE_MASK + 1) == (1 << ECMA_VALUE_SHIFT)), ecma_value_part_must_start_after_flags); JERRY_STATIC_ASSERT (((int)ECMA_VALUE_SHIFT <= (int)JMEM_ALIGNMENT_LOG), ecma_value_shift_must_be_less_than_or_equal_than_mem_alignment_log); JERRY_STATIC_ASSERT ((sizeof (jmem_cpointer_t) <= sizeof (ecma_value_t)), size_of_jmem_cpointer_t_must_be_less_or_equal_to_the_size_of_ecma_value_t); JERRY_STATIC_ASSERT ((sizeof (jmem_cpointer_t) <= sizeof (jmem_cpointer_tag_t)), size_of_jmem_cpointer_t_must_be_less_or_equal_to_the_size_of_jmem_cpointer_tag_t); #ifdef ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY /* cppcheck-suppress zerodiv */ JERRY_STATIC_ASSERT (sizeof (uintptr_t) <= sizeof (ecma_value_t), uintptr_t_must_fit_in_ecma_value_t); #else /* !ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ JERRY_STATIC_ASSERT ((sizeof (uintptr_t) > sizeof (ecma_value_t)), uintptr_t_must_not_fit_in_ecma_value_t); #endif /* ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ JERRY_STATIC_ASSERT ((((int)ECMA_VALUE_FALSE | (1 << (int)ECMA_DIRECT_SHIFT)) == (int)ECMA_VALUE_TRUE) && ECMA_VALUE_FALSE != ECMA_VALUE_TRUE, only_the_lowest_bit_must_be_different_for_simple_value_true_and_false); #if JERRY_BUILTIN_BIGINT JERRY_STATIC_ASSERT (ECMA_NULL_POINTER == (ECMA_BIGINT_ZERO & ~(ecma_value_t) ECMA_VALUE_TYPE_MASK), ecma_bigint_zero_must_be_encoded_as_null_pointer); #endif /* JERRY_BUILTIN_BIGINT */ /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ /** * Get type field of ecma value * * @return type field */ ecma_type_t JERRY_ATTR_CONST ecma_get_value_type_field (ecma_value_t value) /**< ecma value */ { return (ecma_type_t)(((unsigned int) value) & ((unsigned int)ECMA_VALUE_TYPE_MASK)); } /* ecma_get_value_type_field */ /** * Convert a pointer into an ecma value. * * @return ecma value */ static inline ecma_value_t JERRY_ATTR_PURE ecma_pointer_to_ecma_value (const void *ptr) /**< pointer */ { #ifdef ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY JERRY_ASSERT (ptr != NULL); uintptr_t uint_ptr = (uintptr_t) ptr; JERRY_ASSERT ((uint_ptr & ECMA_VALUE_TYPE_MASK) == 0); return (ecma_value_t) uint_ptr; #else /* !ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ jmem_cpointer_t ptr_cp; ECMA_SET_NON_NULL_POINTER (ptr_cp, ptr); return ((ecma_value_t) ptr_cp) << ECMA_VALUE_SHIFT; #endif /* ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ } /* ecma_pointer_to_ecma_value */ /** * Get a pointer from an ecma value * * @return pointer */ static inline void *JERRY_ATTR_PURE ecma_get_pointer_from_ecma_value (ecma_value_t value) /**< value */ { #ifdef ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY void *ptr = (void *) (uintptr_t) ((value) & ~ECMA_VALUE_TYPE_MASK); JERRY_ASSERT (ptr != NULL); return ptr; #else /* !ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ return ECMA_GET_NON_NULL_POINTER (void, value >> ECMA_VALUE_SHIFT); #endif /* ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ } /* ecma_get_pointer_from_ecma_value */ /** * Check if the value is direct ecma-value. * * @return true - if the value is a direct value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_direct (ecma_value_t value) /**< ecma value */ { return (ecma_get_value_type_field (value) == ECMA_TYPE_DIRECT); } /* ecma_is_value_direct */ /** * Check if the value is simple ecma-value. * * @return true - if the value is a simple value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_simple (ecma_value_t value) /**< ecma value */ { return (value & ECMA_DIRECT_TYPE_MASK) == ECMA_DIRECT_TYPE_SIMPLE_VALUE; } /* ecma_is_value_simple */ /** * Check whether the value is a given simple value. * * @return true - if the value is equal to the given simple value, * false - otherwise */ static inline bool JERRY_ATTR_CONST ecma_is_value_equal_to_simple_value (ecma_value_t value, /**< ecma value */ ecma_value_t simple_value) /**< simple value */ { return value == simple_value; } /* ecma_is_value_equal_to_simple_value */ /** * Check if the value is empty. * * @return true - if the value contains implementation-defined empty simple value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_empty (ecma_value_t value) /**< ecma value */ { return ecma_is_value_equal_to_simple_value (value, ECMA_VALUE_EMPTY); } /* ecma_is_value_empty */ /** * Check if the value is undefined. * * @return true - if the value contains ecma-undefined simple value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_undefined (ecma_value_t value) /**< ecma value */ { return ecma_is_value_equal_to_simple_value (value, ECMA_VALUE_UNDEFINED); } /* ecma_is_value_undefined */ /** * Check if the value is null. * * @return true - if the value contains ecma-null simple value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_null (ecma_value_t value) /**< ecma value */ { return ecma_is_value_equal_to_simple_value (value, ECMA_VALUE_NULL); } /* ecma_is_value_null */ /** * Check if the value is boolean. * * @return true - if the value contains ecma-true or ecma-false simple values, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_boolean (ecma_value_t value) /**< ecma value */ { return ecma_is_value_true (value | (1 << ECMA_DIRECT_SHIFT)); } /* ecma_is_value_boolean */ /** * Check if the value is true. * * @return true - if the value contains ecma-true simple value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_true (ecma_value_t value) /**< ecma value */ { return ecma_is_value_equal_to_simple_value (value, ECMA_VALUE_TRUE); } /* ecma_is_value_true */ /** * Check if the value is false. * * @return true - if the value contains ecma-false simple value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_false (ecma_value_t value) /**< ecma value */ { return ecma_is_value_equal_to_simple_value (value, ECMA_VALUE_FALSE); } /* ecma_is_value_false */ /** * Check if the value is not found. * * @return true - if the value contains ecma-not-found simple value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_found (ecma_value_t value) /**< ecma value */ { return value != ECMA_VALUE_NOT_FOUND; } /* ecma_is_value_found */ /** * Check if the value is array hole. * * @return true - if the value contains ecma-array-hole simple value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_array_hole (ecma_value_t value) /**< ecma value */ { return ecma_is_value_equal_to_simple_value (value, ECMA_VALUE_ARRAY_HOLE); } /* ecma_is_value_array_hole */ /** * Check if the value is integer ecma-number. * * @return true - if the value contains an integer ecma-number value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_integer_number (ecma_value_t value) /**< ecma value */ { return (value & ECMA_DIRECT_TYPE_MASK) == ECMA_DIRECT_TYPE_INTEGER_VALUE; } /* ecma_is_value_integer_number */ /** * Check if both values are integer ecma-numbers. * * @return true - if both values contain integer ecma-number values, * false - otherwise */ bool JERRY_ATTR_CONST ecma_are_values_integer_numbers (ecma_value_t first_value, /**< first ecma value */ ecma_value_t second_value) /**< second ecma value */ { JERRY_STATIC_ASSERT (ECMA_DIRECT_TYPE_INTEGER_VALUE == 0, ecma_direct_type_integer_value_must_be_zero); return ((first_value | second_value) & ECMA_DIRECT_TYPE_MASK) == ECMA_DIRECT_TYPE_INTEGER_VALUE; } /* ecma_are_values_integer_numbers */ /** * Check if the value is floating-point ecma-number. * * @return true - if the value contains a floating-point ecma-number value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_float_number (ecma_value_t value) /**< ecma value */ { return (ecma_get_value_type_field (value) == ECMA_TYPE_FLOAT); } /* ecma_is_value_float_number */ /** * Check if the value is ecma-number. * * @return true - if the value contains ecma-number value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_number (ecma_value_t value) /**< ecma value */ { return (ecma_is_value_integer_number (value) || ecma_is_value_float_number (value)); } /* ecma_is_value_number */ JERRY_STATIC_ASSERT (((ECMA_TYPE_STRING | 0x4) == ECMA_TYPE_DIRECT_STRING), ecma_type_string_and_direct_string_must_have_one_bit_difference); /** * Check if the value is ecma-string. * * @return true - if the value contains ecma-string value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_string (ecma_value_t value) /**< ecma value */ { return ((value & (ECMA_VALUE_TYPE_MASK - 0x4)) == ECMA_TYPE_STRING); } /* ecma_is_value_string */ /** * Check if the value is symbol. * * @return true - if the value contains symbol value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_symbol (ecma_value_t value) /**< ecma value */ { return (ecma_get_value_type_field (value) == ECMA_TYPE_SYMBOL); } /* ecma_is_value_symbol */ /** * Check if the value is a specific magic string. * * @return true - if the value the magic string value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_magic_string (ecma_value_t value, /**< ecma value */ lit_magic_string_id_t id) /**< magic string id */ { return value == ecma_make_magic_string_value (id); } /* ecma_is_value_magic_string */ /** * Check if the value is bigint. * * @return true - if the value contains bigint value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_bigint (ecma_value_t value) /**< ecma value */ { #if JERRY_BUILTIN_BIGINT return (ecma_get_value_type_field (value) == ECMA_TYPE_BIGINT); #else /* !JERRY_BUILTIN_BIGINT */ JERRY_UNUSED (value); return false; #endif /* JERRY_BUILTIN_BIGINT */ } /* ecma_is_value_bigint */ /** * Check if the value can be property name. * * @return true - if the value can be property name value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_prop_name (ecma_value_t value) /**< ecma value */ { return ecma_is_value_string (value) || ecma_is_value_symbol (value); } /* ecma_is_value_prop_name */ /** * Check if the value is direct ecma-string. * * @return true - if the value contains direct ecma-string value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_direct_string (ecma_value_t value) /**< ecma value */ { return (ecma_get_value_type_field (value) == ECMA_TYPE_DIRECT_STRING); } /* ecma_is_value_direct_string */ /** * Check if the value is non-direct ecma-string. * * @return true - if the value contains non-direct ecma-string value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_non_direct_string (ecma_value_t value) /**< ecma value */ { return (ecma_get_value_type_field (value) == ECMA_TYPE_STRING); } /* ecma_is_value_non_direct_string */ /** * Check if the value is object. * * @return true - if the value contains object value, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_object (ecma_value_t value) /**< ecma value */ { return (ecma_get_value_type_field (value) == ECMA_TYPE_OBJECT); } /* ecma_is_value_object */ /** * Check if the value is error reference. * * @return true - if the value contains an error reference, * false - otherwise */ bool JERRY_ATTR_CONST ecma_is_value_exception (ecma_value_t value) /**< ecma value */ { return (ecma_get_value_type_field (value) == ECMA_TYPE_ERROR); } /* ecma_is_value_exception */ /** * Debug assertion that specified value's type is one of ECMA-defined * script-visible types, i.e.: undefined, null, boolean, number, string, object. */ void ecma_check_value_type_is_spec_defined (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_is_value_undefined (value) || ecma_is_value_null (value) || ecma_is_value_boolean (value) || ecma_is_value_number (value) || ecma_is_value_string (value) || ecma_is_value_bigint (value) || ecma_is_value_symbol (value) || ecma_is_value_object (value)); } /* ecma_check_value_type_is_spec_defined */ /** * Checks if the given argument is an array or not. * * @return ECMA_VALUE_ERROR- if the operation fails * ECMA_VALUE_{TRUE/FALSE} - depends on whether 'arg' is an array object */ ecma_value_t ecma_is_value_array (ecma_value_t arg) /**< argument */ { if (!ecma_is_value_object (arg)) { return ECMA_VALUE_FALSE; } ecma_object_t *arg_obj_p = ecma_get_object_from_value (arg); if (ecma_get_object_base_type (arg_obj_p) == ECMA_OBJECT_BASE_TYPE_ARRAY) { return ECMA_VALUE_TRUE; } #if JERRY_BUILTIN_PROXY if (ECMA_OBJECT_IS_PROXY (arg_obj_p)) { ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) arg_obj_p; if (proxy_obj_p->handler == ECMA_VALUE_NULL) { return ecma_raise_type_error (ECMA_ERR_PROXY_HANDLER_IS_NULL_FOR_ISARRAY_OPERATION); } return ecma_is_value_array (proxy_obj_p->target); } #endif /* JERRY_BUILTIN_PROXY */ return ECMA_VALUE_FALSE; } /* ecma_is_value_array */ /** * Creates an ecma value from the given raw boolean. * * @return boolean ecma_value */ ecma_value_t JERRY_ATTR_CONST ecma_make_boolean_value (bool boolean_value) /**< raw bool value from which the ecma value will be created */ { return boolean_value ? ECMA_VALUE_TRUE : ECMA_VALUE_FALSE; } /* ecma_make_boolean_value */ /** * Encode an integer number into an ecma-value without allocating memory * * Note: * The value must fit into the range of allowed ecma integer values * * @return ecma-value */ ecma_value_t JERRY_ATTR_CONST ecma_make_integer_value (ecma_integer_value_t integer_value) /**< integer number to be encoded */ { JERRY_ASSERT (ECMA_IS_INTEGER_NUMBER (integer_value)); return (((ecma_value_t) integer_value) << ECMA_DIRECT_SHIFT) | ECMA_DIRECT_TYPE_INTEGER_VALUE; } /* ecma_make_integer_value */ /** * Allocate and initialize a new float number without checks. * * @return ecma-value */ static ecma_value_t ecma_create_float_number (ecma_number_t ecma_number) /**< value of the float number */ { ecma_number_t *ecma_num_p = ecma_alloc_number (); *ecma_num_p = ecma_number; return ecma_pointer_to_ecma_value (ecma_num_p) | ECMA_TYPE_FLOAT; } /* ecma_create_float_number */ /** * Encode float number without checks. * * @return ecma-value */ ecma_value_t ecma_make_float_value (ecma_number_t *ecma_num_p) /**< pointer to the float number */ { return ecma_pointer_to_ecma_value (ecma_num_p) | ECMA_TYPE_FLOAT; } /* ecma_make_float_value */ /** * Create a new NaN value. * * @return ecma-value */ ecma_value_t JERRY_ATTR_CONST ecma_make_nan_value (void) { return ecma_create_float_number (ecma_number_make_nan ()); } /* ecma_make_nan_value */ /** * Checks whether the passed number is +0.0 * * @return true, if it is +0.0, false otherwise */ static inline bool JERRY_ATTR_CONST ecma_is_number_equal_to_positive_zero (ecma_number_t ecma_number) /**< number */ { return ecma_number_to_binary (ecma_number) == ECMA_NUMBER_BINARY_ZERO; } /* ecma_is_number_equal_to_positive_zero */ /** * Encode a property length number into an ecma-value * * @return ecma-value */ ecma_value_t ecma_make_length_value (ecma_length_t number) /**< number to be encoded */ { if (number <= ECMA_INTEGER_NUMBER_MAX) { return ecma_make_integer_value ((ecma_integer_value_t) number); } return ecma_create_float_number ((ecma_number_t) number); } /* ecma_make_length_value */ /** * Encode a number into an ecma-value * * @return ecma-value */ ecma_value_t ecma_make_number_value (ecma_number_t ecma_number) /**< number to be encoded */ { ecma_integer_value_t integer_value = (ecma_integer_value_t) ecma_number; if ((ecma_number_t) integer_value == ecma_number && ((integer_value == 0) ? ecma_is_number_equal_to_positive_zero (ecma_number) : ECMA_IS_INTEGER_NUMBER (integer_value))) { return ecma_make_integer_value (integer_value); } return ecma_create_float_number (ecma_number); } /* ecma_make_number_value */ /** * Encode an int32 number into an ecma-value * * @return ecma-value */ ecma_value_t ecma_make_int32_value (int32_t int32_number) /**< int32 number to be encoded */ { if (ECMA_IS_INTEGER_NUMBER (int32_number)) { return ecma_make_integer_value ((ecma_integer_value_t) int32_number); } return ecma_create_float_number ((ecma_number_t) int32_number); } /* ecma_make_int32_value */ /** * Encode an unsigned int32 number into an ecma-value * * @return ecma-value */ ecma_value_t ecma_make_uint32_value (uint32_t uint32_number) /**< uint32 number to be encoded */ { if (uint32_number <= ECMA_INTEGER_NUMBER_MAX) { return ecma_make_integer_value ((ecma_integer_value_t) uint32_number); } return ecma_create_float_number ((ecma_number_t) uint32_number); } /* ecma_make_uint32_value */ /** * String value constructor * * @return ecma-value representation of the string argument */ ecma_value_t JERRY_ATTR_PURE ecma_make_string_value (const ecma_string_t *ecma_string_p) /**< string to reference in value */ { JERRY_ASSERT (ecma_string_p != NULL); JERRY_ASSERT (!ecma_prop_name_is_symbol ((ecma_string_t *) ecma_string_p)); if ((((uintptr_t) ecma_string_p) & ECMA_VALUE_TYPE_MASK) != 0) { return (ecma_value_t) (uintptr_t) ecma_string_p; } return ecma_pointer_to_ecma_value (ecma_string_p) | ECMA_TYPE_STRING; } /* ecma_make_string_value */ /** * Symbol value constructor * * @return ecma-value representation of the string argument */ ecma_value_t JERRY_ATTR_PURE ecma_make_symbol_value (const ecma_string_t *ecma_symbol_p) /**< symbol to reference in value */ { JERRY_ASSERT (ecma_symbol_p != NULL); JERRY_ASSERT (ecma_prop_name_is_symbol ((ecma_string_t *) ecma_symbol_p)); return ecma_pointer_to_ecma_value (ecma_symbol_p) | ECMA_TYPE_SYMBOL; } /* ecma_make_symbol_value */ /** * Property-name value constructor * * @return ecma-value representation of a property name argument */ ecma_value_t JERRY_ATTR_PURE ecma_make_prop_name_value (const ecma_string_t *ecma_prop_name_p) /**< property name to reference in value */ { JERRY_ASSERT (ecma_prop_name_p != NULL); if (ecma_prop_name_is_symbol ((ecma_string_t *) ecma_prop_name_p)) { return ecma_make_symbol_value (ecma_prop_name_p); } return ecma_make_string_value (ecma_prop_name_p); } /* ecma_make_prop_name_value */ /** * String value constructor * * @return ecma-value representation of the string argument */ ecma_value_t JERRY_ATTR_PURE ecma_make_magic_string_value (lit_magic_string_id_t id) /**< magic string id */ { return (ecma_value_t) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_MAGIC, (uintptr_t) id); } /* ecma_make_magic_string_value */ /** * Object value constructor * * @return ecma-value representation of the object argument */ ecma_value_t JERRY_ATTR_PURE ecma_make_object_value (const ecma_object_t *object_p) /**< object to reference in value */ { JERRY_ASSERT (object_p != NULL); return ecma_pointer_to_ecma_value (object_p) | ECMA_TYPE_OBJECT; } /* ecma_make_object_value */ /** * Error reference constructor * * @return ecma-value representation of the Error reference */ ecma_value_t JERRY_ATTR_PURE ecma_make_extended_primitive_value (const ecma_extended_primitive_t *primitive_p, /**< extended primitive value */ uint32_t type) /**< ecma type of extended primitive value */ { JERRY_ASSERT (primitive_p != NULL); #if JERRY_BUILTIN_BIGINT JERRY_ASSERT (primitive_p != ECMA_BIGINT_POINTER_TO_ZERO); #endif /* JERRY_BUILTIN_BIGINT */ JERRY_ASSERT (type == ECMA_TYPE_BIGINT || type == ECMA_TYPE_ERROR); return ecma_pointer_to_ecma_value (primitive_p) | type; } /* ecma_make_extended_primitive_value */ /** * Get integer value from an integer ecma value * * @return integer value */ ecma_integer_value_t JERRY_ATTR_CONST ecma_get_integer_from_value (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_is_value_integer_number (value)); return ((ecma_integer_value_t) value) >> ECMA_DIRECT_SHIFT; } /* ecma_get_integer_from_value */ /** * Get floating point value from an ecma value * * @return floating point value */ ecma_number_t JERRY_ATTR_PURE ecma_get_float_from_value (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_get_value_type_field (value) == ECMA_TYPE_FLOAT); return *(ecma_number_t *) ecma_get_pointer_from_ecma_value (value); } /* ecma_get_float_from_value */ /** * Get floating point value pointer from an ecma value * * @return floating point value */ ecma_number_t *JERRY_ATTR_PURE ecma_get_pointer_from_float_value (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_get_value_type_field (value) == ECMA_TYPE_FLOAT); return (ecma_number_t *) ecma_get_pointer_from_ecma_value (value); } /* ecma_get_pointer_from_float_value */ /** * Get floating point value from an ecma value * * @return floating point value */ ecma_number_t JERRY_ATTR_PURE ecma_get_number_from_value (ecma_value_t value) /**< ecma value */ { if (ecma_is_value_integer_number (value)) { return (ecma_number_t) ecma_get_integer_from_value (value); } return ecma_get_float_from_value (value); } /* ecma_get_number_from_value */ /** * Get pointer to ecma-string from ecma value * * @return the string pointer */ ecma_string_t *JERRY_ATTR_PURE ecma_get_string_from_value (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_is_value_string (value)); if ((value & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_DIRECT_STRING) { return (ecma_string_t *) (uintptr_t) value; } return (ecma_string_t *) ecma_get_pointer_from_ecma_value (value); } /* ecma_get_string_from_value */ /** * Get pointer to ecma-string from ecma value * * @return the string pointer */ ecma_string_t *JERRY_ATTR_PURE ecma_get_symbol_from_value (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_is_value_symbol (value)); return (ecma_string_t *) ecma_get_pointer_from_ecma_value (value); } /* ecma_get_symbol_from_value */ /** * Get pointer to a property name from ecma value * * @return the string pointer */ ecma_string_t *JERRY_ATTR_PURE ecma_get_prop_name_from_value (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_is_value_prop_name (value)); if ((value & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_DIRECT_STRING) { return (ecma_string_t *) (uintptr_t) value; } return (ecma_string_t *) ecma_get_pointer_from_ecma_value (value); } /* ecma_get_prop_name_from_value */ /** * Get pointer to ecma-object from ecma value * * @return the pointer */ ecma_object_t *JERRY_ATTR_PURE ecma_get_object_from_value (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_is_value_object (value)); return (ecma_object_t *) ecma_get_pointer_from_ecma_value (value); } /* ecma_get_object_from_value */ /** * Get pointer to error reference from ecma value * * @return the pointer */ ecma_extended_primitive_t *JERRY_ATTR_PURE ecma_get_extended_primitive_from_value (ecma_value_t value) /**< ecma value */ { #if JERRY_BUILTIN_BIGINT JERRY_ASSERT (value != ECMA_BIGINT_ZERO); #endif /* JERRY_BUILTIN_BIGINT */ JERRY_ASSERT (ecma_get_value_type_field (value) == ECMA_TYPE_BIGINT || ecma_get_value_type_field (value) == ECMA_TYPE_ERROR); return (ecma_extended_primitive_t *) ecma_get_pointer_from_ecma_value (value); } /* ecma_get_extended_primitive_from_value */ /** * Invert a boolean value * * @return ecma value */ ecma_value_t JERRY_ATTR_CONST ecma_invert_boolean_value (ecma_value_t value) /**< ecma value */ { JERRY_ASSERT (ecma_is_value_boolean (value)); return (value ^ (1 << ECMA_DIRECT_SHIFT)); } /* ecma_invert_boolean_value */ /** * Copy ecma value. * * @return copy of the given value */ ecma_value_t ecma_copy_value (ecma_value_t value) /**< value description */ { switch (ecma_get_value_type_field (value)) { case ECMA_TYPE_FLOAT: { ecma_number_t *num_p = (ecma_number_t *) ecma_get_pointer_from_ecma_value (value); ecma_number_t *new_num_p = ecma_alloc_number (); *new_num_p = *num_p; return ecma_make_float_value (new_num_p); } case ECMA_TYPE_SYMBOL: case ECMA_TYPE_STRING: { ecma_string_t *string_p = (ecma_string_t *) ecma_get_pointer_from_ecma_value (value); ecma_ref_ecma_string_non_direct (string_p); return value; } #if JERRY_BUILTIN_BIGINT case ECMA_TYPE_BIGINT: { if (value != ECMA_BIGINT_ZERO) { ecma_ref_extended_primitive (ecma_get_extended_primitive_from_value (value)); } return value; } #endif /* JERRY_BUILTIN_BIGINT */ case ECMA_TYPE_OBJECT: { ecma_ref_object_inline (ecma_get_object_from_value (value)); return value; } default: { JERRY_ASSERT (ecma_get_value_type_field (value) == ECMA_TYPE_DIRECT || ecma_get_value_type_field (value) == ECMA_TYPE_DIRECT_STRING); return value; } } } /* ecma_copy_value */ /** * Copy ecma value. * * Note: * this function is similar to ecma_copy_value, but it is * faster for direct values since no function call is performed. * It also increases the binary size so it is recommended for * critical code paths only. * * @return copy of the given value */ ecma_value_t ecma_fast_copy_value (ecma_value_t value) /**< value description */ { return (ecma_get_value_type_field (value) == ECMA_TYPE_DIRECT) ? value : ecma_copy_value (value); } /* ecma_fast_copy_value */ /** * Copy the ecma value if not an object * * @return copy of the given value */ ecma_value_t ecma_copy_value_if_not_object (ecma_value_t value) /**< value description */ { if (!ecma_is_value_object (value)) { return ecma_copy_value (value); } return value; } /* ecma_copy_value_if_not_object */ /** * Increase reference counter of a value if it is an object. * * @return void */ void ecma_ref_if_object (ecma_value_t value) /**< value description */ { if (ecma_is_value_object (value)) { ecma_ref_object (ecma_get_object_from_value (value)); } } /* ecma_ref_if_object */ /** * Decrease reference counter of a value if it is an object. * * @return void */ void ecma_deref_if_object (ecma_value_t value) /**< value description */ { if (ecma_is_value_object (value)) { ecma_deref_object (ecma_get_object_from_value (value)); } } /* ecma_deref_if_object */ /** * Assign a new value to an ecma-value * * Note: * value previously stored in the property is freed */ void ecma_value_assign_value (ecma_value_t *value_p, /**< [in, out] ecma value */ ecma_value_t ecma_value) /**< value to assign */ { JERRY_STATIC_ASSERT (ECMA_TYPE_DIRECT == 0, ecma_type_direct_must_be_zero_for_the_next_check); if (*value_p == ecma_value) { return; } if (ecma_get_value_type_field (ecma_value || *value_p) == ECMA_TYPE_DIRECT) { *value_p = ecma_value; } else if (ecma_is_value_float_number (ecma_value) && ecma_is_value_float_number (*value_p)) { const ecma_number_t *num_src_p = (ecma_number_t *) ecma_get_pointer_from_ecma_value (ecma_value); ecma_number_t *num_dst_p = (ecma_number_t *) ecma_get_pointer_from_ecma_value (*value_p); *num_dst_p = *num_src_p; } else { ecma_free_value_if_not_object (*value_p); *value_p = ecma_copy_value_if_not_object (ecma_value); } } /* ecma_value_assign_value */ /** * Update the value of a float number to a new value * * Note: * The original value is destroyed. * * @return updated ecma value */ ecma_value_t ecma_update_float_number (ecma_value_t float_value, /**< original float value */ ecma_number_t new_number) /**< updated number value */ { JERRY_ASSERT (ecma_is_value_float_number (float_value)); ecma_integer_value_t integer_number = (ecma_integer_value_t) new_number; ecma_number_t *number_p = (ecma_number_t *) ecma_get_pointer_from_ecma_value (float_value); if ((ecma_number_t) integer_number == new_number && ((integer_number == 0) ? ecma_is_number_equal_to_positive_zero (new_number) : ECMA_IS_INTEGER_NUMBER (integer_number))) { ecma_dealloc_number (number_p); return ecma_make_integer_value (integer_number); } *number_p = new_number; return float_value; } /* ecma_update_float_number */ /** * Assign a float number to an ecma-value * * Note: * value previously stored in the property is freed */ static void ecma_value_assign_float_number (ecma_value_t *value_p, /**< [in, out] ecma value */ ecma_number_t ecma_number) /**< number to assign */ { if (ecma_is_value_float_number (*value_p)) { ecma_number_t *num_dst_p = (ecma_number_t *) ecma_get_pointer_from_ecma_value (*value_p); *num_dst_p = ecma_number; return; } if (ecma_get_value_type_field (*value_p) != ECMA_TYPE_DIRECT && ecma_get_value_type_field (*value_p) != ECMA_TYPE_OBJECT) { ecma_free_value (*value_p); } *value_p = ecma_create_float_number (ecma_number); } /* ecma_value_assign_float_number */ /** * Assign a number to an ecma-value * * Note: * value previously stored in the property is freed */ void ecma_value_assign_number (ecma_value_t *value_p, /**< [in, out] ecma value */ ecma_number_t ecma_number) /**< number to assign */ { ecma_integer_value_t integer_value = (ecma_integer_value_t) ecma_number; if ((ecma_number_t) integer_value == ecma_number && ((integer_value == 0) ? ecma_is_number_equal_to_positive_zero (ecma_number) : ECMA_IS_INTEGER_NUMBER (integer_value))) { if (ecma_get_value_type_field (*value_p) != ECMA_TYPE_DIRECT && ecma_get_value_type_field (*value_p) != ECMA_TYPE_OBJECT) { ecma_free_value (*value_p); } *value_p = ecma_make_integer_value (integer_value); return; } ecma_value_assign_float_number (value_p, ecma_number); } /* ecma_value_assign_number */ /** * Free the ecma value */ void ecma_free_value (ecma_value_t value) /**< value description */ { switch (ecma_get_value_type_field (value)) { case ECMA_TYPE_FLOAT: { ecma_number_t *number_p = (ecma_number_t *) ecma_get_pointer_from_ecma_value (value); ecma_dealloc_number (number_p); break; } case ECMA_TYPE_SYMBOL: case ECMA_TYPE_STRING: { ecma_string_t *string_p = (ecma_string_t *) ecma_get_pointer_from_ecma_value (value); ecma_deref_ecma_string_non_direct (string_p); break; } case ECMA_TYPE_OBJECT: { ecma_deref_object (ecma_get_object_from_value (value)); break; } #if JERRY_BUILTIN_BIGINT case ECMA_TYPE_BIGINT: { if (value != ECMA_BIGINT_ZERO) { ecma_deref_bigint (ecma_get_extended_primitive_from_value (value)); } break; } #endif /* JERRY_BUILTIN_BIGINT */ default: { JERRY_ASSERT (ecma_get_value_type_field (value) == ECMA_TYPE_DIRECT || ecma_get_value_type_field (value) == ECMA_TYPE_DIRECT_STRING); /* no memory is allocated */ break; } } } /* ecma_free_value */ /** * Free the ecma value * * Note: * this function is similar to ecma_free_value, but it is * faster for direct values since no function call is performed. * It also increases the binary size so it is recommended for * critical code paths only. * * @return void */ void ecma_fast_free_value (ecma_value_t value) /**< value description */ { if (ecma_get_value_type_field (value) != ECMA_TYPE_DIRECT) { ecma_free_value (value); } } /* ecma_fast_free_value */ /** * Free the ecma value if not an object */ void ecma_free_value_if_not_object (ecma_value_t value) /**< value description */ { if (ecma_get_value_type_field (value) != ECMA_TYPE_OBJECT) { ecma_free_value (value); } } /* ecma_free_value_if_not_object */ /** * Free an ecma-value object * * @return void */ void ecma_free_object (ecma_value_t value) /**< value description */ { ecma_deref_object (ecma_get_object_from_value (value)); } /* ecma_free_object */ /** * Free an ecma-value number * * @return void */ void ecma_free_number (ecma_value_t value) /**< value description */ { JERRY_ASSERT (ecma_is_value_number (value)); if (ecma_is_value_float_number (value)) { ecma_number_t *number_p = (ecma_number_t *) ecma_get_pointer_from_ecma_value (value); ecma_dealloc_number (number_p); } } /* ecma_free_number */ /** * Get the literal id associated with the given ecma_value type. * This operation is equivalent to the JavaScript 'typeof' operator. * * @returns one of the following value: * - LIT_MAGIC_STRING_UNDEFINED * - LIT_MAGIC_STRING_OBJECT * - LIT_MAGIC_STRING_BOOLEAN * - LIT_MAGIC_STRING_NUMBER * - LIT_MAGIC_STRING_STRING * - LIT_MAGIC_STRING_FUNCTION */ lit_magic_string_id_t ecma_get_typeof_lit_id (ecma_value_t value) /**< input ecma value */ { lit_magic_string_id_t ret_value = LIT_MAGIC_STRING__EMPTY; if (ecma_is_value_undefined (value)) { ret_value = LIT_MAGIC_STRING_UNDEFINED; } else if (ecma_is_value_null (value)) { ret_value = LIT_MAGIC_STRING_OBJECT; } else if (ecma_is_value_boolean (value)) { ret_value = LIT_MAGIC_STRING_BOOLEAN; } else if (ecma_is_value_number (value)) { ret_value = LIT_MAGIC_STRING_NUMBER; } else if (ecma_is_value_string (value)) { ret_value = LIT_MAGIC_STRING_STRING; } else if (ecma_is_value_symbol (value)) { ret_value = LIT_MAGIC_STRING_SYMBOL; } #if JERRY_BUILTIN_BIGINT else if (ecma_is_value_bigint (value)) { ret_value = LIT_MAGIC_STRING_BIGINT; } #endif /* JERRY_BUILTIN_BIGINT */ else { JERRY_ASSERT (ecma_is_value_object (value)); ret_value = ecma_op_is_callable (value) ? LIT_MAGIC_STRING_FUNCTION : LIT_MAGIC_STRING_OBJECT; } JERRY_ASSERT (ret_value != LIT_MAGIC_STRING__EMPTY); return ret_value; } /* ecma_get_typeof_lit_id */ /** * @} * @} */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/lit/lit-unicode-folding.inc.h000664 001750 001750 00000005054 15164251010 044670 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 file is automatically generated by the gen-unicode.py script * from the CaseFolding.txt file. Do not edit! */ /** * Character interval starting points for folding_skip_to_lower. */ static const uint16_t lit_unicode_folding_skip_to_lower_interval_starts[] JERRY_ATTR_CONST_DATA = { 0x13a0, 0x13f8, 0xab70 }; /** * Character interval lengths for folding_skip_to_lower. */ static const uint8_t lit_unicode_folding_skip_to_lower_interval_lengths[] JERRY_ATTR_CONST_DATA = { 0x0055, 0x0005, 0x004f }; /** * Non-interval characters for folding_skip_to_lower. */ static const uint16_t lit_unicode_folding_skip_to_lower_chars[] JERRY_ATTR_CONST_DATA = { 0x0130 }; /** * Character interval starting points for folding_to_upper. */ static const uint16_t lit_unicode_folding_to_upper_interval_starts[] JERRY_ATTR_CONST_DATA = { 0x03d0, 0x03d5, 0x03f0, 0x13f8, 0x1c80, 0xab70 }; /** * Character interval lengths for folding_to_upper. */ static const uint8_t lit_unicode_folding_to_upper_interval_lengths[] JERRY_ATTR_CONST_DATA = { 0x0001, 0x0001, 0x0001, 0x0005, 0x0008, 0x004f }; /** * Non-interval characters for folding_to_upper. */ static const uint16_t lit_unicode_folding_to_upper_chars[] JERRY_ATTR_CONST_DATA = { 0x00b5, 0x017f, 0x0345, 0x03c2, 0x03f5, 0x1e9b, 0x1fbe }; mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/tvgScene.h000664 001750 001750 00000032726 15164251010 033553 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_SCENE_H_ #define _TVG_SCENE_H_ #include #include "tvgMath.h" #include "tvgPaint.h" struct SceneIterator : Iterator { list* paints; list::iterator itr; SceneIterator(list* p) : paints(p) { begin(); } const Paint* next() override { if (itr == paints->end()) return nullptr; auto paint = *itr; ++itr; return paint; } uint32_t count() override { return paints->size(); } void begin() override { itr = paints->begin(); } }; struct SceneImpl : Scene { Paint::Impl impl; list paints; //children list RenderRegion vport = {}; Array* effects = nullptr; Point fsize; //fixed scene size bool fixed = false; //true: fixed scene size, false: dynamic size bool vdirty = false; uint8_t opacity; //for composition SceneImpl() : impl(Paint::Impl(this)) { } ~SceneImpl() { clearPaints(); resetEffects(false); } void size(const Point& size) { this->fsize = size; fixed = (size.x > 0 && size.y > 0) ? true : false; } uint8_t needComposition(uint8_t opacity) { if (opacity == 0 || paints.empty()) return 0; //post effects, masking, blending may require composition if (effects) impl.mark(CompositionFlag::PostProcessing); if (PAINT(this)->mask(nullptr) != MaskMethod::None) impl.mark(CompositionFlag::Masking); if (impl.blendMethod != BlendMethod::Normal) impl.mark(CompositionFlag::Blending); //Half translucent requires intermediate composition. if (opacity == 255) return impl.cmpFlag; //Only shape or picture may not require composition. if (paints.size() == 1) { auto type = paints.front()->type(); if (type == Type::Shape || type == Type::Picture) return impl.cmpFlag; } impl.mark(CompositionFlag::Opacity); return 1; } bool skip(RenderUpdateFlag flag) { return false; } bool update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) { if (paints.empty()) return true; if (needComposition(opacity)) { /* Overriding opacity value. If this scene is half-translucent, It must do intermediate composition with that opacity value. */ this->opacity = opacity; opacity = 255; } //allow partial rendering? auto recover = fixed ? renderer->partial(true) : false; for (auto paint : paints) { PAINT(paint)->update(renderer, transform, clips, opacity, flag, false); } //recover the condition if (fixed) renderer->partial(recover); if (effects) { ARRAY_FOREACH(p, *effects) { renderer->prepare(*p, transform); } } //this viewport update is more performant than in bounds(). No idea. vport = renderer->viewport(); if (fixed) { auto pt = fsize * transform; vport.intersect({{int32_t(round(transform.e13)), int32_t(round(transform.e23))}, {int32_t(round(pt.x)), int32_t(round(pt.y))}}); } else { vdirty = true; } //bounds(renderer) here hinders parallelization //TODO: we can bring the precise effects region here if (fixed || effects) impl.damage(vport); return true; } bool render(RenderMethod* renderer) { if (paints.empty()) return true; RenderCompositor* cmp = nullptr; auto ret = true; renderer->blend(impl.blendMethod); if (impl.cmpFlag) { cmp = renderer->target(bounds(), renderer->colorSpace(), impl.cmpFlag); renderer->beginComposite(cmp, MaskMethod::None, opacity); } for (auto paint : paints) { ret &= paint->pImpl->render(renderer); } if (cmp) { //Apply post effects if any. if (effects) { //Notify the possibility of the direct composition of the effect result to the origin surface. auto direct = (effects->count == 1) & (impl.marked(CompositionFlag::PostProcessing)); ARRAY_FOREACH(p, *effects) { if ((*p)->valid) renderer->render(cmp, *p, direct); } } renderer->endComposite(cmp); } return ret; } RenderRegion bounds() { if (paints.empty()) return {}; if (!vdirty) return vport; vdirty = false; //Merge regions RenderRegion pRegion = {{INT32_MAX, INT32_MAX}, {0, 0}}; for (auto paint : paints) { auto region = paint->pImpl->bounds(); if (region.min.x < pRegion.min.x) pRegion.min.x = region.min.x; if (pRegion.max.x < region.max.x) pRegion.max.x = region.max.x; if (region.min.y < pRegion.min.y) pRegion.min.y = region.min.y; if (pRegion.max.y < region.max.y) pRegion.max.y = region.max.y; } //Extends the render region if post effects require RenderRegion eRegion{}; if (effects) { ARRAY_FOREACH(p, *effects) { auto effect = *p; if (effect->valid && impl.renderer->region(effect)) eRegion.add(effect->extend); } } pRegion.min.x += eRegion.min.x; pRegion.min.y += eRegion.min.y; pRegion.max.x += eRegion.max.x; pRegion.max.y += eRegion.max.y; vport = RenderRegion::intersect(vport, pRegion); return vport; } bool bounds(Point* pt4, const Matrix& m, bool obb) { if (paints.empty()) return false; Point min = {FLT_MAX, FLT_MAX}; Point max = {-FLT_MAX, -FLT_MAX}; auto ret = false; for (auto paint : paints) { Point tmp[4]; if (!PAINT(paint)->bounds(tmp, obb ? nullptr : &m, false)) continue; //Merge regions for (int i = 0; i < 4; ++i) { if (tmp[i].x < min.x) min.x = tmp[i].x; if (tmp[i].x > max.x) max.x = tmp[i].x; if (tmp[i].y < min.y) min.y = tmp[i].y; if (tmp[i].y > max.y) max.y = tmp[i].y; } ret = true; } pt4[0] = min; pt4[1] = Point{max.x, min.y}; pt4[2] = max; pt4[3] = Point{min.x, max.y}; if (obb) { pt4[0] *= m; pt4[1] *= m; pt4[2] *= m; pt4[3] *= m; } return ret; } bool intersects(const RenderRegion& region) { if (!impl.renderer) return false; if (this->bounds().intersected(region)) { for (auto paint : paints) { if (PAINT(paint)->intersects(region)) return true; } } return false; } Paint* duplicate(Paint* ret) { if (ret) TVGERR("RENDERER", "TODO: duplicate()"); auto scene = Scene::gen(); auto dup = to(scene); for (auto paint : paints) { auto cdup = paint->duplicate(); PAINT(cdup)->parent = scene; cdup->ref(); dup->paints.push_back(cdup); } if (effects) { dup->effects = new Array; ARRAY_FOREACH(p, *effects) { RenderEffect* ret = nullptr; switch ((*p)->type) { case SceneEffect::GaussianBlur: { ret = new RenderEffectGaussianBlur(*(RenderEffectGaussianBlur*)(*p)); break; } case SceneEffect::DropShadow: { ret = new RenderEffectDropShadow(*(RenderEffectDropShadow*)(*p)); break; } case SceneEffect::Fill: { ret = new RenderEffectFill(*(RenderEffectFill*)(*p)); break; } case SceneEffect::Tint: { ret = new RenderEffectTint(*(RenderEffectTint*)(*p)); break; } case SceneEffect::Tritone: { ret = new RenderEffectTritone(*(RenderEffectTritone*)(*p)); break; } default: break; } if (ret) { ret->rd = nullptr; ret->valid = false; dup->effects->push(ret); } } } if (fixed) dup->size(fsize); return scene; } Result clearPaints() { if (paints.empty()) return Result::Success; //Don't need to damage for children auto recover = (fixed && impl.renderer) ? impl.renderer->partial(true) : false; auto partialDmg = !(effects || fixed || recover); auto itr = paints.begin(); while (itr != paints.end()) { auto paint = PAINT((*itr)); //when the paint is destroyed damage will be triggered if (paint->refCnt > 1 && partialDmg) paint->damage(); paint->unref(); paints.erase(itr++); } if (fixed && impl.renderer) impl.renderer->partial(recover); if (effects || fixed) impl.damage(vport); //redraw scene full region return Result::Success; } Result remove(Paint* paint) { if (PAINT(paint)->parent != this) return Result::InsufficientCondition; //when the paint is destroyed damage will be triggered if (PAINT(paint)->refCnt > 1) PAINT(paint)->damage(); PAINT(paint)->unref(); paints.remove(paint); return Result::Success; } Result insert(Paint* target, Paint* at) { if (!target) return Result::InvalidArguments; auto timpl = PAINT(target); if (timpl->parent) { TVGERR("RENDERER", "Target paint(%p) is already owned by a parent(%p)", target, timpl->parent); return Result::InsufficientCondition; } target->ref(); //Relocated the paint to the current scene space timpl->mark(RenderUpdateFlag::Transform); if (!at) { paints.push_back(target); } else { //OPTIMIZE: Remove searching? auto itr = find_if(paints.begin(), paints.end(),[&at](const Paint* paint){ return at == paint; }); if (itr == paints.end()) return Result::InvalidArguments; paints.insert(itr, target); } timpl->parent = this; if (timpl->clipper) PAINT(timpl->clipper)->parent = this; if (timpl->maskData) PAINT(timpl->maskData->target)->parent = this; return Result::Success; } Iterator* iterator() { return new SceneIterator(&paints); } Result resetEffects(bool damage = true) { if (effects) { ARRAY_FOREACH(p, *effects) { if (impl.renderer) impl.renderer->dispose(*p); delete(*p); } delete(effects); effects = nullptr; if (damage) impl.damage(vport); } return Result::Success; } Result add(SceneEffect effect, va_list& args) { if (effect == SceneEffect::Clear) return resetEffects(); if (!this->effects) this->effects = new Array; RenderEffect* re = nullptr; switch (effect) { case SceneEffect::GaussianBlur: { re = RenderEffectGaussianBlur::gen(args); break; } case SceneEffect::DropShadow: { re = RenderEffectDropShadow::gen(args); break; } case SceneEffect::Fill: { re = RenderEffectFill::gen(args); break; } case SceneEffect::Tint: { re = RenderEffectTint::gen(args); break; } case SceneEffect::Tritone: { re = RenderEffectTritone::gen(args); break; } default: break; } if (!re) return Result::InvalidArguments; this->effects->push(re); return Result::Success; } }; #endif //_TVG_SCENE_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgTessellator.h000664 001750 001750 00000006311 15164251010 037167 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_TESSELLATOR_H_ #define _TVG_WG_TESSELLATOR_H_ #include "tvgRender.h" #include "tvgWgGeometry.h" #define MIN_WG_STROKE_WIDTH 1.0f #define MIN_WG_STROKE_ALPHA 0.25f class WgStroker { struct State { Point firstPt; Point firstPtDir; Point prevPt; Point prevPtDir; }; public: WgStroker(WgMeshData* buffer, float width, StrokeCap cap, StrokeJoin join = StrokeJoin::Bevel); void run(const RenderShape& rshape, const RenderPath& path, const Matrix& m); RenderRegion bounds() const; BBox getBBox() const; private: void run(const RenderPath& path, const Matrix& m); float radius() const { return mWidth * 0.5f; } void cap(); void lineTo(const Point& curr); void cubicTo(const Point& cnt1, const Point& cnt2, const Point& end, const Matrix& m); void close(); void join(const Point& dir); void round(const Point& prev, const Point& curr, const Point& center); void miter(const Point& prev, const Point& curr, const Point& center); void bevel(const Point& prev, const Point& curr, const Point& center); void square(const Point& p, const Point& outDir); void squarePoint(const Point& p); void round(const Point& p, const Point& outDir); void roundPoint(const Point& p); WgMeshData* mBuffer; float mWidth = 0.0f; float mMiterLimit = 4.f; StrokeCap mCap = StrokeCap::Square; StrokeJoin mJoin = StrokeJoin::Bevel; State mState = {}; Point mLeftTop = {0.0f, 0.0f}; Point mRightBottom = {0.0f, 0.0f}; float mScale; }; class WgBWTessellator { public: WgBWTessellator(WgMeshData* buffer); void tessellate(const RenderPath& path, const Matrix& matrix); RenderRegion bounds() const; BBox getBBox() const; bool convex = true; private: uint32_t pushVertex(float x, float y); void pushTriangle(uint32_t a, uint32_t b, uint32_t c); WgMeshData* mBuffer; BBox bbox = {}; Point firstPt = {}; Point prevPt = {}; Point prevEdge = {}; int8_t winding = -1; //0: unknown, 1: CW, -1: CCW }; #endif /* _TVG_WG_TESSELLATOR_H_ */mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/ttf/tvgTtfLoader.h000664 001750 001750 00000005762 15164251010 035022 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_TTF_LOADER_H_ #define _TVG_TTF_LOADER_H_ #include #include "tvgLoader.h" #include "tvgTaskScheduler.h" #include "tvgTtfReader.h" using namespace std; struct TtfMetrics : FontMetrics { float baseWidth; //Use as the reference glyph width for italic transform }; struct TtfLoader : public FontLoader { #if defined(_WIN32) && (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) void* mapping = nullptr; #endif TtfReader reader; unordered_map glyphs; //glypha cache. key: codepoint char* text = nullptr; bool nomap = false; bool freeData = false; TtfLoader(); ~TtfLoader(); using FontLoader::open; using FontLoader::read; bool open(const char* path) override; bool open(const char *data, uint32_t size, const char* rpath, bool copy) override; void transform(Paint* paint, FontMetrics& fm, float italicShear) override; bool get(FontMetrics& fm, char* text, RenderPath& out) override; void copy(const FontMetrics& in, FontMetrics& out) override; void release(FontMetrics& fm) override; void metrics(const FontMetrics& fm, TextMetrics& out) override; private: float height(uint32_t loc, float spacing) { return (reader.metrics.hhea.advance * loc - reader.metrics.hhea.linegap) * spacing; } uint32_t feedLine(FontMetrics& fm, float box, float x, uint32_t begin, uint32_t end, Point& cursor, uint32_t& loc, RenderPath& out); void wrapNone(FontMetrics& fm, const Point& box, char* utf8, RenderPath& out); void wrapChar(FontMetrics& fm, const Point& box, char* utf8, RenderPath& out); void wrapWord(FontMetrics& fm, const Point& box, char* utf8, RenderPath& out, bool smart); void wrapEllipsis(FontMetrics& fm, const Point& box, char* utf8, RenderPath& out); TtfGlyphMetrics* request(uint32_t code); void clear(); }; #endif //_TVG_PNG_LOADER_H_ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieLoader.cpp000664 001750 001750 00000030050 15164251010 036470 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgStr.h" #include "tvgLottieLoader.h" #include "tvgLottieModel.h" #include "tvgLottieParser.h" #include "tvgLottieBuilder.h" #include "tvgCompressor.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ LottieCustomSlot::~LottieCustomSlot() { ARRAY_FOREACH(p, props) { delete(p->prop); } } bool LottieLoader::prepare() { LottieParser parser(content, dirName, builder->expressions()); if (!parser.parse()) return false; { ScopedLock lock(key); comp = parser.comp; } if (!comp) return false; if (parser.slots) { auto slotcode = gen(parser.slots, true); apply(slotcode, true); del(slotcode, true); parser.slots = nullptr; } builder->build(comp); release(); return true; } void LottieLoader::run(unsigned tid) { if (comp) builder->update(comp, frameNo); //update frame else if (prepare()) builder->update(comp, 0); //initial loading build = false; } void LottieLoader::release() { if (copy) { tvg::free((char*)content); content = nullptr; } } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ LottieLoader::LottieLoader() : FrameModule(FileType::Lot), builder(new LottieBuilder) { } LottieLoader::~LottieLoader() { done(); release(); //TODO: correct position? delete(comp); delete(builder); tvg::free(dirName); } bool LottieLoader::header() { //A single thread doesn't need to perform intensive tasks. if (TaskScheduler::threads() == 0) { LoadModule::read(); if (prepare()) { w = static_cast(comp->w); h = static_cast(comp->h); segmentEnd = frameCnt = comp->frameCnt(); frameRate = comp->frameRate; return true; } return false; } //Quickly validate the given Lottie file without parsing in order to get the animation info. auto startFrame = 0.0f; auto endFrame = 0.0f; uint32_t depth = 0; auto p = content; while (*p != '\0') { if (*p == '{') { ++depth; ++p; continue; } if (*p == '}') { --depth; ++p; continue; } if (depth != 1) { ++p; continue; } //version if (!strncmp(p, "\"v\":", 4)) { p += 4; continue; } //framerate if (!strncmp(p, "\"fr\":", 5)) { p += 5; auto e = strstr(p, ","); if (!e) e = strstr(p, "}"); frameRate = toFloat(p, nullptr); p = e; continue; } //start frame if (!strncmp(p, "\"ip\":", 5)) { p += 5; auto e = strstr(p, ","); if (!e) e = strstr(p, "}"); startFrame = toFloat(p, nullptr); p = e; continue; } //end frame if (!strncmp(p, "\"op\":", 5)) { p += 5; auto e = strstr(p, ","); if (!e) e = strstr(p, "}"); endFrame = toFloat(p, nullptr); p = e; continue; } //width if (!strncmp(p, "\"w\":", 4)) { p += 4; auto e = strstr(p, ","); if (!e) e = strstr(p, "}"); w = toFloat(p, nullptr); p = e; continue; } //height if (!strncmp(p, "\"h\":", 4)) { p += 4; auto e = strstr(p, ","); if (!e) e = strstr(p, "}"); h = toFloat(p, nullptr); p = e; continue; } ++p; } if (frameRate < FLOAT_EPSILON) { TVGLOG("LOTTIE", "Not a Lottie file? Frame rate is 0!"); return false; } segmentEnd = frameCnt = (endFrame - startFrame); TVGLOG("LOTTIE", "info: frame rate = %f, duration = %f size = %f x %f", frameRate, frameCnt / frameRate, w, h); return true; } bool LottieLoader::open(const char* data, uint32_t size, const char* rpath, bool copy) { if (copy) { content = tvg::malloc(size + 1); if (!content) return false; memcpy((char*)content, data, size); const_cast(content)[size] = '\0'; } else content = data; this->size = size; this->copy = copy; if (!rpath) this->dirName = duplicate("."); else this->dirName = duplicate(rpath); return header(); } bool LottieLoader::open(const char* path) { #ifdef THORVG_FILE_IO_SUPPORT if ((content = LoadModule::open(path, size, true))) { dirName = tvg::dirname(path); copy = true; return header(); } #endif return false; } bool LottieLoader::resize(Paint* paint, float w, float h) { if (!paint) return false; auto sx = w / this->w; auto sy = h / this->h; Matrix m = {sx, 0, 0, 0, sy, 0, 0, 0, 1}; paint->transform(m); //apply the scale to the base clipper auto clipper = PAINT(paint)->clipper; if (clipper) clipper->transform(m); return true; } bool LottieLoader::read() { //the loading has been already completed if (!LoadModule::read()) return true; if (!content || size == 0) return false; TaskScheduler::request(this); return true; } Paint* LottieLoader::paint() { sync(); if (!comp) return nullptr; comp->initiated = true; return comp->root->scene; } bool LottieLoader::apply(uint32_t slotcode, bool byDefault) { if (curSlot == slotcode) return true; if (!ready() || comp->slots.count == 0) return false; auto applied = false; // Reset all slots if slotcode is 0 if (slotcode == 0) { ARRAY_FOREACH(p, comp->slots) (*p)->reset(); applied = true; } else { //Find the custom slot with the slotcode INLIST_FOREACH(this->slots, slot) { if (slot->code != slotcode) continue; //apply the custom slot property to the targets. ARRAY_FOREACH(p, slot->props) { p->target->apply(p->prop, byDefault); } applied = true; break; } } curSlot = slotcode; if (applied) build = true; return applied; } bool LottieLoader::del(uint32_t slotcode, bool byDefault) { if (comp->slots.empty() || slotcode == 0 || !ready()) return false; // Search matching value and remove INLIST_SAFE_FOREACH(this->slots, slot) { if (slot->code != slotcode) continue; if (!byDefault) { ARRAY_FOREACH(p, slot->props) { p->target->reset(); } build = true; } this->slots.remove(slot); delete(slot); break; } return true; } uint32_t LottieLoader::gen(const char* slots, bool byDefault) { if (!slots || !ready() || comp->slots.empty()) return 0; //parsing slot json auto temp = byDefault ? slots : duplicate(slots); LottieParser parser(temp, dirName, builder->expressions()); parser.comp = comp; auto idx = 0; auto custom = new LottieCustomSlot(djb2Encode(slots)); //Generates list of the custom slot overriding while (auto sid = djb2Encode(parser.sid(idx == 0))) { //Associates the overrding target to apply for the current custom slot auto found = false; ARRAY_FOREACH(p, comp->slots) { if ((*p)->sid != sid) continue; //find target if (auto prop = parser.parse(*p)) custom->props.push({prop, *p}); found = true; break; } if (!found) parser.skip(); //skip the value if the target slot is not found ++idx; } tvg::free((char*)temp); //Success, valid custom slot. if (custom->props.count > 0) { this->slots.back(custom); return custom->code; } delete(custom); return 0; } float LottieLoader::shorten(float frameNo) { //This ensures that the target frame number is reached. return nearbyintf((frameNo + startFrame()) * 10000.0f) * 0.0001f; } bool LottieLoader::frame(float no) { no = shorten(no); //Skip update if frame diff is too small. if (!builder->tweening() && fabsf(this->frameNo - no) <= 0.0009f) return false; this->done(); this->frameNo = no; builder->offTween(); if (comp) comp->clear(); //clear synchronously TaskScheduler::request(this); return true; } float LottieLoader::startFrame() { return segmentBegin; } float LottieLoader::totalFrame() { return segmentEnd - segmentBegin; } float LottieLoader::curFrame() { return frameNo - startFrame(); } float LottieLoader::duration() { return (segmentEnd - segmentBegin) / frameRate; } void LottieLoader::sync() { done(); if (build) run(0); } uint32_t LottieLoader::markersCnt() { return ready() ? comp->markers.count : 0; } const char* LottieLoader::markers(uint32_t index) { if (!ready() || index >= comp->markers.count) return nullptr; auto marker = comp->markers.begin() + index; return (*marker)->name; } Result LottieLoader::segment(float begin, float end) { if (begin < 0.0f) begin = 0.0f; if (end > frameCnt) end = frameCnt; if (begin > end) return Result::InvalidArguments; segmentBegin = begin; segmentEnd = end; return Result::Success; } bool LottieLoader::segment(const char* marker, float& begin, float& end) { if (!ready() || comp->markers.count == 0) return false; ARRAY_FOREACH(p, comp->markers) { if (!strcmp(marker, (*p)->name)) { begin = (*p)->time; end = (*p)->time + (*p)->duration; return true; } } return false; } bool LottieLoader::ready() { { ScopedLock lock(key); if (comp) return true; } done(); if (comp) return true; return false; } bool LottieLoader::tween(float from, float to, float progress) { //tweening is not necessary if (tvg::zero(progress)) return frame(from); else if (tvg::equal(progress, 1.0f)) return frame(to); done(); frameNo = shorten(from); builder->onTween(shorten(to), progress); if (comp) comp->clear(); //clear synchronously TaskScheduler::request(this); return true; } bool LottieLoader::assign(const char* layer, uint32_t ix, const char* var, float val) { if (!ready() || !comp->expressions) return false; comp->root->assign(layer, ix, var, val); return true; } bool LottieLoader::quality(uint8_t value) { if (!ready()) return false; if (comp->quality != value) { comp->quality = value; build = true; } return true; } void LottieLoader::set(const AssetResolver* resolver) { builder->resolver = resolver; } src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/gl_engine/tvgGlRenderTarget.h000664 001750 001750 00000003703 15164251010 037230 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_GL_RENDER_RENDER_TARGET_H_ #define _TVG_GL_RENDER_RENDER_TARGET_H_ #include "tvgGlCommon.h" struct GlRenderTarget { GlRenderTarget(); ~GlRenderTarget(); void init(uint32_t width, uint32_t height, GLint resolveId); void reset(); bool invalid() const { return fbo == 0; } RenderRegion viewport{}; uint32_t width = 0, height = 0; GLuint resolvedFbo = 0, fbo = 0, colorTex = 0; private: GLuint colorBuffer = 0; GLuint depthStencilBuffer = 0; }; struct GlRenderTargetPool { GlRenderTargetPool(uint32_t maxWidth, uint32_t maxHeight); ~GlRenderTargetPool(); GlRenderTarget* getRenderTarget(const RenderRegion& vp, GLuint resolveId = 0); private: uint32_t maxWidth = 0; uint32_t maxHeight = 0; Array pool; }; #endif //_TVG_GL_RENDER_RENDER_TARGET_H_src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-rangeerror-prototype.cpp000664 001750 001750 00000002267 15164251010 052614 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-string-object.h" #include "jrt.h" #if JERRY_BUILTIN_ERRORS #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-rangeerror-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID range_error_prototype #include "ecma-builtin-internal-routines-template.inc.h" #endif /* JERRY_BUILTIN_ERRORS */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/parser/js/js-parser-limits.h000664 001750 001750 00000010572 15164251010 044612 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 JS_PARSER_LIMITS_H #define JS_PARSER_LIMITS_H /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_internals Internals * @{ */ /** * Maximum identifier length accepted by the parser. * Limit: LEXER_MAX_STRING_LENGTH. */ #ifndef PARSER_MAXIMUM_IDENT_LENGTH #define PARSER_MAXIMUM_IDENT_LENGTH 255 #endif /* !PARSER_MAXIMUM_IDENT_LENGTH */ /** * Maximum string limit. * Limit: 2147483647 / 65535. */ #define PARSER_MAXIMUM_STRING_LIMIT 65535 /** * Maximum string length. * Limit: PARSER_MAXIMUM_STRING_LIMIT. */ #ifndef PARSER_MAXIMUM_STRING_LENGTH #define PARSER_MAXIMUM_STRING_LENGTH PARSER_MAXIMUM_STRING_LIMIT #endif /* !PARSER_MAXIMUM_STRING_LENGTH */ /** * Maximum number of registers. * Limit: min: 256, max: min(PARSER_MAXIMUM_NUMBER_OF_LITERALS / 2, 16383) */ #ifndef PARSER_MAXIMUM_NUMBER_OF_REGISTERS #define PARSER_MAXIMUM_NUMBER_OF_REGISTERS 256 #endif /* !PARSER_MAXIMUM_NUMBER_OF_REGISTERS */ /** * Maximum number of literals. * Limit: 32767 - PARSER_MAXIMUM_NUMBER_OF_REGISTERS. Recommended: 32767 - PARSER_MAXIMUM_NUMBER_OF_REGISTERS. */ #ifndef PARSER_MAXIMUM_NUMBER_OF_LITERALS #define PARSER_MAXIMUM_NUMBER_OF_LITERALS (32767 - PARSER_MAXIMUM_NUMBER_OF_REGISTERS) #endif /* !PARSER_MAXIMUM_NUMBER_OF_LITERALS */ /** * Maximum depth of scope stack. * Limit: 32767. Recommended: 32767 */ #ifndef PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK #define PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK 32767 #endif /* !PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK */ /** * Maximum code size. * Limit: 16777215. Recommended: 65535, 16777215. */ #ifndef PARSER_MAXIMUM_CODE_SIZE #define PARSER_MAXIMUM_CODE_SIZE (65535 << (JMEM_ALIGNMENT_LOG)) #endif /* !PARSER_MAXIMUM_CODE_SIZE */ /** * Maximum number of values pushed onto the stack by a function. * Limit: 65500. Recommended: 1024. */ #ifndef PARSER_MAXIMUM_STACK_LIMIT #define PARSER_MAXIMUM_STACK_LIMIT 1024 #endif /* !PARSER_MAXIMUM_STACK_LIMIT */ /* Checks. */ #if (PARSER_MAXIMUM_STRING_LENGTH < 1) || (PARSER_MAXIMUM_STRING_LENGTH > PARSER_MAXIMUM_STRING_LIMIT) #error "Maximum string length is not within range." #endif /* (PARSER_MAXIMUM_STRING_LENGTH < 1) || (PARSER_MAXIMUM_STRING_LENGTH > PARSER_MAXIMUM_STRING_LIMIT) */ #if (PARSER_MAXIMUM_IDENT_LENGTH < 1) || (PARSER_MAXIMUM_IDENT_LENGTH > PARSER_MAXIMUM_STRING_LENGTH) #error "Maximum identifier length is not within range." #endif /* (PARSER_MAXIMUM_IDENT_LENGTH < 1) || (PARSER_MAXIMUM_IDENT_LENGTH > PARSER_MAXIMUM_STRING_LENGTH) */ #if ((PARSER_MAXIMUM_NUMBER_OF_LITERALS < 1) \ || (PARSER_MAXIMUM_NUMBER_OF_LITERALS + PARSER_MAXIMUM_NUMBER_OF_REGISTERS > 32767)) #error "Maximum number of literals is not within range." #endif /* ((PARSER_MAXIMUM_NUMBER_OF_LITERALS < 1) \ || (PARSER_MAXIMUM_NUMBER_OF_LITERALS > 32767)) */ #if (PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK < 1) || (PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK > 32767) #error "Maximum depth of scope stack is not within range." #endif /* (PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK < 1) || (PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK > 32767) */ #if ((PARSER_MAXIMUM_NUMBER_OF_REGISTERS * 2) > PARSER_MAXIMUM_NUMBER_OF_LITERALS) #error "Maximum number of registers is not within range." #endif /* ((PARSER_MAXIMUM_NUMBER_OF_REGISTERS * 2) > PARSER_MAXIMUM_NUMBER_OF_LITERALS) */ #if (PARSER_MAXIMUM_CODE_SIZE < 4096) || (PARSER_MAXIMUM_CODE_SIZE > 16777215) #error "Maximum code size is not within range." #endif /* (PARSER_MAXIMUM_CODE_SIZE < 4096) || (PARSER_MAXIMUM_CODE_SIZE > 16777215) */ #if (PARSER_MAXIMUM_STACK_LIMIT < 16) || (PARSER_MAXIMUM_STACK_LIMIT > 65500) #error "Maximum function stack usage is not within range." #endif /* (PARSER_MAXIMUM_STACK_LIMIT < 16) || (PARSER_MAXIMUM_STACK_LIMIT > 65500) */ /** * @} * @} * @} */ #endif /* !JS_PARSER_LIMITS_H */ src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-aggregateerror.inc.h000664 001750 001750 00000002216 15164251010 051612 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ /* * AggregateError built-in description */ #include "ecma-builtin-helpers-macro-defines.inc.h" /* Number properties: * (property name, number value, writable, enumerable, configurable) */ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 2, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) /* Object properties: * (property name, object pointer getter) */ /* ECMA-262 v5, 15.11.3.1 */ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_AGGREGATE_ERROR_UL, ECMA_PROPERTY_FLAG_CONFIGURABLE) #include "ecma-builtin-helpers-macro-undefs.inc.h" src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgRenderer.cpp000664 001750 001750 00000051643 15164251010 036777 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "tvgTaskScheduler.h" #include "tvgWgRenderer.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ static atomic rendererCnt{-1}; void WgRenderer::release() { if (!mContext.queue) return; disposeObjects(); // clear render data paint pools mRenderDataShapePool.release(mContext); mRenderDataPicturePool.release(mContext); mRenderDataEffectParamsPool.release(mContext); // clear render pool mRenderTargetPool.release(mContext); // clear rendering tree stacks mCompositorList.clear(); mRenderTargetStack.clear(); mRenderTargetRoot.release(mContext); // release context handles mCompositor.release(mContext); mContext.release(); // release gpu handles clearTargets(); } void WgRenderer::disposeObjects() { ARRAY_FOREACH(p, mDisposeRenderDatas) { auto renderData = (WgRenderDataPaint*)(*p); if (renderData->type() == Type::Shape) { mRenderDataShapePool.free(mContext, (WgRenderDataShape*)renderData); } else { mRenderDataPicturePool.free(mContext, (WgRenderDataPicture*)renderData); } } mDisposeRenderDatas.clear(); } void WgRenderer::releaseSurfaceTexture() { if (surfaceTexture.texture) { wgpuTextureRelease(surfaceTexture.texture); surfaceTexture.texture = nullptr; } } void WgRenderer::clearTargets() { releaseSurfaceTexture(); if (surface) wgpuSurfaceUnconfigure(surface); targetTexture = nullptr; surface = nullptr; mTargetSurface.stride = 0; mTargetSurface.w = 0; mTargetSurface.h = 0; } bool WgRenderer::surfaceConfigure(WGPUSurface surface, WgContext& context, uint32_t width, uint32_t height) { this->surface = surface; if (width == 0 || height == 0 || !surface) return false; // setup surface configuration WGPUSurfaceConfiguration surfaceConfiguration { .device = context.device, .format = context.preferredFormat, .usage = WGPUTextureUsage_RenderAttachment, .width = width, .height = height, #ifdef __EMSCRIPTEN__ .alphaMode = WGPUCompositeAlphaMode_Premultiplied, .presentMode = WGPUPresentMode_Fifo #elif __linux__ #else .presentMode = WGPUPresentMode_Immediate #endif }; wgpuSurfaceConfigure(surface, &surfaceConfiguration); return true; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) { auto renderDataShape = data ? (WgRenderDataShape*)data : mRenderDataShapePool.allocate(mContext); // update geometry if (!data || (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke))) { renderDataShape->updateMeshes(rshape, flags, transform); } // update transform if ((!data) || (flags & RenderUpdateFlag::Transform)) { renderDataShape->transform = transform; renderDataShape->updateAABB(transform); } // update paint settings if ((!data) || (flags & (RenderUpdateFlag::Transform | RenderUpdateFlag::Blend | RenderUpdateFlag::Color))) { renderDataShape->renderSettingsShape.update(mContext, transform, mTargetSurface.cs, opacity); renderDataShape->renderSettingsStroke.update(mContext, transform, mTargetSurface.cs, opacity); renderDataShape->fillRule = rshape.rule; } // setup fill settings renderDataShape->viewport = vport; renderDataShape->updateVisibility(rshape, opacity); // update shape render settings if (!renderDataShape->renderSettingsShape.skip) { if (flags & RenderUpdateFlag::Gradient && rshape.fill) renderDataShape->renderSettingsShape.update(mContext, rshape.fill); else if (flags & RenderUpdateFlag::Color) renderDataShape->renderSettingsShape.update(mContext, rshape.color); } // update strokes render settings if (rshape.stroke && !renderDataShape->renderSettingsStroke.skip) { if (flags & RenderUpdateFlag::GradientStroke && rshape.stroke->fill) renderDataShape->renderSettingsStroke.update(mContext, rshape.stroke->fill); else if (flags & RenderUpdateFlag::Stroke) renderDataShape->renderSettingsStroke.update(mContext, rshape.stroke->color); } if (flags & RenderUpdateFlag::Clip) renderDataShape->updateClips(clips); return renderDataShape; } RenderData WgRenderer::prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) { auto renderDataPicture = data ? (WgRenderDataPicture*)data : mRenderDataPicturePool.allocate(mContext); // update paint settings renderDataPicture->viewport = vport; renderDataPicture->transform = transform; if (flags & (RenderUpdateFlag::Transform | RenderUpdateFlag::Blend | RenderUpdateFlag::Color)) { renderDataPicture->renderSettings.update(mContext, transform, surface->cs, opacity); } // update image data if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Image)) { renderDataPicture->updateSurface(mContext, surface); } if (flags & RenderUpdateFlag::Clip) renderDataPicture->updateClips(clips); return renderDataPicture; } bool WgRenderer::preRender() { if (mContext.invalid()) return false; mCompositor.reset(mContext); assert(mRenderTargetStack.count == 0); mRenderTargetStack.push(&mRenderTargetRoot); // create root compose settings WgCompose* compose = new WgCompose(); compose->aabb = { { 0, 0 }, { (int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h } }; compose->blend = BlendMethod::Normal; compose->method = MaskMethod::None; compose->opacity = 255; mCompositorList.push(compose); // create root scene WgSceneTask* sceneTask = new WgSceneTask(&mRenderTargetRoot, compose, nullptr); mRenderTaskList.push(sceneTask); mSceneTaskStack.push(sceneTask); return true; } bool WgRenderer::renderShape(RenderData data) { WgPaintTask* paintTask = new WgPaintTask((WgRenderDataPaint*)data, mBlendMethod); WgSceneTask* sceneTask = mSceneTaskStack.last(); sceneTask->children.push(paintTask); mRenderTaskList.push(paintTask); mCompositor.requestShape((WgRenderDataShape*)data); return true; } bool WgRenderer::renderImage(RenderData data) { WgPaintTask* paintTask = new WgPaintTask((WgRenderDataPaint*)data, mBlendMethod); WgSceneTask* sceneTask = mSceneTaskStack.last(); sceneTask->children.push(paintTask); mRenderTaskList.push(paintTask); mCompositor.requestImage((WgRenderDataPicture*)data); return true; } bool WgRenderer::postRender() { // flush stage data to gpu mCompositor.flush(mContext); // create command encoder for drawing WGPUCommandEncoder commandEncoder = mContext.createCommandEncoder(); // run rendering (all the fun is here) WgSceneTask* sceneTaskRoot = mSceneTaskStack.last(); sceneTaskRoot->run(mContext, mCompositor, commandEncoder); // execute and release command encoder mContext.submitCommandEncoder(commandEncoder); mContext.releaseCommandEncoder(commandEncoder); // clear the render tasks tree mSceneTaskStack.pop(); assert(mSceneTaskStack.count == 0); mRenderTargetStack.pop(); assert(mRenderTargetStack.count == 0); ARRAY_FOREACH(p, mRenderTaskList) { delete (*p); }; mRenderTaskList.clear(); ARRAY_FOREACH(p, mCompositorList) { delete (*p); }; mCompositorList.clear(); return true; } void WgRenderer::dispose(RenderData data) { if (!mContext.queue) return; ScopedLock lock(mDisposeKey); mDisposeRenderDatas.push(data); } bool WgRenderer::bounds(RenderData data, Point* pt4, const Matrix& m) { if (data) { auto renderDataPaint = (WgRenderDataPaint*)data; if (renderDataPaint->type() == Type::Shape) { auto renderData = (WgRenderDataShape*)data; if (!renderData->renderSettingsStroke.skip) { tvg::BBox bbox; bbox.init(); auto& vertexes = renderData->meshStrokes.vbuffer; for (uint32_t i = 0; i < vertexes.count; i++) { Point vert = vertexes[i] * m; bbox.min = min(bbox.min, vert); bbox.max = max(bbox.max, vert); } pt4[0] = bbox.min; pt4[1] = {bbox.max.x, bbox.min.y}; pt4[2] = bbox.max; pt4[3] = {bbox.min.x, bbox.max.y}; return true; } } } return false; } RenderRegion WgRenderer::region(RenderData data) { if (!data) return {}; auto renderData = (WgRenderDataPaint*)data; if (renderData->type() == Type::Shape) { auto& v1 = renderData->aabb.min; auto& v2 = renderData->aabb.max; return {{int32_t(nearbyint(v1.x)), int32_t(nearbyint(v1.y))}, {int32_t(nearbyint(v2.x)), int32_t(nearbyint(v2.y))}}; } return {{0, 0}, {(int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h}}; } bool WgRenderer::blend(BlendMethod method) { mBlendMethod = (method == BlendMethod::Composition ? BlendMethod::Normal : method); return true; } ColorSpace WgRenderer::colorSpace() { return mTargetSurface.cs; } const RenderSurface* WgRenderer::mainSurface() { return &mTargetSurface; } bool WgRenderer::clear() { if (mContext.invalid()) return false; //TODO: clear the current target buffer only if clear() is called return true; } bool WgRenderer::sync() { if (mContext.invalid()) return false; disposeObjects(); // if texture buffer used WGPUTexture dstTexture = targetTexture; if (surface) { releaseSurfaceTexture(); wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture); dstTexture = surfaceTexture.texture; } if (!dstTexture) return false; // insure that surface and offscreen target have the same size if ((wgpuTextureGetWidth(dstTexture) == mRenderTargetRoot.width) && (wgpuTextureGetHeight(dstTexture) == mRenderTargetRoot.height)) { WGPUTextureView dstTextureView = mContext.createTextureView(dstTexture); WGPUCommandEncoder commandEncoder = mContext.createCommandEncoder(); // show root offscreen buffer mCompositor.blit(mContext, commandEncoder, &mRenderTargetRoot, dstTextureView); mContext.submitCommandEncoder(commandEncoder); mContext.releaseCommandEncoder(commandEncoder); mContext.releaseTextureView(dstTextureView); } return true; } bool WgRenderer::target(WGPUDevice device, WGPUInstance instance, void* target, uint32_t w, uint32_t h, ColorSpace cs, int type) { if (!instance || !device || !target) { release(); return true; } if (w == 0 || h == 0) return false; // device or instance was changed, need to recreate all instances if ((mContext.device != device) || (mContext.instance != instance)) { release(); mContext.initialize(instance, device); mRenderTargetPool.initialize(mContext, w, h); mRenderTargetRoot.initialize(mContext, w, h); mCompositor.initialize(mContext, w, h); // update render targets dimensions } else if ((mTargetSurface.w != w) || (mTargetSurface.h != h) || (type == 0 ? (surface != (WGPUSurface)target) : (targetTexture != (WGPUTexture)target))) { mRenderTargetPool.release(mContext); mRenderTargetRoot.release(mContext); clearTargets(); mRenderTargetPool.initialize(mContext, w, h); mRenderTargetRoot.initialize(mContext, w, h); mCompositor.resize(mContext, w, h); } // configure surface (must be called after context creation) if (type == 0) { surface = (WGPUSurface)target; surfaceConfigure(surface, mContext, w, h); } else { targetTexture = (WGPUTexture)target; } mTargetSurface.stride = w; mTargetSurface.w = w; mTargetSurface.h = h; mTargetSurface.cs = cs; return true; } WgRenderer::WgRenderer() { if (TaskScheduler::onthread()) { TVGLOG("WG_RENDERER", "Running on a non-dominant thread!, Renderer(%p)", this); } ++rendererCnt; } WgRenderer::~WgRenderer() { release(); --rendererCnt; } RenderCompositor* WgRenderer::target(const RenderRegion& region, TVG_UNUSED ColorSpace cs, TVG_UNUSED CompositionFlag flags) { // create and setup compose data WgCompose* compose = new WgCompose(); compose->aabb = region; compose->flags = flags; mCompositorList.push(compose); return compose; } // please, keep beginComposite/endComposite aligned by logic bool WgRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) { // current composition WgCompose* compose = (WgCompose*)cmp; // allocate new render target and push to the render tree stack WgRenderTarget* newRenderTarget = mRenderTargetPool.allocate(mContext); mRenderTargetStack.push(newRenderTarget); // current and new scenes WgSceneTask* curSceneTask = mSceneTaskStack.last(); WgSceneTask* newSceneTask = new WgSceneTask(newRenderTarget, compose, curSceneTask); // setup masking and blending scenes configuration if ((compose->flags & (tvg::Blending | tvg::Masking)) == (tvg::Blending | tvg::Masking)) { // new scene task must be as masked but not blended newSceneTask->compose->method = curSceneTask->compose->method; newSceneTask->compose->opacity = 255; newSceneTask->compose->blend = BlendMethod::Normal; newSceneTask->renderTargetMsk = curSceneTask->renderTargetMsk; newSceneTask->renderTargetDst = curSceneTask->renderTarget; // cur scene task must be blended but not masked curSceneTask->compose->method = MaskMethod::None; curSceneTask->compose->opacity = opacity; curSceneTask->compose->blend = mBlendMethod; curSceneTask->renderTargetMsk = nullptr; } else // setup masking scenes configuration if (method != MaskMethod::None) { // new scene task must be masked newSceneTask->compose->masked = true; newSceneTask->compose->method = method; newSceneTask->compose->opacity = opacity; newSceneTask->compose->blend = BlendMethod::Normal; newSceneTask->renderTargetMsk = curSceneTask->renderTarget; newSceneTask->renderTargetDst = curSceneTask->renderTargetDst; // cur scene task must not be blended curSceneTask->renderTargetDst = nullptr; } else // setup blending scenes configuration if (method == MaskMethod::None) { // new scene task must be blended newSceneTask->compose->method = MaskMethod::None; newSceneTask->compose->opacity = opacity; newSceneTask->compose->blend = mBlendMethod; newSceneTask->renderTargetMsk = nullptr; newSceneTask->renderTargetDst = curSceneTask->renderTarget; } curSceneTask->children.push(newSceneTask); mRenderTaskList.push(newSceneTask); mSceneTaskStack.push(newSceneTask); return true; } // please, keep beginComposite/endComposite aligned by logic bool WgRenderer::endComposite(RenderCompositor* cmp) { // pop targets and scenes from render tree mRenderTargetPool.free(mContext, mRenderTargetStack.last()); mSceneTaskStack.pop(); mRenderTargetStack.pop(); // in a case of masked target we must pop mask targets and scenes also WgCompose* compose = (WgCompose*)cmp; if (compose->masked) { mRenderTargetPool.free(mContext, mRenderTargetStack.last()); mSceneTaskStack.pop(); mRenderTargetStack.pop(); } return true; } void WgRenderer::prepare(RenderEffect* effect, const Matrix& transform) { if (!effect->rd) effect->rd = mRenderDataEffectParamsPool.allocate(mContext); auto renderData = (WgRenderDataEffectParams*)effect->rd; if (effect->type == SceneEffect::GaussianBlur) { renderData->update(mContext, (RenderEffectGaussianBlur*)effect, transform); } else if (effect->type == SceneEffect::DropShadow) { renderData->update(mContext, (RenderEffectDropShadow*)effect, transform); } else if (effect->type == SceneEffect::Fill) { renderData->update(mContext, (RenderEffectFill*)effect); } else if (effect->type == SceneEffect::Tint) { renderData->update(mContext, (RenderEffectTint*)effect); } else if (effect->type == SceneEffect::Tritone) { renderData->update(mContext, (RenderEffectTritone*)effect); } else { TVGERR("WG_ENGINE", "Missing effect type? = %d", (int) effect->type); return; } } bool WgRenderer::region(RenderEffect* effect) { if (effect->type == SceneEffect::GaussianBlur) { auto gaussian = (RenderEffectGaussianBlur*)effect; auto renderData = (WgRenderDataEffectParams*)gaussian->rd; if (gaussian->direction != 2) { gaussian->extend.min.x = -renderData->extend; gaussian->extend.max.x = +renderData->extend; } if (gaussian->direction != 1) { gaussian->extend.min.y = -renderData->extend; gaussian->extend.max.y = +renderData->extend; } return true; } else if (effect->type == SceneEffect::DropShadow) { auto dropShadow = (RenderEffectDropShadow*)effect; auto renderData = (WgRenderDataEffectParams*)dropShadow->rd; dropShadow->extend.min.x = -std::ceil(renderData->extend + std::abs(renderData->offset.x)); dropShadow->extend.min.y = -std::ceil(renderData->extend + std::abs(renderData->offset.y)); dropShadow->extend.max.x = +std::floor(renderData->extend + std::abs(renderData->offset.x)); dropShadow->extend.max.y = +std::floor(renderData->extend + std::abs(renderData->offset.y)); return true; } return false; } bool WgRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, TVG_UNUSED bool direct) { mSceneTaskStack.last()->effect = effect; return true; } void WgRenderer::dispose(RenderEffect* effect) { auto renderData = (WgRenderDataEffectParams*)effect->rd; mRenderDataEffectParamsPool.free(mContext, renderData); effect->rd = nullptr; }; bool WgRenderer::preUpdate() { if (mContext.invalid()) return false; return true; } bool WgRenderer::postUpdate() { return true; } void WgRenderer::damage(TVG_UNUSED RenderData rd, TVG_UNUSED const RenderRegion& region) { //TODO } bool WgRenderer::partial(bool disable) { //TODO return false; } bool WgRenderer::intersectsShape(RenderData data, TVG_UNUSED const RenderRegion& region) { if (!data) return false; auto shape = (WgRenderDataShape*)data; RenderRegion bbox = { {(int32_t)shape->aabb.min.x, (int32_t)shape->aabb.min.y}, {(int32_t)shape->aabb.max.x, (int32_t)shape->aabb.max.y} }; if (region.intersected(bbox)) { if (region.contained(bbox)) return true; WgIntersector intersector; return intersector.intersectShape(RenderRegion::intersect(region, bbox), shape); } return false; } bool WgRenderer::intersectsImage(RenderData data, TVG_UNUSED const RenderRegion& region) { if (!data) return false; auto picture = (WgRenderDataPicture*)data; WgIntersector intersector; if (intersector.intersectImage(region, picture)) return true; return false; } bool WgRenderer::term() { if (rendererCnt > 0) return false; //TODO: clean up global resources rendererCnt = -1; return true; } WgRenderer* WgRenderer::gen(TVG_UNUSED uint32_t threads) { //initialize engine if (rendererCnt == -1) { //TODO: } return new WgRenderer; }src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/tvgLottieBuilder.h000664 001750 001750 00000021060 15164251010 036316 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_LOTTIE_BUILDER_H_ #define _TVG_LOTTIE_BUILDER_H_ #include "tvgCommon.h" #include "tvgInlist.h" #include "tvgShape.h" #include "tvgLottieExpressions.h" #include "tvgLottieModifier.h" struct LottieComposition; struct AssetResolver; struct RenderRepeater { int cnt; Matrix transform; float offset; Point position; Point anchor; Point scale; float rotation; uint8_t startOpacity; uint8_t endOpacity; bool inorder; }; struct RenderText { Point cursor{}; int line = 0, space = 0, idx = 0; float lineSpace = 0.0f, totalLineSpace = 0.0f; char *p; //current processing character int nChars; float scale; Scene* textScene; Scene* lineScene; float capScale, firstMargin; LottieTextFollowPath* follow; RenderText(LottieText* text, const TextDocument& doc) : p(doc.text), nChars(strlen(p)), scale(doc.size), textScene(Scene::gen()), lineScene(Scene::gen()) { } ~RenderText() { Paint::rel(textScene); Paint::rel(lineScene); } }; enum RenderFragment : uint8_t {ByNone = 0, ByFill, ByStroke}; struct RenderContext { INLIST_ITEM(RenderContext); Shape* propagator = nullptr; //for propagating the shape properties excluding paths Shape* merging = nullptr; //merging shapes if possible (if shapes have same properties) LottieObject** begin = nullptr; //iteration entry point Array repeaters; Matrix* transform = nullptr; LottieRoundnessModifier* roundness = nullptr; LottieOffsetModifier* offset = nullptr; LottieModifier* modifier = nullptr; RenderFragment fragment = ByNone; //render context has been fragmented bool reqFragment = false; //requirement to fragment the render context RenderContext(Shape* propagator) { to(propagator)->reset(); propagator->ref(); this->propagator = propagator; } ~RenderContext() { propagator->unref(false); delete(transform); delete(roundness); delete(offset); } RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false) : propagator(propagator) { if (mergeable) merging = rhs.merging; propagator->ref(); repeaters = rhs.repeaters; fragment = rhs.fragment; if (rhs.roundness) { roundness = new LottieRoundnessModifier(rhs.roundness->buffer, rhs.roundness->r); update(roundness); } if (rhs.offset) { offset = new LottieOffsetModifier(rhs.offset->offset, rhs.offset->miterLimit, rhs.offset->join); update(offset); } if (rhs.transform) { transform = new Matrix; *transform = *rhs.transform; } } void update(LottieModifier* next) { if (modifier) modifier = modifier->decorate(next); else modifier = next; } }; struct LottieBuilder { LottieBuilder() { exps = LottieExpressions::instance(); } ~LottieBuilder() { LottieExpressions::retrieve(exps); } bool expressions() { return exps ? true : false; } void offTween() { if (tween.active) tween.active = false; } void onTween(float to, float progress) { tween.frameNo = to; tween.progress = progress; tween.active = true; } bool tweening() { return tween.active; } bool update(LottieComposition* comp, float progress); void build(LottieComposition* comp); const AssetResolver* resolver = nullptr; //do not free this private: void appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx); bool fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx, RenderFragment fragment); Shape* textShape(LottieText* text, float frameNo, const TextDocument& doc, LottieGlyph* glyph, const RenderText& ctx); void updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo); void updateEffect(LottieLayer* layer, float frameNo, uint8_t quality); void updateLayer(LottieComposition* comp, Scene* scene, LottieLayer* layer, float frameNo); bool updateMatte(LottieComposition* comp, float frameNo, Scene* scene, LottieLayer* layer); void updatePrecomp(LottieComposition* comp, LottieLayer* precomp, float frameNo); void updatePrecomp(LottieComposition* comp, LottieLayer* precomp, float frameNo, Tween& tween); void updateSolid(LottieLayer* layer); void updateImage(LottieGroup* layer); void updateURLFont(LottieLayer* layer, float frameNo, LottieText* text, const TextDocument& doc); void updateLocalFont(LottieLayer* layer, float frameNo, LottieText* text, const TextDocument& doc); bool updateTextRange(LottieText* text, float frameNo, Shape* shape, const TextDocument& doc, RenderText& ctx); void updateText(LottieLayer* layer, float frameNo); void updateMasks(LottieLayer* layer, float frameNo); void updateTransform(LottieLayer* layer, float frameNo); void updateChildren(LottieGroup* parent, float frameNo, Inlist& contexts); void updateGroup(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& pcontexts, RenderContext* ctx); void updateTransform(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); bool updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); bool updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); bool updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); bool updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateRect(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateEllipse(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updatePath(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updatePolystar(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateStar(LottiePolyStar* star, float frameNo, Matrix* transform, Shape* merging, RenderContext* ctx, Tween& tween, LottieExpressions* exps); void updatePolygon(LottieGroup* parent, LottiePolyStar* star, float frameNo, Matrix* transform, Shape* merging, RenderContext* ctx, Tween& tween, LottieExpressions* exps); void updateTrimpath(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateRepeater(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateRoundedCorner(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateOffsetPath(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); RenderPath buffer; //reusable path LottieExpressions* exps; Tween tween; }; #endif //_TVG_LOTTIE_BUILDER_Hmlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/webp/dsp/dec.cpp000664 001750 001750 00000055236 15164251010 034432 0ustar00ddennedyddennedy000000 000000 // Copyright 2010 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Speed-critical decoding functions, default plain-C implementations. // // Author: Skal (pascal.massimino@gmail.com) #include "./dsp.h" #include "../dec/vp8i.h" //------------------------------------------------------------------------------ static WEBP_INLINE uint8_t clip_8b(int v) { return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; } //------------------------------------------------------------------------------ // Transforms (Paragraph 14.4) #define STORE(x, y, v) \ dst[x + y * BPS] = clip_8b(dst[x + y * BPS] + ((v) >> 3)) #define STORE2(y, dc, d, c) do { \ const int DC = (dc); \ STORE(0, y, DC + (d)); \ STORE(1, y, DC + (c)); \ STORE(2, y, DC - (c)); \ STORE(3, y, DC - (d)); \ } while (0) static const int kC1 = 20091 + (1 << 16); static const int kC2 = 35468; #define MUL(a, b) (((a) * (b)) >> 16) static void TransformOne(const int16_t* in, uint8_t* dst) { int C[4 * 4], *tmp; int i; tmp = C; for (i = 0; i < 4; ++i) { // vertical pass const int a = in[0] + in[8]; // [-4096, 4094] const int b = in[0] - in[8]; // [-4095, 4095] const int c = MUL(in[4], kC2) - MUL(in[12], kC1); // [-3783, 3783] const int d = MUL(in[4], kC1) + MUL(in[12], kC2); // [-3785, 3781] tmp[0] = a + d; // [-7881, 7875] tmp[1] = b + c; // [-7878, 7878] tmp[2] = b - c; // [-7878, 7878] tmp[3] = a - d; // [-7877, 7879] tmp += 4; in++; } // Each pass is expanding the dynamic range by ~3.85 (upper bound). // The exact value is (2. + (kC1 + kC2) / 65536). // After the second pass, maximum interval is [-3794, 3794], assuming // an input in [-2048, 2047] interval. We then need to add a dst value // in the [0, 255] range. // In the worst case scenario, the input to clip_8b() can be as large as // [-60713, 60968]. tmp = C; for (i = 0; i < 4; ++i) { // horizontal pass const int dc = tmp[0] + 4; const int a = dc + tmp[8]; const int b = dc - tmp[8]; const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1); const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2); STORE(0, 0, a + d); STORE(1, 0, b + c); STORE(2, 0, b - c); STORE(3, 0, a - d); tmp++; dst += BPS; } } // Simplified transform when only in[0], in[1] and in[4] are non-zero static void TransformAC3(const int16_t* in, uint8_t* dst) { const int a = in[0] + 4; const int c4 = MUL(in[4], kC2); const int d4 = MUL(in[4], kC1); const int c1 = MUL(in[1], kC2); const int d1 = MUL(in[1], kC1); STORE2(0, a + d4, d1, c1); STORE2(1, a + c4, d1, c1); STORE2(2, a - c4, d1, c1); STORE2(3, a - d4, d1, c1); } #undef MUL #undef STORE2 static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) { TransformOne(in, dst); if (do_two) { TransformOne(in + 16, dst + 4); } } static void TransformUV(const int16_t* in, uint8_t* dst) { VP8Transform(in + 0 * 16, dst, 1); VP8Transform(in + 2 * 16, dst + 4 * BPS, 1); } static void TransformDC(const int16_t* in, uint8_t* dst) { const int DC = in[0] + 4; int i, j; for (j = 0; j < 4; ++j) { for (i = 0; i < 4; ++i) { STORE(i, j, DC); } } } static void TransformDCUV(const int16_t* in, uint8_t* dst) { if (in[0 * 16]) VP8TransformDC(in + 0 * 16, dst); if (in[1 * 16]) VP8TransformDC(in + 1 * 16, dst + 4); if (in[2 * 16]) VP8TransformDC(in + 2 * 16, dst + 4 * BPS); if (in[3 * 16]) VP8TransformDC(in + 3 * 16, dst + 4 * BPS + 4); } #undef STORE //------------------------------------------------------------------------------ // Paragraph 14.3 static void TransformWHT(const int16_t* in, int16_t* out) { int tmp[16]; int i; for (i = 0; i < 4; ++i) { const int a0 = in[0 + i] + in[12 + i]; const int a1 = in[4 + i] + in[ 8 + i]; const int a2 = in[4 + i] - in[ 8 + i]; const int a3 = in[0 + i] - in[12 + i]; tmp[0 + i] = a0 + a1; tmp[8 + i] = a0 - a1; tmp[4 + i] = a3 + a2; tmp[12 + i] = a3 - a2; } for (i = 0; i < 4; ++i) { const int dc = tmp[0 + i * 4] + 3; // w/ rounder const int a0 = dc + tmp[3 + i * 4]; const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4]; const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4]; const int a3 = dc - tmp[3 + i * 4]; out[ 0] = (a0 + a1) >> 3; out[16] = (a3 + a2) >> 3; out[32] = (a0 - a1) >> 3; out[48] = (a3 - a2) >> 3; out += 64; } } void (*VP8TransformWHT)(const int16_t* in, int16_t* out); //------------------------------------------------------------------------------ // Intra predictions #define DST(x, y) dst[(x) + (y) * BPS] static WEBP_INLINE void TrueMotion(uint8_t* dst, int size) { const uint8_t* top = dst - BPS; const uint8_t* const clip0 = VP8kclip1 - top[-1]; int y; for (y = 0; y < size; ++y) { const uint8_t* const clip = clip0 + dst[-1]; int x; for (x = 0; x < size; ++x) { dst[x] = clip[top[x]]; } dst += BPS; } } static void TM4(uint8_t* dst) { TrueMotion(dst, 4); } static void TM8uv(uint8_t* dst) { TrueMotion(dst, 8); } static void TM16(uint8_t* dst) { TrueMotion(dst, 16); } //------------------------------------------------------------------------------ // 16x16 static void VE16(uint8_t* dst) { // vertical int j; for (j = 0; j < 16; ++j) { memcpy(dst + j * BPS, dst - BPS, 16); } } static void HE16(uint8_t* dst) { // horizontal int j; for (j = 16; j > 0; --j) { memset(dst, dst[-1], 16); dst += BPS; } } static WEBP_INLINE void Put16(int v, uint8_t* dst) { int j; for (j = 0; j < 16; ++j) { memset(dst + j * BPS, v, 16); } } static void DC16(uint8_t* dst) { // DC int DC = 16; int j; for (j = 0; j < 16; ++j) { DC += dst[-1 + j * BPS] + dst[j - BPS]; } Put16(DC >> 5, dst); } static void DC16NoTop(uint8_t* dst) { // DC with top samples not available int DC = 8; int j; for (j = 0; j < 16; ++j) { DC += dst[-1 + j * BPS]; } Put16(DC >> 4, dst); } static void DC16NoLeft(uint8_t* dst) { // DC with left samples not available int DC = 8; int i; for (i = 0; i < 16; ++i) { DC += dst[i - BPS]; } Put16(DC >> 4, dst); } static void DC16NoTopLeft(uint8_t* dst) { // DC with no top and left samples Put16(0x80, dst); } VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES]; //------------------------------------------------------------------------------ // 4x4 #define AVG3(a, b, c) ((((uint32_t)a) + 2 * (b) + (c) + 2) >> 2) #define AVG2(a, b) (((a) + (b) + 1) >> 1) static void VE4(uint8_t* dst) { // vertical if (!dst) return; const uint8_t* top = dst - BPS; const uint8_t vals[4] = { (uint8_t)AVG3(top[-1], top[0], top[1]), (uint8_t)AVG3(top[ 0], top[1], top[2]), (uint8_t)AVG3(top[ 1], top[2], top[3]), (uint8_t)AVG3(top[ 2], top[3], top[4]) }; int i; for (i = 0; i < 4; ++i) { memcpy(dst + i * BPS, vals, sizeof(vals)); } } static void HE4(uint8_t* dst) { // horizontal const int A = dst[-1 - BPS]; const int B = dst[-1]; const int C = dst[-1 + BPS]; const int D = dst[-1 + 2 * BPS]; const int E = dst[-1 + 3 * BPS]; *(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(A, B, C); *(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(B, C, D); *(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(C, D, E); *(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(D, E, E); } static void DC4(uint8_t* dst) { // DC uint32_t dc = 4; int i; for (i = 0; i < 4; ++i) dc += dst[i - BPS] + dst[-1 + i * BPS]; dc >>= 3; for (i = 0; i < 4; ++i) memset(dst + i * BPS, dc, 4); } static void RD4(uint8_t* dst) { // Down-right const int I = dst[-1 + 0 * BPS]; const int J = dst[-1 + 1 * BPS]; const int K = dst[-1 + 2 * BPS]; const int L = dst[-1 + 3 * BPS]; const int X = dst[-1 - BPS]; const int A = dst[0 - BPS]; const int B = dst[1 - BPS]; const int C = dst[2 - BPS]; const int D = dst[3 - BPS]; DST(0, 3) = AVG3(J, K, L); DST(1, 3) = DST(0, 2) = AVG3(I, J, K); DST(2, 3) = DST(1, 2) = DST(0, 1) = AVG3(X, I, J); DST(3, 3) = DST(2, 2) = DST(1, 1) = DST(0, 0) = AVG3(A, X, I); DST(3, 2) = DST(2, 1) = DST(1, 0) = AVG3(B, A, X); DST(3, 1) = DST(2, 0) = AVG3(C, B, A); DST(3, 0) = AVG3(D, C, B); } static void LD4(uint8_t* dst) { // Down-Left const int A = dst[0 - BPS]; const int B = dst[1 - BPS]; const int C = dst[2 - BPS]; const int D = dst[3 - BPS]; const int E = dst[4 - BPS]; const int F = dst[5 - BPS]; const int G = dst[6 - BPS]; const int H = dst[7 - BPS]; DST(0, 0) = AVG3(A, B, C); DST(1, 0) = DST(0, 1) = AVG3(B, C, D); DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E); DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F); DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G); DST(3, 2) = DST(2, 3) = AVG3(F, G, H); DST(3, 3) = AVG3(G, H, H); } static void VR4(uint8_t* dst) { // Vertical-Right const int I = dst[-1 + 0 * BPS]; const int J = dst[-1 + 1 * BPS]; const int K = dst[-1 + 2 * BPS]; const int X = dst[-1 - BPS]; const int A = dst[0 - BPS]; const int B = dst[1 - BPS]; const int C = dst[2 - BPS]; const int D = dst[3 - BPS]; DST(0, 0) = DST(1, 2) = AVG2(X, A); DST(1, 0) = DST(2, 2) = AVG2(A, B); DST(2, 0) = DST(3, 2) = AVG2(B, C); DST(3, 0) = AVG2(C, D); DST(0, 3) = AVG3(K, J, I); DST(0, 2) = AVG3(J, I, X); DST(0, 1) = DST(1, 3) = AVG3(I, X, A); DST(1, 1) = DST(2, 3) = AVG3(X, A, B); DST(2, 1) = DST(3, 3) = AVG3(A, B, C); DST(3, 1) = AVG3(B, C, D); } static void VL4(uint8_t* dst) { // Vertical-Left const int A = dst[0 - BPS]; const int B = dst[1 - BPS]; const int C = dst[2 - BPS]; const int D = dst[3 - BPS]; const int E = dst[4 - BPS]; const int F = dst[5 - BPS]; const int G = dst[6 - BPS]; const int H = dst[7 - BPS]; DST(0, 0) = AVG2(A, B); DST(1, 0) = DST(0, 2) = AVG2(B, C); DST(2, 0) = DST(1, 2) = AVG2(C, D); DST(3, 0) = DST(2, 2) = AVG2(D, E); DST(0, 1) = AVG3(A, B, C); DST(1, 1) = DST(0, 3) = AVG3(B, C, D); DST(2, 1) = DST(1, 3) = AVG3(C, D, E); DST(3, 1) = DST(2, 3) = AVG3(D, E, F); DST(3, 2) = AVG3(E, F, G); DST(3, 3) = AVG3(F, G, H); } static void HU4(uint8_t* dst) { // Horizontal-Up const int I = dst[-1 + 0 * BPS]; const int J = dst[-1 + 1 * BPS]; const int K = dst[-1 + 2 * BPS]; const int L = dst[-1 + 3 * BPS]; DST(0, 0) = AVG2(I, J); DST(2, 0) = DST(0, 1) = AVG2(J, K); DST(2, 1) = DST(0, 2) = AVG2(K, L); DST(1, 0) = AVG3(I, J, K); DST(3, 0) = DST(1, 1) = AVG3(J, K, L); DST(3, 1) = DST(1, 2) = AVG3(K, L, L); DST(3, 2) = DST(2, 2) = DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L; } static void HD4(uint8_t* dst) { // Horizontal-Down const int I = dst[-1 + 0 * BPS]; const int J = dst[-1 + 1 * BPS]; const int K = dst[-1 + 2 * BPS]; const int L = dst[-1 + 3 * BPS]; const int X = dst[-1 - BPS]; const int A = dst[0 - BPS]; const int B = dst[1 - BPS]; const int C = dst[2 - BPS]; DST(0, 0) = DST(2, 1) = AVG2(I, X); DST(0, 1) = DST(2, 2) = AVG2(J, I); DST(0, 2) = DST(2, 3) = AVG2(K, J); DST(0, 3) = AVG2(L, K); DST(3, 0) = AVG3(A, B, C); DST(2, 0) = AVG3(X, A, B); DST(1, 0) = DST(3, 1) = AVG3(I, X, A); DST(1, 1) = DST(3, 2) = AVG3(J, I, X); DST(1, 2) = DST(3, 3) = AVG3(K, J, I); DST(1, 3) = AVG3(L, K, J); } #undef DST #undef AVG3 #undef AVG2 VP8PredFunc VP8PredLuma4[NUM_BMODES]; //------------------------------------------------------------------------------ // Chroma static void VE8uv(uint8_t* dst) { // vertical int j; for (j = 0; j < 8; ++j) { memcpy(dst + j * BPS, dst - BPS, 8); } } static void HE8uv(uint8_t* dst) { // horizontal int j; for (j = 0; j < 8; ++j) { memset(dst, dst[-1], 8); dst += BPS; } } // helper for chroma-DC predictions static WEBP_INLINE void Put8x8uv(uint8_t value, uint8_t* dst) { int j; for (j = 0; j < 8; ++j) { memset(dst + j * BPS, value, 8); } } static void DC8uv(uint8_t* dst) { // DC int dc0 = 8; int i; for (i = 0; i < 8; ++i) { dc0 += dst[i - BPS] + dst[-1 + i * BPS]; } Put8x8uv(dc0 >> 4, dst); } static void DC8uvNoLeft(uint8_t* dst) { // DC with no left samples int dc0 = 4; int i; for (i = 0; i < 8; ++i) { dc0 += dst[i - BPS]; } Put8x8uv(dc0 >> 3, dst); } static void DC8uvNoTop(uint8_t* dst) { // DC with no top samples int dc0 = 4; int i; for (i = 0; i < 8; ++i) { dc0 += dst[-1 + i * BPS]; } Put8x8uv(dc0 >> 3, dst); } static void DC8uvNoTopLeft(uint8_t* dst) { // DC with nothing Put8x8uv(0x80, dst); } VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES]; //------------------------------------------------------------------------------ // Edge filtering functions // 4 pixels in, 2 pixels out static WEBP_INLINE void do_filter2(uint8_t* p, int step) { const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int a = 3 * (q0 - p0) + VP8ksclip1[p1 - q1]; // in [-893,892] const int a1 = VP8ksclip2[(a + 4) >> 3]; // in [-16,15] const int a2 = VP8ksclip2[(a + 3) >> 3]; p[-step] = VP8kclip1[p0 + a2]; p[ 0] = VP8kclip1[q0 - a1]; } // 4 pixels in, 4 pixels out static WEBP_INLINE void do_filter4(uint8_t* p, int step) { const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int a = 3 * (q0 - p0); const int a1 = VP8ksclip2[(a + 4) >> 3]; const int a2 = VP8ksclip2[(a + 3) >> 3]; const int a3 = (a1 + 1) >> 1; p[-2*step] = VP8kclip1[p1 + a3]; p[- step] = VP8kclip1[p0 + a2]; p[ 0] = VP8kclip1[q0 - a1]; p[ step] = VP8kclip1[q1 - a3]; } // 6 pixels in, 6 pixels out static WEBP_INLINE void do_filter6(uint8_t* p, int step) { const int p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step]; const int q0 = p[0], q1 = p[step], q2 = p[2*step]; const int a = VP8ksclip1[3 * (q0 - p0) + VP8ksclip1[p1 - q1]]; // a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9] const int a1 = (27 * a + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7 const int a2 = (18 * a + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7 const int a3 = (9 * a + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7 p[-3*step] = VP8kclip1[p2 + a3]; p[-2*step] = VP8kclip1[p1 + a2]; p[- step] = VP8kclip1[p0 + a1]; p[ 0] = VP8kclip1[q0 - a1]; p[ step] = VP8kclip1[q1 - a2]; p[ 2*step] = VP8kclip1[q2 - a3]; } static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) { const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; return (VP8kabs0[p1 - p0] > thresh) || (VP8kabs0[q1 - q0] > thresh); } static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int t) { const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step]; return ((4 * VP8kabs0[p0 - q0] + VP8kabs0[p1 - q1]) <= t); } static WEBP_INLINE int needs_filter2(const uint8_t* p, int step, int t, int it) { const int p3 = p[-4 * step], p2 = p[-3 * step], p1 = p[-2 * step]; const int p0 = p[-step], q0 = p[0]; const int q1 = p[step], q2 = p[2 * step], q3 = p[3 * step]; if ((4 * VP8kabs0[p0 - q0] + VP8kabs0[p1 - q1]) > t) return 0; return VP8kabs0[p3 - p2] <= it && VP8kabs0[p2 - p1] <= it && VP8kabs0[p1 - p0] <= it && VP8kabs0[q3 - q2] <= it && VP8kabs0[q2 - q1] <= it && VP8kabs0[q1 - q0] <= it; } //------------------------------------------------------------------------------ // Simple In-loop filtering (Paragraph 15.2) static void SimpleVFilter16(uint8_t* p, int stride, int thresh) { int i; const int thresh2 = 2 * thresh + 1; for (i = 0; i < 16; ++i) { if (needs_filter(p + i, stride, thresh2)) { do_filter2(p + i, stride); } } } static void SimpleHFilter16(uint8_t* p, int stride, int thresh) { int i; const int thresh2 = 2 * thresh + 1; for (i = 0; i < 16; ++i) { if (needs_filter(p + i * stride, 1, thresh2)) { do_filter2(p + i * stride, 1); } } } static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) { int k; for (k = 3; k > 0; --k) { p += 4 * stride; SimpleVFilter16(p, stride, thresh); } } static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) { int k; for (k = 3; k > 0; --k) { p += 4; SimpleHFilter16(p, stride, thresh); } } //------------------------------------------------------------------------------ // Complex In-loop filtering (Paragraph 15.3) static WEBP_INLINE void FilterLoop26(uint8_t* p, int hstride, int vstride, int size, int thresh, int ithresh, int hev_thresh) { const int thresh2 = 2 * thresh + 1; while (size-- > 0) { if (needs_filter2(p, hstride, thresh2, ithresh)) { if (hev(p, hstride, hev_thresh)) { do_filter2(p, hstride); } else { do_filter6(p, hstride); } } p += vstride; } } static WEBP_INLINE void FilterLoop24(uint8_t* p, int hstride, int vstride, int size, int thresh, int ithresh, int hev_thresh) { const int thresh2 = 2 * thresh + 1; while (size-- > 0) { if (needs_filter2(p, hstride, thresh2, ithresh)) { if (hev(p, hstride, hev_thresh)) { do_filter2(p, hstride); } else { do_filter4(p, hstride); } } p += vstride; } } // on macroblock edges static void VFilter16(uint8_t* p, int stride, int thresh, int ithresh, int hev_thresh) { FilterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh); } static void HFilter16(uint8_t* p, int stride, int thresh, int ithresh, int hev_thresh) { FilterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh); } // on three inner edges static void VFilter16i(uint8_t* p, int stride, int thresh, int ithresh, int hev_thresh) { int k; for (k = 3; k > 0; --k) { p += 4 * stride; FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh); } } static void HFilter16i(uint8_t* p, int stride, int thresh, int ithresh, int hev_thresh) { int k; for (k = 3; k > 0; --k) { p += 4; FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh); } } // 8-pixels wide variant, for chroma filtering static void VFilter8(uint8_t* u, uint8_t* v, int stride, int thresh, int ithresh, int hev_thresh) { FilterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh); FilterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh); } static void HFilter8(uint8_t* u, uint8_t* v, int stride, int thresh, int ithresh, int hev_thresh) { FilterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh); FilterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh); } static void VFilter8i(uint8_t* u, uint8_t* v, int stride, int thresh, int ithresh, int hev_thresh) { FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); } static void HFilter8i(uint8_t* u, uint8_t* v, int stride, int thresh, int ithresh, int hev_thresh) { FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh); FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh); } //------------------------------------------------------------------------------ VP8DecIdct2 VP8Transform; VP8DecIdct VP8TransformAC3; VP8DecIdct VP8TransformUV; VP8DecIdct VP8TransformDC; VP8DecIdct VP8TransformDCUV; VP8LumaFilterFunc VP8VFilter16; VP8LumaFilterFunc VP8HFilter16; VP8ChromaFilterFunc VP8VFilter8; VP8ChromaFilterFunc VP8HFilter8; VP8LumaFilterFunc VP8VFilter16i; VP8LumaFilterFunc VP8HFilter16i; VP8ChromaFilterFunc VP8VFilter8i; VP8ChromaFilterFunc VP8HFilter8i; VP8SimpleFilterFunc VP8SimpleVFilter16; VP8SimpleFilterFunc VP8SimpleHFilter16; VP8SimpleFilterFunc VP8SimpleVFilter16i; VP8SimpleFilterFunc VP8SimpleHFilter16i; extern void VP8DspInitSSE2(void); extern void VP8DspInitSSE41(void); extern void VP8DspInitNEON(void); extern void VP8DspInitMIPS32(void); extern void VP8DspInitMIPSdspR2(void); static volatile VP8CPUInfo dec_last_cpuinfo_used = (VP8CPUInfo)&dec_last_cpuinfo_used; WEBP_TSAN_IGNORE_FUNCTION void VP8DspInit(void) { if (dec_last_cpuinfo_used == VP8GetCPUInfo) return; VP8InitClipTables(); VP8TransformWHT = TransformWHT; VP8Transform = TransformTwo; VP8TransformUV = TransformUV; VP8TransformDC = TransformDC; VP8TransformDCUV = TransformDCUV; VP8TransformAC3 = TransformAC3; VP8VFilter16 = VFilter16; VP8HFilter16 = HFilter16; VP8VFilter8 = VFilter8; VP8HFilter8 = HFilter8; VP8VFilter16i = VFilter16i; VP8HFilter16i = HFilter16i; VP8VFilter8i = VFilter8i; VP8HFilter8i = HFilter8i; VP8SimpleVFilter16 = SimpleVFilter16; VP8SimpleHFilter16 = SimpleHFilter16; VP8SimpleVFilter16i = SimpleVFilter16i; VP8SimpleHFilter16i = SimpleHFilter16i; VP8PredLuma4[0] = DC4; VP8PredLuma4[1] = TM4; VP8PredLuma4[2] = VE4; VP8PredLuma4[3] = HE4; VP8PredLuma4[4] = RD4; VP8PredLuma4[5] = VR4; VP8PredLuma4[6] = LD4; VP8PredLuma4[7] = VL4; VP8PredLuma4[8] = HD4; VP8PredLuma4[9] = HU4; VP8PredLuma16[0] = DC16; VP8PredLuma16[1] = TM16; VP8PredLuma16[2] = VE16; VP8PredLuma16[3] = HE16; VP8PredLuma16[4] = DC16NoTop; VP8PredLuma16[5] = DC16NoLeft; VP8PredLuma16[6] = DC16NoTopLeft; VP8PredChroma8[0] = DC8uv; VP8PredChroma8[1] = TM8uv; VP8PredChroma8[2] = VE8uv; VP8PredChroma8[3] = HE8uv; VP8PredChroma8[4] = DC8uvNoTop; VP8PredChroma8[5] = DC8uvNoLeft; VP8PredChroma8[6] = DC8uvNoTopLeft; // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) if (VP8GetCPUInfo(kSSE2)) { VP8DspInitSSE2(); #if defined(WEBP_USE_SSE41) if (VP8GetCPUInfo(kSSE4_1)) { VP8DspInitSSE41(); } #endif } #endif #if defined(WEBP_USE_NEON) if (VP8GetCPUInfo(kNEON)) { VP8DspInitNEON(); } #endif #if defined(WEBP_USE_MIPS32) if (VP8GetCPUInfo(kMIPS32)) { VP8DspInitMIPS32(); } #endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { VP8DspInitMIPSdspR2(); } #endif } dec_last_cpuinfo_used = VP8GetCPUInfo; } lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-uint16array-prototype.cpp000664 001750 001750 00000002226 15164251010 055012 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-uint16array-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID uint16array_prototype #include "ecma-builtin-internal-routines-template.inc.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup uint16arrayprototype ECMA Uint16Array.prototype object built-in * @{ */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/filereadstream.h000664 001750 001750 00000005644 15164251010 040020 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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 #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(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 - !eof_ <= 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_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/.github/FUNDING.yml000664 001750 001750 00000000046 15164251010 032372 0ustar00ddennedyddennedy000000 000000 github: thorvg open_collective: thorvgglaxnimate/external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-lcache.h000664 001750 001750 00000002313 15164251010 043430 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_LCACHE_H #define ECMA_LCACHE_H /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmalcache Property lookup cache * @{ */ #include "ecma-globals.h" #if JERRY_LCACHE void ecma_lcache_insert (const ecma_object_t *object_p, const jmem_cpointer_t name_cp, ecma_property_t *prop_p); ecma_property_t *ecma_lcache_lookup (const ecma_object_t *object_p, const ecma_string_t *prop_name_p); void ecma_lcache_invalidate (const ecma_object_t *object_p, const jmem_cpointer_t name_cp, ecma_property_t *prop_p); #endif /* JERRY_LCACHE */ /** * @} * @} */ #endif /* !ECMA_LCACHE_H */ external/thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/base/ecma-init-finalize.h000664 001750 001750 00000001641 15164251010 044756 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 ECMA_INIT_FINALIZE_H #define ECMA_INIT_FINALIZE_H /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmainitfinalize Initialization and finalization of ECMA components * @{ */ void ecma_init (void); void ecma_finalize (void); /** * @} * @} */ #endif /* !ECMA_INIT_FINALIZE_H */ lottie/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype-unscopables.cpp000664 001750 001750 00000001702 15164251010 054071 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtin-helpers.h" #include "ecma-builtins.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-array-prototype-unscopables.inc.h" #define BUILTIN_UNDERSCORED_ID array_prototype_unscopables #include "ecma-builtin-internal-routines-template.inc.h" src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-int8array.cpp000664 001750 001750 00000004326 15164251010 052506 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-typedarray-object.h" #include "jrt.h" #if JERRY_BUILTIN_TYPEDARRAY #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" #define BUILTIN_INC_HEADER_NAME "ecma-builtin-int8array.inc.h" #define BUILTIN_UNDERSCORED_ID int8array #include "ecma-builtin-internal-routines-template.inc.h" #include "ecma-builtin-typedarray-helpers.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmabuiltins * @{ * * \addtogroup int8array ECMA Int8Array object built-in * @{ */ /** * Handle calling [[Call]] of Int8Array * * @return ecma value */ ecma_value_t ecma_builtin_int8array_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); return ecma_raise_type_error (ECMA_ERR_CONSTRUCTOR_INT8_ARRAY_REQUIRES_NEW); } /* ecma_builtin_int8array_dispatch_call */ /** * Handle calling [[Construct]] of Int8Array * * @return ecma value */ ecma_value_t ecma_builtin_int8array_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { return ecma_typedarray_helper_dispatch_construct (arguments_list_p, arguments_list_len, ECMA_INT8_ARRAY); } /* ecma_builtin_int8array_dispatch_construct */ /** * @} * @} * @} */ #endif /* JERRY_BUILTIN_TYPEDARRAY */ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/bindings/000775 001750 001750 00000000000 15164251010 031601 5ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/raw/tvgRawLoader.h000664 001750 001750 00000002707 15164251010 035006 0ustar00ddennedyddennedy000000 000000 /* * Copyright (c) 2020 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_RAW_LOADER_H_ #define _TVG_RAW_LOADER_H_ class RawLoader : public ImageLoader { public: bool copy = false; RawLoader(); ~RawLoader(); using LoadModule::open; bool open(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy); bool read() override; }; #endif //_TVG_RAW_LOADER_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/resources/test1.svg000664 001750 001750 00000001662 15164251010 033774 0ustar00ddennedyddennedy000000 000000 thorvg/thorvg/src/loaders/lottie/jerryscript/jerry-core/ecma/operations/ecma-string-object.cpp000664 001750 001750 00000010657 15164251010 046601 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 "ecma-string-object.h" #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-objects-general.h" #include "ecma-objects.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmastringobject ECMA String object related routines * @{ */ /** * String object creation operation. * * See also: ECMA-262 v5, 15.5.2.1 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_create_string_object (const ecma_value_t *arguments_list_p, /**< list of arguments that are passed to String constructor */ uint32_t arguments_list_len) /**< length of the arguments' list */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_value_t prim_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); if (arguments_list_len > 0) { ecma_string_t *str_p = ecma_op_to_string (arguments_list_p[0]); if (JERRY_UNLIKELY (str_p == NULL)) { return ECMA_VALUE_ERROR; } prim_value = ecma_make_string_value (str_p); } ecma_builtin_id_t proto_id; #if JERRY_BUILTIN_STRING proto_id = ECMA_BUILTIN_ID_STRING_PROTOTYPE; #else /* !JERRY_BUILTIN_STRING */ proto_id = ECMA_BUILTIN_ID_OBJECT_PROTOTYPE; #endif /* JERRY_BUILTIN_STRING */ ecma_object_t *prototype_obj_p = ecma_builtin_get (proto_id); ecma_object_t *new_target = JERRY_CONTEXT (current_new_target_p); if (new_target) { prototype_obj_p = ecma_op_get_prototype_from_constructor (new_target, proto_id); if (JERRY_UNLIKELY (prototype_obj_p == NULL)) { return ECMA_VALUE_ERROR; } } ecma_object_t *object_p = ecma_create_object (prototype_obj_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_STRING; ext_object_p->u.cls.u3.value = prim_value; if (new_target) { ecma_deref_object (prototype_obj_p); } return ecma_make_object_value (object_p); } /* ecma_op_create_string_object */ /** * List names of a String object's lazy instantiated properties */ void ecma_op_string_list_lazy_property_names (ecma_object_t *obj_p, /**< a String object */ ecma_collection_t *prop_names_p, /**< prop name collection */ ecma_property_counter_t *prop_counter_p, /**< property counters */ jerry_property_filter_t filter) /**< property name filter options */ { JERRY_ASSERT (ecma_get_object_base_type (obj_p) == ECMA_OBJECT_BASE_TYPE_CLASS); if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_INTEGER_INDICES)) { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; JERRY_ASSERT (ext_object_p->u.cls.type == ECMA_OBJECT_CLASS_STRING); ecma_string_t *prim_value_str_p = ecma_get_string_from_value (ext_object_p->u.cls.u3.value); lit_utf8_size_t length = ecma_string_get_length (prim_value_str_p); for (lit_utf8_size_t i = 0; i < length; i++) { ecma_string_t *name_p = ecma_new_ecma_string_from_uint32 (i); /* the properties are enumerable (ECMA-262 v5, 15.5.5.2.9) */ ecma_collection_push_back (prop_names_p, ecma_make_string_value (name_p)); } prop_counter_p->array_index_named_props += length; } if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_STRINGS)) { ecma_collection_push_back (prop_names_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); prop_counter_p->string_named_props++; } } /* ecma_op_string_list_lazy_property_names */ /** * @} * @} */ src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/renderer/wg_engine/tvgWgRenderTarget.h000664 001750 001750 00000004063 15164251010 037256 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/* * Copyright (c) 2023 - 2026 ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _TVG_WG_RENDER_TARGET_H_ #define _TVG_WG_RENDER_TARGET_H_ #include "tvgWgPipelines.h" #include "tvgRender.h" struct WgRenderTarget { WGPUTexture texture{}; WGPUTexture textureMS{}; WGPUTextureView texView{}; WGPUTextureView texViewMS{}; WGPUBindGroup bindGroupRead{}; WGPUBindGroup bindGroupWrite{}; WGPUBindGroup bindGroupTexture{}; uint32_t width{}; uint32_t height{}; void initialize(WgContext& context, uint32_t width, uint32_t height); void release(WgContext& context); }; class WgRenderTargetPool { private: Array list; Array pool; uint32_t width{}; uint32_t height{}; public: WgRenderTarget* allocate(WgContext& context); void free(WgContext& context, WgRenderTarget* renderTarget); void initialize(WgContext& context, uint32_t width, uint32_t height); void release(WgContext& context); }; #endif // _TVG_WG_RENDER_TARGET_H_ modules/glaxnimate/glaxnimate/external/thorvg/thorvg/src/loaders/lottie/rapidjson/internal/regex.h000664 001750 001750 00000063010 15164251010 037746 0ustar00ddennedyddennedy000000 000000 mlt-7.38.0/src// 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. // // 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) #elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif #ifndef RAPIDJSON_REGEX_VERBOSE #define RAPIDJSON_REGEX_VERBOSE 0 #endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { /////////////////////////////////////////////////////////////////////////////// // DecodedStream template 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 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 class GenericRegex { public: typedef Encoding EncodingType; typedef typename Encoding::Ch Ch; template friend class GenericRegexSearch; GenericRegex(const Ch* source, Allocator* allocator = 0) : ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), anchorBegin_(), anchorEnd_() { GenericStringStream ss(source); DecodedStream, Encoding> ds(ss); Parse(ds); } ~GenericRegex() { RAPIDJSON_DELETE(ownAllocator_); } 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()[index]; } const State& GetState(SizeType index) const { RAPIDJSON_ASSERT(index < stateCount_); return states_.template Bottom()[index]; } Range& GetRange(SizeType index) { RAPIDJSON_ASSERT(index < rangeCount_); return ranges_.template Bottom()[index]; } const Range& GetRange(SizeType index) const { RAPIDJSON_ASSERT(index < rangeCount_); return ranges_.template Bottom()[index]; } template void Parse(DecodedStream& ds) { Stack operandStack(allocator_, 256); // Frag Stack operatorStack(allocator_, 256); // Operator Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) *atomCountStack.template Push() = 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() < kAlternation) if (!Eval(operandStack, *operatorStack.template Pop(1))) return; *operatorStack.template Push() = kAlternation; *atomCountStack.template Top() = 0; break; case '(': *operatorStack.template Push() = kLeftParenthesis; *atomCountStack.template Push() = 0; break; case ')': while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) if (!Eval(operandStack, *operatorStack.template Pop(1))) return; if (operatorStack.Empty()) return; operatorStack.template Pop(1); atomCountStack.template Pop(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(s, s, s); } ImplicitConcatenation(atomCountStack, operatorStack); break; case '\\': // Escape character if (!CharacterEscape(ds, &codepoint)) return; // Unsupported escape character // fall through to default RAPIDJSON_DELIBERATE_FALLTHROUGH; default: // Pattern character PushOperand(operandStack, codepoint); ImplicitConcatenation(atomCountStack, operatorStack); } } while (!operatorStack.Empty()) if (!Eval(operandStack, *operatorStack.template Pop(1))) return; // Link the operand to matching state. if (operandStack.GetSize() == sizeof(Frag)) { Frag* e = operandStack.template Pop(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(); s->out = out; s->out1 = out1; s->codepoint = codepoint; s->rangeStart = kRegexInvalidRange; return stateCount_++; } void PushOperand(Stack& operandStack, unsigned codepoint) { SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); *operandStack.template Push() = Frag(s, s, s); } void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { if (*atomCountStack.template Top()) *operatorStack.template Push() = kConcatenation; (*atomCountStack.template Top())++; } 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& operandStack, Operator op) { switch (op) { case kConcatenation: RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); { Frag e2 = *operandStack.template Pop(1); Frag e1 = *operandStack.template Pop(1); Patch(e1.out, e2.start); *operandStack.template Push() = 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(1); Frag e1 = *operandStack.template Pop(1); SizeType s = NewState(e1.start, e2.start, 0); *operandStack.template Push() = 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(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); return true; } return false; case kZeroOrMore: if (operandStack.GetSize() >= sizeof(Frag)) { Frag e = *operandStack.template Pop(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); Patch(e.out, s); *operandStack.template Push() = Frag(s, s, e.minIndex); return true; } return false; case kOneOrMore: if (operandStack.GetSize() >= sizeof(Frag)) { Frag e = *operandStack.template Pop(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); Patch(e.out, s); *operandStack.template Push() = Frag(e.start, s, e.minIndex); return true; } return false; default: // syntax error (e.g. unclosed kLeftParenthesis) return false; } } bool EvalQuantifier(Stack& 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& operandStack) { const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) State* s = states_.template Push(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(src.start + count, src.out + count, src.minIndex + count); stateCount_ += count; } template bool ParseUnsigned(DecodedStream& 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 bool ParseRange(DecodedStream& 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 RAPIDJSON_DELIBERATE_FALLTHROUGH; default: switch (step) { case 1: if (codepoint == '-') { step++; break; } // fall through to step 0 for other characters RAPIDJSON_DELIBERATE_FALLTHROUGH; 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(); r->start = r->end = codepoint; r->next = kRegexInvalidRange; return rangeCount_++; } template bool CharacterEscape(DecodedStream& 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 } } Allocator* ownAllocator_; Allocator* allocator_; Stack states_; Stack ranges_; SizeType root_; SizeType stateCount_; SizeType rangeCount_; static const unsigned kInfinityQuantifier = ~0u; // For SearchWithAnchoring() bool anchorBegin_; bool anchorEnd_; }; template 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(allocator_->Malloc(GetStateSetSize())); state0_.template Reserve(regex_.stateCount_); state1_.template Reserve(regex_.stateCount_); } ~GenericRegexSearch() { Allocator::Free(stateSet_); RAPIDJSON_DELETE(ownAllocator_); } template bool Match(InputStream& is) { return SearchWithAnchoring(is, true, true); } bool Match(const Ch* s) { GenericStringStream is(s); return Match(is); } template bool Search(InputStream& is) { return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); } bool Search(const Ch* s) { GenericStringStream is(s); return Search(is); } private: typedef typename RegexType::State State; typedef typename RegexType::Range Range; template bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { DecodedStream ds(is); state0_.Clear(); Stack *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(); s != current->template End(); ++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& 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() = 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 state0_; Stack state1_; uint32_t* stateSet_; }; typedef GenericRegex > Regex; typedef GenericRegexSearch RegexSearch; } // namespace internal RAPIDJSON_NAMESPACE_END #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_INTERNAL_REGEX_H_ mlt-7.38.0/src/modules/glaxnimate/glaxnimate/external/thorvg/thorvg/test/catch.hpp000664 001750 001750 00002404003 15164251010 031772 0ustar00ddennedyddennedy000000 000000 /* * Catch v2.13.10 * Generated: 2022-10-16 11:01:23.452308 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED // start catch.hpp #define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MINOR 13 #define CATCH_VERSION_PATCH 10 #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ # pragma GCC system_header #endif // start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ // Because REQUIREs trigger GCC's -Wparentheses, and because still // supported version of g++ have only buggy support for _Pragmas, // Wparentheses have to be suppressed globally. # pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL # define CATCH_CONFIG_ALL_PARTS #endif // In the impl file, we want to have access to all parts of the headers // Can also be used to sanely support PCHs #if defined(CATCH_CONFIG_ALL_PARTS) # define CATCH_CONFIG_EXTERNAL_INTERFACES # if defined(CATCH_CONFIG_DISABLE_MATCHERS) # undef CATCH_CONFIG_DISABLE_MATCHERS # endif # if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) # define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER # endif #endif #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ # include # if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) # define CATCH_PLATFORM_MAC # elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) # define CATCH_PLATFORM_IPHONE # endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) # define CATCH_PLATFORM_WINDOWS #endif // end catch_platform.h #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED # define CLARA_CONFIG_MAIN # endif #endif // start catch_user_interfaces.h namespace Catch { unsigned int rngSeed(); } // end catch_user_interfaces.h // start catch_tag_alias_autoregistrar.h // start catch_common.h // start catch_compiler_capabilities.h // Detect a number of compiler features - by compiler // The following features are defined: // // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form // (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. #ifdef __cplusplus # if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) # define CATCH_CPP14_OR_GREATER # endif # if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) # define CATCH_CPP17_OR_GREATER # endif #endif // Only GCC compiler should be used in this block, so other compilers trying to // mask themselves as GCC should be ignored. #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif #if defined(__clang__) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) // As of this writing, IBM XL's implementation of __builtin_constant_p has a bug // which results in calls to destructors being emitted for each temporary, // without a matching initialization. In practice, this can result in something // like `std::string::~string` being called on an uninitialized value. // // For example, this code will likely segfault under IBM XL: // ``` // REQUIRE(std::string("12") + "34" == "1234") // ``` // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. # if !defined(__ibmxl__) && !defined(__CUDACC__) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ # endif # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // Assume that non-Windows platforms support posix signals by default #if !defined(CATCH_PLATFORM_WINDOWS) #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS #endif //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals #if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS # define CATCH_CONFIG_COLOUR_NONE #endif //////////////////////////////////////////////////////////////////////////////// // Android somehow still does not support std::to_string #if defined(__ANDROID__) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING # define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE #endif //////////////////////////////////////////////////////////////////////////////// // Not all Windows environments support SEH properly #if defined(__MINGW32__) # define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #endif //////////////////////////////////////////////////////////////////////////////// // PS4 #if defined(__ORBIS__) # define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE #endif //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin # define _BSD_SOURCE // some versions of cygwin (most) do not support std::to_string. Use the libstd check. // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 # if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING # endif #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #if defined(_MSC_VER) // Universal Windows platform does not support SEH // Or console colours (or console at all...) # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) # define CATCH_CONFIG_COLOUR_NONE # else # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH # endif # if !defined(__clang__) // Handle Clang masquerading for msvc // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor # if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) # define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR # endif // MSVC_TRADITIONAL // Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) # endif // __clang__ #endif // _MSC_VER #if defined(_REENTRANT) || defined(_MSC_VER) // Enable async processing, as -pthread is specified or no additional linking is required # define CATCH_INTERNAL_CONFIG_USE_ASYNC #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Check if we are compiled with -fno-exceptions or equivalent #if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) # define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED #endif //////////////////////////////////////////////////////////////////////////////// // DJGPP #ifdef __DJGPP__ # define CATCH_INTERNAL_CONFIG_NO_WCHAR #endif // __DJGPP__ //////////////////////////////////////////////////////////////////////////////// // Embarcadero C++Build #if defined(__BORLANDC__) #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #endif //////////////////////////////////////////////////////////////////////////////// // Use of __COUNTER__ is suppressed during code analysis in // CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly // handled by it. // Otherwise all supported compilers support COUNTER macro, // but user still might want to turn it off #if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif //////////////////////////////////////////////////////////////////////////////// // RTX is a special version of Windows that is real time. // This means that it is detected as Windows, but does not provide // the same set of capabilities as real Windows does. #if defined(UNDER_RTSS) || defined(RTX64_BUILD) #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #define CATCH_INTERNAL_CONFIG_NO_ASYNC #define CATCH_CONFIG_COLOUR_NONE #endif #if !defined(_GLIBCXX_USE_C99_MATH_TR1) #define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER #endif // Various stdlib support checks that require __has_include #if defined(__has_include) // Check if string_view is available and usable #if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW #endif // Check if optional is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if byte is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # include # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) # define CATCH_INTERNAL_CONFIG_CPP17_BYTE # endif # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if variant is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # if defined(__clang__) && (__clang_major__ < 8) // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 // fix should be in clang 8, workaround in libstdc++ 8.2 # include # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) # define CATCH_CONFIG_NO_CPP17_VARIANT # else # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) # else # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT # endif // defined(__clang__) && (__clang_major__ < 8) # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) #endif // defined(__has_include) #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. #if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_CONFIG_POSIX_SIGNALS #endif // This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. #if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) # define CATCH_CONFIG_WCHAR #endif #if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) # define CATCH_CONFIG_CPP11_TO_STRING #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) # define CATCH_CONFIG_CPP17_OPTIONAL #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) # define CATCH_CONFIG_CPP17_STRING_VIEW #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) # define CATCH_CONFIG_CPP17_VARIANT #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) # define CATCH_CONFIG_CPP17_BYTE #endif #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) # define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif #if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) # define CATCH_CONFIG_NEW_CAPTURE #endif #if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) # define CATCH_CONFIG_DISABLE_EXCEPTIONS #endif #if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) # define CATCH_CONFIG_POLYFILL_ISNAN #endif #if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) # define CATCH_CONFIG_USE_ASYNC #endif #if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) # define CATCH_CONFIG_ANDROID_LOGWRITE #endif #if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) # define CATCH_CONFIG_GLOBAL_NEXTAFTER #endif // Even if we do not think the compiler has that warning, we still have // to provide a macro that can be used by the code. #if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... #if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) #endif #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #elif defined(__clang__) && (__clang_major__ < 5) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) #define CATCH_CATCH_ALL if ((false)) #define CATCH_CATCH_ANON(type) if ((false)) #else #define CATCH_TRY try #define CATCH_CATCH_ALL catch (...) #define CATCH_CATCH_ANON(type) catch (type) #endif #if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) #define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif // end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) #else # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif #include #include #include // We need a dummy global operator<< so we can bring it into Catch namespace later struct Catch_global_namespace_dummy {}; std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; protected: NonCopyable(); virtual ~NonCopyable(); }; struct SourceLineInfo { SourceLineInfo() = delete; SourceLineInfo( char const* _file, std::size_t _line ) noexcept : file( _file ), line( _line ) {} SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo( SourceLineInfo&& ) noexcept = default; SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; bool empty() const noexcept { return file[0] == '\0'; } bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // Bring in operator<< from global namespace into Catch namespace // This is necessary because the overload of operator<< above makes // lookup stop at namespace Catch using ::operator<<; // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { return value; } } #define CATCH_INTERNAL_LINEINFO \ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) // end catch_common.h namespace Catch { struct RegistrarForTagAliases { RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h // start catch_interfaces_testcase.h #include namespace Catch { class TestSpec; struct ITestInvoker { virtual void invoke () const = 0; virtual ~ITestInvoker(); }; class TestCase; struct IConfig; struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector const& getAllTests() const = 0; virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; bool isThrowSafe( TestCase const& testCase, IConfig const& config ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); } // end catch_interfaces_testcase.h // start catch_stringref.h #include #include #include #include namespace Catch { /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, /// it may not be null terminated. class StringRef { public: using size_type = std::size_t; using const_iterator = const char*; private: static constexpr char const* const s_empty = ""; char const* m_start = s_empty; size_type m_size = 0; public: // construction constexpr StringRef() noexcept = default; StringRef( char const* rawChars ) noexcept; constexpr StringRef( char const* rawChars, size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} StringRef( std::string const& stdString ) noexcept : m_start( stdString.c_str() ), m_size( stdString.size() ) {} explicit operator std::string() const { return std::string(m_start, m_size); } public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; auto operator != (StringRef const& other) const noexcept -> bool { return !(*this == other); } auto operator[] ( size_type index ) const noexcept -> char { assert(index < m_size); return m_start[index]; } public: // named queries constexpr auto empty() const noexcept -> bool { return m_size == 0; } constexpr auto size() const noexcept -> size_type { return m_size; } // Returns the current start pointer. If the StringRef is not // null-terminated, throws std::domain_exception auto c_str() const -> char const*; public: // substrings and searches // Returns a substring of [start, start + length). // If start + length > size(), then the substring is [start, size()). // If start > size(), then the substring is empty. auto substr( size_type start, size_type length ) const noexcept -> StringRef; // Returns the current start pointer. May not be null-terminated. auto data() const noexcept -> char const*; constexpr auto isNullTerminated() const noexcept -> bool { return m_start[m_size] == '\0'; } public: // iterators constexpr const_iterator begin() const { return m_start; } constexpr const_iterator end() const { return m_start + m_size; } }; auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { return StringRef( rawChars, size ); } } // namespace Catch constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { return Catch::StringRef( rawChars, size ); } // end catch_stringref.h // start catch_preprocessor.hpp #define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ #define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) #ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ // MSVC needs more evaluations #define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) #define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) #else #define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) #endif #define CATCH_REC_END(...) #define CATCH_REC_OUT #define CATCH_EMPTY() #define CATCH_DEFER(id) id CATCH_EMPTY() #define CATCH_REC_GET_END2() 0, CATCH_REC_END #define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 #define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 #define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT #define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) #define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) #define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) #define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) #define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) // Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, // and passes userdata as the first parameter to each invocation, // e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) #define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) #define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ #define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ #define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) #else // MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) #define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ #define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) #endif #define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ #define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) #define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) #else #define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif #define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) #define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) #define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) #define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) #define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) #define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) #define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) #define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) #define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) #define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) #define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) #define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) #define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define INTERNAL_CATCH_TYPE_GEN\ template struct TypeList {};\ template\ constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ template class...> struct TemplateTypeList{};\ template class...Cs>\ constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ template\ struct append;\ template\ struct rewrap;\ template class, typename...>\ struct create;\ template class, typename>\ struct convert;\ \ template \ struct append { using type = T; };\ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ template< template class L1, typename...E1, typename...Rest>\ struct append, TypeList, Rest...> { using type = L1; };\ \ template< template class Container, template class List, typename...elems>\ struct rewrap, List> { using type = TypeList>; };\ template< template class Container, template class List, class...Elems, typename...Elements>\ struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ \ template

Watch the full video!